mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-06-02 17:26:48 +02:00
* DATEL: Make read sector function vram safe The platform code was performing reads in single byte units, breaking in case the destination buffer was in vram. This caused issues with some games (e.g. Castelvania Order of Ecclesia), and also broke soft resetting. * Halve wait timeout on sector read since we read 2 bytes at the time
178 lines
4.8 KiB
ArmAsm
178 lines
4.8 KiB
ArmAsm
#include "asminc.h"
|
|
|
|
.syntax unified
|
|
.thumb
|
|
|
|
.section "datel_read_spi", "ax"
|
|
@ NOTE!!!: This function needs to set r0 last with mov or something similar so that it updates the zero flags
|
|
@u8 datel_readSpiByte(void);
|
|
BEGIN_ASM_FUNC datel_readSpiByte
|
|
movs r0, 0xFF
|
|
@u8 datel_readWriteSpiByte(u8);
|
|
BEGIN_ASM_FUNC datel_readWriteSpiByte
|
|
push {r1-r3, lr}
|
|
ldr r3, =REG_MCCNT0
|
|
@ Wait if there's a transfer in progress (can happen if the random byte sent by the cycle spi function is still on its way)
|
|
1:
|
|
ldrh r1, [r3]
|
|
lsrs r1, r1, #8
|
|
bcs 1b
|
|
|
|
@ Actually write the byte of interest and wait for it to be sent
|
|
strh r0, [r3, #2]
|
|
1:
|
|
ldrh r1, [r3]
|
|
lsrs r1, r1, #8
|
|
bcs 1b
|
|
@uppper half always 0
|
|
ldrh r0, [r3, #2]
|
|
cmp r0, #0
|
|
pop {r1-r3, pc}
|
|
|
|
@u16 datel_readWriteSpiShort();
|
|
BEGIN_ASM_FUNC datel_readSpiShort
|
|
push {r1-r7, lr}
|
|
bl datel_readSpiByte
|
|
movs r1, r0
|
|
bl datel_readSpiByte
|
|
lsls r0, #8
|
|
orrs r0, r1
|
|
pop {r1-r7, pc}
|
|
|
|
@u8 datel_readSpiByteTimeout(void);
|
|
BEGIN_ASM_FUNC datel_readSpiByteTimeout
|
|
push {r1-r4, lr}
|
|
@ use a timeout of 0x1000 instead of 0xFFF, easier to setup
|
|
@ ldr r4, =DATEL_SD_CMD_TIMEOUT_LEN
|
|
movs r4, #1
|
|
lsls r4, #12
|
|
1:
|
|
bl datel_readSpiByte
|
|
cmp r0, #0xFF
|
|
bne 1f
|
|
subs r4, r4, #1
|
|
bne 1b
|
|
1:
|
|
pop {r1-r4, pc}
|
|
|
|
@bool datel_waitSpiByteTimeout();
|
|
BEGIN_ASM_FUNC datel_waitSpiByteTimeout
|
|
push {r1-r4, lr}
|
|
@ use a timeout of 0x1000 instead of 0xFFFF, easier to setup
|
|
@ ldr r2, =DATEL_SD_WRITE_TIMEOUT_LEN
|
|
movs r2, #1
|
|
lsls r2, #16
|
|
1:
|
|
bl datel_readSpiByte
|
|
bne 1f
|
|
subs r2, #1
|
|
bne 1b
|
|
|
|
movs r0, #0
|
|
pop {r1-r4, pc}
|
|
|
|
1:
|
|
movs r0, #1
|
|
pop {r1-r4, pc}
|
|
|
|
.section "datel_spi_send", "ax"
|
|
@void datel_cycleSpi();
|
|
datel_cycleSpi:
|
|
push {r0-r5, lr}
|
|
adr r0, datel_cycleSpi_data
|
|
ldm r0!, {r1,r3,r4}
|
|
@ First send spi disable command, the second time this loop is called we send spi enable
|
|
movs r5, DATEL_CMD_F2_SPI_DISABLE
|
|
datel_sendNtrCommandF2:
|
|
movs r2, #0xF2
|
|
lsls r0, r5, #8
|
|
str r2, [r1, #8]
|
|
str r0, [r1, #12]
|
|
|
|
@ we shift the 0xF2 loaded above by 14 to get 0xXXXX8000 (MCCNT0_ENABLE)
|
|
lsls r2, #14
|
|
strh r2, [r1]
|
|
@ REG_MCCNT0 + 4 = REG_MCCNT1
|
|
str r4, [r1, #4]
|
|
1:
|
|
ldr r2, [r1, #4]
|
|
cmp r2, #0
|
|
blt 1b
|
|
|
|
adds r5, #4
|
|
cmp r5, DATEL_CMD_F2_SPI_ENABLE
|
|
@ Enable spi and also send a dummy byte at the same time by writing first to REG_MCCNT0 and then to REG_MCD0
|
|
str r3, [r1]
|
|
beq datel_sendNtrCommandF2
|
|
|
|
pop {r0-r5, pc}
|
|
|
|
@ NOTE!!!: This function needs to set r0 last with mov or something similar so that it updates the zero flags
|
|
@u8 datel_spiSendSDIOCommandR0(u32 arg, u8 cmd);
|
|
BEGIN_ASM_FUNC datel_spiSendSDIOCommandR0
|
|
movs r2, 0
|
|
@u8 datel_spiSendSDIOCommand(u32 arg, u8 cmdId, int extraBytes);
|
|
BEGIN_ASM_FUNC datel_spiSendSDIOCommand
|
|
push {r0-r7, lr}
|
|
|
|
bl datel_cycleSpi
|
|
|
|
adr r4, datel_spiSendSDIOCommandR0_ReadSpiByteTimeout
|
|
@ r3 contains ReadSpiByteTimeout
|
|
@ r7 contains ReadWriteSpiByte
|
|
ldm r4!, {r3,r7}
|
|
|
|
@ we use the cmd and arg directly from the stack
|
|
@ r0 is on top, r1 is right below, we read the command id as the last byte pushed of r1,
|
|
@ so at sp -1, the command arguments are at sp+0,sp+1,sp+2,sp+3, the 6th byte is garbage data read
|
|
@ from sp+4, we don't need it to be something meaningful
|
|
mov r4, sp
|
|
@ TODO: this could maybe be optimized by setting 4 in r5, and having the last extra byte be sent by the timeout function itself
|
|
subs r4, #1
|
|
|
|
movs r5, #6
|
|
|
|
@ sets up lr so that the interwork function jumps back here
|
|
bl 1f
|
|
1:
|
|
subs r5, r5, #1
|
|
ldrb r0, [r4, r5]
|
|
@ branch to datel_readWriteSpiByte
|
|
bcs datel_spiSendSDIOCommandR0_Interwork
|
|
|
|
@ branch to datel_readSpiByteTimeout
|
|
bl datel_spiSendSDIOCommandR0_InterworkR3
|
|
|
|
@ load datel_readSpiByte since it's 1 thumb instruction before datel_readWriteSpiByte
|
|
subs r7, r7, #2
|
|
|
|
@ Save the return value
|
|
movs r6, r0
|
|
|
|
@ sets up lr so that the interwork function jumps back here
|
|
bl 1f
|
|
1:
|
|
subs r2, #1
|
|
@ branch to datel_readSpiByte
|
|
bcc datel_spiSendSDIOCommandR0_Interwork
|
|
movs r0, r6
|
|
pop {r1}
|
|
pop {r1-r7, pc}
|
|
|
|
datel_spiSendSDIOCommandR0_Interwork:
|
|
bx r7
|
|
datel_spiSendSDIOCommandR0_InterworkR3:
|
|
bx r3
|
|
.balign 4
|
|
.pool
|
|
datel_cycleSpi_data:
|
|
.word REG_MCCNT0
|
|
.word 0x00FFA040 @ MCCNT0_MODE_SPI | MCCNT0_SPI_HOLD_CS | MCCNT0_ENABLE in lower 16 bit, 0xFF in upper 16
|
|
.word 0xA07F6000 @ MCCNT1_RESET_OFF | MCCNT1_CMD_SCRAMBLE | MCCNT1_READ_DATA_DESCRAMBLE | MCCNT1_CLOCK_SCRAMBLER | MCCNT1_LATENCY2(0x3F)
|
|
.global datel_spiSendSDIOCommandR0_ReadSpiByteTimeout
|
|
datel_spiSendSDIOCommandR0_ReadSpiByteTimeout:
|
|
.word 0
|
|
.global datel_spiSendSDIOCommandR0_ReadWriteSpiByte
|
|
datel_spiSendSDIOCommandR0_ReadWriteSpiByte:
|
|
.word 0
|