- 256 byte Snake!
- 30 Jul 2014 03:45:35 am
- Last edited by Unknownloner on 30 Jul 2014 03:42:06 pm; edited 2 times in total
So for some reason I had the idea that I wanted to make a version of snake that fit within 256 bytes, in assembly. I ended up with this!
I spent a few hours optimizing and adding some minor features, and now here's the new version
I of course have a wait loop to slow it down because otherwise it's unplayably fast. (it almost is too fast as it is)
I'm quite happy with how it turned out, even getting a score counter in there.
Here's the source (assembled with BRASS). It's not very well commented but tomorrow I'll see if I can comment it up for you. I'll also upload it to the archives if anyone wants to try it.
Code:
I spent a few hours optimizing and adding some minor features, and now here's the new version
I of course have a wait loop to slow it down because otherwise it's unplayably fast. (it almost is too fast as it is)
I'm quite happy with how it turned out, even getting a score counter in there.
Here's the source (assembled with BRASS). It's not very well commented but tomorrow I'll see if I can comment it up for you. I'll also upload it to the archives if anyone wants to try it.
Code:
.nolist
#include "ti83plus.inc"
#include "dcs7.inc"
.variablename SNAKE
.list
.org UserMem - 2
.db t2ByteTok, tAsmCmp
xor d
ret
jr main
.dw 0000h ;Description
.db $07, $00
.dw 0000h ;Icon
.dw 0000h ;ALE
.dw $BEEF ;I have 2 extra bytes left, everyone loves beef!
;Positioning is important here.
;I want to be able to inc my way from FOOD_POS to HEAD_POS,
;from HEAD_OFF to TAIL_OFF, from GROWTH to BSIZE, from BODY_X to BODY_Y
;I also want to be able to dec from GROWTH to TAIL_OFF
FOOD_POS = saveSScreen
FOOD_X = FOOD_POS + 1
FOOD_Y = FOOD_POS
HEAD_POS = saveSScreen + 2
HEAD_X = HEAD_POS + 1
HEAD_Y = HEAD_POS
HEAD_OFF = saveSScreen + 4
TAIL_OFF = saveSScreen + 5
GROWTH = saveSScreen + 6
BSIZE = saveSScreen + 7
VEL = saveSScreen + 8
VEL_X = VEL + 1
VEL_Y = VEL
TEXT = saveSScreen + 10
BODY_X = 8000h ;appData
BODY_Y = 8100h ;ramCode
INITIAL_LEN = 4 ;First food gen increases to 5
main:
b_call(_ClrLCDFull)
ld hl,InitialData
ld de,HEAD_POS
ld bc,InitialDataEnd - InitialData
ldir
set textWrite, (iy + sGrFlags)
;Draw border of board
ld hl,64 << 8 ;X=64,Y=0
ld d,h ;X=64,Y=64
ld e,h
call fastlineb
call growAndGenFood
gameLoop:
ld de,0
b_call(_GetCSC)
cp skClear
ret z
inputKeys:
dec a ;Down key
jr nz,$+3
inc e
dec a ;Left key
jr nz,$+3
dec d
dec a ;Right key
jr nz,$+3
inc d
dec a ;Up key
jr nz,$+3
dec e
dec a ;Wrap around from up key
cp 252 ;If Down was pressed, A will be 252. If it's less, ignore key press
jr c,noInput
ld hl,VEL_Y
ld a,(hl)
and e
jr nz,{@}
ld (hl),e
@:
inc hl ;VEL_X
ld a,(hl)
and d
jr nz,{@}
ld (hl),d
@:
noInput:
ld hl,(VEL) ;H = VelX, L = VelY
ld de,HEAD_POS+1 ;HEAD_X
ld a,(de)
add a,h
and 31 ;Clamp X to board (wall wraparound)
ld h,a
ld (de),a
dec de ;HEAD_Y
ld a,(de)
add a,l
and 31 ;Clamp Y to board (wall wraparound)
ld l,a
ld (de),a
ld de,(FOOD_POS)
call cphlde ;Test if head position = food position
push af ;Save flag for decided whether or not to fill in head
push hl ;Save head position
ld bc,noCollisionCheck ;Save return spot to stack
push bc
jr z,growAndGenFood ;Jump to growAndGenFood if head pos == food pos
;COLLISION CHECKING
;No food gen, so check for collision. HL contains head position
add hl,hl
call pixeltesthl
ret z ;noCollisionCheck is still on stack, so we can use this to jump to it if we didn't collide
;End game due to collision
;Pop return addr, head pos, flags
pop hl
pop hl
pop hl
ret
;END COLLISION CHECKING
noCollisionCheck:
ld hl,GROWTH
dec (hl)
jr nz,{@}
inc (hl)
dec hl ;HL = TAIL_OFF
inc (hl)
call DispBodyPart
@:
pop de ;Current head position
ld hl,HEAD_OFF
inc (hl)
ld c,(hl)
ld b,BODY_X >> 8
ld a,d
ld (bc),a
inc b
ld a,e
ld (bc),a
pop af ;Z will be set if we ate food
call nz,DispBodyPart
call ifastcopy ;re-enables interrupts
ld a,1800/22
@:
djnz $+0
dec a
jr nz,{-1@}
jr gameLoop
growAndGenFood:
;Set pen position for displaying score
ld hl,(4 << 8) | 70 ;32 = pen row, 70 = pen col
ld (penCol),hl
ld hl,GROWTH
inc (hl)
;Increment and display score
inc hl ;HL = BSIZE (score)
inc (hl)
ld l,(hl)
ld h,0
call VDispHL
;End of display score
genFood:
ld c,31 ;Mask for values 0-31
call rand127 ;Y value
and c
ld l,a
call rand127 ;X value
and c
ld h,a
push hl
add hl,hl
call pixeltesthl
pop hl ;H = x, L = y
jr nz,genFood
ld (FOOD_POS),hl ;Save food position
;Fall down to DispSquare
;HL = X/Y in squares
DispSquare:
add hl,hl ;Convert to pixels
ld a,h ;iputsprite takes X in A
ld b,2
ld ix,SquareSprite
jp iputsprite
;Displays a body part, offset is given in A
DispBodyPart:
ld a,(hl)
ld h,BODY_X >> 8
ld l,a ;HL = BODY_X + OFFSET
ld a,(hl)
inc h ;HL = BODY_Y + OFFSET
ld l,(hl) ;Y
ld h,a
InitialData:
jr DispSquare ;also HEAD_Y/HEAD_X
; .db 0 ;HEAD_Y
; .db -1 ;HEAD_X
;It doesn't matter what HEAD_OFF and TAIL_OFF are as long as
;they are the same, why not also use it as sprite data
SquareSprite
.db %11000000 ;HEAD_OFF
.db %11000000 ;TAIL_OFF
.db INITIAL_LEN + 1 ;GROWTH (In the code a growth of 1 means no growth, so we add 1 to get desired growth)
.db INITIAL_LEN ;SCORE
.db 0 ;VEL_Y
.db 1 ;VEL_X
InitialDataEnd