Skip to content

Instantly share code, notes, and snippets.

@BlockoS
Last active October 7, 2015 23:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BlockoS/3240689 to your computer and use it in GitHub Desktop.
Save BlockoS/3240689 to your computer and use it in GitHub Desktop.
Turbo Everdrive SD card interface
;;---------------------------------------------------------------------
; 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