bus.rsannotatedbus.rssource142 lines · 4.8 KB · raw
1use crate::input::InputState;
2use crate::memory::{HuCard, Memory};
3use crate::psg::Huc6280Psg;
4use crate::video::VideoSubsystem;
5use huc6280_emu::bus::{BusInterface, ClockSpeed, InterruptLines};
6
7pub struct Bus<'a> {
8    pub memory: &'a mut Memory,
9    pub video: &'a mut VideoSubsystem,
10    pub psg: &'a mut Huc6280Psg,
11    pub cartridge: &'a mut HuCard,
12    pub input: &'a mut InputState,
13    pub cycle_counter: &'a mut u64,
14}
15
16impl Bus<'_> {
17    fn cpu_cycle(&mut self) {
18        *self.cycle_counter += self.memory.cpu_clock_divider();
19
20        // TODO it's really not necessary to sync everything at every CPU cycle
21        self.video.step_to(*self.cycle_counter, self.memory.cpu_registers().irq1_pending_mut());
22        self.memory.cpu_registers().step_timer_to(*self.cycle_counter);
23    }
24
25    #[allow(clippy::match_same_arms)]
26    fn read_io(&mut self, address: u32) -> u8 {
27        if address < 0x1FE800 {
28            // Wait cycle on VDC/VCE access
29            self.cpu_cycle();
30        }
31
32        match address & 0x1FFF {
33            0x0000..=0x03FF => self.video.read_vdc(
34                address,
35                self.cycle_counter,
36                self.memory.cpu_registers().irq1_pending_mut(),
37            ),
38            0x0400..=0x07FF => self.video.read_vce(address),
39            0x0800..=0x0BFF => self.memory.cpu_registers().io_buffer(), // PSG, write-only
40            0x0C00..=0x0FFF => self.memory.cpu_registers().read_timer_register(*self.cycle_counter),
41            0x1000..=0x13FF => {
42                let value = self.input.read_port();
43                self.memory.cpu_registers().update_io_buffer(value, !0)
44            }
45            0x1400..=0x17FF => self.memory.cpu_registers().read_interrupt_register(address),
46            0x1800..=0x1BFF => 0xFF, // CD-ROM
47            0x1C00..=0x1FFF => 0xFF, // Unused
48            _ => unreachable!("value & 0x1FFF is always <= 0x1FFF"),
49        }
50    }
51
52    #[allow(clippy::match_same_arms)]
53    fn write_io(&mut self, address: u32, value: u8) {
54        if address < 0x1FE800 {
55            // Wait cycle on VDC/VCE access
56            self.cpu_cycle();
57        }
58
59        match address & 0x1FFF {
60            0x0000..=0x03FF => self.video.write_vdc(
61                address,
62                value,
63                self.cycle_counter,
64                self.memory.cpu_registers().irq1_pending_mut(),
65            ),
66            0x0400..=0x07FF => self.video.write_vce(address, value),
67            0x0800..=0x0BFF => {
68                self.psg.step_to(*self.cycle_counter);
69                self.psg.write(address, value);
70                self.memory.cpu_registers().update_io_buffer(value, !0);
71            }
72            0x0C00..=0x0FFF => {
73                self.memory.cpu_registers().write_timer_register(
74                    address,
75                    value,
76                    *self.cycle_counter,
77                );
78            }
79            0x1000..=0x13FF => {
80                self.input.write_port(value);
81                self.memory.cpu_registers().update_io_buffer(value, !0);
82            }
83            0x1400..=0x17FF => {
84                self.memory.cpu_registers().write_interrupt_register(address, value);
85            }
86            0x1800..=0x1BFF => {} // CD-ROM
87            0x1C00..=0x1FFF => {} // Unused
88            _ => unreachable!("value & 0x1FFF is always <= 0x1FFF"),
89        }
90    }
91}
92
93impl BusInterface for Bus<'_> {
94    #[inline]
95    #[allow(clippy::match_same_arms)]
96    fn read(&mut self, address: u32) -> u8 {
97        debug_assert!(address < 0x200000);
98
99        self.cpu_cycle();
100
101        match address {
102            0x000000..=0x0FFFFF => self.cartridge.read(address),
103            0x100000..=0x1EFFFF => 0xFF, // CD-ROM
104            0x1F0000..=0x1F7FFF => self.memory.read_working_ram(address),
105            0x1F8000..=0x1FDFFF => 0xFF, // Unused memory
106            0x1FE000..=0x1FFFFF => self.read_io(address),
107            0x200000..=0xFFFFFFFF => panic!("invalid HuC6280 address {address:06X}"),
108        }
109    }
110
111    #[inline]
112    #[allow(clippy::match_same_arms)]
113    fn write(&mut self, address: u32, value: u8) {
114        debug_assert!(address < 0x200000);
115
116        self.cpu_cycle();
117
118        match address {
119            0x000000..=0x0FFFFF => self.cartridge.write(address, value),
120            0x100000..=0x1EFFFF => {} // CD-ROM
121            0x1F0000..=0x1F7FFF => self.memory.write_working_ram(address, value),
122            0x1F8000..=0x1FDFFF => {} // Unused memory
123            0x1FE000..=0x1FFFFF => self.write_io(address, value),
124            0x200000..=0xFFFFFFFF => panic!("invalid HuC6280 address {address:06X}"),
125        }
126    }
127
128    #[inline]
129    fn idle(&mut self) {
130        self.cpu_cycle();
131    }
132
133    #[inline]
134    fn interrupt_lines(&self) -> InterruptLines {
135        self.memory.interrupt_lines()
136    }
137
138    #[inline]
139    fn set_clock_speed(&mut self, speed: ClockSpeed) {
140        self.memory.set_clock_speed(speed);
141    }
142}