diff --git a/src/memory/mos6510.rs b/src/memory/mos6510.rs index ec2b17b..b27b5bc 100644 --- a/src/memory/mos6510.rs +++ b/src/memory/mos6510.rs @@ -27,7 +27,7 @@ impl Memory for Mos6510Port { fn read(&mut self, address: u16) -> u8 { match address % 2 { 0 => self.ddr, - 1 => (self.port.read() & !self.ddr) | (self.writes & self.ddr), + 1 => (self.port.read_data() & !self.ddr) | (self.writes & self.ddr), _ => unreachable!(), } } @@ -36,11 +36,11 @@ impl Memory for Mos6510Port { match address % 2 { 0 => { self.ddr = value; - self.port.write(self.writes & self.ddr); + self.port.write_data(self.writes & self.ddr); } 1 => { self.writes = value; - self.port.write(value & self.ddr); + self.port.write_data(value & self.ddr); } _ => unreachable!(), } @@ -50,10 +50,7 @@ impl Memory for Mos6510Port { self.port.reset(); } - fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> ActiveInterrupt { - match self.port.poll(cycles_since_poll, total_cycle_count) { - true => ActiveInterrupt::IRQ, - false => ActiveInterrupt::None, - } + fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> ActiveInterrupt { + ActiveInterrupt::None } } diff --git a/src/memory/mos652x/cia.rs b/src/memory/mos652x/cia.rs index 973c489..79c40eb 100644 --- a/src/memory/mos652x/cia.rs +++ b/src/memory/mos652x/cia.rs @@ -111,14 +111,10 @@ impl Memory for Cia { } 0x0C => self.shift_register.data, 0x0D => { - // TODO: alarm and shift register flags - let value = self - .interrupts - .read_flags((self.timer_a.interrupt as u8) | (self.timer_b.interrupt as u8) << 1); - - self.timer_a.interrupt = false; - self.timer_b.interrupt = false; + let value = self.interrupts.read_flags(); + // clear the interrupt flags + self.interrupts.clear_flag(value); value } 0x0E => { @@ -209,25 +205,30 @@ impl Memory for Cia { } fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> ActiveInterrupt { - if self.timer_a.poll(cycles_since_poll, total_cycle_count) - && (self.interrupts.interrupt_enable & interrupt_bits::TIMER_A) != 0 - { - return ActiveInterrupt::IRQ; - } + let mut active_interrupt = ActiveInterrupt::None; - if self.timer_b.poll(cycles_since_poll, total_cycle_count) - && (self.interrupts.interrupt_enable & interrupt_bits::TIMER_B) != 0 - { - return ActiveInterrupt::IRQ; + if self.timer_a.poll(cycles_since_poll, total_cycle_count) { + self.interrupts.set_flag(interrupt_bits::TIMER_A); + + if (self.interrupts.interrupt_enable & interrupt_bits::TIMER_A) != 0 { + active_interrupt = ActiveInterrupt::IRQ; + } } - if self.a.poll(cycles_since_poll, total_cycle_count) - || self.b.poll(cycles_since_poll, total_cycle_count) - { - return ActiveInterrupt::IRQ; + if self.timer_b.poll(cycles_since_poll, total_cycle_count) { + self.interrupts.set_flag(interrupt_bits::TIMER_B); + + if (self.interrupts.interrupt_enable & interrupt_bits::TIMER_B) != 0 { + active_interrupt = ActiveInterrupt::IRQ; + } } - ActiveInterrupt::None + let (_ca1, _ca2) = self.a.poll(cycles_since_poll, total_cycle_count); + let (_cb1, _cb2) = self.b.poll(cycles_since_poll, total_cycle_count); + + // TODO: stuff based on ca1, ca2, cb1, cb2 + + active_interrupt } } diff --git a/src/memory/mos652x/mod.rs b/src/memory/mos652x/mod.rs index 792a63e..e092e63 100644 --- a/src/memory/mos652x/mod.rs +++ b/src/memory/mos652x/mod.rs @@ -8,6 +8,13 @@ pub use via::Via; use crate::memory::Port; +#[derive(PartialEq)] +pub enum ActiveTransition { + Rising, + Falling, + None, +} + /// A port and its associated registers on the MOS 6522 VIA or MOS 6526 CIA. pub struct PortRegisters { /// The Port implementation that this instance delegates to. @@ -21,6 +28,9 @@ pub struct PortRegisters { /// Latch enable: Present on the MOS 6522 VIA. latch_enabled: bool, + + /// Previous state of the CX1 and CX2 lines (if any). Used to detect rising and falling edges. + cx_lines: Option<(bool, bool)>, } impl PortRegisters { @@ -30,23 +40,51 @@ impl PortRegisters { writes: 0, ddr: 0, latch_enabled: false, + cx_lines: None, } } /// Read from the port, respecting the DDR. pub fn read(&mut self) -> u8 { - (self.port.read() & !self.ddr) | (self.writes & self.ddr) + (self.port.read_data() & !self.ddr) | (self.writes & self.ddr) } /// Write to the port, respecting the DDR. pub fn write(&mut self, value: u8) { self.writes = value; - self.port.write(value & self.ddr); + self.port.write_data(value & self.ddr); } /// Poll the underlying port for interrupts. - pub fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> bool { - self.port.poll(cycles_since_poll, total_cycle_count) + /// Returns if an edge is detected on either CX1 or CX2. + pub fn poll( + &mut self, + cycles_since_poll: u64, + total_cycle_count: u64, + ) -> (ActiveTransition, ActiveTransition) { + let (cx1, cx2) = self.port.read_control(cycles_since_poll, total_cycle_count); + + if let Some((prev_cx1, prev_cx2)) = self.cx_lines { + let cx1_transition = match (prev_cx1, cx1) { + (false, true) => ActiveTransition::Rising, + (true, false) => ActiveTransition::Falling, + _ => ActiveTransition::None, + }; + + let cx2_transition = match (prev_cx2, cx2) { + (false, true) => ActiveTransition::Rising, + (true, false) => ActiveTransition::Falling, + _ => ActiveTransition::None, + }; + + self.cx_lines = Some((cx1, cx2)); + + (cx1_transition, cx2_transition) + } else { + self.cx_lines = Some((cx1, cx2)); + + (ActiveTransition::None, ActiveTransition::None) + } } /// Reset the port to its initial state. @@ -97,9 +135,6 @@ pub struct Timer { /// signed integer since polling the timer does not happen at every cycle. counter: i32, - /// Whether the timer's interrupt flag is set. - interrupt: bool, - /// If false, the timer will fire once; if true, it will load the latch into the counter and keep going continuous: bool, @@ -118,7 +153,6 @@ impl Timer { Self { latch: 0, counter: 0, - interrupt: false, continuous: false, running: true, output: TimerOutput::None, @@ -142,7 +176,6 @@ impl Timer { if self.counter <= 0 { // The counter underflowed - self.interrupt = true; true } else { false @@ -197,7 +230,6 @@ impl Timer { fn reset(&mut self) { self.latch = 0; self.counter = 0; - self.interrupt = false; self.continuous = false; self.running = true; self.output = TimerOutput::None; @@ -238,19 +270,23 @@ impl ShiftRegister { /// Registers for interrupt flags and interrupt enable bits. /// Each bit from 0 to 6 corresponds to an interrupt source. pub struct InterruptRegister { - /// The current state of which interrupts are enabled. + /// The current state of which interrupts are enabled (to trigger IRQs). /// If a bit is set, the corresponding interrupt is enabled. pub interrupt_enable: u8, + + /// The current state of which interrupts are flagged. + /// Flagged interrupts are cleared on read. + pub interrupt_flags: u8, } impl InterruptRegister { /// Read the apparent value of the interrupt register, based on the provided interrupt enable bits. - pub fn read_flags(&self, mut value: u8) -> u8 { - if (value & self.interrupt_enable) != 0 { - value |= 0x80; + pub fn read_flags(&self) -> u8 { + if (self.interrupt_flags & self.interrupt_enable) != 0 { + self.interrupt_flags | 0x80 + } else { + self.interrupt_flags } - - value } /// Read the value of the interrupt enable register. @@ -273,16 +309,28 @@ impl InterruptRegister { pub fn is_enabled(&self, interrupt: u8) -> bool { (self.interrupt_enable & interrupt) != 0 } + + /// Set the specified interrupt flag (after an interrupt occurred). + pub fn set_flag(&mut self, interrupt: u8) { + self.interrupt_flags |= interrupt; + } + + /// Clear the specified interrupt flag (after it has been serviced). + pub fn clear_flag(&mut self, interrupt: u8) { + self.interrupt_flags &= !interrupt; + } } impl InterruptRegister { fn new() -> Self { Self { interrupt_enable: 0, + interrupt_flags: 0, } } fn reset(&mut self) { self.interrupt_enable = 0; + self.interrupt_flags = 0; } } diff --git a/src/memory/mos652x/pia.rs b/src/memory/mos652x/pia.rs index a112b5f..a187b92 100644 --- a/src/memory/mos652x/pia.rs +++ b/src/memory/mos652x/pia.rs @@ -13,8 +13,11 @@ struct PiaPortRegisters { /// Data direction register. Each bit controls whether the line is an input (0) or output (1) ddr: u8, - // Control register. Each bit has a specific function. + /// Control register. Each bit has a specific function. pub control: u8, + + /// Previous state of the CX1 and CX2 lines (if any). Used to detect rising and falling edges. + cx_lines: Option<(bool, bool)>, } impl PiaPortRegisters { @@ -25,6 +28,7 @@ impl PiaPortRegisters { writes: 0, ddr: 0, control: 0, + cx_lines: None, } } @@ -35,7 +39,7 @@ impl PiaPortRegisters { /// the written value. pub fn read(&mut self) -> u8 { if self.control & pia_control_bits::DDR_SELECT != 0 { - (self.port.read() & !self.ddr) | (self.writes & self.ddr) + (self.port.read_data() & !self.ddr) | (self.writes & self.ddr) } else { self.ddr } @@ -48,7 +52,7 @@ impl PiaPortRegisters { pub fn write(&mut self, value: u8) { if self.control & pia_control_bits::DDR_SELECT != 0 { self.writes = value; - self.port.write(value & self.ddr); + self.port.write_data(value & self.ddr); } else { self.ddr = value; } @@ -56,7 +60,43 @@ impl PiaPortRegisters { /// Poll the underlying port for interrupts. pub fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> bool { - self.port.poll(cycles_since_poll, total_cycle_count) + let (cx1, cx2) = self.port.read_control(cycles_since_poll, total_cycle_count); + + let mut interrupt = false; + + if let Some((prev_cx1, prev_cx2)) = self.cx_lines { + // CX1 + if cx1 != prev_cx1 { + let direction = self.control & pia_control_bits::C1_ACTIVE_TRANSITION; + if (direction == 0 && cx1 == false) || (direction != 0 && cx1 == true) { + // matching edge detected + self.control |= pia_control_bits::C1_ACTIVE_TRANSITION_FLAG; + + if self.control & pia_control_bits::C1_INTERRUPT_ENABLE != 0 { + interrupt = true; + } + } + } + + // CX2 + if (pia_control_bits::C2_DIRECTION & self.control) != 0 { + // input + if cx2 != prev_cx2 { + let direction = self.control & pia_control_bits::C2_INPUT_ACTIVE_TRANSITION; + if (direction == 0 && cx2 == false) || (direction != 0 && cx2 == true) { + // matching edge detected + self.control |= pia_control_bits::C2_ACTIVE_TRANSITION_FLAG; + + if self.control & pia_control_bits::C2_INPUT_INTERRUPT_ENABLE != 0 { + interrupt = true; + } + } + } + } + } + + self.cx_lines = Some((cx1, cx2)); + interrupt } /// Reset the DDR, control register, and underlying port. @@ -71,12 +111,16 @@ impl PiaPortRegisters { #[allow(dead_code)] /// The meanings of each bit in the control register. pub mod pia_control_bits { - pub const C1_ACTIVE_TRANSITION_FLAG: u8 = 0b10000000; // 1 = 0->1, 0 = 1->0 + pub const C1_ACTIVE_TRANSITION_FLAG: u8 = 0b10000000; pub const C2_ACTIVE_TRANSITION_FLAG: u8 = 0b01000000; pub const C2_DIRECTION: u8 = 0b00100000; // 1 = output, 0 = input - pub const C2_CONTROL: u8 = 0b00011000; // ??? + pub const C2_OUTPUT_MANUAL: u8 = 0b00010000; // 0 = pulse, 1 = manual + pub const C2_OUTPUT_TYPE: u8 = 0b00001000; // just read the datasheet for this one + pub const C2_INPUT_ACTIVE_TRANSITION: u8 = 0b00010000; // 0 = negative (falling), 1 = positive (rising) + pub const C2_INPUT_INTERRUPT_ENABLE: u8 = 0b00001000; // 0 = disable IRQs, 1 = enable IRQs pub const DDR_SELECT: u8 = 0b00000100; // enable accessing DDR - pub const C1_CONTROL: u8 = 0b00000011; // interrupt status control + pub const C1_ACTIVE_TRANSITION: u8 = 0b0000010; // 0 = negative (falling), 1 = positive (rising) + pub const C1_INTERRUPT_ENABLE: u8 = 0b00000001; // 0 = disable IRQs, 1 = enable IRQs } /// The MOS 6520 Peripheral Interface Adapter (PIA), containing two ports and diff --git a/src/memory/mos652x/via.rs b/src/memory/mos652x/via.rs index 3e5ab2e..b44a5f0 100644 --- a/src/memory/mos652x/via.rs +++ b/src/memory/mos652x/via.rs @@ -3,6 +3,8 @@ use crate::memory::{ ActiveInterrupt, Memory, Port, }; +use super::ActiveTransition; + #[allow(dead_code)] pub mod sr_control_bits { pub const SHIFT_DISABLED: u8 = 0b000; @@ -14,13 +16,23 @@ pub mod sr_control_bits { pub const SHIFT_OUT_BY_T2: u8 = 0b101; pub const SHIFT_OUT_BY_SYSTEM_CLOCK: u8 = 0b110; pub const SHIFT_OUT_BY_EXTERNAL_CLOCK: u8 = 0b111; // PB6? +} - pub const C1_ACTIVE_TRANSITION_FLAG: u8 = 0b10000000; // 1 = 0->1, 0 = 1->0 - pub const C2_ACTIVE_TRANSITION_FLAG: u8 = 0b01000000; - pub const C2_DIRECTION: u8 = 0b00100000; // 1 = output, 0 = input - pub const C2_CONTROL: u8 = 0b00011000; // ??? - pub const DDR_SELECT: u8 = 0b00000100; // enable accessing DDR - pub const C1_CONTROL: u8 = 0b00000011; // interrupt status control +#[allow(dead_code)] +pub mod pcr_control_bits { + pub const CB2_DIRECTION: u8 = 0b10000000; // 1 = output, 0 = input + pub const CB2_OUTPUT_MODE: u8 = 0b01000000; // 0 = pulse, 1 = manual + pub const CB2_OUTPUT_TYPE: u8 = 0b00100000; // just read the datasheet for this one + pub const CB2_INPUT_ACTIVE_TRANSITION: u8 = 0b01000000; // 0 = falling, 1 = rising + pub const CB2_INPUT_CLEAR_ON_OUTPUT: u8 = 0b00100000; // 0 = clear the interrupt flag when reading port B output, 1 = don't + pub const CB1_ACTIVE_TRANSITION: u8 = 0b00010000; // 0 = falling, 1 = rising + + pub const CA2_DIRECTION: u8 = 0b00001000; // 1 = output, 0 = input + pub const CA2_OUTPUT_MODE: u8 = 0b00000100; // 0 = pulse, 1 = manual + pub const CA2_OUTPUT_TYPE: u8 = 0b00000010; // just read the datasheet for this one + pub const CA2_INPUT_ACTIVE_TRANSITION: u8 = 0b00000100; // 0 = falling, 1 = rising + pub const CA2_INPUT_CLEAR_ON_OUTPUT: u8 = 0b00000010; // 0 = clear the interrupt flag when reading port A output, 1 = don't + pub const CA1_ACTIVE_TRANSITION: u8 = 0b00000001; // 0 = falling, 1 = rising } /// The MOS 6522 Versatile Interface Adapter (VIA). Contains two ports, @@ -39,13 +51,13 @@ pub struct Via { #[allow(dead_code)] pub mod interrupt_bits { pub const MASTER: u8 = 0b10000000; - pub const T1_ENABLE: u8 = 0b01000000; - pub const T2_ENABLE: u8 = 0b00100000; - pub const CB1_ENABLE: u8 = 0b00010000; - pub const CB2_ENABLE: u8 = 0b00001000; - pub const SR_ENABLE: u8 = 0b00000100; - pub const CA1_ENABLE: u8 = 0b00000010; - pub const CA2_ENABLE: u8 = 0b00000001; + pub const T1: u8 = 0b01000000; + pub const T2: u8 = 0b00100000; + pub const CB1: u8 = 0b00010000; + pub const CB2: u8 = 0b00001000; + pub const SR: u8 = 0b00000100; + pub const CA1: u8 = 0b00000010; + pub const CA2: u8 = 0b00000001; } impl Via { @@ -70,14 +82,14 @@ impl Memory for Via { 0x02 => self.b.ddr, 0x03 => self.a.ddr, 0x04 => { - self.t1.interrupt = false; + self.interrupts.clear_flag(interrupt_bits::T1); (self.t1.counter & 0xff) as u8 } 0x05 => ((self.t1.counter >> 8) & 0xff) as u8, 0x06 => (self.t1.latch & 0xff) as u8, 0x07 => ((self.t1.latch >> 8) & 0xff) as u8, 0x08 => { - self.t2.interrupt = false; + self.interrupts.clear_flag(interrupt_bits::T2); (self.t2.counter & 0xff) as u8 } 0x09 => ((self.t2.counter >> 8) & 0xff) as u8, @@ -103,17 +115,7 @@ impl Memory for Via { | (self.a.latch_enabled as u8) } 0x0c => self.pcr, - 0x0d => { - let mut value = 0; - if self.t1.interrupt { - value |= interrupt_bits::T1_ENABLE; - } - if self.t2.interrupt { - value |= interrupt_bits::T2_ENABLE; - } - - self.interrupts.read_flags(value) - } + 0x0d => self.interrupts.read_flags(), 0x0e => self.interrupts.read_enable(), 0x0f => self.a.read(), _ => unreachable!(), @@ -131,19 +133,19 @@ impl Memory for Via { self.t1.latch = (self.t1.latch & 0x00ff) | ((value as u16) << 8); self.t1.counter = self.t1.latch as i32; self.t1.running = true; - self.t1.interrupt = false; + self.interrupts.clear_flag(interrupt_bits::T1); } 0x06 => self.t1.latch = (self.t1.latch & 0xff00) | (value as u16), 0x07 => { self.t1.latch = (self.t1.latch & 0x00ff) | ((value as u16) << 8); - self.t1.interrupt = false; + self.interrupts.clear_flag(interrupt_bits::T1); } 0x08 => self.t2.latch = (self.t2.latch & 0xff00) | (value as u16), 0x09 => { self.t2.latch = (self.t2.latch & 0x00ff) | ((value as u16) << 8); self.t2.counter = self.t2.latch as i32; self.t2.running = true; - self.t2.interrupt = false; + self.interrupts.clear_flag(interrupt_bits::T2); } 0x0a => self.sr.data = value, 0x0b => { @@ -165,12 +167,7 @@ impl Memory for Via { } 0x0c => self.pcr = value, 0x0d => { - if (value & interrupt_bits::T1_ENABLE) == 0 { - self.t1.interrupt = false; - } - if (value & interrupt_bits::T2_ENABLE) == 0 { - self.t2.interrupt = false; - } + self.interrupts.clear_flag(!value); } 0x0e => self.interrupts.write_enable(value), 0x0f => self.a.write(value), @@ -184,25 +181,78 @@ impl Memory for Via { } fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> ActiveInterrupt { - if self.t1.poll(cycles_since_poll, total_cycle_count) - && self.interrupts.is_enabled(interrupt_bits::T1_ENABLE) - { - return ActiveInterrupt::IRQ; + let mut active_interrupt = ActiveInterrupt::None; + + if self.t1.poll(cycles_since_poll, total_cycle_count) { + self.interrupts.set_flag(interrupt_bits::T1); + + if self.interrupts.is_enabled(interrupt_bits::T1) { + active_interrupt = ActiveInterrupt::IRQ; + } + } + + if self.t2.poll(cycles_since_poll, total_cycle_count) { + self.interrupts.set_flag(interrupt_bits::T2); + + if self.interrupts.is_enabled(interrupt_bits::T2) { + active_interrupt = ActiveInterrupt::IRQ; + } } - if self.t2.poll(cycles_since_poll, total_cycle_count) - && self.interrupts.is_enabled(interrupt_bits::T2_ENABLE) + let (ca1, ca2) = self.a.poll(cycles_since_poll, total_cycle_count); + let (cb1, cb2) = self.b.poll(cycles_since_poll, total_cycle_count); + + if (self.pcr & pcr_control_bits::CA1_ACTIVE_TRANSITION == 0 && ca1 == ActiveTransition::Rising) + || (self.pcr & pcr_control_bits::CA1_ACTIVE_TRANSITION != 0 + && ca1 == ActiveTransition::Falling) { - return ActiveInterrupt::IRQ; + self.interrupts.set_flag(interrupt_bits::CA1); + + if self.interrupts.is_enabled(interrupt_bits::CA1) { + active_interrupt = ActiveInterrupt::IRQ; + } } - if self.a.poll(cycles_since_poll, total_cycle_count) - || self.b.poll(cycles_since_poll, total_cycle_count) + if self.pcr & pcr_control_bits::CA2_DIRECTION != 0 { + if (self.pcr & pcr_control_bits::CA2_INPUT_ACTIVE_TRANSITION == 0 + && ca2 == ActiveTransition::Rising) + || (self.pcr & pcr_control_bits::CA2_INPUT_ACTIVE_TRANSITION != 0 + && ca2 == ActiveTransition::Falling) + { + self.interrupts.set_flag(interrupt_bits::CA2); + + if self.interrupts.is_enabled(interrupt_bits::CA2) { + active_interrupt = ActiveInterrupt::IRQ; + } + } + } + + if (self.pcr & pcr_control_bits::CB1_ACTIVE_TRANSITION == 0 && cb1 == ActiveTransition::Rising) + || (self.pcr & pcr_control_bits::CB1_ACTIVE_TRANSITION != 0 + && cb1 == ActiveTransition::Falling) { - return ActiveInterrupt::IRQ; + self.interrupts.set_flag(interrupt_bits::CB1); + + if self.interrupts.is_enabled(interrupt_bits::CB1) { + active_interrupt = ActiveInterrupt::IRQ; + } } - ActiveInterrupt::None + if self.pcr & pcr_control_bits::CB2_DIRECTION != 0 { + if (self.pcr & pcr_control_bits::CB2_INPUT_ACTIVE_TRANSITION == 0 + && cb2 == ActiveTransition::Rising) + || (self.pcr & pcr_control_bits::CB2_INPUT_ACTIVE_TRANSITION != 0 + && cb2 == ActiveTransition::Falling) + { + self.interrupts.set_flag(interrupt_bits::CB2); + + if self.interrupts.is_enabled(interrupt_bits::CB2) { + active_interrupt = ActiveInterrupt::IRQ; + } + } + } + + active_interrupt } } @@ -243,7 +293,7 @@ mod tests { let mut via = Via::new(Box::new(NullPort::new()), Box::new(NullPort::new())); // enable timer 1 interrupts - via.write(0x0e, interrupt_bits::MASTER | interrupt_bits::T1_ENABLE); + via.write(0x0e, interrupt_bits::MASTER | interrupt_bits::T1); // set the timer to count down from 0x10 via.write(0x04, 0x10); @@ -266,7 +316,7 @@ mod tests { let mut via = Via::new(Box::new(NullPort::new()), Box::new(NullPort::new())); // enable timer 2 interrupts - via.write(0x0e, interrupt_bits::MASTER | interrupt_bits::T2_ENABLE); + via.write(0x0e, interrupt_bits::MASTER | interrupt_bits::T2); // set the timer to count down from 0x1234 via.write(0x08, 0x34); @@ -289,7 +339,7 @@ mod tests { let mut via = Via::new(Box::new(NullPort::new()), Box::new(NullPort::new())); // enable timer 1 interrupts - via.write(0x0e, interrupt_bits::MASTER | interrupt_bits::T1_ENABLE); + via.write(0x0e, interrupt_bits::MASTER | interrupt_bits::T1); // set timer 1 to continuous mode via.write(0x0b, 0b01000000); @@ -318,28 +368,25 @@ mod tests { // put something in the register via.write( 0x0e, - interrupt_bits::MASTER | interrupt_bits::T1_ENABLE | interrupt_bits::SR_ENABLE, + interrupt_bits::MASTER | interrupt_bits::T1 | interrupt_bits::SR, ); // we should read this with the master bit cleared - assert_eq!( - interrupt_bits::T1_ENABLE | interrupt_bits::SR_ENABLE, - via.read(0x0e) - ); + assert_eq!(interrupt_bits::T1 | interrupt_bits::SR, via.read(0x0e)); // *set* bits -- this shouldn't clear any via.write( 0x0e, - interrupt_bits::MASTER | interrupt_bits::T1_ENABLE | interrupt_bits::T2_ENABLE, + interrupt_bits::MASTER | interrupt_bits::T1 | interrupt_bits::T2, ); assert_eq!( - interrupt_bits::T1_ENABLE | interrupt_bits::SR_ENABLE | interrupt_bits::T2_ENABLE, + interrupt_bits::T1 | interrupt_bits::SR | interrupt_bits::T2, via.read(0x0e) ); // *clear* bits - via.write(0x0e, interrupt_bits::T2_ENABLE | interrupt_bits::SR_ENABLE); - assert_eq!(interrupt_bits::T1_ENABLE, via.read(0x0e)); + via.write(0x0e, interrupt_bits::T2 | interrupt_bits::SR); + assert_eq!(interrupt_bits::T1, via.read(0x0e)); } #[test] @@ -347,7 +394,7 @@ mod tests { let mut via = Via::new(Box::new(NullPort::new()), Box::new(NullPort::new())); // enable timer 1 interrupts - via.write(0x0e, interrupt_bits::MASTER | interrupt_bits::T1_ENABLE); + via.write(0x0e, interrupt_bits::MASTER | interrupt_bits::T1); // set timer 1 to count down from 0x10 via.write(0x04, 0x10); @@ -370,7 +417,7 @@ mod tests { let mut via = Via::new(Box::new(NullPort::new()), Box::new(NullPort::new())); // enable timer 1 interrupts - via.write(0x0e, interrupt_bits::MASTER | interrupt_bits::T1_ENABLE); + via.write(0x0e, interrupt_bits::MASTER | interrupt_bits::T1); // set timer 1 to continuous mode via.write(0x0b, 0b01000000); @@ -389,7 +436,7 @@ mod tests { } // ...but the flag register should be set - assert_eq!(interrupt_bits::T2_ENABLE, via.read(0x0d)); + assert_eq!(interrupt_bits::T2, via.read(0x0d)); // timer 1 should then trigger an interrupt for _ in 0..0x07 { @@ -399,23 +446,23 @@ mod tests { // ...and set the corresponding flag, plus the master bit assert_eq!( - interrupt_bits::MASTER | interrupt_bits::T1_ENABLE | interrupt_bits::T2_ENABLE, + interrupt_bits::MASTER | interrupt_bits::T1 | interrupt_bits::T2, via.read(0x0d) ); // clearing the master bit should have no effect via.write(0x0d, !interrupt_bits::MASTER); assert_eq!( - interrupt_bits::MASTER | interrupt_bits::T1_ENABLE | interrupt_bits::T2_ENABLE, + interrupt_bits::MASTER | interrupt_bits::T1 | interrupt_bits::T2, via.read(0x0d) ); // clearing just timer 1 should clear the master bit - via.write(0x0d, !interrupt_bits::T1_ENABLE); - assert_eq!(interrupt_bits::T2_ENABLE, via.read(0x0d)); + via.write(0x0d, !interrupt_bits::T1); + assert_eq!(interrupt_bits::T2, via.read(0x0d)); // clearing timer 2 should work as expected - via.write(0x0d, !interrupt_bits::T2_ENABLE); + via.write(0x0d, !interrupt_bits::T2); assert_eq!(0, via.read(0x0d)); // if we let timer 1 run again, it should set the flag again diff --git a/src/memory/ports.rs b/src/memory/ports.rs index 1df290f..cd7489c 100644 --- a/src/memory/ports.rs +++ b/src/memory/ports.rs @@ -1,16 +1,18 @@ /// A Port that can be read from, written to, reset, or polled for interrupts. /// Used in the MOS 6520 PIA and the 6522 VIA. pub trait Port { - /// Read a byte from the port. This is implementation-defined, and may have - /// side effects. - fn read(&mut self) -> u8; + /// Read a byte from the port's data lines. This is implementation-defined, + /// and may have side effects. + fn read_data(&mut self) -> u8; /// Write a byte to the port. This is implementation-defined. - fn write(&mut self, value: u8); + fn write_data(&mut self, value: u8); - /// Poll the port for interrupts. A port may trigger an interrupt for any - /// implementation-defined reason. - fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> bool; + /// Poll the port's control lines. This is implementation-defined. + fn read_control(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> (bool, bool); + + /// Write to the port's CX2 line (CA2 or CB2). This is implementation-defined. + fn write_cx2(&mut self, value: bool); /// Reset the port to its initial state, analogous to a system reboot. fn reset(&mut self); @@ -37,21 +39,27 @@ impl NullPort { } impl Port for NullPort { - fn read(&mut self) -> u8 { + fn read_data(&mut self) -> u8 { if let Some(message) = self.warn { - println!("attempted to read from {} at address {:04x}", message, 0); + println!("attempted to read from null port {} data", message); } 0 } - fn write(&mut self, _value: u8) { + fn write_data(&mut self, _value: u8) { if let Some(message) = self.warn { - println!("attempted to write to {} at address {:04x}", message, 0); + println!("attempted to write to null port {} data", message); } } - fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { - false + fn read_control(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> (bool, bool) { + (false, false) + } + + fn write_cx2(&mut self, _value: bool) { + if let Some(message) = self.warn { + println!("attempted to write to null port {} control line 2", message); + } } fn reset(&mut self) {} @@ -64,8 +72,8 @@ mod tests { #[test] fn test_null() { let mut port = NullPort::new(); - assert_eq!(port.read(), 0); - port.write(0x12); - assert_eq!(port.read(), 0); + assert_eq!(port.read_data(), 0); + port.write_data(0x12); + assert_eq!(port.read_data(), 0); } } diff --git a/src/systems/c64/mod.rs b/src/systems/c64/mod.rs index 52d5752..dbadca7 100644 --- a/src/systems/c64/mod.rs +++ b/src/systems/c64/mod.rs @@ -51,19 +51,21 @@ impl C64Cia1PortA { } impl Port for C64Cia1PortA { - fn read(&mut self) -> u8 { + fn read_data(&mut self) -> u8 { self.keyboard_row.get() } - fn write(&mut self, value: u8) { + fn write_data(&mut self, value: u8) { self.keyboard_row.set(value); } - fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { - false + fn read_control(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> (bool, bool) { + (false, false) } fn reset(&mut self) {} + + fn write_cx2(&mut self, _value: bool) {} } /// Port B on the first CIA chip on the C64 deals with reading columns of the keyboard matrix. @@ -90,7 +92,7 @@ impl C64Cia1PortB { } impl Port for C64Cia1PortB { - fn read(&mut self) -> u8 { + fn read_data(&mut self) -> u8 { let row_mask = self.keyboard_row.get(); let mut value = 0b1111_1111; @@ -115,15 +117,17 @@ impl Port for C64Cia1PortB { value } - fn write(&mut self, _value: u8) { + fn write_data(&mut self, _value: u8) { panic!("Tried to write to keyboard row"); } - fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { - false + fn read_control(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> (bool, bool) { + (false, false) } fn reset(&mut self) {} + + fn write_cx2(&mut self, _value: bool) {} } /// Bank switching implementation performed using the 6510's I/O port. @@ -151,12 +155,12 @@ impl C64BankSwitching { } impl Port for C64BankSwitching { - fn read(&mut self) -> u8 { + fn read_data(&mut self) -> u8 { (self.loram as u8) | (self.hiram as u8) << 1 | (self.charen as u8) << 2 } #[allow(clippy::bool_to_int_with_if)] - fn write(&mut self, value: u8) { + fn write_data(&mut self, value: u8) { self.loram = (value & 0b001) != 0; self.hiram = (value & 0b010) != 0; self.charen = (value & 0b100) != 0; @@ -188,8 +192,8 @@ impl Port for C64BankSwitching { self.selectors[5].set(if !self.hiram { 1 } else { 0 }); } - fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { - false + fn read_control(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> (bool, bool) { + (false, false) } fn reset(&mut self) { @@ -197,6 +201,8 @@ impl Port for C64BankSwitching { self.loram = true; self.charen = true; } + + fn write_cx2(&mut self, _value: bool) {} } /// Configuration for a Commodore 64 system. diff --git a/src/systems/pet/mod.rs b/src/systems/pet/mod.rs index 9ee8f5b..3384bce 100644 --- a/src/systems/pet/mod.rs +++ b/src/systems/pet/mod.rs @@ -51,7 +51,7 @@ impl PetPia1PortA { } impl Port for PetPia1PortA { - fn read(&mut self) -> u8 { + fn read_data(&mut self) -> u8 { 0b1000_0000 | self.keyboard_row.get() //^ diagnostic mode off // ^ IEEE488 (not implemented) @@ -59,11 +59,11 @@ impl Port for PetPia1PortA { // ^^^^ Keyboard row select } - fn write(&mut self, value: u8) { + fn write_data(&mut self, value: u8) { self.keyboard_row.set(value & 0b1111); } - fn poll(&mut self, _cycles_since_poll: u64, total_cycle_count: u64) -> bool { + fn read_control(&mut self, _cycles_since_poll: u64, total_cycle_count: u64) -> (bool, bool) { // let min_elapsed = ((info.cycles_per_second as f64 / 60.0) * (2.0 / 3.0)) as u64; let min_elapsed = 0; // TODO: fix @@ -74,15 +74,14 @@ impl Port for PetPia1PortA { { self.last_draw_cycle = total_cycle_count; self.last_draw_instant = Some(Instant::now()); - true - // false + (false, true) } else { - false + (false, false) } } None => { self.last_draw_instant = Some(Instant::now()); - false + (false, false) } } } @@ -90,6 +89,10 @@ impl Port for PetPia1PortA { fn reset(&mut self) { self.keyboard_row.set(0); } + + fn write_cx2(&mut self, _value: bool) { + // TODO: on old PETs, this blanks the screen lol + } } /// Port B on the first PIA. @@ -115,7 +118,7 @@ impl PetPia1PortB { } impl Port for PetPia1PortB { - fn read(&mut self) -> u8 { + fn read_data(&mut self) -> u8 { let row = self.keyboard_row.get(); let row = KEYBOARD_MAPPING[row as usize % 10]; let mut value = 0b1111_1111; @@ -137,13 +140,15 @@ impl Port for PetPia1PortB { value } - fn write(&mut self, _value: u8) {} + fn write_data(&mut self, _value: u8) {} - fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { - false + fn read_control(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> (bool, bool) { + (false, false) } fn reset(&mut self) {} + + fn write_cx2(&mut self, _value: bool) {} } /// Configuration for a Commodore PET system. diff --git a/src/systems/vic/mod.rs b/src/systems/vic/mod.rs index bbd7840..f235988 100644 --- a/src/systems/vic/mod.rs +++ b/src/systems/vic/mod.rs @@ -126,7 +126,7 @@ impl VicVia1PortA { } impl Port for VicVia1PortA { - fn read(&mut self) -> u8 { + fn read_data(&mut self) -> u8 { let joystick = self.platform.get_joystick_state(); let pin_0 = !joystick.up; @@ -138,13 +138,15 @@ impl Port for VicVia1PortA { (pin_0 as u8) << 2 | (pin_1 as u8) << 3 | (pin_2 as u8) << 4 | (lightpen_fire as u8) << 5 } - fn write(&mut self, _value: u8) {} + fn write_data(&mut self, _value: u8) {} - fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { - false + fn read_control(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> (bool, bool) { + (false, false) } fn reset(&mut self) {} + + fn write_cx2(&mut self, _value: bool) {} } /// Port B on the second VIA chip. @@ -170,19 +172,21 @@ impl VicVia2PortB { } impl Port for VicVia2PortB { - fn read(&mut self) -> u8 { + fn read_data(&mut self) -> u8 { self.keyboard_col.get() | (self.joy_pin_3.get() as u8) << 7 } - fn write(&mut self, value: u8) { + fn write_data(&mut self, value: u8) { self.keyboard_col.set(value); } - fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { - false + fn read_control(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> (bool, bool) { + (false, false) } fn reset(&mut self) {} + + fn write_cx2(&mut self, _value: bool) {} } /// Port A on the second VIA chip. @@ -210,7 +214,7 @@ impl VicVia2PortA { } impl Port for VicVia2PortA { - fn read(&mut self) -> u8 { + fn read_data(&mut self) -> u8 { let col_mask = self.keyboard_col.get(); let mut value = 0b1111_1111; @@ -235,13 +239,15 @@ impl Port for VicVia2PortA { value } - fn write(&mut self, _value: u8) {} + fn write_data(&mut self, _value: u8) {} - fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { - false + fn read_control(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> (bool, bool) { + (false, false) } fn reset(&mut self) {} + + fn write_cx2(&mut self, _value: bool) {} } /// Configuration for a VIC-20 system.