1
/* i1401_cpu.c: IBM 1401 CPU simulator
3
Copyright (c) 1996-1997,
4
Robert M Supnik, Digital Equipment Corporation
5
Commercial use prohibited
7
The register state for the IBM 1401 is:
9
IS I storage address register (PC)
10
AS A storage address register (address of first operand)
11
BS B storage address register (address of second operand)
17
The IBM 1401 is a variable instruction length, decimal data system.
18
Memory consists of 4000, 8000, 12000, or 16000 BCD characters, each
19
containing six bits of data and a word mark. There are no general
20
registers; all instructions are memory to memory, using explicit
21
addresses or an address pointer from a prior instruction.
23
BCD numeric data consists of the low four bits of a character (DIGIT),
24
encoded as X, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, X, X, X, X, X. The high
25
two bits (ZONE) encode the sign of the data as +, +, -, +. Character
26
data uses all six bits of a character. Numeric and character fields are
27
delimited by a word mark. Fields are typically processed in descending
28
address order (low-order data to high-order data).
30
The 1401 encodes a decimal address, and an index register number, in
34
addr + 0 <1:0> of thousands hundreds
35
addr + 1 index register # tens
36
addr + 2 <3:2> of thousands ones
38
Normally the digit values 0, 11, 12, 13, 14, 15 are illegal in addresses.
39
However, in indexing, digits are passed through the adder, and illegal
40
values are normalized to legal counterparts.
42
The 1401 has six instruction formats:
44
op A and B addresses, if any, from AS and BS
45
op d A and B addresses, if any, from AS and BS
46
op aaa B address, if any, from BS
47
op aaa d B address, if any, from BS
51
where aaa is the A address, bbb is the B address, and d is a modifier.
52
The opcode has word mark set; all other characters have word mark clear.
55
/* This routine is the instruction decode routine for the IBM 1401.
56
It is called from the simulator control program to execute
57
instructions in simulated memory, starting at the simulated PC.
58
It runs until 'reason' is set non-zero.
62
1. Reasons to stop. The simulator can be stopped by:
65
breakpoint encountered
66
illegal addresses or instruction formats
67
I/O error in I/O simulator
69
2. Interrupts. The 1401 has no interrupt structure.
71
3. Non-existent memory. On the 1401, references to non-existent
72
memory halt the processor.
74
4. Adding I/O devices. These modules must be modified:
76
i1401_cpu.c add IO dispatches to iodisp
77
i1401_sys.c add pointer to data structures to sim_devices
80
#include "i1401_defs.h"
82
#define ILL_ADR_FLAG 100000 /* invalid addr flag */
83
#define save_ibkpt (cpu_unit.u3) /* saved bkpt addr */
84
#define ADDR_ERR(x) ((x) >= MEMSIZE) /* BA set || too big */
85
#define MM(x) x = x - 1; \
87
x = BA + MAXMEMSIZE - 1; \
90
#define PP(x) x = x + 1; \
92
x = BA + (x % MAXMEMSIZE); \
95
#define BRANCH if (ADDR_ERR (AS)) { \
96
reason = STOP_INVBR; \
98
if (cpu_unit.flags & XSA) BS = IS; \
103
unsigned char M[MAXMEMSIZE] = { 0 }; /* main memory */
104
int32 saved_IS = 0; /* saved IS */
105
int32 AS = 0; /* AS */
106
int32 BS = 0; /* BS */
107
int32 as_err = 0, bs_err = 0; /* error flags */
108
int32 oldIS = 0; /* previous IS */
109
int32 ind[64] = { 0 }; /* indicators */
110
int32 ssa = 1; /* sense switch A */
111
int32 prchk = 0; /* process check stop */
112
int32 iochk = 0; /* I/O check stop */
113
int32 ibkpt_addr = ILL_ADR_FLAG + MAXMEMSIZE; /* breakpoint addr */
114
extern int32 sim_int_char;
116
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
117
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
118
t_stat cpu_reset (DEVICE *dptr);
119
t_stat cpu_svc (UNIT *uptr);
120
t_stat cpu_set_size (UNIT *uptr, int32 value);
121
int32 store_addr_h (int32 addr);
122
int32 store_addr_t (int32 addr);
123
int32 store_addr_u (int32 addr);
124
t_stat iomod (int32 ilnt, int32 mod, const int32 *tptr);
125
t_stat iodisp (int32 dev, int32 unit, int32 flag, int32 mod);
127
extern t_stat read_card (int32 ilnt, int32 mod);
128
extern t_stat punch_card (int32 ilnt, int32 mod);
129
extern t_stat select_stack (int32 mod);
130
extern t_stat carriage_control (int32 mod);
131
extern t_stat write_line (int32 ilnt, int32 mod);
132
extern t_stat inq_io (int32 flag, int32 mod);
133
extern t_stat mt_io (int32 unit, int32 flag, int32 mod);
134
extern t_stat mt_func (int32 unit, int32 mod);
135
extern t_stat sim_activate (UNIT *uptr, int32 delay);
137
/* CPU data structures
139
cpu_dev CPU device descriptor
140
cpu_unit CPU unit descriptor
141
cpu_reg CPU register list
142
cpu_mod CPU modifier list
145
UNIT cpu_unit = { UDATA (&cpu_svc, UNIT_FIX + UNIT_BCD + STDOPT,
149
{ DRDATA (IS, saved_IS, 14), PV_LEFT },
150
{ DRDATA (AS, AS, 14), PV_LEFT },
151
{ DRDATA (BS, BS, 14), PV_LEFT },
152
{ FLDATA (ASERR, as_err, 0) },
153
{ FLDATA (BSERR, bs_err, 0) },
154
{ FLDATA (SSA, ssa, 0) },
155
{ FLDATA (SSB, ind[IN_SSB], 0) },
156
{ FLDATA (SSC, ind[IN_SSC], 0) },
157
{ FLDATA (SSD, ind[IN_SSD], 0) },
158
{ FLDATA (SSE, ind[IN_SSE], 0) },
159
{ FLDATA (SSF, ind[IN_SSF], 0) },
160
{ FLDATA (SSG, ind[IN_SSG], 0) },
161
{ FLDATA (EQU, ind[IN_EQU], 0) },
162
{ FLDATA (UNEQ, ind[IN_UNQ], 0) },
163
{ FLDATA (HIGH, ind[IN_HGH], 0) },
164
{ FLDATA (LOW, ind[IN_LOW], 0) },
165
{ FLDATA (OVF, ind[IN_OVF], 0) },
166
{ FLDATA (IOCHK, iochk, 0) },
167
{ FLDATA (PRCHK, prchk, 0) },
168
{ DRDATA (OLDIS, oldIS, 14), REG_RO + PV_LEFT },
169
{ DRDATA (BREAK, ibkpt_addr, 17), PV_LEFT },
170
{ ORDATA (WRU, sim_int_char, 8) },
174
{ XSA, XSA, "XSA", "XSA", NULL },
175
{ XSA, 0, "no XSA", "NOXSA", NULL },
176
{ HLE, HLE, "HLE", "HLE", NULL },
177
{ HLE, 0, "no HLE", "NOHLE", NULL },
178
{ BBE, BBE, "BBE", "BBE", NULL },
179
{ BBE, 0, "no BBE", "NOBBE", NULL },
180
{ MA, MA, "MA", 0, NULL },
181
{ MA, 0, "no MA", 0, NULL },
182
{ MR, MR, "MR", "MR", NULL },
183
{ MR, 0, "no MR", "NOMR", NULL },
184
{ EPE, EPE, "EPE", "EPE", NULL },
185
{ EPE, 0, "no EPE", "NOEPE", NULL },
186
{ UNIT_MSIZE, 4000, NULL, "4K", &cpu_set_size },
187
{ UNIT_MSIZE, 8000, NULL, "8K", &cpu_set_size },
188
{ UNIT_MSIZE, 12000, NULL, "12K", &cpu_set_size },
189
{ UNIT_MSIZE, 16000, NULL, "16K", &cpu_set_size },
193
"CPU", &cpu_unit, cpu_reg, cpu_mod,
195
&cpu_ex, &cpu_dep, &cpu_reset,
198
/* Opcode table - length, dispatch, and option flags. This table is also
199
used by the symbolic input routine to validate instruction lengths */
201
const int32 op_table[64] = {
203
L1 | L2 | L4 | L5, /* read */
204
L1 | L2 | L4 | L5, /* write */
205
L1 | L2 | L4 | L5, /* write and read */
206
L1 | L2 | L4 | L5, /* punch */
207
L1 | L4, /* read and punch */
208
L1 | L2 | L4 | L5, /* write and read */
209
L1 | L2 | L4 | L5, /* write, read, punch */
210
L1, /* 10: read feed */
213
L1 | L4 | L7 | AREQ | BREQ | MA, /* modify address */
214
L7 | AREQ | BREQ | MDV, /* multiply */
219
L1 | L4 | L7 | BREQ | NOWM, /* clear storage */
220
L1 | L4 | L7 | AREQ | BREQ, /* subtract */
222
L5 | IO, /* magtape */
223
L1 | L8 | BREQ, /* branch wm or zone */
224
L1 | L8 | BREQ | BBE, /* branch if bit eq */
226
L1 | L4 | L7 | AREQ | BREQ, /* 30: move zones */
227
L7 | AREQ | BREQ, /* move supress zero */
229
L1 | L4 | L7 | AREQ | BREQ | NOWM, /* set word mark */
230
L7 | AREQ | BREQ | MDV, /* divide */
236
L2 | L5, /* select stacker */
237
L1 | L4 | L7 | L8 | BREQ | MLS | IO, /* load */
238
L1 | L4 | L7 | L8 | BREQ | MLS | IO, /* move */
241
L1 | L4 | L7 | AREQ | BREQ | MR, /* move to record */
242
L1 | L4 | AREQ | MLS, /* 50: store A addr */
244
L1 | L4 | L7 | AREQ | BREQ, /* zero and subtract */
251
L1 | L4 | L7 | AREQ | BREQ, /* add */
252
L1 | L4 | L5 | L8, /* branch */
253
L1 | L4 | L7 | AREQ | BREQ, /* compare */
254
L1 | L4 | L7 | AREQ | BREQ, /* move numeric */
255
L1 | L4 | L7 | AREQ | BREQ, /* move char edit */
256
L2 | L5, /* carriage control */
258
L1 | L4 | L7 | AREQ | MLS, /* 70: store B addr */
260
L1 | L4 | L7 | AREQ | BREQ, /* zero and add */
261
HNOP | L1 | L4, /* halt */
262
L1 | L4 | L7 | AREQ | BREQ, /* clear word mark */
267
const int32 len_table[9] = { 0, L1, L2, 0, L4, L5, 0, L7, L8 };
269
/* Address character conversion tables. Illegal characters are marked by
270
the flag BA but also contain the post-adder value for indexing */
272
const int32 hun_table[64] = {
273
BA+000, 100, 200, 300, 400, 500, 600, 700,
274
800, 900, 000, BA+300, BA+400, BA+500, BA+600, BA+700,
275
BA+1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700,
276
1800, 1900, 1000, BA+1300, BA+1400, BA+1500, BA+1600, BA+1700,
277
BA+2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700,
278
2800, 2900, 2000, BA+2300, BA+2400, BA+2500, BA+2600, BA+2700,
279
BA+3000, 3100, 3200, 3300, 3400, 3500, 3600, 3700,
280
3800, 3900, 3000, BA+3300, BA+3400, BA+3500, BA+3600, BA+3700 };
282
const int32 ten_table[64] = {
283
BA+00, 10, 20, 30, 40, 50, 60, 70,
284
80, 90, 00, BA+30, BA+40, BA+50, BA+60, BA+70,
285
X1+00, X1+10, X1+20, X1+30, X1+40, X1+50, X1+60, X1+70,
286
X1+80, X1+90, X1+00, X1+30, X1+40, X1+50, X1+60, X1+70,
287
X2+00, X2+10, X2+20, X2+30, X2+40, X2+50, X2+60, X2+70,
288
X2+80, X2+90, X2+00, X2+30, X2+40, X2+50, X2+60, X2+70,
289
X3+00, X3+10, X3+20, X3+30, X3+40, X3+50, X3+60, X3+70,
290
X3+80, X3+90, X3+00, X3+30, X3+40, X3+50, X3+60, X3+70 };
292
const int32 one_table[64] = {
293
BA+0, 1, 2, 3, 4, 5, 6, 7,
294
8, 9, 0, BA+3, BA+4, BA+5, BA+6, BA+7,
295
BA+4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007,
296
4008, 4009, 4000, BA+4003, BA+4004, BA+4005, BA+4006, BA+4007,
297
BA+8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007,
298
8008, 8009, 8000, BA+8003, BA+8004, BA+8005, BA+8006, BA+8007,
299
BA+12000, 12001, 12002, 12003, 12004, 12005, 12006, 12007,
300
12008, 12009, 12000, BA+12003, BA+12004, BA+12005, BA+12006, BA+12007 };
302
static const int32 bin_to_bcd[16] = {
303
10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
305
static const int32 bcd_to_bin[16] = {
306
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 3, 4, 5, 6, 7 };
308
/* ASCII to BCD conversion */
310
const char ascii_to_bcd[128] = {
311
000, 000, 000, 000, 000, 000, 000, 000, /* 000 - 037 */
312
000, 000, 000, 000, 000, 000, 000, 000,
313
000, 000, 000, 000, 000, 000, 000, 000,
314
000, 000, 000, 000, 000, 000, 000, 000,
315
000, 052, 077, 013, 053, 034, 060, 032, /* 040 - 077 */
316
017, 074, 054, 037, 033, 040, 073, 021,
317
012, 001, 002, 003, 004, 005, 006, 007,
318
010, 011, 015, 056, 076, 035, 016, 072,
319
014, 061, 062, 063, 064, 065, 066, 067, /* 100 - 137 */
320
070, 071, 041, 042, 043, 044, 045, 046,
321
047, 050, 051, 022, 023, 024, 025, 026,
322
027, 030, 031, 075, 036, 055, 020, 057,
323
000, 061, 062, 063, 064, 065, 066, 067, /* 140 - 177 */
324
070, 071, 041, 042, 043, 044, 045, 046,
325
047, 050, 051, 022, 023, 024, 025, 026,
326
027, 030, 031, 000, 000, 000, 000, 000 };
328
/* BCD to ASCII conversion - also the "full" print chain */
330
char bcd_to_ascii[64] = {
331
' ', '1', '2', '3', '4', '5', '6', '7',
332
'8', '9', '0', '#', '@', ':', '>', '(',
333
'^', '/', 'S', 'T', 'U', 'V', 'W', 'X',
334
'Y', 'Z', '\'', ',', '%', '=', '\\', '+',
335
'-', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
336
'Q', 'R', '!', '$', '*', ']', ';', '_',
337
'&', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
338
'H', 'I', '?', '.', ')', '[', '<', '"' };
340
t_stat sim_instr (void)
342
extern int32 sim_interval;
343
register int32 IS, D, ilnt, flags;
344
int32 op, xa, t, wm, dev, unit;
345
int32 a, b, i, bsave, carry;
346
int32 qzero, qawm, qbody, qsign, qdollar, qaster, qdecimal;
347
t_stat reason, r1, r2;
349
/* Indicator resets - a 1 marks an indicator that resets when tested */
351
static const int32 ind_table[64] = {
352
0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 07 */
353
0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 17 */
354
0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */
355
0, 1, 1, 0, 1, 0, 0, 0, /* 30 - 37 */
356
0, 0, 1, 0, 0, 0, 0, 0, /* 40 - 47 */
357
0, 0, 1, 0, 1, 0, 0, 0, /* 50 - 57 */
358
0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 67 */
359
0, 0, 1, 0, 0, 0, 0, 0 }; /* 70 - 77 */
361
/* Character collation table for compare with HLE option */
363
static const int32 col_table[64] = {
364
000, 067, 070, 071, 072, 073, 074, 075,
365
076, 077, 066, 024, 025, 026, 027, 030,
366
023, 015, 056, 057, 060, 061, 062, 063,
367
064, 065, 055, 016, 017, 020, 021, 022,
368
014, 044, 045, 046, 047, 050, 051, 052,
369
053, 054, 043, 007, 010, 011, 012, 013,
370
006, 032, 033, 034, 035, 036, 037, 040,
371
041, 042, 031, 001, 002, 003, 004, 005 };
373
/* Summing table for two decimal digits, converted back to BCD */
375
static const int32 sum_table[20] = {
376
10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
378
/* Legal modifier tables */
380
static const int32 w_mod[] = { BCD_S, BCD_SQUARE, -1 };
381
static const int32 ss_mod[] = { 1, 2, 4, 8, -1 };
382
static const int32 mtf_mod[] = { BCD_B, BCD_E, BCD_M, BCD_R, BCD_U, -1 };
385
/* Restore saved state */
391
/* Main instruction fetch/decode loop */
393
while (reason == 0) { /* loop until halted */
394
saved_IS = IS; /* commit prev instr */
395
if (sim_interval <= 0) { /* check clock queue */
396
if (reason = sim_process_event ()) break; }
398
if (IS == ibkpt_addr) { /* breakpoint? */
399
save_ibkpt = ibkpt_addr; /* save ibkpt */
400
ibkpt_addr = ibkpt_addr + ILL_ADR_FLAG; /* disable */
401
sim_activate (&cpu_unit, 1); /* sched re-enable */
402
reason = STOP_IBKPT; /* stop simulation */
405
sim_interval = sim_interval - 1;
407
/* Instruction fetch */
409
if ((M[IS] & WM) == 0) { /* WM under op? */
410
reason = STOP_NOWM; /* no, error */
412
op = M[IS] & CHAR; /* get opcode */
413
flags = op_table[op]; /* get op flags */
414
if ((flags == 0) || (flags & ALLOPT & ~cpu_unit.flags)) {
415
reason = STOP_NXI; /* illegal inst? */
417
if (op == OP_SAR) BS = AS; /* SAR? save ASTAR */
420
if ((t = M[IS]) & WM) goto CHECK_LENGTH; /* WM? 1 char inst */
421
D = t; /* could be D char */
422
AS = hun_table[t]; /* could be A addr */
423
PP (IS); /* if %xy, BA is set */
425
if ((t = M[IS]) & WM) { /* WM? 2 char inst */
426
AS = AS | BA; /* ASTAR bad */
427
if (!(flags & MLS)) BS = AS;
429
AS = AS + ten_table[t]; /* build A addr */
430
dev = t; /* save char as dev */
433
if ((t = M[IS]) & WM) { /* WM? 3 char inst */
434
AS = AS | BA; /* ASTAR bad */
435
if (!(flags & MLS)) BS = AS;
437
AS = AS + one_table[t]; /* finish A addr */
438
unit = (t == BCD_ZERO)? 0: t; /* save char as unit */
439
xa = (AS >> V_INDEX) & M_INDEX; /* get index reg */
440
if (xa && (D != BCD_PERCNT) && (cpu_unit.flags & XSA)) { /* indexed? */
441
AS = AS + hun_table[M[xa] & CHAR] + ten_table[M[xa + 1] & CHAR] +
442
one_table[M[xa + 2] & CHAR];
443
AS = (AS & INDEXMASK) % MAXMEMSIZE; }
444
if (!(flags & MLS)) BS = AS; /* not MLS? B = A */
447
if ((t = M[IS]) & WM) goto CHECK_LENGTH; /* WM? 4 char inst */
448
if ((op == OP_B) && (t == BCD_BLANK)) goto CHECK_LENGTH; /* BR + space? */
449
D = t; /* could be D char */
450
BS = hun_table[t]; /* could be B addr */
453
if ((t = M[IS]) & WM) { /* WM? 5 char inst */
454
BS = BS | BA; /* BSTAR bad */
456
BS = BS + ten_table[t]; /* build B addr */
459
if ((t = M[IS]) & WM) { /* WM? 6 char inst */
460
BS = BS | BA; /* BSTAR bad */
462
BS = BS + one_table[t]; /* finish B addr */
463
xa = (BS >> V_INDEX) & M_INDEX; /* get index reg */
464
if (xa && (cpu_unit.flags & XSA)) { /* indexed? */
465
BS = BS + hun_table[M[xa] & CHAR] + ten_table[M[xa + 1] & CHAR]
466
+ one_table[M[xa + 2] & CHAR];
467
BS = (BS & INDEXMASK) % MAXMEMSIZE; }
470
if ((M[IS] & WM) || (flags & NOWM)) goto CHECK_LENGTH; /* WM? 7 chr */
471
D = M[IS]; /* last char is D */
472
do { PP (IS); } while ((M[IS] & WM) == 0); /* find word mark */
475
ilnt = IS - saved_IS; /* get lnt */
476
if (((flags & len_table [(ilnt <= 8)? ilnt: 8]) == 0) && /* valid lnt? */
477
((flags & HNOP) == 0)) reason = STOP_INVL;
478
if ((flags & BREQ) && ADDR_ERR (BS)) reason = STOP_INVB; /* valid A? */
479
if ((flags & AREQ) && ADDR_ERR (AS)) reason = STOP_INVA; /* valid B? */
480
if (reason) break; /* error in fetch? */
481
switch (op) { /* case on opcode */
483
/* Move instructions A check B check
485
MCW: copy A to B, preserving B WM, here fetch
486
until either A or B WM
487
LCA: copy A to B, overwriting B WM, here fetch
489
MCM: copy A to B, preserving B WM, fetch fetch
490
until record or group mark
491
MSZ: copy A to B, clearing B WM, until A WM; fetch fetch
492
reverse scan and suppress leading zeroes
493
MN: copy A char digit to B char digit, fetch fetch
494
preserving B zone and WM
495
MZ: copy A char zone to B char zone, fetch fetch
496
preserving B digit and WM
499
case OP_MCW: /* move char */
500
if (ilnt >= 8) { /* I/O form? */
501
reason = iodisp (dev, unit, MD_NORM, D);
503
if (ADDR_ERR (AS)) { /* check A addr */
506
do { M[BS] = (M[BS] & WM) | (M[AS] & CHAR); /* move char */
508
MM (AS); MM (BS); } /* decr pointers */
509
while ((wm & WM) == 0); /* stop on A,B WM */
511
case OP_LCA: /* load char */
512
if (ilnt >= 8) { /* I/O form? */
513
reason = iodisp (dev, unit, MD_WM, D);
515
if (ADDR_ERR (AS)) { /* check A addr */
518
do { wm = M[BS] = M[AS]; /* move char + wmark */
519
MM (AS); MM (BS); } /* decr pointers */
520
while ((wm & WM) == 0); /* stop on A WM */
522
case OP_MCM: /* move to rec/group */
523
do { M[BS] = (M[BS] & WM) | (M[AS] & CHAR); /* move char */
525
PP (AS); PP (BS); } /* incr pointers */
526
while (((t & CHAR) != BCD_RECMRK) && (t != (BCD_GRPMRK + WM)));
528
case OP_MSZ: /* move suppress zero */
529
bsave = BS; /* save B start */
530
qzero = 1; /* set suppress */
531
do { M[BS] = M[AS] & ((BS != bsave)? CHAR: DIGIT); /* copy char */
533
MM (AS); MM (BS); } /* decr pointers */
534
while ((wm & WM) == 0); /* stop on A WM */
535
do { PP (BS); /* adv B */
536
t = M[BS]; /* get B, cant be WM */
537
if ((t == BCD_ZERO) || (t == BCD_COMMA)) {
538
if (qzero) M[BS] = 0; }
539
else if ((t == BCD_BLANK) || (t == BCD_MINUS)) ;
540
else if (((t == BCD_DECIMAL) && (cpu_unit.flags & EPE)) ||
541
(t <= BCD_NINE)) qzero = 0;
545
case OP_MN: /* move numeric */
546
M[BS] = (M[BS] & ~DIGIT) | (M[AS] & DIGIT); /* move digit */
547
MM (AS); MM (BS); /* decr pointers */
549
case OP_MZ: /* move zone */
550
M[BS] = (M[BS] & ~ZONE) | (M[AS] & ZONE); /* move high bits */
551
MM (AS); MM (BS); /* decr pointers */
556
A and B are checked in fetch
559
case OP_C: /* compare */
560
if (ilnt != 1) { /* if not chained */
561
ind[IN_EQU] = 1; /* clear indicators */
562
ind[IN_UNQ] = ind[IN_HGH] = ind[IN_LOW] = 0; }
563
do { a = M[AS]; /* get characters */
565
wm = a | b; /* get word marks */
566
if ((a & CHAR) != (b & CHAR)) { /* unequal? */
567
ind[IN_EQU] = 0; /* set indicators */
569
ind[IN_HGH] = col_table[b & CHAR] > col_table [a & CHAR];
570
ind[IN_LOW] = ind[IN_HGH] ^ 1; }
571
MM (AS); MM (BS); } /* decr pointers */
572
while ((wm & WM) == 0); /* stop on A, B WM */
573
if ((a & WM) && !(b & WM)) { /* short A field? */
574
ind[IN_EQU] = ind[IN_LOW] = 0;
575
ind[IN_UNQ] = ind[IN_HGH] = 1; }
576
if (!(cpu_unit.flags & HLE)) /* no HLE? */
577
ind[IN_EQU] = ind[IN_LOW] = ind[IN_HGH] = 0;
580
/* Branch instructions A check B check
582
B 8 char: branch if B char equals d if branch here
583
B 5 char: branch if indicator[d] is set if branch
584
B 4 char: unconditional branch if branch
585
BWZ: branch if (d<0>: B char WM) if branch here
586
(d<1>: B char zone = d zone)
587
BBE: branch if B char & d non-zero if branch here
590
case OP_B: /* branch */
591
if (ilnt <= 4) { BRANCH; } /* uncond branch? */
592
else if (ilnt == 5) { /* branch on ind? */
593
if (ind[D]) { BRANCH; } /* test indicator */
594
if (ind_table[D]) ind[D] = 0; } /* reset if needed */
595
else { if (ADDR_ERR (BS)) { /* branch char eq */
596
reason = STOP_INVB; /* validate B addr */
598
if ((M[BS] & CHAR) == D) { BRANCH; } /* char equal? */
601
case OP_BWZ: /* branch wm or zone */
602
if (((D & 1) && (M[BS] & WM)) || /* d1? test wm */
603
((D & 2) && ((M[BS] & ZONE) == (D & ZONE)))) /* d2? test zone */
605
else { MM (BS); } /* decr pointer */
607
case OP_BBE: /* branch if bit eq */
608
if (M[BS] & D & CHAR) { BRANCH; } /* any bits set? */
609
else { MM (BS); } /* decr pointer */
612
/* Arithmetic instructions A check B check
614
ZA: move A to B, normalizing A sign, fetch fetch
615
preserving B WM, until B WM
616
ZS: move A to B, complementing A sign, fetch fetch
617
preserving B WM, until B WM
618
A: add A to B fetch fetch
619
S: subtract A from B fetch fetch
622
case OP_ZA: case OP_ZS: /* zero and add/sub */
623
a = i = 0; /* clear flags */
624
do { if (a & WM) wm = M[BS] = (M[BS] & WM) | BCD_ZERO;
625
else { a = M[AS]; /* get A char */
626
t = (a & CHAR)? bin_to_bcd[a & DIGIT]: 0;
627
wm = M[BS] = (M[BS] & WM) | t; /* move digit */
629
if (i == 0) i = M[BS] = M[BS] |
630
((((a & ZONE) == BBIT) ^ (op == OP_ZS))? BBIT: ZONE);
632
while ((wm & WM) == 0); /* stop on B WM */
634
case OP_A: case OP_S: /* add/sub */
635
bsave = BS; /* save sign pos */
636
a = M[AS]; /* get A digit/sign */
637
b = M[BS]; /* get B digit/sign */
639
qsign = ((a & ZONE) == BBIT) ^ ((b & ZONE) == BBIT) ^ (op == OP_S);
640
t = bcd_to_bin[a & DIGIT]; /* get A binary */
641
t = bcd_to_bin[b & DIGIT] + (qsign? 10 - t: t); /* sum A + B */
642
carry = (t >= 10); /* get carry */
643
b = (b & ~DIGIT) | sum_table[t]; /* get result */
644
if (qsign && ((b & BBIT) == 0)) b = b | ZONE; /* normalize sign */
645
M[BS] = b; /* store result */
647
if (b & WM) { /* b wm? done */
648
if (qsign && (carry == 0)) M[bsave] = /* compl, no carry? */
649
WM + ((b & ZONE) ^ ABIT) + sum_table[10 - t];
651
do { if (a & WM) a = WM; /* A WM? char = 0 */
652
else { a = M[AS]; /* else get A */
654
b = M[BS]; /* get B */
655
t = bcd_to_bin[a & DIGIT]; /* get A binary */
656
t = bcd_to_bin[b & DIGIT] + (qsign? 9 - t: t) + carry;
657
carry = (t >= 10); /* get carry */
658
if ((b & WM) && (qsign == 0)) { /* last, no recomp? */
659
M[BS] = WM + sum_table[t] + /* zone add */
660
(((a & ZONE) + b + (carry? ABIT: 0)) & ZONE);
661
ind[IN_OVF] = carry; } /* ovflo if carry */
662
else M[BS] = (b & WM) + sum_table[t]; /* normal add */
664
while ((b & WM) == 0); /* stop on B WM */
665
if (qsign && (carry == 0)) { /* recompl, no carry? */
666
M[bsave] = M[bsave] ^ ABIT; /* XOR sign */
667
for (carry = 1; bsave != BS; --bsave) { /* rescan */
668
t = 9 - bcd_to_bin[M[bsave] & DIGIT] + carry;
670
M[bsave] = (M[bsave] & ~DIGIT) | sum_table[t]; } }
673
/* I/O instructions A check B check
675
R: read a card if branch
676
W: write to line printer if branch
677
WR: write and read if branch
678
P: punch a card if branch
679
RP: read and punch if branch
680
WP: write and punch if branch
681
WRP: write read and punch if branch
684
SS: select stacker if branch
685
CC: carriage control if branch
686
MTF: magtape functions
689
case OP_R: /* read */
690
if (reason = iomod (ilnt, D, NULL)) break; /* valid modifier? */
691
reason = read_card (ilnt, D); /* read card */
692
BS = CDR_BUF + CDR_WIDTH;
693
if (ilnt >= 4) { BRANCH; } /* check for branch */
695
case OP_W: /* write */
696
if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */
697
reason = write_line (ilnt, D); /* print line */
698
BS = LPT_BUF + LPT_WIDTH;
699
if (ilnt >= 4) { BRANCH; } /* check for branch */
701
case OP_P: /* punch */
702
if (reason = iomod (ilnt, D, NULL)) break; /* valid modifier? */
703
reason = punch_card (ilnt, D); /* punch card */
704
BS = CDP_BUF + CDP_WIDTH;
705
if (ilnt >= 4) { BRANCH; } /* check for branch */
707
case OP_WR: /* write and read */
708
if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */
709
reason = write_line (ilnt, D); /* print line */
710
r1 = read_card (ilnt, D); /* read card */
711
BS = CDR_BUF + CDR_WIDTH;
712
if (ilnt >= 4) { BRANCH; } /* check for branch */
713
if (reason == SCPE_OK) reason = r1; /* merge errors */
715
case OP_WP: /* write and punch */
716
if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */
717
reason = write_line (ilnt, D); /* print line */
718
r1 = punch_card (ilnt, D); /* punch card */
719
BS = CDP_BUF + CDP_WIDTH;
720
if (ilnt >= 4) { BRANCH; } /* check for branch */
721
if (reason == SCPE_OK) reason = r1; /* merge errors */
723
case OP_RP: /* read and punch */
724
if (reason = iomod (ilnt, D, NULL)) break; /* valid modifier? */
725
reason = read_card (ilnt, D); /* read card */
726
r1 = punch_card (ilnt, D); /* punch card */
727
BS = CDP_BUF + CDP_WIDTH;
728
if (ilnt >= 4) { BRANCH; } /* check for branch */
729
if (reason == SCPE_OK) reason = r1; /* merge errors */
731
case OP_WRP: /* write, read, punch */
732
if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */
733
reason = write_line (ilnt, D); /* print line */
734
r1 = read_card (ilnt, D); /* read card */
735
r2 = punch_card (ilnt, D); /* punch card */
736
BS = CDP_BUF + CDP_WIDTH;
737
if (ilnt >= 4) { BRANCH; } /* check for branch */
738
if (reason == SCPE_OK) reason = (r1 == SCPE_OK)? r2: r1;
740
case OP_SS: /* select stacker */
741
if (reason = iomod (ilnt, D, ss_mod)) break; /* valid modifier? */
742
if (reason = select_stack (D)) break; /* sel stack, error? */
743
if (ilnt >= 4) { BRANCH; } /* check for branch */
745
case OP_CC: /* carriage control */
746
if (reason = carriage_control (D)) break; /* car ctrl, error? */
747
if (ilnt >= 4) { BRANCH; } /* check for branch */
749
case OP_MTF: /* magtape function */
750
if (reason = iomod (ilnt, D, mtf_mod)) break; /* valid modifier? */
751
if (reason = mt_func (unit, D)) break; /* mt func, error? */
752
break; /* can't branch */
753
case OP_RF: case OP_PF: /* read, punch feed */
756
/* Move character and edit
759
qsign sign of A field (0 = +, 1 = minus)
760
qawm A field WM seen and processed
761
qzero zero suppression enabled
762
qbody in body (copying A field characters)
763
qdollar EPE only; $ seen in body
764
qaster EPE only; * seen in body
765
qdecimal EPE only; . seen on first rescan
768
case OP_MCE: /* edit */
769
a = M[AS]; /* get A char */
770
b = M[BS]; /* get B char */
771
if (a & WM) { /* one char A field? */
774
if (b & WM) { /* one char B field? */
777
t = a & DIGIT; MM (AS); /* get A digit */
778
qsign = ((a & ZONE) == BBIT); /* get A field sign */
779
qawm = qzero = qbody = 0; /* clear other flags */
780
qdollar = qaster = qdecimal = 0; /* clear EPE flags */
782
/* Edit pass 1 - from right to left, under B field control */
784
do { b = M[BS]; /* get B char */
785
M[BS] = M[BS] & ~WM; /* clr WM */
786
switch (b & CHAR) { /* case on B char */
787
case BCD_ASTER: /* * */
788
if (!qbody || qdollar || !(cpu_unit.flags & EPE)) break;
789
qaster = 1; /* flag */
790
goto A_CYCLE; /* take A cycle */
791
case BCD_DOLLAR: /* $ */
792
if (!qbody || qaster || !(cpu_unit.flags & EPE)) break;
793
qdollar = 1; /* flag */
794
goto A_CYCLE; /* take A cycle */
795
case BCD_ZERO: /* 0 */
796
if (qawm && !qzero && !(b & WM)) {
797
M[BS] = BCD_ZERO + WM; /* mark with WM */
798
qzero = 1; /* flag supress */
800
if (!qzero) t = t | WM; /* first? set WM */
801
qzero = 1; /* flag suppress */
803
case BCD_BLANK: /* blank */
804
if (qawm) break; /* any A left? */
806
M[BS] = t; /* copy char */
807
if (a & WM) { /* end of A field? */
808
qbody = 0; /* end body */
810
else { qbody = 1; /* in body */
811
a = M[AS]; MM (AS); /* next A */
814
case BCD_C: case BCD_R: case BCD_MINUS: /* C, R, - */
815
if (!qsign && !qbody) M[BS] = BCD_BLANK;
817
case BCD_COMMA: /* , */
818
if (!qbody) M[BS] = BCD_BLANK; /* bl if status */
820
case BCD_AMPER: /* & */
821
M[BS] = BCD_BLANK; /* blank B field */
822
break; } /* end switch */
823
MM (BS); } /* decr B pointer */
824
while ((b & WM) == 0); /* stop on B WM */
826
if (!qawm || !qzero) { /* rescan? */
827
if (qdollar) reason = STOP_MCE3; /* error if $ */
830
/* Edit pass 2 - from left to right, suppressing zeroes */
832
do { b = M[++BS]; /* get B char */
833
switch (b & CHAR) { /* case on B char */
834
case BCD_ONE: case BCD_TWO: case BCD_THREE:
835
case BCD_FOUR: case BCD_FIVE: case BCD_SIX:
836
case BCD_SEVEN: case BCD_EIGHT: case BCD_NINE:
837
qzero = 0; /* turn off supr */
839
case BCD_ZERO: case BCD_COMMA: /* 0 or , */
840
if (qzero && !qdecimal) /* if supr, blank */
841
M[BS] = qaster? BCD_ASTER: BCD_BLANK;
843
case BCD_BLANK: /* blank */
844
if (qaster) M[BS] = BCD_ASTER; /* if EPE *, repl */
846
case BCD_DECIMAL: /* . */
847
if (qzero && (cpu_unit.flags & EPE))
848
qdecimal = 1; /* flag for EPE */
849
case BCD_PERCNT: case BCD_WM: case BCD_BS:
850
case BCD_TS: case BCD_MINUS:
853
qzero = 1; /* restart supr */
854
break; } } /* end case, do */
855
while ((b & WM) == 0);
857
M[BS] = M[BS] & ~WM; /* clear B WM */
858
if (!qdollar && !(qdecimal && qzero)) break; /* rescan again? */
859
if (qdecimal && qzero) qdollar = 0; /* no digits? clr $ */
861
/* Edit pass 3 (extended print only) - from right to left */
863
for (;; ) { /* until chars */
864
b = M[BS]; /* get B char */
865
if ((b == BCD_BLANK) && qdollar) { /* blank & flt $? */
866
M[BS] = BCD_DOLLAR; /* insert $ */
867
break; } /* exit for */
868
if (b == BCD_DECIMAL) { /* decimal? */
869
M[BS] = qaster? BCD_ASTER: BCD_BLANK;
870
break; } /* exit for */
871
if ((b == BCD_ZERO) && !qdollar) /* 0 & ~flt $ */
872
M[BS] = qaster? BCD_ASTER: BCD_BLANK;
873
BS--; } /* end for */
874
break; /* done at last! */
876
/* Miscellaneous instructions A check B check
878
SWM: set WM on A char and B char fetch fetch
879
CWM: clear WM on A char and B char fetch fetch
880
CS: clear from B down to nearest hundreds address if branch fetch
881
MA: add A addr and B addr, store at B addr fetch fetch
882
SAR: store A* at A addr fetch
883
SBR: store B* at A addr fetch
888
case OP_SWM: /* set word mark */
889
M[BS] = M[BS] | WM; /* set A field mark */
890
M[AS] = M[AS] | WM; /* set B field mark */
891
MM (AS); MM (BS); /* decr pointers */
893
case OP_CWM: /* clear word mark */
894
M[BS] = M[BS] & ~WM; /* clear A field mark */
895
M[AS] = M[AS] & ~WM; /* clear B field mark */
896
MM (AS); MM (BS); /* decr pointers */
898
case OP_CS: /* clear storage */
899
t = (BS / 100) * 100; /* lower bound */
900
while (BS >= t) M[BS--] = 0; /* clear region */
901
if (BS < 0) BS = BS + MEMSIZE; /* wrap if needed */
902
if (ilnt >= 7) { BRANCH; } /* branch variant? */
904
case OP_MA: /* modify address */
905
a = one_table[M[AS] & CHAR]; MM (AS); /* get A address */
906
a = a + ten_table[M[AS] & CHAR]; MM (AS);
907
a = a + hun_table[M[AS] & CHAR]; MM (AS);
908
b = one_table[M[BS] & CHAR]; MM (BS); /* get B address */
909
b = b + ten_table[M[BS] & CHAR]; MM (BS);
910
b = b + hun_table[M[BS] & CHAR]; MM (BS);
911
t = ((a + b) & INDEXMASK) % MAXMEMSIZE; /* compute sum */
912
M[BS + 3] = (M[BS + 3] & WM) | store_addr_u (t);
913
M[BS + 2] = (M[BS + 2] & (WM + ZONE)) | store_addr_t (t);
914
M[BS + 1] = (M[BS + 1] & WM) | store_addr_h (t);
915
if (((a % 4000) + (b % 4000)) >= 4000) BS = BS + 2; /* carry? */
917
case OP_SAR: case OP_SBR: /* store A, B reg */
918
M[AS] = (M[AS] & WM) | store_addr_u (BS); MM (AS);
919
M[AS] = (M[AS] & WM) | store_addr_t (BS); MM (AS);
920
M[AS] = (M[AS] & WM) | store_addr_h (BS); MM (AS);
922
case OP_NOP: /* nop */
924
case OP_H: /* halt */
925
if (ilnt >= 4) { BRANCH; } /* branch if called */
926
reason = STOP_HALT; /* stop simulator */
927
saved_IS = IS; /* commit instruction */
930
reason = STOP_NXI; /* unimplemented */
931
break; } /* end switch */
934
/* Simulation halted */
936
as_err = (AS > ADDRMASK);
937
bs_err = (BS > ADDRMASK);
939
} /* end sim_instr */
941
/* store addr_x - convert address to BCD character in x position
944
addr = address to convert
946
char = converted address character
949
int32 store_addr_h (int32 addr)
953
thous = (addr / 1000) & 03;
954
return bin_to_bcd[(addr % 1000) / 100] | (thous << V_ZONE);
957
int32 store_addr_t (int32 addr)
959
return bin_to_bcd[(addr % 100) / 10];
962
int32 store_addr_u (int32 addr)
966
thous = (addr / 1000) & 014;
967
return bin_to_bcd[addr % 10] | (thous << (V_ZONE - 2));
970
/* iomod - check on I/O modifiers
973
ilnt = instruction length
974
mod = modifier character
975
tptr = pointer to table of modifiers, end is -1
977
status = SCPE_OK if ok, STOP_INVM if invalid
980
t_stat iomod (int32 ilnt, int32 mod, const int32 *tptr)
982
if ((ilnt != 2) && (ilnt != 5) && (ilnt < 8)) return SCPE_OK;
983
if (tptr == NULL) return STOP_INVM;
984
do { if (mod == *tptr++) return SCPE_OK; }
989
/* iodisp - dispatch load or move to I/O routine
994
flag = move (MD_NORM) vs load (MD_WM)
998
t_stat iodisp (int32 dev, int32 unit, int32 flag, int32 mod)
1000
if (dev == IO_INQ) return inq_io (flag, mod); /* inq terminal? */
1001
if (dev == IO_MT) return mt_io (unit, flag, mod); /* magtape? */
1002
if (dev == IO_MTB) { /* binary? */
1003
if (flag == MD_WM) return STOP_INVM; /* invalid */
1004
return mt_io (unit, MD_BIN, mod); }
1005
return STOP_NXD; /* not implemented */
1010
t_stat cpu_reset (DEVICE *dptr)
1014
for (i = 0; i < 64; i++) ind[i] = 0;
1018
return cpu_svc (&cpu_unit);
1021
/* Breakpoint service */
1023
t_stat cpu_svc (UNIT *uptr)
1025
if ((ibkpt_addr - ILL_ADR_FLAG) == save_ibkpt) ibkpt_addr = save_ibkpt;
1030
/* Memory examine */
1032
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
1034
if (addr >= MEMSIZE) return SCPE_NXM;
1035
if (vptr != NULL) *vptr = M[addr] & (WM + CHAR);
1039
/* Memory deposit */
1041
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
1043
if (addr >= MEMSIZE) return SCPE_NXM;
1044
M[addr] = val & (WM + CHAR);
1048
/* Memory size change */
1050
t_stat cpu_set_size (UNIT *uptr, int32 value)
1054
if ((value <= 0) || (value > MAXMEMSIZE) || ((value % 1000) != 0))
1056
for (i = value; i < MEMSIZE; i++) mc = mc | M[i];
1057
if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))
1060
for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0;
1061
if (MEMSIZE > 4000) cpu_unit.flags = cpu_unit.flags | MA;
1062
else cpu_unit.flags = cpu_unit.flags & ~MA;