Mercurial > hg > Members > kono > nitros9-code
changeset 3139:96cb9d947e0b
New low-level driver for CoCo3FPGA SD card interface
From Gary Becker.
Add llcoco3fpga.asm to level1/modules
author | Bill Pierce <merlinious999@gmail.com> |
---|---|
date | Sat, 04 Feb 2017 11:06:18 +0100 |
parents | 2071c1c4b6c8 |
children | 03b580a02945 |
files | level1/modules/llcoco3fpga.asm |
diffstat | 1 files changed, 567 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/level1/modules/llcoco3fpga.asm Sat Feb 04 11:06:18 2017 +0100 @@ -0,0 +1,567 @@ +******************************************************************* +* 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) + +*Level EQU 2 + + 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 $FF66 + 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 16 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, 12*8 = 96 +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 Sens 1 more FF + lbsr cmdsend Also does an GETR1 + bcs SDV1 + anda #$7E + bne SDV1 + lda SPIDAT,x Byte 1 of R3/R7, through it away + nop + nop might need more ???????? + nop + lda SPIDAT,x Byte 2 of R3/R7, throught 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, through it away + nop + nop + lda SPIDAT,x Byte 3 of R3/R7, through it away + nop + nop + lda SPIDAT,x Byte 4 of R3/R7, through 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 $ff66 +* 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