diff --git a/src/cpus/m68k/decode.rs b/src/cpus/m68k/decode.rs index ce17bd6..e3f0c36 100644 --- a/src/cpus/m68k/decode.rs +++ b/src/cpus/m68k/decode.rs @@ -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 { diff --git a/src/cpus/m68k/execute.rs b/src/cpus/m68k/execute.rs index 4aaf760..e572a25 100644 --- a/src/cpus/m68k/execute.rs +++ b/src/cpus/m68k/execute.rs @@ -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) => { diff --git a/src/cpus/m68k/state.rs b/src/cpus/m68k/state.rs index e0b3928..17ae157 100644 --- a/src/cpus/m68k/state.rs +++ b/src/cpus/m68k/state.rs @@ -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, diff --git a/src/devices.rs b/src/devices.rs index c8b8c32..0d62596 100644 --- a/src/devices.rs +++ b/src/devices.rs @@ -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>; } diff --git a/src/error.rs b/src/error.rs index f751e33..9f5b121 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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 } diff --git a/src/interrupts.rs b/src/interrupts.rs index 9c4c346..c0fd618 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -33,17 +33,18 @@ impl Signal { } - pub struct InterruptController { pub target: Option, - pub priority: Vec, + 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![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 { + 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) + } } diff --git a/todo.txt b/todo.txt index c61a1ad..4475c3b 100644 --- a/todo.txt +++ b/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 - -