use crate::{M68000, MemoryAccess};
use crate::execution_times as EXEC;
use EXEC::vector_execution_time;
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)]
#[non_exhaustive]
#[repr(C)]
pub enum Vector {
ResetSspPc = 0,
AccessError = 2,
AddressError,
IllegalInstruction,
ZeroDivide,
ChkInstruction,
TrapVInstruction,
PrivilegeViolation,
Trace,
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, }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Exception {
pub vector: u8,
priority: u8,
}
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 {
if self.priority < other.priority {
Ordering::Greater
} else if self.priority > other.priority {
Ordering::Less
} else {
Ordering::Equal
}
}
}
impl M68000 {
pub fn exception(&mut self, ex: Exception) {
if ex.vector == Vector::ResetSspPc as u8 ||
ex.vector == Vector::Trace as u8 ||
ex.vector >= Vector::Level1Interrupt as u8 && ex.vector <= Vector::Level7Interrupt as u8 ||
ex.vector >= Vector::Level1OnChipInterrupt as u8 && ex.vector <= Vector::Level7OnChipInterrupt as u8 {
self.stop = false;
}
self.exceptions.insert(ex);
}
fn reset(&mut self, memory: &mut impl MemoryAccess) -> usize {
self.regs.ssp = memory.get_long(0).expect("An exception occured when reading initial SSP.");
self.regs.pc = 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;
return EXEC::VECTOR_RESET;
}
pub(super) fn process_pending_exceptions(&mut self, memory: &mut impl MemoryAccess) -> usize {
let exceptions: BTreeSet<_> = self.exceptions.drain_filter(|ex| {
if ex.vector >= Vector::Level1Interrupt as u8 && ex.vector <= Vector::Level7Interrupt as u8 ||
ex.vector >= Vector::Level1OnChipInterrupt as u8 && ex.vector <= Vector::Level7OnChipInterrupt as u8 {
let level = ex.vector & 0x7;
if level <= self.regs.sr.interrupt_mask {
return false;
}
}
return true;
}).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.vector >= Vector::Level1Interrupt as u8 && exception.vector <= Vector::Level7Interrupt as u8 ||
exception.vector >= Vector::Level1OnChipInterrupt as u8 && exception.vector <= Vector::Level7OnChipInterrupt as u8 {
self.exception(Exception::from(Vector::SpuriousInterrupt));
} else {
self.exception(Exception::from(e));
}
} else {
self.exception(Exception::from(e));
}
0
},
};
}
total
}
fn process_exception(&mut self, memory: &mut impl MemoryAccess, vector: u8) -> InterpreterResult {
let sr = self.regs.sr.into();
self.regs.sr.t = false;
self.regs.sr.s = true;
#[cfg(feature = "cpu-mc68000")] {
self.push_long(memory, self.regs.pc)?;
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)?; }
}
#[cfg(feature = "cpu-scc68070")] {
if vector == 2 || vector == 3 { self.push_word(memory, 0)?;
self.push_word(memory, 0)?;
self.push_word(memory, 0)?;
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)?;
self.push_word(memory, sr)?;
}
self.regs.pc = memory.get_long(vector as u32 * 4).ok_or(ACCESS_ERROR)?;
Ok(vector_execution_time(vector))
}
}