top of page
Writer's picturearman valaee

6502 Assembly Dice Game

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:


  1. The prompt text output.

  2. Prompt for User input.

  3. If input = 1 play. If input = 0 quit. Any other input will not be accepted.

  4. Generating a Random Number for the player.

  5. Storing the number in PLAYER and DICE variables.

  6. Displaying the dice value on the bitmapped screen with cyan color.

  7. Waiting for the user to view the display and continue by entering 1 or quitting by entering 0.

  8. Generating a Random Number for the CPU.

  9. Storing the number in CPU and DICE variables.

  10. Displaying the dice value on the bitmapped screen with cyan color.

  11. Waiting for the user to view the display and continue by entering 1 or quitting by entering 0.

  12. 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.

  13. Print the result based on step 12.

  14. 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!


24 views0 comments

Comments


bottom of page