AltairZ80: M2SIO, CPU, SIO and PMMI additions and fixes

Adds 6850 DCD status latch to M2SIO devices.
Adds vector interrupt support to M2SIO devices.

Removes CTS inactive transmit disable from PMMI device.

Adds IMSAI-style programmed output to CPU/SIO devices.

SET CPU PO will display "PO: AREG" upon an "OUT 0FFH"
instruction.

SET CPU NOPO will disable the function (default).

Corrects problem with Mode 0 interrupts.

When the CPU receives an interrupt, it pushes the current
program counter on the stack. The current implementation
of Mode 0 was performing interrupt processing after fetching
the next opcode from RAM, which also increases the PC by 1.
This caused PC+1 to be pushed on the stack. The interrupt
processing is now done prior to fetching the next opcode,
preserving the correct program counter.
This commit is contained in:
Patrick Linstruth 2023-09-10 07:11:27 -04:00 committed by Paul Koning
parent 5407544728
commit 90e4597aa9
5 changed files with 100 additions and 60 deletions

View file

@ -547,6 +547,10 @@ static MTAB cpu_mod[] = {
NULL, "Sets CPU switcher port for 8080 / Z80 / 8086" },
{ UNIT_CPU_SWITCHER, 0, "NOSWITCHER", "NOSWITCHER", &cpu_reset_switcher, &cpu_show_switcher,
NULL, "Resets CPU switcher port for 8080 / Z80 / 8086" },
{ UNIT_CPU_PO, UNIT_CPU_PO, "PO", "PO", NULL, NULL,
NULL, "Enable programmed output messages" },
{ UNIT_CPU_PO, 0, "NOPO", "NOPO", NULL, NULL,
NULL, "Disable programmed output messages" },
{ MTAB_XTD | MTAB_VDV, 0, NULL, "AZ80", &cpu_set_ramtype,
NULL, NULL, "Sets the RAM type to AltairZ80 RAM for 8080 / Z80 / 8086" },
{ MTAB_XTD | MTAB_VDV, 1, NULL, "HRAM", &cpu_set_ramtype,
@ -2352,9 +2356,6 @@ static t_stat sim_instr_mmu (void) {
}
PCX = PC;
INCR(1);
op = RAM_PP(PC);
/* 8080 INT/Z80 Interrupt Mode 0
Instruction to execute (ex. RST0-7) is on the data bus
@ -2377,6 +2378,9 @@ static t_stat sim_instr_mmu (void) {
sim_debug(INT_MSG, &cpu_dev, ADDRESS_FORMAT
" INT(mode=0 vectorInterrupt=%X intVector=%d op=%02X)\n", PCX, vectorInterrupt, intVector, op);
} else {
INCR(1);
op = RAM_PP(PC);
}
switch(op) {

View file

@ -84,6 +84,8 @@ typedef enum {
#define UNIT_CPU_STOPONHALT (1 << UNIT_CPU_V_STOPONHALT)
#define UNIT_CPU_V_SWITCHER (UNIT_V_UF+6) /* switcher 8086 <--> 8080/Z80 enabled */
#define UNIT_CPU_SWITCHER (1 << UNIT_CPU_V_SWITCHER)
#define UNIT_CPU_V_PO (UNIT_V_UF+7) /* enable programmed output messages */
#define UNIT_CPU_PO (1 << UNIT_CPU_V_PO)
#define ADDRESS_FORMAT "[0x%08x]"

View file

@ -1168,7 +1168,17 @@ int32 nulldev(const int32 port, const int32 io, const int32 data) {
}
int32 sr_dev(const int32 port, const int32 io, const int32 data) {
return io == 0 ? SR : 0;
if (io == 0) {
return SR;
}
/* Simulate IMSAI functionality of displaying the A */
/* register on the Programmed Output front panel LEDs */
if (cpu_unit.flags & UNIT_CPU_PO) {
sim_printf("PO: %02X\n", data & 0xff);
}
return 0;
}
static int32 toBCD(const int32 x) {

View file

@ -133,8 +133,9 @@
/* Debug flags */
#define STATUS_MSG (1 << 0)
#define ERROR_MSG (1 << 1)
#define VERBOSE_MSG (1 << 2)
#define IRQ_MSG (1 << 1)
#define ERROR_MSG (1 << 2)
#define VERBOSE_MSG (1 << 3)
/* IO Read/Write */
#define IO_RD 0x00 /* IO Read */
@ -143,18 +144,19 @@
typedef struct {
PNP_INFO pnp; /* Must be first */
int32 port; /* Port 0 or 1 */
int32 conn; /* Connected Status */
t_bool conn; /* Connected Status */
TMLN *tmln; /* TMLN pointer */
TMXR *tmxr; /* TMXR pointer */
int32 baud; /* Baud rate */
int32 rts; /* RTS Status */
int32 rxb; /* Receive Buffer */
int32 txb; /* Transmit Buffer */
int32 txp; /* Transmit Pending */
t_bool txp; /* Transmit Pending */
int32 stb; /* Status Buffer */
int32 ctb; /* Control Buffer */
int32 rie; /* Rx Int Enable */
int32 tie; /* Tx Int Enable */
t_bool rie; /* Rx Int Enable */
t_bool tie; /* Tx Int Enable */
t_bool dcdl; /* DCD latch */
uint8 intenable; /* Interrupt Enable */
uint8 intvector; /* Interrupt Vector */
uint8 databus; /* Data Bus Value */
@ -183,6 +185,7 @@ static int32 m2sio1_io(int32 addr, int32 io, int32 data);
static int32 m2sio_io(DEVICE *dptr, int32 addr, int32 io, int32 data);
static int32 m2sio_stat(DEVICE *dptr, int32 io, int32 data);
static int32 m2sio_data(DEVICE *dptr, int32 io, int32 data);
static void m2sio_int(UNIT *uptr);
extern uint32 vectorInterrupt; /* Vector Interrupt bits */
extern uint8 dataBus[MAX_INT_VECTORS]; /* Data bus value */
@ -190,6 +193,7 @@ extern uint8 dataBus[MAX_INT_VECTORS]; /* Data bus value */
/* Debug Flags */
static DEBTAB m2sio_dt[] = {
{ "STATUS", STATUS_MSG, "Status messages" },
{ "IRQ", IRQ_MSG, "Interrupt messages" },
{ "ERROR", ERROR_MSG, "Error messages" },
{ "VERBOSE", VERBOSE_MSG, "Verbose messages" },
{ NULL, 0 }
@ -272,7 +276,7 @@ static REG m2sio0_reg[] = {
{ HRDATAD (M2CTL0, m2sio0_ctx.ctb, 8, "2SIO port 0 control register"), },
{ HRDATAD (M2RXD0, m2sio0_ctx.rxb, 8, "2SIO port 0 rx data buffer"), },
{ HRDATAD (M2TXD0, m2sio0_ctx.txb, 8, "2SIO port 0 tx data buffer"), },
{ HRDATAD (M2TXP0, m2sio0_ctx.txp, 8, "2SIO port 0 tx data pending"), },
{ FLDATAD (M2TXP0, m2sio0_ctx.txp, 0, "2SIO port 0 tx data pending"), },
{ FLDATAD (M2CON0, m2sio0_ctx.conn, 0, "2SIO port 0 connection status"), },
{ FLDATAD (M2RIE0, m2sio0_ctx.rie, 0, "2SIO port 0 receive interrupt enable"), },
{ FLDATAD (M2TIE0, m2sio0_ctx.tie, 0, "2SIO port 0 transmit interrupt enable"), },
@ -282,6 +286,7 @@ static REG m2sio0_reg[] = {
{ FLDATAD (M2DCD0, m2sio0_ctx.stb, 2, "2SIO port 0 DCD status (active low)"), },
{ FLDATAD (M2CTS0, m2sio0_ctx.stb, 3, "2SIO port 0 CTS status (active low)"), },
{ FLDATAD (M2OVRN0, m2sio0_ctx.stb, 4, "2SIO port 0 OVRN status"), },
{ FLDATAD (DCDL0, m2sio0_ctx.dcdl, 0, "2SIO port 0 DCD latch"), },
{ DRDATAD (M2WAIT0, m2sio0_unit[0].wait, 32, "2SIO port 0 wait cycles"), },
{ FLDATAD (M2INTEN0, m2sio0_ctx.intenable, 1, "2SIO port 0 Global vectored interrupt enable"), },
{ DRDATAD (M2VEC0, m2sio0_ctx.intvector, 8, "2SIO port 0 interrupt vector"), },
@ -293,7 +298,7 @@ static REG m2sio1_reg[] = {
{ HRDATAD (M2CTL1, m2sio1_ctx.ctb, 8, "2SIO port 1 control register"), },
{ HRDATAD (M2RXD1, m2sio1_ctx.rxb, 8, "2SIO port 1 rx data buffer"), },
{ HRDATAD (M2TXD1, m2sio1_ctx.txb, 8, "2SIO port 1 tx data buffer"), },
{ HRDATAD (M2TXP1, m2sio1_ctx.txp, 8, "2SIO port 1 tx data pending"), },
{ FLDATAD (M2TXP1, m2sio1_ctx.txp, 0, "2SIO port 1 tx data pending"), },
{ FLDATAD (M2CON1, m2sio1_ctx.conn, 0, "2SIO port 1 connection status"), },
{ FLDATAD (M2RIE1, m2sio1_ctx.rie, 0, "2SIO port 1 receive interrupt enable"), },
{ FLDATAD (M2TIE1, m2sio1_ctx.tie, 0, "2SIO port 1 transmit interrupt enable"), },
@ -303,6 +308,7 @@ static REG m2sio1_reg[] = {
{ FLDATAD (M2DCD1, m2sio1_ctx.stb, 2, "2SIO port 1 DCD status (active low)"), },
{ FLDATAD (M2CTS1, m2sio1_ctx.stb, 3, "2SIO port 1 CTS status (active low)"), },
{ FLDATAD (M2OVRN1, m2sio1_ctx.stb, 4, "2SIO port 1 OVRN status"), },
{ FLDATAD (DCDL1, m2sio1_ctx.dcdl, 0, "2SIO port 1 DCD latch"), },
{ DRDATAD (M2WAIT1, m2sio1_unit[0].wait, 32, "2SIO port 1 wait cycles"), },
{ FLDATAD (M2INTEN1, m2sio1_ctx.intenable, 1, "2SIO port 1 Global vectored interrupt enable"), },
{ DRDATAD (M2VEC1, m2sio1_ctx.intvector, 8, "2SIO port 1 interrupt vector"), },
@ -405,8 +411,9 @@ static t_stat m2sio_reset(DEVICE *dptr, int32 (*routine)(const int32, const int3
tmxr_set_modem_control_passthru(xptr->tmxr);
/* Reset status registers */
xptr->stb = 0;
xptr->txp = 0;
xptr->stb = M2SIO_CTS | M2SIO_DCD;
xptr->txp = FALSE;
xptr->dcdl = FALSE;
if (dptr->units[0].flags & UNIT_ATT) {
m2sio_config_rts(dptr, 1); /* disable RTS */
}
@ -435,7 +442,7 @@ static t_stat m2sio_svc(UNIT *uptr)
if (uptr->flags & UNIT_ATT) {
if (tmxr_poll_conn(xptr->tmxr) >= 0) { /* poll connection */
xptr->conn = 1; /* set connected */
xptr->conn = TRUE; /* set connected */
sim_debug(STATUS_MSG, uptr->dptr, "new connection.\n");
}
@ -450,11 +457,20 @@ static t_stat m2sio_svc(UNIT *uptr)
if ((stb ^ xptr->stb) & M2SIO_CTS) {
sim_debug(STATUS_MSG, uptr->dptr, "CTS state changed to %s.\n", (xptr->stb & M2SIO_CTS) ? "LOW" : "HIGH");
}
if (!xptr->dcdl) {
xptr->stb &= ~M2SIO_DCD;
xptr->stb |= ((s & TMXR_MDM_DCD) || (uptr->flags & UNIT_M2SIO_DCD)) ? 0 : M2SIO_DCD; /* Active Low */
if ((stb ^ xptr->stb) & M2SIO_DCD) {
if ((xptr->stb & M2SIO_DCD) == M2SIO_DCD) {
xptr->dcdl = TRUE;
if (xptr->rie) {
m2sio_int(uptr);
}
}
sim_debug(STATUS_MSG, uptr->dptr, "DCD state changed to %s.\n", (xptr->stb & M2SIO_DCD) ? "LOW" : "HIGH");
}
}
/* Enable receiver if DCD is active low */
xptr->tmln->rcve = !(xptr->stb & M2SIO_DCD);
@ -465,19 +481,25 @@ static t_stat m2sio_svc(UNIT *uptr)
if (uptr->flags & UNIT_ATT) {
if (!(xptr->stb & M2SIO_CTS)) { /* Active low */
r = tmxr_putc_ln(xptr->tmln, xptr->txb);
xptr->txp = 0; /* Reset TX Pending */
xptr->txp = FALSE; /* Reset TX Pending */
} else {
r = SCPE_STALL;
}
} else {
r = sim_putchar(xptr->txb);
xptr->txp = 0; /* Reset TX Pending */
xptr->txp = FALSE; /* Reset TX Pending */
}
if (r == SCPE_LOST) {
xptr->conn = 0; /* Connection was 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->tie)) {
m2sio_int(uptr);
}
}
/* Update TDRE if not set and no character pending */
@ -506,9 +528,8 @@ static t_stat m2sio_svc(UNIT *uptr)
xptr->rxb = c & 0xff;
xptr->stb |= M2SIO_RDRF;
xptr->stb &= ~(M2SIO_FE | M2SIO_OVRN | M2SIO_PE);
if ((xptr->rie) && (xptr->intenable)) {
vectorInterrupt |= (1 << xptr->intvector);
dataBus[xptr->intvector] = xptr->databus;
if (xptr->rie) {
m2sio_int(uptr);
}
}
}
@ -769,15 +790,16 @@ static int32 m2sio_stat(DEVICE *dptr, int32 io, int32 data)
if ((data & M2SIO_RESET) == M2SIO_RESET) {
sim_debug(STATUS_MSG, dptr, "MC6850 master reset.\n");
xptr->stb &= (M2SIO_CTS | M2SIO_DCD); /* Reset status register */
xptr->rxb = 0;
xptr->txp = 0;
xptr->tie = 1;
xptr->rie = 1;
xptr->rxb = 0x00;
xptr->txp = FALSE;
xptr->tie = FALSE;
xptr->rie = FALSE;
xptr->dcdl = FALSE;
m2sio_config_rts(dptr, 1); /* disable RTS */
} else {
/* Interrupt Enable */
xptr->rie = (data & M2SIO_RIE) == M2SIO_RIE; /* Receive enable */
xptr->tie = (data & M2SIO_RTSMSK) == M2SIO_RTSLTIE; /* Transmit enable */
xptr->rie = (data & M2SIO_RIE) == M2SIO_RIE; /* Receive interrupt enable */
xptr->tie = (data & M2SIO_RTSMSK) == M2SIO_RTSLTIE; /* Transmit interrupt enable */
switch (data & M2SIO_RTSMSK) {
case M2SIO_RTSLTIE:
case M2SIO_RTSLTID:
@ -812,15 +834,30 @@ static int32 m2sio_data(DEVICE *dptr, int32 io, int32 data)
if (io == IO_RD) {
r = xptr->rxb;
xptr->stb &= ~(M2SIO_RDRF | M2SIO_FE | M2SIO_OVRN | M2SIO_PE);
xptr->stb &= ~(M2SIO_RDRF | M2SIO_FE | M2SIO_OVRN | M2SIO_PE | M2SIO_IRQ);
xptr->dcdl = FALSE;
} else {
xptr->txb = data;
xptr->stb &= ~M2SIO_TDRE;
xptr->txp = 1;
xptr->stb &= ~(M2SIO_TDRE | M2SIO_IRQ);
xptr->txp = TRUE;
r = 0x00;
}
return r;
}
static void m2sio_int(UNIT *uptr)
{
M2SIO_CTX *xptr;
xptr = (M2SIO_CTX *) uptr->dptr->ctxt;
if (xptr->intenable) {
vectorInterrupt |= (1 << xptr->intvector);
dataBus[xptr->intvector] = xptr->databus;
xptr->stb |= M2SIO_IRQ;
sim_debug(IRQ_MSG, uptr->dptr, "%s: IRQ Vector=%d Status=%02X\n", sim_uname(uptr), xptr->intvector, xptr->stb);
}
}

View file

@ -297,7 +297,7 @@ static t_stat pmmi_reset(DEVICE *dptr)
static t_stat pmmi_svc(UNIT *uptr)
{
int32 c,s,ireg2;
t_stat r;
t_stat r = SCPE_OK;
uint32 ms;
/* Check for new incoming connection */
@ -358,12 +358,14 @@ static t_stat pmmi_svc(UNIT *uptr)
/* TX data */
if (pmmi_ctx.txp) {
if (uptr->flags & UNIT_ATT) {
if (!(pmmi_ctx.ireg2 & PMMI_CTS)) { /* Active low */
/*
** If CTS active low, send byte
** otherwise, toss character
*/
if (!(pmmi_ctx.ireg2 & PMMI_CTS)) {
r = tmxr_putc_ln(pmmi_ctx.tmln, pmmi_ctx.oreg1);
pmmi_ctx.txp = 0; /* Reset TX Pending */
} else {
r = SCPE_STALL;
}
pmmi_ctx.txp = 0; /* Reset TX Pending */
} else {
r = sim_putchar(pmmi_ctx.oreg1);
pmmi_ctx.txp = 0; /* Reset TX Pending */
@ -558,27 +560,12 @@ static t_stat pmmi_config_line(UNIT *uptr)
sprintf(config, "%d-%c%c%c", pmmi_ctx.baud, b,p,s);
sim_debug(STATUS_MSG, uptr->dptr, "setting port configuration to '%s'.\n", config);
r = tmxr_set_config_line(pmmi_ctx.tmln, config);
sim_debug(STATUS_MSG, uptr->dptr, "port configuration set to '%s'.\n", config);
/*
** AltairZ80 and TMXR refuse to want to play together
** nicely when the CLOCK register is set to anything
** other than 0.
**
** This work-around is for those of us that may wish
** to run irrelevant, old software, that use TMXR and
** rely on some semblance of timing (Remote CP/M, BYE,
** RBBS, PCGET/PUT, Xmodem, MEX, Modem7, or most
** other communications software), on contemporary
** hardware.
**
** Serial ports are self-limiting and sockets will run
** at the clocked CPU speed.
*/
pmmi_ctx.tmln->txbps = 0; /* Get TMXR's rate-limiting out of our way */
pmmi_ctx.tmln->rxbps = 0; /* Get TMXR's rate-limiting out of our way */
pmmi_ctx.tmln->txbps = 0; /* Get TMXR out of our way */
pmmi_ctx.tmln->rxbps = 0; /* Get TMXR out of our way */
return r;
}
@ -702,15 +689,15 @@ static int32 pmmi_reg3(int32 io, int32 data)
/* Set/Clear DTR */
s = TMXR_MDM_DTR | ((pmmi_dev.units[0].flags & UNIT_PMMI_RTS) ? TMXR_MDM_RTS : 0);
if (data & PMMI_DTR) {
sim_debug(STATUS_MSG, &pmmi_dev, "setting DTR HIGH.\n");
tmxr_set_get_modem_bits(pmmi_ctx.tmln, s, 0, NULL);
if (pmmi_ctx.oreg0 & PMMI_SH) {
pmmi_ctx.ireg2 &= ~PMMI_AP; /* Answer Phone Bit (active low) */
}
sim_debug(STATUS_MSG, &pmmi_dev, "set DTR HIGH.\n");
} else {
sim_debug(STATUS_MSG, &pmmi_dev, "setting DTR LOW.\n");
tmxr_set_get_modem_bits(pmmi_ctx.tmln, 0, s, NULL);
pmmi_ctx.ireg2 |= PMMI_AP;
sim_debug(STATUS_MSG, &pmmi_dev, "set DTR LOW.\n");
}
}
return 0x00;