diff options
author | camthesaxman <cameronghall@cox.net> | 2018-01-10 23:20:32 -0600 |
---|---|---|
committer | camthesaxman <cameronghall@cox.net> | 2018-01-10 23:20:32 -0600 |
commit | b5667bd097347c1d3958fa8f9eb6d39788c0ff50 (patch) | |
tree | 29aca119bf365256255da97e8e768cd9216688a8 /src/libs/libgcnmultiboot.s | |
parent | d4afe49b06a7ef2422839fcd0340c06c6a8e541b (diff) |
refactor build system
Diffstat (limited to 'src/libs/libgcnmultiboot.s')
-rw-r--r-- | src/libs/libgcnmultiboot.s | 641 |
1 files changed, 641 insertions, 0 deletions
diff --git a/src/libs/libgcnmultiboot.s b/src/libs/libgcnmultiboot.s new file mode 100644 index 000000000..8c8b94998 --- /dev/null +++ b/src/libs/libgcnmultiboot.s @@ -0,0 +1,641 @@ +@ 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 "include/macros.inc" + .include "constants/constants.inc" + + .equiv GCMB_STRUCT_COUNTER1, 0x00 + .equiv GCMB_STRUCT_COUNTER2, 0x01 + .equiv GCMB_STRUCT_MBPROGRESS, 0x02 + .equiv GCMB_STRUCT_SAVEDVCOUNT, 0x03 + .equiv GCMB_STRUCT_KEYA, 0x04 + .equiv GCMB_STRUCT_KEYB, 0x08 + .equiv GCMB_STRUCT_KEYC, 0x0C + .equiv GCMB_STRUCT_BOOT_KEY, 0x10 + .equiv GCMB_STRUCT_IMAGE_SIZE, 0x12 + .equiv GCMB_STRUCT_SESSION_KEY, 0x14 + .equiv GCMB_STRUCT_HASH_VAL, 0x18 + .equiv GCMB_STRUCT_KEYC_DERIVATION, 0x1C + .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, 0x04 + .equiv ROM_HEADER_NINTENDO_LOGO_LENGTH, 0x98 + .equiv ROM_HEADER_NINTENDO_LOGO_END, 0xA0 + + .equiv MBPROGRESS_NONE, 0x00 + .equiv MBPROGRESS_LOGO_CORRECT, 0x01 + .equiv MBPROGRESS_READY_TO_BOOT, 0x02 + + .equiv GCMB_MAGIC_BOOTKEY_HASHVAL, 0xBB + .equiv GCMB_MAGIC_BOOTKEY, 0xBB + .equiv GCMB_MAGIC_COUNTER2, 0xCC + .equiv GCMB_MAGIC_KEYA, 0xDD + .equiv GCMB_MAGIC_KEYB, 0xEE + .equiv GCMB_MAGIC_KEYCDERIVATION, 0xFF + + .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 + @ If there is no interrupt handler, skip counter manipulation + ldr r1, [r0, GCMB_STRUCT_SERIAL_INTR_HANDLER] + cmp r1, 0 + beq GameCubeMultiBoot_Main_SkipCounters + @ Increment the second counter + ldrb r1, [r0, GCMB_STRUCT_COUNTER2] + adds r1, 0x1 + strb r1, [r0, GCMB_STRUCT_COUNTER2] + @ If there is nothing more to do, bail out + ldrb r1, [r0, GCMB_STRUCT_MBPROGRESS] + cmp r1, MBPROGRESS_READY_TO_BOOT + beq GameCubeMultiBoot_Main_Return + @ Save current interrupt master register value + ldr r3, pool_InterruptRegs + ldrh r2, [r3, OFFSET_REG_IME - 0x200] + @ Disable all interrupts + movs r1, 0 + strh r1, [r3, OFFSET_REG_IME - 0x200] + @ Increment the first counter, if it's less than or equal to 10. + ldrb r1, [r0, GCMB_STRUCT_COUNTER1] + cmp r1, 0xA + bgt GameCubeMultiBoot_Main_SkipCounter1Inc + adds r1, 0x1 + strb r1, [r0, GCMB_STRUCT_COUNTER1] +GameCubeMultiBoot_Main_SkipCounter1Inc: + @ Load the saved interrupt master register value (re-enables interrupts if they were enabled before) + strh r2, [r3, OFFSET_REG_IME - 0x200] +GameCubeMultiBoot_Main_SkipCounters: + @ Initialise multiboot structures if required + bcs GameCubeMultiBoot_Init + @ Skip this section (check Nintendo logo) if the check has already passed + ldrb r1, [r0, GCMB_STRUCT_MBPROGRESS] + cmp r1, MBPROGRESS_NONE + bne GameCubeMultiBoot_Main_SkipLogoCheck + @ Bail out if no multiboot image data has been transferred yet + ldr r1, [r0, GCMB_STRUCT_CUR_DEST_PTR] + ldr r2, [r0, GCMB_STRUCT_BASE_DEST_PTR] + subs r1, r2 + beq GameCubeMultiBoot_Main_Return2 + @ Also bail out if not enough data has been transferred + cmp r1, ROM_HEADER_NINTENDO_LOGO_END + bcc GameCubeMultiBoot_Main_Return2 + @ Compare the Nintendo logo of the transferred multiboot image header, with the one in the ROM image of the inserted cart + push {r4-r6} + movs r1, ROM_HEADER_NINTENDO_LOGO_LENGTH + adds r2, ROM_HEADER_NINTENDO_LOGO_OFFSET + ldr r4, pool_NintendoLogo +GameCubeMultiBoot_Main_LogoCmpLoop: + ldm r2!, {r5} + ldm r4!, {r6} + cmp r5, r6 + bne GameCubeMultiBoot_Main_LogoCmpEnd + subs r1, 0x4 + bne GameCubeMultiBoot_Main_LogoCmpLoop + ldm r2!, {r5} + ldm r4!, {r6} + eors r5, r6 + lsrs r5, 8 + str r2, [r0, GCMB_STRUCT_BASE_DEST_PTR] +GameCubeMultiBoot_Main_LogoCmpEnd: + pop {r4-r6} + @ Throw everything away if the logo data didn't match + bne GameCubeMultiBoot_Init + @ Logo matched, set the relevent multiboot progress bit + movs r1, MBPROGRESS_LOGO_CORRECT + strb r1, [r0, GCMB_STRUCT_MBPROGRESS] + @ XOR together KeyA and KeyB to get the initial multiboot image checksum value + ldr r1, [r0, GCMB_STRUCT_KEYA] + ldr r2, [r0, GCMB_STRUCT_KEYB] + eors r1, r2 + str r1, [r0, GCMB_STRUCT_HASH_VAL] + @ ...also use it as the initial value for the image encryption session key. Algorithm is the same as the GBA BIOS multiboot: sessionkey = (initialvalue * 0x6177614b) + 1 + ldr r2, pool_Kawa + muls r1, r2 + adds r1, 0x1 + str r1, [r0, GCMB_STRUCT_SESSION_KEY] +GameCubeMultiBoot_Main_Return: + bx lr +GameCubeMultiBoot_Main_SkipLogoCheck: + @ If this code is executed, then the logo check has passed, and the data being transferred in is encrypted. + @ Set up registers. + ldr r1, [r0, GCMB_STRUCT_CUR_DEST_PTR] + mov r12, r1 + ldr r3, [r0, GCMB_STRUCT_HASH_VAL] + push {r4-r7} + ldr r4, [r0, GCMB_STRUCT_BASE_DEST_PTR] + ldr r5, pool_Kawa + ldr r6, [r0, GCMB_STRUCT_SESSION_KEY] + ldr r7, pool_HashVal +GameCubeMultiBoot_Main_ImageDecryptHashLoop: + @ If there's no more data, break out of the loop + cmp r4, r12 + bcs GameCubeMultiBoot_Main_ImageDecryptHashEnd + @ Get the next uint32 + ldr r1, [r4] + @ Decrypt the ciphertext: plaintext = (ciphertext ^ sessionkey) + hashval + eors r1, r6 + adds r1, r3 + @ Save the current uint32 of plaintext and advance the pointer + stm r4!, {r1} + @ Advance the hashval with this uint32 of plaintext -- this is the same code as GameCubeMultiBoot_Hash. + eors r3, r1 + movs r2, 0x20 +GameCubeMultiBoot_Main_HashLoop: + lsrs r3, 1 + bcc GameCubeMultiBoot_Main_HashSkipEor + eors r3, r7 +GameCubeMultiBoot_Main_HashSkipEor: + subs r2, 0x1 + bne GameCubeMultiBoot_Main_HashLoop + @ Advance the sessionkey with the usual algorithm: sessionkey = (sessionkey * 0x6177614b) + 1 + muls r6, r5 + adds r6, 0x1 + b GameCubeMultiBoot_Main_ImageDecryptHashLoop +GameCubeMultiBoot_Main_ImageDecryptHashEnd: + @ Save the new pointer, sessionkey, hashval + str r4, [r0, GCMB_STRUCT_BASE_DEST_PTR] + str r6, [r0, GCMB_STRUCT_SESSION_KEY] + pop {r4-r7} + str r3, [r0, GCMB_STRUCT_HASH_VAL] + @ Bail out if the image size is unknown + ldrh r1, [r0, GCMB_STRUCT_IMAGE_SIZE] + cmp r1, 0 + bne GameCubeMultiBoot_Main_Return2 + @ Bail out if no image data has been transferred + ldr r1, [r0, GCMB_STRUCT_CUR_DEST_PTR] + ldr r2, [r0, GCMB_STRUCT_BASE_DEST_PTR] + cmp r1, r2 + bne GameCubeMultiBoot_Main_Return2 + @ If KeyC hasn't been generated yet, go generate it + ldr r1, [r0, GCMB_STRUCT_KEYC] + cmp r1, 0 + beq GameCubeMultiBoot_Main_GenerateKeyC + @ If the other side hasn't sent its boot key yet, bail out + ldrh r1, [r0, GCMB_STRUCT_BOOT_KEY] + cmp r1, 0 + beq GameCubeMultiBoot_Main_Return + @ Save off LR so it doesn't get clobbered by the upcoming function call + mov r12, lr + @ Generate the real boot key, which is the checksum of a hardcoded value and KeyC + movs r1, GCMB_MAGIC_BOOTKEY_HASHVAL + ldr r3, [r0, GCMB_STRUCT_KEYC] + bl GameCubeMultiBoot_Hash + ldrh r1, [r0, GCMB_STRUCT_BOOT_KEY] + @ Restore the saved LR value + mov lr, r12 + @ Compare the two boot keys (real and passed in), if they don't match then throw everything away + subs r1, r3 + bne GameCubeMultiBoot_Init + @ The two boot keys matched, tell the caller that the image is ready to boot + movs r1, MBPROGRESS_READY_TO_BOOT + strb r1, [r0, GCMB_STRUCT_MBPROGRESS] + @ Nothing more to do, return. + bx lr +GameCubeMultiBoot_Main_GenerateKeyC: + @ Save off LR so it doesn't get clobbered by the upcoming function call + mov r12, lr + @ KeyC = (SavedVCount << 24) - 1 + ldrb r1, [r0, GCMB_STRUCT_SAVEDVCOUNT] + lsls r1, 24 + subs r1, 0x1 + str r1, [r0, GCMB_STRUCT_KEYC] + @ Hash the KeyC with the multiboot image checksum to generate the KeyC derivation material to be sent to the other side of the link + bl GameCubeMultiBoot_Hash + @ Make sure the sent KeyC derivation material contains a magic value so that the other side can detect it + lsls r3, 8 + adds r3, GCMB_MAGIC_KEYCDERIVATION + @ Save off the KeyC derivation material and return to caller + str r3, [r0, GCMB_STRUCT_KEYC_DERIVATION] + bx r12 +GameCubeMultiBoot_Main_Return2: + 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 + @ If there's no multiboot image ready, just return to caller + ldrb r1, [r0, GCMB_STRUCT_MBPROGRESS] + cmp r1, MBPROGRESS_READY_TO_BOOT + bne GameCubeMultiBoot_ExecuteProgram_Fail + @ Disable interrupts + ldr r3, pool_InterruptRegs + movs r1, 0 + strh r1, [r3, OFFSET_REG_IME - 0x200] + @ Jump to the real entry point of the multiboot image (past the image header), in ARM mode + 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: + @ Throw away anything that got sent + ldr r1, [r3, OFFSET_REG_JOY_RECV - 0x120] + @ Send the game code, the other side of the link must send back the same game code + ldr r1, pool_RubyUSAGameCode + str r1, [r3, OFFSET_REG_JOY_TRANS - 0x120] + movs r1, 0x10 + strh r1, [r3, OFFSET_REG_JOYSTAT - 0x120] + @ Use the saved VCount value to provide 8 bits of entropy for KeyB + ldrb r1, [r0, GCMB_STRUCT_SAVEDVCOUNT] + strb r1, [r0, GCMB_STRUCT_KEYB + 1] + @ If a multiboot image has been transferred at least enough such that the Nintendo logo check has passed, stop everything. + ldrb r1, [r0, GCMB_STRUCT_MBPROGRESS] + cmp r1, 0 + bne GcMbIntrHandler_Stop + @ Set the image destination pointers. + ldr r1, pool_MultiBootLoadAddr + str r1, [r0, GCMB_STRUCT_BASE_DEST_PTR] + str r1, [r0, GCMB_STRUCT_CUR_DEST_PTR] + @ Set the new interrupt handler. + 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 + @ Use the saved VCount value to provide another 8 bits of entropy for KeyB. + ldrb r1, [r0, GCMB_STRUCT_SAVEDVCOUNT] + strb r1, [r0, GCMB_STRUCT_KEYB + 3] + adr r2, GcMbIntrHandler_ReceiveKeyA + b GameCubeMultiBoot_SetInterruptHandler + + .align 2, 0 + +GcMbIntrHandler_ReceiveKeyA: @ 81DCD0C + lsrs r1, 1 @ is receive complete? + bcc GcMbIntrHandler_Stop @ branch if not + ldr r1, [r3, OFFSET_REG_JOY_RECV - 0x120] + @ make sure top 8 bits of the received value is the KeyA magic number, stop if KeyA is invalid + lsrs r2, r1, 24 + cmp r2, GCMB_MAGIC_KEYA + bne GcMbIntrHandler_Stop + @ save received KeyA + str r1, [r0, GCMB_STRUCT_KEYA] + @ use the second GameCubeMultiBoot_Main() counter as another 8 bits of entropy for KeyB + ldrb r1, [r0, GCMB_STRUCT_COUNTER2] + strb r1, [r0, GCMB_STRUCT_KEYB + 2] + movs r2, 0 + movs r3, 0 + ldr r1, [r0, GCMB_STRUCT_KEYB] + lsrs r1, 8 + @ make sure KeyB is valid (other side of the link is supposed to check KeyB too), if it's not then change the byte that was just set so it is +GameCubeMultiBoot_KeyBCheckLoop: + lsrs r1, 1 + adcs r2, r3 + cmp r1, 0 + bne GameCubeMultiBoot_KeyBCheckLoop + cmp r2, 0xE + bgt GameCubeMultiBoot_KeyBSaveNewByte + cmp r2, 0x7 + bge GameCubeMultiBoot_KeyBCheckEnd + movs r1, 0xFF +GameCubeMultiBoot_KeyBSaveNewByte: + strb r1, [r0, GCMB_STRUCT_KEYB + 2] +GameCubeMultiBoot_KeyBCheckEnd: + @ add in the KeyB magic number and send off KeyB + ldr r1, [r0, GCMB_STRUCT_KEYB] + adds r1, GCMB_MAGIC_KEYB + ldr r3, pool_SerialRegs + str r1, [r3, OFFSET_REG_JOY_TRANS - 0x120] + movs r1, 0x30 + strh r1, [r3, OFFSET_REG_JOYSTAT - 0x120] + @ set new interrupt handler + adr r2, GcMbIntrHandler_CheckKeyBSent + b GameCubeMultiBoot_SetInterruptHandler + + .align 2, 0 + +GcMbIntrHandler_CheckKeyBSent: @ 81DCD4C + lsls r1, 31 + bcc GcMbIntrHandler_Stop @ stop if send failed + bmi GameCubeMultiBoot_CheckImageSizeResponse @ branch if receive is complete + adr r2, GcMbIntrHandler_CheckImageSizeResponse + b GameCubeMultiBoot_SetInterruptHandler + + .align 2, 0 + +GcMbIntrHandler_CheckImageSizeResponse: @ 81DCD58 + lsrs r1, 1 @ is receive complete? + bcc GcMbIntrHandler_Stop @ branch if not +GameCubeMultiBoot_CheckImageSizeResponse: + ldr r1, [r3, OFFSET_REG_JOY_RECV - 0x120] + ldr r2, GameCubeMultiBoot_MaximumImageSizeUInt32s + cmp r1, r2 + bhs GcMbIntrHandler_Stop + adds r1, 0x1 + adds r1, r1 + strh r1, [r0, GCMB_STRUCT_IMAGE_SIZE] + ldrb r1, [r0, GCMB_STRUCT_MBPROGRESS] + cmp r1, 0 +GcMbIntrHandler_StopIfNotEqual: + 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_CheckImageResponse + b GameCubeMultiBoot_SetInterruptHandler + + .align 2, 0 + +GcMbIntrHandler_CheckImageResponse: @ 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] + @ get the recieved uint32 + ldr r1, [r3, OFFSET_REG_JOY_RECV - 0x120] + @ put it in the current destination pointer and advance that pointer + stm r2!, {r1} + @ save off the advanced pointer + str r2, [r0, GCMB_STRUCT_CUR_DEST_PTR] + @ decrease the image size (in uint32s) + ldrh r1, [r0, GCMB_STRUCT_IMAGE_SIZE] + subs r1, 0x1 + strh r1, [r0, GCMB_STRUCT_IMAGE_SIZE] + @ branch away if the transfer is not yet complete + bne GameCubeMultiBoot_ReadVCount + +GcMbIntrHandler_SendCounter2: + @ send counter2 with magic number + ldrb r1, [r0, GCMB_STRUCT_COUNTER2] + lsls r1, 8 + adds r1, GCMB_MAGIC_COUNTER2 + str r1, [r3, OFFSET_REG_JOY_TRANS - 0x120] + adr r2, GcMbIntrHandler_CheckCounter2Sent + b GameCubeMultiBoot_SetInterruptHandler + + .align 2, 0 + +GcMbIntrHandler_CheckCounter2Sent: + lsls r1, 31 + +GcMbIntrHandler_StopIfSendFailed: + bcc GcMbIntrHandler_Stop @ stop if send failed + @ if KeyC derivation value has not yet been generated, send Counter2 again, otherwise, send KeyC derivation + ldr r1, [r0, GCMB_STRUCT_KEYC_DERIVATION] + cmp r1, 0 + beq GcMbIntrHandler_SendCounter2 + str r1, [r3, OFFSET_REG_JOY_TRANS - 0x120] + adr r2, GcMbIntrHandler_CheckKeyCDerivationSent + b GameCubeMultiBoot_SetInterruptHandler + + .align 2, 0 + +GcMbIntrHandler_CheckKeyCDerivationSent: @ 81DCDB8 + lsls r1, 31 + bcc GcMbIntrHandler_StopIfSendFailed @ branch if send failed + bmi GameCubeMultiBoot_CheckBootKeyResponse @ branch if receive is complete + adr r2, GcMbIntrHandler_CheckBootKeyResponse + b GameCubeMultiBoot_SetInterruptHandler + + .align 2, 0 + +GcMbIntrHandler_CheckBootKeyResponse: @ 81DCDC4 + lsrs r1, 1 @ is receive complete? + bcc GcMbIntrHandler_StopIfSendFailed @ branch if not + +GameCubeMultiBoot_CheckBootKeyResponse: + ldr r1, [r3, OFFSET_REG_JOY_RECV - 0x120] + @ make sure received boot key contains expected magic number, stop if not + lsrs r2, r1, 24 + cmp r2, GCMB_MAGIC_BOOTKEY + bne GcMbIntrHandler_StopIfNotEqual + @ save received bootkey to be checked in GameCubeMultiBoot_Main() + strh r1, [r0, GCMB_STRUCT_BOOT_KEY] + @ stop if anything more gets sent + adr r2, GcMbIntrHandler_StopUnconditionally + b GameCubeMultiBoot_SetInterruptHandler + + .align 2, 0 + +GcMbIntrHandler_StopUnconditionally: @ 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 + +GameCubeMultiBoot_MaximumImageSizeUInt32s: .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. |