| /************************************************************************* | |
| * * | |
| * $Id: tx0_cpu.c 2066 2009-02-27 15:57:22Z hharte $ * | |
| * * | |
| * Copyright (c) 2009-2012 Howard M. Harte. * | |
| * Based on pdp1_cpu.c, Copyright (c) 1993-2007, Robert M. Supnik * | |
| * * | |
| * Permission is hereby granted, free of charge, to any person obtaining * | |
| * a copy of this software and associated documentation files (the * | |
| * "Software"), to deal in the Software without restriction, including * | |
| * without limitation the rights to use, copy, modify, merge, publish, * | |
| * distribute, sublicense, and/or sell copies of the Software, and to * | |
| * permit persons to whom the Software is furnished to do so, subject to * | |
| * the following conditions: * | |
| * * | |
| * The above copyright notice and this permission notice shall be * | |
| * included in all copies or substantial portions of the Software. * | |
| * * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * | |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * | |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * | |
| * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * | |
| * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * | |
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * | |
| * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * | |
| * * | |
| * Except as contained in this notice, the name of Howard M. Harte shall * | |
| * not be used in advertising or otherwise to promote the sale, use or * | |
| * other dealings in this Software without prior written authorization * | |
| * of Howard M. Harte. * | |
| * * | |
| * Module Description: * | |
| * TX-0 Central Processor * | |
| * * | |
| * Environment: * | |
| * User mode only * | |
| * * | |
| * References: * | |
| * See: www.bitsavers.org/pdf/mit/tx-0/ for documentation. * | |
| * See: www.bitsavers.org/bits/MIT/tx-0/ for software. * | |
| *************************************************************************/ | |
| /* | |
| The original Lincoln Labs TX-0 had only two bits of opcode and no index | |
| register. The machine was moved to room 26-248 at MIT in July 1958 and | |
| after about a year and a half the opcode field was extended to four bits | |
| and an index register was added. | |
| (ref. Computer Museum Report Vol 8, Spring 1984) | |
| -------------------------------------------------------------- | |
| Original TX-0 Registers and Instruction Set | |
| from "A Functional Description of the TX-0 Computer" Oct, 1958 | |
| -------------------------------------------------------------- | |
| The register state for the TX-0 is: | |
| MBR[0:17] Memory Buffer Register (18 bits) | |
| AC[0:17] Accumulator (18 bits) | |
| MAR[0:15] Memory Address Register (16 bits) | |
| PC[0:15] Program Counter (16 bits) | |
| IR[0:1] Instruction Register (2 bits) | |
| LR[0:17] Live Register (18 bits) | |
| TBR[0:17] Toggle Switch Buffer Register (18 toggle switches) | |
| TAC[0:17] Toggle Switch Accumulator (18 toggle switches) | |
| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
| +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| | op | address | | |
| +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |
| This routine is the instruction decode routine for the TX-0. | |
| It is called from the simulator control program to execute | |
| instructions in simulated memory, starting at the simulated PC. | |
| It runs until 'reason' is set non-zero. | |
| General notes: | |
| 1. Instruction Set: The TX-0 had (at least) two different instruction sets. | |
| The original instruction set from 1956 had two bits for the opcode (IR) | |
| while the later 1961 instruction set used five bits for this purpose. | |
| This simulator is designed to simulate both of these modes. The micro | |
| orders were also changed along with the new instruction set. The main | |
| part of the instruction fetch/decode logic is the same for both | |
| instruction sets. Memory address range is adjusted depending on the mode. | |
| IR[2:4] are forced to 0 in the 1956 mode. The Micro Orders are vastly | |
| different, so the main loop decodes and executes the 1961 micro-orders, | |
| while the sim_opr_orig() function decodes and executes the 1956 micro-orders. | |
| 2. Reasons to stop. The simulator can be stopped by: | |
| HALT instruction | |
| breakpoint encountered | |
| I/O error in I/O simulator | |
| 3. Arithmetic. The TX-0 is a 1's complement system. In 1's | |
| complement arithmetic, a negative number is represented by the | |
| complement (XOR 0777777) of its absolute value. Addition of 1's | |
| complement numbers requires propagating the carry out of the high | |
| order bit back to the low order bit. | |
| 4. Adding I/O devices. Three modules must be modified: | |
| tx0_cpu.c add dispatch code | |
| tx0_sys.c add sim_devices table entry | |
| 5. Bugs, limitations, and known issues: | |
| o There is a bug in the 1961 instruction set simulation, which causes the | |
| mouse maze program's searching algorithm to fail. | |
| o The CRY micro-order is not implemented. | |
| o The instruction timing (ie, sim_interval) is not accurate, so implementing a | |
| timing-critical I/O device like the Magtape would require this to be added first. | |
| o PCQ and History do not work. | |
| o Symbolic input does not work. | |
| o Probably lots of other bugs, as Tic-Tac-Toe and Mouse Maze are the only tapes | |
| that I've tested to mostly work. It's difficult to tell what tapes on | |
| bitsavers.org are designed for the 1956 instruction set vs. the later one. | |
| OP Description | |
| -- ----------- | |
| 00 sto x Replace the contents of register x with the contents of the AC | |
| Let the AC remain the same. | |
| 01 add x Add the word in register x to the contents of the AC and leave | |
| the sum in the AC | |
| 10 trn x If the sign digit of the accumulator (AC bit 0) is negative (i.e., a one) | |
| take the next instruction from register x and continue from there. | |
| If the sign is positive (i.e., a zero ignore this instruction and proceed | |
| to the next instruction | |
| 11 opr x Execute one of the operate class commands indicated by the number x | |
| Around 1961, the TX-0 was modified to include additional instructions, and the addressable | |
| memory range was lowered to 8K words. The IR was increased from two bits to five bits. | |
| In addtion, many of the operate-class micro-orders changed. | |
| OPERATE CLASS MICRO-ORDERS FOR 1961 INSTRUCTION SET | |
| --------------------------------------------------- | |
| Cycle.tp | |
| cla --1 --- --- --- --- --- 0.8 Clear AC | |
| amb --- 1-- --- --- --- --- 0.7 Transfer AC contents to MBR | |
| cyr --- --- --- 110 --- --- 1.6 Cycle AC contents right one binary position (AC 17 -> AC 0) | |
| shr --- --- --- 100 --- --- 1.6 Shift AC contents right one binary position (AC 0 unchanged) | |
| mbl --- --- --- 01x --- --- 1.4 Transfer MBR contents to LR | |
| xmb --- --- --- 0x1 --- --- 1.2 Transfer XR contents to MBR | |
| com --- --- --- --- 1-- --- 1.2 Compliment AC | |
| pad --- --- --- --- -1- --- 1.5 Parital add MBR to AC (for each MBR one, complement the correp AC bit) | |
| cry --- --- --- --- --1 --- 1.7 A carry digit is a one if in the next least sigmificant digit, | |
| either ac=0 and mbr=1 or ac=1 and carry digit=1. The carry digits | |
| so determined are partial added to the AC by cry. pad and cry used | |
| together give a full one's complement addition of C(MBR) to C(AC) | |
| anb --- --- --- --- --- 111 1.2-2 And LR and MBR | |
| orb --- --- --- --- --- 101 1.3 Or LR into MBR | |
| lmb --- --- --- --- --- 01x 1.4 Tranfer LR contents to MBR | |
| mbx --- --- --- --- --- 0x1 1.8 Transfer MBR contents to XR | |
| */ | |
| #include "tx0_defs.h" | |
| #define OPR_CLA 0100000 /* 0.8 */ | |
| #define OPR_AMB 0040000 /* 0.7 */ | |
| #define OPR_SHF_MASK 0000700 /* 1.6 */ | |
| #define OPR_CYR 0000600 | |
| #define OPR_SHR 0000400 | |
| #define OPR_MBL_MASK 0000600 /* 1.4 */ | |
| #define OPR_MBL 0000200 | |
| #define OPR_XMB_MASK 0000500 /* 1.2 */ | |
| #define OPR_XMB 0000100 | |
| #define OPR_COM 0000040 /* 1.2 */ | |
| #define OPR_PAD 0000020 /* 1.5 */ | |
| #define OPR_CRY 0000010 /* 1.7 */ | |
| #define OPR_LOG_MASK 0000007 /* Logical operation mask */ | |
| #define OPR_ANB 0000007 /* 1.2-2 */ | |
| #define OPR_ORB 0000005 /* 1.3 */ | |
| #define OPR_LMB_MASK 0000006 /* 1.4 */ | |
| #define OPR_LMB 0000002 | |
| #define OPR_MBX_MASK 0000005 /* 1.8 */ | |
| #define OPR_MBX 0000001 | |
| /* | |
| IN OUT GROUP | |
| ------------ | |
| nop --- -00 000 --- --- --- NOP | |
| tac --- -00 001 --- --- --- 1.1 | |
| tbr --- -00 010 --- --- --- 1.2 | |
| pen --- -00 011 --- --- --- 1.1 | |
| sel --- -00 100 --- --- --- | |
| spare--- -00 101 --- --- --- | |
| rpf --- -00 110 --- --- --- 1.2 | |
| spf --- -00 111 --- --- --- 1.6 | |
| exN --- -01 nnn --- --- --- IOS | |
| cpy --- -10 000 --- --- --- IOS | |
| r1l --- -10 001 --- --- --- IOS | |
| dis --- -10 010 --- --- --- IOS | |
| r3l --- -10 011 --- --- --- IOS | |
| prt --- -10 100 --- --- --- IOS | |
| spare--- -10 101 --- --- --- | |
| p6h --- -10 110 --- --- --- IOS | |
| p7h --- -10 111 --- --- --- IOS | |
| hlt --- -11 000 --- --- --- 1.8 | |
| cll --- -11 001 --- --- --- 0.6 | |
| clr --- -11 010 --- --- --- 0.6 | |
| */ | |
| #define IOS_MASK 0037000 | |
| #define IOS_EX_MASK 0030000 | |
| #define IOS_NOP 0000000 | |
| #define IOS_TAC 0001000 | |
| #define IOS_TBR 0002000 | |
| #define IOS_PEN 0003000 | |
| #define IOS_SEL 0004000 | |
| #define IOS_RPF 0006000 | |
| #define IOS_SPF 0007000 | |
| #define IOS_CPY 0020000 | |
| #define IOS_R1L 0021000 | |
| #define IOS_DIS 0022000 | |
| #define IOS_R3L 0023000 | |
| #define IOS_PRT 0024000 | |
| #define IOS_P6H 0026000 | |
| #define IOS_P7H 0027000 | |
| #define IOS_HLT 0030000 | |
| #define IOS_CLL 0031000 | |
| #define IOS_CLR 0032000 | |
| #define PCQ_SIZE 64 /* must be 2**n */ | |
| #define PCQ_MASK (PCQ_SIZE - 1) | |
| #define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = PC | |
| #define UNIT_V_MSIZE (UNIT_V_UF + 4) /* dummy mask */ | |
| #define UNIT_V_EXT (UNIT_V_UF + 2) | |
| #define UNIT_EXT_INST (1 << UNIT_V_EXT) | |
| #define UNIT_MSIZE (1 << UNIT_V_MSIZE) | |
| #define HIST_PC 0x40000000 | |
| #define HIST_V_SHF 18 | |
| #define HIST_MIN 64 | |
| #define HIST_MAX 65536 | |
| #define TRACE_PRINT(level, args) if(cpu_dev.dctrl & level) { \ | |
| printf args; \ | |
| } | |
| typedef struct { | |
| uint32 pc; | |
| uint32 ir; | |
| uint32 ovac; | |
| uint32 pfio; | |
| uint32 ea; | |
| uint32 opnd; | |
| } InstHistory; | |
| int32 M[MAXMEMSIZE] = { 0 }; /* memory */ | |
| int32 AC = 0; /* AC */ | |
| int32 IR = 0; /* IR */ | |
| int32 PC = 0; /* PC */ | |
| int32 MAR = 0; /* MAR */ | |
| int32 XR = 0; /* XR (index register) */ | |
| int32 MBR = 0; /* MBR */ | |
| int32 LR = 0; /* LR (Live Register) */ | |
| int32 OV = 0; /* overflow */ | |
| int32 TBR = 0; /* sense switches */ | |
| int32 PF = 0; /* program flags */ | |
| int32 TAC = 0; /* Toggle Switch Accumulator */ | |
| int32 iosta = 0; /* status reg */ | |
| int32 ios = 0; /* I/O Stop */ | |
| int32 ch = 0; /* Chime Alarm */ | |
| int32 LP = 0; /* Light Pen / Light Gun flops */ | |
| int32 mode_tst = 1; /* Test Mode Flip-flop */ | |
| int32 mode_rdin = 1; /* Read-In Mode Flip-flop */ | |
| uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ | |
| int32 pcq_p = 0; /* PC queue ptr */ | |
| REG *pcq_r = NULL; /* PC queue reg ptr */ | |
| int32 hst_p = 0; /* history pointer */ | |
| int32 hst_lnt = 0; /* history length */ | |
| InstHistory *hst = NULL; /* inst history */ | |
| int32 fpc_MA; /* shadow ma for FPC access */ | |
| int32 fpc_OP; /* shadow op for FPC access */ | |
| int32 addr_mask = YMASK; | |
| extern UNIT *sim_clock_queue; | |
| extern int32 sim_int_char; | |
| extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ | |
| 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, char *cptr, void *desc); | |
| t_stat cpu_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); | |
| int32 cpu_get_mode (void); | |
| t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); | |
| t_stat cpu_set_ext (UNIT *uptr, int32 val, char *cptr, void *desc); | |
| t_stat cpu_set_noext (UNIT *uptr, int32 val, char *cptr, void *desc); | |
| t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); | |
| t_stat Read (void); | |
| t_stat Write (void); | |
| extern int32 petr (int32 inst, int32 dev, int32 dat); | |
| extern int32 ptp (int32 inst, int32 dev, int32 dat); | |
| extern int32 tti (int32 inst, int32 dev, int32 dat); | |
| extern int32 tto (int32 inst, int32 dev, int32 dat); | |
| extern int32 lpt (int32 inst, int32 dev, int32 dat); | |
| extern int32 dt (int32 inst, int32 dev, int32 dat); | |
| extern int32 drm (int32 inst, int32 dev, int32 dat); | |
| #ifdef USE_DISPLAY | |
| extern int32 dpy (int32 ac); | |
| #endif | |
| /* CPU data structures | |
| cpu_dev CPU device descriptor | |
| cpu_unit CPU unit | |
| cpu_reg CPU register list | |
| cpu_mod CPU modifier list | |
| */ | |
| UNIT cpu_unit = { UDATA (NULL, UNIT_FIX | UNIT_BINK | UNIT_EXT_INST | UNIT_MODE_READIN, MAXMEMSIZE) }; | |
| REG cpu_reg[] = { | |
| { ORDATA (PC, PC, ASIZE) }, | |
| { ORDATA (AC, AC, 18) }, | |
| { ORDATA (IR, IR, 5) }, | |
| { ORDATA (MAR, MAR, 16) }, | |
| { ORDATA (XR, XR, 14) }, | |
| { ORDATA (MBR, MBR, 18) }, | |
| { ORDATA (LR, LR, 18) }, | |
| { ORDATA (TAC, TAC, 18) }, | |
| { ORDATA (TBR, TBR, 18) }, | |
| { ORDATA (PF, PF, 18) }, | |
| { BRDATA (PCQ, pcq, 8, ASIZE, PCQ_SIZE), REG_RO+REG_CIRC }, | |
| { ORDATA (PCQP, pcq_p, 6), REG_HRO }, | |
| { FLDATA (IOS, ios, 0) }, /* In Out Stop */ | |
| { FLDATA (CH, ch, 0) }, /* Chime Alarm */ | |
| { ORDATA (LP, LP, 2) }, /* Light Pen */ | |
| { FLDATA (R, mode_rdin, 0), REG_HRO }, /* Mode "R" (Read In) Flip-Flop */ | |
| { FLDATA (T, mode_tst, 0), REG_HRO }, /* Mode "T" (Test) Flip-Flop */ | |
| { NULL } | |
| }; | |
| MTAB cpu_mod[] = { | |
| { UNIT_EXT_INST, 0, "standard CPU", "TX0STD", &cpu_set_noext }, | |
| { UNIT_EXT_INST, UNIT_EXT_INST, "Extended Instruction Set", "TX0EXT", &cpu_set_ext }, | |
| { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, | |
| { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, | |
| { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, | |
| { UNIT_MODE, 0, "NORMAL", "NORMAL", &cpu_set_mode }, | |
| { UNIT_MODE, UNIT_MODE_TEST, "TEST", "TEST", &cpu_set_mode }, | |
| { UNIT_MODE, UNIT_MODE_READIN, "READIN", "READIN", &cpu_set_mode }, | |
| { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", | |
| &cpu_set_hist, &cpu_show_hist }, | |
| { 0 } | |
| }; | |
| /* Debug flags */ | |
| #define ERROR_MSG (1 << 0) | |
| #define TRACE_MSG (1 << 1) | |
| #define STO_MSG (1 << 2) | |
| #define ADD_MSG (1 << 3) | |
| #define TRN_MSG (1 << 4) | |
| #define ORD_MSG (1 << 5) | |
| #define IOS_MSG (1 << 6) | |
| #define READIN_MSG (1 << 7) | |
| #define VERBOSE_MSG (1 << 8) | |
| #define COUNTERS_MSG (1 << 9) | |
| /* Debug Flags */ | |
| static DEBTAB cpu_dt[] = { | |
| { "ERROR", ERROR_MSG }, | |
| { "TRACE", TRACE_MSG }, | |
| { "STO", STO_MSG }, | |
| { "ADD", ADD_MSG }, | |
| { "TRN", TRN_MSG }, | |
| { "ORD", ORD_MSG }, | |
| { "IOS", IOS_MSG }, | |
| { "READIN", READIN_MSG }, | |
| { "VERBOSE",VERBOSE_MSG }, | |
| { "COUNTERS",COUNTERS_MSG }, | |
| { NULL, 0 } | |
| }; | |
| DEVICE cpu_dev = { | |
| "CPU", &cpu_unit, cpu_reg, cpu_mod, | |
| 1, 8, ASIZE, 1, 8, 18, | |
| &cpu_ex, &cpu_dep, &cpu_reset, | |
| NULL, NULL, NULL, | |
| NULL, DEV_DEBUG, ERROR_MSG, | |
| cpu_dt, NULL | |
| }; | |
| int32 compute_index (int32 y, int32 XR) | |
| { | |
| int32 sum; | |
| y &= YMASK; /* force 13-bit (0 sign) */ | |
| XR &= 037777; /* force 14-bit */ | |
| sum = y + XR; | |
| if (sum > 037777) { /* Carry from bit 4 into bit 17. */ | |
| sum += 1; | |
| } | |
| sum &= YMASK; /* truncate to 13-bit */ | |
| return (sum); | |
| } | |
| /* CPU Instruction usage counters */ | |
| typedef struct { | |
| /* Store group */ | |
| int32 sto, stx, sxa, ado, slr, slx, stz; | |
| /* Add group */ | |
| int32 add, adx, ldx, aux, llr, llx, lda, lax; | |
| /* TRN Group */ | |
| int32 trn, tze, tsx, tix, tra, trx, tlv; | |
| /* OPR Group */ | |
| int32 cla, amb, cyr, shr, mbl, xmb, com, pad, cry, anb, orb, lmb, mbx; | |
| } INST_CTRS; | |
| INST_CTRS inst_ctr; | |
| void tx0_dump_regs(char *desc) | |
| { | |
| TRACE_PRINT(TRACE_MSG, ("%s: AC=%06o, MAR=%05o, MBR=%06o, LR=%06o, XR=%05o\n", desc, AC, MAR, MBR, LR, XR)); | |
| /* Check regs sanity */ | |
| if (AC > DMASK) { | |
| printf("Error: AC > DMASK\n"); | |
| } | |
| if (MBR > DMASK) { | |
| printf("Error: MBR > DMASK\n"); | |
| } | |
| if (LR > DMASK) { | |
| printf("Error: LR > DMASK\n"); | |
| } | |
| if (!MEM_ADDR_OK(MAR)) { | |
| printf("Error: MAR > %06o\n", MEMSIZE); | |
| } | |
| } | |
| t_stat sim_opr_orig(int32 op); | |
| t_stat sim_instr (void) | |
| { | |
| extern int32 sim_interval; | |
| int32 IR, op, inst_class, y; | |
| int32 tempLR; /* LR temporary storage in case both LMB and MBL are set (swap LR<->MBR) */ | |
| t_stat reason; | |
| /* Clear Instruction counters */ | |
| inst_ctr.sto = inst_ctr.stx = inst_ctr.sxa = inst_ctr.ado = inst_ctr.slr = inst_ctr.slx = inst_ctr.stz = 0; | |
| inst_ctr.add = inst_ctr.adx = inst_ctr.ldx = inst_ctr.aux = inst_ctr.llr = inst_ctr.llx = inst_ctr.lda = inst_ctr.lax = 0; | |
| inst_ctr.trn = inst_ctr.trn = inst_ctr.tze = inst_ctr.tsx = inst_ctr.tix = inst_ctr.tra = inst_ctr.trx = inst_ctr.tlv = 0; | |
| inst_ctr.cla = inst_ctr.amb = inst_ctr.cyr = inst_ctr.shr = inst_ctr.mbl = inst_ctr.xmb = inst_ctr.com = inst_ctr.pad = inst_ctr.cry = inst_ctr.anb = inst_ctr.orb = inst_ctr.lmb = inst_ctr.mbx = 0; | |
| #define INCR_ADDR(x) ((x+=1) & (MEMSIZE-1)) | |
| /* Main instruction fetch/decode loop: check events */ | |
| reason = 0; | |
| while (reason == 0) { /* loop until halted */ | |
| if (sim_interval <= 0) { /* check clock queue */ | |
| reason = sim_process_event (); | |
| if (reason != SCPE_OK) | |
| break; | |
| } | |
| if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ | |
| reason = STOP_IBKPT; /* stop simulation */ | |
| break; | |
| } | |
| if (ios) { | |
| TRACE_PRINT(ERROR_MSG, ("I/O Stop - Waiting...\n")); | |
| continue; | |
| } | |
| /* Handle Instruction Execution in TEST and READIN modes */ | |
| if (mode_tst) { /* Test Mode / Readin mode */ | |
| if (mode_rdin) { /* Readin Mode */ | |
| reason = SCPE_OK; /* Default is to continue reading, and transfer control when done. */ | |
| AC = petr(3,0,0); /* Read three chars from tape into AC */ | |
| MAR = AC & AMASK; /* Set memory address */ | |
| IR = AC >> 16; | |
| if (!MEM_ADDR_OK(MAR)) { | |
| TRACE_PRINT(ERROR_MSG, ("READIN: Tape address out of range.\n")); | |
| reason = SCPE_FMT; | |
| } | |
| switch (IR) { | |
| case 00: /* Storage (sto x) */ | |
| case 03: /* Storage (opr x) */ | |
| MBR = petr(3,0,0); /* Read three characters from tape. */ | |
| TRACE_PRINT(READIN_MSG, ("READIN: sto @%06o = %06o\n", MAR, MBR)); | |
| Write(); | |
| break; | |
| case 02: /* Transfer Control (trn x) Start Execution */ | |
| PC = MAR; | |
| reason = SCPE_OK; /* let SIMH start execution. */ | |
| TRACE_PRINT(READIN_MSG, ("READIN: trn %06o (Start Execution)\n", PC)); | |
| reason = cpu_set_mode(&cpu_unit, 0, NULL, NULL); | |
| break; | |
| case 01: /* Transfer (add x) - Halt */ | |
| PC = MAR; | |
| reason = SCPE_STOP; /* let SIMH halt. */ | |
| TRACE_PRINT(READIN_MSG, ("READIN: add %06o (Halt)\n", PC)); | |
| reason = cpu_set_mode(&cpu_unit, 0, NULL, NULL); | |
| break; | |
| default: | |
| reason = SCPE_IERR; | |
| break; | |
| } | |
| } else if (mode_tst) { /* Test mode not implemented yet. */ | |
| TRACE_PRINT(ERROR_MSG, ("TEST Mode not implemented.\n")); | |
| reason = SCPE_STOP; | |
| } else { | |
| TRACE_PRINT(ERROR_MSG, ("Invalid CPU mode.\n")); | |
| reason = SCPE_IERR; | |
| } | |
| continue; /* Proceed with next instruction */ | |
| } | |
| /* Fetch, decode instruction in NORMAL mode */ | |
| MAR = PC; | |
| if (Read ()) break; /* fetch inst */ | |
| IR = (MBR >> 13); /* save in IR */ | |
| inst_class = IR >> 3; | |
| op = MBR & AMASK; | |
| y = MBR & YMASK; | |
| sim_interval = sim_interval - 1; | |
| if ((cpu_unit.flags & UNIT_EXT_INST) == 0) { /* Original instruction set */ | |
| IR &= 030; | |
| MAR = MBR & AMASK; /* 16-bit address field */ | |
| } else { | |
| MAR = MBR & YMASK; /* 13-bit address field */ | |
| } | |
| if (hst_lnt) { /* history enabled? */ | |
| hst_p = (hst_p + 1); /* next entry */ | |
| if (hst_p >= hst_lnt) hst_p = 0; | |
| hst[hst_p].pc = MAR | HIST_PC; /* save state */ | |
| hst[hst_p].ir = IR; | |
| hst[hst_p].ovac = (OV << HIST_V_SHF) | AC; | |
| } | |
| PC = INCR_ADDR (PC); /* increment PC */ | |
| #ifdef USE_FPC | |
| fpc_OP = op; /* shadow opcode for FPC */ | |
| #endif | |
| tx0_dump_regs("START"); | |
| switch (inst_class) { /* decode IR<0:1> */ | |
| /* Logical, load, store instructions */ | |
| case 00: /* sto x */ | |
| switch (IR & 07) { | |
| case 0: /* sto */ | |
| MBR = AC; | |
| Write(); | |
| inst_ctr.sto++; | |
| break; | |
| case 1: /* stx */ | |
| MBR = AC; | |
| MAR = compute_index(y, XR); | |
| Write(); | |
| inst_ctr.stx++; | |
| break; | |
| case 2: /* sxa */ | |
| { | |
| int32 temp = M[MAR]; | |
| temp &= 0760000; | |
| temp |= (XR & YMASK); | |
| MBR = temp; | |
| Write(); | |
| } | |
| inst_ctr.sxa++; | |
| break; | |
| case 3: /* ado */ | |
| { | |
| int32 temp = M[MAR]; | |
| temp += 1; /* add 1 */ | |
| if (temp > DMASK) { /* Overflow, */ | |
| temp += 1; /* propagate carry from bit 0 to bit 17. */ | |
| } | |
| temp &= DMASK; | |
| MBR = temp; | |
| AC = temp; | |
| Write(); | |
| } | |
| inst_ctr.ado++; | |
| break; | |
| case 4: /* slr */ | |
| MBR = LR; | |
| Write(); | |
| inst_ctr.slr++; | |
| break; | |
| case 5: /* slx */ | |
| MAR = compute_index(y, XR); | |
| MBR = LR; | |
| Write(); | |
| inst_ctr.slx++; | |
| break; | |
| case 6: /* stz */ | |
| MBR = 0; | |
| Write(); | |
| inst_ctr.stz++; | |
| break; | |
| case 7: /* no-op */ | |
| break; | |
| } | |
| break; | |
| case 01: /* add x */ | |
| switch (IR & 07) { | |
| case 0: /* add */ | |
| Read(); | |
| AC = AC + MBR; | |
| if (AC > DMASK) { | |
| AC += 1; | |
| } else; | |
| AC &= DMASK; | |
| inst_ctr.add++; | |
| break; | |
| case 1: /* adx */ | |
| MAR = compute_index(y, XR); | |
| Read(); | |
| AC = AC + MBR; | |
| if (AC > DMASK) { | |
| AC += 1; | |
| } else; | |
| AC &= DMASK; | |
| inst_ctr.adx++; | |
| break; | |
| case 2: /* ldx */ | |
| Read(); | |
| XR = MBR & YMASK; /* load XR[5:17] from C(y[5:17]) */ | |
| XR |= ((MBR & SIGN) >> 4); /* Load XR[4] from C(y[0]) */ | |
| inst_ctr.ldx++; | |
| break; | |
| case 3: /* aux (Augment Index) */ | |
| { | |
| uint32 newY = (y & 0017777) | ((y & SIGN) >> 4); | |
| TRACE_PRINT(ADD_MSG, ("[%06o] AUX: y=%05o, XR=%05o = ", PC-1, newY, XR)); | |
| XR = XR + newY; | |
| TRACE_PRINT(ADD_MSG, ("%05o\n", XR)); | |
| break; | |
| } | |
| inst_ctr.aux++; | |
| case 4: /* llr (Load Live Register) */ | |
| Read(); | |
| LR = MBR; | |
| inst_ctr.llr++; | |
| break; | |
| case 5: /* llx (Load Live Register, Indexed) */ | |
| MAR = compute_index(y, XR); | |
| Read(); | |
| LR = MBR; | |
| inst_ctr.llx++; | |
| break; | |
| case 6: /* lda (Load Accumulator) */ | |
| Read(); | |
| AC = MBR; | |
| inst_ctr.lda++; | |
| break; | |
| case 7: /* lax (Load Accumulator, Indexed) */ | |
| MAR = compute_index(y, XR); | |
| Read(); | |
| AC = MBR; | |
| inst_ctr.lax++; | |
| break; | |
| } | |
| break; | |
| case 02: /* trn x */ | |
| switch (IR & 07) { | |
| case 0: /* trn (Transfer on Negative AC) */ | |
| if (AC & SIGN) { | |
| TRACE_PRINT(TRN_MSG, ("[%06o] TRN: Transfer taken: PC=%06o\n", PC-1, y)); | |
| PC = MAR; | |
| } | |
| inst_ctr.trn++; | |
| break; | |
| case 1: /* tze (Transfer on +/- Zero) */ | |
| if ((AC == 0777777) || (AC == 0000000)) { | |
| TRACE_PRINT(TRN_MSG, ("[%06o] TZE: Transfer taken: PC=%06o\n", PC-1, y)); | |
| PC = y; | |
| } | |
| inst_ctr.tze++; | |
| break; | |
| case 2: /* tsx (Transfer and set Index) */ | |
| XR = PC & 0017777; /* XR[4] = 0; */ | |
| TRACE_PRINT(TRN_MSG, ("[%06o] TSX: PC=%06o, XR=%05o\n", PC-1, y, XR)); | |
| PC = y; | |
| inst_ctr.tsx++; | |
| break; | |
| case 3: /* tix (Transfer and Index) */ | |
| TRACE_PRINT(TRN_MSG, ("[%06o] TIX: XR=%05o\n", PC-1, XR)); | |
| if ((XR == 037777) || (XR == 000000)) { /* +/- 0, take next instruction */ | |
| TRACE_PRINT(TRN_MSG, ("+/- 0, transfer not taken.\n")); | |
| } else { /* Not +/- 0 */ | |
| if (XR & 0020000) { /* XR[4] == 1 */ | |
| TRACE_PRINT(TRN_MSG, ("XR is negative, transfer taken,")); | |
| XR ++; | |
| } else { /* XR[4] = 0 */ | |
| TRACE_PRINT(TRN_MSG, ("XR is positive, transfer taken,")); | |
| XR --; | |
| } | |
| PC = y; | |
| XR &= 037777; | |
| TRACE_PRINT(TRN_MSG, (" PC=%06o, XR=%05o\n", PC, XR)); | |
| } | |
| inst_ctr.tix++; | |
| break; | |
| case 4: /* tra (Unconditional Transfer) */ | |
| TRACE_PRINT(TRN_MSG, ("[%06o] TRA: Transfer taken: PC=%06o\n", PC-1, y)); | |
| PC = y; | |
| inst_ctr.tra++; | |
| break; | |
| case 5: /* trx */ | |
| { | |
| int32 newPC; | |
| newPC = compute_index(y, XR); | |
| TRACE_PRINT(TRN_MSG, ("[%06o] TRA: Transfer taken: PC=%06o\n", PC-1, newPC)); | |
| PC = newPC; | |
| } | |
| inst_ctr.trx++; | |
| break; | |
| case 6: /* tlv (Transfer on External Level) */ | |
| TRACE_PRINT(ERROR_MSG, ("[%06o] TODO: Implement TLV\n", PC-1)); | |
| inst_ctr.tlv++; | |
| break; | |
| case 7: /* no-op */ | |
| break; | |
| } | |
| break; | |
| case 03: /* opr x */ | |
| if ((cpu_unit.flags & UNIT_EXT_INST) == 0) { /* Original instruction set */ | |
| reason = sim_opr_orig(op); | |
| break; | |
| } | |
| /* I can't find this mentioned in the TX-0 Documentation, but for the | |
| * lro and xro instructions, this must be needed. | |
| */ | |
| MBR = 0; | |
| /* Cycle 0 */ | |
| if (op & OPR_AMB) { /* 0.7 */ | |
| inst_ctr.amb++; | |
| MBR = AC; | |
| TRACE_PRINT(ORD_MSG, ("[%06o]: AMB: MBR=%06o\n", PC-1, MBR)); | |
| } | |
| if (op & OPR_CLA) { /* 0.8 */ | |
| inst_ctr.cla++; | |
| AC = 0; | |
| TRACE_PRINT(ORD_MSG, ("[%06o]: CLA: AC=%06o\n", PC-1, AC)); | |
| } | |
| /* IOS - In / Out Stop */ | |
| /* Check TTI for character. If so, put in LR and set LR bit 0. */ | |
| if (iosta & IOS_TTI) { | |
| int32 rbuf; | |
| rbuf = tti(0,0,0); | |
| TRACE_PRINT(IOS_MSG, ("TTI: character received=%03o\n", rbuf &077)); | |
| LR &= 0266666; /* Clear bits 0,2,5,8,...,17 */ | |
| LR |= SIGN; /* Set bit 0, character available. */ | |
| LR |= ((rbuf & 001) >> 0) << 15;/* bit 2 */ | |
| LR |= ((rbuf & 002) >> 1) << 12;/* bit 5 */ | |
| LR |= ((rbuf & 004) >> 2) << 9; /* bit 8 */ | |
| LR |= ((rbuf & 010) >> 3) << 6; /* bit 11 */ | |
| LR |= ((rbuf & 020) >> 4) << 3; /* bit 14 */ | |
| LR |= ((rbuf & 040) >> 5) << 0; /* bit 17 */ | |
| } | |
| switch(op & IOS_MASK) { | |
| case IOS_NOP: | |
| break; | |
| case IOS_TAC: | |
| TRACE_PRINT(IOS_MSG, ("[%06o] TAC %06o\n", PC-1, TAC)); | |
| AC |= TAC; | |
| break; | |
| case IOS_TBR: | |
| TRACE_PRINT(IOS_MSG, ("[%06o] TBR %06o\n", PC-1, TBR)); | |
| MBR |= TBR; | |
| break; | |
| case IOS_PEN: | |
| TRACE_PRINT(IOS_MSG, ("[%06o] Light Pen %01o\n", PC-1, LP)); | |
| AC &= AMASK; | |
| AC |= (LP & 1) << 17; | |
| AC |= (LP & 2) << 16; | |
| AC &= DMASK; | |
| break; | |
| case IOS_SEL: | |
| { /* These are used for Magtape control. | |
| Magtape is compatible with IBM 709. Maybe the SIMH 7090 magtape can be leveraged. */ | |
| int32 CLRA = (op & 0100000); | |
| int32 BINDEC = (op & 020); | |
| int32 device = op & 03; | |
| int32 tape_ord = (op >> 2) & 03; | |
| char *tape_cmd[] = {"Backspace Tape", "Read/Select Tape", "Rewind Tape", "Write/Select Tape" }; | |
| TRACE_PRINT(ERROR_MSG, ("[%06o] TODO: SEL (magtape)\n", PC-1)); | |
| printf("Device %d: CLRA=%d, BINDEC=%d: %s\n", device, CLRA, BINDEC, tape_cmd[tape_ord]); | |
| } | |
| break; | |
| case IOS_RPF: /* These are used for Magtape control. */ | |
| TRACE_PRINT(IOS_MSG, ("[%06o] RPF %06o\n", PC-1, PF)); | |
| MBR |= PF; | |
| break; | |
| case IOS_SPF: /* These are used for Magtape control. */ | |
| TRACE_PRINT(IOS_MSG, ("[%06o] SPF %06o\n", PC-1, MBR)); | |
| PF = MBR; | |
| break; | |
| case IOS_CPY: /* These are used for Magtape control. */ | |
| TRACE_PRINT(ERROR_MSG, ("[%06o] TODO: CPY\n", PC-1)); | |
| break; | |
| case IOS_R1L: | |
| AC &= 0333333; /* Clear bits 0,3,6,9,12,15 */ | |
| AC |= petr(1, 0, 0); /* Read one line from PETR */ | |
| break; | |
| case IOS_DIS: | |
| #ifdef USE_DISPLAY | |
| LP = dpy (AC); /* Display point on the CRT */ | |
| #endif /* USE_DISPLAY */ | |
| break; | |
| case IOS_R3L: | |
| AC = petr(3, 0, 0); /* Read three lines from PETR */ | |
| break; | |
| case IOS_PRT: | |
| { | |
| uint32 tmpAC = 0; | |
| tmpAC |= ((AC & 0000001) >> 0) << 0; /* bit 17 */ | |
| tmpAC |= ((AC & 0000010) >> 3) << 1; /* bit 14 */ | |
| tmpAC |= ((AC & 0000100) >> 6) << 2; /* bit 11 */ | |
| tmpAC |= ((AC & 0001000) >> 9) << 3; /* bit 8 */ | |
| tmpAC |= ((AC & 0010000) >> 12) << 4; /* bit 5 */ | |
| tmpAC |= ((AC & 0100000) >> 15) << 5; /* bit 2 */ | |
| tto (0, 0, tmpAC & 077); /* Print one character on TTO */ | |
| } | |
| break; | |
| case IOS_P6H: | |
| case IOS_P7H: | |
| { | |
| uint32 tmpAC = 0; | |
| tmpAC |= ((AC & 0000001) >> 0) << 0; /* bit 17 */ | |
| tmpAC |= ((AC & 0000010) >> 3) << 1; /* bit 14 */ | |
| tmpAC |= ((AC & 0000100) >> 6) << 2; /* bit 11 */ | |
| tmpAC |= ((AC & 0001000) >> 9) << 3; /* bit 8 */ | |
| tmpAC |= ((AC & 0010000) >> 12) << 4; /* bit 5 */ | |
| tmpAC |= ((AC & 0100000) >> 15) << 5; /* bit 2 */ | |
| tmpAC &= 0077; | |
| if ((op & IOS_MASK) == IOS_P7H) { | |
| tmpAC |= 0100; /* Punch 7th hole. */ | |
| TRACE_PRINT(ERROR_MSG, ("[%06o] Punch 7 holes\n", PC-1)); | |
| } else { | |
| TRACE_PRINT(ERROR_MSG, ("[%06o] Punch 6 holes\n", PC-1)); | |
| } | |
| ptp (0, 0, tmpAC); /* Punch character on PTP */ | |
| } | |
| break; | |
| case IOS_HLT: | |
| TRACE_PRINT(IOS_MSG, ("[%06o] HALT Instruction\n", PC-1)); | |
| reason = STOP_HALT; | |
| break; | |
| case IOS_CLL: | |
| AC &= 0000777; | |
| break; | |
| case IOS_CLR: | |
| AC &= 0777000; | |
| break; | |
| default: /* Could be ex0-ex7, handle them here. */ | |
| if ((op & IOS_EX_MASK) == 0010000) { | |
| TRACE_PRINT(ERROR_MSG, ("[%06o] TODO: EX%o\n", PC-1, (op >> 9) & 07)); | |
| } | |
| break; | |
| } | |
| /* Cycle 1 */ | |
| if (op & OPR_COM) { /* 1.2 */ | |
| AC = ~AC; | |
| AC &= DMASK; | |
| TRACE_PRINT(ORD_MSG, ("[%06o]: COM: AC=%06o\n", PC-1, AC)); | |
| inst_ctr.com++; | |
| } | |
| if ((op & OPR_XMB_MASK) == OPR_XMB) { /* 1.2 XR[5:17] -> MBR[5:17], XR[4] -> MBR[0:4] */ | |
| int32 bit14 = (XR >> 13) & 1; | |
| MBR = XR & YMASK; /* XR[5:17] -> MBR[5:17] */ | |
| MBR |= (bit14 << 17); /* XR[4] -> MBR[0] */ | |
| MBR |= (bit14 << 16); /* XR[4] -> MBR[1] */ | |
| MBR |= (bit14 << 15); /* XR[4] -> MBR[2] */ | |
| MBR |= (bit14 << 14); /* XR[4] -> MBR[3] */ | |
| MBR |= (bit14 << 13); /* XR[4] -> MBR[4] */ | |
| TRACE_PRINT(ORD_MSG, ("[%06o]: XMB: XR=%05o, MBR=%06o\n", PC-1, XR, MBR)); | |
| inst_ctr.xmb++; | |
| } | |
| if ((op & OPR_LOG_MASK) == OPR_ANB) { /* 1.2-2 */ | |
| MBR &= LR; | |
| TRACE_PRINT(ORD_MSG, ("[%06o]: ANB: MBR=%06o\n", PC-1, MBR)); | |
| inst_ctr.anb++; | |
| } | |
| if ((op & OPR_LOG_MASK) == OPR_ORB) { /* 1.3 */ | |
| MBR |= LR; | |
| TRACE_PRINT(ORD_MSG, ("[%06o]: ORB: MBR=%06o\n", PC-1, MBR)); | |
| inst_ctr.orb++; | |
| } | |
| tempLR = LR; /* LR temporary storage in case both LMB and MBL are set (swap LR<->MBR) */ | |
| if ((op & OPR_MBL_MASK) == OPR_MBL) { /* 1.4 */ | |
| LR = MBR; | |
| TRACE_PRINT(ORD_MSG, ("[%06o]: MBL: LR=%06o, prev LR=%06o\n", PC-1, LR, tempLR)); | |
| inst_ctr.mbl++; | |
| } | |
| if ((op & OPR_LMB_MASK) == OPR_LMB) { /* 1.4 */ | |
| MBR = tempLR; | |
| TRACE_PRINT(ORD_MSG, ("[%06o]: LMB: LR=%06o, MBR=%06o\n", PC-1, LR, MBR)); | |
| inst_ctr.lmb++; | |
| } | |
| if (op & OPR_PAD) { /* 1.5 Partial Add (XOR): AC = MBR ^ AC */ | |
| if (op & OPR_CRY) { /* 1.7 */ | |
| TRACE_PRINT(ORD_MSG, ("[%06o] PAD+CRY: AC=%06o, MBR=%06o = ", PC-1, AC, MBR)); | |
| AC = AC + MBR; | |
| if (AC > DMASK) { | |
| AC += 1; | |
| } else; | |
| AC &= DMASK; | |
| TRACE_PRINT(ORD_MSG, ("%06o\n", AC)); | |
| } else { | |
| TRACE_PRINT(ORD_MSG, ("[%06o] PAD: AC=%06o, MBR=%06o\n", PC-1, AC, MBR)); | |
| AC = AC ^ MBR; | |
| AC &= DMASK; | |
| TRACE_PRINT(ORD_MSG, ("[%06o] PAD: Check: AC=%06o\n", PC-1, AC)); | |
| } | |
| inst_ctr.pad++; | |
| } | |
| if ((op & OPR_SHF_MASK) == OPR_CYR) { /* 1.6 */ | |
| int32 bit17; | |
| bit17 = (AC & 1) << 17; | |
| AC >>= 1; | |
| AC |= bit17; | |
| TRACE_PRINT(ORD_MSG, ("[%06o]: CYR: AC=%06o\n", PC-1, AC)); | |
| inst_ctr.cyr++; | |
| } | |
| if ((op & OPR_SHF_MASK) == OPR_SHR) { /* 1.6 Shift AC Right, preserve bit 0. */ | |
| int32 bit0; | |
| bit0 = AC & 0400000; | |
| AC = AC >> 1; | |
| AC |= bit0; | |
| TRACE_PRINT(ORD_MSG, ("[%06o]: SHR: AC=%06o\n", PC-1, AC)); | |
| inst_ctr.shr++; | |
| } | |
| if (op & OPR_CRY) { /* 1.7 */ | |
| if (op & OPR_PAD) { | |
| } else { | |
| TRACE_PRINT(ERROR_MSG, ("[%06o] CRY: TODO: AC=%06o\n", PC-1, AC)); | |
| inst_ctr.cry++; | |
| } | |
| } | |
| if ((op & OPR_MBX_MASK) == OPR_MBX) { /* 1.8 MBR[5:17] -> XR[5:17], MBR[0] -> XR[4] */ | |
| int32 tempXR; | |
| tempXR = MBR & YMASK; | |
| tempXR |= (((MBR >> 17) & 1) << 13); | |
| XR = tempXR; | |
| TRACE_PRINT(ORD_MSG, ("[%06o]: MBX: MBR=%06o, XR=%06o\n", PC-1, MBR, XR)); | |
| inst_ctr.mbx++; | |
| } | |
| } | |
| tx0_dump_regs("END"); | |
| #ifdef USE_FPC | |
| fpc_MA = MAR; /* shadow MAR for FPC */ | |
| #endif | |
| } /* end while */ | |
| pcq_r->qptr = pcq_p; /* update pc q ptr */ | |
| TRACE_PRINT(COUNTERS_MSG, ("Instruction Counters\nSTO=%d, STX=%d, SXA=%d, ADO=%d, SLR=%d, SLX=%d, STZ=%d\n", | |
| inst_ctr.sto, inst_ctr.stx, inst_ctr.sxa, inst_ctr.ado, inst_ctr.slr, inst_ctr.slx, inst_ctr.stz)); | |
| TRACE_PRINT(COUNTERS_MSG, ("ADD=%d, ADX=%d, LDX=%d, AUX=%d, LLR=%d, LLX=%d, LDA=%d, LAX=%d\n", | |
| inst_ctr.add, inst_ctr.adx, inst_ctr.ldx, inst_ctr.aux, inst_ctr.llr, inst_ctr.llx, inst_ctr.lda, inst_ctr.lax)); | |
| TRACE_PRINT(COUNTERS_MSG, ("TRN=%d, TZE=%d, TSX=%d, TIX=%d, TRA=%d, TRX=%d, TLV=%d\n", | |
| inst_ctr.trn, inst_ctr.tze, inst_ctr.tsx, inst_ctr.tix, inst_ctr.tra, inst_ctr.trx, inst_ctr.tlv)); | |
| TRACE_PRINT(COUNTERS_MSG, ("CLA=%d, AMB=%d, CYR=%d, SHR=%d, MBL=%d, XMB=%d, COM=%d, PAD=%d, CRY=%d, ANB=%d, ORB=%d, LMB=%d, MBX=%d\n", | |
| inst_ctr.cla, inst_ctr.amb, inst_ctr.cyr, inst_ctr.shr, inst_ctr.mbl, inst_ctr.xmb, inst_ctr.com, inst_ctr.pad, inst_ctr.cry, inst_ctr.anb, inst_ctr.orb, inst_ctr.lmb, inst_ctr.mbx)); | |
| return reason; | |
| } | |
| /* Read and write memory */ | |
| t_stat Read (void) | |
| { | |
| MAR &= (MEMSIZE - 1); | |
| MBR = M[MAR]; | |
| MBR &= DMASK; | |
| return SCPE_OK; | |
| } | |
| t_stat Write (void) | |
| { | |
| MAR &= (MEMSIZE - 1); | |
| MBR &= DMASK; | |
| M[MAR] = MBR; | |
| return SCPE_OK; | |
| } | |
| /* Reset routine */ | |
| t_stat cpu_reset (DEVICE *dptr) | |
| { | |
| ios = 0; | |
| PF = 0; | |
| MAR = 0; | |
| MBR = 0; | |
| pcq_r = find_reg ("PCQ", NULL, dptr); | |
| if (pcq_r) { | |
| pcq_r->qptr = 0; | |
| } else { | |
| return SCPE_IERR; | |
| } | |
| 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) return SCPE_NXM; | |
| if (vptr != NULL) *vptr = M[addr] & DMASK; | |
| 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 & DMASK; | |
| return SCPE_OK; | |
| } | |
| /* Change memory size */ | |
| t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) | |
| { | |
| int32 mc = 0; | |
| uint32 i; | |
| if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) | |
| return SCPE_ARG; | |
| for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; | |
| if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) | |
| return SCPE_OK; | |
| MEMSIZE = val; | |
| for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; | |
| return SCPE_OK; | |
| } | |
| /* Change CPU Mode (Normal, Test, Readin) */ | |
| t_stat cpu_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) | |
| { | |
| if (val == UNIT_MODE_TEST) { | |
| mode_tst = 1; | |
| mode_rdin = 0; | |
| } else if (val == UNIT_MODE_READIN) { | |
| mode_tst = 1; | |
| mode_rdin = 1; | |
| } else { /* Normal Mode */ | |
| mode_tst = 0; | |
| mode_rdin = 0; | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Set TX-0 with Extended Instruction Set */ | |
| t_stat cpu_set_ext (UNIT *uptr, int32 val, char *cptr, void *desc) | |
| { | |
| printf("Set CPU Extended Mode\n"); | |
| return SCPE_OK; | |
| } | |
| t_stat cpu_set_noext (UNIT *uptr, int32 val, char *cptr, void *desc) | |
| { | |
| printf("Set CPU Non-Extended Mode\n"); | |
| return SCPE_OK; | |
| } | |
| int32 cpu_get_mode (void) | |
| { | |
| return (cpu_unit.flags & UNIT_EXT_INST); | |
| } | |
| /* Set history */ | |
| t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) | |
| { | |
| int32 i, lnt; | |
| t_stat r; | |
| if (cptr == NULL) { | |
| for (i = 0; i < hst_lnt; i++) hst[i].pc = 0; | |
| hst_p = 0; | |
| return SCPE_OK; | |
| } | |
| lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); | |
| if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; | |
| hst_p = 0; | |
| if (hst_lnt) { | |
| free (hst); | |
| hst_lnt = 0; | |
| hst = NULL; | |
| } | |
| if (lnt) { | |
| hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); | |
| if (hst == NULL) return SCPE_MEM; | |
| hst_lnt = lnt; | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Show history */ | |
| t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) | |
| { | |
| int32 ov, pf, op, k, di, lnt; | |
| char *cptr = (char *) desc; | |
| t_stat r; | |
| t_value sim_eval; | |
| InstHistory *h; | |
| extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, | |
| UNIT *uptr, int32 sw); | |
| 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, "PC OV AC IO PF EA IR\n\n"); | |
| for (k = 0; k < lnt; k++) { /* print specified */ | |
| h = &hst[(++di) % hst_lnt]; /* entry pointer */ | |
| if (h->pc & HIST_PC) { /* instruction? */ | |
| ov = (h->ovac >> HIST_V_SHF) & 1; /* overflow */ | |
| pf = 0; | |
| op = ((h->ir >> 13) & 037); /* get opcode */ | |
| fprintf (st, "%06o %o %06o %06o %03o ", | |
| h->pc & AMASK, ov, h->ovac & DMASK, h->pfio & DMASK, pf); | |
| if ((op < 032) && (op != 007)) /* mem ref instr */ | |
| fprintf (st, "%06o ", h->ea); | |
| else fprintf (st, " "); | |
| sim_eval = h->ir; | |
| if ((fprint_sym (st, h->pc & AMASK, &sim_eval, &cpu_unit, SWMASK ('M'))) > 0) | |
| fprintf (st, "(undefined) %06o", h->ir); | |
| else if (op < 030) /* mem ref instr */ | |
| fprintf (st, " [%06o]", h->opnd); | |
| fputc ('\n', st); /* end line */ | |
| } /* end else instruction */ | |
| } /* end for */ | |
| return SCPE_OK; | |
| } | |
| /* set "test switches"; from display code */ | |
| void | |
| cpu_set_switches(unsigned long bits) | |
| { | |
| /* just what we want; smaller CPUs might want to shift down? */ | |
| TAC = bits; | |
| } | |
| unsigned long | |
| cpu_get_switches(void) | |
| { | |
| return TAC; | |
| } | |
| t_stat sim_load(FILE *fileref, char *cptr, char *fnam, int32 flag) { | |
| uint32 cnt = 0, word; | |
| t_addr j, lo, hi, sz, sz_words; | |
| char *result; | |
| if (flag) { /* Dump to file. */ | |
| result = get_range(NULL, cptr, &lo, &hi, 8, 0xFFFF, 0); | |
| if (result == NULL) return SCPE_ARG; | |
| for (j = lo; j <= hi; j++) { | |
| if (sim_fwrite(&j, 4, 1, fileref) == 0) return SCPE_IOERR; | |
| if (sim_fwrite(&M[j], 4, 1, fileref) == 0) return SCPE_IOERR; | |
| } | |
| } else { | |
| lo = strtotv(cptr, &result, 8) & 0xFFFF; | |
| sz = sim_fsize(fileref); | |
| sz_words = sz / 4; | |
| for (j = lo; j < sz_words; j++) { | |
| sim_fread(&word, 4, 1, fileref); | |
| M[j] = word; | |
| } | |
| } | |
| printf("%d words %s [%06o - %06o].\n", j - lo, flag ? "dumped" : "loaded", lo, j-1); | |
| return SCPE_OK; | |
| } | |
| /* | |
| Original Operate-class instruction micro orders for the 1956 TX-0 Instruction Set | |
| Operate Fields | |
| -------------- | |
| --1 --- --- --- --- --- CLL 0.8 | |
| --- 1-- --- --- --- --- CLR 0.8 | |
| --- -10 --- --- --- --- IOS 0.8 | |
| --- -11 --- --- --- --- HLT 1.8 | |
| --- --- 111 --- --- --- P7H 0.8 | |
| --- --- 110 --- --- --- P6H 0.8 | |
| --- --- 100 --- --- --- PNT 0.8 | |
| --- --- 001 --- --- --- R1C 0.8 | |
| --- --- 011 --- --- --- R3C 0.8 | |
| --- --- 010 --- --- --- DIS 0.8 | |
| --- --- --- 10- --- --- SHR 1.4 | |
| --- --- --- 11- --- --- CYR 1.4 | |
| --- --- --- 01- --- --- MLR 1.3 | |
| --- --- --- --1 --- 0-- PEN 1.1 | |
| --- --- --- --0 --- 1-- TAC 1.1 | |
| --- --- --- --- 1-- --- COM 1.2 | |
| --- --- --- --- -1- --- PAD 1.4 | |
| --- --- --- --- --1 --- CRY 1.7 | |
| --- --- --- --- --- -01 AMB 1.2 AC -> MBR | |
| --- --- --- --- --- -11 TBR 1.2 TBR -> MBR | |
| --- --- --- --- --- -10 LMB 1.3 LR -> MBR | |
| */ | |
| #define OOPR_CLL 0100000 | |
| #define OOPR_CLR 0040000 | |
| #define OOPR_IOS 0020000 | |
| #define OOPR_HLT 0030000 | |
| #define OOPR_IOS_MASK 0007000 | |
| #define OOPR_P7H 0007000 | |
| #define OOPR_P6H 0006000 | |
| #define OOPR_PNT 0004000 | |
| #define OOPR_R3C 0003000 | |
| #define OOPR_DIS 0002000 | |
| #define OOPR_R1C 0001000 | |
| #define OOPR_SHF_MASK 0000300 | |
| #define OOPR_SHR 0000400 | |
| #define OOPR_CYR 0000300 | |
| #define OOPR_MLR 0000200 | |
| #define OOPR_PEN_MASK 0000104 | |
| #define OOPR_PEN 0000100 | |
| #define OOPR_TAC_MASK 0000104 | |
| #define OOPR_TAC 0000004 | |
| #define OOPR_COM 0000040 | |
| #define OOPR_PAD 0000020 | |
| #define OOPR_CRY 0000010 | |
| #define OOPR_AMB_MASK 0000007 | |
| #define OOPR_AMB 0000001 | |
| #define OOPR_TBR 0000003 | |
| #define OOPR_LMB 0000002 | |
| t_stat sim_opr_orig(int32 op) | |
| { | |
| t_stat reason = SCPE_OK; | |
| if (op & OOPR_CLL) { /* cll 0.8 Clear the left nine digital positions of the AC */ | |
| AC &= 0000777; | |
| TRACE_PRINT(ORD_MSG, ("[%06o]: CLL\n", PC-1)); | |
| } | |
| if (op & OOPR_CLR) { /* clr 0.8 Clear the right nine digital positions of the AC */ | |
| AC &= 0777000; | |
| TRACE_PRINT(ORD_MSG, ("[%06o]: CLR\n", PC-1)); | |
| } | |
| /* IOS - In / Out Stop */ | |
| /* Check TTI for character. If so, put in LR and set LR bit 0. */ | |
| if (iosta & IOS_TTI) { | |
| int32 rbuf; | |
| rbuf = tti(0,0,0); | |
| TRACE_PRINT(IOS_MSG, ("TTI: character received='%c'\n", rbuf &077)); | |
| printf("TTI: character received='%c'\n", rbuf &077); | |
| LR &= 0266666; /* Clear bits 0,2,5,8,...,17 */ | |
| LR |= SIGN; /* Set bit 0, character available. */ | |
| LR |= ((rbuf & 001) >> 0) << 15;/* bit 2 */ | |
| LR |= ((rbuf & 002) >> 1) << 12;/* bit 5 */ | |
| LR |= ((rbuf & 004) >> 2) << 9; /* bit 8 */ | |
| LR |= ((rbuf & 010) >> 3) << 6; /* bit 11 */ | |
| LR |= ((rbuf & 020) >> 4) << 3; /* bit 14 */ | |
| LR |= ((rbuf & 040) >> 5) << 0; /* bit 17 */ | |
| } | |
| if ((op & OOPR_HLT) == OOPR_IOS) { /* I/O 0.8 IOS */ | |
| TRACE_PRINT(IOS_MSG, ("[%06o] I/O Operation\n", PC-1)); | |
| switch (op & OOPR_IOS_MASK) { | |
| case OOPR_P7H: | |
| case OOPR_P6H: | |
| { | |
| uint32 tmpAC = 0; | |
| tmpAC |= ((AC & 0000001) >> 0) << 0; /* bit 17 */ | |
| tmpAC |= ((AC & 0000010) >> 3) << 1; /* bit 14 */ | |
| tmpAC |= ((AC & 0000100) >> 6) << 2; /* bit 11 */ | |
| tmpAC |= ((AC & 0001000) >> 9) << 3; /* bit 8 */ | |
| tmpAC |= ((AC & 0010000) >> 12) << 4; /* bit 5 */ | |
| tmpAC |= ((AC & 0100000) >> 15) << 5; /* bit 2 */ | |
| tmpAC &= 0077; | |
| if ((op & OOPR_IOS_MASK) == OOPR_P7H) { | |
| tmpAC |= 0100; /* Punch 7th hole. */ | |
| TRACE_PRINT(ERROR_MSG, ("[%06o] Punch 7 holes\n", PC-1)); | |
| } else { | |
| TRACE_PRINT(ERROR_MSG, ("[%06o] Punch 6 holes\n", PC-1)); | |
| } | |
| ptp (0, 0, tmpAC); /* Punch one character on TTO */ | |
| } | |
| break; | |
| case OOPR_PNT: | |
| { | |
| uint32 tmpAC = 0; | |
| tmpAC |= ((AC & 0000001) >> 0) << 0; /* bit 17 */ | |
| tmpAC |= ((AC & 0000010) >> 3) << 1; /* bit 14 */ | |
| tmpAC |= ((AC & 0000100) >> 6) << 2; /* bit 11 */ | |
| tmpAC |= ((AC & 0001000) >> 9) << 3; /* bit 8 */ | |
| tmpAC |= ((AC & 0010000) >> 12) << 4; /* bit 5 */ | |
| tmpAC |= ((AC & 0100000) >> 15) << 5; /* bit 2 */ | |
| tto (0, 0, tmpAC & 077); /* Print one character on TTO */ | |
| } | |
| break; | |
| case OOPR_R3C: | |
| AC = petr(3, 0, 0); | |
| break; | |
| case OOPR_R1C: | |
| AC &= 0333333; /* Clear bits 0,3,6,9,12,15 */ | |
| AC |= petr(1, 0, 0); | |
| break; | |
| case OOPR_DIS: | |
| #ifdef USE_DISPLAY | |
| LP = dpy (AC); /* Display point on the CRT */ | |
| #endif /* USE_DISPLAY */ | |
| break; | |
| } | |
| } | |
| /* 1.1 TAC and PEN */ | |
| if ((op & OOPR_PEN_MASK) == OOPR_PEN) { /* pen 1.1 Read the light pen flip flops 1 and 2 into AC0 and AC1 */ | |
| TRACE_PRINT(IOS_MSG, ("[%06o] Light Pen %01o\n", PC-1, LP)); | |
| AC &= AMASK; | |
| AC |= (LP & 1) << 17; | |
| AC |= (LP & 2) << 16; | |
| AC &= DMASK; | |
| } | |
| if ((op & OOPR_TAC_MASK) == OOPR_TAC) { /* tac 1.1 Insert a one in each digital position of the AC whereever there is a one in the corresponding digital position of the TAC */ | |
| TRACE_PRINT(IOS_MSG, ("[%06o] TAC %06o\n", PC-1, TAC)); | |
| AC |= TAC; | |
| } | |
| /* 1.2: COM, AMB, TBR */ | |
| if (op & OOPR_COM) { /* com 1.2 Complement every digit in the accumulator */ | |
| AC = ~AC; | |
| inst_ctr.com++; | |
| } | |
| switch (op & OOPR_AMB_MASK) { | |
| case OOPR_AMB: | |
| inst_ctr.amb++; | |
| MBR = AC; | |
| break; | |
| case OOPR_TBR: | |
| TRACE_PRINT(IOS_MSG, ("[%06o] TBR %06o\n", PC-1, TBR)); | |
| MBR |= TBR; | |
| break; | |
| case OOPR_LMB: | |
| MBR = LR; | |
| inst_ctr.lmb++; | |
| break; | |
| } | |
| /* 1.3, 1.4: can these happen together? */ | |
| switch (op & OOPR_SHF_MASK) { | |
| case OOPR_MLR: | |
| LR = MBR; | |
| inst_ctr.mbl++; | |
| break; | |
| case OOPR_SHR: /* Shift AC Right, preserve bit 0. */ | |
| { | |
| int32 bit0; | |
| bit0 = AC & 0400000; | |
| AC = AC >> 1; | |
| AC |= bit0; | |
| inst_ctr.shr++; | |
| break; | |
| } | |
| case OOPR_CYR: /* cyr 1.4 Cycle the AC right one digital position (AC17 -> AC0) */ | |
| { | |
| int32 bit17; | |
| bit17 = (AC & 1) << 17; | |
| AC >>= 1; | |
| AC |= bit17; | |
| inst_ctr.cyr++; | |
| } | |
| break; | |
| } | |
| if (op & OOPR_PAD) { /* 1.5 Partial Add (XOR): AC = MBR ^ AC */ | |
| if (op & OOPR_CRY) { /* 1.7 */ | |
| TRACE_PRINT(ORD_MSG, ("[%06o] PAD+CRY: AC=%06o, MBR=%06o = ", PC-1, AC, MBR)); | |
| AC = AC + MBR; | |
| if (AC & 01000000) { | |
| AC += 1; | |
| } else; | |
| AC &= DMASK; | |
| TRACE_PRINT(ORD_MSG, ("%06o\n", AC)); | |
| inst_ctr.cry++; | |
| } else { | |
| TRACE_PRINT(ORD_MSG, ("[%06o] PAD: AC=%06o, MBR=%06o\n", PC-1, AC, MBR)); | |
| AC = AC ^ MBR; | |
| AC &= DMASK; | |
| TRACE_PRINT(ORD_MSG, ("[%06o] PAD: Check: AC=%06o\n", PC-1, AC)); | |
| } | |
| inst_ctr.pad++; | |
| } | |
| if (op & OOPR_CRY) { /* 1.7 */ | |
| if (op & OOPR_PAD) { | |
| } else { | |
| TRACE_PRINT(ERROR_MSG, ("[%06o] CRY: TODO: AC=%06o\n", PC-1, AC)); | |
| inst_ctr.cry++; | |
| } | |
| } | |
| if ((op & OOPR_HLT) == OOPR_HLT) { /* hlt 1.8 Halt the computer */ | |
| TRACE_PRINT(IOS_MSG, ("[%06o] HALT Instruction\n", PC-1)); | |
| reason = STOP_HALT; | |
| } | |
| return reason; | |
| } |