1
/* hp2100_cpu3.c: HP 2100/1000 FFP/DBI instructions
3
Copyright (c) 2005-2006, J. David Bryan
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
THE AUTHOR 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 the author 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 the author.
26
CPU3 Fast FORTRAN and Double Integer instructions
28
16-Oct-06 JDB Calls FPP for extended-precision math
29
12-Oct-06 JDB Altered DBLE, DDINT for F-Series FFP compatibility
30
26-Sep-06 JDB Moved from hp2100_cpu1.c to simplify extensions
31
09-Aug-06 JDB Added double-integer instruction set
32
18-Feb-05 JDB Add 2100/21MX Fast FORTRAN Processor instructions
35
- HP 1000 M/E/F-Series Computers Technical Reference Handbook
37
- HP 1000 M/E/F-Series Computers Engineering and Reference Documentation
38
(92851-90001, Mar-1981)
39
- Macro/1000 Reference Manual (92059-90001, Dec-1992)
41
Additional references are listed with the associated firmware
42
implementations, as are the HP option model numbers pertaining to the
46
#include "hp2100_defs.h"
47
#include "hp2100_cpu.h"
48
#include "hp2100_cpu1.h"
50
#if defined (HAVE_INT64) /* int64 support available */
51
#include "hp2100_fp1.h"
52
#else /* int64 support unavailable */
53
#include "hp2100_fp.h"
54
#endif /* end of int64 support */
57
t_stat cpu_ffp (uint32 IR, uint32 intrq); /* Fast FORTRAN Processor */
58
t_stat cpu_dbi (uint32 IR, uint32 intrq); /* Double-Integer instructions */
61
/* Fast FORTRAN Processor.
63
The Fast FORTRAN Processor (FFP) is a set of FORTRAN language accelerators
64
and extended-precision (three-word) floating point routines. Although the
65
FFP is an option for the 2100 and later CPUs, each implements the FFP in a
66
slightly different form.
68
Option implementation by CPU was as follows:
70
2114 2115 2116 2100 1000-M 1000-E 1000-F
71
------ ------ ------ ------ ------ ------ ------
72
N/A N/A N/A 12907A 12977B 13306B std
74
The instruction codes are mapped to routines as follows:
76
Instr. 2100 1000-M 1000-E 1000-F Instr. 2100 1000-M 1000-E 1000-F
77
------ ------ ------ ------ ------ ------ ------ ------ ------ ------
78
105200 -- [nop] [nop] [test] 105220 .XFER .XFER .XFER .XFER
79
105201 DBLE DBLE DBLE DBLE 105221 .GOTO .GOTO .GOTO .GOTO
80
105202 SNGL SNGL SNGL SNGL 105222 ..MAP ..MAP ..MAP ..MAP
81
105203 .XMPY .XMPY .XMPY .DNG 105223 .ENTR .ENTR .ENTR .ENTR
82
105204 .XDIV .XDIV .XDIV .DCO 105224 .ENTP .ENTP .ENTP .ENTP
83
105205 .DFER .DFER .DFER .DFER 105225 -- .PWR2 .PWR2 .PWR2
84
105206 -- .XPAK .XPAK .XPAK 105226 -- .FLUN .FLUN .FLUN
85
105207 -- XADD XADD .BLE 105227 $SETP $SETP $SETP $SETP
87
105210 -- XSUB XSUB .DIN 105230 -- .PACK .PACK .PACK
88
105211 -- XMPY XMPY .DDE 105231 -- -- .CFER .CFER
89
105212 -- XDIV XDIV .DIS 105232 -- -- -- ..FCM
90
105213 .XADD .XADD .XADD .DDS 105233 -- -- -- ..TCM
91
105214 .XSUB .XSUB .XSUB .NGL 105234 -- -- -- --
92
105215 -- .XCOM .XCOM .XCOM 105235 -- -- -- --
93
105216 -- ..DCM ..DCM ..DCM 105236 -- -- -- --
94
105217 -- DDINT DDINT DDINT 105237 -- -- -- --
96
The F-Series maps different instructions to several of the standard FFP
97
opcodes. We first look for these and dispatch them appropriately before
98
falling into the handler for the common instructions.
100
The math functions use the F-Series FPP for implementation. The FPP requires
101
that the host compiler support 64-bit integers. Therefore, if 64-bit
102
integers are not available, the math instructions of the FFP are disabled.
103
We allow this partial implementation as an aid in running systems generated
104
for the FFP. Most system programs did not use the math instructions, but
105
almost all use .ENTR. Supporting the latter even on systems that do not
106
support the former still allows such systems to boot.
110
1. The "$SETP" instruction is sometimes listed as ".SETP" in the
113
2. Extended-precision arithmetic routines (e.g., .XMPY) exist on the
114
1000-F, but they are assigned instruction codes in the single-precision
115
floating-point module range. They are replaced by several double
116
integer instructions, which we dispatch to the double integer handler.
118
3. The software implementation of ..MAP supports 1-, 2-, or 3-dimensional
119
arrays, designated by setting A = -1, 0, and +1, respectively. The
120
firmware implementation supports only 2- and 3-dimensional access.
122
4. The documentation for ..MAP for the 2100 FFP shows A = 0 or -1 for two
123
or three dimensions, respectively, but the 1000 FFP shows A = 0 or +1.
124
The firmware actually only checks the LSB of A.
126
5. The .DFER and .XFER implementations for the 2100 FFP return X+4 and Y+4
127
in the A and B registers, whereas the 1000 FFP returns X+3 and Y+3.
129
6. The .XFER implementation for the 2100 FFP returns to P+2, whereas the
130
1000 implementation returns to P+1.
132
7. The firmware implementations of DBLE, .BLE, and DDINT clear the overflow
133
flag. The software implementations do not change overflow.
135
8. The M/E-Series FFP arithmetic instructions (.XADD, etc.) return negative
136
infinity on negative overflow and positive infinity on positive
137
overflow. The equivalent F-Series instructions return positive infinity
140
Additional references:
141
- DOS/RTE Relocatable Library Reference Manual (24998-90001, Oct-1981)
142
- Implementing the HP 2100 Fast FORTRAN Processor (12907-90010, Nov-1974)
145
static const OP_PAT op_ffp_f[32] = { /* patterns for F-Series only */
146
OP_N, OP_AAF, OP_AX, OP_N, /* [tst] DBLE SNGL .DNG */
147
OP_N, OP_AA, OP_A, OP_AAF, /* .DCO .DFER .XPAK .BLE */
148
OP_N, OP_N, OP_N, OP_N, /* .DIN .DDE .DIS .DDS */
149
OP_AT, OP_A, OP_A, OP_AAX, /* .NGL .XCOM ..DCM DDINT */
150
OP_N, OP_AK, OP_KKKK, OP_A, /* .XFER .GOTO ..MAP .ENTR */
151
OP_A, OP_RK, OP_R, OP_K, /* .ENTP .PWR2 .FLUN $SETP */
152
OP_RC, OP_AA, OP_R, OP_A, /* .PACK .CFER ..FCM ..TCM */
153
OP_N, OP_N, OP_N, OP_N /* --- --- --- --- */
156
static const OP_PAT op_ffp_e[32] = { /* patterns for 2100/M/E-Series */
157
OP_N, OP_AAF, OP_AX, OP_AXX, /* [nop] DBLE SNGL .XMPY */
158
OP_AXX, OP_AA, OP_A, OP_AAXX, /* .XDIV .DFER .XPAK XADD */
159
OP_AAXX, OP_AAXX, OP_AAXX, OP_AXX, /* XSUB XMPY XDIV .XADD */
160
OP_AXX, OP_A, OP_A, OP_AAX, /* .XSUB .XCOM ..DCM DDINT */
161
OP_N, OP_AK, OP_KKKK, OP_A, /* .XFER .GOTO ..MAP .ENTR */
162
OP_A, OP_RK, OP_R, OP_K, /* .ENTP .PWR2 .FLUN $SETP */
163
OP_RC, OP_AA, OP_N, OP_N, /* .PACK .CFER --- --- */
164
OP_N, OP_N, OP_N, OP_N /* --- --- --- --- */
167
t_stat cpu_ffp (uint32 IR, uint32 intrq)
172
uint32 j, sa, sb, sc, da, dc, ra, MA;
174
t_stat reason = SCPE_OK;
176
#if defined (HAVE_INT64) /* int64 support available */
180
#endif /* end of int64 support */
182
if ((cpu_unit.flags & UNIT_FFP) == 0) /* FFP option installed? */
185
entry = IR & 037; /* mask to entry point */
187
if (UNIT_CPU_MODEL != UNIT_1000_F) { /* 2100/M/E-Series? */
188
if (op_ffp_e[entry] != OP_N)
189
if (reason = cpu_ops (op_ffp_e[entry], op, intrq)) /* get instruction operands */
193
#if defined (HAVE_INT64) /* int64 support available */
195
else { /* F-Series */
196
if (op_ffp_f[entry] != OP_N)
197
if (reason = cpu_ops (op_ffp_f[entry], op, intrq)) /* get instruction operands */
200
switch (entry) { /* decode IR<4:0> */
202
case 000: /* [tst] 105200 (OP_N) */
203
XR = 4; /* firmware revision */
204
SR = 0102077; /* test passed code */
205
AR = 0; /* test clears A/B */
207
PC = (PC + 1) & VAMASK; /* P+2 return for firmware w/DBI */
210
case 003: /* .DNG 105203 (OP_N) */
211
return cpu_dbi (0105323, intrq); /* remap to double int handler */
213
case 004: /* .DCO 105204 (OP_N) */
214
return cpu_dbi (0105324, intrq); /* remap to double int handler */
216
case 007: /* .BLE 105207 (OP_AAF) */
217
O = fp_cvt (&op[2], fp_f, fp_t); /* convert value and clear overflow */
218
WriteOp (op[1].word, op[2], fp_t); /* write double-precision value */
221
case 010: /* .DIN 105210 (OP_N) */
222
return cpu_dbi (0105330, intrq); /* remap to double int handler */
224
case 011: /* .DDE 105211 (OP_N) */
225
return cpu_dbi (0105331, intrq); /* remap to double int handler */
227
case 012: /* .DIS 105212 (OP_N) */
228
return cpu_dbi (0105332, intrq); /* remap to double int handler */
230
case 013: /* .DDS 105213 (OP_N) */
231
return cpu_dbi (0105333, intrq); /* remap to double int handler */
233
case 014: /* .NGL 105214 (OP_AT) */
234
O = fp_cvt (&op[1], fp_t, fp_f); /* convert value */
235
AR = op[1].fpk[0]; /* move MSB to A */
236
BR = op[1].fpk[1]; /* move LSB to B */
239
case 032: /* ..FCM 105232 (OP_R) */
240
O = fp_pcom (&op[0], fp_f); /* complement value */
241
AR = op[0].fpk[0]; /* return result */
242
BR = op[0].fpk[1]; /* to A/B registers */
245
case 033: /* ..TCM 105233 (OP_A) */
246
fpop = ReadOp (op[0].word, fp_t); /* read 4-word value */
247
O = fp_pcom (&fpop, fp_t); /* complement it */
248
WriteOp (op[0].word, fpop, fp_t); /* write 4-word value */
250
} /* fall thru if not special to F */
253
#endif /* end of int64 support */
255
switch (entry) { /* decode IR<4:0> */
259
case 000: /* [nop] 105200 (OP_N) */
260
if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 M/E-series */
261
return stop_inst; /* trap if not */
264
#if defined (HAVE_INT64) /* int64 support available */
266
case 001: /* DBLE 105201 (OP_AAF) */
267
O = fp_cvt (&op[2], fp_f, fp_x); /* convert value and clear overflow */
268
WriteOp (op[1].word, op[2], fp_x); /* write extended-precision value */
271
case 002: /* SNGL 105202 (OP_AX) */
272
O = fp_cvt (&op[1], fp_x, fp_f); /* convert value */
273
AR = op[1].fpk[0]; /* move MSB to A */
274
BR = op[1].fpk[1]; /* move LSB to B */
277
case 003: /* .XMPY 105203 (OP_AXX) */
278
i = 0; /* params start at op[0] */
279
goto XMPY; /* process as XMPY */
281
case 004: /* .XDIV 105204 (OP_AXX) */
282
i = 0; /* params start at op[0] */
283
goto XDIV; /* process as XDIV */
285
#endif /* end of int64 support */
287
case 005: /* .DFER 105205 (OP_AA) */
288
BR = op[0].word; /* get destination address */
289
AR = op[1].word; /* get source address */
290
goto XFER; /* do transfer */
292
#if defined (HAVE_INT64) /* int64 support available */
294
case 006: /* .XPAK 105206 (OP_A) */
295
if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */
296
return stop_inst; /* trap if not */
298
if (intrq) { /* interrupt pending? */
299
PC = err_PC; /* restart instruction */
303
fpop = ReadOp (op[0].word, fp_x); /* read unpacked */
304
O = fp_nrpack (&fpop, fpop, (int16) AR, fp_x); /* nrm/rnd/pack mantissa, exponent */
305
WriteOp (op[0].word, fpop, fp_x); /* write result */
308
case 007: /* XADD 105207 (OP_AAXX) */
309
i = 1; /* params start at op[1] */
310
XADD: /* enter here from .XADD */
311
if (intrq) { /* interrupt pending? */
312
PC = err_PC; /* restart instruction */
316
O = fp_exec (001, &fpop, op[i + 1], op[i + 2]); /* three-word add */
317
WriteOp (op[i].word, fpop, fp_x); /* write sum */
320
case 010: /* XSUB 105210 (OP_AAXX) */
321
i = 1; /* params start at op[1] */
322
XSUB: /* enter here from .XSUB */
323
if (intrq) { /* interrupt pending? */
324
PC = err_PC; /* restart instruction */
328
O = fp_exec (021, &fpop, op[i + 1], op[i + 2]); /* three-word subtract */
329
WriteOp (op[i].word, fpop, fp_x); /* write difference */
332
case 011: /* XMPY 105211 (OP_AAXX) */
333
i = 1; /* params start at op[1] */
334
XMPY: /* enter here from .XMPY */
335
if (intrq) { /* interrupt pending? */
336
PC = err_PC; /* restart instruction */
340
O = fp_exec (041, &fpop, op[i + 1], op[i + 2]); /* three-word multiply */
341
WriteOp (op[i].word, fpop, fp_x); /* write product */
344
case 012: /* XDIV 105212 (OP_AAXX) */
345
i = 1; /* params start at op[1] */
346
XDIV: /* enter here from .XDIV */
347
if (intrq) { /* interrupt pending? */
348
PC = err_PC; /* restart instruction */
352
O = fp_exec (061, &fpop, op[i + 1], op[i + 2]); /* three-word divide */
353
WriteOp (op[i].word, fpop, fp_x); /* write quotient */
356
case 013: /* .XADD 105213 (OP_AXX) */
357
i = 0; /* params start at op[0] */
358
goto XADD; /* process as XADD */
360
case 014: /* .XSUB 105214 (OP_AXX) */
361
i = 0; /* params start at op[0] */
362
goto XSUB; /* process as XSUB */
364
case 015: /* .XCOM 105215 (OP_A) */
365
if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */
366
return stop_inst; /* trap if not */
368
fpop = ReadOp (op[0].word, fp_x); /* read unpacked */
369
AR = fp_ucom (&fpop, fp_x); /* complement and rtn exp adj */
370
WriteOp (op[0].word, fpop, fp_x); /* write result */
373
case 016: /* ..DCM 105216 (OP_A) */
374
if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */
375
return stop_inst; /* trap if not */
377
if (intrq) { /* interrupt pending? */
378
PC = err_PC; /* restart instruction */
382
fpop = ReadOp (op[0].word, fp_x); /* read operand */
383
O = fp_pcom (&fpop, fp_x); /* complement (can't ovf neg) */
384
WriteOp (op[0].word, fpop, fp_x); /* write result */
387
case 017: /* DDINT 105217 (OP_AAX) */
388
if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */
389
return stop_inst; /* trap if not */
391
if (intrq) { /* interrupt pending? */
392
PC = err_PC; /* restart instruction */
396
O = fp_trun (&fpop, op[2], fp_x); /* truncate operand (can't ovf) */
397
WriteOp (op[1].word, fpop, fp_x); /* write result */
400
#endif /* end of int64 support */
404
case 020: /* .XFER 105220 (OP_N) */
405
if (UNIT_CPU_TYPE == UNIT_TYPE_2100)
406
PC = (PC + 1) & VAMASK; /* 2100 .XFER returns to P+2 */
407
XFER: /* enter here from .DFER */
408
sc = 3; /* set count for 3-wd xfer */
409
goto CFER; /* do transfer */
411
case 021: /* .GOTO 105221 (OP_AK) */
412
if ((int16) op[1].word < 1) /* index < 1? */
413
op[1].word = 1; /* reset min */
415
sa = PC + op[1].word - 1; /* point to jump target */
416
if (sa >= op[0].word) /* must be <= last target */
419
da = ReadW (sa); /* get jump target */
420
if (reason = resolve (da, &MA, intrq)) { /* resolve indirects */
421
PC = err_PC; /* irq restarts instruction */
425
mp_dms_jmp (MA); /* validate jump addr */
426
PCQ_ENTRY; /* record last PC */
428
BR = op[0].word; /* (for 2100 FFP compat) */
431
case 022: /* ..MAP 105222 (OP_KKKK) */
432
op[1].word = op[1].word - 1; /* decrement 1st subscr */
434
if ((AR & 1) == 0) /* 2-dim access? */
435
op[1].word = op[1].word + /* compute element offset */
436
(op[2].word - 1) * op[3].word;
437
else { /* 3-dim access */
438
if (reason = cpu_ops (OP_KK, op2, intrq)) { /* get 1st, 2nd ranges */
439
PC = err_PC; /* irq restarts instruction */
442
op[1].word = op[1].word + /* offset */
443
((op[3].word - 1) * op2[1].word +
444
op[2].word - 1) * op2[0].word;
447
AR = (op[0].word + op[1].word * BR) & DMASK; /* return element address */
450
case 023: /* .ENTR 105223 (OP_A) */
451
MA = PC - 3; /* get addr of entry point */
452
ENTR: /* enter here from .ENTP */
453
da = op[0].word; /* get addr of 1st formal */
454
dc = MA - da; /* get count of formals */
455
sa = ReadW (MA); /* get addr of return point */
456
ra = ReadW (sa++); /* get rtn, ptr to 1st actual */
457
WriteW (MA, ra); /* stuff rtn into caller's ent */
458
sc = ra - sa; /* get count of actuals */
459
if (sc > dc) /* use min (actuals, formals) */
462
for (j = 0; j < sc; j++) {
463
MA = ReadW (sa++); /* get addr of actual */
464
if (reason = resolve (MA, &MA, intrq)) { /* resolve indirect */
465
PC = err_PC; /* irq restarts instruction */
468
WriteW (da++, MA); /* put addr into formal */
471
AR = ra; /* return address */
472
BR = da; /* addr of 1st unused formal */
475
case 024: /* .ENTP 105224 (OP_A) */
476
MA = PC - 5; /* get addr of entry point */
479
case 025: /* .PWR2 105225 (OP_RK) */
480
if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */
481
return stop_inst; /* trap if not */
483
fp_unpack (&fpop, &expon, op[0], fp_f); /* unpack value */
484
expon = expon + (int16) (op[1].word); /* multiply by 2**n */
485
fp_pack (&fpop, fpop, expon, fp_f); /* repack value */
486
AR = fpop.fpk[0]; /* return result */
487
BR = fpop.fpk[1]; /* to A/B registers */
490
case 026: /* .FLUN 105226 (OP_R) */
491
if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */
492
return stop_inst; /* trap if not */
494
fp_unpack (&fpop, &expon, op[0], fp_f); /* unpack value */
495
AR = (int16) expon; /* return expon to A */
496
BR = fpop.fpk[1]; /* and low mant to B */
499
case 027: /* $SETP 105227 (OP_K) */
500
j = sa = AR; /* save initial value */
501
sb = BR; /* save initial address */
502
AR = 0; /* AR will return = 0 */
503
BR = BR & VAMASK; /* addr must be direct */
506
WriteW (BR, j); /* write value to address */
507
j = (j + 1) & DMASK; /* incr value */
508
BR = (BR + 1) & VAMASK; /* incr address */
509
op[0].word = op[0].word - 1; /* decr count */
510
if (op[0].word && intrq) { /* more and intr? */
511
AR = sa; /* restore A */
512
BR = sb; /* restore B */
513
PC = err_PC; /* restart instruction */
517
while (op[0].word != 0); /* loop until count exhausted */
520
case 030: /* .PACK 105230 (OP_RC) */
521
if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */
522
return stop_inst; /* trap if not */
524
O = fp_nrpack (&fpop, op[0], /* nrm/rnd/pack value */
525
(int16) (op[1].word), fp_f);
526
AR = fpop.fpk[0]; /* return result */
527
BR = fpop.fpk[1]; /* to A/B registers */
530
case 031: /* .CFER 105231 (OP_AA) */
531
if ((UNIT_CPU_MODEL != UNIT_1000_E) && /* must be 1000 E-series */
532
(UNIT_CPU_MODEL != UNIT_1000_F)) /* or 1000 F-series */
533
return stop_inst; /* trap if not */
535
BR = op[0].word; /* get destination address */
536
AR = op[1].word; /* get source address */
537
sc = 4; /* set for 4-wd xfer */
538
CFER: /* enter here from .XFER */
539
for (j = 0; j < sc; j++) { /* xfer loop */
540
WriteW (BR, ReadW (AR)); /* transfer word */
541
AR = (AR + 1) & VAMASK; /* bump source addr */
542
BR = (BR + 1) & VAMASK; /* bump destination addr */
545
E = 0; /* routine clears E */
547
if (UNIT_CPU_TYPE == UNIT_TYPE_2100) { /* 2100 (and .DFER/.XFER)? */
548
AR = (AR + 1) & VAMASK; /* 2100 FFP returns X+4, Y+4 */
549
BR = (BR + 1) & VAMASK;
553
default: /* others undefined */
561
/* Double-Integer Instructions.
563
The double-integer instructions were added to the HP instruction set at
564
revision 1920 of the 1000-F. They were immediately adopted in a number of HP
565
software products, most notably the RTE file management package (FMP)
566
routines. As these routines are used in nearly every RTE program, F-Series
567
programs were almost always a few hundred bytes smaller than their M- and
568
E-Series counterparts. This became significant as RTE continued to grow in
569
size, and some customer programs ran out of address space on E-Series
572
While HP never added double-integer instructions to the standard E-Series, a
573
product from the HP "specials group," HP 93585A, provided microcoded
574
replacements for the E-Series. This could provide just enough address-space
575
savings to allow programs to load in E-Series systems, in addition to
576
accelerating these common operations.
578
M-Series microcode was never offered by HP. However, it costs us nothing to
579
enable double-integer instructions for M-Series simulations. This has the
580
concomitant advantage that it allows RTE-6/VM to run under SIMH (for
581
simulation, we must SET CPU 1000-M, because RTE-6/VM looks for the OS and VM
582
microcode -- which we do not implement yet -- if it detects an E- or F-Series
585
Option implementation by CPU was as follows:
587
2114 2115 2116 2100 1000-M 1000-E 1000-F
588
------ ------ ------ ------ ------ ------ ------
589
N/A N/A N/A N/A N/A 93575A std
591
The routines are mapped to instruction codes as follows:
593
Instr. 1000-E 1000-F Description
594
------ ------ ------ -----------------------------------------
595
[test] 105320 -- [self test]
596
.DAD 105321 105014 Double integer add
597
.DMP 105322 105054 Double integer multiply
598
.DNG 105323 105203 Double integer negate
599
.DCO 105324 105204 Double integer compare
600
.DDI 105325 105074 Double integer divide
601
.DDIR 105326 105134 Double integer divide (reversed)
602
.DSB 105327 105034 Double integer subtract
603
.DIN 105330 105210 Double integer increment
604
.DDE 105331 105211 Double integer decrement
605
.DIS 105332 105212 Double integer increment and skip if zero
606
.DDS 105333 105213 Double integer decrement and skip if zero
607
.DSBR 105334 105114 Double integer subtraction (reversed)
609
On the F-Series, the double-integer instruction codes are split among the
610
floating-point processor and the Fast FORTRAN Processor ranges. They are
611
dispatched from those respective simulators for processing here.
615
1. The E-Series opcodes are listed in Appendix C of the Macro/1000 manual.
616
These should be the same opcodes as given in the 93585A manual listed
617
below, but no copy of the reference below has been located to confirm
618
the proper opcodes. This module should be corrected if needed when such
619
documentation is found.
621
2. The action of the self-test instruction (105320) is unknown. At the
622
moment, we take an unimplemented instruction trap for this. When
623
documentation explaining the action is located, it will be implemented.
625
3. The F-Series firmware executes .DMP and .DDI/.DDIR by floating the
626
32-bit double integer to a 48-bit extended-precision number, calling the
627
FPP to execute the extended-precision multiply/divide, and then fixing
628
the product to a 32-bit double integer. We simulate these directly with
629
64- or 32-bit integer arithmetic.
631
Additional references:
632
- 93575A Double Integer Instructions Installation and Reference Manual
636
static const OP_PAT op_dbi[16] = {
637
OP_N, OP_JD, OP_JD, OP_J, /* [test] .DAD .DMP .DNG */
638
OP_JD, OP_JD, OP_JD, OP_JD, /* .DCO .DDI .DDIR .DSB */
639
OP_J, OP_J, OP_A, OP_A, /* .DIN .DDE .DIS .DDS */
640
OP_JD, OP_N, OP_N, OP_N /* .DSBR --- --- --- */
643
t_stat cpu_dbi (uint32 IR, uint32 intrq)
648
t_stat reason = SCPE_OK;
650
if ((cpu_unit.flags & UNIT_DBI) == 0) /* DBI option installed? */
653
entry = IR & 017; /* mask to entry point */
655
if (op_dbi[entry] != OP_N)
656
if (reason = cpu_ops (op_dbi[entry], op, intrq)) /* get instruction operands */
659
switch (entry) { /* decode IR<3:0> */
661
case 000: /* [test] 105320 (OP_N) */
662
t = (AR << 16) | BR; /* set t for nop */
663
reason = stop_inst; /* function unknown; not impl. */
666
case 001: /* .DAD 105321 (OP_JD) */
667
t = op[0].dword + op[1].dword; /* add values */
668
E = E | (t < op[0].dword); /* carry if result smaller */
669
O = (((~op[0].dword ^ op[1].dword) & /* overflow if sign wrong */
670
(op[0].dword ^ t) & SIGN32) != 0);
673
case 002: /* .DMP 105322 (OP_JD) */
676
#if defined (HAVE_INT64) /* int64 support available */
680
t64 = (t_int64) op[0].dword * /* multiply values */
681
(t_int64) op[1].dword;
682
O = ((t64 < -(t_int64) 0x80000000) || /* overflow if out of range */
683
(t64 > (t_int64) 0x7FFFFFFF));
685
t = ~SIGN32; /* if overflow, rtn max pos */
687
t = (uint32) (t64 & DMASK32); /* else lower 32 bits of result */
689
#else /* int64 support unavailable */
691
uint32 sign, xu, yu, rh, rl;
693
sign = ((int32) op[0].dword < 0) ^ /* save sign of result */
694
((int32) op[1].dword < 0);
696
xu = (uint32) abs ((int32) op[0].dword); /* make operands pos */
697
yu = (uint32) abs ((int32) op[1].dword);
699
if ((xu & 0xFFFF0000) == 0 && /* 16 x 16 multiply? */
700
(yu & 0xFFFF0000) == 0) {
701
t = xu * yu; /* do it */
702
O = 0; /* can't overflow */
705
else if ((xu & 0xFFFF0000) != 0 && /* 32 x 32 multiply? */
706
(yu & 0xFFFF0000) != 0)
707
O = 1; /* always overflows */
709
else { /* 16 x 32 or 32 x 16 */
710
rl = (xu & 0xFFFF) * (yu & 0xFFFF); /* form 1st partial product */
712
if ((xu & 0xFFFF0000) == 0)
713
rh = xu * (yu >> 16) + (rl >> 16); /* 16 x 32 2nd partial */
715
rh = (xu >> 16) * yu + (rl >> 16); /* 32 x 16 2nd partial */
717
O = (rh > 0x7FFF + sign); /* check for out of range */
719
t = (rh << 16) | (rl & 0xFFFF); /* combine partials */
723
t = ~SIGN32; /* if overflow, rtn max pos */
725
t = ~t + 1; /* if result neg, 2s compl */
727
#endif /* end of int64 support */
732
case 003: /* .DNG 105323 (OP_J) */
733
t = ~op[0].dword + 1; /* negate value */
734
O = (op[0].dword == SIGN32); /* overflow if max neg */
735
if (op[0].dword == 0) /* borrow if result zero */
739
case 004: /* .DCO 105324 (OP_JD) */
740
t = op[0].dword; /* copy for later store */
741
if ((int32) op[0].dword < (int32) op[1].dword)
742
PC = (PC + 1) & VAMASK; /* < rtns to P+2 */
743
else if ((int32) op[0].dword > (int32) op[1].dword)
744
PC = (PC + 2) & VAMASK; /* > rtns to P+3 */
745
break; /* = rtns to P+1 */
747
case 005: /* .DDI 105325 (OP_JD) */
749
O = ((op[1].dword == 0) || /* overflow if div 0 */
750
((op[0].dword == SIGN32) && /* or max neg div -1 */
751
((int32) op[1].dword == -1)));
753
t = ~SIGN32; /* rtn max pos for ovf */
755
t = op[0].dword / op[1].dword; /* else return quotient */
758
case 006: /* .DDIR 105326 (OP_JD) */
759
t = op[0].dword; /* swap operands */
760
op[0].dword = op[1].dword;
762
goto DDI; /* continue at .DDI */
764
case 007: /* .DSB 105327 (OP_JD) */
766
t = op[0].dword - op[1].dword; /* subtract values */
767
E = E | (op[0].dword < op[1].dword); /* borrow if minu < subtr */
768
O = (((op[0].dword ^ op[1].dword) & /* overflow if sign wrong */
769
(op[0].dword ^ t) & SIGN32) != 0);
772
case 010: /* .DIN 105330 (OP_J) */
773
t = op[0].dword + 1; /* increment value */
774
O = (t == SIGN32); /* overflow if sign flipped */
776
E = 1; /* carry if result zero */
779
case 011: /* .DDE 105331 (OP_J) */
780
t = op[0].dword - 1; /* decrement value */
781
O = (t == ~SIGN32); /* overflow if sign flipped */
783
E = 1; /* borrow if result -1 */
786
case 012: /* .DIS 105332 (OP_A) */
787
din = ReadOp (op[0].word, in_d); /* get value */
788
t = din.dword = din.dword + 1; /* increment value */
789
WriteOp (op[0].word, din, in_d); /* store it back */
791
PC = (PC + 1) & VAMASK; /* skip if result zero */
794
case 013: /* .DDS 105333 (OP_A) */
795
din = ReadOp (op[0].word, in_d); /* get value */
796
t = din.dword = din.dword - 1; /* decrement value */
797
WriteOp (op[0].word, din, in_d); /* write it back */
799
PC = (PC + 1) & VAMASK; /* skip if result zero */
802
case 014: /* .DSBR 105334 (OP_JD) */
803
t = op[0].dword; /* swap operands */
804
op[0].dword = op[1].dword;
806
goto DSB; /* continue at .DSB */
808
default: /* others undefined */
809
t = (AR << 16) | BR; /* set t for nop */
813
if (reason == SCPE_OK) { /* if return OK */
814
AR = (t >> 16) & DMASK; /* break result */
815
BR = t & DMASK; /* into A and B */