/* 3b2_cpu.h: AT&T 3B2 Model 400 CPU (WE32100) Header | |
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. | |
*/ | |
#ifndef _3B2_CPU_H_ | |
#define _3B2_CPU_H_ | |
#include "3b2_defs.h" | |
#include "3b2_io.h" | |
/* Execution Modes */ | |
#define EX_LVL_KERN 0 | |
#define EX_LVL_EXEC 1 | |
#define EX_LVL_SUPR 2 | |
#define EX_LVL_USER 3 | |
/* Processor Version Number */ | |
#define WE32100_VER 0x1A | |
/* | |
* | |
* Mode Syntax Mode Reg. Bytes Notes | |
* ---------------------------------------------------------------------- | |
* Absolute $expr 7 15 5 | |
* Abs. Deferred *$expr 14 15 5 | |
* Byte Disp. expr(%rn) 12 0-10,12-15 2 | |
* Byte Disp. Def. *expr(%rn) 13 0-10,12-15 2 | |
* Halfword Disp. expr(%rn) 10 0-10,12-15 3 | |
* Halfword Disp. Def. *expr(%rn) 11 0-10,12-15 3 | |
* Word Disp. expr(%rn) 8 0-10,12-15 5 | |
* Word Disp. Def. *expr(%rn) 9 0-10,12-15 5 | |
* AP Short Offset so(%ap) 7 0-14 1 1 | |
* FP Short Offset so(%fp) 6 0-14 1 1 | |
* Byte Immediate &imm8 6 15 2 2,3 | |
* Halfword Immediate &imm16 5 15 3 2,3 | |
* Word Immediate &imm32 4 15 5 2,3 | |
* Positive Literal &lit 0-3 0-15 1 2,3 | |
* Negative Literal &lit 15 0-15 1 2,3 | |
* Register %rn 4 0-14 1 1,3 | |
* Register Deferred (%rn) 5 0-10,12-14 1 1 | |
* Expanded Op. Type {type}opnd 14 0-14 2-6 4 | |
* | |
* Notes: | |
* | |
* 1. Mode field has special meaning if register field is 15; see | |
* absolute or immediate mode. | |
* 2. Mode may not be used for a destination operand. | |
* 3. Mode may not be used if the instruction takes effective address | |
* of the operand. | |
* 4. 'type' overrides instruction type; 'type' determines the operand | |
* type, except that it does not determine the length for immediate | |
* or literals or whether literals are signed or unsigned. 'opnd' | |
* determines actual address mode. For total bytes, add 1 to byte | |
* count for address mode determined by 'opnd'. | |
* | |
*/ | |
/* | |
* Opcodes | |
*/ | |
typedef enum { | |
HALT = 0x00, /* Undocumented instruction */ | |
SPOPRD = 0x02, | |
SPOPD2 = 0x03, | |
MOVAW = 0x04, | |
SPOPRT = 0x06, | |
SPOPT2 = 0x07, | |
RET = 0x08, | |
MOVTRW = 0x0C, | |
SAVE = 0x10, | |
SPOPWD = 0x13, | |
EXTOP = 0x14, | |
SPOPWT = 0x17, | |
RESTORE = 0x18, | |
SWAPWI = 0x1C, | |
SWAPHI = 0x1E, | |
SWAPBI = 0x1F, | |
POPW = 0x20, | |
SPOPRS = 0x22, | |
SPOPS2 = 0x23, | |
JMP = 0x24, | |
CFLUSH = 0x27, | |
TSTW = 0x28, | |
TSTH = 0x2A, | |
TSTB = 0x2B, | |
CALL = 0x2C, | |
BPT = 0x2E, | |
WAIT = 0x2F, | |
EMB = 0x30, // Multi-byte | |
SPOP = 0x32, | |
SPOPWS = 0x33, | |
JSB = 0x34, | |
BSBH = 0x36, | |
BSBB = 0x37, | |
BITW = 0x38, | |
BITH = 0x3A, | |
BITB = 0x3B, | |
CMPW = 0x3C, | |
CMPH = 0x3E, | |
CMPB = 0x3F, | |
RGEQ = 0x40, | |
BGEH = 0x42, | |
BGEB = 0x43, | |
RGTR = 0x44, | |
BGH = 0x46, | |
BGB = 0x47, | |
RLSS = 0x48, | |
BLH = 0x4A, | |
BLB = 0x4B, | |
RLEQ = 0x4C, | |
BLEH = 0x4E, | |
BLEB = 0x4F, | |
RGEQU = 0x50, | |
BGEUH = 0x52, // Also BCCH | |
BGEUB = 0x53, // Also BCCB | |
RGTRU = 0x54, | |
BGUH = 0x56, | |
BGUB = 0x57, | |
BLSSU = 0x58, // Also BCS | |
BLUH = 0x5A, // Also BCSH | |
BLUB = 0x5B, // Also BCSB | |
RLEQU = 0x5C, | |
BLEUH = 0x5E, | |
BLEUB = 0x5F, | |
RVC = 0x60, | |
BVCH = 0x62, | |
BVCB = 0x63, | |
RNEQU = 0x64, | |
BNEH_D = 0x66, // Duplicate of 0x76 | |
BNEB_D = 0x67, // Duplicate of 0x77 | |
RVS = 0x68, | |
BVSH = 0x6A, | |
BVSB = 0x6B, | |
REQLU = 0x6C, | |
BEH_D = 0x6E, // Duplicate of 0x7E | |
BEB_D = 0x6F, // Duplicate of 0x7F | |
NOP = 0x70, | |
NOP3 = 0x72, | |
NOP2 = 0x73, | |
BNEQ = 0x74, | |
RNEQ = 0x74, | |
BNEH = 0x76, | |
BNEB = 0x77, | |
RSB = 0x78, | |
BRH = 0x7A, | |
BRB = 0x7B, | |
REQL = 0x7C, | |
BEH = 0x7E, | |
BEB = 0x7F, | |
CLRW = 0x80, | |
CLRH = 0x82, | |
CLRB = 0x83, | |
MOVW = 0x84, /* done */ | |
MOVH = 0x86, /* done */ | |
MOVB = 0x87, /* done */ | |
MCOMW = 0x88, | |
MCOMH = 0x8A, | |
MCOMB = 0x8B, | |
MNEGW = 0x8C, | |
MNEGH = 0x8E, | |
MNEGB = 0x8F, | |
INCW = 0x90, | |
INCH = 0x92, | |
INCB = 0x93, | |
DECW = 0x94, | |
DECH = 0x96, | |
DECB = 0x97, | |
ADDW2 = 0x9C, | |
ADDH2 = 0x9E, | |
ADDB2 = 0x9F, | |
PUSHW = 0xA0, | |
MODW2 = 0xA4, | |
MODH2 = 0xA6, | |
MODB2 = 0xA7, | |
MULW2 = 0xA8, | |
MULH2 = 0xAA, | |
MULB2 = 0xAB, | |
DIVW2 = 0xAC, | |
DIVH2 = 0xAE, | |
DIVB2 = 0xAF, | |
ORW2 = 0xB0, | |
ORH2 = 0xB2, | |
ORB2 = 0xB3, | |
XORW2 = 0xB4, | |
XORH2 = 0xB6, | |
XORB2 = 0xB7, | |
ANDW2 = 0xB8, | |
ANDH2 = 0xBA, | |
ANDB2 = 0xBB, | |
SUBW2 = 0xBC, | |
SUBH2 = 0xBE, | |
SUBB2 = 0xBF, | |
ALSW3 = 0xC0, | |
ARSW3 = 0xC4, | |
ARSH3 = 0xC6, | |
ARSB3 = 0xC7, | |
INSFW = 0xC8, | |
INSFH = 0xCA, | |
INSFB = 0xCB, | |
EXTFW = 0xCC, | |
EXTFH = 0xCE, | |
EXTFB = 0xCF, | |
LLSW3 = 0xD0, | |
LLSH3 = 0xD2, | |
LLSB3 = 0xD3, | |
LRSW3 = 0xD4, | |
ROTW = 0xD8, | |
ADDW3 = 0xDC, | |
ADDH3 = 0xDE, | |
ADDB3 = 0xDF, | |
PUSHAW = 0xE0, | |
MODW3 = 0xE4, | |
MODH3 = 0xE6, | |
MODB3 = 0xE7, | |
MULW3 = 0xE8, | |
MULH3 = 0xEA, | |
MULB3 = 0xEB, | |
DIVW3 = 0xEC, | |
DIVH3 = 0xEE, | |
DIVB3 = 0xEF, | |
ORW3 = 0xF0, | |
ORH3 = 0xF2, | |
ORB3 = 0xF3, | |
XORW3 = 0xF4, | |
XORH3 = 0xF6, | |
XORB3 = 0xF7, | |
ANDW3 = 0xF8, | |
ANDH3 = 0xFA, | |
ANDB3 = 0xFB, | |
SUBW3 = 0xFC, | |
SUBH3 = 0xFE, | |
SUBB3 = 0xFF, | |
MVERNO = 0x3009, | |
ENBVJMP = 0x300d, | |
DISVJMP = 0x3013, | |
MOVBLW = 0x3019, | |
STREND = 0x301f, | |
INTACK = 0x302f, | |
STRCPY = 0x3035, | |
RETG = 0x3045, | |
GATE = 0x3061, | |
CALLPS = 0x30ac, | |
RETPS = 0x30c8 | |
} opcode; | |
typedef enum { | |
DECODE_SUCCESS, | |
DECODE_ERROR | |
} decode_result; | |
/* Addressing Mode enum */ | |
typedef enum { | |
ABS, // Absolute | |
ABS_DEF, // Absolute Deferred | |
BYTE_DISP, // Byte Displacement | |
BYTE_DISP_DEF, // Byte Displacement Deferred | |
HFWD_DISP, // Halfword Displacement | |
HFWD_DISP_DEF, // Halfword Displacement Deferred | |
WORD_DISP, // Word Displacement | |
WORD_DISP_DEF, // Word Displacement Deferred | |
AP_SHORT_OFF, // AP Short Offset | |
FP_SHORT_OFF, // FP Short Offset | |
BYTE_IMM, // Byte Immediate | |
HFWD_IMM, // Halfword Immediate | |
WORD_IMM, // Word Immediate | |
POS_LIT, // Positive Literal | |
NEG_LIT, // Negative Literal | |
REGISTER, // Register | |
REGISTER_DEF, // Register Deferred | |
EXP // Expanded-operand type | |
} addr_mode; | |
/* | |
* Each instruction expects operands of a certain type. | |
* | |
* The large majority of instructions expect operands that have a | |
* descriptor as the first byte. This descriptor carries all the | |
* information necessary to compute the addressing mode of the | |
* operand. | |
* | |
* e.g.: | |
* | |
* MOVB 6(%r1),%r0 | |
* +------+------+------+------+ | |
* | 0x87 | 0xc1 | 0x06 | 0x40 | | |
* +------+------+------+------+ | |
* ^^^^^^ | |
* Descriptor byte. mode = 13 (0x0c), register = 1 (0x01) | |
* | |
* | |
* Branch instructions have either an 8-bit or a 16-bit signed | |
* displacement value, and lack a descriptor byte. | |
* | |
* e.g.: | |
* | |
* BCCB 0x03 | |
* +------+------+ | |
* | 0x53 | 0x03 | 8-bit displacement | |
* +------+------+ | |
* | |
* BCCH 0x01ff | |
* +------+------+------+ | |
* | 0x52 | 0xff | 0x01 | 16-bit displacement | |
* +------+------+------+ | |
* | |
* | |
* TODO: Describe coprocessor instructions | |
* | |
*/ | |
typedef enum { | |
OP_NONE, /* NULL type */ | |
OP_DESC, /* Descriptor byte */ | |
OP_BYTE, /* 8-bit signed value */ | |
OP_HALF, /* 16-bit signed value */ | |
OP_COPR /* Coprocessor instruction */ | |
} op_mode; | |
/* Describes a mnemonic */ | |
typedef struct _mnemonic { | |
uint16 opcode; | |
int8 op_count; /* Number of operands */ | |
op_mode mode; /* Dispatch mode */ | |
int8 dtype; /* Default data type */ | |
char mnemonic[8]; | |
int8 src_op1; | |
int8 src_op2; | |
int8 src_op3; | |
int8 dst_op; | |
} mnemonic; | |
/* | |
* Structure that describes each operand in a decoded instruction | |
*/ | |
typedef struct _operand { | |
uint8 mode; /* Embedded data addressing mode */ | |
uint8 reg; /* Operand register (0-15) */ | |
int8 dtype; /* Default type for the operand */ | |
int8 etype; /* Expanded type (-1 if none) */ | |
union { | |
uint32 w; | |
uint16 h; | |
uint8 b; | |
} embedded; /* Data consumed as part of the instruction | |
stream, i.e. literals, displacement, | |
etc. */ | |
uint32 data; /* Data either read or written during | |
instruction execution */ | |
} operand; | |
/* | |
* An inst is a combination of a decoded instruction and | |
* 0 to 4 operands. Also used for history record keeping. | |
*/ | |
typedef struct _instr { | |
mnemonic *mn; | |
uint32 psw; | |
uint32 sp; | |
uint32 pc; | |
t_bool valid; | |
operand operands[4]; | |
} instr; | |
/* Function prototypes */ | |
t_stat cpu_svc(UNIT *uptr); | |
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_set_size(UNIT *uptr, int32 val, CONST char *cptr, void *desc); | |
t_stat cpu_set_hist(UNIT *uptr, int32 val, CONST char *cptr, void *desc); | |
t_stat cpu_show_hist(FILE *st, UNIT *uptr, int32 val, CONST void *desc); | |
t_stat cpu_set_halt(UNIT *uptr, int32 val, char *cptr, void *desc); | |
t_stat cpu_clear_halt(UNIT *uptr, int32 val, char *cptr, void *desc); | |
t_stat cpu_boot(int32 unit_num, DEVICE *dptr); | |
t_bool cpu_is_pc_a_subroutine_call (t_addr **ret_addrs); | |
void cpu_register_name(uint8 reg, char *buf, size_t len); | |
void cpu_show_operand(FILE *st, operand *op); | |
void fprint_sym_hist(FILE *st, instr *ip); | |
t_stat fprint_sym_m(FILE *of, t_addr addr, t_value *val); | |
instr *cpu_next_instruction(void); | |
uint8 decode_instruction(instr *instr); | |
t_bool cpu_on_interrupt(uint8 ipl); | |
static uint32 cpu_effective_address(operand * op); | |
static uint32 cpu_read_op(operand * op); | |
static void cpu_write_op(operand * op, t_uint64 val); | |
static void cpu_set_nz_flags(t_uint64 data, operand * op); | |
static SIM_INLINE uint8 cpu_ipl(); | |
static SIM_INLINE void cpu_on_normal_exception(uint8 isc); | |
static SIM_INLINE void cpu_on_stack_exception(uint8 isc); | |
static SIM_INLINE void cpu_on_process_exception(uint8 isc); | |
static SIM_INLINE void cpu_on_reset_exception(uint8 isc); | |
static SIM_INLINE void cpu_perform_gate(uint32 index1, uint32 index2); | |
static SIM_INLINE t_bool mem_exception(); | |
static SIM_INLINE void clear_exceptions(); | |
static SIM_INLINE void set_psw_tm(t_bool val); | |
static SIM_INLINE void clear_instruction(instr *inst); | |
static SIM_INLINE int8 op_type(operand *op); | |
static SIM_INLINE t_bool op_signed(operand *op); | |
static SIM_INLINE t_bool is_byte_immediate(operand * oper); | |
static SIM_INLINE t_bool is_halfword_immediate(operand * oper); | |
static SIM_INLINE t_bool is_word_immediate(operand * oper); | |
static SIM_INLINE t_bool is_positive_literal(operand * oper); | |
static SIM_INLINE t_bool is_negative_literal(operand * oper); | |
static SIM_INLINE t_bool invalid_destination(operand * oper); | |
static SIM_INLINE uint32 sign_extend_n(uint8 val); | |
static SIM_INLINE uint32 sign_extend_b(uint8 val); | |
static SIM_INLINE uint32 zero_extend_b(uint8 val); | |
static SIM_INLINE uint32 sign_extend_h(uint16 val); | |
static SIM_INLINE uint32 zero_extend_h(uint16 val); | |
static SIM_INLINE t_bool cpu_z_flag(); | |
static SIM_INLINE t_bool cpu_n_flag(); | |
static SIM_INLINE t_bool cpu_c_flag(); | |
static SIM_INLINE t_bool cpu_v_flag(); | |
static SIM_INLINE void cpu_set_z_flag(t_bool val); | |
static SIM_INLINE void cpu_set_n_flag(t_bool val); | |
static SIM_INLINE void cpu_set_c_flag(t_bool val); | |
static SIM_INLINE void cpu_set_v_flag(t_bool val); | |
static SIM_INLINE void cpu_set_v_flag_op(t_uint64 val, operand *op); | |
static SIM_INLINE uint8 cpu_execution_level(); | |
static SIM_INLINE void cpu_set_execution_level(uint8 level); | |
static SIM_INLINE void cpu_push_word(uint32 val); | |
static SIM_INLINE uint32 cpu_pop_word(); | |
static SIM_INLINE void irq_push_word(uint32 val); | |
static SIM_INLINE uint32 irq_pop_word(); | |
static SIM_INLINE void cpu_context_switch_1(uint32 pcbp); | |
static SIM_INLINE void cpu_context_switch_2(uint32 pcbp); | |
static SIM_INLINE void cpu_context_switch_3(uint32 pcbp); | |
static SIM_INLINE t_bool op_is_psw(operand *op); | |
static SIM_INLINE t_bool op_is_sp(operand *op); | |
static SIM_INLINE uint32 cpu_next_pc(); | |
static SIM_INLINE void add(t_uint64 a, t_uint64 b, operand *dst); | |
static SIM_INLINE void sub(t_uint64 a, t_uint64 b, operand *dst); | |
/* Helper macros */ | |
#define MOD(A,B,OP1,OP2,SZ) { \ | |
if (op_signed(OP1) && !op_signed(OP2)) { \ | |
result = (SZ)(B) % (A); \ | |
} else if (!op_signed(OP1) && op_signed(OP2)) { \ | |
result = (B) % (SZ)(A); \ | |
} else if (op_signed(OP1) && op_signed(OP2)) { \ | |
result = (SZ)(B) % (SZ)(A); \ | |
} else { \ | |
result = (B) % (A); \ | |
} \ | |
} | |
#define DIV(A,B,OP1,OP2,SZ) { \ | |
if (op_signed(OP1) && !op_signed(OP2)) { \ | |
result = (SZ)(B) / (A); \ | |
} else if (!op_signed(OP1) && op_signed(OP2)) { \ | |
result = (B) / (SZ)(A); \ | |
} else if (op_signed(OP1) && op_signed(OP2)) { \ | |
result = (SZ)(B) / (SZ)(A); \ | |
} else { \ | |
result = (B) / (A); \ | |
} \ | |
} | |
#define OP_R_W(d,a,p) { \ | |
(d) = (uint32) (a)[(p)++]; \ | |
(d) |= (uint32) (a)[(p)++] << 8u; \ | |
(d) |= (uint32) (a)[(p)++] << 16u; \ | |
(d) |= (uint32) (a)[(p)++] << 24u; \ | |
} | |
#define OP_R_H(d,a,p) { \ | |
(d) = (uint16) (a)[(p)++]; \ | |
(d) |= (uint16) (a)[(p)++] << 8u; \ | |
} | |
#define OP_R_B(d,a,p) { \ | |
(d) = (uint8) (a)[(p)++]; \ | |
} | |
#endif |