use crate::memory::{
mos652x::{InterruptRegister, PortRegisters, ShiftRegister, Timer, TimerOutput},
ActiveInterrupt, Memory, Port,
};
#[allow(dead_code)]
pub mod sr_control_bits {
pub const SHIFT_DISABLED: u8 = 0b000;
pub const SHIFT_IN_BY_T2: u8 = 0b001;
pub const SHIFT_IN_BY_SYSTEM_CLOCK: u8 = 0b010;
pub const SHIFT_IN_BY_EXTERNAL_CLOCK: u8 = 0b011; pub const SHIFT_OUT_FREE_RUN: u8 = 0b100; 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; 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 Via {
a: PortRegisters,
b: PortRegisters,
t1: Timer,
t2: Timer,
sr: ShiftRegister,
interrupts: InterruptRegister,
pcr: u8, }
#[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;
}
impl Via {
pub fn new(a: Box<dyn Port>, b: Box<dyn Port>) -> Self {
Self {
a: PortRegisters::new(a),
b: PortRegisters::new(b),
t1: Timer::new(),
t2: Timer::new(),
sr: ShiftRegister::new(),
interrupts: InterruptRegister::new(),
pcr: 0,
}
}
}
impl Memory for Via {
fn read(&mut self, address: u16) -> u8 {
match address % 0x10 {
0x00 => self.b.read(),
0x01 => self.a.read(), 0x02 => self.b.ddr,
0x03 => self.a.ddr,
0x04 => {
self.t1.interrupt = false;
(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.t2.counter & 0xff) as u8
}
0x09 => ((self.t2.counter >> 8) & 0xff) as u8,
0x0a => self.sr.data,
0x0b => {
let t1_output_enable = match self.t1.output {
TimerOutput::None => false,
TimerOutput::Pulse => true,
_ => unreachable!(),
};
let t2_pulse_counting = match self.t2.output {
TimerOutput::None => false,
TimerOutput::PulseCount => true,
_ => unreachable!(),
};
(t1_output_enable as u8) << 7
| (self.t1.continuous as u8) << 6
| (t2_pulse_counting as u8) << 5
| self.sr.control << 2
| (self.b.latch_enabled as u8) << 1
| (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)
}
0x0e => self.interrupts.read_enable(),
0x0f => self.a.read(),
_ => unreachable!(),
}
}
fn write(&mut self, address: u16, value: u8) {
match address % 0x10 {
0x00 => self.b.write(value),
0x01 => self.a.write(value), 0x02 => self.b.ddr = value,
0x03 => self.a.ddr = value,
0x04 => self.t1.latch = (self.t1.latch & 0xff00) | (value as u16),
0x05 => {
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;
}
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;
}
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;
}
0x0a => self.sr.data = value,
0x0b => {
self.t1.continuous = (value & 0b01000000) != 0;
self.sr.control = (value & 0b00011100) >> 2;
self.b.latch_enabled = (value & 0b00000010) != 0;
self.a.latch_enabled = (value & 0b00000001) != 0;
self.t1.output = if (value & 0b10000000) != 0 {
TimerOutput::Pulse
} else {
TimerOutput::None
};
self.t2.output = if (value & 0b00100000) != 0 {
TimerOutput::PulseCount
} else {
TimerOutput::None
};
}
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;
}
}
0x0e => self.interrupts.write_enable(value),
0x0f => self.a.write(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 {
if self.t1.poll(cycles_since_poll, total_cycle_count)
&& self.interrupts.is_enabled(interrupt_bits::T1_ENABLE)
{
return ActiveInterrupt::IRQ;
}
if self.t2.poll(cycles_since_poll, total_cycle_count)
&& self.interrupts.is_enabled(interrupt_bits::T2_ENABLE)
{
return ActiveInterrupt::IRQ;
}
if self.a.poll(cycles_since_poll, total_cycle_count)
|| self.b.poll(cycles_since_poll, total_cycle_count)
{
return ActiveInterrupt::IRQ;
}
ActiveInterrupt::None
}
}
#[cfg(test)]
mod tests {
use crate::memory::NullPort;
use super::*;
#[test]
fn test_read_write() {
let mut via = Via::new(Box::new(NullPort::new()), Box::new(NullPort::new()));
via.write(0x00, 0b10101010);
assert_eq!(0, via.read(0x00));
via.write(0x01, 0b00110011);
assert_eq!(0, via.read(0x01));
via.write(0x02, 0b11110000);
via.write(0x03, 0b00111100);
assert_eq!(0b10100000, via.read(0x00));
assert_eq!(0b11110000, via.read(0x02));
assert_eq!(0b00110000, via.read(0x01));
assert_eq!(0b00111100, via.read(0x03));
via.write(0x00, 0b01010101);
assert_eq!(0b01010000, via.read(0x00));
}
#[test]
fn test_timer_1() {
let mut via = Via::new(Box::new(NullPort::new()), Box::new(NullPort::new()));
via.write(0x0e, interrupt_bits::MASTER | interrupt_bits::T1_ENABLE);
via.write(0x04, 0x10);
via.write(0x05, 0x00);
for _ in 0..0x0F {
assert_eq!(ActiveInterrupt::None, via.poll(1, 0));
}
assert_eq!(ActiveInterrupt::IRQ, via.poll(1, 0));
for _ in 0..0x20 {
assert_eq!(ActiveInterrupt::None, via.poll(1, 0));
}
}
#[test]
fn test_timer_2() {
let mut via = Via::new(Box::new(NullPort::new()), Box::new(NullPort::new()));
via.write(0x0e, interrupt_bits::MASTER | interrupt_bits::T2_ENABLE);
via.write(0x08, 0x34);
assert_eq!(ActiveInterrupt::None, via.poll(1, 0));
via.write(0x09, 0x12);
for _ in 0..0x1233 {
assert_eq!(ActiveInterrupt::None, via.poll(1, 0));
}
assert_eq!(ActiveInterrupt::IRQ, via.poll(1, 0));
}
#[test]
fn test_t1_continuous() {
let mut via = Via::new(Box::new(NullPort::new()), Box::new(NullPort::new()));
via.write(0x0e, interrupt_bits::MASTER | interrupt_bits::T1_ENABLE);
via.write(0x0b, 0b01000000);
via.write(0x04, 0x10);
via.write(0x05, 0x00);
for _ in 0..0x0F {
assert_eq!(ActiveInterrupt::None, via.poll(1, 0));
}
assert_eq!(ActiveInterrupt::IRQ, via.poll(1, 0));
for _ in 0..0x0F {
assert_eq!(ActiveInterrupt::None, via.poll(1, 0));
}
assert_eq!(ActiveInterrupt::IRQ, via.poll(1, 0));
}
#[test]
fn test_ier_register() {
let mut via = Via::new(Box::new(NullPort::new()), Box::new(NullPort::new()));
via.write(
0x0e,
interrupt_bits::MASTER | interrupt_bits::T1_ENABLE | interrupt_bits::SR_ENABLE,
);
assert_eq!(
interrupt_bits::T1_ENABLE | interrupt_bits::SR_ENABLE,
via.read(0x0e)
);
via.write(
0x0e,
interrupt_bits::MASTER | interrupt_bits::T1_ENABLE | interrupt_bits::T2_ENABLE,
);
assert_eq!(
interrupt_bits::T1_ENABLE | interrupt_bits::SR_ENABLE | interrupt_bits::T2_ENABLE,
via.read(0x0e)
);
via.write(0x0e, interrupt_bits::T2_ENABLE | interrupt_bits::SR_ENABLE);
assert_eq!(interrupt_bits::T1_ENABLE, via.read(0x0e));
}
#[test]
fn test_ier_timers() {
let mut via = Via::new(Box::new(NullPort::new()), Box::new(NullPort::new()));
via.write(0x0e, interrupt_bits::MASTER | interrupt_bits::T1_ENABLE);
via.write(0x04, 0x10);
via.write(0x05, 0x00);
via.write(0x08, 0x08);
via.write(0x09, 0x00);
for _ in 0..0x0F {
assert_eq!(ActiveInterrupt::None, via.poll(1, 0));
}
assert_eq!(ActiveInterrupt::IRQ, via.poll(1, 0));
}
#[test]
fn test_ifr() {
let mut via = Via::new(Box::new(NullPort::new()), Box::new(NullPort::new()));
via.write(0x0e, interrupt_bits::MASTER | interrupt_bits::T1_ENABLE);
via.write(0x0b, 0b01000000);
via.write(0x04, 0x10);
via.write(0x05, 0x00);
via.write(0x08, 0x08);
via.write(0x09, 0x00);
for _ in 0..0x08 {
assert_eq!(ActiveInterrupt::None, via.poll(1, 0));
}
assert_eq!(interrupt_bits::T2_ENABLE, via.read(0x0d));
for _ in 0..0x07 {
assert_eq!(ActiveInterrupt::None, via.poll(1, 0));
}
assert_eq!(ActiveInterrupt::IRQ, via.poll(1, 0));
assert_eq!(
interrupt_bits::MASTER | interrupt_bits::T1_ENABLE | interrupt_bits::T2_ENABLE,
via.read(0x0d)
);
via.write(0x0d, !interrupt_bits::MASTER);
assert_eq!(
interrupt_bits::MASTER | interrupt_bits::T1_ENABLE | interrupt_bits::T2_ENABLE,
via.read(0x0d)
);
via.write(0x0d, !interrupt_bits::T1_ENABLE);
assert_eq!(interrupt_bits::T2_ENABLE, via.read(0x0d));
via.write(0x0d, !interrupt_bits::T2_ENABLE);
assert_eq!(0, via.read(0x0d));
for _ in 0..0x0F {
assert_eq!(ActiveInterrupt::None, via.poll(1, 0));
}
assert_eq!(ActiveInterrupt::IRQ, via.poll(1, 0));
}
}