In this blog, I want to demonstrate a straightforward game created using the 6502 Assembly language.
This game uses the 6502 bitmapped screen and the text IO to communicate with the user.
Through the I/O we prompt the user for various inputs and occasionally send messages.
1. Game Process
This simple game of dice is 100% based on luck, so don't rely on your skills!
There are going to be 2 dice, one for the user (player), and another one for the CPU which is the player's competitor.
Using the 6502 random number generator which is saved in the $00FE memory location we generate random numbers for the player and the CPU. Whoever gets a bigger number wins.
In simpler words, roll the dice for the bigger number or lose!
2. Game Instruction
At the beginning of the game, a message will be shown that says: "Enter 1 to play and 0 to quit - During the game enter 1 to continue or 0 to quit!"
And that is pretty much the whole instruction the player needs.
After entering 1 for the first time your dice value will be visible on the bitmapped screen using the number of pixels.
Next, the player can enter 1 to display the value of the CPU's rolled dice!
If the player's number is bigger, the "You WON" message will appear on the character screen.
If the CPU's number is bigger, the "You LOST" message will appear on the character screen.
If the numbers are the same, the "Draw" message will appear on the screen!
3. Analyzing the Code
This is the complete code for this game in the 6502 emulator:
; 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
; Memory Locations
define PLAYER $99
define CPU $98
define STATUS $97
define DICE $96
JSR SCINIT
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
LDX #0
LDA #0
STA PLAYER
STA CPU
STA STATUS
LDY #$00
JSR PROMPT_CHAR
PROMPT_CHAR:
LDA PROMPT_TEXT,Y
BEQ DONE_PROMPT
JSR CHROUT
INY
BNE PROMPT_CHAR
PROMPT_TEXT:
dcb "E","n","t","e","r",32,"1",32,"t","o",32,"r","o","l"
dcb "l",32,"t","h","e",32,"D","I","C","E",32,"o","r",32,"0", 32
dcb "t","o",32,"Q","U","I","T",$0d
dcb 00
DONE_PROMPT:
JSR GET_CHAR
BRK
START1:
JMP START
GET_CHAR:
LDA #160 ; CODE FOR BLACK SPACE
JSR CHROUT
LDA #$83 ; CODE TO MOVE CURSOR LEFT ONE POSITION
JSR CHROUT
JSR CHRIN
CMP #$00
BEQ GET_CHAR
CMP #48 ; IF LOWER THAN 48 GET ANOTHER CHAR
BCC GET_CHAR
CMP #50 ; IF 50 OR HIGHER GET ANOTHER CHAR
BCS GET_CHAR
JSR CHROUT ; PRINT RECIEVED INPUT ON THE SCREEN
CMP #48
BEQ QUIT2 ; QUIT IF INPUT = 0
CMP #49
BEQ PLAY1 ; START GAME IF INPUT = 1
JMP GET_CHAR
PROMPT_WON: ; DISPLAY TEXT WHEN PLAYER WIN THE GAME
LDA WON_TEXT,Y
BEQ START1
JSR CHROUT
INY
BNE PROMPT_WON
PROMPT_LOST: ; DISPLAY TEXT WHEN PLAYER LOSE THE GAME
LDA LOST_TEXT,Y
BEQ START1
JSR CHROUT
INY
BNE PROMPT_LOST
PROMPT_DRAW: ; ; DISPLAY TEXT WHEN IT DRAWS
LDA DRAW_TEXT,Y
BEQ START1
JSR CHROUT
INY
BNE PROMPT_DRAW
LOST_TEXT:
dcb $0d,"Y","o","u",32,"L","O","S","T",$0d
dcb 00
WON_TEXT:
dcb $0d,"Y","o","u",32,"W","O","N",$0d
dcb 00
DRAW_TEXT:
dcb $0d,"D","R","A","W",$0d
dcb 00
QUIT2:
JMP QUIT1 ; SHORT PATH TO QUIT1 BECAUSE OF 128 LIMIT
PLAY1:
JMP PLAY ; SHORT PATH TO PLAY BECAUSE OF 128 LIMIT
CONTINUE:
LDA #160 ; CODE FOR BLACK SPACE
JSR CHROUT
LDA #$83 ; CODE TO MOVE CURSOR LEFT ONE POSITION
JSR CHROUT
JSR CHRIN
CMP #$00
BEQ CONTINUE
CMP #48 ; IF LOWER THAN 48 GET ANOTHER CHAR
BCC CONTINUE
CMP #50 ; IF 50 OR HIGHER GET ANOTHER CHAR
BCS CONTINUE
JSR CHROUT ; PRINT RECIEVED INPUT ON THE SCREEN
CMP #48
BEQ QUIT1 ; QUIT IF INPUT = 0
CMP #49
BEQ CLEAR_SCREEN ; CONTINUE GAME IF INPUT = 1
RTS
CLEAR_SCREEN:
LDA #$00 ; BLACK COLOR CODE
STA ($40),Y
INY
STA ($40),Y
INY
CPY DICE ; AS LONG AS PREVIOUS DICE PIXELS ARE NOT CLEARED
BNE CLEAR_SCREEN
RTS
WON:
LDY #$00
JSR PROMPT_WON ; PLAYER WON
LOST: ; PLAYER LOST
LDY #$00
JSR PROMPT_LOST
DRAW: ; DRAW MATCH
LDY #$00
JSR PROMPT_DRAW
ROLL_DICE: ; GET RANDOM NUMBERS BETWEEN 1 AND 6
LDA $00FE
CMP #1
BCC ROLL_DICE
CMP #7
BCS ROLL_DICE
RTS
QUIT1:
JMP QUIT ; SHORT PATH TO QUIT1 BECAUSE OF 128 LIMIT
PLAY:
JSR ROLL_DICE
STA PLAYER ; STORE RANDOM NUMBER IN PLAYER VAR
STA DICE ; STORE RANDOM NUMBER IN DICE VAR
LDA DICE
CLC
ADC DICE
STA DICE ; DICE = DICE + DICE
LDY #$00
JSR DISPLAY_DICE ; DISPLAY THE VALUE OF DICE USING PIXELS
LDY #$00
JSR CONTINUE ; WAIT FOR USER INPUT (1 = CONTINUE, 2 = QUIT)
JSR ROLL_DICE
STA CPU
STA DICE
LDA DICE
CLC
ADC DICE
STA DICE
LDY #$00
JSR DISPLAY_CPU ; DISPLAY THE VALUE OF DICE USING PIXELS
LDY #$00
JSR CONTINUE ; WAIT FOR USER INPUT (1 = CONTINUE, 2 = QUIT)
JSR RESULT ; DETERMINE THE RESULT OF THE GAME
RESULT:
LDA PLAYER
SEC
SBC CPU ; A = PLAYER - CPU
CMP #0 ; PLAYER = CPU -> A = 0 -> DRAW
BEQ DRAW
CMP #6
BCS LOST ; PLAYER - CPU < 0 -> OVERFLOWS -> A > 5 -> LOST
JSR WON ; PLAYER - CPU > 0 -> 0 < A < 5 -> WON
DISPLAY_DICE:
LDA #$07 ; LOAD ACCUMULATOR WITH CYAN COLOR CODE
STA ($40),Y
LDA #$00 ; LOAD ACCUMULATOR WITH BLACK COLOR CODE (TO MAKE SPACES)
INY ; INCREASE Y REGISTER
STA ($40),Y
INY
CPY DICE ; UNTIL DICE VALUE IS REACHED
BNE DISPLAY_DICE
RTS
DISPLAY_CPU:
LDA #$0A ; LOAD ACCUMULATOR WITH RED COLOR CODE
STA ($40),Y
LDA #$00 ; LOAD ACCUMULATOR WITH BLACK COLOR CODE (TO MAKE SPACES)
INY ; INCREASE Y REGISTER
STA ($40),Y
INY
CPY DICE ; UNTIL DICE VALUE IS REACHED
BNE DISPLAY_CPU
RTS
QUIT: ; QUIT
The first thing that you notice about this code is how such a simple game has so many lines of code. Another thing that came to my mind is that is this going to perform faster than the same code in different languages which may be written in less than 15 lines.
Initially, I had the idea to create a BlackJack game, but a huge challenge is the 128 range for jumping to subroutines. After around 60% of the coding part, I figured that no matter how I organize the routines, I still can't fit them in the valid range that I can access from other required routines!
I also had a similar issue in this program, but I came up with a trick to create empty routines that are closer to the calling command and would only jump to the main desired routines.
As you can see in the code these routines are colored in Green!
Disclaimer: The lines of this code that are marked with Red color have been copied from my SPO600 course material in Seneca College and have been written and licensed by my professor Chris Tyler.
In this game, you can play as many times as you desire just by pressing 1.
Pressing 0 at any time will quit the game.
4. Gameplay Video
5. Code Summary
I figured that understanding the process of a program can be difficult by reading the code so here is a summary:
The prompt text output.
Prompt for User input.
If input = 1 play. If input = 0 quit. Any other input will not be accepted.
Generating a Random Number for the player.
Storing the number in PLAYER and DICE variables.
Displaying the dice value on the bitmapped screen with cyan color.
Waiting for the user to view the display and continue by entering 1 or quitting by entering 0.
Generating a Random Number for the CPU.
Storing the number in CPU and DICE variables.
Displaying the dice value on the bitmapped screen with cyan color.
Waiting for the user to view the display and continue by entering 1 or quitting by entering 0.
Determine the result by subtracting PLAYER from CPU. If PLAYER > CPU then the result should be between 0, and 6 which means the user is the winner of the game. (Not including these numbers) If PLAYER == CPU then the result of the subtraction will be 0 which means DRAW. If CPU > PLAYER, the subtraction result will logically be a negative value, but since we don't have negative values, it is going to overflow to FF value, therefor if the result is 6 or bigger it means that the user lost the game.
Print the result based on step 12.
Jump to Step 1.
6. Conclusion
Writing games and programs using 6502 Assembly language may be challenging but at the same time can be fun and obsessive. I believe that writing this piece of code made me understand the subject and commands of this language much better than before!
Comments