gte_core/
emulator.rs

1use alloc::boxed::Box;
2use alloc::vec;
3use alloc::vec::Vec;
4use gte_w65c02s::{System, W65C02S};
5use log::{debug, error, info, warn};
6use gte_w65c02s::State::AwaitingInterrupt;
7use core::fmt::{Debug, Formatter};
8use bytemuck::bytes_of;
9use heapless::{FnvIndexMap};
10use rtrb::PushError;
11use crate::audio_output::GameTankAudio;
12use crate::blitter::Blitter;
13use crate::cartridges::CartridgeType;
14use crate::emulator::PlayState::{Paused, Playing, WasmInit};
15use crate::gametank_bus::{AcpBus, Bus, CpuBus};
16use crate::inputs::{ControllerButton, InputCommand, KeyState};
17use crate::inputs::ControllerButton::{Down, Left, Right, Start, Up, A, B, C};
18use crate::inputs::InputCommand::{Controller1, Controller2, HardReset, PlayPause, SoftReset};
19use crate::inputs::KeyState::JustReleased;
20
21pub const WIDTH: u32 = 128;
22pub const HEIGHT: u32 = 128;
23
24#[derive(Copy, Clone, Debug, PartialEq)]
25pub enum PlayState {
26    WasmInit,
27    Paused,
28    Playing,
29}
30
31pub trait TimeDaemon {
32    fn get_now_ms(&self) -> f64;
33}
34
35pub struct Emulator<Clock: TimeDaemon> {
36    pub cpu_bus: CpuBus,
37    pub acp_bus: AcpBus,
38    pub cpu: W65C02S,
39    pub acp: W65C02S,
40
41    pub blitter: Blitter,
42
43    pub clock_cycles_to_vblank: i32,
44
45    pub last_emu_tick: f64,
46    pub cpu_ns_per_cycle: f64,
47    pub cpu_frequency_hz: f64,
48    pub last_render_time: f64,
49    pub audio_out: Option<GameTankAudio>,
50    pub target_sample_rate: f64,
51    pub play_state: PlayState,
52    pub wait_counter: u64,
53
54    pub input_state: FnvIndexMap<InputCommand, KeyState, 32>, // capacity of 32 entries
55
56    pub clock: Clock,
57}
58
59impl <Clock: TimeDaemon> Emulator<Clock> {
60    pub fn load_rom(&mut self, bytes: &[u8]) {
61        warn!("loading new rom from memory, size: {}", bytes.len());
62        self.cpu_bus.cartridge = CartridgeType::from_slice(bytes);
63        warn!(" - cartridge loaded from memory");
64        self.cpu.reset();
65        warn!(" - cpu reset");
66        self.acp.reset();
67        warn!(" - acp reset");
68        self.blitter.clear_irq_trigger();
69        warn!(" - blitter irq cleared");
70    }
71}
72
73impl <Clock: TimeDaemon> Debug for Emulator<Clock> {
74    fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
75        f.debug_struct("Emulator")
76            .field("cpu_bus", &self.cpu_bus)
77            .field("acp_bus", &self.acp_bus)
78            .field("cpu", &self.cpu)
79            .field("acp", &self.acp)
80            .field("blitter", &self.blitter)
81            .field("clock_cycles_to_vblank", &self.clock_cycles_to_vblank)
82            .field("last_emu_tick", &self.last_emu_tick);
83
84        Ok(())
85    }}
86
87
88impl <Clock: TimeDaemon> Emulator<Clock> {
89    pub fn wasm_init(&mut self) {
90        if self.play_state == WasmInit {
91            self.play_state = Playing;
92            self.last_emu_tick = self.clock.get_now_ms();
93            self.last_render_time = self.clock.get_now_ms();
94        }
95    }
96
97    pub fn init(clock: Clock, target_sample_rate: f64) -> Self {
98        let play_state = WasmInit;
99
100        let mut bus = CpuBus::default();
101        let mut cpu = W65C02S::new();
102        cpu.step(&mut bus); // take one initial step, to get through the reset vector
103        let acp = W65C02S::new();
104
105        let blitter = Blitter::default();
106
107        let last_cpu_tick_ms = clock.get_now_ms();
108        let cpu_frequency_hz = 3_579_545.0; // Precise frequency
109        let cpu_ns_per_cycle = 1_000_000_000.0 / cpu_frequency_hz; // Nanoseconds per cycle
110
111        let last_render_time = last_cpu_tick_ms;
112
113        Emulator {
114            play_state,
115            cpu_bus: bus,
116            acp_bus: AcpBus::default(),
117            cpu,
118            acp,
119            blitter,
120
121            clock_cycles_to_vblank: 59659,
122            last_emu_tick: last_cpu_tick_ms,
123            cpu_frequency_hz,
124            cpu_ns_per_cycle,
125            last_render_time,
126            audio_out: None,
127            target_sample_rate,
128            wait_counter: 0,
129            input_state: Default::default(),
130            clock,
131        }
132    }
133
134    pub fn process_cycles(&mut self, is_web: bool) {
135        self.process_inputs();
136
137        if self.play_state != Playing {
138            return
139        }
140
141        let now_ms = self.clock.get_now_ms();
142        let mut elapsed_ms = now_ms - self.last_emu_tick;
143
144        if elapsed_ms > 33.0 {
145            warn!("emulator took more than 33ms to process cycles");
146            elapsed_ms = 16.667;
147        }
148
149        let elapsed_ns = elapsed_ms * 1000000.0;
150        let mut remaining_cycles: i32 = (elapsed_ns / self.cpu_ns_per_cycle) as i32;
151
152        let mut acp_cycle_accumulator = 0;
153
154        while remaining_cycles > 0 {
155            if self.cpu.get_state() == AwaitingInterrupt {
156                self.wait_counter += 1;
157                // get cpu's current asm code
158            } else if self.wait_counter > 0 {
159                debug!("waited {} cycles", self.wait_counter);
160                self.wait_counter = 0;
161            }
162
163            let cpu_cycles = self.cpu.step(&mut self.cpu_bus);
164
165            remaining_cycles -= cpu_cycles;
166
167            acp_cycle_accumulator += cpu_cycles * 4;
168
169            // pass aram to acp
170            if self.cpu_bus.system_control.acp_enabled() {
171                self.run_acp(&mut acp_cycle_accumulator);
172            }
173
174            // blit
175            for _ in 0..cpu_cycles {
176                self.blitter.cycle(&mut self.cpu_bus);
177            }
178            // TODO: instant blit option
179
180            let blit_irq = self.blitter.irq_trigger;
181            if blit_irq {
182                debug!("blit irq");
183            }
184            self.cpu.set_irq(blit_irq);
185
186            self.clock_cycles_to_vblank -= cpu_cycles;
187            if self.clock_cycles_to_vblank <= 0 {
188                self.vblank();
189            }
190        }
191
192        self.last_emu_tick = now_ms;
193
194        if !is_web && (now_ms - self.last_render_time) >= 16.67 {
195            debug!("time since last render: {}", now_ms - self.last_render_time);
196            self.last_render_time = now_ms;
197        }
198    }
199
200    fn run_acp(&mut self, acp_cycle_accumulator: &mut i32) {
201        if self.cpu_bus.system_control.clear_acp_reset() {
202            self.acp.reset();
203        }
204
205        if self.cpu_bus.system_control.clear_acp_nmi() {
206            self.acp.set_nmi(true);
207        }
208
209        while *acp_cycle_accumulator > 0 {
210            let acp_cycles = self.acp.step(&mut self.acp_bus);
211            *acp_cycle_accumulator -= acp_cycles;
212            self.acp_bus.irq_counter -= acp_cycles;
213
214            // clear stuff ig
215            self.acp.set_irq(false);
216            self.acp.set_nmi(false);
217
218            if self.acp_bus.irq_counter <= 0 {
219                self.acp_bus.irq_counter = self.cpu_bus.system_control.sample_rate() as i32 * 4;
220                self.acp.set_irq(true);
221
222                let sample_rate = self.cpu_frequency_hz / self.cpu_bus.system_control.sample_rate() as f64;
223                // if audio_out is none or mismatched sample rate
224                if self.audio_out.as_ref().map_or(true, |gta| gta.sample_rate != sample_rate) {
225                    warn!("recreated audio stream with new sample rate: {:.3}Hz ({})", sample_rate, self.cpu_bus.system_control.sample_rate());
226                    self.audio_out = Some(GameTankAudio::new(sample_rate, self.target_sample_rate));
227                }
228
229                if let Some(audio) = &mut self.audio_out {
230                    let next_sample_u8 = self.acp_bus.sample;
231                    if let Err(e) = audio.producer.push(next_sample_u8) {
232                        error!("not enough slots in audio producer: {e}");
233                    }
234                }
235
236                if let Some(audio) = &mut self.audio_out {
237                    audio.convert_to_output_buffers();
238                    // audio.process_audio();
239                }
240            }
241        }
242    }
243
244    fn vblank(&mut self) {
245        self.clock_cycles_to_vblank += 59659;
246
247        if self.cpu_bus.vblank_nmi_enabled() {
248            self.cpu.set_nmi(true);
249            debug!("vblanked");
250        }
251    }
252
253    pub fn set_input_state(&mut self, input_command: InputCommand, state: KeyState) {
254        self.input_state.insert(input_command, state).expect("shit's full dog ://");
255    }
256
257    fn process_inputs(&mut self) {
258        let keys: Vec<_> = self.input_state.keys().cloned().collect();  // Clone keys to avoid borrowing conflicts
259
260        if keys.len() > 0 && self.play_state == WasmInit {
261            self.play_state = Playing;
262        }
263
264        for key in &keys {
265            match key {
266                Controller1(button) => { self.set_gamepad_input(0, &key, &button); }
267                Controller2(button) => { self.set_gamepad_input(1, &key, &button); }
268                PlayPause => {
269                    if self.input_state[key] == JustReleased {
270                        match self.play_state {
271                            Paused => { self.play_state = Playing; }
272                            Playing => { self.play_state = Paused; }
273                            WasmInit => { self.play_state = Playing; }
274                        }
275                    }
276                }
277                SoftReset => {
278                    self.cpu.reset();
279                }
280                HardReset => {
281                    // hard reset reinitializes memory/cpus
282                    let cart = self.cpu_bus.cartridge.clone();
283                    self.cpu_bus = CpuBus::default();
284                    self.cpu_bus.cartridge = cart;
285                    self.cpu = W65C02S::new();
286                    self.cpu.step(&mut self.cpu_bus); // take one initial step, to get through the reset vector
287                    self.acp = W65C02S::new();
288                    self.blitter = Blitter::default();
289                }
290            }
291            self.input_state.insert(*key, self.input_state[key].update()).expect("shit's full dog ://");
292        }
293    }
294    fn set_gamepad_input(&mut self, gamepad: usize, key: &InputCommand, button: &ControllerButton) {
295        let gamepad = &mut self.cpu_bus.system_control.gamepads[gamepad];
296        match button {
297            Up =>     { gamepad.up    = self.input_state[&key].is_pressed(); }
298            Down =>   { gamepad.down  = self.input_state[&key].is_pressed(); }
299            Left =>   { gamepad.left  = self.input_state[&key].is_pressed(); }
300            Right =>  { gamepad.right = self.input_state[&key].is_pressed(); }
301            B =>      { gamepad.b     = self.input_state[&key].is_pressed(); }
302            A =>      { gamepad.a     = self.input_state[&key].is_pressed(); }
303            Start =>  { gamepad.start = self.input_state[&key].is_pressed(); }
304            C =>      { gamepad.c     = self.input_state[&key].is_pressed(); }
305        }
306    }
307}