Aidan Mala

IT professional

Applied ARM-Assembly Software Engineering: A Pure Arm Implementation of Pong on Micro:bit

This was written 100% by hand without any assistance from our silicon based overlords.

Initialisation

.syntax unified
.global main

.type main, %function
main:
  
  initialise:
    @ Set all pins on microbit to output
    bl initialise_output_pins
    
    @ start animation on death and startup
    game_start:
    @ print heart on
    ldr r1, =heart_on
    ldr r0, [r1]
    ldr r1, =game_state
    str r0, [r1]
    mov r0, 0xfff

    pi_1:
      cmp r0, 0
      beq exit_pi_1
      bl print_game
      sub r0, 1
      b pi_1
    exit_pi_1:

    @ print heart off
    ldr r1, =heart_off
    ldr r0, [r1]
    ldr r1, =game_state
    str r0, [r1]
    mov r0, 0xfff

    pi_2: 
      cmp r0, 0
      beq exit_pi_2
      bl print_game
      sub r0, 1
      b pi_2
    exit_pi_2:

    @ set the game state to intended initial gamestate
    ldr r1, =reset_game_state
    ldr r0, [r1]
    ldr r2, =game_state
    str r0, [r2]

    mov r4, 4 @ Direction for ball  between 0 and 5 inclusive
    mov r5, 6 @ Position for ball between 0 and 24 inclusive
    mov r6, 2 @ Direction for paddle between 0 and 1 inclusive
    mov r7, 1 @ Position for paddle between 0 and 4 inclusive
      @ note: that the position of the paddle is of the left pixel
      @       so the other part of the paddle will always be one
      @       to the right.
    
    push {r4-r7} @ Store values

Game loop

  play_game:

    mov r0, 0x2000 @ good pause amount

    pause_g: @pause and print the game
      cmp r0, 0
      beq exit_g
      bl print_game
      sub r0, 1
      b pause_g
    exit_g:


    bl step_game @ step the entire game
    
    @ checks if game hase been lost, if it has restart
    cmp r5, 4
    ble game_start

    @ else:
    b play_game

Update the game state

step_game:
  @ steps the entire game to its next logical state

    pop {r4-r7} @ get position and direction values
    push {lr}
   
    step_ball:
    @ Move ball according to the direction register

      .type move_ball, %function
      @ args:
      @   r4: direction of ball
      @   r5: position of ball
      @   r6: direction of paddle
      @   r7: position of paddle
      move_ball:

        bl check_game

        cmp r4, 0
        beq move_up_left
        cmp r4, 1
        beq move_up
        cmp r4, 2
        beq move_up_right
        cmp r4, 3
        beq move_down_left
        cmp r4, 4
        beq move_down
        cmp r4, 5
        beq move_down_right

        move_up_left:
          mov r3, 6
          b change_gamestate_b

        move_up:
          mov r3, 5
          b change_gamestate_b
        
        move_up_right:
          mov r3, 4
          b change_gamestate_b

        move_down_left:
          mov r3, -4
          b change_gamestate_b

        move_down:
          mov r3, -5
          b change_gamestate_b
        
        move_down_right:
          mov r3, -6
          b change_gamestate_b

        .type change_gamestate_b, %function
        @ args:
        @   r3: how much to change ball position by
        change_gamestate_b:
          

          ldr r1, =game_state
          ldr r2, [r1]
          push {r1}
        
          @ make a variable to remove position of last ball
          mov r0, 0b1
          mov r1, 24
          sub r1, r5
          lsl r0, r1

          eor r2, r0 @ remove last position of ball in game state

          add r5, r3 @ changes the position of the ball

          @ add new position of ball to game state
          mov r0, 0b1
          mov r1, 24
          sub r1, r5
          lsl r0, r1
          orr r2, r0

          pop {r1}
          str r2, [r1] @ store the game back into memory

