view level1/modules/llcoco3fpga.asm @ 3253:6dec962b072d

Fix up comments and whitespace in llcoco3fpga.asm No code changes.
author Tormod Volden <debian.tormod@gmail.com>
date Sat, 07 Apr 2018 22:56:07 +0200
parents c505ae3120c4
children
line wrap: on
line source

*******************************************************************
* llsd - Low-level SDHC/SD/MMC driver
*
* This driver talks to the SD card interface on the DE1 utilizing the CoCo3FPGA
*
* $FF64 (Control Register Write)
* Bit 7 : SD Enable
* Bit 6 : Enable Interrupt
* Bits 5-1 : not used
* Bit 0 : SPI Device Select #1 (DE1 SD Card)
*
* $FF64 (Status Register Read)
* Bit 7 : IRQ Active
* Bits 6-2 : not used
* Bit 1 : SD Card Locked (Write Protect)
* Bit 0 : SD Installed
*
* $FF65 (Data Register R/W)
*
* Edt/Rev  YYYY/MM/DD  Modified by
* Comment
* ------------------------------------------------------------------
*     1    2012/06/16  Gary Becker
* Rewritten to work with 8 bit SPI on CoCo3FPGA
*     2    2015/06/06  Gary Becker
* Removed any code to enable multiple slots (single slot only)

		NAM		llcoco3fpga
		TTL		Low-level SDHC/SD/MMC driver

		USE		defsfile
		USE		rbsuper.d

tylg		SET		Sbrtn+Objct
atrv		SET		ReEnt+rev
rev		SET		0
edition		SET		4

		MOD		eom,name,tylg,atrv,start,0

		ORG		V.LLMem
* Low-level driver static memory area
SEC_CNT		RMB		1	Number of sectors to transfer
SEC_LOC		RMB		2	Where they are or where they go
SEC_ADD		RMB		3	LSN of sector
SDVersion	RMB		1	0 = Byte Addressable SD
* !0 = Sector Addressable SD
CMDStorage	RMB		1	Command storage area for read/write CMDs
SD_SEC_ADD	RMB		4	Four bytes because some devices are byte addressable
CMDCRC		RMB		1

**************************************
* Command bytes storage area
**************************************
CMD0		fcb		$40,$00,$00,$00,$00,$95
*CMD1		fcb		$41,$00,$00,$00,$00,$95
CMD8		fcb		$48,$00,$00,$01,$AA,$87
*CMD13		fcb		$4D,$00,$00,$00,$00,$95
CMD16		fcb		$50,$00,$00,$02,$00,$FF		was 95
ACMD41V1	fcb		$69,$00,$00,$00,$00,$FF		was 95
ACMD41V2	fcb		$69,$40,$00,$00,$00,$FF		was 95
CMD55		fcb		$77,$00,$00,$00,$00,$FF		was 95
CMD58		fcb		$7A,$00,$00,$00,$00,$FF		was 95

* Read/Write commands
CMDRead		EQU		$5100		Command to read a single block
CMDWrite	EQU		$5800		Command to write a sector
CMDEnd		EQU		$00FF		Every command ends with $95
* SPI Address Equates
* SPI Control Register
SPICTRL		EQU		0
SLOT_SEL_0	EQU		1
SPI_IRQ_EN	EQU		$40
SPI_EN		EQU		$80		Sets SPI enable and IRQ enable
* SPI Status Register
SPISTAT		EQU		0
CARD_DET_0	EQU		1
CARD_LOCK_0	EQU		2
IRQ_SLOT_0	EQU		$80
* SPI Transmit / Receive Register
SPIDAT		EQU		1
* Test 8 bit LED display
SPITRACE	EQU		$FF66

name		FCS		/llcoco3fpga/

start		lbra		ll_init
		bra		ll_read
		nop
		lbra		ll_write
		lbra		ll_getstat
		lbra		ll_setstat
		lbra		ll_term

EREAD
		ldd		#$0000+E$Read	A=Disable SPI Interface, but not CS
* B=Read Error
		lbra		RETERR

