@ Space Interlopers
@ by Robert Burton
@ 2025-12-03

.new_game:

    @ r15 is the start address of the screen memory
    @ which is 10000000 00000000
    @ we get this by shifting 0x80 over 8 places
    mov r15 0x80
    mov r1 0x08
    lsft r15 r15 r1

    @ r14 is the button state register address
    mov r0 0x10
    add r14 r15 r0

    @ r13 is the screen memory location of the player spaceship
    @ which is 10000000 0000000F
    mov r2 0x0F
    add r13 r15 r2

    @ r12 is the player spaceship
    @ which starts in the center at 00000011 10000000 
    mov r12 0x07
    mov r0 0x07
    lsft r12 r12 r0

    @ r11 bullet screen memory address (y value)
    mov r2 0x01
    sub r11 r13 r2

    @ r10 bullet x value
    @ which starts in the center at 00000001 00000000 
    mov r10 0x01
    lsft r10 r10 r1

    @ r9 is how far down the first enemy line 
    @ is from the top of the screen
    @ which starts at 1
    mov r9 0x01

    @ r8 enemy movement direction and speed
    @ lowest bit is direction left = 1, right = 0
    @ highest bit is move = 1, pause = 0
    @ example:  move  left = 10000000 00000001
    @ example: pause  left = 00000000 00000001
    @ example:  move right = 10000000 00000000
    @ example: pause right = 00000000 00000000
    mov r8 0x01

    @ r7 first enemy row layout (top)
    @ which is 00000100 01000100
    mov r7 0x04
    mov r0 0x08
    lsft r7 r7 r0
    mov r0 0x44
    add r7 r7 r0

    @ r6 second enemy row layout
    @ which is 00010001 00010000
    mov r0 0x03
    rsft r6 r7 r0
    mov r0 0x01
    lsft r6 r6 r0

    @ r5 third enemy row layout
    @ which is 01000100 01000100 (same as first)
    mov r0 0x00
    add r5 r7 r0

    @ r4 fourth enemy row layout (bottom)
    @ which is 00010001 00010000 (same as second)
    add r4 r6 r0
    
    b .gameplay_loop

@ breakpoints are used for 
@ branches too big to do
@ in single jump
@
@ clear screen then jump 
@ back to start of program   
.new_game_breakpoint_1:
    mov r0 0x10
    mov r2 0x01
    mov r3 0x00
.clear_screen:
    sub r0 r0 r2
    add r1 r15 r0
    str r3 [r1]
    bnz r0 .clear_screen 
    b .new_game

@ main loop of game
@ keep looping until all 
@ enemies are cleared or 
@ enemies reach bottom rows
.gameplay_loop:
    @ draw bullet if between top of screen 
    @ and first enemy row or between  
    @ bottom of screen and last enemy row
    add r0 r15 r9
    slt r1 r11 r0
    bnz r1 .draw_bullet
    mov r3 0x3
    add r0 r0 r3
    slt r1 r11 r0
    bnz r1 .blackout_prev_bullet
.draw_bullet:
    str r10 [r11]

@ blackout bullet if previous bullet:
@ between top of screen and first enemy row
@ or between bottom of screen and bottom enemy row
.blackout_prev_bullet:
    add r0 r15 r9
    mov r2 0x01
    add r2 r11 r2
    slt r1 r2 r0
    bnz r1 .draw_bullet_blackout
    mov r3 0x3
    add r0 r0 r3
    slt r1 r2 r0
    bnz r1 .draw_enemies
    @ do not draw over spaceship
    sub r1 r2 r13
    bz r1 .draw_enemies
.draw_bullet_blackout:
    mov r3 0x00
    str r3 [r2]

@ draw enemy rows on screen
.draw_enemies:
    @ r2 is 1 to increment the pointer
    mov r2 0x01

    @ r9 is how far down the
    @ enemy line is from top (r15)
    add r15 r15 r9

    @ set r3 = first enemy row layout    
    mov r3 0x00
    add r3 r7 r3

    @ draw bullet and enemy row together
    @ if bullet is on first row
    sub r1 r11 r15
    bnz r1 .draw_enemy_first_row
    orr r3 r3 r10

.draw_enemy_first_row:
    str r3 [r15]

    @ increment pointer to next row
    add r15 r15 r2

    @ set r3 = second enemy row layout
    mov r3 0x00
    add r3 r6 r3

    @ draw bullet and enemy row together
    @ if bullet is on second row
    sub r1 r11 r15
    bnz r1 .draw_enemy_second_row
    orr r3 r3 r10

.draw_enemy_second_row:
    @ do not draw row if it reaches the spaceship
    @ this happens when lower rows have been 
    @ cleared but are still being moved down
    slt r0 r15 r13
    bz r0 .skip_second_row
    str r3 [r15]

