In the last post, we created a simple 6502 assembly code that generates a maze for a player to explore. Today, we are going to build codes on top of it in order to implement the rest of objectives.
For this game, we need to accomplish 3 more objectives as following:
1. The game must draw the maze in the bitmapped screen. (Done!)
- A player must be able to use the keyboard to control.
- A player must find a route to reach to the goal within the maze in order to win the game.
- A player cannot goes through the wall.
; zero-page variables define ROW $20 ; current row define COL $21 ; current column define DRAWN_ROW $22 ; number of drawn rows define MAZE_L $14 ; a pointer that points to where the maze will define MAZE_H $15 ; be drawn define PLAYER_L $10 ; a pointer that points to the player in the ; screen define PLAYER_H $11 define TARGET_L $12 ; a pointer that points to the target position define TARGET_H $13 ; where the player wants to proceed ; constants define PATH $03 ; path color define PLAYER $0e ; player color define HEIGHT 7 ; height of the maze define WIDTH 7 ; width of the maze ; ROM routine define SCINIT $ff81 ; initialize/clear screen jsr printHelp jsr drawMaze jsr gameInit jsr gameLoop printHelp: ldy #$00 ; print instructions on the screen pHelpLoop: lda help,y beq done sta $f000,y iny bne pHelpLoop gameInit: lda #$01 ; initialize ROW, COL to make the player sta ROW ; starting at $0221 of the screen sta COL rts gameLoop: jsr updatePosition jsr getkey jsr checkCollision ldx #$00 ; clear out the key buffer stx $ff jmp gameLoop updatePosition: ldy ROW ; load PLAYER pointer with ROW lda table_low,y sta PLAYER_L lda table_high,y sta PLAYER_H ldy COL ; place the player at (POINTER + COL) lda #PLAYER sta (PLAYER_L),y rts getkey: lda $ff ; get the input key cmp #$80 ; allow arrow keys only bmi getkey cmp #$84 bpl getkey pha ; save the accumulator lda #PATH ; set color of the current position to PATH sta (PLAYER_L),y pla ; restore accumulator cmp #$80 ; check key is up bne checkRight dec ROW ; ... if yes, decrement ROW rts checkRight: cmp #$81 ; check if key is right bne checkDown inc COL ; ... if yes, increment COL rts checkDown: cmp #$82 ; check if key is down bne checkLeft inc ROW ; ... if yes, increment ROW rts checkLeft: cmp #$83 ; check if key is left bne done dec COL ; ... if yes, decrement COL rts done: rts ; break out of a loop or subroutine checkCollision: ldy ROW ; load TARGET pointer with ROW lda table_low,y sta TARGET_L lda table_high,y sta TARGET_H ldy COL ; load the color from the target lda (TARGET_L),y; at (POINTER + COL) cmp #$01 beq done cmp #$03 beq done cmp #$0a beq gameComplete lda #$00 sta (TARGET_L),y lda $ff cmp #$80 ; if input key was up... bne ifRight inc ROW ; ... if yes, increment ROW rts ifRight: cmp #$81 ; if input key was right... bne ifDown dec COL ; ... if yes, decrement COL rts ifDown: cmp #$82 ; if input key was down... bne ifLeft dec ROW ; ... if yes, decrement ROW rts ifLeft: cmp #$83 ; if input key was left... bne done inc COL ; ... if yes, increment COL rts gameComplete: jsr SCINIT ldy #$00 ; print game completion message on the screen pGameComplete: lda complete,y beq done sta $f000,y iny bne pGameComplete brk drawMaze: lda #$21 ; a pointer pointing to the first pixel sta MAZE_L ; of the screen lda #$02 sta MAZE_H lda #$00 ; number of drawn rows sta DRAWN_ROW ldx #$00 ; maze data index ldy #$00 ; column index draw: lda maze_data,x sta (MAZE_L), y inx iny cpy #WIDTH ; compare with the number of WIDTH bne draw ; if not, keep drawing the column inc DRAWN_ROW ; increment the number of row lda #HEIGHT cmp DRAWN_ROW ; compare with the number of HEIGHT beq done lda MAZE_L clc adc #$20 ; add 32(0x0020) to increment the row sta MAZE_L ; of the pixel lda MAZE_H adc #$00 sta MAZE_H ldy #$00 ; reset the column index for the new row beq draw ; help text message help: dcb "P","l","a","y",32,"w","i","t","h",32,"a","r","r","o","w" dcb 32,"k","e","y","s",32,"t","o",32,"c","o","n","t","r","o","l",10 dcb 00 ; game complete message complete: dcb "Y","o","u",32,"b","e","a","t",32 dcb "t","h","e",32,"g","a","m","e","!" dcb 00 ; maze map data maze_data: dcb 01,00,01,00,01,01,01 dcb 01,01,01,00,00,00,01 dcb 00,00,01,00,01,00,01 dcb 01,00,01,00,01,01,01 dcb 01,00,01,00,01,00,01 dcb 01,00,01,00,01,00,01 dcb 01,01,01,01,01,00,10 ; 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
Let's walk through the code together. First of all, we print out the helpful instruction on the text screen with
printHelp subroutine. Then, we draw a maze using the
drawMaze subroutine we created from the last post. Having a maze for a player to explore on the screen, we need to first set the game state with the initial player position on the screen
$#0221. After that, we call the subroutine called
gameLoop which constantly loops itself.
gameLoop itself consists of a number of subroutines. The first one is
updatePosition. This subroutine loads the player pointer with the given the row and column information so that we can place the player on the screen. Afterwards, we call the
getKey subroutine to receive the player input from the keyboard. We limit the keyboard input by only accepting arrow keystrokes. Once we receive a key input, we update the number of column and row accordingly. Then, we check if the position the player wants to move is a wall by using the
checkCollision subroutine. If the player hits by the wall, we simply retract the move.
Once reaching to the goal, the screen will congratulate the player with the text message.
Making a simple maze using the 6502 assembly language definitely is harder and more time-consuming as compared to other high-level languages. The game we explored together is also not polished and needs a lot of improvements as well (such as having an alert when the player wants to proceed into the wall). we could use. Yet, this experience gives us a very meaningful insight as to how the game really works under the hood.
Top comments (0)