mirror of https://github.com/wwarthen/RomWBW.git
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.
1771 lines
48 KiB
1771 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:
|
|
CALL NEWLINE ; FORMATTING
|
|
PRTS("SD:$")
|
|
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
|
|
;
|
|
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
|
|
;
|
|
;----------------------------------------------------------------------
|
|
; PROBE FOR SD HARDWARE
|
|
;----------------------------------------------------------------------
|
|
;
|
|
; ON RETURN, ZF SET INDICATES HARDWARE FOUND
|
|
;
|
|
SD_PROBE:
|
|
;
|
|
#IF (SDMODE == SDMODE_DSD)
|
|
LD A,$03 ; SET BIT 0 & 1
|
|
OUT (SD_SELREG),A ; WRITE TO SELECT REG
|
|
IN A,(SD_SELREG) ; READ BACK, BIT 1 IS ALWAYS 0
|
|
CP $01 ; ... SO SHOULD READ BACK AS $01
|
|
RET
|
|
#ENDIF
|
|
;
|
|
XOR A ; SIGNAL SUCCESS
|
|
RET ; AND RETURN
|
|
;
|
|
;=============================================================================
|
|
; 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
|
|
LD C,%01010000 ; C := ATTRIBUTES, REMOVABLE, SD CARD
|
|
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_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:
|
|
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_STAT) ; POINT TO UNIT STATUS
|
|
LD A,(HL) ; GET STATUS
|
|
PUSH AF ; SAVE IT
|
|
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
|
|
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:
|
|
;
|
|
; 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
|
|
|