mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-06-02 01:06:50 +02:00
- Supercard CF (SUPERCARDCF) - GBA Media Player CF (MPCF) - M3 Adapter CF (M3CF) - Max Media Dock CF (MMCF)
203 lines
5.0 KiB
ArmAsm
203 lines
5.0 KiB
ArmAsm
.cpu arm7tdmi
|
|
.syntax unified
|
|
.thumb
|
|
|
|
.section "cf_perform_transfer", "ax"
|
|
|
|
.equ CF_CMD_LBA, 0xE0
|
|
.equ CF_CMD_READ, 0x20
|
|
.equ CF_CMD_WRITE, 0x30
|
|
|
|
@ bool cf_performTransferSectors(u32 numSectors, u32 sector, void* srcAddr, void* dstAddr, u8 command)
|
|
.type cf_performTransferSectors, %function
|
|
.global cf_performTransferSectors
|
|
cf_performTransferSectors:
|
|
push {r0,r1,r4-r7,lr}
|
|
ldr r7, cf_performTransferSectors_waitCardAvailableForCommands
|
|
@ calls waitCardAvailableForCommands
|
|
@ this function doesn't alter r0, but sets the zero flag on failure and clears it on success
|
|
bl cf_performTransferSectors_interwork
|
|
beq cf_performTransferSectors_error
|
|
|
|
ldr r5, cf_performTransferSectors_reg_sector_count
|
|
|
|
@ load 0x20000
|
|
movs r6, #0x01
|
|
lsls r6, #17
|
|
|
|
@ store sector count
|
|
strh r0, [r5]
|
|
adds r5, r6
|
|
|
|
lsls r7, r1, #24
|
|
lsrs r7, r7, #24
|
|
@ store lba1
|
|
strh r7, [r5]
|
|
adds r5, r6
|
|
|
|
lsls r7, r1, #16
|
|
lsrs r7, r7, #24
|
|
@ store lba2
|
|
strh r7, [r5]
|
|
adds r5, r6
|
|
|
|
lsls r7, r1, #8
|
|
lsrs r7, r7, #24
|
|
@ store lba3
|
|
strh r7, [r5]
|
|
adds r5, r6
|
|
|
|
@ Only lower nibble is transferred
|
|
lsls r7, r1, #4
|
|
lsrs r7, r7, #28
|
|
@ store lba4
|
|
adds r7, CF_CMD_LBA
|
|
strh r7, [r5]
|
|
|
|
@ store command
|
|
strh r4, [r5, r6]
|
|
|
|
@ get total number of bytes to write
|
|
lsls r0, #9
|
|
|
|
ldr r7, cf_performTransferSectors_waitNextBlockReady
|
|
read_next_block:
|
|
@ calls waitCardNextBlockReady
|
|
@ this function doesn't alter r0, but sets the zero flag on failure and clears it on success
|
|
bl cf_performTransferSectors_interwork
|
|
beq cf_performTransferSectors_error
|
|
|
|
read_next_int:
|
|
ldm r2!, {r1,r4,r5,r6}
|
|
stm r3!, {r1,r4,r5,r6}
|
|
|
|
subs r0, #16
|
|
beq done
|
|
|
|
@ Shifting left by 0x17 will set the Zero flag if the number that was shifted is a multiple
|
|
@ of 0x200 (indicating a full sector has been written)
|
|
lsls r1, r0, #0x17
|
|
bne read_next_int
|
|
|
|
b read_next_block
|
|
done:
|
|
movs r0, #1
|
|
cf_performTransferSectors_error:
|
|
pop {r0,r1,r4-r7, pc}
|
|
|
|
cf_performTransferSectors_interwork:
|
|
bx r7
|
|
|
|
.balign 4
|
|
.pool
|
|
.global cf_performTransferSectors_reg_sector_count
|
|
cf_performTransferSectors_reg_sector_count:
|
|
.word 0
|
|
.global cf_performTransferSectors_waitCardAvailableForCommands
|
|
cf_performTransferSectors_waitCardAvailableForCommands:
|
|
.word 0
|
|
.global cf_performTransferSectors_waitNextBlockReady
|
|
cf_performTransferSectors_waitNextBlockReady:
|
|
.word 0
|
|
|
|
.section "cf_read_write_functions", "ax"
|
|
.global cf_performTransfer_unlock_label
|
|
.global cf_performTransfer_lock_label
|
|
@ r2 srcAddr
|
|
@ r3 dstAddr
|
|
@ r4 command
|
|
@ top of stack startSector
|
|
@ below it numSectors
|
|
@ cf_performTransfer(dstAddr, srcAddr, command, startSector, numSectors)
|
|
cf_performTransfer:
|
|
@ loads EXMEMCNT register address
|
|
ldr r6, =0x04000200
|
|
@ waitstate 4,2 and arm9 slot2 access
|
|
@ r6 + 4 is EXMEMCNT, use lower 8 bits as 0
|
|
strb r6, [r6, #4]
|
|
|
|
ldr r7, cf_performTransfer_lockUnlockCard
|
|
movs r0, #0
|
|
|
|
@ if the cart requires no lock/unlock sequence, this is replaced with a nop
|
|
cf_performTransfer_unlock_label:
|
|
@ calls lockUnlockCard
|
|
bl cf_performTransfer_interwork
|
|
|
|
@ r2,r3,r4 hold variables not to be touched
|
|
|
|
ldr r7, cf_performTransfer_performTransferSectors
|
|
|
|
@ r1 holds startSector
|
|
@ r5 holds remainingSectors
|
|
pop {r1,r5}
|
|
movs r0, #0xFF
|
|
readNextSectorBlock:
|
|
subs r5, r0
|
|
ble lastRead
|
|
|
|
@ calls performTransferSectors
|
|
@ this function doesn't alter r0, but sets the zero flag on failure and clears it on success
|
|
bl cf_performTransfer_interwork
|
|
beq error
|
|
@ increment sector
|
|
adds r1, r0
|
|
b readNextSectorBlock
|
|
|
|
lastRead:
|
|
adds r0, r5
|
|
@ calls performTransferSectors
|
|
@ this function doesn't alter r0, but sets the zero flag on failure and clears it on success
|
|
bl cf_performTransfer_interwork
|
|
|
|
error:
|
|
ldr r7, cf_performTransfer_lockUnlockCard
|
|
movs r0, #1
|
|
|
|
@ if the cart requires no lock/unlock sequence, this is replaced with a nop
|
|
cf_performTransfer_lock_label:
|
|
@ calls lockUnlockCard
|
|
bl cf_performTransfer_interwork
|
|
|
|
@ waitstate 4,2 and arm7 slot2 access
|
|
movs r2, #0x80
|
|
@ r6 + 4 is EXMEMCNT
|
|
strb r2, [r6, #4]
|
|
pop {r4-r7, pc}
|
|
cf_performTransfer_interwork:
|
|
bx r7
|
|
|
|
@ cf_readSectors(u32 sector, void* buffer, u32 numSectors)
|
|
.type cf_readSectors, %function
|
|
.global cf_readSectors
|
|
cf_readSectors:
|
|
push {r0,r2,r4-r7, lr}
|
|
movs r4, CF_CMD_READ
|
|
movs r3, r1
|
|
ldr r2, cf_performTransfer_reg_data
|
|
|
|
b cf_performTransfer
|
|
|
|
@ cf_writeSectors(u32 sector, void* buffer, u32 numSectors)
|
|
.type cf_writeSectors, %function
|
|
.global cf_writeSectors
|
|
cf_writeSectors:
|
|
push {r0,r2,r4-r7, lr}
|
|
movs r4, CF_CMD_WRITE
|
|
ldr r3, cf_performTransfer_reg_data
|
|
movs r2, r1
|
|
|
|
b cf_performTransfer
|
|
|
|
.balign 4
|
|
.pool
|
|
.global cf_performTransfer_reg_data
|
|
cf_performTransfer_reg_data:
|
|
.word 0
|
|
.global cf_performTransfer_performTransferSectors
|
|
cf_performTransfer_performTransferSectors:
|
|
.word 0
|
|
.global cf_performTransfer_lockUnlockCard
|
|
cf_performTransfer_lockUnlockCard:
|
|
.word 0
|