#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
