diff --git a/emul/hw/rc2014/Makefile b/emul/hw/rc2014/Makefile index 83aa0d8..dc75d2e 100644 --- a/emul/hw/rc2014/Makefile +++ b/emul/hw/rc2014/Makefile @@ -1,5 +1,5 @@ EXTOBJS = ../../emul.o ../../libz80/libz80.o -OBJS = acia.o sdc.o classic.o +OBJS = sio.o acia.o sdc.o classic.o TARGET = classic .PHONY: all diff --git a/emul/hw/rc2014/README.md b/emul/hw/rc2014/README.md index d4ac64b..ce31704 100644 --- a/emul/hw/rc2014/README.md +++ b/emul/hw/rc2014/README.md @@ -10,6 +10,9 @@ Run `make` to build. Run `./classic /path/to/rom` (for example, `os.bin` from RC2014's recipe). Serial I/O is hooked to stdin/stdout. `CTRL+D` to quit. +There are 2 options. `-s` replaces the ACIA with a Zilog SIO and +`-c/path/to/image` hooks up a SD card with specified contents. + ## Memory dump You can press `CTRL+E` to dump the whole 64K of memory into `memdump`. diff --git a/emul/hw/rc2014/classic.c b/emul/hw/rc2014/classic.c index c363d60..3f20de2 100644 --- a/emul/hw/rc2014/classic.c +++ b/emul/hw/rc2014/classic.c @@ -13,16 +13,21 @@ #include #include "../../emul.h" #include "acia.h" +#include "sio.h" #include "sdc.h" #define RAMSTART 0x8000 #define ACIA_CTL_PORT 0x80 #define ACIA_DATA_PORT 0x81 +#define SIO_ACTL_PORT 0x80 +#define SIO_ADATA_PORT 0x81 #define SDC_CTL 0x05 #define SDC_SPI 0x04 #define MAX_ROMSIZE 0x2000 +bool use_sio = false; static ACIA acia; +static SIO sio; static SDC sdc; static uint8_t iord_acia_ctl() @@ -45,6 +50,26 @@ static void iowr_acia_data(uint8_t val) acia_data_wr(&acia, val); } +static uint8_t iord_sio_ctl() +{ + return sio_actl_rd(&sio); +} + +static uint8_t iord_sio_data() +{ + return sio_adata_rd(&sio); +} + +static void iowr_sio_ctl(uint8_t val) +{ + sio_actl_wr(&sio, val); +} + +static void iowr_sio_data(uint8_t val) +{ + sio_adata_wr(&sio, val); +} + static uint8_t iord_sdc_spi() { return sdc_spi_rd(&sdc); @@ -67,13 +92,65 @@ static void iowr_sdc_ctl(uint8_t val) sdc_ctl_wr(&sdc, val); } +static bool has_irq() +{ + return use_sio ? sio_has_irq(&sio) : acia_has_irq(&acia); +} + +static bool hastx() +{ + return use_sio ? sio_hastx(&sio) : acia_hastx(&acia); +} + +static bool hasrx() +{ + return use_sio ? sio_hasrx(&sio) : acia_hasrx(&acia); +} + +static uint8_t _read() +{ + return use_sio ? sio_read(&sio) : acia_read(&acia); +} + +static void _write(uint8_t val) +{ + if (use_sio) { sio_write(&sio, val); } else { acia_write(&acia, val); } +} + +static void usage() +{ + fprintf(stderr, "Usage: ./classic [-s] [-c sdcard.img] /path/to/rom\n"); +} + int main(int argc, char *argv[]) { + FILE *fp = NULL; + int ch; + if (argc < 2) { - fprintf(stderr, "Usage: ./classic /path/to/rom [sdcard.img]\n"); + usage(); return 1; } - FILE *fp = fopen(argv[1], "r"); + acia_init(&acia); + sio_init(&sio); + sdc_init(&sdc); + + while ((ch = getopt(argc, argv, "sc:")) != -1) { + switch (ch) { + case 's': + use_sio = true; + break; + case 'c': + fprintf(stderr, "Setting up SD card image with %s\n", optarg); + sdc.fp = fopen(optarg, "r+"); + break; + } + } + if (optind != argc-1) { + usage(); + return 1; + } + fp = fopen(argv[optind], "r"); if (fp == NULL) { fprintf(stderr, "Can't open %s\n", argv[1]); return 1; @@ -106,16 +183,17 @@ int main(int argc, char *argv[]) tcsetattr(0, TCSADRAIN, &term); } - acia_init(&acia); - sdc_init(&sdc); - if (argc == 3) { - fprintf(stderr, "Setting up SD card image\n"); - sdc.fp = fopen(argv[2], "r+"); + if (use_sio) { + m->iord[SIO_ACTL_PORT] = iord_sio_ctl; + m->iord[SIO_ADATA_PORT] = iord_sio_data; + m->iowr[SIO_ACTL_PORT] = iowr_sio_ctl; + m->iowr[SIO_ADATA_PORT] = iowr_sio_data; + } else { + 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[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->iord[SDC_CTL] = iord_sdc_ctl; @@ -124,12 +202,12 @@ int main(int argc, char *argv[]) char tosend = 0; while (emul_step()) { // Do we have an interrupt? - if (acia_has_irq(&acia)) { + if (has_irq()) { Z80INT(&m->cpu, 0); } // Is the RC2014 transmitting? - if (acia_hastx(&acia)) { - putchar(acia_read(&acia)); + if (hastx()) { + putchar(_read()); fflush(stdout); } // Do we have something to send? @@ -150,8 +228,8 @@ int main(int argc, char *argv[]) break; } } - if (tosend && !acia_hasrx(&acia)) { - acia_write(&acia, tosend); + if (tosend && !hasrx()) { + _write(tosend); tosend = 0; } } diff --git a/emul/hw/rc2014/sio.c b/emul/hw/rc2014/sio.c new file mode 100644 index 0000000..e6b66ab --- /dev/null +++ b/emul/hw/rc2014/sio.c @@ -0,0 +1,70 @@ +#include +#include +#include "sio.h" + +void sio_init(SIO *sio) +{ + memset(sio->wr, 0, sizeof(sio->wr)); + memset(sio->rr, 0, sizeof(sio->rr)); + sio->rx = 0; + sio->tx = 0; + sio->in_int = false; +} + +bool sio_has_irq(SIO *sio) +{ + bool res = sio->in_int; + sio->in_int = false; + return res; +} + +bool sio_hasrx(SIO *sio) +{ + return sio->rr[0] & 0x01; // Receive Character Available +} + +bool sio_hastx(SIO *sio) +{ + return !(sio->rr[0] & 0x04); // Transmit Buffer Empty +} + +uint8_t sio_read(SIO *sio) +{ + sio->rr[0] |= 0x04; // Transmit Buffer Empty high + return sio->tx; +} + +void sio_write(SIO *sio, uint8_t val) +{ + sio->rr[0] |= 0x01; // Receive Character Available high + sio->rx = val; + sio->in_int = true; +} + +uint8_t sio_actl_rd(SIO *sio) +{ + uint8_t target = sio->wr[0] & 0x3; // PTR + return sio->rr[target]; +} + +void sio_actl_wr(SIO *sio, uint8_t val) +{ + uint8_t target = sio->wr[0] & 0x7; // PTR + sio->wr[target] = val; + if (target != 0) { + sio->wr[0] &= ~0x7; + } +} + +uint8_t sio_adata_rd(SIO *sio) +{ + sio->rr[0] &= ~0x01; // Receive Character Available low + return sio->rx; +} + +void sio_adata_wr(SIO *sio, uint8_t val) +{ + sio->tx = val; + sio->rr[0] &= ~0x04; // Transmit Buffer Empty low +} + diff --git a/emul/hw/rc2014/sio.h b/emul/hw/rc2014/sio.h new file mode 100644 index 0000000..cd9f04f --- /dev/null +++ b/emul/hw/rc2014/sio.h @@ -0,0 +1,27 @@ +#include +#include + +/* Extremely simplified Zilog SIO */ + +typedef struct { + // Write registers WR7:0 + uint8_t wr[7]; + // Read registers RR2:0 + uint8_t rr[2]; + uint8_t rx; + uint8_t tx; + // Set to true when writing a byte while SIO is enabled and set back to + // false when sio_has_irq() is false. + bool in_int; +} SIO; + +void sio_init(SIO *sio); +bool sio_has_irq(SIO *sio); +bool sio_hasrx(SIO *sio); +bool sio_hastx(SIO *sio); +uint8_t sio_read(SIO *sio); +void sio_write(SIO *sio, uint8_t val); +uint8_t sio_actl_rd(SIO *sio); +void sio_actl_wr(SIO *sio, uint8_t val); +uint8_t sio_adata_rd(SIO *sio); +void sio_adata_wr(SIO *sio, uint8_t val);