Advertisement
Kitomas

snake game for ezr6502 v1

Feb 2nd, 2024
1,465
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. r16al = $f0
  2. r16ah = r16al + 1
  3. r16bl = r16al + 2
  4. r16bh = r16al + 3
  5. r16a  = r16al
  6. r16b  = r16bl
  7. swap   = $fd
  8. random = $fe
  9. key    = $ff
  10.  
  11. ;background color is always black (0)
  12. FRUIT_COLOR = 14 ;red
  13. SNAKE_COLOR = 13 ;green
  14.  
  15.  
  16.  
  17.  
  18. !macro if_eq .end {  bne .end }
  19.  
  20.  
  21. ;update the screen before waiting a frame
  22. !macro flip_screen { nop }
  23.  
  24.  
  25. !macro phall { ;push a,x,y onto stack
  26.   pha
  27.   txa
  28.   pha
  29.   tya
  30.   pha
  31. }
  32.  
  33. !macro plall { ;pull y,x,a onto stack
  34.   pla
  35.   tay
  36.   pla
  37.   tax
  38.   pla
  39. }
  40.  
  41.  
  42. !macro ldxy_abs .address {
  43.   ldx .address
  44.   ldy .address + 1
  45. }
  46.  
  47. !macro stxy_abs .address {
  48.   stx .address
  49.   sty .address + 1
  50. }
  51.  
  52.  
  53.  
  54.  
  55. *=$600
  56.  
  57.  
  58. init:
  59.   jsr snake_reset
  60. loop:
  61.   jsr snake_keypress
  62.   jsr snake_move
  63.  
  64.   +flip_screen ;wait 4 frames
  65.   +flip_screen
  66.   +flip_screen
  67.   +flip_screen
  68.  
  69.   jmp loop
  70.  
  71.  
  72.  
  73.  
  74. equal16ab: ;ignores N flag
  75.   lda r16ah
  76.   cmp r16bh
  77.   bne .highByteNotEqual
  78.   lda r16al
  79.   cmp r16bl
  80. .highByteNotEqual:
  81.   ;(Z and C should be already set)
  82.   rts
  83.  
  84.  
  85.  
  86. ;add accumulator to r16a
  87. add8_16a:
  88.   pha ;preserve accumulator
  89.   clc
  90.   adc r16al
  91.   sta r16al
  92.   bcc .noInc
  93.   ;increment high byte if low byte overflows
  94.   inc r16ah
  95. .noInc:
  96.   pla ;restore accumulator
  97.   rts
  98.  
  99.  
  100.  
  101. ;get screen address from x,y (in coordinates)
  102.  ;resulting address is returned into x,y
  103.  ;(the result will be $200 + (y&31)*32 + x&31)
  104. get_screen_address:
  105.   pha       ;preserve accumulator
  106.  
  107.   lda #0    ;set r16a to (y%32)*32 (AKA (y&31)<<5 )
  108.   sta r16ah  ;^^
  109.   tya        ;^^
  110.   and #31    ;^^
  111.   asl        ;^^shift accumulator left
  112.   rol r16ah  ;^^rotate left into the high byte
  113.   asl        ;^^
  114.   rol r16ah  ;^^
  115.   asl        ;^^
  116.   rol r16ah  ;^^
  117.   asl        ;^^
  118.   rol r16ah  ;^^
  119.   asl        ;^^
  120.   rol r16ah  ;^^
  121.   sta r16al  ;^^finally, store the low byte
  122.  
  123.   txa          ;r16a += x
  124.   jsr add8_16a  ;^^
  125.  
  126.   ldx r16al ;load address into x,y
  127.   ldy r16ah  ;^^
  128.   iny        ;^^(address must be += by $200
  129.   iny        ;^^ so increment high byte twice)
  130.  
  131.   pla       ;restore accumulator
  132.  
  133.   rts
  134.  
  135.  
  136.  
  137.  
  138. snake_reset:
  139.   ;reset registers
  140.   lda #0
  141.   tax
  142.   ;tay
  143.  
  144.   ;reset single values
  145.   ;sta fruit_addr
  146.   ;sta fruit_addr + 1 ;+1 for MSB
  147.   ;sta snake_dir
  148.   ;sta snake_x
  149.   ;sta snake_y
  150.   ;^^ the above is all set later on anyway
  151.   sta snake_length
  152.   sta snake_first
  153.   sta snake_last
  154.  
  155. .wipeLoop0: ;set stuff to 0
  156.   sta $00,x ;wipe zeropage
  157.   ;(stack is not wiped)
  158.   sta $200,x ;wipe screen
  159.   sta $300,x  ;^^
  160.   sta $400,x  ;^^
  161.   sta $500,x  ;^^
  162.   sta snake_buffer,x ;wipe snake buffer
  163.   inx
  164.  bne .wipeLoop0 ;breaks when x overflows
  165.  
  166.   ;add initial snake segment
  167.   lda random             ;random snake direction
  168.   and #3                  ;^^
  169.   sta snake_dir           ;^^
  170.   ldx random             ;random snake position
  171.   ldy random              ;^^
  172.   +stxy_abs snake_pos     ;^^
  173.  
  174.   jsr get_screen_address ;add (invisible) fruit for baby snake
  175.   +stxy_abs fruit_addr    ;^^to eat when it is born
  176.  
  177.   +ldxy_abs snake_pos    ;the gift of life
  178.   jsr snake_new           ;^^
  179.   ;(a new fruit is created automatically by snake_new!)
  180.  
  181.   lda #0   ;reset registers (again)
  182.   tax       ;^^
  183.   tay       ;^^
  184.  
  185.   ;clear flags
  186.   clc : cli : clv : cld
  187.  
  188.   rts
  189.  
  190.  
  191.  
  192.  
  193. ;enqueue an address from x,y
  194. snake_enqueue:
  195.   +phall             ;preserve registers
  196.  
  197.   txa                ;store low byte of address
  198.   ldx snake_last      ;^^
  199.   sta snake_buffer,x  ;^^
  200.  
  201.   tya                ;store high byte of address
  202.   inx                 ;^^
  203.   sta snake_buffer,x  ;^^
  204.  
  205.   inx                ;snake_last += 2
  206.   stx snake_last      ;^^(+2 because of the 2 inx)
  207.   inc snake_length   ;increase snake length by 1
  208.  
  209.   +plall             ;restore registers
  210.  
  211.   rts
  212.  
  213.  
  214. ;dequeue an address into x,y
  215. snake_dequeue:
  216.   pha                ;preserve accumulator
  217.  
  218.   ldx snake_first    ;store low byte to swap temporarily
  219.   lda snake_buffer,x  ;^^
  220.   sta swap            ;^^
  221.   inx                 ;^^
  222.  
  223.   lda snake_buffer,x ;load high byte into y
  224.   tay                 ;^^
  225.   inx                 ;^^
  226.  
  227.   stx snake_first    ;snake_first += 2
  228.   ldx swap           ;load low byte into x
  229.   dec snake_length   ;decrease snake length by 1
  230.  
  231.   pla                ;restore accumulator
  232.  
  233.   rts
  234.  
  235.  
  236.  
  237.  
  238. ;set a to boolean of if address x,y has snake segment
  239.  ;(really, it's just checking if the byte = SNAKE_COLOR)
  240. snake_check:
  241.   stx r16al        ;store address for indirection
  242.   sty r16ah         ;^^
  243.  
  244.   ldy #0           ;load value from address
  245.   lda (r16al),y     ;^^
  246.   ldy r16ah        ;restore y
  247.  
  248.   cmp #SNAKE_COLOR ;branch if byte == SNAKE_COLOR
  249.   beq .true         ;^^
  250.  
  251. .false:
  252.   lda #0
  253.   rts
  254. .true:
  255.   lda #1
  256.   rts
  257.  
  258.  
  259.  
  260.  
  261. snake_death_effect:
  262.   +phall    ;preserve registers
  263.  
  264.   ldy #0    ;set r16a to $200
  265.   lda #2     ;^^
  266.   sty r16al  ;^^
  267.   sta r16ah  ;^^
  268.  
  269.   ldx #4 ;screen is 4 pages long (1024B)
  270. .loopRandom:
  271.   lda random     ;put random color onto screen
  272.   sta (r16al),y   ;^^
  273.   tya            ;flip screen if y&31 == 31
  274.   and #31         ;^^
  275.   cmp #31         ;^^
  276.   bne .no_blit0   ;^^
  277.     +flip_screen  ;^^
  278.   :.no_blit0:
  279.   iny             ;increment y, breaking 1st loop
  280.   bne .loopRandom  ;^^if y overflows back to 0
  281.   inc r16ah       ;increment page and decrement x,
  282.   dex              ;^^
  283.   bne .loopRandom  ;^^breaking 2nd loop if x overflows
  284.  
  285.   lda #2    ;set r16a back to $200
  286.   sty r16al  ;^^(y=0 currently)
  287.   sta r16ah  ;^^
  288.   ldx #4    ;set up x for 4 pages again
  289. .loopBlack: ;same thing, except the color is always black
  290.   lda #0
  291.   sta (r16al),y
  292.   tya
  293.   and #31
  294.   cmp #31
  295.   bne .no_blit1
  296.     +flip_screen
  297.   :.no_blit1:
  298.   iny
  299.   bne .loopBlack
  300.   inc r16ah
  301.   dex
  302.   bne .loopBlack
  303.  
  304.   ldy #45
  305. .waitLoop: ;wait for about 3/4 a second
  306.   +flip_screen
  307.   dey
  308.   bne .waitLoop
  309.  
  310.   +plall ;restore registers
  311.  
  312.   rts
  313.  
  314.  
  315.  
  316.  
  317. key_w = $77 ;3 = north
  318. key_a = $61 ;2 = west
  319. key_s = $73 ;1 = south
  320. key_d = $64 ;0 = east
  321. ;change snake direction based on wasd keys
  322. snake_keypress:
  323.   +phall       ;preserve registers
  324.  
  325.   lda key      ;load last key pressed
  326.   ora #%100000  ;^^will always be lowercase if bit 5 is set
  327.   ldx snake_dir ;for preventing north/south or east/west flips
  328.  
  329.   cmp #key_d
  330.   bne not_d
  331.   cpx #2
  332.   beq not_d
  333.     lda #0
  334.     sta snake_dir
  335.   :not_d:
  336.  
  337.   cmp #key_s
  338.   bne not_s
  339.   cpx #3
  340.   beq not_s
  341.     lda #1
  342.     sta snake_dir
  343.   :not_s:
  344.  
  345.   cmp #key_a
  346.   bne not_a
  347.   cpx #0
  348.   beq not_a
  349.     lda #2
  350.     sta snake_dir
  351.   :not_a:
  352.  
  353.   cmp #key_w
  354.   bne not_w
  355.   cpx #1
  356.   beq not_w
  357.     lda #3
  358.     sta snake_dir
  359.   :not_w:
  360.  
  361.   lda #0  ;reset key to null
  362.   sta key  ;^^
  363.  
  364.   +plall ;restore registers
  365.   rts
  366.  
  367.  
  368.  
  369.  
  370. ;create new fruit, at random screen position
  371.  ;(while avoiding the snake)
  372.  ;(also, the previous fruit is not removed here,
  373.  ; since it's overwritten when the snake passes over it!)
  374. fruit_new:
  375.   +phall ;preserve registers
  376.  
  377. .reroll:
  378.   ;load random address on-screen
  379.   lda random      ;get random number from 0 -> 1023
  380.   ldx random       ;^^
  381.   and #%11         ;^^get only lowest 2 bits from high byte
  382.   eor #%11         ;^^(this eor is here for reasons)
  383.   tay              ;^^
  384.   iny             ;make sure it starts at $200
  385.   iny              ;^^
  386.  
  387.   jsr snake_check ;'does that pixel contain a snake segment?'
  388.   cmp #1          ;if true is returned, then reroll for a new spot
  389.   beq .reroll      ;^^
  390.  
  391.   +stxy_abs fruit_addr ;put new location of fruit into fruit_addr
  392.   +stxy_abs r16a       ;put the fruit on-screen
  393.   lda #FRUIT_COLOR      ;^^
  394.   ldy #0                ;^^
  395.   sta (r16a),y          ;^^
  396.  
  397.   +plall ;restore registers
  398.  
  399.   rts
  400.  
  401.  
  402.  
  403. ;create new snake segment, at snake's head position (using x,y)
  404.  ;(if snake_ate == 1, then don't dequeue)
  405. snake_new:
  406.   +phall ;preserve registers
  407.  
  408.   jsr get_screen_address ;convert position to address
  409.   jsr snake_enqueue      ;put new segment onto the queue
  410.   +stxy_abs r16a         ;put new segment onto the screen
  411.   lda #SNAKE_COLOR        ;^^
  412.   ldy #0                  ;^^
  413.   sta (r16a),y            ;^^
  414.  
  415.   +ldxy_abs fruit_addr   ;if fruit was just hit, don't remove tail
  416.   +stxy_abs r16b          ;^^
  417.   jsr equal16ab           ;^^
  418.   beq .dontDequeue        ;^^
  419.     jsr snake_dequeue    ;otherwise, remove the tail segment
  420.     +stxy_abs r16a        ;^^
  421.     lda #0                ;^^
  422.     tay                   ;^^
  423.     sta (r16a),y          ;^^
  424.     jmp .dontCreateFruit ;skip call to fruit_new
  425. .dontDequeue:
  426.   jsr fruit_new
  427. .dontCreateFruit:
  428.  
  429.   +plall ;restore registers
  430.  
  431.   rts
  432.  
  433.  
  434.  
  435.  
  436. ;move the snake in its current direction
  437. snake_move:
  438.   +phall ;preserve registers
  439.  
  440.   ;not experienced enough to make a jump table, sooo...
  441.   clv ;clear overflow flag, so i can have a reliable branch
  442.   lda snake_dir
  443.   cmp #0 ;0 = east
  444.   +if_eq .eif0_A
  445.     inc snake_x
  446.     bvc .eif0
  447.   :.eif0_A:
  448.   cmp #1 ;1 = south
  449.   +if_eq .eif0_B
  450.     inc snake_y
  451.     bvc .eif0
  452.   :.eif0_B:
  453.   cmp #2 ;2 = west
  454.   +if_eq .eif0_C
  455.     dec snake_x
  456.     bvc .eif0
  457.   :.eif0_C:
  458.   cmp #3 ;3 = north
  459.   +if_eq .eif0_D
  460.     dec snake_y
  461.     bvc .eif0
  462.   :.eif0_D:
  463.   :.eif0:
  464.  
  465.   lda snake_x ;load x and y % 32
  466.   and #31      ;^^
  467.   tax          ;^^
  468.   lda snake_y  ;^^
  469.   and #31      ;^^
  470.   tay          ;^^
  471.   stx snake_x ;x and y %=32
  472.   sty snake_y  ;^^
  473.  
  474.   jsr get_screen_address
  475.   jsr snake_check
  476.   cmp #1
  477.   bne .notASnakeSegment
  478.   jsr snake_death_effect
  479.   jmp $600
  480. .notASnakeSegment:
  481.  
  482.   +ldxy_abs snake_pos ;create segment from new position
  483.   jsr snake_new        ;^^
  484.  
  485.   +plall ;restore registers
  486.   rts
  487.  
  488.  
  489.  
  490. ;(fruit is color 14, whereas the snake is color 13)
  491. fruit_addr  : !16 0 ;address of fruit on-screen
  492. snake_dir   : !8 0 ;0 -> 3 (east, south, west, north)
  493. snake_pos   : ;alias for snake_x basically
  494. snake_x     : !8 0 ;0 -> 31
  495. snake_y     : !8 0 ;0 -> 31
  496. snake_length: !8 0 ;0 -> 128 (game resets at 128)
  497. snake_first : !8 0 ;offset of first queue element's LSB
  498. snake_last  : !8 0 ;offset of last queue element's LSB
  499. snake_buffer: ;!fill 256 ;snake segment queue
  500. ;(there's no bounds checking for the snake queue lol)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement