#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" 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 150 ms pushl $500*1000 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