blob: a7f79bc1cc3507d9b8995280193d77e8875a1771 [file] [log] [blame] [raw]
/* 3b2_cpu.c: AT&T 3B2 Model 400 CPU (WE32100) Implementation
Copyright (c) 2017, Seth J. Morabito
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 THE AUTHORS OR COPYRIGHT HOLDERS
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 the author shall
not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization
from the author.
*/
#include <assert.h>
#include "3b2_cpu.h"
#include "rom_400_bin.h"
#define MAX_SUB_RETURN_SKIP 9
/* RO memory. */
uint32 *ROM = NULL;
/* Main memory. */
uint32 *RAM = NULL;
/* Save environment for setjmp/longjmp */
jmp_buf save_env;
volatile uint32 abort_context;
/* Pointer to the last decoded instruction */
instr *cpu_instr;
/* The instruction to use if there is no history storage */
instr inst;
/* Circular buffer of instructions */
instr *INST = NULL;
uint32 cpu_hist_size = 0;
uint32 cpu_hist_p = 0;
t_bool cpu_in_wait = FALSE;
volatile size_t cpu_exception_stack_depth = 0;
volatile int32 stop_reason;
volatile uint32 abort_reason;
extern uint16 csr_data;
/* Register data */
uint32 R[16];
/* Other global CPU state */
int8 cpu_dtype = -1; /* Default datatype for the current
instruction */
int8 cpu_etype = -1; /* Currently set expanded datatype */
t_bool cpu_nmi = FALSE; /* If set, there has been an NMI */
uint8 cpu_ilen = 0; /* Length (in bytes) of instruction
currently being executed */
t_bool cpu_ex_halt = FALSE; /* Flag to halt on exceptions /
traps */
t_bool cpu_km = FALSE; /* If true, kernel mode has been forced
for memory access */
BITFIELD psw_bits[] = {
BITFFMT(ET,2,%d), /* Exception Type */
BIT(TM), /* Trace Mask */
BITFFMT(ISC,4,%d), /* Internal State Code */
BIT(I), /* Register Initial Context (I) */
BIT(R), /* Register Initial Context (R) */
BITFFMT(PM,2,%d), /* Previous Execution Level */
BITFFMT(CM,2,%d), /* Current Execution Level */
BITFFMT(IPL,4,%d), /* Interrupt Priority Level */
BIT(TE), /* Trace Enable */
BIT(C), /* Carry */
BIT(V), /* Overflow */
BIT(Z), /* Zero */
BIT(N), /* Negative */
BIT(OE), /* Enable Overflow Trap */
BIT(CD), /* Cache Disable */
BIT(QIE), /* Quick-Interrupt Enable */
BIT(CFD), /* Cache Flush Disable */
BITNCF(6), /* Unused */
ENDBITS
};
/* Registers. */
REG cpu_reg[] = {
{ HRDATAD (PC, R[NUM_PC], 32, "Program Counter") },
{ HRDATAD (R0, R[0], 32, "General purpose register 0") },
{ HRDATAD (R1, R[1], 32, "General purpose register 1") },
{ HRDATAD (R2, R[2], 32, "General purpose register 2") },
{ HRDATAD (R3, R[3], 32, "General purpose register 3") },
{ HRDATAD (R4, R[4], 32, "General purpose register 4") },
{ HRDATAD (R5, R[5], 32, "General purpose register 5") },
{ HRDATAD (R6, R[6], 32, "General purpose register 6") },
{ HRDATAD (R7, R[7], 32, "General purpose register 7") },
{ HRDATAD (R8, R[8], 32, "General purpose register 8") },
{ HRDATAD (FP, R[NUM_FP], 32, "Frame Pointer") },
{ HRDATAD (AP, R[NUM_AP], 32, "Argument Pointer") },
{ HRDATADF (PSW, R[NUM_PSW], 32, "Processor Status Word", psw_bits) },
{ HRDATAD (SP, R[NUM_SP], 32, "Stack Pointer") },
{ HRDATAD (PCBP, R[NUM_PCBP], 32, "Process Control Block Pointer") },
{ HRDATAD (ISP, R[NUM_ISP], 32, "Interrupt Stack Pointer") },
{ NULL }
};
static DEBTAB cpu_deb_tab[] = {
{ "READ", READ_MSG, "Memory read activity" },
{ "WRITE", WRITE_MSG, "Memory write activity" },
{ "DECODE", DECODE_MSG, "Instruction decode" },
{ "EXECUTE", EXECUTE_MSG, "Instruction execute" },
{ "INIT", INIT_MSG, "Initialization" },
{ "IRQ", IRQ_MSG, "Interrupt Handling" },
{ "IO", IO_D_MSG, "I/O Dispatch" },
{ "TRACE", TRACE_MSG, "Call Trace" },
{ NULL, 0 }
};
UNIT cpu_unit = { UDATA (NULL, UNIT_FIX|UNIT_BINK|UNIT_IDLE, MAXMEMSIZE) };
#define UNIT_V_EXHALT (UNIT_V_UF + 0) /* halt to console */
#define UNIT_EXHALT (1u << UNIT_V_EXHALT)
MTAB cpu_mod[] = {
{ UNIT_MSIZE, (1u << 20), NULL, "1M",
&cpu_set_size, NULL, NULL, "Set Memory to 1M bytes" },
{ UNIT_MSIZE, (1u << 21), NULL, "2M",
&cpu_set_size, NULL, NULL, "Set Memory to 2M bytes" },
{ UNIT_MSIZE, (1u << 22), NULL, "4M",
&cpu_set_size, NULL, NULL, "Set Memory to 4M bytes" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY",
&cpu_set_hist, &cpu_show_hist, NULL, "Displays instruction history" },
{ MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle },
{ MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL },
{ UNIT_EXHALT, UNIT_EXHALT, "Halt on Exception", "EX_HALT",
NULL, NULL, NULL, "Enables Halt on exceptions and traps" },
{ UNIT_EXHALT, 0, "No halt on exception", "NOEX_HALT",
NULL, NULL, NULL, "Disables Halt on exceptions and traps" },
{ 0 }
};
DEVICE cpu_dev = {
"CPU", /* Name */
&cpu_unit, /* Units */
cpu_reg, /* Registers */
cpu_mod, /* Modifiers */
1, /* Number of Units */
16, /* Address radix */
32, /* Address width */
1, /* Addr increment */
16, /* Data radix */
8, /* Data width */
&cpu_ex, /* Examine routine */
&cpu_dep, /* Deposit routine */
&cpu_reset, /* Reset routine */
&cpu_boot, /* Boot routine */
NULL, /* Attach routine */
NULL, /* Detach routine */
NULL, /* Context */
DEV_DYNM|DEV_DEBUG, /* Flags */
0, /* Debug control flags */
cpu_deb_tab, /* Debug flag names */
&cpu_set_size, /* Memory size change */
NULL /* Logical names */
};
#define HWORD_OP_COUNT 11
mnemonic hword_ops[HWORD_OP_COUNT] = {
{0x3009, 0, OP_NONE, NA, "MVERNO", -1, -1, -1, -1},
{0x300d, 0, OP_NONE, NA, "ENBVJMP", -1, -1, -1, -1},
{0x3013, 0, OP_NONE, NA, "DISVJMP", -1, -1, -1, -1},
{0x3019, 0, OP_NONE, NA, "MOVBLW", -1, -1, -1, -1},
{0x301f, 0, OP_NONE, NA, "STREND", -1, -1, -1, -1},
{0x302f, 1, OP_DESC, WD, "INTACK", -1, -1, -1, -1},
{0x3035, 0, OP_NONE, NA, "STRCPY", -1, -1, -1, -1},
{0x3045, 0, OP_NONE, NA, "RETG", -1, -1, -1, -1},
{0x3061, 0, OP_NONE, NA, "GATE", -1, -1, -1, -1},
{0x30ac, 0, OP_NONE, NA, "CALLPS", -1, -1, -1, -1},
{0x30c8, 0, OP_NONE, NA, "RETPS", -1, -1, -1, -1}
};
/* Lookup table of operand types. */
mnemonic ops[256] = {
{0x00, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x01, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x02, 2, OP_COPR, WD, "SPOPRD", -1, -1, -1, -1},
{0x03, 3, OP_COPR, WD, "SPOPD2", -1, -1, -1, -1},
{0x04, 2, OP_DESC, WD, "MOVAW", 0, -1, -1, 1},
{0x05, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x06, 2, OP_COPR, WD, "SPOPRT", -1, -1, -1, -1},
{0x07, 3, OP_COPR, WD, "SPOPT2", -1, -1, -1, -1},
{0x08, 0, OP_NONE, NA, "RET", -1, -1, -1, -1},
{0x09, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x0a, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x0b, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x0c, 2, OP_DESC, WD, "MOVTRW", 0, -1, -1, 1},
{0x0d, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x0e, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x0f, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x10, 1, OP_DESC, WD, "SAVE", 0, -1, -1, -1},
{0x11, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x12, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x13, 2, OP_COPR, WD, "SPOPWD", -1, -1, -1, -1},
{0x14, 1, OP_BYTE, NA, "EXTOP", -1, -1, -1, -1},
{0x15, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x16, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x17, 2, OP_COPR, WD, "SPOPWT", -1, -1, -1, -1},
{0x18, 1, OP_DESC, WD, "RESTORE", 0, -1, -1, -1},
{0x19, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x1a, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x1b, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x1c, 1, OP_DESC, WD, "SWAPWI", -1, -1, -1, 0}, /* 3-122 252 */
{0x1d, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x1e, 1, OP_DESC, HW, "SWAPHI", -1, -1, -1, 0}, /* 3-122 252 */
{0x1f, 1, OP_DESC, BT, "SWAPBI", -1, -1, -1, 0}, /* 3-122 252 */
{0x20, 1, OP_DESC, WD, "POPW", -1, -1, -1, 0},
{0x21, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x22, 2, OP_COPR, WD, "SPOPRS", -1, -1, -1, -1},
{0x23, 3, OP_COPR, WD, "SPOPS2", -1, -1, -1, -1},
{0x24, 1, OP_DESC, NA, "JMP", -1, -1, -1, 0},
{0x25, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x26, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x27, 0, OP_NONE, NA, "CFLUSH", -1, -1, -1, -1},
{0x28, 1, OP_DESC, WD, "TSTW", 0, -1, -1, -1},
{0x29, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x2a, 1, OP_DESC, HW, "TSTH", 0, -1, -1, -1},
{0x2b, 1, OP_DESC, BT, "TSTB", 0, -1, -1, -1},
{0x2c, 2, OP_DESC, WD, "CALL", 0, -1, -1, 1},
{0x2d, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x2e, 0, OP_NONE, NA, "BPT", -1, -1, -1, -1},
{0x2f, 0, OP_NONE, NA, "WAIT", -1, -1, -1, -1},
{0x30, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, /* Two-byte instructions */
{0x31, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x32, 1, OP_COPR, WD, "SPOP", -1, -1, -1, -1},
{0x33, 2, OP_COPR, WD, "SPOPWS", -1, -1, -1, -1},
{0x34, 1, OP_DESC, WD, "JSB", -1, -1, -1, 0},
{0x35, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x36, 1, OP_HALF, NA, "BSBH", -1, -1, -1, 0},
{0x37, 1, OP_BYTE, NA, "BSBB", -1, -1, -1, 0},
{0x38, 2, OP_DESC, WD, "BITW", 0, 1, -1, -1},
{0x39, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x3a, 2, OP_DESC, HW, "BITH", 0, 1, -1, -1},
{0x3b, 2, OP_DESC, BT, "BITB", 0, 1, -1, -1},
{0x3c, 2, OP_DESC, WD, "CMPW", 0, 1, -1, -1},
{0x3d, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x3e, 2, OP_DESC, HW, "CMPH", 0, 1, -1, -1},
{0x3f, 2, OP_DESC, BT, "CMPB", 0, 1, -1, -1},
{0x40, 0, OP_NONE, NA, "RGEQ", -1, -1, -1, -1},
{0x41, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x42, 1, OP_HALF, NA, "BGEH", -1, -1, -1, 0},
{0x43, 1, OP_BYTE, NA, "BGEB", -1, -1, -1, 0},
{0x44, 0, OP_NONE, NA, "RGTR", -1, -1, -1, -1},
{0x45, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x46, 1, OP_HALF, NA, "BGH", -1, -1, -1, 0},
{0x47, 1, OP_BYTE, NA, "BGB", -1, -1, -1, 0},
{0x48, 0, OP_NONE, NA, "RLSS", -1, -1, -1, 0},
{0x49, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x4a, 1, OP_HALF, NA, "BLH", -1, -1, -1, 0},
{0x4b, 1, OP_BYTE, NA, "BLB", -1, -1, -1, 0},
{0x4c, 0, OP_NONE, NA, "RLEQ", -1, -1, -1, -1},
{0x4d, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x4e, 1, OP_HALF, NA, "BLEH", -1, -1, -1, 0},
{0x4f, 1, OP_BYTE, NA, "BLEB", -1, -1, -1, 0},
{0x50, 0, OP_NONE, NA, "BGEQU", -1, -1, -1, 0}, /* aka BCC */
{0x51, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x52, 1, OP_HALF, NA, "BGEUH", -1, -1, -1, 0}, /* aka BCCH */
{0x53, 1, OP_BYTE, NA, "BGEUB", -1, -1, -1, 0}, /* aka BCCB */
{0x54, 0, OP_NONE, NA, "RGTRU", -1, -1, -1, -1},
{0x55, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x56, 1, OP_HALF, NA, "BGUH", -1, -1, -1, 0},
{0x57, 1, OP_BYTE, NA, "BGUB", -1, -1, -1, 0},
{0x58, 0, OP_NONE, NA, "RLSSU", -1, -1, -1, 0}, /* aka BCS */
{0x59, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x5a, 1, OP_HALF, NA, "BLUH", -1, -1, -1, 0}, /* aka BCSH */
{0x5b, 1, OP_BYTE, NA, "BLUB", -1, -1, -1, 0}, /* aka BCSB */
{0x5c, 0, OP_NONE, NA, "RLEQU", -1, -1, -1, -1},
{0x5d, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x5e, 1, OP_HALF, NA, "BLEUH", -1, -1, -1, 0},
{0x5f, 1, OP_BYTE, NA, "BLEUB", -1, -1, -1, 0},
{0x60, 0, OP_NONE, NA, "RVC", -1, -1, -1, -1},
{0x61, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x62, 1, OP_HALF, NA, "BVCH", -1, -1, -1, 0},
{0x63, 1, OP_BYTE, NA, "BVCB", -1, -1, -1, 0},
{0x64, 0, OP_NONE, NA, "RNEQU", -1, -1, -1, -1},
{0x65, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x66, 1, OP_HALF, NA, "BNEH", -1, -1, -1, 0}, /* duplicate of 76 */
{0x67, 1, OP_BYTE, NA, "BNEB", -1, -1, -1, 0}, /* duplicate of 77*/
{0x68, 0, OP_NONE, NA, "RVS", -1, -1, -1, -1},
{0x69, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x6a, 1, OP_HALF, NA, "BVSH", -1, -1, -1, 0},
{0x6b, 1, OP_BYTE, NA, "BVSB", -1, -1, -1, 0},
{0x6c, 0, OP_NONE, NA, "REQLU", -1, -1, -1, -1},
{0x6d, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x6e, 1, OP_HALF, NA, "BEH", -1, -1, -1, 0}, /* duplicate of 7e */
{0x6f, 1, OP_BYTE, NA, "BEB", -1, -1, -1, 0}, /* duplicate of 7f */
{0x70, 0, OP_NONE, NA, "NOP", -1, -1, -1, -1},
{0x71, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x72, 0, OP_NONE, NA, "NOP3", -1, -1, -1, -1},
{0x73, 0, OP_NONE, NA, "NOP2", -1, -1, -1, -1},
{0x74, 0, OP_NONE, NA, "RNEQ", -1, -1, -1, -1},
{0x75, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x76, 1, OP_HALF, NA, "BNEH", -1, -1, -1, 0}, /* duplicate of 66 */
{0x77, 1, OP_BYTE, NA, "BNEB", -1, -1, -1, 0}, /* duplicate of 67 */
{0x78, 0, OP_NONE, NA, "RSB", -1, -1, -1, -1},
{0x79, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x7a, 1, OP_HALF, NA, "BRH", -1, -1, -1, 0},
{0x7b, 1, OP_BYTE, NA, "BRB", -1, -1, -1, 0},
{0x7c, 0, OP_NONE, NA, "REQL", -1, -1, -1, -1},
{0x7d, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x7e, 1, OP_HALF, NA, "BEH", -1, -1, -1, 0}, /* duplicate of 6e */
{0x7f, 1, OP_BYTE, NA, "BEB", -1, -1, -1, 0}, /* duplicate of 6f */
{0x80, 1, OP_DESC, WD, "CLRW", -1, -1, -1, 0},
{0x81, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x82, 1, OP_DESC, HW, "CLRH", -1, -1, -1, 0},
{0x83, 1, OP_DESC, BT, "CLRB", -1, -1, -1, 0},
{0x84, 2, OP_DESC, WD, "MOVW", 0, -1, -1, 1},
{0x85, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x86, 2, OP_DESC, HW, "MOVH", 0, -1, -1, 1},
{0x87, 2, OP_DESC, BT, "MOVB", 0, -1, -1, 1},
{0x88, 2, OP_DESC, WD, "MCOMW", 0, -1, -1, 1},
{0x89, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x8a, 2, OP_DESC, HW, "MCOMH", 0, -1, -1, 1},
{0x8b, 2, OP_DESC, BT, "MCOMB", 0, -1, -1, 1},
{0x8c, 2, OP_DESC, WD, "MNEGW", 0, -1, -1, 1},
{0x8d, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x8e, 2, OP_DESC, HW, "MNEGH", 0, -1, -1, 1},
{0x8f, 2, OP_DESC, BT, "MNEGB", 0, -1, -1, 1},
{0x90, 1, OP_DESC, WD, "INCW", -1, -1, -1, 0},
{0x91, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x92, 1, OP_DESC, HW, "INCH", -1, -1, -1, 0},
{0x93, 1, OP_DESC, BT, "INCB", -1, -1, -1, 0},
{0x94, 1, OP_DESC, WD, "DECW", -1, -1, -1, 0},
{0x95, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x96, 1, OP_DESC, HW, "DECH", -1, -1, -1, 0},
{0x97, 1, OP_DESC, BT, "DECB", -1, -1, -1, 0},
{0x98, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x99, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x9a, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x9b, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x9c, 2, OP_DESC, WD, "ADDW2", 0, -1, -1, 1},
{0x9d, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0x9e, 2, OP_DESC, HW, "ADDH2", 0, -1, -1, 1},
{0x9f, 2, OP_DESC, BT, "ADDB2", 0, -1, -1, 1},
{0xa0, 1, OP_DESC, WD, "PUSHW", 0, -1, -1, -1},
{0xa1, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xa2, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xa3, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xa4, 2, OP_DESC, WD, "MODW2", 0, -1, -1, 1},
{0xa5, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xa6, 2, OP_DESC, HW, "MODH2", 0, -1, -1, 1},
{0xa7, 2, OP_DESC, BT, "MODB2", 0, -1, -1, 1},
{0xa8, 2, OP_DESC, WD, "MULW2", 0, -1, -1, 1},
{0xa9, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xaa, 2, OP_DESC, HW, "MULH2", 0, -1, -1, 1},
{0xab, 2, OP_DESC, BT, "MULB2", 0, -1, -1, 1},
{0xac, 2, OP_DESC, WD, "DIVW2", 0, -1, -1, 1},
{0xad, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xae, 2, OP_DESC, HW, "DIVH2", 0, -1, -1, 1},
{0xaf, 2, OP_DESC, BT, "DIVB2", 0, -1, -1, 1},
{0xb0, 2, OP_DESC, WD, "ORW2", 0, -1, -1, 1},
{0xb1, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xb2, 2, OP_DESC, HW, "ORH2", 0, -1, -1, 1},
{0xb3, 2, OP_DESC, BT, "ORB2", 0, -1, -1, 1},
{0xb4, 2, OP_DESC, WD, "XORW2", 0, -1, -1, 1},
{0xb5, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xb6, 2, OP_DESC, HW, "XORH2", 0, -1, -1, 1},
{0xb7, 2, OP_DESC, BT, "XORB2", 0, -1, -1, 1},
{0xb8, 2, OP_DESC, WD, "ANDW2", 0, -1, -1, 1},
{0xb9, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xba, 2, OP_DESC, HW, "ANDH2", 0, -1, -1, 1},
{0xbb, 2, OP_DESC, BT, "ANDB2", 0, -1, -1, 1},
{0xbc, 2, OP_DESC, WD, "SUBW2", 0, -1, -1, 1},
{0xbd, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xbe, 2, OP_DESC, HW, "SUBH2", 0, -1, -1, 1},
{0xbf, 2, OP_DESC, BT, "SUBB2", 0, -1, -1, 1},
{0xc0, 3, OP_DESC, WD, "ALSW3", 0, 1, -1, 2},
{0xc1, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xc2, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xc3, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xc4, 3, OP_DESC, WD, "ARSW3", 0, 1, -1, 2},
{0xc5, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xc6, 3, OP_DESC, HW, "ARSH3", 0, 1, -1, 2},
{0xc7, 3, OP_DESC, BT, "ARSB3", 0, 1, -1, 2},
{0xc8, 4, OP_DESC, WD, "INSFW", 0, 1, 2, 3},
{0xc9, -1, OP_DESC, NA, "???", -1, -1, -1, -1},
{0xca, 4, OP_DESC, HW, "INSFH", 0, 1, 2, 3},
{0xcb, 4, OP_DESC, BT, "INSFB", 0, 1, 2, 3},
{0xcc, 4, OP_DESC, WD, "EXTFW", 0, 1, 2, 3},
{0xcd, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xce, 4, OP_DESC, HW, "EXTFH", 0, 1, 2, 3},
{0xcf, 4, OP_DESC, BT, "EXTFB", 0, 1, 2, 3},
{0xd0, 3, OP_DESC, WD, "LLSW3", 0, 1, -1, 2},
{0xd1, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xd2, 3, OP_DESC, HW, "LLSH3", 0, 1, -1, 2},
{0xd3, 3, OP_DESC, BT, "LLSB3", 0, 1, -1, 2},
{0xd4, 3, OP_DESC, WD, "LRSW3", 0, 1, -1, 2},
{0xd5, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xd6, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xd7, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xd8, 3, OP_DESC, WD, "ROTW", 0, 1, -1, 2}, /* 3-108 238 */
{0xd9, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xda, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xdb, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xdc, 3, OP_DESC, WD, "ADDW3", 0, 1, -1, 2},
{0xdd, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xde, 3, OP_DESC, HW, "ADDH3", 0, 1, -1, 2},
{0xdf, 3, OP_DESC, BT, "ADDB3", 0, 1, -1, 2},
{0xe0, 1, OP_DESC, WD, "PUSHAW", 0, -1, -1, -1},
{0xe1, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xe2, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xe3, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xe4, 3, OP_DESC, WD, "MODW3", 0, 1, -1, 2},
{0xe5, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xe6, 3, OP_DESC, HW, "MODH3", 0, 1, -1, 2},
{0xe7, 3, OP_DESC, BT, "MODB3", 0, 1, -1, 2},
{0xe8, 3, OP_DESC, WD, "MULW3", 0, 1, -1, 2},
{0xe9, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xea, 3, OP_DESC, HW, "MULH3", 0, 1, -1, 2},
{0xeb, 3, OP_DESC, BT, "MULB3", 0, 1, -1, 2},
{0xec, 3, OP_DESC, WD, "DIVW3", 0, 1, -1, 2},
{0xed, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xee, 3, OP_DESC, HW, "DIVH3", 0, 1, -1, 2},
{0xef, 3, OP_DESC, BT, "DIVB3", 0, 1, -1, 2},
{0xf0, 3, OP_DESC, WD, "ORW3", 0, 1, -1, 2},
{0xf1, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xf2, 3, OP_DESC, HW, "ORH3", 0, 1, -1, 2},
{0xf3, 3, OP_DESC, BT, "ORB3", 0, 1, -1, 2},
{0xf4, 3, OP_DESC, WD, "XORW3", 0, 1, -1, 2},
{0xf5, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xf6, 3, OP_DESC, HW, "XORH3", 0, 1, -1, 2},
{0xf7, 3, OP_DESC, BT, "XORB3", 0, 1, -1, 2},
{0xf8, 3, OP_DESC, WD, "ANDW3", 0, 1, -1, 2},
{0xf9, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xfa, 3, OP_DESC, HW, "ANDH3", 0, 1, -1, 2},
{0xfb, 3, OP_DESC, BT, "ANDB3", 0, 1, -1, 2},
{0xfc, 3, OP_DESC, WD, "SUBW3", 0, 1, -1, 2},
{0xfd, -1, OP_NONE, NA, "???", -1, -1, -1, -1},
{0xfe, 3, OP_DESC, HW, "SUBH3", 0, 1, -1, 2},
{0xff, 3, OP_DESC, BT, "SUBB3", 0, 1, -1, 2}
};
/* from MAME (src/devices/cpu/m68000/m68kcpu.c) */
const uint8 shift_8_table[65] =
{
0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff
};
const uint16 shift_16_table[65] =
{
0x0000, 0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff, 0xffff,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff
};
const uint32 shift_32_table[65] =
{
0x00000000, 0x80000000, 0xc0000000, 0xe0000000, 0xf0000000, 0xf8000000,
0xfc000000, 0xfe000000, 0xff000000, 0xff800000, 0xffc00000, 0xffe00000,
0xfff00000, 0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000, 0xffff8000,
0xffffc000, 0xffffe000, 0xfffff000, 0xfffff800, 0xfffffc00, 0xfffffe00,
0xffffff00, 0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0, 0xfffffff8,
0xfffffffc, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
};
void cpu_load_rom()
{
uint32 i, index, sc, mask, val;
if (ROM == NULL) {
return;
}
for (i = 0; i < BOOT_CODE_SIZE; i++) {
val = BOOT_CODE_ARRAY[i];
sc = (~(i & 3) << 3) & 0x1f;
mask = 0xffu << sc;
index = i >> 2;
ROM[index] = (ROM[index] & ~mask) | (val << sc);
}
}
t_stat cpu_boot(int32 unit_num, DEVICE *dptr)
{
/*
* page 2-52 (pdf page 85)
*
* 1. Change to physical address mode
* 2. Fetch the word at physical address 0x80 and store it in
* the PCBP register.
* 3. Fetch the word at the PCB address and store it in the
* PSW.
* 4. Fetch the word at PCB address + 4 bytes and store it
* in the PC.
* 5. Fetch the word at PCB address + 8 bytes and store it
* in the SP.
* 6. Fetch the word at PCB address + 12 bytes and store it
* in the PCB, if bit I in PSW is set.
*/
mmu_disable();
R[NUM_PCBP] = pread_w(0x80);
R[NUM_PSW] = pread_w(R[NUM_PCBP]);
R[NUM_PC] = pread_w(R[NUM_PCBP] + 4);
R[NUM_SP] = pread_w(R[NUM_PCBP] + 8);
if (R[NUM_PSW] & PSW_I_MASK) {
R[NUM_PSW] &= ~PSW_I_MASK;
R[NUM_PCBP] += 12;
}
/* set ISC to External Reset */
R[NUM_PSW] &= ~PSW_ISC_MASK;
R[NUM_PSW] |= 3 << PSW_ISC ;
sim_debug(EXECUTE_MSG, &cpu_dev,
">>> CPU BOOT/RESET COMPLETE. PC=%08x SP=%08x\n",
R[NUM_PC], R[NUM_SP]);
return SCPE_OK;
}
t_stat cpu_ex(t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
{
uint32 uaddr = (uint32) addr;
uint8 value;
t_stat succ;
if (vptr == NULL) {
return SCPE_ARG;
}
if (sw & EX_V_FLAG) {
succ = examine(uaddr, &value);
*vptr = value;
return succ;
} else {
if (!(addr_is_rom(uaddr) || addr_is_mem(uaddr) || addr_is_io(uaddr))) {
*vptr = 0;
return SCPE_NXM;
}
*vptr = (uint32) pread_b(uaddr);
return SCPE_OK;
}
}
t_stat cpu_dep(t_value val, t_addr addr, UNIT *uptr, int32 sw)
{
uint32 uaddr = (uint32) addr;
if (sw & EX_V_FLAG) {
return deposit(uaddr, (uint8) val);
} else {
if (!(addr_is_rom(uaddr) || addr_is_mem(uaddr) || addr_is_io(uaddr))) {
return SCPE_NXM;
}
pwrite_b(uaddr, (uint8) val);
return SCPE_OK;
}
}
t_stat cpu_reset(DEVICE *dptr)
{
int i;
if (!sim_is_running) {
/* Clear registers */
for (i = 0; i < 16; i++) {
R[i] = 0;
}
/* Allocate memory */
if (ROM == NULL) {
ROM = (uint32 *) calloc(BOOT_CODE_SIZE >> 2, sizeof(uint32));
if (ROM == NULL) {
return SCPE_MEM;
}
memset(ROM, 0, BOOT_CODE_SIZE >> 2);
}
if (RAM == NULL) {
RAM = (uint32 *) calloc((size_t)(MEM_SIZE >> 2), sizeof(uint32));
if (RAM == NULL) {
return SCPE_MEM;
}
memset(RAM, 0, (size_t)(MEM_SIZE >> 2));
sim_vm_is_subroutine_call = cpu_is_pc_a_subroutine_call;
}
cpu_load_rom();
}
abort_context = C_NONE;
cpu_nmi = FALSE;
cpu_hist_p = 0;
cpu_in_wait = FALSE;
sim_brk_types = SWMASK('E');
sim_brk_dflt = SWMASK('E');
return SCPE_OK;
}
static const char *cpu_next_caveats =
"The NEXT command in this 3B2 architecture simulator currently will\n"
"enable stepping across subroutine calls which are initiated by the\n"
"JSB, CALL and CALLPS instructions.\n"
"This stepping works by dynamically establishing breakpoints at the\n"
"memory address immediately following the instruction which initiated\n"
"the subroutine call. These dynamic breakpoints are automatically\n"
"removed once the simulator returns to the sim> prompt for any reason.\n"
"If the called routine returns somewhere other than one of these\n"
"locations due to a trap, stack unwind or any other reason, instruction\n"
"execution will continue until some other reason causes execution to stop.\n";
t_bool cpu_is_pc_a_subroutine_call (t_addr **ret_addrs)
{
static t_addr returns[MAX_SUB_RETURN_SKIP+1] = {0};
static t_bool caveats_displayed = FALSE;
int i;
if (!caveats_displayed) {
caveats_displayed = TRUE;
sim_printf ("%s", cpu_next_caveats);
}
/* get data */
if (SCPE_OK != get_aval (R[NUM_PC], &cpu_dev, &cpu_unit)) {
return FALSE;
}
switch (sim_eval[0]) {
case JSB:
case CALL:
case CALLPS:
returns[0] = R[NUM_PC] + (unsigned int) (1 - fprint_sym(stdnul, R[NUM_PC],
sim_eval, &cpu_unit,
SWMASK ('M')));
for (i=1; i<MAX_SUB_RETURN_SKIP; i++) {
/* Possible skip return */
returns[i] = returns[i-1] + 1;
}
returns[i] = 0; /* Make sure the address list ends with a zero */
*ret_addrs = returns;
return TRUE;
default:
return FALSE;
}
}
t_stat cpu_set_hist(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
uint32 i, size;
t_stat result;
if (cptr == NULL) {
/* Disable the feature */
if (INST != NULL) {
for (i = 0; i < cpu_hist_size; i++) {
INST[i].valid = FALSE;
}
}
cpu_hist_size = 0;
cpu_hist_p = 0;
return SCPE_OK;
}
size = (uint32) get_uint(cptr, 10, MAX_HIST_SIZE, &result);
if ((result != SCPE_OK) || (size < MIN_HIST_SIZE)) {
return SCPE_ARG;
}
cpu_hist_p = 0;
if (size > 0) {
if (INST != NULL) {
free(INST);
}
INST = (instr *)calloc(size, sizeof(instr));
if (INST == NULL) {
return SCPE_MEM;
}
memset(INST, 0, sizeof(instr) * size);
cpu_hist_size = size;
}
return SCPE_OK;
}
void fprint_sym_m(FILE *st, instr *ip)
{
int32 i;
if (ip == NULL || ip->mn == NULL) {
fprintf(st, "???");
return;
}
fprintf(st, "%s", ip->mn->mnemonic);
if (ip->mn->op_count > 0) {
fputc(' ', st);
}
/* Show the operand mnemonics */
for (i = 0; i < ip->mn->op_count; i++) {
cpu_show_operand(st, &ip->operands[i]);
if (i < ip->mn->op_count - 1) {
fputc(',', st);
}
}
}
t_stat cpu_show_hist(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
uint32 i;
size_t j, count;
char *cptr = (char *) desc;
t_stat result;
instr *ip;
int32 di;
if (cpu_hist_size == 0) {
return SCPE_NOFNC;
}
/* 'count' is the number of history entries the user wants */
if (cptr) {
count = (size_t) get_uint(cptr, 10, cpu_hist_size, &result);
if ((result != SCPE_OK) || (count == 0)) {
return SCPE_ARG;
}
} else {
count = cpu_hist_size;
}
/* Position for reading from ring buffer */
di = (int32) (cpu_hist_p - count);
if (di < 0) {
di = di + (int32) cpu_hist_size;
}
fprintf(st, "PSW SP PC IR\n");
for (i = 0; i < count; i++) {
ip = &INST[(di++) % (int32) cpu_hist_size];
if (ip->valid) {
/* Show the opcode mnemonic */
fprintf(st, "%08x %08x %08x ", ip->psw, ip->sp, ip->pc);
/* Show the operand data */
if (ip->mn == NULL || ip->mn->op_count < 0) {
fprintf(st, "???");
} else {
fprint_sym_m(st, ip);
if (ip->mn->op_count > 0 && ip->mn->mode == OP_DESC) {
fprintf(st, "\n ");
for (j = 0; j < (uint32) ip->mn->op_count; j++) {
fprintf(st, "%08x", ip->operands[j].data);
if (j < (uint32) ip->mn->op_count - 1) {
fputc(' ', st);
}
}
}
}
fputc('\n', st);
}
}
return SCPE_OK;
}
void cpu_register_name(uint8 reg, char *buf, size_t len) {
switch(reg) {
case 9:
snprintf(buf, len, "%%fp");
break;
case 10:
snprintf(buf, len, "%%ap");
break;
case 11:
snprintf(buf, len, "%%psw");
break;
case 12:
snprintf(buf, len, "%%sp");
break;
case 13:
snprintf(buf, len, "%%pcbp");
break;
case 14:
snprintf(buf, len, "%%isp");
break;
case 15:
snprintf(buf, len, "%%pc");
break;
default:
snprintf(buf, len, "%%r%d", reg);
break;
}
}
void cpu_show_operand(FILE *st, operand *op)
{
char reg_name[8];
if (op->etype != -1) {
switch(op->etype) {
case 0:
fprintf(st, "{uword}");
break;
case 2:
fprintf(st, "{uhalf}");
break;
case 3:
fprintf(st, "{ubyte}");
break;
case 4:
fprintf(st, "{word}");
break;
case 6:
fprintf(st, "{half}");
break;
case 7:
fprintf(st, "{sbyte}");
break;
}
}
switch(op->mode) {
case 0:
case 1:
case 2:
case 3:
fprintf(st, "&0x%x", op->embedded.b);
break;
case 4:
if (op->reg == 15) {
fprintf(st, "&0x%x", op->embedded.w);
} else {
cpu_register_name(op->reg, reg_name, 8);
fprintf(st, "%s", reg_name);
}
break;
case 5:
if (op->reg == 15) {
fprintf(st, "&0x%x", op->embedded.w);
} else {
cpu_register_name(op->reg, reg_name, 8);
fprintf(st, "(%s)", reg_name);
}
break;
case 6: /* FP Short Offset */
if (op->reg == 15) {
fprintf(st, "&0x%x", op->embedded.w);
} else {
fprintf(st, "%d(%%fp)", op->reg);
}
break;
case 7: /* AP Short Offset */
if (op->reg == 15) {
fprintf(st, "$0x%x", op->embedded.w);
} else {
fprintf(st, "%d(%%ap)", op->embedded.w);
}
break;
case 8:
cpu_register_name(op->reg, reg_name, 8);
fprintf(st, "0x%x(%s)", (int32)op->embedded.w, reg_name);
break;
case 9:
cpu_register_name(op->reg, reg_name, 8);
fprintf(st, "*0x%x(%s)", (int32)op->embedded.w, reg_name);
break;
case 10:
cpu_register_name(op->reg, reg_name, 8);
fprintf(st, "0x%x(%s)", (int16)op->embedded.w, reg_name);
break;
case 11:
cpu_register_name(op->reg, reg_name, 8);
fprintf(st, "*0x%x(%s)", (int16)op->embedded.w, reg_name);
break;
case 12:
cpu_register_name(op->reg, reg_name, 8);
fprintf(st, "%d(%s)", (int8)op->embedded.w, reg_name);
break;
case 13:
cpu_register_name(op->reg, reg_name, 8);
fprintf(st, "*%d(%s)", (int8)op->embedded.w, reg_name);
break;
case 14:
if (op->reg == 15) {
fprintf(st, "*$0x%x", op->embedded.w);
}
break;
case 15:
fprintf(st, "&%d", (int32)op->embedded.w);
break;
}
}
t_stat cpu_set_size(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
uint32 *nRAM = NULL;
uint32 uval = (uint32) val;
if ((val <= 0) || (val > MAXMEMSIZE)) {
return SCPE_ARG;
}
/* Do (re-)allocation for memory. */
nRAM = (uint32 *) calloc(uval >> 2, sizeof(uint32));
if (nRAM == NULL) {
return SCPE_MEM;
}
free(RAM);
RAM = nRAM;
MEM_SIZE = uval;
memset(RAM, 0, (size_t)(MEM_SIZE >> 2));
return SCPE_OK;
}
static SIM_INLINE void clear_instruction(instr *inst)
{
uint8 i;
inst->mn = NULL;
inst->psw = 0;
inst->sp = 0;
inst->pc = 0;
for (i = 0; i < 4; i++) {
inst->operands[i].mode = 0;
inst->operands[i].reg = 0;
inst->operands[i].dtype = -1;
inst->operands[i].etype = -1;
inst->operands[i].embedded.w = 0;
inst->operands[i].data = 0;
}
}
/*
* Decode a single descriptor-defined operand from the instruction
* stream. Returns the number of bytes consumed during decode.
*/
static uint8 decode_operand(uint32 pa, instr *instr, uint8 op_number)
{
uint8 desc;
uint8 offset = 0;
operand *oper = &instr->operands[op_number];
/* Set the default data type if none is already set */
if (cpu_dtype == -1) {
cpu_dtype = instr->mn->dtype;
}
/* Read in the descriptor byte */
desc = read_b(pa + offset++, ACC_OF);
oper->mode = (desc >> 4) & 0xf;
oper->reg = desc & 0xf;
oper->dtype = instr->mn->dtype;
oper->etype = cpu_etype;
switch (oper->mode) {
case 0: /* Positive Literal */
case 1: /* Positive Literal */
case 2: /* Positive Literal */
case 3: /* Positive Literal */
case 15: /* Negative literal */
oper->embedded.b = (uint8)desc;
oper->data = oper->embedded.b;
break;
case 4: /* Word Immediate, Register Mode */
switch (oper->reg) {
case 15: /* Word Immediate */
oper->embedded.w = (uint32) read_b(pa + offset++, ACC_OF);
oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 8u;
oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 16u;
oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 24u;
oper->data = oper->embedded.w;
break;
default: /* Register mode */
oper->data = R[oper->reg];
break;
}
break;
case 5: /* Halfword Immediate, Register Deferred Mode */
switch (oper->reg) {
case 15: /* Halfword Immediate */
oper->embedded.h = (uint16) read_b(pa + offset++, ACC_OF);
oper->embedded.h |= ((uint16) read_b(pa + offset++, ACC_OF)) << 8u;
oper->data = oper->embedded.h;
break;
case 11: /* INVALID */
cpu_abort(NORMAL_EXCEPTION, INVALID_DESCRIPTOR);
return offset;
default: /* Register deferred mode */
oper->data = R[oper->reg];
break;
}
break;
case 6: /* Byte Immediate, FP Short Offset */
switch (oper->reg) {
case 15: /* Byte Immediate */
oper->embedded.b = read_b(pa + offset++, ACC_OF);
oper->data = oper->embedded.b;
break;
default: /* FP Short Offset */
oper->embedded.b = oper->reg;
oper->data = oper->embedded.b;
break;
}
break;
case 7: /* Absolute, AP Short Offset */
switch (oper->reg) {
case 15: /* Absolute */
oper->embedded.w = (uint32) read_b(pa + offset++, ACC_OF);
oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 8u;
oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 16u;
oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 24u;
oper->data = oper->embedded.w;
break;
default: /* AP Short Offset */
oper->embedded.b = oper->reg;
oper->data = oper->embedded.b;
break;
}
break;
case 8: /* Word Displacement */
case 9: /* Word Displacement Deferred */
oper->embedded.w = (uint32) read_b(pa + offset++, ACC_OF);
oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 8u;
oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 16u;
oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 24u;
oper->data = oper->embedded.w;
break;
case 10: /* Halfword Displacement */
case 11: /* Halfword Displacement Deferred */
oper->embedded.h = read_b(pa + offset++, ACC_OF);
oper->embedded.h |= ((uint16) read_b(pa + offset++, ACC_OF)) << 8u;
oper->data = oper->embedded.h;
break;
case 12: /* Byte Displacement */
case 13: /* Byte Displacement Deferred */
oper->embedded.b = read_b(pa + offset++, ACC_OF);
oper->data = oper->embedded.b;
break;
case 14: /* Absolute Deferred, Extended-Operand */
switch (oper->reg) {
case 15: /* Absolute Deferred */
oper->embedded.w = (uint32) read_b(pa + offset++, ACC_OF);
oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 8u;
oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 16u;
oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 24u;
break;
case 0:
case 2:
case 3:
case 4:
case 6:
case 7: /* Expanded Datatype */
/* Recursively decode the remainder of the operand after
storing the expanded datatype */
cpu_etype = (int8) oper->reg;
oper->etype = cpu_etype;
offset += decode_operand(pa + offset, instr, op_number);
break;
default:
cpu_abort(NORMAL_EXCEPTION, RESERVED_DATATYPE);
break;
}
break;
default:
cpu_abort(NORMAL_EXCEPTION, INVALID_DESCRIPTOR);
}
return offset;
}
/*
* Decode the instruction currently being pointed at by the PC.
* This routine does the following:
* 1. Read the opcode.
* 2. Determine the number of operands to decode based on
* the opcode type.
* 3. Fetch each opcode from main memory.
*
* This routine may alter the PSW's ET (Exception Type) and
* ISC (Internal State Code) registers if an exceptional condition
* is encountered during decode.
*/
uint8 decode_instruction(instr *instr)
{
uint8 offset = 0;
uint8 b1, b2;
uint16 hword_op;
uint32 pa;
mnemonic *mn = NULL;
int i;
pa = R[NUM_PC];
/* Store off the PC and and PSW for history keeping */
instr->psw = R[NUM_PSW];
instr->sp = R[NUM_SP];
instr->pc = pa;
/* Reset our data types */
cpu_etype = -1;
cpu_dtype = -1;
if (read_operand(pa + offset++, &b1) != SCPE_OK) {
/* We tried to read out of a page that doesn't exist. We
need to let the operating system handle it.*/
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
return offset;
}
/* It should never, ever happen that operand fetch
would cause a page fault. */
if (b1 == 0x30) {
read_operand(pa + offset++, &b2);
hword_op = (uint16) ((uint16)b1 << 8) | (uint16) b2;
for (i = 0; i < HWORD_OP_COUNT; i++) {
if (hword_ops[i].opcode == hword_op) {
mn = &hword_ops[i];
break;
}
}
} else {
mn = &ops[b1];
}
if (mn == NULL) {
cpu_abort(NORMAL_EXCEPTION, ILLEGAL_OPCODE);
return offset;
}
instr->mn = mn;
if (mn->op_count < 0) {
cpu_abort(NORMAL_EXCEPTION, ILLEGAL_OPCODE);
return offset;
}
if (mn->op_count == 0) {
/* Nothing else to do, we're done decoding. */
return offset;
}
switch (mn->mode) {
case OP_NONE:
break;
case OP_BYTE:
instr->operands[0].embedded.b = read_b(pa + offset++, ACC_OF);
instr->operands[0].mode = 6;
instr->operands[0].reg = 15;
break;
case OP_HALF:
instr->operands[0].embedded.h = read_b(pa + offset++, ACC_OF);
instr->operands[0].embedded.h |= read_b(pa + offset++, ACC_OF) << 8;
instr->operands[0].mode = 5;
instr->operands[0].reg = 15;
break;
case OP_COPR:
instr->operands[0].embedded.w = (uint32) read_b(pa + offset++, ACC_OF);
instr->operands[0].embedded.w |= (uint32) read_b(pa + offset++, ACC_OF) << 8;
instr->operands[0].embedded.w |= (uint32) read_b(pa + offset++, ACC_OF) << 16;
instr->operands[0].embedded.w |= (uint32) read_b(pa + offset++, ACC_OF) << 24;
instr->operands[0].mode = 4;
instr->operands[0].reg = 15;
/* Decode subsequent operands */
for (i = 1; i < mn->op_count; i++) {
offset += decode_operand(pa + offset, instr, (uint8) i);
}
break;
case OP_DESC:
for (i = 0; i < mn->op_count; i++) {
offset += decode_operand(pa + offset, instr, (uint8) i);
}
break;
}
return offset;
}
static SIM_INLINE void cpu_context_switch_3(uint32 new_pcbp)
{
if (R[NUM_PSW] & PSW_R_MASK) {
R[0] = R[NUM_PCBP] + 64;
R[2] = read_w(R[0], ACC_AF);
R[0] += 4;
while (R[2] != 0) {
R[1] = read_w(R[0], ACC_AF);
R[0] += 4;
/* Execute MOVBLW instruction inside this loop */
while (R[2] != 0) {
write_w(R[1], read_w(R[0], ACC_AF));
R[2]--;
R[0] += 4;
R[1] += 4;
}
R[2] = read_w(R[0], ACC_AF);
R[0] += 4;
}
R[0] = R[0] + 4;
}
}
static SIM_INLINE void cpu_context_switch_2(uint32 new_pcbp)
{
R[NUM_PCBP] = new_pcbp;
/* Put new PSW, PC and SP values from PCB into registers */
R[NUM_PSW] = read_w(R[NUM_PCBP], ACC_AF);
R[NUM_PSW] &= ~PSW_TM_MASK; /* Clear TM */
R[NUM_PC] = read_w(R[NUM_PCBP] + 4, ACC_AF);
R[NUM_SP] = read_w(R[NUM_PCBP] + 8, ACC_AF);
/* If i-bit is set, increment PCBP past initial context area */
if (R[NUM_PSW] & PSW_I_MASK) {
R[NUM_PSW] &= ~PSW_I_MASK;
R[NUM_PCBP] += 12;
}
}
static SIM_INLINE void cpu_context_switch_1(uint32 new_pcbp)
{
/* Save the current PC in PCB */
write_w(R[NUM_PCBP] + 4, R[NUM_PC]);
/* Copy the 'R' flag from the new PSW to the old PSW */
R[NUM_PSW] &= ~PSW_R_MASK;
R[NUM_PSW] |= (read_w(new_pcbp, ACC_AF) & PSW_R_MASK);
/* Save current PSW and SP in PCB */
write_w(R[NUM_PCBP], R[NUM_PSW]);
write_w(R[NUM_PCBP] + 8, R[NUM_SP]);
/* If R is set, save current R0-R8/FP/AP in PCB */
if (R[NUM_PSW] & PSW_R_MASK) {
write_w(R[NUM_PCBP] + 24, R[NUM_FP]);
write_w(R[NUM_PCBP] + 28, R[0]);
write_w(R[NUM_PCBP] + 32, R[1]);
write_w(R[NUM_PCBP] + 36, R[2]);
write_w(R[NUM_PCBP] + 40, R[3]);
write_w(R[NUM_PCBP] + 44, R[4]);
write_w(R[NUM_PCBP] + 48, R[5]);
write_w(R[NUM_PCBP] + 52, R[6]);
write_w(R[NUM_PCBP] + 56, R[7]);
write_w(R[NUM_PCBP] + 60, R[8]);
write_w(R[NUM_PCBP] + 20, R[NUM_AP]);
R[NUM_FP] = R[NUM_PCBP] + 52;
}
}
t_bool cpu_on_interrupt(uint8 ipl)
{
uint32 new_pcbp;
uint16 id = ipl; /* TODO: Does this need to be uint16? */
sim_debug(IRQ_MSG, &cpu_dev,
"[%08x] [cpu_on_interrupt] ipl=%d\n",
R[NUM_PC], ipl);
/*
* "If a nonmaskable interrupt request is received, an auto-vector
* interrupt acknowledge cycle is performed (as if an autovector
* interrupt at level 0 was being acknowledged) and no
* Interrupt-ID is fetched. The value 0 is used as the ID."
*/
if (cpu_nmi) {
id = 0;
}
cpu_km = TRUE;
if ((R[NUM_PSW] & PSW_QIE_MASK) >> PSW_QIE) {
/* TODO: Maybe implement quick interrupts at some point, but
the 3B2 ROM and SVR3 don't appear to use them. */
assert(0);
}
new_pcbp = read_w(0x8c + (4 * id), ACC_AF);
/* Save the old PCBP */
irq_push_word(R[NUM_PCBP]);
/* Set ISC, TM, and ET to 0, 0, 1 before saving */
R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK);
R[NUM_PSW] |= (1 << PSW_ET);
/* Context switch */
cpu_context_switch_1(new_pcbp);
cpu_context_switch_2(new_pcbp);
/* Set ISC, TM, and ET to 7, 0, 3 in new PSW */
R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK);
R[NUM_PSW] |= (7 << PSW_ISC);
R[NUM_PSW] |= (3 << PSW_ET);
cpu_context_switch_3(new_pcbp);
cpu_km = FALSE;
return TRUE;
}
t_stat sim_instr(void)
{
uint8 et, isc;
/* Temporary register used for overflow detection */
t_uint64 result;
/* Scratch space */
uint32 a, b, c, d;
/* Used for field calculation */
uint32 width, offset;
uint32 mask;
operand *src1, *src2, *src3, *dst;
stop_reason = 0;
abort_reason = (uint32) setjmp(save_env);
/* Exception handler.
*
* This gets a little messy because of exception contexts. If a
* normal-exception happens while we're handling a
* normal-exception, it needs to be treated as a stack-exception.
*/
if (abort_reason != 0) {
if (cpu_exception_stack_depth++ >= 10) {
return STOP_ESTK;
}
if (cpu_unit.flags & UNIT_EXHALT) {
return STOP_EX;
}
if (abort_reason == ABORT_EXC) {
et = R[NUM_PSW] & PSW_ET_MASK;
isc = (R[NUM_PSW] & PSW_ISC_MASK) >> PSW_ISC;
switch(abort_context) {
case C_NORMAL_GATE_VECTOR:
cpu_on_normal_exception(N_GATE_VECTOR);
break;
case C_PROCESS_GATE_PCB:
cpu_on_process_exception(GATE_PCB_FAULT);
break;
case C_PROCESS_OLD_PCB:
cpu_on_process_exception(OLD_PCB_FAULT);
break;
case C_PROCESS_NEW_PCB:
cpu_on_process_exception(NEW_PCB_FAULT);
break;
case C_STACK_FAULT:
cpu_on_stack_exception(STACK_FAULT);
break;
case C_RESET_GATE_VECTOR:
cpu_on_reset_exception(GATE_VECTOR_FAULT);
break;
case C_RESET_SYSTEM_DATA:
cpu_on_reset_exception(SYSTEM_DATA_FAULT);
break;
case C_RESET_INT_STACK:
cpu_on_reset_exception(INTERRUPT_STACK_FAULT);
break;
default:
switch(et) {
case NORMAL_EXCEPTION:
cpu_on_normal_exception(isc);
break;
case STACK_EXCEPTION:
cpu_on_stack_exception(isc);
break;
case RESET_EXCEPTION:
cpu_on_reset_exception(isc);
break;
default:
stop_reason = STOP_EX;
break;
}
break;
}
} else {
/* TODO: Handle traps */
stop_reason = STOP_EX;
}
}
while (stop_reason == 0) {
abort_context = C_NONE;
if (sim_brk_summ && sim_brk_test(R[NUM_PC], SWMASK ('E'))) {
stop_reason = STOP_IBKPT;
break;
}
if (cpu_exception_stack_depth > 0) {
cpu_exception_stack_depth--;
}
if (sim_interval-- <= 0) {
if ((stop_reason = sim_process_event())) {
break;
}
}
/* Process DMA requests */
dmac_service_drqs();
/*
* Post-increment IU mode pointers (if needed).
*
* This is essentially a colossal hack. We never want to
* increment these pointers during an interlocked Read/Write
* operation, so we only increment after a CPU step has
* occured.
*/
if (iu_increment_a) {
increment_modep_a();
}
if (iu_increment_b) {
increment_modep_b();
}
/* Process pending IRQ, if applicable */
if (PSW_CUR_IPL < cpu_ipl()) {
cpu_on_interrupt(cpu_ipl());
cpu_nmi = FALSE;
cpu_in_wait = FALSE;
continue;
}
if (cpu_in_wait) {
if (sim_idle_enab) {
sim_idle(TMR_CLK, TRUE);
}
continue;
}
/* Reset the TM bits */
R[NUM_PSW] &= ~PSW_TM;
R[NUM_PSW] |= PSW_TM_MASK;
/* Record the instruction for history */
if (cpu_hist_size > 0) {
cpu_instr = &INST[cpu_hist_p];
cpu_hist_p = (cpu_hist_p + 1) % cpu_hist_size;
} else {
cpu_instr = &inst;
}
/* Decode the instruction */
memset(cpu_instr, 0, sizeof(instr));
cpu_ilen = decode_instruction(cpu_instr);
/* Make sure to update the valid bit for history keeping (if
* enabled) */
cpu_instr->valid = TRUE;
/*
* Operate on the decoded instruction.
*/
/* Get the operands */
if (cpu_instr->mn->src_op1 >= 0) {
src1 = &cpu_instr->operands[cpu_instr->mn->src_op1];
}
if (cpu_instr->mn->src_op2 >= 0) {
src2 = &cpu_instr->operands[cpu_instr->mn->src_op2];
}
if (cpu_instr->mn->src_op3 >= 0) {
src3 = &cpu_instr->operands[cpu_instr->mn->src_op3];
}
if (cpu_instr->mn->dst_op >= 0) {
dst = &cpu_instr->operands[cpu_instr->mn->dst_op];
}
switch (cpu_instr->mn->opcode) {
case ADDW2:
case ADDH2:
case ADDB2:
a = cpu_read_op(src1);
b = cpu_read_op(dst);
add(a, b, dst);
break;
case ADDW3:
case ADDH3:
case ADDB3:
a = cpu_read_op(src1);
b = cpu_read_op(src2);
add(a, b, dst);
break;
case ALSW3:
a = cpu_read_op(src2);
b = cpu_read_op(src1);
result = (t_uint64)a << (b & 0x1f);
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(result, dst);
break;
case ANDW2:
case ANDH2:
case ANDB2:
a = cpu_read_op(src1);
b = cpu_read_op(dst);
c = a & b;
cpu_write_op(dst, c);
cpu_set_nz_flags(c, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(c, dst);
break;
case ANDW3:
case ANDH3:
case ANDB3:
a = cpu_read_op(src1);
b = cpu_read_op(src2);
c = a & b;
cpu_write_op(dst, c);
cpu_set_nz_flags(c, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(c, dst);
break;
case BEH:
case BEH_D:
if (cpu_z_flag() == 1) {
R[NUM_PC] += sign_extend_h(dst->embedded.h);
continue;
}
break;
case BEB:
case BEB_D:
if (cpu_z_flag() == 1) {
R[NUM_PC] += sign_extend_b(dst->embedded.b);
continue;
}
break;
case BGH:
if ((cpu_n_flag() | cpu_z_flag()) == 0) {
R[NUM_PC] += sign_extend_h(dst->embedded.h);
continue;
}
break;
case BGB:
if ((cpu_n_flag() | cpu_z_flag()) == 0) {
R[NUM_PC] += sign_extend_b(dst->embedded.b);
continue;
}
break;
case BGEH:
if ((cpu_n_flag() == 0) | (cpu_z_flag() == 1)) {
R[NUM_PC] += sign_extend_h(dst->embedded.h);
continue;
}
break;
case BGEB:
if ((cpu_n_flag() == 0) | (cpu_z_flag() == 1)) {
R[NUM_PC] += sign_extend_b(dst->embedded.b);
continue;
}
break;
case BGEUH:
if (cpu_c_flag() == 0) {
R[NUM_PC] += sign_extend_h(dst->embedded.h);
continue;
}
break;
case BGEUB:
if (cpu_c_flag() == 0) {
R[NUM_PC] += sign_extend_b(dst->embedded.b);
continue;
}
break;
case BGUH:
if ((cpu_c_flag() | cpu_z_flag()) == 0) {
R[NUM_PC] += sign_extend_h(dst->embedded.h);
continue;
}
break;
case BGUB:
if ((cpu_c_flag() | cpu_z_flag()) == 0) {
R[NUM_PC] += sign_extend_b(dst->embedded.b);
continue;
}
break;
case BITW:
case BITH:
case BITB:
a = cpu_read_op(src1);
b = cpu_read_op(src2);
c = a & b;
cpu_set_nz_flags(c, src1);
cpu_set_c_flag(0);
cpu_set_v_flag(0);
break;
case BLH:
if ((cpu_n_flag() == 1) && (cpu_z_flag() == 0)) {
R[NUM_PC] += sign_extend_h(dst->embedded.h);
continue;
}
break;
case BLB:
if ((cpu_n_flag() == 1) && (cpu_z_flag() == 0)) {
R[NUM_PC] += sign_extend_b(dst->embedded.b);
continue;
}
break;
case BLEH:
if ((cpu_n_flag() | cpu_z_flag()) == 1) {
R[NUM_PC] += sign_extend_h(dst->embedded.h);
continue;
}
break;
case BLEB:
if ((cpu_n_flag() | cpu_z_flag()) == 1) {
R[NUM_PC] += sign_extend_b(dst->embedded.b);
continue;
}
break;
case BLEUH:
if ((cpu_c_flag() | cpu_z_flag()) == 1) {
R[NUM_PC] += sign_extend_h(dst->embedded.h);
continue;
}
break;
case BLEUB:
if ((cpu_c_flag() | cpu_z_flag()) == 1) {
R[NUM_PC] += sign_extend_b(dst->embedded.b);
continue;
}
break;
case BLUH:
if (cpu_c_flag() == 1) {
R[NUM_PC] += sign_extend_h(dst->embedded.h);
continue;
}
break;
case BLUB:
if (cpu_c_flag() == 1) {
R[NUM_PC] += sign_extend_b(dst->embedded.b);
continue;
}
break;
case BNEH:
case BNEH_D:
if (cpu_z_flag() == 0) {
R[NUM_PC] += sign_extend_h(dst->embedded.h);
continue;
}
break;
case BNEB:
case BNEB_D:
if (cpu_z_flag() == 0) {
R[NUM_PC] += sign_extend_b(dst->embedded.b);
continue;
}
break;
case BPT:
/* TODO: Confirm that a breakpoint trap will increment the
PC. Otherwise, change 'break' to 'continue' */
cpu_abort(NORMAL_EXCEPTION, BREAKPOINT_TRAP);
break;
case BRH:
R[NUM_PC] += sign_extend_h(dst->embedded.h);
continue;
case BRB:
R[NUM_PC] += sign_extend_b(dst->embedded.b);
continue;
case BSBH:
cpu_push_word(R[NUM_PC] + cpu_ilen);
R[NUM_PC] += sign_extend_h(dst->embedded.h);
continue;
case BSBB:
cpu_push_word(R[NUM_PC] + cpu_ilen);
R[NUM_PC] += sign_extend_b(dst->embedded.b);
continue;
case BVCH:
if (cpu_v_flag() == 0) {
R[NUM_PC] += sign_extend_h(dst->embedded.h);
continue;
}
break;
case BVCB:
if (cpu_v_flag() == 0) {
R[NUM_PC] += sign_extend_b(dst->embedded.b);
continue;
}
break;
case BVSH:
if (cpu_v_flag() == 1) {
R[NUM_PC] += sign_extend_h(dst->embedded.h);
continue;
}
break;
case BVSB:
if (cpu_v_flag() == 1) {
R[NUM_PC] += sign_extend_b(dst->embedded.b);
continue;
}
break;
case CALL:
a = cpu_effective_address(src1);
b = cpu_effective_address(dst);
write_w(R[NUM_SP] + 4, R[NUM_AP]);
write_w(R[NUM_SP], R[NUM_PC] + cpu_ilen);
R[NUM_SP] += 8;
R[NUM_PC] = b;
R[NUM_AP] = a;
continue;
case CFLUSH:
break;
case CALLPS:
if (cpu_execution_level() != EX_LVL_KERN) {
cpu_abort(NORMAL_EXCEPTION, PRIVILEGED_OPCODE);
break;
}
a = R[0];
cpu_km = TRUE;
abort_context = C_RESET_INT_STACK;
irq_push_word(R[NUM_PCBP]);
/* Set current PC to start of next instruction (always PC+2) */
R[NUM_PC] += 2;
/* Set old PSW ISC, TM, and ET to 0, 0, 1 */
R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK);
R[NUM_PSW] |= (1 << PSW_ET);
cpu_context_switch_1(a);
cpu_context_switch_2(a);
R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK);
R[NUM_PSW] |= (7 << PSW_ISC);
R[NUM_PSW] |= (3 << PSW_ET);
cpu_context_switch_3(a);
abort_context = C_NONE;
cpu_km = FALSE;
continue;
case CLRW:
case CLRH:
case CLRB:
cpu_write_op(dst, 0);
cpu_set_n_flag(0);
cpu_set_z_flag(1);
cpu_set_c_flag(0);
cpu_set_v_flag(0);
break;
case CMPW:
a = cpu_read_op(src1);
b = cpu_read_op(src2);
cpu_set_z_flag((uint32)b == (uint32)a);
cpu_set_n_flag((int32)b < (int32)a);
cpu_set_c_flag((uint32)b < (uint32)a);
cpu_set_v_flag(0);
break;
case CMPH:
a = cpu_read_op(src1);
b = cpu_read_op(src2);
cpu_set_z_flag((uint16)b == (uint16)a);
cpu_set_n_flag((int16)b < (int16)a);
cpu_set_c_flag((uint16)b < (uint16)a);
cpu_set_v_flag(0);
break;
case CMPB:
a = cpu_read_op(src1);
b = cpu_read_op(src2);
cpu_set_z_flag((uint8)b == (uint8)a);
cpu_set_n_flag((int8)b < (int8)a);
cpu_set_c_flag((uint8)b < (uint8)a);
cpu_set_v_flag(0);
break;
case DECW:
case DECH:
case DECB:
a = cpu_read_op(dst);
sub(a, 1, dst);
break;
case DIVW2:
a = cpu_read_op(src1);
b = cpu_read_op(dst);
if (a == 0) {
cpu_abort(NORMAL_EXCEPTION, INTEGER_ZERO_DIVIDE);
break;
}
if (a == WORD_MASK && b == WD_MSB) {
cpu_set_v_flag(1);
}
DIV(a, b, src1, dst, int32);
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_c_flag(0);
break;
case DIVH2:
a = cpu_read_op(src1);
b = cpu_read_op(dst);
if (a == 0) {
cpu_abort(NORMAL_EXCEPTION, INTEGER_ZERO_DIVIDE);
break;
}
if (a == HALF_MASK && b == HW_MSB) {
cpu_set_v_flag(1);
}
DIV(a, b, src1, dst, int16);
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_c_flag(0);
break;
case DIVB2:
a = cpu_read_op(src1);
b = cpu_read_op(dst);
if (a == 0) {
cpu_abort(NORMAL_EXCEPTION, INTEGER_ZERO_DIVIDE);
break;
}
if (a == BYTE_MASK && b == BT_MSB) {
cpu_set_v_flag(1);
}
result = (uint8)b / (uint8)a;
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_c_flag(0);
break;
case DIVW3:
a = cpu_read_op(src1);
b = cpu_read_op(src2);
if (a == 0) {
cpu_abort(NORMAL_EXCEPTION, INTEGER_ZERO_DIVIDE);
break;
}
if (a == WORD_MASK && b == WD_MSB) {
cpu_set_v_flag(1);
}
DIV(a, b, src1, src2, int32);
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_c_flag(0);
break;
case DIVH3:
a = cpu_read_op(src1);
b = cpu_read_op(src2);
if (a == 0) {
cpu_abort(NORMAL_EXCEPTION, INTEGER_ZERO_DIVIDE);
break;
}
if (a == HALF_MASK && b == HW_MSB) {
cpu_set_v_flag(1);
}
DIV(a, b, src1, src2, int16);
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_c_flag(0);
break;
case DIVB3:
a = cpu_read_op(src1);
b = cpu_read_op(src2);
if (a == 0) {
cpu_abort(NORMAL_EXCEPTION, INTEGER_ZERO_DIVIDE);
break;
}
if (a == BYTE_MASK && b == BT_MSB) {
cpu_set_v_flag(1);
}
result = (uint8)b / (uint8)a;
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_c_flag(0);
break;
case MVERNO:
R[0] = WE32100_VER;
break;
case ENBVJMP:
if (cpu_execution_level() != EX_LVL_KERN) {
cpu_abort(NORMAL_EXCEPTION, PRIVILEGED_OPCODE);
break;
}
mmu_enable();
R[NUM_PC] = R[0];
continue;
case DISVJMP:
if (cpu_execution_level() != EX_LVL_KERN) {
cpu_abort(NORMAL_EXCEPTION, PRIVILEGED_OPCODE);
break;
}
mmu_disable();
R[NUM_PC] = R[0];
continue;
case EXTFW:
case EXTFH:
case EXTFB:
width = (cpu_read_op(src1) & 0x1f) + 1;
offset = cpu_read_op(src2) & 0x1f;
if (width >= 32) {
mask = -1;
} else {
mask = (1ul << width) - 1;
}
mask = mask << offset;
if (width + offset > 32) {
mask |= (1ul << ((width + offset) - 32)) - 1;
}
a = cpu_read_op(src3); /* src */
a &= mask;
a = a >> offset;
cpu_write_op(dst, a);
cpu_set_nz_flags(a, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(a, dst);
break;
case INCW:
case INCH:
case INCB:
a = cpu_read_op(dst);
add(a, 1, dst);
break;
case INSFW:
case INSFH:
case INSFB:
width = (cpu_read_op(src1) & 0x1f) + 1;
offset = cpu_read_op(src2) & 0x1f;
if (width >= 32) {
mask = -1;
} else {
mask = (1ul << width) - 1;
}
a = cpu_read_op(src3) & mask; /* src */
b = cpu_read_op(dst); /* dst */
b &= ~(mask << offset);
b |= (a << offset);
cpu_write_op(dst, b);
cpu_set_nz_flags(b, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(b, dst);
break;
case JMP:
R[NUM_PC] = cpu_effective_address(dst);
continue;
case JSB:
cpu_push_word(R[NUM_PC] + cpu_ilen);
R[NUM_PC] = cpu_effective_address(dst);
continue;
case LLSW3:
result = (t_uint64)cpu_read_op(src2) << (cpu_read_op(src1) & 0x1f);
cpu_write_op(dst, (uint32)(result & WORD_MASK));
cpu_set_nz_flags((uint32)(result & WORD_MASK), dst);
break;
case LLSH3:
a = cpu_read_op(src2) << (cpu_read_op(src1) & 0x1f);
cpu_write_op(dst, a);
cpu_set_nz_flags(a, dst);
break;
case LLSB3:
a = cpu_read_op(src2) << (cpu_read_op(src1) & 0x1f);
cpu_write_op(dst, a);
cpu_set_nz_flags(a, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(a, dst);
break;
case ARSW3:
case ARSH3:
case ARSB3:
a = cpu_read_op(src2);
b = cpu_read_op(src1) & 0x1f;
result = a >> b;
/* Ensure the MSB is copied appropriately */
switch (op_type(src2)) {
case WD:
if (a & 0x80000000) {
result |= shift_32_table[b + 1];
}
break;
case HW:
if (a & 0x8000) {
result |= shift_16_table[b + 1];
}
break;
case BT:
if (a & 0x80) {
result |= shift_8_table[b + 1];
}
break;
}
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_c_flag(0);
cpu_set_v_flag(0);
break;
case LRSW3:
a = (uint32) cpu_read_op(src2) >> (cpu_read_op(src1) & 0x1f);
cpu_write_op(dst, a);
cpu_set_nz_flags(a, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(a, dst);
break;
case GATE:
cpu_km = TRUE;
abort_context = C_PROCESS_GATE_PCB;
if (R[NUM_SP] < read_w(R[NUM_PCBP] + 12, ACC_AF) ||
R[NUM_SP] >= read_w(R[NUM_PCBP] + 16, ACC_AF)) {
sim_debug(EXECUTE_MSG, &cpu_dev,
"[%08x] STACK OUT OF BOUNDS IN GATE. "
"SP=%08x, R[NUM_PCBP]+12=%08x, "
"R[NUM_PCBP]+16=%08x\n",
R[NUM_PC],
R[NUM_SP],
read_w(R[NUM_PCBP] + 12, ACC_AF),
read_w(R[NUM_PCBP] + 16, ACC_AF));
cpu_abort(STACK_EXCEPTION, STACK_BOUND);
}
cpu_km = FALSE;
abort_context = C_STACK_FAULT;
/* Push PC+2 onto stack */
write_w(R[NUM_SP], R[NUM_PC] + 2);
/* Write 1, 0, 2 to ISC, TM, ET */
R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK);
R[NUM_PSW] |= (1 << PSW_ISC);
R[NUM_PSW] |= (2 << PSW_ET);
/* Push PSW onto stack */
write_w(R[NUM_SP] + 4, R[NUM_PSW]);
abort_context = C_NONE;
/* Perform gate entry-point 2 */
cpu_perform_gate(R[0] & 0x7c,
R[1] & 0x7ff8);
/* Finish push of PC and PSW */
R[NUM_SP] += 8;
continue;
case MCOMW:
case MCOMH:
case MCOMB:
a = ~(cpu_read_op(src1));
cpu_write_op(dst, a);
cpu_set_nz_flags(a, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(a, dst);
break;
case MNEGW:
case MNEGH:
case MNEGB:
a = ~cpu_read_op(src1) + 1;
cpu_write_op(dst, a);
cpu_set_nz_flags(a, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(a, dst);
break;
case MOVBLW:
while (R[2] != 0) {
a = read_w(R[0], ACC_AF);
write_w(R[1], a);
R[2]--;
R[0] += 4;
R[1] += 4;
}
break;
case STREND:
while (read_b(R[0], ACC_AF) != 0) {
R[0]++;
}
break;
case SWAPWI:
case SWAPHI:
case SWAPBI:
a = cpu_read_op(dst);
cpu_write_op(dst, R[0]);
R[0] = a;
cpu_set_nz_flags(a, dst);
cpu_set_v_flag(0);
cpu_set_c_flag(0);
break;
case ROTW:
a = cpu_read_op(src1) & 0x1f;
b = (uint32) cpu_read_op(src2);
mask = (CHAR_BIT * sizeof(a) - 1);
d = (b >> a) | (b << ((~a + 1) & mask));
cpu_write_op(dst, d);
cpu_set_nz_flags(d, dst);
cpu_set_v_flag(0);
cpu_set_c_flag(0);
break;
case MOVAW:
a = cpu_effective_address(src1);
cpu_write_op(dst, a);
cpu_set_nz_flags(a, dst);
cpu_set_v_flag(0);
cpu_set_c_flag(0);
break;
case MOVTRW:
a = cpu_effective_address(src1);
result = mmu_xlate_addr(a, ACC_MT);
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_v_flag(0);
cpu_set_c_flag(0);
break;
case MOVW:
case MOVH:
case MOVB:
a = cpu_read_op(src1);
cpu_write_op(dst, a);
/* Flags are never set if the source or destination is the
PSW */
if (!(op_is_psw(src1) || op_is_psw(dst))) {
cpu_set_nz_flags(a, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(a, dst);
}
/* However, if a move to PSW set the O bit, we have to
generate an overflow exception trap */
if (op_is_psw(dst) && (R[NUM_PSW] & PSW_OE_MASK)) {
cpu_abort(NORMAL_EXCEPTION, INTEGER_OVERFLOW);
}
break;
case MODW2:
a = cpu_read_op(src1);
b = cpu_read_op(dst);
if (a == 0) {
cpu_abort(NORMAL_EXCEPTION, INTEGER_ZERO_DIVIDE);
break;
}
MOD(a, b, src1, dst, int32);
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(result, dst);
break;
case MODH2:
a = cpu_read_op(src1);
b = cpu_read_op(dst);
if (a == 0) {
cpu_abort(NORMAL_EXCEPTION, INTEGER_ZERO_DIVIDE);
break;
}
MOD(a, b, src1, dst, int16);
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(result, dst);
break;
case MODB2:
a = cpu_read_op(src1);
b = cpu_read_op(dst);
if (a == 0) {
cpu_abort(NORMAL_EXCEPTION, INTEGER_ZERO_DIVIDE);
break;
}
result = (uint8)b % (uint8)a;
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(result, dst);
break;
break;
case MODW3:
a = cpu_read_op(src1);
b = cpu_read_op(src2);
if (a == 0) {
cpu_abort(NORMAL_EXCEPTION, INTEGER_ZERO_DIVIDE);
break;
}
MOD(a, b, src1, src2, int32);
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(result, dst);
break;
case MODH3:
a = cpu_read_op(src1);
b = cpu_read_op(src2);
if (a == 0) {
cpu_abort(NORMAL_EXCEPTION, INTEGER_ZERO_DIVIDE);
break;
}
MOD(a, b, src1, src2, int16);
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(result, dst);
break;
case MODB3:
a = cpu_read_op(src1);
b = cpu_read_op(src2);
if (a == 0) {
cpu_abort(NORMAL_EXCEPTION, INTEGER_ZERO_DIVIDE);
break;
}
result = (uint8)b % (uint8)a;
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(result, dst);
break;
case MULW2:
result = (t_uint64)cpu_read_op(src1) * (t_uint64)cpu_read_op(dst);
cpu_write_op(dst, (uint32)(result & WORD_MASK));
cpu_set_nz_flags((uint32)(result & WORD_MASK), dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(result, dst);
break;
case MULH2:
a = cpu_read_op(src1) * cpu_read_op(dst);
cpu_write_op(dst, a);
cpu_set_nz_flags(a, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(result, dst);
break;
case MULB2:
a = cpu_read_op(src1) * cpu_read_op(dst);
cpu_write_op(dst, a);
cpu_set_nz_flags(a, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(result, src1);
break;
case MULW3:
result = (t_uint64)cpu_read_op(src1) * (t_uint64)cpu_read_op(src2);
cpu_write_op(dst, (uint32)(result & WORD_MASK));
cpu_set_nz_flags((uint32)(result & WORD_MASK), dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(result, dst);
break;
case MULH3:
a = cpu_read_op(src1) * cpu_read_op(src2);
cpu_write_op(dst, a);
cpu_set_nz_flags(a, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(result, dst);
break;
case MULB3:
a = cpu_read_op(src1) * cpu_read_op(src2);
cpu_write_op(dst, a);
cpu_set_nz_flags(a, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(result, dst);
break;
case NOP:
break;
case NOP2:
cpu_ilen += 1;
break;
case NOP3:
cpu_ilen += 2;
break;
case ORW2:
case ORH2:
case ORB2:
a = (cpu_read_op(src1) | cpu_read_op(dst));
cpu_write_op(dst, a);
cpu_set_nz_flags(a, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(a, dst);
break;
case ORW3:
case ORH3:
case ORB3:
a = (cpu_read_op(src1) | cpu_read_op(src2));
cpu_write_op(dst, a);
cpu_set_nz_flags(a, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(a, dst);
break;
case POPW:
/* N.B. "If dst is the stack pointer (%sp), the results
are indeterminate". The ordering here is important. If
we decrement SP before writing the results, we end up
in a weird, bad state. */
a = read_w(R[NUM_SP] - 4, ACC_AF);
cpu_write_op(dst, a);
R[NUM_SP] -= 4;
cpu_set_nz_flags(a, dst);
cpu_set_c_flag(0);
cpu_set_v_flag(0);
break;
case PUSHAW:
a = cpu_effective_address(src1);
cpu_push_word(a);
cpu_set_nz_flags(a, src1);
cpu_set_c_flag(0);
cpu_set_v_flag(0);
break;
case PUSHW:
a = cpu_read_op(src1);
cpu_push_word(a);
cpu_set_nz_flags(a, src1);
cpu_set_c_flag(0);
cpu_set_v_flag(0);
break;
case RGEQ:
if (cpu_n_flag() == 0 || cpu_z_flag() == 1) {
R[NUM_PC] = cpu_pop_word();
continue;
}
break;
case RGEQU:
if (cpu_c_flag() == 0) {
R[NUM_PC] = cpu_pop_word();
continue;
}
break;
case RGTR:
if ((cpu_n_flag() | cpu_z_flag()) == 0) {
R[NUM_PC] = cpu_pop_word();
continue;
}
break;
case RNEQ:
case RNEQU:
if (cpu_z_flag() == 0) {
R[NUM_PC] = cpu_pop_word();
continue;
}
break;
case RET:
a = R[NUM_AP];
b = read_w(R[NUM_SP] - 4, ACC_AF);
c = read_w(R[NUM_SP] - 8, ACC_AF);
R[NUM_AP] = b;
R[NUM_PC] = c;
R[NUM_SP] = a;
continue;
case RETG:
abort_context = C_STACK_FAULT;
a = read_w(R[NUM_SP] - 4, ACC_AF); /* PSW */
b = read_w(R[NUM_SP] - 8, ACC_AF); /* PC */
abort_context = C_NONE;
if ((a & PSW_CM_MASK) < (R[NUM_PSW] & PSW_CM_MASK)) {
sim_debug(EXECUTE_MSG, &cpu_dev,
"[%08x] Illegal level change. New level=%d, Cur level=%d\n",
R[NUM_PC],
(a & PSW_CM_MASK) >> PSW_CM,
(R[NUM_PSW] & PSW_CM_MASK) >> PSW_CM);
cpu_abort(NORMAL_EXCEPTION, ILLEGAL_LEVEL_CHANGE);
break;
}
/* Clear some state and move it from the current PSW */
a &= ~PSW_IPL_MASK;
a &= ~PSW_CFD_MASK;
a &= ~PSW_QIE_MASK;
a &= ~PSW_CD_MASK;
a &= ~PSW_R_MASK;
a &= ~PSW_ISC_MASK;
a &= ~PSW_TM_MASK;
a &= ~PSW_ET_MASK;
a |= (R[NUM_PSW] & PSW_IPL_MASK);
a |= (R[NUM_PSW] & PSW_CFD_MASK);
a |= (R[NUM_PSW] & PSW_QIE_MASK);
a |= (R[NUM_PSW] & PSW_CD_MASK);
a |= (R[NUM_PSW] & PSW_R_MASK);
a |= (7 << PSW_ISC);
a |= (3 << PSW_ET);
R[NUM_PSW] = a;
R[NUM_PC] = b;
R[NUM_SP] -= 8;
continue;
case RETPS:
if (cpu_execution_level() != EX_LVL_KERN) {
cpu_abort(NORMAL_EXCEPTION, PRIVILEGED_OPCODE);
break;
}
/* Force kernel memory access */
cpu_km = TRUE;
abort_context = C_RESET_INT_STACK;
/* Restore process state */
a = irq_pop_word(); /* New process PCBP */
abort_context = C_PROCESS_OLD_PCB;
b = read_w(a, ACC_AF); /* New PSW */
abort_context = C_PROCESS_NEW_PCB;
/* Copy the 'R' flag from the new PSW to the old PSW */
R[NUM_PSW] &= ~PSW_R_MASK;
R[NUM_PSW] |= (b & PSW_R_MASK);
/* a now holds the new PCBP */
cpu_context_switch_2(a);
/* Perform block moves, if any */
cpu_context_switch_3(a);
/* Restore registers if R bit is set */
if (R[NUM_PSW] & PSW_R_MASK) {
R[NUM_FP] = read_w(a + 24, ACC_AF);
R[0] = read_w(a + 28, ACC_AF);
R[1] = read_w(a + 32, ACC_AF);
R[2] = read_w(a + 36, ACC_AF);
R[3] = read_w(a + 40, ACC_AF);
R[4] = read_w(a + 44, ACC_AF);
R[5] = read_w(a + 48, ACC_AF);
R[6] = read_w(a + 52, ACC_AF);
R[7] = read_w(a + 56, ACC_AF);
R[8] = read_w(a + 60, ACC_AF);
R[NUM_AP] = read_w(a + 20, ACC_AF);
}
abort_context = C_NONE;
/* Un-force kernel memory access */
cpu_km = FALSE;
continue;
case SPOP:
case SPOPRD:
case SPOPRS:
/* Memory fault is signaled when no support processor is
active */
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
break;
case SUBW2:
case SUBH2:
case SUBB2:
a = cpu_read_op(dst);
b = cpu_read_op(src1);
sub(a, b, dst);
break;
case SUBW3:
case SUBH3:
case SUBB3:
a = cpu_read_op(src2);
b = cpu_read_op(src1);
sub(a, b, dst);
break;
case RESTORE:
a = R[NUM_FP] - 28; /* Old FP */
b = read_w(a, ACC_AF); /* Old FP */
c = R[NUM_FP] - 24; /* Old save point */
for (d = src1->reg; d < NUM_FP; d++) {
R[d] = read_w(c, ACC_AF);
c += 4;
}
R[NUM_FP] = b; /* Restore FP */
R[NUM_SP] = a; /* Restore SP */
break;
case RLEQ:
if ((cpu_n_flag() | cpu_z_flag()) == 1) {
R[NUM_PC] = cpu_pop_word();
continue;
}
break;
case RLEQU:
if ((cpu_c_flag() | cpu_z_flag()) == 1) {
R[NUM_PC] = cpu_pop_word();
continue;
}
break;
case RLSS:
if ((cpu_n_flag() == 1) & (cpu_z_flag() == 0)) {
R[NUM_PC] = cpu_pop_word();
continue;
}
break;
case REQL:
if (cpu_z_flag() == 1) {
R[NUM_PC] = cpu_pop_word();
continue;
}
break;
case REQLU:
if (cpu_z_flag() == 1) {
R[NUM_PC] = cpu_pop_word();
continue;
}
break;
case RSB:
R[NUM_PC] = cpu_pop_word();
continue;
case SAVE:
/* Save the FP register */
write_w(R[NUM_SP], R[NUM_FP]);
/* Save all the registers from the one identified by the
src operand up to FP (exclusive) */
for (a = src1->reg, b = 4; a < NUM_FP; a++, b += 4) {
write_w(R[NUM_SP] + b, R[a]);
}
R[NUM_SP] = R[NUM_SP] + 28;
R[NUM_FP] = R[NUM_SP];
break;
case STRCPY:
a = 0;
b = 0;
do {
b = read_b(R[0] + a, ACC_AF);
write_b(R[1] + a, (uint8) b);
a++;
} while (b != '\0');
break;
case TSTW:
a = cpu_read_op(src1);
cpu_set_n_flag((int32)a < 0);
cpu_set_z_flag(a == 0);
cpu_set_c_flag(0);
cpu_set_v_flag(0);
break;
case TSTH:
a = cpu_read_op(src1);
cpu_set_n_flag((int16)a < 0);
cpu_set_z_flag(a == 0);
cpu_set_c_flag(0);
cpu_set_v_flag(0);
break;
case TSTB:
a = cpu_read_op(src1);
cpu_set_n_flag((int8)a < 0);
cpu_set_z_flag(a == 0);
cpu_set_c_flag(0);
cpu_set_v_flag(0);
break;
case WAIT:
cpu_in_wait = TRUE;
break;
case XORW2:
case XORH2:
case XORB2:
a = (cpu_read_op(src1) ^ cpu_read_op(dst));
cpu_write_op(dst, a);
cpu_set_nz_flags(a, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(a, dst);
break;
case XORW3:
case XORH3:
case XORB3:
a = (cpu_read_op(src1) ^ cpu_read_op(src2));
cpu_write_op(dst, a);
cpu_set_nz_flags(a, dst);
cpu_set_c_flag(0);
cpu_set_v_flag_op(a, dst);
break;
default:
stop_reason = STOP_OPCODE;
break;
};
/* Increment the PC appropriately */
R[NUM_PC] += cpu_ilen;
}
return stop_reason;
}
static SIM_INLINE void cpu_on_process_exception(uint8 isc)
{
/* TODO: Handle */
stop_reason = STOP_EX;
return;
}
static SIM_INLINE void cpu_on_reset_exception(uint8 isc)
{
uint32 new_pcbp;
sim_debug(EXECUTE_MSG, &cpu_dev,
"[%08x] [cpu_on_reset_exception %d] SP=%08x PCBP=%08x ISP=%08x\n",
R[NUM_PC], isc, R[NUM_SP], R[NUM_PCBP], R[NUM_ISP]);
if (isc == EXTERNAL_RESET) {
R[NUM_PSW] &= ~(PSW_R_MASK);
}
cpu_km = TRUE;
mmu_disable();
abort_context = C_RESET_SYSTEM_DATA;
new_pcbp = read_w(0x80, ACC_AF);
abort_context = C_RESET_NEW_PCB;
cpu_context_switch_2(new_pcbp);
cpu_km = FALSE;
abort_context = C_NONE;
}
static SIM_INLINE void cpu_on_stack_exception(uint8 isc)
{
uint32 new_pcbp;
sim_debug(EXECUTE_MSG, &cpu_dev,
"[%08x] [cpu_on_stack_exception %d] SP=%08x PCBP=%08x ISP=%08x\n",
R[NUM_PC], isc, R[NUM_SP], R[NUM_PCBP], R[NUM_ISP]);
abort_context = C_RESET_SYSTEM_DATA;
cpu_km = TRUE;
new_pcbp = read_w(0x88, ACC_AF);
abort_context = C_RESET_INT_STACK;
irq_push_word(R[NUM_PCBP]);
abort_context = C_PROCESS_OLD_PCB;
R[NUM_PSW] &= ~(PSW_ET_MASK|PSW_ISC_MASK);
R[NUM_PSW] |= (2 << PSW_ET);
R[NUM_PSW] |= (uint32) (isc << PSW_ISC);
cpu_context_switch_1(new_pcbp);
cpu_context_switch_2(new_pcbp);
/* Set ISC, TM, and ET to 7, 0, 3 in new PSW */
R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK);
R[NUM_PSW] |= (7 << PSW_ISC);
R[NUM_PSW] |= (3 << PSW_ET);
cpu_km = FALSE;
abort_context = C_NONE;
}
static SIM_INLINE void cpu_on_normal_exception(uint8 isc)
{
sim_debug(EXECUTE_MSG, &cpu_dev,
"[%08x] [cpu_on_normal_exception %d] %%sp=%08x abort_context=%d\n",
R[NUM_PC], isc, R[NUM_SP], abort_context);
abort_context = C_PROCESS_GATE_PCB;
cpu_km = TRUE;
if (R[NUM_SP] < read_w(R[NUM_PCBP] + 12, ACC_AF) ||
R[NUM_SP] >= read_w(R[NUM_PCBP] + 16, ACC_AF)) {
sim_debug(EXECUTE_MSG, &cpu_dev,
"[%08x] STACK OUT OF BOUNDS IN EXCEPTION HANDLER. "
"SP=%08x, R[NUM_PCBP]+12=%08x, "
"R[NUM_PCBP]+16=%08x\n",
R[NUM_PC],
R[NUM_SP],
read_w(R[NUM_PCBP] + 12, ACC_AF),
read_w(R[NUM_PCBP] + 16, ACC_AF));
abort_context = C_NONE;
cpu_abort(STACK_EXCEPTION, STACK_BOUND);
}
cpu_km = FALSE;
/* Set context for STACK (FAULT) */
abort_context = C_STACK_FAULT;
write_w(R[NUM_SP], R[NUM_PC]);
/* Write 0, 3 to TM, ET fields of PSW */
R[NUM_PSW] &= ~(PSW_TM_MASK|PSW_ET_MASK);
R[NUM_PSW] |= (3 << PSW_ET);
/* Save address of next instruction and PSW to stack */
write_w(R[NUM_SP] + 4, R[NUM_PSW]);
/* Set context for RESET (GATE VECTOR) */
abort_context = C_RESET_GATE_VECTOR;
cpu_perform_gate(0, ((uint32) isc) << 3);
/* Finish push of old PC and PSW */
R[NUM_SP] += 8;
abort_context = C_NONE;
}
static SIM_INLINE void cpu_perform_gate(uint32 index1, uint32 index2)
{
uint32 gate_l2, new_psw;
cpu_km = TRUE;
gate_l2 = read_w(index1, ACC_AF) + index2;
/* Get new PSW from second-level table */
new_psw = read_w(gate_l2, ACC_AF);
/* Clear state in PSW */
new_psw &= ~(PSW_PM_MASK|PSW_IPL_MASK|PSW_R_MASK|
PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK);
/* Set PM in new PSW */
new_psw |= (R[NUM_PSW] & PSW_CM_MASK) >> 2; /* PM */
new_psw |= (R[NUM_PSW] & PSW_IPL_MASK); /* IPL */
new_psw |= (R[NUM_PSW] & PSW_R_MASK); /* R */
/* Set new PSW ISC, TM, and ET to 7, 1, 3 */
new_psw |= (7 << PSW_ISC); /* ISC */
new_psw |= (1 << PSW_TM); /* TM */
new_psw |= (3 << PSW_ET); /* ET */
R[NUM_PC] = read_w(gate_l2 + 4, ACC_AF);
R[NUM_PSW] = new_psw;
cpu_km = FALSE;
}
/*
* TODO: Setting 'data' to the effective address is bogus. We're only
* doing it because we want to get the address when we trace the
* instructions using "SHOW CPU HISTORY". We should just put
* effective_address as a field in the operand struct and make
* cpu_show_hist smarter.
*/
static uint32 cpu_effective_address(operand *op)
{
/* Register Deferred */
if (op->mode == 5 && op->reg != 11) {
return R[op->reg];
}
/* Absolute */
if (op->mode == 7 && op->reg == 15) {
return op->embedded.w;
}
/* Absolute Deferred */
if (op->mode == 14 && op->reg == 15) {
/* May cause exception */
return read_w(op->embedded.w, ACC_AF);
}
/* FP Short Offset */
if (op->mode == 6 && op->reg != 15) {
return R[NUM_FP] + sign_extend_b(op->embedded.b);
}
/* AP Short Offset */
if (op->mode == 7 && op->reg != 15) {
return R[NUM_AP] + sign_extend_b(op->embedded.b);
}
/* Word Displacement */
if (op->mode == 8) {
return R[op->reg] + op->embedded.w;
}
/* Word Displacement Deferred */
if (op->mode == 9) {
return read_w(R[op->reg] + op->embedded.w, ACC_AF);
}
/* Halfword Displacement */
if (op->mode == 10) {
return R[op->reg] + sign_extend_h(op->embedded.h);
}
/* Halfword Displacement Deferred */
if (op->mode == 11) {
return read_w(R[op->reg] + sign_extend_h(op->embedded.h), ACC_AF);
}
/* Byte Displacement */
if (op->mode == 12) {
return R[op->reg] + sign_extend_b(op->embedded.b);
}
/* Byte Displacement Deferred */
if (op->mode == 13) {
return read_w(R[op->reg] + sign_extend_b(op->embedded.b), ACC_AF);
}
assert(0);
return 0;
}
/*
* Read and Write routines for operands.
*
* The rules for dealing with the type (signed/unsigned,
* byte/halfword/word) of operands are fairly complex.
*
* 1. The expanded operand mode does not affect the treatment of
* Literal Mode operands. All literals are signed.
*
* 2. The expanded operand mode does not affect the length of
* Immediate Mode operands, but does affect whether they are signed
* or unsigned.
*
* 3. When using expanded-mode operands, the new type remains in
* effect for the operands that folow in the instruction unless
* another expanded operand mode overrides it. (This rule in
* particular is managed by decode_instruction())
*
* 4. The expanded operand mode is illegal with coprocessor instructions
* and CALL, SAVE, RESTORE, SWAP INTERLOCKED, PUSAHW, PUSHAW, POPW,
* and JSB. (Illegal Operand Fault)
*
* 5. When writing a byte, the Negative (N) flag is set based on the
* high bit of the data type being written, regardless of the SIGN
* of the extended datatype. e.g.: {ubyte} and {sbyte} both check
* for bit 7, {uhalf} and {shalf} both check for bit 15, and
* {uword} and {sword} both check for bit 31.
*
* 6. For instructions with a signed destination, V is set if the sign
* bit of the output value is different from any truncated bit of
* the result. For instructions with an unsigned destination, V is
* set if any truncated bit is 1.
*/
/*
* Read the data referenced by an operand. Performs sign or zero
* extension as required by the read width and operand type, then
* returns the read value.
*
* "All operations are performed only on 32-bit quantities even though
* an instruction may specify a byte or halfword operand. The WE
* 32100 Microprocessor reads in the correct number of bits for the
* operand and extends the data automatically to 32 bits. It uses
* sign extension when reading signed data or halfwords and zero
* extension when reading unsigned data or bytes (or bit fields that
* contain less than 32 bits). The data type of the source operand
* determines how many bits are fetched and what type of extension is
* applied. Bytes are treated as unsigned, while halfwords and words
* are considered signed. The type of extension applied can be
* changed using the expanded-operand type mode as described in 3.4.5
* Expanded-Operand Type Mode. For sign extension, the value of the
* MSB or sign bit of the data fills the high-order bits to form a
* 32-bit value. In zero extension, zeros fill the high order bits.
* The microprocessor automatically extends a byte or halfword to 32
* bits before performing an operation. Figure 3-3 illustrates sign
* and zero extension. An arithmetic, logical, data transfer, or bit
* field operation always yields an intermediate result that is 32
* bits in length. If the result is to be stored in a register, the
* processor writes all 32 bits to that register. The processor
* automatically strips any surplus high-order bits from a result
* when writing bytes or halfwords to memory." -- "WE 32100
* Microprocessor Information Manual", Section 3.1.1
*
*/
static uint32 cpu_read_op(operand * op)
{
uint32 eff;
uint32 data;
/* Register */
if (op->mode == 4 && op->reg < 15) {
switch (op_type(op)) {
case WD:
case UW:
data = R[op->reg];
break;
case HW:
data = sign_extend_h(R[op->reg] & HALF_MASK);
break;
case UH:
data = R[op->reg] & HALF_MASK;
break;
case BT:
data = R[op->reg] & BYTE_MASK;
break;
case SB:
data = sign_extend_b(R[op->reg] & BYTE_MASK);
break;
default:
assert(0);
}
op->data = data;
return data;
}
/* Literal */
if (op->mode < 4 || op->mode == 15) {
/* Both positive and negative literals are _always_ treated as
signed bytes, and they are _always_ sign extended. They
simply ignore expanded datatypes. */
data = sign_extend_b(op->embedded.b);
op->data = data;
return data;
}
/* Immediate */
if (op->reg == 15 &&
(op->mode == 4 || op->mode == 5 || op->mode == 6)) {
switch (op->mode) {
case 4: /* Word Immediate */
data = op->embedded.w;
op->data = data;
return data;
case 5: /* Halfword Immediate */
data = sign_extend_h(op->embedded.h);
op->data = data;
return data;
case 6: /* Byte Immedaite */
data = sign_extend_b(op->embedded.b);
op->data = data;
return data;
}
}
/* At this point, we'll need to find an effective address */
eff = cpu_effective_address(op);
switch (op_type(op)) {
case WD: /* Signed Word */
case UW: /* Unsigned Word */
data = read_w(eff, ACC_OF);
op->data = data;
return data;
case HW: /* Signed Halfword */
data = sign_extend_h(read_h(eff, ACC_OF));
op->data = data;
return data;
case UH: /* Unsigned Halfword */
data = read_h(eff, ACC_OF);
op->data = data;
return data;
case SB: /* Signed Byte */
data = sign_extend_b(read_b(eff, ACC_OF));
op->data = data;
return data;
case BT: /* Unsigned Byte */
data = read_b(eff, ACC_OF);
op->data = data;
return data;
default:
assert(0);
return 0;
}
}
static void cpu_write_op(operand * op, t_uint64 val)
{
uint32 eff;
op->data = (uint32) val;
/* Writing to a register. */
if (op->mode == 4 && op->reg < 15) {
if ((op->reg == NUM_PSW || op->reg == NUM_PCBP || op->reg == NUM_ISP) &&
cpu_execution_level() != EX_LVL_KERN) {
cpu_abort(NORMAL_EXCEPTION, PRIVILEGED_REGISTER);
return;
}
/* Registers always get the full 32-bits written */
R[op->reg] = (uint32) val;
return;
}
/* Literal mode is not legal. */
if (op->mode < 4 || op->mode == 15) {
cpu_abort(NORMAL_EXCEPTION, INVALID_DESCRIPTOR);
return;
}
/* Immediate mode is not legal. */
if (op->reg == 15 &&
(op->mode == 4 || op->mode == 5 || op->mode == 6)) {
cpu_abort(NORMAL_EXCEPTION, INVALID_DESCRIPTOR);
return;
}
eff = cpu_effective_address(op);
switch (op_type(op)) {
case UW:
case WD:
write_w(eff, (uint32) val);
break;
case HW:
case UH:
if (val > HALF_MASK) {
cpu_set_v_flag(TRUE);
}
write_h(eff, val & HALF_MASK);
break;
case SB:
case BT:
if (val > BYTE_MASK) {
cpu_set_v_flag(TRUE);
}
write_b(eff, val & BYTE_MASK);
break;
default:
assert(0);
}
}
/*
* This returns the current state of the IPL (Interrupt
* Priority Level) bus. This is affected by:
*
* - Latched values in the CSR for:
* o CSRCLK 15
* o CSRDMA 13
* o CSRUART 13
* o CSRDISK 11
* o CSRPIR9 9
* o CSRPIR8 8
* - IRQ currently enabled for:
* o HD Ctlr. 11
*/
static SIM_INLINE uint8 cpu_ipl()
{
/* CSRPIR9 is cleared by writing to c_pir8 */
if (csr_data & CSRPIR8) {
return 8;
}
/* CSRPIR9 is cleared by writing to c_pir9 */
if (csr_data & CSRPIR9) {
return 9;
}
/* CSRDISK is cleared when the floppy "if_irq" goes low */
if (id_irq || (csr_data & CSRDISK)) {
return 11;
}
/* CSRDMA is cleared by write/read to 0x49011 */
/* CSRUART is cleared when the uart "iu_irq" goes low */
if ((csr_data & CSRUART) || (csr_data & CSRDMA)) {
return 13;
}
/* CSRCLK is cleared by $clrclkint */
if (csr_data & CSRCLK) {
return 15;
}
return 0;
}
/*
* Returns the correct datatype for an operand -- either extended type
* or default type.
*/
static SIM_INLINE int8 op_type(operand *op) {
if (op->etype > -1) {
return op->etype;
} else {
return op->dtype;
}
}
static SIM_INLINE t_bool op_signed(operand *op) {
return (op_type(op) == WD || op_type(op) == HW || op_type(op) == SB);
}
static SIM_INLINE t_bool is_byte_immediate(operand * oper)
{
return oper->mode == 6 && oper->reg == 15;
}
static SIM_INLINE t_bool is_halfword_immediate(operand * oper)
{
return oper->mode == 5 && oper->reg == 15;
}
static SIM_INLINE t_bool is_word_immediate(operand * oper)
{
return oper->mode == 4 && oper->reg == 15;
}
static SIM_INLINE t_bool is_positive_literal(operand * oper)
{
return (oper->mode == 0 ||
oper->mode == 1 ||
oper->mode == 2);
}
static SIM_INLINE t_bool is_negative_literal(operand * oper)
{
return oper->mode == 15;
}
/* Returns true if the operand may not be used as a destination */
static SIM_INLINE t_bool invalid_destination(operand * oper)
{
return (is_byte_immediate(oper) ||
is_halfword_immediate(oper) ||
is_word_immediate(oper) ||
is_positive_literal(oper) ||
is_negative_literal(oper));
}
static SIM_INLINE uint32 sign_extend_b(uint8 val)
{
if (val & 0x80)
return ((uint32) val) | 0xffffff00;
return (uint32) val;
}
static SIM_INLINE uint32 zero_extend_b(uint8 val)
{
return (uint32) val & BYTE_MASK;
}
static SIM_INLINE uint32 sign_extend_h(uint16 val)
{
if (val & 0x8000)
return ((uint32) val) | 0xffff0000;
return (uint32) val;
}
static SIM_INLINE uint32 zero_extend_h(uint16 val)
{
return (uint32) val & HALF_MASK;
}
/*
* Returns the current CPU execution level.
*/
static SIM_INLINE uint8 cpu_execution_level()
{
return (R[NUM_PSW] & PSW_CM_MASK) >> PSW_CM;
}
static SIM_INLINE t_bool cpu_z_flag()
{
return (R[NUM_PSW] & PSW_Z_MASK) != 0;
}
static SIM_INLINE t_bool cpu_n_flag()
{
return (R[NUM_PSW] & PSW_N_MASK) != 0;
}
static SIM_INLINE t_bool cpu_c_flag()
{
return (R[NUM_PSW] & PSW_C_MASK) != 0;
}
static SIM_INLINE t_bool cpu_v_flag()
{
return (R[NUM_PSW] & PSW_V_MASK) != 0;
}
static SIM_INLINE void cpu_set_z_flag(t_bool val)
{
if (val) {
R[NUM_PSW] = R[NUM_PSW] | PSW_Z_MASK;
} else {
R[NUM_PSW] = R[NUM_PSW] & ~PSW_Z_MASK;
}
}
static SIM_INLINE void cpu_set_n_flag(t_bool val)
{
if (val) {
R[NUM_PSW] = R[NUM_PSW] | PSW_N_MASK;
} else {
R[NUM_PSW] = R[NUM_PSW] & ~PSW_N_MASK;
}
}
static SIM_INLINE void cpu_set_c_flag(t_bool val)
{
if (val) {
R[NUM_PSW] = R[NUM_PSW] | PSW_C_MASK;
} else {
R[NUM_PSW] = R[NUM_PSW] & ~PSW_C_MASK;
}
}
static SIM_INLINE void cpu_set_v_flag_op(t_uint64 val, operand *op)
{
switch(op_type(op)) {
case WD:
case UW:
cpu_set_v_flag(0);
break;
case HW:
case UH:
cpu_set_v_flag(val > HALF_MASK);
break;
case BT:
case SB:
default:
cpu_set_v_flag(val > BYTE_MASK);
break;
}
}
static SIM_INLINE void cpu_set_v_flag(t_bool val)
{
if (val) {
R[NUM_PSW] = R[NUM_PSW] | PSW_V_MASK;
if (R[NUM_PSW] & PSW_OE_MASK) {
cpu_abort(NORMAL_EXCEPTION, INTEGER_OVERFLOW);
}
} else {
R[NUM_PSW] = R[NUM_PSW] & ~PSW_V_MASK;
}
}
static void cpu_set_nz_flags(t_uint64 data, operand *dst)
{
int8 type = op_type(dst);
switch (type) {
case WD:
case UW:
cpu_set_n_flag(!!(WD_MSB & data));
cpu_set_z_flag((data & WORD_MASK) == 0);
break;
case HW:
case UH:
cpu_set_n_flag(HW_MSB & data);
cpu_set_z_flag((data & HALF_MASK) == 0);
break;
case BT:
case SB:
cpu_set_n_flag(BT_MSB & data);
cpu_set_z_flag((data & BYTE_MASK) == 0);
break;
}
}
static SIM_INLINE void cpu_push_word(uint32 val)
{
write_w(R[NUM_SP], val);
R[NUM_SP] += 4;
}
static SIM_INLINE uint32 cpu_pop_word()
{
uint32 result;
/* We always read fromthe stack first BEFORE decrementing,
in case this causes a fault. */
result = read_w(R[NUM_SP] - 4, ACC_AF);
R[NUM_SP] -= 4;
return result;
}
static SIM_INLINE void irq_push_word(uint32 val)
{
write_w(R[NUM_ISP], val);
R[NUM_ISP] += 4;
}
static SIM_INLINE uint32 irq_pop_word()
{
R[NUM_ISP] -= 4;
return read_w(R[NUM_ISP], ACC_AF);
}
static SIM_INLINE t_bool op_is_psw(operand *op)
{
return (op->mode == 4 && op->reg == NUM_PSW);
}
static SIM_INLINE t_bool op_is_sp(operand *op)
{
return op->reg == NUM_SP;
}
static SIM_INLINE void sub(t_uint64 a, t_uint64 b, operand *dst)
{
t_uint64 result;
result = a - b;
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
cpu_set_c_flag((uint32)b > (uint32)a);
cpu_set_v_flag_op(result, dst);
}
static SIM_INLINE void add(t_uint64 a, t_uint64 b, operand *dst)
{
t_uint64 result;
result = a + b;
cpu_write_op(dst, result);
cpu_set_nz_flags(result, dst);
switch(op_type(dst)) {
case WD:
case UW:
cpu_set_c_flag(result > WORD_MASK);
cpu_set_v_flag(!! (((a ^ ~b) & (a ^ result)) & WD_MSB));
break;
case HW:
case UH:
cpu_set_c_flag(result > HALF_MASK);
cpu_set_v_flag(((a ^ ~b) & (a ^ result)) & HW_MSB);
break;
case BT:
case SB:
cpu_set_c_flag(result > BYTE_MASK);
cpu_set_v_flag(((a ^ ~b) & (a ^ result)) & BT_MSB);
}
}
/*
* Set PSW's ET and ISC fields, and store global exception or fault
* state appropriately.
*/
void cpu_abort(uint8 et, uint8 isc)
{
/* We don't trap Integer Overflow if the OE bit is not set */
if ((R[NUM_PSW] & PSW_OE_MASK) || isc != INTEGER_OVERFLOW) {
R[NUM_PSW] &= ~(PSW_ISC_MASK); /* Clear ISC */
R[NUM_PSW] &= ~(PSW_ET_MASK); /* Clear ET */
R[NUM_PSW] |= et; /* Set ET */
R[NUM_PSW] |= (uint32) (isc << PSW_ISC); /* Set ISC */
if (et == 3 && (isc == BREAKPOINT_TRAP ||
isc == INTEGER_OVERFLOW ||
isc == TRACE_TRAP)) {
longjmp(save_env, ABORT_TRAP);
} else {
longjmp(save_env, ABORT_EXC);
}
}
}