1 /* hp2100_cpu1.c: HP 2100/1000 EAU/FP/IOP microcode simulator
2
3 Copyright (c) 2005-2016, Robert M. Supnik
4 Copyright (c) 2017-2019, J. David Bryan
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23 Except as contained in this notice, the names of the authors shall not be
24 used in advertising or otherwise to promote the sale, use or other dealings
25 in this Software without prior written authorization from the authors.
26
27 CPU1 Extended Arithmetic Unit, Floating Point, and I/O Processor
28 instructions
29
30 23-Jan-19 JDB Moved fmt_ab to hp2100_cpu5.c
31 02-Oct-18 JDB Replaced DMASK with D16_MASK or R_MASK as appropriate
32 02-Aug-18 JDB Moved UIG dispatcher to hp2100_cpu0.c
33 Moved FP and IOP dispatchers from hp2100_cpu2.c
34 25-Jul-18 JDB Use cpu_configuration instead of cpu_unit.flags for tests
35 07-Sep-17 JDB Removed unnecessary "uint16" casts
36 01-Aug-17 JDB Changed TIMER and RRR 16 to test for undefined stops
37 07-Jul-17 JDB Changed "iotrap" from uint32 to t_bool
38 26-Jun-17 JDB Replaced SEXT with SEXT16
39 22-Apr-17 JDB Improved the EAU shift/rotate instructions
40 21-Mar-17 JDB Fixed UIG 1 comment regarding 2000 IOP and F-Series
41 31-Jan-17 JDB Added fmt_ab to print A/B-register error codes
42 30-Jan-17 JDB Removed fprint_ops, fprint_regs (now redundant)
43 05-Aug-16 JDB Renamed the P register from "PC" to "PR"
44 24-Dec-14 JDB Added casts for explicit downward conversions
45 05-Apr-14 JDB Corrected typo in comments for cpu_ops
46 09-May-12 JDB Separated assignments from conditional expressions
47 11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h
48 05-Sep-08 JDB Moved option-present tests to UIG dispatchers
49 Call "user microcode" dispatcher for unclaimed UIG instructions
50 20-Apr-08 JDB Fixed VIS and SIGNAL to depend on the FPP and HAVE_INT64
51 28-Nov-07 JDB Added fprint_ops, fprint_regs for debug printouts
52 17-Nov-07 JDB Enabled DIAG as NOP on 1000 F-Series
53 04-Jan-07 JDB Added special DBI dispatcher for non-INT64 diagnostic
54 29-Dec-06 JDB Allows RRR as NOP if 2114 (diag config test)
55 01-Dec-06 JDB Substitutes FPP for firmware FP if HAVE_INT64
56 16-Oct-06 JDB Generalized operands for F-Series FP types
57 26-Sep-06 JDB Split hp2100_cpu1.c into multiple modules to simplify extensions
58 Added iotrap parameter to UIG dispatchers for RTE microcode
59 22-Feb-05 JDB Removed EXECUTE instruction (is NOP in actual microcode)
60 21-Jan-05 JDB Reorganized CPU option and operand processing flags
61 Split code along microcode modules
62 15-Jan-05 RMS Cloned from hp2100_cpu.c
63
64 Primary references:
65 - HP 1000 M/E/F-Series Computers Technical Reference Handbook
66 (5955-0282, March 1980)
67 - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation
68 (92851-90001, March 1981)
69 - Macro/1000 Reference Manual
70 (92059-90001, December 1992)
71
72 Additional references are listed with the associated firmware
73 implementations, as are the HP option model numbers pertaining to the
74 applicable CPUs.
75
76
77 This source file contains the Extended Arithmetic Unit simulator, the
78 single-precision floating-pointer simulator, and the HP 2000 I/O Processor
79 instructions simulator.
80
81 */
82
83
84
85 #include "hp2100_defs.h"
86 #include "hp2100_cpu.h"
87 #include "hp2100_cpu_dmm.h"
88
89 #if !defined (HAVE_INT64) /* int64 support unavailable */
90
91 #include "hp2100_cpu_fp.h"
92
93 #endif
94
95
96
97 /* EAU.
98
99 The Extended Arithmetic Unit (EAU) adds ten instructions with double-word
100 operands, including multiply, divide, shifts, and rotates. Option
101 implementation by CPU was as follows:
102
103 2114 2115 2116 2100 1000-M 1000-E 1000-F
104 ------ ------ ------ ------ ------ ------ ------
105 N/A 12579A 12579A std std std std
106
107 The instruction codes are mapped to routines as follows:
108
109 Instr. Bits
110 Code 15-8 7-4 2116 2100 1000-M 1000-E 1000-F Note
111 ------ ---- --- ------ ------ ------ ------ ------ ---------------------
112 100000 200 00 [diag] [diag] [self test]
113 100020 200 01 ASL ASL ASL ASL ASL Bits 3-0 encode shift
114 100040 200 02 LSL LSL LSL LSL LSL Bits 3-0 encode shift
115 100060 200 03 TIMER TIMER [deterministic delay]
116 100100 200 04 RRL RRL RRL RRL RRL Bits 3-0 encode shift
117 100200 200 10 MPY MPY MPY MPY MPY
118 100400 201 xx DIV DIV DIV DIV DIV
119 101020 202 01 ASR ASR ASR ASR ASR Bits 3-0 encode shift
120 101040 202 02 LSR LSR LSR LSR LSR Bits 3-0 encode shift
121 101100 202 04 RRR RRR RRR RRR RRR Bits 3-0 encode shift
122 104200 210 xx DLD DLD DLD DLD DLD
123 104400 211 xx DST DST DST DST DST
124
125 The remaining codes for bits 7-4 are undefined and will cause a simulator
126 stop if enabled. On a real 1000-M, all undefined instructions in the 200
127 group decode as MPY, and all in the 202 group decode as NOP. On a real
128 1000-E, instruction patterns 200/05 through 200/07 and 202/03 decode as NOP;
129 all others cause erroneous execution.
130
131 EAU instruction decoding on the 1000 M-series is convoluted. The JEAU
132 microorder maps IR bits 11, 9-7 and 5-4 to bits 2-0 of the microcode jump
133 address. The map is detailed on page IC-84 of the ERD.
134
135 The 1000 E/F-series add two undocumented instructions to the 200 group: TIMER
136 and DIAG. These are described in the ERD on page IA 5-5, paragraph 5-7. The
137 M-series executes these as MPY and RRL, respectively. A third instruction,
138 EXECUTE (100120), is also described but was never implemented, and the
139 E/F-series microcode execute a NOP for this instruction code.
140
141 If the EAU is not installed in a 2115 or 2116, EAU instructions execute as
142 NOPs or cause unimplemented instruction stops if enabled.
143
144
145 Implementation notes:
146
147 1. Under simulation, TIMER and DIAG cause undefined instruction stops if the
148 CPU is not an E/F-Series. Note that TIMER is intentionally executed by
149 several HP programs to differentiate between M- and E/F-series machines.
150
151 2. DIAG is not implemented under simulation. On the E/F, it performs a
152 destructive test of all installed memory. Because of this, it is only
153 functional if the machine is halted, i.e., if the instruction is
154 executed with the INSTR STEP button. If it is executed in a program,
155 the result is NOP.
156
157 3. The RRR 16 instruction is intentionally executed by the diagnostic
158 configurator on the 2114, which does not have an EAU, to differentiate
159 between 2114 and 2100/1000 CPUs.
160
161 4. The shift count is calculated unconditionally, as six of the ten
162 instructions will be using the value.
163
164 5. An arithmetic left shift must be handled as a special case because the
165 shifted operand bits "skip over" the sign bit. That is, the bits are
166 lost from the next-most-significant bit while preserving the MSB. For
167 all other shifts, including the arithmetic right shift, the operand may
168 be shifted and then merged with the appropriate fill bits.
169
170 6. The C standard specifies that the results of bitwise shifts with negative
171 signed operands are undefined (for left shifts) or implementation-defined
172 (for right shifts). Therefore, we must use unsigned operands and handle
173 arithmetic shifts explicitly.
174 */
175
cpu_eau(void)176 t_stat cpu_eau (void)
177 {
178 t_stat reason = SCPE_OK;
179 OPS op;
180 uint32 rs, qs, v1, v2, operand, fill, mask, shift;
181 int32 sop1, sop2;
182
183 if (!(cpu_configuration & CPU_EAU)) /* if the EAU is not installed */
184 return STOP (cpu_ss_unimpl); /* then the instructions execute as NOPs */
185
186 if (IR & 017) /* if the shift count is 1-15 */
187 shift = IR & 017; /* then use it verbatim */
188 else /* otherwise the count iz zero */
189 shift = 16; /* so use a shift count of 16 */
190
191 switch ((IR >> 8) & 0377) { /* decode IR<15:8> */
192
193 case 0200: /* EAU group 0 */
194 switch ((IR >> 4) & 017) { /* decode IR<7:4> */
195
196 case 000: /* DIAG 100000 */
197 if (!(cpu_configuration & CPU_1000_E_F)) /* if the CPU is not an E- or F-series */
198 return STOP (cpu_ss_undef); /* then the instruction is undefined */
199 break; /* and executes as NOP */
200
201
202 case 001: /* ASL 100020-100037 */
203 operand = TO_DWORD (BR, AR); /* form the double-word operand */
204
205 mask = D32_UMAX << 31 - shift; /* form a mask for the bits that will be lost */
206
207 if (operand & D32_SIGN) /* if the operand is negative */
208 O = (~operand & mask & D32_MASK) != 0; /* then set overflow if any of the lost bits are zeros */
209 else /* otherwise it's positive */
210 O = (operand & mask & D32_MASK) != 0; /* so set overflow if any of the lost bits are ones */
211
212 operand = operand << shift & D32_SMAX /* shift the operand left */
213 | operand & D32_SIGN; /* while keeping the original sign bit */
214
215 BR = UPPER_WORD (operand); /* split the operand */
216 AR = LOWER_WORD (operand); /* into its constituent parts */
217 break;
218
219
220 case 002: /* LSL 100040-100057 */
221 operand = TO_DWORD (BR, AR) << shift; /* shift the double-word operand left */
222
223 BR = UPPER_WORD (operand); /* split the operand */
224 AR = LOWER_WORD (operand); /* into its constituent parts */
225 break;
226
227
228 case 004: /* RRL 100100-100117 */
229 operand = TO_DWORD (BR, AR); /* form the double-word operand */
230 fill = operand; /* and fill with operand bits */
231
232 operand = operand << shift /* rotate the operand left */
233 | fill >> 32 - shift; /* while filling in on the right */
234
235 BR = UPPER_WORD (operand); /* split the operand */
236 AR = LOWER_WORD (operand); /* into its constituent parts */
237 break;
238
239
240 case 003: /* TIMER 100060 */
241 if (cpu_configuration & CPU_1000_E_F) { /* if the CPU is an E- or F-series */
242 BR = BR + 1 & R_MASK; /* then increment B */
243
244 if (BR != 0) /* if B did not roll over */
245 PR = err_PR; /* then repeat the instruction */
246 break;
247 }
248
249 else { /* otherwise it's a 21xx or 1000 M-Series */
250 reason = STOP (cpu_ss_undef); /* and the instruction is undefined */
251
252 if (reason != SCPE_OK /* if a stop is indicated */
253 || cpu_configuration & CPU_21XX) /* or the CPU is a 21xx */
254 break; /* then the instruction executes as NOP */
255 }
256
257 /* fall through into the MPY case if 1000 M-Series */
258
259 case 010: /* MPY 100200 (OP_K) */
260 reason = cpu_ops (OP_K, op); /* get operand */
261
262 if (reason == SCPE_OK) { /* successful eval? */
263 sop1 = SEXT16 (AR); /* sext AR */
264 sop2 = SEXT16 (op[0].word); /* sext mem */
265 sop1 = sop1 * sop2; /* signed mpy */
266 BR = UPPER_WORD (sop1); /* to BR'AR */
267 AR = LOWER_WORD (sop1);
268 O = 0; /* no overflow */
269 }
270 break;
271
272
273 default: /* others undefined */
274 return STOP (cpu_ss_unimpl);
275 }
276
277 break;
278
279
280 case 0201: /* DIV 100400 (OP_K) */
281 reason = cpu_ops (OP_K, op); /* get operand */
282
283 if (reason != SCPE_OK) /* eval failed? */
284 break;
285
286 rs = qs = BR & D16_SIGN; /* save divd sign */
287
288 if (rs) { /* neg? */
289 AR = (~AR + 1) & R_MASK; /* make B'A pos */
290 BR = (~BR + (AR == 0)) & R_MASK; /* make divd pos */
291 }
292
293 v2 = op[0].word; /* divr = mem */
294
295 if (v2 & D16_SIGN) { /* neg? */
296 v2 = (~v2 + 1) & D16_MASK; /* make divr pos */
297 qs = qs ^ D16_SIGN; /* sign of quotient */
298 }
299
300 if (BR >= v2) /* if the divisor is too small */
301 O = 1; /* then set overflow */
302
303 else { /* maybe... */
304 O = 0; /* assume ok */
305 v1 = (BR << 16) | AR; /* 32b divd */
306 AR = (v1 / v2) & R_MASK; /* quotient */
307 BR = (v1 % v2) & R_MASK; /* remainder */
308
309 if (AR) { /* quotient > 0? */
310 if (qs) /* apply quo sign */
311 AR = NEG16 (AR);
312
313 if ((AR ^ qs) & D16_SIGN) /* still wrong? ovflo */
314 O = 1;
315 }
316
317 if (rs)
318 BR = NEG16 (BR); /* apply rem sign */
319 }
320 break;
321
322
323 case 0202: /* EAU group 2 */
324 switch ((IR >> 4) & 017) { /* decode IR<7:4> */
325
326 case 001: /* ASR 101020-101037 */
327 O = 0; /* clear ovflo */
328
329 operand = TO_DWORD (BR, AR); /* form the double-word operand */
330 fill = (operand & D32_SIGN ? ~0 : 0); /* and fill with copies of the sign bit */
331
332 operand = operand >> shift /* shift the operand right */
333 | fill << 32 - shift; /* while filling in with sign bits */
334
335 BR = UPPER_WORD (operand); /* split the operand */
336 AR = LOWER_WORD (operand); /* into its constituent parts */
337 break;
338
339
340 case 002: /* LSR 101040-101057 */
341 operand = TO_DWORD (BR, AR) >> shift; /* shift the double-word operand right */
342
343 BR = UPPER_WORD (operand); /* split the operand */
344 AR = LOWER_WORD (operand); /* into its constituent parts */
345 break;
346
347
348 case 004: /* RRR 101100-101117 */
349 operand = TO_DWORD (BR, AR); /* form the double-word operand */
350 fill = operand; /* and fill with operand bits */
351
352 operand = operand >> shift /* rotate the operand right */
353 | fill << 32 - shift; /* while filling in on the left */
354
355 BR = UPPER_WORD (operand); /* split the operand */
356 AR = LOWER_WORD (operand); /* into its constituent parts */
357 break;
358
359
360 default: /* others undefined */
361 return STOP (cpu_ss_undef);
362 }
363
364 break;
365
366
367 case 0210: /* DLD 104200 (OP_D) */
368 reason = cpu_ops (OP_D, op); /* get operand */
369
370 if (reason == SCPE_OK) { /* successful eval? */
371 AR = UPPER_WORD (op[0].dword); /* load AR */
372 BR = LOWER_WORD (op[0].dword); /* load BR */
373 }
374 break;
375
376
377 case 0211: /* DST 104400 (OP_A) */
378 reason = cpu_ops (OP_A, op); /* get operand */
379
380 if (reason == SCPE_OK) { /* successful eval? */
381 WriteW (op[0].word, AR); /* store AR */
382 WriteW ((op[0].word + 1) & LA_MASK, BR); /* store BR */
383 }
384 break;
385
386
387 default: /* should never get here */
388 return SCPE_IERR; /* bad call from cpu_instr */
389 }
390
391 return reason;
392 }
393
394
395
396 #if !defined (HAVE_INT64) /* int64 support unavailable */
397
398 /* Single-Precision Floating Point Instructions.
399
400 The 2100 and 1000 CPUs share the single-precision (two word) floating-point
401 instruction codes. Floating-point firmware was an option on the 2100 and was
402 standard on the 1000-M and E. The 1000-F had a standard hardware Floating
403 Point Processor that executed these six instructions and added extended- and
404 double-precision floating- point instructions, as well as double-integer
405 instructions (the FPP is simulated separately).
406
407 Option implementation by CPU was as follows:
408
409 2114 2115 2116 2100 1000-M 1000-E 1000-F
410 ------ ------ ------ ------ ------ ------ ------
411 N/A N/A N/A 12901A std std N/A
412
413 The instruction codes for the 2100 and 1000-M/E systems are mapped to
414 routines as follows:
415
416 Instr. 2100/1000-M/E Description
417 ------ ------------- -----------------------------------
418 105000 FAD Single real add
419 105020 FSB Single real subtract
420 105040 FMP Single real multiply
421 105060 FDV Single real divide
422 105100 FIX Single integer to single real fix
423 105120 FLT Single real to single integer float
424
425 Bits 3-0 are not decoded by these instructions, so FAD (e.g.) would be
426 executed by any instruction in the range 105000-105017.
427
428 Implementation note: rather than have two simulators that each executes the
429 single-precision FP instruction set, we compile conditionally, based on the
430 availability of 64-bit integer support in the host compiler. 64-bit integers
431 are required for the FPP, so if they are available, then the FPP is used to
432 handle the six single-precision instructions for the 2100 and M/E-Series, and
433 this function is omitted. If support is unavailable, this function is used
434 instead.
435
436 Implementation note: the operands to FAD, etc. are floating-point values, so
437 OP_F would normally be used. However, the firmware FP support routines want
438 floating-point operands as 32-bit integer values, so OP_D is used to achieve
439 this.
440 */
441
442 static const OP_PAT op_fp[8] = {
443 OP_D, OP_D, OP_D, OP_D, /* FAD FSB FMP FDV */
444 OP_N, OP_N, OP_N, OP_N /* FIX FLT --- --- */
445 };
446
cpu_fp(void)447 t_stat cpu_fp (void)
448 {
449 t_stat reason = SCPE_OK;
450 OPS op;
451 uint32 entry;
452
453 entry = (IR >> 4) & 017; /* mask to entry point */
454
455 if (op_fp [entry] != OP_N) {
456 reason = cpu_ops (op_fp [entry], op); /* get instruction operands */
457
458 if (reason != SCPE_OK) /* evaluation failed? */
459 return reason; /* return reason for failure */
460 }
461
462 switch (entry) { /* decode IR<7:4> */
463
464 case 000: /* FAD 105000 (OP_D) */
465 O = f_as (op[0].dword, 0); /* add, upd ovflo */
466 break;
467
468 case 001: /* FSB 105020 (OP_D) */
469 O = f_as (op[0].dword, 1); /* sub, upd ovflo */
470 break;
471
472 case 002: /* FMP 105040 (OP_D) */
473 O = f_mul (op[0].dword); /* mul, upd ovflo */
474 break;
475
476 case 003: /* FDV 105060 (OP_D) */
477 O = f_div (op[0].dword); /* div, upd ovflo */
478 break;
479
480 case 004: /* FIX 105100 (OP_N) */
481 O = f_fix (); /* fix, upd ovflo */
482 break;
483
484 case 005: /* FLT 105120 (OP_N) */
485 O = f_flt (); /* float, upd ovflo */
486 break;
487
488 default: /* should be impossible */
489 return SCPE_IERR;
490 }
491
492 return reason;
493 }
494
495 #endif /* int64 support unavailable */
496
497
498
499 /* HP 2000 I/O Processor.
500
501 The IOP accelerates certain operations of the HP 2000 Time-Share BASIC system
502 I/O processor. Most 2000 systems were delivered with 2100 CPUs, although IOP
503 microcode was developed for the 1000-M and 1000-E. As the I/O processors
504 were specific to the 2000 system, general compatibility with other CPU
505 microcode options was unnecessary, and indeed no other options were possible
506 for the 2100.
507
508 Option implementation by CPU was as follows:
509
510 2114 2115 2116 2100 1000-M 1000-E 1000-F
511 ------ ------ ------ ------ ------ ------ ------
512 N/A N/A N/A 13206A 13207A 22702A N/A
513
514 The routines are mapped to instruction codes as follows:
515
516 Instr. 2100 1000-M/E Description
517 ------ ---------- ---------- --------------------------------------------
518 SAI 105060-117 101400-037 Store A indexed by B (+/- offset in IR<4:0>)
519 LAI 105020-057 105400-037 Load A indexed by B (+/- offset in IR<4:0>)
520 CRC 105150 105460 Generate CRC
521 REST 105340 105461 Restore registers from stack
522 READF 105220 105462 Read F register (stack pointer)
523 INS -- 105463 Initialize F register (stack pointer)
524 ENQ 105240 105464 Enqueue
525 PENQ 105257 105465 Priority enqueue
526 DEQ 105260 105466 Dequeue
527 TRSLT 105160 105467 Translate character
528 ILIST 105000 105470 Indirect address list (similar to $SETP)
529 PRFEI 105222 105471 Power fail exit with I/O
530 PRFEX 105223 105472 Power fail exit
531 PRFIO 105221 105473 Power fail I/O
532 SAVE 105362 105474 Save registers to stack
533
534 MBYTE 105120 105765 Move bytes (MBT)
535 MWORD 105200 105777 Move words (MVW)
536 SBYTE 105300 105764 Store byte (SBT)
537 LBYTE 105320 105763 Load byte (LBT)
538
539 The INS instruction was not required in the 2100 implementation because the
540 stack pointer was actually the memory protect fence register and so could be
541 loaded directly with an OTA/B 05. Also, the 1000 implementation did not
542 offer the MBYTE, MWORD, SBYTE, and LBYTE instructions because the equivalent
543 instructions from the standard Extended Instruction Group were used instead.
544
545 Note that the 2100 MBYTE and MWORD instructions operate slightly differently
546 from the 1000 MBT and MVW instructions. Specifically, the move count is
547 signed on the 2100 and unsigned on the 1000. A negative count on the 2100
548 results in a NOP.
549
550 The simulator remaps the 2100 instructions to the 1000 codes. The four EIG
551 equivalents are dispatched to the EIG simulator. The rest are handled here.
552
553 Additional reference:
554 - HP 2000 Computer System Sources and Listings Documentation
555 (22687-90020, undated), section 3, pages 2-74 through 2-91
556
557
558 Implementation notes:
559
560 1. The SAVE and RESTR instructions use the (otherwise unused) SP register on
561 the 1000 as the stack pointer. On the 2100, there is no SP register, so
562 the instructions use the memory protect fence register as the stack
563 pointer. We update the 2100 fence because it could affect CPU operation
564 if MP is turned on (although, in practice, the 2100 IOP does not use
565 memory protect and so never enables it).
566 */
567
568 static const OP_PAT op_iop[16] = {
569 OP_V, OP_N, OP_N, OP_N, /* CRC RESTR READF INS */
570 OP_N, OP_N, OP_N, OP_V, /* ENQ PENQ DEQ TRSLT */
571 OP_AC, OP_CVA, OP_A, OP_CV, /* ILIST PRFEI PRFEX PRFIO */
572 OP_N, OP_N, OP_N, OP_N /* SAVE --- --- --- */
573 };
574
cpu_iop(uint32 intrq)575 t_stat cpu_iop (uint32 intrq)
576 {
577 t_stat reason = SCPE_OK;
578 OPS op;
579 uint8 byte;
580 uint32 entry, i;
581 HP_WORD hp, tp, t, wc, MA;
582
583 if (cpu_configuration & CPU_2100) { /* 2100 IOP? */
584 if ((IR >= 0105020) && (IR <= 0105057)) /* remap LAI */
585 IR = 0105400 | (IR - 0105020);
586 else if ((IR >= 0105060) && (IR <= 0105117)) /* remap SAI */
587 IR = 0101400 | (IR - 0105060);
588 else {
589 switch (IR) { /* remap others */
590 case 0105000: IR = 0105470; break; /* ILIST */
591 case 0105120: return cpu_eig (0105765, intrq); /* MBYTE (maps to MBT) */
592 case 0105150: IR = 0105460; break; /* CRC */
593 case 0105160: IR = 0105467; break; /* TRSLT */
594 case 0105200: return cpu_eig (0105777, intrq); /* MWORD (maps to MVW) */
595 case 0105220: IR = 0105462; break; /* READF */
596 case 0105221: IR = 0105473; break; /* PRFIO */
597 case 0105222: IR = 0105471; break; /* PRFEI */
598 case 0105223: IR = 0105472; break; /* PRFEX */
599 case 0105240: IR = 0105464; break; /* ENQ */
600 case 0105257: IR = 0105465; break; /* PENQ */
601 case 0105260: IR = 0105466; break; /* DEQ */
602 case 0105300: return cpu_eig (0105764, intrq); /* SBYTE (maps to SBT) */
603 case 0105320: return cpu_eig (0105763, intrq); /* LBYTE (maps to LBT) */
604 case 0105340: IR = 0105461; break; /* REST */
605 case 0105362: IR = 0105474; break; /* SAVE */
606
607 default: /* all others invalid */
608 return STOP (cpu_ss_unimpl);
609 }
610 }
611 }
612
613 entry = IR & 077; /* mask to entry point */
614
615 if (entry <= 037) { /* LAI/SAI 10x400-437 */
616 MA = ((entry - 020) + BR) & LA_MASK; /* +/- offset */
617
618 if (IR & AB_MASK) /* if this is an LAI instruction */
619 AR = ReadW (MA); /* then load the A register */
620 else /* otherwise */
621 WriteW (MA, AR); /* store the A register */
622
623 return reason;
624 }
625
626 else if (entry <= 057) /* IR = 10x440-457? */
627 return STOP (cpu_ss_unimpl); /* not part of IOP */
628
629 entry = entry - 060; /* offset 10x460-477 */
630
631 if (op_iop [entry] != OP_N) {
632 reason = cpu_ops (op_iop [entry], op); /* get instruction operands */
633
634 if (reason != SCPE_OK) /* evaluation failed? */
635 return reason; /* return reason for failure */
636 }
637
638 switch (entry) { /* decode IR<5:0> */
639
640 case 000: /* CRC 105460 (OP_V) */
641 t = ReadW (op[0].word) ^ (AR & 0377); /* xor prev CRC and char */
642 for (i = 0; i < 8; i++) { /* apply polynomial */
643 t = (t >> 1) | ((t & 1) << 15); /* rotate right */
644 if (t & D16_SIGN) t = t ^ 020001; /* old t<0>? xor */
645 }
646 WriteW (op[0].word, t); /* rewrite CRC */
647 break;
648
649 case 001: /* RESTR 105461 (OP_N) */
650 SPR = (SPR - 1) & LA_MASK; /* decr stack ptr */
651 t = ReadW (SPR); /* get E and O */
652 O = ((t >> 1) ^ 1) & 1; /* restore O */
653 E = t & 1; /* restore E */
654 SPR = (SPR - 1) & LA_MASK; /* decr sp */
655 BR = ReadW (SPR); /* restore B */
656 SPR = (SPR - 1) & LA_MASK; /* decr sp */
657 AR = ReadW (SPR); /* restore A */
658 if (cpu_configuration & CPU_2100) /* 2100 keeps sp in MP FR */
659 mp_fence = SPR; /* (in case MP is turned on) */
660 break;
661
662 case 002: /* READF 105462 (OP_N) */
663 AR = SPR; /* copy stk ptr */
664 break;
665
666 case 003: /* INS 105463 (OP_N) */
667 SPR = AR; /* init stk ptr */
668 break;
669
670 case 004: /* ENQ 105464 (OP_N) */
671 hp = ReadW (AR & LA_MASK); /* addr of head */
672 tp = ReadW ((AR + 1) & LA_MASK); /* addr of tail */
673 WriteW ((BR - 1) & LA_MASK, 0); /* entry link */
674 WriteW ((tp - 1) & LA_MASK, BR); /* tail link */
675 WriteW ((AR + 1) & LA_MASK, BR); /* queue tail */
676 if (hp != 0) PR = (PR + 1) & LA_MASK; /* q not empty? skip */
677 break;
678
679 case 005: /* PENQ 105465 (OP_N) */
680 hp = ReadW (AR & LA_MASK); /* addr of head */
681 WriteW ((BR - 1) & LA_MASK, hp); /* becomes entry link */
682 WriteW (AR & LA_MASK, BR); /* queue head */
683 if (hp == 0) /* q empty? */
684 WriteW ((AR + 1) & LA_MASK, BR); /* queue tail */
685 else PR = (PR + 1) & LA_MASK; /* skip */
686 break;
687
688 case 006: /* DEQ 105466 (OP_N) */
689 BR = ReadW (AR & LA_MASK); /* addr of head */
690 if (BR) { /* queue not empty? */
691 hp = ReadW ((BR - 1) & LA_MASK); /* read hd entry link */
692 WriteW (AR & LA_MASK, hp); /* becomes queue head */
693 if (hp == 0) /* q now empty? */
694 WriteW ((AR + 1) & LA_MASK, (AR + 1) & R_MASK);
695 PR = (PR + 1) & LA_MASK; /* skip */
696 }
697 break;
698
699 case 007: /* TRSLT 105467 (OP_V) */
700 wc = ReadW (op[0].word); /* get count */
701 if (wc & D16_SIGN) break; /* cnt < 0? */
702 while (wc != 0) { /* loop */
703 MA = (AR + AR + ReadB (BR)) & LA_MASK;
704 byte = ReadB (MA); /* xlate */
705 WriteB (BR, byte); /* store char */
706 BR = (BR + 1) & R_MASK; /* incr ptr */
707 wc = (wc - 1) & D16_MASK; /* decr cnt */
708 if (wc && intrq) { /* more and intr? */
709 WriteW (op[0].word, wc); /* save count */
710 PR = err_PR; /* stop for now */
711 break;
712 }
713 }
714 break;
715
716 case 010: /* ILIST 105470 (OP_AC) */
717 do { /* for count */
718 WriteW (op[0].word, AR); /* write AR to mem */
719 AR = (AR + 1) & R_MASK; /* incr AR */
720 op[0].word = (op[0].word + 1) & LA_MASK; /* incr MA */
721 op[1].word = (op[1].word - 1) & D16_MASK; /* decr count */
722 }
723 while (op[1].word != 0);
724 break;
725
726 case 011: /* PRFEI 105471 (OP_CVA) */
727 WriteW (op[1].word, 1); /* set flag */
728 reason = cpu_iog (op[0].word); /* execute I/O instr */
729 op[0].word = op[2].word; /* set rtn and fall through */
730
731 case 012: /* PRFEX 105472 (OP_A) */
732 PCQ_ENTRY;
733 PR = ReadW (op[0].word) & LA_MASK; /* jump indirect */
734 WriteW (op[0].word, 0); /* clear exit */
735 break;
736
737 case 013: /* PRFIO 105473 (OP_CV) */
738 WriteW (op[1].word, 1); /* set flag */
739 reason = cpu_iog (op[0].word); /* execute instr */
740 break;
741
742 case 014: /* SAVE 105474 (OP_N) */
743 WriteW (SPR, AR); /* save A */
744 SPR = (SPR + 1) & LA_MASK; /* incr stack ptr */
745 WriteW (SPR, BR); /* save B */
746 SPR = (SPR + 1) & LA_MASK; /* incr stack ptr */
747 t = (HP_WORD) ((O ^ 1) << 1 | E); /* merge E and O */
748 WriteW (SPR, t); /* save E and O */
749 SPR = (SPR + 1) & LA_MASK; /* incr stack ptr */
750 if (cpu_configuration & CPU_2100) /* 2100 keeps sp in MP FR */
751 mp_fence = SPR; /* (in case MP is turned on) */
752 break;
753
754 default: /* instruction unimplemented */
755 return STOP (cpu_ss_unimpl);
756 }
757
758 return reason;
759 }
760