AltairZ80: Improved vector interrupt implementation

This commit is contained in:
Peter Schorn 2021-12-04 20:42:03 +01:00
parent 4c44975e98
commit ab6c2c043e
5 changed files with 137 additions and 25 deletions

View file

@ -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();

View file

@ -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 */

View file

@ -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",

View file

@ -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;
}
}
}

View file

@ -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);