use crate::memory::{
mos652x::{InterruptRegister, PortRegisters, ShiftRegister, Timer},
ActiveInterrupt, Memory, Port,
};
struct TimeRegisters {
tenth_seconds: u8,
seconds: u8,
ten_seconds: u8,
minutes: u8,
ten_minutes: u8,
hours: u8,
ten_hours: u8,
am_pm: u8,
}
impl TimeRegisters {
fn new() -> Self {
Self {
tenth_seconds: 0,
seconds: 0,
ten_seconds: 0,
minutes: 0,
ten_minutes: 0,
hours: 0,
ten_hours: 0,
am_pm: 0,
}
}
}
struct TimeClock {
time: TimeRegisters,
alarm: TimeRegisters,
rtc_rate: bool, write_action: bool, }
impl TimeClock {
fn new() -> Self {
Self {
time: TimeRegisters::new(),
alarm: TimeRegisters::new(),
rtc_rate: false,
write_action: false,
}
}
fn reset(&mut self) {
self.time = TimeRegisters::new();
self.alarm = TimeRegisters::new();
self.rtc_rate = false;
self.write_action = false;
}
}
#[allow(dead_code)]
mod interrupt_bits {
pub const TIMER_A: u8 = 0b0000_0001;
pub const TIMER_B: u8 = 0b0000_0010;
pub const ALARM: u8 = 0b0000_0100;
pub const SHIFT_REGISTER: u8 = 0b0000_1000;
pub const FLAG: u8 = 0b0001_0000;
pub const MASTER: u8 = 0b1000_0000;
}
pub struct Cia {
a: PortRegisters,
b: PortRegisters,
timer_a: Timer,
timer_b: Timer,
time_clock: TimeClock,
shift_register: ShiftRegister,
interrupts: InterruptRegister,
}
impl Cia {
pub fn new(port_a: Box<dyn Port>, port_b: Box<dyn Port>) -> Self {
Self {
a: PortRegisters::new(port_a),
b: PortRegisters::new(port_b),
timer_a: Timer::new(),
timer_b: Timer::new(),
time_clock: TimeClock::new(),
shift_register: ShiftRegister::new(),
interrupts: InterruptRegister::new(),
}
}
}
impl Memory for Cia {
fn read(&mut self, address: u16) -> u8 {
match address % 0x10 {
0x00 => self.a.read(),
0x01 => self.b.read(),
0x02 => self.a.ddr,
0x03 => self.b.ddr,
0x04 => self.timer_a.counter as u8,
0x05 => (self.timer_a.counter >> 8) as u8,
0x06 => self.timer_b.counter as u8,
0x07 => (self.timer_b.counter >> 8) as u8,
0x08 => self.time_clock.time.tenth_seconds,
0x09 => (self.time_clock.time.ten_seconds << 4) | self.time_clock.time.seconds,
0x0A => (self.time_clock.time.ten_minutes << 4) | self.time_clock.time.minutes,
0x0B => {
(self.time_clock.time.am_pm << 7)
| (self.time_clock.time.ten_hours << 4)
| self.time_clock.time.hours
}
0x0C => self.shift_register.data,
0x0D => {
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;
value
}
0x0E => {
(self.timer_a.read_cia() & 0b0011_1111)
| ((self.shift_register.direction as u8) << 6)
| ((self.time_clock.rtc_rate as u8) << 7)
}
0x0F => self.timer_b.read_cia() | ((self.time_clock.write_action as u8) << 7),
_ => unreachable!(),
}
}
fn write(&mut self, address: u16, value: u8) {
match address % 0x10 {
0x00 => self.a.write(value),
0x01 => self.b.write(value),
0x02 => self.a.ddr = value,
0x03 => self.b.ddr = value,
0x04 => self.timer_a.latch = (self.timer_a.latch & 0xFF00) | value as u16,
0x05 => {
self.timer_a.latch = (self.timer_a.latch & 0x00FF) | ((value as u16) << 8);
self.timer_a.counter = self.timer_a.latch as i32;
}
0x06 => self.timer_b.latch = (self.timer_b.latch & 0xFF00) | value as u16,
0x07 => {
self.timer_b.latch = (self.timer_b.latch & 0x00FF) | ((value as u16) << 8);
self.timer_b.counter = self.timer_b.latch as i32;
}
0x08 => match self.time_clock.write_action {
false => self.time_clock.time.tenth_seconds = value,
true => self.time_clock.alarm.tenth_seconds = value,
},
0x09 => match self.time_clock.write_action {
false => {
self.time_clock.time.ten_seconds = value >> 4;
self.time_clock.time.seconds = value & 0x0F;
}
true => {
self.time_clock.alarm.ten_seconds = value >> 4;
self.time_clock.alarm.seconds = value & 0x0F;
}
},
0x0A => match self.time_clock.write_action {
false => {
self.time_clock.time.ten_minutes = value >> 4;
self.time_clock.time.minutes = value & 0x0F;
}
true => {
self.time_clock.alarm.ten_minutes = value >> 4;
self.time_clock.alarm.minutes = value & 0x0F;
}
},
0x0B => match self.time_clock.write_action {
false => {
self.time_clock.time.am_pm = value >> 7;
self.time_clock.time.ten_hours = value >> 4;
self.time_clock.time.hours = value & 0x0F;
}
true => {
self.time_clock.alarm.am_pm = value >> 7;
self.time_clock.alarm.ten_hours = value >> 4;
self.time_clock.alarm.hours = value & 0x0F;
}
},
0x0C => self.shift_register.data = value,
0x0D => self.interrupts.write_enable(value),
0x0E => {
self.timer_a.write_cia(value & 0b0011_1111);
self.shift_register.direction = value & 0b0100_0000 != 0;
self.time_clock.rtc_rate = value & 0b1000_0000 != 0;
}
0x0F => {
self.timer_b.write_cia(value & 0b0111_1111);
self.time_clock.write_action = value & 0b1000_0000 != 0;
}
_ => unreachable!(),
}
}
fn reset(&mut self) {
self.a.reset();
self.b.reset();
self.timer_a.reset();
self.timer_b.reset();
self.time_clock.reset();
self.shift_register.reset();
self.interrupts.reset();
}
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;
}
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.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 cia = Cia::new(Box::new(NullPort::new()), Box::new(NullPort::new()));
cia.write(0x00, 0b10101010);
assert_eq!(0, cia.read(0x00));
cia.write(0x01, 0b00110011);
assert_eq!(0, cia.read(0x01));
cia.write(0x02, 0b11110000);
cia.write(0x03, 0b00111100);
assert_eq!(0b10100000, cia.read(0x00));
assert_eq!(0b11110000, cia.read(0x02));
assert_eq!(0b00110000, cia.read(0x01));
assert_eq!(0b00111100, cia.read(0x03));
cia.write(0x00, 0b01010101);
assert_eq!(0b01010000, cia.read(0x00));
}
#[test]
fn test_timer_a() {
let mut cia = Cia::new(Box::new(NullPort::new()), Box::new(NullPort::new()));
cia.write(0x0D, interrupt_bits::MASTER | interrupt_bits::TIMER_A);
cia.write(0x04, 0x10);
cia.write(0x05, 0x00);
cia.write(0x0E, 0b0000_1001);
for _ in 0..0x0F {
assert_eq!(ActiveInterrupt::None, cia.poll(1, 0));
}
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, 0));
for _ in 0..0x20 {
assert_eq!(ActiveInterrupt::None, cia.poll(1, 0));
}
}
#[test]
fn test_timer_b() {
let mut cia = Cia::new(Box::new(NullPort::new()), Box::new(NullPort::new()));
cia.write(0x0D, interrupt_bits::MASTER | interrupt_bits::TIMER_B);
cia.write(0x06, 0x34);
cia.write(0x07, 0x12);
cia.write(0x0F, 0b0000_1001);
for _ in 0..0x1233 {
assert_eq!(ActiveInterrupt::None, cia.poll(1, 0));
}
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, 0));
}
#[test]
fn test_timer_a_continuous() {
let mut cia = Cia::new(Box::new(NullPort::new()), Box::new(NullPort::new()));
cia.write(0x0D, interrupt_bits::MASTER | interrupt_bits::TIMER_A);
cia.write(0x04, 0x10);
cia.write(0x05, 0x00);
cia.write(0x0E, 0b0000_0001);
assert_eq!(ActiveInterrupt::None, cia.poll(0x0F, 0));
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, 0));
assert_eq!(ActiveInterrupt::None, cia.poll(0x0F, 0));
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, 0));
}
#[test]
fn test_ier_timers() {
let mut cia = Cia::new(Box::new(NullPort::new()), Box::new(NullPort::new()));
cia.write(0x0D, interrupt_bits::MASTER | interrupt_bits::TIMER_A);
cia.write(0x04, 0x10);
cia.write(0x05, 0x00);
cia.write(0x06, 0x08);
cia.write(0x07, 0x00);
cia.write(0x0E, 0b0000_1001);
cia.write(0x0F, 0b0000_1001);
for _ in 0..0x0F {
assert_eq!(ActiveInterrupt::None, cia.poll(1, 0));
}
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, 0));
}
#[test]
fn test_interrupt_flags() {
let mut cia = Cia::new(Box::new(NullPort::new()), Box::new(NullPort::new()));
cia.write(0x0D, interrupt_bits::MASTER | interrupt_bits::TIMER_A);
cia.write(0x0E, 0b0000_0001);
cia.write(0x04, 0x10);
cia.write(0x05, 0x00);
cia.write(0x06, 0x08);
cia.write(0x07, 0x00);
for _ in 0..0x08 {
assert_eq!(ActiveInterrupt::None, cia.poll(1, 0));
}
assert_eq!(interrupt_bits::TIMER_B, cia.read(0x0D));
assert_eq!(0x00, cia.read(0x0D));
for _ in 0..0x07 {
assert_eq!(ActiveInterrupt::None, cia.poll(1, 0));
}
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, 0));
assert_eq!(
interrupt_bits::MASTER | interrupt_bits::TIMER_A,
cia.read(0x0D)
);
assert_eq!(0x00, cia.read(0x0D));
for _ in 0..0x0F {
assert_eq!(ActiveInterrupt::None, cia.poll(1, 0));
}
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, 0));
}
}