336 lines
7.8 KiB
NASM
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
|