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