#![cfg_attr(not(feature = "std"), no_std)]
mod cpu;
pub mod disasm;
pub mod host;
pub mod macros;
pub mod z80;
pub use cpu::*;
pub use host::{Clock, Io, Memory, BreakCause};
pub use z80::{Z80, Z80NMOS, Z80CMOS};
pub use z80::any::Z80Any;
pub const NMI_RESTART: u16 = 0x66;
pub mod opconsts {
#[allow(unused_imports)]
use crate::Prefix;
pub const ED_PREFIX : u8 = 0xED;
pub const DD_PREFIX : u8 = 0xDD;
pub const FD_PREFIX : u8 = 0xFD;
pub const NOP_OPCODE : u8 = 0x00;
pub const HALT_OPCODE : u8 = 0x76;
pub const DI_OPCODE : u8 = 0xF3;
pub const EI_OPCODE : u8 = 0xFB;
pub const RET_OPCODE : u8 = 0xC9;
pub const RET_CC_OPBASE : u8 = 0b11_000_000;
pub const RET_CC_OPMASK : u8 = 0b11_000_111;
pub const RETI_OPCODE_T2: (u8, u8) = (ED_PREFIX, 0x4D);
pub const RETN_OPCODE_T2: (u8, u8) = (ED_PREFIX, 0x45);
pub const RETN_OP2_BASE : u8 = 0b01_000_101;
pub const RETN_OP2_MASK : u8 = 0b11_000_111;
pub const CALL_OPCODE : u8 = 0xCD;
pub const CALL_CC_OPBASE: u8 = 0b11_000_100;
pub const CALL_CC_OPMASK: u8 = 0b11_000_111;
pub const JP_OPCODE : u8 = 0xC3;
pub const JP_CC_OPBASE : u8 = 0b11_000_010;
pub const JP_CC_OPMASK : u8 = 0b11_000_111;
pub const JR_OPCODE : u8 = 0x18;
pub const JR_CC_OPBASE : u8 = 0b00_100_000;
pub const JR_CC_OPMASK : u8 = 0b11_100_111;
pub const RST_00H_OPCODE: u8 = 0xC7;
pub const RST_08H_OPCODE: u8 = 0xCF;
pub const RST_10H_OPCODE: u8 = 0xD7;
pub const RST_18H_OPCODE: u8 = 0xDF;
pub const RST_20H_OPCODE: u8 = 0xE7;
pub const RST_28H_OPCODE: u8 = 0xEF;
pub const RST_30H_OPCODE: u8 = 0xF7;
pub const RST_38H_OPCODE: u8 = 0xFF;
pub const RST_OPBASE: u8 = 0b11_000_111;
pub const RST_OPMASK: u8 = 0b11_000_111;
pub const DJNZ_OPCODE: u8 = 0x10;
}
#[cfg(test)]
mod tests {
use super::*;
use disasm::disasm_memory;
use z80::NMOS;
use opconsts::*;
fn test_opcode(len: usize, opcode: u8, matching: &str) -> CpuDebug {
let deb = debug_opcode(None, opcode, len);
let res = format!("{} {}", deb.mnemonic, deb.args);
assert_eq!(res.as_str(), matching);
deb
}
fn test_opcode2(len: usize, opcode: (u8, u8), matching: &str) -> CpuDebug {
let deb = debug_opcode(Some(opcode.0), opcode.1, len);
let res = format!("{} {}", deb.mnemonic, deb.args);
assert_eq!(res.as_str(), matching);
deb
}
fn debug_opcode(prefix: Option<u8>, opcode: u8, len: usize) -> CpuDebug {
let mut dbg = None;
let mut memory = [0u8;4];
if let Some(pfx) = prefix {
memory[0] = pfx;
memory[1] = opcode;
}
else {
memory[0] = opcode;
}
disasm_memory::<Z80<NMOS>,_,()>(0, &memory[0..len], |deb| {
if dbg.is_some() {
return Err(());
}
dbg = Some(deb);
Ok(())
}).expect("only a single instruction");
dbg.expect("some opcode")
}
#[test]
fn opconst_opcodes() {
test_opcode(1, NOP_OPCODE, "NOP ");
test_opcode(1, HALT_OPCODE, "HALT ");
test_opcode(1, DI_OPCODE, "DI ");
test_opcode(1, EI_OPCODE, "EI ");
test_opcode(1, RET_OPCODE, "RET ");
test_opcode2(2, RETI_OPCODE_T2,"RETI ");
test_opcode2(2, RETN_OPCODE_T2,"RETN ");
test_opcode(3, CALL_OPCODE, "CALL 0");
test_opcode(3, JP_OPCODE, "JP 0");
test_opcode(2, JR_OPCODE, "JR 2");
test_opcode(1, RST_00H_OPCODE, "RST 0");
test_opcode(1, RST_08H_OPCODE, "RST 8");
test_opcode(1, RST_10H_OPCODE, "RST 16");
test_opcode(1, RST_18H_OPCODE, "RST 24");
test_opcode(1, RST_20H_OPCODE, "RST 32");
test_opcode(1, RST_28H_OPCODE, "RST 40");
test_opcode(1, RST_30H_OPCODE, "RST 48");
test_opcode(1, RST_38H_OPCODE, "RST 56");
test_opcode(2, DJNZ_OPCODE, "DJNZ 2");
}
fn test_base_match(len: usize, opbase: u8, opmask: u8, code: u8, matching: &str) {
let opcode = opbase|code;
test_opcode(len, opcode, matching);
assert_eq!((opcode & opmask), opbase);
}
fn test_base_match_ed(len: usize, opbase: u8, opmask: u8, cond: Condition, matching: &str) {
let opcode = opbase|cond.to_code();
test_opcode2(len, (ED_PREFIX, opcode), matching);
assert_eq!((opcode & opmask), opbase);
}
#[test]
fn opconst_base() {
test_base_match(3, JP_CC_OPBASE, JP_CC_OPMASK, Condition::NZ.to_code(), "JP NZ, 0");
test_base_match(3, JP_CC_OPBASE, JP_CC_OPMASK, Condition::Z.to_code(), "JP Z, 0");
test_base_match(3, JP_CC_OPBASE, JP_CC_OPMASK, Condition::NC.to_code(), "JP NC, 0");
test_base_match(3, JP_CC_OPBASE, JP_CC_OPMASK, Condition::C.to_code(), "JP C, 0");
test_base_match(3, JP_CC_OPBASE, JP_CC_OPMASK, Condition::PO.to_code(), "JP PO, 0");
test_base_match(3, JP_CC_OPBASE, JP_CC_OPMASK, Condition::PE.to_code(), "JP PE, 0");
test_base_match(3, JP_CC_OPBASE, JP_CC_OPMASK, Condition::P.to_code(), "JP P, 0");
test_base_match(3, JP_CC_OPBASE, JP_CC_OPMASK, Condition::M.to_code(), "JP M, 0");
test_base_match(3, CALL_CC_OPBASE, CALL_CC_OPMASK, Condition::NZ.to_code(), "CALL NZ, 0");
test_base_match(3, CALL_CC_OPBASE, CALL_CC_OPMASK, Condition::Z.to_code(), "CALL Z, 0");
test_base_match(3, CALL_CC_OPBASE, CALL_CC_OPMASK, Condition::NC.to_code(), "CALL NC, 0");
test_base_match(3, CALL_CC_OPBASE, CALL_CC_OPMASK, Condition::C.to_code(), "CALL C, 0");
test_base_match(3, CALL_CC_OPBASE, CALL_CC_OPMASK, Condition::PO.to_code(), "CALL PO, 0");
test_base_match(3, CALL_CC_OPBASE, CALL_CC_OPMASK, Condition::PE.to_code(), "CALL PE, 0");
test_base_match(3, CALL_CC_OPBASE, CALL_CC_OPMASK, Condition::P.to_code(), "CALL P, 0");
test_base_match(3, CALL_CC_OPBASE, CALL_CC_OPMASK, Condition::M.to_code(), "CALL M, 0");
test_base_match(1, RET_CC_OPBASE, RET_CC_OPMASK, Condition::NZ.to_code(), "RET NZ");
test_base_match(1, RET_CC_OPBASE, RET_CC_OPMASK, Condition::Z.to_code(), "RET Z");
test_base_match(1, RET_CC_OPBASE, RET_CC_OPMASK, Condition::NC.to_code(), "RET NC");
test_base_match(1, RET_CC_OPBASE, RET_CC_OPMASK, Condition::C.to_code(), "RET C");
test_base_match(1, RET_CC_OPBASE, RET_CC_OPMASK, Condition::PO.to_code(), "RET PO");
test_base_match(1, RET_CC_OPBASE, RET_CC_OPMASK, Condition::PE.to_code(), "RET PE");
test_base_match(1, RET_CC_OPBASE, RET_CC_OPMASK, Condition::P.to_code(), "RET P");
test_base_match(1, RET_CC_OPBASE, RET_CC_OPMASK, Condition::M.to_code(), "RET M");
test_base_match(2, JR_CC_OPBASE, JR_CC_OPMASK, Condition::NZ.to_code(), "JR NZ, 2");
test_base_match(2, JR_CC_OPBASE, JR_CC_OPMASK, Condition::Z.to_code(), "JR Z, 2");
test_base_match(2, JR_CC_OPBASE, JR_CC_OPMASK, Condition::NC.to_code(), "JR NC, 2");
test_base_match(2, JR_CC_OPBASE, JR_CC_OPMASK, Condition::C.to_code(), "JR C, 2");
test_base_match(2, JR_CC_OPBASE, JR_CC_OPMASK, Condition::PO.to_code(), "JR NZ, 2");
test_base_match(2, JR_CC_OPBASE, JR_CC_OPMASK, Condition::PE.to_code(), "JR Z, 2");
test_base_match(2, JR_CC_OPBASE, JR_CC_OPMASK, Condition::P.to_code(), "JR NC, 2");
test_base_match(2, JR_CC_OPBASE, JR_CC_OPMASK, Condition::M.to_code(), "JR C, 2");
test_base_match(1, RST_OPBASE, RST_OPMASK, 0x00, "RST 0");
test_base_match(1, RST_OPBASE, RST_OPMASK, 0x08, "RST 8");
test_base_match(1, RST_OPBASE, RST_OPMASK, 0x10, "RST 16");
test_base_match(1, RST_OPBASE, RST_OPMASK, 0x18, "RST 24");
test_base_match(1, RST_OPBASE, RST_OPMASK, 0x20, "RST 32");
test_base_match(1, RST_OPBASE, RST_OPMASK, 0x28, "RST 40");
test_base_match(1, RST_OPBASE, RST_OPMASK, 0x30, "RST 48");
test_base_match(1, RST_OPBASE, RST_OPMASK, 0x38, "RST 56");
test_base_match_ed(2, RETN_OP2_BASE, RETN_OP2_MASK, Condition::NZ, "RETN ");
test_base_match_ed(2, RETN_OP2_BASE, RETN_OP2_MASK, Condition::Z, "RETI ");
test_base_match_ed(2, RETN_OP2_BASE, RETN_OP2_MASK, Condition::NC, "RETN ");
test_base_match_ed(2, RETN_OP2_BASE, RETN_OP2_MASK, Condition::C, "RETN ");
test_base_match_ed(2, RETN_OP2_BASE, RETN_OP2_MASK, Condition::PO, "RETN ");
test_base_match_ed(2, RETN_OP2_BASE, RETN_OP2_MASK, Condition::PE, "RETN ");
test_base_match_ed(2, RETN_OP2_BASE, RETN_OP2_MASK, Condition::P, "RETN ");
test_base_match_ed(2, RETN_OP2_BASE, RETN_OP2_MASK, Condition::M, "RETN ");
}
}