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