;
; sdcard.s - MTX REMEMOTECH or REMEMOrizer SD Card routines
;
; http://users.ece.utexas.edu/~valvano/EE345M/SD_Physical_Layer_Spec.pdf
;
        .module sdcard

p_sdctl	=	0xd4
p_sddat	=	0xd6
p_sddaf	=	0xd7

        .area   _CODE

; Look for REMEMOTECH or REMEMOrizer
; int sdcheck(void)
_sdcheck::
	ld	a,(0xffc1)
	cp	#'R'
	jr	nz,sdcheck2
	ld	a,(0xffc2)
	cp	#'E'
	jr	nz,sdcheck2
	ld	a,(0xffc3)
	cp	#'M'
	jr	z,sdcheckm
	cp	#'Z'
sdcheck2:
	jr	z,sdcheckz
	ld	hl,#0
	ret
sdcheckm:
	; patch code to go faster
	ld	a,#0+62		; SD Card at 0.4MHz on REMEMOTECH, disabled
	ld	(sd04d+1),a
	ld	a,#0x80+62	; SD Card at 0.4MHz on REMEMOTECH, enabled
	ld	(sd04e+1),a
	ld	hl,#sdrblkm
	ld	(sdrcall+1),hl
	ld	hl,#sdwblkm
	ld	(sdwcall+1),hl
	ld	hl,#1
	ret
sdcheckz:
	ld	hl,#1
	ret

; int sdread(unsigned long block, unsigned count, unsigned char *dest)
_sdread::
	ld	iy,#0
	add	iy,sp
	call	sdinit
	jr	nz,sdread2
	ld	l,8(iy)
	ld	h,9(iy)
	call	sdread
	jr	nz,sdread4
	call	sdterm
	ld	hl,#1
	ret
sdread2:
	call	sdterm
sdread4:
	ld	hl,#0
	ret

; int sdwrite(unsigned long block, unsigned count, const unsigned char *dest)
_sdwrite::
	ld	iy,#0
	add	iy,sp
	call	sdinit
	jr	nz,sdwrite2
	ld	l,8(iy)
	ld	h,9(iy)
	call	sdwrite
	jr	nz,sdwrite4
	call	sdterm
	ld	hl,#1
	ret
sdwrite2:
	call	sdterm
sdwrite4:
	ld	hl,#0
	ret

;
sdinit:

; do "warmup"
sd04d:
	ld	a,#0+4		; SD Card at 0.4MHz, disabled
	out	(p_sdctl),a	; without enabled bit
	ld	b,#10		; 10*8 pulses (>74) for a "warmup"
sdini1:	ld	a,#0xff
	out	(p_sddat),a
sdini2:	in	a,(p_sdctl)
	bit	7,a
	jr	z,sdini2
	djnz	sdini1

; enable card
sd04e:
	ld	a,#0x80+4	; SD Card at 0.4MHz, enabled
	out	(p_sdctl),a

; go idle
	ld	b,#255
sdini3:	push	bc
	xor	a		; go_idle
	call	sdcmd0
	pop	bc
	cp	#0x01
	jr	z,sdini4
	djnz	sdini3
	xor	a
	out	(p_sdctl),a	; disable sd card
	dec	a
	ret			; nz => error accessing sd card
sdini4:

; initialise the card
	ld	bc,#1000	; can take a while
sdini5:	push	bc
	ld	a,#0x37		; app_cmd
	call	sdcmd0
	ld	a,#0x29		; send_op_cond, ie: initialise card
	call	sdcmd0
	pop	bc
	and	a
	jr	z,sdini6
	dec	bc
	ld	a,b
	or	c
	jr	nz,sdini5
	xor	a
	out	(p_sdctl),a	; disable sd card
	dec	a
	ret			; nz => error initialising sd card
sdini6:

; switch to full speed
	ld	a,#0x80		; enable SD Card at full speed
	out	(p_sdctl),a
	xor	a		; z => ok
	ret

;
; terminate access to sd card
;
sdterm:	xor	a
	out	(p_sdctl),a	; disable SD Card
	ret

;
sdread:	push	de
	push	hl
; compute byte offset
	call	sdslba
; send command
	ld	a,#0x12		; read_multiple_block
	call	sdcmd
