diff --git a/CHANGELOG.md b/CHANGELOG.md index caf0e4fe..b24ea41c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ -## [0.1.4](https://github.com/dbalsom/martypc/releases/tag/0.1.4) (2023-07-XX) +## [0.1.4](https://github.com/dbalsom/martypc/releases/tag/0.1.4) (2023-08-XX) +* Added MMIO peek functions. Allows Memory debug viewer to peek into MMIO regions, if device supports. +* Brand new, simplified BIU state logic +* Fixed & Improved DMA refresh scheduling. (Fixes 8088MPH CPU test) * Fixed issue where Call Stack could grow uncontrollably with recursive code or interrupts * Fixed CS:IP reporting in Instruction trace mode logs * Fixed memory leak in Instruction trace mode (thanks Folkert) diff --git a/core/src/cpu_808x/biu.rs b/core/src/cpu_808x/biu.rs index ab23cd6f..c770241d 100644 --- a/core/src/cpu_808x/biu.rs +++ b/core/src/cpu_808x/biu.rs @@ -338,6 +338,7 @@ impl Cpu { // - We are scheduling a prefetch during a CODE fetch // - The queue length was 3 at the beginning of T3 + /* // If we are in some kind of bus transfer (not passive) then add any wait states that // might apply. let schedule_adjust = if self.bus_status != BusStatus::Passive { @@ -346,17 +347,34 @@ impl Cpu { else { 0 }; + */ - if self.bus_status == BusStatus::CodeFetch - && (self.queue.len() == 3 || (self.queue.len() == 2 && self.queue_op != QueueOp::Idle)) - { - self.fetch_state = FetchState::Scheduled(ct + schedule_adjust); - self.next_fetch_state = FetchState::Delayed(3); + if ct == 0 { + // Schedule count of 0 indicates fetch after bus transfer is complete, ie, ScheduleNext + if self.bus_status == BusStatus::CodeFetch + && (self.queue.len() == 3 || (self.queue.len() == 2 && self.queue_op != QueueOp::Idle)) + { + self.fetch_state = FetchState::ScheduleNext; + self.next_fetch_state = FetchState::Delayed(3); + } + else { + self.fetch_state = FetchState::ScheduleNext; + self.next_fetch_state = FetchState::InProgress; + }; } else { - self.fetch_state = FetchState::Scheduled(ct + schedule_adjust); - self.next_fetch_state = FetchState::InProgress; - }; + + if self.bus_status == BusStatus::CodeFetch + && (self.queue.len() == 3 || (self.queue.len() == 2 && self.queue_op != QueueOp::Idle)) + { + self.fetch_state = FetchState::Scheduled(ct); + self.next_fetch_state = FetchState::Delayed(3); + } + else { + self.fetch_state = FetchState::Scheduled(ct); + self.next_fetch_state = FetchState::InProgress; + }; + } // new bus logic: transition to PF state self.biu_change_state(BiuStateNew::Prefetch); @@ -366,7 +384,7 @@ impl Cpu { /// T3 or later. This incurs two penalty cycles. pub fn biu_abort_fetch(&mut self) { - self.fetch_state = FetchState::Aborted(2); + self.fetch_state = FetchState::Aborting(2); self.t_cycle = TCycle::T1; self.bus_status = BusStatus::Passive; self.i8288.ale = false; @@ -463,7 +481,7 @@ impl Cpu { // EU has not claimed the bus, attempt to prefetch... if self.biu_queue_has_room() { if !self.fetch_suspended { - self.biu_schedule_fetch(2); + self.biu_schedule_fetch(0); } } else { @@ -533,7 +551,7 @@ impl Cpu { FetchState::Scheduled(c) => { *c = c.saturating_sub(1); } - FetchState::Aborted(c) => { + FetchState::Aborting(c) => { *c = c.saturating_sub(1); if *c == 0 { diff --git a/core/src/cpu_808x/cycle.rs b/core/src/cpu_808x/cycle.rs index 1b99af33..ee0f9df7 100644 --- a/core/src/cpu_808x/cycle.rs +++ b/core/src/cpu_808x/cycle.rs @@ -228,11 +228,11 @@ impl Cpu { // Do DRAM refresh (DMA channel 0) simulation if self.enable_wait_states && self.dram_refresh_simulation { - self.dram_refresh_cycle_num -= 1; + self.dram_refresh_cycle_num = self.dram_refresh_cycle_num.saturating_sub(1); match &mut self.dma_state { DmaState::Idle => { - if self.dram_refresh_cycle_num == 0 { + if self.dram_refresh_cycle_num == 0 && self.dram_refresh_cycle_period > 0 { // DRAM refresh cycle counter has hit terminal count. // Begin DMA transfer simulation by issuing a DREQ. self.dma_state = DmaState::Dreq; @@ -282,8 +282,7 @@ impl Cpu { *cycles = cycles.saturating_sub(1); if *cycles == 3 { // DMAWAIT asserted on S2 - self.wait_states += 7; // Effectively 6 as this is decremented this cycle - //self.wait_states += 6_u32.saturating_sub(self.wait_states); + self.dma_wait_states = 7; // Effectively 6 as this is decremented this cycle self.ready = false; } if *cycles == 0 { @@ -338,7 +337,7 @@ impl Cpu { TCycle::T2 => TCycle::T3, TCycle::T3 => { // If no wait states have been reported, advance to T3, otherwise go to Tw - if self.wait_states == 0 { + if self.wait_states == 0 && self.dma_wait_states == 0 { self.biu_bus_end(); TCycle::T4 } @@ -349,7 +348,7 @@ impl Cpu { TCycle::Tw => { // If we are handling wait states, continue in Tw (decrement at end of cycle) // If we have handled all wait states, transition to T4 - if self.wait_states > 0 { + if self.wait_states > 0 || self.dma_wait_states > 0 { //log::debug!("wait states: {}", self.wait_states); TCycle::Tw } @@ -369,17 +368,7 @@ impl Cpu { self.biu_tick_prefetcher(); match self.fetch_state { - FetchState::Scheduled(1) => { - //trace_print!(self, "fetch decision: {}", self.queue.len()); - if (self.queue.len() == 3 && self.queue_op == QueueOp::Idle) - || (self.queue.len() == 2 && self.queue_op != QueueOp::Idle) - { - if self.bus_status == BusStatus::CodeFetch { - //trace_print!(self, "fetch delay here?"); - } - } - } - FetchState::Scheduled(0) => { + FetchState::ScheduleNext | FetchState::Scheduled(0) => { // A fetch is scheduled for this cycle; however we may have additional delays to process. // If bus_pending_eu is true, then we arrived here during biu_bus_begin. @@ -387,33 +376,17 @@ impl Cpu { // In that case, we should do nothing instead of transitioning to a new fetch state. if !self.bus_pending_eu { - // Check if we are entering Delayed fetch states, but the queue is full. - if !self.biu_queue_has_room() && matches!(self.next_fetch_state, FetchState::Delayed(_)) { - - if self.bus_pending_eu { - // Don't stall the BIU if there is pending bus request from the EU - we are likely - // in bus_begin() and will process an abort instead. - self.trace_comment("SHOULD ABORT!"); + if let BusStatus::Passive = self.bus_status { + + // Begin a fetch if we are not transitioning into any delay state, otherwise transition + // into said state. + if self.next_fetch_state == FetchState::InProgress { + self.begin_fetch(); } else { - //self.biu_abort_fetch_full(); + self.fetch_state = self.next_fetch_state; } } - else if self.last_queue_delay == QueueDelay::Read && !self.fetch_suspended { - // Sc2 delay - - //self.next_fetch_state = FetchState::Delayed(2); - //self.trace_comment("DELAY2"); - } - - // Begin a fetch if we are not transitioning into any delay state, otherwise transition - // into said state. - if self.next_fetch_state == FetchState::InProgress { - self.begin_fetch(); - } - else { - self.fetch_state = self.next_fetch_state; - } } } FetchState::DelayDone => { @@ -451,6 +424,7 @@ impl Cpu { self.cycle_num += 1; self.wait_states = self.wait_states.saturating_sub(1); + self.dma_wait_states = self.dma_wait_states.saturating_sub(1); /* // Try to catch a runaway instruction? diff --git a/core/src/cpu_808x/mod.rs b/core/src/cpu_808x/mod.rs index 6d714850..c0182297 100644 --- a/core/src/cpu_808x/mod.rs +++ b/core/src/cpu_808x/mod.rs @@ -758,6 +758,7 @@ pub struct Cpu dram_refresh_cycle_num: u32, dram_refresh_adjust: u32, dma_aen: bool, + dma_wait_states: u32, // Trap stuff trap_enable_delay: u32, // Number of cycles to delay trap flag enablement. @@ -964,9 +965,10 @@ pub enum FetchState { Suspended, InProgress, Scheduled(u8), + ScheduleNext, Delayed(u8), DelayDone, - Aborted(u8), + Aborting(u8), BlockedByEU } @@ -1168,7 +1170,7 @@ impl Cpu { pub fn is_last_wait(&self) -> bool { match self.t_cycle { TCycle::T3 | TCycle::Tw => { - if self.wait_states == 0 { + if self.wait_states == 0 && self.dma_wait_states == 0 { true } else { @@ -1184,7 +1186,7 @@ impl Cpu { match self.t_cycle { TCycle::T1 | TCycle::T2 => true, TCycle::T3 | TCycle::Tw => { - if self.wait_states != 0 { + if self.wait_states > 0 || self.dma_wait_states > 0 { true } else {