diff --git a/AltairZ80/CMakeLists.txt b/AltairZ80/CMakeLists.txt index 0e5f8591e..b1c1f8183 100644 --- a/AltairZ80/CMakeLists.txt +++ b/AltairZ80/CMakeLists.txt @@ -18,6 +18,7 @@ add_simulator(altairz80 SOURCES altairz80_cpu.c altairz80_cpu_nommu.c + s100_tuart.c s100_jair.c sol20.c s100_vdm1.c diff --git a/AltairZ80/altairz80_sys.c b/AltairZ80/altairz80_sys.c index 473a0ce10..3bba6c048 100644 --- a/AltairZ80/altairz80_sys.c +++ b/AltairZ80/altairz80_sys.c @@ -92,6 +92,9 @@ extern DEVICE sol20s_dev; extern DEVICE sol20p_dev; extern DEVICE vdm1_dev; +extern DEVICE tuart0_dev; +extern DEVICE tuart1_dev; +extern DEVICE tuart2_dev; extern DEVICE cromfdc_dev; extern DEVICE wd179x_dev; extern DEVICE n8vem_dev; @@ -134,7 +137,7 @@ DEVICE *sim_devices[] = { /* Compupro Devices */ &disk1a_dev, &disk2_dev, &disk3_dev, &ss1_dev, &mdriveh_dev, &selchan_dev, &if3_dev, /* Cromemco Devices */ - &cromfdc_dev, + &cromfdc_dev, &tuart0_dev, &tuart1_dev, &tuart2_dev, /* Integrated Business Computers (IBC) Devices */ &ibc_dev, &ibctimer_device, diff --git a/AltairZ80/s100_tuart.c b/AltairZ80/s100_tuart.c new file mode 100644 index 000000000..6d8084b54 --- /dev/null +++ b/AltairZ80/s100_tuart.c @@ -0,0 +1,755 @@ +/* s100_tuart.c: Cromemco TU-ART + + Copyright (c) 2024, Patrick Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + +*/ + +#include "altairz80_defs.h" +#include "sim_tmxr.h" + +#define TUART_NAME "CROMEMCO TU-ART" +#define TUART0_SNAME "TUART0" +#define TUART1_SNAME "TUART1" +#define TUART2_SNAME "TUART2" + +#define TUART_WAIT 1000 /* Service Wait Interval */ + +#define TUART0_IOBASE 0x00 +#define TUART0_IOSIZE 4 +#define TUART1_IOBASE 0x20 +#define TUART1_IOSIZE 4 +#define TUART2_IOBASE 0x50 +#define TUART2_IOSIZE 4 + +/* Status Register */ +#define TUART_FME 0x01 /* Framing Error */ +#define TUART_ORE 0x02 /* Overrun */ +#define TUART_IPG 0x20 /* Interrupt Pending */ +#define TUART_RDA 0x40 /* Receive Data Available */ +#define TUART_TBE 0x80 /* Transmit Buffer Empty */ + +/* Command Register */ +#define TUART_RESET 0x01 /* Reset */ +#define TUART_INTA 0x08 /* Interrupt Enable */ +#define TUART_HBD 0x10 /* High baud rate */ + +#define TUART_110 0x01 +#define TUART_150 0x02 +#define TUART_300 0x04 +#define TUART_1200 0x08 +#define TUART_2400 0x10 +#define TUART_4800 0x20 +#define TUART_9600 0x40 +#define TUART_1STOP 0x80 + +#define TUART_BAUD(xptr) ((xptr->baud * xptr->hbd > 76800) ? 76800 : xptr->baud * xptr->hbd) + +#define TUART_RDAIE 0x10 +#define TUART_TBEIE 0x20 + +#define TUART_RDAIA 0xe7 +#define TUART_TBSIA 0xef + +/* Debug flags */ +#define STATUS_MSG (1 << 0) +#define IRQ_MSG (1 << 1) +#define ERROR_MSG (1 << 2) +#define VERBOSE_MSG (1 << 3) + +/* IO Read/Write */ +#define IO_RD 0x00 /* IO Read */ +#define IO_WR 0x01 /* IO Write */ + +typedef struct { + PNP_INFO pnp; /* Must be first */ + t_bool conn; /* Connected Status */ + TMLN *tmln; /* TMLN pointer */ + TMXR *tmxr; /* TMXR pointer */ + int32 baud; /* Baud rate */ + uint8 hbd; /* High baud mult */ + uint8 sbits; /* Stop bits */ + uint8 rxb; /* Receive Buffer */ + uint8 txb; /* Transmit Buffer */ + t_bool txp; /* Transmit Pending */ + uint8 stb; /* Status Buffer */ + t_bool inta; /* Interrupt Ack Ena */ + uint8 intmask; /* Int Enable Mask */ + uint8 intadr; /* Interrupt Address */ + uint8 intvector; /* Interrupt Vector */ +} TUART_CTX; + +extern t_stat set_iobase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), const char* name, uint8 unmap); + + +static const char* tuart_description(DEVICE *dptr); +static t_stat tuart_svc(UNIT *uptr); +static t_stat tuart_reset(DEVICE *dptr, int32 (*routine)(const int32, const int32, const int32)); +static t_stat tuart0_reset(DEVICE *dptr); +static t_stat tuart1_reset(DEVICE *dptr); +static t_stat tuart2_reset(DEVICE *dptr); +static t_stat tuart_attach(UNIT *uptr, CONST char *cptr); +static t_stat tuart_detach(UNIT *uptr); +static t_stat tuart_set_baud(UNIT *uptr, int32 value, const char *cptr, void *desc); +static t_stat tuart_show_baud(FILE *st, UNIT *uptr, int32 value, const void *desc); +static t_stat tuart_config_line(UNIT *uptr); +static int32 tuart0_io(int32 addr, int32 io, int32 data); +static int32 tuart1_io(int32 addr, int32 io, int32 data); +static int32 tuart2_io(int32 addr, int32 io, int32 data); +static int32 tuart_io(DEVICE *dptr, int32 addr, int32 io, int32 data); +static int32 tuart_stat(DEVICE *dptr, int32 io, int32 data); +static int32 tuart_data(DEVICE *dptr, int32 io, int32 data); +static int32 tuart_command(DEVICE *dptr, int32 io, int32 data); +static int32 tuart_intadrmsk(DEVICE *dptr, int32 io, int32 data); +static void tuart_int(UNIT *uptr); + +extern uint32 vectorInterrupt; /* Vector Interrupt bits */ +extern uint8 dataBus[MAX_INT_VECTORS]; /* Data bus value */ + +/* Debug Flags */ +static DEBTAB tuart_dt[] = { + { "STATUS", STATUS_MSG, "Status messages" }, + { "IRQ", IRQ_MSG, "Interrupt messages" }, + { "ERROR", ERROR_MSG, "Error messages" }, + { "VERBOSE", VERBOSE_MSG, "Verbose messages" }, + { NULL, 0 } +}; + +/* Terminal multiplexer library descriptors */ + +static TMLN tuart0_tmln[] = { /* line descriptors */ + { 0 } +}; + +static TMLN tuart1_tmln[] = { /* line descriptors */ + { 0 } +}; + +static TMLN tuart2_tmln[] = { /* line descriptors */ + { 0 } +}; + +static TMXR tuart0_tmxr = { /* multiplexer descriptor */ + 1, /* number of terminal lines */ + 0, /* listening port (reserved) */ + 0, /* master socket (reserved) */ + tuart0_tmln, /* line descriptor array */ + NULL, /* line connection order */ + NULL /* multiplexer device (derived internally) */ +}; + +static TMXR tuart1_tmxr = { /* multiplexer descriptor */ + 1, /* number of terminal lines */ + 0, /* listening port (reserved) */ + 0, /* master socket (reserved) */ + tuart1_tmln, /* line descriptor array */ + NULL, /* line connection order */ + NULL /* multiplexer device (derived internally) */ +}; + +static TMXR tuart2_tmxr = { /* multiplexer descriptor */ + 1, /* number of terminal lines */ + 0, /* listening port (reserved) */ + 0, /* master socket (reserved) */ + tuart2_tmln, /* line descriptor array */ + NULL, /* line connection order */ + NULL /* multiplexer device (derived internally) */ +}; + +#define UNIT_V_TUART_CONSOLE (UNIT_V_UF + 0) /* Port checks console for input */ +#define UNIT_TUART_CONSOLE (1 << UNIT_V_TUART_CONSOLE) +#define UNIT_V_TUART_EVEN (UNIT_V_UF + 1) /* Mode 2 interrupts even mode */ +#define UNIT_TUART_EVEN (1 << UNIT_V_TUART_EVEN) + +static MTAB tuart_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "IOBASE", "IOBASE", + &set_iobase, &show_iobase, NULL, "Sets TU-ART base I/O address" }, + { UNIT_TUART_CONSOLE, UNIT_TUART_CONSOLE, "CONSOLE", "CONSOLE", NULL, NULL, NULL, + "Port checks for console input" }, + { UNIT_TUART_CONSOLE, 0, "NOCONSOLE", "NOCONSOLE", NULL, NULL, NULL, + "Port does not check for console input" }, + { UNIT_TUART_EVEN, UNIT_TUART_EVEN, "EVEN", "EVEN", NULL, NULL, NULL, + "Mode 2 interrupt even mode" }, + { UNIT_TUART_EVEN, 0, "ODD", "ODD", NULL, NULL, NULL, + "Mode 2 interrupt odd mode" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "BAUD", "BAUD", &tuart_set_baud, &tuart_show_baud, + NULL, "Set baud rate (default=9600)" }, + { 0 } +}; + +static TUART_CTX tuart0_ctx = {{0, 0, TUART0_IOBASE, TUART0_IOSIZE}, 0, tuart0_tmln, &tuart0_tmxr}; +static TUART_CTX tuart1_ctx = {{0, 0, TUART1_IOBASE, TUART1_IOSIZE}, 0, tuart1_tmln, &tuart1_tmxr}; +static TUART_CTX tuart2_ctx = {{0, 0, TUART2_IOBASE, TUART2_IOSIZE}, 0, tuart2_tmln, &tuart2_tmxr}; + +static UNIT tuart0_unit[] = { + { UDATA (&tuart_svc, UNIT_ATTABLE | UNIT_DISABLE | UNIT_TUART_CONSOLE, 0), TUART_WAIT }, +}; +static UNIT tuart1_unit[] = { + { UDATA (&tuart_svc, UNIT_ATTABLE | UNIT_DISABLE, 0), TUART_WAIT }, +}; +static UNIT tuart2_unit[] = { + { UDATA (&tuart_svc, UNIT_ATTABLE | UNIT_DISABLE, 0), TUART_WAIT }, +}; + +static REG tuart0_reg[] = { + { HRDATAD (INTMASK0, tuart0_ctx.intmask, 8, "TU-ART port 0 interrupt mask"), }, + { DRDATAD (INTVEC0, tuart0_ctx.intvector, 8, "TU-ART port 0 interrupt vector"), }, + { HRDATAD (INTADR0, tuart0_ctx.intadr, 8, "TU-ART port 0 interrupt address"), }, + { NULL } +}; +static REG tuart1_reg[] = { + { HRDATAD (INTMASK1, tuart1_ctx.intmask, 8, "TU-ART port 1/A interrupt mask"), }, + { DRDATAD (INTVEC1, tuart1_ctx.intvector, 8, "TU-ART port 1/A interrupt vector"), }, + { HRDATAD (INTADR1, tuart1_ctx.intadr, 8, "TU-ART port 1/A interrupt address"), }, + { NULL } +}; +static REG tuart2_reg[] = { + { HRDATAD (INTMASK2, tuart2_ctx.intmask, 8, "TU-ART port 2/B interrupt mask"), }, + { DRDATAD (INTVEC2, tuart2_ctx.intvector, 8, "TU-ART port 2/B interrupt vector"), }, + { HRDATAD (INTADR2, tuart2_ctx.intadr, 8, "TU-ART port 2/B interrupt address"), }, + { NULL } +}; + +DEVICE tuart0_dev = { + TUART0_SNAME, /* name */ + tuart0_unit, /* unit */ + tuart0_reg, /* registers */ + tuart_mod, /* modifiers */ + 1, /* # units */ + 10, /* address radix */ + 31, /* address width */ + 1, /* address increment */ + 8, /* data radix */ + 8, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &tuart0_reset, /* reset routine */ + NULL, /* boot routine */ + &tuart_attach, /* attach routine */ + &tuart_detach, /* detach routine */ + &tuart0_ctx, /* context */ + (DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX), /* flags */ + 0, /* debug control */ + tuart_dt, /* debug flags */ + NULL, /* mem size routine */ + NULL, /* logical name */ + NULL, /* help */ + NULL, /* attach help */ + NULL, /* context for help */ + &tuart_description /* description */ +}; + +DEVICE tuart1_dev = { + TUART1_SNAME, /* name */ + tuart1_unit, /* unit */ + tuart1_reg, /* registers */ + tuart_mod, /* modifiers */ + 1, /* # units */ + 10, /* address radix */ + 31, /* address width */ + 1, /* address increment */ + 8, /* data radix */ + 8, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &tuart1_reset, /* reset routine */ + NULL, /* boot routine */ + &tuart_attach, /* attach routine */ + &tuart_detach, /* detach routine */ + &tuart1_ctx, /* context */ + (DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX), /* flags */ + 0, /* debug control */ + tuart_dt, /* debug flags */ + NULL, /* mem size routine */ + NULL, /* logical name */ + NULL, /* help */ + NULL, /* attach help */ + NULL, /* context for help */ + &tuart_description /* description */ +}; + +DEVICE tuart2_dev = { + TUART2_SNAME, /* name */ + tuart2_unit, /* unit */ + tuart2_reg, /* registers */ + tuart_mod, /* modifiers */ + 1, /* # units */ + 10, /* address radix */ + 31, /* address width */ + 1, /* address increment */ + 8, /* data radix */ + 8, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &tuart2_reset, /* reset routine */ + NULL, /* boot routine */ + &tuart_attach, /* attach routine */ + &tuart_detach, /* detach routine */ + &tuart2_ctx, /* context */ + (DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX), /* flags */ + 0, /* debug control */ + tuart_dt, /* debug flags */ + NULL, /* mem size routine */ + NULL, /* logical name */ + NULL, /* help */ + NULL, /* attach help */ + NULL, /* context for help */ + &tuart_description /* description */ +}; + +static const char* tuart_description(DEVICE *dptr) +{ + return TUART_NAME; +} + +static t_stat tuart0_reset(DEVICE *dptr) +{ + return(tuart_reset(dptr, &tuart0_io)); +} + +static t_stat tuart1_reset(DEVICE *dptr) +{ + return(tuart_reset(dptr, &tuart1_io)); +} + +static t_stat tuart2_reset(DEVICE *dptr) +{ + return(tuart_reset(dptr, &tuart2_io)); +} + +static t_stat tuart_reset(DEVICE *dptr, int32 (*routine)(const int32, const int32, const int32)) +{ + TUART_CTX *xptr; + + xptr = (TUART_CTX *) dptr->ctxt; + + /* Connect/Disconnect I/O Ports at base address */ + if (sim_map_resource(xptr->pnp.io_base, xptr->pnp.io_size, RESOURCE_TYPE_IO, routine, dptr->name, dptr->flags & DEV_DIS) != 0) { + sim_debug(ERROR_MSG, dptr, "error mapping I/O resource at 0x%02x.\n", xptr->pnp.io_base); + return SCPE_ARG; + } + + /* Set DEVICE for this UNIT */ + dptr->units[0].dptr = dptr; + + /* Reset registers */ + xptr->stb = 0x00; + xptr->txp = FALSE; + xptr->hbd = 1; + xptr->baud = 9600; + xptr->sbits = 1; + + tuart_config_line(&dptr->units[0]); + + if (!(dptr->flags & DEV_DIS) && dptr->units[0].flags & UNIT_TUART_CONSOLE) { + sim_activate_after_abs(&dptr->units[0], dptr->units[0].wait); + } else { + sim_cancel(&dptr->units[0]); + } + + sim_debug(STATUS_MSG, dptr, "reset adapter.\n"); + + return SCPE_OK; +} + + +static t_stat tuart_svc(UNIT *uptr) +{ + TUART_CTX *xptr; + int32 c; + t_stat r; + + xptr = (TUART_CTX *) uptr->dptr->ctxt; + + /* Check for new incoming connection */ + if (uptr->flags & UNIT_ATT) { + if (tmxr_poll_conn(xptr->tmxr) >= 0) { /* poll connection */ + + xptr->conn = TRUE; /* set connected */ + + sim_debug(STATUS_MSG, uptr->dptr, "new connection.\n"); + } + } + + /* Update incoming modem status bits */ + if (uptr->flags & UNIT_ATT) { + xptr->stb = 0x00; + + /* Enable receiver if DCD is active low */ + xptr->tmln->rcve = 1; + } + + /* TX data */ + if (xptr->txp) { + if (uptr->flags & UNIT_ATT) { + r = tmxr_putc_ln(xptr->tmln, xptr->txb); + xptr->txp = FALSE; /* Reset TX Pending */ + } else { + r = sim_putchar(xptr->txb); + xptr->txp = FALSE; /* Reset TX Pending */ + } + + if (r == SCPE_LOST) { + xptr->conn = FALSE; /* Connection was lost */ + sim_debug(STATUS_MSG, uptr->dptr, "lost connection.\n"); + } + + /* If TX buffer now empty, send interrupt */ + if ((!xptr->txp) && (xptr->intmask & TUART_TBEIE)) { + xptr->intadr = TUART_TBSIA; + tuart_int(uptr); + } + + } + + /* Update TBE if not set and no character pending */ + if (!xptr->txp && !(xptr->stb & TUART_TBE)) { + if (uptr->flags & UNIT_ATT) { + tmxr_poll_tx(xptr->tmxr); + xptr->stb |= (tmxr_txdone_ln(xptr->tmln) && xptr->conn) ? TUART_TBE : 0; + } else { + xptr->stb |= TUART_TBE; + } + } + + /* Check for Data if RX buffer empty */ + if (!(xptr->stb & TUART_RDA)) { + if (uptr->flags & UNIT_ATT) { + tmxr_poll_rx(xptr->tmxr); + + c = tmxr_getc_ln(xptr->tmln); + } else if (uptr->flags & UNIT_TUART_CONSOLE) { + c = sim_poll_kbd(); + } else { + c = 0; + } + + if (c & (TMXR_VALID | SCPE_KFLAG)) { + xptr->rxb = c & 0xff; + xptr->stb |= TUART_RDA; + xptr->stb &= ~(TUART_FME | TUART_ORE); + if (xptr->intmask & TUART_RDAIE) { + xptr->intadr = TUART_RDAIA; + tuart_int(uptr); + } + } + } + + sim_activate_after_abs(uptr, uptr->wait); + + return SCPE_OK; +} + + +/* Attach routine */ +static t_stat tuart_attach(UNIT *uptr, CONST char *cptr) +{ + TUART_CTX *xptr; + t_stat r = SCPE_OK; + + xptr = (TUART_CTX *) uptr->dptr->ctxt; + + sim_debug(VERBOSE_MSG, uptr->dptr, "attach (%s).\n", cptr); + + if ((r = tmxr_attach(xptr->tmxr, uptr, cptr)) == SCPE_OK) { + xptr->tmln->rcve = 1; + + r = tuart_config_line(uptr); + + sim_activate_after_abs(uptr, uptr->wait); + } + + return r; +} + + +/* Detach routine */ +static t_stat tuart_detach(UNIT *uptr) +{ + TUART_CTX *xptr; + + if (uptr->dptr == NULL) { + return SCPE_IERR; + } + + sim_debug(VERBOSE_MSG, uptr->dptr, "detach.\n"); + + if (uptr->flags & UNIT_ATT) { + xptr = (TUART_CTX *) uptr->dptr->ctxt; + + if (uptr->flags & UNIT_TUART_CONSOLE) { + uptr->wait = TUART_WAIT; + } else { + sim_cancel(uptr); + } + + return (tmxr_detach(xptr->tmxr, uptr)); + } + + return SCPE_UNATT; +} + +static t_stat tuart_set_baud(UNIT *uptr, int32 value, const char *cptr, void *desc) +{ + TUART_CTX *xptr; + int32 baud; + t_stat r = SCPE_ARG; + + xptr = (TUART_CTX *) uptr->dptr->ctxt; + + if (cptr != NULL) { + if (sscanf(cptr, "%d", &baud)) { + switch (baud) { + case 110: + case 150: + case 300: + case 1200: + case 2400: + case 4800: + case 9600: + xptr->baud = baud; + xptr->hbd = 1; + r = tuart_config_line(uptr); + return r; + + case 19200: + case 38400: + case 76800: + xptr->baud = baud / 8; + xptr->hbd = 8; + r = tuart_config_line(uptr); + return r; + + default: + sim_printf("invalid baud rate\n"); + break; + } + } + } + + return r; +} + +static t_stat tuart_show_baud(FILE *st, UNIT *uptr, int32 value, const void *desc) +{ + TUART_CTX *xptr; + + xptr = (TUART_CTX *) uptr->dptr->ctxt; + + fprintf(st, "%d (wait=%d)", TUART_BAUD(xptr), uptr->wait); + + return SCPE_OK; +} + +static t_stat tuart_config_line(UNIT *uptr) +{ + TUART_CTX *xptr; + char config[20]; + t_stat r = SCPE_OK; + + xptr = (TUART_CTX *) uptr->dptr->ctxt; + + if (xptr != NULL) { + sprintf(config, "%d-8N%d", TUART_BAUD(xptr), xptr->sbits); + + if (uptr->flags & UNIT_ATT) { + r = tmxr_set_config_line(xptr->tmln, config); + + if (xptr->tmln->serport) { + uptr->wait = 9600000 / TUART_BAUD(xptr); + } else { + uptr->wait = TUART_WAIT; + } + + xptr->tmln->txbps = 0; /* Get TMXR's timing out of our way */ + xptr->tmln->rxbps = 0; /* Get TMXR's timing out of our way */ + } + + sim_debug(STATUS_MSG, uptr->dptr, "Port configuration set to '%s'.\n", config); + } + + return r; +} + +static int32 tuart0_io(int32 addr, int32 io, int32 data) +{ + return(tuart_io(&tuart0_dev, addr, io, data)); +} + +static int32 tuart1_io(int32 addr, int32 io, int32 data) +{ + return(tuart_io(&tuart1_dev, addr, io, data)); +} + +static int32 tuart2_io(int32 addr, int32 io, int32 data) +{ + return(tuart_io(&tuart2_dev, addr, io, data)); +} + +static int32 tuart_io(DEVICE *dptr, int32 addr, int32 io, int32 data) +{ + int32 r; + + if ((addr & 0x03) == 0x03) { + r = tuart_intadrmsk(dptr, io, data); + } + else if (addr & 0x02) { + r = tuart_command(dptr, io, data); + } + else if (addr & 0x01) { + r = tuart_data(dptr, io, data); + } else { + r = tuart_stat(dptr, io, data); + } + + return(r); +} + +static int32 tuart_stat(DEVICE *dptr, int32 io, int32 data) +{ + TUART_CTX *xptr; + int32 r = 0xff; + + xptr = (TUART_CTX *) dptr->ctxt; + + if (io == IO_RD) { + r = xptr->stb; + } else { + xptr->sbits = (data & TUART_1STOP) ? 1 : 2; + + switch (data & ~TUART_1STOP) { + case TUART_110: + xptr->baud = 110; + break; + + case TUART_150: + xptr->baud = 150; + break; + + case TUART_300: + xptr->baud = 300; + break; + + case TUART_1200: + xptr->baud = 1200; + break; + + case TUART_2400: + xptr->baud = 2400; + break; + + case TUART_4800: + xptr->baud = 4800; + break; + + case TUART_9600: + default: + xptr->baud = 9600; + break; + } + sim_debug(STATUS_MSG, dptr, "Status Port Write %02X (sbits=%d baud=%d)\n", data, xptr->sbits, xptr->baud); + tuart_config_line(&dptr->units[0]); + } + + return r; +} + +static int32 tuart_data(DEVICE *dptr, int32 io, int32 data) +{ + TUART_CTX *xptr; + int32 r = 0xff; + + xptr = (TUART_CTX *) dptr->ctxt; + + if (io == IO_RD) { + r = xptr->rxb; + xptr->stb &= ~(TUART_RDA | TUART_FME | TUART_ORE | TUART_IPG); + } else { + xptr->txb = data; + xptr->stb &= ~(TUART_TBE | TUART_IPG); + xptr->txp = TRUE; + } + + return r; +} + +static int32 tuart_command(DEVICE *dptr, int32 io, int32 data) +{ + TUART_CTX *xptr; + int32 r = 0xff; + + xptr = (TUART_CTX *) dptr->ctxt; + + if (io == IO_RD) { + } else { + if (data & TUART_RESET) { + xptr->stb &= ~(TUART_ORE); + sim_debug(STATUS_MSG, dptr, "Reset port\n"); + } + xptr->inta = (data & TUART_INTA) ? TRUE : FALSE; + xptr->hbd = (data & TUART_HBD) ? 8 : 1; + sim_debug(STATUS_MSG, dptr, "Command Port Write %02X (inta=%d hbd=%d)\n", data, xptr->inta, xptr->hbd); + tuart_config_line(&dptr->units[0]); + } + + return r; +} + +static int32 tuart_intadrmsk(DEVICE *dptr, int32 io, int32 data) +{ + TUART_CTX *xptr; + + xptr = (TUART_CTX *) dptr->ctxt; + + if (io == IO_RD) { + return xptr->intadr; + } else { + xptr->intmask = data; + } + + return 0xff; +} + +static void tuart_int(UNIT *uptr) +{ + TUART_CTX *xptr; + + xptr = (TUART_CTX *) uptr->dptr->ctxt; + + if (!xptr->inta) { + return; + } + + vectorInterrupt |= (1 << xptr->intvector); + dataBus[xptr->intvector] = xptr->intadr; + if (uptr->flags & UNIT_TUART_EVEN) { + dataBus[xptr->intvector] &= 0xfe; + } + xptr->stb |= TUART_IPG; + + sim_debug(IRQ_MSG, uptr->dptr, "Vector=%d Data bus=%02X\n", xptr->intvector, dataBus[xptr->intvector]); +} + diff --git a/Visual Studio Projects/AltairZ80.vcproj b/Visual Studio Projects/AltairZ80.vcproj index 89ff08f46..085cfbe5a 100644 --- a/Visual Studio Projects/AltairZ80.vcproj +++ b/Visual Studio Projects/AltairZ80.vcproj @@ -406,6 +406,10 @@ RelativePath="..\AltairZ80\s100_tdd.c" > + + diff --git a/Visual Studio Projects/AltairZ80.vcxproj b/Visual Studio Projects/AltairZ80.vcxproj index 6635c661f..fe45d4537 100755 --- a/Visual Studio Projects/AltairZ80.vcxproj +++ b/Visual Studio Projects/AltairZ80.vcxproj @@ -291,6 +291,7 @@ + diff --git a/Visual Studio Projects/AltairZ80.vcxproj.filters b/Visual Studio Projects/AltairZ80.vcxproj.filters index a10506adb..da4ec5608 100755 --- a/Visual Studio Projects/AltairZ80.vcxproj.filters +++ b/Visual Studio Projects/AltairZ80.vcxproj.filters @@ -162,6 +162,9 @@ Source Files + + Source Files + Source Files @@ -309,4 +312,4 @@ Header Files - \ No newline at end of file + diff --git a/makefile b/makefile index 1167f83aa..c7b3aad85 100644 --- a/makefile +++ b/makefile @@ -1894,6 +1894,7 @@ ALTAIR_OPT = -I ${ALTAIRD} ALTAIRZ80D = ${SIMHD}/AltairZ80 ALTAIRZ80 = ${ALTAIRZ80D}/altairz80_cpu.c ${ALTAIRZ80D}/altairz80_cpu_nommu.c \ + ${ALTAIRZ80D}/s100_tuart.c \ ${ALTAIRZ80D}/s100_jair.c \ ${ALTAIRZ80D}/sol20.c \ ${ALTAIRZ80D}/s100_vdm1.c \