From ae470397d7321cff020a4fe681657877799e6b41 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Fri, 17 Apr 2020 15:11:32 -0400 Subject: [PATCH 1/3] emul/hw/rc2014: add sdc emulator (wip) so far, it goes through initialization with the rc2014/sdcard recipe. --- emul/hw/rc2014/Makefile | 2 +- emul/hw/rc2014/classic.c | 30 +++++++++++++ emul/hw/rc2014/sdc.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++ emul/hw/rc2014/sdc.h | 28 ++++++++++++ 4 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 emul/hw/rc2014/sdc.c create mode 100644 emul/hw/rc2014/sdc.h 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); From 7544b3834b1b6c195d974185ad59dd2d17dd8073 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Fri, 17 Apr 2020 19:10:22 -0400 Subject: [PATCH 2/3] emul/hw/rc2014: implement blkread in sdc I now get to "fls" in recipes/rc2014/sdcard! --- emul/hw/rc2014/classic.c | 11 +++++++++-- emul/hw/rc2014/sdc.c | 45 ++++++++++++++++++++++++++++++++++++++++++--- emul/hw/rc2014/sdc.h | 6 ++++++ 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/emul/hw/rc2014/classic.c b/emul/hw/rc2014/classic.c index 1791dfd..f04003d 100644 --- a/emul/hw/rc2014/classic.c +++ b/emul/hw/rc2014/classic.c @@ -68,8 +68,8 @@ static void iowr_sdc_cslow(uint8_t val) int main(int argc, char *argv[]) { - if (argc != 2) { - fprintf(stderr, "Usage: ./classic /path/to/rom\n"); + if (argc < 2) { + fprintf(stderr, "Usage: ./classic /path/to/rom [sdcard.img]\n"); return 1; } FILE *fp = fopen(argv[1], "r"); @@ -107,6 +107,10 @@ int main(int argc, char *argv[]) acia_init(&acia); sdc_init(&sdc); + if (argc == 3) { + fprintf(stderr, "Setting up SD card image\n"); + sdc.fp = fopen(argv[2], "r+"); + } m->iord[ACIA_CTL_PORT] = iord_acia_ctl; m->iord[ACIA_DATA_PORT] = iord_acia_data; m->iowr[ACIA_CTL_PORT] = iowr_acia_ctl; @@ -152,5 +156,8 @@ int main(int argc, char *argv[]) tcsetattr(0, TCSADRAIN, &saved_term); emul_printdebug(); } + if (sdc.fp) { + fclose(sdc.fp); + } return 0; } diff --git a/emul/hw/rc2014/sdc.c b/emul/hw/rc2014/sdc.c index f863038..9560698 100644 --- a/emul/hw/rc2014/sdc.c +++ b/emul/hw/rc2014/sdc.c @@ -1,6 +1,16 @@ #include #include "sdc.h" +// Add data to crc with polynomial 0x1021 +// https://stackoverflow.com/a/23726131 +static uint16_t crc16(uint16_t crc, uint8_t data) +{ + uint8_t x = crc >> 8 ^ data; + x ^= x>>4; + crc = (crc << 8) ^ ((uint16_t)(x << 12)) ^ ((uint16_t)(x <<5)) ^ ((uint16_t)x); + return crc; +} + void sdc_init(SDC *sdc) { sdc->selected = false; @@ -8,6 +18,8 @@ void sdc_init(SDC *sdc) sdc->recvidx = 0; sdc->sendidx = -1; sdc->resp = 0xff; + sdc->fp = NULL; + sdc->cmd17bytes = -1; } void sdc_cslow(SDC *sdc) @@ -38,6 +50,20 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) } return; } + if (sdc->cmd17bytes >= 0) { + if (sdc->fp) { + sdc->resp = getc(sdc->fp); + } + sdc->crc16 = crc16(sdc->crc16, sdc->resp); + sdc->cmd17bytes++; + if (sdc->cmd17bytes == 512) { + sdc->sendbuf[3] = sdc->crc16 >> 8; + sdc->sendbuf[4] = sdc->crc16 & 0xff; + sdc->sendidx = 3; + sdc->cmd17bytes = -1; + } + return; + } if ((sdc->recvidx == 0) && ((val > 0x7f) || (val < 0x40))) { // not a command return; @@ -51,8 +77,9 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) 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; + uint16_t arg1 = (b[1] << 8) | b[2]; + uint16_t arg2 = (b[3] << 8) | b[4]; if (sdc->initstat == 8) { // At this stage, we're expecting CMD0 if (cmd == 0) { @@ -64,7 +91,7 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) } if (sdc->initstat == 9) { // At this stage, we're expecting CMD8 with 0x1aa arg2 - if ((cmd == 8) && (b[3] == 0x01) && (b[4] == 0xaa)) { + if ((cmd == 8) && (arg2 == 0x01aa)) { sdc->initstat++; sdc->sendbuf[0] = 0x01; sdc->sendbuf[1] = 0; @@ -90,7 +117,7 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) } if (sdc->initstat == 11) { // At this stage, we're expecting CMD41 - if ((cmd == 41) && (b[1] == 0x40) && (b[2] == 0x00)) { + if ((cmd == 41) && (arg1 == 0x4000)) { sdc->initstat++; sdc->sendbuf[4] = 0x00; sdc->sendidx = 4; @@ -100,6 +127,18 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) return; } // We have a fully initialized card. + if (cmd == 17) { + if (sdc->fp) { + fseek(sdc->fp, arg2*512, SEEK_SET); + } + sdc->sendbuf[3] = 0x00; + // data token + sdc->sendbuf[4] = 0xfe; + sdc->sendidx = 3; + sdc->cmd17bytes = 0; + sdc->crc16 = 0; + return; + } // Simulate success for any unknown command. sdc->sendbuf[4] = 0x00; sdc->sendidx = 4; diff --git a/emul/hw/rc2014/sdc.h b/emul/hw/rc2014/sdc.h index 22748cd..0d5aa19 100644 --- a/emul/hw/rc2014/sdc.h +++ b/emul/hw/rc2014/sdc.h @@ -19,6 +19,12 @@ typedef struct { // One byte response. When all other response buffers are empty, return // this. uint8_t resp; + // File used for contents read/write + FILE *fp; + // number of bytes read into the current CMD17. -1 means no CMD17 active. + int cmd17bytes; + // running crc16 during read and write operations. + uint16_t crc16; } SDC; void sdc_init(SDC *sdc); From 9c41744e46eec7e48635c78609244ddc0ce5e706 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Fri, 17 Apr 2020 20:19:55 -0400 Subject: [PATCH 3/3] emul/hw/rc2014: implement blk write in sdc All of that should help me implement sdc support much faster in Forth. --- emul/hw/rc2014/sdc.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ emul/hw/rc2014/sdc.h | 3 +++ 2 files changed, 56 insertions(+) diff --git a/emul/hw/rc2014/sdc.c b/emul/hw/rc2014/sdc.c index 9560698..b684048 100644 --- a/emul/hw/rc2014/sdc.c +++ b/emul/hw/rc2014/sdc.c @@ -20,6 +20,7 @@ void sdc_init(SDC *sdc) sdc->resp = 0xff; sdc->fp = NULL; sdc->cmd17bytes = -1; + sdc->cmd24bytes = -2; } void sdc_cslow(SDC *sdc) @@ -64,6 +65,47 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) } return; } + if (sdc->cmd24bytes == -1) { + if (val == 0xff) { + // it's ok to receive idle bytes before the data token. + return; + } + if (val == 0xfe) { + // data token, good + sdc->cmd24bytes = 0; + } else { + // something is wrong, cancel cmd24 + sdc->cmd24bytes = -2; + } + return; + } + if (sdc->cmd24bytes >= 0) { + if (sdc->cmd24bytes < 512) { + if (sdc->fp) { + putc(val, sdc->fp); + } + sdc->crc16 = crc16(sdc->crc16, val); + } else if (sdc->cmd24bytes == 512) { + // CRC MSB + if (val == (sdc->crc16>>8)) { + fprintf(stderr, "Good CRC16 MSB\n"); + } else { + fprintf(stderr, "Bad CRC16 MSB\n"); + } + } else { + if (val == (sdc->crc16&0xff)) { + fprintf(stderr, "Good CRC16 LSB\n"); + } else { + fprintf(stderr, "Bad CRC16 LSB\n"); + } + // valid response for CMD24 + sdc->sendbuf[4] = 0x05; + sdc->sendidx = 4; + sdc->cmd24bytes = -3; + } + sdc->cmd24bytes++; + return; + } if ((sdc->recvidx == 0) && ((val > 0x7f) || (val < 0x40))) { // not a command return; @@ -139,6 +181,17 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) sdc->crc16 = 0; return; } + if (cmd == 24) { + fprintf(stderr, "cmd24\n"); + if (sdc->fp) { + fseek(sdc->fp, arg2*512, SEEK_SET); + } + sdc->sendbuf[4] = 0x00; + sdc->sendidx = 4; + sdc->cmd24bytes = -1; + sdc->crc16 = 0; + return; + } // Simulate success for any unknown command. sdc->sendbuf[4] = 0x00; sdc->sendidx = 4; diff --git a/emul/hw/rc2014/sdc.h b/emul/hw/rc2014/sdc.h index 0d5aa19..6a84ecb 100644 --- a/emul/hw/rc2014/sdc.h +++ b/emul/hw/rc2014/sdc.h @@ -23,6 +23,9 @@ typedef struct { FILE *fp; // number of bytes read into the current CMD17. -1 means no CMD17 active. int cmd17bytes; + // number of bytes received for the current CMD24. -2 means no CMD24 active. + // -1 means we're still waiting for the data token. + int cmd24bytes; // running crc16 during read and write operations. uint16_t crc16; } SDC;