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