Random Number Guessing Game In Assembly
Let's code a simple, random number guessing game in 6502 assembly language. The code that we are going to write is designed to work on the 6502 emulator, which can be found here. If you want to know the specifics of the emulator you can refer to the notes found in the emulator itself or over here.
Let's start, so basically our idea is to generate a random number between 1 and 10 and ask the user to guess the number, if the number is too high we are going to give the user the feedback, and vice versa. Also for this game, as there are 10 possible outcomes, we are going to give the user four chances, which is pretty generous. This is as if the user had four lives and he/she had to guess the right number to win. As the bitmap display is divided into four pages we are going to graphically display the number of lives remaining in the bitmap display.
Code
; Constants
define NEWLINE $0d ; ASCII code for newline character
define CONSTANT_NUMBER $0081; Memory location to store constant number
define remaining_guesses $0091
define guess $0095
define color $0097
start:
LDA #$00 ; set a pointer in memory location $40 to point to $0200
STA $40 ; ... low byte ($00) goes in address $40
LDA #$02
STA $41 ; ... high byte ($02) goes into address $41
LDA #$01 ; color number
STA color
STA $42
LDY #$00 ; set index to 0
lda $fe ; Use the pseudo-random number generator
cmp #$30 ; Check if it's a digit
bcc start ; If not, continue reading
cmp #$40
bcs start
sta CONSTANT_NUMBER
jsr initialize_game ; Initialize the game
jsr print_instructions ; Print game instructions
jsr input_guess ; Input user's guess
jsr input_loop
jsr compare_guess ; Compare the guess with the constant number
jmp start ; Repeat the game
initialize_game:
lda #$04 ; Initialize remaining guesses to 4
sta remaining_guesses
rts ; Return from subroutine
print_instructions:
JSR PRINT
dcb 10,"W","e","l","c","o","m","e",32,"t","o",32,"t","h","e",32,"n","u","m","b","e","r",32,"g","u","e","s","s","i","n","g",32,"g","a","m","e",10,00
JSR PRINT
dcb "Y","o","u",32,"h","a","v","e",32,"4",32,"g","u","e","s","s","e","s",32,"t","o",32 ,"g","u","e","s","s",32,"t","h","e",32,"n","u","m","b","e","r",".",10,00
JSR PRINT
dcb "E","n","t","e","r",32,"y","o","u","r",32 ,"g","u","e","s","s",":",00
rts ; Return from subroutine
input_guess:
lda #$00 ; Initialize guess variable
sta guess
input_loop:
lda #$00 ; Initialize accumulator to 0
read_char:
jsr CHRIN ; Get a character from input
cmp #$29 ; Check if it's a digit
bcc read_char ; If not, continue reading
cmp #$40
bcs read_char
; Convert ASCII digit to binary and store in accumulator
sta guess
jsr CHROUT
end_input:
jsr compare_guess ; Return from subroutine
compare_guess:
lda CONSTANT_NUMBER ; Load the address of the constant number
;lda (CONSTANT_NUMBER_ADDR) ; Load the constant number from memory
cmp guess ; Compare with user's guess
beq correct_guess ; If equal, go to correct_guess
bcs too_low ; If constant number > guess, go to too_high
jmp too_high ; Otherwise, go to too_low
too_low:
JSR PRINT
dcb 10,"T","o","o",32,"l","o","w","!",00
dec remaining_guesses ; Decrement remaining guesses
bne reask ; If remaining guesses, get another guess
jmp end_game ; Otherwise, end the game
too_high:
JSR PRINT
dcb 10,"T","o","o",32,"H","i","g","h"!",00
dec remaining_guesses ; Decrement remaining guesses
bne reask ; If remaining guesses, get another guess
jmp end_game ; Otherwise, end the game
correct_guess:
JSR PRINT
dcb 10,"C","o","n","g","r","a","t","u","l","a","t","i","o","n","s","!",32,"Y","o","u",32,"g","u","e","s","s","e","d",32,"t","h","e",32,"n","u","m","b","e","r","!",00
jmp end_game ; End the game
reask:
JSR LOOP
JSR PRINT
dcb 10,"E","n","t","e","r",32 ,"a","n","o","t","h","e","r",32,"g","u","e","s","s",":",00
JSR input_loop
end_game:
JSR LOOP
JSR PRINT
dcb 10,"T","h","e",32,"n","u","m","b","e","r",32 ,"w","a","s",":",00
lda CONSTANT_NUMBER ; Load the address of the constant number
jsr CHROUT
lda #NEWLINE
jsr CHROUT
brk
rts
; ROM routines
define SCINIT $ff81 ; Initialize/clear screen
define CHRIN $ffcf ; Input character from keyboard
define CHROUT $ffd2 ; Output character to screen
define SCREEN $ffed ; Get screen size
define PLOT $fff0 ; Get/set cursor coordinates
LOOP:
LDA color
STA ($40),y ; set pixel color at the address (pointer)+Y
INY ; increment index
BNE LOOP ; continue until done the page (256 pixels)
INC $41 ; increment the page
LDX $41 ; get the current page number
rts
; zeropage variables
define PRINT_PTR $00
define PRINT_PTR_H $01
define CURRENT $02
define SCRN_PTR $03
define SCRN_PTR_H $04
;Author: Chris Tyler, Src: https://github.com/ctyler/6502js-code/blob/master/colour-selector-live.6502
; --------------------------------------------------------
; Print a message
;
; Prints the message in memory immediately after the
; JSR PRINT. The message must be null-terminated and
; 255 characters maximum in length.
PRINT: pla
clc
adc #$01
sta PRINT_PTR
pla
sta PRINT_PTR_H
tya
pha
ldy #$00
print_next: lda (PRINT_PTR),y
beq print_done
jsr CHROUT
iny
jmp print_next
print_done: tya
clc
adc PRINT_PTR
sta PRINT_PTR
lda PRINT_PTR_H
adc #$00
sta PRINT_PTR_H
pla
tay
lda PRINT_PTR_H
pha
lda PRINT_PTR
pha
rts
Breakdown
The breakdown of the code is as follows:
Define the constants first.
Initialize the bitmap display and generate the random number for the game.
Print the rules of the game for the user.
Take only numerical input from the user.
Compare it with the generated number and react accordingly.
In the end, we have the borrowed code from my professor's repository.
The program achieves the following:
Works perfectly on the 6502 emulator.
Outputs character to the screen and the graphical representation of the Lives on the bitmap display.
Accepts numerical input from the user.
Use mathematical instructions to calculate the number of remaining guesses.
Limitations
This program, sometimes generates a character like "?" or ";" as its random number, I already have check right after the random number is guessed but somehow it passes the check. If you figured out the problem, feel free to create a PR or open an Issue here.
Demo
The most updated version of this code can be found in my GitHub, feel free to contribute new features or improvements by opening PRs.
Sources:
Tyler, C. (n.d.). Software portability and optimization. matrix.senecapolytechnic.ca. https://matrix.senecapolytechnic.ca/~chris.tyler/wiki/doku.php?id=spo600:start
Subscribe to my newsletter
Read articles from Steven David Pillay directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by