z80emu/
host.rs

1/*
2    z80emu: ZiLOG Z80 microprocessor emulation library.
3    Copyright (C) 2019-2024  Rafal Michalski
4
5    For the full copyright notice, see the lib.rs file.
6*/
7/*! This module contains traits that should be implemented by the builder of the host computer.
8
9To complete the emulation of the Z80-based computer the following trait interfaces need to be implemented:
10
11* [Clock] - to determine how the CPU cycles are being counted and when certain events take place.
12* [Io] - to establish a communication with the I/O devices.
13* [Memory] - to build a bridge between the CPU and the host's memory.
14
15When the [Cpu][crate::cpu::Cpu] is executing instructions, it calls [Clock]'s methods to increase the cycle counter,
16only hinting which cycle the currently executed instruction is at.
17It is however up to the [Clock] implementation to actually increase the counter. Some of the [Clock]'s methods return
18timestamps which in turn are being used when accessing peripherals or memory to mark certain actions on the time axis.
19
20Please see [crate::host::cycles] module for the description of each emulated cycle.
21*/
22#[cfg(feature = "std")] use std::error;
23#[cfg(feature = "serde")] use serde::{Serialize, Deserialize};
24use core::fmt;
25#[allow(unused_imports)]
26use core::num::{NonZeroU8, NonZeroU16, Wrapping};
27use core::ops::{Add, AddAssign, Deref, DerefMut};
28
29use super::opconsts::RST_38H_OPCODE;
30
31/** This module defines constants indicating the number of T-states of each of the emulated [Cpu] cycle,
32to help [Clock] implementations count those cycles.
33
34Below are the diagrams showing timings for all CPU cycles. The diagrams include the information when the
35[Io::is_irq] is being called, compared with the moment when the `INT` line is being sampled by the real CPU.
36
37# M1 (opcode fetch)
38
39M1 is indicated by a call to [Clock::add_m1]. The function should return a timestamp that is being passed
40to the [Memory::read_opcode] function and should increase the internal counter by at least [M1_CYCLE_TS].
41```text
42       T1  T2  T3  T4
43       _   _   _   _   _
44      | |_| |_| |_| |_| |_|
45      0               ^ M1_CYCLE_TS
46A0-15 |---pc--|---ir--|
47D0-7      -op-|
48MREQ  --_______--___----
49RD    --______----------
50M1    -________---------
51RFSH  ----------_______-
52WAIT  ......--..........
53      <===============> Memory::read_opcode
54                      ^ Io::is_irq ¹
55INT   ............___.. ¹
56```
57
58# Memory Read or Write.
59
60This cycle is indicated by a call to [Clock::add_mreq]. The function should return a timestamp that is
61being passed to one of: [Memory::read_mem], [Memory::read_mem16] or [Memory::write_mem] functions and should
62increase the internal counter by at least [MEMRW_CYCLE_TS].
63```text
64       T1  T2  T3
65       _   _   _   _  
66      | |_| |_| |_| |_|
67      0           ^ MEMRW_CYCLE_TS
68A0-15 |--address--|
69D0-7      ---data-|
70MREQ  --_________--
71RD    --_________-- (read)
72WR    ------_____-- (write)
73WAIT  .....--......
74      <===========> Memory::read_mem/write_mem
75                  ^ Io::is_irq ¹
76INT   ........___.. ¹
77```
78
79# Input/Output.
80
81This cycle is indicated by a call to [Clock::add_io]. The function should return a timestamp that is
82being passed to one of: [Io::read_io] or [Io::write_io] functions and should increase the internal counter by
83at least [IO_CYCLE_TS].
84The aforementioned [Io] methods can in turn insert an additional number of wait states (visible here as `TW`)
85into this cycle.
86```text
87       T1  T2  TW  T3
88       _   _   _   _   _   
89      | |_| |_| |_| |_| |_|
90      0               ^ IO_CYCLE_TS
91          ^ IO_IORQ_LOW_TS
92A0-15 |-----port------|
93D0-7      |----data---|
94IORQ  -----__________--
95RD    -----__________-- (read)
96WR    -----__________-- (write)
97WAIT  .........__......
98      <===============> Io::read_io/write_io
99                      ^ Io::is_irq ¹
100INT   ............___.. ¹
101```
102
103# Interrupt Request/Acknowledge.
104
105This cycle is indicated by a call to [Clock::add_irq]. The function should return a timestamp that is
106being passed to the [Io::irq_data] function and should increase the internal counter by at least [IRQ_ACK_CYCLE_TS].
107The [Io::irq_data] method can in turn insert an additional number of wait states (visible here as `TW`)
108into this cycle.
109```text
110       T1  T2  TW  TW  T3  T4
111       _   _   _   _   _   _
112      | |_| |_| |_| |_| |_| |_|
113      0                       ^ IRQ_ACK_CYCLE_TS
114              ^ INT_IORQ_LOW_TS
115A0-15 |------pc-------|---ir--|
116D0-7          |--data-|
117M1    -________________--------
118MREQ  -------------------_____-
119IORQ  -----------______--------
120WAIT  .............--..........
121      <=======================> Io::irq_data
122```
123
124# RETI
125
126This is not a cycle but rather a special case for an instruction which is being used by the Z80 peripherals
127to detect the end of the interrupt service routine. The currently active device in the daisy chain, with
128the `IEI` line high, can deactivate its `IEO` line and let the device with lower priority take control over
129the `INT` line. The diagram shows when the [Io::reti] is being called while executing the `RETI` instruction.
130```text
131       T1  T2  T3  T4  T1  T2  T3  T4  T1  T2  T3  T1  T2  T3
132       _   _   _   _   _   _   _   _   _   _   _   _   _   _
133      | |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_|
134Clock |>>> add_m1 >>>>|>>> add_m1 >>>>|> add_mreq |> add_mreq |
135A0-15 |---pc--|---ir--|--pc+1-|---ir--|-----sp----|----sp+1---|
136D0-7      -ED-|       |   -4D-|           ----lo--|   ----hi--|
137                                      ^ Io::reti(pc+2)        ^ Io::is_irq
138INT   ....................................................___..
139```
140¹ The INT line is being probed with the rising edge of the final clock at the end of every instruction
141when the interrupts are enabled, except after the `EI` instruction and prefixes: `0xDD`, `0xFD`.
142
143 [Cpu]: crate::cpu::Cpu
144**/
145pub mod cycles {
146    /// The minimum number of T-states for an `M1` cycle: opcode fetch, non-maskable interrupt and a `HALT` cycle.
147    pub const M1_CYCLE_TS: u8 = 4;
148    /// The minimum number of T-states for a memory read or write cycle.
149    pub const MEMRW_CYCLE_TS: u8 = 3;
150    /// The minimum number of T-states for an `I/O` cycle before the `IORQ` goes low
151    /// and the earliest moment the value might be available on the bus.
152    pub const IO_IORQ_LOW_TS: u8 = 1;
153    /// The minimum number of T-states for an `I/O` cycle.
154    pub const IO_CYCLE_TS: u8 = 4;
155    /// The minimum number of T-states in a maskable interrupt request/acknowledge cycle before the `IORQ` goes low
156    /// and the earliest moment the value might be put on the bus.
157    pub const INT_IORQ_LOW_TS: u8 = 2;
158    /// The number of T-states in a maskable interrupt request/acknowledge cycle.
159    pub const IRQ_ACK_CYCLE_TS: u8 = 6;
160}
161
162use cycles::*;
163
164/// This trait defines an interface to the system clock from the [Cpu] emulation perspective.
165///
166/// An implementation of this trait is responsible for counting T-states during various [Cpu] cycles.
167///
168/// It is however up to the implementation to determine how the counter is being represented and updated.
169///
170/// This trait can be used to emulate the [Cpu] contention by increasing the counter more than the required
171/// [number of T-states][cycles].
172///
173/// [Cpu]: crate::cpu::Cpu
174pub trait Clock {
175    /// This type is being used for an arbitrary representation of the `limit` argument when executing
176    /// instructions. See [Cpu::execute_with_limit](crate::cpu::Cpu::execute_with_limit) for an explanation.
177    type Limit: Sized + Copy;
178    /// This type is being used for timestamping the interactions between [Cpu], [Io] and [Memory]
179    /// implementations.
180    ///
181    /// The implementations of `Clock`, [Io][Io::Timestamp] and [Memory][Memory::Timestamp] should
182    /// have the same exact type assigned as their `Timestamp` associated type in order for the
183    /// [Cpu] to execute instructions.
184    ///
185    /// Values of this type are returned by some of the methods in this trait.
186    ///
187    /// [Cpu]: crate::cpu::Cpu
188    type Timestamp: Sized;
189    /// Should return `true` if the [Clock] has reached the given `limit` otherwise should return `false`.
190    fn is_past_limit(&self, limit: Self::Limit) -> bool;
191    /// This method should increase the counter by at least [IRQ_ACK_CYCLE_TS] `6` T-states.
192    /// The method should return the timestamp that may be passed to [Io::irq_data].
193    /// It's being used at the beginning of the maskable interrupt request/acknowledge cycle.
194    /// The `pc` is a value of the program counter when the interrupt was accepted.
195    fn add_irq(&mut self, pc: u16) -> Self::Timestamp;
196    /// This method should increase the counter by at least the value given in `add_ts`.
197    /// It's being used by internal operations of the [Cpu](crate::cpu::Cpu) without any external access.
198    /// The `address` given here is whatever was put on the address bus before.
199    // fn add_no_mreq<const ADD_TS: u8>(&mut self, address: u16);
200    fn add_no_mreq(&mut self, address: u16, add_ts: NonZeroU8);
201    /// This method should increase the counter by at least [M1_CYCLE_TS] `4`
202    /// and should return the timestamp that may be passed to [Memory::read_opcode].
203    /// This method is also being used when the non-maskable interrupt is being accepted and while the
204    /// `Cpu` is wasting cycles in the `halted` state.
205    fn add_m1(&mut self, address: u16) -> Self::Timestamp;
206    /// This method should increase the counter by at least the value given in [MEMRW_CYCLE_TS] `3`
207    /// and should return the timestamp that may be passed to [Memory::read_mem],
208    //  [Memory::read_mem16] or [Memory::write_mem].
209    fn add_mreq(&mut self, address: u16) -> Self::Timestamp;
210    /// This method should increase the counter by at least [IO_CYCLE_TS] `4` T-states
211    /// and should return the timestamp that may be passed to [Io::read_io] or [Io::write_io].
212    fn add_io(&mut self, port: u16) -> Self::Timestamp;
213    /// This method should increase the counter by the value given in `wait_states`.
214    /// A call to one of [Io::read_io], [Io::write_io] or [Io::irq_data] may request such additional
215    /// number of wait states to be added.
216    fn add_wait_states(&mut self, bus: u16, wait_states: NonZeroU16);
217    /// Should return the current state of the [Clock] as a timestamp.
218    fn as_timestamp(&self) -> Self::Timestamp;
219}
220
221/// This trait defines an interface for handling data provided to `IN` or from `OUT` instruction
222/// families and maskable interrupts.
223///
224/// Please see also [cycles].
225#[allow(unused_variables)]
226#[allow(clippy::wrong_self_convention)]
227pub trait Io {
228    /// This type is being used for timestamping I/O operations. See also [Clock::Timestamp].
229    type Timestamp: Sized;
230    /// A value of this type is returned when a break is being requested by [Io::write_io].
231    type WrIoBreak;
232    /// A value of this type is returned when a break is being requested by [Io::reti].
233    type RetiBreak;
234    /// Called during the `IO_IORQ` cycle when executing one of the `IN` instructions.
235    ///
236    /// Should return a single byte from the emulated device at the given `port` and the optional
237    /// number of wait states to be inserted during the `IO_IORQ` cycle.
238    ///
239    /// The `timestamp` given here has been previously returned from [Clock::add_io].
240    ///
241    /// This method is being used by the [Cpu](crate::cpu::Cpu) to read data from the I/O port.
242    ///
243    /// The default implementation returns `(u8::MAX, None)`.
244    fn read_io(&mut self, port: u16, timestamp: Self::Timestamp) -> (u8, Option<NonZeroU16>) {
245        (u8::max_value(), None)
246    }
247    /// Called during the `IO_IORQ` cycle when executing one of the `OUT` instructions.
248    ///
249    /// Should write a single `data` byte to the device at the given `port`.
250    ///
251    /// The `timestamp` given here has been previously returned from [Clock::add_io].
252    ///
253    /// Returning `Some(Self::WrIoBreak)` from this method is a request to break the execution after
254    /// the currently executed instruction completes.
255    /// See [Cpu::execute_with_limit](crate::cpu::Cpu::execute_with_limit).
256    ///
257    /// The returned tuple's second argument is an optional number of wait states to be inserted
258    /// during the `IO_IORQ` cycle.
259    ///
260    /// This method is being used by the [Cpu](crate::cpu::Cpu) to write data to the I/O port.
261    ///
262    /// The default implementation returns `(None, None)`.
263    fn write_io(&mut self, port: u16, data: u8, timestamp: Self::Timestamp) -> (Option<Self::WrIoBreak>, Option<NonZeroU16>) {
264        (None, None)
265    }
266    /// Should return `true` if the interrupt request signal - the `INT` line - is active.
267    ///
268    /// The `timestamp` given here has been previously returned from the [Clock::as_timestamp] method.
269    ///
270    /// Look at the diagrams in the [cycles] module for the information when exactly, in the execution cycle,
271    /// this method is being called.
272    ///
273    /// The default implementation returns `false`.
274    fn is_irq(&mut self, timestamp: Self::Timestamp) -> bool {
275        false
276    }
277    /// Called during the `INT_IORQ` cycle when the maskable interrupt has been requested and accepted.
278    ///
279    /// Depending on the interrupt mode this method should return a tuple with its first argument being:
280    /// * [IM 0][crate::InterruptMode::Mode0]: an opcode of a command to execute.
281    /// * [IM 1][crate::InterruptMode::Mode1]: ignored by `CPU`.
282    /// * [IM 2][crate::InterruptMode::Mode2]: the lower half of an address of the vector jump table entry.
283    /// The upper half of this address is being taken from the value of the register `I`.
284    ///
285    /// In reality, during the `INT_IORQ` cycle, a byte is being sampled from the data bus where an external
286    /// device places it while requesting an interrupt.
287    ///
288    /// The returned tuple's second argument is an optional number of wait states to be inserted
289    /// during the `INT_IORQ` cycle.
290    ///
291    /// The default implementation returns `(RST_38H_OPCODE, None)` equalizing mode 0 to mode 1.
292    fn irq_data(&mut self, pc: u16, timestamp: Self::Timestamp) -> (u8, Option<NonZeroU16>) {
293        (RST_38H_OPCODE, None)
294    }
295    /// When `RETI` instruction is being executed this method is being called to update the device
296    /// implementation instance, so another interrupt signal can be set up if necessary.
297    ///
298    /// This method is being called in the middle of the instruction execution, before the returning
299    /// address is popped from the machine stack.
300    ///
301    /// The given `address` is pointing immediately after the `RETI` instruction opcode.
302    /// The given `timestamp` is taken after the `RETI` instruction opcode was read but before popping
303    /// the return value from the stack. After calling this method the [Clock] counter will be
304    /// further increased `2 x` by the [MEMRW][MEMRW_CYCLE_TS] cycles.
305    ///
306    /// Returning `Some(Self::RetiBreak)` from this method is a request to break the execution after
307    /// the execution of `RETI` completes. See [Cpu::execute_with_limit](crate::cpu::Cpu::execute_with_limit).
308    ///
309    /// The default implementation returns `None`.
310    fn reti(&mut self, address: u16, timestamp: Self::Timestamp) -> Option<Self::RetiBreak> {
311        None
312    }
313}
314
315/// This trait defines an interface for accessing the host's memory.
316///
317/// Please also see [cycles] module.
318#[allow(unused_variables)]
319pub trait Memory {
320    /// This type is being used for timestamping memory operations. See also [Clock::Timestamp].
321    type Timestamp: Sized;
322    /// Should return a single byte read from the memory present at the given `address`.
323    ///
324    /// This method is being used by the [Cpu](crate::cpu::Cpu) to read data from memory
325    /// during the `MEMRW` read cycle.
326    ///
327    /// The `timestamp` given here has been previously returned from [Clock::add_mreq].
328    ///
329    /// For reading data during `M1` cycles [Memory::read_opcode] is being used instead.
330    ///
331    /// The default implementation forwards call to [Memory::read_debug].
332    fn read_mem(&self, address: u16, ts: Self::Timestamp) -> u8 {
333        self.read_debug(address)
334    }
335    /// Should return the unaligned, 2 consecutive bytes from the memory present at the given
336    /// `address` as a 16-bit unsigned integer in a `LE` (least significant byte first) order.
337    ///
338    /// This method is being used by the [Cpu](crate::cpu::Cpu) to read 16 bit values from memory
339    /// during the `MEMRW` read cycle.
340    ///
341    /// The real CPU splits this read but we are cutting corners here slightly.
342    ///
343    /// The `timestamp` given here has been previously returned from [Clock::add_mreq].
344    ///
345    /// The default implementation composes a 16-bit value from two calls to [Memory::read_debug].
346    fn read_mem16(&self, address: u16, ts: Self::Timestamp) -> u16 {
347        u16::from_le_bytes([self.read_debug(address), self.read_debug(address.wrapping_add(1))])
348    }
349    /// Should return a single byte read from the memory present at the given `pc` address.
350    ///
351    /// This method is being used by the [Cpu](crate::cpu::Cpu) to read instruction opcodes
352    /// from memory during the `M1` cycle and can be used e.g. for implementing ROM/instruction
353    /// traps.
354    ///
355    /// Other, non `M1` related [Cpu](crate::cpu::Cpu) read operations, are performed
356    /// via [Memory::read_mem] and [Memory::read_mem16] methods.
357    ///
358    /// * `pc` contains an address in memory from which the opcode should be read.
359    /// * `ir` contains a memory refresh value that the real CPU would put on the bus during
360    /// the memory refresh cycle.
361    /// * `timestamp` has been previously returned from [Clock::add_m1].
362    ///
363    /// The default implementation forwards call to [Memory::read_debug].
364    fn read_opcode(&mut self, pc: u16, ir: u16, ts: Self::Timestamp) -> u8 {
365        self.read_debug(pc)
366    }
367    /// Should write a byte `value` into the memory at the given `address`.
368    ///
369    /// This method is being used by the [Cpu](crate::cpu::Cpu) to write data into memory
370    /// during the `MEMRW` write cycle.
371    ///
372    /// The `timestamp` given here has been previously returned from [Clock::add_mreq].
373    ///
374    /// The default implementation is a no-op.
375    fn write_mem(&mut self, address: u16, value: u8, ts: Self::Timestamp) {}
376    /// Should return a single byte read from the memory present at the given `address`.
377    ///
378    /// This method is being used by the [Cpu](crate::cpu::Cpu) debugger to get a relative
379    /// jump argument of a conditional instruction when a condition is not satisfied.
380    /// If the debugger is not being used, this call should be optimized out by the compiler
381    /// unless your implementation performs a volatile read here.
382    ///
383    /// The default implementation returns `u8::MAX`.
384    fn read_debug(&self, address: u16) -> u8 {
385        u8::max_value()
386    }
387}
388
389/// An enum representing the execution break cause returned by various methods of the [Cpu](crate::cpu::Cpu)
390/// trait.
391#[derive(Debug)]
392pub enum BreakCause<O, R> {
393    /// The `HALT` instruction was executed.
394    Halt,
395    /// [Io::write_io] requested a break while one of the `OUT` family instructions was executed.
396    WriteIo(O),
397    /// [Io::reti] requested a break while `RETI` was executed.
398    Reti(R)
399}
400
401/// The type returned from some of the [Cpu](crate::cpu::Cpu) trait methods.
402pub type Result<O, R> = core::result::Result<(), BreakCause<O, R>>;
403
404impl<O, R> From<BreakCause<O, R>> for &str {
405    fn from(cause: BreakCause<O, R>) -> &'static str {
406        (&cause).into()
407    }
408}
409
410impl<O, R> From<&BreakCause<O, R>> for &str {
411    fn from(cause: &BreakCause<O, R>) -> &'static str {
412        match cause {
413            BreakCause::Halt => "a HALT instruction was executed",
414            BreakCause::WriteIo(_) => "an I/O write operation has requested a break",
415            BreakCause::Reti(_) => "a break was requested at the end of an interrupt service routine",
416        }
417    }
418}
419
420impl<O, R> fmt::Display for BreakCause<O, R> {
421    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
422        Into::<&str>::into(self).fmt(f)
423    }
424}
425
426#[cfg(feature = "std")]
427impl<O, R> error::Error for BreakCause<O, R> where O: fmt::Debug, R: fmt::Debug {
428    fn description(&self) -> &str {
429        self.into()
430    }
431
432    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
433        None
434    }
435}
436
437/// A simple T-states counter wrapping at 2^bitsize of T.
438/// You may refer to it as a template for implementing [Clock] trait methods.
439#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
440#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
441pub struct TsCounter<T: Copy>(pub Wrapping<T>);
442
443impl<T> Clock for TsCounter<T>
444where T: Copy + PartialEq + PartialOrd + core::convert::From<u8> + core::convert::From<u16>,
445      Wrapping<T>: AddAssign + Add<Output=Wrapping<T>>
446{
447    type Limit = T;
448    type Timestamp = T;
449
450    /// Returns `true` if `self` as `T` >= `limit`. Otherwise returns `false`.
451    #[inline(always)]
452    fn is_past_limit(&self, limit: Self::Limit) -> bool {
453        (self.0).0 >= limit
454    }
455    /// Returns `self` after adding [INT_IORQ_LOW_TS] as `T`.
456    /// Updates `self` by adding [IRQ_ACK_CYCLE_TS] to the previous value of `self`.
457    #[inline]
458    fn add_irq(&mut self, _addr: u16) -> T {
459        let ts = (self.0 + Wrapping(INT_IORQ_LOW_TS.into())).0;
460        self.0 += Wrapping(IRQ_ACK_CYCLE_TS.into());
461        ts
462    }
463    /// Updates `self` by adding `add_ts` T-states to the previous value of `self`.
464    #[inline(always)]
465    fn add_no_mreq(&mut self, _addr: u16, add_ts: NonZeroU8) {
466    // fn add_no_mreq<const ADD_TS: u8>(&mut self, _addr: u16) {
467        self.0 += Wrapping(add_ts.get().into());
468    }
469    /// Returns `self` after adding [IO_IORQ_LOW_TS] as `T`.
470    /// Updates `self` by adding [IO_CYCLE_TS] to the previous value of `self`.
471    #[inline]
472    fn add_io(&mut self, _port: u16) -> T {
473        let ts = (self.0 + Wrapping(IO_IORQ_LOW_TS.into())).0;
474        self.0 += Wrapping(IO_CYCLE_TS.into());
475        ts
476    }
477    /// Updates `self` by adding [MEMRW_CYCLE_TS] to the previous value of `self`.
478    /// Returns `self` after updating as `T`.
479    #[inline(always)]
480    fn add_mreq(&mut self, _addr: u16) -> T {
481        self.0 += Wrapping(MEMRW_CYCLE_TS.into());
482        (self.0).0
483    }
484    /// Updates `self` by adding [M1_CYCLE_TS] to the previous value of `self`.
485    /// Returns `self` after updating as `T`.
486    #[inline(always)]
487    fn add_m1(&mut self, _addr: u16) -> T {
488        self.0 += Wrapping(M1_CYCLE_TS.into());
489        (self.0).0
490    }
491    /// Updates `self` by adding `wait_states` T-states to the previous value of `self`.
492    #[inline]
493    fn add_wait_states(&mut self, _bus: u16, wait_states: NonZeroU16) {
494        self.0 += Wrapping(wait_states.get().into())
495    }
496    /// Returns a copy of `self` as `T`.
497    #[inline(always)]
498    fn as_timestamp(&self) -> T {
499        (self.0).0
500    }
501}
502
503impl<T: Copy> From<T> for TsCounter<T> {
504    fn from(tsc: T) -> Self {
505        TsCounter(Wrapping(tsc))
506    }
507}
508
509impl<T: Copy> From<Wrapping<T>> for TsCounter<T> {
510    fn from(tsc: Wrapping<T>) -> Self {
511        TsCounter(tsc)
512    }
513}
514
515impl<T: Copy> Deref for TsCounter<T> {
516    type Target = Wrapping<T>;
517
518    fn deref(&self) -> &Self::Target {
519        &self.0
520    }
521}
522
523impl<T: Copy> DerefMut for TsCounter<T> {
524    fn deref_mut(&mut self) -> &mut Self::Target {
525        &mut self.0
526    }
527}
528
529#[cfg(test)]
530mod tests {
531    #[allow(unused_imports)]
532    use super::*;
533
534    #[test]
535    fn default_host_impl() {
536      #[derive(Default)]
537      struct Ctrl;
538      impl Io for Ctrl {
539        type Timestamp = i32;
540        type WrIoBreak = ();
541        type RetiBreak = ();
542      }
543      impl Memory for Ctrl {
544        type Timestamp = i32;
545      }
546      let mut ctrl = Ctrl::default();
547      for val in 0..=u16::max_value() {
548        assert_eq!(ctrl.read_io(val, 0), (0xFF, None));
549        assert_eq!(ctrl.write_io(val, 0, 0), (None, None));
550        assert_eq!(ctrl.is_irq(0), false);
551        assert_eq!(ctrl.irq_data(val, 0), (RST_38H_OPCODE, None));
552        assert_eq!(ctrl.reti(val, 0), None);
553        assert_eq!(ctrl.read_mem(val, 0), 0xFF);
554        assert_eq!(ctrl.read_mem16(val, 0), 0xFFFF);
555        assert_eq!(ctrl.read_opcode(val, val, 0), 0xFF);
556        assert_eq!(ctrl.read_debug(val), 0xFF);
557      }
558    }
559
560    #[cfg(feature = "std")]
561    #[test]
562    fn break_cause() {
563      type BrkCause = BreakCause<(),()>;
564      assert_eq!(BrkCause::Halt.to_string(), "a HALT instruction was executed");
565      assert_eq!(BrkCause::WriteIo(()).to_string(), "an I/O write operation has requested a break");
566      assert_eq!(BrkCause::Reti(()).to_string(), "a break was requested at the end of an interrupt service routine");
567    }
568
569    #[test]
570    fn ts_counter() {
571      let mut tc = TsCounter::<i32>::default();
572      assert!(tc.is_past_limit(0));
573      assert!(!tc.is_past_limit(1));
574      tc.0.0 = 1;
575      assert!(!tc.is_past_limit(2));
576      assert!(tc.is_past_limit(1));
577      assert!(tc.is_past_limit(0));
578      tc.0 = Wrapping(0).into();
579      assert_eq!(tc.add_irq(0), INT_IORQ_LOW_TS as i32);
580      assert_eq!(tc.as_timestamp(), IRQ_ACK_CYCLE_TS as i32);
581      tc = 0.into();
582      tc.add_no_mreq(0, NonZeroU8::new(3).unwrap());
583      assert_eq!(*tc, Wrapping(3i32));
584      *tc = Wrapping(0).into();
585      assert_eq!(tc.add_io(0), IO_IORQ_LOW_TS as i32);
586      assert_eq!(tc.as_timestamp(), IO_CYCLE_TS as i32);
587      tc = 0.into();
588      assert_eq!(tc.add_mreq(0), MEMRW_CYCLE_TS as i32);
589      assert_eq!(tc.as_timestamp(), MEMRW_CYCLE_TS as i32);
590      tc.0 = Wrapping(0).into();
591      assert_eq!(tc.add_m1(0), M1_CYCLE_TS as i32);
592      assert_eq!(tc.as_timestamp(), M1_CYCLE_TS as i32);
593      tc = 0.into();
594      tc.add_wait_states(0, NonZeroU16::new(7).unwrap());
595      assert_eq!(tc.as_timestamp(), 7i32);
596    }
597
598    #[cfg(feature = "serde")]
599    #[test]
600    fn tscounter_serde() {
601        let tsc: TsCounter<i32> = serde_json::from_str("0").unwrap();
602        assert_eq!(tsc, TsCounter::default());
603        let tsc = TsCounter::from(-32);
604        let sertsc = serde_json::to_string(&tsc).unwrap();
605        assert_eq!(sertsc, "-32");
606        let tsc_de: TsCounter<i32> = serde_json::from_str(&sertsc).unwrap();
607        assert_eq!(tsc, tsc_de);
608    }
609}