Files
nibbles/nibbles.S
T

373 lines
6.2 KiB
ArmAsm

#define WIDTH 60
#define HEIGHT 40
.bss
// Array of longs representing the game field
.align 4
world: .space WIDTH*HEIGHT*4
.data
// Current length of the snake
snakeLength: .long 0
// Snake position
snakePosX: .long WIDTH / 2
snakePosY: .long HEIGHT / 2
// Snake direction
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:
sgameover: .string " Game over! Score: %i "
sgameover_end:
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
.global start_game
start_game:
// snakeLength = len
movl 4(%esp), %eax
movl %eax, snakeLength
// numApples = n_apples
movl 8(%esp), %eax
movl %eax, numApples
pushl %ebx
pushl %edi
// Call nib_init
call nib_init
// Seed randomness
pushl $0
call time
addl $4, %esp
pushl %eax
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
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 1b
// Spawn initial apples
movl numApples, %ecx
1:
pushl %ecx
call spawnApple
popl %ecx
loop 1b
// Loop forever
loop:
// Sleep for a while
pushl $250*1000
pushl %ecx
call usleep
addl $4, %esp
jmp tick
popl %edi
popl %ebx
ret
// Update stuff
tick:
movl $world, %edi
// Increment cell times
movl snakeLength, %ebx
xorl %ecx, %ecx
1:
movl (%edi, %ecx, 4), %eax
// If the value is negative it's an apple we don't want to disappear
test %eax, %eax
js 2f
// No need to increment if it's already greater than snakeLength
cmpl %ebx, %eax
jg 2f
// Increment the cell value
incl %eax
movl %eax, (%edi, %ecx, 4)
2: incl %ecx // Loop increment
cmpl $WIDTH*HEIGHT, %ecx
jne 1b
// Input
call nib_poll_kbd
// Down
movl $258, %ebx
cmpl %eax, %ebx
jne 1f
movl $0, snakeDirX
movl $1, snakeDirY
1: // Up
movl $259, %ebx
cmpl %eax, %ebx
jne 1f
movl $0, snakeDirX
movl $-1, snakeDirY
1: // Left
movl $260, %ebx
cmpl %eax, %ebx
jne 1f
movl $-1, snakeDirX
movl $0, snakeDirY
1: // Right
movl $261, %ebx
cmpl %eax, %ebx
jne 1f
movl $1, snakeDirX
movl $0, snakeDirY
1:
// Move snake
// snakePosX = (snakePosX + snakeDirY + WIDTH) % WIDTH
movl snakePosX, %eax
movl snakeDirX, %ebx
addl %ebx, %eax
addl $WIDTH, %eax // For modulo wrap-around
xorl %edx, %edx
movl $WIDTH, %ecx
divl %ecx
mov %edx, snakePosX
// snakePosY = (snakePosY + snakeDirY + HEIGHT) % HEIGHT
movl snakePosY, %eax
movl snakeDirY, %ebx
addl %ebx, %eax
addl $HEIGHT, %eax // For modulo wrap-around
xorl %edx, %edx
movl $HEIGHT, %ecx
divl %ecx
mov %edx, snakePosY
// Get value at head position
movl snakePosY, %eax
movl $WIDTH, %edx
mull %edx
addl snakePosX, %eax
movl (%edi, %eax, 4), %edx
// Collision with apple if negative
test %edx, %edx
js 1f
// No need to increment if it's already greater than snakeLength
movl snakeLength, %ebx
cmpl %ebx, %edx
jg 2f
// End the game when the snake collides with itself
jmp gameOver
1: // Spawn a new apple
pushl %edi
pushl %eax
call spawnApple
popl %eax
popl %edi
// Extend the snake!
incl snakeLength
2:
// Set time value to 0 at snake head position
// i = x + y * WIDTH
// %edi = %ebx + %ecx * $WIDTH
movl $0, (%edi, %eax, 4)
jmp draw
//jmp debug_draw
// Draw stuff
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, %ebx
// Call nib_put_scr
// i = x + y * WIDTH
// %eax = %ebx + %ecx * $WIDTH
movl %ecx, %eax
movl $WIDTH, %edx
mull %edx
addl %ebx, %eax
// Fetch the value of the cell
movl $world, %edi
movl (%edi, %eax, 4), %eax
// Compare with the snake length
cmpl snakeLength, %eax
// If the value is negative, it's an apple
test %eax, %eax
js 4f
// If it's greater or equal to snakeLength, it's empty
cmpl snakeLength, %eax
jge 3f
// Else, it's part of the snake
pushl $'@'
jmp 5f
3: pushl $' '
jmp 5f
4: pushl $'o'
5:
movl %ecx, %eax // Y
pushl %eax
movl %ebx, %eax // X
pushl %eax
call nib_put_scr
addl $4*3, %esp
// Restore loop registers
popl %ebx
popl %ecx
decl %ebx // Decrement X
jnz 2b // End of X loop
loop 1b // End of Y loop
jmp loop
// Debug drawing
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, %ebx
// Call nib_put_scr
// i = x + y * WIDTH
// %edi = %ebx + %ecx * $WIDTH
movl %ecx, %eax
movl $WIDTH, %edx
mull %edx
addl %ebx, %eax
// Fetch the value of the cell
movl $world, %edi
movl (%edi, %eax, 4), %eax
pushl %eax
movl %ecx, %eax // Y
pushl %eax
movl %ebx, %eax // X
pushl %eax
call nib_put_scr
addl $4*3, %esp
// Restore loop registers
popl %ebx
popl %ecx
decl %ebx // Decrement X
jnz 2b // End of X loop
loop 1b // End of Y 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