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.
 
 
 
 
 
 

2204 lines
61 KiB

;
;=============================================================================
; MMC/SD/SDHC/SDXC CARD STORAGE DRIVER
;=============================================================================
;
; 1) TESTING
; - TRY TO TEST GOIDLE, FIND CARD THAT REQUIRES 2 REQUESTS
; - DUAL CARDS
; - TEST XC CARD TYPE DETECTION
; - TRY TO GET INIT TO FAIL, REMOVE DELAYS AT START OF GOIDLE?
;
;----------------------------------------------------------------------------------------------
; SD Signal Active JUHA N8 CSIO PPI UART DSD MK4 SC MT
; ------------ ------- ------- ------- ------- ------- ------- ------- ------- ------- -------
; CS (DAT3) LO -> RTC:2 RTC:2 RTC:2 ~PC:4 ~MCR:3 OPR:2 SD:2 ~RTC:2/3OPR:4/5
; CLK HI -> RTC:1 RTC:1 CSIO PC:1 ~MCR:2 OPR:1 CSIO CSIO SPI
; DI (CMD) HI -> RTC:0 RTC:0 CSIO PC:0 ~MCR:0 OPR:0 CSIO CSIO SPI
; DO (DAT0) HI -> RTC:7 RTC:6 CSIO PB:7 ~MSR:5 OPR:0 CSIO CSIO SPI
;----------------------------------------------------------------------------------------------
;
; CS = CHIP SELECT (AKA DAT3 FOR NON-SPI MODE)
; CLK = CLOCK
; DI = MOSI = DATA IN (HOST -> CARD, AKA CMD FOR NON-SPI MODE)
; DO = MISO = 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 (SLAVE):
; 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 (HI FOR CSIO)
; 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.
;
; NOTE: THE CSIO IMPLEMENTATION (INCLUDE MK4) USES SPI MODE 3
; (CPOL=1, CPHA=1) BECAUSE THAT IS THE WAY THAT THE Z180 CSIO
; INTERFACE WORKS. ALL OF THE CLOCK TRANSITIONS LISTED ABOVE
; ARE REVERSED FOR CSIO.
;
; 4) DI SHOULD BE LEFT HI (ACTIVE) WHENEVER UNUSED (FOR EXAMPLE, WHEN
; HOST IS RECEIVING DATA (HOST <- CARD)).
;
;------------------------------------------------------------------------------
;
; === R1 RESPONSE ===
; ALL COMMAND RESPONSES START WITH R1
;
; 7 6 5 4 3 2 1 0
; +---+---+---+---+---+---+---+---+
; | 0 | X | X | X | X | X | X | X |
; +---+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; | | | | | | |
; | | | | | | +--- IDLE
; | | | | | +------- ERASE RESET
; | | | | +----------- ILLEGAL COMMAND
; | | | +--------------- COM CRC ERROR
; | | +------------------- ERASE SEQUENCE ERROR
; | +----------------------- ADDRESS ERROR
; +--------------------------- PARAMETER ERROR
;
; === DATA ERROR TOKEN ===
;
; 7 6 5 4 3 2 1 0
; +---+---+---+---+---+---+---+---+
; | 0 | 0 | 0 | 0 | X | X | X | X |
; +---+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; | | | |
; | | | +--- ERROR - GENERAL OR UNKNOWN ERROR
; | | +------- CC ERROR - INTERNAL CARD CONTROLER ERROR
; | +----------- CARD ECC FAILED - CARD INTERNAL ECC FAILED TO CORRECT DATA
; +--------------- OUT OF RANGE - PARAMAETER OUT OF RANGE ALLOWED FOR CARD
;
;------------------------------------------------------------------------------
;
; *** HACK FOR MISSING PULLUP RESISTORS ***
;
; THERE IS A RECENT TREND FOR SD ADAPTER BOARDS (SUCH AS THOSE USED TO ATTACH AN
; SD CARD TO AN ARDUINO OR RASPBERRY PI) TO OMIT THE PULLUP RESISTORS THAT ARE SUPPOSED
; TO BE ON ALL LINES. DESPITE BEING A CLEAR VIOLATION OF THE SPEC, IT IS SOMETHING THAT
; WE WILL NOW NEED TO LIVE WITH. THE CLK, CS, AND MOSI SIGNALS ARE NOT AN ISSUE SINCE
; WE ARE DRIVING THOSE SIGNALS AS THE HOST. THE PROBLEM IS WITH THE MISO SIGNAL.
; FORTUNATELY, MOST OF THE TIME, THE SD CARD WILL BE DRIVING THE SIGNAL. HOWEVER,
; THERE ARE TWO SCEANRIOS WE NEED TO ACCOMMODATE IN THE CODE:
;
; 1. MISO WILL NOT BE DRIVEN BY THE SD CARD (FLOATING) PRIOR TO RESETING THE
; CARD WITH CMD0. NORMALLY, A COMMAND SEQUENCE INVOLVES WAITING FOR THE
; CARD TO BE "READY" BY READING BYTES FROM THE CARD AND LOOKING FOR $FF.
; WHEN MISO IS FLOATING THIS WILL NOT BE RELIABLE. SINCE THE SPEC INDICATES
; IT IS NOT NECESSARY TO WAIT FOR READY PRIOR TO CMD0, THE CODE HAS BEEN
; MODIFIED TO ISSUE CMD0 WITHOUT WAITING FOR READY.
;
; 2. MISO MAY NOT BE DRIVEN IMMEDIATELY AFTER SENDING A COMMAND (POSSIBLY
; JUST CMD0, BUT NOT SURE). NORMALLY, AFTER SENDING A COMMAND, YOU
; LOOK FOR "FILL" BYTES OF $FF THAT MAY OCCUR PRIOR TO THE RESULT. WHEN MISO
; IS FLOATING IT IS IMPOSSIBLE TO DETERMINE IF THE BYTE RECEIVED IS A FILL
; BYTE OR NOT. BASED ON WHAT I HAVE READ, THERE WILL ALWAYS BE AT LEAST
; ONE FILL BYTE PRIOR TO THE ACTUAL RESULT. ADDITIONALLY, THE SD CARD WILL
; START DRIVING MISO SOMETIME WITHIN THAT FIRST FILL BYTE. SO, WE NOW
; JUST DISCARD THE FIRST BYTE RECEIVED AFTER A COMMAND IS SENT WITH THE
; ASSUMPTION THAT IT MUST BE A FILL BYTE AND IS NOT RELIABLE DUE TO FLOATING
; MISO.
;
; THESE CHANGES ARE CONSISTENT WITH THE POPULAR ARDUINO SDFAT LIBRARY, SO THEY ARE
; PROBABLY PRETTY SAFE. HOWEVER, I HAVE BRACKETED THE CHANGES WITH THE EQUATE BELOW.
; IF YOU WANT TO REVERT THESE HACKS, JUST SET THE EQUATE TO FALSE.
;
SD_NOPULLUP .EQU TRUE ; ASSUME NO PULLUP
;
#IF (SDMODE == SDMODE_JUHA) ; JUHA MINI-BOARD
SD_DEVCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS)
SD_OPRREG .EQU RTCIO ; USES RTC LATCHES FOR OPERATION
SD_OPRDEF .EQU %00000001 ; QUIESCENT STATE
SD_OPRMSK .EQU %10000111 ; MASK FOR BITS WE OWN IN RTC LATCH PORT
SD_INPREG .EQU RTCIO ; INPUT REGISTER IS RTC
SD_CS0 .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)
SD_IOBASE .EQU SD_OPRREG ; IOBASE
;
RTCDEF .SET SD_OPRDEF ; SET DEFAULT IN HBIOS MAINLINE
#ENDIF
;
#IF (SDMODE == SDMODE_N8) ; UNMODIFIED N8-2511
SD_DEVCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS)
SD_OPRREG .EQU RTCIO ; USES RTC LATCHES FOR OPERATION
SD_OPRDEF .EQU %00000001 ; QUIESCENT STATE
SD_OPRMSK .EQU %01000111 ; MASK FOR BITS WE OWN IN RTC LATCH PORT
SD_INPREG .EQU RTCIO ; INPUT REGISTER IS RTC
SD_CS0 .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)
SD_IOBASE .EQU SD_OPRREG ; IOBASE
;
RTCDEF .SET SD_OPRDEF ; SET DEFAULT IN HBIOS MAINLINE
#ENDIF
;
#IF (SDMODE == SDMODE_CSIO) ; N8-2312
SD_DEVCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS)
SD_OPRREG .EQU RTCIO ; USES RTC LATCHES FOR OPERATION
SD_OPRDEF .EQU %00000000 ; QUIESCENT STATE
SD_OPRMSK .EQU %00000100 ; MASK FOR BITS WE OWN IN RTC LATCH PORT
SD_CS0 .EQU %00000100 ; RTC:2 IS SELECT
SD_CNTR .EQU Z180_CNTR
SD_TRDR .EQU Z180_TRDR
SD_IOBASE .EQU SD_OPRREG ; IOBASE
;
RTCDEF .SET SD_OPRDEF ; SET DEFAULT IN HBIOS MAINLINE
#ENDIF
;
#IF (SDMODE == SDMODE_PPI) ; PPISD
SD_DEVCNT .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_CS0 .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)
SD_IOBASE .EQU SD_PPIBASE ; IOBASE
#ENDIF
;
#IF (SDMODE == SDMODE_UART)
SD_DEVCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS)
SD_OPRREG .EQU SIO_MCR ; UART MCR PORT (OUTPUT: CS, CLK, DIN)
SD_OPRDEF .EQU %00001100 ; QUIESCENT STATE
SD_OPRMSK .EQU %00101101 ; MASK FOR BITS WE OWN IN RTC LATCH PORT
SD_INPREG .EQU SIO_MSR ; INPUT REGISTER IS MSR
SD_CS0 .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)
SD_IOBASE .EQU UARTIOB ; IOBASE
#ENDIF
;
#IF (SDMODE == SDMODE_DSD) ; DUAL SD
SD_DEVCNT .EQU SDCNT ; 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_CS0 .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)
SD_IOBASE .EQU SD_OPRREG ; IOBASE
#ENDIF
;
#IF (SDMODE == SDMODE_MK4) ; MARK IV (CSIO STYLE INTERFACE)
SD_DEVCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS)
SD_OPRREG .EQU $89 ; DEDICATED MK4 SDCARD REGISTER
SD_OPRDEF .EQU %00000000 ; QUIESCENT STATE
SD_CS0 .EQU %00000100 ; SELECT ACTIVE
SD_CNTR .EQU Z180_CNTR
SD_TRDR .EQU Z180_TRDR
SD_IOBASE .EQU SD_OPRREG ; IOBASE
#ENDIF
;
#IF (SDMODE == SDMODE_SC) ; SC
SD_DEVCNT .EQU SDCNT ; NUMBER OF PHYSICAL UNITS (SOCKETS)
SD_OPRREG .EQU RTCIO ; USES RTC LATCHES FOR OPERATION
SD_OPRDEF .EQU %00001100 ; QUIESCENT STATE (/CS1 & /CS2 DEASSERTED)
SD_OPRMSK .EQU %00001100 ; MASK FOR BITS WE OWN IN RTC LATCH PORT
SD_CS0 .EQU %00000100 ; RTC:2 IS SELECT FOR PRIMARY SPI CARD
SD_CS1 .EQU %00001000 ; RTC:3 IS SELECT FOR SECONDARY SPI CARD
SD_CNTR .EQU Z180_CNTR
SD_TRDR .EQU Z180_TRDR
SD_IOBASE .EQU SD_OPRREG ; IOBASE
;
RTCDEF .SET SD_OPRDEF ; SET DEFAULT IN HBIOS MAINLINE
#ENDIF
;
#IF (SDMODE == SDMODE_MT) ; MT shift register for RC2014 (ref SDMODE_CSIO)
;
; 3 SPI CHANNELS. CHANNEL 0 (CDX & CSX) IS A DEDICATED CONNECTION TO ONBOARD
; WIZNET W5500 AND IS NOT USED HERE. CHANNEL 1 (CD0 & CS0) & 2 (CD1 & CS1)
; ARE ASSUMED TO BE CONNECTED TO SD CARDS.
;
SD_BASE .EQU $5C ; Module base address
SD_DEVCNT .EQU 2 ; NUMBER OF PHYSICAL UNITS (SOCKETS)
SD_WRTR .EQU SD_BASE + 0 ; Write data and transfer
SD_RDTR .EQU SD_BASE + 1 ; Read data and transfer
SD_RDNTR .EQU SD_BASE + 0 ; Read data and NO transfer
SD_OPRREG .EQU SD_BASE + 2 ; SD CHIP SELECTOR
SD_OPRDEF .EQU %00000000 ; QUIESCENT STATE
SD_CDX .EQU %00000001 ; IN/OUT:SD_OPREG:0 = CD0, PMOD pull CD0 low
SD_CD0 .EQU %00000010 ; IN:SD_OPREG:1 = CD1, IN=0 Card detect switch
SD_CD1 .EQU %00000100 ; IN:SD_OPREG:2 = CD2, IN=0 Card detect switch
SD_CSX .EQU %00001000 ; IN/OUT:SD_OPREG:3 = CS0, PMOD SPI CS
SD_CS0 .EQU %00010000 ; IN/OUT:SD_OPREG:4 = CS1, SDCARD1 CS, IN=1 Card present
SD_CS1 .EQU %00100000 ; IN/OUT:SD_OPREG:5 = CS2, SDCARD2 CS, IN=1 Card present
SD_IOBASE .EQU SD_BASE ; IOBASE
#ENDIF
;
; SD CARD COMMANDS
;
SD_CMD_GO_IDLE_STATE .EQU $40 + 0 ; $40, CMD0 -> R1
SD_CMD_SEND_OP_COND .EQU $40 + 1 ; $41, CMD1 -> R1
SD_CMD_SEND_IF_COND .EQU $40 + 8 ; $48, CMD8 -> R7
SD_CMD_SEND_CSD .EQU $40 + 9 ; $49, CMD9 -> R1
SD_CMD_SEND_CID .EQU $40 + 10 ; $4A, CMD10 -> R1
SD_CMD_SET_BLOCKLEN .EQU $40 + 16 ; $50, CMD16 -> R1
SD_CMD_READ_SNGL_BLK .EQU $40 + 17 ; $51, CMD17 -> R1
SD_CMD_WRITE_BLOCK .EQU $40 + 24 ; $58, CMD24 -> R1
SD_CMD_APP_CMD .EQU $40 + 55 ; $77, CMD55 -> R1
SD_CMD_READ_OCR .EQU $40 + 58 ; $7A, CMD58 -> R3
;
; SD CARD APPLICATION COMMANDS (PRECEDED BY APP_CMD COMMAND)
;
SD_ACMD_SEND_OP_COND .EQU $40 + 41 ; $69, ACMD41 -> R1
SD_ACMD_SEND_SCR .EQU $40 + 51 ; $73, ACMD51 -> R1
;
; 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 CARD (V2)
SD_TYPESDXC .EQU 4 ; SDXC CARD (V3)
;
; SD CARD STATUS (SD_STAT)
;
SD_STOK .EQU 0 ; OK
SD_STINVUNIT .EQU -1 ; INVALID UNIT
SD_STRDYTO .EQU -2 ; TIMEOUT WAITING FOR CARD TO BE READY
SD_STINITTO .EQU -3 ; INITIALIZATION 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
;
; IDE DEVICE CONFIGURATION
;
SD_CFGSIZ .EQU 12 ; SIZE OF CFG TBL ENTRIES
;
; PER DEVICE DATA OFFSETS
;
SD_DEV .EQU 0 ; OFFSET OF DEVICE NUMBER (BYTE)
SD_STAT .EQU 1 ; LAST STATUS (BYTE)
SD_TYPE .EQU 2 ; DEVICE TYPE (BYTE)
SD_FLAGS .EQU 3 ; FLAG BITS (BYTE)
SD_MEDCAP .EQU 4 ; MEDIA CAPACITY (DWORD)
SD_LBA .EQU 8 ; OFFSET OF LBA (DWORD)
;
SD_CFGTBL:
; DEVICE 0, PRIMARY MASTER
.DB 0 ; DRIVER DEVICE NUMBER
.DB 0 ; DEVICE STATUS
.DB 0 ; DEVICE TYPE
.DB 0 ; FLAGS BYTE
.DW 0,0 ; DEVICE CAPACITY
.DW 0,0 ; CURRENT LBA
#IF (SD_DEVCNT >= 2)
; DEVICE 1, PRIMARY SLAVE
.DB 1 ; DRIVER DEVICE NUMBER
.DB 0 ; DEVICE STATUS
.DB 0 ; DEVICE TYPE
.DB 0 ; FLAGS BYTE
.DW 0,0 ; DEVICE CAPACITY
.DW 0,0 ; CURRENT LBA
#ENDIF
;
#IF ($ - SD_CFGTBL) != (SD_DEVCNT * SD_CFGSIZ)
.ECHO "*** INVALID SD CONFIG TABLE ***\n"
#ENDIF
;
.DB $FF ; END MARKER
;
;=============================================================================
; INITIALIZATION ENTRY POINT
;=============================================================================
;
SD_INIT:
CALL NEWLINE ; FORMATTING
PRTS("SD:$")
;
#IF (SDMODE == SDMODE_JUHA)
PRTS(" MODE=JUHA$")
PRTS(" IO=0x$")
LD A,SD_OPRREG
CALL PRTHEXBYTE
;
LD A,(RTCVAL) ; GET RTC PORT SHADOW VALUE
AND ~SD_OPRMSK ; CLEAR OUR BITS
OR SD_OPRDEF ; SET OUR BIT DEFAULTS
LD (RTCVAL),A ; SAVE IT
#ENDIF
;
#IF (SDMODE == SDMODE_N8)
PRTS(" MODE=N8$")
PRTS(" IO=0x$")
LD A,SD_OPRREG
CALL PRTHEXBYTE
;
LD A,(RTCVAL) ; GET RTC PORT SHADOW VALUE
AND ~SD_OPRMSK ; CLEAR OUR BITS
OR SD_OPRDEF ; SET OUR BIT DEFAULTS
LD (RTCVAL),A ; SAVE IT
#ENDIF
;
#IF (SDMODE == SDMODE_CSIO)
PRTS(" MODE=CSIO$")
#IF (SDCSIOFAST)
PRTS(" FAST$")
#ENDIF
PRTS(" OPR=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
;
LD A,(RTCVAL) ; GET RTC PORT SHADOW VALUE
AND ~SD_OPRMSK ; CLEAR OUR BITS
OR SD_OPRDEF ; SET OUR BIT DEFAULTS
LD (RTCVAL),A ; SAVE IT
#ENDIF
;
#IF (SDMODE == SDMODE_PPI)
PRTS(" MODE=PPI$")
PRTS(" IO=0x$")
LD A,SD_PPIBASE
CALL PRTHEXBYTE
#ENDIF
;
#IF (SDMODE == SDMODE_UART)
PRTS(" MODE=UART$")
PRTS(" MCR=0x$")
LD A,SIO_MCR
CALL PRTHEXBYTE
PRTS(" MSR=0x$")
LD A,SIO_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
;
#IF (SDMODE == SDMODE_MK4)
PRTS(" MODE=MK4$")
#IF (SDCSIOFAST)
PRTS(" FAST$")
#ENDIF
PRTS(" OPR=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_SC)
PRTS(" MODE=SC$")
#IF (SDCSIOFAST)
PRTS(" FAST$")
#ENDIF
PRTS(" OPR=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
;
LD A,(RTCVAL) ; GET RTC PORT SHADOW VALUE
AND ~SD_OPRMSK ; CLEAR OUR BITS
OR SD_OPRDEF ; SET OUR BIT DEFAULTS
LD (RTCVAL),A ; SAVE IT
#ENDIF
;
#IF (SDMODE == SDMODE_MT)
PRTS(" MODE=MT$")
PRTS(" IO=0x$")
LD A,SD_BASE
CALL PRTHEXBYTE
#ENDIF
;
CALL SD_PROBE ; CHECK FOR HARDWARE
JR Z,SD_INIT00 ; CONTINUE IF PRESENT
;
; HARDWARE NOT PRESENT
PRTS(" NOT PRESENT$")
OR $FF ; SIGNAL FAILURE
RET
;
SD_INIT00:
;
; SETUP THE DISPATCH TABLE ENTRIES
;
PRTS(" DEVICES=$")
LD A,SD_DEVCNT
CALL PRTDECB
;
; SETUP THE DISPATCH TABLE ENTRIES
;
LD B,SD_DEVCNT ; LOOP CONTROL
LD IY,SD_CFGTBL ; START OF CFG TABLE
SD_INIT0:
PUSH BC ; SAVE LOOP CONTROL
LD BC,SD_FNTBL ; BC := FUNC TABLE ADR
PUSH IY ; CFG ENTRY POINTER
POP DE ; COPY TO DE
CALL DIO_ADDENT ; ADD ENTRY, BC IS NOT DESTROYED
LD BC,SD_CFGSIZ ; SIZE OF CFG ENTRY
ADD IY,BC ; BUMP IY TO NEXT ENTRY
POP BC ; RESTORE BC
DJNZ SD_INIT0 ; LOOP AS NEEDED
;
; INITIALIZE THE SD INTERFACE NOW
CALL SD_SETUP ; DO HARDWARE SETUP/INIT
RET NZ ; ABORT ON ERROR
;
; INITIALIZE INDIVIDUAL UNIT(S) AND DISPLAY DEVICE INVENTORY
LD B,SD_DEVCNT ; INIT LOOP COUNTER TO DEVICE COUNT
LD IY,SD_CFGTBL ; START OF CFG TABLE
SD_INIT1:
PUSH BC ; SAVE LOOP COUNTER/INDEX
CALL SD_INITUNIT ; INITIALIZE IT
#IF (SDTRACE < 2)
CALL NZ,SD_PRTSTAT ; IF ERROR, SHOW IT
#ENDIF
LD BC,SD_CFGSIZ ; SIZE OF CFG ENTRY
ADD IY,BC ; BUMP IY TO NEXT ENTRY
POP BC ; RESTORE LOOP CONTROL
DJNZ SD_INIT1 ; DECREMENT LOOP COUNTER AND LOOP AS NEEDED
;
RET ; DONE
;
; INITIALIZE UNIT DESIGNATED IN ACCUM
;
SD_INITUNIT:
CALL SD_SELUNIT ; SELECT UNIT
RET NZ ; ABORT ON ERROR
;
CALL SD_INITCARD ; INIT THE SELECTED CARD
RET NZ ; ABORT ON ERROR
;
CALL SD_PRTPREFIX
CALL PC_SPACE
;
; PRINT CARD TYPE
LD A,(IY+SD_TYPE) ; GET CARD TYPE VALUE TO A
LD DE,SD_STR_TYPEMMC
CP SD_TYPEMMC
JR Z,SD_INITUNIT1
LD DE,SD_STR_TYPESDSC
CP SD_TYPESDSC
JR Z,SD_INITUNIT1
LD DE,SD_STR_TYPESDHC
CP SD_TYPESDHC
JR Z,SD_INITUNIT1
LD DE,SD_STR_TYPESDXC
CP SD_TYPESDXC
JR Z,SD_INITUNIT1
LD DE,SD_STR_TYPEUNK
SD_INITUNIT1:
CALL WRITESTR
; GET CID (WHICH CONTAINS PRODUCT NAME)
LD A,SD_CMD_SEND_CID ; SEND_CID
CALL SD_INITCMD ; SETUP COMMAND BUFFER
CALL SD_EXECCMD ; RUN COMMAND
RET NZ ; ABORT ON ERROR
LD BC,16 ; 16 BYTES OF CID
LD HL,SD_BUF
CALL SD_GETDATA
CALL SD_DONE
JP NZ,SD_ERRDATA ; DATA XFER ERROR
#IF (SDTRACE >= 3)
CALL SD_PRTPREFIX
LD DE,SD_STR_CID
CALL WRITESTR
LD DE,SD_BUF
LD A,16
CALL PRTHEXBUF
#ENDIF
; PRINT PRODUCT NAME
PRTS(" NAME=$") ; PRINT LABEL
LD B,5 ; PREPARE TO PRINT 5 BYTES
LD HL,SD_BUF + 3 ; AT BYTE OFFSET 3 IN RESULT BUFFER
SD_INITUNIT2:
LD A,(HL) ; GET NEXT BYTE
CALL COUT ; PRINT IT
INC HL ; POINT TO NEXT BYTE
DJNZ SD_INITUNIT2 ; LOOP FOR ALL 5 BYTES
;
; PRINT STORAGE CAPACITY (BLOCK COUNT)
PRTS(" BLOCKS=0x$") ; PRINT FIELD LABEL
LD A,SD_MEDCAP ; OFFSET TO CAPACITY FIELD
CALL LDHLIYA ; HL := IY + A, REG A TRASHED
CALL LD32 ; GET THE CAPACITY VALUE
CALL PRTHEX32 ; PRINT HEX VALUE
;
; PRINT STORAGE SIZE IN MB
PRTS(" SIZE=$") ; PRINT FIELD LABEL
LD B,11 ; 11 BIT SHIFT TO CONVERT BLOCKS --> MB
CALL SRL32 ; RIGHT SHIFT
CALL PRTDEC ; PRINT LOW WORD IN DECIMAL (HIGH WORD DISCARDED)
PRTS("MB$") ; PRINT SUFFIX
;
; CHECK FOR WRITE PROTECT AND NOTIFY USER IF SO
CALL SD_CHKWP ; WRITE PROTECTED?
RET Z ; IF NOT, DONE
PRTS(" WP$") ; NOTIFY USER
;
XOR A ; SIGNAL SUCCESS
RET ; DONE
;
;----------------------------------------------------------------------
; PROBE FOR SD HARDWARE
;----------------------------------------------------------------------
;
; ON RETURN, ZF SET INDICATES HARDWARE FOUND
;
SD_PROBE:
;
#IF (SDMODE == SDMODE_DSD)
; ON DSD, SELREG, BIT 0 IS READ BACK AS WRITTEN, BIT 1
; IS ALWAYS READ AS ZERO. TO TEST FOR EXISTENCE, WE
; WRITE 00 AND MAKE SURE IT READS BACK AS 00, THEN WE
; WRITE 11 AND MAKE SURE IT READS BACK AS 01.
LD C,SD_SELREG ; USE C TO ADDRESS PORT
XOR A ; A := 0
OUT (C),A ; SELREG := 0
IN A,(C) ; READ SELREG BACK
AND $03 ; ISOLATE 2 LOWEST BITS
CP $00 ; BOTH BITS SHOULD BE 0
RET NZ ; FAIL IF NOT
LD A,$03 ; SET 2 LOWEST BITS
OUT (C),A ; DO IT
IN A,(C) ; READ SELREG BACK
AND $03 ; ISOLATE 2 LOWEST BITS
CP $01 ; SHOULD READ BACK AS $01
RET ; RETURN W/ ZF SET AS NEEDED
#ENDIF
;
;#IF (SDMODE == SDMODE_MT)
; LD A,SD_OPRDEF
; OUT (SD_OPRREG),A ; MAKE SURE CONTROL REGISTER IS CLEARED
;;
; ; TEST WITH PMOD NOT CONNECTED
;; IN A,(SD_OPRREG)
;; AND SD_CD0+SD_CS0 ; ISOLATE CD0 AND CS0
;; CP SD_CD0+SD_CS0 ; BOTH SHOULD BE HIGH
;; JR NZ,SD_PROBE_FAIL ; FAIL IF NOT
; ; TEST CD0
;; LD A,SD_CD0 ; D1=DNP CANNOT TEST
;; OUT (SD_OPRREG),A
;; IN A,(SD_OPRREG)
;; AND SD_CD0
;; JR NZ,SD_PROBE_FAIL ; FAIL IF NOT PULLED LOW
; ; TEST CS0
;; LD A,SD_CS0 ; D2=DNP CANNOT TEST
;; OUT (SD_OPRREG),A
;; IN A,(SD_OPRREG)
;; AND SD_CS0
;; JR NZ,SD_PROBE_FAIL ; FAIL IF NOT PULLED LOW
; ; TEST CS1
;; LD A,SD_CS1
;; OUT (SD_OPRREG),A
;; IN A,(SD_OPRREG)
;; AND SD_CS1
;; JR NZ,SD_PROBE_FAIL ; FAIL IF NOT PULLED LOW
;; ; TEST CS2
;; LD A,SD_CS2
;; OUT (SD_OPRREG),A
;; IN A,(SD_OPRREG)
;; AND SD_CS2
;; JR NZ,SD_PROBE_FAIL ; FAIL IF NOT PULLED LOW
;
; LD A,SD_OPRDEF
; OUT (SD_OPRREG),A ; MAKE SURE CONTROL REGISTER IS CLEARED
;#ENDIF
;
XOR A ; SIGNAL SUCCESS
;
;#IF (SDMODE == SDMODE_MT)
;SD_PROBE_FAIL:
; LD A,SD_OPRDEF
; OUT (SD_OPRREG),A ; MAKE SURE CONTROL REGISTER IS CLEARED
;#ENDIF
RET ; AND RETURN
;
;=============================================================================
; DRIVER FUNCTION TABLE
;=============================================================================
;
SD_FNTBL:
.DW SD_STATUS
.DW SD_RESET
.DW SD_SEEK
.DW SD_READ
.DW SD_WRITE
.DW SD_VERIFY
.DW SD_FORMAT
.DW SD_DEVICE
.DW SD_MEDIA
.DW SD_DEFMED
.DW SD_CAP
.DW SD_GEOM
#IF (($ - SD_FNTBL) != (DIO_FNCNT * 2))
.ECHO "*** INVALID IDE FUNCTION TABLE ***\n"
#ENDIF
;
SD_VERIFY:
SD_FORMAT:
SD_DEFMED:
CALL SYSCHK ; INVALID SUB-FUNCTION
LD A,ERR_NOTIMPL
OR A
RET
;
;
;
SD_READ:
CALL HB_DSKREAD ; HOOK HBIOS DISK READ SUPERVISOR
LD A,SD_CMD_READ_SNGL_BLK ; SETUP FOR SINGLE BLOCK READ CMD
JR SD_IO ; CONTINUE TO GENERIC IO ROUTINE
;
;
;
SD_WRITE:
CALL HB_DSKWRITE ; HOOK HBIOS DISK WRITE SUPERVISOR
LD A,SD_CMD_WRITE_BLOCK ; SETUP FOR BLOCK WRITE CMD
JR SD_IO ; CONTINUE TO GENERIC IO ROUTINE
;
;
;
SD_IO:
LD (SD_CMDVAL),A ; SAVE THE SD CARD COMMAND
LD (SD_DSKBUF),HL ; SAVE DISK BUFFER ADDRESS
LD A,E ; GET BLOCK COUNT REQUESTED
LD (SD_BLKCNT),A ; ... AND SAVE IT
OR A ; SET FLAGS
RET Z ; ZERO SECTOR I/O, RETURN W/ E=0 & A=0
;
#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC))
; CONSIDER CAPTURING CURRENT CNTR VALUE HERE AND USE IT
; IN SD_CSIO_DEF
; SET CSIO FOR HIGH SPEED OPERATION
CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING
CALL DLY32 ; WAIT A BIT MORE FOR FINAL BIT
XOR A ; ZERO MEANS MAX SPEED
OUT0 (SD_CNTR),A ; NOW SET CSIO PORT
; HOOK RETURN TO RESTORE CSIO TO DEFAULT SPEED
LD HL,SD_CSIO_DEF ; ROUTE RETURN
PUSH HL ; ... THRU CSIO RESTORE
#ENDIF
;
#IF (SDTRACE == 1)
LD HL,SD_PRTERR ; SET UP SD_PRTERR
PUSH HL ; ... TO FILTER ALL EXITS
#ENDIF
;
CALL SD_SELUNIT ; HARDWARE SELECTION OF TARGET UNIT
RET NZ ; ABORT ON ERROR
LD A,(SD_CMDVAL) ; GET COMMAND VALUE
CP SD_CMD_READ_SNGL_BLK ; IS THIS A READ?
CALL NZ,SD_CHKWP ; CHECK FOR WRITE PROTECT IF NOT A READ
JP NZ,SD_WRTPROT ; HANDLE WRITE PROTECT ERR
LD A,(SD_BLKCNT) ; BLOCK COUNT TO A
LD E,A ; ... AND TO E IN CASE OF ZERO ERR BELOW
OR A ; SET FLAGS
RET Z ; ZERO SECTOR I/O, RETURN W/ E=0 & A=0
LD B,A ; INIT SECTOR DOWNCOUNTER
LD C,0 ; INIT SECTOR READ/WRITE COUNT
SD_IO1:
PUSH BC ; SAVE COUNTERS
LD A,(SD_CMDVAL) ; SET COMMAND
LD C,A ; ... AND PUT IN C
CALL SD_SECTIO ; DO SECTOR I/O
JR NZ,SD_IO2 ; IF ERROR, SKIP INCREMENT
; INCREMENT LBA
LD A,SD_LBA ; LBA OFFSET
CALL LDHLIYA ; HL := IY + A, REG A TRASHED
CALL INC32HL ; INCREMENT THE VALUE
; INCREMENT DMA
LD HL,SD_DSKBUF+1 ; POINT TO MSB OF BUFFER ADR
INC (HL) ; BUMP DMA BY
INC (HL) ; ... 512 BYTES
XOR A ; SIGNAL SUCCESS
SD_IO2:
POP BC ; RECOVER COUNTERS
JR NZ,SD_IO3 ; IF ERROR, BAIL OUT
INC C ; BUMP COUNT OF SECTORS READ
DJNZ SD_IO1 ; LOOP AS NEEDED
SD_IO3:
LD E,C ; SECTOR READ COUNT TO E
LD HL,(SD_DSKBUF) ; CURRENT DMA TO HL
OR A ; SET FLAGS BASED ON RETURN CODE
RET Z ; RETURN IF SUCCESS
LD A,ERR_IO ; SIGNAL IO ERROR
OR A ; SET FLAGS
RET ; AND DONE
;
;
;
SD_STATUS:
; RETURN DEVICE STATUS
LD A,(IY+SD_STAT) ; GET STATUS OF SELECTED DEVICE
OR A ; SET FLAGS
RET ; AND RETURN
;
;
;
SD_RESET:
CALL SD_SELUNIT ; SET CUR UNIT
; RE-INITIALIZE THE SD CARD TO ACCOMMODATE HOT SWAPPING
CALL SD_INITCARD ; RE-INIT SELECTED UNIT
#IF (SDTRACE >= 3)
CALL SD_PRTERR ; PRINT ANY ERRORS
#ENDIF
OR A ; SET RESULT FLAGS
RET
;
;
;
SD_DEVICE:
LD D,DIODEV_SD ; D := DEVICE TYPE
LD E,(IY+SD_DEV) ; E := PHYSICAL DEVICE NUMBER
LD C,%01010000 ; C := ATTRIBUTES, REMOVABLE, SD CARD
LD H,SDMODE ; H := MODE
LD L,(SD_IOBASE) ; L := BASE I/O ADDRESS
XOR A ; SIGNAL SUCCESS
RET
;
;
;
SD_MEDIA:
LD A,E ; GET FLAGS
OR A ; SET FLAGS
JR Z,SD_MEDIA2 ; JUST REPORT CURRENT STATUS AND MEDIA
;
; GET CURRENT STATUS
LD A,(IY+SD_STAT) ; GET STATUS
OR A ; SET FLAGS
JR NZ,SD_MEDIA1 ; ERROR ACTIVE, GO RIGHT TO RESET
;
; USE SEND_CSD TO CHECK CARD
CALL SD_SELUNIT ; SET CUR UNIT
LD A,SD_CMD_SEND_CSD ; SEND_CSD
CALL SD_INITCMD ; SETUP COMMAND BUFFER
CALL SD_EXECCMD ; EXECUTE COMMAND
JR NZ,SD_MEDIA1 ; ERROR, NEED RESET
LD BC,16 ; 16 BYTES OF CSD
LD HL,SD_BUF ; PUT IN OUR PRIVATE BUFFER
CALL SD_GETDATA ; GET THE DATA
CALL SD_DONE ; CLOSE THE TRANSACTION
JR Z,SD_MEDIA2 ; IF SUCCESS, BYPASS RESET
;
SD_MEDIA1:
CALL SD_RESET ; RESET CARD
;
SD_MEDIA2:
LD A,(IY+SD_STAT) ; GET STATUS
OR A ; SET FLAGS
LD D,0 ; NO MEDIA CHANGE DETECTED
LD E,MID_HD ; ASSUME WE ARE OK
RET Z ; RETURN IF GOOD INIT
LD E,MID_NONE ; SIGNAL NO MEDIA
LD A,ERR_NOMEDIA ; NO MEDIA ERROR
OR A ; SET FLAGS
RET ; AND RETURN
;
;
;
;
SD_SEEK:
BIT 7,D ; CHECK FOR LBA FLAG
CALL Z,HB_CHS2LBA ; CLEAR MEANS CHS, CONVERT TO LBA
RES 7,D ; CLEAR FLAG REGARDLESS (DOES NO HARM IF ALREADY LBA)
LD (IY+SD_LBA+0),L ; SAVE NEW LBA
LD (IY+SD_LBA+1),H ; ...
LD (IY+SD_LBA+2),E ; ...
LD (IY+SD_LBA+3),D ; ...
XOR A ; SIGNAL SUCCESS
RET ; AND RETURN
;
;
;
SD_CAP:
LD A,(IY+SD_STAT) ; GET STATUS
PUSH AF ; SAVE IT
LD A,SD_MEDCAP ; OFFSET TO CAPACITY FIELD
CALL LDHLIYA ; HL := IY + A, REG A TRASHED
CALL LD32 ; GET THE CURRENT CAPACITY INTO DE:HL
LD BC,512 ; 512 BYTES PER BLOCK
POP AF ; RECOVER STATUS
OR A ; SET FLAGS
RET
;
;
;
SD_GEOM:
; FOR LBA, WE SIMULATE CHS ACCESS USING 16 HEADS AND 16 SECTORS
; RETURN HS:CC -> DE:HL, SET HIGH BIT OF D TO INDICATE LBA CAPABLE
CALL SD_CAP ; GET TOTAL BLOCKS IN DE:HL, BLOCK SIZE TO BC
LD L,H ; DIVIDE BY 256 FOR # TRACKS
LD H,E ; ... HIGH BYTE DISCARDED, RESULT IN HL
LD D,16 | $80 ; HEADS / CYL = 16, SET LBA BIT
LD E,16 ; SECTORS / TRACK = 16
RET ; DONE, A STILL HAS SD_CAP STATUS
;
;=============================================================================
; FUNCTION SUPPORT ROUTINES
;=============================================================================
;
; (RE)INITIALIZE CARD
;
SD_INITCARD:
;
CALL SD_CHKCD ; CHECK CARD DETECT
JP Z,SD_NOMEDIA ; Z=NO MEDIA, HANDLE IF SO
;
#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC))
CALL SD_CSIO_DEF ; ENSURE CSIO AT DEFAULT SPEED
#ENDIF
;
; 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_INITCARD1:
LD A,$FF ; KEEP DIN HI
PUSH BC ; SAVE LOOP CONTROL
CALL SD_PUT ; SEND 8 CLOCKS
POP BC ; RESTORE LOOP CONTROL
DJNZ SD_INITCARD1 ; LOOP AS NEEDED
;
; MAKE SURE WE FINISH SENDING
#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC))
CALL SD_WAITTX ; WAIT FOR TE TO CLEAR
CALL DLY32 ; WAIT A BIT MORE FOR FINAL BIT
#ENDIF
;
; PUT CARD IN IDLE STATE
CALL SD_GOIDLE ; GO TO IDLE
JP NZ,SD_NOMEDIA ; CONVERT ERROR TO NO MEDIA
;
SD_INITCARD2:
LD (IY+SD_TYPE),SD_TYPESDSC ; ASSUME SDSC CARD TYPE
;
; CMD8 IS REQUIRED FOR V2 CARDS. FAILURE HERE IS OK AND
; JUST MEANS THAT IT IS A V1 CARD
LD A,SD_CMD_SEND_IF_COND ; SEND_IF_COND
CALL SD_INITCMD ; SETUP COMMAND BUFFER
LD HL,SD_CMDP2 ; POINT TO 3RD PARM BYTE
LD (HL),1 ; VHS=1, 2.7-3.6V
INC HL ; POINT TO 4TH PARM BYTE
LD (HL),$AA ; CHECK PATTERN
INC HL ; POINT TO CRC
LD (HL),$87 ; ... AND SET IT TO KNOWN VALUE OF $87
CALL SD_EXECCMDND ; EXEC COMMAND W/ NO DATA RETURNED
;
; GET CARD OUT OF IDLE STATE BY SENDING SD_APP_OP_COND
; REPEATEDLY UNTIL IDLE BIT IS CLEAR
LD A,0
LD (SD_LCNT),A
SD_INITCARD3:
; DELAY A BIT PER SPEC
LD DE,300 ; 16US * 300 = ~5MS
CALL VDELAY ; CPU SPEED NORMALIZED DELAY
; SEND APP CMD INTRODUCER
CALL SD_EXECACMD ; SEND APP COMMAND INTRODUCER
CP SD_STCMDERR ; COMMAND ERROR?
JR Z,SD_INITCARD3A ; IF SO, TRY MMC CARD INIT
OR A ; SET FLAGS
RET NZ ; ABORT IF ANY OTHER ERROR
; SEND APP_OP_COND
LD A,SD_ACMD_SEND_OP_COND ; SD_APP_OP_COND
CALL SD_INITCMD ; SETUP COMMAND BUFFER
LD A,$40 ; P0 = $40 INDICATES WE SUPPORT V2 CARDS
LD (SD_CMDP0),A ; SET COMMAND PARM 0
CALL SD_EXECCMDND ; EXEC COMMAND W/ NO DATA RETURNED
;RET NZ ; ABORT ON ERROR
CP SD_STCMDERR ; COMMAND ERROR?
JR Z,SD_INITCARD3A ; IF SO, TRY MMC CARD INIT
OR A ; SET FLAGS
RET NZ ; ABORT IF ANY OTHER ERROR
; CHECK FOR IDLE, EXIT LOOP IF IDLE CLEARED
LD A,(SD_RC) ; GET CARD RESULT CODE
OR A ; SET FLAGS
JR Z,SD_INITCARD4 ; IF IDLE BIT CLEAR, EXIT LOOP
; LOOP AS NEEDED
LD HL,SD_LCNT ; POINT TO LOOP COUNTER
DEC (HL) ; DECREMENT LOOP COUNTER
JR NZ,SD_INITCARD3 ; LOOP UNTIL COUNTER EXHAUSTED
JP SD_ERRINITTO ; HANDLE INIT TIMEOUT ERROR
;
SD_INITCARD3A:
; TRY MMC CARD INITIALIZATION
; CALL SEND_OP_COND UNTIL CARD IS READY (NOT IDLE)
LD A,0
LD (SD_LCNT),A
SD_INITCARD3B:
; DELAY A BIT PER SPEC
LD DE,300 ; 16US * 300 = ~5MS
CALL VDELAY ; CPU SPEED NORMALIZED DELAY
; SEND OP_COND COMMAND
LD A,SD_CMD_SEND_OP_COND ; SD_OP_COND
CALL SD_INITCMD ; SETUP COMMAND BUFFER
CALL SD_EXECCMDND ; EXEC COMMAND WITH NO DATA
RET NZ ; ABORT ON ERROR
; CHECK FOR IDLE, EXIT LOOP IF IDLE CLEARED
LD A,(SD_RC) ; GET CARD RESULT CODE
OR A ; SET FLAGS
JR Z,SD_INITCARD3C ; IDLE BIT CLEAR, EXIT LOOP
; LOOP AS NEEDED
LD HL,SD_LCNT ; POINT TO LOOP COUNTER
DEC (HL) ; DECREMENT LOOP COUNTER
JR NZ,SD_INITCARD3B ; LOOP UNTIL COUNTER EXHAUSTED
JP SD_ERRINITTO ; HANDLE INIT TIMEOUT ERROR
;
SD_INITCARD3C:
; SUCCESSFUL MMC CARD INITIALIZATION
LD C,SD_TYPEMMC ; MMC CARD TYPE
JR SD_INITCARD5 ; RESUME FLOW
;
SD_INITCARD4:
; CMD58 RETURNS THE 32 BIT OCR REGISTER (R3), WE WANT TO CHECK
; BIT 30, IF SET THIS IS SDHC/XC CARD
LD A,SD_CMD_READ_OCR ; READ_OCR
CALL SD_INITCMD ; SETUP COMMAND BUFFER
CALL SD_EXECCMD ; EXECUTE COMMAND
RET NZ ; ABORT ON ERROR
; CMD58 WORKED, GET OCR DATA AND SET CARD TYPE
CALL SD_GET ; BITS 31-24
CALL SD_DONE ; FINISH THE TRANSACTION
AND $40 ; ISOLATE BIT 30 (CCS)
LD C,SD_TYPESDSC ; ASSUME V1 CARD
JR Z,SD_INITCARD5 ; IF BIT NOT SET, THIS IS SDSC CARD
;
SD_INITCARD4A:
; ACMD51 RETURNS THE 64 BIT SCR REGISTER (ONLY AVAILABLE ON SDSC AND ABOVE)
; SD_SPEC3 (BIT 47) IS SET IF CARD IS SDXC OR GREATER
CALL SD_EXECACMD ; SEND APP COMMAND INTRODUCER
RET NZ ; ABORT ON ERROR (THIS SHOULD ALWAYS WORK)
LD A,SD_ACMD_SEND_SCR ; APP CMD SEND_SCR
CALL SD_INITCMD ; SETUP COMMAND BUFFER
CALL SD_EXECCMD ; EXECUTE COMMAND
RET NZ ; ABORT ON ERROR (THIS SHOULD ALWAYS WORK)
; ACMD51 SUCCEEDED, NOW GET THE SCR REGISTER CONTENTS
LD BC,8 ; 8 BYTES OF SCR
LD HL,SD_BUF ; PUT IN OUR PRIVATE BUFFER
CALL SD_GETDATA ; GET THE DATA
CALL SD_DONE ; CLOSE THE TRANSACTION
JP NZ,SD_ERRDATA ; DATA XFER ERROR
;
#IF (SDTRACE >= 3)
; IF TRACING, DUMP THE SCR CONTENTS
CALL SD_PRTPREFIX
LD DE,SD_STR_SCR
CALL WRITESTR
LD DE,SD_BUF
LD A,8
CALL PRTHEXBUF
#ENDIF
;
; EXTRACT THE SD_SECURITY FIELD AND SET SDHC/SDXC BASED ON VALUE
LD A,(SD_BUF + 1) ; GET THIRD BYTE (BITS 47-40) (55-48)
AND %01110000 ; ISOLATE SD_SECURITY BITS
CP $40 ; CHECK FOR SDXC VALUE
LD C,SD_TYPESDHC ; ASSUME CARD TYPE = SDHC
JR NZ,SD_INITCARD5 ; IF NOT SDXC, DONE
LD C,SD_TYPESDXC ; OTHERWISE, THIS IS SDXC CARD
;
SD_INITCARD5:
LD (IY+SD_TYPE),C ; SAVE CARD TYPE
#IF (SDTRACE >= 3)
CALL SD_PRTPREFIX
LD DE,SD_STR_SDTYPE
CALL WRITESTR
LD A,C
CALL PRTHEXBYTE
#ENDIF
;
; SET OUR DESIRED BLOCK LENGTH (512 BYTES)
LD A,SD_CMD_SET_BLOCKLEN ; SET_BLOCKLEN
CALL SD_INITCMD ; SETUP COMMAND BUFFER
LD DE,512 ; 512 BYTE BLOCK LENGTH
LD HL,SD_CMDP2 ; PUT VALUE INTO PARMS
LD (HL),D ; ... HIGH WORD (P0, P1) REMAIN ZERO
INC HL ; ... VALUE OF DE GET PUT IN LOW WORD (P2, P3)
LD (HL),E ; ... BUT OBSERVE BIG ENDIAN LAYOUT
CALL SD_EXECCMDND ; EXEC COMMAND W/ NO DATA
RET NZ ; ABORT ON ERROR
; HIGH SPEED CSIO OPERATION IS NOW SET AT THE START OF SD_IO
;
;#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC))
; ; PER SPEC, THE CARD SHOULD NOW BE ABLE TO HANDLE FULL SPEED OPERATION
; ; SO, FOR CSIO OPERATION, WE SET CSIO TO MAXIMUM SPEED
; CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING
; CALL DLY32 ; WAIT A BIT MORE FOR FINAL BIT
; XOR A ; ZERO MEANS MAX SPEED
; OUT0 (SD_CNTR),A ; NOW SET CSIO PORT
;#ENDIF
;
; ISSUE SEND_CSD (TO DERIVE CARD CAPACITY)
LD A,SD_CMD_SEND_CSD ; SEND_CSD
CALL SD_INITCMD ; SETUP COMMAND BUFFER
CALL SD_EXECCMD ; EXECUTE COMMAND
RET NZ ; ABORT ON ERROR
LD BC,16 ; 16 BYTES OF CSD
LD HL,SD_BUF ; PUT IN OUR PRIVATE BUFFER
CALL SD_GETDATA ; GET THE DATA
CALL SD_DONE ; CLOSE THE TRANSACTION
JP NZ,SD_ERRDATA ; DATA XFER ERROR
;
#IF (SDTRACE >= 3)
; IF TRACING, DUMP THE CSD CONTENTS
CALL SD_PRTPREFIX
LD DE,SD_STR_CSD
CALL WRITESTR
LD DE,SD_BUF
LD A,16
CALL PRTHEXBUF
#ENDIF
;
; GET SIZE OF DEVICE IN BLOCKS
LD A,(IY+SD_TYPE) ; GET CARD TYPE
OR A ; SET FLAGS
JR Z,SD_INITCARD5A ; HANDLE CARD TYPE UNKNOWN
CP SD_TYPESDHC ; COMPARE TO SDHC (V2)
JP NC,SD_INITCARD8 ; HANDLE SDHC (V2) OR BETTER
JR SD_INITCARD6 ; HANDLE MMC OR SDSC
;
SD_INITCARD5A:
CALL SYSCHK
JP SD_NOMEDIA
;
; CAPACITY CALCULATION FOR MMC OR SDSC (V1) CARDS:
; BYTES = (C_SIZE + 1) * 2^(2+C_SIZE_MULT+READ_BL_LEN) = (C_SIZE+1) << (2+C_SIZE_MULT+READ_BL_LEN)
; BLOCKS = BYTES / 512 = BYTES >> 9
;
SD_INITCARD6: ; GET SIZE FOR V1 CARD
PUSH IX ; SAVE IX
LD IX,SD_BUF ; POINT IX TO BUFFER
LD A,(IX+6) ; GET C_SIZE MSB
AND %00000011 ; MASK OFF TOP 6 BITS (NOT PART OF C_SIZE)
LD C,A ; MSB -> C
LD D,(IX+7) ; D
LD E,(IX+8) ; LSB -> E
LD B,6 ; RIGHT SHIFT WHOLE THING BY 6 BITS
SD_INITCARD7:
SRA C ; SHIFT MSB
RR D ; SHIFT NEXT BYTE
RR E ; SHIFT LSB
DJNZ SD_INITCARD7 ; LOOP TILL DONE
PUSH DE ; DE = C_SIZE, SAVE IT
LD A,(IX+9) ; GET C_SIZE_MULT MSB
LD B,(IX+10) ; GET C_SIZE_MULT LSB
SLA B ; SHIFT LEFT MSB
RLA ; SHIFT LEFT LSB
AND %00000111 ; ISOLATE RELEVANT BITS
LD C,A ; C := C_SIZE_MULT
LD A,(IX+5) ; GET READ_BL_LEN
AND %00001111 ; ISLOATE RELEVANT BITS
LD B,A ; B := READ_BL_LEN
; FINAL MULTIPLIER IS 2^(C_SIZE_MULT + READ_BL_LEN + 2)
LD A,B ; READ_BL_LEN
ADD A,C ; AND C_SIZE_MULT
ADD A,2 ; AND 2 MORE BY DEFINITION
; RELOAD C_SIZE AND CONVERT TO 32 BIT VALUE IN DE:HL
POP HL ; RECOVE C_SIZE
INC HL ; ADD 1
LD DE,0 ; HI WORD IS ZERO
; ADJUST TO 512 BYTE BLOCK COUNT
LD B,A ; NORMALIZE TO BYTE COUNT
CALL SLA32 ; BIT SHIFT LEFT ACCORDING TO MULTIPLIERS
LD B,9 ; NORMALIZE TO 512 BYTE BLOCK COUNT
CALL SRL32 ; BIT SHIFT RIGHT 9 BITS
POP IX ; RESTORE IX
JR SD_INITCARD9 ; RECORD VALUE
;
; CAPACITY CALCULATION FOR SDHC/SDXC (V2/V3) CARDS:
; BLOCKS = (C_SIZE + 1) * 1024 = C_SIZE << 10
;
SD_INITCARD8: ; GET SIZE FOR V2 CARD
PUSH IX ; SAVE IX
LD IX,SD_BUF ; POINT IX TO BUFFER
LD A,(IX + 7) ; GET C_SIZE MSB TO A
AND %00111111 ; ISOLATE RELEVANT BITS
LD H,(IX + 8) ; GET NEXT BYTE TO H
LD L,(IX + 9) ; GET C_SIZE LSB TO L
POP IX ; RESTORE IX
; ADD 1 TO C_SIZE IN A:HL
LD DE,1 ; LOAD 1
ADD HL,DE ; ADD TO HL
ADC A,0 ; HANDLE CARRY
; CONVERT TO 32 BIT, A:HL -> DE:HL
LD D,0
LD E,A
; DIVIDE BY 1024 TO NORMALIZE, LEFT SHIFT 10 BITS
LD B,10 ; SHIFT BY 10 BITS
CALL SLA32 ; SHIFT THE 32 BIT VALUE
JR SD_INITCARD9 ; CONTINUE
;
SD_INITCARD9:
; SAVE DERIVED CAPACITY VALUE IN DE:HL
PUSH HL ; SAVE HL
LD A,SD_MEDCAP ; OFFSET TO CAPACITY FIELD
CALL LDHLIYA ; HL := IY + A, REG A TRASHED
PUSH HL ; MOVE ADDRESS
POP BC ; ... TO BC
POP HL ; RECOVER HL
CALL ST32 ; SAVE THE CAPACITY VALUE (DWORD)
;
; RESET CARD STATUS TO 0 (OK)
XOR A ; A := 0 (STATUS = OK)
LD (IY+SD_STAT),A ; SAVE IT
;
RET ; RETURN, A=0, Z SET
; SECTOR I/O
; SD CARD COMMAND BYTE MUST BE PASSED IN C
;
SD_SECTIO:
PUSH BC
CALL SD_CHKCARD ; CHECK / REINIT CARD AS NEEDED
POP BC
RET NZ ; ABORT IF REINIT FAILED
LD A,C ; LOAD SD CARD COMMAND BYTE
CALL SD_INITCMD ; SETUP COMMAND BUFFER
CALL SD_SETADDR ; SETUP LBA ADDRESS
CALL SD_EXECCMD ; EXECUTE COMMAND
RET NZ ; ABORT ON ERROR
LD HL,(SD_DSKBUF)
LD BC,512 ; LENGTH TO READ
LD A,(SD_CMD) ; GET THE COMMAND
CP SD_CMD_READ_SNGL_BLK ; READ_SINGLE_BLOCK?
JR Z,SD_SECTIO1 ; HANDLE READ
CP SD_CMD_WRITE_BLOCK ; WRITE_BLOCK?
JR Z,SD_SECTIO2 ; HANDLE WRITE
CALL PANIC ; PANIC ON ANYTHING ELSE
LD A,ERR_INTERNAL
OR A
RET
SD_SECTIO1:
; GET SECTOR DATA
CALL SD_GETDATA ; GET THE BLOCK
JR SD_SECTIO3 ; AND CONTINUE
SD_SECTIO2:
; PUT SECTOR DATA
CALL SD_PUTDATA ; PUT THE BLOCK AND FALL THRU
SD_SECTIO3:
; CONTINUE WITH COMMON CODE
CALL SD_DONE ; CLOSE THE TRANSACTION
RET Z ; RETURN WITH A=0 AND Z SET
JP SD_ERRDATA ; DATA XFER ERROR
;
; CHECK THE SD CARD, ATTEMPT TO REINITIALIZE IF NEEDED
;
SD_CHKCARD:
; FIX: NEED TO CHECK CARD DETECT HERE AND
; HANDLE AS ERROR.
;
LD A,(IY+SD_STAT) ; GET CURRENT STATUS
OR A ; SET FLAGS
RET Z ; RETURN WITH A=0 AND Z SET
JP SD_INITCARD ; OTHERWISE INIT CARD
;
; CONVERT LBA ADDRESS TO CARD SPECIFIC ADDRESS IN CMD PARMS
; V1 CARDS REQUIRE BYTE ADDRESSING, SO A TRANSLATION IS DONE IN THAT CASE
;
SD_SETADDR:
LD A,(IY+SD_TYPE) ; GET CARD TYPE
PUSH AF ; SAVE IT
LD A,SD_LBA ; OFFSET OF LBA VALUE
CALL LDHLIYA ; HL := IY + A, REG A TRASHED
CALL LD32 ; LOAD IT TO DE:HL, AF IS TRASHED
POP AF ; GET CARD TYPE BACK
CP SD_TYPESDHC ; IS IT V2 OR BETTER?
JR NC,SD_SETADDR1 ; IF SO, BYPASS TRANSLATION
;
; TRANSLATE BLOCK ADDRESS TO BYTE ADDRESS FOR V1 CARDS
LD D,E
LD E,H
LD H,L
LD L,0
SLA L
RL H
RL E
RL D
;
SD_SETADDR1:
; STORE RESULTANT ADDRESS INTO PARMS (BIG ENDIAN!)
PUSH HL ; SAVE LOW WORD OF ADDRESS
LD HL,SD_CMDP0 ; POINT TO START OF PARM BYTES
LD (HL),D ; SAVE MSB OF HI WORD
INC HL ; NEXT BYTE
LD (HL),E ; SAVE LSB OF HI WORD
INC HL ; NEXT BYTE
POP DE ; RECOVER LOW WORD OF ADDRESS INTO DE
LD (HL),D ; SAVE MSB OF LO WORD
INC HL ; NEXT BYTE
LD (HL),E ; SAVE LSB OF LO WORD
RET ; DONE
;
;=============================================================================
; COMMAND PROCESSING
;=============================================================================
;
; PUT CARD IN IDLE STATE
;
SD_GOIDLE:
CALL SD_GOIDLE1 ; FIRST ATTEMPT
RET Z ; DONE IF SUCCEEDED
; FALL THRU FOR SECOND ATTEMPT IF NEEDED
;
SD_GOIDLE1:
; SEEMS TO HELP SOME CARDS?
;CALL SD_SELECT ; ASSERT CS
;CALL SD_DONE ; SEND 8 CLOCKS AND DEASSERT CS
; SMALL DELAY HERE HELPS SOME CARDS
;;LD DE,300 ; 16US * 300 = ~5MS
;LD DE,60 ; 16US * 60 = ~1MS
;CALL VDELAY ; CPU SPEED NORMALIZED DELAY
; PUT CARD IN IDLE STATE
LD A,SD_CMD_GO_IDLE_STATE ; CMD0 = ENTER IDLE STATE
CALL SD_INITCMD ; INIT COMMAND BUFFER
LD A,$95 ; CRC FOR GO_IDLE_STATE COMMAND IS $95
LD (SD_CMDCRC),A ; SET CRC
CALL SD_EXECCMDND ; EXECUTE COMMAND W/ NO DATA RETURNED
RET NZ ; ABORT ON ERROR
LD A,(SD_RC) ; GET CARD RESULT
DEC A ; MAP EXPECTED $01 -> $00
RET Z ; ALL IS GOOD, RETURN WITH A=0 AND Z SET
JP SD_ERRCMD ; SET COMMAND ERROR VALUE
;
; INITIALIZE COMMAND BUFFER
; COMMAND BYTE IN ACCUM
; HL AND AF DESTROYED
;
SD_INITCMD:
LD HL,SD_CMDBUF ; POINT TO START OF BUFFER
LD (HL),A ; SET THE COMMAND BYTE
XOR A ; CLEAR ACCUM
LD B,7 ; PREPARE TO CLEAR NEXT 7 BYTES (PARMS, CRC, RC, TOK)
SD_INITCMD1:
INC HL ; POINT TO NEXT BYTE
LD (HL),A ; CLEAR IT
DJNZ SD_INITCMD1 ; LOOP TILL DONE
RET
;
; EXECUTE APP COMMAND
;
SD_EXECACMD:
LD A,SD_CMD_APP_CMD ; APP_CMD, AN APP CMD IS NEXT
CALL SD_INITCMD ; SETUP COMMAND BUFFER
JR SD_EXECCMDND ; EXEC COMMAND W/ NO DATA RETURNED
;
; EXECUTE COMMAND WITH NO DATA
;
SD_EXECCMDND:
CALL SD_EXECCMD ; RUN THE COMMAND
JP Z,SD_DONE ; RETURN THRU SD_DONE IF NO ERROR
RET ; ERROR STATUS, JUST RETURN, SD_DONE WAS ALREADY RUN
;
; EXECUTE A COMMAND
; WILL FINISH TRANSACTION IF ERROR OCCURS
; RETURNS STATUS IN A WITH ZF SET ACCORDINGLY
;
SD_EXECCMD:
#IF (SDTRACE >= 3)
PUSH AF
CALL SD_PRTPREFIX
LD DE,SD_CMDBUF
PRTS(" CMD$")
LD A,6
CALL PRTHEXBUF
LD DE,SD_STR_ARROW
CALL WRITESTR
POP AF
#ENDIF
;
CALL SD_SELECT
;
#IF (SD_NOPULLUP)
; DO NOT WAIT FOR READY PRIOR TO CMD0! THIS HACK IS REQUIRED BY
; STUPID SD CARD ADAPTERS THAT NOW OMIT THE MISO PULL-UP. SEE
; COMMENTS AT TOP OF THIS FILE.
LD A,(SD_CMDBUF)
CP SD_CMD_GO_IDLE_STATE
JR Z,SD_EXECCMD0
#ENDIF
;
; WAIT FOR CARD TO BE READY
CALL SD_WAITRDY ; WAIT FOR CARD TO BE READY FOR A COMMAND
JP NZ,SD_ERRRDYTO ; HANDLE TIMEOUT ERROR
;
SD_EXECCMD0:
; SEND THE COMMAND
LD HL,SD_CMDBUF ; POINT TO COMMAND BUFFER
LD E,6 ; COMMANDS ARE 6 BYTES
SD_EXECCMD1:
LD A,(HL) ; PREPARE TO SEND NEXT BYTE
CALL SD_PUT ; SEND IT
INC HL ; POINT TO NEXT BYTE
DEC E ; DEC LOOP COUNTER
JR NZ,SD_EXECCMD1 ; LOOP TILL DONE W/ ALL 6 BYTES
;
#IF (SD_NOPULLUP)
; THE FIRST FILL BYTE IS DISCARDED! THIS HACK IS REQUIRED BY
; STUPID SD CARD ADAPTERS THAT NOW OMIT THE MISO PULL-UP. SEE
; COMMENTS AT TOP OF THIS FILE.
;
;CALL SD_GET ; GET A BYTE AND DISCARD IT
;
; THE Z180 -K REVISION CSIO DOES NOT KEEP MOSI HIGH WHEN
; RECEIVING VIA MISO. INSTEAD MOSI IS LEFT AT WHICHEVER LOGIC
; LEVEL IT WAS LAST SET TO. THIS CAUSES SOME SD CARDS A PROBLEM
; BECAUSE THEY EXPECT MOSI TO BE CONSISTENTLY HIGH WHEN IDLE.
; BY USING A PUT INSTEAD OF A GET, WE CAN FORCE MOSI TO BE
; LEFT AT THE PROPER LOGIC LEVEL. THE SD CARD DOES NOT CARE
; IF A PUT OR A GET IS USED TO IGNORE THE BYTE BECAUSE THE
; CLOCK RUNS IN EITHER CASE.
LD A,$FF ; KEEP MOSI HI
CALL SD_PUT ; SEND 8 CLOCKS
#ENDIF
;
; GET RESULT
LD E,0 ; INIT TIMEOUT LOOP COUNTER
SD_EXECCMD2:
CALL SD_GET ; GET A BYTE FROM THE CARD
OR A ; SET FLAGS
JP P,SD_EXECCMD3 ; IF HIGH BIT IS 0, WE HAVE RESULT
DEC E ; OTHERWISE DECREMENT LOOP COUNTER
JR NZ,SD_EXECCMD2 ; AND LOOP UNTIL TIMEOUT
JP SD_ERRCMDTO
;
SD_EXECCMD3:
; COMMAND COMPLETE, SAVE RC AND PRINT DIAGNOSTICS AS APPROPRIATE
LD (SD_RC),A ; RECORD THE RESULT
#IF (SDTRACE >= 3)
CALL SD_PRTRC ; IF MAX TRACING, PRINT RC
#ENDIF
#IF (DSKYENABLE)
PUSH AF
CALL SD_DSKY ; IF USING DSKY, SHOW IT THERE
POP AF
#ENDIF
AND ~$01 ; MASK OFF IDLE BIT AND SET FLAGS
RET Z ; IF RC = 0, NO ERROR, RETURN
CALL SD_DONE ; IF ERROR, COMPLETE TRANSACTION
JP SD_ERRCMD ; ... AND HANDLE IT
;
; SD_GETDATA
;
SD_GETDATA:
#IF (SDMODE == SDMODE_MT)
LD DE,$7FFF ; LOOP MAX (TIMEOUT)
SD_GETDATA1:
IN A,(SD_RDTR)
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 ; SAVE TOKEN VALUE
#IF (SDTRACE >= 3)
PUSH AF
CALL SD_PRTTOK
POP AF
#ENDIF
CP $FE ; PACKET START?
JR NZ,SD_GETDATA4 ; NOPE, ABORT, A HAS ERROR CODE
LD D,B ; SIZE TO DB
LD B,C
LD C,(SD_RDTR)
INC B
DEC B
JR Z,SD_GETDATA3
INC D
SD_GETDATA3:
INIR ; GET BLOCK
DEC D
JR NZ,SD_GETDATA3 ; LOOP FOR ALL BYTES
IN A,(SD_RDTR) ; DISCARD CRC BYTE 1
IN A,(SD_RDTR) ; DISCARD CRC BYTE 2
#ELSE
PUSH HL ; SAVE DESTINATION ADDRESS
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 ; SAVE TOKEN VALUE
#IF (SDTRACE >= 3)
PUSH AF
CALL SD_PRTTOK
POP AF
#ENDIF
POP DE ; RESTORE LENGTH TO RECEIVE
POP HL ; RECOVER DEST ADDRESS
CP $FE ; PACKET START?
JR NZ,SD_GETDATA4 ; NOPE, ABORT, A HAS ERROR CODE
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
#ENDIF
XOR A ; RESULT IS ZERO
SD_GETDATA4:
RET
;
; SD_PUTDATA
;
SD_PUTDATA:
#IF (SDMODE == SDMODE_MT)
LD D,B ; length to DB
LD B,C
LD A,$FE ; PACKET START
OUT (SD_WRTR),A ; SEND IT
LD C,SD_WRTR
INC B ; IF B!=0
DEC B
JR Z,SD_PUTDATA1
INC D ; THEN D=D+1
SD_PUTDATA1:
OTIR ; SEND B BYTES
DEC D
JR NZ,SD_PUTDATA1 ; LOOP FOR ALL BYTES
LD A,$FF ; DUMMY CRC BYTE
OUT (SD_WRTR),A
OUT (SD_WRTR),A ; SEND IT TWICE
LD DE,$7FFF ; LOOP MAX (TIMEOUT)
SD_PUTDATA2:
IN A,(SD_RDTR)
#ELSE
PUSH HL ; SAVE SOURCE ADDRESS
PUSH BC ; SAVE LENGTH TO SEND
LD A,$FE ; PACKET START
CALL SD_PUT ; SEND IT
POP DE ; RECOVER LENGTH TO SEND
POP HL ; RECOVER SOURCE ADDRESS
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
#ENDIF
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:
LD (SD_TOK),A
#IF (SDTRACE >= 3)
PUSH AF
CALL SD_PRTTOK
POP AF
#ENDIF
AND $1F
CP $05
RET NZ
XOR A
RET
;
; WAIT FOR CARD TO BE READY ($FF). MUST ALREADY BE SELECTED.
;
SD_WAITRDY:
LD DE,$FFFF ; 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
;
; FINISH A TRANSACTION - PRESERVE AF
;
; PER SPEC: AFTER THE LAST SPI BUS TRANSACTION, THE HOST IS REQUIRED, TO PROVIDE
; 8 (EIGHT) CLOCK CYCLES FOR THE CARD TO COMPLETE THE OPERATION BEFORE SHUTTING
; DOWN THE CLOCK. THROUGHOUT THIS 8 CLOCKS PERIOD THE STATE OF THE CS SIGNAL IS
; IRRELEVANT. IT CAN BE ASSERTED OR DE-ASSERTED.
;
; NOTE THAT I HAVE FOUND AT LEAST ONE MMC CARD THAT FAILS UNLESS THE CS SIGNAL
; REMAINS ACTIVE DURING THE 8 CLOCKS, SO THE CLOCKS ARE SENT BEFORE DESELECTING THE CARD.
;
SD_DONE:
PUSH AF
LD A,$FF
CALL SD_PUT
CALL SD_DESELECT
POP AF
RET
;
;=============================================================================
; HARDWARE INTERFACE ROUTINES
;=============================================================================
;
; PERFORM HARDWARE SPECIFIC INITIALIZATION
;
SD_SETUP:
;
#IF (SDMODE == SDMODE_PPI)
; PPISD IS DESIGNED TO CORESIDE ON THE SAME PARALLEL PORT
; AS A DSKY. SEE DSKY.ASM FOR DETAILS.
LD A,82H ; PPI PORT A=OUT, B=IN, C=OUT
OUT (SD_PPIX),A
#ENDIF
;
#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC))
; CSIO SETUP FOR Z180 CSIO
; LD A,2 ; DIV 80, 225KHZ @ 18MHZ CLK
LD A,6 ; DIV 1280, 14KHZ @ 18MHZ CLK
OUT0 (SD_CNTR),A
#ENDIF
;
#IF ((SDMODE == SDMODE_JUHA) | (SDMODE == SDMODE_N8) | (SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_SC))
LD A,(RTCVAL)
LD (SD_OPRVAL),A
OUT (SD_OPRREG),A
#ENDIF
;
#IF ((SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_DSD) | (SDMODE == SDMODE_PPI)| (SDMODE == SDMODE_MT))
LD A,SD_OPRDEF
LD (SD_OPRVAL),A
OUT (SD_OPRREG),A
#ENDIF
;
#IF (SDMODE == SDMODE_UART)
IN A,(SD_OPRREG) ; OPRREG == SIO_MCR
AND ~SD_OPRMSK ; MASK OFF SD CONTROL BITS
OR SD_OPRDEF ; SET DEFAULT BITS
LD (SD_OPRVAL),A ; RECORD THE WORKING VALLUE
OUT (SD_OPRREG),A ; OPRREG == SIO_MCR
#ENDIF
;
XOR A
RET
;
; TAKE ANY ACTIONS REQUIRED TO SELECT DESIRED PHYSICAL UNIT
; UNIT IS SPECIFIED IN A
;
SD_SELUNIT:
LD A,(IY+SD_DEV) ; GET CURRENT DEVICE
;
#IF (SDMODE == SDMODE_DSD)
; SELECT REQUESTED UNIT
OUT (SD_SELREG),A ; ACTUALLY SELECT THE CARD
#ENDIF
XOR A ; SIGNAL SUCCESS
RET ; DONE
;
; CHECK FOR CARD DETECT (NZ = MEDIA PRESENT)
;
SD_CHKCD:
#IF ((SDMODE == SDMODE_DSD) | (SDMODE == SDMODE_MK4))
IN A,(SD_OPRREG) ; GET OPERATIONS REGISTER
BIT 5,A ; TEST CARD DETECT BIT
#ELSE
OR $FF ; ASSUME CARD PRESENT
#ENDIF
RET ; DONE
;
; CHECK FOR WRITE PROTECT (NZ = WRITE PROTECTED)
;
SD_CHKWP:
#IF ((SDMODE == SDMODE_DSD) | (SDMODE == SDMODE_MK4))
IN A,(SD_OPRREG) ; GET OPERATIONS REGISTER
BIT 4,A ; TEST WP BIT
#ELSE
XOR A ; WP NOT SUPPORTED BY HARDWARE, ASSUME WP OFF
#ENDIF
RET ; AND RETURN
;
; SELECT CARD
;
SD_SELECT:
; ; FINISH SENDING BEFORE ASSERTING CS!
;#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC))
; CALL SD_WAITTX
;#ENDIF
;
LD A,(IY+SD_DEV) ; GET CURRENT DEVICE
OR A ; SET FLAGS
LD A,(SD_OPRVAL) ; GET CURRENT OPRVAL BACK
JR NZ,SD_SELECT1 ; IF NOT ZERO, DO SECONDARY
; ASSERT PRIMARY CS, DEASSERT SECONDARY (IF ANY)
OR SD_CS0
#IF (SD_DEVCNT > 1)
AND ~SD_CS1
#ENDIF
JR SD_SELECT2
SD_SELECT1:
; DEASSERT PRIMARY CS, ASSERT SECONDARY (IF ANY)
AND ~SD_CS0
#IF (SD_DEVCNT > 1)
OR SD_CS1
#ENDIF
SD_SELECT2:
; ADJUST BIT(S) FOR INTERFACES USING INVERTED CS BITS
#IF ((SDMODE == SDMODE_PPI) | (SDMODE == SDMODE_UART) | (SDMODE == SDMODE_SC))
#IF (SD_DEVCNT > 1)
XOR SD_CS0 | SD_CS1
#ELSE
XOR SD_CS0
#ENDIF
#ENDIF
LD (SD_OPRVAL),A
OUT (SD_OPRREG),A
;;
;#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC))
; CALL DLY32 ; DELAY FOR FINAL BIT
;#ENDIF
;
RET
;
; DESELECT CARD
;
SD_DESELECT:
#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC))
; DON'T REMOVE CS UNTIL WE ARE DONE SENDING!
CALL SD_WAITTX ; WAIT FOR TE TO CLEAR
;
; ACCORDING TO Z180 DOCS, IT MAY TAKE UP TO 1 BIT TIME TO
; FINISH SENDING AFTER TE IS CLEARED. THE DELAY BELOW WILL
; DO THIS FOR THE SLOWEST POSSIBLE SEND RATE WHICH IS
; CLK / 1320, SO DELAY AT LEAST 1320 T-STATES
;
; IN PRACTICE, A SMALLER DELAY IS FINE BASED ON LOGIC ANALYZER
; TRACES.
CALL DLY32 ; DELAY FOR FINAL BIT
#ENDIF
;
LD A,(SD_OPRVAL)
#IF (SD_DEVCNT > 1)
AND ~(SD_CS0 | SD_CS1)
#ELSE
AND ~SD_CS0
#ENDIF
; ADJUST BIT(S) FOR INTERFACES USING INVERTED CS BITS
#IF ((SDMODE == SDMODE_PPI) | (SDMODE == SDMODE_UART) | (SDMODE == SDMODE_SC))
#IF (SD_DEVCNT > 1)
XOR SD_CS0 | SD_CS1
#ELSE
XOR SD_CS0
#ENDIF
#ENDIF
LD (SD_OPRVAL),A
OUT (SD_OPRREG),A
RET
;
#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC))
;
; CSIO WAIT FOR TRANSMIT READY (TX REGSITER EMPTY)
;
SD_WAITTX:
IN0 A,(SD_CNTR) ; GET CSIO STATUS
BIT 4,A ; TX EMPTY?
JR NZ,SD_WAITTX ; LOOP WHILE BUSY
RET ; DONE
;
; CSIO WAIT FOR RECEIVER READY (BYTE AVAILABLE)
;
SD_WAITRX:
IN0 A,(SD_CNTR) ; WAIT FOR RECEIVER TO FINISH
BIT 5,A ; RX EMPTY?
JR NZ,SD_WAITRX ; LOOP WHILE BUSY
RET ; DONE
;
#ENDIF
;
; SEND ONE BYTE
;
SD_PUT:
;
#IF (SDMODE == SDMODE_MT)
OUT (SD_WRTR),A
#ELSE
;
#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC))
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
#IF (SDMODE == SDMODE_UART)
XOR $FF ; DI IS INVERTED ON UART
#ENDIF
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
#ENDIF
RET ; DONE
;
; RECEIVE ONE BYTE
;
;
SD_GET:
;
#IF (SDMODE == SDMODE_MT)
IN A,(SD_RDTR)
#ELSE
#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC))
CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING
IN0 A,(SD_CNTR) ; GET CSIO STATUS
SET 5,A ; START RECEIVER
OUT0 (SD_CNTR),A
CALL SD_WAITRX
IN0 A,(SD_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_UART)
RLA ; ROTATE INP:5 INTO CF
RLA ; "
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
#IF (SDMODE == SDMODE_UART)
XOR $FF ; DO IS INVERTED ON UART
#ENDIF
#ENDIF
#ENDIF
RET
;
; SET CSIO TO DEFAULT SPEED
;
#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC))
;
SD_CSIO_DEF:
; SET CSIO FOR DEFAULT OPERATION
PUSH AF ; PRESERVE AF
CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING
CALL DLY32 ; WAIT A BIT MORE FOR FINAL BIT
LD A,Z180_CNTR_DEF ; DIV 1280, 14KHZ @ 18MHZ CLK
OUT0 (SD_CNTR),A ; DO IT
POP AF ; RESTORE AF
RET
;
#ENDIF
;
;
;=============================================================================
; ERROR HANDLING AND DIAGNOSTICS
;=============================================================================
;
; ERROR HANDLERS
;
SD_INVUNIT:
LD A,SD_STINVUNIT
JR SD_ERR2 ; SPECIAL CASE FOR INVALID UNIT
;
SD_ERRRDYTO:
LD A,SD_STRDYTO
JR SD_ERR
;
SD_ERRINITTO:
LD A,SD_STINITTO
JR SD_ERR
;
SD_ERRCMDTO:
LD A,SD_STCMDTO
JR SD_ERR
;
SD_ERRCMD:
LD A,SD_STCMDERR
JR SD_ERR
;
SD_ERRDATA:
LD A,SD_STDATAERR
JR SD_ERR
;
SD_ERRDATATO:
LD A,SD_STDATATO
JR SD_ERR
;
SD_ERRCRC:
LD A,SD_STCRCERR
JR SD_ERR
;
SD_NOMEDIA:
LD A,SD_STNOMEDIA
JR SD_ERR
;
SD_WRTPROT:
LD A,SD_STWRTPROT
JR SD_ERR2 ; DO NOT UPDATE UNIT STATUS!
;
SD_ERR:
LD (IY+SD_STAT),A ; SAVE NEW STATUS
SD_ERR2:
#IF (SDTRACE >= 2)
CALL SD_PRTSTAT
CALL SD_REGDUMP
#ENDIF
PUSH AF
CALL SD_DESELECT ; De-select if there was an error
POP AF
OR A ; SET FLAGS
RET
;
;
;
SD_PRTERR:
RET Z ; DONE IF NO ERRORS
; FALL THRU TO SD_PRTSTAT
;
; PRINT STATUS STRING
;
SD_PRTSTAT:
PUSH AF
PUSH DE
PUSH HL
OR A
LD DE,SD_STR_STOK
JR Z,SD_PRTSTAT1
INC A
LD DE,SD_STR_STINVUNIT
JR Z,SD_PRTSTAT2 ; INVALID UNIT IS SPECIAL CASE
INC A
LD DE,SD_STR_STRDYTO
JR Z,SD_PRTSTAT1
INC A
LD DE,SD_STR_STINITTO
JR Z,SD_PRTSTAT1
INC A
LD DE,SD_STR_STCMDTO
JR Z,SD_PRTSTAT1
INC A
LD DE,SD_STR_STCMDERR
JR Z,SD_PRTSTAT1
INC A
LD DE,SD_STR_STDATAERR
JR Z,SD_PRTSTAT1
INC A
LD DE,SD_STR_STDATATO
JR Z,SD_PRTSTAT1
INC A
LD DE,SD_STR_STCRCERR
JR Z,SD_PRTSTAT1
INC A
LD DE,SD_STR_STNOMEDIA
JR Z,SD_PRTSTAT1
INC A
LD DE,SD_STR_STWRTPROT
JR Z,SD_PRTSTAT1
LD DE,SD_STR_STUNK
SD_PRTSTAT1:
CALL SD_PRTPREFIX ; PRINT UNIT PREFIX
JR SD_PRTSTAT3
SD_PRTSTAT2:
CALL NEWLINE
PRTS("SD:$") ; NO UNIT NUM IN PREFIX FOR INVALID UNIT
SD_PRTSTAT3:
CALL PC_SPACE ; FORMATTING
CALL WRITESTR
POP HL
POP DE
POP AF
RET
;
SD_PRTRC:
PUSH AF
PUSH DE
LD DE,SD_STR_RC
CALL WRITESTR
LD A,(SD_RC)
CALL PRTHEXBYTE
POP DE
POP AF
RET
;
SD_PRTTOK:
PUSH AF
PUSH DE
LD DE,SD_STR_TOK
CALL WRITESTR
LD A,(SD_TOK)
CALL PRTHEXBYTE
POP DE
POP AF
RET
;
;
;
SD_REGDUMP:
PUSH AF
PUSH BC
PUSH HL
CALL PC_SPACE
CALL PC_LBKT
LD HL,SD_CMDBUF
LD B,8
SD_REGDUMP1:
LD A,(HL)
INC HL
CALL PRTHEXBYTE
DJNZ SD_REGDUMP1
CALL PC_RBKT
POP HL
POP BC
POP AF
RET
;
; PRINT DIAGNONSTIC PREFIX
;
SD_PRTPREFIX:
PUSH AF
CALL NEWLINE
PRTS("SD$")
LD A,(IY+SD_DEV) ; GET CURRENT DEVICE NUM
ADD A,'0'
CALL COUT
CALL PC_COLON
POP AF
RET
;
; DISPLAY COMMAND, LOW ORDER WORD OF PARMS, AND RC
;
#IF (DSKYENABLE)
SD_DSKY:
PUSH AF
PUSH HL
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 HL
POP AF
RET
#ENDIF
;
;=============================================================================
; STRING DATA
;=============================================================================
;
SD_STR_ARROW .TEXT " -->$"
SD_STR_RC .TEXT " RC=$"
SD_STR_TOK .TEXT " TOK=$"
SD_STR_CSD .TEXT " CSD =$"
SD_STR_CID .TEXT " CID =$"
SD_STR_SCR .TEXT " SCR =$"
SD_STR_SDTYPE .TEXT " SD CARD TYPE ID=$"
;
SD_STR_STOK .TEXT "OK$"
SD_STR_STINVUNIT .TEXT "INVALID UNIT$"
SD_STR_STRDYTO .TEXT "READY TIMEOUT$"
SD_STR_STINITTO .TEXT "INITIALIZATION TIMEOUT$"
SD_STR_STCMDTO .TEXT "COMMAND TIMEOUT$"
SD_STR_STCMDERR .TEXT "COMMAND ERROR$"
SD_STR_STDATAERR .TEXT "DATA ERROR$"
SD_STR_STDATATO .TEXT "DATA TIMEOUT$"
SD_STR_STCRCERR .TEXT "CRC ERROR$"
SD_STR_STNOMEDIA .TEXT "NO MEDIA$"
SD_STR_STWRTPROT .TEXT "WRITE PROTECTED$"
SD_STR_STUNK .TEXT "UNKNOWN$"
SD_STR_TYPEUNK .TEXT "UNK$"
SD_STR_TYPEMMC .TEXT "MMC$"
SD_STR_TYPESDSC .TEXT "SDSC$"
SD_STR_TYPESDHC .TEXT "SDHC$"
SD_STR_TYPESDXC .TEXT "SDXC$"
;
;=============================================================================
; DATA STORAGE
;=============================================================================
;
SD_OPRVAL .DB 0 ; CURRENT OPR REG VALUE
SD_LCNT .DB 0 ; LOOP COUNTER
SD_CMDVAL .DB 0 ; PENDING COMMAND FOR IO FUCNTIONS
SD_BLKCNT .DB 0 ; BLOCK COUNT REQUESTED FOR IO FUNCTIONS
;
SD_BUF .FILL 16,0 ; WORK BUFFER
;
SD_CMDBUF: ; START OF STD CMD BUF
SD_CMD .DB 0 ; COMMAND BYTE
SD_CMDP0 .DB 0 ; FIRST PARM BYTE (MSB)
SD_CMDP1 .DB 0
SD_CMDP2 .DB 0
SD_CMDP3 .DB 0 ; LAST PARM BYTE (LSB)
SD_CMDCRC .DB 0 ; CRC
;
SD_RC .DB 0 ; RETURN CODE FROM CMD
SD_TOK .DB 0 ; TOKEN FROM DATA XFR
;
SD_DSKBUF .DW 0 ; ADR OF ACTIVE DISK BUFFER
;
;=============================================================================
; HELPER ROUTINES
;=============================================================================
;
; MSB<-->LSB MIRROR BITS IN A, RESULT IN C
;
MIRROR:
#IF (((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC)) & SDCSIOFAST)
; 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
#ELSE
; SLOWER BUT LESS CODE SPACE
LD C,A ; A = 76543210
RLCA
RLCA ; A = 54321076
XOR C
AND 0AAH
XOR C ; A = 56341270
LD C,A
RLCA
RLCA
RLCA ; A = 41270563
RRC C ; C = 05634127
XOR C
AND 066H
XOR C ; A = 01234567
LD C,A ; RETURN RESULT IN C
RET
#ENDIF
;
; LOOKUP TABLE TO MIRROR BITS IN A BYTE
;
#IF (((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC)) & SDCSIOFAST)
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