; 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. .set GCMB_STRUCT_BASE_DEST_PTR, 0x20 .set GCMB_STRUCT_CUR_DEST_PTR, 0x24 .set GCMB_STRUCT_SERIAL_INTR_HANDLER, 0x28 thumb_func_start GameCubeMultiBoot_Hash GameCubeMultiBoot_Hash: ; 81DCB38 push {r4,lr} ldr r4, pool_HashVal eors r3, r1 movs r2, 0x20 $loop: lsrs r3, 1 bcc $skipEor eors r3, r4 $skipEor: subs r2, 0x1 bne $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, RomHeaderNintendoLogo - RomBase 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 $unableToExecute ldr r3, pool_InterruptRegs movs r1, 0 strh r1, [r3, OFFSET_REG_IME - 0x200] ldr r1, pool_MultiBootLoadAddr adds r1, 0xC0 bx r1 $unableToExecute: 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 $clearStructLoop: stm r0!, {r1} cmp r0, r3 blo $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