RE-POST February: Lab 2

This post will be about an interesting mini-project and its implementation.

6502 Math and String

Criteria to be met

  1. Must work in 6502 Emulator.
  2. Must output to the character screen as well as the graphics screen.
  3. Must accept user input from the keyboard.
  4. Must use some arithmetic/math instructions.

Simple Game Chosen: Maze

I have always been interested in games and have previously coded in C++ and JavaScript a simple maze game and wanted to try doing it in 6502. This was harder than I expected, especially since I wasn't familiar with the assembly language. There was a lot of time when I had to refer to the documentation to understand what the code does, outside of what we had gone through in the first lab.

The following links are the documentation that I used to understand the language:

At first, when I saw the code for "Place a Graphic on the Screen" and copy paste the provided code to the 6502 emulator, I thought this would be a good way to create my maze and that I would just need to create a collision for the walls. Therefore, while playing around with the code and deciding how my maze would look like, my code for the maze has become:

    

define HEIGHT 8

define WIDTH 8

drawMaze:

    lda #$21

    sta POINT_MAZE_DRAWN_LOW

    lda #$02

    sta POINT_MAZE_DRAWN_HIGH

    lda #$00

    sta DRAW_MAZE

    ldx #$00

    ldy #$00

draw:

    lda maze_data,x

    sta (POINT_MAZE_DRAWN_LOW), y

    inx

    iny

    cpy #WIDTH

    bne draw

    inc DRAW_MAZE

    lda #HEIGHT

    cmp DRAW_MAZE

    beq done

    lda POINT_MAZE_DRAWN_LOW

    clc

    adc #$20

    sta POINT_MAZE_DRAWN_LOW

    lda POINT_MAZE_DRAWN_HIGH

    adc #$00

    sta POINT_MAZE_DRAWN_HIGH

    ldy #$00

    beq draw

maze_data:

    dcb 01,01,00,01,01,01,10,00

    dcb 00,01,00,01,00,00,00,00

    dcb 01,01,00,01,01,00,01,00

    dcb 01,00,00,00,01,01,01,00

    dcb 01,01,01,00,00,00,01,00

    dcb 00,00,01,00,01,01,01,00

    dcb 00,01,01,01,01,00,01,01

    dcb 01,01,00,00,00,00,00,01

Afterward, I tried the "Etch-a-SketchTM Style Drawing" code and saw that I could use this logic to create my player path:

    

define CURRENT_ROW $20

define CURRENT_COL $21

define DRAW_MAZE $22

define POINT_MAZE_DRAWN_LOW $14

define POINT_MAZE_DRAWN_HIGH $15

define POINT_PLAYER_LOW $10

define POINT_PLAYER_HIGH $11

define POINT_TARGET_LOW $12

define POINT_TARGET_HIGH $13

define PATH $04

define PLAYER $0d


gameInstruction:

    ldy #$00

pHelpLoop:

    lda help,y

    beq done

    sta $f000,y

    iny

    bne pHelpLoop

gameInitialization:

    lda #$01

    sta CURRENT_ROW

    sta CURRENT_COL

    rts

gameLoop:

    jsr updatePosition

    jsr getInput

    jsr checkCollision

    ldx #$00

    stx $ff

    jmp gameLoop


updatePosition:

    ldy CURRENT_ROW

    lda table_low,y

    sta POINT_PLAYER_LOW

    lda table_high,y

    sta POINT_PLAYER_HIGH

    ldy CURRENT_COL    

    lda #PLAYER

    sta (POINT_PLAYER_LOW),y

    rts

getInput:

    lda $ff

    cmp #$80  

    bmi getInput

    cmp #$84

    bpl getInput

    pha    

    lda #PATH  

    sta (POINT_PLAYER_LOW),y

    pla    

    cmp #$80   

    bne checkRight

    dec CURRENT_ROW    

    rts

checkRight:

    cmp #$81

    bne checkDown

    inc CURRENT_COL

    rts

checkDown:

    cmp #$82

    bne checkLeft

    inc CURRENT_ROW

    rts

checkLeft:

    cmp #$83

    bne done

    dec CURRENT_COL

    rts

done:

    rts

help:

    dcb "T","o",32,"p","l","a","y",44,32,"u","s","e",32

    dcb "t","h","e",32,"a","r","r","o","w",32,"k","e","y","s"

    dcb 00


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

Now that I have both my maze map and my player, I have to add the wall collision and I will be using the ROM routines to clean and initialize the screen:

  

define SCINIT $ff81

jsr gameInstruction

jsr drawMaze

jsr gameInitialization

jsr gameLoop

checkCollision:

    ldy CURRENT_ROW

    lda table_low,y

    sta POINT_TARGET_LOW

    lda table_high,y

    sta POINT_TARGET_HIGH

    ldy CURRENT_COL

    lda (POINT_TARGET_LOW),y

    cmp #$01

    beq done

    cmp #$04

    beq done

    cmp #$0a

    beq gameComplete

    lda #$00

    sta (POINT_TARGET_LOW),y

    lda $ff

    cmp #$80

    bne ifRight

    inc CURRENT_ROW

    rts

ifRight:

    cmp #$81

    bne ifDown

    dec CURRENT_COL

    rts

ifDown:

    cmp #$82

    bne ifLeft

    dec CURRENT_ROW

    rts

ifLeft:

    cmp #$83

    bne done

    inc CURRENT_COL

    rts

gameComplete:

    jsr SCINIT

    ldy #$00

pGameComplete:

    lda complete,y

    beq done

    sta $f000,y

    iny

    bne pGameComplete

    brk

