Last active
          October 7, 2025 15:30 
        
      - 
            
      
        
      
    Star
      
          
          (139)
      
  
You must be signed in to star a gist 
- 
              
      
        
      
    Fork
      
          
          (31)
      
  
You must be signed in to fork a gist 
- 
      
- 
        Save wkjagt/9043907 to your computer and use it in GitHub Desktop. 
Revisions
- 
        wkjagt revised this gist Feb 17, 2014 . 1 changed file with 4 additions and 2 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,8 +3,10 @@ ; \__ \ ' \/ _` | / / -_) _ \__ \ () / / ; |___/_||_\__,_|_\_\___\___/___/\__/___| ; An annotated version of the snake example from Nick Morgan's 6502 assembly tutorial ; on http://skilldrick.github.io/easy6502/ that I created as an exercise for myself ; to learn a little bit about assembly. I **think** I understood everything, but I may ; also be completely wrong :-) ; Change direction with keys: W A S D 
- 
        wkjagt revised this gist Feb 17, 2014 . 1 changed file with 60 additions and 57 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,12 +5,11 @@ ; An annotated version of the snake example on http://skilldrick.github.io/easy6502/ ; that I mainly created as an exercise for myself to learn a little bit about assembly. ; Change direction with keys: W A S D ; $00-01 => screen location of apple, stored as two bytes, where the first ; byte is the least significant. ; $10-11 => screen location of snake head stored as two bytes ; $12-?? => snake body (in byte pairs) ; $02 => direction ; 1 => up (bin 0001) @@ -79,41 +78,41 @@ initSnake: ;initial snake direction (2 => right) lda #2 ;start direction, put the dec number 2 in register A sta $02 ;store value of register A at address $02 ;initial snake length of 4 lda #4 ;start length, put the dec number 4 (the snake is 4 bytes long) ;in register A sta $03 ;store value of register A at address $03 ;Initial snake head's location's least significant byte to determine ;where in a 8x32 strip the head will start. hex $11 is just right ;of the center of the first row of a strip lda #$11 ;put the hex number $11 (dec 17) in register A sta $10 ;store value of register A at address hex 10 ;Initial snake body, two least significant bytes set to hex $10 ;and hex $0f, one and two places left of the head respectively lda #$10 ;put the hex number $10 (dec 16) in register A sta $12 ;store value of register A at address hex $12 lda #$0f ;put the hex number $0f (dec 15) in register A sta $14 ;store value of register A at address hex $14 ;the most significant bytes of the head and body of the snake ;are all set to hex $04, which is the third 8x32 strip. lda #$04 ;put the hex number $04 in register A sta $11 ;store value of register A at address hex 11 sta $13 ;store value of register A at address hex 13 sta $15 ;store value of register A at address hex 15 rts ;return generateApplePosition: ;Th least significant byte of the apple position will determine where ;in a 8x32 strip the apple is placed. This number can be any one byte value because ;the size of one 8x32 strip fits exactly in one out of 256 bytes lda $fe ;load a random number between 0 and 255 from address $fe into register A sta $00 ;store value of register A at address hex 00 ;load a new random number from 2 to 5 into $01 for the most significant byte of ;the apple position. This will determine in which 8x32 strip the apple is placed @@ -123,7 +122,7 @@ generateApplePosition: ;register A. Hex 03 is binary 00000011, so only the two least significant bits ;are kept, resulting in a value between 0 (bin 00000000) and 3 (bin 00000011). ;Add 2 to the result, giving a random value between 2 and 5 and #$03 ;mask out lowest 2 bits clc ;clear carry flag adc #2 ;add to register A, using carry bit for overflow. sta $01 ;store value of y coordinate from register A into address $01 @@ -143,7 +142,10 @@ loop: readKeys: ;for getting keypresses, the last address ($ff) in the zero page contains ;the hex code of the last pressed key lda $ff ;load the value of the latest keypress from address $ff into register A cmp #$77 ;compare value in register A to hex $77 (W) beq upKey ;Branch On Equal, to upKey cmp #$64 ;compare value in register A to hex $64 (D) @@ -156,14 +158,14 @@ readKeys: upKey: lda #4 ;load value 4 into register A, correspoding to the value for DOWN bit $02 ;AND with value at address $02 (the current direction), ;setting the zero flag if the result of ANDing the two values ;is 0. So comparing to 4 (bin 0100) only sets zero flag if ;current direction is 4 (DOWN). So for an illegal move (current ;direction is DOWN), the result of an AND would be a non zero value ;so the zero flag would not be set. For a legal move the bit in the ;new direction should not be the same as the one set for DOWN, ;so the zero flag needs to be set bne illegalMove ;Branch If Not Equal: meaning the zero flag is not set. lda #1 ;Ending up here means the move is legal, load the value 1 (UP) into @@ -173,7 +175,7 @@ upKey: rightKey: lda #8 ;load value 8 into register A, corresponding to the value for LEFT bit $02 ;AND with current direction at address $02 and check if result ;is zero bne illegalMove ;Branch If Not Equal: meaning the zero flag is not set. @@ -184,7 +186,7 @@ rightKey: downKey: lda #1 ;load value 1 into register A, correspoding to the value for UP bit $02 ;AND with current direction at address $02 and check if result ;is zero bne illegalMove ;Branch If Not Equal: meaning the zero flag is not set. @@ -195,7 +197,7 @@ downKey: leftKey: lda #2 ;load value 1 into register A, correspoding to the value for RIGHT bit $02 ;AND with current direction at address $02 and check if result ;is zero bne illegalMove ;Branch If Not Equal: meaning the zero flag is not set. @@ -218,15 +220,15 @@ checkCollision: checkAppleCollision: ;check if the snake collided with the apple by comparing the least significant ;and most significant byte of the position of the snake's head and the apple. lda $00 ;load value at address $00 (the least significant ;byte of the apple's position) into register A cmp $10 ;compare to the value stored at address $10 ;(the least significant byte of the position of the snake's head) bne doneCheckingAppleCollision ;if different, branch to doneCheckingAppleCollision lda $01 ;load value of address $01 (the most significant byte ;of the apple's position) into register A cmp $11 ;compare the value stored at address $11 (the most ;significant byte of the position of the snake's head) bne doneCheckingAppleCollision ;if different, branch to doneCheckingAppleCollision ;Ending up here means the coordinates of the snake head are equal to that of @@ -246,22 +248,23 @@ checkSnakeCollision: ldx #2 ;Load the value 2 into the X register, so we start with the first segment snakeCollisionLoop: lda $10,x ;load the value stored at address $10 (the least significant byte of ;the location of the snake's head) plus the value of the x register ;(2 in the first iteration) to get the least significant byte of the ;position of the next snake segment cmp $10 ;compare to the value at address $10 (the least significant ;byte of the position of the snake's head bne continueCollisionLoop ;if not equals, we haven't found a collision yet, ;branch to continueCollisionLoop to continue the loop maybeCollided: ;ending up here means we found a segment of the snake's body that ;has a least significant byte that's equal to that of the snake's head. lda $11,x ;load the value stored at address $11 (most significant byte of ;the location of the snake's head) plus the value of the x register ;(2 in the first iteration) to get the most significant byte ;of the position of the next snake segment cmp $11 ;compare to the value at address $11 (the most significant ;byte of the position of the snake head) beq didCollide ;both position bytes of the compared segment of the snake body ;are equal to those of the head, so we have a collision of the @@ -272,8 +275,8 @@ continueCollisionLoop: ;the coordinates for snake head and body segments inx ;increment the value of the x register inx ;increment the value of the x register cpx $03 ;compare the value in the x register to the value stored at ;address $03 (snake length). beq didntCollide ;if equals, we got to last section with no collision: branch ;to didntCollide @@ -292,7 +295,7 @@ didntCollide: updateSnake: ;collision checks have been done, update the snake. Load the length of the snake ;minus one into the A register ldx $03 ;load the value stored at address $03 (snake length) into register X dex ;decrement the value in the X register txa ;transfer the value stored in the X register into the A register. WHY? @@ -309,7 +312,7 @@ updateloop: ; ;from: x=== ;to: === lda $10,x ;load the value stored at address $10 + x into register A sta $12,x ;store the value of register A into address $12 ;plus the value of register X dex ;decrement X, and set negative flag if value becomes negative @@ -328,13 +331,13 @@ updateloop: lsr ;shift to right bcs left ;if a 1 "fell off", we started with bin 1000, so the snakes needs to go left up: lda $10 ;put value stored at address $10 (the least significant byte, meaning the ;position in a 8x32 strip) in register A sec ;set carry flag sbc #$20 ;Subtract with Carry: subtract hex $20 (dec 32) together with the NOT of the ;carry bit from value in register A. If overflow occurs the carry bit is clear. ;This moves the snake up one row in its strip and checks for overflow sta $10 ;store value of register A at address $10 (the least significant byte ;of the head's position) bcc upup ;If the carry flag is clear, we had an overflow because of the subtraction, ;so we need to move to the strip above the current one @@ -344,19 +347,19 @@ upup: dec $11 ;decrement the most significant byte of the snake's head's position to ;move the snake's head to the next up 8x32 strip lda #$1 ;load hex value $1 (dec 1) into register A cmp $11 ;compare the value at address $11 (snake head's most significant ;byte, determining which strip it's in). If it's 1, we're one strip too ;(the first one has a most significant byte of $02), which means the snake ;hit the top of the screen beq collision ;branch if equal to collision rts ;return right: inc $10 ;increment the value at address $10 (snake head's least ;significant byte, determining where in the 8x32 strip the head is ;located) to move the head to the right lda #$1f ;load value hex $1f (dec 31) into register A bit $10 ;the value stored at address $10 (the snake head coordinate) is ANDed ;with hex $1f (bin 11111), meaning all multiples of hex $20 (dec 32) ;will be zero (because they all end with bit patterns ending in 5 zeros) ;if it's zero, it means we hit the right of the screen @@ -368,7 +371,7 @@ down: clc ;clear carry flag adc #$20 ;add hex $20 (dec 32) to the value in register A and set the carry flag ;if overflow occurs sta $10 ;store the result at address $10 bcs downdown ;if the carry flag is set, an overflow occurred when adding hex $20 to the ;least significant byte of the location of the snake's head, so we need to move ;the next 8x3 strip @@ -404,29 +407,29 @@ collision: drawApple: ldy #0 ;load the value 0 into the Y register lda $fe ;load the value stored at address $fe (the random number generator) ;into register A sta ($00),y ;dereference to the address stored at address $00 and $01 ;(the address of the apple on the screen) and set the value to ;the value of register A and add the value of Y (0) to it. This results ;in the apple getting a random color rts ;return drawSnake: ldx #0 ;set the value of the X register to 0 lda #1 ;set the value of the A register to 1 sta ($10,x) ;dereference to the memory address that's stored at address ;$10 (the two bytes for the location of the head of the snake) and ;set its value to the one stored in register A ldx $03 ;set the value of the x register to the value stored in memory at ;location $03 (the length of the snake) lda #0 ;set the value of the a register to 0 sta ($10,x) ;dereference to the memory address that's stored at address ;$10, add the length of the snake to it, and store the value of ;register A (0) in the resulting address. This draws a black pixel on the ;tail. Because the snake is moving, the head "draws" on the screen in ;white as it moves, and the tail works as an eraser, erasing the white trail ;using black pixels rts ;return 
