1
/* i1620_cpu.c: IBM 1620 CPU simulator
3
Copyright (c) 2002-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 CPU module incorporates code and comments from the 1620 simulator by
27
Geoff Kuenning, with his permission.
29
26-Mar-04 RMS Fixed warnings with -std=c99
30
02-Nov-03 RMS Fixed bug in branch digit (found by Dave Babcock)
31
21-Aug-03 RMS Fixed bug in immediate index add (found by Michael Short)
32
25-Apr-03 RMS Changed t_addr to uint32 throughout
33
18-Oct-02 RMS Fixed bugs in invalid result testing (found by Hans Pufal)
35
The simulated register state for the IBM 1620 is:
39
IR1 [PC] program counter
40
IR2 instruction register 2 (subroutine return address)
43
PR1 manual save address
46
Additional internal registers OR3, PR2, and PR3 are not simulated.
48
The IBM 1620 is a fixed instruction length, variable data length, decimal
49
data system. Memory consists of 20000 - 60000 BCD digits, each containing
50
four bits of data and a flag. There are no general registers; all
51
instructions are memory to memory.
53
The 1620 uses a fixed, 12 digit instruction format:
60
ppppp = P (usually destination) address
61
qqqqq = Q (usually source) address
63
Immediate instructions use the qqqqq field as the second operand.
65
The 1620 Model 1 uses table lookups for add and multiply; for that reason,
66
it was nicknamed CADET (Can't Add, Doesn't Even Try). The Model 2 does
67
adds in hardware and uses the add table memory for index registers.
70
/* This routine is the instruction decode routine for the IBM 1620.
71
It is called from the simulator control program to execute
72
instructions in simulated memory, starting at the simulated PC.
73
It runs until 'reason' is set non-zero.
77
1. Reasons to stop. The simulator can be stopped by:
80
breakpoint encountered
81
illegal addresses or instruction formats
82
I/O error in I/O simulator
84
2. Interrupts. The 1620 has no interrupt structure.
86
3. Non-existent memory. On the 1620, all memory references
87
are modulo the memory size.
89
4. Adding I/O devices. These modules must be modified:
91
i1620_cpu.c add iodisp table entry
92
i1620_sys.c add sim_devices table entry
95
#include "i1620_defs.h"
97
#define PCQ_SIZE 64 /* must be 2**n */
98
#define PCQ_MASK (PCQ_SIZE - 1)
99
#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = saved_PC
101
uint8 M[MAXMEMSIZE] = { 0 }; /* main memory */
102
uint32 saved_PC = 0; /* saved PC */
103
uint32 IR2 = 1; /* inst reg 2 */
104
uint32 PAR = 0; /* P address */
105
uint32 QAR = 0; /* Q address */
106
uint32 PR1 = 1; /* proc reg 1 */
107
uint32 iae = 1; /* ind addr enb */
108
uint32 idxe = 0; /* index enable */
109
uint32 idxb = 0; /* index band */
110
uint32 io_stop = 1; /* I/O stop */
111
uint32 ar_stop = 1; /* arith stop */
112
int32 ind_max = 16; /* iadr nest limit */
113
uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */
114
int32 pcq_p = 0; /* PC queue ptr */
115
REG *pcq_r = NULL; /* PC queue reg ptr */
116
uint8 ind[NUM_IND] = { 0 }; /* indicators */
118
extern int32 sim_int_char;
119
extern int32 sim_interval;
120
extern int32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */
121
extern FILE *sim_log;
123
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
124
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
125
t_stat cpu_reset (DEVICE *dptr);
126
t_stat cpu_set_opt1 (UNIT *uptr, int32 val, char *cptr, void *desc);
127
t_stat cpu_set_opt2 (UNIT *uptr, int32 val, char *cptr, void *desc);
128
t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc);
129
t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
130
t_stat cpu_set_save (UNIT *uptr, int32 val, char *cptr, void *desc);
131
t_stat cpu_set_table (UNIT *uptr, int32 val, char *cptr, void *desc);
133
int32 get_2d (uint32 ad);
134
t_stat get_addr (uint32 alast, int32 lnt, t_bool indexok, uint32 *addr);
135
t_stat cvt_addr (uint32 alast, int32 lnt, t_bool signok, int32 *val);
136
t_stat get_idx (uint32 aidx);
137
t_stat xmt_field (uint32 d, uint32 s, uint32 skp);
138
t_stat xmt_record (uint32 d, uint32 s, t_bool cpy);
139
t_stat xmt_index (uint32 d, uint32 s);
140
t_stat xmt_divd (uint32 d, uint32 s);
141
t_stat xmt_tns (uint32 d, uint32 s);
142
t_stat xmt_tnf (uint32 d, uint32 s);
143
t_stat add_field (uint32 d, uint32 s, t_bool sub, t_bool sto, uint32 skp, int32 *sta);
144
uint32 add_one_digit (uint32 dst, uint32 src, uint32 *cry);
145
t_stat mul_field (uint32 mpc, uint32 mpy);
146
t_stat mul_one_digit (uint32 mpyd, uint32 mpcp, uint32 prop, uint32 last);
147
t_stat div_field (uint32 dvd, uint32 dvr, int32 *ez);
148
t_stat div_one_digit (uint32 dvd, uint32 dvr, uint32 max, uint32 *quod, uint32 *quop);
149
t_stat oct_to_dec (uint32 tbl, uint32 s);
150
t_stat dec_to_oct (uint32 d, uint32 tbl, int32 *ez);
151
t_stat or_field (uint32 d, uint32 s);
152
t_stat and_field (uint32 d, uint32 s);
153
t_stat xor_field (uint32 d, uint32 s);
154
t_stat com_field (uint32 d, uint32 s);
157
extern t_stat tty (uint32 op, uint32 pa, uint32 f0, uint32 f1);
158
extern t_stat ptp (uint32 op, uint32 pa, uint32 f0, uint32 f1);
159
extern t_stat ptr (uint32 op, uint32 pa, uint32 f0, uint32 f1);
160
extern t_stat cdp (uint32 op, uint32 pa, uint32 f0, uint32 f1);
161
extern t_stat cdr (uint32 op, uint32 pa, uint32 f0, uint32 f1);
162
extern t_stat dp (uint32 op, uint32 pa, uint32 f0, uint32 f1);
163
extern t_stat lpt (uint32 op, uint32 pa, uint32 f0, uint32 f1);
164
extern t_stat btp (uint32 op, uint32 pa, uint32 f0, uint32 f1);
165
extern t_stat btr (uint32 op, uint32 pa, uint32 f0, uint32 f1);
167
extern t_stat fp_add (uint32 d, uint32 s, t_bool sub);
168
extern t_stat fp_mul (uint32 d, uint32 s);
169
extern t_stat fp_div (uint32 d, uint32 s);
170
extern t_stat fp_fsl (uint32 d, uint32 s);
171
extern t_stat fp_fsr (uint32 d, uint32 s);
173
/* CPU data structures
175
cpu_dev CPU device descriptor
176
cpu_unit CPU unit descriptor
177
cpu_reg CPU register list
178
cpu_mod CPU modifier list
181
UNIT cpu_unit = { UDATA (NULL, UNIT_FIX+UNIT_BCD+MI_STD, MAXMEMSIZE) };
184
{ DRDATA (PC, saved_PC, 16), PV_LEFT },
185
{ DRDATA (IR2, IR2, 16), PV_LEFT },
186
{ DRDATA (PR1, PR1, 16), PV_LEFT },
187
{ DRDATA (PAR, PAR, 16), PV_LEFT + REG_RO },
188
{ DRDATA (QAR, QAR, 16), PV_LEFT + REG_RO },
189
{ FLDATA (SW1, ind[IN_SW1], 0) },
190
{ FLDATA (SW2, ind[IN_SW2], 0) },
191
{ FLDATA (SW3, ind[IN_SW3], 0) },
192
{ FLDATA (SW4, ind[IN_SW4], 0) },
193
{ FLDATA (HP, ind[IN_HP], 0) },
194
{ FLDATA (EZ, ind[IN_EZ], 0) },
195
{ FLDATA (OVF, ind[IN_OVF], 0) },
196
{ FLDATA (EXPCHK, ind[IN_EXPCHK], 0) },
197
{ FLDATA (RDCHK, ind[IN_RDCHK], 0) },
198
{ FLDATA (WRCHK, ind[IN_WRCHK], 0) },
199
{ FLDATA (ARSTOP, ar_stop, 0) },
200
{ FLDATA (IOSTOP, io_stop, 0) },
201
{ BRDATA (IND, ind, 10, 1, NUM_IND) },
202
{ FLDATA (IAE, iae, 0) },
203
{ FLDATA (IDXE, idxe, 0) },
204
{ FLDATA (IDXB, idxb, 0) },
205
{ DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT },
206
{ BRDATA (PCQ, pcq, 10, 14, PCQ_SIZE), REG_RO+REG_CIRC },
207
{ ORDATA (PCQP, pcq_p, 6), REG_HRO },
208
{ ORDATA (WRU, sim_int_char, 8) },
212
{ IF_IA, IF_IA, "IA", "IA", &cpu_set_opt1 },
213
{ IF_IA, 0, "no IA", "NOIA", &cpu_set_opt1 },
214
{ IF_EDT, IF_EDT, "EDT", "EDT", &cpu_set_opt1 },
215
{ IF_EDT, 0, "no EDT", "NOEDT", &cpu_set_opt1 },
216
{ IF_DIV, IF_DIV, "DIV", "DIV", &cpu_set_opt1 },
217
{ IF_DIV, 0, "no DIV", "NODIV", &cpu_set_opt1 },
218
{ IF_FP, IF_FP, "FP", "FP", NULL },
219
{ IF_FP, 0, "no FP", "NOFP", NULL },
220
{ IF_BIN, IF_BIN, "BIN", "BIN", &cpu_set_opt2 },
221
{ IF_BIN, 0, "no BIN", "NOBIN", &cpu_set_opt2 },
222
{ IF_IDX, IF_IDX, "IDX", "IDX", &cpu_set_opt2 },
223
{ IF_IDX, 0, "no IDX", "NOIDX", &cpu_set_opt2 },
224
{ IF_MII, IF_MII, "Model 2", "MOD2", &cpu_set_model },
225
{ IF_MII, 0, "Model 1", "MOD1", &cpu_set_model },
226
{ UNIT_MSIZE, 20000, NULL, "20K", &cpu_set_size },
227
{ UNIT_MSIZE, 40000, NULL, "40K", &cpu_set_size },
228
{ UNIT_MSIZE, 60000, NULL, "60K", &cpu_set_size },
229
{ UNIT_MSIZE, 0, NULL, "SAVE", &cpu_set_save },
230
{ UNIT_MSIZE, 0, NULL, "TABLE", &cpu_set_table },
234
"CPU", &cpu_unit, cpu_reg, cpu_mod,
236
&cpu_ex, &cpu_dep, &cpu_reset,
239
/* Instruction table */
241
const int32 op_table[100] = {
243
IF_FP + IF_VPA + IF_VQA, /* FADD */
244
IF_FP + IF_VPA + IF_VQA, /* FSUB */
245
IF_FP + IF_VPA + IF_VQA, /* FMUL */
247
IF_FP + IF_VPA + IF_VQA, /* FSL */
248
IF_FP + IF_MII + IF_VPA + IF_VQA, /* TFL */
249
IF_FP + IF_MII + IF_VPA + IF_VQA, /* BTFL */
250
IF_FP + IF_VPA + IF_VQA, /* FSR */
251
IF_FP + IF_VPA + IF_VQA, /* FDV */
252
IF_MII + IF_VPA + IF_IMM, /* 10: BTAM */
253
IF_VPA + IF_IMM, /* AM */
254
IF_VPA + IF_IMM, /* SM */
255
IF_VPA + IF_IMM, /* MM */
256
IF_VPA + IF_IMM, /* CM */
257
IF_VPA + IF_IMM, /* TDM */
258
IF_VPA + IF_IMM, /* TFM */
259
IF_VPA + IF_IMM, /* BTM */
260
IF_DIV + IF_VPA + IF_IMM, /* LDM */
261
IF_DIV + IF_VPA + IF_IMM, /* DM */
262
IF_MII + IF_VPA + IF_VQA, /* 20: BTA */
263
IF_VPA + IF_VQA, /* A */
264
IF_VPA + IF_VQA, /* S */
265
IF_VPA + IF_VQA, /* M */
266
IF_VPA + IF_VQA, /* C */
267
IF_VPA + IF_VQA, /* TD */
268
IF_VPA + IF_VQA, /* TF */
269
IF_VPA + IF_VQA, /* BT */
270
IF_DIV + IF_VPA + IF_VQA, /* LD */
271
IF_DIV + IF_VPA + IF_VQA, /* D */
272
IF_MII + IF_VPA + IF_VQA, /* 30: TRNM */
273
IF_VPA + IF_VQA, /* TR */
285
IF_VPA + IF_VQA, /* BD */
286
IF_VPA + IF_VQA, /* BNF */
287
IF_VPA + IF_VQA, /* BNR */
297
IF_VPA + IF_VQA, /* BNG - disk sys */
302
IF_MII + IF_VPA, /* 60: BS */
303
IF_IDX + IF_VPA + IF_NQX, /* BX */
304
IF_IDX + IF_VPA + IF_IMM, /* BXM */
305
IF_IDX + IF_VPA + IF_NQX, /* BCX */
306
IF_IDX + IF_VPA + IF_IMM, /* BCXM */
307
IF_IDX + IF_VPA + IF_NQX, /* BLX */
308
IF_IDX + IF_VPA + IF_IMM, /* BLXM */
309
IF_IDX + IF_VPA + IF_NQX, /* BSX */
312
IF_IDX + IF_VPA + IF_VQA, /* 70: MA */
313
IF_EDT + IF_VPA + IF_VQA, /* MF */
314
IF_EDT + IF_VPA + IF_VQA, /* MF */
315
IF_EDT + IF_VPA + IF_VQA, /* TNF */
332
IF_BIN + IF_VPA + IF_4QA, /* 90: BBT */
333
IF_BIN + IF_VPA + IF_4QA, /* BMK */
334
IF_BIN + IF_VPA + IF_VQA, /* ORF */
335
IF_BIN + IF_VPA + IF_VQA, /* ANDF */
336
IF_BIN + IF_VPA + IF_VQA, /* CPLF */
337
IF_BIN + IF_VPA + IF_VQA, /* EORF */
338
IF_BIN + IF_VPA + IF_VQA, /* OTD */
339
IF_BIN + IF_VPA + IF_VQA, /* DTO */
344
/* IO dispatch table */
346
t_stat (*iodisp[NUM_IO])(uint32 op, uint32 pa, uint32 f0, uint32 f1) = {
347
NULL, &tty, &ptp, &ptr, &cdp, /* 00 - 09 */
348
&cdr, NULL, &dp, NULL, &lpt,
349
NULL, NULL, NULL, NULL, NULL, /* 10 - 19 */
350
NULL, NULL, NULL, NULL, NULL,
351
NULL, NULL, NULL, NULL, NULL, /* 20 - 29 */
352
NULL, NULL, NULL, NULL, NULL,
353
NULL, NULL, &btp, &btr, NULL, /* 30 - 39 */
354
NULL, NULL, NULL, NULL, NULL,
355
NULL, NULL, NULL, NULL, NULL, /* 40 - 49 */
356
NULL, NULL, NULL, NULL, NULL,
357
NULL, NULL, NULL, NULL, NULL, /* 50 - 59 */
358
NULL, NULL, NULL, NULL, NULL,
359
NULL, NULL, NULL, NULL, NULL, /* 60 - 69 */
360
NULL, NULL, NULL, NULL, NULL,
361
NULL, NULL, NULL, NULL, NULL, /* 70 - 79 */
362
NULL, NULL, NULL, NULL, NULL,
363
NULL, NULL, NULL, NULL, NULL, /* 80 - 89 */
364
NULL, NULL, NULL, NULL, NULL,
365
NULL, NULL, NULL, NULL, NULL, /* 90 - 99 */
366
NULL, NULL, NULL, NULL, NULL };
368
/* Indicator table: -1 = illegal, +1 = resets when tested */
370
const int32 ind_table[NUM_IND] = {
371
-1, 0, 0, 0, 0, -1, 1, 1, -1, 1, /* 00 - 09 */
372
-1, 0, 0, 0, 1, 1, 1, 1, -1, 0, /* 10 - 19 */
373
-1, -1, -1, -1, -1, 0, -1, -1, -1, -1, /* 20 - 29 */
374
0, 0, 0, 1, 1, 0, 1, 1, 1, 0, /* 30 - 39 */
375
-1, -1, 1, -1, -1, -1, -1, -1, -1, -1, /* 40 - 49 */
376
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 50 - 59 */
377
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 60 - 69 */
378
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 - 79 */
379
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 89 */
380
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; /* 90 - 99 */
382
/* Add table for 1620 Model 1 */
384
const uint8 std_add_table[ADD_TABLE_LEN] = {
385
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
386
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
387
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11,
388
0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12,
389
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13,
390
0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14,
391
0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
392
0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
393
0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
394
0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 };
397
/* Add table for 1620 Model 2 ("hardware add") */
399
const uint8 sum_table[20] = {
400
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
401
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19 };
405
const uint8 std_mul_table[MUL_TABLE_LEN] = {
406
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
407
0, 0, 1, 0, 2, 0, 3, 0, 4, 0,
408
0, 0, 2, 0, 4, 0, 6, 0, 8, 0,
409
0, 0, 3, 0, 6, 0, 9, 0, 2, 1,
410
0, 0, 4, 0, 8, 0, 2, 1, 6, 1,
411
0, 0, 5, 0, 0, 1, 5, 1, 0, 2,
412
0, 0, 6, 0, 2, 1, 8, 1, 4, 2,
413
0, 0, 7, 0, 4, 1, 1, 2, 8, 2,
414
0, 0, 8, 0, 6, 1, 4, 2, 2, 3,
415
0, 0, 9, 0, 8, 1, 7, 2, 6, 3,
416
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
417
5, 0, 6, 0, 7, 0, 8, 0, 9, 0,
418
0, 1, 2, 1, 4, 1, 6, 1, 8, 1,
419
5, 1, 8, 1, 1, 2, 4, 2, 7, 2,
420
0, 2, 4, 2, 8, 2, 2, 3, 6, 3,
421
5, 2, 0, 3, 5, 3, 0, 4, 5, 4,
422
0, 3, 6, 3, 2, 4, 8, 4, 4, 5,
423
5, 3, 2, 4, 9, 4, 6, 5, 3, 6,
424
0, 4, 8, 4, 6, 5, 4, 6, 2, 7,
425
5, 4, 4, 5, 3, 6, 2, 7, 1, 8 };
427
#define BRANCH(x) PCQ_ENTRY; PC = (x)
428
#define GET_IDXADDR(x) ((idxb? IDX_B: IDX_A) + ((x) * ADDR_LEN) + (ADDR_LEN - 1))
430
t_stat sim_instr (void)
432
uint32 PC, pla, qla, f0, f1;
433
int32 i, t, idx, flags, sta, dev, op;
436
/* Restore saved state */
439
if ((cpu_unit.flags & IF_IA) == 0) iae = 0;
440
if ((cpu_unit.flags & IF_IDX) == 0) idxe = idxb = 0;
441
upd_ind (); /* update indicators */
444
/* Main instruction fetch/decode loop */
446
while (reason == 0) { /* loop until halted */
447
saved_PC = PC; /* commit prev instr */
448
if (sim_interval <= 0) { /* check clock queue */
449
if (reason = sim_process_event ()) break; }
451
if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */
452
reason = STOP_IBKPT; /* stop simulation */
455
sim_interval = sim_interval - 1;
457
/* Instruction fetch and address decode */
459
if (PC & 1) { /* PC odd? */
460
reason = STOP_INVIAD; /* stop */
463
op = get_2d (PC); /* get opcode */
464
if (op < 0) { /* invalid? */
465
reason = STOP_INVINS;
467
flags = op_table[op]; /* get op, flags */
468
if ((flags & ALLOPT) && /* need option? */
469
!(flags & ALLOPT & cpu_unit.flags)) { /* any set? */
470
reason = STOP_INVINS; /* no, error */
473
pla = ADDR_A (PC, I_PL); /* P last addr */
474
qla = ADDR_A (PC, I_QL); /* Q last addr */
475
if (flags & IF_VPA) { /* need P? */
476
reason = get_addr (pla, 5, TRUE, &PAR); /* get P addr */
477
if (reason != SCPE_OK) break; } /* stop if error */
478
if (flags & (IF_VQA | IF_4QA | IF_NQX)) { /* need Q? */
479
reason = get_addr (qla, /* get Q addr */
480
((flags & IF_4QA)? 4: 5), /* 4 or 5 digits */
481
((flags & IF_NQX)? FALSE: TRUE), /* not or indexed */
483
if (reason != SCPE_OK) { /* stop if invalid */
484
reason = reason + (STOP_INVQDG - STOP_INVPDG);
486
else if (flags & IF_IMM) QAR = qla; /* immediate? */
487
PC = PC + INST_LEN; /* advance PC */
488
switch (op) { /* case on op */
490
/* Transmit digit - P,Q are valid */
494
M[PAR] = M[QAR] & (FLAG | DIGIT); /* move dig, flag */
497
/* Transmit field - P,Q are valid */
501
reason = xmt_field (PAR, QAR, 1); /* xmit field */
504
/* Transmit record - P,Q are valid */
507
reason = xmt_record (PAR, QAR, TRUE); /* xmit record */
510
/* Transmit record no record mark - P,Q are valid */
513
reason = xmt_record (PAR, QAR, FALSE); /* xmit record but */
514
break; /* not rec mark */
516
/* Set flag - P is valid */
519
M[PAR] = M[PAR] | FLAG; /* set flag on P */
522
/* Clear flag - P is valid */
525
M[PAR] = M[PAR] & ~FLAG; /* clear flag on P */
528
/* Branch - P is valid */
531
BRANCH (PAR); /* branch to P */
534
/* Branch and transmit - P,Q are valid */
538
reason = xmt_field (ADDR_S (PAR, 1), QAR, 1); /* xmit field to P-1 */
539
IR2 = PC; /* save PC */
540
BRANCH (PAR); /* branch to P */
543
/* Branch and transmit floating - P,Q are valid */
546
reason = xmt_field (ADDR_S (PAR, 1), QAR, 3); /* skip 3 flags */
547
IR2 = PC; /* save PC */
548
BRANCH (PAR); /* branch to P */
551
/* Branch and transmit address - P,Q are valid */
555
reason = xmt_field (ADDR_S (PAR, 1), QAR, 4); /* skip 4 flags */
556
IR2 = PC; /* save PC */
557
BRANCH (PAR); /* branch to P */
563
if (PR1 != 1) { /* PR1 valid? */
564
BRANCH (PR1); /* return to PR1 */
565
PR1 = 1; } /* invalidate */
566
else if (IR2 != 1) { /* IR2 valid? */
567
BRANCH (IR2); /* return to IR2 */
568
IR2 = 1; } /* invalidate */
569
else reason = STOP_INVRTN; /* MAR check */
572
/* Branch on digit (not zero) - P,Q are valid */
575
if ((M[QAR] & DIGIT) != 0) { /* digit != 0? */
576
BRANCH (PAR); } /* branch */
579
/* Branch no flag - P,Q are valid */
582
if ((M[QAR] & FLAG) == 0) { /* flag == 0? */
583
BRANCH (PAR); } /* branch */
586
/* Branch no record mark (8-2 not set) - P,Q are valid */
589
if ((M[QAR] & REC_MARK) != REC_MARK) { /* not rec mark? */
590
BRANCH (PAR); } /* branch */
593
/* Branch no group mark - P,Q are valid */
596
if ((M[QAR] & DIGIT) != GRP_MARK) { /* not grp mark? */
597
BRANCH (PAR); } /* branch */
600
/* Branch (no) indicator - P is valid */
604
upd_ind (); /* update indicators */
605
t = get_2d (ADDR_A (saved_PC, I_BR)); /* get ind number */
606
if ((t < 0) || (ind_table[t] < 0)) { /* not valid? */
607
reason = STOP_INVIND; /* stop */
609
if ((ind[t] != 0) ^ (op == OP_BNI)) { /* ind value correct? */
610
BRANCH (PAR); } /* branch */
611
if (ind_table[t] > 0) ind[t] = 0; /* reset if needed */
614
/* Add/subtract/compare - P,Q are valid */
618
reason = add_field (PAR, QAR, FALSE, TRUE, 0, &sta); /* add, store */
619
if (sta == ADD_CARRY) ind[IN_OVF] = 1; /* cout => ovflo */
620
if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
625
reason = add_field (PAR, QAR, TRUE, TRUE, 0, &sta); /* sub, store */
626
if (sta == ADD_CARRY) ind[IN_OVF] = 1; /* cout => ovflo */
627
if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
632
reason = add_field (PAR, QAR, TRUE, FALSE, 0, &sta); /* sub, nostore */
633
if (sta == ADD_CARRY) ind[IN_OVF] = 1; /* cout => ovflo */
634
if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
637
/* Multiply - P,Q are valid */
641
reason = mul_field (PAR, QAR); /* multiply */
644
/* IO instructions - P is valid */
648
if ((PAR & 1) == 0) { /* P even? */
649
reason = STOP_INVEAD; /* stop */
655
dev = get_2d (ADDR_A (saved_PC, I_IO)); /* get IO dev */
656
f0 = M[ADDR_A (saved_PC, I_CTL)] & DIGIT; /* get function */
657
f1 = M[ADDR_A (saved_PC, I_CTL + 1)] & DIGIT;
658
if ((dev < 0) || (iodisp[dev] == NULL)) /* undefined dev? */
659
reason = STOP_INVIO; /* stop */
660
else reason = iodisp[dev] (op, PAR, f0, f1); /* call device */
663
/* Divide special feature instructions */
667
for (i = 0; i < PROD_AREA_LEN; i++) /* clear prod area */
668
M[PROD_AREA + i] = 0;
669
t = M[QAR] & FLAG; /* save Q sign */
670
reason = xmt_divd (PAR, QAR); /* xmit dividend */
671
M[PROD_AREA + PROD_AREA_LEN - 1] |= t; /* set sign */
674
/* Divide - P,Q are valid */
678
reason = div_field (PAR, QAR, &t); /* divide */
679
ind[IN_EZ] = t; /* set indicator */
680
if ((reason == STOP_OVERFL) && !ar_stop) /* ovflo stop? */
681
reason = SCPE_OK; /* no */
684
/* Edit special feature instructions */
686
/* Move flag - P,Q are valid */
689
M[PAR] = (M[PAR] & ~FLAG) | (M[QAR] & FLAG); /* copy Q flag */
690
M[QAR] = M[QAR] & ~FLAG; /* clr Q flag */
693
/* Transmit numeric strip - P,Q are valid, P is source */
696
if ((PAR & 1) == 0) { /* P must be odd */
697
reason = STOP_INVEAD;
699
reason = xmt_tns (QAR, PAR); /* xmit and strip */
702
/* Transmit numeric fill - P,Q are valid */
705
if ((PAR & 1) == 0) { /* P must be odd */
706
reason = STOP_INVEAD;
708
reason = xmt_tnf (PAR, QAR); /* xmit and strip */
711
/* Index special feature instructions */
713
/* Move address - P,Q are valid */
716
for (i = 0; i < ADDR_LEN; i++) { /* move 5 digits */
717
M[PAR] = (M[PAR] & FLAG) | (M[QAR] & DIGIT);
718
MM (PAR); MM (QAR); }
721
/* Branch load index - P,Q are valid, Q not indexed */
725
idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
726
if (idx < 0) { /* disabled? */
727
reason = STOP_INVIDX; /* stop */
729
xmt_index (GET_IDXADDR (idx), QAR); /* copy Q to idx */
730
BRANCH (PAR); /* branch to P */
733
/* Branch store index - P,Q are valid, Q not indexed */
736
idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
737
if (idx < 0) { /* disabled? */
738
reason = STOP_INVIDX; /* stop */
740
xmt_index (QAR, GET_IDXADDR (idx)); /* copy idx to Q */
741
BRANCH (PAR); /* branch to P */
744
/* Branch and modify index - P,Q are valid, Q not indexed */
747
idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
748
if (idx < 0) { /* disabled? */
749
reason = STOP_INVIDX; /* stop */
751
reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 0, &sta);
752
if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
753
BRANCH (PAR); /* branch to P */
757
idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
758
if (idx < 0) { /* disabled? */
759
reason = STOP_INVIDX; /* stop */
761
reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 3, &sta);
762
if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
763
BRANCH (PAR); /* branch to P */
766
/* Branch conditionally and modify index - P,Q are valid, Q not indexed */
769
idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
770
if (idx < 0) { /* disabled? */
771
reason = STOP_INVIDX; /* stop */
773
reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 0, &sta);
774
if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
775
if ((ind[IN_EZ] == 0) && (sta == ADD_NOCRY)) { /* ~z, ~c, ~sign chg? */
776
BRANCH (PAR); } /* branch */
780
idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
781
if (idx < 0) { /* disabled? */
782
reason = STOP_INVIDX; /* stop */
784
reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 3, &sta);
785
if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
786
if ((ind[IN_EZ] == 0) && (sta == ADD_NOCRY)) { /* ~z, ~c, ~sign chg? */
787
BRANCH (PAR); } /* branch */
790
/* Branch and select - P is valid */
793
t = M[ADDR_A (saved_PC, I_SEL)] & DIGIT; /* get select */
794
switch (t) { /* case on select */
796
idxe = idxb = 0; /* indexing off */
799
idxe = 1; idxb = 0; /* index band A */
802
idxe = idxb = 1; /* index band B */
805
iae = 0; /* indirect off */
808
iae = 1; /* indirect on */
811
reason = STOP_INVSEL; /* undefined */
816
/* Binary special feature instructions */
818
/* Branch on bit - P,Q are valid, Q is 4d address */
821
t = M[ADDR_A (saved_PC, I_Q)]; /* get Q0 digit */
822
if (t & M[QAR] & DIGIT) { /* match to mem? */
823
BRANCH (PAR); } /* branch */
826
/* Branch on mask - P,Q are valid, Q is 4d address */
829
t = M[ADDR_A (saved_PC, I_Q)]; /* get Q0 digit */
830
if (((t ^ M[QAR]) & /* match to mem? */
831
((t & FLAG)? (FLAG + DIGIT): DIGIT)) == 0) {
832
BRANCH (PAR); } /* branch */
835
/* Or - P,Q are valid */
838
reason = or_field (PAR, QAR); /* OR fields */
841
/* AND - P,Q are valid */
844
reason = and_field (PAR, QAR); /* AND fields */
847
/* Exclusive or - P,Q are valid */
850
reason = xor_field (PAR, QAR); /* XOR fields */
853
/* Complement - P,Q are valid */
856
reason = com_field (PAR, QAR); /* COM field */
859
/* Octal to decimal - P,Q are valid */
862
reason = oct_to_dec (PAR, QAR); /* convert */
865
/* Decimal to octal - P,Q are valid */
868
reason = dec_to_oct (PAR, QAR, &t); /* convert */
869
ind[IN_EZ] = t; /* set indicator */
870
if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
873
/* Floating point special feature instructions */
876
reason = fp_add (PAR, QAR, FALSE); /* add */
877
if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK;
881
reason = fp_add (PAR, QAR, TRUE); /* subtract */
882
if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK;
886
reason = fp_mul (PAR, QAR); /* multiply */
887
if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK;
891
reason = fp_div (PAR, QAR); /* divide */
892
if (ar_stop && ind[IN_OVF]) reason = STOP_FPDVZ;
893
if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK;
897
reason = fp_fsl (PAR, QAR); /* shift left */
901
reason = fp_fsr (PAR, QAR); /* shift right */
907
saved_PC = PC; /* commit inst */
908
reason = STOP_HALT; /* stop */
916
/* Invalid instruction code */
919
reason = STOP_INVINS; /* stop */
920
break; } /* end switch */
923
/* Simulation halted */
925
pcq_r->qptr = pcq_p; /* update pc q ptr */
930
/* Utility routines */
935
ad = address of high digit
937
val = field converted to binary
941
int32 get_2d (uint32 ad)
945
d = M[ad] & DIGIT; /* get 1st digit */
946
d1 = M[ADDR_A (ad, 1)] & DIGIT; /* get 2nd digit */
947
if (BAD_DIGIT (d) || BAD_DIGIT (d1)) return -1; /* bad? error */
948
return ((d * 10) + d1); /* cvt to binary */
951
/* Get address routine
954
alast = address of low digit
956
indexok = TRUE if indexing allowed
957
&addr = pointer to address output
959
return = error status (in terms of P address)
960
addr = address converted to binary
963
- If indexing produces a negative result, the effective address is
964
the 10's complement of the result
965
- An address that exceeds memory produces a MAR check stop
968
t_stat get_addr (uint32 alast, int32 lnt, t_bool indexok, uint32 *reta)
971
int32 cnt, idx, idxa, idxv, addr;
973
if (iae) indir = FLAG; /* init indirect */
976
cnt = 0; /* count depth */
977
do { indir = indir & M[alast]; /* get indirect */
978
if (cvt_addr (alast, lnt, FALSE, &addr)) /* cvt addr to bin */
979
return STOP_INVPDG; /* bad? */
980
idx = get_idx (ADDR_S (alast, 1)); /* get index reg num */
981
if (indexok && (idx > 0)) { /* indexable? */
982
idxa = GET_IDXADDR (idx); /* get idx reg addr */
983
if (cvt_addr (idxa, ADDR_LEN, TRUE, &idxv)) /* cvt idx reg */
985
addr = addr + idxv; /* add in index */
986
if (addr < 0) addr = addr + 100000; } /* -? 10's comp */
987
if (addr >= (int32) MEMSIZE) return STOP_INVPAD;/* invalid addr? */
988
alast = addr; /* new address */
989
lnt = ADDR_LEN; } /* std len */
990
while (indir && (cnt++ < ind_max));
991
if (cnt > ind_max) return STOP_INVPIA; /* indir too deep? */
992
*reta = addr; /* return address */
996
/* Convert address to binary
999
alast = address of low digit
1001
signok = TRUE if signed
1002
val = address of output
1004
status = 0 if ok, != 0 if error
1007
t_stat cvt_addr (uint32 alast, int32 lnt, t_bool signok, int32 *val)
1009
int32 sign = 0, addr = 0, t;
1011
if (signok && (M[alast] & FLAG)) sign = 1; /* signed? */
1012
alast = alast - lnt; /* find start */
1013
do { PP (alast); /* incr mem addr */
1014
t = M[alast] & DIGIT; /* get digit */
1015
if (BAD_DIGIT (t)) return STOP_INVDIG; /* bad? error */
1016
addr = (addr * 10) + t; } /* cvt to bin */
1018
if (sign) *val = -addr; /* minus? */
1023
/* Get index register number
1026
aidx = address of low digit
1028
index = >0 if indexed
1030
<0 if indexing disabled
1033
t_stat get_idx (uint32 aidx)
1037
if (idxe == 0) return -1; /* indexing off? */
1038
for (i = idx = 0; i < 3; i++) { /* 3 flags worth */
1039
if (M[aidx] & FLAG) idx = idx | (1 << i); /* test flag */
1040
MM (aidx); } /* next digit */
1044
/* Update indicators routine */
1048
ind[IN_HPEZ] = ind[IN_HP] | ind[IN_EZ]; /* HPEZ = HP | EZ */
1049
ind[IN_DERR] = ind[IN_DACH] | ind[IN_DWLR] | ind[IN_DCYO];
1050
ind[IN_ANYCHK] = ind[IN_RDCHK] | ind[IN_WRCHK] | /* ANYCHK = all chks */
1051
ind[IN_MBREVEN] | ind[IN_MBRODD] |
1052
ind[IN_PRCHK] | ind[IN_DACH];
1053
ind[IN_IXN] = ind[IN_IXA] = ind[IN_IXB] = 0; /* clr index indics */
1054
if (!idxe) ind[IN_IXN] = 1; /* off? */
1055
else if (!idxb) ind[IN_IXA] = 1; /* on, band A? */
1056
else ind[IN_IXB] = 1; /* no, band B */
1060
/* Transmit routines */
1062
/* Transmit field from 's' to 'd' - ignore first 'skp' flags */
1064
t_stat xmt_field (uint32 d, uint32 s, uint32 skp)
1069
do { t = M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */
1070
MM (d); MM (s); /* decr mem addrs */
1071
if (cnt++ >= MEMSIZE) return STOP_FWRAP; } /* (stop runaway) */
1072
while (((t & FLAG) == 0) || (cnt <= skp)); /* until flag */
1076
/* Transmit record from 's' to 'd' - copy record mark if 'cpy' = TRUE */
1078
t_stat xmt_record (uint32 d, uint32 s, t_bool cpy)
1082
while ((M[s] & REC_MARK) != REC_MARK) { /* until rec mark */
1083
M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */
1084
PP (d); PP (s); /* incr mem addrs */
1085
if (cnt++ >= MEMSIZE) return STOP_RWRAP; } /* (stop runaway) */
1086
if (cpy) M[d] = M[s] & (FLAG | DIGIT); /* copy rec mark */
1090
/* Transmit index from 's' to 'd' - fixed five character field */
1092
t_stat xmt_index (uint32 d, uint32 s)
1096
M[d] = M[s] & (FLAG | DIGIT); /* preserve sign */
1097
MM (d); MM (s); /* decr mem addrs */
1098
for (i = 0; i < ADDR_LEN - 2; i++) { /* copy 3 digits */
1099
M[d] = M[s] & DIGIT; /* without flags */
1100
MM (d); MM (s); } /* decr mem addrs */
1101
M[d] = (M[s] & DIGIT) | FLAG; /* set flag on last */
1105
/* Transmit dividend from 'd' to 's' - clear flag on first digit */
1107
t_stat xmt_divd (uint32 d, uint32 s)
1111
M[d] = M[s] & DIGIT; /* first w/o flag */
1112
do { MM (d); MM (s); /* decr mem addrs */
1113
M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */
1114
if (cnt++ >= MEMSIZE) return STOP_FWRAP; } /* (stop runaway) */
1115
while ((M[d] & FLAG) == 0); /* until src flag */
1119
/* Transmit numeric strip from 's' to 'd' - s is odd */
1121
t_stat xmt_tns (uint32 d, uint32 s)
1126
t = M[s] & DIGIT; /* get units */
1127
z = M[s - 1] & DIGIT; /* get zone */
1128
if ((z == 1) || (z == 5) || ((z == 2) && (t == 0))) /* 1x, 5x, 20? */
1129
M[d] = t | FLAG; /* set flag */
1130
else M[d] = t; /* else clear flag */
1131
do { MM (d); /* decr mem addrs */
1133
t = M[d] & FLAG; /* save dst flag */
1134
M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */
1135
if (cnt >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */
1137
while (t == 0); /* until dst flag */
1138
M[d] = M[d] | FLAG; /* set flag at end */
1142
/* Transmit numeric fill from 's' to 'd' - d is odd */
1144
t_stat xmt_tnf (uint32 d, uint32 s)
1149
t = M[s]; /* get 1st digit */
1150
M[d] = t & DIGIT; /* store */
1151
M[d - 1] = (t & FLAG)? 5: 7; /* set sign from flag */
1152
do { MM (s); /* decr mem addr */
1154
t = M[s]; /* get src digit */
1155
M[d] = t & DIGIT; /* move to dst, no flag */
1156
M[d - 1] = 7; /* set zone */
1157
if (cnt >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */
1159
while ((t & FLAG) == 0); /* until src flag */
1166
d = destination field low (P)
1167
s = source field low (Q)
1168
sub = TRUE if subtracting
1169
sto = TRUE if storing
1170
skp = number of source field flags, beyond sign, to ignore
1173
sta = ADD_NOCRY: no carry out, no sign change
1174
ADD_SCHNG: sign change
1175
ADD_CARRY: carry out
1177
Reference Manual: "When the sum is zero, the sign of the P field
1181
t_stat add_field (uint32 d, uint32 s, t_bool sub, t_bool sto, uint32 skp, int32 *sta)
1183
uint32 cry, src, dst, res, comp, dp, dsv;
1184
uint32 src_f = 0, cnt = 0, dst_f;
1186
*sta = ADD_NOCRY; /* assume no cry */
1187
dsv = d; /* save dst */
1188
comp = ((M[d] ^ M[s]) & FLAG) ^ (sub? FLAG: 0); /* set compl flag */
1189
cry = 0; /* clr carry */
1190
ind[IN_HP] = ((M[d] & FLAG) == 0); /* set sign from res */
1191
ind[IN_EZ] = 1; /* assume zero */
1193
dst = M[d] & DIGIT; /* 1st digits */
1195
if (BAD_DIGIT (dst) || BAD_DIGIT (src)) /* bad digit? */
1197
if (comp) src = 10 - src; /* complement? */
1198
res = add_one_digit (dst, src, &cry); /* add */
1199
if (sto) M[d] = (M[d] & FLAG) | res; /* store */
1200
MM (d); MM (s); /* decr mem addrs */
1201
do { dst = M[d] & DIGIT; /* get dst digit */
1202
dst_f = M[d] & FLAG; /* get dst flag */
1203
if (src_f) src = 0; /* src done? src = 0 */
1205
src = M[s] & DIGIT; /* get src digit */
1206
if (cnt >= skp) src_f = M[s] & FLAG; /* get src flag */
1207
MM (s); } /* decr src addr */
1208
if (BAD_DIGIT (dst) || BAD_DIGIT (src)) /* bad digit? */
1210
if (comp) src = 9 - src; /* complement? */
1211
res = add_one_digit (dst, src, &cry); /* add */
1212
if (sto) M[d] = dst_f | res; /* store */
1213
MM (d); /* decr dst addr */
1214
if (cnt++ >= MEMSIZE) return STOP_FWRAP; } /* (stop runaway) */
1215
while (dst_f == 0); /* until dst done */
1216
if (!src_f) ind[IN_OVF] = 1; /* !src done? ovf */
1217
if (comp && !cry && !ind[IN_EZ]) { /* recomp needed? */
1218
ind[IN_HP] = ind[IN_HP] ^ 1; /* flip indicator */
1219
if (sto) { /* storing? */
1220
for (cry = 1, dp = dsv; dp != d; ) { /* rescan */
1221
dst = M[dp] & DIGIT; /* get dst digit */
1222
res = add_one_digit (9 - dst, 0, &cry); /* "add" */
1223
M[dp] = (M[dp] & FLAG) | res; /* store */
1224
MM (dp); } /* decr dst addr */
1225
M[dsv] = M[dsv] ^ FLAG; } /* compl sign */
1226
*sta = ADD_SIGNC; /* sign changed */
1227
return SCPE_OK; } /* end if recomp */
1228
if (ind[IN_EZ]) ind[IN_HP] = 0; /* res = 0? clr HP */
1229
if (!comp && cry) *sta = ADD_CARRY; /* set status */
1233
/* Add one digit via table (Model 1) or "hardware" (Model 2) */
1235
uint32 add_one_digit (uint32 dst, uint32 src, uint32 *cry)
1239
if (*cry) src = src + 1; /* cry in? incr src */
1240
if (src >= 10) { /* src > 10? */
1241
src = src - 10; /* src -= 10 */
1242
*cry = 1; } /* carry out */
1243
else *cry = 0; /* else no carry */
1244
if (cpu_unit.flags & IF_MII) /* Model 2? */
1245
res = sum_table[dst + src]; /* "hardware" */
1246
else res = M[ADD_TABLE + (dst * 10) + src]; /* table lookup */
1247
if (res & FLAG) *cry = 1; /* carry out? */
1248
if (res & DIGIT) ind[IN_EZ] = 0; /* nz? clr ind */
1255
mpc = multiplicand address
1256
mpy = multiplier address
1260
Reference manual: "A zero product may have a negative or positive sign,
1261
depending on the signs of the fields at the P and Q addresses."
1264
t_stat mul_field (uint32 mpc, uint32 mpy)
1267
uint32 pro; /* prod pointer */
1268
uint32 mpyd, mpyf; /* mpy digit, flag */
1269
uint32 cnt = 0; /* counter */
1270
uint8 sign; /* final sign */
1273
PR1 = 1; /* step on PR1 */
1274
for (i = 0; i < PROD_AREA_LEN; i++) /* clr prod area */
1275
M[PROD_AREA + i] = 0;
1276
sign = (M[mpc] & FLAG) ^ (M[mpy] & FLAG); /* get final sign */
1277
ind[IN_HP] = (sign == 0); /* set indicators */
1279
pro = PROD_AREA + PROD_AREA_LEN - 1; /* product ptr */
1281
/* Loop on multiplier (mpy) and product (pro) digits */
1283
do { mpyd = M[mpy] & DIGIT; /* multiplier digit */
1284
mpyf = (M[mpy] & FLAG) && (cnt != 0); /* last digit flag */
1285
if (BAD_DIGIT (mpyd)) return STOP_INVDIG; /* bad? */
1286
r = mul_one_digit (mpyd, mpc, pro, mpyf); /* prod += mpc*mpy_dig */
1287
if (r != SCPE_OK) return r; /* error? */
1288
MM (mpy); MM (pro); /* decr mpyr, prod addrs */
1289
if (cnt++ > MEMSIZE) return STOP_FWRAP; } /* (stop runaway) */
1290
while ((mpyf == 0) || (cnt <= 1)); /* until mpyr flag */
1292
if (ind[IN_EZ]) ind[IN_HP] = 0; /* res = 0? clr HP */
1293
M[PROD_AREA + PROD_AREA_LEN - 1] |= sign; /* set final sign */
1300
mpyd = multiplier digit (tested valid)
1301
mpcp = multiplicand low address
1302
prop = product low address
1303
last = last iteration flag (set flag on high product)
1305
prod += multiplicand * multiplier_digit
1308
The multiply table address is constructed as follows:
1309
- double the multiplier digit
1310
- use the 10's digit of the doubled result, + 1, as the 100's digit
1311
of the table address
1312
- use the multiplicand digit as the 10's digit of the table address
1313
- use the unit digit of the doubled result as the unit digit of the
1315
EZ indicator is cleared if a non-zero digit is ever generated
1318
t_stat mul_one_digit (uint32 mpyd, uint32 mpcp, uint32 prop, uint32 last)
1320
uint32 mpta, mptb; /* mult table */
1321
uint32 mptd; /* mult table digit */
1322
uint32 mpcd, mpcf; /* mpc digit, flag */
1323
uint32 prwp; /* prod working ptr */
1324
uint32 prod; /* product digit */
1325
uint32 cry; /* carry */
1326
uint32 mpcc, cryc; /* counters */
1328
mptb = MUL_TABLE + ((mpyd <= 4)? (mpyd * 2): /* set mpy table 100's, */
1329
(((mpyd - 5) * 2) + 100)); /* 1's digits */
1331
/* Inner loop on multiplicand (mpcp) and product (prop) digits */
1333
mpcc = 0; /* multiplicand ctr */
1334
do { prwp = prop; /* product working ptr */
1335
mpcd = M[mpcp] & DIGIT; /* multiplicand digit */
1336
mpcf = M[mpcp] & FLAG; /* multiplicand flag */
1337
if (BAD_DIGIT (mpcd)) return STOP_INVDIG; /* bad? */
1338
mpta = mptb + (mpcd * 10); /* mpy table 10's digit */
1339
cry = 0; /* init carry */
1340
mptd = M[mpta] & DIGIT; /* mpy table digit */
1341
if (BAD_DIGIT (mptd)) return STOP_INVDIG; /* bad? */
1342
prod = M[prwp] & DIGIT; /* product digit */
1343
if (BAD_DIGIT (prod)) return STOP_INVDIG; /* bad? */
1344
M[prwp] = add_one_digit (prod, mptd, &cry); /* add mpy tbl to prod */
1345
MM (prwp); /* decr working ptr */
1346
mptd = M[mpta + 1] & DIGIT; /* mpy table digit */
1347
if (BAD_DIGIT (mptd)) return STOP_INVDIG; /* bad? */
1348
prod = M[prwp] & DIGIT; /* product digit */
1349
if (BAD_DIGIT (prod)) return STOP_INVDIG; /* bad? */
1350
M[prwp] = add_one_digit (prod, mptd, &cry); /* add mpy tbl to prod */
1351
cryc = 0; /* (stop runaway) */
1352
while (cry) { /* propagate carry */
1353
MM (prwp); /* decr working ptr */
1354
prod = M[prwp] & DIGIT; /* product digit */
1355
if (BAD_DIGIT (prod)) return STOP_INVDIG; /* bad? */
1356
M[prwp] = add_one_digit (prod, 0, &cry); /* add cry */
1357
if (cryc++ > MEMSIZE) return STOP_FWRAP; }
1358
MM (mpcp); MM (prop); /* decr mpc, prod ptrs */
1359
if (mpcc++ > MEMSIZE) return STOP_FWRAP; }
1360
while ((mpcf == 0) || (mpcc <= 1)); /* until mpcf flag */
1362
M[prop] = M[prop] | FLAG; /* flag high product */
1366
/* Divide routine - comments from Geoff Kuenning's 1620 simulator
1368
The destination of the divide is given by:
1370
100 - <# digits in quotient>
1372
Which is more easily calculated as:
1374
100 - <# digits in divisor> - <# digits in dividend>
1376
The quotient goes into 99 minus the divisor length. The
1377
remainder goes into 99. The load dividend instruction (above)
1378
should have specified a P address of 99 minus the size of the
1381
Note that this all implies that "dest" points to the *leftmost*
1382
digit of the dividend.
1384
After the division, the assumed decimal point will be as many
1385
positions to the left as there are digits in the divisor. In
1386
other words, a 4-digit divisor will produce 4 (assumed) decimal
1389
There are other ways to do these things. In particular, the
1390
load-dividend instruction doesn't have to specify the above
1391
formula; if it's done differently, then you don't have to get
1392
decimal places. This is not well-explained in the books I have.
1394
How to divide on a 1620:
1396
The dividend is the field at 99:
1400
The divisor is somewhere else in memory:
1404
The divide operation specifies the left-most digit of the
1405
dividend as the place to begin trial subtractions:
1409
The loop works as follows:
1411
1. Call the left-most digit of the dividend "current_dividend".
1412
Call the location current_dividend - <divisor_length>
1414
2. Clear the flag at current_dividend, and set one at
1417
88 = _001234567890, q_d = 88, c_d = 90
1418
[Not actually done; divisor length controls subtract.]
1419
3. Subtract the divisor from the field at current-dividend,
1420
using normal 1620 rules, except that signs are ignored.
1421
Continue these subtractions until either 10 subtractions
1422
have been done, or you get a negative result:
1424
88 = _00_2234567890, q_d = 88, c_d = 90
1425
4. If 10 subtractions have been done, set the overflow
1426
indicator and abort. Otherwise, add the divisor back to
1427
correct for the oversubtraction:
1429
88 = _001234567890, q_d = 88, c_d = 90
1430
5. Store the (net) number of subtractions in quotient_digit:
1432
88 = _001234567890, q_d = 88, c_d = 90
1433
6. If this is not the first pass, clear the flag at
1434
quotient_digit. Increment quotient_digit and
1435
current_dividend, and set a flag at the new
1438
88 = _0_01234567890, q_d = 89, c_d = 91
1439
[If first pass, set a flag at quotient digit.]
1440
7. If current_dividend is not 100, repeat steps 3 through 7.
1441
8. Set flags at 99 and quotient_digit - 1 according to the
1442
rules of algebra: the quotient's sign is the exclusive-or
1443
of the signs of the divisor and dividend, and the
1444
remainder has the sign of the dividend:
1446
10 / 3 = 3 remainder 1
1447
10 / -3 = -3 remainder 1
1448
-10 / 3 = -3 remainder -1
1449
-10 / -3 = 3 remainder -1
1451
This preserves the relationship dd = q * dv + r.
1453
Our example continues as follows for steps 3 through 7:
1455
3. 88 = _0_00_334567890, q_d = 89, c_d = 91
1456
4. 88 = _0_00034567890
1457
5. 88 = _0_40034567890
1458
6. 88 = _04_0034567890, q_d = 90, c_d = 92
1459
3. 88 = _04_00_34567890
1460
4. 88 = _04_0004567890
1461
5. 88 = _04_1004567890
1462
6. 88 = _041_004567890, q_d = 91, c_d = 93
1463
3. 88 = _041_00_2567890
1464
4. 88 = _041_001567890
1465
5. 88 = _041_101567890
1466
6. 88 = _0411_01567890, q_d = 92, c_d = 94
1467
3. 88 = _0411_00_367890
1468
4. 88 = _0411_00067890
1469
5. 88 = _0411_50067890
1470
6. 88 = _04115_0067890, q_d = 93, c_d = 95
1471
3. 88 = _04115_00_37890
1472
4. 88 = _04115_0007890
1473
5. 88 = _04115_2007890
1474
6. 88 = _041152_007890, q_d = 94, c_d = 96
1475
3. 88 = _041152_00_2890
1476
4. 88 = _041152_001890
1477
5. 88 = _041152_201890
1478
6. 88 = _0411522_01890, q_d = 95, c_d = 97
1479
3. 88 = _0411522_00_390
1480
4. 88 = _0411522_00090
1481
5. 88 = _0411522_60090
1482
6. 88 = _04115226_0090, q_d = 96, c_d = 98
1483
3. 88 = _04115226_00_30
1484
4. 88 = _04115226_0000
1485
5. 88 = _04115226_3000
1486
6. 88 = _041152263_000, q_d = 97, c_d = 99
1487
3. 88 = _041152263_00_3
1488
4. 88 = _041152263_000
1489
5. 88 = _041152263_000
1490
6. 88 = _0411522630_00, q_d = 98, c_d = 100
1492
In the actual code below, we elide several of these steps in
1493
various ways for convenience and efficiency.
1495
Note that the EZ indicator is NOT valid for divide, because it
1496
is cleared by any non-zero result in an intermediate add. The
1497
code maintains its own EZ indicator for the quotient.
1500
t_stat div_field (uint32 dvd, uint32 dvr, int32 *ez)
1502
uint32 quop, quod, quos; /* quo ptr, dig, sign */
1503
uint32 dvds; /* dvd sign */
1504
t_bool first = TRUE; /* first pass */
1507
dvds = (M[PROD_AREA + PROD_AREA_LEN - 1]) & FLAG; /* dividend sign */
1508
quos = dvds ^ (M[dvr] & FLAG); /* quotient sign */
1509
ind[IN_HP] = (quos == 0); /* set indicators */
1512
/* Loop on current dividend, high order digit at dvd */
1514
do { r = div_one_digit (dvd, dvr, 10, &quod, &quop); /* dev quo digit */
1515
if (r != SCPE_OK) return r; /* error? */
1517
/* Store quotient digit and advance current dividend pointer */
1519
if (first) { /* first pass? */
1520
if (quod >= 10) { /* overflow? */
1521
ind[IN_OVF] = 1; /* set indicator */
1522
return STOP_OVERFL; } /* stop */
1523
M[quop] = FLAG | quod; /* set flag on quo */
1525
else M[quop] = quod; /* store quo digit */
1526
if (quod) *ez = 0; /* if nz, clr ind */
1527
PP (dvd); } /* incr dvd ptr */
1528
while (dvd != (PROD_AREA + PROD_AREA_LEN)); /* until end prod */
1530
/* Division done. Set signs of quo, rem, set flag on high order remainder */
1532
if (*ez) ind[IN_HP] = 0; /* res = 0? clr HP */
1533
M[PROD_AREA + PROD_AREA_LEN - 1] |= dvds; /* remainder sign */
1534
M[quop] = M[quop] | quos; /* quotient sign */
1535
PP (quop); /* high remainder */
1536
M[quop] = M[quop] | FLAG; /* set flag */
1543
dvd = current dividend address (high digit)
1544
dvr = divisor address (low digit)
1545
max = max number of iterations before overflow
1546
&quod = address to store quotient digit
1547
&quop = address to store quotient pointer (can be NULL)
1551
Divide step calculates a quotient digit by repeatedly subtracting the
1552
divisor from the current dividend. The divisor's length controls the
1553
subtraction; dividend flags are ignored.
1556
t_stat div_one_digit (uint32 dvd, uint32 dvr, uint32 max,
1557
uint32 *quod, uint32 *quop)
1559
uint32 dvrp, dvrd, dvrf; /* dvr ptr, dig, flag */
1560
uint32 dvdp, dvdd; /* dvd ptr, dig */
1561
uint32 qd, cry; /* quo dig, carry */
1564
for (qd = 0; qd < max; qd++) { /* devel quo dig */
1565
dvrp = dvr; /* divisor ptr */
1566
dvdp = dvd; /* dividend ptr */
1568
cry = 1; /* carry in = 1 */
1569
do { /* sub dvr fm dvd */
1570
dvdd = M[dvdp] & DIGIT; /* dividend digit */
1571
if (BAD_DIGIT (dvdd)) return STOP_INVDIG; /* bad? */
1572
dvrd = M[dvrp] & DIGIT; /* divisor digit */
1573
dvrf = M[dvrp] & FLAG; /* divisor flag */
1574
if (BAD_DIGIT (dvrd)) return STOP_INVDIG; /* bad? */
1575
M[dvdp] = add_one_digit (dvdd, 9 - dvrd, &cry); /* sub */
1576
MM (dvdp); MM (dvrp); /* decr ptrs */
1577
if (cnt++ > MEMSIZE) return STOP_FWRAP; } /* (stop runaway) */
1578
while ((dvrf == 0) || (cnt <= 1)); /* until dvr flag */
1579
if (!cry) { /* !cry = borrow */
1580
dvdd = M[dvdp] & DIGIT; /* borrow digit */
1581
if (BAD_DIGIT (dvdd)) return STOP_INVDIG; /* bad? */
1582
M[dvdp] = add_one_digit (dvdd, 9, &cry); } /* sub */
1583
if (!cry) break; } /* !cry = negative */
1585
/* Add back the divisor to correct for the negative result */
1587
dvrp = dvr; /* divisor ptr */
1588
dvdp = dvd; /* dividend ptr */
1590
cry = 0; /* carry in = 0 */
1591
do { dvdd = M[dvdp] & DIGIT; /* dividend digit */
1592
dvrd = M[dvrp] & DIGIT; /* divisor digit */
1593
dvrf = M[dvrp] & FLAG; /* divisor flag */
1594
M[dvdp] = add_one_digit (dvdd, dvrd, &cry); /* add */
1595
MM (dvdp); MM (dvrp); cnt++; } /* decr ptrs */
1596
while ((dvrf == 0) || (cnt <= 1)); /* until dvr flag */
1597
if (cry) { /* carry out? */
1598
dvdd = M[dvdp] & DIGIT; /* borrow digit */
1599
M[dvdp] = add_one_digit (dvdd, 0, &cry); } /* add */
1600
if (quop != NULL) *quop = dvdp; /* set quo addr */
1601
*quod = qd; /* set quo digit */
1605
/* Logical operation routines (and, or, xor, complement)
1608
d = destination address
1613
Destination flags are preserved; EZ reflects the result.
1614
COM does not obey normal field length restrictions.
1617
t_stat or_field (uint32 d, uint32 s)
1622
ind[IN_EZ] = 1; /* assume result zero */
1623
do { t = M[s]; /* get src */
1624
M[d] = (M[d] & FLAG) | ((M[d] | t) & 07); /* OR src to dst */
1625
if (M[d] & DIGIT) ind[IN_EZ] = 0; /* nz dig? clr ind */
1626
MM (d); MM (s); /* decr pointers */
1627
if (cnt++ >= MEMSIZE) return STOP_FWRAP; } /* (stop runaway) */
1628
while (((t & FLAG) == 0) || (cnt <= 1)); /* until src flag */
1632
t_stat and_field (uint32 d, uint32 s)
1637
ind[IN_EZ] = 1; /* assume result zero */
1638
do { t = M[s]; /* get src */
1639
M[d] = (M[d] & FLAG) | ((M[d] & t) & 07); /* AND src to dst */
1640
if (M[d] & DIGIT) ind[IN_EZ] = 0; /* nz dig? clr ind */
1641
MM (d); MM (s); /* decr pointers */
1642
if (cnt++ >= MEMSIZE) return STOP_FWRAP; } /* (stop runaway) */
1643
while (((t & FLAG) == 0) || (cnt <= 1)); /* until src flag */
1647
t_stat xor_field (uint32 d, uint32 s)
1652
ind[IN_EZ] = 1; /* assume result zero */
1653
do { t = M[s]; /* get src */
1654
M[d] = (M[d] & FLAG) | ((M[d] ^ t) & 07); /* XOR src to dst */
1655
if (M[d] & DIGIT) ind[IN_EZ] = 0; /* nz dig? clr ind */
1656
MM (d); MM (s); /* decr pointers */
1657
if (cnt++ >= MEMSIZE) return STOP_FWRAP; } /* (stop runaway) */
1658
while (((t & FLAG) == 0) || (cnt <= 1)); /* until src flag */
1662
t_stat com_field (uint32 d, uint32 s)
1667
ind[IN_EZ] = 1; /* assume result zero */
1668
do { t = M[s]; /* get src */
1669
M[d] = (t & FLAG) | ((t ^ 07) & 07); /* comp src to dst */
1670
if (M[d] & DIGIT) ind[IN_EZ] = 0; /* nz dig? clr ind */
1671
MM (d); MM (s); /* decr pointers */
1672
if (cnt++ >= MEMSIZE) return STOP_FWRAP; } /* (stop runaway) */
1673
while ((t & FLAG) == 0); /* until src flag */
1680
tbl = conversion table address (low digit)
1683
product area = converted source
1686
OTD is a cousin of multiply. The octal digits in the source are
1687
multiplied by successive values in the conversion table, and the
1688
results are accumulated in the product area. Although the manual
1689
does not say, this code assumes that EZ and HP are affected.
1692
t_stat oct_to_dec (uint32 tbl, uint32 s)
1694
uint32 cnt = 0, tblc;
1695
uint32 i, sd, sf, tf, sign;
1698
for (i = 0; i < PROD_AREA_LEN; i++) /* clr prod area */
1699
M[PROD_AREA + i] = 0;
1700
sign = M[s] & FLAG; /* save sign */
1701
ind[IN_EZ] = 1; /* set indicators */
1702
ind[IN_HP] = (sign == 0);
1703
do { sd = M[s] & DIGIT; /* src digit */
1704
sf = M[s] & FLAG; /* src flag */
1705
r = mul_one_digit (sd, tbl, PROD_AREA + PROD_AREA_LEN - 1, sf);
1706
if (r != SCPE_OK) return r; /* err? */
1707
MM (s); /* decr src addr */
1708
MM (tbl); /* skip 1st tbl dig */
1709
tblc = 0; /* count */
1711
tf = M[tbl] & FLAG; /* get next */
1712
MM (tbl); /* decr ptr */
1713
if (tblc++ > MEMSIZE) return STOP_FWRAP; }
1714
while (tf == 0); /* until flag */
1715
if (cnt++ > MEMSIZE) return STOP_FWRAP; } /* (stop runaway) */
1717
if (ind[IN_EZ]) ind[IN_HP] = 0; /* res = 0? clr HP */
1718
M[PROD_AREA + PROD_AREA_LEN - 1] |= sign; /* set sign */
1725
d = destination address
1726
tbl = conversion table address (low digit of highest power)
1727
&ez = address of soft EZ indicator
1728
product area = field to convert
1732
DTO is a cousin to divide. The number in the product area is repeatedly
1733
divided by successive values in the conversion table, and the quotient
1734
digits are stored in the destination. Although the manual does not say,
1735
this code assumes that EZ and HP are affected.
1738
t_stat dec_to_oct (uint32 d, uint32 tbl, int32 *ez)
1740
uint32 sign, octd, t;
1741
t_bool first = TRUE;
1745
sign = M[PROD_AREA + PROD_AREA_LEN - 1] & FLAG; /* input sign */
1746
*ez = 1; /* set indicators */
1747
ind[IN_HP] = (sign == 0);
1749
r = div_one_digit (PROD_AREA + PROD_AREA_LEN - 1, /* divide */
1750
tbl, 8, &octd, NULL);
1751
if (r != SCPE_OK) return r; /* error? */
1752
if (first) { /* first pass? */
1753
if (octd >= 8) { /* overflow? */
1754
ind[IN_OVF] = 1; /* set indicator */
1755
return SCPE_OK; } /* stop */
1756
M[d] = FLAG | octd; /* set flag on quo */
1758
else M[d] = octd; /* store quo digit */
1759
if (octd) *ez = 0; /* if nz, clr ind */
1760
PP (tbl); /* incr tbl addr */
1761
if ((M[tbl] & REC_MARK) == REC_MARK) break; /* record mark? */
1762
PP (tbl); /* skip flag */
1763
if ((M[tbl] & REC_MARK) == REC_MARK) break; /* record mark? */
1764
do { PP (tbl); /* look for F, rec mk */
1766
while (((t & FLAG) == 0) && ((t & REC_MARK) != REC_MARK));
1767
MM (tbl); /* step back one */
1768
PP (d); /* incr quo addr */
1769
if (ctr++ > MEMSIZE) return STOP_FWRAP; } /* (stop runaway) */
1770
if (*ez) ind[IN_HP] = 0; /* res = 0? clr HP */
1771
M[d] = M[d] | sign; /* set result sign */
1777
t_stat cpu_reset (DEVICE *dptr)
1780
static t_bool one_time = TRUE;
1782
PR1 = IR2 = 1; /* invalidate PR1,IR2 */
1784
for (i = IN_SW4 + 1; i < NUM_IND; i++) ind[i] = 0; /* init indicators */
1785
if (cpu_unit.flags & IF_IA) iae = 1; /* indirect enabled? */
1787
idxe = idxb = 0; /* indexing off */
1788
pcq_r = find_reg ("PCQ", NULL, dptr); /* init old PC queue */
1789
if (pcq_r) pcq_r->qptr = 0;
1790
else return SCPE_IERR;
1791
sim_brk_types = sim_brk_dflt = SWMASK ('E'); /* init breakpoints */
1792
upd_ind (); /* update indicators */
1793
if (one_time) cpu_set_table (&cpu_unit, 1, NULL, NULL); /* set default tables */
1798
/* Memory examine */
1800
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
1802
if (addr >= MEMSIZE) return SCPE_NXM;
1803
if (vptr != NULL) *vptr = M[addr] & (FLAG | DIGIT);
1807
/* Memory deposit */
1809
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
1811
if (addr >= MEMSIZE) return SCPE_NXM;
1812
M[addr] = val & (FLAG | DIGIT);
1816
/* Memory size change */
1818
t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
1823
if ((val <= 0) || (val > MAXMEMSIZE) || ((val % 1000) != 0))
1825
for (i = val; i < MEMSIZE; i++) mc = mc | M[i];
1826
if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))
1829
for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0;
1835
t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc)
1837
if (val) cpu_unit.flags = (cpu_unit.flags & (UNIT_SCP | UNIT_BCD | MII_OPT)) |
1838
IF_DIV | IF_IA | IF_EDT;
1839
else cpu_unit.flags = cpu_unit.flags & (UNIT_SCP | UNIT_BCD | MI_OPT);
1843
/* Set/clear Model 1 option */
1845
t_stat cpu_set_opt1 (UNIT *uptr, int32 val, char *cptr, void *desc)
1847
if (cpu_unit.flags & IF_MII) {
1848
printf ("Feature is standard on 1620 Model 2\n");
1849
if (sim_log) fprintf (sim_log, "Feature is standard on 1620 Model 2\n");
1850
return SCPE_NOFNC; }
1854
/* Set/clear Model 2 option */
1856
t_stat cpu_set_opt2 (UNIT *uptr, int32 val, char *cptr, void *desc)
1858
if (!(cpu_unit.flags & IF_MII)) {
1859
printf ("Feature is not available on 1620 Model 1\n");
1860
if (sim_log) fprintf (sim_log, "Feature is not available on 1620 Model 1\n");
1861
return SCPE_NOFNC; }
1865
/* Front panel save */
1867
t_stat cpu_set_save (UNIT *uptr, int32 val, char *cptr, void *desc)
1869
if (saved_PC & 1) return SCPE_NOFNC;
1874
/* Set standard add/multiply tables */
1876
t_stat cpu_set_table (UNIT *uptr, int32 val, char *cptr, void *desc)
1880
for (i = 0; i < MUL_TABLE_LEN; i++) /* set mul table */
1881
M[MUL_TABLE + i] = std_mul_table[i];
1882
if (((cpu_unit.flags & IF_MII) == 0) || val) { /* set add table */
1883
for (i = 0; i < ADD_TABLE_LEN; i++)
1884
M[ADD_TABLE + i] = std_add_table[i]; }