; --------------------------------------------------------------
; CPCSD IO driver
; Copyright 2012, Adrien Destugues
; Copyright 2012, Mauricio Muñoz Lucero
; Adapted from 6809 code by Daniel Coulom

; This program is distributed under the terms of the MIT Licence
; The original 6809 code is distributed under a non-commercial licence as
; follows:
; This code is shared freely in the hope it will be useful, but without any
; guarantee. The author denies all responsability. You can use, modify and
; share it, as long as you keep this licence and references to the author in
; every copy. Commercial exploitation is not allowed.

; I think that changes made in this version are important enough to allow me to
; use the MIT licence instead. Here is a list of changes :
; * Rewrite of the code for z80, with a lot of changes to make use of all the
; registers in the CPU and lack of addressing modes
; * Change of IO ports to match the Amstrad CPC ones instead of thomson
; * Add support for an activity LED
; * Error handling : we print the error to screen in this example
; * Optimisation for little endian systems: SD commands are stored backwards in
; memory
; * Use of the "read multiple sector" and "write multiple sector" commands for
; faster operation
; * Added a lot of comments on what we are doing and why
; The only things left from the original code is some label names and the
; commands sent to the SD card for initialisation. Since there are not that
; much ways to do that anyway, I think it is safe to say this is a different
; implementation of the same thing.
; If you disagree, or think you might get into problems, just stick to the
; original licence and be safe.

; --------------------------------------------------------------
; SD card interface for Amstrad CPC
; Uses SD card in SPI mode with bit-banging through the printer port.
; Wiring of the card is as follows :
; MOSI		<- D0
; SCK		<- D1
; Busy LED	<- D2 (lit when bit set to 0)
; MOSI		-> /BUSY
; GND		-- GND (there are plenty of them)
; 5V		-- extra soldering needed on CPC. I put it on pin /INIT so it
;				so it doesn't harm other devices (this pin should be high level
;				in centronics standard)

; Quick reminder about the parallel port :
; Port EFxx : Data lines
; Port F5xx, bit 6 : /BUSY bit (card input)

; TODO:
; *ROM friendly version (CMD17/24 buffer init is done in RAM, need to allocate
; some space for it !)
; *Reduce RAM usage as much as possible
; * Some SDHC cards actually use byte addressing, we need to check for it (in
; answer to CMD8) and adjust SDTYP
; *Tape-port version for internal mounting in the 6128Plus, using :
; F600 bit 5 -> MOSI
; F600 bit 4 -> CLK
; F500 bit 7 <- MISO
; (this version likely has room for slightly more optimizations)
; There is no separate port for a LED, so use the LED instead of one of the
; 4148s (needs different wiring !), to still get an activity indicator.
; +5v --->LED>----+----R 470----CPC
;                 |
;              SD card
; Needs a LED with 1.7V to 2V voltage drop (to get 3 to 3.3V towards SD card)
; Make sure this doesn't create some problems. (switching time and offset
; voltage). Since this is designed for internal mounting, going with no led at
; all is likely safer (there will be no way to eject the card)

; Some compilation information:
; vasmz80_oldstyle -Dtest cpcsd_m.z80 -Fbin -o A.BIN
;
; Some macro exist:
;  * lowfootprint: create a lighter (but slower) version (~40kbps)
;  * nowrite: desactivate write sector function
;  * test: activate test code
; vasmz80_oldstyle -Dlowfootprint -Dnowrite cpcsd_m.z80 -Fbin -o A.BIN

; Write
WRITE_PORT		EQU $EF

DATA_OUT		EQU $01
CLOCK			EQU $02
ACCESS_LED		EQU $04

DATA_OUT_BIT	EQU 1
CLOCK_BIT		EQU 2
ACCESS_LED_BIT	EQU 3

; Read
READ_PORT		EQU $F5
DATA_IN			EQU $40
DATA_IN_BIT		EQU 6

START_TOKEN		EQU $FE

GET_BIT_6		EQU 0b11000000