* ll_read - Low level read routine
*
* Entry:
*    Registers:
*      Y  = address of path descriptor
*      U  = address of device memory area
*    Static Variables of interest:
*      V.PhysSect = starting physical sector to read from
*      V.SectCnt  = number of physical sectors to read
*      V.SectSize = physical sector size (0=256,1=512,2=1024,3=2048)
*      V.CchPSpot = address where physical sector(s) will go
*
* Exit:
*    All registers may be modified
*    Static variables may NOT be modified
*    Initialization errors are not flagged as an error
*    SDCards are hot pluggable and card might be plugged in later
ll_read
* Setup read command
		orcc		#IntMasks		disable interrupts
		ldx		V.Port-UOFFSET,u	Get address of hardware
		lda		V.SectCnt,u		Get number of sectors to read
		sta		SEC_CNT,u		Save it to our usable storage
		ldd		V.CchPSpot,u		get the location to copy the sector into
		std		SEC_LOC,u		Save it into our usable storage
		ldd		V.PhysSect,u		Copy Sector Adrress into our storage
		std		SEC_ADD,u
		lda		V.PhysSect+2,u
		sta		SEC_ADD+2,u
		lda		SPISTAT,x
		lsra
		bcc		EREAD			No card installed, so no reads
lphr		lda		SEC_CNT,u
		ldd		#CMDRead
		std		CMDStorage,u		Read command and clear MSB of address
		ldd		#CMDEnd
		std		SD_SEC_ADD+3,u		Clear LSB of address and CRC
rd_loop		lda		#(SPI_EN+SLOT_SEL_0)	but not IRQ enable
		bsr		LSNMap0			Setup the appropriate LSN value for the card, build command,
* setup SPI to access the card, and sends command
		bcs		EREAD			If we timed out, branch with error
		bne		EREAD			If the R1 was not 0

		ldy		SEC_LOC,u		Get the sector buffer address
		ldb		#$00			256 loops of 2 reads of 1 bytes is 512 bytes
lprd		lda		SPIDAT,x		Send FF (receive a byte) until we get FE
		cmpa		#$FE			Do we need more cycles??????
		nop
		bne		lprd
* Read the 512 Byte sector
* we need a minumum of 4 CPU cycles to read in the 8 bits
RDSectorLoop	lda		SPIDAT,x
		sta		,y+		? cycles ?????????????
		nop				might be too much ???????
		lda		SPIDAT,x
		sta		,y+
		decb
		bne		RDSectorLoop
* Get the last two bytes of the sector (CRC bytes)
		lda		SPIDAT,x	Send 2x FF to get the CRC
		sty		SEC_LOC,u	Save out buffer pointer
*		nop				Might be too many cycles ?????????
		lda		SPIDAT,x	We ignore the CRC
		dec		SEC_CNT,u	decrement # of hw sectors to read
		beq		finird		if zero, we are finished
*Increment sector number by 1 for sector addressable or $200 for byte addressable
incsec		inc		SEC_ADD+2,u	add one to 3 byte LSN
		bne		lphr		if we are at 0 then we need to add
		inc		SEC_ADD+1,u	the carry to the next byte
		bne		lphr
		inc		SEC_ADD,u
		bra		lphr

* No errors, exit
finird
		ldd		#$0000	Disable SPI and exit
		sta		SPICTRL,x
		andcc		#^(IntMasks+Carry)	Renable Interrupts and clear carry
		rts					return

**************
* LSNMap
* Take physical LSN and convert into SDHC/SD/MMC LSN
* SD/MMC uses a 32 bit byte mapping for the LSN, so we must shift the logical LSN up one bit
* and then clear the 4th byte to build the correct LSN string
* SDHC uses a 32 bit 512 byte sector mapping for the LSN.So there is no need to shift the LSN
* we can just write it as is and clear out the upper LSN byte, because we only get 3 bytes from coco for LSN
* does not preserve Reg A
**************
LSNMap0
		sta		SPICTRL,x
		nop
		lda		SPIDAT,x	Send 1 FF
		lda		SDVersion,u
		bne		secadd		GoTo Sector Address type
