#define WIDTH 60
#define HEIGHT 40by Simon Holmberg
This is a document generated by docco. My report is interleaved with the source code in the form of comments in markdown format.
#define WIDTH 60
#define HEIGHT 40.bssArray of longs representing the game field
world is an array of increasing time values. By having the snake head set
the time value to 0 at its current position, the actual snake will be
defined by the locations where the time value is lesser than the snake
length. This makes the tail of the snake automatically “move” visually, and
allows for a snake with an arbitrary length without complicated arrays of
coordinates!
Apples are defined by negative time values which do not get incremented each frame. If we wanted to, we could instead decrease this value and compare it with a lifetime value, to conveniently make apples disappear after a set amount of time.
.align 4
world: .space WIDTH*HEIGHT*4.dataCurrent length of the snake
snakeLength: .long 0Snake position (starts in the middle of the field)
snakePosX: .long WIDTH / 2
snakePosY: .long HEIGHT / 2Snake direction (start moving to the right)
snakeDirX: .long 1
snakeDirY: .long 0Number of apples
numApples: .long 0Game over message
sgameover: .string " Game over! Score: %i "
sgameover_end:Various utility strings mainly used for debugging
string1: .string "Started with snake length %i and %i apples!\n"
string2: .string "Snake position: (%i, %i)\n"
stringi: .string "%i\n"
stringtest: .string "> %i %i %c\n".textstart_game(int len, int n_apples)
.global start_game
start_game:Save our function arguments for later
snakeLength = len
movl 4(%esp), %eax
movl %eax, snakeLengthnumApples = n_apples
movl 8(%esp), %eax
movl %eax, numApplesStore all registers so we can restore their state before we return later
pushaCall nib_init
call nib_initSeed randomness by calling srand with a time value from time
pushl $0
call time
addl $4, %esp
pushl %eax
call srand
addl $4, %espInitialize the snake array by setting every cell to a time value greater
than snakeLength
movl $world, %edi
movl snakeLength, %eax
movl $WIDTH*HEIGHT, %ebx
xorl %ecx, %ecx
1:
movl %eax, (%edi, %ecx, 4)
incl %ecx
cmpl %ebx, %ecx
jl 1bSpawn initial apples
movl numApples, %ecx
1:
pushl %ecx
call spawnApple
popl %ecx
loop 1bloop:Sleep for a while using usleep so the snake doesn’t wiggle at the
speed of light!
pushl $250*1000
call usleep
addl $4, %espJump to tick, our updating function, which later chain-jumps to draw
jmp tickRestore all our registers as they were before start_game was called
and return
popa
retThis is where we update the world.
tick:
movl $world, %ediLoop
movl snakeLength, %ebx
xorl %ecx, %ecx
1:
movl (%edi, %ecx, 4), %eaxIf the value is negative it’s an apple we don’t want to disappear
test %eax, %eax
js 2fNo need to increment if it’s already greater than snakeLength
cmpl %ebx, %eax
jg 2fIncrement the cell value
incl %eax
movl %eax, (%edi, %ecx, 4)
2: incl %ecx // Loop increment
cmpl $WIDTH*HEIGHT, %ecx
jne 1bCall nib_poll_kbd and check down, up, left and right in succession
call nib_poll_kbdDown
movl $258, %ebx
cmpl %eax, %ebx
jne 1f
movl $0, snakeDirX
movl $1, snakeDirYUp
1: movl $259, %ebx
cmpl %eax, %ebx
jne 1f
movl $0, snakeDirX
movl $-1, snakeDirYLeft
1: movl $260, %ebx
cmpl %eax, %ebx
jne 1f
movl $-1, snakeDirX
movl $0, snakeDirYRight
1: movl $261, %ebx
cmpl %eax, %ebx
jne 1f
movl $1, snakeDirX
movl $0, snakeDirY
1:snakePosX = (snakePosX + snakeDirY + WIDTH) % WIDTH
movl snakePosX, %eax
movl snakeDirX, %ebx
addl %ebx, %eaxAdd width for modulo wrap-around
addl $WIDTH, %eax
xorl %edx, %edx
movl $WIDTH, %ecx
divl %ecx
mov %edx, snakePosXsnakePosY = (snakePosY + snakeDirY + HEIGHT) % HEIGHT
movl snakePosY, %eax
movl snakeDirY, %ebx
addl %ebx, %eaxAdd height for modulo wrap-around
addl $HEIGHT, %eax
xorl %edx, %edx
movl $HEIGHT, %ecx
divl %ecx
mov %edx, snakePosYGet value at head position
movl snakePosY, %eax
movl $WIDTH, %edx
mull %edx
addl snakePosX, %eax
movl (%edi, %eax, 4), %edxCollision with apple if negative
test %edx, %edx
js 1fCompare value with snakeLength
movl snakeLength, %ebx
cmpl %ebx, %edxEnd the game if the snake collides with itself, i.e. %edx < snakeLength
jge 2f
jmp gameOverSpawn a new apple and extend the snake!
1: pushl %edi
pushl %eax
call spawnApple
popl %eax
popl %edi
incl snakeLength
2: movl $0, (%edi, %eax, 4)Proceed to draw
jmp drawjmp debug_draw
draw:Loop on Y-axis
movl $HEIGHT, %ecx
1:Loop on X-axis
movl $WIDTH, %ebx
2:Save loop registers
pushl %ecx
pushl %ebx
subl $1, %ecx
subl $1, %ebxCall nib_put_scr depending of the value in a cell
movl %ecx, %eax
movl $WIDTH, %edx
mull %edx
addl %ebx, %eaxFetch the value of the cell
movl $world, %edi
movl (%edi, %eax, 4), %eaxCompare with the snake length
cmpl snakeLength, %eaxIf the value is negative, it’s an apple
test %eax, %eax
js 4fIf it’s greater or equal to snakeLength, it’s empty
cmpl snakeLength, %eax
jge 3fElse, it’s part of the snake
pushl $'@'
jmp 5f
3: pushl $' '
jmp 5f
4: pushl $'o'
5:Push Y-coordinate
movl %ecx, %eax
pushl %eaxPush X-coordinate
movl %ebx, %eax
pushl %eaxnib_put_scr(int x, int y, int c)
call nib_put_scr
addl $4*3, %espRestore loop registers
popl %ebx
popl %ecxDecrement X
decl %ebxEnd of X-loop
jnz 2bEnd of Y-loop
loop 1bJump back to the start of our game loop!
jmp loopCalled when the game ends. It displays a game over message and score, and quits after 3 seconds.
gameOver:Calculate string length
movl $sgameover_end, %eax
subl $sgameover, %eaxDiscout null terminator
subl $1, %eaxCalculate X-pos in the middle of the screen
movl $2, %ebx
xorl %edx, %edx
divl %ebx
negl %eax
addl $WIDTH/2, %eaxPush snakeLength as score
pushl snakeLengthPush printf-string
pushl $sgameoverPush X-coordinate
pushl %eaxPush Y-coordinate
pushl $HEIGHT/2mvprintw(int y, int x, const char *fmt, ...)
call mvprintw
add $4*3, %espCall refresh to refresh the screen
call refreshSleep for 3 seconds before calling nib_end
pushl $3000*1000
call usleep
addl $4, %esp
call nib_endSpawns an apple in a random location in the world.
spawnApple:
pushl %ebx%ecx = rand() % HEIGHT
call rand
xorl %edx, %edx
movl $HEIGHT, %ebx
divl %ebx
movl %edx, %ecx%ebx = rand() % WIDTH
pushl %ecx
call rand
xorl %edx, %edx
movl $WIDTH, %ebx
divl %ebx
movl %edx, %ebx
popl %ecx movl %ecx, %eax
movl $WIDTH, %edx
mull %edx
addl %ebx, %eaxSet the cell negative to represent an apple
movl $world, %ecx
movl $-1, %edx
movl %edx, (%ecx, %eax, 4)
popl %ebx
retThis is basically the same as draw, except it draws the actual ASCII-value
of the world for debugging purposes.
debug_draw:Loop Y
movl $HEIGHT, %ecx
1:Loop X
movl $WIDTH, %ebx
2:Save loop registers
pushl %ecx
pushl %ebx
subl $1, %ecx
subl $1, %ebxCall nib_put_scr
movl %ecx, %eax
movl $WIDTH, %edx
mull %edx
addl %ebx, %eaxFetch the value of the cell
movl $world, %edi
movl (%edi, %eax, 4), %eax
pushl %eaxY coordinate
movl %ecx, %eax
pushl %eaxX coordinate
movl %ebx, %eax
pushl %eax
call nib_put_scr
addl $4*3, %espRestore loop registers
popl %ebx
popl %ecxDecrement X
decl %ebxEnd of X-loop
jnz 2bEnd of Y-loop
loop 1bJump back to the start of our game loop!
jmp loop