From ef11059382aed4926c251a47e01d1db72f85893a Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Tue, 7 May 2019 14:50:24 -0400 Subject: [PATCH] parts/z80/sdc: new part Incomplete, but what it does *does* work. I could handshake CMD0+CMD8 on a MicroSD HC. --- parts/z80/sdc.asm | 133 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 parts/z80/sdc.asm diff --git a/parts/z80/sdc.asm b/parts/z80/sdc.asm new file mode 100644 index 0000000..c3e599d --- /dev/null +++ b/parts/z80/sdc.asm @@ -0,0 +1,133 @@ +; sdc +; +; Manages the initialization of a SD card and implement a block device to read +; and write from/to it, in SPI mode. +; +; Note that SPI can't really be used directly from the z80, so this part +; assumes that you have a device that handles SPI communication on behalf of +; the z80. This device is assumed to work in a particular way. +; +; That device has 3 ports. One write-only port to make CS high, one to make CS +; low (data sent is irrelevant), and one read/write port to send and receive +; bytes with the card through the SPI protocol. The device acts as a SPI master +; and writing to that port initiates a byte exchange. Data from the slave is +; then placed on a buffer that can be read by reading the same port. +; +; It's through that kind of device that this code below is supposed to work. + +; *** Defines *** +; SDC_PORT_CSHIGH: Port number to make CS high +; SDC_PORT_CSLOW: Port number to make CS low +; SDC_PORT_SPI: Port number to send/receive SPI data + +; Wake the SD card up. After power up, a SD card has to receive at least 74 +; dummy clocks with CS and DI high. We send 80. +sdcWakeUp: + out (SDC_PORT_CSHIGH), a + ld b, 10 ; 10 * 8 == 80 + ld a, 0xff +.loop: + out (SDC_PORT_SPI), a + nop + djnz .loop + ret + +; Initiate SPI exchange with the SD card. A is the data to send. Received data +; is placed in A. +sdcSendRecv: + out (SDC_PORT_SPI), a + nop + nop + in a, (SDC_PORT_SPI) + nop + nop + ret + +; sdcSendRecv 0xff until the response is something else than 0xff for a maximum +; of 20 times. Returns 0xff if no response. +sdcWaitResp: + push bc + ld b, 20 +.loop: + ld a, 0xff + call sdcSendRecv + inc a ; if 0xff, it's going to become zero + jr nz, .end ; not zero? good, that's our command + djnz .loop +.end: + ; whether we had a success or failure, we return the result. + ; But first, let's bring it back to its original value. + dec a + pop bc + ret + +; Sends a command to the SD card, along with arguments and specified CRC fields. +; (CRC is only needed in initial commands though). +; A: Command to send +; H: Arg 1 (MSB) +; L: Arg 2 +; D: Arg 3 +; E: Arg 4 (LSB) +; C: CRC +; +; Returns R1 response in A. +; +; This does *not* handle CS. You have to select/deselect the card outside this +; routine. +sdcCmd: + ; Wait until ready to receive commands + push af + call sdcWaitResp + pop af + + call sdcSendRecv + ; Arguments + ld a, h + call sdcSendRecv + ld a, l + call sdcSendRecv + ld a, d + call sdcSendRecv + ld a, e + call sdcSendRecv + ; send CRC + ld a, c + call sdcSendRecv + + ; And now we just have to wait for a valid response... + call sdcWaitResp + ret + +; Send a command that expects a R1 response, handling CS. +sdcCmdR1: + out (SDC_PORT_CSLOW), a + call sdcCmd + out (SDC_PORT_CSHIGH), a + ret + +; Send a command that expects a R7 response, handling CS. A R7 is a R1 followed +; by 4 bytes. Those 4 bytes are returned in HL/DE in the same order as in +; sdcCmd. +sdcCmdR7: + out (SDC_PORT_CSLOW), a + call sdcCmd + + ; We have our R1 response in A. Let's try reading the next 4 bytes in + ; case we have a R3. + push af + ld a, 0xff + call sdcSendRecv + ld h, a + ld a, 0xff + call sdcSendRecv + ld l, a + ld a, 0xff + call sdcSendRecv + ld d, a + ld a, 0xff + call sdcSendRecv + ld e, a + pop af + + out (SDC_PORT_CSHIGH), a + ret