From 97a46a7b9b6300deb4b77d48d92bdf7157d57517 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Thu, 29 Oct 2020 15:01:25 -0400 Subject: [PATCH] emul/z80: decouple SDC and SPI My idea of plugging a RC2014 bridge directly onto a Sega Master System cartridge doesn't work. The SMS eats all I/O addr space, we can't use it. Therefore, this naive idea, in the emulator, of reusing sdc.c in sms.c as-is, doesn't work either. I'll have to find another way of communicating to a SPI device on the SMS. I'll probably do it through a controller port. Meanwhile, I need to decouple SPI from SDC in the emulator code so that I can reuse sdc.c. This is what is done here. --- emul/z80/Makefile | 2 +- emul/z80/emul.h | 1 + emul/z80/rc2014.c | 27 ++++++++++++++--------- emul/z80/rc2014_spi.c | 28 +++++++++++++++++++++++ emul/z80/rc2014_spi.h | 17 ++++++++++++++ emul/z80/sdc.c | 60 +++++++++++++++++--------------------------------- emul/z80/sdc.h | 15 ++++--------- emul/z80/sms.c | 7 ++++-- recipes/rc2014/blk/619 | 4 +++- 9 files changed, 95 insertions(+), 66 deletions(-) create mode 100644 emul/z80/rc2014_spi.c create mode 100644 emul/z80/rc2014_spi.h diff --git a/emul/z80/Makefile b/emul/z80/Makefile index bff3a11..070bc58 100644 --- a/emul/z80/Makefile +++ b/emul/z80/Makefile @@ -1,6 +1,6 @@ TARGETS = forth rc2014 sms ti84 OBJS = emul.o z80.o -RC2014_OBJS = $(OBJS) sio.o acia.o sdc.o +RC2014_OBJS = $(OBJS) sio.o acia.o sdc.o rc2014_spi.o SMS_OBJS = $(OBJS) sms_vdp.o sms_ports.o sms_pad.o ps2_kbd.o sdc.o TI84_OBJS = $(OBJS) t6a04.o ti84_kbd.o CDIR = ../../cvm diff --git a/emul/z80/emul.h b/emul/z80/emul.h index 9941833..2381757 100644 --- a/emul/z80/emul.h +++ b/emul/z80/emul.h @@ -5,6 +5,7 @@ typedef byte (*IORD) (); typedef void (*IOWR) (byte data); +typedef byte (*EXCH) (byte data); typedef struct { Z80Context cpu; diff --git a/emul/z80/rc2014.c b/emul/z80/rc2014.c index eb89a96..e687123 100644 --- a/emul/z80/rc2014.c +++ b/emul/z80/rc2014.c @@ -15,6 +15,7 @@ #include "acia.h" #include "sio.h" #include "sdc.h" +#include "rc2014_spi.h" #define RAMSTART 0x8000 #define ACIA_CTL_PORT 0x80 @@ -29,6 +30,7 @@ bool use_sio = false; static ACIA acia; static SIO sio; static SDC sdc; +static SPI spi; static uint8_t iord_acia_ctl() { @@ -70,26 +72,28 @@ static void iowr_sio_data(uint8_t val) sio_adata_wr(&sio, val); } -static uint8_t iord_sdc_spi() +static uint8_t iord_spi() { - return sdc_spi_rd(&sdc); + return spi_rd(&spi); } -static void iowr_sdc_spi(uint8_t val) +static void iowr_spi(uint8_t val) { - sdc_spi_wr(&sdc, val); + spi_wr(&spi, val); } +byte spix_sdc(byte val) { return sdc_spix(&sdc, val); } + // in emulation, exchanges are always instantaneous, so we // always report as ready. -static uint8_t iord_sdc_ctl() +static uint8_t iord_spi_ctl() { return 0; } -static void iowr_sdc_ctl(uint8_t val) +static void iowr_spi_ctl(uint8_t val) { - sdc_ctl_wr(&sdc, val); + spi_ctl_wr(&spi, val); } static bool has_irq() @@ -134,6 +138,7 @@ int main(int argc, char *argv[]) acia_init(&acia); sio_init(&sio); sdc_init(&sdc); + spi_init(&spi, spix_sdc); while ((ch = getopt(argc, argv, "sc:")) != -1) { switch (ch) { @@ -198,10 +203,10 @@ int main(int argc, char *argv[]) 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->iord[SDC_CTL] = iord_sdc_ctl; - m->iowr[SDC_CTL] = iowr_sdc_ctl; + m->iord[SDC_SPI] = iord_spi; + m->iowr[SDC_SPI] = iowr_spi; + m->iord[SDC_CTL] = iord_spi_ctl; + m->iowr[SDC_CTL] = iowr_spi_ctl; char tosend = 0; while (emul_step()) { diff --git a/emul/z80/rc2014_spi.c b/emul/z80/rc2014_spi.c new file mode 100644 index 0000000..94115c9 --- /dev/null +++ b/emul/z80/rc2014_spi.c @@ -0,0 +1,28 @@ +#include "rc2014_spi.h" + +void spi_init(SPI *spi, EXCH spixfn) +{ + spi->selected = false; + spi->resp = 0xff; + spi->spixfn = spixfn; +} + +// TODO: for now, any nonzero value enables the SPI. To allow +// emulation of systems with multi-devices SPI relay, change +// this. +void spi_ctl_wr(SPI *spi, byte val) +{ + spi->selected = val; +} + +void spi_wr(SPI *spi, byte val) +{ + if (spi->selected) { + spi->resp = spi->spixfn(val); + } +} + +byte spi_rd(SPI *spi) +{ + return spi->selected ? spi->resp : 0xff; +} diff --git a/emul/z80/rc2014_spi.h b/emul/z80/rc2014_spi.h new file mode 100644 index 0000000..66193fb --- /dev/null +++ b/emul/z80/rc2014_spi.h @@ -0,0 +1,17 @@ +#include "emul.h" + +/* Emulates a SPI relay designed for the RC2014, enabled by poking on the CTL + port, then allowing a SPI exchange by writing to, then reading from, the + data port. +*/ + +typedef struct { + bool selected; + byte resp; + EXCH spixfn; +} SPI; + +void spi_init(SPI *spi, EXCH spixfn); +void spi_ctl_wr(SPI *spi, byte val); +void spi_wr(SPI *spi, byte val); +byte spi_rd(SPI *spi); diff --git a/emul/z80/sdc.c b/emul/z80/sdc.c index 6339e0b..3d7c269 100644 --- a/emul/z80/sdc.c +++ b/emul/z80/sdc.c @@ -13,47 +13,34 @@ static uint16_t crc16(uint16_t crc, uint8_t data) void sdc_init(SDC *sdc) { - sdc->selected = false; sdc->initstat = 0; sdc->recvidx = 0; sdc->sendidx = -1; - sdc->resp = 0xff; sdc->fp = NULL; sdc->cmd17bytes = -1; sdc->cmd24bytes = -2; } -// TODO: for now, any nonzero value enables the SDC. To allow -// emulation of systems with multi-devices SPI relay, change -// this. -void sdc_ctl_wr(SDC *sdc, uint8_t val) +byte sdc_spix(SDC *sdc, byte val) { - sdc->selected = val; -} - -void sdc_spi_wr(SDC *sdc, uint8_t val) -{ - if (!sdc->selected) { - return; - } - sdc->resp = 0xff; + byte resp = 0xff; if (sdc->initstat < 8) { // not woken yet. sdc->initstat++; - return; + return resp; } if (sdc->sendidx >= 0) { - sdc->resp = sdc->sendbuf[sdc->sendidx++]; + resp = sdc->sendbuf[sdc->sendidx++]; if (sdc->sendidx == 5) { sdc->sendidx = -1; } - return; + return resp; } if (sdc->cmd17bytes >= 0) { if (sdc->fp) { - sdc->resp = getc(sdc->fp); + resp = getc(sdc->fp); } - sdc->crc16 = crc16(sdc->crc16, sdc->resp); + sdc->crc16 = crc16(sdc->crc16, resp); sdc->cmd17bytes++; if (sdc->cmd17bytes == 512) { sdc->sendbuf[3] = sdc->crc16 >> 8; @@ -61,12 +48,12 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) sdc->sendidx = 3; sdc->cmd17bytes = -1; } - return; + return resp; } if (sdc->cmd24bytes == -1) { if (val == 0xff) { // it's ok to receive idle bytes before the data token. - return; + return resp; } if (val == 0xfe) { // data token, good @@ -75,7 +62,7 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) // something is wrong, cancel cmd24 sdc->cmd24bytes = -2; } - return; + return resp; } if (sdc->cmd24bytes >= 0) { if (sdc->cmd24bytes < 512) { @@ -102,16 +89,16 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) sdc->cmd24bytes = -3; } sdc->cmd24bytes++; - return; + return resp; } if ((sdc->recvidx == 0) && ((val > 0x7f) || (val < 0x40))) { // not a command - return; + return resp; } sdc->recvbuf[sdc->recvidx++] = val; if (sdc->recvidx < 6) { // incomplete command - return; + return resp; } // Command complete val &= 0x3f; @@ -128,7 +115,7 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) sdc->sendbuf[4] = 0x01; sdc->sendidx = 4; } - return; + return resp; } if (sdc->initstat == 9) { // At this stage, we're expecting CMD8 with 0x1aa arg2 @@ -143,7 +130,7 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) } else { sdc-> initstat = 8; } - return; + return resp; } if (sdc->initstat == 10) { // At this stage, we're expecting CMD55 @@ -154,7 +141,7 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) } else { sdc->initstat = 8; } - return; + return resp; } if (sdc->initstat == 11) { // At this stage, we're expecting CMD41 @@ -165,7 +152,7 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) } else { sdc->initstat = 8; } - return; + return resp; } // We have a fully initialized card. if (cmd == 17) { @@ -178,7 +165,7 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) sdc->sendidx = 3; sdc->cmd17bytes = 0; sdc->crc16 = 0; - return; + return resp; } if (cmd == 24) { if (sdc->fp) { @@ -188,17 +175,10 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) sdc->sendidx = 4; sdc->cmd24bytes = -1; sdc->crc16 = 0; - return; + return resp; } // 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; + return resp; } diff --git a/emul/z80/sdc.h b/emul/z80/sdc.h index 7298ad2..28e3050 100644 --- a/emul/z80/sdc.h +++ b/emul/z80/sdc.h @@ -1,24 +1,19 @@ -#include -#include +#include "emul.h" 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]; + byte 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]; + byte 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; // File used for contents read/write FILE *fp; // number of bytes read into the current CMD17. -1 means no CMD17 active. @@ -31,6 +26,4 @@ typedef struct { } SDC; void sdc_init(SDC *sdc); -void sdc_ctl_wr(SDC *sdc, uint8_t val); -void sdc_spi_wr(SDC *sdc, uint8_t val); -uint8_t sdc_spi_rd(SDC *sdc); +byte sdc_spix(SDC *sdc, byte val); diff --git a/emul/z80/sms.c b/emul/z80/sms.c index 055d3fc..c118b83 100644 --- a/emul/z80/sms.c +++ b/emul/z80/sms.c @@ -90,7 +90,8 @@ static void iowr_ports_ctl(uint8_t val) ports_ctl_wr(&ports, val); } -static uint8_t iord_sdc_spi() +// TODO: re-add as controller-based SPI +/* static uint8_t iord_sdc_spi() { return sdc_spi_rd(&sdc); } @@ -110,7 +111,7 @@ static uint8_t iord_sdc_ctl() static void iowr_sdc_ctl(uint8_t val) { sdc_ctl_wr(&sdc, val); -} +}*/ void create_window() { @@ -356,10 +357,12 @@ int main(int argc, char *argv[]) m->iowr[VDP_CMD_PORT] = iowr_vdp_cmd; m->iowr[VDP_DATA_PORT] = iowr_vdp_data; m->iowr[PORTS_CTL_PORT] = iowr_ports_ctl; + /* TODO: re-add m->iord[SDC_SPI] = iord_sdc_spi; m->iowr[SDC_SPI] = iowr_sdc_spi; m->iord[SDC_CTL] = iord_sdc_ctl; m->iowr[SDC_CTL] = iowr_sdc_ctl; + */ conn = xcb_connect(NULL, NULL); screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; diff --git a/recipes/rc2014/blk/619 b/recipes/rc2014/blk/619 index 0ead75a..50e3c68 100644 --- a/recipes/rc2014/blk/619 +++ b/recipes/rc2014/blk/619 @@ -1,13 +1,15 @@ 0xff00 CONSTANT RS_ADDR 0xfffa CONSTANT PS_ADDR RS_ADDR 0x80 - CONSTANT SYSVARS 0x8000 CONSTANT HERESTART +4 CONSTANT SPI_DATA 5 CONSTANT SPI_CTL 1 CONSTANT SDC_DEVID 602 LOAD ( acia decl ) 5 LOAD ( z80 assembler ) 262 LOAD ( xcomp ) 282 LOAD ( boot.z80.decl ) 270 LOAD ( xcomp overrides ) 283 335 LOADR ( boot.z80 ) 353 LOAD ( xcomp core low ) 603 605 LOADR ( acia ) +419 LOAD 423 436 LOADR 390 LOAD ( xcomp core high ) (entry) _ ( Update LATEST ) PC ORG @ 8 + ! -," ACIA$ " EOT, +," ACIA$ BLK$ " EOT,