You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1204 lines
28 KiB

;
;=============================================================================
; SD/SDHC/SDXC CARD STORAGE DRIVER
;=============================================================================
;
;----------------------------------------------------------------------
; SD Signal Active JUHA N8 CSIO PPI UART DSD
; ------------ ------- ------- ------- ------- ------- ------- -------
; CS (DAT3) LO -> RTC:2 RTC:2 RTC:2 ~PC:4 MCR:3 OPR:2
; CLK HI -> RTC:1 RTC:1 N/A PC:1 ~MCR:2 OPR:1
; DI (CMD) HI -> RTC:0 RTC:0 N/A PC:0 ~MCR:0 OPR:0
; DO (DAT0) HI -> RTC:7 RTC:6 N/A PB:7 ~MSR:5 OPR:0
;----------------------------------------------------------------------
;
; CS = CHIP SELECT (AKA DAT3 FOR NON-SPI MODE)
; CLK = CLOCK
; DI = DATA IN (HOST -> CARD, AKA CMD FOR NON-SPI MODE)
; DO = DATA OUT (HOST <- CARD, AKA DAT0 FOR NON-SPI MODE)
;
; NOTES:
; 1) SIGNAL NAMES ARE FROM THE SD CARD SPEC AND ARE NAMED FROM THE
; PERSPECTIVE OF THE SD CARD:
; DI = DATA IN: HOST -> CARD = MOSI (MASTER OUT/SLAVE IN)
; DO = DATA OUT: HOST <- CARD = MISO (MASTER IN/SLAVE OUT)
;
; 2) THE QUIESCENT STATE OF THE OUTPUT SIGNALS (HOST -> CARD) IS:
; CS = HI (NOT SELECTED)
; CLK = LO
; DI = HI (ACTIVE IS THE NATURAL/DEFAULT STATE FOR DATA IN)
;
; 3) SPI MODE 0 IMPLEMENTATION IS USED (CPOL=0, CPHA=0)
; THE DATA MUST BE AVAILABLE BEFORE THE FIRST CLOCK SIGNAL RISING.
; THE CLOCK IDLE STATE IS ZERO. THE DATA ON MISO AND MOSI LINES
; MUST BE STABLE WHILE THE CLOCK IS HIGH AND CAN BE CHANGED WHEN
; THE CLOCK IS LOW. THE DATA IS CAPTURED ON THE CLOCK'S LOW-TO-HIGH
; TRANSITION AND PROPAGATED ON HIGH-TO-LOW CLOCK TRANSITION.
;
; 4) DI SHOULD BE LEFT HI (ACTIVE) WHENEVER UNUSED (FOR EXAMPLE, WHEN
; HOST IS RECEIVING DATA (HOST <- CARD)).
;
#IF (SDMODE == SDMODE_JUHA) ; JUHA MINI-BOARD
SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS)
SD_OPRREG .EQU RTC ; USES RTC LATCHES FOR OPERATION
SD_OPRDEF .EQU %00000001 ; QUIESCENT STATE???
SD_INPREG .EQU RTC ; INPUT REGISTER IS RTC
SD_CS .EQU %00000100 ; RTC:2 IS SELECT
SD_CLK .EQU %00000010 ; RTC:1 IS CLOCK
SD_DI .EQU %00000001 ; RTC:0 IS DATA IN (CARD <- CPU)
SD_DO .EQU %10000000 ; RTC:7 IS DATA OUT (CARD -> CPU)
#ENDIF
;
#IF (SDMODE == SDMODE_N8) ; UNMODIFIED N8-2511
SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS)
SD_OPRREG .EQU RTC ; USES RTC LATCHES FOR OPERATION
SD_OPRDEF .EQU %00000001 ; QUIESCENT STATE???
SD_INPREG .EQU RTC ; INPUT REGISTER IS RTC
SD_CS .EQU %00000100 ; RTC:2 IS SELECT
SD_CLK .EQU %00000010 ; RTC:1 IS CLOCK
SD_DI .EQU %00000001 ; RTC:0 IS DATA IN (CARD <- CPU)
SD_DO .EQU %01000000 ; RTC:6 IS DATA OUT (CARD -> CPU)
#ENDIF
;
#IF (SDMODE == SDMODE_CSIO) ; N8-2312
SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS)
SD_OPRREG .EQU RTC ; USES RTC LATCHES FOR OPERATION
SD_OPRDEF .EQU %00000000 ; QUIESCENT STATE
SD_CS .EQU %00000100 ; RTC:2 IS SELECT
SD_CNTR .EQU CPU_CNTR
SD_TRDR .EQU CPU_TRDR
#ENDIF
;
#IF (SDMODE == SDMODE_PPI) ; PPISD
SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS)
SD_PPIBASE .EQU PPIBASE ; BASE IO PORT FOR PPI
SD_PPIB .EQU PPIBASE + 1 ; PPI PORT B (INPUT: DOUT)
SD_PPIC .EQU PPIBASE + 2 ; PPI PORT C (OUTPUT: CS, CLK, DIN)
SD_PPIX .EQU PPIBASE + 3 ; PPI CONTROL PORT
SD_OPRREG .EQU SD_PPIC ; PPI PORT C IS OPR REG
SD_OPRDEF .EQU %00110001 ; CS HI, DI HI
SD_INPREG .EQU SD_PPIB ; INPUT REGISTER IS PPI PORT B
SD_CS .EQU %00010000 ; PPIC:4 IS SELECT
SD_CLK .EQU %00000010 ; PPIC:1 IS CLOCK
SD_DI .EQU %00000001 ; PPIC:0 IS DATA IN (CARD <- CPU)
SD_DO .EQU %10000000 ; PPIB:7 IS DATA OUT (CARD -> CPU)
#ENDIF
;
#IF (SDMODE == SDMODE_UART)
SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS)
SD_MCR .EQU SIO_MCR ; UART MCR PORT (OUTPUT: CS, CLK, DIN)
SD_MSR .EQU SIO_MSR ; UART MSR PORT (INPUT: DOUT)
SD_CS .EQU %00001000 ; UART MCR:3 IS SELECT
SD_CLK .EQU %00000100 ; UART MCR:2 IS CLOCK
SD_DI .EQU %00000001 ; UART MCR:0 IS DATA IN (CARD <- CPU)
SD_DO .EQU %00100000 ; UART MSR:5 IS DATA OUT (CARD -> CPU)
#ENDIF
;
#IF (SDMODE == SDMODE_DSD) ; DUAL SD
SD_UNITCNT .EQU 2 ; NUMBER OF PHYSICAL UNITS (SOCKETS)
SD_OPRREG .EQU $08 ; DEDICATED OPERATIONS REGISTER
SD_OPRDEF .EQU %00000001 ; QUIESCENT STATE
SD_INPREG .EQU SD_OPRREG ; INPUT REGISTER IS OPRREG
SD_SELREG .EQU SD_OPRREG + 1 ; DEDICATED SELECTION REGISTER
SD_SELDEF .EQU %00000000 ; SELECTION REGISTER DEFAULT
SD_CS .EQU %00000100 ; RTC:2 IS SELECT
SD_CLK .EQU %00000010 ; RTC:1 IS CLOCK
SD_DI .EQU %00000001 ; RTC:6 IS DATA IN (CARD <- CPU)
SD_DO .EQU %00000001 ; RTC:0 IS DATA OUT (CARD -> CPU)
#ENDIF
;
; SD CARD COMMANDS
;
SD_CMD0 .EQU $40 | 0 ; 0x40, GO_IDLE_STATE
SD_CMD1 .EQU $40 | 1 ; 0x41, SEND_OP_COND
SD_CMD8 .EQU $40 | 8 ; 0x48, SEND_IF_COND
SD_CMD9 .EQU $40 | 9 ; 0x49, SEND_CSD
SD_CMD10 .EQU $40 | 10 ; 0x4A, SEND_CID
SD_CMD16 .EQU $40 | 16 ; 0x50, SET_BLOCKLEN
SD_CMD17 .EQU $40 | 17 ; 0x51, READ_SINGLE_BLOCK
SD_CMD24 .EQU $40 | 24 ; 0x58, WRITE_BLOCK
SD_CMD55 .EQU $40 | 55 ; 0x77, APP_CMD
SD_CMD58 .EQU $40 | 58 ; 0x7A, READ_OCR
; SD APPLICATION SPECIFIC COMMANDS
SD_ACMD41 .EQU $40 | 41 ; 0x69, SD_APP_OP_COND
;
; SD CARD TYPE
;
SD_TYPEUNK .EQU 0 ; CARD TYPE UNKNOWN/UNDETERMINED
SD_TYPEMMC .EQU 1 ; MULTIMEDIA CARD (MMC STANDARD)
SD_TYPESDSC .EQU 2 ; SDSC CARD (V1)
SD_TYPESDHC .EQU 3 ; SDHC/SDXC CARD (V2)
;
; SD CARD STATUS (SD_STAT)
;
SD_STOK .EQU 0 ; OK
SD_STNOTRDY .EQU -1 ; NOT READY (INITIALIZATION PENDING)
SD_STRDYTO .EQU -2 ; TIMEOUT WAITING FOR CARD TO BE READY
SD_STINITTO .EQU -3 ; INITIALIZATOIN TIMEOUT
SD_STCMDTO .EQU -4 ; TIMEOUT WAITING FOR COMMAND RESPONSE
SD_STCMDERR .EQU -5 ; COMMAND ERROR OCCURRED (REF SD_RC)
SD_STDATAERR .EQU -6 ; DATA ERROR OCCURRED (REF SD_TOK)
SD_STDATATO .EQU -7 ; DATA TRANSFER TIMEOUT
SD_STCRCERR .EQU -8 ; CRC ERROR ON RECEIVED DATA PACKET
SD_STNOMEDIA .EQU -9 ; NO MEDIA IN CONNECTOR
SD_STWRTPROT .EQU -10 ; ATTEMPT TO WRITE TO WRITE PROTECTED MEDIA
;
;
;
SD_DISPATCH:
LD A,B ; GET REQUESTED FUNCTION
AND $0F
JP Z,SD_READ
DEC A
JP Z,SD_WRITE
DEC A
JP Z,SD_STATUS
DEC A
JP Z,SD_MEDIA
CALL PANIC
;
;
;
SD_MEDIA:
CALL SD_SELUNIT
;
; INITIALIZE THE SD CARD TO ACCOMMODATE HOT SWAPPING
CALL SD_INITCARD
LD A,MID_HD ; ASSUME SUCCESS
RET Z ; RETURN IF GOOD INIT
CALL SD_PRT
LD A,MID_NONE ; IF FAILURE, RETURN NO MEDIA
RET
;
SD_INIT:
PRTS("SD:$")
PRTS(" UNITS=$")
LD A,SD_UNITCNT
CALL PRTHEXBYTE
#IF (SDMODE == SDMODE_JUHA)
PRTS(" MODE=JUHA$")
PRTS(" IO=0x$")
LD A,SD_OPRREG
CALL PRTHEXBYTE
#ENDIF
;
#IF (SDMODE == SDMODE_N8)
PRTS(" MODE=N8$")
PRTS(" IO=0x$")
LD A,SD_OPRREG
CALL PRTHEXBYTE
#ENDIF
;
#IF (SDMODE == SDMODE_CSIO)
PRTS(" MODE=CSIO$")
#IF (SDCSIOFAST)
PRTS(" FAST$")
#ENDIF
PRTS(" IO=0x$")
LD A,SD_OPRREG
CALL PRTHEXBYTE
PRTS(" CNTR=0x$")
LD A,SD_CNTR
CALL PRTHEXBYTE
PRTS(" TRDR=0x$")
LD A,SD_TRDR
CALL PRTHEXBYTE
#ENDIF
;
#IF (SDMODE == SDMODE_PPI)
PRTS(" MODE=PPI$")
PRTS(" BASEIO=0x$")
LD A,SD_PPIBASE
CALL PRTHEXBYTE
#ENDIF
;
#IF (SDMODE == SDMODE_UART)
PRTS(" MODE=UART$")
PRTS(" MCR=0x$")
LD A,SD_MCR
CALL PRTHEXBYTE
PRTS(" MSR=0x$")
LD A,SD_MSR
CALL PRTHEXBYTE
#ENDIF
;
#IF (SDMODE == SDMODE_DSD)
PRTS(" MODE=DSD$")
PRTS(" OPR=0x$")
LD A,SD_OPRREG
CALL PRTHEXBYTE
PRTS(" SEL=0x$")
LD A,SD_SELREG
CALL PRTHEXBYTE
#ENDIF
;
LD A,SD_STNOTRDY
LD HL,SD_STATLST
LD (SD_STATPTR),HL
LD (HL),A
INC HL
LD (HL),A
LD A,SD_TYPEUNK
LD HL,SD_TYPELST
LD (SD_TYPEPTR),HL
LD (HL),A
INC HL
LD (HL),A
RET
;
SD_STATUS:
CALL SD_SELUNIT
LD HL,(SD_STATPTR)
LD A,(HL)
OR A
RET
;
SD_READ:
CALL SD_SELUNIT
CALL SD_RDSEC
JR SD_PRT
;
SD_WRITE:
CALL SD_SELUNIT
CALL SD_CHKWP
CALL NZ,SD_WRTPROT
CALL Z,SD_WRSEC
JR SD_PRT
;
SD_PRT:
#IF (SDTRACE >= 1)
RET Z
PUSH AF
CALL SD_PRTPREFIX
CALL PC_SPACE
CALL SD_PRTSTAT
POP AF
#ENDIF
RET
;
;=============================================================================
; SD HARDWARE INTERFACE ROUTINES
;=============================================================================
;
; TAKE ANY ACTIONS REQUIRED TO SELECT DESIRED PHYSICAL UNIT
;
SD_SELUNIT:
LD A,C
AND 0FH ; ISOLATE THE UNIT NIBBLE
CP SD_UNITCNT ; CHECK VALIDITY (EXCEED UNIT COUNT?)
CALL NC,PANIC ; PANIC ON INVALID VALUE
LD (SD_UNIT),A ; SAVE CURRENT UNIT NUM
#IF (SDMODE == SDMODE_DSD)
; SELECT REQUESTED UNIT
OUT (SD_SELREG),A ; ACTUALLY SELECT THE CARD
#ENDIF
LD HL,SD_STATLST ; POINT TO START OF STATUS LIST
LD D,0 ; SETUP DE TO HAVE OFFSET
LD E,A ; FOR CURRENT UNIT
ADD HL,DE ; APPLY THE OFFSET
LD (SD_STATPTR),HL ; SAVE IT
LD HL,SD_TYPELST ; POINT TO START OF CARD TYPE LIST
ADD HL,DE ; APPLY THE OFFSET
LD (SD_TYPEPTR),HL ; SAVE IT
RET
;
; PERFORM HARDWARE SPECIFIC INITIALIZATION
;
SD_SETUP:
;
#IF ((SDMODE == SDMODE_JUHA) | (SDMODE == SDMODE_N8))
LD A,SD_OPRDEF
LD (SD_OPRVAL),A
OUT (SD_OPRREG),A
#ENDIF
;
#IF (SDMODE == SDMODE_CSIO)
; CSIO SETUP
; LD A,2 ; 18MHz/20 <= 400kHz
LD A,6 ; ???
OUT0 (SD_CNTR),A
LD A,SD_OPRDEF
LD (SD_OPRVAL),A
OUT (SD_OPRREG),A
#ENDIF
;
#IF (SDMODE == SDMODE_PPI)
LD A,82H ; PPI PORT A=OUT, B=IN, C=OUT
OUT (PPIX),A
;LD A,30H ; PC4,5 /CS HIGH
LD A,SD_OPRDEF
LD (SD_OPRVAL),A
OUT (SD_OPRREG),A
#ENDIF
;
#IF (SDMODE == SDMODE_UART)
IN A,(SD_MCR)
OR SD_CS ; DEASSERT = HI = 1
AND ~SD_DI ; ASSERT DIN = LO = 0 (INVERTED)
AND ~SD_CLK ; DEASSERT CLK = HI = 1 (INVERTED)
OUT (SD_MCR),A
#ENDIF
;
#IF (SDMODE == SDMODE_DSD)
LD A,SD_OPRDEF
LD (SD_OPRVAL),A
OUT (SD_OPRREG),A
;
IN A,(SD_OPRREG)
BIT 5,A ; CARD DETECT
JP Z,SD_NOMEDIA ; NO MEDIA DETECTED
#ENDIF
;
XOR A
RET
;
; SELECT CARD
;
SD_SELECT:
LD A,(SD_OPRVAL)
#IF (SDMODE == SDMODE_PPI)
AND ~SD_CS ; SET SD_CS (CHIP SELECT)
#ELSE
OR SD_CS ; SET SD_CS (CHIP SELECT)
#ENDIF
LD (SD_OPRVAL),A
OUT (SD_OPRREG),A
RET
;
; DESELECT CARD
;
SD_DESELECT:
LD A,(SD_OPRVAL)
#IF (SDMODE == SDMODE_PPI)
OR SD_CS ; RESET SD_CS (CHIP SELECT)
#ELSE
AND ~SD_CS ; RESET SD_CS (CHIP SELECT)
#ENDIF
LD (SD_OPRVAL),A
OUT (SD_OPRREG),A
RET
;
; CHECK FOR WRITE PROTECT (NZ = WRITE PROTECTED)
;
SD_CHKWP:
XOR A ; ASSUME NOT WRITE PROTECTED
#IF (SDMODE == SDMODE_DSD)
IN A,(SD_OPRREG)
BIT 4,A
#ENDIF
RET
;
;
;
#IF (SDMODE == SDMODE_CSIO)
SD_WAITTX: ; WAIT FOR TX EMPTY
IN0 A,(SD_CNTR) ; get CSIO status
BIT 4,A ; Tx empty?
JR NZ,SD_WAITTX
RET
;
;
;
SD_WAITRX:
IN0 A,(SD_CNTR) ; wait for receiver to finish
BIT 5,A
JR NZ,SD_WAITRX
RET
;
;
;
MIRROR: ; MSB<-->LSB mirror bits in A, result in C
#IF (!SDCSIOFAST) ; slow speed, least code space
LD B,8 ; bit counter
MIRROR1:
RLA ; rotate bit 7 into carry
RR C ; rotate carry into result
DJNZ MIRROR1 ; do all 8 bits
RET
#ELSE ; fastest but uses most code space
LD BC,MIRTAB ; 256 byte mirror table
ADD A,C ; add offset
LD C,A
JR NC,MIRROR2
INC B
MIRROR2:
LD A,(BC) ; get result
LD C,A ; return result in C
RET
#ENDIF
;
MIRTAB: .DB 00H, 80H, 40H, 0C0H, 20H, 0A0H, 60H, 0E0H, 10H, 90H, 50H, 0D0H, 30H, 0B0H, 70H, 0F0H
.DB 08H, 88H, 48H, 0C8H, 28H, 0A8H, 68H, 0E8H, 18H, 98H, 58H, 0D8H, 38H, 0B8H, 78H, 0F8H
.DB 04H, 84H, 44H, 0C4H, 24H, 0A4H, 64H, 0E4H, 14H, 94H, 54H, 0D4H, 34H, 0B4H, 74H, 0F4H
.DB 0CH, 8CH, 4CH, 0CCH, 2CH, 0ACH, 6CH, 0ECH, 1CH, 9CH, 5CH, 0DCH, 3CH, 0BCH, 7CH, 0FCH
.DB 02H, 82H, 42H, 0C2H, 22H, 0A2H, 62H, 0E2H, 12H, 92H, 52H, 0D2H, 32H, 0B2H, 72H, 0F2H
.DB 0AH, 8AH, 4AH, 0CAH, 2AH, 0AAH, 6AH, 0EAH, 1AH, 9AH, 5AH, 0DAH, 3AH, 0BAH, 7AH, 0FAH
.DB 06H, 86H, 46H, 0C6H, 26H, 0A6H, 66H, 0E6H, 16H, 96H, 56H, 0D6H, 36H, 0B6H, 76H, 0F6H
.DB 0EH, 8EH, 4EH, 0CEH, 2EH, 0AEH, 6EH, 0EEH, 1EH, 9EH, 5EH, 0DEH, 3EH, 0BEH, 7EH, 0FEH
.DB 01H, 81H, 41H, 0C1H, 21H, 0A1H, 61H, 0E1H, 11H, 91H, 51H, 0D1H, 31H, 0B1H, 71H, 0F1H
.DB 09H, 89H, 49H, 0C9H, 29H, 0A9H, 69H, 0E9H, 19H, 99H, 59H, 0D9H, 39H, 0B9H, 79H, 0F9H
.DB 05H, 85H, 45H, 0C5H, 25H, 0A5H, 65H, 0E5H, 15H, 95H, 55H, 0D5H, 35H, 0B5H, 75H, 0F5H
.DB 0DH, 8DH, 4DH, 0CDH, 2DH, 0ADH, 6DH, 0EDH, 1DH, 9DH, 5DH, 0DDH, 3DH, 0BDH, 7DH, 0FDH
.DB 03H, 83H, 43H, 0C3H, 23H, 0A3H, 63H, 0E3H, 13H, 93H, 53H, 0D3H, 33H, 0B3H, 73H, 0F3H
.DB 0BH, 8BH, 4BH, 0CBH, 2BH, 0ABH, 6BH, 0EBH, 1BH, 9BH, 5BH, 0DBH, 3BH, 0BBH, 7BH, 0FBH
.DB 07H, 87H, 47H, 0C7H, 27H, 0A7H, 67H, 0E7H, 17H, 97H, 57H, 0D7H, 37H, 0B7H, 77H, 0F7H
.DB 0FH, 8FH, 4FH, 0CFH, 2FH, 0AFH, 6FH, 0EFH, 1FH, 9FH, 5FH, 0DFH, 3FH, 0BFH, 7FH, 0FFH
#ENDIF
;
; SEND ONE BYTE
;
SD_PUT:
#IF (SDMODE == SDMODE_CSIO)
CALL MIRROR ; MSB<-->LSB mirror bits, result in C
CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING
OUT0 (SD_TRDR),C ; put byte in buffer
IN0 A,(SD_CNTR)
SET 4,A ; set transmit enable
OUT0 (SD_CNTR),A
#ELSE
LD C,A ; C=BYTE TO SEND
LD B,8 ; SEND 8 BITS (LOOP 8 TIMES)
LD A,(SD_OPRVAL) ; LOAD CURRENT OPR VALUE
SD_PUT1:
RRA ; PREPARE TO GET DATA BIT FROM CF
RL C ; ROTATE NEXT BIT FROM C INTO CF
RLA ; ROTATE CF INTO A:0, SD_DO is OPR:0
OUT (SD_OPRREG),A ; ASSERT DATA BIT
XOR SD_CLK ; TOGGLE CLOCK
OUT (SD_OPRREG),A ; UPDATE CLOCK AND ASSERT DATA BIT
XOR SD_CLK ; TOGGLE CLOCK
OUT (SD_OPRREG),A ; UPDATE CLOCK
DJNZ SD_PUT1 ; REPEAT FOR ALL 8 BITS
LD A,(SD_OPRVAL) ; LOAD CURRENT OPR VALUE
OUT (SD_OPRREG),A ; LEAVE WITH CLOCK LOW
#ENDIF
RET ; let it do the rest
;
; RECEIVE ONE BYTE
;
SD_GET:
#IF (SDMODE == SDMODE_CSIO)
CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING
IN0 A,(CPU_CNTR) ; get CSIO status
SET 5,A ; start receiver
OUT0 (CPU_CNTR),A
CALL SD_WAITRX
IN0 A,(CPU_TRDR) ; get received byte
CALL MIRROR ; MSB<-->LSB mirror bits
LD A,C ; keep result
#ELSE
LD B,8 ; RECEIVE 8 BITS (LOOP 8 TIMES)
LD A,(SD_OPRVAL) ; LOAD CURRENT OPR VALUE
SD_GET1:
XOR SD_CLK ; TOGGLE CLOCK
OUT (SD_OPRREG),A ; UPDATE CLOCK
IN A,(SD_INPREG) ; READ THE DATA WHILE CLOCK IS ACTIVE
#IF ((SDMODE == SDMODE_JUHA) | (SDMODE == SDMODE_PPI))
RLA ; ROTATE INP:7 INTO CF
#ENDIF
#IF (SDMODE == SDMODE_N8)
RLA ; ROTATE INP:6 INTO CF
RLA ; "
#ENDIF
#IF (SDMODE == SDMODE_DSD)
RRA ; ROTATE INP:0 INTO CF
#ENDIF
RL C ; ROTATE CF INTO C:0
LD A,(SD_OPRVAL) ; BACK TO INITIAL VALUES (TOGGLE CLOCK)
OUT (SD_OPRREG),A ; DO IT
DJNZ SD_GET1 ; REPEAT FOR ALL 8 BITS
LD A,C ; GET BYTE RECEIVED INTO A
#ENDIF
RET
;
;==================================================================================================
; SD DISK DRIVER PROTOCOL IMPLEMENTATION
;==================================================================================================
;
; SELECT CARD AND WAIT FOR IT TO BE READY ($FF)
;
SD_WAITRDY:
CALL SD_SELECT ; SELECT CARD
LD DE,0 ; LOOP MAX (TIMEOUT)
SD_WAITRDY1:
CALL SD_GET
INC A ; $FF -> $00
RET Z ; IF READY, RETURN
DEC DE
LD A,D
OR E
JR NZ,SD_WAITRDY1 ; KEEP TRYING UNTIL TIMEOUT
XOR A ; ZERO ACCUM
DEC A ; ACCUM := $FF TO SIGNAL ERROR
RET ; TIMEOUT
;
; COMPLETE A TRANSACTION - PRESERVE AF
;
SD_DONE:
PUSH AF
CALL SD_DESELECT
LD A,$FF
CALL SD_PUT
POP AF
RET
;
; SD_GETDATA
;
SD_GETDATA:
PUSH BC ; SAVE LENGTH TO RECEIVE
LD DE,$7FFF ; LOOP MAX (TIMEOUT)
SD_GETDATA1:
CALL SD_GET
CP $FF ; WANT BYTE != $FF
JR NZ,SD_GETDATA2 ; NOT $FF, MOVE ON
DEC DE
BIT 7,D
JR Z,SD_GETDATA1 ; KEEP TRYING UNTIL TIMEOUT
SD_GETDATA2:
LD (SD_TOK),A
POP DE ; RESTORE LENGTH TO RECEIVE
CP $FE ; PACKET START?
JR NZ,SD_GETDATA4 ; NOPE, ABORT, A HAS ERROR CODE
LD HL,(DIOBUF) ; RECEIVE BUFFER
SD_GETDATA3:
CALL SD_GET ; GET NEXT BYTE
LD (HL),A ; SAVE IT
INC HL
DEC DE
LD A,D
OR E
JR NZ,SD_GETDATA3 ; LOOP FOR ALL BYTES
CALL SD_GET ; DISCARD CRC BYTE 1
CALL SD_GET ; DISCARD CRC BYTE 2
XOR A ; RESULT IS ZERO
SD_GETDATA4:
RET
;
; SD_PUTDATA
;
SD_PUTDATA:
PUSH BC ; SAVE LENGTH TO SEND
LD A,$FE ; PACKET START
CALL SD_PUT ; SEND IT
POP DE ; RESTORE LENGTH TO SEND
LD HL,(DIOBUF) ; RECEIVE BUFFER
SD_PUTDATA1:
LD A,(HL) ; GET NEXT BYTE TO SEND
CALL SD_PUT ; SEND IF
INC HL
DEC DE
LD A,D
OR E
JR NZ,SD_PUTDATA1 ; LOOP FOR ALL BYTES
LD A,$FF ; DUMMY CRC BYTE
CALL SD_PUT
LD A,$FF ; DUMMY CRC BYTE
CALL SD_PUT
LD DE,$7FFF ; LOOP MAX (TIMEOUT)
SD_PUTDATA2:
CALL SD_GET
CP $FF ; WANT BYTE != $FF
JR NZ,SD_PUTDATA3 ; NOT $FF, MOVE ON
DEC DE
BIT 7,D
JR Z,SD_PUTDATA2 ; KEEP TRYING UNTIL TIMEOUT
SD_PUTDATA3:
AND $1F
LD (SD_TOK),A
CP $05
RET NZ
XOR A
RET
;
; SETUP COMMAND BUFFER
;
SD_SETCMD0: ; NO PARMS
LD HL,SD_CMDBUF
LD (HL),A
INC HL
XOR A
LD (HL),A
INC HL
LD (HL),A
INC HL
LD (HL),A
INC HL
LD (HL),A
INC HL
LD A,$FF
LD (HL),A
RET
;
SD_SETCMDP: ; W/ PARMS IN BC & DE
CALL SD_SETCMD0
LD HL,SD_CMDP0
LD (HL),B
INC HL
LD (HL),C
INC HL
LD (HL),D
INC HL
LD (HL),E
RET
;
; EXECUTE A SD CARD COMMAND
;
SD_EXEC:
CALL SD_WAITRDY
JP NZ,SD_ERRRDYTO ; RETURN VIA READY TIMEOUT HANDLER
XOR A
LD (SD_RC),A
LD (SD_TOK),A
LD HL,SD_CMDBUF
LD E,6 ; COMMANDS ARE 6 BYTES
SD_EXEC1:
#IF (SDMODE == SDMODE_CSIO)
CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING
IN A,(SD_OPRREG)
OR SD_CS ; SET CS
OUT (SD_OPRREG),A
#ENDIF
#IF (SDMODE == SDMODE_UART)
IN A,(SIO_MCR)
AND ~SD_CS ; ASSERT = LO = 0
OUT (SIO_MCR),A
#ENDIF
LD A,(HL)
CALL SD_PUT
INC HL
DEC E
JR NZ,SD_EXEC1
LD DE,$100 ; LOOP MAX (TIMEOUT)
SD_EXEC2:
CALL SD_GET
OR A ; SET FLAGS
JP P,SD_EXEC3 ; IF HIGH BIT IS 0, WE HAVE RESULT
DEC DE
BIT 7,D
JR Z,SD_EXEC2
JP SD_ERRCMDTO
SD_EXEC3:
LD (SD_RC),A
#IF (SDTRACE >= 2)
CALL SD_PRTTRN
#ENDIF
#IF (DSKYENABLE)
CALL SD_DSKY
#ENDIF
OR A
RET
;
SD_EXECCMD0: ; EXEC COMMAND, NO PARMS
CALL SD_SETCMD0
JR SD_EXEC
;
SD_EXECCMDP: ; EXEC CMD W/ PARMS IN BC/DE
CALL SD_SETCMDP
JR SD_EXEC
;
; PUT CARD IN IDLE STATE
;
SD_GOIDLE:
;CALL SD_DONE ; SEEMS TO HELP SOME CARDS...
; SMALL DELAY HERE HELPS SOME CARDS
LD DE,200 ; 5 MILISECONDS
CALL VDELAY
; PUT CARD IN IDLE STATE
LD A,SD_CMD0 ; CMD0 = ENTER IDLE STATE
CALL SD_SETCMD0
LD A,$95
LD (SD_CMDBUF+5),A ; SET CRC=$95
CALL SD_EXEC ; EXEC CMD
CALL SD_DONE ; SIGNAL COMMAND COMPLETE
JP P,SD_GOIDLE1 ; VALID RESULT, CHECK IT
CALL SD_EXEC ; 2ND TRY
CALL SD_DONE ; SIGNAL COMMAND COMPLETE
RET M ; COMMAND FAILED
;
SD_GOIDLE1: ; COMMAND OK, CHECK FOR EXPECTED RESULT
DEC A ; MAP EXPECTED $01 -> $00
RET Z ; IF $00, ALL GOOD, RETURN
JP SD_ERRCMD ; OTHERWISE, HANDLE COMMAND ERROR
;
; INIT CARD
;
SD_INITCARD:
CALL SD_SETUP ; DO HARDWARE SETUP/INIT
RET NZ
;
; WAKE UP THE CARD, KEEP DIN HI (ASSERTED) AND /CS HI (DEASSERTED)
LD B,$10 ; MIN 74 CLOCKS REQUIRED, WE USE 128 ($10 * 8)
SD_INITCARD000:
LD A,$FF
PUSH BC
CALL SD_PUT
POP BC
DJNZ SD_INITCARD000
;CALL SD_SELECT
; PUT CARD IN IDLE STATE
CALL SD_GOIDLE
RET NZ ; FAILED
SD_INITCARD00:
LD A,SD_TYPESDSC ; ASSUME SDSC CARD TYPE
LD HL,(SD_TYPEPTR) ; LOAD THE CARD TYPE ADDRESS
LD (HL),A ; SAVE IT
; CMD8 IS REQUIRED FOR V2 CARDS. FAILURE HERE IS OK AND
; JUST MEANS THAT IT IS A V1.X CARD
LD A,SD_CMD8
LD BC,0
LD D,1 ; VHS=1, 2.7-3.6V
LD E,$AA ; CHECK PATTERN
CALL SD_SETCMDP
LD A,$87
LD (SD_CMDBUF+5),A ; SET CRC=$87
CALL SD_EXEC ; EXEC CMD
CALL M,SD_DONE ; CLOSE COMMAND IF ERROR
RET M ; ABORT DUE TO PROCESSING ERROR
AND ~$01 ; IGNORE BIT 0 (IDLE)
JR NZ,SD_INITCARD0 ; CMD RESULT ERR, SKIP AHEAD
; CMD8 WORKED, NEED TO CONSUME CMD8 RESPONSE BYTES (4)
CALL SD_GET
CALL SD_GET
CALL SD_GET
CALL SD_GET
SD_INITCARD0:
CALL SD_DONE
LD A,0
LD (SD_LCNT),A
SD_INITCARD1:
; CALL SD_APP_OP_COND UNTIL CARD IS READY (NOT IDLE)
LD DE,200 ; 5 MILLISECONDS
CALL VDELAY
LD A,SD_CMD55 ; APP CMD IS NEXT
CALL SD_EXECCMD0
CALL SD_DONE
RET M ; ABORT ON PROCESSING ERROR
AND ~$01 ; ONLY 0 (OK) OR 1 (IDLE) ARE OK
JP NZ,SD_ERRCMD
LD A,SD_ACMD41 ; SD_APP_OP_COND
LD BC,$4000 ; INDICATE WE SUPPORT HC
LD DE,$0000
CALL SD_EXECCMDP
CALL SD_DONE
RET M ; ABORT ON PROCESSING ERROR
CP $00 ; INIT DONE?
JR Z,SD_INITCARD2 ; YUP, MOVE ON
CP $01 ; IDLE?
JP NZ,SD_ERRCMD ; NOPE, MUST BE CMD ERROR, ABORT
LD HL,SD_LCNT ; POINT TO LOOP COUNTER
DEC (HL) ; DECREMENT LOOP COUNTER
JR NZ,SD_INITCARD1 ; LOOP UNTIL COUNTER EXHAUSTED
LD A,$FF ; SIGNAL TIMEOUT
OR A
JP SD_ERRINITTO
SD_INITCARD2:
; CMD58 RETURNS THE 32 BIT OCR REGISTER, WE WANT TO CHECK
; BIT 30, IF SET THIS IS SDHC/XC CARD
LD A,SD_CMD58
CALL SD_EXECCMD0
CALL NZ,SD_DONE
RET M ; ABORT ON PROCESSING ERROR
JP NZ,SD_ERRCMD
; CMD58 WORKED, GET OCR DATA AND SET CARD TYPE
CALL SD_GET ; BITS 31-24
AND $40 ; ISOLATE BIT 30 (CCS)
JR Z,SD_INITCARD21 ; NOT HC/XC, BYPASS
LD HL,(SD_TYPEPTR) ; LOAD THE CARD TYPE ADDRESS
LD A,SD_TYPESDHC ; CARD TYPE = SDHC
LD (HL),A ; SAVE IT
SD_INITCARD21:
CALL SD_GET ; BITS 23-16, DISCARD
CALL SD_GET ; BITS 15-8, DISCARD
CALL SD_GET ; BITS 7-0, DISCARD
CALL SD_DONE
; SET OUR DESIRED BLOCK LENGTH (512 BYTES)
LD A,SD_CMD16 ; SET_BLOCK_LEN
LD BC,0
LD DE,512
CALL SD_EXECCMDP
CALL SD_DONE
RET M ; ABORT ON PROCESSING ERROR
JP NZ,SD_ERRCMD
#IF (SDTRACE >= 2)
CALL NEWLINE
LD DE,SDSTR_SDTYPE
CALL WRITESTR
LD HL,(SD_TYPEPTR)
LD A,(HL)
CALL PRTHEXBYTE
#ENDIF
#IF (SDMODE == SDMODE_CSIO)
CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING
XOR A ; NOW SET CSIO PORT TO FULL SPEED
OUT (CPU_CNTR),A
#ENDIF
XOR A ; A = 0 (STATUS = OK)
LD HL,(SD_STATPTR) ; LOAD STATUS ADDRESS
LD (HL),A ; SAVE IT
RET ; RETURN WITH A=0, AND Z SET
;;
;; GET AND PRINT CSD, CID
;;
;SD_CARDINFO:
; LD A,SD_CMD9 ; SEND_CSD
; CALL SD_EXECCMD0
; CALL SD_DONE
; JP NZ,SD_ERRCMD ; ABORT IF PROBLEM
; LD BC,16 ; 16 BYTES OF CSD
; CALL SD_GETDATA
; CALL SD_DONE
;
; LD DE,SDSTR_CSD
; CALL WRITESTR
; LD DE,SECBUF
; LD A,16
; CALL PRTHEXBUF
;
; LD A,SD_CMD10 ; SEND_CID
; CALL SD_EXECCMD0
; CALL SD_DONE
; JP NZ,SD_ERRCMD ; ABORT IF PROBLEM
; LD BC,16 ; 16 BYTES OF CID
; CALL SD_GETDATA
; CALL SD_DONE
;
; LD DE,SDSTR_CID
; CALL WRITESTR
; LD DE,SECBUF
; LD A,16
; CALL PRTHEXBUF
;
; RET
;
; CHECK THE SD CARD, ATTEMPT TO REINITIALIZE IF NEEDED
;
SD_CHKCARD:
LD HL,(SD_STATPTR) ; LOAD STATUS ADDRESS
LD A,(HL) ; GET STATUS
OR A ; SET FLAGS
CALL NZ,SD_INITCARD ; INIT CARD IF NOT READY
RET ; RETURN WITH STATUS IN A
SD_RDSEC:
CALL SD_CHKCARD ; CHECK / REINIT CARD AS NEEDED
RET NZ
CALL SD_SETADDR ; SETUP BLOCK ADDRESS
LD A,SD_CMD17 ; READ_SINGLE_BLOCK
CALL SD_EXECCMDP ; EXEC CMD WITH BLOCK ADDRESS AS PARM
CALL NZ,SD_DONE ; TRANSACTION DONE IF ERROR OCCURRED
JP NZ,SD_ERRCMD ; ABORT ON ERROR
LD BC,512 ; LENGTH TO READ
CALL SD_GETDATA ; GET THE BLOCK
CALL SD_DONE
JP NZ,SD_ERRDATA ; DATA XFER ERROR
RET
;
; WRITE ONE SECTOR
;
SD_WRSEC:
CALL SD_CHKCARD ; CHECK / REINIT CARD AS NEEDED
RET NZ
CALL SD_SETADDR ; SETUP BLOCK ADDRESS
LD A,SD_CMD24 ; WRITE_BLOCK
CALL SD_EXECCMDP ; EXEC CMD WITH BLOCK ADDRESS AS PARM
CALL NZ,SD_DONE ; TRANSACTION DONE IF ERROR OCCURRED
JP NZ,SD_ERRCMD ; ABORT ON ERROR
LD BC,512 ; LENGTH TO WRITE
CALL SD_PUTDATA ; PUT THE BLOCK
CALL SD_DONE
JP NZ,SD_ERRDATA ; DATA XFER ERROR
RET
;
;
;
SD_SETADDR:
LD HL,(SD_TYPEPTR)
LD A,(HL)
CP SD_TYPESDSC
JR Z,SD_SETADDRSDSC
CP SD_TYPESDHC
JR Z,SD_SETADDRSDHC
CALL PANIC
SD_SETADDRSDSC:
LD HL,(HSTSEC)
LD E,0
LD D,L
LD HL,(HSTTRK)
LD C,L
LD B,H
XOR A
RL D
RL C
RL B
RET
SD_SETADDRSDHC:
LD A,(HSTSEC) ; GET SECTOR (LSB ONLY)
LD E,A ; PUT IN E
LD HL,(HSTTRK) ; GET TRACK
LD D,L ; TRACK LSB -> D
LD C,H ; TRACK MSB -> C
LD B,0 ; B ALWAYS ZERO
RET
;
; HANDLE READY TIMEOUT ERROR
;
SD_ERRRDYTO:
LD A,SD_STRDYTO
JR SD_CARDERR
;
SD_ERRINITTO:
LD A,SD_STINITTO
JR SD_CARDERR
;
SD_ERRCMDTO:
LD A,SD_STCMDTO
JR SD_CARDERR
;
SD_ERRCMD:
LD A,SD_STCMDERR
JR SD_CARDERR
;
SD_ERRDATA:
LD A,SD_STDATAERR
JR SD_CARDERR
;
SD_ERRDATATO:
LD A,SD_STDATATO
JR SD_CARDERR
;
SD_ERRCRC:
LD A,SD_STCRCERR
JR SD_CARDERR
;
SD_NOMEDIA:
LD A,SD_STNOMEDIA
JR SD_CARDERR
;
SD_WRTPROT:
LD A,SD_STWRTPROT
JR SD_CARDERR
;
; GENERIC ERROR HANDLER
;
SD_CARDERR:
PUSH HL ; IS THIS NEEDED?
LD HL,(SD_STATPTR)
LD (HL),A
POP HL ; IS THIS NEEDED?
#IF (SDTRACE >= 2)
CALL NEWLINE
PUSH AF ; IS THIS NEEDED?
PRTC('<')
CALL SD_PRTSTAT
PRTC('>')
POP AF ; IS THIS NEEDED?
#ENDIF
OR A
RET
;
; PRINT DIAGNONSTIC PREFIX
;
SD_PRTPREFIX:
CALL NEWLINE
LD DE,SDSTR_PREFIX
CALL WRITESTR
PUSH AF
LD A,(SD_UNIT)
ADD A,'0'
CALL COUT
POP AF
CALL PC_COLON
RET
;
; PRINT STATUS STRING
;
SD_PRTSTAT:
PUSH HL ; IS THIS NEEDED?
LD HL,(SD_STATPTR)
LD A,(HL)
POP HL ; IS THIS NEEDED?
OR A
LD DE,SDSTR_STOK
JR Z,SD_PRTSTAT1
INC A
LD DE,SDSTR_STNOTRDY
JR Z,SD_PRTSTAT1
INC A
LD DE,SDSTR_STRDYTO
JR Z,SD_PRTSTAT1
INC A
LD DE,SDSTR_STINITTO
JR Z,SD_PRTSTAT1
INC A
LD DE,SDSTR_STCMDTO
JR Z,SD_PRTSTAT1
INC A
LD DE,SDSTR_STCMDERR
JR Z,SD_PRTSTAT1
INC A
LD DE,SDSTR_STDATAERR
JR Z,SD_PRTSTAT1
INC A
LD DE,SDSTR_STDATATO
JR Z,SD_PRTSTAT1
INC A
LD DE,SDSTR_STCRCERR
JR Z,SD_PRTSTAT1
INC A
LD DE,SDSTR_STNOMEDIA
JR Z,SD_PRTSTAT1
INC A
LD DE,SDSTR_STWRTPROT
JR Z,SD_PRTSTAT1
LD DE,SDSTR_STUNK
;
SD_PRTSTAT1:
CALL WRITESTR
PUSH HL ; IS THIS NEEDED?
LD HL,(SD_STATPTR)
LD A,(HL)
POP HL ; IS THIS NEEDED?
CP SD_STCMDERR
JR Z,SD_PRTCMDERR
CP SD_STDATAERR
JR Z,SD_PRTDATAERR
RET
;
SD_PRTCMDERR:
LD A,(SD_RC)
JR SD_PRTCODE
;
SD_PRTDATAERR:
LD A,(SD_TOK)
JR SD_PRTCODE
;
SD_PRTCODE:
CALL PC_LPAREN
PUSH AF
LD A,(SD_CMD)
CALL PRTHEXBYTE
LD DE,SDSTR_ARROW
CALL WRITESTR
POP AF
CALL PRTHEXBYTE
CALL PC_RPAREN
RET
;
; PRT COMMAND TRACE
;
SD_PRTTRN:
PUSH AF
CALL SD_PRTPREFIX
LD DE,SD_CMDBUF
LD A,6
CALL PRTHEXBUF
LD DE,SDSTR_ARROW
CALL WRITESTR
LD DE,SDSTR_RC
CALL WRITESTR
LD A,(SD_RC)
CALL PRTHEXBYTE
CALL PC_SPACE
LD DE,SDSTR_TOK
CALL WRITESTR
LD A,(SD_TOK)
CALL PRTHEXBYTE
POP AF
RET
;
; DISPLAY COMMAND, LOW ORDER WORD OF PARMS, AND RC
;
#IF (DSKYENABLE)
SD_DSKY:
PUSH AF
LD HL,DSKY_HEXBUF
LD A,(SD_CMD)
LD (HL),A
INC HL
LD A,(SD_CMDP2)
LD (HL),A
INC HL
LD A,(SD_CMDP3)
LD (HL),A
INC HL
LD A,(SD_RC)
CALL DSKY_HEXOUT
POP AF
RET
#ENDIF
;
;
;
SDSTR_PREFIX .TEXT "SD$"
SDSTR_ARROW .TEXT " -> $"
SDSTR_RC .TEXT "RC=$"
SDSTR_TOK .TEXT "TOK=$"
SDSTR_STOK .TEXT "OK$"
SDSTR_SDTYPE .TEXT "SD CARD TYPE: $"
;
SDSTR_STNOTRDY .TEXT "NOT READY$"
SDSTR_STRDYTO .TEXT "READY TIMEOUT$"
SDSTR_STINITTO .TEXT "INITIALIZATION TIMEOUT$"
SDSTR_STCMDTO .TEXT "COMMAND TIMEOUT$"
SDSTR_STCMDERR .TEXT "COMMAND ERROR$"
SDSTR_STDATAERR .TEXT "DATA ERROR$"
SDSTR_STDATATO .TEXT "DATA TIMEOUT$"
SDSTR_STCRCERR .TEXT "CRC ERROR$"
SDSTR_STNOMEDIA .TEXT "NO MEDIA$"
SDSTR_STWRTPROT .TEXT "WRITE PROTECTED$"
SDSTR_STUNK .TEXT "UNKNOWN$"
;
;==================================================================================================
; SD DISK DRIVER - DATA
;==================================================================================================
;
SD_STATLST .FILL SD_UNITCNT,0 ; LIST OF UNIT STATUSES (2 UNITS)
SD_STATPTR .DW SD_STATLST ; ADDRESS OF STATUS FOR CURRENT UNIT
SD_TYPELST .FILL SD_UNITCNT,0 ; LIST OF CARD TYPES (2 UNITS)
SD_TYPEPTR .Dw SD_TYPELST ; ADDRESS OF CARD TYPE FOR CURRENT UNIT
SD_UNIT .DB 0 ; CURRENT UNIT NUMBER
SD_RC .DB 0 ; RETURN CODE FROM CMD
SD_TOK .DB 0 ; TOKEN FROM DATA XFR
SD_OPRVAL .DB 0 ; CURRENT OPR REG VALUE
SD_LCNT .DB 0 ; LOOP COUNTER
;
SD_CMDBUF: ; START OF STD CMD BUF
SD_CMD .DB 0
SD_CMDP0 .DB 0
SD_CMDP1 .DB 0
SD_CMDP2 .DB 0
SD_CMDP3 .DB 0
SD_CMDCRC .DB 0