1use std::io::Write;
9use std::time::{Duration, Instant};
10use std::thread::{spawn, sleep, JoinHandle};
11use std::sync::mpsc::{SyncSender, Receiver, TryRecvError};
12use log::{debug};
13use super::*;
14use super::debug::*;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub enum RunnerMsg {
19 Terminate,
21 Reset,
23 Nmi,
25 DebugNext,
27 DebugCompletion,
29 DebugRunIrq,
31 Continue
33}
34
35impl PioStream for Receiver<u8> {
36 fn slurp(&mut self) -> Option<u8> {
38 self.try_recv().ok()
39 }
40}
41
42impl PioSink for SyncSender<u8> {
43 fn flush(&mut self, data: u8) -> bool {
45 self.try_send(data).is_ok()
46 }
47}
48
49
50impl<F: Flavour, const EXT_HZ: u32, const FRAME_HZ: u32>
52 Ral1243<F, Receiver<u8>, SyncSender<u8>, EXT_HZ, FRAME_HZ>
53{
54 pub fn start_thread<T>(
60 run_rx: Receiver<RunnerMsg>,
61 ramsizekb: usize,
62 clock_hz: Ts,
63 exroms: Option<T>,
64 pio_stream: Receiver<u8>,
65 pio_sink: SyncSender<u8>
66 ) -> JoinHandle<()>
67 where T: IntoIterator<Item=Rom> + Send + 'static,
68 T::IntoIter: ExactSizeIterator
69 {
70 thread::spawn(move || {
71 let mut computer = Self::new(ramsizekb, clock_hz, exroms, pio_stream, pio_sink);
72 computer.run(run_rx);
73 })
74 }
75
76 fn run(&mut self, run_rx: Receiver<RunnerMsg>) {
77 let frame_duration: Duration = FrameRunner::<EXT_HZ, FRAME_HZ>::frame_duration();
78
79 let mut nmi_request = false;
80
81 let mut time = Instant::now();
82
83 let mut total_ts = 0u64;
84 let mut duration = Duration::ZERO;
85 let mut frame_count = 0;
86
87 loop {
88 if nmi_request && self.nmi().is_some() {
89 nmi_request = false;
90 }
91
92 let mtime = Instant::now();
93 let delta_ts = self.step();
94 let elapsed = mtime.elapsed();
95
96 duration += elapsed;
97 total_ts += u64::from(delta_ts);
98
99 match run_rx.try_recv() {
100 Ok(RunnerMsg::Terminate) => break,
101 Ok(RunnerMsg::Reset) => {
102 nmi_request = false;
103 self.reset();
104 }
105 Ok(RunnerMsg::Nmi) => {
106 nmi_request = self.nmi().is_none();
107 }
108 Ok(RunnerMsg::DebugNext|
109 RunnerMsg::DebugRunIrq|
110 RunnerMsg::DebugCompletion) => {
111 if self.debug(&run_rx, &mut nmi_request) == RunnerMsg::Terminate {
112 break
113 }
114 duration = Duration::ZERO;
115 total_ts = 0;
116 frame_count = 0;
117 time = Instant::now();
118 continue
119 }
120 Ok(RunnerMsg::Continue)|
121 Err(TryRecvError::Empty) => {},
122 Err(TryRecvError::Disconnected) => break,
123 }
124
125 if let Some(duration) = frame_duration.checked_sub(time.elapsed()) {
126 thread::sleep(duration);
127 }
128 time += frame_duration;
129
130 frame_count += 1;
131 if frame_count == FRAME_HZ * 5 {
132 debug!("emulation max {:.4} MHz", total_ts as f64 / duration.as_secs_f64() / 1e6);
133 duration = Duration::ZERO;
134 total_ts = 0;
135 frame_count = 0;
136 }
137 }
138 }
139
140 fn run_to_completion<P>(&mut self, print_debug: P) -> (Option<CpuDebug>, Ts)
142 where P: FnOnce(&CpuDebug, &Z80<F>)
143 {
144 let (dbg, ts) = self.debug_step();
145 if let Some(deb) = dbg.as_ref() {
146 if was_just_a_ret(deb, &self.cpu) {
147 return (dbg, ts);
148 }
149 else if was_just_a_call(deb, &self.cpu) {
150 print_debug(deb, &self.cpu);
151 let pc = deb.pc.wrapping_add(deb.code.len() as u16);
152 let (_, ts) = run_for(ts, FRAME_HZ, || self.run_until_brkpt(core::slice::from_ref(&pc)));
153 return (None, ts);
154 }
155 }
156 run_for(ts, FRAME_HZ, || self.debug_runto_ret())
157 }
158
159 fn debug(&mut self, run_rx: &Receiver<RunnerMsg>, nmi_request: &mut bool) -> RunnerMsg {
160 let stdout = io::stdout();
161 let mut header_lines = u8::MAX;
162 let mut last_ts = 0;
163
164 let mut print_debug = |deb: &_, cpu: &_| {
165 let mut handle = stdout.lock();
166 if header_lines > 10 {
167 let _ = writeln!(handle, "{}", Header);
168 header_lines = 0;
169 }
170 else {
171 header_lines += 1;
172 }
173 let _ = writeln!(handle, "{}", Debugger::of(deb, cpu));
174 };
175
176 loop {
177 if *nmi_request {
178 if let Some(ts) = self.nmi() {
179 last_ts += ts;
180 *nmi_request = false;
181 }
182 }
183 {
185 let deb = self.debug_preview();
186 let mut handle = stdout.lock();
187 let _ = write!(handle, "{}T: +{} \r", Preview::of(&deb), last_ts);
188 handle.flush().unwrap();
189 }
190 let (deb, ts) = match run_rx.recv() {
192 Ok(RunnerMsg::Continue) => return RunnerMsg::Continue,
193 Ok(RunnerMsg::Terminate)|
194 Err(..) => return RunnerMsg::Terminate,
195 Ok(RunnerMsg::Reset) => {
196 *nmi_request = false;
197 self.reset();
198 continue
199 }
200 Ok(RunnerMsg::Nmi) => {
201 *nmi_request = true;
202 continue
203 }
204 Ok(RunnerMsg::DebugCompletion) => self.run_to_completion(&mut print_debug),
205 Ok(RunnerMsg::DebugRunIrq) => run_for(0, FRAME_HZ, || self.debug_runto_int()),
206 Ok(RunnerMsg::DebugNext) => self.debug_step()
207 };
208
209 last_ts = ts;
210
211 if let Some(deb) = deb {
212 print_debug(&deb, &self.cpu);
213 }
214 }
215 }
216}
217
218fn run_for<D>(
221 mut start_ts: Ts,
222 mut frames: u32,
223 mut debug: impl FnMut() -> (Option<D>, Ts)
224 ) -> (Option<D>, Ts)
225{
226 loop {
227 match debug() {
228 (Some(deb), ts) => break (Some(deb), start_ts + ts),
229 (None, ts) => {
230 start_ts += ts;
231 if frames == 0 {
232 break (None, start_ts)
233 }
234 else {
235 frames -= 1;
236 }
237 }
238 }
239 }
240}