; Macro
  ifdef lowfootprint
	macro RD_BIT_TO_REG ; (15 NOP)
	OUT		(C),C		; (4) C: FF = clock high, data high, led off
	IN		A,(C)		; (4) bit6 is relevant
	RLA					; (1) ... now bit 7
	OUT		(C),B		; (4) B: E5 = clock low, data high, led off.
	RLA					; (1) ... and now the carry
	RL		\1			; (1) and we can put it in "\1" bit 0
	endmacro
  else
	macro RD_BIT_TO_REG
	OUT		(C),C		; (4) C: FF = clock high, data high, led off
	IN		A,(C)		; (4) bit6 is relevant
	RLA					; (1) ... now bit 7
	OUT		(C),B		; (4) B: E5 = clock low, data high, led off.
	RLA					; (1) ... now bit 7
;    ADD D   ; Put bit into carry
	RL		\1			; (1) and we can put it in "\1" bit 0
	endmacro
  endif

	macro RD_BYTE_TO_REG ; Valid Reg: ?:
	rept	8
	RD_BIT_TO_REG \1;
	endr
	endmacro

	macro RD_BYTE_N_DROP
	rept	8
	OUT		(C),C	; C: FF = clock high, data high, led off
;	IN		F,(C)	; bit6 is relevant
	OUT		(C),B	; B: E5 = clock low, data high, led off.
	endr
	endmacro

	macro CALL_RBYTE_N_DROP
  ifdef lowfootprint
	CALL RBYTE
	CALL RBYTE
  else
	RD_BYTE_N_DROP
	RD_BYTE_N_DROP
  endif
	endmacro

	ORG $4000

 ifdef test
; ------------------------------------------------------
; TEST CODE : just write the screen, then read it again.
start
	CALL	SD_Init
	; Set start address
;    LD   HL,(&B8B4) ; Recuperation de TIME
;    PUSH HL
	LD   HL,$C000
	LD   (ADDR0),HL
	; Set end address + 1
	LD   HL,$0000
	LD   (ADDR1),HL

;	CALL	SD_WRITE
	CALL	SD_READ
;    LD   HL,(&B8B4) ; Recuperation de TIME
;    PUSH HL
;    POP HL
;    CALL printhex
;    LD   H,L
;    CALL printhex
;    POP HL
;    CALL printhex
;    LD   H,L
;    CALL printhex
	JR $				; Done !
  endif

; ------------------------------------------------------
;  Initialize the card
; ------------------------------------------------------
SD_Init

; First of all, we need to send at least 74 clock pulses to the card so it can
; self-initialize.
	LD   L,$0A								; loop 10 times
.loop_power
	CALL RBYTE								; just send some clock pulses
	DEC  L
	JR   NZ,.loop_power

; Then, we send CMD0 to the card. This command resets the card and make it
; select SPI mode, because the chip select line is tied to ground (in hardware)
	LD   IY,CMD0
	CALL EXECMD
	JP   C,ERROR

; For SDHC cards, there is an extra step to do for initializing.
; As this command is only valid for SDHC, we don't check the return code.
	LD   IY,CMD8							; commande CMD8
	CALL EXECMD								; execution commande

; Now the card is intialized and ready to accept acual commands.
; We initialize it with "Application Command" 41. This command will only work
; for mass storage cards, and not for SDIO.
; It is an application command, so we need to prefix it with CMD55.
INIT
	; Try 32 times
	LD   IXL,$20
.loop_initialization
	LD   IY,CMD55
	CALL EXECMD

	LD   IY,ACMD41
	CALL EXECMD
	CP   (IY)
	JR   Z,TESTSD

  ifdef debug
	; Just show some activity so the user knows we're working (with my test
	; card we run this loop 3 times)
	LD A,'.'
	CALL $BB5A
;	DI
  endif

	DEC  IXL
	JR   NZ,.loop_initialization

	JP   ERROR

