1476 lines
57 KiB
C
1476 lines
57 KiB
C
/*************************************************************************
|
|
* *
|
|
* $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 LPEN = 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;
|
|
|
|
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, LPEN, 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) {
|
|
sim_printf("Error: AC > DMASK\n");
|
|
}
|
|
if (MBR > DMASK) {
|
|
sim_printf("Error: MBR > DMASK\n");
|
|
}
|
|
if (LR > DMASK) {
|
|
sim_printf("Error: LR > DMASK\n");
|
|
}
|
|
if (!MEM_ADDR_OK(MAR)) {
|
|
sim_printf("Error: MAR > %06o\n", MEMSIZE);
|
|
}
|
|
|
|
}
|
|
|
|
t_stat sim_opr_orig(int32 op);
|
|
|
|
t_stat sim_instr (void)
|
|
{
|
|
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.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;
|
|
}
|
|
AC &= DMASK;
|
|
inst_ctr.add++;
|
|
break;
|
|
case 1: /* adx */
|
|
MAR = compute_index(y, XR);
|
|
Read();
|
|
AC = AC + MBR;
|
|
if (AC > DMASK) {
|
|
AC += 1;
|
|
}
|
|
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, LPEN));
|
|
AC &= AMASK;
|
|
AC |= (LPEN & 1) << 17;
|
|
AC |= (LPEN & 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));
|
|
sim_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
|
|
LPEN = 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;
|
|
}
|
|
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 > (int32)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)
|
|
{
|
|
sim_printf("Set CPU Extended Mode\n");
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat cpu_set_noext (UNIT *uptr, int32 val, char *cptr, void *desc)
|
|
{
|
|
sim_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;
|
|
|
|
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, int flag) {
|
|
uint32 word;
|
|
t_addr j, lo, hi, sz, sz_words;
|
|
const 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;
|
|
}
|
|
}
|
|
|
|
sim_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));
|
|
sim_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
|
|
LPEN = 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, LPEN));
|
|
AC &= AMASK;
|
|
AC |= (LPEN & 1) << 17;
|
|
AC |= (LPEN & 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;
|
|
}
|
|
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;
|
|
}
|