blob: 0aabf92a684c7b197ca37bfbdc03b8cebe96e127 [file] [log] [blame] [raw]
/* id4_cpu.c: Interdata 4 CPU simulator
Copyright (c) 1993-2001, Robert M. Supnik
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 Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
10-Aug-01 RMS Removed register in declarations
07-Oct-00 RMS Overhauled I/O subsystem
14-Apr-99 RMS Changed t_addr to unsigned
The register state for the Interdata 4 CPU is:
R[0:F]<0:15> general registers
F[0:7]<0:31> floating point registers
PSW<0:31> processor status word, including
STAT<0:11> status flags
CC<0:3> condition codes
PC<0:15> program counter
int_req[8]<0:31> interrupt requests
int_enb[8]<0:31> interrupt enables
The Interdata 4 has three instruction formats: register to register,
register to memory, and register to storage. The formats are:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| op | R1 | R2 | register-register
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| op | R1 | RX | register-memory
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| address |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| op | R1 | RX | register-storage
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| address |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
For register-memory and register-storage instructions, an effective
address is calculated as follows:
effective addr = address + RX (if RX > 0)
Register-memory instructions can access an address space of 65K bytes.
*/
/* This routine is the instruction decode routine for the Interdata 4.
It is called from the simulator control program to execute
instructions in simulated memory, starting at the simulated PC.
It runs until 'reason' is set non-zero.
General notes:
1. Reasons to stop. The simulator can be stopped by:
HALT instruction
breakpoint encountered
wait state and no I/O outstanding
invalid instruction
I/O error in I/O simulator
2. Interrupts. Each device has an interrupt request flag and an
interrupt enabled flag. To facilitate evaluation, all interrupt
requests are kept in int_req, and all enables in int_enb. If
external interrupts are enabled in the PSW, and an external request
is pending, an interrupt occurs.
3. Non-existent memory. On the Interdata 4, reads to non-existent
memory return zero, and writes are ignored. In the simulator, the
largest possible memory is instantiated and initialized to zero.
Thus, only writes need be checked against actual memory size.
4. Adding I/O devices. These modules must be modified:
id4_defs.h add a definition for the device mnemonic
id4_cpu.c add dispatch routine to table dev_tab
id4_sys.c add pointer to data structures to sim_devices
*/
#include "id4_defs.h"
#define ILL_ADR_FLAG (MAXMEMSIZE)
#define save_ibkpt (cpu_unit.u3)
#define UNIT_V_MSIZE (UNIT_V_UF) /* dummy mask */
#define UNIT_MSIZE (1 << UNIT_V_MSIZE)
#define SIGN_EXT(x) (((x) & SIGN)? (x) | ~MAGMASK: (x))
#define CC_GL(x) if ((x) & SIGN) CC = CC_L; \
else if (x) CC = CC_G; \
else CC = 0
#define CC_GL_C(x) if ((x) & SIGN) CC = CC_L; \
else if (x) CC = CC_G; \
else CC = 0
/* else CC = CC & (CC_G | CC_L) */
#define BUILD_PSW ((PSW & ~CC_MASK) | CC)
#define PSW_SWAP(o,n) WriteW ((o), BUILD_PSW); \
WriteW ((o) + 2, PC); \
PSW = ReadW (n); \
PC = ReadW ((n) + 2); \
CC = PSW & CC_MASK
uint16 M[MAXMEMSIZE >> 1] = { 0 }; /* memory */
int32 R[16] = { 0 }; /* general registers */
uint32 F[8] = { 0 }; /* fp registers */
int32 PSW = 0; /* processor status word */
int32 saved_PC = 0; /* program counter */
int32 SR = 0; /* switch register */
int32 DR = 0; /* display register */
int32 drmod = 0; /* mode */
int32 srpos = 0; /* switch register pos */
int32 drpos = 0; /* display register pos */
int32 int_req[INTSZ] = { 0 }; /* interrupt requests */
int32 int_enb[INTSZ] = { 0 }; /* interrupt enables */
t_bool qanyin = FALSE; /* interrupt outstanding */
int32 stop_inst = 0; /* stop on ill inst */
int32 ibkpt_addr = ILL_ADR_FLAG | AMASK; /* breakpoint addr */
int32 old_PC = 0; /* previous PC */
extern int32 sim_int_char;
extern UNIT *sim_clock_queue;
t_bool int_eval (void);
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
t_stat cpu_reset (DEVICE *dptr);
t_stat cpu_svc (UNIT *uptr);
t_stat cpu_set_size (UNIT *uptr, int32 value);
extern int32 le (int32 op, int32 r1, int32 r2, int32 ea);
extern int32 ce (int32 op, int32 r1, int32 r2, int32 ea);
extern int32 ase (int32 op, int32 r1, int32 r2, int32 ea);
extern int32 me (int32 op, int32 r1, int32 r2, int32 ea);
extern int32 de (int32 op, int32 r1, int32 r2, int32 ea);
extern int32 display (int32 op, int32 datout);
extern int32 tt (int32 op, int32 datout);
extern int32 pt (int32 op, int32 datout);
int32 (*dev_tab[DEVNO])(int32 op, int32 datout) = {
NULL, &display, &tt, &pt, NULL, NULL, NULL, NULL };
/* CPU data structures
cpu_dev CPU device descriptor
cpu_unit CPU unit descriptor
cpu_reg CPU register list
cpu_mod CPU modifiers list
*/
UNIT cpu_unit = { UDATA (&cpu_svc, UNIT_FIX + UNIT_BINK,
MAXMEMSIZE) };
REG cpu_reg[] = {
{ HRDATA (PC, saved_PC, 16) },
{ HRDATA (R0, R[0], 16) },
{ HRDATA (R1, R[1], 16) },
{ HRDATA (R2, R[2], 16) },
{ HRDATA (R3, R[3], 16) },
{ HRDATA (R4, R[4], 16) },
{ HRDATA (R5, R[5], 16) },
{ HRDATA (R6, R[6], 16) },
{ HRDATA (R7, R[7], 16) },
{ HRDATA (R8, R[8], 16) },
{ HRDATA (R9, R[9], 16) },
{ HRDATA (RA, R[10], 16) },
{ HRDATA (RB, R[11], 16) },
{ HRDATA (RC, R[12], 16) },
{ HRDATA (RD, R[13], 16) },
{ HRDATA (RE, R[14], 16) },
{ HRDATA (RF, R[15], 16) },
{ HRDATA (F0, F[0], 32) },
{ HRDATA (F2, F[1], 32) },
{ HRDATA (F4, F[2], 32) },
{ HRDATA (F6, F[3], 32) },
{ HRDATA (F8, F[4], 32) },
{ HRDATA (FA, F[5], 32) },
{ HRDATA (FC, F[6], 32) },
{ HRDATA (FE, F[7], 32) },
{ HRDATA (PSW, PSW, 16) },
{ HRDATA (CC, PSW, 4) },
{ HRDATA (SR, SR, 16) },
{ HRDATA (DR, DR, 16) },
{ GRDATA (DR1, DR, 16, 16, 16) },
{ FLDATA (DRMOD, drmod, 0) },
{ FLDATA (SRPOS, srpos, 0) },
{ HRDATA (DRPOS, drpos, 2) },
{ HRDATA (IRQ0, int_req[0], 32) },
{ HRDATA (IRQ1, int_req[1], 32) },
{ HRDATA (IRQ2, int_req[2], 32) },
{ HRDATA (IRQ3, int_req[3], 32) },
{ HRDATA (IRQ4, int_req[4], 32) },
{ HRDATA (IRQ5, int_req[5], 32) },
{ HRDATA (IRQ6, int_req[6], 32) },
{ HRDATA (IRQ7, int_req[7], 32) },
{ HRDATA (IEN0, int_enb[0], 32) },
{ HRDATA (IEN1, int_enb[1], 32) },
{ HRDATA (IEN2, int_enb[2], 32) },
{ HRDATA (IEN3, int_enb[3], 32) },
{ HRDATA (IEN4, int_enb[4], 32) },
{ HRDATA (IEN5, int_enb[5], 32) },
{ HRDATA (IEN6, int_enb[6], 32) },
{ HRDATA (IEN7, int_enb[7], 32) },
{ FLDATA (STOP_INST, stop_inst, 0) },
{ HRDATA (OLDPC, old_PC, 16), REG_RO },
{ HRDATA (BREAK, ibkpt_addr, 17) },
{ ORDATA (WRU, sim_int_char, 8) },
{ NULL } };
MTAB cpu_mod[] = {
{ UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size },
{ UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size },
{ UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size },
{ UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size },
{ UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size},
{ UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size},
{ 0 } };
DEVICE cpu_dev = {
"CPU", &cpu_unit, cpu_reg, cpu_mod,
1, 16, 16, 2, 16, 16,
&cpu_ex, &cpu_dep, &cpu_reset,
NULL, NULL, NULL };
t_stat sim_instr (void)
{
extern int32 sim_interval;
int32 dev, i, j, r, t;
int32 PC, OP, R1, R2, EA, CC;
int32 inc, lim;
t_stat reason;
/* Restore register state */
PC = saved_PC & AMASK;
CC = PSW & CC_MASK; /* isolate cond codes */
qanyin = int_eval (); /* eval interrupts */
reason = 0;
/* Main instruction fetch/decode loop */
while (reason == 0) { /* loop until halted */
if (sim_interval <= 0) { /* check clock queue */
if (reason = sim_process_event ()) break;
qanyin = int_eval (); }
if ((PSW & PSW_EXI) && qanyin) { /* interrupt? */
PSW_SWAP (EXOPSW, EXNPSW); /* swap PSW */
continue; }
if (PSW & PSW_WAIT) { /* wait state? */
if (sim_clock_queue != NULL) sim_interval = 0; /* force check */
else reason = STOP_WAIT;
continue; }
if (PC == ibkpt_addr) { /* breakpoint? */
save_ibkpt = ibkpt_addr; /* save address */
ibkpt_addr = ibkpt_addr | ILL_ADR_FLAG; /* disable */
sim_activate (&cpu_unit, 1); /* sched re-enable */
reason = STOP_IBKPT; /* stop simulation */
break; }
sim_interval = sim_interval - 1;
t = ReadW (PC); /* fetch instr */
PC = (PC + 2) & AMASK; /* increment PC */
OP = (t >> 8) & 0xFF; /* isolate op, R1, R2 */
R1 = (t >> 4) & 0xF;
R2 = t & 0xF;
if (OP & OP_4B) { /* RX or RS? */
EA = ReadW (PC); /* fetch address */
PC = (PC + 2) & AMASK; /* increment PC */
if (R2) EA = (EA + R[R2]) & AMASK; /* index calculation */
}
else EA = R[R2]; /* RR? "EA" = reg content */
switch (OP) { /* case on opcode */
/* Load/store instructions */
case 0x48: /* LH */
EA = ReadW (EA); /* fetch operand */
case 0x08: /* LHR */
case 0xC8: /* LHI */
R[R1] = EA; /* load operand */
CC_GL (R[R1]); /* set G,L */
break;
case 0x40: /* STH */
WriteW (EA, R[R1]); /* store register */
break;
case 0xD1: /* LM */
for ( ; R1 <= 0xF; R1++) { /* loop thru reg */
R[R1] = ReadW (EA); /* load register */
EA = (EA + 2) & AMASK; } /* incr mem addr */
break;
case 0xD0: /* STM */
for ( ; R1 <= 0xF; R1++) { /* loop thru reg */
WriteW (EA, R[R1]); /* store register */
EA = (EA + 2) & AMASK; } /* incr mem addr */
break;
case 0x93: /* LDBR */
R[R1] = R[R2] & 0xFF; /* load byte */
break;
case 0xD3: /* LDB */
R[R1] = ReadB (EA); /* load byte */
break;
case 0x92: /* STBR */
R[R2] = (R[R2] & ~0xFF) | (R[R1] & 0xFF); /* store byte */
break;
case 0xD2: /* STB */
WriteB (EA, R[R1] & 0xFF); /* store byte */
break;
/* Control instructions */
case 0x01: /* BALR */
case 0x41: /* BAL */
old_PC = R[R1] = PC; /* save cur PC */
PC = EA; /* branch */
break;
case 0x02: /* BTCR */
case 0x42: /* BTC */
if (CC & R1) { /* test CC's */
old_PC = PC; /* branch if true */
PC = EA; }
break;
case 0x03: /* BFCR */
case 0x43: /* BFC */
if ((CC & R1) == 0) { /* test CC's */
old_PC = PC; /* branch if false */
PC = EA; }
break;
case 0xC0: /* BXH */
inc = R[(R1 + 1) & 0xF]; /* inc = R1 + 1 */
lim = R[(R1 + 2) & 0xF]; /* lim = R1 + 2 */
R[R1] = (R[R1] + inc) & DMASK; /* or -? */
if (R[R1] > lim) { /* if R1 > lim */
old_PC = PC; /* branch */
PC = EA; }
break;
case 0xC1: /* BXLE */
inc = R[(R1 + 1) & 0xF]; /* inc = R1 + 1 */
lim = R[(R1 + 2) & 0xF]; /* lim = R1 + 2 */
R[R1] = (R[R1] + inc) & DMASK; /* R1 = R1 + inc */
if (R[R1] <= lim) { /* if R1 <= lim */
old_PC = PC; /* branch */
PC = EA; }
break;
case 0xC2: /* LPSW */
old_PC = PC; /* effective branch */
PSW = ReadW (EA); /* read PSW/CC */
CC = PSW & CC_MASK; /* separate CC */
PC = ReadW ((EA + 2) & AMASK); /* read PC */
break;
/* Logical and shift instructions */
case 0x44: /* NH */
EA = ReadW (EA); /* fetch operand */
case 0x04: /* NHR */
case 0xC4: /* NHI */
R[R1] = R[R1] & EA; /* result */
CC_GL (R[R1]); /* set G,L */
break;
case 0x46: /* OH */
EA = ReadW (EA); /* fetch operand */
case 0x06: /* OHR */
case 0xC6: /* OHI */
R[R1] = R[R1] | EA; /* result */
CC_GL (R[R1]); /* set G,L */
break;
case 0x47: /* XH */
EA = ReadW (EA); /* fetch operand */
case 0x07: /* XHR */
case 0xC7: /* XHI */
R[R1] = R[R1] ^ EA; /* result */
CC_GL (R[R1]); /* set G,L */
break;
case 0xCC: /* SRHL */
t = EA & 0xF; /* shift count */
r = R[R1] >> t; /* result */
CC_GL (r); /* set G,L */
if (t && ((R[R1] >> (t - 1)) & 1)) CC = CC | CC_C;
R[R1] = r; /* store result */
break;
case 0xCD: /* SLHL */
t = EA & 0xF; /* shift count */
r = R[R1] << t; /* result */
R[R1] = r & DMASK; /* store masked result */
CC_GL (R[R1]); /* set G,L */
if (t && (r & 0x10000)) CC = CC_C; /* set C if shft out */
break;
case 0xCE: /* SRHA */
t = EA & 0xF; /* shift count */
r = (SIGN_EXT (R[R1]) >> t) & DMASK; /* result */
CC_GL (r); /* set G,L */
if (t && ((R[R1] >> (t - 1)) & 1)) CC = CC | CC_C;
R[R1] = r; /* store result */
break;
case 0xCF: /* SLHA */
t = EA & 0xF; /* shift count */
r = R[R1] << t; /* raw result */
R[R1] = (R[R1] & SIGN) | (r & MAGMASK); /* arith result */
CC_GL (R[R1]); /* set G,L */
if (t && (r & SIGN)) CC = CC | CC_C; /* set C if shft out */
break;
/* Arithmetic instructions */
case 0x45: /* CLH */
EA = ReadW (EA); /* fetch operand */
case 0x05: /* CLHR */
case 0xC5: /* CLHI */
r = (R[R1] - EA) & DMASK; /* result */
CC_GL (r); /* set G,L */
if (R[R1] < EA) CC = CC | CC_C; /* set C if borrow */
if (((R[R1] ^ EA) & (~R[R1] ^ r)) & SIGN) CC = CC | CC_V;
break;
case 0x4A: /* AH */
EA = ReadW (EA); /* fetch operand */
case 0x0A: /* AHR */
case 0xCA: /* AHI */
r = (R[R1] + EA) & DMASK; /* result */
CC_GL (r); /* set G,L */
if (r < EA) CC = CC | CC_C; /* set C if carry */
if (((~R[R1] ^ EA) & (R[R1] ^ r)) & SIGN) CC = CC | CC_V;
R[R1] = r;
break;
case 0x4B: /* SH */
EA = ReadW (EA); /* fetch operand */
case 0x0B: /* SHR */
case 0xCB: /* SHI */
r = (R[R1] - EA) & DMASK; /* result */
CC_GL (r); /* set G,L */
if (R[R1] < EA) CC = CC | CC_C; /* set C if borrow */
if (((R[R1] ^ EA) & (~R[R1] ^ r)) & SIGN) CC = CC | CC_V;
R[R1] = r;
break;
case 0x4C: /* MH */
EA = ReadW (EA); /* fetch operand */
case 0x0C: /* MHR */
r = SIGN_EXT (R[R1 | 1]) * SIGN_EXT (EA); /* multiply */
R[R1] = (r >> 16) & DMASK; /* high result */
R[R1 | 1] = r & DMASK; /* low result */
break;
case 0x4D: /* DH */
EA = ReadW (EA); /* fetch operand */
case 0x0D: /* DHR */
r = (SIGN_EXT (R[R1]) << 16) | R[R1 | 1]; /* form 32b divd */
if (EA) { /* if divisor != 0 */
t = r / SIGN_EXT (EA); /* quotient */
r = r % SIGN_EXT (EA); } /* remainder */
if (EA && ((t < 0x8000) || (t >= -0x8000))) { /* if quo fits */
R[R1] = r; /* store remainder */
R[R1 | 1] = t; } /* store quotient */
else if (PSW & PSW_DFI) { /* div fault enabled? */
PSW_SWAP (IDOPSW, IDNPSW); } /* swap PSW */
break;
case 0x4E: /* ACH */
EA = ReadW (EA); /* fetch operand */
case 0x0E: /* ACHR */
t = R[R1] + EA + ((CC & CC_C) != 0); /* raw result */
r = t & 0xFFFF; /* masked result */
CC_GL_C (r); /* set G,L */
if (t > DMASK) CC = CC | CC_C; /* set C if carry */
if (((~R[R1] ^ EA) & (R[R1] ^ r)) & SIGN) CC = CC | CC_V;
R[R1] = r; /* store result */
break;
case 0x4F: /* SCH */
EA = ReadW (EA); /* fetch operand */
case 0x0F: /* SCHR */
t = R[R1] - EA - ((CC & CC_C) != 0); /* raw result */
r = t & 0xFFFF; /* masked result */
CC_GL_C (r); /* set G,L */
if (t < 0) CC = CC | CC_C; /* set C if borrow */
if (((R[R1] ^ EA) & (~R[R1] ^ t)) & 0x8000) CC = CC | CC_V;
R[R1] = r; /* store result */
break;
/* Floating point instructions */
case 0x68: /* LE */
case 0x28: /* LER */
CC = le (OP, R1, R2, EA);
break;
case 0x69: /* CE */
case 0x29: /* CER */
CC = ce (OP, R1, R2, EA);
break;
case 0x6A: /* AE */
case 0x6B: /* SE */
case 0x2A: /* AER */
case 0x2B: /* SER */
CC = ase (OP, R1, R2, EA);
break;
case 0x6C: /* ME */
case 0x2C: /* MER */
CC = me (OP, R1, R2, EA);
break;
case 0x6D: /* DE */
case 0x2D: /* DER */
t = de (OP, R1, R2, EA); /* perform divide */
if (t >= 0) CC = t; /* if ok, set CC */
else if (PSW & PSW_FDI) { /* else fault */
PSW_SWAP (FDOPSW, FDNPSW); } /* swap PSW */
break;
case 0x60: /* STE */
WriteW ((F[R1 >> 1] >> 16) & DMASK, EA);
WriteW (F[R1 >> 1] & DMASK, ((EA + 2) & AMASK));
break;
/* I/O instructions */
case 0xDE: /* OC */
EA = ReadW (EA); /* fetch operand */
case 0x9E: /* OCR */
dev = R[R1] & DEV_MAX;
if (dev_tab[dev]) {
dev_tab[dev] (IO_ADR, EA); /* select */
t = dev_tab[dev] (IO_OC, EA); /* send command */
qanyin = int_eval (); /* re-eval intr */
if (t & IOT_EXM) CC = CC_V; /* set V if err */
else CC = 0;
reason = t >> IOT_V_REASON; }
else CC = CC_V;
break;
case 0xDA: /* WD */
EA = ReadW (EA); /* fetch operand */
case 0x9A: /* WDR */
dev = R[R1] & DEV_MAX;
if (dev_tab[dev]) {
dev_tab[dev] (IO_ADR, EA); /* select */
t = dev_tab[dev] (IO_WD, EA); /* send data */
qanyin = int_eval (); /* re-eval intr */
if (t & IOT_EXM) CC = CC_V; /* set V if err */
else CC = 0;
reason = t >> IOT_V_REASON; }
else CC = CC_V;
break;
case 0xD6: /* WB */
case 0x96: /* WBR */
dev = R[R1] & DEV_MAX;
if (OP & OP_4B) {
EA = ReadW (EA); /* start */
lim = ReadW ((EA + 2) & 0xFFFF); } /* end */
else lim = R[(R2 + 1) & 0xF];
if (dev_tab[dev]) {
dev_tab[dev] (IO_ADR, EA); /* select */
for ( ; EA <= lim; EA = ((EA + 1) & 0xFFFF)) {
t = dev_tab[dev] (IO_WD, ReadB (EA));
if (reason = t >> IOT_V_REASON) break;
t = dev_tab[dev] (IO_SS, 0);
if (CC = t & 0xF) break; }
qanyin = int_eval (); } /* re-eval intr */
else CC = CC_V;
break;
case 0xDB: /* RD */
case 0x9B: /* RDR */
dev = R[R1] & DEV_MAX;
if (dev_tab[dev]) {
dev_tab[dev] (IO_ADR, EA); /* select */
t = dev_tab[dev] (IO_RD, 0); /* get data */
qanyin = int_eval (); /* re-eval intr */
if (OP & OP_4B) { /* RX or RR? */
WriteB (EA, t & 0xFF); }
else R[R2] = t & 0xFF;
if (t & IOT_EXM) CC = CC_V; /* set V if err */
else CC = 0;
reason = t >> IOT_V_REASON; }
else CC = CC_V;
break;
case 0xD7: /* RB */
case 0x97: /* RBR */
dev = R[R1] & DEV_MAX;
if (OP & OP_4B) {
EA = ReadW (EA); /* start */
lim = ReadW ((EA + 2) & 0xFFFF); } /* end */
else lim = R[(R2 + 1) & 0xF];
if (dev_tab[dev]) {
dev_tab[dev] (IO_ADR, EA); /* select */
for ( ; EA <= lim; EA = ((EA + 1) & 0xFFFF)) {
t = dev_tab[dev] (IO_RD, 0);
WriteB (EA, t & 0xFF);
if (reason = t >> IOT_V_REASON) break;
t = dev_tab[dev] (IO_SS, 0);
if (CC = t & 0xF) break; }
qanyin = int_eval (); } /* re-eval intr */
else CC = CC_V;
break;
case 0xDF: /* AI */
case 0x9F: /* AIR */
for (i = t = 0; i < INTSZ; i++) { /* loop thru array */
uint32 temp;
if (temp = int_req[i] & int_enb[i]) { /* loop thru word */
for (j = 0; j < 32; j++) {
if (temp & INT_V(j)) break;
t = t + 1; } }
else t = t + 32; }
R[R1] = t & DEV_MAX;
CLR_INT (t & DEV_MAX); /* clear int req */
/* fall through */
case 0xDD: /* SS */
case 0x9D: /* SSR */
dev = R[R1] & DEV_MAX;
if (dev_tab[dev]) {
dev_tab[dev] (IO_ADR, EA); /* select */
t = dev_tab[dev] (IO_SS, 0); /* get status */
qanyin = int_eval (); /* re-eval intr */
if (OP & OP_4B) { /* RR or RX? */
WriteB (EA, t & 0xFF); }
else R[R2] = t & 0xFF;
CC = t & 0xF;
reason = t >> IOT_V_REASON; }
else CC = CC_V;
break;
default: /* undefined */
PC = (PC - ((OP & OP_4B)? 4: 2)) & AMASK;
if (reason = stop_inst) break; /* stop on undef? */
PSW_SWAP (ILOPSW, ILNPSW); /* swap PSW */
break; } /* end switch */
} /* end while */
/* Simulation halted */
PSW = BUILD_PSW;
saved_PC = PC & AMASK;
return reason;
}
/* Evaluate interrupt */
t_bool int_eval (void)
{
int i;
for (i = 0; i < INTSZ; i++)
if (int_req[i] & int_enb[i]) return TRUE;
return FALSE;
}
/* Display register device */
int32 display (int32 op, int32 dat)
{
int t;
switch (op) {
case IO_ADR: /* select */
drpos = srpos = 0; /* clear counters */
break;
case IO_OC: /* command */
op = op & 0xC0;
if (op == 0x40) drmod = 1; /* x40 = inc */
else if (op == 0x80) drmod = 0; /* x80 = norm */
else if (op == 0xC0) drmod = drmod ^ 1; /* xC0 = flip */
break;
case IO_WD: /* write */
DR = (DR & ~(0xFF << (drpos * 8))) | (dat << (drpos * 8));
if (drmod) drpos = (drpos + 1) & 0x3;
break;
case IO_RD: /* read */
t = (SR >> (srpos * 8)) & 0xFF;
srpos = srpos ^ 1;
return t;
case IO_SS: /* status */
return 0x80; }
return 0;
}
/* Reset routine */
t_stat cpu_reset (DEVICE *dptr)
{
PSW = 0;
DR = 0;
drmod = 0;
return cpu_svc (&cpu_unit);
}
/* Memory examine */
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
{
if (addr >= MEMSIZE) return SCPE_NXM;
if (vptr != NULL) *vptr = ReadW (addr);
return SCPE_OK;
}
/* Memory deposit */
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
{
if (addr >= MEMSIZE) return SCPE_NXM;
WriteW (addr, val);
return SCPE_OK;
}
/* Breakpoint service */
t_stat cpu_svc (UNIT *uptr)
{
if ((ibkpt_addr & ~ILL_ADR_FLAG) == save_ibkpt) ibkpt_addr = save_ibkpt;
save_ibkpt = -1;
return SCPE_OK;
}
t_stat cpu_set_size (UNIT *uptr, int32 value)
{
int32 mc = 0;
t_addr i;
if ((value <= 0) || (value > MAXMEMSIZE) || ((value & 0xFFF) != 0))
return SCPE_ARG;
for (i = value; i < MEMSIZE; i++) mc = mc | M[i];
if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))
return SCPE_OK;
MEMSIZE = value;
for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0;
return SCPE_OK;
}