Last active
October 7, 2015 23:18
-
-
Save BlockoS/3240689 to your computer and use it in GitHub Desktop.
Turbo Everdrive SD card interface
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;--------------------------------------------------------------------- | |
; Turbo-Everdrive SD card library | |
;;--------------------------------------------------------------------- | |
SPI_SS = 0 | |
SPI_FULL_SPEED = 1 | |
SPI_AREAD = 2 | |
STATE_SPI = 0 | |
STATE_RY = 1 | |
STATE_FIFO_WR = 2 | |
STATE_FIFO_RD = 3 | |
STATE_FIFO_RST = 4 | |
REG_SPI = 0 | |
REG_SPI2 = 1 | |
REG_FIFO = 3 | |
REG_STATE = 4 | |
REG_KEY = 5 | |
REG_CFG = 6 | |
REG_MAP = 7 | |
REG_SPI_CFG = 8 | |
REG_FIRM_VER = 9 | |
REG_KEY2 = 10 | |
SPI_REG_ADDR = $C000 ; SPI registers base address | |
SPI_TIMEOUT = 1024 | |
;;--------------------------------------------------------------------- | |
; SD card commands | |
;;--------------------------------------------------------------------- | |
SD_CMD_GO_IDLE_STATE = $40 ; Init card in spi mode if CS low | |
SD_CMD_WAKE_UP = $41 ; Bring card out of idle state | |
SD_CMD_SEND_IF_COND = $48 ; Verify SD Memory Card interface operating condition | |
SD_CMD_SEND_CSD = $49 ; Read the Card Specific Data (CSD register) | |
SD_CMD_SEND_CID = $4A ; Read the card identification information (CID register) | |
SD_STOP_TRANSMISSION = $4C ; Forces the card to stop transmission | |
SD_CMD_SEND_STATUS = $4D ; Read the card status register | |
SD_CMD_READ_BLOCK = $51 ; Read a single data block from the card | |
SD_CMD_READ_MULTIPLE_BLOCK = $52 ; Read blocks of data until a STOP_TRANSMISSION | |
SD_CMD_SET_BLOCK_COUNT = $57 ; Specify block count for multiple block read or write | |
SD_CMD_WRITE_BLOCK = $58 ; Write a single data block to the card | |
SD_CMD_WRITE_MULTIPLE_BLOCK = $59 ; Write blocks of data until a STOP_TRANSMISSION | |
SD_CMD_ERASE_WR_BLK_START = $60 ; Sets the address of the first block to be erased | |
SD_CMD_ERASE_WR_BLK_END = $61 ; Sets the address of the last block of the continuous range to be erased | |
SD_CMD_ERASE = $66 ; Erase all previously selected blocks | |
SD_CMD_APP_CMD = $77 ; Escape for application specific command | |
SD_CMD_READ_OCR = $7A ; Read the OCR register of a card | |
SD_CMD_SET_WR_BLK_ERASE_COUNT = $97 ; Set the number of write blocks to be pre-erased before writing | |
SD_CMD_SD_SEND_OP_COMD = $A9 ; Sends host capacity support information and activates the card's initialization process | |
SD_CMD_41_TODO = $69 ; | |
;;--------------------------------------------------------------------- | |
; SD card type | |
;;--------------------------------------------------------------------- | |
SD_V2 = 2 | |
SD_HC = 1 | |
;;--------------------------------------------------------------------- | |
; Errors | |
;;--------------------------------------------------------------------- | |
ERR_FILE_TOO_BIG = 140 | |
ERR_OS_RISK = 141 | |
ERR_WRONG_OS_SIZE = 142 | |
ERR_OS_FRAGMENTATION = 143 | |
ERR_OS_BAD_TILE = 144 | |
FAT_ERR_INIT = 110 | |
FAT_LFN_ERROR = 115 | |
DISK_ERR_INIT = 50 | |
DISK_ERR_RD1 = 62 | |
DISK_ERR_RD2 = 63 | |
DISK_ERR_WR1 = 64 | |
DISK_ERR_WR2 = 65 | |
DISK_ERR_WR3 = 66 | |
DISK_ERR_WR4 = 67 | |
DISK_ERR_WR5 = 68 | |
;;--------------------------------------------------------------------- | |
; | |
;;--------------------------------------------------------------------- | |
SPI_SS_OFF .macro | |
lda SPI_REG_ADDR + REG_SPI_CFG | |
ora #(1 << SPI_SS) | |
sta SPI_REG_ADDR + REG_SPI_CFG | |
.endm | |
SPI_SS_ON .macro | |
lda SPI_REG_ADDR + REG_SPI_CFG | |
and #~(1 << SPI_SS) | |
sta SPI_REG_ADDR + REG_SPI_CFG | |
.endm | |
SPI_SPEED_ON .macro | |
lda SPI_REG_ADDR + REG_SPI_CFG | |
ora #(1 << SPI_FULL_SPEED) | |
sta SPI_REG_ADDR + REG_SPI_CFG | |
.endm | |
SPI_SPEED_OFF .macro | |
lda SPI_REG_ADDR + REG_SPI_CFG | |
and #~(1 << SPI_FULL_SPEED) | |
sta SPI_REG_ADDR + REG_SPI_CFG | |
.endm | |
SPI_AREAD_ON .macro | |
lda SPI_REG_ADDR + REG_SPI_CFG | |
ora #(1 << SPI_AREAD) | |
sta SPI_REG_ADDR + REG_SPI_CFG | |
.endm | |
SPI_AREAD_OFF .macro | |
lda SPI_REG_ADDR + REG_SPI_CFG | |
and #~(1 << SPI_AREAD) | |
sta SPI_REG_ADDR + REG_SPI_CFG | |
.endm | |
SPI_BUSY_WAIT .macro | |
stw #SPI_TIMEOUT, <_ed_timeout | |
.spi_busy_wait_\@: | |
decw <_ed_timeout | |
ora <_ed_timeout | |
beq .spi_busy_end_\@ ; If we leave there the clary flag should have been set by sbc | |
lda SPI_REG_ADDR + REG_STATE | |
and #(1 << STATE_SPI) | |
beq .spi_busy_wait_\@ | |
clc | |
.spi_busy_end_\@: | |
.endm | |
FIFO_RD_WAIT .macro | |
.spi_rd_wait_\@: | |
lda SPI_REG_ADDR + REG_STATE | |
and #(1 << STATE_FIFO_RD) | |
beq .spi_rd_wait_\@ | |
.endm | |
FIFO_WR_WAIT .macro | |
.spi_wr_wait_\@: | |
lda SPI_REG_ADDR + REG_STATE | |
and #(1 << STATE_FIFO_WR) | |
beq .spi_wr_wait_\@ | |
.endm | |
;;--------------------------------------------------------------------- | |
; | |
;;--------------------------------------------------------------------- | |
.zp | |
_ed_bl .ds 1 ; MPR6 save | |
_ed_addr .ds 4 ; current card address | |
_ed_buffer .ds 4 ; 4 bytes buffer | |
_ed_cardtype .ds 1 ; card type | |
_ed_timeout .ds 2 | |
ed_block_cp_inst .ds 1 | |
ed_block_cp_src .ds 2 | |
ed_block_cp_dst .ds 2 | |
ed_block_cp_size .ds 2 | |
ed_block_cp_rts .ds 1 | |
.code | |
;;--------------------------------------------------------------------- | |
; name : ed_map | |
; desc : Save mpr 6 and set it in order to use Everdrive's registers. | |
;;--------------------------------------------------------------------- | |
ed_map .macro | |
tma #$6 | |
sta <_ed_bl | |
cla | |
tam #$6 | |
.endm | |
;;--------------------------------------------------------------------- | |
; name : ed_unmap | |
; desc : Restore mpr 6. | |
;;--------------------------------------------------------------------- | |
ed_unmap .macro | |
lda <_ed_bl | |
tam #$6 | |
.endm | |
;;--------------------------------------------------------------------- | |
; name : ed_begin | |
; desc : Enable everdrive. | |
; in : | |
; out : | |
;;--------------------------------------------------------------------- | |
.ifdef HUC | |
_ed_begin: | |
jsr ed_begin | |
ed_unmap | |
rts | |
.endif | |
ed_begin: | |
ed_map | |
; Put the following instructions into ram: | |
; tai src, dst, size | |
; rts | |
lda #$f3 ; tai | |
sta <ed_block_cp_inst | |
stw #SPI_REG_ADDR, <ed_block_cp_src | |
stw #512, <ed_block_cp_size | |
lda #$60 ; rts | |
sta <ed_block_cp_rts | |
lda #$A5 | |
sta SPI_REG_ADDR + REG_KEY | |
lda #$56 | |
sta SPI_REG_ADDR + REG_KEY2 | |
stz SPI_REG_ADDR + REG_CFG | |
stz SPI_REG_ADDR + REG_SPI_CFG | |
rts | |
;;--------------------------------------------------------------------- | |
; name : ed_end | |
; desc : Disable everdrive. | |
; in : | |
; out : | |
;;--------------------------------------------------------------------- | |
.ifdef HUC | |
_ed_end: | |
ed_map | |
.endif | |
ed_end: | |
stz SPI_REG_ADDR + REG_KEY | |
ed_unmap | |
rts | |
;;--------------------------------------------------------------------- | |
; name : spi_recv | |
; desc : Read a byte from spi register. | |
; in : | |
; out : A value of spi register. | |
;;--------------------------------------------------------------------- | |
spi_recv: | |
lda #$ff | |
; Warning! do not add code here or you will break spi_recv! | |
;;--------------------------------------------------------------------- | |
; name : spi_send | |
; desc : Write a byte to spi register. | |
; in : A byte to write | |
; out : X value of spi register. | |
;;--------------------------------------------------------------------- | |
.ifdef HUC | |
_spi_send: | |
.endif | |
spi_send: | |
sta SPI_REG_ADDR + REG_SPI | |
SPI_BUSY_WAIT | |
lda SPI_REG_ADDR + REG_SPI | |
rts | |
;;--------------------------------------------------------------------- | |
; name : mmc_cmd | |
; desc : Write a byte to spi register. | |
; in : A command | |
; X crc | |
; _ed_buffer 4 bytes data | |
; out : X command result | |
;;--------------------------------------------------------------------- | |
.ifdef HUC | |
_mmc_cmd: | |
.endif | |
mmc_cmd: | |
phx | |
pha | |
SPI_BUSY_WAIT | |
bcc .no_timeout | |
; Wait timeout occured! | |
pla ; Restore stack. | |
pla | |
clx ; (todo) | |
rts | |
.no_timeout: | |
SPI_SS_OFF | |
SPI_SS_ON | |
jsr spi_recv | |
jsr spi_recv | |
; Send command | |
pla | |
jsr spi_send | |
; Send arguments | |
lda <_ed_buffer+3 | |
jsr spi_send | |
lda <_ed_buffer+2 | |
jsr spi_send | |
lda <_ed_buffer+1 | |
jsr spi_send | |
lda <_ed_buffer | |
jsr spi_send | |
; Send crc | |
pla | |
jsr spi_send | |
; Wait for response | |
jsr spi_recv | |
jsr spi_recv | |
cmp #$ff | |
bne .l1 | |
clx | |
cly | |
.l0: | |
jsr spi_recv | |
cmp #$ff | |
bne .l1 | |
inx | |
bne .l0 | |
iny | |
cpy #high(2048) | |
bne .l0 | |
.l1: | |
tax | |
SPI_SS_OFF | |
rts | |
;;--------------------------------------------------------------------- | |
; name : disk_init | |
; desc : Initialize disk. | |
; in : | |
; out : X error code | |
;;--------------------------------------------------------------------- | |
.ifdef HUC | |
_disk_init: | |
ed_map | |
jsr disk_init | |
ed_unmap | |
rts | |
.endif | |
disk_init: | |
stz <_ed_cardtype | |
lda #$ff | |
sta <_ed_addr | |
sta <_ed_addr+1 | |
sta <_ed_addr+2 | |
sta <_ed_addr+3 | |
SPI_SS_OFF | |
SPI_SPEED_OFF | |
ldx #32 | |
.l0: | |
jsr spi_recv | |
dex | |
bne .l0 | |
; Send reset command to turn SD card in SPI mode. | |
stz <_ed_buffer | |
stz <_ed_buffer+1 | |
stz <_ed_buffer+2 | |
stz <_ed_buffer+3 | |
lda #SD_CMD_GO_IDLE_STATE | |
ldx #$95 | |
jsr mmc_cmd | |
cpx #$01 | |
beq .l1 | |
lda #SD_CMD_GO_IDLE_STATE | |
ldx #$95 | |
jsr mmc_cmd | |
cpx #$01 | |
beq .l1 | |
ldx #(DISK_ERR_INIT) | |
rts | |
.l1: | |
lda #$aa | |
sta <_ed_buffer | |
lda #$01 | |
sta <_ed_buffer+1 | |
stz <_ed_buffer+2 | |
stz <_ed_buffer+3 | |
lda #SD_CMD_SEND_IF_COND | |
ldx #$87 | |
jsr mmc_cmd | |
jsr spi_recv | |
jsr spi_recv | |
jsr spi_recv | |
jsr spi_recv | |
jsr spi_recv | |
cmp #$ff | |
bne .l3 | |
ldx #(DISK_ERR_INIT + 1) | |
rts | |
.l3 | |
cpx #$05 | |
beq .l4 | |
smb1 <_ed_cardtype | |
.l4 | |
bbs1 <_ed_cardtype, .v2_card | |
jmp .std_card | |
.v2_card: | |
lda #low(16384) | |
sta <_cl | |
lda #high(16384) | |
sta <_ch | |
.l5: | |
lda #$ff | |
sta <_ed_buffer | |
sta <_ed_buffer+1 | |
sta <_ed_buffer+2 | |
sta <_ed_buffer+3 | |
lda #SD_CMD_APP_CMD | |
ldx #$95 | |
jsr mmc_cmd | |
cpx #$ff | |
bne .l6 | |
ldx #(DISK_ERR_INIT + 2) | |
rts | |
.l6: | |
cpx #$01 | |
bne .l8 | |
stz <_ed_buffer | |
stz <_ed_buffer+1 | |
lda #$30 | |
sta <_ed_buffer+2 | |
lda #$40 | |
sta <_ed_buffer+3 | |
lda #SD_CMD_41_TODO | |
ldx #$95 | |
jsr mmc_cmd | |
cpx #$ff | |
bne .l7 | |
ldx #(DISK_ERR_INIT + 3) | |
rts | |
.l7: | |
cpx #$00 | |
beq .l9 | |
.l8: | |
sec | |
lda <_cl | |
sbc #$01 | |
sta <_cl | |
lda <_ch | |
sbc #$00 | |
sta <_ch | |
bcs .l5 | |
lda #(DISK_ERR_INIT + 4) | |
rts | |
.l9: | |
stz <_ed_buffer | |
stz <_ed_buffer+1 | |
stz <_ed_buffer+2 | |
stz <_ed_buffer+3 | |
lda #SD_CMD_READ_OCR | |
ldx #$95 | |
jsr mmc_cmd | |
cpx #$ff | |
bne .l10 | |
ldx #(DISK_ERR_INIT + 5) | |
rts | |
.l10: | |
SPI_SS_ON | |
jsr spi_recv | |
pha | |
jsr spi_recv | |
jsr spi_recv | |
jsr spi_recv | |
pla | |
and #$40 | |
bne .l11 | |
smb0 <_ed_cardtype | |
.l11: | |
SPI_SPEED_ON | |
clx | |
rts | |
.std_card: | |
stz <_ed_buffer | |
stz <_ed_buffer+1 | |
stz <_ed_buffer+2 | |
stz <_ed_buffer+3 | |
lda #SD_CMD_APP_CMD | |
ldx #$95 | |
jsr mmc_cmd | |
cpx #$ff | |
bne .l12 | |
ldx #(DISK_ERR_INIT + 6) | |
rts | |
.l12: | |
lda #SD_CMD_41_TODO | |
ldx #$95 | |
jsr mmc_cmd | |
cpx #$ff | |
bne .l13 | |
ldx #(DISK_ERR_INIT + 7) | |
rts | |
.l13: | |
lda #low(16384) | |
sta <_cl | |
lda #high(16384) | |
sta <_ch | |
.l14: | |
cpx #$01 | |
bcs .l17 | |
stz <_ed_buffer | |
stz <_ed_buffer+1 | |
stz <_ed_buffer+2 | |
stz <_ed_buffer+3 | |
lda #SD_CMD_APP_CMD | |
ldx #$95 | |
jsr mmc_cmd | |
cpx #$ff | |
bne .l15 | |
ldx #(DISK_ERR_INIT + 8) | |
rts | |
.l15: | |
cpx #$01 | |
bne .l18 | |
lda #SD_CMD_41_TODO | |
ldx #$95 | |
jsr mmc_cmd | |
cpx #$ff | |
bne .l16 | |
ldx #(DISK_ERR_INIT + 9) | |
rts | |
.l16: | |
cpx #$00 | |
beq .l19 | |
.l17: | |
stz <_ed_buffer | |
stz <_ed_buffer+1 | |
stz <_ed_buffer+2 | |
stz <_ed_buffer+3 | |
lda #SD_CMD_WAKE_UP | |
ldx #$95 | |
jsr mmc_cmd | |
cpx #$00 | |
beq .l19 | |
.l18: | |
sec | |
lda <_cl | |
sbc #$01 | |
sta <_cl | |
lda <_ch | |
sbc #$00 | |
sta <_ch | |
bcs .l14 | |
lda #(DISK_ERR_INIT + 10) | |
rts | |
.l19: | |
SPI_SPEED_ON | |
clx | |
rts | |
;;--------------------------------------------------------------------- | |
; name : disk_close_rw | |
; desc : | |
; in : | |
; out : | |
;;--------------------------------------------------------------------- | |
.ifdef HUC | |
_disk_close_rw: | |
ed_map | |
jsr disk_close_rw | |
ed_unmap | |
rts | |
.endif | |
disk_close_rw: | |
stz <_ed_buffer | |
stz <_ed_buffer+1 | |
stz <_ed_buffer+2 | |
stz <_ed_buffer+3 | |
SPI_SS_OFF | |
lda #SD_STOP_TRANSMISSION | |
ldx #$95 | |
jsr mmc_cmd | |
rts | |
;;--------------------------------------------------------------------- | |
; name : spi_send_to_ram | |
; desc : Copy 512 bytes from sd to ram | |
; in : A:X destination | |
; out : X = 0 if everything was ok | |
;;--------------------------------------------------------------------- | |
.ifdef HUC | |
_spi_send_to_ram: | |
.endif | |
spi_send_to_ram: | |
__stw <ed_block_cp_dst | |
clx | |
cly | |
.wait_spi | |
lda #$ff | |
sta SPI_REG_ADDR | |
lda SPI_REG_ADDR | |
cmp #$fe | |
beq .begin_transfer | |
dey | |
bne .wait_spi | |
dex | |
bne .wait_spi | |
ldx #1 | |
rts | |
.begin_transfer | |
lda SPI_REG_ADDR + REG_SPI_CFG | |
ora #4 | |
sta SPI_REG_ADDR + REG_SPI_CFG | |
lda SPI_REG_ADDR | |
jsr ed_block_cp_inst | |
lda SPI_REG_ADDR | |
lda SPI_REG_ADDR + REG_SPI_CFG | |
and #3 | |
sta SPI_REG_ADDR + REG_SPI_CFG | |
clx | |
rts | |
;;--------------------------------------------------------------------- | |
; name : disk_open_read | |
; desc : Open SD card for reading. | |
; in : _ed_addr 32 bytes address (byte address on standard SD) | |
; (sector address on SD HC) | |
; out : X DISK_ERR_RD2 if an error occured, 0 for success | |
;;--------------------------------------------------------------------- | |
.ifdef HUC | |
_disk_open_read: | |
ed_map | |
jsr disk_open_read | |
ed_unmap | |
rts | |
.endif | |
disk_open_read: | |
bbr0 <_ed_cardtype, .l0 | |
; Standard SD cards take a byte address. | |
; Whereas SDHC cards take a sector (hence the mul by 512). | |
lda <_ed_addr+2 | |
sta <_ed_addr+3 | |
lda <_ed_addr+1 | |
sta <_ed_addr+2 | |
lda <_ed_addr | |
sta <_ed_addr+1 | |
stz <_ed_addr | |
asl <_ed_addr+1 | |
rol <_ed_addr+2 | |
rol <_ed_addr+3 | |
.l0: | |
jsr disk_close_rw | |
lda <_ed_addr+3 | |
sta <_ed_buffer+3 | |
lda <_ed_addr+2 | |
sta <_ed_buffer+2 | |
lda <_ed_addr+1 | |
sta <_ed_buffer+1 | |
lda <_ed_addr | |
sta <_ed_buffer | |
lda #SD_CMD_READ_MULTIPLE_BLOCK | |
ldx #$95 | |
jsr mmc_cmd | |
cpx #$00 | |
beq .l1 | |
ldx #DISK_ERR_RD2 | |
rts | |
.l1: | |
SPI_SS_ON; | |
clx | |
rts | |
;;--------------------------------------------------------------------- | |
; name : disk_read_sector | |
; desc : Read sector. | |
; in : | |
; out : X DISK_ERR_RD1 if an error occured, 0 for success | |
;;--------------------------------------------------------------------- | |
.ifdef HUC | |
_disk_read_sector: | |
ed_map | |
jsr disk_read_sector | |
ed_unmap | |
rts | |
.endif | |
disk_read_sector: | |
jsr spi_send_to_ram | |
cpx #$00 | |
beq .l0 | |
ldx #DISK_ERR_RD1 | |
rts | |
.l0: | |
inc <_ed_addr | |
bne .l1 | |
inc <_ed_addr+1 | |
bne .l1 | |
inc <_ed_addr+2 | |
bne .l1 | |
inc <_ed_addr+3 | |
bne .l1 | |
.l1: | |
clx | |
rts | |
;;--------------------------------------------------------------------- | |
; name : disk_open_write | |
; desc : Open SD card for writing. | |
; in : (todo) block | |
; (todo) pre erase count | |
; (todo) data pointer | |
; out : X DISK_ERR_WR1 if an error occured, 0 for success | |
;;--------------------------------------------------------------------- | |
.ifdef HUC | |
_disk_open_write: | |
.endif | |
disk_open_write: | |
; (todo) addr | |
jsr disk_close_rw | |
; (todo) SD_CMD_SET_WR_BLK_ERASE_COUNT, erase count | |
; (todo) SD_CMD_WRITE_MULTIPLE_BLOCK, block | |
rts |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment