| /* 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; | |
| } |