diff --git a/emul/hw/rc2014/Makefile b/emul/hw/rc2014/Makefile index 3ba5a7b..1ed63a4 100644 --- a/emul/hw/rc2014/Makefile +++ b/emul/hw/rc2014/Makefile @@ -1,5 +1,5 @@ EXTOBJS = ../../emul.o ../../libz80/libz80.o -OBJS = acia.o classic.o +OBJS = acia.o sdc.o classic.o TARGET = classic .PHONY: all diff --git a/emul/hw/rc2014/classic.c b/emul/hw/rc2014/classic.c index 1f58e49..1791dfd 100644 --- a/emul/hw/rc2014/classic.c +++ b/emul/hw/rc2014/classic.c @@ -13,13 +13,18 @@ #include #include "../../emul.h" #include "acia.h" +#include "sdc.h" #define RAMSTART 0x8000 #define ACIA_CTL_PORT 0x80 #define ACIA_DATA_PORT 0x81 +#define SDC_CSHIGH 0x06 +#define SDC_CSLOW 0x05 +#define SDC_SPI 0x04 #define MAX_ROMSIZE 0x2000 static ACIA acia; +static SDC sdc; static uint8_t iord_acia_ctl() { @@ -41,6 +46,26 @@ static void iowr_acia_data(uint8_t val) acia_data_wr(&acia, val); } +static uint8_t iord_sdc_spi() +{ + return sdc_spi_rd(&sdc); +} + +static void iowr_sdc_spi(uint8_t val) +{ + sdc_spi_wr(&sdc, val); +} + +static void iowr_sdc_cshigh(uint8_t val) +{ + sdc_cshigh(&sdc); +} + +static void iowr_sdc_cslow(uint8_t val) +{ + sdc_cslow(&sdc); +} + int main(int argc, char *argv[]) { if (argc != 2) { @@ -81,10 +106,15 @@ int main(int argc, char *argv[]) } acia_init(&acia); + sdc_init(&sdc); m->iord[ACIA_CTL_PORT] = iord_acia_ctl; m->iord[ACIA_DATA_PORT] = iord_acia_data; m->iowr[ACIA_CTL_PORT] = iowr_acia_ctl; m->iowr[ACIA_DATA_PORT] = iowr_acia_data; + m->iord[SDC_SPI] = iord_sdc_spi; + m->iowr[SDC_SPI] = iowr_sdc_spi; + m->iowr[SDC_CSHIGH] = iowr_sdc_cshigh; + m->iowr[SDC_CSLOW] = iowr_sdc_cslow; char tosend = 0; while (emul_step()) { diff --git a/emul/hw/rc2014/sdc.c b/emul/hw/rc2014/sdc.c new file mode 100644 index 0000000..f863038 --- /dev/null +++ b/emul/hw/rc2014/sdc.c @@ -0,0 +1,114 @@ +#include +#include "sdc.h" + +void sdc_init(SDC *sdc) +{ + sdc->selected = false; + sdc->initstat = 0; + sdc->recvidx = 0; + sdc->sendidx = -1; + sdc->resp = 0xff; +} + +void sdc_cslow(SDC *sdc) +{ + sdc->selected = true; +} + +void sdc_cshigh(SDC *sdc) +{ + sdc->selected = false; +} + +void sdc_spi_wr(SDC *sdc, uint8_t val) +{ + if (!sdc->selected) { + return; + } + sdc->resp = 0xff; + if (sdc->initstat < 8) { + // not woken yet. + sdc->initstat++; + return; + } + if (sdc->sendidx >= 0) { + sdc->resp = sdc->sendbuf[sdc->sendidx++]; + if (sdc->sendidx == 5) { + sdc->sendidx = -1; + } + return; + } + if ((sdc->recvidx == 0) && ((val > 0x7f) || (val < 0x40))) { + // not a command + return; + } + sdc->recvbuf[sdc->recvidx++] = val; + if (sdc->recvidx < 6) { + // incomplete command + return; + } + // Command complete + val &= 0x3f; + sdc->recvidx = 0; + uint8_t *b = sdc->recvbuf; + fprintf(stderr, "cmd %02x %02x %02x %02x %02x\n", b[0], b[1], b[2], b[3], b[4]); + uint8_t cmd = b[0] & 0x3f; + if (sdc->initstat == 8) { + // At this stage, we're expecting CMD0 + if (cmd == 0) { + sdc->initstat++; + sdc->sendbuf[4] = 0x01; + sdc->sendidx = 4; + } + return; + } + if (sdc->initstat == 9) { + // At this stage, we're expecting CMD8 with 0x1aa arg2 + if ((cmd == 8) && (b[3] == 0x01) && (b[4] == 0xaa)) { + sdc->initstat++; + sdc->sendbuf[0] = 0x01; + sdc->sendbuf[1] = 0; + sdc->sendbuf[2] = 0; + sdc->sendbuf[3] = 0x01; + sdc->sendbuf[4] = 0xaa; + sdc->sendidx = 0; + } else { + sdc-> initstat = 8; + } + return; + } + if (sdc->initstat == 10) { + // At this stage, we're expecting CMD55 + if (cmd == 55) { + sdc->initstat++; + sdc->sendbuf[4] = 0x01; + sdc->sendidx = 4; + } else { + sdc->initstat = 8; + } + return; + } + if (sdc->initstat == 11) { + // At this stage, we're expecting CMD41 + if ((cmd == 41) && (b[1] == 0x40) && (b[2] == 0x00)) { + sdc->initstat++; + sdc->sendbuf[4] = 0x00; + sdc->sendidx = 4; + } else { + sdc->initstat = 8; + } + return; + } + // We have a fully initialized card. + // Simulate success for any unknown command. + sdc->sendbuf[4] = 0x00; + sdc->sendidx = 4; +} + +uint8_t sdc_spi_rd(SDC *sdc) +{ + if (!sdc->selected) { + return 0xff; + } + return sdc->resp; +} diff --git a/emul/hw/rc2014/sdc.h b/emul/hw/rc2014/sdc.h new file mode 100644 index 0000000..22748cd --- /dev/null +++ b/emul/hw/rc2014/sdc.h @@ -0,0 +1,28 @@ +#include +#include + +typedef struct { + bool selected; + // Initialization status. 0 == not woken 8 == woken 9 == CMD0 received + // 10 == CMD8 received, 11 == CMD55 received, 12 == CMD41 received (fully + // initialized). + unsigned int initstat; + // We receive commands into this buffer. + uint8_t recvbuf[6]; + // Where the next SPI byte should be stored in recvbuf. + unsigned int recvidx; + // Buffer to the arguments for a response + uint8_t sendbuf[5]; + // Index of the next byte from sendbuf we should return. If -1, buffer is + // empty. + int sendidx; + // One byte response. When all other response buffers are empty, return + // this. + uint8_t resp; +} SDC; + +void sdc_init(SDC *sdc); +void sdc_cslow(SDC *sdc); +void sdc_cshigh(SDC *sdc); +void sdc_spi_wr(SDC *sdc, uint8_t val); +uint8_t sdc_spi_rd(SDC *sdc);