1 /* nova_cpu.c: NOVA CPU simulator
3 Copyright (c) 1993-2008, Robert M. Supnik
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:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
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.
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.
26 cpu Nova central processor
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
63 The register state for the NOVA CPU is:
65 AC[0:3]<0:15> general registers
67 PC<0:14> program counter
69 The NOVA has three instruction formats: memory reference, I/O transfer,
70 and operate. The memory reference format is:
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 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
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
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>)]
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
105 The I/O transfer format is:
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 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
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.
116 The operate format is:
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,
135 +--- operation (complement,
144 The operate instruction can be microprogrammed to perform operations
145 on the source and destination AC's and the Carry flag.
147 Some notes from Bruce Ray:
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)
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.
159 3. Nova 3 adds the following instructions to the standard Nova
163 stack push/pop instructions
164 save/return instructions
165 stack register manipulation instructions
168 4. Nova 4 adds the following instructions to the Nova 3 instruction
173 secret (undocumented) stack instructions [PSHN, SAVN]
175 5. Nova, Nova 3 and Nova 4 unsigned mul/div instructions are the
176 same instruction code values on all machines.
178 6. Undocumented Nova 3 behaviour for LDB, STB and SAVN has been
179 added to appropriate code.
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.
191 This feature has been implemented in our Nova emulation for all to enjoy.
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.
201 1. Reasons to stop. The simulator can be stopped by:
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
209 2. Interrupts. Interrupts are maintained by four parallel variables:
211 dev_done device done flags
212 dev_disable device interrupt disable flags
213 dev_busy device busy flags
214 int_req interrupt requests
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.
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.
226 4. Adding I/O devices. These modules must be modified:
228 nova_defs.h add interrupt request definition
229 nova_sys.c add sim_devices entry
232 #include "nova_defs.h"
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
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; \
249 M[x] = (M[x] - 1) & DMASK; \
252 #define INCREMENT_PC PC = (PC + 1) & AMASK /* increment PC */
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)
269 #define MODE_64K (cpu_unit.flags & UNIT_64KW)
270 #define MODE_64K_ACTIVE ((cpu_unit.flags & UNIT_64KW) && (0xFFFF == AMASK))
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 */
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);
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
) ;
329 void mask_out (int32 mask
);
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
);
340 /* CPU data structures
342 cpu_dev CPU device descriptor
343 cpu_unit CPU unit descriptor
344 cpu_reg CPU register list
345 cpu_mod CPU modifiers list
349 UDATA (NULL
, UNIT_FIX
+UNIT_BINK
+UNIT_MDV
, DFTMEMSIZE
/* MAXMEMSIZE */ )
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) },
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
},
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
},
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
,
418 t_stat
sim_instr (void)
423 /* Restore register state */
425 if (build_devtab () != SCPE_OK
) return SCPE_IERR
; /* build dispatch */
426 PC
= saved_PC
& AMASK
; /* load local PC */
428 mask_out (pimask
); /* reset int system */
431 /* Main instruction fetch/decode loop */
433 while (reason
== 0) { /* loop until halted */
435 if (sim_interval
<= 0) { /* check clock queue */
436 if ( (reason
= sim_process_event ()) ) break;
439 if (int_req
> INT_PENDING
) { /* interrupt or exception? */
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 */
449 int_req
= int_req
& ~INT_ION
; /* intr off */
450 PCQ_ENTRY
; /* save old PC */
452 if (int_req
& INT_STK
) { /* stack overflow? */
453 int_req
= int_req
& ~INT_STK
; /* clear */
454 MA
= STK_JMP
; /* jmp @3 */
457 MA
= INT_JMP
; /* intr: jmp @1 */
459 if ( MODE_64K_ACTIVE
) {
460 indf
= IND_STEP (MA
);
464 for (i
= 0, indf
= 1; indf
&& (i
< ind_max
); i
++) {
465 indf
= IND_STEP (MA
); /* indirect loop */
468 reason
= STOP_IND_INT
;
473 } /* end interrupt */
475 if (sim_brk_summ
&& sim_brk_test (PC
, SWMASK ('E'))) { /* breakpoint? */
476 reason
= STOP_IBKPT
; /* stop simulation */
480 IR
= M
[PC
]; /* fetch instr */
483 hist_save( PC
, IR
) ; /* PC, int_req unchanged */
487 int_req
= int_req
| INT_NO_ION_PENDING
; /* clear ION delay */
488 sim_interval
= sim_interval
- 1;
490 /* Operate instruction */
492 if (IR
& I_OPR
) { /* operate? */
493 int32 src
, srcAC
, dstAC
;
495 srcAC
= I_GETSRC (IR
); /* get reg decodes */
496 dstAC
= I_GETDST (IR
);
497 switch (I_GETCRY (IR
)) { /* decode carry */
505 src
= AC
[srcAC
] | CBIT
;
507 case 3: /* complement */
508 src
= AC
[srcAC
] | (C
^ CBIT
);
510 } /* end switch carry */
512 switch (I_GETALU (IR
)) { /* decode ALU */
517 src
= ((src
^ DMASK
) + 1) & CDMASK
;
522 src
= (src
+ 1) & CDMASK
;
525 src
= ((src
^ DMASK
) + AC
[dstAC
]) & CDMASK
;
528 src
= ((src
^ DMASK
) + AC
[dstAC
] + 1) & CDMASK
;
531 src
= (src
+ AC
[dstAC
]) & CDMASK
;
534 src
= src
& (AC
[dstAC
] | CBIT
);
536 } /* end switch oper */
538 switch (I_GETSHF (IR
)) { /* decode shift */
542 src
= ((src
<< 1) | (src
>> 16)) & CDMASK
;
545 src
= ((src
>> 1) | (src
<< 16)) & CDMASK
;
548 src
= ((src
& 0377) << 8) | ((src
>> 8) & 0377) |
551 } /* end switch shift */
553 switch (I_GETSKP (IR
)) { /* decode skip */
555 if ((IR
& I_NLD
) && (cpu_unit
.flags
& UNIT_STK
)) {
556 int_req
= int_req
| INT_TRAP
; /* Nova 3 or 4 trap */
564 if (src
< CBIT
) INCREMENT_PC
;
567 if (src
>= CBIT
) INCREMENT_PC
;
570 if ((src
& DMASK
) == 0) INCREMENT_PC
;
573 if ((src
& DMASK
) != 0) INCREMENT_PC
;
576 if (src
<= CBIT
) INCREMENT_PC
;
579 if (src
> CBIT
) INCREMENT_PC
;
581 } /* end switch skip */
582 if ((IR
& I_NLD
) == 0) { /* load? */
583 AC
[dstAC
] = src
& DMASK
;
586 } /* end if operate */
588 /* Memory reference instructions */
590 else if (IR
< 060000) { /* mem ref? */
593 MA
= I_GETDISP (IR
); /* get disp */
594 switch (I_GETMODE (IR
)) { /* decode mode */
595 case 0: /* page zero */
597 case 1: /* PC relative */
598 if (MA
& DISPSIGN
) MA
= 0177400 | MA
;
599 MA
= (MA
+ PC
- 1) & AMASK
;
601 case 2: /* AC2 relative */
602 if (MA
& DISPSIGN
) MA
= 0177400 | MA
;
603 MA
= (MA
+ AC
[2]) & AMASK
;
605 case 3: /* AC3 relative */
606 if (MA
& DISPSIGN
) MA
= 0177400 | MA
;
607 MA
= (MA
+ AC
[3]) & AMASK
;
609 } /* end switch mode */
611 if ( (indf
= IR
& I_IND
) ) { /* indirect? */
612 if ( MODE_64K_ACTIVE
) { /* 64k mode? */
613 indf
= IND_STEP (MA
);
615 else /* compat mode */
617 for (i
= 0; indf
&& (i
< ind_max
); i
++) { /* count */
618 indf
= IND_STEP (MA
); /* resolve indirect */
620 if (i
>= ind_max
) { /* too many? */
627 switch (I_GETOPAC (IR
)) { /* decode op + AC */
635 src
= (M
[MA
] + 1) & DMASK
;
636 if (MEM_ADDR_OK(MA
)) M
[MA
] = src
;
637 if (src
== 0) INCREMENT_PC
;
640 src
= (M
[MA
] - 1) & DMASK
;
641 if (MEM_ADDR_OK(MA
)) M
[MA
] = src
;
642 if (src
== 0) INCREMENT_PC
;
644 case 004: /* LDA 0 */
647 case 005: /* LDA 1 */
650 case 006: /* LDA 2 */
653 case 007: /* LDA 3 */
656 case 010: /* STA 0 */
657 if (MEM_ADDR_OK(MA
)) M
[MA
] = AC
[0];
659 case 011: /* STA 1 */
660 if (MEM_ADDR_OK(MA
)) M
[MA
] = AC
[1];
662 case 012: /* STA 2 */
663 if (MEM_ADDR_OK(MA
)) M
[MA
] = AC
[2];
665 case 013: /* STA 3 */
666 if (MEM_ADDR_OK(MA
)) M
[MA
] = AC
[3];
671 /* IOT instruction */
674 int32 dstAC
, pulse
, code
, device
, iodata
;
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> */
683 case 0: /* skip if busy */
684 if ((device
== DEV_CPU
)? (int_req
& INT_ION
) != 0:
685 (dev_busy
& dev_table
[device
].mask
) != 0)
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)
695 case 2: /* skip if done */
696 if ((device
== DEV_CPU
)? pwr_low
!= 0:
697 (dev_done
& dev_table
[device
].mask
) != 0)
701 case 3: /* skip if not done */
702 if ((device
== DEV_CPU
)? pwr_low
== 0:
703 (dev_done
& dev_table
[device
].mask
) == 0)
709 /* Hmm, this means a Nova 3 _must_ have DEV_MDV enabled - not true in DG land */
711 else if (device
== DEV_MDV
) {
712 switch (code
) { /* case on opcode */
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
;
721 case ioDIA
: /* load byte */
722 if (cpu_unit
.flags
& UNIT_BYT
)
724 AC
[dstAC
] = (M
[AC
[pulse
] >> 1] >> ((AC
[pulse
] & 1)? 0: 8)) & 0377 ;
726 else if (cpu_unit
.flags
& UNIT_STK
) /* if Nova 3 this is really a SAV... 2007-Jun-01, BKR */
729 if (MEM_ADDR_OK (SP
)) M
[SP
] = AC
[0];
731 if (MEM_ADDR_OK (SP
)) M
[SP
] = AC
[1];
733 if (MEM_ADDR_OK (SP
)) M
[SP
] = AC
[2];
735 if (MEM_ADDR_OK (SP
)) M
[SP
] = FP
;
737 if (MEM_ADDR_OK (SP
)) M
[SP
] = (C
>> 1) | (AC
[3] & AMASK
);
738 AC
[3] = FP
= SP
& AMASK
;
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
;
754 case ioDIB
: /* push, pop */
755 if (cpu_unit
.flags
& UNIT_STK
) {
756 if (pulse
== iopN
) { /* push (PSHA) */
758 if (MEM_ADDR_OK (SP
)) M
[SP
] = AC
[dstAC
];
761 if ((pulse
== iopS
) && /* Nova 4 pshn (PSHN) */
762 (cpu_unit
.flags
& UNIT_BYT
)) {
764 if (MEM_ADDR_OK (SP
)) M
[SP
] = AC
[dstAC
];
765 if ( (SP
& 0xFFFF) > (M
[042] & 0xFFFF) )
767 int_req
= int_req
| INT_STK
;
770 if (pulse
== iopC
) { /* pop (POPA) */
777 case ioDOB
: /* store byte */
778 if (cpu_unit
.flags
& UNIT_BYT
)
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));
787 else if (cpu_unit
.flags
& UNIT_STK
) /* if Nova 3 this is really a SAV... 2007-Jun-01, BKR */
790 if (MEM_ADDR_OK (SP
)) M
[SP
] = AC
[0];
792 if (MEM_ADDR_OK (SP
)) M
[SP
] = AC
[1];
794 if (MEM_ADDR_OK (SP
)) M
[SP
] = AC
[2];
796 if (MEM_ADDR_OK (SP
)) M
[SP
] = FP
;
798 if (MEM_ADDR_OK (SP
)) M
[SP
] = (C
>> 1) | (AC
[3] & AMASK
);
799 AC
[3] = FP
= SP
& AMASK
;
804 case ioDIC
: /* save, return */
805 if (cpu_unit
.flags
& UNIT_STK
) {
806 if (pulse
== iopN
) { /* save */
808 if (MEM_ADDR_OK (SP
)) M
[SP
] = AC
[0];
810 if (MEM_ADDR_OK (SP
)) M
[SP
] = AC
[1];
812 if (MEM_ADDR_OK (SP
)) M
[SP
] = AC
[2];
814 if (MEM_ADDR_OK (SP
)) M
[SP
] = FP
;
816 if (MEM_ADDR_OK (SP
)) M
[SP
] = (C
>> 1) | (AC
[3] & AMASK
);
817 AC
[3] = FP
= SP
& AMASK
;
820 else if (pulse
== iopC
) { /* retn */
823 C
= (M
[SP
] << 1) & CBIT
;
836 else if ((pulse
== iopS
) && /* Nova 4 SAVN */
837 (cpu_unit
.flags
& UNIT_BYT
)) {
838 int32 frameSz
= M
[PC
] ;
841 if (MEM_ADDR_OK (SP
)) M
[SP
] = AC
[0];
843 if (MEM_ADDR_OK (SP
)) M
[SP
] = AC
[1];
845 if (MEM_ADDR_OK (SP
)) M
[SP
] = AC
[2];
847 if (MEM_ADDR_OK (SP
)) M
[SP
] = FP
;
849 if (MEM_ADDR_OK (SP
)) M
[SP
] = (C
>> 1) | (AC
[3] & AMASK
);
850 AC
[3] = FP
= SP
& AMASK
;
851 SP
= (SP
+ frameSz
) & AMASK
;
854 int_req
= int_req
| INT_STK
;
861 if ((dstAC
== 2) && (cpu_unit
.flags
& UNIT_MDV
))
862 { /* Nova, Nova3 or Nova 4 */
863 uint32 mddata
, uAC0
, uAC1
, uAC2
;
865 uAC0
= (uint32
) AC
[0];
866 uAC1
= (uint32
) AC
[1];
867 uAC2
= (uint32
) AC
[2];
870 mddata
= (uAC1
* uAC2
) + uAC0
;
871 AC
[0] = (mddata
>> 16) & DMASK
;
872 AC
[1] = mddata
& DMASK
;
876 if ((uAC0
>= uAC2
) || (uAC2
== 0))
883 mddata
= (uAC0
<< 16) | uAC1
;
884 AC
[1] = mddata
/ uAC2
;
885 AC
[0] = mddata
% uAC2
;
889 else if ((dstAC
== 3) && (cpu_unit
.flags
& UNIT_BYT
) /* assuming UNIT_BYT = Nova 4 */)
894 mddata
= (SEXT (AC
[1]) * SEXT (AC
[2])) + SEXT (AC
[0]);
895 AC
[0] = (mddata
>> 16) & DMASK
;
896 AC
[1] = mddata
& DMASK
;
898 else if (pulse
== iopN
)
900 if ((AC
[2] == 0) || /* overflow? */
901 ((AC
[0] == 0100000) && (AC
[1] == 0) && (AC
[2] == 0177777)))
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))
918 AC
[0] = AC
[0] & DMASK
;
922 else if ((dstAC
== 3) && (cpu_unit
.flags
& UNIT_STK
)) /* if Nova 3 this is really a PSHA... 2007-Jun-01, BKR */
925 if (MEM_ADDR_OK (SP
)) M
[SP
] = AC
[dstAC
];
929 } /* end case code */
930 } /* end if mul/div */
932 else if (device
== DEV_CPU
) { /* CPU control */
933 switch (code
) { /* decode IR<5:7> */
935 case ioNIO
: /* NIOP <x> CPU ? */
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 ;
947 case ioDIA
: /* read switches */
951 case ioDIB
: /* int ack */
954 iodata
= int_req
& (-int_req
);
955 for (i
= DEV_LOW
; i
<= DEV_HIGH
; i
++) {
956 if (iodata
& dev_table
[i
].mask
) {
963 case ioDOB
: /* mask out */
964 mask_out (pimask
= AC
[dstAC
]);
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 */
973 case ioDOC
: /* halt */
976 } /* end switch code */
978 switch (pulse
) { /* decode IR<8:9> */
981 int_req
= (int_req
| INT_ION
) & ~INT_NO_ION_PENDING
;
985 int_req
= int_req
& ~INT_ION
;
987 } /* end switch pulse */
988 } /* end CPU control */
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;
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).
1001 * Perform these non-supported device functions only if 'stop_dev'
1002 * is zero (i.e. I/O access trap is not in effect).
1004 else if ( stop_dev
== 0 )
1006 switch (code
) /* decode IR<5:7> */
1011 AC
[dstAC
] = 0 ; /* idle I/O bus data */
1015 /* (This should have been caught in previous CPU skip code) */
1016 if ( (pulse
== 1 /* SKPBZ */) || (pulse
== 3 /* SKPDZ */) )
1020 } /* end of 'switch' */
1021 } /* end of handling non-existant device */
1022 else reason
= stop_dev
;
1026 /* Simulation halted */
1029 pcq_r
->qptr
= pcq_p
; /* update pc q ptr */
1033 /* New priority mask out */
1035 void mask_out (int32 newmask
)
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
;
1050 t_stat
cpu_reset (DEVICE
*dptr
)
1052 int_req
= int_req
& ~(INT_ION
| INT_STK
| INT_TRAP
);
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');
1064 /* Memory examine */
1066 t_stat
cpu_ex (t_value
*vptr
, t_addr addr
, UNIT
*uptr
, int32 sw
)
1068 if (addr
>= MEMSIZE
) return SCPE_NXM
;
1069 if (vptr
!= NULL
) *vptr
= M
[addr
] & DMASK
;
1073 /* Memory deposit */
1075 t_stat
cpu_dep (t_value val
, t_addr addr
, UNIT
*uptr
, int32 sw
)
1077 if (addr
>= MEMSIZE
) return SCPE_NXM
;
1078 M
[addr
] = val
& DMASK
;
1082 /* Alter memory size */
1084 t_stat
cpu_set_size (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
1089 if ((val
<= 0) || (val
> MAXMEMSIZE
) || ((val
& 07777) != 0))
1091 for (i
= val
; i
< MEMSIZE
; i
++) mc
= mc
| M
[i
];
1092 if ((mc
!= 0) && (!get_yn ("Really truncate memory [N]?", FALSE
)))
1095 for (i
= MEMSIZE
; i
< MAXMEMSIZE
; i
++) M
[i
] = 0;
1099 /* Build dispatch table */
1101 t_stat
build_devtab (void)
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
;
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
;
1126 * Data General APL (Automatic Program Load) boot code
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:
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:
1153 * DG paper tape: 091-000004-xx, Binary Loader (BLDR.AB)
1155 * - The Binary Loader was in turn used to load tapes in the usual DG 'absolute binary' format.
1158 #define BOOT_START 00000
1159 #define BOOT_LEN (sizeof(boot_rom) / sizeof(int32))
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 */
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 */
1199 t_stat
cpu_boot (int32 unitno
, DEVICE
*dptr
)
1203 for (i
= 0; i
< BOOT_LEN
; i
++) M
[BOOT_START
+ i
] = boot_rom
[i
];
1204 saved_PC
= BOOT_START
;
1208 /* 1-to-1 map for I/O devices */
1210 int32
MapAddr (int32 map
, int32 addr
)
1215 /* History subsystem
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 )
1227 local user routines:
1229 int uHist_save( int32 next_pc, int32 our_ir, void * usrHistInfo ) ;
1230 int uHist_fprintf( FILE * fp, int itemNum, void * usrHistInfo ) ;
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?)
1241 /* generalized CPU execution trace */
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 */
1247 /* save history entry (proposed local routine) */
1249 static int hist_save( int32 pc
, int32 our_ir
)
1251 Hist_entry
* hist_ptr
;
1256 hist_p
= (hist_p
+ 1) ; /* next entry */
1257 if ( hist_p
>= hist_cnt
)
1261 hist_ptr
= &hist
[ hist_p
] ;
1263 /* (machine-specific stuff) */
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
;
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? */
1282 } /* end of 'hist_save' */
1284 /* setup history save area (proposed global routine) */
1286 t_stat
hist_set( UNIT
* uptr
, int32 val
, char * cptr
, void * desc
)
1293 for (i
= 0 ; i
< hist_cnt
; ++i
)
1296 hist
[i
].ir
= HIST_IR_INVALID
;
1299 return ( SCPE_OK
) ;
1301 lnt
= (int32
) get_uint(cptr
, 10, HIST_MAX
, &r
) ;
1302 if ( (r
!= SCPE_OK
) || (lnt
&& (lnt
< HIST_MIN
)) )
1304 return ( SCPE_ARG
) ;
1315 hist
= (Hist_entry
*) calloc( lnt
, sizeof(Hist_entry
) ) ;
1318 return ( SCPE_MEM
) ;
1322 return ( SCPE_OK
) ;
1323 } /* end of 'hist_set' */
1326 int hist_fprintf( FILE * fp
, int itemNum
, Hist_entry
* hptr
)
1334 fprintf( fp
, "\n\n" ) ;
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)
1345 if ( cpu_unit
.flags
& UNIT_STK
/* Nova 3 or Nova 4 */ )
1347 fprintf( fp
, "%06o %06o ", SP
, FP
) ;
1350 sim_eval
= (hptr
->ir
& 0xFFFF) ;
1351 if ( (fprint_sym(fp
, (hptr
->pc
& AMASK
), &sim_eval
, &cpu_unit
, SWMASK ('M'))) > 0 )
1353 fprintf( fp
, "(undefined) %04o", (hptr
->ir
& 0xFFFF) ) ;
1356 display ION flag value, pend value?
1357 display devBusy, devDone, devIntr info?
1360 if ( 0 ) /* display INTRP codes? */
1364 devBitNames( hptr
->devIntr
, tmp
, NULL
) ;
1365 fprintf( fp
, " %s", tmp
) ;
1368 fprintf( fp
, "\n" ) ;
1371 } /* end of 'hist_fprintf' */
1374 /* show execution history (proposed global routine) */
1376 t_stat
hist_show( FILE * st
, UNIT
* uptr
, int32 val
, void * desc
)
1379 char * cptr
= (char *) desc
;
1386 return ( SCPE_NOFNC
) ; /* enabled? */
1389 { /* number of entries specified */
1390 lnt
= (int32
) get_uint( cptr
, 10, hist_cnt
, &r
) ;
1391 if ( (r
!= SCPE_OK
) || (lnt
== 0) )
1393 return ( SCPE_ARG
) ;
1398 lnt
= hist_cnt
; /* display all entries */
1400 di
= hist_p
- lnt
; /* work forward */
1403 di
= di
+ hist_cnt
;
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? */
1411 hist_fprintf( st
, k
, hptr
) ;
1412 } /* end else instruction */
1415 } /* end of 'hist_show' */
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" },
1450 char * devBitNames( int32 flags
, char * ptr
, char * sepStr
)
1457 for ( a
= 0 ; (devBits
[a
].dBit
) ; ++a
)
1458 if ( devBits
[a
].dBit
& ((devBits
[a
].dInvertMask
)? ~flags
: flags
) )
1462 strcat( ptr
, (sepStr
) ? sepStr
: " " ) ;
1463 strcat( ptr
, devBits
[a
].dName
) ;
1467 strcpy( ptr
, devBits
[a
].dName
) ;
1472 } /* end of 'devBitNames' */