; Now the card is initialized, we read the OCR register that gives us
; informations on the type of card (SD or SDHC) and the voltage it can work
; with. Since we use 3.3V and this is mandatory for all cards (it would already
; be toast at this point anyway), we ignore the voltage info and keep only the
; relevant bit (SD/SDHC)
; This is important because the read and write routines are slightly different.
TESTSD
	LD   IY,CMD58							; Read OCR
	CALL EXECMD
	JP   C,ERROR

	; The OCR is 4 bytes, but only the first one is relevant.
	CALL RBYTE
	LD	A,E
	ADD	A,E
	LD   (SDTYP),A

	CALL RBYTE
	CALL RBYTE
	CALL RBYTE

	; yay! initialization succesful!
	RET

; ------------------------------------------------------
;  EXECUTE A COMMAND
;  Input: IY points to command buffer
;  Corrupts: A, BC, DE, IXH, IY
; ------------------------------------------------------
; The command buffer is stored backwards in memory. The first byte sent is the
; one pointer by IY, then IY is decremented to get the next byte. This is
; because the SD card needs sector IDs in big endian format, and on the CPC it
; is easier to do the math in little endian. This way the reversing is handled
; witohut any cost
EXECMD

; Wait for SD card to be ready. When the card is busy, it does not answer to
; SPI requests and we read FF. When the card is ready, we read something else
; and we can go on with sending commands.
.loop_wait_ready
	CALL RBYTE
	LD A,E
	CP $FF
	JR NZ,.loop_wait_ready

; A command is made of 6 bytes :
; Command ID + $40
; 4 parameter bytes
; 1 CRC byte

; The CRC byte is ignored in SPI mode, except for the first commands (CMD0 and
; CMD8). For other commands, we can put any value, but we still must send the
; byte.
	LD   IXH,6
.loop_send_cmd
	LD   C,(IY + 0)							; Get next byte
	DEC  IY
	CALL WBYTE								; and write it !
	DEC  IXH
	JR   NZ,.loop_send_cmd

; The card can be busy again, then after some time it will send a return code.
; We wait for at most 256 attempts - notice IXH was left at FF by previous
; loop.
; The expected return code is stored just below the command itself, pointed by
; IY
.loop_get_return_code
	CALL RBYTE								; Get card return code
	LD A,E
	CP   (IY)								; Is it the right code ?
	RET  Z									; If so, we're done 
											; (CP cleared the carry)
	DEC  IXH								; Retry 256 times in the worst case
	JR   NZ,.loop_get_return_code
	SCF										; We didn't get the code in time
	RET										; Set the carry to indicate error.

; ------------------------------------------------------
;  ERROR HANDLING
; Input: A = error code
; ------------------------------------------------------
; So far we just print e, followed by the error code as found in A.
; Then, we enter an infinite loop.
ERROR
	JP INIT
;	PUSH AF
;	LD A,'e'
;	Call $BB5A

;	POP HL
;	CALL printhex

	JR   $

; ------------------------------------------------------
; READ SECTORS from card
; Input: ADDR0 = start address
;		ADDR1 = end address + 1 (first byte not written)
;		We only read full sectors, so we need ADDR1 = ADDR0 + n*512.
; ------------------------------------------------------
SD_READ
	LD   HL,(ADDR0)							; get start of buffer

.send_cmd_read_sector
; TODO maybe when reading only one sector, it is faster to use CMD17.
; In that case we don't need CMD12 at the end, and the card may answer faster
; (multiple read may make the card prepare a full 2K bloc or so)
	LD   IY,CMD18							; Send read sector command
	CALL EXECMD
	JP   C,ERROR

; The card can take as much time as it wants to answer this command. When it's
; ok, it will send the start token (FE) followed by the sector data.
.loop_wait_begin_sector
	CALL RBYTE
	LD A,E

	CP   START_TOKEN						; Is it the start  yet ?
	JR   NZ,.loop_wait_begin_sector

; We read 256*2 bytes - that's one sector.
  ifdef lowfootprint
	LD   IXH,0 ; 3NOP
.loop_read_sector
	CALL RBYTE								; Read a byte
	LD   (HL),E								; Store it in the destination area
	INC  HL
	CALL RBYTE
	LD   (HL),E
	INC  HL
	DEC  IXH   ; 2NOP
	JR   NZ,.loop_read_sector
  else
	; WARNING: BC need to contain $E5FF (previously done by CALL RBYTE)
	LD A,64
	LD D,GET_BIT_6
