@ 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: @ 82DED70 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: @ 82DED84 ldr r1, [r0, GCMB_STRUCT_SERIAL_INTR_HANDLER] cmp r1, 0 beq _082DEDAA ldrb r1, [r0, 0x1] adds r1, 0x1 strb r1, [r0, 0x1] ldrb r1, [r0, 0x2] cmp r1, 0x2 beq _082DEDF4 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 _082DEDA8 adds r1, 0x1 strb r1, [r0] _082DEDA8: strh r2, [r3, OFFSET_REG_IME - 0x200] _082DEDAA: bcs GameCubeMultiBoot_Init ldrb r1, [r0, 0x2] cmp r1, 0 bne _082DEDF6 ldr r1, [r0, GCMB_STRUCT_CUR_DEST_PTR] ldr r2, [r0, GCMB_STRUCT_BASE_DEST_PTR] subs r1, r2 beq _082DEE76 cmp r1, 0xA0 bcc _082DEE76 push {r4-r6} movs r1, 0x98 adds r2, ROM_HEADER_NINTENDO_LOGO_OFFSET ldr r4, pool_NintendoLogo _082DEDC6: ldm r2!, {r5} ldm r4!, {r6} cmp r5, r6 bne _082DEDDC subs r1, 0x4 bne _082DEDC6 ldm r2!, {r5} ldm r4!, {r6} eors r5, r6 lsrs r5, 8 str r2, [r0, GCMB_STRUCT_BASE_DEST_PTR] _082DEDDC: 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] _082DEDF4: bx lr _082DEDF6: 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 _082DEE06: cmp r4, r12 bcs _082DEE26 ldr r1, [r4] eors r1, r6 adds r1, r3 stm r4!, {r1} eors r3, r1 movs r2, 0x20 _082DEE16: lsrs r3, 1 bcc _082DEE1C eors r3, r7 _082DEE1C: subs r2, 0x1 bne _082DEE16 muls r6, r5 adds r6, 0x1 b _082DEE06 _082DEE26: 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 _082DEE76 ldr r1, [r0, GCMB_STRUCT_CUR_DEST_PTR] ldr r2, [r0, GCMB_STRUCT_BASE_DEST_PTR] cmp r1, r2 bne _082DEE76 ldr r1, [r0, 0xC] cmp r1, 0 beq _082DEE60 ldrh r1, [r0, 0x10] cmp r1, 0 beq _082DEDF4 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 _082DEE60: 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 _082DEE76: 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: @ 82DEE84 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: @ 82DEE98 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: @ 82DEEE2 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: @ 82DEF24 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: @ 82DEF30 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_82DEF44 b GameCubeMultiBoot_SetInterruptHandler .align 2, 0 GcMbIntrHandler_82DEF44: @ 82DEF44 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 _082DEF5E: lsrs r1, 1 adcs r2, r3 cmp r1, 0 bne _082DEF5E cmp r2, 0xE bgt _082DEF70 cmp r2, 0x7 bge _082DEF72 movs r1, 0xFF _082DEF70: strb r1, [r0, 0xA] _082DEF72: 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_82DEF84 b GameCubeMultiBoot_SetInterruptHandler .align 2, 0 GcMbIntrHandler_82DEF84: @ 82DEF84 lsls r1, 31 bcc GcMbIntrHandler_Stop @ stop if send failed bmi _082DEF94 @ branch if receive is complete adr r2, GcMbIntrHandler_82DEF90 b GameCubeMultiBoot_SetInterruptHandler .align 2, 0 GcMbIntrHandler_82DEF90: @ 82DEF90 lsrs r1, 1 @ is receive complete? bcc GcMbIntrHandler_Stop @ branch if not _082DEF94: ldr r1, [r3, OFFSET_REG_JOY_RECV - 0x120] ldr r2, _082DF034 cmp r1, r2 bhs GcMbIntrHandler_Stop adds r1, 0x1 adds r1, r1 strh r1, [r0, 0x12] ldrb r1, [r0, 0x2] cmp r1, 0 _082DEFA6: 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_82DEFB4 b GameCubeMultiBoot_SetInterruptHandler .align 2, 0 GcMbIntrHandler_82DEFB4: @ 82DEFB4 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 _082DEFD2: ldrb r1, [r0, 0x1] lsls r1, 8 adds r1, 0xCC str r1, [r3, OFFSET_REG_JOY_TRANS - 0x120] adr r2, _082DEFE0 b GameCubeMultiBoot_SetInterruptHandler .align 2, 0 _082DEFE0: lsls r1, 31 _082DEFE2: bcc GcMbIntrHandler_Stop ldr r1, [r0, 0x1C] cmp r1, 0 beq _082DEFD2 str r1, [r3, OFFSET_REG_JOY_TRANS - 0x120] adr r2, GcMbIntrHandler_82DEFF0 b GameCubeMultiBoot_SetInterruptHandler .align 2, 0 GcMbIntrHandler_82DEFF0: @ 82DEFF0 lsls r1, 31 bcc _082DEFE2 @ branch if send failed bmi _082DF000 @ branch if receive is complete adr r2, GcMbIntrHandler_82DEFFC b GameCubeMultiBoot_SetInterruptHandler .align 2, 0 GcMbIntrHandler_82DEFFC: @ 82DEFFC lsrs r1, 1 @ is receive complete? bcc _082DEFE2 @ branch if not _082DF000: ldr r1, [r3, OFFSET_REG_JOY_RECV - 0x120] lsrs r2, r1, 24 cmp r2, 0xBB bne _082DEFA6 strh r1, [r0, 0x10] adr r2, GcMbIntrHandler_82DF010 b GameCubeMultiBoot_SetInterruptHandler .align 2, 0 GcMbIntrHandler_82DF010: @ 82DF010 b GcMbIntrHandler_Stop thumb_func_end GameCubeMultiBoot_HandleSerialInterrupt non_word_aligned_thumb_func_start GameCubeMultiBoot_Quit @ void GameCubeMultiBoot_Quit(); GameCubeMultiBoot_Quit: @ 82DF012 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 _082DF034: .4byte 0x4000 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.