#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};
use super::{Z80, any::Z80Any, CpuFlags};
pub trait Flavour: Clone + Copy + Default + PartialEq + Eq {
const CONSTANT_OUT_DATA: u8;
const ACCEPTING_INT_RESETS_IFF2_EARLY: bool;
fn tag() -> &'static str;
fn memptr_mix(msb: u8, lsb: u8) -> (u8, u8);
fn begin_instruction(&mut self);
fn flags_modified(&mut self);
fn get_q(&self, acc:u8, flags: CpuFlags) -> u8;
fn cpu_into_any(cpu: Z80<Self>) -> Z80Any;
fn unwrap_cpu_any(cpu_any: Z80Any) -> Z80<Self>;
fn reset(&mut self) {
*self = Default::default();
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(default, rename_all(serialize = "camelCase")))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct NMOS {
#[cfg_attr(feature = "serde", serde(alias = "flagsModified"))]
flags_modified: bool,
#[cfg_attr(feature = "serde", serde(alias = "lastFlagsModified"))]
last_flags_modified: bool
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(into = "NMOS", from = "NMOS"))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct CMOS;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(default, rename_all(serialize = "camelCase")))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct BM1 {
#[cfg_attr(feature = "serde", serde(alias = "flagsModified"))]
flags_modified: bool,
#[cfg_attr(feature = "serde", serde(alias = "lastFlagsModified"))]
last_flags_modified: bool
}
impl Flavour for NMOS {
const CONSTANT_OUT_DATA: u8 = 0;
const ACCEPTING_INT_RESETS_IFF2_EARLY: bool = true;
fn tag() -> &'static str {
"NMOS"
}
#[inline(always)]
fn memptr_mix(msb: u8, lsb: u8) -> (u8, u8) {
(msb, lsb.wrapping_add(1))
}
#[inline(always)]
fn begin_instruction(&mut self) {
self.last_flags_modified = self.flags_modified;
self.flags_modified = false;
}
#[inline(always)]
fn flags_modified(&mut self) {
self.flags_modified = true;
}
#[inline(always)]
fn get_q(&self, acc: u8, flags: CpuFlags) -> u8 {
if self.last_flags_modified {
acc
}
else {
acc | flags.bits()
}
}
fn cpu_into_any(cpu: Z80<Self>) -> Z80Any {
Z80Any::NMOS(cpu)
}
fn unwrap_cpu_any(cpu_any: Z80Any) -> Z80<Self> {
cpu_any.unwrap_nmos()
}
}
impl Flavour for CMOS {
const CONSTANT_OUT_DATA: u8 = u8::max_value();
const ACCEPTING_INT_RESETS_IFF2_EARLY: bool = false;
fn tag() -> &'static str {
"CMOS"
}
#[inline(always)]
fn memptr_mix(msb: u8, lsb: u8) -> (u8, u8) {
(msb, lsb.wrapping_add(1))
}
#[inline(always)]
fn begin_instruction(&mut self) {}
#[inline(always)]
fn flags_modified(&mut self) {}
#[inline(always)]
fn get_q(&self, acc: u8, _flags: CpuFlags) -> u8 { acc }
fn cpu_into_any(cpu: Z80<Self>) -> Z80Any {
Z80Any::CMOS(cpu)
}
fn unwrap_cpu_any(cpu_any: Z80Any) -> Z80<Self> {
cpu_any.unwrap_cmos()
}
}
impl Flavour for BM1 {
const CONSTANT_OUT_DATA: u8 = 0;
const ACCEPTING_INT_RESETS_IFF2_EARLY: bool = false;
fn tag() -> &'static str {
"BM1"
}
#[inline(always)]
fn memptr_mix(_msb: u8, lsb: u8) -> (u8, u8) {
(0, lsb.wrapping_add(1))
}
#[inline(always)]
fn begin_instruction(&mut self) {
self.last_flags_modified = self.flags_modified;
self.flags_modified = false;
}
#[inline(always)]
fn flags_modified(&mut self) {
self.flags_modified = true;
}
#[inline(always)]
fn get_q(&self, acc: u8, flags: CpuFlags) -> u8 {
if self.last_flags_modified {
acc
}
else {
acc | flags.bits()
}
}
fn cpu_into_any(cpu: Z80<Self>) -> Z80Any {
Z80Any::BM1(cpu)
}
fn unwrap_cpu_any(cpu_any: Z80Any) -> Z80<Self> {
cpu_any.unwrap_bm1()
}
}
impl From<NMOS> for CMOS {
fn from(_: NMOS) -> Self {
CMOS
}
}
impl From<CMOS> for NMOS {
fn from(_: CMOS) -> Self {
NMOS::default()
}
}
impl From<BM1> for CMOS {
fn from(_: BM1) -> Self {
CMOS
}
}
impl From<CMOS> for BM1 {
fn from(_: CMOS) -> Self {
BM1::default()
}
}
impl From<NMOS> for BM1 {
fn from(NMOS{flags_modified, last_flags_modified}: NMOS) -> Self {
BM1 {flags_modified, last_flags_modified}
}
}
impl From<BM1> for NMOS {
fn from(BM1{flags_modified, last_flags_modified}: BM1) -> Self {
NMOS {flags_modified, last_flags_modified}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn flavour_works() {
assert_eq!(NMOS::memptr_mix(1, 1), (1, 2));
assert_eq!(NMOS::memptr_mix(1, 255), (1, 0));
assert_eq!(CMOS::memptr_mix(1, 1), (1, 2));
assert_eq!(CMOS::memptr_mix(1, 255), (1, 0));
assert_eq!(BM1::memptr_mix(1, 1), (0, 2));
assert_eq!(BM1::memptr_mix(1, 255), (0, 0));
let flags = CpuFlags::all();
let mut flav = NMOS::default();
assert!(!flav.last_flags_modified);
assert!(!flav.flags_modified);
assert_eq!(flav.get_q(0, flags), 0xFF);
flav.begin_instruction();
assert!(!flav.last_flags_modified);
assert!(!flav.flags_modified);
assert_eq!(flav.get_q(0, flags), 0xFF);
flav.flags_modified();
assert!(!flav.last_flags_modified);
assert!(flav.flags_modified);
assert_eq!(flav.get_q(0, flags), 0xFF);
flav.begin_instruction();
assert!(flav.last_flags_modified);
assert!(!flav.flags_modified);
assert_eq!(flav.get_q(0, flags), 0);
let mut flav = BM1::default();
assert!(!flav.last_flags_modified);
assert!(!flav.flags_modified);
assert_eq!(flav.get_q(0, flags), 0xFF);
flav.begin_instruction();
assert!(!flav.last_flags_modified);
assert!(!flav.flags_modified);
assert_eq!(flav.get_q(0, flags), 0xFF);
flav.flags_modified();
assert!(!flav.last_flags_modified);
assert!(flav.flags_modified);
assert_eq!(flav.get_q(0, flags), 0xFF);
flav.begin_instruction();
assert!(flav.last_flags_modified);
assert!(!flav.flags_modified);
assert_eq!(flav.get_q(0, flags), 0);
let mut flav = CMOS::default();
assert_eq!(flav.get_q(0, flags), 0);
flav.begin_instruction();
assert_eq!(flav.get_q(1, flags), 1);
flav.flags_modified();
assert_eq!(flav.get_q(2, flags), 2);
flav.begin_instruction();
assert_eq!(flav.get_q(3, flags), 3);
}
#[test]
fn flavour_conversion() {
let cmos: Z80<CMOS> = Z80::<BM1>::default().into_flavour();
let nmos = Z80::<NMOS>::from_flavour(cmos);
let bm1 = nmos.into_flavour::<BM1>();
assert_eq!(bm1, Z80::<BM1>::default());
let cmos1: Z80<CMOS> = Z80::<CMOS>::default();
let cmos2: Z80<CMOS> = Z80::<CMOS>::default();
assert_eq!(Z80Any::from(cmos1), Z80Any::CMOS(cmos2));
let nmos1: Z80<NMOS> = Z80::<NMOS>::default();
let nmos2: Z80<NMOS> = Z80::<NMOS>::default();
assert_eq!(Z80Any::from(nmos1), Z80Any::NMOS(nmos2));
let bm1_1: Z80<BM1> = Z80::<BM1>::default();
let bm1_2: Z80<BM1> = Z80::<BM1>::default();
assert_eq!(Z80Any::from(bm1_1), Z80Any::BM1(bm1_2));
}
#[cfg(feature = "serde")]
#[test]
fn flavour_serde() {
assert_eq!(NMOS::tag(), "NMOS");
assert_eq!(CMOS::tag(), "CMOS");
assert_eq!(BM1::tag(), "BM1");
assert_eq!(serde_json::to_string(&NMOS::default()).unwrap(), r#"{"flagsModified":false,"lastFlagsModified":false}"#);
assert_eq!(serde_json::to_string(&CMOS::default()).unwrap(), r#"{"flagsModified":false,"lastFlagsModified":false}"#);
assert_eq!(serde_json::to_string(&BM1::default()).unwrap(), r#"{"flagsModified":false,"lastFlagsModified":false}"#);
let flav: NMOS = serde_json::from_str(r#"{"flags_modified":false,"last_flags_modified":false}"#).unwrap();
assert!(flav == NMOS::default());
let flav: NMOS = serde_json::from_str(r#"{}"#).unwrap();
assert!(flav == NMOS::default());
let flav: NMOS = serde_json::from_str(r#"{"flagsModified":true,"lastFlagsModified":true}"#).unwrap();
assert!(flav == NMOS { flags_modified: true, last_flags_modified: true});
let flav: CMOS = serde_json::from_str(r#"{"flags_modified":false,"last_flags_modified":false}"#).unwrap();
assert!(flav == CMOS::default());
let flav: CMOS = serde_json::from_str(r#"{}"#).unwrap();
assert!(flav == CMOS::default());
let flav: CMOS = serde_json::from_str(r#"{"flagsModified":true,"lastFlagsModified":true}"#).unwrap();
assert!(flav == CMOS);
let flav: BM1 = serde_json::from_str(r#"{"flags_modified":false,"last_flags_modified":false}"#).unwrap();
assert!(flav == BM1::default());
let flav: BM1 = serde_json::from_str(r#"{}"#).unwrap();
assert!(flav == BM1::default());
let flav: BM1 = serde_json::from_str(r#"{"flagsModified":true,"lastFlagsModified":true}"#).unwrap();
assert!(flav == BM1 { flags_modified: true, last_flags_modified: true});
}
}