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