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.
 
 
 
 
 
 

1737 lines
48 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
; ------------ ------- ------- ------- ------- ------- ------- ------- -------
; CS (DAT3) LO -> RTC:2 RTC:2 RTC:2 ~PC:4 ~MCR:3 OPR:2 MK4_SD:2
; CLK HI -> RTC:1 RTC:1 N/A PC:1 ~MCR:2 OPR:1 N/A
; DI (CMD) HI -> RTC:0 RTC:0 N/A PC:0 ~MCR:0 OPR:0 N/A
; DO (DAT0) HI -> RTC:7 RTC:6 N/A PB:7 ~MSR:5 OPR:0 N/A
;------------------------------------------------------------------------------
;
; 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 (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 4
; (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
;
#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 Z180_CNTR
SD_TRDR .EQU Z180_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_OPRREG .EQU SIO_MCR ; UART MCR PORT (OUTPUT: CS, CLK, DIN)
SD_OPRDEF .EQU %00001100 ; QUIESCENT STATE
SD_INPREG .EQU SIO_MSR ; INPUT REGISTER IS MSR
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
;
#IF (SDMODE == SDMODE_MK4) ; MARK IV (CSIO STYLE INTERFACE)
SD_UNITCNT .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS)
SD_OPRREG .EQU MK4_SD ; DEDICATED MK4 SDCARD REGISTER
SD_OPRDEF .EQU %00000000 ; QUIESCENT STATE
SD_CS .EQU %00000100 ; SELECT ACTIVE
SD_CNTR .EQU Z180_CNTR
SD_TRDR .EQU Z180_TRDR
#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 ; 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
;
; PER UNIT DATA OFFSETS (CAREFUL NOT TO EXCEED PER UNIT SPACE IN SD_UNITDATA)
; SEE SD_UNITDATA IN DATA STORAGE BELOW
;
SD_STAT .EQU 0 ; LAST STATUS (1 BYTE)
SD_TYPE .EQU 1 ; CARD TYPE (1 BYTE)
SD_CAPACITY .EQU 2 ; CARD CAPACITY (1 DWORD/4 BYTES)
;
; MACRO TO RETURN POINTER TO FIELD WITHIN UNIT DATA
;
#DEFINE SD_DPTR(FIELD) CALL SD_DPTRIMP \ .DB FIELD
;
;=============================================================================
; INITIALIZATION ENTRY POINT
;=============================================================================
;
SD_INIT:
PRTS("SD:$")
;
; SETUP THE DISPATCH TABLE ENTRIES
;
LD B,SD_UNITCNT ; LOOP CONTROL
LD C,0 ; PHYSICAL UNIT INDEX
SD_INIT0:
PUSH BC ; SAVE LOOP CONTROL
LD B,C ; PHYSICAL UNIT
LD C,DIODEV_SD ; DEVICE TYPE
LD DE,0 ; UNIT DATA BLOB ADDRESS
CALL DIO_ADDENT ; ADD ENTRY, BC IS NOT DESTROYED
POP BC ; RESTORE LOOP CONTROL
INC C ; NEXT PHYSICAL UNIT
DJNZ SD_INIT0 ; LOOP UNTIL DONE
;
#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(" 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_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
;
PRTS(" UNITS=$")
LD A,SD_UNITCNT
CALL PRTDECB
;
; INITIALIZE THE SD INTERFACE NOW
CALL SD_SETUP ; DO HARDWARE SETUP/INIT
RET NZ ; ABORT ON ERROR
;
; CLEAR OUT ALL DATA (FOR ALL UNITS)
LD HL,SD_UNITDATA
LD BC,SD_DATALEN
XOR A
CALL FILL
;
; INITIALIZE INDIVIDUAL UNIT(S)
LD B,SD_UNITCNT ; INIT LOOP COUNTER TO UNIT COUNT
LD C,0 ; INIT UNIT INDEX TO ZERO
SD_INIT1:
PUSH BC ; SAVE LOOP COUNTER/INDEX
LD A,C ; UNIT ID TO A
CALL SD_INITUNIT ; INITIALIZE IT
#IF (SDTRACE < 2)
CALL NZ,SD_PRTSTAT ; IF ERROR, SHOW IT
#ENDIF
POP BC ; RESTORE LOOP COUNTER/INDEX
INC C ; INCREMENT UNIT INDEX
DJNZ SD_INIT1 ; DECREMENT LOOP COUNTER AND LOOP AS NEEDED
;
RET ; DONE
;
; INITIALIZE UNIT DESIGNATED IN ACCUM
;
SD_INITUNIT
LD (SD_UNIT),A ; SET CURRENT UNIT
;
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
;
; PRINT CARD TYPE
PRTS(" TYPE=$")
SD_DPTR(SD_TYPE) ; SET HL TO ADR OF CARD TYPE, TRASHES AF
LD A,(HL)
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
;
PRTS(" BLOCKS=0x$") ; LABEL
SD_DPTR(SD_CAPACITY) ; SET HL TO ADR OF CARD CAPACITY, TRASHES AF
CALL LD32 ; GET THE VALUE
CALL PRTHEX32 ; PRINT IT
;
; CONVERT VALUE TO MEGABYTES AND PRINT IT
LD B,11 ; SHIFT 11 BITS TO CONVERT FROM
CALL SRL32 ; ... 512 BYTE BLOCKS TO MEGABYTES
;
PRTS(" SIZE=$") ; PRINT LABEL
CALL PRTDEC ; PRINT VALUE
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
RET ; DONE
;
;=============================================================================
; FUNCTION DISPATCH ENTRY POINT
;=============================================================================
;
SD_DISPATCH:
; VERIFY AND SAVE THE TARGET DEVICE/UNIT LOCALLY IN DRIVER
LD A,C ; DEVICE/UNIT FROM C
AND $0F ; ISOLATE UNIT NUM
CP SD_UNITCNT
CALL NC,PANIC ; PANIC IF TOO HIGH
LD (SD_UNIT),A ; SAVE IT
;
; DISPATCH ACCORDING TO DISK SUB-FUNCTION
LD A,B ; GET REQUESTED FUNCTION
AND $0F ; ISOLATE SUB-FUNCTION
JP Z,SD_STATUS ; SUB-FUNC 0: STATUS
DEC A
JP Z,SD_RESET ; SUB-FUNC 1: RESET
DEC A
JP Z,SD_SEEK ; SUB-FUNC 2: SEEK
DEC A
JP Z,SD_READ ; SUB-FUNC 3: READ SECTORS
DEC A
JP Z,SD_WRITE ; SUB-FUNC 4: WRITE SECTORS
DEC A
JP Z,SD_VERIFY ; SUB-FUNC 5: VERIFY SECTORS
DEC A
JP Z,SD_FORMAT ; SUB-FUNC 6: FORMAT TRACK
DEC A
JP Z,SD_DEVICE ; SUB-FUNC 7: DEVICE REPORT
DEC A
JP Z,SD_MEDIA ; SUB-FUNC 8: MEDIA REPORT
DEC A
JP Z,SD_DEFMED ; SUB-FUNC 9: DEFINE MEDIA
DEC A
JP Z,SD_CAP ; SUB-FUNC 10: REPORT CAPACITY
DEC A
JP Z,SD_GEOM ; SUB-FUNC 11: REPORT GEOMETRY
;
SD_VERIFY:
SD_FORMAT:
SD_DEFMED:
CALL PANIC ; INVALID SUB-FUNCTION
;
;
;
SD_READ:
LD (SD_DSKBUF),HL ; SAVE DISK BUFFER ADDRESS
#IF (SDTRACE == 1)
LD HL,SD_PRTERR ; SET UP SD_PRTERR
PUSH HL ; ... TO FILTER ALL EXITS
#ENDIF
CALL SD_SELUNIT
; READ A SECTOR
LD C,SD_CMD_READ_SNGL_BLK ; SET READ_SINGLE_BLOCK COMMAND
JP SD_SECTIO ; DO SECTOR I/O
;
;
;
SD_WRITE:
LD (SD_DSKBUF),HL ; SAVE DISK BUFFER ADDRESS
#IF (SDTRACE == 1)
LD HL,SD_PRTERR ; SET UP SD_PRTERR
PUSH HL ; ... TO FILTER ALL EXITS
#ENDIF
CALL SD_SELUNIT
CALL SD_CHKWP ; CHECK FOR WRITE PROTECT
JP NZ,SD_WRTPROT ; HANDLE IT IF SO
; WRITE A SECTOR
LD C,SD_CMD_WRITE_BLOCK ; SET WRITE_BLOCK COMMAND
JP SD_SECTIO ; DO SECTOR I/O
;
SD_STATUS:
; RETURN UNIT STATUS
SD_DPTR(SD_STAT) ; HL := ADR OF STATUS, AF TRASHED
LD A,(HL) ; GET STATUS OF SELECTED UNIT
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 == 1)
CALL SD_PRTERR ; PRINT ANY ERRORS
#ENDIF
OR A ; SET RESULT FLAGS
RET
;
;
;
SD_DEVICE:
LD D,DIODEV_SD ; D := DEVICE TYPE
LD E,C ; E := PHYSICAL UNIT
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
;
SD_DPTR(SD_STAT) ; POINT TO UNIT STATUS
LD A,(HL) ; GET STATUS
OR A ; SET FLAGS
JR NZ,SD_MEDIA1 ; ERROR ACTIVE, TO 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_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:
SD_DPTR(SD_STAT) ; HL := ADR OF STATUS, AF TRASHED
LD A,(HL) ; GET STATUS OF SELECTED UNIT
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
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 BC,HSTLBA ; POINT TO LBA STORAGE
CALL ST32 ; SAVE LBA ADDRESS
XOR A ; SIGNAL SUCCESS
RET ; AND RETURN
;
;
;
SD_CAP:
SD_DPTR(SD_CAPACITY) ; POINT HL TO CAPACITY OF CUR UNIT
CALL LD32 ; GET THE CURRENT CAPACITY DO DE:HL
LD BC,512 ; 512 BYTES PER BLOCK
XOR A ; SIGNAL SUCCESS
RET ; AND DONE
;
;
;
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
XOR A ; SIGNAL SUCCESS
RET
;
;=============================================================================
; FUNCTION SUPPORT ROUTINES
;=============================================================================
;
; (RE)INITIALIZE CARD
;
SD_INITCARD:
;
; CLEAR OUT UNIT SPECIFIC DATA
SD_DPTR(0) ; SET HL TO START OF UNIT DATA
LD BC,SD_UNITDATALEN
XOR A
CALL FILL
;
CALL SD_CHKCD ; CHECK CARD DETECT
JP Z,SD_NOMEDIA ; Z=NO MEDIA, HANDLE IF SO
;
; 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
;
; PUT CARD IN IDLE STATE
CALL SD_GOIDLE ; GO TO IDLE
RET NZ ; ABORT IF FAILED
;
SD_INITCARD2:
SD_DPTR(SD_TYPE) ; SET HL TO ADR OF CARD TYPE, TRASHES AF
LD A,SD_TYPESDSC ; ASSUME SDSC CARD TYPE
LD (HL),A ; SAVE IT
;
; 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
; 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:
SD_DPTR(SD_TYPE) ; SET HL TO ADR OF CARD TYPE, TRASHES AF
LD (HL),C ; SAVE IT
#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
#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4))
; 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
XOR A ; ZERO MEANS MAX SPEED
OUT (Z180_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
SD_DPTR(SD_TYPE) ; SET HL TO ADR OF CARD TYPE, TRASHES AF
LD A,(HL) ; GET CARD TYPE
OR A ; SET FLAGS
CALL Z,PANIC ; PANIC IF 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
;
; 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: ; COMMON CODE TO RECORD RESULTANT SIZE (IN DE:HL)
; SAVE DERIVED CAPACITY VALUE
PUSH HL ; SAVE HL
SD_DPTR(SD_CAPACITY) ; SET HL TO ADR OF CARD CAPACITY, TRASHES AF
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)
SD_DPTR(SD_STAT) ; HL := ADR OF STATUS, AF TRASHED
XOR A ; A := 0 (STATUS = OK)
LD (HL),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
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 FOR CARD DETECT HERE AND
; HANDLE AS ERROR.
;
SD_DPTR(SD_STAT) ; HL = ADR OF STATUS, AF TRASHED
LD A,(HL) ; 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 IN HSTLBA TO CARD SPECIFIC ADDRESS IN CMD PARMS
; V1 CARDS REQUIRE BYTE ADDRESSING, SO A TRANSLATION IS DONE IN THAT CASE
;
SD_SETADDR:
SD_DPTR(SD_TYPE) ; SET HL TO ADR OF CARD TYPE, TRASHES AF
LD A,(HL) ; GET CARD TYPE
PUSH AF ; SAVE IT
LD HL,HSTLBA ; POINT TO INCOMING LBA VALUE
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 Z=0 AND Z SET
LD A,SD_STCMDERR ; SET COMMAND ERROR VALUE, NZ ALREADY SET
RET ; AND RETURN
;
; 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
; 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
; 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
;
; 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:
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
XOR A ; RESULT IS ZERO
SD_GETDATA4:
RET
;
; SD_PUTDATA
;
SD_PUTDATA:
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
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
;
; SELECT CARD AND WAIT FOR IT TO BE READY ($FF)
;
SD_WAITRDY:
CALL SD_SELECT ; SELECT CARD
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 DESLECTING 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_JUHA) | (SDMODE == SDMODE_N8) | (SDMODE == SDMODE_DSD))
LD A,SD_OPRDEF
LD (SD_OPRVAL),A
OUT (SD_OPRREG),A
#ENDIF
;
#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4))
; 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 (SD_PPIX),A
LD A,SD_OPRDEF
LD (SD_OPRVAL),A
OUT (SD_OPRREG),A
#ENDIF
;
#IF (SDMODE == SDMODE_UART)
SD_OPRMSK .EQU (SD_CS | SD_CLK | SD_DI)
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,(SD_UNIT)
;
CP SD_UNITCNT ; CHECK VALIDITY (EXCEED UNIT COUNT?)
JP NC,SD_INVUNIT ; HANDLE INVALID UNIT
;
#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:
LD A,(SD_OPRVAL)
#IF ((SDMODE == SDMODE_PPI) | (SDMODE == SDMODE_UART))
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) | (SDMODE == SDMODE_UART))
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
;
#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4))
;
; CSIO WAIT FOR TRANSMIT READY (TX REGSITER EMPTY)
;
SD_WAITTX: ; WAIT FOR TX EMPTY
IN0 A,(SD_CNTR) ; GET CSIO STATUS
BIT 4,A ; TX EMPTY?
JR NZ,SD_WAITTX
RET
;
; CSIO WAIT FOR RECEIVER READY (BYTE AVAILABLE)
;
SD_WAITRX:
IN0 A,(SD_CNTR) ; WAIT FOR RECEIVER TO FINISH
BIT 5,A
JR NZ,SD_WAITRX
RET
;
#ENDIF
;
; SEND ONE BYTE
;
SD_PUT:
#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4))
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
RET ; DONE
;
; RECEIVE ONE BYTE
;
SD_GET:
#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4))
CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING
IN0 A,(Z180_CNTR) ; GET CSIO STATUS
SET 5,A ; START RECEIVER
OUT0 (Z180_CNTR),A
CALL SD_WAITRX
IN0 A,(Z180_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
RET
;
;=============================================================================
; 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:
PUSH HL ; IS THIS NEEDED?
PUSH AF ; SAVE INCOMING STATUS
SD_DPTR(SD_STAT) ; GET STATUS ADR IN HL, AF TRASHED
POP AF ; RESTORE INCOMING STATUS
LD (HL),A ; UPDATE STATUS
POP HL ; IS THIS NEEDED?
SD_ERR2:
#IF (SDTRACE >= 2)
CALL SD_PRTSTAT
CALL SD_REGDUMP
#ENDIF
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:
CALL NEWLINE
PRTS("SD$")
PUSH AF
LD A,(SD_UNIT)
ADD A,'0'
CALL COUT
POP AF
CALL PC_COLON
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_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_UNIT .DB 0 ; ACTIVE UNIT, DEFAULT TO ZERO
SD_DSKBUF .DW 0 ; ACTIVE DISK BUFFER
;
; UNIT SPECIFIC DATA STORAGE
;
SD_UNITDATA .FILL SD_UNITCNT * 8, 0 ; PER UNIT DATA, 8 BYTES
SD_DATALEN .EQU $ - SD_UNITDATA ; LENGTH OF ENTIRE DATA STORAGE FOR ALL UNITS
SD_UNITDATALEN .EQU SD_DATALEN / SD_UNITCNT ; LENGTH OF PER UNIT DATA
;
;=============================================================================
; HELPER ROUTINES
;=============================================================================
;
; IMPLEMENTATION FOR MACRO SD_DPTR
; SET HL TO ADDRESS OF FIELD WITHIN PER UNIT DATA
; HL := ADR OF SD_UNITDATA[(SD_UNIT)][(SP)]
; ENTER WITH TOP-OF-STACK = ADDRESS OF FIELD OFFSET
; AF IS TRASHED
;
SD_DPTRIMP:
LD HL,SD_UNITDATA ; POINT TO START OF UNIT DATA ARRAY
LD A,(SD_UNIT) ; GET CURRENT UNIT NUM
RLCA ; MULTIPLY BY
RLCA ; ... SIZE OF PER UNIT DATA
RLCA ; ... (8 BYTES)
EX (SP),HL ; GET PTR TO FIELD OFFSET VALUE FROM TOS
ADD A,(HL) ; ADD IT TO START OF UNIT DATA IN ACCUM
INC HL ; BUMP HL TO NEXT REAL INSTRUCTION
EX (SP),HL ; AND PUT IT BACK ON STACK, HL GETS ADR OF START OF DATA
JP ADDHLA ; CALC FINAL ADR IN HL AND RETURN
;
; MSB<-->LSB MIRROR BITS IN A, RESULT IN C
;
MIRROR:
#IF (SDCSIOFAST) ; SLOW SPEED, LEAST 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 ; FASTEST BUT USES MOST 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
#ENDIF
;
; LOOKUP TABLE TO MIRROR BITS IN A BYTE
;
#IF (((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4)) & 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