1use bitvec::prelude::*;
2use log::{trace, warn};
3use serde::{Deserialize, Serialize};
4
5use crate::{
6 consts::{INT_JOYPAD, INT_TIMER},
7 context,
8 interface::{Input, LinkCable},
9 serial::SerialTransfer,
10 util::{pack, trait_alias},
11};
12
13trait_alias!(pub trait Context = context::InterruptFlag + context::Ppu + context::Apu + context::Model);
14
15#[derive(Serialize, Deserialize)]
16pub struct Io {
17 select_action_buttons: bool,
18 select_direction_buttons: bool,
19
20 divider: u16,
21 timer_counter: u8,
22 timer_modulo: u8,
23 timer_enable: bool,
24 input_clock_select: u8,
25 prev_timer_clock: bool,
26 timer_reload: bool,
27 timer_reloaded: bool,
28
29 serial: SerialTransfer,
30 input: Input,
31}
32
33impl Default for Io {
34 fn default() -> Self {
35 Self {
36 select_action_buttons: true,
37 select_direction_buttons: true,
38 divider: 0,
39 timer_counter: 0,
40 timer_modulo: 0,
41 timer_enable: false,
42 input_clock_select: 0,
43 prev_timer_clock: false,
44 timer_reload: false,
45 timer_reloaded: false,
46 serial: SerialTransfer::default(),
47 input: Input::default(),
48 }
49 }
50}
51
52impl Io {
53 pub fn new() -> Self {
54 Self::default()
55 }
56
57 pub fn tick(&mut self, ctx: &mut impl Context) {
58 self.divider = self.divider.wrapping_add(4);
59
60 self.timer_reloaded = false;
61
62 if self.timer_reload {
63 log::trace!("Timer reload: ${:02X}", self.timer_modulo);
64 self.timer_counter = self.timer_modulo;
65 ctx.set_interrupt_flag_bit(INT_TIMER);
66 self.timer_reload = false;
67 self.timer_reloaded = true;
68 }
69
70 const TIMER_DIVIDER_BITS: [u8; 4] = [9, 3, 5, 7];
71 let clock_bit = TIMER_DIVIDER_BITS[self.input_clock_select as usize] as usize;
72 let timer_clock = self.timer_enable && self.divider.view_bits::<Lsb0>()[clock_bit];
73
74 if self.prev_timer_clock && !timer_clock {
76 let (new_counter, overflow) = self.timer_counter.overflowing_add(1);
77 self.timer_counter = new_counter;
78 if overflow {
79 log::trace!("Timer overflow");
80 self.timer_reload = true;
81 }
82 }
83
84 self.prev_timer_clock = timer_clock;
85 }
86
87 pub fn set_input(&mut self, ctx: &mut impl Context, input: &Input) {
88 let prev_lines = self.keypad_input_lines();
89 self.input = input.clone();
90 let cur_lines = self.keypad_input_lines();
91
92 for i in 0..4 {
93 if prev_lines[i] && !cur_lines[i] {
94 ctx.set_interrupt_flag_bit(INT_JOYPAD);
95 }
96 }
97 }
98
99 pub fn serial(&mut self) -> &mut SerialTransfer {
100 &mut self.serial
101 }
102
103 pub fn set_link_cable(&mut self, link_cable: Option<Box<dyn LinkCable + Send + Sync>>) {
104 self.serial.set_link_cable(link_cable);
105 }
106
107 fn keypad_input_lines(&self) -> [bool; 4] {
108 let mut lines = [true; 4];
109 let r = &self.input.pad;
110 if !self.select_action_buttons {
111 lines[0] &= !r.a;
112 lines[1] &= !r.b;
113 lines[2] &= !r.select;
114 lines[3] &= !r.start;
115 }
116 if !self.select_direction_buttons {
117 lines[0] &= !r.right;
118 lines[1] &= !r.left;
119 lines[2] &= !r.up;
120 lines[3] &= !r.down;
121 }
122 lines
123 }
124
125 pub fn read(&mut self, ctx: &mut impl Context, addr: u16) -> u8 {
126 let ret = match addr & 0xff {
127 0x00 => {
129 let lines = self.keypad_input_lines();
130 pack! {
131 6..=7 => !0,
132 5 => self.select_action_buttons,
133 4 => self.select_direction_buttons,
134 3 => lines[3],
135 2 => lines[2],
136 1 => lines[1],
137 0 => lines[0],
138 }
139 }
140 0x01 => self.serial.read_sb(),
142 0x02 => self.serial.read_sc(),
144 0x04 => (self.divider >> 8) as u8,
146 0x05 => self.timer_counter,
148 0x06 => self.timer_modulo,
150 0x07 => pack! {
152 3..=7 => !0,
153 2 => self.timer_enable,
154 0..=1 => self.input_clock_select,
155 },
156 0x0f => pack! {
158 5..=7 => !0,
159 0..=4 => ctx.interrupt_flag(),
160 },
161 0xff => pack! {
163 0..=7 => ctx.interrupt_enable(),
164 },
165
166 0x10..=0x3F | 0x76..=0x77 => ctx.apu_mut().read(addr),
168 0x40..=0x4F | 0x68..=0x6C => ctx.read_ppu(addr),
170
171 _ => {
172 warn!("Unknown I/O Read: {:04X}", addr);
173 !0
174 }
175 };
176
177 trace!("I/O Read: (${addr:04X}) => ${ret:02X}");
178 ret
179 }
180
181 pub fn write(&mut self, ctx: &mut impl Context, addr: u16, data: u8) {
182 trace!("I/O write: ${addr:04X} = ${data:02X}");
183
184 match addr & 0xff {
185 0x00 => {
187 let v = data.view_bits::<Lsb0>();
188 self.select_direction_buttons = v[4];
189 self.select_action_buttons = v[5];
190 }
191 0x01 => self.serial.write_sb(data),
193 0x02 => self.serial.write_sc(data),
195 0x04 => self.divider = 0,
197 0x05 => {
199 if self.timer_reload {
201 self.timer_reload = false;
202 }
203 if !self.timer_reloaded {
205 self.timer_counter = data;
206 }
207 }
208 0x06 => {
210 self.timer_modulo = data;
211 if self.timer_reloaded {
213 self.timer_counter = data;
214 }
215 }
216 0x07 => {
218 let v = data.view_bits::<Lsb0>();
219 self.timer_enable = v[2];
220 self.input_clock_select = v[0..=1].load();
221 }
222 0x0f => ctx.set_interrupt_flag(data & 0x1f),
224 0xff => {
226 trace!("IE = {data:02X}");
227 ctx.set_interrupt_enable(data)
228 }
229
230 0x10..=0x3F | 0x76..=0x77 => ctx.apu_mut().write(addr, data),
232 0x40..=0x4F | 0x68..=0x6C => ctx.write_ppu(addr, data),
234
235 _ => {
236 warn!("Write to ${:04X} = ${:02X}", addr, data);
237 }
238 }
239 }
240}