dmd_core/src/cpu.rs

3289 lines
119 KiB
Rust

#![allow(clippy::unreadable_literal)]
use crate::bus::{AccessCode, Bus};
use crate::err::*;
use crate::instr::*;
use log::trace;
use std::fmt;
///
/// PSW Flags and Offsets
///
const F_ET: u32 = 0x00000003;
const F_TM: u32 = 0x00000004;
const F_ISC: u32 = 0x00000078;
const F_I: u32 = 0x00000080;
const F_R: u32 = 0x00000100;
const F_PM: u32 = 0x00000600;
const F_CM: u32 = 0x00001800;
const F_IPL: u32 = 0x0001e000;
const F_C: u32 = 0x00040000;
const F_V: u32 = 0x00080000;
const F_Z: u32 = 0x00100000;
const F_N: u32 = 0x00200000;
const F_CD: u32 = 0x00800000;
const F_QIE: u32 = 0x01000000;
const F_CFD: u32 = 0x02000000;
const O_ET: u32 = 0;
const O_TM: u32 = 2;
const O_ISC: u32 = 3;
///
/// Register Indexes
///
const R_FP: usize = 9;
const R_AP: usize = 10;
const R_PSW: usize = 11;
const R_SP: usize = 12;
const R_PCBP: usize = 13;
const R_ISP: usize = 14;
const R_PC: usize = 15;
const IPL_TABLE: [u32; 64] = [
0, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
];
const WE32100_VERSION: u32 = 0x1a;
const HALFWORD_MNEMONIC_COUNT: usize = 11;
pub enum ExceptionType {
ExternalMemory,
}
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub enum AddrMode {
None,
Absolute,
AbsoluteDeferred,
ByteDisplacement,
ByteDisplacementDeferred,
HalfwordDisplacement,
HalfwordDisplacementDeferred,
WordDisplacement,
WordDisplacementDeferred,
ApShortOffset,
FpShortOffset,
ByteImmediate,
HalfwordImmediate,
WordImmediate,
PositiveLiteral,
NegativeLiteral,
Register,
RegisterDeferred,
Expanded,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum OpType {
Lit,
Src,
Dest,
None,
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Data {
None,
Byte,
Half,
Word,
SByte,
UHalf,
UWord,
}
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum CpuLevel {
User,
Supervisor,
Executive,
Kernel,
}
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum ErrorContext {
None,
NormalGateVector,
ProcessGatePcb,
ProcessOldPcb,
ProcessNewPcb,
ResteGateVector,
ResetSystemData,
ResetIntStack,
StackFault,
}
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub struct Operand {
pub size: u8,
pub mode: AddrMode,
data_type: Data,
expanded_type: Option<Data>,
pub register: Option<usize>,
pub embedded: u32,
pub data: u32, // Data moved to / from operand
pub eff: u32, // Effective address (if any)
}
impl Operand {
fn new(
size: u8,
mode: AddrMode,
data_type: Data,
expanded_type: Option<Data>,
register: Option<usize>,
embedded: u32,
) -> Operand {
Operand {
size,
mode,
data_type,
expanded_type,
register,
embedded,
data: 0,
eff: 0,
}
}
fn data_type(&self) -> Data {
match self.expanded_type {
Some(t) => t,
None => self.data_type,
}
}
fn clear(&mut self) {
self.size = 0;
self.mode = AddrMode::None;
self.data_type = Data::None;
self.expanded_type = None;
self.register = None;
self.embedded = 0;
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct Mnemonic {
opcode: u16,
dtype: Data,
name: &'static str,
ops: [OpType; 4],
}
#[derive(Debug, Eq, PartialEq)]
pub struct Instruction {
pub opcode: u16,
pub name: &'static str,
pub data_type: Data,
pub len: u8,
/// The raw data decoded from the byte stream
pub data: [u8; 32],
pub operands: [Operand; 4],
}
impl fmt::Display for Operand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.mode {
AddrMode::None => Ok(()),
AddrMode::Absolute => {
write!(f, "0x{:x}", self.embedded)
}
AddrMode::AbsoluteDeferred => write!(f, "*$0x{:x}", self.embedded),
AddrMode::ByteDisplacement => {
write!(f, "{}(%r{})", self.embedded as i8, self.register.unwrap())
}
AddrMode::ByteDisplacementDeferred => {
write!(f, "*{}(%r{})", self.embedded as i8, self.register.unwrap())
}
AddrMode::HalfwordDisplacement => {
write!(f, "0x{:x}(%r{})", self.embedded as i16, self.register.unwrap())
}
AddrMode::HalfwordDisplacementDeferred => {
write!(f, "*0x{:x}(%r{})", self.embedded as i16, self.register.unwrap())
}
AddrMode::WordDisplacement => {
write!(f, "0x{:x}(%r{})", self.embedded, self.register.unwrap())
}
AddrMode::WordDisplacementDeferred => {
write!(f, "*0x{:x}(%r{})", self.embedded, self.register.unwrap())
}
AddrMode::ApShortOffset => {
write!(f, "{}(%ap)", self.embedded as i8)
}
AddrMode::FpShortOffset => {
write!(f, "{}(%fp)", self.embedded as i8)
}
AddrMode::ByteImmediate => {
write!(f, "&{}", self.embedded as u8)
}
AddrMode::HalfwordImmediate => {
write!(f, "&0x{:x}", self.embedded as u16)
}
AddrMode::WordImmediate => {
write!(f, "&0x{:x}", self.embedded)
}
AddrMode::PositiveLiteral => {
write!(f, "$0x{:x}", self.embedded)
}
AddrMode::NegativeLiteral => {
write!(f, "-$0x{:x}", self.embedded)
}
AddrMode::Register => {
write!(f, "%r{}", self.register.unwrap())
}
AddrMode::RegisterDeferred => {
write!(f, "(%r{})", self.register.unwrap())
}
AddrMode::Expanded => {
write!(f, "{{???}}")
}
}
}
}
impl fmt::Display for Instruction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut i = 0;
loop {
if i < self.len {
write!(f, "{:02x} ", self.data[i as usize])?;
} else {
write!(f, " ")?;
}
i += 1;
if i > 16 {
break;
}
}
write!(f, "{:8} ", self.name)?;
for index in 0..=3 {
let op = &self.operands[index];
if op.mode == AddrMode::None {
break;
}
if index == 0 {
write!(f, "{}", op)?;
} else {
write!(f, ",{}", op)?;
}
}
Ok(())
}
}
macro_rules! mn {
($opcode:expr, $dtype:expr, $name:expr, $ops:expr) => {
Mnemonic {
opcode: $opcode,
dtype: $dtype,
name: $name,
ops: $ops,
}
};
}
fn sign_extend_halfword(data: u16) -> u32 {
i32::from(data as i16) as u32
}
fn sign_extend_byte(data: u8) -> u32 {
i32::from(data as i8) as u32
}
fn add_offset(val: u32, offset: u32) -> u32 {
((val as i32).wrapping_add(offset as i32)) as u32
}
static BYTE_MNEMONICS: [Option<Mnemonic>; 256] = [
Some(mn!(0x00, Data::None, "halt", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x02, Data::Word, "SPOPRD", [OpType::Lit, OpType::Src, OpType::None, OpType::None])),
Some(mn!(0x03, Data::Word, "SPOPRD2", [OpType::Lit, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0x04, Data::Word, "MOVAW", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0x06, Data::Word, "SPOPRT", [OpType::Lit, OpType::Src, OpType::None, OpType::None])),
Some(mn!(0x07, Data::Word, "SPOPT2", [OpType::Lit, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0x08, Data::None, "RET", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
None,
None,
Some(mn!(0x0C, Data::Word, "MOVTRW", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
None,
None,
Some(mn!(0x10, Data::Word, "SAVE", [OpType::Src, OpType::None, OpType::None, OpType::None])),
None,
None,
Some(mn!(0x13, Data::Word, "SPOPWD", [OpType::Lit, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x14, Data::Byte, "EXTOP", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
None,
Some(mn!(0x17, Data::Word, "SPOPWT", [OpType::Lit, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x18, Data::None, "RESTORE", [OpType::Src, OpType::None, OpType::None, OpType::None])),
None,
None,
None,
Some(mn!(0x1C, Data::Word, "SWAPWI", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x1E, Data::Half, "SWAPHI", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x1F, Data::Byte, "SWAPBI", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x20, Data::Word, "POPW", [OpType::Src, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x22, Data::Word, "SPOPRS", [OpType::Lit, OpType::Src, OpType::None, OpType::None])),
Some(mn!(0x23, Data::Word, "SPOPS2", [OpType::Lit, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0x24, Data::Word, "JMP", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
None,
None,
Some(mn!(0x27, Data::None, "CFLUSH", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x28, Data::Word, "TSTW", [OpType::Src, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x2A, Data::Half, "TSTH", [OpType::Src, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x2B, Data::Byte, "TSTB", [OpType::Src, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x2C, Data::Word, "CALL", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0x2E, Data::None, "BPT", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x2F, Data::None, "WAIT", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
None,
Some(mn!(0x32, Data::Word, "SPOP", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x33, Data::Word, "SPOPWS", [OpType::Lit, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x34, Data::Word, "JSB", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x36, Data::Half, "BSBH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x37, Data::Byte, "BSBB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x38, Data::Word, "BITW", [OpType::Src, OpType::Src, OpType::None, OpType::None])),
None,
Some(mn!(0x3A, Data::Half, "BITH", [OpType::Src, OpType::Src, OpType::None, OpType::None])),
Some(mn!(0x3B, Data::Byte, "BITB", [OpType::Src, OpType::Src, OpType::None, OpType::None])),
Some(mn!(0x3C, Data::Word, "CMPW", [OpType::Src, OpType::Src, OpType::None, OpType::None])),
None,
Some(mn!(0x3E, Data::Half, "CMPH", [OpType::Src, OpType::Src, OpType::None, OpType::None])),
Some(mn!(0x3F, Data::Byte, "CMPB", [OpType::Src, OpType::Src, OpType::None, OpType::None])),
Some(mn!(0x40, Data::None, "RGEQ", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x42, Data::Half, "BGEH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x43, Data::Byte, "BGEB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x44, Data::None, "RGTR", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x46, Data::Half, "BGH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x47, Data::Byte, "BGB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x48, Data::None, "RLSS", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x4A, Data::Half, "BLH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x4B, Data::Byte, "BLB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x4C, Data::None, "RLEQ", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x4E, Data::Half, "BLEH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x4F, Data::Byte, "BLEB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x50, Data::None, "RGEQU", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x52, Data::Half, "BGEUH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x53, Data::Byte, "BGEUB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x54, Data::None, "RGTRU", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x56, Data::Half, "BGUH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x57, Data::Byte, "BGUB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x58, Data::None, "RLSSU", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x5A, Data::Half, "BLUH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x5B, Data::Byte, "BLUB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x5C, Data::None, "RLEQU", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x5E, Data::Half, "BLEUH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x5F, Data::Byte, "BLEUB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x60, Data::None, "RVC", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x62, Data::Half, "BVCH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x63, Data::Byte, "BVCB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x64, Data::None, "RNEQU", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x66, Data::Half, "BNEH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x67, Data::Byte, "BNEB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x68, Data::None, "RVS", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x6A, Data::Half, "BVSH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x6B, Data::Byte, "BVSB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x6C, Data::None, "REQLU", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x6E, Data::Half, "BEH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x6F, Data::Byte, "BEB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x70, Data::None, "NOP", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x72, Data::None, "NOP3", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x73, Data::None, "NOP2", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x74, Data::None, "RNEQ", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x76, Data::Half, "BNEH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x77, Data::Byte, "BNEB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x78, Data::None, "RSB", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x7A, Data::Half, "BRH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x7B, Data::Byte, "BRB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x7C, Data::None, "REQL", [OpType::None, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x7E, Data::Half, "BEH", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x7F, Data::Byte, "BEB", [OpType::Lit, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x80, Data::Word, "CLRW", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x82, Data::Half, "CLRH", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x83, Data::Byte, "CLRB", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x84, Data::Word, "MOVW", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0x86, Data::Half, "MOVH", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x87, Data::Byte, "MOVB", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x88, Data::Word, "MCOMW", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0x8A, Data::Half, "MCOMH", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x8B, Data::Byte, "MCOMB", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x8C, Data::Word, "MNEGW", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0x8E, Data::Half, "MNEGH", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x8F, Data::Byte, "MNEGB", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x90, Data::Word, "INCW", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x92, Data::Half, "INCH", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x93, Data::Byte, "INCB", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x94, Data::Word, "DECW", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
None,
Some(mn!(0x96, Data::Half, "DECH", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x97, Data::Byte, "DECB", [OpType::Dest, OpType::None, OpType::None, OpType::None])),
None,
None,
None,
None,
Some(mn!(0x9C, Data::Word, "ADDW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0x9E, Data::Half, "ADDH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0x9F, Data::Byte, "ADDB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xA0, Data::Word, "PUSHW", [OpType::Src, OpType::None, OpType::None, OpType::None])),
None,
None,
None,
Some(mn!(0xA4, Data::Word, "MODW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0xA6, Data::Half, "MODH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xA7, Data::Byte, "MODB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xA8, Data::Word, "MULW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0xAA, Data::Half, "MULH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xAB, Data::Byte, "MULB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xAC, Data::Word, "DIVW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0xAE, Data::Half, "DIVH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xAF, Data::Byte, "DIVB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xB0, Data::Word, "ORW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0xB2, Data::Half, "ORH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xB3, Data::Byte, "ORB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xB4, Data::Word, "XORW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0xB6, Data::Half, "XORH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xB7, Data::Byte, "XORB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xB8, Data::Word, "ANDW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0xBA, Data::Half, "ANDH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xBB, Data::Byte, "ANDB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xBC, Data::Word, "SUBW2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
None,
Some(mn!(0xBE, Data::Half, "SUBH2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xBF, Data::Byte, "SUBB2", [OpType::Src, OpType::Dest, OpType::None, OpType::None])),
Some(mn!(0xC0, Data::Word, "ALSW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
None,
None,
Some(mn!(0xC4, Data::Word, "ARSW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xC6, Data::Half, "ARSH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xC7, Data::Byte, "ARSB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xC8, Data::Word, "INSFW", [OpType::Src, OpType::Src, OpType::Src, OpType::Dest])),
None,
Some(mn!(0xCA, Data::Half, "INSFH", [OpType::Src, OpType::Src, OpType::Src, OpType::Dest])),
Some(mn!(0xCB, Data::Byte, "INSFB", [OpType::Src, OpType::Src, OpType::Src, OpType::Dest])),
Some(mn!(0xCC, Data::Word, "EXTFW", [OpType::Src, OpType::Src, OpType::Src, OpType::Dest])),
None,
Some(mn!(0xCE, Data::Half, "EXTFH", [OpType::Src, OpType::Src, OpType::Src, OpType::Dest])),
Some(mn!(0xCF, Data::Byte, "EXTFB", [OpType::Src, OpType::Src, OpType::Src, OpType::Dest])),
Some(mn!(0xD0, Data::Word, "LLSW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xD2, Data::Half, "LLSH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xD3, Data::Byte, "LLSB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xD4, Data::Word, "LRSW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
None,
None,
Some(mn!(0xD8, Data::Word, "ROTW", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
None,
None,
Some(mn!(0xDC, Data::Word, "ADDW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xDE, Data::Half, "ADDH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xDF, Data::Byte, "ADDB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xE0, Data::Word, "PUSHAW", [OpType::Src, OpType::None, OpType::None, OpType::None])),
None,
None,
None,
Some(mn!(0xE4, Data::Word, "MODW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xE6, Data::Half, "MODH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xE7, Data::Byte, "MODB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xE8, Data::Word, "MULW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xEA, Data::Half, "MULH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xEB, Data::Byte, "MULB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xEC, Data::Word, "DIVW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xEE, Data::Half, "DIVH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xEF, Data::Byte, "DIVB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xF0, Data::Word, "ORW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xF2, Data::Half, "ORH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xF3, Data::Byte, "ORB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xF4, Data::Word, "XORW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xF6, Data::Half, "XORH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xF7, Data::Byte, "XORB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xF8, Data::Word, "ANDW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xFA, Data::Half, "ANDH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xFB, Data::Byte, "ANDB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xFC, Data::Word, "SUBW3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
None,
Some(mn!(0xFE, Data::Half, "SUBH3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
Some(mn!(0xFF, Data::Byte, "SUBB3", [OpType::Src, OpType::Src, OpType::Dest, OpType::None])),
];
static HALFWORD_MNEMONICS: [Option<Mnemonic>; HALFWORD_MNEMONIC_COUNT] = [
Some(mn!(
0x3009,
Data::None,
"MVERNO",
[OpType::None, OpType::None, OpType::None, OpType::None]
)),
Some(mn!(
0x300d,
Data::None,
"ENBVJMP",
[OpType::None, OpType::None, OpType::None, OpType::None]
)),
Some(mn!(
0x3013,
Data::None,
"DISVJMP",
[OpType::None, OpType::None, OpType::None, OpType::None]
)),
Some(mn!(
0x3019,
Data::None,
"MOVBLW",
[OpType::None, OpType::None, OpType::None, OpType::None]
)),
Some(mn!(
0x301f,
Data::None,
"STREND",
[OpType::None, OpType::None, OpType::None, OpType::None]
)),
Some(mn!(
0x302f,
Data::None,
"INTACK",
[OpType::None, OpType::None, OpType::None, OpType::None]
)),
Some(mn!(
0x303f,
Data::None,
"STRCPY",
[OpType::None, OpType::None, OpType::None, OpType::None]
)),
Some(mn!(0x3045, Data::None, "RETG", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(0x3061, Data::None, "GATE", [OpType::None, OpType::None, OpType::None, OpType::None])),
Some(mn!(
0x30ac,
Data::None,
"CALLPS",
[OpType::None, OpType::None, OpType::None, OpType::None]
)),
Some(mn!(
0x30c8,
Data::None,
"RETPS",
[OpType::None, OpType::None, OpType::None, OpType::None]
)),
];
static NULL_MNEMONIC: Option<Mnemonic> = None;
pub struct Cpu {
//
// Note that we store registers as an array of type u32 because
// we often need to reference registers by index (0-15) when decoding
// and executing instructions.
//
pub r: [u32; 16],
error_context: ErrorContext,
ir: Instruction,
}
impl Default for Cpu {
fn default() -> Self {
Cpu::new()
}
}
impl Cpu {
pub fn new() -> Cpu {
Cpu {
r: [0; 16],
error_context: ErrorContext::None,
ir: Instruction {
opcode: 0,
name: "???",
data_type: Data::None,
len: 0,
data: [0; 32],
operands: [
Operand::new(0, AddrMode::None, Data::None, None, None, 0),
Operand::new(0, AddrMode::None, Data::None, None, None, 0),
Operand::new(0, AddrMode::None, Data::None, None, None, 0),
Operand::new(0, AddrMode::None, Data::None, None, None, 0),
],
},
}
}
/// Reset the CPU.
pub fn reset(&mut self, bus: &mut Bus) -> Result<(), BusError> {
//
// The WE32100 Manual, Page 2-52, describes the reset process
//
// 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.
//
self.r[R_PCBP] = bus.read_word(0x80, AccessCode::AddressFetch)?;
self.r[R_PSW] = bus.read_word(self.r[R_PCBP] as usize, AccessCode::AddressFetch)?;
self.r[R_PC] = bus.read_word(self.r[R_PCBP] as usize + 4, AccessCode::AddressFetch)?;
self.r[R_SP] = bus.read_word(self.r[R_PCBP] as usize + 8, AccessCode::AddressFetch)?;
if self.r[R_PSW] & F_I != 0 {
self.r[R_PSW] &= !F_I;
self.r[R_PCBP] += 12;
}
self.set_isc(3); // Set ISC = 3
Ok(())
}
/// Compute the effective address for an Operand.
fn effective_address(&mut self, bus: &mut Bus, index: usize) -> Result<u32, CpuError> {
let embedded = self.ir.operands[index].embedded;
let mode = self.ir.operands[index].mode;
let register = self.ir.operands[index].register;
let addr: u32 = match mode {
AddrMode::RegisterDeferred => {
let r = match register {
Some(v) => v,
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
};
self.r[r]
}
AddrMode::Absolute => embedded,
AddrMode::AbsoluteDeferred => {
bus.read_word(embedded as usize, AccessCode::AddressFetch)?
}
AddrMode::FpShortOffset => add_offset(self.r[R_FP], sign_extend_byte(embedded as u8)),
AddrMode::ApShortOffset => add_offset(self.r[R_AP], sign_extend_byte(embedded as u8)),
AddrMode::WordDisplacement => {
let r = match register {
Some(v) => v,
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
};
add_offset(self.r[r], embedded)
}
AddrMode::WordDisplacementDeferred => {
let r = match register {
Some(v) => v,
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
};
bus.read_word((add_offset(self.r[r], embedded)) as usize, AccessCode::AddressFetch)?
}
AddrMode::HalfwordDisplacement => {
let r = match register {
Some(v) => v,
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
};
add_offset(self.r[r], sign_extend_halfword(embedded as u16))
}
AddrMode::HalfwordDisplacementDeferred => {
let r = match register {
Some(v) => v,
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
};
bus.read_word(
(add_offset(self.r[r], sign_extend_halfword(embedded as u16))) as usize,
AccessCode::AddressFetch,
)?
}
AddrMode::ByteDisplacement => {
let r = match register {
Some(v) => v,
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
};
add_offset(self.r[r], sign_extend_byte(embedded as u8))
}
AddrMode::ByteDisplacementDeferred => {
let r = match register {
Some(v) => v,
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
};
bus.read_word(
add_offset(self.r[r], sign_extend_byte(embedded as u8)) as usize,
AccessCode::AddressFetch,
)?
}
_ => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
};
self.ir.operands[index].data = addr;
Ok(addr)
}
/// Read the value pointed at by an Operand
pub fn read_op(&mut self, bus: &mut Bus, index: usize) -> Result<u32, CpuError> {
let mut op = self.ir.operands[index];
let val: u32 = match op.mode {
AddrMode::Register => {
let r = match op.register {
Some(v) => v,
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
};
match op.data_type() {
Data::Word | Data::UWord => self.r[r],
Data::Half => sign_extend_halfword(self.r[r] as u16),
Data::UHalf => u32::from(self.r[r] as u16),
Data::Byte => u32::from(self.r[r] as u8),
Data::SByte => sign_extend_byte(self.r[r] as u8),
_ => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
}
}
AddrMode::PositiveLiteral | AddrMode::NegativeLiteral => {
sign_extend_byte(op.embedded as u8)
}
AddrMode::WordImmediate => op.embedded,
AddrMode::HalfwordImmediate => sign_extend_halfword(op.embedded as u16),
AddrMode::ByteImmediate => sign_extend_byte(op.embedded as u8),
_ => {
let eff = self.effective_address(bus, index)?;
op.eff = eff;
match op.data_type() {
Data::UWord | Data::Word => {
bus.read_word(eff as usize, AccessCode::InstrFetch)?
}
Data::Half => {
sign_extend_halfword(bus.read_half(eff as usize, AccessCode::InstrFetch)?)
}
Data::UHalf => u32::from(bus.read_half(eff as usize, AccessCode::InstrFetch)?),
Data::Byte => u32::from(bus.read_byte(eff as usize, AccessCode::InstrFetch)?),
Data::SByte => {
sign_extend_byte(bus.read_byte(eff as usize, AccessCode::InstrFetch)?)
}
_ => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
}
}
};
op.data = val;
Ok(val)
}
/// Write a value to the location specified by an Operand
pub fn write_op(&mut self, bus: &mut Bus, index: usize, val: u32) -> Result<(), CpuError> {
let mode = self.ir.operands[index].mode;
let register = self.ir.operands[index].register;
let data_type = self.ir.operands[index].data_type();
self.ir.operands[index].data = val;
match mode {
AddrMode::Register => match register {
Some(r) => self.r[r] = val,
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
},
AddrMode::NegativeLiteral
| AddrMode::PositiveLiteral
| AddrMode::ByteImmediate
| AddrMode::HalfwordImmediate
| AddrMode::WordImmediate => {
return Err(CpuError::Exception(CpuException::IllegalOpcode));
}
_ => {
let eff = self.effective_address(bus, index)?;
self.ir.operands[index].eff = eff;
match data_type {
Data::UWord | Data::Word => bus.write_word(eff as usize, val)?,
Data::Half | Data::UHalf => bus.write_half(eff as usize, val as u16)?,
Data::Byte | Data::SByte => bus.write_byte(eff as usize, val as u8)?,
_ => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
}
}
};
Ok(())
}
fn context_switch_1(&mut self, bus: &mut Bus, new_pcbp: u32) -> Result<(), CpuError> {
// Save the current PC in the PCB
bus.write_word((self.r[R_PCBP] + 4) as usize, self.r[R_PC])?;
// Copy the 'R' flag from the new PSW to the old PSW
self.r[R_PSW] &= !F_R;
self.r[R_PSW] |= bus.read_word(new_pcbp as usize, AccessCode::AddressFetch)? & F_R;
// Save the current PSW and SP in the old PCB
bus.write_word(self.r[R_PCBP] as usize, self.r[R_PSW])?;
bus.write_word((self.r[R_PCBP] + 8) as usize, self.r[R_SP])?;
// If R is set, save the current R0-R8,FP,AP in the PCB
if (self.r[R_PSW] & F_R) != 0 {
bus.write_word((self.r[R_PCBP] + 24) as usize, self.r[R_FP])?;
bus.write_word((self.r[R_PCBP] + 28) as usize, self.r[0])?;
bus.write_word((self.r[R_PCBP] + 32) as usize, self.r[1])?;
bus.write_word((self.r[R_PCBP] + 36) as usize, self.r[2])?;
bus.write_word((self.r[R_PCBP] + 40) as usize, self.r[3])?;
bus.write_word((self.r[R_PCBP] + 44) as usize, self.r[4])?;
bus.write_word((self.r[R_PCBP] + 48) as usize, self.r[5])?;
bus.write_word((self.r[R_PCBP] + 52) as usize, self.r[6])?;
bus.write_word((self.r[R_PCBP] + 56) as usize, self.r[7])?;
bus.write_word((self.r[R_PCBP] + 60) as usize, self.r[8])?;
bus.write_word((self.r[R_PCBP] + 20) as usize, self.r[R_AP])?;
self.r[R_FP] = self.r[R_PCBP] + 52;
}
Ok(())
}
fn context_switch_2(&mut self, bus: &mut Bus, new_pcbp: u32) -> Result<(), CpuError> {
self.r[R_PCBP] = new_pcbp;
// Put new PSW, PC, and SP values from PCB into registers
self.r[R_PSW] = bus.read_word(self.r[R_PCBP] as usize, AccessCode::AddressFetch)?;
self.r[R_PSW] &= !F_TM;
self.r[R_PC] = bus.read_word((self.r[R_PCBP] + 4) as usize, AccessCode::AddressFetch)?;
self.r[R_SP] = bus.read_word((self.r[R_PCBP] + 8) as usize, AccessCode::AddressFetch)?;
// If the I-bit is set, increment the PCBP past initial context area
if (self.r[R_PSW] & F_I) != 0 {
self.r[R_PSW] &= !F_I;
self.r[R_PCBP] += 12;
}
Ok(())
}
fn context_switch_3(&mut self, bus: &mut Bus) -> Result<(), CpuError> {
if (self.r[R_PSW] & F_R) != 0 {
self.r[0] = self.r[R_PCBP] + 64;
self.r[2] = bus.read_word(self.r[0] as usize, AccessCode::AddressFetch)?;
self.r[0] += 4;
while self.r[2] != 0 {
self.r[1] = bus.read_word(self.r[0] as usize, AccessCode::AddressFetch)?;
self.r[0] += 4;
// Execute MOVBLW instruction inside this loop
while self.r[2] != 0 {
let tmp = bus.read_word(self.r[0] as usize, AccessCode::AddressFetch)?;
bus.write_word(self.r[1] as usize, tmp)?;
self.r[2] -= 1;
self.r[0] += 4;
self.r[1] += 4;
}
self.r[2] = bus.read_word(self.r[0] as usize, AccessCode::AddressFetch)?;
self.r[0] += 4;
}
self.r[0] += 4;
}
Ok(())
}
fn add(&mut self, bus: &mut Bus, a: u32, b: u32, dst: usize) -> Result<(), CpuError> {
let result: u64 = u64::from(a).wrapping_add(u64::from(b));
self.write_op(bus, dst, result as u32)?;
self.set_nz_flags(result as u32, dst);
let data_type = self.ir.operands[dst].data_type();
match data_type {
Data::Word | Data::UWord => {
self.set_c_flag(result > 0xffffffff);
self.set_v_flag((((a ^ !b) & (a ^ result as u32)) & 0x80000000) != 0);
}
Data::Half | Data::UHalf => {
self.set_c_flag(result > 0xffff);
self.set_v_flag((((a ^ !b) & (a ^ result as u32)) & 0x8000) != 0);
}
Data::Byte | Data::SByte => {
self.set_c_flag(result > 0xff);
self.set_v_flag((((a ^ !b) & (a ^ result as u32)) & 0x80) != 0);
}
_ => {
return Err(CpuError::Exception(CpuException::IllegalOpcode));
}
}
Ok(())
}
fn sub(&mut self, bus: &mut Bus, a: u32, b: u32, dst: usize) -> Result<(), CpuError> {
let result: u64 = u64::from(a).wrapping_sub(u64::from(b));
self.write_op(bus, dst, result as u32)?;
self.set_nz_flags(result as u32, dst);
self.set_c_flag(b > a);
self.set_v_flag_op(result as u32, dst);
Ok(())
}
fn div(&mut self, a: u32, b: u32, _src: usize, dst: usize) -> u32 {
match self.ir.operands[dst].data_type {
Data::Word => (b as i32 / a as i32) as u32,
Data::Half => (b as i16 / a as i16) as u32,
Data::SByte => (b as i8 / a as i8) as u32,
Data::UWord => b / a,
Data::UHalf => u32::from(b as u16 / a as u16),
Data::Byte => u32::from(b as u8 / a as u8),
_ => b / a,
}
}
fn modulo(&mut self, a: u32, b: u32, _src: usize, dst: usize) -> u32 {
match self.ir.operands[dst].data_type {
Data::Word => (b as i32 % a as i32) as u32,
Data::Half => (b as i16 % a as i16) as u32,
Data::SByte => (b as i8 % a as i8) as u32,
Data::UWord => b % a,
Data::UHalf => u32::from(b as u16 % a as u16),
Data::Byte => u32::from(b as u8 % a as u8),
_ => b % a,
}
}
// TODO: Remove unwraps
fn on_interrupt(&mut self, bus: &mut Bus, vector: u8) {
let new_pcbp = bus
.read_word((0x8c + (4 * u32::from(vector))) as usize, AccessCode::AddressFetch)
.unwrap();
self.irq_push(bus, self.r[R_PCBP]).unwrap();
self.r[R_PSW] &= !(F_ISC | F_TM | F_ET);
self.r[R_PSW] |= 1;
self.context_switch_1(bus, new_pcbp).unwrap();
self.context_switch_2(bus, new_pcbp).unwrap();
self.r[R_PSW] &= !(F_ISC | F_TM | F_ET);
self.r[R_PSW] |= 7 << 3;
self.r[R_PSW] |= 3;
self.context_switch_3(bus).unwrap();
}
#[allow(clippy::cognitive_complexity)]
fn dispatch(&mut self, bus: &mut Bus) -> Result<i32, CpuError> {
// Update anything that needs updating.
bus.service();
if let Some(val) = bus.get_interrupts() {
let cpu_ipl = (self.r[R_PSW]) >> 13 & 0xf;
if cpu_ipl < IPL_TABLE[(val & 0x3f) as usize] {
trace!(
"[PC={:08x} PSW={:08x}] INTERRUPT 0x{:04x}",
&self.r[R_PC],
&self.r[R_PSW],
(!val) & 0x3f
);
self.on_interrupt(bus, (!val) & 0x3f);
}
}
self.decode_instruction(bus)?;
let mut pc_increment: i32 = i32::from(self.ir.len);
match self.ir.opcode {
NOP => {
pc_increment = 1;
}
NOP2 => {
pc_increment = 2;
}
NOP3 => {
pc_increment = 3;
}
ADDW2 | ADDH2 | ADDB2 => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
self.add(bus, a, b, 1)?;
}
ADDW3 | ADDH3 | ADDB3 => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
self.add(bus, a, b, 2)?
}
ALSW3 => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
let result = u64::from(b) << (a & 0x1f);
self.write_op(bus, 2, result as u32)?;
self.set_nz_flags(result as u32, 2);
self.set_c_flag(false);
self.set_v_flag_op(result as u32, 2);
}
ANDW2 | ANDH2 | ANDB2 => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
let result = a & b;
self.write_op(bus, 1, result)?;
self.set_nz_flags(result, 1);
self.set_c_flag(false);
self.set_v_flag_op(result, 1);
}
ANDW3 | ANDH3 | ANDB3 => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
let result = a & b;
self.write_op(bus, 2, result)?;
self.set_nz_flags(result, 2);
self.set_c_flag(false);
self.set_v_flag_op(result, 2);
}
BEH | BEH_D => {
if self.z_flag() {
pc_increment = sign_extend_halfword(self.ir.operands[0].embedded as u16) as i32;
}
}
BEB | BEB_D => {
if self.z_flag() {
pc_increment = sign_extend_byte(self.ir.operands[0].embedded as u8) as i32;
}
}
BGH => {
if !(self.n_flag() || self.z_flag()) {
pc_increment = sign_extend_halfword(self.ir.operands[0].embedded as u16) as i32;
}
}
BGB => {
if !(self.n_flag() || self.z_flag()) {
pc_increment = sign_extend_byte(self.ir.operands[0].embedded as u8) as i32;
}
}
BGEH => {
if !self.n_flag() || self.z_flag() {
pc_increment = sign_extend_halfword(self.ir.operands[0].embedded as u16) as i32;
}
}
BGEB => {
if !self.n_flag() || self.z_flag() {
pc_increment = sign_extend_byte(self.ir.operands[0].embedded as u8) as i32;
}
}
BGEUH => {
if !self.c_flag() {
pc_increment = sign_extend_halfword(self.ir.operands[0].embedded as u16) as i32;
}
}
BGEUB => {
if !self.c_flag() {
pc_increment = sign_extend_byte(self.ir.operands[0].embedded as u8) as i32;
}
}
BGUH => {
if !(self.c_flag() || self.z_flag()) {
pc_increment = sign_extend_halfword(self.ir.operands[0].embedded as u16) as i32;
}
}
BGUB => {
if !(self.c_flag() || self.z_flag()) {
pc_increment = sign_extend_byte(self.ir.operands[0].embedded as u8) as i32;
}
}
BITW | BITH | BITB => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
let result = a & b;
self.set_nz_flags(result, 1);
self.set_c_flag(false);
self.set_v_flag(false);
}
BLH => {
if self.n_flag() && !self.z_flag() {
pc_increment = sign_extend_halfword(self.ir.operands[0].embedded as u16) as i32;
}
}
BLB => {
if self.n_flag() && !self.z_flag() {
pc_increment = sign_extend_byte(self.ir.operands[0].embedded as u8) as i32;
}
}
BLEH => {
if self.n_flag() || self.z_flag() {
pc_increment = sign_extend_halfword(self.ir.operands[0].embedded as u16) as i32;
}
}
BLEB => {
if self.n_flag() || self.z_flag() {
pc_increment = sign_extend_byte(self.ir.operands[0].embedded as u8) as i32;
}
}
BLEUH => {
if self.c_flag() || self.z_flag() {
pc_increment = sign_extend_halfword(self.ir.operands[0].embedded as u16) as i32;
}
}
BLEUB => {
if self.c_flag() || self.z_flag() {
pc_increment = sign_extend_byte(self.ir.operands[0].embedded as u8) as i32;
}
}
BLUH => {
if self.c_flag() {
pc_increment = sign_extend_halfword(self.ir.operands[0].embedded as u16) as i32;
}
}
BLUB => {
if self.c_flag() {
pc_increment = sign_extend_byte(self.ir.operands[0].embedded as u8) as i32;
}
}
BNEH | BNEH_D => {
if !self.z_flag() {
pc_increment = sign_extend_halfword(self.ir.operands[0].embedded as u16) as i32;
}
}
BNEB | BNEB_D => {
if !self.z_flag() {
pc_increment = sign_extend_byte(self.ir.operands[0].embedded as u8) as i32;
}
}
BPT | HALT => {
// TODO: Breakpoint Trap
unimplemented!()
}
BRH => {
pc_increment = sign_extend_halfword(self.ir.operands[0].embedded as u16) as i32;
}
BRB => {
pc_increment = sign_extend_byte(self.ir.operands[0].embedded as u8) as i32;
}
BSBH => {
let offset = sign_extend_halfword(self.ir.operands[0].embedded as u16) as i32;
let return_pc = (self.r[R_PC] as i32 + pc_increment) as u32;
self.stack_push(bus, return_pc)?;
pc_increment = offset;
}
BSBB => {
let offset = sign_extend_byte(self.ir.operands[0].embedded as u8) as i32;
let return_pc = (self.r[R_PC] as i32 + pc_increment) as u32;
self.stack_push(bus, return_pc)?;
pc_increment = offset;
}
BVCH => {
if !self.v_flag() {
pc_increment = sign_extend_halfword(self.ir.operands[0].embedded as u16) as i32;
}
}
BVCB => {
if !self.v_flag() {
pc_increment = sign_extend_byte(self.ir.operands[0].embedded as u8) as i32;
}
}
BVSH => {
if self.v_flag() {
pc_increment = sign_extend_halfword(self.ir.operands[0].embedded as u16) as i32;
}
}
BVSB => {
if self.v_flag() {
pc_increment = sign_extend_byte(self.ir.operands[0].embedded as u8) as i32;
}
}
CALL => {
let a = self.effective_address(bus, 0)?;
let b = self.effective_address(bus, 1)?;
let return_pc = (self.r[R_PC] as i32 + pc_increment) as u32;
bus.write_word((self.r[R_SP] + 4) as usize, self.r[R_AP])?;
bus.write_word(self.r[R_SP] as usize, return_pc)?;
self.r[R_SP] += 8;
self.r[R_PC] = b;
self.r[R_AP] = a;
pc_increment = 0;
}
CFLUSH => {}
CALLPS => {
match self.priv_level() {
CpuLevel::Kernel => {
let a = self.r[0];
self.error_context = ErrorContext::ResetIntStack;
self.irq_push(bus, self.r[R_PCBP])?;
// Set the current PC to the start of the next instruction
// (always PC + 2)
pc_increment = 0;
self.r[R_PC] += 2;
// Set old PSW ISC, TM, and ET to 0, 0, 1
self.r[R_PSW] &= !(F_ISC | F_TM | F_ET);
self.r[R_PSW] |= 1 << O_ET;
self.context_switch_1(bus, a)?;
self.context_switch_2(bus, a)?;
self.r[R_PSW] &= !(F_ISC | F_TM | F_ET);
self.r[R_PSW] |= 7 << O_ISC;
self.r[R_PSW] |= 3 << O_ET;
self.context_switch_3(bus)?;
self.error_context = ErrorContext::None;
}
_ => return Err(CpuError::Exception(CpuException::PrivilegedOpcode)),
}
}
CLRW | CLRH | CLRB => {
self.write_op(bus, 0, 0)?;
self.set_n_flag(false);
self.set_z_flag(true);
self.set_c_flag(false);
self.set_v_flag(false);
}
CMPW => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
self.set_z_flag(b == a);
self.set_n_flag((b as i32) < (a as i32));
self.set_c_flag(b < a);
self.set_v_flag(false);
}
CMPH => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
self.set_z_flag((b as u16) == (a as u16));
self.set_n_flag((b as i16) < (a as i16));
self.set_c_flag((b as u16) < (a as u16));
self.set_v_flag(false);
}
CMPB => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
self.set_z_flag((b as u8) == (a as u8));
self.set_n_flag((b as i8) < (a as i8));
self.set_c_flag((b as u8) < (a as u8));
self.set_v_flag(false);
}
DECW | DECH | DECB => {
let dst = 0;
let a = self.read_op(bus, dst)?;
self.sub(bus, a, 1, dst)?;
}
DIVW2 => {
// TODO: Division needs to be revisited.
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
if a == 0 {
return Err(CpuError::Exception(CpuException::IntegerZeroDivide));
}
if a == 0xffffffff && b == 0x80000000 {
self.set_v_flag(true);
}
let result = self.div(a, b, 0, 1);
self.write_op(bus, 1, result)?;
self.set_nz_flags(result, 1);
self.set_c_flag(false);
}
DIVH2 => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
if a == 0 {
return Err(CpuError::Exception(CpuException::IntegerZeroDivide));
}
if a == 0xffff && b == 0x8000 {
self.set_v_flag(true);
}
let result = self.div(a, b, 0, 1);
self.write_op(bus, 1, result)?;
self.set_nz_flags(result, 1);
self.set_c_flag(false);
}
DIVB2 => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
if a == 0 {
return Err(CpuError::Exception(CpuException::IntegerZeroDivide));
}
if a == 0xff && b == 0x80 {
self.set_v_flag(true);
}
let result = self.div(a, b, 0, 1);
self.write_op(bus, 1, result)?;
self.set_nz_flags(result, 1);
self.set_c_flag(false);
}
DIVW3 => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
if a == 0 {
return Err(CpuError::Exception(CpuException::IntegerZeroDivide));
}
if a == 0xffffffff && b == 0x80000000 {
self.set_v_flag(true);
}
let result = self.div(a, b, 0, 1);
self.write_op(bus, 2, result)?;
self.set_nz_flags(result, 2);
self.set_c_flag(false);
}
DIVH3 => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
if a == 0 {
return Err(CpuError::Exception(CpuException::IntegerZeroDivide));
}
if a == 0xffff && b == 0x8000 {
self.set_v_flag(true);
}
let result = self.div(a, b, 0, 1);
self.write_op(bus, 2, result)?;
self.set_nz_flags(result, 2);
self.set_c_flag(false);
}
DIVB3 => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
if a == 0 {
return Err(CpuError::Exception(CpuException::IntegerZeroDivide));
}
if a == 0xff && b == 0x80 {
self.set_v_flag(true);
}
let result = self.div(a, b, 0, 1);
self.write_op(bus, 2, result)?;
self.set_nz_flags(result, 2);
self.set_c_flag(false);
}
MVERNO => {
self.r[0] = WE32100_VERSION;
}
ENBVJMP => {
match self.priv_level() {
CpuLevel::Kernel => {
// TODO: Enable MMU, if present
self.r[R_PC] = self.r[0];
pc_increment = 0;
}
_ => {
return Err(CpuError::Exception(CpuException::PrivilegedOpcode));
}
}
}
DISVJMP => {
match self.priv_level() {
CpuLevel::Kernel => {
// TODO: Disable MMU, if present
self.r[R_PC] = self.r[0];
pc_increment = 0;
}
_ => {
return Err(CpuError::Exception(CpuException::PrivilegedOpcode));
}
}
}
EXTFW | EXTFH | EXTFB => {
let width = (self.read_op(bus, 0)? & 0x1f) + 1;
let offset = self.read_op(bus, 1)? & 0x1f;
let mut mask = if width >= 32 {
0xffffffff
} else {
(1 << width) - 1
};
mask <<= offset;
if width + offset > 32 {
mask |= (1 << ((width + offset) - 32)) - 1;
}
let mut a = self.read_op(bus, 2)?;
a &= mask;
a >>= offset;
self.write_op(bus, 3, a)?;
self.set_nz_flags(a, 3);
self.set_c_flag(false);
self.set_v_flag_op(a, 3);
}
INCW | INCH | INCB => {
let a = self.read_op(bus, 0)?;
self.add(bus, a, 1, 0)?;
}
INSFW | INSFH | INSFB => {
let width = (self.read_op(bus, 0)? & 0x1f) + 1;
let offset = self.read_op(bus, 1)? & 0x1f;
let mask = if width >= 32 {
0xffffffff
} else {
(1 << width) - 1
};
let a = self.read_op(bus, 2)? & mask;
let mut b = self.read_op(bus, 3)?;
b &= !(mask << offset);
b |= a << offset;
self.write_op(bus, 3, b)?;
self.set_nz_flags(b, 3);
self.set_c_flag(false);
self.set_v_flag_op(b, 3);
}
JMP => {
self.r[R_PC] = self.effective_address(bus, 0)?;
pc_increment = 0;
}
JSB => {
let dst = 0;
self.stack_push(bus, (self.r[R_PC] as i32 + pc_increment) as u32)?;
self.r[R_PC] = self.effective_address(bus, dst)?;
pc_increment = 0;
}
LLSW3 | LLSH3 | LLSB3 => {
let a: u64 = u64::from(self.read_op(bus, 1)?);
let b = self.read_op(bus, 0)? & 0x1f;
let result = (a << b) as u32;
self.write_op(bus, 2, result)?;
self.set_nz_flags(result, 2);
self.set_c_flag(false);
self.set_v_flag_op(result, 2);
}
ARSW3 | ARSH3 | ARSB3 => {
let a = self.read_op(bus, 1)?;
let b = self.read_op(bus, 0)? & 0x1f;
let result = match self.ir.operands[0].data_type() {
Data::Word => (a as i32 >> b as i32) as u32,
Data::UWord => a >> b,
Data::Half => (a as i16 >> b as i16) as u32,
Data::UHalf => u32::from(a as u16 >> b as u16),
Data::Byte => u32::from(a as u8 >> b as u8),
Data::SByte => (a as i8 >> b as i8) as u32,
_ => 0,
};
self.write_op(bus, 2, result)?;
self.set_nz_flags(result, 2);
self.set_c_flag(false);
self.set_v_flag(false);
}
LRSW3 => {
let a = self.read_op(bus, 1)?;
let b = self.read_op(bus, 0)? & 0x1f;
let result = a >> b;
self.write_op(bus, 2, result)?;
self.set_nz_flags(result, 2);
self.set_c_flag(false);
self.set_v_flag_op(result, 2);
}
MCOMW | MCOMH | MCOMB => {
let a = self.read_op(bus, 0)?;
let result = !a;
self.write_op(bus, 1, result)?;
self.set_nz_flags(result, 1);
self.set_c_flag(false);
self.set_v_flag_op(result, 1);
}
MNEGW | MNEGH | MNEGB => {
let a = self.read_op(bus, 0)?;
let result = (!a).wrapping_add(1);
self.write_op(bus, 1, result)?;
self.set_nz_flags(result, 1);
self.set_c_flag(false);
self.set_v_flag_op(result, 1);
}
MOVBLW => {
while self.r[2] != 0 {
let a = bus.read_word(self.r[0] as usize, AccessCode::AddressFetch)?;
bus.write_word(self.r[1] as usize, a)?;
self.r[2] -= 1;
self.r[0] += 4;
self.r[1] += 4;
}
}
STREND => {
while bus.read_byte(self.r[0] as usize, AccessCode::AddressFetch)? != 0 {
self.r[0] += 1;
}
}
SWAPWI | SWAPHI | SWAPBI => {
let a = self.read_op(bus, 0)?;
self.write_op(bus, 0, self.r[0])?;
self.r[0] = a;
self.set_n_flag((a as i32) < 0);
self.set_z_flag(a == 0);
self.set_c_flag(false);
self.set_v_flag(false);
}
ROTW => {
let a = self.read_op(bus, 0)? & 0x1f;
let b = self.read_op(bus, 1)?;
let result = b.rotate_right(a);
self.write_op(bus, 2, result)?;
self.set_nz_flags(result, 2);
self.set_c_flag(false);
self.set_v_flag(false);
}
MOVAW => {
let result = self.effective_address(bus, 0)?;
self.write_op(bus, 1, result)?;
}
MOVB | MOVH | MOVW => {
let val = self.read_op(bus, 0)?;
self.write_op(bus, 1, val)?;
self.set_nz_flags(val, 1);
self.set_c_flag(false);
self.set_v_flag_op(val, 1);
}
MOVTRW => {
let val = self.effective_address(bus, 0)?;
self.write_op(bus, 1, val)?;
self.set_nz_flags(val, 1);
self.set_c_flag(false);
self.set_v_flag_op(val, 1);
}
MODW2 | MODH2 | MODB2 => {
// TODO: Modulo needs to be revisited.
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
if a == 0 {
return Err(CpuError::Exception(CpuException::IntegerZeroDivide));
}
let result = self.modulo(a, b, 0, 1);
self.write_op(bus, 1, result)?;
self.set_nz_flags(result, 1);
self.set_c_flag(false);
self.set_v_flag_op(result, 1);
}
MODW3 | MODH3 | MODB3 => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
if a == 0 {
return Err(CpuError::Exception(CpuException::IntegerZeroDivide));
}
let result = self.modulo(a, b, 0, 1);
self.write_op(bus, 2, result)?;
self.set_nz_flags(result, 2);
self.set_c_flag(false);
self.set_v_flag_op(result, 2);
}
MULW2 | MULH2 | MULB2 => {
let result = self.read_op(bus, 0)? * self.read_op(bus, 1)?;
self.write_op(bus, 1, result)?;
self.set_nz_flags(result, 1);
self.set_c_flag(false);
self.set_v_flag_op(result, 1);
}
MULW3 | MULH3 | MULB3 => {
let result = self.read_op(bus, 0)? * self.read_op(bus, 1)?;
self.write_op(bus, 2, result)?;
self.set_nz_flags(result, 2);
self.set_c_flag(false);
self.set_v_flag_op(result, 2);
}
ORW2 | ORH2 | ORB2 => {
let result = self.read_op(bus, 0)? | self.read_op(bus, 1)?;
self.write_op(bus, 1, result)?;
self.set_nz_flags(result, 1);
self.set_c_flag(false);
self.set_v_flag_op(result, 1);
}
ORW3 | ORH3 | ORB3 => {
let result = self.read_op(bus, 0)? | self.read_op(bus, 1)?;
self.write_op(bus, 2, result)?;
self.set_nz_flags(result, 2);
self.set_c_flag(false);
self.set_v_flag_op(result, 2);
}
POPW => {
let val = bus.read_word(self.r[R_SP] as usize - 4, AccessCode::AddressFetch)?;
self.write_op(bus, 0, val)?;
self.r[R_SP] -= 4;
self.set_nz_flags(val, 0);
self.set_c_flag(false);
self.set_v_flag(false);
}
PUSHAW => {
let val = self.effective_address(bus, 0)?;
self.stack_push(bus, val)?;
self.set_nz_flags(val, 0);
self.set_c_flag(false);
self.set_v_flag(false);
}
PUSHW => {
let val = self.read_op(bus, 0)?;
self.stack_push(bus, val)?;
self.set_nz_flags(val, 0);
self.set_c_flag(false);
self.set_v_flag(false);
}
RESTORE => {
let a = self.r[R_FP] - 28;
let b = bus.read_word(a as usize, AccessCode::AddressFetch)?;
let mut c = self.r[R_FP] - 24;
let mut r = match self.ir.operands[0].register {
Some(r) => r,
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
};
while r < R_FP {
self.r[r] = bus.read_word(c as usize, AccessCode::AddressFetch)?;
r += 1;
c += 4;
}
self.r[R_FP] = b;
self.r[R_SP] = a;
}
RETG => {
let mut new_psw =
bus.read_word(self.r[R_SP] as usize - 4, AccessCode::AddressFetch)?;
let new_pc = bus.read_word(self.r[R_SP] as usize - 8, AccessCode::AddressFetch)?;
// TODO: Check for illegal level change
new_psw &= !(F_IPL | F_CFD | F_QIE | F_CD | F_R | F_ISC | F_TM | F_ET);
new_psw |= self.r[R_PSW] & F_IPL;
new_psw |= self.r[R_PSW] & F_CFD;
new_psw |= self.r[R_PSW] & F_QIE;
new_psw |= self.r[R_PSW] & F_CD;
new_psw |= self.r[R_PSW] & F_R;
new_psw |= 7 << O_ISC;
new_psw |= 3 << O_ET;
self.r[R_PSW] = new_psw;
self.r[R_PC] = new_pc;
self.r[R_SP] -= 8;
pc_increment = 0;
}
RGEQ => {
if !self.n_flag() || self.z_flag() {
self.r[R_PC] = self.stack_pop(bus)?;
pc_increment = 0;
}
}
RGEQU => {
if !self.c_flag() {
self.r[R_PC] = self.stack_pop(bus)?;
pc_increment = 0;
}
}
RGTR => {
if !self.n_flag() && !self.z_flag() {
self.r[R_PC] = self.stack_pop(bus)?;
pc_increment = 0;
}
}
RNEQ | RNEQU => {
if !self.z_flag() {
self.r[R_PC] = self.stack_pop(bus)?;
pc_increment = 0;
}
}
RLEQ => {
if self.n_flag() || self.z_flag() {
self.r[R_PC] = self.stack_pop(bus)?;
pc_increment = 0;
}
}
RLEQU => {
if self.c_flag() || self.z_flag() {
self.r[R_PC] = self.stack_pop(bus)?;
pc_increment = 0;
}
}
RLSS => {
if self.n_flag() || !self.z_flag() {
self.r[R_PC] = self.stack_pop(bus)?;
pc_increment = 0;
}
}
REQL | REQLU => {
if self.z_flag() {
self.r[R_PC] = self.stack_pop(bus)?;
pc_increment = 0;
}
}
RSB => {
self.r[R_PC] = self.stack_pop(bus)?;
pc_increment = 0;
}
RET => {
let a = self.r[R_AP];
let b = bus.read_word((self.r[R_SP] - 4) as usize, AccessCode::AddressFetch)?;
let c = bus.read_word((self.r[R_SP] - 8) as usize, AccessCode::AddressFetch)?;
self.r[R_AP] = b;
self.r[R_PC] = c;
self.r[R_SP] = a;
pc_increment = 0;
}
RETPS => match self.priv_level() {
CpuLevel::Kernel => {
let new_pcbp = self.irq_pop(bus)?;
let new_psw = bus.read_word(new_pcbp as usize, AccessCode::AddressFetch)?;
self.r[R_PSW] &= !F_R;
self.r[R_PSW] |= new_psw & F_R;
self.context_switch_2(bus, new_pcbp)?;
self.context_switch_3(bus)?;
if self.r[R_PSW] & F_R != 0 {
self.r[R_FP] =
bus.read_word((new_pcbp + 24) as usize, AccessCode::AddressFetch)?;
self.r[0] =
bus.read_word((new_pcbp + 28) as usize, AccessCode::AddressFetch)?;
self.r[1] =
bus.read_word((new_pcbp + 32) as usize, AccessCode::AddressFetch)?;
self.r[2] =
bus.read_word((new_pcbp + 36) as usize, AccessCode::AddressFetch)?;
self.r[3] =
bus.read_word((new_pcbp + 40) as usize, AccessCode::AddressFetch)?;
self.r[4] =
bus.read_word((new_pcbp + 44) as usize, AccessCode::AddressFetch)?;
self.r[5] =
bus.read_word((new_pcbp + 48) as usize, AccessCode::AddressFetch)?;
self.r[6] =
bus.read_word((new_pcbp + 52) as usize, AccessCode::AddressFetch)?;
self.r[7] =
bus.read_word((new_pcbp + 56) as usize, AccessCode::AddressFetch)?;
self.r[8] =
bus.read_word((new_pcbp + 60) as usize, AccessCode::AddressFetch)?;
self.r[R_AP] =
bus.read_word((new_pcbp + 20) as usize, AccessCode::AddressFetch)?;
}
pc_increment = 0;
}
_ => return Err(CpuError::Exception(CpuException::PrivilegedOpcode)),
},
SAVE => {
bus.write_word(self.r[R_SP] as usize, self.r[R_FP])?;
let mut r = match self.ir.operands[0].register {
Some(r) => r,
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
};
let mut stack_offset = 4;
while r < R_FP {
bus.write_word(self.r[R_SP] as usize + stack_offset, self.r[r])?;
r += 1;
stack_offset += 4;
}
self.r[R_SP] += 28;
self.r[R_FP] = self.r[R_SP];
}
SUBW2 | SUBH2 | SUBB2 => {
let a = self.read_op(bus, 1)?;
let b = self.read_op(bus, 0)?;
self.sub(bus, a, b, 1)?;
}
SUBW3 | SUBH3 | SUBB3 => {
let a = self.read_op(bus, 1)?;
let b = self.read_op(bus, 0)?;
self.sub(bus, a, b, 2)?;
}
TSTW => {
let a = self.read_op(bus, 0)?;
self.set_n_flag((a as i32) < 0);
self.set_z_flag(a == 0);
self.set_c_flag(false);
self.set_v_flag(false);
}
TSTH => {
let a = self.read_op(bus, 0)?;
self.set_n_flag((a as i16) < 0);
self.set_z_flag(a == 0);
self.set_c_flag(false);
self.set_v_flag(false);
}
TSTB => {
let a = self.read_op(bus, 0)?;
self.set_n_flag((a as i8) < 0);
self.set_z_flag(a == 0);
self.set_c_flag(false);
self.set_v_flag(false);
}
XORW2 | XORH2 | XORB2 => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
let result = a ^ b;
self.write_op(bus, 1, result)?;
self.set_nz_flags(result, 1);
self.set_c_flag(false);
self.set_v_flag_op(result, 1);
}
XORW3 | XORH3 | XORB3 => {
let a = self.read_op(bus, 0)?;
let b = self.read_op(bus, 1)?;
let result = a ^ b;
self.write_op(bus, 2, result)?;
self.set_nz_flags(result, 2);
self.set_c_flag(false);
self.set_v_flag_op(result, 2);
}
_ => {
return Err(CpuError::Exception(CpuException::IllegalOpcode));
}
};
Ok(pc_increment)
}
fn gate(&mut self, bus: &mut Bus, index1: usize, index2: usize) -> Result<(), CpuError> {
let gate_l2 = bus.read_word(index1, AccessCode::AddressFetch)? as usize + index2;
let mut new_psw = bus.read_word(gate_l2, AccessCode::AddressFetch)?;
// Clear state in the new PSw
new_psw &= !(F_PM | F_IPL | F_R | F_ISC | F_TM | F_ET);
// Set new state
new_psw |= (self.r[R_PSW] & F_CM) >> 2; // PM (set from CM)
new_psw |= self.r[R_PSW] & F_IPL;
new_psw |= self.r[R_PSW] & F_R;
// Set new ISC, TM, and ET to 7, 1, 3 respectively
new_psw |= 7 << O_ISC;
new_psw |= 1 << O_TM;
new_psw |= 3 << O_ET;
// Finally, set the new PC and PSW
self.r[R_PC] = bus.read_word(gate_l2 + 4, AccessCode::AddressFetch)?;
self.r[R_PSW] = new_psw;
Ok(())
}
fn on_exception(&mut self, bus: &mut Bus, exc: &ExceptionType) -> Result<(), CpuError> {
let (et, isc) = match exc {
ExceptionType::ExternalMemory => (3, 5),
};
// TODO: Don't trap integer overflow
self.r[R_PSW] &= !F_ET;
self.r[R_PSW] &= !F_ISC;
self.r[R_PSW] |= et;
self.r[R_PSW] |= isc << O_ISC;
// Handle the exception
match exc {
ExceptionType::ExternalMemory => {
// TODO: Check for stac-bounds exception here
// Push the address of the next instruction to the stack.
bus.write_word(self.r[R_SP] as usize, self.r[R_PC])?;
// Write 0, 3 to the PSW ET and ISC fields
self.r[R_PSW] &= !(F_ET | F_ISC);
self.r[R_PSW] |= 3 << O_ISC;
// Push the PSW to the stack
bus.write_word(self.r[R_SP] as usize + 4, self.r[R_PSW])?;
self.gate(bus, 0usize, (isc as usize) << O_ISC)?;
// Finish stack push
self.r[R_SP] += 8;
}
};
Ok(())
}
/// Step the CPU by one instruction.
pub fn step(&mut self, bus: &mut Bus) {
// TODO: On CPU Exception or Bus Error, handle each error with the appropriate exception handler routine
match self.dispatch(bus) {
Ok(i) => {
// We should have the necessary information to trace after dispatch.
// trace!("[PC={:08x} PSW={:08x}] {}", &self.r[R_PC], &self.r[R_PSW], &self.ir);
self.r[R_PC] = (self.r[R_PC] as i32 + i) as u32
}
Err(CpuError::Bus(BusError::NoDevice(_)))
| Err(CpuError::Bus(BusError::Read(_)))
| Err(CpuError::Bus(BusError::Write(_))) => {
self.on_exception(bus, &ExceptionType::ExternalMemory).unwrap();
}
// Err(CpuError::Bus(BusError::Alignment)) => {}
// Err(CpuError::Bus(BusError::Permission)) => {}
// Err(CpuError::Exception(CpuException::IllegalOpcode)) => {}
// Err(CpuError::Exception(CpuException::InvalidDescriptor)) => {}
// Err(CpuError::Exception(CpuException::PrivilegedOpcode)) => {}
Err(e) => {
panic!(
"Unexpected CPU Error '{}'. PC={:08x} R0={:08x} R1={:08x} R2={:08x} OP={:?}",
e, &self.r[R_PC], &self.r[0], &self.r[1], &self.r[2], &self.ir
)
}
}
}
pub fn step_with_error(&mut self, bus: &mut Bus) -> Result<(), CpuError> {
match self.dispatch(bus) {
Ok(i) => self.r[R_PC] = (self.r[R_PC] as i32 + i) as u32,
Err(e) => return Err(e),
}
Ok(())
}
/// Set the CPU's Program Counter to the specified value
pub fn set_pc(&mut self, val: u32) {
self.r[R_PC] = val;
}
#[allow(clippy::too_many_arguments)]
fn set_operand(
&mut self,
index: usize,
size: u8,
mode: AddrMode,
data_type: Data,
expanded_type: Option<Data>,
register: Option<usize>,
embedded: u32,
) {
self.ir.operands[index].size = size;
self.ir.operands[index].mode = mode;
self.ir.operands[index].data_type = data_type;
self.ir.operands[index].expanded_type = expanded_type;
self.ir.operands[index].register = register;
self.ir.operands[index].embedded = embedded;
}
fn accumulate_instruction_byte(&mut self, bus: &mut Bus) -> Result<u8, CpuError> {
let addr: usize = self.r[R_PC] as usize + self.ir.len as usize;
let datum: u8 = bus.read_byte(addr, AccessCode::InstrFetch)?;
self.ir.data[self.ir.len as usize] = datum;
self.ir.len += 1;
Ok(datum)
}
fn accumulate_instruction_half(&mut self, bus: &mut Bus) -> Result<u16, CpuError> {
let addr: usize = self.r[R_PC] as usize + self.ir.len as usize;
let datum: u16 = bus.read_op_half(addr)?;
self.ir.data[self.ir.len as usize] = (datum & 0xff) as u8;
self.ir.data[(self.ir.len + 1) as usize] = ((datum >> 8) & 0xff) as u8;
self.ir.len += 2;
Ok(datum)
}
fn accumulate_instruction_word(&mut self, bus: &mut Bus) -> Result<u32, CpuError> {
let addr: usize = self.r[R_PC] as usize + self.ir.len as usize;
let datum: u32 = bus.read_op_word(addr)?;
self.ir.data[self.ir.len as usize] = (datum & 0xff) as u8;
self.ir.data[(self.ir.len + 1) as usize] = ((datum >> 8) & 0xff) as u8;
self.ir.data[(self.ir.len + 2) as usize] = ((datum >> 16) & 0xff) as u8;
self.ir.data[(self.ir.len + 3) as usize] = ((datum >> 24) & 0xff) as u8;
self.ir.len += 4;
Ok(datum)
}
/// Decode a literal Operand type.
///
/// These operands belong to only certain instructions, where a word without
/// a descriptor byte immediately follows the opcode.
fn decode_literal_operand(
&mut self,
bus: &mut Bus,
index: usize,
mn: &Mnemonic,
) -> Result<(), CpuError> {
match mn.dtype {
Data::Byte => {
let b: u8 = self.accumulate_instruction_byte(bus)?;
self.set_operand(index, 1, AddrMode::None, Data::Byte, None, None, u32::from(b));
}
Data::Half => {
let h: u16 = self.accumulate_instruction_half(bus)?;
self.set_operand(index, 2, AddrMode::None, Data::Half, None, None, u32::from(h));
}
Data::Word => {
let w: u32 = self.accumulate_instruction_word(bus)?;
self.set_operand(index, 4, AddrMode::None, Data::Word, None, None, w);
}
_ => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
}
Ok(())
}
/// Decode a descriptor Operand type.
fn decode_descriptor_operand(
&mut self,
bus: &mut Bus,
index: usize,
dtype: Data,
etype: Option<Data>,
recur: bool,
) -> Result<(), CpuError> {
let descriptor_byte: u8 = self.accumulate_instruction_byte(bus)?;
let m = (descriptor_byte & 0xf0) >> 4;
let r = descriptor_byte & 0xf;
// The descriptor is either 1 or 2 bytes, depending on whether this is a recursive
// call or not.
let dsize = if recur {
2
} else {
1
};
match m {
0..=3 => {
// Positive Literal
self.set_operand(
index,
dsize,
AddrMode::PositiveLiteral,
dtype,
etype,
None,
u32::from(descriptor_byte),
);
}
4 => {
match r {
15 => {
// Word Immediate
let w = self.accumulate_instruction_word(bus)?;
self.set_operand(
index,
dsize + 4,
AddrMode::WordImmediate,
dtype,
etype,
None,
w,
);
}
_ => {
// Register
self.set_operand(
index,
dsize,
AddrMode::Register,
dtype,
etype,
Some(r as usize),
0,
);
}
}
}
5 => {
match r {
15 => {
// Halfword Immediate
let h = self.accumulate_instruction_half(bus)?;
self.set_operand(
index,
dsize + 2,
AddrMode::HalfwordImmediate,
dtype,
etype,
None,
u32::from(h),
);
}
11 => {
// Illegal
return Err(CpuError::Exception(CpuException::IllegalOpcode));
}
_ => {
// Register Deferred Mode
self.set_operand(
index,
dsize,
AddrMode::RegisterDeferred,
dtype,
etype,
Some(r as usize),
0,
);
}
}
}
6 => {
match r {
15 => {
// Byte Immediate
let b = self.accumulate_instruction_byte(bus)?;
self.set_operand(
index,
dsize + 1,
AddrMode::ByteImmediate,
dtype,
etype,
None,
u32::from(b),
);
}
_ => {
// FP Short Offset
self.set_operand(
index,
dsize,
AddrMode::FpShortOffset,
dtype,
etype,
Some(R_FP),
u32::from(r),
);
}
}
}
7 => {
match r {
15 => {
// Absolute
let w = self.accumulate_instruction_word(bus)?;
self.set_operand(
index,
dsize + 4,
AddrMode::Absolute,
dtype,
etype,
None,
w,
);
}
_ => {
// AP Short Offset
self.set_operand(
index,
dsize,
AddrMode::ApShortOffset,
dtype,
etype,
Some(R_AP),
u32::from(r),
);
}
}
}
8 => {
match r {
11 => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
_ => {
// Word Displacement
let disp = self.accumulate_instruction_word(bus)?;
self.set_operand(
index,
dsize + 4,
AddrMode::WordDisplacement,
dtype,
etype,
Some(r as usize),
disp,
);
}
}
}
9 => {
match r {
11 => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
_ => {
// Word Displacement Deferred
let disp = self.accumulate_instruction_word(bus)?;
self.set_operand(
index,
dsize + 4,
AddrMode::WordDisplacementDeferred,
dtype,
etype,
Some(r as usize),
disp,
);
}
}
}
10 => {
match r {
11 => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
_ => {
// Halfword Displacement
let disp = self.accumulate_instruction_half(bus)?;
self.set_operand(
index,
dsize + 2,
AddrMode::HalfwordDisplacement,
dtype,
etype,
Some(r as usize),
u32::from(disp),
);
}
}
}
11 => {
match r {
11 => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
_ => {
// Halfword Displacement Deferred
let disp = self.accumulate_instruction_half(bus)?;
self.set_operand(
index,
dsize + 2,
AddrMode::HalfwordDisplacementDeferred,
dtype,
etype,
Some(r as usize),
u32::from(disp),
);
}
}
}
12 => {
match r {
11 => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
_ => {
// Byte Displacement
let disp = self.accumulate_instruction_byte(bus)?;
self.set_operand(
index,
dsize + 1,
AddrMode::ByteDisplacement,
dtype,
etype,
Some(r as usize),
u32::from(disp),
);
}
}
}
13 => {
match r {
11 => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
_ => {
// Byte Displacement Deferred
let disp = self.accumulate_instruction_byte(bus)?;
self.set_operand(
index,
dsize + 1,
AddrMode::ByteDisplacementDeferred,
dtype,
etype,
Some(r as usize),
u32::from(disp),
);
}
}
}
14 => match r {
0 => self.decode_descriptor_operand(bus, index, dtype, Some(Data::UWord), true)?,
2 => self.decode_descriptor_operand(bus, index, dtype, Some(Data::UHalf), true)?,
3 => self.decode_descriptor_operand(bus, index, dtype, Some(Data::Byte), true)?,
4 => self.decode_descriptor_operand(bus, index, dtype, Some(Data::Word), true)?,
6 => self.decode_descriptor_operand(bus, index, dtype, Some(Data::Half), true)?,
7 => self.decode_descriptor_operand(bus, index, dtype, Some(Data::SByte), true)?,
15 => {
let w = self.accumulate_instruction_word(bus)?;
self.set_operand(
index,
dsize + 4,
AddrMode::AbsoluteDeferred,
dtype,
etype,
None,
w,
);
}
_ => {
return Err(CpuError::Exception(CpuException::IllegalOpcode));
}
},
15 => {
// Negative Literal
self.set_operand(
index,
1,
AddrMode::NegativeLiteral,
dtype,
etype,
None,
u32::from(descriptor_byte),
);
}
_ => {
return Err(CpuError::Exception(CpuException::IllegalOpcode));
}
};
Ok(())
}
/// Fully decode an Operand
fn decode_operand(
&mut self,
bus: &mut Bus,
index: usize,
mn: &Mnemonic,
ot: OpType,
etype: Option<Data>,
) -> Result<(), CpuError> {
match ot {
OpType::Lit => self.decode_literal_operand(bus, index, mn),
OpType::Src | OpType::Dest => {
self.decode_descriptor_operand(bus, index, mn.dtype, etype, false)
}
OpType::None => Ok(()),
}
}
/// Decode the instruction currently pointed at by the Program Counter.
fn decode_instruction(&mut self, bus: &mut Bus) -> Result<(), CpuError> {
self.ir.len = 0;
// Read the first byte of the instruction. Most instructions are only
// one byte, so this is usually enough.
let b1 = self.accumulate_instruction_byte(bus)?;
// Map the Mnemonic to the opcode we just read. But there's a special
// case if the value we read was '0x30'. This indicates that the instruction
// we're reading is a halfword, requiring two bytes.
let mut mn: &Option<Mnemonic> = &NULL_MNEMONIC;
if b1 == 0x30 {
let b2 = self.accumulate_instruction_byte(bus)?;
let opcode = (u16::from(b1) << 8) | u16::from(b2);
for m in &HALFWORD_MNEMONICS {
if m.is_some() && m.as_ref().unwrap().opcode == opcode {
mn = m;
break;
}
}
} else {
mn = &BYTE_MNEMONICS[b1 as usize];
};
// If we found a valid mnemonic, read in and decode all of its operands.
// Otherwise, we must return a CpuException::IllegalOpcode
match mn {
Some(mn) => {
let mut etype: Option<Data> = None;
for (index, ot) in mn.ops.iter().enumerate() {
if *ot == OpType::None {
self.ir.operands[index].clear();
} else {
// Push a decoded operand
self.decode_operand(bus, index, mn, *ot, etype)?;
etype = self.ir.operands[index].expanded_type;
}
}
self.ir.opcode = mn.opcode;
self.ir.name = mn.name;
self.ir.data_type = mn.dtype;
}
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
}
Ok(())
}
/// Convenience operations on flags.
fn set_v_flag_op(&mut self, val: u32, index: usize) {
match self.ir.operands[index].data_type {
Data::Word | Data::UWord => self.set_v_flag(false),
Data::Half | Data::UHalf => self.set_v_flag(val > 0xffff),
Data::Byte | Data::SByte => self.set_v_flag(val > 0xff),
Data::None => {
// Intentionally ignored
}
}
}
fn set_nz_flags(&mut self, val: u32, index: usize) {
match self.ir.operands[index].data_type {
Data::Word | Data::UWord => {
self.set_n_flag((val & 0x80000000) != 0);
self.set_z_flag(val == 0);
}
Data::Half | Data::UHalf => {
self.set_n_flag((val & 0x8000) != 0);
self.set_z_flag(val as u16 == 0);
}
Data::Byte | Data::SByte => {
self.set_n_flag((val & 0x80) != 0);
self.set_z_flag(val as u8 == 0);
}
Data::None => {
// Intentionally ignored
}
}
}
fn set_c_flag(&mut self, set: bool) {
if set {
self.r[R_PSW] |= F_C;
} else {
self.r[R_PSW] &= !F_C;
}
}
fn c_flag(&self) -> bool {
((self.r[R_PSW] & F_C) >> 18) == 1
}
fn set_v_flag(&mut self, set: bool) {
if set {
self.r[R_PSW] |= F_V;
} else {
self.r[R_PSW] &= !F_V;
}
}
fn v_flag(&self) -> bool {
((self.r[R_PSW] & F_V) >> 19) == 1
}
fn set_z_flag(&mut self, set: bool) {
if set {
self.r[R_PSW] |= F_Z;
} else {
self.r[R_PSW] &= !F_Z;
}
}
fn z_flag(&self) -> bool {
((self.r[R_PSW] & F_Z) >> 20) == 1
}
fn set_n_flag(&mut self, set: bool) {
if set {
self.r[R_PSW] |= F_N;
} else {
self.r[R_PSW] &= !F_N;
}
}
fn n_flag(&self) -> bool {
((self.r[R_PSW] & F_N) >> 21) == 1
}
pub fn set_isc(&mut self, val: u32) {
self.r[R_PSW] &= !F_ISC; // Clear existing value
self.r[R_PSW] |= (val & 0xf) << 3; // Set new value
}
pub fn set_priv_level(&mut self, level: CpuLevel) {
let val = match level {
CpuLevel::Kernel => 0,
CpuLevel::Executive => 1,
CpuLevel::Supervisor => 2,
CpuLevel::User => 3,
};
let old_level = (self.r[R_PSW] & F_CM) >> 11;
self.r[R_PSW] &= !F_PM; // Clear PM
self.r[R_PSW] |= (old_level & 3) << 9; // Set PM
self.r[R_PSW] &= !F_CM; // Clear CM
self.r[R_PSW] |= (val & 3) << 11; // Set CM
}
pub fn priv_level(&self) -> CpuLevel {
let cm = ((self.r[R_PSW] & F_CM) >> 11) & 3;
match cm {
0 => CpuLevel::Kernel,
1 => CpuLevel::Executive,
2 => CpuLevel::Supervisor,
_ => CpuLevel::User,
}
}
pub fn stack_push(&mut self, bus: &mut Bus, val: u32) -> Result<(), CpuError> {
bus.write_word(self.r[R_SP] as usize, val)?;
self.r[R_SP] += 4;
Ok(())
}
pub fn stack_pop(&mut self, bus: &mut Bus) -> Result<u32, CpuError> {
let result = bus.read_word((self.r[R_SP] - 4) as usize, AccessCode::AddressFetch)?;
self.r[R_SP] -= 4;
Ok(result)
}
pub fn irq_push(&mut self, bus: &mut Bus, val: u32) -> Result<(), CpuError> {
bus.write_word(self.r[R_ISP] as usize, val)?;
self.r[R_ISP] += 4;
Ok(())
}
pub fn irq_pop(&mut self, bus: &mut Bus) -> Result<u32, CpuError> {
self.r[R_ISP] -= 4;
let result = bus.read_word((self.r[R_ISP]) as usize, AccessCode::AddressFetch)?;
Ok(result)
}
pub fn get_pc(&self) -> u32 {
self.r[R_PC]
}
pub fn get_ap(&self) -> u32 {
self.r[R_AP]
}
pub fn get_psw(&self) -> u32 {
self.r[R_PSW]
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bus::Bus;
const BASE: usize = 0x700000;
/// Helper function to set up and prepare a cpu and bus
/// with a supplied program.
fn do_with_program<F>(program: &[u8], test: F)
where
F: Fn(&mut Cpu, &mut Bus),
{
let mut cpu: Cpu = Cpu::new();
let mut bus: Bus = Bus::new(0x10000);
bus.load(BASE, program).unwrap();
cpu.r[R_PC] = BASE as u32;
test(&mut cpu, &mut bus);
}
#[test]
fn sign_extension() {
assert_eq!(0xffff8000, sign_extend_halfword(0x8000));
assert_eq!(0xffffff80, sign_extend_byte(0x80));
}
#[test]
fn can_set_and_clear_nzvc_flags() {
let mut cpu = Cpu::new();
cpu.set_c_flag(true);
assert_eq!(cpu.r[R_PSW], F_C);
cpu.set_v_flag(true);
assert_eq!(cpu.r[R_PSW], F_C | F_V);
cpu.set_z_flag(true);
assert_eq!(cpu.r[R_PSW], F_C | F_V | F_Z);
cpu.set_n_flag(true);
assert_eq!(cpu.r[R_PSW], F_C | F_V | F_Z | F_N);
cpu.set_c_flag(false);
assert_eq!(cpu.r[R_PSW], F_V | F_Z | F_N);
cpu.set_v_flag(false);
assert_eq!(cpu.r[R_PSW], F_Z | F_N);
cpu.set_z_flag(false);
assert_eq!(cpu.r[R_PSW], F_N);
cpu.set_n_flag(false);
assert_eq!(cpu.r[R_PSW], 0);
}
#[test]
fn can_set_isc_flag() {
let mut cpu = Cpu::new();
for i in 0..15 {
cpu.set_isc(i);
assert_eq!(i << 3, cpu.r[R_PSW]);
}
cpu.set_isc(16); // Out of range, should fail
assert_eq!(0, cpu.r[R_PSW]);
}
#[test]
fn decodes_byte_literal_operand() {
let program: [u8; 2] = [0x4f, 0x06]; // BLEB 0x6
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(1, AddrMode::None, Data::Byte, None, None, 6)
);
})
}
#[test]
fn decodes_halfword_literal_operand() {
let program: [u8; 3] = [0x4e, 0xff, 0x0f]; // BLEH 0xfff
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(2, AddrMode::None, Data::Half, None, None, 0xfff)
);
})
}
#[test]
fn decodes_word_literal_operand() {
let program: [u8; 5] = [0x32, 0xff, 0x4f, 0x00, 0x00]; // SPOP 0x4fff
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(4, AddrMode::None, Data::Word, None, None, 0x4fff)
);
});
}
#[test]
fn decodes_positive_literal_operand() {
let program: [u8; 3] = [0x87, 0x04, 0x44]; // MOVB &4,%r4
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(1, AddrMode::PositiveLiteral, Data::Byte, None, None, 0x04)
);
});
}
#[test]
fn decodes_word_immediate_operand() {
let program = [0x84, 0x4f, 0x78, 0x56, 0x34, 0x12, 0x43]; // MOVW &0x12345678,%r3
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(5, AddrMode::WordImmediate, Data::Word, None, None, 0x12345678)
);
});
}
#[test]
fn decodes_register_operand() {
let program: [u8; 3] = [0x87, 0x04, 0x44]; // MOVB &4,%r4
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[1],
Operand::new(1, AddrMode::Register, Data::Byte, None, Some(4), 0)
);
});
}
#[test]
fn decodes_halfword_immediate_operand() {
let program = [0x84, 0x5f, 0x34, 0x12, 0x42]; // MOVW &0x1234,%r2
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(3, AddrMode::HalfwordImmediate, Data::Word, None, None, 0x1234,)
);
});
}
#[test]
fn decodes_register_deferred_operand() {
let program: [u8; 3] = [0x86, 0x52, 0x41]; // MOVH (%r2),%r1
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(1, AddrMode::RegisterDeferred, Data::Half, None, Some(2), 0)
);
});
}
#[test]
fn decodes_byte_immediate_operand() {
let program: [u8; 4] = [0x84, 0x6f, 0x28, 0x46]; // MOVW &40,%r6
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(2, AddrMode::ByteImmediate, Data::Word, None, None, 40)
);
});
}
#[test]
fn decodes_fp_short_offset_operand() {
let program: [u8; 3] = [0x84, 0x6C, 0x40]; // MOVW 12(%fp),%r0
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(1, AddrMode::FpShortOffset, Data::Word, None, Some(R_FP), 12)
);
});
}
#[test]
fn decodes_absolute_operand() {
let program: [u8; 7] = [0x87, 0x7f, 0x00, 0x01, 0x00, 0x00, 0x40]; // MOVB $0x100, %r0
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(5, AddrMode::Absolute, Data::Byte, None, None, 0x00000100)
);
});
}
#[test]
fn decodes_absolute_deferred_operand() {
let program = [0x87, 0xef, 0x00, 0x01, 0x00, 0x00, 0x40]; // MOVB *$0x100,%r0
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(5, AddrMode::AbsoluteDeferred, Data::Byte, None, None, 0x00000100)
);
});
}
#[test]
fn decodes_ap_short_offset_operand() {
let program: [u8; 3] = [0x84, 0x74, 0x43]; // MOVW 4(%ap),%r3
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(1, AddrMode::ApShortOffset, Data::Word, None, Some(R_AP), 4)
);
});
}
#[test]
fn decodes_word_displacement_operand() {
let program: [u8; 7] = [0x87, 0x82, 0x34, 0x12, 0x00, 0x00, 0x44]; // MOVB 0x1234(%r2),%r4
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(5, AddrMode::WordDisplacement, Data::Byte, None, Some(2), 0x1234,)
);
});
}
#[test]
fn decodes_word_displacement_deferred_operand() {
let program: [u8; 7] = [0x87, 0x92, 0x50, 0x40, 0x00, 0x00, 0x40]; // MOVB *0x4050(%r2),%r0
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(
5,
AddrMode::WordDisplacementDeferred,
Data::Byte,
None,
Some(2),
0x4050,
)
);
});
}
#[test]
fn decodes_halfword_displacement_operand() {
let program: [u8; 5] = [0x87, 0xa2, 0x34, 0x12, 0x44]; // MOVB 0x1234(%r2),%r4
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(3, AddrMode::HalfwordDisplacement, Data::Byte, None, Some(2), 0x1234,)
);
});
}
#[test]
fn decodes_halfword_displacement_deferred_operand() {
let program: [u8; 5] = [0x87, 0xb2, 0x50, 0x40, 0x40]; // MOVB *0x4050(%r2),%r0
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(
3,
AddrMode::HalfwordDisplacementDeferred,
Data::Byte,
None,
Some(2),
0x4050,
)
);
});
}
#[test]
fn decodes_byte_displacement_operand() {
let program: [u8; 4] = [0x87, 0xc1, 0x06, 0x40]; // MOVB 6(%r1),%r0
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(2, AddrMode::ByteDisplacement, Data::Byte, None, Some(1), 6)
);
});
}
#[test]
fn decodes_byte_displacement_deferred_operand() {
let program: [u8; 4] = [0x87, 0xd2, 0x30, 0x43]; // MOVB *0x30(%r2),%r3
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(
2,
AddrMode::ByteDisplacementDeferred,
Data::Byte,
None,
Some(2),
0x30
)
);
});
}
#[test]
fn decodes_expanded_type_operand() {
let program: [u8; 6] = [0x87, 0xe7, 0x40, 0xe2, 0xc1, 0x04]; // MOVB {sbyte}%r0,{uhalf}4(%r1)
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(2, AddrMode::Register, Data::Byte, Some(Data::SByte), Some(0), 0,)
);
assert_eq!(
cpu.ir.operands[1],
Operand::new(
3,
AddrMode::ByteDisplacement,
Data::Byte,
Some(Data::UHalf),
Some(1),
4,
)
);
});
}
#[test]
fn decodes_negative_literal_operand() {
let program: [u8; 3] = [0x87, 0xff, 0x40]; // MOVB &-1,%r0
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(
cpu.ir.operands[0],
Operand::new(1, AddrMode::NegativeLiteral, Data::Byte, None, None, 0xff)
);
});
}
fn assert_instruction(cpu: &Cpu, opcode: u16, size: u8, name: &'static str, data_type: Data) {
assert_eq!(cpu.ir.opcode, opcode);
assert_eq!(cpu.ir.len, size);
assert_eq!(cpu.ir.name, name);
assert_eq!(cpu.ir.data_type, data_type);
}
// #[test]
// fn adds_two_numbers() {
// let program = [0xdc, 0x4f, 0x78, 0x56, 0x34, 0x12, 0x43]; // ADDW3 &16, &0x12345678,%r3
// }
#[test]
fn decodes_halfword_instructions() {
let program = [0x30, 0x0d]; // ENBVJMP
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_instruction(cpu, 0x300d, 2, "ENBVJMP", Data::None);
})
}
#[test]
fn decodes_instructions() {
let program: [u8; 10] = [
0x87, 0xe7, 0x40, 0xe2, 0xc1, 0x04, // MOVB {sbyte}%r0,{uhalf}4(%r1)
0x87, 0xd2, 0x30, 0x43, // MOVB *0x30(%r2),%r3
];
do_with_program(&program, |cpu, bus| {
{
cpu.set_pc(BASE as u32);
cpu.decode_instruction(bus).unwrap();
let expected_operands = [
Operand::new(2, AddrMode::Register, Data::Byte, Some(Data::SByte), Some(0), 0),
Operand::new(
3,
AddrMode::ByteDisplacement,
Data::Byte,
Some(Data::UHalf),
Some(1),
4,
),
];
assert_instruction(cpu, 0x87, 6, "MOVB", Data::Byte);
assert_eq!(cpu.ir.operands[0], expected_operands[0]);
assert_eq!(cpu.ir.operands[1], expected_operands[1]);
}
{
cpu.set_pc((BASE + 6) as u32);
cpu.decode_instruction(bus).unwrap();
let expected_operands = [
Operand::new(
2,
AddrMode::ByteDisplacementDeferred,
Data::Byte,
None,
Some(2),
0x30,
),
Operand::new(1, AddrMode::Register, Data::Byte, None, Some(3), 0),
];
assert_instruction(cpu, 0x87, 4, "MOVB", Data::Byte);
assert_eq!(cpu.ir.operands[0], expected_operands[0]);
assert_eq!(cpu.ir.operands[1], expected_operands[1]);
}
})
}
#[test]
fn reads_register_operand_data() {
{
let program = [0x87, 0xe7, 0x40, 0xe2, 0x41]; // MOVB {sbyte}%r0,{uhalf}%r1
do_with_program(&program, |cpu, bus| {
cpu.r[0] = 0xff;
cpu.decode_instruction(bus).unwrap();
assert_eq!(0xffffffff, cpu.read_op(bus, 0).unwrap());
});
}
{
let program = [0x87, 0x40, 0x41]; // MOVB %r0,%r1
do_with_program(&program, |cpu, bus| {
cpu.r[0] = 0xff;
cpu.decode_instruction(bus).unwrap();
assert_eq!(0xff, cpu.read_op(bus, 0).unwrap());
});
}
}
#[test]
fn reads_positive_literal_operand_data() {
let program = [0x87, 0x04, 0x44];
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(4, cpu.read_op(bus, 0).unwrap() as i8);
});
}
#[test]
fn reads_negative_literal_operand_data() {
let program = [0x87, 0xff, 0x44];
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(-1, cpu.read_op(bus, 0).unwrap() as i8);
});
}
#[test]
fn reads_word_immediate_operand_data() {
let program = [0x84, 0x4f, 0x78, 0x56, 0x34, 0x12, 0x43]; // MOVW &0x12345678,%r3
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(0x12345678, cpu.read_op(bus, 0).unwrap())
});
}
#[test]
fn reads_halfword_immediate_operand_data() {
let program = [0x84, 0x5f, 0x34, 0x12, 0x42]; // MOVW &0x1234,%r2
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(0x1234, cpu.read_op(bus, 0).unwrap())
});
}
#[test]
fn reads_negative_halfword_immediate_operand_data() {
let program = [0x84, 0x5f, 0x00, 0x80, 0x42]; // MOVW &0x8000,%r2
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(0xffff8000, cpu.read_op(bus, 0).unwrap())
});
}
#[test]
fn reads_byte_immediate_operand_data() {
let program = [0x84, 0x6f, 0x28, 0x42]; // MOVW &40,%r2
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(40, cpu.read_op(bus, 0).unwrap())
});
}
#[test]
fn reads_negative_byte_immediate_operand_data() {
let program = [0x84, 0x6f, 0xff, 0x42]; // MOVW &-1,%r2
do_with_program(&program, |cpu, bus| {
cpu.decode_instruction(bus).unwrap();
assert_eq!(-1, cpu.read_op(bus, 0).unwrap() as i32)
});
}
#[test]
fn reads_absolute_operand_data() {
let program = [0x87, 0x7f, 0x00, 0x02, 0x70, 0x00, 0x04]; // MOVB $0x700200,%r0
do_with_program(&program, |cpu, bus| {
bus.write_byte(0x700200, 0x5a).unwrap();
cpu.decode_instruction(bus).unwrap();
assert_eq!(0x5a, cpu.read_op(bus, 0).unwrap());
});
}
#[test]
fn reads_absolute_deferred_operand_data() {
let program = [0x87, 0xef, 0x00, 0x01, 0x70, 0x00, 0x41]; // MOVB *$0x700100,%r0
do_with_program(&program, |cpu, bus| {
bus.write_word(0x700100, 0x700300).unwrap();
bus.write_byte(0x700300, 0x1f).unwrap();
cpu.decode_instruction(bus).unwrap();
assert_eq!(0x1f, cpu.read_op(bus, 0).unwrap());
});
}
#[test]
fn reads_positive_byte_displacement_operand_data() {
let program = [
0x87, 0xc1, 0x06, 0x40, // MOVB 6(%r1),%r0
];
do_with_program(&program, |cpu, bus| {
cpu.r[1] = 0x700200;
bus.write_byte(0x700206, 0x1f).unwrap();
cpu.decode_instruction(bus).unwrap();
assert_eq!(0x1f, cpu.read_op(bus, 0).unwrap());
});
}
#[test]
fn reads_negative_byte_displacement_operand_data() {
let program = [
0x87, 0xc1, 0xfe, 0x40, // MOVB -2(%r1),%r0
];
do_with_program(&program, |cpu, bus| {
cpu.r[1] = 0x700200;
bus.write_byte(0x7001fe, 0xc5).unwrap();
cpu.decode_instruction(bus).unwrap();
assert_eq!(0xc5, cpu.read_op(bus, 0).unwrap());
});
}
#[test]
fn reads_byte_displacement_deferred_operand_data() {
let program = [0x87, 0xd2, 0x30, 0x43]; // MOVB *0x30(%r2),%r3
do_with_program(&program, |cpu, bus| {
cpu.r[2] = 0x700200;
bus.write_word(0x700230, 0x700300).unwrap();
bus.write_byte(0x700300, 0x5a).unwrap();
cpu.decode_instruction(bus).unwrap();
assert_eq!(0x5a, cpu.read_op(bus, 0).unwrap());
})
}
#[test]
fn reads_halword_displacement_operand_data() {
let program = [0x87, 0xa2, 0x01, 0x11, 0x48]; // MOVB 0x1101(%r2),%r8
do_with_program(&program, |cpu, bus| {
cpu.r[2] = 0x700000;
bus.write_byte(0x701101, 0x1f).unwrap();
cpu.decode_instruction(bus).unwrap();
assert_eq!(0x1f, cpu.read_op(bus, 0).unwrap());
});
}
#[test]
fn reads_halfword_displacement_deferred_operand_data() {
let program = [0x87, 0xb2, 0x00, 0x02, 0x46]; // MOVB *0x200(%r2),%r6
do_with_program(&program, |cpu, bus| {
cpu.r[2] = 0x700000;
bus.write_word(0x700200, 0x700500).unwrap();
bus.write_byte(0x700500, 0x5a).unwrap();
cpu.decode_instruction(bus).unwrap();
assert_eq!(0x5a, cpu.read_op(bus, 0).unwrap());
})
}
#[test]
fn reads_word_displacement_operand_data() {
let program = [0x87, 0x82, 0x01, 0x11, 0x00, 0x00, 0x48]; // MOVB 0x1101(%r2),%r8
do_with_program(&program, |cpu, bus| {
cpu.r[2] = 0x700000;
bus.write_byte(0x701101, 0x1f).unwrap();
cpu.decode_instruction(bus).unwrap();
assert_eq!(0x1f, cpu.read_op(bus, 0).unwrap());
});
}
#[test]
fn reads_word_displacement_deferred_operand_data() {
let program = [0x87, 0x92, 0x00, 0x02, 0x00, 0x00, 0x46]; // MOVB *0x200(%r2),%r6
do_with_program(&program, |cpu, bus| {
cpu.r[2] = 0x700000;
bus.write_word(0x700200, 0x700500).unwrap();
bus.write_byte(0x700500, 0x5a).unwrap();
cpu.decode_instruction(bus).unwrap();
assert_eq!(0x5a, cpu.read_op(bus, 0).unwrap());
})
}
#[test]
fn reads_ap_short_offset_operand_data() {
let program = [0x84, 0x74, 0x43]; // MOVW 4(%ap),%r3
do_with_program(&program, |cpu, bus| {
cpu.r[R_AP] = 0x700500;
bus.write_word(0x700504, 0x12345678).unwrap();
cpu.decode_instruction(bus).unwrap();
assert_eq!(0x12345678, cpu.read_op(bus, 0).unwrap());
});
}
#[test]
fn reads_fp_short_offset_operand_data() {
let program = [0x84, 0x6c, 0x40]; // MOVW 12(%fp),%r0
do_with_program(&program, |cpu, bus| {
cpu.r[R_FP] = 0x700200;
bus.write_word(0x70020c, 0x12345678).unwrap();
cpu.decode_instruction(bus).unwrap();
assert_eq!(0x12345678, cpu.read_op(bus, 0).unwrap());
});
}
}