*Save the sector number into the command
		ldd		SEC_ADD+1,u	bytes 1 and 2 (middle and LSB)
		aslb				Byte address needs to be shifter one more bit
		rola
		std		SD_SEC_ADD+1,u	and stored in the first 3 bytes of a 4 byte address
		lda		SEC_ADD,u	MSB
		rola
		sta		SD_SEC_ADD,u
		bra		merge
secadd
		ldd		SEC_ADD+1,u	Save the sector number into our storage
		std		SD_SEC_ADD+2,u	Store in the last three bytes of the 4 byte address
		lda		SEC_ADD,u
		sta		SD_SEC_ADD+1,u
merge		lda		SPIDAT,x	Send 1 FF
LSNMap1
		leay		CMDStorage,u
* Fall through to cmdsend

* cmdsend - Sends a 6 byte command
* Entry:  X = HW addr
*         Y = Address of first byte of command sequence
* Exit:
* Registers preserved: all but A/B/X
cmdsend
		lda		,y
		sta		SPITRACE
		ldb		#6
cslp		lda		,y+
		sta		SPIDAT,x
		decb
		bne		cslp
* If finished, fall through to GETR1

* Entry:  X = HW addr
* Exit:   A = R1
*         CC.C = 0 OK
*         CC.C = 1 ERROR
* Registers preserved: all but A/B
GetR1
		andcc		#^Carry		Clear Carry
		ldb		#$00		Probably too much
lpgtr1		lda		SPIDAT,x
		bpl		finigtr1
		decb
		bne		lpgtr1
		comb				set carry for error
finigtr1	rts

* ll_write - Low level write routine
*
* Entry:
*    Registers:
*      Y  = address of path descrip
*      U  = address of device memory area
*    Static Variables of interest:
*      V.PhysSect = starting physical sector to write to
*      V.SectCnt  = number of physical sectors to write
*      V.SectSize = physical sector size (0=256,1=512,2=1024,3=2048)
*      V.CchPSpot = address of data to write to device
*
* Exit:
*    All registers may be modified
*    Static variables may NOT be modified
ll_write
		orcc		#IntMasks	disable interrupts
		ldx		V.Port-UOFFSET,u	Get address of hardware
		lda		V.SectCnt,u		Get number of sectors to write
		sta		SEC_CNT,u	Save it to our usable storage
		ldd		V.CchPSpot,u	get the location to of the sector send
		std		SEC_LOC,u	Save it into our usable storage
		ldd		V.PhysSect,u	Copy Sector Adrress into our storage
		std		SEC_ADD,u
		lda		V.PhysSect+2,u
		sta		SEC_ADD+2,u
		lda		SPISTAT,x
		lsra
		lbcc		EWRITE		No card installed, so no writes
		lsra
		lbcs		EWP		Write Protected, then exit with WP error
* The big read sector loop comes to here
lphw		ldd		#CMDWrite
		std		CMDStorage,u
		ldd		#CMDEnd
		std		SD_SEC_ADD+3,u	LSB of address and CRC
wr_loop		lda		#(SPI_EN+SLOT_SEL_0)
		bsr		LSNMap0		Setup the appropriate LSN value for the card, build command,
* setup SPI to access the card, and sends command
		bcs		EWRITE
		bne		EWRITE

		lda		SPIDAT,x	2 bytes >= 1 byte after R1
		nop				Might not be enough ?????
		nop
		lda		SPIDAT,x
		ldd		#$FE00		Start of sector byte and clear counter
		ldy		SEC_LOC,u	get the location of the sectors(s) to write
		sta		SPIDAT,x	Mark the start of the sector
		nop				Too much ???????
* Write the 512 Byte sector
WRSectorLoop	lda		,y+
		sta		SPIDAT,x
		nop
		lda		,y+
		sta		SPIDAT,x
		decb
		bne		WRSectorLoop
		lda		SPIDAT,x	send two FFs as CRC
		sty		SEC_LOC,u	Save out buffer pointer
		nop				Might not be enough ???????
		stb		SPIDAT,x	Second FF (send 0 to check)
		cmpa		#$E5		Response - Data accepted token
		beq		fnd0		First byte? if not, check four more bytes.
* Make sure the response was accepted
		lda		SPIDAT,x	This should be the data we got back during the write of the last CRC
