;
;=============================================================================
; 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
;
		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
;
	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)
	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))
      #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))
	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))
	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
