1 /* i1620_cpu.c: IBM 1620 CPU simulator
2 
3    Copyright (c) 2002-2008, Robert M. Supnik
4 
5    Permission is hereby granted, free of charge, to any person obtaining a
6    copy of this software and associated documentation files (the "Software"),
7    to deal in the Software without restriction, including without limitation
8    the rights to use, copy, modify, merge, publish, distribute, sublicense,
9    and/or sell copies of the Software, and to permit persons to whom the
10    Software is furnished to do so, subject to the following conditions:
11 
12    The above copyright notice and this permission notice shall be included in
13    all copies or substantial portions of the Software.
14 
15    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18    ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19    IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22    Except as contained in this notice, the name of Robert M Supnik shall not be
23    used in advertising or otherwise to promote the sale, use or other dealings
24    in this Software without prior written authorization from Robert M Supnik.
25 
26    This CPU module incorporates code and comments from the 1620 simulator by
27    Geoff Kuenning, with his permission.
28 
29    28-May-06    RMS     Fixed bug in cpu history Peter Schorn)
30    22-Sep-05    RMS     Fixed declarations (Sterling Garwood)
31    16-Aug-05    RMS     Fixed C++ declaration and cast problems
32    07-Nov-04    RMS     Added instruction history
33    26-Mar-04    RMS     Fixed warnings with -std=c99
34    02-Nov-03    RMS     Fixed bug in branch digit (Dave Babcock)
35    21-Aug-03    RMS     Fixed bug in immediate index add (Michael Short)
36    25-Apr-03    RMS     Changed t_addr to uint32 throughout
37    18-Oct-02    RMS     Fixed bugs in invalid result testing (Hans Pufal)
38 
39    The simulated register state for the IBM 1620 is:
40 
41    1620         sim     comment
42 
43    IR1          [PC]    program counter
44    IR2                  instruction register 2 (subroutine return address)
45    OR1          [QAR]   Q address
46    OR2          [PAR]   P address
47    PR1                  manual save address
48    ind[0:99]            indicators
49 
50    Additional internal registers OR3, PR2, and PR3 are not simulated.
51 
52    The IBM 1620 is a fixed instruction length, variable data length, decimal
53    data system.  Memory consists of 20000 - 60000 BCD digits, each containing
54    four bits of data and a flag.  There are no general registers; all
55    instructions are memory to memory.
56 
57    The 1620 uses a fixed, 12 digit instruction format:
58 
59         oo ppppp qqqqq
60 
61    where
62 
63         oo      =       opcode
64         ppppp   =       P (usually destination) address
65         qqqqq   =       Q (usually source) address
66 
67    Immediate instructions use the qqqqq field as the second operand.
68 
69    The 1620 Model 1 uses table lookups for add and multiply; for that reason,
70    it was nicknamed CADET (Can't Add, Doesn't Even Try).  The Model 2 does
71    adds in hardware and uses the add table memory for index registers.
72 
73    This routine is the instruction decode routine for the IBM 1620.
74    It is called from the simulator control program to execute
75    instructions in simulated memory, starting at the simulated PC.
76    It runs until 'reason' is set non-zero.
77 
78    General notes:
79 
80    1. Reasons to stop.  The simulator can be stopped by:
81 
82         HALT instruction
83         breakpoint encountered
84         illegal addresses or instruction formats
85         I/O error in I/O simulator
86 
87    2. Interrupts.  The 1620 has no interrupt structure.
88 
89    3. Non-existent memory.  On the 1620, all memory references
90       are modulo the memory size.
91 
92    4. Adding I/O devices.  These modules must be modified:
93 
94         i1620_cpu.c     add iodisp table entry
95         i1620_sys.c     add sim_devices table entry
96 */
97 
98 #include "i1620_defs.h"
99 
100 #define PCQ_SIZE        64                              /* must be 2**n */
101 #define PCQ_MASK        (PCQ_SIZE - 1)
102 #define PCQ_ENTRY       pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = saved_PC
103 
104 #define HIST_PC         0x40000000
105 #define HIST_MIN        64
106 #define HIST_MAX        65536
107 
108 typedef struct {
109     uint16              vld;
110     uint16              pc;
111     uint8               inst[INST_LEN];
112     } InstHistory;
113 
114 uint8 M[MAXMEMSIZE] = { 0 };                            /* main memory */
115 uint32 saved_PC = 0;                                    /* saved PC */
116 uint32 IR2 = 1;                                         /* inst reg 2 */
117 uint32 PAR = 0;                                         /* P address */
118 uint32 QAR = 0;                                         /* Q address */
119 uint32 PR1 = 1;                                         /* proc reg 1 */
120 uint32 iae = 1;                                         /* ind addr enb */
121 uint32 idxe = 0;                                        /* index enable */
122 uint32 idxb = 0;                                        /* index band */
123 uint32 io_stop = 1;                                     /* I/O stop */
124 uint32 ar_stop = 1;                                     /* arith stop */
125 int32 ind_max = 16;                                     /* iadr nest limit */
126 uint16 pcq[PCQ_SIZE] = { 0 };                           /* PC queue */
127 int32 pcq_p = 0;                                        /* PC queue ptr */
128 REG *pcq_r = NULL;                                      /* PC queue reg ptr */
129 int32 hst_p = 0;                                        /* history pointer */
130 int32 hst_lnt = 0;                                      /* history length */
131 InstHistory *hst = NULL;                                /* instruction history */
132 uint8 ind[NUM_IND] = { 0 };                             /* indicators */
133 
134 extern int32 sim_int_char;
135 extern int32 sim_interval;
136 extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */
137 extern FILE *sim_log;
138 
139 t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
140 t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
141 t_stat cpu_reset (DEVICE *dptr);
142 t_stat cpu_set_opt1 (UNIT *uptr, int32 val, char *cptr, void *desc);
143 t_stat cpu_set_opt2 (UNIT *uptr, int32 val, char *cptr, void *desc);
144 t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc);
145 t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
146 t_stat cpu_set_save (UNIT *uptr, int32 val, char *cptr, void *desc);
147 t_stat cpu_set_table (UNIT *uptr, int32 val, char *cptr, void *desc);
148 t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc);
149 t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc);
150 
151 int32 get_2d (uint32 ad);
152 t_stat get_addr (uint32 alast, int32 lnt, t_bool indexok, uint32 *addr);
153 t_stat cvt_addr (uint32 alast, int32 lnt, t_bool signok, int32 *val);
154 t_stat get_idx (uint32 aidx);
155 t_stat xmt_field (uint32 d, uint32 s, uint32 skp);
156 t_stat xmt_record (uint32 d, uint32 s, t_bool cpy);
157 t_stat xmt_index (uint32 d, uint32 s);
158 t_stat xmt_divd (uint32 d, uint32 s);
159 t_stat xmt_tns (uint32 d, uint32 s);
160 t_stat xmt_tnf (uint32 d, uint32 s);
161 t_stat add_field (uint32 d, uint32 s, t_bool sub, t_bool sto, uint32 skp, int32 *sta);
162 uint32 add_one_digit (uint32 dst, uint32 src, uint32 *cry);
163 t_stat mul_field (uint32 mpc, uint32 mpy);
164 t_stat mul_one_digit (uint32 mpyd, uint32 mpcp, uint32 prop, uint32 last);
165 t_stat div_field (uint32 dvd, uint32 dvr, int32 *ez);
166 t_stat div_one_digit (uint32 dvd, uint32 dvr, uint32 max, uint32 *quod, uint32 *quop);
167 t_stat oct_to_dec (uint32 tbl, uint32 s);
168 t_stat dec_to_oct (uint32 d, uint32 tbl, int32 *ez);
169 t_stat or_field (uint32 d, uint32 s);
170 t_stat and_field (uint32 d, uint32 s);
171 t_stat xor_field (uint32 d, uint32 s);
172 t_stat com_field (uint32 d, uint32 s);
173 void upd_ind (void);
174 
175 extern t_stat tty (uint32 op, uint32 pa, uint32 f0, uint32 f1);
176 extern t_stat ptp (uint32 op, uint32 pa, uint32 f0, uint32 f1);
177 extern t_stat ptr (uint32 op, uint32 pa, uint32 f0, uint32 f1);
178 extern t_stat cdp (uint32 op, uint32 pa, uint32 f0, uint32 f1);
179 extern t_stat cdr (uint32 op, uint32 pa, uint32 f0, uint32 f1);
180 extern t_stat dp (uint32 op, uint32 pa, uint32 f0, uint32 f1);
181 extern t_stat lpt (uint32 op, uint32 pa, uint32 f0, uint32 f1);
182 extern t_stat btp (uint32 op, uint32 pa, uint32 f0, uint32 f1);
183 extern t_stat btr (uint32 op, uint32 pa, uint32 f0, uint32 f1);
184 
185 extern t_stat fp_add (uint32 d, uint32 s, t_bool sub);
186 extern t_stat fp_mul (uint32 d, uint32 s);
187 extern t_stat fp_div (uint32 d, uint32 s);
188 extern t_stat fp_fsl (uint32 d, uint32 s);
189 extern t_stat fp_fsr (uint32 d, uint32 s);
190 
191 /* CPU data structures
192 
193    cpu_dev      CPU device descriptor
194    cpu_unit     CPU unit descriptor
195    cpu_reg      CPU register list
196    cpu_mod      CPU modifier list
197 */
198 
199 UNIT cpu_unit = { UDATA (NULL, UNIT_FIX+UNIT_BCD+MI_STD, MAXMEMSIZE) };
200 
201 REG cpu_reg[] = {
202     { DRDATA (PC, saved_PC, 16), PV_LEFT },
203     { DRDATA (IR2, IR2, 16), PV_LEFT },
204     { DRDATA (PR1, PR1, 16), PV_LEFT },
205     { DRDATA (PAR, PAR, 16), PV_LEFT + REG_RO },
206     { DRDATA (QAR, QAR, 16), PV_LEFT + REG_RO },
207     { FLDATA (SW1, ind[IN_SW1], 0) },
208     { FLDATA (SW2, ind[IN_SW2], 0) },
209     { FLDATA (SW3, ind[IN_SW3], 0) },
210     { FLDATA (SW4, ind[IN_SW4], 0) },
211     { FLDATA (HP, ind[IN_HP], 0) },
212     { FLDATA (EZ, ind[IN_EZ], 0) },
213     { FLDATA (OVF, ind[IN_OVF], 0) },
214     { FLDATA (EXPCHK, ind[IN_EXPCHK], 0) },
215     { FLDATA (RDCHK, ind[IN_RDCHK], 0) },
216     { FLDATA (WRCHK, ind[IN_WRCHK], 0) },
217     { FLDATA (ARSTOP, ar_stop, 0) },
218     { FLDATA (IOSTOP, io_stop, 0) },
219     { BRDATA (IND, ind, 10, 1, NUM_IND) },
220     { FLDATA (IAE, iae, 0) },
221     { FLDATA (IDXE, idxe, 0) },
222     { FLDATA (IDXB, idxb, 0) },
223     { DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT },
224     { BRDATA (PCQ, pcq, 10, 14, PCQ_SIZE), REG_RO+REG_CIRC },
225     { ORDATA (PCQP, pcq_p, 6), REG_HRO },
226     { ORDATA (WRU, sim_int_char, 8) },
227     { NULL }
228     };
229 
230 MTAB cpu_mod[] = {
231     { IF_IA, IF_IA, "IA", "IA", &cpu_set_opt1 },
232     { IF_IA, 0, "no IA", "NOIA", &cpu_set_opt1 },
233     { IF_EDT, IF_EDT, "EDT", "EDT", &cpu_set_opt1 },
234     { IF_EDT, 0, "no EDT", "NOEDT", &cpu_set_opt1 },
235     { IF_DIV, IF_DIV, "DIV", "DIV", &cpu_set_opt1 },
236     { IF_DIV, 0, "no DIV", "NODIV", &cpu_set_opt1 },
237     { IF_FP, IF_FP, "FP", "FP", NULL },
238     { IF_FP, 0, "no FP", "NOFP", NULL },
239     { IF_BIN, IF_BIN, "BIN", "BIN", &cpu_set_opt2 },
240     { IF_BIN, 0, "no BIN", "NOBIN", &cpu_set_opt2 },
241     { IF_IDX, IF_IDX, "IDX", "IDX", &cpu_set_opt2 },
242     { IF_IDX, 0, "no IDX", "NOIDX", &cpu_set_opt2 },
243     { IF_MII, IF_MII, "Model 2", "MOD2", &cpu_set_model },
244     { IF_MII, 0, "Model 1", "MOD1", &cpu_set_model },
245     { UNIT_MSIZE, 20000, NULL, "20K", &cpu_set_size },
246     { UNIT_MSIZE, 40000, NULL, "40K", &cpu_set_size },
247     { UNIT_MSIZE, 60000, NULL, "60K", &cpu_set_size },
248     { UNIT_MSIZE, 0, NULL, "SAVE", &cpu_set_save },
249     { UNIT_MSIZE, 0, NULL, "TABLE", &cpu_set_table },
250     { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY",
251       &cpu_set_hist, &cpu_show_hist },
252     { 0 }
253     };
254 
255 DEVICE cpu_dev = {
256     "CPU", &cpu_unit, cpu_reg, cpu_mod,
257     1, 10, 18, 1, 16, 5,
258     &cpu_ex, &cpu_dep, &cpu_reset,
259     NULL, NULL, NULL
260     };
261 
262 /* Instruction table */
263 
264 const int32 op_table[100] = {
265     0,                                                  /* 0 */
266     IF_FP + IF_VPA + IF_VQA,                            /* FADD */
267     IF_FP + IF_VPA + IF_VQA,                            /* FSUB */
268     IF_FP + IF_VPA + IF_VQA,                            /* FMUL */
269     0,
270     IF_FP + IF_VPA + IF_VQA,                            /* FSL */
271     IF_FP + IF_MII + IF_VPA + IF_VQA,                   /* TFL */
272     IF_FP + IF_MII + IF_VPA + IF_VQA,                   /* BTFL */
273     IF_FP + IF_VPA + IF_VQA,                            /* FSR */
274     IF_FP + IF_VPA + IF_VQA,                            /* FDV */
275     IF_MII + IF_VPA + IF_IMM,                           /* 10: BTAM */
276     IF_VPA + IF_IMM,                                    /* AM */
277     IF_VPA + IF_IMM,                                    /* SM */
278     IF_VPA + IF_IMM,                                    /* MM */
279     IF_VPA + IF_IMM,                                    /* CM */
280     IF_VPA + IF_IMM,                                    /* TDM */
281     IF_VPA + IF_IMM,                                    /* TFM */
282     IF_VPA + IF_IMM,                                    /* BTM */
283     IF_DIV + IF_VPA + IF_IMM,                           /* LDM */
284     IF_DIV + IF_VPA + IF_IMM,                           /* DM */
285     IF_MII + IF_VPA + IF_VQA,                           /* 20: BTA */
286     IF_VPA + IF_VQA,                                    /* A */
287     IF_VPA + IF_VQA,                                    /* S */
288     IF_VPA + IF_VQA,                                    /* M */
289     IF_VPA + IF_VQA,                                    /* C */
290     IF_VPA + IF_VQA,                                    /* TD */
291     IF_VPA + IF_VQA,                                    /* TF */
292     IF_VPA + IF_VQA,                                    /* BT */
293     IF_DIV + IF_VPA + IF_VQA,                           /* LD */
294     IF_DIV + IF_VPA + IF_VQA,                           /* D */
295     IF_MII + IF_VPA + IF_VQA,                           /* 30: TRNM */
296     IF_VPA + IF_VQA,                                    /* TR */
297     IF_VPA,                                             /* SF */
298     IF_VPA,                                             /* CF */
299     IF_VPA,                                             /* K */
300     IF_VPA,                                             /* DN */
301     IF_VPA,                                             /* RN */
302     IF_VPA,                                             /* RA */
303     IF_VPA,                                             /* WN */
304     IF_VPA,                                             /* WA */
305     0,                                                  /* 40 */
306     0,                                                  /* NOP */
307     0,                                                  /* BB */
308     IF_VPA + IF_VQA,                                    /* BD */
309     IF_VPA + IF_VQA,                                    /* BNF */
310     IF_VPA + IF_VQA,                                    /* BNR */
311     IF_VPA,                                             /* BI */
312     IF_VPA,                                             /* BNI */
313     0,                                                  /* H */
314     IF_VPA,                                             /* B */
315     0,                                                  /* 50 */
316     0,
317     0,
318     0,
319     0,
320     IF_VPA + IF_VQA,                                    /* BNG - disk sys */
321     0,
322     0,
323     0,
324     0,
325     IF_MII + IF_VPA,                                    /* 60: BS */
326     IF_IDX + IF_VPA + IF_NQX,                           /* BX */
327     IF_IDX + IF_VPA + IF_IMM,                           /* BXM */
328     IF_IDX + IF_VPA + IF_NQX,                           /* BCX */
329     IF_IDX + IF_VPA + IF_IMM,                           /* BCXM */
330     IF_IDX + IF_VPA + IF_NQX,                           /* BLX */
331     IF_IDX + IF_VPA + IF_IMM,                           /* BLXM */
332     IF_IDX + IF_VPA + IF_NQX,                           /* BSX */
333     0,
334     0,
335     IF_IDX + IF_VPA + IF_VQA,                           /* 70: MA */
336     IF_EDT + IF_VPA + IF_VQA,                           /* MF */
337     IF_EDT + IF_VPA + IF_VQA,                           /* MF */
338     IF_EDT + IF_VPA + IF_VQA,                           /* TNF */
339     0,
340     0,
341     0,
342     0,
343     0,
344     0,
345     0,                                                  /* 80 */
346     0,
347     0,
348     0,
349     0,
350     0,
351     0,
352     0,
353     0,
354     0,
355     IF_BIN + IF_VPA + IF_4QA,                           /* 90: BBT */
356     IF_BIN + IF_VPA + IF_4QA,                           /* BMK */
357     IF_BIN + IF_VPA + IF_VQA,                           /* ORF */
358     IF_BIN + IF_VPA + IF_VQA,                           /* ANDF */
359     IF_BIN + IF_VPA + IF_VQA,                           /* CPLF */
360     IF_BIN + IF_VPA + IF_VQA,                           /* EORF */
361     IF_BIN + IF_VPA + IF_VQA,                           /* OTD */
362     IF_BIN + IF_VPA + IF_VQA,                           /* DTO */
363     0,
364     0
365     };
366 
367 /* IO dispatch table */
368 
369 t_stat (*iodisp[NUM_IO])(uint32 op, uint32 pa, uint32 f0, uint32 f1) = {
370     NULL, &tty, &ptp, &ptr, &cdp,                       /* 00 - 09 */
371     &cdr, NULL, &dp,  NULL, &lpt,
372     NULL, NULL, NULL, NULL, NULL,                       /* 10 - 19 */
373     NULL, NULL, NULL, NULL, NULL,
374     NULL, NULL, NULL, NULL, NULL,                       /* 20 - 29 */
375     NULL, NULL, NULL, NULL, NULL,
376     NULL, NULL, &btp, &btr, NULL,                       /* 30 - 39 */
377     NULL, NULL, NULL, NULL, NULL,
378     NULL, NULL, NULL, NULL, NULL,                       /* 40 - 49 */
379     NULL, NULL, NULL, NULL, NULL,
380     NULL, NULL, NULL, NULL, NULL,                       /* 50 - 59 */
381     NULL, NULL, NULL, NULL, NULL,
382     NULL, NULL, NULL, NULL, NULL,                       /* 60 - 69 */
383     NULL, NULL, NULL, NULL, NULL,
384     NULL, NULL, NULL, NULL, NULL,                       /* 70 - 79 */
385     NULL, NULL, NULL, NULL, NULL,
386     NULL, NULL, NULL, NULL, NULL,                       /* 80 - 89 */
387     NULL, NULL, NULL, NULL, NULL,
388     NULL, NULL, NULL, NULL, NULL,                       /* 90 - 99 */
389     NULL, NULL, NULL, NULL, NULL
390     };
391 
392 /* Indicator table: -1 = illegal, +1 = resets when tested */
393 
394 const int32 ind_table[NUM_IND] = {
395     -1,  0,  0,  0,  0, -1,  1,  1, -1,  1,             /* 00 - 09 */
396     -1,  0,  0,  0,  1,  1,  1,  1, -1,  0,             /* 10 - 19 */
397     -1, -1, -1, -1, -1,  0, -1, -1, -1, -1,             /* 20 - 29 */
398      0,  0,  0,  1,  1,  0,  1,  1,  1,  0,             /* 30 - 39 */
399     -1, -1,  1, -1, -1, -1, -1, -1, -1, -1,             /* 40 - 49 */
400     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,             /* 50 - 59 */
401     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,             /* 60 - 69 */
402     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,             /* 70 - 79 */
403     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,             /* 80 - 89 */
404     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1              /* 90 - 99 */
405     };
406 
407 /* Add table for 1620 Model 1 */
408 
409 const uint8 std_add_table[ADD_TABLE_LEN] = {
410     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
411     0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
412     0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11,
413     0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12,
414     0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13,
415     0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14,
416     0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
417     0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
418     0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
419     0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18
420     };
421 
422 /* Add table for 1620 Model 2 ("hardware add") */
423 
424 const uint8 sum_table[20] = {
425     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
426     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19
427     };
428 
429 /* Multiply table */
430 
431 const uint8 std_mul_table[MUL_TABLE_LEN] = {
432     0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
433     0, 0, 1, 0, 2, 0, 3, 0, 4, 0,
434     0, 0, 2, 0, 4, 0, 6, 0, 8, 0,
435     0, 0, 3, 0, 6, 0, 9, 0, 2, 1,
436     0, 0, 4, 0, 8, 0, 2, 1, 6, 1,
437     0, 0, 5, 0, 0, 1, 5, 1, 0, 2,
438     0, 0, 6, 0, 2, 1, 8, 1, 4, 2,
439     0, 0, 7, 0, 4, 1, 1, 2, 8, 2,
440     0, 0, 8, 0, 6, 1, 4, 2, 2, 3,
441     0, 0, 9, 0, 8, 1, 7, 2, 6, 3,
442     0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
443     5, 0, 6, 0, 7, 0, 8, 0, 9, 0,
444     0, 1, 2, 1, 4, 1, 6, 1, 8, 1,
445     5, 1, 8, 1, 1, 2, 4, 2, 7, 2,
446     0, 2, 4, 2, 8, 2, 2, 3, 6, 3,
447     5, 2, 0, 3, 5, 3, 0, 4, 5, 4,
448     0, 3, 6, 3, 2, 4, 8, 4, 4, 5,
449     5, 3, 2, 4, 9, 4, 6, 5, 3, 6,
450     0, 4, 8, 4, 6, 5, 4, 6, 2, 7,
451     5, 4, 4, 5, 3, 6, 2, 7, 1, 8
452     };
453 
454 #define BRANCH(x)       PCQ_ENTRY; PC = (x)
455 #define GET_IDXADDR(x)  ((idxb? IDX_B: IDX_A) + ((x) * ADDR_LEN) + (ADDR_LEN - 1))
456 
sim_instr(void)457 t_stat sim_instr (void)
458 {
459 uint32 PC, pla, qla, f0, f1;
460 int32 i, t, idx, flags, sta, dev, op;
461 t_stat reason;
462 
463 /* Restore saved state */
464 
465 PC = saved_PC;
466 if ((cpu_unit.flags & IF_IA) == 0)
467     iae = 0;
468 if ((cpu_unit.flags & IF_IDX) == 0)
469     idxe = idxb = 0;
470 upd_ind ();                                             /* update indicators */
471 reason = 0;
472 
473 /* Main instruction fetch/decode loop */
474 
475 while (reason == 0) {                                   /* loop until halted */
476 
477     saved_PC = PC;                                      /* commit prev instr */
478     if (sim_interval <= 0) {                            /* check clock queue */
479         if ((reason = sim_process_event ()))
480             break;
481         }
482 
483     if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */
484         reason = STOP_IBKPT;                            /* stop simulation */
485         break;
486 		}
487 
488     sim_interval = sim_interval - 1;
489 
490 /* Instruction fetch and address decode */
491 
492     if (PC & 1) {                                       /* PC odd? */
493         reason = STOP_INVIAD;                           /* stop */
494         break;
495         }
496 
497     op = get_2d (PC);                                   /* get opcode */
498     if (op < 0) {                                       /* invalid? */
499         reason = STOP_INVINS;
500         break;
501         }
502     flags = op_table[op];                               /* get op, flags */
503     if ((flags & ALLOPT) &&                             /* need option? */
504         !(flags & ALLOPT & cpu_unit.flags)) {           /* any set? */
505         reason = STOP_INVINS;                           /* no, error */
506         break;
507         }
508 
509     pla = ADDR_A (PC, I_PL);                            /* P last addr */
510     qla = ADDR_A (PC, I_QL);                            /* Q last addr */
511     if (flags & IF_VPA) {                               /* need P? */
512         reason = get_addr (pla, 5, TRUE, &PAR);         /* get P addr */
513         if (reason != SCPE_OK)                          /* stop if error */
514             break;
515         }
516     if (flags & (IF_VQA | IF_4QA | IF_NQX)) {           /* need Q? */
517         reason = get_addr (qla,                         /* get Q addr */
518             ((flags & IF_4QA)? 4: 5),                   /* 4 or 5 digits */
519             ((flags & IF_NQX)? FALSE: TRUE),            /* not or indexed */
520             &QAR);
521         if (reason != SCPE_OK) {                        /* stop if invalid */
522             reason = reason + (STOP_INVQDG - STOP_INVPDG);
523             break;
524             }
525         }
526     else if (flags & IF_IMM)                             /* immediate? */
527         QAR = qla;
528 
529     if (hst_lnt) {                                      /* history enabled? */
530         hst_p = (hst_p + 1);                            /* next entry */
531         if (hst_p >= hst_lnt)
532             hst_p = 0;
533         hst[hst_p].vld = 1;
534         hst[hst_p].pc = PC;
535         for (i = 0; i < INST_LEN; i++)
536             hst[hst_p].inst[i] = M[(PC + i) % MEMSIZE];
537         }
538 
539     PC = PC + INST_LEN;                                 /* advance PC */
540     switch (op) {                                       /* case on op */
541 
542 /* Transmit digit - P,Q are valid */
543 
544     case OP_TD:
545     case OP_TDM:
546         M[PAR] = M[QAR] & (FLAG | DIGIT);               /* move dig, flag */
547         break;
548 
549 /* Transmit field - P,Q are valid */
550 
551     case OP_TF:
552     case OP_TFM:
553         reason = xmt_field (PAR, QAR, 1);               /* xmit field */
554         break;
555 
556 /* Transmit record - P,Q are valid */
557 
558     case OP_TR:
559         reason = xmt_record (PAR, QAR, TRUE);           /* xmit record */
560         break;
561 
562 /* Transmit record no record mark - P,Q are valid */
563 
564     case OP_TRNM:
565         reason = xmt_record (PAR, QAR, FALSE);          /* xmit record but */
566         break;                                          /* not rec mark */
567 
568 /* Set flag - P is valid */
569 
570     case OP_SF:
571         M[PAR] = M[PAR] | FLAG;                         /* set flag on P */
572         break;
573 
574 /* Clear flag - P is valid */
575 
576     case OP_CF:
577         M[PAR] = M[PAR] & ~FLAG;                        /* clear flag on P */
578         break;
579 
580 /* Branch - P is valid */
581 
582     case OP_B:
583         BRANCH (PAR);                                   /* branch to P */
584         break;
585 
586 /* Branch and transmit - P,Q are valid */
587 
588     case OP_BT:
589     case OP_BTM:
590         reason = xmt_field (ADDR_S (PAR, 1), QAR, 1);   /* xmit field to P-1 */
591         IR2 = PC;                                       /* save PC */
592         BRANCH (PAR);                                   /* branch to P */
593         break;
594 
595 /* Branch and transmit floating - P,Q are valid */
596 
597     case OP_BTFL:
598         reason = xmt_field (ADDR_S (PAR, 1), QAR, 3);   /* skip 3 flags */
599         IR2 = PC;                                       /* save PC */
600         BRANCH (PAR);                                   /* branch to P */
601         break;
602 
603 /* Branch and transmit address - P,Q are valid */
604 
605     case OP_BTA:
606     case OP_BTAM:
607         reason = xmt_field (ADDR_S (PAR, 1), QAR, 4);   /* skip 4 flags */
608         IR2 = PC;                                       /* save PC */
609         BRANCH (PAR);                                   /* branch to P */
610         break;
611 
612 /* Branch back */
613 
614     case OP_BB:
615         if (PR1 != 1) {                                 /* PR1 valid? */
616             BRANCH (PR1);                               /* return to PR1 */
617             PR1 = 1;                                    /* invalidate */
618             }
619         else if (IR2 != 1) {                            /* IR2 valid? */
620             BRANCH (IR2);                               /* return to IR2 */
621             IR2 = 1;                                    /* invalidate */
622             }
623         else reason = STOP_INVRTN;                      /* MAR check */
624         break;
625 
626 /* Branch on digit (not zero) - P,Q are valid */
627 
628     case OP_BD:
629         if ((M[QAR] & DIGIT) != 0) {                    /* digit != 0? */
630             BRANCH (PAR);                               /* branch */
631             }
632         break;
633 
634 /* Branch no flag - P,Q are valid */
635 
636     case OP_BNF:
637         if ((M[QAR] & FLAG) == 0) {                     /* flag == 0? */
638             BRANCH (PAR);                               /* branch */
639             }
640         break;
641 
642 /* Branch no record mark (8-2 not set) - P,Q are valid */
643 
644     case OP_BNR:
645         if ((M[QAR] & REC_MARK) != REC_MARK) {          /* not rec mark? */
646             BRANCH (PAR);                               /* branch */
647             }
648         break;
649 
650 /* Branch no group mark - P,Q are valid */
651 
652     case OP_BNG:
653         if ((M[QAR] & DIGIT) != GRP_MARK) {             /* not grp mark? */
654             BRANCH (PAR);                               /* branch */
655             }
656         break;
657 
658 /* Branch (no) indicator - P is valid */
659 
660     case OP_BI:
661     case OP_BNI:
662         upd_ind ();                                     /* update indicators */
663         t = get_2d (ADDR_A (saved_PC, I_BR));           /* get ind number */
664         if ((t < 0) || (ind_table[t] < 0)) {            /* not valid? */
665             reason = STOP_INVIND;                       /* stop */
666             break;
667             }
668         if ((ind[t] != 0) ^ (op == OP_BNI)) {           /* ind value correct? */
669             BRANCH (PAR);                               /* branch */
670             }
671         if (ind_table[t] > 0)                           /* reset if needed */
672             ind[t] = 0;
673         break;
674 
675 /* Add/subtract/compare - P,Q are valid */
676 
677     case OP_A:
678     case OP_AM:
679         reason = add_field (PAR, QAR, FALSE, TRUE, 0, &sta); /* add, store */
680         if (sta == ADD_CARRY)                           /* cout => ovflo */
681             ind[IN_OVF] = 1;
682         if (ar_stop && ind[IN_OVF])
683             reason = STOP_OVERFL;
684         break;
685 
686     case OP_S:
687     case OP_SM:
688         reason = add_field (PAR, QAR, TRUE, TRUE, 0, &sta); /* sub, store */
689         if (sta == ADD_CARRY)                           /* cout => ovflo */
690             ind[IN_OVF] = 1;
691         if (ar_stop && ind[IN_OVF])
692             reason = STOP_OVERFL;
693         break;
694 
695     case OP_C:
696     case OP_CM:
697         reason = add_field (PAR, QAR, TRUE, FALSE, 0, &sta); /* sub, nostore */
698         if (sta == ADD_CARRY)                           /* cout => ovflo */
699             ind[IN_OVF] = 1;
700         if (ar_stop && ind[IN_OVF])
701             reason = STOP_OVERFL;
702         break;
703 
704 /* Multiply - P,Q are valid */
705 
706     case OP_M:
707     case OP_MM:
708         reason = mul_field (PAR, QAR);                  /* multiply */
709         break;
710 
711 /* IO instructions - P is valid */
712 
713     case OP_RA:
714     case OP_WA:
715         if ((PAR & 1) == 0) {                           /* P even? */
716             reason = STOP_INVEAD;                       /* stop */
717             break;
718             }
719     case OP_K:
720     case OP_DN:
721     case OP_RN:
722     case OP_WN:
723         dev = get_2d (ADDR_A (saved_PC, I_IO));         /* get IO dev */
724         f0 = M[ADDR_A (saved_PC, I_CTL)] & DIGIT;       /* get function */
725         f1 = M[ADDR_A (saved_PC, I_CTL + 1)] & DIGIT;
726         if ((dev < 0) || (iodisp[dev] == NULL))         /* undefined dev? */
727             reason = STOP_INVIO;                        /* stop */
728         else reason = iodisp[dev] (op, PAR, f0, f1);    /* call device */
729         break;
730 
731 /* Divide special feature instructions */
732 
733     case OP_LD:
734     case OP_LDM:
735         for (i = 0; i < PROD_AREA_LEN; i++)             /* clear prod area */
736             M[PROD_AREA + i] = 0;
737         t = M[QAR] & FLAG;                              /* save Q sign */
738         reason = xmt_divd (PAR, QAR);                   /* xmit dividend */
739         M[PROD_AREA + PROD_AREA_LEN - 1] |= t;          /* set sign */
740         break;
741 
742 /* Divide - P,Q are valid */
743 
744     case OP_D:
745     case OP_DM:
746         reason = div_field (PAR, QAR, &t);              /* divide */
747         ind[IN_EZ] = t;                                 /* set indicator */
748         if ((reason == STOP_OVERFL) && !ar_stop)        /* ovflo stop? */
749             reason = SCPE_OK;                           /* no */
750         break;
751 
752 /* Edit special feature instructions */
753 
754 /* Move flag - P,Q are valid */
755 
756     case OP_MF:
757         M[PAR] = (M[PAR] & ~FLAG) | (M[QAR] & FLAG);    /* copy Q flag */
758         M[QAR] = M[QAR] & ~FLAG;                        /* clr Q flag */
759         break;
760 
761 /* Transmit numeric strip - P,Q are valid, P is source */
762 
763     case OP_TNS:
764         if ((PAR & 1) == 0) {                           /* P must be odd */
765             reason = STOP_INVEAD;
766             break;
767             }
768         reason = xmt_tns (QAR, PAR);                    /* xmit and strip */
769         break;
770 
771 /* Transmit numeric fill - P,Q are valid */
772 
773     case OP_TNF:
774         if ((PAR & 1) == 0) {                           /* P must be odd */
775             reason = STOP_INVEAD;
776             break;
777             }
778         reason = xmt_tnf (PAR, QAR);                    /* xmit and strip */
779         break;
780 
781 /* Index special feature instructions */
782 
783 /* Move address - P,Q are valid */
784 
785     case OP_MA:
786         for (i = 0; i < ADDR_LEN; i++) {                /* move 5 digits */
787             M[PAR] = (M[PAR] & FLAG) | (M[QAR] & DIGIT);
788             MM (PAR); MM (QAR);
789             }
790         break;
791 
792 /* Branch load index - P,Q are valid, Q not indexed */
793 
794     case OP_BLX:
795     case OP_BLXM:
796         idx = get_idx (ADDR_A (saved_PC, I_QL - 1));    /* get index */
797         if (idx < 0) {                                  /* disabled? */
798             reason = STOP_INVIDX;                       /* stop */
799             break;
800             }
801         xmt_index (GET_IDXADDR (idx), QAR);             /* copy Q to idx */
802         BRANCH (PAR);                                   /* branch to P */
803         break;
804 
805 /* Branch store index - P,Q are valid, Q not indexed */
806 
807     case OP_BSX:
808         idx = get_idx (ADDR_A (saved_PC, I_QL - 1));    /* get index */
809         if (idx < 0) {                                  /* disabled? */
810             reason = STOP_INVIDX;                       /* stop */
811             break;
812             }
813         xmt_index (QAR, GET_IDXADDR (idx));             /* copy idx to Q */
814         BRANCH (PAR);                                   /* branch to P */
815         break;
816 
817 /* Branch and modify index - P,Q are valid, Q not indexed */
818 
819     case OP_BX:
820         idx = get_idx (ADDR_A (saved_PC, I_QL - 1));    /* get index */
821         if (idx < 0) {                                  /* disabled? */
822             reason = STOP_INVIDX;                       /* stop */
823             break;
824             }
825         reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 0, &sta);
826         if (ar_stop && ind[IN_OVF])
827             reason = STOP_OVERFL;
828         BRANCH (PAR);                                   /* branch to P */
829         break;
830 
831     case OP_BXM:
832         idx = get_idx (ADDR_A (saved_PC, I_QL - 1));    /* get index */
833         if (idx < 0) {                                  /* disabled? */
834             reason = STOP_INVIDX;                       /* stop */
835             break;
836             }
837         reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 3, &sta);
838         if (ar_stop && ind[IN_OVF])
839             reason = STOP_OVERFL;
840         BRANCH (PAR);                                   /* branch to P */
841         break;
842 
843 /* Branch conditionally and modify index - P,Q are valid, Q not indexed */
844 
845     case OP_BCX:
846         idx = get_idx (ADDR_A (saved_PC, I_QL - 1));    /* get index */
847         if (idx < 0) {                                  /* disabled? */
848             reason = STOP_INVIDX;                       /* stop */
849             break;
850             }
851         reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 0, &sta);
852         if (ar_stop && ind[IN_OVF])
853             reason = STOP_OVERFL;
854         if ((ind[IN_EZ] == 0) && (sta == ADD_NOCRY)) {  /* ~z, ~c, ~sign chg? */
855             BRANCH (PAR);                               /* branch */
856 			}
857         break;
858 
859     case OP_BCXM:
860         idx = get_idx (ADDR_A (saved_PC, I_QL - 1));    /* get index */
861         if (idx < 0) {                                  /* disabled? */
862             reason = STOP_INVIDX;                       /* stop */
863             break;
864             }
865         reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 3, &sta);
866         if (ar_stop && ind[IN_OVF])
867             reason = STOP_OVERFL;
868         if ((ind[IN_EZ] == 0) && (sta == ADD_NOCRY)) {  /* ~z, ~c, ~sign chg? */
869             BRANCH (PAR);                               /* branch */
870 			}
871         break;
872 
873 /* Branch and select - P is valid */
874 
875     case OP_BS:
876         t = M[ADDR_A (saved_PC, I_SEL)] & DIGIT;        /* get select */
877         switch (t) {                                    /* case on select */
878         case 0:
879             idxe = idxb = 0;                            /* indexing off */
880             break;
881         case 1:
882             idxe = 1; idxb = 0;                         /* index band A */
883             break;
884         case 2:
885             idxe = idxb = 1;                            /* index band B */
886             break;
887         case 8:
888             iae = 0;                                    /* indirect off */
889             break;
890         case 9:
891             iae = 1;                                    /* indirect on */
892             break;
893         default:
894             reason = STOP_INVSEL;                       /* undefined */
895             break;
896 			}
897         BRANCH (PAR);
898         break;
899 
900 /* Binary special feature instructions */
901 
902 /* Branch on bit - P,Q are valid, Q is 4d address */
903 
904     case OP_BBT:
905         t = M[ADDR_A (saved_PC, I_Q)];                  /* get Q0 digit */
906         if (t & M[QAR] & DIGIT) {                       /* match to mem? */
907             BRANCH (PAR);                               /* branch */
908             }
909         break;
910 
911 /* Branch on mask - P,Q are valid, Q is 4d address */
912 
913     case OP_BMK:
914         t = M[ADDR_A (saved_PC, I_Q)];                  /* get Q0 digit */
915         if (((t ^ M[QAR]) &                             /* match to mem? */
916             ((t & FLAG)? (FLAG + DIGIT): DIGIT)) == 0) {
917             BRANCH (PAR);                               /* branch */
918             }
919         break;
920 
921 /* Or - P,Q are valid */
922 
923     case OP_ORF:
924         reason = or_field (PAR, QAR);                   /* OR fields */
925         break;
926 
927 /* AND - P,Q are valid */
928 
929     case OP_ANDF:
930         reason = and_field (PAR, QAR);                  /* AND fields */
931         break;
932 
933 /* Exclusive or - P,Q are valid */
934 
935     case OP_EORF:
936         reason = xor_field (PAR, QAR);                  /* XOR fields */
937         break;
938 
939 /* Complement - P,Q are valid */
940 
941     case OP_CPLF:
942         reason = com_field (PAR, QAR);                  /* COM field */
943         break;
944 
945 /* Octal to decimal - P,Q are valid */
946 
947     case OP_OTD:
948         reason = oct_to_dec (PAR, QAR);                 /* convert */
949         break;
950 
951 /* Decimal to octal - P,Q are valid */
952 
953     case OP_DTO:
954         reason = dec_to_oct (PAR, QAR, &t);             /* convert */
955         ind[IN_EZ] = t;                                 /* set indicator */
956         if (ar_stop && ind[IN_OVF])
957             reason = STOP_OVERFL;
958         break;
959 
960 /* Floating point special feature instructions */
961 
962     case OP_FADD:
963         reason = fp_add (PAR, QAR, FALSE);              /* add */
964         if (ar_stop && ind[IN_EXPCHK])
965             reason = STOP_EXPCHK;
966         break;
967 
968     case OP_FSUB:
969         reason = fp_add (PAR, QAR, TRUE);               /* subtract */
970         if (ar_stop && ind[IN_EXPCHK])
971             reason = STOP_EXPCHK;
972         break;
973 
974     case OP_FMUL:
975         reason = fp_mul (PAR, QAR);                     /* multiply */
976         if (ar_stop && ind[IN_EXPCHK])
977             reason = STOP_EXPCHK;
978         break;
979 
980     case OP_FDIV:
981         reason = fp_div (PAR, QAR);                     /* divide */
982         if (ar_stop && ind[IN_OVF])
983             reason = STOP_FPDVZ;
984         if (ar_stop && ind[IN_EXPCHK])
985             reason = STOP_EXPCHK;
986         break;
987 
988     case OP_FSL:
989         reason = fp_fsl (PAR, QAR);                     /* shift left */
990         break;
991 
992     case OP_FSR:
993         reason = fp_fsr (PAR, QAR);                     /* shift right */
994         break;
995 
996 /* Halt */
997 
998     case OP_H:
999         saved_PC = PC;                                  /* commit inst */
1000         reason = STOP_HALT;                             /* stop */
1001         break;
1002 
1003 /* NOP */
1004 
1005     case OP_NOP:
1006         break;
1007 
1008 /* Invalid instruction code */
1009 
1010     default:
1011         reason = STOP_INVINS;                           /* stop */
1012         break;
1013         }                                               /* end switch */
1014     }                                                   /* end while */
1015 
1016 /* Simulation halted */
1017 
1018 pcq_r->qptr = pcq_p;                                    /* update pc q ptr */
1019 upd_ind ();
1020 return reason;
1021 }
1022 
1023 /* Utility routines */
1024 
1025 /* Get 2 digit field
1026 
1027    Inputs:
1028         ad      =       address of high digit
1029    Outputs:
1030         val     =       field converted to binary
1031                         -1 if bad digit
1032 */
1033 
get_2d(uint32 ad)1034 int32 get_2d (uint32 ad)
1035 {
1036 int32 d, d1;
1037 
1038 d = M[ad] & DIGIT;                                      /* get 1st digit */
1039 d1 = M[ADDR_A (ad, 1)] & DIGIT;                         /* get 2nd digit */
1040 if (BAD_DIGIT (d) || BAD_DIGIT (d1))                    /* bad? error */
1041     return -1;
1042 return ((d * 10) + d1);                                 /* cvt to binary */
1043 }
1044 
1045 /* Get address routine
1046 
1047    Inputs:
1048         alast   =       address of low digit
1049         lnt     =       length
1050         indexok =       TRUE if indexing allowed
1051         &addr   =       pointer to address output
1052    Output:
1053         return  =       error status (in terms of P address)
1054         addr    =       address converted to binary
1055 
1056    Notes:
1057    - If indexing produces a negative result, the effective address is
1058      the 10's complement of the result
1059    - An address that exceeds memory produces a MAR check stop
1060 */
1061 
get_addr(uint32 alast,int32 lnt,t_bool indexok,uint32 * reta)1062 t_stat get_addr (uint32 alast, int32 lnt, t_bool indexok, uint32 *reta)
1063 {
1064 uint8 indir;
1065 int32 cnt, idx, idxa, idxv, addr;
1066 
1067 if (iae)                                                /* init indirect */
1068     indir = FLAG;
1069 else indir = 0;
1070 
1071 cnt = 0;                                                /* count depth */
1072 do {
1073     indir = indir & M[alast];                           /* get indirect */
1074     if (cvt_addr (alast, lnt, FALSE, &addr))            /* cvt addr to bin */
1075         return STOP_INVPDG;                             /* bad? */
1076     idx = get_idx (ADDR_S (alast, 1));                  /* get index reg num */
1077     if (indexok && (idx > 0)) {                         /* indexable? */
1078         idxa = GET_IDXADDR (idx);                       /* get idx reg addr */
1079         if (cvt_addr (idxa, ADDR_LEN, TRUE, &idxv))     /* cvt idx reg */
1080             return STOP_INVPDG;
1081         addr = addr + idxv;                             /* add in index */
1082         if (addr < 0)                                   /* -? 10's comp */
1083             addr = addr + 100000;
1084         }
1085     if (addr >= (int32) MEMSIZE)                        /* invalid addr? */
1086         return STOP_INVPAD;
1087     alast = addr;                                       /* new address */
1088     lnt = ADDR_LEN;                                     /* std len */
1089     } while (indir && (cnt++ < ind_max));
1090 if (cnt > ind_max)                                      /* indir too deep? */
1091     return STOP_INVPIA;
1092 *reta = addr;                                           /* return address */
1093 return SCPE_OK;
1094 }
1095 
1096 /* Convert address to binary
1097 
1098    Inputs:
1099         alast   =       address of low digit
1100         lnt     =       length
1101         signok  =       TRUE if signed
1102         val     =       address of output
1103    Outputs:
1104         status  =       0 if ok, != 0 if error
1105 */
1106 
cvt_addr(uint32 alast,int32 lnt,t_bool signok,int32 * val)1107 t_stat cvt_addr (uint32 alast, int32 lnt, t_bool signok, int32 *val)
1108 {
1109 int32 sign = 0, addr = 0, t;
1110 
1111 if (signok && (M[alast] & FLAG))                        /* signed? */
1112     sign = 1;
1113 alast = alast - lnt;                                    /* find start */
1114 do {
1115     PP (alast);                                         /* incr mem addr */
1116     t = M[alast] & DIGIT;                               /* get digit */
1117     if (BAD_DIGIT (t))                                  /* bad? error */
1118         return STOP_INVDIG;
1119     addr = (addr * 10) + t;                             /* cvt to bin */
1120     } while (--lnt > 0);
1121 if (sign)                                               /* minus? */
1122     *val = -addr;
1123 else *val = addr;
1124 return SCPE_OK;
1125 }
1126 
1127 /* Get index register number
1128 
1129    Inputs:
1130         aidx    =       address of low digit
1131    Outputs:
1132         index   =       >0 if indexed
1133                         =0 if not indexed
1134                         <0 if indexing disabled
1135 */
1136 
get_idx(uint32 aidx)1137 t_stat get_idx (uint32 aidx)
1138 {
1139 int32 i, idx;
1140 
1141 if (idxe == 0)                                          /* indexing off? */
1142     return -1;
1143 for (i = idx = 0; i < 3; i++) {                         /* 3 flags worth */
1144     if (M[aidx] & FLAG)                                 /* test flag */
1145         idx = idx | (1 << i);
1146     MM (aidx);                                          /* next digit */
1147     }
1148 return idx;
1149 }
1150 
1151 /* Update indicators routine */
1152 
upd_ind(void)1153 void upd_ind (void)
1154 {
1155 ind[IN_HPEZ] = ind[IN_HP] | ind[IN_EZ];                 /* HPEZ = HP | EZ */
1156 ind[IN_DERR] = ind[IN_DACH] | ind[IN_DWLR] | ind[IN_DCYO];
1157 ind[IN_ANYCHK] = ind[IN_RDCHK] | ind[IN_WRCHK] |        /* ANYCHK = all chks */
1158     ind[IN_MBREVEN] | ind[IN_MBRODD] |
1159     ind[IN_PRCHK] | ind[IN_DACH];
1160 ind[IN_IXN] = ind[IN_IXA] = ind[IN_IXB] = 0;            /* clr index indics */
1161 if (!idxe)                                              /* off? */
1162     ind[IN_IXN] = 1;
1163 else if (!idxb)                                         /* on, band A? */
1164     ind[IN_IXA] = 1;
1165 else ind[IN_IXB] = 1;                                   /* no, band B */
1166 return;
1167 }
1168 
1169 /* Transmit routines */
1170 
1171 /* Transmit field from 's' to 'd' - ignore first 'skp' flags */
1172 
xmt_field(uint32 d,uint32 s,uint32 skp)1173 t_stat xmt_field (uint32 d, uint32 s, uint32 skp)
1174 {
1175 uint32 cnt = 0;
1176 uint8 t;
1177 
1178 do {
1179     t = M[d] = M[s] & (FLAG | DIGIT);                   /* copy src to dst */
1180     MM (d);                                             /* decr mem addrs */
1181     MM (s);
1182     if (cnt++ >= MEMSIZE)                               /* (stop runaway) */
1183         return STOP_FWRAP;
1184     } while (((t & FLAG) == 0) || (cnt <= skp));        /* until flag */
1185 return SCPE_OK;
1186 }
1187 
1188 /* Transmit record from 's' to 'd' - copy record mark if 'cpy' = TRUE */
1189 
xmt_record(uint32 d,uint32 s,t_bool cpy)1190 t_stat xmt_record (uint32 d, uint32 s, t_bool cpy)
1191 {
1192 uint32 cnt = 0;
1193 
1194 while ((M[s] & REC_MARK) != REC_MARK) {                 /* until rec mark */
1195     M[d] = M[s] & (FLAG | DIGIT);                       /* copy src to dst */
1196     PP (d);                                             /* incr mem addrs */
1197     PP (s);
1198     if (cnt++ >= MEMSIZE)                               /* (stop runaway) */
1199         return STOP_FWRAP;
1200     }
1201 if (cpy)                                                /* copy rec mark */
1202     M[d] = M[s] & (FLAG | DIGIT);
1203 return SCPE_OK;
1204 }
1205 
1206 /* Transmit index from 's' to 'd' - fixed five character field */
1207 
xmt_index(uint32 d,uint32 s)1208 t_stat xmt_index (uint32 d, uint32 s)
1209 {
1210 int32 i;
1211 
1212 M[d] = M[s] & (FLAG | DIGIT);                           /* preserve sign */
1213 MM (d); MM (s);                                         /* decr mem addrs */
1214 for (i = 0; i < ADDR_LEN - 2; i++) {                    /* copy 3 digits */
1215     M[d] = M[s] & DIGIT;                                /* without flags */
1216     MM (d);                                             /* decr mem addrs */
1217     MM (s);
1218     }
1219 M[d] = (M[s] & DIGIT) | FLAG;                           /* set flag on last */
1220 return SCPE_OK;
1221 }
1222 
1223 /* Transmit dividend from 'd' to 's' - clear flag on first digit */
1224 
xmt_divd(uint32 d,uint32 s)1225 t_stat xmt_divd (uint32 d, uint32 s)
1226 {
1227 uint32 cnt = 0;
1228 
1229 M[d] = M[s] & DIGIT;                                    /* first w/o flag */
1230 do {
1231     MM (d);                                             /* decr mem addrs */
1232     MM (s);
1233     M[d] = M[s] & (FLAG | DIGIT);                       /* copy src to dst */
1234     if (cnt++ >= MEMSIZE)                               /* (stop runaway) */
1235         return STOP_FWRAP;
1236     } while ((M[d] & FLAG) == 0);                       /* until src flag */
1237 return SCPE_OK;
1238 }
1239 
1240 /* Transmit numeric strip from 's' to 'd' - s is odd */
1241 
xmt_tns(uint32 d,uint32 s)1242 t_stat xmt_tns (uint32 d, uint32 s)
1243 {
1244 uint32 cnt = 0;
1245 uint8 t, z;
1246 
1247 t = M[s] & DIGIT;                                       /* get units */
1248 z = M[s - 1] & DIGIT;                                   /* get zone */
1249 if ((z == 1) || (z == 5) || ((z == 2) && (t == 0)))     /* 1x, 5x, 20? */
1250     M[d] = t | FLAG;                                    /* set flag */
1251 else M[d] = t;                                          /* else clear flag */
1252 do {
1253     MM (d);                                             /* decr mem addrs */
1254     s = ADDR_S (s, 2);
1255     t = M[d] & FLAG;                                    /* save dst flag */
1256     M[d] = M[s] & (FLAG | DIGIT);                       /* copy src to dst */
1257     if (cnt >= MEMSIZE)                                 /* (stop runaway) */
1258         return STOP_FWRAP;
1259     cnt = cnt + 2;
1260     } while (t == 0);                                   /* until dst flag */
1261 M[d] = M[d] | FLAG;                                     /* set flag at end */
1262 return SCPE_OK;
1263 }
1264 
1265 /* Transmit numeric fill from 's' to 'd' - d is odd */
1266 
xmt_tnf(uint32 d,uint32 s)1267 t_stat xmt_tnf (uint32 d, uint32 s)
1268 {
1269 uint32 cnt = 0;
1270 uint8 t;
1271 
1272 t = M[s];                                               /* get 1st digit */
1273 M[d] = t & DIGIT;                                       /* store */
1274 M[d - 1] = (t & FLAG)? 5: 7;                            /* set sign from flag */
1275 do {
1276     MM (s);                                             /* decr mem addr */
1277     d = ADDR_S (d, 2);
1278     t = M[s];                                           /* get src digit */
1279     M[d] = t & DIGIT;                                   /* move to dst, no flag */
1280     M[d - 1] = 7;                                       /* set zone */
1281     if (cnt >= MEMSIZE)                                 /* (stop runaway) */
1282         return STOP_FWRAP;
1283     cnt = cnt + 2;
1284     } while ((t & FLAG) == 0);                          /* until src flag */
1285 return SCPE_OK;
1286 }
1287 
1288 /* Add routine
1289 
1290    Inputs:
1291         d       =       destination field low (P)
1292         s       =       source field low (Q)
1293         sub     =       TRUE if subtracting
1294         sto     =       TRUE if storing
1295         skp     =       number of source field flags, beyond sign, to ignore
1296    Output:
1297         return  =       status
1298         sta     =       ADD_NOCRY: no carry out, no sign change
1299                         ADD_SCHNG: sign change
1300                         ADD_CARRY: carry out
1301 
1302    Reference Manual: "When the sum is zero, the sign of the P field
1303    is retained."
1304 */
1305 
add_field(uint32 d,uint32 s,t_bool sub,t_bool sto,uint32 skp,int32 * sta)1306 t_stat add_field (uint32 d, uint32 s, t_bool sub, t_bool sto, uint32 skp, int32 *sta)
1307 {
1308 uint32 cry, src, dst, res, comp, dp, dsv;
1309 uint32 src_f = 0, cnt = 0, dst_f;
1310 
1311 *sta = ADD_NOCRY;                                       /* assume no cry */
1312 dsv = d;                                                /* save dst */
1313 comp = ((M[d] ^ M[s]) & FLAG) ^ (sub? FLAG: 0);         /* set compl flag */
1314 cry = 0;                                                /* clr carry */
1315 ind[IN_HP] = ((M[d] & FLAG) == 0);                      /* set sign from res */
1316 ind[IN_EZ] = 1;                                         /* assume zero */
1317 
1318 dst = M[d] & DIGIT;                                     /* 1st digits */
1319 src = M[s] & DIGIT;
1320 if (BAD_DIGIT (dst) || BAD_DIGIT (src))                 /* bad digit? */
1321      return STOP_INVDIG;
1322 if (comp)                                               /* complement? */
1323     src = 10 - src;
1324 res = add_one_digit (dst, src, &cry);                   /* add */
1325 if (sto)                                                /* store */
1326     M[d] = (M[d] & FLAG) | res;
1327 MM (d); MM (s);                                         /* decr mem addrs */
1328 do {
1329     dst = M[d] & DIGIT;                                 /* get dst digit */
1330     dst_f = M[d] & FLAG;                                /* get dst flag */
1331     if (src_f)                                          /* src done? src = 0 */
1332         src = 0;
1333     else {
1334         src = M[s] & DIGIT;                             /* get src digit */
1335         if (cnt >= skp)                                 /* get src flag */
1336             src_f = M[s] & FLAG;
1337         MM (s);                                         /* decr src addr */
1338         }
1339     if (BAD_DIGIT (dst) || BAD_DIGIT (src))             /* bad digit? */
1340         return STOP_INVDIG;
1341     if (comp)                                           /* complement? */
1342         src = 9 - src;
1343     res = add_one_digit (dst, src, &cry);               /* add */
1344     if (sto)                                            /* store */
1345         M[d] = dst_f | res;
1346     MM (d);                                             /* decr dst addr */
1347     if (cnt++ >= MEMSIZE)                               /* (stop runaway) */
1348         return STOP_FWRAP;
1349     } while (dst_f == 0);                               /* until dst done */
1350 if (!src_f)                                             /* !src done? ovf */
1351     ind[IN_OVF] = 1;
1352 if (comp && !cry && !ind[IN_EZ]) {                      /* recomp needed? */
1353     ind[IN_HP] = ind[IN_HP] ^ 1;                        /* flip indicator */
1354     if (sto) {                                          /* storing? */
1355         for (cry = 1, dp = dsv; dp != d; ) {            /* rescan */
1356             dst = M[dp] & DIGIT;                        /* get dst digit */
1357             res = add_one_digit (9 - dst, 0, &cry);     /* "add" */
1358             M[dp] = (M[dp] & FLAG) | res;               /* store */
1359             MM (dp);                                    /* decr dst addr */
1360             }
1361         M[dsv] = M[dsv] ^ FLAG;                         /* compl sign */
1362         }
1363     *sta = ADD_SIGNC;                                   /* sign changed */
1364     return SCPE_OK;
1365     }                                                   /* end if recomp */
1366 if (ind[IN_EZ])                                         /* res = 0? clr HP */
1367     ind[IN_HP] = 0;
1368 if (!comp && cry)                                       /* set status */
1369     *sta = ADD_CARRY;
1370 return SCPE_OK;
1371 }
1372 
1373 /* Add one digit via table (Model 1) or "hardware" (Model 2) */
1374 
add_one_digit(uint32 dst,uint32 src,uint32 * cry)1375 uint32 add_one_digit (uint32 dst, uint32 src, uint32 *cry)
1376 {
1377 uint32 res;
1378 
1379 if (*cry) src = src + 1;                                /* cry in? incr src */
1380 if (src >= 10) {                                        /* src > 10? */
1381     src = src - 10;                                     /* src -= 10 */
1382     *cry = 1;                                           /* carry out */
1383     }
1384 else *cry = 0;                                          /* else no carry */
1385 if (cpu_unit.flags & IF_MII)                            /* Model 2? */
1386     res = sum_table[dst + src];                         /* "hardware" */
1387 else res = M[ADD_TABLE + (dst * 10) + src];             /* table lookup */
1388 if (res & FLAG)                                         /* carry out? */
1389     *cry = 1;
1390 if (res & DIGIT)                                        /* nz? clr ind */
1391     ind[IN_EZ] = 0;
1392 return res & DIGIT;
1393 }
1394 
1395 /* Multiply routine
1396 
1397    Inputs:
1398         mpc     =       multiplicand address
1399         mpy     =       multiplier address
1400    Outputs:
1401         return  =       status
1402 
1403    Reference manual: "A zero product may have a negative or positive sign,
1404    depending on the signs of the fields at the P and Q addresses."
1405 */
1406 
mul_field(uint32 mpc,uint32 mpy)1407 t_stat mul_field (uint32 mpc, uint32 mpy)
1408 {
1409 int32 i;
1410 uint32 pro;                                             /* prod pointer */
1411 uint32 mpyd, mpyf;                                      /* mpy digit, flag */
1412 uint32 cnt = 0;                                         /* counter */
1413 uint8 sign;                                             /* final sign */
1414 t_stat r;
1415 
1416 PR1 = 1;                                                /* step on PR1 */
1417 for (i = 0; i < PROD_AREA_LEN; i++)                     /* clr prod area */
1418     M[PROD_AREA + i] = 0;
1419 sign = (M[mpc] & FLAG) ^ (M[mpy] & FLAG);               /* get final sign */
1420 ind[IN_HP] = (sign == 0);                               /* set indicators */
1421 ind[IN_EZ] = 1;
1422 pro = PROD_AREA + PROD_AREA_LEN - 1;                    /* product ptr */
1423 
1424 /* Loop on multiplier (mpy) and product (pro) digits */
1425 
1426 do {
1427     mpyd = M[mpy] & DIGIT;                              /* multiplier digit */
1428     mpyf = (M[mpy] & FLAG) && (cnt != 0);               /* last digit flag */
1429     if (BAD_DIGIT (mpyd))                               /* bad? */
1430         return STOP_INVDIG;
1431     r = mul_one_digit (mpyd, mpc, pro, mpyf);           /* prod += mpc*mpy_dig */
1432     if (r != SCPE_OK)                                   /* error? */
1433         return r;
1434     MM (mpy);                                           /* decr mpyr, prod addrs */
1435     MM (pro);
1436     if (cnt++ >= MEMSIZE)                               /* (stop runaway) */
1437         return STOP_FWRAP;
1438     } while ((mpyf == 0) || (cnt <= 1));                /* until mpyr flag */
1439 
1440 if (ind[IN_EZ])                                         /* res = 0? clr HP */
1441     ind[IN_HP] = 0;
1442 M[PROD_AREA + PROD_AREA_LEN - 1] |= sign;               /* set final sign */
1443 return SCPE_OK;
1444 }
1445 
1446 /* Multiply step
1447 
1448    Inputs:
1449         mpyd    =       multiplier digit (tested valid)
1450         mpcp    =       multiplicand low address
1451         prop    =       product low address
1452         last    =       last iteration flag (set flag on high product)
1453    Outputs:
1454         prod    +=      multiplicand * multiplier_digit
1455         return  =       status
1456 
1457    The multiply table address is constructed as follows:
1458    - double the multiplier digit
1459    - use the 10's digit of the doubled result, + 1, as the 100's digit
1460      of the table address
1461    - use the multiplicand digit as the 10's digit of the table address
1462    - use the unit digit of the doubled result as the unit digit of the
1463      table address
1464    EZ indicator is cleared if a non-zero digit is ever generated
1465 */
1466 
mul_one_digit(uint32 mpyd,uint32 mpcp,uint32 prop,uint32 last)1467 t_stat mul_one_digit (uint32 mpyd, uint32 mpcp, uint32 prop, uint32 last)
1468 {
1469 uint32 mpta, mptb;                                      /* mult table */
1470 uint32 mptd;                                            /* mult table digit */
1471 uint32 mpcd, mpcf;                                      /* mpc digit, flag */
1472 uint32 prwp;                                            /* prod working ptr */
1473 uint32 prod;                                            /* product digit */
1474 uint32 cry;                                             /* carry */
1475 uint32 mpcc, cryc;                                      /* counters */
1476 
1477 mptb = MUL_TABLE + ((mpyd <= 4)? (mpyd * 2):            /* set mpy table 100's, */
1478     (((mpyd - 5) * 2) + 100));                          /* 1's digits */
1479 
1480 /* Inner loop on multiplicand (mpcp) and product (prop) digits */
1481 
1482 mpcc = 0;                                               /* multiplicand ctr */
1483 do {
1484     prwp = prop;                                        /* product working ptr */
1485     mpcd = M[mpcp] & DIGIT;                             /* multiplicand digit */
1486     mpcf = M[mpcp] & FLAG;                              /* multiplicand flag */
1487     if (BAD_DIGIT (mpcd))                               /* bad? */
1488         return STOP_INVDIG;
1489     mpta = mptb + (mpcd * 10);                          /* mpy table 10's digit */
1490     cry = 0;                                            /* init carry */
1491     mptd = M[mpta] & DIGIT;                             /* mpy table digit */
1492     if (BAD_DIGIT (mptd))                               /* bad? */
1493         return STOP_INVDIG;
1494     prod = M[prwp] & DIGIT;                             /* product digit */
1495     if (BAD_DIGIT (prod))                               /* bad? */
1496         return STOP_INVDIG;
1497     M[prwp] = add_one_digit (prod, mptd, &cry);         /* add mpy tbl to prod */
1498     MM (prwp);                                          /* decr working ptr */
1499     mptd = M[mpta + 1] & DIGIT;                         /* mpy table digit */
1500     if (BAD_DIGIT (mptd))                               /* bad? */
1501         return STOP_INVDIG;
1502     prod = M[prwp] & DIGIT;                             /* product digit */
1503     if (BAD_DIGIT (prod))                               /* bad? */
1504         return STOP_INVDIG;
1505     M[prwp] = add_one_digit (prod, mptd, &cry);         /* add mpy tbl to prod */
1506     cryc = 0;                                           /* (stop runaway) */
1507     while (cry) {                                       /* propagate carry */
1508         MM (prwp);                                      /* decr working ptr */
1509         prod = M[prwp] & DIGIT;                         /* product digit */
1510         if (BAD_DIGIT (prod))                           /* bad? */
1511             return STOP_INVDIG;
1512         M[prwp] = add_one_digit (prod, 0, &cry);        /* add cry */
1513         if (cryc++ > MEMSIZE)
1514             return STOP_FWRAP;
1515         }
1516     MM (mpcp);                                          /* decr mpc, prod ptrs */
1517     MM (prop);
1518     if (mpcc++ > MEMSIZE)
1519         return STOP_FWRAP;
1520     } while ((mpcf == 0) || (mpcc <= 1));               /* until mpcf flag */
1521 if (last)                                               /* flag high product */
1522     M[prop] = M[prop] | FLAG;
1523 return SCPE_OK;
1524 }
1525 
1526 /* Divide routine - comments from Geoff Kuenning's 1620 simulator
1527 
1528    The destination of the divide is given by:
1529 
1530         100 - <# digits in quotient>
1531 
1532    Which is more easily calculated as:
1533 
1534         100 - <# digits in divisor> - <# digits in dividend>
1535 
1536    The quotient goes into 99 minus the divisor length.  The
1537    remainder goes into 99.  The load dividend instruction (above)
1538    should have specified a P address of 99 minus the size of the
1539    divisor.
1540 
1541    Note that this all implies that "dest" points to the *leftmost*
1542    digit of the dividend.
1543 
1544    After the division, the assumed decimal point will be as many
1545    positions to the left as there are digits in the divisor.  In
1546    other words, a 4-digit divisor will produce 4 (assumed) decimal
1547    places.
1548 
1549    There are other ways to do these things.  In particular, the
1550    load-dividend instruction doesn't have to specify the above
1551    formula; if it's done differently, then you don't have to get
1552    decimal places.  This is not well-explained in the books I have.
1553 
1554    How to divide on a 1620:
1555 
1556    The dividend is the field at 99:
1557 
1558             90 = _1234567890
1559 
1560    The divisor is somewhere else in memory:
1561 
1562             _03
1563 
1564    The divide operation specifies the left-most digit of the
1565    dividend as the place to begin trial subtractions:
1566 
1567             DM  90,3
1568 
1569    The loop works as follows:
1570 
1571         1.  Call the left-most digit of the dividend "current_dividend".
1572             Call the location current_dividend - <divisor_length>
1573             "quotient_digit".
1574         2.  Clear the flag at current_dividend, and set one at
1575             quotient_digit.
1576 
1577                 88 = _001234567890, q_d = 88, c_d = 90
1578             [Not actually done; divisor length controls subtract.]
1579         3.  Subtract the divisor from the field at current-dividend,
1580             using normal 1620 rules, except that signs are ignored.
1581             Continue these subtractions until either 10 subtractions
1582             have been done, or you get a negative result:
1583 
1584                 88 = _00_2234567890, q_d = 88, c_d = 90
1585         4.  If 10 subtractions have been done, set the overflow
1586             indicator and abort.  Otherwise, add the divisor back to
1587             correct for the oversubtraction:
1588 
1589                 88 = _001234567890, q_d = 88, c_d = 90
1590         5.  Store the (net) number of subtractions in quotient_digit:
1591 
1592                 88 = _001234567890, q_d = 88, c_d = 90
1593         6.  If this is not the first pass, clear the flag at
1594             quotient_digit.  Increment quotient_digit and
1595             current_dividend, and set a flag at the new
1596             quotient_digit:
1597 
1598                 88 = _0_01234567890, q_d = 89, c_d = 91
1599             [If first pass, set a flag at quotient digit.]
1600         7.  If current_dividend is not 100, repeat steps 3 through 7.
1601         8.  Set flags at 99 and quotient_digit - 1 according to the
1602             rules of algebra:  the quotient's sign is the exclusive-or
1603             of the signs of the divisor and dividend, and the
1604             remainder has the sign of the dividend:
1605 
1606                  10 /  3 =  3 remainder  1
1607                  10 / -3 = -3 remainder  1
1608                 -10 /  3 = -3 remainder -1
1609                 -10 / -3 =  3 remainder -1
1610 
1611             This preserves the relationship dd = q * dv + r.
1612 
1613    Our example continues as follows for steps 3 through 7:
1614 
1615             3.  88 = _0_00_334567890, q_d = 89, c_d = 91
1616             4.  88 = _0_00034567890
1617             5.  88 = _0_40034567890
1618             6.  88 = _04_0034567890, q_d = 90, c_d = 92
1619             3.  88 = _04_00_34567890
1620             4.  88 = _04_0004567890
1621             5.  88 = _04_1004567890
1622             6.  88 = _041_004567890, q_d = 91, c_d = 93
1623             3.  88 = _041_00_2567890
1624             4.  88 = _041_001567890
1625             5.  88 = _041_101567890
1626             6.  88 = _0411_01567890, q_d = 92, c_d = 94
1627             3.  88 = _0411_00_367890
1628             4.  88 = _0411_00067890
1629             5.  88 = _0411_50067890
1630             6.  88 = _04115_0067890, q_d = 93, c_d = 95
1631             3.  88 = _04115_00_37890
1632             4.  88 = _04115_0007890
1633             5.  88 = _04115_2007890
1634             6.  88 = _041152_007890, q_d = 94, c_d = 96
1635             3.  88 = _041152_00_2890
1636             4.  88 = _041152_001890
1637             5.  88 = _041152_201890
1638             6.  88 = _0411522_01890, q_d = 95, c_d = 97
1639             3.  88 = _0411522_00_390
1640             4.  88 = _0411522_00090
1641             5.  88 = _0411522_60090
1642             6.  88 = _04115226_0090, q_d = 96, c_d = 98
1643             3.  88 = _04115226_00_30
1644             4.  88 = _04115226_0000
1645             5.  88 = _04115226_3000
1646             6.  88 = _041152263_000, q_d = 97, c_d = 99
1647             3.  88 = _041152263_00_3
1648             4.  88 = _041152263_000
1649             5.  88 = _041152263_000
1650             6.  88 = _0411522630_00, q_d = 98, c_d = 100
1651 
1652    In the actual code below, we elide several of these steps in
1653    various ways for convenience and efficiency.
1654 
1655    Note that the EZ indicator is NOT valid for divide, because it
1656    is cleared by any non-zero result in an intermediate add.  The
1657    code maintains its own EZ indicator for the quotient.
1658 */
1659 
div_field(uint32 dvd,uint32 dvr,int32 * ez)1660 t_stat div_field (uint32 dvd, uint32 dvr, int32 *ez)
1661 {
1662 uint32 quop, quod, quos;                                /* quo ptr, dig, sign */
1663 uint32 dvds;                                            /* dvd sign */
1664 t_bool first = TRUE;                                    /* first pass */
1665 t_stat r;
1666 
1667 dvds = (M[PROD_AREA + PROD_AREA_LEN - 1]) & FLAG;       /* dividend sign */
1668 quos = dvds ^ (M[dvr] & FLAG);                          /* quotient sign */
1669 ind[IN_HP] = (quos == 0);                               /* set indicators */
1670 *ez = 1;
1671 
1672 /* Loop on current dividend, high order digit at dvd */
1673 
1674 do {
1675     r = div_one_digit (dvd, dvr, 10, &quod, &quop);     /* dev quo digit */
1676     if (r != SCPE_OK)                                   /* error? */
1677         return r;
1678 
1679 /* Store quotient digit and advance current dividend pointer */
1680 
1681     if (first) {                                        /* first pass? */
1682         if (quod >= 10) {                               /* overflow? */
1683             ind[IN_OVF] = 1;                            /* set indicator */
1684             return STOP_OVERFL;                         /* stop */
1685             }
1686         M[quop] = FLAG | quod;                          /* set flag on quo */
1687         first = FALSE;
1688         }
1689     else M[quop] = quod;                                /* store quo digit */
1690     if (quod)                                           /* if nz, clr ind */
1691         *ez = 0;
1692     PP (dvd);                                           /* incr dvd ptr */
1693     } while (dvd != (PROD_AREA + PROD_AREA_LEN));       /* until end prod */
1694 
1695 /* Division done.  Set signs of quo, rem, set flag on high order remainder */
1696 
1697 if (*ez)                                                /* res = 0? clr HP */
1698     ind[IN_HP] = 0;
1699 M[PROD_AREA + PROD_AREA_LEN - 1] |= dvds;               /* remainder sign */
1700 M[quop] = M[quop] | quos;                               /* quotient sign */
1701 PP (quop);                                              /* high remainder */
1702 M[quop] = M[quop] | FLAG;                               /* set flag */
1703 return SCPE_OK;
1704 }
1705 
1706 /* Divide step
1707 
1708    Inputs:
1709         dvd     =       current dividend address (high digit)
1710         dvr     =       divisor address (low digit)
1711         max     =       max number of iterations before overflow
1712         &quod   =       address to store quotient digit
1713         &quop   =       address to store quotient pointer (can be NULL)
1714    Outputs:
1715         return  =       status
1716 
1717    Divide step calculates a quotient digit by repeatedly subtracting the
1718    divisor from the current dividend.  The divisor's length controls the
1719    subtraction; dividend flags are ignored.
1720 */
1721 
div_one_digit(uint32 dvd,uint32 dvr,uint32 max,uint32 * quod,uint32 * quop)1722 t_stat div_one_digit (uint32 dvd, uint32 dvr, uint32 max,
1723                  uint32 *quod, uint32 *quop)
1724 {
1725 uint32 dvrp, dvrd, dvrf;                                /* dvr ptr, dig, flag */
1726 uint32 dvdp, dvdd;                                      /* dvd ptr, dig */
1727 uint32 qd, cry;                                         /* quo dig, carry */
1728 uint32 cnt;
1729 
1730 for (qd = 0; qd < max; qd++) {                          /* devel quo dig */
1731     dvrp = dvr;                                         /* divisor ptr */
1732     dvdp = dvd;                                         /* dividend ptr */
1733     cnt = 0;
1734     cry = 1;                                            /* carry in = 1 */
1735     do {                                                /* sub dvr fm dvd */
1736         dvdd = M[dvdp] & DIGIT;                         /* dividend digit */
1737         if (BAD_DIGIT (dvdd))                           /* bad? */
1738             return STOP_INVDIG;
1739         dvrd = M[dvrp] & DIGIT;                         /* divisor digit */
1740         dvrf = M[dvrp] & FLAG;                          /* divisor flag */
1741         if (BAD_DIGIT (dvrd))                           /* bad? */
1742             return STOP_INVDIG;
1743         M[dvdp] = add_one_digit (dvdd, 9 - dvrd, &cry); /* sub */
1744         MM (dvdp);                                      /* decr ptrs */
1745         MM (dvrp);
1746         if (cnt++ >= MEMSIZE)                           /* (stop runaway) */
1747             return STOP_FWRAP;
1748         } while ((dvrf == 0) || (cnt <= 1));            /* until dvr flag */
1749     if (!cry) {                                         /* !cry = borrow */
1750         dvdd = M[dvdp] & DIGIT;                         /* borrow digit */
1751         if (BAD_DIGIT (dvdd))                           /* bad? */
1752             return STOP_INVDIG;
1753         M[dvdp] = add_one_digit (dvdd, 9, &cry);        /* sub */
1754         }
1755     if (!cry)                                           /* !cry = negative */
1756         break;
1757     }
1758 
1759 /* Add back the divisor to correct for the negative result */
1760 
1761 dvrp = dvr;                                             /* divisor ptr */
1762 dvdp = dvd;                                             /* dividend ptr */
1763 cnt = 0;
1764 cry = 0;                                                /* carry in = 0 */
1765 do {
1766     dvdd = M[dvdp] & DIGIT;                             /* dividend digit */
1767     dvrd = M[dvrp] & DIGIT;                             /* divisor digit */
1768     dvrf = M[dvrp] & FLAG;                              /* divisor flag */
1769     M[dvdp] = add_one_digit (dvdd, dvrd, &cry);         /* add */
1770     MM (dvdp);                                          /* decr ptrs */
1771     MM (dvrp);
1772     cnt++;
1773     } while ((dvrf == 0) || (cnt <= 1));                /* until dvr flag */
1774 if (cry) {                                              /* carry out? */
1775     dvdd = M[dvdp] & DIGIT;                             /* borrow digit */
1776     M[dvdp] = add_one_digit (dvdd, 0, &cry);            /* add */
1777     }
1778 if (quop != NULL)                                       /* set quo addr */
1779     *quop = dvdp;
1780 *quod = qd;                                             /* set quo digit */
1781 return SCPE_OK;
1782 }
1783 
1784 /* Logical operation routines (and, or, xor, complement)
1785 
1786    Inputs:
1787         d       =       destination address
1788         s       =       source address
1789    Output:
1790         return  =       status
1791 
1792    Destination flags are preserved; EZ reflects the result.
1793    COM does not obey normal field length restrictions.
1794 */
1795 
or_field(uint32 d,uint32 s)1796 t_stat or_field (uint32 d, uint32 s)
1797 {
1798 uint32 cnt = 0;
1799 int32 t;
1800 
1801 ind[IN_EZ] = 1;                                         /* assume result zero */
1802 do {
1803     t = M[s];                                           /* get src */
1804     M[d] = (M[d] & FLAG) | ((M[d] | t) & 07);           /* OR src to dst */
1805     if (M[d] & DIGIT)                                   /* nz dig? clr ind */
1806         ind[IN_EZ] = 0;
1807     MM (d);                                             /* decr pointers */
1808     MM (s);
1809     if (cnt++ >= MEMSIZE)                               /* (stop runaway) */
1810         return STOP_FWRAP;
1811     } while (((t & FLAG) == 0) || (cnt <= 1));          /* until src flag */
1812 return SCPE_OK;
1813 }
1814 
and_field(uint32 d,uint32 s)1815 t_stat and_field (uint32 d, uint32 s)
1816 {
1817 uint32 cnt = 0;
1818 int32 t;
1819 
1820 ind[IN_EZ] = 1;                                         /* assume result zero */
1821 do {
1822     t = M[s];                                           /* get src */
1823     M[d] = (M[d] & FLAG) | ((M[d] & t) & 07);           /* AND src to dst */
1824     if (M[d] & DIGIT)                                   /* nz dig? clr ind */
1825         ind[IN_EZ] = 0;
1826     MM (d);                                             /* decr pointers */
1827     MM (s);
1828     if (cnt++ >= MEMSIZE)                               /* (stop runaway) */
1829         return STOP_FWRAP;
1830     } while (((t & FLAG) == 0) || (cnt <= 1));          /* until src flag */
1831 return SCPE_OK;
1832 }
1833 
xor_field(uint32 d,uint32 s)1834 t_stat xor_field (uint32 d, uint32 s)
1835 {
1836 uint32 cnt = 0;
1837 int32 t;
1838 
1839 ind[IN_EZ] = 1;                                         /* assume result zero */
1840 do {
1841     t = M[s];                                           /* get src */
1842     M[d] = (M[d] & FLAG) | ((M[d] ^ t) & 07);           /* XOR src to dst */
1843     if (M[d] & DIGIT)                                   /* nz dig? clr ind */
1844         ind[IN_EZ] = 0;
1845     MM (d);                                             /* decr pointers */
1846     MM (s);
1847     if (cnt++ >= MEMSIZE)                               /* (stop runaway) */
1848         return STOP_FWRAP;
1849     } while (((t & FLAG) == 0) || (cnt <= 1));          /* until src flag */
1850 return SCPE_OK;
1851 }
1852 
com_field(uint32 d,uint32 s)1853 t_stat com_field (uint32 d, uint32 s)
1854 {
1855 uint32 cnt = 0;
1856 int32 t;
1857 
1858 ind[IN_EZ] = 1;                                         /* assume result zero */
1859 do {
1860     t = M[s];                                           /* get src */
1861     M[d] = (t & FLAG) | ((t ^ 07) & 07);                /* comp src to dst */
1862     if (M[d] & DIGIT)                                   /* nz dig? clr ind */
1863         ind[IN_EZ] = 0;
1864     MM (d);                                             /* decr pointers */
1865     MM (s);
1866     if (cnt++ >= MEMSIZE)                               /* (stop runaway) */
1867         return STOP_FWRAP;
1868     } while ((t & FLAG) == 0);                          /* until src flag */
1869 return SCPE_OK;
1870 }
1871 
1872 /* Octal to decimal
1873 
1874    Inputs:
1875         tbl     =       conversion table address (low digit)
1876         s       =       source address
1877    Outputs:
1878         product area =  converted source
1879         result  =       status
1880 
1881    OTD is a cousin of multiply.  The octal digits in the source are
1882    multiplied by successive values in the conversion table, and the
1883    results are accumulated in the product area.  Although the manual
1884    does not say, this code assumes that EZ and HP are affected.
1885  */
1886 
oct_to_dec(uint32 tbl,uint32 s)1887 t_stat oct_to_dec (uint32 tbl, uint32 s)
1888 {
1889 uint32 cnt = 0, tblc;
1890 uint32 i, sd, sf, tf, sign;
1891 t_stat r;
1892 
1893 for (i = 0; i < PROD_AREA_LEN; i++)                     /* clr prod area */
1894     M[PROD_AREA + i] = 0;
1895 sign = M[s] & FLAG;                                     /* save sign */
1896 ind[IN_EZ] = 1;                                         /* set indicators */
1897 ind[IN_HP] = (sign == 0);
1898 do {
1899     sd = M[s] & DIGIT;                                  /* src digit */
1900     sf = M[s] & FLAG;                                   /* src flag */
1901     r = mul_one_digit (sd, tbl, PROD_AREA + PROD_AREA_LEN - 1, sf);
1902     if (r != SCPE_OK)                                   /* err? */
1903         return r;
1904     MM (s);                                             /* decr src addr */
1905     MM (tbl);                                           /* skip 1st tbl dig */
1906     tblc = 0;                                           /* count */
1907     do {
1908         tf = M[tbl] & FLAG;                             /* get next */
1909         MM (tbl);                                       /* decr ptr */
1910         if (tblc++ > MEMSIZE)
1911             return STOP_FWRAP;
1912         } while (tf == 0);                              /* until flag */
1913     if (cnt++ >= MEMSIZE)                               /* (stop runaway) */
1914         return STOP_FWRAP;
1915     } while (sf == 0);
1916 if (ind[IN_EZ])                                         /* res = 0? clr HP */
1917     ind[IN_HP] = 0;
1918 M[PROD_AREA + PROD_AREA_LEN - 1] |= sign;               /* set sign */
1919 return SCPE_OK;
1920 }
1921 
1922 /* Decimal to octal
1923 
1924    Inputs:
1925         d       =       destination address
1926         tbl     =       conversion table address (low digit of highest power)
1927         &ez     =       address of soft EZ indicator
1928         product area =  field to convert
1929    Outputs:
1930         return  =       status
1931 
1932    DTO is a cousin to divide.  The number in the product area is repeatedly
1933    divided by successive values in the conversion table, and the quotient
1934    digits are stored in the destination.  Although the manual does not say,
1935    this code assumes that EZ and HP are affected.
1936  */
1937 
dec_to_oct(uint32 d,uint32 tbl,int32 * ez)1938 t_stat dec_to_oct (uint32 d, uint32 tbl, int32 *ez)
1939 {
1940 uint32 sign, octd, t;
1941 t_bool first = TRUE;
1942 uint32 ctr = 0;
1943 t_stat r;
1944 
1945 sign = M[PROD_AREA + PROD_AREA_LEN - 1] & FLAG;         /* input sign */
1946 *ez = 1;                                                /* set indicators */
1947 ind[IN_HP] = (sign == 0);
1948 for ( ;; ) {
1949     r = div_one_digit (PROD_AREA + PROD_AREA_LEN - 1,   /* divide */
1950         tbl, 8, &octd, NULL);
1951     if (r != SCPE_OK)                                   /* error? */
1952         return r;
1953     if (first) {                                        /* first pass? */
1954         if (octd >= 8) {                                /* overflow? */
1955             ind[IN_OVF] = 1;                            /* set indicator */
1956             return SCPE_OK;                             /* stop */
1957             }
1958         M[d] = FLAG | octd;                             /* set flag on quo */
1959         first = FALSE;
1960         }
1961     else M[d] = octd;                                   /* store quo digit */
1962     if (octd)                                           /* if nz, clr ind */
1963         *ez = 0;
1964     PP (tbl);                                           /* incr tbl addr */
1965     if ((M[tbl] & REC_MARK) == REC_MARK)                /* record mark? */
1966         break;
1967     PP (tbl);                                           /* skip flag */
1968     if ((M[tbl] & REC_MARK) == REC_MARK)                /* record mark? */
1969         break;
1970     do {                                                /* look for F, rec mk */
1971         PP (tbl);
1972         t = M[tbl];
1973         } while (((t & FLAG) == 0) && ((t & REC_MARK) != REC_MARK));
1974     MM (tbl);                                           /* step back one */
1975     PP (d);                                             /* incr quo addr */
1976     if (ctr++ > MEMSIZE)                                /* (stop runaway) */
1977         return STOP_FWRAP;
1978     }
1979 if (*ez)                                                /* res = 0? clr HP */
1980     ind[IN_HP] = 0;
1981 M[d] = M[d] | sign;                                     /* set result sign */
1982 return SCPE_OK;
1983 }
1984 
1985 /* Reset routine */
1986 
cpu_reset(DEVICE * dptr)1987 t_stat cpu_reset (DEVICE *dptr)
1988 {
1989 int32 i;
1990 static t_bool one_time = TRUE;
1991 
1992 PR1 = IR2 = 1;                                          /* invalidate PR1,IR2 */
1993 ind[0] = 0;
1994 for (i = IN_SW4 + 1; i < NUM_IND; i++)                  /* init indicators */
1995     ind[i] = 0;
1996 if (cpu_unit.flags & IF_IA)                             /* indirect enabled? */
1997     iae = 1;
1998 else iae = 0;
1999 idxe = idxb = 0;                                        /* indexing off */
2000 pcq_r = find_reg ("PCQ", NULL, dptr);                   /* init old PC queue */
2001 if (pcq_r)
2002     pcq_r->qptr = 0;
2003 else return SCPE_IERR;
2004 sim_brk_types = sim_brk_dflt = SWMASK ('E');            /* init breakpoints */
2005 upd_ind ();                                             /* update indicators */
2006 if (one_time)                                           /* set default tables */
2007     cpu_set_table (&cpu_unit, 1, NULL, NULL);
2008 one_time = FALSE;
2009 return SCPE_OK;
2010 }
2011 
2012 /* Memory examine */
2013 
cpu_ex(t_value * vptr,t_addr addr,UNIT * uptr,int32 sw)2014 t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
2015 {
2016 if (addr >= MEMSIZE)
2017     return SCPE_NXM;
2018 if (vptr != NULL)
2019     *vptr = M[addr] & (FLAG | DIGIT);
2020 return SCPE_OK;
2021 }
2022 
2023 /* Memory deposit */
2024 
cpu_dep(t_value val,t_addr addr,UNIT * uptr,int32 sw)2025 t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
2026 {
2027 if (addr >= MEMSIZE)
2028     return SCPE_NXM;
2029 M[addr] = val & (FLAG | DIGIT);
2030 return SCPE_OK;
2031 }
2032 
2033 /* Memory size change */
2034 
cpu_set_size(UNIT * uptr,int32 val,char * cptr,void * desc)2035 t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
2036 {
2037 int32 mc = 0;
2038 uint32 i;
2039 
2040 if ((val <= 0) || (val > MAXMEMSIZE) || ((val % 1000) != 0))
2041     return SCPE_ARG;
2042 for (i = val; i < MEMSIZE; i++)
2043     mc = mc | M[i];
2044 if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))
2045     return SCPE_OK;
2046 MEMSIZE = val;
2047 for (i = MEMSIZE; i < MAXMEMSIZE; i++)
2048     M[i] = 0;
2049 return SCPE_OK;
2050 }
2051 
2052 /* Model change */
2053 
cpu_set_model(UNIT * uptr,int32 val,char * cptr,void * desc)2054 t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc)
2055 {
2056 if (val)
2057     cpu_unit.flags = (cpu_unit.flags & (UNIT_SCP | UNIT_BCD | MII_OPT)) |
2058         IF_DIV | IF_IA | IF_EDT;
2059 else cpu_unit.flags = cpu_unit.flags & (UNIT_SCP | UNIT_BCD | MI_OPT);
2060 return SCPE_OK;
2061 }
2062 
2063 /* Set/clear Model 1 option */
2064 
cpu_set_opt1(UNIT * uptr,int32 val,char * cptr,void * desc)2065 t_stat cpu_set_opt1 (UNIT *uptr, int32 val, char *cptr, void *desc)
2066 {
2067 if (cpu_unit.flags & IF_MII) {
2068     printf ("Feature is standard on 1620 Model 2\n");
2069     if (sim_log)
2070         fprintf (sim_log, "Feature is standard on 1620 Model 2\n");
2071     return SCPE_NOFNC;
2072     }
2073 return SCPE_OK;
2074 }
2075 
2076 /* Set/clear Model 2 option */
2077 
cpu_set_opt2(UNIT * uptr,int32 val,char * cptr,void * desc)2078 t_stat cpu_set_opt2 (UNIT *uptr, int32 val, char *cptr, void *desc)
2079 {
2080 if (!(cpu_unit.flags & IF_MII)) {
2081     printf ("Feature is not available on 1620 Model 1\n");
2082     if (sim_log)
2083         fprintf (sim_log, "Feature is not available on 1620 Model 1\n");
2084     return SCPE_NOFNC;
2085     }
2086 return SCPE_OK;
2087 }
2088 
2089 /* Front panel save */
2090 
cpu_set_save(UNIT * uptr,int32 val,char * cptr,void * desc)2091 t_stat cpu_set_save (UNIT *uptr, int32 val, char *cptr, void *desc)
2092 {
2093 if (saved_PC & 1)
2094     return SCPE_NOFNC;
2095 PR1 = saved_PC;
2096 return SCPE_OK;
2097 }
2098 
2099 /* Set standard add/multiply tables */
2100 
cpu_set_table(UNIT * uptr,int32 val,char * cptr,void * desc)2101 t_stat cpu_set_table (UNIT *uptr, int32 val, char *cptr, void *desc)
2102 {
2103 int32 i;
2104 
2105 for (i = 0; i < MUL_TABLE_LEN; i++)                     /* set mul table */
2106     M[MUL_TABLE + i] = std_mul_table[i];
2107 if (((cpu_unit.flags & IF_MII) == 0) || val) {          /* set add table */
2108     for (i = 0; i < ADD_TABLE_LEN; i++)
2109         M[ADD_TABLE + i] = std_add_table[i];
2110     }
2111 return SCPE_OK;
2112 }
2113 
2114 /* Set history */
2115 
cpu_set_hist(UNIT * uptr,int32 val,char * cptr,void * desc)2116 t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc)
2117 {
2118 int32 i, lnt;
2119 t_stat r;
2120 
2121 if (cptr == NULL) {
2122     for (i = 0; i < hst_lnt; i++)
2123         hst[i].vld = 0;
2124     hst_p = 0;
2125     return SCPE_OK;
2126     }
2127 lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r);
2128 if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN)))
2129     return SCPE_ARG;
2130 hst_p = 0;
2131 if (hst_lnt) {
2132     free (hst);
2133     hst_lnt = 0;
2134     hst = NULL;
2135     }
2136 if (lnt) {
2137     hst = (InstHistory *) calloc (lnt, sizeof (InstHistory));
2138     if (hst == NULL)
2139         return SCPE_MEM;
2140     hst_lnt = lnt;
2141     }
2142 return SCPE_OK;
2143 }
2144 
2145 /* Show history */
2146 
cpu_show_hist(FILE * st,UNIT * uptr,int32 val,void * desc)2147 t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc)
2148 {
2149 int32 i, k, di, lnt;
2150 char *cptr = (char *) desc;
2151 t_value sim_eval[INST_LEN];
2152 t_stat r;
2153 InstHistory *h;
2154 extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val,
2155     UNIT *uptr, int32 sw);
2156 
2157 if (hst_lnt == 0)                                       /* enabled? */
2158     return SCPE_NOFNC;
2159 if (cptr) {
2160     lnt = (int32) get_uint (cptr, 10, hst_lnt, &r);
2161     if ((r != SCPE_OK) || (lnt == 0))
2162         return SCPE_ARG;
2163     }
2164 else lnt = hst_lnt;
2165 di = hst_p - lnt;                                       /* work forward */
2166 if (di < 0)
2167     di = di + hst_lnt;
2168 fprintf (st, "PC     IR\n\n");
2169 for (k = 0; k < lnt; k++) {                             /* print specified */
2170     h = &hst[(++di) % hst_lnt];                         /* entry pointer */
2171     if (h->vld) {                                       /* instruction? */
2172         fprintf (st, "%05d  ", h->pc);
2173         for (i = 0; i < INST_LEN; i++)
2174             sim_eval[i] = h->inst[i];
2175         if ((fprint_sym (st, h->pc, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) {
2176             fprintf (st, "(undefined)");
2177             for (i = 0; i < INST_LEN; i++)
2178                 fprintf (st, "%02X", h->inst[i]);
2179             }
2180         fputc ('\n', st);                               /* end line */
2181         }                                               /* end else instruction */
2182     }                                                   /* end for */
2183 return SCPE_OK;
2184 }
2185