1 /* i1620_cpu.c: IBM 1620 CPU simulator
3 Copyright (c) 2002-2006, Robert M. Supnik
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 Except as contained in this notice, the name of Robert M Supnik shall not 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.
26 This CPU module incorporates code and comments from the 1620 simulator by
27 Geoff Kuenning, with his permission.
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)
39 The simulated register state for the IBM 1620 is:
43 IR1 [PC] program counter
44 IR2 instruction register 2 (subroutine return address)
47 PR1 manual save address
50 Additional internal registers OR3, PR2, and PR3 are not simulated.
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.
57 The 1620 uses a fixed, 12 digit instruction format:
64 ppppp = P (usually destination) address
65 qqqqq = Q (usually source) address
67 Immediate instructions use the qqqqq field as the second operand.
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.
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.
80 1. Reasons to stop. The simulator can be stopped by:
83 breakpoint encountered
84 illegal addresses or instruction formats
85 I/O error in I/O simulator
87 2. Interrupts. The 1620 has no interrupt structure.
89 3. Non-existent memory. On the 1620, all memory references
90 are modulo the memory size.
92 4. Adding I/O devices. These modules must be modified:
94 i1620_cpu.c add iodisp table entry
95 i1620_sys.c add sim_devices table entry
98 #include "i1620_defs.h"
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
104 #define HIST_PC 0x40000000
106 #define HIST_MAX 65536
111 uint8 inst
[INST_LEN
];
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 */
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
;
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
);
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
);
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
);
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
);
191 /* CPU data structures
193 cpu_dev CPU device descriptor
194 cpu_unit CPU unit descriptor
195 cpu_reg CPU register list
196 cpu_mod CPU modifier list
199 UNIT cpu_unit
= { UDATA (NULL
, UNIT_FIX
+UNIT_BCD
+MI_STD
, MAXMEMSIZE
) };
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) },
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
},
256 "CPU", &cpu_unit
, cpu_reg
, cpu_mod
,
258 &cpu_ex
, &cpu_dep
, &cpu_reset
,
262 /* Instruction table */
264 const int32 op_table
[100] = {
266 IF_FP
+ IF_VPA
+ IF_VQA
, /* FADD */
267 IF_FP
+ IF_VPA
+ IF_VQA
, /* FSUB */
268 IF_FP
+ IF_VPA
+ IF_VQA
, /* FMUL */
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 */
308 IF_VPA
+ IF_VQA
, /* BD */
309 IF_VPA
+ IF_VQA
, /* BNF */
310 IF_VPA
+ IF_VQA
, /* BNR */
320 IF_VPA
+ IF_VQA
, /* BNG - disk sys */
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 */
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 */
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 */
367 /* IO dispatch table */
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
392 /* Indicator table: -1 = illegal, +1 = resets when tested */
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 */
407 /* Add table for 1620 Model 1 */
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
422 /* Add table for 1620 Model 2 ("hardware add") */
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
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
454 #define BRANCH(x) PCQ_ENTRY; PC = (x)
455 #define GET_IDXADDR(x) ((idxb? IDX_B: IDX_A) + ((x) * ADDR_LEN) + (ADDR_LEN - 1))
457 t_stat
sim_instr (void)
459 uint32 PC
, pla
, qla
, f0
, f1
;
460 int32 i
, t
, idx
, flags
, sta
, dev
, op
;
463 /* Restore saved state */
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 */
471 /* Main instruction fetch/decode loop */
473 while (reason
== 0) { /* loop until halted */
475 saved_PC
= PC
; /* commit prev instr */
476 if (sim_interval
<= 0) { /* check clock queue */
477 if (reason
= sim_process_event ()) break;
480 if (sim_brk_summ
&& sim_brk_test (PC
, SWMASK ('E'))) { /* breakpoint? */
481 reason
= STOP_IBKPT
; /* stop simulation */
485 sim_interval
= sim_interval
- 1;
487 /* Instruction fetch and address decode */
489 if (PC
& 1) { /* PC odd? */
490 reason
= STOP_INVIAD
; /* stop */
494 op
= get_2d (PC
); /* get opcode */
495 if (op
< 0) { /* invalid? */
496 reason
= STOP_INVINS
;
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 */
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 */
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 */
517 if (reason
!= SCPE_OK
) { /* stop if invalid */
518 reason
= reason
+ (STOP_INVQDG
- STOP_INVPDG
);
522 else if (flags
& IF_IMM
) QAR
= qla
; /* immediate? */
524 if (hst_lnt
) { /* history enabled? */
525 hst_p
= (hst_p
+ 1); /* next entry */
526 if (hst_p
>= hst_lnt
) hst_p
= 0;
529 for (i
= 0; i
< INST_LEN
; i
++)
530 hst
[hst_p
].inst
[i
] = M
[(PC
+ i
) % MEMSIZE
];
533 PC
= PC
+ INST_LEN
; /* advance PC */
534 switch (op
) { /* case on op */
536 /* Transmit digit - P,Q are valid */
540 M
[PAR
] = M
[QAR
] & (FLAG
| DIGIT
); /* move dig, flag */
543 /* Transmit field - P,Q are valid */
547 reason
= xmt_field (PAR
, QAR
, 1); /* xmit field */
550 /* Transmit record - P,Q are valid */
553 reason
= xmt_record (PAR
, QAR
, TRUE
); /* xmit record */
556 /* Transmit record no record mark - P,Q are valid */
559 reason
= xmt_record (PAR
, QAR
, FALSE
); /* xmit record but */
560 break; /* not rec mark */
562 /* Set flag - P is valid */
565 M
[PAR
] = M
[PAR
] | FLAG
; /* set flag on P */
568 /* Clear flag - P is valid */
571 M
[PAR
] = M
[PAR
] & ~FLAG
; /* clear flag on P */
574 /* Branch - P is valid */
577 BRANCH (PAR
); /* branch to P */
580 /* Branch and transmit - P,Q are valid */
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 */
589 /* Branch and transmit floating - P,Q are valid */
592 reason
= xmt_field (ADDR_S (PAR
, 1), QAR
, 3); /* skip 3 flags */
593 IR2
= PC
; /* save PC */
594 BRANCH (PAR
); /* branch to P */
597 /* Branch and transmit address - P,Q are valid */
601 reason
= xmt_field (ADDR_S (PAR
, 1), QAR
, 4); /* skip 4 flags */
602 IR2
= PC
; /* save PC */
603 BRANCH (PAR
); /* branch to P */
609 if (PR1
!= 1) { /* PR1 valid? */
610 BRANCH (PR1
); /* return to PR1 */
611 PR1
= 1; /* invalidate */
613 else if (IR2
!= 1) { /* IR2 valid? */
614 BRANCH (IR2
); /* return to IR2 */
615 IR2
= 1; /* invalidate */
617 else reason
= STOP_INVRTN
; /* MAR check */
620 /* Branch on digit (not zero) - P,Q are valid */
623 if ((M
[QAR
] & DIGIT
) != 0) { /* digit != 0? */
624 BRANCH (PAR
); /* branch */
628 /* Branch no flag - P,Q are valid */
631 if ((M
[QAR
] & FLAG
) == 0) { /* flag == 0? */
632 BRANCH (PAR
); /* branch */
636 /* Branch no record mark (8-2 not set) - P,Q are valid */
639 if ((M
[QAR
] & REC_MARK
) != REC_MARK
) { /* not rec mark? */
640 BRANCH (PAR
); /* branch */
644 /* Branch no group mark - P,Q are valid */
647 if ((M
[QAR
] & DIGIT
) != GRP_MARK
) { /* not grp mark? */
648 BRANCH (PAR
); /* branch */
652 /* Branch (no) indicator - P is valid */
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 */
662 if ((ind
[t
] != 0) ^ (op
== OP_BNI
)) { /* ind value correct? */
663 BRANCH (PAR
); /* branch */
665 if (ind_table
[t
] > 0) ind
[t
] = 0; /* reset if needed */
668 /* Add/subtract/compare - P,Q are valid */
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
;
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
;
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
;
691 /* Multiply - P,Q are valid */
695 reason
= mul_field (PAR
, QAR
); /* multiply */
698 /* IO instructions - P is valid */
702 if ((PAR
& 1) == 0) { /* P even? */
703 reason
= STOP_INVEAD
; /* stop */
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 */
718 /* Divide special feature instructions */
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 */
729 /* Divide - P,Q are valid */
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 */
739 /* Edit special feature instructions */
741 /* Move flag - P,Q are valid */
744 M
[PAR
] = (M
[PAR
] & ~FLAG
) | (M
[QAR
] & FLAG
); /* copy Q flag */
745 M
[QAR
] = M
[QAR
] & ~FLAG
; /* clr Q flag */
748 /* Transmit numeric strip - P,Q are valid, P is source */
751 if ((PAR
& 1) == 0) { /* P must be odd */
752 reason
= STOP_INVEAD
;
755 reason
= xmt_tns (QAR
, PAR
); /* xmit and strip */
758 /* Transmit numeric fill - P,Q are valid */
761 if ((PAR
& 1) == 0) { /* P must be odd */
762 reason
= STOP_INVEAD
;
765 reason
= xmt_tnf (PAR
, QAR
); /* xmit and strip */
768 /* Index special feature instructions */
770 /* Move address - P,Q are valid */
773 for (i
= 0; i
< ADDR_LEN
; i
++) { /* move 5 digits */
774 M
[PAR
] = (M
[PAR
] & FLAG
) | (M
[QAR
] & DIGIT
);
779 /* Branch load index - P,Q are valid, Q not indexed */
783 idx
= get_idx (ADDR_A (saved_PC
, I_QL
- 1)); /* get index */
784 if (idx
< 0) { /* disabled? */
785 reason
= STOP_INVIDX
; /* stop */
788 xmt_index (GET_IDXADDR (idx
), QAR
); /* copy Q to idx */
789 BRANCH (PAR
); /* branch to P */
792 /* Branch store index - P,Q are valid, Q not indexed */
795 idx
= get_idx (ADDR_A (saved_PC
, I_QL
- 1)); /* get index */
796 if (idx
< 0) { /* disabled? */
797 reason
= STOP_INVIDX
; /* stop */
800 xmt_index (QAR
, GET_IDXADDR (idx
)); /* copy idx to Q */
801 BRANCH (PAR
); /* branch to P */
804 /* Branch and modify index - P,Q are valid, Q not indexed */
807 idx
= get_idx (ADDR_A (saved_PC
, I_QL
- 1)); /* get index */
808 if (idx
< 0) { /* disabled? */
809 reason
= STOP_INVIDX
; /* stop */
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 */
818 idx
= get_idx (ADDR_A (saved_PC
, I_QL
- 1)); /* get index */
819 if (idx
< 0) { /* disabled? */
820 reason
= STOP_INVIDX
; /* stop */
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 */
828 /* Branch conditionally and modify index - P,Q are valid, Q not indexed */
831 idx
= get_idx (ADDR_A (saved_PC
, I_QL
- 1)); /* get index */
832 if (idx
< 0) { /* disabled? */
833 reason
= STOP_INVIDX
; /* stop */
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 */
844 idx
= get_idx (ADDR_A (saved_PC
, I_QL
- 1)); /* get index */
845 if (idx
< 0) { /* disabled? */
846 reason
= STOP_INVIDX
; /* stop */
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 */
856 /* Branch and select - P is valid */
859 t
= M
[ADDR_A (saved_PC
, I_SEL
)] & DIGIT
; /* get select */
860 switch (t
) { /* case on select */
862 idxe
= idxb
= 0; /* indexing off */
865 idxe
= 1; idxb
= 0; /* index band A */
868 idxe
= idxb
= 1; /* index band B */
871 iae
= 0; /* indirect off */
874 iae
= 1; /* indirect on */
877 reason
= STOP_INVSEL
; /* undefined */
883 /* Binary special feature instructions */
885 /* Branch on bit - P,Q are valid, Q is 4d address */
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 */
894 /* Branch on mask - P,Q are valid, Q is 4d address */
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 */
904 /* Or - P,Q are valid */
907 reason
= or_field (PAR
, QAR
); /* OR fields */
910 /* AND - P,Q are valid */
913 reason
= and_field (PAR
, QAR
); /* AND fields */
916 /* Exclusive or - P,Q are valid */
919 reason
= xor_field (PAR
, QAR
); /* XOR fields */
922 /* Complement - P,Q are valid */
925 reason
= com_field (PAR
, QAR
); /* COM field */
928 /* Octal to decimal - P,Q are valid */
931 reason
= oct_to_dec (PAR
, QAR
); /* convert */
934 /* Decimal to octal - P,Q are valid */
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
;
942 /* Floating point special feature instructions */
945 reason
= fp_add (PAR
, QAR
, FALSE
); /* add */
946 if (ar_stop
&& ind
[IN_EXPCHK
]) reason
= STOP_EXPCHK
;
950 reason
= fp_add (PAR
, QAR
, TRUE
); /* subtract */
951 if (ar_stop
&& ind
[IN_EXPCHK
]) reason
= STOP_EXPCHK
;
955 reason
= fp_mul (PAR
, QAR
); /* multiply */
956 if (ar_stop
&& ind
[IN_EXPCHK
]) reason
= STOP_EXPCHK
;
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
;
966 reason
= fp_fsl (PAR
, QAR
); /* shift left */
970 reason
= fp_fsr (PAR
, QAR
); /* shift right */
976 saved_PC
= PC
; /* commit inst */
977 reason
= STOP_HALT
; /* stop */
985 /* Invalid instruction code */
988 reason
= STOP_INVINS
; /* stop */
993 /* Simulation halted */
995 pcq_r
->qptr
= pcq_p
; /* update pc q ptr */
1000 /* Utility routines */
1002 /* Get 2 digit field
1005 ad = address of high digit
1007 val = field converted to binary
1011 int32
get_2d (uint32 ad
)
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 */
1021 /* Get address routine
1024 alast = address of low digit
1026 indexok = TRUE if indexing allowed
1027 &addr = pointer to address output
1029 return = error status (in terms of P address)
1030 addr = address converted to binary
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
1038 t_stat
get_addr (uint32 alast
, int32 lnt
, t_bool indexok
, uint32
*reta
)
1041 int32 cnt
, idx
, idxa
, idxv
, addr
;
1043 if (iae
) indir
= FLAG
; /* init indirect */
1046 cnt
= 0; /* count depth */
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 */
1056 addr
= addr
+ idxv
; /* add in index */
1057 if (addr
< 0) addr
= addr
+ 100000; /* -? 10's comp */
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 */
1068 /* Convert address to binary
1071 alast = address of low digit
1073 signok = TRUE if signed
1074 val = address of output
1076 status = 0 if ok, != 0 if error
1079 t_stat
cvt_addr (uint32 alast
, int32 lnt
, t_bool signok
, int32
*val
)
1081 int32 sign
= 0, addr
= 0, t
;
1083 if (signok
&& (M
[alast
] & FLAG
)) sign
= 1; /* signed? */
1084 alast
= alast
- lnt
; /* find start */
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? */
1096 /* Get index register number
1099 aidx = address of low digit
1101 index = >0 if indexed
1103 <0 if indexing disabled
1106 t_stat
get_idx (uint32 aidx
)
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 */
1118 /* Update indicators routine */
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 */
1134 /* Transmit routines */
1136 /* Transmit field from 's' to 'd' - ignore first 'skp' flags */
1138 t_stat
xmt_field (uint32 d
, uint32 s
, uint32 skp
)
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 */
1151 /* Transmit record from 's' to 'd' - copy record mark if 'cpy' = TRUE */
1153 t_stat
xmt_record (uint32 d
, uint32 s
, t_bool cpy
)
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) */
1162 if (cpy
) M
[d
] = M
[s
] & (FLAG
| DIGIT
); /* copy rec mark */
1166 /* Transmit index from 's' to 'd' - fixed five character field */
1168 t_stat
xmt_index (uint32 d
, uint32 s
)
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 */
1178 M
[d
] = (M
[s
] & DIGIT
) | FLAG
; /* set flag on last */
1182 /* Transmit dividend from 'd' to 's' - clear flag on first digit */
1184 t_stat
xmt_divd (uint32 d
, uint32 s
)
1188 M
[d
] = M
[s
] & DIGIT
; /* first w/o flag */
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 */
1197 /* Transmit numeric strip from 's' to 'd' - s is odd */
1199 t_stat
xmt_tns (uint32 d
, uint32 s
)
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 */
1210 MM (d
); /* decr mem addrs */
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) */
1216 } while (t
== 0); /* until dst flag */
1217 M
[d
] = M
[d
] | FLAG
; /* set flag at end */
1221 /* Transmit numeric fill from 's' to 'd' - d is odd */
1223 t_stat
xmt_tnf (uint32 d
, uint32 s
)
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 */
1232 MM (s
); /* decr mem addr */
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) */
1239 } while ((t
& FLAG
) == 0); /* until src flag */
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
1253 sta = ADD_NOCRY: no carry out, no sign change
1254 ADD_SCHNG: sign change
1255 ADD_CARRY: carry out
1257 Reference Manual: "When the sum is zero, the sign of the P field
1261 t_stat
add_field (uint32 d
, uint32 s
, t_bool sub
, t_bool sto
, uint32 skp
, int32
*sta
)
1263 uint32 cry
, src
, dst
, res
, comp
, dp
, dsv
;
1264 uint32 src_f
= 0, cnt
= 0, dst_f
;
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 */
1273 dst
= M
[d
] & DIGIT
; /* 1st digits */
1275 if (BAD_DIGIT (dst
) || BAD_DIGIT (src
)) /* bad digit? */
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 */
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 */
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 */
1290 if (BAD_DIGIT (dst
) || BAD_DIGIT (src
)) /* bad digit? */
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 */
1308 M
[dsv
] = M
[dsv
] ^ FLAG
; /* compl sign */
1310 *sta
= ADD_SIGNC
; /* sign changed */
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 */
1318 /* Add one digit via table (Model 1) or "hardware" (Model 2) */
1320 uint32
add_one_digit (uint32 dst
, uint32 src
, uint32
*cry
)
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 */
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 */
1341 mpc = multiplicand address
1342 mpy = multiplier address
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."
1350 t_stat
mul_field (uint32 mpc
, uint32 mpy
)
1353 uint32 pro
; /* prod pointer */
1354 uint32 mpyd
, mpyf
; /* mpy digit, flag */
1355 uint32 cnt
= 0; /* counter */
1356 uint8 sign
; /* final sign */
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 */
1365 pro
= PROD_AREA
+ PROD_AREA_LEN
- 1; /* product ptr */
1367 /* Loop on multiplier (mpy) and product (pro) digits */
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 */
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 */
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)
1392 prod += multiplicand * multiplier_digit
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
1402 EZ indicator is cleared if a non-zero digit is ever generated
1405 t_stat
mul_one_digit (uint32 mpyd
, uint32 mpcp
, uint32 prop
, uint32 last
)
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 */
1415 mptb
= MUL_TABLE
+ ((mpyd
<= 4)? (mpyd
* 2): /* set mpy table 100's, */
1416 (((mpyd
- 5) * 2) + 100)); /* 1's digits */
1418 /* Inner loop on multiplicand (mpcp) and product (prop) digits */
1420 mpcc
= 0; /* multiplicand ctr */
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
;
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 */
1454 /* Divide routine - comments from Geoff Kuenning's 1620 simulator
1456 The destination of the divide is given by:
1458 100 - <# digits in quotient>
1460 Which is more easily calculated as:
1462 100 - <# digits in divisor> - <# digits in dividend>
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
1469 Note that this all implies that "dest" points to the *leftmost*
1470 digit of the dividend.
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
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.
1482 How to divide on a 1620:
1484 The dividend is the field at 99:
1488 The divisor is somewhere else in memory:
1492 The divide operation specifies the left-most digit of the
1493 dividend as the place to begin trial subtractions:
1497 The loop works as follows:
1499 1. Call the left-most digit of the dividend "current_dividend".
1500 Call the location current_dividend - <divisor_length>
1502 2. Clear the flag at current_dividend, and set one at
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:
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:
1517 88 = _001234567890, q_d = 88, c_d = 90
1518 5. Store the (net) number of subtractions in quotient_digit:
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
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:
1534 10 / 3 = 3 remainder 1
1535 10 / -3 = -3 remainder 1
1536 -10 / 3 = -3 remainder -1
1537 -10 / -3 = 3 remainder -1
1539 This preserves the relationship dd = q * dv + r.
1541 Our example continues as follows for steps 3 through 7:
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
1580 In the actual code below, we elide several of these steps in
1581 various ways for convenience and efficiency.
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.
1588 t_stat
div_field (uint32 dvd
, uint32 dvr
, int32
*ez
)
1590 uint32 quop
, quod
, quos
; /* quo ptr, dig, sign */
1591 uint32 dvds
; /* dvd sign */
1592 t_bool first
= TRUE
; /* first pass */
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 */
1600 /* Loop on current dividend, high order digit at dvd */
1603 r
= div_one_digit (dvd
, dvr
, 10, &quod
, &quop
); /* dev quo digit */
1604 if (r
!= SCPE_OK
) return r
; /* error? */
1606 /* Store quotient digit and advance current dividend pointer */
1608 if (first
) { /* first pass? */
1609 if (quod
>= 10) { /* overflow? */
1610 ind
[IN_OVF
] = 1; /* set indicator */
1611 return STOP_OVERFL
; /* stop */
1613 M
[quop
] = FLAG
| quod
; /* set flag on quo */
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 */
1621 /* Division done. Set signs of quo, rem, set flag on high order remainder */
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 */
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)
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.
1647 t_stat
div_one_digit (uint32 dvd
, uint32 dvr
, uint32 max
,
1648 uint32
*quod
, uint32
*quop
)
1650 uint32 dvrp
, dvrd
, dvrf
; /* dvr ptr, dig, flag */
1651 uint32 dvdp
, dvdd
; /* dvd ptr, dig */
1652 uint32 qd
, cry
; /* quo dig, carry */
1655 for (qd
= 0; qd
< max
; qd
++) { /* devel quo dig */
1656 dvrp
= dvr
; /* divisor ptr */
1657 dvdp
= dvd
; /* dividend ptr */
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 */
1675 if (!cry
) break; /* !cry = negative */
1678 /* Add back the divisor to correct for the negative result */
1680 dvrp
= dvr
; /* divisor ptr */
1681 dvdp
= dvd
; /* dividend ptr */
1683 cry
= 0; /* carry in = 0 */
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 */
1695 if (quop
!= NULL
) *quop
= dvdp
; /* set quo addr */
1696 *quod
= qd
; /* set quo digit */
1700 /* Logical operation routines (and, or, xor, complement)
1703 d = destination address
1708 Destination flags are preserved; EZ reflects the result.
1709 COM does not obey normal field length restrictions.
1712 t_stat
or_field (uint32 d
, uint32 s
)
1717 ind
[IN_EZ
] = 1; /* assume result zero */
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 */
1728 t_stat
and_field (uint32 d
, uint32 s
)
1733 ind
[IN_EZ
] = 1; /* assume result zero */
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 */
1744 t_stat
xor_field (uint32 d
, uint32 s
)
1749 ind
[IN_EZ
] = 1; /* assume result zero */
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 */
1760 t_stat
com_field (uint32 d
, uint32 s
)
1765 ind
[IN_EZ
] = 1; /* assume result zero */
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 */
1779 tbl = conversion table address (low digit)
1782 product area = converted source
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.
1791 t_stat
oct_to_dec (uint32 tbl
, uint32 s
)
1793 uint32 cnt
= 0, tblc
;
1794 uint32 i
, sd
, sf
, tf
, sign
;
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);
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 */
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) */
1817 if (ind
[IN_EZ
]) ind
[IN_HP
] = 0; /* res = 0? clr HP */
1818 M
[PROD_AREA
+ PROD_AREA_LEN
- 1] |= sign
; /* set sign */
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
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.
1838 t_stat
dec_to_oct (uint32 d
, uint32 tbl
, int32
*ez
)
1840 uint32 sign
, octd
, t
;
1841 t_bool first
= TRUE
;
1845 sign
= M
[PROD_AREA
+ PROD_AREA_LEN
- 1] & FLAG
; /* input sign */
1846 *ez
= 1; /* set indicators */
1847 ind
[IN_HP
] = (sign
== 0);
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 */
1857 M
[d
] = FLAG
| octd
; /* set flag on quo */
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 */
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) */
1874 if (*ez
) ind
[IN_HP
] = 0; /* res = 0? clr HP */
1875 M
[d
] = M
[d
] | sign
; /* set result sign */
1881 t_stat
cpu_reset (DEVICE
*dptr
)
1884 static t_bool one_time
= TRUE
;
1886 PR1
= IR2
= 1; /* invalidate PR1,IR2 */
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? */
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 */
1902 /* Memory examine */
1904 t_stat
cpu_ex (t_value
*vptr
, t_addr addr
, UNIT
*uptr
, int32 sw
)
1906 if (addr
>= MEMSIZE
) return SCPE_NXM
;
1907 if (vptr
!= NULL
) *vptr
= M
[addr
] & (FLAG
| DIGIT
);
1911 /* Memory deposit */
1913 t_stat
cpu_dep (t_value val
, t_addr addr
, UNIT
*uptr
, int32 sw
)
1915 if (addr
>= MEMSIZE
) return SCPE_NXM
;
1916 M
[addr
] = val
& (FLAG
| DIGIT
);
1920 /* Memory size change */
1922 t_stat
cpu_set_size (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
1927 if ((val
<= 0) || (val
> MAXMEMSIZE
) || ((val
% 1000) != 0))
1929 for (i
= val
; i
< MEMSIZE
; i
++) mc
= mc
| M
[i
];
1930 if ((mc
!= 0) && (!get_yn ("Really truncate memory [N]?", FALSE
)))
1933 for (i
= MEMSIZE
; i
< MAXMEMSIZE
; i
++) M
[i
] = 0;
1939 t_stat
cpu_set_model (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
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
);
1947 /* Set/clear Model 1 option */
1949 t_stat
cpu_set_opt1 (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
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");
1959 /* Set/clear Model 2 option */
1961 t_stat
cpu_set_opt2 (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
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");
1971 /* Front panel save */
1973 t_stat
cpu_set_save (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
1975 if (saved_PC
& 1) return SCPE_NOFNC
;
1980 /* Set standard add/multiply tables */
1982 t_stat
cpu_set_table (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
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
];
1997 t_stat
cpu_set_hist (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
2003 for (i
= 0; i
< hst_lnt
; i
++) hst
[i
].vld
= 0;
2007 lnt
= (int32
) get_uint (cptr
, 10, HIST_MAX
, &r
);
2008 if ((r
!= SCPE_OK
) || (lnt
&& (lnt
< HIST_MIN
))) return SCPE_ARG
;
2016 hst
= (InstHistory
*) calloc (lnt
, sizeof (InstHistory
));
2017 if (hst
== NULL
) return SCPE_MEM
;
2025 t_stat
cpu_show_hist (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
2027 int32 i
, k
, di
, lnt
;
2028 char *cptr
= (char *) desc
;
2029 t_value sim_eval
[INST_LEN
];
2032 extern t_stat
fprint_sym (FILE *ofile
, t_addr addr
, t_value
*val
,
2033 UNIT
*uptr
, int32 sw
);
2035 if (hst_lnt
== 0) return SCPE_NOFNC
; /* enabled? */
2037 lnt
= (int32
) get_uint (cptr
, 10, hst_lnt
, &r
);
2038 if ((r
!= SCPE_OK
) || (lnt
== 0)) return SCPE_ARG
;
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
]);
2055 fputc ('\n', st
); /* end line */
2056 } /* end else instruction */