use crate::memory::{ActiveInterrupt, Memory, Port};
struct PiaPortRegisters {
port: Box<dyn Port>,
writes: u8,
ddr: u8,
pub control: u8,
}
impl PiaPortRegisters {
pub fn new(port: Box<dyn Port>) -> Self {
Self {
port,
writes: 0,
ddr: 0,
control: 0,
}
}
pub fn read(&mut self) -> u8 {
if self.control & pia_control_bits::DDR_SELECT != 0 {
(self.port.read() & !self.ddr) | (self.writes & self.ddr)
} else {
self.ddr
}
}
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);
} else {
self.ddr = value;
}
}
pub fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> bool {
self.port.poll(cycles_since_poll, total_cycle_count)
}
pub fn reset(&mut self) {
self.ddr = 0;
self.control = 0;
self.port.reset();
}
}
#[allow(dead_code)]
pub mod pia_control_bits {
pub const C1_ACTIVE_TRANSITION_FLAG: u8 = 0b10000000; pub const C2_ACTIVE_TRANSITION_FLAG: u8 = 0b01000000;
pub const C2_DIRECTION: u8 = 0b00100000; pub const C2_CONTROL: u8 = 0b00011000; pub const DDR_SELECT: u8 = 0b00000100; pub const C1_CONTROL: u8 = 0b00000011; }
pub struct Pia {
a: PiaPortRegisters,
b: PiaPortRegisters,
}
impl Pia {
pub fn new(a: Box<dyn Port>, b: Box<dyn Port>) -> Self {
Self {
a: PiaPortRegisters::new(a),
b: PiaPortRegisters::new(b),
}
}
}
impl Memory for Pia {
fn read(&mut self, address: u16) -> u8 {
match address % 0x04 {
0x00 => self.a.read(),
0x01 => self.a.control,
0x02 => self.b.read(),
0x03 => self.b.control,
_ => unreachable!(),
}
}
fn write(&mut self, address: u16, value: u8) {
match address % 0x04 {
0x00 => self.a.write(value),
0x01 => self.a.control = value,
0x02 => self.b.write(value),
0x03 => self.b.control = value,
_ => unreachable!(),
}
}
fn reset(&mut self) {
self.a.reset();
self.b.reset();
}
fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> ActiveInterrupt {
let a = self.a.poll(cycles_since_poll, total_cycle_count);
let b = self.b.poll(cycles_since_poll, total_cycle_count);
if a || b {
ActiveInterrupt::IRQ
} else {
ActiveInterrupt::None
}
}
}
#[cfg(test)]
mod tests {
use crate::memory::NullPort;
use super::*;
#[test]
fn test_read() {
let mut pia = Pia::new(Box::new(NullPort::new()), Box::new(NullPort::new()));
pia.write(0x01, pia_control_bits::DDR_SELECT);
assert_eq!(0, pia.read(0x00));
assert_eq!(pia_control_bits::DDR_SELECT, pia.read(0x01));
assert_eq!(0, pia.read(0x02));
assert_eq!(0, pia.read(0x03));
assert_eq!(0, pia.read(0x04));
pia.write(0x01, 0);
assert_eq!(0, pia.read(0x00));
assert_eq!(0, pia.read(0x01));
assert_eq!(0, pia.read(0x02));
assert_eq!(0, pia.read(0x03));
}
#[test]
fn test_write() {
let mut pia = Pia::new(Box::new(NullPort::new()), Box::new(NullPort::new()));
pia.write(0x01, pia_control_bits::DDR_SELECT);
pia.write(0x00, 0b10101010);
assert_eq!(0, pia.read(0x00));
pia.write(0x01, 0);
pia.write(0x00, 0b11110000);
pia.write(0x01, pia_control_bits::DDR_SELECT);
assert_eq!(0b10100000, pia.read(0x00));
assert_eq!(pia_control_bits::DDR_SELECT, pia.read(0x01));
pia.write(0x00, 0b01010101);
assert_eq!(0b01010000, pia.read(0x00));
}
}