@ 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.