; Basic snake game

; Only tested in DCPU-16 Studio. Make sure you run it with "cycle exact"

; ticked, so that it's emulating the correct CPU speed (100khz).

; Controls: W A S D

; On a game over a pink square is displayed in the top left corner. Your

; current score is stored in register X, your current level is stored in

; register Y. Press "R" to restart the game.

; rxi

; MAIN

: main ; Entry point

jsr game

; GAME

; Coords stored as 8bit pair, upper is X, lower is Y

: snake dat 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,

0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,

0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0

: length dat 0

: direction dat 0

: apple dat 0

: speed dat 0

: score dat 0

: level dat 0

: game ; Main game routine

jsr gamereset

: gameloop

; Handle input

jsr gameinput

; Limit speed

jsr gamewait

; Handle game

jsr movesnake

; Draw

jsr clear

jsr drawsnake

jsr drawapple

jsr flip

; Loop

set pc , gameloop

: gameend

set pc , pop

: gamereset ; Call to reset the game state

jsr resetsnake

jsr resetapple

set [ speed ] , 0x0D00

set [ score ] , 0x0000

set [ level ] , 1

set pc , pop

: gameover ; Call on game over

set push , x

set push , y

set x , [ score ] ; Set X register to final score

set y , [ level ] ; Set Y register to final level

set [ 0x8000 ] , 0xDD00 ; Display pink square to indicate gameover

: gameoverloop

ifn [ 0x9000 ] , 0x72 ; Wait for "r" key

set pc , gameoverloop

jsr gamereset

set pop , y

set pop , x

set pc , pop

: gamewait ; Burn a few cycles to limit the speed of the game

set push , a

set a , [ speed ]

: waitloop

sub a , 1

ifn a , 0

set pc , waitloop

set a , pop

set pc , pop

: gameinput ; Handle user input keys

set push , a

set a , [ 0x9000 ]

ife a , 0x77 ; W

set [ direction ] , 0

ife a , 0x61 ; A

set [ direction ] , 2

ife a , 0x73 ; S

set [ direction ] , 1

ife a , 0x64 ; D

set [ direction ] , 3

set [ 0x9000 ] , 0

set a , pop

set pc , pop

: nextlevel

add [ level ] , 1

ifg [ speed ] , 0xFF ; Limiter max speed

sub [ speed ] , 0x0200 ; Increase speed

set [ length ] , 2

set pc , pop

: movesnake ; Move snake and handle collision

set push , a

set push , b

set push , c

; Shift body

set c , snake

set a , c

add a , [ length ]

set b , a

add b , 1

: movebodyloop

set [ b ] , [ a ]

sub a , 1

sub b , 1

ifn b , c

set pc , movebodyloop

; Move head

set a , [ direction ]

ife a , 0 ; Up

sub [ snake ] , 0x0001

ife a , 1 ; Down

add [ snake ] , 0x0001

ife a , 2 ; Left

sub [ snake ] , 0x0100

ife a , 3 ; Right

add [ snake ] , 0x0100

; Handle apple collision

ife [ snake ] , [ apple ]

jsr eatapple

; Handle self collision

jsr selfcollision

; Handle border collision

jsr bordercollision

set c , pop

set b , pop

set a , pop

set pc , pop

: eatapple ; Called on apple collision

set push , a

ifg 0x2E , [ length ] ; Limit maximum length

add [ length ] , 1 ; Increase length

; Check if the level should change

set a , [ level ]

mul a , [ level ]

add a , 5

ife a , [ length ]

jsr nextlevel

add [ score ] , 10

jsr resetapple

set a , pop

set pc , pop

: selfcollision ; Checks collision against self

set push , z

set a , [ snake ]

jsr bodycollision

ife z , 1

jsr gameover

set z , pop

set pc , pop

: bodycollision ; Checks entire snake body against coords in register a

set push , a ; sets register z to 1 on collision, set to 0 if not

set push , b

set b , snake

add b , [ length ]

: bodycollisionloop

sub b , 1

ife b , snake

set pc , bodycollisionfalse

ife [ b ] , a

set pc , bodycollisiontrue

set pc , bodycollisionloop

: bodycollisiontrue

set z , 1

set pc , bodycollisionend

: bodycollisionfalse

set z , 0

set pc , bodycollisionend

: bodycollisionend

set b , pop

set a , pop

set pc , pop

: bordercollision ; Checks collision against border

set push , a

; Check X position

set a , [ snake ]

shr a , 8

ifg a , 0x1F

jsr gameover

; Check Y position

set a , [ snake ]

and a , 0xFF

ifg a , 0x0F

jsr gameover

set a , pop

set pc , pop

: resetapple ; Sets apple's position to a new random one

set push , a

set push , z

: resetappleloop

; Get X position

jsr rand

mod z , 0x20

set a , z

shl a , 8

; Get Y position

jsr rand

mod z , 0x10

bor a , z

; Set position

set [ apple ] , a

; Check we didn't spawn on the snake

jsr bodycollision

ife z , 1

set pc , resetappleloop

set z , pop

set a , pop

set pc , pop

: resetsnake ; Resets snakes position and length

set push , a

set a , snake

set [ a ] , 0x0F05

add a , 1

set [ a ] , 0x0E05

set [ direction ] , 3

set [ length ] , 2

set pop , a

set pc , pop

: drawapple ; Draw the apple to the buffer

set a , [ apple ]

jsr drawxy

set pc , pop

: drawsnake ; Draw the snake to the buffer

set push , a

set push , b

set b , [ length ]

: drawsnakeloop

sub b , 1

set a , snake

add a , b

set a , [ a ]

jsr drawxy

ifn b , - 1

set pc , drawsnakeloop

set b , pop

set a , pop

set pc , pop

; GENERAL

: rand ; Sets Z register to a 'random' number

set push , a

set push , b

set push , x

set push , y

set a , [ randvara ]

set b , [ randvarb ]

set x , a

and x , 0xFF

mul x , 369

shr a , 8

add a , x

set y , a

and y , 0xFF

mul y , 180

shr b , 8

add b , y

set z , a

shl z , 8

add z , b

set [ randvara ] , a

set [ randvarb ] , b

set y , pop

set x , pop

set b , pop

set a , pop

set pc , pop

: randvara dat 0x2351

: randvarb dat 0x1252

; DRAWING

; Everything here uses a double buffer and needs to be

; flipped before it's drawn to the actual screen memory

: doublebuf dat 0x2000 ; Address of double buffer

: screen dat 0x8000 ; Address of screen

: drawxy ; Draws 'pixel' from register a

set push , x

set push , y

set x , a

shr x , 8

set y , a

and y , 0xFF

mul y , 0x20

add x , y

set y , [ doublebuf ]

add y , x

set [ y ] , 0xFFFF

set y , pop

set x , pop

set pc , pop

: clear ; Clear the screen

set push , a

set a , [ doublebuf ]

: cls_loop

set [ a ] , 0

add a , 1

ifn a , 0x2200

set pc , cls_loop

set a , pop

set pc , pop

: flip ; Draws buffer to screen memory

set push , a

set push , b

set a , [ doublebuf ]

set b , [ screen ]

: fliploop

set [ b ] , [ a ]

add a , 1

add b , 1

ifn b , 0x8200

set pc , fliploop

set b , pop

set a , pop