*		nop				But we do send another ff so we can get the E5
		cmpa		#$E5		Response - Data accepted token
		beq		fnd0		First byte? if not, check three more bytes.
		lda		SPIDAT,x
		cmpa		#$E5		Response - Data accepted token if this is not it, then we have an issue
		beq		fnd0		First byte? if not, check two more bytes.
		lda		SPIDAT,x
		cmpa		#$E5		Response - Data accepted token if this is not it, then we have an issue
		beq		fnd0		First byte? if not, check one more byte.
		lda		SPIDAT,x
		cmpa		#$E5		Response - Data accepted token if this is not it, then we have an issue
		bne		EWRITE		Write error
* Check to see if the write is complete
fnd0		lda		SPIDAT,x
		beq		lpwr2
		bra		fnd0	Ths beq and bra could be a bne, but I want the extra cycles
lpwr2		lda		SPIDAT,x
		cmpa		#$FF
		beq		wfin
		bra		lpwr2
wfin		ldb		#10		Lets send 10 more FF just in case
finlp		lda		SPIDAT,x
		decb
		bne		finlp
		dec		SEC_CNT,u	decrement # of hw sectors to read
		beq		finiwr		if zero, we are finished
		inc		SEC_ADD+2,u	add one to 3 byte LSN
		bne		lphw		if we are at 0 then we need to add
		inc		SEC_ADD+1,u	the carry to the next byte
		lbne		lphw
		inc		SEC_ADD,u
		lbra		lphw
* No errors, exit
finiwr
		ldd		#$0000	Diable SPI and exit
		sta		SPICTRL,x
		andcc		#^(IntMasks+Carry)	Renable Interrupts and clear carry
		rts

* Error handlers
EWRITE
		ldd		#$0000+E$Write	A=Enable SPI Interface, but not CS
* B=Write Error
		bra		RETERR
EWP
		ldd		#$0000+E$WP	A=Enable SPI Interface, but not CS
* B=Write Protect Error

RETERR
		sta		SPICTRL,x	Set the hardware
		andcc		#^IntMasks	Enable interrupts
		coma				Set Carry
		rts
NOTRDY
		ldd		#$0000+E$NotRdy	Turn off controller and turn on IRQ and flag not ready error
		bra		RETERR
* ll_init - Low level init routine
* Entry:
*    Y  = address of device descriptor
*    U  = address of low level device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
* Note: This routine is called ONCE: for the first device
* IT IS NOT CALLED PER DEVICE!
*
ll_init
		orcc		#IntMasks		disable interrupts
		lda		$FFD9			Speed up
		ldx		V.PORT-UOFFSET,u	load x with the hw address for the IRQ routine
		lda		SPISTAT,x
		lsra
		bcc		NOTRDY			If there is no card, nothing to do
* Enable SPI
		ldd		#SPI_EN*256+$10		Enable SPI Interface, but not CS
* 16*8 cycles is >= 74
		sta		SPICTRL,x

*send at least 74 clock cycles with no SS, 16*8 = 128
lpff		lda		SPIDAT,x	Send FF
		decb				2 cycles, need 4
*		nop				2 cycles
		bne		lpff		3 Cycles
*Initialize card 0
CRD0		lda		#SPI_EN+SLOT_SEL_0	Enable SPI and lower CS
		sta		SPICTRL,x

* sdinit - initializes SD Cards, if installed
sdinit
* Send CMD0
		lda		SPIDAT,x	Send 1 FF
		nop				????????? enough
		leay		CMD0,pcr	Might need more cycles ???????
		lda		SPIDAT,x	Send 1 more FF
		lbsr		cmdsend		Also does a GETR1
		bcs		NOTRDY
		anda		#$7E		Idle is ok
		bne		NOTRDY		but nothing else

