Introduction
After learning a lot about assembly, getting familiar with the instructions for the 6502 8bit processor, and experimenting with different exercises – it is time to create something of my own. For this lab, I chose to create a maze game. The process of figuring out how to make everything work, in the little time I had, was complicated. I used snippets from the examples provided by Professor Chris Tyler, and what I’ve learned from the Software Portability & Optimization course so far, to come up with a considerably complex implementation of the popular game. In this blog, I will go over the steps I’ve taken to implement this game, and I will discuss how it can be further improved in the future.
Requirements
The instructions are to create a program that:
- Your program must work in the 6502 Emulator
- You must output to the character screen as well as the graphics (bitmapped) screen.
- You must accept user input from the keyboard in some form.
- You must use some arithmetic/math instructions (to add, subtract, do bitwise operations, or rotate/shift).
The maze game neatly fits these requirements – involving character output for instructions, character input to record movements, and arithmetic/math when calculating the position of the player after every move. To make the program interesting, I also planned to implement these rules:
- The player will lose, meaning the game would stop, if they “bumps” into a maze wall/border. They always have to move within the given path. This can make the game harder, as the player needs to focus to not make the wrong move.
- Player must be able to choose between a hard/easy mode, for variety and allowing easy testing. Adds more complexity to the game.
- Player should be able to restart the game at any point, and continue playing smoothly without restarting the program – after a win, or a lose.
Examples & Tools
After analyzing the resources available about 6502 tools, I found a few examples of code that proved very useful in the development of the maze game:
ROM Routines
Our 6502 Emulator has a few useful ROM Routines, that mimic some of the built-in firmware found in the physical systems. I recognized that for my implementation, I would use:
- SCINIT $ff81 – Initialize and clear the character display
- CHRIN $ffcf – Input one character from keyboard (returns A (that is, the return value will be placed in the Accumulator register))
- CHROUT $ffdw – Outputs one character (A) to the screen at the current cursor position. Screen will wrap/scroll appropriately. Printable characters and cursor codes ($80/$81/$82/$83 for up/right/down/left) as well as RETURN ($0d) are accepted. Printable ASCII codes with the high bit set will be printed in reverse video.
Note: these descriptions were pulled, verbatim, from the website.
Etch-a-Sketch™
To start my ideation phase, I loaded the “Etch-a-Sketch™ Style Drawing” program, provided in the course examples. I immediately saw some similarities between the functionalities of this program, and some of the requirements for my maze game:
- A cursor, or “dot”, moving around the screen, leaving a trail on every “visited” pixel.
- The cursor is controller by user keyboard input.
- Help/instructions message printed to the console.
; This code is Copyright©2020-2024 Seneca College of Applied Arts and
; Technology. Each of these programs is free software; you can
; redistribute them and/or modify them under the terms of the
; 'GNU|General Public License as published by the Free Software
; Foundation; either version 2 of the License, or (at your option)
; any later version.
; zero-page variable locations
define ROW $20 ; current row
define COL $21 ; current column
define POINTER $10 ; ptr: start of row
define POINTER_H $11
; constants
define DOT $01 ; dot colour
define CURSOR $04 ; black colour
ldy #$00 ; put help text on screen
print: lda help,y
beq setup
sta $f000,y
iny
bne print
setup: lda #$0f ; set initial ROW,COL
sta ROW
sta COL
draw: lda ROW ; ensure ROW is in range 0:31
and #$1f
sta ROW
lda COL ; ensure COL is in range 0:31
and #$1f
sta COL
ldy ROW ; load POINTER with start-of-row
lda table_low,y
sta POINTER
lda table_high,y
sta POINTER_H
ldy COL ; store CURSOR at POINTER plus COL
lda #CURSOR
sta (POINTER),y
getkey: lda $ff ; get a keystroke
beq getkey
ldx #$00 ; clear out the key buffer
stx $ff
cmp #$43 ; handle C or c
beq clear
cmp #$63
beq clear
cmp #$80 ; if not a cursor key, ignore
bmi getkey
cmp #$84
bpl getkey
pha ; save A
lda #DOT ; set current position to DOT
sta (POINTER),y
pla ; restore A
cmp #$80 ; check key == up
bne check1
dec ROW ; ... if yes, decrement ROW
jmp done
check1: cmp #$81 ; check key == right
bne check2
inc COL ; ... if yes, increment COL
jmp done
check2: cmp #$82 ; check if key == down
bne check3
inc ROW ; ... if yes, increment ROW
jmp done
check3: cmp #$83 ; check if key == left
bne done
dec COL ; ... if yes, decrement COL
clc
bcc done
clear: lda table_low ; clear the screen
sta POINTER
lda table_high
sta POINTER_H
ldy #$00
tya
c_loop: sta (POINTER),y
iny
bne c_loop
inc POINTER_H
ldx POINTER_H
cpx #$06
bne c_loop
done: clc ; repeat
bcc draw
; these two tables contain the high and low bytes
; of the addresses of the start of each row
table_high:
dcb $02,$02,$02,$02,$02,$02,$02,$02
dcb $03,$03,$03,$03,$03,$03,$03,$03
dcb $04,$04,$04,$04,$04,$04,$04,$04
dcb $05,$05,$05,$05,$05,$05,$05,$05,
table_low:
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
; help message for the character screen
help:
dcb "A","r","r","o","w",32,"k","e","y","s"
dcb 32,"d","r","a","w",32,"/",32,"'","C","'"
dcb 32,"k","e","y",32,"c","l","e","a","r","s"
dcb 00
Other than the already-implemented functionalities, I saw a lot of versatility in the POINTER, ROW & COL implementation, using the table_high & table_low to store the high & low bytes of the addresses of every row start. Using this existing implementation will definitely move me forward in my progress.
Print Subroutine
I also found an implementation of the Print Subroutine, included in the “Color Selector” example on the course github.
; Colour selector - live updates
; (C)2020 Chris Tyler - Seneca College
; Licensed under the GPLv2+ - see LICENSE file
....
; --------------------------------------------------------
; 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
Initial Implementation
Working off-of the Etch-a-Sketch™ example, I wanted to adjust it to first fill the screen with a specific background, and have the user trace the image.
Creating the Background/Maze
I used the PiskelApp website to draw the 32×32 pixel background, which would be used for my simple implementation of the game. I exported the spirtes I created as “c file”, which gave me a format that was easy to convert to DCB
. I divided the image into 4 parts, for each page, to make it faster drawing.
As you can see from the illustration, once the player “hit” the blue background, the game was over. I achieved this by loading the color of the “next” pixel on the accumulator, and comparing it with the background color.

