1 /* pdp11_cis.c: PDP-11 CIS optional instruction set simulator
2 
3    Copyright (c) 1993-2008, Robert M Supnik
4 
5    Permission is hereby granted, free of charge, to any person obtaining a
6    copy of this software and associated documentation files (the "Software"),
7    to deal in the Software without restriction, including without limitation
8    the rights to use, copy, modify, merge, publish, distribute, sublicense,
9    and/or sell copies of the Software, and to permit persons to whom the
10    Software is furnished to do so, subject to the following conditions:
11 
12    The above copyright notice and this permission notice shall be included in
13    all copies or substantial portions of the Software.
14 
15    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18    ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19    IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22    Except as contained in this notice, the name of Robert M Supnik shall not be
23    used in advertising or otherwise to promote the sale, use or other dealings
24    in this Software without prior written authorization from Robert M Supnik.
25 
26    This module simulates the PDP-11 commercial instruction set (CIS).
27 
28    16-Oct-08    RMS     Fixed overflow bug in ASHx (Word/NibbleLShift)
29                         Fixed bug in DIVx (LntDstr calculation)
30    30-May-06    RMS     Added interrupt tests to character instructions
31                         Added 11/44 stack probe test to MOVCx (only)
32    22-May-06    RMS     Fixed bug in decode table (John Dundas)
33                         Fixed bug in ASHP (John Dundas)
34                         Fixed bug in write decimal string with mmgt enabled
35                         Fixed bug in 0-length strings in multiply/divide
36    16-Sep-04    RMS     Fixed bug in CMPP/N of negative strings
37    17-Oct-02    RMS     Fixed compiler warning (Hans Pufal)
38    08-Oct-02    RMS     Fixed macro definitions
39 
40    The commercial instruction set consists of three instruction formats:
41 
42    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    register operands
43    | 0  1  1  1  1  1| 0  0  0  0|      opcode     |    076030:076057
44    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    076070:076077
45 
46    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    inline operands
47    | 0  1  1  1  1  1| 0  0  0  1|      opcode     |    076130:076157
48    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    076170:076177
49 
50    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    load descriptors
51    | 0  1  1  1  1  1| 0  0  0  0|op| 1  0|  reg   |    076020:076027
52    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    076060:076067
53 
54    The CIS instructions operate on character strings, packed (decimal)
55    strings, and numeric (decimal) strings.  Strings are described by
56    a two word descriptor:
57 
58    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
59    |                 length in bytes               |    char string
60    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    descriptor
61    |             starting byte address             |
62    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
63 
64    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
65    |  |str type|                    |   length     |    decimal string
66    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    descriptor
67    |             starting byte address             |
68    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
69 
70    Decimal string types are:
71 
72      <14:12>            data type               bytes occupied by n digits
73         0               signed zoned                    n
74         1               unsigned zone                   n
75         2               trailing overpunch              n
76         3               leading overpunch               n
77         4               trailing separate               n+1
78         5               leading separate                n+1
79         6               signed packed                   n/2 +1
80         7               unsigned packed                 n/2 +1
81 
82    Zero length character strings occupy no memory; zero length decimal strings
83    require either zero bytes (zoned, overpunch) or one byte (separate, packed).
84 
85    CIS instructions can run for a very long time, so they are interruptible
86    and restartable.  In the simulator, all instructions run to completion.
87    The code is unoptimized.
88 */
89 
90 #include "pdp11_defs.h"
91 
92 /* Opcode bits */
93 
94 #define INLINE          0100                            /* inline */
95 #define PACKED          0020                            /* packed */
96 #define NUMERIC         0000                            /* numeric */
97 
98 /* Interrupt test latency */
99 
100 #define INT_TEST        100
101 
102 /* Operand type definitions */
103 
104 #define R0_DESC         1                               /* descr in R0:R1 */
105 #define R2_DESC         2                               /* descr in R2:R3 */
106 #define R4_DESC         3                               /* descr in R4:R5 */
107 #define R4_ARG          4                               /* argument in R4 */
108 #define IN_DESC         5                               /* inline descriptor */
109 #define IN_ARG          6                               /* inline argument */
110 #define MAXOPN          4                               /* max # operands */
111 
112 /* Decimal data type definitions */
113 
114 #define XZ              0                               /* signed zoned */
115 #define UZ              1                               /* unsigned zoned */
116 #define TO              2                               /* trailing overpunch */
117 #define LO              3                               /* leading overpunch */
118 #define TS              4                               /* trailing separate */
119 #define LS              5                               /* leading separate */
120 #define XP              6                               /* signed packed */
121 #define UP              7                               /* unsigned packed */
122 
123 /* Decimal descriptor definitions */
124 
125 #define DTYP_M          07                              /* type mask */
126 #define DTYP_V          12                              /* type position */
127 #define DLNT_M          037                             /* length mask */
128 #define DLNT_V          0                               /* length position */
129 #define GET_DTYP(x)     (((x) >> DTYP_V) & DTYP_M)
130 #define GET_DLNT(x)     (((x) >> DLNT_V) & DLNT_M)
131 
132 /* Shift operand definitions */
133 
134 #define ASHRND_M        017                             /* round digit mask */
135 #define ASHRND_V        8                               /* round digit pos */
136 #define ASHLNT_M        0377                            /* shift count mask */
137 #define ASHLNT_V        0                               /* shift length pos */
138 #define ASHSGN          0200                            /* shift sign */
139 #define GET_ASHRND(x)   (((x) >> ASHRND_V) & ASHRND_M)
140 #define GET_ASHLNT(x)   (((x) >> ASHLNT_V) & ASHLNT_M)
141 
142 /* Operand array aliases */
143 
144 #define A1LNT           arg[0]
145 #define A1ADR           arg[1]
146 #define A2LNT           arg[2]
147 #define A2ADR           arg[3]
148 #define A3LNT           arg[4]
149 #define A3ADR           arg[5]
150 #define A1              &arg[0]
151 #define A2              &arg[2]
152 #define A3              &arg[4]
153 
154 /* Condition code macros */
155 
156 #define GET_BIT(ir,n)   (((ir) >> (n)) & 1)
157 #define GET_SIGN_L(ir)  GET_BIT((ir), 31)
158 #define GET_SIGN_W(ir)  GET_BIT((ir), 15)
159 #define GET_SIGN_B(ir)  GET_BIT((ir), 7)
160 #define GET_Z(ir)       ((ir) == 0)
161 
162 /* Decimal string structure */
163 
164 #define DSTRLNT         4
165 #define DSTRMAX         (DSTRLNT - 1)
166 #define MAXDVAL         429496730                       /* 2^32 / 10 */
167 
168 typedef struct {
169     uint32              sign;
170     uint32              val[DSTRLNT];
171     } DSTR;
172 
173 static DSTR Dstr0 = { 0, 0, 0, 0, 0 };
174 
175 extern int32 isenable, dsenable;
176 extern int32 N, Z, V, C, fpd, ipl;
177 extern int32 R[8], trap_req;
178 extern int32 sim_interval;
179 extern uint32 cpu_type;
180 extern FILE *sim_deb;
181 
182 int32 ReadDstr (int32 *dscr, DSTR *dec, int32 flag);
183 void WriteDstr (int32 *dscr, DSTR *dec, int32 flag);
184 int32 AddDstr (DSTR *src1, DSTR *src2, DSTR *dst, int32 cin);
185 void SubDstr (DSTR *src1, DSTR *src2, DSTR *dst);
186 int32 CmpDstr (DSTR *src1, DSTR *src2);
187 int32 TestDstr (DSTR *dsrc);
188 int32 LntDstr (DSTR *dsrc, int32 nz);
189 uint32 NibbleLshift (DSTR *dsrc, int32 sc);
190 uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin);
191 int32 WordLshift (DSTR *dsrc, int32 sc);
192 void WordRshift (DSTR *dsrc, int32 sc);
193 void CreateTable (DSTR *dsrc, DSTR mtable[10]);
194 t_bool cis_int_test (int32 cycles, int32 oldpc, t_stat *st);
195 int32 movx_setup (int32 op, int32 *arg);
196 void movx_cleanup (int32 op);
197 
198 extern int32 ReadW (int32 addr);
199 extern void WriteW (int32 data, int32 addr);
200 extern int32 ReadB (int32 addr);
201 extern int32 ReadMB (int32 addr);
202 extern void WriteB (int32 data, int32 addr);
203 extern int32 calc_ints (int32 nipl, int32 trq);
204 
205 /* Table of instruction operands */
206 
207 static int32 opntab[128][MAXOPN] = {
208     0, 0, 0, 0, 0, 0, 0, 0,                             /* 000 - 007 */
209     0, 0, 0, 0, 0, 0, 0, 0,
210     0, 0, 0, 0, 0, 0, 0, 0,
211     0, 0, 0, 0, 0, 0, 0, 0,
212     0, 0, 0, 0, 0, 0, 0, 0,                             /* 010 - 017 */
213     0, 0, 0, 0, 0, 0, 0, 0,
214     0, 0, 0, 0, 0, 0, 0, 0,
215     0, 0, 0, 0, 0, 0, 0, 0,
216     0, 0, 0, 0, 0, 0, 0, 0,                             /* LD2R */
217     0, 0, 0, 0, 0, 0, 0, 0,
218     0, 0, 0, 0, 0, 0, 0, 0,
219     0, 0, 0, 0, 0, 0, 0, 0,
220     0, 0, 0, 0,                                         /* MOVC */
221     0, 0, 0, 0,                                         /* MOVRC */
222     0, 0, 0, 0,                                         /* MOVTC */
223                 0, 0, 0, 0,                             /* 033 */
224     0, 0, 0, 0, 0, 0, 0, 0,                             /* 034 - 037 */
225     0, 0, 0, 0, 0, 0, 0, 0,
226     0, 0, 0, 0,                                         /* LOCC */
227     0, 0, 0, 0,                                         /* SKPC */
228     0, 0, 0, 0,                                         /* SCANC */
229     0, 0, 0, 0,                                         /* SPANC */
230     0, 0, 0, 0,                                         /* CMPC */
231     0, 0, 0, 0,                                         /* MATC */
232     0, 0, 0, 0, 0, 0, 0, 0,                             /* 046 - 047 */
233     R0_DESC, R2_DESC, R4_DESC, 0,                       /* ADDN */
234     R0_DESC, R2_DESC, R4_DESC, 0,                       /* SUBN */
235     R0_DESC, R2_DESC, 0, 0,                             /* CMPN */
236     R0_DESC, 0, 0, 0,                                   /* CVTNL */
237     R0_DESC, R2_DESC, 0, 0,                             /* CVTPN */
238     R0_DESC, R2_DESC, 0, 0,                             /* CVTNP */
239     R0_DESC, R2_DESC, R4_ARG, 0,                        /* ASHN */
240     R0_DESC, 0, 0, 0,                                   /* CVTLN */
241     0, 0, 0, 0, 0, 0, 0, 0,                             /* LD3R */
242     0, 0, 0, 0, 0, 0, 0, 0,
243     0, 0, 0, 0, 0, 0, 0, 0,
244     0, 0, 0, 0, 0, 0, 0, 0,
245     R0_DESC, R2_DESC, R4_DESC, 0,                       /* ADDP */
246     R0_DESC, R2_DESC, R4_DESC, 0,                       /* SUBP */
247     R0_DESC, R2_DESC, 0, 0,                             /* CMPP */
248     R0_DESC, 0, 0, 0,                                   /* CVTPL */
249     R0_DESC, R2_DESC, R4_DESC, 0,                       /* MULP */
250     R0_DESC, R2_DESC, R4_DESC, 0,                       /* DIVP */
251     R0_DESC, R2_DESC, R4_ARG, 0,                        /* ASHP */
252     R0_DESC, 0, 0, 0,                                   /* CVTLP */
253     0, 0, 0, 0, 0, 0, 0, 0,                             /* 100 - 107 */
254     0, 0, 0, 0, 0, 0, 0, 0,
255     0, 0, 0, 0, 0, 0, 0, 0,
256     0, 0, 0, 0, 0, 0, 0, 0,
257     0, 0, 0, 0, 0, 0, 0, 0,                             /* 110 - 117 */
258     0, 0, 0, 0, 0, 0, 0, 0,
259     0, 0, 0, 0, 0, 0, 0, 0,
260     0, 0, 0, 0, 0, 0, 0, 0,
261     0, 0, 0, 0, 0, 0, 0, 0,                             /* 120 - 127 */
262     0, 0, 0, 0, 0, 0, 0, 0,
263     0, 0, 0, 0, 0, 0, 0, 0,
264     0, 0, 0, 0, 0, 0, 0, 0,
265     IN_DESC, IN_DESC, IN_ARG, 0,                        /* MOVCI */
266     IN_DESC, IN_DESC, IN_ARG, 0,                        /* MOVRCI */
267     IN_DESC, IN_DESC, IN_ARG, IN_ARG,                   /* MOVTCI */
268                 0, 0, 0, 0,                             /* 133 */
269     0, 0, 0, 0, 0, 0, 0, 0,                             /* 134 - 137 */
270     0, 0, 0, 0, 0, 0, 0, 0,
271     IN_DESC, IN_ARG, 0, 0,                              /* LOCCI */
272     IN_DESC, IN_ARG, 0, 0,                              /* SKPCI */
273     IN_DESC, IN_DESC, 0, 0,                             /* SCANCI */
274     IN_DESC, IN_DESC, 0, 0,                             /* SPANCI */
275     IN_DESC, IN_DESC, IN_ARG, 0,                        /* CMPCI */
276     IN_DESC, IN_DESC, 0, 0,                             /* MATCI */
277     0, 0, 0, 0, 0, 0, 0, 0,                             /* 146 - 147 */
278     IN_DESC, IN_DESC, IN_DESC, 0,                       /* ADDNI */
279     IN_DESC, IN_DESC, IN_DESC, 0,                       /* SUBNI */
280     IN_DESC, IN_DESC, 0, 0,                             /* CMPNI */
281     IN_DESC, IN_ARG, 0, 0,                              /* CVTNLI */
282     IN_DESC, IN_DESC, 0, 0,                             /* CVTPNI */
283     IN_DESC, IN_DESC, 0, 0,                             /* CVTNPI */
284     IN_DESC, IN_DESC, IN_ARG, 0,                        /* ASHNI */
285     IN_DESC, IN_DESC, 0, 0,                             /* CVTLNI */
286     0, 0, 0, 0, 0, 0, 0, 0,                             /* 160 - 167 */
287     0, 0, 0, 0, 0, 0, 0, 0,
288     0, 0, 0, 0, 0, 0, 0, 0,
289     0, 0, 0, 0, 0, 0, 0, 0,
290     IN_DESC, IN_DESC, IN_DESC, 0,                       /* ADDPI */
291     IN_DESC, IN_DESC, IN_DESC, 0,                       /* SUBPI */
292     IN_DESC, IN_DESC, 0, 0,                             /* CMPPI */
293     IN_DESC, IN_ARG, 0, 0,                              /* CVTPLI */
294     IN_DESC, IN_DESC, IN_DESC, 0,                       /* MULPI */
295     IN_DESC, IN_DESC, IN_DESC, 0,                       /* DIVPI */
296     IN_DESC, IN_DESC, IN_ARG, 0,                        /* ASHPI */
297     IN_DESC, IN_DESC, 0, 0                              /* CVTLPI */
298     };
299 
300 /* ASCII to overpunch table: sign is <7>, digit is <4:0> */
301 
302 static int32 overbin[128] = {
303     0, 0, 0, 0, 0, 0, 0, 0,                             /* 000 - 037 */
304     0, 0, 0, 0, 0, 0, 0, 0,
305     0, 0, 0, 0, 0, 0, 0, 0,
306     0, 0, 0, 0, 0, 0, 0, 0,
307     0, 0x80, 0, 0, 0, 0, 0, 0,                          /* 040 - 077 */
308     0, 0, 0, 0, 0, 0, 0, 0,
309     0, 1, 2, 3, 4, 5, 6, 7,
310     8, 9, 0x80, 0, 0, 0, 0, 0,
311     0, 1, 2, 3, 4, 5, 6, 7,                             /* 100 - 137 */
312     8, 9, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86,
313     0x87, 0x88, 0x89, 0, 0, 0, 0, 0,
314     0, 0, 0, 0, 0, 0x80, 0, 0,
315     0, 0, 0, 0, 0, 0, 0, 0,                             /* 140 - 177 */
316     0, 0, 0, 0, 0, 0, 0, 0,
317     0, 0, 0, 0, 0, 0, 0, 0,
318     0, 0, 0, 0, 0, 0x80, 0, 0
319     };
320 
321 /* Overpunch to ASCII table: indexed by sign and digit */
322 
323 static int32 binover[2][16] = {
324     '{', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
325     '0', '0', '0', '0', '0', '0',
326     '}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
327     '0', '0', '0', '0', '0', '0'
328     };
329 
330 static unsigned char movbuf[65536];
331 
332 /* CIS emulator */
333 
cis11(int32 IR)334 t_stat cis11 (int32 IR)
335 {
336 int32 c, i, j, t, op, rn, addr;
337 int32 match, limit, mvlnt, shift;
338 int32 spc, ldivd, ldivr;
339 int32 arg[6];                                           /* operands */
340 int32 old_PC;
341 uint32 nc, digit, result;
342 t_stat st;
343 static DSTR accum, src1, src2, dst;
344 static DSTR mptable[10];
345 static DSTR Dstr1 = { 0, 0x10, 0, 0, 0 };
346 
347 old_PC = (PC - 2) & 0177777;                            /* original PC */
348 op = IR & 0177;                                         /* IR <6:0> */
349 for (i = j = 0; (i < MAXOPN) && opntab[op][i]; i++) {   /* parse operands */
350     switch (opntab[op][i]) {                            /* case on op type */
351 
352         case R0_DESC:
353             arg[j++] = R[0];
354             arg[j++] = R[1];
355             break;
356 
357         case R2_DESC:
358             arg[j++] = R[2];
359             arg[j++] = R[3];
360             break;
361 
362         case R4_DESC:
363             arg[j++] = R[4];
364             arg[j++] = R[5];
365             break;
366 
367         case R4_ARG:
368             arg[j++] = R[4];
369             break;
370 
371         case IN_DESC:
372             addr = ReadW (PC | isenable);
373             PC = (PC + 2) & 0177777;
374             arg[j++] = ReadW (addr | dsenable);
375             arg[j++] = ReadW (((addr + 2) & 0177777) | dsenable);
376             break;
377 
378         case IN_ARG:
379             arg[j++] = ReadW (PC | isenable);
380             PC = (PC + 2) & 0177777;
381             break;
382 
383         default:
384             return SCPE_IERR;
385             }                                           /* end case */
386     }                                                   /* end for */
387 switch (op) {                                           /* case on opcode */
388 
389 /* MOVC, MOVTC, MOVCI, MOVTCI
390 
391    Operands (MOVC, MOVTC):
392         R0, R1          =       source string descriptor
393         R2, R3          =       dest string descriptor
394         R4<7:0>         =       fill character
395         R5              =       translation table address (MOVTC only)
396    Operands (MOVCI, MOVTCI):
397         A1LNT, A1ADR    =       source string descriptor
398         A2LNT, A2ADR    =       dest string descriptor
399         A3LNT<7:0>      =       fill character
400         A3ADR           =       translation table address (MOVTCI only)
401 
402    Condition codes:
403         NZVC            =       set from src.lnt - dst.lnt
404 
405    Registers (MOVC, MOVTC only)
406         R0              =       max (0, src.len - dst.len)
407         R1:R3           =       0
408         R4:R5           =       unchanged
409 
410         Notes:
411         - If either the source or destination lengths are zero,
412           the move loops exit immediately.
413         - If the source length does not exceed the destination
414           length, the fill loop exits immediately.
415 */
416 
417     case 030: case 032: case 0130: case 0132:
418         if (!fpd) {                                     /* first time? */
419             mvlnt = movx_setup (op, arg);               /* set up reg */
420             if (R[1] < R[3]) {                          /* move backwards? */
421                 R[1] = (R[1] + mvlnt) & 0177777;        /* bias addresses */
422                 R[3] = (R[3] + mvlnt) & 0177777;
423                 }
424             }
425 
426 /* At this point,
427 
428    R0-R5    =   arguments
429    M[SP]    =   move length */
430 
431         if (R[0] && R[2]) {                             /* move to do? */
432             if (R[1] < R[3]) {                          /* backwards? */
433                 for (i = 0; R[0] && R[2]; ) {           /* move loop */
434                     t = ReadB (((R[1] - 1) & 0177777) | dsenable);
435                     if (op & 2)
436                         t = ReadB (((R[5] + t) & 0177777) | dsenable);
437                     WriteB (t, ((R[3] - 1) & 0177777) | dsenable);
438                     R[0]--;
439                     R[1] = (R[1] - 1) & 0177777;
440                     R[2]--;
441                     R[3] = (R[3] - 1) & 0177777;
442                     if ((++i >= INT_TEST) && R[0] && R[2]) {
443                         if (cis_int_test (i, old_PC, &st))
444                             return st;
445                         i = 0;
446                         }
447                     }                                   /* end for lnts */
448                 mvlnt = ReadW (SP | dsenable);          /* recover mvlnt */
449                 R[3] = (R[3] + mvlnt) & 0177777;        /* end of dst str */
450                 }                                       /* end if bkwd */
451             else {                                      /* forward */
452                 for (i = 0; R[0] && R[2]; ) {           /* move loop */
453                     t = ReadB ((R[1] & 0177777) | dsenable);
454                     if (op & 2)
455                         t = ReadB (((R[5] + t) & 0177777) | dsenable);
456                     WriteB (t, (R[3] & 0177777) | dsenable);
457                     R[0]--;
458                     R[1] = (R[1] + 1) & 0177777;
459                     R[2]--;
460                     R[3] = (R[3] + 1) & 0177777;
461                     if ((++i >= INT_TEST) && R[0] && R[2]) {
462                         if (cis_int_test (i, old_PC, &st))
463                             return st;
464                         i = 0;
465                         }
466                     }                                   /* end for lnts */
467                 }                                       /* end else fwd */
468             }                                           /* end if move */
469         for (i = 0; i < R[2]; i++) {
470             WriteB (R[4], ((R[3] + i) & 0177777) | dsenable);
471             }
472         movx_cleanup (op);                              /* cleanup */
473         return SCPE_OK;
474 
475 /* MOVRC, MOVRCI
476 
477    Operands (MOVC, MOVTC):
478         R0, R1          =       source string descriptor
479         R2, R3          =       dest string descriptor
480         R4<7:0>         =       fill character
481    Operands (MOVCI, MOVTCI):
482         A1LNT, A1ADR    =       source string descriptor
483         A2LNT, A2ADR    =       dest string descriptor
484         A3LNT<7:0>      =       fill character
485 
486    Condition codes:
487         NZVC            =       set from src.lnt - dst.lnt
488 
489    Registers (MOVRC only)
490         R0              =       max (0, src.len - dst.len)
491         R1:R3           =       0
492         R4:R5           =       unchanged
493 
494    Notes: see MOVC, MOVCI
495 */
496 
497     case 031: case 0131:
498         if (!fpd) {                                     /* first time? */
499             mvlnt = movx_setup (op, arg);               /* set up reg */
500             R[1] = (R[1] + R[0] - mvlnt) & 0177777;     /* eff move start */
501             R[3] = (R[3] + R[2] - mvlnt) & 0177777;
502             if (R[1] < R[3]) {                          /* move backwards? */
503                 R[1] = (R[1] + mvlnt) & 0177777;        /* bias addresses */
504                 R[3] = (R[3] + mvlnt) & 0177777;
505                 }
506             }
507 
508 /* At this point,
509 
510    R0-R5    =   arguments
511    M[SP]    =   move length */
512 
513         if (R[0] && R[2]) {                             /* move to do? */
514             if (R[1] < R[3]) {                          /* backwards? */
515                 for (i = 0; R[0] && R[2]; ) {           /* move loop */
516                     t = ReadB (((R[1] - 1) & 0177777) | dsenable);
517                     WriteB (t, ((R[3] - 1) & 0177777) | dsenable);
518                     R[0]--;
519                     R[1] = (R[1] - 1) & 0177777;
520                     R[2]--;
521                     R[3] = (R[3] - 1) & 0177777;
522                     if ((++i >= INT_TEST) && R[0] && R[2]) {
523                         if (cis_int_test (i, old_PC, &st))
524                             return st;
525                         i = 0;
526                         }
527                     }                                   /* end for lnts */
528                 }                                       /* end if bkwd */
529             else {                                      /* forward */
530                 for (i = 0; R[0] && R[2]; ) {           /* move loop */
531                     t = ReadB ((R[1] & 0177777) | dsenable);
532                     WriteB (t, (R[3] & 0177777) | dsenable);
533                     R[0]--;
534                     R[1] = (R[1] + 1) & 0177777;
535                     R[2]--;
536                     R[3] = (R[3] + 1) & 0177777;
537                     if ((++i >= INT_TEST) && R[0] && R[2]) {
538                         if (cis_int_test (i, old_PC, &st))
539                             return st;
540                         i = 0;
541                         }
542                     }                                   /* end for lnts */
543                 mvlnt = ReadW (SP | dsenable);          /* recover mvlnt */
544                 R[3] = (R[3] - mvlnt) & 0177777;        /* start of dst str */
545                 }                                       /* end else fwd */
546             }                                           /* end if move */
547         for (i = 0; i < R[2]; i++) {
548             WriteB (R[4], ((R[3] - R[2] + i) & 0177777) | dsenable);
549             }
550         movx_cleanup (op);                              /* cleanup */
551         return SCPE_OK;
552 
553 /* Load descriptors - no operands */
554 
555     case 020: case 021: case 022: case 023:
556     case 024: case 025: case 026: case 027:
557     case 060: case 061: case 062: case 063:
558     case 064: case 065: case 066: case 067:
559         limit = (op & 040)? 6: 4;
560         rn = IR & 07;                                   /* get register */
561         t = R[rn];
562         spc = (rn == 7)? isenable: dsenable;
563         for (j = 0; j < limit; j = j + 2) {             /* loop for 2,3 dscr */
564             addr = ReadW (((t + j) & 0177777) | spc);
565             R[j] = ReadW (addr | dsenable);
566             R[j + 1] = ReadW (((addr + 2) & 0177777) | dsenable);
567             }
568         if (rn >= limit)
569             R[rn] = (R[rn] + limit) & 0177777;
570         return SCPE_OK;
571 
572 /* LOCC, SKPC, LOCCI, SKPCI
573 
574    Operands (LOCC, SKPC):
575         R0, R1          =       source string descriptor
576         R4<7:0>         =       match character
577    Operands (LOCCI, SKPCI):
578         A1LNT, A1ADR    =       source string descriptor
579         A2LNT<7:0>      =       match character
580 
581    Condition codes:
582         NZ              =       set from R0
583         VC              =       0
584 
585    Registers:
586         R0:R1           =       substring descriptor where operation terminated
587 */
588 
589     case 0140: case 0141:                               /* inline */
590         if (!fpd) {                                     /* FPD clear? */
591             WriteW (R[4], ((SP - 2) & 0177777) | dsenable);
592             SP = (SP - 2) & 0177777;                    /* push R4 */
593             R[0] = A1LNT;                               /* args to registers */
594             R[1] = A1ADR;
595             R[4] = A2LNT;
596             }                                           /* fall through */
597     case 040: case 041:                                 /* register */
598         fpd = 1;                                        /* set FPD */
599         R[4] = R[4] & 0377;                             /* match character */
600         for (i = 0; R[0] != 0;) {                       /* loop */
601             c = ReadB (R[1] | dsenable);                /* get char */
602             if ((c == R[4]) ^ (op & 1))                 /* = + LOC, != + SKP? */
603                 break;
604             R[0]--;                                     /* decr count, */
605             R[1] = (R[1] + 1) & 0177777;                /* incr addr */
606             if ((++i >= INT_TEST) && R[0]) {            /* test for intr? */
607                 if (cis_int_test (i, old_PC, &st))
608                     return st;
609                 i = 0;
610                 }
611             }
612         N = GET_SIGN_W (R[0]);
613         Z = GET_Z (R[0]);
614         V = C = 0;
615         fpd = 0;                                        /* instr done */
616         if (op & INLINE) {                              /* inline? */
617             R[4] = ReadW (SP | dsenable);               /* restore R4 */
618             SP = (SP + 2) & 0177777;
619             }
620         return SCPE_OK;
621 
622 /* SCANC, SPANC, SCANCI, SPANCI
623 
624    Operands (SCANC, SPANC):
625         R0, R1          =       source string descriptor
626         R4<7:0>         =       mask
627         R5              =       table address
628    Operands (SCANCI, SPANCI):
629         A1LNT, A1ADR    =       source string descriptor
630         A2LNT<7:0>      =       match character
631         A2ADR           =       table address
632 
633    Condition codes:
634         NZ              =       set from R0
635         VC              =       0
636 
637    Registers:
638         R0:R1           =       substring descriptor where operation terminated
639 */
640 
641     case 0142: case 0143:                               /* inline */
642         if (!fpd) {                                     /* FPD clear? */
643             WriteW (R[4], ((SP - 4) & 0177777) | dsenable);
644             WriteW (R[5], ((SP - 2) & 0177777) | dsenable);
645             SP = (SP - 4) & 0177777;                    /* push R4, R5 */
646             R[0] = A1LNT;                               /* args to registers */
647             R[1] = A1ADR;
648             R[4] = A2LNT;
649             R[5] = A2ADR;
650             }                                           /* fall through */
651     case 042: case 043:                                 /* register */
652         fpd = 1;                                        /* set FPD */
653         R[4] = R[4] & 0377;                             /* match character */
654         for (i = 0; R[0] != 0;) {                       /* loop */
655             t = ReadB (R[1] | dsenable);                /* get char as index */
656             c = ReadB (((R[5] + t) & 0177777) | dsenable);
657             if (((c & R[4]) != 0) ^ (op & 1))           /* != + SCN, = + SPN? */
658                 break;
659             R[0]--;                                     /* decr count, */
660             R[1] = (R[1] + 1) & 0177777;                /* incr addr */
661             if ((++i >= INT_TEST) && R[0]) {            /* test for intr? */
662                 if (cis_int_test (i, old_PC, &st))
663                     return st;
664                 i = 0;
665                 }
666             }
667         N = GET_SIGN_W (R[0]);
668         Z = GET_Z (R[0]);
669         V = C = 0;
670         fpd = 0;                                        /* instr done */
671         if (op & INLINE) {                              /* inline? */
672             R[4] = ReadW (SP | dsenable);               /* restore R4, R5 */
673             R[5] = ReadW (((SP + 2) & 0177777) | dsenable);
674             SP = (SP + 4) & 0177777;
675             }
676         return SCPE_OK;
677 
678 /* CMPC, CMPCI
679 
680    Operands (CMPC):
681         R0, R1          =       source1 string descriptor
682         R2, R3          =       source2 string descriptor
683         R4<7:0>         =       fill character
684    Operands (CMPCI):
685         A1LNT, A1ADR    =       source1 string descriptor
686         A2LNT, A2ADR    =       source2 string descriptor
687         A3LNT<7:0>      =       fill character
688 
689    Condition codes:
690         NZVC            =       set from src1 - src2 at mismatch, or
691                         =       0100 if equal
692 
693    Registers (CMPC only):
694         R0:R1           =       unmatched source1 substring descriptor
695         R2:R3           =       unmatched source2 substring descriptor
696 */
697 
698    case 0144:                                           /* inline */
699         if (!fpd) {                                     /* FPD clear? */
700             WriteW (R[0], ((SP - 10) & 0177777) | dsenable);
701             WriteW (R[1], ((SP - 8) & 0177777) | dsenable);
702             WriteW (R[2], ((SP - 6) & 0177777) | dsenable);
703             WriteW (R[3], ((SP - 4) & 0177777) | dsenable);
704             WriteW (R[4], ((SP - 2) & 0177777) | dsenable);
705             SP = (SP - 10) & 0177777;                   /* push R0 - R4 */
706             R[0] = A1LNT;                               /* args to registers */
707             R[1] = A1ADR;
708             R[2] = A2LNT;
709             R[3] = A2ADR;
710             R[4] = A3LNT;
711             }                                           /* fall through */
712    case 044:                                            /* register */
713         fpd = 1;                                        /* set FPD */
714         R[4] = R[4] & 0377;                             /* mask fill */
715         c = t = 0;
716         for (i = 0; (R[0] || R[2]); ) {                 /* until cnts == 0 */
717             if (R[0])                                   /* get src1 or fill */
718                 c = ReadB (R[1] | dsenable);
719             else c = R[4];
720             if (R[2])                                   /* get src2 or fill */
721                 t = ReadB (R[3] | dsenable);
722             else t = R[4];
723             if (c != t)                                 /* if diff, done */
724                 break;
725             if (R[0]) {                                 /* if more src1 */
726                 R[0]--;                                 /* decr count, */
727                 R[1] = (R[1] + 1) & 0177777;            /* incr addr */
728                 }
729             if (R[2]) {                                 /* if more src2 */
730                 R[2]--;                                 /* decr count, */
731                 R[3] = (R[3] + 1) & 0177777;            /* incr addr */
732                 }
733             if ((++i >= INT_TEST) && (R[0] || R[2])) {  /* test for intr? */
734                 if (cis_int_test (i, old_PC, &st))
735                     return st;
736                 i = 0;
737                 }
738             }
739         j = c - t;                                      /* last chars read */
740         N = GET_SIGN_B (j);                             /* set cc's */
741         Z = GET_Z (j);
742         V = GET_SIGN_B ((c ^ t) & (~t ^ j));
743         C = (c < t);
744         fpd = 0;                                        /* instr done */
745         if (op & INLINE) {                              /* inline? */
746             R[0] = ReadW (SP | dsenable);               /* restore R0 - R4 */
747             R[1] = ReadW (((SP + 2) & 0177777) | dsenable);
748             R[2] = ReadW (((SP + 4) & 0177777) | dsenable);
749             R[3] = ReadW (((SP + 6) & 0177777) | dsenable);
750             R[4] = ReadW (((SP + 8) & 0177777) | dsenable);
751             SP = (SP + 10) & 0177777;
752             }
753         return SCPE_OK;
754 
755 /* MATC, MATCI
756 
757    Operands (MATC):
758         R0, R1          =       source string descriptor
759         R2, R3          =       substring descriptor
760    Operands (MATCI):
761         A1LNT, A1ADR    =       source1 string descriptor
762         A2LNT, A2ADR    =       source2 string descriptor
763 
764    Condition codes:
765         NZ              =       set from R0
766         VC              =       0
767 
768    Registers:
769         R0:R1           =       source substring descriptor for match
770 
771    Notes:
772         - If the string is zero length, and the substring is not,
773           the outer loop exits immediately, and the result is
774           "no match"
775         - If the substring is zero length, the inner loop always
776           exits immediately, and the result is a "match"
777         - If the string is zero length, and the substring is as
778           well, the outer loop executes, the inner loop exits
779           immediately, and the result is a match, but the result
780           is the length of the string (zero), or "no match"
781 */
782 
783     case 0145:                                          /* inline */
784         if (!fpd) {                                     /* FPD clear? */
785             WriteW (R[2], ((SP - 4) & 0177777) | dsenable);
786             WriteW (R[3], ((SP - 2) & 0177777) | dsenable);
787             SP = (SP - 4) & 0177777;                    /* push R2, R3 */
788             R[0] = A1LNT;                               /* args to registers */
789             R[1] = A1ADR;
790             R[2] = A2LNT;
791             R[3] = A2ADR;
792             }                                           /* fall through */
793     case 0045:                                          /* register */
794         fpd = 1;
795         for (match = 0; R[0] >= R[2]; ) {               /* loop thru string */
796             for (i = 0, match = 1; match && (i < R[2]); i++) {
797                 c = ReadB (((R[1] + i) & 0177777) | dsenable);
798                 t = ReadB (((R[3] + i) & 0177777) | dsenable);
799                 match = (c == t);                       /* end for substring */
800                 }
801             if (match)                                  /* exit if match */
802                 break;
803             R[0]--;                                     /* on to next char */
804             R[1] = (R[1] + 1) & 0177777;
805             if (cis_int_test (i, old_PC, &st))
806                 return st;
807             }
808         if (!match) {                                   /* if no match */
809             R[1] = (R[1] + R[0]) & 0177777;
810             R[0] = 0;
811             }
812         N = GET_SIGN_W (R[0]);
813         Z = GET_Z (R[0]);
814         V = C = 0;
815         fpd = 0;                                        /* instr done */
816         if (op & INLINE) {                              /* inline? */
817             R[2] = ReadW (SP | dsenable);               /* restore R2, R3 */
818             R[3] = ReadW (((SP + 2) & 0177777) | dsenable);
819             SP = (SP + 4) & 0177777;
820             }
821         return SCPE_OK;
822 
823 /* ADDN, SUBN, ADDP, SUBP, ADDNI, SUBNI, ADDPI, SUBPI
824 
825    Operands:
826         A1LNT, A1ADR    =       source1 string descriptor
827         A2LNT, A2ADR    =       source2 string descriptor
828         A3LNT, A3ADR    =       destination string descriptor
829 
830    Condition codes:
831         NZV             =       set from result
832         C               =       0
833 
834    Registers (ADDN, ADDP, SUBN, SUBP only):
835         R0:R3           =       0
836 */
837 
838     case 050: case 051: case 070: case 071:
839     case 0150: case 0151: case 0170: case 0171:
840         ReadDstr (A1, &src1, op);                       /* get source1 */
841         ReadDstr (A2, &src2, op);                       /* get source2 */
842         if (op & 1)                                     /* sub? invert sign */
843             src1.sign = src1.sign ^ 1;
844         if (src1.sign ^ src2.sign) {                    /* opp signs?  sub */
845             if (CmpDstr (&src1, &src2) < 0) {           /* src1 < src2? */
846                 SubDstr (&src1, &src2, &dst);           /* src2 - src1 */
847                 dst.sign = src2.sign;                   /* sign = src2 */
848                 }
849             else {
850                 SubDstr (&src2, &src1, &dst);           /* src1 - src2 */
851                 dst.sign = src1.sign;                   /* sign = src1 */
852                 }
853             V = 0;                                      /* can't carry */
854             }
855         else {                                          /* addition */
856             V = AddDstr (&src1, &src2, &dst, 0);        /* add magnitudes */
857             dst.sign = src1.sign;                       /* set result sign */
858             }
859         C = 0;
860         WriteDstr (A3, &dst, op);                       /* store result */
861         if ((op & INLINE) == 0)                         /* if reg, clr reg */
862             R[0] = R[1] = R[2] = R[3] = 0;
863         return SCPE_OK;
864 
865 /* MULP, MULPI
866 
867    Operands:
868         A1LNT, A1ADR    =       source1 string descriptor
869         A2LNT, A2ADR    =       source2 string descriptor
870         A3LNT, A3ADR    =       destination string descriptor
871 
872    Condition codes:
873         NZV             =       set from result
874         C               =       0
875 
876    Registers (MULP only):
877         R0:R3           =       0
878 */
879 
880     case 074: case 0174:
881         dst = Dstr0;                                    /* clear result */
882         if (ReadDstr (A1, &src1, op) && ReadDstr (A2, &src2, op)) {
883             dst.sign = src1.sign ^ src2.sign;           /* sign of result */
884             accum = Dstr0;                              /* clear accum */
885             NibbleRshift (&src1, 1, 0);                 /* shift out sign */
886             CreateTable (&src1, mptable);               /* create *1, *2, ... */
887             for (i = 1; i < (DSTRLNT * 8); i++) {       /* 31 iterations */
888                 digit = (src2.val[i / 8] >> ((i % 8) * 4)) & 0xF;
889                 if (digit > 0)                          /* add in digit*mpcnd */
890                     AddDstr (&mptable[digit], &accum, &accum, 0);
891                 nc = NibbleRshift (&accum, 1, 0);       /* ac right 4 */
892                 NibbleRshift (&dst, 1, nc);             /* result right 4 */
893                 }
894             V = TestDstr (&accum) != 0;                 /* if ovflo, set V */
895             }
896         else V = 0;                                     /* result = 0 */
897         C = 0;                                          /* C = 0 */
898         WriteDstr (A3, &dst, op);                       /* store result */
899         if ((op & INLINE) == 0)                         /* if reg, clr reg */
900                 R[0] = R[1] = R[2] = R[3] = 0;
901         return SCPE_OK;
902 
903 /* DIVP, DIVPI
904 
905    Operands:
906         A1LNT, A1ADR    =       divisor string descriptor
907         A2LNT, A2ADR    =       dividend string descriptor
908         A3LNT, A3ADR    =       destination string descriptor
909 
910    Condition codes:
911         NZV             =       set from result
912         C               =       set if divide by zero
913 
914    Registers (DIVP only):
915         R0:R3           =       0
916 */
917 
918     case 075: case 0175:
919         ldivr = ReadDstr (A1, &src1, op);               /* get divisor */
920         if (ldivr == 0) {                               /* divisor = 0? */
921             V = C = 1;                                  /* set cc's */
922             return SCPE_OK;
923             }
924         ldivr = LntDstr (&src1, ldivr);                 /* get exact length */
925         ldivd = ReadDstr (A2, &src2, op);               /* get dividend */
926         ldivd = LntDstr (&src2, ldivd);                 /* get exact length */
927         dst = Dstr0;                                    /* clear dest */
928         NibbleRshift (&src1, 1, 0);                     /* right justify ops */
929         NibbleRshift (&src2, 1, 0);
930         if ((t = ldivd - ldivr) >= 0) {                 /* any divide to do? */
931             WordLshift (&src1, t / 8);                  /* align divr to divd */
932             NibbleLshift (&src1, t % 8);
933             CreateTable (&src1, mptable);               /* create *1, *2, ... */
934             for (i = 0; i <= t; i++) {                  /* divide loop */
935                 for (digit = 9; digit > 0; digit--) {   /* find digit */
936                     if (CmpDstr (&src2, &mptable[digit]) >= 0) {
937                         SubDstr (&mptable[digit], &src2, &src2);
938                         dst.val[0] = dst.val[0] | digit;
939                         break;
940                         }                               /* end if */
941                     }                                   /* end for */
942                 NibbleLshift (&src2, 1);                /* shift dividend */
943                 NibbleLshift (&dst, 1);                 /* shift quotient */
944                 }                                       /* end divide loop */
945             dst.sign = src1.sign ^ src2.sign;           /* calculate sign */
946             }                                           /* end if */
947         V = C = 0;
948         WriteDstr (A3, &dst, op);                       /* store result */
949         if ((op & INLINE) == 0)                         /* if reg, clr reg */
950             R[0] = R[1] = R[2] = R[3] = 0;
951         return SCPE_OK;
952 
953 /* CMPN, CMPP, CMPNI, CMPPI
954 
955    Operands:
956         A1LNT, A1ADR    =       source1 string descriptor
957         A2LNT, A2ADR    =       source2 string descriptor
958 
959    Condition codes:
960         NZ              =       set from comparison
961         VC              =       0
962 
963    Registers (CMPN, CMPP only):
964         R0:R3           =       0
965 */
966 
967     case 052: case 072: case 0152: case 0172:
968         ReadDstr (A1, &src1, op);                       /* get source1 */
969         ReadDstr (A2, &src2, op);                       /* get source2 */
970         N = Z = V = C = 0;
971         if (src1.sign != src2.sign) N = src1.sign;
972         else {
973             t = CmpDstr (&src1, &src2);                 /* compare strings */
974             if (t < 0)
975                 N = (src1.sign? 0: 1);
976             else if (t > 0)
977                 N = (src1.sign? 1: 0);
978             else Z = 1;
979             }
980         if ((op & INLINE) == 0)                         /* if reg, clr reg */
981             R[0] = R[1] = R[2] = R[3] = 0;
982         return SCPE_OK;
983 
984 /* ASHN, ASHP, ASHNI, ASHPI
985 
986    Operands:
987         A1LNT, A1ADR    =       source string descriptor
988         A2LNT, A2ADR    =       destination string descriptor
989         A3LNT<11:8>     =       rounding digit
990         A3LNT<7:0>      =       shift count
991 
992    Condition codes:
993         NZV             =       set from result
994         C               =       0
995 
996    Registers (ASHN, ASHP only):
997         R0:R1, R4       =       0
998 */
999 
1000     case 056: case 076: case 0156: case 0176:
1001         ReadDstr (A1, &src1, op);                       /* get source */
1002         V = C = 0;                                      /* init cc's */
1003         shift = GET_ASHLNT (A3LNT);                     /* get shift count */
1004         if (shift & ASHSGN) {                           /* right shift? */
1005             shift = (ASHLNT_M + 1 - shift);             /* !shift! */
1006             WordRshift (&src1, shift / 8);              /* do word shifts */
1007             NibbleRshift (&src1, shift % 8, 0);         /* do nibble shifts */
1008             t = GET_ASHRND (A3LNT);                     /* get rounding digit */
1009             if ((t + (src1.val[0] & 0xF)) > 9)          /* rounding needed? */
1010                 AddDstr (&src1, &Dstr1, &src1, 0);      /* round */
1011             src1.val[0] = src1.val[0] & ~0xF;           /* clear sign */
1012             }                                           /* end right shift */
1013         else if (shift) {                               /* left shift? */
1014             if (WordLshift (&src1, shift / 8))          /* do word shifts */
1015                 V = 1;
1016             if (NibbleLshift (&src1, shift % 8))        /* do nibble shifts */
1017                 V = 1;
1018             }                                           /* end left shift */
1019         WriteDstr (A2, &src1, op);                      /* store result */
1020         if ((op & INLINE) == 0)                         /* if reg, clr reg */
1021             R[0] = R[1] = R[4] = 0;
1022         return SCPE_OK;
1023 
1024 /* CVTPN, CVTPNI
1025 
1026    Operands:
1027         A1LNT, A1ADR    =       source string descriptor
1028         A2LNT, A2ADR    =       destination string descriptor
1029 
1030    Condition codes:
1031         NZV             =       set from result
1032         C               =       0
1033 
1034    Registers (CVTPN only):
1035         R0:R1           =       0
1036 */
1037 
1038     case 054: case 0154:
1039         ReadDstr (A1, &src1, PACKED);                   /* get source */
1040         V = C = 0;                                      /* init cc's */
1041         WriteDstr (A2, &src1, NUMERIC);                 /* write dest */
1042         if ((op & INLINE) == 0)                         /* if reg, clr reg */
1043             R[0] = R[1] = 0;
1044         return SCPE_OK;
1045 
1046 /* CVTNP, CVTNPI
1047 
1048    Operands:
1049         A1LNT, A1ADR    =       source string descriptor
1050         A2LNT, A2ADR    =       destination string descriptor
1051 
1052    Condition codes:
1053         NZV             =       set from result
1054         C               =       0
1055 
1056    Registers (CVTNP only):
1057         R0:R1           =       0
1058 */
1059 
1060     case 055: case 0155:
1061         ReadDstr (A1, &src1, NUMERIC);                  /* get source */
1062         V = C = 0;                                      /* init cc's */
1063         WriteDstr (A2, &src1, PACKED);                  /* write dest */
1064         if ((op & INLINE) == 0)                         /* if reg, clr reg */
1065             R[0] = R[1] = 0;
1066         return SCPE_OK;
1067 
1068 /* CVTNL, CVTPL, CVTNLI, CVTPLI
1069 
1070    Operands:
1071         A1LNT, A1ADR    =       source string descriptor
1072         A2LNT           =       destination address (inline only)
1073 
1074    Condition codes:
1075         NZV             =       set from result
1076         C               =       source < 0 and result != 0
1077 
1078    Registers (CVTNL, CVTPL only):
1079         R0:R1           =       0
1080         R2:R3           =       result
1081 */
1082 
1083     case 053: case 073: case 0153: case 0173:
1084         ReadDstr (A1, &src1, op);                       /* get source */
1085         V = result = 0;                                 /* clear V, result */
1086         for (i = (DSTRLNT * 8) - 1; i > 0; i--) {       /* loop thru digits */
1087             digit = (src1.val[i / 8] >> ((i % 8) * 4)) & 0xF;
1088             if (digit || result || V) {                 /* skip initial 0's */
1089                 if (result >= MAXDVAL)
1090                     V = 1;
1091                 result = (result * 10) + digit;
1092                 if (result < digit)
1093                     V = 1;
1094                 }                                       /* end if */
1095             }                                           /* end for */
1096         if (src1.sign)
1097             result = (~result + 1) & 0xFFFFFFFF;
1098         N = GET_SIGN_L (result);
1099         Z = GET_Z (result);
1100         V = V | (N ^ src1.sign);                        /* overflow if +2**31 */
1101         C = src1.sign && (Z == 0);                      /* set C based on std */
1102         if (op & INLINE) {                              /* inline? */
1103             WriteW (result & 0177777, A2LNT | dsenable);
1104             WriteW ((result >> 16) & 0177777,
1105                 ((A2LNT + 2) & 0177777) | dsenable);
1106             }
1107         else {
1108             R[0] = R[1] = 0;
1109             R[2] = (result >> 16) & 0177777;
1110             R[3] = result & 0177777;
1111             }
1112         return SCPE_OK;
1113 
1114 /* CVTLN, CVTLP, CVTLNI, CVTLPI
1115 
1116    Operands:
1117         A1LNT, A1ADR    =       destination string descriptor
1118         A2LNT, A2ADR    =       source long (CVTLNI, CVTLPI) - VAX format
1119         R2:R3           =       source long (CVTLN, CVTLP) - EIS format
1120 
1121    Condition codes:
1122         NZV             =       set from result
1123         C               =       0
1124 
1125    Registers (CVTLN, CVTLP only)
1126         R2:R3           =       0
1127 */
1128 
1129     case 057: case 077:
1130         result = (R[2] << 16) | R[3];                   /* op in EIS format */
1131         R[2] = R[3] = 0;                                /* clear registers */
1132         goto CVTLx;                                     /* join common code */
1133     case 0157: case 0177:
1134         result = (A2ADR << 16) | A2LNT;                 /* op in VAX format */
1135     CVTLx:
1136         dst = Dstr0;                                    /* clear result */
1137         if ((dst.sign = GET_SIGN_L (result)))
1138             result = (~result + 1) & 0xFFFFFFFF;
1139         for (i = 1; (i < (DSTRLNT * 8)) && result; i++) {
1140             digit = result % 10;
1141             result = result / 10;
1142             dst.val[i / 8] = dst.val[i / 8] | (digit << ((i % 8) * 4));
1143             }
1144         V = C = 0;
1145         WriteDstr (A1, &dst, op);                       /* write result */
1146         return SCPE_OK;
1147 
1148     default:
1149         setTRAP (TRAP_ILL);
1150         break;
1151         }                                               /* end case */
1152 return SCPE_OK;
1153 }                                                       /* end cis */
1154 
1155 /* Get decimal string
1156 
1157    Arguments:
1158         dscr    =       decimal string descriptor
1159         src     =       decimal string structure
1160         flag    =       numeric/packed flag
1161 
1162    The routine returns the length in int32's of the non-zero part of
1163    the string.
1164 
1165    This routine plays fast and loose with operand checking, as did the
1166    original 11/23 microcode (half of which I wrote).  In particular,
1167 
1168    - If the flag specifies packed, the type is not checked at all.
1169      The sign of an unsigned string is assumed to be 0xF (an
1170      alternative for +).
1171    - If the flag specifies numeric, packed types will be treated
1172      as unsigned zoned.
1173    - For separate, only the '-' sign is checked, not the '+'.
1174 
1175    However, to simplify the code elsewhere, digits are range checked,
1176    and bad digits are replaced with 0's.
1177 */
1178 
ReadDstr(int32 * dscr,DSTR * src,int32 flag)1179 int32 ReadDstr (int32 *dscr, DSTR *src, int32 flag)
1180 {
1181 int32 c, i, end, lnt, type, t;
1182 
1183 *src = Dstr0;                                           /* clear result */
1184 type = GET_DTYP (dscr[0]);                              /* get type */
1185 lnt = GET_DLNT (dscr[0]);                               /* get string length */
1186 if (flag & PACKED) {                                    /* packed? */
1187     end = lnt / 2;                                      /* last byte */
1188     for (i = 0; i <= end; i++) {                        /* loop thru string */
1189         c = ReadB (((dscr[1] + end - i) & 0177777) | dsenable);
1190         if (i == 0)                                     /* save sign */
1191             t = c & 0xF;
1192         if ((i == end) && ((lnt & 1) == 0))
1193             c = c & 0xF;
1194         if (c >= 0xA0)                                  /* check hi digit */
1195             c = c & 0xF;
1196         if ((c & 0xF) >= 0xA)                           /* check lo digit */
1197             c = c & 0xF0;
1198         src->val[i / 4] = src->val[i / 4] | (c << ((i % 4) * 8));
1199         }                                               /* end for */
1200     if ((t == 0xB) || (t == 0xD))                       /* if -, set sign */
1201         src->sign = 1;
1202     src->val[0] = src->val[0] & ~0xF;                   /* clear sign */
1203     }                                                   /* end packed */
1204 else {                                                  /* numeric */
1205     if (type >= TS) src->sign = (ReadB ((((type == TS)?
1206          dscr[1] + lnt: dscr[1] - 1) & 0177777) | dsenable) == '-');
1207     for (i = 1; i <= lnt; i++) {                        /* loop thru string */
1208         c = ReadB (((dscr[1] + lnt - i) & 0177777) | dsenable);
1209         if ((i == 1) && (type == XZ) && ((c & 0xF0) == 0x70))
1210             src->sign = 1;                              /* signed zoned */
1211         else if (((i == 1) && (type == TO)) ||
1212             ((i == lnt) && (type == LO))) {
1213             c = overbin[c & 0177];                      /* get sign and digit */
1214             src->sign = c >> 7;                         /* set sign */
1215             }
1216         c = c & 0xF;                                    /* get digit */
1217         if (c > 9)                                      /* range check */
1218             c = 0;
1219         src->val[i / 8] = src->val[i / 8] | (c << ((i % 8) * 4));
1220         }                                               /* end for */
1221     }                                                   /* end numeric */
1222 return TestDstr (src);                                  /* clean -0 */
1223 }
1224 
1225 /* Store decimal string
1226 
1227    Arguments:
1228         dsrc    =       decimal string descriptor
1229         src     =       decimal string structure
1230         flag    =       numeric/packed flag
1231 
1232    PSW.NZ are also set to their proper values
1233    PSW.V will be set on overflow; it must be initialized elsewhere
1234    (to allow for external overflow calculations)
1235 
1236    The rules for the stored sign and the PSW sign are:
1237 
1238    - Stored sign is negative if input is negative, string type
1239      is signed, and the result is non-zero or there was overflow
1240    - PSW sign is negative if input is negative, string type is
1241      signed, and the result is non-zero
1242 
1243    Thus, the stored sign and the PSW sign will differ in one case:
1244    a negative zero generated by overflow is stored with a negative
1245    sign, but PSW.N is clear
1246 */
1247 
WriteDstr(int32 * dscr,DSTR * dst,int32 flag)1248 void WriteDstr (int32 *dscr, DSTR *dst, int32 flag)
1249 {
1250 int32 c, i, limit, end, type, lnt;
1251 uint32 mask;
1252 static uint32 masktab[8] = {
1253     0xFFFFFFF0, 0xFFFFFF00, 0xFFFFF000, 0xFFFF0000,
1254     0xFFF00000, 0xFF000000, 0xF0000000, 0x00000000
1255     };
1256 static int32 unsignedtab[8] = { 0, 1, 0, 0, 0, 0, 0, 1 };
1257 
1258 type = GET_DTYP (dscr[0]);                              /* get type */
1259 lnt = GET_DLNT (dscr[0]);                               /* get string length */
1260 mask = 0;                                               /* can't ovflo */
1261 Z = 1;                                                  /* assume all 0's */
1262 limit = lnt / 8;                                        /* limit for test */
1263 for (i = 0; i < DSTRLNT; i++) {                         /* loop thru value */
1264     if (i == limit)                                     /* at limit, get mask */
1265         mask = masktab[lnt % 8];
1266     else if (i > limit)                                 /* beyond, all ovflo */
1267         mask = 0xFFFFFFFF;
1268     if (dst->val[i] & mask)                             /* test for ovflo */
1269         V = 1;
1270     if ((dst->val[i] = dst->val[i] & ~mask))            /* test nz */
1271         Z = 0;
1272     }
1273 dst->sign = dst->sign & ~unsignedtab[type] & ~(Z & ~V);
1274 N = dst->sign & ~Z;                                     /* N = sign, if ~zero */
1275 
1276 if (flag & PACKED) {                                    /* packed? */
1277     end = lnt / 2;                                      /* end of string */
1278     if (type == UP)
1279         dst->val[0] = dst->val[0] | 0xF;
1280     else dst->val[0] = dst->val[0] | 0xC | dst->sign;
1281     for (i = 0; i <= end; i++) {                        /* store string */
1282         c = (dst->val[i / 4] >> ((i % 4) * 8)) & 0xFF;
1283         WriteB (c, ((dscr[1] + end - i) & 0177777) | dsenable);
1284         }                                               /* end for */
1285     }                                                   /* end packed */
1286 else {
1287     if (type >= TS) WriteB (dst->sign? '-': '+', (((type == TS)?
1288          dscr[1] + lnt: dscr[1] - 1) & 0177777) | dsenable);
1289     for (i = 1; i <= lnt; i++) {                        /* store string */
1290         c = (dst->val[i / 8] >> ((i % 8) * 4)) & 0xF;   /* get digit */
1291         if ((i == 1) && (type == XZ) && dst->sign)
1292             c = c | 0x70;                               /* signed zoned */
1293         else if (((i == 1) && (type == TO)) ||
1294             ((i == lnt) && (type == LO)))
1295             c = binover[dst->sign][c];                  /* get sign and digit */
1296         else c = c | 0x30;                              /* default */
1297         WriteB (c, ((dscr[1] + lnt - i) & 0177777) |dsenable );
1298         }                                               /* end for */
1299     }                                                   /* end numeric */
1300 return;
1301 }
1302 
1303 /* Add decimal string magnitudes
1304 
1305    Arguments:
1306         s1      =       source1 decimal string
1307         s2      =       source2 decimal string
1308         ds      =       destination decimal string
1309         cy      =       carry in
1310    Output       =       1 if carry, 0 if no carry
1311 
1312    This algorithm courtesy Anton Chernoff, circa 1992 or even earlier.
1313 
1314    We trace the history of a pair of adjacent digits to see how the
1315    carry is fixed; each parenthesized item is a 4b digit.
1316 
1317    Assume we are adding:
1318 
1319         (a)(b)  I
1320    +    (x)(y)  J
1321 
1322    First compute I^J:
1323 
1324         (a^x)(b^y)      TMP
1325 
1326    Note that the low bit of each digit is the same as the low bit of
1327    the sum of the digits, ignoring the cary, since the low bit of the
1328    sum is the xor of the bits.
1329 
1330    Now compute I+J+66 to get decimal addition with carry forced left
1331    one digit:
1332 
1333         (a+x+6+carry mod 16)(b+y+6 mod 16)      SUM
1334 
1335    Note that if there was a carry from b+y+6, then the low bit of the
1336    left digit is different from the expected low bit from the xor.
1337    If we xor this SUM into TMP, then the low bit of each digit is 1
1338    if there was a carry, and 0 if not.  We need to subtract 6 from each
1339    digit that did not have a carry, so take ~(SUM ^ TMP) & 0x11, shift
1340    it right 4 to the digits that are affected, and subtract 6*adjustment
1341    (actually, shift it right 3 and subtract 3*adjustment).
1342 */
1343 
AddDstr(DSTR * s1,DSTR * s2,DSTR * ds,int32 cy)1344 int32 AddDstr (DSTR *s1, DSTR *s2, DSTR *ds, int32 cy)
1345 {
1346 int32 i;
1347 uint32 sm1, sm2, tm1, tm2, tm3, tm4;
1348 
1349 for (i = 0; i < DSTRLNT; i++) {                         /* loop low to high */
1350     tm1 = s1->val[i] ^ (s2->val[i] + cy);               /* xor operands */
1351     sm1 = s1->val[i] + (s2->val[i] + cy);               /* sum operands */
1352     sm2 = sm1 + 0x66666666;                             /* force carry out */
1353     cy = ((sm1 < s1->val[i]) || (sm2 < sm1));           /* check for overflow */
1354     tm2 = tm1 ^ sm2;                                    /* get carry flags */
1355     tm3 = (tm2 >> 3) | (cy << 29);                      /* compute adjustment */
1356     tm4 = 0x22222222 & ~tm3;                            /* clear where carry */
1357     ds->val[i] = sm2 - (3 * tm4);                       /* final result */
1358     }
1359 return cy;
1360 }
1361 
1362 /* Subtract decimal string magnitudes
1363 
1364    Arguments:
1365         s1      =       source1 decimal string
1366         s2      =       source2 decimal string
1367         ds      =       destination decimal string
1368    Outputs:             s2 - s1 in ds
1369 
1370    Note: the routine assumes that s1 <= s2
1371 
1372 */
1373 
SubDstr(DSTR * s1,DSTR * s2,DSTR * ds)1374 void SubDstr (DSTR *s1, DSTR *s2, DSTR *ds)
1375 {
1376 int32 i;
1377 DSTR compl;
1378 
1379 for (i = 0; i < DSTRLNT; i++)
1380     compl.val[i] = 0x99999999 - s1->val[i];
1381 AddDstr (&compl, s2, ds, 1);                            /* s1 + ~s2 + 1 */
1382 return;
1383 }
1384 
1385 /* Compare decimal string magnitudes
1386 
1387    Arguments:
1388         s1      =       source1 decimal string
1389         s2      =       source2 decimal string
1390    Output       =       1 if >, 0 if =, -1 if <
1391 */
1392 
CmpDstr(DSTR * s1,DSTR * s2)1393 int32 CmpDstr (DSTR *s1, DSTR *s2)
1394 {
1395 int32 i;
1396 
1397 for (i = DSTRMAX; i >=0; i--) {
1398     if (s1->val[i] > s2->val[i])
1399         return 1;
1400     if (s1->val[i] < s2->val[i])
1401         return -1;
1402     }
1403 return 0;
1404 }
1405 
1406 /* Test decimal string for zero
1407 
1408    Arguments:
1409         dsrc    =       decimal string structure
1410 
1411    Returns the non-zero length of the string, in int32 units
1412    If the string is zero, the sign is cleared
1413 */
1414 
TestDstr(DSTR * dsrc)1415 int32 TestDstr (DSTR *dsrc)
1416 {
1417 int32 i;
1418 
1419 for (i = DSTRMAX; i >= 0; i--) {
1420     if (dsrc->val[i])
1421         return (i + 1);
1422     }
1423 dsrc->sign = 0;
1424 return 0;
1425 }
1426 
1427 /* Get exact length of decimal string
1428 
1429    Arguments:
1430         dsrc    =       decimal string structure
1431         nz      =       result from TestDstr
1432 */
1433 
LntDstr(DSTR * dsrc,int32 nz)1434 int32 LntDstr (DSTR *dsrc, int32 nz)
1435 {
1436 int32 i;
1437 
1438 if (nz == 0)
1439     return 0;
1440 for (i = 7; i >= 0; i--) {
1441     if ((dsrc->val[nz - 1] >> (i * 4)) & 0xF)
1442         break;
1443     }
1444 return ((nz - 1) * 8) + i;
1445 }
1446 
1447 /* Create table of multiples
1448 
1449    Arguments:
1450         dsrc    =       base decimal string structure
1451         mtable[10] =    array of decimal string structures
1452 
1453    Note that dsrc has a high order zero nibble; this
1454    guarantees that the largest multiple won't overflow.
1455    Also note that mtable[0] is not filled in.
1456 */
1457 
CreateTable(DSTR * dsrc,DSTR mtable[10])1458 void CreateTable (DSTR *dsrc, DSTR mtable[10])
1459 {
1460 int32 (i);
1461 
1462 mtable[1] = *dsrc;
1463 for (i = 2; i < 10; i++)
1464     AddDstr (&mtable[1], &mtable[i-1], &mtable[i], 0);
1465 return;
1466 }
1467 
1468 /* Word shift right
1469 
1470    Arguments:
1471         dsrc    =       decimal string structure
1472         sc      =       shift count
1473 */
1474 
WordRshift(DSTR * dsrc,int32 sc)1475 void WordRshift (DSTR *dsrc, int32 sc)
1476 {
1477 int32 i;
1478 
1479 if (sc) {
1480     for (i = 0; i < DSTRLNT; i++) {
1481         if ((i + sc) < DSTRLNT)
1482             dsrc->val[i] = dsrc->val[i + sc];
1483         else dsrc->val[i] = 0;
1484         }
1485     }
1486 return;
1487 }
1488 
1489 /* Word shift left
1490 
1491    Arguments:
1492         dsrc    =       decimal string structure
1493         sc      =       shift count
1494 */
1495 
WordLshift(DSTR * dsrc,int32 sc)1496 int32 WordLshift (DSTR *dsrc, int32 sc)
1497 {
1498 int32 i, c;
1499 
1500 c = 0;
1501 if (sc) {
1502     for (i = DSTRMAX; i >= 0; i--) {
1503         if (i >= sc)
1504             dsrc->val[i] = dsrc->val[i - sc];
1505         else {
1506             c |= dsrc->val[i];
1507             dsrc->val[i] = 0;
1508             }
1509         }
1510     }
1511 return c;
1512 }
1513 
1514 /* Nibble shift decimal string right
1515 
1516    Arguments:
1517         dsrc    =       decimal string structure
1518         sc      =       shift count
1519         cin     =       carry in
1520 */
1521 
NibbleRshift(DSTR * dsrc,int32 sc,uint32 cin)1522 uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin)
1523 {
1524 int32 i, s, nc;
1525 
1526 if ((s = sc * 4)) {
1527     for (i = DSTRMAX; i >= 0; i--) {
1528         nc = (dsrc->val[i] << (32 - s)) & 0xFFFFFFFF;
1529         dsrc->val[i] = ((dsrc->val[i] >> s) |
1530             cin) & 0xFFFFFFFF;
1531         cin = nc;
1532         }
1533     return cin;
1534     }
1535 return 0;
1536 }
1537 
1538 /* Nibble shift decimal string left
1539 
1540    Arguments:
1541         dsrc    =       decimal string structure
1542         sc      =       shift count
1543 */
1544 
NibbleLshift(DSTR * dsrc,int32 sc)1545 uint32 NibbleLshift (DSTR *dsrc, int32 sc)
1546 {
1547 int32 i, s;
1548 uint32 nc, cin;
1549 
1550 cin = 0;
1551 if ((s = sc * 4)) {
1552     for (i = 0; i < DSTRLNT; i++) {
1553         nc = dsrc->val[i] >> (32 - s);
1554         dsrc->val[i] = ((dsrc->val[i] << s) | cin) & 0xFFFFFFFF;
1555         cin = nc;
1556         }
1557     return cin;
1558     }
1559 return 0;
1560 }
1561 
1562 /* Common setup routine for MOVC class instructions */
1563 
movx_setup(int32 op,int32 * arg)1564 int32 movx_setup (int32 op, int32 *arg)
1565 {
1566 int32 mvlnt, t;
1567 
1568 if (CPUT (CPUT_44)) {                                   /* 11/44? */
1569     ReadMB (((SP - 0200) & 0177777) | dsenable);        /* probe both blocks */
1570     ReadMB (((SP - 0100) & 0177777) | dsenable);        /* in 64W stack area */
1571     }
1572 if (op & INLINE) {                                      /* inline */
1573     mvlnt = (A1LNT < A2LNT)? A1LNT: A2LNT;
1574     WriteW (mvlnt, ((SP - 14) & 0177777) | dsenable);   /* push move length */
1575     WriteW (R[0], ((SP - 12) & 0177777) | dsenable);    /* push R0 - R5 */
1576     WriteW (R[1], ((SP - 10) & 0177777) | dsenable);
1577     WriteW (R[2], ((SP - 8) & 0177777) | dsenable);
1578     WriteW (R[3], ((SP - 6) & 0177777) | dsenable);
1579     WriteW (R[4], ((SP - 4) & 0177777) | dsenable);
1580     WriteW (R[5], ((SP - 2) & 0177777) | dsenable);
1581     SP = (SP - 14) & 0177777;
1582     R[0] = A1LNT;                                       /* args to registers */
1583     R[1] = A1ADR;
1584     R[2] = A2LNT;
1585     R[3] = A2ADR;
1586     R[4] = A3LNT;
1587     R[5] = A3ADR & 0177777;
1588     }
1589 else {                                                  /* register */
1590     mvlnt = (R[0] < R[2])? R[0]: R[2];
1591     WriteW (mvlnt, ((SP - 2) & 0177777) | dsenable);    /* push move length */
1592     SP = (SP - 2) & 0177777;
1593     }
1594 fpd = 1;
1595 t = R[0] - R[2];                                        /* src.lnt - dst.lnt */
1596 N = GET_SIGN_W (t);                                     /* set cc's from diff */
1597 Z = GET_Z (t);
1598 V = GET_SIGN_W ((R[0] ^ R[2]) & (~R[2] ^ t));
1599 C = (R[0] < R[2]);
1600 return mvlnt;
1601 }
1602 
1603 /* Common cleanup routine for MOVC class instructions */
1604 
movx_cleanup(int32 op)1605 void movx_cleanup (int32 op)
1606 {
1607 SP = (SP + 2) & 0177777;                                /* discard mvlnt */
1608 if (op & INLINE) {                                      /* inline? */
1609     R[0] = ReadW (SP | dsenable);                       /* restore R0 - R5 */
1610     R[1] = ReadW (((SP + 2) & 0177777) | dsenable);
1611     R[2] = ReadW (((SP + 4) & 0177777) | dsenable);
1612     R[3] = ReadW (((SP + 6) & 0177777) | dsenable);
1613     R[4] = ReadW (((SP + 8) & 0177777) | dsenable);
1614     R[5] = ReadW (((SP + 10) & 0177777) | dsenable);
1615     SP = (SP + 12) & 0177777;
1616     }
1617 else R[1] = R[2] = R[3] = 0;                            /* reg, clear R1 - R3 */
1618 fpd = 0;                                                /* instr done */
1619 return;
1620 }
1621 
1622 /* Test for CIS mid-instruction interrupt */
1623 
cis_int_test(int32 cycles,int32 oldpc,t_stat * st)1624 t_bool cis_int_test (int32 cycles, int32 oldpc, t_stat *st)
1625 {
1626 while (cycles >= 0) {                                   /* until delay done */
1627     if (sim_interval > cycles) {                        /* event > delay */
1628         sim_interval = sim_interval - cycles;
1629         break;
1630         }
1631     else {                                              /* event <= delay */
1632         cycles = cycles - sim_interval;                 /* decr delay */
1633         sim_interval = 0;                               /* process event */
1634         *st = sim_process_event ();
1635         trap_req = calc_ints (ipl, trap_req);           /* recalc int req */
1636         if ((*st != SCPE_OK) ||                         /* bad status or */
1637             trap_req & TRAP_INT) {                      /* interrupt? */
1638             PC = oldpc;                                 /* back out */
1639             return TRUE;
1640             }                                           /* end if stop */
1641         }                                               /* end else event */
1642     }                                                   /* end while delay */
1643 return FALSE;
1644 }
1645