mirror of
https://github.com/transistorfet/moa.git
synced 2024-06-10 07:29:31 +00:00
Fixed interrupts
Previously the m68k wasn't masking interrupts with an equal priorty. I also modified how they work, such that the cpus will check the controller rather than wait for the notification call
This commit is contained in:
parent
c1ca666aa4
commit
93c080eae6
|
@ -67,7 +67,8 @@ impl M68kDecoder {
|
|||
|
||||
pub fn decode_at(&mut self, memory: &mut dyn Addressable, start: u32) -> Result<(), Error> {
|
||||
self.init(start);
|
||||
self.instruction = self.decode_one(memory)
|
||||
self.instruction = self.decode_one(memory)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn decode_one(&mut self, memory: &mut dyn Addressable) -> Result<Instruction, Error> {
|
||||
|
|
|
@ -41,23 +41,7 @@ impl Steppable for M68k {
|
|||
}
|
||||
}
|
||||
|
||||
impl Interruptable for M68k {
|
||||
fn interrupt_state_change(&mut self, state: bool, priority: u8, number: u8) -> Result<(), Error> {
|
||||
let ipl = if state {
|
||||
InterruptPriority::from_u8(priority)
|
||||
} else {
|
||||
InterruptPriority::NoInterrupt
|
||||
};
|
||||
|
||||
if ipl != self.state.pending_ipl {
|
||||
self.state.pending_ipl = ipl;
|
||||
if ipl != InterruptPriority::NoInterrupt {
|
||||
self.state.ipl_ack_num = number;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Interruptable for M68k { }
|
||||
|
||||
impl Transmutable for M68k {
|
||||
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
|
||||
|
@ -90,7 +74,7 @@ impl M68k {
|
|||
//Err(Error { err: ErrorType::Processor, native, .. }) => {
|
||||
// TODO match arm conditional is temporary: illegal instructions generate a top level error in order to debug and fix issues with decode
|
||||
Err(Error { err: ErrorType::Processor, native, .. }) if native != Exceptions::IllegalInstruction as u32 => {
|
||||
self.exception(system, native as u8)?;
|
||||
self.exception(system, native as u8, false)?;
|
||||
Ok(())
|
||||
},
|
||||
Err(err) => Err(err),
|
||||
|
@ -121,17 +105,22 @@ impl M68k {
|
|||
}
|
||||
|
||||
pub fn check_pending_interrupts(&mut self, system: &System) -> Result<(), Error> {
|
||||
self.state.pending_ipl = match system.get_interrupt_controller().check() {
|
||||
(true, priority) => InterruptPriority::from_u8(priority),
|
||||
(false, _) => InterruptPriority::NoInterrupt,
|
||||
};
|
||||
|
||||
let current_ipl = self.state.current_ipl as u8;
|
||||
let pending_ipl = self.state.pending_ipl as u8;
|
||||
|
||||
if self.state.pending_ipl != InterruptPriority::NoInterrupt {
|
||||
let priority_mask = ((self.state.sr & Flags::IntMask as u16) >> 8) as u8;
|
||||
|
||||
if (pending_ipl >= priority_mask || pending_ipl == 7) && pending_ipl >= current_ipl {
|
||||
if (pending_ipl > priority_mask || pending_ipl == 7) && pending_ipl >= current_ipl {
|
||||
debug!("{} interrupt: {} {}", DEV_NAME, pending_ipl, priority_mask);
|
||||
self.state.current_ipl = self.state.pending_ipl;
|
||||
self.exception(system, self.state.ipl_ack_num)?;
|
||||
self.state.sr = (self.state.sr & !(Flags::IntMask as u16)) | ((self.state.current_ipl as u16) << 8);
|
||||
let ack_num = system.get_interrupt_controller().acknowledge(self.state.current_ipl as u8)?;
|
||||
self.exception(system, ack_num, true)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +132,7 @@ impl M68k {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn exception(&mut self, system: &System, number: u8) -> Result<(), Error> {
|
||||
pub fn exception(&mut self, system: &System, number: u8, is_interrupt: bool) -> Result<(), Error> {
|
||||
debug!("{}: raising exception {}", DEV_NAME, number);
|
||||
let offset = (number as u16) << 2;
|
||||
if self.cputype >= M68kType::MC68010 {
|
||||
|
@ -151,8 +140,13 @@ impl M68k {
|
|||
}
|
||||
self.push_long(system, self.state.pc)?;
|
||||
self.push_word(system, self.state.sr)?;
|
||||
|
||||
// Changes to the flags must happen after the previous value has been pushed to the stack
|
||||
self.set_flag(Flags::Supervisor, true);
|
||||
self.set_flag(Flags::Tracing, false);
|
||||
if is_interrupt {
|
||||
self.state.sr = (self.state.sr & !(Flags::IntMask as u16)) | ((self.state.current_ipl as u16) << 8);
|
||||
}
|
||||
|
||||
self.state.pc = self.port.read_beu32((self.state.vbr + offset as u32) as Address)?;
|
||||
Ok(())
|
||||
|
@ -351,7 +345,7 @@ impl M68k {
|
|||
Instruction::DIVW(src, dest, sign) => {
|
||||
let value = self.get_target_value(system, src, Size::Word)?;
|
||||
if value == 0 {
|
||||
self.exception(system, Exceptions::ZeroDivide as u8)?;
|
||||
self.exception(system, Exceptions::ZeroDivide as u8, false)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -370,7 +364,7 @@ impl M68k {
|
|||
Instruction::DIVL(src, dest_h, dest_l, sign) => {
|
||||
let value = self.get_target_value(system, src, Size::Long)?;
|
||||
if value == 0 {
|
||||
self.exception(system, Exceptions::ZeroDivide as u8)?;
|
||||
self.exception(system, Exceptions::ZeroDivide as u8, false)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -651,11 +645,11 @@ impl M68k {
|
|||
self.set_logic_flags(value, size);
|
||||
},
|
||||
Instruction::TRAP(number) => {
|
||||
self.exception(system, 32 + number)?;
|
||||
self.exception(system, 32 + number, false)?;
|
||||
},
|
||||
Instruction::TRAPV => {
|
||||
if self.get_flag(Flags::Overflow) {
|
||||
self.exception(system, Exceptions::TrapvInstruction as u8)?;
|
||||
self.exception(system, Exceptions::TrapvInstruction as u8, false)?;
|
||||
}
|
||||
},
|
||||
Instruction::UNLK(reg) => {
|
||||
|
|
|
@ -88,7 +88,6 @@ pub struct M68kState {
|
|||
pub status: Status,
|
||||
pub current_ipl: InterruptPriority,
|
||||
pub pending_ipl: InterruptPriority,
|
||||
pub ipl_ack_num: u8,
|
||||
|
||||
pub pc: u32,
|
||||
pub sr: u16,
|
||||
|
@ -106,7 +105,6 @@ impl M68kState {
|
|||
status: Status::Init,
|
||||
current_ipl: InterruptPriority::NoInterrupt,
|
||||
pending_ipl: InterruptPriority::NoInterrupt,
|
||||
ipl_ack_num: 0,
|
||||
|
||||
pc: 0,
|
||||
sr: FLAGS_ON_RESET,
|
||||
|
|
|
@ -32,7 +32,7 @@ pub trait Steppable {
|
|||
/// A device that can receive an interrupt. The `interrupt_state_change()` method
|
||||
/// will be called whenever an interrupt signal changes goes high or low.
|
||||
pub trait Interruptable {
|
||||
fn interrupt_state_change(&mut self, state: bool, priority: u8, number: u8) -> Result<(), Error>;
|
||||
//fn interrupt_state_change(&mut self, state: bool, priority: u8, number: u8) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ pub enum LogLevel {
|
|||
Debug,
|
||||
}
|
||||
|
||||
static mut LOG_LEVEL: LogLevel = LogLevel::Warning;
|
||||
static mut LOG_LEVEL: LogLevel = LogLevel::Debug;
|
||||
|
||||
pub fn log_level() -> LogLevel {
|
||||
unsafe { LOG_LEVEL }
|
||||
|
|
|
@ -33,17 +33,18 @@ impl Signal {
|
|||
}
|
||||
|
||||
|
||||
|
||||
pub struct InterruptController {
|
||||
pub target: Option<TransmutableBox>,
|
||||
pub priority: Vec<Signal>,
|
||||
pub interrupts: Vec<(bool, u8)>,
|
||||
pub highest: u8,
|
||||
}
|
||||
|
||||
impl InterruptController {
|
||||
pub fn new() -> InterruptController {
|
||||
InterruptController {
|
||||
target: None,
|
||||
priority: iter::repeat_with(|| Signal::new()).take(7).collect::<Vec<_>>(), //vec![Signal::new(); 7],
|
||||
interrupts: vec![(false, 0); 7],
|
||||
highest: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,28 +58,29 @@ impl InterruptController {
|
|||
}
|
||||
|
||||
pub fn set(&mut self, state: bool, priority: u8, number: u8) -> Result<(), Error> {
|
||||
let signal = &mut self.priority[priority as usize];
|
||||
signal.set(state);
|
||||
match signal.has_changed() {
|
||||
Some(value) => self.notify_interrupt_state(value, priority, number)?,
|
||||
None => { },
|
||||
self.interrupts[priority as usize].0 = state;
|
||||
self.interrupts[priority as usize].1 = number;
|
||||
if state && priority > self.highest {
|
||||
self.highest = priority;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn notify_interrupt_state(&mut self, state: bool, priority: u8, number: u8) -> Result<(), Error> {
|
||||
// TODO how does this find the specific device it's connected to?
|
||||
// TODO for the time being, this will find the first device to handle it or fail
|
||||
|
||||
debug!("interrupts: priority {} state changed to {}", priority, state);
|
||||
match &self.target {
|
||||
Some(dev) => {
|
||||
Ok(dev.borrow_mut().as_interruptable().unwrap().interrupt_state_change(state, priority, number)?)
|
||||
},
|
||||
None => {
|
||||
Err(Error::new(&format!("unhandled interrupt: {:x}", number)))
|
||||
},
|
||||
pub fn check(&mut self) -> (bool, u8) {
|
||||
if self.highest > 0 {
|
||||
(true, self.highest)
|
||||
} else {
|
||||
(false, 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn acknowledge(&mut self, priority: u8) -> Result<u8, Error> {
|
||||
let acknowledge = self.interrupts[priority as usize].1;
|
||||
self.interrupts[priority as usize].0 = false;
|
||||
while self.highest > 0 && !self.interrupts[self.highest as usize].0 {
|
||||
self.highest -= 1;
|
||||
}
|
||||
Ok(acknowledge)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
41
todo.txt
41
todo.txt
|
@ -1,37 +1,30 @@
|
|||
|
||||
* implement a Z80
|
||||
* master system emulation?
|
||||
* what about a Z280? Is it just an extension, like the 68k variants?
|
||||
* separate the debugger out of m68k
|
||||
|
||||
* i need a better way of handling disperate reads/writes to I/O spaces, rather than having multiple devices or having a massive chunk of address space allocated, continuously
|
||||
* should you modify Addressable to also take the absolute address as input? I'm thinking of how the same device could be mapped to multiple addresses in memory instead
|
||||
of taking up a whole range of addresses
|
||||
|
||||
|
||||
* implement a Z80
|
||||
* maybe see about a Mac 128k or something
|
||||
|
||||
* add instruction timing to M68k
|
||||
* YM7101 timing is causing it to be very slow... speeding this up increasing rendering speed a lot, even though the frame shouldn't be drawn that often
|
||||
* improve the speed of the event loop somehow (replace heap?)
|
||||
|
||||
* modify the interrupt handling to make the interrupted device acknowledge the interrupt, probably via the interrupt controller somehow. It might need to be async
|
||||
such that the cpu tells the int_controller it's acknowledged, and then the interrupting device can check the interrupt controller if it needs to see the ack.
|
||||
The ym7101 could use the ack as the "off" signal, and perhaps use that for the vsync int bit
|
||||
* there's potentially an issue with interrupts for m68k where it might not properly mask interrupts causing overlapping interrupts that cause problems (eventually executing RAM in Sonic)
|
||||
* YM7101 timing is causing it to be very slow... speeding this up increasing rendering speed a lot, even though the frame shouldn't be drawn that often... not sure what's wrong with the timing
|
||||
* make the ym7101 set/reset the v_int occurred flag based on the interrupt controller
|
||||
|
||||
|
||||
* each device that can make a bus request should have a BusPort which is used to access the bus. Not sure how it'll be created or passed to the device, since
|
||||
the offset should be set by the builder or system, and the mask and data size should be sent by the CPU (although I suppose some systems could hook it up differently)
|
||||
* what about even vs odd accesses? If you access a byte, should the bus port possible turn it into a word access, and return only the byte portion?
|
||||
this would be more accurate for the 68000 which doesn't have an A0 address pin
|
||||
* there is clearly an issue with the ROM writing 4 bytes to the data port when the autoincrement is only 2. This might be an issue with the fact that the CPU
|
||||
is making full long word requests even though the 68000 shouldn't be able to (a long word would be 2 word accesses)
|
||||
* each device that can make a bus request should have a BusPort which is used to access the bus
|
||||
|
||||
|
||||
|
||||
* separate the debugger out of m68k
|
||||
* make devices nameable, using a hashmap to store them
|
||||
* can you eventually make the system connections all configurable via a config file?
|
||||
* you could modify read/write to return the number of bytes read or written for dynamic bus sizing used by the MC68020+
|
||||
* you could modify read()/write() in Addressable to return the number of bytes read or written for dynamic bus sizing used by the MC68020+
|
||||
* should you simulate bus arbitration?
|
||||
|
||||
* maybe see about a Mac 128k or something
|
||||
|
||||
|
||||
* make tests for each instruction
|
||||
* check all instructions in the docs
|
||||
|
||||
* unimplemented: ABCD, ADDX, BFFFO, BFINS, BKPT, CHK, EXG, ILLEGAL, MOVEfromCCR, MOVEP, RTR, RTD, SBCD, SUBX
|
||||
* >=MC68020 undecoded & unimplemented: CALLM, CAS, CAS2, CHK2, CMP2, RTM, PACK, TRAPcc, UNPK
|
||||
|
@ -42,9 +35,5 @@
|
|||
|
||||
|
||||
* how can you have multple CPUs
|
||||
* should you simulate bus arbitration?
|
||||
* can you eventually make the system connections all configurable via a config file?
|
||||
|
||||
|
||||
* check all instructions in the docs
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user