.skip_second_row:
    @ increment pointer to next row
    add r15 r15 r2

    @ set r3 = third enemy row layout
    mov r3 0x00
    add r3 r5 r3

    @ draw bullet and enemy row together
    @ if bullet is on third row
    sub r1 r11 r15
    bnz r1 .draw_enemy_third_row
    orr r3 r3 r10

.draw_enemy_third_row:
    @ do not draw row if it reaches the spaceship
    @ this happens when lower rows have been 
    @ cleared but are still being moved down
    slt r0 r15 r13
    bz r0 .skip_third_row
    str r3 [r15]

.skip_third_row:
    @ increment pointer to next row
    add r15 r15 r2

    @ set r3 = fourth enemy row layout
    mov r3 0x00
    add r3 r4 r3

    @ draw bullet and enemy row together
    @ if bullet is on fourth row
    sub r1 r11 r15
    bnz r1 .draw_enemy_fourth_row
    orr r3 r3 r10

.draw_enemy_fourth_row:
    @ do not draw row if it reaches the spaceship
    @ this happens when lower rows have been 
    @ cleared but are still being moved down
    slt r0 r15 r13
    bz r0 .reset_screen_pointer
    str r3 [r15]

.reset_screen_pointer:
    mov r2 0x03
    sub r15 r15 r2
    sub r15 r15 r9

.draw_spaceship:
    str r12 [r13]

@ button inputs for controlling spaceship
.handle_buttons:
    @ get current button state
    ldr r3 [r14]

    @ fire (A B or Up) 010011
    mov r0 0x13
    and r1 r3 r0
    bnz r1 .fire

    @ right 000100
    mov r0 0x04
    and r1 r3 r0
    bnz r1 .right

    @ left 001000
    mov r0 0x08
    and r1 r3 r0
    bnz r1 .left

    b .move_enemies

@ fire bullet
.fire:
    @ check if bullet is fired
    mov r0 0x01
    sub r2 r13 r0
    sub r1 r2 r11
    bnz r1 .move_enemies
    @ move bullet up 1
    @ and erase old bullet
    mov r3 0x00
    str r3 [r11]
    sub r11 r11 r0
    b .move_enemies

@ move player spaceship to right
.right:
    @ do not move if at right edge
    @ compare ship value to 00000000 00000001
    mov r0 0x01
    and r1 r12 r0
    bnz r1 .move_enemies
    @ move ship right
    rsft r12 r12 r0
    @ set high bit (left) to 0 
    @ signed right shift may add extra 1
    not r0 r15
    and r12 r12 r0
    @ shift bullet if it has not been fired
    mov r0 0x01
    sub r2 r13 r0
    sub r1 r2 r11
    bnz r1 .move_enemies
    rsft r10 r10 r0
    b .move_enemies

@ move player spaceship to left
.left:
    @ do not move if at left edge
    @ compare ship value to 10000000 00000000
    and r1 r12 r15
    bnz r1 .move_enemies
    @ move ship left
    mov r0 0x01
    lsft r12 r12 r0
    @ shift bullet if it has not been fired
    mov r0 0x01
    sub r2 r13 r0
    sub r1 r2 r11
    bnz r1 .move_enemies
    lsft r10 r10 r0
    b .move_enemies

@ breakpoints are used for 
@ branches too big to do
@ in single jump
.gameplay_loop_breakpoint:
    mov r0 0x00
    b .gameplay_loop

@ fill screen then jump 
@ up to breakpoint 1
.new_game_breakpoint_2:
    mov r0 0x10
    mov r2 0x01
    mov r3 0x00
    not r3 r3
.fill_screen:
    sub r0 r0 r2
    add r1 r15 r0
    str r3 [r1]
    bnz r0 .fill_screen 
    b .new_game_breakpoint_1

@ move enemy rows left or right
@ move down when at edge of screen
@ enemies move every other loop
.move_enemies:
    @ check if enemies move this loop
    @ r8 highest bit 1 = move, 0 = wait
    and r1 r8 r15
    @ flip move bit
    eor r8 r8 r15
    bz r1 .move_bullet

    @ check which direction to shift
    @ r8 lowest bit 1 = left, 0 = right
    mov r0 0x01
    and r1 r8 r0
    bz r8 .shift_enemies_right

    @ shift all enemy rows left
    lsft r7 r7 r0
    lsft r6 r6 r0
    lsft r5 r5 r0
    lsft r4 r4 r0
    b .check_enemy_at_edge