Update the paddle

    step_paddle:
    @ If paddle can move:
    @   move paddle
    @ Else:
    @   step timer
      cmp r6, 0
      beq move_left_p
      cmp r6, 1
      beq move_right_p
      cmp r6, 2
      beq no_move_p
      
      move_left_p:
        ldr r1, =game_state
        ldr r2, [r1]
        push {r1}
      
        @ makes a copy of r7 to alter
        mov r3, r7
        sub r3, 1

        @ removes the right side of the paddle from game state
        mov r0, 0b1
        mov r1, 24
        sub r1, r3
        lsl r0, r1
        eor r2, r0

        @ adds pixel to the left
        sub r1, 2
        mov r0, 0b1
        lsl r0, r1
        orr r2, r0

        add r7, 1 @ changes location of the paddle

        pop {r1}
        str r2, [r1]

        pop {lr}
        push {r4-r7}
        bx lr
      
       move_right_p:
        ldr r1, =game_state
        ldr r2, [r1]
        push {r1}
        @ makes a copy of r7 to alter
        mov r3, r7

        @ removes the left side of the paddle from game state
        mov r0, 0b1
        mov r1, 24
        sub r1, r3
        lsl r0, r1
        eor r2, r0

        @ adds pixel to the right
        add r1, 2
        mov r0, 0b1
        lsl r0, r1
        orr r2, r0

        sub r7, 1 @ changes location of the paddle

        pop {r1}
        str r2, [r1] @ store result back

        pop {lr}
        push {r4 - r7}
        bx lr

      no_move_p:
        pop {lr}
        push {r4-r7}
        bx lr

Handle the ball rebounding off wall/paddle

Rebounding off the walls

  check_game:
    @ checks if ball is on bound or paddle and changes direction accordingly


    .type check_ball, %function
    @ args:
    @   r5: position of ball
    @ returns:
    @   r4: adjested direction
    check_ball:
    @ Checks if the ball is on any bound
      
    @ Checks if ball is in top corner
    cmp r5, 20
    beq corner_rebound
    cmp r5, 24
    beq corner_rebound

    @ Checks if ball on roof
    cmp r5, 20
    bgt roof_rebound

    push {lr}
    @ Checks if ball is on right wall
    cmp r5, 5
    beq right_wall_rebound

    cmp r5, 10
    beq right_wall_rebound
    
    cmp r5, 15
    beq right_wall_rebound

    @ Checks if ball is on left wall
    cmp r5, 9
    beq left_wall_rebound

    cmp r5, 14
    beq left_wall_rebound

    cmp r5, 19
    beq left_wall_rebound

    @ if not on the edge, check if ball is on paddle
    b check_paddle 

    corner_rebound:
      push {lr}
      bl random_number @ Generate random number
      pop {lr}
      @ See if random number is odd
      mov r1, 0b1
      tst r0, r1
      bne set_down
      b set_opp

      set_down:
        @ set direction of ball to down
        
        mov r4, 0x4
        b check_paddle_pos @ returns to step ball
      
      set_opp:
        @ if direction up right, set direction down left
        cmp r4, 2
        beq set_down_left 
        
        @ else set the direction to down right
        mov r4, 5
        b check_paddle_pos @ returns to step ball

        set_down_left:
        mov r4, 3
        b check_paddle_pos @ returns to step ball
    
    roof_rebound:
      add r4, 3 @ changes direction of ball to the downwards (in same direction)
      b check_paddle_pos @ returns to step ball

    left_wall_rebound:
    @ makes sure when rebounding off wall it flips with direction
      pop {lr} @ needs to pop this as was not used (so later on can recall to main thred)

      cmp r4, 3
      blt lw_rebound_up @ sees if the ball is going up or down

      @ if it is going down, set to down rebound
      cmp r4, 4
      beq check_paddle_pos @ if ball going down directly, don't rebound

      mov r4, 5
      b check_paddle_pos 

        @ else set to up rebound
        lw_rebound_up:

          cmp r4, 1
          beq check_paddle_pos @ if ball going up directly, don't rebound

          mov r4, 2
          b check_paddle_pos

    
    right_wall_rebound:
    @ makes sure when rebounding off wall it flips with direction
      pop {lr} @ needs to pop this as was not used (so later on can recall to main thred)

      cmp r4, 3
      blt rw_rebound_up @ sees if the ball is going up or down

      @ if it is going down, set to down rebound
      cmp r4, 4
      beq check_paddle_pos @ if ball going down directly, don't rebound

      mov r4, 3
      b check_paddle_pos 

        @ else set to up rebound
        rw_rebound_up:
          cmp r4, 1
          beq check_paddle_pos @ if ball going up directly, don't rebound

          mov r4, 0
          b check_paddle_pos

Rebounding off the paddle



    check_paddle:
    @ Checks if the ball is on the paddle
    @ If it is:
    @   go direction according to which pixel the ball hits on the paddle and if the paddle is moving
    
      mov r1, r7 @ coppies pos of paddle to scratch register

      add r1, 4 @ changing value of r1 to compair it to pssilble row
      cmp r1, r5 @ checks if ball is on paddle, left side
      IT eq
      bleq paddle_rebound

      add r1, 1
      cmp r1, r5 @ checks if ball is on paddle, right side
      IT eq
      bleq paddle_rebound

      bl check_paddle_pos

      pop {lr} @ return to step ball

      bx lr

      paddle_rebound:
        @ ball rebound from paddle
        @ changes direction of ball to something random
        push {lr}
        bl random_number @ Generate random number
        pop {lr}
        @ See if random number is odd
        mov r1, 0b1
        tst r0, r1
        bne set_up_p @ change the direction of the ball to directly up
        b set_away_p @ set the direction of the ball to logically opposite

        set_up_p:
          mov r4, 1
          bx lr
        
        set_away_p:
          @ checks if ball is heading down
          cmp r4, 4
          beq mix_dir @ if it is make sure it doesn't head straight up

          sub r4, 3 @ else make it go opposite direction
          bx lr
          
          mix_dir:
            @ set ball going left
            mov r4, 0
            bx lr

Paddle AI

      check_paddle_pos:
        push {lr}

        bl ai_dir

        @ checks if paddle will try and go into the right wall
        cmp r7, 1
        IT eq
        bleq check_paddle_dir_right

        @ checks if paddle will try and go into the left wall
        cmp r7, 4
        IT eq
        bleq check_paddle_dir_left

        pop {lr}
        bx lr

        check_paddle_dir_right:
          cmp r6, 1
          IT ne
          bxne lr @ return if it is not

          @ else set direction to left 
          mov r6, 0
          bx lr
        
        check_paddle_dir_left:
          cmp r6, 0
          IT ne
          bxne lr @ returns if not trying to go into wall

          @ else set direction to right
          mov r6, 1
          bx lr
        
        ai_dir:
        @ change direction based on random number gen
          push {lr}
          bl random_number
          pop {lr}

          mov r1, 0b1
          tst r0, r1
          bne change_paddle_left

          mov r6, 1 @ change paddle direction right (ev)
          bx lr

          change_paddle_left:
          mov r6, 0 @ change paddle direction left
          bx lr

Random Number Generator

  .type random_number, %function
  @ returns:
  @      r0: randomly generated number

  random_number:
    ldr r1, =0x4000D000 @ Base address to RNG
    mov r0, 0b1
    str r0, [r1] @ starts random number genorator
    mov r0, 0xffff
    pause_r:
      cmp r0, 0
      beq exit_r
      sub r0, 1
      b pause_r

    exit_r:
    @ stops rRNG
    mov r0, 0b1 
    str r0, [r1, 0x4]
    ldr r0, [r1, 0x508] @ stores result of RNG to output
    bx lr

Static Variables

.data
game_state:
  .word  0b1100001000000000000000000
reset_game_state:
  .word  0b1100001000000000000000000

heart_on:
  .word 0b0010001110111111111101010 

heart_off:
  .word 0b0010001010100011010101010