/* i701_cpu.c: IBM 701 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 701 central processor | |
The IBM 701 also know as "Defense Calculator" was introduced by IBM | |
on April 7, 1953. This computer was start of IBM 700 and 7000 line. | |
Memory was 2048 36 bit words. Each instruction could be signed plus | |
or minus, plus would access memory as 18 bit words, minus as 36 bit | |
words. There was a expansion option to add another 2048 words of | |
memory, but I can't find documentation on how it worked. Memory cycle | |
time was 12 microseconds. The 701 was withdrawn from the market | |
October 1, 1954 replaced by 704 and 702. A total of 19 machines were | |
installed. | |
The system state for the IBM 701 is: | |
AC<S,P,Q,1:35> AC register | |
MQ<S,1:35> MQ register | |
IC<0:15> program counter | |
SSW<0:5> sense switches | |
SLT<0:3> sense lights | |
ACOVF AC overflow | |
DVC divide check | |
IOC I/O check | |
The 701 had one instruction format: memory reference, | |
00000 000011111111 | |
S 12345 678901234567 | |
+-+-----+------------+ | |
| |opcod| address | memory reference | |
+-+-----+------------+ | |
This routine is the instruction decode routine for the 701. | |
It is called from the simulator control program to execute | |
instructions in simulated memory, starting at the simulated PC. | |
It runs until a stop condition occurs. | |
General notes: | |
1. Reasons to stop. The simulator can be stopped by: | |
HALT instruction | |
illegal instruction | |
illegal I/O operation for device | |
illegal I/O operation for channel | |
breakpoint encountered | |
nested XEC's exceeding limit | |
divide check | |
I/O error in I/O simulator | |
3. Arithmetic. The 701 uses signed magnitude arithmetic for | |
integer and floating point calculations, and 2's complement | |
arithmetic for indexing calculations. | |
4. Adding I/O devices. These modules must be modified: | |
i7090_defs.h add device definitions | |
i7090_chan.c add channel subsystem | |
i701_sys.c add sim_devices table entry | |
*/ | |
#include "i7090_defs.h" | |
#define HIST_XCT 1 /* instruction */ | |
#define HIST_INT 2 /* interrupt cycle */ | |
#define HIST_TRP 3 /* trap cycle */ | |
#define HIST_MIN 64 | |
#define HIST_MAX 65536 | |
#define HIST_NOEA 0x40000000 | |
#define HIST_PC 0x10000 | |
struct InstHistory | |
{ | |
t_int64 ac; | |
t_int64 mq; | |
t_int64 op; | |
t_int64 sr; | |
uint32 ic; | |
uint16 ea; | |
}; | |
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); | |
t_uint64 M[MAXMEMSIZE] = { 0 }; /* memory */ | |
t_uint64 AC, MQ; /* registers */ | |
uint16 IC; /* program counter */ | |
uint8 SL; /* Sense lights */ | |
uint8 SW = 0; /* Sense switch */ | |
uint8 dcheck; /* Divide check */ | |
uint8 acoflag; /* AC Overflow */ | |
uint8 ihold = 0; /* Hold interrupts */ | |
uint16 iotraps; /* IO trap flags */ | |
t_uint64 ioflags; /* Trap enable flags */ | |
uint8 iocheck; | |
uint8 iowait; /* Waiting on io */ | |
uint8 dualcore; /* Set to true if dual core in | |
use */ | |
uint16 dev_pulse[NUM_CHAN]; /* SPRA device pulses */ | |
int cycle_time = 120; /* Cycle time of 12us */ | |
/* History information */ | |
int32 hst_p = 0; /* History pointer */ | |
int32 hst_lnt = 0; /* History length */ | |
struct InstHistory *hst = NULL; /* History stack */ | |
extern uint32 drum_addr; | |
uint32 hsdrm_addr; | |
extern UNIT chan_unit[]; | |
#undef AMASK /* Change definition of AMASK here */ | |
#define AMASK 00000000007777L | |
/* 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(NULL, UNIT_BINK, MAXMEMSIZE / 2) }; | |
REG cpu_reg[] = { | |
{ORDATAD(IC, IC, 15, "Instruction counter"), REG_FIT}, | |
{ORDATAD(AC, AC, 38, "Accumulator"), REG_FIT}, | |
{ORDATAD(MQ, MQ, 36, "Multiplier quotent"), REG_FIT}, | |
{ORDATAD(SL, SL, 4, "Lights"), REG_FIT}, | |
{ORDATAD(SW, SW, 6, "Switch register"), REG_FIT}, | |
{FLDATAD(SW1, SW, 0, "Switch 0"), REG_FIT}, | |
{FLDATAD(SW2, SW, 1, "Switch 1"), REG_FIT}, | |
{FLDATAD(SW3, SW, 2, "Switch 2"), REG_FIT}, | |
{FLDATAD(SW4, SW, 3, "Switch 3"), REG_FIT}, | |
{FLDATAD(SW5, SW, 4, "Switch 4"), REG_FIT}, | |
{FLDATAD(SW6, SW, 5, "Switch 5"), REG_FIT}, | |
{ORDATAD(ACOVF, acoflag, 1, "Overflow flag"), REG_FIT}, | |
{ORDATAD(IOC, iocheck, 1, "I/O Check flag"), REG_FIT}, | |
{ORDATAD(DVC, dcheck, 1, "Divide Check"), REG_FIT}, | |
{NULL} | |
}; | |
MTAB cpu_mod[] = { | |
{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, 8, 15, 1, 8, 36, | |
&cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, | |
NULL, 0, 0, NULL, | |
NULL, NULL, &cpu_help, NULL, NULL, &cpu_description | |
}; | |
/* Simulate instructions */ | |
t_stat | |
sim_instr(void) | |
{ | |
t_stat reason; | |
t_uint64 temp = 0; | |
t_uint64 ibr; | |
t_uint64 SR; | |
uint16 opcode; | |
uint16 MA; | |
uint8 f; | |
int shiftcnt; | |
int stopnext = 0; | |
int instr_count = 0; /* Number of instructions to execute */ | |
if (sim_step != 0) { | |
instr_count = sim_step; | |
sim_cancel_step(); | |
} | |
reason = 0; | |
iowait = 0; | |
while (reason == 0) { /* loop until halted */ | |
/* If doing fast I/O don't sit in idle loop */ | |
if (iowait == 0 && stopnext) | |
return SCPE_STEP; | |
if (sim_interval <= 0) { /* event queue? */ | |
reason = sim_process_event(); | |
if (reason != SCPE_OK) { | |
if (reason == SCPE_STEP && iowait) | |
stopnext = 1; | |
else | |
break; /* process */ | |
} | |
} | |
if (iowait == 0 && sim_brk_summ && sim_brk_test(IC, SWMASK('E'))) { | |
reason = STOP_IBKPT; | |
break; | |
} | |
/* Split out current instruction */ | |
if (iowait) { | |
/* If we are awaiting I/O complete, don't fetch. */ | |
sim_interval -= 6; | |
iowait = 0; | |
} else { | |
MA = IC >> 1; | |
sim_interval -= 24; /* count down */ | |
SR = ReadP(MA); | |
temp = SR; | |
if ((IC & 1) == 0) | |
temp >>= 18; | |
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].ea = 0; | |
hst[hst_p].op = temp & RMASK; | |
hst[hst_p].ac = AC; | |
hst[hst_p].mq = MQ; | |
hst[hst_p].sr = 0; | |
} | |
IC = (IC + 1) & AMASK; | |
} | |
ihold = 0; | |
opcode = ((uint16)(temp >> 12L)) & 077; | |
MA = (uint16)(temp & AMASK); | |
ibr = SR = ReadP(MA>>1); | |
if ((opcode & 040) == 0) { | |
if (MA & 1) | |
SR <<= 18; | |
SR &= LMASK; | |
} | |
if (hst_lnt) { /* history enabled? */ | |
hst[hst_p].sr = SR; | |
hst[hst_p].ea = MA; | |
} | |
switch (opcode & 037) { | |
case 19: /* RND */ | |
if (MQ & ONEBIT) | |
AC++; | |
break; | |
case 30: /* SENSE */ | |
switch (MA) { | |
case 64: /* SLN */ | |
SL = 0; | |
break; | |
case 65: /* */ | |
SL |= 1; | |
break; | |
case 66: | |
SL |= 2; | |
break; | |
case 67: | |
SL |= 4; | |
break; | |
case 68: | |
SL |= 8; | |
break; | |
case 69: | |
if ((SW & 1) == 0) | |
IC++; | |
break; | |
case 70: | |
if ((SW & 2) == 0) | |
IC++; | |
break; | |
case 71: | |
if ((SW & 4) == 0) | |
IC++; | |
break; | |
case 72: | |
if ((SW & 8) == 0) | |
IC++; | |
break; | |
case 73: | |
if ((SW & 16) == 0) | |
IC++; | |
break; | |
case 74: | |
if ((SW & 32) == 0) | |
IC++; | |
break; | |
case 1024: | |
case 1025: | |
MA -= 1024; | |
dev_pulse[0] |= 1 << MA; | |
break; | |
case 522: | |
if (dev_pulse[0] & PRINT_I) | |
IC++; | |
dev_pulse[0] &= ~PRINT_I; | |
break; | |
case 512: | |
case 513: | |
case 514: | |
case 515: | |
case 516: | |
case 517: | |
case 518: | |
case 519: | |
case 520: | |
case 521: | |
MA = (MA - 512) + 5; | |
dev_pulse[0] |= 1 << MA; | |
break; | |
} | |
break; | |
case 0: /* STOP */ | |
/* Stop at HTR instruction if trapped */ | |
IC--; | |
halt: | |
reason = STOP_HALT; | |
/* Clear off any pending events before we halt */ | |
do { | |
f = chan_active(0); | |
chan_proc(); | |
for (shiftcnt = 1; shiftcnt < NUM_CHAN; shiftcnt++) { | |
f |= chan_active(shiftcnt); | |
} | |
sim_interval = 0; | |
(void)sim_process_event(); | |
} while (f); | |
if (reason != 0) | |
IC = MA; | |
break; | |
case 8: /* NO OP */ | |
break; | |
case 1: /* TR */ | |
IC = MA; | |
break; | |
case 4: /* TR 0 */ | |
f = (AC & AMMASK) == 0; | |
branch: | |
if (f) { | |
IC = MA; | |
} | |
break; | |
case 2: /* TR OV */ | |
f = acoflag; | |
acoflag = 0; | |
goto branch; | |
case 3: /* TR + */ | |
f = ((AC & AMSIGN) == 0); | |
goto branch; | |
case 10: /* R ADD */ | |
AC = ((SR & MSIGN) << 2) | (SR & PMASK); | |
sim_interval -= 6; | |
break; | |
case 6: /* R SUB */ | |
AC = (((SR & MSIGN) ^ MSIGN) << 2) | (SR & PMASK); | |
sim_interval -= 6; | |
break; | |
case 15: /* LOAD MQ */ | |
MQ = SR; | |
sim_interval -= 6; | |
break; | |
case 14: /* STORE MQ */ | |
SR = MQ; | |
goto store; | |
case 12: /* STORE */ | |
SR = AC & PMASK; | |
if (AC & AMSIGN) | |
SR |= MSIGN; | |
store: | |
if ((opcode & 040) == 0) { | |
SR &= LMASK; | |
if (MA & 1) { | |
ibr &= LMASK; | |
SR >>= 18; | |
} else { | |
ibr &= RMASK; | |
} | |
SR |= ibr; | |
} | |
WriteP(MA>>1, SR); | |
if (hst_lnt) { /* history enabled? */ | |
hst[hst_p].sr = SR; | |
} | |
sim_interval -= 6; | |
break; | |
/* Logic operations */ | |
case 13: /* EXTR or STORE A */ | |
if ((opcode & 040) == 0) { | |
SR &= ~(AMASK << 18); | |
SR |= AC & (AMASK << 18); | |
} else { | |
t_uint64 t = AC & PMASK; | |
if (AC & AMSIGN) | |
t |= MSIGN; | |
SR &= t; | |
} | |
goto store; | |
case 7: /* SUB AB */ | |
SR |= MSIGN; | |
goto iadd; | |
case 11: /* ADD AB */ | |
SR &= PMASK; | |
goto iadd; | |
case 5: /* SUB */ | |
SR ^= MSIGN; | |
/* Fall through */ | |
case 9: /* ADD */ | |
iadd: | |
f = 0; | |
/* Make AC Positive */ | |
if (AC & AMSIGN) { | |
f = 2; | |
AC &= AMMASK; | |
} | |
if (AC & APSIGN) | |
f |= 8; | |
/* Check signes of SR & AC */ | |
if (((SR & MSIGN) && ((f & 2) == 0)) || | |
(((SR & MSIGN) == 0) && ((f & 2) != 0))) { | |
AC ^= AMMASK; /* One's compliment */ | |
f |= 1; | |
} | |
AC = AC + (SR & PMASK); | |
/* Check carry from Q */ | |
if (f & 1) { /* Check if signs were not same */ | |
if (AC & AMSIGN) { | |
f ^= 2; | |
AC++; | |
if (((AC & APSIGN) != 0) != ((f & 8) != 0)) | |
acoflag = 1; | |
} else { | |
AC ^= AMMASK; /* One's compliment */ | |
} | |
} else { | |
if (((AC & APSIGN) != 0) != ((f & 8) != 0)) | |
acoflag = 1; | |
} | |
/* Restore sign to AC */ | |
AC &= AMMASK; | |
if (f & 2) | |
AC |= AMSIGN; | |
sim_interval -= 6; | |
break; | |
case 16: /* MPY */ | |
case 17: /* MPY R */ | |
shiftcnt = 043; | |
sim_interval -= 34 * 6; | |
f = 0; | |
/* Save sign */ | |
if (MQ & MSIGN) | |
f |= 1; | |
if (SR & MSIGN) | |
f |= 2; | |
SR &= PMASK; | |
MQ &= PMASK; | |
AC = 0; /* Clear AC */ | |
if (SR == 0) { | |
MQ = 0; | |
} else { | |
while (shiftcnt-- > 0) { | |
if (MQ & 1) | |
AC += SR; | |
MQ >>= 1; | |
if (AC & 1) | |
MQ |= ONEBIT; | |
AC >>= 1; | |
} | |
} | |
if ((opcode & 037) == 17 && MQ & ONEBIT) | |
AC++; | |
if (f & 2) | |
f ^= 1; | |
if (f & 1) { | |
MQ |= MSIGN; | |
AC |= AMSIGN; | |
} | |
break; | |
case 18: /* DIV */ | |
shiftcnt = 043; | |
sim_interval -= 34 * 6; | |
/* Save sign */ | |
if (SR & MSIGN) { | |
SR &= PMASK; | |
f = 1; | |
} else | |
f = 0; | |
if (AC & AMSIGN) | |
f |= 2; | |
/* Check if SR less then AC */ | |
if (((SR - (AC & AMMASK)) & AMSIGN) || | |
(SR == (AC & AMMASK))) { | |
dcheck = 1; | |
MQ &= PMASK; | |
if (f == 1 || f == 2) | |
MQ |= MSIGN; | |
goto halt; | |
} | |
/* Clear signs */ | |
MQ &= PMASK; | |
AC &= AMMASK; | |
/* Do divide operation */ | |
do { | |
AC <<= 1; | |
AC &= AMMASK; | |
MQ <<= 1; | |
if (MQ & MSIGN) { | |
MQ ^= MSIGN; | |
AC |= 1; | |
} | |
if (SR <= AC) { | |
AC -= SR; | |
MQ |= 1; | |
} | |
} while (--shiftcnt != 0); | |
switch (f) { | |
case 0: | |
break; | |
case 3: | |
AC |= AMSIGN; | |
break; | |
case 2: | |
AC |= AMSIGN; | |
/* FALL THRU */ | |
case 1: | |
MQ |= MSIGN; | |
break; | |
} | |
break; | |
/* Shift */ | |
case 20: /* L LEFT */ | |
shiftcnt = MA & 0377; | |
sim_interval -= (shiftcnt >> 6) * 6; | |
/* Save sign */ | |
if (MQ & MSIGN) | |
f = 1; | |
else | |
f = 0; | |
/* Clear it for now */ | |
AC &= AQMASK; | |
while (shiftcnt-- > 0) { | |
MQ <<= 1; | |
AC <<= 1; | |
if (MQ & MSIGN) | |
AC |= 1; | |
if (AC & APSIGN) | |
acoflag = 1; | |
} | |
/* Restore sign when done */ | |
AC &= AMMASK; | |
MQ &= PMASK; | |
if (f) { | |
AC |= AMSIGN; | |
MQ |= MSIGN; | |
} | |
break; | |
case 21: /* L RIGHT */ | |
shiftcnt = MA & 0377; | |
sim_interval -= (shiftcnt >> 6) * 6; | |
/* Save sign */ | |
if (AC & AMSIGN) | |
f = 1; | |
else | |
f = 0; | |
/* Clear it for now */ | |
AC &= AMMASK; | |
MQ &= PMASK; | |
while (shiftcnt-- > 0) { | |
if (AC & 1) | |
MQ |= MSIGN;; | |
MQ >>= 1; | |
AC >>= 1; | |
} | |
/* Restore sign when done */ | |
AC &= AMMASK; | |
if (f) { | |
AC |= AMSIGN; | |
MQ |= MSIGN; | |
} | |
break; | |
case 22: /* A LEFT */ | |
shiftcnt = MA & 0377; | |
sim_interval -= (shiftcnt >> 6) * 6; | |
/* Save sign */ | |
if (AC & AMSIGN) | |
f = 1; | |
else | |
f = 0; | |
/* Clear it for now */ | |
AC &= AQMASK; | |
while (shiftcnt-- > 0) { | |
AC <<= 1; | |
if (AC & APSIGN) | |
acoflag = 1; | |
} | |
/* Restore sign and overflow when done */ | |
AC &= AMMASK; | |
if (f) | |
AC |= AMSIGN; | |
break; | |
case 23: /* A RIGHT */ | |
shiftcnt = MA & 0377; | |
sim_interval -= (shiftcnt >> 6) * 6; | |
/* Save sign */ | |
if (AC & AMSIGN) | |
f = 1; | |
else | |
f = 0; | |
/* Clear it for now */ | |
AC &= AMMASK; | |
AC >>= shiftcnt; | |
/* Restore sign when done */ | |
if (f) | |
AC |= AMSIGN; | |
break; | |
/* 704 Input output Instructions */ | |
case 29: /* SET DR */ | |
if (chan_test(0, DEV_SEL)) { | |
drum_addr = (uint32)MA; | |
chan_clear(0, DEV_FULL); /* Incase something got | |
read while waiting */ | |
} else | |
iocheck = 1; | |
break; | |
case 31: /* COPY */ | |
/* If no channel, set Iocheck and treat as nop */ | |
if (chan_unit[0].flags & UNIT_DIS) { | |
iocheck = 1; | |
break; | |
} | |
/* If device disconnecting, just wait */ | |
if (chan_test(0, DEV_DISCO)) { | |
iowait = 1; | |
break; | |
} | |
/* Instruct is NOP first time */ | |
/* Incomplete last word leaves result in MQ */ | |
if (chan_select(0)) { | |
extern uint8 bcnt[NUM_CHAN]; | |
chan_set(0, STA_ACTIVE); | |
switch (chan_flags[0] & (DEV_WRITE | DEV_FULL)) { | |
case DEV_WRITE | DEV_FULL: | |
case 0: | |
/* On EOR skip 1, on EOF skip two */ | |
if (chan_test(0, CHS_EOF|CHS_EOT|DEV_REOR)) | |
chan_set(0, DEV_DISCO); | |
iowait = 1; | |
break; | |
case DEV_WRITE: | |
MQ = assembly[0] = SR; | |
bcnt[0] = 6; | |
chan_set(0, DEV_FULL); | |
break; | |
case DEV_FULL: | |
SR = MQ; | |
WriteP(MA, MQ); | |
bcnt[0] = 6; | |
chan_clear(0, DEV_FULL); | |
break; | |
} | |
} else { | |
if (chan_stat(0, CHS_EOF|CHS_EOT)) { | |
IC++; | |
/* On EOR skip two */ | |
} else if (chan_stat(0, DEV_REOR)) { | |
IC += 2; | |
/* Advance 1 on Error and set iocheck */ | |
} else if (chan_stat(0, CHS_ERR)) { | |
iocheck = 1; | |
IC++; | |
} | |
chan_clear(0, STA_ACTIVE|DEV_REOR|CHS_ERR); | |
break; | |
} | |
break; | |
/* Input/Output Instuctions */ | |
case 24: /* Read select */ | |
opcode = IO_RDS; | |
MQ = 0; | |
goto iostart; | |
case 26: /* Write select */ | |
opcode = IO_WRS; | |
goto iostart; | |
case 27: /* Write EOF */ | |
opcode = IO_WEF; | |
goto iostart; | |
case 25: /* Read Backwards */ | |
opcode = IO_RDB; | |
MQ = 0; | |
goto iostart; | |
case 28: /* Rewind */ | |
opcode = IO_REW; | |
iostart: | |
switch (chan_cmd(MA, opcode)) { | |
case SCPE_BUSY: | |
iowait = 1; /* Channel is active, hold */ | |
case SCPE_OK: | |
ihold = 1; /* Hold interupts for one */ | |
break; | |
case SCPE_IOERR: | |
iocheck = 1; | |
break; | |
case SCPE_NODEV: | |
reason = STOP_IOCHECK; | |
break; | |
} | |
break; | |
default: | |
reason = STOP_UUO; | |
break; | |
} | |
chan_proc(); /* process any pending channel events */ | |
if (instr_count != 0 && --instr_count == 0) | |
return SCPE_STEP; | |
} /* end while */ | |
/* Simulation halted */ | |
return reason; | |
} | |
/* Reset routine */ | |
t_stat | |
cpu_reset(DEVICE * dptr) | |
{ | |
AC = 0; | |
MQ = 0; | |
dualcore = 0; | |
iotraps = 0; | |
ioflags = 0; | |
dcheck = acoflag = iocheck = 0; | |
sim_brk_types = sim_brk_dflt = SWMASK('E'); | |
return SCPE_OK; | |
} | |
/* Memory examine */ | |
t_stat | |
cpu_ex(t_value * vptr, t_addr addr, UNIT * uptr, int32 sw) | |
{ | |
if (addr >= (MEMSIZE * 2)) | |
return SCPE_NXM; | |
if (vptr == NULL) | |
return SCPE_OK; | |
*vptr = M[(addr & 07777) >> 1]; | |
if ((addr & 0400000) == 0) { | |
if ( addr & 1) | |
*vptr <<= 18; | |
else | |
*vptr &= LMASK; | |
} | |
*vptr &= 0777777777777L; | |
return SCPE_OK; | |
} | |
/* Memory deposit */ | |
t_stat | |
cpu_dep(t_value val, t_addr addr, UNIT * uptr, int32 sw) | |
{ | |
t_addr a = (addr >> 1) & 03777; | |
if (addr >= (MEMSIZE * 2)) | |
return SCPE_NXM; | |
if ((addr & 0400000) == 0) { | |
if (addr & 1) { | |
M[a] &= LMASK; | |
M[a] |= (val >> 18) & RMASK; | |
} else { | |
M[a] &= RMASK; | |
M[a] |= val & LMASK; | |
} | |
} else | |
M[a] = val & 0777777777777L; | |
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 AC MQ EA SR\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, "%06lo ", h->ic & AMASK); | |
switch (h->ac & (AMSIGN | AQSIGN | APSIGN)) { | |
case AMSIGN | AQSIGN | APSIGN: | |
fprintf(st, "-QP"); | |
break; | |
case AMSIGN | AQSIGN: | |
fprintf(st, " -Q"); | |
break; | |
case AMSIGN | APSIGN: | |
fprintf(st, " -P"); | |
break; | |
case AMSIGN: | |
fprintf(st, " -"); | |
break; | |
case AQSIGN | APSIGN: | |
fprintf(st, " QP"); | |
break; | |
case AQSIGN: | |
fprintf(st, " Q"); | |
break; | |
case APSIGN: | |
fprintf(st, " P"); | |
break; | |
case 0: | |
fprintf(st, " "); | |
break; | |
} | |
fprint_val(st, h->ac & PMASK, 8, 35, PV_RZRO); | |
fputc(' ', st); | |
if (h->mq & MSIGN) | |
fputc('-', st); | |
else | |
fputc(' ', st); | |
fprint_val(st, h->mq & PMASK, 8, 35, PV_RZRO); | |
fputc(' ', st); | |
fprint_val(st, h->ea, 8, 12, PV_RZRO); | |
fputc(' ', st); | |
if (h->sr & MSIGN) | |
fputc('-', st); | |
else | |
fputc(' ', st); | |
fprint_val(st, h->sr & PMASK, 8, 35, PV_RZRO); | |
fputc(' ', st); | |
sim_eval = h->op; | |
if ( | |
(fprint_sym | |
(st, h->ic & AMASK, &sim_eval, &cpu_unit, | |
SWMASK('M'))) > 0) fprintf(st, "(undefined) %012llo", | |
h->op); | |
fputc('\n', st); /* end line */ | |
} /* end else instruction */ | |
} /* end for */ | |
return SCPE_OK; | |
} | |
const char * | |
cpu_description (DEVICE *dptr) | |
{ | |
return "IBM 701 CPU"; | |
} | |
t_stat | |
cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
{ | |
fprintf (st, "The CPU behaves as a IBM 701\n"); | |
fprintf (st, "These switches are recognized when examining or depositing in CPU memory:\n\n"); | |
fprintf (st, " -c examine/deposit characters, 6 per word\n"); | |
fprintf (st, " -l examine/deposit half words\n"); | |
fprintf (st, " -m examine/deposit IBM 701 instructions\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; | |
} | |