use super::execute::RUN_MODE_BERR_AERR_RESET;
use super::memory::{AddressBus, BusFaultKind};
use super::op_cache::CachedOp;
use super::types::CpuType;
use crate::fpu::FloatX80;
#[cfg(feature = "serde")]
fn precise_bus_default() -> bool {
true
}
pub const XFLAG_SET: u32 = 0x100;
pub const NFLAG_SET: u32 = 0x80;
pub const VFLAG_SET: u32 = 0x80;
pub const CFLAG_SET: u32 = 0x100;
pub const SFLAG_SET: u32 = 4;
pub const MFLAG_SET: u32 = 2;
pub const FC_USER_DATA: u32 = 1;
pub const FC_USER_PROGRAM: u32 = 2;
pub const FC_SUPERVISOR_DATA: u32 = 5;
pub const FC_SUPERVISOR_PROGRAM: u32 = 6;
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CpuCore {
pub dar: [u32; 16],
pub dar_save: [u32; 16],
pub sr_save: u16,
pub ppc: u32,
pub pc: u32,
pub sp: [u32; 8],
pub vbr: u32,
pub sfc: u32,
pub dfc: u32,
pub cacr: u32,
pub caar: u32,
pub cacr_pending_ops: u32,
pub itt0: u32,
pub itt1: u32,
pub dtt0: u32,
pub dtt1: u32,
pub ir: u32,
pub fpr: [FloatX80; 8],
pub fpiar: u32,
pub fpsr: u32,
pub fpcr: u32,
pub t1_flag: u32,
pub t0_flag: u32,
pub s_flag: u32,
pub m_flag: u32,
pub x_flag: u32,
pub n_flag: u32,
pub not_z_flag: u32,
pub v_flag: u32,
pub c_flag: u32,
pub int_mask: u32,
pub int_level: u32,
pub stopped: u32,
pub change_of_flow: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub loop_mode: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub loop_body_word: u16,
#[cfg_attr(feature = "serde", serde(default))]
pub loop_dbcc_word: u16,
pub prefetch_queue: [u16; 2],
pub prefetch_count: u8,
pub consume_without_prefetch: bool,
pub pending_sync_clocks: u32,
#[cfg_attr(feature = "serde", serde(skip, default = "precise_bus_default"))]
pub(crate) precise_bus: bool,
pub cpu_type: CpuType,
pub address_mask: u32,
pub sr_mask: u32,
pub instr_mode: u32,
pub run_mode: u32,
pub exception_processing: bool,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) instruction_exception_vector: Option<u32>,
#[cfg_attr(feature = "serde", serde(skip))]
pub last_exception_vector: Option<u32>,
pub has_pmmu: bool,
pub pmmu_enabled: bool,
pub is_pre_68020: bool,
pub fpu_just_reset: bool,
pub fpu_present: bool,
pub reset_cycles: u32,
pub cyc_bcc_notake_b: i32,
pub cyc_bcc_notake_w: i32,
pub cyc_dbcc_f_noexp: i32,
pub cyc_dbcc_f_exp: i32,
pub cyc_scc_r_true: i32,
pub cyc_movem_w: i32,
pub cyc_movem_l: i32,
pub cyc_shift: i32,
pub cyc_reset: i32,
pub virq_state: u32,
pub nmi_pending: u32,
pub mmu_crp_aptr: u32,
pub mmu_crp_limit: u32,
pub mmu_srp_aptr: u32,
pub mmu_srp_limit: u32,
pub mmu_tc: u32,
pub mmu_sr: u32,
pub mmu_tt0: u32,
pub mmu_tt1: u32,
pub dacr0: u32,
pub dacr1: u32,
pub iacr0: u32,
pub iacr1: u32,
pub pcr: u32,
pub buscr: u32,
#[cfg_attr(feature = "serde", serde(skip))]
pub atc: crate::mmu::Atc,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) pending_fault_cause: Option<crate::mmu::MmuFaultCause>,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) mmu_fc_override: Option<u8>,
pub(crate) mmu_read_override: Option<(u32, u32)>,
pub(crate) mmu_write_suppress: Option<u32>,
pub(crate) pending_fault_wdata: u32,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) fault_resume: Option<(u32, u16, [u32; 16])>,
pub cycles_remaining: i32,
pub initial_cycles: i32,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) decode_table: Option<Box<[CachedOp; super::op_cache::DECODE_TABLE_SIZE]>>,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) fm_ptr: usize,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) fm_base: u32,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) fm_len: u32,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) trace_record_skip: [u32; 4],
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) trace_probe_skip: [u32; 4],
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) trace_record_skip_at: u8,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) trace_probe_skip_at: u8,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) trace_recording: bool,
pub oep060: crate::core::timing_060::Oep060Timing,
pub emulate_unimplemented_060: bool,
pub sst_m68000_compat: bool,
}
pub const CACR_EI: u32 = 1 << 0;
pub const CACR_FI: u32 = 1 << 1;
pub const CACR_CEI: u32 = 1 << 2;
pub const CACR_CI: u32 = 1 << 3;
pub const CACR_IBE: u32 = 1 << 4;
pub const CACR_ED: u32 = 1 << 8;
pub const CACR_FD: u32 = 1 << 9;
pub const CACR_CED: u32 = 1 << 10;
pub const CACR_CD: u32 = 1 << 11;
pub const CACR_DBE: u32 = 1 << 12;
pub const CACR_WA: u32 = 1 << 13;
pub const CACR_040_IE: u32 = 1 << 15;
pub const CACR_040_DE: u32 = 1 << 31;
pub const CACR_060_EDC: u32 = 1 << 31;
pub const CACR_060_NAD: u32 = 1 << 30;
pub const CACR_060_ESB: u32 = 1 << 29;
pub const CACR_060_DPI: u32 = 1 << 28;
pub const CACR_060_FOC: u32 = 1 << 27;
pub const CACR_060_EBC: u32 = 1 << 23;
pub const CACR_060_CABC: u32 = 1 << 22;
pub const CACR_060_CUBC: u32 = 1 << 21;
pub const CACR_060_EIC: u32 = 1 << 15;
pub const CACR_060_NAI: u32 = 1 << 14;
pub const CACR_060_FIC: u32 = 1 << 13;
pub const PCR_ESS: u32 = 1 << 0;
pub const PCR_DFP: u32 = 1 << 1;
pub const PCR_EDEBUG: u32 = 1 << 7;
pub const PCR_060_RESET: u32 = 0x0430_0100;
impl Default for CpuCore {
fn default() -> Self {
Self::new()
}
}
impl CpuCore {
#[inline]
pub(crate) fn scale_cycles_for_cpu_type(&self, cycles: i32) -> i32 {
use crate::core::types::CpuType;
match self.cpu_type {
CpuType::M68000 | CpuType::M68010 | CpuType::Invalid => cycles,
_ => ((cycles * 5 + 7) / 8).max(2),
}
}
pub fn new() -> Self {
let mut cpu = Self {
dar: [0; 16],
dar_save: [0; 16],
sr_save: 0,
ppc: 0,
pc: 0,
sp: [0; 8],
vbr: 0,
sfc: 0,
dfc: 0,
cacr: 0,
caar: 0,
cacr_pending_ops: 0,
itt0: 0,
itt1: 0,
dtt0: 0,
dtt1: 0,
ir: 0,
fpr: [FloatX80::default(); 8],
fpiar: 0,
fpsr: 0,
fpcr: 0,
t1_flag: 0,
t0_flag: 0,
s_flag: SFLAG_SET, m_flag: 0,
x_flag: 0,
n_flag: 0,
not_z_flag: 1, v_flag: 0,
c_flag: 0,
int_mask: 0x0700, int_level: 0,
stopped: 0,
fm_ptr: 0,
fm_base: 0,
fm_len: 0,
trace_record_skip: [super::trace_jit::TRACE_PC_NONE; 4],
trace_probe_skip: [super::trace_jit::TRACE_PC_NONE; 4],
trace_record_skip_at: 0,
trace_probe_skip_at: 0,
trace_recording: false,
change_of_flow: false,
loop_mode: false,
loop_body_word: 0,
loop_dbcc_word: 0,
prefetch_queue: [0; 2],
prefetch_count: 0,
consume_without_prefetch: false,
pending_sync_clocks: 0,
precise_bus: true,
cpu_type: CpuType::M68000,
address_mask: 0x00FFFFFF, sr_mask: 0xA71F, instr_mode: 0,
run_mode: 0,
exception_processing: false,
instruction_exception_vector: None,
last_exception_vector: None,
has_pmmu: false,
pmmu_enabled: false,
is_pre_68020: true,
fpu_just_reset: true,
reset_cycles: 0,
cyc_bcc_notake_b: -2,
cyc_bcc_notake_w: 2,
cyc_dbcc_f_noexp: -2,
cyc_dbcc_f_exp: 2,
cyc_scc_r_true: 2,
cyc_movem_w: 2,
cyc_movem_l: 3,
cyc_shift: 1,
cyc_reset: 132,
virq_state: 0,
nmi_pending: 0,
mmu_crp_aptr: 0,
mmu_crp_limit: 0,
mmu_srp_aptr: 0,
mmu_srp_limit: 0,
mmu_tc: 0,
mmu_sr: 0,
mmu_tt0: 0,
mmu_tt1: 0,
dacr0: 0,
dacr1: 0,
iacr0: 0,
iacr1: 0,
pcr: PCR_060_RESET,
buscr: 0,
atc: crate::mmu::Atc::default(),
pending_fault_cause: None,
mmu_fc_override: None,
mmu_read_override: None,
mmu_write_suppress: None,
pending_fault_wdata: 0,
fault_resume: None,
cycles_remaining: 0,
initial_cycles: 0,
decode_table: None,
oep060: Default::default(),
emulate_unimplemented_060: false,
sst_m68000_compat: false,
fpu_present: true,
};
cpu.set_cpu_type(CpuType::M68000);
cpu
}
#[inline]
pub fn set_sst_m68000_compat(&mut self, on: bool) {
self.sst_m68000_compat = on;
}
pub fn set_cpu_type(&mut self, cpu_type: CpuType) {
self.clear_execution_pipeline_state();
self.cpu_type = cpu_type;
self.is_pre_68020 = matches!(
cpu_type,
CpuType::M68000 | CpuType::M68010 | CpuType::SCC68070
);
self.clear_decoded_op_cache();
match cpu_type {
CpuType::M68000 => {
self.address_mask = 0x00FFFFFF;
self.sr_mask = 0xA71F;
self.has_pmmu = false;
}
CpuType::M68010 => {
self.address_mask = 0x00FFFFFF;
self.sr_mask = 0xA71F;
self.has_pmmu = false;
}
CpuType::SCC68070 => {
self.address_mask = 0xFFFFFFFF;
self.sr_mask = 0xA71F;
self.has_pmmu = false;
}
CpuType::M68EC020 | CpuType::M68020 => {
self.address_mask = 0xFFFFFFFF;
self.sr_mask = 0xF71F;
self.has_pmmu = false;
}
CpuType::M68EC030 => {
self.address_mask = 0xFFFFFFFF;
self.sr_mask = 0xF71F;
self.has_pmmu = false;
}
CpuType::M68030 => {
self.address_mask = 0xFFFFFFFF;
self.sr_mask = 0xF71F;
self.has_pmmu = true;
}
CpuType::M68EC040 | CpuType::M68LC040 => {
self.address_mask = 0xFFFFFFFF;
self.sr_mask = 0xF71F;
self.has_pmmu = false;
}
CpuType::M68040 => {
self.address_mask = 0xFFFFFFFF;
self.sr_mask = 0xF71F;
self.has_pmmu = true;
}
CpuType::M68060 => {
self.address_mask = 0xFFFFFFFF;
self.sr_mask = 0xB71F;
self.has_pmmu = true;
}
_ => {}
}
}
#[inline]
fn sp_index(&self) -> usize {
if self.cpu_type == CpuType::M68060 {
self.s_flag as usize
} else {
(self.s_flag | ((self.s_flag >> 1) & self.m_flag)) as usize
}
}
fn backup_sp(&mut self) {
let idx = self.sp_index();
self.sp[idx] = self.dar[15];
}
fn restore_sp(&mut self) {
let idx = self.sp_index();
self.dar[15] = self.sp[idx];
}
pub fn set_s_flag(&mut self, value: u32) {
self.backup_sp();
self.s_flag = value;
self.restore_sp();
}
pub fn set_sm_flag(&mut self, value: u32) {
self.backup_sp();
self.s_flag = value & SFLAG_SET;
self.m_flag = value & MFLAG_SET;
self.restore_sp();
}
pub fn set_sm_flag_nosp(&mut self, value: u32) {
self.s_flag = value & SFLAG_SET;
self.m_flag = value & MFLAG_SET;
}
pub fn pulse_reset(&mut self) {
self.clear_execution_pipeline_state();
self.stopped = 0;
self.t1_flag = 0;
self.t0_flag = 0;
self.m_flag = 0;
self.run_mode = 0;
self.instr_mode = 0;
self.vbr = 0;
self.cacr = 0;
self.cacr_pending_ops |= CACR_CI | CACR_CD;
self.pcr = PCR_060_RESET;
self.buscr = 0;
self.oep060.branch_cache.clear_all();
self.mmu_tc = 0;
self.pmmu_enabled = false;
self.itt0 = 0;
self.itt1 = 0;
self.dtt0 = 0;
self.dtt1 = 0;
self.mmu_tt0 = 0;
self.mmu_tt1 = 0;
self.atc.flush_all();
self.prefetch_queue = [0; 2];
self.prefetch_count = 0;
self.consume_without_prefetch = false;
self.pending_sync_clocks = 0;
self.x_flag = 0;
self.n_flag = 0;
self.v_flag = 0;
self.c_flag = 0;
self.not_z_flag = 0;
self.set_s_flag(SFLAG_SET);
self.int_mask = 0x0700; }
#[inline]
pub fn prefetch_enabled(&self) -> bool {
self.precise_bus && matches!(self.cpu_type, CpuType::M68000 | CpuType::M68010)
}
pub(crate) fn set_precise_bus(&mut self, precise: bool) {
if self.precise_bus == precise {
return;
}
self.clear_execution_pipeline_state();
self.precise_bus = precise;
self.prefetch_count = 0;
self.pending_sync_clocks = 0;
self.consume_without_prefetch = false;
self.loop_mode = false;
}
#[inline]
pub(crate) fn clear_execution_pipeline_state(&mut self) {
self.break_060_pipeline();
}
#[inline]
pub fn internal_cycles(&mut self, clocks: u32) {
if self.prefetch_enabled() {
self.pending_sync_clocks = self.pending_sync_clocks.wrapping_add(clocks);
}
}
#[inline]
pub(crate) fn flush_sync<B: AddressBus>(&mut self, bus: &mut B) {
if self.pending_sync_clocks > 0 {
let clocks = std::mem::take(&mut self.pending_sync_clocks);
bus.sync(clocks);
}
}
#[inline]
pub(crate) fn ipl_poll_point<B: AddressBus>(&mut self, bus: &mut B) {
if self.prefetch_enabled() {
bus.ipl_hold_sample();
}
}
#[inline]
pub fn invalidate_prefetch(&mut self) {
self.prefetch_count = 0;
}
fn prefetch_read<B: AddressBus>(&mut self, bus: &mut B, addr: u32) -> Option<u16> {
self.flush_sync(bus);
let addr = self.address(addr);
match bus.try_read_word(addr) {
Ok(v) => Some(v),
Err(_) => {
self.trigger_bus_error(bus, addr, false, true, 2);
None
}
}
}
pub fn full_prefetch<B: AddressBus>(&mut self, bus: &mut B) {
self.prefetch_first(bus);
self.prefetch_second(bus);
}
pub fn prefetch_first<B: AddressBus>(&mut self, bus: &mut B) {
if !self.prefetch_enabled() {
return;
}
self.prefetch_count = 0;
if self.pc & 1 != 0 {
return;
}
if let Some(w) = self.prefetch_read(bus, self.pc) {
self.prefetch_queue[0] = w;
self.prefetch_count = 1;
}
}
pub fn prefetch_second<B: AddressBus>(&mut self, bus: &mut B) {
if !self.prefetch_enabled() || self.prefetch_count != 1 {
return;
}
if self.pc & 1 != 0 || self.run_mode == super::execute::RUN_MODE_BERR_AERR_RESET {
return;
}
if let Some(w) = self.prefetch_read(bus, self.pc.wrapping_add(2)) {
self.prefetch_queue[1] = w;
self.prefetch_count = 2;
}
}
pub fn top_up_prefetch_one<B: AddressBus>(&mut self, bus: &mut B) {
if self.loop_mode {
return;
}
if !self.prefetch_enabled() || self.pc & 1 != 0 || self.prefetch_count >= 2 {
return;
}
if self.run_mode == super::execute::RUN_MODE_BERR_AERR_RESET {
return;
}
let slot = self.prefetch_count as usize;
let addr = self.pc.wrapping_add(2 * slot as u32);
if let Some(w) = self.prefetch_read(bus, addr) {
self.prefetch_queue[slot] = w;
self.prefetch_count += 1;
}
}
pub fn top_up_prefetch<B: AddressBus>(&mut self, bus: &mut B) {
if self.loop_mode {
return;
}
if !self.prefetch_enabled() || self.pc & 1 != 0 || self.stopped != 0 {
return;
}
if self.run_mode == super::execute::RUN_MODE_BERR_AERR_RESET {
return;
}
while self.prefetch_count < 2 {
let before = self.prefetch_count;
self.top_up_prefetch_one(bus);
if self.prefetch_count == before {
return;
}
}
}
pub fn read_long_predec_68000<B: AddressBus>(&mut self, bus: &mut B, addr: u32) -> u32 {
let lo = self.read_16(bus, addr.wrapping_add(2)) as u32;
let hi = self.read_16(bus, addr) as u32;
(hi << 16) | lo
}
pub fn write_long_mm_interleaved_68000<B: AddressBus>(
&mut self,
bus: &mut B,
addr: u32,
value: u32,
) {
self.write_16(bus, addr.wrapping_add(2), (value & 0xFFFF) as u16);
self.ipl_poll_point(bus);
self.top_up_prefetch(bus);
self.write_16(bus, addr, (value >> 16) as u16);
}
pub fn consume_imm_16_no_prefetch<B: AddressBus>(&mut self, bus: &mut B) -> u16 {
if self.prefetch_count > 0 {
let word = self.prefetch_queue[0];
self.prefetch_queue[0] = self.prefetch_queue[1];
self.prefetch_count -= 1;
self.pc = self.pc.wrapping_add(2);
return word;
}
match self.prefetch_read(bus, self.pc) {
Some(w) => {
self.pc = self.pc.wrapping_add(2);
w
}
None => 0,
}
}
pub fn reset<B: AddressBus>(&mut self, bus: &mut B) {
self.pulse_reset();
let ssp = bus.read_long(0);
self.dar[15] = ssp;
self.sp[SFLAG_SET as usize] = ssp; self.sp[(SFLAG_SET | MFLAG_SET) as usize] = ssp;
self.pc = bus.read_long(4);
self.invalidate_prefetch();
self.cycles_remaining -= self.cyc_reset;
}
pub fn reset_soft(&mut self) {
self.pulse_reset();
}
#[inline]
pub fn d(&self, reg: usize) -> u32 {
self.dar[reg & 7]
}
#[inline]
pub fn set_d(&mut self, reg: usize, value: u32) {
self.dar[reg & 7] = value;
}
#[inline]
pub fn is_stopped(&self) -> bool {
self.stopped != 0 && self.run_mode != RUN_MODE_BERR_AERR_RESET
}
#[inline]
pub fn is_halted(&self) -> bool {
self.stopped != 0 && self.run_mode == RUN_MODE_BERR_AERR_RESET
}
#[inline]
pub fn a(&self, reg: usize) -> u32 {
self.dar[8 + (reg & 7)]
}
#[inline]
pub fn set_a(&mut self, reg: usize, value: u32) {
self.dar[8 + (reg & 7)] = value;
}
#[inline]
pub fn sp(&self) -> u32 {
self.dar[15]
}
#[inline]
pub fn set_sp(&mut self, value: u32) {
self.dar[15] = value;
}
pub fn get_usp(&self) -> u32 {
if self.s_flag == 0 {
self.dar[15]
} else {
self.sp[0]
}
}
pub fn set_usp(&mut self, value: u32) {
if self.s_flag == 0 {
self.dar[15] = value;
} else {
self.sp[0] = value;
}
}
pub fn read_control_register(&self, reg: u16) -> u32 {
match reg {
0x000 => self.sfc, 0x001 => self.dfc, 0x002 => self.cacr, 0x003 => self.mmu_tc, 0x004 => self.itt0, 0x005 => self.itt1, 0x006 => self.dtt0, 0x007 => self.dtt1, 0x008 if self.is_060() => self.buscr,
0x008 => self.dacr0, 0x009 => self.dacr1, 0x00A => self.iacr0, 0x00B => self.iacr1, 0x800 => {
if self.s_flag == 0 {
self.dar[15]
} else {
self.sp[0]
}
}
0x801 => self.vbr, 0x802 => self.caar, 0x803 => {
if self.s_flag != 0 && self.m_flag != 0 {
self.dar[15]
} else {
self.sp[6]
}
}
0x804 => {
if self.s_flag != 0 && self.m_flag == 0 {
self.dar[15]
} else {
self.sp[4]
}
}
0x805 => self.mmu_sr, 0x806 => self.mmu_crp_aptr, 0x807 => self.mmu_srp_aptr, 0x808 if self.is_060() => self.pcr, _ => 0, }
}
pub fn write_control_register(&mut self, reg: u16, value: u32) {
match reg {
0x000 => self.sfc = value & 7, 0x001 => self.dfc = value & 7, 0x002 => {
use crate::core::types::CpuType;
let (persist, strobes) = match self.cpu_type {
CpuType::M68EC020 | CpuType::M68020 => (CACR_EI | CACR_FI, CACR_CEI | CACR_CI),
CpuType::M68EC030 | CpuType::M68030 => (
CACR_EI | CACR_FI | CACR_IBE | CACR_ED | CACR_FD | CACR_DBE | CACR_WA,
CACR_CEI | CACR_CI | CACR_CED | CACR_CD,
),
CpuType::M68060 => {
if value & CACR_060_CABC != 0 {
self.oep060.branch_cache.clear_all();
} else if value & CACR_060_CUBC != 0 {
self.oep060.branch_cache.clear_user();
}
if self.cacr & CACR_060_EBC != 0 && value & CACR_060_EBC == 0 {
self.oep060.branch_cache.clear_all();
}
(
CACR_060_EDC
| CACR_060_NAD
| CACR_060_ESB
| CACR_060_DPI
| CACR_060_FOC
| CACR_060_EBC
| CACR_060_EIC
| CACR_060_NAI
| CACR_060_FIC,
0,
)
}
_ => (CACR_040_IE | CACR_040_DE, 0),
};
self.cacr = value & persist;
self.cacr_pending_ops |= value & strobes;
}
0x003 => {
self.mmu_tc = if self.is_040() {
value & 0xC000
} else if self.is_060() {
value & 0xFFFE
} else {
value
};
self.pmmu_enabled = self.tc_enable();
}
0x004 => self.itt0 = value & 0xFFFF_E364, 0x005 => self.itt1 = value & 0xFFFF_E364, 0x006 => self.dtt0 = value & 0xFFFF_E364, 0x007 => self.dtt1 = value & 0xFFFF_E364, 0x008 if self.is_060() => {
self.buscr = (self.buscr & 0x5000_0000) | (value & 0xA000_0000);
}
0x008 => self.dacr0 = value, 0x009 => self.dacr1 = value, 0x00A => self.iacr0 = value, 0x00B => self.iacr1 = value, 0x800 => {
if self.s_flag == 0 {
self.dar[15] = value;
} else {
self.sp[0] = value;
}
}
0x801 => self.vbr = value, 0x802 => self.caar = value, 0x803 => {
if self.s_flag != 0 && self.m_flag != 0 {
self.dar[15] = value;
} else {
self.sp[6] = value;
}
}
0x804 => {
if self.s_flag != 0 && self.m_flag == 0 {
self.dar[15] = value;
} else {
self.sp[4] = value;
}
}
0x805 => self.mmu_sr = value, 0x806 => self.mmu_crp_aptr = value, 0x807 => self.mmu_srp_aptr = value, 0x808 if self.is_060() => {
let writable = PCR_EDEBUG | PCR_DFP | PCR_ESS;
self.pcr = (self.pcr & !writable) | (value & writable);
}
_ => {} }
if matches!(reg, 0x003 | 0x004 | 0x005 | 0x006 | 0x007 | 0x806 | 0x807) {
self.atc.flush_all();
}
}
#[inline]
pub fn is_040(&self) -> bool {
matches!(
self.cpu_type,
CpuType::M68EC040 | CpuType::M68LC040 | CpuType::M68040
)
}
#[inline]
pub(crate) fn trap_unimpl_060(&self) -> bool {
self.cpu_type == CpuType::M68060 && !self.emulate_unimplemented_060
}
#[inline]
pub fn is_060(&self) -> bool {
self.cpu_type == CpuType::M68060
}
#[inline]
pub fn tc_enable(&self) -> bool {
if self.is_040() || self.is_060() {
self.mmu_tc & 0x0000_8000 != 0
} else {
self.mmu_tc & 0x8000_0000 != 0
}
}
#[inline]
pub fn address(&self, addr: u32) -> u32 {
addr & self.address_mask
}
#[inline]
pub(crate) fn faulted(&self) -> bool {
self.run_mode == RUN_MODE_BERR_AERR_RESET
}
pub(crate) fn end_faulted_instruction(&mut self) {
if let Some((handler_pc, handler_sr, handler_dar)) = self.fault_resume.take() {
self.pc = handler_pc;
self.set_sr_noint_nosp(handler_sr);
self.dar = handler_dar;
}
if self.stopped != 0 {
return;
}
self.run_mode = super::execute::RUN_MODE_NORMAL;
}
pub(crate) fn trigger_address_error<B: AddressBus>(
&mut self,
bus: &mut B,
address: u32,
write: bool,
instruction: bool,
) {
if self.faulted() {
return;
}
if std::env::var_os("M68K_DIAG_ADDRESS_ERROR").is_some() {
eprintln!(
"m68k address error: addr={address:#010X} write={write} instr={instruction} \
pc={:#010X} ppc={:#010X} sp={:#010X} sr={:#06X}",
self.pc,
self.ppc,
self.sp(),
self.get_sr(),
);
let sp = self.sp();
let mut words = Vec::new();
for i in -8i32..8 {
let a = sp.wrapping_add((i * 2) as u32);
words.push(format!("{:04X}", bus.read_word(a)));
}
eprintln!(
"m68k stack around sp ({:#010X}-16..+16): {}",
sp,
words.join(" ")
);
eprintln!(
"m68k regs d0-d7={:08X?} a0-a7={:08X?} vbr={:#010X}",
&self.dar[0..8],
&self.dar[8..16],
self.vbr
);
}
self.set_sr_noint_nosp(self.sr_save);
self.dar = self.dar_save;
let _ = self.exception_address_error(bus, address, write, instruction);
self.fault_resume = Some((self.pc, self.get_sr(), self.dar));
self.run_mode = RUN_MODE_BERR_AERR_RESET;
}
pub(crate) fn trigger_address_error_no_rollback<B: AddressBus>(
&mut self,
bus: &mut B,
address: u32,
write: bool,
instruction: bool,
) {
if self.faulted() {
return;
}
let _ = self.exception_address_error(bus, address, write, instruction);
self.run_mode = RUN_MODE_BERR_AERR_RESET;
}
pub(crate) fn trigger_bus_error<B: AddressBus>(
&mut self,
bus: &mut B,
address: u32,
write: bool,
instruction: bool,
size: u32,
) {
if self.faulted() {
return;
}
if self.exception_processing {
self.stopped = 1;
self.run_mode = RUN_MODE_BERR_AERR_RESET;
return;
}
self.set_sr_noint_nosp(self.sr_save);
self.dar = self.dar_save;
self.exception_processing = true;
let cause = self.pending_fault_cause.take();
let _ = self.exception_bus_error(bus, address, write, instruction, size, cause);
self.exception_processing = false;
self.fault_resume = Some((self.pc, self.get_sr(), self.dar));
self.run_mode = RUN_MODE_BERR_AERR_RESET;
}
pub(crate) fn trigger_bus_error_no_rollback<B: AddressBus>(
&mut self,
bus: &mut B,
address: u32,
write: bool,
instruction: bool,
) {
if self.faulted() {
return;
}
if self.exception_processing {
self.stopped = 1;
self.run_mode = RUN_MODE_BERR_AERR_RESET;
return;
}
self.exception_processing = true;
let cause = self.pending_fault_cause.take();
let _ = self.exception_bus_error(bus, address, write, instruction, 2, cause);
self.exception_processing = false;
self.fault_resume = Some((self.pc, self.get_sr(), self.dar));
self.run_mode = RUN_MODE_BERR_AERR_RESET;
}
#[inline]
pub(crate) fn mmu_page_mask(&self) -> u32 {
if self.is_040() || self.is_060() {
if self.mmu_tc & 0x0000_4000 != 0 {
0x1FFF
} else {
0xFFF
}
} else {
let ps = ((self.mmu_tc >> 20) & 0xF).clamp(8, 15);
(1u32 << ps) - 1
}
}
#[inline]
pub fn read_8<B: AddressBus>(&mut self, bus: &mut B, addr: u32) -> u8 {
if self.faulted() {
return 0;
}
self.flush_sync(bus);
let mut addr = self.address(addr);
if let Some((a, v)) = self.mmu_read_override
&& a == addr
{
self.mmu_read_override = None;
return v as u8;
}
{
if self.has_pmmu && self.pmmu_enabled {
match crate::mmu::translate_address(
self,
bus,
addr,
false,
self.is_supervisor(),
false,
) {
Ok(p) => addr = self.address(p),
Err(f) => {
self.handle_mmu_fault(bus, f, false, false, 1);
return 0;
}
}
}
}
match bus.try_read_byte(addr) {
Ok(v) => v,
Err(f) => {
if matches!(f.kind, BusFaultKind::BusError) {
self.trigger_bus_error(bus, addr, false, false, 1);
}
0
}
}
}
#[inline]
pub fn read_16<B: AddressBus>(&mut self, bus: &mut B, addr: u32) -> u16 {
if self.faulted() {
return 0;
}
self.flush_sync(bus);
let mut addr = self.address(addr);
if matches!(
self.cpu_type,
CpuType::M68000 | CpuType::M68010 | CpuType::SCC68070
) && (addr & 1) != 0
{
self.trigger_address_error(bus, addr, false, false);
return 0;
}
if self.has_pmmu && self.pmmu_enabled {
let pm = self.mmu_page_mask();
if addr & pm == pm {
let hi = self.read_8(bus, addr) as u16;
if self.faulted() {
return 0;
}
let lo = self.read_8(bus, addr.wrapping_add(1)) as u16;
return (hi << 8) | lo;
}
}
if let Some((a, v)) = self.mmu_read_override
&& a == addr
{
self.mmu_read_override = None;
return v as u16;
}
{
if self.has_pmmu && self.pmmu_enabled {
match crate::mmu::translate_address(
self,
bus,
addr,
false,
self.is_supervisor(),
false,
) {
Ok(p) => addr = self.address(p),
Err(f) => {
self.handle_mmu_fault(bus, f, false, false, 2);
return 0;
}
}
}
}
match bus.try_read_word(addr) {
Ok(v) => v,
Err(f) => {
if matches!(f.kind, BusFaultKind::BusError) {
self.trigger_bus_error(bus, addr, false, false, 2);
}
0
}
}
}
#[inline]
pub fn read_32<B: AddressBus>(&mut self, bus: &mut B, addr: u32) -> u32 {
if self.faulted() {
return 0;
}
self.flush_sync(bus);
let mut addr = self.address(addr);
if matches!(
self.cpu_type,
CpuType::M68000 | CpuType::M68010 | CpuType::SCC68070
) && (addr & 1) != 0
{
self.trigger_address_error(bus, addr, false, false);
return 0;
}
if self.has_pmmu && self.pmmu_enabled {
let pm = self.mmu_page_mask();
if addr & pm > pm - 3 {
let hi = self.read_16(bus, addr) as u32;
if self.faulted() {
return 0;
}
let lo = self.read_16(bus, addr.wrapping_add(2)) as u32;
return (hi << 16) | lo;
}
}
if let Some((a, v)) = self.mmu_read_override
&& a == addr
{
self.mmu_read_override = None;
return v;
}
{
if self.has_pmmu && self.pmmu_enabled {
match crate::mmu::translate_address(
self,
bus,
addr,
false,
self.is_supervisor(),
false,
) {
Ok(p) => addr = self.address(p),
Err(f) => {
self.handle_mmu_fault(bus, f, false, false, 4);
return 0;
}
}
}
}
match bus.try_read_long(addr) {
Ok(v) => v,
Err(f) => {
if matches!(f.kind, BusFaultKind::BusError) {
self.trigger_bus_error(bus, addr, false, false, 4);
}
0
}
}
}
#[inline]
pub fn write_8<B: AddressBus>(&mut self, bus: &mut B, addr: u32, value: u8) {
if self.faulted() {
return;
}
self.flush_sync(bus);
let mut addr = self.address(addr);
if self.mmu_write_suppress == Some(addr) {
self.mmu_write_suppress = None;
return;
}
self.pending_fault_wdata = value as u32;
{
if self.has_pmmu && self.pmmu_enabled {
match crate::mmu::translate_address(
self,
bus,
addr,
true,
self.is_supervisor(),
false,
) {
Ok(p) => addr = self.address(p),
Err(f) => {
self.handle_mmu_fault(bus, f, true, false, 1);
return;
}
}
}
}
if let Err(f) = bus.try_write_byte(addr, value)
&& matches!(f.kind, BusFaultKind::BusError)
{
self.trigger_bus_error(bus, addr, true, false, 1);
}
}
#[inline]
pub fn write_16<B: AddressBus>(&mut self, bus: &mut B, addr: u32, value: u16) {
if self.faulted() {
return;
}
self.flush_sync(bus);
let mut addr = self.address(addr);
if matches!(
self.cpu_type,
CpuType::M68000 | CpuType::M68010 | CpuType::SCC68070
) && (addr & 1) != 0
{
self.trigger_address_error(bus, addr, true, false);
return;
}
if self.has_pmmu && self.pmmu_enabled {
let pm = self.mmu_page_mask();
if addr & pm == pm {
self.write_8(bus, addr, (value >> 8) as u8);
if self.faulted() {
return;
}
self.write_8(bus, addr.wrapping_add(1), value as u8);
return;
}
}
if self.mmu_write_suppress == Some(addr) {
self.mmu_write_suppress = None;
return;
}
self.pending_fault_wdata = value as u32;
{
if self.has_pmmu && self.pmmu_enabled {
match crate::mmu::translate_address(
self,
bus,
addr,
true,
self.is_supervisor(),
false,
) {
Ok(p) => addr = self.address(p),
Err(f) => {
self.handle_mmu_fault(bus, f, true, false, 2);
return;
}
}
}
}
if let Err(f) = bus.try_write_word(addr, value)
&& matches!(f.kind, BusFaultKind::BusError)
{
self.trigger_bus_error(bus, addr, true, false, 2);
}
}
#[inline]
pub fn write_32<B: AddressBus>(&mut self, bus: &mut B, addr: u32, value: u32) {
if self.faulted() {
return;
}
self.flush_sync(bus);
let mut addr = self.address(addr);
if matches!(
self.cpu_type,
CpuType::M68000 | CpuType::M68010 | CpuType::SCC68070
) && (addr & 1) != 0
{
self.trigger_address_error(bus, addr, true, false);
return;
}
if self.has_pmmu && self.pmmu_enabled {
let pm = self.mmu_page_mask();
if addr & pm > pm - 3 {
self.write_16(bus, addr, (value >> 16) as u16);
if self.faulted() {
return;
}
self.write_16(bus, addr.wrapping_add(2), value as u16);
return;
}
}
if self.mmu_write_suppress == Some(addr) {
self.mmu_write_suppress = None;
return;
}
self.pending_fault_wdata = value;
{
if self.has_pmmu && self.pmmu_enabled {
match crate::mmu::translate_address(
self,
bus,
addr,
true,
self.is_supervisor(),
false,
) {
Ok(p) => addr = self.address(p),
Err(f) => {
self.handle_mmu_fault(bus, f, true, false, 4);
return;
}
}
}
}
if let Err(f) = bus.try_write_long(addr, value)
&& matches!(f.kind, BusFaultKind::BusError)
{
self.trigger_bus_error(bus, addr, true, false, 4);
}
}
pub(crate) fn handle_mmu_fault<B: AddressBus>(
&mut self,
bus: &mut B,
fault: crate::mmu::MmuFault,
write: bool,
instruction: bool,
size: u32,
) {
use crate::core::exceptions::vector;
use crate::mmu::MmuFaultKind;
self.pending_fault_cause = Some(fault.cause);
match fault.kind {
MmuFaultKind::BusError => {
self.trigger_bus_error(bus, fault.address, write, instruction, size)
}
MmuFaultKind::ConfigurationError => {
let _ = self.take_exception(bus, vector::MMU_CONFIGURATION_ERROR);
self.fault_resume = Some((self.pc, self.get_sr(), self.dar));
self.run_mode = RUN_MODE_BERR_AERR_RESET;
}
MmuFaultKind::IllegalOperation => {
let _ = self.take_exception(bus, vector::MMU_ILLEGAL_OPERATION_ERROR);
self.fault_resume = Some((self.pc, self.get_sr(), self.dar));
self.run_mode = RUN_MODE_BERR_AERR_RESET;
}
MmuFaultKind::AccessLevelViolation => {
self.trigger_bus_error(bus, fault.address, write, instruction, size);
}
}
self.mmu_fc_override = None;
}
pub(crate) fn handle_mmu_fetch_fault<B: AddressBus>(
&mut self,
bus: &mut B,
fault: crate::mmu::MmuFault,
) {
use crate::core::exceptions::vector;
use crate::mmu::MmuFaultKind;
match fault.kind {
MmuFaultKind::BusError => {
self.trigger_bus_error_no_rollback(bus, fault.address, false, true)
}
MmuFaultKind::ConfigurationError => {
let _ = self.take_exception(bus, vector::MMU_CONFIGURATION_ERROR);
self.run_mode = RUN_MODE_BERR_AERR_RESET;
}
MmuFaultKind::IllegalOperation => {
let _ = self.take_exception(bus, vector::MMU_ILLEGAL_OPERATION_ERROR);
self.run_mode = RUN_MODE_BERR_AERR_RESET;
}
MmuFaultKind::AccessLevelViolation => {
let _ = self.take_exception(bus, vector::MMU_ACCESS_LEVEL_VIOLATION_ERROR);
self.run_mode = RUN_MODE_BERR_AERR_RESET;
}
}
}
pub fn exec_mmu_op0<B: AddressBus>(&mut self, bus: &mut B, opcode: u16) -> i32 {
use super::ea::AddressingMode;
use super::types::Size;
if self.is_060() {
return 0;
}
if !self.has_pmmu {
return 0;
}
if !self.is_supervisor() {
if self.is_040() {
return 0;
}
return self.exception_privilege(bus);
}
let modes = self.read_imm_16(bus);
let is_ptest = (modes & 0xE000) == 0x8000;
let is_040 = matches!(
self.cpu_type,
super::types::CpuType::M68EC040
| super::types::CpuType::M68LC040
| super::types::CpuType::M68040
);
if is_ptest && is_040 {
return 4;
}
if is_ptest {
let level = ((modes >> 10) & 7) as u32;
let read = (modes & 0x0200) != 0;
let a_bit = (modes & 0x0100) != 0;
let an = ((modes >> 5) & 7) as usize;
let fcf = (modes & 0x1F) as u32;
let fc = if fcf & 0x10 != 0 {
fcf & 7 } else if fcf & 0x08 != 0 {
self.d((fcf & 7) as usize) & 7 } else if fcf == 1 {
self.dfc & 7
} else {
self.sfc & 7 };
let ea_mode = ((opcode >> 3) & 0x7) as u8;
let ea_reg = (opcode & 0x7) as u8;
let Some(am) = AddressingMode::decode(ea_mode, ea_reg) else {
return 0;
};
let super::ea::EaResult::Memory(addr) = self.resolve_ea(bus, am, Size::Long) else {
return 0;
};
let (sr, desc_addr) = crate::mmu::ptest_030(self, bus, addr, fc, !read, level);
self.mmu_sr = sr as u32;
if a_bit && level != 0 {
self.set_a(an, desc_addr);
}
return 8;
}
if (modes & 0xFDE0) == 0x2000 || (modes & 0xE200) == 0x2000
|| modes == 0xA000 || modes == 0x2800
|| (modes & 0xFFF8) == 0x2C00
{
return 8;
}
let ea_mode = ((opcode >> 3) & 0x7) as u8;
let ea_reg = (opcode & 0x7) as u8;
let Some(am) = AddressingMode::decode(ea_mode, ea_reg) else {
return 0;
};
let to_ea = (modes & 0x0200) != 0;
let preg = ((modes >> 10) & 0x1F) as u8;
let ea = self.resolve_ea(bus, am, Size::Long);
fn ea_addr_only(ea: super::ea::EaResult) -> Option<u32> {
match ea {
super::ea::EaResult::Memory(a) => Some(a),
_ => None,
}
}
if to_ea {
match preg {
0x10 => {
self.write_resolved_ea(bus, ea, Size::Long, self.mmu_tc);
4
}
0x12 => {
let Some(a) = ea_addr_only(ea) else { return 0 };
self.write_32(bus, a, self.mmu_srp_limit);
self.write_32(bus, a.wrapping_add(4), self.mmu_srp_aptr);
8
}
0x13 => {
let Some(a) = ea_addr_only(ea) else { return 0 };
self.write_32(bus, a, self.mmu_crp_limit);
self.write_32(bus, a.wrapping_add(4), self.mmu_crp_aptr);
8
}
0x02 => {
self.write_resolved_ea(bus, ea, Size::Long, self.mmu_tt0);
4
}
0x03 => {
self.write_resolved_ea(bus, ea, Size::Long, self.mmu_tt1);
4
}
0x18 => {
self.write_resolved_ea(bus, ea, Size::Word, self.mmu_sr & 0xFFFF);
4
}
_ => 0,
}
} else {
match preg {
0x10 => {
let v = self.read_resolved_ea(bus, ea, Size::Long);
self.mmu_tc = v;
self.pmmu_enabled = self.tc_enable();
4
}
0x12 => {
let Some(a) = ea_addr_only(ea) else { return 0 };
let limit = self.read_32(bus, a);
let aptr = self.read_32(bus, a.wrapping_add(4));
self.mmu_srp_limit = limit;
self.mmu_srp_aptr = aptr;
8
}
0x13 => {
let Some(a) = ea_addr_only(ea) else { return 0 };
let limit = self.read_32(bus, a);
let aptr = self.read_32(bus, a.wrapping_add(4));
self.mmu_crp_limit = limit;
self.mmu_crp_aptr = aptr;
8
}
0x02 => {
let v = self.read_resolved_ea(bus, ea, Size::Long);
self.mmu_tt0 = v;
4
}
0x03 => {
let v = self.read_resolved_ea(bus, ea, Size::Long);
self.mmu_tt1 = v;
4
}
0x18 => {
let v = self.read_resolved_ea(bus, ea, Size::Word);
self.mmu_sr = v & 0xFFFF;
4
}
_ => 0,
}
}
}
pub fn get_sr(&self) -> u16 {
let mut sr = 0u16;
sr |= (self.t1_flag & 0x8000) as u16;
sr |= (self.t0_flag & 0x4000) as u16;
sr |= ((self.s_flag & SFLAG_SET) << 11) as u16;
sr |= ((self.m_flag & MFLAG_SET) << 11) as u16;
sr |= (self.int_mask & 0x0700) as u16;
sr |= ((self.x_flag & XFLAG_SET) >> 4) as u16;
sr |= ((self.n_flag & NFLAG_SET) >> 4) as u16;
sr |= if self.not_z_flag == 0 { 0x04 } else { 0x00 };
sr |= ((self.v_flag & VFLAG_SET) >> 6) as u16;
sr |= ((self.c_flag & CFLAG_SET) >> 8) as u16;
sr
}
pub fn set_sr(&mut self, sr: u16) {
let sr = sr & self.sr_mask as u16;
self.t1_flag = (sr as u32) & 0x8000;
self.t0_flag = (sr as u32) & 0x4000;
self.int_mask = (sr as u32) & 0x0700;
self.set_ccr_internal(sr as u8);
let mut sm = ((sr >> 11) & 6) as u32;
if (sm & SFLAG_SET) == 0 {
sm &= !MFLAG_SET;
}
self.set_sm_flag(sm);
}
pub fn set_sr_noint_nosp(&mut self, sr: u16) {
let sr = sr & self.sr_mask as u16;
self.t1_flag = (sr as u32) & 0x8000;
self.t0_flag = (sr as u32) & 0x4000;
self.int_mask = (sr as u32) & 0x0700;
self.set_ccr_internal(sr as u8);
let mut sm = ((sr >> 11) & 6) as u32;
if (sm & SFLAG_SET) == 0 {
sm &= !MFLAG_SET;
}
self.set_sm_flag_nosp(sm);
}
fn set_ccr_internal(&mut self, ccr: u8) {
self.x_flag = if ccr & 0x10 != 0 { XFLAG_SET } else { 0 };
self.n_flag = if ccr & 0x08 != 0 { NFLAG_SET } else { 0 };
self.not_z_flag = if ccr & 0x04 != 0 { 0 } else { 1 };
self.v_flag = if ccr & 0x02 != 0 { VFLAG_SET } else { 0 };
self.c_flag = if ccr & 0x01 != 0 { CFLAG_SET } else { 0 };
}
pub fn get_ccr(&self) -> u8 {
(self.get_sr() & 0xFF) as u8
}
pub fn set_ccr(&mut self, ccr: u8) {
self.set_ccr_internal(ccr);
}
#[inline]
pub fn flag_x(&self) -> bool {
self.x_flag != 0
}
#[inline]
pub fn flag_n(&self) -> bool {
self.n_flag != 0
}
#[inline]
pub fn flag_z(&self) -> bool {
self.not_z_flag == 0
}
#[inline]
pub fn flag_v(&self) -> bool {
self.v_flag != 0
}
#[inline]
pub fn flag_c(&self) -> bool {
self.c_flag != 0
}
#[inline]
pub fn is_supervisor(&self) -> bool {
self.s_flag != 0
}
pub fn test_condition(&self, cond: u8) -> bool {
match cond & 0x0F {
0x0 => true, 0x1 => false, 0x2 => !self.flag_c() && !self.flag_z(), 0x3 => self.flag_c() || self.flag_z(), 0x4 => !self.flag_c(), 0x5 => self.flag_c(), 0x6 => !self.flag_z(), 0x7 => self.flag_z(), 0x8 => !self.flag_v(), 0x9 => self.flag_v(), 0xA => !self.flag_n(), 0xB => self.flag_n(), 0xC => self.flag_n() == self.flag_v(), 0xD => self.flag_n() != self.flag_v(), 0xE => !self.flag_z() && (self.flag_n() == self.flag_v()), 0xF => self.flag_z() || (self.flag_n() != self.flag_v()), _ => true,
}
}
}