define BKG $10
define FRG $11
define BRD $12
define SCORE $13 ; score address
; zero-page variable locations
define ROW $20 ; current row
define COL $21 ; current column
define POINTER $22 ; ptr: start of row
define POINTER_H $23
; constants
define DOT $01 ; dot colour
define CURSOR $04 ; black colour
ldy #$00 ; set y index
lda #$06
sta BKG
lda #$01
sta FRG
lda #$05
sta BRD
loop:
ldx circimg_1 , y
lda $0000, x
sta $0200,y ; color pixel on page
ldx circimg_2 , y
lda $0000, x
sta $0300,y
ldx circimg_3 , y
lda $0000, x
sta $0400,y
ldx circimg_4 , y
lda $0000, x
sta $0500,y
iny ; increment Y
bne loop
ldy #$00 ; put help text on screen
print: lda help,y
beq setup
sta $f000,y
iny
bne print
setup:
lda #$07 ; set initial ROW,COL
sta ROW
lda #$10
sta COL
lda #$00
tay
tax
draw:
lda ROW ; ensure ROW is in range 0:31
and #$1f
sta ROW
lda COL ; ensure COL is in range 0:31
and #$1f
sta COL
ldy ROW ; load POINTER with start-of-row
lda table_low,y
sta POINTER
lda table_high,y
sta POINTER_H
ldy COL ; store CURSOR at POINTER plus COL
lda (POINTER), y ; load the curr screen color at dot position
cmp BKG ; check if it's BKG (stored @ $0010)
beq game_lost ; if it's $10, lost the game.
inc SCORE ; score = number of "good" moves.
; store CURSOR at POINTER plus COL
lda #CURSOR
sta (POINTER),y
getkey: lda $ff ; get a keystroke
beq getkey
ldx #$00 ; clear out the key buffer
stx $ff
cmp #$43 ; handle C or c
beq clear
cmp #$63
beq clear
pha ; save A
lda #DOT ; set current position to DOT
sta (POINTER),y
pla ; restore A
cmp #$77 ; check key == up
bne check1
dec ROW ; ... if yes, decrement ROW
jmp done
check1: cmp #$64 ; check key == right
bne check2
inc COL ; ... if yes, increment COL
jmp done
check2: cmp #$73 ; check if key == down
bne check3
inc ROW ; ... if yes, increment ROW
jmp done
check3: cmp #$61 ; check if key == left
bne done
dec COL ; ... if yes, decrement COL
clc
bcc done
clear: lda table_low ; clear the screen
sta POINTER
lda table_high
sta POINTER_H
ldy #$00
tya
c_loop: sta (POINTER),y
iny
bne c_loop
inc POINTER_H
ldx POINTER_H
cpx #$06
bne c_loop
done: clc ; repeat
bcc draw
game_lost:
ldy #$00
lda #$00
print_game_over:
sta $0200,y ; color pixel on page
sta $0300,y
sta $0400,y
sta $0500,y
iny ; increment Y
bne print_game_over
; these two tables contain the high and low bytes
; of the addresses of the start of each row
table_high:
dcb $02,$02,$02,$02,$02,$02,$02,$02
dcb $03,$03,$03,$03,$03,$03,$03,$03
dcb $04,$04,$04,$04,$04,$04,$04,$04
dcb $05,$05,$05,$05,$05,$05,$05,$05,
table_low:
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
; help message for the character screen
help:
dcb "A","r","r","o","w",32,"k","e","y","s"
dcb 32,"d","r","a","w",32,"/",32,"'","C","'"
dcb 32,"k","e","y",32,"c","l","e","a","r","s"
dcb 00
circimg_1:
DCB $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12
DCB $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12
DCB $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $11, $11, $11, $11, $11, $11, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $12, $12, $12
circimg_2:
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $10, $10, $11, $11, $11, $10, $10, $10, $10, $11, $11, $11, $10, $10, $10, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $11, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $11, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $11, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $11, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $11, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $11, $10, $10, $10, $10, $12, $12, $12
circimg_3:
DCB $12, $12, $12, $10, $10, $10, $10, $11, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $11, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $11, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $11, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $11, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $11, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $10, $10, $10, $10, $11, $11, $10, $10, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $10, $10, $11, $11, $11, $10, $10, $10, $10, $11, $11, $11, $10, $10, $10, $10, $10, $10, $10, $10, $12, $12, $12
circimg_4:
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $11, $11, $11, $11, $11, $11, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $12, $12, $12
DCB $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12
DCB $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12
DCB $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12, $12
Improvements & Maze Implementation
Now that my “circle” demo was working, I could move on to the maze implementation. Initially, I wanted to load a couple maze games for the user to choose from, but eventually opted for simplicity, and chose 2 maze maps – in 2 modes, easy and hard.
Generating the Maze
I used a maze generator which has the option to dictate the dimensions. There was a small issue with getting the 32×32 maze to work, since it was only producing odd-number dimensions, but using Piskel I was able to fix the maze, and convert it to DCB
format.
Selecting Difficulty Level
To implement selecting the difficulty level, I first printed the instructions – to type in 1, or 2. If the key typed is neither, I wait for the input to be valid, and then I display the cooresponding maze on the screen. Then I continue to the actual play fo the game.
setup:
lda #$00
sta COL ; set initial COL, ROW will depend on mode
tay
tax
lda #$04
fill_screen:
sta $0200,y
sta $0300,y
sta $0400,y
sta $0500,y
iny
bne fill_screen
start_menu:
jsr SCINIT ; Erase the screen on setup
jsr PRINT ; Usage instructions, prompt for difficulty level
dcb "M","a","z","e",32,"G","a","m","e","!",$0D
dcb "*",32,32,"G","e","t",32,"t","o",32,"t","h","e",
32,"g","r","e","e","n",32,"f","i","n","i","s","h",$0D
dcb "*",32,32,"D","o",32,"n","o","t",32,"t","o","u","c","h"
,32,"t","h","e",32,"m","a","z","e",32,"w","a","l","l","s",$0D
dcb "*",32,32,"U","s","e",32,"W","A","S","D",32,"t","o",
32,"m","o","v","e",$0D
dcb "S","e","l","e","c","t",32,"d","i","f","f","i","c","u",
"l","t","y",32,"l","e","v","e","l",":",$0D
dcb "1",32,"-",32,"E","a","s","y",$0D
dcb "2",32,"-",32,"H","a","r","d",$0D
dcb "E","n","t","e","r",32,"t","h","e",32,
"n","u","m","b","e","r",":",00
get_input:
JSR CHRIN ; Use ROM routine to get single character
check_easy: ; Check if easy mode selected, display easy maze
cmp #$31 ; key == 1
bne check_hard
jsr PRINT ; Print selected mode
dcb $0D,"N","o","w",32,"p","l","a","y","i","n","g",32,
"E","A","S","Y",32,"l","e","v","e","l",46,32
dcb "P","r","e","s","s",32,"X",32,"t","o",32,"r","e","s",
"t","a","r","t",46,00
lda #$02 ; Set starting row to 2
sta ROW
clc
ldy #$00
draw_easy_maze: ; Draw easy maze on bitmap screen
lda MAZE_EASY_1, y
sta $0200,y
lda MAZE_EASY_2, y
sta $0300,y
lda MAZE_EASY_3, y
sta $0400,y
lda MAZE_EASY_4, y
sta $0500,y
iny
bne draw_easy_maze
beq play
check_hard: ; Check if hard mode selected, display hard maze
cmp #$32 ; key == 2
bne get_input ; get input again, if not right.
jsr PRINT
dcb $0D,"N","o","w",32,"p","l","a","y","i","n","g",
32,"H","A","R","D",32,"l","e","v","e","l",32
dcb "P","r","e","s","s",32,"X",32,"t","o",32,"r","e","s","t",
"a","r","t",46,00
lda #$01 ; Set starting row to 1
sta ROW
clc
ldy #$00
draw_hard_maze: ; Draw hard maze on bitmap screen
lda MAZE_HARD_1, y
sta $0200,y
lda MAZE_HARD_2, y
sta $0300,y
lda MAZE_HARD_3, y
sta $0400,y
lda MAZE_HARD_4, y
sta $0500,y
iny
bne draw_hard_maze
beq play
Registering Player Moves
For my game, I decided to improve upon the Etch-a-Sketch™ code, to have the player move with WASD keys. The arrow keys worked great, but hitting the “down” arrow sometimes caused the screen to scroll down, which became very annoying. I also decided to use “X” to restart the game, in which case the program would jump back to “setup”, initializing the screen and reprompting the level.
getkey:
lda $ff ; get a keystroke
beq getkey
ldx #$00 ; clear out the key buffer
stx $ff
cmp #$58 ; handles X or x
beq restart
cmp #$78
beq restart
pha ; save A
lda #PATH_CLR ; color the "path"
sta (POINTER),y
pla ; restore A
cmp #$77 ; 'W' or 'w'
beq move_up
cmp #$57
beq move_up
cmp #$64 ; 'D' or 'd'
beq move_right
cmp #$44
beq move_right
cmp #$73 ; 'S' or 's'
beq move_down
cmp #$53
beq move_down
cmp #$61 ; 'A' or 'a'
beq move_left
cmp #$41
beq move_left
jmp done ; If no movement, do nothing
move_up:
dec ROW
jmp done
move_right:
inc COL
jmp done
move_down:
inc ROW
jmp done
move_left:
dec COL
jmp done
restart:
jsr setup
done:
jsr draw
Checking Win/Loss
To check if the player won (reached the finish), or lost (reached a maze wall), I used the same technique as the demo circle game, checking the color of the pixel. In the circle game, I used the backgorund BKG
and border BRD
colors as variables, stored in memory, but to simplify the game I decided to set static values – black (#$00
) for walls/borders, white (#$01
) for the maze path, and green (#$05
) for the maze finish. In short, I check if the current pointer stores black, if it does – player loses. If the player reached green, the player won. In either case, a message is displayed on the character screen, and the bitmap display gets filled with a graphic (GAME OVER for loss, and a blank GREEN screen for winning – to be improved later).
draw:
; Ensure ROW & COL are in range 0:31
lda ROW
and #$1f
sta ROW
lda COL
and #$1f
sta COL
; Update POINTER with the location
ldy ROW
lda table_low,y
sta POINTER
lda table_high,y
sta POINTER_H
; Check if we hit wall or finish
ldy COL
lda (POINTER), y ; load color at curr position
cmp #BORDER_CLR ; check if it's a border color (lost)
bne check_won
; PRINT GAME LOST MESSAGE
ldy #$00
print_game_over:
lda game_over_screen1 , y
sta $0200,y ; color pixel on page
lda game_over_screen2 , y
sta $0300,y
lda game_over_screen3 , y
sta $0400,y
lda game_over_screen4 , y
sta $0500,y
iny ; increment Y
bne print_game_over
jsr SCINIT
jsr PRINT
dcb $0D,"Y","o","u",32,"l","o","s","t","!","!",$0D,00
jsr wait_for_restart
check_won:
cmp #FINISH_CLR ; check if it's finish color (won)
bne continue_game
; PRINT GAME WON MESSAGE
lda #$05
ldy #$00
print_game_won:
sta $0200,y
sta $0300,y
sta $0400,y
sta $0500,y
iny
bne print_game_won
jsr SCINIT
jsr PRINT
dcb $0D,"Y","o","u",32,"w","o","n","!","!","!",$0D,00
jsr wait_for_restart
continue_game:
; store DOT_CLR at the current location
lda #DOT_CLR
sta (POINTER),y
...
wait_for_restart:
jsr PRINT
dcb $0D,"P","r","e","s","s",32,"a","n","y",32,"k","e","y",
32,"t","o",32,"R","E","S","T","A","R","T",".",".",".",00
wait_key_press:
lda $ff ; get a keystroke
beq wait_key_press
jmp setup
Final Results & Reccomendations

The final result is a fully functioning, small version of a maze game. You can choose a difficulty level, you lose when touching the walls, and can restart anytime. Using the example “Etch-a-Sketch™” code was very helpful in achieving this result, but the custom functionalities were still hard to implement. I ran into many errors and spent a lot of time debugging the code (which is very hard in assembly), but the final result is great nonetheless! There are some things I would improve, given the time and resources:
- Adding a better “winning” screen
- More efficient and readable code, possibly create a subroutine for filling the screen with an image.
- More maze options, and a better selection process, similar to the “color-selector” example.
- Some score method, to output score.
- Have smaller mazes, less than 32×32, to be displayed in the middle of the screen – to save space.
Download the full maze game source code to see the final result, including the maze image data:
Leave a Reply