Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- r16al = $f0
- r16ah = r16al + 1
- r16bl = r16al + 2
- r16bh = r16al + 3
- r16a = r16al
- r16b = r16bl
- swap = $fd
- random = $fe
- key = $ff
- ;background color is always black (0)
- FRUIT_COLOR = 14 ;red
- SNAKE_COLOR = 13 ;green
- !macro if_eq .end { bne .end }
- ;update the screen before waiting a frame
- !macro flip_screen { nop }
- !macro phall { ;push a,x,y onto stack
- pha
- txa
- pha
- tya
- pha
- }
- !macro plall { ;pull y,x,a onto stack
- pla
- tay
- pla
- tax
- pla
- }
- !macro ldxy_abs .address {
- ldx .address
- ldy .address + 1
- }
- !macro stxy_abs .address {
- stx .address
- sty .address + 1
- }
- *=$600
- init:
- jsr snake_reset
- loop:
- jsr snake_keypress
- jsr snake_move
- +flip_screen ;wait 4 frames
- +flip_screen
- +flip_screen
- +flip_screen
- jmp loop
- equal16ab: ;ignores N flag
- lda r16ah
- cmp r16bh
- bne .highByteNotEqual
- lda r16al
- cmp r16bl
- .highByteNotEqual:
- ;(Z and C should be already set)
- rts
- ;add accumulator to r16a
- add8_16a:
- pha ;preserve accumulator
- clc
- adc r16al
- sta r16al
- bcc .noInc
- ;increment high byte if low byte overflows
- inc r16ah
- .noInc:
- pla ;restore accumulator
- rts
- ;get screen address from x,y (in coordinates)
- ;resulting address is returned into x,y
- ;(the result will be $200 + (y&31)*32 + x&31)
- get_screen_address:
- pha ;preserve accumulator
- lda #0 ;set r16a to (y%32)*32 (AKA (y&31)<<5 )
- sta r16ah ;^^
- tya ;^^
- and #31 ;^^
- asl ;^^shift accumulator left
- rol r16ah ;^^rotate left into the high byte
- asl ;^^
- rol r16ah ;^^
- asl ;^^
- rol r16ah ;^^
- asl ;^^
- rol r16ah ;^^
- asl ;^^
- rol r16ah ;^^
- sta r16al ;^^finally, store the low byte
- txa ;r16a += x
- jsr add8_16a ;^^
- ldx r16al ;load address into x,y
- ldy r16ah ;^^
- iny ;^^(address must be += by $200
- iny ;^^ so increment high byte twice)
- pla ;restore accumulator
- rts
- snake_reset:
- ;reset registers
- lda #0
- tax
- ;tay
- ;reset single values
- ;sta fruit_addr
- ;sta fruit_addr + 1 ;+1 for MSB
- ;sta snake_dir
- ;sta snake_x
- ;sta snake_y
- ;^^ the above is all set later on anyway
- sta snake_length
- sta snake_first
- sta snake_last
- .wipeLoop0: ;set stuff to 0
- sta $00,x ;wipe zeropage
- ;(stack is not wiped)
- sta $200,x ;wipe screen
- sta $300,x ;^^
- sta $400,x ;^^
- sta $500,x ;^^
- sta snake_buffer,x ;wipe snake buffer
- inx
- bne .wipeLoop0 ;breaks when x overflows
- ;add initial snake segment
- lda random ;random snake direction
- and #3 ;^^
- sta snake_dir ;^^
- ldx random ;random snake position
- ldy random ;^^
- +stxy_abs snake_pos ;^^
- jsr get_screen_address ;add (invisible) fruit for baby snake
- +stxy_abs fruit_addr ;^^to eat when it is born
- +ldxy_abs snake_pos ;the gift of life
- jsr snake_new ;^^
- ;(a new fruit is created automatically by snake_new!)
- lda #0 ;reset registers (again)
- tax ;^^
- tay ;^^
- ;clear flags
- clc : cli : clv : cld
- rts
- ;enqueue an address from x,y
- snake_enqueue:
- +phall ;preserve registers
- txa ;store low byte of address
- ldx snake_last ;^^
- sta snake_buffer,x ;^^
- tya ;store high byte of address
- inx ;^^
- sta snake_buffer,x ;^^
- inx ;snake_last += 2
- stx snake_last ;^^(+2 because of the 2 inx)
- inc snake_length ;increase snake length by 1
- +plall ;restore registers
- rts
- ;dequeue an address into x,y
- snake_dequeue:
- pha ;preserve accumulator
- ldx snake_first ;store low byte to swap temporarily
- lda snake_buffer,x ;^^
- sta swap ;^^
- inx ;^^
- lda snake_buffer,x ;load high byte into y
- tay ;^^
- inx ;^^
- stx snake_first ;snake_first += 2
- ldx swap ;load low byte into x
- dec snake_length ;decrease snake length by 1
- pla ;restore accumulator
- rts
- ;set a to boolean of if address x,y has snake segment
- ;(really, it's just checking if the byte = SNAKE_COLOR)
- snake_check:
- stx r16al ;store address for indirection
- sty r16ah ;^^
- ldy #0 ;load value from address
- lda (r16al),y ;^^
- ldy r16ah ;restore y
- cmp #SNAKE_COLOR ;branch if byte == SNAKE_COLOR
- beq .true ;^^
- .false:
- lda #0
- rts
- .true:
- lda #1
- rts
- snake_death_effect:
- +phall ;preserve registers
- ldy #0 ;set r16a to $200
- lda #2 ;^^
- sty r16al ;^^
- sta r16ah ;^^
- ldx #4 ;screen is 4 pages long (1024B)
- .loopRandom:
- lda random ;put random color onto screen
- sta (r16al),y ;^^
- tya ;flip screen if y&31 == 31
- and #31 ;^^
- cmp #31 ;^^
- bne .no_blit0 ;^^
- +flip_screen ;^^
- :.no_blit0:
- iny ;increment y, breaking 1st loop
- bne .loopRandom ;^^if y overflows back to 0
- inc r16ah ;increment page and decrement x,
- dex ;^^
- bne .loopRandom ;^^breaking 2nd loop if x overflows
- lda #2 ;set r16a back to $200
- sty r16al ;^^(y=0 currently)
- sta r16ah ;^^
- ldx #4 ;set up x for 4 pages again
- .loopBlack: ;same thing, except the color is always black
- lda #0
- sta (r16al),y
- tya
- and #31
- cmp #31
- bne .no_blit1
- +flip_screen
- :.no_blit1:
- iny
- bne .loopBlack
- inc r16ah
- dex
- bne .loopBlack
- ldy #45
- .waitLoop: ;wait for about 3/4 a second
- +flip_screen
- dey
- bne .waitLoop
- +plall ;restore registers
- rts
- key_w = $77 ;3 = north
- key_a = $61 ;2 = west
- key_s = $73 ;1 = south
- key_d = $64 ;0 = east
- ;change snake direction based on wasd keys
- snake_keypress:
- +phall ;preserve registers
- lda key ;load last key pressed
- ora #%100000 ;^^will always be lowercase if bit 5 is set
- ldx snake_dir ;for preventing north/south or east/west flips
- cmp #key_d
- bne not_d
- cpx #2
- beq not_d
- lda #0
- sta snake_dir
- :not_d:
- cmp #key_s
- bne not_s
- cpx #3
- beq not_s
- lda #1
- sta snake_dir
- :not_s:
- cmp #key_a
- bne not_a
- cpx #0
- beq not_a
- lda #2
- sta snake_dir
- :not_a:
- cmp #key_w
- bne not_w
- cpx #1
- beq not_w
- lda #3
- sta snake_dir
- :not_w:
- lda #0 ;reset key to null
- sta key ;^^
- +plall ;restore registers
- rts
- ;create new fruit, at random screen position
- ;(while avoiding the snake)
- ;(also, the previous fruit is not removed here,
- ; since it's overwritten when the snake passes over it!)
- fruit_new:
- +phall ;preserve registers
- .reroll:
- ;load random address on-screen
- lda random ;get random number from 0 -> 1023
- ldx random ;^^
- and #%11 ;^^get only lowest 2 bits from high byte
- eor #%11 ;^^(this eor is here for reasons)
- tay ;^^
- iny ;make sure it starts at $200
- iny ;^^
- jsr snake_check ;'does that pixel contain a snake segment?'
- cmp #1 ;if true is returned, then reroll for a new spot
- beq .reroll ;^^
- +stxy_abs fruit_addr ;put new location of fruit into fruit_addr
- +stxy_abs r16a ;put the fruit on-screen
- lda #FRUIT_COLOR ;^^
- ldy #0 ;^^
- sta (r16a),y ;^^
- +plall ;restore registers
- rts
- ;create new snake segment, at snake's head position (using x,y)
- ;(if snake_ate == 1, then don't dequeue)
- snake_new:
- +phall ;preserve registers
- jsr get_screen_address ;convert position to address
- jsr snake_enqueue ;put new segment onto the queue
- +stxy_abs r16a ;put new segment onto the screen
- lda #SNAKE_COLOR ;^^
- ldy #0 ;^^
- sta (r16a),y ;^^
- +ldxy_abs fruit_addr ;if fruit was just hit, don't remove tail
- +stxy_abs r16b ;^^
- jsr equal16ab ;^^
- beq .dontDequeue ;^^
- jsr snake_dequeue ;otherwise, remove the tail segment
- +stxy_abs r16a ;^^
- lda #0 ;^^
- tay ;^^
- sta (r16a),y ;^^
- jmp .dontCreateFruit ;skip call to fruit_new
- .dontDequeue:
- jsr fruit_new
- .dontCreateFruit:
- +plall ;restore registers
- rts
- ;move the snake in its current direction
- snake_move:
- +phall ;preserve registers
- ;not experienced enough to make a jump table, sooo...
- clv ;clear overflow flag, so i can have a reliable branch
- lda snake_dir
- cmp #0 ;0 = east
- +if_eq .eif0_A
- inc snake_x
- bvc .eif0
- :.eif0_A:
- cmp #1 ;1 = south
- +if_eq .eif0_B
- inc snake_y
- bvc .eif0
- :.eif0_B:
- cmp #2 ;2 = west
- +if_eq .eif0_C
- dec snake_x
- bvc .eif0
- :.eif0_C:
- cmp #3 ;3 = north
- +if_eq .eif0_D
- dec snake_y
- bvc .eif0
- :.eif0_D:
- :.eif0:
- lda snake_x ;load x and y % 32
- and #31 ;^^
- tax ;^^
- lda snake_y ;^^
- and #31 ;^^
- tay ;^^
- stx snake_x ;x and y %=32
- sty snake_y ;^^
- jsr get_screen_address
- jsr snake_check
- cmp #1
- bne .notASnakeSegment
- jsr snake_death_effect
- jmp $600
- .notASnakeSegment:
- +ldxy_abs snake_pos ;create segment from new position
- jsr snake_new ;^^
- +plall ;restore registers
- rts
- ;(fruit is color 14, whereas the snake is color 13)
- fruit_addr : !16 0 ;address of fruit on-screen
- snake_dir : !8 0 ;0 -> 3 (east, south, west, north)
- snake_pos : ;alias for snake_x basically
- snake_x : !8 0 ;0 -> 31
- snake_y : !8 0 ;0 -> 31
- snake_length: !8 0 ;0 -> 128 (game resets at 128)
- snake_first : !8 0 ;offset of first queue element's LSB
- snake_last : !8 0 ;offset of last queue element's LSB
- snake_buffer: ;!fill 256 ;snake segment queue
- ;(there's no bounds checking for the snake queue lol)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement