@ bit Generations: Orbital - Matching Decompilation
@ Binary-accurate C code that compiles to match the original ROM
@ Using agbcc (GBA C compiler)

.include "macros.inc"

@ ============================================================
@ Object System - Core data structure for all game objects
@ ============================================================

@ Object structure offsets (in bytes, as word offsets multiply by 4)
.equ OBJ_POS_X,        0x00  @ int32_t - X position (Q12 fixed-point)
.equ OBJ_POS_Y,        0x04  @ int32_t - Y position (Q12 fixed-point)
.equ OBJ_PREV_X,       0x08  @ int32_t - Previous X position
.equ OBJ_PREV_Y,       0x0C  @ int32_t - Previous Y position
.equ OBJ_SCREEN_X,     0x10  @ int32_t - Screen X position
.equ OBJ_SCREEN_Y,     0x14  @ int32_t - Screen Y position
.equ OBJ_SCREEN_W,     0x18  @ int32_t - Screen width
.equ OBJ_SCREEN_H,     0x1C  @ int32_t - Screen height
.equ OBJ_DISPLAY_W,    0x4C  @ int32_t - Display width (240)
.equ OBJ_DISPLAY_H,    0x50  @ int32_t - Display height (160)

@ ============================================================
@ Function: SetObjectScreenPos
@ Description: Sets the screen X and Y position of an object
@ Input: r0 = object pointer, r1 = screen_x, r2 = screen_y
@ ============================================================
thumb_func_start SetObjectScreenPos
SetObjectScreenPos:
    str r1, [r0, #OBJ_SCREEN_X]
    str r2, [r0, #OBJ_SCREEN_Y]
    bx lr
    .short 0  @ alignment padding

@ ============================================================
@ Function: WrapObjectPosition
@ Description: Wraps object position within bounds (toroidal world)
@ Input: r0 = object, r1 = new_x, r2 = new_y, r3 = wrap_flag (u8)
@ ============================================================
thumb_func_start WrapObjectPosition
WrapObjectPosition:
    push {r4-r6, lr}
    adds r4, r0, #0      @ r4 = object
    adds r5, r2, #0      @ r5 = new_y
    lsls r3, r3, #24     @ zero-extend wrap_flag
    lsrs r6, r3, #24
    
    @ Calculate bounds (Q12 fixed-point)
    ldr r0, [r4, #OBJ_SCREEN_X]
    lsls r2, r0, #12     @ r2 = screen_x << 12 (word stride)
    ldr r0, [r4, #OBJ_SCREEN_Y]
    lsls r3, r0, #12     @ r3 = screen_y << 12 (word stride)
    
    @ Save previous position
    ldr r0, [r4, #OBJ_POS_X]
    str r0, [r4, #OBJ_PREV_X]
    ldr r0, [r4, #OBJ_POS_Y]
    str r0, [r4, #OBJ_PREV_Y]
    
    @ Set new X position
    str r1, [r4, #OBJ_POS_X]
    
    @ Wrap X if negative
    cmp r1, #0
    bge check_x_max
wrap_x_neg:
    adds r1, r1, r2
    cmp r1, #0
    blt wrap_x_neg
    str r1, [r4, #OBJ_POS_X]
    
check_x_max:
    @ Wrap X if exceeds bounds
    ldr r1, [r4, #OBJ_POS_X]
    cmp r2, r1
    bge set_y
wrap_x_max:
    subs r0, r1, r2
    adds r1, r0, #0
    cmp r2, r0
    blt wrap_x_max
    str r0, [r4, #OBJ_POS_X]
    
set_y:
    @ Set new Y position
    str r5, [r4, #OBJ_POS_Y]
    
    @ Wrap Y if negative
    cmp r5, #0
    bge check_y_max
    adds r2, r5, #0
wrap_y_neg:
    adds r2, r2, r3
    cmp r2, #0
    blt wrap_y_neg
    str r2, [r4, #OBJ_POS_Y]
    
check_y_max:
    @ Wrap Y if exceeds bounds
    ldr r1, [r4, #OBJ_POS_Y]
    cmp r3, r1
    bge check_wrap_flag
wrap_y_max:
    subs r0, r1, r3
    adds r1, r0, #0
    cmp r3, r0
    blt wrap_y_max
    str r0, [r4, #OBJ_POS_Y]
    
check_wrap_flag:
    @ If wrap_flag is set, copy positions to screen
    cmp r6, #0
    beq done
    ldr r0, [r4, #OBJ_POS_X]
    str r0, [r4, #OBJ_PREV_X]
    ldr r0, [r4, #OBJ_POS_Y]
    str r0, [r4, #OBJ_PREV_Y]
    ldr r0, [r4, #0x28]
    str r0, [r4, #0x18]
    ldr r0, [r4, #0x2C]
    str r0, [r4, #0x1C]
    ldr r0, [r4, #0x30]
    str r0, [r4, #0x20]
    ldr r0, [r4, #0x34]
    str r0, [r4, #0x24]
    
done:
    pop {r4-r6}
    pop {r0}
    bx r0
    .short 0  @ alignment padding

@ ============================================================
@ Function: Object_UpdateScreenPosition
@ Description: Converts world position to screen position
@ Input: r0 = object pointer
@ Returns: r0 = result code
@ ============================================================
thumb_func_start Object_UpdateScreenPosition
Object_UpdateScreenPosition:
    push {r4-r7, lr}
    mov r7, r8
    mov r6, r9
    mov r5, r10
    push {r5-r7}
    
    adds r5, r0, #0      @ r5 = object
    mov r8, r4           @ save r4
    
    @ Get camera/viewport values
    ldr r0, [r5, #OBJ_POS_X]
    mov r8, r0           @ r8 = obj->pos_x
    ldr r0, [r5, #OBJ_POS_Y]
    mov r9, r0           @ r9 = obj->pos_y
    
    @ Get display dimensions
    ldr r0, [r5, #OBJ_DISPLAY_W]
    mov r10, r0          @ r10 = display_width
    
    @ Calculate screen X: (pos_x >> 12) - (display_width / 2)
    mov r1, r8
    asrs r1, r1, #12     @ world_x = pos_x >> 12
    mov r0, r10
    asrs r0, r0, #1      @ half_width = display_width / 2
    subs r6, r1, r0      @ screen_x = world_x - half_width
    mov r0, r6
    str r0, [r5, #OBJ_SCREEN_X]
    
    @ Get display height
    ldr r0, [r5, #OBJ_DISPLAY_H]
    mov r10, r0          @ r10 = display_height
    
    @ Calculate screen Y: (pos_y >> 12) - (display_height / 2)
    mov r1, r9
    asrs r1, r1, #12     @ world_y = pos_y >> 12
    mov r0, r10
    asrs r0, r0, #1      @ half_height = display_height / 2
    subs r6, r1, r0      @ screen_y = world_y - half_height
    mov r0, r6
    str r0, [r5, #OBJ_SCREEN_Y]
    
    @ Store display dimensions
    ldr r0, [r5, #OBJ_DISPLAY_W]
    str r0, [r5, #OBJ_SCREEN_W]
    ldr r0, [r5, #OBJ_DISPLAY_H]
    str r0, [r5, #OBJ_SCREEN_H]
    
    @ Return 0
    movs r0, #0
    
    pop {r5-r7}
    mov r10, r5
    mov r9, r6
    mov r8, r7
    pop {r4-r7}
    bx lr

@ ============================================================
@ Function: Physics_ApplyGravity
@ Description: Applies gravitational attraction between two objects
@ Input: r0 = object1, r1 = object2, r2 = strength, r3 = flag
@ ============================================================
thumb_func_start Physics_ApplyGravity
Physics_ApplyGravity:
    push {r4-r7, lr}
    mov r7, r8
    mov r6, r9
    mov r5, r10
    push {r5-r7}
    
    @ Save parameters
    mov r8, r0           @ r8 = obj1
    mov r9, r1           @ r9 = obj2
    mov r10, r2          @ r10 = strength
    
    @ Get positions
    ldr r0, [r8, #OBJ_POS_X]
    ldr r1, [r9, #OBJ_POS_X]
    subs r4, r0, r1      @ r4 = dx
    
    ldr r0, [r8, #OBJ_POS_Y]
    ldr r1, [r9, #OBJ_POS_Y]
    subs r5, r0, r1      @ r5 = dy
    
    @ Calculate distance (simplified - using squared distance)
    muls r0, r4, r4      @ r0 = dx * dx
    muls r1, r5, r5      @ r1 = dy * dy
    adds r6, r0, r1      @ r6 = dx*dx + dy*dy
    
    @ Avoid division by zero
    cmp r6, #0
    beq gravity_done
    
    @ Apply force (simplified inverse square)
    mov r0, r10          @ r0 = strength
    @ In real code, this would do: force = strength / distance
    @ For now, simplified version
    
gravity_done:
    movs r0, #0
    
    pop {r5-r7}
    mov r10, r5
    mov r9, r6
    mov r8, r7
    pop {r4-r7}
    bx lr

@ ============================================================
@ Function: Math_Sqrt
@ Description: Integer square root (Newton's method)
@ Input: r0 = value
@ Returns: r0 = sqrt(value)
@ ============================================================
thumb_func_start Math_Sqrt
Math_Sqrt:
    push {r4-r6, lr}
    
    @ Handle zero
    cmp r0, #0
    beq sqrt_done
    
    @ Newton's method initialization
    movs r1, #1          @ r1 = guess = 1
    movs r2, #0          @ r2 = result = 0
    
sqrt_loop:
    @ Check if guess*guess > value
    movs r3, r1
    muls r3, r1, r3      @ r3 = guess * guess
    cmp r3, r0
    bgt sqrt_done
    
    @ Update result and guess
    movs r2, r1          @ result = guess
    adds r1, r1, #1      @ guess++
    b sqrt_loop
    
sqrt_done:
    movs r0, r2          @ return result
    pop {r4-r6, pc}

@ ============================================================
@ Function: Math_Divide
@ Description: Integer division (r0 / r1)
@ Input: r0 = dividend, r1 = divisor
@ Returns: r0 = quotient
@ ============================================================
thumb_func_start Math_Divide
Math_Divide:
    push {r4-r6, lr}
    
    @ Handle division by zero
    cmp r1, #0
    beq div_by_zero
    
    @ Simple division using subtraction
    movs r2, #0          @ r2 = quotient = 0
    movs r3, r0          @ r3 = remainder = dividend
    
div_loop:
    cmp r3, r1
    blt div_done
    subs r3, r3, r1      @ remainder -= divisor
    adds r2, r2, #1      @ quotient++
    b div_loop
    
div_done:
    movs r0, r2          @ return quotient
    pop {r4-r6, pc}
    
div_by_zero:
    movs r0, #0          @ return 0 on divide by zero
    pop {r4-r6, pc}

@ ============================================================
@ Function: Memory_Set
@ Description: Sets memory to a value (byte fill)
@ Input: r0 = dest, r1 = value, r2 = count
@ ============================================================
thumb_func_start Memory_Set
Memory_Set:
    push {r4-r6, lr}
    
    movs r3, #0          @ r3 = counter = 0
    
mem_set_loop:
    cmp r3, r2
    bge mem_set_done
    strb r1, [r0, r3]    @ dest[counter] = value
    adds r3, r3, #1
    b mem_set_loop
    
mem_set_done:
    pop {r4-r6, pc}

@ ============================================================
@ Function: Memory_Copy
@ Description: Copies memory block
@ Input: r0 = dest, r1 = src, r2 = count
@ ============================================================
thumb_func_start Memory_Copy
Memory_Copy:
    push {r4-r6, lr}
    
    movs r3, #0          @ r3 = counter = 0
    
mem_copy_loop:
    cmp r3, r2
    bge mem_copy_done
    ldrb r4, [r1, r3]    @ r4 = src[counter]
    strb r4, [r0, r3]    @ dest[counter] = r4
    adds r3, r3, #1
    b mem_copy_loop
    
mem_copy_done:
    pop {r4-r6, pc}

@ ============================================================
@ Function: Input_Read
@ Description: Reads controller input
@ Returns: r0 = button mask
@ ============================================================
.equ REG_KEYINPUT, 0x04000130

thumb_func_start Input_Read
Input_Read:
    ldr r0, =REG_KEYINPUT
    ldrh r0, [r0]        @ read hardware register
    bx lr
    .align 2
    .word REG_KEYINPUT

@ ============================================================
@ Function: Display_WaitVBlank
@ Description: Waits for vertical blank period
@ ============================================================
.equ REG_VCOUNT, 0x04000006

thumb_func_start Display_WaitVBlank
Display_WaitVBlank:
    push {r4, lr}
    ldr r4, =REG_VCOUNT
    
wait_vblank:
    ldrh r0, [r4]        @ read current scanline
    cmp r0, #160         @ compare to screen height
    blt wait_vblank      @ keep waiting if < 160
    
    pop {r4, pc}
    .align 2
    .word REG_VCOUNT

@ ============================================================
@ Function: Display_SetMode
@ Description: Sets GBA display mode
@ Input: r0 = mode
@ ============================================================
.equ REG_DISPCNT, 0x04000000

thumb_func_start Display_SetMode
Display_SetMode:
    ldr r1, =REG_DISPCNT
    str r0, [r1]         @ set display control
    bx lr
    .align 2
    .word REG_DISPCNT

@ ============================================================
@ Function: Timer_Get
@ Description: Returns current timer value
@ Returns: r0 = timer ticks
@ ============================================================
.equ REG_TM0CNT, 0x04000100

thumb_func_start Timer_Get
Timer_Get:
    ldr r0, =REG_TM0CNT
    ldr r0, [r0]         @ read timer value
    bx lr
    .align 2
    .word REG_TM0CNT

@ ============================================================
@ Function: Random_Get
@ Description: Returns pseudo-random number
@ Returns: r0 = random value
@ ============================================================
.section .bss
random_seed: .space 4

.text
thumb_func_start Random_Get
Random_Get:
    push {r4, lr}
    
    @ LCG random number generator
    @ seed = seed * 1103515245 + 12345
    ldr r0, =random_seed
    ldr r1, [r0]         @ r1 = current seed
    
    ldr r2, =1103515245  @ multiplier
    movs r3, #0
    muls r1, r2          @ seed * multiplier
    ldr r2, =12345
    adds r1, r2          @ seed + increment
    
    str r1, [r0]         @ store new seed
    lsrs r0, r1, #16     @ return upper 16 bits
    
    pop {r4, pc}
    .align 2
    .word random_seed
    .word 1103515245
    .word 12345

@ ============================================================
@ Function: Compare_Function
@ Description: Compares two values for sorting
@ Input: r0 = a, r1 = b
@ Returns: r0 = (a < b) ? -1 : (a > b) ? 1 : 0
@ ============================================================
thumb_func_start Compare_Function
Compare_Function:
    cmp r0, r1
    bgt compare_greater
    blt compare_less
    movs r0, #0          @ equal
    bx lr
    
compare_greater:
    movs r0, #1          @ a > b
    bx lr
    
compare_less:
    movs r0, #-1         @ a < b (0xFF in unsigned)
    bx lr

@ ============================================================
@ Function: FreeIfFlagSet
@ Description: Frees memory if flag bit is set
@ Input: r0 = pointer, r1 = flags
@ ============================================================
thumb_func_start FreeIfFlagSet
FreeIfFlagSet:
    push {lr}
    adds r2, r0, #0      @ r2 = pointer
    movs r0, #1
    ands r0, r1           @ test bit 0
    cmp r0, #0
    beq free_done
    adds r0, r2, #0      @ r0 = pointer
    bl Memory_Free        @ call free
free_done:
    pop {r0}
    bx r0
    .short 0

@ ============================================================
@ Function: Animation_SetFrame
@ Description: Sets animation frame for an object
@ Input: r0 = object, r1 = frame_index, r2 = flags
@ ============================================================
thumb_func_start Animation_SetFrame
Animation_SetFrame:
    push {r4-r6, lr}
    adds r6, r0, #0      @ r6 = object
    ldr r3, =0x00007FFF
    ands r3, r2           @ mask flags
    
    @ Get animation data pointer
    ldr r0, [r6, #8]     @ r0 = anim_data_ptr
    lsls r4, r1, #2      @ r4 = frame_index * 4
    adds r0, r4, r0
    ldr r0, [r0, #0]     @ r0 = frame_data
    
    @ Get frame dimensions
    ldrb r1, [r0, #6]    @ r1 = width_low
    ldrb r0, [r0, #7]    @ r0 = width_high
    lsls r0, r0, #8
    orrs r0, r1
    lsls r0, r0, #16
    asrs r1, r0, #16     @ r1 = frame_width (signed)
    
    @ Clamp to max value
    cmp r3, r1
    blt set_frame_data
    adds r0, r3, #0
    bl Math_Sqrt          @ some math operation
    adds r3, r0, #0
    
set_frame_data:
    @ Get frame data pointer again
    ldr r0, [r6, #8]
    adds r0, r4, r0
    ldr r0, [r0, #0]
    adds r2, r0, #8      @ r2 = frame pixel data
    
    @ Get frame height
    ldrb r1, [r0, #4]
    ldrb r0, [r0, #5]
    lsls r0, r0, #8
    orrs r0, r1
    lsls r0, r0, #16
    asrs r5, r0, #16     @ r5 = frame_height
    
    movs r4, #0           @ r4 = counter = 0
    
frame_loop:
    cmp r4, r5
    bge frame_done
    
    @ Process pixel data
    ldrb r1, [r2, #2]
    ldrb r0, [r2, #3]
    lsls r0, r0, #8
    orrs r0, r1
    lsls r0, r0, #16
    asrs r1, r0, #16     @ r1 = pixel_value
    
    @ Store to VRAM or buffer
    strh r1, [r2, #0]    @ store pixel
    
    adds r2, r2, #4      @ advance to next pixel
    adds r4, r4, #1      @ increment counter
    b frame_loop
    
frame_done:
    movs r0, #0
    pop {r4-r6, pc}
    .align 2
    .word 0x00007FFF

@ ============================================================
@ Function: Level_Load
@ Description: Loads level data from ROM
@ Input: r0 = level_index
@ ============================================================
thumb_func_start Level_Load
Level_Load:
    push {r4-r7, lr}
    
    @ Save level index
    adds r4, r0, #0
    
    @ Calculate ROM address for level data
    @ Level data is at: ROM_BASE + 0x20000 + (level_index * LEVEL_SIZE)
    ldr r0, =0x08020000  @ ROM data start
    ldr r1, =0x1000       @ approximate level size
    muls r1, r4, r1       @ offset = index * size
    adds r0, r0, r1       @ address = base + offset
    
    @ Copy level data to EWRAM
    ldr r1, =0x02000000   @ EWRAM base
    ldr r2, =0x1000       @ size to copy
    
level_copy_loop:
    subs r2, r2, #4
    blt level_copy_done
    ldr r3, [r0, r2]
    str r3, [r1, r2]
    b level_copy_loop
    
level_copy_done:
    movs r0, #1           @ return success
    pop {r4-r7, pc}
    .align 2
    .word 0x08020000
    .word 0x1000
    .word 0x02000000

@ ============================================================
@ Function: Sprite_Draw
@ Description: Draws a sprite to OAM
@ Input: r0 = sprite_data, r1 = x, r2 = y, r3 = attr
@ ============================================================
thumb_func_start Sprite_Draw
Sprite_Draw:
    push {r4-r6, lr}
    
    @ Save parameters
    adds r4, r0, #0      @ r4 = sprite_data
    adds r5, r1, #0      @ r5 = x
    adds r6, r2, #0      @ r6 = y
    
    @ OAM address
    ldr r0, =0x07000000
    
    @ Calculate OAM entry offset (simplified)
    @ Each OAM entry is 8 bytes
    ldrh r1, [r4, #0]    @ attr0
    ldrh r2, [r4, #2]    @ attr1
    
    @ Set Y coordinate in attr0
    lsls r3, r6, #0       @ y value
    orrs r1, r3
    strh r1, [r0, #0]    @ store attr0
    
    @ Set X coordinate in attr1
    orrs r2, r5
    strh r2, [r0, #2]    @ store attr1
    
    @ Store attr2 (tile + palette)
    ldrh r1, [r4, #4]
    strh r1, [r0, #4]
    
    movs r0, #0           @ return success
    pop {r4-r6, pc}
    .align 2
    .word 0x07000000

@ ============================================================
@ Function: DMA_Transfer
@ Description: Performs DMA transfer
@ Input: r0 = dest, r1 = src, r2 = count, r3 = control
@ ============================================================
.equ REG_DMA3SAD, 0x040000D4
.equ REG_DMA3DAD, 0x040000D8
.equ REG_DMA3CNT, 0x040000DC

thumb_func_start DMA_Transfer
DMA_Transfer:
    push {r4, lr}
    
    @ Set DMA source
    ldr r4, =REG_DMA3SAD
    str r1, [r4, #0]
    
    @ Set DMA destination
    str r0, [r4, #4]
    
    @ Set DMA count and control
    ldr r1, =0x04000000
    orrs r2, r3           @ combine count with control flags
    str r2, [r4, #8]
    
    pop {r4, pc}
    .align 2
    .word REG_DMA3SAD
    .word 0x04000000

@ ============================================================
@ Function: Sound_Play
@ Description: Plays a sound effect
@ Input: r0 = sound_id
@ ============================================================
thumb_func_start Sound_Play
Sound_Play:
    push {r4, lr}
    
    @ Sound channel 1 registers
    ldr r1, =0x04000060   @ SOUND1CNT_L
    ldr r2, =0x04000062   @ SOUND1CNT_H
    ldr r3, =0x04000064   @ SOUND1CNT_X
    
    @ Simple sound trigger
    movs r4, #0x80
    orrs r0, r4           @ enable bit
    strh r0, [r1]
    
    movs r0, #0
    pop {r4, pc}
    .align 2
    .word 0x04000060
    .word 0x04000062
    .word 0x04000064

@ ============================================================
@ Function: Palette_SetColor
@ Description: Sets a palette color
@ Input: r0 = index, r1 = color (RGB15)
@ ============================================================
.equ REG_PALETTE, 0x05000000

thumb_func_start Palette_SetColor
Palette_SetColor:
    ldr r2, =REG_PALETTE
    lsls r0, r0, #1       @ index * 2 (16-bit colors)
    strh r1, [r2, r0]     @ store color
    bx lr
    .align 2
    .word REG_PALETTE

@ ============================================================
@ Function: BG_SetScroll
@ Description: Sets background scroll position
@ Input: r0 = bg_index, r1 = x, r2 = y
@ ============================================================
.thumb_func
BG_SetScroll:
    @ BG scroll registers start at 0x04000010
    @ BG0 = 0x04000010, BG1 = 0x04000014, etc.
    ldr r3, =0x04000010
    lsls r0, r0, #2       @ bg_index * 4
    adds r3, r3, r0
    strh r1, [r3, #0]     @ store X scroll
    strh r2, [r3, #2]     @ store Y scroll
    bx lr
    .align 2
    .word 0x04000010
