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