2223 lines
85 KiB
C
2223 lines
85 KiB
C
/* i1620_cpu.c: IBM 1620 CPU simulator
|
|
|
|
Copyright (c) 2002-2013, Robert M. Supnik
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of Robert M Supnik shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Robert M Supnik.
|
|
|
|
This CPU module incorporates code and comments from the 1620 simulator by
|
|
Geoff Kuenning, with his permission.
|
|
|
|
10-Dec-13 RMS Fixed several bugs in add and compare (Bob Armstrong)
|
|
Fixed handling of P field in K instruction (Bob Armstrong)
|
|
28-May-06 RMS Fixed bug in cpu history (Peter Schorn)
|
|
22-Sep-05 RMS Fixed declarations (Sterling Garwood)
|
|
16-Aug-05 RMS Fixed C++ declaration and cast problems
|
|
07-Nov-04 RMS Added instruction history
|
|
26-Mar-04 RMS Fixed warnings with -std=c99
|
|
02-Nov-03 RMS Fixed bug in branch digit (Dave Babcock)
|
|
21-Aug-03 RMS Fixed bug in immediate index add (Michael Short)
|
|
25-Apr-03 RMS Changed t_addr to uint32 throughout
|
|
18-Oct-02 RMS Fixed bugs in invalid result testing (Hans Pufal)
|
|
|
|
The simulated register state for the IBM 1620 is:
|
|
|
|
1620 sim comment
|
|
|
|
IR1 [PC] program counter
|
|
IR2 instruction register 2 (subroutine return address)
|
|
OR1 [QAR] Q address
|
|
OR2 [PAR] P address
|
|
PR1 manual save address
|
|
ind[0:99] indicators
|
|
|
|
Additional internal registers OR3, PR2, and PR3 are not simulated.
|
|
|
|
The IBM 1620 is a fixed instruction length, variable data length, decimal
|
|
data system. Memory consists of 20000 - 60000 BCD digits, each containing
|
|
four bits of data and a flag. There are no general registers; all
|
|
instructions are memory to memory.
|
|
|
|
The 1620 uses a fixed, 12 digit instruction format:
|
|
|
|
oo ppppp qqqqq
|
|
|
|
where
|
|
|
|
oo = opcode
|
|
ppppp = P (usually destination) address
|
|
qqqqq = Q (usually source) address
|
|
|
|
Immediate instructions use the qqqqq field as the second operand.
|
|
|
|
The 1620 Model 1 uses table lookups for add and multiply; for that reason,
|
|
it was nicknamed CADET (Can't Add, Doesn't Even Try). The Model 2 does
|
|
adds in hardware and uses the add table memory for index registers.
|
|
|
|
This routine is the instruction decode routine for the IBM 1620.
|
|
It is called from the simulator control program to execute
|
|
instructions in simulated memory, starting at the simulated PC.
|
|
It runs until 'reason' is set non-zero.
|
|
|
|
General notes:
|
|
|
|
1. Reasons to stop. The simulator can be stopped by:
|
|
|
|
HALT instruction
|
|
breakpoint encountered
|
|
illegal addresses or instruction formats
|
|
I/O error in I/O simulator
|
|
|
|
2. Interrupts. The 1620 has no interrupt structure.
|
|
|
|
3. Non-existent memory. On the 1620, all memory references
|
|
are modulo the memory size.
|
|
|
|
4. Adding I/O devices. These modules must be modified:
|
|
|
|
i1620_cpu.c add iodisp table entry
|
|
i1620_sys.c add sim_devices table entry
|
|
*/
|
|
|
|
#include "i1620_defs.h"
|
|
|
|
#define PCQ_SIZE 64 /* must be 2**n */
|
|
#define PCQ_MASK (PCQ_SIZE - 1)
|
|
#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = saved_PC
|
|
|
|
#define HIST_PC 0x40000000
|
|
#define HIST_MIN 64
|
|
#define HIST_MAX 65536
|
|
|
|
typedef struct {
|
|
uint16 vld;
|
|
uint16 pc;
|
|
uint8 inst[INST_LEN];
|
|
} InstHistory;
|
|
|
|
uint8 M[MAXMEMSIZE] = { 0 }; /* main memory */
|
|
uint32 saved_PC = 0; /* saved PC */
|
|
uint32 IR2 = 1; /* inst reg 2 */
|
|
uint32 PAR = 0; /* P address */
|
|
uint32 QAR = 0; /* Q address */
|
|
uint32 PR1 = 1; /* proc reg 1 */
|
|
uint32 iae = 1; /* ind addr enb */
|
|
uint32 idxe = 0; /* index enable */
|
|
uint32 idxb = 0; /* index band */
|
|
uint32 io_stop = 1; /* I/O stop */
|
|
uint32 ar_stop = 1; /* arith stop */
|
|
int32 ind_max = 16; /* iadr nest limit */
|
|
uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */
|
|
int32 pcq_p = 0; /* PC queue ptr */
|
|
REG *pcq_r = NULL; /* PC queue reg ptr */
|
|
int32 hst_p = 0; /* history pointer */
|
|
int32 hst_lnt = 0; /* history length */
|
|
InstHistory *hst = NULL; /* instruction history */
|
|
uint8 ind[NUM_IND] = { 0 }; /* indicators */
|
|
|
|
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
|
|
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
|
|
t_stat cpu_reset (DEVICE *dptr);
|
|
t_stat cpu_set_opt1 (UNIT *uptr, int32 val, char *cptr, void *desc);
|
|
t_stat cpu_set_opt2 (UNIT *uptr, int32 val, char *cptr, void *desc);
|
|
t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc);
|
|
t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
|
|
t_stat cpu_set_save (UNIT *uptr, int32 val, char *cptr, void *desc);
|
|
t_stat cpu_set_table (UNIT *uptr, int32 val, char *cptr, void *desc);
|
|
t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc);
|
|
t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc);
|
|
|
|
int32 get_2d (uint32 ad);
|
|
t_stat get_addr (uint32 alast, int32 lnt, t_bool indexok, uint32 *addr);
|
|
t_stat cvt_addr (uint32 alast, int32 lnt, t_bool signok, int32 *val);
|
|
t_stat get_idx (uint32 aidx);
|
|
t_stat xmt_field (uint32 d, uint32 s, uint32 skp);
|
|
t_stat xmt_record (uint32 d, uint32 s, t_bool cpy);
|
|
t_stat xmt_index (uint32 d, uint32 s);
|
|
t_stat xmt_divd (uint32 d, uint32 s);
|
|
t_stat xmt_tns (uint32 d, uint32 s);
|
|
t_stat xmt_tnf (uint32 d, uint32 s);
|
|
t_stat add_field (uint32 d, uint32 s, t_bool sub, t_bool sto, uint32 skp, int32 *sta);
|
|
uint32 add_one_digit (uint32 dst, uint32 src, uint32 *cry);
|
|
t_stat mul_field (uint32 mpc, uint32 mpy);
|
|
t_stat mul_one_digit (uint32 mpyd, uint32 mpcp, uint32 prop, uint32 last);
|
|
t_stat div_field (uint32 dvd, uint32 dvr, int32 *ez);
|
|
t_stat div_one_digit (uint32 dvd, uint32 dvr, uint32 max, uint32 *quod, uint32 *quop);
|
|
t_stat oct_to_dec (uint32 tbl, uint32 s);
|
|
t_stat dec_to_oct (uint32 d, uint32 tbl, int32 *ez);
|
|
t_stat or_field (uint32 d, uint32 s);
|
|
t_stat and_field (uint32 d, uint32 s);
|
|
t_stat xor_field (uint32 d, uint32 s);
|
|
t_stat com_field (uint32 d, uint32 s);
|
|
void upd_ind (void);
|
|
|
|
extern t_stat tty (uint32 op, uint32 pa, uint32 f0, uint32 f1);
|
|
extern t_stat ptp (uint32 op, uint32 pa, uint32 f0, uint32 f1);
|
|
extern t_stat ptr (uint32 op, uint32 pa, uint32 f0, uint32 f1);
|
|
extern t_stat cdp (uint32 op, uint32 pa, uint32 f0, uint32 f1);
|
|
extern t_stat cdr (uint32 op, uint32 pa, uint32 f0, uint32 f1);
|
|
extern t_stat dp (uint32 op, uint32 pa, uint32 f0, uint32 f1);
|
|
extern t_stat lpt (uint32 op, uint32 pa, uint32 f0, uint32 f1);
|
|
extern t_stat btp (uint32 op, uint32 pa, uint32 f0, uint32 f1);
|
|
extern t_stat btr (uint32 op, uint32 pa, uint32 f0, uint32 f1);
|
|
|
|
extern t_stat fp_add (uint32 d, uint32 s, t_bool sub);
|
|
extern t_stat fp_mul (uint32 d, uint32 s);
|
|
extern t_stat fp_div (uint32 d, uint32 s);
|
|
extern t_stat fp_fsl (uint32 d, uint32 s);
|
|
extern t_stat fp_fsr (uint32 d, uint32 s);
|
|
|
|
/* CPU data structures
|
|
|
|
cpu_dev CPU device descriptor
|
|
cpu_unit CPU unit descriptor
|
|
cpu_reg CPU register list
|
|
cpu_mod CPU modifier list
|
|
*/
|
|
|
|
UNIT cpu_unit = { UDATA (NULL, UNIT_FIX+UNIT_BCD+MI_STD, MAXMEMSIZE) };
|
|
|
|
REG cpu_reg[] = {
|
|
{ DRDATA (PC, saved_PC, 16), PV_LEFT },
|
|
{ DRDATA (IR2, IR2, 16), PV_LEFT },
|
|
{ DRDATA (PR1, PR1, 16), PV_LEFT },
|
|
{ DRDATA (PAR, PAR, 16), PV_LEFT + REG_RO },
|
|
{ DRDATA (QAR, QAR, 16), PV_LEFT + REG_RO },
|
|
{ FLDATA (SW1, ind[IN_SW1], 0) },
|
|
{ FLDATA (SW2, ind[IN_SW2], 0) },
|
|
{ FLDATA (SW3, ind[IN_SW3], 0) },
|
|
{ FLDATA (SW4, ind[IN_SW4], 0) },
|
|
{ FLDATA (HP, ind[IN_HP], 0) },
|
|
{ FLDATA (EZ, ind[IN_EZ], 0) },
|
|
{ FLDATA (OVF, ind[IN_OVF], 0) },
|
|
{ FLDATA (EXPCHK, ind[IN_EXPCHK], 0) },
|
|
{ FLDATA (RDCHK, ind[IN_RDCHK], 0) },
|
|
{ FLDATA (WRCHK, ind[IN_WRCHK], 0) },
|
|
{ FLDATA (ARSTOP, ar_stop, 0) },
|
|
{ FLDATA (IOSTOP, io_stop, 0) },
|
|
{ BRDATA (IND, ind, 10, 1, NUM_IND) },
|
|
{ FLDATA (IAE, iae, 0) },
|
|
{ FLDATA (IDXE, idxe, 0) },
|
|
{ FLDATA (IDXB, idxb, 0) },
|
|
{ DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT },
|
|
{ BRDATA (PCQ, pcq, 10, 14, PCQ_SIZE), REG_RO+REG_CIRC },
|
|
{ ORDATA (PCQP, pcq_p, 6), REG_HRO },
|
|
{ ORDATA (WRU, sim_int_char, 8) },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB cpu_mod[] = {
|
|
{ IF_IA, IF_IA, "IA", "IA", &cpu_set_opt1 },
|
|
{ IF_IA, 0, "no IA", "NOIA", &cpu_set_opt1 },
|
|
{ IF_EDT, IF_EDT, "EDT", "EDT", &cpu_set_opt1 },
|
|
{ IF_EDT, 0, "no EDT", "NOEDT", &cpu_set_opt1 },
|
|
{ IF_DIV, IF_DIV, "DIV", "DIV", &cpu_set_opt1 },
|
|
{ IF_DIV, 0, "no DIV", "NODIV", &cpu_set_opt1 },
|
|
{ IF_FP, IF_FP, "FP", "FP", NULL },
|
|
{ IF_FP, 0, "no FP", "NOFP", NULL },
|
|
{ IF_BIN, IF_BIN, "BIN", "BIN", &cpu_set_opt2 },
|
|
{ IF_BIN, 0, "no BIN", "NOBIN", &cpu_set_opt2 },
|
|
{ IF_IDX, IF_IDX, "IDX", "IDX", &cpu_set_opt2 },
|
|
{ IF_IDX, 0, "no IDX", "NOIDX", &cpu_set_opt2 },
|
|
{ IF_MII, IF_MII, "Model 2", "MOD2", &cpu_set_model },
|
|
{ IF_MII, 0, "Model 1", "MOD1", &cpu_set_model },
|
|
{ UNIT_MSIZE, 20000, NULL, "20K", &cpu_set_size },
|
|
{ UNIT_MSIZE, 40000, NULL, "40K", &cpu_set_size },
|
|
{ UNIT_MSIZE, 60000, NULL, "60K", &cpu_set_size },
|
|
{ UNIT_MSIZE, 0, NULL, "SAVE", &cpu_set_save },
|
|
{ UNIT_MSIZE, 0, NULL, "TABLE", &cpu_set_table },
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY",
|
|
&cpu_set_hist, &cpu_show_hist },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE cpu_dev = {
|
|
"CPU", &cpu_unit, cpu_reg, cpu_mod,
|
|
1, 10, 18, 1, 16, 5,
|
|
&cpu_ex, &cpu_dep, &cpu_reset,
|
|
NULL, NULL, NULL
|
|
};
|
|
|
|
/* Instruction table */
|
|
|
|
const int32 op_table[100] = {
|
|
0, /* 0 */
|
|
IF_FP + IF_VPA + IF_VQA, /* FADD */
|
|
IF_FP + IF_VPA + IF_VQA, /* FSUB */
|
|
IF_FP + IF_VPA + IF_VQA, /* FMUL */
|
|
0,
|
|
IF_FP + IF_VPA + IF_VQA, /* FSL */
|
|
IF_FP + IF_MII + IF_VPA + IF_VQA, /* TFL */
|
|
IF_FP + IF_MII + IF_VPA + IF_VQA, /* BTFL */
|
|
IF_FP + IF_VPA + IF_VQA, /* FSR */
|
|
IF_FP + IF_VPA + IF_VQA, /* FDV */
|
|
IF_MII + IF_VPA + IF_IMM, /* 10: BTAM */
|
|
IF_VPA + IF_IMM, /* AM */
|
|
IF_VPA + IF_IMM, /* SM */
|
|
IF_VPA + IF_IMM, /* MM */
|
|
IF_VPA + IF_IMM, /* CM */
|
|
IF_VPA + IF_IMM, /* TDM */
|
|
IF_VPA + IF_IMM, /* TFM */
|
|
IF_VPA + IF_IMM, /* BTM */
|
|
IF_DIV + IF_VPA + IF_IMM, /* LDM */
|
|
IF_DIV + IF_VPA + IF_IMM, /* DM */
|
|
IF_MII + IF_VPA + IF_VQA, /* 20: BTA */
|
|
IF_VPA + IF_VQA, /* A */
|
|
IF_VPA + IF_VQA, /* S */
|
|
IF_VPA + IF_VQA, /* M */
|
|
IF_VPA + IF_VQA, /* C */
|
|
IF_VPA + IF_VQA, /* TD */
|
|
IF_VPA + IF_VQA, /* TF */
|
|
IF_VPA + IF_VQA, /* BT */
|
|
IF_DIV + IF_VPA + IF_VQA, /* LD */
|
|
IF_DIV + IF_VPA + IF_VQA, /* D */
|
|
IF_MII + IF_VPA + IF_VQA, /* 30: TRNM */
|
|
IF_VPA + IF_VQA, /* TR */
|
|
IF_VPA, /* SF */
|
|
IF_VPA, /* CF */
|
|
0, /* K */
|
|
IF_VPA, /* DN */
|
|
IF_VPA, /* RN */
|
|
IF_VPA, /* RA */
|
|
IF_VPA, /* WN */
|
|
IF_VPA, /* WA */
|
|
0, /* 40 */
|
|
0, /* NOP */
|
|
0, /* BB */
|
|
IF_VPA + IF_VQA, /* BD */
|
|
IF_VPA + IF_VQA, /* BNF */
|
|
IF_VPA + IF_VQA, /* BNR */
|
|
IF_VPA, /* BI */
|
|
IF_VPA, /* BNI */
|
|
0, /* H */
|
|
IF_VPA, /* B */
|
|
0, /* 50 */
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
IF_VPA + IF_VQA, /* BNG - disk sys */
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
IF_MII + IF_VPA, /* 60: BS */
|
|
IF_IDX + IF_VPA + IF_NQX, /* BX */
|
|
IF_IDX + IF_VPA + IF_IMM, /* BXM */
|
|
IF_IDX + IF_VPA + IF_NQX, /* BCX */
|
|
IF_IDX + IF_VPA + IF_IMM, /* BCXM */
|
|
IF_IDX + IF_VPA + IF_NQX, /* BLX */
|
|
IF_IDX + IF_VPA + IF_IMM, /* BLXM */
|
|
IF_IDX + IF_VPA + IF_NQX, /* BSX */
|
|
0,
|
|
0,
|
|
IF_IDX + IF_VPA + IF_VQA, /* 70: MA */
|
|
IF_EDT + IF_VPA + IF_VQA, /* MF */
|
|
IF_EDT + IF_VPA + IF_VQA, /* MF */
|
|
IF_EDT + IF_VPA + IF_VQA, /* TNF */
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0, /* 80 */
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
IF_BIN + IF_VPA + IF_4QA, /* 90: BBT */
|
|
IF_BIN + IF_VPA + IF_4QA, /* BMK */
|
|
IF_BIN + IF_VPA + IF_VQA, /* ORF */
|
|
IF_BIN + IF_VPA + IF_VQA, /* ANDF */
|
|
IF_BIN + IF_VPA + IF_VQA, /* CPLF */
|
|
IF_BIN + IF_VPA + IF_VQA, /* EORF */
|
|
IF_BIN + IF_VPA + IF_VQA, /* OTD */
|
|
IF_BIN + IF_VPA + IF_VQA, /* DTO */
|
|
0,
|
|
0
|
|
};
|
|
|
|
/* IO dispatch table */
|
|
|
|
t_stat (*iodisp[NUM_IO])(uint32 op, uint32 pa, uint32 f0, uint32 f1) = {
|
|
NULL, &tty, &ptp, &ptr, &cdp, /* 00 - 09 */
|
|
&cdr, NULL, &dp, NULL, &lpt,
|
|
NULL, NULL, NULL, NULL, NULL, /* 10 - 19 */
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, /* 20 - 29 */
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, &btp, &btr, NULL, /* 30 - 39 */
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, /* 40 - 49 */
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, /* 50 - 59 */
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, /* 60 - 69 */
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, /* 70 - 79 */
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, /* 80 - 89 */
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, /* 90 - 99 */
|
|
NULL, NULL, NULL, NULL, NULL
|
|
};
|
|
|
|
/* K instruction validate P field table */
|
|
|
|
const uint8 k_valid_p[NUM_IO] = {
|
|
0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
|
};
|
|
|
|
/* Indicator table: -1 = illegal, +1 = resets when tested */
|
|
|
|
const int32 ind_table[NUM_IND] = {
|
|
-1, 0, 0, 0, 0, -1, 1, 1, -1, 1, /* 00 - 09 */
|
|
-1, 0, 0, 0, 1, 1, 1, 1, -1, 0, /* 10 - 19 */
|
|
-1, -1, -1, -1, -1, 0, -1, -1, -1, -1, /* 20 - 29 */
|
|
0, 0, 0, 1, 1, 0, 1, 1, 1, 0, /* 30 - 39 */
|
|
-1, -1, 1, -1, -1, -1, -1, -1, -1, -1, /* 40 - 49 */
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 50 - 59 */
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 60 - 69 */
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 - 79 */
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 89 */
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* 90 - 99 */
|
|
};
|
|
|
|
/* Add table for 1620 Model 1 */
|
|
|
|
const uint8 std_add_table[ADD_TABLE_LEN] = {
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
|
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
|
|
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11,
|
|
0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12,
|
|
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13,
|
|
0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14,
|
|
0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
|
|
0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
|
0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18
|
|
};
|
|
|
|
/* Add table for 1620 Model 2 ("hardware add") */
|
|
|
|
const uint8 sum_table[20] = {
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19
|
|
};
|
|
|
|
/* Multiply table */
|
|
|
|
const uint8 std_mul_table[MUL_TABLE_LEN] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 1, 0, 2, 0, 3, 0, 4, 0,
|
|
0, 0, 2, 0, 4, 0, 6, 0, 8, 0,
|
|
0, 0, 3, 0, 6, 0, 9, 0, 2, 1,
|
|
0, 0, 4, 0, 8, 0, 2, 1, 6, 1,
|
|
0, 0, 5, 0, 0, 1, 5, 1, 0, 2,
|
|
0, 0, 6, 0, 2, 1, 8, 1, 4, 2,
|
|
0, 0, 7, 0, 4, 1, 1, 2, 8, 2,
|
|
0, 0, 8, 0, 6, 1, 4, 2, 2, 3,
|
|
0, 0, 9, 0, 8, 1, 7, 2, 6, 3,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
5, 0, 6, 0, 7, 0, 8, 0, 9, 0,
|
|
0, 1, 2, 1, 4, 1, 6, 1, 8, 1,
|
|
5, 1, 8, 1, 1, 2, 4, 2, 7, 2,
|
|
0, 2, 4, 2, 8, 2, 2, 3, 6, 3,
|
|
5, 2, 0, 3, 5, 3, 0, 4, 5, 4,
|
|
0, 3, 6, 3, 2, 4, 8, 4, 4, 5,
|
|
5, 3, 2, 4, 9, 4, 6, 5, 3, 6,
|
|
0, 4, 8, 4, 6, 5, 4, 6, 2, 7,
|
|
5, 4, 4, 5, 3, 6, 2, 7, 1, 8
|
|
};
|
|
|
|
#define BRANCH(x) PCQ_ENTRY; PC = (x)
|
|
#define GET_IDXADDR(x) ((idxb? IDX_B: IDX_A) + ((x) * ADDR_LEN) + (ADDR_LEN - 1))
|
|
|
|
t_stat sim_instr (void)
|
|
{
|
|
uint32 PC, pla, qla, f0, f1;
|
|
int32 i, t, idx, flags, sta, dev, op;
|
|
t_stat reason;
|
|
|
|
/* Restore saved state */
|
|
|
|
PC = saved_PC;
|
|
if ((cpu_unit.flags & IF_IA) == 0)
|
|
iae = 0;
|
|
if ((cpu_unit.flags & IF_IDX) == 0)
|
|
idxe = idxb = 0;
|
|
upd_ind (); /* update indicators */
|
|
reason = 0;
|
|
|
|
/* Main instruction fetch/decode loop */
|
|
|
|
while (reason == 0) { /* loop until halted */
|
|
|
|
saved_PC = PC; /* commit prev instr */
|
|
if (sim_interval <= 0) { /* check clock queue */
|
|
if ((reason = sim_process_event ()))
|
|
break;
|
|
}
|
|
|
|
if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */
|
|
reason = STOP_IBKPT; /* stop simulation */
|
|
break;
|
|
}
|
|
|
|
sim_interval = sim_interval - 1;
|
|
|
|
/* Instruction fetch and address decode */
|
|
|
|
if (PC & 1) { /* PC odd? */
|
|
reason = STOP_INVIAD; /* stop */
|
|
break;
|
|
}
|
|
|
|
op = get_2d (PC); /* get opcode */
|
|
if (op < 0) { /* invalid? */
|
|
reason = STOP_INVINS;
|
|
break;
|
|
}
|
|
flags = op_table[op]; /* get op, flags */
|
|
if ((flags & ALLOPT) && /* need option? */
|
|
!(flags & ALLOPT & cpu_unit.flags)) { /* any set? */
|
|
reason = STOP_INVINS; /* no, error */
|
|
break;
|
|
}
|
|
|
|
pla = ADDR_A (PC, I_PL); /* P last addr */
|
|
qla = ADDR_A (PC, I_QL); /* Q last addr */
|
|
if (flags & IF_VPA) { /* need P? */
|
|
reason = get_addr (pla, 5, TRUE, &PAR); /* get P addr */
|
|
if (reason != SCPE_OK) /* stop if error */
|
|
break;
|
|
}
|
|
if (flags & (IF_VQA | IF_4QA | IF_NQX)) { /* need Q? */
|
|
reason = get_addr (qla, /* get Q addr */
|
|
((flags & IF_4QA)? 4: 5), /* 4 or 5 digits */
|
|
((flags & IF_NQX)? FALSE: TRUE), /* not or indexed */
|
|
&QAR);
|
|
if (reason != SCPE_OK) { /* stop if invalid */
|
|
reason = reason + (STOP_INVQDG - STOP_INVPDG);
|
|
break;
|
|
}
|
|
}
|
|
else if (flags & IF_IMM) /* immediate? */
|
|
QAR = qla;
|
|
|
|
if (hst_lnt) { /* history enabled? */
|
|
hst_p = (hst_p + 1); /* next entry */
|
|
if (hst_p >= hst_lnt)
|
|
hst_p = 0;
|
|
hst[hst_p].vld = 1;
|
|
hst[hst_p].pc = PC;
|
|
for (i = 0; i < INST_LEN; i++)
|
|
hst[hst_p].inst[i] = M[(PC + i) % MEMSIZE];
|
|
}
|
|
|
|
PC = PC + INST_LEN; /* advance PC */
|
|
switch (op) { /* case on op */
|
|
|
|
/* Transmit digit - P,Q are valid */
|
|
|
|
case OP_TD:
|
|
case OP_TDM:
|
|
M[PAR] = M[QAR] & (FLAG | DIGIT); /* move dig, flag */
|
|
break;
|
|
|
|
/* Transmit field - P,Q are valid */
|
|
|
|
case OP_TF:
|
|
case OP_TFM:
|
|
reason = xmt_field (PAR, QAR, 1); /* xmit field */
|
|
break;
|
|
|
|
/* Transmit record - P,Q are valid */
|
|
|
|
case OP_TR:
|
|
reason = xmt_record (PAR, QAR, TRUE); /* xmit record */
|
|
break;
|
|
|
|
/* Transmit record no record mark - P,Q are valid */
|
|
|
|
case OP_TRNM:
|
|
reason = xmt_record (PAR, QAR, FALSE); /* xmit record but */
|
|
break; /* not rec mark */
|
|
|
|
/* Set flag - P is valid */
|
|
|
|
case OP_SF:
|
|
M[PAR] = M[PAR] | FLAG; /* set flag on P */
|
|
break;
|
|
|
|
/* Clear flag - P is valid */
|
|
|
|
case OP_CF:
|
|
M[PAR] = M[PAR] & ~FLAG; /* clear flag on P */
|
|
break;
|
|
|
|
/* Branch - P is valid */
|
|
|
|
case OP_B:
|
|
BRANCH (PAR); /* branch to P */
|
|
break;
|
|
|
|
/* Branch and transmit - P,Q are valid */
|
|
|
|
case OP_BT:
|
|
case OP_BTM:
|
|
reason = xmt_field (ADDR_S (PAR, 1), QAR, 1); /* xmit field to P-1 */
|
|
IR2 = PC; /* save PC */
|
|
BRANCH (PAR); /* branch to P */
|
|
break;
|
|
|
|
/* Branch and transmit floating - P,Q are valid */
|
|
|
|
case OP_BTFL:
|
|
reason = xmt_field (ADDR_S (PAR, 1), QAR, 3); /* skip 3 flags */
|
|
IR2 = PC; /* save PC */
|
|
BRANCH (PAR); /* branch to P */
|
|
break;
|
|
|
|
/* Branch and transmit address - P,Q are valid */
|
|
|
|
case OP_BTA:
|
|
case OP_BTAM:
|
|
reason = xmt_field (ADDR_S (PAR, 1), QAR, 4); /* skip 4 flags */
|
|
IR2 = PC; /* save PC */
|
|
BRANCH (PAR); /* branch to P */
|
|
break;
|
|
|
|
/* Branch back */
|
|
|
|
case OP_BB:
|
|
if (PR1 != 1) { /* PR1 valid? */
|
|
BRANCH (PR1); /* return to PR1 */
|
|
PR1 = 1; /* invalidate */
|
|
}
|
|
else if (IR2 != 1) { /* IR2 valid? */
|
|
BRANCH (IR2); /* return to IR2 */
|
|
IR2 = 1; /* invalidate */
|
|
}
|
|
else reason = STOP_INVRTN; /* MAR check */
|
|
break;
|
|
|
|
/* Branch on digit (not zero) - P,Q are valid */
|
|
|
|
case OP_BD:
|
|
if ((M[QAR] & DIGIT) != 0) { /* digit != 0? */
|
|
BRANCH (PAR); /* branch */
|
|
}
|
|
break;
|
|
|
|
/* Branch no flag - P,Q are valid */
|
|
|
|
case OP_BNF:
|
|
if ((M[QAR] & FLAG) == 0) { /* flag == 0? */
|
|
BRANCH (PAR); /* branch */
|
|
}
|
|
break;
|
|
|
|
/* Branch no record mark (8-2 not set) - P,Q are valid */
|
|
|
|
case OP_BNR:
|
|
if ((M[QAR] & REC_MARK) != REC_MARK) { /* not rec mark? */
|
|
BRANCH (PAR); /* branch */
|
|
}
|
|
break;
|
|
|
|
/* Branch no group mark - P,Q are valid */
|
|
|
|
case OP_BNG:
|
|
if ((M[QAR] & DIGIT) != GRP_MARK) { /* not grp mark? */
|
|
BRANCH (PAR); /* branch */
|
|
}
|
|
break;
|
|
|
|
/* Branch (no) indicator - P is valid */
|
|
|
|
case OP_BI:
|
|
case OP_BNI:
|
|
upd_ind (); /* update indicators */
|
|
t = get_2d (ADDR_A (saved_PC, I_BR)); /* get ind number */
|
|
if ((t < 0) || (ind_table[t] < 0)) { /* not valid? */
|
|
reason = STOP_INVIND; /* stop */
|
|
break;
|
|
}
|
|
if ((ind[t] != 0) ^ (op == OP_BNI)) { /* ind value correct? */
|
|
BRANCH (PAR); /* branch */
|
|
}
|
|
if (ind_table[t] > 0) /* reset if needed */
|
|
ind[t] = 0;
|
|
break;
|
|
|
|
/* Add/subtract/compare - P,Q are valid */
|
|
|
|
case OP_A:
|
|
case OP_AM:
|
|
reason = add_field (PAR, QAR, FALSE, TRUE, 0, &sta); /* add, store */
|
|
if (sta == ADD_CARRY) /* cout => ovflo */
|
|
ind[IN_OVF] = 1;
|
|
if (ar_stop && ind[IN_OVF])
|
|
reason = STOP_OVERFL;
|
|
break;
|
|
|
|
case OP_S:
|
|
case OP_SM:
|
|
reason = add_field (PAR, QAR, TRUE, TRUE, 0, &sta); /* sub, store */
|
|
if (sta == ADD_CARRY) /* cout => ovflo */
|
|
ind[IN_OVF] = 1;
|
|
if (ar_stop && ind[IN_OVF])
|
|
reason = STOP_OVERFL;
|
|
break;
|
|
|
|
/* IBM's diagnostics try a compare that generates a carry out; it does not
|
|
generate overflow. Therefore, do not set overflow on a carry out status. */
|
|
|
|
case OP_C:
|
|
case OP_CM:
|
|
reason = add_field (PAR, QAR, TRUE, FALSE, 0, &sta); /* sub, nostore */
|
|
if (ar_stop && ind[IN_OVF])
|
|
reason = STOP_OVERFL;
|
|
break;
|
|
|
|
/* Multiply - P,Q are valid */
|
|
|
|
case OP_M:
|
|
case OP_MM:
|
|
reason = mul_field (PAR, QAR); /* multiply */
|
|
break;
|
|
|
|
/* IO instructions - P is valid, except for K */
|
|
|
|
case OP_RA:
|
|
case OP_WA:
|
|
if ((PAR & 1) == 0) { /* P even? */
|
|
reason = STOP_INVEAD; /* stop */
|
|
break;
|
|
}
|
|
case OP_DN:
|
|
case OP_RN:
|
|
case OP_WN:
|
|
dev = get_2d (ADDR_A (saved_PC, I_IO)); /* get IO dev */
|
|
f0 = M[ADDR_A (saved_PC, I_CTL)] & DIGIT; /* get function */
|
|
f1 = M[ADDR_A (saved_PC, I_CTL + 1)] & DIGIT;
|
|
if ((dev < 0) || (iodisp[dev] == NULL)) /* undefined dev? */
|
|
reason = STOP_INVIO; /* stop */
|
|
else reason = iodisp[dev] (op, PAR, f0, f1); /* call device */
|
|
break;
|
|
|
|
case OP_K:
|
|
dev = get_2d (ADDR_A (saved_PC, I_IO)); /* get IO dev */
|
|
if (k_valid_p[dev]) { /* validate P? */
|
|
reason = get_addr (pla, 5, TRUE, &PAR); /* get P addr */
|
|
if (reason != SCPE_OK) /* stop if error */
|
|
break;
|
|
}
|
|
else PAR = 0;
|
|
f0 = M[ADDR_A (saved_PC, I_CTL)] & DIGIT; /* get function */
|
|
f1 = M[ADDR_A (saved_PC, I_CTL + 1)] & DIGIT;
|
|
if ((dev < 0) || (iodisp[dev] == NULL)) /* undefined dev? */
|
|
reason = STOP_INVIO; /* stop */
|
|
else reason = iodisp[dev] (op, PAR, f0, f1); /* call device */
|
|
break;
|
|
|
|
/* Divide special feature instructions */
|
|
|
|
case OP_LD:
|
|
case OP_LDM:
|
|
for (i = 0; i < PROD_AREA_LEN; i++) /* clear prod area */
|
|
M[PROD_AREA + i] = 0;
|
|
t = M[QAR] & FLAG; /* save Q sign */
|
|
reason = xmt_divd (PAR, QAR); /* xmit dividend */
|
|
M[PROD_AREA + PROD_AREA_LEN - 1] |= t; /* set sign */
|
|
break;
|
|
|
|
/* Divide - P,Q are valid */
|
|
|
|
case OP_D:
|
|
case OP_DM:
|
|
reason = div_field (PAR, QAR, &t); /* divide */
|
|
ind[IN_EZ] = t; /* set indicator */
|
|
if ((reason == STOP_OVERFL) && !ar_stop) /* ovflo stop? */
|
|
reason = SCPE_OK; /* no */
|
|
break;
|
|
|
|
/* Edit special feature instructions */
|
|
|
|
/* Move flag - P,Q are valid */
|
|
|
|
case OP_MF:
|
|
M[PAR] = (M[PAR] & ~FLAG) | (M[QAR] & FLAG); /* copy Q flag */
|
|
M[QAR] = M[QAR] & ~FLAG; /* clr Q flag */
|
|
break;
|
|
|
|
/* Transmit numeric strip - P,Q are valid, P is source */
|
|
|
|
case OP_TNS:
|
|
if ((PAR & 1) == 0) { /* P must be odd */
|
|
reason = STOP_INVEAD;
|
|
break;
|
|
}
|
|
reason = xmt_tns (QAR, PAR); /* xmit and strip */
|
|
break;
|
|
|
|
/* Transmit numeric fill - P,Q are valid */
|
|
|
|
case OP_TNF:
|
|
if ((PAR & 1) == 0) { /* P must be odd */
|
|
reason = STOP_INVEAD;
|
|
break;
|
|
}
|
|
reason = xmt_tnf (PAR, QAR); /* xmit and strip */
|
|
break;
|
|
|
|
/* Index special feature instructions */
|
|
|
|
/* Move address - P,Q are valid */
|
|
|
|
case OP_MA:
|
|
for (i = 0; i < ADDR_LEN; i++) { /* move 5 digits */
|
|
M[PAR] = (M[PAR] & FLAG) | (M[QAR] & DIGIT);
|
|
MM (PAR); MM (QAR);
|
|
}
|
|
break;
|
|
|
|
/* Branch load index - P,Q are valid, Q not indexed */
|
|
|
|
case OP_BLX:
|
|
case OP_BLXM:
|
|
idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
|
|
if (idx < 0) { /* disabled? */
|
|
reason = STOP_INVIDX; /* stop */
|
|
break;
|
|
}
|
|
xmt_index (GET_IDXADDR (idx), QAR); /* copy Q to idx */
|
|
BRANCH (PAR); /* branch to P */
|
|
break;
|
|
|
|
/* Branch store index - P,Q are valid, Q not indexed */
|
|
|
|
case OP_BSX:
|
|
idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
|
|
if (idx < 0) { /* disabled? */
|
|
reason = STOP_INVIDX; /* stop */
|
|
break;
|
|
}
|
|
xmt_index (QAR, GET_IDXADDR (idx)); /* copy idx to Q */
|
|
BRANCH (PAR); /* branch to P */
|
|
break;
|
|
|
|
/* Branch and modify index - P,Q are valid, Q not indexed */
|
|
|
|
case OP_BX:
|
|
idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
|
|
if (idx < 0) { /* disabled? */
|
|
reason = STOP_INVIDX; /* stop */
|
|
break;
|
|
}
|
|
reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 0, &sta);
|
|
if (ar_stop && ind[IN_OVF])
|
|
reason = STOP_OVERFL;
|
|
BRANCH (PAR); /* branch to P */
|
|
break;
|
|
|
|
case OP_BXM:
|
|
idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
|
|
if (idx < 0) { /* disabled? */
|
|
reason = STOP_INVIDX; /* stop */
|
|
break;
|
|
}
|
|
reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 3, &sta);
|
|
if (ar_stop && ind[IN_OVF])
|
|
reason = STOP_OVERFL;
|
|
BRANCH (PAR); /* branch to P */
|
|
break;
|
|
|
|
/* Branch conditionally and modify index - P,Q are valid, Q not indexed */
|
|
|
|
case OP_BCX:
|
|
idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
|
|
if (idx < 0) { /* disabled? */
|
|
reason = STOP_INVIDX; /* stop */
|
|
break;
|
|
}
|
|
reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 0, &sta);
|
|
if (ar_stop && ind[IN_OVF])
|
|
reason = STOP_OVERFL;
|
|
if ((ind[IN_EZ] == 0) && (sta == ADD_NOCRY)) { /* ~z, ~c, ~sign chg? */
|
|
BRANCH (PAR); /* branch */
|
|
}
|
|
break;
|
|
|
|
case OP_BCXM:
|
|
idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */
|
|
if (idx < 0) { /* disabled? */
|
|
reason = STOP_INVIDX; /* stop */
|
|
break;
|
|
}
|
|
reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 3, &sta);
|
|
if (ar_stop && ind[IN_OVF])
|
|
reason = STOP_OVERFL;
|
|
if ((ind[IN_EZ] == 0) && (sta == ADD_NOCRY)) { /* ~z, ~c, ~sign chg? */
|
|
BRANCH (PAR); /* branch */
|
|
}
|
|
break;
|
|
|
|
/* Branch and select - P is valid */
|
|
|
|
case OP_BS:
|
|
t = M[ADDR_A (saved_PC, I_SEL)] & DIGIT; /* get select */
|
|
switch (t) { /* case on select */
|
|
case 0:
|
|
idxe = idxb = 0; /* indexing off */
|
|
break;
|
|
case 1:
|
|
idxe = 1; idxb = 0; /* index band A */
|
|
break;
|
|
case 2:
|
|
idxe = idxb = 1; /* index band B */
|
|
break;
|
|
case 8:
|
|
iae = 0; /* indirect off */
|
|
break;
|
|
case 9:
|
|
iae = 1; /* indirect on */
|
|
break;
|
|
default:
|
|
reason = STOP_INVSEL; /* undefined */
|
|
break;
|
|
}
|
|
BRANCH (PAR);
|
|
break;
|
|
|
|
/* Binary special feature instructions */
|
|
|
|
/* Branch on bit - P,Q are valid, Q is 4d address */
|
|
|
|
case OP_BBT:
|
|
t = M[ADDR_A (saved_PC, I_Q)]; /* get Q0 digit */
|
|
if (t & M[QAR] & DIGIT) { /* match to mem? */
|
|
BRANCH (PAR); /* branch */
|
|
}
|
|
break;
|
|
|
|
/* Branch on mask - P,Q are valid, Q is 4d address */
|
|
|
|
case OP_BMK:
|
|
t = M[ADDR_A (saved_PC, I_Q)]; /* get Q0 digit */
|
|
if (((t ^ M[QAR]) & /* match to mem? */
|
|
((t & FLAG)? (FLAG + DIGIT): DIGIT)) == 0) {
|
|
BRANCH (PAR); /* branch */
|
|
}
|
|
break;
|
|
|
|
/* Or - P,Q are valid */
|
|
|
|
case OP_ORF:
|
|
reason = or_field (PAR, QAR); /* OR fields */
|
|
break;
|
|
|
|
/* AND - P,Q are valid */
|
|
|
|
case OP_ANDF:
|
|
reason = and_field (PAR, QAR); /* AND fields */
|
|
break;
|
|
|
|
/* Exclusive or - P,Q are valid */
|
|
|
|
case OP_EORF:
|
|
reason = xor_field (PAR, QAR); /* XOR fields */
|
|
break;
|
|
|
|
/* Complement - P,Q are valid */
|
|
|
|
case OP_CPLF:
|
|
reason = com_field (PAR, QAR); /* COM field */
|
|
break;
|
|
|
|
/* Octal to decimal - P,Q are valid */
|
|
|
|
case OP_OTD:
|
|
reason = oct_to_dec (PAR, QAR); /* convert */
|
|
break;
|
|
|
|
/* Decimal to octal - P,Q are valid */
|
|
|
|
case OP_DTO:
|
|
reason = dec_to_oct (PAR, QAR, &t); /* convert */
|
|
ind[IN_EZ] = t; /* set indicator */
|
|
if (ar_stop && ind[IN_OVF])
|
|
reason = STOP_OVERFL;
|
|
break;
|
|
|
|
/* Floating point special feature instructions */
|
|
|
|
case OP_FADD:
|
|
reason = fp_add (PAR, QAR, FALSE); /* add */
|
|
if (ar_stop && ind[IN_EXPCHK])
|
|
reason = STOP_EXPCHK;
|
|
break;
|
|
|
|
case OP_FSUB:
|
|
reason = fp_add (PAR, QAR, TRUE); /* subtract */
|
|
if (ar_stop && ind[IN_EXPCHK])
|
|
reason = STOP_EXPCHK;
|
|
break;
|
|
|
|
case OP_FMUL:
|
|
reason = fp_mul (PAR, QAR); /* multiply */
|
|
if (ar_stop && ind[IN_EXPCHK])
|
|
reason = STOP_EXPCHK;
|
|
break;
|
|
|
|
case OP_FDIV:
|
|
reason = fp_div (PAR, QAR); /* divide */
|
|
if (ar_stop && ind[IN_OVF])
|
|
reason = STOP_FPDVZ;
|
|
if (ar_stop && ind[IN_EXPCHK])
|
|
reason = STOP_EXPCHK;
|
|
break;
|
|
|
|
case OP_FSL:
|
|
reason = fp_fsl (PAR, QAR); /* shift left */
|
|
break;
|
|
|
|
case OP_FSR:
|
|
reason = fp_fsr (PAR, QAR); /* shift right */
|
|
break;
|
|
|
|
/* Halt */
|
|
|
|
case OP_H:
|
|
saved_PC = PC; /* commit inst */
|
|
reason = STOP_HALT; /* stop */
|
|
break;
|
|
|
|
/* NOP */
|
|
|
|
case OP_NOP:
|
|
break;
|
|
|
|
/* Invalid instruction code */
|
|
|
|
default:
|
|
reason = STOP_INVINS; /* stop */
|
|
break;
|
|
} /* end switch */
|
|
} /* end while */
|
|
|
|
/* Simulation halted */
|
|
|
|
pcq_r->qptr = pcq_p; /* update pc q ptr */
|
|
upd_ind ();
|
|
return reason;
|
|
}
|
|
|
|
/* Utility routines */
|
|
|
|
/* Get 2 digit field
|
|
|
|
Inputs:
|
|
ad = address of high digit
|
|
Outputs:
|
|
val = field converted to binary
|
|
-1 if bad digit
|
|
*/
|
|
|
|
int32 get_2d (uint32 ad)
|
|
{
|
|
int32 d, d1;
|
|
|
|
d = M[ad] & DIGIT; /* get 1st digit */
|
|
d1 = M[ADDR_A (ad, 1)] & DIGIT; /* get 2nd digit */
|
|
if (BAD_DIGIT (d) || BAD_DIGIT (d1)) /* bad? error */
|
|
return -1;
|
|
return ((d * 10) + d1); /* cvt to binary */
|
|
}
|
|
|
|
/* Get address routine
|
|
|
|
Inputs:
|
|
alast = address of low digit
|
|
lnt = length
|
|
indexok = TRUE if indexing allowed
|
|
&addr = pointer to address output
|
|
Output:
|
|
return = error status (in terms of P address)
|
|
addr = address converted to binary
|
|
|
|
Notes:
|
|
- If indexing produces a negative result, the effective address is
|
|
the 10's complement of the result
|
|
- An address that exceeds memory produces a MAR check stop
|
|
*/
|
|
|
|
t_stat get_addr (uint32 alast, int32 lnt, t_bool indexok, uint32 *reta)
|
|
{
|
|
uint8 indir;
|
|
int32 cnt, idx, idxa, idxv, addr;
|
|
|
|
if (iae) /* init indirect */
|
|
indir = FLAG;
|
|
else indir = 0;
|
|
|
|
cnt = 0; /* count depth */
|
|
do {
|
|
indir = indir & M[alast]; /* get indirect */
|
|
if (cvt_addr (alast, lnt, FALSE, &addr)) /* cvt addr to bin */
|
|
return STOP_INVPDG; /* bad? */
|
|
idx = get_idx (ADDR_S (alast, 1)); /* get index reg num */
|
|
if (indexok && (idx > 0)) { /* indexable? */
|
|
idxa = GET_IDXADDR (idx); /* get idx reg addr */
|
|
if (cvt_addr (idxa, ADDR_LEN, TRUE, &idxv)) /* cvt idx reg */
|
|
return STOP_INVPDG;
|
|
addr = addr + idxv; /* add in index */
|
|
if (addr < 0) /* -? 10's comp */
|
|
addr = addr + 100000;
|
|
}
|
|
if (addr >= (int32) MEMSIZE) /* invalid addr? */
|
|
return STOP_INVPAD;
|
|
alast = addr; /* new address */
|
|
lnt = ADDR_LEN; /* std len */
|
|
} while (indir && (cnt++ < ind_max));
|
|
if (cnt > ind_max) /* indir too deep? */
|
|
return STOP_INVPIA;
|
|
*reta = addr; /* return address */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Convert address to binary
|
|
|
|
Inputs:
|
|
alast = address of low digit
|
|
lnt = length
|
|
signok = TRUE if signed
|
|
val = address of output
|
|
Outputs:
|
|
status = 0 if ok, != 0 if error
|
|
*/
|
|
|
|
t_stat cvt_addr (uint32 alast, int32 lnt, t_bool signok, int32 *val)
|
|
{
|
|
int32 sign = 0, addr = 0, t;
|
|
|
|
if (signok && (M[alast] & FLAG)) /* signed? */
|
|
sign = 1;
|
|
alast = alast - lnt; /* find start */
|
|
do {
|
|
PP (alast); /* incr mem addr */
|
|
t = M[alast] & DIGIT; /* get digit */
|
|
if (BAD_DIGIT (t)) /* bad? error */
|
|
return STOP_INVDIG;
|
|
addr = (addr * 10) + t; /* cvt to bin */
|
|
} while (--lnt > 0);
|
|
if (sign) /* minus? */
|
|
*val = -addr;
|
|
else *val = addr;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Get index register number
|
|
|
|
Inputs:
|
|
aidx = address of low digit
|
|
Outputs:
|
|
index = >0 if indexed
|
|
=0 if not indexed
|
|
<0 if indexing disabled
|
|
*/
|
|
|
|
t_stat get_idx (uint32 aidx)
|
|
{
|
|
int32 i, idx;
|
|
|
|
if (idxe == 0) /* indexing off? */
|
|
return -1;
|
|
for (i = idx = 0; i < 3; i++) { /* 3 flags worth */
|
|
if (M[aidx] & FLAG) /* test flag */
|
|
idx = idx | (1 << i);
|
|
MM (aidx); /* next digit */
|
|
}
|
|
return idx;
|
|
}
|
|
|
|
/* Update indicators routine */
|
|
|
|
void upd_ind (void)
|
|
{
|
|
ind[IN_HPEZ] = ind[IN_HP] | ind[IN_EZ]; /* HPEZ = HP | EZ */
|
|
ind[IN_DERR] = ind[IN_DACH] | ind[IN_DWLR] | ind[IN_DCYO];
|
|
ind[IN_ANYCHK] = ind[IN_RDCHK] | ind[IN_WRCHK] | /* ANYCHK = all chks */
|
|
ind[IN_MBREVEN] | ind[IN_MBRODD] |
|
|
ind[IN_PRCHK] | ind[IN_DACH];
|
|
ind[IN_IXN] = ind[IN_IXA] = ind[IN_IXB] = 0; /* clr index indics */
|
|
if (!idxe) /* off? */
|
|
ind[IN_IXN] = 1;
|
|
else if (!idxb) /* on, band A? */
|
|
ind[IN_IXA] = 1;
|
|
else ind[IN_IXB] = 1; /* no, band B */
|
|
return;
|
|
}
|
|
|
|
/* Transmit routines */
|
|
|
|
/* Transmit field from 's' to 'd' - ignore first 'skp' flags */
|
|
|
|
t_stat xmt_field (uint32 d, uint32 s, uint32 skp)
|
|
{
|
|
uint32 cnt = 0;
|
|
uint8 t;
|
|
|
|
do {
|
|
t = M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */
|
|
MM (d); /* decr mem addrs */
|
|
MM (s);
|
|
if (cnt++ >= MEMSIZE) /* (stop runaway) */
|
|
return STOP_FWRAP;
|
|
} while (((t & FLAG) == 0) || (cnt <= skp)); /* until flag */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Transmit record from 's' to 'd' - copy record mark if 'cpy' = TRUE */
|
|
|
|
t_stat xmt_record (uint32 d, uint32 s, t_bool cpy)
|
|
{
|
|
uint32 cnt = 0;
|
|
|
|
while ((M[s] & REC_MARK) != REC_MARK) { /* until rec mark */
|
|
M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */
|
|
PP (d); /* incr mem addrs */
|
|
PP (s);
|
|
if (cnt++ >= MEMSIZE) /* (stop runaway) */
|
|
return STOP_FWRAP;
|
|
}
|
|
if (cpy) /* copy rec mark */
|
|
M[d] = M[s] & (FLAG | DIGIT);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Transmit index from 's' to 'd' - fixed five character field */
|
|
|
|
t_stat xmt_index (uint32 d, uint32 s)
|
|
{
|
|
int32 i;
|
|
|
|
M[d] = M[s] & (FLAG | DIGIT); /* preserve sign */
|
|
MM (d); MM (s); /* decr mem addrs */
|
|
for (i = 0; i < ADDR_LEN - 2; i++) { /* copy 3 digits */
|
|
M[d] = M[s] & DIGIT; /* without flags */
|
|
MM (d); /* decr mem addrs */
|
|
MM (s);
|
|
}
|
|
M[d] = (M[s] & DIGIT) | FLAG; /* set flag on last */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Transmit dividend from 'd' to 's' - clear flag on first digit */
|
|
|
|
t_stat xmt_divd (uint32 d, uint32 s)
|
|
{
|
|
uint32 cnt = 0;
|
|
|
|
M[d] = M[s] & DIGIT; /* first w/o flag */
|
|
do {
|
|
MM (d); /* decr mem addrs */
|
|
MM (s);
|
|
M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */
|
|
if (cnt++ >= MEMSIZE) /* (stop runaway) */
|
|
return STOP_FWRAP;
|
|
} while ((M[d] & FLAG) == 0); /* until src flag */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Transmit numeric strip from 's' to 'd' - s is odd */
|
|
|
|
t_stat xmt_tns (uint32 d, uint32 s)
|
|
{
|
|
uint32 cnt = 0;
|
|
uint8 t, z;
|
|
|
|
t = M[s] & DIGIT; /* get units */
|
|
z = M[s - 1] & DIGIT; /* get zone */
|
|
if ((z == 1) || (z == 5) || ((z == 2) && (t == 0))) /* 1x, 5x, 20? */
|
|
M[d] = t | FLAG; /* set flag */
|
|
else M[d] = t; /* else clear flag */
|
|
do {
|
|
MM (d); /* decr mem addrs */
|
|
s = ADDR_S (s, 2);
|
|
t = M[d] & FLAG; /* save dst flag */
|
|
M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */
|
|
if (cnt >= MEMSIZE) /* (stop runaway) */
|
|
return STOP_FWRAP;
|
|
cnt = cnt + 2;
|
|
} while (t == 0); /* until dst flag */
|
|
M[d] = M[d] | FLAG; /* set flag at end */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Transmit numeric fill from 's' to 'd' - d is odd */
|
|
|
|
t_stat xmt_tnf (uint32 d, uint32 s)
|
|
{
|
|
uint32 cnt = 0;
|
|
uint8 t;
|
|
|
|
t = M[s]; /* get 1st digit */
|
|
M[d] = t & DIGIT; /* store */
|
|
M[d - 1] = (t & FLAG)? 5: 7; /* set sign from flag */
|
|
do {
|
|
MM (s); /* decr mem addr */
|
|
d = ADDR_S (d, 2);
|
|
t = M[s]; /* get src digit */
|
|
M[d] = t & DIGIT; /* move to dst, no flag */
|
|
M[d - 1] = 7; /* set zone */
|
|
if (cnt >= MEMSIZE) /* (stop runaway) */
|
|
return STOP_FWRAP;
|
|
cnt = cnt + 2;
|
|
} while ((t & FLAG) == 0); /* until src flag */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Add routine
|
|
|
|
Inputs:
|
|
d = destination field low (P)
|
|
s = source field low (Q)
|
|
sub = TRUE if subtracting
|
|
sto = TRUE if storing
|
|
skp = number of source field flags, beyond sign, to ignore
|
|
Output:
|
|
return = status
|
|
sta = ADD_NOCRY: no carry out, no sign change
|
|
ADD_SCHNG: sign change
|
|
ADD_CARRY: carry out
|
|
|
|
Reference Manual: "When the sum is zero, the sign of the P field
|
|
is retained."
|
|
|
|
Bob Armstrong: record marks are treated by add as though they were zero */
|
|
|
|
t_stat add_field (uint32 d, uint32 s, t_bool sub, t_bool sto, uint32 skp, int32 *sta)
|
|
{
|
|
uint32 cry, src, dst, res, comp, dp, dsv;
|
|
uint32 src_f = 0, cnt = 0, dst_f;
|
|
|
|
*sta = ADD_NOCRY; /* assume no cry */
|
|
dsv = d; /* save dst */
|
|
comp = ((M[d] ^ M[s]) & FLAG) ^ (sub? FLAG: 0); /* set compl flag */
|
|
cry = 0; /* clr carry */
|
|
ind[IN_HP] = ((M[d] & FLAG) == 0); /* set sign from res */
|
|
ind[IN_EZ] = 1; /* assume zero */
|
|
|
|
dst = M[d] & DIGIT; /* 1st digits */
|
|
src = M[s] & DIGIT;
|
|
if (dst == REC_MARK) /* chk for rec mark */
|
|
dst = 0;
|
|
if (src == REC_MARK)
|
|
src = 0;
|
|
if (BAD_DIGIT (dst) || BAD_DIGIT (src)) /* bad digit? */
|
|
return STOP_INVDIG;
|
|
if (comp) /* complement? */
|
|
src = 10 - src;
|
|
res = add_one_digit (dst, src, &cry); /* add */
|
|
if (sto) /* store */
|
|
M[d] = (M[d] & FLAG) | res;
|
|
MM (d); MM (s); /* decr mem addrs */
|
|
do {
|
|
dst = M[d] & DIGIT; /* get dst digit */
|
|
if (dst == REC_MARK)
|
|
dst = 0;
|
|
dst_f = M[d] & FLAG; /* get dst flag */
|
|
if (src_f) /* src done? src = 0 */
|
|
src = 0;
|
|
else {
|
|
src = M[s] & DIGIT; /* get src digit */
|
|
if (src == REC_MARK)
|
|
src = 0;
|
|
if (cnt >= skp) /* get src flag */
|
|
src_f = M[s] & FLAG;
|
|
MM (s); /* decr src addr */
|
|
}
|
|
if (BAD_DIGIT (dst) || BAD_DIGIT (src)) /* bad digit? */
|
|
return STOP_INVDIG;
|
|
if (comp) /* complement? */
|
|
src = 9 - src;
|
|
res = add_one_digit (dst, src, &cry); /* add */
|
|
if (sto) /* store */
|
|
M[d] = dst_f | res;
|
|
MM (d); /* decr dst addr */
|
|
if (cnt++ >= MEMSIZE) /* (stop runaway) */
|
|
return STOP_FWRAP;
|
|
} while (dst_f == 0); /* until dst done */
|
|
if (!src_f) /* !src done? ovf */
|
|
ind[IN_OVF] = 1;
|
|
|
|
/* Because recomplement is done (model 1) with table lookup, the first digit
|
|
must be explicitly 10s complemented, and not 9s complemented with a carry
|
|
in of 1. (Bob Armstrong) */
|
|
|
|
if (comp && !cry && !ind[IN_EZ]) { /* recomp needed? */
|
|
ind[IN_HP] = ind[IN_HP] ^ 1; /* flip indicator */
|
|
if (sto) { /* storing? */
|
|
for (cry = 0, dp = dsv; dp != d; ) { /* rescan */
|
|
dst = M[dp] & DIGIT; /* get dst digit */
|
|
dst = (dp == dsv)? (10 - dst): (9 - dst); /* 10 or 9s comp */
|
|
res = add_one_digit (0, dst, &cry); /* "add" */
|
|
M[dp] = (M[dp] & FLAG) | res; /* store */
|
|
MM (dp); /* decr dst addr */
|
|
}
|
|
M[dsv] = M[dsv] ^ FLAG; /* compl sign */
|
|
}
|
|
*sta = ADD_SIGNC; /* sign changed */
|
|
return SCPE_OK;
|
|
} /* end if recomp */
|
|
if (ind[IN_EZ]) /* res = 0? clr HP */
|
|
ind[IN_HP] = 0;
|
|
if (!comp && cry) /* set status */
|
|
*sta = ADD_CARRY;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Add one digit via table (Model 1) or "hardware" (Model 2) */
|
|
|
|
uint32 add_one_digit (uint32 dst, uint32 src, uint32 *cry)
|
|
{
|
|
uint32 res;
|
|
|
|
if (*cry) /* cry in? incr src */
|
|
src = src + 1;
|
|
if (src >= 10) { /* src > 10? */
|
|
src = src - 10; /* src -= 10 */
|
|
*cry = 1; /* carry out */
|
|
}
|
|
else *cry = 0; /* else no carry */
|
|
if (cpu_unit.flags & IF_MII) /* Model 2? */
|
|
res = sum_table[dst + src]; /* "hardware" */
|
|
else res = M[ADD_TABLE + (dst * 10) + src]; /* table lookup */
|
|
if (res & FLAG) /* carry out? */
|
|
*cry = 1;
|
|
if (res & DIGIT) /* nz? clr ind */
|
|
ind[IN_EZ] = 0;
|
|
return res & DIGIT;
|
|
}
|
|
|
|
/* Multiply routine
|
|
|
|
Inputs:
|
|
mpc = multiplicand address
|
|
mpy = multiplier address
|
|
Outputs:
|
|
return = status
|
|
|
|
Reference manual: "A zero product may have a negative or positive sign,
|
|
depending on the signs of the fields at the P and Q addresses."
|
|
*/
|
|
|
|
t_stat mul_field (uint32 mpc, uint32 mpy)
|
|
{
|
|
int32 i;
|
|
uint32 pro; /* prod pointer */
|
|
uint32 mpyd, mpyf; /* mpy digit, flag */
|
|
uint32 cnt = 0; /* counter */
|
|
uint8 sign; /* final sign */
|
|
t_stat r;
|
|
|
|
PR1 = 1; /* step on PR1 */
|
|
for (i = 0; i < PROD_AREA_LEN; i++) /* clr prod area */
|
|
M[PROD_AREA + i] = 0;
|
|
sign = (M[mpc] & FLAG) ^ (M[mpy] & FLAG); /* get final sign */
|
|
ind[IN_HP] = (sign == 0); /* set indicators */
|
|
ind[IN_EZ] = 1;
|
|
pro = PROD_AREA + PROD_AREA_LEN - 1; /* product ptr */
|
|
|
|
/* Loop on multiplier (mpy) and product (pro) digits */
|
|
|
|
do {
|
|
mpyd = M[mpy] & DIGIT; /* multiplier digit */
|
|
mpyf = (M[mpy] & FLAG) && (cnt != 0); /* last digit flag */
|
|
if (BAD_DIGIT (mpyd)) /* bad? */
|
|
return STOP_INVDIG;
|
|
r = mul_one_digit (mpyd, mpc, pro, mpyf); /* prod += mpc*mpy_dig */
|
|
if (r != SCPE_OK) /* error? */
|
|
return r;
|
|
MM (mpy); /* decr mpyr, prod addrs */
|
|
MM (pro);
|
|
if (cnt++ >= MEMSIZE) /* (stop runaway) */
|
|
return STOP_FWRAP;
|
|
} while ((mpyf == 0) || (cnt <= 1)); /* until mpyr flag */
|
|
|
|
if (ind[IN_EZ]) /* res = 0? clr HP */
|
|
ind[IN_HP] = 0;
|
|
M[PROD_AREA + PROD_AREA_LEN - 1] |= sign; /* set final sign */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Multiply step
|
|
|
|
Inputs:
|
|
mpyd = multiplier digit (tested valid)
|
|
mpcp = multiplicand low address
|
|
prop = product low address
|
|
last = last iteration flag (set flag on high product)
|
|
Outputs:
|
|
prod += multiplicand * multiplier_digit
|
|
return = status
|
|
|
|
The multiply table address is constructed as follows:
|
|
- double the multiplier digit
|
|
- use the 10's digit of the doubled result, + 1, as the 100's digit
|
|
of the table address
|
|
- use the multiplicand digit as the 10's digit of the table address
|
|
- use the unit digit of the doubled result as the unit digit of the
|
|
table address
|
|
EZ indicator is cleared if a non-zero digit is ever generated
|
|
*/
|
|
|
|
t_stat mul_one_digit (uint32 mpyd, uint32 mpcp, uint32 prop, uint32 last)
|
|
{
|
|
uint32 mpta, mptb; /* mult table */
|
|
uint32 mptd; /* mult table digit */
|
|
uint32 mpcd, mpcf; /* mpc digit, flag */
|
|
uint32 prwp; /* prod working ptr */
|
|
uint32 prod; /* product digit */
|
|
uint32 cry; /* carry */
|
|
uint32 mpcc, cryc; /* counters */
|
|
|
|
mptb = MUL_TABLE + ((mpyd <= 4)? (mpyd * 2): /* set mpy table 100's, */
|
|
(((mpyd - 5) * 2) + 100)); /* 1's digits */
|
|
|
|
/* Inner loop on multiplicand (mpcp) and product (prop) digits */
|
|
|
|
mpcc = 0; /* multiplicand ctr */
|
|
do {
|
|
prwp = prop; /* product working ptr */
|
|
mpcd = M[mpcp] & DIGIT; /* multiplicand digit */
|
|
mpcf = M[mpcp] & FLAG; /* multiplicand flag */
|
|
if (BAD_DIGIT (mpcd)) /* bad? */
|
|
return STOP_INVDIG;
|
|
mpta = mptb + (mpcd * 10); /* mpy table 10's digit */
|
|
cry = 0; /* init carry */
|
|
mptd = M[mpta] & DIGIT; /* mpy table digit */
|
|
if (BAD_DIGIT (mptd)) /* bad? */
|
|
return STOP_INVDIG;
|
|
prod = M[prwp] & DIGIT; /* product digit */
|
|
if (BAD_DIGIT (prod)) /* bad? */
|
|
return STOP_INVDIG;
|
|
M[prwp] = add_one_digit (prod, mptd, &cry); /* add mpy tbl to prod */
|
|
MM (prwp); /* decr working ptr */
|
|
mptd = M[mpta + 1] & DIGIT; /* mpy table digit */
|
|
if (BAD_DIGIT (mptd)) /* bad? */
|
|
return STOP_INVDIG;
|
|
prod = M[prwp] & DIGIT; /* product digit */
|
|
if (BAD_DIGIT (prod)) /* bad? */
|
|
return STOP_INVDIG;
|
|
M[prwp] = add_one_digit (prod, mptd, &cry); /* add mpy tbl to prod */
|
|
cryc = 0; /* (stop runaway) */
|
|
while (cry) { /* propagate carry */
|
|
MM (prwp); /* decr working ptr */
|
|
prod = M[prwp] & DIGIT; /* product digit */
|
|
if (BAD_DIGIT (prod)) /* bad? */
|
|
return STOP_INVDIG;
|
|
M[prwp] = add_one_digit (prod, 0, &cry); /* add cry */
|
|
if (cryc++ > MEMSIZE)
|
|
return STOP_FWRAP;
|
|
}
|
|
MM (mpcp); /* decr mpc, prod ptrs */
|
|
MM (prop);
|
|
if (mpcc++ > MEMSIZE)
|
|
return STOP_FWRAP;
|
|
} while ((mpcf == 0) || (mpcc <= 1)); /* until mpcf flag */
|
|
if (last) /* flag high product */
|
|
M[prop] = M[prop] | FLAG;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Divide routine - comments from Geoff Kuenning's 1620 simulator
|
|
|
|
The destination of the divide is given by:
|
|
|
|
100 - <# digits in quotient>
|
|
|
|
Which is more easily calculated as:
|
|
|
|
100 - <# digits in divisor> - <# digits in dividend>
|
|
|
|
The quotient goes into 99 minus the divisor length. The
|
|
remainder goes into 99. The load dividend instruction (above)
|
|
should have specified a P address of 99 minus the size of the
|
|
divisor.
|
|
|
|
Note that this all implies that "dest" points to the *leftmost*
|
|
digit of the dividend.
|
|
|
|
After the division, the assumed decimal point will be as many
|
|
positions to the left as there are digits in the divisor. In
|
|
other words, a 4-digit divisor will produce 4 (assumed) decimal
|
|
places.
|
|
|
|
There are other ways to do these things. In particular, the
|
|
load-dividend instruction doesn't have to specify the above
|
|
formula; if it's done differently, then you don't have to get
|
|
decimal places. This is not well-explained in the books I have.
|
|
|
|
How to divide on a 1620:
|
|
|
|
The dividend is the field at 99:
|
|
|
|
90 = _1234567890
|
|
|
|
The divisor is somewhere else in memory:
|
|
|
|
_03
|
|
|
|
The divide operation specifies the left-most digit of the
|
|
dividend as the place to begin trial subtractions:
|
|
|
|
DM 90,3
|
|
|
|
The loop works as follows:
|
|
|
|
1. Call the left-most digit of the dividend "current_dividend".
|
|
Call the location current_dividend - <divisor_length>
|
|
"quotient_digit".
|
|
2. Clear the flag at current_dividend, and set one at
|
|
quotient_digit.
|
|
|
|
88 = _001234567890, q_d = 88, c_d = 90
|
|
[Not actually done; divisor length controls subtract.]
|
|
3. Subtract the divisor from the field at current-dividend,
|
|
using normal 1620 rules, except that signs are ignored.
|
|
Continue these subtractions until either 10 subtractions
|
|
have been done, or you get a negative result:
|
|
|
|
88 = _00_2234567890, q_d = 88, c_d = 90
|
|
4. If 10 subtractions have been done, set the overflow
|
|
indicator and abort. Otherwise, add the divisor back to
|
|
correct for the oversubtraction:
|
|
|
|
88 = _001234567890, q_d = 88, c_d = 90
|
|
5. Store the (net) number of subtractions in quotient_digit:
|
|
|
|
88 = _001234567890, q_d = 88, c_d = 90
|
|
6. If this is not the first pass, clear the flag at
|
|
quotient_digit. Increment quotient_digit and
|
|
current_dividend, and set a flag at the new
|
|
quotient_digit:
|
|
|
|
88 = _0_01234567890, q_d = 89, c_d = 91
|
|
[If first pass, set a flag at quotient digit.]
|
|
7. If current_dividend is not 100, repeat steps 3 through 7.
|
|
8. Set flags at 99 and quotient_digit - 1 according to the
|
|
rules of algebra: the quotient's sign is the exclusive-or
|
|
of the signs of the divisor and dividend, and the
|
|
remainder has the sign of the dividend:
|
|
|
|
10 / 3 = 3 remainder 1
|
|
10 / -3 = -3 remainder 1
|
|
-10 / 3 = -3 remainder -1
|
|
-10 / -3 = 3 remainder -1
|
|
|
|
This preserves the relationship dd = q * dv + r.
|
|
|
|
Our example continues as follows for steps 3 through 7:
|
|
|
|
3. 88 = _0_00_334567890, q_d = 89, c_d = 91
|
|
4. 88 = _0_00034567890
|
|
5. 88 = _0_40034567890
|
|
6. 88 = _04_0034567890, q_d = 90, c_d = 92
|
|
3. 88 = _04_00_34567890
|
|
4. 88 = _04_0004567890
|
|
5. 88 = _04_1004567890
|
|
6. 88 = _041_004567890, q_d = 91, c_d = 93
|
|
3. 88 = _041_00_2567890
|
|
4. 88 = _041_001567890
|
|
5. 88 = _041_101567890
|
|
6. 88 = _0411_01567890, q_d = 92, c_d = 94
|
|
3. 88 = _0411_00_367890
|
|
4. 88 = _0411_00067890
|
|
5. 88 = _0411_50067890
|
|
6. 88 = _04115_0067890, q_d = 93, c_d = 95
|
|
3. 88 = _04115_00_37890
|
|
4. 88 = _04115_0007890
|
|
5. 88 = _04115_2007890
|
|
6. 88 = _041152_007890, q_d = 94, c_d = 96
|
|
3. 88 = _041152_00_2890
|
|
4. 88 = _041152_001890
|
|
5. 88 = _041152_201890
|
|
6. 88 = _0411522_01890, q_d = 95, c_d = 97
|
|
3. 88 = _0411522_00_390
|
|
4. 88 = _0411522_00090
|
|
5. 88 = _0411522_60090
|
|
6. 88 = _04115226_0090, q_d = 96, c_d = 98
|
|
3. 88 = _04115226_00_30
|
|
4. 88 = _04115226_0000
|
|
5. 88 = _04115226_3000
|
|
6. 88 = _041152263_000, q_d = 97, c_d = 99
|
|
3. 88 = _041152263_00_3
|
|
4. 88 = _041152263_000
|
|
5. 88 = _041152263_000
|
|
6. 88 = _0411522630_00, q_d = 98, c_d = 100
|
|
|
|
In the actual code below, we elide several of these steps in
|
|
various ways for convenience and efficiency.
|
|
|
|
Note that the EZ indicator is NOT valid for divide, because it
|
|
is cleared by any non-zero result in an intermediate add. The
|
|
code maintains its own EZ indicator for the quotient.
|
|
*/
|
|
|
|
t_stat div_field (uint32 dvd, uint32 dvr, int32 *ez)
|
|
{
|
|
uint32 quop, quod, quos; /* quo ptr, dig, sign */
|
|
uint32 dvds; /* dvd sign */
|
|
t_bool first = TRUE; /* first pass */
|
|
t_stat r;
|
|
|
|
dvds = (M[PROD_AREA + PROD_AREA_LEN - 1]) & FLAG; /* dividend sign */
|
|
quos = dvds ^ (M[dvr] & FLAG); /* quotient sign */
|
|
ind[IN_HP] = (quos == 0); /* set indicators */
|
|
*ez = 1;
|
|
|
|
/* Loop on current dividend, high order digit at dvd */
|
|
|
|
do {
|
|
r = div_one_digit (dvd, dvr, 10, &quod, &quop); /* dev quo digit */
|
|
if (r != SCPE_OK) /* error? */
|
|
return r;
|
|
|
|
/* Store quotient digit and advance current dividend pointer */
|
|
|
|
if (first) { /* first pass? */
|
|
if (quod >= 10) { /* overflow? */
|
|
ind[IN_OVF] = 1; /* set indicator */
|
|
return STOP_OVERFL; /* stop */
|
|
}
|
|
M[quop] = FLAG | quod; /* set flag on quo */
|
|
first = FALSE;
|
|
}
|
|
else M[quop] = quod; /* store quo digit */
|
|
if (quod) /* if nz, clr ind */
|
|
*ez = 0;
|
|
PP (dvd); /* incr dvd ptr */
|
|
} while (dvd != (PROD_AREA + PROD_AREA_LEN)); /* until end prod */
|
|
|
|
/* Division done. Set signs of quo, rem, set flag on high order remainder */
|
|
|
|
if (*ez) /* res = 0? clr HP */
|
|
ind[IN_HP] = 0;
|
|
M[PROD_AREA + PROD_AREA_LEN - 1] |= dvds; /* remainder sign */
|
|
M[quop] = M[quop] | quos; /* quotient sign */
|
|
PP (quop); /* high remainder */
|
|
M[quop] = M[quop] | FLAG; /* set flag */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Divide step
|
|
|
|
Inputs:
|
|
dvd = current dividend address (high digit)
|
|
dvr = divisor address (low digit)
|
|
max = max number of iterations before overflow
|
|
&quod = address to store quotient digit
|
|
&quop = address to store quotient pointer (can be NULL)
|
|
Outputs:
|
|
return = status
|
|
|
|
Divide step calculates a quotient digit by repeatedly subtracting the
|
|
divisor from the current dividend. The divisor's length controls the
|
|
subtraction; dividend flags are ignored.
|
|
*/
|
|
|
|
t_stat div_one_digit (uint32 dvd, uint32 dvr, uint32 max,
|
|
uint32 *quod, uint32 *quop)
|
|
{
|
|
uint32 dvrp, dvrd, dvrf; /* dvr ptr, dig, flag */
|
|
uint32 dvdp, dvdd; /* dvd ptr, dig */
|
|
uint32 qd, cry; /* quo dig, carry */
|
|
uint32 cnt;
|
|
|
|
for (qd = 0; qd < max; qd++) { /* devel quo dig */
|
|
dvrp = dvr; /* divisor ptr */
|
|
dvdp = dvd; /* dividend ptr */
|
|
cnt = 0;
|
|
cry = 1; /* carry in = 1 */
|
|
do { /* sub dvr fm dvd */
|
|
dvdd = M[dvdp] & DIGIT; /* dividend digit */
|
|
if (BAD_DIGIT (dvdd)) /* bad? */
|
|
return STOP_INVDIG;
|
|
dvrd = M[dvrp] & DIGIT; /* divisor digit */
|
|
dvrf = M[dvrp] & FLAG; /* divisor flag */
|
|
if (BAD_DIGIT (dvrd)) /* bad? */
|
|
return STOP_INVDIG;
|
|
M[dvdp] = add_one_digit (dvdd, 9 - dvrd, &cry); /* sub */
|
|
MM (dvdp); /* decr ptrs */
|
|
MM (dvrp);
|
|
if (cnt++ >= MEMSIZE) /* (stop runaway) */
|
|
return STOP_FWRAP;
|
|
} while ((dvrf == 0) || (cnt <= 1)); /* until dvr flag */
|
|
if (!cry) { /* !cry = borrow */
|
|
dvdd = M[dvdp] & DIGIT; /* borrow digit */
|
|
if (BAD_DIGIT (dvdd)) /* bad? */
|
|
return STOP_INVDIG;
|
|
M[dvdp] = add_one_digit (dvdd, 9, &cry); /* sub */
|
|
}
|
|
if (!cry) /* !cry = negative */
|
|
break;
|
|
}
|
|
|
|
/* Add back the divisor to correct for the negative result */
|
|
|
|
dvrp = dvr; /* divisor ptr */
|
|
dvdp = dvd; /* dividend ptr */
|
|
cnt = 0;
|
|
cry = 0; /* carry in = 0 */
|
|
do {
|
|
dvdd = M[dvdp] & DIGIT; /* dividend digit */
|
|
dvrd = M[dvrp] & DIGIT; /* divisor digit */
|
|
dvrf = M[dvrp] & FLAG; /* divisor flag */
|
|
M[dvdp] = add_one_digit (dvdd, dvrd, &cry); /* add */
|
|
MM (dvdp); /* decr ptrs */
|
|
MM (dvrp);
|
|
cnt++;
|
|
} while ((dvrf == 0) || (cnt <= 1)); /* until dvr flag */
|
|
if (cry) { /* carry out? */
|
|
dvdd = M[dvdp] & DIGIT; /* borrow digit */
|
|
M[dvdp] = add_one_digit (dvdd, 0, &cry); /* add */
|
|
}
|
|
if (quop != NULL) /* set quo addr */
|
|
*quop = dvdp;
|
|
*quod = qd; /* set quo digit */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Logical operation routines (and, or, xor, complement)
|
|
|
|
Inputs:
|
|
d = destination address
|
|
s = source address
|
|
Output:
|
|
return = status
|
|
|
|
Destination flags are preserved; EZ reflects the result.
|
|
COM does not obey normal field length restrictions.
|
|
*/
|
|
|
|
t_stat or_field (uint32 d, uint32 s)
|
|
{
|
|
uint32 cnt = 0;
|
|
int32 t;
|
|
|
|
ind[IN_EZ] = 1; /* assume result zero */
|
|
do {
|
|
t = M[s]; /* get src */
|
|
M[d] = (M[d] & FLAG) | ((M[d] | t) & 07); /* OR src to dst */
|
|
if (M[d] & DIGIT) /* nz dig? clr ind */
|
|
ind[IN_EZ] = 0;
|
|
MM (d); /* decr pointers */
|
|
MM (s);
|
|
if (cnt++ >= MEMSIZE) /* (stop runaway) */
|
|
return STOP_FWRAP;
|
|
} while (((t & FLAG) == 0) || (cnt <= 1)); /* until src flag */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat and_field (uint32 d, uint32 s)
|
|
{
|
|
uint32 cnt = 0;
|
|
int32 t;
|
|
|
|
ind[IN_EZ] = 1; /* assume result zero */
|
|
do {
|
|
t = M[s]; /* get src */
|
|
M[d] = (M[d] & FLAG) | ((M[d] & t) & 07); /* AND src to dst */
|
|
if (M[d] & DIGIT) /* nz dig? clr ind */
|
|
ind[IN_EZ] = 0;
|
|
MM (d); /* decr pointers */
|
|
MM (s);
|
|
if (cnt++ >= MEMSIZE) /* (stop runaway) */
|
|
return STOP_FWRAP;
|
|
} while (((t & FLAG) == 0) || (cnt <= 1)); /* until src flag */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat xor_field (uint32 d, uint32 s)
|
|
{
|
|
uint32 cnt = 0;
|
|
int32 t;
|
|
|
|
ind[IN_EZ] = 1; /* assume result zero */
|
|
do {
|
|
t = M[s]; /* get src */
|
|
M[d] = (M[d] & FLAG) | ((M[d] ^ t) & 07); /* XOR src to dst */
|
|
if (M[d] & DIGIT) /* nz dig? clr ind */
|
|
ind[IN_EZ] = 0;
|
|
MM (d); /* decr pointers */
|
|
MM (s);
|
|
if (cnt++ >= MEMSIZE) /* (stop runaway) */
|
|
return STOP_FWRAP;
|
|
} while (((t & FLAG) == 0) || (cnt <= 1)); /* until src flag */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat com_field (uint32 d, uint32 s)
|
|
{
|
|
uint32 cnt = 0;
|
|
int32 t;
|
|
|
|
ind[IN_EZ] = 1; /* assume result zero */
|
|
do {
|
|
t = M[s]; /* get src */
|
|
M[d] = (t & FLAG) | ((t ^ 07) & 07); /* comp src to dst */
|
|
if (M[d] & DIGIT) /* nz dig? clr ind */
|
|
ind[IN_EZ] = 0;
|
|
MM (d); /* decr pointers */
|
|
MM (s);
|
|
if (cnt++ >= MEMSIZE) /* (stop runaway) */
|
|
return STOP_FWRAP;
|
|
} while ((t & FLAG) == 0); /* until src flag */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Octal to decimal
|
|
|
|
Inputs:
|
|
tbl = conversion table address (low digit)
|
|
s = source address
|
|
Outputs:
|
|
product area = converted source
|
|
result = status
|
|
|
|
OTD is a cousin of multiply. The octal digits in the source are
|
|
multiplied by successive values in the conversion table, and the
|
|
results are accumulated in the product area. Although the manual
|
|
does not say, this code assumes that EZ and HP are affected.
|
|
*/
|
|
|
|
t_stat oct_to_dec (uint32 tbl, uint32 s)
|
|
{
|
|
uint32 cnt = 0, tblc;
|
|
uint32 i, sd, sf, tf, sign;
|
|
t_stat r;
|
|
|
|
for (i = 0; i < PROD_AREA_LEN; i++) /* clr prod area */
|
|
M[PROD_AREA + i] = 0;
|
|
sign = M[s] & FLAG; /* save sign */
|
|
ind[IN_EZ] = 1; /* set indicators */
|
|
ind[IN_HP] = (sign == 0);
|
|
do {
|
|
sd = M[s] & DIGIT; /* src digit */
|
|
sf = M[s] & FLAG; /* src flag */
|
|
r = mul_one_digit (sd, tbl, PROD_AREA + PROD_AREA_LEN - 1, sf);
|
|
if (r != SCPE_OK) /* err? */
|
|
return r;
|
|
MM (s); /* decr src addr */
|
|
MM (tbl); /* skip 1st tbl dig */
|
|
tblc = 0; /* count */
|
|
do {
|
|
tf = M[tbl] & FLAG; /* get next */
|
|
MM (tbl); /* decr ptr */
|
|
if (tblc++ > MEMSIZE)
|
|
return STOP_FWRAP;
|
|
} while (tf == 0); /* until flag */
|
|
if (cnt++ >= MEMSIZE) /* (stop runaway) */
|
|
return STOP_FWRAP;
|
|
} while (sf == 0);
|
|
if (ind[IN_EZ]) /* res = 0? clr HP */
|
|
ind[IN_HP] = 0;
|
|
M[PROD_AREA + PROD_AREA_LEN - 1] |= sign; /* set sign */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Decimal to octal
|
|
|
|
Inputs:
|
|
d = destination address
|
|
tbl = conversion table address (low digit of highest power)
|
|
&ez = address of soft EZ indicator
|
|
product area = field to convert
|
|
Outputs:
|
|
return = status
|
|
|
|
DTO is a cousin to divide. The number in the product area is repeatedly
|
|
divided by successive values in the conversion table, and the quotient
|
|
digits are stored in the destination. Although the manual does not say,
|
|
this code assumes that EZ and HP are affected.
|
|
*/
|
|
|
|
t_stat dec_to_oct (uint32 d, uint32 tbl, int32 *ez)
|
|
{
|
|
uint32 sign, octd, t;
|
|
t_bool first = TRUE;
|
|
uint32 ctr = 0;
|
|
t_stat r;
|
|
|
|
sign = M[PROD_AREA + PROD_AREA_LEN - 1] & FLAG; /* input sign */
|
|
*ez = 1; /* set indicators */
|
|
ind[IN_HP] = (sign == 0);
|
|
for ( ;; ) {
|
|
r = div_one_digit (PROD_AREA + PROD_AREA_LEN - 1, /* divide */
|
|
tbl, 8, &octd, NULL);
|
|
if (r != SCPE_OK) /* error? */
|
|
return r;
|
|
if (first) { /* first pass? */
|
|
if (octd >= 8) { /* overflow? */
|
|
ind[IN_OVF] = 1; /* set indicator */
|
|
return SCPE_OK; /* stop */
|
|
}
|
|
M[d] = FLAG | octd; /* set flag on quo */
|
|
first = FALSE;
|
|
}
|
|
else M[d] = octd; /* store quo digit */
|
|
if (octd) /* if nz, clr ind */
|
|
*ez = 0;
|
|
PP (tbl); /* incr tbl addr */
|
|
if ((M[tbl] & REC_MARK) == REC_MARK) /* record mark? */
|
|
break;
|
|
PP (tbl); /* skip flag */
|
|
if ((M[tbl] & REC_MARK) == REC_MARK) /* record mark? */
|
|
break;
|
|
do { /* look for F, rec mk */
|
|
PP (tbl);
|
|
t = M[tbl];
|
|
} while (((t & FLAG) == 0) && ((t & REC_MARK) != REC_MARK));
|
|
MM (tbl); /* step back one */
|
|
PP (d); /* incr quo addr */
|
|
if (ctr++ > MEMSIZE) /* (stop runaway) */
|
|
return STOP_FWRAP;
|
|
}
|
|
if (*ez) /* res = 0? clr HP */
|
|
ind[IN_HP] = 0;
|
|
M[d] = M[d] | sign; /* set result sign */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Reset routine */
|
|
|
|
t_stat cpu_reset (DEVICE *dptr)
|
|
{
|
|
int32 i;
|
|
static t_bool one_time = TRUE;
|
|
|
|
PR1 = IR2 = 1; /* invalidate PR1,IR2 */
|
|
ind[0] = 0;
|
|
for (i = IN_SW4 + 1; i < NUM_IND; i++) /* init indicators */
|
|
ind[i] = 0;
|
|
if (cpu_unit.flags & IF_IA) /* indirect enabled? */
|
|
iae = 1;
|
|
else iae = 0;
|
|
idxe = idxb = 0; /* indexing off */
|
|
pcq_r = find_reg ("PCQ", NULL, dptr); /* init old PC queue */
|
|
if (pcq_r)
|
|
pcq_r->qptr = 0;
|
|
else return SCPE_IERR;
|
|
sim_brk_types = sim_brk_dflt = SWMASK ('E'); /* init breakpoints */
|
|
upd_ind (); /* update indicators */
|
|
if (one_time) /* set default tables */
|
|
cpu_set_table (&cpu_unit, 1, NULL, NULL);
|
|
one_time = FALSE;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Memory examine */
|
|
|
|
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
|
|
{
|
|
if (addr >= MEMSIZE)
|
|
return SCPE_NXM;
|
|
if (vptr != NULL)
|
|
*vptr = M[addr] & (FLAG | DIGIT);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Memory deposit */
|
|
|
|
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
|
|
{
|
|
if (addr >= MEMSIZE)
|
|
return SCPE_NXM;
|
|
M[addr] = val & (FLAG | DIGIT);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Memory size change */
|
|
|
|
t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
|
|
{
|
|
int32 mc = 0;
|
|
uint32 i;
|
|
|
|
if ((val <= 0) || (val > MAXMEMSIZE) || ((val % 1000) != 0))
|
|
return SCPE_ARG;
|
|
for (i = val; i < MEMSIZE; i++)
|
|
mc = mc | M[i];
|
|
if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))
|
|
return SCPE_OK;
|
|
MEMSIZE = val;
|
|
for (i = MEMSIZE; i < MAXMEMSIZE; i++)
|
|
M[i] = 0;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Model change */
|
|
|
|
t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc)
|
|
{
|
|
if (val)
|
|
cpu_unit.flags = (cpu_unit.flags & (UNIT_SCP | UNIT_BCD | MII_OPT)) |
|
|
IF_DIV | IF_IA | IF_EDT;
|
|
else cpu_unit.flags = cpu_unit.flags & (UNIT_SCP | UNIT_BCD | MI_OPT);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Set/clear Model 1 option */
|
|
|
|
t_stat cpu_set_opt1 (UNIT *uptr, int32 val, char *cptr, void *desc)
|
|
{
|
|
if (cpu_unit.flags & IF_MII) {
|
|
sim_printf ("Feature is standard on 1620 Model 2\n");
|
|
return SCPE_NOFNC;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Set/clear Model 2 option */
|
|
|
|
t_stat cpu_set_opt2 (UNIT *uptr, int32 val, char *cptr, void *desc)
|
|
{
|
|
if (!(cpu_unit.flags & IF_MII)) {
|
|
sim_printf ("Feature is not available on 1620 Model 1\n");
|
|
return SCPE_NOFNC;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Front panel save */
|
|
|
|
t_stat cpu_set_save (UNIT *uptr, int32 val, char *cptr, void *desc)
|
|
{
|
|
if (saved_PC & 1)
|
|
return SCPE_NOFNC;
|
|
PR1 = saved_PC;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Set standard add/multiply tables */
|
|
|
|
t_stat cpu_set_table (UNIT *uptr, int32 val, char *cptr, void *desc)
|
|
{
|
|
int32 i;
|
|
|
|
for (i = 0; i < MUL_TABLE_LEN; i++) /* set mul table */
|
|
M[MUL_TABLE + i] = std_mul_table[i];
|
|
if (((cpu_unit.flags & IF_MII) == 0) || val) { /* set add table */
|
|
for (i = 0; i < ADD_TABLE_LEN; i++)
|
|
M[ADD_TABLE + i] = std_add_table[i];
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Set history */
|
|
|
|
t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc)
|
|
{
|
|
int32 i, lnt;
|
|
t_stat r;
|
|
|
|
if (cptr == NULL) {
|
|
for (i = 0; i < hst_lnt; i++)
|
|
hst[i].vld = 0;
|
|
hst_p = 0;
|
|
return SCPE_OK;
|
|
}
|
|
lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r);
|
|
if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN)))
|
|
return SCPE_ARG;
|
|
hst_p = 0;
|
|
if (hst_lnt) {
|
|
free (hst);
|
|
hst_lnt = 0;
|
|
hst = NULL;
|
|
}
|
|
if (lnt) {
|
|
hst = (InstHistory *) calloc (lnt, sizeof (InstHistory));
|
|
if (hst == NULL)
|
|
return SCPE_MEM;
|
|
hst_lnt = lnt;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Show history */
|
|
|
|
t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc)
|
|
{
|
|
int32 i, k, di, lnt;
|
|
char *cptr = (char *) desc;
|
|
t_value sim_eval[INST_LEN];
|
|
t_stat r;
|
|
InstHistory *h;
|
|
extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val,
|
|
UNIT *uptr, int32 sw);
|
|
|
|
if (hst_lnt == 0) /* enabled? */
|
|
return SCPE_NOFNC;
|
|
if (cptr) {
|
|
lnt = (int32) get_uint (cptr, 10, hst_lnt, &r);
|
|
if ((r != SCPE_OK) || (lnt == 0))
|
|
return SCPE_ARG;
|
|
}
|
|
else lnt = hst_lnt;
|
|
di = hst_p - lnt; /* work forward */
|
|
if (di < 0)
|
|
di = di + hst_lnt;
|
|
fprintf (st, "PC IR\n\n");
|
|
for (k = 0; k < lnt; k++) { /* print specified */
|
|
h = &hst[(++di) % hst_lnt]; /* entry pointer */
|
|
if (h->vld) { /* instruction? */
|
|
fprintf (st, "%05d ", h->pc);
|
|
for (i = 0; i < INST_LEN; i++)
|
|
sim_eval[i] = h->inst[i];
|
|
if ((fprint_sym (st, h->pc, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) {
|
|
fprintf (st, "(undefined)");
|
|
for (i = 0; i < INST_LEN; i++)
|
|
fprintf (st, "%02X", h->inst[i]);
|
|
}
|
|
fputc ('\n', st); /* end line */
|
|
} /* end else instruction */
|
|
} /* end for */
|
|
return SCPE_OK;
|
|
}
|