From ab6c2c043ed792a7d5c3116c3a9bd7a32825fe77 Mon Sep 17 00:00:00 2001 From: Peter Schorn Date: Sat, 4 Dec 2021 20:42:03 +0100 Subject: [PATCH] AltairZ80: Improved vector interrupt implementation --- AltairZ80/altairz80_cpu.c | 79 +++++++++++++++++++++++++++++++++----- AltairZ80/altairz80_defs.h | 1 + AltairZ80/altairz80_sio.c | 19 +++++++++ AltairZ80/s100_2sio.c | 35 ++++++++++++----- AltairZ80/wd179x.c | 28 +++++++++++--- 5 files changed, 137 insertions(+), 25 deletions(-) diff --git a/AltairZ80/altairz80_cpu.c b/AltairZ80/altairz80_cpu.c index 34a96a2d..82a9b685 100644 --- a/AltairZ80/altairz80_cpu.c +++ b/AltairZ80/altairz80_cpu.c @@ -35,6 +35,7 @@ /* Debug flags */ #define IN_MSG (1 << 0) #define OUT_MSG (1 << 1) +#define INT_MSG (1 << 2) #define PCQ_SIZE 64 /* must be 2**n */ #define PCQ_SIZE_LOG2 6 /* log2 of PCQ_SIZE */ @@ -241,7 +242,8 @@ UNIT cpu_unit = { int32 IP_S; /* IP register (8086) */ int32 FLAGS_S; /* flags register (8086) */ int32 SR = 0; /* switch register */ - int32 vectorInterrupt = 0; /* Vector Interrupt bitfield */ + uint32 vectorInterrupt = 0; /* VI0-7 Vector Interrupt bitfield */ + uint8 dataBus[MAX_INT_VECTORS]; /* Vector Interrupt data bus value */ static int32 bankSelect = 0; /* determines selected memory bank */ static uint32 common = 0xc000; /* addresses >= 'common' are in common memory */ static uint32 common_low = 0; /* Common area is in low memory */ @@ -473,6 +475,10 @@ REG cpu_reg[] = { }, /* 82 */ { HRDATAD(COMMONLOW,common_low, 1, "If set, use low memory for common area"), }, /* 83 */ + { HRDATAD(VECINT,vectorInterrupt, 2, "Vector Interrupt psuedo register"), + }, /* 84 */ + { BRDATAD (DATABUS, dataBus, 16, 8, MAX_INT_VECTORS, "Data bus pseudo register"), + REG_RO + REG_CIRC }, /* 85 */ { NULL } }; @@ -572,6 +578,7 @@ static MTAB cpu_mod[] = { static DEBTAB cpu_dt[] = { { "LOG_IN", IN_MSG, "Log IN operations" }, { "LOG_OUT", OUT_MSG, "Log OUT operations" }, + { "LOG_INT", INT_MSG, "Log interrupts" }, { NULL, 0 } }; @@ -2186,9 +2193,7 @@ static t_stat sim_instr_mmu (void) { PC = timerInterruptHandler & ADDRMASK; } - if ((IM_S == 2) && vectorInterrupt && (IFF_S & 1)) { - int32 vectable = (IR_S & 0xFF00); - int32 vector; + if ((IM_S == 1) && vectorInterrupt && (IFF_S & 1)) { /* Z80 Interrupt Mode 1 */ uint32 tempVectorInterrupt = vectorInterrupt; uint8 intVector = 0; @@ -2198,6 +2203,7 @@ static t_stat sim_instr_mmu (void) { } vectorInterrupt &= ~(1 << intVector); + specialProcessing = clockFrequency | sim_brk_summ; IFF_S = 0; /* disable interrupts */ CHECK_BREAK_TWO_BYTES_EXTENDED(SP - 2, SP - 1, (vectorInterrupt |= (1 << intVector), IFF_S |= 1)); @@ -2210,9 +2216,39 @@ static t_stat sim_instr_mmu (void) { PCQ_ENTRY(PC - 1); } - vector = GetBYTE(vectable + (intVector * 2)+1) << 8; - vector |= GetBYTE(vectable + (intVector * 2)); - PC = vector & ADDRMASK; + PC = 0x0038; + + sim_debug(INT_MSG, &cpu_dev, ADDRESS_FORMAT + " INT(mode=1 intVector=%d PC=%04X)\n", PCX, intVector, PC); + } else if ((IM_S == 2) && vectorInterrupt && (IFF_S & 1)) { + int32 vector; + uint32 tempVectorInterrupt = vectorInterrupt; + uint8 intVector = 0; + + while ((tempVectorInterrupt & 1) == 0) { + tempVectorInterrupt >>= 1; + intVector++; + } + + vectorInterrupt &= ~(1 << intVector); + + specialProcessing = clockFrequency | sim_brk_summ; + IFF_S = 0; /* disable interrupts */ + CHECK_BREAK_TWO_BYTES_EXTENDED(SP - 2, SP - 1, (vectorInterrupt |= (1 << intVector), IFF_S |= 1)); + if ((GetBYTE(PC) == HALTINSTRUCTION) && ((cpu_unit.flags & UNIT_CPU_STOPONHALT) == 0)) { + PUSH(PC + 1); + PCQ_ENTRY(PC); + } + else { + PUSH(PC); + PCQ_ENTRY(PC - 1); + } + + vector = (HIGH_REGISTER(IR_S) << 8) | dataBus[intVector]; + PC = ((GetBYTE(vector+1) << 8) | GetBYTE(vector)) & ADDRMASK; + + sim_debug(INT_MSG, &cpu_dev, ADDRESS_FORMAT + " INT(mode=2 intVector=%d vector=%04X PC=%04X)\n", PCX, intVector, vector, PC); } if (keyboardInterrupt && (IFF_S & 1)) { @@ -2246,7 +2282,32 @@ static t_stat sim_instr_mmu (void) { PCX = PC; INCR(1); - switch(RAM_PP(PC)) { + op = RAM_PP(PC); + + /* 8080 INT/Z80 Interrupt Mode 0 + Instruction to execute (ex. RST0-7) is on the data bus + NOTE: does not support multi-byte instructions such as CALL + */ + if ((IM_S == 0) && vectorInterrupt && (IFF_S & 1)) { /* 8080/Z80 Interrupt Mode 0 */ + uint32 tempVectorInterrupt = vectorInterrupt; + uint8 intVector = 0; + + while ((tempVectorInterrupt & 1) == 0) { + tempVectorInterrupt >>= 1; + intVector++; + } + + IFF_S = 0; /* disable interrupts */ + + vectorInterrupt &= ~(1 << intVector); + + op = dataBus[intVector]; + + sim_debug(INT_MSG, &cpu_dev, ADDRESS_FORMAT + " INT(mode=0 vectorInterrupt=%X intVector=%d op=%02X)\n", PCX, vectorInterrupt, intVector, op); + } + + switch(op) { case 0x00: /* NOP */ tStates += 4; /* NOP 4 */ @@ -6261,7 +6322,7 @@ static t_stat cpu_reset(DEVICE *dptr) { BC_S = DE_S = HL_S = 0; BC1_S = DE1_S = HL1_S = 0; IR_S = IX_S = IY_S = SP_S = 0; - IFF_S = 3; + IM_S = IFF_S = 0; /* Set IM0, reset IFF1 and IFF2 */ setBankSelect(0); cpu8086reset(); m68k_cpu_reset(); diff --git a/AltairZ80/altairz80_defs.h b/AltairZ80/altairz80_defs.h index 4f8279d5..f8e6897c 100644 --- a/AltairZ80/altairz80_defs.h +++ b/AltairZ80/altairz80_defs.h @@ -45,6 +45,7 @@ #define ALTAIR_ROM_LOW 0xff00 /* start address of regular Altair ROM */ #define RESOURCE_TYPE_MEMORY 1 #define RESOURCE_TYPE_IO 2 +#define MAX_INT_VECTORS 32 /* maximum number of interrupt vectors */ #define NUM_OF_DSK 16 /* NUM_OF_DSK must be power of two */ #define LDA_INSTRUCTION 0x3e /* op-code for LD A,<8-bit value> instruction */ diff --git a/AltairZ80/altairz80_sio.c b/AltairZ80/altairz80_sio.c index 786a2184..71ea0d6f 100644 --- a/AltairZ80/altairz80_sio.c +++ b/AltairZ80/altairz80_sio.c @@ -167,6 +167,8 @@ extern uint32 PCX; extern int32 SR; extern UNIT cpu_unit; extern const char* handlerNameForPort(const int32 port); +extern uint32 vectorInterrupt; /* Interrupt Request */ +extern uint8 dataBus[MAX_INT_VECTORS]; /* Data Bus Value */ /* Debug Flags */ static DEBTAB generic_dt[] = { @@ -220,6 +222,8 @@ static int32 versionPos = 0; /* determines state for sending static int32 lastCPMStatus = 0; /* result of last attachCPM command */ static int32 lastCommand = 0; /* most recent command processed on port 0xfeh */ static int32 getCommonPos = 0; /* determines state for sending the 'common' register */ +static int32 genInterruptPos = 0; /* determines state for receiving interrupt vector and data */ +static int32 genInterruptVec = 0; /* stores interrupt vector */ /* CPU Clock Frequency related */ static uint32 newClockFrequency; @@ -1253,6 +1257,7 @@ enum simhPseudoDeviceCommands { /* do not change order or remove commands, add o readURLCmd, /* 30 read the contents of an URL */ getCPUClockFrequency, /* 31 get the clock frequency of the CPU */ setCPUClockFrequency, /* 32 set the clock frequency of the CPU */ + genInterruptCmd, /* 33 generate interrupt */ kSimhPseudoDeviceCommands }; @@ -1290,6 +1295,7 @@ static const char *cmdNames[kSimhPseudoDeviceCommands] = { "readURL", "getCPUClockFrequency", "setCPUClockFrequency", + "genInterrupt", }; #define TIMER_STACK_LIMIT 10 /* stack depth of timer stack */ @@ -1732,6 +1738,19 @@ static int32 simh_out(const int32 port, const int32 data) { } break; + case genInterruptCmd: + if (genInterruptPos == 0) { + genInterruptVec = data; + genInterruptPos = 1; + sim_printf("genInterruptVec=%d genInterruptPos=%d\n", genInterruptVec, genInterruptPos); + } else { + vectorInterrupt |= (1 << genInterruptVec); + dataBus[genInterruptVec] = data; + genInterruptPos = lastCommand = 0; + sim_printf("genInterruptVec=%d vectorInterrupt=%X dataBus=%02X genInterruptPos=%d\n", genInterruptVec, vectorInterrupt, data, genInterruptPos); + } + break; + default: /* lastCommand not yet set */ sim_debug(CMD_MSG, &simh_device, "SIMH: " ADDRESS_FORMAT " CMD(0x%02x) <- %i (0x%02x, '%s')\n", diff --git a/AltairZ80/s100_2sio.c b/AltairZ80/s100_2sio.c index 271e3386..6c4ca125 100644 --- a/AltairZ80/s100_2sio.c +++ b/AltairZ80/s100_2sio.c @@ -159,6 +159,7 @@ typedef struct { int32 tie; /* Tx Int Enable */ uint8 intenable; /* Interrupt Enable */ uint8 intvector; /* Interrupt Vector */ + uint8 databus; /* Data Bus Value */ } M2SIO_CTX; extern uint32 getClockFrequency(void); @@ -185,7 +186,8 @@ 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); -extern int32 vectorInterrupt; /* Vector Interrupt bits */ +extern uint32 vectorInterrupt; /* Vector Interrupt bits */ +extern uint8 dataBus[MAX_INT_VECTORS]; /* Data bus value */ /* Debug Flags */ static DEBTAB m2sio_dt[] = { @@ -224,21 +226,27 @@ static TMXR m2sio1_tmxr = { /* multiplexer descriptor */ }; -#define UNIT_V_M2SIO_DTR (UNIT_V_UF + 0) /* DTR follows RTS */ +#define UNIT_V_M2SIO_CONSOLE (UNIT_V_UF + 0) /* Port checks console for input */ +#define UNIT_M2SIO_CONSOLE (1 << UNIT_V_M2SIO_CONSOLE) +#define UNIT_V_M2SIO_DTR (UNIT_V_UF + 1) /* DTR follows RTS */ #define UNIT_M2SIO_DTR (1 << UNIT_V_M2SIO_DTR) -#define UNIT_V_M2SIO_DCD (UNIT_V_UF + 1) /* Force DCD active low */ +#define UNIT_V_M2SIO_DCD (UNIT_V_UF + 2) /* Force DCD active low */ #define UNIT_M2SIO_DCD (1 << UNIT_V_M2SIO_DCD) static MTAB m2sio_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL, "Sets MITS 2SIO base I/O address" }, - { UNIT_M2SIO_DTR, UNIT_M2SIO_DTR, "DTR", "DTR", NULL, NULL, NULL, + { UNIT_M2SIO_CONSOLE, UNIT_M2SIO_CONSOLE, "CONSOLE", "CONSOLE", NULL, NULL, NULL, + "Port checks for console input" }, + { UNIT_M2SIO_CONSOLE, 0, "NOCONSOLE", "NOCONSOLE", NULL, NULL, NULL, + "Port does not check for console input" }, + { UNIT_M2SIO_DTR, UNIT_M2SIO_DTR, "DTR", "DTR", NULL, NULL, NULL, "DTR follows RTS" }, - { UNIT_M2SIO_DTR, 0, "NODTR", "NODTR", NULL, NULL, NULL, + { UNIT_M2SIO_DTR, 0, "NODTR", "NODTR", NULL, NULL, NULL, "DTR does not follow RTS (default)" }, - { UNIT_M2SIO_DCD, UNIT_M2SIO_DCD, "DCD", "DCD", NULL, NULL, NULL, + { UNIT_M2SIO_DCD, UNIT_M2SIO_DCD, "DCD", "DCD", NULL, NULL, NULL, "Force DCD active low" }, - { UNIT_M2SIO_DCD, 0, "NODCD", "NODCD", NULL, NULL, NULL, + { UNIT_M2SIO_DCD, 0, "NODCD", "NODCD", NULL, NULL, NULL, "DCD follows status line (default)" }, { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "BAUD", "BAUD", &m2sio_set_baud, &m2sio_show_baud, NULL, "Set baud rate (default=9600)" }, @@ -249,7 +257,7 @@ static M2SIO_CTX m2sio0_ctx = {{0, 0, M2SIO0_IOBASE, M2SIO0_IOSIZE}, 0, 0, m2sio static M2SIO_CTX m2sio1_ctx = {{0, 0, M2SIO1_IOBASE, M2SIO1_IOSIZE}, 1, 0, m2sio1_tmln, &m2sio1_tmxr, M2SIO_BAUD, 1}; static UNIT m2sio0_unit[] = { - { UDATA (&m2sio_svc, UNIT_ATTABLE | UNIT_DISABLE, 0), M2SIO_WAIT }, + { UDATA (&m2sio_svc, UNIT_ATTABLE | UNIT_DISABLE | UNIT_M2SIO_CONSOLE, 0), M2SIO_WAIT }, }; static UNIT m2sio1_unit[] = { { UDATA (&m2sio_svc, UNIT_ATTABLE | UNIT_DISABLE, 0), M2SIO_WAIT }, @@ -273,6 +281,7 @@ static REG m2sio0_reg[] = { { 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"), }, + { HRDATAD (M2DBVAL0, m2sio0_ctx.databus, 8, "2SIO port 0 data bus value"), }, { NULL } }; static REG m2sio1_reg[] = { @@ -293,6 +302,7 @@ static REG m2sio1_reg[] = { { 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"), }, + { HRDATAD (M2DBVAL1, m2sio1_ctx.databus, 8, "2SIO port 1 data bus value"), }, { NULL } }; @@ -482,15 +492,20 @@ static t_stat m2sio_svc(UNIT *uptr) tmxr_poll_rx(xptr->tmxr); c = tmxr_getc_ln(xptr->tmln); - } else { + } else if (uptr->flags & UNIT_M2SIO_CONSOLE) { c = sim_poll_kbd(); + } else { + c = 0; } if (c & (TMXR_VALID | SCPE_KFLAG)) { 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); + if ((xptr->rie) && (xptr->intenable)) { + vectorInterrupt |= (1 << xptr->intvector); + dataBus[xptr->intvector] = xptr->databus; + } } } diff --git a/AltairZ80/wd179x.c b/AltairZ80/wd179x.c index 0b989134..deb4fb3a 100644 --- a/AltairZ80/wd179x.c +++ b/AltairZ80/wd179x.c @@ -133,7 +133,8 @@ typedef struct { WD179X_DRIVE_INFO drive[WD179X_MAX_DRIVES]; } WD179X_INFO; -extern int32 vectorInterrupt; /* FDC interrupt pending */ +extern uint32 vectorInterrupt; /* FDC interrupt pending */ +extern uint8 dataBus[MAX_INT_VECTORS]; /* FDC interrupt data bus values */ static SECTOR_FORMAT sdata; extern uint32 PCX; @@ -953,7 +954,10 @@ static uint8 Do1793Command(uint8 cCommand) } } else { wd179x_info->intrq = 1; - if (wd179x_info->intenable) vectorInterrupt |= (1 << wd179x_info->intvector); + if (wd179x_info->intenable) { + vectorInterrupt |= (1 << wd179x_info->intvector); + dataBus[wd179x_info->intvector] = wd179x_info->intvector*2; + } } wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */ } @@ -997,7 +1001,10 @@ static uint8 Do1793Command(uint8 cCommand) wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */ wd179x_info->intrq = 1; - if (wd179x_info->intenable) vectorInterrupt |= (1 << wd179x_info->intvector); + if (wd179x_info->intenable) { + vectorInterrupt |= (1 << wd179x_info->intvector); + dataBus[wd179x_info->intvector] = wd179x_info->intvector*2; + } wd179x_info->drq = 1; break; /* Type II Commands */ @@ -1051,7 +1058,10 @@ uint8 WD179X_Write(const uint32 Addr, uint8 cData) wd179x_info->fdc_write_track = FALSE; wd179x_info->fdc_datacount = 0; wd179x_info->fdc_dataindex = 0; - if (wd179x_info->intenable) vectorInterrupt |= (1 << wd179x_info->intvector); + if (wd179x_info->intenable) { + vectorInterrupt |= (1 << wd179x_info->intvector); + dataBus[wd179x_info->intvector] = wd179x_info->intvector*2; + } Do1793Command(cData); break; @@ -1077,7 +1087,10 @@ uint8 WD179X_Write(const uint32 Addr, uint8 cData) wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Clear DRQ, BUSY */ wd179x_info->drq = 0; wd179x_info->intrq = 1; - if (wd179x_info->intenable) vectorInterrupt |= (1 << wd179x_info->intvector); + if (wd179x_info->intenable) { + vectorInterrupt |= (1 << wd179x_info->intvector); + dataBus[wd179x_info->intvector] = wd179x_info->intvector*2; + } sim_debug(WR_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " Writing sector, T:%2d/S:%d/N:%2d, Len=%d\n", wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_head, wd179x_info->fdc_sector, 128 << wd179x_info->fdc_sec_len); @@ -1198,7 +1211,10 @@ uint8 WD179X_Write(const uint32 Addr, uint8 cData) wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY | WD179X_STAT_LOST_DATA); /* Clear BUSY, LOST_DATA */ wd179x_info->drq = 0; wd179x_info->intrq = 1; - if (wd179x_info->intenable) vectorInterrupt |= (1 << wd179x_info->intvector); + if (wd179x_info->intenable) { + vectorInterrupt |= (1 << wd179x_info->intvector); + dataBus[wd179x_info->intvector] = wd179x_info->intvector*2; + } /* Recalculate disk size */ pDrive->uptr->capac = sim_fsize(pDrive->uptr->fileref);