use super::cpu::CpuCore;
use super::ea::{AddressingMode, EaResult};
use super::execute::RUN_MODE_BERR_AERR_RESET;
use super::memory::AddressBus;
use super::types::{CpuType, InternalStepResult, Size};
pub(crate) const ALINE_TRAP_SENTINEL: i32 = -1_000_000;
pub(crate) const FLINE_TRAP_SENTINEL: i32 = -1_000_001;
pub(crate) const TRAP_SENTINEL_BASE: i32 = -1_000_100;
pub(crate) const BKPT_SENTINEL_BASE: i32 = -1_000_200;
pub(crate) const ILLEGAL_SENTINEL: i32 = -1_000_300;
pub(crate) fn dispatch_instruction<B: AddressBus>(
cpu: &mut CpuCore,
bus: &mut B,
opcode: u16,
) -> InternalStepResult {
let group = (opcode >> 12) & 0xF;
let cycles = match group {
0x0 => dispatch_group_0(cpu, bus, opcode), 0x1 => dispatch_move(cpu, bus, opcode, Size::Byte),
0x2 => dispatch_move(cpu, bus, opcode, Size::Long),
0x3 => dispatch_move(cpu, bus, opcode, Size::Word),
0x4 => dispatch_group_4(cpu, bus, opcode), 0x5 => dispatch_group_5(cpu, bus, opcode), 0x6 => dispatch_group_6(cpu, bus, opcode), 0x7 => dispatch_moveq(cpu, opcode),
0x8 => dispatch_group_8(cpu, bus, opcode), 0x9 => dispatch_group_9(cpu, bus, opcode), 0xA => exception_1010(cpu, opcode),
0xB => dispatch_group_b(cpu, bus, opcode), 0xC => dispatch_group_c(cpu, bus, opcode), 0xD => dispatch_group_d(cpu, bus, opcode), 0xE => dispatch_group_e(cpu, bus, opcode), 0xF => dispatch_group_f(cpu, bus, opcode),
_ => unreachable!(),
};
if cycles >= 0 {
return InternalStepResult::Ok { cycles };
}
if cycles == ALINE_TRAP_SENTINEL {
return InternalStepResult::AlineTrap { opcode };
}
if cycles == FLINE_TRAP_SENTINEL {
return InternalStepResult::FlineTrap { opcode };
}
if (TRAP_SENTINEL_BASE..TRAP_SENTINEL_BASE + 16).contains(&cycles) {
let trap_num = (cycles - TRAP_SENTINEL_BASE) as u8;
return InternalStepResult::TrapInstruction { trap_num };
}
if (BKPT_SENTINEL_BASE..BKPT_SENTINEL_BASE + 8).contains(&cycles) {
let bp_num = (cycles - BKPT_SENTINEL_BASE) as u8;
return InternalStepResult::Breakpoint { bp_num };
}
if cycles == ILLEGAL_SENTINEL {
return InternalStepResult::IllegalInstruction { opcode };
}
InternalStepResult::Ok { cycles }
}
fn dispatch_group_f<B: AddressBus>(cpu: &mut CpuCore, bus: &mut B, opcode: u16) -> i32 {
let has_coproc_interface = !cpu.is_pre_68020;
if !has_coproc_interface {
return exception_1111(cpu, opcode);
}
let sub = (opcode >> 8) & 0xF;
if (opcode & 0xFFF8) == 0xF620 {
let supports_move16 = matches!(
cpu.cpu_type,
CpuType::M68EC030
| CpuType::M68030
| CpuType::M68EC040
| CpuType::M68LC040
| CpuType::M68040
);
if !supports_move16 {
return illegal_instruction(cpu, bus);
}
return cpu.exec_move16(bus, opcode);
}
let is_cache_cpu = matches!(
cpu.cpu_type,
CpuType::M68EC030
| CpuType::M68030
| CpuType::M68EC040
| CpuType::M68LC040
| CpuType::M68040
);
if is_cache_cpu && (opcode >> 8) & 0xF == 4 {
if !cpu.is_supervisor() {
return cpu.take_exception(bus, 8); }
return 4;
}
if is_cache_cpu && (opcode >> 8) & 0xF == 5 {
if !cpu.is_supervisor() {
return cpu.take_exception(bus, 8); }
return 4;
}
if ((opcode >> 9) & 0x7) == 0 {
let cycles = cpu.exec_mmu_op0(bus, opcode);
if cycles != 0 {
return cycles;
}
}
if (opcode & 0xFFC0) == 0xF240 {
let ea_mode = ((opcode >> 3) & 7) as u8;
let ea_reg = (opcode & 7) as usize;
let w2 = cpu.read_imm_16(bus);
let cond = (w2 & 0x3F) as u8;
return cpu.exec_fscc(bus, ea_mode, ea_reg, cond);
}
if (opcode & 0xFFC0) == 0xF280 {
let cond = (opcode & 0x3F) as u8;
let disp = cpu.read_imm_16(bus) as i16 as i32;
return cpu.exec_fbcc(cond, disp);
}
if (opcode & 0xFFC0) == 0xF2C0 {
let cond = (opcode & 0x3F) as u8;
let disp = cpu.read_imm_32(bus) as i32;
return cpu.exec_fbcc(cond, disp);
}
let cycles = match sub {
0x2 => cpu.exec_fpu_op0(bus, opcode),
0x3 => cpu.exec_fpu_op1(bus, opcode),
_ => 0,
};
if cycles != 0 {
return cycles;
}
FLINE_TRAP_SENTINEL
}
fn dispatch_move<B: AddressBus>(cpu: &mut CpuCore, bus: &mut B, opcode: u16, size: Size) -> i32 {
let src_reg = (opcode & 7) as u8;
let src_mode = ((opcode >> 3) & 7) as u8;
let dst_reg = ((opcode >> 9) & 7) as u8;
let dst_mode = ((opcode >> 6) & 7) as u8;
let src = AddressingMode::decode(src_mode, src_reg);
let dst = AddressingMode::decode(dst_mode, dst_reg);
match (src, dst) {
(Some(src_ea), Some(dst_ea)) => {
if dst_mode == 1 {
if size == Size::Byte {
illegal_instruction(cpu, bus)
} else {
cpu.exec_movea(bus, size, src_ea, dst_reg as usize)
}
} else {
cpu.exec_move(bus, size, src_ea, dst_ea)
}
}
_ => illegal_instruction(cpu, bus),
}
}
fn dispatch_moveq(cpu: &mut CpuCore, opcode: u16) -> i32 {
let reg = ((opcode >> 9) & 7) as usize;
let data = (opcode & 0xFF) as i8 as i32 as u32;
cpu.set_d(reg, data);
cpu.n_flag = if (data as i32) < 0 { 0x80 } else { 0 };
cpu.not_z_flag = data;
cpu.v_flag = 0;
cpu.c_flag = 0;
4
}
fn dispatch_group_0<B: AddressBus>(cpu: &mut CpuCore, bus: &mut B, opcode: u16) -> i32 {
if opcode == 0x0EFC || opcode == 0x0CFC || opcode == 0x0AFC {
if cpu.cpu_type == CpuType::M68000
|| cpu.cpu_type == CpuType::M68010
|| cpu.cpu_type == CpuType::SCC68070
{
return illegal_instruction(cpu, bus);
}
return cpu.exec_cas2(bus, opcode);
}
if (opcode & 0x0FC0) == 0x0AC0 || (opcode & 0x0FC0) == 0x0CC0 || (opcode & 0x0FC0) == 0x0EC0 {
if cpu.cpu_type == CpuType::M68000
|| cpu.cpu_type == CpuType::M68010
|| cpu.cpu_type == CpuType::SCC68070
{
return illegal_instruction(cpu, bus);
}
return cpu.exec_cas(bus, opcode);
}
if (opcode & 0xFF00) == 0x0E00 {
if cpu.cpu_type == CpuType::M68000 {
return illegal_instruction(cpu, bus);
}
return cpu.exec_moves(bus, opcode);
}
if (opcode & 0xFFF0) == 0x06C0 {
if !matches!(
cpu.cpu_type,
CpuType::M68EC020
| CpuType::M68020
| CpuType::M68EC030
| CpuType::M68030
| CpuType::M68EC040
| CpuType::M68LC040
| CpuType::M68040
) {
return illegal_instruction(cpu, bus);
}
return cpu.exec_rtm(bus, opcode);
}
if (opcode & 0xFFC0) == 0x06C0 {
if !matches!(
cpu.cpu_type,
CpuType::M68EC020
| CpuType::M68020
| CpuType::M68EC030
| CpuType::M68030
| CpuType::M68EC040
| CpuType::M68LC040
| CpuType::M68040
) {
return illegal_instruction(cpu, bus);
}
return cpu.exec_callm(bus, opcode);
}
if (opcode & 0x0800) == 0
&& (opcode & 0x0100) == 0
&& (opcode & 0x00C0) == 0x00C0
&& ((opcode >> 9) & 3) != 3
{
if cpu.is_pre_68020 {
return illegal_instruction(cpu, bus);
}
return cpu.exec_cmp2_chk2(bus, opcode);
}
if (opcode & 0xF138) == 0x0108 {
let dreg = ((opcode >> 9) & 7) as usize;
let areg = (opcode & 7) as usize;
let is_long = (opcode & 0x0040) != 0;
let reg_to_mem = (opcode & 0x0080) != 0;
let disp = cpu.read_imm_16(bus) as i16 as i32;
let base = cpu.a(areg);
let addr = (base as i32).wrapping_add(disp) as u32;
if is_long {
if reg_to_mem {
let v = cpu.d(dreg);
cpu.write_8(bus, addr, ((v >> 24) & 0xFF) as u8);
cpu.write_8(bus, addr.wrapping_add(2), ((v >> 16) & 0xFF) as u8);
cpu.write_8(bus, addr.wrapping_add(4), ((v >> 8) & 0xFF) as u8);
cpu.write_8(bus, addr.wrapping_add(6), (v & 0xFF) as u8);
} else {
let b0 = cpu.read_8(bus, addr) as u32;
let b1 = cpu.read_8(bus, addr.wrapping_add(2)) as u32;
let b2 = cpu.read_8(bus, addr.wrapping_add(4)) as u32;
let b3 = cpu.read_8(bus, addr.wrapping_add(6)) as u32;
let v = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
cpu.set_d(dreg, v);
}
} else if reg_to_mem {
let v = cpu.d(dreg) & 0xFFFF;
cpu.write_8(bus, addr, ((v >> 8) & 0xFF) as u8);
cpu.write_8(bus, addr.wrapping_add(2), (v & 0xFF) as u8);
} else {
let hi = cpu.read_8(bus, addr) as u32;
let lo = cpu.read_8(bus, addr.wrapping_add(2)) as u32;
let v = (hi << 8) | lo;
cpu.set_d(dreg, (cpu.d(dreg) & 0xFFFF0000) | v);
}
return if is_long { 24 } else { 16 };
}
let subop = (opcode >> 8) & 0xF;
let ea_mode = ((opcode >> 3) & 7) as u8;
let ea_reg = (opcode & 7) as u8;
match subop {
0x0 if ea_mode == 7 && ea_reg == 4 && ((opcode >> 6) & 3) == 0 => cpu.exec_ori_ccr(bus),
0x0 if ea_mode == 7 && ea_reg == 4 && ((opcode >> 6) & 3) == 1 => cpu.exec_ori_sr(bus),
0x0 => {
if let Some(mode) = AddressingMode::decode(ea_mode, ea_reg) {
let size = decode_size_00((opcode >> 6) & 3);
cpu.exec_ori(bus, size, mode)
} else {
illegal_instruction(cpu, bus)
}
}
0x2 if ea_mode == 7 && ea_reg == 4 && ((opcode >> 6) & 3) == 0 => cpu.exec_andi_ccr(bus),
0x2 if ea_mode == 7 && ea_reg == 4 && ((opcode >> 6) & 3) == 1 => cpu.exec_andi_sr(bus),
0x2 => {
if let Some(mode) = AddressingMode::decode(ea_mode, ea_reg) {
let size = decode_size_00((opcode >> 6) & 3);
cpu.exec_andi(bus, size, mode)
} else {
illegal_instruction(cpu, bus)
}
}
0x4 => {
let size_bits = (opcode >> 6) & 3;
if size_bits == 3 {
return 4; }
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let size = decode_size_00(size_bits);
let imm = read_immediate(cpu, bus, size);
let ea = cpu.resolve_ea(bus, mode, size);
let dst = cpu.read_resolved_ea(bus, ea, size);
let (result, _) = cpu.exec_sub(bus, size, imm, dst);
cpu.write_resolved_ea(bus, ea, size, result);
if size == Size::Long { 16 } else { 8 } }
0x6 => {
let size_bits = (opcode >> 6) & 3;
if size_bits == 3 {
return 4;
}
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let size = decode_size_00(size_bits);
let imm = read_immediate(cpu, bus, size);
let ea = cpu.resolve_ea(bus, mode, size);
let dst = cpu.read_resolved_ea(bus, ea, size);
let (result, cycles) = cpu.exec_add(bus, size, imm, dst);
cpu.write_resolved_ea(bus, ea, size, result);
cycles + if size == Size::Long { 8 } else { 4 }
}
0xA if ea_mode == 7 && ea_reg == 4 && ((opcode >> 6) & 3) == 0 => cpu.exec_eori_ccr(bus),
0xA if ea_mode == 7 && ea_reg == 4 && ((opcode >> 6) & 3) == 1 => cpu.exec_eori_sr(bus),
0xA => {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let size = decode_size_00((opcode >> 6) & 3);
cpu.exec_eori(bus, size, mode)
}
0xC => {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let size = decode_size_00((opcode >> 6) & 3);
let imm = read_immediate(cpu, bus, size);
let dst = cpu.read_ea(bus, mode, size);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
cpu.exec_cmp(size, imm, dst)
}
_ => {
let bit_op = (opcode >> 6) & 3;
let mode = AddressingMode::decode(ea_mode, ea_reg);
if let Some(ea) = mode {
let bit_num = if opcode & 0x100 != 0 {
let reg = ((opcode >> 9) & 7) as usize;
cpu.d(reg)
} else {
cpu.read_imm_16(bus) as u32
};
match bit_op {
0 => cpu.exec_btst(bus, bit_num, ea),
1 => cpu.exec_bchg(bus, bit_num, ea),
2 => cpu.exec_bclr(bus, bit_num, ea),
3 => cpu.exec_bset(bus, bit_num, ea),
_ => illegal_instruction(cpu, bus),
}
} else {
illegal_instruction(cpu, bus)
}
}
}
}
fn dispatch_group_4<B: AddressBus>(cpu: &mut CpuCore, bus: &mut B, opcode: u16) -> i32 {
let subop = (opcode >> 8) & 0xF;
let ea_mode = ((opcode >> 3) & 7) as u8;
let ea_reg = (opcode & 7) as u8;
let opmode = (opcode >> 6) & 7;
if (opcode & 0xFFF8) == 0x4808 {
if cpu.is_pre_68020 {
return illegal_instruction(cpu, bus);
}
return cpu.exec_link_long(bus, ea_reg as usize);
}
if (opcode & 0xFFC0) == 0x4C00 {
if cpu.is_pre_68020 {
return illegal_instruction(cpu, bus);
}
return cpu.exec_mull(bus, opcode);
}
if (opcode & 0xFFC0) == 0x4C40 {
if cpu.is_pre_68020 {
return illegal_instruction(cpu, bus);
}
return cpu.exec_divl(bus, opcode);
}
if (opcode & 0xFFC0) == 0x40C0 {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let sr = cpu.get_sr() as u32;
cpu.write_ea(bus, mode, Size::Word, sr);
return if mode.is_register_direct() { 6 } else { 8 };
}
if (opcode & 0xFFC0) == 0x42C0 {
if cpu.cpu_type == CpuType::M68000 {
return illegal_instruction(cpu, bus);
}
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let ccr = cpu.get_ccr() as u32;
cpu.write_ea(bus, mode, Size::Word, ccr);
return if mode.is_register_direct() { 6 } else { 8 };
}
if opmode == 0b110 {
let dst_reg = ((opcode >> 9) & 7) as usize;
if let Some(mode) = AddressingMode::decode(ea_mode, ea_reg) {
let size = Size::Word;
let bound = cpu.read_ea(bus, mode, size);
return cpu.exec_chk(bus, size, bound, dst_reg);
} else {
return illegal_instruction(cpu, bus);
}
}
match opcode {
0x4E70 => {
if cpu.is_supervisor() {
bus.reset_devices();
132
} else {
cpu.exception_privilege(bus)
}
} 0x4E71 => 4, 0x4E72 => {
if cpu.is_supervisor() {
let sr = cpu.read_imm_16(bus);
cpu.stop(sr);
4
} else {
cpu.exception_privilege(bus)
}
}
0x4E73 => {
if cpu.is_supervisor() {
match cpu.cpu_type {
CpuType::M68000 => {
let sr = cpu.pull_16(bus);
cpu.pc = cpu.pull_32(bus);
cpu.set_sr(sr);
20
}
CpuType::M68010 | CpuType::SCC68070 => {
let sp = cpu.a(7);
let format = cpu.read_16(bus, sp.wrapping_add(6)) >> 12;
if format != 0 {
return cpu.take_exception(bus, 14); }
let sr = cpu.pull_16(bus);
cpu.pc = cpu.pull_32(bus);
let _ = cpu.pull_16(bus); cpu.set_sr(sr);
20
}
_ => {
loop {
let sp = cpu.a(7);
let format = cpu.read_16(bus, sp.wrapping_add(6)) >> 12;
match format {
0 => {
let sr = cpu.pull_16(bus);
cpu.pc = cpu.pull_32(bus);
let _ = cpu.pull_16(bus); cpu.set_sr(sr);
return 20;
}
1 => {
let sr = cpu.pull_16(bus);
cpu.dar[15] = cpu.dar[15].wrapping_add(4 + 2);
cpu.set_sr(sr);
continue;
}
2 => {
let sr = cpu.pull_16(bus);
cpu.pc = cpu.pull_32(bus);
let _ = cpu.pull_16(bus); cpu.dar[15] = cpu.dar[15].wrapping_add(4); cpu.set_sr(sr);
return 20;
}
_ => {
return cpu.take_exception(bus, 14); }
}
}
}
}
} else {
cpu.exception_privilege(bus)
}
}
0x4E74 => {
if cpu.cpu_type == CpuType::M68000 {
illegal_instruction(cpu, bus)
} else {
let disp = cpu.read_imm_16(bus) as i16 as i32;
cpu.pc = cpu.pull_32(bus);
cpu.dar[15] = (cpu.dar[15] as i32).wrapping_add(disp) as u32;
20
}
}
0x4E75 => {
cpu.change_of_flow = true;
cpu.pc = cpu.pull_32(bus);
16
}
0x4E76 => {
if cpu.flag_v() {
cpu.take_exception(bus, 7)
} else {
4
}
}
0x4E77 => {
let ccr = cpu.pull_16(bus) as u8;
cpu.set_ccr(ccr);
cpu.change_of_flow = true;
cpu.pc = cpu.pull_32(bus);
20
}
0x4E7A => {
if cpu.cpu_type == CpuType::M68000 {
return illegal_instruction(cpu, bus);
}
let ext = bus.read_word(cpu.pc);
cpu.pc += 2;
let reg_type = (ext >> 15) & 1; let reg_num = ((ext >> 12) & 7) as usize;
let ctrl_reg = ext & 0xFFF;
if matches!(cpu.cpu_type, CpuType::M68010 | CpuType::SCC68070)
&& !matches!(ctrl_reg, 0x000 | 0x001 | 0x800 | 0x801)
{
return illegal_instruction(cpu, bus);
}
if matches!(cpu.cpu_type, CpuType::M68EC020 | CpuType::M68020)
&& !matches!(
ctrl_reg,
0x000 | 0x001 | 0x002 | 0x800 | 0x801 | 0x802 | 0x803 | 0x804
)
{
return illegal_instruction(cpu, bus);
}
if matches!(cpu.cpu_type, CpuType::M68EC030 | CpuType::M68030)
&& !matches!(
ctrl_reg,
0x000 | 0x001 | 0x002 | 0x800 | 0x801 | 0x802 | 0x803 | 0x804
)
{
return illegal_instruction(cpu, bus);
}
if !cpu.is_supervisor() {
return cpu.take_exception(bus, 8); }
let value = cpu.read_control_register(ctrl_reg);
if reg_type == 0 {
cpu.set_d(reg_num, value);
} else {
cpu.set_a(reg_num, value);
}
12
}
0x4E7B => {
if cpu.cpu_type == CpuType::M68000 {
return illegal_instruction(cpu, bus);
}
let ext = bus.read_word(cpu.pc);
cpu.pc += 2;
let reg_type = (ext >> 15) & 1; let reg_num = ((ext >> 12) & 7) as usize;
let ctrl_reg = ext & 0xFFF;
if matches!(cpu.cpu_type, CpuType::M68010 | CpuType::SCC68070)
&& !matches!(ctrl_reg, 0x000 | 0x001 | 0x800 | 0x801)
{
return illegal_instruction(cpu, bus);
}
if matches!(cpu.cpu_type, CpuType::M68EC020 | CpuType::M68020)
&& !matches!(
ctrl_reg,
0x000 | 0x001 | 0x002 | 0x800 | 0x801 | 0x802 | 0x803 | 0x804
)
{
return illegal_instruction(cpu, bus);
}
if matches!(cpu.cpu_type, CpuType::M68EC030 | CpuType::M68030)
&& !matches!(
ctrl_reg,
0x000 | 0x001 | 0x002 | 0x800 | 0x801 | 0x802 | 0x803 | 0x804
)
{
return illegal_instruction(cpu, bus);
}
if !cpu.is_supervisor() {
return cpu.take_exception(bus, 8); }
let value = if reg_type == 0 {
cpu.d(reg_num)
} else {
cpu.a(reg_num)
};
cpu.write_control_register(ctrl_reg, value);
12
}
_ => {
match subop {
0x0 => {
let size = decode_size_00((opcode >> 6) & 3);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
cpu.exec_negx(bus, size, mode)
}
0x2 => {
let size = decode_size_00((opcode >> 6) & 3);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
cpu.exec_clr(bus, size, mode)
}
0x4 if (opcode >> 6) & 3 == 3 => {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let value = cpu.read_ea(bus, mode, Size::Word) as u8;
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
cpu.set_ccr(value);
12
}
0x4 => {
let size = decode_size_00((opcode >> 6) & 3);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
cpu.exec_neg(bus, size, mode)
}
0x6 if (opcode >> 6) & 3 == 3 => {
if !cpu.is_supervisor() {
return cpu.exception_privilege(bus);
}
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let value = cpu.read_ea(bus, mode, Size::Word);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
cpu.set_sr(value as u16);
12
}
0x6 => {
let size = decode_size_00((opcode >> 6) & 3);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
cpu.exec_not(bus, size, mode)
}
0x8 if (opcode >> 6) & 3 == 1 && ea_mode == 0 => {
cpu.exec_swap(ea_reg as usize)
}
0x8 if (opcode >> 6) & 3 == 1 && ea_mode == 1 => {
let bp_num = (opcode & 7) as u8;
BKPT_SENTINEL_BASE + bp_num as i32
}
0x8 if (opcode >> 6) & 3 == 0 => {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
cpu.exec_nbcd(bus, mode)
}
0x8 if (opcode >> 6) & 3 == 2 && ea_mode == 0 => {
cpu.exec_ext(Size::Word, ea_reg as usize)
}
0x8 if (opcode >> 6) & 3 == 3 && ea_mode == 0 => {
cpu.exec_ext(Size::Long, ea_reg as usize)
}
0x9 if (opcode >> 6) & 3 == 3 && ea_mode == 0 => {
if cpu.is_pre_68020 {
illegal_instruction(cpu, bus)
} else {
cpu.exec_extb(ea_reg as usize)
}
}
0xA if opcode == 0x4AFC => {
ILLEGAL_SENTINEL
}
0xA if (opcode >> 6) & 3 == 3 => {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
cpu.exec_tas(bus, mode)
}
0xA => {
let size = decode_size_00((opcode >> 6) & 3);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
cpu.exec_tst(bus, size, mode)
}
0xE if (opcode >> 4) & 0xF == 4 => {
let trap_num = (opcode & 0xF) as u8;
TRAP_SENTINEL_BASE + trap_num as i32
}
0xE if (opcode & 0xFFF8) == 0x4E50 => {
cpu.exec_link(bus, ea_reg as usize)
}
0xE if (opcode & 0xFFF8) == 0x4E58 => {
cpu.exec_unlk(bus, ea_reg as usize)
}
_ if (opcode & 0xFFF8) == 0x4E60 => {
if cpu.is_supervisor() {
let reg = (opcode & 7) as usize;
cpu.set_usp(cpu.a(reg));
4
} else {
cpu.exception_privilege(bus)
}
}
_ if (opcode & 0xFFF8) == 0x4E68 => {
if cpu.is_supervisor() {
let reg = (opcode & 7) as usize;
let usp = cpu.get_usp();
cpu.set_a(reg, usp);
4
} else {
cpu.exception_privilege(bus)
}
}
_ if (opcode & 0xFFC0) == 0x4E80 => {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let addr = cpu.get_ea_address(bus, mode, Size::Long);
cpu.change_of_flow = true;
cpu.push_32(bus, cpu.pc);
cpu.pc = addr;
16
}
_ if (opcode & 0xFFC0) == 0x4EC0 => {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
cpu.change_of_flow = true;
cpu.pc = cpu.get_ea_address(bus, mode, Size::Long);
8
}
_ if (opcode & 0xF1C0) == 0x41C0 => {
let reg = ((opcode >> 9) & 7) as usize;
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
if mode.is_register_direct() || matches!(mode, AddressingMode::Immediate) {
illegal_instruction(cpu, bus)
} else {
cpu.exec_lea(bus, mode, reg)
}
}
_ if (opcode & 0xFFC0) == 0x4840 => {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
if mode.is_register_direct() || matches!(mode, AddressingMode::Immediate) {
illegal_instruction(cpu, bus)
} else {
cpu.exec_pea(bus, mode)
}
}
_ if (opcode & 0x0400) == 0 && (opcode >> 6) & 3 == 2 && ea_mode >= 2 => {
let mask = cpu.read_imm_16(bus);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
cpu.exec_movem_to_mem(bus, Size::Word, mode, mask)
}
_ if (opcode & 0x0400) == 0 && (opcode >> 6) & 3 == 3 && ea_mode >= 2 => {
let mask = cpu.read_imm_16(bus);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
cpu.exec_movem_to_mem(bus, Size::Long, mode, mask)
}
_ if (opcode & 0x0400) != 0 && (opcode >> 10) & 3 == 3 && ea_mode >= 2 => {
let mask = cpu.read_imm_16(bus);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let size = if (opcode >> 6) & 1 == 0 {
Size::Word
} else {
Size::Long
};
cpu.exec_movem_to_reg(bus, size, mode, mask)
}
_ => illegal_instruction(cpu, bus),
}
}
}
}
fn dispatch_group_5<B: AddressBus>(cpu: &mut CpuCore, bus: &mut B, opcode: u16) -> i32 {
let size_bits = (opcode >> 6) & 3;
let ea_mode = ((opcode >> 3) & 7) as u8;
let ea_reg = (opcode & 7) as u8;
if size_bits == 3 {
let is_020_plus = matches!(
cpu.cpu_type,
CpuType::M68EC020
| CpuType::M68020
| CpuType::M68EC030
| CpuType::M68030
| CpuType::M68EC040
| CpuType::M68LC040
| CpuType::M68040
);
if is_020_plus && ea_mode == 7 && (ea_reg == 2 || ea_reg == 3 || ea_reg == 4) {
let condition = ((opcode >> 8) & 0xF) as u8;
match ea_reg {
2 => {
let _ = cpu.read_imm_16(bus);
}
3 => {
let _ = cpu.read_imm_32(bus);
}
4 => {}
_ => {}
}
if cpu.test_condition(condition) {
return cpu.take_exception(bus, 7);
} else {
return 4;
}
}
let condition = ((opcode >> 8) & 0xF) as u8;
if ea_mode == 1 {
let counter = cpu.d(ea_reg as usize) as u16;
let disp = cpu.read_imm_16(bus) as i16;
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
if !cpu.test_condition(condition) {
let new_counter = counter.wrapping_sub(1);
cpu.set_d(
ea_reg as usize,
(cpu.d(ea_reg as usize) & 0xFFFF0000) | new_counter as u32,
);
if new_counter != 0xFFFF {
cpu.pc = (cpu.pc as i32).wrapping_add(disp as i32 - 2) as u32;
10
} else {
14
}
} else {
12
}
} else {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let value = if cpu.test_condition(condition) {
0xFF
} else {
0x00
};
cpu.write_ea(bus, mode, Size::Byte, value);
if mode.is_register_direct() { 4 } else { 8 }
}
} else {
let data = ((opcode >> 9) & 7) as u32;
let data = if data == 0 { 8 } else { data };
let size = decode_size_00(size_bits);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
if opcode & 0x100 == 0 {
cpu.exec_addq(bus, size, data, mode)
} else {
cpu.exec_subq(bus, size, data, mode)
}
}
}
fn dispatch_group_6<B: AddressBus>(cpu: &mut CpuCore, bus: &mut B, opcode: u16) -> i32 {
let condition = ((opcode >> 8) & 0xF) as u8;
let displacement = (opcode & 0xFF) as u8;
let base_pc = cpu.pc;
let disp: i32 = if displacement == 0 {
cpu.read_imm_16(bus) as i16 as i32
} else if displacement == 0xFF {
cpu.read_imm_32(bus) as i32
} else {
displacement as i8 as i32
};
match condition {
0 => {
cpu.change_of_flow = true;
cpu.pc = (base_pc as i32).wrapping_add(disp) as u32;
10
}
1 => {
cpu.change_of_flow = true;
cpu.push_32(bus, cpu.pc);
cpu.pc = (base_pc as i32).wrapping_add(disp) as u32;
18
}
_ => {
if cpu.test_condition(condition) {
cpu.change_of_flow = true;
cpu.pc = (base_pc as i32).wrapping_add(disp) as u32;
10
} else if displacement == 0 {
12
} else {
8
}
}
}
}
fn dispatch_group_8<B: AddressBus>(cpu: &mut CpuCore, bus: &mut B, opcode: u16) -> i32 {
let reg = ((opcode >> 9) & 7) as usize;
let ea_mode = ((opcode >> 3) & 7) as u8;
let ea_reg = (opcode & 7) as u8;
let op_mode = (opcode >> 6) & 7;
match op_mode {
0..=2 => {
let size = decode_size_012(op_mode);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let src = cpu.read_ea(bus, mode, size);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
let (result, _) = cpu.exec_or(bus, size, src, cpu.d(reg));
cpu.set_d(reg, (cpu.d(reg) & !size.mask()) | result);
4
}
4..=6 => {
if op_mode == 4 && ea_mode == 0 {
cpu.exec_sbcd_rr(ea_reg as usize, reg)
} else if op_mode == 4 && ea_mode == 1 {
cpu.exec_sbcd_mm(bus, ea_reg as usize, reg)
} else if op_mode == 5 && (ea_mode == 0 || ea_mode == 1) {
if cpu.is_pre_68020 {
return illegal_instruction(cpu, bus);
}
let adj = cpu.read_imm_16(bus);
if ea_mode == 0 {
cpu.exec_pack_rr(ea_reg as usize, reg, adj)
} else {
cpu.exec_pack_mm(bus, ea_reg as usize, reg, adj)
}
} else if op_mode == 6 && (ea_mode == 0 || ea_mode == 1) {
if cpu.is_pre_68020 {
return illegal_instruction(cpu, bus);
}
let adj = cpu.read_imm_16(bus);
if ea_mode == 0 {
cpu.exec_unpk_rr(ea_reg as usize, reg, adj)
} else {
cpu.exec_unpk_mm(bus, ea_reg as usize, reg, adj)
}
} else {
let size = decode_size_012(op_mode - 4);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let ea = cpu.resolve_ea(bus, mode, size);
let dst = cpu.read_resolved_ea(bus, ea, size);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
let (result, _) = cpu.exec_or(bus, size, cpu.d(reg), dst);
cpu.write_resolved_ea(bus, ea, size, result);
8
}
}
3 => {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
cpu.exec_divu(bus, mode, reg)
}
7 => {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
cpu.exec_divs(bus, mode, reg)
}
_ => illegal_instruction(cpu, bus),
}
}
fn dispatch_group_9<B: AddressBus>(cpu: &mut CpuCore, bus: &mut B, opcode: u16) -> i32 {
let reg = ((opcode >> 9) & 7) as usize;
let ea_mode = ((opcode >> 3) & 7) as u8;
let ea_reg = (opcode & 7) as u8;
let op_mode = (opcode >> 6) & 7;
match op_mode {
0..=2 => {
let size = decode_size_012(op_mode);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let src = cpu.read_ea(bus, mode, size);
let dst = cpu.d(reg) & size.mask(); let (result, _) = cpu.exec_sub(bus, size, src, dst);
cpu.set_d(reg, (cpu.d(reg) & !size.mask()) | result);
4
}
3 | 7 => {
let size = if op_mode == 3 { Size::Word } else { Size::Long };
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let src = cpu.read_ea(bus, mode, size);
cpu.exec_suba(bus, size, src, reg)
}
4..=6 => {
let size = decode_size_012(op_mode - 4);
if ea_mode == 0 {
let src = cpu.d(ea_reg as usize) & size.mask();
let dst = cpu.d(reg) & size.mask();
let result = cpu.exec_subx(size, src, dst);
cpu.set_d(reg, (cpu.d(reg) & !size.mask()) | result);
4
} else if ea_mode == 1 {
let src_ea = cpu.resolve_ea(bus, AddressingMode::PreDecrement(ea_reg), size);
let dst_ea = cpu.resolve_ea(bus, AddressingMode::PreDecrement(reg as u8), size);
let src = cpu.read_resolved_ea(bus, src_ea, size);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
let dst = cpu.read_resolved_ea(bus, dst_ea, size);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
if cpu.cpu_type == CpuType::M68000
&& size != Size::Byte
&& let EaResult::Memory(addr) = dst_ea
&& (addr & 1) != 0
{
cpu.trigger_address_error(bus, addr, true, false);
return 50;
}
let result = cpu.exec_subx(size, src, dst);
cpu.write_resolved_ea(bus, dst_ea, size, result);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
18
} else {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let src = cpu.d(reg) & size.mask(); let ea = cpu.resolve_ea(bus, mode, size);
let dst = cpu.read_resolved_ea(bus, ea, size);
let (result, _) = cpu.exec_sub(bus, size, src, dst);
cpu.write_resolved_ea(bus, ea, size, result);
8
}
}
_ => illegal_instruction(cpu, bus),
}
}
fn dispatch_group_b<B: AddressBus>(cpu: &mut CpuCore, bus: &mut B, opcode: u16) -> i32 {
let reg = ((opcode >> 9) & 7) as usize;
let ea_mode = ((opcode >> 3) & 7) as u8;
let ea_reg = (opcode & 7) as u8;
let op_mode = (opcode >> 6) & 7;
match op_mode {
0..=2 => {
let size = decode_size_012(op_mode);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let src = cpu.read_ea(bus, mode, size);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
cpu.exec_cmp(size, src, cpu.d(reg))
}
3 | 7 => {
let size = if op_mode == 3 { Size::Word } else { Size::Long };
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let src = cpu.read_ea(bus, mode, size);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
cpu.exec_cmpa(size, src, reg)
}
4..=6 => {
let size = decode_size_012(op_mode - 4);
if ea_mode == 1 {
let src_ea = cpu.resolve_ea(bus, AddressingMode::PostIncrement(ea_reg), size);
let src_val = cpu.read_resolved_ea(bus, src_ea, size);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
let dst_ea = cpu.resolve_ea(bus, AddressingMode::PostIncrement(reg as u8), size);
let dst_val = cpu.read_resolved_ea(bus, dst_ea, size);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
cpu.exec_cmp(size, src_val, dst_val)
} else {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let ea = cpu.resolve_ea(bus, mode, size);
let dst = cpu.read_resolved_ea(bus, ea, size);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
let result = (cpu.d(reg) ^ dst) & size.mask();
cpu.write_resolved_ea(bus, ea, size, result);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
cpu.set_logic_flags(result, size);
8
}
}
_ => illegal_instruction(cpu, bus),
}
}
fn dispatch_group_c<B: AddressBus>(cpu: &mut CpuCore, bus: &mut B, opcode: u16) -> i32 {
let reg = ((opcode >> 9) & 7) as usize;
let ea_mode = ((opcode >> 3) & 7) as u8;
let ea_reg = (opcode & 7) as u8;
let op_mode = (opcode >> 6) & 7;
match op_mode {
0..=2 => {
let size = decode_size_012(op_mode);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let src = cpu.read_ea(bus, mode, size);
let (result, _) = cpu.exec_and(bus, size, src, cpu.d(reg));
cpu.set_d(reg, (cpu.d(reg) & !size.mask()) | result);
4
}
4..=6 => {
if op_mode == 4 && (ea_mode == 0 || ea_mode == 1) {
if ea_mode == 0 {
cpu.exec_abcd_rr(ea_reg as usize, reg)
} else {
cpu.exec_abcd_mm(bus, ea_reg as usize, reg)
}
} else {
let mode_field = (opcode >> 3) & 0x1F;
if mode_field == 0x08 || mode_field == 0x09 || mode_field == 0x11 {
cpu.exec_exg(opcode)
} else {
let size = decode_size_012(op_mode - 4);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let ea = cpu.resolve_ea(bus, mode, size);
let dst = cpu.read_resolved_ea(bus, ea, size);
let (result, _) = cpu.exec_and(bus, size, cpu.d(reg), dst);
cpu.write_resolved_ea(bus, ea, size, result);
8
}
}
}
3 => {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
cpu.exec_mulu(bus, mode, reg)
}
7 => {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
cpu.exec_muls(bus, mode, reg)
}
_ => illegal_instruction(cpu, bus),
}
}
fn dispatch_group_d<B: AddressBus>(cpu: &mut CpuCore, bus: &mut B, opcode: u16) -> i32 {
let reg = ((opcode >> 9) & 7) as usize;
let ea_mode = ((opcode >> 3) & 7) as u8;
let ea_reg = (opcode & 7) as u8;
let op_mode = (opcode >> 6) & 7;
match op_mode {
0..=2 => {
let size = decode_size_012(op_mode);
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let src = cpu.read_ea(bus, mode, size);
let dst = cpu.d(reg) & size.mask(); let (result, _) = cpu.exec_add(bus, size, src, dst);
cpu.set_d(reg, (cpu.d(reg) & !size.mask()) | result);
4
}
3 | 7 => {
let size = if op_mode == 3 { Size::Word } else { Size::Long };
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let src = cpu.read_ea(bus, mode, size);
cpu.exec_adda(bus, size, src, reg)
}
4..=6 => {
let size = decode_size_012(op_mode - 4);
if ea_mode == 0 {
let src = cpu.d(ea_reg as usize) & size.mask();
let dst = cpu.d(reg) & size.mask();
let result = cpu.exec_addx(size, src, dst);
cpu.set_d(reg, (cpu.d(reg) & !size.mask()) | result);
4
} else if ea_mode == 1 {
let src_ea = cpu.resolve_ea(bus, AddressingMode::PreDecrement(ea_reg), size);
let dst_ea = cpu.resolve_ea(bus, AddressingMode::PreDecrement(reg as u8), size);
let src = cpu.read_resolved_ea(bus, src_ea, size);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
let dst = cpu.read_resolved_ea(bus, dst_ea, size);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
if cpu.cpu_type == CpuType::M68000
&& size != Size::Byte
&& let EaResult::Memory(addr) = dst_ea
&& (addr & 1) != 0
{
cpu.trigger_address_error(bus, addr, true, false);
return 50;
}
let result = cpu.exec_addx(size, src, dst);
cpu.write_resolved_ea(bus, dst_ea, size, result);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
18
} else {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let src = cpu.d(reg) & size.mask(); let ea = cpu.resolve_ea(bus, mode, size);
let dst = cpu.read_resolved_ea(bus, ea, size);
let (result, _) = cpu.exec_add(bus, size, src, dst);
cpu.write_resolved_ea(bus, ea, size, result);
8
}
}
_ => illegal_instruction(cpu, bus),
}
}
fn dispatch_group_e<B: AddressBus>(cpu: &mut CpuCore, bus: &mut B, opcode: u16) -> i32 {
let ea_mode = ((opcode >> 3) & 7) as u8;
let ea_reg = (opcode & 7) as u8;
if (opcode & 0x00C0) == 0x00C0 && ((opcode >> 8) & 0xF) >= 0x8 {
if cpu.is_pre_68020 {
return illegal_instruction(cpu, bus);
}
return cpu.exec_bitfield(bus, opcode);
}
if (opcode >> 6) & 3 == 3 {
let mode = AddressingMode::decode(ea_mode, ea_reg).unwrap();
let ea = cpu.resolve_ea(bus, mode, Size::Word);
let value = cpu.read_resolved_ea(bus, ea, Size::Word);
if cpu.run_mode == RUN_MODE_BERR_AERR_RESET {
return 50;
}
let op = (opcode >> 9) & 7;
let direction = (opcode >> 8) & 1;
let (result, cycles) = match (op, direction) {
(0, 0) => cpu.exec_asr(Size::Word, 1, value),
(0, 1) => cpu.exec_asl(Size::Word, 1, value),
(1, 0) => cpu.exec_lsr(Size::Word, 1, value),
(1, 1) => cpu.exec_lsl(Size::Word, 1, value),
(2, 0) => cpu.exec_roxr(Size::Word, 1, value),
(2, 1) => cpu.exec_roxl(Size::Word, 1, value),
(3, 0) => cpu.exec_ror(Size::Word, 1, value),
(3, 1) => cpu.exec_rol(Size::Word, 1, value),
_ => return illegal_instruction(cpu, bus),
};
cpu.write_resolved_ea(bus, ea, Size::Word, result);
cycles + 4
} else {
let size = decode_size_00((opcode >> 6) & 3);
let count_or_reg = ((opcode >> 9) & 7) as usize;
let shift = if opcode & 0x20 != 0 {
cpu.d(count_or_reg) & 63
} else {
let c = count_or_reg as u32;
if c == 0 { 8 } else { c }
};
let reg = ea_reg as usize;
let value = cpu.d(reg) & size.mask();
let direction = (opcode >> 8) & 1;
let op = (opcode >> 3) & 3;
let (result, cycles) = match (op, direction) {
(0, 0) => cpu.exec_asr(size, shift, value),
(0, 1) => cpu.exec_asl(size, shift, value),
(1, 0) => cpu.exec_lsr(size, shift, value),
(1, 1) => cpu.exec_lsl(size, shift, value),
(2, 0) => cpu.exec_roxr(size, shift, value),
(2, 1) => cpu.exec_roxl(size, shift, value),
(3, 0) => cpu.exec_ror(size, shift, value),
(3, 1) => cpu.exec_rol(size, shift, value),
_ => return illegal_instruction(cpu, bus),
};
cpu.set_d(reg, (cpu.d(reg) & !size.mask()) | result);
cycles
}
}
fn decode_size_00(bits: u16) -> Size {
match bits {
0 => Size::Byte,
1 => Size::Word,
2 => Size::Long,
_ => Size::Byte,
}
}
fn decode_size_012(bits: u16) -> Size {
match bits {
0 => Size::Byte,
1 => Size::Word,
2 => Size::Long,
_ => Size::Long,
}
}
fn read_immediate<B: AddressBus>(cpu: &mut CpuCore, bus: &mut B, size: Size) -> u32 {
match size {
Size::Byte => cpu.read_imm_16(bus) as u32 & 0xFF,
Size::Word => cpu.read_imm_16(bus) as u32,
Size::Long => cpu.read_imm_32(bus),
}
}
fn illegal_instruction<B: AddressBus>(_cpu: &mut CpuCore, _bus: &mut B) -> i32 {
ILLEGAL_SENTINEL
}
fn exception_1010(_cpu: &mut CpuCore, _opcode: u16) -> i32 {
super::decode::ALINE_TRAP_SENTINEL
}
fn exception_1111(_cpu: &mut CpuCore, _opcode: u16) -> i32 {
super::decode::FLINE_TRAP_SENTINEL
}