Expand description
Helper macros when implementing Io
, Memory
and Clock
for wrapper types.
use z80emu::{*, host::TsCounter};
struct Ticks<T: Copy>(TsCounter<T>);
struct BusWrap<B>(B);
impl Clock for Ticks<i32> {
forward_host_clock_types!{ @ => TsCounter<i32> }
forward_host_clock_methods!{ @ => |c| c.0 }
}
impl<B: Io> Io for BusWrap<B> {
forward_host_io_types!{ @ => B }
forward_host_io_methods!{ @ => |b| b.0 }
}
impl<B: Memory> Memory for BusWrap<B> {
forward_host_memory_types!{ @ => B }
forward_host_memory_methods!{ @ => |b| b.0 }
}
One practical scenario is that the wrapper requires only a few methods with custom behaviour while forwarding the rest.
use core::num::NonZeroU16;
use z80emu::*;
struct DebugBus<'a, B> {
bus: &'a mut B,
irq_data: Option<u8>
}
// To illustrate an example with accessors:
impl<'a, B> DebugBus<'a, B> {
fn bus_mut(&mut self) -> &mut B {
&mut *self.bus
}
fn bus_ref(&self) -> &B {
&*self.bus
}
}
impl<B: Io> Io for DebugBus<'_, B> {
// while we should use just this:
// forward_host_io_types!{ @ => B }
// here we illustrate how to forward only selected types:
type Timestamp = <B as Io>::Timestamp;
forward_host_io_types! { (WrIoBreak RetiBreak) => B }
/// Capture data from an interrupting device.
fn irq_data(&mut self, pc: u16, ts: Self::Timestamp) -> (u8, Option<NonZeroU16>) {
let (data, delay) = self.bus.irq_data(pc, ts);
self.irq_data = Some(data);
(data, delay)
}
// forward remaining methods
forward_host_io_methods! {
(write_io, read_io, is_irq, reti) => |me| me.bus_mut() }
// be carefull if you don't forward a method, a default implementation
// will be used for that method instead
}
impl<B: Memory> Memory for DebugBus<'_, B> {
forward_host_memory_types!{ @ => B }
// here we can split implementations between methods that access &self:
forward_host_memory_methods!{ @ref => |me| me.bus_ref() }
// from methods accessing &mut self
forward_host_memory_methods!{ @mut => |me| me.bus_mut() }
}