4018 lines
151 KiB
C
4018 lines
151 KiB
C
/* vax_cpu.c: VAX CPU
|
|
|
|
Copyright (c) 1998-2019, 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
|
|
|
|
23-Apr-19 RMS Added hook for unpredictable indexed immediate .aw
|
|
14-Apr-19 RMS Added hook for non-standard MxPR CC's
|
|
31-Mar-17 RMS Fixed uninitialized variable on FPD path (COVERITY)
|
|
13-Mar-17 RMS Fixed dangling else in show_opnd (COVERITY)
|
|
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
|
|
|
|
|
|
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 mxpr_cc_vc = 0; /* MxPR V,C bits */
|
|
int32 pcq_p = 0; /* PC queue ptr */
|
|
int32 badabo = 0;
|
|
int32 cpu_instruction_set = CPU_INSTRUCTION_SET; /* Instruction Groups */
|
|
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 = /* idle mask */
|
|
#if defined (VAX_411) || defined (VAX_412)
|
|
VAX_IDLE_INFOSERVER;
|
|
uint32 cpu_idle_type = 2; /* default INFOSERVER */
|
|
#else
|
|
VAX_IDLE_VMS;
|
|
uint32 cpu_idle_type = 1; /* default VMS */
|
|
#endif
|
|
int32 extra_bytes; /* bytes referenced by current string instruction */
|
|
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;
|
|
|
|
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, CONST char *cptr, void *desc);
|
|
t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
t_stat cpu_show_virt (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
t_stat cpu_set_idle (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
t_stat cpu_show_idle (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
t_stat cpu_set_instruction_set (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
t_stat cpu_show_instruction_set (FILE *st, UNIT *uptr, int32 val, CONST 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);
|
|
int32 cpu_emulate_exception (int32 *opnd, int32 cc, int32 opc, int32 acc);
|
|
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 (NULL, UNIT_FIX|UNIT_BINK, INITMEMSIZE)
|
|
};
|
|
|
|
const char *psl_modes[] = {"K", "E", "S", "U"};
|
|
|
|
|
|
BITFIELD cpu_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", cpu_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|MDM|INFOSERVER}{: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|MTAB_NC, 0, "HISTORY", "HISTORY=n",
|
|
&cpu_set_hist, &cpu_show_hist, NULL, "Enable/Display 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 */
|
|
CPU_INST_MODIFIERS /* Model specific cpu instruction modifiers from vaxXXX_defs.h */
|
|
{ 0 }
|
|
};
|
|
|
|
DEBTAB cpu_deb[] = {
|
|
{ "INTEXC", LOG_CPU_I, "interrupt and exception activities" },
|
|
{ "REI", LOG_CPU_R, "REI activities" },
|
|
{ "ABORT", LOG_CPU_A, "Abort activities" },
|
|
{ "CONTEXT", LOG_CPU_P, "context switching activities" },
|
|
{ "RSVDFAULT", LOG_CPU_FAULT_RSVD, "reserved fault activities" },
|
|
{ "FLTFAULT", LOG_CPU_FAULT_FLT, "floating fault activities" },
|
|
{ "CMODFAULT", LOG_CPU_FAULT_CMODE, "cmode fault activities" },
|
|
{ "MCHKFAULT", LOG_CPU_FAULT_MCHK, "machine check fault activities" },
|
|
{ "EMULFAULT", LOG_CPU_FAULT_EMUL, "instruction emulation fault activities" },
|
|
{ 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, CONST 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 ret;
|
|
int32 r = 0, rh = 0, temp = 0;
|
|
int32 spec = 0, disp = 0, rn = 0, index = 0, numspec = 0;
|
|
int32 vfldrp1 = 0, brdisp = 0, flg = 0, mstat = 0;
|
|
uint32 va = 0, iad = 0;
|
|
int32 opnd[OPND_SIZE]; /* operand queue */
|
|
|
|
if ((ret = build_dib_tab ()) != SCPE_OK) /* build, chk dib_tab */
|
|
return ret;
|
|
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;
|
|
}
|
|
}
|
|
sim_debug (LOG_CPU_A, &cpu_dev, "abort=%08X, fault_PC=%08x, SP=%08x, PC=%08x, PSL=%08x ",
|
|
-abortval, fault_PC, PC, SP, PSL);
|
|
sim_debug_bits(LOG_CPU_A, &cpu_dev, cpu_psl_bits, PSL, PSL, 1);
|
|
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 */
|
|
sim_debug (LOG_CPU_FAULT_MCHK, &cpu_dev, "%s fault_PC=%08x, PSL=%08x, cc=%08x, PC=%08x, delta-%08X, p1=%08X\n",
|
|
opcode[opc], fault_PC, PSL, cc, PC, delta, p1);
|
|
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 i, j;
|
|
|
|
/* Optionally record instruction history results from prior instruction */
|
|
|
|
if (hst_lnt) {
|
|
InstHistory *hlast = &hst[hst_p ? hst_p-1 : hst_lnt -1];
|
|
|
|
switch (DR_GETRES(drom[hlast->opc][0]) << DR_V_RESMASK) {
|
|
case RB_O:
|
|
case RB_OB:
|
|
case RB_OW:
|
|
case RB_OL:
|
|
case RB_OQ:
|
|
break;
|
|
case RB_Q:
|
|
hlast->res[1] = rh;
|
|
hlast->res[0] = r;
|
|
break;
|
|
case RB_B:
|
|
case RB_W:
|
|
case RB_L:
|
|
hlast->res[0] = r;
|
|
break;
|
|
case RB_R5:
|
|
hlast->res[5] = R[5];
|
|
hlast->res[4] = R[4];
|
|
case RB_R3:
|
|
hlast->res[3] = R[3];
|
|
hlast->res[2] = R[2];
|
|
case RB_R1:
|
|
hlast->res[1] = R[1];
|
|
case RB_R0:
|
|
hlast->res[0] = R[0];
|
|
break;
|
|
case RB_SP:
|
|
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 + (extra_bytes>>5));/* count instr */
|
|
extra_bytes = 0; /* digest string count */
|
|
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 !defined(FULL_VAX)
|
|
if (((DR_GETIGRP(numspec) == DR_GETIGRP(IG_BSDFL)) && (!(cpu_instruction_set & VAX_DFLOAT))) ||
|
|
((DR_GETIGRP(numspec) == DR_GETIGRP(IG_BSGFL)) && (!(cpu_instruction_set & VAX_GFLOAT))) ||
|
|
(DR_GETIGRP(numspec) == DR_GETIGRP(IG_RSVD))) /* explicit reserved instruction? */
|
|
RSVD_INST_FAULT(opc);
|
|
#endif
|
|
#if defined(VAX_610)
|
|
/*
|
|
* This case is formally UNPREDICTABLE, but it is how the MicroVAX I
|
|
* CPU worked. Instructions without the DR_F in their drom table
|
|
* entry are specifically uninterruptible instructions, so this
|
|
* would not ever happen during normal execution, but the MicroVAX I
|
|
* HCORE diagnostic contrives this as a test and expects thost cases
|
|
* to be ignored.
|
|
*/
|
|
if ((PSL & PSL_FPD) && (numspec & DR_F)) {
|
|
#else
|
|
if (PSL & PSL_FPD) {
|
|
if ((numspec & DR_F) == 0)
|
|
RSVD_INST_FAULT(opc);
|
|
#endif
|
|
j = 0; /* no operands */
|
|
}
|
|
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:
|
|
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) {
|
|
SETPC (PC + 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:
|
|
index = index + R[rn];
|
|
if (rn == nPC) {
|
|
IDX_IMM_TEST;
|
|
SETPC (PC + DR_LNT (disp));
|
|
}
|
|
else {
|
|
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:
|
|
r = rh = 0;
|
|
WRITE_Q (r, rh); /* 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 */
|
|
((PC - fault_PC) == 6) && /* 6 byte instruction? */
|
|
(fault_PC & 0x80000000) && /* in system space? */
|
|
(((cpu_idle_mask & VAX_IDLE_VMS) && /* VMS 5.0 and 5.1 */
|
|
(PSL_GETIPL (PSL) == 0x3) && /* at IPL 3 */
|
|
(PSL_IS & PSL)) || /* on the interrupt stack */
|
|
((((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 & 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(ADAWI);
|
|
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) || /* at a */
|
|
(fault_PC == 0x20046A36))) || /* 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);
|
|
if ((cpu_idle_mask & VAX_IDLE_INFOSERVER) &&/* INFOSERVER Idle? */
|
|
(PSL & PSL_IS) && /* on IS? */
|
|
(brdisp == 0xF1) && /* Branch to prior INCL */
|
|
(PSL_GETIPL (PSL) == 0x3)) /* at IPL 3 */
|
|
cpu_idle();
|
|
}
|
|
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_ALWAYS (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 */
|
|
if (fault_PC == 0x20040C09) /* MicroVAX 2 Boot ROM Character Prompt? */
|
|
cpu_idle();
|
|
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 */
|
|
r = op0 + temp;
|
|
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 */
|
|
r = op0 + temp;
|
|
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(HALT);
|
|
else {
|
|
/* allow potentially pending I/O (console output,
|
|
or other devices) to complete before taking
|
|
the appropriate halt action */
|
|
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 */
|
|
}
|
|
if (cpu_unit.flags & UNIT_CONH) /* halt to console? */
|
|
cc = con_halt (CON_HLTINS, cc); /* enter firmware */
|
|
else
|
|
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(BISPW);
|
|
PSL = PSL | (opnd[0] & ~CC_MASK);
|
|
cc = cc | (opnd[0] & CC_MASK);
|
|
break;
|
|
|
|
case BICPSW:
|
|
if (opnd[0] & PSW_MBZ)
|
|
RSVD_OPND_FAULT(BICPSW);
|
|
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:
|
|
#if defined(VAX_610)
|
|
if (opc == CMPC5) {
|
|
cc = cpu_emulate_exception (opnd, cc, opc, acc);
|
|
break;
|
|
}
|
|
#endif
|
|
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:
|
|
mxpr_cc_vc = cc & CC_C; /* std: V=0, C unchgd */
|
|
cc = op_mtpr (opnd);
|
|
cc = cc | (mxpr_cc_vc & (CC_V|CC_C)); /* or in V,C */
|
|
SET_IRQL; /* update intreq */
|
|
break;
|
|
|
|
case MFPR:
|
|
mxpr_cc_vc = cc & CC_C; /* std: V=0, C unchgd */
|
|
r = op_mfpr (opnd);
|
|
WRITE_L (r);
|
|
CC_IIZZ_L (r); /* set NV, clr VC */
|
|
cc = cc | (mxpr_cc_vc & (CC_V|CC_C)); /* or in V,C */
|
|
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:
|
|
#if defined(VAX_610)
|
|
cc = cpu_emulate_exception (opnd, cc, opc, acc);
|
|
break;
|
|
#endif
|
|
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,
|
|
(hst_lnt ? &hst[hst_p ? hst_p-1 : hst_lnt -1] : NULL) );
|
|
if (cc & LSIGN) { /* ACBH branch? */
|
|
BRANCHW (brdisp);
|
|
cc = cc & CC_MASK; /* mask off flag */
|
|
}
|
|
break;
|
|
|
|
default:
|
|
RSVD_INST_FAULT(opc);
|
|
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;
|
|
}
|
|
|
|
|
|
/* CIS instructions - invoke emulator interface
|
|
|
|
opnd[0:5] = six operands to be pushed (if PSL<fpd> = 0)
|
|
cc = condition codes
|
|
opc = opcode
|
|
|
|
If FPD is set, push old PC and PSL on stack, vector thru SCB.
|
|
If FPD is clear, push opcode, old PC, operands, new PC, and PSL
|
|
on stack, vector thru SCB.
|
|
In both cases, the exception occurs in the current mode.
|
|
*/
|
|
|
|
int32 cpu_emulate_exception (int32 *opnd, int32 cc, int32 opc, int32 acc)
|
|
{
|
|
int32 vec;
|
|
|
|
if (PSL & PSL_FPD) { /* FPD set? */
|
|
Read (SP - 1, L_BYTE, WA); /* wchk stack */
|
|
Write (SP - 8, fault_PC, L_LONG, WA); /* push old PC */
|
|
Write (SP - 4, PSL | cc, L_LONG, WA); /* push PSL */
|
|
SP = SP - 8; /* decr stk ptr */
|
|
vec = ReadLP ((SCBB + SCB_EMULFPD) & PAMASK);
|
|
sim_debug (LOG_CPU_FAULT_EMUL, &cpu_dev, "FPD OP=%s, fault_PC=%08x, PC=%08x, PSL=%08x, SP=%08x, nPC=%08x ",
|
|
opcode[opc], fault_PC, PC, PSL, SP, vec);
|
|
sim_debug_bits(LOG_CPU_FAULT_EMUL, &cpu_dev, cpu_psl_bits, PSL, PSL, 1);
|
|
}
|
|
else {
|
|
if (opc == CVTPL) /* CVTPL? .wl */
|
|
opnd[2] = (opnd[2] >= 0)? ~opnd[2]: opnd[3];
|
|
Read (SP - 1, L_BYTE, WA); /* wchk stack */
|
|
Write (SP - 48, opc, L_LONG, WA); /* push opcode */
|
|
Write (SP - 44, fault_PC, L_LONG, WA); /* push old PC */
|
|
Write (SP - 40, opnd[0], L_LONG, WA); /* push operands */
|
|
Write (SP - 36, opnd[1], L_LONG, WA);
|
|
Write (SP - 32, opnd[2], L_LONG, WA);
|
|
Write (SP - 28, opnd[3], L_LONG, WA);
|
|
Write (SP - 24, opnd[4], L_LONG, WA);
|
|
Write (SP - 20, opnd[5], L_LONG, WA);
|
|
Write (SP - 8, PC, L_LONG, WA); /* push cur PC */
|
|
Write (SP - 4, PSL | cc, L_LONG, WA); /* push PSL */
|
|
SP = SP - 48; /* decr stk ptr */
|
|
vec = ReadLP ((SCBB + SCB_EMULATE) & PAMASK);
|
|
sim_debug (LOG_CPU_FAULT_EMUL, &cpu_dev, "OP=%s, fault_PC=%08x, PC=%08x, PSL=%08x, SP=%08x, nPC=%08x ",
|
|
opcode[opc], fault_PC, PC, PSL, SP, vec);
|
|
sim_debug_bits(LOG_CPU_FAULT_EMUL, &cpu_dev, cpu_psl_bits, PSL, PSL, 1);
|
|
}
|
|
PSL = PSL & ~(PSL_TP | PSL_FPD | PSW_DV | PSW_FU | PSW_IV | PSW_T);
|
|
JUMP (vec & ~03); /* set new PC */
|
|
return 0; /* set new cc's */
|
|
}
|
|
|
|
|
|
/* Idle before the next instruction */
|
|
|
|
void cpu_idle (void)
|
|
{
|
|
sim_idle (TMR_CLK, TRUE);
|
|
}
|
|
|
|
/*
|
|
* This sequence of instructions is a mix that mimics
|
|
* a resonable instruction set that is a close estimate
|
|
* to the calibrated result without a direct "loop to
|
|
* self" instruction that would halt simulation.
|
|
*/
|
|
|
|
static const char *vax_clock_precalibrate_commands[] = {
|
|
"-m 100 INCL 120",
|
|
"-m 103 INCL 124",
|
|
"-m 106 MULL3 120,124,128",
|
|
"-m 10D BRW 100",
|
|
"PC 100",
|
|
NULL};
|
|
|
|
/* 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? */
|
|
vax_init();
|
|
sim_brk_types = sim_brk_dflt = SWMASK ('E');
|
|
sim_vm_is_subroutine_call = cpu_is_pc_a_subroutine_call;
|
|
sim_clock_precalibrate_commands = vax_clock_precalibrate_commands;
|
|
sim_vm_initial_ips = SIM_INITIAL_IPS;
|
|
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, CONST 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, CONST void *desc)
|
|
{
|
|
t_stat r;
|
|
const char *cptr = (const 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, CONST 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, CONST void *desc)
|
|
{
|
|
int32 di, lnt;
|
|
const char *cptr = (const 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;
|
|
if (disp >= BB) /* ignore branches */
|
|
break;
|
|
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) && (DR_GETRES(drom[h->opc][0]))) {
|
|
fprintf (st, " ->");
|
|
switch (DR_GETRES(drom[h->opc][0]) << DR_V_RESMASK) {
|
|
case RB_O:
|
|
fprintf (st, " %08X %08X %08X %08X", h->res[0], h->res[1], h->res[2], h->res[3]);
|
|
break;
|
|
case RB_Q:
|
|
fprintf (st, " %08X %08X", h->res[0], h->res[1]);
|
|
break;
|
|
case RB_B:
|
|
case RB_W:
|
|
case RB_L:
|
|
fprintf (st, " %08X", h->res[0]);
|
|
break;
|
|
case RB_R5:
|
|
case RB_R3:
|
|
case RB_R1:
|
|
case RB_R0:
|
|
if (1) {
|
|
static const int rcnts[] = {1, 2, 4, 6};
|
|
int i;
|
|
|
|
for (i = 0; i < rcnts[DR_GETRES(drom[h->opc][0]) - DR_GETRES(RB_R0)]; i++)
|
|
fprintf (st, " R%d:%08X", i, h->res[i]);
|
|
}
|
|
break;
|
|
case RB_SP:
|
|
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 },
|
|
{ "INFOSERVER", VAX_IDLE_INFOSERVER },
|
|
{ "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 },
|
|
{ "MDM", VAX_IDLE_ELN },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
/* Set and show idle */
|
|
|
|
t_stat cpu_set_idle (UNIT *uptr, int32 val, CONST 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, CONST 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;
|
|
}
|
|
|
|
static struct {
|
|
int32 mask;
|
|
const char *match;
|
|
const char *desc;
|
|
} inst_groups[] = {
|
|
{0, "", ""}, /* Reserved Opcode */
|
|
{VAX_BASE, "BASE", "Base Group"}, /* Base Instruction Group */
|
|
{VAX_GFLOAT, "G-FLOAT", "G-Float"}, /* Base subgroup G-Float */
|
|
{VAX_DFLOAT, "D-FLOAT", "D-Float"}, /* Base subgroup D-Float */
|
|
{VAX_PACKED, "PACKED", "Packed-Decimal-String-Group"}, /* packed-decimal-string group */
|
|
{VAX_EXTAC, "EXTENDED", "Extended-Accuracy-Group"}, /* extended-accuracy group */
|
|
{VAX_EMONL, "EMULATED", "Emulated-Only-Group"}, /* emulated only instructions */
|
|
// {VAX_VECTR, "VECTOR", "Vector-Processing-Group"}, /* vector-processing group */
|
|
{0, NULL, NULL}
|
|
};
|
|
|
|
t_stat cpu_set_instruction_set (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
if (!cptr || !*cptr)
|
|
return SCPE_ARG;
|
|
while (1) {
|
|
int i;
|
|
t_bool invert = FALSE;
|
|
char gbuf[CBUFSIZE];
|
|
|
|
cptr = get_glyph (cptr, gbuf, ';');
|
|
if (!gbuf[0])
|
|
break;
|
|
if (0 == strncmp (gbuf, "NO", 2)) {
|
|
invert = TRUE;
|
|
memmove (gbuf, gbuf + 2, 1 + strlen (gbuf + 2));
|
|
}
|
|
for (i=0; inst_groups[i].match != NULL; i++)
|
|
if (MATCH_CMD (gbuf, inst_groups[i].match) == 0)
|
|
break;
|
|
if (inst_groups[i].match == NULL)
|
|
return sim_messagef (SCPE_ARG, "unknown instruction set group: %s\n", gbuf);
|
|
if (invert)
|
|
cpu_instruction_set &= ~inst_groups[i].mask;
|
|
else
|
|
cpu_instruction_set |= inst_groups[i].mask;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Used when sorting a list of opcode names */
|
|
static int _opc_name_compare (const void *pa, const void *pb)
|
|
{
|
|
const char **a = (const char **)pa;
|
|
const char **b = (const char **)pb;
|
|
|
|
return strcmp (*a, *b);
|
|
}
|
|
|
|
t_stat cpu_show_instruction_group (FILE *st, int32 groupmask)
|
|
{
|
|
int opc;
|
|
int group;
|
|
int matches;
|
|
char const *opcd_tmp[NUM_INST];
|
|
|
|
for (opc=matches=0; opc<NUM_INST; opc++) {
|
|
t_bool match = FALSE;
|
|
|
|
for (group=0; (!match) && (group<=IG_MAX_GRP); group++) {
|
|
if ((1 << group) & groupmask)
|
|
match = (DR_GETIGRP(drom[opc][0]) == group);
|
|
}
|
|
if (match)
|
|
opcd_tmp[matches++] = opcode[opc];
|
|
}
|
|
qsort ((void *)opcd_tmp, matches, sizeof (opcd_tmp[0]), _opc_name_compare);
|
|
for (opc=0; opc<matches; opc++)
|
|
fprintf (st, "%s\t%s", (0 == opc%8) ? "\n" : "", opcd_tmp[opc]);
|
|
fprintf (st, "\n");
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat cpu_show_instruction_set (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
int i;
|
|
|
|
fprintf (st, "Implementing: ");
|
|
if ((cpu_instruction_set & FULL_INSTRUCTION_SET) == FULL_INSTRUCTION_SET) {
|
|
fprintf (st, "All standard VAX instructions");
|
|
#if defined(CMPM_VAX)
|
|
fprintf (st, " and Compatibility mode");
|
|
#endif
|
|
if (sim_switches & SWMASK ('V'))
|
|
cpu_show_instruction_group (st, cpu_instruction_set);
|
|
// if (cpu_instruction_set & ~VAX_VECTR)
|
|
// fprintf (st, ",\n\tEmulating: Vector-Group");
|
|
}
|
|
else {
|
|
if ((cpu_instruction_set & VAX_FULL_BASE) == VAX_FULL_BASE) {
|
|
fprintf (st, "Base Instruction Group");
|
|
if (sim_switches & SWMASK ('V'))
|
|
cpu_show_instruction_group (st, VAX_FULL_BASE);
|
|
}
|
|
else {
|
|
if ((cpu_instruction_set & VAX_BASE) == VAX_BASE) {
|
|
fprintf (st, "Base Instruction Group");
|
|
if (!(cpu_instruction_set & VAX_GFLOAT)) {
|
|
fprintf (st, " without G-Float");
|
|
if (!(cpu_instruction_set & VAX_DFLOAT))
|
|
fprintf (st, " and D-Float");
|
|
}
|
|
else {
|
|
if (!(cpu_instruction_set & VAX_DFLOAT))
|
|
fprintf (st, " without D-Float");
|
|
}
|
|
}
|
|
else {
|
|
if (!(cpu_instruction_set & VAX_DFLOAT))
|
|
fprintf (st, " without D-Float");
|
|
}
|
|
if (sim_switches & SWMASK ('V'))
|
|
cpu_show_instruction_group (st, cpu_instruction_set);
|
|
}
|
|
for (i=4; inst_groups[i].match; i++) {
|
|
if (cpu_instruction_set & inst_groups[i].mask) {
|
|
fprintf (st, " %s", inst_groups[i].desc);
|
|
if (sim_switches & SWMASK ('V'))
|
|
cpu_show_instruction_group (st, inst_groups[i].mask);
|
|
}
|
|
}
|
|
fprintf (st, "\n%sEmulating:", (sim_switches & SWMASK ('V')) ? "" : "\t");
|
|
for (i=1; inst_groups[i].match; i++) {
|
|
if (!(cpu_instruction_set & inst_groups[i].mask)) {
|
|
fprintf (st, " %s", inst_groups[i].desc);
|
|
if (sim_switches & SWMASK ('V'))
|
|
cpu_show_instruction_group (st, inst_groups[i].mask);
|
|
}
|
|
}
|
|
}
|
|
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_messagef (SCPE_OK, "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;
|
|
|
|
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|\n");
|
|
fprintf (st, " 32V|ELN|INFOSERVER}{: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);
|
|
fprintf (st, "Different VAX systems implemented different VAX architecture instructions\n");
|
|
fprintf (st, "in hardware with other instructions possibly emulated by software in the\n");
|
|
fprintf (st, "system. The instructions that a particular simulator implements can be\n");
|
|
fprintf (st, "displayed with:\n\n");
|
|
fprintf (st, " sim> SHOW CPU INSTRUCTIONS display the instructoin groups that are\n");
|
|
fprintf (st, " implemented and emulated\n");
|
|
fprintf (st, " sim> SHOW CPU -V INSTRUCTIONS disable the list of instructions implemented\n");
|
|
fprintf (st, " and emulated\n\n");
|
|
return SCPE_OK;
|
|
}
|