| /* vax_cpu.c: VAX CPU | |
| Copyright (c) 1998-2012, 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 | |
| ROBERT M SUPNIK 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 Robert M Supnik shall not be | |
| used in advertising or otherwise to promote the sale, use or other dealings | |
| in this Software without prior written authorization from Robert M Supnik. | |
| cpu VAX central processor | |
| 20-Sep-11 MP Fixed idle conditions for various versions of Ultrix, | |
| Quasijarus-4.3BSD, NetBSD and OpenBSD. | |
| Note: Since NetBSD and OpenBSD are still actively | |
| developed operating systems, new versions of | |
| these OSes are moving targets with regard to | |
| providing idle detection. At this time, recent versions | |
| of OpenBSD have veered from the traditional OS idle | |
| approach taken in the other BSD derived OSes. | |
| Determining a reasonable idle detection pattern does | |
| not seem possible for these versions. | |
| 13-Sep-11 RMS Fixed XFC, BPT to clear PSL<tp> before exception | |
| (Camiel Vanderhoeven) | |
| 23-Mar-11 RMS Revised for new idle design (Mark Pizzolato) | |
| 05-Jan-11 MP Added Asynch I/O support | |
| 24-Apr-10 RMS Added OLDVMS idle timer option | |
| Fixed bug in SET CPU IDLE | |
| 21-May-08 RMS Removed inline support | |
| 28-May-08 RMS Inlined instruction prefetch, physical memory routines | |
| 13-Aug-07 RMS Fixed bug in read access g-format indexed specifiers | |
| 28-Apr-07 RMS Removed clock initialization | |
| 29-Oct-06 RMS Added idle support | |
| 22-May-06 RMS Fixed format error in CPU history (Peter Schorn) | |
| 10-May-06 RMS Added -kesu switches for virtual addressing modes | |
| Fixed bugs in examine virtual | |
| Rewrote history function for greater usability | |
| Fixed bug in reported VA on faulting cross-page write | |
| 02-May-06 RMS Fixed fault cleanup to clear PSL<tp> | |
| Fixed ADAWI r-mode to preserve dst<31:16> | |
| Fixed ACBD/G to test correct operand | |
| Fixed access checking on modify-class specifiers | |
| Fixed branch displacements in history buffer | |
| (Tim Stark) | |
| 17-Nov-05 RMS Fixed CVTfi with integer overflow to trap if PSW<iv> set | |
| 13-Nov-05 RMS Fixed breakpoint test with 64b addresses | |
| 25-Oct-05 RMS Removed cpu_extmem | |
| 22-Sep-05 RMS Fixed declarations (Sterling Garwood) | |
| 16-Aug-05 RMS Fixed C++ declaration and cast problems | |
| 13-Jan-05 RMS Fixed initial state of cpu_extmem | |
| 06-Nov-04 RMS Added =n to SHOW HISTORY | |
| 30-Sep-04 RMS Added octaword specifier decodes and instructions | |
| Moved model-specific routines to system module | |
| 02-Sep-04 RMS Fixed bug in EMODD/G, second word of quad dst not probed | |
| 28-Jun-04 RMS Fixed bug in DIVBx, DIVWx (Peter Trimmel) | |
| 18-Apr-04 RMS Added octaword macros | |
| 25-Jan-04 RMS Removed local debug logging support | |
| RMS,MP Added extended physical memory support | |
| 31-Dec-03 RMS Fixed bug in set_cpu_hist | |
| 21-Dec-03 RMS Added autoconfiguration controls | |
| 29-Oct-03 RMS Fixed WriteB declaration (Mark Pizzolato) | |
| 23-Sep-03 RMS Revised instruction history for dynamic sizing | |
| 17-May-03 RMS Fixed operand order in EMODx | |
| 23-Apr-03 RMS Revised for 32b/64b t_addr | |
| 05-Jan-02 RMS Added memory size restore support | |
| 25-Dec-02 RMS Added instruction history (Mark Pizzolato) | |
| 29-Sep-02 RMS Revised to build dib_tab dynamically | |
| 14-Jul-02 RMS Added halt to console, infinite loop detection (Mark Pizzolato) | |
| 02-May-02 RMS Fixed bug in indexed autoincrement register logging | |
| 30-Apr-02 RMS Added TODR powerup routine | |
| 18-Apr-02 RMS Cleanup ambiguous signed left shifts | |
| 15-Apr-02 RMS Fixed bug in CASEL condition codes | |
| The register state for the VAX is: | |
| R[0:15] general registers | |
| PSL<31:0> processor status longword | |
| TP<30> trace pending | |
| FPD<27> first part done | |
| IS<26> interrupt stack | |
| CM<25:24> current mode | |
| PM<23:22> previous mode | |
| IPL<20:16> interrupt priority level | |
| PSW<15:0> non-privileged processor status word | |
| DV<7> decimal overflow trap enable | |
| FU<6> floating underflow fault enable | |
| IV<5> integer overflow trap enable | |
| T<4> trace trap enable | |
| CC<3:0> condition codes | |
| SCBB system control block base | |
| PCBB process control block base | |
| SBR system page table base | |
| SLR system page table length | |
| P0BR process region 0 page table base | |
| P0LR process region 0 page table length | |
| P1BR process region 1 page table base | |
| P1LR process region 1 page table length | |
| SIRR/SISR software interrupt request/summary register | |
| ASTLVL AST level register | |
| The VAX has a variable length instruction format with up to six operands: | |
| opcode byte | |
| operand 1 specifier | |
| : | |
| operand n specifier | |
| Each operand specifier is a byte consisting of an addressing mode, a | |
| register, and possibly 1-8 bytes of extension: | |
| number name extension mnemonic operation | |
| 0-3 short literal - #n op <- specifier | |
| 4 index - [Rn] index by Rn | |
| 5 register - Rn op <- Rn | |
| 6 register def - (Rn) op <- M[Rn] | |
| 7 autodecrement - -(Rn) Rn <- Rn - length | |
| op <- M[Rn] | |
| 8 autoincrement - (Rn)+ op <- M[Rn] | |
| Rn <- Rn + length | |
| 9 auto deferred - @(Rn)+ op <- M[M[Rn]] | |
| Rn <- Rn + 4 | |
| A byte displ byte d d(Rn) op <- M[Rn + sxt.d] | |
| B byte displ def byte d @d(Rn) op <- M[M[Rn + sxt.d]] | |
| C word displ word d d(Rn) op <- M[Rn + sxt.d] | |
| D word displ def word d @d(Rn) op <- M[M[Rn + sxt.d]] | |
| E long displ long d d(Rn) op <- M[Rn + d] | |
| F long displ def long d @d(Rn) op <- M[M[Rn + d]] | |
| When the general register is the PC, certain modes are forbidden, and | |
| others have special interpretations: | |
| 4F index fault | |
| 5F register fault | |
| 6F register def fault | |
| 7F autodecrement fault | |
| 8F immediate 1-8B #imm op <- imm | |
| 9 absolute 4B @#imm op <- M[imm] | |
| A byte relative byte d d(Rn) op <- M[PC + sxt.d] | |
| B byte rel def byte d @d(Rn) op <- M[M[PC + sxt.d]] | |
| C word relative word d d(Rn) op <- M[PC + sxt.d] | |
| D word rel def word d @d(Rn) op <- M[M[PC + sxt.d]] | |
| E long relative long d d(Rn) op <- M[PC + d] | |
| F long rel def long d @d(Rn) op <- M[M[PC + d]] | |
| This routine is the instruction decode routine for the VAX. It | |
| is called from the simulator control program to execute instructions | |
| in simulated memory, starting at the simulated PC. It runs until an | |
| enabled exception is encountered. | |
| General notes: | |
| 1. Traps and interrupts. Variable trpirq microencodes the outstanding | |
| trap request (if any) and the level of the highest outstanding | |
| interrupt (if any). | |
| 2. Interrupt requests are maintained in the int_req array, one word per | |
| interrupt level, one bit per device. | |
| 3. Adding I/O devices. These modules must be modified: | |
| vax_defs.h add device address and interrupt definitions | |
| vax_sys.c add sim_devices table entry | |
| */ | |
| /* Definitions */ | |
| #include "vax_defs.h" | |
| #define UNIT_V_CONH (UNIT_V_UF + 0) /* halt to console */ | |
| #define UNIT_V_MSIZE (UNIT_V_UF + 1) /* dummy */ | |
| #define UNIT_CONH (1u << UNIT_V_CONH) | |
| #define UNIT_MSIZE (1u << UNIT_V_MSIZE) | |
| #define GET_CUR acc = ACC_MASK (PSL_GETCUR (PSL)) | |
| #define OPND_SIZE 16 | |
| #define INST_SIZE 52 | |
| #define op0 opnd[0] | |
| #define op1 opnd[1] | |
| #define op2 opnd[2] | |
| #define op3 opnd[3] | |
| #define op4 opnd[4] | |
| #define op5 opnd[5] | |
| #define op6 opnd[6] | |
| #define op7 opnd[7] | |
| #define op8 opnd[8] | |
| #define CHECK_FOR_PC if (rn == nPC) \ | |
| RSVD_ADDR_FAULT | |
| #define CHECK_FOR_SP if (rn >= nSP) \ | |
| RSVD_ADDR_FAULT | |
| #define CHECK_FOR_AP if (rn >= nAP) \ | |
| RSVD_ADDR_FAULT | |
| #define WRITE_B(r) if (spec > (GRN | nPC)) \ | |
| Write (va, r, L_BYTE, WA); \ | |
| else R[rn] = (R[rn] & ~BMASK) | ((r) & BMASK) | |
| #define WRITE_W(r) if (spec > (GRN | nPC)) \ | |
| Write (va, r, L_WORD, WA); \ | |
| else R[rn] = (R[rn] & ~WMASK) | ((r) & WMASK) | |
| #define WRITE_L(r) if (spec > (GRN | nPC)) \ | |
| Write (va, r, L_LONG, WA); \ | |
| else R[rn] = (r) | |
| #define WRITE_Q(arl,arh) if (spec > (GRN | nPC)) { \ | |
| if ((Test (va + 7, WA, &mstat) >= 0) || \ | |
| (Test (va, WA, &mstat) < 0)) \ | |
| Write (va, arl, L_LONG, WA); \ | |
| Write (va + 4, arh, L_LONG, WA); \ | |
| } \ | |
| else { \ | |
| if (rn >= nSP) \ | |
| RSVD_ADDR_FAULT; \ | |
| R[rn] = arl; \ | |
| R[rn + 1] = arh; \ | |
| } \ | |
| r = arl; \ | |
| rh = arh | |
| #define HIST_MIN 64 | |
| #define HIST_MAX 250000 | |
| typedef struct { | |
| double time; | |
| int32 iPC; | |
| int32 PSL; | |
| int32 opc; | |
| uint8 inst[INST_SIZE]; | |
| uint32 opnd[OPND_SIZE]; | |
| uint32 res[6]; | |
| } InstHistory; | |
| uint32 *M = NULL; /* memory */ | |
| int32 R[16]; /* registers */ | |
| int32 STK[5]; /* stack pointers */ | |
| int32 PSL; /* PSL */ | |
| int32 SCBB = 0; /* SCB base */ | |
| int32 PCBB = 0; /* PCB base */ | |
| int32 P0BR = 0; /* P0 mem mgt */ | |
| int32 P0LR = 0; | |
| int32 P1BR = 0; /* P1 mem mgt */ | |
| int32 P1LR = 0; | |
| int32 SBR = 0; /* S0 mem mgt */ | |
| int32 SLR = 0; | |
| int32 SISR; /* swre int req */ | |
| int32 ASTLVL; /* AST level */ | |
| int32 mapen; /* map enable */ | |
| int32 pme; /* perf mon enable */ | |
| int32 trpirq; /* trap/intr req */ | |
| int32 in_ie = 0; /* in exc, int */ | |
| int32 recq[6]; /* recovery queue */ | |
| int32 recqptr; /* recq pointer */ | |
| int32 hlt_pin = 0; /* HLT pin intr */ | |
| int32 mem_err = 0; | |
| int32 crd_err = 0; | |
| int32 p1 = 0, p2 = 0; /* fault parameters */ | |
| int32 fault_PC; /* fault PC */ | |
| int32 pcq_p = 0; /* PC queue ptr */ | |
| int32 badabo = 0; | |
| int32 cpu_astop = 0; | |
| int32 mchk_va, mchk_ref; /* mem ref param */ | |
| int32 ibufl, ibufh; /* prefetch buf */ | |
| int32 ibcnt, ppc; /* prefetch ctl */ | |
| uint32 cpu_idle_mask = VAX_IDLE_VMS; /* idle mask */ | |
| uint32 cpu_idle_type = 1; /* default VMS */ | |
| jmp_buf save_env; | |
| REG *pcq_r = NULL; /* PC queue reg ptr */ | |
| int32 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ | |
| InstHistory *hst = NULL; /* instruction history */ | |
| int32 hst_p = 0; /* history pointer */ | |
| int32 hst_lnt = 0; /* history length */ | |
| int32 hst_switches; /* history option switches */ | |
| FILE *hst_log; /* history log file */ | |
| int32 hst_log_p; /* history last log written pointer */ | |
| int32 step_out_nest_level = 0; /* step to call return - nest level */ | |
| const uint32 byte_mask[33] = { 0x00000000, | |
| 0x00000001, 0x00000003, 0x00000007, 0x0000000F, | |
| 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF, | |
| 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, | |
| 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, | |
| 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, | |
| 0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF, | |
| 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, | |
| 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF | |
| }; | |
| const uint32 byte_sign[33] = { 0x00000000, | |
| 0x00000001, 0x00000002, 0x00000004, 0x00000008, | |
| 0x00000010, 0x00000020, 0x00000040, 0x00000080, | |
| 0x00000100, 0x00000200, 0x00000400, 0x00000800, | |
| 0x00001000, 0x00002000, 0x00004000, 0x00008000, | |
| 0x00010000, 0x00020000, 0x00040000, 0x00080000, | |
| 0x00100000, 0x00200000, 0x00400000, 0x00800000, | |
| 0x01000000, 0x02000000, 0x04000000, 0x08000000, | |
| 0x10000000, 0x20000000, 0x40000000, 0x80000000 | |
| }; | |
| const uint32 align[4] = { | |
| 0xFFFFFFFF, 0x00FFFFFF, 0x0000FFFF, 0x000000FF | |
| }; | |
| /* External and forward references */ | |
| extern int32 sys_model; | |
| extern const char *opcode[]; | |
| t_stat cpu_reset (DEVICE *dptr); | |
| t_bool cpu_is_pc_a_subroutine_call (t_addr **ret_addrs); | |
| t_stat cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); | |
| t_stat cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); | |
| t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); | |
| t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); | |
| t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); | |
| t_stat cpu_show_virt (FILE *st, UNIT *uptr, int32 val, void *desc); | |
| t_stat cpu_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc); | |
| t_stat cpu_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc); | |
| const char *cpu_description (DEVICE *dptr); | |
| int32 cpu_get_vsw (int32 sw); | |
| static SIM_INLINE int32 get_istr (int32 lnt, int32 acc); | |
| int32 ReadOcta (int32 va, int32 *opnd, int32 j, int32 acc); | |
| t_bool cpu_show_opnd (FILE *st, InstHistory *h, int32 line); | |
| t_stat cpu_show_hist_records (FILE *st, t_bool do_header, int32 start, int32 count); | |
| t_stat cpu_idle_svc (UNIT *uptr); | |
| void cpu_idle (void); | |
| /* 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 (&cpu_idle_svc, UNIT_FIX|UNIT_BINK, INITMEMSIZE) | |
| }; | |
| const char *psl_modes[] = {"K", "E", "S", "U"}; | |
| BITFIELD psl_bits[] = { | |
| BIT(C), /* Carry */ | |
| BIT(V), /* Overflow */ | |
| BIT(Z), /* Zero */ | |
| BIT(N), /* Negative */ | |
| BIT(T), /* trace */ | |
| BIT(IV), /* Integer overflow */ | |
| BIT(FU), /* Floating underflow */ | |
| BIT(DV), /* Decimal overflow */ | |
| BITNCF(8), /* MBZ */ | |
| BITFFMT(IPL,5,%d), /* IPL */ | |
| BITNCF(1), /* MBZ */ | |
| BITFNAM(PRVMOD,2,psl_modes), /* Previous Access Mode */ | |
| BITFNAM(CURMOD,2,psl_modes), /* Current Access Mode */ | |
| BIT(IS), /* Interrupt Stack */ | |
| BIT(FPD), /* First Part Done */ | |
| BITNCF(2), /* MBZ */ | |
| BIT(TP), /* Trace Pending */ | |
| BIT(CM), /* Compatibility Mode */ | |
| ENDBITS | |
| }; | |
| REG cpu_reg[] = { | |
| { HRDATAD (PC, R[nPC], 32, "program counter") }, | |
| { HRDATAD (R0, R[0], 32, "General Purpose Register 0") }, | |
| { HRDATAD (R1, R[1], 32, "General Purpose Register 1") }, | |
| { HRDATAD (R2, R[2], 32, "General Purpose Register 2") }, | |
| { HRDATAD (R3, R[3], 32, "General Purpose Register 3") }, | |
| { HRDATAD (R4, R[4], 32, "General Purpose Register 4") }, | |
| { HRDATAD (R5, R[5], 32, "General Purpose Register 5") }, | |
| { HRDATAD (R6, R[6], 32, "General Purpose Register 6") }, | |
| { HRDATAD (R7, R[7], 32, "General Purpose Register 7") }, | |
| { HRDATAD (R8, R[8], 32, "General Purpose Register 8") }, | |
| { HRDATAD (R9, R[9], 32, "General Purpose Register 9") }, | |
| { HRDATAD (R10, R[10], 32, "General Purpose Register 10") }, | |
| { HRDATAD (R11, R[11], 32, "General Purpose Register 11") }, | |
| { HRDATAD (R12, R[12], 32, "General Purpose Register 12") }, | |
| { HRDATAD (R13, R[13], 32, "General Purpose Register 13") }, | |
| { HRDATAD (R14, R[14], 32, "General Purpose Register 14") }, | |
| { HRDATAD (AP, R[nAP], 32, "Alias for R12") }, | |
| { HRDATAD (FP, R[nFP], 32, "Alias for R13") }, | |
| { HRDATAD (SP, R[nSP], 32, "Alias for R14") }, | |
| { HRDATADF(PSL, PSL, 32, "processor status longword", psl_bits) }, | |
| { HRDATAD (CC, PSL, 4, "condition codes, PSL<3:0>") }, | |
| { HRDATAD (KSP, KSP, 32, "kernel stack pointer") }, | |
| { HRDATAD (ESP, ESP, 32, "executive stack pointer") }, | |
| { HRDATAD (SSP, SSP, 32, "supervisor stack pointer") }, | |
| { HRDATAD (USP, USP, 32, "user stack pointer") }, | |
| { HRDATAD (IS, IS, 32, "interrupt stack pointer") }, | |
| { HRDATAD (SCBB, SCBB, 32, "system control block base") }, | |
| { HRDATAD (PCBB, PCBB, 32, "process control block base") }, | |
| { HRDATAD (P0BR, P0BR, 32, "P0 base register") }, | |
| { HRDATAD (P0LR, P0LR, 22, "P0 length register") }, | |
| { HRDATAD (P1BR, P1BR, 32, "P1 base register") }, | |
| { HRDATAD (P1LR, P1LR, 22, "P1 length register") }, | |
| { HRDATAD (SBR, SBR, 32, "system base register") }, | |
| { HRDATAD (SLR, SLR, 22, "system length register") }, | |
| { HRDATAD (SISR, SISR, 16, "software interrupt summary register") }, | |
| { HRDATAD (ASTLVL, ASTLVL, 4, "AST level register") }, | |
| { FLDATAD (MAPEN, mapen, 0, "memory management enable") }, | |
| { FLDATAD (PME, pme, 0, "performance monitor enable") }, | |
| { HRDATAD (TRPIRQ, trpirq, 8, "trap/interrupt pending") }, | |
| { FLDATAD (CRDERR, crd_err, 0, "correctible read data error flag") }, | |
| { FLDATAD (MEMERR, mem_err, 0, "memory error flag") }, | |
| { FLDATA (HLTPIN, hlt_pin, 0) }, | |
| { HRDATA (IDLE_MASK, cpu_idle_mask, 16), REG_HIDDEN }, | |
| { DRDATA (IDLE_INDX, cpu_idle_type, 4), REG_HRO }, | |
| { DRDATA (IDLE_ENAB, sim_idle_enab, 4), REG_HRO }, | |
| { BRDATAD (PCQ, pcq, 16, 32, PCQ_SIZE, "PC prior to last PC change or interrupt;"), REG_RO+REG_CIRC }, | |
| { HRDATA (PCQP, pcq_p, 6), REG_HRO }, | |
| { HRDATA (BADABO, badabo, 32), REG_HRO }, | |
| { HRDATAD (WRU, sim_int_char, 8, "interrupt character") }, | |
| { HRDATA (MODEL, sys_model, 32), REG_HRO }, | |
| { NULL } | |
| }; | |
| MTAB cpu_mod[] = { | |
| { UNIT_CONH, 0, "HALT to SIMH", "SIMHALT", NULL, NULL, NULL, "Set HALT to trap to simulator" }, | |
| { UNIT_CONH, UNIT_CONH, "HALT to console", "CONHALT", NULL, NULL, NULL, "Set HALT to trap to console ROM" }, | |
| { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE={VMS|ULTRIX|ULTRIX-1.X|ULTRIXOLD|NETBSD|NETBSDOLD|OPENBSD|OPENBSDOLD|QUASIJARUS|32V|ELN}{:n}", &cpu_set_idle, &cpu_show_idle, NULL, "Display idle detection mode" }, | |
| { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL, NULL, "Disables idle detection" }, | |
| MEM_MODIFIERS, /* Model specific memory modifiers from vaxXXX_defs.h */ | |
| { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", | |
| &cpu_set_hist, &cpu_show_hist, NULL, "Displays instruction history" }, | |
| { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL, | |
| NULL, &cpu_show_virt, NULL, "show translation for address arg in KESU mode" }, | |
| CPU_MODEL_MODIFIERS, /* Model specific cpu modifiers from vaxXXX_defs.h */ | |
| { 0 } | |
| }; | |
| DEBTAB cpu_deb[] = { | |
| { "INTEXC", LOG_CPU_I }, | |
| { "REI", LOG_CPU_R }, | |
| { "CONTEXT", LOG_CPU_P }, | |
| { "EVENT", SIM_DBG_EVENT }, | |
| { "ACTIVATE", SIM_DBG_ACTIVATE }, | |
| { "ASYNCH", SIM_DBG_AIO_QUEUE }, | |
| { NULL, 0 } | |
| }; | |
| DEVICE cpu_dev = { | |
| "CPU", &cpu_unit, cpu_reg, cpu_mod, | |
| 1, 16, 32, 1, 16, 8, | |
| &cpu_ex, &cpu_dep, &cpu_reset, | |
| &cpu_boot, NULL, NULL, | |
| NULL, DEV_DYNM | DEV_DEBUG, 0, | |
| cpu_deb, &cpu_set_size, NULL, &cpu_help, NULL, NULL, | |
| &cpu_description | |
| }; | |
| t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc) | |
| { | |
| fprintf (st, "model="); | |
| return cpu_print_model (st); | |
| } | |
| const char *cpu_description (DEVICE *dptr) | |
| { | |
| static char buf[80]; | |
| uint32 min_mem = 4096, max_mem = 0; | |
| MTAB *mptr; | |
| for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) { | |
| if (mptr->valid != &cpu_set_size) | |
| continue; | |
| if ((mptr->match >> 20) < min_mem) | |
| min_mem = (mptr->match >> 20); | |
| if ((mptr->match >> 20) > max_mem) | |
| max_mem = (mptr->match >> 20); | |
| } | |
| sprintf (buf, "VAX CPU with %dMB-%dMB of memory", (int)min_mem, (int)max_mem); | |
| return buf; | |
| } | |
| t_stat sim_instr (void) | |
| { | |
| volatile int32 opc = 0, cc; /* used by setjmp */ | |
| volatile int32 acc; /* set by setjmp */ | |
| int abortval; | |
| t_stat r; | |
| if ((r = build_dib_tab ()) != SCPE_OK) /* build, chk dib_tab */ | |
| return r; | |
| if ((PSL & PSL_MBZ) || /* validate PSL<mbz> */ | |
| ((PSL & PSL_CM) && BadCmPSL (PSL)) || /* validate PSL<cm> */ | |
| ((PSL_GETCUR (PSL) != KERN) && /* esu => is, ipl = 0 */ | |
| (PSL & (PSL_IS|PSL_IPL))) || | |
| ((PSL & PSL_IS) && ((PSL & PSL_IPL) == 0))) /* is => ipl > 0 */ | |
| return sim_messagef (SCPE_STOP, "Unreasonable PSL value: %08X\r\n", PSL); | |
| cc = PSL & CC_MASK; /* split PSL */ | |
| PSL = PSL & ~CC_MASK; | |
| in_ie = 0; /* not in exc */ | |
| set_map_reg (); /* set map reg */ | |
| GET_CUR; /* set access mask */ | |
| SET_IRQL; /* eval interrupts */ | |
| FLUSH_ISTR; /* clear prefetch */ | |
| abortval = setjmp (save_env); /* set abort hdlr */ | |
| if (abortval > 0) { /* sim stop? */ | |
| PSL = PSL | cc; /* put PSL together */ | |
| pcq_r->qptr = pcq_p; /* update pc q ptr */ | |
| if (hst_log) { /* auto logging history? */ | |
| cpu_show_hist_records (hst_log, FALSE, hst_log_p, (hst_p < hst_log_p) ? hst_lnt - (hst_log_p - hst_p) : hst_p - hst_log_p); | |
| hst_log_p = hst_p; /* record everything logged */ | |
| } | |
| return abortval; /* return to SCP */ | |
| } | |
| else if (abortval < 0) { /* mm or rsrv or int */ | |
| int32 i, delta; | |
| if ((PSL & PSL_FPD) == 0) { /* FPD? no recovery */ | |
| for (i = 0; i < recqptr; i++) { /* unwind inst */ | |
| int32 rrn, rlnt; | |
| rrn = RQ_GETRN (recq[i]); /* recover reg # */ | |
| rlnt = DR_LNT (RQ_GETLNT (recq[i])); /* recovery lnt */ | |
| if (recq[i] & RQ_DIR) | |
| R[rrn] = R[rrn] - rlnt; | |
| else R[rrn] = R[rrn] + rlnt; | |
| } | |
| } | |
| PSL = PSL & ~PSL_TP; /* clear <tp> */ | |
| recqptr = 0; /* clear queue */ | |
| delta = PC - fault_PC; /* save delta PC */ | |
| SETPC (fault_PC); /* restore PC */ | |
| switch (-abortval) { /* case on abort code */ | |
| case SCB_RESIN: /* rsrv inst fault */ | |
| case SCB_RESAD: /* rsrv addr fault */ | |
| case SCB_RESOP: /* rsrv opnd fault */ | |
| if (in_ie) /* in exc? panic */ | |
| ABORT (STOP_INIE); | |
| cc = intexc (-abortval, cc, 0, IE_EXC); /* take exception */ | |
| GET_CUR; /* PSL<cur> changed */ | |
| break; | |
| case SCB_CMODE: /* comp mode fault */ | |
| case SCB_ARITH: /* arithmetic fault */ | |
| if (in_ie) /* in exc? panic */ | |
| ABORT (STOP_INIE); | |
| cc = intexc (-abortval, cc, 0, IE_EXC); /* take exception */ | |
| GET_CUR; | |
| in_ie = 1; | |
| Write (SP - 4, p1, L_LONG, WA); /* write arith param */ | |
| SP = SP - 4; | |
| in_ie = 0; | |
| break; | |
| case SCB_ACV: /* ACV fault */ | |
| case SCB_TNV: /* TNV fault */ | |
| if (in_ie) { /* in exception? */ | |
| if (PSL & PSL_IS) /* on is? panic */ | |
| ABORT (STOP_INIE); | |
| cc = intexc (SCB_KSNV, cc, 0, IE_SVE); /* ksnv */ | |
| GET_CUR; | |
| } | |
| else { | |
| cc = intexc (-abortval, cc, 0, IE_EXC); /* take exception */ | |
| GET_CUR; | |
| in_ie = 1; | |
| Write (SP - 8, p1, L_LONG, WA); /* write mm params */ | |
| Write (SP - 4, p2, L_LONG, WA); | |
| SP = SP - 8; | |
| in_ie = 0; | |
| } | |
| break; | |
| case SCB_MCHK: /* machine check */ | |
| cc = machine_check (p1, opc, cc, delta); /* system specific */ | |
| in_ie = 0; | |
| GET_CUR; /* PSL<cur> changed */ | |
| break; | |
| case 1: /* interrupt */ | |
| break; /* just proceed */ | |
| default: /* other */ | |
| badabo = abortval; /* save code */ | |
| ABORT (STOP_UNKABO); /* panic */ | |
| } /* end case */ | |
| } /* end else */ | |
| /* Main instruction loop */ | |
| for ( ;; ) { | |
| int32 spec, disp, rn, index, numspec; | |
| int32 vfldrp1, brdisp, flg, mstat; | |
| int32 i, j, r, rh, temp; | |
| uint32 va, iad; | |
| int32 opnd[OPND_SIZE]; /* operand queue */ | |
| /* Optionally record instruction history results from prior instruction */ | |
| if (hst_lnt) { | |
| InstHistory *hlast = &hst[hst_p ? hst_p-1 : hst_lnt -1]; | |
| int res = (drom[hlast->opc][0] & DR_M_RESMASK) >> DR_V_RESMASK; | |
| switch ((drom[hlast->opc][0] & DR_M_RESMASK) >> DR_V_RESMASK) { | |
| case RB_O>>DR_V_RESMASK: | |
| break; | |
| case RB_Q>>DR_V_RESMASK: | |
| hlast->res[1] = rh; | |
| hlast->res[0] = r; | |
| break; | |
| case RB_B>>DR_V_RESMASK: | |
| case RB_W>>DR_V_RESMASK: | |
| case RB_L>>DR_V_RESMASK: | |
| hlast->res[0] = r; | |
| break; | |
| case RB_R5>>DR_V_RESMASK: | |
| hlast->res[5] = R[5]; | |
| hlast->res[4] = R[4]; | |
| case RB_R3>>DR_V_RESMASK: | |
| hlast->res[3] = R[3]; | |
| hlast->res[2] = R[2]; | |
| case RB_R1>>DR_V_RESMASK: | |
| hlast->res[1] = R[1]; | |
| case RB_R0>>DR_V_RESMASK: | |
| hlast->res[0] = R[0]; | |
| break; | |
| case RB_SP>>DR_V_RESMASK: | |
| hlast->res[0] = Read (SP, L_LONG, RA); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| if (cpu_astop) { | |
| cpu_astop = 0; | |
| ABORT (SCPE_STOP); | |
| } | |
| fault_PC = PC; | |
| recqptr = 0; /* clr recovery q */ | |
| AIO_CHECK_EVENT; /* queue async events */ | |
| if (sim_interval <= 0) { /* chk clock queue */ | |
| temp = sim_process_event (); | |
| if (temp) | |
| ABORT (temp); | |
| SET_IRQL; /* update interrupts */ | |
| } | |
| /* Test for non-instruction dispatches, in SRM order | |
| - trap or interrupt (trpirq != 0) | |
| - PSL<tp> set | |
| If any of these conditions are met, re-dispatch; otherwise, | |
| set PSL<tp> from PSL<t>. | |
| */ | |
| if (trpirq) { /* trap or interrupt? */ | |
| if ((temp = GET_TRAP (trpirq))) { /* trap? */ | |
| cc = intexc (SCB_ARITH, cc, 0, IE_EXC); /* take, clear trap */ | |
| GET_CUR; /* set cur mode */ | |
| in_ie = 1; | |
| Write (SP - 4, temp, L_LONG, WA); /* write parameter */ | |
| SP = SP - 4; | |
| in_ie = 0; | |
| } | |
| else if ((temp = GET_IRQL (trpirq))) { /* interrupt? */ | |
| int32 vec; | |
| if (temp == IPL_HLTPIN) { /* console halt? */ | |
| hlt_pin = 0; /* clear intr */ | |
| trpirq = 0; /* clear everything */ | |
| cc = con_halt (CON_HLTPIN, cc); /* invoke firmware */ | |
| continue; /* continue */ | |
| } | |
| else if (temp >= IPL_HMIN) /* hardware req? */ | |
| vec = get_vector (temp); /* get vector */ | |
| else if (temp > IPL_SMAX) | |
| ABORT (STOP_UIPL); | |
| else { | |
| vec = SCB_IPLSOFT + (temp << 2); | |
| SISR = SISR & ~(1u << temp); | |
| } | |
| if (vec) /* take intr */ | |
| cc = intexc (vec, cc, temp, IE_INT); | |
| GET_CUR; /* set cur mode */ | |
| } | |
| else trpirq = 0; /* clear everything */ | |
| SET_IRQL; /* eval interrupts */ | |
| continue; | |
| } | |
| if (PSL & (PSL_CM|PSL_TP|PSW_T)) { /* PSL event? */ | |
| if (PSL & PSL_TP) { /* trace trap? */ | |
| PSL = PSL & ~PSL_TP; /* clear <tp> */ | |
| cc = intexc (SCB_TP, cc, 0, IE_EXC); /* take trap */ | |
| GET_CUR; /* set cur mode */ | |
| continue; | |
| } | |
| if (PSL & PSW_T) /* if T, set TP */ | |
| PSL = PSL | PSL_TP; | |
| if (PSL & PSL_CM) { /* compat mode? */ | |
| cc = op_cmode (cc); /* exec instr */ | |
| continue; /* skip fetch */ | |
| } | |
| } /* end PSL event */ | |
| if (sim_brk_summ && | |
| sim_brk_test ((uint32) PC, SWMASK ('E'))) { /* breakpoint? */ | |
| ABORT (STOP_IBKPT); /* stop simulation */ | |
| } | |
| sim_interval = sim_interval - 1; /* count instr */ | |
| GET_ISTR (opc, L_BYTE); /* get opcode */ | |
| if (opc == 0xFD) { /* 2 byte op? */ | |
| GET_ISTR (opc, L_BYTE); /* get second byte */ | |
| opc = opc | 0x100; /* flag */ | |
| } | |
| numspec = drom[opc][0]; /* get # specs */ | |
| if (PSL & PSL_FPD) { | |
| if ((numspec & DR_F) == 0) | |
| RSVD_INST_FAULT; | |
| } | |
| else { | |
| numspec = numspec & DR_NSPMASK; /* get # specifiers */ | |
| /* Specifier flows. Operands are parsed and placed into queue opnd. | |
| r.bwl opnd[j] = value of operand | |
| r.q opnd[j:j+1] = value of operand | |
| r.o opnd[j:j+3] = value of operand | |
| a.bwlqo opnd[j] = address of operand | |
| m.bwl opnd[j] = value of operand | |
| m.q opnd[j:j+1] = value of operand | |
| m.o opnd[j:j+3] = value of operand | |
| w.bwlqo opnd[j] = register/memory flag | |
| opnd[j+1] = memory address | |
| For the last memory specifier, the specifier is in spec, the register | |
| number is in rn, and the effective address is in va. Modify specifiers | |
| (always last) can test spec > reg+PC, as short literal are illegal for | |
| modifiers specifiers, and final index specifiers are always illegal. | |
| */ | |
| for (i = 1, j = 0; i <= numspec; i++) { /* loop thru specs */ | |
| disp = drom[opc][i]; /* get dispatch */ | |
| if (disp >= BB) { | |
| GET_ISTR (brdisp, DR_LNT (disp & 1)); | |
| break; | |
| } | |
| GET_ISTR (spec, L_BYTE); /* get spec byte */ | |
| rn = spec & RGMASK; /* get reg # */ | |
| disp = (spec & ~RGMASK) | disp; /* merge w dispatch */ | |
| switch (disp) { /* dispatch spec */ | |
| /* Short literal - only read access permitted */ | |
| case SH0|RB: case SH0|RW: case SH0|RL: | |
| case SH1|RB: case SH1|RW: case SH1|RL: | |
| case SH2|RB: case SH2|RW: case SH2|RL: | |
| case SH3|RB: case SH3|RW: case SH3|RL: | |
| opnd[j++] = spec; | |
| break; | |
| case SH0|RQ: case SH1|RQ: case SH2|RQ: case SH3|RQ: | |
| opnd[j++] = spec; | |
| opnd[j++] = 0; | |
| break; | |
| case SH0|RO: case SH1|RO: case SH2|RO: case SH3|RO: | |
| opnd[j++] = spec; | |
| opnd[j++] = 0; | |
| opnd[j++] = 0; | |
| opnd[j++] = 0; | |
| break; | |
| case SH0|RF: case SH1|RF: case SH2|RF: case SH3|RF: | |
| opnd[j++] = (spec << 4) | 0x4000; | |
| break; | |
| case SH0|RD: case SH1|RD: case SH2|RD: case SH3|RD: | |
| opnd[j++] = (spec << 4) | 0x4000; | |
| opnd[j++] = 0; | |
| break; | |
| case SH0|RG: case SH1|RG: case SH2|RG: case SH3|RG: | |
| opnd[j++] = (spec << 1) | 0x4000; | |
| opnd[j++] = 0; | |
| break; | |
| case SH0|RH: case SH1|RH: case SH2|RH: case SH3|RH: | |
| opnd[j++] = ((spec & 0x7) << 29) | (0x4000 | ((spec >> 3) & 0x7)); | |
| opnd[j++] = 0; | |
| opnd[j++] = 0; | |
| opnd[j++] = 0; | |
| break; | |
| /* Register */ | |
| case GRN|RB: case GRN|MB: | |
| CHECK_FOR_PC; | |
| opnd[j++] = R[rn] & BMASK; | |
| break; | |
| case GRN|RW: case GRN|MW: | |
| CHECK_FOR_PC; | |
| opnd[j++] = R[rn] & WMASK; | |
| break; | |
| case GRN|VB: | |
| vfldrp1 = R[(rn + 1) & RGMASK]; | |
| case GRN|WB: case GRN|WW: case GRN|WL: case GRN|WQ: case GRN|WO: | |
| opnd[j++] = rn; | |
| case GRN|RL: case GRN|RF: case GRN|ML: | |
| CHECK_FOR_PC; | |
| opnd[j++] = R[rn]; | |
| break; | |
| case GRN|RQ: case GRN|RD: case GRN|RG: case GRN|MQ: | |
| CHECK_FOR_SP; | |
| opnd[j++] = R[rn]; | |
| opnd[j++] = R[rn + 1]; | |
| break; | |
| case GRN|RO: case GRN|RH: case GRN|MO: | |
| CHECK_FOR_AP; | |
| opnd[j++] = R[rn]; | |
| opnd[j++] = R[rn + 1]; | |
| opnd[j++] = R[rn + 2]; | |
| opnd[j++] = R[rn + 3]; | |
| break; | |
| /* Register deferred, autodecrement */ | |
| case RGD|VB: | |
| case RGD|WB: case RGD|WW: case RGD|WL: case RGD|WQ: case RGD|WO: | |
| opnd[j++] = OP_MEM; | |
| case RGD|AB: case RGD|AW: case RGD|AL: case RGD|AQ: case RGD|AO: | |
| CHECK_FOR_PC; | |
| va = opnd[j++] = R[rn]; | |
| break; | |
| case ADC|VB: | |
| case ADC|WB: case ADC|WW: case ADC|WL: case ADC|WQ: case ADC|WO: | |
| opnd[j++] = OP_MEM; | |
| case ADC|AB: case ADC|AW: case ADC|AL: case ADC|AQ: case ADC|AO: | |
| CHECK_FOR_PC; | |
| va = opnd[j++] = R[rn] = R[rn] - DR_LNT (disp); | |
| recq[recqptr++] = RQ_REC (disp, rn); | |
| break; | |
| case ADC|RB: case ADC|RW: case ADC|RL: case ADC|RF: | |
| R[rn] = R[rn] - (DR_LNT (disp)); | |
| recq[recqptr++] = RQ_REC (disp, rn); | |
| case RGD|RB: case RGD|RW: case RGD|RL: case RGD|RF: | |
| CHECK_FOR_PC; | |
| opnd[j++] = Read (va = R[rn], DR_LNT (disp), RA); | |
| break; | |
| case ADC|RQ: case ADC|RD: case ADC|RG: | |
| R[rn] = R[rn] - 8; | |
| recq[recqptr++] = RQ_REC (disp, rn); | |
| case RGD|RQ: case RGD|RD: case RGD|RG: | |
| CHECK_FOR_PC; | |
| opnd[j++] = Read (va = R[rn], L_LONG, RA); | |
| opnd[j++] = Read (R[rn] + 4, L_LONG, RA); | |
| break; | |
| case ADC|RO: case ADC|RH: | |
| R[rn] = R[rn] - 16; | |
| recq[recqptr++] = RQ_REC (disp, rn); | |
| case RGD|RO: case RGD|RH: | |
| CHECK_FOR_PC; | |
| j = ReadOcta (va = R[rn], opnd, j, RA); | |
| break; | |
| case ADC|MB: case ADC|MW: case ADC|ML: | |
| R[rn] = R[rn] - (DR_LNT (disp)); | |
| recq[recqptr++] = RQ_REC (disp, rn); | |
| case RGD|MB: case RGD|MW: case RGD|ML: | |
| CHECK_FOR_PC; | |
| opnd[j++] = Read (va = R[rn], DR_LNT (disp), WA); | |
| break; | |
| case ADC|MQ: | |
| R[rn] = R[rn] - 8; | |
| recq[recqptr++] = RQ_REC (disp, rn); | |
| case RGD|MQ: | |
| CHECK_FOR_PC; | |
| opnd[j++] = Read (va = R[rn], L_LONG, WA); | |
| opnd[j++] = Read (R[rn] + 4, L_LONG, WA); | |
| break; | |
| case ADC|MO: | |
| R[rn] = R[rn] - 16; | |
| recq[recqptr++] = RQ_REC (disp, rn); | |
| case RGD|MO: | |
| CHECK_FOR_PC; | |
| j = ReadOcta (va = R[rn], opnd, j, WA); | |
| break; | |
| /* Autoincrement */ | |
| case AIN|VB: | |
| case AIN|WB: case AIN|WW: case AIN|WL: case AIN|WQ: case AIN|WO: | |
| /* CHECK_FOR_PC; */ | |
| opnd[j++] = OP_MEM; | |
| case AIN|AB: case AIN|AW: case AIN|AL: case AIN|AQ: case AIN|AO: | |
| va = opnd[j++] = R[rn]; | |
| if (rn == nPC) { | |
| if (DR_LNT (disp) >= L_QUAD) { | |
| GET_ISTR (temp, L_LONG); | |
| GET_ISTR (temp, L_LONG); | |
| if (DR_LNT (disp) == L_OCTA) { | |
| GET_ISTR (temp, L_LONG); | |
| GET_ISTR (temp, L_LONG); | |
| } | |
| } | |
| else GET_ISTR (temp, DR_LNT (disp)); | |
| } | |
| else { | |
| R[rn] = R[rn] + DR_LNT (disp); | |
| recq[recqptr++] = RQ_REC (disp, rn); | |
| } | |
| break; | |
| case AIN|RB: case AIN|RW: case AIN|RL: case AIN|RF: | |
| va = R[rn]; | |
| if (rn == nPC) { | |
| GET_ISTR (opnd[j++], DR_LNT (disp)); | |
| } | |
| else { | |
| opnd[j++] = Read (R[rn], DR_LNT (disp), RA); | |
| R[rn] = R[rn] + DR_LNT (disp); | |
| recq[recqptr++] = RQ_REC (disp, rn); | |
| } | |
| break; | |
| case AIN|RQ: case AIN|RD: case AIN|RG: | |
| va = R[rn]; | |
| if (rn == nPC) { | |
| GET_ISTR (opnd[j++], L_LONG); | |
| GET_ISTR (opnd[j++], L_LONG); | |
| } | |
| else { | |
| opnd[j++] = Read (va, L_LONG, RA); | |
| opnd[j++] = Read (va + 4, L_LONG, RA); | |
| R[rn] = R[rn] + 8; | |
| recq[recqptr++] = RQ_REC (disp, rn); | |
| } | |
| break; | |
| case AIN|RO: case AIN|RH: | |
| va = R[rn]; | |
| if (rn == nPC) { | |
| GET_ISTR (opnd[j++], L_LONG); | |
| GET_ISTR (opnd[j++], L_LONG); | |
| GET_ISTR (opnd[j++], L_LONG); | |
| GET_ISTR (opnd[j++], L_LONG); | |
| } | |
| else { | |
| j = ReadOcta (va, opnd, j, RA); | |
| R[rn] = R[rn] + 16; | |
| recq[recqptr++] = RQ_REC (disp, rn); | |
| } | |
| break; | |
| case AIN|MB: case AIN|MW: case AIN|ML: | |
| va = R[rn]; | |
| if (rn == nPC) { | |
| GET_ISTR (opnd[j++], DR_LNT (disp)); | |
| } | |
| else { | |
| opnd[j++] = Read (R[rn], DR_LNT (disp), WA); | |
| R[rn] = R[rn] + DR_LNT (disp); | |
| recq[recqptr++] = RQ_REC (disp, rn); | |
| } | |
| break; | |
| case AIN|MQ: | |
| va = R[rn]; | |
| if (rn == nPC) { | |
| GET_ISTR (opnd[j++], L_LONG); | |
| GET_ISTR (opnd[j++], L_LONG); | |
| } | |
| else { | |
| opnd[j++] = Read (va, L_LONG, WA); | |
| opnd[j++] = Read (va + 4, L_LONG, WA); | |
| R[rn] = R[rn] + 8; | |
| recq[recqptr++] = RQ_REC (disp, rn); | |
| } | |
| break; | |
| case AIN|MO: | |
| va = R[rn]; | |
| if (rn == nPC) { | |
| GET_ISTR (opnd[j++], L_LONG); | |
| GET_ISTR (opnd[j++], L_LONG); | |
| GET_ISTR (opnd[j++], L_LONG); | |
| GET_ISTR (opnd[j++], L_LONG); | |
| } | |
| else { | |
| j = ReadOcta (va, opnd, j, WA); | |
| R[rn] = R[rn] + 16; | |
| recq[recqptr++] = RQ_REC (disp, rn); | |
| } | |
| break; | |
| /* Autoincrement deferred */ | |
| case AID|VB: | |
| case AID|WB: case AID|WW: case AID|WL: case AID|WQ: case AID|WO: | |
| opnd[j++] = OP_MEM; | |
| case AID|AB: case AID|AW: case AID|AL: case AID|AQ: case AID|AO: | |
| if (rn == nPC) { | |
| GET_ISTR (va = opnd[j++], L_LONG); | |
| } | |
| else { | |
| va = opnd[j++] = Read (R[rn], L_LONG, RA); | |
| R[rn] = R[rn] + 4; | |
| recq[recqptr++] = RQ_REC (AID|RL, rn); | |
| } | |
| break; | |
| case AID|RB: case AID|RW: case AID|RL: case AID|RF: | |
| if (rn == nPC) { | |
| GET_ISTR (va, L_LONG); | |
| } | |
| else { | |
| va = Read (R[rn], L_LONG, RA); | |
| R[rn] = R[rn] + 4; | |
| recq[recqptr++] = RQ_REC (AID|RL, rn); | |
| } | |
| opnd[j++] = Read (va, DR_LNT (disp), RA); | |
| break; | |
| case AID|RQ: case AID|RD: case AID|RG: | |
| if (rn == nPC) { | |
| GET_ISTR (va, L_LONG); | |
| } | |
| else { | |
| va = Read (R[rn], L_LONG, RA); | |
| R[rn] = R[rn] + 4; | |
| recq[recqptr++] = RQ_REC (AID|RL, rn); | |
| } | |
| opnd[j++] = Read (va, L_LONG, RA); | |
| opnd[j++] = Read (va + 4, L_LONG, RA); | |
| break; | |
| case AID|RO: case AID|RH: | |
| if (rn == nPC) { | |
| GET_ISTR (va, L_LONG); | |
| } | |
| else { | |
| va = Read (R[rn], L_LONG, RA); | |
| R[rn] = R[rn] + 4; | |
| recq[recqptr++] = RQ_REC (AID|RL, rn); | |
| } | |
| j = ReadOcta (va, opnd, j, RA); | |
| break; | |
| case AID|MB: case AID|MW: case AID|ML: | |
| if (rn == nPC) { | |
| GET_ISTR (va, L_LONG); | |
| } | |
| else { | |
| va = Read (R[rn], L_LONG, RA); | |
| R[rn] = R[rn] + 4; | |
| recq[recqptr++] = RQ_REC (AID|RL, rn); | |
| } | |
| opnd[j++] = Read (va, DR_LNT (disp), WA); | |
| break; | |
| case AID|MQ: | |
| if (rn == nPC) { | |
| GET_ISTR (va, L_LONG); | |
| } | |
| else { | |
| va = Read (R[rn], L_LONG, RA); | |
| R[rn] = R[rn] + 4; | |
| recq[recqptr++] = RQ_REC (AID|RL, rn); | |
| } | |
| opnd[j++] = Read (va, L_LONG, WA); | |
| opnd[j++] = Read (va + 4, L_LONG, WA); | |
| break; | |
| case AID|MO: | |
| if (rn == nPC) { | |
| GET_ISTR (va, L_LONG); | |
| } | |
| else { | |
| va = Read (R[rn], L_LONG, RA); | |
| R[rn] = R[rn] + 4; | |
| recq[recqptr++] = RQ_REC (AID|RL, rn); | |
| } | |
| j = ReadOcta (va, opnd, j, WA); | |
| break; | |
| /* Byte displacement */ | |
| case BDP|VB: | |
| case BDP|WB: case BDP|WW: case BDP|WL: case BDP|WQ: case BDP|WO: | |
| opnd[j++] = OP_MEM; | |
| case BDP|AB: case BDP|AW: case BDP|AL: case BDP|AQ: case BDP|AO: | |
| GET_ISTR (temp, L_BYTE); | |
| va = opnd[j++] = R[rn] + SXTB (temp); | |
| break; | |
| case BDP|RB: case BDP|RW: case BDP|RL: case BDP|RF: | |
| GET_ISTR (temp, L_BYTE); | |
| va = R[rn] + SXTB (temp); | |
| opnd[j++] = Read (va, DR_LNT (disp), RA); | |
| break; | |
| case BDP|RQ: case BDP|RD: case BDP|RG: | |
| GET_ISTR (temp, L_BYTE); | |
| va = R[rn] + SXTB (temp); | |
| opnd[j++] = Read (va, L_LONG, RA); | |
| opnd[j++] = Read (va + 4, L_LONG, RA); | |
| break; | |
| case BDP|RO: case BDP|RH: | |
| GET_ISTR (temp, L_BYTE); | |
| va = R[rn] + SXTB (temp); | |
| j = ReadOcta (va, opnd, j, RA); | |
| break; | |
| case BDP|MB: case BDP|MW: case BDP|ML: | |
| GET_ISTR (temp, L_BYTE); | |
| va = R[rn] + SXTB (temp); | |
| opnd[j++] = Read (va, DR_LNT (disp), WA); | |
| break; | |
| case BDP|MQ: | |
| GET_ISTR (temp, L_BYTE); | |
| va = R[rn] + SXTB (temp); | |
| opnd[j++] = Read (va, L_LONG, WA); | |
| opnd[j++] = Read (va + 4, L_LONG, WA); | |
| break; | |
| case BDP|MO: | |
| GET_ISTR (temp, L_BYTE); | |
| va = R[rn] + SXTB (temp); | |
| j = ReadOcta (va, opnd, j, WA); | |
| break; | |
| /* Byte displacement deferred */ | |
| case BDD|VB: | |
| case BDD|WB: case BDD|WW: case BDD|WL: case BDD|WQ: case BDD|WO: | |
| opnd[j++] = OP_MEM; | |
| case BDD|AB: case BDD|AW: case BDD|AL: case BDD|AQ: case BDD|AO: | |
| GET_ISTR (temp, L_BYTE); | |
| iad = R[rn] + SXTB (temp); | |
| va = opnd[j++] = Read (iad, L_LONG, RA); | |
| break; | |
| case BDD|RB: case BDD|RW: case BDD|RL: case BDD|RF: | |
| GET_ISTR (temp, L_BYTE); | |
| iad = R[rn] + SXTB (temp); | |
| va = Read (iad, L_LONG, RA); | |
| opnd[j++] = Read (va, DR_LNT (disp), RA); | |
| break; | |
| case BDD|RQ: case BDD|RD: case BDD|RG: | |
| GET_ISTR (temp, L_BYTE); | |
| iad = R[rn] + SXTB (temp); | |
| va = Read (iad, L_LONG, RA); | |
| opnd[j++] = Read (va, L_LONG, RA); | |
| opnd[j++] = Read (va + 4, L_LONG, RA); | |
| break; | |
| case BDD|RO: case BDD|RH: | |
| GET_ISTR (temp, L_BYTE); | |
| iad = R[rn] + SXTB (temp); | |
| va = Read (iad, L_LONG, RA); | |
| j = ReadOcta (va, opnd, j, RA); | |
| break; | |
| case BDD|MB: case BDD|MW: case BDD|ML: | |
| GET_ISTR (temp, L_BYTE); | |
| iad = R[rn] + SXTB (temp); | |
| va = Read (iad, L_LONG, RA); | |
| opnd[j++] = Read (va, DR_LNT (disp), WA); | |
| break; | |
| case BDD|MQ: | |
| GET_ISTR (temp, L_BYTE); | |
| iad = R[rn] + SXTB (temp); | |
| va = Read (iad, L_LONG, RA); | |
| opnd[j++] = Read (va, L_LONG, WA); | |
| opnd[j++] = Read (va + 4, L_LONG, WA); | |
| break; | |
| case BDD|MO: | |
| GET_ISTR (temp, L_BYTE); | |
| iad = R[rn] + SXTB (temp); | |
| va = Read (iad, L_LONG, RA); | |
| j = ReadOcta (va, opnd, j, WA); | |
| break; | |
| /* Word displacement */ | |
| case WDP|VB: | |
| case WDP|WB: case WDP|WW: case WDP|WL: case WDP|WQ: case WDP|WO: | |
| opnd[j++] = OP_MEM; | |
| case WDP|AB: case WDP|AW: case WDP|AL: case WDP|AQ: case WDP|AO: | |
| GET_ISTR (temp, L_WORD); | |
| va = opnd[j++] = R[rn] + SXTW (temp); | |
| break; | |
| case WDP|RB: case WDP|RW: case WDP|RL: case WDP|RF: | |
| GET_ISTR (temp, L_WORD); | |
| va = R[rn] + SXTW (temp); | |
| opnd[j++] = Read (va, DR_LNT (disp), RA); | |
| break; | |
| case WDP|RQ: case WDP|RD: case WDP|RG: | |
| GET_ISTR (temp, L_WORD); | |
| va = R[rn] + SXTW (temp); | |
| opnd[j++] = Read (va, L_LONG, RA); | |
| opnd[j++] = Read (va + 4, L_LONG, RA); | |
| break; | |
| case WDP|RO: case WDP|RH: | |
| GET_ISTR (temp, L_WORD); | |
| va = R[rn] + SXTW (temp); | |
| j = ReadOcta (va, opnd, j, RA); | |
| break; | |
| case WDP|MB: case WDP|MW: case WDP|ML: | |
| GET_ISTR (temp, L_WORD); | |
| va = R[rn] + SXTW (temp); | |
| opnd[j++] = Read (va, DR_LNT (disp), WA); | |
| break; | |
| case WDP|MQ: | |
| GET_ISTR (temp, L_WORD); | |
| va = R[rn] + SXTW (temp); | |
| opnd[j++] = Read (va, L_LONG, WA); | |
| opnd[j++] = Read (va + 4, L_LONG, WA); | |
| break; | |
| case WDP|MO: | |
| GET_ISTR (temp, L_WORD); | |
| va = R[rn] + SXTW (temp); | |
| j = ReadOcta (va, opnd, j, WA); | |
| break; | |
| /* Word displacement deferred */ | |
| case WDD|VB: | |
| case WDD|WB: case WDD|WW: case WDD|WL: case WDD|WQ: case WDD|WO: | |
| opnd[j++] = OP_MEM; | |
| case WDD|AB: case WDD|AW: case WDD|AL: case WDD|AQ: case WDD|AO: | |
| GET_ISTR (temp, L_WORD); | |
| iad = R[rn] + SXTW (temp); | |
| va = opnd[j++] = Read (iad, L_LONG, RA); | |
| break; | |
| case WDD|RB: case WDD|RW: case WDD|RL: case WDD|RF: | |
| GET_ISTR (temp, L_WORD); | |
| iad = R[rn] + SXTW (temp); | |
| va = Read (iad, L_LONG, RA); | |
| opnd[j++] = Read (va, DR_LNT (disp), RA); | |
| break; | |
| case WDD|RQ: case WDD|RD: case WDD|RG: | |
| GET_ISTR (temp, L_WORD); | |
| iad = R[rn] + SXTW (temp); | |
| va = Read (iad, L_LONG, RA); | |
| opnd[j++] = Read (va, L_LONG, RA); | |
| opnd[j++] = Read (va + 4, L_LONG, RA); | |
| break; | |
| case WDD|RO: case WDD|RH: | |
| GET_ISTR (temp, L_WORD); | |
| iad = R[rn] + SXTW (temp); | |
| va = Read (iad, L_LONG, RA); | |
| j = ReadOcta (va, opnd, j, RA); | |
| break; | |
| case WDD|MB: case WDD|MW: case WDD|ML: | |
| GET_ISTR (temp, L_WORD); | |
| iad = R[rn] + SXTW (temp); | |
| va = Read (iad, L_LONG, RA); | |
| opnd[j++] = Read (va, DR_LNT (disp), WA); | |
| break; | |
| case WDD|MQ: | |
| GET_ISTR (temp, L_WORD); | |
| iad = R[rn] + SXTW (temp); | |
| va = Read (iad, L_LONG, RA); | |
| opnd[j++] = Read (va, L_LONG, WA); | |
| opnd[j++] = Read (va + 4, L_LONG, WA); | |
| break; | |
| case WDD|MO: | |
| GET_ISTR (temp, L_WORD); | |
| iad = R[rn] + SXTW (temp); | |
| va = Read (iad, L_LONG, RA); | |
| j = ReadOcta (va, opnd, j, WA); | |
| break; | |
| /* Longword displacement */ | |
| case LDP|VB: | |
| case LDP|WB: case LDP|WW: case LDP|WL: case LDP|WQ: case LDP|WO: | |
| opnd[j++] = OP_MEM; | |
| case LDP|AB: case LDP|AW: case LDP|AL: case LDP|AQ: case LDP|AO: | |
| GET_ISTR (temp, L_LONG); | |
| va = opnd[j++] = R[rn] + temp; | |
| break; | |
| case LDP|RB: case LDP|RW: case LDP|RL: case LDP|RF: | |
| GET_ISTR (temp, L_LONG); | |
| va = R[rn] + temp; | |
| opnd[j++] = Read (va, DR_LNT (disp), RA); | |
| break; | |
| case LDP|RQ: case LDP|RD: case LDP|RG: | |
| GET_ISTR (temp, L_LONG); | |
| va = R[rn] + temp; | |
| opnd[j++] = Read (va, L_LONG, RA); | |
| opnd[j++] = Read (va + 4, L_LONG, RA); | |
| break; | |
| case LDP|RO: case LDP|RH: | |
| GET_ISTR (temp, L_LONG); | |
| va = R[rn] + temp; | |
| j = ReadOcta (va, opnd, j, RA); | |
| break; | |
| case LDP|MB: case LDP|MW: case LDP|ML: | |
| GET_ISTR (temp, L_LONG); | |
| va = R[rn] + temp; | |
| opnd[j++] = Read (va, DR_LNT (disp), WA); | |
| break; | |
| case LDP|MQ: | |
| GET_ISTR (temp, L_LONG); | |
| va = R[rn] + temp; | |
| opnd[j++] = Read (va, L_LONG, WA); | |
| opnd[j++] = Read (va + 4, L_LONG, WA); | |
| break; | |
| case LDP|MO: | |
| GET_ISTR (temp, L_LONG); | |
| va = R[rn] + temp; | |
| j = ReadOcta (va, opnd, j, WA); | |
| break; | |
| /* Longword displacement deferred */ | |
| case LDD|VB: | |
| case LDD|WB: case LDD|WW: case LDD|WL: case LDD|WQ: case LDD|WO: | |
| opnd[j++] = OP_MEM; | |
| case LDD|AB: case LDD|AW: case LDD|AL: case LDD|AQ: case LDD|AO: | |
| GET_ISTR (temp, L_LONG); | |
| iad = R[rn] + temp; | |
| va = opnd[j++] = Read (iad, L_LONG, RA); | |
| break; | |
| case LDD|RB: case LDD|RW: case LDD|RL: case LDD|RF: | |
| GET_ISTR (temp, L_LONG); | |
| iad = R[rn] + temp; | |
| va = Read (iad, L_LONG, RA); | |
| opnd[j++] = Read (va, DR_LNT (disp), RA); | |
| break; | |
| case LDD|RQ: case LDD|RD: case LDD|RG: | |
| GET_ISTR (temp, L_LONG); | |
| iad = R[rn] + temp; | |
| va = Read (iad, L_LONG, RA); | |
| opnd[j++] = Read (va, L_LONG, RA); | |
| opnd[j++] = Read (va + 4, L_LONG, RA); | |
| break; | |
| case LDD|RO: case LDD|RH: | |
| GET_ISTR (temp, L_LONG); | |
| iad = R[rn] + temp; | |
| va = Read (iad, L_LONG, RA); | |
| j = ReadOcta (va, opnd, j, RA); | |
| break; | |
| case LDD|MB: case LDD|MW: case LDD|ML: | |
| GET_ISTR (temp, L_LONG); | |
| iad = R[rn] + temp; | |
| va = Read (iad, L_LONG, RA); | |
| opnd[j++] = Read (va, DR_LNT (disp), WA); | |
| break; | |
| case LDD|MQ: | |
| GET_ISTR (temp, L_LONG); | |
| iad = R[rn] + temp; | |
| va = Read (iad, L_LONG, RA); | |
| opnd[j++] = Read (va, L_LONG, WA); | |
| opnd[j++] = Read (va + 4, L_LONG, WA); | |
| break; | |
| case LDD|MO: | |
| GET_ISTR (temp, L_LONG); | |
| iad = R[rn] + temp; | |
| va = Read (iad, L_LONG, RA); | |
| j = ReadOcta (va, opnd, j, WA); | |
| break; | |
| /* Index */ | |
| case IDX|VB: | |
| case IDX|WB: case IDX|WW: case IDX|WL: case IDX|WQ: case IDX|WO: | |
| case IDX|AB: case IDX|AW: case IDX|AL: case IDX|AQ: case IDX|AO: | |
| case IDX|MB: case IDX|MW: case IDX|ML: case IDX|MQ: case IDX|MO: | |
| case IDX|RB: case IDX|RW: case IDX|RL: case IDX|RQ: case IDX|RO: | |
| case IDX|RF: case IDX|RD: case IDX|RG: case IDX|RH: | |
| CHECK_FOR_PC; | |
| index = R[rn] << (disp & DR_LNMASK); | |
| GET_ISTR (spec, L_BYTE); | |
| rn = spec & RGMASK; | |
| switch (spec & ~RGMASK) { | |
| case ADC: | |
| R[rn] = R[rn] - DR_LNT (disp); | |
| recq[recqptr++] = RQ_REC (ADC | (disp & DR_LNMASK), rn); | |
| case RGD: | |
| CHECK_FOR_PC; | |
| index = index + R[rn]; | |
| break; | |
| case AIN: | |
| CHECK_FOR_PC; | |
| index = index + R[rn]; | |
| R[rn] = R[rn] + DR_LNT (disp); | |
| recq[recqptr++] = RQ_REC (AIN | (disp & DR_LNMASK), rn); | |
| break; | |
| case AID: | |
| if (rn == nPC) { | |
| GET_ISTR (temp, L_LONG); | |
| } | |
| else { | |
| temp = Read (R[rn], L_LONG, RA); | |
| R[rn] = R[rn] + 4; | |
| recq[recqptr++] = RQ_REC (AID|RL, rn); | |
| } | |
| index = temp + index; | |
| break; | |
| case BDP: | |
| GET_ISTR (temp, L_BYTE); | |
| index = index + R[rn] + SXTB (temp); | |
| break; | |
| case BDD: | |
| GET_ISTR (temp, L_BYTE); | |
| index = index + Read (R[rn] + SXTB (temp), L_LONG, RA); | |
| break; | |
| case WDP: | |
| GET_ISTR (temp, L_WORD); | |
| index = index + R[rn] + SXTW (temp); | |
| break; | |
| case WDD: | |
| GET_ISTR (temp, L_WORD); | |
| index = index + Read (R[rn] + SXTW (temp), L_LONG, RA); | |
| break; | |
| case LDP: | |
| GET_ISTR (temp, L_LONG); | |
| index = index + R[rn] + temp; | |
| break; | |
| case LDD: | |
| GET_ISTR (temp, L_LONG); | |
| index = index + Read (R[rn] + temp, L_LONG, RA); | |
| break; | |
| default: | |
| RSVD_ADDR_FAULT; /* end case idxspec */ | |
| } | |
| switch (disp & (DR_ACMASK|DR_SPFLAG|DR_LNMASK)) { /* case acc+lnt */ | |
| case VB: | |
| case WB: case WW: case WL: case WQ: case WO: | |
| opnd[j++] = OP_MEM; | |
| case AB: case AW: case AL: case AQ: case AO: | |
| va = opnd[j++] = index; | |
| break; | |
| case RB: case RW: case RL: case RF: | |
| opnd[j++] = Read (va = index, DR_LNT (disp), RA); | |
| break; | |
| case RQ: case RD: case RG: | |
| opnd[j++] = Read (va = index, L_LONG, RA); | |
| opnd[j++] = Read (index + 4, L_LONG, RA); | |
| break; | |
| case RO: case RH: | |
| j = ReadOcta (va = index, opnd, j, RA); | |
| break; | |
| case MB: case MW: case ML: | |
| opnd[j++] = Read (va = index, DR_LNT (disp), WA); | |
| break; | |
| case MQ: | |
| opnd[j++] = Read (va = index, L_LONG, WA); | |
| opnd[j++] = Read (index + 4, L_LONG, WA); | |
| break; | |
| case MO: | |
| j = ReadOcta (va = index, opnd, j, WA); | |
| break; | |
| default: /* all others */ | |
| RSVD_ADDR_FAULT; /* fault */ | |
| break; | |
| } /* end case access/lnt */ | |
| break; /* end index */ | |
| default: /* all others */ | |
| RSVD_ADDR_FAULT; /* fault */ | |
| break; | |
| } /* end case spec */ | |
| } /* end for */ | |
| } /* end if not FPD */ | |
| /* Optionally record instruction history */ | |
| if (hst_lnt) { | |
| int32 lim; | |
| t_value wd; | |
| InstHistory *h = &hst[hst_p]; | |
| h->iPC = fault_PC; | |
| h->PSL = PSL | cc; | |
| h->opc = opc; | |
| for (i = 0; i < j; i++) | |
| h->opnd[i] = opnd[i]; | |
| lim = PC - fault_PC; | |
| if ((uint32) lim > INST_SIZE) | |
| lim = INST_SIZE; | |
| for (i = 0; i < lim; i++) { | |
| if ((cpu_ex (&wd, fault_PC + i, &cpu_unit, SWMASK ('V'))) == SCPE_OK) | |
| h->inst[i] = (uint8) wd; | |
| else { | |
| h->inst[0] = h->inst[1] = 0xFF; | |
| break; | |
| } | |
| } | |
| if (hst_switches & SWMASK('T')) | |
| h->time = sim_gtime(); | |
| hst_p = hst_p + 1; | |
| if (hst_p >= hst_lnt) | |
| hst_p = 0; | |
| if (hst_log && (hst_p == hst_log_p)) | |
| cpu_show_hist_records (hst_log, FALSE, hst_log_p, hst_lnt); | |
| } | |
| /* Dispatch to instructions */ | |
| switch (opc) { | |
| /* Single operand instructions with dest, write only - CLRx dst.wx | |
| spec = reg/memory flag | |
| rn = register number | |
| va = virtual address | |
| */ | |
| case CLRB: | |
| r = 0; | |
| WRITE_B (r); /* store result */ | |
| CC_ZZ1P; /* set cc's */ | |
| break; | |
| case CLRW: | |
| r = 0; | |
| WRITE_W (r); /* store result */ | |
| CC_ZZ1P; /* set cc's */ | |
| break; | |
| case CLRL: | |
| r = 0; | |
| WRITE_L (r); /* store result */ | |
| CC_ZZ1P; /* set cc's */ | |
| break; | |
| case CLRQ: | |
| WRITE_Q (0, 0); /* store result */ | |
| CC_ZZ1P; /* set cc's */ | |
| break; | |
| /* Single operand instructions with source, read only - TSTx src.rx | |
| opnd[0] = source | |
| */ | |
| case TSTB: | |
| CC_IIZZ_B (op0); /* set cc's */ | |
| break; | |
| case TSTW: | |
| CC_IIZZ_W (op0); /* set cc's */ | |
| break; | |
| case TSTL: | |
| CC_IIZZ_L (op0); /* set cc's */ | |
| if ((cc == CC_Z) && /* zero result and */ | |
| ((((cpu_idle_mask & VAX_IDLE_ULTOLD) && /* running Old Ultrix or friends? */ | |
| (PSL_GETIPL (PSL) == 0x1)) || /* at IPL 1? */ | |
| ((cpu_idle_mask & VAX_IDLE_QUAD) && /* running Quasijarus or friends? */ | |
| (PSL_GETIPL (PSL) == 0x0))) && /* at IPL 0? */ | |
| (fault_PC & 0x80000000) && /* in system space? */ | |
| ((PC - fault_PC) == 6) && /* 6 byte instruction? */ | |
| ((fault_PC & 0x7fffffff) < 0x4000))) /* in low system space? */ | |
| cpu_idle(); /* idle loop */ | |
| break; | |
| /* Single operand instructions with source, read/write - op src.mx | |
| opnd[0] = operand | |
| spec = reg/mem flag | |
| rn = register number | |
| va = operand address | |
| */ | |
| case INCB: | |
| r = (op0 + 1) & BMASK; /* calc result */ | |
| WRITE_B (r); /* store result */ | |
| CC_ADD_B (r, 1, op0); /* set cc's */ | |
| break; | |
| case INCW: | |
| r = (op0 + 1) & WMASK; /* calc result */ | |
| WRITE_W (r); /* store result */ | |
| CC_ADD_W (r, 1, op0); /* set cc's */ | |
| break; | |
| case INCL: | |
| r = (op0 + 1) & LMASK; /* calc result */ | |
| WRITE_L (r); /* store result */ | |
| CC_ADD_L (r, 1, op0); /* set cc's */ | |
| break; | |
| case DECB: | |
| r = (op0 - 1) & BMASK; /* calc result */ | |
| WRITE_B (r); /* store result */ | |
| CC_SUB_B (r, 1, op0); /* set cc's */ | |
| break; | |
| case DECW: | |
| r = (op0 - 1) & WMASK; /* calc result */ | |
| WRITE_W (r); /* store result */ | |
| CC_SUB_W (r, 1, op0); /* set cc's */ | |
| break; | |
| case DECL: | |
| r = (op0 - 1) & LMASK; /* calc result */ | |
| WRITE_L (r); /* store result */ | |
| CC_SUB_L (r, 1, op0); /* set cc's */ | |
| break; | |
| /* Push instructions - PUSHL src.rl or PUSHAx src.ax | |
| opnd[0] = source | |
| */ | |
| case PUSHL: case PUSHAB: case PUSHAW: case PUSHAL: case PUSHAQ: | |
| Write (SP - 4, op0, L_LONG, WA); /* push operand */ | |
| SP = SP - 4; /* decr stack ptr */ | |
| CC_IIZP_L (op0); /* set cc's */ | |
| break; | |
| /* Moves, converts, and ADAWI - op src.rx, dst.wx | |
| opnd[0] = source | |
| spec = reg/mem flag | |
| rn = register number | |
| va = operand address | |
| */ | |
| case MOVB: | |
| r = op0; | |
| WRITE_B (r); /* result */ | |
| CC_IIZP_B (r); /* set cc's */ | |
| break; | |
| case MOVW: case MOVZBW: | |
| r = op0; | |
| WRITE_W (r); /* result */ | |
| CC_IIZP_W (r); /* set cc's */ | |
| break; | |
| case MOVL: case MOVZBL: case MOVZWL: | |
| case MOVAB: case MOVAW: case MOVAL: case MOVAQ: | |
| r = op0; | |
| WRITE_L (r); /* result */ | |
| CC_IIZP_L (r); /* set cc's */ | |
| break; | |
| case MCOMB: | |
| r = op0 ^ BMASK; /* compl opnd */ | |
| WRITE_B (r); /* store result */ | |
| CC_IIZP_B (r); /* set cc's */ | |
| break; | |
| case MCOMW: | |
| r = op0 ^ WMASK; /* compl opnd */ | |
| WRITE_W (r); /* store result */ | |
| CC_IIZP_W (r); /* set cc's */ | |
| break; | |
| case MCOML: | |
| r = op0 ^ LMASK; /* compl opnd */ | |
| WRITE_L (r); /* store result */ | |
| CC_IIZP_L (r); /* set cc's */ | |
| break; | |
| case MNEGB: | |
| r = (-op0) & BMASK; /* negate opnd */ | |
| WRITE_B (r); /* store result */ | |
| CC_SUB_B (r, op0, 0); /* set cc's */ | |
| break; | |
| case MNEGW: | |
| r = (-op0) & WMASK; /* negate opnd */ | |
| WRITE_W (r); /* store result */ | |
| CC_SUB_W (r, op0, 0); /* set cc's */ | |
| break; | |
| case MNEGL: | |
| r = (-op0) & LMASK; /* negate opnd */ | |
| WRITE_L (r); /* store result */ | |
| CC_SUB_L (r, op0, 0); /* set cc's */ | |
| break; | |
| case CVTBW: | |
| r = SXTBW (op0); /* ext sign */ | |
| WRITE_W (r); /* store result */ | |
| CC_IIZZ_W (r); /* set cc's */ | |
| break; | |
| case CVTBL: | |
| r = SXTB (op0); /* ext sign */ | |
| WRITE_L (r); /* store result */ | |
| CC_IIZZ_L (r); /* set cc's */ | |
| break; | |
| case CVTWL: | |
| r = SXTW (op0); /* ext sign */ | |
| WRITE_L (r); /* store result */ | |
| CC_IIZZ_L (r); /* set cc's */ | |
| break; | |
| case CVTLB: | |
| r = op0 & BMASK; /* set result */ | |
| WRITE_B (r); /* store result */ | |
| CC_IIZZ_B (r); /* initial cc's */ | |
| if ((op0 > 127) || (op0 < -128)) { | |
| V_INTOV; | |
| } | |
| break; | |
| case CVTLW: | |
| r = op0 & WMASK; /* set result */ | |
| WRITE_W (r); /* store result */ | |
| CC_IIZZ_W (r); /* initial cc's */ | |
| if ((op0 > 32767) || (op0 < -32768)) { | |
| V_INTOV; | |
| } | |
| break; | |
| case CVTWB: | |
| r = op0 & BMASK; /* set result */ | |
| WRITE_B (r); /* store result */ | |
| CC_IIZZ_B (r); /* initial cc's */ | |
| temp = SXTW (op0); /* cvt op to long */ | |
| if ((temp > 127) || (temp < -128)) { | |
| V_INTOV; | |
| } | |
| break; | |
| case ADAWI: | |
| if (op1 >= 0) temp = R[op1] & WMASK; /* reg? ADDW2 */ | |
| else { | |
| if (op2 & 1) /* mem? chk align */ | |
| RSVD_OPND_FAULT; | |
| temp = Read (op2, L_WORD, WA); /* ok, ADDW2 */ | |
| } | |
| r = (op0 + temp) & WMASK; | |
| WRITE_W (r); | |
| CC_ADD_W (r, op0, temp); /* set cc's */ | |
| break; | |
| /* Integer operates, 2 operand, read only - op src1.rx, src2.rx | |
| opnd[0] = source1 | |
| opnd[1] = source2 | |
| */ | |
| case CMPB: | |
| CC_CMP_B (op0, op1); /* set cc's */ | |
| break; | |
| case CMPW: | |
| CC_CMP_W (op0, op1); /* set cc's */ | |
| break; | |
| case CMPL: | |
| CC_CMP_L (op0, op1); /* set cc's */ | |
| break; | |
| case BITB: | |
| r = op1 & op0; /* calc result */ | |
| CC_IIZP_B (r); /* set cc's */ | |
| break; | |
| case BITW: | |
| r = op1 & op0; /* calc result */ | |
| CC_IIZP_W (r); /* set cc's */ | |
| break; | |
| case BITL: | |
| r = op1 & op0; /* calc result */ | |
| CC_IIZP_L (r); /* set cc's */ | |
| if ((cc == CC_Z) && | |
| (cpu_idle_mask & VAX_IDLE_ULT) && /* running Ultrix or friends? */ | |
| ((PSL & PSL_IS) != 0) && /* on IS? */ | |
| (PSL_GETIPL (PSL) == 0x18) && /* at IPL 18? */ | |
| (fault_PC & 0x80000000) && /* in system space? */ | |
| ((PC - fault_PC) == 8) && /* 8 byte instruction? */ | |
| ((fault_PC & 0x7fffffff) < 0x6000)) /* in low system space? */ | |
| cpu_idle(); /* idle loop */ | |
| break; | |
| /* Integer operates, 2 operand read/write, and 3 operand, also MOVQ | |
| op2 src.rx, dst.mx op3 src.rx, src.rx, dst.wx | |
| opnd[0] = source1 | |
| opnd[1] = source2 | |
| spec = register/memory flag | |
| rn = register number | |
| va = memory address | |
| */ | |
| case ADDB2: case ADDB3: | |
| r = (op1 + op0) & BMASK; /* calc result */ | |
| WRITE_B (r); /* store result */ | |
| CC_ADD_B (r, op0, op1); /* set cc's */ | |
| break; | |
| case ADDW2: case ADDW3: | |
| r = (op1 + op0) & WMASK; /* calc result */ | |
| WRITE_W (r); /* store result */ | |
| CC_ADD_W (r, op0, op1); /* set cc's */ | |
| break; | |
| case ADWC: | |
| r = (op1 + op0 + (cc & CC_C)) & LMASK; /* calc result */ | |
| WRITE_L (r); /* store result */ | |
| CC_ADD_L (r, op0, op1); /* set cc's */ | |
| if ((r == op1) && op0) /* special case */ | |
| cc = cc | CC_C; | |
| break; | |
| case ADDL2: case ADDL3: | |
| r = (op1 + op0) & LMASK; /* calc result */ | |
| WRITE_L (r); /* store result */ | |
| CC_ADD_L (r, op0, op1); /* set cc's */ | |
| break; | |
| case SUBB2: case SUBB3: | |
| r = (op1 - op0) & BMASK; /* calc result */ | |
| WRITE_B (r); /* store result */ | |
| CC_SUB_B (r, op0, op1); /* set cc's */ | |
| break; | |
| case SUBW2: case SUBW3: | |
| r = (op1 - op0) & WMASK; /* calc result */ | |
| WRITE_W (r); /* store result */ | |
| CC_SUB_W (r, op0, op1); /* set cc's */ | |
| break; | |
| case SBWC: | |
| r = (op1 - op0 - (cc & CC_C)) & LMASK; /* calc result */ | |
| WRITE_L (r); /* store result */ | |
| CC_SUB_L (r, op0, op1); /* set cc's */ | |
| if ((op0 == op1) && r) /* special case */ | |
| cc = cc | CC_C; | |
| break; | |
| case SUBL2: case SUBL3: | |
| r = (op1 - op0) & LMASK; /* calc result */ | |
| WRITE_L (r); /* store result */ | |
| CC_SUB_L (r, op0, op1); /* set cc's */ | |
| break; | |
| case MULB2: case MULB3: | |
| temp = SXTB (op0) * SXTB (op1); /* multiply */ | |
| r = temp & BMASK; /* mask to result */ | |
| WRITE_B (r); /* store result */ | |
| CC_IIZZ_B (r); /* set cc's */ | |
| if ((temp > 127) || (temp < -128)) { | |
| V_INTOV; | |
| } | |
| break; | |
| case MULW2: case MULW3: | |
| temp = SXTW (op0) * SXTW (op1); /* multiply */ | |
| r = temp & WMASK; /* mask to result */ | |
| WRITE_W (r); /* store result */ | |
| CC_IIZZ_W (r); /* set cc's */ | |
| if ((temp > 32767) || (temp < -32768)) { | |
| V_INTOV; | |
| } | |
| break; | |
| case MULL2: case MULL3: | |
| r = op_emul (op0, op1, &rh); /* get 64b result */ | |
| WRITE_L (r); /* store result */ | |
| CC_IIZZ_L (r); /* set cc's */ | |
| if (rh != ((r & LSIGN)? -1: 0)) { /* chk overflow */ | |
| V_INTOV; | |
| } | |
| break; | |
| case DIVB2: case DIVB3: | |
| if (op0 == 0) { /* div by zero? */ | |
| r = op1; | |
| temp = CC_V; | |
| SET_TRAP (TRAP_DIVZRO); | |
| } | |
| else if ((op0 == BMASK) && (op1 == BSIGN)) { /* overflow? */ | |
| r = op1; | |
| temp = CC_V; | |
| INTOV; | |
| } | |
| else { | |
| r = SXTB (op1) / SXTB (op0); /* ok, divide */ | |
| temp = 0; | |
| } | |
| r = r & BMASK; /* mask to result */ | |
| WRITE_B (r); /* write result */ | |
| CC_IIZZ_B (r); /* set cc's */ | |
| cc = cc | temp; /* error? set V */ | |
| break; | |
| case DIVW2: case DIVW3: | |
| if (op0 == 0) { /* div by zero? */ | |
| r = op1; | |
| temp = CC_V; | |
| SET_TRAP (TRAP_DIVZRO); | |
| } | |
| else if ((op0 == WMASK) && (op1 == WSIGN)) { /* overflow? */ | |
| r = op1; | |
| temp = CC_V; | |
| INTOV; | |
| } | |
| else { | |
| r = SXTW (op1) / SXTW (op0); /* ok, divide */ | |
| temp = 0; | |
| } | |
| r = r & WMASK; /* mask to result */ | |
| WRITE_W (r); /* write result */ | |
| CC_IIZZ_W (r); /* set cc's */ | |
| cc = cc | temp; /* error? set V */ | |
| break; | |
| case DIVL2: case DIVL3: | |
| if (op0 == 0) { /* div by zero? */ | |
| r = op1; | |
| temp = CC_V; | |
| SET_TRAP (TRAP_DIVZRO); | |
| } | |
| else if ((((uint32)op0) == LMASK) && | |
| (((uint32)op1) == LSIGN)) { /* overflow? */ | |
| r = op1; | |
| temp = CC_V; | |
| INTOV; | |
| } | |
| else { | |
| r = op1 / op0; /* ok, divide */ | |
| temp = 0; | |
| } | |
| r = r & LMASK; /* mask to result */ | |
| WRITE_L (r); /* write result */ | |
| CC_IIZZ_L (r); /* set cc's */ | |
| cc = cc | temp; /* error? set V */ | |
| break; | |
| case BISB2: case BISB3: | |
| r = op1 | op0; /* calc result */ | |
| WRITE_B (r); /* store result */ | |
| CC_IIZP_B (r); /* set cc's */ | |
| break; | |
| case BISW2: case BISW3: | |
| r = op1 | op0; /* calc result */ | |
| WRITE_W (r); /* store result */ | |
| CC_IIZP_W (r); /* set cc's */ | |
| break; | |
| case BISL2: case BISL3: | |
| r = op1 | op0; /* calc result */ | |
| WRITE_L (r); /* store result */ | |
| CC_IIZP_L (r); /* set cc's */ | |
| break; | |
| case BICB2: case BICB3: | |
| r = op1 & ~op0; /* calc result */ | |
| WRITE_B (r); /* store result */ | |
| CC_IIZP_B (r); /* set cc's */ | |
| break; | |
| case BICW2: case BICW3: | |
| r = op1 & ~op0; /* calc result */ | |
| WRITE_W (r); /* store result */ | |
| CC_IIZP_W (r); /* set cc's */ | |
| break; | |
| case BICL2: case BICL3: | |
| r = op1 & ~op0; /* calc result */ | |
| WRITE_L (r); /* store result */ | |
| CC_IIZP_L (r); /* set cc's */ | |
| break; | |
| case XORB2: case XORB3: | |
| r = op1 ^ op0; /* calc result */ | |
| WRITE_B (r); /* store result */ | |
| CC_IIZP_B (r); /* set cc's */ | |
| break; | |
| case XORW2: case XORW3: | |
| r = op1 ^ op0; /* calc result */ | |
| WRITE_W (r); /* store result */ | |
| CC_IIZP_W (r); /* set cc's */ | |
| break; | |
| case XORL2: case XORL3: | |
| r = op1 ^ op0; /* calc result */ | |
| WRITE_L (r); /* store result */ | |
| CC_IIZP_L (r); /* set cc's */ | |
| break; | |
| /* MOVQ - movq src.rq, dst.wq | |
| opnd[0:1] = source | |
| spec = register/memory flag | |
| rn = register number | |
| va = memory address | |
| */ | |
| case MOVQ: | |
| WRITE_Q (op0, op1); /* store result */ | |
| CC_IIZP_Q (op0, op1); | |
| break; | |
| /* Shifts - op shf.rb,src.rl,dst.wl | |
| opnd[0] = shift count | |
| opnd[1] = source | |
| spec = register/memory flag | |
| rn = register number | |
| va = memory address | |
| */ | |
| case ROTL: | |
| j = op0 % 32; /* reduce sc, mod 32 */ | |
| if (j) | |
| r = ((((uint32) op1) << j) | (((uint32) op1) >> (32 - j))) & LMASK; | |
| else r = op1; | |
| WRITE_L (r); /* store result */ | |
| CC_IIZP_L (r); /* set cc's */ | |
| break; | |
| case ASHL: | |
| if (op0 & BSIGN) { /* right shift? */ | |
| temp = 0x100 - op0; /* get |shift| */ | |
| if (temp > 31) /* sc > 31? */ | |
| r = (op1 & LSIGN)? LMASK: 0; | |
| else r = op1 >> temp; /* shift */ | |
| WRITE_L (r); /* store result */ | |
| CC_IIZZ_L (r); /* set cc's */ | |
| break; | |
| } | |
| else { | |
| if (op0 > 31) /* sc > 31? */ | |
| r = temp = 0; | |
| else { | |
| r = (((uint32) op1) << op0) & LMASK; /* shift */ | |
| temp = r >> op0; /* shift back */ | |
| } | |
| WRITE_L (r); /* store result */ | |
| CC_IIZZ_L (r); /* set cc's */ | |
| if (op1 != temp) { /* bits lost? */ | |
| V_INTOV; | |
| } | |
| } | |
| break; | |
| case ASHQ: | |
| r = op_ashq (opnd, &rh, &flg); /* do qw shift */ | |
| WRITE_Q (r, rh); /* store results */ | |
| CC_IIZZ_Q (r, rh); /* set cc's */ | |
| if (flg) { /* if ovflo, set */ | |
| V_INTOV; | |
| } | |
| break; | |
| /* EMUL - emul mplr.rl,mpcn.rl,add.rl,dst.wq | |
| op0 = multiplier | |
| op1 = multiplicand | |
| op2 = adder | |
| op3:op4 = destination (.wq) | |
| */ | |
| case EMUL: | |
| r = op_emul (op0, op1, &rh); /* calc 64b result */ | |
| r = r + op2; /* add 32b value */ | |
| rh = rh + (((uint32) r) < ((uint32) op2)) - /* into 64b result */ | |
| ((op2 & LSIGN)? 1: 0); | |
| WRITE_Q (r, rh); /* write result */ | |
| CC_IIZZ_Q (r, rh); /* set cc's */ | |
| break; | |
| /* EDIV - ediv dvr.rl,dvd.rq,quo.wl,rem.wl | |
| op0 = divisor (.rl) | |
| op1:op2 = dividend (.rq) | |
| op3:op4 = quotient address (.wl) | |
| op5:op6 = remainder address (.wl) | |
| */ | |
| case EDIV: | |
| if (op5 < 0) /* wtest remainder */ | |
| Read (op6, L_LONG, WA); | |
| if (op0 == 0) { /* divide by zero? */ | |
| flg = CC_V; /* set V */ | |
| r = opnd[1]; /* quo = low divd */ | |
| rh = 0; /* rem = 0 */ | |
| SET_TRAP (TRAP_DIVZRO); /* set trap */ | |
| } | |
| else { | |
| r = op_ediv (opnd, &rh, &flg); /* extended divide */ | |
| if (flg) { /* if ovf+IV, set trap */ | |
| INTOV; | |
| } | |
| } | |
| if (op3 >= 0) /* store quotient */ | |
| R[op3] = r; | |
| else Write (op4, r, L_LONG, WA); | |
| if (op5 >= 0) /* store remainder */ | |
| R[op5] = rh; | |
| else Write (op6, rh, L_LONG, WA); | |
| CC_IIZZ_L (r); /* set cc's */ | |
| cc = cc | flg; /* set V if required */ | |
| break; | |
| /* Control instructions */ | |
| /* Simple branches and subroutine calls */ | |
| case BRB: | |
| BRANCHB (brdisp); /* branch */ | |
| break; | |
| case BRW: | |
| BRANCHW (brdisp); /* branch */ | |
| break; | |
| case BSBB: | |
| Write (SP - 4, PC, L_LONG, WA); /* push PC on stk */ | |
| SP = SP - 4; /* decr stk ptr */ | |
| BRANCHB (brdisp); /* branch */ | |
| if (sim_switches & SWMASK ('R')) | |
| ++step_out_nest_level; | |
| break; | |
| case BSBW: | |
| Write (SP - 4, PC, L_LONG, WA); /* push PC on stk */ | |
| SP = SP - 4; /* decr stk ptr */ | |
| BRANCHW (brdisp); /* branch */ | |
| if (sim_switches & SWMASK ('R')) | |
| ++step_out_nest_level; | |
| break; | |
| case BGEQ: | |
| if (!(cc & CC_N)) /* br if N = 0 */ | |
| BRANCHB (brdisp); | |
| break; | |
| case BLSS: | |
| if (cc & CC_N) /* br if N = 1 */ | |
| BRANCHB (brdisp); | |
| break; | |
| case BNEQ: | |
| if (!(cc & CC_Z)) /* br if Z = 0 */ | |
| BRANCHB (brdisp); | |
| break; | |
| case BEQL: | |
| if (cc & CC_Z) { /* br if Z = 1 */ | |
| BRANCHB (brdisp); | |
| if ((((PSL & PSL_IS) != 0) && /* on IS? */ | |
| (PSL_GETIPL (PSL) == 0x1F) && /* at IPL 31 */ | |
| (mapen == 0) && /* Running from ROM */ | |
| (fault_PC == 0x2004361B)) || /* Boot ROM Character Prompt */ | |
| ((cpu_idle_mask & VAX_IDLE_ELN) && /* VAXELN Idle? */ | |
| (PSL & PSL_IS) && /* on IS? */ | |
| (brdisp == 0xFA) && /* Branch to prior TSTL */ | |
| (PSL_GETIPL (PSL) == 0x4))) /* at IPL 4 */ | |
| cpu_idle(); | |
| } | |
| break; | |
| case BVC: | |
| if (!(cc & CC_V)) /* br if V = 0 */ | |
| BRANCHB (brdisp); | |
| break; | |
| case BVS: | |
| if (cc & CC_V) /* br if V = 1 */ | |
| BRANCHB (brdisp); | |
| break; | |
| case BGEQU: | |
| if (!(cc & CC_C)) /* br if C = 0 */ | |
| BRANCHB (brdisp); | |
| break; | |
| case BLSSU: | |
| if (cc & CC_C) /* br if C = 1 */ | |
| BRANCHB (brdisp); | |
| break; | |
| case BGTR: | |
| if (!(cc & (CC_N | CC_Z))) /* br if N | Z = 0 */ | |
| BRANCHB (brdisp); | |
| break; | |
| case BLEQ: | |
| if (cc & (CC_N | CC_Z)) /* br if N | Z = 1 */ | |
| BRANCHB (brdisp); | |
| break; | |
| case BGTRU: | |
| if (!(cc & (CC_C | CC_Z))) /* br if C | Z = 0 */ | |
| BRANCHB (brdisp); | |
| break; | |
| case BLEQU: | |
| if (cc & (CC_C | CC_Z)) /* br if C | Z = 1 */ | |
| BRANCHB (brdisp); | |
| break; | |
| /* Simple jumps and subroutine calls - op addr.ab | |
| opnd[0] = address | |
| */ | |
| case JSB: | |
| Write (SP - 4, PC, L_LONG, WA); /* push PC on stk */ | |
| SP = SP - 4; /* decr stk ptr */ | |
| if (sim_switches & SWMASK ('R')) | |
| ++step_out_nest_level; | |
| case JMP: | |
| JUMP (op0); /* jump */ | |
| break; | |
| case RSB: | |
| temp = Read (SP, L_LONG, RA); /* get top of stk */ | |
| SP = SP + 4; /* incr stk ptr */ | |
| JUMP (temp); | |
| if (sim_switches & SWMASK ('R')) { | |
| if (step_out_nest_level <= 0) | |
| ABORT (SCPE_STEP); | |
| else | |
| --step_out_nest_level; | |
| } | |
| break; | |
| /* SOB instructions - op idx.ml,disp.bb | |
| opnd[0] = index | |
| spec = register/memory flag | |
| rn = register number | |
| va = memory address | |
| */ | |
| case SOBGEQ: | |
| r = op0 - 1; /* decr index */ | |
| WRITE_L (r); /* store result */ | |
| CC_IIZP_L (r); /* set cc's */ | |
| V_SUB_L (r, 1, op0); /* test for ovflo */ | |
| if (r >= 0) /* if >= 0, branch */ | |
| BRANCHB_ALWAYS (brdisp); | |
| break; | |
| case SOBGTR: | |
| r = op0 - 1; /* decr index */ | |
| WRITE_L (r); /* store result */ | |
| CC_IIZP_L (r); /* set cc's */ | |
| V_SUB_L (r, 1, op0); /* test for ovflo */ | |
| if (r > 0) /* if >= 0, branch */ | |
| BRANCHB_ALWAYS (brdisp); | |
| break; | |
| /* AOB instructions - op limit.rl,idx.ml,disp.bb | |
| opnd[0] = limit | |
| opnd[1] = index | |
| spec = register/memory flag | |
| rn = register number | |
| va = memory address | |
| */ | |
| case AOBLSS: | |
| r = op1 + 1; /* incr index */ | |
| WRITE_L (r); /* store result */ | |
| CC_IIZP_L (r); /* set cc's */ | |
| V_ADD_L (r, 1, op1); /* test for ovflo */ | |
| if (r < op0) /* if < lim, branch */ | |
| BRANCHB_ALWAYS (brdisp); | |
| break; | |
| case AOBLEQ: | |
| r = op1 + 1; /* incr index */ | |
| WRITE_L (r); /* store result */ | |
| CC_IIZP_L (r); /* set cc's */ | |
| V_ADD_L (r, 1, op1); /* test for ovflo */ | |
| if (r <= op0) /* if < lim, branch */ | |
| BRANCHB_ALWAYS (brdisp); | |
| break; | |
| /* ACB instructions - op limit.rx,add.rx,index.mx,disp.bw | |
| opnd[0] = limit | |
| opnd[1] = adder | |
| opnd[2] = index | |
| spec = register/memory flag | |
| rn = register number | |
| va = memory address | |
| */ | |
| case ACBB: | |
| r = (op2 + op1) & BMASK; /* calc result */ | |
| WRITE_B (r); /* store result */ | |
| CC_IIZP_B (r); /* set cc's */ | |
| V_ADD_B (r, op1, op2); /* test for ovflo */ | |
| if ((op1 & BSIGN)? (SXTB (r) >= SXTB (op0)): (SXTB (r) <= SXTB (op0))) | |
| BRANCHW_ALWAYS (brdisp); | |
| break; | |
| case ACBW: | |
| r = (op2 + op1) & WMASK; /* calc result */ | |
| WRITE_W (r); /* store result */ | |
| CC_IIZP_W (r); /* set cc's */ | |
| V_ADD_W (r, op1, op2); /* test for ovflo */ | |
| if ((op1 & WSIGN)? (SXTW (r) >= SXTW (op0)): (SXTW (r) <= SXTW (op0))) | |
| BRANCHW_ALWAYS (brdisp); | |
| break; | |
| case ACBL: | |
| r = (op2 + op1) & LMASK; /* calc result */ | |
| WRITE_L (r); /* store result */ | |
| CC_IIZP_L (r); /* set cc's */ | |
| V_ADD_L (r, op1, op2); /* test for ovflo */ | |
| if ((op1 & LSIGN)? (r >= op0): (r <= op0)) | |
| BRANCHW_ALWAYS (brdisp); | |
| break; | |
| /* CASE instructions - casex sel.rx,base.rx,lim.rx | |
| opnd[0] = selector | |
| opnd[1] = base | |
| opnd[2] = limit | |
| */ | |
| case CASEB: | |
| r = (op0 - op1) & BMASK; /* sel - base */ | |
| CC_CMP_B (r, op2); /* r:limit, set cc's */ | |
| if (r > op2) /* r > limit (unsgnd)? */ | |
| JUMP (PC + ((op2 + 1) * 2)); | |
| else { | |
| temp = Read (PC + (r * 2), L_WORD, RA); | |
| BRANCHW (temp); | |
| } | |
| break; | |
| case CASEW: | |
| r = (op0 - op1) & WMASK; /* sel - base */ | |
| CC_CMP_W (r, op2); /* r:limit, set cc's */ | |
| if (r > op2) /* r > limit (unsgnd)? */ | |
| JUMP (PC + ((op2 + 1) * 2)); | |
| else { | |
| temp = Read (PC + (r * 2), L_WORD, RA); | |
| BRANCHW (temp); | |
| } | |
| break; | |
| case CASEL: | |
| r = (op0 - op1) & LMASK; /* sel - base */ | |
| CC_CMP_L (r, op2); /* r:limit, set cc's */ | |
| if (((uint32) r) > ((uint32) op2)) /* r > limit (unsgnd)? */ | |
| JUMP (PC + ((op2 + 1) * 2)); | |
| else { | |
| temp = Read (PC + (r * 2), L_WORD, RA); | |
| BRANCHW (temp); | |
| } | |
| break; | |
| /* Branch on bit instructions - bbxy pos.rl,op.wb,disp.bb | |
| opnd[0] = position | |
| opnd[1] = register number/memory flag | |
| opnd[2] = memory address, if memory | |
| */ | |
| case BBS: | |
| if (op_bb_n (opnd, acc)) { /* br if bit set */ | |
| BRANCHB_ALWAYS (brdisp); | |
| if (((PSL & PSL_IS) != 0) && /* on IS? */ | |
| (PSL_GETIPL (PSL) == 0x3) && /* at IPL 3? */ | |
| ((cpu_idle_mask & VAX_IDLE_VMS) != 0)) /* running VMS? */ | |
| cpu_idle (); /* idle loop */ | |
| } | |
| break; | |
| case BBC: | |
| if (!op_bb_n (opnd, acc)) /* br if bit clr */ | |
| BRANCHB_ALWAYS (brdisp); | |
| break; | |
| case BBSS: case BBSSI: | |
| if (op_bb_x (opnd, 1, acc)) /* br if set, set */ | |
| BRANCHB (brdisp); | |
| break; | |
| case BBCC: case BBCCI: | |
| if (!op_bb_x (opnd, 0, acc)) /* br if clr, clr*/ | |
| BRANCHB (brdisp); | |
| break; | |
| case BBSC: | |
| if (op_bb_x (opnd, 0, acc)) /* br if clr, set */ | |
| BRANCHB_ALWAYS (brdisp); | |
| break; | |
| case BBCS: | |
| if (!op_bb_x (opnd, 1, acc)) /* br if set, clr */ | |
| BRANCHB_ALWAYS (brdisp); | |
| break; | |
| case BLBS: | |
| if (op0 & 1) /* br if bit set */ | |
| BRANCHB (brdisp); | |
| break; | |
| case BLBC: | |
| if ((op0 & 1) == 0) /* br if bit clear */ | |
| BRANCHB (brdisp); | |
| break; | |
| /* Extract field instructions - ext?v pos.rl,size.rb,base.wb,dst.wl | |
| opnd[0] = position | |
| opnd[1] = size | |
| opnd[2] = register number/memory flag | |
| opnd[3] = register content/memory address | |
| spec = register/memory flag | |
| rn = register number | |
| va = memory address | |
| */ | |
| case EXTV: | |
| r = op_extv (opnd, vfldrp1, acc); /* get field */ | |
| if (r & byte_sign[op1]) | |
| r = r | ~byte_mask[op1]; | |
| WRITE_L (r); /* store field */ | |
| CC_IIZP_L (r); /* set cc's */ | |
| break; | |
| case EXTZV: | |
| r = op_extv (opnd, vfldrp1, acc); /* get field */ | |
| WRITE_L (r); /* store field */ | |
| CC_IIZP_L (r); /* set cc's */ | |
| break; | |
| /* Compare field instructions - cmp?v pos.rl,size.rb,base.wb,src2.rl | |
| opnd[0] = position | |
| opnd[1] = size | |
| opnd[2] = register number/memory flag | |
| opnd[3] = register content/memory address | |
| opnd[4] = source2 | |
| */ | |
| case CMPV: | |
| r = op_extv (opnd, vfldrp1, acc); /* get field */ | |
| if (r & byte_sign[op1]) | |
| r = r | ~byte_mask[op1]; | |
| CC_CMP_L (r, op4); /* set cc's */ | |
| break; | |
| case CMPZV: | |
| r = op_extv (opnd, vfldrp1, acc); /* get field */ | |
| CC_CMP_L (r, op4); /* set cc's */ | |
| break; | |
| /* Find first field instructions - ff? pos.rl,size.rb,base.wb,dst.wl | |
| opnd[0] = position | |
| opnd[1] = size | |
| opnd[2] = register number/memory flag | |
| opnd[3] = register content/memory address | |
| spec = register/memory flag | |
| rn = register number | |
| va = memory address | |
| */ | |
| case FFS: | |
| r = op_extv (opnd, vfldrp1, acc); /* get field */ | |
| temp = op_ffs (r, op1); /* find first 1 */ | |
| WRITE_L (op0 + temp); /* store result */ | |
| cc = r? 0: CC_Z; /* set cc's */ | |
| if ((cc == CC_Z) && /* No set bits found? */ | |
| (cpu_idle_mask & VAX_IDLE_ULT1X) && /* running Ultrix 1.X" */ | |
| (PSL_GETIPL (PSL) == 0x0) && /* at IPL 0? */ | |
| (fault_PC & 0x80000000) && /* in system space? */ | |
| ((fault_PC & 0x7fffffff) < 0x3000)) /* in low system space? */ | |
| cpu_idle(); /* idle loop */ | |
| break; | |
| case FFC: | |
| r = op_extv (opnd, vfldrp1, acc); /* get field */ | |
| r = r ^ byte_mask[op1]; /* invert bits */ | |
| temp = op_ffs (r, op1); /* find first 1 */ | |
| WRITE_L (op0 + temp); /* store result */ | |
| cc = r? 0: CC_Z; /* set cc's */ | |
| break; | |
| /* Insert field instruction - insv src.rl,pos.rb,size.rl,base.wb | |
| opnd[0] = source | |
| opnd[1] = position | |
| opnd[2] = size | |
| opnd[3] = register number/memory flag | |
| opnd[4] = register content/memory address | |
| */ | |
| case INSV: | |
| op_insv (opnd, vfldrp1, acc); /* insert field */ | |
| break; | |
| /* Call and return - call? arg.rx,proc.ab | |
| opnd[0] = argument | |
| opnd[1] = procedure address | |
| */ | |
| case CALLS: | |
| cc = op_call (opnd, TRUE, acc); | |
| if (sim_switches & SWMASK ('R')) | |
| ++step_out_nest_level; | |
| break; | |
| case CALLG: | |
| cc = op_call (opnd, FALSE, acc); | |
| if (sim_switches & SWMASK ('R')) | |
| ++step_out_nest_level; | |
| break; | |
| case RET: | |
| cc = op_ret (acc); | |
| if (sim_switches & SWMASK ('R')) { | |
| if (step_out_nest_level <= 0) | |
| ABORT (SCPE_STEP); | |
| else | |
| --step_out_nest_level; | |
| } | |
| break; | |
| /* Miscellaneous instructions */ | |
| case HALT: | |
| if (PSL & PSL_CUR) /* not kern? rsvd inst */ | |
| RSVD_INST_FAULT; | |
| else if (cpu_unit.flags & UNIT_CONH) /* halt to console? */ | |
| cc = con_halt (CON_HLTINS, cc); /* enter firmware */ | |
| else { | |
| /* allow potentially pending I/O (console output, | |
| or other devices) to complete before dropping | |
| back to scp */ | |
| while ((sim_clock_queue != QUEUE_LIST_END) && | |
| ((sim_clock_queue->flags & UNIT_IDLE) == 0)) { | |
| sim_interval = 0; | |
| temp = sim_process_event (); | |
| if (temp) | |
| ABORT (temp); | |
| SET_IRQL; /* update interrupts */ | |
| } | |
| ABORT (STOP_HALT); /* halt to simulator */ | |
| } | |
| case NOP: | |
| break; | |
| case BPT: | |
| SETPC (fault_PC); | |
| PSL = PSL & ~PSL_TP; /* clear <tp> */ | |
| cc = intexc (SCB_BPT, cc, 0, IE_EXC); | |
| GET_CUR; | |
| break; | |
| case XFC: | |
| SETPC (fault_PC); | |
| PSL = PSL & ~PSL_TP; /* clear <tp> */ | |
| cc = intexc (SCB_XFC, cc, 0, IE_EXC); | |
| GET_CUR; | |
| break; | |
| case BISPSW: | |
| if (opnd[0] & PSW_MBZ) | |
| RSVD_OPND_FAULT; | |
| PSL = PSL | (opnd[0] & ~CC_MASK); | |
| cc = cc | (opnd[0] & CC_MASK); | |
| break; | |
| case BICPSW: | |
| if (opnd[0] & PSW_MBZ) | |
| RSVD_OPND_FAULT; | |
| PSL = PSL & ~opnd[0]; | |
| cc = cc & ~opnd[0]; | |
| break; | |
| case MOVPSL: | |
| r = PSL | cc; | |
| WRITE_L (r); | |
| break; | |
| case PUSHR: | |
| op_pushr (opnd, acc); | |
| break; | |
| case POPR: | |
| op_popr (opnd, acc); | |
| break; | |
| case INDEX: | |
| if ((op0 < op1) || (op0 > op2)) | |
| SET_TRAP (TRAP_SUBSCR); | |
| r = (op0 + op4) * op3; | |
| WRITE_L (r); | |
| CC_IIZZ_L (r); | |
| break; | |
| /* Queue and interlocked queue */ | |
| case INSQUE: | |
| cc = op_insque (opnd, acc); | |
| break; | |
| case REMQUE: | |
| cc = op_remque (opnd, acc); | |
| break; | |
| case INSQHI: | |
| cc = op_insqhi (opnd, acc); | |
| break; | |
| case INSQTI: | |
| cc = op_insqti (opnd, acc); | |
| break; | |
| case REMQHI: | |
| cc = op_remqhi (opnd, acc); | |
| break; | |
| case REMQTI: | |
| cc = op_remqti (opnd, acc); | |
| break; | |
| /* String instructions */ | |
| case MOVC3: case MOVC5: | |
| cc = op_movc (opnd, opc & 4, acc); | |
| break; | |
| case CMPC3: case CMPC5: | |
| cc = op_cmpc (opnd, opc & 4, acc); | |
| break; | |
| case LOCC: case SKPC: | |
| cc = op_locskp (opnd, opc & 1, acc); | |
| break; | |
| case SCANC: case SPANC: | |
| cc = op_scnspn (opnd, opc & 1, acc); | |
| break; | |
| /* Floating point instructions */ | |
| case TSTF: case TSTD: | |
| r = op_movfd (op0); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case TSTG: | |
| r = op_movg (op0); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case MOVF: | |
| r = op_movfd (op0); | |
| WRITE_L (r); | |
| CC_IIZP_FP (r); | |
| break; | |
| case MOVD: | |
| if ((r = op_movfd (op0)) == 0) | |
| op1 = 0; | |
| WRITE_Q (r, op1); | |
| CC_IIZP_FP (r); | |
| break; | |
| case MOVG: | |
| if ((r = op_movg (op0)) == 0) | |
| op1 = 0; | |
| WRITE_Q (r, op1); | |
| CC_IIZP_FP (r); | |
| break; | |
| case MNEGF: | |
| r = op_mnegfd (op0); | |
| WRITE_L (r); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case MNEGD: | |
| if ((r = op_mnegfd (op0)) == 0) | |
| op1 = 0; | |
| WRITE_Q (r, op1); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case MNEGG: | |
| if ((r = op_mnegg (op0)) == 0) | |
| op1 = 0; | |
| WRITE_Q (r, op1); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case CMPF: | |
| cc = op_cmpfd (op0, 0, op1, 0); | |
| break; | |
| case CMPD: | |
| cc = op_cmpfd (op0, op1, op2, op3); | |
| break; | |
| case CMPG: | |
| cc = op_cmpg (op0, op1, op2, op3); | |
| break; | |
| case CVTBF: | |
| r = op_cvtifdg (SXTB (op0), NULL, opc); | |
| WRITE_L (r); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case CVTWF: | |
| r = op_cvtifdg (SXTW (op0), NULL, opc); | |
| WRITE_L (r); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case CVTLF: | |
| r = op_cvtifdg (op0, NULL, opc); | |
| WRITE_L (r); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case CVTBD: case CVTBG: | |
| r = op_cvtifdg (SXTB (op0), &rh, opc); | |
| WRITE_Q (r, rh); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case CVTWD: case CVTWG: | |
| r = op_cvtifdg (SXTW (op0), &rh, opc); | |
| WRITE_Q (r, rh); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case CVTLD: case CVTLG: | |
| r = op_cvtifdg (op0, &rh, opc); | |
| WRITE_Q (r, rh); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case CVTFB: case CVTDB: case CVTGB: | |
| r = op_cvtfdgi (opnd, &flg, opc) & BMASK; | |
| WRITE_B (r); | |
| CC_IIZZ_B (r); | |
| if (flg) { | |
| V_INTOV; | |
| } | |
| break; | |
| case CVTFW: case CVTDW: case CVTGW: | |
| r = op_cvtfdgi (opnd, &flg, opc) & WMASK; | |
| WRITE_W (r); | |
| CC_IIZZ_W (r); | |
| if (flg) { | |
| V_INTOV; | |
| } | |
| break; | |
| case CVTFL: case CVTDL: case CVTGL: | |
| case CVTRFL: case CVTRDL: case CVTRGL: | |
| r = op_cvtfdgi (opnd, &flg, opc) & LMASK; | |
| WRITE_L (r); | |
| CC_IIZZ_L (r); | |
| if (flg) { | |
| V_INTOV; | |
| } | |
| break; | |
| case CVTFD: | |
| r = op_movfd (op0); | |
| WRITE_Q (r, 0); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case CVTDF: | |
| r = op_cvtdf (opnd); | |
| WRITE_L (r); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case CVTFG: | |
| r = op_cvtfg (opnd, &rh); | |
| WRITE_Q (r, rh); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case CVTGF: | |
| r = op_cvtgf (opnd); | |
| WRITE_L (r); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case ADDF2: case ADDF3: | |
| r = op_addf (opnd, FALSE); | |
| WRITE_L (r); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case ADDD2: case ADDD3: | |
| r = op_addd (opnd, &rh, FALSE); | |
| WRITE_Q (r, rh); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case ADDG2: case ADDG3: | |
| r = op_addg (opnd, &rh, FALSE); | |
| WRITE_Q (r, rh); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case SUBF2: case SUBF3: | |
| r = op_addf (opnd, TRUE); | |
| WRITE_L (r); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case SUBD2: case SUBD3: | |
| r = op_addd (opnd, &rh, TRUE); | |
| WRITE_Q (r, rh); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case SUBG2: case SUBG3: | |
| r = op_addg (opnd, &rh, TRUE); | |
| WRITE_Q (r, rh); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case MULF2: case MULF3: | |
| r = op_mulf (opnd); | |
| WRITE_L (r); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case MULD2: case MULD3: | |
| r = op_muld (opnd, &rh); | |
| WRITE_Q (r, rh); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case MULG2: case MULG3: | |
| r = op_mulg (opnd, &rh); | |
| WRITE_Q (r, rh); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case DIVF2: case DIVF3: | |
| r = op_divf (opnd); | |
| WRITE_L (r); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case DIVD2: case DIVD3: | |
| r = op_divd (opnd, &rh); | |
| WRITE_Q (r, rh); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case DIVG2: case DIVG3: | |
| r = op_divg (opnd, &rh); | |
| WRITE_Q (r, rh); | |
| CC_IIZZ_FP (r); | |
| break; | |
| case ACBF: | |
| r = op_addf (opnd + 1, FALSE); /* add + index */ | |
| temp = op_cmpfd (r, 0, op0, 0); /* result : limit */ | |
| WRITE_L (r); /* write result */ | |
| CC_IIZP_FP (r); /* set cc's */ | |
| if ((temp & CC_Z) || ((op1 & FPSIGN)? /* test br cond */ | |
| !(temp & CC_N): (temp & CC_N))) | |
| BRANCHW (brdisp); | |
| break; | |
| case ACBD: | |
| r = op_addd (opnd + 2, &rh, FALSE); | |
| temp = op_cmpfd (r, rh, op0, op1); | |
| WRITE_Q (r, rh); | |
| CC_IIZP_FP (r); | |
| if ((temp & CC_Z) || ((op2 & FPSIGN)? /* test br cond */ | |
| !(temp & CC_N): (temp & CC_N))) | |
| BRANCHW (brdisp); | |
| break; | |
| case ACBG: | |
| r = op_addg (opnd + 2, &rh, FALSE); | |
| temp = op_cmpg (r, rh, op0, op1); | |
| WRITE_Q (r, rh); | |
| CC_IIZP_FP (r); | |
| if ((temp & CC_Z) || ((op2 & FPSIGN)? /* test br cond */ | |
| !(temp & CC_N): (temp & CC_N))) | |
| BRANCHW (brdisp); | |
| break; | |
| /* EMODF | |
| op0 = multiplier | |
| op1 = extension | |
| op2 = multiplicand | |
| op3:op4 = integer destination (int.wl) | |
| op5:op6 = floating destination (flt.wl) | |
| */ | |
| case EMODF: | |
| r = op_emodf (opnd, &temp, &flg); | |
| if (op5 < 0) | |
| Read (op6, L_LONG, WA); | |
| if (op3 >= 0) | |
| R[op3] = temp; | |
| else Write (op4, temp, L_LONG, WA); | |
| WRITE_L (r); | |
| CC_IIZZ_FP (r); | |
| if (flg) { | |
| V_INTOV; | |
| } | |
| break; | |
| /* EMODD, EMODG | |
| op0:op1 = multiplier | |
| op2 = extension | |
| op3:op4 = multiplicand | |
| op5:op6 = integer destination (int.wl) | |
| op7:op8 = floating destination (flt.wq) | |
| */ | |
| case EMODD: | |
| r = op_emodd (opnd, &rh, &temp, &flg); | |
| if (op7 < 0) { | |
| Read (op8, L_BYTE, WA); | |
| Read ((op8 + 7) & LMASK, L_BYTE, WA); | |
| } | |
| if (op5 >= 0) | |
| R[op5] = temp; | |
| else Write (op6, temp, L_LONG, WA); | |
| WRITE_Q (r, rh); | |
| CC_IIZZ_FP (r); | |
| if (flg) { | |
| V_INTOV; | |
| } | |
| break; | |
| case EMODG: | |
| r = op_emodg (opnd, &rh, &temp, &flg); | |
| if (op7 < 0) { | |
| Read (op8, L_BYTE, WA); | |
| Read ((op8 + 7) & LMASK, L_BYTE, WA); | |
| } | |
| if (op5 >= 0) | |
| R[op5] = temp; | |
| else Write (op6, temp, L_LONG, WA); | |
| WRITE_Q (r, rh); | |
| CC_IIZZ_FP (r); | |
| if (flg) { | |
| V_INTOV; | |
| } | |
| break; | |
| /* POLY */ | |
| case POLYF: | |
| op_polyf (opnd, acc); | |
| CC_IIZZ_FP (R[0]); | |
| break; | |
| case POLYD: | |
| op_polyd (opnd, acc); | |
| CC_IIZZ_FP (R[0]); | |
| break; | |
| case POLYG: | |
| op_polyg (opnd, acc); | |
| CC_IIZZ_FP (R[0]); | |
| break; | |
| /* Operating system instructions */ | |
| case CHMK: case CHME: case CHMS: case CHMU: | |
| cc = op_chm (opnd, cc, opc); /* CHMx */ | |
| GET_CUR; /* update cur mode */ | |
| SET_IRQL; /* update intreq */ | |
| break; | |
| case REI: | |
| cc = op_rei (acc); /* REI */ | |
| GET_CUR; /* update cur mode */ | |
| SET_IRQL; /* update intreq */ | |
| break; | |
| case LDPCTX: | |
| op_ldpctx (acc); | |
| break; | |
| case SVPCTX: | |
| op_svpctx (acc); | |
| break; | |
| case PROBER: case PROBEW: | |
| cc = (cc & CC_C) | op_probe (opnd, opc & 1); | |
| break; | |
| case MTPR: | |
| cc = (cc & CC_C) | op_mtpr (opnd); | |
| SET_IRQL; /* update intreq */ | |
| break; | |
| case MFPR: | |
| r = op_mfpr (opnd); | |
| WRITE_L (r); | |
| CC_IIZP_L (r); | |
| break; | |
| /* CIS or emulated instructions */ | |
| case CVTPL: | |
| case MOVP: case CMPP3: case CMPP4: case CVTLP: | |
| case CVTPS: case CVTSP: case CVTTP: case CVTPT: | |
| case ADDP4: case ADDP6: case SUBP4: case SUBP6: | |
| case MULP: case DIVP: case ASHP: case CRC: | |
| case MOVTC: case MOVTUC: case MATCHC: case EDITPC: | |
| cc = op_cis (opnd, cc, opc, acc); | |
| break; | |
| /* Octaword or reserved instructions */ | |
| case PUSHAO: case MOVAO: case CLRO: case MOVO: | |
| case TSTH: case MOVH: case MNEGH: case CMPH: | |
| case CVTBH: case CVTWH: case CVTLH: | |
| case CVTHB: case CVTHW: case CVTHL: case CVTRHL: | |
| case CVTFH: case CVTDH: case CVTGH: | |
| case CVTHF: case CVTHD: case CVTHG: | |
| case ADDH2: case ADDH3: case SUBH2: case SUBH3: | |
| case MULH2: case MULH3: case DIVH2: case DIVH3: | |
| case ACBH: case POLYH: case EMODH: | |
| cc = op_octa (opnd, cc, opc, acc, spec, va); | |
| if (cc & LSIGN) { /* ACBH branch? */ | |
| BRANCHW (brdisp); | |
| cc = cc & CC_MASK; /* mask off flag */ | |
| } | |
| break; | |
| default: | |
| RSVD_INST_FAULT; | |
| break; | |
| } /* end case op */ | |
| } /* end for */ | |
| } /* end sim_instr */ | |
| /* Prefetch buffer routine | |
| Prefetch buffer state | |
| ibufl, ibufh = the prefetch buffer | |
| ibcnt = number of bytes available (0, 4, 8) | |
| ppc = physical PC | |
| The get_istr routines fetches the indicated number of bytes from | |
| the prefetch buffer. Although it is complicated, it is faster | |
| than calling Read on every byte of the instruction stream. | |
| If the prefetch buffer has enough bytes, the required bytes are | |
| extracted from the prefetch buffer and returned. If it does not | |
| have enough bytes, enough prefetch words are fetched until there | |
| are. A longword is only prefetched if data is needed from it, | |
| so any translation errors are real. | |
| */ | |
| static SIM_INLINE int32 get_istr (int32 lnt, int32 acc) | |
| { | |
| int32 bo = PC & 3; | |
| int32 sc, val, t; | |
| while ((bo + lnt) > ibcnt) { /* until enuf bytes */ | |
| if ((ppc < 0) || (VA_GETOFF (ppc) == 0)) { /* PPC inv, xpg? */ | |
| ppc = Test ((PC + ibcnt) & ~03, RD, &t); /* xlate PC */ | |
| if (ppc < 0) | |
| Read ((PC + ibcnt) & ~03, L_LONG, RA); | |
| } | |
| if (ibcnt == 0) /* fill low */ | |
| ibufl = ReadLP (ppc); | |
| else ibufh = ReadLP (ppc); /* or high */ | |
| ppc = ppc + 4; /* incr phys PC */ | |
| ibcnt = ibcnt + 4; /* incr ibuf cnt */ | |
| } | |
| PC = PC + lnt; /* incr PC */ | |
| if (lnt == L_BYTE) /* byte? */ | |
| val = (ibufl >> (bo << 3)) & BMASK; | |
| else if (lnt == L_WORD) { /* word? */ | |
| if (bo == 3) | |
| val = ((ibufl >> 24) & 0xFF) | ((ibufh & 0xFF) << 8); | |
| else val = (ibufl >> (bo << 3)) & WMASK; | |
| } | |
| else if (bo) { /* unaligned lw? */ | |
| sc = bo << 3; | |
| val = (((ibufl >> sc) & align[bo]) | (((uint32) ibufh) << (32 - sc))); | |
| } | |
| else val = ibufl; /* aligned lw */ | |
| if ((bo + lnt) >= 4) { /* retire ibufl? */ | |
| ibufl = ibufh; | |
| ibcnt = ibcnt - 4; | |
| } | |
| return val; | |
| } | |
| /* Read octaword specifier */ | |
| int32 ReadOcta (int32 va, int32 *opnd, int32 j, int32 acc) | |
| { | |
| opnd[j++] = Read (va, L_LONG, acc); | |
| opnd[j++] = Read (va + 4, L_LONG, acc); | |
| opnd[j++] = Read (va + 8, L_LONG, acc); | |
| opnd[j++] = Read (va + 12, L_LONG, acc); | |
| return j; | |
| } | |
| /* Schedule idle before the next instruction */ | |
| void cpu_idle (void) | |
| { | |
| sim_activate (&cpu_unit, 0); | |
| return; | |
| } | |
| /* Idle service */ | |
| t_stat cpu_idle_svc (UNIT *uptr) | |
| { | |
| sim_idle (TMR_CLK, TRUE); | |
| return SCPE_OK; | |
| } | |
| /* Reset */ | |
| t_stat cpu_reset (DEVICE *dptr) | |
| { | |
| hlt_pin = 0; | |
| mem_err = 0; | |
| crd_err = 0; | |
| PSL = PSL_IS | PSL_IPL1F; | |
| SISR = 0; | |
| ASTLVL = 4; | |
| mapen = 0; | |
| FLUSH_ISTR; /* init I-stream */ | |
| if (M == NULL) { /* first time init? */ | |
| sim_brk_types = sim_brk_dflt = SWMASK ('E'); | |
| sim_vm_is_subroutine_call = cpu_is_pc_a_subroutine_call; | |
| pcq_r = find_reg ("PCQ", NULL, dptr); | |
| if (pcq_r == NULL) | |
| return SCPE_IERR; | |
| pcq_r->qptr = 0; | |
| M = (uint32 *) calloc (((uint32) MEMSIZE) >> 2, sizeof (uint32)); | |
| if (M == NULL) | |
| return SCPE_MEM; | |
| auto_config(NULL, 0); /* do an initial auto configure */ | |
| } | |
| return build_dib_tab (); | |
| } | |
| static const char *cpu_next_caveats = | |
| "The NEXT command in this VAX architecture simulator currently will\n" | |
| "enable stepping across subroutine calls which are initiated by the\n" | |
| "BSBB, BSBW, JSB, CALLG, CALLS, CHMK, CHME, CHMS, and CHMU instructions.\n" | |
| "This stepping works by dynamically establishing breakpoints at the\n" | |
| "memory address immediately following the instruction which initiated\n" | |
| "the subroutine call. These dynamic breakpoints are automatically\n" | |
| "removed once the simulator returns to the sim> prompt for any reason.\n" | |
| "If the called routine returns somewhere other than one of these\n" | |
| "locations due to a trap, stack unwind or any other reason, instruction\n" | |
| "execution will continue until some other reason causes execution to stop.\n"; | |
| t_bool cpu_is_pc_a_subroutine_call (t_addr **ret_addrs) | |
| { | |
| #define MAX_SUB_RETURN_SKIP 9 | |
| static t_addr returns[MAX_SUB_RETURN_SKIP+1] = {0}; | |
| static t_bool caveats_displayed = FALSE; | |
| int i; | |
| int32 saved_sim_switches = sim_switches; | |
| if (!caveats_displayed) { | |
| caveats_displayed = TRUE; | |
| sim_printf ("%s", cpu_next_caveats); | |
| } | |
| sim_switches |= SWMASK('V'); | |
| if (SCPE_OK != get_aval (PC, &cpu_dev, &cpu_unit)) {/* get data */ | |
| sim_switches = saved_sim_switches; | |
| return FALSE; | |
| } | |
| sim_switches = saved_sim_switches; | |
| switch (sim_eval[0]) | |
| { | |
| case BSBB: case BSBW: case JSB: | |
| case CALLG: case CALLS: | |
| case CHMK: case CHME: case CHMS: case CHMU: | |
| returns[0] = PC + (1 - fprint_sym (stdnul, PC, sim_eval, &cpu_unit, SWMASK ('M'))); | |
| for (i=1; i<MAX_SUB_RETURN_SKIP; i++) | |
| returns[i] = returns[i-1] + 1; /* Possible skip return */ | |
| returns[i] = 0; /* Make sure the address list ends with a zero */ | |
| *ret_addrs = returns; | |
| return TRUE; | |
| default: | |
| return FALSE; | |
| } | |
| } | |
| /* Memory examine */ | |
| t_stat cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) | |
| { | |
| int32 st; | |
| uint32 addr = (uint32) exta; | |
| if (vptr == NULL) | |
| return SCPE_ARG; | |
| if (sw & SWMASK ('V')) { | |
| int32 acc = cpu_get_vsw (sw); | |
| addr = Test (addr, acc, &st); | |
| } | |
| else addr = addr & PAMASK; | |
| if (ADDR_IS_MEM (addr) || ADDR_IS_CDG (addr) || | |
| ADDR_IS_ROM (addr) || ADDR_IS_NVR (addr)) { | |
| *vptr = (uint32) ReadB (addr); | |
| return SCPE_OK; | |
| } | |
| return SCPE_NXM; | |
| } | |
| /* Memory deposit */ | |
| t_stat cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) | |
| { | |
| int32 st; | |
| uint32 addr = (uint32) exta; | |
| if (sw & SWMASK ('V')) { | |
| int32 acc = cpu_get_vsw (sw); | |
| addr = Test (addr, acc, &st); | |
| } | |
| else addr = addr & PAMASK; | |
| if (ADDR_IS_MEM (addr) || ADDR_IS_CDG (addr) || | |
| ADDR_IS_NVR (addr)) { | |
| WriteB (addr, (int32) val); | |
| return SCPE_OK; | |
| } | |
| if (ADDR_IS_ROM (addr)) { | |
| rom_wr_B (addr, (int32) val); | |
| return SCPE_OK; | |
| } | |
| return SCPE_NXM; | |
| } | |
| /* Memory allocation */ | |
| t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) | |
| { | |
| int32 mc = 0; | |
| uint32 i, clim, uval = (uint32)val; | |
| uint32 *nM = NULL; | |
| if ((val <= 0) || (val > MAXMEMSIZE_X)) | |
| return SCPE_ARG; | |
| for (i = val; i < MEMSIZE; i = i + 4) | |
| mc = mc | M[i >> 2]; | |
| if ((mc != 0) && !get_yn ("Really truncate memory [N]?", FALSE)) | |
| return SCPE_OK; | |
| nM = (uint32 *) calloc (uval >> 2, sizeof (uint32)); | |
| if (nM == NULL) | |
| return SCPE_MEM; | |
| clim = (uint32)((uval < MEMSIZE)? uval: MEMSIZE); | |
| for (i = 0; i < clim; i = i + 4) | |
| nM[i >> 2] = M[i >> 2]; | |
| free (M); | |
| M = nM; | |
| MEMSIZE = uval; | |
| reset_all (0); | |
| return SCPE_OK; | |
| } | |
| /* Virtual address translation */ | |
| t_stat cpu_show_virt (FILE *of, UNIT *uptr, int32 val, void *desc) | |
| { | |
| t_stat r; | |
| char *cptr = (char *) desc; | |
| uint32 va, pa; | |
| int32 st; | |
| static const char *mm_str[] = { | |
| "Access control violation", | |
| "Length violation", | |
| "Process PTE access control violation", | |
| "Process PTE length violation", | |
| "Translation not valid", | |
| "Internal error", | |
| "Process PTE translation not valid" | |
| }; | |
| if (cptr) { | |
| va = (uint32) get_uint (cptr, 16, 0xFFFFFFFF, &r); | |
| if (r == SCPE_OK) { | |
| int32 acc = cpu_get_vsw (sim_switches); | |
| pa = Test (va, acc, &st); | |
| if (st == PR_OK) | |
| fprintf (of, "Virtual %-X = physical %-X\n", va, pa); | |
| else fprintf (of, "Virtual %-X: %s\n", va, mm_str[st]); | |
| return SCPE_OK; | |
| } | |
| } | |
| fprintf (of, "Invalid argument\n"); | |
| return SCPE_OK; | |
| } | |
| /* Get access mode for examine, deposit, show virtual */ | |
| int32 cpu_get_vsw (int32 sw) | |
| { | |
| int32 md; | |
| set_map_reg (); /* update dyn reg */ | |
| if (sw & SWMASK ('K')) | |
| md = KERN; | |
| else if (sw & SWMASK ('E')) | |
| md = EXEC; | |
| else if (sw & SWMASK ('S')) | |
| md = SUPV; | |
| else if (sw & SWMASK ('U')) | |
| md = USER; | |
| else md = PSL_GETCUR (PSL); | |
| return ACC_MASK (md); | |
| } | |
| /* Set history */ | |
| t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) | |
| { | |
| int32 i, lnt; | |
| char gbuf[CBUFSIZE]; | |
| t_stat r; | |
| if (cptr == NULL) { | |
| for (i = 0; i < hst_lnt; i++) | |
| hst[i].iPC = 0; | |
| hst_p = 0; | |
| if (hst_log) { | |
| sim_set_fsize (hst_log, (t_addr)0); | |
| hst_log_p = 0; | |
| cpu_show_hist_records (hst_log, TRUE, 0, 0); | |
| } | |
| return SCPE_OK; | |
| } | |
| cptr = get_glyph (cptr, gbuf, ':'); | |
| lnt = (int32) get_uint (gbuf, 10, HIST_MAX, &r); | |
| if (r != SCPE_OK) | |
| return sim_messagef (SCPE_ARG, "Invalid Numeric Value: %s\n", gbuf); | |
| if (lnt && (lnt < HIST_MIN)) | |
| return sim_messagef (SCPE_ARG, "%d is less than the minumum history value of %d\n", lnt, HIST_MIN); | |
| hst_p = 0; | |
| if (hst_lnt) { | |
| free (hst); | |
| hst_lnt = 0; | |
| hst = NULL; | |
| if (hst_log) { | |
| fclose (hst_log); | |
| hst_log = NULL; | |
| } | |
| } | |
| if (lnt) { | |
| hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); | |
| if (hst == NULL) | |
| return SCPE_MEM; | |
| hst_lnt = lnt; | |
| hst_switches = sim_switches; | |
| if (cptr && *cptr) { | |
| hst_log = sim_fopen (cptr, "w"); | |
| if (hst_log) | |
| cpu_show_hist_records (hst_log, TRUE, 0, 0); | |
| else { | |
| free (hst); | |
| hst_lnt = 0; | |
| hst = NULL; | |
| return sim_messagef(SCPE_OPENERR, "Unable to open file '%s': %s\n", cptr, strerror (errno)); | |
| } | |
| } | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Show history */ | |
| t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) | |
| { | |
| int32 di, lnt; | |
| char *cptr = (char *) desc; | |
| t_stat r; | |
| if (hst_lnt == 0) /* enabled? */ | |
| return SCPE_NOFNC; | |
| 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; | |
| return cpu_show_hist_records (st, TRUE, di, lnt); | |
| } | |
| t_stat cpu_show_hist_records (FILE *st, t_bool do_header, int32 start, int32 count) | |
| { | |
| int32 i, k, numspec; | |
| InstHistory *h; | |
| if (hst_lnt == 0) /* enabled? */ | |
| return SCPE_NOFNC; | |
| if (do_header) { | |
| if (hst_switches & SWMASK('T')) | |
| fprintf (st," TIME "); | |
| fprintf (st, "PC PSL IR\n\n"); | |
| } | |
| for (k = 0; k < count; k++) { /* print specified */ | |
| h = &hst[(start++) % hst_lnt]; /* entry pointer */ | |
| if (h->iPC == 0) /* filled in? */ | |
| continue; | |
| if (hst_switches & SWMASK('T')) /* sim_time */ | |
| fprintf(st, "%10.0f ", h->time); | |
| fprintf(st, "%08X %08X| ", h->iPC, h->PSL); /* PC, PSL */ | |
| numspec = DR_GETNSP (drom[h->opc][0]); /* #specifiers */ | |
| if (opcode[h->opc] == NULL) /* undefined? */ | |
| fprintf (st, "%03X (undefined)", h->opc); | |
| else if (h->PSL & PSL_FPD) /* FPD set? */ | |
| fprintf (st, "%s FPD set", opcode[h->opc]); | |
| else { /* normal */ | |
| for (i = 0; i < INST_SIZE; i++) | |
| sim_eval[i] = h->inst[i]; | |
| if ((fprint_sym (st, h->iPC, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) | |
| fprintf (st, "%03X (undefined)", h->opc); | |
| if ((numspec > 1) || | |
| ((numspec == 1) && (drom[h->opc][1] < BB))) { | |
| if (cpu_show_opnd (st, h, 0)) { /* operands; more? */ | |
| if (cpu_show_opnd (st, h, 1)) { /* 2nd line; more? */ | |
| cpu_show_opnd (st, h, 2); /* octa, 3rd/4th */ | |
| cpu_show_opnd (st, h, 3); | |
| } | |
| } | |
| } | |
| } /* end else */ | |
| fputc ('\n', st); /* end line */ | |
| } /* end for */ | |
| fflush (st); | |
| return SCPE_OK; | |
| } | |
| t_bool cpu_show_opnd (FILE *st, InstHistory *h, int32 line) | |
| { | |
| int32 numspec, i, j, disp; | |
| t_bool more; | |
| numspec = drom[h->opc][0] & DR_NSPMASK; /* #specifiers */ | |
| fputs ("\n ", st); /* space */ | |
| if (hst_switches & SWMASK('T')) | |
| fputs (" ", st); | |
| for (i = 1, j = 0, more = FALSE; i <= numspec; i++) { /* loop thru specs */ | |
| disp = drom[h->opc][i]; /* specifier type */ | |
| if (disp == RG) /* fix specials */ | |
| disp = RQ; | |
| else if (disp >= BB) | |
| break; /* ignore branches */ | |
| else switch (disp & (DR_LNMASK|DR_ACMASK)) { | |
| case RB: case RW: case RL: /* read */ | |
| case AB: case AW: case AL: case AQ: case AO: /* address */ | |
| case MB: case MW: case ML: /* modify */ | |
| if (line == 0) | |
| fprintf (st, " %08X", h->opnd[j]); | |
| else fputs (" ", st); | |
| j = j + 1; | |
| break; | |
| case RQ: case MQ: /* read, modify quad */ | |
| if (line <= 1) | |
| fprintf (st, " %08X", h->opnd[j + line]); | |
| else fputs (" ", st); | |
| if (line == 0) | |
| more = TRUE; | |
| j = j + 2; | |
| break; | |
| case RO: case MO: /* read, modify octa */ | |
| fprintf (st, " %08X", h->opnd[j + line]); | |
| more = TRUE; | |
| j = j + 4; | |
| break; | |
| case WB: case WW: case WL: case WQ: case WO: /* write */ | |
| if (line == 0) | |
| fprintf (st, " %08X", h->opnd[j + 1]); | |
| else fputs (" ", st); | |
| j = j + 2; | |
| break; | |
| } /* end case */ | |
| } /* end for */ | |
| if ((line == 0) && ((drom[h->opc][0] & DR_M_RESMASK) >> DR_V_RESMASK)) { | |
| fprintf (st, " ->"); | |
| switch ((drom[h->opc][0] & DR_M_RESMASK) >> DR_V_RESMASK) { | |
| case RB_O>>DR_V_RESMASK: | |
| fprintf (st, " %08X %08X %08X %08X", h->res[0], h->res[1], h->res[2], h->res[3]); | |
| break; | |
| case RB_Q>>DR_V_RESMASK: | |
| fprintf (st, " %08X %08X", h->res[0], h->res[1]); | |
| break; | |
| case RB_B>>DR_V_RESMASK: | |
| case RB_W>>DR_V_RESMASK: | |
| case RB_L>>DR_V_RESMASK: | |
| fprintf (st, " %08X", h->res[0]); | |
| break; | |
| case RB_R5>>DR_V_RESMASK: | |
| case RB_R3>>DR_V_RESMASK: | |
| case RB_R1>>DR_V_RESMASK: | |
| case RB_R0>>DR_V_RESMASK: | |
| if (1) { | |
| static const int rcnts[] = {1, 2, 4, 6}; | |
| int i; | |
| for (i = 0; i < rcnts[((drom[h->opc][0] & DR_M_RESMASK) - RB_R0) >> DR_V_RESMASK]; i++) | |
| fprintf (st, " R%d:%08X", i, h->res[i]); | |
| } | |
| break; | |
| case RB_SP>>DR_V_RESMASK: | |
| fprintf (st, " SP: %08X", h->res[0]); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| return more; | |
| } | |
| struct os_idle { | |
| const char *name; | |
| uint32 mask; | |
| }; | |
| static struct os_idle os_tab[] = { | |
| { "VMS", VAX_IDLE_VMS }, | |
| { "ULTRIX", VAX_IDLE_ULT }, | |
| { "ULTRIXOLD", VAX_IDLE_ULTOLD }, | |
| { "ULTRIX-1.X", VAX_IDLE_ULT1X }, | |
| { "3BSD", VAX_IDLE_ULT1X }, | |
| { "4.0BSD", VAX_IDLE_ULT1X }, | |
| { "4.1BSD", VAX_IDLE_ULT1X }, | |
| { "4.2BSD", VAX_IDLE_ULT1X }, | |
| { "QUASIJARUS", VAX_IDLE_QUAD }, | |
| { "4.3BSD", VAX_IDLE_QUAD }, | |
| { "4.4BSD-Reno", VAX_IDLE_QUAD }, | |
| { "NETBSD", VAX_IDLE_BSDNEW }, | |
| { "NETBSDOLD", VAX_IDLE_ULTOLD }, | |
| { "OPENBSD", VAX_IDLE_BSDNEW }, | |
| { "OPENBSDOLD", VAX_IDLE_QUAD }, | |
| { "32V", VAX_IDLE_VMS }, | |
| { "ELN", VAX_IDLE_ELN }, | |
| { NULL, 0 } | |
| }; | |
| /* Set and show idle */ | |
| t_stat cpu_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc) | |
| { | |
| uint32 i; | |
| char gbuf[CBUFSIZE]; | |
| if (cptr != NULL) { | |
| cptr = get_glyph (cptr, gbuf, ':'); | |
| for (i = 0; os_tab[i].name != NULL; i++) { | |
| if (strcmp (os_tab[i].name, gbuf) == 0) { | |
| cpu_idle_type = i + 1; | |
| cpu_idle_mask = os_tab[i].mask; | |
| return sim_set_idle (uptr, val, cptr, desc); | |
| } | |
| } | |
| return SCPE_ARG; | |
| } | |
| return sim_set_idle (uptr, val, cptr, desc); | |
| } | |
| t_stat cpu_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc) | |
| { | |
| if (sim_idle_enab && (cpu_idle_type != 0)) | |
| fprintf (st, "idle=%s, ", os_tab[cpu_idle_type - 1].name); | |
| sim_show_idle (st, uptr, val, desc); | |
| return SCPE_OK; | |
| } | |
| t_stat cpu_load_bootcode (const char *filename, const unsigned char *builtin_code, size_t size, t_bool rom, t_addr offset) | |
| { | |
| char args[CBUFSIZE]; | |
| t_stat r; | |
| int32 saved_sim_switches = sim_switches; | |
| sim_printf ("Loading boot code from %s%s\n", builtin_code ? "internal " : "", filename); | |
| if (builtin_code) | |
| sim_set_memory_load_file (builtin_code, size); | |
| if (rom) | |
| sprintf (args, "-R %s", filename); | |
| else | |
| sprintf (args, "-O %s %X", filename, (int)offset); | |
| r = load_cmd (0, args); | |
| sim_set_memory_load_file (NULL, 0); | |
| sim_switches = saved_sim_switches; | |
| return r; | |
| } | |
| t_stat cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
| { | |
| fprintf (st, "The ");cpu_print_model (st);fprintf (st, " CPU help\n\n"); | |
| fprintf (st, "CPU options include the size of main memory.\n\n"); | |
| if (dptr->modifiers) { | |
| MTAB *mptr; | |
| extern t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); | |
| for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) | |
| if (mptr->valid == &cpu_set_size) | |
| fprintf (st, " sim> SET CPU %4s set memory size = %sB\n", mptr->mstring, mptr->mstring); | |
| fprintf (st, "\n"); | |
| } | |
| cpu_model_help (st, dptr, uptr, flag, cptr); | |
| fprintf (st, "CPU options include the treatment of the HALT instruction.\n\n"); | |
| fprintf (st, " sim> SET CPU SIMHALT kernel HALT returns to simulator\n"); | |
| fprintf (st, " sim> SET CPU CONHALT kernel HALT returns to boot ROM console\n\n"); | |
| fprintf (st, "The CPU also implements a command to display a virtual to physical address\n"); | |
| fprintf (st, "translation:\n\n"); | |
| fprintf (st, " sim> SHOW {-kesu} CPU VIRTUAL=n show translation for address n\n"); | |
| fprintf (st, " in kernel/exec/supervisor/user mode\n\n"); | |
| fprintf (st, "Memory can be loaded with a binary byte stream using the LOAD command. The\n"); | |
| fprintf (st, "LOAD command recognizes three switches:\n\n"); | |
| fprintf (st, " -o origin argument follows file name\n"); | |
| fprintf (st, " -r load the boot ROM\n"); | |
| fprintf (st, " -n load the non-volatile RAM\n\n"); | |
| fprintf (st, "These switches are recognized when examining or depositing in CPU memory:\n\n"); | |
| fprintf (st, " -b examine/deposit bytes\n"); | |
| fprintf (st, " -w examine/deposit words\n"); | |
| fprintf (st, " -l examine/deposit longwords\n"); | |
| fprintf (st, " -d data radix is decimal\n"); | |
| fprintf (st, " -o data radix is octal\n"); | |
| fprintf (st, " -h data radix is hexadecimal\n"); | |
| fprintf (st, " -m examine (only) VAX instructions\n"); | |
| fprintf (st, " -p examine/deposit PDP-11 (compatibility mode) instructions\n"); | |
| fprintf (st, " -r examine (only) RADIX50 encoded data\n"); | |
| fprintf (st, " -v interpret address as virtual, current mode\n"); | |
| fprintf (st, " -k interpret address as virtual, kernel mode\n"); | |
| fprintf (st, " -e interpret address as virtual, executive mode\n"); | |
| fprintf (st, " -s interpret address as virtual, supervisor mode\n"); | |
| fprintf (st, " -u interpret address as virtual, user mode\n\n"); | |
| fprintf (st, "The CPU attempts to detect when the simulator is idle. When idle, the\n"); | |
| fprintf (st, "simulator does not use any resources on the host system. Idle detection is\n"); | |
| fprintf (st, "controlled by the SET IDLE and SET NOIDLE commands:\n\n"); | |
| fprintf (st, " sim> SET CPU IDLE{=VMS|ULTRIX|ULTRIXOLD|ULTRIX-1.X|\n"); | |
| fprintf (st, " 3BSD|4.0BSD|4.1BSD|4.2BSD|QUASIJARUS|\n"); | |
| fprintf (st, " NETBSD|NETBSDOLD|OPENBSD|OPENBSDOLD|32V|ELN}{:n}\n"); | |
| fprintf (st, " enable idle detection\n"); | |
| fprintf (st, " sim> SET CPU NOIDLE disable idle detection\n\n"); | |
| fprintf (st, "Idle detection is disabled by default. If idle detection is enabled with\n"); | |
| fprintf (st, "an incorrect operating system setting, simulator performance or correct\n"); | |
| fprintf (st, "functionality could be impacted. The default operating system setting is\n"); | |
| fprintf (st, "VMS. The value 'n', if present in the \"SET CPU IDLE={OS}:n\" command,\n"); | |
| fprintf (st, "indicats the number of seconds which the simulator must run before idling\n"); | |
| fprintf (st, "starts.\n\n"); | |
| fprintf (st, "The CPU can maintain a history of the most recently executed instructions.\n"); | |
| fprintf (st, "This is controlled by the SET CPU HISTORY and SHOW CPU HISTORY commands:\n\n"); | |
| fprintf (st, " sim> SET CPU HISTORY clear history buffer\n"); | |
| fprintf (st, " sim> SET CPU HISTORY=0 disable history\n"); | |
| fprintf (st, " sim> SET CPU {-T} HISTORY=n{:file} enable history, length = n\n"); | |
| fprintf (st, " sim> SHOW CPU HISTORY print CPU history\n"); | |
| fprintf (st, " sim> SHOW CPU HISTORY=n print first n entries of CPU history\n\n"); | |
| fprintf (st, "The -T switch causes simulator time to be recorded (and displayed)\n"); | |
| fprintf (st, "with each history entry.\n"); | |
| fprintf (st, "When writing history to a file (SET CPU HISTORY=n:file), 'n' specifies\n"); | |
| fprintf (st, "the buffer flush frequency. Warning: prodigious amounts of disk space\n"); | |
| fprintf (st, "may be comsumed. The maximum length for the history is %d entries.\n\n", HIST_MAX); | |
| return SCPE_OK; | |
| } |