#[cfg(feature = "std")] use std::error;
#[cfg(feature = "serde")] use serde::{Serialize, Deserialize};
use core::fmt;
#[allow(unused_imports)]
use core::num::{NonZeroU8, NonZeroU16, Wrapping};
use core::ops::{Add, AddAssign, Deref, DerefMut};
use super::opconsts::RST_38H_OPCODE;
pub mod cycles {
pub const M1_CYCLE_TS: u8 = 4;
pub const MEMRW_CYCLE_TS: u8 = 3;
pub const IO_IORQ_LOW_TS: u8 = 1;
pub const IO_CYCLE_TS: u8 = 4;
pub const INT_IORQ_LOW_TS: u8 = 2;
pub const IRQ_ACK_CYCLE_TS: u8 = 6;
}
use cycles::*;
pub trait Clock {
type Limit: Sized + Copy;
type Timestamp: Sized;
fn is_past_limit(&self, limit: Self::Limit) -> bool;
fn add_irq(&mut self, pc: u16) -> Self::Timestamp;
fn add_no_mreq(&mut self, address: u16, add_ts: NonZeroU8);
fn add_m1(&mut self, address: u16) -> Self::Timestamp;
fn add_mreq(&mut self, address: u16) -> Self::Timestamp;
fn add_io(&mut self, port: u16) -> Self::Timestamp;
fn add_wait_states(&mut self, bus: u16, wait_states: NonZeroU16);
fn as_timestamp(&self) -> Self::Timestamp;
}
#[allow(unused_variables)]
#[allow(clippy::wrong_self_convention)]
pub trait Io {
type Timestamp: Sized;
type WrIoBreak;
type RetiBreak;
fn read_io(&mut self, port: u16, timestamp: Self::Timestamp) -> (u8, Option<NonZeroU16>) {
(u8::max_value(), None)
}
fn write_io(&mut self, port: u16, data: u8, timestamp: Self::Timestamp) -> (Option<Self::WrIoBreak>, Option<NonZeroU16>) {
(None, None)
}
fn is_irq(&mut self, timestamp: Self::Timestamp) -> bool {
false
}
fn irq_data(&mut self, pc: u16, timestamp: Self::Timestamp) -> (u8, Option<NonZeroU16>) {
(RST_38H_OPCODE, None)
}
fn reti(&mut self, address: u16, timestamp: Self::Timestamp) -> Option<Self::RetiBreak> {
None
}
}
#[allow(unused_variables)]
pub trait Memory {
type Timestamp: Sized;
fn read_mem(&self, address: u16, ts: Self::Timestamp) -> u8 {
self.read_debug(address)
}
fn read_mem16(&self, address: u16, ts: Self::Timestamp) -> u16 {
u16::from_le_bytes([self.read_debug(address), self.read_debug(address.wrapping_add(1))])
}
fn read_opcode(&mut self, pc: u16, ir: u16, ts: Self::Timestamp) -> u8 {
self.read_debug(pc)
}
fn write_mem(&mut self, address: u16, value: u8, ts: Self::Timestamp) {}
fn read_debug(&self, address: u16) -> u8 {
u8::max_value()
}
}
#[derive(Debug)]
pub enum BreakCause<O, R> {
Halt,
WriteIo(O),
Reti(R)
}
pub type Result<O, R> = core::result::Result<(), BreakCause<O, R>>;
impl<O, R> From<BreakCause<O, R>> for &str {
fn from(cause: BreakCause<O, R>) -> &'static str {
(&cause).into()
}
}
impl<O, R> From<&BreakCause<O, R>> for &str {
fn from(cause: &BreakCause<O, R>) -> &'static str {
match cause {
BreakCause::Halt => "a HALT instruction was executed",
BreakCause::WriteIo(_) => "an I/O write operation has requested a break",
BreakCause::Reti(_) => "a break was requested at the end of an interrupt service routine",
}
}
}
impl<O, R> fmt::Display for BreakCause<O, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Into::<&str>::into(self).fmt(f)
}
}
#[cfg(feature = "std")]
impl<O, R> error::Error for BreakCause<O, R> where O: fmt::Debug, R: fmt::Debug {
fn description(&self) -> &str {
self.into()
}
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct TsCounter<T: Copy>(pub Wrapping<T>);
impl<T> Clock for TsCounter<T>
where T: Copy + PartialEq + PartialOrd + core::convert::From<u8> + core::convert::From<u16>,
Wrapping<T>: AddAssign + Add<Output=Wrapping<T>>
{
type Limit = T;
type Timestamp = T;
#[inline(always)]
fn is_past_limit(&self, limit: Self::Limit) -> bool {
(self.0).0 >= limit
}
#[inline]
fn add_irq(&mut self, _addr: u16) -> T {
let ts = (self.0 + Wrapping(INT_IORQ_LOW_TS.into())).0;
self.0 += Wrapping(IRQ_ACK_CYCLE_TS.into());
ts
}
#[inline(always)]
fn add_no_mreq(&mut self, _addr: u16, add_ts: NonZeroU8) {
self.0 += Wrapping(add_ts.get().into());
}
#[inline]
fn add_io(&mut self, _port: u16) -> T {
let ts = (self.0 + Wrapping(IO_IORQ_LOW_TS.into())).0;
self.0 += Wrapping(IO_CYCLE_TS.into());
ts
}
#[inline(always)]
fn add_mreq(&mut self, _addr: u16) -> T {
self.0 += Wrapping(MEMRW_CYCLE_TS.into());
(self.0).0
}
#[inline(always)]
fn add_m1(&mut self, _addr: u16) -> T {
self.0 += Wrapping(M1_CYCLE_TS.into());
(self.0).0
}
#[inline]
fn add_wait_states(&mut self, _bus: u16, wait_states: NonZeroU16) {
self.0 += Wrapping(wait_states.get().into())
}
#[inline(always)]
fn as_timestamp(&self) -> T {
(self.0).0
}
}
impl<T: Copy> From<T> for TsCounter<T> {
fn from(tsc: T) -> Self {
TsCounter(Wrapping(tsc))
}
}
impl<T: Copy> From<Wrapping<T>> for TsCounter<T> {
fn from(tsc: Wrapping<T>) -> Self {
TsCounter(tsc)
}
}
impl<T: Copy> Deref for TsCounter<T> {
type Target = Wrapping<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: Copy> DerefMut for TsCounter<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn default_host_impl() {
#[derive(Default)]
struct Ctrl;
impl Io for Ctrl {
type Timestamp = i32;
type WrIoBreak = ();
type RetiBreak = ();
}
impl Memory for Ctrl {
type Timestamp = i32;
}
let mut ctrl = Ctrl::default();
for val in 0..=u16::max_value() {
assert_eq!(ctrl.read_io(val, 0), (0xFF, None));
assert_eq!(ctrl.write_io(val, 0, 0), (None, None));
assert_eq!(ctrl.is_irq(0), false);
assert_eq!(ctrl.irq_data(val, 0), (RST_38H_OPCODE, None));
assert_eq!(ctrl.reti(val, 0), None);
assert_eq!(ctrl.read_mem(val, 0), 0xFF);
assert_eq!(ctrl.read_mem16(val, 0), 0xFFFF);
assert_eq!(ctrl.read_opcode(val, val, 0), 0xFF);
assert_eq!(ctrl.read_debug(val), 0xFF);
}
}
#[cfg(feature = "std")]
#[test]
fn break_cause() {
type BrkCause = BreakCause<(),()>;
assert_eq!(BrkCause::Halt.to_string(), "a HALT instruction was executed");
assert_eq!(BrkCause::WriteIo(()).to_string(), "an I/O write operation has requested a break");
assert_eq!(BrkCause::Reti(()).to_string(), "a break was requested at the end of an interrupt service routine");
}
#[test]
fn ts_counter() {
let mut tc = TsCounter::<i32>::default();
assert!(tc.is_past_limit(0));
assert!(!tc.is_past_limit(1));
tc.0.0 = 1;
assert!(!tc.is_past_limit(2));
assert!(tc.is_past_limit(1));
assert!(tc.is_past_limit(0));
tc.0 = Wrapping(0).into();
assert_eq!(tc.add_irq(0), INT_IORQ_LOW_TS as i32);
assert_eq!(tc.as_timestamp(), IRQ_ACK_CYCLE_TS as i32);
tc = 0.into();
tc.add_no_mreq(0, NonZeroU8::new(3).unwrap());
assert_eq!(*tc, Wrapping(3i32));
*tc = Wrapping(0).into();
assert_eq!(tc.add_io(0), IO_IORQ_LOW_TS as i32);
assert_eq!(tc.as_timestamp(), IO_CYCLE_TS as i32);
tc = 0.into();
assert_eq!(tc.add_mreq(0), MEMRW_CYCLE_TS as i32);
assert_eq!(tc.as_timestamp(), MEMRW_CYCLE_TS as i32);
tc.0 = Wrapping(0).into();
assert_eq!(tc.add_m1(0), M1_CYCLE_TS as i32);
assert_eq!(tc.as_timestamp(), M1_CYCLE_TS as i32);
tc = 0.into();
tc.add_wait_states(0, NonZeroU16::new(7).unwrap());
assert_eq!(tc.as_timestamp(), 7i32);
}
#[cfg(feature = "serde")]
#[test]
fn tscounter_serde() {
let tsc: TsCounter<i32> = serde_json::from_str("0").unwrap();
assert_eq!(tsc, TsCounter::default());
let tsc = TsCounter::from(-32);
let sertsc = serde_json::to_string(&tsc).unwrap();
assert_eq!(sertsc, "-32");
let tsc_de: TsCounter<i32> = serde_json::from_str(&sertsc).unwrap();
assert_eq!(tsc, tsc_de);
}
}