First Commit of my working state
[simh.git] / I1620 / i1620_cpu.c
1 /* i1620_cpu.c: IBM 1620 CPU simulator
2
3 Copyright (c) 2002-2006, 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 (found by Peter Schorn)
30 22-Sep-05 RMS Fixed declarations (from 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 (found by Dave Babcock)
35 21-Aug-03 RMS Fixed bug in immediate index add (found by Michael Short)
36 25-Apr-03 RMS Changed t_addr to uint32 throughout
37 18-Oct-02 RMS Fixed bugs in invalid result testing (found by 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
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) iae = 0;
467 if ((cpu_unit.flags & IF_IDX) == 0) idxe = idxb = 0;
468 upd_ind (); /* update indicators */
469 reason = 0;
470
471 /* Main instruction fetch/decode loop */
472
473 while (reason == 0) { /* loop until halted */
474
475 saved_PC = PC; /* commit prev instr */
476 if (sim_interval <= 0) { /* check clock queue */
477 if (reason = sim_process_event ()) break;
478 }
479
480 if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */
481 reason = STOP_IBKPT; /* stop simulation */
482 break;
483 }
484
485 sim_interval = sim_interval - 1;
486
487 /* Instruction fetch and address decode */
488
489 if (PC & 1) { /* PC odd? */
490 reason = STOP_INVIAD; /* stop */
491 break;
492 }
493
494 op = get_2d (PC); /* get opcode */
495 if (op < 0) { /* invalid? */
496 reason = STOP_INVINS;
497 break;
498 }
499 flags = op_table[op]; /* get op, flags */
500 if ((flags & ALLOPT) && /* need option? */
501 !(flags & ALLOPT & cpu_unit.flags)) { /* any set? */
502 reason = STOP_INVINS; /* no, error */
503 break;
504 }
505
506 pla = ADDR_A (PC, I_PL); /* P last addr */
507 qla = ADDR_A (PC, I_QL); /* Q last addr */
508 if (flags & IF_VPA) { /* need P? */
509 reason = get_addr (pla, 5, TRUE, &PAR); /* get P addr */
510 if (reason != SCPE_OK) break; /* stop if error */
511 }
512 if (flags & (IF_VQA | IF_4QA | IF_NQX)) { /* need Q? */
513 reason = get_addr (qla, /* get Q addr */
514 ((flags & IF_4QA)? 4: 5), /* 4 or 5 digits */
515 ((flags & IF_NQX)? FALSE: TRUE), /* not or indexed */
516 &QAR);
517 if (reason != SCPE_OK) { /* stop if invalid */
518 reason = reason + (STOP_INVQDG - STOP_INVPDG);
519 break;
520 }
521 }
522 else if (flags & IF_IMM) QAR = qla; /* immediate? */
523
524 if (hst_lnt) { /* history enabled? */
525 hst_p = (hst_p + 1); /* next entry */
526 if (hst_p >= hst_lnt) hst_p = 0;
527 hst[hst_p].vld = 1;
528 hst[hst_p].pc = PC;
529 for (i = 0; i < INST_LEN; i++)
530 hst[hst_p].inst[i] = M[(PC + i) % MEMSIZE];
531 }
532
533 PC = PC + INST_LEN; /* advance PC */
534 switch (op) { /* case on op */
535
536 /* Transmit digit - P,Q are valid */
537
538 case OP_TD:
539 case OP_TDM:
540 M[PAR] = M[QAR] & (FLAG | DIGIT); /* move dig, flag */
541 break;
542
543 /* Transmit field - P,Q are valid */
544
545 case OP_TF:
546 case OP_TFM:
547 reason = xmt_field (PAR, QAR, 1); /* xmit field */
548 break;
549
550 /* Transmit record - P,Q are valid */
551
552 case OP_TR:
553 reason = xmt_record (PAR, QAR, TRUE); /* xmit record */
554 break;
555
556 /* Transmit record no record mark - P,Q are valid */
557
558 case OP_TRNM:
559 reason = xmt_record (PAR, QAR, FALSE); /* xmit record but */
560 break; /* not rec mark */
561
562 /* Set flag - P is valid */
563
564 case OP_SF:
565 M[PAR] = M[PAR] | FLAG; /* set flag on P */
566 break;
567
568 /* Clear flag - P is valid */
569
570 case OP_CF:
571 M[PAR] = M[PAR] & ~FLAG; /* clear flag on P */
572 break;
573
574 /* Branch - P is valid */
575
576 case OP_B:
577 BRANCH (PAR); /* branch to P */
578 break;
579
580 /* Branch and transmit - P,Q are valid */
581
582 case OP_BT:
583 case OP_BTM:
584 reason = xmt_field (ADDR_S (PAR, 1), QAR, 1); /* xmit field to P-1 */
585 IR2 = PC; /* save PC */
586 BRANCH (PAR); /* branch to P */
587 break;
588
589 /* Branch and transmit floating - P,Q are valid */
590
591 case OP_BTFL:
592 reason = xmt_field (ADDR_S (PAR, 1), QAR, 3); /* skip 3 flags */
593 IR2 = PC; /* save PC */
594 BRANCH (PAR); /* branch to P */
595 break;
596
597 /* Branch and transmit address - P,Q are valid */
598
599 case OP_BTA:
600 case OP_BTAM:
601 reason = xmt_field (ADDR_S (PAR, 1), QAR, 4); /* skip 4 flags */
602 IR2 = PC; /* save PC */
603 BRANCH (PAR); /* branch to P */
604 break;
605
606 /* Branch back */
607
608 case OP_BB:
609 if (PR1 != 1) { /* PR1 valid? */
610 BRANCH (PR1); /* return to PR1 */
611 PR1 = 1; /* invalidate */
612 }
613 else if (IR2 != 1) { /* IR2 valid? */
614 BRANCH (IR2); /* return to IR2 */
615 IR2 = 1; /* invalidate */
616 }
617 else reason = STOP_INVRTN; /* MAR check */
618 break;
619
620 /* Branch on digit (not zero) - P,Q are valid */
621
622 case OP_BD:
623 if ((M[QAR] & DIGIT) != 0) { /* digit != 0? */
624 BRANCH (PAR); /* branch */
625 }
626 break;
627
628 /* Branch no flag - P,Q are valid */
629
630 case OP_BNF:
631 if ((M[QAR] & FLAG) == 0) { /* flag == 0? */
632 BRANCH (PAR); /* branch */
633 }
634 break;
635
636 /* Branch no record mark (8-2 not set) - P,Q are valid */
637
638 case OP_BNR:
639 if ((M[QAR] & REC_MARK) != REC_MARK) { /* not rec mark? */
640 BRANCH (PAR); /* branch */
641 }
642 break;
643
644 /* Branch no group mark - P,Q are valid */
645
646 case OP_BNG:
647 if ((M[QAR] & DIGIT) != GRP_MARK) { /* not grp mark? */
648 BRANCH (PAR); /* branch */
649 }
650 break;
651
652 /* Branch (no) indicator - P is valid */
653
654 case OP_BI:
655 case OP_BNI:
656 upd_ind (); /* update indicators */
657 t = get_2d (ADDR_A (saved_PC, I_BR)); /* get ind number */
658 if ((t < 0) || (ind_table[t] < 0)) { /* not valid? */
659 reason = STOP_INVIND; /* stop */
660 break;
661 }
662 if ((ind[t] != 0) ^ (op == OP_BNI)) { /* ind value correct? */
663 BRANCH (PAR); /* branch */
664 }
665 if (ind_table[t] > 0) ind[t] = 0; /* reset if needed */
666 break;
667
668 /* Add/subtract/compare - P,Q are valid */
669
670 case OP_A:
671 case OP_AM:
672 reason = add_field (PAR, QAR, FALSE, TRUE, 0, &sta); /* add, store */
673 if (sta == ADD_CARRY) ind[IN_OVF] = 1; /* cout => ovflo */
674 if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
675 break;
676
677 case OP_S:
678 case OP_SM:
679 reason = add_field (PAR, QAR, TRUE, TRUE, 0, &sta); /* sub, store */
680 if (sta == ADD_CARRY) ind[IN_OVF] = 1; /* cout => ovflo */
681 if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
682 break;
683
684 case OP_C:
685 case OP_CM:
686 reason = add_field (PAR, QAR, TRUE, FALSE, 0, &sta); /* sub, nostore */
687 if (sta == ADD_CARRY) ind[IN_OVF] = 1; /* cout => ovflo */
688 if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
689 break;
690
691 /* Multiply - P,Q are valid */
692
693 case OP_M:
694 case OP_MM:
695 reason = mul_field (PAR, QAR); /* multiply */
696 break;
697
698 /* IO instructions - P is valid */
699
700 case OP_RA:
701 case OP_WA:
702 if ((PAR & 1) == 0) { /* P even? */
703 reason = STOP_INVEAD; /* stop */
704 break;
705 }
706 case OP_K:
707 case OP_DN:
708 case OP_RN:
709 case OP_WN:
710 dev = get_2d (ADDR_A (saved_PC, I_IO)); /* get IO dev */
711 f0 = M[ADDR_A (saved_PC, I_CTL)] & DIGIT; /* get function */
712 f1 = M[ADDR_A (saved_PC, I_CTL + 1)] & DIGIT;
713 if ((dev < 0) || (iodisp[dev] == NULL)) /* undefined dev? */
714 reason = STOP_INVIO; /* stop */
715 else reason = iodisp[dev] (op, PAR, f0, f1); /* call device */
716 break;
717
718 /* Divide special feature instructions */
719
720 case OP_LD:
721 case OP_LDM:
722 for (i = 0; i < PROD_AREA_LEN; i++) /* clear prod area */
723 M[PROD_AREA + i] = 0;
724 t = M[QAR] & FLAG; /* save Q sign */
725 reason = xmt_divd (PAR, QAR); /* xmit dividend */
726 M[PROD_AREA + PROD_AREA_LEN - 1] |= t; /* set sign */
727 break;
728
729 /* Divide - P,Q are valid */
730
731 case OP_D:
732 case OP_DM:
733 reason = div_field (PAR, QAR, &t); /* divide */
734 ind[IN_EZ] = t; /* set indicator */
735 if ((reason == STOP_OVERFL) && !ar_stop) /* ovflo stop? */
736 reason = SCPE_OK; /* no */
737 break;
738
739 /* Edit special feature instructions */
740
741 /* Move flag - P,Q are valid */
742
743 case OP_MF:
744 M[PAR] = (M[PAR] & ~FLAG) | (M[QAR] & FLAG); /* copy Q flag */
745 M[QAR] = M[QAR] & ~FLAG; /* clr Q flag */
746 break;
747
748 /* Transmit numeric strip - P,Q are valid, P is source */
749
750 case OP_TNS:
751 if ((PAR & 1) == 0) { /* P must be odd */
752 reason = STOP_INVEAD;
753 break;
754 }
755 reason = xmt_tns (QAR, PAR); /* xmit and strip */
756 break;
757
758 /* Transmit numeric fill - P,Q are valid */
759
760 case OP_TNF:
761 if ((PAR & 1) == 0) { /* P must be odd */
762 reason = STOP_INVEAD;
763 break;
764 }
765 reason = xmt_tnf (PAR, QAR); /* xmit and strip */
766 break;
767
768 /* Index special feature instructions */
769
770 /* Move address - P,Q are valid */
771
772 case OP_MA:
773 for (i = 0; i < ADDR_LEN; i++) { /* move 5 digits */
774 M[PAR] = (M[PAR] & FLAG) | (M[QAR] & DIGIT);
775 MM (PAR); MM (QAR);
776 }
777 break;
778
779 /* Branch load index - P,Q are valid, Q not indexed */
780
781 case OP_BLX:
782 case OP_BLXM:
783 idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
784 if (idx < 0) { /* disabled? */
785 reason = STOP_INVIDX; /* stop */
786 break;
787 }
788 xmt_index (GET_IDXADDR (idx), QAR); /* copy Q to idx */
789 BRANCH (PAR); /* branch to P */
790 break;
791
792 /* Branch store index - P,Q are valid, Q not indexed */
793
794 case OP_BSX:
795 idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
796 if (idx < 0) { /* disabled? */
797 reason = STOP_INVIDX; /* stop */
798 break;
799 }
800 xmt_index (QAR, GET_IDXADDR (idx)); /* copy idx to Q */
801 BRANCH (PAR); /* branch to P */
802 break;
803
804 /* Branch and modify index - P,Q are valid, Q not indexed */
805
806 case OP_BX:
807 idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
808 if (idx < 0) { /* disabled? */
809 reason = STOP_INVIDX; /* stop */
810 break;
811 }
812 reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 0, &sta);
813 if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
814 BRANCH (PAR); /* branch to P */
815 break;
816
817 case OP_BXM:
818 idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
819 if (idx < 0) { /* disabled? */
820 reason = STOP_INVIDX; /* stop */
821 break;
822 }
823 reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 3, &sta);
824 if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
825 BRANCH (PAR); /* branch to P */
826 break;
827
828 /* Branch conditionally and modify index - P,Q are valid, Q not indexed */
829
830 case OP_BCX:
831 idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
832 if (idx < 0) { /* disabled? */
833 reason = STOP_INVIDX; /* stop */
834 break;
835 }
836 reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 0, &sta);
837 if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
838 if ((ind[IN_EZ] == 0) && (sta == ADD_NOCRY)) { /* ~z, ~c, ~sign chg? */
839 BRANCH (PAR); /* branch */
840 }
841 break;
842
843 case OP_BCXM:
844 idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
845 if (idx < 0) { /* disabled? */
846 reason = STOP_INVIDX; /* stop */
847 break;
848 }
849 reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 3, &sta);
850 if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
851 if ((ind[IN_EZ] == 0) && (sta == ADD_NOCRY)) { /* ~z, ~c, ~sign chg? */
852 BRANCH (PAR); /* branch */
853 }
854 break;
855
856 /* Branch and select - P is valid */
857
858 case OP_BS:
859 t = M[ADDR_A (saved_PC, I_SEL)] & DIGIT; /* get select */
860 switch (t) { /* case on select */
861 case 0:
862 idxe = idxb = 0; /* indexing off */
863 break;
864 case 1:
865 idxe = 1; idxb = 0; /* index band A */
866 break;
867 case 2:
868 idxe = idxb = 1; /* index band B */
869 break;
870 case 8:
871 iae = 0; /* indirect off */
872 break;
873 case 9:
874 iae = 1; /* indirect on */
875 break;
876 default:
877 reason = STOP_INVSEL; /* undefined */
878 break;
879 }
880 BRANCH (PAR);
881 break;
882
883 /* Binary special feature instructions */
884
885 /* Branch on bit - P,Q are valid, Q is 4d address */
886
887 case OP_BBT:
888 t = M[ADDR_A (saved_PC, I_Q)]; /* get Q0 digit */
889 if (t & M[QAR] & DIGIT) { /* match to mem? */
890 BRANCH (PAR); /* branch */
891 }
892 break;
893
894 /* Branch on mask - P,Q are valid, Q is 4d address */
895
896 case OP_BMK:
897 t = M[ADDR_A (saved_PC, I_Q)]; /* get Q0 digit */
898 if (((t ^ M[QAR]) & /* match to mem? */
899 ((t & FLAG)? (FLAG + DIGIT): DIGIT)) == 0) {
900 BRANCH (PAR); /* branch */
901 }
902 break;
903
904 /* Or - P,Q are valid */
905
906 case OP_ORF:
907 reason = or_field (PAR, QAR); /* OR fields */
908 break;
909
910 /* AND - P,Q are valid */
911
912 case OP_ANDF:
913 reason = and_field (PAR, QAR); /* AND fields */
914 break;
915
916 /* Exclusive or - P,Q are valid */
917
918 case OP_EORF:
919 reason = xor_field (PAR, QAR); /* XOR fields */
920 break;
921
922 /* Complement - P,Q are valid */
923
924 case OP_CPLF:
925 reason = com_field (PAR, QAR); /* COM field */
926 break;
927
928 /* Octal to decimal - P,Q are valid */
929
930 case OP_OTD:
931 reason = oct_to_dec (PAR, QAR); /* convert */
932 break;
933
934 /* Decimal to octal - P,Q are valid */
935
936 case OP_DTO:
937 reason = dec_to_oct (PAR, QAR, &t); /* convert */
938 ind[IN_EZ] = t; /* set indicator */
939 if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL;
940 break;
941
942 /* Floating point special feature instructions */
943
944 case OP_FADD:
945 reason = fp_add (PAR, QAR, FALSE); /* add */
946 if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK;
947 break;
948
949 case OP_FSUB:
950 reason = fp_add (PAR, QAR, TRUE); /* subtract */
951 if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK;
952 break;
953
954 case OP_FMUL:
955 reason = fp_mul (PAR, QAR); /* multiply */
956 if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK;
957 break;
958
959 case OP_FDIV:
960 reason = fp_div (PAR, QAR); /* divide */
961 if (ar_stop && ind[IN_OVF]) reason = STOP_FPDVZ;
962 if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK;
963 break;
964
965 case OP_FSL:
966 reason = fp_fsl (PAR, QAR); /* shift left */
967 break;
968
969 case OP_FSR:
970 reason = fp_fsr (PAR, QAR); /* shift right */
971 break;
972
973 /* Halt */
974
975 case OP_H:
976 saved_PC = PC; /* commit inst */
977 reason = STOP_HALT; /* stop */
978 break;
979
980 /* NOP */
981
982 case OP_NOP:
983 break;
984
985 /* Invalid instruction code */
986
987 default:
988 reason = STOP_INVINS; /* stop */
989 break;
990 } /* end switch */
991 } /* end while */
992
993 /* Simulation halted */
994
995 pcq_r->qptr = pcq_p; /* update pc q ptr */
996 upd_ind ();
997 return reason;
998 }
999
1000 /* Utility routines */
1001
1002 /* Get 2 digit field
1003
1004 Inputs:
1005 ad = address of high digit
1006 Outputs:
1007 val = field converted to binary
1008 -1 if bad digit
1009 */
1010
1011 int32 get_2d (uint32 ad)
1012 {
1013 int32 d, d1;
1014
1015 d = M[ad] & DIGIT; /* get 1st digit */
1016 d1 = M[ADDR_A (ad, 1)] & DIGIT; /* get 2nd digit */
1017 if (BAD_DIGIT (d) || BAD_DIGIT (d1)) return -1; /* bad? error */
1018 return ((d * 10) + d1); /* cvt to binary */
1019 }
1020
1021 /* Get address routine
1022
1023 Inputs:
1024 alast = address of low digit
1025 lnt = length
1026 indexok = TRUE if indexing allowed
1027 &addr = pointer to address output
1028 Output:
1029 return = error status (in terms of P address)
1030 addr = address converted to binary
1031
1032 Notes:
1033 - If indexing produces a negative result, the effective address is
1034 the 10's complement of the result
1035 - An address that exceeds memory produces a MAR check stop
1036 */
1037
1038 t_stat get_addr (uint32 alast, int32 lnt, t_bool indexok, uint32 *reta)
1039 {
1040 uint8 indir;
1041 int32 cnt, idx, idxa, idxv, addr;
1042
1043 if (iae) indir = FLAG; /* init indirect */
1044 else indir = 0;
1045
1046 cnt = 0; /* count depth */
1047 do {
1048 indir = indir & M[alast]; /* get indirect */
1049 if (cvt_addr (alast, lnt, FALSE, &addr)) /* cvt addr to bin */
1050 return STOP_INVPDG; /* bad? */
1051 idx = get_idx (ADDR_S (alast, 1)); /* get index reg num */
1052 if (indexok && (idx > 0)) { /* indexable? */
1053 idxa = GET_IDXADDR (idx); /* get idx reg addr */
1054 if (cvt_addr (idxa, ADDR_LEN, TRUE, &idxv)) /* cvt idx reg */
1055 return STOP_INVPDG;
1056 addr = addr + idxv; /* add in index */
1057 if (addr < 0) addr = addr + 100000; /* -? 10's comp */
1058 }
1059 if (addr >= (int32) MEMSIZE) return STOP_INVPAD; /* invalid addr? */
1060 alast = addr; /* new address */
1061 lnt = ADDR_LEN; /* std len */
1062 } while (indir && (cnt++ < ind_max));
1063 if (cnt > ind_max) return STOP_INVPIA; /* indir too deep? */
1064 *reta = addr; /* return address */
1065 return SCPE_OK;
1066 }
1067
1068 /* Convert address to binary
1069
1070 Inputs:
1071 alast = address of low digit
1072 lnt = length
1073 signok = TRUE if signed
1074 val = address of output
1075 Outputs:
1076 status = 0 if ok, != 0 if error
1077 */
1078
1079 t_stat cvt_addr (uint32 alast, int32 lnt, t_bool signok, int32 *val)
1080 {
1081 int32 sign = 0, addr = 0, t;
1082
1083 if (signok && (M[alast] & FLAG)) sign = 1; /* signed? */
1084 alast = alast - lnt; /* find start */
1085 do {
1086 PP (alast); /* incr mem addr */
1087 t = M[alast] & DIGIT; /* get digit */
1088 if (BAD_DIGIT (t)) return STOP_INVDIG; /* bad? error */
1089 addr = (addr * 10) + t; /* cvt to bin */
1090 } while (--lnt > 0);
1091 if (sign) *val = -addr; /* minus? */
1092 else *val = addr;
1093 return SCPE_OK;
1094 }
1095
1096 /* Get index register number
1097
1098 Inputs:
1099 aidx = address of low digit
1100 Outputs:
1101 index = >0 if indexed
1102 =0 if not indexed
1103 <0 if indexing disabled
1104 */
1105
1106 t_stat get_idx (uint32 aidx)
1107 {
1108 int32 i, idx;
1109
1110 if (idxe == 0) return -1; /* indexing off? */
1111 for (i = idx = 0; i < 3; i++) { /* 3 flags worth */
1112 if (M[aidx] & FLAG) idx = idx | (1 << i); /* test flag */
1113 MM (aidx); /* next digit */
1114 }
1115 return idx;
1116 }
1117
1118 /* Update indicators routine */
1119
1120 void upd_ind (void)
1121 {
1122 ind[IN_HPEZ] = ind[IN_HP] | ind[IN_EZ]; /* HPEZ = HP | EZ */
1123 ind[IN_DERR] = ind[IN_DACH] | ind[IN_DWLR] | ind[IN_DCYO];
1124 ind[IN_ANYCHK] = ind[IN_RDCHK] | ind[IN_WRCHK] | /* ANYCHK = all chks */
1125 ind[IN_MBREVEN] | ind[IN_MBRODD] |
1126 ind[IN_PRCHK] | ind[IN_DACH];
1127 ind[IN_IXN] = ind[IN_IXA] = ind[IN_IXB] = 0; /* clr index indics */
1128 if (!idxe) ind[IN_IXN] = 1; /* off? */
1129 else if (!idxb) ind[IN_IXA] = 1; /* on, band A? */
1130 else ind[IN_IXB] = 1; /* no, band B */
1131 return;
1132 }
1133
1134 /* Transmit routines */
1135
1136 /* Transmit field from 's' to 'd' - ignore first 'skp' flags */
1137
1138 t_stat xmt_field (uint32 d, uint32 s, uint32 skp)
1139 {
1140 uint32 cnt = 0;
1141 uint8 t;
1142
1143 do {
1144 t = M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */
1145 MM (d); MM (s); /* decr mem addrs */
1146 if (cnt++ >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */
1147 } while (((t & FLAG) == 0) || (cnt <= skp)); /* until flag */
1148 return SCPE_OK;
1149 }
1150
1151 /* Transmit record from 's' to 'd' - copy record mark if 'cpy' = TRUE */
1152
1153 t_stat xmt_record (uint32 d, uint32 s, t_bool cpy)
1154 {
1155 uint32 cnt = 0;
1156
1157 while ((M[s] & REC_MARK) != REC_MARK) { /* until rec mark */
1158 M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */
1159 PP (d); PP (s); /* incr mem addrs */
1160 if (cnt++ >= MEMSIZE) return STOP_RWRAP; /* (stop runaway) */
1161 }
1162 if (cpy) M[d] = M[s] & (FLAG | DIGIT); /* copy rec mark */
1163 return SCPE_OK;
1164 }
1165
1166 /* Transmit index from 's' to 'd' - fixed five character field */
1167
1168 t_stat xmt_index (uint32 d, uint32 s)
1169 {
1170 int32 i;
1171
1172 M[d] = M[s] & (FLAG | DIGIT); /* preserve sign */
1173 MM (d); MM (s); /* decr mem addrs */
1174 for (i = 0; i < ADDR_LEN - 2; i++) { /* copy 3 digits */
1175 M[d] = M[s] & DIGIT; /* without flags */
1176 MM (d); MM (s); /* decr mem addrs */
1177 }
1178 M[d] = (M[s] & DIGIT) | FLAG; /* set flag on last */
1179 return SCPE_OK;
1180 }
1181
1182 /* Transmit dividend from 'd' to 's' - clear flag on first digit */
1183
1184 t_stat xmt_divd (uint32 d, uint32 s)
1185 {
1186 uint32 cnt = 0;
1187
1188 M[d] = M[s] & DIGIT; /* first w/o flag */
1189 do {
1190 MM (d); MM (s); /* decr mem addrs */
1191 M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */
1192 if (cnt++ >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */
1193 } while ((M[d] & FLAG) == 0); /* until src flag */
1194 return SCPE_OK;
1195 }
1196
1197 /* Transmit numeric strip from 's' to 'd' - s is odd */
1198
1199 t_stat xmt_tns (uint32 d, uint32 s)
1200 {
1201 uint32 cnt = 0;
1202 uint8 t, z;
1203
1204 t = M[s] & DIGIT; /* get units */
1205 z = M[s - 1] & DIGIT; /* get zone */
1206 if ((z == 1) || (z == 5) || ((z == 2) && (t == 0))) /* 1x, 5x, 20? */
1207 M[d] = t | FLAG; /* set flag */
1208 else M[d] = t; /* else clear flag */
1209 do {
1210 MM (d); /* decr mem addrs */
1211 s = ADDR_S (s, 2);
1212 t = M[d] & FLAG; /* save dst flag */
1213 M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */
1214 if (cnt >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */
1215 cnt = cnt + 2;
1216 } while (t == 0); /* until dst flag */
1217 M[d] = M[d] | FLAG; /* set flag at end */
1218 return SCPE_OK;
1219 }
1220
1221 /* Transmit numeric fill from 's' to 'd' - d is odd */
1222
1223 t_stat xmt_tnf (uint32 d, uint32 s)
1224 {
1225 uint32 cnt = 0;
1226 uint8 t;
1227
1228 t = M[s]; /* get 1st digit */
1229 M[d] = t & DIGIT; /* store */
1230 M[d - 1] = (t & FLAG)? 5: 7; /* set sign from flag */
1231 do {
1232 MM (s); /* decr mem addr */
1233 d = ADDR_S (d, 2);
1234 t = M[s]; /* get src digit */
1235 M[d] = t & DIGIT; /* move to dst, no flag */
1236 M[d - 1] = 7; /* set zone */
1237 if (cnt >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */
1238 cnt = cnt + 2;
1239 } while ((t & FLAG) == 0); /* until src flag */
1240 return SCPE_OK;
1241 }
1242
1243 /* Add routine
1244
1245 Inputs:
1246 d = destination field low (P)
1247 s = source field low (Q)
1248 sub = TRUE if subtracting
1249 sto = TRUE if storing
1250 skp = number of source field flags, beyond sign, to ignore
1251 Output:
1252 return = status
1253 sta = ADD_NOCRY: no carry out, no sign change
1254 ADD_SCHNG: sign change
1255 ADD_CARRY: carry out
1256
1257 Reference Manual: "When the sum is zero, the sign of the P field
1258 is retained."
1259 */
1260
1261 t_stat add_field (uint32 d, uint32 s, t_bool sub, t_bool sto, uint32 skp, int32 *sta)
1262 {
1263 uint32 cry, src, dst, res, comp, dp, dsv;
1264 uint32 src_f = 0, cnt = 0, dst_f;
1265
1266 *sta = ADD_NOCRY; /* assume no cry */
1267 dsv = d; /* save dst */
1268 comp = ((M[d] ^ M[s]) & FLAG) ^ (sub? FLAG: 0); /* set compl flag */
1269 cry = 0; /* clr carry */
1270 ind[IN_HP] = ((M[d] & FLAG) == 0); /* set sign from res */
1271 ind[IN_EZ] = 1; /* assume zero */
1272
1273 dst = M[d] & DIGIT; /* 1st digits */
1274 src = M[s] & DIGIT;
1275 if (BAD_DIGIT (dst) || BAD_DIGIT (src)) /* bad digit? */
1276 return STOP_INVDIG;
1277 if (comp) src = 10 - src; /* complement? */
1278 res = add_one_digit (dst, src, &cry); /* add */
1279 if (sto) M[d] = (M[d] & FLAG) | res; /* store */
1280 MM (d); MM (s); /* decr mem addrs */
1281 do {
1282 dst = M[d] & DIGIT; /* get dst digit */
1283 dst_f = M[d] & FLAG; /* get dst flag */
1284 if (src_f) src = 0; /* src done? src = 0 */
1285 else {
1286 src = M[s] & DIGIT; /* get src digit */
1287 if (cnt >= skp) src_f = M[s] & FLAG; /* get src flag */
1288 MM (s); /* decr src addr */
1289 }
1290 if (BAD_DIGIT (dst) || BAD_DIGIT (src)) /* bad digit? */
1291 return STOP_INVDIG;
1292 if (comp) src = 9 - src; /* complement? */
1293 res = add_one_digit (dst, src, &cry); /* add */
1294 if (sto) M[d] = dst_f | res; /* store */
1295 MM (d); /* decr dst addr */
1296 if (cnt++ >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */
1297 } while (dst_f == 0); /* until dst done */
1298 if (!src_f) ind[IN_OVF] = 1; /* !src done? ovf */
1299 if (comp && !cry && !ind[IN_EZ]) { /* recomp needed? */
1300 ind[IN_HP] = ind[IN_HP] ^ 1; /* flip indicator */
1301 if (sto) { /* storing? */
1302 for (cry = 1, dp = dsv; dp != d; ) { /* rescan */
1303 dst = M[dp] & DIGIT; /* get dst digit */
1304 res = add_one_digit (9 - dst, 0, &cry); /* "add" */
1305 M[dp] = (M[dp] & FLAG) | res; /* store */
1306 MM (dp); /* decr dst addr */
1307 }
1308 M[dsv] = M[dsv] ^ FLAG; /* compl sign */
1309 }
1310 *sta = ADD_SIGNC; /* sign changed */
1311 return SCPE_OK;
1312 } /* end if recomp */
1313 if (ind[IN_EZ]) ind[IN_HP] = 0; /* res = 0? clr HP */
1314 if (!comp && cry) *sta = ADD_CARRY; /* set status */
1315 return SCPE_OK;
1316 }
1317
1318 /* Add one digit via table (Model 1) or "hardware" (Model 2) */
1319
1320 uint32 add_one_digit (uint32 dst, uint32 src, uint32 *cry)
1321 {
1322 uint32 res;
1323
1324 if (*cry) src = src + 1; /* cry in? incr src */
1325 if (src >= 10) { /* src > 10? */
1326 src = src - 10; /* src -= 10 */
1327 *cry = 1; /* carry out */
1328 }
1329 else *cry = 0; /* else no carry */
1330 if (cpu_unit.flags & IF_MII) /* Model 2? */
1331 res = sum_table[dst + src]; /* "hardware" */
1332 else res = M[ADD_TABLE + (dst * 10) + src]; /* table lookup */
1333 if (res & FLAG) *cry = 1; /* carry out? */
1334 if (res & DIGIT) ind[IN_EZ] = 0; /* nz? clr ind */
1335 return res & DIGIT;
1336 }
1337
1338 /* Multiply routine
1339
1340 Inputs:
1341 mpc = multiplicand address
1342 mpy = multiplier address
1343 Outputs:
1344 return = status
1345
1346 Reference manual: "A zero product may have a negative or positive sign,
1347 depending on the signs of the fields at the P and Q addresses."
1348 */
1349
1350 t_stat mul_field (uint32 mpc, uint32 mpy)
1351 {
1352 int32 i;
1353 uint32 pro; /* prod pointer */
1354 uint32 mpyd, mpyf; /* mpy digit, flag */
1355 uint32 cnt = 0; /* counter */
1356 uint8 sign; /* final sign */
1357 t_stat r;
1358
1359 PR1 = 1; /* step on PR1 */
1360 for (i = 0; i < PROD_AREA_LEN; i++) /* clr prod area */
1361 M[PROD_AREA + i] = 0;
1362 sign = (M[mpc] & FLAG) ^ (M[mpy] & FLAG); /* get final sign */
1363 ind[IN_HP] = (sign == 0); /* set indicators */
1364 ind[IN_EZ] = 1;
1365 pro = PROD_AREA + PROD_AREA_LEN - 1; /* product ptr */
1366
1367 /* Loop on multiplier (mpy) and product (pro) digits */
1368
1369 do {
1370 mpyd = M[mpy] & DIGIT; /* multiplier digit */
1371 mpyf = (M[mpy] & FLAG) && (cnt != 0); /* last digit flag */
1372 if (BAD_DIGIT (mpyd)) return STOP_INVDIG; /* bad? */
1373 r = mul_one_digit (mpyd, mpc, pro, mpyf); /* prod += mpc*mpy_dig */
1374 if (r != SCPE_OK) return r; /* error? */
1375 MM (mpy); MM (pro); /* decr mpyr, prod addrs */
1376 if (cnt++ > MEMSIZE) return STOP_FWRAP; /* (stop runaway) */
1377 } while ((mpyf == 0) || (cnt <= 1)); /* until mpyr flag */
1378
1379 if (ind[IN_EZ]) ind[IN_HP] = 0; /* res = 0? clr HP */
1380 M[PROD_AREA + PROD_AREA_LEN - 1] |= sign; /* set final sign */
1381 return SCPE_OK;
1382 }
1383
1384 /* Multiply step
1385
1386 Inputs:
1387 mpyd = multiplier digit (tested valid)
1388 mpcp = multiplicand low address
1389 prop = product low address
1390 last = last iteration flag (set flag on high product)
1391 Outputs:
1392 prod += multiplicand * multiplier_digit
1393 return = status
1394
1395 The multiply table address is constructed as follows:
1396 - double the multiplier digit
1397 - use the 10's digit of the doubled result, + 1, as the 100's digit
1398 of the table address
1399 - use the multiplicand digit as the 10's digit of the table address
1400 - use the unit digit of the doubled result as the unit digit of the
1401 table address
1402 EZ indicator is cleared if a non-zero digit is ever generated
1403 */
1404
1405 t_stat mul_one_digit (uint32 mpyd, uint32 mpcp, uint32 prop, uint32 last)
1406 {
1407 uint32 mpta, mptb; /* mult table */
1408 uint32 mptd; /* mult table digit */
1409 uint32 mpcd, mpcf; /* mpc digit, flag */
1410 uint32 prwp; /* prod working ptr */
1411 uint32 prod; /* product digit */
1412 uint32 cry; /* carry */
1413 uint32 mpcc, cryc; /* counters */
1414
1415 mptb = MUL_TABLE + ((mpyd <= 4)? (mpyd * 2): /* set mpy table 100's, */
1416 (((mpyd - 5) * 2) + 100)); /* 1's digits */
1417
1418 /* Inner loop on multiplicand (mpcp) and product (prop) digits */
1419
1420 mpcc = 0; /* multiplicand ctr */
1421 do {
1422 prwp = prop; /* product working ptr */
1423 mpcd = M[mpcp] & DIGIT; /* multiplicand digit */
1424 mpcf = M[mpcp] & FLAG; /* multiplicand flag */
1425 if (BAD_DIGIT (mpcd)) return STOP_INVDIG; /* bad? */
1426 mpta = mptb + (mpcd * 10); /* mpy table 10's digit */
1427 cry = 0; /* init carry */
1428 mptd = M[mpta] & DIGIT; /* mpy table digit */
1429 if (BAD_DIGIT (mptd)) return STOP_INVDIG; /* bad? */
1430 prod = M[prwp] & DIGIT; /* product digit */
1431 if (BAD_DIGIT (prod)) return STOP_INVDIG; /* bad? */
1432 M[prwp] = add_one_digit (prod, mptd, &cry); /* add mpy tbl to prod */
1433 MM (prwp); /* decr working ptr */
1434 mptd = M[mpta + 1] & DIGIT; /* mpy table digit */
1435 if (BAD_DIGIT (mptd)) return STOP_INVDIG; /* bad? */
1436 prod = M[prwp] & DIGIT; /* product digit */
1437 if (BAD_DIGIT (prod)) return STOP_INVDIG; /* bad? */
1438 M[prwp] = add_one_digit (prod, mptd, &cry); /* add mpy tbl to prod */
1439 cryc = 0; /* (stop runaway) */
1440 while (cry) { /* propagate carry */
1441 MM (prwp); /* decr working ptr */
1442 prod = M[prwp] & DIGIT; /* product digit */
1443 if (BAD_DIGIT (prod)) return STOP_INVDIG; /* bad? */
1444 M[prwp] = add_one_digit (prod, 0, &cry); /* add cry */
1445 if (cryc++ > MEMSIZE) return STOP_FWRAP;
1446 }
1447 MM (mpcp); MM (prop); /* decr mpc, prod ptrs */
1448 if (mpcc++ > MEMSIZE) return STOP_FWRAP;
1449 } while ((mpcf == 0) || (mpcc <= 1)); /* until mpcf flag */
1450 if (last) M[prop] = M[prop] | FLAG; /* flag high product */
1451 return SCPE_OK;
1452 }
1453
1454 /* Divide routine - comments from Geoff Kuenning's 1620 simulator
1455
1456 The destination of the divide is given by:
1457
1458 100 - <# digits in quotient>
1459
1460 Which is more easily calculated as:
1461
1462 100 - <# digits in divisor> - <# digits in dividend>
1463
1464 The quotient goes into 99 minus the divisor length. The
1465 remainder goes into 99. The load dividend instruction (above)
1466 should have specified a P address of 99 minus the size of the
1467 divisor.
1468
1469 Note that this all implies that "dest" points to the *leftmost*
1470 digit of the dividend.
1471
1472 After the division, the assumed decimal point will be as many
1473 positions to the left as there are digits in the divisor. In
1474 other words, a 4-digit divisor will produce 4 (assumed) decimal
1475 places.
1476
1477 There are other ways to do these things. In particular, the
1478 load-dividend instruction doesn't have to specify the above
1479 formula; if it's done differently, then you don't have to get
1480 decimal places. This is not well-explained in the books I have.
1481
1482 How to divide on a 1620:
1483
1484 The dividend is the field at 99:
1485
1486 90 = _1234567890
1487
1488 The divisor is somewhere else in memory:
1489
1490 _03
1491
1492 The divide operation specifies the left-most digit of the
1493 dividend as the place to begin trial subtractions:
1494
1495 DM 90,3
1496
1497 The loop works as follows:
1498
1499 1. Call the left-most digit of the dividend "current_dividend".
1500 Call the location current_dividend - <divisor_length>
1501 "quotient_digit".
1502 2. Clear the flag at current_dividend, and set one at
1503 quotient_digit.
1504
1505 88 = _001234567890, q_d = 88, c_d = 90
1506 [Not actually done; divisor length controls subtract.]
1507 3. Subtract the divisor from the field at current-dividend,
1508 using normal 1620 rules, except that signs are ignored.
1509 Continue these subtractions until either 10 subtractions
1510 have been done, or you get a negative result:
1511
1512 88 = _00_2234567890, q_d = 88, c_d = 90
1513 4. If 10 subtractions have been done, set the overflow
1514 indicator and abort. Otherwise, add the divisor back to
1515 correct for the oversubtraction:
1516
1517 88 = _001234567890, q_d = 88, c_d = 90
1518 5. Store the (net) number of subtractions in quotient_digit:
1519
1520 88 = _001234567890, q_d = 88, c_d = 90
1521 6. If this is not the first pass, clear the flag at
1522 quotient_digit. Increment quotient_digit and
1523 current_dividend, and set a flag at the new
1524 quotient_digit:
1525
1526 88 = _0_01234567890, q_d = 89, c_d = 91
1527 [If first pass, set a flag at quotient digit.]
1528 7. If current_dividend is not 100, repeat steps 3 through 7.
1529 8. Set flags at 99 and quotient_digit - 1 according to the
1530 rules of algebra: the quotient's sign is the exclusive-or
1531 of the signs of the divisor and dividend, and the
1532 remainder has the sign of the dividend:
1533
1534 10 / 3 = 3 remainder 1
1535 10 / -3 = -3 remainder 1
1536 -10 / 3 = -3 remainder -1
1537 -10 / -3 = 3 remainder -1
1538
1539 This preserves the relationship dd = q * dv + r.
1540
1541 Our example continues as follows for steps 3 through 7:
1542
1543 3. 88 = _0_00_334567890, q_d = 89, c_d = 91
1544 4. 88 = _0_00034567890
1545 5. 88 = _0_40034567890
1546 6. 88 = _04_0034567890, q_d = 90, c_d = 92
1547 3. 88 = _04_00_34567890
1548 4. 88 = _04_0004567890
1549 5. 88 = _04_1004567890
1550 6. 88 = _041_004567890, q_d = 91, c_d = 93
1551 3. 88 = _041_00_2567890
1552 4. 88 = _041_001567890
1553 5. 88 = _041_101567890
1554 6. 88 = _0411_01567890, q_d = 92, c_d = 94
1555 3. 88 = _0411_00_367890
1556 4. 88 = _0411_00067890
1557 5. 88 = _0411_50067890
1558 6. 88 = _04115_0067890, q_d = 93, c_d = 95
1559 3. 88 = _04115_00_37890
1560 4. 88 = _04115_0007890
1561 5. 88 = _04115_2007890
1562 6. 88 = _041152_007890, q_d = 94, c_d = 96
1563 3. 88 = _041152_00_2890
1564 4. 88 = _041152_001890
1565 5. 88 = _041152_201890
1566 6. 88 = _0411522_01890, q_d = 95, c_d = 97
1567 3. 88 = _0411522_00_390
1568 4. 88 = _0411522_00090
1569 5. 88 = _0411522_60090
1570 6. 88 = _04115226_0090, q_d = 96, c_d = 98
1571 3. 88 = _04115226_00_30
1572 4. 88 = _04115226_0000
1573 5. 88 = _04115226_3000
1574 6. 88 = _041152263_000, q_d = 97, c_d = 99
1575 3. 88 = _041152263_00_3
1576 4. 88 = _041152263_000
1577 5. 88 = _041152263_000
1578 6. 88 = _0411522630_00, q_d = 98, c_d = 100
1579
1580 In the actual code below, we elide several of these steps in
1581 various ways for convenience and efficiency.
1582
1583 Note that the EZ indicator is NOT valid for divide, because it
1584 is cleared by any non-zero result in an intermediate add. The
1585 code maintains its own EZ indicator for the quotient.
1586 */
1587
1588 t_stat div_field (uint32 dvd, uint32 dvr, int32 *ez)
1589 {
1590 uint32 quop, quod, quos; /* quo ptr, dig, sign */
1591 uint32 dvds; /* dvd sign */
1592 t_bool first = TRUE; /* first pass */
1593 t_stat r;
1594
1595 dvds = (M[PROD_AREA + PROD_AREA_LEN - 1]) & FLAG; /* dividend sign */
1596 quos = dvds ^ (M[dvr] & FLAG); /* quotient sign */
1597 ind[IN_HP] = (quos == 0); /* set indicators */
1598 *ez = 1;
1599
1600 /* Loop on current dividend, high order digit at dvd */
1601
1602 do {
1603 r = div_one_digit (dvd, dvr, 10, &quod, &quop); /* dev quo digit */
1604 if (r != SCPE_OK) return r; /* error? */
1605
1606 /* Store quotient digit and advance current dividend pointer */
1607
1608 if (first) { /* first pass? */
1609 if (quod >= 10) { /* overflow? */
1610 ind[IN_OVF] = 1; /* set indicator */
1611 return STOP_OVERFL; /* stop */
1612 }
1613 M[quop] = FLAG | quod; /* set flag on quo */
1614 first = FALSE;
1615 }
1616 else M[quop] = quod; /* store quo digit */
1617 if (quod) *ez = 0; /* if nz, clr ind */
1618 PP (dvd); /* incr dvd ptr */
1619 } while (dvd != (PROD_AREA + PROD_AREA_LEN)); /* until end prod */
1620
1621 /* Division done. Set signs of quo, rem, set flag on high order remainder */
1622
1623 if (*ez) ind[IN_HP] = 0; /* res = 0? clr HP */
1624 M[PROD_AREA + PROD_AREA_LEN - 1] |= dvds; /* remainder sign */
1625 M[quop] = M[quop] | quos; /* quotient sign */
1626 PP (quop); /* high remainder */
1627 M[quop] = M[quop] | FLAG; /* set flag */
1628 return SCPE_OK;
1629 }
1630
1631 /* Divide step
1632
1633 Inputs:
1634 dvd = current dividend address (high digit)
1635 dvr = divisor address (low digit)
1636 max = max number of iterations before overflow
1637 &quod = address to store quotient digit
1638 &quop = address to store quotient pointer (can be NULL)
1639 Outputs:
1640 return = status
1641
1642 Divide step calculates a quotient digit by repeatedly subtracting the
1643 divisor from the current dividend. The divisor's length controls the
1644 subtraction; dividend flags are ignored.
1645 */
1646
1647 t_stat div_one_digit (uint32 dvd, uint32 dvr, uint32 max,
1648 uint32 *quod, uint32 *quop)
1649 {
1650 uint32 dvrp, dvrd, dvrf; /* dvr ptr, dig, flag */
1651 uint32 dvdp, dvdd; /* dvd ptr, dig */
1652 uint32 qd, cry; /* quo dig, carry */
1653 uint32 cnt;
1654
1655 for (qd = 0; qd < max; qd++) { /* devel quo dig */
1656 dvrp = dvr; /* divisor ptr */
1657 dvdp = dvd; /* dividend ptr */
1658 cnt = 0;
1659 cry = 1; /* carry in = 1 */
1660 do { /* sub dvr fm dvd */
1661 dvdd = M[dvdp] & DIGIT; /* dividend digit */
1662 if (BAD_DIGIT (dvdd)) return STOP_INVDIG; /* bad? */
1663 dvrd = M[dvrp] & DIGIT; /* divisor digit */
1664 dvrf = M[dvrp] & FLAG; /* divisor flag */
1665 if (BAD_DIGIT (dvrd)) return STOP_INVDIG; /* bad? */
1666 M[dvdp] = add_one_digit (dvdd, 9 - dvrd, &cry); /* sub */
1667 MM (dvdp); MM (dvrp); /* decr ptrs */
1668 if (cnt++ > MEMSIZE) return STOP_FWRAP; /* (stop runaway) */
1669 } while ((dvrf == 0) || (cnt <= 1)); /* until dvr flag */
1670 if (!cry) { /* !cry = borrow */
1671 dvdd = M[dvdp] & DIGIT; /* borrow digit */
1672 if (BAD_DIGIT (dvdd)) return STOP_INVDIG; /* bad? */
1673 M[dvdp] = add_one_digit (dvdd, 9, &cry); /* sub */
1674 }
1675 if (!cry) break; /* !cry = negative */
1676 }
1677
1678 /* Add back the divisor to correct for the negative result */
1679
1680 dvrp = dvr; /* divisor ptr */
1681 dvdp = dvd; /* dividend ptr */
1682 cnt = 0;
1683 cry = 0; /* carry in = 0 */
1684 do {
1685 dvdd = M[dvdp] & DIGIT; /* dividend digit */
1686 dvrd = M[dvrp] & DIGIT; /* divisor digit */
1687 dvrf = M[dvrp] & FLAG; /* divisor flag */
1688 M[dvdp] = add_one_digit (dvdd, dvrd, &cry); /* add */
1689 MM (dvdp); MM (dvrp); cnt++; /* decr ptrs */
1690 } while ((dvrf == 0) || (cnt <= 1)); /* until dvr flag */
1691 if (cry) { /* carry out? */
1692 dvdd = M[dvdp] & DIGIT; /* borrow digit */
1693 M[dvdp] = add_one_digit (dvdd, 0, &cry); /* add */
1694 }
1695 if (quop != NULL) *quop = dvdp; /* set quo addr */
1696 *quod = qd; /* set quo digit */
1697 return SCPE_OK;
1698 }
1699
1700 /* Logical operation routines (and, or, xor, complement)
1701
1702 Inputs:
1703 d = destination address
1704 s = source address
1705 Output:
1706 return = status
1707
1708 Destination flags are preserved; EZ reflects the result.
1709 COM does not obey normal field length restrictions.
1710 */
1711
1712 t_stat or_field (uint32 d, uint32 s)
1713 {
1714 uint32 cnt = 0;
1715 int32 t;
1716
1717 ind[IN_EZ] = 1; /* assume result zero */
1718 do {
1719 t = M[s]; /* get src */
1720 M[d] = (M[d] & FLAG) | ((M[d] | t) & 07); /* OR src to dst */
1721 if (M[d] & DIGIT) ind[IN_EZ] = 0; /* nz dig? clr ind */
1722 MM (d); MM (s); /* decr pointers */
1723 if (cnt++ >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */
1724 } while (((t & FLAG) == 0) || (cnt <= 1)); /* until src flag */
1725 return SCPE_OK;
1726 }
1727
1728 t_stat and_field (uint32 d, uint32 s)
1729 {
1730 uint32 cnt = 0;
1731 int32 t;
1732
1733 ind[IN_EZ] = 1; /* assume result zero */
1734 do {
1735 t = M[s]; /* get src */
1736 M[d] = (M[d] & FLAG) | ((M[d] & t) & 07); /* AND src to dst */
1737 if (M[d] & DIGIT) ind[IN_EZ] = 0; /* nz dig? clr ind */
1738 MM (d); MM (s); /* decr pointers */
1739 if (cnt++ >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */
1740 } while (((t & FLAG) == 0) || (cnt <= 1)); /* until src flag */
1741 return SCPE_OK;
1742 }
1743
1744 t_stat xor_field (uint32 d, uint32 s)
1745 {
1746 uint32 cnt = 0;
1747 int32 t;
1748
1749 ind[IN_EZ] = 1; /* assume result zero */
1750 do {
1751 t = M[s]; /* get src */
1752 M[d] = (M[d] & FLAG) | ((M[d] ^ t) & 07); /* XOR src to dst */
1753 if (M[d] & DIGIT) ind[IN_EZ] = 0; /* nz dig? clr ind */
1754 MM (d); MM (s); /* decr pointers */
1755 if (cnt++ >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */
1756 } while (((t & FLAG) == 0) || (cnt <= 1)); /* until src flag */
1757 return SCPE_OK;
1758 }
1759
1760 t_stat com_field (uint32 d, uint32 s)
1761 {
1762 uint32 cnt = 0;
1763 int32 t;
1764
1765 ind[IN_EZ] = 1; /* assume result zero */
1766 do {
1767 t = M[s]; /* get src */
1768 M[d] = (t & FLAG) | ((t ^ 07) & 07); /* comp src to dst */
1769 if (M[d] & DIGIT) ind[IN_EZ] = 0; /* nz dig? clr ind */
1770 MM (d); MM (s); /* decr pointers */
1771 if (cnt++ >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */
1772 } while ((t & FLAG) == 0); /* until src flag */
1773 return SCPE_OK;
1774 }
1775
1776 /* Octal to decimal
1777
1778 Inputs:
1779 tbl = conversion table address (low digit)
1780 s = source address
1781 Outputs:
1782 product area = converted source
1783 result = status
1784
1785 OTD is a cousin of multiply. The octal digits in the source are
1786 multiplied by successive values in the conversion table, and the
1787 results are accumulated in the product area. Although the manual
1788 does not say, this code assumes that EZ and HP are affected.
1789 */
1790
1791 t_stat oct_to_dec (uint32 tbl, uint32 s)
1792 {
1793 uint32 cnt = 0, tblc;
1794 uint32 i, sd, sf, tf, sign;
1795 t_stat r;
1796
1797 for (i = 0; i < PROD_AREA_LEN; i++) /* clr prod area */
1798 M[PROD_AREA + i] = 0;
1799 sign = M[s] & FLAG; /* save sign */
1800 ind[IN_EZ] = 1; /* set indicators */
1801 ind[IN_HP] = (sign == 0);
1802 do {
1803 sd = M[s] & DIGIT; /* src digit */
1804 sf = M[s] & FLAG; /* src flag */
1805 r = mul_one_digit (sd, tbl, PROD_AREA + PROD_AREA_LEN - 1, sf);
1806 if (r != SCPE_OK) return r; /* err? */
1807 MM (s); /* decr src addr */
1808 MM (tbl); /* skip 1st tbl dig */
1809 tblc = 0; /* count */
1810 do {
1811 tf = M[tbl] & FLAG; /* get next */
1812 MM (tbl); /* decr ptr */
1813 if (tblc++ > MEMSIZE) return STOP_FWRAP;
1814 } while (tf == 0); /* until flag */
1815 if (cnt++ > MEMSIZE) return STOP_FWRAP; /* (stop runaway) */
1816 } while (sf == 0);
1817 if (ind[IN_EZ]) ind[IN_HP] = 0; /* res = 0? clr HP */
1818 M[PROD_AREA + PROD_AREA_LEN - 1] |= sign; /* set sign */
1819 return SCPE_OK;
1820 }
1821
1822 /* Decimal to octal
1823
1824 Inputs:
1825 d = destination address
1826 tbl = conversion table address (low digit of highest power)
1827 &ez = address of soft EZ indicator
1828 product area = field to convert
1829 Outputs:
1830 return = status
1831
1832 DTO is a cousin to divide. The number in the product area is repeatedly
1833 divided by successive values in the conversion table, and the quotient
1834 digits are stored in the destination. Although the manual does not say,
1835 this code assumes that EZ and HP are affected.
1836 */
1837
1838 t_stat dec_to_oct (uint32 d, uint32 tbl, int32 *ez)
1839 {
1840 uint32 sign, octd, t;
1841 t_bool first = TRUE;
1842 uint32 ctr = 0;
1843 t_stat r;
1844
1845 sign = M[PROD_AREA + PROD_AREA_LEN - 1] & FLAG; /* input sign */
1846 *ez = 1; /* set indicators */
1847 ind[IN_HP] = (sign == 0);
1848 for ( ;; ) {
1849 r = div_one_digit (PROD_AREA + PROD_AREA_LEN - 1, /* divide */
1850 tbl, 8, &octd, NULL);
1851 if (r != SCPE_OK) return r; /* error? */
1852 if (first) { /* first pass? */
1853 if (octd >= 8) { /* overflow? */
1854 ind[IN_OVF] = 1; /* set indicator */
1855 return SCPE_OK; /* stop */
1856 }
1857 M[d] = FLAG | octd; /* set flag on quo */
1858 first = FALSE;
1859 }
1860 else M[d] = octd; /* store quo digit */
1861 if (octd) *ez = 0; /* if nz, clr ind */
1862 PP (tbl); /* incr tbl addr */
1863 if ((M[tbl] & REC_MARK) == REC_MARK) break; /* record mark? */
1864 PP (tbl); /* skip flag */
1865 if ((M[tbl] & REC_MARK) == REC_MARK) break; /* record mark? */
1866 do { /* look for F, rec mk */
1867 PP (tbl);
1868 t = M[tbl];
1869 } while (((t & FLAG) == 0) && ((t & REC_MARK) != REC_MARK));
1870 MM (tbl); /* step back one */
1871 PP (d); /* incr quo addr */
1872 if (ctr++ > MEMSIZE) return STOP_FWRAP; /* (stop runaway) */
1873 }
1874 if (*ez) ind[IN_HP] = 0; /* res = 0? clr HP */
1875 M[d] = M[d] | sign; /* set result sign */
1876 return SCPE_OK;
1877 }
1878
1879 /* Reset routine */
1880
1881 t_stat cpu_reset (DEVICE *dptr)
1882 {
1883 int32 i;
1884 static t_bool one_time = TRUE;
1885
1886 PR1 = IR2 = 1; /* invalidate PR1,IR2 */
1887 ind[0] = 0;
1888 for (i = IN_SW4 + 1; i < NUM_IND; i++) ind[i] = 0; /* init indicators */
1889 if (cpu_unit.flags & IF_IA) iae = 1; /* indirect enabled? */
1890 else iae = 0;
1891 idxe = idxb = 0; /* indexing off */
1892 pcq_r = find_reg ("PCQ", NULL, dptr); /* init old PC queue */
1893 if (pcq_r) pcq_r->qptr = 0;
1894 else return SCPE_IERR;
1895 sim_brk_types = sim_brk_dflt = SWMASK ('E'); /* init breakpoints */
1896 upd_ind (); /* update indicators */
1897 if (one_time) cpu_set_table (&cpu_unit, 1, NULL, NULL); /* set default tables */
1898 one_time = FALSE;
1899 return SCPE_OK;
1900 }
1901
1902 /* Memory examine */
1903
1904 t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
1905 {
1906 if (addr >= MEMSIZE) return SCPE_NXM;
1907 if (vptr != NULL) *vptr = M[addr] & (FLAG | DIGIT);
1908 return SCPE_OK;
1909 }
1910
1911 /* Memory deposit */
1912
1913 t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
1914 {
1915 if (addr >= MEMSIZE) return SCPE_NXM;
1916 M[addr] = val & (FLAG | DIGIT);
1917 return SCPE_OK;
1918 }
1919
1920 /* Memory size change */
1921
1922 t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
1923 {
1924 int32 mc = 0;
1925 uint32 i;
1926
1927 if ((val <= 0) || (val > MAXMEMSIZE) || ((val % 1000) != 0))
1928 return SCPE_ARG;
1929 for (i = val; i < MEMSIZE; i++) mc = mc | M[i];
1930 if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))
1931 return SCPE_OK;
1932 MEMSIZE = val;
1933 for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0;
1934 return SCPE_OK;
1935 }
1936
1937 /* Model change */
1938
1939 t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc)
1940 {
1941 if (val) cpu_unit.flags = (cpu_unit.flags & (UNIT_SCP | UNIT_BCD | MII_OPT)) |
1942 IF_DIV | IF_IA | IF_EDT;
1943 else cpu_unit.flags = cpu_unit.flags & (UNIT_SCP | UNIT_BCD | MI_OPT);
1944 return SCPE_OK;
1945 }
1946
1947 /* Set/clear Model 1 option */
1948
1949 t_stat cpu_set_opt1 (UNIT *uptr, int32 val, char *cptr, void *desc)
1950 {
1951 if (cpu_unit.flags & IF_MII) {
1952 printf ("Feature is standard on 1620 Model 2\n");
1953 if (sim_log) fprintf (sim_log, "Feature is standard on 1620 Model 2\n");
1954 return SCPE_NOFNC;
1955 }
1956 return SCPE_OK;
1957 }
1958
1959 /* Set/clear Model 2 option */
1960
1961 t_stat cpu_set_opt2 (UNIT *uptr, int32 val, char *cptr, void *desc)
1962 {
1963 if (!(cpu_unit.flags & IF_MII)) {
1964 printf ("Feature is not available on 1620 Model 1\n");
1965 if (sim_log) fprintf (sim_log, "Feature is not available on 1620 Model 1\n");
1966 return SCPE_NOFNC;
1967 }
1968 return SCPE_OK;
1969 }
1970
1971 /* Front panel save */
1972
1973 t_stat cpu_set_save (UNIT *uptr, int32 val, char *cptr, void *desc)
1974 {
1975 if (saved_PC & 1) return SCPE_NOFNC;
1976 PR1 = saved_PC;
1977 return SCPE_OK;
1978 }
1979
1980 /* Set standard add/multiply tables */
1981
1982 t_stat cpu_set_table (UNIT *uptr, int32 val, char *cptr, void *desc)
1983 {
1984 int32 i;
1985
1986 for (i = 0; i < MUL_TABLE_LEN; i++) /* set mul table */
1987 M[MUL_TABLE + i] = std_mul_table[i];
1988 if (((cpu_unit.flags & IF_MII) == 0) || val) { /* set add table */
1989 for (i = 0; i < ADD_TABLE_LEN; i++)
1990 M[ADD_TABLE + i] = std_add_table[i];
1991 }
1992 return SCPE_OK;
1993 }
1994
1995 /* Set history */
1996
1997 t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc)
1998 {
1999 int32 i, lnt;
2000 t_stat r;
2001
2002 if (cptr == NULL) {
2003 for (i = 0; i < hst_lnt; i++) hst[i].vld = 0;
2004 hst_p = 0;
2005 return SCPE_OK;
2006 }
2007 lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r);
2008 if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG;
2009 hst_p = 0;
2010 if (hst_lnt) {
2011 free (hst);
2012 hst_lnt = 0;
2013 hst = NULL;
2014 }
2015 if (lnt) {
2016 hst = (InstHistory *) calloc (lnt, sizeof (InstHistory));
2017 if (hst == NULL) return SCPE_MEM;
2018 hst_lnt = lnt;
2019 }
2020 return SCPE_OK;
2021 }
2022
2023 /* Show history */
2024
2025 t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc)
2026 {
2027 int32 i, k, di, lnt;
2028 char *cptr = (char *) desc;
2029 t_value sim_eval[INST_LEN];
2030 t_stat r;
2031 InstHistory *h;
2032 extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val,
2033 UNIT *uptr, int32 sw);
2034
2035 if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */
2036 if (cptr) {
2037 lnt = (int32) get_uint (cptr, 10, hst_lnt, &r);
2038 if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG;
2039 }
2040 else lnt = hst_lnt;
2041 di = hst_p - lnt; /* work forward */
2042 if (di < 0) di = di + hst_lnt;
2043 fprintf (st, "PC IR\n\n");
2044 for (k = 0; k < lnt; k++) { /* print specified */
2045 h = &hst[(++di) % hst_lnt]; /* entry pointer */
2046 if (h->vld) { /* instruction? */
2047 fprintf (st, "%05d ", h->pc);
2048 for (i = 0; i < INST_LEN; i++)
2049 sim_eval[i] = h->inst[i];
2050 if ((fprint_sym (st, h->pc, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) {
2051 fprintf (st, "(undefined)");
2052 for (i = 0; i < INST_LEN; i++)
2053 fprintf (st, "%02X", h->inst[i]);
2054 }
2055 fputc ('\n', st); /* end line */
2056 } /* end else instruction */
2057 } /* end for */
2058 return SCPE_OK;
2059 }