#[macro_export]
macro_rules! memdev_fields {
($ty:ident, len: $len:literal, {
$($start:literal $(..= $end:literal)? => $target:tt,)*
}) => {
impl $crate::memdev::MemDevice for $ty {
const LEN: usize = $len;
fn read_byte_relative(&self, addr: $crate::memdev::RelativeAddr) -> u8 {
$crate::dispatch_memdev_byte!($ty, addr, |addr| {
$($start $(..= $end)? => $crate::memdev_fields!(@reader: self, addr, $target),)*
})
}
fn read_bytes_relative(&self, addr: $crate::memdev::RelativeAddr, data: &mut [u8]) {
$crate::dispatch_memdev_bytes!($ty, addr, data, |addr, mut data| {
$($start $(..= $end)? => {
$crate::memdev_fields!(@range_reader: self, addr, data, $target)
},)*
})
}
fn write_byte_relative(&mut self, addr: $crate::memdev::RelativeAddr, val: u8) {
$crate::dispatch_memdev_byte!($ty, addr, |addr| {
$($start $(..= $end)? => $crate::memdev_fields!(@writer: self, addr, val, $target),)*
})
}
fn write_bytes_relative(&mut self, addr: $crate::memdev::RelativeAddr, data: &[u8]) {
$crate::dispatch_memdev_bytes!($ty, addr, data, |addr, ref data| {
$($start $(..= $end)? => {
$crate::memdev_fields!(@range_writer: self, addr, data, $target)
},)*
});
}
}
};
(@reader: $self:ident, $addr:expr, $target:ident) => {
$crate::memdev::MemDevice::read_byte_relative(&$self.$target, $addr)
};
(@reader: $self:ident, $addr:expr, $target:literal) => {
$target
};
(@reader: $self:ident, $addr:expr, { $target:ident, skip_over: $skip:literal }) => {
$crate::memdev_fields!(@reader: $self, $addr.skip_over($skip), $target)
};
(@reader: $self:ident, $addr:expr, { $target:ident, readonly }) => {
$crate::memdev_fields!(@reader: $self, $addr, $target)
};
(@range_reader: $self:ident, $addr:expr, $data:ident, $target:ident) => {
$crate::memdev::MemDevice::read_bytes_relative(&$self.$target, $addr, $data)
};
(@range_reader: $self:ident, $addr:expr, $data:ident, $target:literal) => {
$data.fill($target)
};
(@range_reader: $self:ident, $addr:expr, $data:ident, { $target:ident, skip_over: $skip:literal }) => {
$crate::memdev_fields!(@range_reader: $self, $addr.skip_over($skip), $data, $target)
};
(@range_reader: $self:ident, $addr:expr, $data:ident, { $target:ident, readonly }) => {
$crate::memdev_fields!(@range_reader: $self, $addr, $data, $target)
};
(@writer: $self:ident, $addr:expr, $val:ident, $target:ident) => {
$crate::memdev::MemDevice::write_byte_relative(&mut $self.$target, $addr, $val)
};
(@writer: $self:ident, $addr:expr, $val:ident, $target:literal) => {
()
};
(@writer: $self:ident, $addr:expr, $val:ident, { $target:ident, skip_over: $skip:literal }) => {
$crate::memdev_fields!(@writer: $self, $addr.skip_over($skip), $val, $target)
};
(@writer: $self:ident, $addr:expr, $val:ident, { $target:ident, readonly }) => {
()
};
(@range_writer: $self:ident, $addr:expr, $data:ident, $target:ident) => {
$crate::memdev::MemDevice::write_bytes_relative(&mut $self.$target, $addr, $data)
};
(@range_writer: $self:ident, $addr:expr, $data:ident, $target:literal) => {
()
};
(@range_writer: $self:ident, $addr:expr, $data:ident, { $target:ident, skip_over: $skip:literal }) => {
$crate::memdev_fields!(@range_writer: $self, $addr.skip_over($skip), $data, $target)
};
(@range_writer: $self:ident, $addr:expr, $data:ident, { $target:ident, readonly }) => {
()
};
(@addr: $base:ident, $addr:tt) => { $base.offset_by($addr) };
(@addr: $base:ident, $addr:literal..=$end:literal) => { $base.offset_by($addr) };
}
#[macro_export]
macro_rules! dispatch_memdev_byte {
($dev:ty, $addr:ident, |$outaddr:ident| {
$($start:literal $(..= $end:literal)? $(if $cond:expr)? => $target:expr,)*
}) => {{
let addr: $crate::memdev::RelativeAddr = $addr;
#[allow(unused)]
let $addr = ();
$crate::check_addr!($dev, addr);
match addr.relative() {
$($start $(..= $end)? $(if $cond)? => {
#[allow(unused)]
let $outaddr = addr.offset_by($start);
{ $target }
})*
#[allow(unreachable_patterns)]
_ => panic!(concat!("Unmapped Address {} in device ", stringify!($dev)), addr),
}
}};
}
#[macro_export]
macro_rules! dispatch_memdev_bytes {
($dev:ty, $addr:ident, $data:ident, |$outaddr:ident, $refmut:tt $outdata:ident| {
$($start:literal $(..= $end:literal)? $(if $cond:expr)? => $target:expr,)*
}) => {{
let orig_data = $data;
let mut addr: $crate::memdev::RelativeAddr = $addr;
let mut data_len: usize = orig_data.len();
#[allow(unused)]
let $addr = ();
#[allow(unused)]
let $data = ();
$crate::check_addr!($dev, addr, data_len);
let mut start = 0;
while data_len > 0 {
match addr.relative() {
$($start $(..= $end)? $(if $cond)? => {
let available = ($crate::dispatch_memdev_bytes!(@get_end: $start $(..= $end)?) - addr.relative() + 1) as usize;
let read = available.min(data_len);
data_len -= read;
let end_exclusive = start+read;
let datarange = start..end_exclusive;
#[allow(unused)]
let $outaddr = addr.offset_by($start);
#[allow(unused)]
let $outdata = $crate::dispatch_memdev_bytes!(@slicedata: $refmut orig_data[datarange]);
start = end_exclusive;
addr = addr.move_forward_by_wrapping(read as u16);
{ $target; }
})*
#[allow(unreachable_patterns)]
_ => panic!(concat!("Unmapped Address {} in device ", stringify!($dev)), addr),
}
}
}};
(@get_end: $start:literal) => { $start };
(@get_end: $start:literal ..= $end:literal) => { $end };
(@slicedata: mut $sliced:expr) => { &mut $sliced };
(@slicedata: ref $sliced:expr) => { &$sliced };
}
#[macro_export]
macro_rules! check_addr {
($dev:ty, $addr:ident) => {
assert!(
$addr.index() < <$dev as $crate::memdev::MemDevice>::LEN,
concat!("Address {} out of range for ", stringify!($dev)),
$addr,
);
};
($dev:ty, $addr:ident, $len:expr) => {{
let len = $len;
assert!(
$crate::memdev::RangeOverlaps::encloses(
&(0..<$dev as $crate::memdev::MemDevice>::LEN),
&$addr.range(len),
),
concat!(
"Address {} + slice of length {} not fully enclosed by ",
stringify!($dev)
),
$addr,
len,
);
}};
}
#[macro_export]
macro_rules! memdev_bytes_from_byte {
($dev:ty) => {
fn read_bytes_relative(&self, addr: $crate::memdev::RelativeAddr, data: &mut [u8]) {
$crate::check_addr!($dev, addr, data.len());
for (offset, dest) in data.iter_mut().enumerate() {
*dest = $crate::memdev::MemDevice::read_byte_relative(
self,
addr.move_forward_by(offset as u16),
);
}
}
fn write_bytes_relative(&mut self, addr: $crate::memdev::RelativeAddr, data: &[u8]) {
$crate::check_addr!($dev, addr, data.len());
for (offset, source) in data.iter().enumerate() {
$crate::memdev::MemDevice::write_byte_relative(
self,
addr.move_forward_by(offset as u16),
*source,
);
}
}
};
}