.loop_read_sector
	LD   I,A
	rept 8
	RD_BYTE_TO_REG E
	LD   (HL),E
	INC  HL
	endr
	LD   A,I
	DEC  A
	JP   NZ,.loop_read_sector
;.loop_read_sector
;    LD   I,A
;	RD_BYTE_TO_REG E                    ; Read a byte
;    LD   (HL),E                         ; Store it in the destination area
;    INC  HL
;	RD_BYTE_TO_REG E
;    LD   (HL),E
;    INC  HL
;    LD   A,I
;    DEC  A
;    JP   NZ,.loop_read_sector
  endif

; The card also sends us an useless 16 bit CRC.
  ifdef lowfootprint
	CALL RBYTE
	CALL RBYTE
  else
	RD_BYTE_N_DROP
	RD_BYTE_N_DROP
  endif

; We can now test if we reached the last sector
; Since sectors are 512-byte aligned, we don't need to check the low byte of
; the addresses. This makes the code quite a bit simpler. 
.last_sector_reached
	LD   A,(ADDR1+1)
	XOR H
  ifdef lowfootprint
	JR   NZ,.loop_wait_begin_sector				; read again if needed
  else
	JP   NZ,.loop_wait_begin_sector				; read again if needed
  endif

	; Send the command to end data transmission
	LD IY,CMD12
	CALL EXECMD
	JP C,ERROR

	; Loading finished !
	RET

  ifndef nowrite
; ------------------------------------------------------
; WRITE SECTORS to the SD card
; Very similar to the read code, but the other way around.
; ------------------------------------------------------
SD_WRITE
	LD   HL,(ADDR0)								; buffer address

.send_cmd_write_sector
	LD   IY,CMD25								; This time we send CMD24
	CALL EXECMD
	JP   C,ERROR

.next_sector
	; TODO wait for card to get ready (we read FF from it) before writing first
	; sector. Looks my test card is ok without it, but...

	; Like in read mode, we send a start token to tell the card we're starting
	LD   C,START_TOKEN
	CALL WBYTE

	; Then we send 256*2 bytes - a sector again.
  ifdef lowfootprint
	LD   IXL,0
.loop_write_sector
	rept 2
	LD   C,(HL)									; get next byte
	INC  HL
	CALL WBYTE									; write it
	endr
	DEC  IXL
	JR   NZ,.loop_write_sector
  else
	LD   IXL,64
.loop_write_sector
	rept 8
	LD   C,(HL)									; get next byte
	INC  HL
	CALL WBYTE									; write it
	endr
	DEC  IXL
	JP   NZ,.loop_write_sector
  endif

	; Finally, we need to write CRC bytes. They are ignored by the card anyway,
	; so we use the RBYTE routine which will actually be identical to writing
	; $FF (but slightly faster)
	CALL_RBYTE_N_DROP
	CALL_RBYTE_N_DROP

	; And, we need to wait for the card to finish the operation. For all other
	; cases we rely on the next call to EXECMD to do this, but here, we are
	; writing data, and we don't want it to get lost.
	; When we are not accessing the card, there is no clock for it, so it is
	; sleeping and can't do any work.
	; When the card is done it will stop sending FF bytes.
.loop_wait_write_finish
	CALL RBYTE
	LD A,E
	CP $FF
	JR NZ,.loop_wait_write_finish

.last_sector_reached
	LD   A,(ADDR1+1)
	XOR H
  ifdef lowfootprint
	JR   NZ,.next_sector						; lecture secteur suivant
  else
	JP   NZ,.next_sector						; lecture secteur suivant
  endif

	LD IY,CMD25
	CALL EXECMD
	JP C,ERROR

	RET

; Now for the low-level layer. Reading and writing a byte to the card is
; what we will be doing most of the time.
; You'll see that this code is optimized as much as possible (can you do better
; than us ?)
  endif

; ------------------------------------------------------
;  READ BYTE
;  Output : E = byte read
;  Corrupts: AF, BC
; ------------------------------------------------------
RBYTE
  ifdef lowfootprint
	LD		BC,$E5FF
	LD		D,8
