RESTRICTION: The PDP-15 FPP is only partially debugged. Do NOT enable this feature for normal operations. WARNING: The core simulator files (scp.c, sim_*.c) have been reorganized. Unzip V3.2-0 to an empty directory before attempting to compile the source. IMPORTANT: If you are compiling for UNIX, please read the notes for Ethernet very carefully. You may need to download a new version of the pcap library, or make changes to the makefile, to get Ethernet support to work. 1. New Features in 3.2-0 1.1 SCP and libraries - Added SHOW <device> RADIX command. - Added SHOW <device> MODIFIERS command. - Added SHOW <device> NAMES command. - Added SET/SHOW <device> DEBUG command. - Added sim_vm_parse_addr and sim_vm_fprint_addr optional interfaces. - Added REG_VMAD flag. - Split SCP into separate libraries for easier modification. - Added more room to the device and unit flag fields. - Changed terminal multiplexor library to support unlimited. number of async lines. 1.2 All DECtapes - Added STOP_EOR flag to enable end-of-reel error stop - Added device debug support. 1.3 Nova and Eclipse - Added QTY and ALM multiplexors (Bruce Ray). 1.4 LGP-30 - Added LGP-30/LGP-21 simulator. 1.5 PDP-11 - Added format, address increment inhibit, transfer overrun detection to RK. - Added device debug support to HK, RP, TM, TQ, TS. - Added DEUNA/DELUA (XU) support (Dave Hittner). - Add DZ per-line logging. 1.6 18b PDP's - Added support for 1-4 (PDP-9)/1-16 (PDP-15) additional terminals. 1.7 PDP-10 - Added DEUNA/DELUA (XU) support (Dave Hittner). 1.8 VAX - Added extended memory to 512MB (Mark Pizzolato). - Added RXV21 support. 2. Bugs Fixed in 3.2-0 2.1 SCP - Fixed double logging of SHOW BREAK (found by Mark Pizzolato). - Fixed implementation of REG_VMIO. 2.2 Nova and Eclipse - Fixed device enable/disable support (found by Bruce Ray). 2.3 PDP-1 - Fixed bug in LOAD (found by Mark Crispin). 2.4 PDP-10 - Fixed bug in floating point unpack. - Fixed bug in FIXR (found by Phil Stone, fixed by Chris Smith). 2.6 PDP-11 - Fixed bug in RQ interrupt control (found by Tom Evans). 2.6 PDP-18B - Fixed bug in PDP-15 XVM g_mode implementation. - Fixed bug in PDP-15 indexed address calculation. - Fixed bug in PDP-15 autoindexed address calculation. - Fixed bugs in FPP-15 instruction decode. - Fixed clock response to CAF. - Fixed bug in hardware read-in mode bootstrap. - Fixed PDP-15 XVM instruction decoding errors. 2.7 VAX - Fixed PC read fault in EXTxV. - Fixed PC write fault in INSV.
2553 lines
74 KiB
C
2553 lines
74 KiB
C
/* vax_cpu.c: VAX CPU simulator
|
||
|
||
Copyright (c) 1998-2004, 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 CVAX central processor
|
||
|
||
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 (found by 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 (from Mark Pizzolato)
|
||
29-Sep-02 RMS Revised to build dib_tab dynamically
|
||
14-Jul-02 RMS Added halt to console, infinite loop detection
|
||
(from 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 CVAX CPU adds a few specific IPRs:
|
||
|
||
CADR cache disable register
|
||
MSER memory system error 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 OP_MEM -1
|
||
#define UNIT_V_CONH (UNIT_V_UF + 0) /* halt to console */
|
||
#define UNIT_V_EXTM (UNIT_V_UF + 1) /* ext memory enable */
|
||
#define UNIT_V_MSIZE (UNIT_V_UF + 2) /* dummy */
|
||
#define UNIT_CONH (1u << UNIT_V_CONH)
|
||
#define UNIT_EXTM (1u << UNIT_V_EXTM)
|
||
#define UNIT_MSIZE (1u << UNIT_V_MSIZE)
|
||
#define GET_CUR acc = ACC_MASK (PSL_GETCUR (PSL))
|
||
|
||
#define OPND_SIZE 10
|
||
#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 RECW(l) ((l) << 4) | rn
|
||
#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(rl,rh) if (spec > (GRN | nPC)) { \
|
||
if (Test (va + 7, WA, &mstat) >= 0) \
|
||
Write (va, rl, L_LONG, WA); \
|
||
Write (va + 4, rh, L_LONG, WA); } \
|
||
else { R[rn] = rl; R[rnplus1] = rh; }
|
||
|
||
#define HIST_MIN 128
|
||
#define HIST_MAX 65536
|
||
struct InstHistory {
|
||
int32 iPC;
|
||
int32 PSL;
|
||
int32 opc;
|
||
int32 brdest;
|
||
int32 opnd[OPND_SIZE]; };
|
||
|
||
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 CADR = 0; /* cache disable */
|
||
int32 MSER = 0; /* mem error */
|
||
int32 mapen; /* map enable */
|
||
int32 trpirq; /* trap/intr req */
|
||
int32 conpc, conpsl; /* console reg */
|
||
int32 in_ie = 0; /* in exc, int */
|
||
int32 recq[6]; /* recovery queue */
|
||
int32 recqptr; /* recq pointer */
|
||
int32 mem_err = 0; /* mem err intr */
|
||
int32 crd_err = 0; /* CRD err intr */
|
||
int32 hlt_pin = 0; /* HLT pin intr */
|
||
int32 p1 = 0, p2 = 0; /* fault parameters */
|
||
int32 fault_PC; /* fault PC */
|
||
int32 pcq_p = 0; /* PC queue ptr */
|
||
int32 hst_p = 0; /* history pointer */
|
||
int32 hst_lnt = 0; /* history length */
|
||
int32 badabo = 0;
|
||
int32 cpu_astop = 0;
|
||
int32 dbg_stop = 0;
|
||
int32 mchk_va, mchk_ref; /* mem ref param */
|
||
int32 ibufl, ibufh; /* prefetch buf */
|
||
int32 ibcnt, ppc; /* prefetch ctl */
|
||
int32 autcon_enb = 1; /* autoconfig enable */
|
||
int32 cpu_extmem = 0; /* extended memory */
|
||
jmp_buf save_env;
|
||
REG *pcq_r = NULL; /* PC queue reg ptr */
|
||
int32 pcq[PCQ_SIZE] = { 0 }; /* PC queue */
|
||
struct InstHistory *hst = NULL; /* instruction history */
|
||
|
||
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 sim_interval;
|
||
extern int32 sim_int_char;
|
||
extern int32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */
|
||
extern UNIT clk_unit;
|
||
|
||
extern t_stat build_dib_tab (void);
|
||
extern UNIT rom_unit, nvr_unit;
|
||
extern int32 op_ashq (int32 *opnd, int32 *rh, int32 *flg);
|
||
extern int32 op_emul (int32 mpy, int32 mpc, int32 *rh);
|
||
extern int32 op_ediv (int32 *opnd, int32 *rh, int32 *flg);
|
||
extern int32 op_bb_n (int32 *opnd, int32 acc);
|
||
extern int32 op_bb_x (int32 *opnd, int32 newb, int32 acc);
|
||
extern int32 op_extv (int32 *opnd, int32 vfldrp1, int32 acc);
|
||
extern int32 op_ffs (uint32 fld, int32 size);
|
||
extern void op_insv (int32 *opnd, int32 vfldrp1, int32 acc);
|
||
extern int32 op_call (int32 *opnd, t_bool gs, int32 acc);
|
||
extern int32 op_ret (int32 acc);
|
||
extern int32 op_insque (int32 *opnd, int32 acc);
|
||
extern int32 op_remque (int32 *opnd, int32 acc);
|
||
extern int32 op_insqhi (int32 *opnd, int32 acc);
|
||
extern int32 op_insqti (int32 *opnd, int32 acc);
|
||
extern int32 op_remqhi (int32 *opnd, int32 acc);
|
||
extern int32 op_remqti (int32 *opnd, int32 acc);
|
||
extern void op_pushr (int32 *opnd, int32 acc);
|
||
extern void op_popr (int32 *opnd, int32 acc);
|
||
extern int32 op_movc (int32 *opnd, int32 opc, int32 acc);
|
||
extern int32 op_cmpc (int32 *opnd, int32 opc, int32 acc);
|
||
extern int32 op_locskp (int32 *opnd, int32 opc, int32 acc);
|
||
extern int32 op_scnspn (int32 *opnd, int32 opc, int32 acc);
|
||
extern int32 op_chm (int32 *opnd, int32 cc, int32 opc);
|
||
extern int32 op_rei (int32 acc);
|
||
extern void op_ldpctx (int32 acc);
|
||
extern void op_svpctx (int32 acc);
|
||
extern int32 op_probe (int32 *opnd, int32 opc);
|
||
extern int32 op_mtpr (int32 *opnd);
|
||
extern int32 op_mfpr (int32 *opnd);
|
||
extern int32 op_movfd (int32 val);
|
||
extern int32 op_movg (int32 val);
|
||
extern int32 op_mnegfd (int32 val);
|
||
extern int32 op_mnegg (int32 val);
|
||
extern int32 op_cmpfd (int32 h1, int32 l1, int32 h2, int32 l2);
|
||
extern int32 op_cmpg (int32 h1, int32 l1, int32 h2, int32 l2);
|
||
extern int32 op_cvtifdg (int32 val, int32 *rh, int32 opc);
|
||
extern int32 op_cvtfdgi (int32 *opnd, int32 *rh, int32 opc);
|
||
extern int32 op_cvtdf (int32 *opnd);
|
||
extern int32 op_cvtgf (int32 *opnd);
|
||
extern int32 op_cvtfg (int32 *opnd, int32 *rh);
|
||
extern int32 op_addf (int32 *opnd, t_bool sub);
|
||
extern int32 op_addd (int32 *opnd, int32 *rh, t_bool sub);
|
||
extern int32 op_addg (int32 *opnd, int32 *rh, t_bool sub);
|
||
extern int32 op_mulf (int32 *opnd);
|
||
extern int32 op_muld (int32 *opnd, int32 *rh);
|
||
extern int32 op_mulg (int32 *opnd, int32 *rh);
|
||
extern int32 op_divf (int32 *opnd);
|
||
extern int32 op_divd (int32 *opnd, int32 *rh);
|
||
extern int32 op_divg (int32 *opnd, int32 *rh);
|
||
extern int32 op_emodf (int32 *opnd, int32 *intgr, int32 *flg);
|
||
extern int32 op_emodd (int32 *opnd, int32 *rh, int32 *intgr, int32 *flg);
|
||
extern int32 op_emodg (int32 *opnd, int32 *rh, int32 *intgr, int32 *flg);
|
||
extern void op_polyf (int32 *opnd, int32 acc);
|
||
extern void op_polyd (int32 *opnd, int32 acc);
|
||
extern void op_polyg (int32 *opnd, int32 acc);
|
||
extern int32 op_emulate (int32 *opnd, int32 cc, int32 opc, int32 acc);
|
||
extern int32 intexc (int32 vec, int32 cc, int32 ipl, int ei);
|
||
extern int32 Read (uint32 va, int32 lnt, int32 acc);
|
||
extern void Write (uint32 va, int32 val, int32 lnt, int32 acc);
|
||
extern int32 ReadB (uint32 pa);
|
||
extern void WriteB (uint32 pa, int32 val);
|
||
extern int32 Test (uint32 va, int32 acc, int32 *status);
|
||
extern int32 ReadLP (uint32 pa);
|
||
extern int32 eval_int (void);
|
||
extern int32 get_vector (int32 lvl);
|
||
extern void set_map_reg (void);
|
||
extern void rom_wr (int32 pa, int32 val, int32 lnt);
|
||
extern uint16 drom[NUM_INST][MAX_SPEC + 1];
|
||
extern t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||
extern t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||
extern t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||
|
||
t_stat cpu_reset (DEVICE *dptr);
|
||
t_stat cpu_boot (int32 unitno, DEVICE *dptr);
|
||
t_stat cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw);
|
||
t_stat cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw);
|
||
t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||
t_stat cpu_set_extm (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||
t_stat cpu_show_virt (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||
t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||
t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||
int32 get_istr (int32 lnt, int32 acc);
|
||
int32 con_halt (int32 code, int32 cc);
|
||
|
||
/* CPU data structures
|
||
|
||
cpu_dev CPU device descriptor
|
||
cpu_unit CPU unit
|
||
cpu_reg CPU register list
|
||
cpu_mod CPU modifier list
|
||
*/
|
||
|
||
UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK + UNIT_EXTM, INITMEMSIZE) };
|
||
|
||
REG cpu_reg[] = {
|
||
{ HRDATA (PC, R[nPC], 32) },
|
||
{ HRDATA (R0, R[0], 32) },
|
||
{ HRDATA (R1, R[1], 32) },
|
||
{ HRDATA (R2, R[2], 32) },
|
||
{ HRDATA (R3, R[3], 32) },
|
||
{ HRDATA (R4, R[4], 32) },
|
||
{ HRDATA (R5, R[5], 32) },
|
||
{ HRDATA (R6, R[6], 32) },
|
||
{ HRDATA (R7, R[7], 32) },
|
||
{ HRDATA (R8, R[8], 32) },
|
||
{ HRDATA (R9, R[9], 32) },
|
||
{ HRDATA (R10, R[10], 32) },
|
||
{ HRDATA (R11, R[11], 32) },
|
||
{ HRDATA (R12, R[12], 32) },
|
||
{ HRDATA (R13, R[13], 32) },
|
||
{ HRDATA (R14, R[14], 32) },
|
||
{ HRDATA (AP, R[nAP], 32) },
|
||
{ HRDATA (FP, R[nFP], 32) },
|
||
{ HRDATA (SP, R[nSP], 32) },
|
||
{ HRDATA (PSL, PSL, 32) },
|
||
{ HRDATA (CC, PSL, 4) },
|
||
{ HRDATA (KSP, KSP, 32) },
|
||
{ HRDATA (ESP, ESP, 32) },
|
||
{ HRDATA (SSP, SSP, 32) },
|
||
{ HRDATA (USP, USP, 32) },
|
||
{ HRDATA (IS, IS, 32) },
|
||
{ HRDATA (SCBB, SCBB, 32) },
|
||
{ HRDATA (PCBB, PCBB, 32) },
|
||
{ HRDATA (P0BR, P0BR, 32) },
|
||
{ HRDATA (P0LR, P0LR, 22) },
|
||
{ HRDATA (P1BR, P1BR, 32) },
|
||
{ HRDATA (P1LR, P1LR, 22) },
|
||
{ HRDATA (SBR, SBR, 32) },
|
||
{ HRDATA (SLR, SLR, 22) },
|
||
{ HRDATA (SISR, SISR, 16) },
|
||
{ HRDATA (ASTLVL, ASTLVL, 4) },
|
||
{ HRDATA (CADR, CADR, 8) },
|
||
{ HRDATA (MSER, MSER, 8) },
|
||
{ FLDATA (MAPEN, mapen, 0) },
|
||
{ HRDATA (TRPIRQ, trpirq, 8) },
|
||
{ FLDATA (CRDERR, crd_err, 0) },
|
||
{ FLDATA (MEMERR, mem_err, 0) },
|
||
{ FLDATA (HLTPIN, hlt_pin, 0) },
|
||
{ FLDATA (DBGSTOP, dbg_stop, 0), REG_HIDDEN },
|
||
{ BRDATA (PCQ, pcq, 16, 32, PCQ_SIZE), REG_RO+REG_CIRC },
|
||
{ HRDATA (PCQP, pcq_p, 6), REG_HRO },
|
||
{ HRDATA (BADABO, badabo, 32), REG_HRO },
|
||
{ FLDATA (AUTOCON, autcon_enb, 0), REG_HRO },
|
||
{ HRDATA (WRU, sim_int_char, 8) },
|
||
{ NULL } };
|
||
|
||
MTAB cpu_mod[] = {
|
||
{ UNIT_EXTM, UNIT_EXTM, NULL, "EXTENDEDMEMORY", &cpu_set_extm },
|
||
{ UNIT_EXTM, 0, NULL, "NOEXTENDEDMEMORY", &cpu_set_extm },
|
||
{ UNIT_MSIZE, (1u << 24), NULL, "16M", &cpu_set_size },
|
||
{ UNIT_MSIZE, (1u << 25), NULL, "32M", &cpu_set_size },
|
||
{ UNIT_MSIZE, (1u << 25) + (1u << 24), NULL, "48M", &cpu_set_size },
|
||
{ UNIT_MSIZE, (1u << 26), NULL, "64M", &cpu_set_size },
|
||
{ UNIT_MSIZE, (1u << 27), NULL, "128M", &cpu_set_size },
|
||
{ UNIT_MSIZE, (1u << 28), NULL, "256M", &cpu_set_size },
|
||
{ UNIT_MSIZE, (1u << 29), NULL, "512M", &cpu_set_size },
|
||
{ UNIT_CONH, 0, "HALT to SIMH", "SIMHALT", NULL },
|
||
{ UNIT_CONH, UNIT_CONH, "HALT to console", "CONHALT", NULL },
|
||
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "IOSPACE", NULL,
|
||
NULL, &show_iospace },
|
||
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "HISTORY", "HISTORY",
|
||
&cpu_set_hist, &cpu_show_hist },
|
||
{ MTAB_XTD|MTAB_VDV, 1, "AUTOCONFIG", "AUTOCONFIG",
|
||
&set_autocon, &show_autocon },
|
||
{ MTAB_XTD|MTAB_VDV, 0, NULL, "NOAUTOCONFIG",
|
||
&set_autocon, NULL },
|
||
{ MTAB_XTD|MTAB_VDV, 0, NULL, "VIRTUAL", &cpu_show_virt },
|
||
{ 0 } };
|
||
|
||
DEBTAB cpu_deb[] = {
|
||
{ "INTEXC", LOG_CPU_I },
|
||
{ "REI", LOG_CPU_R },
|
||
{ "CONTEXT", LOG_CPU_P },
|
||
{ 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 };
|
||
|
||
t_stat sim_instr (void)
|
||
{
|
||
volatile int32 opc, cc; /* used by setjmp */
|
||
int32 acc; /* set by setjmp */
|
||
int abortval;
|
||
t_stat r;
|
||
|
||
if ((r = build_dib_tab ()) != SCPE_OK) return r; /* build, chk dib_tab */
|
||
cpu_extmem = cpu_unit.flags & UNIT_EXTM; /* external flag */
|
||
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 */
|
||
sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init clock */
|
||
|
||
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 */
|
||
return abortval; } /* return to SCP */
|
||
else if (abortval < 0) { /* mm or rsrv or int */
|
||
int32 i, temp, st1, st2, hsir;
|
||
if ((PSL & PSL_FPD) == 0) { /* FPD? no recovery */
|
||
for (i = 0; i < recqptr; i++) { /* unwind inst */
|
||
int32 rrn, rlnt;
|
||
rrn = recq[i] & 0xF; /* recovery reg# */
|
||
rlnt = DR_LNT ((recq[i] >> 4) & 0x3); /* recovery lnt */
|
||
if (recq[i] & 0x800) R[rrn] = R[rrn] - rlnt;
|
||
else R[rrn] = R[rrn] + rlnt; } }
|
||
recqptr = 0; /* clear queue */
|
||
temp = fault_PC - PC; /* delta PC if needed */
|
||
SETPC (fault_PC); /* restore PC */
|
||
switch (-abortval) { /* case on abort code */
|
||
case SCB_RESIN: case SCB_RESAD: case SCB_RESOP: /* reserved fault */
|
||
if (in_ie) ABORT (STOP_INIE); /* in exc? panic */
|
||
cc = intexc (-abortval, cc, 0, IE_EXC); /* take exception */
|
||
GET_CUR; /* PSL<cur> changed */
|
||
break;
|
||
case SCB_ARITH: /* arithmetic fault */
|
||
if (in_ie) ABORT (STOP_INIE); /* in exc? panic */
|
||
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: case SCB_TNV: /* mem management */
|
||
if (in_ie) { /* in exception? */
|
||
if (PSL & PSL_IS) ABORT (STOP_INIE); /* on is? panic */
|
||
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 */
|
||
if (in_ie) ABORT (STOP_INIE); /* in exception? */
|
||
if (p1 & 0x80) p1 = p1 + mchk_ref; /* mref? set v/p */
|
||
p2 = mchk_va + 4; /* save vap */
|
||
for (i = hsir = 0; i < 16; i++) { /* find hsir */
|
||
if ((SISR >> i) & 1) hsir = i; }
|
||
st1 = ((((uint32) opc) & 0xFF) << 24) |
|
||
(hsir << 16) |
|
||
((CADR & 0xFF) << 8) |
|
||
(MSER & 0xFF);
|
||
st2 = 0x00C07000 + (temp & 0xFF);
|
||
cc = intexc (-abortval, cc, 0, IE_SVE); /* take exception */
|
||
GET_CUR; /* PSL<cur> changed */
|
||
in_ie = 1;
|
||
SP = SP - 20; /* push 5 words */
|
||
Write (SP, 16, L_LONG, WA); /* # bytes */
|
||
Write (SP + 4, p1, L_LONG, WA); /* mcheck type */
|
||
Write (SP + 8, p2, L_LONG, WA); /* address */
|
||
Write (SP + 12, st1, L_LONG, WA); /* state 1 */
|
||
Write (SP + 16, st2, L_LONG, WA); /* state 2 */
|
||
in_ie = 0;
|
||
break;
|
||
case 1: /* interrupt */
|
||
break; /* just proceed */
|
||
default: /* other */
|
||
badabo = abortval; /* save code */
|
||
ABORT (STOP_UNKABO); } /* panic */
|
||
} /* end else */
|
||
|
||
/* Main instruction loop */
|
||
|
||
for ( ;; ) {
|
||
int32 spec, disp, rn, index, numspec;
|
||
int32 vfldrp1, brdisp, flg, mstat;
|
||
int32 i, j, r, rh, temp;
|
||
uint32 va, iad;
|
||
int32 opnd[OPND_SIZE]; /* operand queue */
|
||
|
||
if (cpu_astop) {
|
||
cpu_astop = 0;
|
||
ABORT (SCPE_STOP); }
|
||
|
||
fault_PC = PC;
|
||
recqptr = 0; /* clr recovery q */
|
||
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? */
|
||
cc = con_halt (CON_HLTPIN, cc); /* invoke firmware */
|
||
hlt_pin = 0; /* clear intr */
|
||
trpirq = 0; /* clear everything */
|
||
continue; } /* continue */
|
||
if (temp == IPL_MEMERR) { /* mem error? */
|
||
vec = SCB_MEMERR;
|
||
mem_err = 0; }
|
||
else if (temp == IPL_CRDERR) { /* CRD error? */
|
||
vec = SCB_CRDERR;
|
||
crd_err = 0; }
|
||
else if (temp > IPL_HMAX) { /* error req lvl? */
|
||
ABORT (STOP_UIPL); } /* unknown intr */
|
||
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) cc = intexc (vec, cc, temp, IE_INT);/* take intr */
|
||
GET_CUR; } /* set cur mode */
|
||
else trpirq = 0; /* clear everything */
|
||
SET_IRQL; /* eval interrupts */
|
||
continue; }
|
||
|
||
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) PSL = PSL | PSL_TP; /* if T, set TP */
|
||
|
||
if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */
|
||
ABORT (STOP_IBKPT); } /* stop simulation */
|
||
|
||
sim_interval = sim_interval - 1; /* count instr */
|
||
GET_ISTR (opc, L_BYTE); /* get opcode */
|
||
if (opc == 0xFD) { /* 2 byte op? */
|
||
GET_ISTR (opc, L_BYTE); /* get second byte */
|
||
opc = opc | 0x100; } /* flag */
|
||
numspec = drom[opc][0]; /* get # specs */
|
||
if (PSL & PSL_FPD) {
|
||
if ((numspec & DR_F) == 0) RSVD_INST_FAULT; }
|
||
else { numspec = numspec & DR_NSPMASK; /* get # specifiers */
|
||
|
||
/* Specifier flows. Operands are parsed and placed into queue opnd.
|
||
|
||
r.bwl opnd[j] = value of operand
|
||
r.q opnd[j:j+1] = value of operand
|
||
a.bwlq opnd[j] = address of operand
|
||
m.bwl opnd[j] = value of operand
|
||
m.q opnd[j:j+1] = value of operand
|
||
w.bwlq 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));
|
||
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|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;
|
||
|
||
/* 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[rnplus1];
|
||
case GRN|WB: case GRN|WW: case GRN|WL: case GRN|WQ:
|
||
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[rnplus1];
|
||
break;
|
||
|
||
/* Register deferred, autodecrement */
|
||
|
||
case RGD|VB:
|
||
case RGD|WB: case RGD|WW: case RGD|WL: case RGD|WQ:
|
||
opnd[j++] = OP_MEM;
|
||
case RGD|AB: case RGD|AW: case RGD|AL: case RGD|AQ:
|
||
CHECK_FOR_PC;
|
||
va = opnd[j++] = R[rn];
|
||
break;
|
||
case ADC|VB:
|
||
case ADC|WB: case ADC|WW: case ADC|WL: case ADC|WQ:
|
||
opnd[j++] = OP_MEM;
|
||
case ADC|AB: case ADC|AW: case ADC|AL: case ADC|AQ:
|
||
CHECK_FOR_PC;
|
||
va = opnd[j++] = R[rn] = R[rn] - DR_LNT (disp);
|
||
recq[recqptr++] = RECW (disp);
|
||
break;
|
||
case ADC|RB: case ADC|RW: case ADC|RL: case ADC|RF:
|
||
case ADC|MB: case ADC|MW: case ADC|ML:
|
||
R[rn] = R[rn] - (DR_LNT (disp));
|
||
recq[recqptr++] = RECW (disp);
|
||
case RGD|RB: case RGD|RW: case RGD|RL: case RGD|RF:
|
||
case RGD|MB: case RGD|MW: case RGD|ML:
|
||
CHECK_FOR_PC;
|
||
opnd[j++] = Read (va = R[rn], DR_LNT (disp), RA);
|
||
break;
|
||
case ADC|RQ: case ADC|RD: case ADC|RG: case ADC|MQ:
|
||
R[rn] = R[rn] - 8;
|
||
recq[recqptr++] = RECW (disp);
|
||
case RGD|RQ: case RGD|RD: case RGD|RG: case RGD|MQ:
|
||
CHECK_FOR_PC;
|
||
opnd[j++] = Read (va = R[rn], L_LONG, RA);
|
||
opnd[j++] = Read (R[rn] + 4, L_LONG, RA);
|
||
break;
|
||
|
||
/* Autoincrement */
|
||
|
||
case AIN|VB:
|
||
case AIN|WB: case AIN|WW: case AIN|WL: case AIN|WQ:
|
||
/* CHECK_FOR_PC; */
|
||
opnd[j++] = OP_MEM;
|
||
case AIN|AB: case AIN|AW: case AIN|AL: case AIN|AQ:
|
||
va = opnd[j++] = R[rn];
|
||
if (rn == nPC) {
|
||
if (DR_LNT (disp) == L_QUAD) {
|
||
GET_ISTR (temp, L_LONG);
|
||
GET_ISTR (temp, L_LONG); }
|
||
else GET_ISTR (temp, DR_LNT (disp)); }
|
||
else {
|
||
R[rn] = R[rn] + DR_LNT (disp);
|
||
recq[recqptr++] = RECW (disp); }
|
||
break;
|
||
case AIN|MB: case AIN|MW: case AIN|ML:
|
||
/* CHECK_FOR_PC; */
|
||
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++] = RECW (disp); }
|
||
break;
|
||
case AIN|MQ:
|
||
/* CHECK_FOR_PC; */
|
||
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++] = RECW (disp); }
|
||
break;
|
||
|
||
/* Autoincrement deferred */
|
||
|
||
case AID|VB:
|
||
case AID|WB: case AID|WW: case AID|WL: case AID|WQ:
|
||
opnd[j++] = OP_MEM;
|
||
case AID|AB: case AID|AW: case AID|AL: case AID|AQ:
|
||
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++] = RECW (AID|RL); }
|
||
break;
|
||
case AID|RB: case AID|RW: case AID|RL: case AID|RF:
|
||
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++] = RECW (AID|RL); }
|
||
opnd[j++] = Read (va, DR_LNT (disp), RA);
|
||
break;
|
||
case AID|RQ: case AID|RD: case AID|RG: 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++] = RECW (AID|RL); }
|
||
opnd[j++] = Read (va, L_LONG, RA);
|
||
opnd[j++] = Read (va + 4, L_LONG, RA);
|
||
break;
|
||
|
||
/* Byte displacement */
|
||
|
||
case BDP|VB:
|
||
case BDP|WB: case BDP|WW: case BDP|WL: case BDP|WQ:
|
||
opnd[j++] = OP_MEM;
|
||
case BDP|AB: case BDP|AW: case BDP|AL: case BDP|AQ:
|
||
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:
|
||
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), RA);
|
||
break;
|
||
case BDP|RQ: case BDP|RD: case BDP|RG: case BDP|MQ:
|
||
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;
|
||
|
||
/* Byte displacement deferred */
|
||
|
||
case BDD|VB:
|
||
case BDD|WB: case BDD|WW: case BDD|WL: case BDD|WQ:
|
||
opnd[j++] = OP_MEM;
|
||
case BDD|AB: case BDD|AW: case BDD|AL: case BDD|AQ:
|
||
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:
|
||
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), RA);
|
||
break;
|
||
case BDD|RQ: case BDD|RD: case BDD|RG: 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, RA);
|
||
opnd[j++] = Read (va + 4, L_LONG, RA);
|
||
break;
|
||
|
||
/* Word displacement */
|
||
|
||
case WDP|VB:
|
||
case WDP|WB: case WDP|WW: case WDP|WL: case WDP|WQ:
|
||
opnd[j++] = OP_MEM;
|
||
case WDP|AB: case WDP|AW: case WDP|AL: case WDP|AQ:
|
||
GET_ISTR (temp, L_WORD);
|
||
va = opnd[j++] = R[rn] + SXTW (temp);
|
||
break;
|
||
case WDP|MB: case WDP|MW: case WDP|ML:
|
||
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|MQ: 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;
|
||
|
||
/* Word displacement deferred */
|
||
|
||
case WDD|VB:
|
||
case WDD|WB: case WDD|WW: case WDD|WL: case WDD|WQ:
|
||
opnd[j++] = OP_MEM;
|
||
case WDD|AB: case WDD|AW: case WDD|AL: case WDD|AQ:
|
||
GET_ISTR (temp, L_WORD);
|
||
iad = R[rn] + SXTW (temp);
|
||
va = opnd[j++] = Read (iad, L_LONG, RA);
|
||
break;
|
||
case WDD|MB: case WDD|MW: case WDD|ML:
|
||
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|MQ: 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;
|
||
|
||
/* Longword displacement */
|
||
|
||
case LDP|VB:
|
||
case LDP|WB: case LDP|WW: case LDP|WL: case LDP|WQ:
|
||
opnd[j++] = OP_MEM;
|
||
case LDP|AB: case LDP|AW: case LDP|AL: case LDP|AQ:
|
||
GET_ISTR (temp, L_LONG);
|
||
va = opnd[j++] = R[rn] + temp;
|
||
break;
|
||
case LDP|MB: case LDP|MW: case LDP|ML:
|
||
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|MQ: 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;
|
||
|
||
/* Longword displacement deferred */
|
||
|
||
case LDD|VB:
|
||
case LDD|WB: case LDD|WW: case LDD|WL: case LDD|WQ:
|
||
opnd[j++] = OP_MEM;
|
||
case LDD|AB: case LDD|AW: case LDD|AL: case LDD|AQ:
|
||
GET_ISTR (temp, L_LONG);
|
||
iad = R[rn] + temp;
|
||
va = opnd[j++] = Read (iad, L_LONG, RA);
|
||
break;
|
||
case LDD|MB: case LDD|MW: case LDD|ML:
|
||
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|MQ: 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;
|
||
|
||
/* Index */
|
||
|
||
case IDX|VB:
|
||
case IDX|WB: case IDX|WW: case IDX|WL: case IDX|WQ:
|
||
case IDX|AB: case IDX|AW: case IDX|AL: case IDX|AQ:
|
||
case IDX|MB: case IDX|MW: case IDX|ML: case IDX|MQ:
|
||
case IDX|RB: case IDX|RW: case IDX|RL: case IDX|RQ:
|
||
case IDX|RF: case IDX|RD: case IDX|RG:
|
||
index = R[rn] << (disp & 03);
|
||
CHECK_FOR_PC;
|
||
GET_ISTR (spec, L_BYTE);
|
||
rn = spec & RGMASK;
|
||
switch (spec & ~RGMASK) {
|
||
case ADC:
|
||
R[rn] = R[rn] - DR_LNT (disp);
|
||
recq[recqptr++] = RECW (ADC | (disp & DR_LNMASK));
|
||
case RGD:
|
||
CHECK_FOR_PC;
|
||
index = index + R[rn];
|
||
break;
|
||
case AIN:
|
||
CHECK_FOR_PC;
|
||
index = index + R[rn];
|
||
R[rn] = R[rn] + DR_LNT (disp);
|
||
recq[recqptr++] = RECW (AIN | (disp & DR_LNMASK));
|
||
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++] = RECW (AID|RL); }
|
||
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 & 0xF) { /* case disp type */
|
||
case WB: case WW: case WL: case WQ:
|
||
opnd[j++] = OP_MEM;
|
||
case AB: case AW: case AL: case AQ:
|
||
va = opnd[j++] = index;
|
||
break;
|
||
case MB: case MW: case ML:
|
||
case RB: case RW: case RL:
|
||
opnd[j++] = Read (va = index, DR_LNT (disp), RA);
|
||
break;
|
||
case RQ: case MQ:
|
||
opnd[j++] = Read (va = index, L_LONG, RA);
|
||
opnd[j++] = Read (index + 4, L_LONG, RA);
|
||
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) {
|
||
hst[hst_p].iPC = fault_PC;
|
||
hst[hst_p].PSL = PSL | cc;
|
||
hst[hst_p].opc = opc;
|
||
hst[hst_p].brdest = brdisp + PC;
|
||
for (i = 0; i < (numspec & DR_NSPMASK); i++)
|
||
hst[hst_p].opnd[i] = opnd[i];
|
||
hst_p = hst_p + 1;
|
||
if (hst_p >= hst_lnt) hst_p = 0;
|
||
}
|
||
|
||
/* 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:
|
||
WRITE_B (0); /* store result */
|
||
CC_ZZ1P; /* set cc's */
|
||
break;
|
||
case CLRW:
|
||
WRITE_W (0); /* store result */
|
||
CC_ZZ1P; /* set cc's */
|
||
break;
|
||
case CLRL:
|
||
WRITE_L (0); /* store result */
|
||
CC_ZZ1P; /* set cc's */
|
||
break;
|
||
case CLRQ:
|
||
WRITE_Q (0, 0); /* store result */
|
||
CC_ZZ1P; /* set cc's */
|
||
break;
|
||
|
||
/* Single operand instructions with source, read only - TSTx src.rx
|
||
|
||
opnd[0] = source
|
||
*/
|
||
|
||
case TSTB:
|
||
CC_IIZZ_B (op0); /* set cc's */
|
||
break;
|
||
case TSTW:
|
||
CC_IIZZ_W (op0); /* set cc's */
|
||
break;
|
||
case TSTL:
|
||
CC_IIZZ_L (op0); /* set cc's */
|
||
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:
|
||
WRITE_B (op0); /* result */
|
||
CC_IIZP_B (op0); /* set cc's */
|
||
break;
|
||
case MOVW: case MOVZBW:
|
||
WRITE_W (op0); /* result */
|
||
CC_IIZP_W (op0); /* set cc's */
|
||
break;
|
||
case MOVL: case MOVZBL: case MOVZWL:
|
||
case MOVAB: case MOVAW: case MOVAL: case MOVAQ:
|
||
WRITE_L (op0); /* result */
|
||
CC_IIZP_L (op0); /* 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) { /* reg? ADDW2 */
|
||
temp = R[op1];
|
||
r = R[op1] = (op0 + temp) & WMASK; }
|
||
else {
|
||
if (op2 & 1) RSVD_OPND_FAULT; /* mem? chk align */
|
||
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 */
|
||
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) cc = cc | CC_C; /* special case */
|
||
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) cc = cc | CC_C; /* special case */
|
||
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)) { V_INTOV; } /* chk overflow */
|
||
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; }
|
||
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; }
|
||
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 ((op0 == LMASK) && (op1 == LSIGN)) { /* overflow? */
|
||
r = op1;
|
||
temp = CC_V;
|
||
INTOV; }
|
||
else {
|
||
r = op1 / op0; /* ok, divide */
|
||
temp = 0; }
|
||
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) r = (op1 & LSIGN)? -1: 0; /* sc > 31? */
|
||
else r = op1 >> temp; /* shift */
|
||
WRITE_L (r); /* store result */
|
||
CC_IIZZ_L (r); /* set cc's */
|
||
break; }
|
||
else {
|
||
if (op0 > 31) r = temp = 0; /* sc > 31? */
|
||
else {
|
||
r = ((uint32) op1) << op0; /* shift */
|
||
temp = r >> op0; } /* shift back */
|
||
WRITE_L (r); /* store result */
|
||
CC_IIZZ_L (r); /* set cc's */
|
||
if (op1 != temp) { V_INTOV; } } /* bits lost? */
|
||
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) { V_INTOV; } /* if ovflo, set */
|
||
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) Read (op6, L_LONG, WA); /* wtest remainder */
|
||
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) { INTOV; } } /* if ovf+IV, set trap */
|
||
if (op3 >= 0) R[op3] = r; /* store quotient */
|
||
else Write (op4, r, L_LONG, WA);
|
||
if (op5 >= 0) R[op5] = rh; /* store remainder */
|
||
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 */
|
||
if ((PC == fault_PC) && (PSL_GETIPL (PSL) == 0x1F))
|
||
ABORT (STOP_LOOP);
|
||
break;
|
||
|
||
case BRW:
|
||
BRANCHW (brdisp); /* branch */
|
||
if ((PC == fault_PC) && (PSL_GETIPL (PSL) == 0x1F))
|
||
ABORT (STOP_LOOP);
|
||
break;
|
||
|
||
case BSBB:
|
||
Write (SP - 4, PC, L_LONG, WA); /* push PC on stk */
|
||
SP = SP - 4; /* decr stk ptr */
|
||
BRANCHB (brdisp); /* branch */
|
||
break;
|
||
|
||
case BSBW:
|
||
Write (SP - 4, PC, L_LONG, WA); /* push PC on stk */
|
||
SP = SP - 4; /* decr stk ptr */
|
||
BRANCHW (brdisp); /* branch */
|
||
break;
|
||
|
||
case BGEQ:
|
||
if (!(cc & CC_N)) BRANCHB (brdisp); /* br if N = 0 */
|
||
break;
|
||
case BLSS:
|
||
if (cc & CC_N) BRANCHB (brdisp); /* br if N = 1 */
|
||
break;
|
||
case BNEQ:
|
||
if (!(cc & CC_Z)) BRANCHB (brdisp); /* br if Z = 0 */
|
||
break;
|
||
case BEQL:
|
||
if (cc & CC_Z) BRANCHB (brdisp); /* br if Z = 1 */
|
||
break;
|
||
case BVC:
|
||
if (!(cc & CC_V)) BRANCHB (brdisp); /* br if V = 0 */
|
||
break;
|
||
case BVS:
|
||
if (cc & CC_V) BRANCHB (brdisp); /* br if V = 1 */
|
||
break;
|
||
case BGEQU:
|
||
if (!(cc & CC_C)) BRANCHB (brdisp); /* br if C = 0 */
|
||
break;
|
||
case BLSSU:
|
||
if (cc & CC_C) BRANCHB (brdisp); /* br if C = 1 */
|
||
break;
|
||
case BGTR:
|
||
if (!(cc & (CC_N | CC_Z))) BRANCHB (brdisp); /* br if N | Z = 0 */
|
||
break;
|
||
case BLEQ:
|
||
if (cc & (CC_N | CC_Z)) BRANCHB (brdisp); /* br if N | Z = 1 */
|
||
break;
|
||
case BGTRU:
|
||
if (!(cc & (CC_C | CC_Z))) BRANCHB (brdisp); /* br if C | Z = 0 */
|
||
break;
|
||
case BLEQU:
|
||
if (cc & (CC_C | CC_Z)) BRANCHB (brdisp); /* br if C | Z = 1 */
|
||
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 */
|
||
case JMP:
|
||
JUMP (op0); /* jump */
|
||
break;
|
||
|
||
case RSB:
|
||
temp = Read (SP, L_LONG, RA); /* get top of stk */
|
||
SP = SP + 4; /* incr stk ptr */
|
||
JUMP (temp);
|
||
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) BRANCHB (brdisp); /* if >= 0, branch */
|
||
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) BRANCHB (brdisp); /* if >= 0, branch */
|
||
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) BRANCHB (brdisp); /* if < lim, branch */
|
||
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) BRANCHB (brdisp); /* if < lim, branch */
|
||
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 (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 (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 (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) JUMP (PC + ((op2 + 1) * 2)); /* r > limit (unsgnd)? */
|
||
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) JUMP (PC + ((op2 + 1) * 2)); /* r > limit (unsgnd)? */
|
||
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)) BRANCHB (brdisp); /* br if bit set */
|
||
break;
|
||
case BBC:
|
||
if (!op_bb_n (opnd, acc)) BRANCHB (brdisp); /* br if bit clr */
|
||
break;
|
||
case BBSS: case BBSSI:
|
||
if (op_bb_x (opnd, 1, acc)) BRANCHB (brdisp); /* br if set, set */
|
||
break;
|
||
case BBCC: case BBCCI:
|
||
if (!op_bb_x (opnd, 0, acc)) BRANCHB (brdisp); /* br if clr, clr*/
|
||
break;
|
||
case BBSC:
|
||
if (op_bb_x (opnd, 0, acc)) BRANCHB (brdisp); /* br if clr, set */
|
||
break;
|
||
case BBCS:
|
||
if (!op_bb_x (opnd, 1, acc)) BRANCHB (brdisp); /* br if set, clr */
|
||
break;
|
||
case BLBS:
|
||
if (op0 & 1) BRANCHB (brdisp); /* br if bit set */
|
||
break;
|
||
case BLBC:
|
||
if ((op0 & 1) == 0) BRANCHB (brdisp); /* br if bit clear */
|
||
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 */
|
||
break;
|
||
case FFC:
|
||
r = op_extv (opnd, vfldrp1, acc); /* get field */
|
||
r = r ^ byte_mask[op1]; /* invert bits */
|
||
temp = op_ffs (r, op1); /* find first 1 */
|
||
WRITE_L (op0 + temp); /* store result */
|
||
cc = r? 0: CC_Z; /* set cc's */
|
||
break;
|
||
|
||
/* Insert field instruction - insv src.rl,pos.rb,size.rl,base.wb
|
||
|
||
opnd[0] = source
|
||
opnd[1] = position
|
||
opnd[2] = size
|
||
opnd[3] = register number/memory flag
|
||
opnd[4] = register content/memory address
|
||
*/
|
||
|
||
case INSV:
|
||
op_insv (opnd, vfldrp1, acc); /* insert field */
|
||
break;
|
||
|
||
/* Call and return - call? arg.rx,proc.ab
|
||
|
||
opnd[0] = argument
|
||
opnd[1] = procedure address
|
||
*/
|
||
|
||
case CALLS:
|
||
cc = op_call (opnd, TRUE, acc);
|
||
break;
|
||
case CALLG:
|
||
cc = op_call (opnd, FALSE, acc);
|
||
break;
|
||
case RET:
|
||
cc = op_ret (acc);
|
||
break;
|
||
|
||
/* Miscellaneous instructions */
|
||
|
||
case HALT:
|
||
if (PSL & PSL_CUR) RSVD_INST_FAULT; /* not kern? rsvd inst */
|
||
else 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);
|
||
cc = intexc (SCB_BPT, cc, 0, IE_EXC);
|
||
GET_CUR;
|
||
break;
|
||
case XFC:
|
||
SETPC (fault_PC);
|
||
cc = intexc (SCB_XFC, cc, 0, IE_EXC);
|
||
GET_CUR;
|
||
break;
|
||
case BISPSW:
|
||
if (opnd[0] & PSW_MBZ) RSVD_OPND_FAULT;
|
||
PSL = PSL | (opnd[0] & ~CC_MASK);
|
||
cc = cc | (opnd[0] & CC_MASK);
|
||
break;
|
||
case BICPSW:
|
||
if (opnd[0] & PSW_MBZ) RSVD_OPND_FAULT;
|
||
PSL = PSL & ~opnd[0];
|
||
cc = cc & ~opnd[0];
|
||
break;
|
||
case MOVPSL:
|
||
r = PSL | cc;
|
||
WRITE_L (r);
|
||
break;
|
||
case PUSHR:
|
||
op_pushr (opnd, acc);
|
||
break;
|
||
case POPR:
|
||
op_popr (opnd, acc);
|
||
break;
|
||
case INDEX:
|
||
if ((op0 < op1) || (op0 > op2)) SET_TRAP (TRAP_SUBSCR);
|
||
r = (op0 + op4) * op3;
|
||
WRITE_L (r);
|
||
CC_IIZZ_L (r);
|
||
break;
|
||
|
||
/* Queue and interlocked queue */
|
||
|
||
case INSQUE:
|
||
cc = op_insque (opnd, acc);
|
||
break;
|
||
case REMQUE:
|
||
cc = op_remque (opnd, acc);
|
||
break;
|
||
case INSQHI:
|
||
cc = op_insqhi (opnd, acc);
|
||
break;
|
||
case INSQTI:
|
||
cc = op_insqti (opnd, acc);
|
||
break;
|
||
case REMQHI:
|
||
cc = op_remqhi (opnd, acc);
|
||
break;
|
||
case REMQTI:
|
||
cc = op_remqti (opnd, acc);
|
||
break;
|
||
|
||
/* String instructions */
|
||
|
||
case MOVC3:
|
||
case MOVC5:
|
||
cc = op_movc (opnd, opc & 4, acc);
|
||
break;
|
||
case CMPC3:
|
||
case CMPC5:
|
||
cc = op_cmpc (opnd, opc & 4, acc);
|
||
break;
|
||
case LOCC:
|
||
case SKPC:
|
||
cc = op_locskp (opnd, opc & 1, acc);
|
||
break;
|
||
case SCANC:
|
||
case SPANC:
|
||
cc = op_scnspn (opnd, opc & 1, acc);
|
||
break;
|
||
|
||
/* Floating point instructions */
|
||
|
||
case TSTF: case TSTD:
|
||
r = op_movfd (op0);
|
||
CC_IIZZ_FP (r);
|
||
break;
|
||
case TSTG:
|
||
r = op_movg (op0);
|
||
CC_IIZZ_FP (r);
|
||
break;
|
||
|
||
case MOVF:
|
||
r = op_movfd (op0);
|
||
WRITE_L (r);
|
||
CC_IIZP_FP (r);
|
||
break;
|
||
case MOVD:
|
||
if ((r = op_movfd (op0)) == 0) op1 = 0;
|
||
WRITE_Q (r, op1);
|
||
CC_IIZP_FP (r);
|
||
break;
|
||
case MOVG:
|
||
if ((r = op_movg (op0)) == 0) op1 = 0;
|
||
WRITE_Q (r, op1);
|
||
CC_IIZP_FP (r);
|
||
break;
|
||
|
||
case MNEGF:
|
||
r = op_mnegfd (op0);
|
||
WRITE_L (r);
|
||
CC_IIZZ_FP (r);
|
||
break;
|
||
case MNEGD:
|
||
if ((r = op_mnegfd (op0)) == 0) op1 = 0;
|
||
WRITE_Q (r, op1);
|
||
CC_IIZZ_FP (r);
|
||
break;
|
||
case MNEGG:
|
||
if ((r = op_mnegg (op0)) == 0) op1 = 0;
|
||
WRITE_Q (r, op1);
|
||
CC_IIZZ_FP (r);
|
||
break;
|
||
|
||
case CMPF:
|
||
cc = op_cmpfd (op0, 0, op1, 0);
|
||
break;
|
||
case CMPD:
|
||
cc = op_cmpfd (op0, op1, op2, op3);
|
||
break;
|
||
case CMPG:
|
||
cc = op_cmpg (op0, op1, op2, op3);
|
||
break;
|
||
|
||
case CVTBF:
|
||
r = op_cvtifdg (SXTB (op0), NULL, opc);
|
||
WRITE_L (r);
|
||
CC_IIZZ_FP (r);
|
||
break;
|
||
case CVTWF:
|
||
r = op_cvtifdg (SXTW (op0), NULL, opc);
|
||
WRITE_L (r);
|
||
CC_IIZZ_FP (r);
|
||
break;
|
||
case CVTLF:
|
||
r = op_cvtifdg (op0, NULL, opc);
|
||
WRITE_L (r);
|
||
CC_IIZZ_FP (r);
|
||
break;
|
||
case CVTBD: case CVTBG:
|
||
r = op_cvtifdg (SXTB (op0), &rh, opc);
|
||
WRITE_Q (r, rh);
|
||
CC_IIZZ_FP (r);
|
||
break;
|
||
case CVTWD: case CVTWG:
|
||
r = op_cvtifdg (SXTW (op0), &rh, opc);
|
||
WRITE_Q (r, rh);
|
||
CC_IIZZ_FP (r);
|
||
break;
|
||
case CVTLD: case CVTLG:
|
||
r = op_cvtifdg (op0, &rh, opc);
|
||
WRITE_Q (r, rh);
|
||
CC_IIZZ_FP (r);
|
||
break;
|
||
|
||
case CVTFB: case CVTDB: case CVTGB:
|
||
r = op_cvtfdgi (opnd, &temp, opc) & BMASK;
|
||
WRITE_B (r);
|
||
CC_IIZZ_B (r);
|
||
cc = cc | temp;
|
||
break;
|
||
case CVTFW: case CVTDW: case CVTGW:
|
||
r = op_cvtfdgi (opnd, &temp, opc) & WMASK;
|
||
WRITE_W (r);
|
||
CC_IIZZ_W (r);
|
||
cc = cc | temp;
|
||
break;
|
||
case CVTFL: case CVTDL: case CVTGL:
|
||
case CVTRFL: case CVTRDL: case CVTRGL:
|
||
r = op_cvtfdgi (opnd, &temp, opc) & LMASK;
|
||
WRITE_L (r);
|
||
CC_IIZZ_L (r);
|
||
cc = cc | temp;
|
||
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) || ((op1 & 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) || ((op1 & 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_LONG, 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_LONG, WA);
|
||
if (op5 >= 0) R[op5] = temp;
|
||
else Write (op6, temp, L_LONG, WA);
|
||
WRITE_Q (r, rh);
|
||
CC_IIZZ_FP (r);
|
||
if (flg) { V_INTOV; }
|
||
break;
|
||
|
||
/* POLY */
|
||
|
||
case POLYF:
|
||
op_polyf (opnd, acc);
|
||
CC_IIZZ_FP (R[0]);
|
||
break;
|
||
|
||
case POLYD:
|
||
op_polyd (opnd, acc);
|
||
CC_IIZZ_FP (R[0]);
|
||
break;
|
||
|
||
case POLYG:
|
||
op_polyg (opnd, acc);
|
||
CC_IIZZ_FP (R[0]);
|
||
break;
|
||
|
||
/* Operating system instructions */
|
||
|
||
case CHMK: case CHME: case CHMS: case CHMU:
|
||
cc = op_chm (opnd, cc, opc); /* CHMx */
|
||
GET_CUR; /* update cur mode */
|
||
SET_IRQL; /* update intreq */
|
||
break;
|
||
case REI:
|
||
cc = op_rei (acc); /* REI */
|
||
GET_CUR; /* update cur mode */
|
||
SET_IRQL; /* update intreq */
|
||
break;
|
||
case LDPCTX:
|
||
op_ldpctx (acc);
|
||
break;
|
||
case SVPCTX:
|
||
op_svpctx (acc);
|
||
break;
|
||
case PROBER: case PROBEW:
|
||
cc = (cc & CC_C) | op_probe (opnd, opc & 1);
|
||
break;
|
||
case MTPR:
|
||
cc = (cc & CC_C) | op_mtpr (opnd);
|
||
SET_IRQL; /* update intreq */
|
||
break;
|
||
case MFPR:
|
||
r = op_mfpr (opnd);
|
||
WRITE_L (r);
|
||
CC_IIZP_L (r);
|
||
break;
|
||
|
||
/* Emulated instructions */
|
||
|
||
case CVTPL:
|
||
opnd[2] = (opnd[2] >= 0)? ~opnd[2]: opnd[3];
|
||
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_emulate (opnd, cc, opc, acc);
|
||
break;
|
||
default:
|
||
RSVD_INST_FAULT;
|
||
break; } /* end case op */
|
||
} /* end for */
|
||
ABORT (STOP_UNKNOWN);
|
||
} /* 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.
|
||
*/
|
||
|
||
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) ibufl = ReadLP (ppc); /* fill low */
|
||
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) val = (ibufl >> (bo << 3)) & BMASK; /* byte? */
|
||
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;
|
||
}
|
||
|
||
/* Console entry */
|
||
|
||
int32 con_halt (int32 code, int32 cc)
|
||
{
|
||
int32 temp;
|
||
|
||
conpc = PC; /* save PC */
|
||
conpsl = ((PSL | cc) & 0xFFFF00FF) | CON_HLTINS; /* PSL, param */
|
||
temp = (PSL >> PSL_V_CUR) & 0x7; /* get is'cur */
|
||
if (temp > 4) conpsl = conpsl | CON_BADPSL; /* invalid? */
|
||
else STK[temp] = SP; /* save stack */
|
||
if (mapen) conpsl = conpsl | CON_MAPON; /* mapping on? */
|
||
mapen = 0; /* turn off map */
|
||
SP = IS; /* set SP from IS */
|
||
PSL = PSL_IS | PSL_IPL1F; /* PSL = 41F0000 */
|
||
JUMP (ROMBASE); /* PC = 20040000 */
|
||
return 0; /* new cc = 0 */
|
||
}
|
||
|
||
/* Reset */
|
||
|
||
t_stat cpu_reset (DEVICE *dptr)
|
||
{
|
||
mem_err = 0;
|
||
crd_err = 0;
|
||
hlt_pin = 0;
|
||
PSL = PSL_IS | PSL_IPL1F;
|
||
SISR = 0;
|
||
ASTLVL = 4;
|
||
MSER = 0;
|
||
CADR = 0;
|
||
mapen = 0;
|
||
if (M == NULL) M = calloc (((uint32) MEMSIZE) >> 2, sizeof (int32));
|
||
if (M == NULL) return SCPE_MEM;
|
||
pcq_r = find_reg ("PCQ", NULL, dptr);
|
||
if (pcq_r) pcq_r->qptr = 0;
|
||
else return SCPE_IERR;
|
||
sim_brk_types = sim_brk_dflt = SWMASK ('E');
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Bootstrap */
|
||
|
||
t_stat cpu_boot (int32 unitno, DEVICE *dptr)
|
||
{
|
||
extern int32 clk_csr;
|
||
extern t_stat qba_powerup (void);
|
||
extern t_stat sysd_powerup (void);
|
||
extern t_stat todr_powerup (void);
|
||
extern uint32 *rom;
|
||
extern t_stat load_cmd (int32 flag, char *cptr);
|
||
extern FILE *sim_log;
|
||
t_stat r;
|
||
|
||
qba_powerup ();
|
||
sysd_powerup ();
|
||
todr_powerup ();
|
||
PC = ROMBASE;
|
||
PSL = PSL_IS | PSL_IPL1F;
|
||
conpc = 0;
|
||
conpsl = PSL_IS | PSL_IPL1F | CON_PWRUP;
|
||
if (rom == NULL) return SCPE_IERR;
|
||
if (*rom == 0) { /* no boot? */
|
||
if (cpu_unit.flags & UNIT_EXTM) { /* KA655X? */
|
||
printf ("Loading boot code from ka655x.bin\n");
|
||
if (sim_log) fprintf (sim_log,
|
||
"Loading boot code from ka655x.bin\n");
|
||
r = load_cmd (0, "-R ka655x.bin"); }
|
||
else { /* KA655 */
|
||
printf ("Loading boot code from ka655.bin\n");
|
||
if (sim_log) fprintf (sim_log,
|
||
"Loading boot code from ka655.bin\n");
|
||
r = load_cmd (0, "-R ka655.bin"); }
|
||
if (r != SCPE_OK) return r; }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* 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')) addr = Test (addr, RD, &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')) addr = Test (addr, RD, &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 (addr, (int32) val, L_BYTE);
|
||
return SCPE_OK; }
|
||
return SCPE_NXM;
|
||
}
|
||
|
||
/* Enable/disable extended physical memory */
|
||
|
||
t_stat cpu_set_extm (UNIT *uptr, int32 val, char *cptr, void *desc)
|
||
{
|
||
if ((val == 0) && (MEMSIZE > MAXMEMSIZE)) return SCPE_ARG;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Memory allocation */
|
||
|
||
t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
|
||
{
|
||
int32 mc = 0;
|
||
uint32 i, clim;
|
||
uint32 *nM = NULL;
|
||
|
||
if ((val <= 0) ||
|
||
(val > ((uptr->flags & UNIT_EXTM)? MAXMEMSIZE_X: MAXMEMSIZE))) 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 = calloc (val >> 2, sizeof (uint32));
|
||
if (nM == NULL) return SCPE_MEM;
|
||
clim = (uint32) ((((uint32) val) < MEMSIZE)? val: MEMSIZE);
|
||
for (i = 0; i < clim; i = i + 4) nM[i >> 2] = M[i >> 2];
|
||
free (M);
|
||
M = nM;
|
||
MEMSIZE = val;
|
||
return SCPE_OK; }
|
||
|
||
/* Virtual address translation */
|
||
|
||
t_stat cpu_show_virt (UNIT *uptr, int32 val, char *cptr, void *desc)
|
||
{
|
||
t_stat r;
|
||
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 == NULL) return SCPE_ARG;
|
||
va = (uint32) get_uint (cptr, 16, 0xFFFFFFFF, &r);
|
||
if (r != SCPE_OK) return SCPE_ARG;
|
||
pa = Test (va, RD, &st);
|
||
if (st == PR_OK) printf ("Virtual %-X = physical %-X\n", va, pa);
|
||
else printf ("Virtual %-X: %s\n", va, mm_str[st]);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Set history */
|
||
|
||
t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc)
|
||
{
|
||
int32 i, lnt;
|
||
t_stat r;
|
||
|
||
if (cptr == NULL) {
|
||
for (i = 0; i < hst_lnt; i++) hst[i].iPC = 0;
|
||
hst_p = 0;
|
||
return SCPE_OK; }
|
||
lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r);
|
||
if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG;
|
||
hst_p = 0;
|
||
if (hst_lnt) {
|
||
free (hst);
|
||
hst_lnt = 0;
|
||
hst = NULL; }
|
||
if (lnt) {
|
||
hst = calloc (sizeof (struct InstHistory), lnt);
|
||
if (hst == NULL) return SCPE_MEM;
|
||
hst_lnt = lnt; }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Show history */
|
||
|
||
t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc)
|
||
{
|
||
int32 i, j, k, di, disp, numspec;
|
||
struct InstHistory *h;
|
||
extern char *opcode[];
|
||
|
||
if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */
|
||
di = hst_p; /* work forward */
|
||
for (k = 0; k < hst_lnt; k++) { /* print specified */
|
||
h = &hst[(di++) % hst_lnt]; /* entry pointer */
|
||
if (h->iPC == 0) continue; /* filled in? */
|
||
fprintf(st, "%08X %08X ", h->iPC, h->PSL); /* PC, PSL */
|
||
numspec = drom[h->opc][0] & DR_NSPMASK; /* #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 */
|
||
fprintf (st, "%s", opcode[h->opc]); /* print opcode */
|
||
for (i = 1, j = 0; i <= numspec; i++) { /* loop thru specs */
|
||
fputc ((i == 1)? ' ': ',', st); /* separator */
|
||
disp = drom[h->opc][i]; /* specifier type */
|
||
if (disp == RG) disp = RQ; /* fix specials */
|
||
else if (disp >= BB) fprintf (st, "%X", h->brdest);
|
||
else switch (disp & 0xF) { /* case on type */
|
||
case RB: case RW: case RL: /* read */
|
||
case AB: case AW: case AL: case AQ: /* address */
|
||
case MB: case MW: case ML: /* modify */
|
||
fprintf (st, "%X", h->opnd[j++]);
|
||
break;
|
||
case RQ: /* read quad */
|
||
case MQ: /* modify quad */
|
||
fprintf (st, "%X%08X", h->opnd[j], h->opnd[j + 1]);
|
||
j = j + 2;
|
||
break;
|
||
case WB: case WW: case WL: case WQ: /* write */
|
||
if (h->opnd[j] < 0) fprintf (st, "%X", h->opnd[j + 1]);
|
||
else fprintf (st, "R%d", h->opnd[j]);
|
||
j = j + 2;
|
||
break;
|
||
} /* end case */
|
||
} /* end for */
|
||
} /* end else */
|
||
fputc ('\n', st); /* end line */
|
||
} /* end for */
|
||
return SCPE_OK;
|
||
}
|