.cpu arm7tdmi
.syntax unified

#define LITE
#include "../asminc.h"

.macro LOAD_EXMEMCNT
    @ loads EXMEMCNT register address
    ldr r7,= 0x04000200
    @ waitstate 4,2 and arm9 slot2 access
    @ r7 holds the EXMEMCNT address, use lower 8 bits as 0
    strb r7, [r7, #4]
.endm

.macro RESTORE_EXMEMCNT
    @ waitstate 4,2 and arm7 slot2 access
    movs r2, #0x80
    strb r2, [r7, #4]
.endm

.macro SD_COMMAND_ARGUMENT value
    movs r2, \value|0x40
.endm

.equ sd_dataadd,     0x9000000
.equ sd_resetaddr,   0x9440000
.equ reg_scsd_cmd,   0x9800000
.equ sd_crc_bit,     0x0100000
.equ sd_rw4,         0x0200000
.equ sd_rw1,         0x0000000
.equ sd_buff_bit,    0x0400000
.equ sd_command_bit, 0x0800000
.equ sd_st,          0x0040000 + sd_crc_bit
.equ sd_status_addr, sd_dataadd + sd_st
.equ sd_buff_bit_addr, sd_dataadd + sd_buff_bit
.equ sd_dataread_4,  sd_dataadd + sd_rw4
.equ sd_dataread_1,  sd_dataadd + sd_rw1
.equ sd_datawrite_4, sd_dataadd + sd_rw4
.equ sd_datawrite_1, sd_dataadd + sd_rw1

.section "sclite_sd_command_drop", "ax"
@ void sclite_sdCommandAndDropResponse6(uint32_t dummy, uint32_t argument, SD_COMMANDS command)
@ command is passed in r2
BEGIN_THUMB_FUNCTION sclite_sdCommandAndDropResponse6
    @ among the pushed registers there are the command and
    @ argument one, which are then used at the loop to
    @ send the sd command
    push {r1-r7,lr}

    @ loads reg_scsd_cmd
    movs r7, #0x98
    lsls r7, r7, #20

    @ while(*r7 & 0x01) == 0
SDCommand_loop:
    ldrh r0, [r7]
    lsrs r0, r0, #1
    bcc SDCommand_loop

    @ perform an extra read
    ldrh r0, [r7]

    @ loads sd_buff_bit_addr
    movs r1, #0x94
    lsls r1, r1, #20
    @ lower halfword is 0
    strh r1, [r1]

    @ loads sd_dataadd + sd_command_bit + sd_rw4
    movs r1, #0x9A
    lsls r1, r1, #20

    @ the command buffer starts at sp+4 (r1 and r2) since this loop starts with offset 5
    @ decrement the value of the stack pointer address used by 1
    mov r4,sp
    movs r3, #4
write_SDCommand_loop:
    ldrb r0, [r4, r3]
    lsls r2, r0, #20
    adds r2, r2, r0
    str r2, [r1]
    subs r3, r3, #1
    bpl write_SDCommand_loop

    @ loads sd_dataadd + sd_crc_bit + sd_rw4
    movs r1, #0x9B
    lsls r1, r1, #20
    movs r2, #0
    str r2, [r1]

    @ while(*r7 & 0x01) != 0
SDCommand_loop_2nd:
    ldrh r0, [r7]
    lsrs r0, r0, #1
    bcs SDCommand_loop_2nd

    movs r6, #4
SDCommand_drop_resp:
    ldmia r7!, {r0-r5}
    subs r6, r6, #1
    bne SDCommand_drop_resp

    @ restore stack space
    pop {r1-r7,pc}
.balign 4
.pool

.section "sclite_write_sector", "ax"
@ void sclite_writeSector(uint32_t sector, uint8_t* buff, uint32_t writenum)
BEGIN_THUMB_FUNCTION sclite_writeSector
    push {r1,r2,r4-r7,lr}

    @ load EXMEMCNT register
    LOAD_EXMEMCNT

    @ r1 for now holds the sector
.global sclite_writeSectorSdhcLabel
sclite_writeSectorSdhcLabel:
    @ if not sdhc this needs to be shifted to the left by 9
    lsls r1, r0, #9
    @ movs r1, r0

    @ enable sd access
    @ this function won't touch r1
    movs r0, #3
    CALL sccmn_changeMode writeInterwork

    @ SDResetCard
    @ write 0
    ldr r2,=sd_resetaddr
    strh r2, [r2]

    @ WRITE_MULTIPLE_BLOCK
    SD_COMMAND_ARGUMENT #25
    @ 2nd parameter is in r1 from above

    CALL sclite_sdCommandAndDropResponse6 writeInterwork

    @ this function will trash r4 but leave r0 and r1 untouched
    CALL sccmn_sdSendClock10 writeInterwork

    @ loads the saved r0 and r2 (readnum)
    @ into r0 and r1
    pop {r0,r1}
write_sector_loop:
    @ all the functions called in this loop don't change the value of r0 and r1
    @ except sclite_writeData, which will increase r0 by 512
    @ sccmn_sdio4BitCrc16 will write the checksum to r2-r3
    CALL sccmn_sdio4BitCrc16 writeInterwork

    @ first argument is buffer
    @ regs r2 and r3 hold the crc
    CALL sclite_writeData writeInterwork

    @ this function will trash r4 but leave r0 and r1 untouched
    CALL sccmn_sdSendClock10 writeInterwork

    subs r1, #1
    bne write_sector_loop

    @ STOP_TRANSMISSION
    SD_COMMAND_ARGUMENT #12
    @ 2nd parameter is passed in r1
    @ and from the loop above r1 is already 0

    CALL sclite_sdCommandAndDropResponse6 writeInterwork

    @ this function will trash r4 but leave r0 and r1 untouched
    CALL sccmn_sdSendClock10 writeInterwork

    @ loads sd_dataadd
    movs r0, #0x90
    lsls r0, r0, #20

    @ while(*r1 &0x100) == 0
beginwhile_WriteSector:
    ldrh r1, [r0]
    lsrs r1, #9
    bcc beginwhile_WriteSector

    movs r0, #1
    CALL sccmn_changeMode writeInterwork

    @ restore EXMEMCNT register
    RESTORE_EXMEMCNT

    pop {r4-r7,pc}
INTERWORK writeInterwork
INTERWORK_FUNCTION sclite_writeData writeInterwork
INTERWORK_FUNCTION sccmn_sdio4BitCrc16 writeInterwork
INTERWORK_FUNCTION sccmn_sdSendClock10 writeInterwork
INTERWORK_FUNCTION sccmn_changeMode writeInterwork
INTERWORK_FUNCTION sclite_sdCommandAndDropResponse6 writeInterwork

.balign 4
.pool

.section "sclite_read_sector", "ax"
@ bool sclite_readSector(uint32_t sector, uint8_t *buff, uint32_t readnum)
BEGIN_THUMB_FUNCTION sclite_readSector
    push {r1,r2-r7,lr}
    LOAD_EXMEMCNT

    @ r1 for now holds the sector
.global sclite_readSectorSdhcLabel
sclite_readSectorSdhcLabel:
    @ if not sdhc this needs to be shifted to the left by 9
    lsls r1, r0, #9
    @ movs r1, r0

    @ enable sd access
    @ this function won't touch r1
    movs r0, #3
    CALL sccmn_changeMode readInterwork

    @ SDResetCard
    @ write 0
    ldr r2,= sd_resetaddr
    strh r2, [r2]

    @ READ_MULTIPLE_BLOCK
    SD_COMMAND_ARGUMENT #18
    @ 2nd parameter is in r1 from above

    CALL sclite_sdCommandAndDropResponse6 readInterwork

    @ loads the saved r0 and r2 (writenum)
    @ into r0 and r1
    pop {r0,r1}
read_sector_loop:
    @ all the functions called in this loop don't change the value of r0 or r1
    @ except sclite_readData, which will increase r0 by 512 automatically
    CALL sclite_readData readInterwork

    subs r1, #1
    bne read_sector_loop

    @ STOP_TRANSMISSION
    SD_COMMAND_ARGUMENT #12
    @ 2nd parameter is passed in r1
    @ and from the loop above r1 is already 0

    CALL sclite_sdCommandAndDropResponse6 readInterwork

    @ this function will trash r4 but leave r0 and r1 untouched
    CALL sccmn_sdSendClock10 readInterwork


    movs r0, #1
    CALL sccmn_changeMode readInterwork

    RESTORE_EXMEMCNT

    @ returns true
    @ the change mode function above doesn't touch r0, so it's still 1
    @ movs r0, #1
    pop {r3-r7,pc}
INTERWORK readInterwork
INTERWORK_FUNCTION sccmn_changeMode readInterwork
INTERWORK_FUNCTION sclite_sdCommandAndDropResponse6 readInterwork
INTERWORK_FUNCTION sclite_readData readInterwork
INTERWORK_FUNCTION sccmn_sdSendClock10 readInterwork

.balign 4
.pool

.section "sclite_read_data", "ax"
@ void sclite_readData(void* buffer)
BEGIN_THUMB_FUNCTION sclite_readData
    push {r1,r4-r7,lr}

    @ dummy read SD_STATUS
    ldr r1,= sd_status_addr
    ldrh r1, [r1]

    @ loads sd_buff_bit_addr
    movs r1, #0x94
    lsls r1, r1, #20

    @ loops as long as sd_buff_bit is 1
sclite_readData_buff_bit_loop:
    ldrh r3, [r1]
    lsrs r3, #1
    bcs sclite_readData_buff_bit_loop

    @ loads sd_dataread_4
    movs r1, #0x92
    lsls r1, r1, #20

    @ performs a dummy read of a short to initiate the transfer
    ldrh r2, [r1]

    @ lr holds buffer + 512
    movs r2, #0x80
    lsls r2, r2, #2
    adds r2, r0, r2
    mov lr, r2

    ldmia r1!, {r2-r3}
    stmia r0!, {r2-r3}

sclite_readData_loop:
    @ load 6 ints at the time, for a total of 24 bytes
    ldmia r1!, {r2-r7}
    stmia r0!, {r2-r7}
    cmp	r0,lr
    bne sclite_readData_loop

    @ drop crc16
    ldr r2, [r1]
    ldrh r2, [r1]

    @ loads sd_dataread_1
    movs r1, #0x90
    lsls r1, r1, #20
    ldrh r2, [r1]

    pop {r1,r4-r7,pc}

.balign 4
.pool

.section "sclite_write_data", "ax"
@ void sclite_writeData(void*& buffer, int value_to_keep, int crc_buff1, int crc_buff2)
@ this function updates r0, leaves r1 untouched and thrashes r4
BEGIN_THUMB_FUNCTION sclite_writeData
    @ loads sd_dataadd
    movs r4, #0x90
    lsls r4, r4, #20

    @ save sd_dataadd constant above for later
    push {r1-r7,lr}

waitOnWriteFalse_WriteData:
    ldrh r1, [r4]
    lsrs r1, #9
    bcc waitOnWriteFalse_WriteData

    @ dummy read SD_DATAADD
    ldrh r1, [r4]

    @ transmission start bit (lower 16 bit of r4 are 0)
    strh r4, [r4]


    @ loads sd_datawrite_4
    movs r7, #0x92
    lsls r7, r7, #20

    @ lr holds buffer + 512
    movs r2, #0x80
    lsls r2, r2, #2
    adds r2, r0, r2
    mov lr, r2

    ldmia r0!, {r1-r2}
    stmia r7!, {r1-r2}

sclite_writeData_loop:
    @ load 6 ints at the time, for a total of 24 bytes
    ldmia r0!, {r1-r6}
    stmia r7!, {r1-r6}
    cmp	r0, lr
    bne sclite_writeData_loop


    @ r1 holds a value that has not to be changed
    @ r2-r3 hold the crc value to be written
    @ r4 holds the value sd_dataadd saved at the start
    pop {r1-r4}
    stmia r7!, {r2-r3}

    @ write end bit
    movs r3, #0xff
    strh r3, [r4]

waitOnWriteTrue_WriteData:
    ldrh r3, [r4]
    lsrs r3, #9
    bcs waitOnWriteTrue_WriteData

    @ dummy writes to end the transfer
    movs r3, #0
    str r3, [r4]
    str r3, [r4]

    pop {r5-r7,pc}

.balign 4
.pool
