pokeruby/asm/libgcnmultiboot.s
2016-10-28 15:41:15 -07:00

551 lines
12 KiB
ArmAsm

@ This library can be used to download and execute a multi-boot image from
@ a GameCube using the JOY Bus protocol over the link cable.
.include "asm/macros.inc"
.include "constants/constants.inc"
.equiv GCMB_STRUCT_BASE_DEST_PTR, 0x20
.equiv GCMB_STRUCT_CUR_DEST_PTR, 0x24
.equiv GCMB_STRUCT_SERIAL_INTR_HANDLER, 0x28
.equiv ROM_HEADER_NINTENDO_LOGO_OFFSET, 0x4
.syntax unified
.text
thumb_func_start GameCubeMultiBoot_Hash
GameCubeMultiBoot_Hash: @ 81DCB38
push {r4,lr}
ldr r4, pool_HashVal
eors r3, r1
movs r2, 0x20
GameCubeMultiBoot_Hash_Loop:
lsrs r3, 1
bcc GameCubeMultiBoot_Hash_SkipEor
eors r3, r4
GameCubeMultiBoot_Hash_SkipEor:
subs r2, 0x1
bne GameCubeMultiBoot_Hash_Loop
pop {r4,pc}
thumb_func_end GameCubeMultiBoot_Hash
thumb_func_start GameCubeMultiBoot_Main
@ void GameCubeMultiBoot_Main(struct GameCubeMultiBoot *mb)@
GameCubeMultiBoot_Main: @ 81DCB4C
ldr r1, [r0, GCMB_STRUCT_SERIAL_INTR_HANDLER]
cmp r1, 0
beq _081DCB72
ldrb r1, [r0, 0x1]
adds r1, 0x1
strb r1, [r0, 0x1]
ldrb r1, [r0, 0x2]
cmp r1, 0x2
beq _081DCBBC
ldr r3, pool_InterruptRegs
ldrh r2, [r3, OFFSET_REG_IME - 0x200]
movs r1, 0
strh r1, [r3, OFFSET_REG_IME - 0x200]
ldrb r1, [r0]
cmp r1, 0xA
bgt _081DCB70
adds r1, 0x1
strb r1, [r0]
_081DCB70:
strh r2, [r3, OFFSET_REG_IME - 0x200]
_081DCB72:
bcs GameCubeMultiBoot_Init
ldrb r1, [r0, 0x2]
cmp r1, 0
bne _081DCBBE
ldr r1, [r0, GCMB_STRUCT_CUR_DEST_PTR]
ldr r2, [r0, GCMB_STRUCT_BASE_DEST_PTR]
subs r1, r2
beq _081DCC3E
cmp r1, 0xA0
bcc _081DCC3E
push {r4-r6}
movs r1, 0x98
adds r2, ROM_HEADER_NINTENDO_LOGO_OFFSET
ldr r4, pool_NintendoLogo
_081DCB8E:
ldm r2!, {r5}
ldm r4!, {r6}
cmp r5, r6
bne _081DCBA4
subs r1, 0x4
bne _081DCB8E
ldm r2!, {r5}
ldm r4!, {r6}
eors r5, r6
lsrs r5, 8
str r2, [r0, GCMB_STRUCT_BASE_DEST_PTR]
_081DCBA4:
pop {r4-r6}
bne GameCubeMultiBoot_Init
movs r1, 0x1
strb r1, [r0, 0x2]
ldr r1, [r0, 0x4]
ldr r2, [r0, 0x8]
eors r1, r2
str r1, [r0, 0x18]
ldr r2, pool_Kawa
muls r1, r2
adds r1, 0x1
str r1, [r0, 0x14]
_081DCBBC:
bx lr
_081DCBBE:
ldr r1, [r0, GCMB_STRUCT_CUR_DEST_PTR]
mov r12, r1
ldr r3, [r0, 0x18]
push {r4-r7}
ldr r4, [r0, GCMB_STRUCT_BASE_DEST_PTR]
ldr r5, pool_Kawa
ldr r6, [r0, 0x14]
ldr r7, pool_HashVal
_081DCBCE:
cmp r4, r12
bcs _081DCBEE
ldr r1, [r4]
eors r1, r6
adds r1, r3
stm r4!, {r1}
eors r3, r1
movs r2, 0x20
_081DCBDE:
lsrs r3, 1
bcc _081DCBE4
eors r3, r7
_081DCBE4:
subs r2, 0x1
bne _081DCBDE
muls r6, r5
adds r6, 0x1
b _081DCBCE
_081DCBEE:
str r4, [r0, GCMB_STRUCT_BASE_DEST_PTR]
str r6, [r0, 0x14]
pop {r4-r7}
str r3, [r0, 0x18]
ldrh r1, [r0, 0x12]
cmp r1, 0
bne _081DCC3E
ldr r1, [r0, GCMB_STRUCT_CUR_DEST_PTR]
ldr r2, [r0, GCMB_STRUCT_BASE_DEST_PTR]
cmp r1, r2
bne _081DCC3E
ldr r1, [r0, 0xC]
cmp r1, 0
beq _081DCC28
ldrh r1, [r0, 0x10]
cmp r1, 0
beq _081DCBBC
mov r12, lr
movs r1, 0xBB
ldr r3, [r0, 0xC]
bl GameCubeMultiBoot_Hash
ldrh r1, [r0, 0x10]
mov lr, r12
subs r1, r3
bne GameCubeMultiBoot_Init
movs r1, 0x2
strb r1, [r0, 0x2]
bx lr
_081DCC28:
mov r12, lr
ldrb r1, [r0, 0x3]
lsls r1, 24
subs r1, 0x1
str r1, [r0, 0xC]
bl GameCubeMultiBoot_Hash
lsls r3, 8
adds r3, 0xFF
str r3, [r0, 0x1C]
bx r12
_081DCC3E:
bx lr
thumb_func_end GameCubeMultiBoot_Main
.align 2, 0
pool_HashVal: .4byte 0xa1c1
pool_Kawa: .ascii "Kawa" @ name of BIOS developer
pool_NintendoLogo: .4byte RomHeaderNintendoLogo
thumb_func_start GameCubeMultiBoot_ExecuteProgram
@ void GameCubeMultiBoot_ExecuteProgram(struct GameCubeMultiBoot *mb)@
GameCubeMultiBoot_ExecuteProgram: @ 81DCC4C
ldrb r1, [r0, 0x2]
cmp r1, 0x2
bne GameCubeMultiBoot_ExecuteProgram_Fail
ldr r3, pool_InterruptRegs
movs r1, 0
strh r1, [r3, OFFSET_REG_IME - 0x200]
ldr r1, pool_MultiBootLoadAddr
adds r1, 0xC0
bx r1
GameCubeMultiBoot_ExecuteProgram_Fail:
bx lr
thumb_func_end GameCubeMultiBoot_ExecuteProgram
thumb_func_start GameCubeMultiBoot_Init
@ void GameCubeMultiBoot_Init(struct GameCubeMultiBoot *mb)@
GameCubeMultiBoot_Init: @ 81DCC60
ldr r3, pool_InterruptRegs
@ Save IME register.
ldrh r2, [r3, OFFSET_REG_IME - 0x200]
@ Disable interrupts.
movs r1, 0
strh r1, [r3, OFFSET_REG_IME - 0x200]
@ Set the handler to the "Stop" routine.
@ Unless the first command that is received is a device reset command, the
@ "Stop" routine will be executed and no further commands will be processed.
adr r3, GcMbIntrHandler_Stop
str r3, [r0, GCMB_STRUCT_SERIAL_INTR_HANDLER]
ldrb r3, [r0, 0x3]
push {r3}
ldrb r3, [r0, 0x1]
push {r0,r3}
adds r3, r0, 0
adds r3, GCMB_STRUCT_BASE_DEST_PTR
@ clear all but the last 3 fields of the struct
GameCubeMultiBoot_Init_ClearStructLoop:
stm r0!, {r1}
cmp r0, r3
blo GameCubeMultiBoot_Init_ClearStructLoop
pop {r0,r3}
lsrs r3, 1
strb r3, [r0, 0x3]
pop {r3}
strb r3, [r0, 0x1]
ldr r3, pool_SerialRegs
@ Turn off JOY Bus mode.
lsls r0, r3, 10
strh r0, [r3, OFFSET_REG_RCNT - 0x120]
@ Turn on JOY Bus mode.
movs r0, 0xC0
lsls r0, 8
strh r0, [r3, OFFSET_REG_RCNT - 0x120]
@ Init JOY Bus registers.
movs r0, 0x47
strh r0, [r3, OFFSET_REG_JOYCNT - 0x120]
strh r1, [r3, OFFSET_REG_JOYSTAT - 0x120]
ldr r3, pool_InterruptRegs
@ Acknowledge serial interrupt.
movs r0, INTR_FLAG_SERIAL
strh r0, [r3, OFFSET_REG_IF - 0x200]
@ Enable serial interrupt.
ldrh r1, [r3, OFFSET_REG_IE - 0x200]
orrs r1, r0
strh r1, [r3, OFFSET_REG_IE - 0x200]
@ Restore IME register.
strh r2, [r3, OFFSET_REG_IME - 0x200]
bx lr
thumb_func_end GameCubeMultiBoot_Init
non_word_aligned_thumb_func_start GameCubeMultiBoot_HandleSerialInterrupt
@ void GameCubeMultiBoot_HandleSerialInterrupt(struct GameCubeMultiBoot *mb)@
GameCubeMultiBoot_HandleSerialInterrupt: @ 81DCCAA
ldr r3, pool_SerialRegs
@ Acknowledge reset/receive/send flags.
ldrh r1, [r3, OFFSET_REG_JOYCNT - 0x120]
strh r1, [r3, OFFSET_REG_JOYCNT - 0x120]
movs r2, 0
strb r2, [r0]
ldr r2, [r0, GCMB_STRUCT_SERIAL_INTR_HANDLER]
cmp r2, 0
beq GameCubeMultiBoot_HandleSerialInterruptDone
lsrs r1, 1 @ was a device reset command received?
bcs GameCubeMultiBoot_BeginHandshake @ branch if so
mov pc, r2
.align 2, 0
@ Zero the status and the interrupt handler pointer.
@ Commands from the GameCube will not be processed after this is executed
@ unless GameCubeMultiBoot_Init() is called again.
GcMbIntrHandler_Stop:
movs r2, 0
strh r2, [r3, OFFSET_REG_JOYSTAT - 0x120]
GameCubeMultiBoot_SetInterruptHandler:
str r2, [r0, GCMB_STRUCT_SERIAL_INTR_HANDLER]
GameCubeMultiBoot_ReadVCount:
ldr r3, pool_RegDispstat
ldrh r1, [r3, OFFSET_REG_VCOUNT - OFFSET_REG_DISPSTAT]
strb r1, [r0, 0x3]
GameCubeMultiBoot_HandleSerialInterruptDone:
bx lr
GameCubeMultiBoot_BeginHandshake:
ldr r1, [r3, OFFSET_REG_JOY_RECV - 0x120]
ldr r1, pool_RubyUSAGameCode
str r1, [r3, OFFSET_REG_JOY_TRANS - 0x120]
movs r1, 0x10
strh r1, [r3, OFFSET_REG_JOYSTAT - 0x120]
ldrb r1, [r0, 0x3]
strb r1, [r0, 0x9]
ldrb r1, [r0, 0x2]
cmp r1, 0
bne GcMbIntrHandler_Stop
ldr r1, pool_MultiBootLoadAddr
str r1, [r0, GCMB_STRUCT_BASE_DEST_PTR]
str r1, [r0, GCMB_STRUCT_CUR_DEST_PTR]
adr r2, GcMbIntrHandler_CheckGameCodeSent
b GameCubeMultiBoot_SetInterruptHandler
.align 2, 0
GcMbIntrHandler_CheckGameCodeSent: @ 81DCCEC
lsls r1, 31
bcc GcMbIntrHandler_Stop @ stop if send failed
bmi GameCubeMultiBoot_CheckHandshakeResponse @ branch if receive is complete
@ If the response hasn't been fully received yet,
@ check again upon the next interrupt.
adr r2, GcMbIntrHandler_CheckHandshakeResponse
b GameCubeMultiBoot_SetInterruptHandler
.align 2, 0
GcMbIntrHandler_CheckHandshakeResponse: @ 81DCCF8
lsrs r1, 1 @ is receive complete?
bcc GcMbIntrHandler_Stop @ stop if not
GameCubeMultiBoot_CheckHandshakeResponse:
ldr r1, [r3, OFFSET_REG_JOY_RECV - 0x120]
ldr r2, pool_RubyUSAGameCode
cmp r1, r2
bne GcMbIntrHandler_Stop @ stop if the GameCube didn't reply with the same game code
ldrb r1, [r0, 0x3]
strb r1, [r0, 0xB]
adr r2, GcMbIntrHandler_81DCD0C
b GameCubeMultiBoot_SetInterruptHandler
.align 2, 0
GcMbIntrHandler_81DCD0C: @ 81DCD0C
lsrs r1, 1 @ is receive complete?
bcc GcMbIntrHandler_Stop @ branch if not
ldr r1, [r3, OFFSET_REG_JOY_RECV - 0x120]
lsrs r2, r1, 24
cmp r2, 0xDD
bne GcMbIntrHandler_Stop
str r1, [r0, 0x4]
ldrb r1, [r0, 0x1]
strb r1, [r0, 0xA]
movs r2, 0
movs r3, 0
ldr r1, [r0, 0x8]
lsrs r1, 8
_081DCD26:
lsrs r1, 1
adcs r2, r3
cmp r1, 0
bne _081DCD26
cmp r2, 0xE
bgt _081DCD38
cmp r2, 0x7
bge _081DCD3A
movs r1, 0xFF
_081DCD38:
strb r1, [r0, 0xA]
_081DCD3A:
ldr r1, [r0, 0x8]
adds r1, 0xEE
ldr r3, pool_SerialRegs
str r1, [r3, OFFSET_REG_JOY_TRANS - 0x120]
movs r1, 0x30
strh r1, [r3, OFFSET_REG_JOYSTAT - 0x120]
adr r2, GcMbIntrHandler_81DCD4C
b GameCubeMultiBoot_SetInterruptHandler
.align 2, 0
GcMbIntrHandler_81DCD4C: @ 81DCD4C
lsls r1, 31
bcc GcMbIntrHandler_Stop @ stop if send failed
bmi _081DCD5C @ branch if receive is complete
adr r2, GcMbIntrHandler_81DCD58
b GameCubeMultiBoot_SetInterruptHandler
.align 2, 0
GcMbIntrHandler_81DCD58: @ 81DCD58
lsrs r1, 1 @ is receive complete?
bcc GcMbIntrHandler_Stop @ branch if not
_081DCD5C:
ldr r1, [r3, OFFSET_REG_JOY_RECV - 0x120]
ldr r2, _081DCDFC
cmp r1, r2
bhs GcMbIntrHandler_Stop
adds r1, 0x1
adds r1, r1
strh r1, [r0, 0x12]
ldrb r1, [r0, 0x2]
cmp r1, 0
_081DCD6E:
bne GcMbIntrHandler_Stop
ldr r1, pool_MultiBootLoadAddr
str r1, [r0, GCMB_STRUCT_BASE_DEST_PTR]
str r1, [r0, GCMB_STRUCT_CUR_DEST_PTR]
adr r2, GcMbIntrHandler_81DCD7C
b GameCubeMultiBoot_SetInterruptHandler
.align 2, 0
GcMbIntrHandler_81DCD7C: @ 81DCD7C
lsrs r1, 1 @ is receive complete?
bcc GcMbIntrHandler_Stop @ branch if not
ldr r2, [r0, GCMB_STRUCT_CUR_DEST_PTR]
movs r1, 0x4
ands r1, r2
adds r1, 0x8
lsls r1, 2
strh r1, [r3, OFFSET_REG_JOYSTAT - 0x120]
ldr r1, [r3, OFFSET_REG_JOY_RECV - 0x120]
stm r2!, {r1}
str r2, [r0, GCMB_STRUCT_CUR_DEST_PTR]
ldrh r1, [r0, 0x12]
subs r1, 0x1
strh r1, [r0, 0x12]
bne GameCubeMultiBoot_ReadVCount
_081DCD9A:
ldrb r1, [r0, 0x1]
lsls r1, 8
adds r1, 0xCC
str r1, [r3, OFFSET_REG_JOY_TRANS - 0x120]
adr r2, _081DCDA8
b GameCubeMultiBoot_SetInterruptHandler
.align 2, 0
_081DCDA8:
lsls r1, 31
_081DCDAA:
bcc GcMbIntrHandler_Stop
ldr r1, [r0, 0x1C]
cmp r1, 0
beq _081DCD9A
str r1, [r3, OFFSET_REG_JOY_TRANS - 0x120]
adr r2, GcMbIntrHandler_81DCDB8
b GameCubeMultiBoot_SetInterruptHandler
.align 2, 0
GcMbIntrHandler_81DCDB8: @ 81DCDB8
lsls r1, 31
bcc _081DCDAA @ branch if send failed
bmi _081DCDC8 @ branch if receive is complete
adr r2, GcMbIntrHandler_81DCDC4
b GameCubeMultiBoot_SetInterruptHandler
.align 2, 0
GcMbIntrHandler_81DCDC4: @ 81DCDC4
lsrs r1, 1 @ is receive complete?
bcc _081DCDAA @ branch if not
_081DCDC8:
ldr r1, [r3, OFFSET_REG_JOY_RECV - 0x120]
lsrs r2, r1, 24
cmp r2, 0xBB
bne _081DCD6E
strh r1, [r0, 0x10]
adr r2, GcMbIntrHandler_81DCDD8
b GameCubeMultiBoot_SetInterruptHandler
.align 2, 0
GcMbIntrHandler_81DCDD8: @ 81DCDD8
b GcMbIntrHandler_Stop
thumb_func_end GameCubeMultiBoot_HandleSerialInterrupt
non_word_aligned_thumb_func_start GameCubeMultiBoot_Quit
@ void GameCubeMultiBoot_Quit()@
GameCubeMultiBoot_Quit: @ 81DCDDA
ldr r3, pool_InterruptRegs
@ Save IME register.
ldrh r2, [r3, OFFSET_REG_IME - 0x200]
@ Disable interrupts.
movs r1, 0
strh r1, [r3, OFFSET_REG_IME - 0x200]
ldr r3, pool_SerialRegs
@ Acknowledge all JOYCNT flags.
movs r0, 0x7
strh r0, [r3, OFFSET_REG_JOYCNT - 0x120]
@ Turn off JOY Bus mode.
lsls r0, r3, 10
strh r0, [r3, OFFSET_REG_RCNT - 0x120] @ store 0x8000
ldr r3, pool_InterruptRegs
@ Acknowledge serial interrupt.
movs r0, INTR_FLAG_SERIAL
strh r0, [r3, OFFSET_REG_IF - 0x200]
@ Disable serial interrupt.
ldrh r1, [r3, OFFSET_REG_IE - 0x200]
bics r1, r0
strh r1, [r3, OFFSET_REG_IE - 0x200]
@ Restore IME register.
strh r2, [r3, OFFSET_REG_IME - 0x200]
bx lr
thumb_func_end GameCubeMultiBoot_Quit
.align 2, 0
_081DCDFC: .4byte 0x00004000
pool_InterruptRegs: .4byte REG_BASE + 0x200
pool_SerialRegs: .4byte REG_BASE + 0x120
pool_RegDispstat: .4byte REG_DISPSTAT
pool_RubyUSAGameCode: .ascii "AXVE"
pool_MultiBootLoadAddr: .4byte EWRAM_START
.align 2, 0 @ Don't pad with nop.