blob: 654de0c9313ebdeed72a71c5d9efdf69fc582c92 [file] [log] [blame] [raw]
/* i7070_cpu.c: IBM 7070 CPU simulator
Copyright (c) 2005-2016, Richard Cornwell
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
RICHARD CORNWELL 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.
cpu IBM 7070 central processor
The IBM 7070 was introduced in June 1960, as a replacement to the IBM 650.
It had core memory up to 10,000 10 digit words.
The 7072 was introduced November 1962 and the 7074 on November 1961.
The 7074 is a faster version of the 7070 with the addition of memory up
to 40,000 10 digit words. The first 100 memory locations can be used as
index registers. Most memory reference instructions allow for a field
of digits to be selected to operate on and not modify the rest.
The 7070 is a decimal machine with each word consisting of 10 digits
plus a sign. The sign can be plus, minus or alpha. Alpha data is stored
5 characters to a word (2 digits per character).
The system state for the IBM 7070 is:
AC1<0:10> AC1 register
AC2<0:10> AC2 register
AC3<0:10> AC3 register
IC<0:5> program counter
The 7070 had one basic instuction format.
<sign> 01 23 45 6789
<sign> and 01 are opcode. Alpha is not allowed.
23 specify an index register from memory location 01 to 99.
or if extended addressing is enabled 10-99. 01-09 specify
high order digit of address.
45 encode either a field, or operands depending on instruction.
6789 are address in memory. If index is specified they are
added to fields <sign> [1]2345 of memory addressed by field 23.
Accumulators may be accessed 9991/2/3 or 99991/2/3.
Signs are stored as 9 for plus.
6 for minus.
3 for alpha.
Options supported are Timer, Extended addressing and Floating point.
*/
#include "i7070_defs.h"
#include "sim_timer.h"
#define UNIT_V_MSIZE (UNIT_V_UF + 0)
#define UNIT_MSIZE (7 << UNIT_V_MSIZE)
#define UNIT_V_CPUMODEL (UNIT_V_UF + 4)
#define UNIT_MODEL (0x01 << UNIT_V_CPUMODEL)
#define CPU_MODEL ((cpu_unit.flags >> UNIT_V_CPUMODEL) & 0x01)
#define MODEL(x) (x << UNIT_V_CPUMODEL)
#define MEMAMOUNT(x) (x << UNIT_V_MSIZE)
#define OPTION_FLOAT (1 << (UNIT_V_CPUMODEL + 1))
#define OPTION_TIMER (1 << (UNIT_V_CPUMODEL + 2))
#define OPTION_EXTEND (1 << (UNIT_V_CPUMODEL + 3))
#define TMR_RTC 1
#define HIST_NOEA 0x10000000
#define HIST_NOAFT 0x20000000
#define HIST_NOBEF 0x40000000
#define HIST_PC 0x10000
#define HIST_MIN 64
#define HIST_MAX 65536
struct InstHistory
{
t_int64 op;
uint32 ic;
uint32 ea;
t_int64 before;
t_int64 after;
};
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_size(UNIT * uptr, int32 val, CONST char *cptr,
void *desc);
t_stat cpu_show_hist(FILE * st, UNIT * uptr, int32 val,
CONST void *desc);
t_stat cpu_set_hist(UNIT * uptr, int32 val, CONST char *cptr,
void *desc);
t_stat cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *cpu_description (DEVICE *dptr);
void mem_init(void);
/* Interval timer option */
t_stat rtc_srv(UNIT * uptr);
t_stat rtc_reset(DEVICE * dptr);
t_uint64 M[MAXMEMSIZE] = { PSIGN }; /* memory */
t_uint64 AC[4]; /* registers */
t_uint64 inds; /* Error indicators */
t_uint64 diaglatch; /* Diagnostic latches */
uint16 timer; /* Timer register */
uint32 IC; /* program counter */
uint16 timer_clock; /* Timer clock */
uint8 SW = 0; /* Sense switch */
uint8 emode; /* Extended address mode */
uint16 pri_latchs[10]; /* Priority latchs */
uint32 pri_mask = 0xFFFFFF; /* Priority masks */
uint8 pri_enb = 1; /* Enable priority procs */
uint8 lpr_chan9[NUM_CHAN]; /* Line printer on channel 9 */
int cycle_time = 20; /* Cycle time of 12us */
/* History information */
int32 hst_p = 0; /* History pointer */
int32 hst_lnt = 0; /* History length */
struct InstHistory *hst = NULL; /* History stack */
void (*sim_vm_init) (void) = &mem_init;
/* CPU data structures
cpu_dev CPU device descriptor
cpu_unit CPU unit descriptor
cpu_reg CPU register list
cpu_mod CPU modifiers list
*/
UNIT cpu_unit =
{ UDATA(&rtc_srv, OPTION_FLOAT|MEMAMOUNT(1)|MODEL(0x0), 10000), 10 };
REG cpu_reg[] = {
{DRDATA(IC, IC, 20), REG_FIT},
{HRDATA(AC1, AC[1], 44), REG_VMIO|REG_FIT},
{HRDATA(AC2, AC[2], 44), REG_VMIO|REG_FIT},
{HRDATA(AC3, AC[3], 44), REG_VMIO|REG_FIT},
{HRDATA(IND, inds, 44), REG_VMIO|REG_FIT},
{ORDATA(SW, SW, 4), REG_FIT},
{FLDATA(SW1, SW, 0), REG_FIT},
{FLDATA(SW2, SW, 1), REG_FIT},
{FLDATA(SW3, SW, 2), REG_FIT},
{FLDATA(SW4, SW, 3), REG_FIT},
{NULL}
};
MTAB cpu_mod[] = {
{UNIT_MODEL, MODEL(0x0), "7070", "7070", NULL, NULL, NULL},
{UNIT_MODEL, MODEL(0x1), "7074", "7074", NULL, NULL, NULL},
{UNIT_MSIZE, MEMAMOUNT(0), "5K", "5K", &cpu_set_size},
{UNIT_MSIZE, MEMAMOUNT(1), "10K", "10K", &cpu_set_size},
{UNIT_MSIZE, MEMAMOUNT(2), "15K", "15K", &cpu_set_size},
{UNIT_MSIZE, MEMAMOUNT(3), "20K", "20K", &cpu_set_size},
{UNIT_MSIZE, MEMAMOUNT(4), "25K", "25K", &cpu_set_size},
{UNIT_MSIZE, MEMAMOUNT(5), "30K", "30K", &cpu_set_size},
{OPTION_FLOAT, 0, NULL, "NOFLOAT", NULL, NULL, NULL},
{OPTION_FLOAT, OPTION_FLOAT, "FLOAT", "FLOAT", NULL, NULL, NULL},
{OPTION_EXTEND, 0, NULL, "NOEXTEND", NULL, NULL, NULL},
{OPTION_EXTEND, OPTION_EXTEND, "EXTEND", "EXTEND", NULL, NULL, NULL},
{OPTION_TIMER, 0, NULL, "NOCLOCK", NULL, NULL, NULL},
{OPTION_TIMER, OPTION_TIMER, "CLOCK", "CLOCK", NULL, NULL, NULL},
{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, 44,
&cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL,
NULL, DEV_DEBUG, 0, dev_debug,
NULL, NULL, &cpu_help, NULL, NULL, &cpu_description
};
uint32 dscale[4][16] = {
{0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 0,0,0,0,0,0},
{0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 0,0,0,0,0,0},
{0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 0,0,0,0,0,0},
{0, 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000,
0,0,0,0,0,0}
};
t_uint64 fdmask[11] = {
0x0000000000LL,
0xF000000000LL, 0xFF00000000LL, 0xFFF0000000LL, 0xFFFF000000LL,
0xFFFFF00000LL, 0xFFFFFF0000LL, 0xFFFFFFF000LL, 0xFFFFFFFF00LL,
0xFFFFFFFFF0LL, 0xFFFFFFFFFFLL
};
t_uint64 rdmask[11] = {
0xFFFFFFFFFFLL, 0x0FFFFFFFFFLL, 0x00FFFFFFFFLL, 0x000FFFFFFFLL,
0x0000FFFFFFLL, 0x00000FFFFFLL, 0x000000FFFFLL, 0x0000000FFFLL,
0x00000000FFLL, 0x000000000FLL, 0x0LL
};
t_uint64 ldmask[11] = {
0x0LL, 0xFLL, 0xFFLL, 0xFFFLL, 0xFFFFLL, 0xFFFFFLL, 0xFFFFFFLL, 0xFFFFFFFLL,
0xFFFFFFFFLL, 0xFFFFFFFFFLL, 0xFFFFFFFFFFLL
};
t_uint64 dmask[11] = {
0x0LL, 0xFLL, 0xF0LL, 0xF00LL, 0xF000LL, 0xF0000LL,
0xF00000LL, 0xF000000LL, 0xF0000000LL, 0xF00000000LL, 0xF000000000LL
};
#define gdigit(w, d) (((w) >> ((d) * 4)) & 0xF)
#define sdigit(d, v) ((((t_uint64)v) & 0xFLL) << ((d) * 4))
#define mdigit(d) (0xFLL << ((d) * 4))
t_uint64 ReadP(uint32 addr) {
sim_interval -= (CPU_MODEL == 0x0)? 2: 1;
if (emode) {
if (addr > MAXMEMSIZE) {
switch(addr) {
case 99991: return AC[1];
case 99992: return AC[2];
case 99993: return AC[3];
default: return 0LL;
}
}
} else {
if (addr >= 9990) {
switch(addr) {
case 9991: return AC[1];
case 9992: return AC[2];
case 9993: return AC[3];
default: return 0LL;
}
}
}
if (addr < MEMSIZE && addr < MAXMEMSIZE)
return M[addr];
return 0LL;
}
void WriteP(uint32 addr, t_uint64 value) {
sim_interval -= (CPU_MODEL == 0x0)? 2: 1;
if (emode) {
if (addr > MAXMEMSIZE) {
switch(addr) {
case 99991: AC[1] = value; return;
case 99992: AC[2] = value; return;
case 99993: AC[3] = value; return;
}
}
} else {
if (addr >= 9990) {
switch(addr) {
case 9991: AC[1] = value; return;
case 9992: AC[2] = value; return;
case 9993: AC[3] = value; return;
default: return;
}
}
}
if (addr < MEMSIZE && addr < MAXMEMSIZE)
M[addr] = value;
}
t_stat
sim_instr(void)
{
t_stat reason;
t_uint64 temp;
t_uint64 MBR;
uint16 opcode = 0;
uint32 MA = 0;
uint32 utmp; /* Unsigned temp */
int tmp; /* Signed temp */
uint8 f = 0;
uint8 stopnext;
uint8 IX = 0;
uint8 f1 = 0;
uint8 f2 = 0;
uint8 op2 = 0;
int iowait = 0; /* Wait for IO to start */
int chwait = 0; /* Wait for channel to be inactive */
int sign;
int instr_count = 0; /* Number of instructions to execute */
if (sim_step != 0) {
instr_count = sim_step;
sim_cancel_step();
}
reason = 0;
iowait = 0;
stopnext = 0;
while (reason == 0) { /* loop until halted */
/* If doing fast I/O don't sit in idle loop */
if (iowait == 0 && chwait == 0 && stopnext)
return SCPE_STEP;
if (chwait != 0 && chan_active(chwait))
sim_interval = 0;
else
chwait = 0;
if (iowait)
sim_interval = 0;
if (sim_interval <= 0) { /* event queue? */
reason = sim_process_event();
if (reason != SCPE_OK) {
if (reason == SCPE_STEP && iowait)
stopnext = 1;
else
break; /* process */
}
}
/* Only check for break points during actual fetch */
if (iowait == 0 && chwait == 0
&& sim_brk_summ && sim_brk_test(IC, SWMASK('E'))) {
reason = STOP_IBKPT;
break;
}
/* Don't do interupt if waiting on IO or channel */
if (pri_enb && iowait == 0 && chwait == 0) {
/* Check if we have to process one */
if ((tmp = scan_irq()) != 0) {
/* Save instruction counter */
if (CPU_MODEL == 0x1) /* On 7074 location 97 modified */
MBR = M[97];
else
MBR = 0; /* On 7070/2 location cleared */
upd_idx(&MBR, IC);
MBR &= DMASK;
MBR |= PSIGN;
M[97] = MBR;
/* Save indicators */
M[100] = inds;
inds = PSIGN;
pri_enb = 0;
IC = tmp;
sim_debug(DEBUG_TRAP, &cpu_dev, "IRQ= %d %d\n\r", IC, tmp);
}
}
/* Main instruction fetch/decode loop */
if (chwait == 0) {
/* Split out current instruction */
sim_interval -= 24; /* count down */
/* If waiting for IO don't bump IC or create history */
if (iowait)
/* Don't do a fetch if waiting on I/O to be ready */
iowait = 0;
else {
MBR = ReadP(IC);
if (hst_lnt) { /* history enabled? */
hst_p = (hst_p + 1); /* next entry */
if (hst_p >= hst_lnt)
hst_p = 0;
hst[hst_p].ic = (IC) | HIST_PC;
hst[hst_p].op = MBR;
hst[hst_p].after = 0;
}
IC++;
MA = MBR & 0xf; MBR >>= 4;
MA += dscale[0][MBR & 0xf]; MBR >>= 4;
MA += dscale[1][MBR & 0xf]; MBR >>= 4;
MA += dscale[2][MBR & 0xf]; MBR >>= 4;
f2 = MBR & 0xf; MBR >>= 4;
f1 = MBR & 0xf; MBR >>= 4;
IX = MBR & 0xf; MBR >>= 4;
IX += dscale[0][MBR & 0xf]; MBR >>= 4;
opcode = MBR & 0xff;
op2 = (opcode >> 4) & 0xf;
if ((MBR & (SMASK >> 32)) == (MSIGN >> 32))
opcode |= 0x100;
/* Check if extended addressing mode */
if (emode && IX < 10) {
MA += dscale[3][IX];
IX = 0;
}
/* Handle indexing */
if (IX > 0) {
sim_interval -= (CPU_MODEL == 0x0)? 10: 1;
MBR = M[IX];
utmp = dec_bin_idx(MBR);
if ((MBR & SMASK) == MSIGN) { /* Change sign */
if (MA < utmp) {
if (emode)
MA = 100000 - MA - utmp;
else
MA = 10000 - MA - utmp;
} else
MA -= utmp;
} else if ((MBR & SMASK) == PSIGN) {
MA += utmp;
if (emode) {
if (MA > 100000)
MA -= 100000;
} else {
if (MA > 10000)
MA -= 10000;
}
} else {
reason = STOP_INDEX;
break;
}
}
IX = f2 + dscale[0][f1];
/* Fetch data */
MBR = ReadP(MA);
if (hst_lnt) { /* history enabled? */
hst[hst_p].ea = MA;
hst[hst_p].before = MBR;
}
}
switch(opcode) {
/* Zero add absolute */
case OP_ZAA:
MBR &= DMASK;
MBR |= PSIGN;
goto set_ac;
/* Zero sub absolute */
case OP_ZSA:
MBR &= DMASK;
MBR |= MSIGN;
goto set_ac;
/* Load AC negitive from memory */
case OP_ZS1: case OP_ZS2: case OP_ZS3:
if ((MBR & SMASK) != ASIGN)
MBR ^= SMASK;
/* Zero add, load AC from memory */
case OP_ZA1: case OP_ZA2: case OP_ZA3:
set_ac:
MBR = (MBR & SMASK)|((rdmask[f1] & MBR) >> ((9 - f2) * 4));
AC[op2] = MBR;
sim_interval -= (CPU_MODEL == 0x0)? (f2 - f1)/3: 1;
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = AC[op2];
}
break;
/* AC - memory */
case OP_S1: case OP_S2: case OP_S3:
sign = (MBR & SMASK) != MSIGN;
if ((MBR & SMASK) == ASIGN)
sign |= 8;
goto add;
/* AC - |memory| */
case OP_SA:
/* AC + |memory| */
sign = 1;
goto add;
case OP_AA:
sign = 0;
goto add;
/* AC + memory */
case OP_A1: case OP_A2: case OP_A3:
/* Get field. */
sign = (MBR & SMASK) == MSIGN;
if ((MBR & SMASK) == ASIGN)
sign |= 8;
add:
if ((AC[op2] & SMASK) == ASIGN)
sign |= 8;
MBR = (rdmask[f1] & MBR) >> ((9 - f2) * 4);
sim_interval -= (CPU_MODEL == 0x0)? 4*(f2 - f1)/3: 1;
if ((AC[op2] & SMASK) == MSIGN)
sign ^= 3;
AC[op2] &= DMASK;
if (sign & 1) {
int cy;
cy = dec_add(&AC[op2], NINES - MBR);
cy |= dec_add(&AC[op2], 1LL);
if (cy == 0) {
AC[op2] = NINES - AC[op2];
dec_add(&AC[op2], 1LL);
sim_interval -= (CPU_MODEL == 0x0)?
12*(f2 - f1)/3: 1;
sign ^= 3;
}
} else {
if(dec_add(&AC[op2], MBR))
inds |= 1LL << (4 * (3 - op2)); /* Set overflow */
}
AC[op2] &= DMASK;
if (sign & 8)
AC[op2] |= ASIGN;
else if (sign & 2)
AC[op2] |= MSIGN;
else
AC[op2] |= PSIGN;
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = AC[op2];
}
break;
/* |memory| + AC */
case OP_AAS1: case OP_AAS2: case OP_AAS3:
sign = ((MBR & SMASK) == MSIGN)?2:0;
goto addstore;
/* mem = AC - |memory| */
case OP_SS1: case OP_SS2: case OP_SS3:
sign = ((MBR & SMASK) != MSIGN)?1:2;
goto addstore;
/* memory + AC */
case OP_AS1: case OP_AS2: case OP_AS3:
/* Get field. */
sign = ((MBR & SMASK) == MSIGN)?3:0;
addstore:
if ((MBR & SMASK) == ASIGN)
sign |= 4;
switch (AC[op2] & SMASK) {
case ASIGN: sign |= 8; break;
case MSIGN: sign ^= 1; break;
}
temp = (rdmask[f1] & MBR) >> ((9 - f2) * 4);
sim_interval -= (CPU_MODEL == 0x0)? 4*(f2 - f1)/3: 1;
if (sign & 1) {
int cy;
cy = dec_add(&temp, NINES - (AC[op2] & DMASK));
cy |= dec_add(&temp, 1LL);
if (cy == 0) {
temp = NINES - temp;
dec_add(&temp, 1LL);
sim_interval -= (CPU_MODEL == 0x0)?
12*(f2 - f1)/3: 1;
sign ^= 3;
}
} else {
if(dec_add(&temp, DMASK&AC[op2]))
inds |= 1LL << (4 * (3 - op2)); /* Set overflow */
}
/* Put results back */
utmp = (MBR & SMASK) >> 40;/* Original sign for compare */
MBR &= DMASK;
MBR &= ~(rdmask[f1] & fdmask[f2+1]); /* Clear digits. */
/* Check overflow */
if (temp & ~ldmask[f2-f1+1]) {
if (inds & 0x0F00000000LL) {
inds &= 0xFF0FFFFFFFFLL; /* Set field */
inds |= 0x00900000000LL;
} else {
reason = STOP_FIELD;
}
}
temp &= ldmask[f2-f1+1];
/* Compute final sign */
if ((opcode & 0x10f) == (OP_AAS1 & 0x10f)) {
sign = utmp;
} else if (sign & 0xc) {
sign = ASIGN >> 40;
} else if (sign & 2) {
sign = MSIGN >> 40;
} else {
sign = PSIGN >> 40;
}
/* Check for sign change, and other data in word */
if (MBR != 0 && ((sign != utmp && f1 != 0 && f2 != 9) ||
(sign == (ASIGN >> 40) && utmp != (ASIGN >> 40)))) {
if (inds & 0xF000000000LL) {
inds &= 0xF0FFFFFFFFFLL; /* Set field */
inds |= 0x09000000000LL;
} else {
reason = STOP_SIGN;
}
}
/* Restore results and sign */
MBR |= DMASK & temp << ((9 - f2) * 4);
MBR |= ((t_uint64)sign) << 40;
WriteP(MA, MBR);
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = MBR;
}
break;
/* AC :: memory */
case OP_C1: case OP_C2: case OP_C3:
temp = (rdmask[f1] & MBR) >> ((9 - f2) * 4);
temp |= MBR & SMASK; /* Copy sign */
inds &= 0xFFFFF000FFFLL;
switch(dec_cmp(temp, AC[op2])) {
case -1: inds |= 0x0000001000LL; break;
case 1: inds |= 0x0000100000LL; break;
case 0: inds |= 0x0000010000LL; break;
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = AC[op2];
}
break;
/* Clear Memory, store */
case OP_ZST1: case OP_ZST2: case OP_ZST3:
MBR = SMASK & AC[op2]; /* Same sign as AC */
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOBEF;
}
/* Store digit */
case OP_STD1: case OP_STD2: case OP_STD3:
/* Store AC */
case OP_ST1: case OP_ST2: case OP_ST3:
/* Check for sign change, and other data in word */
if ((opcode & 0x10f) == (OP_ST1 & 0x10f)) {
if ((AC[op2] & SMASK) != (MBR & SMASK) &&
(MBR & DMASK) != 0) {
if (inds & 0xF000000000LL) {
inds &= 0xF0FFFFFFFFFLL; /* Set field */
inds |= 0x09000000000LL;
} else {
reason = STOP_SIGN;
break;
}
}
/* Restore set */
MBR &= DMASK;
MBR |= SMASK & AC[op2];
}
MBR &= ~(rdmask[f1] & fdmask[f2+1]); /* Clear digits. */
temp = AC[op2] & DMASK;
/* Check overflow */
if (temp & ~ldmask[f2-f1+1]) {
if (inds & 0x0F00000000LL) {
inds &= 0xFF0FFFFFFFFLL; /* Set field */
inds |= 0x00900000000LL;
} else {
reason = STOP_FIELD;
break;
}
}
temp &= ldmask[f2-f1+1];
/* Restore results */
MBR |= DMASK & (temp << ((9 - f2) * 4));
WriteP(MA, MBR);
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = MBR;
}
break;
/* Branch AC zero */
case OP_BZ1: case OP_BZ2: case OP_BZ3:
if ((AC[op2] & DMASK) == 0)
IC = MA;
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT;
}
break;
/* Branch AC overflow */
case OP_BV1: case OP_BV2: case OP_BV3:
if ((inds >> (4 * (3 - op2))) & 0x1) {
IC = MA;
inds &= ~(0xFLL << (4 * (3 - op2)));/* clear overflow */
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT;
}
break;
/* Branch AC minus */
case OP_BM1: case OP_BM2: case OP_BM3:
if ((AC[op2] & SMASK) == MSIGN)
IC = MA;
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT;
}
break;
/* Multiply */
case OP_M:
/* Multiplicand in AC[3] */
AC[1] = AC[2] = 0;
sign = (MBR & SMASK) >> 40;
MBR = (rdmask[f1] & MBR) >> ((9 - f2) * 4);
sign = (((AC[3] & SMASK) >> 40) != sign) ? 6 : 9;
/* Multiply MBR * AC[3] result to AC[1],AC[2] <low> */
for(tmp = 36; tmp >= 0; tmp-=4) {
int digit = (AC[3] >> tmp) & 0xf;
AC[1] <<= 4;
AC[1] &= DMASK;
AC[1] |= (AC[2] >> 36) & 0xf;
AC[2] <<= 4;
AC[2] &= DMASK;
if (digit != 0) {
sim_interval -= (CPU_MODEL == 0x0)?
12*digit: digit;
/* Form product */
mul_step(&AC[2], MBR, digit);
digit = (AC[2] >> 40) & 0xff;
if (digit != 0)
dec_add_noov(&AC[1], digit);
AC[2] &= DMASK;
} else
sim_interval -= (CPU_MODEL == 0x0)? 2: 0;
}
/* Set sign */
AC[1] |= ((t_uint64)sign) << 40;
AC[2] |= ((t_uint64)sign) << 40;
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = AC[1];
}
break;
/* Divide */
case OP_D:
/* dividend AC[1],AC[2] */
/* divisor in MBR */
sign = (MBR & SMASK) >> 40;
AC[3] = (rdmask[f1] & MBR) >> ((9 - f2) * 4);
if (AC[3] == 0) {
AC[3] |= ((t_uint64)sign) << 40;
reason = STOP_DIV;
break;
}
utmp = (AC[1] & SMASK) >> 40;
/* Compute sign */
if (utmp != 3 && utmp != sign)
utmp ^= 0xf;
/* If any are alpha, result is alpha */
if (sign == 3 || utmp == 3 || utmp == 0xc)
utmp = 3;
/* Divide AC1,AC2 by AC3 */
AC[1] &= DMASK;
AC[2] &= DMASK;
dec_comp(&AC[3]);
for(tmp = 10; tmp != 0; --tmp)
div_step(AC[3]);
dec_comp(&AC[3]);
/* Fix signs */
AC[1] |= ((t_uint64)utmp) << 40;
AC[2] |= ((t_uint64)sign) << 40;
AC[3] &= DMASK;
AC[3] |= ((t_uint64)sign) << 40;
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = AC[1];
}
break;
/* Shift control */
case OP_SC:
op2 = (MA / 1000) % 10;
if (op2 == 0 || op2 > 3)
break;
utmp = MA % 100;
if (utmp > 10)
break;
temp = AC[op2] & DMASK;
switch ((MA / 100) % 10) {
case 0:
temp >>= utmp * 4;
break;
case 1:
if (utmp != 0) {
temp >>= (utmp - 1) * 4;
f1 = temp & 0xF;
temp >>= 4;
if (f1 > 5)
dec_add(&temp, 1LL);
}
break;
case 2:
temp <<= utmp * 4;
break;
case 3:
utmp = 0;
if (temp != 0) {
while((temp & dmask[10]) == 0) {
utmp++;
temp <<= 4;
}
}
if (IX) {
MBR = ReadP(IX);
MBR &= ~IMASK;
MBR &= DMASK;
MBR |= PSIGN;
if (utmp > 10) /* BCD adjust */
utmp += 6;
MBR |= ((t_uint64)utmp) << 16;
WriteP(IX, MBR);
}
break;
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].before = AC[op2];
}
AC[op2] &= SMASK;
AC[op2] |= DMASK & temp;
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = AC[op2];
}
break;
/* Coupled shift control */
case OP_CSC:
utmp = MA % 100; /* Number of shifts */
if (utmp > 20)
break;
op2 = (MA / 100) % 10; /* Operand */
f2 += f1 * 10;
if (hst_lnt) { /* history enabled? */
hst[hst_p].before = AC[1];
}
switch (op2) {
default:
case 0: /* Shift right coupled */
sign = (AC[1] >> 40) & 0xf;
AC[1] &= DMASK;
AC[2] &= DMASK;
for(;utmp != 0; utmp--) {
f1 = AC[1] & 0xf;
AC[1] >>= 4;
AC[2] |= ((t_uint64)f1) << 40;
AC[2] >>= 4;
}
break;
case 1: /* Shift right coupled and round */
sign = (AC[1] >> 40) & 0xf;
AC[1] &= DMASK;
AC[2] &= DMASK;
for(;utmp != 0; utmp--) {
f1 = AC[1] & 0xf;
AC[1] >>= 4;
AC[2] |= ((t_uint64)f1) << 40;
f1 = AC[2] & 0xf;
AC[2] >>= 4;
}
if (f1 > 5)
if (dec_add(&AC[2], 1LL))
if (dec_add(&AC[1], 1LL))
inds |= 1LL << 8; /* Set overflow */
break;
case 2: /* Shift left coupled */
sign = (AC[2] >> 40) & 0xf;
AC[1] &= DMASK;
AC[2] &= DMASK;
for(;utmp != 0; utmp--) {
AC[1] <<= 4;
AC[1] &= DMASK;
AC[1] |= (AC[2] >> 36) & 0xf;
AC[2] <<= 4;
AC[2] &= DMASK;
}
break;
case 3: /* Shift left and count coupled */
sign = (AC[2] >> 40) & 0xf;
AC[1] &= DMASK;
AC[2] &= DMASK;
utmp = 0;
if (AC[1] != 0 || AC[2] != 0) {
while((AC[1] & dmask[10]) == 0) {
AC[1] <<= 4;
AC[1] &= DMASK;
AC[1] |= (AC[2] >> 36) & 0xf;
AC[2] <<= 4;
AC[2] &= DMASK;
utmp++;
}
}
if (IX) {
MBR = ReadP(IX);
MBR &= ~IMASK;
MBR &= DMASK;
if (utmp > 10) /* BCD adjust */
utmp += 6;
MBR |= ((t_uint64)utmp) << 16;
WriteP(IX, MBR);
}
break;
case 4: /* Shift right from point ac1 */
sign = (AC[1] >> 40) & 0xf;
tmp = (MA / 1000) % 10; /* Split point */
AC[1] &= DMASK;
AC[2] &= DMASK;
for(;utmp != 0; utmp--) {
f1 = AC[1] & 0xf;
AC[1] = (AC[1] & fdmask[tmp]) |
((AC[1] & rdmask[tmp]) >> 4);
AC[2] |= ((t_uint64)f1) << 40;
AC[2] >>= 4;
}
break;
case 5: /* Shift left from point ac1 */
sign = (AC[2] >> 40) & 0xf;
tmp = (MA / 1000) % 10; /* Split point */
AC[1] &= DMASK;
AC[2] &= DMASK;
for(;utmp != 0; utmp--) {
AC[1] = (AC[1] & rdmask[tmp]) |
((AC[1] & fdmask[tmp]) << 4);
}
break;
case 6: /* Shift right from point ac2 */
sign = (AC[2] >> 40) & 0xf;
tmp = (MA / 1000) % 10; /* Split point */
AC[1] &= DMASK;
AC[2] &= DMASK;
for(;utmp != 0; utmp--) {
AC[2] = (AC[2] & fdmask[tmp]) |
((AC[2] & rdmask[tmp]) >> 4);
}
break;
case 7: /* Shift left from point ac2 */
sign = (AC[2] >> 40) & 0xf;
tmp = (MA / 1000) % 10; /* Split point */
AC[1] &= DMASK;
AC[2] &= DMASK;
for(;utmp != 0; utmp--) {
AC[1] <<= 4;
AC[1] &= DMASK;
AC[1] |= ((AC[2] & fdmask[tmp]) >> 36) & 0xf;
AC[2] = (AC[2] & rdmask[tmp]) |
((AC[2] & fdmask[tmp]) << 4);
}
break;
}
AC[1] |= ((t_uint64)sign) << 40;
AC[2] |= ((t_uint64)sign) << 40;
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = AC[1];
}
break;
/* AC1 : |Memory | */
case OP_CA:
MBR = (rdmask[f1] & MBR) >> ((9 - f2) * 4);
inds &= 0xFFFFF000FFFLL;
switch(dec_cmp(MBR & DMASK, AC[1]&DMASK)) {
case -1: inds |= 0x0000001000LL; break;
case 1: inds |= 0x0000100000LL; break;
case 0: inds |= 0x0000010000LL; break;
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = AC[1];
}
break;
/* Compare digit */
case OP_CD:
inds &= 0xFFFFF000FFFLL;
MBR >>= ((9 - f2) * 4);
MBR &= 0xF;
if (MBR > f1)
inds |= 0x0000100000LL;
else if (MBR < f1)
inds |= 0x0000001000LL;
else
inds |= 0x0000010000LL;
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT;
}
break;
/* Branch load index */
case OP_BLX:
upd_idx(&M[IX], IC);
/* Branch */
case OP_B:
IC = MA;
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT;
}
break;
/* Branch low */
case OP_BL:
if (inds & 0x0000001000LL)
IC = MA;
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT;
}
break;
/* Branch high */
case OP_BH:
if (inds & 0x0000100000LL)
IC = MA;
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT;
}
break;
/* Branch equal */
case OP_BE:
if (inds & 0x0000010000LL)
IC = MA;
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT;
}
break;
/* Extended memory on 7074 */
case OP_EXMEM:
if (CPU_MODEL == 0x1 && cpu_unit.flags & OPTION_EXTEND) {
switch(f1) {
case 0:
if (emode)
IC = MA;
break;
case 1:
emode = 0;
break;
case 2:
emode = 1;
break;
}
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT;
}
break;
case OP_HB:
IC = MA;
/* fall through */
case OP_HP:
reason = STOP_HALT;
/* fall through */
case OP_NOP:
/* fall through */
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT;
}
break;
/* Floating point option */
/* Floating point: sEEMMMMMMM EE+50*/
case OP_FD: /* AC2 <- 0, AC1,AC2 <- AC1/M, norm */
if ((cpu_unit.flags & OPTION_FLOAT) == 0) {
reason = STOP_UUO;
break;
}
AC[2] = 0;
case OP_FDD: /* AC1,2 <- AC1/M, norm */
if ((cpu_unit.flags & OPTION_FLOAT) == 0) {
reason = STOP_UUO;
break;
}
if ((AC[1] & SMASK) == ASIGN || (MBR & SMASK) == ASIGN) {
reason = STOP_SIGN;
break;
}
/* Extract signs */
sign = (MBR & SMASK) == MSIGN;
if ((AC[1] & SMASK) == MSIGN)
sign ^= 3;
/* Extract exponents */
f1 = (((AC[1] >> 36) & 0xf) * 10) +
((AC[1] >> 32) & 0xf);
tmp = (((MBR >> 36) & 0xf) * 10) + ((MBR >> 32) & 0xf);
tmp = 51 + (f1 - tmp);
/* Clear exponents */
MBR &= FMASK;
if (MBR == 0) {
reason = STOP_DIV;
break;
}
AC[1] &= FMASK;
AC[2] &= FMASK;
AC[1] = NINES - AC[1]; /* One's compliment AC[1]&AC[2] */
AC[2] = FNINES - AC[2];
dec_add(&AC[2], 1LL);
if (AC[2] & EMASK) { /* Propagate carry */
dec_add(&AC[1], AC[2] >> 32);
AC[2] &= FMASK;
}
/* Check if overflow on first try */
temp = AC[1];
if (dec_add(&temp, MBR)) {
/* Yes, shift right before starting */
AC[1] <<= 4;
AC[1] &= DMASK;
AC[2] <<= 4;
AC[1] |= (AC[2] >> 32) & 0xf;
AC[2] &= FMASK;
tmp--;
} else
f1++;
utmp = 8;
do {
int cnt = 0;
/* Repeated subtract until overflow */
while(1) {
temp = AC[1];
if (dec_add(&temp, MBR))
break;
cnt++;
AC[1] = temp; /* Restore AC if not less */
if (cnt > 9) { /* Catch divide check */
reason = STOP_DIV;
goto done;
}
}
/* Shift right coupled */
AC[1] <<= 4;
AC[1] &= DMASK;
AC[2] <<= 4;
AC[1] |= (AC[2] >> 32) & 0xf;
AC[2] &= FMASK;
AC[2] |= cnt; /* Put in count */
} while (--utmp != 0);
/* Restore remainder to correct value */
dec_comp(&AC[1]);
/* Now exchange AC1 & AC2 to get results in right place */
temp = AC[1];
AC[1] = AC[2];
AC[2] = temp;
/* Check overflow */
if (tmp > 99) {
inds |= 0x0001000000LL; /* Set overflow */
tmp = 0;
}
/* Save exponents */
bin_dec(&AC[1], tmp, 8, 2); /* Restore exponent */
if (f1 < 8)
AC[2] = 0;
else {
f1 -= 8;
AC[2] >>= 4;
if ((AC[2] & EMASK) != 0) {
if (f1-- != 0)
AC[2] >>= 4;
else
AC[2] = f1 = 0;
}
bin_dec(&AC[2], f1, 8, 2); /* Restore exponent */
}
/* Fix signs */
if (sign & 1) {
AC[1] |= MSIGN;
} else {
AC[1] |= PSIGN;
}
if (sign & 3) {
AC[2] |= MSIGN;
} else {
AC[2] |= PSIGN;
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = AC[1];
}
break;
/* Floating point multiply */
case OP_FM: /* AC1,2 <- AC1 * M, norm */
if ((cpu_unit.flags & OPTION_FLOAT) == 0) {
reason = STOP_UUO;
break;
}
if ((AC[1] & SMASK) == ASIGN || (MBR & SMASK) == ASIGN) {
reason = STOP_SIGN;
break;
}
/* Extract signs */
sign = (MBR & SMASK) == MSIGN;
sign ^= (AC[1] & SMASK) == MSIGN;
/* Extract exponents */
utmp = (((AC[1] >> 36) & 0xf) * 10) +
((AC[1] >> 32) & 0xf);
f1 = (((MBR >> 36) & 0xf) * 10) + ((MBR >> 32) & 0xf);
utmp += f1;
utmp -= 50;
/* Clear exponents */
MBR &= FMASK;
temp = AC[1];
AC[1] = 0;
AC[2] = 0;
/* Multiplicand in AC[3] */
/* Multiply MBR * AC[1] result to AC[1],AC[2] <low> */
for(tmp = 28; tmp >= 0; tmp-=4) {
int digit = (temp >> tmp) & 0xf;
AC[1] <<= 4;
AC[1] &= DMASK;
AC[1] |= (AC[2] >> 28) & 0xf;
AC[2] <<= 4;
AC[2] &= FMASK;
if (digit != 0) {
sim_interval -= (CPU_MODEL == 0x0)? 12*digit:digit;
/* Form product */
mul_step(&AC[2], MBR, digit);
digit = (AC[2] >> 32) & 0xff;
if (digit != 0)
dec_add(&AC[1], digit);
AC[2] &= FMASK;
} else
sim_interval -= (CPU_MODEL == 0x0)? 2: 0;
}
/* Check if needs to be normalized */
if ((AC[1] & NMASK) == 0) {
AC[1] <<= 4;
AC[1] |= (AC[2] >> 28) & 0xf;
AC[2] <<= 4;
AC[2] &= FMASK;
utmp--;
}
/* Check overflow */
if (utmp > 99) {
inds |= 0x0001000000LL; /* Set overflow */
utmp = 0;
}
/* Save exponents */
bin_dec(&AC[1], utmp, 8, 2); /* Restore exponent */
if (utmp < 8)
AC[2] = 0;
else
bin_dec(&AC[2], utmp-8, 8, 2); /* Restore exponent */
/* Set signs */
if (sign) {
AC[1] |= MSIGN;
AC[2] |= MSIGN;
} else {
AC[1] |= PSIGN;
AC[2] |= PSIGN;
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = AC[1];
}
break;
/* Floating point round */
case OP_FR: /* AC1 <- +.5, AC2 <- 0 */
if ((cpu_unit.flags & OPTION_FLOAT) == 0) {
reason = STOP_UUO;
break;
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].before = AC[1];
}
if (((AC[2] >> 28) & 0xf) > 5) {
temp = AC[1] & SMASK;
AC[1] &= DMASK;
if (dec_add(&AC[1], 1LL)) {
inds |= 0x0001000000LL;
AC[1] = 0;
}
AC[1] |= temp; /* Restore sign */
}
AC[2] = PSIGN;
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = AC[1];
}
break;
/* Floating subtract absolute */
case OP_FSA: /* AC2 <- 0, AC1,2 <- AC1,2-|M|, norm */
if ((cpu_unit.flags & OPTION_FLOAT) == 0) {
reason = STOP_UUO;
break;
}
if ((AC[1] & SMASK) == ASIGN || (MBR & SMASK) == ASIGN) {
reason = STOP_SIGN;
break;
}
sign = 1;
AC[2] = 0x65000000000LL;
goto float_add;
/* Floating subtract */
case OP_FS: /* AC2 <- 0, AC1,2 <- AC1,2 - M, norm */
if ((cpu_unit.flags & OPTION_FLOAT) == 0) {
reason = STOP_UUO;
break;
}
if ((AC[1] & SMASK) == ASIGN || (MBR & SMASK) == ASIGN) {
reason = STOP_SIGN;
break;
}
sign = (MBR & SMASK) != MSIGN;
AC[2] = 0x65000000000LL;
goto float_add;
/* Floating add absolute */
case OP_FAA: /* AC2 <- 0, AC1,2 <- AC1,2+|M|, norm */
if ((cpu_unit.flags & OPTION_FLOAT) == 0) {
reason = STOP_UUO;
break;
}
if ((AC[1] & SMASK) == ASIGN || (MBR & SMASK) == ASIGN) {
reason = STOP_SIGN;
break;
}
sign = 0;
goto float_add;
/* Floating add */
case OP_FA: /* AC2 <- 0, AC1,2 <- AC1,2+M, norm */
if ((cpu_unit.flags & OPTION_FLOAT) == 0) {
reason = STOP_UUO;
break;
}
AC[2] = 0x65000000000LL;
/* Floating add double */
case OP_FAD: /* AC1,2 <- AC1,2 + M, norm */
/* Floating add double, suppress norm */
case OP_FADS: /* AC1,2 <- AC1,2 + M */
if ((cpu_unit.flags & OPTION_FLOAT) == 0) {
reason = STOP_UUO;
break;
}
if ((AC[1] & SMASK) == ASIGN || (MBR & SMASK) == ASIGN) {
reason = STOP_SIGN;
goto done;
}
sign = (MBR & SMASK) == MSIGN;
float_add:
/* Extract sign */
if ((AC[1] & SMASK) == MSIGN)
sign ^= 3;
/* Compare exponents */
utmp = (((AC[1] >> 36) & 0xf) * 10) +
((AC[1] >> 32) & 0xf);
f1 = (((MBR >> 36) & 0xf) * 10) + ((MBR >> 32) & 0xf);
tmp = ((signed int)utmp) - ((signed int)f1);
/* Clear exponents and sign */
MBR &= FMASK;
AC[1] &= FMASK;
AC[2] &= FMASK;
temp = 0;
if (tmp > 0) { /* AC Bigger */
/* Shift MBR */
if (tmp > 16)
goto float_norm;
while(tmp > 0) {
temp |= ((t_uint64)(MBR & 0xf)) << 32;
MBR >>= 4;
temp >>= 4;
tmp--;
}
} else if (tmp < 0) { /* AC Smaller */
utmp = f1;
/* Shift AC */
if (tmp > -16) {
while(tmp < 0) {
AC[2] |= ((t_uint64)(AC[1] & 0xf)) << 32;
AC[1] >>= 4;
AC[2] >>= 4;
tmp++;
}
} else {
AC[1] = MBR;
AC[2] = 0;
goto float_norm;
}
}
/* Do actual addition now */
if (sign & 1) {
/* Do compliment add */
dec_add(&AC[2], FNINES - temp);
dec_add(&AC[2], 1LL);
dec_add(&AC[1], FNINES - MBR);
/* Carry between halfs */
if (AC[2] & EMASK) { /* Carry out */
dec_add(&AC[1], (AC[2] >> 32) & 0xff);
AC[2] &= FMASK;
}
if ((AC[1] & EMASK) == 0) {
AC[2] = FNINES - (AC[2] & FMASK);
AC[1] = FNINES - (AC[1] & FMASK);
dec_add(&AC[2], 1LL);
if (AC[2] & EMASK) { /* Carry out */
dec_add(&AC[1], (AC[2] >> 32) & 0xff);
AC[2] &= FMASK;
}
sim_interval -= (CPU_MODEL == 0x0)?
12*(f2 - f1)/3: 1;
sign ^= 3;
}
AC[1] &= FMASK;
} else {
/* Add low then high */
dec_add(&AC[2], temp);
dec_add(&AC[1], MBR);
if (AC[2] & EMASK) /* Carry out */
dec_add(&AC[1], (AC[2] >> 32) & 0xf);
}
if (AC[1] & EMASK) {
AC[2] |= ((t_uint64)(AC[1] & 0xf)) << 32;
AC[1] >>= 4;
AC[2] >>= 4;
utmp++;
}
float_norm:
/* Normalize result */
tmp = utmp;
if (opcode != OP_FADS && AC[1] != 0 && AC[2] != 0) {
while((AC[1] & NMASK) == 0) {
AC[1] <<= 4;
AC[1] |= (AC[2] >> 28) & 0xf;
AC[2] <<= 4;
AC[2] &= FMASK;
tmp--;
}
}
/* Set exponent if error */
if (AC[1] == 0 && AC[2] == 0)
tmp = 50;
if (tmp < 0) {
inds |= 0x0010000000LL; /* Set underflow */
tmp = 0;
}
if (tmp > 99) {
inds |= 0x0001000000LL; /* Set overflow */
tmp = 0;
}
/* Restore exponents */
bin_dec(&AC[1], tmp, 8, 2); /* Restore exponent */
if (tmp < 8)
AC[2] = 0;
else
bin_dec(&AC[2], tmp-8, 8, 2); /* Restore exponent */
/* Set signs */
if (sign & 2) {
AC[1] |= MSIGN;
AC[2] |= MSIGN;
} else {
AC[1] |= PSIGN;
AC[2] |= PSIGN;
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = AC[1];
}
break;
/* Floating zero and add */
case OP_FZA: /* AC2 <- 0, AC1 <- M, norm */
if ((cpu_unit.flags & OPTION_FLOAT) == 0) {
reason = STOP_UUO;
break;
}
/* Check sign */
if ((MBR & SMASK) == ASIGN) {
reason = STOP_SIGN;
break;
}
AC[2] = 0;
/* Extract exponent */
tmp = (((MBR >> 36) & 0xf) * 10) + ((MBR >> 32) & 0xf);
AC[1] = MBR & FMASK;
if (AC[1] != 0) {
while((AC[1] & NMASK) == 0) {
tmp--;
AC[1] <<= 4;
}
} else
tmp = 50;
if (tmp < 0) {
inds |= 0x0010000000LL; /* Set underflow */
tmp = 0;
}
bin_dec(&AC[1], tmp, 8, 2); /* Restore exponent */
AC[1] |= MBR & SMASK;
AC[2] |= MBR & SMASK;
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = AC[1];
}
break;
case OP_FBU: /* Branch floating overflow */
if ((cpu_unit.flags & OPTION_FLOAT) == 0) {
reason = STOP_UUO;
break;
}
if ((inds & 0x000F000000LL) != 0)
IC = MA;
inds &= 0xFFFF0FFFFFFLL; /* Clear flag */
break;
case OP_FBV: /* Branch floating underflow */
if ((cpu_unit.flags & OPTION_FLOAT) == 0) {
reason = STOP_UUO;
break;
}
if ((inds & 0x00F0000000LL) != 0)
IC = MA;
inds &= 0xFFF0FFFFFFFLL; /* Clear flag */
break;
/* Load with interchange */
case OP_XLIN:
MBR = (MBR & (SMASK|OMASK)) | ((MBR >> 16) & AMASK) |
((MBR << 16) & IMASK);
/* Load index */
case OP_XL:
WriteP(IX, MBR);
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT;
}
break;
/* Unload index */
case OP_XU:
MBR = ReadP(IX);
WriteP(MA, MBR);
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = MBR;
}
break;
/* Index zero subtract */
case OP_XZS:
MBR = ReadP(IX);
if (hst_lnt) { /* history enabled? */
hst[hst_p].before = MBR;
}
upd_idx(&MBR, MA);
MBR &= DMASK;
MBR |= MSIGN;
WriteP(IX, MBR);
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = MBR;
}
break;
/* Index zero add */
case OP_XZA:
MBR = ReadP(IX);
if (hst_lnt) { /* history enabled? */
hst[hst_p].before = MBR;
}
upd_idx(&MBR, MA);
MBR &= DMASK;
MBR |= PSIGN;
WriteP(IX, MBR);
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = MBR;
}
break;
/* Index subtract */
case OP_XS:
MBR = ReadP(IX);
if (hst_lnt) { /* history enabled? */
hst[hst_p].before = MBR;
}
temp = dec_bin_idx(MBR);
sign = ((MBR & SMASK)>> 40);
MBR &= DMASK;
switch(sign) {
case 0x6: /* + - tc b add */
temp += MA;
break;
default:
case 0x3: /* + a add res a */
case 0x9: /* + + add */
temp = ~temp + MA + 1;
if (temp & 0x8000) {
temp = ~temp + 1;
if (sign == 0x9) sign = 0x6;
}
break;
}
MBR |= ((t_uint64)sign) << 40;
upd_idx(&MBR, (uint32)temp);
WriteP(IX, MBR);
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = MBR;
}
break;
/* Index add */
case OP_XA:
MBR = ReadP(IX);
if (hst_lnt) { /* history enabled? */
hst[hst_p].before = MBR;
}
temp = 0;
upd_idx(&temp, MA);
sign = ((MBR & SMASK)>> 40);
MBR &= DMASK;
switch(sign) {
default:
case 0x3: /* + a add res a */
case 0x9: /* + + add */
dec_add(&temp, MBR & ((emode)?IMASK2:IMASK));
break;
case 0x6: /* + - tc b add */
if (temp == 0)
break;
dec_comp(&temp);
dec_add(&temp, MBR & ((emode)?IMASK2:IMASK));
if (temp & ((emode)?XMASK2:XMASK)) {
dec_comp(&temp);
sign = 0x9;
}
break;
}
MBR |= ((t_uint64)sign) << 40;
MBR &= (emode)?~IMASK2:~IMASK;
MBR |= temp;
WriteP(IX, MBR);
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = MBR;
}
break;
/* Index set non-indexing */
case OP_XSN:
MBR = ReadP(IX);
if (hst_lnt) { /* history enabled? */
hst[hst_p].before = MBR;
}
bin_dec(&MBR, MA, 0, 4);
WriteP(IX, MBR);
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = MBR;
}
break;
/* Branch if index word index = 0 */
case OP_BXN:
MBR = ReadP(IX);
if (hst_lnt) { /* history enabled? */
hst[hst_p].before = MBR;
hst[hst_p].ic |= HIST_NOAFT;
}
if ((MBR & IMASK) != 0)
IC = MA;
break;
/* Branch decrement index */
case OP_BDX:
MBR = ReadP(IX);
if (hst_lnt) { /* history enabled? */
hst[hst_p].before = MBR;
}
temp = MBR & IMASK;
dec_add(&temp, (emode)?0x999990000LL:0x99990000LL);
MBR &= (emode)?~IMASK2:~IMASK;
MBR |= temp & (emode)?IMASK2:IMASK;
WriteP(IX, MBR);
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = MBR;
}
goto checkix;
/* Branch increment index */
case OP_BIX:
temp = MBR = ReadP(IX);
if (hst_lnt) { /* history enabled? */
hst[hst_p].before = MBR;
}
dec_add(&temp, 0x10000LL);
MBR &= (emode)?~IMASK2:~IMASK;
MBR |= temp & ((emode)?IMASK2:IMASK);
WriteP(IX, MBR);
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = MBR;
}
goto checkix;
/* Branch compared index */
case OP_BCX:
MBR = ReadP(IX);
if (hst_lnt) { /* history enabled? */
hst[hst_p].before = MBR;
hst[hst_p].ic |= HIST_NOAFT;
}
checkix:
temp = (MBR & IMASK) >> 16;
MBR &= AMASK;
dec_comp(&temp);
if(dec_add(&temp, MBR))
IC = MA;
break;
/* Branch if index word index minus */
case OP_BXM:
MBR = ReadP(IX);
if (hst_lnt) { /* history enabled? */
hst[hst_p].before = MBR;
hst[hst_p].ic |= HIST_NOAFT;
}
if ((MBR & SMASK) == MSIGN)
IC = MA;
break;
/* Field over flow control */
case OP_BFLD:
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF;
}
switch (f2) {
case 0:
if ((inds & 0x0F00000000LL) == 0x0900000000LL) {
IC = MA;
inds ^= 0x0F00000000LL;
}
break;
case 1:
if ((inds & 0x0F00000000LL) != 0x0500000000LL) {
inds &= 0xFF0FFFFFFFFLL;
inds |= 0x00500000000LL;
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOEA;
}
break;
case 2:
if ((inds & 0x0F00000000LL) == 0x0900000000LL)
reason = STOP_SIGN;
else
inds &= 0xFF0FFFFFFFFLL;
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOEA;
}
break;
}
break;
/* Sign change control */
case OP_CS:
switch (f2) {
case 0:
inds &= 0xFFFFF000FFFLL;
utmp = ((MBR >> 40) & 0xf);
if (utmp > f1)
inds |= 0x00000100000LL;
else if (utmp < f1)
inds |= 0x00000001000LL;
else
inds |= 0x00000010000LL;
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT;
}
break;
case 1:
MBR &= DMASK;
MBR |= ((t_uint64)f1) << 40;
WriteP(MA, MBR);
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = MBR;
}
break;
case 2:
if ((inds & 0xF000000000LL) == 0) {
inds |= 0x5000000000LL;
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOEA|HIST_NOBEF|HIST_NOAFT;
}
break;
case 3:
if ((inds & 0xF000000000LL) == 0x9000000000LL)
reason = STOP_SIGN;
else
inds &= 0xF0FFFFFFFFFLL;
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOEA|HIST_NOBEF|HIST_NOAFT;
}
break;
case 4:
if ((inds & 0xF000000000LL) == 0x9000000000LL) {
IC = MA;
inds ^= 0xF000000000LL;
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT;
}
break;
}
break;
/* Record scatter */
case OP_RS:
/* Generate source address */
temp = M[IX];
utmp = dec_bin_idx(temp);
do {
uint32 dst, limit;
MBR = ReadP(MA++); /* Grab next RDW */
get_rdw(MBR, &dst, &limit);
while(dst <= limit) {
WriteP(dst++, ReadP(utmp++));
if (utmp > MEMSIZE)
utmp = 0;
}
} while ((MBR & SMASK) != MSIGN);
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT;
}
break;
/* Record gather */
case OP_RG:
/* Generate destination address */
temp = M[IX];
utmp = dec_bin_idx(temp);
do {
uint32 src, limit;
MBR = ReadP(MA++); /* Grab next RDW */
get_rdw(MBR, &src, &limit);
while(src <= limit) {
WriteP(utmp++, ReadP(src++));
if (utmp > MEMSIZE)
utmp = 0;
}
} while ((MBR & SMASK) != MSIGN);
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT;
}
break;
/* Edit alpha to numeric */
case OP_ENB:
case OP_ENS:
case OP_ENA:
/* Generate source address */
temp = M[IX];
utmp = dec_bin_idx(temp);
do {
uint32 dst, limit;
MBR = ReadP(MA++); /* Grab next RDW */
get_rdw(MBR, &dst, &limit);
while(dst <= limit) {
t_uint64 buffer;
f1 = (opcode == OP_ENB)? 0: 1;
temp = ReadP(utmp++);
if (utmp > MEMSIZE)
utmp = 0;
buffer = 0x9090909090LL|ASIGN;
for(tmp = 9; tmp > 4; tmp--) {
if (f1 == 0) {
if ((temp & dmask[tmp+1]) == 0)
buffer &= ~(0xFFLL << ((tmp-4)*8));
else
f1 = 1;
}
buffer |= (temp & dmask[tmp+1]) << ((tmp-4)*8);
}
WriteP(dst++, buffer);
if (opcode == OP_ENS) {
switch(temp & SMASK) {
case ASIGN:
buffer = 0x9090909090LL|ASIGN;
break;
case PSIGN:
buffer = 0x9090909060LL|ASIGN;
break;
case MSIGN:
buffer = 0x9090909070LL|ASIGN;
break;
}
} else
buffer = 0x9090909090LL|ASIGN;
for(; tmp >= 0; tmp--) {
if (f1 == 0) {
if ((temp & dmask[tmp+1]) == 0)
buffer &= ~(0xFFLL << (tmp * 8));
else
f1 = 1;
}
buffer |= (temp & dmask[tmp+1]) << (tmp*8);
}
WriteP(dst++, buffer);
}
} while ((MBR & SMASK) != MSIGN);
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT;
}
break;
case OP_EAN:
/* Generate destination address */
temp = M[IX];
utmp = dec_bin_idx(temp);
do {
uint32 src, limit;
MBR = ReadP(MA++); /* Grab next RDW */
get_rdw(MBR, &src, &limit);
while(src <= limit) {
t_uint64 buffer = 0;
temp = ReadP(src++);
for(tmp = 8, f1 = 16; tmp >= 0; tmp-= 2) {
buffer |= (temp & dmask[tmp+1]) << f1;
f1 += 4;
}
temp = ReadP(src++);
for(tmp = 8, f1 = 16; tmp >= 0; tmp-=2) {
buffer |= (temp & dmask[tmp+1]) >> f1;
f1 -= 4;
}
if ((temp & 0xF0LL) == 0x70LL)
buffer |= MSIGN;
else
buffer |= PSIGN;
WriteP(utmp++, buffer);
if (utmp > MEMSIZE)
utmp = 0;
}
} while ((MBR & SMASK) != MSIGN);
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT;
}
break;
case OP_LL:
case OP_LE:
case OP_LEH:
/* Generate increment */
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT;
}
temp = M[98];
utmp = dec_bin_idx(temp);
do {
uint32 src, limit;
MBR = ReadP(MA++); /* Grab next RDW */
get_rdw(MBR, &src, &limit);
while(src <= limit) {
temp = ReadP(src);
temp = (rdmask[f1] & temp) >> ((9 - f2) * 4);
switch(dec_cmp(temp, AC[3])) {
case -1:
if (opcode == OP_LL) {
f = 1;
AC[3] = temp;
bin_dec(&M[98], src, 4,(emode)?5:4);
M[98] &= DMASK;
M[98] |= PSIGN;
}
break;
case 1:
if (opcode != OP_LEH)
break;
case 0:
if (opcode != OP_LL) {
f = 1;
bin_dec(&M[98], src, 4,(emode)?5:4);
M[98] &= DMASK;
M[98] |= PSIGN;
goto found;
}
break;
}
src += utmp;
}
} while ((MBR & SMASK) != MSIGN);
found:
if (f)
IC++;
break;
/* Electronic switch */
case OP_BSW21:
case OP_BSW22:
case OP_BSW23:
/* Read flag */
opcode -= OP_BSW21;
opcode += 101;
MBR = ReadP(opcode);
if (hst_lnt) { /* history enabled? */
hst[hst_p].before = MBR;
}
/* Compute mask */
f2 = 4 * (9 - f2);
temp = 0xFLL << f2;
switch(f1) {
case 0: /* Switch test */
case 3: /* test and turn on */
case 4: /* test and turn off */
if (MBR & temp)
IC = MA;
}
if (f1 != 0)
MBR &= ~(temp);
if (f1 & 1) /* Set flag */
MBR |= 0x1LL << f2;
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = MBR;
}
WriteP(opcode, MBR);
break;
/* Priority control */
case OP_PC:
utmp = 0;
temp = 0xF;
/* Convert digits into bits */
for(f1 = 0; f1 < 10; f1++) {
if ((MBR & temp) != 0)
utmp |= 1;
temp <<= 4;
utmp <<= 1;
}
utmp >>= 1;
if (f2 == 1) {
utmp <<= 10;
pri_mask &= 0x3FF;
pri_mask |= utmp;
} else if (f2 == 0) {
pri_mask &= 0xFFC00;
pri_mask |= utmp;
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT;
}
break;
/* Test priority latches */
case OP_PRTST:
if (f1 == 0 && f2 == 0) {
for(tmp = 0; tmp < 10; tmp++) {
if (pri_latchs[tmp] != 0) {
IC = MA;
break;
}
}
} else {
if ((pri_latchs[f1] >> f2) & 1)
IC = MA;
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT;
}
break;
/* Set priority latches */
case OP_PRION:
switch(f1) {
case 0:
case 8:
case 9:
case 1:
case 2:
case 3:
case 4:
pri_latchs[f1] |= 1 << f2;
break;
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT|HIST_NOEA;
}
break;
/* Clear priority latches */
case OP_PRIOF:
pri_latchs[f1] &= ~(1 << f2);
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF|HIST_NOEA;
}
break;
case OP_PR:
if (pri_enb == 1) /* Not in priority mode, nop */
break;
if ((tmp = scan_irq()) != 0) {
if (MA != 97) {
/* Save instruction counter */
MBR = ReadP(97);
upd_idx(&MBR, MA);
MBR &= DMASK;
MBR |= PSIGN;
WriteP(97, MBR);
pri_enb = 0;
if (hst_lnt) { /* history enabled? */
hst[hst_p].after = MBR;
}
} else if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT;
}
inds = PSIGN;
IC = tmp;
} else {
if (MA == 97)
IC = dec_bin_idx(MBR);
else
IC = MA;
inds = ReadP(100);
pri_enb = 1;
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT;
}
}
break;
/* Check switch */
case OP_BSWITCH:
if (f1 == 0 || f1 > 4) {
reason = STOP_UUO;
break;
}
switch(f2) {
case 0:
if ((SW >> (f1 - 1)) & 1)
IC = MA;
break;
case 1:
case 2:
if (chan_active(((f2 - 1) * 4) + f1))
IC = MA;
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF;
}
break;
/* Inquiry station */
case OP_INQ:
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF;
}
IX = 0;
if (f1 != 1) { /* Only support group one */
reason = STOP_IOCHECK;
goto done;
}
switch (f2) {
case 0: utmp = (IO_RDS << 8)|CHN_ALPHA; break; /* QR */
case 1: utmp = (IO_WRS << 8)|CHN_ALPHA; break; /* QW */
default:
reason = STOP_UUO;
goto done;
}
/* Start off command */
switch (chan_cmd(4, utmp, MA)) {
case SCPE_BUSY:
iowait = 1;
break;
case SCPE_IOERR:
reason = STOP_IOCHECK;
break;
case SCPE_OK:
while(chan_active(0)) {
sim_interval = 0;
reason = sim_process_event();
if (reason != SCPE_OK)
break;
chan_proc();
}
break;
}
break;
/* Unit record I/O */
case OP_UREC:
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF;
}
switch (f2) {
case 0: utmp = (IO_TRS << 8); break; /* US */
case 1: utmp = (IO_RDS << 8)|CHN_ALPHA; break;/* UR */
case 4: /* TYP */
case 2: /* UW */
case 3: /* UWIV */
utmp = (IO_WRS << 8)|CHN_ALPHA;
break;
case 9:
if (cpu_unit.flags & OPTION_TIMER) {
if (f1 == 0)
timer = 0;
else if (f1 == 1)
WriteP(MA, PSIGN|timer);
goto done;
}
/* fall through */
default:
reason = STOP_UUO;
goto done;
}
/* Start off command */
switch (chan_cmd(f1, utmp, MA)) {
case SCPE_BUSY:
iowait = 1;
break;
case SCPE_IOERR:
reason = STOP_IOCHECK;
break;
case SCPE_OK:
while(chan_active(0)) {
sim_interval = 0;
reason = sim_process_event();
if (reason != SCPE_OK)
break;
chan_proc();
}
switch (f2) {
case 0: /* US */
chan_stat(0, CHS_ERR);
break;
case 1: /* UR */
if (chan_stat(0, CHS_ERR))
break;
IC++;
if (chan_stat(0, CHS_EOF))
break;
IC++;
break;
case 4: /* TYP */
if (chan_stat(0, CHS_ERR))
break;
IC++;
break;
case 2: /* UW */
if (chan_stat(0, CHS_ERR))
break;
IC++;
if (lpr_chan9[0])
break;
IC++;
break;
case 3: /* UWIV */
chan_stat(0, CHS_ERR);
break;
}
}
done:
break;
/* Tape channel channels */
case OP_TAP1:
case OP_TAP2:
case OP_TAP3:
case OP_TAP4:
case OP_TAPP1:
case OP_TAPP2:
case OP_TAPP3:
case OP_TAPP4:
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF;
}
/* If pending IRQ, wait for it to be processed */
if ((pri_latchs[opcode & 0xf] >> f1) & 1) {
iowait = 1;
break;
}
/* If the channel is busy no need to continue */
if (chan_active(opcode & 0xf)) {
iowait = 1;
break;
}
/* Set up channel */
utmp = ((opcode & 0xf) << 8) + f1;
if ((opcode & 0x100) == 0) /* Set priority signal */
utmp |= 0x1000;
tmp = 0;
switch (f2) {
case 0:
switch(MA % 10) {
case 0: tmp = (IO_TRS << 8);
utmp &= 0xfff;
break; /* TSEL */
case 1: tmp = (IO_WEF << 8); break; /* TM */
case 2: tmp = (IO_REW << 8);
utmp &= 0xfff;
break; /* TRW */
case 3: tmp = (IO_RUN << 8);
utmp &= 0xfff;
break; /* TRU */
case 4: tmp = (IO_BSR << 8);
utmp &= 0xfff;
break; /* TRB */
case 5: tmp = (IO_WRS << 8)|CHN_SEGMENT|CHN_ALPHA;
break; /* TSM */
case 6: tmp = (IO_ERG << 8);
utmp &= 0xfff;
break; /* TSK */
case 7: chan_stat(opcode & 0xf, CHS_EOF);
goto done; /* TEF */
case 8: tmp = (IO_SDL << 8);
utmp &= 0xfff;
break; /* TSDL */
case 9: tmp = (IO_SDH << 8);
utmp &= 0xfff;
break; /* TSDH */
}
break;
case 1: tmp = (IO_RDS << 8); break; /* TR */
case 2: tmp = (IO_RDS << 8)|CHN_RECORD;
break; /* TRR */
case 3: tmp = (IO_WRS << 8); break; /* TW */
case 4: tmp = (IO_WRS << 8)|CHN_RECORD;
break; /* TWR */
case 5: tmp = (IO_WRS << 8)|CHN_COMPRESS;
break; /* TWZ */
case 6: tmp = (IO_WRS << 8)|CHN_COMPRESS|CHN_RECORD;
break; /* TWC */
case 7: tmp = (IO_RDS << 8)|CHN_SEGMENT|CHN_ALPHA;
break; /* TSF */
case 8: tmp = (IO_RDS << 8)|CHN_SEGMENT|CHN_RECORD|CHN_ALPHA;
break; /* TSB */
case 9: tmp = (IO_RDS << 8)|CHN_ALPHA;
break; /* TRA */
}
MBR = ((utmp & 0x1000) ? PSIGN:MSIGN) | 0x8000000000LL |
(((t_uint64)f2)<<32);
upd_idx(&MBR, IC);
bin_dec(&MBR, MA, 0, 4);
f = (utmp >> 8) & 0xf;
WriteP(150 + (f * 10) + (utmp & 0xF), MBR);
/* Start off command */
switch(chan_cmd(utmp, tmp, MA)) {
case SCPE_BUSY:
iowait = 1;
break;
case SCPE_IOERR:
reason = STOP_IOCHECK;
break;
case SCPE_OK:
/* Not priority transfer, wait for channel done */
if ((utmp & 0x1000) == 0)
chwait = f;
}
break;
/* Binary tape read channel 1 */
case OP_TRN:
case OP_TRNP:
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF;
}
/* If pending IRQ, wait for it to be processed */
if ((pri_latchs[1] >> f1) & 1) {
iowait = 1;
break;
}
/* If the channel is busy no need to continue */
if (chan_active(1)) {
iowait = 1;
break;
}
/* Set up channel */
utmp = (1 << 8) + f1 + 020; /* Binary mode */
if ((opcode & 0x100) == 0) /* Set priority signal */
utmp |= 0x1000;
switch (f2) {
case 1: tmp = (IO_RDS << 8)|CHN_ALPHA; break;
default: /* Anything else is error */
reason = STOP_UUO;
goto done;
}
MBR = ((utmp & 0x1000) ? PSIGN:MSIGN) | 0x8000000000LL |
(((t_uint64)f2)<<32);
upd_idx(&MBR, IC);
bin_dec(&MBR, MA, 0, 4);
f = (utmp >> 8) & 0xf;
WriteP(150 + (f * 10) + (utmp & 0xF), MBR);
/* Start off command */
switch(chan_cmd(utmp, tmp, MA)) {
case SCPE_BUSY:
iowait = 1;
break;
case SCPE_IOERR:
reason = STOP_IOCHECK;
break;
case SCPE_OK:
/* Not priority transfer, wait for channel done */
if ((utmp & 0x1000) == 0)
chwait = f;
}
break;
case OP_CHNP1:
case OP_CHNP2:
case OP_CHNP3:
case OP_CHNP4:
case OP_CHN1:
case OP_CHN2:
case OP_CHN3:
case OP_CHN4:
/* If the channel is busy no need to continue */
if (chan_active(opcode & 0xf)) {
iowait = 1;
break;
}
utmp = ((opcode & 0xf) << 8) + ((f1 & 3) - 1) + 0x200;
if ((opcode & 0x100) == 0) /* Set priority signal */
utmp |= 0x1000;
/* Build initial status word */
MBR = ((t_uint64)opcode & 0xFF) << 32;
MBR |= (opcode & 0x100)?MSIGN:PSIGN;
upd_idx(&MBR, IC);
bin_dec(&MBR, MA, 0, 4);
tmp = 0xff;
switch (f2) {
case 1: tmp = CHN_COMPRESS; break; /* DCP */
case 2: tmp = 0; break; /* DCUA */
case 3: tmp = CHN_RECORD; break; /* DCUR */
case 4: tmp = CHN_RECORD|CHN_COMPRESS;break;/*DCPR*/
case 6: tmp = CHN_NUM_MODE; break; /* DCU */
}
if (tmp == 0xff)
break;
tmp |= IO_RDS << 8; /* Activate channel */
/* Start off command */
switch(chan_cmd(utmp, tmp, MA)) {
case SCPE_BUSY:
iowait = 1;
break;
case SCPE_IOERR:
reason = STOP_IOCHECK;
break;
case SCPE_OK:
WriteP(350 + ((utmp >> 8) & 0xf) - 4, MBR);
break;
}
if (hst_lnt) { /* history enabled? */
hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF;
}
break;
/* Diagnostics instructions, not fully implimented */
/* All of these errors can not occur on the simulation */
case OP_DIAGT:
if (IX == 99) {
IX = 63;
} else if (IX == 98) {
IX = 63;
diaglatch |= 1LL << 63;
IC = MA;
break;
} else if (IX > 60) {
break;
}
if (diaglatch & (1LL << IX))
IC = MA;
diaglatch &= ~(1LL << IX);
break;
case OP_DIAGR:
if (IX > 60)
break;
if (IX == 0)
diaglatch &= 1LL << 63;
else
diaglatch &= ~(1LL << IX);
break;
case OP_DIAGC:
case OP_DIAGS:
break;
}
}
chan_proc(); /* process any pending channel events */
if (instr_count != 0 && --instr_count == 0)
return SCPE_STEP;
} /* end while */
/* Simulation halted */
return reason;
}
/* Decimal arithmetic routines */
/* Add a to b result in a */
int dec_add(t_uint64 *a, t_uint64 b) {
t_uint64 t1,t2,t3;
t1 = *a ^ b;
t2 = *a + b;
t3 = t2 + 0x6666666666LL;
t2 = ((t2 < *a) || (t3 < t2));
t2 = ((t1 ^ t3) >> 3) | (t2 << 37);
t2 = 0x2222222222LL & ~t2;
t1 = t3 - (3 * t2);
if ((t1 & (~DMASK)) != 0) {
t1 &= DMASK;
*a = t1;
return 1;
}
*a = t1;
return 0;
}
/* Decimal arithmetic routines */
/* Add a to b result in a */
/* Don't detect overflow, and use 2 more guard digits */
void dec_add_noov(t_uint64 *a, t_uint64 b) {
t_uint64 t1,t2,t3;
t1 = *a ^ b;
t2 = *a + b;
t3 = t2 + 0x666666666666LL;
t2 = ((t2 < *a) || (t3 < t2));
t2 = ((t1 ^ t3) >> 3) | (t2 << 45);
t2 = 0x222222222222LL & ~t2;
t1 = t3 - (3 * t2);
*a = t1;
}
/* tens compliment a */
void dec_comp(t_uint64 *a) {
*a = 0x9999999999LL - *a;
dec_add(a, 1LL);
}
/* Compare to words, includeing sign */
int dec_cmp(t_uint64 a, t_uint64 b) {
t_uint64 t1,t2,t3;
a = 0x99999999999LL - a;
t1 = a ^ b;
t2 = a + b;
t3 = t2 + 0x66666666666LL;
t2 = ((t2 < a) || (t3 < t2));
t2 = ((t1 ^ t3) >> 3) | (t2 << 41);
t2 = 0x22222222222LL & ~t2;
t1 = t3 - (3 * t2);
if (t1 == 0x99999999999LL) {
return 0;
}
if ((t1 & ~(SMASK|DMASK)) != 0) {
return 1;
}
return -1;
}
/* Do a multiply step */
void mul_step(t_uint64 *a, t_uint64 b, int c) {
t_uint64 prod;
int i;
for(i = 0; i < 40; i+=4) {
/* Multiply each digit */
prod = ((b >> i) & 0xf) * c;
/* Convert to decimal */
prod = ((prod / 10) << 4) + (prod % 10);
if (prod != 0) {
prod <<= i; /* Move into place */
dec_add_noov(a, prod);
}
}
}
void div_step(t_uint64 b) {
t_uint64 t1,t2,t3;
AC[1] &= DMASK;
AC[1] <<= 4;
AC[1] |= (AC[2] >> 36) & 0xf;
AC[2] <<= 4;
AC[2] &= DMASK;
/* Repeated subtract until overflow */
while((AC[2] & 0xF) != 0x9) {
t1 = AC[1] ^ b;
t2 = AC[1] + b;
t3 = t2 + 0x66666666666LL;
t2 = ((t2 < AC[1]) || (t3 < t2));
t2 = ((t1 ^ t3) >> 3) | (t2 << 41);
t2 = 0x22222222222LL & ~t2;
t1 = t3 - (3 * t2);
if ((t1 & ~DMASK) == 0)
return;
AC[1] = t1 & DMASK;
AC[2] += 1LL;
}
}
/* Convert a binary number to BCD */
void bin_dec(t_uint64 *a, uint32 b, int s, int l) {
s *= 4;
l *= 4;
l += s;
while (s < l) {
*a &= ~(0xFLL << s);
*a |= ((t_uint64)(b % 10)) << s;
b /= 10;
s += 4;
}
}
/* Convert index to binary */
uint32 dec_bin_idx(t_uint64 a) {
uint32 v = (a >> 16) & 0xf;
v += dscale[0][(a >> 20) & 0xf];
v += dscale[1][(a >> 24) & 0xf];
v += dscale[2][(a >> 28) & 0xf];
if (emode)
a += dscale[3][(a >> 32) & 0xf];
return v;
}
uint32 dec_bin_lim(t_uint64 a, uint32 b) {
uint32 v = a & 0xf;
v += dscale[0][(a >> 4) & 0xf];
v += dscale[1][(a >> 8) & 0xf];
v += dscale[2][(a >> 12) & 0xf];
if (emode) {
if (v < b)
v += dscale[3][((a >> 32) & 0xf)+1];
}
return v;
}
/* Extract information from a RDW */
int get_rdw(t_uint64 a, uint32 *base, uint32 *limit) {
*base = dec_bin_idx(a);
*limit = dec_bin_lim(a, *base);
return (a >> 40);
}
void upd_idx(t_uint64 *a, uint32 b) {
bin_dec(a, b, 4, (emode)?5:4);
}
/* Scan for interupt */
int scan_irq() {
int irq = 0;
int tmp;
for(tmp = 0; tmp < 20 && irq == 0; tmp++) {
if ((pri_mask & (1 << tmp)) == 0) {
switch(tmp) {
case 9: /* Unit A */
if (pri_latchs[0] & 0x002) {
pri_latchs[0] &= ~0x002;
irq = 104;
}
break;
case 8: /* Unit B */
if (pri_latchs[0] & 0x004) {
pri_latchs[0] &= ~0x004;
irq = 105;
}
break;
case 7: /* Tape Chan 1 */
if (pri_latchs[1]) {
int i;
for(i = 0; i < 10; i++) {
if (pri_latchs[1] & (1 << i)) {
pri_latchs[1] &= ~(1 << i);
irq = 150 + ((M[110+i] >> 36) & 0xf);
/* Save final record address */
upd_idx(&M[99], 110+i);
M[99] &= DMASK;
M[99] |= PSIGN;
break;
}
}
}
break;
case 6: /* Tape Chan 2 */
if (pri_latchs[2]) {
int i;
for(i = 0; i < 10; i++) {
if (pri_latchs[2] & (1 << i)) {
pri_latchs[2] &= ~(1 << i);
irq = 150 + ((M[120+i] >> 36) & 0xf);
/* Save final record address */
upd_idx(&M[99], 120+i);
M[99] &= DMASK;
M[99] |= PSIGN;
break;
}
}
}
break;
case 5: /* 7300 disk seek */
break; /* Not implimented */
case 4: /* 7300 disk read and write */
break; /* Not implimented */
case 3: /* Inquiry group 1 */
if (pri_latchs[0] & 0x080) {
pri_latchs[0] &= ~0x080;
irq = 106;
}
break;
case 2: /* Inquiry group 2 */
if (pri_latchs[0] & 0x100) {
pri_latchs[0] &= ~0x100;
irq = 107;
}
break;
case 1: /* Tape Chan 3 */
if (pri_latchs[3]) {
int i;
for(i = 0; i < 10; i++) {
if (pri_latchs[3] & (1 << i)) {
pri_latchs[3] &= ~(1 << i);
irq = 150 + ((M[130+i] >> 36) & 0xf);
/* Save final record address */
upd_idx(&M[99], 130+i);
M[99] &= DMASK;
M[99] |= PSIGN;
break;
}
}
}
break;
case 0: /* Tape Chan 4 */
if (pri_latchs[4]) {
int i;
for(i = 0; i < 10; i++) {
if (pri_latchs[4] & (1 << i)) {
pri_latchs[4] &= ~(1 << i);
irq = 150 + ((M[140+i] >> 36) & 0xf);
/* Save final record address */
upd_idx(&M[99], 140+i);
M[99] &= DMASK;
M[99] |= PSIGN;
break;
}
}
}
break;
case 10: /* Channel 1 */
if (pri_latchs[8] & 0x002) {
pri_latchs[8] &= ~0x002;
irq = 311;
upd_idx(&M[99], 301);
M[99] &= DMASK;
M[99] |= PSIGN;
}
break;
case 11: /* Channel 1 Attn */
if (pri_latchs[9] & 0x002) {
pri_latchs[9] &= ~0x002;
irq = 321;
upd_idx(&M[99], 301);
M[99] &= DMASK;
M[99] |= PSIGN;
}
break;
case 12: /* Channel 2 */
if (pri_latchs[8] & 0x004) {
pri_latchs[8] &= ~0x004;
irq = 312;
upd_idx(&M[99], 302);
M[99] &= DMASK;
M[99] |= PSIGN;
}
break;
case 13: /* Channel 2 Attn */
if (pri_latchs[9] & 0x004) {
pri_latchs[9] &= ~0x004;
irq = 322;
upd_idx(&M[99], 302);
M[99] &= DMASK;
M[99] |= PSIGN;
}
break;
case 14: /* Channel 3 */
if (pri_latchs[8] & 0x008) {
pri_latchs[8] &= ~0x008;
irq = 313;
upd_idx(&M[99], 303);
M[99] &= DMASK;
M[99] |= PSIGN;
}
break;
case 15: /* Channel 3 Attn */
if (pri_latchs[9] & 0x008) {
pri_latchs[9] &= ~0x008;
irq = 323;
upd_idx(&M[99], 303);
M[99] &= DMASK;
M[99] |= PSIGN;
}
break;
case 16: /* Channel 4 */
if (pri_latchs[8] & 0x010) {
pri_latchs[8] &= ~0x010;
irq = 314;
upd_idx(&M[99], 304);
M[99] &= DMASK;
M[99] |= PSIGN;
}
break;
case 17: /* Channel 4 Attn */
if (pri_latchs[9] & 0x010) {
pri_latchs[9] &= ~0x010;
irq = 324;
upd_idx(&M[99], 304);
M[99] &= DMASK;
M[99] |= PSIGN;
}
break;
}
}
}
return irq;
}
/* Initialize memory to all plus zero */
void
mem_init() {
int i;
/* Force memory to have all valid signs */
for(i = 0; i < MAXMEMSIZE; i++)
M[i] = PSIGN;
}
/* Reset routine */
t_stat
cpu_reset(DEVICE * dptr)
{
AC[1] = PSIGN;
AC[2] = PSIGN;
AC[3] = PSIGN;
inds = PSIGN;
pri_enb = 1;
sim_brk_types = sim_brk_dflt = SWMASK('E');
if (cpu_unit.flags & OPTION_TIMER) {
sim_rtcn_init_unit (&cpu_unit, cpu_unit.wait, TMR_RTC);
sim_activate(&cpu_unit, cpu_unit.wait);
}
return SCPE_OK;
}
/* Interval timer routines */
t_stat
rtc_srv(UNIT * uptr)
{
if (cpu_unit.flags & OPTION_TIMER) {
timer_clock++;
if (timer_clock == 300) {
t_uint64 t = (t_uint64) timer;
dec_add(&t, 1);
timer = t & 0xfff;
timer_clock = 0;
}
sim_activate(&cpu_unit, sim_rtcn_calb(uptr->wait, TMR_RTC));
}
return SCPE_OK;
}
t_stat
rtc_reset(DEVICE * dptr)
{
if (cpu_unit.flags & OPTION_TIMER) {
sim_activate(&cpu_unit, cpu_unit.wait);
}
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];
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;
return SCPE_OK;
}
t_stat
cpu_set_size(UNIT * uptr, int32 val, CONST char *cptr, void *desc)
{
t_uint64 mc = 0;
uint32 i;
int32 v;
v = val >> UNIT_V_MSIZE;
v = (v + 1) * 5000;
if ((v <= 0) || (v > MAXMEMSIZE))
return SCPE_ARG;
for (i = v-1; i < MEMSIZE; i++) {
if (M[i] != PSIGN) {
mc = 1;
break;
}
}
if ((mc != 0) && (!get_yn("Really truncate memory [N]?", FALSE)))
return SCPE_OK;
cpu_unit.flags &= ~UNIT_MSIZE;
cpu_unit.flags |= val;
MEMSIZE = v;
for (i = MEMSIZE; i < MAXMEMSIZE; i++)
M[i] = PSIGN;
return SCPE_OK;
}
/* Handle execute history */
/* Set history */
t_stat
cpu_set_hist(UNIT * uptr, int32 val, CONST char *cptr, void *desc)
{
int32 i, lnt;
t_stat r;
if (cptr == NULL) {
for (i = 0; i < hst_lnt; i++)
hst[i].ic = 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 = (struct InstHistory *)calloc(sizeof(struct InstHistory), lnt);
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, CONST void *desc)
{
int32 k, di, lnt;
char *cptr = (char *) desc;
t_stat r;
t_value sim_eval;
struct InstHistory *h;
if (hst_lnt == 0)
return SCPE_NOFNC; /* enabled? */
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, "IC EA BEFORE AFTER INST\n\n");
for (k = 0; k < lnt; k++) { /* print specified */
h = &hst[(++di) % hst_lnt]; /* entry pointer */
if (h->ic & HIST_PC) { /* instruction? */
fprintf(st, "%05d ", h->ic & 0xffff);
if (h->ic & HIST_NOEA)
fputs(" ", st);
else
fprintf(st, " %05d ", h->ea);
if (h->ic & HIST_NOBEF)
fputs(" ", st);
else {
switch (h->before & SMASK) {
case PSIGN: fputc('+', st); break;
case MSIGN: fputc('-', st); break;
case ASIGN: fputc('@', st); break;
default: fputc('#', st); break;
}
fprint_val(st, h->before & DMASK, 16, 40, PV_RZRO);
}
fputc(' ', st);
if (h->ic & HIST_NOAFT)
fputs(" ", st);
else {
switch (h->after & SMASK) {
case PSIGN: fputc('+', st); break;
case MSIGN: fputc('-', st); break;
case ASIGN: fputc('@', st); break;
default: fputc('#', st); break;
}
fprint_val(st, h->after & DMASK, 16, 40, PV_RZRO);
}
fputc(' ', st);
sim_eval = h->op;
if (
(fprint_sym(st, h->ic & AMASK, &sim_eval, &cpu_unit,
SWMASK('M'))) > 0) fputs("(undefined)", st);
fputc('\n', st); /* end line */
} /* end else instruction */
} /* end for */
return SCPE_OK;
}
t_stat
cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) {
fprintf (st, "The CPU can be set to a IBM 7070 or IBM 7074\n");
fprintf (st, "The type of CPU can be set by one of the following commands\n\n");
fprintf (st, " sim> set CPU 7070 sets IBM 7070 emulation\n");
fprintf (st, " sim> set CPU 7074 sets IBM 7074 emulation\n\n");
fprintf (st, "These switches are recognized when examining or depositing in CPU memory:\n\n");
fprintf (st, " -c examine/deposit characters, 5 per word\n");
fprintf (st, " -m examine/deposit IBM 7070 instructions\n\n");
fprintf (st, "The memory of the CPU can be set in 5K incrememts from 5K to 30K with the\n\n");
fprintf (st, " sim> SET CPU xK\n\n");
fprintf (st, "For the IBM 7070 the following options can be enabled\n\n");
fprintf (st, " sim> SET CPU FLOAT enables Floating Point\n");
fprintf (st, " sim> SET CPU NOFLOAT disables Floating Point\n\n");
fprintf (st, " sim> SET CPU EXTEND enables extended memory\n");
fprintf (st, " sim> SET CPU NOEXTEND disables extended memory\n\n");
fprintf (st, " sim> SET CPU CLOCK enables timer clock\n");
fprintf (st, " sim> SET CPU NOCLOCK disables timer clock\n\n");
fprintf (st, "The CPU can maintain a history of the most recently executed instructions.\n");
fprintf (st, "This is controlled by the SET CPU HISTORY and SHOW CPU HISTORY commands:\n\n");
fprintf (st, " sim> SET CPU HISTORY clear history buffer\n");
fprintf (st, " sim> SET CPU HISTORY=0 disable history\n");
fprintf (st, " sim> SET CPU HISTORY=n{:file} enable history, length = n\n");
fprintf (st, " sim> SHOW CPU HISTORY print CPU history\n");
fprint_set_help(st, dptr);
fprint_show_help(st, dptr);
return SCPE_OK;
}
const char *
cpu_description (DEVICE *dptr) {
return "IBM 7070 CPU";
}