#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