1
/* pdp11_cis.c: PDP-11 CIS optional instruction set simulator
3
Copyright (c) 1993-2004, Robert M Supnik
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:
12
The above copyright notice and this permission notice shall be included in
13
all copies or substantial portions of the Software.
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.
22
Except as contained in this notice, the name of Robert M Supnik shall not
23
be 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.
26
This module simulates the PDP-11 commercial instruction set (CIS).
28
17-Oct-02 RMS Fixed compiler warning (found by Hans Pufal)
29
08-Oct-02 RMS Fixed macro definitions
31
The commercial instruction set consists of three instruction formats:
33
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ register operands
34
| 0 1 1 1 1 1| 0 0 0 0| opcode | 076030:076057
35
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 076070:076077
37
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ inline operands
38
| 0 1 1 1 1 1| 0 0 0 1| opcode | 076130:076157
39
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 076170:076177
41
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ load descriptors
42
| 0 1 1 1 1 1| 0 0 0 0|op| 1 0| reg | 076020:076027
43
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 076060:076067
45
The CIS instructions operate on character strings, packed (decimal)
46
strings, and numeric (decimal) strings. Strings are described by
47
a two word descriptor:
49
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
50
| length in bytes | char string
51
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ descriptor
52
| starting byte address |
53
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
55
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
56
| |str type| | length | decimal string
57
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ descriptor
58
| starting byte address |
59
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
61
Decimal string types are:
63
<14:12> data type bytes occupied by n digits
66
2 trailing overpunch n
68
4 trailing separate n+1
69
5 leading separate n+1
70
6 signed packed n/2 +1
71
7 unsigned packed n/2 +1
73
Zero length character strings occupy no memory; zero length decimal strings
74
require either zero bytes (zoned, overpunch) or one byte (separate, packed).
76
CIS instructions can run for a very long time, so they are interruptible
77
and restartable. In the simulator, all instructions run to completion.
78
The code is unoptimized.
81
#include "pdp11_defs.h"
85
#define INLINE 0100 /* inline */
86
#define PACKED 0020 /* packed */
87
#define NUMERIC 0000 /* numeric */
89
/* Operand type definitions */
91
#define R0_DESC 1 /* descr in R0:R1 */
92
#define R2_DESC 2 /* descr in R2:R3 */
93
#define R4_DESC 3 /* descr in R4:R5 */
94
#define R4_ARG 4 /* argument in R4 */
95
#define IN_DESC 5 /* inline descriptor */
96
#define IN_ARG 6 /* inline argument */
97
#define IN_DESC_R0 7 /* inline descr to R0:R1 */
98
#define MAXOPN 4 /* max # operands */
100
/* Decimal data type definitions */
102
#define XZ 0 /* signed zoned */
103
#define UZ 1 /* unsigned zoned */
104
#define TO 2 /* trailing overpunch */
105
#define LO 3 /* leading overpunch */
106
#define TS 4 /* trailing separate */
107
#define LS 5 /* leading separate */
108
#define XP 6 /* signed packed */
109
#define UP 7 /* unsigned packed */
111
/* Decimal descriptor definitions */
113
#define DTYP_M 07 /* type mask */
114
#define DTYP_V 12 /* type position */
115
#define DLNT_M 037 /* length mask */
116
#define DLNT_V 0 /* length position */
117
#define GET_DTYP(x) (((x) >> DTYP_V) & DTYP_M)
118
#define GET_DLNT(x) (((x) >> DLNT_V) & DLNT_M)
120
/* Shift operand definitions */
122
#define ASHRND_M 017 /* round digit mask */
123
#define ASHRND_V 8 /* round digit pos */
124
#define ASHLNT_M 0377 /* shift count mask */
125
#define ASHLNT_V 0 /* shift length pos */
126
#define ASHSGN 0200 /* shift sign */
127
#define GET_ASHRND(x) (((x) >> ASHRND_V) & ASHRND_M)
128
#define GET_ASHLNT(x) (((x) >> ASHLNT_V) & ASHLNT_M)
130
/* Operand array aliases */
142
/* Condition code macros */
144
#define GET_BIT(ir,n) (((ir) >> (n)) & 1)
145
#define GET_SIGN_L(ir) GET_BIT((ir), 31)
146
#define GET_SIGN_W(ir) GET_BIT((ir), 15)
147
#define GET_SIGN_B(ir) GET_BIT((ir), 7)
148
#define GET_Z(ir) ((ir) == 0)
150
/* Decimal string structure */
153
#define DSTRMAX (DSTRLNT - 1)
154
#define MAXDVAL 429496730 /* 2^32 / 10 */
158
unsigned int32 val[DSTRLNT]; };
160
typedef struct dstr DSTR;
162
static DSTR Dstr0 = { 0, 0, 0, 0, 0 };
164
extern int32 isenable, dsenable;
165
extern int32 N, Z, V, C;
166
extern int32 R[8], trap_req;
167
extern int32 ReadW (int32 addr);
168
extern void WriteW (int32 data, int32 addr);
169
extern int32 ReadB (int32 addr);
170
extern void WriteB (int32 data, int32 addr);
171
int32 ReadDstr (int32 *dscr, DSTR *dec, int32 flag);
172
void WriteDstr (int32 *dscr, DSTR *dec, int32 flag);
173
int32 AddDstr (DSTR *src1, DSTR *src2, DSTR *dst, int32 cin);
174
void SubDstr (DSTR *src1, DSTR *src2, DSTR *dst);
175
int32 CmpDstr (DSTR *src1, DSTR *src2);
176
int32 TestDstr (DSTR *dsrc);
177
int32 LntDstr (DSTR *dsrc, int32 nz);
178
unsigned int32 NibbleLshift (DSTR *dsrc, int32 sc, unsigned int32 cin);
179
unsigned int32 NibbleRshift (DSTR *dsrc, int32 sc, unsigned int32 cin);
180
int32 WordLshift (DSTR *dsrc, int32 sc);
181
void WordRshift (DSTR *dsrc, int32 sc);
182
void CreateTable (DSTR *dsrc, DSTR mtable[10]);
184
/* Table of instruction operands */
186
static int32 opntab[128][MAXOPN] = {
187
0, 0, 0, 0, 0, 0, 0, 0, /* 000 - 007 */
188
0, 0, 0, 0, 0, 0, 0, 0,
189
0, 0, 0, 0, 0, 0, 0, 0,
190
0, 0, 0, 0, 0, 0, 0, 0,
191
0, 0, 0, 0, 0, 0, 0, 0, /* 010 - 017 */
192
0, 0, 0, 0, 0, 0, 0, 0,
193
0, 0, 0, 0, 0, 0, 0, 0,
194
0, 0, 0, 0, 0, 0, 0, 0,
195
0, 0, 0, 0, 0, 0, 0, 0, /* LD2R */
196
0, 0, 0, 0, 0, 0, 0, 0,
197
0, 0, 0, 0, 0, 0, 0, 0,
198
0, 0, 0, 0, 0, 0, 0, 0,
199
R0_DESC, R2_DESC, R4_ARG, 0, /* MOVC */
200
R0_DESC, R2_DESC, R4_ARG, 0, /* MOVRC */
201
R0_DESC, R2_DESC, R4_DESC, 0, /* MOVTC */
202
0, 0, 0, 0, /* 033 */
203
0, 0, 0, 0, 0, 0, 0, 0, /* 034 - 037 */
204
0, 0, 0, 0, 0, 0, 0, 0,
205
R4_ARG, 0, 0, 0, /* LOCC */
206
R4_ARG, 0, 0, 0, /* SKPC */
207
R4_DESC, 0, 0, 0, /* SCANC */
208
R4_DESC, 0, 0, 0, /* SPANC */
209
R0_DESC, R2_DESC, R4_ARG, 0, /* CMPC */
210
R2_DESC, 0, 0, 0, /* MATC */
211
0, 0, 0, 0, 0, 0, 0, 0, /* 046 - 047 */
212
R0_DESC, R2_DESC, R4_DESC, 0, /* ADDN */
213
R0_DESC, R2_DESC, R4_DESC, 0, /* SUBN */
214
R0_DESC, R2_DESC, 0, 0, /* CMPN */
215
R0_DESC, 0, 0, 0, /* CVTNL */
216
R0_DESC, R2_DESC, 0, 0, /* CVTPN */
217
R0_DESC, R2_DESC, 0, 0, /* CVTNP */
218
R0_DESC, R2_DESC, R4_ARG, 0, /* ASHN */
219
R0_DESC, 0, 0, 0, /* CVTLN */
220
0, 0, 0, 0, 0, 0, 0, 0, /* LD3R */
221
0, 0, 0, 0, 0, 0, 0, 0,
222
0, 0, 0, 0, 0, 0, 0, 0,
223
0, 0, 0, 0, 0, 0, 0, 0,
224
R0_DESC, R2_DESC, R4_DESC, 0, /* ADDP */
225
R0_DESC, R2_DESC, R4_DESC, 0, /* SUBP */
226
R0_DESC, R2_DESC, 0, 0, /* CMPP */
227
R0_DESC, 0, 0, 0, /* CVTPL */
228
R0_DESC, R2_DESC, R4_DESC, 0, /* MULP */
229
R0_DESC, R2_DESC, R4_DESC, 0, /* DIVP */
230
R0_DESC, R2_DESC, R4_ARG, 0, /* ASHP */
231
R0_DESC, 0, 0, 0, /* CVTLP */
232
0, 0, 0, 0, 0, 0, 0, 0, /* 100 - 107 */
233
0, 0, 0, 0, 0, 0, 0, 0,
234
0, 0, 0, 0, 0, 0, 0, 0,
235
0, 0, 0, 0, 0, 0, 0, 0,
236
0, 0, 0, 0, 0, 0, 0, 0, /* 110 - 117 */
237
0, 0, 0, 0, 0, 0, 0, 0,
238
0, 0, 0, 0, 0, 0, 0, 0,
239
0, 0, 0, 0, 0, 0, 0, 0,
240
0, 0, 0, 0, 0, 0, 0, 0, /* 120 - 127 */
241
0, 0, 0, 0, 0, 0, 0, 0,
242
0, 0, 0, 0, 0, 0, 0, 0,
243
0, 0, 0, 0, 0, 0, 0, 0,
244
IN_DESC, IN_DESC, IN_ARG, 0, /* MOVCI */
245
IN_DESC, IN_DESC, IN_ARG, 0, /* MOVRCI */
246
IN_DESC, IN_DESC, IN_ARG, IN_ARG, /* MOVTCI */
247
0, 0, 0, 0, /* 133 */
248
0, 0, 0, 0, 0, 0, 0, 0, /* 134 - 137 */
249
0, 0, 0, 0, 0, 0, 0, 0,
250
IN_DESC_R0, IN_ARG, 0, 0, /* LOCCI */
251
IN_DESC_R0, IN_ARG, 0, 0, /* SKPCI */
252
IN_DESC_R0, IN_DESC, 0, 0, /* SCANCI */
253
IN_DESC_R0, IN_DESC, 0, 0, /* SPANCI */
254
IN_DESC, IN_DESC, IN_ARG, 0, /* CMPCI */
255
IN_DESC_R0, IN_DESC, 0, 0, /* MATCI */
256
0, 0, 0, 0, 0, 0, 0, 0, /* 146 - 147 */
257
IN_DESC, IN_DESC, IN_DESC, 0, /* ADDNI */
258
IN_DESC, IN_DESC, IN_DESC, 0, /* SUBNI */
259
IN_DESC, IN_DESC, 0, 0, /* CMPNI */
260
IN_DESC, IN_ARG, 0, 0, /* CVTNLI */
261
IN_DESC, IN_DESC, 0, 0, /* CVTPNI */
262
IN_DESC, IN_DESC, 0, 0, /* CVTNPI */
263
IN_DESC, IN_DESC, IN_ARG, 0, /* ASHNI */
264
IN_DESC, IN_DESC, 0, 0, /* CVTLNI */
265
0, 0, 0, 0, 0, 0, 0, 0, /* 160 - 167 */
266
0, 0, 0, 0, 0, 0, 0, 0,
267
0, 0, 0, 0, 0, 0, 0, 0,
268
0, 0, 0, 0, 0, 0, 0, 0,
269
IN_DESC, IN_DESC, IN_DESC, 0, /* ADDPI */
270
IN_DESC, IN_DESC, IN_DESC, 0, /* SUBPI */
271
IN_DESC, IN_DESC, 0, 0, /* CMPPI */
272
IN_DESC, 0, 0, 0, /* CVTPLI */
273
IN_DESC, IN_DESC, IN_DESC, 0, /* MULPI */
274
IN_DESC, IN_DESC, IN_DESC, 0, /* DIVPI */
275
IN_DESC, IN_DESC, IN_ARG, 0, /* ASHPI */
276
IN_DESC, IN_DESC, 0, 0 /* CVTLPI */
279
/* ASCII to overpunch table: sign is <7>, digit is <4:0> */
281
static int32 overbin[128] = {
282
0, 0, 0, 0, 0, 0, 0, 0, /* 000 - 037 */
283
0, 0, 0, 0, 0, 0, 0, 0,
284
0, 0, 0, 0, 0, 0, 0, 0,
285
0, 0, 0, 0, 0, 0, 0, 0,
286
0, 0x80, 0, 0, 0, 0, 0, 0, /* 040 - 077 */
287
0, 0, 0, 0, 0, 0, 0, 0,
288
0, 1, 2, 3, 4, 5, 6, 7,
289
8, 9, 0x80, 0, 0, 0, 0, 0,
290
0, 1, 2, 3, 4, 5, 6, 7, /* 100 - 137 */
291
8, 9, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86,
292
0x87, 0x88, 0x89, 0, 0, 0, 0, 0,
293
0, 0, 0, 0, 0, 0x80, 0, 0,
294
0, 0, 0, 0, 0, 0, 0, 0, /* 140 - 177 */
295
0, 0, 0, 0, 0, 0, 0, 0,
296
0, 0, 0, 0, 0, 0, 0, 0,
297
0, 0, 0, 0, 0, 0x80, 0, 0
300
/* Overpunch to ASCII table: indexed by sign and digit */
302
static int32 binover[2][16] = {
303
'{', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
304
'0', '0', '0', '0', '0', '0',
305
'}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
306
'0', '0', '0', '0', '0', '0'
309
static unsigned char movbuf[65536];
313
void cis11 (int32 IR)
315
int32 c, i, j, k, t, op, rn, addr;
316
int32 fill, mask, match, limit, mvlnt, shift;
317
int32 spc, ldivd, ldivr;
318
int32 arg[6]; /* operands */
319
uint32 nc, digit, result;
320
static DSTR accum, src1, src2, dst;
321
static DSTR mptable[10];
322
static DSTR Dstr1 = { 0, 0x10, 0, 0, 0 };
324
op = IR & 0177; /* IR <6:0> */
325
for (i = j = 0; (i < MAXOPN) && opntab[op][i]; i++) { /* parse operands */
326
switch (opntab[op][i]) { /* case on op type */
343
addr = ReadW (PC | isenable);
344
PC = (PC + 2) & 0177777;
345
arg[j++] = ReadW (addr | dsenable);
346
arg[j++] = ReadW (((addr + 2) & 0177777) | dsenable);
349
addr = ReadW (PC | isenable);
350
PC = (PC + 2) & 0177777;
351
R[0] = ReadW (addr | dsenable);
352
R[1] = ReadW (((addr + 2) & 0177777) | dsenable);
355
arg[j++] = ReadW (PC | isenable);
356
PC = (PC + 2) & 0177777;
357
break; } /* end case */
359
switch (op) { /* case on opcode */
361
/* MOVC, MOVTC, MOVCI, MOVTCI
364
A1LNT, A1ADR = source string descriptor
365
A2LNT, A2ADR = dest string descriptor
366
A3LNT<7:0> = fill character
367
A3ADR = translation table address (MOVTC, MOVTCI only)
370
NZVC = set from src.lnt - dst.lnt
372
Registers (MOVC, MOVTC only)
373
R0 = max (0, src.len - dst.len)
378
- To avoid overlap problems, the entire source string is
379
buffered in movbuf. On a modern microprocessor, for most
380
string sizes, this will be handled in the on chip cache.
381
- If either the source or destination lengths are zero,
382
the move loops exit immediately.
383
- If the source length does not exceed the destination
384
length, the fill loop exits immediately.
387
case 030: case 032: case 0130: case 0132:
388
mvlnt = (A1LNT < A2LNT)? A1LNT: A2LNT; /* calc move lnt */
389
for (i = 0; i < mvlnt; i++) {
390
movbuf[i] = ReadB (((A1ADR + i) & 0177777) | dsenable); }
391
for (i = 0; i < mvlnt; i++) {
393
if (op & 2) t = ReadB (((A3ADR + t) & 0177777) | dsenable);
394
WriteB (t, ((A2ADR + i) & 0177777) | dsenable); }
395
fill = A3LNT & 0377; /* do fill, if any */
396
for (i = mvlnt; i < A2LNT; i++) {
397
WriteB (fill, ((A2ADR + i) & 0177777) | dsenable); }
398
t = A1LNT - A2LNT; /* src.lnt - dst.lnt */
399
N = GET_SIGN_W (t); /* set cc's from diff */
401
V = GET_SIGN_W ((A1LNT ^ A2LNT) & (~A2LNT ^ t));
403
if ((op & INLINE) == 0) { /* if reg, set reg */
404
R[0] = C? 0: t & 0177777;
405
R[1] = R[2] = R[3] = 0;
406
R[4] = R[4] & 0377; }
412
A1LNT, A1ADR = source string descriptor
413
A2LNT, A2ADR = dest string descriptor
414
A3LNT<7:0> = fill character
417
NZVC = set from src.lnt - dst.lnt
419
Registers (MOVRC only)
420
R0 = max (0, src.len - dst.len)
424
Notes: see MOVC, MOVCI
428
mvlnt = (A1LNT < A2LNT)? A1LNT: A2LNT; /* calc move lnt */
429
addr = A1ADR + A1LNT - mvlnt;
430
for (i = 0; i < mvlnt; i++) {
431
movbuf[i] = ReadB (((addr + i) & 0177777) | dsenable); }
432
addr = A2ADR + A2LNT - mvlnt;
433
for (i = 0; i < mvlnt; i++) {
434
WriteB (movbuf[i], ((addr + i) & 0177777) | dsenable); }
435
fill = A3LNT & 0377; /* do fill, if any */
436
for (i = mvlnt, j = 0; i < A2LNT; i++, j++) {
437
WriteB (fill, ((A2ADR + j) & 0177777) | dsenable); }
438
t = A1LNT - A2LNT; /* src.lnt - dst.lnt */
439
N = GET_SIGN_W (t); /* set cc's from diff */
441
V = GET_SIGN_W ((A1LNT ^ A2LNT) & (~A2LNT ^ t));
443
if ((op & INLINE) == 0) { /* if reg, set reg */
444
R[0] = C? 0: t & 0177777;
445
R[1] = R[2] = R[3] = 0;
446
R[4] = R[4] & 0377; }
449
/* Load descriptors - no operands */
451
case 020: case 021: case 022: case 023:
452
case 024: case 025: case 026: case 027:
453
case 060: case 061: case 062: case 063:
454
case 064: case 065: case 066: case 067:
455
limit = (op & 040)? 6: 4;
456
rn = IR & 07; /* get register */
458
spc = (rn == 7)? isenable: dsenable;
459
for (j = 0; j < limit; j = j + 2) { /* loop for 2,3 dscr */
460
addr = ReadW (((t + j) & 0177777) | spc);
461
R[j] = ReadW (addr | dsenable);
462
R[j + 1] = ReadW (((addr + 2) & 0177777) | dsenable); }
463
if (rn >= limit) R[rn] = (R[rn] + limit) & 0177777;
466
/* LOCC, SKPC, LOCCI, SKPCI
469
R0, R1 = source string descriptor
470
A1LNT<7:0> = match character
477
R0:R1 = substring descriptor where operation terminated
480
case 040: case 041: case 0140: case 0141:
481
match = A1LNT & 0377; /* match character */
482
for ( ; R[0] != 0; R[0]--) { /* loop */
483
c = ReadB (R[1] | dsenable); /* get char */
484
if ((c == match) ^ (op & 1)) break; /* = + LOC, != + SKP? */
485
R[1] = (R[1] + 1) & 0177777; }
486
N = GET_SIGN_W (R[0]);
489
if ((op & INLINE) == 0) R[4] = R[4] & 0377; /* if reg, set reg */
492
/* SCANC, SPANC, SCANCI, SPANCI
495
R0, R1 = source string descriptor
497
A1ADR = table address
504
R0:R1 = substring descriptor where operation terminated
507
case 042: case 043: case 0142: case 0143:
508
mask = A1LNT & 0377; /* mask character */
509
for (; R[0] != 0; R[0]--) { /* loop */
510
t = ReadB (R[1] | dsenable); /* get char as index */
511
c = ReadB (((A1ADR + t) & 0177777) | dsenable);
512
if (((c & mask) != 0) ^ (op & 1)) break; /* != + SCN, = + SPN? */
513
R[1] = (R[1] + 1) & 0177777; }
514
N = GET_SIGN_W (R[0]);
517
if ((op & INLINE) == 0) R[4] = R[4] & 0377; /* if reg, set reg */
523
A1LNT, A1ADR = source1 string descriptor
524
A2LNT, A2ADR = source2 string descriptor
525
A3LNT<7:0> = fill character
528
NZVC = set from src1 - src2 at mismatch, or
531
Registers (CMPC only):
532
R0:R1 = unmatched source1 substring descriptor
533
R2:R3 = unmatched source2 substring descriptor
538
for (i = 0; i < ((A1LNT > A2LNT)? A1LNT: A2LNT); i++) {
539
if (i < A1LNT) c = ReadB (((A1ADR + i) & 0177777) | dsenable);
540
else c = A3LNT & 0377;
541
if (i < A2LNT) t = ReadB (((A2ADR + i) & 0177777) | dsenable);
542
else t = A3LNT & 0377;
544
j = c - t; /* last chars read */
545
N = GET_SIGN_B (j); /* set cc's */
547
V = GET_SIGN_B ((c ^ t) & (~t ^ j));
549
if ((op & INLINE) == 0) { /* if reg, set reg */
550
j = (i > A1LNT)? A1LNT: i; /* #src1 chars used */
551
k = (i > A2LNT)? A2LNT: i; /* #src2 chars used */
553
R[1] = (A1ADR + j) & 0177777;
555
R[3] = (A2ADR + k) & 0177777;
556
R[4] = R[4] & 0377; }
562
R0, R1 = source string descriptor
563
A1LNT, A1ADR = substring descriptor
570
R0:R1 = source substring descriptor for match
573
- If the string is zero length, and the substring is not,
574
the outer loop exits immediately, and the result is
576
- If the substring is zero length, the inner loop always
577
exits immediately, and the result is a "match"
578
- If the string is zero length, and the substring is as
579
well, the outer loop executes, the inner loop exits
580
immediately, and the result is a match, but the result
581
is the length of the string (zero), or "no match"
584
case 0045: case 0145:
585
for (match = 0; R[0] >= A1LNT; R[0]--) { /* loop thru string */
586
for (i = 0, match = 1; match && (i < A1LNT); i++) {
587
c = ReadB (((R[1] + i) & 0177777) | dsenable);
588
t = ReadB (((A1ADR + i) & 0177777) | dsenable);
589
match = (c == t); } /* end for substring */
590
if (match) break; /* exit if match */
591
R[1] = (R[1] + 1) & 0177777; } /* end for string */
592
if (!match) { /* if no match */
593
R[1] = (R[1] + R[0]) & 0177777;
595
N = GET_SIGN_W (R[0]);
600
/* ADDN, SUBN, ADDP, SUBP, ADDNI, SUBNI, ADDPI, SUBPI
603
A1LNT, A1ADR = source1 string descriptor
604
A2LNT, A2ADR = source2 string descriptor
605
A3LNT, A3ADR = destination string descriptor
608
NZV = set from result
611
Registers (ADDN, ADDP, SUBN, SUBP only):
615
case 050: case 051: case 070: case 071:
616
case 0150: case 0151: case 0170: case 0171:
617
ReadDstr (A1, &src1, op); /* get source1 */
618
ReadDstr (A2, &src2, op); /* get source2 */
619
if (op & 1) src1.sign = src1.sign ^ 1; /* sub? invert sign */
620
if (src1.sign ^ src2.sign) { /* opp signs? sub */
621
if (CmpDstr (&src1, &src2) < 0) { /* src1 < src2? */
622
SubDstr (&src1, &src2, &dst); /* src2 - src1 */
623
dst.sign = src2.sign; } /* sign = src2 */
625
SubDstr (&src2, &src1, &dst); /* src1 - src2 */
626
dst.sign = src1.sign; } /* sign = src1 */
627
V = 0; } /* can't carry */
628
else { /* addition */
629
V = AddDstr (&src1, &src2, &dst, 0); /* add magnitudes */
630
dst.sign = src1.sign; } /* set result sign */
632
WriteDstr (A3, &dst, op); /* store result */
633
if ((op & INLINE) == 0) /* if reg, clr reg */
634
R[0] = R[1] = R[2] = R[3] = 0;
640
A1LNT, A1ADR = source1 string descriptor
641
A2LNT, A2ADR = source2 string descriptor
642
A3LNT, A3ADR = destination string descriptor
645
NZV = set from result
648
Registers (MULP only):
653
dst = Dstr0; /* clear result */
654
if (ReadDstr (A1, &src1, op) && ReadDstr (A2, &src2, op)) {
655
dst.sign = src1.sign ^ src2.sign; /* sign of result */
656
accum = Dstr0; /* clear accum */
657
NibbleRshift (&src1, 1, 0); /* shift out sign */
658
CreateTable (&src1, mptable); /* create *1, *2, ... */
659
for (i = 1; i < (DSTRLNT * 8); i++) { /* 31 iterations */
660
digit = (src2.val[i / 8] >> ((i % 8) * 4)) & 0xF;
661
if (digit > 0) /* add in digit*mpcnd */
662
AddDstr (&mptable[digit], &accum, &accum, 0);
663
nc = NibbleRshift (&accum, 1, 0); /* ac right 4 */
664
NibbleRshift (&dst, 1, nc); } /* result right 4 */
665
V = TestDstr (&accum) != 0; } /* if ovflo, set V */
666
else V = 0; /* result = 0 */
668
WriteDstr (A3, &dst, op); /* store result */
669
if ((op & INLINE) == 0) /* if reg, clr reg */
670
R[0] = R[1] = R[2] = R[3] = 0;
676
A1LNT, A1ADR = divisor string descriptor
677
A2LNT, A2ADR = dividend string descriptor
678
A3LNT, A3ADR = destination string descriptor
681
NZV = set from result
682
C = set if divide by zero
684
Registers (DIVP only):
689
ldivr = ReadDstr (A1, &src1, op); /* get divisor */
690
if (ldivr == 0) { /* divisor = 0? */
691
V = C = 1; /* set cc's */
693
ldivr = LntDstr (&src1, ldivr); /* get exact length */
694
ldivd = ReadDstr (A2, &src2, op); /* get dividend */
695
ldivd = LntDstr (&src2, ldivd); /* get exact length */
696
dst = Dstr0; /* clear dest */
697
NibbleRshift (&src1, 1, 0); /* right justify ops */
698
NibbleRshift (&src2, 1, 0);
699
if ((t = ldivd - ldivr) >= 0) { /* any divide to do? */
700
WordLshift (&src1, t / 8); /* align divr to divd */
701
NibbleLshift (&src1, t % 8, 0);
702
CreateTable (&src1, mptable); /* create *1, *2, ... */
703
for (i = 0; i <= t; i++) { /* divide loop */
704
for (digit = 9; digit > 0; digit--) { /* find digit */
705
if (CmpDstr (&src2, &mptable[digit]) >= 0) {
706
SubDstr (&mptable[digit], &src2, &src2);
707
dst.val[0] = dst.val[0] | digit;
708
break; } } /* end if, for */
709
NibbleLshift (&src2, 1, 0); /* shift dividend */
710
NibbleLshift (&dst, 1, 0); /* shift quotient */
711
} /* end divide loop */
712
dst.sign = src1.sign ^ src2.sign; /* calculate sign */
715
WriteDstr (A3, &dst, op); /* store result */
716
if ((op & INLINE) == 0) /* if reg, clr reg */
717
R[0] = R[1] = R[2] = R[3] = 0;
720
/* CMPN, CMPP, CMPNI, CMPPI
723
A1LNT, A1ADR = source1 string descriptor
724
A2LNT, A2ADR = source2 string descriptor
727
NZ = set from comparison
730
Registers (CMPN, CMPP only):
734
case 052: case 072: case 0152: case 0172:
735
ReadDstr (A1, &src1, op); /* get source1 */
736
ReadDstr (A2, &src2, op); /* get source2 */
738
if (src1.sign != src2.sign) N = src1.sign;
740
t = CmpDstr (&src1, &src2); /* compare strings */
742
else if (t == 0) Z = 1; }
743
if ((op & INLINE) == 0) /* if reg, clr reg */
744
R[0] = R[1] = R[2] = R[3] = 0;
747
/* ASHN, ASHP, ASHNI, ASHPI
750
A1LNT, A1ADR = source string descriptor
751
A2LNT, A2ADR = destination string descriptor
752
A3LNT<11:8> = rounding digit
753
A3LNT<7:0> = shift count
756
NZV = set from result
759
Registers (ASHN, ASHP only):
763
case 056: case 076: case 0156: case 0176:
764
ReadDstr (A1, &src1, op); /* get source */
765
V = C = 0; /* init cc's */
766
shift = GET_ASHLNT (A3LNT); /* get shift count */
767
if (shift & ASHSGN) { /* right shift? */
768
shift = (ASHLNT_M + 1 - shift); /* !shift! */
769
WordRshift (&src1, shift / 8); /* do word shifts */
770
NibbleRshift (&src1, shift % 8, 0); /* do nibble shifts */
771
t = GET_ASHRND (A3LNT); /* get rounding digit */
772
if ((t + (src1.val[0] & 0xF)) > 9) /* rounding needed? */
773
AddDstr (&src1, &Dstr1, &src1, 0); /* round */
774
src1.val[0] = src1.val[0] & ~0xF; /* clear sign */
775
} /* end right shift */
776
else if (shift) { /* left shift? */
777
if (WordLshift (&src1, shift / 8)) V = 1; /* do word shifts */
778
if (NibbleLshift (&src1, shift % 8, 0)) V = 1;
779
} /* end left shift */
780
WriteDstr (A2, &src1, op); /* store result */
781
if ((op & INLINE) == 0) /* if reg, clr reg */
782
R[0] = R[1] = R[4] = 0;
788
A1LNT, A1ADR = source string descriptor
789
A2LNT, A2ADR = destination string descriptor
792
NZV = set from result
795
Registers (CVTPN only):
800
ReadDstr (A1, &src1, PACKED); /* get source */
801
V = C = 0; /* init cc's */
802
WriteDstr (A2, &src1, NUMERIC); /* write dest */
803
if ((op & INLINE) == 0) R[0] = R[1] = 0; /* if reg, clr reg */
809
A1LNT, A1ADR = source string descriptor
810
A2LNT, A2ADR = destination string descriptor
813
NZV = set from result
816
Registers (CVTNP only):
821
ReadDstr (A1, &src1, NUMERIC); /* get source */
822
V = C = 0; /* init cc's */
823
WriteDstr (A2, &src1, PACKED); /* write dest */
824
if ((op & INLINE) == 0) R[0] = R[1] = 0; /* if reg, clr reg */
827
/* CVTNL, CVTPL, CVTNLI, CVTPLI
830
A1LNT, A1ADR = source string descriptor
831
A2LNT = destination address (inline only)
834
NZV = set from result
835
C = source < 0 and result != 0
837
Registers (CVTNL, CVTPL only):
842
case 053: case 073: case 0153: case 0173:
843
ReadDstr (A1, &src1, op); /* get source */
844
V = result = 0; /* clear V, result */
845
for (i = (DSTRLNT * 8) - 1; i > 0; i--) { /* loop thru digits */
846
digit = (src1.val[i / 8] >> ((i % 8) * 4)) & 0xF;
847
if (digit || result || V) { /* skip initial 0's */
848
if (result >= MAXDVAL) V = 1;
849
result = (result * 10) + digit;
850
if (result < digit) V = 1; } /* end if */
852
if (src1.sign) result = (~result + 1) & 0xFFFFFFFF;
853
N = GET_SIGN_L (result);
855
V = V | (N ^ src1.sign); /* overflow if +2**31 */
856
C = src1.sign && (Z == 0); /* set C based on std */
857
if (op & INLINE) { /* inline? */
858
WriteW (result & 0177777, A2LNT | dsenable);
859
WriteW ((result >> 16) & 0177777,
860
((A2LNT + 2) & 0177777) | dsenable); }
863
R[2] = (result >> 16) & 0177777;
864
R[3] = result & 0177777; }
867
/* CVTLN, CVTLP, CVTLNI, CVTLPI
870
A1LNT, A1ADR = destination string descriptor
871
A2LNT, A2ADR = source long (CVTLNI, CVTLPI) - VAX format
872
R2:R3 = source long (CVTLN, CVTLP) - EIS format
875
NZV = set from result
878
Registers (CVTLN, CVTLP only)
883
result = (R[2] << 16) | R[3]; /* op in EIS format */
884
R[2] = R[3] = 0; /* clear registers */
885
goto CVTLx; /* join common code */
886
case 0157: case 0177:
887
result = (A2ADR << 16) | A2LNT; /* op in VAX format */
889
dst = Dstr0; /* clear result */
890
if (dst.sign = GET_SIGN_L (result)) result = (~result + 1) & 0xFFFFFFFF;
891
for (i = 1; (i < (DSTRLNT * 8)) && result; i++) {
893
result = result / 10;
894
dst.val[i / 8] = dst.val[i / 8] | (digit << ((i % 8) * 4)); }
896
WriteDstr (A1, &dst, op); /* write result */
904
/* Get decimal string
907
dscr = decimal string descriptor
908
src = decimal string structure
909
flag = numeric/packed flag
911
The routine returns the length in int32's of the non-zero part of
914
This routine plays fast and loose with operand checking, as did the
915
original 11/23 microcode (half of which I wrote). In particular,
917
- If the flag specifies packed, the type is not checked at all.
918
The sign of an unsigned string is assumed to be 0xF (an
920
- If the flag specifies numeric, packed types will be treated
922
- For separate, only the '-' sign is checked, not the '+'.
924
However, to simplify the code elsewhere, digits are range checked,
925
and bad digits are replaced with 0's.
928
int32 ReadDstr (int32 *dscr, DSTR *src, int32 flag)
930
int32 c, i, end, lnt, type, t;
932
*src = Dstr0; /* clear result */
933
type = GET_DTYP (dscr[0]); /* get type */
934
lnt = GET_DLNT (dscr[0]); /* get string length */
935
if (flag & PACKED) { /* packed? */
936
end = lnt / 2; /* last byte */
937
for (i = 0; i <= end; i++) { /* loop thru string */
938
c = ReadB (((dscr[1] + end - i) & 0177777) | dsenable);
939
if (i == 0) t = c & 0xF; /* save sign */
940
if ((i == end) && ((lnt & 1) == 0)) c = c & 0xF;
941
if (c >= 0xA0) c = c & 0xF; /* check hi digit */
942
if ((c & 0xF) >= 0xA) c = c & 0xF0; /* check lo digit */
943
src->val[i / 4] = src->val[i / 4] | (c << ((i % 4) * 8));
945
if ((t == 0xB) || (t == 0xD)) src->sign = 1; /* if -, set sign */
946
src->val[0] = src->val[0] & ~0xF; /* clear sign */
949
if (type >= TS) src->sign = (ReadB ((((type == TS)?
950
dscr[1] + lnt: dscr[1] - 1) & 0177777) | dsenable) == '-');
951
for (i = 1; i <= lnt; i++) { /* loop thru string */
952
c = ReadB (((dscr[1] + lnt - i) & 0177777) | dsenable);
953
if ((i == 1) && (type == XZ) && ((c & 0xF0) == 0x70))
954
src->sign = 1; /* signed zoned */
955
else if (((i == 1) && (type == TO)) ||
956
((i == lnt) && (type == LO))) {
957
c = overbin[c & 0177]; /* get sign and digit */
958
src->sign = c >> 7; } /* set sign */
959
c = c & 0xF; /* get digit */
960
if (c > 9) c = 0; /* range check */
961
src->val[i / 8] = src->val[i / 8] | (c << ((i % 8) * 4));
964
return TestDstr (src); /* clean -0 */
967
/* Store decimal string
970
dsrc = decimal string descriptor
971
src = decimal string structure
972
flag = numeric/packed flag
973
PSW.NZ are also set to their proper values
974
PSW.V will be set on overflow; it must be initialized elsewhere
975
(to allow for external overflow calculations)
977
The rules for the stored sign and the PSW sign are:
978
- Stored sign is negative if input is negative, string type
979
is signed, and the result is non-zero or there was overflow
980
- PSW sign is negative if input is negative, string type is
981
signed, and the result is non-zero
982
Thus, the stored sign and the PSW sign will differ in one case:
983
a negative zero generated by overflow is stored with a negative
984
sign, but PSW.N is clear
987
void WriteDstr (int32 *dscr, DSTR *dst, int32 flag)
989
int32 c, i, limit, end, type, lnt;
991
static uint32 masktab[8] = {
992
0xFFFFFFF0, 0xFFFFFF00, 0xFFFFF000, 0xFFFF0000,
993
0xFFF00000, 0xFF000000, 0xF0000000, 0x00000000 };
994
static int32 unsignedtab[8] = { 0, 1, 0, 0, 0, 0, 0, 1 };
996
type = GET_DTYP (dscr[0]); /* get type */
997
lnt = GET_DLNT (dscr[0]); /* get string length */
998
mask = 0; /* can't ovflo */
999
Z = 1; /* assume all 0's */
1000
limit = lnt / 8; /* limit for test */
1001
for (i = 0; i < DSTRLNT; i++) { /* loop thru value */
1002
if (i == limit) mask = masktab[lnt % 8]; /* at limit, get mask */
1003
else if (i > limit) mask = 0xFFFFFFFF; /* beyond, all ovflo */
1004
if (dst->val[i] & mask) V = 1; /* test for ovflo */
1005
if (dst->val[i] = dst->val[i] & ~mask) Z = 0; } /* test nz */
1006
dst->sign = dst->sign & ~unsignedtab[type] & ~(Z & ~V);
1007
N = dst->sign & ~Z; /* N = sign, if ~zero */
1009
if (flag & PACKED) { /* packed? */
1010
end = lnt / 2; /* end of string */
1011
if (type == UP) dst->val[0] = dst->val[0] | 0xF;
1012
else dst->val[0] = dst->val[0] | 0xC | dst->sign;
1013
for (i = 0; i <= end; i++) { /* store string */
1014
c = (dst->val[i / 4] >> ((i % 4) * 8)) & 0xFF;
1015
WriteB (c, ((dscr[1] + end - i) & 0177777));
1019
if (type >= TS) WriteB (dst->sign? '-': '+', (((type == TS)?
1020
dscr[1] + lnt: dscr[1] - 1) & 0177777) | dsenable);
1021
for (i = 1; i <= lnt; i++) { /* store string */
1022
c = (dst->val[i / 8] >> ((i % 8) * 4)) & 0xF; /* get digit */
1023
if ((i == 1) && (type == XZ) && dst->sign)
1024
c = c | 0x70; /* signed zoned */
1025
else if (((i == 1) && (type == TO)) ||
1026
((i == lnt) && (type == LO)))
1027
c = binover[dst->sign][c]; /* get sign and digit */
1028
else c = c | 0x30; /* default */
1029
WriteB (c, ((dscr[1] + lnt - i) & 0177777));
1035
/* Add decimal string magnitudes
1038
s1 = source1 decimal string
1039
s2 = source2 decimal string
1040
ds = destination decimal string
1042
Output = 1 if carry, 0 if no carry
1044
This algorithm courtesy Anton Chernoff, circa 1992 or even earlier
1046
We trace the history of a pair of adjacent digits to see how the
1047
carry is fixed; each parenthesized item is a 4b digit.
1049
Assume we are adding:
1056
Note that the low bit of each digit is the same as the low bit of
1057
the sum of the digits, ignoring the cary, since the low bit of the
1058
sum is the xor of the bits.
1060
Now compute I+J+66 to get decimal addition with carry forced left
1062
(a+x+6+carry mod 16)(b+y+6 mod 16) SUM
1064
Note that if there was a carry from b+y+6, then the low bit of the
1065
left digit is different from the expected low bit from the xor.
1066
If we xor this SUM into TMP, then the low bit of each digit is 1
1067
if there was a carry, and 0 if not. We need to subtract 6 from each
1068
digit that did not have a carry, so take ~(SUM ^ TMP) & 0x11, shift
1069
it right 4 to the digits that are affected, and subtract 6*adjustment
1070
(actually, shift it right 3 and subtract 3*adjustment).
1073
int32 AddDstr (DSTR *s1, DSTR *s2, DSTR *ds, int32 cy)
1076
unsigned int32 sm1, sm2, tm1, tm2, tm3, tm4;
1078
for (i = 0; i < DSTRLNT; i++) { /* loop low to high */
1079
tm1 = s1->val[i] ^ (s2->val[i] + cy); /* xor operands */
1080
sm1 = s1->val[i] + (s2->val[i] + cy); /* sum operands */
1081
sm2 = sm1 + 0x66666666; /* force carry out */
1082
cy = ((sm1 < s1->val[i]) || (sm2 < sm1)); /* check for overflow */
1083
tm2 = tm1 ^ sm2; /* get carry flags */
1084
tm3 = (tm2 >> 3) | (cy << 29); /* compute adjustment */
1085
tm4 = 0x22222222 & ~tm3; /* clear where carry */
1086
ds->val[i] = sm2 - (3 * tm4); } /* final result */
1090
/* Subtract decimal string magnitudes
1093
s1 = source1 decimal string
1094
s2 = source2 decimal string
1095
ds = destination decimal string
1096
Outputs: s2 - s1 in ds
1098
Note: the routine assumes that s1 <= s2
1102
void SubDstr (DSTR *s1, DSTR *s2, DSTR *ds)
1107
for (i = 0; i < DSTRLNT; i++) compl.val[i] = 0x99999999 - s1->val[i];
1108
AddDstr (&compl, s2, ds, 1); /* s1 + ~s2 + 1 */
1112
/* Compare decimal string magnitudes
1115
s1 = source1 decimal string
1116
s2 = source2 decimal string
1117
Output = 1 if >, 0 if =, -1 if <
1120
int32 CmpDstr (DSTR *s1, DSTR *s2)
1124
for (i = DSTRMAX; i >=0; i--) {
1125
if (s1->val[i] > s2->val[i]) return 1;
1126
if (s1->val[i] < s2->val[i]) return -1; }
1130
/* Test decimal string for zero
1133
dsrc = decimal string structure
1134
Returns the non-zero length of the string, in int32 units
1135
If the string is zero, the sign is cleared
1138
int32 TestDstr (DSTR *dsrc)
1142
for (i = DSTRMAX; i >= 0; i--) if (dsrc->val[i]) return (i + 1);
1147
/* Get exact length of decimal string
1150
dsrc = decimal string structure
1151
nz = result from TestDstr
1154
int32 LntDstr (DSTR *dsrc, int32 nz)
1158
for (i = 7; i > 0; i--) {
1159
if ((dsrc->val[nz - 1] >> (i * 4)) & 0xF) break; }
1160
return ((nz - 1) * 8) + i;
1163
/* Create table of multiples
1166
dsrc = base decimal string structure
1167
mtable[10] = array of decimal string structures
1169
Note that dsrc has a high order zero nibble; this
1170
guarantees that the largest multiple won't overflow
1171
Also note that mtable[0] is not filled in
1174
void CreateTable (DSTR *dsrc, DSTR mtable[10])
1179
for (i = 2; i < 10; i++) AddDstr (&mtable[1], &mtable[i-1], &mtable[i], 0);
1186
dsrc = decimal string structure
1190
void WordRshift (DSTR *dsrc, int32 sc)
1195
for (i = 0; i < DSTRLNT; i++) {
1196
if ((i + sc) < DSTRLNT) dsrc->val[i] = dsrc->val[i + sc];
1197
else dsrc->val[i] = 0; } }
1204
dsrc = decimal string structure
1208
int32 WordLshift (DSTR *dsrc, int32 sc)
1214
for (i = DSTRMAX; i >= 0; i--) {
1215
if (i > (DSTRMAX - sc)) c = c | dsrc->val[i];
1216
if ((i - sc) >= 0) dsrc->val[i] = dsrc->val[i - sc];
1217
else dsrc->val[i] = 0; } }
1221
/* Nibble shift decimal string right
1224
dsrc = decimal string structure
1229
unsigned int32 NibbleRshift (DSTR *dsrc, int32 sc, unsigned int32 cin)
1235
for (i = DSTRMAX; i >= 0; i--) {
1237
dsrc->val[i] = ((dsrc->val[i] >> s) |
1238
(cin << rs)) & 0xFFFFFFFF;
1244
/* Nibble shift decimal string left
1247
dsrc = decimal string structure
1252
unsigned int32 NibbleLshift (DSTR *dsrc, int32 sc, unsigned int32 cin)
1258
for (i = 0; i < DSTRLNT; i++) {
1260
dsrc->val[i] = ((dsrc->val[i] << s) |
1261
(cin >> rs)) & 0xFFFFFFFF;