@ shift all enemy rows right
.shift_enemies_right:
    mov r0 0x01
    rsft r7 r7 r0
    rsft r6 r6 r0
    rsft r5 r5 r0
    rsft r4 r4 r0
    @ remove extra left (high) bit 
    @ from signed right shift
    not r0 r15
    and r7 r7 r0
    and r6 r6 r0
    and r5 r5 r0
    and r4 r4 r0

@ check if any enemy is touching 
@ either edge of the screen
@ compare rows to 10000000 00000001
.check_enemy_at_edge:
    mov r1 0x01
    add r0 r15 r1
    and r1 r7 r0
    bnz r1 .switch_enemy_direction
    and r1 r6 r0
    bnz r1 .switch_enemy_direction
    and r1 r5 r0
    bnz r1 .switch_enemy_direction
    and r1 r4 r0
    bnz r1 .switch_enemy_direction
    b .move_bullet

@ switch direction enemy lines move
@ enemy lines move down 
@ when they hit the right edge
.switch_enemy_direction:
    @ flip direction bit (lowest)
    mov r1 0x01
    eor r8 r8 r1
    @ skip moving down when direction bit = 1
    bz r8 .move_bullet

    @ erase old top line and 
    @ move enemy lines down 1
    mov r0 0x00
    add r2 r9 r15
    str r0 [r2]
    add r9 r9 r1
    

@ move bullet up one if it has been fired
@ and check if hit enemy
.move_bullet:
    @ skip move if bullet 
    @ still on starting line
    mov r0 0x01
    sub r2 r13 r0
    sub r1 r2 r11
    bz r1 .check_win

    @ move bullet up 1
    sub r11 r11 r0

    @ check if bullet past top of screen
    and r1 r11 r15
    bz r1 .reset_bullet

    @ check for enemy hit
    @ check if bullet it on enemy row
    mov r2 0x01
    add r0 r15 r9
    sub r1 r11 r0
    bz r1 .check_hit_first_row
    add r0 r0 r2
    sub r1 r11 r0
    bz r1 .check_hit_second_row
    add r0 r0 r2
    sub r1 r11 r0
    bz r1 .check_hit_third_row
    add r0 r0 r2
    sub r1 r11 r0
    bz r1 .check_hit_fourth_row
    b .check_win

@ check whichever row bullet is on
@ to see if it overlaps with enemy
.check_hit_first_row:
    and r1 r10 r7
    bz r1 .check_win
    eor r7 r10 r7
    b .reset_bullet
.check_hit_second_row:
    and r1 r10 r6
    bz r1 .check_win
    eor r6 r10 r6
    b .reset_bullet
.check_hit_third_row:
    and r1 r10 r5
    bz r1 .check_win
    eor r5 r10 r5
    b .reset_bullet
.check_hit_fourth_row:
    and r1 r10 r4
    bz r1 .check_win
    eor r4 r10 r4
    b .reset_bullet

@ when bullet goes off top edge of screen
@ or hits an enemy, reset bullet to
@ top of spaceship
.reset_bullet:
    mov r0 0x01
    add r2 r11 r0
    mov r3 0x00
    str r3 [r2]
    @ reset y value
    mov r2 0x01
    sub r11 r13 r2
    @ re-center on spaceship
    mov r10 0x01

@ start at right edge and loop 
@ until find edge of spaceship
.find_ship_edge:
    and r1 r10 r12
    bnz r1 .center_bullet
    lsft r10 r10 r0
    b .find_ship_edge

@ shift bullet from
@ edge of ship to center
.center_bullet:
    lsft r10 r10 r0

@ check each enemy row for remaining enemies
@ if all rows empty, reset game
@ r0 tracks lowest row with enemies
.check_win:
    @check fourth row empty
    bz r4 .check_third_row_empty
    mov r0 0x03
    b .check_gameover
.check_third_row_empty:
    bz r5 .check_second_row_empty
    mov r0 0x02
    b .check_gameover
.check_second_row_empty:
    bz r6 .check_first_row_empty
    mov r0 0x01
    b .check_gameover
.check_first_row_empty:
    bz r7 .new_game_breakpoint_2
    mov r0 0x00
    b .check_gameover

@ if row with enemies reaches the 
@ line where the bullet starts
@ reset the game
@
@ branch is too many lines away
@ first jump to breakpoint 2
@ then jump to breakpoint 1
@ then jump to start of loop
.check_gameover:
    add r1 r15 r9
    add r1 r1 r0
    mov r0 0x01
    sub r2 r13 r0
    sub r0 r1 r2
    bz r0 .new_game_breakpoint_2

    @ repeat the gameplay loop
    @
    @ branch is too many lines away
    @ first jump to breakpoint 
    @ then jump to start of loop
    b .gameplay_loop_breakpoint