complete:

    dcb "C","o","n","g","r","a","t", "u", "l", "a", "t", "i", "o", "n", "!","!","!",32

    dcb "T","h","e",32,"g","a","m","e",32,"i","s",32,

    dcb "c","o","m","p","l","e","t","e","d","."

    dcb 00

Furthermore, I have leveraged the code in "Place a Message on the Character Display" to instruct the player how to play the game and a message at the end, when the game is completed.

After coding the maze map and player path separately because the code started to get too big, and scrolling up and down was inconvenient in the 6502 emulator. Now that I have the maze map with collision and the player path working individually, I need to combine them together. After, a couple of trial and error, I have realized that there is an order of how the code has to be placed. Such as when I put the maze map at the beginning, nothing is being shown, however leaving it at the end, would work correctly. Here is the final code for my simple maze game, using the 6502 emulator:


  
define CURRENT_ROW $20
define CURRENT_COL $21
define DRAW_MAZE $22 
define POINT_MAZE_DRAWN_LOW $14
define POINT_MAZE_DRAWN_HIGH $15
define POINT_PLAYER_LOW $10
define POINT_PLAYER_HIGH $11
define POINT_TARGET_LOW $12
define POINT_TARGET_HIGH $13
define PATH $04
define PLAYER $0d
define HEIGHT 8
define WIDTH 8
define SCINIT $ff81

    jsr gameInstruction
    jsr drawMaze
    jsr gameInitialization
    jsr gameLoop

gameInstruction: 
ldy #$00
pHelpLoop: 
lda help,y
beq done
sta $f000,y
iny
bne pHelpLoop

gameInitialization: 
lda #$01
sta CURRENT_ROW
sta CURRENT_COL
rts

gameLoop: 
jsr updatePosition
jsr getInput
jsr checkCollision
ldx #$00
stx $ff
jmp gameLoop

updatePosition: 
ldy CURRENT_ROW
lda table_low,y
sta POINT_PLAYER_LOW
lda table_high,y
sta POINT_PLAYER_HIGH
ldy CURRENT_COL     
    lda #PLAYER
    sta (POINT_PLAYER_LOW),y
    rts

getInput: 
lda $ff
    cmp #$80   
    bmi getInput
    cmp #$84
    bpl getInput
pha     
    lda #PATH   
    sta (POINT_PLAYER_LOW),y
    pla     
    cmp #$80    
    bne checkRight
dec CURRENT_ROW     
    rts

checkRight: 
cmp #$81 
bne checkDown
inc CURRENT_COL
rts

checkDown: 
cmp #$82
bne checkLeft
inc CURRENT_ROW 
rts

checkLeft: 
cmp #$83
bne done
dec CURRENT_COL
rts

done: 
rts

checkCollision: 
ldy CURRENT_ROW 
lda table_low,y
sta POINT_TARGET_LOW
lda table_high,y
sta POINT_TARGET_HIGH
ldy CURRENT_COL     
    lda (POINT_TARGET_LOW),y
    cmp #$01
    beq done
    cmp #$04
    beq done
    cmp #$0a
    beq gameComplete
    lda #$00
    sta (POINT_TARGET_LOW),y
    lda $ff
    cmp #$80    
    bne ifRight
    inc CURRENT_ROW     
    rts

ifRight: 
cmp #$81 
bne ifDown
    dec CURRENT_COL     
    rts

ifDown: 
cmp #$82 
bne ifLeft
    dec CURRENT_ROW     
    rts
ifLeft:
cmp #$83 
bne done
    inc CURRENT_COL     
    rts

gameComplete: 
jsr SCINIT
ldy #$00
 
pGameComplete: 
lda complete,y
beq done
sta $f000,y
iny
bne pGameComplete
brk

drawMaze: 
lda #$21 
sta POINT_MAZE_DRAWN_LOW 
lda #$02
sta POINT_MAZE_DRAWN_HIGH
    lda #$00    
    sta DRAW_MAZE
    ldx #$00    
    ldy #$00
    
draw: 
lda maze_data,x
sta (POINT_MAZE_DRAWN_LOW), y
inx
iny
cpy #WIDTH 
bne draw 
    inc DRAW_MAZE   
    lda #HEIGHT
    cmp DRAW_MAZE   
    beq done
    lda POINT_MAZE_DRAWN_LOW
    clc
    adc #$20    
    sta POINT_MAZE_DRAWN_LOW  
    lda POINT_MAZE_DRAWN_HIGH
    adc #$00
    sta POINT_MAZE_DRAWN_HIGH
    ldy #$00    
    beq draw            

help:
dcb "T","o",32,"p","l","a","y",44,32,"u","s","e",32
dcb "t","h","e",32,"a","r","r","o","w",32,"k","e","y","s"
dcb 00

complete:
dcb "C","o","n","g","r","a","t", "u", "l", "a", "t", "i", "o", "n", "!","!","!",32
dcb "T","h","e",32,"g","a","m","e",32,"i","s",32,
dcb "c","o","m","p","l","e","t","e","d","."
dcb 00

maze_data:
dcb 01,01,00,01,01,01,10,00
dcb 00,01,00,01,00,00,00,00
dcb 01,01,00,01,01,00,01,00
dcb 01,00,00,00,01,01,01,00
dcb 01,01,01,00,00,00,01,00
dcb 00,00,01,00,01,01,01,00
dcb 00,01,01,01,01,00,01,01
dcb 01,01,00,00,00,00,00,01

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
After assembling the code and pressing on "Run":






When the player has reached their goal:

Comments

Popular posts from this blog