use crate::core::space::{AddressSpace, MemAttrs};
use crate::core::value::Width;
use super::isa::{Arg, Cond, Insn, Mode, Op, Size, decode, ea_of};
use super::{ADDRESS_MASK, Config, Lines, flags, vector};
mod fc {
pub(super) const USER_DATA: u8 = 1;
pub(super) const USER_PROGRAM: u8 = 2;
pub(super) const SUPER_DATA: u8 = 5;
pub(super) const SUPER_PROGRAM: u8 = 6;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct State {
pub d: [u32; 8],
pub a: [u32; 8],
pub other_sp: u32,
pub pc: u32,
pub sr: u16,
pub prefetch: [u16; 2],
pub cycles: u64,
pub halted: bool,
pub stopped: bool,
pub reset_pending: bool,
pub faults: u64,
pub last_fault: u32,
}
impl State {
pub(super) const fn new() -> State {
State {
d: [0; 8],
a: [0; 8],
other_sp: 0,
pc: 0,
sr: flags::S | flags::IPL,
prefetch: [0; 2],
cycles: 0,
halted: false,
stopped: false,
reset_pending: true,
faults: 0,
last_fault: 0,
}
}
#[inline]
pub(super) const fn supervisor(&self) -> bool {
self.sr & flags::S != 0
}
#[must_use]
pub(super) const fn usp(&self) -> u32 {
if self.supervisor() {
self.other_sp
} else {
self.a[7]
}
}
#[must_use]
pub(super) const fn ssp(&self) -> u32 {
if self.supervisor() {
self.a[7]
} else {
self.other_sp
}
}
pub(super) const fn set_usp(&mut self, value: u32) {
if self.supervisor() {
self.other_sp = value;
} else {
self.a[7] = value;
}
}
pub(super) const fn set_sr(&mut self, value: u16) {
let value = value & flags::IMPLEMENTED;
if (value & flags::S) != (self.sr & flags::S) {
let active = self.a[7];
self.a[7] = self.other_sp;
self.other_sp = active;
}
self.sr = value;
}
#[inline]
pub(super) const fn ccr(&self) -> u8 {
(self.sr & flags::CCR) as u8
}
#[inline]
pub(super) const fn flag(&self, mask: u16) -> bool {
self.sr & mask != 0
}
#[inline]
pub(super) const fn ipl_mask(&self) -> u8 {
((self.sr & flags::IPL) >> 8) as u8
}
#[must_use]
pub(super) const fn test(&self, cond: Cond) -> bool {
let c = self.flag(flags::C);
let v = self.flag(flags::V);
let z = self.flag(flags::Z);
let n = self.flag(flags::N);
match cond.0 {
0x0 => true,
0x1 => false,
0x2 => !c && !z,
0x3 => c || z,
0x4 => !c,
0x5 => c,
0x6 => !z,
0x7 => z,
0x8 => !v,
0x9 => v,
0xa => !n,
0xb => n,
0xc => n == v,
0xd => n != v,
0xe => !z && (n == v),
_ => z || (n != v),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Trap {
Address {
addr: u32,
read: bool,
fc: u8,
},
Bus {
addr: u32,
read: bool,
fc: u8,
},
Vectored {
vector: u8,
pc: u32,
},
}
impl Trap {
const fn at(vector: u8, pc: u32) -> Trap {
Trap::Vectored { vector, pc }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Loc {
D(u8),
A(u8),
Mem(u32),
Value(u32),
Prefetched(u32, u32),
}
pub(super) struct Exec<'a> {
state: &'a mut State,
space: &'a AddressSpace,
cfg: &'a Config,
lines: &'a Lines,
opcode: u16,
used: u64,
source_was_memory: bool,
deferred_postincrement: Option<(u8, u32)>,
prologue: u32,
deferred_slides: u32,
}
impl<'a> Exec<'a> {
pub(super) fn new(
state: &'a mut State,
space: &'a AddressSpace,
cfg: &'a Config,
lines: &'a Lines,
) -> Exec<'a> {
Exec {
state,
space,
cfg,
lines,
opcode: 0,
used: 0,
prologue: 4,
source_was_memory: false,
deferred_postincrement: None,
deferred_slides: 0,
}
}
pub(super) fn step(&mut self) -> u64 {
if self.state.reset_pending {
self.reset_sequence();
return self.used;
}
if self.state.halted {
return 0;
}
if let Some(level) = self.pending_interrupt() {
self.state.stopped = false;
self.take_interrupt(level);
return self.used;
}
if self.state.stopped {
self.internal(4);
return self.used;
}
self.instruction();
self.used
}
fn internal(&mut self, cycles: u32) {
self.used = self.used.wrapping_add(u64::from(cycles));
self.state.cycles = self.state.cycles.wrapping_add(u64::from(cycles));
}
fn attrs(&self) -> MemAttrs {
MemAttrs::DEFAULT
.with_requester(self.cfg.requester)
.with_privileged(self.state.supervisor())
}
fn data_fc(&self) -> u8 {
if self.state.supervisor() {
fc::SUPER_DATA
} else {
fc::USER_DATA
}
}
fn program_fc(&self) -> u8 {
if self.state.supervisor() {
fc::SUPER_PROGRAM
} else {
fc::USER_PROGRAM
}
}
fn read_byte(&mut self, addr: u32) -> Result<u8, Trap> {
self.internal(4);
let fc = self.data_fc();
match self
.space
.read(u64::from(addr & ADDRESS_MASK), Width::U8, self.attrs())
{
Ok(v) => Ok(v as u8),
Err(_) => Err(self.bus_fault(addr, true, fc)),
}
}
fn read_word(&mut self, addr: u32) -> Result<u16, Trap> {
let fc = self.data_fc();
self.read_word_fc(addr, fc)
}
fn read_word_fc(&mut self, addr: u32, fc: u8) -> Result<u16, Trap> {
if addr & 1 != 0 {
return Err(Trap::Address {
addr,
read: true,
fc,
});
}
self.internal(4);
match self
.space
.read(u64::from(addr & ADDRESS_MASK), Width::U16, self.attrs())
{
Ok(v) => Ok(v as u16),
Err(_) => Err(self.bus_fault(addr, true, fc)),
}
}
fn read_long(&mut self, addr: u32) -> Result<u32, Trap> {
let hi = self.read_word(addr)?;
let lo = self.read_word(addr.wrapping_add(2))?;
Ok((u32::from(hi) << 16) | u32::from(lo))
}
fn write_byte(&mut self, addr: u32, value: u8) -> Result<(), Trap> {
self.internal(4);
let fc = self.data_fc();
match self.space.write(
u64::from(addr & ADDRESS_MASK),
Width::U8,
u64::from(value),
self.attrs(),
) {
Ok(()) => Ok(()),
Err(_) => Err(self.bus_fault(addr, false, fc)),
}
}
fn write_word(&mut self, addr: u32, value: u16) -> Result<(), Trap> {
let fc = self.data_fc();
if addr & 1 != 0 {
return Err(Trap::Address {
addr,
read: false,
fc,
});
}
self.internal(4);
match self.space.write(
u64::from(addr & ADDRESS_MASK),
Width::U16,
u64::from(value),
self.attrs(),
) {
Ok(()) => Ok(()),
Err(_) => Err(self.bus_fault(addr, false, fc)),
}
}
fn write_long(&mut self, addr: u32, value: u32) -> Result<(), Trap> {
self.write_word(addr, (value >> 16) as u16)?;
self.write_word(addr.wrapping_add(2), value as u16)
}
fn write_long_low_first(&mut self, addr: u32, value: u32) -> Result<(), Trap> {
self.write_word(addr.wrapping_add(2), value as u16)?;
self.write_word(addr, (value >> 16) as u16)
}
fn bus_fault(&mut self, addr: u32, read: bool, fc: u8) -> Trap {
self.state.faults = self.state.faults.wrapping_add(1);
self.state.last_fault = addr;
Trap::Bus { addr, read, fc }
}
fn slide(&mut self) -> Result<(), Trap> {
let fetch = self.state.pc.wrapping_add(4);
let fc = self.program_fc();
let word = self.read_word_fc(fetch, fc)?;
self.state.prefetch[0] = self.state.prefetch[1];
self.state.prefetch[1] = word;
self.state.pc = self.state.pc.wrapping_add(2);
Ok(())
}
fn ext(&mut self, delay: u32) -> Result<u16, Trap> {
let word = self.state.prefetch[1];
if delay != 0 {
self.internal(delay);
}
self.slide()?;
Ok(word)
}
fn ext_deferred(&mut self) -> u16 {
let word = self.state.prefetch[1];
self.deferred_slides += 1;
word
}
fn settle(&mut self) -> Result<(), Trap> {
while self.deferred_slides > 0 {
self.deferred_slides -= 1;
self.slide()?;
}
self.slide()
}
fn refill(&mut self, target: u32, gap: u32) -> Result<(), Trap> {
self.deferred_slides = 0;
let fc = self.program_fc();
self.state.pc = target.wrapping_sub(4);
let first = self.read_word_fc(target, fc)?;
self.state.pc = target.wrapping_sub(2);
if gap != 0 {
self.internal(gap);
}
let second = self.read_word_fc(target.wrapping_add(2), fc)?;
self.state.pc = target;
self.state.prefetch = [first, second];
Ok(())
}
fn reset_sequence(&mut self) {
self.state.reset_pending = false;
self.state.halted = false;
self.state.stopped = false;
self.state.set_sr(flags::S | flags::IPL);
self.internal(4);
let outcome = (|| -> Result<(), Trap> {
let ssp = self.read_long(0)?;
let pc = self.read_long(4)?;
self.state.a[7] = ssp;
self.refill(pc, 0)
})();
if outcome.is_err() {
self.state.halted = true;
}
}
fn pending_interrupt(&self) -> Option<u8> {
if self.lines.take_level_seven() {
return Some(7);
}
let level = self.lines.ipl();
if level != 0 && level > self.state.ipl_mask() {
Some(level)
} else {
None
}
}
fn take_interrupt(&mut self, level: u8) {
let vector = self
.lines
.take_vector()
.unwrap_or(vector::AUTOVECTOR_BASE.wrapping_add(level));
let pc = self.state.pc;
let sr = self.state.sr;
let raised = (sr & !flags::IPL) | (u16::from(level) << 8);
self.prologue = 14;
self.enter_exception(vector, pc, raised, None);
}
fn enter_exception(&mut self, vector: u8, pc: u32, new_sr: u16, group0: Option<Group0>) {
let saved_sr = self.state.sr;
self.state.set_sr((new_sr | flags::S) & !flags::T);
self.state.stopped = false;
self.internal(self.prologue);
let outcome = (|| -> Result<(), Trap> {
let mut sp = self.state.a[7];
sp = sp.wrapping_sub(2);
self.write_word(sp, pc as u16)?;
sp = sp.wrapping_sub(4);
self.write_word(sp, saved_sr)?;
self.write_word(sp.wrapping_add(2), (pc >> 16) as u16)?;
if let Some(g0) = group0 {
sp = sp.wrapping_sub(2);
self.write_word(sp, g0.ir)?;
sp = sp.wrapping_sub(2);
self.write_word(sp, g0.addr as u16)?;
sp = sp.wrapping_sub(4);
self.write_word(sp, g0.ssw)?;
self.write_word(sp.wrapping_add(2), (g0.addr >> 16) as u16)?;
}
self.state.a[7] = sp;
let base = u32::from(vector) * 4;
let target = self.read_long(base)?;
self.refill(target, 2)
})();
if outcome.is_err() {
self.state.halted = true;
}
}
fn special_status(&self, read: bool, fc: u8) -> u16 {
let not_instruction = fc == self.program_fc();
(self.opcode & !0x001f)
| (u16::from(read) << 4)
| (u16::from(not_instruction) << 3)
| u16::from(fc & 7)
}
fn service(&mut self, trap: Trap) {
match trap {
Trap::Address { addr, read, fc } | Trap::Bus { addr, read, fc } => {
let vector = if matches!(trap, Trap::Address { .. }) {
vector::ADDRESS_ERROR
} else {
vector::BUS_ERROR
};
let ssw = self.special_status(read, fc);
let g0 = Group0 {
ssw,
addr,
ir: self.opcode,
};
let pc = self.state.pc;
let sr = self.state.sr;
self.enter_exception(vector, pc, sr, Some(g0));
}
Trap::Vectored { vector, pc } => {
let sr = self.state.sr;
self.enter_exception(vector, pc, sr, None);
}
}
}
fn instruction(&mut self) {
self.opcode = self.state.prefetch[0];
let pc0 = self.state.pc;
let traced = self.state.flag(flags::T);
let insn = decode(self.opcode);
let outcome = if insn.privileged && !self.state.supervisor() {
Err(Trap::at(vector::PRIVILEGE, pc0))
} else {
self.execute(insn, pc0)
};
match outcome {
Ok(()) => {
if traced {
let pc = self.state.pc;
self.service(Trap::at(vector::TRACE, pc));
}
}
Err(trap) => self.service(trap),
}
}
#[allow(clippy::too_many_lines)]
fn execute(&mut self, insn: Insn, pc0: u32) -> Result<(), Trap> {
let opcode = self.opcode;
let size = match insn.size.resolve(opcode) {
Some(size) => size,
None => Size::Word,
};
match insn.op {
Op::Illegal => Err(Trap::at(vector::ILLEGAL, pc0)),
Op::LineA => Err(Trap::at(vector::LINE_A, pc0)),
Op::LineF => Err(Trap::at(vector::LINE_F, pc0)),
Op::Nop => self.settle(),
Op::Reset => {
self.internal(128);
self.lines.pulse_reset();
self.settle()
}
Op::Stop => {
let word = self.ext(0)?;
self.state.set_sr(word);
self.settle()?;
self.state.stopped = true;
Ok(())
}
Op::Move | Op::Movea => self.op_move(insn, size),
Op::Moveq => {
let value = i32::from(opcode as i8) as u32;
self.state.d[reg_hi(opcode)] = value;
self.set_logic_flags(value, Size::Long);
self.settle()
}
Op::Add | Op::Addi | Op::Addq => self.op_binary(insn, size, BinOp::Add),
Op::Sub | Op::Subi | Op::Subq => self.op_binary(insn, size, BinOp::Sub),
Op::And | Op::Andi => self.op_binary(insn, size, BinOp::And),
Op::Or | Op::Ori => self.op_binary(insn, size, BinOp::Or),
Op::Eor | Op::Eori => self.op_binary(insn, size, BinOp::Eor),
Op::Cmp | Op::Cmpi => self.op_compare(insn, size),
Op::Cmpm => self.op_cmpm(size),
Op::Adda | Op::Suba => self.op_adda(insn, size),
Op::Cmpa => self.op_cmpa(size),
Op::Addx | Op::Subx => self.op_addx(insn, size),
Op::Abcd | Op::Sbcd => self.op_bcd(insn),
Op::Nbcd => self.op_nbcd(),
Op::Neg | Op::Negx | Op::Not | Op::Clr => self.op_unary(insn, size),
Op::Tst => self.op_tst(size),
Op::Tas => self.op_tas(),
Op::Ext => {
let n = reg_lo(opcode);
let value = if size == Size::Long {
i32::from(self.state.d[n] as i16) as u32
} else {
let byte = self.state.d[n] as i8;
(self.state.d[n] & 0xffff_0000) | u32::from(byte as u16)
};
self.state.d[n] = value;
self.set_logic_flags(value, size);
self.settle()
}
Op::Swap => {
let n = reg_lo(opcode);
let value = self.state.d[n].rotate_left(16);
self.state.d[n] = value;
self.set_logic_flags(value, Size::Long);
self.settle()
}
Op::Exg => self.op_exg(insn),
Op::Muls | Op::Mulu => self.op_mul(insn),
Op::Divs | Op::Divu => self.op_div(insn, pc0),
Op::Chk => self.op_chk(),
Op::Btst | Op::Bchg | Op::Bclr | Op::Bset => self.op_bit(insn, size),
Op::Asl | Op::Asr | Op::Lsl | Op::Lsr | Op::Rol | Op::Ror | Op::Roxl | Op::Roxr => {
self.op_shift(insn, size)
}
Op::Lea => {
let Loc::Mem(addr) = self.resolve_control(Arg::Ea, ExtraCycles::Control)? else {
return Err(Trap::at(vector::ILLEGAL, pc0));
};
self.state.a[reg_hi(opcode)] = addr;
self.settle()
}
Op::Pea => self.op_pea(),
Op::Jmp => {
let target = self.jump_target()?;
self.refill(target, 0)
}
Op::Jsr => self.op_jsr(pc0),
Op::Bra | Op::Bsr | Op::Bcc => self.op_branch(insn),
Op::Dbcc => self.op_dbcc(),
Op::Scc => self.op_scc(),
Op::Rts => {
let sp = self.state.a[7];
let target = self.read_long(sp)?;
self.state.a[7] = sp.wrapping_add(4);
self.refill(target, 0)
}
Op::Rtr => {
let sp = self.state.a[7];
let high = self.read_word(sp.wrapping_add(2))?;
let ccr = self.read_word(sp)?;
let low = self.read_word(sp.wrapping_add(4))?;
self.state.a[7] = sp.wrapping_add(6);
let sr = (self.state.sr & !flags::CCR) | (ccr & flags::CCR);
self.state.set_sr(sr);
self.refill((u32::from(high) << 16) | u32::from(low), 0)
}
Op::Rte => {
let sp = self.state.a[7];
let high = self.read_word(sp.wrapping_add(2))?;
let sr = self.read_word(sp)?;
let low = self.read_word(sp.wrapping_add(4))?;
self.state.a[7] = sp.wrapping_add(6);
self.state.set_sr(sr);
self.refill((u32::from(high) << 16) | u32::from(low), 0)
}
Op::Trap => {
let n = (opcode & 0xf) as u8;
let next = self.state.pc.wrapping_add(2);
Err(Trap::at(vector::TRAP_BASE.wrapping_add(n), next))
}
Op::Trapv => {
self.settle()?;
if self.state.flag(flags::V) {
self.prologue = 0;
let pc = self.state.pc;
return Err(Trap::at(vector::TRAPV, pc));
}
Ok(())
}
Op::Link => self.op_link(),
Op::Unlk => {
let n = reg_lo(opcode);
let frame = self.state.a[n];
let saved = self.read_long(frame)?;
self.state.a[7] = frame.wrapping_add(4);
self.state.a[n] = saved;
self.settle()
}
Op::MoveFromSr => self.op_move_from_sr(),
Op::MoveToCcr | Op::MoveToSr => self.op_move_to_sr(insn),
Op::MoveUsp => {
let n = reg_lo(opcode);
if insn.src == Arg::Usp {
self.state.a[n] = self.state.usp();
} else {
let value = self.state.a[n];
self.state.set_usp(value);
}
self.settle()
}
Op::OriToCcr | Op::AndiToCcr | Op::EoriToCcr => self.op_imm_to_ccr(insn.op),
Op::OriToSr | Op::AndiToSr | Op::EoriToSr => self.op_imm_to_sr(insn.op),
Op::Movem => self.op_movem(insn, size),
Op::Movep => self.op_movep(insn, size),
}
}
fn resolve(&mut self, arg: Arg, size: Size) -> Result<Loc, Trap> {
let opcode = self.opcode;
match arg {
Arg::None => Ok(Loc::Value(0)),
Arg::DnHi => Ok(Loc::D(reg_hi(opcode) as u8)),
Arg::DnLo => Ok(Loc::D(reg_lo(opcode) as u8)),
Arg::AnHi => Ok(Loc::A(reg_hi(opcode) as u8)),
Arg::AnLo => Ok(Loc::A(reg_lo(opcode) as u8)),
Arg::Quick => {
let q = (opcode >> 9) & 7;
Ok(Loc::Value(if q == 0 { 8 } else { u32::from(q) }))
}
Arg::QuickByte => Ok(Loc::Value(i32::from(opcode as i8) as u32)),
Arg::Vector => Ok(Loc::Value(u32::from(opcode & 0xf))),
Arg::Imm => {
let value = match size {
Size::Byte => u32::from(self.ext(0)? & 0xff),
Size::Word => u32::from(self.ext(0)?),
Size::Long => {
let hi = self.ext(0)?;
let lo = self.ext(0)?;
(u32::from(hi) << 16) | u32::from(lo)
}
};
Ok(Loc::Value(value))
}
Arg::Disp16 => {
let word = self.ext(0)?;
Ok(Loc::Value(i32::from(word as i16) as u32))
}
Arg::Ccr => Ok(Loc::Value(u32::from(self.state.ccr()))),
Arg::Sr => Ok(Loc::Value(u32::from(self.state.sr))),
Arg::Usp => Ok(Loc::Value(self.state.usp())),
Arg::Ea | Arg::EaDst => self.resolve_ea(arg, size, ExtraCycles::Operand),
Arg::RmLo
| Arg::RmHi
| Arg::PostLo
| Arg::PostHi
| Arg::ShiftCount
| Arg::RegList
| Arg::MovepEa
| Arg::BitNumber
| Arg::Disp8 => {
debug_assert!(false, "{arg:?} is not resolved as an operand");
Ok(Loc::Value(0))
}
}
}
fn resolve_control(&mut self, arg: Arg, extra: ExtraCycles) -> Result<Loc, Trap> {
self.resolve_ea(arg, Size::Long, extra)
}
fn resolve_ea(&mut self, arg: Arg, size: Size, extra: ExtraCycles) -> Result<Loc, Trap> {
let opcode = self.opcode;
let Some((mode, reg)) = ea_of(arg, opcode) else {
debug_assert!(false, "{opcode:04x} has no effective address in {arg:?}");
return Ok(Loc::Value(0));
};
let reg = reg as usize;
match mode {
Mode::DataReg => Ok(Loc::D(reg as u8)),
Mode::AddrReg => Ok(Loc::A(reg as u8)),
Mode::Indirect => Ok(Loc::Mem(self.state.a[reg])),
Mode::PostInc => {
let addr = self.state.a[reg];
if extra == ExtraCycles::MoveDest {
self.deferred_postincrement = Some((reg as u8, step(size, reg)));
} else {
self.state.a[reg] = addr.wrapping_add(step(size, reg));
}
Ok(Loc::Mem(addr))
}
Mode::PreDec => {
if extra != ExtraCycles::MoveDest {
self.internal(2);
}
let addr = self.state.a[reg].wrapping_sub(step(size, reg));
if extra != ExtraCycles::MoveDest {
self.state.a[reg] = addr;
}
Ok(Loc::Mem(addr))
}
Mode::Disp16 => {
let disp = i32::from(self.ext(0)? as i16) as u32;
Ok(Loc::Mem(self.state.a[reg].wrapping_add(disp)))
}
Mode::Index8 => {
let word = self.ext(extra.index_delay())?;
Ok(Loc::Mem(self.index_address(self.state.a[reg], word)))
}
Mode::AbsShort => {
let word = self.ext(0)?;
Ok(Loc::Mem(i32::from(word as i16) as u32))
}
Mode::AbsLong => {
let hi = self.ext(0)?;
let defer = extra == ExtraCycles::MoveDest && self.source_was_memory;
let lo = if defer {
self.ext_deferred()
} else {
self.ext(0)?
};
Ok(Loc::Mem((u32::from(hi) << 16) | u32::from(lo)))
}
Mode::PcDisp16 => {
let base = self.state.pc.wrapping_add(2);
let disp = i32::from(self.ext(0)? as i16) as u32;
Ok(Loc::Mem(base.wrapping_add(disp)))
}
Mode::PcIndex8 => {
let base = self.state.pc.wrapping_add(2);
let word = self.ext(extra.index_delay())?;
Ok(Loc::Mem(self.index_address(base, word)))
}
Mode::Imm => {
let value = match size {
Size::Byte => u32::from(self.ext(0)? & 0xff),
Size::Word => u32::from(self.ext(0)?),
Size::Long => {
let hi = self.ext(0)?;
let lo = self.ext(0)?;
(u32::from(hi) << 16) | u32::from(lo)
}
};
Ok(Loc::Value(value))
}
}
}
fn jump_target(&mut self) -> Result<u32, Trap> {
let Some((mode, reg)) = ea_of(Arg::Ea, self.opcode) else {
debug_assert!(false, "{:04x} is not a jump", self.opcode);
return Ok(self.state.pc);
};
let reg = reg as usize;
let queued = self.state.prefetch[1];
Ok(match mode {
Mode::Indirect => self.state.a[reg],
Mode::Disp16 => {
self.internal(2);
self.state.a[reg].wrapping_add(i32::from(queued as i16) as u32)
}
Mode::Index8 => {
self.internal(6);
self.index_address(self.state.a[reg], queued)
}
Mode::AbsShort => {
self.internal(2);
i32::from(queued as i16) as u32
}
Mode::AbsLong => {
let hi = self.ext(0)?;
(u32::from(hi) << 16) | u32::from(self.state.prefetch[1])
}
Mode::PcDisp16 => {
self.internal(2);
self.state
.pc
.wrapping_add(2)
.wrapping_add(i32::from(queued as i16) as u32)
}
Mode::PcIndex8 => {
self.internal(6);
let base = self.state.pc.wrapping_add(2);
self.index_address(base, queued)
}
Mode::DataReg | Mode::AddrReg | Mode::PostInc | Mode::PreDec | Mode::Imm => {
debug_assert!(false, "{:04x} jumps to a non-control mode", self.opcode);
self.state.pc
}
})
}
fn index_address(&self, base: u32, ext: u16) -> u32 {
let reg = ((ext >> 12) & 7) as usize;
let value = if ext & 0x8000 != 0 {
self.state.a[reg]
} else {
self.state.d[reg]
};
let index = if ext & 0x0800 != 0 {
value
} else {
i32::from(value as i16) as u32
};
let disp = i32::from(ext as i8) as u32;
base.wrapping_add(index).wrapping_add(disp)
}
fn read_loc(&mut self, loc: Loc, size: Size) -> Result<u32, Trap> {
Ok(match loc {
Loc::D(n) => self.state.d[n as usize] & size.mask(),
Loc::A(n) => self.state.a[n as usize] & size.mask(),
Loc::Value(v) => v & size.mask(),
Loc::Prefetched(_, value) => value & size.mask(),
Loc::Mem(addr) => match size {
Size::Byte => u32::from(self.read_byte(addr)?),
Size::Word => u32::from(self.read_word(addr)?),
Size::Long => self.read_long(addr)?,
},
})
}
fn write_loc(&mut self, loc: Loc, size: Size, value: u32) -> Result<(), Trap> {
match loc {
Loc::D(n) => {
let n = n as usize;
self.state.d[n] = merge(self.state.d[n], value, size);
}
Loc::A(n) => self.state.a[n as usize] = value,
Loc::Value(_) => {}
Loc::Prefetched(addr, _) | Loc::Mem(addr) => match size {
Size::Byte => self.write_byte(addr, value as u8)?,
Size::Word => self.write_word(addr, value as u16)?,
Size::Long => self.write_long(addr, value)?,
},
}
Ok(())
}
fn write_back(&mut self, loc: Loc, size: Size, value: u32) -> Result<(), Trap> {
if let (Loc::Mem(addr) | Loc::Prefetched(addr, _), Size::Long) = (loc, size) {
return self.write_long_low_first(addr, value);
}
self.write_loc(loc, size, value)
}
fn set_flag(&mut self, mask: u16, on: bool) {
if on {
self.state.sr |= mask;
} else {
self.state.sr &= !mask;
}
}
fn set_logic_flags(&mut self, value: u32, size: Size) {
let value = value & size.mask();
self.set_flag(flags::N, value & size.sign_bit() != 0);
self.set_flag(flags::Z, value == 0);
self.set_flag(flags::V, false);
self.set_flag(flags::C, false);
}
fn op_move(&mut self, insn: Insn, size: Size) -> Result<(), Trap> {
self.source_was_memory =
ea_of(insn.src, self.opcode).is_some_and(|(mode, _)| mode.is_memory());
let src = self.resolve(insn.src, size)?;
let value = self.read_loc(src, size)?;
if insn.op == Op::Movea {
let value = if size == Size::Word {
i32::from(value as i16) as u32
} else {
value
};
self.state.a[reg_hi(self.opcode)] = value;
return self.settle();
}
let dst = self.resolve_ea(insn.dst, size, ExtraCycles::MoveDest)?;
self.set_logic_flags(value, size);
if let Some((Mode::PreDec, reg)) = ea_of(insn.dst, self.opcode) {
let reg = reg as usize;
self.settle()?;
if size == Size::Long {
let low = self.state.a[reg].wrapping_sub(2);
self.state.a[reg] = low;
self.write_word(low, value as u16)?;
let high = low.wrapping_sub(2);
self.state.a[reg] = high;
self.write_word(high, (value >> 16) as u16)?;
} else {
let addr = self.state.a[reg].wrapping_sub(step(size, reg));
self.state.a[reg] = addr;
self.write_loc(Loc::Mem(addr), size, value)?;
}
} else {
self.write_loc(dst, size, value)?;
self.settle()?;
}
if let Some((reg, by)) = self.deferred_postincrement.take() {
let reg = reg as usize;
self.state.a[reg] = self.state.a[reg].wrapping_add(by);
}
Ok(())
}
fn op_binary(&mut self, insn: Insn, size: Size, kind: BinOp) -> Result<(), Trap> {
let src = self.resolve(insn.src, size)?;
let src_value = self.read_loc(src, size)?;
let dst = self.resolve(insn.dst, size)?;
if let Loc::A(n) = dst
&& matches!(insn.op, Op::Addq | Op::Subq)
{
let base = self.state.a[n as usize];
self.state.a[n as usize] = if kind == BinOp::Add {
base.wrapping_add(src_value)
} else {
base.wrapping_sub(src_value)
};
self.internal(if size == Size::Long { 2 } else { 4 });
return self.settle();
}
let dst_value = self.read_loc(dst, size)?;
let result = match kind {
BinOp::Add => {
let r = dst_value.wrapping_add(src_value) & size.mask();
self.set_add_flags(src_value, dst_value, r, size, true);
r
}
BinOp::Sub => {
let r = dst_value.wrapping_sub(src_value) & size.mask();
self.set_sub_flags(src_value, dst_value, r, size, true);
r
}
BinOp::And => {
let r = dst_value & src_value & size.mask();
self.set_logic_flags(r, size);
r
}
BinOp::Or => {
let r = (dst_value | src_value) & size.mask();
self.set_logic_flags(r, size);
r
}
BinOp::Eor => {
let r = (dst_value ^ src_value) & size.mask();
self.set_logic_flags(r, size);
r
}
};
self.arith_internal(insn, size, dst);
if matches!(dst, Loc::D(_) | Loc::A(_)) {
self.write_loc(dst, size, result)?;
self.settle()
} else {
self.settle()?;
self.write_back(dst, size, result)
}
}
fn arith_internal(&mut self, insn: Insn, size: Size, dst: Loc) {
if size != Size::Long || !matches!(dst, Loc::D(_)) {
return;
}
let cheap_source = match insn.src {
Arg::Imm | Arg::Quick | Arg::DnHi => true,
Arg::Ea => matches!(
ea_of(Arg::Ea, self.opcode),
Some((Mode::DataReg | Mode::AddrReg | Mode::Imm, _))
),
_ => false,
};
self.internal(if cheap_source { 4 } else { 2 });
}
fn op_compare(&mut self, insn: Insn, size: Size) -> Result<(), Trap> {
let src = self.resolve(insn.src, size)?;
let src_value = self.read_loc(src, size)?;
let dst = self.resolve(insn.dst, size)?;
let dst_value = self.read_loc(dst, size)?;
let result = dst_value.wrapping_sub(src_value) & size.mask();
self.set_sub_flags(src_value, dst_value, result, size, false);
if size == Size::Long && matches!(dst, Loc::D(_)) {
self.internal(2);
}
self.settle()
}
fn op_cmpm(&mut self, size: Size) -> Result<(), Trap> {
let y = reg_lo(self.opcode);
let x = reg_hi(self.opcode);
let src_addr = self.state.a[y];
self.state.a[y] = src_addr.wrapping_add(step(size, y));
let src_value = self.read_loc(Loc::Mem(src_addr), size)?;
let dst_addr = self.state.a[x];
self.state.a[x] = dst_addr.wrapping_add(step(size, x));
let dst_value = self.read_loc(Loc::Mem(dst_addr), size)?;
let result = dst_value.wrapping_sub(src_value) & size.mask();
self.set_sub_flags(src_value, dst_value, result, size, false);
self.settle()
}
fn op_adda(&mut self, insn: Insn, size: Size) -> Result<(), Trap> {
let src = self.resolve(insn.src, size)?;
let raw = self.read_loc(src, size)?;
let value = if size == Size::Word {
i32::from(raw as i16) as u32
} else {
raw
};
let n = reg_hi(self.opcode);
let base = self.state.a[n];
self.state.a[n] = if insn.op == Op::Adda {
base.wrapping_add(value)
} else {
base.wrapping_sub(value)
};
let cheap = matches!(
ea_of(Arg::Ea, self.opcode),
Some((Mode::DataReg | Mode::AddrReg | Mode::Imm, _))
);
self.internal(if size == Size::Word || cheap { 4 } else { 2 });
self.settle()
}
fn op_cmpa(&mut self, size: Size) -> Result<(), Trap> {
let src = self.resolve(Arg::Ea, size)?;
let raw = self.read_loc(src, size)?;
let value = if size == Size::Word {
i32::from(raw as i16) as u32
} else {
raw
};
let dst_value = self.state.a[reg_hi(self.opcode)];
let result = dst_value.wrapping_sub(value);
self.set_sub_flags(value, dst_value, result, Size::Long, false);
self.internal(2);
self.settle()
}
fn op_addx(&mut self, insn: Insn, size: Size) -> Result<(), Trap> {
let x = u32::from(self.state.flag(flags::X));
let memory = self.opcode & 0x0008 != 0;
let (src_value, dst) = if memory {
let y = reg_lo(self.opcode);
let xr = reg_hi(self.opcode);
self.internal(2);
let src_value = self.read_predecrement(y, size)?;
let dst_value = self.read_predecrement(xr, size)?;
(src_value, Loc::Prefetched(self.state.a[xr], dst_value))
} else {
let src_value = self.state.d[reg_lo(self.opcode)] & size.mask();
(src_value, Loc::D(reg_hi(self.opcode) as u8))
};
let dst_value = self.read_loc(dst, size)?;
let was_zero = self.state.flag(flags::Z);
let result = if insn.op == Op::Addx {
let r = dst_value.wrapping_add(src_value).wrapping_add(x) & size.mask();
self.set_add_flags(src_value, dst_value, r, size, true);
r
} else {
let r = dst_value.wrapping_sub(src_value).wrapping_sub(x) & size.mask();
self.set_sub_flags(src_value, dst_value, r, size, true);
r
};
self.set_flag(flags::Z, result == 0 && was_zero);
if size == Size::Long && matches!(dst, Loc::D(_)) {
self.internal(4);
}
if let Loc::D(_) = dst {
self.write_loc(dst, size, result)?;
return self.settle();
}
let (Loc::Mem(addr) | Loc::Prefetched(addr, _)) = dst else {
return self.settle();
};
if size == Size::Long {
self.write_word(addr.wrapping_add(2), result as u16)?;
self.settle()?;
return self.write_word(addr, (result >> 16) as u16);
}
self.settle()?;
self.write_back(dst, size, result)
}
fn op_bcd(&mut self, insn: Insn) -> Result<(), Trap> {
let size = Size::Byte;
let memory = self.opcode & 0x0008 != 0;
let (src_value, dst) = if memory {
let y = reg_lo(self.opcode);
let x = reg_hi(self.opcode);
let src_value = self.read_predecrement(y, size)?;
let dst_value = self.read_predecrement(x, size)?;
(src_value, Loc::Prefetched(self.state.a[x], dst_value))
} else {
(
self.state.d[reg_lo(self.opcode)] & 0xff,
Loc::D(reg_hi(self.opcode) as u8),
)
};
let dst_value = self.read_loc(dst, size)?;
let result = if insn.op == Op::Abcd {
self.bcd_add(src_value, dst_value)
} else {
self.bcd_sub(src_value, dst_value)
};
self.internal(2);
if matches!(dst, Loc::D(_)) {
self.write_loc(dst, size, result)?;
self.settle()
} else {
self.settle()?;
self.write_back(dst, size, result)
}
}
fn op_nbcd(&mut self) -> Result<(), Trap> {
let dst = self.resolve(Arg::Ea, Size::Byte)?;
let value = self.read_loc(dst, Size::Byte)?;
let result = self.bcd_sub(value, 0);
if matches!(dst, Loc::D(_)) {
self.internal(2);
self.write_loc(dst, Size::Byte, result)?;
self.settle()
} else {
self.settle()?;
self.write_back(dst, Size::Byte, result)
}
}
fn op_unary(&mut self, insn: Insn, size: Size) -> Result<(), Trap> {
let dst = self.resolve(insn.dst, size)?;
let value = self.read_loc(dst, size)?;
let x = u32::from(self.state.flag(flags::X));
let result = match insn.op {
Op::Clr => {
self.set_flag(flags::N, false);
self.set_flag(flags::Z, true);
self.set_flag(flags::V, false);
self.set_flag(flags::C, false);
0
}
Op::Not => {
let r = !value & size.mask();
self.set_logic_flags(r, size);
r
}
Op::Neg => {
let r = 0u32.wrapping_sub(value) & size.mask();
self.set_sub_flags(value, 0, r, size, true);
r
}
_ => {
let r = 0u32.wrapping_sub(value).wrapping_sub(x) & size.mask();
let before = self.state.flag(flags::Z);
self.set_sub_flags(value, 0, r, size, true);
self.set_flag(flags::Z, if r == 0 { before } else { false });
r
}
};
if size == Size::Long && matches!(dst, Loc::D(_)) {
self.internal(2);
}
if matches!(dst, Loc::D(_) | Loc::A(_)) {
self.write_loc(dst, size, result)?;
self.settle()
} else {
self.settle()?;
self.write_back(dst, size, result)
}
}
fn op_tst(&mut self, size: Size) -> Result<(), Trap> {
let src = self.resolve(Arg::Ea, size)?;
let value = self.read_loc(src, size)?;
self.set_logic_flags(value, size);
self.settle()
}
fn op_tas(&mut self) -> Result<(), Trap> {
let dst = self.resolve(Arg::Ea, Size::Byte)?;
let value = self.read_loc(dst, Size::Byte)?;
self.set_logic_flags(value, Size::Byte);
let result = value | 0x80;
if matches!(dst, Loc::D(_)) {
self.write_loc(dst, Size::Byte, result)?;
self.settle()
} else {
self.internal(2);
self.write_loc(dst, Size::Byte, result)?;
self.settle()
}
}
fn op_exg(&mut self, insn: Insn) -> Result<(), Trap> {
let hi = reg_hi(self.opcode);
let lo = reg_lo(self.opcode);
match (insn.src, insn.dst) {
(Arg::DnHi, Arg::DnLo) => self.state.d.swap(hi, lo),
(Arg::AnHi, Arg::AnLo) => self.state.a.swap(hi, lo),
_ => {
core::mem::swap(&mut self.state.d[hi], &mut self.state.a[lo]);
}
}
self.internal(2);
self.settle()
}
fn op_mul(&mut self, insn: Insn) -> Result<(), Trap> {
let src = self.resolve(Arg::Ea, Size::Word)?;
let source = self.read_loc(src, Size::Word)? as u16;
let n = reg_hi(self.opcode);
let dest = self.state.d[n] as u16;
let (result, extra) = if insn.op == Op::Mulu {
let product = u32::from(source).wrapping_mul(u32::from(dest));
(product, 34 + 2 * source.count_ones())
} else {
let product = (i32::from(source as i16)).wrapping_mul(i32::from(dest as i16)) as u32;
let pairs = (u32::from(source) << 1) ^ u32::from(source);
(product, 34 + 2 * (pairs & 0xffff).count_ones())
};
self.internal(extra);
self.state.d[n] = result;
self.set_logic_flags(result, Size::Long);
self.settle()
}
fn op_div(&mut self, insn: Insn, pc0: u32) -> Result<(), Trap> {
let src = self.resolve(Arg::Ea, Size::Word)?;
let divisor = self.read_loc(src, Size::Word)? as u16;
let n = reg_hi(self.opcode);
let dividend = self.state.d[n];
if divisor == 0 {
self.set_flag(flags::N, false);
self.set_flag(flags::Z, false);
self.set_flag(flags::V, false);
self.set_flag(flags::C, false);
self.prologue = 8;
return Err(Trap::at(vector::DIVIDE_BY_ZERO, pc0));
}
if insn.op == Op::Divu {
let quotient = dividend / u32::from(divisor);
let remainder = dividend % u32::from(divisor);
self.internal(divu_cycles(dividend, divisor));
if quotient > 0xffff {
self.set_flag(flags::V, true);
self.set_flag(flags::C, false);
return self.settle();
}
self.state.d[n] = (remainder << 16) | (quotient & 0xffff);
self.set_logic_flags(quotient & 0xffff, Size::Word);
} else {
let dividend = dividend as i32;
let divisor = i32::from(divisor as i16);
self.internal(divs_cycles(dividend, divisor as i16));
let quotient = dividend.wrapping_div(divisor);
let remainder = dividend.wrapping_rem(divisor);
if !(-0x8000..=0x7fff).contains("ient) {
self.set_flag(flags::V, true);
self.set_flag(flags::C, false);
return self.settle();
}
self.state.d[n] = ((remainder as u32) << 16) | (quotient as u32 & 0xffff);
self.set_logic_flags(quotient as u32 & 0xffff, Size::Word);
}
self.settle()
}
fn op_chk(&mut self) -> Result<(), Trap> {
let src = self.resolve(Arg::Ea, Size::Word)?;
let bound = self.read_loc(src, Size::Word)? as i16;
let value = self.state.d[reg_hi(self.opcode)] as i16;
self.set_flag(flags::Z, false);
self.set_flag(flags::V, false);
self.set_flag(flags::C, false);
self.settle()?;
if value < 0 || value > bound {
self.set_flag(flags::N, value < 0);
self.prologue = if value > bound { 4 } else { 6 };
let pc = self.state.pc;
return Err(Trap::at(vector::CHK, pc));
}
self.internal(6);
Ok(())
}
fn op_bit(&mut self, insn: Insn, size: Size) -> Result<(), Trap> {
let bit = match insn.src {
Arg::BitNumber => u32::from(self.ext(0)? & 0xff),
_ => self.state.d[reg_hi(self.opcode)],
};
let dst = self.resolve(insn.dst, size)?;
let width = if size == Size::Long { 32 } else { 8 };
let bit = bit % width;
let value = self.read_loc(dst, size)?;
let mask = 1u32 << bit;
self.set_flag(flags::Z, value & mask == 0);
let result = match insn.op {
Op::Btst => {
if size == Size::Long
|| matches!(ea_of(insn.dst, self.opcode), Some((Mode::Imm, _)))
{
self.internal(2);
}
return self.settle();
}
Op::Bchg => value ^ mask,
Op::Bclr => value & !mask,
_ => value | mask,
};
if matches!(dst, Loc::D(_)) {
self.internal(if bit >= 16 { 4 } else { 2 });
if insn.op == Op::Bclr {
self.internal(2);
}
self.write_loc(dst, size, result)?;
self.settle()
} else {
self.settle()?;
self.write_back(dst, size, result)
}
}
fn op_shift(&mut self, insn: Insn, size: Size) -> Result<(), Trap> {
if insn.dst == Arg::Ea {
let dst = self.resolve(Arg::Ea, Size::Word)?;
let value = self.read_loc(dst, Size::Word)?;
let result = self.shift(insn.op, value, 1, Size::Word);
self.settle()?;
return self.write_back(dst, Size::Word, result);
}
let count = if self.opcode & 0x0020 == 0 {
let q = (self.opcode >> 9) & 7;
if q == 0 { 8 } else { u32::from(q) }
} else {
self.state.d[reg_hi(self.opcode)] % 64
};
let n = reg_lo(self.opcode);
let value = self.state.d[n] & size.mask();
let result = self.shift(insn.op, value, count, size);
self.state.d[n] = merge(self.state.d[n], result, size);
self.internal(if size == Size::Long { 4 } else { 2 } + 2 * count);
self.settle()
}
fn shift(&mut self, op: Op, value: u32, count: u32, size: Size) -> u32 {
let bits = size.bytes() * 8;
let mask = size.mask();
let sign = size.sign_bit();
let mut result = value & mask;
let mut carry = false;
let mut overflow = false;
let steps = count.min(bits);
let exhausted = count > bits;
match op {
Op::Asl => {
for _ in 0..steps {
carry = result & sign != 0;
let next = (result << 1) & mask;
if (next ^ result) & sign != 0 {
overflow = true;
}
result = next;
}
if exhausted {
carry = false;
}
}
Op::Asr => {
for _ in 0..steps {
carry = result & 1 != 0;
result = (result >> 1) | (result & sign);
}
if exhausted {
carry = false;
}
}
Op::Lsl => {
for _ in 0..steps {
carry = result & sign != 0;
result = (result << 1) & mask;
}
if exhausted {
carry = false;
}
}
Op::Lsr => {
for _ in 0..steps {
carry = result & 1 != 0;
result >>= 1;
}
if exhausted {
carry = false;
}
}
Op::Rol => {
for _ in 0..count {
carry = result & sign != 0;
result = ((result << 1) | u32::from(carry)) & mask;
}
}
Op::Ror => {
for _ in 0..count {
carry = result & 1 != 0;
result = (result >> 1) | (if carry { sign } else { 0 });
}
}
Op::Roxl => {
let mut x = self.state.flag(flags::X);
for _ in 0..count {
carry = result & sign != 0;
result = ((result << 1) | u32::from(x)) & mask;
x = carry;
}
if count == 0 {
carry = x;
} else {
self.set_flag(flags::X, x);
}
self.set_flag(flags::N, result & sign != 0);
self.set_flag(flags::Z, result == 0);
self.set_flag(flags::V, false);
self.set_flag(flags::C, carry);
return result;
}
_ => {
let mut x = self.state.flag(flags::X);
for _ in 0..count {
carry = result & 1 != 0;
result = (result >> 1) | (if x { sign } else { 0 });
x = carry;
}
if count == 0 {
carry = x;
} else {
self.set_flag(flags::X, x);
}
self.set_flag(flags::N, result & sign != 0);
self.set_flag(flags::Z, result == 0);
self.set_flag(flags::V, false);
self.set_flag(flags::C, carry);
return result;
}
}
self.set_flag(flags::N, result & sign != 0);
self.set_flag(flags::Z, result == 0);
self.set_flag(flags::V, overflow);
self.set_flag(flags::C, count != 0 && carry);
if count != 0 && matches!(op, Op::Asl | Op::Asr | Op::Lsl | Op::Lsr) {
self.set_flag(flags::X, carry);
}
result
}
fn op_pea(&mut self) -> Result<(), Trap> {
let Loc::Mem(addr) = self.resolve_control(Arg::Ea, ExtraCycles::Control)? else {
let pc = self.state.pc;
return Err(Trap::at(vector::ILLEGAL, pc));
};
let sp = self.state.a[7].wrapping_sub(4);
self.state.a[7] = sp;
let absolute = matches!(
ea_of(Arg::Ea, self.opcode),
Some((Mode::AbsShort | Mode::AbsLong, _))
);
if absolute {
self.write_long(sp, addr)?;
return self.settle();
}
self.settle()?;
self.write_long(sp, addr)
}
fn op_jsr(&mut self, pc0: u32) -> Result<(), Trap> {
let target = self.jump_target()?;
let words =
1 + ea_of(Arg::Ea, self.opcode).map_or(0, |(mode, _)| mode.ext_words(Size::Long));
let ret = pc0.wrapping_add(2 * words);
self.deferred_slides = 0;
let fc = self.program_fc();
self.state.pc = target.wrapping_sub(4);
let first = self.read_word_fc(target, fc)?;
self.state.pc = target.wrapping_sub(2);
let sp = self.state.a[7].wrapping_sub(4);
self.state.a[7] = sp;
self.write_long(sp, ret)?;
let second = self.read_word_fc(target.wrapping_add(2), fc)?;
self.state.pc = target;
self.state.prefetch = [first, second];
Ok(())
}
fn op_branch(&mut self, insn: Insn) -> Result<(), Trap> {
let byte = self.opcode as i8;
let taken = match insn.op {
Op::Bcc => self.state.test(Cond::from_opcode(self.opcode)),
_ => true,
};
let base = self.state.pc.wrapping_add(2);
if byte == 0 {
let word = self.state.prefetch[1];
if !taken {
self.internal(4);
self.ext(0)?;
return self.settle();
}
let target = base.wrapping_add(i32::from(word as i16) as u32);
self.internal(2);
if insn.op == Op::Bsr {
let ret = base.wrapping_add(2);
let sp = self.state.a[7].wrapping_sub(4);
self.state.a[7] = sp;
self.write_long(sp, ret)?;
}
return self.refill(target, 0);
}
if !taken {
self.internal(4);
return self.settle();
}
let target = base.wrapping_add(i32::from(byte) as u32);
self.internal(2);
if insn.op == Op::Bsr {
let ret = base;
let sp = self.state.a[7].wrapping_sub(4);
self.state.a[7] = sp;
self.write_long(sp, ret)?;
}
self.refill(target, 0)
}
fn op_dbcc(&mut self) -> Result<(), Trap> {
let base = self.state.pc.wrapping_add(2);
let n = reg_lo(self.opcode);
if self.state.test(Cond::from_opcode(self.opcode)) {
self.internal(4);
self.ext(0)?;
return self.settle();
}
let counter = (self.state.d[n] as u16).wrapping_sub(1);
self.state.d[n] = merge(self.state.d[n], u32::from(counter), Size::Word);
if counter == 0xffff {
self.internal(6);
self.ext(0)?;
return self.settle();
}
let word = self.state.prefetch[1];
self.internal(2);
let target = base.wrapping_add(i32::from(word as i16) as u32);
self.refill(target, 0)
}
fn op_scc(&mut self) -> Result<(), Trap> {
let set = self.state.test(Cond::from_opcode(self.opcode));
let dst = self.resolve(Arg::Ea, Size::Byte)?;
let value = if set { 0xff } else { 0x00 };
if !matches!(dst, Loc::D(_)) {
self.read_loc(dst, Size::Byte)?;
}
if matches!(dst, Loc::D(_)) {
if set {
self.internal(2);
}
self.write_loc(dst, Size::Byte, value)?;
self.settle()
} else {
self.settle()?;
self.write_back(dst, Size::Byte, value)
}
}
fn op_link(&mut self) -> Result<(), Trap> {
let n = reg_lo(self.opcode);
let disp = i32::from(self.ext(0)? as i16) as u32;
let sp = self.state.a[7].wrapping_sub(4);
self.state.a[7] = sp;
let value = self.state.a[n];
self.write_long(sp, value)?;
self.state.a[n] = sp;
self.state.a[7] = sp.wrapping_add(disp);
self.settle()
}
fn op_move_from_sr(&mut self) -> Result<(), Trap> {
let dst = self.resolve(Arg::Ea, Size::Word)?;
let _ = self.read_loc(dst, Size::Word)?;
let sr = self.state.sr;
if matches!(dst, Loc::D(_)) {
self.internal(2);
self.write_loc(dst, Size::Word, u32::from(sr))?;
return self.settle();
}
self.settle()?;
self.write_loc(dst, Size::Word, u32::from(sr))
}
fn op_move_to_sr(&mut self, insn: Insn) -> Result<(), Trap> {
let src = self.resolve(Arg::Ea, Size::Word)?;
let value = self.read_loc(src, Size::Word)? as u16;
if insn.op == Op::MoveToCcr {
let sr = (self.state.sr & !flags::CCR) | (value & flags::CCR);
self.state.set_sr(sr);
} else {
self.state.set_sr(value);
}
self.internal(4);
let next = self.state.pc.wrapping_add(2);
self.refill(next, 0)
}
fn op_imm_to_ccr(&mut self, op: Op) -> Result<(), Trap> {
let value = self.ext(0)? & 0xff;
let ccr = u16::from(self.state.ccr());
let result = match op {
Op::OriToCcr => ccr | value,
Op::AndiToCcr => ccr & value,
_ => ccr ^ value,
};
let sr = (self.state.sr & !flags::CCR) | (result & flags::CCR);
self.state.set_sr(sr);
self.internal(8);
self.idle_fetch()?;
self.settle()
}
fn op_imm_to_sr(&mut self, op: Op) -> Result<(), Trap> {
let value = self.ext(0)?;
let sr = self.state.sr;
let result = match op {
Op::OriToSr => sr | value,
Op::AndiToSr => sr & value,
_ => sr ^ value,
};
self.state.set_sr(result);
self.internal(8);
self.idle_fetch()?;
self.settle()
}
fn idle_fetch(&mut self) -> Result<(), Trap> {
let addr = self.state.pc.wrapping_add(2);
let fc = self.program_fc();
self.read_word_fc(addr, fc)?;
Ok(())
}
fn op_movem(&mut self, insn: Insn, size: Size) -> Result<(), Trap> {
let mask = self.ext(0)?;
let to_memory = insn.dst == Arg::Ea;
let Some((mode, reg)) = ea_of(Arg::Ea, self.opcode) else {
let pc = self.state.pc;
return Err(Trap::at(vector::ILLEGAL, pc));
};
let reg = reg as usize;
let long = size == Size::Long;
if to_memory && mode == Mode::PreDec {
let initial = self.state.a[reg];
for bit in 0..16u32 {
if mask & (1 << bit) == 0 {
continue;
}
let index = 15 - bit;
let value = if index as usize == reg + 8 {
initial
} else {
self.register(index)
};
let low = self.state.a[reg].wrapping_sub(2);
self.write_word(low, value as u16)?;
self.state.a[reg] = low;
if long {
let high = low.wrapping_sub(2);
self.write_word(high, (value >> 16) as u16)?;
self.state.a[reg] = high;
}
}
return self.settle();
}
let mut addr = if !to_memory && mode == Mode::PostInc {
self.state.a[reg]
} else {
let Loc::Mem(addr) = self.resolve_control(Arg::Ea, ExtraCycles::Operand)? else {
let pc = self.state.pc;
return Err(Trap::at(vector::ILLEGAL, pc));
};
addr
};
let walking = !to_memory && mode == Mode::PostInc;
for bit in 0..16u32 {
if mask & (1 << bit) == 0 {
continue;
}
if to_memory {
let value = self.register(bit);
if long {
self.write_word(addr, (value >> 16) as u16)?;
self.write_word(addr.wrapping_add(2), value as u16)?;
} else {
self.write_word(addr, value as u16)?;
}
addr = addr.wrapping_add(if long { 4 } else { 2 });
} else {
if walking {
self.state.a[reg] = addr.wrapping_add(2);
}
let high = self.read_word(addr)?;
let value = if long {
if walking {
self.state.a[reg] = addr.wrapping_add(4);
}
let low = self.read_word(addr.wrapping_add(2))?;
(u32::from(high) << 16) | u32::from(low)
} else {
i32::from(high as i16) as u32
};
addr = addr.wrapping_add(if long { 4 } else { 2 });
self.set_register(bit, value);
}
}
if !to_memory {
if walking {
self.state.a[reg] = addr.wrapping_add(2);
}
self.read_word(addr)?;
if walking {
self.state.a[reg] = addr;
}
}
self.settle()
}
fn register(&self, index: u32) -> u32 {
if index < 8 {
self.state.d[index as usize]
} else {
self.state.a[(index - 8) as usize]
}
}
fn set_register(&mut self, index: u32, value: u32) {
if index < 8 {
self.state.d[index as usize] = value;
} else {
self.state.a[(index - 8) as usize] = value;
}
}
fn op_movep(&mut self, insn: Insn, size: Size) -> Result<(), Trap> {
let n = reg_hi(self.opcode);
let areg = reg_lo(self.opcode);
let disp = i32::from(self.ext(0)? as i16) as u32;
let base = self.state.a[areg].wrapping_add(disp);
let to_memory = insn.dst == Arg::MovepEa;
let count = if size == Size::Long { 4 } else { 2 };
if to_memory {
let value = self.state.d[n];
for i in 0..count {
let shift = 8 * (count - 1 - i);
let byte = (value >> shift) as u8;
self.write_byte(base.wrapping_add(i * 2), byte)?;
}
} else {
let mut value = 0u32;
for i in 0..count {
let byte = self.read_byte(base.wrapping_add(i * 2))?;
value = (value << 8) | u32::from(byte);
}
self.state.d[n] = merge(self.state.d[n], value, size);
}
self.settle()
}
fn predecrement(&mut self, reg: usize, size: Size) -> u32 {
let addr = self.state.a[reg].wrapping_sub(step(size, reg));
self.state.a[reg] = addr;
addr
}
fn read_predecrement(&mut self, reg: usize, size: Size) -> Result<u32, Trap> {
if size != Size::Long {
let addr = self.predecrement(reg, size);
return self.read_loc(Loc::Mem(addr), size);
}
let low_at = self.state.a[reg].wrapping_sub(2);
self.state.a[reg] = low_at;
let low = self.read_word(low_at)?;
let high_at = low_at.wrapping_sub(2);
self.state.a[reg] = high_at;
let high = self.read_word(high_at)?;
Ok((u32::from(high) << 16) | u32::from(low))
}
fn set_add_flags(&mut self, src: u32, dst: u32, result: u32, size: Size, extend: bool) {
let sign = size.sign_bit();
let sm = src & sign != 0;
let dm = dst & sign != 0;
let rm = result & sign != 0;
let carry = (sm && dm) || (!rm && (sm || dm));
let overflow = (sm && dm && !rm) || (!sm && !dm && rm);
self.set_flag(flags::N, rm);
self.set_flag(flags::Z, result & size.mask() == 0);
self.set_flag(flags::V, overflow);
self.set_flag(flags::C, carry);
if extend {
self.set_flag(flags::X, carry);
}
}
fn set_sub_flags(&mut self, src: u32, dst: u32, result: u32, size: Size, extend: bool) {
let sign = size.sign_bit();
let sm = src & sign != 0;
let dm = dst & sign != 0;
let rm = result & sign != 0;
let borrow = (sm && !dm) || (rm && (sm || !dm));
let overflow = (!sm && dm && !rm) || (sm && !dm && rm);
self.set_flag(flags::N, rm);
self.set_flag(flags::Z, result & size.mask() == 0);
self.set_flag(flags::V, overflow);
self.set_flag(flags::C, borrow);
if extend {
self.set_flag(flags::X, borrow);
}
}
fn bcd_add(&mut self, src: u32, dst: u32) -> u32 {
let x = u32::from(self.state.flag(flags::X));
let low = (src & 0x0f) + (dst & 0x0f) + x;
let binary = (src & 0xff) + (dst & 0xff) + x;
let carry = binary > 0x99;
let mut result = binary;
if low > 9 {
result += 6;
}
if carry {
result += 0x60;
}
let result = result & 0xff;
self.set_flag(flags::C, carry);
self.set_flag(flags::X, carry);
self.set_flag(flags::N, result & 0x80 != 0);
self.set_flag(flags::V, !binary & result & 0x80 != 0);
if result != 0 {
self.set_flag(flags::Z, false);
}
result
}
fn bcd_sub(&mut self, src: u32, dst: u32) -> u32 {
let x = i32::from(self.state.flag(flags::X));
let src = (src & 0xff) as i32;
let dst = (dst & 0xff) as i32;
let low = (dst & 0x0f) - (src & 0x0f) - x;
let mut result = dst - src - x;
let binary = (result as u32) & 0xff;
let binary_borrow = result < 0;
if low < 0 {
result -= 6;
}
let borrow = result < 0;
if binary_borrow {
result -= 0x60;
}
let result = (result as u32) & 0xff;
self.set_flag(flags::C, borrow);
self.set_flag(flags::X, borrow);
self.set_flag(flags::N, result & 0x80 != 0);
self.set_flag(flags::V, binary & !result & 0x80 != 0);
if result != 0 {
self.set_flag(flags::Z, false);
}
result
}
}
#[derive(Debug, Clone, Copy)]
struct Group0 {
ssw: u16,
addr: u32,
ir: u16,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum BinOp {
Add,
Sub,
And,
Or,
Eor,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ExtraCycles {
Operand,
MoveDest,
Control,
}
impl ExtraCycles {
const fn index_delay(self) -> u32 {
match self {
ExtraCycles::Operand | ExtraCycles::MoveDest => 2,
ExtraCycles::Control => 4,
}
}
}
#[inline]
const fn reg_hi(opcode: u16) -> usize {
((opcode >> 9) & 7) as usize
}
#[inline]
const fn reg_lo(opcode: u16) -> usize {
(opcode & 7) as usize
}
#[inline]
const fn step(size: Size, reg: usize) -> u32 {
match size {
Size::Byte if reg == 7 => 2,
other => other.bytes(),
}
}
#[inline]
const fn merge(old: u32, value: u32, size: Size) -> u32 {
(old & !size.mask()) | (value & size.mask())
}
fn divu_cycles(dividend: u32, divisor: u16) -> u32 {
if divisor == 0 {
return 0;
}
if (dividend >> 16) >= u32::from(divisor) {
return 6;
}
let divisor = u32::from(divisor);
let mut cycles = 12;
let mut high = dividend >> 16;
let mut low = dividend & 0xffff;
for _ in 0..15 {
let carried = high & 0x8000 != 0;
high = ((high << 1) | (low >> 15)) & 0xffff;
low = (low << 1) & 0xffff;
if carried {
high = high.wrapping_sub(divisor) & 0xffff;
cycles += 4;
} else if high >= divisor {
high -= divisor;
cycles += 6;
} else {
cycles += 8;
}
}
cycles
}
fn divs_cycles(dividend: i32, divisor: i16) -> u32 {
if divisor == 0 {
return 0;
}
let magnitude = dividend.unsigned_abs() / u32::from(divisor.unsigned_abs());
let negative = (dividend < 0) != (divisor < 0);
let limit = if negative { 0x8000 } else { 0x7fff };
if magnitude > limit {
return 12 + if dividend < 0 { 2 } else { 0 };
}
debug_assert!(magnitude <= 0x8000);
let zeros = 15 - (magnitude & 0xffff).count_ones();
116 + if dividend < 0 { 4 } else { 0 }
+ if negative { 2 } else { 0 }
+ 2 * zeros
+ 2 * (magnitude & 1)
}