Commit | Line | Data |
---|---|---|
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 | |
273 | typedef 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 | |
291 | uint16 M[MAXMEMSIZE] = { 0 }; /* memory */\r | |
292 | int32 AC[4] = { 0 }; /* accumulators */\r | |
293 | int32 C = 0; /* carry flag */\r | |
294 | int32 saved_PC = 0; /* program counter */\r | |
295 | int32 SP = 0; /* stack pointer */\r | |
296 | int32 FP = 0; /* frame pointer */\r | |
297 | int32 SR = 0; /* switch register */\r | |
298 | int32 dev_done = 0; /* device done flags */\r | |
299 | int32 dev_busy = 0; /* device busy flags */\r | |
300 | int32 dev_disable = 0; /* int disable flags */\r | |
301 | int32 int_req = 0; /* interrupt requests */\r | |
302 | int32 pimask = 0; /* priority int mask */\r | |
303 | int32 pwr_low = 0; /* power fail flag */\r | |
304 | int32 ind_max = 65536; /* iadr nest limit */\r | |
305 | int32 stop_dev = 0; /* stop on ill dev */\r | |
306 | uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */\r | |
307 | int32 pcq_p = 0; /* PC queue ptr */\r | |
308 | REG *pcq_r = NULL; /* PC queue reg ptr */\r | |
309 | struct ndev dev_table[64]; /* dispatch table */\r | |
310 | int32 AMASK = 077777 ; /* current memory address mask */\r | |
311 | /* (default to 32KW) */\r | |
312 | static int32 hist_p = 0 ; /* history pointer */\r | |
313 | static int32 hist_cnt = 0 ; /* history count */\r | |
314 | static Hist_entry * hist = NULL ; /* instruction history */\r | |
315 | \r | |
316 | \r | |
317 | t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);\r | |
318 | t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);\r | |
319 | t_stat cpu_reset (DEVICE *dptr);\r | |
320 | t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);\r | |
321 | t_stat cpu_boot (int32 unitno, DEVICE *dptr);\r | |
322 | t_stat build_devtab (void);\r | |
323 | \r | |
324 | t_stat hist_set( UNIT * uptr, int32 val, char * cptr, void * desc ) ;\r | |
325 | t_stat hist_show( FILE * st, UNIT * uptr, int32 val, void * desc ) ;\r | |
326 | static int hist_save( int32 pc, int32 our_ir ) ;\r | |
327 | char * devBitNames( int32 flags, char * ptr, char * sepStr ) ;\r | |
328 | \r | |
329 | void mask_out (int32 mask);\r | |
330 | \r | |
331 | \r | |
332 | extern int32 sim_interval;\r | |
333 | extern int32 sim_int_char;\r | |
334 | extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */\r | |
335 | extern DEVICE * sim_devices[];\r | |
336 | extern 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 | |
348 | UNIT cpu_unit = {\r | |
349 | UDATA (NULL, UNIT_FIX+UNIT_BINK+UNIT_MDV, DFTMEMSIZE /* MAXMEMSIZE */ )\r | |
350 | };\r | |
351 | \r | |
352 | REG 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 | |
381 | MTAB 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 | |
411 | DEVICE 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 | |
418 | t_stat sim_instr (void)\r | |
419 | {\r | |
420 | int32 PC, IR, i;\r | |
421 | t_stat reason;\r | |
422 | \r | |
423 | /* Restore register state */\r | |
424 | \r | |
425 | if (build_devtab () != SCPE_OK) return SCPE_IERR; /* build dispatch */\r | |
426 | PC = saved_PC & AMASK; /* load local PC */\r | |
427 | C = C & CBIT;\r | |
428 | mask_out (pimask); /* reset int system */\r | |
429 | reason = 0;\r | |
430 | \r | |
431 | /* Main instruction fetch/decode loop */\r | |
432 | \r | |
433 | while (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 | |
1028 | saved_PC = PC;\r | |
1029 | pcq_r->qptr = pcq_p; /* update pc q ptr */\r | |
1030 | return ( reason ) ;\r | |
1031 | }\r | |
1032 | \r | |
1033 | /* New priority mask out */\r | |
1034 | \r | |
1035 | void mask_out (int32 newmask)\r | |
1036 | {\r | |
1037 | int32 i;\r | |
1038 | \r | |
1039 | dev_disable = 0;\r | |
1040 | for (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 | |
1044 | DEV_UPDATE_INTR ;\r | |
1045 | return;\r | |
1046 | }\r | |
1047 | \r | |
1048 | /* Reset routine */\r | |
1049 | \r | |
1050 | t_stat cpu_reset (DEVICE *dptr)\r | |
1051 | {\r | |
1052 | int_req = int_req & ~(INT_ION | INT_STK | INT_TRAP);\r | |
1053 | pimask = 0;\r | |
1054 | dev_disable = 0;\r | |
1055 | pwr_low = 0;\r | |
1056 | AMASK = 077777 ; /* 32KW mode */\r | |
1057 | pcq_r = find_reg ("PCQ", NULL, dptr);\r | |
1058 | if (pcq_r) pcq_r->qptr = 0;\r | |
1059 | else return SCPE_IERR;\r | |
1060 | sim_brk_types = sim_brk_dflt = SWMASK ('E');\r | |
1061 | return SCPE_OK;\r | |
1062 | }\r | |
1063 | \r | |
1064 | /* Memory examine */\r | |
1065 | \r | |
1066 | t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)\r | |
1067 | {\r | |
1068 | if (addr >= MEMSIZE) return SCPE_NXM;\r | |
1069 | if (vptr != NULL) *vptr = M[addr] & DMASK;\r | |
1070 | return SCPE_OK;\r | |
1071 | }\r | |
1072 | \r | |
1073 | /* Memory deposit */\r | |
1074 | \r | |
1075 | t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)\r | |
1076 | {\r | |
1077 | if (addr >= MEMSIZE) return SCPE_NXM;\r | |
1078 | M[addr] = val & DMASK;\r | |
1079 | return SCPE_OK;\r | |
1080 | }\r | |
1081 | \r | |
1082 | /* Alter memory size */\r | |
1083 | \r | |
1084 | t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)\r | |
1085 | {\r | |
1086 | int32 mc = 0;\r | |
1087 | t_addr i;\r | |
1088 | \r | |
1089 | if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0))\r | |
1090 | return SCPE_ARG;\r | |
1091 | for (i = val; i < MEMSIZE; i++) mc = mc | M[i];\r | |
1092 | if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))\r | |
1093 | return SCPE_OK;\r | |
1094 | MEMSIZE = val;\r | |
1095 | for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0;\r | |
1096 | return SCPE_OK;\r | |
1097 | }\r | |
1098 | \r | |
1099 | /* Build dispatch table */\r | |
1100 | \r | |
1101 | t_stat build_devtab (void)\r | |
1102 | {\r | |
1103 | DEVICE *dptr;\r | |
1104 | DIB *dibp;\r | |
1105 | int32 i, dn;\r | |
1106 | \r | |
1107 | for (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 | |
1112 | for (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 | |
1121 | return 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 | |
1161 | static 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 | |
1199 | t_stat cpu_boot (int32 unitno, DEVICE *dptr)\r | |
1200 | {\r | |
1201 | int32 i;\r | |
1202 | \r | |
1203 | for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];\r | |
1204 | saved_PC = BOOT_START;\r | |
1205 | return SCPE_OK;\r | |
1206 | }\r | |
1207 | \r | |
1208 | /* 1-to-1 map for I/O devices */\r | |
1209 | \r | |
1210 | int32 MapAddr (int32 map, int32 addr)\r | |
1211 | {\r | |
1212 | return addr;\r | |
1213 | }\r | |
1214 | \r | |
1215 | /* History subsystem\r | |
1216 | \r | |
1217 | global routines\r | |
1218 | \r | |
1219 | t_stat hist_set( UNIT * uptr, int32 val, char * cptr, void * desc, void ** HistCookie, sizeof(usrHistInfo) ) ;\r | |
1220 | t_stat hist_show( FILE * st, UNIT * uptr, int32 val, void * desc, void * HistCookie ) ;\r | |
1221 | int hist_save( int32 next_pc, int32 our_ir, void * usrHistInfo )\r | |
1222 | \r | |
1223 | local user struct:\r | |
1224 | \r | |
1225 | usrHistInfo\r | |
1226 | \r | |
1227 | local user routines:\r | |
1228 | \r | |
1229 | int uHist_save( int32 next_pc, int32 our_ir, void * usrHistInfo ) ;\r | |
1230 | int uHist_fprintf( FILE * fp, int itemNum, void * usrHistInfo ) ;\r | |
1231 | \r | |
1232 | typedef 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 | |
1249 | static int hist_save( int32 pc, int32 our_ir )\r | |
1250 | {\r | |
1251 | Hist_entry * hist_ptr ;\r | |
1252 | \r | |
1253 | if ( 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 | |
1281 | return ( -1 ) ;\r | |
1282 | } /* end of 'hist_save' */\r | |
1283 | \r | |
1284 | /* setup history save area (proposed global routine) */\r | |
1285 | \r | |
1286 | t_stat hist_set( UNIT * uptr, int32 val, char * cptr, void * desc )\r | |
1287 | {\r | |
1288 | int32 i, lnt ;\r | |
1289 | t_stat r ;\r | |
1290 | \r | |
1291 | if ( 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 | |
1301 | lnt = (int32) get_uint(cptr, 10, HIST_MAX, &r) ;\r | |
1302 | if ( (r != SCPE_OK) || (lnt && (lnt < HIST_MIN)) )\r | |
1303 | {\r | |
1304 | return ( SCPE_ARG ) ;\r | |
1305 | }\r | |
1306 | hist_p = 0;\r | |
1307 | if ( hist_cnt )\r | |
1308 | {\r | |
1309 | free( hist ) ;\r | |
1310 | hist_cnt = 0 ;\r | |
1311 | hist = NULL ;\r | |
1312 | }\r | |
1313 | if ( 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 | |
1322 | return ( SCPE_OK ) ;\r | |
1323 | } /* end of 'hist_set' */\r | |
1324 | \r | |
1325 | \r | |
1326 | int hist_fprintf( FILE * fp, int itemNum, Hist_entry * hptr )\r | |
1327 | {\r | |
1328 | t_value sim_eval ;\r | |
1329 | \r | |
1330 | if ( 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 | |
1370 | return ( 0 ) ;\r | |
1371 | } /* end of 'hist_fprintf' */\r | |
1372 | \r | |
1373 | \r | |
1374 | /* show execution history (proposed global routine) */\r | |
1375 | \r | |
1376 | t_stat hist_show( FILE * st, UNIT * uptr, int32 val, void * desc )\r | |
1377 | {\r | |
1378 | int32 k, di, lnt ;\r | |
1379 | char * cptr = (char *) desc ;\r | |
1380 | t_stat r ;\r | |
1381 | Hist_entry * hptr ;\r | |
1382 | \r | |
1383 | \r | |
1384 | if (hist_cnt == 0)\r | |
1385 | {\r | |
1386 | return ( SCPE_NOFNC ) ; /* enabled? */\r | |
1387 | }\r | |
1388 | if ( 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 | |
1406 | for ( 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 | |
1414 | return SCPE_OK;\r | |
1415 | } /* end of 'hist_show' */\r | |
1416 | \r | |
1417 | \r | |
1418 | \r | |
1419 | struct 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 | |
1450 | char * devBitNames( int32 flags, char * ptr, char * sepStr )\r | |
1451 | {\r | |
1452 | int a ;\r | |
1453 | \r | |
1454 | if ( 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 | |
1471 | return ( ptr ) ;\r | |
1472 | } /* end of 'devBitNames' */\r |