1use crate::{
2 AudioBatch, CartridgeError, ControllerState, CoreCommand, DebugSnapshot, NES, SaveStateError,
3 VideoFrame,
4};
5
6#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
7pub struct FrontendInput {
8 pub controller1: ControllerState,
9 pub controller2: ControllerState,
10 pub reset: bool,
11 pub toggle_pause: bool,
12 pub step_frame: bool,
13 pub step_cpu_instruction: bool,
14 pub quit: bool,
15}
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum RunMode {
19 Running,
20 Paused,
21}
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub enum ExecutionTarget {
25 None,
26 Frame,
27 CpuInstruction,
28}
29
30#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31pub struct RuntimeStatus {
32 pub mode: RunMode,
33 pub executed: ExecutionTarget,
34 pub quit_requested: bool,
35}
36
37#[derive(Clone, Copy, Debug)]
38pub struct RuntimeSnapshot<'a> {
39 pub status: RuntimeStatus,
40 pub video: VideoFrame<'a>,
41 pub audio: AudioBatch<'a>,
42 pub debug: DebugSnapshot,
43}
44
45pub struct FrontendRuntime {
46 nes: NES,
47 mode: RunMode,
48}
49
50impl FrontendRuntime {
51 pub fn new(nes: NES) -> Self {
52 Self {
53 nes,
54 mode: RunMode::Running,
55 }
56 }
57
58 pub fn from_rom_bytes(rom: &[u8]) -> Result<Self, CartridgeError> {
59 let mut nes = NES::new();
60 nes.load_cartridge_ines(rom)?;
61 nes.reset();
62 Ok(Self::new(nes))
63 }
64
65 pub fn mode(&self) -> RunMode {
66 self.mode
67 }
68
69 pub fn nes(&self) -> &NES {
70 &self.nes
71 }
72
73 pub fn nes_mut(&mut self) -> &mut NES {
74 &mut self.nes
75 }
76
77 pub fn save_state(&self) -> Result<Vec<u8>, SaveStateError> {
78 self.nes.save_state()
79 }
80
81 pub fn load_state(&mut self, bytes: &[u8]) -> Result<(), SaveStateError> {
82 self.nes.load_state(bytes)
83 }
84
85 pub fn set_mode(&mut self, mode: RunMode) {
86 self.mode = mode;
87 }
88
89 pub fn snapshot(&self) -> RuntimeSnapshot<'_> {
90 RuntimeSnapshot {
91 status: RuntimeStatus {
92 mode: self.mode,
93 executed: ExecutionTarget::None,
94 quit_requested: false,
95 },
96 video: self.nes.video_frame(),
97 audio: self.nes.audio_batch(),
98 debug: self.nes.debug_snapshot(),
99 }
100 }
101
102 pub fn step(&mut self, input: FrontendInput) -> RuntimeSnapshot<'_> {
103 self.nes.clear_audio_samples();
104 self.nes.execute(CoreCommand::SetControllerState {
105 port: 0,
106 state: input.controller1,
107 });
108 self.nes.execute(CoreCommand::SetControllerState {
109 port: 1,
110 state: input.controller2,
111 });
112
113 if input.reset {
114 self.nes.execute(CoreCommand::Reset);
115 }
116
117 if input.toggle_pause {
118 self.mode = match self.mode {
119 RunMode::Running => RunMode::Paused,
120 RunMode::Paused => RunMode::Running,
121 };
122 }
123
124 let executed = if input.step_cpu_instruction {
125 self.nes.execute(CoreCommand::StepCpuInstruction);
126 ExecutionTarget::CpuInstruction
127 } else if input.step_frame || matches!(self.mode, RunMode::Running) {
128 self.nes.execute(CoreCommand::RunFrame);
129 ExecutionTarget::Frame
130 } else {
131 ExecutionTarget::None
132 };
133
134 RuntimeSnapshot {
135 status: RuntimeStatus {
136 mode: self.mode,
137 executed,
138 quit_requested: input.quit,
139 },
140 video: self.nes.video_frame(),
141 audio: self.nes.audio_batch(),
142 debug: self.nes.debug_snapshot(),
143 }
144 }
145}