mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-12-26 03:34:15 +01:00
642 lines
19 KiB
ArmAsm
642 lines
19 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_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:
|
|
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:
|
|
@ 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:
|
|
@ 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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
b GcMbIntrHandler_Stop
|
|
|
|
thumb_func_end GameCubeMultiBoot_HandleSerialInterrupt
|
|
|
|
non_word_aligned_thumb_func_start GameCubeMultiBoot_Quit
|
|
@ void GameCubeMultiBoot_Quit();
|
|
GameCubeMultiBoot_Quit:
|
|
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 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.
|