2021-09-28 23:09:38 +00:00
|
|
|
|
|
|
|
use crate::error::Error;
|
|
|
|
use crate::memory::{Address, AddressSpace};
|
|
|
|
|
2021-09-30 00:11:48 +00:00
|
|
|
use super::execute::MC68010;
|
|
|
|
use super::execute::ERR_ILLEGAL_INSTRUCTION;
|
2021-09-28 23:09:38 +00:00
|
|
|
|
2021-10-02 05:06:53 +00:00
|
|
|
const OPCG_BIT_OPS: u8 = 0x0;
|
|
|
|
const OPCG_MOVE_BYTE: u8 = 0x1;
|
|
|
|
const OPCG_MOVE_LONG: u8 = 0x2;
|
|
|
|
const OPCG_MOVE_WORD: u8 = 0x3;
|
|
|
|
const OPCG_MISC: u8 = 0x04;
|
|
|
|
const OPCG_ADDQ_SUBQ: u8 = 0x5;
|
|
|
|
const OPCG_BRANCH: u8 = 0x6;
|
|
|
|
const OPCG_MOVEQ: u8 = 0x7;
|
|
|
|
const OPCG_DIV_OR: u8 = 0x8;
|
|
|
|
const OPCG_SUB: u8 = 0x9;
|
|
|
|
const OPCG_RESERVED1: u8 = 0xA;
|
|
|
|
const OPCG_CMP_EOR: u8 = 0xB;
|
|
|
|
const OPCG_MUL_AND: u8 = 0xC;
|
|
|
|
const OPCG_ADD: u8 = 0xD;
|
|
|
|
const OPCG_SHIFT: u8 = 0xE;
|
|
|
|
const OPCG_RESERVED2: u8 = 0xF;
|
|
|
|
|
|
|
|
|
2021-09-28 23:09:38 +00:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
2021-09-30 00:11:48 +00:00
|
|
|
pub enum Sign {
|
2021-09-28 23:09:38 +00:00
|
|
|
Signed,
|
|
|
|
Unsigned,
|
|
|
|
}
|
|
|
|
|
2021-09-29 19:24:04 +00:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
2021-09-30 00:11:48 +00:00
|
|
|
pub enum Direction {
|
2021-09-29 19:24:04 +00:00
|
|
|
FromTarget,
|
|
|
|
ToTarget,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
2021-09-30 00:11:48 +00:00
|
|
|
pub enum ShiftDirection {
|
2021-09-29 19:24:04 +00:00
|
|
|
Right,
|
|
|
|
Left,
|
|
|
|
}
|
2021-09-30 00:11:48 +00:00
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
|
|
pub enum RegisterType {
|
|
|
|
Data,
|
|
|
|
Address,
|
|
|
|
}
|
|
|
|
|
2021-09-29 19:24:04 +00:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
2021-09-30 00:11:48 +00:00
|
|
|
pub enum ControlRegister {
|
2021-09-29 19:24:04 +00:00
|
|
|
VBR,
|
|
|
|
}
|
|
|
|
|
2021-09-28 23:09:38 +00:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
2021-09-30 00:11:48 +00:00
|
|
|
pub enum Size {
|
2021-09-28 23:09:38 +00:00
|
|
|
Byte,
|
|
|
|
Word,
|
|
|
|
Long,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
2021-09-30 00:11:48 +00:00
|
|
|
pub enum Condition {
|
2021-09-29 19:24:04 +00:00
|
|
|
True,
|
|
|
|
False,
|
|
|
|
High,
|
|
|
|
LowOrSame,
|
2021-09-28 23:09:38 +00:00
|
|
|
CarryClear,
|
|
|
|
CarrySet,
|
|
|
|
NotEqual,
|
2021-09-29 19:24:04 +00:00
|
|
|
Equal,
|
|
|
|
OverflowClear,
|
|
|
|
OverflowSet,
|
|
|
|
Plus,
|
|
|
|
Minus,
|
2021-09-28 23:09:38 +00:00
|
|
|
GreaterThanOrEqual,
|
2021-09-29 19:24:04 +00:00
|
|
|
LessThan,
|
2021-09-28 23:09:38 +00:00
|
|
|
GreaterThan,
|
|
|
|
LessThanOrEqual,
|
|
|
|
}
|
|
|
|
|
2021-09-30 04:52:38 +00:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
2021-09-30 00:11:48 +00:00
|
|
|
pub enum Target {
|
2021-09-28 23:09:38 +00:00
|
|
|
Immediate(u32),
|
|
|
|
DirectDReg(u8),
|
|
|
|
DirectAReg(u8),
|
|
|
|
IndirectAReg(u8),
|
|
|
|
IndirectARegInc(u8),
|
|
|
|
IndirectARegDec(u8),
|
2021-09-30 00:11:48 +00:00
|
|
|
IndirectARegOffset(u8, i32),
|
|
|
|
IndirectARegXRegOffset(u8, RegisterType, u8, i32, Size),
|
2021-09-28 23:09:38 +00:00
|
|
|
IndirectMemory(u32),
|
2021-09-30 00:11:48 +00:00
|
|
|
IndirectPCOffset(i32),
|
|
|
|
IndirectPCXRegOffset(RegisterType, u8, i32, Size),
|
2021-09-28 23:09:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
2021-09-30 00:11:48 +00:00
|
|
|
pub enum Instruction {
|
2021-09-29 19:24:04 +00:00
|
|
|
//ABCD
|
2021-09-28 23:09:38 +00:00
|
|
|
ADD(Target, Target, Size),
|
|
|
|
AND(Target, Target, Size),
|
2021-09-29 19:24:04 +00:00
|
|
|
ANDtoCCR(u8),
|
|
|
|
ANDtoSR(u16),
|
|
|
|
ASd(Target, Target, Size, ShiftDirection),
|
2021-09-28 23:09:38 +00:00
|
|
|
|
2021-09-30 00:11:48 +00:00
|
|
|
Bcc(Condition, i16),
|
|
|
|
BRA(i16),
|
|
|
|
BSR(i16),
|
2021-09-29 19:24:04 +00:00
|
|
|
BTST(Target, Target, Size),
|
|
|
|
BCHG(Target, Target, Size),
|
|
|
|
BCLR(Target, Target, Size),
|
|
|
|
BSET(Target, Target, Size),
|
2021-09-28 23:09:38 +00:00
|
|
|
|
|
|
|
CLR(Target, Size),
|
|
|
|
CMP(Target, Target, Size),
|
2021-10-02 05:06:53 +00:00
|
|
|
CMPA(Target, u8, Size),
|
2021-09-28 23:09:38 +00:00
|
|
|
|
|
|
|
DBcc(Condition, u16),
|
|
|
|
DIV(Target, Target, Size, Sign),
|
|
|
|
|
2021-09-29 19:24:04 +00:00
|
|
|
EOR(Target, Target, Size),
|
|
|
|
EORtoCCR(u8),
|
|
|
|
EORtoSR(u16),
|
|
|
|
EXG(Target, Target),
|
|
|
|
EXT(u8, Size),
|
|
|
|
|
|
|
|
ILLEGAL,
|
|
|
|
|
2021-09-28 23:09:38 +00:00
|
|
|
JMP(Target),
|
2021-09-29 19:24:04 +00:00
|
|
|
JSR(Target),
|
|
|
|
|
|
|
|
LEA(Target, u8),
|
|
|
|
LINK(u8, u16),
|
|
|
|
LSd(Target, Target, Size, ShiftDirection),
|
|
|
|
|
|
|
|
MOVE(Target, Target, Size),
|
2021-10-01 19:25:23 +00:00
|
|
|
MOVEA(Target, u8, Size),
|
2021-09-29 19:24:04 +00:00
|
|
|
MOVEfromSR(Target),
|
|
|
|
MOVEtoSR(Target),
|
|
|
|
MOVEtoCCR(Target),
|
|
|
|
MOVEC(Target, ControlRegister, Direction),
|
|
|
|
MOVEUSP(Target, Direction),
|
|
|
|
MOVEM(Target, Size, Direction, u16),
|
|
|
|
MOVEQ(u8, u8),
|
|
|
|
MUL(Target, Target, Size, Sign),
|
|
|
|
|
|
|
|
NBCD(Target),
|
|
|
|
NEG(Target, Size),
|
|
|
|
NEGX(Target, Size),
|
|
|
|
|
|
|
|
NOP,
|
|
|
|
NOT(Target, Size),
|
|
|
|
|
|
|
|
OR(Target, Target, Size),
|
|
|
|
ORtoCCR(u8),
|
|
|
|
ORtoSR(u16),
|
|
|
|
|
|
|
|
PEA(Target),
|
|
|
|
|
|
|
|
RESET,
|
|
|
|
ROd(Target, Target, Size, ShiftDirection),
|
|
|
|
ROXd(Target, Target, Size, ShiftDirection),
|
|
|
|
RTE,
|
|
|
|
RTR,
|
|
|
|
RTS,
|
|
|
|
|
|
|
|
//SBCD
|
|
|
|
//Scc
|
|
|
|
STOP(u16),
|
|
|
|
SUB(Target, Target, Size),
|
|
|
|
SWAP(u8),
|
|
|
|
|
|
|
|
TAS(Target),
|
|
|
|
TST(Target, Size),
|
|
|
|
TRAP(u8),
|
|
|
|
TRAPV,
|
|
|
|
|
|
|
|
UNLK(u8),
|
2021-09-28 23:09:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl MC68010 {
|
|
|
|
fn read_instruction_word(&mut self, space: &mut AddressSpace) -> Result<u16, Error> {
|
|
|
|
let word = space.read_beu16(self.pc as Address)?;
|
2021-09-30 04:52:38 +00:00
|
|
|
//debug!("{:#010x} {:#06x?}", self.pc, word);
|
2021-09-28 23:09:38 +00:00
|
|
|
self.pc += 2;
|
|
|
|
Ok(word)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_instruction_long(&mut self, space: &mut AddressSpace) -> Result<u32, Error> {
|
|
|
|
let word = space.read_beu32(self.pc as Address)?;
|
2021-09-30 04:52:38 +00:00
|
|
|
//debug!("{:#010x} {:#010x}", self.pc, word);
|
2021-09-28 23:09:38 +00:00
|
|
|
self.pc += 4;
|
|
|
|
Ok(word)
|
|
|
|
}
|
|
|
|
|
2021-09-30 00:11:48 +00:00
|
|
|
pub fn decode_one(&mut self, space: &mut AddressSpace) -> Result<Instruction, Error> {
|
2021-09-28 23:09:38 +00:00
|
|
|
let ins = self.read_instruction_word(space)?;
|
|
|
|
|
|
|
|
match ((ins & 0xF000) >> 12) as u8 {
|
|
|
|
OPCG_BIT_OPS => {
|
2021-09-29 19:24:04 +00:00
|
|
|
let optype = (ins & 0x0F00) >> 8;
|
|
|
|
|
|
|
|
if (ins & 0x3F) == 0b111100 {
|
|
|
|
match (ins & 0x00C0) >> 6 {
|
|
|
|
0b00 => {
|
|
|
|
let data = self.read_instruction_word(space)?;
|
|
|
|
match optype {
|
|
|
|
0b0000 => Ok(Instruction::ORtoCCR(data as u8)),
|
|
|
|
0b0001 => Ok(Instruction::ANDtoCCR(data as u8)),
|
|
|
|
0b1010 => Ok(Instruction::EORtoCCR(data as u8)),
|
|
|
|
_ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
0b01 => {
|
|
|
|
let data = self.read_instruction_word(space)?;
|
|
|
|
match optype {
|
|
|
|
0b0000 => Ok(Instruction::ORtoSR(data)),
|
|
|
|
0b0001 => Ok(Instruction::ANDtoSR(data)),
|
|
|
|
0b1010 => Ok(Instruction::EORtoSR(data)),
|
|
|
|
_ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
}
|
|
|
|
} else if (ins & 0x0100) == 0x0100 || (ins & 0x0F00) == 0x0800 {
|
|
|
|
let bitnum = if (ins & 0x0100) == 0x0100 {
|
|
|
|
Target::DirectDReg(get_high_reg(ins))
|
|
|
|
} else {
|
|
|
|
Target::Immediate(self.read_instruction_word(space)? as u32)
|
|
|
|
};
|
|
|
|
|
|
|
|
let target = self.decode_lower_effective_address(space, ins, Some(Size::Byte))?;
|
|
|
|
let size = match target {
|
|
|
|
Target::DirectAReg(_) | Target::DirectDReg(_) => Size::Long,
|
|
|
|
_ => Size::Byte,
|
|
|
|
};
|
|
|
|
|
|
|
|
match (ins & 0x00C0) >> 6 {
|
|
|
|
0b00 => Ok(Instruction::BTST(bitnum, target, size)),
|
|
|
|
0b01 => Ok(Instruction::BCHG(bitnum, target, size)),
|
|
|
|
0b10 => Ok(Instruction::BCLR(bitnum, target, size)),
|
|
|
|
0b11 => Ok(Instruction::BSET(bitnum, target, size)),
|
|
|
|
_ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
}
|
2021-09-28 23:09:38 +00:00
|
|
|
|
2021-09-29 19:24:04 +00:00
|
|
|
} else {
|
|
|
|
let size = get_size(ins);
|
|
|
|
let target = self.decode_lower_effective_address(space, ins, size)?;
|
|
|
|
let data = match size {
|
|
|
|
Some(Size::Long) => self.read_instruction_long(space)?,
|
2021-09-30 00:11:48 +00:00
|
|
|
Some(_) => self.read_instruction_word(space)? as u32,
|
2021-09-29 19:24:04 +00:00
|
|
|
None => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
};
|
|
|
|
|
|
|
|
match optype {
|
|
|
|
0b0000 => Ok(Instruction::OR(Target::Immediate(data), target, size.unwrap())),
|
|
|
|
0b0010 => Ok(Instruction::AND(Target::Immediate(data), target, size.unwrap())),
|
|
|
|
0b0100 => Ok(Instruction::SUB(Target::Immediate(data), target, size.unwrap())),
|
|
|
|
0b0110 => Ok(Instruction::ADD(Target::Immediate(data), target, size.unwrap())),
|
|
|
|
0b1010 => Ok(Instruction::EOR(Target::Immediate(data), target, size.unwrap())),
|
|
|
|
0b1100 => Ok(Instruction::CMP(Target::Immediate(data), target, size.unwrap())),
|
|
|
|
_ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
}
|
|
|
|
}
|
2021-09-28 23:09:38 +00:00
|
|
|
},
|
2021-09-29 19:24:04 +00:00
|
|
|
OPCG_MOVE_BYTE => {
|
|
|
|
let src = self.decode_lower_effective_address(space, ins, Some(Size::Byte))?;
|
|
|
|
let dest = self.decode_upper_effective_address(space, ins, Some(Size::Byte))?;
|
|
|
|
Ok(Instruction::MOVE(src, dest, Size::Byte))
|
2021-09-28 23:09:38 +00:00
|
|
|
},
|
|
|
|
OPCG_MOVE_LONG => {
|
2021-09-29 19:24:04 +00:00
|
|
|
let src = self.decode_lower_effective_address(space, ins, Some(Size::Long))?;
|
|
|
|
let dest = self.decode_upper_effective_address(space, ins, Some(Size::Long))?;
|
2021-10-01 19:25:23 +00:00
|
|
|
if let Target::DirectAReg(reg) = dest {
|
|
|
|
Ok(Instruction::MOVEA(src, reg, Size::Long))
|
|
|
|
} else {
|
|
|
|
Ok(Instruction::MOVE(src, dest, Size::Long))
|
|
|
|
}
|
2021-09-29 19:24:04 +00:00
|
|
|
},
|
|
|
|
OPCG_MOVE_WORD => {
|
|
|
|
let src = self.decode_lower_effective_address(space, ins, Some(Size::Word))?;
|
|
|
|
let dest = self.decode_upper_effective_address(space, ins, Some(Size::Word))?;
|
2021-10-01 19:25:23 +00:00
|
|
|
if let Target::DirectAReg(reg) = dest {
|
|
|
|
Ok(Instruction::MOVEA(src, reg, Size::Word))
|
|
|
|
} else {
|
|
|
|
Ok(Instruction::MOVE(src, dest, Size::Word))
|
|
|
|
}
|
2021-09-28 23:09:38 +00:00
|
|
|
},
|
|
|
|
OPCG_MISC => {
|
2021-09-29 19:24:04 +00:00
|
|
|
if (ins & 0b000101000000) == 0b000100000000 {
|
|
|
|
// CHK Instruction
|
|
|
|
panic!("Not Implemented");
|
|
|
|
} else if (ins & 0b000111000000) == 0b000111000000 {
|
|
|
|
let src = self.decode_lower_effective_address(space, ins, None)?;
|
2021-09-28 23:09:38 +00:00
|
|
|
let dest = get_high_reg(ins);
|
|
|
|
Ok(Instruction::LEA(src, dest))
|
2021-09-30 22:15:23 +00:00
|
|
|
} else if (ins & 0b100000000000) == 0b000000000000 {
|
2021-09-29 19:24:04 +00:00
|
|
|
let target = self.decode_lower_effective_address(space, ins, Some(Size::Word))?;
|
|
|
|
match (ins & 0x0700) >> 8 {
|
|
|
|
0b000 => {
|
|
|
|
match get_size(ins) {
|
|
|
|
Some(size) => Ok(Instruction::NEGX(target, size)),
|
|
|
|
None => Ok(Instruction::MOVEfromSR(target)),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
0b010 => {
|
|
|
|
match get_size(ins) {
|
|
|
|
Some(size) => Ok(Instruction::CLR(target, size)),
|
|
|
|
None => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
0b100 => {
|
|
|
|
match get_size(ins) {
|
|
|
|
Some(size) => Ok(Instruction::NEG(target, size)),
|
|
|
|
None => Ok(Instruction::MOVEtoCCR(target)),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
0b110 => {
|
|
|
|
match get_size(ins) {
|
|
|
|
Some(size) => Ok(Instruction::NOT(target, size)),
|
|
|
|
None => Ok(Instruction::MOVEtoSR(target)),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
}
|
2021-09-30 22:15:23 +00:00
|
|
|
} else if (ins & 0b101110000000) == 0b100010000000 {
|
|
|
|
let mode = get_low_mode(ins);
|
|
|
|
let size = if (ins & 0x0040) == 0 { Size::Word } else { Size::Long };
|
|
|
|
|
|
|
|
if mode == 0b000 {
|
|
|
|
Ok(Instruction::EXT(get_low_reg(ins), size))
|
|
|
|
} else {
|
|
|
|
let target = self.decode_lower_effective_address(space, ins, None)?;
|
|
|
|
let data = self.read_instruction_word(space)?;
|
2021-10-02 05:06:53 +00:00
|
|
|
let dir = if (ins & 0x0400) == 0 { Direction::ToTarget } else { Direction::FromTarget };
|
2021-09-30 22:15:23 +00:00
|
|
|
Ok(Instruction::MOVEM(target, size, dir, data))
|
2021-09-29 19:24:04 +00:00
|
|
|
}
|
|
|
|
} else if (ins & 0b111100000000) == 0b100000000000 {
|
|
|
|
let subselect = (ins & 0x01C0) >> 6;
|
|
|
|
let mode = get_low_mode(ins);
|
|
|
|
match (subselect, mode) {
|
2021-09-30 00:11:48 +00:00
|
|
|
(0b000, _) => {
|
2021-09-29 19:24:04 +00:00
|
|
|
let target = self.decode_lower_effective_address(space, ins, Some(Size::Byte))?;
|
|
|
|
Ok(Instruction::NBCD(target))
|
|
|
|
},
|
|
|
|
(0b001, 0b000) => {
|
|
|
|
Ok(Instruction::SWAP(get_low_reg(ins)))
|
|
|
|
},
|
2021-09-30 00:11:48 +00:00
|
|
|
(0b001, _) => {
|
2021-09-29 19:24:04 +00:00
|
|
|
let target = self.decode_lower_effective_address(space, ins, None)?;
|
|
|
|
Ok(Instruction::PEA(target))
|
|
|
|
},
|
|
|
|
_ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
}
|
2021-09-30 22:15:23 +00:00
|
|
|
} else if (ins & 0b111100000000) == 0b101000000000 {
|
|
|
|
let target = self.decode_lower_effective_address(space, ins, Some(Size::Word))?;
|
|
|
|
match get_size(ins) {
|
|
|
|
Some(size) => Ok(Instruction::TST(target, size)),
|
|
|
|
None => Ok(Instruction::TAS(target)),
|
|
|
|
}
|
2021-09-28 23:09:38 +00:00
|
|
|
} else if (ins & 0b111110000000) == 0b111010000000 {
|
2021-09-29 19:24:04 +00:00
|
|
|
let target = self.decode_lower_effective_address(space, ins, None)?;
|
2021-09-28 23:09:38 +00:00
|
|
|
if (ins & 0b01000000) == 0 {
|
|
|
|
Ok(Instruction::JSR(target))
|
|
|
|
} else {
|
|
|
|
Ok(Instruction::JMP(target))
|
|
|
|
}
|
2021-09-30 00:11:48 +00:00
|
|
|
} else if (ins & 0b111111110000) == 0b111001000000 {
|
|
|
|
Ok(Instruction::TRAP((ins & 0x000F) as u8))
|
2021-09-29 19:24:04 +00:00
|
|
|
} else if (ins & 0b111111110000) == 0b111001010000 {
|
|
|
|
let reg = get_low_reg(ins);
|
|
|
|
if (ins & 0b1000) == 0 {
|
|
|
|
let data = self.read_instruction_word(space)?;
|
|
|
|
Ok(Instruction::LINK(reg, data))
|
|
|
|
} else {
|
|
|
|
Ok(Instruction::UNLK(reg))
|
|
|
|
}
|
|
|
|
} else if (ins & 0b111111110000) == 0b111001100000 {
|
|
|
|
let reg = get_low_reg(ins);
|
|
|
|
let dir = if (ins & 0b1000) == 0 { Direction::FromTarget } else { Direction::ToTarget };
|
|
|
|
Ok(Instruction::MOVEUSP(Target::DirectAReg(reg), dir))
|
2021-09-28 23:09:38 +00:00
|
|
|
} else {
|
2021-09-29 19:24:04 +00:00
|
|
|
match ins & 0x0FFF {
|
|
|
|
0xAFC => Ok(Instruction::ILLEGAL),
|
|
|
|
0xE70 => Ok(Instruction::RESET),
|
|
|
|
0xE71 => Ok(Instruction::NOP),
|
|
|
|
0xE72 => {
|
|
|
|
let data = self.read_instruction_word(space)?;
|
|
|
|
Ok(Instruction::STOP(data))
|
|
|
|
},
|
|
|
|
0xE73 => Ok(Instruction::RTE),
|
|
|
|
0xE75 => Ok(Instruction::RTS),
|
|
|
|
0xE76 => Ok(Instruction::TRAPV),
|
|
|
|
0xE77 => Ok(Instruction::RTR),
|
|
|
|
0xE7A | 0xE7B => {
|
|
|
|
let dir = if ins & 0x01 == 0 { Direction::ToTarget } else { Direction::FromTarget };
|
|
|
|
let ins2 = self.read_instruction_word(space)?;
|
|
|
|
let target = match ins2 & 0x8000 {
|
|
|
|
0 => Target::DirectDReg(((ins2 & 0x7000) >> 12) as u8),
|
|
|
|
_ => Target::DirectAReg(((ins2 & 0x7000) >> 12) as u8),
|
|
|
|
};
|
|
|
|
let creg = match ins2 & 0xFFF {
|
|
|
|
0x801 => ControlRegister::VBR,
|
|
|
|
_ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
};
|
|
|
|
Ok(Instruction::MOVEC(target, creg, dir))
|
|
|
|
},
|
|
|
|
_ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
}
|
2021-09-28 23:09:38 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
OPCG_ADDQ_SUBQ => {
|
2021-09-29 19:24:04 +00:00
|
|
|
let size = match get_size(ins) {
|
|
|
|
Some(size) => size,
|
|
|
|
None => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
};
|
|
|
|
|
|
|
|
let target = self.decode_lower_effective_address(space, ins, Some(size))?;
|
|
|
|
let mut data = ((ins & 0x0E00) >> 9) as u32;
|
|
|
|
if data == 0 {
|
|
|
|
data = 8;
|
|
|
|
}
|
2021-09-28 23:09:38 +00:00
|
|
|
|
2021-09-29 19:24:04 +00:00
|
|
|
if (ins & 0x0100) == 0 {
|
|
|
|
Ok(Instruction::ADD(Target::Immediate(data), target, size))
|
|
|
|
} else {
|
|
|
|
Ok(Instruction::SUB(Target::Immediate(data), target, size))
|
|
|
|
}
|
2021-09-28 23:09:38 +00:00
|
|
|
},
|
|
|
|
OPCG_BRANCH => {
|
2021-09-30 04:52:38 +00:00
|
|
|
let mut disp = ((ins & 0xFF) as i8) as u16;
|
2021-09-29 19:24:04 +00:00
|
|
|
if disp == 0 {
|
|
|
|
disp = self.read_instruction_word(space)?;
|
|
|
|
}
|
|
|
|
let condition = get_condition(ins);
|
|
|
|
match condition {
|
2021-09-30 00:11:48 +00:00
|
|
|
Condition::True => Ok(Instruction::BRA(disp as i16)),
|
|
|
|
Condition::False => Ok(Instruction::BSR(disp as i16)),
|
|
|
|
_ => Ok(Instruction::Bcc(condition, disp as i16)),
|
2021-09-29 19:24:04 +00:00
|
|
|
}
|
2021-09-28 23:09:38 +00:00
|
|
|
},
|
|
|
|
OPCG_MOVEQ => {
|
2021-09-29 19:24:04 +00:00
|
|
|
// TODO make sure the 9th bit is 0
|
|
|
|
let reg = get_high_reg(ins);
|
|
|
|
let data = (ins & 0xFF) as u8;
|
|
|
|
Ok(Instruction::MOVEQ(data, reg))
|
2021-09-28 23:09:38 +00:00
|
|
|
},
|
|
|
|
OPCG_DIV_OR => {
|
2021-09-29 19:24:04 +00:00
|
|
|
let size = get_size(ins);
|
|
|
|
|
|
|
|
if size.is_none() {
|
|
|
|
let sign = if (ins & 0x0100) == 0 { Sign::Unsigned } else { Sign::Signed };
|
|
|
|
let data_reg = Target::DirectDReg(get_high_reg(ins));
|
|
|
|
let effective_addr = self.decode_lower_effective_address(space, ins, size)?;
|
|
|
|
Ok(Instruction::DIV(effective_addr, data_reg, Size::Word, sign))
|
|
|
|
} else if (ins & 0b000111110000) == 0b000100000000 {
|
|
|
|
// TODO SBCD
|
|
|
|
panic!("Not Implemented");
|
|
|
|
} else {
|
|
|
|
let data_reg = Target::DirectDReg(get_high_reg(ins));
|
|
|
|
let effective_addr = self.decode_lower_effective_address(space, ins, size)?;
|
|
|
|
let (from, to) = if (ins & 0x0100) == 0 { (effective_addr, data_reg) } else { (data_reg, effective_addr) };
|
|
|
|
Ok(Instruction::OR(from, to, size.unwrap()))
|
|
|
|
}
|
2021-09-28 23:09:38 +00:00
|
|
|
},
|
|
|
|
OPCG_SUB => {
|
2021-09-29 19:24:04 +00:00
|
|
|
// TODO need to decode the SUBX instruction (would likely be erroneously decoded atm)
|
|
|
|
let reg = get_high_reg(ins);
|
|
|
|
let dir = (ins & 0x0100) >> 8;
|
|
|
|
let size = get_size(ins);
|
|
|
|
match size {
|
|
|
|
Some(size) => {
|
|
|
|
let target = self.decode_lower_effective_address(space, ins, Some(size))?;
|
|
|
|
if dir == 0 {
|
|
|
|
Ok(Instruction::SUB(target, Target::DirectDReg(reg), size))
|
|
|
|
} else {
|
|
|
|
Ok(Instruction::SUB(Target::DirectDReg(reg), target, size))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
None => {
|
|
|
|
let size = if dir == 0 { Size::Word } else { Size::Long };
|
|
|
|
let target = self.decode_lower_effective_address(space, ins, Some(size))?;
|
|
|
|
Ok(Instruction::SUB(target, Target::DirectAReg(reg), size))
|
|
|
|
},
|
|
|
|
}
|
2021-09-28 23:09:38 +00:00
|
|
|
},
|
|
|
|
OPCG_CMP_EOR => {
|
2021-09-29 19:24:04 +00:00
|
|
|
let reg = get_high_reg(ins);
|
|
|
|
let optype = (ins & 0x0100) >> 8;
|
|
|
|
let size = get_size(ins);
|
|
|
|
match (optype, size) {
|
|
|
|
(0b1, Some(size)) => {
|
|
|
|
// TODO need to decode the CMPM instruction (mode == 0b001) (would likely be erroneously decoded atm)
|
|
|
|
let target = self.decode_lower_effective_address(space, ins, Some(size))?;
|
|
|
|
Ok(Instruction::EOR(Target::DirectDReg(reg), target, size))
|
|
|
|
},
|
|
|
|
(0b0, Some(size)) => {
|
|
|
|
let target = self.decode_lower_effective_address(space, ins, Some(size))?;
|
2021-10-02 05:06:53 +00:00
|
|
|
Ok(Instruction::CMP(target, Target::DirectDReg(reg), size))
|
2021-09-29 19:24:04 +00:00
|
|
|
},
|
2021-09-30 00:11:48 +00:00
|
|
|
(_, None) => {
|
2021-09-29 19:24:04 +00:00
|
|
|
let size = if optype == 0 { Size::Word } else { Size::Long };
|
|
|
|
let target = self.decode_lower_effective_address(space, ins, Some(size))?;
|
2021-10-02 05:06:53 +00:00
|
|
|
Ok(Instruction::CMPA(target, reg, size))
|
2021-09-29 19:24:04 +00:00
|
|
|
},
|
|
|
|
_ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
}
|
2021-09-28 23:09:38 +00:00
|
|
|
},
|
2021-09-29 19:24:04 +00:00
|
|
|
OPCG_MUL_AND => {
|
|
|
|
let size = get_size(ins);
|
|
|
|
|
|
|
|
if size.is_none() {
|
|
|
|
let sign = if (ins & 0x0100) == 0 { Sign::Unsigned } else { Sign::Signed };
|
|
|
|
let data_reg = Target::DirectDReg(get_high_reg(ins));
|
|
|
|
let effective_addr = self.decode_lower_effective_address(space, ins, size)?;
|
|
|
|
Ok(Instruction::MUL(effective_addr, data_reg, Size::Word, sign))
|
|
|
|
} else if (ins & 0b000111110000) == 0b000100000000 {
|
|
|
|
// TODO ABCD or EXG
|
|
|
|
panic!("Not Implemented");
|
|
|
|
} else {
|
|
|
|
let data_reg = Target::DirectDReg(get_high_reg(ins));
|
|
|
|
let effective_addr = self.decode_lower_effective_address(space, ins, size)?;
|
|
|
|
let (from, to) = if (ins & 0x0100) == 0 { (effective_addr, data_reg) } else { (data_reg, effective_addr) };
|
|
|
|
Ok(Instruction::AND(from, to, size.unwrap()))
|
|
|
|
}
|
2021-09-28 23:09:38 +00:00
|
|
|
},
|
|
|
|
OPCG_ADD => {
|
2021-09-29 19:24:04 +00:00
|
|
|
// TODO need to decode the ADDX instruction (would likely be erroneously decoded atm)
|
|
|
|
let reg = get_high_reg(ins);
|
|
|
|
let dir = (ins & 0x0100) >> 8;
|
|
|
|
let size = get_size(ins);
|
|
|
|
match size {
|
|
|
|
Some(size) => {
|
|
|
|
let target = self.decode_lower_effective_address(space, ins, Some(size))?;
|
|
|
|
if dir == 0 {
|
|
|
|
Ok(Instruction::ADD(target, Target::DirectDReg(reg), size))
|
|
|
|
} else {
|
|
|
|
Ok(Instruction::ADD(Target::DirectDReg(reg), target, size))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
None => {
|
|
|
|
let size = if dir == 0 { Size::Word } else { Size::Long };
|
|
|
|
let target = self.decode_lower_effective_address(space, ins, Some(size))?;
|
|
|
|
Ok(Instruction::ADD(target, Target::DirectAReg(reg), size))
|
|
|
|
},
|
|
|
|
}
|
2021-09-28 23:09:38 +00:00
|
|
|
},
|
|
|
|
OPCG_SHIFT => {
|
2021-09-29 19:24:04 +00:00
|
|
|
let dir = if (ins & 0x0100) == 0 { ShiftDirection::Right } else { ShiftDirection::Left };
|
|
|
|
match get_size(ins) {
|
|
|
|
Some(size) => {
|
|
|
|
let reg = get_low_reg(ins);
|
|
|
|
let rotation = get_high_reg(ins);
|
|
|
|
let count = if (ins & 0x0020) == 0 {
|
2021-10-01 22:38:21 +00:00
|
|
|
Target::Immediate(if rotation != 0 { rotation as u32 } else { 8 })
|
2021-09-29 19:24:04 +00:00
|
|
|
} else {
|
|
|
|
Target::DirectDReg(rotation)
|
|
|
|
};
|
|
|
|
|
|
|
|
match (ins & 0x0018) >> 3 {
|
|
|
|
0b00 => Ok(Instruction::ASd(count, Target::DirectDReg(reg), size, dir)),
|
|
|
|
0b01 => Ok(Instruction::LSd(count, Target::DirectDReg(reg), size, dir)),
|
|
|
|
0b10 => Ok(Instruction::ROXd(count, Target::DirectDReg(reg), size, dir)),
|
|
|
|
0b11 => Ok(Instruction::ROd(count, Target::DirectDReg(reg), size, dir)),
|
|
|
|
_ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
None => {
|
|
|
|
let target = self.decode_lower_effective_address(space, ins, Some(Size::Word))?;
|
|
|
|
let count = Target::Immediate(1);
|
|
|
|
|
|
|
|
match (ins & 0x0600) >> 9 {
|
|
|
|
0b00 => Ok(Instruction::ASd(count, target, Size::Word, dir)),
|
|
|
|
0b01 => Ok(Instruction::LSd(count, target, Size::Word, dir)),
|
|
|
|
0b10 => Ok(Instruction::ROXd(count, target, Size::Word, dir)),
|
|
|
|
0b11 => Ok(Instruction::ROd(count, target, Size::Word, dir)),
|
|
|
|
_ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
2021-09-28 23:09:38 +00:00
|
|
|
},
|
|
|
|
_ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-29 19:24:04 +00:00
|
|
|
fn decode_lower_effective_address(&mut self, space: &mut AddressSpace, ins: u16, size: Option<Size>) -> Result<Target, Error> {
|
2021-09-28 23:09:38 +00:00
|
|
|
let reg = get_low_reg(ins);
|
2021-09-29 19:24:04 +00:00
|
|
|
let mode = get_low_mode(ins);
|
|
|
|
self.get_mode_as_target(space, mode, reg, size)
|
2021-09-28 23:09:38 +00:00
|
|
|
}
|
|
|
|
|
2021-09-29 19:24:04 +00:00
|
|
|
fn decode_upper_effective_address(&mut self, space: &mut AddressSpace, ins: u16, size: Option<Size>) -> Result<Target, Error> {
|
|
|
|
let reg = get_high_reg(ins);
|
|
|
|
let mode = get_high_mode(ins);
|
|
|
|
self.get_mode_as_target(space, mode, reg, size)
|
|
|
|
}
|
|
|
|
|
2021-10-01 22:38:21 +00:00
|
|
|
fn decode_brief_extension_word(&self, brief_extension: u16) -> (RegisterType, u8, i32, Size) {
|
|
|
|
let data = sign_extend_to_long((brief_extension & 0x00FF) as u32, Size::Byte);
|
2021-09-30 00:11:48 +00:00
|
|
|
let xreg = ((brief_extension & 0x7000) >> 12) as u8;
|
2021-09-29 19:24:04 +00:00
|
|
|
let size = if (brief_extension & 0x0800) == 0 { Size::Word } else { Size::Long };
|
|
|
|
|
2021-09-30 00:11:48 +00:00
|
|
|
let rtype = if (brief_extension & 0x8000) == 0 { RegisterType::Data } else { RegisterType::Address };
|
2021-09-29 19:24:04 +00:00
|
|
|
|
2021-09-30 00:11:48 +00:00
|
|
|
(rtype, xreg, data, size)
|
2021-09-29 19:24:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_mode_as_target(&mut self, space: &mut AddressSpace, mode: u8, reg: u8, size: Option<Size>) -> Result<Target, Error> {
|
2021-09-28 23:09:38 +00:00
|
|
|
let value = match mode {
|
2021-09-29 19:24:04 +00:00
|
|
|
0b000 => Target::DirectDReg(reg),
|
|
|
|
0b001 => Target::DirectAReg(reg),
|
2021-09-28 23:09:38 +00:00
|
|
|
0b010 => Target::IndirectAReg(reg),
|
2021-09-29 19:24:04 +00:00
|
|
|
0b011 => Target::IndirectARegInc(reg),
|
|
|
|
0b100 => Target::IndirectARegDec(reg),
|
2021-09-28 23:09:38 +00:00
|
|
|
0b101 => {
|
2021-10-01 22:38:21 +00:00
|
|
|
let data = sign_extend_to_long(self.read_instruction_word(space)? as u32, Size::Word);
|
|
|
|
Target::IndirectARegOffset(reg, data)
|
2021-09-28 23:09:38 +00:00
|
|
|
},
|
2021-09-29 19:24:04 +00:00
|
|
|
0b110 => {
|
|
|
|
let brief_extension = self.read_instruction_word(space)?;
|
2021-09-30 00:11:48 +00:00
|
|
|
let (rtype, xreg, data, size) = self.decode_brief_extension_word(brief_extension);
|
2021-10-01 22:38:21 +00:00
|
|
|
Target::IndirectARegXRegOffset(reg, rtype, xreg, data, size)
|
2021-09-29 19:24:04 +00:00
|
|
|
},
|
2021-09-28 23:09:38 +00:00
|
|
|
0b111 => {
|
|
|
|
match reg {
|
|
|
|
0b000 => {
|
2021-10-02 05:06:53 +00:00
|
|
|
let value = sign_extend_to_long(self.read_instruction_word(space)? as u32, Size::Word) as u32;
|
2021-09-28 23:09:38 +00:00
|
|
|
Target::IndirectMemory(value)
|
|
|
|
},
|
|
|
|
0b001 => {
|
|
|
|
let value = self.read_instruction_long(space)?;
|
|
|
|
Target::IndirectMemory(value)
|
|
|
|
},
|
|
|
|
0b010 => {
|
2021-10-01 22:38:21 +00:00
|
|
|
let data = sign_extend_to_long(self.read_instruction_word(space)? as u32, Size::Word);
|
|
|
|
Target::IndirectPCOffset(data)
|
2021-09-28 23:09:38 +00:00
|
|
|
},
|
2021-09-29 19:24:04 +00:00
|
|
|
0b011 => {
|
|
|
|
let brief_extension = self.read_instruction_word(space)?;
|
2021-09-30 00:11:48 +00:00
|
|
|
let (rtype, xreg, data, size) = self.decode_brief_extension_word(brief_extension);
|
2021-10-01 22:38:21 +00:00
|
|
|
Target::IndirectPCXRegOffset(rtype, xreg, data, size)
|
2021-09-29 19:24:04 +00:00
|
|
|
},
|
|
|
|
0b100 => {
|
|
|
|
let data = match size {
|
|
|
|
Some(Size::Byte) | Some(Size::Word) => self.read_instruction_word(space)? as u32,
|
|
|
|
Some(Size::Long) => self.read_instruction_long(space)?,
|
|
|
|
None => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
};
|
|
|
|
Target::Immediate(data)
|
|
|
|
},
|
2021-09-28 23:09:38 +00:00
|
|
|
_ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)),
|
|
|
|
};
|
|
|
|
Ok(value)
|
|
|
|
}
|
2021-09-29 19:24:04 +00:00
|
|
|
|
2021-09-28 23:09:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn get_high_reg(ins: u16) -> u8 {
|
2021-09-29 19:24:04 +00:00
|
|
|
((ins & 0x0E00) >> 9) as u8
|
2021-09-28 23:09:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn get_low_reg(ins: u16) -> u8 {
|
|
|
|
(ins & 0x0007) as u8
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
2021-09-29 19:24:04 +00:00
|
|
|
fn get_high_mode(ins: u16) -> u8 {
|
|
|
|
((ins & 0x01C0) >> 6) as u8
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn get_low_mode(ins: u16) -> u8 {
|
2021-09-28 23:09:38 +00:00
|
|
|
((ins & 0x0038) >> 3) as u8
|
|
|
|
}
|
|
|
|
|
2021-09-29 19:24:04 +00:00
|
|
|
#[inline(always)]
|
|
|
|
fn get_size(ins: u16) -> Option<Size> {
|
|
|
|
match (ins & 0x00C0) >> 6 {
|
|
|
|
0b00 => Some(Size::Byte),
|
|
|
|
0b01 => Some(Size::Word),
|
|
|
|
0b10 => Some(Size::Long),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn get_condition(ins: u16) -> Condition {
|
|
|
|
match (ins & 0x0F00) >> 8 {
|
|
|
|
0b0000 => Condition::True,
|
|
|
|
0b0001 => Condition::False,
|
|
|
|
0b0010 => Condition::High,
|
|
|
|
0b0011 => Condition::LowOrSame,
|
|
|
|
0b0100 => Condition::CarryClear,
|
|
|
|
0b0101 => Condition::CarrySet,
|
|
|
|
0b0110 => Condition::NotEqual,
|
|
|
|
0b0111 => Condition::Equal,
|
|
|
|
0b1000 => Condition::OverflowClear,
|
|
|
|
0b1001 => Condition::OverflowSet,
|
|
|
|
0b1010 => Condition::Plus,
|
|
|
|
0b1011 => Condition::Minus,
|
|
|
|
0b1100 => Condition::GreaterThanOrEqual,
|
|
|
|
0b1101 => Condition::LessThan,
|
|
|
|
0b1110 => Condition::GreaterThan,
|
|
|
|
0b1111 => Condition::LessThanOrEqual,
|
|
|
|
|
|
|
|
_ => Condition::True,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-28 23:09:38 +00:00
|
|
|
|
2021-09-30 00:11:48 +00:00
|
|
|
impl Size {
|
|
|
|
pub fn in_bytes(&self) -> u32 {
|
|
|
|
match self {
|
|
|
|
Size::Byte => 1,
|
|
|
|
Size::Word => 2,
|
|
|
|
Size::Long => 4,
|
|
|
|
}
|
|
|
|
}
|
2021-10-01 03:27:01 +00:00
|
|
|
|
|
|
|
pub fn in_bits(&self) -> u32 {
|
|
|
|
match self {
|
|
|
|
Size::Byte => 8,
|
|
|
|
Size::Word => 16,
|
|
|
|
Size::Long => 32,
|
|
|
|
}
|
|
|
|
}
|
2021-09-28 23:09:38 +00:00
|
|
|
}
|
2021-09-30 00:11:48 +00:00
|
|
|
|
2021-10-01 22:38:21 +00:00
|
|
|
pub fn sign_extend_to_long(value: u32, from: Size) -> i32 {
|
|
|
|
match from {
|
|
|
|
Size::Byte => ((value as u8) as i8) as i32,
|
|
|
|
Size::Word => ((value as u16) as i16) as i32,
|
|
|
|
Size::Long => value as i32,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|