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