; await response
sdrea1:	ld	b,#255
sdrea2:	call	spicmf		; send ff
	cp	#0xfe		; means data will follow
	jr	z,sdrea3
	djnz	sdrea2
	pop	hl
	pop	de
	xor	a
	dec	a		; nz => failed data read
	ret
; read the bytes
sdrea3:	ld	bc,#p_sddaf	; b=0, c=port to read data and send next ff
	pop	hl
	push	hl

sdrcall:
	call	sdrblkz		; can be patched to sdwblkz

; another block
	pop	af
	push	hl
	dec	6(iy)
	jr	nz,sdrea1
	ld	a,#0x0c		; stop_transmission
	call	sdcmd0

; done
	pop	hl
	pop	de
	xor	a		; => ok
	ret

sdrblkm:
	in	a,(c)		; send first ff
	inir			; first 256 bytes
	inir			; next 256 bytes
	in	a,(c)		; get crc lsb, send ff
	in	a,(p_sddat)	; get crc msb
	ret

sdrblkz:
				; >=16+7+4 - ~4 = 23t per byte
	in	a,(c)		; 12t
	nop			;  4t
	nop			;  4t
	nop			;  4t
	nop			;  4t
inilp1:	ini			; 16t
	jr	nz,inilp1	; 12t/7t
	nop			;  4t
inilp2:	ini			; 16t
	jr	nz,inilp2	; 12t/7t
	nop			;  4t
	in	a,(c)		; 12t
	nop			;  4t
	nop			;  4t
	nop			;  4t
	nop			;  4t
	in	a,(p_sddat)	; 12t
	ret

;
sdwrite:
	push	de
	push	hl
; compute byte offset
	call	sdslba
; send command
	ld	b,#255
sdwri2:	ld	a,#0x18		; write_block
	call	sdcmd
	and	a
	jr	z,sdwri3
	djnz	sdwri2
	pop	hl
	pop	de
	xor	a
	dec	a		; nz => failed data write
	ret
; write the bytes
sdwri3:	call	spicmf		; send ff dummy byte
	ld	a,#0xfe		; means data will follow
	call	spicmd
; write the bytes
	ld	bc,#p_sddat	; b=0, c=port to write data
	pop	hl
	push	hl

sdwcall:
	call	sdwblkz		; can be patched to sdwblkm

	call	spicmf		; send dummy crc lsb
	call	spicmf		; send dummy crc msb
sdwri4:	call	spicmf		; wait for completion
	inc	a		; ff signals completion
	jr	nz,sdwri4
; done
	pop	hl
	pop	de
	xor	a		; => ok
	ret

sdwblkm:
	otir			; first 256 bytes
	otir			; next 256 bytes
	ret

sdwblkz:
				; >=16+7+4 - ~4 = 23t per byte
otilp1:	outi			; 16t
	jr	nz,otilp1	; 12t/7t
	nop			;  4t
otilp2:	outi			; 16t
	jr	nz,otilp2	; 12t/7t
	nop			;  4t
	ret
;
; sd command
;   before a=command, hlde=params
;   after a=status
;
sdcmd0:	ld	hl,#0
	ld	d,h
	ld	e,l
sdcmd:	push	af
	call	spicmf		; dummy byte
	pop	af
	or	#0x40
	call	spicmd
	ld	a,h
	call	spicmd
	ld	a,l
	call	spicmd
	ld	a,d
	call	spicmd
	ld	a,e
	call	spicmd
	ld	a,#0x95		; checksum for go_idle
	call	spicmd
	call	spicmf		; dummy byte, to give card a chance to process
	ld	b,#10
sdcmdr:	call	spicmf		; dummy byte, fetch the result
	bit	7,a
	ret	z
	djnz	sdcmdr
	ret
;
; set sd lba
; (iy+2..5) is 32 bit LBA, but we must compute byte offset
;
sdslba:	ld	e,#0
	ld	d,2(iy)
	ld	l,3(iy)
	ld	h,4(iy)
	sla	d
	rl	l
	rl	h		; hlde <<= 9
	ret
;
; spi command
;
spicmf:	ld	a,#0xff
spicmd:	out	(p_sddat),a
spicm2:	in	a,(p_sdctl)
	bit	7,a
	jr	z,spicm2
	in	a,(p_sddat)
	ret