* Send CMD8
		lda		SPIDAT,x	Send 1 FF
		nop				?????? enough
		leay		CMD8,pcr	Might need more cycles ??????
		lda		SPIDAT,x	Send 1 more FF
		lbsr		cmdsend		Also does an GETR1
		bcs		SDV1
		anda		#$7E
		bne		SDV1
		lda		SPIDAT,x	Byte 1 of R3/R7, throw it away
		nop
		nop				might need more ????????
		nop
		lda		SPIDAT,x	Byte 2 of R3/R7, throw it away
		nop
		nop
		nop
		lda		SPIDAT,x	Byte 3 of R3/R7, should be 1
		cmpa		#$01		2 cycles
		bne		NOTRDY		2 cycles
		nop
		lda		SPIDAT,x	Byte 4 of R3/R7, should be $AA
		cmpa		#$AA		2 cycles
		bne		NOTRDY		2 cycles
		nop

* Send ACMD41 by first CMD55
loop41V2	lda		SPIDAT,x	Send 1 FF
		nop
		leay		CMD55,pcr	might need more ??????
		lda		SPIDAT,x	Send 1 FF
		lbsr		cmdsend		Also does an GETR1
		bcs		NOTRDY
		anda		#$7E		Idle is ok
		bne		NOTRDY		but nothing else

* Then send ACMD41
		lda		SPIDAT,x
		nop
		leay		ACMD41V2,pcr
		lda		SPIDAT,x
		lbsr		cmdsend
		bcs		NOTRDY		No response
		beq		Send58		If 0 then CMD58
		cmpa		#$01		if 1 then try again
		beq		loop41V2
		lbra		NOTRDY

* Send CMD58 to V2 card
Send58		lda		SPIDAT,x
		nop				?????? ENOUGH
		leay		CMD58,pcr	Read OCR
		lda		SPIDAT,x
		lbsr		cmdsend
		lbcs		NOTRDY
		lda		SPIDAT,x	Byte 1 of OCR
		anda		#$40		CCS bit 1= sector 0= byte
		sta		SDVersion,u
		lda		SPIDAT,x	Byte 2 of R3/R7, throw it away
		nop
		nop
		lda		SPIDAT,x	Byte 3 of R3/R7, throw it away
		nop
		nop
		lda		SPIDAT,x	Byte 4 of R3/R7, throw it away
		lda		SDVersion,u	0 = byte addressable, !0 = block addressable
		bne		FININIT
		bra		Send16

* Send ACMD41 by first CMD55
SDV1

loop41V1	lda		SPIDAT,x	Get extra bytes in case we got a bad R7 previously
		nop
		lda		SPIDAT,x
*		nop
		clr		SDVersion,u	Byte addressable
		lda		SPIDAT,x
*		nop
		leay		CMD55,pcr
		lda		SPIDAT,x
		lbsr		cmdsend
		lbcs		NOTRDY
		anda		#$7E		Idle is ok
		lbne		NOTRDY		but nothing else

* Then send ACMD41
		lda		SPIDAT,x
*		nop
		leay		ACMD41V1,pcr
		lda		SPIDAT,x
		lbsr		cmdsend
		lbcs		NOTRDY
		beq		Send16		If 0 then CMD16
		cmpa		#$01		if 1 then try again
		beq		loop41V1
		lbra		NOTRDY
* Send CMD16
Send16		lda		SPIDAT,x
*		nop
		leay		CMD16,pcr
		lda		SPIDAT,x
		lbsr		cmdsend
		lbne		NOTRDY		but nothing else
* Finish INIT
FININIT
		lda		SPIDAT,x	Send last FF
*		nop
*		lda		SDVersion,u
*		sta		SPITRACE
*		lda		#SPI_EN+SPI_IRQ_EN	Turn on SPI and Interrupt and turn off CS
		lda		#SPI_EN			Turn on SPI and turn off CS
		sta		SPICTRL,x

*Finished with initialization
*Use the stat routine to return

* ll_getstat - Low level GetStat routine
* ll_setstat - Low level SetStat routine
*
* Entry:
*    Y   = address of path descriptor
*    U   = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
ll_getstat
ll_setstat
		andcc		#^Carry
		clrb
		rts

* ll_term - Low level term routine
*
* Entry:
*    Y  = address of device descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
* Note: This routine is called ONCE: for the last device
* IT IS NOT CALLED PER DEVICE!
*
ll_term
		clrb
		andcc		#^Carry
		rts

		EMOD
eom		EQU		*
		END