First Commit of my working state
[simh.git] / HP2100 / hp2100_cpu6.c
1 /* hp2100_cpu6.c: HP 1000 RTE-6/VM OS instructions
2
3 Copyright (c) 2006-2007, J. David Bryan
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 Except as contained in this notice, the name of the author 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 the author.
25
26 CPU6 RTE-6/VM OS instructions
27
28 27-Nov-07 JDB Implemented OS instructions
29 26-Sep-06 JDB Created
30
31 Primary references:
32 - HP 1000 M/E/F-Series Computers Technical Reference Handbook
33 (5955-0282, Mar-1980)
34 - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation
35 (92851-90001, Mar-1981)
36 - Macro/1000 Reference Manual (92059-90001, Dec-1992)
37
38 Additional references are listed with the associated firmware
39 implementations, as are the HP option model numbers pertaining to the
40 applicable CPUs.
41 */
42
43
44 #include <setjmp.h>
45 #include "hp2100_defs.h"
46 #include "hp2100_cpu.h"
47 #include "hp2100_cpu1.h"
48
49
50 /* external variables */
51
52 extern jmp_buf save_env; /* MP abort handler */
53
54
55 /* Offsets to data and addresses within RTE. */
56
57 static const uint32 xi = 0001647; /* XI address */
58 static const uint32 intba = 0001654; /* INTBA address */
59 static const uint32 intlg = 0001655; /* INTLG address */
60 static const uint32 eqt1 = 0001660; /* EQT1 address */
61 static const uint32 eqt11 = 0001672; /* EQT11 address */
62 static const uint32 pvcn = 0001712; /* PVCN address */
63 static const uint32 xsusp = 0001730; /* XSUSP address */
64 static const uint32 dummy = 0001737; /* DUMMY address */
65 static const uint32 mptfl = 0001770; /* MPTFL address */
66 static const uint32 eqt12 = 0001771; /* EQT12 address */
67 static const uint32 eqt15 = 0001774; /* EQT15 address */
68 static const uint32 vctr = 0002000; /* VCTR address */
69
70 static const uint32 CLC_0 = 0004700; /* CLC 0 instruction */
71 static const uint32 STC_0 = 0000700; /* STC 0 instruction */
72 static const uint32 CLF_0 = 0001100; /* CLF 0 instruction */
73 static const uint32 STF_0 = 0000100; /* STF 0 instruction */
74 static const uint32 SFS_0_C = 0003300; /* SFS 0,C instruction */
75
76 enum vctr_offsets { dms_offset = 0, /* DMS status */
77 int_offset, /* interrupt system status */
78 sc_offset, /* select code */
79 clck_offset, /* TBG IRQ handler */
80 cic4_offset, /* illegal IRQ handler */
81 cic2_offset, /* device IRQ handler */
82 sked_offset, /* prog sched IRQ handler */
83 rqst_offset, /* EXEC request handler */
84 cic_offset, /* IRQ location */
85 perr_offset, /* parity error IRQ handler */
86 mper_offset, /* memory protect IRQ handler */
87 lxnd_offset }; /* $LIBR return */
88
89
90 /* RTE-6/VM Operating System Instructions
91
92 The OS instructions were added to acccelerate certain time-consuming
93 operations of the RTE-6/VM operating system, HP product number 92084A.
94 Microcode was available for the E- and F-Series; the M-Series used software
95 equivalents.
96
97 Option implementation by CPU was as follows:
98
99 2114 2115 2116 2100 1000-M 1000-E 1000-F
100 ------ ------ ------ ------ ------ ------ ------
101 N/A N/A N/A N/A N/A 92084A 92084A
102
103 The routines are mapped to instruction codes as follows:
104
105 Instr. 1000-E/F Description
106 ------ -------- ----------------------------------------------
107 $LIBR 105340 Enter privileged/reentrant library routine
108 $LIBX 105341 Exit privileged/reentrant library routine
109 .TICK 105342 TBG tick interrupt handler
110 .TNAM 105343 Find ID segment that matches name
111 .STIO 105344 Configure I/O instructions
112 .FNW 105345 Find word with user increment
113 .IRT 105346 Interrupt return processing
114 .LLS 105347 Linked list search
115
116 .SIP 105350 Skip if interrupt pending
117 .YLD 105351 .SIP completion return point
118 .CPM 105352 Compare words LT/EQ/GT
119 .ETEQ 105353 Set up EQT pointers in base page
120 .ENTN 105354 Transfer parameter addresses (utility)
121 $OTST * 105355 OS firmware self test
122 .ENTC 105356 Transfer parameter addresses (priv/reent)
123 .DSPI 105357 Set display indicator
124
125 Opcodes 105354-105357 are "dual use" instructions that take different
126 actions, depending on whether they are executed from a trap cell during an
127 interrupt. When executed from a trap cell, they have these actions:
128
129 Instr. 1000-E/F Description
130 ------ -------- ----------------------------------------------
131 $DCPC * 105354 DCPC channel interrupt processing
132 $MPV * 105355 MP/DMS/PE interrupt processing
133 $DEV * 105356 Standard device interrupt processing
134 $TBG * 105357 TBG interrupt processing
135
136 * These mnemonics are recognized by symbolic examine/deposit but are not
137 official HP mnemonics.
138
139 Notes:
140
141 1. The microcode differentiates between interrupt processing and normal
142 execution of the "dual use" instructions by testing the CPU flag.
143 Interrupt vectoring sets the flag; a normal instruction fetch clears it.
144 Under simulation, interrupt vectoring is indicated by the value of the
145 "iotrap" parameter (0 = normal instruction, 1 = trap cell instruction).
146
147 2. The operand patterns for .ENTN and .ENTC normally would be coded as
148 "OP_A", as each takes a single address as a parameter. However, because
149 they might also be executed from a trap cell, we cannot assume that P+1
150 is an address, or we might cause a DM abort when trying to resolve
151 indirects. Therefore, "OP_A" handling is done within each routine, once
152 the type of use is determined.
153
154 3. The microcode for .ENTC, .ENTN, .FNW, .LLS, .TICK, and .TNAM explicitly
155 checks for interrupts during instruction execution. In addition, the
156 .STIO, .CPM, and .LLS instructions implicitly check for interrupts
157 during parameter indirect resolution. Because the simulator calculates
158 interrupt requests only between instructions, this behavior is not
159 simulated.
160
161 4. The microcode executes certain I/O instructions (e.g., CLF 0) by
162 building the instruction in the IR and executing an IOG micro-order. We
163 simulate this behavior by calling the "iogrp" handler with the
164 appropriate instruction, rather than manipulating the I/O system
165 directly, so that we will remain unaffected by any future changes to the
166 underlying I/O simulation structure.
167
168 5. The $OTST and .DSPI microcode uses features (reading the RPL switches
169 and boot loader ROM data, loading the display register) that are not
170 simulated. The remaining functions of the $OTST instruction are
171 provided. The .DSPI instruction is a NOP or unimplemented instruction
172 stop.
173
174 6. The microcode detects a privileged system and takes some additional
175 actions if one is found. We provide simulations of these actions.
176 However, at the current time, the simulator does not provide a
177 privileged interrupt fence card, so this code is untested.
178
179 7. Because of the volume of calls to the OS firmware, debug printouts
180 attempt to write only one line per instruction invocation. This means
181 that calling and returned register values are printed separately, with a
182 newline added at the end of execution. However, many instructions can
183 MP or DM abort, either intentionally or due to improper use. That would
184 leave debug lines without the required trailing newlines.
185
186 There are two ways to address this: either we could replace the CPU's
187 setjmp buffer with one that points to a routine that adds the missing
188 newline, or we can add a semaphore that is tested on entry to see if it
189 is already set, implying a longjmp occurred, and then add the newline if
190 so. The latter would add the newline when the MP trap cell instruction
191 was entered or when the next user-level instruction was executed.
192 However, the merged-line problem would still exist if some other module
193 generated intervening debug printouts. So we do the former. This does
194 mean that this routine must be changed if the MP abort mechanism is
195 changed.
196
197 8. The $LIBX instruction is executed to complete either a privileged or
198 reentrant execution. In the former case, the privileged nest counter
199 ($PVCN) is decremented. In the latter, $PVCN decrement is attempted but
200 the write will trap with an MP violation, as reentrant routines execute
201 with the interrupt system on. RTE will then complete the release of
202 memory allocated for the original $LIBR call.
203
204 Additional references:
205 - RTE-6/VM OS Microcode Source (92084-18831, revision 8).
206 - RTE-6/VM Technical Specifications (92084-90015, Apr-1983).
207 */
208
209
210 /* Save the CPU registers.
211
212 The CPU registers are saved in the current ID segment in preparation for
213 interrupt handling. Although the RTE base page has separate pointers for the
214 P, A, B, and E/O registers, they are always contiguous, and the microcode
215 simply increments the P-register pointer (XSUSP) to store the remaining
216 values.
217
218 This routine is called from the trap cell interrupt handlers and from the
219 $LIBX processor. In the latter case, the privileged system interrupt
220 handling is not required, so it is bypassed. In either case, the current map
221 will be the system map when we are called.
222 */
223
224 static t_stat cpu_save_regs (uint32 iotrap)
225 {
226 uint16 save_area, priv_fence;
227 t_stat reason = SCPE_OK;
228
229 save_area = ReadW (xsusp); /* addr of PABEO save area */
230
231 WriteW (save_area + 0, PC); /* save P */
232 WriteW (save_area + 1, AR); /* save A */
233 WriteW (save_area + 2, BR); /* save B */
234 WriteW (save_area + 3, (E << 15) & SIGN | O & 1); /* save E and O */
235
236 save_area = ReadW (xi); /* addr of XY save area */
237 WriteWA (save_area + 0, XR); /* save X (in user map) */
238 WriteWA (save_area + 1, YR); /* save Y (in user map) */
239
240 if (iotrap) { /* do priv setup only if IRQ */
241 priv_fence = ReadW (dummy); /* get priv fence select code */
242
243 if (priv_fence) { /* privileged system? */
244 reason = iogrp (STC_0 + priv_fence, iotrap); /* STC SC on priv fence */
245 reason = iogrp (CLC_0 + DMA0, iotrap); /* CLC 6 to inh IRQ on DCPC 0 */
246 reason = iogrp (CLC_0 + DMA1, iotrap); /* CLC 7 to inh IRQ on DCPC 1 */
247 reason = iogrp (STF_0, iotrap); /* turn interrupt system back on */
248 }
249 }
250
251 return reason;
252 }
253
254
255 /* Save the machine state at interrupt.
256
257 This routine is called from each of the trap cell instructions. Its purpose
258 is to save the complete state of the machine in preparation for interrupt
259 handling.
260
261 For the MP/DMS/PE interrupt, the interrupting device must not be cleared and
262 the CPU registers must not be saved until it is established that the
263 interrupt is not caused by a parity error. Parity errors cannot be
264 inhibited, so the interrupt may have occurred while in RTE. Saving the
265 registers would overwrite the user's registers that were saved at RTE entry.
266
267 Note that the trap cell instructions are dual-use and invoke this routine
268 only when they are executed during interrupts. Therefore, the current map
269 will always be the system map when we are called.
270 */
271
272 static t_stat cpu_save_state (uint32 iotrap)
273 {
274 uint16 vectors;
275 uint32 saved_PC, int_sys_off;
276 t_stat reason;
277
278 saved_PC = PC; /* save current PC */
279 reason = iogrp (SFS_0_C, iotrap); /* turn interrupt system off */
280 int_sys_off = (PC == saved_PC); /* set flag if already off */
281 PC = saved_PC; /* restore PC in case it bumped */
282
283 vectors = ReadW (vctr); /* get address of vectors (in SMAP) */
284
285 WriteW (vectors + dms_offset, dms_upd_sr ()); /* save DMS status (SSM) */
286 WriteW (vectors + int_offset, int_sys_off); /* save int status */
287 WriteW (vectors + sc_offset, intaddr); /* save select code */
288
289 WriteW (mptfl, 1); /* show MP is off */
290
291 if (intaddr != 5) { /* only if not MP interrupt */
292 reason = iogrp (CLF_0 + intaddr, iotrap); /* issue CLF to device */
293 cpu_save_regs (iotrap); /* save CPU registers */
294 }
295
296 return reason;
297 }
298
299
300 /* Get the interrupt table entry corresponding to a select code.
301
302 Return the word in the RTE interrupt table that corresponds to the
303 interrupting select code. Return 0 if the select code is beyond the end of
304 the table.
305 */
306
307 uint32 cpu_get_intbl (uint32 select_code)
308 {
309 uint16 interrupt_table; /* interrupt table (starts with SC 06) */
310 uint16 table_length; /* length of interrupt table */
311
312 interrupt_table = ReadW (intba); /* get int table address */
313 table_length = ReadW (intlg); /* get int table length */
314
315 if (select_code - 6 > table_length) /* SC beyond end of table? */
316 return 0; /* return 0 for illegal interrupt */
317 else
318 return ReadW (interrupt_table + select_code - 6); /* else return table entry */
319 }
320
321
322 /* RTE-6/VM OS instruction dispatcher.
323
324 Debugging printouts are provided with the OS and OSTBG debug flags. The OS
325 flag enables tracing for all instructions except for the three-instruction
326 sequence executed for the time-base generator interrupt ($TBG, .TICK, and
327 .IRT). The OSTBG flag enables tracing for just the TBG sequence. The flags
328 are separate, as the TBG generates 100 interrupts per second. Use caution
329 when specifying the OSTBG flag, as the debug output file will grow rapidly.
330 Note that the OS flag enables the .IRT instruction trace for all cases except
331 a TBG interrupt.
332
333 The firmware self-test instruction (105355) is always allowed, regardless of
334 the UNIT_VMAOS setting. This is because RTE-6/VM will always test for the
335 presence of OS and VMA firmware on E/F-Series machines. If the firmware is
336 not present, then these instructions are NOPs and return to P+1. RTE will
337 then HLT 21. This means that RTE-6/VM will not run on an E/F-Series machine
338 without the OS and VMA firmware.
339
340 Howwever, RTE allows the firmware instructions to be disabled for debugging
341 purposes. If the firmware is present and returns to P+2 but sets the X
342 register to 0, then RTE will use software equivalents. We enable this
343 condition when the OS firmware is enabled (SET CPU VMA), the OS debug flag is
344 set (SET CPU DEBUG=OS), but debug output has been disabled (SET CONSOLE
345 NODEBUG). That is:
346
347 OS Debug
348 Firmware Flag Output Tracing Self-Test Instruction
349 ======== ===== ====== ======= =====================
350 disabled x x off NOP
351 enabled clear x off X = revision code
352 enabled set off off X = 0
353 enabled set on on X = revision code
354 */
355
356 static const OP_PAT op_os[16] = {
357 OP_A, OP_A, OP_N, OP_N, /* $LIBR $LIBX .TICK .TNAM */
358 OP_A, OP_K, OP_A, OP_KK, /* .STIO .FNW .IRT .LLS */
359 OP_N, OP_C, OP_KK, OP_N, /* .SIP .YLD .CPM .ETEQ */
360 OP_N, OP_N, OP_N, OP_N /* .ENTN $OTST .ENTC .DSPI */
361 };
362
363 t_stat cpu_rte_os (uint32 IR, uint32 intrq, uint32 iotrap)
364 {
365 t_stat reason = SCPE_OK;
366 OPS op;
367 OP_PAT pattern;
368 uint32 entry, count, cp, sa, da, i, ma;
369 uint16 vectors, save_area, priv_fence, eoreg, eqt, key;
370 char test[6], target[6];
371 jmp_buf mp_handler;
372 int abortval;
373 t_bool debug_print;
374 static t_bool tbg_tick = FALSE; /* set if processing TBG interrupt */
375
376 if ((IR != 0105355) && /* allow self-test with OS disabled */
377 (cpu_unit.flags & UNIT_VMAOS) == 0) /* VMA/OS option installed? */
378 return stop_inst;
379
380 entry = IR & 017; /* mask to entry point */
381 pattern = op_os[entry]; /* get operand pattern */
382
383 if (pattern != OP_N)
384 if (reason = cpu_ops (pattern, op, intrq)) /* get instruction operands */
385 return reason;
386
387 tbg_tick = tbg_tick || (IR == 0105357) && iotrap; /* set TBG interrupting flag */
388
389 debug_print = (DEBUG_PRI (cpu_dev, DEB_OS) && !tbg_tick) ||
390 (DEBUG_PRI (cpu_dev, DEB_OSTBG) && tbg_tick);
391
392 if (debug_print) {
393 fprintf (sim_deb, ">>CPU OS: IR = %06o (", IR); /* print preamble and IR */
394 fprint_sym (sim_deb, (iotrap ? intaddr : err_PC), /* print instruction mnemonic */
395 (t_value *) &IR, NULL, SWMASK('M'));
396 fputc (')', sim_deb);
397
398 fprint_ops (pattern, op); /* print operands */
399
400 memcpy (mp_handler, save_env, sizeof (jmp_buf)); /* save original MP handler */
401 abortval = setjmp (save_env); /* set new MP abort handler */
402
403 if (abortval != 0) { /* MP abort? */
404 fputs ("...MP abort\n", sim_deb); /* report it and terminate line */
405 memcpy (save_env, mp_handler, sizeof (jmp_buf)); /* restore original MP handler */
406 longjmp (save_env, abortval); /* transfer to MP handler */
407 }
408 }
409
410 switch (entry) { /* decode IR<3:0> */
411
412 case 000: /* $LIBR 105340 (OP_A) */
413 if ((op[0].word != 0) || /* reentrant call? */
414 (CTL (PRO) && (ReadW (dummy) != 0))) { /* or priv call + MP on + priv sys? */
415 if (dms_ump) { /* called from user map? */
416 dms_viol (err_PC, MVI_PRV); /* privilege violation */
417 }
418 dms_ump = SMAP; /* set system map */
419
420 vectors = ReadW (vctr); /* get address of vectors (in SMAP) */
421 PC = ReadW (vectors + mper_offset); /* vector to $MPER for processing */
422 }
423
424 else { /* privileged call */
425 if (CTL (PRO)) { /* memory protect on? */
426 clrCTL (PRO); /* turn it off */
427 reason = iogrp (CLF_0, iotrap); /* turn interrupt system off */
428 WriteW (mptfl, 1); /* show MP is off */
429 save_area = ReadW (xsusp); /* get addr of P save area */
430
431 if (dms_ump) /* user map current? */
432 WriteWA (save_area, (PC - 2) & VAMASK); /* set point of suspension */
433 else /* system map current */
434 WriteW (save_area, (PC - 2) & VAMASK); /* set point of suspension */
435 }
436
437 WriteW (pvcn, (ReadW (pvcn) + 1) & DMASK); /* increment priv nest counter */
438 }
439 break;
440
441 case 001: /* $LIBX 105341 (OP_A) */
442 PC = ReadW (op[0].word); /* set P to return point */
443 count = (ReadW (pvcn) - 1) & DMASK; /* decrement priv nest counter */
444 WriteW (pvcn, count); /* write it back */
445
446 if (count == 0) { /* end of priv mode? */
447 dms_ump = SMAP; /* set system map */
448 reason = cpu_save_regs (iotrap); /* save registers */
449 vectors = ReadW (vctr); /* get address of vectors */
450 PC = ReadW (vectors + lxnd_offset); /* vector to $LXND for processing */
451 }
452 break;
453
454 case 002: /* .TICK 105342 (OP_N) */
455 if (debug_print) /* debugging? */
456 fprint_regs (",", REG_A | REG_B, 0); /* print entry registers */
457
458 do {
459 eqt = (ReadW (AR) + 1) & DMASK; /* bump timeout from EQT15 */
460
461 if (eqt != 1) { /* was timeout active? */
462 WriteW (AR, eqt); /* yes, write it back */
463
464 if (eqt == 0) /* did timeout expire? */
465 break; /* P+0 return for timeout */
466 }
467
468 AR = (AR + 15) & DMASK; /* point at next EQT15 */
469 BR = (BR - 1) & DMASK; /* decrement count of EQTs */
470 } while ((BR > 0) && (eqt != 0)); /* loop until timeout or done */
471
472 if (BR == 0) /* which termination condition? */
473 PC = (PC + 1) & VAMASK; /* P+1 return for no timeout */
474
475 if (debug_print) /* debugging? */
476 fprint_regs ("; result:", /* print return registers */
477 REG_A | REG_B | REG_P_REL,
478 err_PC + 1);
479 break;
480
481 case 003: /* .TNAM 105343 (OP_N) */
482 if (debug_print) /* debugging? */
483 fprint_regs (",", REG_A | REG_B, 0); /* print entry registers */
484
485 E = 1; /* preset flag for not found */
486 cp = (BR << 1) & DMASK; /* form char addr (B is direct) */
487
488 for (i = 0; i < 5; i++) { /* copy target name */
489 target[i] = (char) ReadB (cp); /* name is only 5 chars */
490 cp = (cp + 1) & DMASK;
491 }
492
493 if ((target[0] == '\0') && (target[1] == '\0')) /* if name is null, */
494 break; /* return immed to P+0 */
495
496 key = ReadW (AR); /* get first keyword addr */
497
498 while (key != 0) { /* end of keywords? */
499 cp = ((key + 12) << 1) & DMASK; /* form char addr of name */
500
501 for (i = 0; i < 6; i++) { /* copy test name */
502 test[i] = (char) ReadB (cp); /* name is only 5 chars */
503 cp = (cp + 1) & DMASK; /* but copy 6 to get flags */
504 }
505
506 if (strncmp (target, test, 5) == 0) { /* names match? */
507 AR = (key + 15) & DMASK; /* A = addr of IDSEG [15] */
508 BR = key; /* B = addr of IDSEG [0] */
509 E = (uint32) ((test[5] >> 4) & 1); /* E = short ID segment bit */
510 PC = (PC + 1) & VAMASK; /* P+1 for found return */
511 break;
512 }
513
514 AR = (AR + 1) & DMASK; /* bump to next keyword */
515 key = ReadW (AR); /* get next keyword */
516 };
517
518 if (debug_print) /* debugging? */
519 fprint_regs ("; result:", /* print return registers */
520 REG_A | REG_B | REG_E | REG_P_REL,
521 err_PC + 1);
522 break;
523
524 case 004: /* .STIO 105344 (OP_A) */
525 count = op[0].word - PC; /* get count of operands */
526
527 if (debug_print) /* debugging? */
528 fprintf (sim_deb, /* print registers on entry */
529 ", A = %06o, count = %d", AR, count);
530
531 for (i = 0; i < count; i++) {
532 ma = ReadW (PC); /* get operand address */
533
534 if (reason = resolve (ma, &ma, intrq)) { /* resolve indirect */
535 PC = err_PC; /* IRQ restarts instruction */
536 break;
537 }
538
539 WriteW (ma, ReadW (ma) & ~I_DEVMASK | AR); /* set SC into instruction */
540 PC = (PC + 1) & VAMASK; /* bump to next */
541 }
542 break;
543
544 case 005: /* .FNW 105345 (OP_K) */
545 if (debug_print) /* debugging? */
546 fprint_regs (",", REG_A | REG_B | REG_X, 0); /* print entry registers */
547
548 while (XR != 0) { /* all comparisons done? */
549 key = ReadW (BR); /* read a buffer word */
550
551 if (key == AR) { /* does it match? */
552 PC = (PC + 1) & VAMASK; /* P+1 found return */
553 break;
554 }
555
556 BR = (BR + op[0].word) & DMASK; /* increment buffer ptr */
557 XR = (XR - 1) & DMASK; /* decrement remaining count */
558 }
559 /* P+0 not found return */
560 if (debug_print) /* debugging? */
561 fprint_regs ("; result:", /* print return registers */
562 REG_A | REG_B | REG_X | REG_P_REL,
563 err_PC + 2);
564 break;
565
566 case 006: /* .IRT 105346 (OP_A) */
567 save_area = ReadW (xsusp); /* addr of PABEO save area */
568
569 WriteW (op[0].word, ReadW (save_area + 0)); /* restore P to DEF RTN */
570
571 AR = ReadW (save_area + 1); /* restore A */
572 BR = ReadW (save_area + 2); /* restore B */
573
574 eoreg = ReadW (save_area + 3); /* get combined E and O */
575 E = (eoreg >> 15) & 1; /* restore E */
576 O = eoreg & 1; /* restore O */
577
578 save_area = ReadW (xi); /* addr of XY save area */
579 XR = ReadWA (save_area + 0); /* restore X (from user map) */
580 YR = ReadWA (save_area + 1); /* restore Y (from user map) */
581
582 reason = iogrp (CLF_0, iotrap); /* turn interrupt system off */
583 WriteW (mptfl, 0); /* show MP is on */
584
585 priv_fence = ReadW (dummy); /* get priv fence select code */
586
587 if (priv_fence) { /* privileged system? */
588 reason = iogrp (CLC_0 + priv_fence, iotrap); /* CLC SC on priv fence */
589 reason = iogrp (STF_0 + priv_fence, iotrap); /* STF SC on priv fence */
590
591 if (cpu_get_intbl (DMA0) & SIGN) /* DCPC 0 active? */
592 reason = iogrp (STC_0 + DMA0, iotrap); /* STC 6 to enable IRQ on DCPC 0 */
593
594 if (cpu_get_intbl (DMA1) & SIGN) /* DCPC 1 active? */
595 reason = iogrp (STC_0 + DMA1, iotrap); /* STC 7 to enable IRQ on DCPC 1 */
596 }
597
598 tbg_tick = 0; /* .IRT terminates TBG servicing */
599 break;
600
601 case 007: /* .LLS 105347 (OP_KK) */
602 if (debug_print) /* debugging? */
603 fprint_regs (",", REG_A | REG_B | REG_E, 0); /* print entry registers */
604
605 AR = AR & ~SIGN; /* clear sign bit of A */
606
607 while ((AR != 0) && ((AR & SIGN) == 0)) { /* end of list or bad list? */
608 key = ReadW ((AR + op[1].word) & VAMASK); /* get key value */
609
610 if ((E == 0) && (key == op[0].word) || /* for E = 0, key = arg? */
611 (E != 0) && (key > op[0].word)) /* for E = 1, key > arg? */
612 break; /* search is done */
613
614 BR = AR; /* B = last link */
615 AR = ReadW (AR); /* A = next link */
616 }
617
618 if (AR == 0) /* exhausted list? */
619 PC = (PC + 1) & VAMASK; /* P+1 arg not found */
620 else if ((AR & SIGN) == 0) /* good link? */
621 PC = (PC + 2) & VAMASK; /* P+2 arg found */
622 /* P+0 bad link */
623 if (debug_print) /* debugging? */
624 fprint_regs ("; result:", /* print return registers */
625 REG_A | REG_B | REG_P_REL,
626 err_PC + 3);
627 break;
628
629 case 010: /* .SIP 105350 (OP_N) */
630 reason = iogrp (STF_0, iotrap); /* turn interrupt system on */
631 intrq = calc_int (); /* check for interrupt requests */
632 reason = iogrp (CLF_0, iotrap); /* turn interrupt system off */
633
634 if (intrq) /* was interrupt pending? */
635 PC = (PC + 1) & VAMASK; /* P+1 return for pending IRQ */
636 /* P+0 return for no pending IRQ */
637 if (debug_print) /* debugging? */
638 fprintf (sim_deb, /* print return registers */
639 "CIR = %02o, return = P+%d",
640 intrq, PC - (err_PC + 1));
641 break;
642
643 case 011: /* .YLD 105351 (OP_C) */
644 PC = op[0].word; /* pick up point of resumption */
645 reason = iogrp (STF_0, iotrap); /* turn interrupt system on */
646 ion_defer = 0; /* kill defer so irq occurs immed */
647 break;
648
649 case 012: /* .CPM 105352 (OP_KK) */
650 if (INT16 (op[0].word) > INT16 (op[1].word))
651 PC = (PC + 2) & VAMASK; /* P+2 arg1 > arg2 */
652 else if (INT16 (op[0].word) < INT16 (op[1].word))
653 PC = (PC + 1) & VAMASK; /* P+1 arg1 < arg2 */
654 /* P+0 arg1 = arg2 */
655 if (debug_print) /* debugging? */
656 fprint_regs (",", REG_P_REL, err_PC + 3); /* print return registers */
657 break;
658
659 case 013: /* .ETEQ 105353 (OP_N) */
660 eqt = ReadW (eqt1); /* get addr of EQT1 */
661
662 if (AR != eqt) { /* already set up? */
663 for (eqt = eqt1; eqt <= eqt11; eqt++) /* init EQT1-EQT11 */
664 WriteW (eqt & VAMASK, (AR++ & DMASK));
665 for (eqt = eqt12; eqt <= eqt15; eqt++) /* init EQT12-EQT15 */
666 WriteW (eqt & VAMASK, (AR++ & DMASK)); /* (not contig with EQT1-11) */
667 }
668
669 if (debug_print) /* debugging? */
670 fprintf (sim_deb, /* print return registers */
671 ", A = %06o, EQT1 = %06o", AR, eqt);
672 break;
673
674 case 014: /* .ENTN/$DCPC 105354 (OP_N) */
675 if (iotrap) { /* in trap cell? */
676 reason = cpu_save_state (iotrap); /* DMA interrupt */
677 AR = cpu_get_intbl (intaddr) & ~SIGN; /* get intbl value and strip sign */
678 goto DEVINT; /* vector by intbl value */
679 }
680
681 else { /* .ENTN instruction */
682 ma = (PC - 2) & VAMASK; /* get addr of entry point */
683
684 ENTX: /* enter here from .ENTC */
685 reason = cpu_ops (OP_A, op, intrq); /* get instruction operand */
686 da = op[0].word; /* get addr of 1st formal */
687 count = ma - da; /* get count of formals */
688 sa = ReadW (ma); /* get addr of 1st actual */
689 WriteW (ma, (sa + count) & VAMASK); /* adjust return point to skip actuals */
690
691 if (debug_print) /* debugging? */
692 fprintf (sim_deb, /* print entry registers */
693 ", op [0] = %06o, pcount = %d",
694 da, count);
695
696 for (i = 0; i < count; i++) { /* parameter loop */
697 ma = ReadW (sa); /* get addr of actual */
698 sa = (sa + 1) & VAMASK; /* increment address */
699
700 if (reason = resolve (ma, &ma, intrq)) { /* resolve indirect */
701 PC = err_PC; /* irq restarts instruction */
702 break;
703 }
704
705 WriteW (da, ma); /* put addr into formal */
706 da = (da + 1) & VAMASK; /* increment address */
707 }
708
709 if (entry == 016) /* call was .ENTC? */
710 AR = sa; /* set A to return address */
711 }
712 break;
713
714 case 015: /* $OTST/$MPV 105355 (OP_N) */
715 if (iotrap) { /* in trap cell? */
716 reason = cpu_save_state (iotrap); /* MP/DMS/PE interrupt */
717 vectors = ReadW (vctr); /* get address of vectors (in SMAP) */
718
719 if (mp_viol & SIGN) { /* parity error? */
720 WriteW (vectors + cic_offset, PC); /* save point of suspension in $CIC */
721 PC = ReadW (vectors + perr_offset); /* vector to $PERR for processing */
722 }
723
724 else { /* MP/DMS violation */
725 cpu_save_regs (iotrap); /* save CPU registers */
726 PC = ReadW (vectors + rqst_offset); /* vector to $RQST for processing */
727 }
728
729 if (debug_print) { /* debugging? */
730 fprint_regs (",", REG_CIR, 0); /* print interrupt source */
731 /* and cause */
732 if (mp_viol & SIGN)
733 fputs (", parity error", sim_deb);
734 else if (mp_mevff)
735 fputs (", DM violation", sim_deb);
736 else
737 fputs (", MP violation", sim_deb);
738 }
739 }
740
741 else { /* self-test instruction */
742 if (cpu_unit.flags & UNIT_VMAOS) { /* VMA/OS option installed? */
743 YR = 0000000; /* RPL switch (not implemented) */
744 AR = 0000000; /* LDR [B] (not implemented) */
745 SR = 0102077; /* test passed code */
746 PC = (PC + 1) & VAMASK; /* P+1 return for firmware OK */
747
748 if ((cpu_dev.dctrl & DEB_OS) && /* OS debug flag set, */
749 (sim_deb == NULL)) /* but debugging disabled? */
750 XR = 0; /* rev = 0 means RTE won't use ucode */
751 else
752 XR = 010; /* firmware revision 10B = 8 */
753 }
754
755 if (debug_print) /* debugging? */
756 fprint_regs (",", REG_X | REG_P_REL, /* print return registers */
757 err_PC + 1);
758 }
759
760 break; /* self-test is NOP if no firmware */
761
762 case 016: /* .ENTC/$DEV 105356 (OP_N) */
763 if (iotrap) { /* in trap cell? */
764 reason = cpu_save_state (iotrap); /* device interrupt */
765 AR = cpu_get_intbl (intaddr); /* get interrupt table value */
766
767 DEVINT:
768 vectors = ReadW (vctr); /* get address of vectors (in SMAP) */
769
770 if (INT16 (AR) < 0) /* negative (program ID)? */
771 PC = ReadW (vectors + sked_offset); /* vector to $SKED for processing */
772 else if (AR > 0) /* positive (EQT address)? */
773 PC = ReadW (vectors + cic2_offset); /* vector to $CIC2 for processing */
774 else /* zero (illegal interrupt) */
775 PC = ReadW (vectors + cic4_offset); /* vector to $CIC4 for processing */
776
777 if (debug_print) /* debugging? */
778 fprintf (sim_deb, /* print return registers */
779 ", CIR = %02o, INTBL = %06o",
780 intaddr, AR);
781 }
782
783 else { /* .ENTC instruction */
784 ma = (PC - 4) & VAMASK; /* get addr of entry point */
785 goto ENTX; /* continue with common processing */
786 }
787 break;
788
789 case 017: /* .DSPI/$TBG 105357 (OP_N) */
790 if (iotrap) { /* in trap cell? */
791 reason = cpu_save_state (iotrap); /* TBG interrupt */
792 vectors = ReadW (vctr); /* get address of vectors (in SMAP) */
793 PC = ReadW (vectors + clck_offset); /* vector to $CLCK for processing */
794
795 if (debug_print) /* debugging? */
796 fprint_regs (",", REG_CIR, 0); /* print interrupt source */
797 }
798
799 else /* .DSPI instruction */
800 reason = stop_inst; /* not implemented yet */
801
802 break;
803 }
804
805 if (debug_print) { /* debugging? */
806 fputc ('\n', sim_deb); /* terminate line */
807 memcpy (save_env, mp_handler, sizeof (jmp_buf)); /* restore original MP handler */
808 }
809
810 return reason;
811 }