snake6502/src/game.asm

336 lines
7.8 KiB
NASM

SEG programSegment
statusPlay: ; do Game
; Check counter
ldx irqn
dex
stx irqn
beq irqsometime ; if counter is 0, then do these "rare" things
rts ; else do nothing and simply return
; as you can see, game actually runs at 12 Hz
irqsometime:
; Things that must be done only one in four interrupts (12 Hz)
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; If I am here, counter reached 0, so first reset it
ldx #4
stx irqn
; Get pressed key and decide snake direction
jsr $ffe4 ; Kernal routine GETIN
cmp #0
beq keybEndCheck ; if no key pressed, just skip this switch-case
ldx #$7f ; else, if key pressed, reset random variable
stx random
keybCheckA: ; check for press of key `A`
cmp #$41
bne keybCheckS ; if not pressed `A`, just skip to next key to check
lda direction ; else check if current direction is right
cmp #6
beq keybEndCheck ; if yes, you can't turn over yourself, so just skip to next key check
lda #4
sta direction ; else set direction to left and store new value
jmp keybEndCheck ; skip all other key tests
keybCheckS: ; simply re-do for S, D, W and other keys...
cmp #$53
bne keybCheckD
lda direction
cmp #8
beq keybEndCheck
lda #2
sta direction
jmp keybEndCheck
keybCheckD:
cmp #$44
bne keybCheckW
lda direction
cmp #4
beq keybEndCheck
lda #6
sta direction
jmp keybEndCheck
keybCheckW:
cmp #$57
bne keybCheckP
lda direction
cmp #2
beq keybEndCheck
lda #8
sta direction
jmp keybEndCheck
keybCheckP:
cmp #$50
bne keybEndCheck
lda #5
sta direction
jmp keybEndCheck
keybEndCheck:
; Get joystick status and decide snake direction
; Joystick register bits 4:0 => Fire,Right,Left,Down,Up
; 0 = Pressed; 1 = Idle
lda $dc00 ; CIA joystick port 2 register
ror ; rotate bit and put bit#0 in CF
tax ; store byte value for next key check
bcs joyCheckDown ; if CF = 1, then key was not depressed, so skip and check next...
; ... else key was depressed!
lda direction ; check for not overlapping direction (turn over yourself)
cmp #2
beq joyEndCheck
lda #8
sta direction
jmp joyEndCheck
joyCheckDown:
txa
ror
tax
bcs joyCheckLeft
lda direction
cmp #8
beq joyEndCheck
lda #2
sta direction
jmp joyEndCheck
joyCheckLeft:
txa
ror
tax
bcs joyCheckRight
lda direction
cmp #6
beq joyEndCheck
lda #4
sta direction
jmp joyEndCheck
joyCheckRight:
txa
ror
tax
bcs joyCheckFire
lda direction
cmp #4
beq joyEndCheck
lda #6
sta direction
jmp joyEndCheck
joyCheckFire: ; `Fire` joystick key used to pause game
txa
ror
tax
bcs joyEndCheck
lda #5
sta direction
joyEndCheck:
; Get direction and move head accordingly
lda direction
dirCheck2: ; check if direction is down...
cmp #2
bne dirCheck4 ; if not down, then skip and check next direction,
ldy snakeY ; else, direction is down, so get snakeY
iny ; increment snakeY (keep in mind that screen up/down coordinates are reversed)
sty snakeY ; update snakeY
dirCheck4: ; simply re-do for other directions...
cmp #4
bne dirCheck6
ldx snakeX
dex
stx snakeX
dirCheck6:
cmp #6
bne dirCheck8
ldx snakeX
inx
stx snakeX
dirCheck8:
cmp #8
bne dirCheck5
ldy snakeY
dey
sty snakeY
dirCheck5:
cmp #5
bne dirEndCheck
jmp skipPauseTests
dirEndCheck:
; Check screen boundaries overflow
lda snakeX
cmp #40
bne overCheckX0 ; if snakeX is not 40, then all ok, skip to next test
lda #0 ; else, there is an overflow, so trespass screen border
sta snakeX
overCheckX0: ; simply re-do for every side of the screen
lda snakeX
cmp #$ff
bne overCheckY1
lda #39
sta snakeX
overCheckY1:
lda snakeY
cmp #25
bne overCheckY0
lda #1
sta snakeY
overCheckY0
lda snakeY
cmp #0
bne overEndCheck
lda #24
sta snakeY
overEndCheck:
; Put new head coordinates in list
ldy listStart
lda snakeX
sta listX,y
lda snakeY
sta listY,y
iny
sty listStart
ldy #0
; Check for food eat / wall hit / self-eat
; - - - - - - - - - - - - - - - - - - - - - -
; --- Food eat ---
lda snakeX ; calc head location in memory
sta calcTileX
lda snakeY
sta calcTileY
jsr calcTileMem
lda (tileMem),y ; read content of head memory location
cmp #FOOD_TILE
beq foodEaten ; if memory does contain food, then perform foodEaten actions,
jmp checkSelfEat ; else just loooong jump to test if I ate myself
foodEaten:
ldx length ; else, increment snake length
inx
stx length
cpx #$40 ; check if level is finished
bne genFood ; if not, skip
clc
lda score ; else increment total score
adc length
sta score
lda score + 1
adc #$0
sta score + 1
jsr levelresetvar ; reset vars and go to next level
lda #ST_LEVEL_TITLE
sta status
rts
genFood:
ldx random
inx
stx random
txa
genFoodX: ; calculate `random` modulo SCREEN_W
sec
sbc #SCREEN_W
cmp #SCREEN_W
bcs genFoodX
sta calcTileX
txa
genFoodY: ; calculate `random` modulo 22 (22 = SCREEN_H - 1)
sec
sbc #22
cmp #22
bcs genFoodY
clc ; add 1 because 1st line can not be used
adc #1
sta calcTileY
; Now I have X and Y coordinate for food stored in calcTileX, calcTileY
; and I must check it is not the location that I am going to overwrite
; with the head in draw snake head...
lda calcTileX
cmp snakeX
bne foodOK
lda calcTileY
cmp snakeY
beq genFood
foodOK:
#if DEBUG = 1
; print choosen X,Y for food
ldy #14
lda calcTileX
jsr printByte
ldy #17
lda calcTileY
jsr printByte
#endif
ldy #0
jsr calcTileMem ; calc food address in memory
lda (tileMem),y ; check if memory is empty
cmp #EMPTY_TILE ; is this an empty tile?
bne genFood ; if not, must generate another number
lda #FOOD_TILE ; else, just put that fucking piece of food there
sta (tileMem),y
lda #$d4
clc
adc tileMem + 1
sta tileMem + 1
lda FOOD_COLOR
sta (tileMem),y
; print partial
ldy #36
lda length
jsr printByte
jmp checkEndSelfEat
checkEndFood:
; --- Self eat ---
checkSelfEat:
cmp #SNAKE_TILE
bne checkEndSelfEat
jmp gameover
checkEndSelfEat:
checkWallHit:
cmp #WALL_TILE
bne checkEndWallHit
jmp gameover
checkEndWallHit:
; Draw snake head
ldy #0
lda snakeX ; calc char address in video memory, and put SNAKE_TILE
sta calcTileX
lda snakeY
sta calcTileY
jsr calcTileMem
lda #SNAKE_TILE
sta (tileMem),y
lda #$d4 ; add #$d400 to previous address (obtain color memory
clc ; correspondent), and put SNAKE_COLOR
adc tileMem + 1
sta tileMem + 1
lda SNAKE_COLOR
sta (tileMem),y
; Erase snake tail
lda listStart ; take start of list, and subtract snake length,
sec ; to obtain index of end of list
sbc length
tax ; use previous value as index in list, and calc video memory address
lda listX,x
sta calcTileX
lda listY,x
sta calcTileY
jsr calcTileMem
lda #EMPTY_TILE ; erase snake tail tile
sta (tileMem),y
skipPauseTests:
rts