use crate::{CpuDetails, M68000, MemoryAccess, StackFormat};
use crate::interpreter::InterpreterResult;
use std::cmp::Ordering;
use std::collections::BTreeSet;
pub const ACCESS_ERROR: u8 = Vector::AccessError as u8;
pub const ADDRESS_ERROR: u8 = Vector::AddressError as u8;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(not(feature = "ffi"), non_exhaustive)]
#[cfg_attr(feature = "ffi", repr(C))]
pub enum Vector {
ResetSspPc = 0,
AccessError = 2,
AddressError,
IllegalInstruction,
ZeroDivide,
ChkInstruction,
TrapVInstruction,
PrivilegeViolation,
Trace,
LineAEmulator,
LineFEmulator,
FormatError = 14,
UninitializedInterrupt,
SpuriousInterrupt = 24,
Level1Interrupt,
Level2Interrupt,
Level3Interrupt,
Level4Interrupt,
Level5Interrupt,
Level6Interrupt,
Level7Interrupt,
Trap0Instruction,
Trap1Instruction,
Trap2Instruction,
Trap3Instruction,
Trap4Instruction,
Trap5Instruction,
Trap6Instruction,
Trap7Instruction,
Trap8Instruction,
Trap9Instruction,
Trap10Instruction,
Trap11Instruction,
Trap12Instruction,
Trap13Instruction,
Trap14Instruction,
Trap15Instruction,
Level1OnChipInterrupt = 57,
Level2OnChipInterrupt,
Level3OnChipInterrupt,
Level4OnChipInterrupt,
Level5OnChipInterrupt,
Level6OnChipInterrupt,
Level7OnChipInterrupt,
UserInterrupt,
}
const fn get_vector_priority(vector: u8) -> u8 {
match vector {
3 => 0, 2 => 1, 9 => 2, 24..=31 => 3, 64..=255 => 3, 4 => 4, 8 => 5, _ => u8::MAX, }
}
const fn is_interrupt(vector: u8) -> bool {
vector >= Vector::Level1Interrupt as u8 && vector <= Vector::Level7Interrupt as u8 ||
vector >= Vector::Level1OnChipInterrupt as u8 && vector <= Vector::Level7OnChipInterrupt as u8
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Exception {
pub vector: u8,
priority: u8,
}
impl Exception {
#[inline]
pub const fn is_interrupt(&self) -> bool {
is_interrupt(self.vector)
}
}
impl From<u8> for Exception {
fn from(vector: u8) -> Self {
let priority = get_vector_priority(vector);
Self { vector, priority }
}
}
impl From<Vector> for Exception {
fn from(vector: Vector) -> Self {
Self::from(vector as u8)
}
}
impl PartialOrd for Exception {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Exception {
fn cmp(&self, other: &Self) -> Ordering {
match self.priority.cmp(&other.priority) {
Ordering::Greater => Ordering::Less,
Ordering::Less => Ordering::Greater,
Ordering::Equal => Ordering::Equal,
}
}
}
impl<CPU: CpuDetails> M68000<CPU> {
pub fn exception(&mut self, ex: Exception) {
if ex.vector == Vector::ResetSspPc as u8 ||
ex.vector == Vector::Trace as u8 ||
ex.is_interrupt() {
self.stop = false;
}
self.exceptions.insert(ex);
}
fn reset<M: MemoryAccess + ?Sized>(&mut self, memory: &mut M) -> usize {
self.regs.ssp.0 = memory.get_long(0).expect("An exception occured when reading initial SSP.");
self.regs.pc.0 = memory.get_long(4).expect("An exception occured when reading initial PC.");
self.regs.sr.t = false;
self.regs.sr.s = true;
self.regs.sr.interrupt_mask = 7;
self.stop = false;
CPU::VECTOR_RESET
}
pub(super) fn process_pending_exceptions<M: MemoryAccess + ?Sized>(&mut self, memory: &mut M) -> usize {
let mut to_process = Vec::new();
for ex in &self.exceptions {
if ex.is_interrupt() {
let level = ex.vector & 0x7;
if level != 7 && level <= self.regs.sr.interrupt_mask {
continue;
}
}
to_process.push(ex.clone()); }
for ex in &to_process {
self.exceptions.remove(ex);
}
let exceptions: BTreeSet<_> = to_process.into_iter().collect();
let mut total = 0;
for exception in exceptions.iter() {
if exception.vector == Vector::ResetSspPc as u8 {
self.exceptions.clear(); return self.reset(memory);
}
total += match self.process_exception(memory, exception.vector) {
Ok(cycles) => cycles,
Err(e) => {
if e == ACCESS_ERROR {
if exception.vector == ACCESS_ERROR {
panic!("An access error occured during access error processing (at {:#X})", self.regs.pc);
}
if exception.is_interrupt() {
self.exception(Exception::from(Vector::SpuriousInterrupt));
} else {
self.exception(Exception::from(e));
}
} else {
self.exception(Exception::from(e));
}
0
},
};
}
total
}
fn process_exception<M: MemoryAccess + ?Sized>(&mut self, memory: &mut M, vector: u8) -> InterpreterResult {
let sr = self.regs.sr.into();
self.regs.sr.t = false;
self.regs.sr.s = true;
if is_interrupt(vector) {
self.regs.sr.interrupt_mask = vector & 7; }
match CPU::STACK_FORMAT {
StackFormat::MC68000 => {
self.push_long(memory, self.regs.pc.0)?;
self.push_word(memory, sr)?;
if vector == 2 || vector == 3 { self.push_word(memory, self.current_opcode)?;
self.push_long(memory, 0)?; self.push_word(memory, 0)?; }
},
StackFormat::SCC68070 => {
if vector == 2 || vector == 3 { self.push_word(memory, 0)?; self.push_word(memory, self.current_opcode)?; self.push_word(memory, self.current_opcode)?; self.push_long(memory, 0)?; self.push_long(memory, 0)?; self.push_long(memory, 0)?; self.push_word(memory, 0)?; self.push_word(memory, 0)?; self.push_word(memory, 0)?; self.push_word(memory, 0)?; self.push_word(memory, 0xF000 | (vector as u16 * 4))?;
} else { self.push_word(memory, vector as u16 * 4)?;
}
self.push_long(memory, self.regs.pc.0)?;
self.push_word(memory, sr)?;
},
}
self.regs.pc.0 = memory.get_long(vector as u32 * 4).ok_or(ACCESS_ERROR)?;
Ok(CPU::vector_execution_time(vector))
}
}