- 
        wkjagt revised this gist Feb 17, 2014 . 1 changed file with 0 additions and 2 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -109,8 +109,6 @@ initSnake: generateApplePosition: ;least significant byte of the apple position. This will determine where ;in a 8x32 strip the apple is placed. This number can be any one byte value because ;the size of one 8x32 strip fits exactly in one byte 
- 
        wkjagt revised this gist Feb 17, 2014 . 1 changed file with 84 additions and 41 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -20,6 +20,48 @@ ; $03 => snake length, in number of bytes, not segments ;The screens is divided in 8 strips of 8x32 "pixels". Each strip ;is stored in a page, having their own most significant byte. Each ;page has 256 bytes, starting at $00 and ending at $ff. ; ------------------------------------------------------------ ;1 | $0200 - $02ff | ;2 | | ;3 | | ;4 | | ;5 | | ;6 | | ;7 | | ;8 | | ; ------------------------------------------------------------ ;9 | $03 - $03ff | ;10 | | ;11 | | ;12 | | ;13 | | ;14 | | ;15 | | ;16 | | ; ------------------------------------------------------------ ;17 | $04 - $03ff | ;18 | | ;19 | | ;20 | | ;21 | | ;22 | | ;23 | | ;24 | | ; ------------------------------------------------------------ ;25 | $05 - $03ff | ;26 | | ;27 | | ;28 | | ;29 | | ;30 | | ;31 | | ;32 | | ; ------------------------------------------------------------ jsr init ;jump to subroutine init jsr loop ;jump to subroutine loop @@ -37,31 +79,32 @@ initSnake: ;initial snake direction (2 => right) lda #2 ;start direction, put the dec number 2 in register A sta $02 ;store value of register A in address $02 ;initial snake length of 4 lda #4 ;start length, put the dec number 4 (the snake is 4 bytes long) ;in register A sta $03 ;store value of register A in address $03 ;initial snake head's location's least significant byte to determine ;where in a 8x32 strip the head will start. hex $11 is just right ;of the center of the first row of a strip lda #$11 ;put the hex number $11 (dec 17) in register A sta $10 ;store value of register A in address hex 10 ;initial snake body, two least significant bytes set to hex $10 ;and hex $0f, one and two places left of the head respectively lda #$10 ;put the hex number $10 (dec 16) in register A sta $12 ;store value of register A in address hex $12 lda #$0f ;put the hex number $0f (dec 15) in register A sta $14 ;store value of register A in address hex $14 ;the most significant bytes of the head and body of the snake ;are all set to hex $04, which is the third 8x32 strip. lda #$04 ;put the hex number $04 in register A sta $11 ;store value of register A in address hex 11 sta $13 ;store value of register A in address hex 13 sta $15 ;store value of register A in address hex 15 rts ;return @@ -71,12 +114,12 @@ generateApplePosition: ;least significant byte of the apple position. This will determine where ;in a 8x32 strip the apple is placed. This number can be any one byte value because ;the size of one 8x32 strip fits exactly in one byte lda $fe ;load a random number between 0 and 255 from address $fe into register A sta $00 ;store value of register A in address hex 00 ;load a new random number from 2 to 5 into $01 for the most significant byte of ;the apple position. This will determine in which 8x32 strip the apple is placed lda $fe ;load a random number from address $fe into register A ;AND: logical AND with accumulator. Apply logical AND with hex $03 to value in ;register A. Hex 03 is binary 00000011, so only the two least significant bits @@ -85,7 +128,7 @@ generateApplePosition: and #$03 ;mask out lowest 2 bits by clc ;clear carry flag adc #2 ;add to register A, using carry bit for overflow. sta $01 ;store value of y coordinate from register A into address $01 rts ;return @@ -115,7 +158,7 @@ readKeys: upKey: lda #4 ;load value 4 into register A, correspoding to the value for DOWN bit $02 ;AND with value in address $02 (current direction), returning ;setting the zero flag if the result of ANDing the two values ;is 0. So comparing to 4 (bin 0100) only sets zero flag if ;current direction is 4 (DOWN). So for an illegal move (current @@ -132,7 +175,7 @@ upKey: rightKey: lda #8 ;load value 8 into register A, corresponding to the value for LEFT bit $02 ;AND with current direction in address $02 and check if result ;is zero bne illegalMove ;Branch If Not Equal: meaning the zero flag is not set. @@ -143,7 +186,7 @@ rightKey: downKey: lda #1 ;load value 1 into register A, correspoding to the value for UP bit $02 ;AND with current direction in address $02 and check if result ;is zero bne illegalMove ;Branch If Not Equal: meaning the zero flag is not set. @@ -154,7 +197,7 @@ downKey: leftKey: lda #2 ;load value 1 into register A, correspoding to the value for RIGHT bit $02 ;AND with current direction in address $02 and check if result ;is zero bne illegalMove ;Branch If Not Equal: meaning the zero flag is not set. @@ -177,12 +220,12 @@ checkCollision: checkAppleCollision: ;check if the snake collided with the apple by comparing the least significant ;and most significant byte of the position of the snake's head and the apple. lda $00 ;load value of address $00 (the least significant ;byte of the apple's position) into register A cmp $10 ;compare to the value stored in address $10 ;(the least significant byte of the position of the snake's head) bne doneCheckingAppleCollision ;if different, branch to doneCheckingAppleCollision lda $01 ;load value of address $01 (the most significant byte ;of the apple's position) into register A cmp $11 ;compare to memory locatoin $11 (the most significant byte ;of the position of the snake's head) @@ -205,22 +248,22 @@ checkSnakeCollision: ldx #2 ;Load the value 2 into the X register, so we start with the first segment snakeCollisionLoop: lda $10,x ;load the value of address $10 (x coordinate of head) plus ;the value of the x register (2 in the first iteration) to get the ;least significant byte of the position of the next snake segment cmp $10 ;compare to the value in address $10 (the least significant ;byte of the position of the snake's head bne continueCollisionLoop ;if not equals, we haven't found a collision yet, ;branch to continueCollisionLoop to continue the loop maybeCollided: ;ending up here means we found a segment of the snake's body that ;has an a least significant byte that's equal to that of the head lda $11,x ;load the value of address $11 (most significant byte of ;the location of the head) plus the value of the x register ;(2 in the first iteration) to get the most significant byte ;of the position of the next snake segment cmp $11 ;compare to the value in address $11 (the most significant ;byte of the position of the snake head) beq didCollide ;both position bytes of the compared segment of the snake body ;are equal to those of the head, so we have a collision of the @@ -251,7 +294,7 @@ didntCollide: updateSnake: ;collision checks have been done, update the snake. Load the length of the snake ;minus one into the A register ldx $03 ;load the value of address $03 (snake length) into register X dex ;decrement the value in the X register txa ;transfer the value stored in the X register into the A register. WHY? @@ -268,16 +311,16 @@ updateloop: ; ;from: x=== ;to: === lda $10,x ;load the value of address $10 + x into register A sta $12,x ;store the value of register A into address $12 ;plus the value of register X dex ;decrement X, and set negative flag if value becomes negative bpl updateloop ;branch to updateLoop if positive (negative flag not set) ;now determine where to move the head, based on the direction of the snake ;lsr: Logical Shift Right. Shift all bits in register A one bit to the right ;the bit that "falls off" is stored in the carry flag lda $02 ;load the value from address $02 (direction) into register A lsr ;shift to right bcs up ;if a 1 "fell off", we started with bin 0001, so the snakes needs to go up lsr ;shift to right @@ -287,13 +330,13 @@ updateloop: lsr ;shift to right bcs left ;if a 1 "fell off", we started with bin 1000, so the snakes needs to go left up: lda $10 ;put value from address $10 (the least significant byte, meaning the ;position in a 8x32 strip) in register A sec ;set carry flag sbc #$20 ;Subtract with Carry: subtract hex $20 (dec 32) together with the not of the ;carry bit from value in register A. If overflow occurs the carry bit is clear. ;This moves the snake up one row in its strip and checks for overflow sta $10 ;store value of register A in address $10 (the least significant byte ;of the head's position) bcc upup ;If the carry flag is clear, we had an overflow because of the subtraction, ;so we need to move to the strip above the current one @@ -303,31 +346,31 @@ upup: dec $11 ;decrement the most significant byte of the snake's head's position to ;move the snake's head to the next up 8x32 strip lda #$1 ;load hex value $1 (dec 1) into register A cmp $11 ;compare the value in address $11 (snake head's most significant ;byte, determining which strip it's in). If it's 1, we're one strip too ;(the first one has a most significant byte of $02), which means we've ;the top of the screen beq collision ;branch if equal to collision rts ;return right: inc $10 ;increment the value in address $10 (snake head's least ;significant byte, determining where in the 8x32 strip the head is ;located) to move the head to the right lda #$1f ;load value hex $1f (dec 31) into register A bit $10 ;the value in address $10 (the snake head coordinate) is ANDed ;with hex $1f (bin 11111), meaning all multiples of hex $20 (dec 32) ;will be zero (because they all end with bit patterns ending in 5 zeros) ;if it's zero, it means we hit the right of the screen beq collision ;branch to collision if zero flag is set rts ;return down: lda $10 ;put value from address $10 (the least significant byte, meaning the ;position in a 8x32 strip) in register A clc ;clear carry flag adc #$20 ;add hex $20 (dec 32) to the value in register A and set the carry flag ;if overflow occurs sta $10 ;store the result in address $10 bcs downdown ;if the carry flag is set, an overflow occurred when adding hex $20 to the ;least significant byte of the location of the snake's head, so we need to move ;the next 8x3 strip @@ -363,10 +406,10 @@ collision: drawApple: ldy #0 ;load the value 0 into the Y register lda $fe ;load the value of address $fe (the random number generator) ;into register A sta ($00),y ;dereference to the address stored in address $00 and $01 ;(the address of the apple on the screen) and set the value to ;the value of register A and add the value of Y (0) to it. This results ;in the apple getting a random color rts ;return @@ -375,13 +418,13 @@ drawApple: drawSnake: ldx #0 ;set the value of the x register to 0 lda #1 ;set the value of the a register to 1 sta ($10,x) ;dereference to the memory address that's stored in address ;$10 (the two bytes for the location of the head of the snake) and ;set its value to the one stored in register A ldx $03 ;set the value of the x register to the value stored in memory at ;location $03 (the length of the snake) lda #0 ;set the value of the a register to 0 sta ($10,x) ;dereference to the memory address that's stored in address ;$10, add the length of the snake to it, and store the value of ;register A (0) in the resulting address. This draws a black pixel on the ;tail. Because the snake is moving, the head "draws" on the screen in 
