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