blob: eab9ca8fe1b0df00be892b2e0279d71e8a024ecc [file] [log] [blame] [raw]
/* pdp11_cpu.c: PDP-11 CPU simulator
Copyright (c) 1993-1999, 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.
14-Apr-99 RMS Changed t_addr to unsigned
18-Aug-98 RMS Added CIS support
09-May-98 RMS Fixed bug in DIV overflow test
19-Jan-97 RMS Added RP/RM support
06-Apr-96 RMS Added dynamic memory sizing
29-Feb-96 RMS Added TM11 support
17-Jul-94 RMS Corrected updating of MMR1 if MMR0 locked
The register state for the PDP-11 is:
REGFILE[0:5][0] general register set
REGFILE[0:5][1] alternate general register set
STACKFILE[4] stack pointers for kernel, supervisor, unused, user
PC program counter
PSW processor status word
<15:14> = CM current processor mode
<13:12> = PM previous processor mode
<11> = RS register set select
<7:5> = IPL interrupt priority level
<4> = TBIT trace trap enable
<3:0> = NZVC condition codes
FR[0:5] floating point accumulators
FPS floating point status register
FEC floating exception code
FEA floating exception address
MMR0,1,2,3 memory management control registers
APRFILE[0:63] memory management relocation registers for
kernel, supervisor, unused, user
<31:16> = PAR processor address registers
<15:0> = PDR processor data registers
PIRQ processor interrupt request register
CPUERR CPU error register
MEMERR memory system error register
CCR cache control register
MAINT maintenance register
HITMISS cache status register
SR switch register
DR display register
*/
/* The PDP-11 has many instruction formats:
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ double operand
| opcode | source spec | dest spec | 010000:067777
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 110000:167777
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ register + operand
| opcode | src reg| dest spec | 004000:004777
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 070000:077777
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ single operand
| opcode | dest spec | 000100:000177
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 000300:000377
005000:007777
105000:107777
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ single register
| opcode |dest reg| 000200:000207
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 000230:000237
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ no operand
| opcode | 000000:000007
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ branch
| opcode | branch displacement | 000400:003477
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 100000:103477
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ EMT/TRAP
| opcode | trap code | 104000:104777
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ cond code operator
| opcode | immediate | 000240:000277
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
An operand specifier consists of an addressing mode and a register.
The addressing modes are:
0 register direct R op = R
1 register deferred (R) op = M[R]
2 autoincrement (R)+ op = M[R]; R = R + length
3 autoincrement deferred @(R)+ op = M[M[R]]; R = R + 2
4 autodecrement -(R) R = R - length; op = M[R]
5 autodecrement deferred @-(R) R = R - 2; op = M[M[R]]
6 displacement d(R) op = M[R + disp]
7 displacement deferred @d(R) op = M[M[R + disp]]
There are eight general registers, R0-R7. R6 is the stack pointer,
R7 the PC. The combination of addressing modes with R7 yields:
27 immediate #n op = M[PC]; PC = PC + 2
37 absolute @#n op = M[M[PC]]; PC = PC + 2
67 relative d(PC) op = M[PC + disp]
77 relative deferred @d(PC) op = M[M[PC + disp]]
*/
/* This routine is the instruction decode routine for the PDP-11. 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. Virtual address format. PDP-11 memory management uses the 16b
virtual address, the type of reference (instruction or data), and
the current mode, to construct the 22b physical address. To
package this conveniently, the simulator uses a 19b pseudo virtual
address, consisting of the 16b virtual address prefixed with the
current mode and ispace/dspace indicator. These are precalculated
as isenable and dsenable for ispace and dspace, respectively, and
must be recalculated whenever MMR0, MMR3, or PSW<cm> changes.
2. Traps and interrupts. Variable trap_req bit-encodes all possible
traps. In addition, an interrupt pending bit is encoded as the
lowest priority trap. Traps are processed by trap_vec and trap_clear,
which provide the vector and subordinate traps to clear, respectively.
Variable int_req bit encodes all possible interrupts. It is masked
under the interrupt masks, int_mask[ipl]. If any interrupt request
is not masked, the interrupt bit is set in trap_req. While most
interrupts are handled centrally, a device can supply an interrupt
acknowledge routine.
3. PSW handling. The PSW is kept as components, for easier access.
Because the PSW can be explicitly written as address 17777776,
all instructions must update PSW before executing their last write.
4. Adding I/O devices. This requires modifications to three modules:
pdp11_defs.h add interrupt request definitions
pdp11_cpu.c add I/O page linkages
pdp11_sys.c add to sim_devices
*/
/* Definitions */
#include "pdp11_defs.h"
#include <setjmp.h>
#define calc_is(md) ((md) << VA_V_MODE)
#define calc_ds(md) (calc_is((md)) | ((MMR3 & dsmask[(md)])? VA_DS: 0))
#define calc_MMR1(val) (MMR1 = MMR1? ((val) << 8) | MMR1: (val))
#define calc_ints(lv,rq,tr) (((rq) & int_mask[(lv)])? \
((tr) | TRAP_INT) : ((tr) & ~TRAP_INT))
#define GET_SIGN_W(v) ((v) >> 15)
#define GET_SIGN_B(v) ((v) >> 7)
#define GET_Z(v) ((v) == 0)
#define JMP_PC(x) old_PC = PC; PC = (x)
#define BRANCH_F(x) old_PC = PC; PC = (PC + (((x) + (x)) & 0377)) & 0177777
#define BRANCH_B(x) old_PC = PC; PC = (PC + (((x) + (x)) | 0177400)) & 0177777
#define ILL_ADR_FLAG 0200000
#define save_ibkpt (cpu_unit.u3) /* will be SAVEd */
#define last_pa (cpu_unit.u4) /* and RESTOREd */
#define UNIT_V_18B (UNIT_V_UF) /* force 18b addr */
#define UNIT_18B (1u << UNIT_V_18B)
#define UNIT_V_CIS (UNIT_V_UF + 1) /* CIS present */
#define UNIT_CIS (1u << UNIT_V_CIS)
#define UNIT_V_MSIZE (UNIT_V_UF + 2) /* dummy */
#define UNIT_MSIZE (1u << UNIT_V_MSIZE)
/* Global state */
unsigned int16 *M = NULL; /* address of memory */
int32 REGFILE[6][2] = { 0 }; /* R0-R5, two sets */
int32 STACKFILE[4] = { 0 }; /* SP, four modes */
int32 saved_PC = 0; /* program counter */
int32 R[8] = { 0 }; /* working registers */
int32 PSW = 0; /* PSW */
int32 cm = 0; /* current mode */
int32 pm = 0; /* previous mode */
int32 rs = 0; /* register set */
int32 ipl = 0; /* int pri level */
int32 tbit = 0; /* trace flag */
int32 N = 0, Z = 0, V = 0, C = 0; /* condition codes */
int32 wait_state = 0; /* wait state */
int32 trap_req = 0; /* trap requests */
int32 int_req = 0; /* interrupt requests */
int32 PIRQ = 0; /* programmed int req */
int32 SR = 0; /* switch register */
int32 DR = 0; /* display register */
fpac_t FR[6] = { 0 }; /* fp accumulators */
int32 FPS = 0; /* fp status */
int32 FEC = 0; /* fp exception code */
int32 FEA = 0; /* fp exception addr */
int32 APRFILE[64] = { 0 }; /* PARs/PDRs */
int32 MMR0 = 0; /* MMR0 - status */
int32 MMR1 = 0; /* MMR1 - R+/-R */
int32 MMR2 = 0; /* MMR2 - saved PC */
int32 MMR3 = 0; /* MMR3 - 22b status */
int32 isenable = 0, dsenable = 0; /* i, d space flags */
int32 CPUERR = 0; /* CPU error reg */
int32 MEMERR = 0; /* memory error reg */
int32 CCR = 0; /* cache control reg */
int32 HITMISS = 0; /* hit/miss reg */
int32 MAINT = (0 << 9) + (0 << 8) + (4 << 4); /* maint bit<9> = Q/U */
/* <8> = hwre FP */
/* <6:4> = sys type */
int32 stop_trap = 1; /* stop on trap */
int32 stop_vecabort = 1; /* stop on vec abort */
int32 stop_spabort = 1; /* stop on SP abort */
int32 wait_enable = 0; /* wait state enable */
int32 ibkpt_addr = ILL_ADR_FLAG | VAMASK; /* breakpoint addr */
int32 old_PC = 0; /* previous PC */
jmp_buf save_env; /* abort handler */
int32 dsmask[4] = { MMR3_KDS, MMR3_SDS, 0, MMR3_UDS }; /* dspace enables */
int32 int_mask[8] = { INT_IPL0, INT_IPL1, INT_IPL2, /* interrupt masks */
INT_IPL3, INT_IPL4, INT_IPL5, INT_IPL6, INT_IPL7 };
extern int32 sim_int_char;
/* Function declarations */
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
t_stat cpu_reset (DEVICE *dptr);
t_stat cpu_svc (UNIT *uptr);
t_stat cpu_set_size (UNIT *uptr, int32 value);
int32 GeteaB (int32 spec);
int32 GeteaW (int32 spec);
int32 relocR (int32 addr);
int32 relocW (int32 addr);
int32 ReadW (int32 addr);
int32 ReadB (int32 addr);
int32 ReadMW (int32 addr);
int32 ReadMB (int32 addr);
void WriteW (int32 data, int32 addr);
void WriteB (int32 data, int32 addr);
void PWriteW (int32 data, int32 addr);
void PWriteB (int32 data, int32 addr);
t_stat iopageR (int32 *data, int32 addr, int32 access);
t_stat iopageW (int32 data, int32 addr, int32 access);
t_stat CPU_rd (int32 *data, int32 addr, int32 access);
t_stat CPU_wr (int32 data, int32 addr, int32 access);
t_stat APR_rd (int32 *data, int32 addr, int32 access);
t_stat APR_wr (int32 data, int32 addr, int32 access);
t_stat SR_MMR012_rd (int32 *data, int32 addr, int32 access);
t_stat SR_MMR012_wr (int32 data, int32 addr, int32 access);
t_stat MMR3_rd (int32 *data, int32 addr, int32 access);
t_stat MMR3_wr (int32 data, int32 addr, int32 access);
extern t_stat std_rd (int32 *data, int32 addr, int32 access);
extern t_stat std_wr (int32 data, int32 addr, int32 access);
extern t_stat lpt_rd (int32 *data, int32 addr, int32 access);
extern t_stat lpt_wr (int32 data, int32 addr, int32 access);
extern t_stat rk_rd (int32 *data, int32 addr, int32 access);
extern t_stat rk_wr (int32 data, int32 addr, int32 access);
extern int32 rk_inta (void);
extern t_stat rl_rd (int32 *data, int32 addr, int32 access);
extern t_stat rl_wr (int32 data, int32 addr, int32 access);
extern t_stat rp_rd (int32 *data, int32 addr, int32 access);
extern t_stat rp_wr (int32 data, int32 addr, int32 access);
extern int32 rp_inta (void);
extern t_stat rx_rd (int32 *data, int32 addr, int32 access);
extern t_stat rx_wr (int32 data, int32 addr, int32 access);
extern t_stat tm_rd (int32 *data, int32 addr, int32 access);
extern t_stat tm_wr (int32 data, int32 addr, int32 access);
extern t_stat sim_activate (UNIT *uptr, int32 delay);
/* Auxiliary data structures */
struct iolink { /* I/O page linkage */
int32 low; /* low I/O addr */
int32 high; /* high I/O addr */
t_stat (*read)(); /* read routine */
t_stat (*write)(); }; /* write routine */
struct iolink iotable[] = {
{ 017777740, 017777777, &CPU_rd, &CPU_wr },
{ 017777546, 017777567, &std_rd, &std_wr },
{ 017777514, 017777517, &lpt_rd, &lpt_wr },
{ 017777400, 017777417, &rk_rd, &rk_wr },
{ 017774400, 017774411, &rl_rd, &rl_wr },
{ 017776700, 017776753, &rp_rd, &rp_wr },
{ 017777170, 017777173, &rx_rd, &rx_wr },
{ 017772520, 017772533, &tm_rd, &tm_wr },
{ 017777600, 017777677, &APR_rd, &APR_wr },
{ 017772200, 017772377, &APR_rd, &APR_wr },
{ 017777570, 017777577, &SR_MMR012_rd, &SR_MMR012_wr },
{ 017772516, 017772517, &MMR3_rd, &MMR3_wr },
{ 0, 0, NULL } };
int32 int_vec[32] = { /* int req to vector */
0, 0, 0, VEC_PIRQ, VEC_CLK, 0, 0, VEC_PIRQ,
VEC_RK, VEC_RL, VEC_RX, VEC_TM, VEC_RP, 0, 0, VEC_PIRQ,
VEC_TTI, VEC_TTO, VEC_PTR, VEC_PTP, VEC_LPT, 0, 0, 0,
0, 0, 0, 0, VEC_PIRQ, VEC_PIRQ, VEC_PIRQ, VEC_PIRQ };
int32 (*int_ack[32])() = { /* int ack routines */
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&rk_inta, NULL, NULL, NULL, &rp_inta, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
int32 trap_vec[TRAP_V_MAX] = { /* trap req to vector */
VEC_RED, VEC_ODD, VEC_MME, VEC_NXM,
VEC_PAR, VEC_PRV, VEC_ILL, VEC_BPT,
VEC_IOT, VEC_EMT, VEC_TRAP, VEC_TRC,
VEC_YEL, VEC_PWRFL, VEC_FPE };
int32 trap_clear[TRAP_V_MAX] = { /* trap clears */
TRAP_RED+TRAP_PAR+TRAP_YEL+TRAP_TRC,
TRAP_ODD+TRAP_PAR+TRAP_YEL+TRAP_TRC,
TRAP_MME+TRAP_PAR+TRAP_YEL+TRAP_TRC,
TRAP_NXM+TRAP_PAR+TRAP_YEL+TRAP_TRC,
TRAP_PAR+TRAP_TRC, TRAP_PRV+TRAP_TRC,
TRAP_ILL+TRAP_TRC, TRAP_BPT+TRAP_TRC,
TRAP_IOT+TRAP_TRC, TRAP_EMT+TRAP_TRC,
TRAP_TRAP+TRAP_TRC, TRAP_TRC,
TRAP_YEL, TRAP_PWRFL, TRAP_FPE };
/* CPU data structures
cpu_dev CPU device descriptor
cpu_unit CPU unit descriptor
cpu_reg CPU register list
cpu_mod CPU modifier list
*/
UNIT cpu_unit = { UDATA (&cpu_svc, UNIT_FIX + UNIT_BINK, INIMEMSIZE) };
REG cpu_reg[] = {
{ ORDATA (PC, saved_PC, 16) },
{ ORDATA (R0, REGFILE[0][0], 16) },
{ ORDATA (R1, REGFILE[1][0], 16) },
{ ORDATA (R2, REGFILE[2][0], 16) },
{ ORDATA (R3, REGFILE[3][0], 16) },
{ ORDATA (R4, REGFILE[4][0], 16) },
{ ORDATA (R5, REGFILE[5][0], 16) },
{ ORDATA (R10, REGFILE[0][1], 16) },
{ ORDATA (R11, REGFILE[1][1], 16) },
{ ORDATA (R12, REGFILE[2][1], 16) },
{ ORDATA (R13, REGFILE[3][1], 16) },
{ ORDATA (R14, REGFILE[4][1], 16) },
{ ORDATA (R15, REGFILE[5][1], 16) },
{ ORDATA (KSP, STACKFILE[KERNEL], 16) },
{ ORDATA (SSP, STACKFILE[SUPER], 16) },
{ ORDATA (USP, STACKFILE[USER], 16) },
{ ORDATA (PSW, PSW, 16) },
{ GRDATA (CM, PSW, 8, 2, PSW_V_CM) },
{ GRDATA (PM, PSW, 8, 2, PSW_V_PM) },
{ FLDATA (RS, PSW, PSW_V_RS) },
{ GRDATA (IPL, PSW, 8, 3, PSW_V_IPL) },
{ FLDATA (T, PSW, PSW_V_TBIT) },
{ FLDATA (N, PSW, PSW_V_N) },
{ FLDATA (Z, PSW, PSW_V_Z) },
{ FLDATA (V, PSW, PSW_V_V) },
{ FLDATA (C, PSW, PSW_V_C) },
{ ORDATA (SR, SR, 16) },
{ ORDATA (DR, DR, 16) },
{ ORDATA (MEMERR, MEMERR, 16) },
{ ORDATA (CCR, CCR, 16) },
{ ORDATA (MAINT, MAINT, 16) },
{ ORDATA (HITMISS, HITMISS, 16) },
{ ORDATA (CPUERR, CPUERR, 16) },
{ ORDATA (INT, int_req, 32), REG_RO },
{ ORDATA (TRAPS, trap_req, TRAP_V_MAX) },
{ ORDATA (PIRQ, PIRQ, 16) },
{ FLDATA (WAIT, wait_state, 0) },
{ FLDATA (WAIT_ENABLE, wait_enable, 0) },
{ ORDATA (STOP_TRAPS, stop_trap, TRAP_V_MAX) },
{ FLDATA (STOP_VECA, stop_vecabort, 0) },
{ FLDATA (STOP_SPA, stop_spabort, 0) },
{ ORDATA (FAC0H, FR[0].h, 32) },
{ ORDATA (FAC0L, FR[0].l, 32) },
{ ORDATA (FAC1H, FR[1].h, 32) },
{ ORDATA (FAC1L, FR[1].l, 32) },
{ ORDATA (FAC2H, FR[2].h, 32) },
{ ORDATA (FAC2L, FR[2].l, 32) },
{ ORDATA (FAC3H, FR[3].h, 32) },
{ ORDATA (FAC3L, FR[3].l, 32) },
{ ORDATA (FAC4H, FR[4].h, 32) },
{ ORDATA (FAC4L, FR[4].l, 32) },
{ ORDATA (FAC5H, FR[5].h, 32) },
{ ORDATA (FAC5L, FR[5].l, 32) },
{ ORDATA (FPS, FPS, 16) },
{ ORDATA (FEA, FEA, 16) },
{ ORDATA (FEC, FEC, 4) },
{ ORDATA (MMR0, MMR0, 16) },
{ ORDATA (MMR1, MMR1, 16) },
{ ORDATA (MMR2, MMR2, 16) },
{ ORDATA (MMR3, MMR3, 16) },
{ GRDATA (KIPAR0, APRFILE[000], 8, 16, 16) },
{ GRDATA (KIPDR0, APRFILE[000], 8, 16, 0) },
{ GRDATA (KIPAR1, APRFILE[001], 8, 16, 16) },
{ GRDATA (KIPDR1, APRFILE[001], 8, 16, 0) },
{ GRDATA (KIPAR2, APRFILE[002], 8, 16, 16) },
{ GRDATA (KIPDR2, APRFILE[002], 8, 16, 0) },
{ GRDATA (KIPAR3, APRFILE[003], 8, 16, 16) },
{ GRDATA (KIPDR3, APRFILE[003], 8, 16, 0) },
{ GRDATA (KIPAR4, APRFILE[004], 8, 16, 16) },
{ GRDATA (KIPDR4, APRFILE[004], 8, 16, 0) },
{ GRDATA (KIPAR5, APRFILE[005], 8, 16, 16) },
{ GRDATA (KIPDR5, APRFILE[005], 8, 16, 0) },
{ GRDATA (KIPAR6, APRFILE[006], 8, 16, 16) },
{ GRDATA (KIPDR6, APRFILE[006], 8, 16, 0) },
{ GRDATA (KIPAR7, APRFILE[007], 8, 16, 16) },
{ GRDATA (KIPDR7, APRFILE[007], 8, 16, 0) },
{ GRDATA (KDPAR0, APRFILE[010], 8, 16, 16) },
{ GRDATA (KDPDR0, APRFILE[010], 8, 16, 0) },
{ GRDATA (KDPAR1, APRFILE[011], 8, 16, 16) },
{ GRDATA (KDPDR1, APRFILE[011], 8, 16, 0) },
{ GRDATA (KDPAR2, APRFILE[012], 8, 16, 16) },
{ GRDATA (KDPDR2, APRFILE[012], 8, 16, 0) },
{ GRDATA (KDPAR3, APRFILE[013], 8, 16, 16) },
{ GRDATA (KDPDR3, APRFILE[013], 8, 16, 0) },
{ GRDATA (KDPAR4, APRFILE[014], 8, 16, 16) },
{ GRDATA (KDPDR4, APRFILE[014], 8, 16, 0) },
{ GRDATA (KDPAR5, APRFILE[015], 8, 16, 16) },
{ GRDATA (KDPDR5, APRFILE[015], 8, 16, 0) },
{ GRDATA (KDPAR6, APRFILE[016], 8, 16, 16) },
{ GRDATA (KDPDR6, APRFILE[016], 8, 16, 0) },
{ GRDATA (KDPAR7, APRFILE[017], 8, 16, 16) },
{ GRDATA (KDPDR7, APRFILE[017], 8, 16, 0) },
{ GRDATA (SIPAR0, APRFILE[020], 8, 16, 16) },
{ GRDATA (SIPDR0, APRFILE[020], 8, 16, 0) },
{ GRDATA (SIPAR1, APRFILE[021], 8, 16, 16) },
{ GRDATA (SIPDR1, APRFILE[021], 8, 16, 0) },
{ GRDATA (SIPAR2, APRFILE[022], 8, 16, 16) },
{ GRDATA (SIPDR2, APRFILE[022], 8, 16, 0) },
{ GRDATA (SIPAR3, APRFILE[023], 8, 16, 16) },
{ GRDATA (SIPDR3, APRFILE[023], 8, 16, 0) },
{ GRDATA (SIPAR4, APRFILE[024], 8, 16, 16) },
{ GRDATA (SIPDR4, APRFILE[024], 8, 16, 0) },
{ GRDATA (SIPAR5, APRFILE[025], 8, 16, 16) },
{ GRDATA (SIPDR5, APRFILE[025], 8, 16, 0) },
{ GRDATA (SIPAR6, APRFILE[026], 8, 16, 16) },
{ GRDATA (SIPDR6, APRFILE[026], 8, 16, 0) },
{ GRDATA (SIPAR7, APRFILE[027], 8, 16, 16) },
{ GRDATA (SIPDR7, APRFILE[027], 8, 16, 0) },
{ GRDATA (SDPAR0, APRFILE[030], 8, 16, 16) },
{ GRDATA (SDPDR0, APRFILE[030], 8, 16, 0) },
{ GRDATA (SDPAR1, APRFILE[031], 8, 16, 16) },
{ GRDATA (SDPDR1, APRFILE[031], 8, 16, 0) },
{ GRDATA (SDPAR2, APRFILE[032], 8, 16, 16) },
{ GRDATA (SDPDR2, APRFILE[032], 8, 16, 0) },
{ GRDATA (SDPAR3, APRFILE[033], 8, 16, 16) },
{ GRDATA (SDPDR3, APRFILE[033], 8, 16, 0) },
{ GRDATA (SDPAR4, APRFILE[034], 8, 16, 16) },
{ GRDATA (SDPDR4, APRFILE[034], 8, 16, 0) },
{ GRDATA (SDPAR5, APRFILE[035], 8, 16, 16) },
{ GRDATA (SDPDR5, APRFILE[035], 8, 16, 0) },
{ GRDATA (SDPAR6, APRFILE[036], 8, 16, 16) },
{ GRDATA (SDPDR6, APRFILE[036], 8, 16, 0) },
{ GRDATA (SDPAR7, APRFILE[037], 8, 16, 16) },
{ GRDATA (SDPDR7, APRFILE[037], 8, 16, 0) },
{ GRDATA (UIPAR0, APRFILE[060], 8, 16, 16) },
{ GRDATA (UIPDR0, APRFILE[060], 8, 16, 0) },
{ GRDATA (UIPAR1, APRFILE[061], 8, 16, 16) },
{ GRDATA (UIPDR1, APRFILE[061], 8, 16, 0) },
{ GRDATA (UIPAR2, APRFILE[062], 8, 16, 16) },
{ GRDATA (UIPDR2, APRFILE[062], 8, 16, 0) },
{ GRDATA (UIPAR3, APRFILE[063], 8, 16, 16) },
{ GRDATA (UIPDR3, APRFILE[063], 8, 16, 0) },
{ GRDATA (UIPAR4, APRFILE[064], 8, 16, 16) },
{ GRDATA (UIPDR4, APRFILE[064], 8, 16, 0) },
{ GRDATA (UIPAR5, APRFILE[065], 8, 16, 16) },
{ GRDATA (UIPDR5, APRFILE[065], 8, 16, 0) },
{ GRDATA (UIPAR6, APRFILE[066], 8, 16, 16) },
{ GRDATA (UIPDR6, APRFILE[066], 8, 16, 0) },
{ GRDATA (UIPAR7, APRFILE[067], 8, 16, 16) },
{ GRDATA (UIPDR7, APRFILE[067], 8, 16, 0) },
{ GRDATA (UDPAR0, APRFILE[070], 8, 16, 16) },
{ GRDATA (UDPDR0, APRFILE[070], 8, 16, 0) },
{ GRDATA (UDPAR1, APRFILE[071], 8, 16, 16) },
{ GRDATA (UDPDR1, APRFILE[071], 8, 16, 0) },
{ GRDATA (UDPAR2, APRFILE[072], 8, 16, 16) },
{ GRDATA (UDPDR2, APRFILE[072], 8, 16, 0) },
{ GRDATA (UDPAR3, APRFILE[073], 8, 16, 16) },
{ GRDATA (UDPDR3, APRFILE[073], 8, 16, 0) },
{ GRDATA (UDPAR4, APRFILE[074], 8, 16, 16) },
{ GRDATA (UDPDR4, APRFILE[074], 8, 16, 0) },
{ GRDATA (UDPAR5, APRFILE[075], 8, 16, 16) },
{ GRDATA (UDPDR5, APRFILE[075], 8, 16, 0) },
{ GRDATA (UDPAR6, APRFILE[076], 8, 16, 16) },
{ GRDATA (UDPDR6, APRFILE[076], 8, 16, 0) },
{ GRDATA (UDPAR7, APRFILE[077], 8, 16, 16) },
{ GRDATA (UDPDR7, APRFILE[077], 8, 16, 0) },
{ FLDATA (18B_ADDR, cpu_unit.flags, UNIT_V_18B), REG_HRO },
{ FLDATA (CIS, cpu_unit.flags, UNIT_V_CIS), REG_HRO },
{ ORDATA (OLDPC, old_PC, 16), REG_RO },
{ ORDATA (BREAK, ibkpt_addr, 17) },
{ ORDATA (WRU, sim_int_char, 8) },
{ NULL} };
MTAB cpu_mod[] = {
{ UNIT_18B, UNIT_18B, "18b addressing", "18B", NULL },
{ UNIT_18B, 0, NULL, "22B", NULL },
{ UNIT_CIS, UNIT_CIS, "CIS", "CIS", NULL },
{ UNIT_CIS, 0, "No CIS", "NOCIS", NULL },
{ UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size},
{ UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size},
{ UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size},
{ UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size},
{ UNIT_MSIZE, 98304, NULL, "96K", &cpu_set_size},
{ UNIT_MSIZE, 131072, NULL, "128K", &cpu_set_size},
{ UNIT_MSIZE, 229376, NULL, "192K", &cpu_set_size},
{ UNIT_MSIZE, 262144, NULL, "256K", &cpu_set_size},
{ UNIT_MSIZE, 393216, NULL, "384K", &cpu_set_size},
{ UNIT_MSIZE, 524288, NULL, "512K", &cpu_set_size},
{ UNIT_MSIZE, 786432, NULL, "768K", &cpu_set_size},
{ UNIT_MSIZE, 1048576, NULL, "1024K", &cpu_set_size},
{ UNIT_MSIZE, 2097152, NULL, "2048K", &cpu_set_size},
{ UNIT_MSIZE, 3145728, NULL, "3072K", &cpu_set_size},
{ UNIT_MSIZE, 4194304, NULL, "4096K", &cpu_set_size},
{ UNIT_MSIZE, 1048576, NULL, "1M", &cpu_set_size},
{ UNIT_MSIZE, 2097152, NULL, "2M", &cpu_set_size},
{ UNIT_MSIZE, 3145728, NULL, "3M", &cpu_set_size},
{ UNIT_MSIZE, 4194304, NULL, "4M", &cpu_set_size},
{ 0 } };
DEVICE cpu_dev = {
"CPU", &cpu_unit, cpu_reg, cpu_mod,
1, 8, 22, 2, 8, 16,
&cpu_ex, &cpu_dep, &cpu_reset,
NULL, NULL, NULL };
t_stat sim_instr (void)
{
extern int32 sim_interval;
extern UNIT *sim_clock_queue;
register int32 IR, srcspec, srcreg, dstspec, dstreg;
register int32 src, src2, dst;
register int32 i, t, sign, oldrs, trapnum;
int32 abortval;
volatile int32 trapea;
t_stat reason;
void fp11 (int32 IR);
void cis11 (int32 IR);
/* Restore register state
1. PSW components
2. Active register file based on PSW<rs>
3. Active stack pointer based on PSW<cm>
4. Memory management control flags
5. Interrupt system
*/
cm = (PSW >> PSW_V_CM) & 03; /* call calc_is,ds */
pm = (PSW >> PSW_V_PM) & 03;
rs = (PSW >> PSW_V_RS) & 01;
ipl = (PSW >> PSW_V_IPL) & 07; /* call calc_ints */
tbit = (PSW >> PSW_V_TBIT) & 01;
N = (PSW >> PSW_V_N) & 01;
Z = (PSW >> PSW_V_Z) & 01;
V = (PSW >> PSW_V_V) & 01;
C = (PSW >> PSW_V_C) & 01;
for (i = 0; i < 6; i++) R[i] = REGFILE[i][rs];
SP = STACKFILE[cm];
PC = saved_PC;
isenable = calc_is (cm);
dsenable = calc_ds (cm);
CPU_wr (PIRQ, 017777772, WRITE); /* rewrite PIRQ */
trap_req = calc_ints (ipl, int_req, trap_req);
trapea = 0;
reason = 0;
/* Abort handling
If an abort occurs in memory management or memory access, the lower
level routine executes a longjmp to this area OUTSIDE the main
simulation loop. The longjmp specifies a trap mask which is OR'd
into the trap_req register. Simulation then resumes at the fetch
phase, and the trap is sprung.
Aborts which occur within a trap sequence (trapea != 0) require
special handling. If the abort occured on the stack pushes, and
the mode (encoded in trapea) is kernel, an "emergency" kernel
stack is created at 4, and a red zone stack trap taken.
*/
abortval = setjmp (save_env); /* set abort hdlr */
if (abortval != 0) {
trap_req = trap_req | abortval; /* or in trap flag */
if ((trapea > 0) && (stop_vecabort)) reason = STOP_VECABORT;
if ((trapea < 0) && (stop_spabort)) reason = STOP_SPABORT;
if (trapea == ~KERNEL) { /* kernel stk abort? */
setTRAP (TRAP_RED);
setCPUERR (CPUE_RED);
STACKFILE[KERNEL] = 4;
if (cm == KERNEL) SP = 4; } }
/* Main instruction fetch/decode loop
Check for traps or interrupts. If trap, locate the vector and check
for stop condition. If interrupt, locate the vector.
*/
while (reason == 0) {
if (sim_interval <= 0) { /* check clock queue */
reason = sim_process_event ();
trap_req = calc_ints (ipl, int_req, trap_req);
continue; }
if (trap_req) { /* check traps, ints */
trapea = 0; /* assume srch fails */
if (t = trap_req & TRAP_ALL) { /* if a trap */
for (trapnum = 0; trapnum < TRAP_V_MAX; trapnum++) {
if ((t >> trapnum) & 1) {
trapea = trap_vec[trapnum];
trap_req = trap_req & ~trap_clear[trapnum];
if ((stop_trap >> trapnum) & 1)
reason = trapnum + 1;
break; } } }
else if (t = int_req & int_mask[ipl]) { /* if an interrupt */
for (i = 0; i < 32; i++) {
if ((t >> i) & 1) {
int_req = int_req & ~(1u << i);
if (int_ack[i]) trapea = int_ack[i]();
else trapea = int_vec[i];
trapnum = TRAP_V_MAX;
break; } } }
if (trapea == 0) { /* nothing to do? */
trap_req = calc_ints (ipl, int_req, 0); /* recalculate */
continue; } /* back to fetch */
/* Process a trap or interrupt
1. Exit wait state
2. Save the current SP and PSW
3. Read the new PC, new PSW from trapea, kernel data space
4. Get the mode and stack selected by the new PSW
5. Push the old PC and PSW on the new stack
6. Update SP, PSW, and PC
7. If not stack overflow, check for stack overflow
*/
wait_state = 0; /* exit wait state */
STACKFILE[cm] = SP;
PSW = (cm << PSW_V_CM) | (pm << PSW_V_PM) | (rs << PSW_V_RS) |
(ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) |
(N << PSW_V_N) | (Z << PSW_V_Z) |
(V << PSW_V_V) | (C << PSW_V_C);
oldrs = rs;
src = ReadW (trapea | calc_ds (KERNEL));
src2 = ReadW ((trapea + 2) | calc_ds (KERNEL));
t = (src2 >> PSW_V_CM) & 03;
trapea = ~t; /* flag pushes */
WriteW (PSW, ((STACKFILE[t] - 2) & 0177777) | calc_ds (t));
WriteW (PC, ((STACKFILE[t] - 4) & 0177777) | calc_ds (t));
trapea = 0; /* clear trap flag */
pm = cm;
cm = t; /* call calc_is,ds */
rs = (src2 >> PSW_V_RS) & 01;
ipl = (src2 >> PSW_V_IPL) & 07; /* call calc_ints */
tbit = (src2 >> PSW_V_TBIT) & 01;
N = (src2 >> PSW_V_N) & 01;
Z = (src2 >> PSW_V_Z) & 01;
V = (src2 >> PSW_V_V) & 01;
C = (src2 >> PSW_V_C) & 01;
if (rs != oldrs) { /* if rs chg, swap */
for (i = 0; i < 6; i++) {
REGFILE[i][oldrs] = R[i];
R[i] = REGFILE[i][rs]; } }
SP = (STACKFILE[cm] - 4) & 0177777; /* update SP, PC */
JMP_PC (src);
isenable = calc_is (cm);
dsenable = calc_ds (cm);
trap_req = calc_ints (ipl, int_req, trap_req);
if ((SP < STKLIM) && (cm == KERNEL) &&
(trapnum != TRAP_V_RED) && (trapnum != TRAP_V_YEL)) {
setTRAP (TRAP_YEL);
setCPUERR (CPUE_YEL); }
continue; } /* end if traps */
/* Fetch and decode next instruction */
if (tbit) setTRAP (TRAP_TRC);
if (wait_state) { /* wait state? */
if (sim_clock_queue != NULL) sim_interval = 0; /* force check */
else reason = STOP_WAIT;
continue; }
if (PC == ibkpt_addr) { /* breakpoint? */
save_ibkpt = ibkpt_addr; /* save bkpt */
ibkpt_addr = ibkpt_addr | ILL_ADR_FLAG; /* disable */
sim_activate (&cpu_unit, 1); /* sched re-enable */
reason = STOP_IBKPT; /* stop simulation */
continue; }
if (update_MM) { /* if mm not frozen */
MMR1 = 0;
MMR2 = PC; }
IR = ReadW (PC | isenable); /* fetch instruction */
PC = (PC + 2) & 0177777; /* incr PC, mod 65k */
sim_interval = sim_interval - 1;
srcspec = (IR >> 6) & 077; /* src, dst specs */
dstspec = IR & 077;
srcreg = (srcspec <= 07); /* src, dst = rmode? */
dstreg = (dstspec <= 07);
switch ((IR >> 12) & 017) { /* decode IR<15:12> */
/* Opcode 0: no operands, specials, branches, JSR, SOPs */
case 000:
switch ((IR >> 6) & 077) { /* decode IR<11:6> */
case 000: /* no operand */
if (IR > 000010) { /* 000010 - 000077 */
setTRAP (TRAP_ILL); /* illegal */
break; }
switch (IR) { /* decode IR<2:0> */
case 0: /* HALT */
if (cm == KERNEL) reason = STOP_HALT;
else { setTRAP (TRAP_PRV);
setCPUERR (CPUE_HALT); }
break;
case 1: /* WAIT */
if (cm == KERNEL && wait_enable) wait_state = 1;
break;
case 3: /* BPT */
setTRAP (TRAP_BPT);
break;
case 4: /* IOT */
setTRAP (TRAP_IOT);
break;
case 5: /* RESET */
if (cm == KERNEL) {
reset_all (1);
PIRQ = 0;
int_req = 0;
MMR0 = MMR0 & ~(MMR0_MME | MMR0_FREEZE);
MMR3 = 0;
trap_req = trap_req & ~TRAP_INT;
dsenable = calc_ds (cm); }
break;
/* Opcode 0: specials, continued */
case 2: /* RTI */
case 6: /* RTT */
src = ReadW (SP | dsenable);
src2 = ReadW (((SP + 2) & 0177777) | dsenable);
STACKFILE[cm] = SP = (SP + 4) & 0177777;
oldrs = rs;
if (cm == KERNEL) {
cm = (src2 >> PSW_V_CM) & 03;
pm = (src2 >> PSW_V_PM) & 03;
rs = (src2 >> PSW_V_RS) & 01;
ipl = (src2 >> PSW_V_IPL) & 07; }
else { cm = cm | ((src2 >> PSW_V_CM) & 03);
pm = pm | ((src2 >> PSW_V_PM) & 03);
rs = rs | ((src2 >> PSW_V_RS) & 01); }
tbit = (src2 >> PSW_V_TBIT) & 01;
N = (src2 >> PSW_V_N) & 01;
Z = (src2 >> PSW_V_Z) & 01;
V = (src2 >> PSW_V_V) & 01;
C = (src2 >> PSW_V_C) & 01;
trap_req = calc_ints (ipl, int_req, trap_req);
isenable = calc_is (cm);
dsenable = calc_ds (cm);
if (rs != oldrs) {
for (i = 0; i < 6; i++) {
REGFILE[i][oldrs] = R[i];
R[i] = REGFILE[i][rs]; } }
SP = STACKFILE[cm];
JMP_PC (src);
if ((IR == 000002) && tbit) setTRAP (TRAP_TRC);
break;
case 7: /* MFPT */
R[0] = 5; /* report J-11 */
break; } /* end switch no ops */
break; /* end case no ops */
/* Opcode 0: specials, continued */
case 001: /* JMP */
if (dstreg) setTRAP (TRAP_ILL);
else { JMP_PC (GeteaW (dstspec) & 0177777); }
break; /* end JMP */
case 002: /* RTS et al*/
if (IR < 000210) { /* RTS */
dstspec = dstspec & 07;
JMP_PC (R[dstspec]);
R[dstspec] = ReadW (SP | dsenable);
SP = (SP + 2) & 0177777;
break; } /* end if RTS */
if (IR < 000230) {
setTRAP (TRAP_ILL);
break; }
if (IR < 000240) { /* SPL */
if (cm == KERNEL) ipl = IR & 07;
trap_req = calc_ints (ipl, int_req, trap_req);
break; } /* end if SPL */
if (IR < 000260) { /* clear CC */
if (IR & 010) N = 0;
if (IR & 004) Z = 0;
if (IR & 002) V = 0;
if (IR & 001) C = 0;
break; } /* end if clear CCs */
if (IR & 010) N = 1; /* set CC */
if (IR & 004) Z = 1;
if (IR & 002) V = 1;
if (IR & 001) C = 1;
break; /* end case RTS et al */
case 003: /* SWAB */
dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = ((dst & 0377) << 8) | ((dst >> 8) & 0377);
N = GET_SIGN_B (dst & 0377);
Z = GET_Z (dst & 0377);
V = C = 0;
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break; /* end SWAB */
/* Opcode 0: branches, JSR */
case 004: case 005: /* BR */
BRANCH_F (IR);
break;
case 006: case 007: /* BR */
BRANCH_B (IR);
break;
case 010: case 011: /* BNE */
if (Z == 0) { BRANCH_F (IR); }
break;
case 012: case 013: /* BNE */
if (Z == 0) { BRANCH_B (IR); }
break;
case 014: case 015: /* BEQ */
if (Z) { BRANCH_F (IR); }
break;
case 016: case 017: /* BEQ */
if (Z) { BRANCH_B (IR); }
break;
case 020: case 021: /* BGE */
if ((N ^ V) == 0) { BRANCH_F (IR); }
break;
case 022: case 023: /* BGE */
if ((N ^ V) == 0) { BRANCH_B (IR); }
break;
case 024: case 025: /* BLT */
if (N ^ V) { BRANCH_F (IR); }
break;
case 026: case 027: /* BLT */
if (N ^ V) { BRANCH_B (IR); }
break;
case 030: case 031: /* BGT */
if ((Z | (N ^ V)) == 0) { BRANCH_F (IR); }
break;
case 032: case 033: /* BGT */
if ((Z | (N ^ V)) == 0) { BRANCH_B (IR); }
break;
case 034: case 035: /* BLE */
if (Z | (N ^ V)) { BRANCH_F (IR); }
break;
case 036: case 037: /* BLE */
if (Z | (N ^ V)) { BRANCH_B (IR); }
break;
case 040: case 041: case 042: case 043: /* JSR */
case 044: case 045: case 046: case 047:
if (dstreg) setTRAP (TRAP_ILL);
else { srcspec = srcspec & 07;
dst = GeteaW (dstspec);
SP = (SP - 2) & 0177777;
if (update_MM) MMR1 = calc_MMR1 (0366);
WriteW (R[srcspec], SP | dsenable);
if ((SP < STKLIM) && (cm == KERNEL)) {
setTRAP (TRAP_YEL);
setCPUERR (CPUE_YEL); }
R[srcspec] = PC;
JMP_PC (dst & 0177777); }
break; /* end JSR */
/* Opcode 0: SOPs */
case 050: /* CLR */
N = V = C = 0;
Z = 1;
if (dstreg) R[dstspec] = 0;
else WriteW (0, GeteaW (dstspec));
break;
case 051: /* COM */
dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = dst ^ 0177777;
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = 0;
C = 1;
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break;
case 052: /* INC */
dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = (dst + 1) & 0177777;
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = (dst == 0100000);
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break;
case 053: /* DEC */
dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = (dst - 1) & 0177777;
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = (dst == 077777);
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break;
case 054: /* NEG */
dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = (-dst) & 0177777;
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = (dst == 0100000);
C = Z ^ 1;
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break;
case 055: /* ADC */
dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = (dst + C) & 0177777;
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = (C && (dst == 0100000));
C = C & Z;
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break;
/* Opcode 0: SOPs, continued */
case 056: /* SBC */
dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = (dst - C) & 0177777;
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = (C && (dst == 077777));
C = (C && (dst == 0177777));
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break;
case 057: /* TST */
dst = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = C = 0;
break;
case 060: /* ROR */
src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = (src >> 1) | (C << 15);
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
C = (src & 1);
V = N ^ C;
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break;
case 061: /* ROL */
src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = ((src << 1) | C) & 0177777;
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
C = GET_SIGN_W (src);
V = N ^ C;
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break;
case 062: /* ASR */
src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = (src >> 1) | (src & 0100000);
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
C = (src & 1);
V = N ^ C;
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break;
case 063: /* ASL */
src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = (src << 1) & 0177777;
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
C = GET_SIGN_W (src);
V = N ^ C;
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break;
/* Opcode 0: SOPS, continued
Notes:
- MxPI must mask GeteaW returned address to force ispace
- MxPI must set MMR1 for SP recovery in case of fault
*/
case 064: /* MARK */
i = (PC + dstspec + dstspec) & 0177777;
JMP_PC (R[5]);
R[5] = ReadW (i | dsenable);
SP = (i + 2) & 0177777;
break;
case 065: /* MFPI */
if (dstreg) {
if ((dstspec == 6) && (cm != pm)) dst = STACKFILE[pm];
else dst = R[dstspec]; }
else dst = ReadW ((GeteaW (dstspec) & 0177777) | calc_is (pm));
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = 0;
SP = (SP - 2) & 0177777;
if (update_MM) MMR1 = calc_MMR1 (0366);
WriteW (dst, SP | dsenable);
if ((cm == KERNEL) && (SP < STKLIM)) {
setTRAP (TRAP_YEL);
setCPUERR (CPUE_YEL); }
break;
case 066: /* MTPI */
dst = ReadW (SP | dsenable);
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = 0;
SP = (SP + 2) & 0177777;
if (update_MM) MMR1 = 026;
if (dstreg) {
if ((dstspec == 6) && (cm != pm)) STACKFILE[pm] = dst;
else R[dstspec] = dst; }
else { i = ((cm == pm) && (cm == USER))?
calc_ds (pm): calc_is (pm);
WriteW (dst, (GeteaW (dstspec) & 0177777) | i); }
break;
case 067: /* SXT */
dst = N? 0177777: 0;
Z = N ^ 1;
V = 0;
if (dstreg) R[dstspec] = dst;
else WriteW (dst, GeteaW (dstspec));
break;
/* Opcode 0: SOPs, continued */
case 070: /* CSM */
if (((MMR3 & MMR3_CSM) == 0) || (cm == KERNEL))
setTRAP (TRAP_ILL);
else { dst = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
PSW = (cm << PSW_V_CM) | (pm << PSW_V_PM) |
(rs << PSW_V_RS) | (ipl << PSW_V_IPL) |
(tbit << PSW_V_TBIT);
STACKFILE[cm] = SP;
WriteW (PSW, ((SP - 2) & 0177777) | calc_ds (SUPER));
WriteW (PC, ((SP - 4) & 0177777) | calc_ds (SUPER));
WriteW (dst, ((SP - 6) & 0177777) | calc_ds (SUPER));
SP = (SP - 6) & 0177777;
pm = cm;
cm = SUPER;
tbit = 0;
isenable = calc_is (cm);
dsenable = calc_ds (cm);
PC = ReadW (010 | isenable); }
break;
case 072: /* TSTSET */
if (dstreg) setTRAP (TRAP_ILL);
else { dst = ReadMW (GeteaW (dstspec));
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = 0;
C = (dst & 1);
PWriteW (R[0] | 1, last_pa);
R[0] = dst; }
break;
case 073: /* WRTLCK */
if (dstreg) setTRAP (TRAP_ILL);
else { N = GET_SIGN_W (R[0]);
Z = GET_Z (R[0]);
V = 0;
WriteW (R[0], GeteaW (dstspec)); }
break;
default:
setTRAP (TRAP_ILL);
break; } /* end switch SOPs */
break; /* end case 000 */
/* Opcodes 01 - 06: double operand word instructions
Add: v = [sign (src) = sign (src2)] and [sign (src) != sign (result)]
Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)]
*/
case 001: /* MOV */
dst = srcreg? R[srcspec]: ReadW (GeteaW (srcspec));
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = 0;
if (dstreg) R[dstspec] = dst;
else WriteW (dst, GeteaW (dstspec));
break;
case 002: /* CMP */
src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec));
src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
dst = (src - src2) & 0177777;
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = GET_SIGN_W ((src ^ src2) & (~src2 ^ dst));
C = (src < src2);
break;
case 003: /* BIT */
src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec));
src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
dst = src2 & src;
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = 0;
break;
case 004: /* BIC */
src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec));
src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = src2 & ~src;
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = 0;
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break;
case 005: /* BIS */
src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec));
src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = src2 | src;
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = 0;
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break;
case 006: /* ADD */
src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec));
src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = (src2 + src) & 0177777;
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = GET_SIGN_W ((~src ^ src2) & (src ^ dst));
C = (dst < src);
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break;
/* Opcode 07: EIS, FIS (not implemented), CIS
Notes:
- The code assumes that the host int length is at least 32 bits.
- MUL carry: C is set if the (signed) result doesn't fit in 16 bits.
- Divide has three error cases:
1. Divide by zero.
2. Divide largest negative number by -1.
3. (Signed) quotient doesn't fit in 16 bits.
Cases 1 and 2 must be tested in advance, to avoid C runtime errors.
- ASHx left: overflow if the bits shifted out do not equal the sign
of the result (convert shift out to 1/0, xor against sign).
- ASHx right: if right shift sign extends, then the shift and
conditional or of shifted -1 is redundant. If right shift zero
extends, then the shift and conditional or does sign extension.
*/
case 007:
srcspec = srcspec & 07;
switch ((IR >> 9) & 07) { /* decode IR<11:9> */
case 0: /* MUL */
src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
src = R[srcspec];
if (GET_SIGN_W (src2)) src2 = src2 | ~077777;
if (GET_SIGN_W (src)) src = src | ~077777;
dst = src * src2;
R[srcspec] = (dst >> 16) & 0177777;
R[srcspec | 1] = dst & 0177777;
N = (dst < 0);
Z = GET_Z (dst);
V = 0;
C = ((dst > 077777) || (dst < -0100000));
break;
case 1: /* DIV */
src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
src = (R[srcspec] << 16) | R[srcspec | 1];
if (src2 == 0) {
V = C = 1;
break; }
if ((src == 020000000000) && (src2 == 0177777)) {
V = 1;
C = 0;
break; }
if (GET_SIGN_W (src2)) src2 = src2 | ~077777;
if (GET_SIGN_W (R[srcspec])) src = src | ~017777777777;
dst = src / src2;
if ((dst > 077777) || (dst < -0100000)) {
V = 1;
C = 0;
break; }
R[srcspec] = dst & 0177777;
R[srcspec | 1] = (src - (src2 * dst)) & 0177777;
N = (dst < 0);
Z = GET_Z (dst);
V = C = 0;
break;
/* Opcode 7: EIS, continued */
case 2: /* ASH */
src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
src2 = src2 & 077;
sign = GET_SIGN_W (R[srcspec]);
src = sign? R[srcspec] | ~077777: R[srcspec];
if (src2 == 0) { /* [0] */
dst = src;
V = C = 0; }
else if (src2 <= 15) { /* [1,15] */
dst = src << src2;
i = (src >> (16 - src2)) & 0177777;
V = (i != ((dst & 0100000)? 0177777: 0));
C = (i & 1); }
else if (src2 <= 31) { /* [16,31] */
dst = 0;
V = (src != 0);
C = (src << (src2 - 16)) & 1; }
else { /* [-32,-1] */
dst = (src >> (64 - src2)) | (-sign << (src2 - 32));
V = 0;
C = ((src >> (63 - src2)) & 1); }
dst = R[srcspec] = dst & 0177777;
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
break;
case 3: /* ASHC */
src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec));
src2 = src2 & 077;
sign = GET_SIGN_W (R[srcspec]);
src = (R[srcspec] << 16) | R[srcspec | 1];
if (src2 == 0) { /* [0] */
dst = src;
V = C = 0; }
else if (src2 <= 31) { /* [1,31] */
dst = src << src2;
i = (src >> (32 - src2)) | (-sign << src2);
V = (i != ((dst & 020000000000)? -1: 0));
C = (i & 1); }
else { /* [-32,-1] */
dst = (src >> (64 - src2)) | (-sign << (src2 - 32));
V = 0;
C = ((src >> (63 - src2)) & 1); }
i = R[srcspec] = (dst >> 16) & 0177777;
dst = R[srcspec | 1] = dst & 0177777;
N = GET_SIGN_W (i);
Z = GET_Z (dst | i);
break;
/* Opcode 7: EIS, continued */
case 4: /* XOR */
dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = dst ^ R[srcspec];
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = 0;
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break;
case 5: /* FIS - not impl */
setTRAP (TRAP_ILL);
break;
case 6: /* CIS - not impl */
if (cpu_unit.flags & UNIT_CIS) cis11 (IR);
else setTRAP (TRAP_ILL);
break;
case 7: /* SOB */
R[srcspec] = (R[srcspec] - 1) & 0177777;
if (R[srcspec]) {
JMP_PC ((PC - dstspec - dstspec) & 0177777); }
break; } /* end switch EIS */
break; /* end case 007 */
/* Opcode 10: branches, traps, SOPs */
case 010:
switch ((IR >> 6) & 077) { /* decode IR<11:6> */
case 000: case 001: /* BPL */
if (N == 0) { BRANCH_F (IR); }
break;
case 002: case 003: /* BPL */
if (N == 0) { BRANCH_B (IR); }
break;
case 004: case 005: /* BMI */
if (N) { BRANCH_F (IR); }
break;
case 006: case 007: /* BMI */
if (N) { BRANCH_B (IR); }
break;
case 010: case 011: /* BHI */
if ((C | Z) == 0) { BRANCH_F (IR); }
break;
case 012: case 013: /* BHI */
if ((C | Z) == 0) { BRANCH_B (IR); }
break;
case 014: case 015: /* BLOS */
if (C | Z) { BRANCH_F (IR); }
break;
case 016: case 017: /* BLOS */
if (C | Z) { BRANCH_B (IR); }
break;
case 020: case 021: /* BVC */
if (V == 0) { BRANCH_F (IR); }
break;
case 022: case 023: /* BVC */
if (V == 0) { BRANCH_B (IR); }
break;
case 024: case 025: /* BVS */
if (V) { BRANCH_F (IR); }
break;
case 026: case 027: /* BVS */
if (V) { BRANCH_B (IR); }
break;
case 030: case 031: /* BCC */
if (C == 0) { BRANCH_F (IR); }
break;
case 032: case 033: /* BCC */
if (C == 0) { BRANCH_B (IR); }
break;
case 034: case 035: /* BCS */
if (C) { BRANCH_F (IR); }
break;
case 036: case 037: /* BCS */
if (C) { BRANCH_B (IR); }
break;
case 040: case 041: case 042: case 043: /* EMT */
setTRAP (TRAP_EMT);
break;
case 044: case 045: case 046: case 047: /* TRAP */
setTRAP (TRAP_TRAP);
break;
/* Opcode 10, continued: SOPs */
case 050: /* CLRB */
N = V = C = 0;
Z = 1;
if (dstreg) R[dstspec] = R[dstspec] & 0177400;
else WriteB (0, GeteaB (dstspec));
break;
case 051: /* COMB */
dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
dst = (dst ^ 0377) & 0377;
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
V = 0;
C = 1;
if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
else PWriteB (dst, last_pa);
break;
case 052: /* INCB */
dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
dst = (dst + 1) & 0377;
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
V = (dst == 0200);
if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
else PWriteB (dst, last_pa);
break;
case 053: /* DECB */
dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
dst = (dst - 1) & 0377;
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
V = (dst == 0177);
if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
else PWriteB (dst, last_pa);
break;
case 054: /* NEGB */
dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
dst = (-dst) & 0377;
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
V = (dst == 0200);
C = (Z ^ 1);
if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
else PWriteB (dst, last_pa);
break;
case 055: /* ADCB */
dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
dst = (dst + C) & 0377;
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
V = (C && (dst == 0200));
C = C & Z;
if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
else PWriteB (dst, last_pa);
break;
/* Opcode 10: SOPs, continued */
case 056: /* SBCB */
dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
dst = (dst - C) & 0377;
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
V = (C && (dst == 0177));
C = (C && (dst == 0377));
if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
else PWriteB (dst, last_pa);
break;
case 057: /* TSTB */
dst = dstreg? R[dstspec] & 0377: ReadB (GeteaB (dstspec));
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
V = C = 0;
break;
case 060: /* RORB */
src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
dst = ((src & 0377) >> 1) | (C << 7);
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
C = (src & 1);
V = N ^ C;
if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
else PWriteB (dst, last_pa);
break;
case 061: /* ROLB */
src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
dst = ((src << 1) | C) & 0377;
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
C = GET_SIGN_B (src & 0377);
V = N ^ C;
if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
else PWriteB (dst, last_pa);
break;
case 062: /* ASRB */
src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
dst = ((src & 0377) >> 1) | (src & 0200);
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
C = (src & 1);
V = N ^ C;
if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
else PWriteB (dst, last_pa);
break;
case 063: /* ASLB */
src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
dst = (src << 1) & 0377;
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
C = GET_SIGN_B (src & 0377);
V = N ^ C;
if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
else PWriteB (dst, last_pa);
break;
/* Opcode 10: SOPs, continued
Notes:
- MTPS cannot alter the T bit
- MxPD must mask GeteaW returned address, dspace is from cm not pm
- MxPD must set MMR1 for SP recovery in case of fault
*/
case 064: /* MTPS */
dst = dstreg? R[dstspec]: ReadB (GeteaB (dstspec));
if (cm == KERNEL) {
ipl = (dst >> PSW_V_IPL) & 07;
trap_req = calc_ints (ipl, int_req, trap_req); }
N = (dst >> PSW_V_N) & 01;
Z = (dst >> PSW_V_Z) & 01;
V = (dst >> PSW_V_V) & 01;
C = (dst >> PSW_V_C) & 01;
break;
case 065: /* MFPD */
if (dstreg) {
if ((dstspec == 6) && (cm != pm)) dst = STACKFILE[pm];
else dst = R[dstspec]; }
else dst = ReadW ((GeteaW (dstspec) & 0177777) | calc_ds (pm));
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = 0;
SP = (SP - 2) & 0177777;
if (update_MM) MMR1 = calc_MMR1 (0366);
WriteW (dst, SP | dsenable);
if ((cm == KERNEL) && (SP < STKLIM)) {
setTRAP (TRAP_YEL);
setCPUERR (CPUE_YEL); }
break;
case 066: /* MTPD */
dst = ReadW (SP | dsenable);
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = 0;
SP = (SP + 2) & 0177777;
if (update_MM) MMR1 = 026;
if (dstreg) {
if ((dstspec == 6) && (cm != pm)) STACKFILE[pm] = dst;
else R[dstspec] = dst; }
else WriteW (dst, (GeteaW (dstspec) & 0177777) | calc_ds (pm));
break;
case 067: /* MFPS */
dst = (ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) |
(N << PSW_V_N) | (Z << PSW_V_Z) |
(V << PSW_V_V) | (C << PSW_V_C);
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
V = 0;
if (dstreg) R[dstspec] = (dst & 0200)? 0177400 | dst: dst;
else WriteB (dst, GeteaB (dstspec));
break;
default:
setTRAP (TRAP_ILL);
break; } /* end switch SOPs */
break; /* end case 010 */
/* Opcodes 11 - 16: double operand byte instructions
Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)]
Sub: v = [sign (src) != sign (src2)] and [sign (src) = sign (result)]
*/
case 011: /* MOVB */
dst = srcreg? R[srcspec] & 0377: ReadB (GeteaB (srcspec));
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
V = 0;
if (dstreg) R[dstspec] = (dst & 0200)? 0177400 | dst: dst;
else WriteB (dst, GeteaB (dstspec));
break;
case 012: /* CMPB */
src = srcreg? R[srcspec] & 0377: ReadB (GeteaB (srcspec));
src2 = dstreg? R[dstspec] & 0377: ReadB (GeteaB (dstspec));
dst = (src - src2) & 0377;
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
V = GET_SIGN_B ((src ^ src2) & (~src2 ^ dst));
C = (src < src2);
break;
case 013: /* BITB */
src = srcreg? R[srcspec]: ReadB (GeteaB (srcspec));
src2 = dstreg? R[dstspec]: ReadB (GeteaB (dstspec));
dst = (src2 & src) & 0377;
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
V = 0;
break;
case 014: /* BICB */
src = srcreg? R[srcspec]: ReadB (GeteaB (srcspec));
src2 = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
dst = (src2 & ~src) & 0377;
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
V = 0;
if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
else PWriteB (dst, last_pa);
break;
case 015: /* BISB */
src = srcreg? R[srcspec]: ReadB (GeteaB (srcspec));
src2 = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec));
dst = (src2 | src) & 0377;
N = GET_SIGN_B (dst);
Z = GET_Z (dst);
V = 0;
if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst;
else PWriteB (dst, last_pa);
break;
case 016: /* SUB */
src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec));
src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec));
dst = (src2 - src) & 0177777;
N = GET_SIGN_W (dst);
Z = GET_Z (dst);
V = GET_SIGN_W ((src ^ src2) & (~src ^ dst));
C = (src2 < src);
if (dstreg) R[dstspec] = dst;
else PWriteW (dst, last_pa);
break;
/* Opcode 17: floating point */
case 017:
fp11 (IR); /* floating point */
break; /* end case 017 */
} /* end switch op */
} /* end main loop */
/* Simulation halted */
PSW = (cm << PSW_V_CM) | (pm << PSW_V_PM) | (rs << PSW_V_RS) |
(ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) |
(N << PSW_V_N) | (Z << PSW_V_Z) | (V << PSW_V_V) | (C << PSW_V_C);
for (i = 0; i < 6; i++) REGFILE[i][rs] = R[i];
STACKFILE[cm] = SP;
saved_PC = PC & 0177777;
return reason;
}
/* Effective address calculations
Inputs:
spec = specifier <5:0>
Outputs:
ea = effective address
<15:0> = virtual address
<16> = instruction/data data space
<18:17> = mode
Data space calculation: the PDP-11 features both instruction and data
spaces. Instruction space contains the instruction and any sequential
add ons (eg, immediates, absolute addresses). Data space contains all
data operands and indirect addresses. If data space is enabled, then
memory references are directed according to these rules:
Mode Index ref Indirect ref Direct ref
10..16 na na data
17 na na instruction
20..26 na na data
27 na na instruction
30..36 na data data
37 na instruction (absolute) data
40..46 na na data
47 na na instruction
50..56 na data data
57 na instruction data
60..67 instruction na data
70..77 instruction data data
According to the PDP-11 Architecture Handbook, MMR1 records all
autoincrement and autodecrement operations, including those which
explicitly reference the PC. For the J-11, this is only true for
autodecrement operands, autodecrement deferred operands, and
autoincrement destination operands that involve a write to memory.
The simulator follows the Handbook, for simplicity.
Notes:
- dsenable will direct a reference to data space if data space is enabled
- ds will direct a reference to data space if data space is enabled AND if
the specifier register is not PC; this is used for 17, 27, 37, 47, 57
- Modes 2x, 3x, 4x, and 5x must update MMR1 if updating enabled
- Modes 46 and 56 must check for stack overflow if kernel mode
*/
/* Effective address calculation for words */
int32 GeteaW (int32 spec)
{
int32 adr, reg, ds;
reg = spec & 07; /* register number */
ds = (reg == 7)? isenable: dsenable; /* dspace if not PC */
switch (spec >> 3) { /* decode spec<5:3> */
default: /* can't get here */
case 1: /* (R) */
return (R[reg] | ds);
case 2: /* (R)+ */
R[reg] = ((adr = R[reg]) + 2) & 0177777;
if (update_MM) MMR1 = calc_MMR1 (020 | reg);
return (adr | ds);
case 3: /* @(R)+ */
R[reg] = ((adr = R[reg]) + 2) & 0177777;
if (update_MM) MMR1 = calc_MMR1 (020 | reg);
adr = ReadW (adr | ds);
return (adr | dsenable);
case 4: /* -(R) */
adr = R[reg] = (R[reg] - 2) & 0177777;
if (update_MM) MMR1 = calc_MMR1 (0360 | reg);
if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) {
setTRAP (TRAP_YEL);
setCPUERR (CPUE_YEL); }
return (adr | ds);
case 5: /* @-(R) */
adr = R[reg] = (R[reg] - 2) & 0177777;
if (update_MM) MMR1 = calc_MMR1 (0360 | reg);
if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) {
setTRAP (TRAP_YEL);
setCPUERR (CPUE_YEL); }
adr = ReadW (adr | ds);
return (adr | dsenable);
case 6: /* d(r) */
adr = ReadW (PC | isenable);
PC = (PC + 2) & 0177777;
return (((R[reg] + adr) & 0177777) | dsenable);
case 7: /* @d(R) */
adr = ReadW (PC | isenable);
PC = (PC + 2) & 0177777;
adr = ReadW (((R[reg] + adr) & 0177777) | dsenable);
return (adr | dsenable);
} /* end switch */
}
/* Effective address calculation for bytes */
int32 GeteaB (int32 spec)
{
int32 adr, reg, ds, delta;
reg = spec & 07; /* reg number */
ds = (reg == 7)? isenable: dsenable; /* dspace if not PC */
switch (spec >> 3) { /* decode spec<5:3> */
default: /* can't get here */
case 1: /* (R) */
return (R[reg] | ds);
case 2: /* (R)+ */
delta = 1 + (reg >= 6); /* 2 if R6, PC */
R[reg] = ((adr = R[reg]) + delta) & 0177777;
if (update_MM) MMR1 = calc_MMR1 ((delta << 3) | reg);
return (adr | ds);
case 3: /* @(R)+ */
adr = R[reg];
R[reg] = ((adr = R[reg]) + 2) & 0177777;
if (update_MM) MMR1 = calc_MMR1 (020 | reg);
adr = ReadW (adr | ds);
return (adr | dsenable);
case 4: /* -(R) */
delta = 1 + (reg >= 6); /* 2 if R6, PC */
adr = R[reg] = (R[reg] - delta) & 0177777;
if (update_MM) MMR1 = calc_MMR1 ((((-delta) & 037) << 3) | reg);
if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) {
setTRAP (TRAP_YEL);
setCPUERR (CPUE_YEL); }
return (adr | ds);
case 5: /* @-(R) */
adr = R[reg] = (R[reg] - 2) & 0177777;
if (update_MM) MMR1 = calc_MMR1 (0360 | reg);
if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) {
setTRAP (TRAP_YEL);
setCPUERR (CPUE_YEL); }
adr = ReadW (adr | ds);
return (adr | dsenable);
case 6: /* d(r) */
adr = ReadW (PC | isenable);
PC = (PC + 2) & 0177777;
return (((R[reg] + adr) & 0177777) | dsenable);
case 7: /* @d(R) */
adr = ReadW (PC | isenable);
PC = (PC + 2) & 0177777;
adr = ReadW (((R[reg] + adr) & 0177777) | dsenable);
return (adr | dsenable);
} /* end switch */
}
/* Read byte and word routines, read only and read-modify-write versions
Inputs:
va = virtual address, <18:16> = mode, I/D space
Outputs:
data = data read from memory or I/O space
*/
int32 ReadW (int32 va)
{
int32 pa, data;
if (va & 1) { /* odd address? */
setCPUERR (CPUE_ODD);
ABORT (TRAP_ODD); }
pa = relocR (va); /* relocate */
if (ADDR_IS_MEM (pa)) return (M[pa >> 1]); /* memory address? */
if (pa < IOPAGEBASE) { /* I/O address? */
setCPUERR (CPUE_NXM);
ABORT (TRAP_NXM); }
if (iopageR (&data, pa, READ) != SCPE_OK) { /* invalid I/O addr? */
setCPUERR (CPUE_TMO);
ABORT (TRAP_NXM); }
return data;
}
int32 ReadB (int32 va)
{
int32 pa, data;
pa = relocR (va); /* relocate */
if (ADDR_IS_MEM (pa)) return (va & 1? M[pa >> 1] >> 8: M[pa >> 1]) & 0377;
if (pa < IOPAGEBASE) { /* I/O address? */
setCPUERR (CPUE_NXM);
ABORT (TRAP_NXM); }
if (iopageR (&data, pa, READ) != SCPE_OK) { /* invalid I/O addr? */
setCPUERR (CPUE_TMO);
ABORT (TRAP_NXM); }
return ((va & 1)? data >> 8: data) & 0377;
}
int32 ReadMW (int32 va)
{
int32 data;
if (va & 1) { /* odd address? */
setCPUERR (CPUE_ODD);
ABORT (TRAP_ODD); }
last_pa = relocW (va); /* reloc, wrt chk */
if (ADDR_IS_MEM (last_pa)) return (M[last_pa >> 1]); /* memory address? */
if (last_pa < IOPAGEBASE) { /* I/O address? */
setCPUERR (CPUE_NXM);
ABORT (TRAP_NXM); }
if (iopageR (&data, last_pa, READ) != SCPE_OK) { /* invalid I/O addr? */
setCPUERR (CPUE_TMO);
ABORT (TRAP_NXM); }
return data;
}
int32 ReadMB (int32 va)
{
int32 data;
last_pa = relocW (va); /* reloc, wrt chk */
if (ADDR_IS_MEM (last_pa))
return (va & 1? M[last_pa >> 1] >> 8: M[last_pa >> 1]) & 0377;
if (last_pa < IOPAGEBASE) { /* I/O address? */
setCPUERR (CPUE_NXM);
ABORT (TRAP_NXM); }
if (iopageR (&data, last_pa, READ) != SCPE_OK) { /* invalid I/O addr? */
setCPUERR (CPUE_TMO);
ABORT (TRAP_NXM); }
return ((va & 1)? data >> 8: data) & 0377;
}
/* Write byte and word routines
Inputs:
data = data to be written
va = virtual address, <18:16> = mode, I/D space, or
pa = physical address
Outputs: none
*/
void WriteW (int32 data, int32 va)
{
int32 pa;
if (va & 1) { /* odd address? */
setCPUERR (CPUE_ODD);
ABORT (TRAP_ODD); }
pa = relocW (va); /* relocate */
if (ADDR_IS_MEM (pa)) { /* memory address? */
M[pa >> 1] = data;
return; }
if (pa < IOPAGEBASE) { /* I/O address? */
setCPUERR (CPUE_NXM);
ABORT (TRAP_NXM); }
if (iopageW (data, pa, WRITE) != SCPE_OK) { /* invalid I/O addr? */
setCPUERR (CPUE_TMO);
ABORT (TRAP_NXM); }
return;
}
void WriteB (int32 data, int32 va)
{
int32 pa;
pa = relocW (va); /* relocate */
if (ADDR_IS_MEM (pa)) { /* memory address? */
if (va & 1) M[pa >> 1] = (M[pa >> 1] & 0377) | (data << 8);
else M[pa >> 1] = (M[pa >> 1] & ~0377) | data;
return; }
if (pa < IOPAGEBASE) { /* I/O address? */
setCPUERR (CPUE_NXM);
ABORT (TRAP_NXM); }
if (iopageW (data, pa, WRITEB) != SCPE_OK) { /* invalid I/O addr? */
setCPUERR (CPUE_TMO);
ABORT (TRAP_NXM); }
return;
}
void PWriteW (int32 data, int32 pa)
{
if (ADDR_IS_MEM (pa)) { /* memory address? */
M[pa >> 1] = data;
return; }
if (pa < IOPAGEBASE) { /* I/O address? */
setCPUERR (CPUE_NXM);
ABORT (TRAP_NXM); }
if (iopageW (data, pa, WRITE) != SCPE_OK) { /* invalid I/O addr? */
setCPUERR (CPUE_TMO);
ABORT (TRAP_NXM); }
return;
}
void PWriteB (int32 data, int32 pa)
{
if (ADDR_IS_MEM (pa)) { /* memory address? */
if (pa & 1) M[pa >> 1] = (M[pa >> 1] & 0377) | (data << 8);
else M[pa >> 1] = (M[pa >> 1] & ~0377) | data;
return; }
if (pa < IOPAGEBASE) { /* I/O address? */
setCPUERR (CPUE_NXM);
ABORT (TRAP_NXM); }
if (iopageW (data, pa, WRITEB) != SCPE_OK) { /* invalid I/O addr? */
setCPUERR (CPUE_TMO);
ABORT (TRAP_NXM); }
return;
}
/* Relocate virtual address, read access
Inputs:
va = virtual address, <18:16> = mode, I/D space
Outputs:
pa = physical address
On aborts, this routine aborts back to the top level simulator
with an appropriate trap code.
Notes:
- APRFILE[UNUSED] is all zeroes, forcing non-resident abort
- Aborts must update MMR0<15:13,6:1> if updating is enabled
*/
int32 relocR (int32 va)
{
int32 dbn, plf, apridx, apr, pa;
if (MMR0 & MMR0_MME) { /* if mmgt */
apridx = (va >> VA_V_APF) & 077; /* index into APR */
apr = APRFILE[apridx]; /* with va<18:13> */
dbn = va & VA_BN; /* extr block num */
plf = (apr & PDR_PLF) >> 2; /* extr page length */
if ((apr & PDR_NR) == 0) { /* if non-resident */
if (update_MM) MMR0 = MMR0 | (apridx << MMR0_V_PAGE);
MMR0 = MMR0 | MMR0_NR;
ABORT (TRAP_MME); } /* abort ref */
if ((apr & PDR_ED)? dbn < plf: dbn > plf) { /* if pg lnt error */
if (update_MM) MMR0 = MMR0 | (apridx << MMR0_V_PAGE);
MMR0 = MMR0 | MMR0_PL;
ABORT (TRAP_MME); } /* abort ref */
pa = (va & VA_DF) + ((apr >> 10) & 017777700);
if ((MMR3 & MMR3_M22E) == 0) {
pa = pa & 0777777;
if (pa >= 0760000) pa = 017000000 | pa; } }
else { pa = va & 0177777; /* mmgt off */
if (pa >= 0160000) pa = 017600000 | pa; }
return pa;
}
/* Relocate virtual address, write access
Inputs:
va = virtual address, <18:16> = mode, I/D space
Outputs:
pa = physical address
On aborts, this routine aborts back to the top level simulator
with an appropriate trap code.
Notes:
- APRFILE[UNUSED] is all zeroes, forcing non-resident abort
- Aborts must update MMR0<15:13,6:1> if updating is enabled
*/
int32 relocW (int32 va)
{
int32 dbn, plf, apridx, apr, pa;
if (MMR0 & MMR0_MME) { /* if mmgt */
apridx = (va >> VA_V_APF) & 077; /* index into APR */
apr = APRFILE[apridx]; /* with va<18:13> */
dbn = va & VA_BN; /* extr block num */
plf = (apr & PDR_PLF) >> 2; /* extr page length */
if ((apr & PDR_NR) == 0) { /* if non-resident */
if (update_MM) MMR0 = MMR0 | (apridx << MMR0_V_PAGE);
MMR0 = MMR0 | MMR0_NR;
ABORT (TRAP_MME); } /* abort ref */
if ((apr & PDR_ED)? dbn < plf: dbn > plf) { /* if pg lnt error */
if (update_MM) MMR0 = MMR0 | (apridx << MMR0_V_PAGE);
MMR0 = MMR0 | MMR0_PL;
ABORT (TRAP_MME); } /* abort ref */
if ((apr & PDR_RW) == 0) { /* if rd only error */
if (update_MM) MMR0 = MMR0 | (apridx << MMR0_V_PAGE);
MMR0 = MMR0 | MMR0_RO;
ABORT (TRAP_MME); } /* abort ref */
APRFILE[apridx] = apr | PDR_W; /* set W */
pa = (va & VA_DF) + ((apr >> 10) & 017777700);
if ((MMR3 & MMR3_M22E) == 0) {
pa = pa & 0777777;
if (pa >= 0760000) pa = 017000000 | pa; } }
else { pa = va & 0177777; /* mmgt off */
if (pa >= 0160000) pa = 017600000 | pa; }
return pa;
}
/* Relocate virtual address, console access
Inputs:
va = virtual address
sw = switches
Outputs:
pa = physical address
On aborts, this routine returns -1
*/
int32 relocC (int32 va, int32 sw)
{
int32 mode, dbn, plf, apridx, apr, pa;
if (MMR0 & MMR0_MME) { /* if mmgt */
if (sw & SWMASK ('K')) mode = KERNEL;
else if (sw & SWMASK ('S')) mode = SUPER;
else if (sw & SWMASK ('U')) mode = USER;
else if (sw & SWMASK ('P')) mode = (PSW >> PSW_V_PM) & 03;
else mode = (PSW >> PSW_V_CM) & 03;
va = va | ((sw & SWMASK ('D'))? calc_ds (mode): calc_is (mode));
apridx = (va >> VA_V_APF) & 077; /* index into APR */
apr = APRFILE[apridx]; /* with va<18:13> */
dbn = va & VA_BN; /* extr block num */
plf = (apr & PDR_PLF) >> 2; /* extr page length */
if ((apr & PDR_NR) == 0) return -1;
if ((apr & PDR_ED)? dbn < plf: dbn > plf) return -1;
pa = (va & VA_DF) + ((apr >> 10) & 017777700);
if ((MMR3 & MMR3_M22E) == 0) {
pa = pa & 0777777;
if (pa >= 0760000) pa = 017000000 | pa; } }
else { pa = va & 0177777; /* mmgt off */
if (pa >= 0160000) pa = 017600000 | pa; }
return pa;
}
/* I/O page lookup and linkage routines
Inputs:
*data = pointer to data to read, if READ
data = data to store, if WRITE or WRITEB
pa = address
access = READ, WRITE, or WRITEB
Outputs:
status = SCPE_OK or SCPE_NXM
*/
t_stat iopageR (int32 *data, int32 pa, int32 access)
{
t_stat stat;
struct iolink *p;
for (p = &iotable[0]; p -> low != 0; p++ ) {
if ((pa >= p -> low) && (pa <= p -> high)) {
stat = p -> read (data, pa, access);
trap_req = calc_ints (ipl, int_req, trap_req);
return stat; } }
return SCPE_NXM;
}
t_stat iopageW (int32 data, int32 pa, int32 access)
{
t_stat stat;
struct iolink *p;
for (p = &iotable[0]; p -> low != 0; p++ ) {
if ((pa >= p -> low) && (pa <= p -> high)) {
stat = p -> write (data, pa, access);
trap_req = calc_ints (ipl, int_req, trap_req);
return stat; } }
return SCPE_NXM;
}
/* I/O page routines for CPU registers
Switch register and memory management registers
SR 17777570 read only
MMR0 17777572 read/write, certain bits unimplemented or read only
MMR1 17777574 read only
MMR2 17777576 read only
MMR3 17777516 read/write, certain bits unimplemented
*/
t_stat SR_MMR012_rd (int32 *data, int32 pa, int32 access)
{
switch ((pa >> 1) & 3) { /* decode pa<2:1> */
case 0: /* SR */
*data = SR;
return SCPE_OK;
case 1: /* MMR0 */
*data = MMR0 & MMR0_IMP;
return SCPE_OK;
case 2: /* MMR1 */
*data = MMR1;
return SCPE_OK;
case 3: /* MMR2 */
*data = MMR2;
return SCPE_OK; } /* end switch pa */
}
t_stat SR_MMR012_wr (int32 data, int32 pa, int32 access)
{
switch ((pa >> 1) & 3) { /* decode pa<2:1> */
case 0: /* DR */
DR = data;
return SCPE_OK;
case 1: /* MMR0 */
if (access == WRITEB) data = (pa & 1)?
(MMR0 & 0377) | (data << 8): (MMR0 & ~0377) | data;
MMR0 = (MMR0 & ~MMR0_RW) | (data & MMR0_RW);
return SCPE_OK;
default: /* MMR1, MMR2 */
return SCPE_OK; } /* end switch pa */
}
t_stat MMR3_rd (int32 *data, int32 pa, int32 access) /* MMR3 */
{
*data = MMR3 & MMR3_IMP;
return SCPE_OK;
}
t_stat MMR3_wr (int32 data, int32 pa, int32 access) /* MMR3 */
{
if (pa & 1) return SCPE_OK;
MMR3 = data & MMR3_RW;
if (cpu_unit.flags & UNIT_18B)
MMR3 = MMR3 & ~(MMR3_BME + MMR3_M22E); /* for UNIX V6 */
dsenable = calc_ds (cm);
return SCPE_OK;
}
/* PARs and PDRs. These are grouped in I/O space as follows:
17772200 - 17772276 supervisor block
17772300 - 17772376 kernel block
17777600 - 17777676 user block
Within each block, the subblocks are I PDR's, D PDR's, I PAR's, D PAR's
Thus, the algorithm for converting between I/O space addresses and
APRFILE indices is as follows:
idx<3:0> = dspace'page = pa<4:1>
par = PDR vs PAR = pa<5>
idx<5:4> = ker/sup/user = pa<8>'~pa<6>
Note that the W bit is read only; it is cleared by any write to an APR
*/
t_stat APR_rd (int32 *data, int32 pa, int32 access)
{
t_stat left, idx;
idx = (pa >> 1) & 017; /* dspace'page */
left = (pa >> 5) & 1; /* PDR vs PAR */
if ((pa & 0100) == 0) idx = idx | 020; /* 1 for super, user */
if (pa & 0400) idx = idx | 040; /* 1 for user only */
*data = left? (APRFILE[idx] >> 16) & 0177777: APRFILE[idx] & PDR_IMP;
return SCPE_OK;
}
t_stat APR_wr (int32 data, int32 pa, int32 access)
{
int32 left, idx, curr;
idx = (pa >> 1) & 017; /* dspace'page */
left = (pa >> 5) & 1; /* PDR vs PAR */
if ((pa & 0100) == 0) idx = idx | 020; /* 1 for super, user */
if (pa & 0400) idx = idx | 040; /* 1 for user only */
curr = left? (APRFILE[idx] >> 16) & 0177777: APRFILE[idx] & PDR_IMP;
if (access == WRITEB) data = (pa & 1)?
(curr & 0377) | (data << 8): (curr & ~0377) | data;
if (left) APRFILE[idx] =
((APRFILE[idx] & 0177777) | (data << 16)) & ~PDR_W;
else APRFILE[idx] =
((APRFILE[idx] & ~PDR_RW) | (data & PDR_RW)) & ~PDR_W;
return SCPE_OK;
}
/* CPU control registers
MEMERR 17777744 read only, clear on write
CCR 17777746 read/write
MAINT 17777750 read only
HITMISS 17777752 read only
CPUERR 17777766 read only, clear on write
PIRQ 17777772 read/write, with side effects
PSW 17777776 read/write, with side effects
*/
t_stat CPU_rd (int32 *data, int32 pa, int32 access)
{
switch ((pa >> 1) & 017) { /* decode pa<4:1> */
case 2: /* MEMERR */
*data = MEMERR;
MEMERR = 0;
return SCPE_OK;
case 3: /* CCR */
*data = CCR;
return SCPE_OK;
case 4: /* MAint */
*data = MAINT;
return SCPE_OK;
case 5: /* Hit/miss */
*data = HITMISS;
return SCPE_OK;
case 013: /* CPUERR */
*data = CPUERR & CPUE_IMP;
CPUERR = 0;
return SCPE_OK;
case 015: /* PIRQ */
*data = PIRQ;
return SCPE_OK;
case 017: /* PSW */
if (access == READC) *data = PSW;
else *data = (cm << PSW_V_CM) | (pm << PSW_V_PM) | (rs << PSW_V_RS) |
(ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) |
(N << PSW_V_N) | (Z << PSW_V_Z) |
(V << PSW_V_V) | (C << PSW_V_C);
return SCPE_OK; } /* end switch PA */
return SCPE_NXM; /* unimplemented */
}
/* CPU control registers, continued */
t_stat CPU_wr (int32 data, int32 pa, int32 access)
{
int32 i, pl, curr, oldrs;
switch ((pa >> 1) & 017) { /* decode pa<4:1> */
case 2: /* MEMERR */
MEMERR = 0;
return SCPE_OK;
case 3: /* CCR */
if (access == WRITEB) data = (pa & 1)?
(CCR & 0377) | (data << 8): (CCR & ~0377) | data;
CCR = data;
return SCPE_OK;
case 4: /* MAINT */
return SCPE_OK;
case 5: /* Hit/miss */
return SCPE_OK;
case 013: /* CPUERR */
CPUERR = 0;
return SCPE_OK;
case 015: /* PIRQ */
if (access == WRITEB) {
if (pa & 1) data = data << 8;
else return SCPE_OK; }
int_req = int_req & ~(INT_PIR7 + INT_PIR6 + INT_PIR5 + INT_PIR4 +
INT_PIR3 + INT_PIR2 + INT_PIR1);
PIRQ = data & PIRQ_RW;
pl = 0;
if (PIRQ & PIRQ_PIR1) { int_req = int_req | INT_PIR1; pl = 0042; }
if (PIRQ & PIRQ_PIR2) { int_req = int_req | INT_PIR2; pl = 0104; }
if (PIRQ & PIRQ_PIR3) { int_req = int_req | INT_PIR3; pl = 0146; }
if (PIRQ & PIRQ_PIR4) { int_req = int_req | INT_PIR4; pl = 0210; }
if (PIRQ & PIRQ_PIR5) { int_req = int_req | INT_PIR5; pl = 0252; }
if (PIRQ & PIRQ_PIR6) { int_req = int_req | INT_PIR6; pl = 0314; }
if (PIRQ & PIRQ_PIR7) { int_req = int_req | INT_PIR7; pl = 0356; }
PIRQ = PIRQ | pl;
return SCPE_OK;
/* CPU control registers, continued
Note: Explicit writes to the PSW do not modify the T bit
*/
case 017: /* PSW */
if (access == WRITEC) { /* console access? */
PSW = data & PSW_RW;
return SCPE_OK; }
curr = (cm << PSW_V_CM) | (pm << PSW_V_PM) | (rs << PSW_V_RS) |
(ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) |
(N << PSW_V_N) | (Z << PSW_V_Z) |
(V << PSW_V_V) | (C << PSW_V_C);
STACKFILE[cm] = SP;
if (access == WRITEB) data = (pa & 1)?
(curr & 0377) | (data << 8): (curr & ~0377) | data;
curr = (curr & ~PSW_RW) | (data & PSW_RW);
oldrs = rs;
cm = (curr >> PSW_V_CM) & 03; /* call calc_is,ds */
pm = (curr >> PSW_V_PM) & 03;
rs = (curr >> PSW_V_RS) & 01;
ipl = (curr >> PSW_V_IPL) & 07;
N = (curr >> PSW_V_N) & 01;
Z = (curr >> PSW_V_Z) & 01;
V = (curr >> PSW_V_V) & 01;
C = (curr >> PSW_V_C) & 01;
if (rs != oldrs) {
for (i = 0; i < 6; i++) {
REGFILE[i][oldrs] = R[i];
R[i] = REGFILE[i][rs]; } }
SP = STACKFILE[cm];
isenable = calc_is (cm);
dsenable = calc_ds (cm);
return SCPE_OK; } /* end switch pa */
return SCPE_NXM; /* unimplemented */
}
/* Reset routine */
t_stat cpu_reset (DEVICE *dptr)
{
PIRQ = MMR0 = MMR1 = MMR2 = MMR3 = 0;
DR = CPUERR = MEMERR = CCR = HITMISS = 0;
PSW = 000340;
trap_req = 0;
wait_state = 0;
if (M == NULL) M = calloc (MEMSIZE >> 1, sizeof (unsigned int16));
if (M == NULL) return SCPE_MEM;
return cpu_svc (&cpu_unit);
}
/* Memory examine */
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
{
int32 iodata;
t_stat stat;
if (vptr == NULL) return SCPE_ARG;
if (sw & SWMASK ('V')) { /* -v */
if (addr >= VASIZE) return SCPE_NXM;
addr = relocC (addr, sw); /* relocate */
if (addr < 0) return SCPE_REL; }
if (addr < MEMSIZE) {
*vptr = M[addr >> 1] & 0177777;
return SCPE_OK; }
if (addr < IOPAGEBASE) return SCPE_NXM;
stat = iopageR (&iodata, addr, READC);
*vptr = iodata;
return stat;
}
/* Memory deposit */
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
{
if (sw & SWMASK ('V')) { /* -v */
if (addr >= VASIZE) return SCPE_NXM;
addr = relocC (addr, sw); /* relocate */
if (addr < 0) return SCPE_REL; }
if (addr < MEMSIZE) {
M[addr >> 1] = val & 0177777;
return SCPE_OK; }
if (addr < IOPAGEBASE) return SCPE_NXM;
return iopageW ((int32) val, addr, WRITEC);
}
/* Breakpoint service */
t_stat cpu_svc (UNIT *uptr)
{
if ((ibkpt_addr & ~ILL_ADR_FLAG) == save_ibkpt) ibkpt_addr = save_ibkpt;
save_ibkpt = -1;
return SCPE_OK;
}
/* Memory allocation */
t_stat cpu_set_size (UNIT *uptr, int32 value)
{
int32 mc = 0;
t_addr i, clim;
unsigned int16 *nM = NULL;
if ((value <= 0) || (value > MAXMEMSIZE) || ((value & 07777) != 0))
return SCPE_ARG;
for (i = value; i < MEMSIZE; i = i + 2) mc = mc | M[i >> 1];
if ((mc != 0) && !get_yn ("Really truncate memory [N]?", FALSE))
return SCPE_OK;
nM = calloc (value >> 1, sizeof (unsigned int16));
if (nM == NULL) return SCPE_MEM;
clim = (((t_addr) value) < MEMSIZE)? value: MEMSIZE;
for (i = 0; i < clim; i = i + 2) nM[i >> 1] = M[i >> 1];
free (M);
M = nM;
MEMSIZE = value;
return SCPE_OK; }