- 
        wkjagt revised this gist Feb 17, 2014 . 1 changed file with 9 additions and 3 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,13 +4,19 @@ ; |___/_||_\__,_|_\_\___\___/___/\__/___| ; An annotated version of the snake example on http://skilldrick.github.io/easy6502/ ; that I mainly created as an exercise for myself to learn a little bit about assembly. ; ; Change direction: W A S D ; $00-01 => screen location of apple, stored as two bytes, where the first byte is the least ; significant. ; $10-11 => screen location of snake head stored as two bytes ; $12-?? => snake body (in byte pairs) ; $02 => direction ; 1 => up (bin 0001) ; 2 => right (bin 0010) ; 4 => down (bin 0100) ; 8 => left (bin 1000) ; $03 => snake length, in number of bytes, not segments 
- 
        wkjagt created this gist Feb 17, 2014 .There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,400 @@ ; ___ _ __ ___ __ ___ ; / __|_ _ __ _| |_____ / /| __|/ \_ ) ; \__ \ ' \/ _` | / / -_) _ \__ \ () / / ; |___/_||_\__,_|_\_\___\___/___/\__/___| ; An annotated version of the snake example on http://skilldrick.github.io/easy6502/ ; Change direction: W A S D ; $00-01 => screen location of apple ; $10-11 => screen location of snake head ; $12-?? => snake body (in byte pairs) ; $02 => direction (1 => up, 2 => right, 4 => down, 8 => left) ; $03 => snake length, in number of bytes, not segments jsr init ;jump to subroutine init jsr loop ;jump to subroutine loop init: jsr initSnake ;jump to subroutine initSnake jsr generateApplePosition ;jump to subroutine generateApplePosition rts ;return initSnake: ;start the snake in a horizontal position in the middle of the game field ;having a total length of one head and 4 bytes for the segments, meaning a ;total length of 3: the head and two segments. ;The head is looking right, and the snaking moving to the right. ;initial snake direction (2 => right) lda #2 ;start direction, put the dec number 2 in register A sta $02 ;store value of register A in memory location $02 ;initial snake length of 4 lda #4 ;start length, put the dec number 4 in register A sta $03 ;store value of register A in memory location $03 ;initial snake head location least significant byte to determine ;where in a 8x32 strip the head will start. hex $11 is just right ;of the center of the first row of a strip lda #$11 ;put the hex number $11 (dec 17) in register A sta $10 ;store value of register A in memory location hex 10 ;initial snake body, two least significant bytes set to hex $10 ;and hex $0f, one and two places left of the head respectively lda #$10 ;put the hex number $10 (dec 16) in register A sta $12 ;store value of register A in memory location hex 12 lda #$0f ;put the hex number $0f (dec 15) in register A sta $14 ;store value of register A in memory location hex 14 ;the most significant bytes of the head and body of the snake ;are all set to hex $04, which is the third 8x32 strip. lda #$04 ;put the hex number $04 in register A sta $11 ;store value of register A in memory location hex 11 sta $13 ;store value of register A in memory location hex 13 sta $15 ;store value of register A in memory location hex 15 rts ;return generateApplePosition: ;load a new random byte into $00 ;least significant byte of the apple position. This will determine where ;in a 8x32 strip the apple is placed. This number can be any one byte value because ;the size of one 8x32 strip fits exactly in one byte lda $fe ;load a random number between 0 and 255 from memory location $fe into register A sta $00 ;store value of register A in memory location hex 00 ;load a new random number from 2 to 5 into $01 for the most significant byte of ;the apple position. This will determine in which 8x32 strip the apple is placed lda $fe ;load a random number from memory location $fe into register A ;AND: logical AND with accumulator. Apply logical AND with hex $03 to value in ;register A. Hex 03 is binary 00000011, so only the two least significant bits ;are kept, resulting in a value between 0 (bin 00000000) and 3 (bin 00000011). ;Add 2 to the result, giving a random value between 2 and 5 and #$03 ;mask out lowest 2 bits by clc ;clear carry flag adc #2 ;add to register A, using carry bit for overflow. sta $01 ;store value of y coordinate from register A into memory location $01 rts ;return loop: ;the main game loop jsr readKeys ;jump to subroutine readKeys jsr checkCollision ;jump to subroutine checkCollision jsr updateSnake ;jump to subroutine updateSnake jsr drawApple ;jump to subroutine drawApple jsr drawSnake ;jump to subroutine drawSnake jsr spinWheels ;jump to subroutine spinWheels jmp loop ;jump to loop (this is what makes it loop) readKeys: lda $ff ;load the value of the latest keypress from location $ff into register A cmp #$77 ;compare value in register A to hex $77 (W) beq upKey ;Branch On Equal, to upKey cmp #$64 ;compare value in register A to hex $64 (D) beq rightKey ;Branch On Equal, to rightKey cmp #$73 ;compare value in register A to hex $73 (S) beq downKey ;Branch On Equal, to downKey cmp #$61 ;compare value in register A to hex $61 (A) beq leftKey ;Branch On Equal, to leftKey rts ;return upKey: lda #4 ;load value 4 into register A, correspoding to the value for DOWN bit $02 ;AND with value in memory location $02 (current direction), returning ;setting the zero flag if the result of ANDing the two values ;is 0. So comparing to 4 (bin 0100) only sets zero flag if ;current direction is 4 (DOWN). So for an illegal move (current ;direction is DOWN), the result of an AND would be a non zero value ;so the zero flag would not be set. For a legal move the bit in the ;new direction should not the same as the one set for DOWN, so the zero ;flag needs to set bne illegalMove ;Branch If Not Equal: meaning the zero flag is not set. lda #1 ;Ending up here means the move is legal, load the value 1 (UP) into ;register A sta $02 ;Store the value of A (the new direction) into register A rts ;return rightKey: lda #8 ;load value 8 into register A, corresponding to the value for LEFT bit $02 ;AND with current direction in memory location $02 and check if result ;is zero bne illegalMove ;Branch If Not Equal: meaning the zero flag is not set. lda #2 ;Ending up here means the move is legal, load the value 2 (RIGHT) into ;register A sta $02 ;Store the value of A (the new direction) into register A rts ;return downKey: lda #1 ;load value 1 into register A, correspoding to the value for UP bit $02 ;AND with current direction in memory location $02 and check if result ;is zero bne illegalMove ;Branch If Not Equal: meaning the zero flag is not set. lda #4 ;Ending up here means the move is legal, load the value 4 (DOWN) into ;register A sta $02 ;Store the value of A (the new direction) into register A rts ;return leftKey: lda #2 ;load value 1 into register A, correspoding to the value for RIGHT bit $02 ;AND with current direction in memory location $02 and check if result ;is zero bne illegalMove ;Branch If Not Equal: meaning the zero flag is not set. lda #8 ;Ending up here means the move is legal, load the value 8 (LEFT) into ;register A sta $02 ;Store the value of A (the new direction) into register A rts ;return illegalMove: ;for an illegal move, just return, so the keypress is ignored rts ;return checkCollision: jsr checkAppleCollision ;jump to subroutine checkAppleCollision jsr checkSnakeCollision ;jump to subroutine checkSnakeCollision rts ;return checkAppleCollision: ;check if the snake collided with the apple by comparing the least significant ;and most significant byte of the position of the snake's head and the apple. lda $00 ;load value of memory location $00 (the least significant ;byte of the apple's position) into register A cmp $10 ;compare to the value stored in memory location $10 ;(the least significant byte of the position of the snake's head) bne doneCheckingAppleCollision ;if different, branch to doneCheckingAppleCollision lda $01 ;load value of memory location $01 (the most significant byte ;of the apple's position) into register A cmp $11 ;compare to memory locatoin $11 (the most significant byte ;of the position of the snake's head) bne doneCheckingAppleCollision ;if different, branch to doneCheckingAppleCollision ;Ending up here means the coordinates of the snake head are equal to that of ;the apple: eat apple inc $03 ;increment the value held in memory $03 (snake length) inc $03 ;twice because we're adding two bytes for one segment ;create a new apple jsr generateApplePosition ;jump to subroutine generateApplePosition doneCheckingAppleCollision: ;the snake head was not on the apple. Don't do anything with the apple rts ;return checkSnakeCollision: ldx #2 ;Load the value 2 into the X register, so we start with the first segment snakeCollisionLoop: lda $10,x ;load the value of memory location $10 (x coordinate of head) plus ;the value of the x register (2 in the first iteration) to get the ;least significant byte of the position of the next snake segment cmp $10 ;compare to the value in memory location $10 (the least significant ;byte of the position of the snake's head bne continueCollisionLoop ;if not equals, we haven't found a collision yet, ;branch to continueCollisionLoop to continue the loop maybeCollided: ;ending up here means we found a segment of the snake's body that ;has an a least significant byte that's equal to that of the head lda $11,x ;load the value of memory location $11 (most significant byte of ;the location of the head) plus the value of the x register ;(2 in the first iteration) to get the most significant byte ;of the position of the next snake segment cmp $11 ;compare to the value in memory location $11 (the most significant ;byte of the position of the snake head) beq didCollide ;both position bytes of the compared segment of the snake body ;are equal to those of the head, so we have a collision of the ;snake's head with its own body. continueCollisionLoop: ;increment the value in the x register twice because we use two bytes to store ;the coordinates for snake head and body segments inx ;increment the value of the x register inx ;increment the value of the x register cpx $03 ;compare the value in the x register to the value in memory ;location $03 (snake length). beq didntCollide ;if equals, we got to last section with no collision: branch ;to didntCollide ;ending up here means we haven't checked all snake body segments yet jmp snakeCollisionLoop;jump to snakeCollisionLoop to continue the loop didCollide: ;there was a collision jmp gameOver ;jump to gameOver didntCollide: ;there was no collision, continue the game rts ;return updateSnake: ;collision checks have been done, update the snake. Load the length of the snake ;minus one into the A register ldx $03 ;load the value of memory location $03 (snake length) into register X dex ;decrement the value in the X register txa ;transfer the value stored in the X register into the A register. WHY? updateloop: ;Example: the length of the snake is 4 bytes (two segments). In the lines above ;the X register has been set to 3. The snake coordinates are now stored as follows: ;$10,$11 : the snake head ;$12,$13,$14,$15: the snake body segments (two bytes for each of the 2 segments) ; ;The loop shifts all coordinates of the snake two places further in memory, ;calculating the offset of the origin from $10 and place it in memory offset to ;$12, effectively shifting each of the snake's segments one place further: ; ;from: x=== ;to: === lda $10,x ;load the value of memory location $10 + x into register A sta $12,x ;store the value of register A into memory location $12 ;plus the value of register X dex ;decrement X, and set negative flag if value becomes negative bpl updateloop ;branch to updateLoop if positive (negative flag not set) ;now determine where to move the head, based on the direction of the snake ;lsr: Logical Shift Right. Shift all bits in register A one bit to the right ;the bit that "falls off" is stored in the carry flag lda $02 ;load the value from memory location $02 (direction) into register A lsr ;shift to right bcs up ;if a 1 "fell off", we started with bin 0001, so the snakes needs to go up lsr ;shift to right bcs right ;if a 1 "fell off", we started with bin 0010, so the snakes needs to go right lsr ;shift to right bcs down ;if a 1 "fell off", we started with bin 0100, so the snakes needs to go down lsr ;shift to right bcs left ;if a 1 "fell off", we started with bin 1000, so the snakes needs to go left up: lda $10 ;put value from memory location $10 (the least significant byte, meaning the ;position in a 8x32 strip) in register A sec ;set carry flag sbc #$20 ;Subtract with Carry: subtract hex $20 (dec 32) together with the not of the ;carry bit from value in register A. If overflow occurs the carry bit is clear. ;This moves the snake up one row in its strip and checks for overflow sta $10 ;store value of register A in memory location $10 (the least significant byte ;of the head's position) bcc upup ;If the carry flag is clear, we had an overflow because of the subtraction, ;so we need to move to the strip above the current one rts ;return upup: ;An overflow occurred when subtracting 20 from the least significant byte dec $11 ;decrement the most significant byte of the snake's head's position to ;move the snake's head to the next up 8x32 strip lda #$1 ;load hex value $1 (dec 1) into register A cmp $11 ;compare the value in memory location $11 (snake head's most significant ;byte, determining which strip it's in). If it's 1, we're one strip too ;(the first one has a most significant byte of $02), which means we've ;the top of the screen beq collision ;branch if equal to collision rts ;return right: inc $10 ;increment the value in memory location $10 (snake head's least ;significant byte, determining where in the 8x32 strip the head is ;located) to move the head to the right lda #$1f ;load value hex $1f (dec 31) into register A bit $10 ;the value in memory location $10 (the snake head coordinate) is ANDed ;with hex $1f (bin 11111), meaning all multiples of hex $20 (dec 32) ;will be zero (because they all end with bit patterns ending in 5 zeros) ;if it's zero, it means we hit the right of the screen beq collision ;branch to collision if zero flag is set rts ;return down: lda $10 ;put value from memory location $10 (the least significant byte, meaning the ;position in a 8x32 strip) in register A clc ;clear carry flag adc #$20 ;add hex $20 (dec 32) to the value in register A and set the carry flag ;if overflow occurs sta $10 ;store the result in memory location $10 bcs downdown ;if the carry flag is set, an overflow occurred when adding hex $20 to the ;least significant byte of the location of the snake's head, so we need to move ;the next 8x3 strip rts ;return downdown: inc $11 ;increment the value in location hex $11, holding the most significatnt byte ;of the location of the snake's head. lda #$6 ;load the value hex $6 into the A register cmp $11 ;if the most significant byte of the head's location is equals to 6, we're ;one strip to far down (the last one was hex $05) beq collision ;if equals to 6, the snake collided with the bottom of the screen rts ;return left: ;A collision with the left side of the screen happens if the head wraps around to ;the previous row, on the right most side of the screen, where, because the screen ;is 32 wide, the right most positions always have a least significant byte that ends ;in 11111 in binary form (hex $1f). ANDing with hex $1f in this column will always ;return hex $1f, so comparing the result of the AND with hex $1f will determine if ;the snake collided with the left side of the screen. dec $10 ;subtract one from the value held in memory position $10 (least significant ;byte of the snake head position) to make it move left. lda $10 ;load value held in memory position $10 (least significant byte of the ;snake head position) into register A and #$1f ;AND the value hex $1f (bin 11111) with the value in register A cmp #$1f ;compare the ANDed value above with bin 11111. beq collision ;branch to collision if equals rts ;return collision: jmp gameOver ;jump to gameOver drawApple: ldy #0 ;load the value 0 into the Y register lda $fe ;load the value of memory location $fe (the random number generator) ;into register A sta ($00),y ;dereference to the memory location stored in memory location $00 and $01 ;(the memory location of the apple on the screen) and set the value to ;the value of register A and add the value of Y (0) to it. This results ;in the apple getting a random color rts ;return drawSnake: ldx #0 ;set the value of the x register to 0 lda #1 ;set the value of the a register to 1 sta ($10,x) ;dereference to the memory address that's stored in memory location ;$10 (the two bytes for the location of the head of the snake) and ;set its value to the one stored in register A ldx $03 ;set the value of the x register to the value stored in memory at ;location $03 (the length of the snake) lda #0 ;set the value of the a register to 0 sta ($10,x) ;dereference to the memory address that's stored in memory location ;$10, add the length of the snake to it, and store the value of ;register A (0) in the resulting address. This draws a black pixel on the ;tail. Because the snake is moving, the head "draws" on the screen in ;white as it moves, and the tail works as an eraser, erasing the white ;using black pixels rts ;return spinWheels: ;slow the game down by wasting cycles ldx #0 ;load zero in the X register spinloop: nop ;no operation, just skip a cycle nop ;no operation, just skip a cycle dex ;subtract one from the value stored in register x bne spinloop ;if the zero flag is clear, loop. The first dex above wrapped the ;value of x to hex $ff, so the next zero value is 255 (hex $ff) ;loops later. rts ;return gameOver: ;game over is literally the end of the program