/* hp2100_cpu.c: HP 2100 CPU simulator | |
Copyright (c) 1993-2002, 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. | |
22-Mar-02 RMS Changed to allocate memory array dynamically | |
11-Mar-02 RMS Cleaned up setjmp/auto variable interaction | |
17-Feb-02 RMS Added DMS support | |
Fixed bugs in extended instructions | |
03-Feb-02 RMS Added terminal multiplexor support | |
Changed PCQ macro to use unmodified PC | |
Fixed flop restore logic (found by Bill McDermith) | |
Fixed SZx,SLx,RSS bug (found by Bill McDermith) | |
Added floating point support | |
16-Jan-02 RMS Added additional device support | |
07-Jan-02 RMS Fixed DMA register tables (found by Bill McDermith) | |
07-Dec-01 RMS Revised to use breakpoint package | |
03-Dec-01 RMS Added extended SET/SHOW support | |
10-Aug-01 RMS Removed register in declarations | |
26-Nov-00 RMS Fixed bug in dual device number routine | |
21-Nov-00 RMS Fixed bug in reset routine | |
15-Oct-00 RMS Added dynamic device number support | |
The register state for the HP 2100 CPU is: | |
AR<15:0> A register - addressable as location 0 | |
BR<15:0> B register - addressable as location 1 | |
PC<14:0> P register (program counter) | |
SR<15:0> S register | |
E extend flag (carry out) | |
O overflow flag | |
The 21MX adds a pair of index registers: | |
XR<15:0> X register | |
YR<15:0> Y register | |
The original HP 2116 has four instruction formats: memory reference, | |
shift, alter/skip, and I/O. The HP 2100 added extended memory reference | |
and extended arithmetic. The HP21MX added extended byte, bit, and word | |
instructions as well as extended memory. | |
The memory reference format is: | |
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
|in| op |cp| offset | memory reference | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
<14:11> mnemonic action | |
0010 AND A = A & M[MA] | |
0011 JSB M[MA] = P, P = MA + 1 | |
0100 XOR A = A ^ M[MA] | |
0101 JMP P = MA | |
0110 IOR A = A | M[MA] | |
0111 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 | |
1000 ADA A = A + M[MA] | |
1001 ADB B = B + M[MA] | |
1010 CPA skip if A != M[MA] | |
1011 CPB skip if B != M[MA] | |
1100 LDA A = M[MA] | |
1101 LDB B = M[MA] | |
1110 STA M[MA] = A | |
1111 STB M[MA] = B | |
<15,10> mode action | |
0,0 page zero direct MA = IR<9:0> | |
0,1 current page direct MA = PC<14:0>'IR,9:0> | |
1,0 page zero indirect MA = M[IR<9:0>] | |
1,1 current page indirect MA = M[PC<14:10>'IR<9:0>] | |
Memory reference instructions can access an address space of 32K words. | |
An instruction can directly reference the first 1024 words of memory | |
(called page zero), as well as 1024 words of the current page; it can | |
indirectly access all 32K. | |
*/ | |
/* The shift format is: | |
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| 0 0 0 0|ab| 0|s1| op1 |ce|s2|sl| op2 | shift | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| | \---+---/ | | | \---+---/ | |
| | | | | | | | |
| | | | | | +---- shift 2 opcode | |
| | | | | +---------- skip if low bit == 0 | |
| | | | +------------- shift 2 enable | |
| | | +---------------- clear Extend | |
| | +---------------------- shift 1 opcode | |
| +---------------------------- shift 1 enable | |
+---------------------------------- A/B select | |
The alter/skip format is: | |
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| 0 0 0 0|ab| 1|regop| e op|se|ss|sl|in|sz|rs| alter/skip | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| \-+-/ \-+-/ | | | | | | | |
| | | | | | | | +- reverse skip sense | |
| | | | | | | +---- skip if register == 0 | |
| | | | | | +------- increment register | |
| | | | | +---------- skip if low bit == 0 | |
| | | | +------------- skip if sign bit == 0 | |
| | | +---------------- skip if Extend == 0 | |
| | +--------------------- clr/com/set Extend | |
| +--------------------------- clr/com/set register | |
+---------------------------------- A/B select | |
The I/O transfer format is: | |
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| 1 0 0 0|ab| 1|hc| opcode | device | I/O transfer | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| | \---+---/\-------+-------/ | |
| | | | | |
| | | +--------- device select | |
| | +---------------------- opcode | |
| +---------------------------- hold/clear flag | |
+---------------------------------- A/B select | |
The IO transfer instruction controls the specified device. | |
Depending on the opcode, the instruction may set or clear | |
the device flag, start or stop I/O, or read or write data. | |
*/ | |
/* The 2100 added an extended memory reference instruction; | |
the 21MX added extended arithmetic, operate, byte, word, | |
and bit instructions. Note that the HP 21xx is, despite | |
the right-to-left bit numbering, a big endian system. | |
Bits <15:8> are byte 0, and bits <7:0> are byte 1. | |
The extended memory reference format (HP 2100) is: | |
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| 1| 0 0 0|op| 0| opcode | extended mem ref | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
|in| operand address | | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
The extended arithmetic format (HP 2100) is: | |
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| 1| 0 0 0 0 0|dr| 0 0| opcode |shift count| extended arithmetic | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
The extended operate format (HP 21MX) is: | |
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| 1| 0 0 0|op| 0| 1 1 1 1 1| opcode | extended operate | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
The extended byte and word format (HP 21MX) is: | |
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| 1| 0 0 0 1 0 1 1 1 1 1 1| opcode | extended byte/word | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
|in| operand address | | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0| | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
The extended bit operate format (HP 21MX) is: | |
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| 1| 0 0 0 1 0 1 1 1 1 1 1 1| opcode | extended bit operate | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
|in| operand address | | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
|in| operand address | | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
*/ | |
/* This routine is the instruction decode routine for the HP 2100. | |
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 | |
infinite indirection loop | |
unimplemented instruction and stop_inst flag set | |
unknown I/O device and stop_dev flag set | |
I/O error in I/O simulator | |
2. Interrupts. I/O devices are modelled as four parallel arrays: | |
device commands as bit array dev_cmd[2][31..0] | |
device flags as bit array dev_flg[2][31..0] | |
device flag buffers as bit array dev_fbf[2][31..0] | |
device controls as bit array dev_ctl[2][31..0] | |
The HP 2100 interrupt structure is based on flag, flag buffer,. | |
and control. If a device flag is set, the flag buffer is set, | |
the control bit is set, and the device is the highest priority | |
on the interrupt chain, it requests an interrupt. When the | |
interrupt is acknowledged, the flag buffer is cleared, preventing | |
further interrupt requests from that device. The combination of | |
flag and control set blocks interrupts from lower priority devices. | |
Command plays no direct role in interrupts. The command flop | |
tells whether a device is active. It is set by STC and cleared | |
by CLC; it is also cleared when the device flag is set. Simple | |
devices don't need to track command separately from control. | |
3. Non-existent memory. On the HP 2100, 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: | |
hp2100_defs.h add interrupt request definition | |
hp2100_cpu.c add device information table entry | |
hp2100_sys.c add sim_devices table entry | |
*/ | |
#include "hp2100_defs.h" | |
#include <setjmp.h> | |
#define PCQ_SIZE 64 /* must be 2**n */ | |
#define PCQ_MASK (PCQ_SIZE - 1) | |
#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = err_PC | |
#define UNIT_V_MSIZE (UNIT_V_UF) /* dummy mask */ | |
#define UNIT_MSIZE (1 << UNIT_V_MSIZE) | |
#define UNIT_V_2100 (UNIT_V_UF + 1) /* 2100 vs 2116 */ | |
#define UNIT_2100 (1 << UNIT_V_2100) | |
#define UNIT_V_21MX (UNIT_V_UF + 2) /* 21MX vs 2100 */ | |
#define UNIT_21MX (1 << UNIT_V_21MX) | |
#define ABORT(val) longjmp (save_env, (val)) | |
#define DMAR0 1 | |
#define DMAR1 2 | |
#define SEXT(x) (((x) & SIGN)? (((int32) (x)) | ~DMASK): ((int32) (x))) | |
/* Memory protection tests */ | |
#define MP_TEST(x) (CTL (PRO) && ((x) > 1) && ((x) < mfence)) | |
#define MP_TESTJ(x) (CTL (PRO) && ((x) < mfence)) | |
uint16 *M = NULL; /* memory */ | |
int32 saved_AR = 0; /* A register */ | |
int32 saved_BR = 0; /* B register */ | |
int32 PC = 0; /* P register */ | |
int32 SR = 0; /* S register */ | |
int32 XR = 0; /* X register */ | |
int32 YR = 0; /* Y register */ | |
int32 E = 0; /* E register */ | |
int32 O = 0; /* O register */ | |
int32 dev_cmd[2] = { 0 }; /* device command */ | |
int32 dev_ctl[2] = { 0 }; /* device control */ | |
int32 dev_flg[2] = { 0 }; /* device flags */ | |
int32 dev_fbf[2] = { 0 }; /* device flag bufs */ | |
struct DMA dmac[2] = { { 0 }, { 0 } }; /* DMA channels */ | |
int32 ion = 0; /* interrupt enable */ | |
int32 ion_defer = 0; /* interrupt defer */ | |
int32 intaddr = 0; /* interrupt addr */ | |
int32 mfence = 0; /* mem prot fence */ | |
int32 maddr = 0; /* mem prot err addr */ | |
int32 err_PC = 0; /* error PC */ | |
int32 dms_enb = 0; /* dms enable */ | |
int32 dms_ump = 0; /* dms user map */ | |
int32 dms_sr = 0; /* dms status register */ | |
int32 dms_fence = 0; /* dms base page fence */ | |
int32 dms_vr = 0; /* dms violation register */ | |
int32 dms_sma = 0; /* dms saved ma */ | |
int32 dms_map[MAP_NUM * MAP_LNT] = { 0 }; /* dms maps */ | |
int32 ind_max = 16; /* iadr nest limit */ | |
int32 stop_inst = 1; /* stop on ill inst */ | |
int32 stop_dev = 2; /* stop on ill dev */ | |
uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ | |
int32 pcq_p = 0; /* PC queue ptr */ | |
REG *pcq_r = NULL; /* PC queue reg ptr */ | |
jmp_buf save_env; /* abort handler */ | |
extern int32 sim_interval; | |
extern int32 sim_int_char; | |
extern int32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ | |
extern FILE *sim_log; | |
uint8 ReadB (int32 addr); | |
uint8 ReadBA (int32 addr); | |
uint16 ReadW (int32 addr); | |
uint16 ReadWA (int32 addr); | |
uint32 ReadF (int32 addr); | |
uint16 ReadIO (int32 addr, int32 map); | |
void WriteB (int32 addr, int32 dat); | |
void WriteBA (int32 addr, int32 dat); | |
void WriteW (int32 addr, int32 dat); | |
void WriteWA (int32 addr, int32 dat); | |
void WriteIO (int32 addr, int32 dat, int32 map); | |
int32 dms (int32 va, int32 map, int32 prot); | |
uint16 dms_rmap (int32 mapi); | |
void dms_wmap (int32 mapi, int32 dat); | |
void dms_viol (int32 va, int32 st, t_bool io); | |
int32 dms_upd_sr (void); | |
int32 shift (int32 inval, int32 flag, int32 oper); | |
int32 calc_dma (void); | |
int32 calc_int (void); | |
void dma_cycle (int32 chan, int32 map); | |
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 dma0_reset (DEVICE *dptr); | |
t_stat dma1_reset (DEVICE *dptr); | |
t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); | |
t_bool dev_conflict (void); | |
extern int32 f_as (uint32 op, t_bool sub); | |
extern int32 f_mul (uint32 op); | |
extern int32 f_div (uint32 op); | |
extern int32 f_fix (void); | |
extern void f_flt (void); | |
/* 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 (NULL, UNIT_FIX + UNIT_BINK, VASIZE) }; | |
REG cpu_reg[] = { | |
{ ORDATA (P, PC, 15) }, | |
{ ORDATA (A, saved_AR, 16) }, | |
{ ORDATA (B, saved_BR, 16) }, | |
{ ORDATA (X, XR, 16) }, | |
{ ORDATA (Y, YR, 16) }, | |
{ ORDATA (S, SR, 16) }, | |
{ FLDATA (E, E, 0) }, | |
{ FLDATA (O, O, 0) }, | |
{ FLDATA (ION, ion, 0) }, | |
{ FLDATA (ION_DEFER, ion_defer, 0) }, | |
{ ORDATA (IADDR, intaddr, 6) }, | |
{ FLDATA (MPCTL, dev_ctl[PRO/32], INT_V (PRO)) }, | |
{ FLDATA (MPFLG, dev_flg[PRO/32], INT_V (PRO)) }, | |
{ FLDATA (MPFBF, dev_fbf[PRO/32], INT_V (PRO)) }, | |
{ ORDATA (MFENCE, mfence, 15) }, | |
{ ORDATA (MADDR, maddr, 16) }, | |
{ FLDATA (DMSENB, dms_enb, 0) }, | |
{ FLDATA (DMSCUR, dms_ump, VA_N_PAG) }, | |
{ ORDATA (DMSSR, dms_sr, 16) }, | |
{ ORDATA (DMSVR, dms_vr, 16) }, | |
{ ORDATA (DMSSMA, dms_sma, 15), REG_HIDDEN }, | |
{ BRDATA (DMSMAP, dms_map, 8, PA_N_SIZE, MAP_NUM * MAP_LNT) }, | |
{ FLDATA (STOP_INST, stop_inst, 0) }, | |
{ FLDATA (STOP_DEV, stop_dev, 1) }, | |
{ DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT }, | |
{ BRDATA (PCQ, pcq, 8, 15, PCQ_SIZE), REG_RO+REG_CIRC }, | |
{ ORDATA (PCQP, pcq_p, 6), REG_HRO }, | |
{ ORDATA (WRU, sim_int_char, 8) }, | |
{ FLDATA (T2100, cpu_unit.flags, UNIT_V_2100), REG_HRO }, | |
{ FLDATA (T21MX, cpu_unit.flags, UNIT_V_21MX), REG_HRO }, | |
{ ORDATA (HCMD, dev_cmd[0], 32), REG_HRO }, | |
{ ORDATA (LCMD, dev_cmd[1], 32), REG_HRO }, | |
{ ORDATA (HCTL, dev_ctl[0], 32), REG_HRO }, | |
{ ORDATA (LCTL, dev_ctl[1], 32), REG_HRO }, | |
{ ORDATA (HFLG, dev_flg[0], 32), REG_HRO }, | |
{ ORDATA (LFLG, dev_flg[1], 32), REG_HRO }, | |
{ ORDATA (HFBF, dev_fbf[0], 32), REG_HRO }, | |
{ ORDATA (LFBF, dev_fbf[1], 32), REG_HRO }, | |
{ NULL } }; | |
MTAB cpu_mod[] = { | |
{ UNIT_2100 + UNIT_21MX, 0, "2116", "2116", NULL }, | |
{ UNIT_2100 + UNIT_21MX, UNIT_2100, "2100", "2100", NULL }, | |
{ UNIT_2100 + UNIT_21MX, UNIT_21MX, "21MX", "21MX", NULL }, | |
{ UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, | |
{ UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, | |
{ UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, | |
{ UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, | |
{ UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, | |
{ UNIT_MSIZE, 131072, NULL, "128K", &cpu_set_size }, | |
{ UNIT_MSIZE, 262144, NULL, "256K", &cpu_set_size }, | |
{ UNIT_MSIZE, 524288, NULL, "512K", &cpu_set_size }, | |
{ UNIT_MSIZE, 1048576, NULL, "1024K", &cpu_set_size }, | |
{ 0 } }; | |
DEVICE cpu_dev = { | |
"CPU", &cpu_unit, cpu_reg, cpu_mod, | |
1, 8, 15, 1, 8, 16, | |
&cpu_ex, &cpu_dep, &cpu_reset, | |
NULL, NULL, NULL }; | |
/* DMA controller data structures | |
dmax_dev DMAx device descriptor | |
dmax_reg DMAx register list | |
*/ | |
UNIT dma0_unit = { UDATA (NULL, 0, 0) }; | |
REG dma0_reg[] = { | |
{ FLDATA (CMD, dev_cmd[DMA0/32], INT_V (DMA0)) }, | |
{ FLDATA (CTL, dev_ctl[DMA0/32], INT_V (DMA0)) }, | |
{ FLDATA (FLG, dev_flg[DMA0/32], INT_V (DMA0)) }, | |
{ FLDATA (FBF, dev_fbf[DMA0/32], INT_V (DMA0)) }, | |
{ ORDATA (CW1, dmac[0].cw1, 16) }, | |
{ ORDATA (CW2, dmac[0].cw2, 16) }, | |
{ ORDATA (CW3, dmac[0].cw3, 16) }, | |
{ NULL } }; | |
DEVICE dma0_dev = { | |
"DMA0", &dma0_unit, dma0_reg, NULL, | |
1, 8, 1, 1, 8, 16, | |
NULL, NULL, &dma0_reset, | |
NULL, NULL, NULL }; | |
UNIT dma1_unit = { UDATA (NULL, 0, 0) }; | |
REG dma1_reg[] = { | |
{ FLDATA (CMD, dev_cmd[DMA1/32], INT_V (DMA1)) }, | |
{ FLDATA (CTL, dev_ctl[DMA1/32], INT_V (DMA1)) }, | |
{ FLDATA (FLG, dev_flg[DMA1/32], INT_V (DMA1)) }, | |
{ FLDATA (FBF, dev_fbf[DMA1/32], INT_V (DMA1)) }, | |
{ ORDATA (CW1, dmac[1].cw1, 16) }, | |
{ ORDATA (CW2, dmac[1].cw2, 16) }, | |
{ ORDATA (CW3, dmac[1].cw3, 16) }, | |
{ NULL } }; | |
DEVICE dma1_dev = { | |
"DMA1", &dma1_unit, dma1_reg, NULL, | |
1, 8, 1, 1, 8, 16, | |
NULL, NULL, &dma1_reset, | |
NULL, NULL, NULL }; | |
/* Extended instruction decode tables */ | |
static const uint8 ext_addr[192] = { /* ext inst format */ | |
0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, /* 1: 2 word inst */ | |
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | |
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | |
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | |
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | |
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | |
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | |
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | |
0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, | |
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | |
1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, | |
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; | |
static const uint8 exg_breq[64] = { /* ext grp B only */ | |
1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1, /* 1: <11> must be 1 */ | |
1,1,0,1,0,0,0,0,0,0,1,1,1,1,1,1, | |
0,0,0,1,0,1,1,0,0,0,0,1,0,1,1,0, | |
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 }; | |
static const uint8 exg_addr[64] = { /* ext grp format */ | |
0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,0, /* 1: 2 word inst */ | |
0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,1, /* 2: 3 word with count */ | |
1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,0, /* 3: 3 word inst */ | |
0,0,1,0,0,2,2,0,0,0,1,3,3,3,2,2 }; | |
/* Interrupt defer table */ | |
static const int32 defer_tab[] = { 0, 1, 1, 1, 0, 0, 0, 1 }; | |
/* Device dispatch table */ | |
int32 devdisp (int32 devno, int32 inst, int32 IR, int32 outdat); | |
int32 cpuio (int32 op, int32 IR, int32 outdat); | |
int32 ovfio (int32 op, int32 IR, int32 outdat); | |
int32 pwrio (int32 op, int32 IR, int32 outdat); | |
int32 proio (int32 op, int32 IR, int32 outdat); | |
int32 dmsio (int32 op, int32 IR, int32 outdat); | |
int32 dmpio (int32 op, int32 IR, int32 outdat); | |
int32 nulio (int32 op, int32 IR, int32 outdat); | |
int32 (*dtab[64])() = { | |
&cpuio, &ovfio, &dmsio, &dmsio, &pwrio, &proio, &dmpio, &dmpio, | |
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; | |
/* Device information blocks */ | |
extern DIB ptr_dib, ptp_dib; | |
extern DIB tty_dib; | |
extern DIB clk_dib; | |
extern DIB lpt_dib; | |
extern DIB dp_dib[]; | |
extern DIB dq_dib[]; | |
extern DIB dr_dib[]; | |
extern DIB mt_dib[]; | |
extern DIB ms_dib[]; | |
extern DIB mux_dib[]; | |
extern DIB muxc_dib; | |
DIB *dib_tab[] = { | |
&ptr_dib, | |
&ptp_dib, | |
&tty_dib, | |
&clk_dib, | |
&lpt_dib, | |
&dp_dib[0], | |
&dp_dib[1], | |
&dq_dib[0], | |
&dq_dib[1], | |
&dr_dib[0], | |
&dr_dib[1], | |
&mt_dib[0], | |
&mt_dib[1], | |
&ms_dib[0], | |
&ms_dib[1], | |
&mux_dib[0], | |
&mux_dib[1], | |
&muxc_dib, | |
NULL }; | |
t_stat sim_instr (void) | |
{ | |
int32 intrq, dmarq; /* set after setjmp */ | |
t_stat reason; /* set after setjmp */ | |
int32 i, dev; /* temp */ | |
DIB *dibp; /* temp */ | |
int abortval; | |
/* Restore register state */ | |
if (dev_conflict ()) return SCPE_STOP; /* check consistency */ | |
AR = saved_AR & DMASK; /* restore reg */ | |
BR = saved_BR & DMASK; | |
dms_fence = dms_sr & MST_FENCE; /* separate fence */ | |
err_PC = PC = PC & VAMASK; /* load local PC */ | |
reason = 0; | |
/* Restore I/O state */ | |
for (i = VARDEV; i <= DEVMASK; i++) dtab[i] = NULL; /* clr disp table */ | |
dev_cmd[0] = dev_cmd[0] & M_FXDEV; /* clear dynamic info */ | |
dev_ctl[0] = dev_ctl[0] & M_FXDEV; | |
dev_flg[0] = dev_flg[0] & M_FXDEV; | |
dev_fbf[0] = dev_fbf[0] & M_FXDEV; | |
dev_cmd[1] = dev_ctl[1] = dev_flg[1] = dev_fbf[1] = 0; | |
for (i = 0; dibp = dib_tab[i]; i++) { /* loop thru dev */ | |
if (dibp -> enb) { /* enabled? */ | |
dev = dibp -> devno; /* get dev # */ | |
if (dibp -> cmd) { setCMD (dev); } /* restore cmd */ | |
if (dibp -> ctl) { setCTL (dev); } /* restore ctl */ | |
if (dibp -> flg) { setFLG (dev); } /* restore flg */ | |
clrFBF (dev); /* also sets fbf */ | |
if (dibp -> fbf) { setFBF (dev); } /* restore fbf */ | |
dtab[dev] = dibp -> iot; } } /* set I/O dispatch */ | |
/* Abort handling | |
If an abort occurs in memory protection, the relocation routine | |
executes a longjmp to this area OUTSIDE the main simulation loop. | |
Memory protection errors are the only sources of aborts in the | |
HP 2100. All referenced variables must be globals, and all sim_instr | |
scoped automatics must be set after the setjmp. | |
*/ | |
abortval = setjmp (save_env); /* set abort hdlr */ | |
if (abortval != 0) { /* mem mgt abort? */ | |
if (abortval > 0) { setFLG (PRO); } /* dms abort? */ | |
else maddr = err_PC | 0100000; /* mprot abort */ | |
intrq = PRO; } /* protection intr */ | |
dmarq = calc_dma (); /* recalc DMA masks */ | |
intrq = calc_int (); /* recalc interrupts */ | |
/* Main instruction fetch/decode loop */ | |
while (reason == 0) { /* loop until halted */ | |
int32 IR, MA, absel, i, dev, t, opnd; | |
int32 M1, iodata, op, sc, q, r, wc; | |
int32 mapi, mapj; | |
uint32 fop; | |
if (sim_interval <= 0) { /* check clock queue */ | |
if (reason = sim_process_event ()) break; | |
dmarq = calc_dma (); /* recalc DMA reqs */ | |
intrq = calc_int (); } /* recalc interrupts */ | |
if (dmarq) { | |
if (dmarq & DMAR0) dma_cycle (0, PAMAP); /* DMA1 cycle? */ | |
if (dmarq & DMAR1) dma_cycle (1, PBMAP); /* DMA2 cycle? */ | |
dmarq = calc_dma (); /* recalc DMA reqs */ | |
intrq = calc_int (); } /* recalc interrupts */ | |
if (intrq && ((intrq <= PRO) || !ion_defer)) { /* interrupt request? */ | |
clrFBF (intrq); /* clear flag buffer */ | |
intaddr = intrq; /* save int addr */ | |
err_PC = PC; /* save PC for error */ | |
if (dms_enb) dms_sr = dms_sr | MST_ENBI; /* dms enabled? */ | |
else dms_sr = dms_sr & ~MST_ENBI; | |
if (dms_ump) { /* user map? */ | |
dms_sr = dms_sr | MST_UMPI; | |
dms_ump = 0; } /* switch to system */ | |
else dms_sr = dms_sr & ~MST_UMPI; | |
IR = ReadW (intrq); /* get dispatch instr */ | |
ion_defer = 1; /* defer interrupts */ | |
intrq = 0; /* clear request */ | |
clrCTL (PRO); } /* protection off */ | |
else { if (sim_brk_summ && | |
sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ | |
reason = STOP_IBKPT; /* stop simulation */ | |
break; } | |
err_PC = PC; /* save PC for error */ | |
IR = ReadW (PC); /* fetch instr */ | |
PC = (PC + 1) & VAMASK; | |
sim_interval = sim_interval - 1; | |
ion_defer = 0; } | |
absel = (IR & AB)? 1: 0; /* get A/B select */ | |
/* Memory reference instructions */ | |
if (IR & MROP) { /* mem ref? */ | |
MA = IR & (IA | DISP); /* ind + disp */ | |
if (IR & CP) MA = ((PC - 1) & PAGENO) | MA; /* current page? */ | |
for (i = 0; (i < ind_max) && (MA & IA); i++) /* resolve multi- */ | |
MA = ReadW (MA & VAMASK); /* level indirect */ | |
if (i >= ind_max) { /* indirect loop? */ | |
reason = STOP_IND; | |
break; } | |
switch ((IR >> 11) & 017) { /* decode IR<14:11> */ | |
case 002: /* AND */ | |
AR = AR & ReadW (MA); | |
break; | |
case 003: /* JSB */ | |
WriteW (MA, PC); | |
PCQ_ENTRY; | |
PC = (MA + 1) & VAMASK; | |
if (IR & IA) ion_defer = 1; | |
break; | |
case 004: /* XOR */ | |
AR = AR ^ ReadW (MA); | |
break; | |
case 005: /* JMP */ | |
if (MP_TESTJ (MA)) ABORT (ABORT_FENCE); | |
PCQ_ENTRY; | |
PC = MA; | |
if (IR & IA) ion_defer = 1; | |
break; | |
case 006: /* IOR */ | |
AR = AR | ReadW (MA); | |
break; | |
case 007: /* ISZ */ | |
t = (ReadW (MA) + 1) & DMASK; | |
WriteW (MA, t); | |
if (t == 0) PC = (PC + 1) & VAMASK; | |
break; | |
/* Memory reference instructions, continued */ | |
case 010: /* ADA */ | |
opnd = (int32) ReadW (MA); | |
t = (int32) AR + opnd; | |
if (t > DMASK) E = 1; | |
if (((~AR ^ opnd) & (AR ^ t)) & SIGN) O = 1; | |
AR = t & DMASK; | |
break; | |
case 011: /* ADB */ | |
opnd = (int32) ReadW (MA); | |
t = (int32) BR + opnd; | |
if (t > DMASK) E = 1; | |
if (((~BR ^ opnd) & (BR ^ t)) & SIGN) O = 1; | |
BR = t & DMASK; | |
break; | |
case 012: /* CPA */ | |
if (AR != ReadW (MA)) PC = (PC + 1) & VAMASK; | |
break; | |
case 013: /* CPB */ | |
if (BR != ReadW (MA)) PC = (PC + 1) & VAMASK; | |
break; | |
case 014: /* LDA */ | |
AR = ReadW (MA); | |
break; | |
case 015: /* LDB */ | |
BR = ReadW (MA); | |
break; | |
case 016: /* STA */ | |
WriteW (MA, AR); | |
break; | |
case 017: /* STB */ | |
WriteW (MA, BR); | |
break; } /* end case IR */ | |
} /* end if mem ref */ | |
/* Alter/skip instructions */ | |
else if ((IR & NMROP) == ASKP) { /* alter/skip? */ | |
int32 skip = 0; /* no skip */ | |
if (IR & 000400) t = 0; /* CLx */ | |
else t = ABREG[absel]; | |
if (IR & 001000) t = t ^ DMASK; /* CMx */ | |
if (IR & 000001) { /* RSS? */ | |
if ((IR & 000040) && (E != 0)) skip = 1;/* SEZ,RSS */ | |
if (IR & 000100) E = 0; /* CLE */ | |
if (IR & 000200) E = E ^ 1; /* CME */ | |
if (((IR & 000030) == 000030) && /* SSx,SLx,RSS */ | |
((t & 0100001) == 0100001)) skip = 1; | |
if (((IR & 000030) == 000020) && /* SSx,RSS */ | |
((t & SIGN) != 0)) skip = 1; | |
if (((IR & 000030) == 000010) && /* SLx,RSS */ | |
((t & 1) != 0)) skip = 1; | |
if (IR & 000004) { /* INx */ | |
t = (t + 1) & DMASK; | |
if (t == 0) E = 1; | |
if (t == SIGN) O = 1; } | |
if ((IR & 000002) && (t != 0)) skip = 1;/* SZx,RSS */ | |
if ((IR & 000072) == 0) skip = 1; /* RSS */ | |
} /* end if RSS */ | |
else { if ((IR & 000040) && (E == 0)) skip = 1;/* SEZ */ | |
if (IR & 000100) E = 0; /* CLE */ | |
if (IR & 000200) E = E ^ 1; /* CME */ | |
if ((IR & 000020) && /* SSx */ | |
((t & SIGN) == 0)) skip = 1; | |
if ((IR & 000010) && /* SLx */ | |
((t & 1) == 0)) skip = 1; | |
if (IR & 000004) { /* INx */ | |
t = (t + 1) & DMASK; | |
if (t == 0) E = 1; | |
if (t == SIGN) O = 1; } | |
if ((IR & 000002) && (t == 0)) skip = 1;/* SZx */ | |
} /* end if ~RSS */ | |
ABREG[absel] = t; /* store result */ | |
PC = (PC + skip) & VAMASK; /* add in skip */ | |
} /* end if alter/skip */ | |
/* Shift instructions */ | |
else if ((IR & NMROP) == SHFT) { /* shift? */ | |
t = shift (ABREG[absel], IR & 01000, IR >> 6); /* do first shift */ | |
if (IR & 000040) E = 0; /* CLE */ | |
if ((IR & 000010) && ((t & 1) == 0)) /* SLx */ | |
PC = (PC + 1) & VAMASK; | |
ABREG[absel] = shift (t, IR & 00020, IR); /* do second shift */ | |
} /* end if shift */ | |
/* I/O instructions */ | |
else if ((IR & NMROP) == IOT) { /* I/O? */ | |
dev = IR & DEVMASK; /* get device */ | |
t = (IR >> 6) & 07; /* get subopcode */ | |
if (CTL (PRO) && ((t == ioHLT) || (dev != OVF))) { | |
ABORT (ABORT_FENCE); } | |
iodata = devdisp (dev, t, IR, ABREG[absel]); /* process I/O */ | |
if ((t == ioMIX) || (t == ioLIX)) /* store ret data */ | |
ABREG[absel] = iodata & DMASK; | |
if (t == ioHLT) reason = STOP_HALT; | |
else reason = iodata >> IOT_V_REASON; | |
ion_defer = defer_tab[t]; /* set defer */ | |
dmarq = calc_dma (); /* recalc DMA */ | |
intrq = calc_int (); /* recalc interrupts */ | |
} /* end if I/O */ | |
/* Extended instructions */ | |
else if (cpu_unit.flags & (UNIT_2100 | UNIT_21MX)) { /* ext instr? */ | |
int32 awc; | |
op = (IR >> 4) & 0277; /* get opcode */ | |
if (ext_addr[op]) { /* extended mem ref? */ | |
MA = ReadW (PC); /* get next address */ | |
PC = (PC + 1) & VAMASK; | |
for (i = 0; (i < ind_max) && (MA & IA); i++) | |
MA = ReadW (MA & VAMASK); | |
if (i >= ind_max) { | |
reason = STOP_IND; | |
break; } } | |
sc = (IR & 017); /* get shift count */ | |
if (sc == 0) sc = 16; | |
switch (op) { /* decode IR<11:4> */ | |
case 0010: /* MUL */ | |
t = SEXT (AR) * SEXT (ReadW (MA)); | |
BR = (t >> 16) & DMASK; | |
AR = t & DMASK; | |
O = 0; | |
break; | |
case 0020: /* DIV */ | |
t = (SEXT (BR) << 16) | (int32) AR; /* get divd */ | |
opnd = SEXT (ReadW (MA)); /* get divisor */ | |
if ((opnd == 0) || /* divide by zero? */ | |
((t == SIGN32) && (opnd == -1)) || /* -2**32 / -1? */ | |
((q = t / opnd) > 077777) || /* quo too big? */ | |
(q < -0100000)) { /* quo too small? */ | |
O = 1; /* set overflow */ | |
if (BR & SIGN) { /* divd negative? */ | |
BR = (-BR) & DMASK; /* make B'A pos */ | |
AR = (~AR + (BR == 0)) & DMASK; } | |
} /* end if div fail */ | |
else { AR = q & DMASK; /* set quo, rem */ | |
BR = (t % opnd) & DMASK; | |
O = 0; } /* end else ok */ | |
break; | |
case 0210: /* DLD */ | |
AR = ReadW (MA); | |
MA = (MA + 1) & VAMASK; | |
BR = ReadW (MA); | |
break; | |
case 0220: /* DST */ | |
WriteW (MA, AR); | |
MA = (MA + 1) & VAMASK; | |
WriteW (MA, BR); | |
break; | |
/* Extended arithmetic instructions */ | |
case 0001: /* ASL */ | |
t = (SEXT (BR) >> (16 - sc)) & DMASK; | |
if (t != ((BR & SIGN)? DMASK: 0)) O = 1; | |
else O = 0; | |
BR = (BR & SIGN) || ((BR << sc) & 077777) || | |
(AR >> (16 - sc)); | |
AR = (AR << sc) & DMASK; | |
break; | |
case 0002: /* LSL */ | |
BR = ((BR << sc) | (AR >> (16 - sc))) & DMASK; | |
AR = (AR << sc) & DMASK; | |
break; | |
case 0004: /* RRL */ | |
t = BR; | |
BR = ((BR << sc) | (AR >> (16 - sc))) & DMASK; | |
AR = ((AR << sc) | (t >> (16 - sc))) & DMASK; | |
break; | |
case 0041: /* ASR */ | |
AR = ((BR << (16 - sc)) | (AR >> sc)) & DMASK; | |
BR = (SEXT (BR) >> sc) & DMASK; | |
O = 0; | |
break; | |
case 0042: /* LSR */ | |
AR = ((BR << (16 - sc)) | (AR >> sc)) & DMASK; | |
BR = BR >> sc; | |
break; | |
case 0044: /* RRR */ | |
t = AR; | |
AR = ((AR >> sc) | (BR << (16 - sc))) & DMASK; | |
BR = ((BR >> sc) | (t << (16 - sc))) & DMASK; | |
break; | |
/* Floating point instructions */ | |
case 0240: /* FAD */ | |
fop = ReadF (MA); /* get fop */ | |
O = O | f_as (fop, 0); /* add */ | |
break; | |
case 0241: /* FSB */ | |
fop = ReadF (MA); /* get fop */ | |
O = O | f_as (fop, 1); /* subtract */ | |
break; | |
case 0242: /* FMP */ | |
fop = ReadF (MA); /* get fop */ | |
O = O | f_mul (fop); /* multiply */ | |
break; | |
case 0243: /* FDV */ | |
fop = ReadF (MA); /* get fop */ | |
O = O | f_div (fop); /* divide */ | |
break; | |
case 0244: /* FIX */ | |
O = O | f_fix (); /* fix */ | |
break; | |
case 0245: /* FLT */ | |
f_flt (); /* float */ | |
break; | |
/* Extended instruction group, including DMS */ | |
case 0074: case 0075: /* DMS inst grp, A */ | |
case 0076: case 0077: /* ext inst grp, A */ | |
if (exg_breq[IR & 077]) { /* must have B set? */ | |
reason = stop_inst; | |
break; } | |
case 0274: case 0275: /* DMS inst grp, B */ | |
case 0276: case 0277: /* ext inst grp, B */ | |
if ((cpu_unit.flags & UNIT_21MX) == 0) { | |
reason = stop_inst; | |
break; } | |
op = IR & 077; /* get sub opcode */ | |
if (exg_addr[op]) { /* mem addr? */ | |
MA = ReadW (PC); /* get next address */ | |
PC = (PC + 1) & VAMASK; | |
for (i = 0; (i < ind_max) && (MA & IA); i++) | |
MA = ReadW (MA & VAMASK); | |
if (i >= ind_max) { | |
reason = STOP_IND; | |
break; } } | |
if (exg_addr[op] == 2) { /* word of zero? */ | |
wc = ReadW (MA); /* get count */ | |
if (ReadW (PC) == 0) WriteW (PC, wc); | |
awc = PC; /* and addr */ | |
PC = (PC + 1) & VAMASK; } | |
if (exg_addr[op] == 3) { /* second address? */ | |
M1 = ReadW (PC); /* get next address */ | |
PC = (PC + 1) & VAMASK; | |
for (i = 0; (i < ind_max) && (M1 & IA); i++) | |
M1 = ReadW (M1 & VAMASK); | |
if (i >= ind_max) { | |
reason = STOP_IND; | |
break; } } | |
switch (op) { /* case on sub op */ | |
/* Extended instruction group: DMS */ | |
case 002: /* MBI */ | |
dms_viol (err_PC, MVI_PRV, 0); /* priv if PRO */ | |
if (XR == 0) break; /* nop if X = 0 */ | |
AR = AR & ~1; /* force A, B even */ | |
BR = BR & ~1; | |
for ( ; XR == 0; XR--) { /* loop */ | |
t = ReadB (AR); /* read curr */ | |
WriteBA (BR, t); /* write alt */ | |
AR = (AR + 1) & DMASK; /* inc ptrs */ | |
BR = (BR + 1) & DMASK; } | |
break; | |
case 003: /* MBF */ | |
if (XR == 0) break; | |
AR = AR & ~1; /* force A, B even */ | |
BR = BR & ~1; | |
for ( ; XR == 0; XR--) { | |
t = ReadBA (AR); | |
WriteB (BR, t); | |
AR = (AR + 1) & DMASK; | |
BR = (BR + 1) & DMASK; } | |
break; | |
case 004: /* MBW */ | |
dms_viol (err_PC, MVI_PRV, 0); | |
if (XR == 0) break; | |
AR = AR & ~1; /* force A, B even */ | |
BR = BR & ~1; | |
for ( ; XR == 0; XR--) { | |
t = ReadBA (AR); | |
WriteBA (BR, t); | |
AR = (AR + 1) & DMASK; | |
BR = (BR + 1) & DMASK; } | |
break; | |
case 005: /* MWI */ | |
dms_viol (err_PC, MVI_PRV, 0); | |
if (XR == 0) break; | |
for ( ; XR == 0; XR--) { | |
t = ReadW (AR & VAMASK); | |
WriteWA (BR & VAMASK, t); | |
AR = (AR + 1) & DMASK; | |
BR = (BR + 1) & DMASK; } | |
break; | |
case 006: /* MWF */ | |
if (XR == 0) break; | |
for ( ; XR == 0; XR--) { | |
t = ReadWA (AR & VAMASK); | |
WriteW (BR & VAMASK, t); | |
AR = (AR + 1) & DMASK; | |
BR = (BR + 1) & DMASK; } | |
break; | |
case 007: /* MWW */ | |
dms_viol (err_PC, MVI_PRV, 0); | |
if (XR == 0) break; | |
for ( ; XR == 0; XR--) { | |
t = ReadWA (AR & VAMASK); | |
WriteWA (BR & VAMASK, t); | |
AR = (AR + 1) & DMASK; | |
BR = (BR + 1) & DMASK; } | |
break; | |
/* Extended instruction group: DMS, continued */ | |
case 010: /* SYA, SYB */ | |
case 011: /* USA, USB */ | |
case 012: /* PAA, PAB */ | |
case 013: /* PBA, PBB */ | |
mapi = (op & 03) << VA_N_PAG; /* map base */ | |
if (ABREG[absel] & SIGN) { /* store? */ | |
for (i = 0; i < MAP_LNT; i++) { | |
t = dms_rmap (mapi + i); | |
WriteW ((ABREG[absel] + i) & VAMASK, t); } } | |
else { /* load */ | |
dms_viol (err_PC, MVI_PRV, 0); /* priv if PRO */ | |
for (i = 0; i < MAP_LNT; i++) { | |
t = ReadW (ABREG[absel] + i & VAMASK); | |
dms_wmap (mapi + i, t); } } | |
ABREG[absel] = (ABREG[absel] + MAP_LNT) & DMASK; | |
break; | |
case 014: /* SSM */ | |
WriteW (MA, dms_upd_sr ()); /* store stat */ | |
break; | |
case 015: /* JRS */ | |
if (dms_ump) dms_viol (err_PC, MVI_PRV, 0); | |
t = ReadW (MA); /* get status */ | |
if (t & 0100000) dms_enb = 1; /* set/clr enb */ | |
else dms_enb = 0; | |
if (t & 0040000) dms_ump = 1; /* set/clr usr */ | |
else dms_ump = 0; | |
PCQ_ENTRY; /* save old PC */ | |
PC = M1; /* jump */ | |
ion_defer = 1; /* defer intr */ | |
break; | |
/* Extended instruction group: DMS, continued */ | |
case 020: /* XMM */ | |
if (XR == 0) break; /* nop? */ | |
if (XR & SIGN) { /* store? */ | |
for ( ; XR == 0; XR = (XR + 1) & DMASK) { | |
t = dms_rmap (AR & MAP_MASK); | |
WriteW (BR & VAMASK, t); | |
AR = (AR + 1) & DMASK; | |
BR = (BR + 1) & DMASK; } } | |
else { /* load */ | |
for ( ; XR == 0; XR--) { | |
t = ReadW (BR & VAMASK); | |
dms_wmap (AR & MAP_MASK, t); | |
AR = (AR + 1) & DMASK; | |
BR = (BR + 1) & DMASK; } } | |
break; | |
case 021: /* XMS */ | |
if (XR & SIGN) break; /* nop? */ | |
for ( ; XR == 0; XR = (XR - 1) & DMASK) { | |
dms_wmap (AR & MAP_MASK, BR); | |
AR = (AR + 1) & DMASK; | |
BR = (BR + 1) & DMASK; } | |
break; | |
case 022: /* XMA, XMB */ | |
if (ABREG[absel] & 0100000) mapi = SMAP; | |
else mapi = UMAP; | |
if (ABREG[absel] & 0040000) mapj = PAMAP; | |
else mapj = PBMAP; | |
for (i = 0; i < MAP_LNT; i++) { | |
t = dms_rmap (mapi + i); | |
dms_wmap (mapj + i, t); } | |
break; | |
case 024: /* XLA, XLB */ | |
ABREG[absel] = ReadWA (MA); /* load alt */ | |
break; | |
case 025: /* XSA, XSB */ | |
dms_viol (err_PC, MVI_PRV, 0); /* priv if PRO */ | |
WriteWA (MA, ABREG[absel]); /* store alt */ | |
break; | |
case 026: /* XCA, XCB */ | |
if (ABREG[absel] != ReadWA (MA)) | |
PC = (PC + 1) & VAMASK; | |
break; | |
case 027: /* LFA, LFB */ | |
if (dms_ump) dms_viol (err_PC, MVI_PRV, 0); | |
dms_sr = (dms_sr & ~(MST_FLT | MST_FENCE)) | | |
(ABREG[absel] & (MST_FLT | MST_FENCE)); | |
dms_fence = dms_sr & MST_FENCE; | |
break; | |
/* Extended instruction group: DMS, continued */ | |
case 030: /* RSA, RSB */ | |
ABREG[absel] = dms_upd_sr (); /* save stat */ | |
break; | |
case 031: /* RVA, RVB */ | |
ABREG[absel] = dms_vr; /* save viol */ | |
break; | |
case 032: /* DJP */ | |
if (dms_ump) dms_viol (err_PC, MVI_PRV, 0); | |
if (MP_TESTJ (MA)) ABORT (ABORT_FENCE); | |
dms_enb = 0; | |
PCQ_ENTRY; | |
PC = MA; | |
ion_defer = 1; | |
break; | |
case 033: /* DJS */ | |
if (dms_ump) dms_viol (err_PC, MVI_PRV, 0); | |
dms_enb = 0; | |
WriteW (MA, PC); | |
PCQ_ENTRY; | |
PC = (MA + 1) & VAMASK; | |
ion_defer = 1; | |
break; | |
case 034: /* SJP */ | |
if (dms_ump) dms_viol (err_PC, MVI_PRV, 0); | |
if (MP_TESTJ (MA)) ABORT (ABORT_FENCE); | |
dms_enb = 1; | |
dms_ump = 0; | |
PCQ_ENTRY; | |
PC = MA; | |
ion_defer = 1; | |
break; | |
case 035: /* SJS */ | |
if (dms_ump) dms_viol (err_PC, MVI_PRV, 0); | |
dms_enb = 1; | |
dms_ump = 0; | |
WriteW (MA, PC); | |
PCQ_ENTRY; | |
PC = (MA + 1) & VAMASK; | |
ion_defer = 1; | |
break; | |
case 036: /* UJP */ | |
if (dms_ump) dms_viol (err_PC, MVI_PRV, 0); | |
if (MP_TESTJ (MA)) ABORT (ABORT_FENCE); | |
dms_enb = 1; | |
dms_ump = 1; | |
PCQ_ENTRY; | |
PC = MA; | |
ion_defer = 1; | |
break; | |
case 037: /* UJS */ | |
if (dms_ump) dms_viol (err_PC, MVI_PRV, 0); | |
dms_enb = 1; | |
dms_ump = 1; | |
WriteW (MA, PC); | |
PCQ_ENTRY; | |
PC = (MA + 1) & VAMASK; | |
ion_defer = 1; | |
break; | |
/* Extended instruction group: index register instructions */ | |
case 040: /* SAX, SBX */ | |
MA = (MA + XR) & VAMASK; | |
WriteW (MA, ABREG[absel]); | |
break; | |
case 041: /* CAX, CBX */ | |
XR = ABREG[absel]; | |
break; | |
case 042: /* LAX, LBX */ | |
MA = (MA + XR) & VAMASK; | |
ABREG[absel] = ReadW (MA); | |
break; | |
case 043: /* STX */ | |
WriteW (MA, XR); | |
break; | |
case 044: /* CXA, CXB */ | |
ABREG[absel] = XR; | |
break; | |
case 045: /* LDX */ | |
XR = ReadW (MA); | |
break; | |
case 046: /* ADX */ | |
opnd = ReadW (MA); | |
t = XR + opnd; | |
if (t > DMASK) E = 1; | |
if (((~XR ^ opnd) & (XR ^ t)) & SIGN) O = 1; | |
XR = t & DMASK; | |
break; | |
case 047: /* XAX, XBX */ | |
t = XR; | |
XR = ABREG[absel]; | |
ABREG[absel] = t; | |
break; | |
case 050: /* SAY, SBY */ | |
MA = (MA + YR) & VAMASK; | |
WriteW (MA, ABREG[absel]); | |
break; | |
case 051: /* CAY, CBY */ | |
YR = ABREG[absel]; | |
break; | |
case 052: /* LAY, LBY */ | |
MA = (MA + YR) & VAMASK; | |
ABREG[absel] = ReadW (MA); | |
break; | |
case 053: /* STY */ | |
WriteW (MA, YR); | |
break; | |
case 054: /* CYA, CYB */ | |
ABREG[absel] = YR; | |
break; | |
case 055: /* LDY */ | |
YR = ReadW (MA); | |
break; | |
case 056: /* ADY */ | |
opnd = ReadW (MA); | |
t = YR + opnd; | |
if (t > DMASK) E = 1; | |
if (((~YR ^ opnd) & (YR ^ t)) & SIGN) O = 1; | |
YR = t & DMASK; | |
break; | |
case 057: /* XAY, XBY */ | |
t = YR; | |
YR = ABREG[absel]; | |
ABREG[absel] = t; | |
break; | |
case 060: /* ISX */ | |
XR = (XR + 1) & DMASK; | |
if (XR == 0) PC = (PC + 1) & VAMASK; | |
break; | |
case 061: /* DSX */ | |
XR = (XR - 1) & DMASK; | |
if (XR == 0) PC = (PC + 1) & VAMASK; | |
break; | |
case 062: /* JLY */ | |
if (MP_TESTJ (MA)) ABORT (ABORT_FENCE); | |
PCQ_ENTRY; | |
YR = PC; | |
PC = MA; | |
break; | |
case 070: /* ISY */ | |
YR = (YR + 1) & DMASK; | |
if (YR == 0) PC = (PC + 1) & VAMASK; | |
break; | |
case 071: /* DSY */ | |
YR = (YR - 1) & DMASK; | |
if (YR == 0) PC = (PC + 1) & VAMASK; | |
break; | |
case 072: /* JPY */ | |
MA = (ReadW (PC) + YR) & VAMASK; /* no indirect */ | |
PC = (PC + 1) & VAMASK; | |
if (MP_TESTJ (MA)) ABORT (ABORT_FENCE); | |
PCQ_ENTRY; | |
PC = MA; | |
break; | |
/* Extended instruction group: byte */ | |
case 063: /* LBT */ | |
AR = ReadB (BR); | |
BR = (BR + 1) & DMASK; | |
break; | |
case 064: /* SBT */ | |
WriteB (BR, AR); | |
BR = (BR + 1) & DMASK; | |
break; | |
case 065: /* MBT */ | |
t = ReadW (awc); /* get wc */ | |
while (t) { /* while count */ | |
q = ReadB (AR); /* move byte */ | |
WriteB (BR, q); | |
AR = (AR + 1) & DMASK; /* incr src */ | |
BR = (BR + 1) & DMASK; /* incr dst */ | |
t = (t - 1) & DMASK; /* decr cnt */ | |
WriteW (awc, t); } | |
break; | |
case 066: /* CBT */ | |
t = ReadW (awc); /* get wc */ | |
while (t) { /* while count */ | |
q = ReadB (AR); /* get src1 */ | |
r = ReadB (BR); /* get src2 */ | |
if (q != r) { /* compare */ | |
PC = (PC + 1 + (q > r)) & VAMASK; | |
BR = (BR + t) & DMASK; /* update BR */ | |
WriteW (awc, 0); /* clr awc */ | |
break; } | |
AR = (AR + 1) & DMASK; /* incr src1 */ | |
BR = (BR + 1) & DMASK; /* incr src2 */ | |
t = (t - 1) & DMASK; /* decr cnt */ | |
WriteW (awc, t); } | |
break; | |
case 067: /* SFB */ | |
q = AR & 0377; /* test byte */ | |
r = (AR >> 8) & 0377; /* term byte */ | |
for (;;) { /* scan */ | |
t = ReadB (BR); /* get byte */ | |
if (t == q) break; /* test match? */ | |
BR = (BR + 1) & DMASK; | |
if (t == r) { /* term match? */ | |
PC = (PC + 1) & VAMASK; | |
break; } } | |
break; | |
/* Extended instruction group: bit, word */ | |
case 073: /* SBS */ | |
WriteW (M1, M[M1] | M [MA]); | |
break; | |
case 074: /* CBS */ | |
WriteW (M1, M[M1] & ~M[MA]); | |
break; | |
case 075: /* TBS */ | |
if ((M[M1] & M[MA]) != M[MA]) PC = (PC + 1) & VAMASK; | |
break; | |
case 076: /* CMW */ | |
t = ReadW (awc); /* get wc */ | |
while (t) { /* while count */ | |
q = SEXT (M[AR & VAMASK]); | |
r = SEXT (M[BR & VAMASK]); | |
if (q != r) { /* compare */ | |
PC = (PC + 1 + (q > r)) & VAMASK; | |
BR = (BR + t) & DMASK; /* update BR */ | |
WriteW (awc, 0); /* clr awc */ | |
break; } | |
AR = (AR + 1) & DMASK; /* incr src1 */ | |
BR = (BR + 1) & DMASK; /* incr src2 */ | |
t = (t - 1) & DMASK; /* decr cnt */ | |
WriteW (awc, t); } | |
break; | |
case 077: /* MVW */ | |
t = ReadW (awc); /* get wc */ | |
while (t) { /* while count */ | |
q = ReadW (AR & VAMASK); /* move word */ | |
WriteW (BR & VAMASK, q); | |
AR = (AR + 1) & DMASK; /* incr src */ | |
BR = (BR + 1) & DMASK; /* incr dst */ | |
t = (t - 1) & DMASK; /* decr cnt */ | |
WriteW (awc, t); } | |
break; } /* end ext group */ | |
default: | |
reason = stop_inst; } /* end switch IR */ | |
} /* end if extended */ | |
} /* end while */ | |
/* Simulation halted */ | |
saved_AR = AR & DMASK; | |
saved_BR = BR & DMASK; | |
for (i = 0; dibp = dib_tab[i]; i++) { /* loop thru dev */ | |
dev = dibp -> devno; | |
dibp -> cmd = CMD (dev); | |
dibp -> ctl = CTL (dev); | |
dibp -> flg = FLG (dev); | |
dibp -> fbf = FBF (dev); } | |
pcq_r -> qptr = pcq_p; /* update pc q ptr */ | |
return reason; | |
} | |
/* Shift micro operation */ | |
int32 shift (int32 t, int32 flag, int32 op) | |
{ | |
int32 oldE; | |
op = op & 07; /* get shift op */ | |
if (flag) { /* enabled? */ | |
switch (op) { /* case on operation */ | |
case 00: /* signed left shift */ | |
return ((t & SIGN) | ((t << 1) & 077777)); | |
case 01: /* signed right shift */ | |
return ((t & SIGN) | (t >> 1)); | |
case 02: /* rotate left */ | |
return (((t << 1) | (t >> 15)) & DMASK); | |
case 03: /* rotate right */ | |
return (((t >> 1) | (t << 15)) & DMASK); | |
case 04: /* left shift, 0 sign */ | |
return ((t << 1) & 077777); | |
case 05: /* ext right rotate */ | |
oldE = E; | |
E = t & 1; | |
return ((t >> 1) | (oldE << 15)); | |
case 06: /* ext left rotate */ | |
oldE = E; | |
E = (t >> 15) & 1; | |
return (((t << 1) | oldE) & DMASK); | |
case 07: /* rotate left four */ | |
return (((t << 4) | (t >> 12)) & DMASK); | |
} /* end case */ | |
} /* end if */ | |
if (op == 05) E = t & 1; /* disabled ext rgt rot */ | |
if (op == 06) E = (t >> 15) & 1; /* disabled ext lft rot */ | |
return t; /* input unchanged */ | |
} | |
/* Device dispatch */ | |
int32 devdisp (int32 devno, int32 inst, int32 IR, int32 dat) | |
{ | |
if (dtab[devno]) return dtab[devno] (inst, IR, dat); | |
else return nulio (inst, IR, dat); | |
} | |
/* Calculate DMA requests */ | |
int32 calc_dma (void) | |
{ | |
int32 r = 0; | |
if (CMD (DMA0) && dmac[0].cw3 && /* check DMA0 cycle */ | |
FLG (dmac[0].cw1 & DEVMASK)) r = r | DMAR0; | |
if (CMD (DMA0) && dmac[1].cw3 && /* check DMA1 cycle */ | |
FLG (dmac[1].cw1 & DEVMASK)) r = r | DMAR1; | |
return r; | |
} | |
/* Calculate interrupt requests | |
This routine takes into account all the relevant state of the | |
interrupt system: ion, dev_flg, dev_fbf, and dev_ctl. | |
1. dev_flg & dev_ctl determines the end of the priority grant. | |
The break in the chain will occur at the first device for | |
which dev_flg & dev_ctl is true. This is determined by | |
AND'ing the set bits with their 2's complement; only the low | |
order (highest priority) bit will differ. 1 less than | |
that, or'd with the single set bit itself, is the mask of | |
possible interrupting devices. If ION is clear, only devices | |
4 and 5 are eligible to interrupt. | |
2. dev_flg & dev_ctl & dev_fbf determines the outstanding | |
interrupt requests. All three bits must be on for a device | |
to request an interrupt. This is the masked under the | |
result from #1 to determine the highest priority interrupt, | |
if any. | |
*/ | |
int32 calc_int (void) | |
{ | |
int32 j, lomask, mask[2], req[2]; | |
lomask = dev_flg[0] & dev_ctl[0] & ~M_NXDEV; /* start chain calc */ | |
req[0] = lomask & dev_fbf[0]; /* calc requests */ | |
lomask = lomask & (-lomask); /* chain & -chain */ | |
mask[0] = lomask | (lomask - 1); /* enabled devices */ | |
req[0] = req[0] & mask[0]; /* highest request */ | |
if (ion) { /* ion? */ | |
if (lomask == 0) { /* no break in chn? */ | |
mask[1] = dev_flg[1] & dev_ctl[1]; /* do all devices */ | |
req[1] = mask[1] & dev_fbf[1]; | |
mask[1] = mask[1] & (-mask[1]); | |
mask[1] = mask[1] | (mask[1] - 1); | |
req[1] = req[1] & mask[1]; } | |
else req[1] = 0; } | |
else { req[0] = req[0] & (INT_M (PWR) | INT_M (PRO)); | |
req[1] = 0; } | |
if (req[0]) { /* if low request */ | |
for (j = 0; j < 32; j++) { /* find dev # */ | |
if (req[0] & INT_M (j)) return j; } } | |
if (req[1]) { /* if hi request */ | |
for (j = 0; j < 32; j++) { /* find dev # */ | |
if (req[1] & INT_M (j)) return (32 + j); } } | |
return 0; | |
} | |
/* Memory access routines */ | |
uint8 ReadB (int32 va) | |
{ | |
int32 pa; | |
if (dms_enb) pa = dms (va >> 1, dms_ump, RD); | |
else pa = va >> 1; | |
if (va & 1) return (M[pa] & 0377); | |
else return ((M[pa] >> 8) & 0377); | |
} | |
uint8 ReadBA (int32 va) | |
{ | |
int32 pa; | |
if (dms_enb) pa = dms (va >> 1, dms_ump ^ MAP_LNT, RD); | |
else pa = va >> 1; | |
if (va & 1) return (M[pa] & 0377); | |
else return ((M[pa] >> 8) & 0377); | |
} | |
uint16 ReadW (int32 va) | |
{ | |
int32 pa; | |
if (dms_enb) pa = dms (va, dms_ump, RD); | |
else pa = va; | |
return M[pa]; | |
} | |
uint16 ReadWA (int32 va) | |
{ | |
int32 pa; | |
if (dms_enb) pa = dms (va, dms_ump ^ MAP_LNT, RD); | |
else pa = va; | |
return M[pa]; | |
} | |
uint32 ReadF (int32 va) | |
{ | |
uint32 t = ReadW (va); | |
uint32 t1 = ReadW ((va + 1) & VAMASK); | |
return (t << 16) | t1; | |
} | |
uint16 ReadIO (int32 va, int32 map) | |
{ | |
int32 pa; | |
if (dms_enb) pa = dms (va, map, RD); | |
else pa = va; | |
return M[pa]; | |
} | |
void WriteB (int32 va, int32 dat) | |
{ | |
int32 pa; | |
if (MP_TEST (va)) ABORT (ABORT_FENCE); | |
if (dms_enb) pa = dms (va >> 1, dms_ump ^ MAP_LNT, WR); | |
else pa = va >> 1; | |
if (MEM_ADDR_OK (pa)) { | |
if (va & 1) M[pa] = (M[pa] & 0177400) | (dat & 0377); | |
else M[pa] = (M[pa] & 0377) | ((dat & 0377) << 8); } | |
return; | |
} | |
void WriteBA (int32 va, int32 dat) | |
{ | |
int32 pa; | |
if (MP_TEST (va)) ABORT (ABORT_FENCE); | |
if (dms_enb) pa = dms (va >> 1, dms_ump ^ MAP_LNT, WR); | |
else pa = va >> 1; | |
if (MEM_ADDR_OK (pa)) { | |
if (va & 1) M[pa] = (M[pa] & 0177400) | (dat & 0377); | |
else M[pa] = (M[pa] & 0377) | ((dat & 0377) << 8); } | |
return; | |
} | |
void WriteW (int32 va, int32 dat) | |
{ | |
int32 pa; | |
if (MP_TEST (va)) ABORT (ABORT_FENCE); | |
if (dms_enb) pa = dms (va, dms_ump, WR); | |
else pa = va; | |
if (MEM_ADDR_OK (pa)) M[pa] = dat; | |
return; | |
} | |
void WriteWA (int32 va, int32 dat) | |
{ | |
int32 pa; | |
if (MP_TEST (va)) ABORT (ABORT_FENCE); | |
if (dms_enb) pa = dms (va, dms_ump ^ MAP_LNT, WR); | |
else pa = va; | |
if (MEM_ADDR_OK (pa)) M[pa] = dat; | |
return; | |
} | |
void WriteIO (int32 va, int32 dat, int32 map) | |
{ | |
int32 pa; | |
if (dms_enb) pa = dms (va, map, WR); | |
else pa = va; | |
if (MEM_ADDR_OK (pa)) M[pa] = dat; | |
return; | |
} | |
/* DMS relocation */ | |
int32 dms (int32 va, int32 map, int32 prot) | |
{ | |
int32 pgn, mpr; | |
if (va <= 1) return va; /* A, B */ | |
pgn = VA_GETPAG (va); /* get page num */ | |
if (pgn == 0) { /* base page? */ | |
if ((dms_sr & MST_FLT)? /* check unmapped */ | |
(va >= dms_fence): /* 1B10: >= fence */ | |
(va < dms_fence)) { /* 0B10: < fence */ | |
if (prot == WR) dms_viol (va, MVI_BPG, 0); /* if W, viol */ | |
return va; } } /* no mapping */ | |
mpr = dms_map[map + pgn]; /* get map reg */ | |
if (mpr & prot) dms_viol (va, prot << (MVI_V_WPR - MAPA_V_WPR), 0); | |
return (PA_GETPAG (mpr) | VA_GETOFF (va)); | |
} | |
/* DMS read and write maps */ | |
uint16 dms_rmap (int32 mapi) | |
{ | |
int32 t; | |
mapi = mapi & MAP_MASK; | |
t = (((dms_map[mapi] >> VA_N_OFF) & PA_M_PAG) | | |
((dms_map[mapi] & (RD | WR)) << (MAPM_V_WPR - MAPA_V_WPR))); | |
return (uint16) t; | |
} | |
void dms_wmap (int32 mapi, int32 dat) | |
{ | |
mapi = mapi & MAP_MASK; | |
dms_map[mapi] = ((dat & PA_M_PAG) << VA_N_OFF) | | |
((dat >> (MAPM_V_WPR - MAPA_V_WPR)) & (RD | WR)); | |
return; | |
} | |
/* DMS violation | |
DMS violation processing occurs in two parts | |
- The violation register is set based on DMS status | |
- An interrupt (abort) occurs only if CTL (PRO) is set | |
Bit 7 (ME bus disabled/enabled) records whether relocation | |
actually occurred in the aborted cycle. For read and write | |
violations, bit 7 will be set; for base page and privilege | |
violations, it will be clear | |
I/O map references set status bits but never abort | |
*/ | |
void dms_viol (int32 va, int32 st, t_bool io) | |
{ | |
dms_sr = st | VA_GETPAG (va) | | |
((st & (MVI_RPR | MVI_WPR))? MVI_MEB: 0) | /* set MEB */ | |
(dms_enb? MVI_MEM: 0) | /* set MEM */ | |
(dms_ump? MVI_UMP: 0); /* set UMAP */ | |
if (CTL (PRO) && !io) ABORT (ABORT_DMS); | |
return; | |
} | |
/* DMS update status */ | |
int32 dms_upd_sr (void) | |
{ | |
dms_sr = dms_sr & ~(MST_ENB | MST_UMP | MST_PRO); | |
if (dms_enb) dms_sr = dms_sr | MST_ENB; | |
if (dms_ump) dms_sr = dms_sr | MST_UMP; | |
if (CTL (PRO)) dms_sr = dms_sr | MST_PRO; | |
return dms_sr; | |
} | |
/* Device 0 (CPU) I/O routine */ | |
int32 cpuio (int32 inst, int32 IR, int32 dat) | |
{ | |
int i; | |
switch (inst) { /* case on opcode */ | |
case ioFLG: /* flag */ | |
ion = (IR & HC)? 0: 1; /* interrupts off/on */ | |
return dat; | |
case ioSFC: /* skip flag clear */ | |
if (!ion) PC = (PC + 1) & VAMASK; | |
return dat; | |
case ioSFS: /* skip flag set */ | |
if (ion) PC = (PC + 1) & VAMASK; | |
return dat; | |
case ioLIX: /* load */ | |
dat = 0; /* returns 0 */ | |
break; | |
case ioCTL: /* control */ | |
if (IR & AB) { /* = CLC 06..77 */ | |
for (i = 6; i <= DEVMASK; i++) devdisp (i, inst, AB + i, 0); } | |
break; | |
default: | |
break; } | |
if (IR & HC) ion = 0; /* HC option */ | |
return dat; | |
} | |
/* Device 1 (overflow) I/O routine */ | |
int32 ovfio (int32 inst, int32 IR, int32 dat) | |
{ | |
switch (inst) { /* case on opcode */ | |
case ioFLG: /* flag */ | |
O = (IR & HC)? 0: 1; /* clear/set overflow */ | |
return dat; | |
case ioSFC: /* skip flag clear */ | |
if (!O) PC = (PC + 1) & VAMASK; | |
break; /* can clear flag */ | |
case ioSFS: /* skip flag set */ | |
if (O) PC = (PC + 1) & VAMASK; | |
break; /* can clear flag */ | |
case ioMIX: /* merge */ | |
dat = dat | SR; | |
break; | |
case ioLIX: /* load */ | |
dat = SR; | |
break; | |
case ioOTX: /* output */ | |
SR = dat; | |
break; | |
default: | |
break; } | |
if (IR & HC) O = 0; /* HC option */ | |
return dat; | |
} | |
/* Device 4 (power fail) I/O routine */ | |
int32 pwrio (int32 inst, int32 IR, int32 dat) | |
{ | |
switch (inst) { /* case on opcode */ | |
case ioMIX: /* merge */ | |
dat = dat | intaddr; | |
break; | |
case ioLIX: /* load */ | |
dat = intaddr; | |
break; | |
default: | |
break; } | |
return dat; | |
} | |
/* Device 5 (memory protect) I/O routine */ | |
int32 proio (int32 inst, int32 IR, int32 dat) | |
{ | |
switch (inst) { /* case on opcode */ | |
case ioSFC: /* skip flag clear */ | |
if (FLG (PRO)) PC = (PC + 1) & VAMASK; | |
return dat; | |
case ioSFS: /* skip flag set */ | |
return dat; | |
case ioMIX: /* merge */ | |
dat = dat | maddr; | |
break; | |
case ioLIX: /* load */ | |
dat = maddr; | |
break; | |
case ioOTX: /* output */ | |
mfence = dat & VAMASK; | |
break; | |
case ioCTL: /* control clear/set */ | |
if ((IR & AB) == 0) { /* STC */ | |
setCTL (PRO); | |
clrFLG (PRO); } | |
break; | |
default: | |
break; } | |
return dat; | |
} | |
/* Devices 2,3 (secondary DMA) I/O routine */ | |
int32 dmsio (int32 inst, int32 IR, int32 dat) | |
{ | |
int32 ch; | |
ch = IR & 1; /* get channel num */ | |
switch (inst) { /* case on opcode */ | |
case ioMIX: /* merge */ | |
dat = dat | dmac[ch].cw3; | |
break; | |
case ioLIX: /* load */ | |
dat = dmac[ch].cw3; | |
break; | |
case ioOTX: /* output */ | |
if (CTL (DMALT0 + ch)) dmac[ch].cw3 = dat; | |
else dmac[ch].cw2 = dat; | |
break; | |
case ioCTL: /* control clear/set */ | |
if (IR & AB) { clrCTL (DMALT0 + ch); } /* CLC */ | |
else { setCTL (DMALT0 + ch); } /* STC */ | |
break; | |
default: | |
break; } | |
return dat; | |
} | |
/* Devices 6,7 (primary DMA) I/O routine */ | |
int32 dmpio (int32 inst, int32 IR, int32 dat) | |
{ | |
int32 ch, ddev; | |
ch = IR & 1; /* get channel number */ | |
ddev = dmac[ch].cw1 & DEVMASK; /* get target device */ | |
switch (inst) { /* case on opcode */ | |
case ioFLG: /* flag */ | |
if ((IR & HC) == 0) { clrCMD (DMA0 + ch); } /* set -> abort */ | |
break; | |
case ioSFC: /* skip flag clear */ | |
if (FLG (DMA0 + ch) == 0) PC = (PC + 1) & VAMASK; | |
return dat; | |
case ioSFS: /* skip flag set */ | |
if (FLG (DMA0 + ch) != 0) PC = (PC + 1) & VAMASK; | |
return dat; | |
case ioMIX: case ioLIX: /* load, merge */ | |
dat = DMASK; | |
break; | |
case ioOTX: /* output */ | |
dmac[ch].cw1 = dat; | |
break; | |
case ioCTL: /* control */ | |
if (IR & AB) { clrCTL (DMA0 + ch); } /* CLC: cmd unchgd */ | |
else { setCTL (DMA0 + ch); /* STC: set ctl, cmd */ | |
setCMD (DMA0 + ch); } | |
break; | |
default: | |
break; } | |
if (IR & HC) { clrFLG (DMA0 + ch); } /* HC option */ | |
return dat; | |
} | |
/* DMA cycle routine */ | |
void dma_cycle (int32 ch, int32 map) | |
{ | |
int32 temp, dev, MA; | |
dev = dmac[ch].cw1 & DEVMASK; /* get device */ | |
MA = dmac[ch].cw2 & VAMASK; /* get mem addr */ | |
if (dmac[ch].cw2 & DMA2_OI) { /* input? */ | |
temp = devdisp (dev, ioLIX, HC + dev, 0); /* do LIA dev,C */ | |
WriteIO (MA, temp & DMASK, map); } /* store data */ | |
else devdisp (dev, ioOTX, HC + dev, ReadIO (MA, map)); /* do OTA dev,C */ | |
dmac[ch].cw2 = (dmac[ch].cw2 & DMA2_OI) | ((dmac[ch].cw2 + 1) & VAMASK); | |
dmac[ch].cw3 = (dmac[ch].cw3 + 1) & DMASK; /* incr wcount */ | |
if (dmac[ch].cw3) { /* more to do? */ | |
if (dmac[ch].cw1 & DMA1_STC) devdisp (dev, ioCTL, dev, 0); } | |
else { if (dmac[ch].cw1 & DMA1_CLC) devdisp (dev, ioCTL, AB + dev, 0); | |
else if ((dmac[ch].cw1 & DMA1_STC) && ((dmac[ch].cw2 & DMA2_OI) == 0)) | |
devdisp (dev, ioCTL, dev, 0); | |
setFLG (DMA0 + ch); /* set DMA flg */ | |
clrCMD (DMA0 + ch); } /* clr DMA cmd */ | |
return; | |
} | |
/* Unimplemented device routine */ | |
int32 nulio (int32 inst, int32 IR, int32 dat) | |
{ | |
switch (inst) { /* case on opcode */ | |
case ioSFC: /* skip flag clear */ | |
PC = (PC + 1) & VAMASK; | |
return (stop_dev << IOT_V_REASON) | dat; | |
case ioSFS: /* skip flag set */ | |
return (stop_dev << IOT_V_REASON) | dat; | |
default: | |
break; } | |
if (IR & HC) { clrFLG (IR & DEVMASK); } /* HC option */ | |
return (stop_dev << IOT_V_REASON) | dat; | |
} | |
/* Reset routines */ | |
t_stat cpu_reset (DEVICE *dptr) | |
{ | |
saved_AR = saved_BR = 0; | |
XR = YR = 0; | |
E = 0; | |
O = 0; | |
ion = ion_defer = 0; | |
clrCMD (PWR); | |
clrCTL (PWR); | |
clrFLG (PWR); | |
clrFBF (PWR); | |
clrCMD (PRO); | |
clrCTL (PRO); | |
clrFLG (PRO); | |
clrFBF (PRO); | |
mfence = 0; | |
maddr = 0; | |
dms_enb = dms_ump = 0; | |
dms_sr = dms_fence = 0; | |
dms_vr = dms_sma = 0; | |
pcq_r = find_reg ("PCQ", NULL, dptr); | |
sim_brk_types = sim_brk_dflt = SWMASK ('E'); | |
if (M == NULL) M = calloc (PASIZE, sizeof (unsigned int16)); | |
if (M == NULL) return SCPE_MEM; | |
if (pcq_r) pcq_r -> qptr = 0; | |
else return SCPE_IERR; | |
return SCPE_OK; | |
} | |
t_stat dma0_reset (DEVICE *tptr) | |
{ | |
clrCMD (DMA0); | |
clrCTL (DMA0); | |
clrFLG (DMA0); | |
clrFBF (DMA0); | |
dmac[0].cw1 = dmac[0].cw2 = dmac[0].cw3 = 0; | |
return SCPE_OK; | |
} | |
t_stat dma1_reset (DEVICE *tptr) | |
{ | |
clrCMD (DMA1); | |
clrCTL (DMA1); | |
clrFLG (DMA1); | |
clrFBF (DMA1); | |
dmac[1].cw1 = dmac[1].cw2 = dmac[1].cw3 = 0; | |
return SCPE_OK; | |
} | |
/* Memory examine */ | |
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) | |
{ | |
int32 d; | |
if (addr >= MEMSIZE) return SCPE_NXM; | |
if (addr == 0) d = saved_AR; | |
else if (addr == 1) d = saved_BR; | |
else d = M[addr]; | |
if (vptr != NULL) *vptr = d & DMASK; | |
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; | |
if (addr == 0) saved_AR = val & DMASK; | |
else if (addr == 1) saved_BR = val & DMASK; | |
else M[addr] = val & DMASK; | |
return SCPE_OK; | |
} | |
t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) | |
{ | |
int32 mc = 0; | |
t_addr i; | |
if ((val <= 0) || (val > PASIZE) || ((val & 07777) != 0) || | |
(!(uptr -> flags & UNIT_21MX) && (val > 32768))) | |
return SCPE_ARG; | |
for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; | |
if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) | |
return SCPE_OK; | |
MEMSIZE = val; | |
for (i = MEMSIZE; i < PASIZE; i++) M[i] = 0; | |
return SCPE_OK; | |
} | |
/* Set/show device number */ | |
t_stat hp_setdev (UNIT *uptr, int32 num, char *cptr, void *desc) | |
{ | |
int32 i, newdev; | |
DIB *dibp = (DIB *) desc; | |
t_stat r; | |
if (cptr == NULL) return SCPE_ARG; | |
if ((dibp == NULL) || (num > 1)) return SCPE_IERR; | |
newdev = get_uint (cptr, 8, DEVMASK - num, &r); | |
if (r != SCPE_OK) return r; | |
if (newdev < VARDEV) return SCPE_ARG; | |
for (i = 0; i <= num; i++, dibp++) dibp -> devno = newdev + i; | |
return SCPE_OK; | |
} | |
t_stat hp_showdev (FILE *st, UNIT *uptr, int32 num, void *desc) | |
{ | |
int32 i; | |
DIB *dibp = (DIB *) desc; | |
if (dibp == NULL) return SCPE_IERR; | |
fprintf (st, "devno=%o", dibp -> devno); | |
for (i = 1; i <= num; i++) fprintf (st, "/%o", dibp -> devno + i); | |
return SCPE_OK; | |
} | |
/* Enable a device */ | |
t_stat set_enb (UNIT *uptr, int32 num, char *cptr, void *desc) | |
{ | |
int32 i; | |
DEVICE *dptr; | |
DIB *dibp; | |
if (cptr != NULL) return SCPE_ARG; | |
if ((uptr == NULL) || (desc == NULL)) return SCPE_IERR; | |
dptr = find_dev_from_unit (uptr); /* find device */ | |
if (dptr == NULL) return SCPE_IERR; | |
dibp = (DIB *) desc; | |
if (dibp -> enb) return SCPE_OK; /* already enb? */ | |
for (i = 0; i <= num; i++, dibp++) dibp -> enb = 1; | |
if (dptr -> reset) return dptr -> reset (dptr); | |
else return SCPE_OK; | |
} | |
/* Disable a device */ | |
t_stat set_dis (UNIT *uptr, int32 num, char *cptr, void *desc) | |
{ | |
int32 i; | |
DEVICE *dptr; | |
DIB *dibp; | |
UNIT *up; | |
if (cptr != NULL) return SCPE_ARG; | |
if ((uptr == NULL) || (desc == NULL)) return SCPE_IERR; | |
dptr = find_dev_from_unit (uptr); /* find device */ | |
if (dptr == NULL) return SCPE_IERR; | |
dibp = (DIB *) desc; | |
if (dibp -> enb == 0) return SCPE_OK; /* already dis? */ | |
for (i = 0; i < dptr -> numunits; i++) { /* check units */ | |
up = (dptr -> units) + i; | |
if ((up -> flags & UNIT_ATT) || sim_is_active (up)) | |
return SCPE_NOFNC; } | |
for (i = 0; i <= num; i++, dibp++) dibp -> enb = 0; | |
if (dptr -> reset) return dptr -> reset (dptr); | |
else return SCPE_OK; | |
} | |
/* Test for device conflict */ | |
t_bool dev_conflict (void) | |
{ | |
DIB *dibp, *chkp; | |
int32 i, j, dno; | |
for (i = 0; chkp = dib_tab[i]; i++) { | |
if (chkp -> enb) { | |
dno = chkp -> devno; | |
for (j = 0; dibp = dib_tab[j]; j++) { | |
if (dibp -> enb && (chkp != dibp) && (dno == dibp -> devno)) { | |
printf ("Device number conflict, devno = %d\n", dno); | |
if (sim_log) fprintf (sim_log, | |
"Device number conflict, devno = %d\n", dno); | |
return TRUE; } } } } | |
return FALSE; | |
} |