spectrusty/chip/
ay_player.rs

1/*
2    Copyright (C) 2020-2022  Rafal Michalski
3
4    This file is part of SPECTRUSTY, a Rust library for building emulators.
5
6    For the full copyright notice, see the lib.rs file.
7*/
8//! An emulator of a video-less chip for ZX Spectrum to be used in a music player.
9use core::num::NonZeroU16;
10use core::marker::PhantomData;
11use core::num::Wrapping;
12
13use crate::z80emu::{
14    Cpu, Clock, Io, Memory, CpuDebug, CpuDebugFn, BreakCause,
15    opconsts,
16    host::{TsCounter, Result, cycles::M1_CYCLE_TS}
17};
18#[cfg(feature = "snapshot")]
19use serde::{Serialize, Deserialize};
20
21use crate::audio::*;
22use crate::peripherals::ay::{audio::*, Ay3_8913Io, AyPortDecode, AyRegRecorder};
23use crate::clock::{FTs, FTsData2};
24use crate::memory::{ZxMemory, Memory64k};
25use crate::bus::{BusDevice, NullDevice};
26use crate::chip::{
27    FrameState, ControlUnit, ZxSpectrum128Config, HostConfig,
28    nanos_from_frame_tc_cpu_hz
29};
30
31#[derive(Clone)]
32#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
33#[cfg_attr(feature = "snapshot", serde(rename_all = "camelCase"))]
34pub struct AyPlayer<P> {
35    pub frames: Wrapping<u64>,
36    pub tsc: TsCounter<FTs>,
37    pub memory: Memory64k,
38    pub ay_sound: Ay3_891xAudio,
39    pub ay_io: Ay3_8913Io<FTs>,
40    #[cfg_attr(feature = "snapshot", serde(skip))]
41    pub earmic_changes: Vec<FTsData2>,
42    pub last_earmic: u8,
43    pub prev_earmic: u8,
44        cpu_rate: u32,
45        frame_tstates: FTs,
46        bus: NullDevice<FTs>,
47    #[cfg_attr(feature = "snapshot", serde(skip))]
48        _port_decode: PhantomData<P>
49}
50
51impl<P> Default for AyPlayer<P> {
52    fn default() -> Self {
53        AyPlayer {
54            frames: Wrapping(0),
55            tsc: TsCounter::default(),
56            memory: Memory64k::default(),
57            ay_sound: Ay3_891xAudio::default(),
58            ay_io: Ay3_8913Io::default(),
59            earmic_changes: Vec::new(),
60            last_earmic: 3,
61            prev_earmic: 3,
62            cpu_rate: ZxSpectrum128Config::CPU_HZ,
63            frame_tstates: ZxSpectrum128Config::FRAME_TSTATES,
64            bus: NullDevice::default(),
65            _port_decode: PhantomData
66        }
67    }
68}
69
70impl<P, A: Blep> AudioFrame<A> for AyPlayer<P> {
71    fn ensure_audio_frame_time(&self, blep: &mut A, sample_rate: u32, cpu_hz: f64) {
72        blep.ensure_frame_time(sample_rate, cpu_hz, self.frame_tstates, MARGIN_TSTATES)
73    }
74
75    fn get_audio_frame_end_time(&self) -> FTs {
76        let ts = self.tsc.as_timestamp();
77        assert!(ts >= self.frame_tstates, "AyPlayer::get_audio_frame_end_time:: frame execution didn't finish yet: {} < {}", ts, self.frame_tstates);
78        ts
79    }
80}
81
82impl<P, A> AyAudioFrame<A> for AyPlayer<P>
83    where A: Blep
84{
85    fn render_ay_audio_frame<V: AmpLevels<A::SampleDelta>>(&mut self, blep: &mut A, chans: [usize; 3]) {
86        let end_ts = self.tsc.as_timestamp();
87        let changes = self.ay_io.recorder.drain_ay_reg_changes();
88        self.ay_sound.render_audio::<V,_,A>(changes, blep, end_ts, self.frame_tstates, chans)
89    }
90}
91
92impl<P, A, L> EarMicOutAudioFrame<A> for AyPlayer<P>
93    where A: Blep<SampleDelta=L>,
94          L: SampleDelta
95{
96    #[inline(always)]
97    fn render_earmic_out_audio_frame<V: AmpLevels<L>>(&self, blep: &mut A, channel: usize) {
98        render_audio_frame_ts::<V,L,A,_>(self.prev_earmic,
99                                         None,
100                                         &self.earmic_changes,
101                                         blep, channel)
102    }
103}
104
105impl<P: AyPortDecode> FrameState for AyPlayer<P> {
106    fn current_frame(&self) -> u64 {
107        self.frames.0
108    }
109
110    fn set_frame_counter(&mut self, fc: u64) {
111        self.frames = Wrapping(fc);
112    }
113
114    fn frame_tstate(&self) -> (u64, FTs) {
115        let mut frames = self.frames;
116        let ts = self.tsc.as_timestamp();
117        let ts_norm = ts.rem_euclid(self.frame_tstates);
118        if ts_norm != ts {
119            frames = if ts < 0 {
120                frames - Wrapping(1)
121            }
122            else {
123                frames + Wrapping(1)
124            }
125        }
126        (frames.0, ts_norm)
127    }
128
129    fn current_tstate(&self) -> FTs {
130        self.tsc.as_timestamp()
131    }
132
133    fn set_frame_tstate(&mut self, ts: FTs) {
134        *self.tsc = Wrapping(ts);
135    }
136
137    fn is_frame_over(&self) -> bool {
138        self.tsc.as_timestamp() >= self.frame_tstates
139    }
140}
141
142impl<P: AyPortDecode> ControlUnit for AyPlayer<P> {
143    type BusDevice = NullDevice<FTs>;
144
145    fn bus_device_mut(&mut self) -> &mut Self::BusDevice {
146        &mut self.bus
147    }
148
149    fn bus_device_ref(&self) -> &Self::BusDevice {
150        &self.bus
151    }
152
153    fn into_bus_device(self) -> Self::BusDevice {
154        self.bus
155    }
156
157    fn reset<C: Cpu>(&mut self, cpu: &mut C, hard: bool) {
158        if hard {
159            cpu.reset();
160            self.ay_sound.reset();
161            let ts = self.tsc.as_timestamp();
162            self.ay_io.reset(ts);
163            self.bus.reset(ts);
164        }
165        else {
166            self.execute_instruction(cpu, opconsts::RST_00H_OPCODE).unwrap();
167        }
168    }
169
170    fn nmi<C: Cpu>(&mut self, cpu: &mut C) -> bool {
171        let mut tsc = self.ensure_next_frame_tsc();
172        let res = cpu.nmi(self, &mut tsc);
173        self.tsc = tsc;
174        res
175    }
176
177    fn execute_next_frame<C: Cpu>(&mut self, cpu: &mut C) {
178        let mut tsc = self.ensure_next_frame_tsc();
179        loop {
180            match cpu.execute_with_limit(self, &mut tsc, self.frame_tstates) {
181                Ok(()) => break,
182                Err(BreakCause::Halt) => {
183                    assert_eq!(0, self.frame_tstates.rem_euclid(M1_CYCLE_TS as FTs));
184                    let ts = self.frame_tstates + tsc.as_timestamp()
185                                                     .rem_euclid(M1_CYCLE_TS as FTs);
186                    let r_incr = (ts - tsc.as_timestamp()) / M1_CYCLE_TS as i32;
187                    cpu.add_r(r_incr);
188                    tsc.0 = Wrapping(ts);
189                    break;
190                }
191                Err(_) => {}
192            }
193        }
194        self.bus.update_timestamp(tsc.as_timestamp());
195        self.tsc = tsc;
196    }
197
198    fn ensure_next_frame(&mut self) {
199        self.ensure_next_frame_tsc();
200    }
201
202    fn execute_single_step<C: Cpu, F>(&mut self,
203                cpu: &mut C,
204                debug: Option<F>
205            ) -> Result<(), ()>
206        where F: FnOnce(CpuDebug)
207    {
208        let mut tsc = self.ensure_next_frame_tsc();
209        let res = cpu.execute_next(self, &mut tsc, debug);
210        self.tsc = tsc;
211        res
212    }
213}
214
215impl<P: AyPortDecode> AyPlayer<P> {
216    pub fn cpu_clock_rate(&self) -> u32 {
217        self.cpu_rate
218    }
219
220    pub fn frame_cycle_count(&self) -> FTs {
221        self.frame_tstates
222    }
223
224    pub fn frame_duration_nanos(&self) -> u32 {
225        nanos_from_frame_tc_cpu_hz(self.frame_tstates as u32, self.cpu_rate) as u32
226    }
227
228    pub fn ensure_audio_frame_time<B: Blep>(&self, blep: &mut B, sample_rate: u32) {
229        <Self as AudioFrame<B>>::ensure_audio_frame_time(self, blep, sample_rate, self.cpu_rate as f64)
230    }
231
232    fn ensure_next_frame_tsc(&mut self) -> TsCounter<FTs> {
233        let ts = self.tsc.as_timestamp();
234        if ts >= self.frame_tstates {
235            self.bus.next_frame(self.frame_tstates);
236            self.frames += Wrapping(1);
237            self.ay_io.recorder.clear();
238            self.earmic_changes.clear();
239            self.prev_earmic = self.last_earmic;
240            self.tsc.0 = Wrapping(ts) - Wrapping(self.frame_tstates);
241        }
242        self.tsc
243    }
244
245    fn execute_instruction<C: Cpu>(&mut self, cpu: &mut C, code: u8) -> Result<(), ()> {
246        const DEBUG: Option<CpuDebugFn> = None;
247        let mut tsc = self.ensure_next_frame_tsc();
248        let res = cpu.execute_instruction(self, &mut tsc, DEBUG, code);
249        self.tsc = tsc;
250        res
251    }
252    /// Changes the cpu clock frequency and the duration of frames.
253    pub fn set_host_config<H: HostConfig>(&mut self) {
254        self.ensure_next_frame();
255        self.cpu_rate = H::CPU_HZ;
256        self.frame_tstates = H::FRAME_TSTATES;
257    }
258    /// Changes the cpu clock frequency and the duration of frames.
259    pub fn set_config(&mut self, cpu_rate: u32, frame_tstates: FTs) {
260        assert!(frame_tstates > 0 && frame_tstates as u32 <= cpu_rate);
261        self.ensure_next_frame();
262        self.cpu_rate = cpu_rate;
263        self.frame_tstates = frame_tstates;
264    }
265    /// Resets the frames counter.
266    pub fn reset_frames(&mut self) {
267        self.frames.0 = 0;
268    }
269    /// Writes data directly into the AY registers, recording changes and advancing the clock counter.
270    ///
271    /// Sets the clock counter to `timestamp`.
272    ///
273    /// # Panics
274    /// The `timestamp` must be larger than or equal to the current clock counter value.
275    pub fn write_ay(&mut self, timestamp: FTs, reg: u8, val: u8) {
276        assert!(timestamp >= self.tsc.as_timestamp());
277        *self.tsc = Wrapping(timestamp);
278        let reg = reg.into();
279        self.ay_io.set(reg, val);
280        self.ay_io.recorder.record_ay_reg_change(reg, val, timestamp);
281    }
282}
283
284impl<P> Io for AyPlayer<P> where P: AyPortDecode {
285    type Timestamp = FTs;
286    type WrIoBreak = ();
287    type RetiBreak = ();
288
289    #[inline(always)]
290    fn is_irq(&mut self, ts: FTs) -> bool {
291        ts & !31 == 0
292    }
293
294    fn read_io(&mut self, port: u16, ts: FTs) -> (u8, Option<NonZeroU16>) {
295        let val = if P::is_data_read(port) {
296            self.ay_io.data_port_read(port, ts)
297        }
298        else {
299            0xff
300        };
301        (val, None)
302    }
303
304    fn write_io(&mut self, port: u16, data: u8, ts: FTs) -> (Option<()>, Option<NonZeroU16>) {
305        if port & 1 == 0 {
306            let earmic = data >> 3 & 3;
307            if self.last_earmic != earmic {
308                self.last_earmic = earmic;
309                self.earmic_changes.push((ts, earmic).into());
310            }
311        }
312        else {
313            P::write_ay_io(&mut self.ay_io, port, data, ts);
314        }
315        (None, None)
316    }
317}
318
319impl<P> Memory for AyPlayer<P> {
320    type Timestamp = FTs;
321
322    #[inline]
323    fn read_debug(&self, addr: u16) -> u8 {
324        self.memory.read(addr)
325    }
326
327    #[inline]
328    fn read_mem(&self, addr: u16, _ts: FTs) -> u8 {
329        self.memory.read(addr)
330    }
331
332    #[inline]
333    fn read_mem16(&self, addr: u16, _ts: FTs) -> u16 {
334        self.memory.read16(addr)
335    }
336
337    #[inline]
338    fn read_opcode(&mut self, pc: u16, _ir: u16, _ts: FTs) -> u8 {
339        self.memory.read(pc)
340    }
341
342    #[inline]
343    fn write_mem(&mut self, addr: u16, val: u8, _ts: FTs) {
344        self.memory.write(addr, val);
345    }
346}