; ;============================================================================= ; MMC/SD/SDHC/SDXC CARD STORAGE DRIVER ;============================================================================= ; ; 1) TESTING ; - TRY TO TEST GOIDLE, FIND CARD THAT REQUIRES 2 REQUESTS ; - DUAL CARDS ; - TEST XC CARD TYPE DETECTION ; - TRY TO GET INIT TO FAIL, REMOVE DELAYS AT START OF GOIDLE? ; ;----------------------------------------------------------------------------------------------------- ; SD Signal Active JUHA N8 CSIO PPI UART DSD MK4 SC MT PIO ; ------------ ------- ------- ------- ------- ------- ------- ------- ------- ------- -------------- ; CS (DAT3) LO -> RTC:2 RTC:2 RTC:2 ~PC:4 ~MCR:3 OPR:2 SD:2 ~RTC:2/3OPR:4/5~OPR:3 ; CLK HI -> RTC:1 RTC:1 CSIO PC:1 ~MCR:2 OPR:1 CSIO CSIO SPI OPR:4 ; DI (CMD) HI -> RTC:0 RTC:0 CSIO PC:0 ~MCR:0 OPR:0 CSIO CSIO SPI OPR:0 ; DO (DAT0) HI -> RTC:7 RTC:6 CSIO PB:7 ~MSR:5 OPR:0 CSIO CSIO SPI OPR:7 ;----------------------------------------------------------------------------------------------------- ; ; CS = CHIP SELECT (AKA DAT3 FOR NON-SPI MODE) ; CLK = CLOCK ; DI = MOSI = DATA IN (HOST -> CARD, AKA CMD FOR NON-SPI MODE) ; DO = MISO = DATA OUT (HOST <- CARD, AKA DAT0 FOR NON-SPI MODE) ; ; NOTES: ; 1) SIGNAL NAMES ARE FROM THE SD CARD SPEC AND ARE NAMED FROM THE ; PERSPECTIVE OF THE SD CARD (SLAVE): ; DI = DATA IN: HOST -> CARD = MOSI (MASTER OUT/SLAVE IN) ; DO = DATA OUT: HOST <- CARD = MISO (MASTER IN/SLAVE OUT) ; ; 2) THE QUIESCENT STATE OF THE OUTPUT SIGNALS (HOST -> CARD) IS: ; CS = HI (NOT SELECTED) ; CLK = LO (HI FOR CSIO) ; DI = HI (ACTIVE IS THE NATURAL/DEFAULT STATE FOR DATA IN) ; ; 3) SPI MODE 0 IMPLEMENTATION IS USED (CPOL=0, CPHA=0) ; THE DATA MUST BE AVAILABLE BEFORE THE FIRST CLOCK SIGNAL RISING. ; THE CLOCK IDLE STATE IS ZERO. THE DATA ON MISO AND MOSI LINES ; MUST BE STABLE WHILE THE CLOCK IS HIGH AND CAN BE CHANGED WHEN ; THE CLOCK IS LOW. THE DATA IS CAPTURED ON THE CLOCK'S LOW-TO-HIGH ; TRANSITION AND PROPAGATED ON HIGH-TO-LOW CLOCK TRANSITION. ; ; NOTE: THE CSIO IMPLEMENTATION (INCLUDE MK4) USES SPI MODE 3 ; (CPOL=1, CPHA=1) BECAUSE THAT IS THE WAY THAT THE Z180 CSIO ; INTERFACE WORKS. ALL OF THE CLOCK TRANSITIONS LISTED ABOVE ; ARE REVERSED FOR CSIO. ; ; 4) DI SHOULD BE LEFT HI (ACTIVE) WHENEVER UNUSED (FOR EXAMPLE, WHEN ; HOST IS RECEIVING DATA (HOST <- CARD)). ; ;------------------------------------------------------------------------------ ; ; === R1 RESPONSE === ; ALL COMMAND RESPONSES START WITH R1 ; ; 7 6 5 4 3 2 1 0 ; +---+---+---+---+---+---+---+---+ ; | 0 | X | X | X | X | X | X | X | ; +---+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ; | | | | | | | ; | | | | | | +--- IDLE ; | | | | | +------- ERASE RESET ; | | | | +----------- ILLEGAL COMMAND ; | | | +--------------- COM CRC ERROR ; | | +------------------- ERASE SEQUENCE ERROR ; | +----------------------- ADDRESS ERROR ; +--------------------------- PARAMETER ERROR ; ; === DATA ERROR TOKEN === ; ; 7 6 5 4 3 2 1 0 ; +---+---+---+---+---+---+---+---+ ; | 0 | 0 | 0 | 0 | X | X | X | X | ; +---+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ; | | | | ; | | | +--- ERROR - GENERAL OR UNKNOWN ERROR ; | | +------- CC ERROR - INTERNAL CARD CONTROLER ERROR ; | +----------- CARD ECC FAILED - CARD INTERNAL ECC FAILED TO CORRECT DATA ; +--------------- OUT OF RANGE - PARAMAETER OUT OF RANGE ALLOWED FOR CARD ; ;------------------------------------------------------------------------------ ; ; *** HACK FOR MISSING PULLUP RESISTORS *** ; ; THERE IS A RECENT TREND FOR SD ADAPTER BOARDS (SUCH AS THOSE USED TO ATTACH AN ; SD CARD TO AN ARDUINO OR RASPBERRY PI) TO OMIT THE PULLUP RESISTORS THAT ARE SUPPOSED ; TO BE ON ALL LINES. DESPITE BEING A CLEAR VIOLATION OF THE SPEC, IT IS SOMETHING THAT ; WE WILL NOW NEED TO LIVE WITH. THE CLK, CS, AND MOSI SIGNALS ARE NOT AN ISSUE SINCE ; WE ARE DRIVING THOSE SIGNALS AS THE HOST. THE PROBLEM IS WITH THE MISO SIGNAL. ; FORTUNATELY, MOST OF THE TIME, THE SD CARD WILL BE DRIVING THE SIGNAL. HOWEVER, ; THERE ARE TWO SCEANRIOS WE NEED TO ACCOMMODATE IN THE CODE: ; ; 1. MISO WILL NOT BE DRIVEN BY THE SD CARD (FLOATING) PRIOR TO RESETING THE ; CARD WITH CMD0. NORMALLY, A COMMAND SEQUENCE INVOLVES WAITING FOR THE ; CARD TO BE "READY" BY READING BYTES FROM THE CARD AND LOOKING FOR $FF. ; WHEN MISO IS FLOATING THIS WILL NOT BE RELIABLE. SINCE THE SPEC INDICATES ; IT IS NOT NECESSARY TO WAIT FOR READY PRIOR TO CMD0, THE CODE HAS BEEN ; MODIFIED TO ISSUE CMD0 WITHOUT WAITING FOR READY. ; ; 2. MISO MAY NOT BE DRIVEN IMMEDIATELY AFTER SENDING A COMMAND (POSSIBLY ; JUST CMD0, BUT NOT SURE). NORMALLY, AFTER SENDING A COMMAND, YOU ; LOOK FOR "FILL" BYTES OF $FF THAT MAY OCCUR PRIOR TO THE RESULT. WHEN MISO ; IS FLOATING IT IS IMPOSSIBLE TO DETERMINE IF THE BYTE RECEIVED IS A FILL ; BYTE OR NOT. BASED ON WHAT I HAVE READ, THERE WILL ALWAYS BE AT LEAST ; ONE FILL BYTE PRIOR TO THE ACTUAL RESULT. ADDITIONALLY, THE SD CARD WILL ; START DRIVING MISO SOMETIME WITHIN THAT FIRST FILL BYTE. SO, WE NOW ; JUST DISCARD THE FIRST BYTE RECEIVED AFTER A COMMAND IS SENT WITH THE ; ASSUMPTION THAT IT MUST BE A FILL BYTE AND IS NOT RELIABLE DUE TO FLOATING ; MISO. ; ; THESE CHANGES ARE CONSISTENT WITH THE POPULAR ARDUINO SDFAT LIBRARY, SO THEY ARE ; PROBABLY PRETTY SAFE. HOWEVER, I HAVE BRACKETED THE CHANGES WITH THE EQUATE BELOW. ; IF YOU WANT TO REVERT THESE HACKS, JUST SET THE EQUATE TO FALSE. ; SD_NOPULLUP .EQU TRUE ; ASSUME NO PULLUP ; SD_DEVCNT .EQU SDCNT ; SET SD_DEVCNT TO SDCNT CONFIG VAR ; DEVECHO "SD: MODE=" ; #IF (SDMODE == SDMODE_JUHA) ; JUHA MINI-BOARD SD_DEVMAX .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_OPRREG .EQU RTCIO ; USES RTC LATCHES FOR OPERATION SD_OPRDEF .EQU %00000001 ; QUIESCENT STATE SD_OPRMSK .EQU %10000111 ; MASK FOR BITS WE OWN IN RTC LATCH PORT SD_INPREG .EQU RTCIO ; INPUT REGISTER IS RTC SD_CS0 .EQU %00000100 ; RTC:2 IS SELECT SD_CLK .EQU %00000010 ; RTC:1 IS CLOCK SD_DI .EQU %00000001 ; RTC:0 IS DATA IN (CARD <- CPU) SD_DO .EQU %10000000 ; RTC:7 IS DATA OUT (CARD -> CPU) SD_IOBASE .EQU SD_OPRREG ; IOBASE SD_INVCS .EQU FALSE ; INVERT CS DEVECHO "JUHA" ; RTCDEF .SET RTCDEF | SD_OPRDEF ; SET DEFAULT IN HBIOS MAINLINE #ENDIF ; #IF (SDMODE == SDMODE_N8) ; UNMODIFIED N8-2511 SD_DEVMAX .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_OPRREG .EQU RTCIO ; USES RTC LATCHES FOR OPERATION SD_OPRDEF .EQU %00000001 ; QUIESCENT STATE SD_OPRMSK .EQU %01000111 ; MASK FOR BITS WE OWN IN RTC LATCH PORT SD_INPREG .EQU RTCIO ; INPUT REGISTER IS RTC SD_CS0 .EQU %00000100 ; RTC:2 IS SELECT SD_CLK .EQU %00000010 ; RTC:1 IS CLOCK SD_DI .EQU %00000001 ; RTC:0 IS DATA IN (CARD <- CPU) SD_DO .EQU %01000000 ; RTC:6 IS DATA OUT (CARD -> CPU) SD_IOBASE .EQU SD_OPRREG ; IOBASE SD_INVCS .EQU FALSE ; INVERT CS DEVECHO "N8" ; RTCDEF .SET RTCDEF | SD_OPRDEF ; SET DEFAULT IN HBIOS MAINLINE #ENDIF ; #IF (SDMODE == SDMODE_CSIO) ; N8-2312 SD_DEVMAX .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_OPRREG .EQU RTCIO ; USES RTC LATCHES FOR OPERATION SD_OPRDEF .EQU %00000000 ; QUIESCENT STATE SD_OPRMSK .EQU %00000100 ; MASK FOR BITS WE OWN IN RTC LATCH PORT SD_CS0 .EQU %00000100 ; RTC:2 IS SELECT SD_CNTR .EQU Z180_CNTR SD_TRDR .EQU Z180_TRDR SD_IOBASE .EQU SD_OPRREG ; IOBASE SD_INVCS .EQU FALSE ; INVERT CS DEVECHO "CSIO" ; RTCDEF .SET RTCDEF | SD_OPRDEF ; SET DEFAULT IN HBIOS MAINLINE #ENDIF ; #IF (SDMODE == SDMODE_PPI) ; PPISD SD_DEVMAX .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_PPIBASE .EQU SDPPIBASE ; BASE IO PORT FOR PPI SD_PPIB .EQU SDPPIBASE + 1 ; PPI PORT B (INPUT: DOUT) SD_PPIB .EQU SDPPIBASE + 1 ; PPI PORT B (INPUT: DOUT) SD_PPIC .EQU SDPPIBASE + 2 ; PPI PORT C (OUTPUT: CS, CLK, DIN) SD_PPIX .EQU SDPPIBASE + 3 ; PPI CONTROL PORT SD_OPRREG .EQU SD_PPIC ; PPI PORT C IS OPR REG SD_OPRDEF .EQU %00110001 ; CS HI, DI HI SD_INPREG .EQU SD_PPIB ; INPUT REGISTER IS PPI PORT B SD_CS0 .EQU %00010000 ; PPIC:4 IS SELECT SD_CLK .EQU %00000010 ; PPIC:1 IS CLOCK SD_DI .EQU %00000001 ; PPIC:0 IS DATA IN (CARD <- CPU) SD_DO .EQU %10000000 ; PPIB:7 IS DATA OUT (CARD -> CPU) SD_IOBASE .EQU SD_PPIBASE ; IOBASE SD_INVCS .EQU TRUE ; INVERT CS DEVECHO "PPI" #ENDIF ; #IF (SDMODE == SDMODE_UART) SD_DEVMAX .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_OPRREG .EQU SIO_MCR ; UART MCR PORT (OUTPUT: CS, CLK, DIN) SD_OPRDEF .EQU %00001100 ; QUIESCENT STATE SD_OPRMSK .EQU %00101101 ; MASK FOR BITS WE OWN IN RTC LATCH PORT SD_INPREG .EQU SIO_MSR ; INPUT REGISTER IS MSR SD_CS0 .EQU %00001000 ; UART MCR:3 IS SELECT SD_CLK .EQU %00000100 ; UART MCR:2 IS CLOCK SD_DI .EQU %00000001 ; UART MCR:0 IS DATA IN (CARD <- CPU) SD_DO .EQU %00100000 ; UART MSR:5 IS DATA OUT (CARD -> CPU) SD_IOBASE .EQU UARTIOB ; IOBASE SD_INVCS .EQU TRUE ; INVERT CS DEVECHO "UART" #ENDIF ; #IF (SDMODE == SDMODE_DSD) ; DUAL SD SD_DEVMAX .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_CS0 .EQU %00000100 ; RTC:2 IS SELECT SD_CLK .EQU %00000010 ; RTC:1 IS CLOCK SD_DI .EQU %00000001 ; RTC:6 IS DATA IN (CARD <- CPU) SD_DO .EQU %00000001 ; RTC:0 IS DATA OUT (CARD -> CPU) SD_IOBASE .EQU SD_OPRREG ; IOBASE SD_INVCS .EQU FALSE ; INVERT CS DEVECHO "DSD" #ENDIF ; #IF (SDMODE == SDMODE_MK4) ; MARK IV (CSIO STYLE INTERFACE) SD_DEVMAX .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_OPRREG .EQU $89 ; DEDICATED MK4 SDCARD REGISTER SD_OPRDEF .EQU %00000000 ; QUIESCENT STATE SD_CS0 .EQU %00000100 ; SELECT ACTIVE SD_CNTR .EQU Z180_CNTR SD_TRDR .EQU Z180_TRDR SD_IOBASE .EQU SD_OPRREG ; IOBASE SD_INVCS .EQU FALSE ; INVERT CS DEVECHO "MK4" #ENDIF ; #IF (SDMODE == SDMODE_SC) ; SC SD_DEVMAX .EQU 2 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_OPRREG .EQU RTCIO ; USES RTC LATCHES FOR OPERATION SD_OPRDEF .EQU %00001100 ; QUIESCENT STATE (/CS1 & /CS2 DEASSERTED) SD_OPRMSK .EQU %00001100 ; MASK FOR BITS WE OWN IN RTC LATCH PORT SD_CS0 .EQU %00000100 ; RTC:2 IS SELECT FOR PRIMARY SPI CARD SD_CS1 .EQU %00001000 ; RTC:3 IS SELECT FOR SECONDARY SPI CARD SD_CNTR .EQU Z180_CNTR SD_TRDR .EQU Z180_TRDR SD_IOBASE .EQU SD_OPRREG ; IOBASE SD_INVCS .EQU TRUE ; INVERT CS DEVECHO "SC" RTCDEF .SET RTCDEF | SD_OPRDEF ; SET DEFAULT IN HBIOS MAINLINE #ENDIF ; #IF (SDMODE == SDMODE_GM) ; GM SD_DEVMAX .EQU 2 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_OPRREG .EQU RTCIO ; USES RTC LATCHES FOR OPERATION SD_OPRDEF .EQU %00000100 ; QUIESCENT STATE (/CS1 & /CS2 DEASSERTED) SD_OPRMSK .EQU %00000100 ; MASK FOR BITS WE OWN IN RTC LATCH PORT SD_CS0 .EQU %00000100 ; RTC:2 IS SELECT FOR PRIMARY SPI CARD SD_CS1 .EQU %00000000 ; RTC:3 IS SELECT FOR SECONDARY SPI CARD SD_CNTR .EQU Z180_CNTR SD_TRDR .EQU Z180_TRDR SD_IOBASE .EQU SD_OPRREG ; IOBASE SD_INVCS .EQU TRUE ; INVERT CS DEVECHO "GM" RTCDEF .SET RTCDEF | SD_OPRDEF ; SET DEFAULT IN HBIOS MAINLINE #ENDIF ; #IF (SDMODE == SDMODE_MT) ; MT shift register for RCBUS (ref SDMODE_CSIO) ; ; 3 SPI CHANNELS. CHANNEL 0 (CDX & CSX) IS A DEDICATED CONNECTION TO ONBOARD ; WIZNET W5500 AND IS NOT USED HERE. CHANNEL 1 (CD0 & CS0) & 2 (CD1 & CS1) ; ARE ASSUMED TO BE CONNECTED TO SD CARDS. ; ; NOTE THAT DOING AN "IN RDTR" WILL RETURN THE *EXISTING* CONTENTS ; OF THE SHIFT REGISTER, THEN INITIATE AN SPI READ. SO THE "IN" WILL BE ; RETURNING THE DATA RECEIVED FROM THE PRIOR "IN RDTR", OR "OUT WRTR". ; "IN RDNTR" WILL RETURN THE EXISTING SHIFT REGISTER CONTENTS WITHOUT ; INITIATING A NEW SPI READ. ; ; THANKS TO DOUGLAS MILLER FOR BRINGING THIS BEHAVIOR TO MY ATTENTION ; AND SUPPLYING ASSOCIATED FIXES. ; #IF (PLATFORM == PLT_DUO) SD_BASE .EQU $8C ; Module base address #ELSE SD_BASE .EQU $5C ; Module base address #ENDIF SD_DEVMAX .EQU 2 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_WRTR .EQU SD_BASE + 0 ; Write data and transfer SD_RDTR .EQU SD_BASE + 1 ; Read data and transfer SD_RDNTR .EQU SD_BASE + 0 ; Read data and NO transfer SD_OPRREG .EQU SD_BASE + 2 ; SD CHIP SELECTOR SD_OPRDEF .EQU %00000000 ; QUIESCENT STATE SD_CDX .EQU %00000001 ; IN/OUT:SD_OPREG:0 = CD0, PMOD pull CD0 low #IF (!SDMTSWAP) ; USE NATURAL ORDER SD_CD0 .EQU %00000010 ; IN:SD_OPREG:1 = CD1, IN=0 Card detect switch SD_CD1 .EQU %00000100 ; IN:SD_OPREG:2 = CD2, IN=0 Card detect switch #ELSE ; REVERSE THE PORTS SD_CD0 .EQU %00000100 ; IN:SD_OPREG:2 = CD2, IN=0 Card detect switch SD_CD1 .EQU %00000010 ; IN:SD_OPREG:1 = CD1, IN=0 Card detect switch #ENDIF SD_CSX .EQU %00001000 ; IN/OUT:SD_OPREG:3 = CS0, PMOD SPI CS #IF (!SDMTSWAP) ; USE NATURAL ORDER SD_CS0 .EQU %00010000 ; IN/OUT:SD_OPREG:4 = CS1, SDCARD1 CS, IN=1 Card present SD_CS1 .EQU %00100000 ; IN/OUT:SD_OPREG:5 = CS2, SDCARD2 CS, IN=1 Card present #ELSE ; REVERSE THE PORTS SD_CS0 .EQU %00100000 ; IN/OUT:SD_OPREG:5 = CS2, SDCARD2 CS, IN=1 Card present SD_CS1 .EQU %00010000 ; IN/OUT:SD_OPREG:4 = CS1, SDCARD1 CS, IN=1 Card present #ENDIF SD_IOBASE .EQU SD_BASE ; IOBASE SD_INVCS .EQU FALSE ; INVERT CS DEVECHO "MT" #ENDIF ; #IF (SDMODE == SDMODE_PIO) ; Z80 PIO ; ; These mappings work for the RCbus Gluino card with an Arduino ; shield attached and are the ones also used in other bitbang setups ; directly attached to a PIO. It also works on a straight digital I/O ; port as the config writes will disappear into oblivion harmlessly ; ; The Gluino mapping (ie Arduino pin mapping equivalent) is thus ; D10 SS, D11 CIPO, D12 COPI, D13 SCL. ; ; For speed reasons MISO/MOSI are mapped to the top and bottom bits. ; RomWBW doesn't yet use this fact but the optimized Fuzix routines do. ; SD_DEVMAX .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_IOBASE .EQU $69 ; IO BASE ADDRESS FOR SD INTERFACE SD_OPRREG .EQU SD_IOBASE ; OUTPUT PORT (OUTPUT: CS, CLK, DIN) ;--- WBW ;SD_OPRDEF .EQU %11111111 ; OUTPUT PORT DEFAULT STATE SD_OPRDEF .EQU %11101111 ; OUTPUT PORT DEFAULT STATE ;--- SD_INPREG .EQU SD_IOBASE ; INPUT REGISTER SD_CS0 .EQU %00001000 ; SELECT SD_CLK .EQU %00010000 ; CLOCK SD_DI .EQU %00000001 ; DATA IN (CARD <- CPU) MOSI SD_DO .EQU %10000000 ; DATA OUT (CARD -> CPU) MISO SD_CINIT .EQU TRUE ; INITIALIZE OUTPUT PORT SD_DDR .EQU $6B ; DATA DIRECTION REGISTER SD_DDRVAL .EQU %11100110 ; DATA DIRECTION REGISTER VALUE SD_INVCS .EQU TRUE ; INVERT CS DEVECHO "PIO" #ENDIF ; #IF (SDMODE == SDMODE_USR) ; USER DEFINED HARDWARE CONFIGURATION ; ; THIS MODE IS INTENDED TO ALLOW A USER TO EASILY CONFIGURE A CUSTOM ; SD CARD INTERFACE. IT IS NOT YET COMPLETE OR TESTED AND PROBABLY DOES ; NOT YET WORK. ; SD_DEVMAX .EQU 2 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_IOBASE .EQU $01 ; IO BASE ADDRESS FOR SD INTERFACE SD_OPRREG .EQU SD_IOBASE ; OUTPUT PORT (OUTPUT: CS, CLK, DIN) SD_OPRDEF .EQU %00000101 ; OUTPUT PORT DEFAULT STATE SD_OPRMSK .EQU %00001111 ; OUTPUT PORT MASK SD_INPREG .EQU SD_IOBASE ; INPUT REGISTER SD_CS0 .EQU %00001000 ; SELECT SD_CLK .EQU %00000100 ; CLOCK SD_DI .EQU %00000010 ; DATA IN (CARD <- CPU) MOSI SD_DO .EQU %00000001 ; DATA OUT (CARD -> CPU) MISO SD_CINIT .EQU TRUE ; INITIALIZE OUTPUT PORT SD_DDR .EQU $03 ; DATA DIRECTION REGISTER SD_DDRVAL .EQU %00001101 ; DATA DIRECTION REGISTER VALUE SD_INVCS .EQU FALSE ; INVERT CS DEVECHO "USR" #ENDIF ; #IF (SDMODE == SDMODE_Z80R) ; Z80 Retro ; ; SPLIT OVER TWO REGISTERS TO DRIVE CLK. THE CS LINE IS ON THE GPIO ; WHICH IS THE SAME LATCHES THAT CONTROL MMU ON/OFF, SO DON;T GLITCH ; THEM WHEN UPDATING! ; SD_DEVMAX .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_OPRDEF .EQU %00000001 ; OUTPUT PORT DEFAULT STATE SD_OPRMSK .EQU %00000101 ; OUTPUT PORT MASK SD_OPRREG .EQU $64 ; CS VIA GPIO SD_IOBASE .EQU $68 ; 68/69 FOR OUTPUT SD_IOREG .EQU SD_IOBASE ; INPUT REGISTER SD_IOCLK .EQU SD_IOBASE+1 ; CLOCK IS OFF A0 SD_GPIO .EQU $64 ; MISO IS ON THE GPIO SD_CS0 .EQU %00000100 ; SELECT SD_DI .EQU %00000001 ; DATA IN (CARD <- CPU) MOSI SD_DO .EQU %00000001 ; DATA OUT (CARD -> CPU) MISO SD_CINIT .EQU FALSE ; INITIALIZE OUTPUT PORT SD_INVCS .EQU TRUE ; INVERT CS DEVECHO "Z80R" #ENDIF ; ; FOR NOW WE JUST HOOK UP ONE UNIT. THERE ARE EIGHT PORTS FOR DIFFERENT ; THINGS BUT THIS WILL GET US GOING. ; #IF (SDMODE == SDMODE_EPITX) ; Z180 ITX - CSIO, 82C55 for CS SD_DEVMAX .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_OPRREG .EQU $42 ; 82C55 PORT C, LOW 3 ARE \CS MUX SD_OPRDEF .EQU %11111111 ; QUIESCENT STATE (ROM ENABLED) SD_CS0 .EQU %11111000 ; SPI CHANNEL 0 (4 INPUTS, ROM EN, CHAN 0) SD_CNTR .EQU Z180_CNTR SD_TRDR .EQU Z180_TRDR SD_IOBASE .EQU SD_OPRREG ; IOBASE SD_INVCS .EQU FALSE ; INVERT CS DEVECHO "EPITX" #ENDIF ; ; S100 FPGA Z80 SPI-BASED SD CARD ; ; BASE PORT: $6C ; BASE + 0: DATA IN/OUT ; BASE + 1: SPI CLOCK SPEED (0=LOW 4KHZ, 1=HIGH 10MHZ) ; BASE + 2: SELECT (W), STATUS (R) ; BASE + 3: START READ (IN OPCODE), START WRITE (OUT OPCODE), ANY VALUE ; ; STATUS BITS: ; 7: BUSY (1=BUSY) ; 0: PRIMARY DEVICE SELECT STATUS (1=SELECTED) ; 1: SECONDARY DEVICE SELECT STATUS (1=SELECTED) ; ; SELECT BITS (INVERTED!!!): ; 0: PRIMARY DEVICE, USE VALUE ~$01 ; 1: SECONDARY DEVICE, USE VALUE ~$02 ; #IF (SDMODE == SDMODE_FZ80) ; S100 FPGA Z80 SD_IOBASE .EQU $6C ; IOBASE SD_DATA .EQU SD_IOBASE + 0 ; DATA IN/OUT PORT SD_CLKSEL .EQU SD_IOBASE + 1 ; CLOCK SPEED SELECT PORT SD_SELSTAT .EQU SD_IOBASE + 2 ; DEVICE SELECT PORT (W) / STATUS (R) SD_ACTION .EQU SD_IOBASE + 3 ; INITIATE R/W ACTION VIA IN/OUT ; SD_DEVMAX .EQU 2 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_OPRREG .EQU SD_SELSTAT ; SELECT/STATUS PORT SD_OPRDEF .EQU $FF ; QUIESCENT STATE SD_CS0 .EQU %00000001 ; PRIMARY DEVICE SELECT BIT SD_CS1 .EQU %00000010 ; SECONDARY DEVICE SELECT BIT SD_INVCS .EQU TRUE ; INVERT CS DEVECHO "FZ80" #ENDIF ; #IF (SDMODE == SDMODE_EZ512) ; Z80 PIO ON EAZY80-512 ; ; Equivalent to PIO mode, but with different port addresses ; SD_DEVMAX .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_IOBASE .EQU $02 ; IO BASE ADDRESS FOR SD INTERFACE SD_OPRREG .EQU SD_IOBASE ; OUTPUT PORT (OUTPUT: CS, CLK, DIN) SD_OPRDEF .EQU %11101111 ; OUTPUT PORT DEFAULT STATE SD_INPREG .EQU SD_IOBASE ; INPUT REGISTER SD_CS0 .EQU %00001000 ; SELECT SD_CLK .EQU %00010000 ; CLOCK SD_DI .EQU %00000001 ; DATA IN (CARD <- CPU) MOSI SD_DO .EQU %10000000 ; DATA OUT (CARD -> CPU) MISO SD_CINIT .EQU TRUE ; INITIALIZE OUTPUT PORT SD_DDR .EQU $03 ; DATA DIRECTION REGISTER SD_DDRVAL .EQU %11100110 ; DATA DIRECTION REGISTER VALUE SD_INVCS .EQU TRUE ; INVERT CS DEVECHO "EZ512" #ENDIF ; #IF (SDMODE == SDMODE_K80W) ; Z80 PIO ON K80W ; ; Equivalent to PIO mode, but with different port addresses ; SD_DEVMAX .EQU 1 ; NUMBER OF PHYSICAL UNITS (SOCKETS) SD_IOBASE .EQU $82 ; IO BASE ADDRESS FOR SD INTERFACE SD_OPRREG .EQU SD_IOBASE ; OUTPUT PORT (OUTPUT: CS, CLK, DIN) SD_OPRDEF .EQU %11101111 ; OUTPUT PORT DEFAULT STATE SD_INPREG .EQU SD_IOBASE ; INPUT REGISTER SD_CS0 .EQU %00001000 ; SELECT SD_CLK .EQU %00010000 ; CLOCK SD_DI .EQU %00000001 ; DATA IN (CARD <- CPU) MOSI SD_DO .EQU %10000000 ; DATA OUT (CARD -> CPU) MISO SD_CINIT .EQU TRUE ; INITIALIZE OUTPUT PORT SD_DDR .EQU $83 ; DATA DIRECTION REGISTER SD_DDRVAL .EQU %11100110 ; DATA DIRECTION REGISTER VALUE SD_INVCS .EQU TRUE ; INVERT CS DEVECHO "EZ512" #ENDIF ; DEVECHO ", IO=" DEVECHO SD_IOBASE DEVECHO ", UNITS=" DEVECHO SDCNT DEVECHO "\n" ; #IF (SD_DEVCNT > SD_DEVMAX) .ECHO "*** ERROR: SDCNT EXCEEDS MAXIMUM SUPPORTED BY INTERFACE!!!\n" !!! ; FORCE AN ASSEMBLY ERROR #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_CMD_CRC_ON_OFF .EQU $40 + 59 ; $7B, CMD59 -> R1 ; ; SD CARD APPLICATION COMMANDS (PRECEDED BY APP_CMD COMMAND) ; SD_ACMD_SEND_OP_COND .EQU $40 + 41 ; $69, ACMD41 -> R1 SD_ACMD_SEND_SCR .EQU $40 + 51 ; $73, ACMD51 -> R1 ; ; SD CARD TYPE ; SD_TYPEUNK .EQU 0 ; CARD TYPE UNKNOWN/UNDETERMINED SD_TYPEMMC .EQU 1 ; MULTIMEDIA CARD (MMC STANDARD) SD_TYPESDSC .EQU 2 ; SDSC CARD (V1) SD_TYPESDHC .EQU 3 ; SDHC CARD (V2) SD_TYPESDXC .EQU 4 ; SDXC CARD (V3) ; ; SD CARD STATUS (SD_STAT) ; SD_STOK .EQU 0 ; OK SD_STINVUNIT .EQU -1 ; INVALID UNIT SD_STRDYTO .EQU -2 ; TIMEOUT WAITING FOR CARD TO BE READY SD_STINITTO .EQU -3 ; INITIALIZATION TIMEOUT SD_STCMDTO .EQU -4 ; TIMEOUT WAITING FOR COMMAND RESPONSE SD_STCMDERR .EQU -5 ; COMMAND ERROR OCCURRED (REF SD_RC) SD_STDATAERR .EQU -6 ; DATA ERROR OCCURRED (REF SD_TOK) SD_STDATATO .EQU -7 ; DATA TRANSFER TIMEOUT SD_STCRCERR .EQU -8 ; CRC ERROR ON RECEIVED DATA PACKET SD_STNOMEDIA .EQU -9 ; NO MEDIA IN CONNECTOR SD_STWRTPROT .EQU -10 ; ATTEMPT TO WRITE TO WRITE PROTECTED MEDIA ; ; IDE DEVICE CONFIGURATION ; SD_CFGSIZ .EQU 12 ; SIZE OF CFG TBL ENTRIES ; ; PER DEVICE DATA OFFSETS ; SD_DEV .EQU 0 ; OFFSET OF DEVICE NUMBER (BYTE) SD_STAT .EQU 1 ; LAST STATUS (BYTE) SD_TYPE .EQU 2 ; DEVICE TYPE (BYTE) SD_FLAGS .EQU 3 ; FLAG BITS (BYTE) SD_MEDCAP .EQU 4 ; MEDIA CAPACITY (DWORD) SD_LBA .EQU 8 ; OFFSET OF LBA (DWORD) ; SD_CFGTBL: ; DEVICE 0, PRIMARY MASTER .DB 0 ; DRIVER DEVICE NUMBER .DB 0 ; DEVICE STATUS .DB 0 ; DEVICE TYPE .DB 0 ; FLAGS BYTE .DW 0,0 ; DEVICE CAPACITY .DW 0,0 ; CURRENT LBA #IF (SD_DEVCNT >= 2) ; DEVICE 1, PRIMARY SLAVE .DB 1 ; DRIVER DEVICE NUMBER .DB 0 ; DEVICE STATUS .DB 0 ; DEVICE TYPE .DB 0 ; FLAGS BYTE .DW 0,0 ; DEVICE CAPACITY .DW 0,0 ; CURRENT LBA #ENDIF ; #IF ($ - SD_CFGTBL) != (SD_DEVCNT * SD_CFGSIZ) .ECHO "*** INVALID SD CONFIG TABLE ***\n" #ENDIF ; .DB $FF ; END MARKER ; ;============================================================================= ; INITIALIZATION ENTRY POINT ;============================================================================= ; SD_INIT: CALL NEWLINE ; FORMATTING PRTS("SD:$") ; #IF (SDMODE == SDMODE_JUHA) PRTS(" MODE=JUHA$") PRTS(" IO=0x$") LD A,SD_OPRREG CALL PRTHEXBYTE ; LD A,(HB_RTCVAL) ; GET RTC PORT SHADOW VALUE AND ~SD_OPRMSK ; CLEAR OUR BITS OR SD_OPRDEF ; SET OUR BIT DEFAULTS LD (HB_RTCVAL),A ; SAVE IT #ENDIF ; #IF (SDMODE == SDMODE_N8) PRTS(" MODE=N8$") PRTS(" IO=0x$") LD A,SD_OPRREG CALL PRTHEXBYTE ; LD A,(HB_RTCVAL) ; GET RTC PORT SHADOW VALUE AND ~SD_OPRMSK ; CLEAR OUR BITS OR SD_OPRDEF ; SET OUR BIT DEFAULTS LD (HB_RTCVAL),A ; SAVE IT #ENDIF ; #IF (SDMODE == SDMODE_CSIO) PRTS(" MODE=CSIO$") #IF (SDCSIOFAST) PRTS(" FAST$") #ENDIF PRTS(" OPR=0x$") LD A,SD_OPRREG CALL PRTHEXBYTE PRTS(" CNTR=0x$") LD A,SD_CNTR CALL PRTHEXBYTE PRTS(" TRDR=0x$") LD A,SD_TRDR CALL PRTHEXBYTE ; LD A,(HB_RTCVAL) ; GET RTC PORT SHADOW VALUE AND ~SD_OPRMSK ; CLEAR OUR BITS OR SD_OPRDEF ; SET OUR BIT DEFAULTS LD (HB_RTCVAL),A ; SAVE IT #ENDIF ; #IF (SDMODE == SDMODE_PPI) PRTS(" MODE=PPI$") PRTS(" IO=0x$") LD A,SD_PPIBASE CALL PRTHEXBYTE #ENDIF ; #IF (SDMODE == SDMODE_UART) PRTS(" MODE=UART$") PRTS(" MCR=0x$") LD A,SIO_MCR CALL PRTHEXBYTE PRTS(" MSR=0x$") LD A,SIO_MSR CALL PRTHEXBYTE #ENDIF ; #IF (SDMODE == SDMODE_DSD) PRTS(" MODE=DSD$") PRTS(" OPR=0x$") LD A,SD_OPRREG CALL PRTHEXBYTE PRTS(" SEL=0x$") LD A,SD_SELREG CALL PRTHEXBYTE #ENDIF ; #IF (SDMODE == SDMODE_MK4) PRTS(" MODE=MK4$") #IF (SDCSIOFAST) PRTS(" FAST$") #ENDIF PRTS(" OPR=0x$") LD A,SD_OPRREG CALL PRTHEXBYTE PRTS(" CNTR=0x$") LD A,SD_CNTR CALL PRTHEXBYTE PRTS(" TRDR=0x$") LD A,SD_TRDR CALL PRTHEXBYTE #ENDIF ; #IF ((SDMODE == SDMODE_SC) | (SDMODE == SDMODE_GM)) PRTS(" MODE=SC$") #IF (SDCSIOFAST) PRTS(" FAST$") #ENDIF PRTS(" OPR=0x$") LD A,SD_OPRREG CALL PRTHEXBYTE PRTS(" CNTR=0x$") LD A,SD_CNTR CALL PRTHEXBYTE PRTS(" TRDR=0x$") LD A,SD_TRDR CALL PRTHEXBYTE ; LD A,(HB_RTCVAL) ; GET RTC PORT SHADOW VALUE AND ~SD_OPRMSK ; CLEAR OUR BITS OR SD_OPRDEF ; SET OUR BIT DEFAULTS LD (HB_RTCVAL),A ; SAVE IT #ENDIF ; #IF (SDMODE == SDMODE_MT) PRTS(" MODE=MT$") PRTS(" IO=0x$") LD A,SD_BASE CALL PRTHEXBYTE #ENDIF ; #IF (SDMODE == SDMODE_PIO) PRTS(" MODE=PIO$") PRTS(" IO=0x$") LD A,SD_IOBASE CALL PRTHEXBYTE #ENDIF ; #IF (SDMODE == SDMODE_Z80R) PRTS(" MODE=Z80R$") PRTS(" IO=0x$") LD A,SD_IOBASE CALL PRTHEXBYTE LD A,SD_OPRDEF LD (SD_OPRVAL),A #ENDIF ; #IF (SDMODE == SDMODE_USR) PRTS(" MODE=USER$") PRTS(" IO=0x$") LD A,SD_IOBASE CALL PRTHEXBYTE #ENDIF ; #IF (SDMODE == SDMODE_EPITX) PRTS(" MODE=EPITX$") #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_FZ80) PRTS(" MODE=FZ80$") PRTS(" IO=0x$") LD A,SD_IOBASE CALL PRTHEXBYTE #ENDIF ; #IF (SDMODE == SDMODE_EZ512) PRTS(" MODE=EZ512$") PRTS(" IO=0x$") LD A,SD_IOBASE CALL PRTHEXBYTE #ENDIF ; #IF (SDMODE == SDMODE_K80W) PRTS(" MODE=K80W$") PRTS(" IO=0x$") LD A,SD_IOBASE CALL PRTHEXBYTE #ENDIF ; CALL SD_PROBE ; CHECK FOR HARDWARE JR Z,SD_INIT00 ; CONTINUE IF PRESENT ; ; HARDWARE NOT PRESENT PRTS(" NOT PRESENT$") OR $FF ; SIGNAL FAILURE RET ; SD_INIT00: ; ; SETUP THE DISPATCH TABLE ENTRIES ; PRTS(" DEVICES=$") LD A,SD_DEVCNT CALL PRTDECB ; ; SETUP THE DISPATCH TABLE ENTRIES ; LD B,SD_DEVCNT ; LOOP CONTROL LD IY,SD_CFGTBL ; START OF CFG TABLE SD_INIT0: PUSH BC ; SAVE LOOP CONTROL LD BC,SD_FNTBL ; BC := FUNC TABLE ADR PUSH IY ; CFG ENTRY POINTER POP DE ; COPY TO DE CALL DIO_ADDENT ; ADD ENTRY, BC IS NOT DESTROYED LD BC,SD_CFGSIZ ; SIZE OF CFG ENTRY ADD IY,BC ; BUMP IY TO NEXT ENTRY POP BC ; RESTORE BC DJNZ SD_INIT0 ; LOOP AS NEEDED ; ; INITIALIZE THE SD INTERFACE NOW CALL SD_SETUP ; DO HARDWARE SETUP/INIT RET NZ ; ABORT ON ERROR ; ; INITIALIZE INDIVIDUAL UNIT(S) AND DISPLAY DEVICE INVENTORY LD B,SD_DEVCNT ; INIT LOOP COUNTER TO DEVICE COUNT LD IY,SD_CFGTBL ; START OF CFG TABLE SD_INIT1: PUSH BC ; SAVE LOOP COUNTER/INDEX CALL SD_INITUNIT ; INITIALIZE IT #IF (SDTRACE < 2) CALL NZ,SD_PRTSTAT ; IF ERROR, SHOW IT #ENDIF LD BC,SD_CFGSIZ ; SIZE OF CFG ENTRY ADD IY,BC ; BUMP IY TO NEXT ENTRY POP BC ; RESTORE LOOP CONTROL DJNZ SD_INIT1 ; DECREMENT LOOP COUNTER AND LOOP AS NEEDED ; RET ; DONE ; ; INITIALIZE UNIT DESIGNATED IN ACCUM ; SD_INITUNIT: CALL SD_SELUNIT ; SELECT UNIT RET NZ ; ABORT ON ERROR ; CALL SD_INITCARD ; INIT THE SELECTED CARD RET NZ ; ABORT ON ERROR ; CALL SD_PRTPREFIX CALL PC_SPACE ; ; PRINT CARD TYPE LD A,(IY+SD_TYPE) ; GET CARD TYPE VALUE TO A LD DE,SD_STR_TYPEMMC CP SD_TYPEMMC JR Z,SD_INITUNIT1 LD DE,SD_STR_TYPESDSC CP SD_TYPESDSC JR Z,SD_INITUNIT1 LD DE,SD_STR_TYPESDHC CP SD_TYPESDHC JR Z,SD_INITUNIT1 LD DE,SD_STR_TYPESDXC CP SD_TYPESDXC JR Z,SD_INITUNIT1 LD DE,SD_STR_TYPEUNK SD_INITUNIT1: CALL WRITESTR ; GET CID (WHICH CONTAINS PRODUCT NAME) LD A,SD_CMD_SEND_CID ; SEND_CID CALL SD_INITCMD ; SETUP COMMAND BUFFER CALL SD_EXECCMD ; RUN COMMAND RET NZ ; ABORT ON ERROR LD BC,16 ; 16 BYTES OF CID LD HL,SD_BUF CALL SD_GETDATA CALL SD_DONE JP NZ,SD_ERRDATA ; DATA XFER ERROR #IF (SDTRACE >= 3) CALL SD_PRTPREFIX LD DE,SD_STR_CID CALL WRITESTR LD DE,SD_BUF LD A,16 CALL PRTHEXBUF #ENDIF ; PRINT PRODUCT NAME PRTS(" NAME=$") ; PRINT LABEL LD B,5 ; PREPARE TO PRINT 5 BYTES LD HL,SD_BUF + 3 ; AT BYTE OFFSET 3 IN RESULT BUFFER SD_INITUNIT2: LD A,(HL) ; GET NEXT BYTE CALL COUT ; PRINT IT INC HL ; POINT TO NEXT BYTE DJNZ SD_INITUNIT2 ; LOOP FOR ALL 5 BYTES ; ; PRINT STORAGE CAPACITY (BLOCK COUNT) PRTS(" BLOCKS=0x$") ; PRINT FIELD LABEL LD A,SD_MEDCAP ; OFFSET TO CAPACITY FIELD CALL LDHLIYA ; HL := IY + A, REG A TRASHED CALL LD32 ; GET THE CAPACITY VALUE CALL PRTHEX32 ; PRINT HEX VALUE ; ; PRINT STORAGE SIZE IN MB PRTS(" SIZE=$") ; PRINT FIELD LABEL LD B,11 ; 11 BIT SHIFT TO CONVERT BLOCKS --> MB CALL SRL32 ; RIGHT SHIFT ;CALL PRTDEC ; PRINT LOW WORD IN DECIMAL (HIGH WORD DISCARDED) CALL PRTDEC32 ; PRINT DWORD IN DECIMAL PRTS("MB$") ; PRINT SUFFIX ; ; CHECK FOR WRITE PROTECT AND NOTIFY USER IF SO CALL SD_CHKWP ; WRITE PROTECTED? RET Z ; IF NOT, DONE PRTS(" WP$") ; NOTIFY USER ; XOR A ; SIGNAL SUCCESS RET ; DONE ; ;---------------------------------------------------------------------- ; PROBE FOR SD HARDWARE ;---------------------------------------------------------------------- ; ; ON RETURN, ZF SET INDICATES HARDWARE FOUND ; SD_PROBE: ; #IF (SDMODE == SDMODE_DSD) ; ON DSD, SELREG, BIT 0 IS READ BACK AS WRITTEN, BIT 1 ; IS ALWAYS READ AS ZERO. TO TEST FOR EXISTENCE, WE ; WRITE 00 AND MAKE SURE IT READS BACK AS 00, THEN WE ; WRITE 11 AND MAKE SURE IT READS BACK AS 01. LD C,SD_SELREG ; USE C TO ADDRESS PORT XOR A ; A := 0 OUT (C),A ; SELREG := 0 IN A,(C) ; READ SELREG BACK AND $03 ; ISOLATE 2 LOWEST BITS CP $00 ; BOTH BITS SHOULD BE 0 RET NZ ; FAIL IF NOT LD A,$03 ; SET 2 LOWEST BITS OUT (C),A ; DO IT IN A,(C) ; READ SELREG BACK AND $03 ; ISOLATE 2 LOWEST BITS CP $01 ; SHOULD READ BACK AS $01 RET ; RETURN W/ ZF SET AS NEEDED #ENDIF ; ;#IF (SDMODE == SDMODE_MT) ; LD A,SD_OPRDEF ; OUT (SD_OPRREG),A ; MAKE SURE CONTROL REGISTER IS CLEARED ;; ; ; TEST WITH PMOD NOT CONNECTED ;; IN A,(SD_OPRREG) ;; AND SD_CD0+SD_CS0 ; ISOLATE CD0 AND CS0 ;; CP SD_CD0+SD_CS0 ; BOTH SHOULD BE HIGH ;; JR NZ,SD_PROBE_FAIL ; FAIL IF NOT ; ; TEST CD0 ;; LD A,SD_CD0 ; D1=DNP CANNOT TEST ;; OUT (SD_OPRREG),A ;; IN A,(SD_OPRREG) ;; AND SD_CD0 ;; JR NZ,SD_PROBE_FAIL ; FAIL IF NOT PULLED LOW ; ; TEST CS0 ;; LD A,SD_CS0 ; D2=DNP CANNOT TEST ;; OUT (SD_OPRREG),A ;; IN A,(SD_OPRREG) ;; AND SD_CS0 ;; JR NZ,SD_PROBE_FAIL ; FAIL IF NOT PULLED LOW ; ; TEST CS1 ;; LD A,SD_CS1 ;; OUT (SD_OPRREG),A ;; IN A,(SD_OPRREG) ;; AND SD_CS1 ;; JR NZ,SD_PROBE_FAIL ; FAIL IF NOT PULLED LOW ;; ; TEST CS2 ;; LD A,SD_CS2 ;; OUT (SD_OPRREG),A ;; IN A,(SD_OPRREG) ;; AND SD_CS2 ;; JR NZ,SD_PROBE_FAIL ; FAIL IF NOT PULLED LOW ; ; LD A,SD_OPRDEF ; OUT (SD_OPRREG),A ; MAKE SURE CONTROL REGISTER IS CLEARED ;#ENDIF ; XOR A ; SIGNAL SUCCESS ; ;#IF (SDMODE == SDMODE_MT) ;SD_PROBE_FAIL: ; LD A,SD_OPRDEF ; OUT (SD_OPRREG),A ; MAKE SURE CONTROL REGISTER IS CLEARED ;#ENDIF RET ; AND RETURN ; ;============================================================================= ; DRIVER FUNCTION TABLE ;============================================================================= ; SD_FNTBL: .DW SD_STATUS .DW SD_RESET .DW SD_SEEK .DW SD_READ .DW SD_WRITE .DW SD_VERIFY .DW SD_FORMAT .DW SD_DEVICE .DW SD_MEDIA .DW SD_DEFMED .DW SD_CAP .DW SD_GEOM #IF (($ - SD_FNTBL) != (DIO_FNCNT * 2)) .ECHO "*** INVALID IDE FUNCTION TABLE ***\n" #ENDIF ; SD_VERIFY: SD_FORMAT: SD_DEFMED: SYSCHKERR(ERR_NOTIMPL) ; INVALID SUB-FUNCTION RET ; ; ; SD_READ: CALL HB_DSKREAD ; HOOK HBIOS DISK READ SUPERVISOR LD A,SD_CMD_READ_SNGL_BLK ; SETUP FOR SINGLE BLOCK READ CMD JR SD_IO ; CONTINUE TO GENERIC IO ROUTINE ; ; ; SD_WRITE: CALL HB_DSKWRITE ; HOOK HBIOS DISK WRITE SUPERVISOR LD A,SD_CMD_WRITE_BLOCK ; SETUP FOR BLOCK WRITE CMD JR SD_IO ; CONTINUE TO GENERIC IO ROUTINE ; ; ; SD_IO: LD (SD_CMDVAL),A ; SAVE THE SD CARD COMMAND LD (SD_DSKBUF),HL ; SAVE DISK BUFFER ADDRESS LD A,E ; GET BLOCK COUNT REQUESTED LD (SD_BLKCNT),A ; ... AND SAVE IT OR A ; SET FLAGS RET Z ; ZERO SECTOR I/O, RETURN W/ E=0 & A=0 ; ; SET CSIO FOR HIGH SPEED OPERATION CALL SD_SPD_FAST ; ; HOOK RETURN TO RESTORE CSIO TO DEFAULT SPEED LD HL,SD_SPD_STD ; ROUTE RETURN PUSH HL ; ... THRU CSIO RESTORE ; #IF (SDTRACE == 1) LD HL,SD_PRTERR ; SET UP SD_PRTERR PUSH HL ; ... TO FILTER ALL EXITS #ENDIF ; CALL SD_SELUNIT ; HARDWARE SELECTION OF TARGET UNIT RET NZ ; ABORT ON ERROR LD A,(SD_CMDVAL) ; GET COMMAND VALUE CP SD_CMD_READ_SNGL_BLK ; IS THIS A READ? CALL NZ,SD_CHKWP ; CHECK FOR WRITE PROTECT IF NOT A READ JP NZ,SD_WRTPROT ; HANDLE WRITE PROTECT ERR LD A,(SD_BLKCNT) ; BLOCK COUNT TO A LD E,A ; ... AND TO E IN CASE OF ZERO ERR BELOW OR A ; SET FLAGS RET Z ; ZERO SECTOR I/O, RETURN W/ E=0 & A=0 LD B,A ; INIT SECTOR DOWNCOUNTER LD C,0 ; INIT SECTOR READ/WRITE COUNT SD_IO1: PUSH BC ; SAVE COUNTERS LD A,(SD_CMDVAL) ; SET COMMAND LD C,A ; ... AND PUT IN C CALL SD_SECTIO ; DO SECTOR I/O JR NZ,SD_IO2 ; IF ERROR, SKIP INCREMENT ; INCREMENT LBA LD A,SD_LBA ; LBA OFFSET CALL LDHLIYA ; HL := IY + A, REG A TRASHED CALL INC32HL ; INCREMENT THE VALUE ; INCREMENT DMA LD HL,SD_DSKBUF+1 ; POINT TO MSB OF BUFFER ADR INC (HL) ; BUMP DMA BY INC (HL) ; ... 512 BYTES XOR A ; SIGNAL SUCCESS SD_IO2: POP BC ; RECOVER COUNTERS JR NZ,SD_IO3 ; IF ERROR, BAIL OUT INC C ; BUMP COUNT OF SECTORS READ DJNZ SD_IO1 ; LOOP AS NEEDED SD_IO3: LD E,C ; SECTOR READ COUNT TO E LD HL,(SD_DSKBUF) ; CURRENT DMA TO HL OR A ; SET FLAGS BASED ON RETURN CODE RET Z ; RETURN IF SUCCESS LD A,ERR_IO ; SIGNAL IO ERROR OR A ; SET FLAGS RET ; AND DONE ; ; ; SD_STATUS: ; RETURN DEVICE STATUS LD A,(IY+SD_STAT) ; GET STATUS OF SELECTED DEVICE OR A ; SET FLAGS RET ; AND RETURN ; ; ; SD_RESET: CALL SD_SELUNIT ; SET CUR UNIT ; RE-INITIALIZE THE SD CARD TO ACCOMMODATE HOT SWAPPING CALL SD_INITCARD ; RE-INIT SELECTED UNIT #IF (SDTRACE >= 3) CALL SD_PRTERR ; PRINT ANY ERRORS #ENDIF OR A ; SET RESULT FLAGS RET ; ; ; SD_DEVICE: LD D,DIODEV_SD ; D := DEVICE TYPE LD E,(IY+SD_DEV) ; E := PHYSICAL DEVICE NUMBER LD C,%00110010 ; C := ATTRIBUTES, REMOVABLE, SD CARD LD H,SDMODE ; H := MODE LD L,(SD_IOBASE) ; L := BASE I/O ADDRESS XOR A ; SIGNAL SUCCESS RET ; ; ; SD_MEDIA: LD A,E ; GET FLAGS OR A ; SET FLAGS JR Z,SD_MEDIA2 ; JUST REPORT CURRENT STATUS AND MEDIA ; ; GET CURRENT STATUS LD A,(IY+SD_STAT) ; GET STATUS OR A ; SET FLAGS JR NZ,SD_MEDIA1 ; ERROR ACTIVE, GO RIGHT TO RESET ; ; USE SEND_CSD TO CHECK CARD ;;;LD A,'C' ;;; ;;;CALL COUT ;;; CALL SD_SPD_FAST ; GO FAST FOR COMPATIBILITY 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: ;;;LD A,'R' ;;; ;;;CALL COUT ;;; CALL SD_RESET ; RESET CARD ; SD_MEDIA2: ;;;LD A,'D' ;;; ;;;CALL COUT ;;; CALL SD_SPD_STD ; BACK TO STD SPEED LD A,(IY+SD_STAT) ; GET STATUS OR A ; SET FLAGS LD D,0 ; NO MEDIA CHANGE DETECTED LD E,MID_HD ; ASSUME WE ARE OK RET Z ; RETURN IF GOOD INIT LD E,MID_NONE ; SIGNAL NO MEDIA LD A,ERR_NOMEDIA ; NO MEDIA ERROR OR A ; SET FLAGS RET ; AND RETURN ; ; ; SD_SEEK: BIT 7,D ; CHECK FOR LBA FLAG CALL Z,HB_CHS2LBA ; CLEAR MEANS CHS, CONVERT TO LBA RES 7,D ; CLEAR FLAG REGARDLESS (DOES NO HARM IF ALREADY LBA) LD (IY+SD_LBA+0),L ; SAVE NEW LBA LD (IY+SD_LBA+1),H ; ... LD (IY+SD_LBA+2),E ; ... LD (IY+SD_LBA+3),D ; ... XOR A ; SIGNAL SUCCESS RET ; AND RETURN ; ; ; SD_CAP: LD A,(IY+SD_STAT) ; GET STATUS PUSH AF ; SAVE IT LD A,SD_MEDCAP ; OFFSET TO CAPACITY FIELD CALL LDHLIYA ; HL := IY + A, REG A TRASHED CALL LD32 ; GET THE CURRENT CAPACITY INTO DE:HL LD BC,512 ; 512 BYTES PER BLOCK POP AF ; RECOVER STATUS OR A ; SET FLAGS RET ; ; ; SD_GEOM: ; FOR LBA, WE SIMULATE CHS ACCESS USING 16 HEADS AND 16 SECTORS ; RETURN HS:CC -> DE:HL, SET HIGH BIT OF D TO INDICATE LBA CAPABLE CALL SD_CAP ; GET TOTAL BLOCKS IN DE:HL, BLOCK SIZE TO BC LD L,H ; DIVIDE BY 256 FOR # TRACKS LD H,E ; ... HIGH BYTE DISCARDED, RESULT IN HL LD D,16 | $80 ; HEADS / CYL = 16, SET LBA BIT LD E,16 ; SECTORS / TRACK = 16 RET ; DONE, A STILL HAS SD_CAP STATUS ; ;============================================================================= ; FUNCTION SUPPORT ROUTINES ;============================================================================= ; ; (RE)INITIALIZE CARD ; SD_INITCARD: ; CALL SD_CHKCD ; CHECK CARD DETECT JP Z,SD_NOMEDIA ; Z=NO MEDIA, HANDLE IF SO ; CALL SD_SPD_SLOW ; SET SLOW SPEED FOR INIT ; ; 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 ; #IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_GM) | (SDMODE == SDMODE_EPITX)) ; MAKE SURE CSIO IS DONE SENDING DATA CALL SD_WAITTX ; WAIT FOR TE TO CLEAR CALL DLY32 ; WAIT A BIT MORE FOR FINAL BIT #ENDIF ; ; PUT CARD IN IDLE STATE CALL SD_GOIDLE ; GO TO IDLE JP NZ,SD_NOMEDIA ; CONVERT ERROR TO NO MEDIA ; SD_INITCARD2: LD (IY+SD_TYPE),SD_TYPESDSC ; ASSUME SDSC CARD TYPE ; ; CMD8 IS REQUIRED FOR V2 CARDS. FAILURE HERE IS OK AND ; JUST MEANS THAT IT IS A V1 CARD LD A,SD_CMD_SEND_IF_COND ; SEND_IF_COND CALL SD_INITCMD ; SETUP COMMAND BUFFER LD HL,SD_CMDP2 ; POINT TO 3RD PARM BYTE LD (HL),1 ; VHS=1, 2.7-3.6V INC HL ; POINT TO 4TH PARM BYTE LD (HL),$AA ; CHECK PATTERN INC HL ; POINT TO CRC LD (HL),$87 ; ... AND SET IT TO KNOWN VALUE OF $87 CALL SD_EXECCMDND ; EXEC COMMAND W/ NO DATA RETURNED ; ; GET CARD OUT OF IDLE STATE BY SENDING SD_APP_OP_COND ; REPEATEDLY UNTIL IDLE BIT IS CLEAR LD A,0 LD (SD_LCNT),A SD_INITCARD3: ; DELAY A BIT PER SPEC LD DE,300 ; 16US * 300 = ~5MS CALL VDELAY ; CPU SPEED NORMALIZED DELAY ; SEND APP CMD INTRODUCER CALL SD_EXECACMD ; SEND APP COMMAND INTRODUCER CP SD_STCMDERR ; COMMAND ERROR? JR Z,SD_INITCARD3A ; IF SO, TRY MMC CARD INIT OR A ; SET FLAGS RET NZ ; ABORT IF ANY OTHER ERROR ; SEND APP_OP_COND LD A,SD_ACMD_SEND_OP_COND ; SD_APP_OP_COND CALL SD_INITCMD ; SETUP COMMAND BUFFER LD A,$40 ; P0 = $40 INDICATES WE SUPPORT V2 CARDS LD (SD_CMDP0),A ; SET COMMAND PARM 0 CALL SD_EXECCMDND ; EXEC COMMAND W/ NO DATA RETURNED ;RET NZ ; ABORT ON ERROR CP SD_STCMDERR ; COMMAND ERROR? JR Z,SD_INITCARD3A ; IF SO, TRY MMC CARD INIT OR A ; SET FLAGS RET NZ ; ABORT IF ANY OTHER ERROR ; CHECK FOR IDLE, EXIT LOOP IF IDLE CLEARED LD A,(SD_RC) ; GET CARD RESULT CODE OR A ; SET FLAGS JR Z,SD_INITCARD4 ; IF IDLE BIT CLEAR, EXIT LOOP ; LOOP AS NEEDED LD HL,SD_LCNT ; POINT TO LOOP COUNTER DEC (HL) ; DECREMENT LOOP COUNTER JR NZ,SD_INITCARD3 ; LOOP UNTIL COUNTER EXHAUSTED JP SD_ERRINITTO ; HANDLE INIT TIMEOUT ERROR ; SD_INITCARD3A: ; TRY MMC CARD INITIALIZATION ; CALL SEND_OP_COND UNTIL CARD IS READY (NOT IDLE) LD A,0 LD (SD_LCNT),A SD_INITCARD3B: ; DELAY A BIT PER SPEC LD DE,300 ; 16US * 300 = ~5MS CALL VDELAY ; CPU SPEED NORMALIZED DELAY ; SEND OP_COND COMMAND LD A,SD_CMD_SEND_OP_COND ; SD_OP_COND CALL SD_INITCMD ; SETUP COMMAND BUFFER CALL SD_EXECCMDND ; EXEC COMMAND WITH NO DATA RET NZ ; ABORT ON ERROR ; CHECK FOR IDLE, EXIT LOOP IF IDLE CLEARED LD A,(SD_RC) ; GET CARD RESULT CODE OR A ; SET FLAGS JR Z,SD_INITCARD3C ; IDLE BIT CLEAR, EXIT LOOP ; LOOP AS NEEDED LD HL,SD_LCNT ; POINT TO LOOP COUNTER DEC (HL) ; DECREMENT LOOP COUNTER JR NZ,SD_INITCARD3B ; LOOP UNTIL COUNTER EXHAUSTED JP SD_ERRINITTO ; HANDLE INIT TIMEOUT ERROR ; SD_INITCARD3C: ; SUCCESSFUL MMC CARD INITIALIZATION LD C,SD_TYPEMMC ; MMC CARD TYPE JR SD_INITCARD5 ; RESUME FLOW ; SD_INITCARD4: ; CMD58 RETURNS THE 32 BIT OCR REGISTER (R3), WE WANT TO CHECK ; BIT 30, IF SET THIS IS SDHC/XC CARD LD A,SD_CMD_READ_OCR ; READ_OCR CALL SD_INITCMD ; SETUP COMMAND BUFFER CALL SD_EXECCMD ; EXECUTE COMMAND RET NZ ; ABORT ON ERROR ; CMD58 WORKED, GET OCR DATA LD B,4 ; 4 BYTES OF OCR LD HL,SD_BUF ; PUT IN OUR PRIVATE BUFFER SD_INITCARD4B: PUSH BC ; SAVE LOOP CONTROL CALL SD_GET ; GET NEXT BYTE POP BC ; RESTORE LOOP CONTROL LD (HL),A ; SAVE IT INC HL ; BUMP BUF PTR DJNZ SD_INITCARD4B ; LOOP AS NEEDED CALL SD_DONE ; FINISH THE TRANSACTION ; #IF (SDTRACE >= 3) ; IF TRACING, DUMP THE OCR CONTENTS CALL SD_PRTPREFIX LD DE,SD_STR_OCR CALL WRITESTR LD DE,SD_BUF LD A,4 CALL PRTHEXBUF #ENDIF LD A,(SD_BUF) ; FIRST BYTE OF BUF (BITS 31-24 OF OCR) AND $40 ; ISOLATE BIT 30 (CCS) LD C,SD_TYPESDSC ; ASSUME V1 CARD JR Z,SD_INITCARD5 ; IF BIT NOT SET, THIS IS SDSC CARD ; SD_INITCARD4A: ; ACMD51 RETURNS THE 64 BIT SCR REGISTER (ONLY AVAILABLE ON SDSC AND ABOVE) ; SD_SPEC3 (BIT 47) IS SET IF CARD IS SDXC OR GREATER CALL SD_EXECACMD ; SEND APP COMMAND INTRODUCER RET NZ ; ABORT ON ERROR (THIS SHOULD ALWAYS WORK) LD A,SD_ACMD_SEND_SCR ; APP CMD SEND_SCR CALL SD_INITCMD ; SETUP COMMAND BUFFER CALL SD_EXECCMD ; EXECUTE COMMAND RET NZ ; ABORT ON ERROR (THIS SHOULD ALWAYS WORK) ; ACMD51 SUCCEEDED, NOW GET THE SCR REGISTER CONTENTS LD BC,8 ; 8 BYTES OF SCR LD HL,SD_BUF ; PUT IN OUR PRIVATE BUFFER CALL SD_GETDATA ; GET THE DATA CALL SD_DONE ; CLOSE THE TRANSACTION JP NZ,SD_ERRDATA ; DATA XFER ERROR ; #IF (SDTRACE >= 3) ; IF TRACING, DUMP THE SCR CONTENTS CALL SD_PRTPREFIX LD DE,SD_STR_SCR CALL WRITESTR LD DE,SD_BUF LD A,8 CALL PRTHEXBUF #ENDIF ; ; EXTRACT THE SD_SECURITY FIELD AND SET SDHC/SDXC BASED ON VALUE LD A,(SD_BUF + 1) ; GET THIRD BYTE (BITS 47-40) (55-48) AND %01110000 ; ISOLATE SD_SECURITY BITS CP $40 ; CHECK FOR SDXC VALUE LD C,SD_TYPESDHC ; ASSUME CARD TYPE = SDHC JR NZ,SD_INITCARD5 ; IF NOT SDXC, DONE LD C,SD_TYPESDXC ; OTHERWISE, THIS IS SDXC CARD ; SD_INITCARD5: LD (IY+SD_TYPE),C ; SAVE CARD TYPE #IF (SDTRACE >= 3) CALL SD_PRTPREFIX LD DE,SD_STR_SDTYPE CALL WRITESTR LD A,C CALL PRTHEXBYTE #ENDIF ; ; SET OUR DESIRED BLOCK LENGTH (512 BYTES) LD A,SD_CMD_SET_BLOCKLEN ; SET_BLOCKLEN CALL SD_INITCMD ; SETUP COMMAND BUFFER LD DE,512 ; 512 BYTE BLOCK LENGTH LD HL,SD_CMDP2 ; PUT VALUE INTO PARMS LD (HL),D ; ... HIGH WORD (P0, P1) REMAIN ZERO INC HL ; ... VALUE OF DE GET PUT IN LOW WORD (P2, P3) LD (HL),E ; ... BUT OBSERVE BIG ENDIAN LAYOUT CALL SD_EXECCMDND ; EXEC COMMAND W/ NO DATA RET NZ ; ABORT ON ERROR ; HIGH SPEED CSIO OPERATION IS NOW SET AT THE START OF SD_IO ; ;#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_GM)) ; ; PER SPEC, THE CARD SHOULD NOW BE ABLE TO HANDLE FULL SPEED OPERATION ; ; SO, FOR CSIO OPERATION, WE SET CSIO TO MAXIMUM SPEED ; CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING ; CALL DLY32 ; WAIT A BIT MORE FOR FINAL BIT ; XOR A ; ZERO MEANS MAX SPEED ; OUT0 (SD_CNTR),A ; NOW SET CSIO PORT ;#ENDIF ; ; ISSUE SEND_CSD (TO DERIVE CARD CAPACITY) LD A,SD_CMD_SEND_CSD ; SEND_CSD CALL SD_INITCMD ; SETUP COMMAND BUFFER CALL SD_EXECCMD ; EXECUTE COMMAND RET NZ ; ABORT ON ERROR LD BC,16 ; 16 BYTES OF CSD LD HL,SD_BUF ; PUT IN OUR PRIVATE BUFFER CALL SD_GETDATA ; GET THE DATA CALL SD_DONE ; CLOSE THE TRANSACTION JP NZ,SD_ERRDATA ; DATA XFER ERROR ; #IF (SDTRACE >= 3) ; IF TRACING, DUMP THE CSD CONTENTS CALL SD_PRTPREFIX LD DE,SD_STR_CSD CALL WRITESTR LD DE,SD_BUF LD A,16 CALL PRTHEXBUF #ENDIF ; ; GET SIZE OF DEVICE IN BLOCKS LD A,(IY+SD_TYPE) ; GET CARD TYPE OR A ; SET FLAGS JR Z,SD_INITCARD5A ; HANDLE CARD TYPE UNKNOWN CP SD_TYPESDHC ; COMPARE TO SDHC (V2) JP NC,SD_INITCARD8 ; HANDLE SDHC (V2) OR BETTER JR SD_INITCARD6 ; HANDLE MMC OR SDSC ; SD_INITCARD5A: PUSH AF SYSCHKERR(ERR_NOMEDIA) POP AF JP SD_NOMEDIA ; ; CAPACITY CALCULATION FOR MMC OR SDSC (V1) CARDS: ; BYTES = (C_SIZE + 1) * 2^(2+C_SIZE_MULT+READ_BL_LEN) = (C_SIZE+1) << (2+C_SIZE_MULT+READ_BL_LEN) ; BLOCKS = BYTES / 512 = BYTES >> 9 ; SD_INITCARD6: ; GET SIZE FOR V1 CARD PUSH IX ; SAVE IX LD IX,SD_BUF ; POINT IX TO BUFFER LD A,(IX+6) ; GET C_SIZE MSB AND %00000011 ; MASK OFF TOP 6 BITS (NOT PART OF C_SIZE) LD C,A ; MSB -> C LD D,(IX+7) ; D LD E,(IX+8) ; LSB -> E LD B,6 ; RIGHT SHIFT WHOLE THING BY 6 BITS SD_INITCARD7: SRA C ; SHIFT MSB RR D ; SHIFT NEXT BYTE RR E ; SHIFT LSB DJNZ SD_INITCARD7 ; LOOP TILL DONE PUSH DE ; DE = C_SIZE, SAVE IT LD A,(IX+9) ; GET C_SIZE_MULT MSB LD B,(IX+10) ; GET C_SIZE_MULT LSB SLA B ; SHIFT LEFT MSB RLA ; SHIFT LEFT LSB AND %00000111 ; ISOLATE RELEVANT BITS LD C,A ; C := C_SIZE_MULT LD A,(IX+5) ; GET READ_BL_LEN AND %00001111 ; ISLOATE RELEVANT BITS LD B,A ; B := READ_BL_LEN ; FINAL MULTIPLIER IS 2^(C_SIZE_MULT + READ_BL_LEN + 2) LD A,B ; READ_BL_LEN ADD A,C ; AND C_SIZE_MULT ADD A,2 ; AND 2 MORE BY DEFINITION ; RELOAD C_SIZE AND CONVERT TO 32 BIT VALUE IN DE:HL POP HL ; RECOVE C_SIZE INC HL ; ADD 1 LD DE,0 ; HI WORD IS ZERO ; ADJUST TO 512 BYTE BLOCK COUNT LD B,A ; NORMALIZE TO BYTE COUNT CALL SLA32 ; BIT SHIFT LEFT ACCORDING TO MULTIPLIERS LD B,9 ; NORMALIZE TO 512 BYTE BLOCK COUNT CALL SRL32 ; BIT SHIFT RIGHT 9 BITS POP IX ; RESTORE IX JR SD_INITCARD9 ; RECORD VALUE ; ; CAPACITY CALCULATION FOR SDHC/SDXC (V2/V3) CARDS: ; BLOCKS = (C_SIZE + 1) * 1024 = C_SIZE << 10 ; SD_INITCARD8: ; GET SIZE FOR V2 CARD PUSH IX ; SAVE IX LD IX,SD_BUF ; POINT IX TO BUFFER LD A,(IX + 7) ; GET C_SIZE MSB TO A AND %00111111 ; ISOLATE RELEVANT BITS LD H,(IX + 8) ; GET NEXT BYTE TO H LD L,(IX + 9) ; GET C_SIZE LSB TO L POP IX ; RESTORE IX ; ADD 1 TO C_SIZE IN A:HL LD DE,1 ; LOAD 1 ADD HL,DE ; ADD TO HL ADC A,0 ; HANDLE CARRY ; CONVERT TO 32 BIT, A:HL -> DE:HL LD D,0 LD E,A ; DIVIDE BY 1024 TO NORMALIZE, LEFT SHIFT 10 BITS LD B,10 ; SHIFT BY 10 BITS CALL SLA32 ; SHIFT THE 32 BIT VALUE JR SD_INITCARD9 ; CONTINUE ; SD_INITCARD9: ; SAVE DERIVED CAPACITY VALUE IN DE:HL PUSH HL ; SAVE HL LD A,SD_MEDCAP ; OFFSET TO CAPACITY FIELD CALL LDHLIYA ; HL := IY + A, REG A TRASHED PUSH HL ; MOVE ADDRESS POP BC ; ... TO BC POP HL ; RECOVER HL CALL ST32 ; SAVE THE CAPACITY VALUE (DWORD) ; ; RESET CARD STATUS TO 0 (OK) XOR A ; A := 0 (STATUS = OK) LD (IY+SD_STAT),A ; SAVE IT ; RET ; RETURN, A=0, Z SET ; SECTOR I/O ; SD CARD COMMAND BYTE MUST BE PASSED IN C ; SD_SECTIO: PUSH BC CALL SD_CHKCARD ; CHECK / REINIT CARD AS NEEDED POP BC RET NZ ; ABORT IF REINIT FAILED LD A,C ; LOAD SD CARD COMMAND BYTE CALL SD_INITCMD ; SETUP COMMAND BUFFER CALL SD_SETADDR ; SETUP LBA ADDRESS CALL SD_EXECCMD ; EXECUTE COMMAND RET NZ ; ABORT ON ERROR LD HL,(SD_DSKBUF) LD BC,512 ; LENGTH TO READ LD A,(SD_CMD) ; GET THE COMMAND CP SD_CMD_READ_SNGL_BLK ; READ_SINGLE_BLOCK? JR Z,SD_SECTIO1 ; HANDLE READ CP SD_CMD_WRITE_BLOCK ; WRITE_BLOCK? JR Z,SD_SECTIO2 ; HANDLE WRITE CALL PANIC ; PANIC ON ANYTHING ELSE LD A,ERR_INTERNAL OR A RET SD_SECTIO1: ; GET SECTOR DATA CALL SD_GETDATA ; GET THE BLOCK JR SD_SECTIO3 ; AND CONTINUE SD_SECTIO2: ; PUT SECTOR DATA CALL SD_PUTDATA ; PUT THE BLOCK AND FALL THRU SD_SECTIO3: ; CONTINUE WITH COMMON CODE CALL SD_DONE ; CLOSE THE TRANSACTION RET Z ; RETURN WITH A=0 AND Z SET JP SD_ERRDATA ; DATA XFER ERROR ; ; CHECK THE SD CARD, ATTEMPT TO REINITIALIZE IF NEEDED ; SD_CHKCARD: ; FIX: NEED TO CHECK CARD DETECT HERE AND ; HANDLE AS ERROR. ; LD A,(IY+SD_STAT) ; GET CURRENT STATUS OR A ; SET FLAGS RET Z ; RETURN WITH A=0 AND Z SET JP SD_INITCARD ; OTHERWISE INIT CARD ; ; CONVERT LBA ADDRESS TO CARD SPECIFIC ADDRESS IN CMD PARMS ; V1 CARDS REQUIRE BYTE ADDRESSING, SO A TRANSLATION IS DONE IN THAT CASE ; SD_SETADDR: LD A,(IY+SD_TYPE) ; GET CARD TYPE PUSH AF ; SAVE IT LD A,SD_LBA ; OFFSET OF LBA VALUE CALL LDHLIYA ; HL := IY + A, REG A TRASHED ;;;#IF (DSKYENABLE) ;;; #IF (DSKYDSKACT) CALL HB_DSKACT ; SHOW ACTIVITY ;;; #ENDIF ;;;#ENDIF 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? ;;; DEBUG CALL SD_SELECT ; ASSERT CS CALL SD_DONE ; SEND 8 CLOCKS AND DEASSERT CS ;;; DEBUG ; SMALL DELAY HERE HELPS SOME CARDS ;;LD DE,300 ; 16US * 300 = ~5MS ;LD DE,60 ; 16US * 60 = ~1MS ;CALL VDELAY ; CPU SPEED NORMALIZED DELAY ; PUT CARD IN IDLE STATE LD A,SD_CMD_GO_IDLE_STATE ; CMD0 = ENTER IDLE STATE CALL SD_INITCMD ; INIT COMMAND BUFFER LD A,$95 ; CRC FOR GO_IDLE_STATE COMMAND IS $95 LD (SD_CMDCRC),A ; SET CRC CALL SD_EXECCMDND ; EXECUTE COMMAND W/ NO DATA RETURNED RET NZ ; ABORT ON ERROR LD A,(SD_RC) ; GET CARD RESULT DEC A ; MAP EXPECTED $01 -> $00 RET Z ; ALL IS GOOD, RETURN WITH A=0 AND Z SET JP SD_ERRCMD ; SET COMMAND ERROR VALUE ; ; INITIALIZE COMMAND BUFFER ; COMMAND BYTE IN ACCUM ; HL AND AF DESTROYED ; SD_INITCMD: LD HL,SD_CMDBUF ; POINT TO START OF BUFFER LD (HL),A ; SET THE COMMAND BYTE XOR A ; CLEAR ACCUM LD B,7 ; PREPARE TO CLEAR NEXT 7 BYTES (PARMS, CRC, RC, TOK) SD_INITCMD1: INC HL ; POINT TO NEXT BYTE LD (HL),A ; CLEAR IT DJNZ SD_INITCMD1 ; LOOP TILL DONE ; THE CRC POSITION IS ACTUALLY 7 BITS OF CRC (HIGH BITS) AND ; 1 BIT INDICATING END OF COMMAND. MOST CARDS DON'T REALLY ; CARE ABOUT THE END OF COMMAND BIT, BUT I HAVE WORKED WITH ; AT LEAST ONE PERSON THAT HAD A CARD THAT NEEDED THIS. ; SO, BELOW WE STUFF THE CRC POSITION WITH $FF. DEC A ; $FF TO ACCUM LD (SD_CMDCRC),A ; PUT $FF IN CRC POSITION RET ; ; EXECUTE APP COMMAND ; SD_EXECACMD: LD A,SD_CMD_APP_CMD ; APP_CMD, AN APP CMD IS NEXT CALL SD_INITCMD ; SETUP COMMAND BUFFER JR SD_EXECCMDND ; EXEC COMMAND W/ NO DATA RETURNED ; ; EXECUTE COMMAND WITH NO DATA ; SD_EXECCMDND: CALL SD_EXECCMD ; RUN THE COMMAND JP Z,SD_DONE ; RETURN THRU SD_DONE IF NO ERROR RET ; ERROR STATUS, JUST RETURN, SD_DONE WAS ALREADY RUN ; ; EXECUTE A COMMAND ; WILL FINISH TRANSACTION IF ERROR OCCURS ; RETURNS STATUS IN A WITH ZF SET ACCORDINGLY ; SD_EXECCMD: #IF (SDTRACE >= 3) PUSH AF CALL SD_PRTPREFIX LD DE,SD_CMDBUF PRTS(" CMD$") LD A,6 CALL PRTHEXBUF LD DE,SD_STR_ARROW CALL WRITESTR POP AF #ENDIF ; CALL SD_SELECT ; #IF (SD_NOPULLUP) ; DO NOT WAIT FOR READY PRIOR TO CMD0! THIS HACK IS REQUIRED BY ; STUPID SD CARD ADAPTERS THAT NOW OMIT THE MISO PULL-UP. SEE ; COMMENTS AT TOP OF THIS FILE. LD A,(SD_CMDBUF) CP SD_CMD_GO_IDLE_STATE JR Z,SD_EXECCMD0 #ENDIF ; ; WAIT FOR CARD TO BE READY CALL SD_WAITRDY ; WAIT FOR CARD TO BE READY FOR A COMMAND JP NZ,SD_ERRRDYTO ; HANDLE TIMEOUT ERROR ; SD_EXECCMD0: ; SEND THE COMMAND LD HL,SD_CMDBUF ; POINT TO COMMAND BUFFER LD E,6 ; COMMANDS ARE 6 BYTES SD_EXECCMD1: LD A,(HL) ; PREPARE TO SEND NEXT BYTE CALL SD_PUT ; SEND IT INC HL ; POINT TO NEXT BYTE DEC E ; DEC LOOP COUNTER JR NZ,SD_EXECCMD1 ; LOOP TILL DONE W/ ALL 6 BYTES ; #IF (SD_NOPULLUP) ; THE FIRST FILL BYTE IS DISCARDED! THIS HACK IS REQUIRED BY ; STUPID SD CARD ADAPTERS THAT NOW OMIT THE MISO PULL-UP. SEE ; COMMENTS AT TOP OF THIS FILE. ; ;CALL SD_GET ; GET A BYTE AND DISCARD IT ; ; THE Z180 -K REVISION CSIO DOES NOT KEEP MOSI HIGH WHEN ; RECEIVING VIA MISO. INSTEAD MOSI IS LEFT AT WHICHEVER LOGIC ; LEVEL IT WAS LAST SET TO. THIS CAUSES SOME SD CARDS A PROBLEM ; BECAUSE THEY EXPECT MOSI TO BE CONSISTENTLY HIGH WHEN IDLE. ; BY USING A PUT INSTEAD OF A GET, WE CAN FORCE MOSI TO BE ; LEFT AT THE PROPER LOGIC LEVEL. THE SD CARD DOES NOT CARE ; IF A PUT OR A GET IS USED TO IGNORE THE BYTE BECAUSE THE ; CLOCK RUNS IN EITHER CASE. LD A,$FF ; KEEP MOSI HI CALL SD_PUT ; SEND 8 CLOCKS #ENDIF ; ; GET RESULT LD E,0 ; INIT TIMEOUT LOOP COUNTER #IF (SDMODE == SDMODE_MT) CALL SD_GET ; DISCARD STALE DATA FROM PUT #ENDIF ; 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 AND ~$01 ; MASK OFF IDLE BIT AND SET FLAGS RET Z ; IF RC = 0, NO ERROR, RETURN CALL SD_DONE ; IF ERROR, COMPLETE TRANSACTION JP SD_ERRCMD ; ... AND HANDLE IT ; ; SD_GETDATA ; SD_GETDATA: #IF (SDMODE == SDMODE_MT) LD DE,$7FFF ; LOOP MAX (TIMEOUT) SD_GETDATA1: IN A,(SD_RDTR) CP $FF ; WANT BYTE != $FF JR NZ,SD_GETDATA2 ; NOT $FF, MOVE ON DEC DE BIT 7,D JR Z,SD_GETDATA1 ; KEEP TRYING UNTIL TIMEOUT SD_GETDATA2: LD (SD_TOK),A ; SAVE TOKEN VALUE #IF (SDTRACE >= 3) PUSH AF CALL SD_PRTTOK POP AF #ENDIF CP $FE ; PACKET START? JR NZ,SD_GETDATA4 ; NOPE, ABORT, A HAS ERROR CODE LD D,B ; SIZE TO DB LD B,C LD C,(SD_RDTR) INC B DEC B JR Z,SD_GETDATA3 INC D SD_GETDATA3: INIR ; GET BLOCK DEC D JR NZ,SD_GETDATA3 ; LOOP FOR ALL BYTES IN A,(SD_RDTR) ; DISCARD CRC BYTE 1 IN A,(SD_RDTR) ; DISCARD CRC BYTE 2 #ELSE PUSH HL ; SAVE DESTINATION ADDRESS PUSH BC ; SAVE LENGTH TO RECEIVE LD DE,$7FFF ; LOOP MAX (TIMEOUT) SD_GETDATA1: CALL SD_GET CP $FF ; WANT BYTE != $FF JR NZ,SD_GETDATA2 ; NOT $FF, MOVE ON DEC DE BIT 7,D JR Z,SD_GETDATA1 ; KEEP TRYING UNTIL TIMEOUT SD_GETDATA2: LD (SD_TOK),A ; SAVE TOKEN VALUE #IF (SDTRACE >= 3) PUSH AF CALL SD_PRTTOK POP AF #ENDIF POP DE ; RESTORE LENGTH TO RECEIVE POP HL ; RECOVER DEST ADDRESS CP $FE ; PACKET START? JR NZ,SD_GETDATA4 ; NOPE, ABORT, A HAS ERROR CODE SD_GETDATA3: CALL SD_GET ; GET NEXT BYTE LD (HL),A ; SAVE IT INC HL DEC DE LD A,D OR E JR NZ,SD_GETDATA3 ; LOOP FOR ALL BYTES ;;;CALL PC_SPACE CALL SD_GET ; DISCARD CRC BYTE 1 ;;;CALL PRTHEXBYTE CALL SD_GET ; DISCARD CRC BYTE 2 ;;;CALL PRTHEXBYTE #ENDIF XOR A ; RESULT IS ZERO SD_GETDATA4: RET ; ; SD_PUTDATA ; SD_PUTDATA: #IF (SDMODE == SDMODE_MT) LD D,B ; length to DB LD B,C LD A,$FE ; PACKET START OUT (SD_WRTR),A ; SEND IT LD C,SD_WRTR INC B ; IF B!=0 DEC B JR Z,SD_PUTDATA1 INC D ; THEN D=D+1 SD_PUTDATA1: OTIR ; SEND B BYTES DEC D JR NZ,SD_PUTDATA1 ; LOOP FOR ALL BYTES LD A,$FF ; DUMMY CRC BYTE OUT (SD_WRTR),A OUT (SD_WRTR),A ; SEND IT TWICE LD DE,$7FFF ; LOOP MAX (TIMEOUT) IN A,(SD_RDTR) ; DISCARD STALE DATA FROM OUT SD_PUTDATA2: IN A,(SD_RDTR) #ELSE PUSH HL ; SAVE SOURCE ADDRESS PUSH BC ; SAVE LENGTH TO SEND LD A,$FE ; PACKET START CALL SD_PUT ; SEND IT POP DE ; RECOVER LENGTH TO SEND POP HL ; RECOVER SOURCE ADDRESS SD_PUTDATA1: LD A,(HL) ; GET NEXT BYTE TO SEND CALL SD_PUT ; SEND IF INC HL DEC DE LD A,D OR E JR NZ,SD_PUTDATA1 ; LOOP FOR ALL BYTES LD A,$FF ; DUMMY CRC BYTE CALL SD_PUT LD A,$FF ; DUMMY CRC BYTE CALL SD_PUT LD DE,$7FFF ; LOOP MAX (TIMEOUT) SD_PUTDATA2: CALL SD_GET #ENDIF CP $FF ; WANT BYTE != $FF JR NZ,SD_PUTDATA3 ; NOT $FF, MOVE ON DEC DE BIT 7,D JR Z,SD_PUTDATA2 ; KEEP TRYING UNTIL TIMEOUT SD_PUTDATA3: LD (SD_TOK),A #IF (SDTRACE >= 3) PUSH AF CALL SD_PRTTOK POP AF #ENDIF AND $1F CP $05 RET NZ XOR A RET ; ; WAIT FOR CARD TO BE READY ($FF). MUST ALREADY BE SELECTED. ; SD_WAITRDY: LD DE,$FFFF ; LOOP MAX (TIMEOUT) #IF (SDMODE == SDMODE_MT) CALL SD_GET ; DISCARD STALE DATA - UNKNOWN CONTEXT #ENDIF SD_WAITRDY1: CALL SD_GET INC A ; $FF -> $00 RET Z ; IF READY, RETURN DEC DE LD A,D OR E JR NZ,SD_WAITRDY1 ; KEEP TRYING UNTIL TIMEOUT XOR A ; ZERO ACCUM DEC A ; ACCUM := $FF TO SIGNAL ERROR RET ; TIMEOUT ; ; FINISH A TRANSACTION - PRESERVE AF ; ; PER SPEC: AFTER THE LAST SPI BUS TRANSACTION, THE HOST IS REQUIRED, TO PROVIDE ; 8 (EIGHT) CLOCK CYCLES FOR THE CARD TO COMPLETE THE OPERATION BEFORE SHUTTING ; DOWN THE CLOCK. THROUGHOUT THIS 8 CLOCKS PERIOD THE STATE OF THE CS SIGNAL IS ; IRRELEVANT. IT CAN BE ASSERTED OR DE-ASSERTED. ; ; NOTE THAT I HAVE FOUND AT LEAST ONE MMC CARD THAT FAILS UNLESS THE CS SIGNAL ; REMAINS ACTIVE DURING THE 8 CLOCKS, SO THE CLOCKS ARE SENT BEFORE DESELECTING THE CARD. ; SD_DONE: PUSH AF LD A,$FF CALL SD_PUT #IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_EPITX) | (SDMODE == SDMODE_GM)) ; MAKE SURE CSIO IS DONE SENDING DATA CALL SD_WAITTX ; WAIT FOR TE TO CLEAR CALL DLY32 ; WAIT A BIT MORE FOR FINAL BIT #ENDIF CALL SD_DESELECT POP AF RET ; ;============================================================================= ; HARDWARE INTERFACE ROUTINES ;============================================================================= ; ; PERFORM HARDWARE SPECIFIC INITIALIZATION ; SD_SETUP: ; #IF (SDMODE == SDMODE_PPI) ; PPISD IS DESIGNED TO CORESIDE ON THE SAME PARALLEL PORT ; AS A DSKY. SEE DSKY.ASM FOR DETAILS. LD A,82H ; PPI PORT A=OUT, B=IN, C=OUT OUT (SD_PPIX),A #ENDIF ; #IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_EPITX) | (SDMODE == SDMODE_GM)) ; CSIO SETUP FOR Z180 CSIO ; LD A,2 ; DIV 80, 225KHZ @ 18MHZ CLK LD A,6 ; DIV 1280, 14KHZ @ 18MHZ CLK OUT0 (SD_CNTR),A #ENDIF ; #IF ((SDMODE == SDMODE_JUHA) | (SDMODE == SDMODE_N8) | (SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_GM)) LD A,(HB_RTCVAL) LD (SD_OPRVAL),A OUT (SD_OPRREG),A #ENDIF ; #IF ((SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_DSD) | (SDMODE == SDMODE_PPI) | (SDMODE == SDMODE_MT) | (SDMODE == SDMODE_EPITX)) LD A,SD_OPRDEF LD (SD_OPRVAL),A OUT (SD_OPRREG),A #ENDIF ; #IF (SDMODE == SDMODE_UART) IN A,(SD_OPRREG) ; OPRREG == SIO_MCR AND ~SD_OPRMSK ; MASK OFF SD CONTROL BITS OR SD_OPRDEF ; SET DEFAULT BITS LD (SD_OPRVAL),A ; RECORD THE WORKING VALLUE OUT (SD_OPRREG),A ; OPRREG == SIO_MCR #ENDIF ; #IF ((SDMODE == SDMODE_PIO) | (SDMODE == SDMODE_EZ512) | (SDMODE == SDMODE_K80W)) LD A,SD_OPRDEF ; All output bits high LD (SD_OPRVAL),A ; WBW OUT (SD_OPRREG),A LD A,$CF ; Port B mode 3 OUT (SD_DDR),A LD A,SD_DDRVAL ; Set the direction bits OUT (SD_DDR),A LD A,$07 ; No interrupts OUT (SD_DDR),A #ENDIF ; #IF (SDMODE == SDMODE_USR) #IF (SD_CINIT == TRUE) LD A,(SD_OPRMSK) ; GET OUTPUT PORT MASK CPL ; INVERT ACCUMULATOR LD C,A ; SAVE IT FOR LATER LD A,(SD_DDR) ; GET DATA DIRECTION REGISTER VALUE AND C ; ZERO ALL INTERESTING BITS OR (SD_DDRVAL) ; ADD OUR VALUE LD (SD_DDR),A ; WRITE IT #ENDIF ; LD A,SD_OPRDEF LD (SD_OPRVAL),A OUT (SD_OPRREG),A #ENDIF ; #IF (SDMODE == SDMODE_FZ80) LD A,SD_OPRDEF ; DEFAULT SELECT VALUE LD (SD_OPRVAL),A ; PUT IN SHADOW OUT (SD_OPRREG),A ; WRITE TO PORT XOR A ; LOW SPEED OPERATION LD (SD_CLKSEL),A ; DO IT CALL SD_DESELECT ; MAKE SURE CARD(S) ARE NOT SELECTED #ENDIF ; XOR A RET ; ; TAKE ANY ACTIONS REQUIRED TO SELECT DESIRED PHYSICAL UNIT ; UNIT IS SPECIFIED IN A ; SD_SELUNIT: LD A,(IY+SD_DEV) ; GET CURRENT DEVICE ; #IF (SDMODE == SDMODE_DSD) ; SELECT REQUESTED UNIT OUT (SD_SELREG),A ; ACTUALLY SELECT THE CARD #ENDIF XOR A ; SIGNAL SUCCESS RET ; DONE ; ; CHECK FOR CARD DETECT (NZ = MEDIA PRESENT) ; SD_CHKCD: #IF ((SDMODE == SDMODE_DSD) | (SDMODE == SDMODE_MK4)) IN A,(SD_OPRREG) ; GET OPERATIONS REGISTER BIT 5,A ; TEST CARD DETECT BIT #ELSE OR $FF ; ASSUME CARD PRESENT #ENDIF RET ; DONE ; ; CHECK FOR WRITE PROTECT (NZ = WRITE PROTECTED) ; SD_CHKWP: #IF ((SDMODE == SDMODE_DSD) | (SDMODE == SDMODE_MK4)) IN A,(SD_OPRREG) ; GET OPERATIONS REGISTER BIT 4,A ; TEST WP BIT #ELSE XOR A ; WP NOT SUPPORTED BY HARDWARE, ASSUME WP OFF #ENDIF RET ; AND RETURN ; ; SELECT CARD ; SD_SELECT: ; ; FINISH SENDING BEFORE ASSERTING CS! ;#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_GM)) ; CALL SD_WAITTX ;#ENDIF ; #IF ((SDMODE == SDMODE_SC) | (SDMODE == SDMODE_MT) | (SDMODE == SDMODE_FZ80) | (SDMODE == SDMODE_GM)) LD A,(IY+SD_DEV) ; GET CURRENT DEVICE OR A ; SET FLAGS LD A,(SD_OPRVAL) ; GET CURRENT OPRVAL BACK JR NZ,SD_SELECT1 ; IF NOT ZERO, DO SECONDARY ; ASSERT PRIMARY CS, DEASSERT SECONDARY (IF ANY) OR SD_CS0 #IF (SD_DEVCNT > 1) AND ~SD_CS1 #ENDIF JR SD_SELECT2 SD_SELECT1: ; DEASSERT PRIMARY CS, ASSERT SECONDARY (IF ANY) AND ~SD_CS0 #IF (SD_DEVCNT > 1) OR SD_CS1 #ENDIF #ELSE #IF (SDMODE == SDMODE_EPITX) LD A,(SD_OPRVAL) AND $F8 OR SD_CS0 ; WILL DO 1-7 LATER #ELSE LD A,(SD_OPRVAL) ; GET CURRENT OPRVAL BACK OR SD_CS0 #ENDIF #ENDIF ; SD_SELECT2: ; ADJUST BIT(S) FOR INTERFACES USING INVERTED CS BITS ;#IF ((SDMODE == SDMODE_PPI) | (SDMODE == SDMODE_UART) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_GM)) #IF (SD_INVCS) #IF (((SDMODE == SDMODE_SC) | (SDMODE == SDMODE_FZ80) | (SDMODE == SDMODE_GM)) & (SD_DEVCNT > 1)) XOR SD_CS0 | SD_CS1 #ELSE XOR SD_CS0 #ENDIF #ENDIF LD (SD_OPRVAL),A OUT (SD_OPRREG),A ;; ;#IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_GM)) ; CALL DLY32 ; DELAY FOR FINAL BIT ;#ENDIF ; RET ; ; DESELECT CARD ; SD_DESELECT: #IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_EPITX) | (SDMODE == SDMODE_GM)) ; DON'T REMOVE CS UNTIL WE ARE DONE SENDING! CALL SD_WAITTX ; WAIT FOR TE TO CLEAR ; ; ACCORDING TO Z180 DOCS, IT MAY TAKE UP TO 1 BIT TIME TO ; FINISH SENDING AFTER TE IS CLEARED. THE DELAY BELOW WILL ; DO THIS FOR THE SLOWEST POSSIBLE SEND RATE WHICH IS ; CLK / 1320, SO DELAY AT LEAST 1320 T-STATES ; ; IN PRACTICE, A SMALLER DELAY IS FINE BASED ON LOGIC ANALYZER ; TRACES. CALL DLY32 ; DELAY FOR FINAL BIT #ENDIF ; #IF (SDMODE == SDMODE_FZ80) CALL SD_WAITBSY #ENDIF ; LD A,(SD_OPRVAL) #IF (((SDMODE == SDMODE_SC) | (SDMODE_MT) | (SDMODE == SDMODE_GM)) & (SD_DEVCNT > 1)) AND ~(SD_CS0 | SD_CS1) #ELSE #IF (SDMODE == SDMODE_EPITX) OR 7 ; CHAN 7 IS USED FOR DESELECTS #ELSE AND ~SD_CS0 #ENDIF #ENDIF ; ADJUST BIT(S) FOR INTERFACES USING INVERTED CS BITS #IF (SD_INVCS) #IF (((SDMODE == SDMODE_SC) | (SDMODE == SDMODE_FZ80) | (SDMODE == SDMODE_GM)) & (SD_DEVCNT > 1)) XOR SD_CS0 | SD_CS1 #ELSE XOR SD_CS0 #ENDIF #ENDIF LD (SD_OPRVAL),A OUT (SD_OPRREG),A RET ; #IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_EPITX) | (SDMODE == SDMODE_GM)) ; ; CSIO WAIT FOR TRANSMIT READY (TX REGISTER EMPTY) ; SD_WAITTX: IN0 A,(SD_CNTR) ; GET CSIO STATUS BIT 4,A ; TX EMPTY? JR NZ,SD_WAITTX ; LOOP WHILE BUSY RET ; DONE ; ; CSIO WAIT FOR RECEIVER READY (BYTE AVAILABLE) ; SD_WAITRX: IN0 A,(SD_CNTR) ; WAIT FOR RECEIVER TO FINISH BIT 5,A ; RX EMPTY? JR NZ,SD_WAITRX ; LOOP WHILE BUSY RET ; DONE ; #ENDIF ; #IF (SDMODE == SDMODE_FZ80) ; ; WAIT WHILE FPGA SPI INTERFACE IS BUSY SENDING OR RECEIVING ; SD_WAITBSY: PUSH AF SD_WAITBSY1: IN A,(SD_SELSTAT) BIT 7,A JR NZ,SD_WAITBSY1 POP AF RET #ENDIF ; ; SEND ONE BYTE ; SD_PUT: ; #IF (SDMODE == SDMODE_MT) OUT (SD_WRTR),A #ENDIF ; #IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_EPITX) | (SDMODE == SDMODE_GM)) 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 #ENDIF ; #IF (SDMODE == SDMODE_Z80R) ; USE C - THE CALLING CODE FOR COMMAND SEND FAILS TO SAVE HL/DE ; WHILST THE OTHER PATHS DO ? LD C,A RL C RLA OUT (SD_IOREG),A OUT (SD_IOCLK),A RL C RLA OUT (SD_IOREG),A OUT (SD_IOCLK),A RL C RLA OUT (SD_IOREG),A OUT (SD_IOCLK),A RL C RLA OUT (SD_IOREG),A OUT (SD_IOCLK),A RL C RLA OUT (SD_IOREG),A OUT (SD_IOCLK),A RL C RLA OUT (SD_IOREG),A OUT (SD_IOCLK),A RL C RLA OUT (SD_IOREG),A OUT (SD_IOCLK),A RL C RLA OUT (SD_IOREG),A OUT (SD_IOCLK),A #ENDIF ; #IF ((SDMODE == SDMODE_JUHA) | (SDMODE == SDMODE_N8) | (SDMODE == SDMODE_PPI) | (SDMODE == SDMODE_UART) | (SDMODE == SDMODE_DSD) | (SDMODE == SDMODE_USR) | (SDMODE == SDMODE_PIO) | (SDMODE == SDMODE_EZ512) | (SDMODE == SDMODE_K80W)) #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 ; #IF (SDMODE == SDMODE_FZ80) CALL SD_WAITBSY ; WAIT FOR PENDING ACTIVITY OUT (SD_DATA),A ; POST THE VALUE OUT (SD_ACTION),A ; INITIATE THE WRITE ;;;CALL PC_SPACE ; *DEBUG* ;;;CALL PC_GT ; *DEBUG* ;;;CALL PRTHEXBYTE ; *DEBUG* #ENDIF RET ; DONE ; ; RECEIVE ONE BYTE ; ; SD_GET: ; #IF (SDMODE == SDMODE_MT) IN A,(SD_RDTR) #ENDIF ; #IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_EPITX) | (SDMODE == SDMODE_GM)) CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING IN0 A,(SD_CNTR) ; GET CSIO STATUS SET 5,A ; START RECEIVER OUT0 (SD_CNTR),A CALL SD_WAITRX IN0 A,(SD_TRDR) ; GET RECEIVED BYTE CALL MIRROR ; MSB<-->LSB MIRROR BITS LD A,C ; KEEP RESULT #ENDIF ; #IF (SDMODE == SDMODE_Z80R) ; MUST PRESERVE HL,DE PUSH DE LD A,1 LD C,SD_GPIO OUT (SD_IOREG),A OUT (SD_IOCLK),A IN B,(C) RR B RL E OUT (SD_IOREG),A OUT (SD_IOCLK),A IN B,(C) RR B RL E OUT (SD_IOREG),A OUT (SD_IOCLK),A IN B,(C) RR B RL E OUT (SD_IOREG),A OUT (SD_IOCLK),A IN B,(C) RR B RL E OUT (SD_IOREG),A OUT (SD_IOCLK),A IN B,(C) RR B RL E OUT (SD_IOREG),A OUT (SD_IOCLK),A IN B,(C) RR B RL E OUT (SD_IOREG),A OUT (SD_IOCLK),A IN B,(C) RR B RL E OUT (SD_IOREG),A OUT (SD_IOCLK),A IN B,(C) RR B RL E LD A,E POP DE #ENDIF ; #IF ((SDMODE == SDMODE_JUHA) | (SDMODE == SDMODE_N8) | (SDMODE == SDMODE_PPI) | (SDMODE == SDMODE_UART) | (SDMODE == SDMODE_DSD) | (SDMODE == SDMODE_USR) | (SDMODE == SDMODE_PIO) | (SDMODE == SDMODE_EZ512) | (SDMODE == SDMODE_K80W)) 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) | (SDMODE == SDMODE_PIO) | (SDMODE == SDMODE_EZ512) | (SDMODE == SDMODE_K80W)) 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 ; #IF (SDMODE == SDMODE_FZ80) CALL SD_WAITBSY ; WAIT FOR PENDING ACTIVITY IN A,(SD_ACTION) ; INITIATE READ CALL SD_WAITBSY ; WAIT FOR DONE IN A,(SD_DATA) ; GET THE VALUE #ENDIF RET ; ; SET STANDARD SPEED (RESTORE SPI INTERFACE TO DEFAULTS) ; SD_SPD_STD: ; #IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_EPITX) | (SDMODE == SDMODE_GM)) ; SET CSIO FOR DEFAULT OPERATION PUSH AF ; PRESERVE AF CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING CALL DLY32 ; WAIT A BIT MORE FOR FINAL BIT LD A,Z180_CNTR_DEF ; DIV 1280, 14KHZ @ 18MHZ CLK OUT0 (SD_CNTR),A ; DO IT POP AF ; RESTORE AF #ENDIF RET ; ; SET SLOW SPEED ; SD_SPD_SLOW: ; #IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_EPITX) | (SDMODE == SDMODE_GM)) ; SET CSIO FOR DEFAULT OPERATION PUSH AF ; PRESERVE AF CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING CALL DLY32 ; WAIT A BIT MORE FOR FINAL BIT LD A,Z180_CNTR_DEF ; DIV 1280, 14KHZ @ 18MHZ CLK OUT0 (SD_CNTR),A ; DO IT POP AF ; RESTORE AF #ENDIF RET ; ; SET FAST SPEED ; SD_SPD_FAST: ; #IF ((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_EPITX) | (SDMODE == SDMODE_GM)) ; SET CSIO FOR HIGH SPEED OPERATION PUSH AF ; PRESERVE AF CALL SD_WAITTX ; MAKE SURE WE ARE DONE SENDING CALL DLY32 ; WAIT A BIT MORE FOR FINAL BIT XOR A ; 0 IS HIGHEST CLOCK SPEED OUT0 (SD_CNTR),A ; DO IT POP AF ; RESTORE AF #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: LD (IY+SD_STAT),A ; SAVE NEW STATUS SD_ERR2: #IF (SDTRACE >= 2) CALL SD_PRTSTAT CALL SD_REGDUMP #ENDIF PUSH AF CALL SD_DESELECT ; De-select if there was an error POP AF OR A ; SET FLAGS RET ; ; ; SD_PRTERR: RET Z ; DONE IF NO ERRORS ; FALL THRU TO SD_PRTSTAT ; ; PRINT STATUS STRING ; SD_PRTSTAT: PUSH AF PUSH DE PUSH HL LD A,(IY+SD_STAT) OR A LD DE,SD_STR_STOK JR Z,SD_PRTSTAT1 INC A LD DE,SD_STR_STINVUNIT JR Z,SD_PRTSTAT2 ; INVALID UNIT IS SPECIAL CASE INC A LD DE,SD_STR_STRDYTO JR Z,SD_PRTSTAT1 INC A LD DE,SD_STR_STINITTO JR Z,SD_PRTSTAT1 INC A LD DE,SD_STR_STCMDTO JR Z,SD_PRTSTAT1 INC A LD DE,SD_STR_STCMDERR JR Z,SD_PRTSTAT1 INC A LD DE,SD_STR_STDATAERR JR Z,SD_PRTSTAT1 INC A LD DE,SD_STR_STDATATO JR Z,SD_PRTSTAT1 INC A LD DE,SD_STR_STCRCERR JR Z,SD_PRTSTAT1 INC A LD DE,SD_STR_STNOMEDIA JR Z,SD_PRTSTAT1 INC A LD DE,SD_STR_STWRTPROT JR Z,SD_PRTSTAT1 LD DE,SD_STR_STUNK SD_PRTSTAT1: CALL SD_PRTPREFIX ; PRINT UNIT PREFIX JR SD_PRTSTAT3 SD_PRTSTAT2: CALL NEWLINE PRTS("SD:$") ; NO UNIT NUM IN PREFIX FOR INVALID UNIT SD_PRTSTAT3: CALL PC_SPACE ; FORMATTING CALL WRITESTR POP HL POP DE POP AF RET ; SD_PRTRC: PUSH AF PUSH DE LD DE,SD_STR_RC CALL WRITESTR LD A,(SD_RC) CALL PRTHEXBYTE POP DE POP AF RET ; SD_PRTTOK: PUSH AF PUSH DE LD DE,SD_STR_TOK CALL WRITESTR LD A,(SD_TOK) CALL PRTHEXBYTE POP DE POP AF RET ; ; ; SD_REGDUMP: PUSH AF PUSH BC PUSH HL CALL PC_SPACE CALL PC_LBKT LD HL,SD_CMDBUF LD B,8 SD_REGDUMP1: LD A,(HL) INC HL CALL PRTHEXBYTE DJNZ SD_REGDUMP1 CALL PC_RBKT POP HL POP BC POP AF RET ; ; PRINT DIAGNONSTIC PREFIX ; SD_PRTPREFIX: PUSH AF CALL NEWLINE PRTS("SD$") LD A,(IY+SD_DEV) ; GET CURRENT DEVICE NUM ADD A,'0' CALL COUT CALL PC_COLON POP AF RET ; ;============================================================================= ; 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_OCR .TEXT " OCR =$" SD_STR_SDTYPE .TEXT " SD CARD TYPE ID=$" ; SD_STR_STOK .TEXT "OK$" SD_STR_STINVUNIT .TEXT "INVALID UNIT$" SD_STR_STRDYTO .TEXT "READY TIMEOUT$" SD_STR_STINITTO .TEXT "INITIALIZATION TIMEOUT$" SD_STR_STCMDTO .TEXT "COMMAND TIMEOUT$" SD_STR_STCMDERR .TEXT "COMMAND ERROR$" SD_STR_STDATAERR .TEXT "DATA ERROR$" SD_STR_STDATATO .TEXT "DATA TIMEOUT$" SD_STR_STCRCERR .TEXT "CRC ERROR$" SD_STR_STNOMEDIA .TEXT "NO MEDIA$" SD_STR_STWRTPROT .TEXT "WRITE PROTECTED$" SD_STR_STUNK .TEXT "UNKNOWN$" SD_STR_TYPEUNK .TEXT "UNK$" SD_STR_TYPEMMC .TEXT "MMC$" SD_STR_TYPESDSC .TEXT "SDSC$" SD_STR_TYPESDHC .TEXT "SDHC$" SD_STR_TYPESDXC .TEXT "SDXC$" ; ;============================================================================= ; DATA STORAGE ;============================================================================= ; SD_OPRVAL .DB 0 ; CURRENT OPR REG VALUE SD_LCNT .DB 0 ; LOOP COUNTER SD_CMDVAL .DB 0 ; PENDING COMMAND FOR IO FUCNTIONS SD_BLKCNT .DB 0 ; BLOCK COUNT REQUESTED FOR IO FUNCTIONS ; SD_BUF .FILL 16,0 ; WORK BUFFER ; SD_CMDBUF: ; START OF STD CMD BUF SD_CMD .DB 0 ; COMMAND BYTE SD_CMDP0 .DB 0 ; FIRST PARM BYTE (MSB) SD_CMDP1 .DB 0 SD_CMDP2 .DB 0 SD_CMDP3 .DB 0 ; LAST PARM BYTE (LSB) SD_CMDCRC .DB 0 ; CRC ; SD_RC .DB 0 ; RETURN CODE FROM CMD SD_TOK .DB 0 ; TOKEN FROM DATA XFR ; SD_DSKBUF .DW 0 ; ADR OF ACTIVE DISK BUFFER ; ;============================================================================= ; HELPER ROUTINES ;============================================================================= ; ; MSB<-->LSB MIRROR BITS IN A, RESULT IN C ; MIRROR: #IF (((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_EPITX) | (SDMODE == SDMODE_GM)) & SDCSIOFAST) ; FASTEST BUT USES MOST CODE SPACE LD BC,MIRTAB ; 256 BYTE MIRROR TABLE ADD A,C ; ADD OFFSET LD C,A JR NC,MIRROR2 INC B MIRROR2: LD A,(BC) ; GET RESULT LD C,A ; RETURN RESULT IN C RET #ELSE ; SLOWER BUT LESS CODE SPACE LD C,A ; A = 76543210 RLCA RLCA ; A = 54321076 XOR C AND 0AAH XOR C ; A = 56341270 LD C,A RLCA RLCA RLCA ; A = 41270563 RRC C ; C = 05634127 XOR C AND 066H XOR C ; A = 01234567 LD C,A ; RETURN RESULT IN C RET #ENDIF ; ; LOOKUP TABLE TO MIRROR BITS IN A BYTE ; #IF (((SDMODE == SDMODE_CSIO) | (SDMODE == SDMODE_MK4) | (SDMODE == SDMODE_SC) | (SDMODE == SDMODE_EPITX) | (SDMODE == SDMODE_GM)) & 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