Can now boot the last test programs from floppy, and runs INSTRUCTION-C and PAGING-C without errors.
1796 lines
51 KiB
C
1796 lines
51 KiB
C
/*
|
|
* Copyright (c) 2023 Anders Magnusson.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <setjmp.h>
|
|
|
|
#include "sim_defs.h"
|
|
|
|
#include "nd100_defs.h"
|
|
|
|
#define MAXMEMSIZE 512*1024
|
|
|
|
typedef struct {
|
|
int ir;
|
|
int16 sts;
|
|
int16 d;
|
|
int16 p;
|
|
int16 b;
|
|
int16 l;
|
|
int16 a;
|
|
int16 t;
|
|
int16 x;
|
|
} Hist_entry ;
|
|
|
|
#define HIST_IR_INVALID -1
|
|
#define HIST_MIN 0
|
|
#define HIST_MAX 1000000
|
|
|
|
static int32 hist_p = 0;
|
|
static int32 hist_cnt = 0;
|
|
static Hist_entry *hist = NULL;
|
|
static struct intr *ilnk[4]; /* level 10-13 */
|
|
jmp_buf env;
|
|
|
|
uint16 R[8], RBLK[16][8], regSTH, oregP;
|
|
int curlvl; /* current interrupt level */
|
|
int iic, iie, iid; /* IIC/IIE/IID register */
|
|
int pid, pie; /* PID/PIE register */
|
|
int ald, eccr, pvl, lmp, opr;
|
|
|
|
#define SETC() (regSTL |= STS_C)
|
|
#define CLRC() (regSTL &= ~STS_C)
|
|
#define SETQ() (regSTL |= STS_Q)
|
|
#define CLRQ() (regSTL &= ~STS_Q)
|
|
#define SETO() (regSTL |= STS_O)
|
|
#define CLRO() (regSTL &= ~STS_O)
|
|
|
|
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 hist_set(UNIT * uptr, int32 val, CONST char * cptr, void * desc);
|
|
t_stat hist_show(FILE * st, UNIT * uptr, int32 val, CONST void * desc);
|
|
static void hist_fprintf(FILE *fp, int itemNum, Hist_entry *hptr);
|
|
static void hist_save(int ir);
|
|
|
|
static int getoff(int ir);
|
|
static int iox_check(int dev);
|
|
static int nd_trr(int reg);
|
|
static int nd_tra(int reg);
|
|
static int nd_mcl(int reg);
|
|
static int nd_mst(int reg);
|
|
static int highest_level(void);
|
|
static void identrm(int);
|
|
static uint16 add3(uint16 a, uint16 d, uint16 c);
|
|
int fls(int);
|
|
|
|
int ins_store(int ir, int addr);
|
|
int ins_stdf(int ir, int addr);
|
|
int ins_lddf(int ir, int addr);
|
|
int ins_min(int ir, int addr);
|
|
int ins_load(int ir, int addr);
|
|
int ins_add(int ir, int addr);
|
|
int ins_andor(int ir, int addr);
|
|
void ins_dnz(int ins);
|
|
void ins_nlz(int ins);
|
|
int ins_fad(int ir, int addr);
|
|
int ins_fsb(int ir, int addr);
|
|
int ins_fmu(int ir, int addr);
|
|
int ins_fdv(int ir, int addr);
|
|
int ins_mpy(int ir, int addr);
|
|
int ins_jmpl(int ir, int addr);
|
|
int ins_cjp(int ir, int addr);
|
|
int ins_skp(int ir, int addr);
|
|
int ins_skip_ext(int IR);
|
|
int ins_rop(int ir, int addr);
|
|
int ins_mis(int IR, int addr);
|
|
int ins_sht(int IR, int addr);
|
|
int ins_na(int IR, int addr);
|
|
int ins_iox(int IR, int addr);
|
|
int ins_arg(int IR, int addr);
|
|
int ins_bop(int IR, int addr);
|
|
|
|
#define ID(x) (((x) & ND_MEMMSK) >> ND_MEMSH)
|
|
|
|
int (*ins_table[32])(int ir, int addr) = {
|
|
ins_store, ins_store, ins_store, ins_store, /* STZ/STA/STT/STX */
|
|
ins_stdf, ins_lddf, ins_stdf, ins_lddf, /* STD/LDD/STF/LDF */
|
|
ins_min, ins_load, ins_load, ins_load, /* MIN/LDA/LDT/LDX */
|
|
ins_add, ins_add, ins_andor, ins_andor, /* ADD/SUB/ADN/ORA */
|
|
ins_fad, ins_fsb, ins_fmu, ins_fdv, /* FAD/FSB/FMU/FDV */
|
|
ins_mpy, ins_jmpl, ins_cjp, ins_jmpl, /* MPY/JMP/CJP/JPL */
|
|
ins_skp, ins_rop, ins_mis, ins_sht, /* SKP/ROP/MIS/SHT */
|
|
ins_na, ins_iox, ins_arg, ins_bop /* --/IOX/ARG/BOP */
|
|
};
|
|
|
|
UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) };
|
|
|
|
REG cpu_reg[] = {
|
|
{ ORDATA(STS, R[0], 16) },
|
|
{ ORDATA(D, regD, 16) },
|
|
{ ORDATA(P, regP, 16) },
|
|
{ ORDATA(B, regB, 16) },
|
|
{ ORDATA(L, regL, 16) },
|
|
{ ORDATA(A, regA, 16) },
|
|
{ ORDATA(T, regT, 16) },
|
|
{ ORDATA(X, regX, 16) },
|
|
{ ORDATA(oldP, oregP, 16) },
|
|
{ DRDATA(LVL, curlvl, 4) },
|
|
{ DRDATA(LMP, lmp, 16) },
|
|
{ DRDATA(PVL, pvl, 4) },
|
|
{ ORDATA(PID, pid, 16) },
|
|
{ ORDATA(PIE, pie, 16) },
|
|
{ ORDATA(IIC, iic, 4) },
|
|
{ ORDATA(IIE, iie, 10) },
|
|
{ ORDATA(OPR, opr, 16) },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB cpu_mod[] = {
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY",
|
|
&hist_set, &hist_show },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE cpu_dev = {
|
|
"CPU", &cpu_unit, cpu_reg, cpu_mod,
|
|
1, 8, 16, 1, 8, 16,
|
|
&cpu_ex, &cpu_dep, &cpu_reset,
|
|
NULL, NULL, NULL
|
|
};
|
|
|
|
t_stat
|
|
sim_instr(void)
|
|
{
|
|
int IR;
|
|
int reason;
|
|
int n, i;
|
|
int first = 1;
|
|
uint16 off;
|
|
|
|
(void)setjmp(env);
|
|
reason = 0;
|
|
|
|
while (reason == 0) {
|
|
if (sim_interval <= 0)
|
|
if ((reason = sim_process_event ()))
|
|
break;
|
|
|
|
if (regSTL & STS_Z)
|
|
intrpt14(IIE_V, PM_DMA); /* no longjmp here */
|
|
if ((pid & pie) > (0177777 >> (15-curlvl)) && ISION()) {
|
|
/* need to interrupt */
|
|
pvl = curlvl;
|
|
n = highest_level();
|
|
for (i = 0; i < 8; i++) {
|
|
RBLK[curlvl][i] = R[i];
|
|
R[i] = RBLK[n][i];
|
|
}
|
|
curlvl = n;
|
|
if (curlvl == 14 && iic == 1) /* mon call */
|
|
regT = SEXT8(IR);
|
|
}
|
|
|
|
/*
|
|
* ND100 manual clause 2.3.8.3 says that the instruction faults happens
|
|
* after the P reg is incremented, hence will point to one instruction
|
|
* past the instruction causing the fault.
|
|
* But; if the fault is during fetch, P will not be incremented.
|
|
*/
|
|
IR = rdmem(regP, M_FETCH);
|
|
oregP = regP++;
|
|
|
|
sim_interval--;
|
|
|
|
if (regSTH & STS_TG)
|
|
return STOP_BP;
|
|
|
|
if (first == 0 && sim_brk_summ && sim_brk_test(oregP, SWMASK ('E'))) {
|
|
reason = STOP_BP;
|
|
break;
|
|
}
|
|
first = 0;
|
|
|
|
if (hist_cnt)
|
|
hist_save(IR);
|
|
|
|
/*
|
|
* Execute instruction. We intercept it here before calling
|
|
* the instruction routine and just update IR.
|
|
*/
|
|
if (ISEXR(IR)) { /* Execute instruction */
|
|
IR = R[(IR >> 3) & 07];
|
|
#ifdef notyet
|
|
if (ISEXR(IR))
|
|
trap...
|
|
#endif
|
|
if (hist_cnt)
|
|
hist_save(IR);
|
|
}
|
|
|
|
if (ID(IR) < ND_CJP || ID(IR) == ND_JPL)
|
|
off = getoff(IR);
|
|
|
|
reason = (*ins_table[ID(IR)])(IR, off);
|
|
}
|
|
return reason;
|
|
}
|
|
|
|
t_stat
|
|
cpu_reset(DEVICE *dptr)
|
|
{
|
|
sim_brk_types = sim_brk_dflt = SWMASK ('E');
|
|
regSTH |= STS_N100;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat
|
|
cpu_ex(t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
|
|
{
|
|
if (addr >= MAXMEMSIZE)
|
|
return SCPE_ARG;
|
|
*vptr = rdmem(addr, M_PT); /* XXX */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Memory deposit */
|
|
|
|
t_stat
|
|
cpu_dep(t_value val, t_addr addr, UNIT *uptr, int32 sw)
|
|
{
|
|
return SCPE_ARG;
|
|
}
|
|
|
|
t_stat
|
|
cpu_set_size(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
return SCPE_ARG;
|
|
}
|
|
|
|
t_stat
|
|
cpu_boot(int32 unitno, DEVICE *dptr)
|
|
{
|
|
return SCPE_ARG;
|
|
}
|
|
|
|
/*
|
|
* Store register.
|
|
*/
|
|
int
|
|
ins_store(int IR, int off)
|
|
{
|
|
int n = ((IR >> 11) & 03) + 4;
|
|
wrmem(off, n == 4 ? 0 : R[n], SELPT2(IR));
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/*
|
|
* Store double or triple reg.
|
|
*/
|
|
int
|
|
ins_stdf(int IR, int off)
|
|
{
|
|
int pt = SELPT2(IR);
|
|
|
|
if (ID(IR) == ID(ND_STF) /* && 48-bit */)
|
|
wrmem(off++, regT, pt);
|
|
wrmem(off++, regA, pt);
|
|
wrmem(off, regD, pt);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/*
|
|
* Load double or triple reg.
|
|
*/
|
|
int
|
|
ins_lddf(int IR, int off)
|
|
{
|
|
int pt = SELPT2(IR);
|
|
if (ID(IR) == ID(ND_LDF) /* && 48-bit */)
|
|
regT = rdmem(off++, pt);
|
|
regA = rdmem(off++, pt);
|
|
regD = rdmem(off, pt);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/*
|
|
* Load one reg.
|
|
*/
|
|
int
|
|
ins_load(int IR, int off)
|
|
{
|
|
R[((IR&014000) >> 11) + 4] = rdmem(off, SELPT2(IR));
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/*
|
|
* Increment (and skip?).
|
|
*/
|
|
int
|
|
ins_min(int IR, int off)
|
|
{
|
|
uint16 s;
|
|
int pt = SELPT2(IR);
|
|
|
|
wrmem(off, s = rdmem(off, pt) + 1, pt);
|
|
if (s == 0)
|
|
regP++;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/*
|
|
* Add/sub.
|
|
*/
|
|
int
|
|
ins_add(int IR, int off)
|
|
{
|
|
uint16 d = rdmem(off, SELPT2(IR));
|
|
int n = 0;
|
|
|
|
if (ID(IR) == ID(ND_SUB))
|
|
n = 1, d = ~d;
|
|
regA = add3(regA, d, n);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/*
|
|
* And/or
|
|
*/
|
|
int
|
|
ins_andor(int IR, int off)
|
|
{
|
|
uint16 s = rdmem(off, SELPT2(IR));
|
|
|
|
regA = BIT11(IR) ? (regA | s) : (regA & s);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/*
|
|
* Byte instructions (BFILL, MOVB, MOVBF). Requires at least ND100.
|
|
*
|
|
* Byte operands occupy fields within the memory. Operands are specified
|
|
* by two 16-bit words, known as descriptors, giving the start address
|
|
* and the field length.
|
|
*
|
|
* The start address is in register A if source, X if destination (D1)
|
|
* The rest of the descriptor is in D if source, T if destination (D2)
|
|
*
|
|
* In D2 bits
|
|
* 11-0 are the field length (in bytes).
|
|
* 12 - Ignored
|
|
* 13 - Must be 0
|
|
* 14 - 0 = normal PT, 1 = alternate
|
|
* 15 - 0 = left byte start, 1 = right byte.
|
|
*
|
|
*/
|
|
#define BYTELN(x) ((x) & 07777)
|
|
/*
|
|
* Byte fill (BFILL).
|
|
*
|
|
* This instruction has only one operand. The destination operand is
|
|
* specified in the X and T registers. The right-most byte in the A-reg
|
|
* (bits 0-7) is filled into the destination field.
|
|
* After execution, the X-register and T-register bit 15 point to the
|
|
* end of the field (after the last byte). The T-register bits (0-11)
|
|
* equal zero.
|
|
* The instruction will always have a skip return (no error condition).
|
|
*/
|
|
static void
|
|
ins_bfill(int IR)
|
|
{
|
|
while (regT & 07777) {
|
|
wrbyte(regX, regA, BIT15(regT), M_APT);
|
|
regT--;
|
|
regT ^= 0100000;
|
|
if (BIT15(regT) == 0)
|
|
regX++;
|
|
}
|
|
regP++; /* skip return */
|
|
}
|
|
|
|
/*
|
|
* Move bytes (MOVB)
|
|
*
|
|
* This instruction moves a block of bytes from the location specified for
|
|
* the source operand to the location specified for the destination operand.
|
|
* The move operation takes care of source— and destination-field overlap.
|
|
* The number of bytes moved is determined by the shortest field length of
|
|
* the operands.
|
|
* After execution, the AD and XT registers (bit 15 in D and T) point to
|
|
* the end of the field that is moved (after the last byte). D-reg. bits
|
|
* 0-11 equal zero and T-reg. bits 0-11 contain the number of bytes moved.
|
|
* The T—reg. bits 12—13 and the D-reg. bit 12 are used during the execution,
|
|
* and are left cleared. Bit 13 must be zero before execution (used as an
|
|
* interrupt mark).
|
|
* The instruction will always have a skip return (no error condition).
|
|
*
|
|
* Implementation note: Because this instruction can be interrupted (by a
|
|
* page fault for example) byte for byte must be moved, and every state
|
|
* will be stored in the registers (similar to the microcode implementation).
|
|
* The usage is:
|
|
* - BIT13(regD): all regs are setup in the middle of execution.
|
|
*/
|
|
static void
|
|
ins_movb(int IR)
|
|
{
|
|
int i;
|
|
|
|
if (BIT13(regD) == 0) {
|
|
/* setup for copy */
|
|
int len = BYTELN(regD);
|
|
if (BYTELN(regT) < len)
|
|
len = BYTELN(regT);
|
|
regT = (regT & 0140000) | len; /* T is max */
|
|
regD = (regD & 0140000); /* D is zero */
|
|
regD |= (1 << 13); /* setup done! */
|
|
}
|
|
|
|
if (regX > regA) { /* copy bottom-top */
|
|
for (i = BYTELN(regD); i < BYTELN(regT); i++, regD++) {
|
|
int8 w = rdbyte(regA, BIT15(regD), M_APT);
|
|
wrbyte(regX, w, BIT15(regT), M_APT);
|
|
regD ^= 0100000;
|
|
if (BIT15(regD) == 0) regA++;
|
|
regT ^= 0100000;
|
|
if (BIT15(regT) == 0) regX++;
|
|
}
|
|
regD &= 0140000; /* Clear setup + count bits */
|
|
} else { /* copy top-bottom */
|
|
|
|
}
|
|
regP++; /* skip return */
|
|
}
|
|
|
|
/*
|
|
* This instruction moves a block of bytes from the location specified as
|
|
* the source operand to the location specified as the destination operand.
|
|
* The move operation always starts with the first byte (lower address).
|
|
*
|
|
* The number of bytes moved is determined by the shortest field length of
|
|
* the operands. Forbidden overlap exists when the source data to be moved,
|
|
* will be destroyed. That happens when a byte is stored in a word before
|
|
* that word is read from memory. This is reported by an error return (no skip).
|
|
*
|
|
* After successful execution, the A,D and X,T registers (bit 15 in D and T)
|
|
* point to the end of the fields that are moved (after the last byte).
|
|
* The numbers initially contained in the D- and T-registers, bits 0-11,
|
|
* are decremented by the number of bytes moved.
|
|
*
|
|
* The T—reg. bits 12—13 and the D—reg. bit 12 are used during the execution
|
|
* and are left cleared. Bit 13 must be zero before execution (used as an
|
|
* interrupt mark). The instruction will have a skip—return when
|
|
* no illegal overlap exists.
|
|
*/
|
|
static void
|
|
ins_movbf(int IR)
|
|
{
|
|
int i;
|
|
|
|
if (BIT13(regD) == 0) {
|
|
/* setup for copy */
|
|
int len = BYTELN(regD);
|
|
if (BYTELN(regT) < len)
|
|
len = BYTELN(regT);
|
|
regT = (regT & 0140000) | len; /* T is max */
|
|
regD = (regD & 0140000); /* D is zero */
|
|
regD |= (1 << 13); /* setup done! */
|
|
if (regX > regA && regX < (regA + (len >> 1)))
|
|
return;
|
|
}
|
|
|
|
for (i = BYTELN(regD); i < BYTELN(regT); i++, regD++) {
|
|
int8 w = rdbyte(regA, BIT15(regD), M_APT);
|
|
wrbyte(regX, w, BIT15(regT), M_APT);
|
|
regD ^= 0100000;
|
|
if (BIT15(regD) == 0) regA++;
|
|
regT ^= 0100000;
|
|
if (BIT15(regT) == 0) regX++;
|
|
}
|
|
regD &= 0140000; /* Clear setup + count bits */
|
|
regT &= 0140000; /* Clear setup + count bits */
|
|
regP++; /* skip return */
|
|
}
|
|
|
|
/*
|
|
* Instructions with the same encoding as skip instructions.
|
|
*/
|
|
int
|
|
ins_skip_ext(int IR)
|
|
{
|
|
uint16 d;
|
|
int16 ss, sd;
|
|
int32 shc;
|
|
int paddr, reason = 0;
|
|
|
|
if ((IR & 0177707) == ND_SKP_CLEPT) {
|
|
intrpt14(IIE_II, PM_CPU);
|
|
} else if ((IR & 0177700) == ND_SKP_LDATX) {
|
|
/* Special physmem instructions */
|
|
d = regX + ((IR & 070) >> 3);
|
|
paddr = ((unsigned int)regT << 16) | (unsigned int)d;
|
|
switch (IR & ~070) {
|
|
case ND_SKP_LDATX:
|
|
regA = prdmem(paddr, PM_CPU);
|
|
break;
|
|
case ND_SKP_STZTX:
|
|
pwrmem(paddr, 0, PM_CPU);
|
|
break;
|
|
case ND_SKP_STATX:
|
|
pwrmem(paddr, regA, PM_CPU);
|
|
break;
|
|
case ND_SKP_STDTX:
|
|
pwrmem(paddr++, regA, PM_CPU);
|
|
pwrmem(paddr, regD, PM_CPU);
|
|
break;
|
|
case ND_SKP_LDXTX:
|
|
regX = prdmem(paddr, PM_CPU);
|
|
break;
|
|
case ND_SKP_LDDTX:
|
|
regA = prdmem(paddr++, PM_CPU);
|
|
regD = prdmem(paddr, PM_CPU);
|
|
break;
|
|
case ND_SKP_LDBTX:
|
|
regB = (prdmem(paddr, PM_CPU) << 1) | 0177000;
|
|
break;
|
|
default:
|
|
intrpt14(IIE_II, PM_CPU);
|
|
}
|
|
} else if (IR == ND_SKP_MIX3) {
|
|
/* X = (A-1)*3 */
|
|
regX = (regA-1)*3;
|
|
} else if (IR == ND_SKP_IDENT10) {
|
|
identrm(10);
|
|
} else if (IR == ND_SKP_IDENT11) {
|
|
identrm(11);
|
|
} else if (IR == ND_SKP_IDENT12) {
|
|
identrm(12);
|
|
} else if (IR == ND_SKP_IDENT13) {
|
|
identrm(13);
|
|
} else if (IR == ND_SKP_ADDD) {
|
|
intrpt14(IIE_II, PM_CPU);
|
|
} else if (IR == ND_SKP_BFILL) {
|
|
ins_bfill(IR);
|
|
} else if (IR == ND_SKP_MOVB) {
|
|
ins_movb(IR);
|
|
} else if (IR == ND_SKP_MOVBF) {
|
|
ins_movbf(IR);
|
|
} else if (IR == ND_SKP_LWCS) {
|
|
;
|
|
} else if (IR == 0140127) { /* XXX */
|
|
intrpt14(IIE_II, PM_CPU);
|
|
} else if (IR == ND_SKP_VERSN) {
|
|
intrpt14(IIE_II, PM_CPU);
|
|
} else if (IR == ND_SKP_LBYT) {
|
|
d = rdmem(regT + (regX >> 1), M_APT);
|
|
if (regX & 1)
|
|
regA = d & 0377;
|
|
else
|
|
regA = d >> 8;
|
|
} else if (IR == ND_SKP_SBYT) {
|
|
d = regT + (regX >> 1);
|
|
if (regX & 1)
|
|
wrmem(d, (rdmem(d, M_APT) & 0xff00) | (regA & 0xff), M_APT);
|
|
else
|
|
wrmem(d, (rdmem(d, M_APT) & 0xff) | (regA << 8), M_APT);
|
|
} else if ((IR & 0177700) == ND_SKP_RMPY) {
|
|
ss = R[(IR & 070) >> 3];
|
|
sd = R[IR & 07];
|
|
shc = ss * sd;
|
|
regD = shc;
|
|
regA = shc >> 16;
|
|
} else if ((IR & 0177700) == ND_SKP_RDIV) {
|
|
ss = R[(IR & 070) >> 3];
|
|
shc = (regA << 16) | regD;
|
|
regA = shc / ss;
|
|
regD = shc % ss;
|
|
} else if (IR == 0142700 || IR == 0143700) {
|
|
/* ??? what is this? */
|
|
intrpt14(IIE_II, PM_CPU);
|
|
} else
|
|
reason = STOP_UNHINS;
|
|
return reason;
|
|
}
|
|
|
|
/*
|
|
* The instruction SRB <level * 8> stores the contents of the register
|
|
* block on the program level specified in the level field of
|
|
* the instruction. The specified register block is stored in
|
|
* succeeding memory locations starting at the location specified by
|
|
* the contents of the X register.
|
|
* If the current program level is specified, the stored P register points
|
|
* to the instruction following SRB.
|
|
*
|
|
* Affected: (EL), +1 +2 +3 +4 +5 +6 +7
|
|
* P X T A D L STS B
|
|
*/
|
|
static int s2r[] = { rnP, rnX, rnT, rnA, rnD, rnL, rnSTS, rnB };
|
|
static void
|
|
ins_srb(int IR)
|
|
{
|
|
int i, n = (IR >> 3) & 017;
|
|
|
|
/* Save current level (maybe used) to reg block */
|
|
for (i = 0; i < 8; i++)
|
|
RBLK[curlvl][i] = R[i];
|
|
|
|
/* store requested block to memory */
|
|
for (i = 0; i < 8; i++)
|
|
wrmem(regX+i, RBLK[n][s2r[i]], M_APT);
|
|
}
|
|
|
|
/*
|
|
* The instruction LRB <level * 8> loads the contents of the register
|
|
* block on program level specified in the level field of the instruction.
|
|
* The specified register block is loaded by the contents of succeeding
|
|
* memory locations starting at the location specified by the contents
|
|
* of the X register.
|
|
* If the current program level is specified, the P register is not affected.
|
|
* Affected: All the registers on specified program level are affected.
|
|
* Note: If the current level is specified, the P register is not affected.
|
|
*/
|
|
static void
|
|
ins_lrb(int IR)
|
|
{
|
|
int i, n = (IR >> 3) & 017;
|
|
|
|
/* fetch from memory */
|
|
for (i = 0; i < 8; i++)
|
|
RBLK[n][s2r[i]] = rdmem(regX+i, M_APT);
|
|
RBLK[n][rnSTS] &= 0377;
|
|
|
|
if (n == curlvl)
|
|
for (i = 0; i < 8; i++)
|
|
if (i != rnP)
|
|
R[i] = RBLK[n][i];
|
|
}
|
|
|
|
/*
|
|
* Priv instructions, mostly for handling status reg.
|
|
*/
|
|
static int
|
|
nd_mis_opc(int IR)
|
|
{
|
|
static int srbits[] = { 0, STS_IONI, STS_IONI, 0,
|
|
STS_PONI, (STS_PONI|STS_IONI), STS_SEXI, STS_SEXI,
|
|
STS_PONI, 0, (STS_PONI|STS_IONI) };
|
|
int rv = 0;
|
|
|
|
mm_privcheck();
|
|
switch (IR) {
|
|
/* case ND_MIS_OPCOM: */
|
|
/* break; */
|
|
|
|
case ND_MIS_IOF:
|
|
case ND_MIS_POF:
|
|
case ND_MIS_PIOF:
|
|
case ND_MIS_REX:
|
|
regSTH &= ~srbits[IR & 017];
|
|
break;
|
|
|
|
case ND_MIS_ION:
|
|
case ND_MIS_SEX:
|
|
case ND_MIS_PON:
|
|
case ND_MIS_PION:
|
|
regSTH |= srbits[IR & 017];
|
|
break;
|
|
|
|
case ND_MIS_IOXT:
|
|
rv = iox_check(regT);
|
|
break;
|
|
|
|
case ND_MIS_EXAM:
|
|
regT = prdmem((regA << 16) | regD, M_FETCH);
|
|
break;
|
|
|
|
case ND_MIS_DEPO:
|
|
pwrmem((regA << 16) | regD, regT, M_FETCH);
|
|
break;
|
|
|
|
default:
|
|
rv = STOP_UNHINS;
|
|
break;
|
|
}
|
|
return rv;
|
|
|
|
}
|
|
|
|
/*
|
|
* Miscellaneous instructions.
|
|
*/
|
|
int
|
|
ins_mis(int IR, int off)
|
|
{
|
|
int reason = 0;
|
|
int n, i;
|
|
|
|
if ((IR & 0177760) == ND_MIS_OPCOM)
|
|
reason = nd_mis_opc(IR);
|
|
else if ((IR & ND_MIS_TRMSK) == ND_MIS_TRA)
|
|
reason = nd_tra(IR & ~ND_MIS_TRMSK);
|
|
else if ((IR & ND_MIS_TRMSK) == ND_MIS_TRR)
|
|
reason = nd_trr(IR & ~ND_MIS_TRMSK);
|
|
else if ((IR & ND_MIS_TRMSK) == ND_MIS_MST)
|
|
reason = nd_mst(IR & ~ND_MIS_TRMSK);
|
|
else if ((IR & ND_MIS_TRMSK) == ND_MIS_MCL)
|
|
reason = nd_mcl(IR & ~ND_MIS_TRMSK);
|
|
else if ((IR & ND_MIS_IRRMSK) == ND_MIS_IRR) {
|
|
mm_privcheck();
|
|
n = (IR >> 3) & 017;
|
|
if (n == curlvl)
|
|
regA = R[IR & 07];
|
|
else
|
|
regA = RBLK[n][IR & 07];
|
|
} else if ((IR & ND_MIS_IRRMSK) == ND_MIS_IRW) {
|
|
mm_privcheck();
|
|
n = (IR >> 3) & 017;
|
|
RBLK[n][IR & 07] = regA;
|
|
if (n == curlvl && (IR & 07) != rnP)
|
|
R[IR & 07] = regA;
|
|
} else if ((IR & ND_MIS_RBMSK) == ND_MIS_SRB) {
|
|
ins_srb(IR);
|
|
} else if ((IR & ND_MIS_RBMSK) == ND_MIS_LRB) {
|
|
ins_lrb(IR);
|
|
} else if ((IR & ND_MONMSK) == ND_MON) {
|
|
RBLK[14][rnT] = SEXT8(IR);
|
|
intrpt14(IIE_MC, PM_CPU);
|
|
} else if ((IR & ND_MONMSK) == ND_WAIT) {
|
|
if (ISION() == 0) {
|
|
reason = STOP_WAIT;
|
|
} else if (curlvl > 0) {
|
|
pid &= ~(1 << curlvl);
|
|
n = highest_level();
|
|
if (curlvl != n) {
|
|
for (i = 0; i < 8; i++) {
|
|
RBLK[curlvl][i] = R[i];
|
|
R[i] = RBLK[n][i];
|
|
}
|
|
}
|
|
curlvl = n;
|
|
}
|
|
} else if ((IR & ND_MONMSK) == ND_MIS_NLZ) {
|
|
/* convert integer to floating point */
|
|
ins_nlz(IR);
|
|
} else if ((IR & ND_MONMSK) == ND_MIS_DNZ) {
|
|
ins_dnz(IR);
|
|
} else
|
|
reason = STOP_UNHINS;
|
|
return reason;
|
|
}
|
|
|
|
int
|
|
ins_sht(int IR, int off)
|
|
{
|
|
char sht_reg[] = { rnT, rnD, rnA, 0 };
|
|
int m, n, rs, i;
|
|
uint32 ushc;
|
|
|
|
rs = sht_reg[(IR >> 7) & 03];
|
|
n = BIT5(IR) ? 32 - IR & 037 : IR & 037;
|
|
m = BIT7(regSTL);
|
|
|
|
ushc = rs ? R[rs] : (regA << 16) | regD;
|
|
if (BIT5(IR)) { /* right */
|
|
int mm = BIT0(ushc);
|
|
for (i = 0; i < n; i++) {
|
|
m = BIT0(ushc);
|
|
ushc = ushc >> 1;
|
|
switch (IR & 03000) {
|
|
case 0: /* Arithmetic */
|
|
ushc |= (rs ? (BIT14(ushc) << 15) :
|
|
(BIT30(ushc) << 31));
|
|
break;
|
|
case 01000: /* ROT */
|
|
ushc |= (rs ? (m << 15) :
|
|
(m << 31));
|
|
break;
|
|
case 02000: /* zero end input */
|
|
break;
|
|
case 03000: /* link end input */
|
|
ushc |= (rs ? (mm << 15) :
|
|
(mm << 31));
|
|
break;
|
|
}
|
|
}
|
|
} else { /* left */
|
|
int mm = (rs ? BIT15(ushc) : BIT31(ushc));
|
|
for (i = 0; i < n; i++) {
|
|
m = (rs ? BIT15(ushc) : BIT31(ushc));
|
|
ushc = ushc << 1;
|
|
switch (IR & 03000) {
|
|
case 01000: /* ROT */
|
|
ushc |= m; break;
|
|
case 0: /* Arithmetic */
|
|
case 02000: /* zero end input */
|
|
break;
|
|
case 03000: /* link end input */
|
|
ushc |= mm; break;
|
|
}
|
|
}
|
|
}
|
|
regSTL = (regSTL & ~STS_M) | (m << 7);
|
|
if (rs == 0) {
|
|
regA = ushc >> 16;
|
|
regD = ushc;
|
|
} else
|
|
R[rs] = ushc;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
int
|
|
ins_na(int IR, int addr)
|
|
{
|
|
return STOP_UNHINS;
|
|
}
|
|
|
|
int
|
|
ins_iox(int IR, int addr)
|
|
{
|
|
return iox_check(IR & ND_IOXMSK);
|
|
}
|
|
|
|
int
|
|
ins_arg(int IR, int addr)
|
|
{
|
|
int rs, n = (IR >> 8) & 03;
|
|
|
|
rs = n ? n + 4 : 3;
|
|
R[rs] = add3(BIT10(IR) ? R[rs] : 0, SEXT8(IR), 0);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
int
|
|
ins_bop(int IR, int addr)
|
|
{
|
|
|
|
int rd, n, reason = 0;
|
|
|
|
regSTL = (regSTL & 0377) | regSTH; /* Hack for bskp */
|
|
rd = IR & 7;
|
|
n = (IR >> 3) & 017;
|
|
switch ((IR >> 7) & 017) {
|
|
case 000: /* BSET zero/one */
|
|
R[rd] &= ~(1 << n);
|
|
break;
|
|
|
|
case 001:
|
|
R[rd] |= (1 << n);
|
|
break;
|
|
|
|
case 002: /* BSET BCM bit = ~bit */
|
|
R[rd] ^= (1 << n);
|
|
break;
|
|
|
|
case 003: /* BSET BAC bit = K */
|
|
R[rd] &= ~(1 << n);
|
|
if (regSTL & STS_K)
|
|
R[rd] |= (1 << n);
|
|
break;
|
|
|
|
case 004: /* BSKP zero/one */
|
|
case 005:
|
|
if (((R[rd] >> n) & 1) == BIT7(IR))
|
|
regP++;
|
|
break;
|
|
|
|
case 006: /* BSKP BCM K == ~bit */
|
|
if (((regSTL & STS_K) != 0) ^ (((R[rd] >> n) & 1) != 0))
|
|
regP++;
|
|
break;
|
|
|
|
case 010: /* BSTA store ~K and set K */
|
|
R[rd] &= ~(1 << n);
|
|
if ((regSTL & STS_K) == 0)
|
|
R[rd] |= (1 << n);
|
|
regSTL |= STS_K;
|
|
break;
|
|
|
|
case 011: /* BSTA store K and clear K */
|
|
R[rd] &= ~(1 << n);
|
|
if (regSTL & STS_K)
|
|
R[rd] |= (1 << n);
|
|
regSTL &= ~STS_K;
|
|
break;
|
|
|
|
case 012: /* BLDA load ~K */
|
|
regSTL &= ~STS_K;
|
|
if (((R[rd] >> n) & 1) == 0)
|
|
regSTL |= STS_K;
|
|
break;
|
|
|
|
case 013: /* BLDA load K */
|
|
regSTL &= ~STS_K;
|
|
if ((R[rd] >> n) & 1)
|
|
regSTL |= STS_K;
|
|
break;
|
|
|
|
case 014: /* BANC K = ~bit & K */
|
|
if (regSTL & STS_K) {
|
|
if ((R[rd] >> n) & 1)
|
|
regSTL &= ~STS_K;
|
|
}
|
|
break;
|
|
|
|
case 015: /* BAND K = bit & K */
|
|
if (regSTL & STS_K) {
|
|
if (((R[rd] >> n) & 1) == 0)
|
|
regSTL &= ~STS_K;
|
|
}
|
|
break;
|
|
|
|
case 016: /* BORC K = ~bit | K */
|
|
if ((regSTL & STS_K) == 0) {
|
|
if (((R[rd] >> n) & 1) == 0)
|
|
regSTL |= STS_K;
|
|
}
|
|
break;
|
|
|
|
case 017: /* BORA K = bit | K */
|
|
if ((regSTL & STS_K) == 0) {
|
|
if ((R[rd] >> n) & 1)
|
|
regSTL |= STS_K;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
reason = STOP_UNHINS;
|
|
break;
|
|
}
|
|
regSTL &= 0377;
|
|
return reason;
|
|
}
|
|
|
|
/*
|
|
* 48-bit floating point. T has the sign and exponent, A has the most
|
|
* significant bits and D has the least.
|
|
* Exponent is biased 16384, mantissa is 0.5 <= X < 1.0.
|
|
*/
|
|
struct fp {
|
|
int s;
|
|
int e;
|
|
t_uint64 m;
|
|
};
|
|
|
|
static void
|
|
mkfp48(struct fp *fp, uint16 w1, uint16 w2, uint16 w3)
|
|
{
|
|
fp->s = BIT15(w1);
|
|
fp->e = (w1 & 077777) - 16384;
|
|
fp->m = ((t_uint64)w2 << 16) + (t_uint64)w3;
|
|
}
|
|
|
|
/*
|
|
* Description of DNZ instruction from the NORD-10 manual:
|
|
*
|
|
* Converts the floating number in the floating accumulator to a
|
|
* single precision fixed point number in the A register, using the
|
|
* scaling of the DNZ instruction as a scaling factor.
|
|
*
|
|
* When converting to integers, the scaling factor should be —16, a
|
|
* greater scaling factor will cause the fixed point number to be
|
|
* greater. After this instruction the contents of the T and D registers
|
|
* will all be zeros.
|
|
*
|
|
* If the conversion causes underflow, the T, A and D registers will all
|
|
* be set to zero. If the conversion causes overflow, the error
|
|
* indicator 2 is set to one. Overflow occurs if the resulting integer
|
|
* in absolute value is greater than 32767.
|
|
* The conversion will truncate and negative numbers are converted to
|
|
* positive numbers before conversion. The result will again be
|
|
* converted to a negative number.
|
|
*/
|
|
void
|
|
ins_dnz(int ins)
|
|
{
|
|
int32 val = 0; /* XXX remove warnings */
|
|
int sh;
|
|
|
|
sh = (regT & 077777) - 16384 + SEXT8(ins);
|
|
if (sh < 0) {
|
|
val = regA;
|
|
val >>= -sh;
|
|
} else {
|
|
|
|
}
|
|
#ifdef notyet
|
|
if (val > 32767)
|
|
z = 1;
|
|
#endif
|
|
if (regT & 0100000)
|
|
val = -val;
|
|
regT = regD = 0;
|
|
regA = (uint16)val;
|
|
}
|
|
|
|
/*
|
|
* Description of DNZ instruction from the NORD-10 manual:
|
|
*
|
|
* Converts the number in the A register to a standard form floating
|
|
* number in the floating accumulator, using the scaling of the NLZ
|
|
* instruction as a scaling factor. For integers, the scaling factor
|
|
* should be +16, a larger scaling factor will result in a higher floating,
|
|
* point number. Because of the single precision fixed point number,
|
|
* the D register will be cleared.
|
|
*/
|
|
void
|
|
ins_nlz(int ins)
|
|
{
|
|
int sh, s;
|
|
int val;
|
|
|
|
s = 0;
|
|
regD = 0;
|
|
if (regA == 0) { /* zero, special case */
|
|
regT = 0;
|
|
return;
|
|
}
|
|
|
|
val = (int)(int16)regA;
|
|
sh = 16384 + SEXT8(ins);
|
|
if (val < 0)
|
|
val = -val, s = 0100000;
|
|
if (val > 32767)
|
|
val >>= 1, sh++;
|
|
while ((val & 0100000) == 0) {
|
|
val <<= 1;
|
|
sh--;
|
|
}
|
|
regT = sh + s;
|
|
regA = val;
|
|
}
|
|
|
|
/*
|
|
* Multiplication of two 48-bit floating point numbers.
|
|
*
|
|
* The contents of the floating accumulator are multiplied with the
|
|
* number of the effective floating word locations with the result in
|
|
* the floating accumulator. The previous setting of the carry and overflow
|
|
* indicators are lost.
|
|
* Affected: (T), (A), (D), O, Q, TG
|
|
*/
|
|
int
|
|
ins_fmu(int IR, int addr)
|
|
{
|
|
struct fp f1, f2;
|
|
int s3, e3, pt = SELPT2(IR);
|
|
t_uint64 m3;
|
|
|
|
/* Fetch from memory */
|
|
mkfp48(&f1, rdmem(addr, pt), rdmem(addr+1, pt), rdmem(addr+2, pt));
|
|
|
|
/* From registers */
|
|
mkfp48(&f2, regT, regA, regD);
|
|
|
|
/* calc */
|
|
m3 = f1.m * f2.m;
|
|
e3 = f1.e + f2.e;
|
|
s3 = f1.s ^ f2.s;
|
|
|
|
/* normalize (if needed) */
|
|
if ((m3 & (1ULL << 63)) == 0) {
|
|
m3 <<= 1;
|
|
e3--;
|
|
}
|
|
|
|
/* restore regs */
|
|
regA = (uint16)(m3 >> 48);
|
|
regD = (uint16)(m3 >> 32);
|
|
regT = (e3 + 16384) | (s3 << 15);
|
|
if (m3 == 0 || e3 < -16383)
|
|
regT = regA = regD = 0;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/*
|
|
* Division of two 48-bit floating point numbers.
|
|
*
|
|
* The contents of the floating accumulator are divided by the number
|
|
* in the effective floating word locations. Result in floating
|
|
* accumulator. If division by zero is attempted, the error indicator 2
|
|
* is set to one. The error indicator 2 may be sensed by a BSKP instruc-
|
|
* tion (see BOP). The previous setting of the carry and overflow
|
|
* indicators are lost.
|
|
* Affected: (T), (A), (D), Z, C, O, Q, TG
|
|
*/
|
|
int
|
|
ins_fdv(int IR, int addr)
|
|
{
|
|
struct fp f1, f2;
|
|
int s3, e3, pt;
|
|
t_uint64 m3;
|
|
|
|
/* Fetch from memory */
|
|
pt = SELPT2(IR);
|
|
mkfp48(&f1, rdmem(addr, pt), rdmem(addr+1, pt), rdmem(addr+2, pt));
|
|
|
|
/* From registers */
|
|
mkfp48(&f2, regT, regA, regD);
|
|
f2.m <<= 32;
|
|
|
|
/* Initial sanity */
|
|
if (f1.m == 0) {
|
|
regSTL |= STS_Z;
|
|
regT |= 077777;
|
|
regA = regD = 0177777;
|
|
intrpt14(IIE_V, PM_CPU);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* calc */
|
|
s3 = f1.s ^ f2.s;
|
|
e3 = f2.e - f1.e;
|
|
m3 = f2.m / f1.m;
|
|
if (f2.m%f1.m) /* "guard" bit */
|
|
m3++;
|
|
|
|
/* normalize (if needed) */
|
|
if (m3 >= (1ULL << 32)) {
|
|
m3 >>= 1;
|
|
e3++;
|
|
}
|
|
|
|
/* restore regs */
|
|
regA = (uint16)(m3 >> 16);
|
|
regD = (uint16)m3;
|
|
regT = (e3 + 16384) | (s3 << 15);
|
|
if (f2.m == 0 || e3 < -16383)
|
|
regT = regA = regD = 0;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/*
|
|
* Add two 48-bit numbers.
|
|
*
|
|
* The contents of the effective location and the two following locations
|
|
* are added to the floating accumulator with the result in the floating
|
|
* accumulator. The previous setting of the carry and overflow indicators
|
|
* are lost.
|
|
* Affected: (T), (A), (D), C, O, Q, TG
|
|
*/
|
|
static void
|
|
add48(struct fp *f1, struct fp *f2)
|
|
{
|
|
struct fp *ft;
|
|
t_uint64 m3;
|
|
int scale, gbit;
|
|
|
|
/* Ensure f1 is larger */
|
|
if (f2->e > f1->e)
|
|
ft = f1, f1 = f2, f2 = ft;
|
|
|
|
if ((scale = f1->e - f2->e) > 31) {
|
|
m3 = f1->m;
|
|
goto done;
|
|
}
|
|
|
|
/* get shifted out guard bit */
|
|
gbit = scale ? (((1LL << scale)-1) & f2->m) != 0 : 0;
|
|
f2->m >>= scale;
|
|
m3 = (f1->m + f2->m) | gbit;
|
|
if (m3 > 0xffffffffLL) {
|
|
m3 >>= 1;
|
|
f1->e++;
|
|
}
|
|
|
|
done: regT = (f1->e + 16384) | (f1->s << 15);
|
|
regA = (uint16)(m3 >> 16);
|
|
regD = (uint16)m3;
|
|
}
|
|
|
|
/*
|
|
* Subtract two 48-bit numbers.
|
|
* The numbers are of different sign.
|
|
*
|
|
* The contents of the effective location and the two following locations
|
|
* are subtracted from the floating accumulator with the result
|
|
* in the floating accumulator. The previous setting of the carry and
|
|
* overflow indicators are lost.
|
|
* Affected: (T), (A), (D), C, O, Q, TG
|
|
*/
|
|
static void
|
|
sub48(struct fp *f1, struct fp *f2, int addr)
|
|
{
|
|
struct fp *ft;
|
|
t_uint64 m3;
|
|
int scale, gbit;
|
|
|
|
/* Ensure f1 is bigger */
|
|
if (f2->e > f1->e)
|
|
ft = f1, f1 = f2, f2 = ft;
|
|
|
|
if ((scale = f1->e - f2->e) > 31) {
|
|
m3 = f1->m;
|
|
goto done;
|
|
}
|
|
|
|
/* get shifted out sticky bit */
|
|
gbit = scale ? (((1LL << scale)-1) & f2->m) != 0 : 0;
|
|
f2->m >>= scale;
|
|
f2->e = f1->e;
|
|
/* check for swap of mantissa */
|
|
if (f2->m > f1->m)
|
|
ft = f1, f1 = f2, f2 = ft;
|
|
m3 = (f1->m - f2->m) | gbit;
|
|
|
|
if (m3 == 0) {
|
|
regT = regA = regD = 0;
|
|
return;
|
|
}
|
|
while ((m3 & 0x80000000LL) == 0){
|
|
m3 <<= 1;
|
|
f1->e--;
|
|
}
|
|
|
|
done: regT = (f1->e + 16384) | (f1->s << 15);
|
|
regA = (uint16)(m3 >> 16);
|
|
regD = (uint16)m3;
|
|
}
|
|
|
|
int
|
|
ins_fad(int IR, int addr)
|
|
{
|
|
struct fp f1, f2;
|
|
int pt = SELPT2(IR);
|
|
|
|
mkfp48(&f1, rdmem(addr, pt), rdmem(addr+1, pt), rdmem(addr+2, pt));
|
|
mkfp48(&f2, regT, regA, regD);
|
|
|
|
if (f1.s ^ f2.s)
|
|
sub48(&f1, &f2, addr);
|
|
else
|
|
add48(&f1, &f2);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
int
|
|
ins_fsb(int IR, int addr)
|
|
{
|
|
struct fp f1, f2;
|
|
int pt = SELPT2(IR);
|
|
|
|
mkfp48(&f1, rdmem(addr, pt), rdmem(addr+1, pt), rdmem(addr+2, pt));
|
|
mkfp48(&f2, regT, regA, regD);
|
|
|
|
f1.s ^= 1; /* swap sign of right op */
|
|
|
|
if (f1.s ^ f2.s)
|
|
sub48(&f1, &f2, addr);
|
|
else
|
|
add48(&f1, &f2);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/*
|
|
* Add three numbers, setting overflow and carry as needed.
|
|
*/
|
|
uint16
|
|
add3(uint16 a, uint16 d, uint16 c)
|
|
{
|
|
int32 res;
|
|
|
|
CLRC();
|
|
CLRQ();
|
|
res = a + d + c;;
|
|
|
|
if (res > 0177777)
|
|
SETC();
|
|
if (((a ^ d) & 0100000) == 0) {
|
|
/* sign bits equal */
|
|
if ((a ^ res) & 0100000)
|
|
SETQ(), SETO(); /* result sign differ */
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Multiply A with memory. Set flags.
|
|
*/
|
|
int
|
|
ins_mpy(int IR, int off)
|
|
{
|
|
int res = (int)(int16)regA * (int)(int16)rdmem(off, SELPT2(IR));
|
|
regA = res;
|
|
regSTL &= ~STS_Q;
|
|
if (res > 32767)
|
|
regSTL |= (STS_Q|STS_O);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/*
|
|
* Jump or jump and link.
|
|
*/
|
|
int
|
|
ins_jmpl(int IR, int off)
|
|
{
|
|
if (BIT12(IR))
|
|
regL = regP;
|
|
regP = off;
|
|
return SCPE_OK;
|
|
}
|
|
/*
|
|
* Conditional jump.
|
|
* off is unused here.
|
|
*/
|
|
int
|
|
ins_cjp(int IR, int off)
|
|
{
|
|
char cjpmsk[] = { 01, 01, 02, 02, 05, 05, 02, 01 };
|
|
int n, i;
|
|
uint16 s;
|
|
|
|
n = (IR & ND_CJPMSK) >> ND_CJPSH;
|
|
if (cjpmsk[n] & 04)
|
|
regX++;
|
|
s = n & 04 ? regX : regA;
|
|
i = (SEXT8(IR)-1);
|
|
if (cjpmsk[n] & 01) { /* pos/neg */
|
|
if (BIT8(IR) == BIT15(s))
|
|
regP += i;
|
|
} else if (cjpmsk[n] & 02) { /* zero/filled */
|
|
if (BIT8(IR) == !!s)
|
|
regP += i;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
int
|
|
ins_skp(int IR, int off)
|
|
{
|
|
int c_o, shc, n, rv = SCPE_OK;
|
|
uint16 s, d;
|
|
|
|
if (IR & 0300) { /* extended instructions */
|
|
rv = ins_skip_ext(IR);
|
|
} else {
|
|
s = ~(IR & 070 ? R[(IR & 070) >> 3] : 0);
|
|
d = IR & 07 ? R[IR & 07] : 0;
|
|
shc = d + s + 1;
|
|
c_o = (!BIT15(s ^ d) && BIT15(d ^ shc));
|
|
|
|
switch ((IR >> 8) & 03) {
|
|
case 0: /* eql */ n = ((shc & 0177777) == 0); break;
|
|
case 1: /* geq */ n = !BIT15(shc); break;
|
|
case 2: /* gre */ n = ((BIT15(shc) ^ c_o) == 0); break;
|
|
case 3: /* mgre */ n = (shc > 0177777); break;
|
|
}
|
|
if (BIT10(IR)) n = !n;
|
|
if (n) regP++;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
int
|
|
ins_rop(int IR, int off)
|
|
{
|
|
int n, rs, rd;
|
|
uint16 s, d;
|
|
|
|
rs = (IR & 070) >> 3;
|
|
rd = IR & 07;
|
|
s = rs ? R[rs] : 0;
|
|
d = rd ? R[rd] : 0;
|
|
if (BIT6(IR)) d = 0; /* clear dest */
|
|
if (BIT7(IR)) s = ~s; /* complement src */
|
|
if (BIT10(IR)) { /* add source to destination */
|
|
n = 0;
|
|
if (BIT8(IR)) n = 1;
|
|
else if (BIT9(IR)) n = BIT6(regSTL);
|
|
d = add3(s, d, n);
|
|
} else { /* logical instructons */
|
|
if (BIT8(IR)) {
|
|
if (BIT9(IR)) d |= s;
|
|
else d &= s;
|
|
} else {
|
|
if (BIT9(IR) == 0) { /* swap */
|
|
if (rs) R[rs] = d;
|
|
d = s;
|
|
} else
|
|
d ^= s;
|
|
}
|
|
}
|
|
if (rd) R[rd] = d;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/*
|
|
* Look for some device at dev.
|
|
*/
|
|
static int
|
|
iox_check(int dev)
|
|
{
|
|
|
|
mm_privcheck();
|
|
|
|
if ((dev & 0177774) == 010)
|
|
return iox_clk(dev);
|
|
if ((dev & 0177770) == 0300)
|
|
return iox_tty(dev);
|
|
if ((dev & 0177770) == 01560)
|
|
return iox_floppy(dev);
|
|
|
|
intrpt14(IIE_IOX, PM_CPU);
|
|
return SCPE_OK;
|
|
|
|
}
|
|
|
|
static int
|
|
getoff(int ir)
|
|
{
|
|
int ea;
|
|
|
|
ea = BIT8(ir) ? regB : oregP;
|
|
if (BIT10(ir) & !BIT9(ir) & !BIT8(ir))
|
|
ea = 0;
|
|
ea += SEXT8(ir);
|
|
if (BIT9(ir))
|
|
ea = rdmem(ea & 0177777, BIT8(ir) ? M_APT : M_PT);
|
|
if (BIT10(ir))
|
|
ea += regX;
|
|
return ea & 0177777;
|
|
}
|
|
|
|
/*
|
|
* bit-clear value in internal reg.
|
|
*/
|
|
static int
|
|
nd_mcl(int reg)
|
|
{
|
|
int reason = SCPE_OK;
|
|
|
|
mm_privcheck();
|
|
|
|
switch (reg) {
|
|
case IR_STS:
|
|
regSTL &= ~(regA & 0377);
|
|
break;
|
|
case 006: /* PID */
|
|
pid &= ~regA;
|
|
break;
|
|
|
|
case 007: /* PIE */
|
|
pie &= ~regA;
|
|
break;
|
|
|
|
default:
|
|
reason = STOP_UNHINS;
|
|
}
|
|
return reason;
|
|
}
|
|
|
|
/*
|
|
* Or-set value of internal reg.
|
|
*/
|
|
static int
|
|
nd_mst(int reg)
|
|
{
|
|
int reason = SCPE_OK;
|
|
|
|
mm_privcheck();
|
|
|
|
switch (reg) {
|
|
case IR_STS:
|
|
regSTL |= (regA & 0377);
|
|
break;
|
|
case IR_PID: /* PID */
|
|
pid |= regA;
|
|
break;
|
|
case IR_PIE: /* PIE */
|
|
pie |= regA;
|
|
break;
|
|
|
|
default:
|
|
reason = STOP_UNHINS;
|
|
}
|
|
return reason;
|
|
}
|
|
|
|
/*
|
|
* Set value of internal reg.
|
|
*/
|
|
static int
|
|
nd_trr(int reg)
|
|
{
|
|
int reason = SCPE_OK;
|
|
|
|
mm_privcheck();
|
|
|
|
switch (reg) {
|
|
case IR_STS:
|
|
regSTL = regA & 0377;
|
|
break;
|
|
case IR_LMP: /* lamp reg */
|
|
lmp = regA;
|
|
break;
|
|
case IR_PCR: /* PCR (paging control register) */
|
|
mm_wrpcr();
|
|
break;
|
|
case 005: /* IIE */
|
|
iie = regA & 03776;
|
|
break;
|
|
case 006: /* PID */
|
|
pid = regA;
|
|
break;
|
|
case 007: /* PIE */
|
|
pie = regA;
|
|
break;
|
|
case IR_CCL:
|
|
break;
|
|
case IR_LCIL:
|
|
break;
|
|
case IR_UCIL:
|
|
break;
|
|
case IR_ECCR:
|
|
eccr = regA;
|
|
break;
|
|
default:
|
|
reason = STOP_UNHINS;
|
|
}
|
|
return reason;
|
|
}
|
|
|
|
/*
|
|
* Read value of internal reg.
|
|
*/
|
|
static int
|
|
nd_tra(int reg)
|
|
{
|
|
int reason = SCPE_OK;
|
|
|
|
mm_privcheck();
|
|
|
|
switch (reg) {
|
|
case IR_PANS:
|
|
regA = 0;
|
|
break;
|
|
case IR_STS: /* STS */
|
|
regA = regSTL | regSTH | (curlvl << 8);
|
|
break;
|
|
case IRR_OPR:
|
|
regA = opr;
|
|
break;
|
|
case IRR_PVL:
|
|
regA = (pvl << 3) | 0153602;
|
|
break;
|
|
case IR_IIC: /* read IIC. Clears IIC and IID */
|
|
regA = iic;
|
|
iic = iid = 0;
|
|
pid &= ~(1 << 14);
|
|
break;
|
|
case IR_PID: /* 6 */
|
|
regA = pid;
|
|
break;
|
|
case IR_PIE:
|
|
regA = pie;
|
|
break;
|
|
case IR_CSR: /* CCR */
|
|
regA = 04; /* cache disabled */
|
|
break;
|
|
case 012: /* ALD */
|
|
regA = ald;
|
|
break;
|
|
default:
|
|
reason = mm_tra(reg);
|
|
}
|
|
return reason;
|
|
}
|
|
|
|
int
|
|
highest_level()
|
|
{
|
|
int i, d = pid & pie;
|
|
|
|
for (i = 15; i >= 0; i--)
|
|
if (d & (1 << i))
|
|
return i;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Find last bit set.
|
|
*/
|
|
int
|
|
fls(int msk)
|
|
{
|
|
int i, j;
|
|
|
|
if (msk == 0)
|
|
return -1;
|
|
for (i = 15, j = 0100000; i >= 0; i--, j >>= 1)
|
|
if (j & msk)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Post an internal interrupt for the given source.
|
|
*/
|
|
void
|
|
intrpt14(int src, int where)
|
|
{
|
|
iid |= src; /* set detect flipflop */
|
|
for (iic = 0; (src & 1) == 0; iic++, src >>= 1)
|
|
;
|
|
|
|
if (iid & iie) { /* if internal int enabled, post priority int */
|
|
pid |= (1 << 14);
|
|
/* If we can interrupt, jump directly back */
|
|
if (ISION() && curlvl < 14 && (pie & (1 << 14)) &&
|
|
(where == PM_CPU))
|
|
longjmp(env, 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
extint(int lvl, struct intr *ip)
|
|
{
|
|
pid |= (1 << lvl);
|
|
lvl -= 10;
|
|
if (ip->inuse)
|
|
return;
|
|
ip->inuse = 1;
|
|
ip->next = ilnk[lvl];
|
|
ilnk[lvl] = ip;
|
|
}
|
|
|
|
/*
|
|
* Fetch ident from interrupting device.
|
|
* If no device interrupting, post IOX error.
|
|
*/
|
|
void
|
|
identrm(int id)
|
|
{
|
|
struct intr *ip = ilnk[id-10];
|
|
|
|
if (ip == 0) {
|
|
intrpt14(IIE_IOX, PM_CPU);
|
|
regA = 0;
|
|
} else {
|
|
regA = ip->ident;
|
|
ilnk[id-10] = ip->next;
|
|
ip->next = NULL;
|
|
ip->inuse = 0;
|
|
if (ilnk[id-10] == 0)
|
|
pid &= ~(1 << id);
|
|
}
|
|
}
|
|
|
|
static void
|
|
hist_save(int ir)
|
|
{
|
|
Hist_entry *hist_ptr;
|
|
|
|
if (hist == NULL || hist_cnt == 0)
|
|
return;
|
|
if (++hist_p == hist_cnt)
|
|
hist_p = 0;
|
|
hist_ptr = &hist[hist_p];
|
|
|
|
hist_ptr->ir = ir;
|
|
hist_ptr->sts = regSTL | regSTH | (curlvl << 8);
|
|
hist_ptr->d = regD;
|
|
hist_ptr->p = oregP;
|
|
hist_ptr->b = regB;
|
|
hist_ptr->l = regL;
|
|
hist_ptr->a = regA;
|
|
hist_ptr->t = regT;
|
|
hist_ptr->x = regX;
|
|
}
|
|
|
|
t_stat
|
|
hist_set(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
int32 i, lnt;
|
|
t_stat r;
|
|
|
|
if (cptr == NULL) {
|
|
for (i = 0 ; i < hist_cnt ; i++)
|
|
hist[i].ir = HIST_IR_INVALID;
|
|
hist_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;
|
|
|
|
hist_p = 0;
|
|
if (hist_cnt) {
|
|
free(hist);
|
|
hist_cnt = 0;
|
|
hist = NULL;
|
|
}
|
|
|
|
if (lnt) {
|
|
hist = (Hist_entry *) calloc(lnt, sizeof(Hist_entry));
|
|
if (hist == NULL)
|
|
return SCPE_MEM;
|
|
hist_cnt = lnt;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static void
|
|
hist_fprintf(FILE *fp, int itemNum, Hist_entry *hptr)
|
|
{
|
|
t_value val;
|
|
|
|
if (hptr == NULL)
|
|
return;
|
|
if (itemNum == 0)
|
|
fprintf(fp, "\n\n");
|
|
fprintf(fp, "%06o: IR=%06o STS=%06o D=%06o B=%06o "
|
|
"L=%06o A=%06o T=%06o X=%06o ",
|
|
hptr->p & 0177777, hptr->ir & 0177777, hptr->sts & 0177777,
|
|
hptr->d & 0177777, hptr->b & 0177777, hptr->l & 0177777,
|
|
hptr->a & 0177777, hptr->t & 0177777, hptr->x & 0177777);
|
|
val = hptr->ir;
|
|
fprint_sym(fp, hptr->p, &val, 0, SWMASK ('M'));
|
|
fprintf(fp, "\n");
|
|
}
|
|
|
|
static void
|
|
ioxprint(FILE *fp, Hist_entry *hptr, int ioaddr)
|
|
{
|
|
fprintf(fp, "%06o: iox(%06o) %s A=%06o\n",
|
|
hptr->p, ioaddr & 0177777, ioaddr & 1 ? "out" : "in ",
|
|
hptr->a & 0177777);
|
|
}
|
|
|
|
t_stat
|
|
hist_show(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
int32 k, di, lnt;
|
|
CONST char *cptr = (CONST char *) desc;
|
|
t_stat r;
|
|
Hist_entry *hptr;
|
|
|
|
if (hist_cnt == 0)
|
|
return SCPE_NOFNC;
|
|
|
|
if (cptr) { /* number of entries specified */
|
|
lnt = (int32)get_uint(cptr, 10, hist_cnt, &r);
|
|
if ((r != SCPE_OK) || (lnt == 0))
|
|
return SCPE_ARG;
|
|
} else
|
|
lnt = hist_cnt;
|
|
|
|
di = hist_p - lnt;
|
|
if (di < 0)
|
|
di = di + hist_cnt;
|
|
|
|
for (k = 0 ; k < lnt ; ++k) {
|
|
hptr = &hist[(++di) % hist_cnt];
|
|
if (sim_switches & SWMASK('I')) {
|
|
/* print iox instructions */
|
|
if ((hptr->ir & ND_MEMMSK) == ND_IOX)
|
|
ioxprint(st, hptr, hptr->ir & ~ND_MEMMSK);
|
|
if (hptr->ir == ND_MIS_IOXT)
|
|
ioxprint(st, hptr, hptr->t);
|
|
} else {
|
|
if (hptr->ir != HIST_IR_INVALID)
|
|
hist_fprintf(st, k, hptr);
|
|
}
|
|
}
|
|
return SCPE_OK;
|
|
}
|