Fixed errors and documented everything
This commit is contained in:
@@ -1,50 +1,80 @@
|
||||
// by Simon Holmberg
|
||||
//
|
||||
// This is a document generated by [docco](http://jashkenas.github.io/docco/).
|
||||
// My report is interleaved with the source code in the form of comments in
|
||||
// [markdown](http://daringfireball.net/projects/markdown/) format.
|
||||
//
|
||||
//
|
||||
#define WIDTH 60
|
||||
#define HEIGHT 40
|
||||
|
||||
// .bss
|
||||
// ====
|
||||
.bss
|
||||
// Array 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
|
||||
|
||||
// .data
|
||||
// =====
|
||||
.data
|
||||
// Current length of the snake
|
||||
snakeLength: .long 0
|
||||
// Snake position
|
||||
// Snake position (starts in the middle of the field)
|
||||
snakePosX: .long WIDTH / 2
|
||||
snakePosY: .long HEIGHT / 2
|
||||
// Snake direction
|
||||
// Snake direction (start moving to the right)
|
||||
snakeDirX: .long 1
|
||||
snakeDirY: .long 0
|
||||
// Number of apples
|
||||
numApples: .long 0
|
||||
// Optional score-keeping
|
||||
score: .long 0
|
||||
// Utility strings
|
||||
snibbles: .ascii "NIBBLES"
|
||||
snibbles_end:
|
||||
// Game 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"
|
||||
|
||||
// .text
|
||||
// =====
|
||||
.text
|
||||
// start_game
|
||||
// ============
|
||||
// `start_game(int len, int n_apples)`
|
||||
.global start_game
|
||||
start_game:
|
||||
// snakeLength = len
|
||||
// Initialization
|
||||
// --------------
|
||||
|
||||
// Save our function arguments for later
|
||||
|
||||
// `snakeLength = len`
|
||||
movl 4(%esp), %eax
|
||||
movl %eax, snakeLength
|
||||
// numApples = n_apples
|
||||
// `numApples = n_apples`
|
||||
movl 8(%esp), %eax
|
||||
movl %eax, numApples
|
||||
|
||||
pushl %ebx
|
||||
pushl %edi
|
||||
|
||||
// Store all registers so we can restore their state before we return later
|
||||
pusha
|
||||
|
||||
// Call nib_init
|
||||
call nib_init
|
||||
// Seed randomness
|
||||
// Seed randomness by calling `srand` with a time value from `time`
|
||||
pushl $0
|
||||
call time
|
||||
addl $4, %esp
|
||||
@@ -52,22 +82,8 @@ start_game:
|
||||
call srand
|
||||
addl $4, %esp
|
||||
|
||||
// Print our arguments
|
||||
//pushl numApples
|
||||
//pushl snakeLength
|
||||
//pushl $string1
|
||||
//call printf
|
||||
//addl $4*3, %esp
|
||||
|
||||
// Print worm position
|
||||
//pushl snakePosY
|
||||
//pushl snakePosX
|
||||
//pushl $string2
|
||||
//call printf
|
||||
//addl $4*3, %esp
|
||||
|
||||
// Initialize the snake array
|
||||
// Set every cell to a time value greater than the snake length
|
||||
// Initialize the snake array by setting every cell to a time value greater
|
||||
// than `snakeLength`
|
||||
movl $world, %edi
|
||||
movl snakeLength, %eax
|
||||
movl $WIDTH*HEIGHT, %ebx
|
||||
@@ -86,25 +102,33 @@ start_game:
|
||||
popl %ecx
|
||||
loop 1b
|
||||
|
||||
// Loop forever
|
||||
// Game loop
|
||||
// ---------
|
||||
loop:
|
||||
// Sleep for a while
|
||||
// Sleep for a while using `usleep` so the snake doesn't wiggle at the
|
||||
// speed of light!
|
||||
pushl $250*1000
|
||||
pushl %ecx
|
||||
call usleep
|
||||
addl $4, %esp
|
||||
|
||||
// Jump to `tick`, our updating function, which later chain-jumps to `draw`
|
||||
jmp tick
|
||||
|
||||
popl %edi
|
||||
popl %ebx
|
||||
// Restore all our registers as they were before `start_game` was called
|
||||
// and return
|
||||
popa
|
||||
ret
|
||||
|
||||
// Update stuff
|
||||
// tick
|
||||
// ====
|
||||
// This is where we update the world.
|
||||
tick:
|
||||
movl $world, %edi
|
||||
|
||||
// Increment cell times
|
||||
// --------------------
|
||||
|
||||
// Loop
|
||||
movl snakeLength, %ebx
|
||||
xorl %ecx, %ecx
|
||||
1:
|
||||
@@ -123,6 +147,9 @@ tick:
|
||||
jne 1b
|
||||
|
||||
// Input
|
||||
// -----
|
||||
|
||||
// Call nib_poll_kbd and check down, up, left and right in succession
|
||||
call nib_poll_kbd
|
||||
// Down
|
||||
movl $258, %ebx
|
||||
@@ -130,20 +157,20 @@ tick:
|
||||
jne 1f
|
||||
movl $0, snakeDirX
|
||||
movl $1, snakeDirY
|
||||
1: // Up
|
||||
movl $259, %ebx
|
||||
// Up
|
||||
1: movl $259, %ebx
|
||||
cmpl %eax, %ebx
|
||||
jne 1f
|
||||
movl $0, snakeDirX
|
||||
movl $-1, snakeDirY
|
||||
1: // Left
|
||||
movl $260, %ebx
|
||||
// Left
|
||||
1: movl $260, %ebx
|
||||
cmpl %eax, %ebx
|
||||
jne 1f
|
||||
movl $-1, snakeDirX
|
||||
movl $0, snakeDirY
|
||||
1: // Right
|
||||
movl $261, %ebx
|
||||
// Right
|
||||
1: movl $261, %ebx
|
||||
cmpl %eax, %ebx
|
||||
jne 1f
|
||||
movl $1, snakeDirX
|
||||
@@ -151,25 +178,32 @@ tick:
|
||||
1:
|
||||
|
||||
// Move snake
|
||||
// snakePosX = (snakePosX + snakeDirY + WIDTH) % WIDTH
|
||||
// ----------
|
||||
|
||||
// `snakePosX = (snakePosX + snakeDirY + WIDTH) % WIDTH`
|
||||
movl snakePosX, %eax
|
||||
movl snakeDirX, %ebx
|
||||
addl %ebx, %eax
|
||||
addl $WIDTH, %eax // For modulo wrap-around
|
||||
// Add width for modulo wrap-around
|
||||
addl $WIDTH, %eax
|
||||
xorl %edx, %edx
|
||||
movl $WIDTH, %ecx
|
||||
divl %ecx
|
||||
mov %edx, snakePosX
|
||||
// snakePosY = (snakePosY + snakeDirY + HEIGHT) % HEIGHT
|
||||
// `snakePosY = (snakePosY + snakeDirY + HEIGHT) % HEIGHT`
|
||||
movl snakePosY, %eax
|
||||
movl snakeDirY, %ebx
|
||||
addl %ebx, %eax
|
||||
addl $HEIGHT, %eax // For modulo wrap-around
|
||||
// Add height for modulo wrap-around
|
||||
addl $HEIGHT, %eax
|
||||
xorl %edx, %edx
|
||||
movl $HEIGHT, %ecx
|
||||
divl %ecx
|
||||
mov %edx, snakePosY
|
||||
|
||||
// Collision
|
||||
// ---------
|
||||
|
||||
// Get value at head position
|
||||
movl snakePosY, %eax
|
||||
movl $WIDTH, %edx
|
||||
@@ -180,36 +214,42 @@ tick:
|
||||
// Collision with apple if negative
|
||||
test %edx, %edx
|
||||
js 1f
|
||||
// No need to increment if it's already greater than snakeLength
|
||||
// Compare value with `snakeLength`
|
||||
movl snakeLength, %ebx
|
||||
cmpl %ebx, %edx
|
||||
jg 2f
|
||||
// End the game when the snake collides with itself
|
||||
// End the game if the snake collides with itself, i.e. `%edx < snakeLength`
|
||||
jge 2f
|
||||
jmp gameOver
|
||||
1: // Spawn a new apple
|
||||
pushl %edi
|
||||
// Spawn a new apple and extend the snake!
|
||||
1: pushl %edi
|
||||
pushl %eax
|
||||
call spawnApple
|
||||
popl %eax
|
||||
popl %edi
|
||||
// Extend the snake!
|
||||
incl snakeLength
|
||||
2:
|
||||
|
||||
// Update world
|
||||
// ------------
|
||||
|
||||
// Set time value to 0 at snake head position
|
||||
// i = x + y * WIDTH
|
||||
// %edi = %ebx + %ecx * $WIDTH
|
||||
//
|
||||
// `i = x + y * WIDTH`
|
||||
//
|
||||
// `%edi = %ebx + %ecx * $WIDTH`
|
||||
movl $0, (%edi, %eax, 4)
|
||||
|
||||
// Proceed to `draw`
|
||||
jmp draw
|
||||
//jmp debug_draw
|
||||
|
||||
// Draw stuff
|
||||
// draw
|
||||
// ====
|
||||
draw:
|
||||
// Loop Y
|
||||
// Loop on Y-axis
|
||||
movl $HEIGHT, %ecx
|
||||
1:
|
||||
// Loop X
|
||||
// Loop on X-axis
|
||||
movl $WIDTH, %ebx
|
||||
2:
|
||||
// Save loop registers
|
||||
@@ -218,10 +258,11 @@ draw:
|
||||
subl $1, %ecx
|
||||
subl $1, %ebx
|
||||
|
||||
// Call nib_put_scr
|
||||
// Call `nib_put_scr` depending of the value in a cell
|
||||
|
||||
// i = x + y * WIDTH
|
||||
// %eax = %ebx + %ecx * $WIDTH
|
||||
// `i = x + y * WIDTH`
|
||||
//
|
||||
// `%eax = %ebx + %ecx * $WIDTH`
|
||||
movl %ecx, %eax
|
||||
movl $WIDTH, %edx
|
||||
mull %edx
|
||||
@@ -246,12 +287,15 @@ draw:
|
||||
4: pushl $'o'
|
||||
5:
|
||||
|
||||
movl %ecx, %eax // Y
|
||||
// Push Y-coordinate
|
||||
movl %ecx, %eax
|
||||
pushl %eax
|
||||
|
||||
movl %ebx, %eax // X
|
||||
// Push X-coordinate
|
||||
movl %ebx, %eax
|
||||
pushl %eax
|
||||
|
||||
// `nib_put_scr(int x, int y, int c)`
|
||||
call nib_put_scr
|
||||
addl $4*3, %esp
|
||||
|
||||
@@ -259,13 +303,101 @@ draw:
|
||||
popl %ebx
|
||||
popl %ecx
|
||||
|
||||
decl %ebx // Decrement X
|
||||
jnz 2b // End of X loop
|
||||
loop 1b // End of Y loop
|
||||
// Decrement X
|
||||
decl %ebx
|
||||
// End of X-loop
|
||||
jnz 2b
|
||||
// End of Y-loop
|
||||
loop 1b
|
||||
|
||||
// Jump back to the start of our game loop!
|
||||
jmp loop
|
||||
|
||||
// Debug drawing
|
||||
// gameOver
|
||||
// ========
|
||||
// Called when the game ends. It displays a game over message
|
||||
// and score, and quits after **3 seconds**.
|
||||
gameOver:
|
||||
// Calculate message position
|
||||
// --------------------------
|
||||
|
||||
// Calculate string length
|
||||
movl $sgameover_end, %eax
|
||||
subl $sgameover, %eax
|
||||
// Discout null terminator
|
||||
subl $1, %eax
|
||||
|
||||
// Calculate X-pos in the middle of the screen
|
||||
movl $2, %ebx
|
||||
xorl %edx, %edx
|
||||
divl %ebx
|
||||
negl %eax
|
||||
addl $WIDTH/2, %eax
|
||||
|
||||
// Print game over message
|
||||
// -----------------------
|
||||
|
||||
// Push `snakeLength` as score
|
||||
pushl snakeLength
|
||||
// Push printf-string
|
||||
pushl $sgameover
|
||||
// Push X-coordinate
|
||||
pushl %eax
|
||||
// Push Y-coordinate
|
||||
pushl $HEIGHT/2
|
||||
// `mvprintw(int y, int x, const char *fmt, ...)`
|
||||
call mvprintw
|
||||
add $4*3, %esp
|
||||
// Call `refresh` to refresh the screen
|
||||
call refresh
|
||||
|
||||
// Sleep for 3 seconds before calling `nib_end`
|
||||
pushl $3000*1000
|
||||
call usleep
|
||||
addl $4, %esp
|
||||
call nib_end
|
||||
|
||||
// spawnApple
|
||||
// ==========
|
||||
// Spawns 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
|
||||
|
||||
// `i = x + y * WIDTH`
|
||||
//
|
||||
// `%eax = %ebx + %ecx * $WIDTH`
|
||||
movl %ecx, %eax
|
||||
movl $WIDTH, %edx
|
||||
mull %edx
|
||||
addl %ebx, %eax
|
||||
|
||||
// Set the cell negative to represent an apple
|
||||
movl $world, %ecx
|
||||
movl $-1, %edx
|
||||
movl %edx, (%ecx, %eax, 4)
|
||||
|
||||
popl %ebx
|
||||
ret
|
||||
|
||||
// debug_draw
|
||||
// =============
|
||||
// This 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
|
||||
@@ -281,8 +413,9 @@ debug_draw:
|
||||
|
||||
// Call nib_put_scr
|
||||
|
||||
// i = x + y * WIDTH
|
||||
// %edi = %ebx + %ecx * $WIDTH
|
||||
// `i = x + y * WIDTH`
|
||||
//
|
||||
// `%edi = %ebx + %ecx * $WIDTH`
|
||||
movl %ecx, %eax
|
||||
movl $WIDTH, %edx
|
||||
mull %edx
|
||||
@@ -292,10 +425,12 @@ debug_draw:
|
||||
movl (%edi, %eax, 4), %eax
|
||||
pushl %eax
|
||||
|
||||
movl %ecx, %eax // Y
|
||||
// Y coordinate
|
||||
movl %ecx, %eax
|
||||
pushl %eax
|
||||
|
||||
movl %ebx, %eax // X
|
||||
// X coordinate
|
||||
movl %ebx, %eax
|
||||
pushl %eax
|
||||
|
||||
call nib_put_scr
|
||||
@@ -305,68 +440,14 @@ debug_draw:
|
||||
popl %ebx
|
||||
popl %ecx
|
||||
|
||||
decl %ebx // Decrement X
|
||||
jnz 2b // End of X loop
|
||||
loop 1b // End of Y loop
|
||||
// Decrement X
|
||||
decl %ebx
|
||||
// End of X-loop
|
||||
jnz 2b
|
||||
// End of Y-loop
|
||||
loop 1b
|
||||
|
||||
// Jump back to the start of our game loop!
|
||||
|
||||
jmp loop
|
||||
|
||||
gameOver:
|
||||
// Calculate string length
|
||||
movl $sgameover_end, %eax
|
||||
subl $sgameover, %eax
|
||||
subl $1, %eax // Discount null terminator
|
||||
|
||||
// Calculate X-pos
|
||||
movl $2, %ebx
|
||||
xorl %edx, %edx
|
||||
divl %ebx
|
||||
negl %eax
|
||||
addl $WIDTH/2, %eax
|
||||
|
||||
// Print game over message
|
||||
pushl snakeLength
|
||||
pushl $sgameover
|
||||
pushl %eax // X
|
||||
pushl $HEIGHT/2 // Y
|
||||
call mvprintw
|
||||
add $4*3, %esp
|
||||
call refresh
|
||||
|
||||
pushl $3000*1000
|
||||
call usleep
|
||||
addl $4, %esp
|
||||
call nib_end
|
||||
|
||||
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
|
||||
|
||||
// i = x + y * WIDTH
|
||||
// %eax = %ebx + %ecx * $WIDTH
|
||||
movl %ecx, %eax
|
||||
movl $WIDTH, %edx
|
||||
mull %edx
|
||||
addl %ebx, %eax
|
||||
|
||||
// Set the cell negative to represent an apple
|
||||
movl $world, %ecx
|
||||
movl $-1, %edx
|
||||
movl %edx, (%ecx, %eax, 4)
|
||||
|
||||
popl %ebx
|
||||
ret
|
||||
|
||||
Reference in New Issue
Block a user