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