tgbr_core/
io.rs

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        // Counting on falling edge
75        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            // P1: Joypad (R/W)
128            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            // SB: Serial transfer data (R/W)
141            0x01 => self.serial.read_sb(),
142            // SC: Serial transfer control (R/W)
143            0x02 => self.serial.read_sc(),
144            // DIV: Divider register (R/W)
145            0x04 => (self.divider >> 8) as u8,
146            // TIMA: Timer counter (R/W)
147            0x05 => self.timer_counter,
148            // TMA: Timer modulo (R/W)
149            0x06 => self.timer_modulo,
150            // TAC: Timer control (R/W)
151            0x07 => pack! {
152                3..=7 => !0,
153                2     => self.timer_enable,
154                0..=1 => self.input_clock_select,
155            },
156            // IF: Interrupt flag (R/W)
157            0x0f => pack! {
158                5..=7 => !0,
159                0..=4 => ctx.interrupt_flag(),
160            },
161            // IE: Interrupt enable (R/W)
162            0xff => pack! {
163                0..=7 => ctx.interrupt_enable(),
164            },
165
166            // APU Registers
167            0x10..=0x3F | 0x76..=0x77 => ctx.apu_mut().read(addr),
168            // PPU Registers
169            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            // P1: Joypad (R/W)
186            0x00 => {
187                let v = data.view_bits::<Lsb0>();
188                self.select_direction_buttons = v[4];
189                self.select_action_buttons = v[5];
190            }
191            // SB: Serial transfer data (R/W)
192            0x01 => self.serial.write_sb(data),
193            // SC: Serial transfer control (R/W)
194            0x02 => self.serial.write_sc(data),
195            // DIV: Divider register (R/W)
196            0x04 => self.divider = 0,
197            // TIMA: Timer counter (R/W)
198            0x05 => {
199                // On the reload delay cycle, cancel reloading
200                if self.timer_reload {
201                    self.timer_reload = false;
202                }
203                // On the timer reloaded cycle, ignore writing to TIMA
204                if !self.timer_reloaded {
205                    self.timer_counter = data;
206                }
207            }
208            // TMA: Timer modulo (R/W)
209            0x06 => {
210                self.timer_modulo = data;
211                // On the timer reloaded cycle, this value is loaded into TIMA
212                if self.timer_reloaded {
213                    self.timer_counter = data;
214                }
215            }
216            // TAC: Timer control (R/W)
217            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            // IF: Interrupt flag (R/W)
223            0x0f => ctx.set_interrupt_flag(data & 0x1f),
224            // IE: Interrupt enable (R/W)
225            0xff => {
226                trace!("IE = {data:02X}");
227                ctx.set_interrupt_enable(data)
228            }
229
230            // APU Registers
231            0x10..=0x3F | 0x76..=0x77 => ctx.apu_mut().write(addr, data),
232            // PPU Registers
233            0x40..=0x4F | 0x68..=0x6C => ctx.write_ppu(addr, data),
234
235            _ => {
236                warn!("Write to ${:04X} = ${:02X}", addr, data);
237            }
238        }
239    }
240}