.loop_read_byte
	RD_BIT_TO_REG E;
	DEC	D
	JR  NZ,.loop_read_byte
	RET
  else
	LD		BC,$E5FF
		; E5 = Write AND Read port. As they both decode the I/O direction as
		; well as the address, and work only in a single way, this is fine,
		; there is no IO conflict.
	LD D,GET_BIT_6
	RD_BYTE_TO_REG E
	RET
  endif

; ------------------------------------------------------
;  WRITE BYTE
;	Input: C = byte to write
;  Corrupts: A, BC, DE
; ------------------------------------------------------
WBYTE
  ifdef lowfootprint
	LD   DE,$0608
		; D = clock=1, led=1, data=0 (will be ORed later)
		; E = loop counter
	LD   B,WRITE_PORT
	XOR A
.loop_write_byte
	RL   C				; Get the first bit in carry
	ADC  D

	OUT  (C),A			; Clock = 1, Led = 1, data = as needed
	XOR  A				; Everything = 0. We should keep the data bit, but this
	OUT  (C),A			; looks to work ok and is 1 NOP faster...

	; ... and go on for all 8 bytes (we could unroll that).
	DEC  E
	JR   NZ,.loop_write_byte

	RET
  else
	LD   D,$06
		; D = clock=1, led=1, data=0 (will be ORed later)
	LD   B,WRITE_PORT
	XOR A
	REPT 8
	RL   C				; Get the first bit in carry
	ADC D

	OUT  (C),A			; Clock = 1, Led = 1, data = as needed
	XOR A				; Everything = 0. We should keep the data bit, but this
	OUT  (C),A			; looks to work ok and is 1 NOP faster...
	ENDR

	RET
  endif



; ------------------------------------------------------
;  COMMANDS
; Stored with last sent byte first - This makes it easier to handle math with
; sectors and the like
; 		RESULT, CRC
; 		PARAMS
; CMDx	VALUE
; ------------------------------------------------------
	DEFB $01,$95 
	DEFB $00,$00,$00,$00
CMD0 
	DEFB $40								; go iddle state

	DEFB $00,$87
	DEFB $AA,$01,$00,$00
CMD8
	DEFB $48								; send interface condition

	DEFB $00,$00
	DEFB $00,$00,$00,$00
CMD12
	DEFB $40 + 12							; end multiple sector operation

	DEFB $00,$65
	DEFB $00,$00,$00,$00
CMD55
	DEFB $77								; application command

	DEFB $00,$77
	DEFB $00,$00,$00,$40 
ACMD41
	DEFB $69								; activate card initialization 

	DEFB $00,$FF
	DEFB $00,$00,$00,$00
CMD58
	DEFB $7A								; read OCR

; ------------------------------------------------------
;  RAM DATA BUFFERS
; ------------------------------------------------------

; READSECTOR command. RSECT1 and RSECT2 will be replaced with actual sector pos.
	DEFB 0,$FF
RSECT2
	DEFS 1									; SDHC address
RSECT1
	DEFS 3									; SD address
CMD18
	DEFB $52

; WRITESECTOR command. WSECT1 and WSECT2 will be replaced with actual sector pos.
	DEFB 0,$FF
WSECT2
	DEFS 1									; SDHC address
WSECT1
	DEFS 3									; SD address
CMD25
	DEFB $59


ADDR0 
	DEFS 2									; Load address

ADDR1
	DEFS 2									; End load address

SDTYP
	DEFS 1									; type $00=SD, $80=SDHC

  ifdef test
; ---------------------------------------------------------------------------
; Print H value as hex
; Corrupts AF
; Only used in ERROR handler.
; ---------------------------------------------------------------------------
printhex
	LD   A,H
	CALL .bin2hex1
	LD   A,H
	JR   .bin2hex2
.bin2hex1
	RRA
	RRA
	RRA
	RRA
.bin2hex2
	OR   $F0
	DAA
	ADD  A,$A0
	ADC  A,$40
	CALL &BB5A
;	DI
	RET
; ------------------------------------------------------
  endif
	END
