/* 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"; | |
} | |