First Commit of my working state
[simh.git] / NOVA / nova_cpu.c
CommitLineData
196ba1fc
PH
1/* nova_cpu.c: NOVA CPU simulator\r
2\r
3 Copyright (c) 1993-2008, Robert M. Supnik\r
4\r
5 Permission is hereby granted, free of charge, to any person obtaining a\r
6 copy of this software and associated documentation files (the "Software"),\r
7 to deal in the Software without restriction, including without limitation\r
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
9 and/or sell copies of the Software, and to permit persons to whom the\r
10 Software is furnished to do so, subject to the following conditions:\r
11\r
12 The above copyright notice and this permission notice shall be included in\r
13 all copies or substantial portions of the Software.\r
14\r
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21\r
22 Except as contained in this notice, the name of Robert M Supnik shall not be\r
23 used in advertising or otherwise to promote the sale, use or other dealings\r
24 in this Software without prior written authorization from Robert M Supnik.\r
25\r
26 cpu Nova central processor\r
27\r
28 04-Jul-07 BKR DEV_SET/CLR macros now used,\r
29 support for non-existant devices added\r
30 CPU bootstrap code warning: high-speed devices may not boot properly,\r
31 execution history facility added,\r
32 documented Nova 3 secret LDB/STB/SAVN behavior,\r
33 added support for secret Nova 3 LDB/STB/SAVN substitute actions,\r
34 'ind_max' changed from 16 to 65536 for better unmapped system compatibility,\r
35 INT_TRAP added for Nova 3, 4 trap instruction handling,\r
36 28-Apr-07 RMS Removed clock initialization\r
37 06-Feb-06 RMS Fixed bug in DIVS (found by Mark Hittinger)\r
38 22-Sep-05 RMS Fixed declarations (from Sterling Garwood)\r
39 25-Aug-05 RMS Fixed DIVS case 2^31 / - 1\r
40 14-Jan-04 RMS Fixed device enable/disable support (found by Bruce Ray)\r
41 19-Jan-03 RMS Changed CMASK to CDMASK for Apple Dev Kit conflict\r
42 03-Oct-02 RMS Added DIB infrastructure\r
43 30-Dec-01 RMS Added old PC queue\r
44 07-Dec-01 RMS Revised to use breakpoint package\r
45 30-Nov-01 RMS Added extended SET/SHOW support\r
46 10-Aug-01 RMS Removed register in declarations\r
47 17-Jul-01 RMS Moved function prototype\r
48 26-Apr-01 RMS Added device enable/disable support\r
49 05-Mar-01 RMS Added clock calibration\r
50 22-Dec-00 RMS Added Bruce Ray's second terminal\r
51 15-Dec-00 RMS Added Charles Owen's CPU bootstrap\r
52 08-Dec-00 RMS Changes from Bruce Ray\r
53 -- fixed trap test to include Nova 3\r
54 -- fixed DIV and DIVS divide by 0\r
55 -- fixed RETN to set SP from FP\r
56 -- fixed IORST to preserve carry\r
57 -- added "secret" Nova 4 PSHN/SAVEN instructions\r
58 -- added plotter support\r
59 15-Oct-00 RMS Fixed bug in MDV test, added stack, byte, trap instructions\r
60 14-Apr-98 RMS Changed t_addr to unsigned\r
61 15-Sep-97 RMS Added read and write breakpoints\r
62\r
63 The register state for the NOVA CPU is:\r
64\r
65 AC[0:3]<0:15> general registers\r
66 C carry flag\r
67 PC<0:14> program counter\r
68 \r
69 The NOVA has three instruction formats: memory reference, I/O transfer,\r
70 and operate. The memory reference format is:\r
71\r
72 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\r
73 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r
74 | 0| op | AC |in| mode| displacement | memory reference\r
75 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r
76\r
77 <0:4> mnemonic action\r
78\r
79 00000 JMP PC = MA\r
80 00001 JMS AC3 = PC, PC = MA\r
81 00010 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0\r
82 00011 DSZ M[MA] = M[MA] - 1, skip if M[MA] == 0\r
83 001'n LDA ACn = M[MA]\r
84 010'n STA M[MA] = ACn\r
85\r
86 <5:7> mode action\r
87\r
88 000 page zero direct MA = zext (IR<8:15>)\r
89 001 PC relative direct MA = PC + sext (IR<8:15>)\r
90 010 AC2 relative direct MA = AC2 + sext (IR<8:15>)\r
91 011 AC3 relative direct MA = AC3 + sext (IR<8:15>)\r
92 100 page zero indirect MA = M[zext (IR<8:15>)]\r
93 101 PC relative indirect MA = M[PC + sext (IR<8:15>)]\r
94 110 AC2 relative indirect MA = M[AC2 + sext (IR<8:15>)]\r
95 111 AC3 relative indirect MA = M[AC3 + sext (IR<8:15>)]\r
96\r
97 Memory reference instructions can access an address space of 32K words.\r
98 An instruction can directly reference the first 256 words of memory\r
99 (called page zero), as well as 256 words relative to the PC, AC2, or\r
100 AC3; it can indirectly access all 32K words. If an indirect address\r
101 is in locations 00020-00027, the indirect address is incremented and\r
102 rewritten to memory before use; if in 00030-00037, decremented and\r
103 rewritten.\r
104\r
105 The I/O transfer format is:\r
106\r
107 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\r
108 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r
109 | 0 1 1| AC | opcode |pulse| device | I/O transfer\r
110 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r
111\r
112 The IOT instruction sends the opcode, pulse, and specified AC to the\r
113 specified I/O device. The device may accept data, provide data,\r
114 initiate or cancel operations, or skip on status.\r
115\r
116 The operate format is:\r
117\r
118 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\r
119 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r
120 | 1|srcAC|dstAC| opcode |shift|carry|nl| skip | operate\r
121 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r
122 \______/ \___/ \___/ | | | |\r
123 | | | | | | +--- reverse skip sense\r
124 | | | | | +--- skip if C == 0\r
125 | | | | +--- skip if result == 0\r
126 | | | +--- don't load result\r
127 | | +--- carry in (load as is,\r
128 | | set to Zero,\r
129 | | set to One,\r
130 | | load Complement)\r
131 | +--- shift (none,\r
132 | left one,\r
133 | right one,\r
134 | byte swap)\r
135 +--- operation (complement,\r
136 negate,\r
137 move,\r
138 increment,\r
139 add complement,\r
140 subtract,\r
141 add,\r
142 and)\r
143\r
144 The operate instruction can be microprogrammed to perform operations\r
145 on the source and destination AC's and the Carry flag.\r
146\r
147 Some notes from Bruce Ray:\r
148\r
149 1. DG uses the value of the autoindex location -before- the\r
150 modification to determine if additional indirect address\r
151 levels are to be performed. Most DG emulators conform to\r
152 this standard, but some vendor machines (i.e. Point 4 Mark 8)\r
153 do not.\r
154\r
155 2. Infinite indirect references may occur on unmapped systems\r
156 and can "hang" the hardware. Some DG diagnostics perform\r
157 10,000s of references during a single instruction.\r
158\r
159 3. Nova 3 adds the following instructions to the standard Nova\r
160 instruction set:\r
161\r
162 trap instructions\r
163 stack push/pop instructions\r
164 save/return instructions\r
165 stack register manipulation instructions\r
166 unsigned MUL/DIV\r
167\r
168 4. Nova 4 adds the following instructions to the Nova 3 instruction\r
169 set:\r
170\r
171 signed MUL/DIV\r
172 load/store byte\r
173 secret (undocumented) stack instructions [PSHN, SAVN]\r
174\r
175 5. Nova, Nova 3 and Nova 4 unsigned mul/div instructions are the\r
176 same instruction code values on all machines.\r
177\r
178 6. Undocumented Nova 3 behaviour for LDB, STB and SAVN has been\r
179 added to appropriate code.\r
180\r
181 7. Most 3rd party vendors had a user-controlled method to increase the\r
182 logical address space from 32 KW to 64 KW. This capability came at\r
183 the expense of disabling multi-level indirect addressing when the 64KW\r
184 mode is in effect, and keeping DG multi-level indirect compatibility\r
185 when 64KW mode is inactive. The most common implementation was to use\r
186 an "NIOP <ac>,CPU" instruction to control whether 32 KW or 64 KW\r
187 addressing mode was wanted, and <ac> bit 15 (the least-significant bit\r
188 of an accumulator) determined which mode was set:\r
189 0 = 32 KW (DG compatible), 1 = 64 KW.\r
190\r
191 This feature has been implemented in our Nova emulation for all to enjoy.\r
192\r
193\r
194 This routine is the instruction decode routine for the NOVA.\r
195 It is called from the simulator control program to execute\r
196 instructions in simulated memory, starting at the simulated PC.\r
197 It runs until 'reason' is set non-zero.\r
198\r
199 General notes:\r
200\r
201 1. Reasons to stop. The simulator can be stopped by:\r
202\r
203 HALT instruction\r
204 breakpoint encountered\r
205 infinite indirection loop\r
206 unknown I/O device and STOP_DEV flag set\r
207 I/O error in I/O simulator\r
208\r
209 2. Interrupts. Interrupts are maintained by four parallel variables:\r
210\r
211 dev_done device done flags\r
212 dev_disable device interrupt disable flags\r
213 dev_busy device busy flags\r
214 int_req interrupt requests\r
215\r
216 In addition, int_req contains the interrupt enable and ION pending\r
217 flags. If ION and ION pending are set, and at least one interrupt\r
218 request is pending, then an interrupt occurs. Note that the 16b PIO\r
219 mask must be mapped to the simulator's device bit mapping.\r
220 \r
221 3. Non-existent memory. On the NOVA, reads to non-existent memory\r
222 return zero, and writes are ignored. In the simulator, the\r
223 largest possible memory is instantiated and initialized to zero.\r
224 Thus, only writes need be checked against actual memory size.\r
225\r
226 4. Adding I/O devices. These modules must be modified:\r
227\r
228 nova_defs.h add interrupt request definition\r
229 nova_sys.c add sim_devices entry\r
230*/\r
231\r
232#include "nova_defs.h"\r
233\r
234\r
235#define PCQ_SIZE 64 /* must be 2**n */\r
236#define PCQ_MASK (PCQ_SIZE - 1)\r
237#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = PC\r
238\r
239\r
240#define INCA(x) (((x) + 1) & AMASK)\r
241#define DECA(x) (((x) - 1) & AMASK)\r
242#define SEXT(x) (((x) & SIGN)? ((x) | ~DMASK): (x))\r
243#define STK_CHECK(x,y) if (((x) & 0377) < (y)) int_req = int_req | INT_STK\r
244#define IND_STEP(x) M[x] & A_IND; /* return next level indicator */ \\r
245 if ( ((x) <= AUTO_TOP) && ((x) >= AUTO_INC) ) \\r
246 if ( (x) < AUTO_DEC ) \\r
247 M[x] = (M[x] + 1) & DMASK; \\r
248 else \\r
249 M[x] = (M[x] - 1) & DMASK; \\r
250 x = M[x] & AMASK\r
251\r
252#define INCREMENT_PC PC = (PC + 1) & AMASK /* increment PC */\r
253\r
254#define UNIT_V_MDV (UNIT_V_UF + 0) /* MDV present */\r
255#define UNIT_V_STK (UNIT_V_UF + 1) /* stack instr */\r
256#define UNIT_V_BYT (UNIT_V_UF + 2) /* byte instr */\r
257#define UNIT_V_64KW (UNIT_V_UF + 3) /* 64KW mem support */\r
258#define UNIT_V_MSIZE (UNIT_V_UF + 4) /* dummy mask */\r
259#define UNIT_MDV (1 << UNIT_V_MDV)\r
260#define UNIT_STK (1 << UNIT_V_STK)\r
261#define UNIT_BYT (1 << UNIT_V_BYT)\r
262#define UNIT_64KW (1 << UNIT_V_64KW)\r
263#define UNIT_MSIZE (1 << UNIT_V_MSIZE)\r
264#define UNIT_IOPT (UNIT_MDV | UNIT_STK | UNIT_BYT | UNIT_64KW)\r
265#define UNIT_NOVA3 (UNIT_MDV | UNIT_STK)\r
266#define UNIT_NOVA4 (UNIT_MDV | UNIT_STK | UNIT_BYT)\r
267#define UNIT_KERONIX (UNIT_MDV | UNIT_64KW)\r
268\r
269#define MODE_64K (cpu_unit.flags & UNIT_64KW)\r
270#define MODE_64K_ACTIVE ((cpu_unit.flags & UNIT_64KW) && (0xFFFF == AMASK))\r
271\r
272\r
273typedef struct\r
274 {\r
275 int32 pc;\r
276 int16 ir;\r
277 int16 ac0 ;\r
278 int16 ac1 ;\r
279 int16 ac2 ;\r
280 int16 ac3 ;\r
281 int16 carry ;\r
282 int16 sp ;\r
283 int16 fp ;\r
284 int32 devDone ;\r
285 int32 devBusy ;\r
286 int32 devDisable ;\r
287 int32 devIntr ;\r
288 } Hist_entry ;\r
289\r
290\r
291uint16 M[MAXMEMSIZE] = { 0 }; /* memory */\r
292int32 AC[4] = { 0 }; /* accumulators */\r
293int32 C = 0; /* carry flag */\r
294int32 saved_PC = 0; /* program counter */\r
295int32 SP = 0; /* stack pointer */\r
296int32 FP = 0; /* frame pointer */\r
297int32 SR = 0; /* switch register */\r
298int32 dev_done = 0; /* device done flags */\r
299int32 dev_busy = 0; /* device busy flags */\r
300int32 dev_disable = 0; /* int disable flags */\r
301int32 int_req = 0; /* interrupt requests */\r
302int32 pimask = 0; /* priority int mask */\r
303int32 pwr_low = 0; /* power fail flag */\r
304int32 ind_max = 65536; /* iadr nest limit */\r
305int32 stop_dev = 0; /* stop on ill dev */\r
306uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */\r
307int32 pcq_p = 0; /* PC queue ptr */\r
308REG *pcq_r = NULL; /* PC queue reg ptr */\r
309struct ndev dev_table[64]; /* dispatch table */\r
310int32 AMASK = 077777 ; /* current memory address mask */\r
311 /* (default to 32KW) */\r
312static int32 hist_p = 0 ; /* history pointer */\r
313static int32 hist_cnt = 0 ; /* history count */\r
314static Hist_entry * hist = NULL ; /* instruction history */\r
315\r
316\r
317t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);\r
318t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);\r
319t_stat cpu_reset (DEVICE *dptr);\r
320t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);\r
321t_stat cpu_boot (int32 unitno, DEVICE *dptr);\r
322t_stat build_devtab (void);\r
323\r
324t_stat hist_set( UNIT * uptr, int32 val, char * cptr, void * desc ) ;\r
325t_stat hist_show( FILE * st, UNIT * uptr, int32 val, void * desc ) ;\r
326static int hist_save( int32 pc, int32 our_ir ) ;\r
327char * devBitNames( int32 flags, char * ptr, char * sepStr ) ;\r
328\r
329void mask_out (int32 mask);\r
330\r
331\r
332extern int32 sim_interval;\r
333extern int32 sim_int_char;\r
334extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */\r
335extern DEVICE * sim_devices[];\r
336extern t_stat fprint_sym(FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw);\r
337\r
338\r
339\r
340/* CPU data structures\r
341\r
342 cpu_dev CPU device descriptor\r
343 cpu_unit CPU unit descriptor\r
344 cpu_reg CPU register list\r
345 cpu_mod CPU modifiers list\r
346*/\r
347\r
348UNIT cpu_unit = {\r
349 UDATA (NULL, UNIT_FIX+UNIT_BINK+UNIT_MDV, DFTMEMSIZE /* MAXMEMSIZE */ )\r
350 };\r
351\r
352REG cpu_reg[] = {\r
353 { ORDATA (PC, saved_PC, 15) },\r
354 { ORDATA (AC0, AC[0], 16) },\r
355 { ORDATA (AC1, AC[1], 16) },\r
356 { ORDATA (AC2, AC[2], 16) },\r
357 { ORDATA (AC3, AC[3], 16) },\r
358 { FLDATA (C, C, 16) },\r
359 { ORDATA (SP, SP, 16) },\r
360 { ORDATA (FP, FP, 16) },\r
361 { ORDATA (SR, SR, 16) },\r
362 { ORDATA (PI, pimask, 16) },\r
363 { FLDATA (ION, int_req, INT_V_ION) },\r
364 { FLDATA (ION_DELAY, int_req, INT_V_NO_ION_PENDING) },\r
365 { FLDATA (STKOVF, int_req, INT_V_STK) },\r
366 { FLDATA (PWR, pwr_low, 0) },\r
367 { ORDATA (INT, int_req, INT_V_ION+1), REG_RO },\r
368 { ORDATA (BUSY, dev_busy, INT_V_ION+1), REG_RO },\r
369 { ORDATA (DONE, dev_done, INT_V_ION+1), REG_RO },\r
370 { ORDATA (DISABLE, dev_disable, INT_V_ION+1), REG_RO },\r
371 { FLDATA (STOP_DEV, stop_dev, 0) },\r
372 { DRDATA (INDMAX, ind_max, 32), REG_NZ + PV_LEFT },\r
373 { ORDATA (AMASK, AMASK, 16) },\r
374 { DRDATA (MEMSIZE, cpu_unit.capac, 32), REG_NZ + PV_LEFT },\r
375 { BRDATA (PCQ, pcq, 8, 16, PCQ_SIZE), REG_RO+REG_CIRC },\r
376 { ORDATA (PCQP, pcq_p, 6), REG_HRO },\r
377 { ORDATA (WRU, sim_int_char, 8) },\r
378 { NULL }\r
379 };\r
380\r
381MTAB cpu_mod[] = {\r
382 { UNIT_IOPT, UNIT_NOVA3, "NOVA3", "NOVA3", NULL },\r
383 { UNIT_IOPT, UNIT_NOVA4, "NOVA4", "NOVA4", NULL },\r
384 { UNIT_IOPT, UNIT_KERONIX, "KERONIX", "KERONIX", NULL },\r
385 { UNIT_IOPT, UNIT_MDV, "MDV", "MDV", NULL },\r
386 { UNIT_IOPT, UNIT_64KW, "EXT64KW", "EXT64KW", NULL },\r
387 { UNIT_IOPT, 0, "none", "NONE", NULL },\r
388 { UNIT_MSIZE, ( 4 * 1024), NULL, "4K", &cpu_set_size },\r
389 { UNIT_MSIZE, ( 8 * 1024), NULL, "8K", &cpu_set_size },\r
390 { UNIT_MSIZE, (12 * 1024), NULL, "12K", &cpu_set_size },\r
391 { UNIT_MSIZE, (16 * 1024), NULL, "16K", &cpu_set_size },\r
392 { UNIT_MSIZE, (20 * 1024), NULL, "20K", &cpu_set_size },\r
393 { UNIT_MSIZE, (24 * 1024), NULL, "24K", &cpu_set_size },\r
394 { UNIT_MSIZE, (28 * 1024), NULL, "28K", &cpu_set_size },\r
395 { UNIT_MSIZE, (32 * 1024), NULL, "32K", &cpu_set_size },\r
396\r
397 { UNIT_MSIZE, (36 * 1024), NULL, "36K", &cpu_set_size },\r
398 { UNIT_MSIZE, (40 * 1024), NULL, "40K", &cpu_set_size },\r
399 { UNIT_MSIZE, (44 * 1024), NULL, "44K", &cpu_set_size },\r
400 { UNIT_MSIZE, (48 * 1024), NULL, "48K", &cpu_set_size },\r
401 { UNIT_MSIZE, (52 * 1024), NULL, "52K", &cpu_set_size },\r
402 { UNIT_MSIZE, (56 * 1024), NULL, "56K", &cpu_set_size },\r
403 { UNIT_MSIZE, (60 * 1024), NULL, "60K", &cpu_set_size },\r
404 { UNIT_MSIZE, (64 * 1024), NULL, "64K", &cpu_set_size },\r
405 { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY",\r
406 &hist_set, &hist_show },\r
407\r
408 { 0 }\r
409 };\r
410\r
411DEVICE cpu_dev = {\r
412 "CPU", &cpu_unit, cpu_reg, cpu_mod,\r
413 1, 8, 16 /* = 64 KW, 15 = 32KW */, 1, 8, 16,\r
414 &cpu_ex, &cpu_dep, &cpu_reset,\r
415 NULL, NULL, NULL\r
416 };\r
417\r
418t_stat sim_instr (void)\r
419{\r
420int32 PC, IR, i;\r
421t_stat reason;\r
422\r
423/* Restore register state */\r
424\r
425if (build_devtab () != SCPE_OK) return SCPE_IERR; /* build dispatch */\r
426PC = saved_PC & AMASK; /* load local PC */\r
427C = C & CBIT;\r
428mask_out (pimask); /* reset int system */\r
429reason = 0;\r
430\r
431/* Main instruction fetch/decode loop */\r
432\r
433while (reason == 0) { /* loop until halted */\r
434\r
435 if (sim_interval <= 0) { /* check clock queue */\r
436 if ( (reason = sim_process_event ()) ) break;\r
437 }\r
438\r
439 if (int_req > INT_PENDING) { /* interrupt or exception? */\r
440 int32 MA, indf;\r
441\r
442 if (int_req & INT_TRAP) { /* trap instruction? */\r
443 int_req = int_req & ~INT_TRAP ; /* clear */\r
444 PCQ_ENTRY; /* save old PC */\r
445 M[TRP_SAV] = (PC - 1) & AMASK;\r
446 MA = TRP_JMP; /* jmp @47 */\r
447 }\r
448 else {\r
449 int_req = int_req & ~INT_ION; /* intr off */\r
450 PCQ_ENTRY; /* save old PC */\r
451 M[INT_SAV] = PC;\r
452 if (int_req & INT_STK) { /* stack overflow? */\r
453 int_req = int_req & ~INT_STK; /* clear */\r
454 MA = STK_JMP; /* jmp @3 */\r
455 }\r
456 else\r
457 MA = INT_JMP; /* intr: jmp @1 */\r
458 }\r
459 if ( MODE_64K_ACTIVE ) {\r
460 indf = IND_STEP (MA);\r
461 }\r
462 else\r
463 {\r
464 for (i = 0, indf = 1; indf && (i < ind_max); i++) {\r
465 indf = IND_STEP (MA); /* indirect loop */\r
466 }\r
467 if (i >= ind_max) {\r
468 reason = STOP_IND_INT;\r
469 break;\r
470 }\r
471 }\r
472 PC = MA;\r
473 } /* end interrupt */\r
474\r
475 if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */\r
476 reason = STOP_IBKPT; /* stop simulation */\r
477 break;\r
478 }\r
479\r
480 IR = M[PC]; /* fetch instr */\r
481 if ( hist_cnt )\r
482 {\r
483 hist_save( PC, IR ) ; /* PC, int_req unchanged */\r
484 }\r
485\r
486 INCREMENT_PC ;\r
487 int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */\r
488 sim_interval = sim_interval - 1;\r
489\r
490/* Operate instruction */\r
491\r
492 if (IR & I_OPR) { /* operate? */\r
493 int32 src, srcAC, dstAC;\r
494\r
495 srcAC = I_GETSRC (IR); /* get reg decodes */\r
496 dstAC = I_GETDST (IR);\r
497 switch (I_GETCRY (IR)) { /* decode carry */\r
498 case 0: /* load */\r
499 src = AC[srcAC] | C;\r
500 break;\r
501 case 1: /* clear */\r
502 src = AC[srcAC];\r
503 break;\r
504 case 2: /* set */\r
505 src = AC[srcAC] | CBIT;\r
506 break;\r
507 case 3: /* complement */\r
508 src = AC[srcAC] | (C ^ CBIT);\r
509 break;\r
510 } /* end switch carry */\r
511\r
512 switch (I_GETALU (IR)) { /* decode ALU */\r
513 case 0: /* COM */\r
514 src = src ^ DMASK;\r
515 break;\r
516 case 1: /* NEG */\r
517 src = ((src ^ DMASK) + 1) & CDMASK;\r
518 break;\r
519 case 2: /* MOV */\r
520 break;\r
521 case 3: /* INC */\r
522 src = (src + 1) & CDMASK;\r
523 break;\r
524 case 4: /* ADC */\r
525 src = ((src ^ DMASK) + AC[dstAC]) & CDMASK;\r
526 break;\r
527 case 5: /* SUB */\r
528 src = ((src ^ DMASK) + AC[dstAC] + 1) & CDMASK;\r
529 break;\r
530 case 6: /* ADD */\r
531 src = (src + AC[dstAC]) & CDMASK;\r
532 break;\r
533 case 7: /* AND */\r
534 src = src & (AC[dstAC] | CBIT);\r
535 break;\r
536 } /* end switch oper */\r
537\r
538 switch (I_GETSHF (IR)) { /* decode shift */\r
539 case 0: /* nop */\r
540 break;\r
541 case 1: /* L */\r
542 src = ((src << 1) | (src >> 16)) & CDMASK;\r
543 break;\r
544 case 2: /* R */\r
545 src = ((src >> 1) | (src << 16)) & CDMASK;\r
546 break;\r
547 case 3: /* S */\r
548 src = ((src & 0377) << 8) | ((src >> 8) & 0377) |\r
549 (src & CBIT);\r
550 break;\r
551 } /* end switch shift */\r
552\r
553 switch (I_GETSKP (IR)) { /* decode skip */\r
554 case 0: /* nop */\r
555 if ((IR & I_NLD) && (cpu_unit.flags & UNIT_STK)) {\r
556 int_req = int_req | INT_TRAP ; /* Nova 3 or 4 trap */\r
557 continue ;\r
558 }\r
559 break;\r
560 case 1: /* SKP */\r
561 INCREMENT_PC ;\r
562 break;\r
563 case 2: /* SZC */\r
564 if (src < CBIT) INCREMENT_PC ;\r
565 break;\r
566 case 3: /* SNC */\r
567 if (src >= CBIT) INCREMENT_PC ;\r
568 break;\r
569 case 4: /* SZR */\r
570 if ((src & DMASK) == 0) INCREMENT_PC ;\r
571 break;\r
572 case 5: /* SNR */\r
573 if ((src & DMASK) != 0) INCREMENT_PC ;\r
574 break;\r
575 case 6: /* SEZ */\r
576 if (src <= CBIT) INCREMENT_PC ;\r
577 break;\r
578 case 7: /* SBN */\r
579 if (src > CBIT) INCREMENT_PC ;\r
580 break;\r
581 } /* end switch skip */\r
582 if ((IR & I_NLD) == 0) { /* load? */\r
583 AC[dstAC] = src & DMASK;\r
584 C = src & CBIT;\r
585 } /* end if load */\r
586 } /* end if operate */\r
587\r
588/* Memory reference instructions */\r
589\r
590 else if (IR < 060000) { /* mem ref? */\r
591 int32 src, MA, indf;\r
592\r
593 MA = I_GETDISP (IR); /* get disp */\r
594 switch (I_GETMODE (IR)) { /* decode mode */\r
595 case 0: /* page zero */\r
596 break;\r
597 case 1: /* PC relative */\r
598 if (MA & DISPSIGN) MA = 0177400 | MA;\r
599 MA = (MA + PC - 1) & AMASK;\r
600 break;\r
601 case 2: /* AC2 relative */\r
602 if (MA & DISPSIGN) MA = 0177400 | MA;\r
603 MA = (MA + AC[2]) & AMASK;\r
604 break;\r
605 case 3: /* AC3 relative */\r
606 if (MA & DISPSIGN) MA = 0177400 | MA;\r
607 MA = (MA + AC[3]) & AMASK;\r
608 break;\r
609 } /* end switch mode */\r
610\r
611 if ( (indf = IR & I_IND) ) { /* indirect? */\r
612 if ( MODE_64K_ACTIVE ) { /* 64k mode? */\r
613 indf = IND_STEP (MA);\r
614 }\r
615 else /* compat mode */\r
616 {\r
617 for (i = 0; indf && (i < ind_max); i++) { /* count */\r
618 indf = IND_STEP (MA); /* resolve indirect */\r
619 }\r
620 if (i >= ind_max) { /* too many? */\r
621 reason = STOP_IND;\r
622 break;\r
623 }\r
624 }\r
625 }\r
626\r
627 switch (I_GETOPAC (IR)) { /* decode op + AC */\r
628 case 001: /* JSR */\r
629 AC[3] = PC;\r
630 case 000: /* JMP */\r
631 PCQ_ENTRY;\r
632 PC = MA;\r
633 break;\r
634 case 002: /* ISZ */\r
635 src = (M[MA] + 1) & DMASK;\r
636 if (MEM_ADDR_OK(MA)) M[MA] = src;\r
637 if (src == 0) INCREMENT_PC ;\r
638 break;\r
639 case 003: /* DSZ */\r
640 src = (M[MA] - 1) & DMASK;\r
641 if (MEM_ADDR_OK(MA)) M[MA] = src;\r
642 if (src == 0) INCREMENT_PC ;\r
643 break;\r
644 case 004: /* LDA 0 */\r
645 AC[0] = M[MA];\r
646 break;\r
647 case 005: /* LDA 1 */\r
648 AC[1] = M[MA];\r
649 break;\r
650 case 006: /* LDA 2 */\r
651 AC[2] = M[MA];\r
652 break;\r
653 case 007: /* LDA 3 */\r
654 AC[3] = M[MA];\r
655 break;\r
656 case 010: /* STA 0 */\r
657 if (MEM_ADDR_OK(MA)) M[MA] = AC[0];\r
658 break;\r
659 case 011: /* STA 1 */\r
660 if (MEM_ADDR_OK(MA)) M[MA] = AC[1];\r
661 break;\r
662 case 012: /* STA 2 */\r
663 if (MEM_ADDR_OK(MA)) M[MA] = AC[2];\r
664 break;\r
665 case 013: /* STA 3 */\r
666 if (MEM_ADDR_OK(MA)) M[MA] = AC[3];\r
667 break;\r
668 } /* end switch */\r
669 } /* end mem ref */\r
670\r
671/* IOT instruction */\r
672\r
673 else { /* IOT */\r
674 int32 dstAC, pulse, code, device, iodata;\r
675\r
676 dstAC = I_GETDST (IR); /* decode fields */\r
677 code = I_GETIOT (IR);\r
678 pulse = I_GETPULSE (IR);\r
679 device = I_GETDEV (IR);\r
680 if (code == ioSKP) { /* IO skip? */\r
681 switch (pulse) { /* decode IR<8:9> */\r
682\r
683 case 0: /* skip if busy */\r
684 if ((device == DEV_CPU)? (int_req & INT_ION) != 0:\r
685 (dev_busy & dev_table[device].mask) != 0)\r
686 INCREMENT_PC ;\r
687 break;\r
688\r
689 case 1: /* skip if not busy */\r
690 if ((device == DEV_CPU)? (int_req & INT_ION) == 0:\r
691 (dev_busy & dev_table[device].mask) == 0)\r
692 INCREMENT_PC ;\r
693 break;\r
694\r
695 case 2: /* skip if done */\r
696 if ((device == DEV_CPU)? pwr_low != 0:\r
697 (dev_done & dev_table[device].mask) != 0)\r
698 INCREMENT_PC ;\r
699 break;\r
700\r
701 case 3: /* skip if not done */\r
702 if ((device == DEV_CPU)? pwr_low == 0:\r
703 (dev_done & dev_table[device].mask) == 0)\r
704 INCREMENT_PC ;\r
705 break;\r
706 } /* end switch */\r
707 } /* end IO skip */\r
708\r
709 /* Hmm, this means a Nova 3 _must_ have DEV_MDV enabled - not true in DG land */\r
710\r
711 else if (device == DEV_MDV) {\r
712 switch (code) { /* case on opcode */\r
713\r
714 case ioNIO: /* frame ptr */\r
715 if (cpu_unit.flags & UNIT_STK) {\r
716 if (pulse == iopN) FP = AC[dstAC] & AMASK ;\r
717 if (pulse == iopC) AC[dstAC] = FP & AMASK ;\r
718 }\r
719 break;\r
720\r
721 case ioDIA: /* load byte */\r
722 if (cpu_unit.flags & UNIT_BYT)\r
723 {\r
724 AC[dstAC] = (M[AC[pulse] >> 1] >> ((AC[pulse] & 1)? 0: 8)) & 0377 ;\r
725 }\r
726 else if (cpu_unit.flags & UNIT_STK) /* if Nova 3 this is really a SAV... 2007-Jun-01, BKR */\r
727 {\r
728 SP = INCA (SP);\r
729 if (MEM_ADDR_OK (SP)) M[SP] = AC[0];\r
730 SP = INCA (SP);\r
731 if (MEM_ADDR_OK (SP)) M[SP] = AC[1];\r
732 SP = INCA (SP);\r
733 if (MEM_ADDR_OK (SP)) M[SP] = AC[2];\r
734 SP = INCA (SP);\r
735 if (MEM_ADDR_OK (SP)) M[SP] = FP;\r
736 SP = INCA (SP);\r
737 if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK);\r
738 AC[3] = FP = SP & AMASK; \r
739 STK_CHECK (SP, 5);\r
740 }\r
741 else\r
742 {\r
743 AC[dstAC] = 0;\r
744 }\r
745 break;\r
746\r
747 case ioDOA: /* stack ptr */\r
748 if (cpu_unit.flags & UNIT_STK) {\r
749 if (pulse == iopN) SP = AC[dstAC] & AMASK;\r
750 if (pulse == iopC) AC[dstAC] = SP & AMASK;\r
751 }\r
752 break;\r
753\r
754 case ioDIB: /* push, pop */\r
755 if (cpu_unit.flags & UNIT_STK) {\r
756 if (pulse == iopN) { /* push (PSHA) */\r
757 SP = INCA (SP);\r
758 if (MEM_ADDR_OK (SP)) M[SP] = AC[dstAC];\r
759 STK_CHECK (SP, 1);\r
760 }\r
761 if ((pulse == iopS) && /* Nova 4 pshn (PSHN) */\r
762 (cpu_unit.flags & UNIT_BYT)) {\r
763 SP = INCA (SP);\r
764 if (MEM_ADDR_OK (SP)) M[SP] = AC[dstAC];\r
765 if ( (SP & 0xFFFF) > (M[042] & 0xFFFF) )\r
766 {\r
767 int_req = int_req | INT_STK ;\r
768 }\r
769 }\r
770 if (pulse == iopC) { /* pop (POPA) */\r
771 AC[dstAC] = M[SP];\r
772 SP = DECA (SP);\r
773 }\r
774 }\r
775 break;\r
776\r
777 case ioDOB: /* store byte */\r
778 if (cpu_unit.flags & UNIT_BYT)\r
779 {\r
780 int32 MA, val;\r
781 MA = AC[pulse] >> 1;\r
782 val = AC[dstAC] & 0377;\r
783 if (MEM_ADDR_OK (MA)) M[MA] = (AC[pulse] & 1)?\r
784 ((M[MA] & ~0377) | val)\r
785 : ((M[MA] & 0377) | (val << 8));\r
786 }\r
787 else if (cpu_unit.flags & UNIT_STK) /* if Nova 3 this is really a SAV... 2007-Jun-01, BKR */\r
788 {\r
789 SP = INCA (SP);\r
790 if (MEM_ADDR_OK (SP)) M[SP] = AC[0];\r
791 SP = INCA (SP);\r
792 if (MEM_ADDR_OK (SP)) M[SP] = AC[1];\r
793 SP = INCA (SP);\r
794 if (MEM_ADDR_OK (SP)) M[SP] = AC[2];\r
795 SP = INCA (SP);\r
796 if (MEM_ADDR_OK (SP)) M[SP] = FP;\r
797 SP = INCA (SP);\r
798 if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK);\r
799 AC[3] = FP = SP & AMASK; \r
800 STK_CHECK (SP, 5);\r
801 }\r
802 break;\r
803\r
804 case ioDIC: /* save, return */\r
805 if (cpu_unit.flags & UNIT_STK) {\r
806 if (pulse == iopN) { /* save */\r
807 SP = INCA (SP);\r
808 if (MEM_ADDR_OK (SP)) M[SP] = AC[0];\r
809 SP = INCA (SP);\r
810 if (MEM_ADDR_OK (SP)) M[SP] = AC[1];\r
811 SP = INCA (SP);\r
812 if (MEM_ADDR_OK (SP)) M[SP] = AC[2];\r
813 SP = INCA (SP);\r
814 if (MEM_ADDR_OK (SP)) M[SP] = FP;\r
815 SP = INCA (SP);\r
816 if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK);\r
817 AC[3] = FP = SP & AMASK; \r
818 STK_CHECK (SP, 5);\r
819 }\r
820 else if (pulse == iopC) { /* retn */\r
821 PCQ_ENTRY;\r
822 SP = FP & AMASK;\r
823 C = (M[SP] << 1) & CBIT;\r
824 PC = M[SP] & AMASK;\r
825 SP = DECA (SP);\r
826 AC[3] = M[SP];\r
827 SP = DECA (SP);\r
828 AC[2] = M[SP];\r
829 SP = DECA (SP);\r
830 AC[1] = M[SP];\r
831 SP = DECA (SP);\r
832 AC[0] = M[SP];\r
833 SP = DECA (SP);\r
834 FP = AC[3] & AMASK;\r
835 }\r
836 else if ((pulse == iopS) && /* Nova 4 SAVN */\r
837 (cpu_unit.flags & UNIT_BYT)) {\r
838 int32 frameSz = M[PC] ;\r
839 PC = INCA (PC) ;\r
840 SP = INCA (SP);\r
841 if (MEM_ADDR_OK (SP)) M[SP] = AC[0];\r
842 SP = INCA (SP);\r
843 if (MEM_ADDR_OK (SP)) M[SP] = AC[1];\r
844 SP = INCA (SP);\r
845 if (MEM_ADDR_OK (SP)) M[SP] = AC[2];\r
846 SP = INCA (SP);\r
847 if (MEM_ADDR_OK (SP)) M[SP] = FP;\r
848 SP = INCA (SP);\r
849 if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK);\r
850 AC[3] = FP = SP & AMASK ;\r
851 SP = (SP + frameSz) & AMASK ;\r
852 if (SP > M[042])\r
853 {\r
854 int_req = int_req | INT_STK;\r
855 }\r
856 }\r
857 }\r
858 break;\r
859\r
860 case ioDOC:\r
861 if ((dstAC == 2) && (cpu_unit.flags & UNIT_MDV))\r
862 { /* Nova, Nova3 or Nova 4 */\r
863 uint32 mddata, uAC0, uAC1, uAC2;\r
864\r
865 uAC0 = (uint32) AC[0];\r
866 uAC1 = (uint32) AC[1];\r
867 uAC2 = (uint32) AC[2];\r
868 if (pulse == iopP)\r
869 { /* mul */\r
870 mddata = (uAC1 * uAC2) + uAC0;\r
871 AC[0] = (mddata >> 16) & DMASK;\r
872 AC[1] = mddata & DMASK;\r
873 }\r
874 if (pulse == iopS)\r
875 { /* div */\r
876 if ((uAC0 >= uAC2) || (uAC2 == 0))\r
877 {\r
878 C = CBIT;\r
879 }\r
880 else\r
881 {\r
882 C = 0;\r
883 mddata = (uAC0 << 16) | uAC1;\r
884 AC[1] = mddata / uAC2;\r
885 AC[0] = mddata % uAC2;\r
886 }\r
887 }\r
888 }\r
889 else if ((dstAC == 3) && (cpu_unit.flags & UNIT_BYT) /* assuming UNIT_BYT = Nova 4 */)\r
890 {\r
891 int32 mddata;\r
892 if (pulse == iopC)\r
893 { /* muls */\r
894 mddata = (SEXT (AC[1]) * SEXT (AC[2])) + SEXT (AC[0]);\r
895 AC[0] = (mddata >> 16) & DMASK;\r
896 AC[1] = mddata & DMASK;\r
897 }\r
898 else if (pulse == iopN)\r
899 { /* divs */\r
900 if ((AC[2] == 0) || /* overflow? */\r
901 ((AC[0] == 0100000) && (AC[1] == 0) && (AC[2] == 0177777)))\r
902 {\r
903 C = CBIT;\r
904 }\r
905 else\r
906 {\r
907 mddata = (SEXT (AC[0]) << 16) | AC[1];\r
908 AC[1] = mddata / SEXT (AC[2]);\r
909 AC[0] = mddata % SEXT (AC[2]);\r
910 if ((AC[1] > 077777) || (AC[1] < -0100000))\r
911 {\r
912 C = CBIT;\r
913 }\r
914 else\r
915 {\r
916 C = 0;\r
917 }\r
918 AC[0] = AC[0] & DMASK;\r
919 }\r
920 }\r
921 }\r
922 else if ((dstAC == 3) && (cpu_unit.flags & UNIT_STK)) /* if Nova 3 this is really a PSHA... 2007-Jun-01, BKR */\r
923 {\r
924 SP = INCA (SP);\r
925 if (MEM_ADDR_OK (SP)) M[SP] = AC[dstAC];\r
926 STK_CHECK (SP, 1);\r
927 }\r
928 break;\r
929 } /* end case code */\r
930 } /* end if mul/div */\r
931\r
932 else if (device == DEV_CPU) { /* CPU control */\r
933 switch (code) { /* decode IR<5:7> */\r
934\r
935 case ioNIO: /* NIOP <x> CPU ? */\r
936 if ( pulse == iopP )\r
937 if ( MODE_64K )\r
938 {\r
939 /* Keronix/Point4/SCI/INI/IDP (and others) */\r
940 /* 64 KW memory extension: */\r
941 /* NIOP - set memory mode (32/64 KW) per AC: */\r
942 /* B15: 0 = 32 KW, 1 = 64 KW mode */\r
943 AMASK = (AC[dstAC] & 0x0001) ? 0177777 : 077777 ;\r
944 }\r
945 break ;\r
946\r
947 case ioDIA: /* read switches */\r
948 AC[dstAC] = SR;\r
949 break;\r
950\r
951 case ioDIB: /* int ack */\r
952 AC[dstAC] = 0;\r
953 DEV_UPDATE_INTR ;\r
954 iodata = int_req & (-int_req);\r
955 for (i = DEV_LOW; i <= DEV_HIGH; i++) {\r
956 if (iodata & dev_table[i].mask) {\r
957 AC[dstAC] = i;\r
958 break;\r
959 }\r
960 }\r
961 break;\r
962\r
963 case ioDOB: /* mask out */\r
964 mask_out (pimask = AC[dstAC]);\r
965 break;\r
966\r
967 case ioDIC: /* io reset */\r
968 reset_all (0); /* reset devices */\r
969 mask_out( 0 ) ; /* clear all device masks */\r
970 AMASK = 077777 ; /* reset memory mode */\r
971 break;\r
972\r
973 case ioDOC: /* halt */\r
974 reason = STOP_HALT;\r
975 break;\r
976 } /* end switch code */\r
977\r
978 switch (pulse) { /* decode IR<8:9> */\r
979\r
980 case iopS: /* ion */\r
981 int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING;\r
982 break;\r
983\r
984 case iopC: /* iof */\r
985 int_req = int_req & ~INT_ION;\r
986 break;\r
987 } /* end switch pulse */\r
988 } /* end CPU control */\r
989\r
990 else if (dev_table[device].routine) { /* normal device */\r
991 iodata = dev_table[device].routine (pulse, code, AC[dstAC]);\r
992 reason = iodata >> IOT_V_REASON;\r
993 if (code & 1) AC[dstAC] = iodata & 0177777;\r
994 }\r
995\r
996/* bkr, 2007-May-30\r
997 * if device does not exist certain I/O instructions will still\r
998 * return data: DIA/B/C will return idle data bus value and\r
999 * SKPBZ/SKPDZ will sense zero value (and will therefore skip).\r
1000 *\r
1001 * Perform these non-supported device functions only if 'stop_dev'\r
1002 * is zero (i.e. I/O access trap is not in effect).\r
1003 */\r
1004 else if ( stop_dev == 0 )\r
1005 {\r
1006 switch (code) /* decode IR<5:7> */\r
1007 {\r
1008 case ioDIA:\r
1009 case ioDIB:\r
1010 case ioDIC:\r
1011 AC[dstAC] = 0 ; /* idle I/O bus data */\r
1012 break;\r
1013\r
1014 case ioSKP:\r
1015 /* (This should have been caught in previous CPU skip code) */\r
1016 if ( (pulse == 1 /* SKPBZ */) || (pulse == 3 /* SKPDZ */) )\r
1017 {\r
1018 INCREMENT_PC ;\r
1019 }\r
1020 } /* end of 'switch' */\r
1021 } /* end of handling non-existant device */\r
1022 else reason = stop_dev;\r
1023 } /* end if IOT */\r
1024 } /* end while */\r
1025\r
1026/* Simulation halted */\r
1027\r
1028saved_PC = PC;\r
1029pcq_r->qptr = pcq_p; /* update pc q ptr */\r
1030return ( reason ) ;\r
1031}\r
1032\r
1033/* New priority mask out */\r
1034\r
1035void mask_out (int32 newmask)\r
1036{\r
1037int32 i;\r
1038\r
1039dev_disable = 0;\r
1040for (i = DEV_LOW; i <= DEV_HIGH; i++) {\r
1041 if (newmask & dev_table[i].pi)\r
1042 dev_disable = dev_disable | dev_table[i].mask;\r
1043 }\r
1044DEV_UPDATE_INTR ;\r
1045return;\r
1046}\r
1047\r
1048/* Reset routine */\r
1049\r
1050t_stat cpu_reset (DEVICE *dptr)\r
1051{\r
1052int_req = int_req & ~(INT_ION | INT_STK | INT_TRAP);\r
1053pimask = 0;\r
1054dev_disable = 0;\r
1055pwr_low = 0;\r
1056AMASK = 077777 ; /* 32KW mode */\r
1057pcq_r = find_reg ("PCQ", NULL, dptr);\r
1058if (pcq_r) pcq_r->qptr = 0;\r
1059else return SCPE_IERR;\r
1060sim_brk_types = sim_brk_dflt = SWMASK ('E');\r
1061return SCPE_OK;\r
1062}\r
1063\r
1064/* Memory examine */\r
1065\r
1066t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)\r
1067{\r
1068if (addr >= MEMSIZE) return SCPE_NXM;\r
1069if (vptr != NULL) *vptr = M[addr] & DMASK;\r
1070return SCPE_OK;\r
1071}\r
1072\r
1073/* Memory deposit */\r
1074\r
1075t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)\r
1076{\r
1077if (addr >= MEMSIZE) return SCPE_NXM;\r
1078M[addr] = val & DMASK;\r
1079return SCPE_OK;\r
1080}\r
1081\r
1082/* Alter memory size */\r
1083\r
1084t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)\r
1085{\r
1086int32 mc = 0;\r
1087t_addr i;\r
1088\r
1089if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0))\r
1090 return SCPE_ARG;\r
1091for (i = val; i < MEMSIZE; i++) mc = mc | M[i];\r
1092if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))\r
1093 return SCPE_OK;\r
1094MEMSIZE = val;\r
1095for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0;\r
1096return SCPE_OK;\r
1097}\r
1098\r
1099/* Build dispatch table */\r
1100\r
1101t_stat build_devtab (void)\r
1102{\r
1103DEVICE *dptr;\r
1104DIB *dibp;\r
1105int32 i, dn;\r
1106\r
1107for (i = 0; i < 64; i++) { /* clr dev_table */\r
1108 dev_table[i].mask = 0;\r
1109 dev_table[i].pi = 0;\r
1110 dev_table[i].routine = NULL;\r
1111 }\r
1112for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */\r
1113 if (!(dptr->flags & DEV_DIS) && /* enabled and */\r
1114 ( (dibp = (DIB *) dptr->ctxt)) ) { /* defined DIB? */\r
1115 dn = dibp->dnum; /* get dev num */\r
1116 dev_table[dn].mask = dibp->mask; /* copy entries */\r
1117 dev_table[dn].pi = dibp->pi;\r
1118 dev_table[dn].routine = dibp->routine;\r
1119 }\r
1120 }\r
1121return SCPE_OK;\r
1122}\r
1123\r
1124/* BKR notes:\r
1125 *\r
1126 * Data General APL (Automatic Program Load) boot code\r
1127 *\r
1128 * - This bootstrap code is called the "APL option" in DG documentation (Automatic\r
1129 * Program Load), and cost ~$400 USD (in 1970 - wow!) to load 32(10) words from\r
1130 * a PROM to main (core) memory location 0 - 32.\r
1131 * - This code is documented in various DG Nova programming manuals and was\r
1132 * quite static (i.e. no revisions or updates to code were made). \r
1133 * - switch register is used to determine device code and device type.\r
1134 * - lower 6-bits of switch register determines device code (0-63.).\r
1135 * - most significant bit determines if device is "low speed" or "high speed".\r
1136 * - "high speed" devices have effective boot program logic of:\r
1137 *\r
1138 * IORST\r
1139 * NIOS <device>\r
1140 * JMP .\r
1141 *\r
1142 * - "high speed" devices use data channel (DCH) to read first sector/record\r
1143 * of device into memory (usually starting at location 0), which then over-writes\r
1144 * the 'JMP .' instruction of boot code. This usually has a jump to some other\r
1145 * device and operating system specific boot code that was loaded from the device.\r
1146 * - "low speed" devices are assumed to be sequential character-oriented devices\r
1147 * (i.e. Teletype (r) reader, paper tape reader).\r
1148 * - "low speed" devices are assumed to start read operations with a 'S' pulse,\r
1149 * read data buffer with a DIA instruction and have standard DG I/O Busy/Done logic.\r
1150 * - "low speed" devices usually read in a more full-featured 'binary loader' with\r
1151 * the APL boot code:\r
1152 *\r
1153 * DG paper tape: 091-000004-xx, Binary Loader (BLDR.AB)\r
1154 *\r
1155 * - The Binary Loader was in turn used to load tapes in the usual DG 'absolute binary' format.\r
1156 */\r
1157\r
1158#define BOOT_START 00000\r
1159#define BOOT_LEN (sizeof(boot_rom) / sizeof(int32))\r
1160\r
1161static const int32 boot_rom[] = {\r
1162 0062677, /* IORST ;reset all I/O */\r
1163 0060477, /* READS 0 ;read SR into AC0 */\r
1164 0024026, /* LDA 1,C77 ;get dev mask */\r
1165 0107400, /* AND 0,1 ;isolate dev code */\r
1166 0124000, /* COM 1,1 ;- device code - 1 */\r
1167 0010014, /* LOOP: ISZ OP1 ;device code to all */\r
1168 0010030, /* ISZ OP2 ;I/O instructions */\r
1169 0010032, /* ISZ OP3 */\r
1170 0125404, /* INC 1,1,SZR ;done? */\r
1171 0000005, /* JMP LOOP ;no, increment again */\r
1172 0030016, /* LDA 2,C377 ;place JMP 377 into */\r
1173 0050377, /* STA 2,377 ;location 377 */\r
1174 0060077, /* OP1: 060077 ;start device (NIOS 0) */\r
1175 0101102, /* MOVL 0,0,SZC ;test switch 0, low speed? */\r
1176 0000377, /* C377: JMP 377 ;no - jmp 377 & wait */\r
1177 0004030, /* LOOP2: JSR GET+1 ;get a frame */\r
1178 0101065, /* MOVC 0,0,SNR ;is it non-zero? */\r
1179 0000017, /* JMP LOOP2 ;no, ignore */\r
1180 0004027, /* LOOP4: JSR GET ;yes, get full word */\r
1181 0046026, /* STA 1,@C77 ;store starting at 100 */\r
1182 /* ;2's complement of word ct */\r
1183 0010100, /* ISZ 100 ;done? */\r
1184 0000022, /* JMP LOOP4 ;no, get another */\r
1185 0000077, /* C77: JMP 77 ;yes location ctr and */\r
1186 /* ;jmp to last word */\r
1187 0126420, /* GET: SUBZ 1,1 ; clr AC1, set carry */\r
1188 /* OP2: */\r
1189 0063577, /* LOOP3: 063577 ;done? (SKPDN 0) - 1 */\r
1190 0000030, /* JMP LOOP3 ;no -- wait */\r
1191 0060477, /* OP3: 060477 ;y -- read in ac0 (DIAS 0,0) */\r
1192 0107363, /* ADDCS 0,1,SNC ;add 2 frames swapped - got 2nd? */\r
1193 0000030, /* JMP LOOP3 ;no go back after it */\r
1194 0125300, /* MOVS 1,1 ;yes swap them */\r
1195 0001400, /* JMP 0,3 ;rtn with full word */\r
1196 0000000 /* 0 ;padding */\r
1197 };\r
1198\r
1199t_stat cpu_boot (int32 unitno, DEVICE *dptr)\r
1200{\r
1201int32 i;\r
1202\r
1203for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];\r
1204saved_PC = BOOT_START;\r
1205return SCPE_OK;\r
1206}\r
1207\r
1208/* 1-to-1 map for I/O devices */\r
1209\r
1210int32 MapAddr (int32 map, int32 addr)\r
1211{\r
1212return addr;\r
1213}\r
1214\r
1215/* History subsystem\r
1216\r
1217global routines\r
1218\r
1219t_stat hist_set( UNIT * uptr, int32 val, char * cptr, void * desc, void ** HistCookie, sizeof(usrHistInfo) ) ;\r
1220t_stat hist_show( FILE * st, UNIT * uptr, int32 val, void * desc, void * HistCookie ) ;\r
1221int hist_save( int32 next_pc, int32 our_ir, void * usrHistInfo )\r
1222\r
1223local user struct:\r
1224\r
1225usrHistInfo\r
1226\r
1227local user routines:\r
1228\r
1229int uHist_save( int32 next_pc, int32 our_ir, void * usrHistInfo ) ;\r
1230int uHist_fprintf( FILE * fp, int itemNum, void * usrHistInfo ) ;\r
1231\r
1232typedef struct\r
1233 {\r
1234 int hMax ; // total # entries in queue (0 = inactive)\r
1235 int hCount ; // current entry\r
1236 void * hPtr ; // pointer to save area\r
1237 int hSize ; // size of each user save area (not used by global routines?)\r
1238 } Hist_info ;\r
1239 */\r
1240\r
1241/* generalized CPU execution trace */\r
1242\r
1243#define HIST_IR_INVALID -1\r
1244#define HIST_MIN 0 /* 0 == deactivate history feature, else size of queue */\r
1245#define HIST_MAX 1000000 /* completely arbitrary max size value */\r
1246\r
1247/* save history entry (proposed local routine) */\r
1248\r
1249static int hist_save( int32 pc, int32 our_ir )\r
1250{\r
1251Hist_entry * hist_ptr ;\r
1252\r
1253if ( hist )\r
1254 if ( hist_cnt )\r
1255 {\r
1256 hist_p = (hist_p + 1) ; /* next entry */\r
1257 if ( hist_p >= hist_cnt )\r
1258 {\r
1259 hist_p = 0 ;\r
1260 }\r
1261 hist_ptr = &hist[ hist_p ] ;\r
1262\r
1263 /* (machine-specific stuff) */\r
1264\r
1265 hist_ptr->pc = pc ;\r
1266 hist_ptr->ir = our_ir ;\r
1267 hist_ptr->ac0 = AC[ 0 ] ;\r
1268 hist_ptr->ac1 = AC[ 1 ] ;\r
1269 hist_ptr->ac2 = AC[ 2 ] ;\r
1270 hist_ptr->ac3 = AC[ 3 ] ;\r
1271 hist_ptr->carry = C ;\r
1272 hist_ptr->fp = FP ;\r
1273 hist_ptr->sp = SP ;\r
1274 hist_ptr->devBusy = dev_busy ;\r
1275 hist_ptr->devDone = dev_done ;\r
1276 hist_ptr->devDisable = dev_disable ;\r
1277 hist_ptr->devIntr = int_req ;\r
1278 /* how 'bout state and AMASK? */\r
1279 return ( hist_p ) ;\r
1280 }\r
1281return ( -1 ) ;\r
1282} /* end of 'hist_save' */\r
1283\r
1284/* setup history save area (proposed global routine) */\r
1285\r
1286t_stat hist_set( UNIT * uptr, int32 val, char * cptr, void * desc )\r
1287{\r
1288int32 i, lnt ;\r
1289t_stat r ;\r
1290\r
1291if ( cptr == NULL )\r
1292 {\r
1293 for (i = 0 ; i < hist_cnt ; ++i )\r
1294 {\r
1295 hist[i].pc = 0 ;\r
1296 hist[i].ir = HIST_IR_INVALID ;\r
1297 }\r
1298 hist_p = 0 ;\r
1299 return ( SCPE_OK ) ;\r
1300 }\r
1301lnt = (int32) get_uint(cptr, 10, HIST_MAX, &r) ;\r
1302if ( (r != SCPE_OK) || (lnt && (lnt < HIST_MIN)) )\r
1303 {\r
1304 return ( SCPE_ARG ) ;\r
1305 }\r
1306hist_p = 0;\r
1307if ( hist_cnt )\r
1308 {\r
1309 free( hist ) ;\r
1310 hist_cnt = 0 ;\r
1311 hist = NULL ;\r
1312 }\r
1313if ( lnt )\r
1314 {\r
1315 hist = (Hist_entry *) calloc( lnt, sizeof(Hist_entry) ) ;\r
1316 if ( hist == NULL )\r
1317 {\r
1318 return ( SCPE_MEM ) ;\r
1319 }\r
1320 hist_cnt = lnt ;\r
1321 }\r
1322return ( SCPE_OK ) ;\r
1323} /* end of 'hist_set' */\r
1324\r
1325\r
1326int hist_fprintf( FILE * fp, int itemNum, Hist_entry * hptr )\r
1327{\r
1328t_value sim_eval ;\r
1329\r
1330if ( hptr )\r
1331 {\r
1332 if ( itemNum == 0 )\r
1333 {\r
1334 fprintf( fp, "\n\n" ) ;\r
1335 }\r
1336 fprintf( fp, "%05o / %06o %06o %06o %06o %06o %o ",\r
1337 (hptr->pc & 0x7FFF),\r
1338 (hptr->ir & 0xFFFF),\r
1339 (hptr->ac0 & 0xFFFF),\r
1340 (hptr->ac1 & 0xFFFF),\r
1341 (hptr->ac2 & 0xFFFF),\r
1342 (hptr->ac3 & 0xFFFF),\r
1343 ((hptr->carry) ? 1 : 0)\r
1344 ) ;\r
1345 if ( cpu_unit.flags & UNIT_STK /* Nova 3 or Nova 4 */ ) \r
1346 {\r
1347 fprintf( fp, "%06o %06o ", SP, FP ) ;\r
1348 }\r
1349\r
1350 sim_eval = (hptr->ir & 0xFFFF) ;\r
1351 if ( (fprint_sym(fp, (hptr->pc & AMASK), &sim_eval, &cpu_unit, SWMASK ('M'))) > 0 )\r
1352 {\r
1353 fprintf( fp, "(undefined) %04o", (hptr->ir & 0xFFFF) ) ;\r
1354 }\r
1355 /*\r
1356 display ION flag value, pend value?\r
1357 display devBusy, devDone, devIntr info?\r
1358 */\r
1359\r
1360 if ( 0 ) /* display INTRP codes? */\r
1361 {\r
1362 char tmp[ 500 ] ;\r
1363\r
1364 devBitNames( hptr->devIntr, tmp, NULL ) ;\r
1365 fprintf( fp, " %s", tmp ) ;\r
1366 }\r
1367\r
1368 fprintf( fp, "\n" ) ;\r
1369 }\r
1370return ( 0 ) ;\r
1371} /* end of 'hist_fprintf' */\r
1372\r
1373\r
1374/* show execution history (proposed global routine) */\r
1375\r
1376t_stat hist_show( FILE * st, UNIT * uptr, int32 val, void * desc )\r
1377{\r
1378int32 k, di, lnt ;\r
1379char * cptr = (char *) desc ;\r
1380t_stat r ;\r
1381Hist_entry * hptr ;\r
1382\r
1383\r
1384if (hist_cnt == 0)\r
1385 {\r
1386 return ( SCPE_NOFNC ) ; /* enabled? */\r
1387 }\r
1388if ( cptr )\r
1389 { /* number of entries specified */\r
1390 lnt = (int32) get_uint( cptr, 10, hist_cnt, &r ) ;\r
1391 if ( (r != SCPE_OK) || (lnt == 0) )\r
1392 {\r
1393 return ( SCPE_ARG ) ;\r
1394 }\r
1395 }\r
1396 else\r
1397 {\r
1398 lnt = hist_cnt ; /* display all entries */\r
1399 }\r
1400 di = hist_p - lnt; /* work forward */\r
1401 if ( di < 0 )\r
1402 {\r
1403 di = di + hist_cnt ;\r
1404 }\r
1405\r
1406for ( k = 0 ; k < lnt ; ++k )\r
1407 { /* print specified */\r
1408 hptr = &hist[ (++di) % hist_cnt] ; /* entry pointer */\r
1409 if ( hptr->ir != HIST_IR_INVALID ) /* valid entry? */\r
1410 {\r
1411 hist_fprintf( st, k, hptr ) ;\r
1412 } /* end else instruction */\r
1413 } /* end for */\r
1414return SCPE_OK;\r
1415} /* end of 'hist_show' */\r
1416\r
1417\r
1418\r
1419struct Dbits\r
1420 {\r
1421 int32 dBit ;\r
1422 int32 dInvertMask ;\r
1423 char * dName ;\r
1424 } devBits [] =\r
1425\r
1426 {\r
1427 { INT_TRAP, 0, "TRAP" }, /* (in order of approximate DG interrupt mask priority) */\r
1428 { INT_ION, 0, "ION" },\r
1429 { INT_NO_ION_PENDING, 1, "IONPND" }, /* (invert this logic to provide cleaner display) */\r
1430 { INT_STK, 0, "STK" },\r
1431 { INT_PIT, 0, "PIT" },\r
1432 { INT_DKP, 0, "DKP" },\r
1433 { INT_DSK, 0, "DSK" },\r
1434 { INT_MTA, 0, "MTA" },\r
1435 { INT_LPT, 0, "LPT" },\r
1436 { INT_PTR, 0, "PTR" },\r
1437 { INT_PTP, 0, "PTP" },\r
1438 { INT_PLT, 0, "PLT" },\r
1439 { INT_CLK, 0, "CLK" },\r
1440 { INT_ALM, 0, "ALM" },\r
1441 { INT_QTY, 0, "QTY" },\r
1442 { INT_TTO1, 0, "TTO1" },\r
1443 { INT_TTI1, 0, "TTI1" },\r
1444 { INT_TTO, 0, "TTO" },\r
1445 { INT_TTI, 0, "TTI" },\r
1446 { 0, 0, NULL }\r
1447 } ;\r
1448\r
1449\r
1450char * devBitNames( int32 flags, char * ptr, char * sepStr )\r
1451{\r
1452int a ;\r
1453\r
1454if ( ptr )\r
1455 {\r
1456 *ptr = 0 ;\r
1457 for ( a = 0 ; (devBits[a].dBit) ; ++a )\r
1458 if ( devBits[a].dBit & ((devBits[a].dInvertMask)? ~flags : flags) )\r
1459 {\r
1460 if ( *ptr )\r
1461 {\r
1462 strcat( ptr, (sepStr) ? sepStr : " " ) ;\r
1463 strcat( ptr, devBits[a].dName ) ;\r
1464 }\r
1465 else\r
1466 {\r
1467 strcpy( ptr, devBits[a].dName ) ;\r
1468 }\r
1469 }\r
1470 }\r
1471return ( ptr ) ;\r
1472} /* end of 'devBitNames' */\r