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