From 676c62c171998221826ed37f088aeea57b2de950 Mon Sep 17 00:00:00 2001 From: jborza Date: Sat, 27 Apr 2019 21:13:15 +0200 Subject: [PATCH] test binaries for the graphic emulator --- bins/00.bin | Bin 0 -> 5 bytes bins/01.asm | 8 + bins/01.bin | Bin 0 -> 15 bytes bins/01a.asm | 8 + bins/01a.bin | Bin 0 -> 12 bytes bins/02.asm | 8 + bins/02.bin | Bin 0 -> 12 bytes bins/03.asm | 22 ++ bins/03.bin | Bin 0 -> 19 bytes bins/04.asm | 22 ++ bins/04.bin | Bin 0 -> 19 bytes bins/05.asm | 17 ++ bins/05.bin | Bin 0 -> 24 bytes bins/06.asm | 32 +++ bins/06.bin | 1 + bins/07.asm | 19 ++ bins/07.bin | Bin 0 -> 29 bytes bins/easy6502_notes.txt | 24 +++ bins/hex2bin.py | 22 ++ bins/snake.asm | 274 ++++++++++++++++++++++++ bins/snake.bin | Bin 0 -> 309 bytes bins/snake6502.asm | 452 ++++++++++++++++++++++++++++++++++++++++ bins/snake_fast.asm | 274 ++++++++++++++++++++++++ bins/snake_fast.bin | Bin 0 -> 309 bytes 24 files changed, 1183 insertions(+) create mode 100644 bins/00.bin create mode 100644 bins/01.asm create mode 100644 bins/01.bin create mode 100644 bins/01a.asm create mode 100644 bins/01a.bin create mode 100644 bins/02.asm create mode 100644 bins/02.bin create mode 100644 bins/03.asm create mode 100644 bins/03.bin create mode 100644 bins/04.asm create mode 100644 bins/04.bin create mode 100644 bins/05.asm create mode 100644 bins/05.bin create mode 100644 bins/06.asm create mode 100644 bins/06.bin create mode 100644 bins/07.asm create mode 100644 bins/07.bin create mode 100644 bins/easy6502_notes.txt create mode 100644 bins/hex2bin.py create mode 100644 bins/snake.asm create mode 100644 bins/snake.bin create mode 100644 bins/snake6502.asm create mode 100644 bins/snake_fast.asm create mode 100644 bins/snake_fast.bin diff --git a/bins/00.bin b/bins/00.bin new file mode 100644 index 0000000000000000000000000000000000000000..83e126a4ca89d948e36109f46458c7adcbf97db8 GIT binary patch literal 5 McmZ3<*vr5K00jR5IsgCw literal 0 HcmV?d00001 diff --git a/bins/01.asm b/bins/01.asm new file mode 100644 index 0000000..5074ede --- /dev/null +++ b/bins/01.asm @@ -0,0 +1,8 @@ +LDY #$0F +LDX #$00 +loop: +TYA +STA $0200,X +INX +DEY +BNE loop \ No newline at end of file diff --git a/bins/01.bin b/bins/01.bin new file mode 100644 index 0000000000000000000000000000000000000000..310bd9ec8ba8cc9f17940348b989b18fc9ff966d GIT binary patch literal 15 WcmZ3<*vr7QlC_tSX(dN56B7U-p98u8 literal 0 HcmV?d00001 diff --git a/bins/01a.asm b/bins/01a.asm new file mode 100644 index 0000000..5074ede --- /dev/null +++ b/bins/01a.asm @@ -0,0 +1,8 @@ +LDY #$0F +LDX #$00 +loop: +TYA +STA $0200,X +INX +DEY +BNE loop \ No newline at end of file diff --git a/bins/01a.bin b/bins/01a.bin new file mode 100644 index 0000000000000000000000000000000000000000..c94ac60bfd467514f845ac47f65cefde1fb40801 GIT binary patch literal 12 UcmZ3$zldSRTn45W9T$E803T`v!2kdN literal 0 HcmV?d00001 diff --git a/bins/02.asm b/bins/02.asm new file mode 100644 index 0000000..5074ede --- /dev/null +++ b/bins/02.asm @@ -0,0 +1,8 @@ +LDY #$0F +LDX #$00 +loop: +TYA +STA $0200,X +INX +DEY +BNE loop \ No newline at end of file diff --git a/bins/02.bin b/bins/02.bin new file mode 100644 index 0000000000000000000000000000000000000000..c94ac60bfd467514f845ac47f65cefde1fb40801 GIT binary patch literal 12 UcmZ3$zldSRTn45W9T$E803T`v!2kdN literal 0 HcmV?d00001 diff --git a/bins/03.asm b/bins/03.asm new file mode 100644 index 0000000..1499519 --- /dev/null +++ b/bins/03.asm @@ -0,0 +1,22 @@ +;number of loops to perform +LDA #$10 +STA $A0 + +;number of bytes to color in a loop + +outer: +LDY #$0A +;LDX #$00 + +loop: +;LDA #$01 +STA $0200, X +INX +DEY +BNE loop + +;outer loop handling +DEC $A0 +LDA $A0 +BNE outer + diff --git a/bins/03.bin b/bins/03.bin new file mode 100644 index 0000000000000000000000000000000000000000..80476c645096773d801ec8663c61e548a47efc3a GIT binary patch literal 19 bcmZ1}(7Ip&*IWjs7abRV9$T<(xbPhSf3gc_ literal 0 HcmV?d00001 diff --git a/bins/06.asm b/bins/06.asm new file mode 100644 index 0000000..00cf747 --- /dev/null +++ b/bins/06.asm @@ -0,0 +1,32 @@ +start: +;number of loops to perform +LDA #$10 +STA $A0 + + +outer: +;number of bytes to color in a loop +LDY #$03 +;LDX #$00 + +loop: +;LDA #$01 +STA $0200, X +INX +DEY +BNE loop + +;outer loop handling +DEC $A0 +LDA $A0 +BNE outer + +;overwrite the address for drawing and loop once again + +LDA #$30 +ADC $0607 ;add 3*16 for the next color strip +STA $0607 +LDA #$90 +CMP $0607 +;LDA $0607 +BNE start \ No newline at end of file diff --git a/bins/06.bin b/bins/06.bin new file mode 100644 index 0000000..9ce52fd --- /dev/null +++ b/bins/06.bin @@ -0,0 +1 @@ +Ơ0m \ No newline at end of file diff --git a/bins/07.asm b/bins/07.asm new file mode 100644 index 0000000..0a6b0ac --- /dev/null +++ b/bins/07.asm @@ -0,0 +1,19 @@ + LDX #$00 + LDY #$00 +firstloop: + TXA + STA $0200,Y + PHA + INX + INY + STA $0200,Y + INY + PHA + CPY #$20 + BNE firstloop ;loop until Y is $10 +secondloop: + PLA + STA $0200,Y + INY + CPY #$40 ;loop until Y is $20 + BNE secondloop \ No newline at end of file diff --git a/bins/07.bin b/bins/07.bin new file mode 100644 index 0000000000000000000000000000000000000000..035cd0f5af3b707f43d29ba4528ff7d808583d01 GIT binary patch literal 29 icmZ3)uz;a!CIgelixWWdgvSAe3m-B-tOE`gz5@WU3JpB~ literal 0 HcmV?d00001 diff --git a/bins/easy6502_notes.txt b/bins/easy6502_notes.txt new file mode 100644 index 0000000..8c17b51 --- /dev/null +++ b/bins/easy6502_notes.txt @@ -0,0 +1,24 @@ +Notes: + +Memory location $fe contains a new random byte on every instruction. +Memory location $ff contains the ascii code of the last key pressed. + +Memory locations $200 to $5ff map to the screen pixels. Different values will +draw different colour pixels. The colours are: + +$0: Black +$1: White +$2: Red +$3: Cyan +$4: Purple +$5: Green +$6: Blue +$7: Yellow +$8: Orange +$9: Brown +$a: Light red +$b: Dark grey +$c: Grey +$d: Light green +$e: Light blue +$f: Light grey \ No newline at end of file diff --git a/bins/hex2bin.py b/bins/hex2bin.py new file mode 100644 index 0000000..dddc26f --- /dev/null +++ b/bins/hex2bin.py @@ -0,0 +1,22 @@ +import sys + +output_name = sys.argv[1] +print(output_name) + +f = open(output_name, 'wb') + +def as_byte(value): + return bytes([value]) + +def process_line(line): + hexes = line[5:].strip() + hex_values = [int(hex,16) for hex in hexes.split(' ')] + for hex_value in hex_values: + print(as_byte(hex_value)) + f.write(as_byte(hex_value)) + #print(chr(hex_value), end = '') + +for line in sys.stdin: + process_line(line) + +f.close() diff --git a/bins/snake.asm b/bins/snake.asm new file mode 100644 index 0000000..cbeefd4 --- /dev/null +++ b/bins/snake.asm @@ -0,0 +1,274 @@ +; ___ _ __ ___ __ ___ +; / __|_ _ __ _| |_____ / /| __|/ \_ ) +; \__ \ ' \/ _` | / / -_) _ \__ \ () / / +; |___/_||_\__,_|_\_\___\___/___/\__/___| + +; Change direction: W A S D + +define appleL $00 ; screen location of apple, low byte +define appleH $01 ; screen location of apple, high byte +define snakeHeadL $10 ; screen location of snake head, low byte +define snakeHeadH $11 ; screen location of snake head, high byte +define snakeBodyStart $12 ; start of snake body byte pairs +define snakeDirection $02 ; direction (possible values are below) +define snakeLength $03 ; snake length, in bytes + +; Directions (each using a separate bit) +define movingUp 1 +define movingRight 2 +define movingDown 4 +define movingLeft 8 + +; ASCII values of keys controlling the snake +define ASCII_w $77 +define ASCII_a $61 +define ASCII_s $73 +define ASCII_d $64 + +; System variables +define sysRandom $fe +define sysLastKey $ff + + + jsr init + jsr loop + +init: + jsr initSnake + jsr generateApplePosition + rts + + +initSnake: + lda #movingRight ;start direction + sta snakeDirection + + lda #4 ;start length (2 segments) + sta snakeLength + + lda #$11 + sta snakeHeadL + + lda #$10 + sta snakeBodyStart + + lda #$0f + sta $14 ; body segment 1 + + lda #$04 + sta snakeHeadH + sta $13 ; body segment 1 + sta $15 ; body segment 2 + rts + + +generateApplePosition: + ;load a new random byte into $00 + lda sysRandom + sta appleL + + ;load a new random number from 2 to 5 into $01 + lda sysRandom + and #$03 ;mask out lowest 2 bits + clc + adc #2 + sta appleH + + rts + + +loop: + jsr readKeys + jsr checkCollision + jsr updateSnake + jsr drawApple + jsr drawSnake + jsr spinWheels + jmp loop + + +readKeys: + lda sysLastKey + cmp #ASCII_w + beq upKey + cmp #ASCII_d + beq rightKey + cmp #ASCII_s + beq downKey + cmp #ASCII_a + beq leftKey + rts +upKey: + lda #movingDown + bit snakeDirection + bne illegalMove + + lda #movingUp + sta snakeDirection + rts +rightKey: + lda #movingLeft + bit snakeDirection + bne illegalMove + + lda #movingRight + sta snakeDirection + rts +downKey: + lda #movingUp + bit snakeDirection + bne illegalMove + + lda #movingDown + sta snakeDirection + rts +leftKey: + lda #movingRight + bit snakeDirection + bne illegalMove + + lda #movingLeft + sta snakeDirection + rts +illegalMove: + rts + + +checkCollision: + jsr checkAppleCollision + jsr checkSnakeCollision + rts + + +checkAppleCollision: + lda appleL + cmp snakeHeadL + bne doneCheckingAppleCollision + lda appleH + cmp snakeHeadH + bne doneCheckingAppleCollision + + ;eat apple + inc snakeLength + inc snakeLength ;increase length + jsr generateApplePosition +doneCheckingAppleCollision: + rts + + +checkSnakeCollision: + ldx #2 ;start with second segment +snakeCollisionLoop: + lda snakeHeadL,x + cmp snakeHeadL + bne continueCollisionLoop + +maybeCollided: + lda snakeHeadH,x + cmp snakeHeadH + beq didCollide + +continueCollisionLoop: + inx + inx + cpx snakeLength ;got to last section with no collision + beq didntCollide + jmp snakeCollisionLoop + +didCollide: + jmp gameOver +didntCollide: + rts + + +updateSnake: + ldx snakeLength + dex + txa +updateloop: + lda snakeHeadL,x + sta snakeBodyStart,x + dex + bpl updateloop + + lda snakeDirection + lsr + bcs up + lsr + bcs right + lsr + bcs down + lsr + bcs left +up: + lda snakeHeadL + sec + sbc #$20 + sta snakeHeadL + bcc upup + rts +upup: + dec snakeHeadH + lda #$1 + cmp snakeHeadH + beq collision + rts +right: + inc snakeHeadL + lda #$1f + bit snakeHeadL + beq collision + rts +down: + lda snakeHeadL + clc + adc #$20 + sta snakeHeadL + bcs downdown + rts +downdown: + inc snakeHeadH + lda #$6 + cmp snakeHeadH + beq collision + rts +left: + dec snakeHeadL + lda snakeHeadL + and #$1f + cmp #$1f + beq collision + rts +collision: + jmp gameOver + + +drawApple: + ldy #0 + lda sysRandom + sta (appleL),y + rts + + +drawSnake: + ldx snakeLength + lda #0 + sta (snakeHeadL,x) ; erase end of tail + + ldx #0 + lda #1 + sta (snakeHeadL,x) ; paint head + rts + + +spinWheels: + ldx #0 +spinloop: + nop + nop + dex + bne spinloop + rts + + +gameOver: \ No newline at end of file diff --git a/bins/snake.bin b/bins/snake.bin new file mode 100644 index 0000000000000000000000000000000000000000..d5569e02032bca021cf2b1a7956ee25987aaaa26 GIT binary patch literal 309 zcmWlUu}T9$5QcB};#>j-XJdqf6gNl^BZ!rdXE@?Jj9`=6!aBt^f}o(i1XdiYYw`q= zz+H6@AxBKLtq9Rg_s#t9&HMyFW1u$B8L()prD0<+76olb4s18f#va-uc3T^{?I`X| zwN*@4Ko3BlK>dU$q4Q)i2KQIo`F3&RyTz>^6xaTkr7_gii72aCngIv98!Rdy26aFk za8H^6nVx~(fnBu3s_oR$R7oAz@$R*`USQb5!Yqw%t=C_%hsg&_E)#x>%liesbV~ej zI?G#G-p}$h%P$;`zscgG;+2tL^QT;+q(k&6JM2wEmMgA}K*;uag$_q)k$S~J`x?qV VML{mn4Cj%k8S*^ZY|84F{{d*iZBGCI literal 0 HcmV?d00001 diff --git a/bins/snake6502.asm b/bins/snake6502.asm new file mode 100644 index 0000000..1469aea --- /dev/null +++ b/bins/snake6502.asm @@ -0,0 +1,452 @@ +; ___ _ __ ___ __ ___ +; / __|_ _ __ _| |_____ / /| __|/ \_ ) +; \__ \ ' \/ _` | / / -_) _ \__ \ () / / +; |___/_||_\__,_|_\_\___\___/___/\__/___| + +; 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 + +; $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 + + +;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 + +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 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 + 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 + ;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 + + 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: + ;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) + 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 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 + ;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 at address $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 at address $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 at address $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 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 + ;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 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 + ;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 stored at + ;address $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 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? + +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 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 + 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 + 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 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 + 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 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 + 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 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 + 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 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 + + +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 \ No newline at end of file diff --git a/bins/snake_fast.asm b/bins/snake_fast.asm new file mode 100644 index 0000000..d3b6b75 --- /dev/null +++ b/bins/snake_fast.asm @@ -0,0 +1,274 @@ +; ___ _ __ ___ __ ___ +; / __|_ _ __ _| |_____ / /| __|/ \_ ) +; \__ \ ' \/ _` | / / -_) _ \__ \ () / / +; |___/_||_\__,_|_\_\___\___/___/\__/___| + +; Change direction: W A S D + +define appleL $00 ; screen location of apple, low byte +define appleH $01 ; screen location of apple, high byte +define snakeHeadL $10 ; screen location of snake head, low byte +define snakeHeadH $11 ; screen location of snake head, high byte +define snakeBodyStart $12 ; start of snake body byte pairs +define snakeDirection $02 ; direction (possible values are below) +define snakeLength $03 ; snake length, in bytes + +; Directions (each using a separate bit) +define movingUp 1 +define movingRight 2 +define movingDown 4 +define movingLeft 8 + +; ASCII values of keys controlling the snake +define ASCII_w $57 +define ASCII_a $41 +define ASCII_s $53 +define ASCII_d $44 + +; System variables +define sysRandom $fe +define sysLastKey $ff + + + jsr init + jsr loop + +init: + jsr initSnake + jsr generateApplePosition + rts + + +initSnake: + lda #movingRight ;start direction + sta snakeDirection + + lda #4 ;start length (2 segments) + sta snakeLength + + lda #$11 + sta snakeHeadL + + lda #$10 + sta snakeBodyStart + + lda #$0f + sta $14 ; body segment 1 + + lda #$04 + sta snakeHeadH + sta $13 ; body segment 1 + sta $15 ; body segment 2 + rts + + +generateApplePosition: + ;load a new random byte into $00 + lda sysRandom + sta appleL + + ;load a new random number from 2 to 5 into $01 + lda sysRandom + and #$03 ;mask out lowest 2 bits + clc + adc #2 + sta appleH + + rts + + +loop: + jsr readKeys + jsr checkCollision + jsr updateSnake + jsr drawApple + jsr drawSnake + jsr spinWheels + jmp loop + + +readKeys: + lda sysLastKey + cmp #ASCII_w + beq upKey + cmp #ASCII_d + beq rightKey + cmp #ASCII_s + beq downKey + cmp #ASCII_a + beq leftKey + rts +upKey: + lda #movingDown + bit snakeDirection + bne illegalMove + + lda #movingUp + sta snakeDirection + rts +rightKey: + lda #movingLeft + bit snakeDirection + bne illegalMove + + lda #movingRight + sta snakeDirection + rts +downKey: + lda #movingUp + bit snakeDirection + bne illegalMove + + lda #movingDown + sta snakeDirection + rts +leftKey: + lda #movingRight + bit snakeDirection + bne illegalMove + + lda #movingLeft + sta snakeDirection + rts +illegalMove: + rts + + +checkCollision: + jsr checkAppleCollision + jsr checkSnakeCollision + rts + + +checkAppleCollision: + lda appleL + cmp snakeHeadL + bne doneCheckingAppleCollision + lda appleH + cmp snakeHeadH + bne doneCheckingAppleCollision + + ;eat apple + inc snakeLength + inc snakeLength ;increase length + jsr generateApplePosition +doneCheckingAppleCollision: + rts + + +checkSnakeCollision: + ldx #2 ;start with second segment +snakeCollisionLoop: + lda snakeHeadL,x + cmp snakeHeadL + bne continueCollisionLoop + +maybeCollided: + lda snakeHeadH,x + cmp snakeHeadH + beq didCollide + +continueCollisionLoop: + inx + inx + cpx snakeLength ;got to last section with no collision + beq didntCollide + jmp snakeCollisionLoop + +didCollide: + jmp gameOver +didntCollide: + rts + + +updateSnake: + ldx snakeLength + dex + txa +updateloop: + lda snakeHeadL,x + sta snakeBodyStart,x + dex + bpl updateloop + + lda snakeDirection + lsr + bcs up + lsr + bcs right + lsr + bcs down + lsr + bcs left +up: + lda snakeHeadL + sec + sbc #$20 + sta snakeHeadL + bcc upup + rts +upup: + dec snakeHeadH + lda #$1 + cmp snakeHeadH + beq collision + rts +right: + inc snakeHeadL + lda #$1f + bit snakeHeadL + beq collision + rts +down: + lda snakeHeadL + clc + adc #$20 + sta snakeHeadL + bcs downdown + rts +downdown: + inc snakeHeadH + lda #$6 + cmp snakeHeadH + beq collision + rts +left: + dec snakeHeadL + lda snakeHeadL + and #$1f + cmp #$1f + beq collision + rts +collision: + jmp gameOver + + +drawApple: + ldy #0 + lda sysRandom + sta (appleL),y + rts + + +drawSnake: + ldx snakeLength + lda #0 + sta (snakeHeadL,x) ; erase end of tail + + ldx #0 + lda #1 + sta (snakeHeadL,x) ; paint head + rts + + +spinWheels: + ldx #1 +spinloop: + nop + nop + dex + bne spinloop + rts + + +gameOver: \ No newline at end of file diff --git a/bins/snake_fast.bin b/bins/snake_fast.bin new file mode 100644 index 0000000000000000000000000000000000000000..bc9ecd1edb7255c309d3cc912a49555f63a3b122 GIT binary patch literal 309 zcmWlUu}T9$5QcB};#>j-XJdqf6t_qaBZ!rdR9$=Z%1)& zs+}Tx1NH&zC)j>sZDP-p$r$`!aTnUfb?6qiVNhI!W8%h8S0}8TCT+_?W>f1 UibT%Q4CfK68PZ&BHf8lo|M@y>&;S4c literal 0 HcmV?d00001