├── roms └── 48.rom ├── librespectrum ├── src │ ├── cpu │ │ ├── mod.rs │ │ ├── decoder │ │ │ ├── mod.rs │ │ │ ├── disassembler.rs │ │ │ ├── instruction.rs │ │ │ └── decoder.rs │ │ ├── flags.rs │ │ └── tokens.rs │ ├── devs │ │ ├── mem │ │ │ ├── mod.rs │ │ │ ├── memory.rs │ │ │ └── dynamic_48k.rs │ │ ├── device.rs │ │ ├── mod.rs │ │ └── bus_logger.rs │ ├── misc │ │ ├── identifiable.rs │ │ ├── mod.rs │ │ ├── macros.rs │ │ ├── u16_cell.rs │ │ └── ring_buff.rs │ ├── bus │ │ ├── mod.rs │ │ ├── clock.rs │ │ ├── cpu_bus.rs │ │ ├── bus_line.rs │ │ └── task.rs │ └── lib.rs ├── tests │ ├── exerciser │ │ ├── zexall.bin │ │ ├── zexall.tap │ │ ├── zexdoc.bin │ │ ├── zexdoc.tap │ │ └── zexall.src │ ├── disassembler_tests.rs │ └── misc │ │ └── opcodes.lst └── Cargo.toml ├── color_clash ├── src │ ├── palette │ │ ├── mod.rs │ │ ├── blending.rs │ │ └── zx_color.rs │ ├── widgets │ │ ├── mod.rs │ │ ├── z_stack.rs │ │ └── frame_buffer_view.rs │ ├── models │ │ ├── mod.rs │ │ ├── video_mode.rs │ │ ├── attributes.rs │ │ └── frame_buffer.rs │ └── main.rs ├── NOTES.md └── Cargo.toml ├── .gitignore ├── Cargo.toml ├── NOTES.txt ├── respectrum_tools ├── Cargo.toml └── src │ └── disasm.rs └── respectrum_egui ├── Cargo.toml └── src ├── windows ├── mod.rs ├── cpu_window.rs ├── bus_window.rs ├── disassm_window.rs └── memory_window.rs └── emul.rs /roms/48.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teq/respectrum/master/roms/48.rom -------------------------------------------------------------------------------- /librespectrum/src/cpu/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod decoder; 2 | 3 | mod flags; 4 | pub use flags::*; 5 | 6 | pub mod tokens; 7 | -------------------------------------------------------------------------------- /color_clash/src/palette/mod.rs: -------------------------------------------------------------------------------- 1 | mod blending; 2 | pub use blending::*; 3 | 4 | mod zx_color; 5 | pub use zx_color::ZXColor; 6 | -------------------------------------------------------------------------------- /librespectrum/tests/exerciser/zexall.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teq/respectrum/master/librespectrum/tests/exerciser/zexall.bin -------------------------------------------------------------------------------- /librespectrum/tests/exerciser/zexall.tap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teq/respectrum/master/librespectrum/tests/exerciser/zexall.tap -------------------------------------------------------------------------------- /librespectrum/tests/exerciser/zexdoc.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teq/respectrum/master/librespectrum/tests/exerciser/zexdoc.bin -------------------------------------------------------------------------------- /librespectrum/tests/exerciser/zexdoc.tap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teq/respectrum/master/librespectrum/tests/exerciser/zexdoc.tap -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE settings and project files 2 | .idea 3 | .vscode 4 | .vimrc.* 5 | tmux-*.log 6 | 7 | # Build artifacts 8 | /target 9 | -------------------------------------------------------------------------------- /librespectrum/src/devs/mem/mod.rs: -------------------------------------------------------------------------------- 1 | mod dynamic_48k; 2 | pub use dynamic_48k::Dynamic48k; 3 | 4 | mod memory; 5 | pub use memory::Memory; 6 | -------------------------------------------------------------------------------- /color_clash/src/widgets/mod.rs: -------------------------------------------------------------------------------- 1 | mod frame_buffer_view; 2 | pub use frame_buffer_view::FrameBufferView; 3 | 4 | mod z_stack; 5 | pub use z_stack::ZStack; 6 | -------------------------------------------------------------------------------- /librespectrum/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "librespectrum" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | bitflags = "1.3.2" 8 | -------------------------------------------------------------------------------- /librespectrum/src/misc/identifiable.rs: -------------------------------------------------------------------------------- 1 | /// Represents identifiable object 2 | pub trait Identifiable { 3 | 4 | /// Get object ID 5 | fn id(&self) -> u32; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /librespectrum/src/cpu/decoder/mod.rs: -------------------------------------------------------------------------------- 1 | mod decoder; 2 | pub use decoder::*; 3 | 4 | mod disassembler; 5 | pub use disassembler::*; 6 | 7 | mod instruction; 8 | pub use instruction::*; 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | resolver = "2" 4 | 5 | members = [ 6 | "color_clash", 7 | "librespectrum", 8 | "respectrum_egui", 9 | "respectrum_tools", 10 | ] 11 | -------------------------------------------------------------------------------- /librespectrum/src/devs/device.rs: -------------------------------------------------------------------------------- 1 | use crate::{bus::NoReturnTask, misc::Identifiable}; 2 | 3 | pub trait Device: Identifiable { 4 | fn run<'a>(&'a self) -> Box; 5 | } 6 | -------------------------------------------------------------------------------- /librespectrum/src/devs/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mem; 2 | 3 | mod bus_logger; 4 | pub use bus_logger::*; 5 | 6 | mod cpu; 7 | pub use cpu::*; 8 | 9 | mod device; 10 | pub use device::Device; 11 | -------------------------------------------------------------------------------- /NOTES.txt: -------------------------------------------------------------------------------- 1 | 13-07-2020 2 | 3 | It's not clear how to emulate CPI/CPD. According to docs it has the same m-cycles as LDI/LDD, 4 | but does it actually write to memory on the last m-cycle as CPI/CPD does? 5 | -------------------------------------------------------------------------------- /color_clash/src/models/mod.rs: -------------------------------------------------------------------------------- 1 | mod attributes; 2 | pub use attributes::Attributes; 3 | 4 | mod frame_buffer; 5 | pub use frame_buffer::FrameBuffer; 6 | 7 | mod video_mode; 8 | pub use video_mode::VideoMode; 9 | -------------------------------------------------------------------------------- /librespectrum/src/bus/mod.rs: -------------------------------------------------------------------------------- 1 | mod bus_line; 2 | pub use bus_line::*; 3 | 4 | mod clock; 5 | pub use clock::*; 6 | 7 | mod cpu_bus; 8 | pub use cpu_bus::*; 9 | 10 | mod task; 11 | pub use task::*; 12 | -------------------------------------------------------------------------------- /librespectrum/src/misc/mod.rs: -------------------------------------------------------------------------------- 1 | mod identifiable; 2 | pub use identifiable::Identifiable; 3 | 4 | pub mod macros; 5 | 6 | mod ring_buff; 7 | pub use ring_buff::*; 8 | 9 | mod u16_cell; 10 | pub use u16_cell::*; 11 | -------------------------------------------------------------------------------- /color_clash/src/models/video_mode.rs: -------------------------------------------------------------------------------- 1 | use druid::Data; 2 | 3 | /// ZX Spectrum video mode 4 | #[derive(Clone, PartialEq, Data)] 5 | pub enum VideoMode { 6 | /// Standart ZX video mode 7 | STD8X8, 8 | /// Multicolor with 8x4 attr blocks 9 | MUL8X4, 10 | } 11 | -------------------------------------------------------------------------------- /librespectrum/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(coroutines, coroutine_trait)] 2 | #![feature(never_type)] 3 | #![feature(trait_alias)] 4 | #![feature(bigint_helper_methods)] 5 | 6 | #[macro_use] 7 | extern crate bitflags; 8 | 9 | pub mod bus; 10 | pub mod cpu; 11 | pub mod devs; 12 | pub mod misc; 13 | -------------------------------------------------------------------------------- /color_clash/NOTES.md: -------------------------------------------------------------------------------- 1 | 2 | Tools: 3 | - Brush. Pattern: dot, etc.. 4 | - Lines, polylines 5 | - Rectangle/square 6 | - Circle/ellipse 7 | - Fill: solid/texture 8 | - Select/copy/paste (snap to blocks) 9 | - Zoom 2x 4x 8x 10 | 11 | - Color palette 12 | - Mode: xor, or, and 13 | - Attr: nop 14 | -------------------------------------------------------------------------------- /respectrum_tools/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "respectrum_tools" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [[bin]] 7 | name = "disasm" 8 | path = "src/disasm.rs" 9 | 10 | [dependencies] 11 | librespectrum = { path = "../librespectrum" } 12 | clap = { version = "3.1.6", features = ["derive"] } 13 | -------------------------------------------------------------------------------- /respectrum_egui/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "respectrum_egui" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [[bin]] 7 | name = "emul-egui" 8 | path = "src/emul.rs" 9 | 10 | [dependencies] 11 | librespectrum = { path = "../librespectrum" } 12 | egui = "0.18.0" 13 | egui_extras = "0.18.0" 14 | eframe = "0.18.0" 15 | -------------------------------------------------------------------------------- /librespectrum/src/devs/mem/memory.rs: -------------------------------------------------------------------------------- 1 | use crate::devs::Device; 2 | 3 | pub trait Memory: Device { 4 | 5 | /// Check if given address is writable (located in RAM) 6 | fn writable(&self, addr: u16) -> bool; 7 | 8 | /// Write byte to the memory 9 | fn write(&self, addr: u16, byte: u8); 10 | 11 | /// Read byte from the memory 12 | fn read(&self, addr: u16) -> u8; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /color_clash/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "color_clash" 3 | version = "0.1.0" 4 | authors = ["Andrey Tselishchev "] 5 | edition = "2024" 6 | 7 | [[bin]] 8 | name = "cc" 9 | path = "src/main.rs" 10 | 11 | [dependencies] 12 | bitflags = "1" 13 | bitvec = "1" 14 | clap = { version = "3", features = ["derive"] } 15 | druid = { git = "https://github.com/linebender/druid.git", features = ["im"] } 16 | -------------------------------------------------------------------------------- /color_clash/src/palette/blending.rs: -------------------------------------------------------------------------------- 1 | use super::ZXColor; 2 | use druid::{Data, Lens}; 3 | 4 | #[derive(Default)] 5 | #[derive(Clone, Lens, Data)] 6 | pub struct BlendingMode { 7 | pixel: PixelBlending, 8 | ink: Option, 9 | paper: Option, 10 | bright: bool, 11 | flash: bool, 12 | } 13 | 14 | #[derive(Default)] 15 | #[derive(Clone, PartialEq, Data)] 16 | pub enum PixelBlending { 17 | #[default] XOR, 18 | SET, 19 | RES, 20 | NOP, 21 | } 22 | -------------------------------------------------------------------------------- /librespectrum/src/bus/clock.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | 3 | /// System clock. Counts cycles with half t-cycle precision. 4 | #[derive(Default)] 5 | pub struct Clock { 6 | 7 | /// Half t-cycles count since system start. Even on rising, odd on falling. 8 | htcycles: Cell, 9 | 10 | } 11 | 12 | impl Clock { 13 | 14 | /// Set clock in half t-cycles 15 | pub fn set(&self, val: u64) { 16 | self.htcycles.set(val); 17 | } 18 | 19 | /// Get clock in half t-cycles 20 | pub fn get(&self) -> u64 { 21 | self.htcycles.get() 22 | } 23 | 24 | /// Get offset in half t-cycles to the next Nth t-cycle rising edge 25 | pub fn rising(&self, n: usize) -> usize { 26 | (n << 1) - (self.get() & 1) as usize 27 | } 28 | 29 | /// Get offset in half t-cycles to the next Nth t-cycle falling edge 30 | pub fn falling(&self, n: usize) -> usize { 31 | (n << 1) - (!self.get() & 1) as usize 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /librespectrum/src/misc/macros.rs: -------------------------------------------------------------------------------- 1 | 2 | /// Merge high and low u8 bytes to a u16 word 3 | #[macro_export] 4 | macro_rules! mkword { 5 | ($high: expr, $low: expr) => { (($high as u16) << 8) | $low as u16 } 6 | } 7 | 8 | /// Split u16 word to high and low u8 bytes 9 | #[macro_export] 10 | macro_rules! spword { 11 | ($word: expr) => { 12 | { 13 | let word = $word as u16; 14 | ((word >> 8) as u8, (word & 0xff) as u8) 15 | } 16 | } 17 | } 18 | 19 | /// Yield results from generator from within another generator. 20 | /// Analogous to python's `yield from` syntax 21 | #[macro_export] 22 | macro_rules! yield_from { 23 | ($input: expr) => { 24 | { 25 | let mut task = $input; 26 | loop { 27 | match std::pin::Pin::new(&mut task).resume(()) { 28 | std::ops::CoroutineState::Yielded(some) => yield some, 29 | std::ops::CoroutineState::Complete(result) => break result 30 | } 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /librespectrum/src/cpu/decoder/disassembler.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | pin::Pin, 3 | ops::{Coroutine, CoroutineState}, 4 | }; 5 | 6 | use super::{Instruction, instruction_decoder}; 7 | 8 | pub struct DisassembledLine { 9 | pub address: u16, 10 | pub bytes: Vec, 11 | pub instruction: Option, 12 | } 13 | 14 | pub fn disassembler( 15 | base_address: u16, 16 | line_bytes: usize 17 | ) -> impl Coroutine, Return=!> { 18 | 19 | let mut address = base_address; 20 | let mut bytes = Vec::with_capacity(line_bytes); 21 | let mut decoder = instruction_decoder(); 22 | 23 | #[coroutine] move |mut byte: u8| { 24 | 25 | loop { 26 | 27 | bytes.push(byte); 28 | let bytes_len = bytes.len() as u16; 29 | if let CoroutineState::Complete(instruction) = Pin::new(&mut decoder).resume(byte) { 30 | byte = yield Some(DisassembledLine { address, bytes, instruction: Some(instruction) }); 31 | address = address.wrapping_add(bytes_len); 32 | bytes = Vec::with_capacity(line_bytes); 33 | decoder = instruction_decoder(); 34 | } else if bytes.len() >= line_bytes { 35 | byte = yield Some(DisassembledLine { address, bytes, instruction: None }); 36 | address = address.wrapping_add(bytes_len); 37 | bytes = Vec::with_capacity(line_bytes); 38 | } else { 39 | byte = yield None; 40 | } 41 | 42 | } 43 | 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /color_clash/src/palette/zx_color.rs: -------------------------------------------------------------------------------- 1 | use druid::Data; 2 | 3 | const PALETTE: [([u8; 3], [u8; 3]); 8] = [ 4 | ([0x00, 0x00, 0x00], [0x00, 0x00, 0x00]), 5 | ([0x00, 0x00, 0xee], [0x00, 0x00, 0xff]), 6 | ([0xee, 0x00, 0x00], [0xff, 0x00, 0x00]), 7 | ([0xee, 0x00, 0xee], [0xff, 0x00, 0xff]), 8 | ([0x00, 0xee, 0x00], [0x00, 0xff, 0x00]), 9 | ([0x00, 0xee, 0xee], [0x00, 0xff, 0xff]), 10 | ([0xee, 0xee, 0x00], [0xff, 0xff, 0x00]), 11 | ([0xee, 0xee, 0xee], [0xff, 0xff, 0xff]), 12 | ]; 13 | 14 | #[derive(Debug, Default, Clone, Data)] 15 | pub struct ZXColor { 16 | index: usize 17 | } 18 | 19 | impl ZXColor { 20 | 21 | pub fn palette() -> Vec { 22 | (0..PALETTE.len()).map(|index| Self{index}).collect() 23 | } 24 | 25 | pub fn new(index: usize) -> Self { 26 | assert!(index < PALETTE.len()); 27 | Self { index } 28 | } 29 | 30 | pub fn index(&self) -> usize { 31 | self.index 32 | } 33 | 34 | pub fn rgb_dim(&self) -> [u8; 3] { 35 | PALETTE[self.index as usize].0 36 | } 37 | 38 | pub fn rgb_bright(&self) -> [u8; 3] { 39 | PALETTE[self.index as usize].1 40 | } 41 | 42 | } 43 | 44 | impl From for ZXColor where T: Into { 45 | fn from(index: T) -> Self { 46 | Self::new(index.into()) 47 | } 48 | } 49 | 50 | impl PartialEq for ZXColor { 51 | fn eq(&self, other: &ZXColor) -> bool { 52 | self.index == other.index 53 | } 54 | } 55 | 56 | impl PartialEq for ZXColor where T: Into + Copy { 57 | fn eq(&self, other: &T) -> bool { 58 | self.index == (*other).into() 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /librespectrum/src/bus/cpu_bus.rs: -------------------------------------------------------------------------------- 1 | use super::BusLine; 2 | 3 | bitflags! { 4 | #[derive(Default)] 5 | pub struct Ctrl : u8 { 6 | const NONE = 0; 7 | const MREQ = 1 << 0; 8 | const IORQ = 1 << 1; 9 | const RD = 1 << 2; 10 | const WR = 1 << 3; 11 | const RFSH = 1 << 4; 12 | } 13 | } 14 | 15 | /// Z80 CPU bus 16 | pub struct CpuBus { 17 | /// Address bus (tri-state outputs) 18 | pub addr: BusLine, 19 | /// Data bus (tri-state in/outputs) 20 | pub data: BusLine, 21 | /// Tri-state control outputs 22 | pub ctrl: BusLine, 23 | /// M1 output 24 | pub m1: BusLine, 25 | /// BUSAK output 26 | pub busak: BusLine, 27 | /// HALT output 28 | pub halt: BusLine, 29 | /// WAIT input 30 | pub wait: BusLine, 31 | /// INT input 32 | pub int: BusLine, 33 | /// NMI input 34 | pub nmi: BusLine, 35 | /// RESET input 36 | pub reset: BusLine, 37 | /// BUSRQ input 38 | pub busrq: BusLine, 39 | } 40 | 41 | impl Default for CpuBus { 42 | fn default() -> Self { 43 | Self { 44 | addr: BusLine::new("ADDR"), 45 | data: BusLine::new("DATA"), 46 | ctrl: BusLine::new("CTRL"), 47 | m1: BusLine::new("M1"), 48 | busak: BusLine::new("BUSAK"), 49 | halt: BusLine::new("HALT"), 50 | wait: BusLine::new("WAIT"), 51 | int: BusLine::new("INT"), 52 | nmi: BusLine::new("NMI"), 53 | reset: BusLine::new("RESET"), 54 | busrq: BusLine::new("BUSRQ"), 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /color_clash/src/widgets/z_stack.rs: -------------------------------------------------------------------------------- 1 | use druid::{ 2 | widget::prelude::*, 3 | Data, WidgetPod, Point 4 | }; 5 | 6 | pub struct ZStack { 7 | children: Vec>>> 8 | } 9 | 10 | impl ZStack { 11 | 12 | pub fn new() -> Self { 13 | Self { children: vec!() } 14 | } 15 | 16 | pub fn add_child(&mut self, child: impl Widget + 'static) { 17 | self.children.push(WidgetPod::new(Box::new(child))); 18 | } 19 | 20 | pub fn with_child(mut self, child: impl Widget + 'static) -> Self { 21 | self.add_child(child); 22 | self 23 | } 24 | 25 | } 26 | 27 | impl Widget for ZStack { 28 | 29 | fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) { 30 | for child in self.children.iter_mut() { 31 | child.event(ctx, event, data, env); 32 | } 33 | } 34 | 35 | fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) { 36 | for child in self.children.iter_mut() { 37 | child.lifecycle(ctx, event, data, env); 38 | } 39 | } 40 | 41 | fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &T, data: &T, env: &Env) { 42 | for child in self.children.iter_mut() { 43 | child.update(ctx, data, env); 44 | } 45 | } 46 | 47 | fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size { 48 | for child in self.children.iter_mut() { 49 | child.layout(ctx, bc, data, env); 50 | child.set_origin(ctx, Point::ORIGIN); 51 | } 52 | bc.max() 53 | } 54 | 55 | fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) { 56 | for child in self.children.iter_mut() { 57 | child.paint(ctx, data, env); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /respectrum_tools/src/disasm.rs: -------------------------------------------------------------------------------- 1 | #![feature(coroutines, coroutine_trait)] 2 | 3 | extern crate librespectrum; 4 | 5 | use clap::Parser; 6 | 7 | use std::{ 8 | fs, 9 | vec::Vec, 10 | pin::Pin, 11 | path::PathBuf, 12 | ops::{Coroutine, CoroutineState}, 13 | io::{self, BufReader, BufRead, Read} 14 | }; 15 | 16 | use librespectrum::cpu::decoder::disassembler; 17 | 18 | /// Maximum bytes to process for each disassembled line 19 | const LINE_BYTES: usize = 4; 20 | 21 | /// Z80 disassembler 22 | #[derive(Parser, Debug)] 23 | #[clap(author, version, about, long_about = None)] 24 | struct Args { 25 | 26 | /// Disassemble base address 27 | #[clap(short, long, value_name = "ADDR", default_value_t = 0)] 28 | base_address: u16, 29 | 30 | /// Binary file to disassemble 31 | #[clap(short, long, value_name = "FILE")] 32 | input_file: Option, 33 | 34 | } 35 | 36 | fn main() { 37 | 38 | let args = Args::parse(); 39 | 40 | let reader: Box = match args.input_file { 41 | None => Box::new(BufReader::new(io::stdin())), 42 | Some(filename) => Box::new(BufReader::new(fs::File::open(filename).unwrap())) 43 | }; 44 | 45 | let mut bytes = reader.bytes(); 46 | let mut disasm = disassembler(args.base_address, LINE_BYTES); 47 | 48 | while let Some(Ok(byte)) = bytes.next() { 49 | 50 | if let CoroutineState::Yielded(Some(line)) = Pin::new(&mut disasm).resume(byte) { 51 | println!( 52 | "{:0>4X}: {:2X}", byte)).collect::>().join(" "), 55 | (if let Some(instr) = line.instruction {instr.format_mnemonic()} else {String::from("...")}), 56 | bytes = LINE_BYTES * 3 - 1 57 | ); 58 | } 59 | 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /librespectrum/src/misc/u16_cell.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fmt, 3 | cell::Cell, 4 | mem::ManuallyDrop, 5 | }; 6 | 7 | #[repr(C)] 8 | /// Stores u16 value in Cell and allows to access high & low bytes individually 9 | pub union U16Cell { 10 | w: ManuallyDrop>, 11 | b: ManuallyDrop, 12 | } 13 | 14 | #[repr(C)] 15 | #[cfg(target_endian = "little")] 16 | pub struct WordBytes { 17 | pub lo: Cell, 18 | pub hi: Cell, 19 | } 20 | 21 | #[repr(C)] 22 | #[cfg(target_endian = "big")] 23 | pub struct WordBytes { 24 | pub hi: Cell, 25 | pub lo: Cell, 26 | } 27 | 28 | impl Default for U16Cell { 29 | 30 | fn default() -> Self { 31 | Self { w: ManuallyDrop::new(Cell::new(0)) } 32 | } 33 | 34 | } 35 | 36 | impl fmt::Debug for U16Cell { 37 | 38 | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 39 | formatter.write_fmt(format_args!("{:04x}h", self.value().get() )) 40 | } 41 | 42 | } 43 | 44 | impl U16Cell { 45 | 46 | pub fn value(&self) -> &Cell { 47 | unsafe { &self.w } 48 | } 49 | 50 | pub fn bytes(&self) -> &WordBytes { 51 | unsafe { &self.b } 52 | } 53 | 54 | } 55 | 56 | #[cfg(test)] 57 | mod tests { 58 | use super::*; 59 | 60 | #[test] 61 | fn allows_to_update_its_value_and_individual_high_and_low_halves() { 62 | 63 | let u16cell: U16Cell = Default::default(); 64 | assert_eq!(u16cell.value().get(), 0); 65 | 66 | u16cell.value().set(0x1234); 67 | assert_eq!(u16cell.value().get(), 0x1234); 68 | assert_eq!(u16cell.bytes().hi.get(), 0x12); 69 | assert_eq!(u16cell.bytes().lo.get(), 0x34); 70 | 71 | u16cell.bytes().hi.set(0xab); 72 | assert_eq!(u16cell.value().get(), 0xab34); 73 | 74 | u16cell.bytes().lo.set(0xcd); 75 | assert_eq!(u16cell.value().get(), 0xabcd); 76 | 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /respectrum_egui/src/windows/mod.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | use eframe::egui::*; 3 | 4 | mod bus_window; 5 | pub use bus_window::BusWindow; 6 | mod cpu_window; 7 | pub use cpu_window::CpuWindow; 8 | mod disassm_window; 9 | pub use disassm_window::DisassmWindow; 10 | mod memory_window; 11 | pub use memory_window::MemoryWindow; 12 | 13 | pub trait SubWindow { 14 | 15 | /// Window name 16 | fn name(&self) -> String; 17 | 18 | /// Window draw function 19 | fn show(&mut self, ctx: &Context, focused: bool) -> Response; 20 | 21 | } 22 | 23 | /// Draw widow with title 24 | pub fn draw_window( 25 | name: impl Into + Hash, 26 | focused: bool, 27 | ctx: &Context, 28 | add_contents: impl FnOnce(&mut Ui) -> (), 29 | ) -> Response { 30 | 31 | Area::new(&name).show(ctx, |ui| { 32 | 33 | Frame::window(&ctx.style()) 34 | .inner_margin(style::Margin::same(0.0)) 35 | .fill(if focused {Color32::LIGHT_BLUE} else {Color32::LIGHT_GRAY}) 36 | .show(ui, |ui| 37 | { 38 | 39 | Frame::window(&ctx.style()) 40 | .stroke(Stroke::none()) 41 | .fill(Color32::TRANSPARENT) 42 | .show(ui, |ui| 43 | { 44 | ui.add(Label::new(RichText::new(name).color( 45 | if focused {Color32::BLACK} else {Color32::GRAY} 46 | ))); 47 | }); 48 | 49 | ui.add_space(-3.0); 50 | 51 | Frame::window(&ctx.style()) 52 | .rounding(Rounding { 53 | nw: 0.0, ne: 0.0, 54 | ..ctx.style().visuals.window_rounding 55 | }) 56 | .stroke(Stroke::none()) 57 | .show(ui, |ui| 58 | { 59 | add_contents(ui); 60 | }); 61 | 62 | }); 63 | 64 | }).response 65 | 66 | } 67 | 68 | pub fn cursor_color(focused: bool) -> Color32 { 69 | if focused {Color32::LIGHT_BLUE} else {Color32::LIGHT_GRAY} 70 | } 71 | -------------------------------------------------------------------------------- /librespectrum/src/devs/bus_logger.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc, cell::RefCell}; 2 | 3 | use crate::{ 4 | bus::{Clock, CpuBus, NoReturnTask, Ctrl}, 5 | devs::Device, misc::{Identifiable, RingBuff}, 6 | }; 7 | 8 | #[derive(Default, Clone, Copy)] 9 | pub struct BusState { 10 | pub htcyc: u64, 11 | pub addr: Option, 12 | pub data: Option, 13 | pub ctrl: Option, 14 | pub m1: Option, 15 | pub busak: Option, 16 | pub halt: Option, 17 | pub wait: Option, 18 | pub int: Option, 19 | pub nmi: Option, 20 | pub reset: Option, 21 | pub busrq: Option, 22 | } 23 | 24 | /// CPU bus logger 25 | pub struct BusLogger { 26 | bus: Rc, 27 | clock: Rc, 28 | pub readings: RefCell>, 29 | } 30 | 31 | impl BusLogger { 32 | pub fn new(bus: Rc, clock: Rc) -> Self { 33 | Self { bus, clock, readings: RefCell::new(RingBuff::new()) } 34 | } 35 | } 36 | 37 | impl Identifiable for BusLogger { 38 | fn id(&self) -> u32 { 10 } 39 | } 40 | 41 | impl Device for BusLogger { 42 | 43 | fn run<'a>(&'a self) -> Box { 44 | 45 | Box::new(#[coroutine] move || { 46 | 47 | loop { 48 | 49 | let state = BusState { 50 | htcyc: self.clock.get(), 51 | addr: self.bus.addr.probe(), 52 | data: self.bus.data.probe(), 53 | ctrl: self.bus.ctrl.probe(), 54 | m1: self.bus.m1.probe(), 55 | busak: self.bus.busak.probe(), 56 | halt: self.bus.halt.probe(), 57 | wait: self.bus.wait.probe(), 58 | int: self.bus.int.probe(), 59 | nmi: self.bus.nmi.probe(), 60 | reset: self.bus.reset.probe(), 61 | busrq: self.bus.busrq.probe(), 62 | }; 63 | 64 | self.readings.borrow_mut().push(state); 65 | 66 | yield 1; 67 | 68 | } 69 | 70 | }) 71 | 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /librespectrum/src/cpu/flags.rs: -------------------------------------------------------------------------------- 1 | use crate::cpu::tokens::Condition; 2 | 3 | bitflags! { 4 | /// CPU flags 5 | pub struct Flags : u8 { 6 | /// No flags set 7 | const NONE = 0; 8 | /// Carry flag 9 | const C = 1 << 0; 10 | /// Add / Subtract flag 11 | const N = 1 << 1; 12 | /// Parity / Overflow flag 13 | const P = 1 << 2; 14 | /// Bit 3 of the result 15 | const X = 1 << 3; 16 | /// Half Carry flag 17 | const H = 1 << 4; 18 | /// Bit 5 of the result 19 | const Y = 1 << 5; 20 | /// Zero flag 21 | const Z = 1 << 6; 22 | /// Sign flag 23 | const S = 1 << 7; 24 | /// Bits 3 & 5 of the result 25 | const XY = Self::X.bits | Self::Y.bits; 26 | } 27 | } 28 | 29 | impl From for Flags { 30 | fn from(value: u8) -> Self { 31 | unsafe { Flags::from_bits_unchecked(value) } 32 | } 33 | } 34 | 35 | impl Flags { 36 | pub fn set_zs_flags_u8(&mut self, value: u8) -> &mut Self { 37 | self.set(Flags::Z, value == 0); 38 | self.set(Flags::S, value.cast_signed() < 0); 39 | self 40 | } 41 | 42 | pub fn set_zs_flags_u16(&mut self, value: u16) -> &mut Self { 43 | self.set(Flags::Z, value == 0); 44 | self.set(Flags::S, value.cast_signed() < 0); 45 | self 46 | } 47 | 48 | pub fn set_parity_flag(&mut self, value: u8) -> &mut Self { 49 | let mut value = value; 50 | value ^= value >> 4; 51 | value ^= value >> 2; 52 | value ^= value >> 1; 53 | self.set(Flags::P, value & 1 != 0); 54 | self 55 | } 56 | 57 | pub fn satisfy(&self, condition: Condition) -> bool { 58 | match condition { 59 | Condition::NZ => !self.contains(Flags::Z), 60 | Condition::Z => self.contains(Flags::Z), 61 | Condition::NC => !self.contains(Flags::C), 62 | Condition::C => self.contains(Flags::C), 63 | Condition::PO => !self.contains(Flags::P), 64 | Condition::PE => self.contains(Flags::P), 65 | Condition::P => !self.contains(Flags::S), 66 | Condition::M => self.contains(Flags::S), 67 | Condition::None => true, 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /color_clash/src/models/attributes.rs: -------------------------------------------------------------------------------- 1 | use druid::Data; 2 | use bitflags::bitflags; 3 | 4 | use crate::palette::ZXColor; 5 | 6 | bitflags! { 7 | /// Color, brightness and flash attributes 8 | #[derive(Data)] 9 | pub struct Attributes: u8 { 10 | const FLASH = 0b10000000; 11 | const BRIGHT = 0b01000000; 12 | const PAPER_MASK = 0b00111000; 13 | const INK_MASK = 0b00000111; 14 | } 15 | } 16 | 17 | impl Default for Attributes { 18 | fn default() -> Self { 19 | 0b00111000.into() 20 | } 21 | } 22 | 23 | impl From for Attributes where T: Into { 24 | fn from(value: T) -> Self { 25 | Self::from_bits_truncate(value.into()) 26 | } 27 | } 28 | 29 | impl Attributes { 30 | 31 | pub fn get_paper(&self) -> ZXColor { 32 | ((*self & Attributes::PAPER_MASK).bits >> 3).into() 33 | } 34 | 35 | pub fn get_ink(&self) -> ZXColor { 36 | (*self & Attributes::INK_MASK).bits.into() 37 | } 38 | 39 | pub fn set_paper(&mut self, paper: impl Into) { 40 | *self &= !Attributes::PAPER_MASK; 41 | *self |= ((paper.into().index() as u8 & 7) << 3).into(); 42 | } 43 | 44 | pub fn set_ink(&mut self, ink: impl Into) { 45 | *self &= !Attributes::INK_MASK; 46 | *self |= (ink.into().index() as u8 & 7).into(); 47 | } 48 | 49 | pub fn get_paper_rgb(&self) -> [u8; 3] { 50 | self.get_rgb(self.get_paper()) 51 | } 52 | 53 | pub fn get_ink_rgb(&self) -> [u8; 3] { 54 | self.get_rgb(self.get_ink()) 55 | } 56 | 57 | fn get_rgb(&self, color: ZXColor) -> [u8; 3] { 58 | if self.contains(Attributes::BRIGHT) { 59 | color.rgb_bright() 60 | } else { 61 | color.rgb_dim() 62 | } 63 | } 64 | 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | use super::*; 70 | 71 | #[test] 72 | fn allows_to_update_individual_attributes() { 73 | let mut attr: Attributes = Default::default(); 74 | assert!(!attr.contains(Attributes::FLASH | Attributes::BRIGHT)); 75 | assert_eq!(attr.get_paper(), 7u8); 76 | assert_eq!(attr.get_ink(), 0u8); 77 | attr.set_paper(3u8); 78 | attr.set_ink(5u8); 79 | attr |= Attributes::FLASH | Attributes::BRIGHT; 80 | assert_eq!(attr.bits, 0b11011101); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /librespectrum/src/devs/mem/dynamic_48k.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::Cell, rc::Rc}; 2 | 3 | use crate::{ 4 | bus::{Clock, CpuBus, Ctrl, NoReturnTask}, 5 | devs::Device, misc::Identifiable, 6 | }; 7 | use super::Memory; 8 | 9 | /// Standard dynamic 48k memory 10 | pub struct Dynamic48k { 11 | bus: Rc, 12 | clock: Rc, 13 | memory: Vec>, 14 | } 15 | 16 | impl Dynamic48k { 17 | 18 | /// Create new memory instance 19 | pub fn new(bus: Rc, clock: Rc) -> Self { 20 | Self { 21 | bus, clock, 22 | memory: vec![Default::default(); usize::pow(2, 16)] 23 | } 24 | } 25 | 26 | /// Load data to the memory at the given address 27 | pub fn load(&self, addr: u16, data: &Vec) { 28 | for (index, &byte) in data.iter().enumerate() { 29 | self.memory[addr as usize + index].set(byte) 30 | } 31 | } 32 | 33 | } 34 | 35 | impl Memory for Dynamic48k { 36 | 37 | fn writable(&self, addr: u16) -> bool { 38 | addr & 0xc000 != 0 // First 16KB are not writable (ROM) 39 | } 40 | 41 | fn write(&self, addr: u16, byte: u8) { 42 | if self.writable(addr) { 43 | self.memory[addr as usize].set(byte); 44 | } 45 | } 46 | 47 | fn read(&self, addr: u16) -> u8 { 48 | self.memory[addr as usize].get() 49 | } 50 | 51 | } 52 | 53 | impl Identifiable for Dynamic48k { 54 | fn id(&self) -> u32 { 2 } 55 | } 56 | 57 | impl Device for Dynamic48k { 58 | 59 | fn run<'a>(&'a self) -> Box { 60 | 61 | Box::new(#[coroutine] move || { 62 | 63 | loop { 64 | 65 | // Wait for MREQ 66 | while !self.bus.ctrl.probe().unwrap_or(Ctrl::NONE).contains(Ctrl::MREQ) { 67 | yield self.clock.rising(1); 68 | } 69 | 70 | let addr = self.bus.addr.expect(); 71 | let ctrl = self.bus.ctrl.expect(); 72 | 73 | // Perform read or write 74 | if ctrl.contains(Ctrl::RD) { 75 | self.bus.data.drive(self, self.read(addr)); 76 | yield self.clock.rising(3); 77 | self.bus.data.release(self); 78 | } else if ctrl.contains(Ctrl::WR) { 79 | let byte = self.bus.data.expect(); 80 | self.write(addr, byte); 81 | yield self.clock.rising(2); 82 | } 83 | 84 | } 85 | 86 | }) 87 | 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /librespectrum/src/misc/ring_buff.rs: -------------------------------------------------------------------------------- 1 | 2 | pub struct RingBuff { 3 | items: [Option; N], 4 | head: usize, 5 | } 6 | 7 | impl RingBuff { 8 | 9 | pub fn new() -> Self { 10 | assert!(N > 0); 11 | Self { items: [None; N], head: 0 } 12 | } 13 | 14 | pub fn from(items: [T; K]) -> Self { 15 | assert!(K <= N); 16 | let mut buff = Self::new(); 17 | for (i, &item) in items.iter().enumerate() { 18 | buff.items[i] = Some(item); 19 | } 20 | buff.head = Self::next(K - 1); 21 | buff 22 | } 23 | 24 | pub fn len(&self) -> usize { 25 | if self.items[self.head].is_some() { N } else { self.head } 26 | } 27 | 28 | pub fn push(&mut self, item: T) -> &mut Self { 29 | self.items[self.head] = Some(item); 30 | self.head = Self::next(self.head); 31 | self 32 | } 33 | 34 | pub fn iter_to_tail<'a>(&'a self) -> RingBuffIterator<'a, T, N> { 35 | RingBuffIterator { 36 | items: &self.items, 37 | index: Self::prev(self.head), 38 | count: self.len(), 39 | } 40 | } 41 | 42 | pub fn next(index: usize) -> usize { 43 | if index == N - 1 { 0 } else { index + 1 } 44 | } 45 | 46 | pub fn prev(index: usize) -> usize { 47 | if index == 0 { N - 1 } else { index - 1 } 48 | } 49 | 50 | } 51 | 52 | pub struct RingBuffIterator<'a, T, const N: usize> { 53 | items: &'a [Option; N], 54 | index: usize, 55 | count: usize, 56 | } 57 | 58 | impl Iterator for RingBuffIterator<'_, T, N> { 59 | 60 | type Item = T; 61 | 62 | fn next(&mut self) -> Option { 63 | 64 | let next = if self.count > 0 { self.items[self.index] } else { None }; 65 | if next.is_some() { 66 | self.index = RingBuff::::prev(self.index); 67 | self.count -= 1; 68 | } 69 | next 70 | 71 | } 72 | 73 | } 74 | 75 | #[cfg(test)] 76 | mod tests { 77 | use super::*; 78 | 79 | #[test] 80 | fn returns_length() { 81 | let buff = RingBuff::::from([1, 2]); 82 | assert_eq!(buff.len(), 2); 83 | } 84 | 85 | #[test] 86 | fn allows_to_iterate_through_items() { 87 | let buff = RingBuff::::from([1, 2, 3]); 88 | assert_eq!(buff.iter_to_tail().collect::>(), vec![3, 2, 1]); 89 | } 90 | 91 | #[test] 92 | fn behaves_like_ring_buffer() { 93 | let mut buff = RingBuff::::from([1, 2]); 94 | assert_eq!(buff.push(3).push(4).len(), 3); 95 | assert_eq!(buff.iter_to_tail().collect::>(), vec![4, 3, 2]); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /color_clash/src/widgets/frame_buffer_view.rs: -------------------------------------------------------------------------------- 1 | use druid::{ 2 | piet::{ImageFormat, InterpolationMode, StrokeStyle}, 3 | kurbo::Rect, 4 | widget::prelude::*, 5 | Event, MouseButton, Point, Color, 6 | }; 7 | 8 | use crate::models::FrameBuffer; 9 | 10 | pub struct FrameBufferView { 11 | pub base_zoom: f64, 12 | pub zoom: f64, 13 | pub image: Vec, 14 | } 15 | 16 | impl FrameBufferView { 17 | 18 | pub fn new() -> Self { 19 | Self { base_zoom: 2.0, zoom: 1.0, image: vec!() } 20 | } 21 | 22 | fn effective_zoom(&self) -> f64 { 23 | self.base_zoom * self.zoom 24 | } 25 | 26 | fn widget2fb_coords(&self, point: Point) -> (usize, usize) { 27 | ( 28 | (point.x / self.effective_zoom()) as usize, 29 | (point.y / self.effective_zoom()) as usize 30 | ) 31 | } 32 | 33 | } 34 | 35 | impl Widget for FrameBufferView { 36 | 37 | fn event(&mut self, _ctx: &mut EventCtx, event: &Event, data: &mut FrameBuffer, _env: &Env) { 38 | match event { 39 | Event::MouseDown(e) if e.button == MouseButton::Left => { 40 | let (x, y) = self.widget2fb_coords(e.pos); 41 | data.pixels[data.width * y + x] = true; 42 | }, 43 | Event::MouseMove(_e) => {}, 44 | _ => {} 45 | } 46 | } 47 | 48 | fn lifecycle(&mut self, _ctx: &mut LifeCycleCtx, _event: &LifeCycle, _data: &FrameBuffer, _env: &Env) {} 49 | 50 | fn update(&mut self, ctx: &mut UpdateCtx, old_data: &FrameBuffer, data: &FrameBuffer, _env: &Env) { 51 | if !data.same(old_data) { 52 | self.image = data.to_rgb_image(); 53 | ctx.request_paint(); 54 | } 55 | } 56 | 57 | fn layout(&mut self, _ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &FrameBuffer, _env: &Env) -> Size { 58 | bc.constrain(( 59 | data.width as f64 * self.base_zoom * self.zoom, 60 | data.height as f64 * self.base_zoom * self.zoom 61 | )) 62 | } 63 | 64 | fn paint(&mut self, ctx: &mut PaintCtx, data: &FrameBuffer, _env: &Env) { 65 | 66 | if self.image.is_empty() { 67 | self.image = data.to_rgb_image(); 68 | } 69 | 70 | // Draw image 71 | let image_size = ctx.size(); 72 | let image = ctx.make_image(data.width, data.height, &self.image, ImageFormat::Rgb).unwrap(); 73 | ctx.draw_image(&image, image_size.to_rect(), InterpolationMode::NearestNeighbor); 74 | 75 | // Draw attribute grid 76 | let stroke_style = StrokeStyle::new().dash_pattern(&[4.0, 4.0]); 77 | ctx.stroke_styled( 78 | Rect::from_origin_size(Point::ORIGIN, image_size), 79 | &Color::YELLOW, 1.0, &stroke_style 80 | ); 81 | 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /librespectrum/tests/disassembler_tests.rs: -------------------------------------------------------------------------------- 1 | #![feature(coroutines, coroutine_trait)] 2 | 3 | extern crate librespectrum; 4 | 5 | use std::{ 6 | fs::File, 7 | pin::Pin, 8 | io::{self, BufRead}, 9 | ops::{Coroutine, CoroutineState} 10 | }; 11 | 12 | use librespectrum::cpu::decoder::instruction_decoder; 13 | 14 | #[test] 15 | fn disassembler_recognizes_all_z80_opcodes() { 16 | 17 | // File with reference listing 18 | let file = File::open("tests/misc/opcodes.lst").unwrap(); 19 | let mut lines = io::BufReader::new(file).lines().enumerate(); 20 | 21 | // Iterate over listing lines 22 | while let Some((line_idx, Ok(line))) = lines.next() { 23 | 24 | let report_failure = |details: String| { 25 | panic!("Failure at line {}:\n{}\nDetails: {}", line_idx + 1, line, details); 26 | }; 27 | 28 | // Line body excluding possible comments after ";" 29 | let body = if let Some(index) = line.find(";") { &line[..index] } else { &line[..] }; 30 | 31 | if body.is_empty() { continue; } 32 | 33 | // Split opcode bytes and parsed disassembled mnemonic 34 | let mut parts = body.split('|'); 35 | 36 | let mut bytes_iter = parts.next().unwrap().trim().split(" ") 37 | .map(|s| u8::from_str_radix(s, 16).unwrap()) 38 | .enumerate().peekable(); 39 | let expected_mnemonic = parts.next().unwrap().trim(); 40 | 41 | let mut decoder = instruction_decoder(); 42 | 43 | // Feed bytes to disassembler and observe results 44 | while let Some((byte_num, byte)) = bytes_iter.next() { 45 | 46 | let result = Pin::new(&mut decoder).resume(byte); 47 | 48 | if bytes_iter.peek().is_some() { 49 | 50 | // Some bytes left in current opcode, disassembler should yield nothing 51 | if let CoroutineState::Complete(instruction) = result { 52 | report_failure(format!( 53 | "Unexpected output when parsing byte number {}: {}", 54 | byte_num, instruction 55 | )); 56 | } 57 | 58 | } else { 59 | 60 | // It's the last byte for current opcode, disassembler should yield a line 61 | if let CoroutineState::Complete(instruction) = result { 62 | 63 | let formatted_mnemonic = instruction.format_mnemonic(); 64 | if formatted_mnemonic != expected_mnemonic { 65 | report_failure(format!( 66 | "Wrong mnemonic. Expecting: {}, got: {}", 67 | expected_mnemonic, formatted_mnemonic 68 | )); 69 | } 70 | 71 | } else { 72 | report_failure(String::from("No output on last opcode byte")); 73 | } 74 | 75 | } 76 | 77 | } 78 | 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /color_clash/src/models/frame_buffer.rs: -------------------------------------------------------------------------------- 1 | use std::io::BufRead; 2 | use bitvec::prelude::*; 3 | use druid::{ 4 | Data, Lens, 5 | im::Vector, 6 | }; 7 | 8 | use crate::models::{VideoMode, Attributes}; 9 | 10 | /// ZX Spectrum frame buffer 11 | #[derive(Clone, Lens, Data)] 12 | pub struct FrameBuffer { 13 | /// Attribute columns 14 | pub cols: usize, 15 | /// Attribute rows 16 | pub rows: usize, 17 | /// Width in pixels 18 | pub width: usize, 19 | /// Height in pixels 20 | pub height: usize, 21 | /// Frame pixels 22 | pub pixels: Vector, 23 | /// Frame attributes 24 | pub attrs: Vector, 25 | } 26 | 27 | impl FrameBuffer { 28 | 29 | /// Creates frame buffer for given video mode 30 | pub fn new_for(mode: VideoMode) -> Self { 31 | 32 | let (cols, rows, width, height) = match mode { 33 | VideoMode::STD8X8 => (32usize, 24usize, 256usize, 192usize), 34 | VideoMode::MUL8X4 => (32usize, 48usize, 256usize, 192usize), 35 | }; 36 | 37 | Self { 38 | cols, rows, width, height, 39 | pixels: vec![false; width * height].into(), 40 | attrs: vec![Default::default(); cols * rows].into(), 41 | } 42 | 43 | } 44 | 45 | /// Reads framebuffer from file 46 | pub fn load(mut reader: Box) -> Self { 47 | 48 | let mut buffer = [Default::default(); 6912]; 49 | reader.read_exact(&mut buffer).unwrap(); 50 | 51 | let (cols, rows, width, height) = (32usize, 24usize, 256usize, 192usize); 52 | let (pix_buf, attr_buf) = buffer.split_at(cols * height); 53 | 54 | let mut pixels = Vector::::new(); 55 | 56 | for line in 0..height { 57 | let line_addr = (line & 0xc0) << 5 | (line & 0x7) << 8 | (line & 0x38) << 2; 58 | let line_buf = &pix_buf[line_addr..line_addr+cols]; 59 | pixels.append(line_buf.view_bits::().iter().map(|x| x == true).collect()); 60 | } 61 | 62 | Self { 63 | cols, rows, width, height, pixels, 64 | attrs: attr_buf.iter().map(|x| (*x).into()).collect(), 65 | } 66 | 67 | } 68 | 69 | /// Returns framebuffer as RGB pixel array 70 | pub fn to_rgb_image(&self) -> Vec { 71 | 72 | let mut result: Vec = Vec::with_capacity(self.width * self.height * 3); 73 | 74 | for y in 0..self.height { 75 | let row = y * self.rows / self.height; 76 | 77 | for x in 0..self.width { 78 | let col = x * self.cols / self.width; 79 | let attr = &self.attrs[row * self.cols + col]; 80 | let pixel_rgb = if self.pixels[y * self.width + x] { 81 | attr.get_ink_rgb() 82 | } else { 83 | attr.get_paper_rgb() 84 | }; 85 | result.extend(pixel_rgb); 86 | } 87 | } 88 | 89 | result 90 | 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /librespectrum/src/bus/bus_line.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | 3 | use crate::misc::Identifiable; 4 | 5 | /// Signal bus line 6 | pub struct BusLine { 7 | 8 | /// Line name 9 | name: &'static str, 10 | 11 | /// Line owner and state 12 | state: Cell>, 13 | 14 | } 15 | 16 | impl BusLine { 17 | 18 | /// Create new bus line 19 | pub fn new(name: &'static str) -> Self { 20 | Self { name, state: Cell::new(None) } 21 | } 22 | 23 | /// Bus line name 24 | pub fn name(&self) -> &str { 25 | self.name 26 | } 27 | 28 | /// Get line owner (if any) 29 | pub fn owner(&self) -> Option { 30 | self.state.get().and_then(|(owner, ..)| Some(owner)) 31 | } 32 | 33 | /// Probe signal line 34 | pub fn probe(&self) -> Option { 35 | self.state.get().and_then(|(.., value)| Some(value)) 36 | } 37 | 38 | /// Expect signal on the line 39 | pub fn expect(&self) -> T { 40 | self.probe().unwrap() 41 | } 42 | 43 | /// Drive signal line 44 | pub fn drive(&self, device: &U, value: T) { 45 | match self.state.get() { 46 | None => self.state.set(Some((device.id(), value))), 47 | Some((owner, ..)) if owner == device.id() => self.state.set(Some((owner, value))), 48 | Some((owner, ..)) => panic!("Device {} conflicts with {} on a bus line {}", device.id(), owner, self.name) 49 | } 50 | } 51 | 52 | /// Release signal line 53 | pub fn release(&self, device: &U) { 54 | match self.state.get() { 55 | None => (), 56 | Some((owner, ..)) if owner == device.id() => self.state.set(None), 57 | Some((..)) => panic!("Device {} doesn't own the line {}", device.id(), self.name) 58 | } 59 | } 60 | 61 | } 62 | 63 | #[cfg(test)] 64 | mod tests { 65 | 66 | use super::*; 67 | 68 | fn mkline() -> BusLine:: { BusLine::::new("Test line") } 69 | 70 | struct TestDevice { 71 | id: u32 72 | } 73 | 74 | impl Identifiable for TestDevice { 75 | fn id(&self) -> u32 { self.id } 76 | } 77 | 78 | static DEV1: TestDevice = TestDevice { id: 1 }; 79 | static DEV2: TestDevice = TestDevice { id: 2 }; 80 | 81 | #[test] 82 | fn line_drive_and_probe() { 83 | let line = mkline(); 84 | assert_eq!(line.probe(), None); 85 | line.drive(&DEV1, false); 86 | assert_eq!(line.probe(), Some(false)); 87 | line.drive(&DEV1, true); 88 | assert_eq!(line.probe(), Some(true)); 89 | line.release(&DEV1); 90 | assert_eq!(line.probe(), None); 91 | line.drive(&DEV2, true); 92 | assert_eq!(line.probe(), Some(true)); 93 | } 94 | 95 | #[test] 96 | #[should_panic(expected="conflict")] 97 | fn only_one_device_can_drive_the_line() { 98 | let line = mkline(); 99 | line.drive(&DEV1, false); 100 | line.drive(&DEV2, true); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /respectrum_egui/src/windows/cpu_window.rs: -------------------------------------------------------------------------------- 1 | use egui::*; 2 | use librespectrum::{cpu::Flags, bus::Scheduler, devs::Cpu}; 3 | use std::{rc::Rc, cell::RefCell}; 4 | 5 | use super::{SubWindow, draw_window}; 6 | 7 | pub struct CpuWindow<'a> { 8 | cpu: Rc, 9 | scheduler: Rc>>, 10 | } 11 | 12 | impl<'a> CpuWindow<'a> { 13 | pub fn new(cpu: Rc, scheduler: Rc>>) -> Self { 14 | Self { cpu, scheduler } 15 | } 16 | 17 | fn handle_keyboard(&mut self, input: &InputState) { 18 | if input.key_pressed(Key::Enter) { 19 | self.scheduler.borrow_mut().advance(1); 20 | } 21 | } 22 | } 23 | 24 | fn reg_label(ui: &mut Ui, label: &str, value: u16) { 25 | ui.label(label); 26 | ui.label(format!("{:04X}", value)); 27 | } 28 | 29 | fn flag_label(ui: &mut Ui, label: &str, is_set: bool) { 30 | let color = if is_set { Color32::GREEN } else { Color32::GRAY }; 31 | ui.colored_label(color, label); 32 | } 33 | 34 | impl SubWindow for CpuWindow<'_> { 35 | 36 | fn name(&self) -> String { String::from("CPU") } 37 | 38 | fn show(&mut self, ctx: &Context, focused: bool) -> Response { 39 | 40 | draw_window(self.name(), focused, ctx, |ui| { 41 | 42 | self.handle_keyboard(&ui.input()); 43 | 44 | Grid::new("cpu_regs").min_col_width(20.0).show(ui, |ui| { 45 | 46 | reg_label(ui, "AF:", self.cpu.af.value().get()); 47 | reg_label(ui, "AF':", self.cpu.alt_af.value().get()); 48 | ui.end_row(); 49 | 50 | reg_label(ui, "BC:", self.cpu.bc.value().get()); 51 | reg_label(ui, "BC':", self.cpu.alt_bc.value().get()); 52 | ui.end_row(); 53 | 54 | reg_label(ui, "DE:", self.cpu.de.value().get()); 55 | reg_label(ui, "DE':", self.cpu.alt_de.value().get()); 56 | ui.end_row(); 57 | 58 | reg_label(ui, "HL:", self.cpu.hl.value().get()); 59 | reg_label(ui, "HL':", self.cpu.alt_hl.value().get()); 60 | ui.end_row(); 61 | 62 | reg_label(ui, "IX:", self.cpu.ix.value().get()); 63 | reg_label(ui, "IY:", self.cpu.iy.value().get()); 64 | ui.end_row(); 65 | 66 | reg_label(ui, "PC:", self.cpu.pc.value().get()); 67 | reg_label(ui, "SP:", self.cpu.sp.value().get()); 68 | ui.end_row(); 69 | 70 | }); 71 | 72 | ui.horizontal(|ui| { 73 | let flags = Flags::from(self.cpu.af.bytes().lo.get()); 74 | flag_label(ui, "C", flags.contains(Flags::C)); 75 | flag_label(ui, "N", flags.contains(Flags::N)); 76 | flag_label(ui, "P", flags.contains(Flags::P)); 77 | flag_label(ui, "X", flags.contains(Flags::X)); 78 | flag_label(ui, "H", flags.contains(Flags::H)); 79 | flag_label(ui, "Y", flags.contains(Flags::Y)); 80 | flag_label(ui, "Z", flags.contains(Flags::Z)); 81 | flag_label(ui, "S", flags.contains(Flags::S)); 82 | }); 83 | 84 | ui.horizontal(|ui| { 85 | ui.label(format!("IM{}", self.cpu.im.get())); 86 | flag_label(ui, "IFF1", self.cpu.iff1.get()); 87 | flag_label(ui, "IFF2", self.cpu.iff2.get()); 88 | }); 89 | 90 | }) 91 | 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /respectrum_egui/src/emul.rs: -------------------------------------------------------------------------------- 1 | #![feature(coroutines, coroutine_trait)] 2 | 3 | extern crate librespectrum; 4 | 5 | use librespectrum::{ 6 | bus::{CpuBus, Clock, Scheduler}, 7 | devs::{mem::{Dynamic48k, Memory}, Device, Cpu, BusLogger} 8 | }; 9 | 10 | use std::{ 11 | rc::Rc, 12 | vec::Vec, 13 | fs::File, 14 | io::Read, 15 | ops::Deref, 16 | cell::RefCell, 17 | }; 18 | 19 | mod windows; 20 | use windows::{SubWindow, CpuWindow, DisassmWindow, MemoryWindow, BusWindow}; 21 | 22 | struct EmulApp<'a> { 23 | windows: Vec<(bool, Box)>, 24 | focus: usize, 25 | } 26 | 27 | impl eframe::App for EmulApp<'_> { 28 | 29 | fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { 30 | 31 | let mut style = ctx.style().deref().clone(); 32 | style.override_text_style = Some(egui::TextStyle::Monospace); 33 | ctx.set_style(style); 34 | 35 | egui::TopBottomPanel::top("top_panel").show(ctx, |ui| { 36 | egui::menu::bar(ui, |ui| { 37 | ui.menu_button("File", |ui| { 38 | if ui.button("Quit").clicked() { 39 | frame.quit(); 40 | } 41 | }); 42 | ui.menu_button("Window", |ui| { 43 | for (open, window) in &mut self.windows { 44 | ui.checkbox(open, window.name()); 45 | } 46 | }); 47 | ui.with_layout(egui::Layout::right_to_left(), |ui| { 48 | egui::warn_if_debug_build(ui); 49 | }); 50 | }); 51 | }); 52 | 53 | for (idx, (open, window)) in self.windows.iter_mut().enumerate() { 54 | if *open { 55 | let response = window.show(ctx, self.focus == idx); 56 | if response.clicked() || response.drag_started() { 57 | self.focus = idx; 58 | } 59 | } 60 | } 61 | 62 | } 63 | 64 | } 65 | 66 | fn main() { 67 | 68 | let bus: Rc = Default::default(); 69 | let clock: Rc = Default::default(); 70 | let cpu = Rc::new(Cpu::new(Rc::clone(&bus), Rc::clone(&clock))); 71 | let mem: Rc = { 72 | let mem = Rc::new(Dynamic48k::new(Rc::clone(&bus), Rc::clone(&clock))); 73 | let mut buffer: Vec = Vec::new(); 74 | File::open("roms/48.rom").unwrap().read_to_end(&mut buffer).unwrap(); 75 | mem.load(0, &buffer); 76 | mem 77 | }; 78 | let logger = Rc::new(BusLogger::new(Rc::clone(&bus), Rc::clone(&clock))); 79 | 80 | let scheduler = Rc::new(RefCell::new( 81 | Scheduler::new(Rc::clone(&clock), vec![cpu.run(), mem.run(), logger.run()]) 82 | )); 83 | 84 | let app = Box::new(EmulApp { 85 | windows: vec![ 86 | (true, Box::new(CpuWindow::new(Rc::clone(&cpu), Rc::clone(&scheduler)))), 87 | (true, Box::new(DisassmWindow::new(Rc::clone(&cpu), Rc::clone(&mem)))), 88 | (true, Box::new(MemoryWindow::new(Rc::clone(&mem)))), 89 | (true, Box::new(BusWindow::new(Rc::clone(&logger)))), 90 | ], 91 | focus: 0, 92 | }); 93 | 94 | run_native(app); 95 | 96 | } 97 | 98 | #[allow(unsafe_code)] 99 | fn run_native<'a>(app: Box) -> ! { 100 | 101 | let native_options = eframe::NativeOptions { 102 | initial_window_size: Some(egui::vec2(1024.0, 768.0)), 103 | min_window_size: Some(egui::vec2(800.0, 600.0)), 104 | ..Default::default() 105 | }; 106 | 107 | let static_app = unsafe { 108 | std::mem::transmute::, Box>(app) 109 | }; 110 | 111 | eframe::run_native( 112 | "reSpectrum - ZX Spectrum emulator", 113 | native_options, 114 | Box::new(|_| static_app), 115 | ); 116 | 117 | } 118 | -------------------------------------------------------------------------------- /color_clash/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{self, BufReader}, 3 | path::PathBuf, 4 | fs::File, 5 | }; 6 | use druid::{ 7 | piet::Color, 8 | widget::{Button, Flex, BackgroundBrush, Label}, 9 | WindowId, Menu, Env, AppLauncher, Data, Lens, 10 | PlatformError, Widget, WidgetExt, WindowDesc, 11 | }; 12 | use clap::Parser; 13 | 14 | mod models; 15 | mod palette; 16 | mod widgets; 17 | 18 | use crate::{ 19 | models::{VideoMode, FrameBuffer}, 20 | palette::{BlendingMode, ZXColor}, 21 | widgets::{ZStack, FrameBufferView}, 22 | }; 23 | 24 | #[derive(Parser, Debug)] 25 | #[clap(name = "Color Clash", version, author)] 26 | #[clap(about = "ZX Spectrum graphics editor")] 27 | #[clap(long_about = None)] 28 | struct Args { 29 | 30 | /// File to open 31 | #[clap(short, long, value_name = "FILE")] 32 | open: Option, 33 | 34 | /// Read image data from stdin 35 | #[clap(short, long)] 36 | stdin: bool, 37 | 38 | } 39 | 40 | #[derive(Clone, Lens, Data)] 41 | pub struct AppState { 42 | frame_buffer: FrameBuffer, 43 | blending_mode: BlendingMode, 44 | } 45 | 46 | fn main() -> Result<(), PlatformError> { 47 | 48 | let args = Args::parse(); 49 | 50 | let frame_buffer: FrameBuffer = if args.stdin { 51 | FrameBuffer::load(Box::new(BufReader::new(io::stdin()))) 52 | } else if let Some(filename) = args.open { 53 | FrameBuffer::load(Box::new(BufReader::new(File::open(filename).unwrap()))) 54 | } else { 55 | FrameBuffer::new_for(VideoMode::STD8X8) 56 | }; 57 | 58 | let state = AppState { 59 | frame_buffer, 60 | blending_mode: Default::default(), 61 | }; 62 | 63 | let main_window = WindowDesc::new(ui_builder()) 64 | .menu(make_menu) 65 | .title("Color Clash"); 66 | 67 | AppLauncher::with_window(main_window) 68 | .log_to_console() 69 | .launch(state) 70 | 71 | } 72 | 73 | fn ui_builder() -> impl Widget { 74 | 75 | fn color_label(text: &str, color: Color) -> impl Widget { 76 | Label::new(text) 77 | // .with_text_color(Color::) 78 | .center() 79 | .fix_size(32.0, 32.0) 80 | .background(BackgroundBrush::Color(color)) 81 | } 82 | 83 | let mut dim_colors = Flex::column(); 84 | let mut bri_colors = Flex::column(); 85 | for color in ZXColor::palette() { 86 | let [r, g, b] = color.rgb_dim(); 87 | dim_colors.add_child(color_label("", Color::rgb8(r, g, b))); 88 | let [r, g, b] = color.rgb_bright(); 89 | bri_colors.add_child(color_label("", Color::rgb8(r, g, b))); 90 | } 91 | 92 | ZStack::new() 93 | .with_child( 94 | FrameBufferView::new() 95 | .lens(AppState::frame_buffer) 96 | .scroll().center().expand() 97 | ) 98 | .with_child( 99 | Flex::column() 100 | .with_child(Button::new("btn1")) 101 | .with_child(Button::new("btn2")) 102 | .padding(10.0) 103 | .background(BackgroundBrush::Color(Color::from_rgba32_u32(0x00000060))) 104 | .rounded(5.0) 105 | .padding(10.0) 106 | .align_left() 107 | ) 108 | .with_child( 109 | Flex::row() 110 | .with_child(dim_colors) 111 | .with_child(bri_colors) 112 | .padding(10.0) 113 | .background(BackgroundBrush::Color(Color::from_rgba32_u32(0x00000060))) 114 | .rounded(5.0) 115 | .padding(10.0) 116 | .align_right() 117 | ) 118 | // .debug_paint_layout() 119 | // .debug_invalidation() 120 | 121 | } 122 | 123 | fn make_menu(_: Option, _state: &AppState, _: &Env) -> Menu { 124 | let mut base = Menu::empty(); 125 | #[cfg(target_os = "macos")] 126 | { 127 | base = druid::platform_menus::mac::menu_bar(); 128 | } 129 | #[cfg(any(target_os = "windows", target_os = "linux", target_os = "openbsd"))] 130 | { 131 | base = base.entry(druid::platform_menus::win::file::default()); 132 | } 133 | // if state.menu_count != 0 { 134 | // let mut custom = Menu::new(LocalizedString::new("Custom")); 135 | 136 | // for i in 1..=state.menu_count { 137 | // custom = custom.entry( 138 | // MenuItem::new( 139 | // LocalizedString::new("hello-counter") 140 | // .with_arg("count", move |_: &State, _| i.into()), 141 | // ) 142 | // .on_activate(move |_ctx, data, _env| data.selected = i) 143 | // .enabled_if(move |_data, _env| i % 3 != 0) 144 | // .selected_if(move |data, _env| i == data.selected), 145 | // ); 146 | // } 147 | // base = base.entry(custom); 148 | // } 149 | // base.rebuild_on(|old_data, data, _env| old_data.menu_count != data.menu_count) 150 | base 151 | } 152 | -------------------------------------------------------------------------------- /respectrum_egui/src/windows/bus_window.rs: -------------------------------------------------------------------------------- 1 | use egui::*; 2 | use egui_extras::{Size, TableBuilder}; 3 | use librespectrum::{devs::BusLogger, bus::Ctrl}; 4 | use std::rc::Rc; 5 | 6 | use super::{SubWindow, draw_window}; 7 | 8 | pub struct BusWindow { 9 | logger: Rc 10 | } 11 | 12 | impl BusWindow { 13 | pub fn new(logger: Rc) -> Self { 14 | Self { logger } 15 | } 16 | } 17 | 18 | impl SubWindow for BusWindow { 19 | 20 | fn name(&self) -> String { String::from("Bus") } 21 | 22 | fn show(&mut self, ctx: &Context, focused: bool) -> Response { 23 | 24 | draw_window(self.name(), focused, ctx, |ui| { 25 | 26 | let text_height = egui::TextStyle::Body.resolve(ui.style()).size; 27 | 28 | TableBuilder::new(ui) 29 | .striped(true) 30 | .cell_layout(egui::Layout::left_to_right().with_cross_align(egui::Align::Center)) 31 | .columns(Size::initial(30.0), 16) 32 | .header(text_height, |mut header| { 33 | header.col(|ui| { ui.label("T"); }); 34 | header.col(|ui| { ui.label("ADDR"); }); 35 | header.col(|ui| { ui.label("DATA"); }); 36 | header.col(|ui| { ui.label("RD"); }); 37 | header.col(|ui| { ui.label("WR"); }); 38 | header.col(|ui| { ui.label("MREQ"); }); 39 | header.col(|ui| { ui.label("IORQ"); }); 40 | header.col(|ui| { ui.label("RFSH"); }); 41 | header.col(|ui| { ui.label("M1"); }); 42 | header.col(|ui| { ui.label("BUSRQ"); }); 43 | header.col(|ui| { ui.label("BUSAK"); }); 44 | header.col(|ui| { ui.label("WAIT"); }); 45 | header.col(|ui| { ui.label("HALT"); }); 46 | header.col(|ui| { ui.label("INT"); }); 47 | header.col(|ui| { ui.label("NMI"); }); 48 | header.col(|ui| { ui.label("RESET"); }); 49 | }) 50 | .body(|mut body| { 51 | 52 | for reading in self.logger.readings.borrow().iter_to_tail() { 53 | body.row(text_height, |mut row| { 54 | row.col(|ui| { ui.label(format!("{}", reading.htcyc)); }); 55 | row.col(|ui| { if let Some(addr) = reading.addr { ui.label(format!("{:04X}", addr)); } else { ui.colored_label(Color32::GRAY, "----"); } }); 56 | row.col(|ui| { if let Some(data) = reading.data { ui.label(format!("{:02X}", data)); } else { ui.colored_label(Color32::GRAY, "--"); } }); 57 | row.col(|ui| { if let Some(ctrl) = reading.ctrl { ui.label(if ctrl.contains(Ctrl::RD) { "H" } else { "L" }); } else { ui.colored_label(Color32::GRAY, "-"); } }); 58 | row.col(|ui| { if let Some(ctrl) = reading.ctrl { ui.label(if ctrl.contains(Ctrl::WR) { "H" } else { "L" }); } else { ui.colored_label(Color32::GRAY, "-"); } }); 59 | row.col(|ui| { if let Some(ctrl) = reading.ctrl { ui.label(if ctrl.contains(Ctrl::MREQ) { "H" } else { "L" }); } else { ui.colored_label(Color32::GRAY, "-"); } }); 60 | row.col(|ui| { if let Some(ctrl) = reading.ctrl { ui.label(if ctrl.contains(Ctrl::IORQ) { "H" } else { "L" }); } else { ui.colored_label(Color32::GRAY, "-"); } }); 61 | row.col(|ui| { if let Some(ctrl) = reading.ctrl { ui.label(if ctrl.contains(Ctrl::RFSH) { "H" } else { "L" }); } else { ui.colored_label(Color32::GRAY, "-"); } }); 62 | row.col(|ui| { if let Some(m1) = reading.m1 { ui.label(if m1 { "H" } else { "L" }); } else { ui.colored_label(Color32::GRAY, "-"); } }); 63 | row.col(|ui| { if let Some(busrq) = reading.busrq { ui.label(if busrq { "H" } else { "L" }); } else { ui.colored_label(Color32::GRAY, "-"); } }); 64 | row.col(|ui| { if let Some(busak) = reading.busak { ui.label(if busak { "H" } else { "L" }); } else { ui.colored_label(Color32::GRAY, "-"); } }); 65 | row.col(|ui| { if let Some(wait) = reading.wait { ui.label(if wait { "H" } else { "L" }); } else { ui.colored_label(Color32::GRAY, "-"); } }); 66 | row.col(|ui| { if let Some(halt) = reading.halt { ui.label(if halt { "H" } else { "L" }); } else { ui.colored_label(Color32::GRAY, "-"); } }); 67 | row.col(|ui| { if let Some(int) = reading.int { ui.label(if int { "H" } else { "L" }); } else { ui.colored_label(Color32::GRAY, "-"); } }); 68 | row.col(|ui| { if let Some(nmi) = reading.nmi { ui.label(if nmi { "H" } else { "L" }); } else { ui.colored_label(Color32::GRAY, "-"); } }); 69 | row.col(|ui| { if let Some(reset) = reading.reset { ui.label(if reset { "H" } else { "L" }); } else { ui.colored_label(Color32::GRAY, "-"); } }); 70 | }); 71 | } 72 | 73 | }); 74 | 75 | }) 76 | 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /respectrum_egui/src/windows/disassm_window.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cmp::min, 3 | rc::Rc, 4 | pin::Pin, 5 | ops::{Coroutine, CoroutineState}, 6 | }; 7 | 8 | use egui::*; 9 | 10 | use librespectrum::{ 11 | devs::{mem::Memory, Cpu}, 12 | cpu::decoder::disassembler 13 | }; 14 | 15 | use super::{SubWindow, draw_window, cursor_color}; 16 | 17 | /// Maximum bytes to process for each disassembled line 18 | const LINE_BYTES: usize = 4; 19 | 20 | pub struct DisassmWindow { 21 | cpu: Rc, 22 | memory: Rc, 23 | addr: u16, 24 | rows: usize, 25 | cursor: usize, 26 | } 27 | 28 | impl DisassmWindow { 29 | 30 | pub fn new(cpu: Rc, memory: Rc) -> Self { 31 | Self { cpu, memory, addr: 0, rows: 24, cursor: 0 } 32 | } 33 | 34 | fn prev_instr(&self) -> u16 { 35 | let mut ptr = self.addr.wrapping_sub((LINE_BYTES * 2) as u16); 36 | let mut disasm = disassembler(ptr, LINE_BYTES); 37 | let mut prev = self.addr; 38 | loop { 39 | let byte = self.memory.read(ptr); 40 | ptr = ptr.wrapping_add(1); 41 | if let CoroutineState::Yielded(Some(line)) = Pin::new(&mut disasm).resume(byte) { 42 | if (line.address.wrapping_sub(self.addr) as i16) >= 0 { 43 | return prev; 44 | } 45 | prev = line.address; 46 | } 47 | } 48 | } 49 | 50 | fn next_instr(&self) -> u16 { 51 | let mut ptr = self.addr; 52 | let mut disasm = disassembler(ptr, LINE_BYTES); 53 | loop { 54 | let byte = self.memory.read(ptr); 55 | ptr = ptr.wrapping_add(1); 56 | if let CoroutineState::Yielded(Some(line)) = Pin::new(&mut disasm).resume(byte) { 57 | if (line.address.wrapping_sub(self.addr) as i16) > 0 { 58 | return line.address; 59 | } 60 | } 61 | } 62 | } 63 | 64 | fn handle_keyboard(&mut self, input: &InputState) { 65 | 66 | if input.key_pressed(Key::ArrowUp) { 67 | self.cursor = if input.modifiers.alt {0} else { 68 | if self.cursor == 0 { self.addr = self.prev_instr(); } 69 | self.cursor.saturating_sub(1) 70 | }; 71 | } 72 | 73 | if input.key_pressed(Key::ArrowDown) { 74 | self.cursor = if input.modifiers.alt {self.rows - 1} else { 75 | if self.cursor == self.rows - 1 { self.addr = self.next_instr(); } 76 | min(self.rows - 1, self.cursor + 1) 77 | }; 78 | } 79 | 80 | } 81 | 82 | } 83 | 84 | impl SubWindow for DisassmWindow { 85 | 86 | fn name(&self) -> String { String::from("Disassembler") } 87 | 88 | fn show(&mut self, ctx: &Context, focused: bool) -> Response { 89 | 90 | draw_window(self.name(), focused, ctx, |ui| { 91 | 92 | if focused { 93 | self.handle_keyboard(&ui.input()); 94 | } 95 | 96 | Grid::new("memory").min_col_width(0.0).show(ui, |ui| { 97 | 98 | let mut disasm = disassembler(self.addr, LINE_BYTES); 99 | let mut ptr = self.addr; 100 | let pc = self.cpu.pc.value().get(); 101 | 102 | for row in 0..self.rows { 103 | 104 | // Feed bytes until line is disassembled 105 | let line = loop { 106 | let byte = self.memory.read(ptr); 107 | ptr = ptr.wrapping_add(1); 108 | if let CoroutineState::Yielded(Some(line)) = Pin::new(&mut disasm).resume(byte) { 109 | break line; 110 | } 111 | }; 112 | 113 | let line_color = if line.address <= pc && pc < line.address + line.bytes.len() as u16 { 114 | Color32::BLUE 115 | } else { 116 | Color32::BLACK 117 | }; 118 | 119 | // Print address 120 | if ui.add( 121 | Label::new( 122 | RichText::new(format!("{:0>4X}", line.address)) 123 | .background_color(if self.cursor == row {cursor_color(focused)} else {Color32::default()}) 124 | .color(line_color) 125 | ).sense(Sense::click()) 126 | ).clicked() { 127 | self.cursor = row; 128 | } 129 | 130 | // Print bytes 131 | ui.add(Separator::default().vertical()); 132 | ui.add(Label::new( 133 | RichText::new(format!( 134 | "{:2X}", byte)).collect::>().join(" "), 136 | bytes = LINE_BYTES * 3 - 1 137 | )).color(line_color) 138 | )); 139 | 140 | // Print mnemonic (if any) 141 | ui.add(Separator::default().vertical()); 142 | if let Some(instr) = line.instruction { 143 | ui.add(Label::new( 144 | RichText::new(format!("{:<16}", instr.format_mnemonic())).color(line_color) 145 | )); 146 | } else { 147 | ui.label("..."); 148 | } 149 | 150 | ui.end_row(); 151 | 152 | } 153 | 154 | }); 155 | 156 | }) 157 | 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /librespectrum/src/bus/task.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fmt, 3 | rc::Rc, 4 | pin::Pin, 5 | ops::{Coroutine, CoroutineState}, 6 | }; 7 | 8 | use super::Clock; 9 | 10 | /// Task which returns a value when it's completed 11 | /// or yields next wake up time as an offset in half t-cycles to the current clock 12 | pub trait Task = Coroutine<(), Yield=usize, Return=T> + Unpin; 13 | 14 | /// Task which never returns 15 | pub trait NoReturnTask = Task; 16 | 17 | /// Task execution step 18 | struct Step { 19 | pub htcycles: u64, 20 | pub task_idx: usize, 21 | pub next: Option>, 22 | } 23 | 24 | impl fmt::Debug for Step { 25 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 26 | match &self.next { 27 | &Some(ref step) => write!(f, "({:?},{:?}),{:?}", self.htcycles, self.task_idx, *step), 28 | &None => write!(f, "({:?},{:?})", self.htcycles, self.task_idx), 29 | } 30 | } 31 | } 32 | 33 | impl Step { 34 | /// Schedule given task at given htcycles 35 | fn schedule(head: &mut Option>, htcycles: u64, task_idx: usize) { 36 | match head { 37 | Some(step) if step.htcycles > htcycles => { 38 | *head = Some(Box::new(Step { htcycles, task_idx, next: head.take() })); 39 | }, 40 | Some(step) => Step::schedule(&mut step.next, htcycles, task_idx), 41 | None => *head = Some(Box::new(Step { htcycles, task_idx, next: None })) 42 | } 43 | } 44 | } 45 | 46 | /// Clock-synced tasks scheduler 47 | pub struct Scheduler<'a> { 48 | 49 | /// System clock 50 | clock: Rc, 51 | 52 | /// Managed tasks 53 | tasks: Vec>, 54 | 55 | /// Execution steps queue head 56 | head: Option>, 57 | 58 | } 59 | 60 | impl fmt::Debug for Scheduler<'_> { 61 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 62 | write!(f, "clock: {:?}, queue: {:?}", self.clock.get(), self.head) 63 | } 64 | } 65 | 66 | impl<'a> Scheduler<'a> { 67 | 68 | /// Create new scheduler instance 69 | pub fn new(clock: Rc, tasks: Vec>) -> Self { 70 | fn init(htcycles: u64, i: usize) -> Option> { 71 | if i == 0 {None} else { 72 | Some(Box::new(Step { htcycles, task_idx: i-1, next: init(htcycles, i-1) })) 73 | } 74 | } 75 | let htcycles = clock.get(); 76 | let tasks_len = tasks.len(); 77 | Self { clock, tasks, head: init(htcycles, tasks_len) } 78 | } 79 | 80 | /// Advance N half t-cycles forward 81 | pub fn advance(&mut self, offset: u64) { 82 | 83 | // htcycles to advance to 84 | let target_htcycles = self.clock.get() + offset; 85 | 86 | // Check if next step is before target_htcycles 87 | if let Some(step) = &self.head && step.htcycles < target_htcycles { 88 | 89 | // Consume the step and set head to the next one 90 | let Step { htcycles: task_htcycles, task_idx, next } = *self.head.take().unwrap(); 91 | let task = &mut self.tasks[task_idx]; 92 | self.head = next; 93 | 94 | // Advance to task's htcycles and continue task execution 95 | self.clock.set(task_htcycles); 96 | if let CoroutineState::Yielded(offset) = Pin::new(task).resume(()) { 97 | // Re-schedule current task with returned htcycles offset 98 | Step::schedule(&mut self.head, task_htcycles + offset as u64, task_idx); 99 | } else { 100 | panic!("Expecting task to never return (complete)"); 101 | } 102 | 103 | // Recursively advance to the next step 104 | let htcycles_left = target_htcycles - task_htcycles; 105 | if htcycles_left > 0 { 106 | self.advance(htcycles_left); 107 | } 108 | 109 | } else { 110 | 111 | // Just advance to target_htcycles 112 | self.clock.set(target_htcycles); 113 | 114 | } 115 | 116 | } 117 | 118 | } 119 | 120 | #[cfg(test)] 121 | mod tests { 122 | 123 | use super::*; 124 | use std::cell::RefCell; 125 | 126 | struct SharedState { 127 | clock: Rc, 128 | seq: RefCell> 129 | } 130 | 131 | struct Foo { state: Rc } 132 | impl Foo { 133 | fn run<'a>(&'a self) -> Box { 134 | Box::new(#[coroutine] move || { 135 | loop { 136 | yield self.state.clock.rising(3); // skip to 3rd raising edge 137 | self.state.seq.borrow_mut().push((self.state.clock.get(), true)); 138 | } 139 | }) 140 | } 141 | } 142 | 143 | struct Bar { state: Rc } 144 | impl Bar { 145 | fn run<'a>(&'a self) -> Box { 146 | Box::new(#[coroutine] move || { 147 | loop { 148 | yield self.state.clock.falling(1); // skip to 1st falling edge 149 | self.state.seq.borrow_mut().push((self.state.clock.get(), false)); 150 | } 151 | }) 152 | } 153 | } 154 | 155 | #[test] 156 | fn scheduler_executes_tasks() { 157 | 158 | let clock: Rc = Default::default(); 159 | 160 | let state = Rc::new(SharedState { 161 | clock: Rc::clone(&clock), 162 | seq: RefCell::new(vec![]) 163 | }); 164 | 165 | let foo = Foo { state: Rc::clone(&state) }; 166 | let bar = Bar { state: Rc::clone(&state) }; 167 | 168 | let mut scheduler = Scheduler::new(clock, vec![foo.run(), bar.run()]); 169 | 170 | scheduler.advance(10); 171 | assert_eq!( 172 | *state.seq.borrow(), 173 | vec![(1, false), (3, false), (5, false), (6, true), (7, false), (9, false)] 174 | ); 175 | 176 | state.seq.borrow_mut().clear(); 177 | scheduler.advance(10); 178 | assert_eq!( 179 | *state.seq.borrow(), 180 | vec![(11, false), (12, true), (13, false), (15, false), (17, false), (18, true), (19, false)] 181 | ); 182 | 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /respectrum_egui/src/windows/memory_window.rs: -------------------------------------------------------------------------------- 1 | use std::{cmp::min, rc::Rc}; 2 | use egui::*; 3 | 4 | use librespectrum::devs::mem::Memory; 5 | 6 | use super::{SubWindow, draw_window, cursor_color}; 7 | 8 | #[derive(Debug, PartialEq, Clone, Copy)] 9 | enum Cursor { 10 | Address(usize), 11 | Memory(usize, usize), 12 | Text(usize, usize), 13 | } 14 | 15 | pub struct MemoryWindow { 16 | memory: Rc, 17 | cols: usize, 18 | rows: usize, 19 | addr: u16, 20 | cursor: Cursor, 21 | } 22 | 23 | impl MemoryWindow { 24 | 25 | pub fn new(memory: Rc) -> Self { 26 | Self { memory, cols: 8, rows: 24, addr: 0, cursor: Cursor::Address(0) } 27 | } 28 | 29 | fn handle_keyboard(&mut self, input: &InputState) { 30 | 31 | let (Cursor::Address(row) | Cursor::Memory(_, row) | Cursor::Text(_, row)) = self.cursor; 32 | 33 | if input.key_pressed(Key::ArrowUp) { 34 | self.cursor = if input.modifiers.alt { 35 | match self.cursor { 36 | Cursor::Address(_) => Cursor::Address(0), 37 | Cursor::Memory(col, _) => Cursor::Memory(col, 0), 38 | Cursor::Text(col, _) => Cursor::Text(col, 0) 39 | } 40 | } else { 41 | if row == 0 { self.addr = self.addr.overflowing_sub(self.cols as u16).0; } 42 | match self.cursor { 43 | Cursor::Address(row) => Cursor::Address(row.saturating_sub(1)), 44 | Cursor::Memory(col, row) => Cursor::Memory(col, row.saturating_sub(1)), 45 | Cursor::Text(col, row) => Cursor::Text(col, row.saturating_sub(1)) 46 | } 47 | }; 48 | } 49 | 50 | if input.key_pressed(Key::ArrowDown) { 51 | self.cursor = if input.modifiers.alt { 52 | match self.cursor { 53 | Cursor::Address(_) => Cursor::Address(self.rows - 1), 54 | Cursor::Memory(col, _) => Cursor::Memory(col, self.rows - 1), 55 | Cursor::Text(col, _) => Cursor::Text(col, self.rows - 1) 56 | } 57 | } else { 58 | if row == self.rows - 1 { self.addr = self.addr.overflowing_add(self.cols as u16).0; } 59 | match self.cursor { 60 | Cursor::Address(row) => Cursor::Address(min(self.rows - 1, row + 1)), 61 | Cursor::Memory(col, row) => Cursor::Memory(col, min(self.rows - 1, row + 1)), 62 | Cursor::Text(col, row) => Cursor::Text(col, min(self.rows - 1, row + 1)) 63 | } 64 | }; 65 | } 66 | 67 | if input.key_pressed(Key::ArrowLeft) { 68 | self.cursor = if input.modifiers.alt { 69 | match self.cursor { 70 | Cursor::Memory(col, row) if col == 0 => Cursor::Address(row), 71 | Cursor::Memory(_, row) => Cursor::Memory(0, row), 72 | Cursor::Text(col, row) if col == 0 => Cursor::Memory(0, row), 73 | Cursor::Text(_, row) => Cursor::Text(0, row), 74 | _ => self.cursor 75 | } 76 | } else { 77 | match self.cursor { 78 | Cursor::Memory(col, row) if col == 0 => Cursor::Address(row), 79 | Cursor::Memory(col, row) => Cursor::Memory(col - 1, row), 80 | Cursor::Text(col, row) if col == 0 => Cursor::Memory(self.cols - 1, row), 81 | Cursor::Text(col, row) => Cursor::Text(col - 1, row), 82 | _ => self.cursor 83 | } 84 | }; 85 | } 86 | 87 | if input.key_pressed(Key::ArrowRight) { 88 | self.cursor = if input.modifiers.alt { 89 | match self.cursor { 90 | Cursor::Address(row) => Cursor::Memory(self.cols - 1, row), 91 | Cursor::Memory(col, row) if col == self.cols - 1 => Cursor::Text(self.cols - 1, row), 92 | Cursor::Memory(_, row) => Cursor::Memory(self.cols - 1, row), 93 | Cursor::Text(_, row) => Cursor::Text(self.cols - 1, row), 94 | _ => self.cursor 95 | } 96 | } else { 97 | match self.cursor { 98 | Cursor::Address(row) => Cursor::Memory(0, row), 99 | Cursor::Memory(col, row) if col == self.cols - 1 => Cursor::Text(0, row), 100 | Cursor::Memory(col, row) => Cursor::Memory(col + 1, row), 101 | Cursor::Text(col, row) if col < self.cols - 1 => Cursor::Text(col + 1, row), 102 | _ => self.cursor 103 | } 104 | }; 105 | } 106 | 107 | if input.key_pressed(Key::PageUp) { 108 | self.addr = self.addr.overflowing_sub((self.cols * self.rows) as u16).0; 109 | } 110 | 111 | if input.key_pressed(Key::PageDown) { 112 | self.addr = self.addr.overflowing_add((self.cols * self.rows) as u16).0; 113 | } 114 | 115 | if input.key_pressed(Key::Home) { 116 | self.cursor = Cursor::Address(row); 117 | } 118 | 119 | if input.key_pressed(Key::End) { 120 | self.cursor = Cursor::Text(self.cols - 1, row); 121 | } 122 | 123 | } 124 | 125 | } 126 | 127 | impl SubWindow for MemoryWindow { 128 | 129 | fn name(&self) -> String { String::from("Memory") } 130 | 131 | fn show(&mut self, ctx: &Context, focused: bool) -> Response { 132 | 133 | draw_window(self.name(), focused, ctx, |ui| { 134 | 135 | if focused { 136 | self.handle_keyboard(&ui.input()); 137 | } 138 | 139 | Grid::new("memory").min_col_width(0.0).show(ui, |ui| { 140 | 141 | for row in 0..self.rows { 142 | 143 | let row_addr = self.addr.overflowing_add((row * self.cols) as u16).0; 144 | 145 | let label = Label::new( 146 | RichText::new( 147 | format!("{:04X}", row_addr) 148 | ).background_color( 149 | if self.cursor == Cursor::Address(row) {cursor_color(focused)} else {Color32::default()} 150 | ) 151 | ).sense(Sense::click()); 152 | 153 | if ui.add(label).clicked() { 154 | self.cursor = Cursor::Address(row); 155 | } 156 | 157 | ui.add(Separator::default().vertical()); 158 | 159 | for col in 0..self.cols { 160 | 161 | let addr = row_addr.overflowing_add(col as u16).0; 162 | 163 | let label = Label::new( 164 | RichText::new( 165 | format!("{:02X}", self.memory.read(addr)) 166 | ).background_color( 167 | if self.cursor == Cursor::Memory(col, row) {cursor_color(focused)} else {Color32::default()} 168 | ).color( 169 | if self.memory.writable(addr) {Color32::BLACK} else {Color32::GRAY} 170 | ) 171 | ).sense(Sense::click()); 172 | 173 | if ui.add(label).clicked() { 174 | self.cursor = Cursor::Memory(col, row); 175 | } 176 | 177 | } 178 | 179 | ui.add(Separator::default().vertical()); 180 | 181 | ui.horizontal(|ui| { 182 | 183 | ui.spacing_mut().item_spacing.x = 0.0; 184 | 185 | for col in 0..self.cols { 186 | 187 | let addr = row_addr.overflowing_add(col as u16).0; 188 | 189 | let byte = self.memory.read(addr); 190 | 191 | let is_ascii = byte.is_ascii_alphanumeric() || byte.is_ascii_graphic() 192 | || byte.is_ascii_punctuation() || byte == 0x20 /* space */; 193 | 194 | let label = Label::new( 195 | RichText::new( 196 | (if is_ascii {byte as char} else {'.'}).to_string() 197 | ).background_color( 198 | if self.cursor == Cursor::Text(col, row) {cursor_color(focused)} else {Color32::default()} 199 | ).color( 200 | if is_ascii {Color32::DARK_GRAY} else {Color32::LIGHT_GRAY} 201 | ) 202 | ).sense(Sense::click()); 203 | 204 | if ui.add(label).clicked() { 205 | self.cursor = Cursor::Text(col, row); 206 | } 207 | 208 | } 209 | 210 | }); 211 | 212 | ui.end_row(); 213 | 214 | } 215 | 216 | }); 217 | 218 | }) 219 | 220 | } 221 | 222 | } 223 | -------------------------------------------------------------------------------- /librespectrum/src/cpu/tokens.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// Decoded CPU token 4 | #[allow(non_camel_case_types)] 5 | #[derive(Debug, Clone, Copy)] 6 | pub enum Token { 7 | 8 | // Non-opcode tokens 9 | Prefix(u16), 10 | Displacement(i8), 11 | Data(DataValue), 12 | 13 | // 8-bit Load 14 | LD_RG_RG(Reg, Reg), 15 | LD_RG_N(Reg), 16 | LD_A_AtRP(RegPair), 17 | LD_AtRP_A(RegPair), 18 | LD_A_MM, 19 | LD_MM_A, 20 | 21 | // 16-bit Load 22 | LD_RP_NN(RegPair), 23 | LD_RP_MM(RegPair), 24 | LD_MM_RP(RegPair), 25 | LD_SP_RP(RegPair), 26 | POP(RegPair), 27 | PUSH(RegPair), 28 | 29 | // Exchange 30 | EX_DE_HL, 31 | EX_AF, 32 | EXX, 33 | EX_AtSP_RP(RegPair), 34 | 35 | // 8-bit arithmetic and logic 36 | ALU(AluOp, Option), 37 | INC_RG(Reg), 38 | DEC_RG(Reg), 39 | 40 | // General-Purpose Arithmetic and CPU Control 41 | DAA, 42 | CPL, 43 | NEG, 44 | CCF, 45 | SCF, 46 | NOP, 47 | HALT, 48 | DI, 49 | EI, 50 | IM(IntMode), 51 | 52 | // 16-Bit Arithmetic 53 | ADD_RP_RP(RegPair, RegPair), 54 | ADC_HL_RP(RegPair), 55 | SBC_HL_RP(RegPair), 56 | INC_RP(RegPair), 57 | DEC_RP(RegPair), 58 | 59 | // Rotate and Shift 60 | SHOP(ShiftOp, Reg, Option), 61 | 62 | // Bit Set, Reset and Test 63 | BIT(u8, Reg), 64 | SET(u8, Reg, Option), 65 | RES(u8, Reg, Option), 66 | 67 | // Jump, Call and Return 68 | JP(Condition), 69 | JP_RP(RegPair), 70 | JR(Condition), 71 | DJNZ, 72 | CALL(Condition), 73 | RET(Condition), 74 | RETI, 75 | RETN, 76 | RST(u8), 77 | 78 | // IO 79 | IN_A_N, 80 | OUT_N_A, 81 | IN_RG_AtBC(Reg), 82 | OUT_AtBC_RG(Reg), 83 | IN_AtBC, // undocumented 84 | OUT_AtBC_0, // undocumented 85 | 86 | // Block transfer, search and IO 87 | BLOP(BlockOp), 88 | 89 | } 90 | 91 | /// CPU M-cycle type 92 | pub enum MCycle { 93 | /// Internal CPU Operation 94 | IOP, 95 | /// Opcode Fetch (aka M1) 96 | OCF, 97 | /// Memory Read 98 | MR, 99 | /// Memory Read of High Byte 100 | MRH, 101 | /// Memory Read of Low Byte 102 | MRL, 103 | /// Memory Write 104 | MW, 105 | /// Memory Write of High Byte 106 | MWH, 107 | /// Memory Write of Low Byte 108 | MWL, 109 | /// Immediate Data Read 110 | ID, 111 | /// Immediate Data Read of High Byte 112 | IDH, 113 | /// Immediate Data Read of Low Byte 114 | IDL, 115 | /// Port Read 116 | PR, 117 | /// Port Write 118 | PW, 119 | /// Stack Read of High Byte 120 | SRH, 121 | /// Stack Read of Low Byte 122 | SRL, 123 | /// Stack Write of High Byte 124 | SWH, 125 | /// Stack Write of Low Byte 126 | SWL, 127 | } 128 | 129 | #[derive(Debug, Clone, Copy)] 130 | pub enum TokenType { 131 | /// Actual opcode or opcode prefix 132 | Opcode, 133 | /// Displacement byte 134 | Displacement, 135 | /// Immediate data 136 | Data 137 | } 138 | 139 | /// Opcode immediate data value 140 | #[derive(Debug, Clone, Copy)] 141 | pub enum DataValue { 142 | Byte(u8), 143 | Word(u16) 144 | } 145 | 146 | /// 8-bit register 147 | #[derive(Debug, PartialEq, Clone, Copy)] 148 | pub enum Reg { 149 | B = 0, C, D, E, H, L, AtHL, A, // DO NOT reorder 150 | F, I, R, IXH, IXL, IYH, IYL, AtIX, AtIY, 151 | } 152 | 153 | impl From for Reg { 154 | fn from(code: u8) -> Self { 155 | unsafe { std::mem::transmute(code & 0b111) } 156 | } 157 | } 158 | 159 | /// 16-bit register pair 160 | #[derive(Debug, PartialEq, Clone, Copy)] 161 | pub enum RegPair { 162 | BC = 0, DE, HL, SPorAF, // DO NOT reorder 163 | SP, AF, PC, IR, IX, IY 164 | } 165 | 166 | impl From for RegPair { 167 | fn from(code: u8) -> Self { 168 | unsafe { std::mem::transmute(code & 0b11) } 169 | } 170 | } 171 | 172 | impl RegPair { 173 | pub fn prefer_sp(self) -> RegPair { 174 | return if self == RegPair::SPorAF { RegPair::SP } else { self }; 175 | } 176 | pub fn prefer_af(self) -> RegPair { 177 | return if self == RegPair::SPorAF { RegPair::AF } else { self }; 178 | } 179 | } 180 | 181 | /// Condition 182 | #[derive(Debug, PartialEq, Clone, Copy)] 183 | pub enum Condition { 184 | // DO NOT reorder 185 | /// Non zero 186 | NZ = 0, 187 | /// Zero 188 | Z, 189 | /// Non carry 190 | NC, 191 | /// Carry 192 | C, 193 | /// Parity odd 194 | PO, 195 | /// Parity even 196 | PE, 197 | /// Sign positive 198 | P, 199 | /// Sign negative 200 | M, 201 | /// No condition 202 | None 203 | } 204 | 205 | impl From for Condition { 206 | fn from(code: u8) -> Self { 207 | unsafe { std::mem::transmute(code & 0b111) } 208 | } 209 | } 210 | 211 | /// ALU operation 212 | #[derive(Debug, PartialEq, Clone, Copy)] 213 | pub enum AluOp { 214 | ADD = 0, ADC, SUB, SBC, AND, XOR, OR, CP, // DO NOT reorder 215 | } 216 | 217 | impl From for AluOp { 218 | fn from(code: u8) -> Self { 219 | unsafe { std::mem::transmute(code & 0b111) } 220 | } 221 | } 222 | 223 | /// Shift operation 224 | #[derive(Debug, PartialEq, Clone, Copy)] 225 | pub enum ShiftOp { 226 | RLC = 0, RRC, RL, RR, SLA, SRA, SLL, SRL, // DO NOT reorder 227 | RLCA, RRCA, RLA, RRA, RLD, RRD 228 | } 229 | 230 | impl From for ShiftOp { 231 | fn from(code: u8) -> Self { 232 | unsafe { std::mem::transmute(code & 0b111) } 233 | } 234 | } 235 | 236 | /// Interrupt mode 237 | #[derive(Debug, PartialEq, Clone, Copy, Default)] 238 | pub enum IntMode { 239 | #[default] IM0 = 0, IM01, IM1, IM2, // DO NOT reorder 240 | } 241 | 242 | impl From for IntMode { 243 | fn from(code: u8) -> Self { 244 | unsafe { std::mem::transmute(code & 0b11) } 245 | } 246 | } 247 | 248 | impl fmt::Display for IntMode { 249 | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 250 | formatter.write_str(match self { 251 | IntMode::IM0 | IntMode::IM01 => "0", 252 | IntMode::IM1 => "1", 253 | IntMode::IM2 => "2" 254 | }) 255 | } 256 | } 257 | 258 | /// Block operation 259 | #[derive(Debug, PartialEq, Clone, Copy)] 260 | pub enum BlockOp { // DO NOT reorder 261 | LDI = 0, CPI, INI, OUTI, 262 | LDD, CPD, IND, OUTD, 263 | LDIR, CPIR, INIR, OTIR, 264 | LDDR, CPDR, INDR, OTDR, 265 | } 266 | 267 | impl From for BlockOp { 268 | fn from(code: u8) -> Self { 269 | unsafe { std::mem::transmute(code & 0b1111) } 270 | } 271 | } 272 | 273 | #[cfg(test)] 274 | mod tests { 275 | use super::*; 276 | 277 | #[test] 278 | fn reg_from_returns_register_corresponding_to_opcode_bits() { 279 | assert_eq!(Reg::B, Reg::from(0)); 280 | assert_eq!(Reg::C, Reg::from(1)); 281 | assert_eq!(Reg::D, Reg::from(2)); 282 | assert_eq!(Reg::E, Reg::from(3)); 283 | assert_eq!(Reg::H, Reg::from(4)); 284 | assert_eq!(Reg::L, Reg::from(5)); 285 | assert_eq!(Reg::AtHL, Reg::from(6)); 286 | assert_eq!(Reg::A, Reg::from(7)); 287 | } 288 | 289 | #[test] 290 | fn reg_pair_from_returns_register_pair_corresponding_to_opcode_bits() { 291 | assert_eq!(RegPair::BC, RegPair::from(0)); 292 | assert_eq!(RegPair::DE, RegPair::from(1)); 293 | assert_eq!(RegPair::HL, RegPair::from(2)); 294 | assert_eq!(RegPair::SPorAF, RegPair::from(3)); 295 | } 296 | 297 | #[test] 298 | fn condition_from_returns_condition_corresponding_to_opcode_bits() { 299 | assert_eq!(Condition::NZ, Condition::from(0)); 300 | assert_eq!(Condition::Z, Condition::from(1)); 301 | assert_eq!(Condition::NC, Condition::from(2)); 302 | assert_eq!(Condition::C, Condition::from(3)); 303 | assert_eq!(Condition::PO, Condition::from(4)); 304 | assert_eq!(Condition::PE, Condition::from(5)); 305 | assert_eq!(Condition::P, Condition::from(6)); 306 | assert_eq!(Condition::M, Condition::from(7)); 307 | } 308 | 309 | #[test] 310 | fn alu_op_from_returns_alu_operation_corresponding_to_opcode_bits() { 311 | assert_eq!(AluOp::ADD, AluOp::from(0)); 312 | assert_eq!(AluOp::ADC, AluOp::from(1)); 313 | assert_eq!(AluOp::SUB, AluOp::from(2)); 314 | assert_eq!(AluOp::SBC, AluOp::from(3)); 315 | assert_eq!(AluOp::AND, AluOp::from(4)); 316 | assert_eq!(AluOp::XOR, AluOp::from(5)); 317 | assert_eq!(AluOp::OR, AluOp::from(6)); 318 | assert_eq!(AluOp::CP, AluOp::from(7)); 319 | } 320 | 321 | #[test] 322 | fn shift_op_from_returns_shift_operation_corresponding_to_opcode_bits() { 323 | assert_eq!(ShiftOp::RLC, ShiftOp::from(0)); 324 | assert_eq!(ShiftOp::RRC, ShiftOp::from(1)); 325 | assert_eq!(ShiftOp::RL, ShiftOp::from(2)); 326 | assert_eq!(ShiftOp::RR, ShiftOp::from(3)); 327 | assert_eq!(ShiftOp::SLA, ShiftOp::from(4)); 328 | assert_eq!(ShiftOp::SRA, ShiftOp::from(5)); 329 | assert_eq!(ShiftOp::SLL, ShiftOp::from(6)); 330 | assert_eq!(ShiftOp::SRL, ShiftOp::from(7)); 331 | } 332 | 333 | #[test] 334 | fn int_mode_from_returns_int_mode_corresponding_to_opcode_bits() { 335 | assert_eq!(IntMode::IM0, IntMode::from(0)); 336 | assert_eq!(IntMode::IM01, IntMode::from(1)); 337 | assert_eq!(IntMode::IM1, IntMode::from(2)); 338 | assert_eq!(IntMode::IM2, IntMode::from(3)); 339 | } 340 | 341 | #[test] 342 | fn block_op_from_returns_block_operation_corresponding_to_opcode_bits() { 343 | assert_eq!(BlockOp::LDI, BlockOp::from(0x0)); 344 | assert_eq!(BlockOp::CPI, BlockOp::from(0x1)); 345 | assert_eq!(BlockOp::INI, BlockOp::from(0x2)); 346 | assert_eq!(BlockOp::OUTI, BlockOp::from(0x3)); 347 | assert_eq!(BlockOp::LDD, BlockOp::from(0x4)); 348 | assert_eq!(BlockOp::CPD, BlockOp::from(0x5)); 349 | assert_eq!(BlockOp::IND, BlockOp::from(0x6)); 350 | assert_eq!(BlockOp::OUTD, BlockOp::from(0x7)); 351 | assert_eq!(BlockOp::LDIR, BlockOp::from(0x8)); 352 | assert_eq!(BlockOp::CPIR, BlockOp::from(0x9)); 353 | assert_eq!(BlockOp::INIR, BlockOp::from(0xa)); 354 | assert_eq!(BlockOp::OTIR, BlockOp::from(0xb)); 355 | assert_eq!(BlockOp::LDDR, BlockOp::from(0xc)); 356 | assert_eq!(BlockOp::CPDR, BlockOp::from(0xd)); 357 | assert_eq!(BlockOp::INDR, BlockOp::from(0xe)); 358 | assert_eq!(BlockOp::OTDR, BlockOp::from(0xf)); 359 | } 360 | 361 | } 362 | -------------------------------------------------------------------------------- /librespectrum/src/cpu/decoder/instruction.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::cpu::tokens::{ 4 | Token, ShiftOp, BlockOp, 5 | IntMode, Condition, AluOp, Reg, 6 | RegPair, DataValue 7 | }; 8 | 9 | /// Z80 CPU instruction 10 | pub struct Instruction { 11 | pub opcode: Token, 12 | pub displacement: Option, 13 | pub data: Option, 14 | } 15 | 16 | impl Default for Instruction { 17 | fn default() -> Self { 18 | Self { 19 | opcode: Token::NOP, 20 | displacement: None, 21 | data: None 22 | } 23 | } 24 | } 25 | 26 | impl fmt::Display for Instruction { 27 | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 28 | formatter.write_str(self.format_mnemonic().as_str()) 29 | } 30 | } 31 | 32 | impl Instruction { 33 | 34 | pub fn expect_byte_data(&self) -> u8 { 35 | if let Some(DataValue::Byte(value)) = self.data { 36 | value 37 | } else { 38 | panic!("Expecting immediate data to be a byte"); 39 | } 40 | } 41 | 42 | pub fn expect_word_data(&self) -> u16 { 43 | if let Some(DataValue::Word(value)) = self.data { 44 | value 45 | } else { 46 | panic!("Expecting immediate data to be a word"); 47 | } 48 | } 49 | 50 | pub fn format_mnemonic(&self) -> String { 51 | 52 | match &self.opcode { 53 | 54 | // 8-bit Load 55 | Token::LD_RG_RG(dst, src) => format!("LD {},{}", self.format_reg(dst), self.format_reg(src)), 56 | Token::LD_RG_N(reg) => format!("LD {},{}", self.format_reg(reg), self.format_data()), 57 | Token::LD_A_AtRP(rpair) => format!("LD A,({})", self.format_regpair(rpair)), 58 | Token::LD_AtRP_A(rpair) => format!("LD ({}),A", self.format_regpair(rpair)), 59 | Token::LD_A_MM => format!("LD A,({})", self.format_data()), 60 | Token::LD_MM_A => format!("LD ({}),A", self.format_data()), 61 | 62 | // 16-bit Load 63 | Token::LD_RP_NN(rpair) => format!("LD {},{}", self.format_regpair(rpair), self.format_data()), 64 | Token::LD_RP_MM(rpair) => format!("LD {},({})", self.format_regpair(rpair), self.format_data()), 65 | Token::LD_MM_RP(rpair) => format!("LD ({}),{}", self.format_data(), self.format_regpair(rpair)), 66 | Token::LD_SP_RP(rpair) => format!("LD SP,{}", self.format_regpair(rpair)), 67 | Token::POP(rpair) => format!("POP {}", self.format_regpair(rpair)), 68 | Token::PUSH(rpair) => format!("PUSH {}", self.format_regpair(rpair)), 69 | 70 | // Exchange 71 | Token::EX_DE_HL => String::from("EX DE,HL"), 72 | Token::EX_AF => String::from("EX AF,AF'"), 73 | Token::EXX => String::from("EXX"), 74 | Token::EX_AtSP_RP(rpair) => format!("EX (SP),{}", self.format_regpair(rpair)), 75 | 76 | // 8-bit arithmetic and logic 77 | Token::ALU(op, maybe_reg) => { 78 | if let Some(reg) = maybe_reg { 79 | self.format_alu_op(op, &self.format_reg(reg)) 80 | } else { 81 | self.format_alu_op(op, &self.format_data()) 82 | } 83 | }, 84 | Token::INC_RG(reg) => format!("INC {}", self.format_reg(reg)), 85 | Token::DEC_RG(reg) => format!("DEC {}", self.format_reg(reg)), 86 | 87 | // General-Purpose Arithmetic and CPU Control 88 | Token::DAA => String::from("DAA"), 89 | Token::CPL => String::from("CPL"), 90 | Token::NEG => String::from("NEG"), 91 | Token::CCF => String::from("CCF"), 92 | Token::SCF => String::from("SCF"), 93 | Token::NOP => String::from("NOP"), 94 | Token::HALT => String::from("HALT"), 95 | Token::DI => String::from("DI"), 96 | Token::EI => String::from("EI"), 97 | Token::IM(mode) => format!("IM {}", match mode { 98 | IntMode::IM0 | IntMode::IM01 => "0", 99 | IntMode::IM1 => "1", 100 | IntMode::IM2 => "2", 101 | }), 102 | 103 | // 16-Bit Arithmetic 104 | Token::ADD_RP_RP(dst, src) => format!("ADD {},{}", self.format_regpair(dst), self.format_regpair(src)), 105 | Token::ADC_HL_RP(rpair) => format!("ADC HL,{}", self.format_regpair(rpair)), 106 | Token::SBC_HL_RP(rpair) => format!("SBC HL,{}", self.format_regpair(rpair)), 107 | Token::INC_RP(rpair) => format!("INC {}", self.format_regpair(rpair)), 108 | Token::DEC_RP(rpair) => format!("DEC {}", self.format_regpair(rpair)), 109 | 110 | // Rotate and Shift 111 | Token::SHOP(op, reg, None) => match op { 112 | ShiftOp::RLC => format!("RLC {}", self.format_reg(reg)), 113 | ShiftOp::RRC => format!("RRC {}", self.format_reg(reg)), 114 | ShiftOp::RL => format!("RL {}", self.format_reg(reg)), 115 | ShiftOp::RR => format!("RR {}", self.format_reg(reg)), 116 | ShiftOp::SLA => format!("SLA {}", self.format_reg(reg)), 117 | ShiftOp::SRA => format!("SRA {}", self.format_reg(reg)), 118 | ShiftOp::SLL => format!("SLL {}", self.format_reg(reg)), 119 | ShiftOp::SRL => format!("SRL {}", self.format_reg(reg)), 120 | ShiftOp::RLCA => String::from("RLCA"), 121 | ShiftOp::RRCA => String::from("RRCA"), 122 | ShiftOp::RLA => String::from("RLA"), 123 | ShiftOp::RRA => String::from("RRA"), 124 | ShiftOp::RLD => String::from("RLD"), 125 | ShiftOp::RRD => String::from("RRD"), 126 | }, 127 | Token::SHOP(op, reg, Some(dst)) => match op { 128 | ShiftOp::RLC => format!("RLC {},{}", self.format_reg(reg), self.format_reg(dst)), 129 | ShiftOp::RRC => format!("RRC {},{}", self.format_reg(reg), self.format_reg(dst)), 130 | ShiftOp::RL => format!("RL {},{}", self.format_reg(reg), self.format_reg(dst)), 131 | ShiftOp::RR => format!("RR {},{}", self.format_reg(reg), self.format_reg(dst)), 132 | ShiftOp::SLA => format!("SLA {},{}", self.format_reg(reg), self.format_reg(dst)), 133 | ShiftOp::SRA => format!("SRA {},{}", self.format_reg(reg), self.format_reg(dst)), 134 | ShiftOp::SLL => format!("SLL {},{}", self.format_reg(reg), self.format_reg(dst)), 135 | ShiftOp::SRL => format!("SRL {},{}", self.format_reg(reg), self.format_reg(dst)), 136 | _ => unreachable!() 137 | }, 138 | 139 | // Bit Set, Reset and Test 140 | Token::BIT(bit, reg) => format!("BIT {},{}", bit, self.format_reg(reg)), 141 | Token::SET(bit, reg, None) => format!("SET {},{}", bit, self.format_reg(reg)), 142 | Token::SET(bit, reg, Some(dst)) => format!( 143 | "SET {},{},{}", 144 | bit, self.format_reg(reg), self.format_reg(dst) 145 | ), 146 | Token::RES(bit, reg, None) => format!("RES {},{}", bit, self.format_reg(reg)), 147 | Token::RES(bit, reg, Some(dst)) => format!( 148 | "RES {},{},{}", 149 | bit, self.format_reg(reg), self.format_reg(dst) 150 | ), 151 | 152 | // Jump, Call and Return 153 | Token::JP(cond) => match cond { 154 | Condition::None => format!("JP {}", self.format_data()), 155 | _ => format!("JP {},{}", self.format_condition(cond), self.format_data()) 156 | }, 157 | Token::JP_RP(rpair) => format!("JP ({})", self.format_regpair(rpair)), 158 | Token::JR(cond) => match cond { 159 | Condition::None => format!("JR {}", self.format_addr_displacement()), 160 | _ => format!("JR {},{}", self.format_condition(cond), self.format_addr_displacement()) 161 | }, 162 | Token::DJNZ => format!("DJNZ {}", self.format_addr_displacement()), 163 | Token::CALL(cond) => match cond { 164 | Condition::None => format!("CALL {}", self.format_data()), 165 | _ => format!("CALL {},{}", self.format_condition(cond), self.format_data()) 166 | }, 167 | Token::RET(cond) => match cond { 168 | Condition::None => String::from("RET"), 169 | _ => format!("RET {}", self.format_condition(cond)) 170 | }, 171 | Token::RETI => String::from("RETI"), 172 | Token::RETN => String::from("RETN"), 173 | Token::RST(addr) => format!("RST {}", self.format_byte(*addr)), 174 | 175 | // IO 176 | Token::IN_A_N => format!("IN A,({})", self.format_data()), 177 | Token::OUT_N_A => format!("OUT ({}),A", self.format_data()), 178 | Token::IN_RG_AtBC(reg) => format!("IN {},(C)", self.format_reg(reg)), 179 | Token::OUT_AtBC_RG(reg) => format!("OUT (C),{}", self.format_reg(reg)), 180 | Token::IN_AtBC => String::from("IN (C)"), 181 | Token::OUT_AtBC_0 => String::from("OUT (C),0"), 182 | 183 | // Block transfer, search and IO 184 | Token::BLOP(op) => String::from(match op { 185 | BlockOp::LDI => "LDI", 186 | BlockOp::CPI => "CPI", 187 | BlockOp::INI => "INI", 188 | BlockOp::OUTI => "OUTI", 189 | BlockOp::LDD => "LDD", 190 | BlockOp::CPD => "CPD", 191 | BlockOp::IND => "IND", 192 | BlockOp::OUTD => "OUTD", 193 | BlockOp::LDIR => "LDIR", 194 | BlockOp::CPIR => "CPIR", 195 | BlockOp::INIR => "INIR", 196 | BlockOp::OTIR => "OTIR", 197 | BlockOp::LDDR => "LDDR", 198 | BlockOp::CPDR => "CPDR", 199 | BlockOp::INDR => "INDR", 200 | BlockOp::OTDR => "OTDR", 201 | }), 202 | 203 | other => unreachable!("{:?}", other) 204 | 205 | } 206 | 207 | } 208 | 209 | fn format_condition(&self, condition: &Condition) -> &'static str { 210 | match condition { 211 | Condition::NZ => "NZ", 212 | Condition::Z => "Z", 213 | Condition::NC => "NC", 214 | Condition::C => "C", 215 | Condition::PO => "PO", 216 | Condition::PE => "PE", 217 | Condition::P => "P", 218 | Condition::M => "M", 219 | other => unreachable!("{:?}", other) 220 | } 221 | } 222 | 223 | fn format_regpair(&self, regpair: &RegPair) -> &'static str { 224 | match regpair { 225 | RegPair::BC => "BC", 226 | RegPair::DE => "DE", 227 | RegPair::HL => "HL", 228 | RegPair::SP => "SP", 229 | RegPair::AF => "AF", 230 | RegPair::IX => "IX", 231 | RegPair::IY => "IY", 232 | other => unreachable!("{:?}", other) 233 | } 234 | } 235 | 236 | fn format_reg(&self, reg: &Reg) -> String { 237 | match reg { 238 | Reg::B => String::from("B"), 239 | Reg::C => String::from("C"), 240 | Reg::D => String::from("D"), 241 | Reg::E => String::from("E"), 242 | Reg::H => String::from("H"), 243 | Reg::L => String::from("L"), 244 | Reg::AtHL => String::from("(HL)"), 245 | Reg::A => String::from("A"), 246 | Reg::I => String::from("I"), 247 | Reg::R => String::from("R"), 248 | Reg::IXH => String::from("IXH"), 249 | Reg::IXL => String::from("IXL"), 250 | Reg::IYH => String::from("IYH"), 251 | Reg::IYL => String::from("IYL"), 252 | Reg::AtIX => { 253 | let displacement = self.displacement.unwrap(); 254 | format!("(IX{})", self.format_number_with_sign(displacement as i32)) 255 | }, 256 | Reg::AtIY => { 257 | let displacement = self.displacement.unwrap(); 258 | format!("(IY{})", self.format_number_with_sign(displacement as i32)) 259 | }, 260 | other => unreachable!("{:?}", other) 261 | } 262 | } 263 | 264 | fn format_alu_op(&self, op: &AluOp, operand: &String) -> String { 265 | match op { 266 | AluOp::ADD => format!("ADD A,{}", operand), 267 | AluOp::ADC => format!("ADC A,{}", operand), 268 | AluOp::SUB => format!("SUB {}", operand), 269 | AluOp::SBC => format!("SBC A,{}", operand), 270 | AluOp::AND => format!("AND {}", operand), 271 | AluOp::XOR => format!("XOR {}", operand), 272 | AluOp::OR => format!("OR {}", operand), 273 | AluOp::CP => format!("CP {}", operand), 274 | } 275 | } 276 | 277 | fn format_data(&self) -> String { 278 | match self.data { 279 | Some(DataValue::Byte(byte)) => self.format_byte(byte), 280 | Some(DataValue::Word(word)) => self.format_word(word), 281 | _ => panic!("Expecting immediate data") 282 | } 283 | } 284 | 285 | fn format_addr_displacement(&self) -> String { 286 | let displacement = self.displacement.unwrap() as i32 + 2; 287 | format!("${}", self.format_number_with_sign(displacement)) 288 | } 289 | 290 | fn format_byte(&self, byte: u8) -> String { 291 | format!("{:X}h", byte) 292 | } 293 | 294 | fn format_word(&self, word: u16) -> String { 295 | format!("{:X}h", word) 296 | } 297 | 298 | fn format_number_with_sign(&self, num: i32) -> String { 299 | format!("{}{:X}h", (if num < 0 { "-" } else { "+" }), num.abs()) 300 | } 301 | 302 | } 303 | -------------------------------------------------------------------------------- /librespectrum/src/cpu/decoder/decoder.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | pin::Pin, 3 | ops::{Coroutine, CoroutineState}, 4 | }; 5 | 6 | use crate::{ 7 | mkword, 8 | cpu::tokens::{ 9 | Token, TokenType, DataValue, Reg, RegPair, 10 | IntMode, BlockOp, ShiftOp, AluOp, Condition 11 | }, 12 | }; 13 | 14 | use super::Instruction; 15 | 16 | /// Result of decoding a byte into a token 17 | pub struct TokenDecodeResult { 18 | /// Decoded token 19 | pub token: Token, 20 | /// Expected type for the next token 21 | pub upnext: TokenType, 22 | } 23 | 24 | /// Create coroutine which accepts bytes, yields decoded 25 | /// tokens and returns cpu instruction when entire instruction sequence is decoded 26 | pub fn instruction_decoder() -> impl Coroutine { 27 | 28 | let mut tdecoder = token_decoder(); 29 | let mut displacement: Option = None; 30 | let mut data: Option = None; 31 | let mut opcode: Option = None; 32 | 33 | #[coroutine] move |mut byte: u8| { 34 | 35 | loop { 36 | 37 | let state = Pin::new(&mut tdecoder).resume(byte); 38 | 39 | let (complete, result) = match state { 40 | CoroutineState::Yielded(result) => (false, result), 41 | CoroutineState::Complete(result) => (true, result), 42 | }; 43 | 44 | match result.token { 45 | Token::Prefix(_) => (), 46 | Token::Displacement(value) => displacement = Some(value), 47 | Token::Data(value) => data = Some(value), 48 | token => opcode = Some(token) 49 | } 50 | 51 | if complete { 52 | return Instruction { 53 | opcode: opcode.expect("Expecting opcode to be defined"), 54 | displacement, 55 | data, 56 | }; 57 | } else { 58 | byte = yield result; 59 | } 60 | 61 | } 62 | 63 | } 64 | 65 | } 66 | 67 | fn get_x(byte: u8) -> u8 { (byte & 0b11000000) >> 6 } 68 | fn get_y(byte: u8) -> u8 { (byte & 0b00111000) >> 3 } 69 | fn get_z(byte: u8) -> u8 { byte & 0b00000111 } 70 | fn get_p(byte: u8) -> u8 { (byte & 0b00110000) >> 4 } 71 | fn get_q(byte: u8) -> u8 { (byte & 0b00001000) >> 3 } 72 | 73 | /// Create coroutine which accepts bytes, yields decoded 74 | /// tokens and returns when entire instruction sequence is decoded 75 | fn token_decoder() -> impl Coroutine { 76 | 77 | #[coroutine] |mut byte: u8| { 78 | 79 | let mut prefix: Option = None; 80 | 81 | { 82 | // Z80 opcode may have multiple (possibly) duplicate and overriding each other prefixes. 83 | // Here we try to interpret incoming bytes as prefix bytes until we reach actual opcode 84 | // or displacement byte 85 | let mut pdecoder = prefix_decoder(); 86 | while let CoroutineState::Yielded(result) = Pin::new(&mut pdecoder).resume(byte) { 87 | if let TokenDecodeResult { token: Token::Prefix(code), .. } = result { 88 | prefix = Some(code); 89 | } 90 | // Re-yield current prefix token and advance to the next byte 91 | byte = yield result; 92 | } 93 | } 94 | 95 | // When DD/FD prefix is set it alters (HL), H, L to (IX/IY+d), IXH/IYH, IXL/IYL resp. 96 | let alt_reg = move |reg: Reg| match prefix { 97 | None => reg, 98 | Some(other) => match other & 0xff { 99 | 0xdd => match reg { 100 | Reg::H => Reg::IXH, 101 | Reg::L => Reg::IXL, 102 | Reg::AtHL => Reg::AtIX, 103 | _ => reg 104 | }, 105 | 0xfd => match reg { 106 | Reg::H => Reg::IYH, 107 | Reg::L => Reg::IYL, 108 | Reg::AtHL => Reg::AtIY, 109 | _ => reg 110 | }, 111 | _ => reg 112 | } 113 | }; 114 | 115 | // When DD/FD prefix is set it alters HL to IX/IY 116 | let alt_rpair = move |rpair: RegPair| match prefix { 117 | None => rpair, 118 | Some(other) => match other & 0xff { 119 | 0xdd => if rpair == RegPair::HL { RegPair::IX } else { rpair }, 120 | 0xfd => if rpair == RegPair::HL { RegPair::IY } else { rpair }, 121 | _ => rpair 122 | } 123 | }; 124 | 125 | match prefix { 126 | 127 | Some(0xed) => match (get_x(byte), get_y(byte), get_z(byte)) { 128 | (1, 6, 0) => TokenDecodeResult { token: Token::IN_AtBC, upnext: TokenType::Opcode }, 129 | (1, y, 0) => TokenDecodeResult { token: Token::IN_RG_AtBC(Reg::from(y)), upnext: TokenType::Opcode }, 130 | (1, 6, 1) => TokenDecodeResult { token: Token::OUT_AtBC_0, upnext: TokenType::Opcode }, 131 | (1, y, 1) => TokenDecodeResult { token: Token::OUT_AtBC_RG(Reg::from(y)), upnext: TokenType::Opcode }, 132 | (1, _, 2) => { 133 | let (p, q) = (get_p(byte), get_q(byte)); 134 | TokenDecodeResult { 135 | token: if q == 0 { 136 | Token::SBC_HL_RP(RegPair::from(p).prefer_sp()) 137 | } else { 138 | Token::ADC_HL_RP(RegPair::from(p).prefer_sp()) 139 | }, 140 | upnext: TokenType::Opcode 141 | } 142 | }, 143 | (1, _, 3) => { 144 | let (p, q) = (get_p(byte), get_q(byte)); 145 | let low_data_byte = yield TokenDecodeResult { 146 | token: if q == 0 { 147 | Token::LD_MM_RP(RegPair::from(p).prefer_sp()) 148 | } else { 149 | Token::LD_RP_MM(RegPair::from(p).prefer_sp()) 150 | }, 151 | upnext: TokenType::Data 152 | }; 153 | let high_data_byte = yield TokenDecodeResult { 154 | token: Token::Data(DataValue::Byte(low_data_byte)), 155 | upnext: TokenType::Data 156 | }; 157 | TokenDecodeResult { 158 | token: Token::Data(DataValue::Word(mkword!(high_data_byte, low_data_byte))), 159 | upnext: TokenType::Opcode 160 | } 161 | }, 162 | (1, _, 4) => TokenDecodeResult { token: Token::NEG, upnext: TokenType::Opcode }, 163 | (1, 1, 5) => TokenDecodeResult { token: Token::RETI, upnext: TokenType::Opcode }, 164 | (1, _, 5) => TokenDecodeResult { token: Token::RETN, upnext: TokenType::Opcode }, 165 | (1, y, 6) => TokenDecodeResult { token: Token::IM(IntMode::from(y)), upnext: TokenType::Opcode }, 166 | (1, 0, 7) => TokenDecodeResult { token: Token::LD_RG_RG(Reg::I, Reg::A), upnext: TokenType::Opcode }, 167 | (1, 1, 7) => TokenDecodeResult { token: Token::LD_RG_RG(Reg::R, Reg::A), upnext: TokenType::Opcode }, 168 | (1, 2, 7) => TokenDecodeResult { token: Token::LD_RG_RG(Reg::A, Reg::I), upnext: TokenType::Opcode }, 169 | (1, 3, 7) => TokenDecodeResult { token: Token::LD_RG_RG(Reg::A, Reg::R), upnext: TokenType::Opcode }, 170 | (1, 4, 7) => TokenDecodeResult { token: Token::SHOP(ShiftOp::RRD, Reg::AtHL, None), upnext: TokenType::Opcode }, 171 | (1, 5, 7) => TokenDecodeResult { token: Token::SHOP(ShiftOp::RLD, Reg::AtHL, None), upnext: TokenType::Opcode }, 172 | (1, _, 7) => TokenDecodeResult { token: Token::NOP, upnext: TokenType::Opcode }, 173 | (1, _, _) => unreachable!(), 174 | (2, y, z) if z <= 3 && y >= 4 => TokenDecodeResult { 175 | token: Token::BLOP(BlockOp::from((y << 2 ) | z)), 176 | upnext: TokenType::Opcode 177 | }, 178 | (2, _, _) => TokenDecodeResult { token: Token::NOP, upnext: TokenType::Opcode }, 179 | (_, _, _) => TokenDecodeResult { token: Token::NOP, upnext: TokenType::Opcode } 180 | }, 181 | 182 | Some(0xcb) => { 183 | let (y, z) = (get_y(byte), get_z(byte)); 184 | match get_x(byte) { 185 | 0 => TokenDecodeResult { token: Token::SHOP(ShiftOp::from(y), Reg::from(z), None), upnext: TokenType::Opcode }, 186 | 1 => TokenDecodeResult { token: Token::BIT(y, Reg::from(z)), upnext: TokenType::Opcode }, 187 | 2 => TokenDecodeResult { token: Token::RES(y, Reg::from(z), None), upnext: TokenType::Opcode }, 188 | 3 => TokenDecodeResult { token: Token::SET(y, Reg::from(z), None), upnext: TokenType::Opcode }, 189 | _ => unreachable!() 190 | } 191 | }, 192 | 193 | Some(0xcbdd) | Some(0xcbfd) => { 194 | // First byte after DDCB/FDCB is a displacement byte and then opcode follows 195 | byte = yield TokenDecodeResult { token: Token::Displacement(byte as i8), upnext: TokenType::Opcode }; 196 | TokenDecodeResult { 197 | token: match (get_x(byte), get_y(byte), get_z(byte)) { 198 | (0, y, 6) => Token::SHOP(ShiftOp::from(y), alt_reg(Reg::AtHL), None), 199 | (0, y, z) => Token::SHOP(ShiftOp::from(y), alt_reg(Reg::AtHL), Some(Reg::from(z))), 200 | (1, y, _) => Token::BIT(y, alt_reg(Reg::AtHL)), 201 | (2, y, 6) => Token::RES(y, alt_reg(Reg::AtHL), None), 202 | (2, y, z) => Token::RES(y, alt_reg(Reg::AtHL), Some(Reg::from(z))), 203 | (3, y, 6) => Token::SET(y, alt_reg(Reg::AtHL), None), 204 | (3, y, z) => Token::SET(y, alt_reg(Reg::AtHL), Some(Reg::from(z))), 205 | (_, _, _) => unreachable!() 206 | }, 207 | upnext: TokenType::Opcode 208 | } 209 | }, 210 | 211 | Some(0xdd) | Some(0xfd) | None => match (get_x(byte), get_y(byte), get_z(byte)) { 212 | 213 | // x=0, z=0 214 | (0, 0, 0) => TokenDecodeResult { token: Token::NOP, upnext: TokenType::Opcode }, 215 | (0, 1, 0) => TokenDecodeResult { token: Token::EX_AF, upnext: TokenType::Opcode }, 216 | (0, y, 0) => { 217 | let displacement_byte = yield TokenDecodeResult { 218 | token: match y { 219 | 2 => Token::DJNZ, 220 | 3 => Token::JR(Condition::None), 221 | _ => Token::JR(Condition::from(y & 0b11)) 222 | }, 223 | upnext: TokenType::Displacement 224 | }; 225 | TokenDecodeResult { token: Token::Displacement(displacement_byte as i8), upnext: TokenType::Opcode } 226 | }, 227 | 228 | // x=0, z=1 229 | (0, _, 1) => { 230 | let p = get_p(byte); 231 | TokenDecodeResult { 232 | token: if get_q(byte) == 0 { 233 | let low_data_byte = yield TokenDecodeResult { 234 | token: Token::LD_RP_NN(alt_rpair(RegPair::from(p).prefer_sp())), 235 | upnext: TokenType::Data 236 | }; 237 | let high_data_byte = yield TokenDecodeResult { 238 | token: Token::Data(DataValue::Byte(low_data_byte)), 239 | upnext: TokenType::Data 240 | }; 241 | Token::Data(DataValue::Word(mkword!(high_data_byte, low_data_byte))) 242 | } else { 243 | Token::ADD_RP_RP(alt_rpair(RegPair::HL), alt_rpair(RegPair::from(p).prefer_sp())) 244 | }, 245 | upnext: TokenType::Opcode 246 | } 247 | }, 248 | 249 | // x=0, z=2 250 | (0, 0, 2) => TokenDecodeResult { token: Token::LD_AtRP_A(RegPair::BC), upnext: TokenType::Opcode }, 251 | (0, 1, 2) => TokenDecodeResult { token: Token::LD_A_AtRP(RegPair::BC), upnext: TokenType::Opcode }, 252 | (0, 2, 2) => TokenDecodeResult { token: Token::LD_AtRP_A(RegPair::DE), upnext: TokenType::Opcode }, 253 | (0, 3, 2) => TokenDecodeResult { token: Token::LD_A_AtRP(RegPair::DE), upnext: TokenType::Opcode }, 254 | (0, y, 2) => { 255 | let low_data_byte = yield TokenDecodeResult { 256 | token: match y { 257 | 4 => Token::LD_MM_RP(alt_rpair(RegPair::HL)), 258 | 5 => Token::LD_RP_MM(alt_rpair(RegPair::HL)), 259 | 6 => Token::LD_MM_A, 260 | 7 => Token::LD_A_MM, 261 | _ => unreachable!() 262 | }, 263 | upnext: TokenType::Data 264 | }; 265 | let high_data_byte = yield TokenDecodeResult { 266 | token: Token::Data(DataValue::Byte(low_data_byte)), 267 | upnext: TokenType::Data 268 | }; 269 | TokenDecodeResult { 270 | token: Token::Data(DataValue::Word(mkword!(high_data_byte, low_data_byte))), 271 | upnext: TokenType::Opcode 272 | } 273 | }, 274 | 275 | // x=0, z=3 276 | (0, _, 3) => { 277 | let (p, q) = (get_p(byte), get_q(byte)); 278 | let rp = alt_rpair(RegPair::from(p).prefer_sp()); 279 | TokenDecodeResult { 280 | token: if q == 0 { Token::INC_RP(rp) } else { Token::DEC_RP(rp) }, 281 | upnext: TokenType::Opcode 282 | } 283 | }, 284 | 285 | // x=0, z=4,5 286 | (0, y, z @ (4 | 5)) => { 287 | let reg = Reg::from(y); 288 | let opcode_token = if z == 4 { 289 | Token::INC_RG(alt_reg(reg)) 290 | } else { 291 | Token::DEC_RG(alt_reg(reg)) 292 | }; 293 | TokenDecodeResult { 294 | token: if prefix.is_some() && reg == Reg::AtHL { 295 | let displacement_byte = yield TokenDecodeResult { 296 | token: opcode_token, 297 | upnext: TokenType::Displacement 298 | }; 299 | Token::Displacement(displacement_byte as i8) 300 | } else { 301 | opcode_token 302 | }, 303 | upnext: TokenType::Opcode 304 | } 305 | }, 306 | 307 | // x=0, z=6 308 | (0, y, 6) => { 309 | let reg = Reg::from(y); 310 | let opcode_token = Token::LD_RG_N(alt_reg(reg)); 311 | let data_byte = yield TokenDecodeResult { 312 | token: if prefix.is_some() && reg == Reg::AtHL { 313 | let displacement_byte = yield TokenDecodeResult { 314 | token: opcode_token, 315 | upnext: TokenType::Displacement 316 | }; 317 | Token::Displacement(displacement_byte as i8) 318 | } else { 319 | opcode_token 320 | }, 321 | upnext: TokenType::Data 322 | }; 323 | TokenDecodeResult { 324 | token: Token::Data(DataValue::Byte(data_byte)), 325 | upnext: TokenType::Opcode 326 | } 327 | }, 328 | 329 | // x=0, z=7 330 | (0, 0, 7) => TokenDecodeResult { token: Token::SHOP(ShiftOp::RLCA, Reg::A, None), upnext: TokenType::Opcode }, 331 | (0, 1, 7) => TokenDecodeResult { token: Token::SHOP(ShiftOp::RRCA, Reg::A, None), upnext: TokenType::Opcode }, 332 | (0, 2, 7) => TokenDecodeResult { token: Token::SHOP(ShiftOp::RLA, Reg::A, None), upnext: TokenType::Opcode }, 333 | (0, 3, 7) => TokenDecodeResult { token: Token::SHOP(ShiftOp::RRA, Reg::A, None), upnext: TokenType::Opcode }, 334 | (0, 4, 7) => TokenDecodeResult { token: Token::DAA, upnext: TokenType::Opcode }, 335 | (0, 5, 7) => TokenDecodeResult { token: Token::CPL, upnext: TokenType::Opcode }, 336 | (0, 6, 7) => TokenDecodeResult { token: Token::SCF, upnext: TokenType::Opcode }, 337 | (0, 7, 7) => TokenDecodeResult { token: Token::CCF, upnext: TokenType::Opcode }, 338 | (0, _, 7) => unreachable!(), 339 | 340 | // x=1 341 | (1, y, z) => { 342 | let dst_reg = Reg::from(y); 343 | let src_reg = Reg::from(z); 344 | let opcode_token = if dst_reg == Reg::AtHL && src_reg == Reg::AtHL { 345 | Token::HALT // exception 346 | } else if dst_reg == Reg::AtHL { 347 | Token::LD_RG_RG(alt_reg(dst_reg), src_reg) 348 | } else if src_reg == Reg::AtHL { 349 | Token::LD_RG_RG(dst_reg, alt_reg(src_reg)) 350 | } else { 351 | Token::LD_RG_RG(alt_reg(dst_reg), alt_reg(src_reg)) 352 | }; 353 | TokenDecodeResult { 354 | token: if prefix.is_some() && (dst_reg == Reg::AtHL || src_reg == Reg::AtHL) { 355 | let displacement_byte = yield TokenDecodeResult { 356 | token: opcode_token, 357 | upnext: TokenType::Displacement 358 | }; 359 | Token::Displacement(displacement_byte as i8) 360 | } else { 361 | opcode_token 362 | }, 363 | upnext: TokenType::Opcode 364 | } 365 | }, 366 | 367 | // x=2 368 | (2, y, z) => { 369 | let reg = Reg::from(z); 370 | let opcode_token = Token::ALU(AluOp::from(y), Some(alt_reg(reg))); 371 | TokenDecodeResult { 372 | token: if prefix.is_some() && reg == Reg::AtHL { 373 | let displacement_byte = yield TokenDecodeResult { 374 | token: opcode_token, 375 | upnext: TokenType::Displacement 376 | }; 377 | Token::Displacement(displacement_byte as i8) 378 | } else { 379 | opcode_token 380 | }, 381 | upnext: TokenType::Opcode 382 | } 383 | }, 384 | 385 | // x=3, z=0 386 | (3, y, 0) => TokenDecodeResult { 387 | token: Token::RET(Condition::from(y)), 388 | upnext: TokenType::Opcode 389 | }, 390 | 391 | // x=3, z=1 392 | (3, _, 1) => { 393 | let (p, q) = (get_p(byte), get_q(byte)); 394 | TokenDecodeResult { 395 | token: if q == 0 { 396 | Token::POP(alt_rpair(RegPair::from(p).prefer_af())) 397 | } else { 398 | match p { 399 | 0 => Token::RET(Condition::None), 400 | 1 => Token::EXX, 401 | 2 => Token::JP_RP(alt_rpair(RegPair::HL)), 402 | 3 => Token::LD_SP_RP(alt_rpair(RegPair::HL)), 403 | _ => unreachable!() 404 | } 405 | }, 406 | upnext: TokenType::Opcode 407 | } 408 | }, 409 | 410 | // x=3, z=2 & 411 | // x=3, y=0, z=3 412 | (3, y, z @ 2) | (3, y @ 0, z @ 3) => { 413 | let low_data_byte = yield TokenDecodeResult { 414 | token: Token::JP(if z == 2 { Condition::from(y) } else { Condition::None }), 415 | upnext: TokenType::Data 416 | }; 417 | let high_data_byte = yield TokenDecodeResult { 418 | token: Token::Data(DataValue::Byte(low_data_byte)), 419 | upnext: TokenType::Data 420 | }; 421 | TokenDecodeResult { 422 | token: Token::Data(DataValue::Word(mkword!(high_data_byte, low_data_byte))), 423 | upnext: TokenType::Opcode 424 | } 425 | }, 426 | 427 | // x=3, z=3 428 | (3, y @ (2 | 3), 3) => { 429 | let port_byte = yield TokenDecodeResult { 430 | token: if y == 2 { Token::OUT_N_A } else { Token::IN_A_N }, 431 | upnext: TokenType::Data 432 | }; 433 | TokenDecodeResult { 434 | token: Token::Data(DataValue::Byte(port_byte)), 435 | upnext: TokenType::Opcode 436 | } 437 | }, 438 | (3, 4, 3) => TokenDecodeResult { 439 | token: Token::EX_AtSP_RP(alt_rpair(RegPair::HL)), 440 | upnext: TokenType::Opcode 441 | }, 442 | (3, 5, 3) => TokenDecodeResult { token: Token::EX_DE_HL, upnext: TokenType::Opcode }, 443 | (3, 6, 3) => TokenDecodeResult { token: Token::DI, upnext: TokenType::Opcode }, 444 | (3, 7, 3) => TokenDecodeResult { token: Token::EI, upnext: TokenType::Opcode }, 445 | (3, _, 3) => unreachable!(), 446 | 447 | // x=3, z=4,5 448 | (3, y, z @ (4 | 5)) => { 449 | let (p, q) = (get_p(byte), get_q(byte)); 450 | if z == 5 && q == 0 { 451 | TokenDecodeResult { 452 | token: Token::PUSH(alt_rpair(RegPair::from(p).prefer_af())), 453 | upnext: TokenType::Opcode 454 | } 455 | } else { 456 | let low_data_byte = yield TokenDecodeResult { 457 | token: Token::CALL(if z == 4 { Condition::from(y) } else { Condition::None }), 458 | upnext: TokenType::Data 459 | }; 460 | let high_data_byte = yield TokenDecodeResult { 461 | token: Token::Data(DataValue::Byte(low_data_byte)), 462 | upnext: TokenType::Data 463 | }; 464 | TokenDecodeResult { 465 | token: Token::Data(DataValue::Word(mkword!(high_data_byte, low_data_byte))), 466 | upnext: TokenType::Opcode 467 | } 468 | } 469 | }, 470 | 471 | // x=3, z=6 472 | (3, y, 6) => { 473 | let data_byte = yield TokenDecodeResult { 474 | token: Token::ALU(AluOp::from(y), None), 475 | upnext: TokenType::Data 476 | }; 477 | TokenDecodeResult { 478 | token: Token::Data(DataValue::Byte(data_byte)), 479 | upnext: TokenType::Opcode 480 | } 481 | }, 482 | 483 | // x=3, z=7 484 | (3, y, 7) => TokenDecodeResult { 485 | token: Token::RST(y << 3), 486 | upnext: TokenType::Opcode 487 | }, 488 | 489 | (_, _, _) => unreachable!() 490 | 491 | }, 492 | 493 | _ => unreachable!() 494 | 495 | } 496 | 497 | } 498 | 499 | } 500 | 501 | /// Create coroutine which accepts bytes, yields decoded 502 | /// prefix tokens and completes on first non-prefix token 503 | fn prefix_decoder() -> impl Coroutine { 504 | 505 | #[coroutine] |mut byte: u8| { 506 | 507 | let mut current_prefix: Option = None; 508 | 509 | loop { 510 | 511 | let (prefix, upnext) = match current_prefix { 512 | 513 | // CB & ED are always followed by opcode byte 514 | Some(0xcb) | Some(0xed) => return, 515 | 516 | // DDCB & FDCB are always followed by displacement byte 517 | Some(0xcbdd) | Some(0xcbfd) => return, 518 | 519 | Some(val @ (0xdd | 0xfd)) => match byte { 520 | 521 | // If DD or FD followed by DD, ED or FD we should ignore former prefix 522 | 0xed | 0xdd | 0xfd => (byte as u16, TokenType::Opcode), 523 | 524 | // DD or FD followed by CB gives DDCB or FDCB 525 | 0xcb => (mkword!(byte, val), TokenType::Displacement), 526 | 527 | // Otherwise it is followed by opcode 528 | _ => return 529 | 530 | }, 531 | 532 | _ => match byte { 533 | 534 | // All prefixes start with CB, ED, DD or FD 535 | 0xcb | 0xed | 0xdd | 0xfd => (byte as u16, TokenType::Opcode), 536 | 537 | // Otherwise it is an opcode 538 | _ => return 539 | 540 | } 541 | 542 | }; 543 | 544 | // Yield prefix token and advance to the next byte 545 | byte = yield TokenDecodeResult { token: Token::Prefix(prefix), upnext }; 546 | 547 | current_prefix = Some(prefix); 548 | 549 | } 550 | 551 | } 552 | 553 | } 554 | -------------------------------------------------------------------------------- /librespectrum/tests/misc/opcodes.lst: -------------------------------------------------------------------------------- 1 | ; ------------------------------------------------- 2 | ; List taken from "The Undocumented Z80 Documented" 3 | ; Chapter 9 Instructions Sorted by Opcode 4 | ; *** marks undocumented instruction 5 | ;-------------------------------------------------- 6 | 7 | ; Page 36 8 | 9 | 00 | NOP 10 | 01 80 C8 | LD BC,C880h 11 | 02 | LD (BC),A 12 | 03 | INC BC 13 | 04 | INC B 14 | 05 | DEC B 15 | 06 AD | LD B,ADh 16 | 07 | RLCA 17 | 08 | EX AF,AF' 18 | 09 | ADD HL,BC 19 | 0A | LD A,(BC) 20 | 0B | DEC BC 21 | 0C | INC C 22 | 0D | DEC C 23 | 0E B5 | LD C,B5h 24 | 0F | RRCA 25 | 10 38 | DJNZ $+3Ah 26 | 11 32 DC | LD DE,DC32h 27 | 12 | LD (DE),A 28 | 13 | INC DE 29 | 14 | INC D 30 | 15 | DEC D 31 | 16 2B | LD D,2Bh 32 | 17 | RLA 33 | 18 99 | JR $-65h 34 | 19 | ADD HL,DE 35 | 1A | LD A,(DE) 36 | 1B | DEC DE 37 | 1C | INC E 38 | 1D | DEC E 39 | 1E C3 | LD E,C3h 40 | 1F | RRA 41 | 20 05 | JR NZ,$+7h 42 | 21 11 9E | LD HL,9E11h 43 | 22 0B 2B | LD (2B0Bh),HL 44 | 23 | INC HL 45 | 24 | INC H 46 | 25 | DEC H 47 | 26 6E | LD H,6Eh 48 | 27 | DAA 49 | 28 06 | JR Z,$+8h 50 | 29 | ADD HL,HL 51 | 2A 33 CA | LD HL,(CA33h) 52 | 2B | DEC HL 53 | 2C | INC L 54 | 2D | DEC L 55 | 2E B4 | LD L,B4h 56 | 2F | CPL 57 | 30 DB | JR NC,$-23h 58 | 31 30 17 | LD SP,1730h 59 | 32 90 60 | LD (6090h),A 60 | 33 | INC SP 61 | 34 | INC (HL) 62 | 35 | DEC (HL) 63 | 36 7D | LD (HL),7Dh 64 | 37 | SCF 65 | 38 1B | JR C,$+1Dh 66 | 39 | ADD HL,SP 67 | 3A 65 6C | LD A,(6C65h) 68 | 3B | DEC SP 69 | 3C | INC A 70 | 3D | DEC A 71 | 3E 56 | LD A,56h 72 | 3F | CCF 73 | 40 | LD B,B 74 | 41 | LD B,C 75 | 42 | LD B,D 76 | 43 | LD B,E 77 | 44 | LD B,H 78 | 45 | LD B,L 79 | 46 | LD B,(HL) 80 | 47 | LD B,A 81 | 48 | LD C,B 82 | 49 | LD C,C 83 | 4A | LD C,D 84 | 4B | LD C,E 85 | 4C | LD C,H 86 | 4D | LD C,L 87 | 4E | LD C,(HL) 88 | 4F | LD C,A 89 | 50 | LD D,B 90 | 51 | LD D,C 91 | 52 | LD D,D 92 | 53 | LD D,E 93 | 54 | LD D,H 94 | 55 | LD D,L 95 | 56 | LD D,(HL) 96 | 57 | LD D,A 97 | 58 | LD E,B 98 | 59 | LD E,C 99 | 5A | LD E,D 100 | 5B | LD E,E 101 | 5C | LD E,H 102 | 5D | LD E,L 103 | 5E | LD E,(HL) 104 | 5F | LD E,A 105 | 60 | LD H,B 106 | 61 | LD H,C 107 | 62 | LD H,D 108 | 63 | LD H,E 109 | 64 | LD H,H 110 | 65 | LD H,L 111 | 66 | LD H,(HL) 112 | 67 | LD H,A 113 | 68 | LD L,B 114 | 69 | LD L,C 115 | 6A | LD L,D 116 | 6B | LD L,E 117 | 6C | LD L,H 118 | 6D | LD L,L 119 | 6E | LD L,(HL) 120 | 6F | LD L,A 121 | 70 | LD (HL),B 122 | 71 | LD (HL),C 123 | 72 | LD (HL),D 124 | 73 | LD (HL),E 125 | 74 | LD (HL),H 126 | 75 | LD (HL),L 127 | 76 | HALT 128 | 77 | LD (HL),A 129 | 78 | LD A,B 130 | 79 | LD A,C 131 | 7A | LD A,D 132 | 7B | LD A,E 133 | 7C | LD A,H 134 | 7D | LD A,L 135 | 7E | LD A,(HL) 136 | 7F | LD A,A 137 | 80 | ADD A,B 138 | 81 | ADD A,C 139 | 82 | ADD A,D 140 | 83 | ADD A,E 141 | 84 | ADD A,H 142 | 85 | ADD A,L 143 | 86 | ADD A,(HL) 144 | 87 | ADD A,A 145 | 88 | ADC A,B 146 | 89 | ADC A,C 147 | 8A | ADC A,D 148 | 8B | ADC A,E 149 | 8C | ADC A,H 150 | 8D | ADC A,L 151 | 8E | ADC A,(HL) 152 | 8F | ADC A,A 153 | 90 | SUB B 154 | 91 | SUB C 155 | 92 | SUB D 156 | 93 | SUB E 157 | 94 | SUB H 158 | 95 | SUB L 159 | 96 | SUB (HL) 160 | 97 | SUB A 161 | 98 | SBC A,B 162 | 99 | SBC A,C 163 | 9A | SBC A,D 164 | 9B | SBC A,E 165 | 9C | SBC A,H 166 | 9D | SBC A,L 167 | 9E | SBC A,(HL) 168 | 9F | SBC A,A 169 | A0 | AND B 170 | A1 | AND C 171 | A2 | AND D 172 | A3 | AND E 173 | A4 | AND H 174 | 175 | ; Page 37 176 | 177 | A5 | AND L 178 | A6 | AND (HL) 179 | A7 | AND A 180 | A8 | XOR B 181 | A9 | XOR C 182 | AA | XOR D 183 | AB | XOR E 184 | AC | XOR H 185 | AD | XOR L 186 | AE | XOR (HL) 187 | AF | XOR A 188 | B0 | OR B 189 | B1 | OR C 190 | B2 | OR D 191 | B3 | OR E 192 | B4 | OR H 193 | B5 | OR L 194 | B6 | OR (HL) 195 | B7 | OR A 196 | B8 | CP B 197 | B9 | CP C 198 | BA | CP D 199 | BB | CP E 200 | BC | CP H 201 | BD | CP L 202 | BE | CP (HL) 203 | BF | CP A 204 | C0 | RET NZ 205 | C1 | POP BC 206 | C2 AA 62 | JP NZ,62AAh 207 | C3 1A 0A | JP A1Ah 208 | C4 81 14 | CALL NZ,1481h 209 | C5 | PUSH BC 210 | C6 82 | ADD A,82h 211 | C7 | RST 0h 212 | C8 | RET Z 213 | C9 | RET 214 | CA 9D 09 | JP Z,99Dh 215 | CB 00 | RLC B 216 | CB 01 | RLC C 217 | CB 02 | RLC D 218 | CB 03 | RLC E 219 | CB 04 | RLC H 220 | CB 05 | RLC L 221 | CB 06 | RLC (HL) 222 | CB 07 | RLC A 223 | CB 08 | RRC B 224 | CB 09 | RRC C 225 | CB 0A | RRC D 226 | CB 0B | RRC E 227 | CB 0C | RRC H 228 | CB 0D | RRC L 229 | CB 0E | RRC (HL) 230 | CB 0F | RRC A 231 | CB 10 | RL B 232 | CB 11 | RL C 233 | CB 12 | RL D 234 | CB 13 | RL E 235 | CB 14 | RL H 236 | CB 15 | RL L 237 | CB 16 | RL (HL) 238 | CB 17 | RL A 239 | CB 18 | RR B 240 | CB 19 | RR C 241 | CB 1A | RR D 242 | CB 1B | RR E 243 | CB 1C | RR H 244 | CB 1D | RR L 245 | CB 1E | RR (HL) 246 | CB 1F | RR A 247 | CB 20 | SLA B 248 | CB 21 | SLA C 249 | CB 22 | SLA D 250 | CB 23 | SLA E 251 | CB 24 | SLA H 252 | CB 25 | SLA L 253 | CB 26 | SLA (HL) 254 | CB 27 | SLA A 255 | CB 28 | SRA B 256 | CB 29 | SRA C 257 | CB 2A | SRA D 258 | CB 2B | SRA E 259 | CB 2C | SRA H 260 | CB 2D | SRA L 261 | CB 2E | SRA (HL) 262 | CB 2F | SRA A 263 | CB 30 | SLL B; *** 264 | CB 31 | SLL C; *** 265 | CB 32 | SLL D; *** 266 | CB 33 | SLL E; *** 267 | CB 34 | SLL H; *** 268 | CB 35 | SLL L; *** 269 | CB 36 | SLL (HL); *** 270 | CB 37 | SLL A; *** 271 | CB 38 | SRL B 272 | CB 39 | SRL C 273 | CB 3A | SRL D 274 | CB 3B | SRL E 275 | CB 3C | SRL H 276 | CB 3D | SRL L 277 | CB 3E | SRL (HL) 278 | CB 3F | SRL A 279 | CB 40 | BIT 0,B 280 | CB 41 | BIT 0,C 281 | CB 42 | BIT 0,D 282 | CB 43 | BIT 0,E 283 | CB 44 | BIT 0,H 284 | CB 45 | BIT 0,L 285 | CB 46 | BIT 0,(HL) 286 | CB 47 | BIT 0,A 287 | CB 48 | BIT 1,B 288 | CB 49 | BIT 1,C 289 | CB 4A | BIT 1,D 290 | CB 4B | BIT 1,E 291 | CB 4C | BIT 1,H 292 | CB 4D | BIT 1,L 293 | CB 4E | BIT 1,(HL) 294 | CB 4F | BIT 1,A 295 | CB 50 | BIT 2,B 296 | CB 51 | BIT 2,C 297 | CB 52 | BIT 2,D 298 | CB 53 | BIT 2,E 299 | CB 54 | BIT 2,H 300 | CB 55 | BIT 2,L 301 | CB 56 | BIT 2,(HL) 302 | CB 57 | BIT 2,A 303 | CB 58 | BIT 3,B 304 | CB 59 | BIT 3,C 305 | CB 5A | BIT 3,D 306 | CB 5B | BIT 3,E 307 | CB 5C | BIT 3,H 308 | CB 5D | BIT 3,L 309 | CB 5E | BIT 3,(HL) 310 | CB 5F | BIT 3,A 311 | CB 60 | BIT 4,B 312 | CB 61 | BIT 4,C 313 | CB 62 | BIT 4,D 314 | CB 63 | BIT 4,E 315 | CB 64 | BIT 4,H 316 | CB 65 | BIT 4,L 317 | CB 66 | BIT 4,(HL) 318 | CB 67 | BIT 4,A 319 | CB 68 | BIT 5,B 320 | CB 69 | BIT 5,C 321 | CB 6A | BIT 5,D 322 | CB 6B | BIT 5,E 323 | CB 6C | BIT 5,H 324 | CB 6D | BIT 5,L 325 | CB 6E | BIT 5,(HL) 326 | CB 6F | BIT 5,A 327 | CB 70 | BIT 6,B 328 | CB 71 | BIT 6,C 329 | CB 72 | BIT 6,D 330 | CB 73 | BIT 6,E 331 | CB 74 | BIT 6,H 332 | CB 75 | BIT 6,L 333 | CB 76 | BIT 6,(HL) 334 | CB 77 | BIT 6,A 335 | CB 78 | BIT 7,B 336 | CB 79 | BIT 7,C 337 | CB 7A | BIT 7,D 338 | CB 7B | BIT 7,E 339 | CB 7C | BIT 7,H 340 | CB 7D | BIT 7,L 341 | CB 7E | BIT 7,(HL) 342 | CB 7F | BIT 7,A 343 | CB 80 | RES 0,B 344 | CB 81 | RES 0,C 345 | CB 82 | RES 0,D 346 | CB 83 | RES 0,E 347 | CB 84 | RES 0,H 348 | CB 85 | RES 0,L 349 | CB 86 | RES 0,(HL) 350 | CB 87 | RES 0,A 351 | CB 88 | RES 1,B 352 | CB 89 | RES 1,C 353 | CB 8A | RES 1,D 354 | CB 8B | RES 1,E 355 | CB 8C | RES 1,H 356 | CB 8D | RES 1,L 357 | CB 8E | RES 1,(HL) 358 | CB 8F | RES 1,A 359 | CB 90 | RES 2,B 360 | CB 91 | RES 2,C 361 | CB 92 | RES 2,D 362 | CB 93 | RES 2,E 363 | CB 94 | RES 2,H 364 | CB 95 | RES 2,L 365 | CB 96 | RES 2,(HL) 366 | CB 97 | RES 2,A 367 | CB 98 | RES 3,B 368 | CB 99 | RES 3,C 369 | CB 9A | RES 3,D 370 | CB 9B | RES 3,E 371 | CB 9C | RES 3,H 372 | CB 9D | RES 3,L 373 | CB 9E | RES 3,(HL) 374 | CB 9F | RES 3,A 375 | CB A0 | RES 4,B 376 | CB A1 | RES 4,C 377 | CB A2 | RES 4,D 378 | CB A3 | RES 4,E 379 | CB A4 | RES 4,H 380 | CB A5 | RES 4,L 381 | CB A6 | RES 4,(HL) 382 | CB A7 | RES 4,A 383 | CB A8 | RES 5,B 384 | CB A9 | RES 5,C 385 | CB AA | RES 5,D 386 | CB AB | RES 5,E 387 | CB AC | RES 5,H 388 | CB AD | RES 5,L 389 | CB AE | RES 5,(HL) 390 | CB AF | RES 5,A 391 | CB B0 | RES 6,B 392 | CB B1 | RES 6,C 393 | CB B2 | RES 6,D 394 | CB B3 | RES 6,E 395 | CB B4 | RES 6,H 396 | CB B5 | RES 6,L 397 | CB B6 | RES 6,(HL) 398 | CB B7 | RES 6,A 399 | CB B8 | RES 7,B 400 | CB B9 | RES 7,C 401 | CB BA | RES 7,D 402 | CB BB | RES 7,E 403 | CB BC | RES 7,H 404 | CB BD | RES 7,L 405 | CB BE | RES 7,(HL) 406 | CB BF | RES 7,A 407 | CB C0 | SET 0,B 408 | CB C1 | SET 0,C 409 | CB C2 | SET 0,D 410 | CB C3 | SET 0,E 411 | CB C4 | SET 0,H 412 | CB C5 | SET 0,L 413 | CB C6 | SET 0,(HL) 414 | CB C7 | SET 0,A 415 | CB C8 | SET 1,B 416 | CB C9 | SET 1,C 417 | 418 | ; Page 38 419 | 420 | CB CA | SET 1,D 421 | CB CB | SET 1,E 422 | CB CC | SET 1,H 423 | CB CD | SET 1,L 424 | CB CE | SET 1,(HL) 425 | CB CF | SET 1,A 426 | CB D0 | SET 2,B 427 | CB D1 | SET 2,C 428 | CB D2 | SET 2,D 429 | CB D3 | SET 2,E 430 | CB D4 | SET 2,H 431 | CB D5 | SET 2,L 432 | CB D6 | SET 2,(HL) 433 | CB D7 | SET 2,A 434 | CB D8 | SET 3,B 435 | CB D9 | SET 3,C 436 | CB DA | SET 3,D 437 | CB DB | SET 3,E 438 | CB DC | SET 3,H 439 | CB DD | SET 3,L 440 | CB DE | SET 3,(HL) 441 | CB DF | SET 3,A 442 | CB E0 | SET 4,B 443 | CB E1 | SET 4,C 444 | CB E2 | SET 4,D 445 | CB E3 | SET 4,E 446 | CB E4 | SET 4,H 447 | CB E5 | SET 4,L 448 | CB E6 | SET 4,(HL) 449 | CB E7 | SET 4,A 450 | CB E8 | SET 5,B 451 | CB E9 | SET 5,C 452 | CB EA | SET 5,D 453 | CB EB | SET 5,E 454 | CB EC | SET 5,H 455 | CB ED | SET 5,L 456 | CB EE | SET 5,(HL) 457 | CB EF | SET 5,A 458 | CB F0 | SET 6,B 459 | CB F1 | SET 6,C 460 | CB F2 | SET 6,D 461 | CB F3 | SET 6,E 462 | CB F4 | SET 6,H 463 | CB F5 | SET 6,L 464 | CB F6 | SET 6,(HL) 465 | CB F7 | SET 6,A 466 | CB F8 | SET 7,B 467 | CB F9 | SET 7,C 468 | CB FA | SET 7,D 469 | CB FB | SET 7,E 470 | CB FC | SET 7,H 471 | CB FD | SET 7,L 472 | CB FE | SET 7,(HL) 473 | CB FF | SET 7,A 474 | CC 3F B0 | CALL Z,B03Fh 475 | CD A9 B7 | CALL B7A9h 476 | CE 74 | ADC A,74h 477 | CF | RST 8h 478 | D0 | RET NC 479 | D1 | POP DE 480 | D2 5A 29 | JP NC,295Ah 481 | D3 CA | OUT (CAh),A 482 | D4 44 29 | CALL NC,2944h 483 | D5 | PUSH DE 484 | D6 B6 | SUB B6h 485 | D7 | RST 10h 486 | D8 | RET C 487 | D9 | EXX 488 | DA 9B B8 | JP C,B89Bh 489 | DB A6 | IN A,(A6h) 490 | DC 80 8D | CALL C,8D80h 491 | DD 09 | ADD IX,BC 492 | DD 19 | ADD IX,DE 493 | DD 21 0D EA | LD IX,EA0Dh 494 | DD 22 25 AC | LD (AC25h),IX 495 | DD 23 | INC IX 496 | DD 24 | INC IXH; *** 497 | DD 25 | DEC IXH; *** 498 | DD 26 A8 | LD IXH,A8h; *** 499 | DD 29 | ADD IX,IX 500 | DD 2A E6 32 | LD IX,(32E6h) 501 | DD 2B | DEC IX 502 | DD 2C | INC IXL; *** 503 | DD 2D | DEC IXL; *** 504 | DD 2E EA | LD IXL,EAh; *** 505 | DD 34 D3 | INC (IX-2Dh) 506 | DD 35 C2 | DEC (IX-3Eh) 507 | DD 36 3B 93 | LD (IX+3Bh),93h 508 | DD 39 | ADD IX,SP 509 | DD 44 | LD B,IXH; *** 510 | DD 45 | LD B,IXL; *** 511 | DD 46 BA | LD B,(IX-46h) 512 | DD 4C | LD C,IXH; *** 513 | DD 4D | LD C,IXL; *** 514 | DD 4E AD | LD C,(IX-53h) 515 | DD 54 | LD D,IXH; *** 516 | DD 55 | LD D,IXL; *** 517 | DD 56 B1 | LD D,(IX-4Fh) 518 | DD 5C | LD E,IXH; *** 519 | DD 5D | LD E,IXL; *** 520 | DD 5E C5 | LD E,(IX-3Bh) 521 | DD 60 | LD IXH,B; *** 522 | DD 61 | LD IXH,C; *** 523 | DD 62 | LD IXH,D; *** 524 | DD 63 | LD IXH,E; *** 525 | DD 64 | LD IXH,IXH; *** 526 | DD 65 | LD IXH,IXL; *** 527 | DD 66 CC | LD H,(IX-34h) 528 | DD 67 | LD IXH,A; *** 529 | DD 68 | LD IXL,B; *** 530 | DD 69 | LD IXL,C; *** 531 | DD 6A | LD IXL,D; *** 532 | DD 6B | LD IXL,E; *** 533 | DD 6C | LD IXL,IXH; *** 534 | DD 6D | LD IXL,IXL; *** 535 | DD 6E 0A | LD L,(IX+Ah) 536 | DD 6F | LD IXL,A; *** 537 | DD 70 91 | LD (IX-6Fh),B 538 | DD 71 33 | LD (IX+33h),C 539 | DD 72 AC | LD (IX-54h),D 540 | DD 73 00 | LD (IX+0h),E 541 | DD 74 43 | LD (IX+43h),H 542 | DD 75 CA | LD (IX-36h),L 543 | DD 77 92 | LD (IX-6Eh),A 544 | DD 7C | LD A,IXH; *** 545 | DD 7D | LD A,IXL; *** 546 | DD 7E 3E | LD A,(IX+3Eh) 547 | DD 84 | ADD A,IXH; *** 548 | DD 85 | ADD A,IXL; *** 549 | DD 86 FB | ADD A,(IX-5h) 550 | DD 8C | ADC A,IXH; *** 551 | DD 8D | ADC A,IXL; *** 552 | DD 8E 97 | ADC A,(IX-69h) 553 | DD 94 | SUB IXH; *** 554 | DD 95 | SUB IXL; *** 555 | DD 96 90 | SUB (IX-70h) 556 | DD 9C | SBC A,IXH; *** 557 | DD 9D | SBC A,IXL; *** 558 | DD 9E 10 | SBC A,(IX+10h) 559 | DD A4 | AND IXH; *** 560 | DD A5 | AND IXL; *** 561 | DD A6 6E | AND (IX+6Eh) 562 | DD AC | XOR IXH; *** 563 | DD AD | XOR IXL; *** 564 | DD AE 94 | XOR (IX-6Ch) 565 | DD B4 | OR IXH; *** 566 | DD B5 | OR IXL; *** 567 | DD B6 99 | OR (IX-67h) 568 | DD BC | CP IXH; *** 569 | DD BD | CP IXL; *** 570 | DD BE 98 | CP (IX-68h) 571 | DD CB 52 00 | RLC (IX+52h),B; *** 572 | DD CB 53 01 | RLC (IX+53h),C; *** 573 | DD CB EF 02 | RLC (IX-11h),D; *** 574 | DD CB 8B 03 | RLC (IX-75h),E; *** 575 | DD CB 43 04 | RLC (IX+43h),H; *** 576 | DD CB 10 05 | RLC (IX+10h),L; *** 577 | DD CB 02 06 | RLC (IX+2h) 578 | DD CB 86 07 | RLC (IX-7Ah),A; *** 579 | DD CB DD 08 | RRC (IX-23h),B; *** 580 | DD CB 7C 09 | RRC (IX+7Ch),C; *** 581 | DD CB 63 0A | RRC (IX+63h),D; *** 582 | DD CB 21 0B | RRC (IX+21h),E; *** 583 | DD CB 76 0C | RRC (IX+76h),H; *** 584 | DD CB 78 0D | RRC (IX+78h),L; *** 585 | DD CB 69 0E | RRC (IX+69h) 586 | DD CB 4D 0F | RRC (IX+4Dh),A; *** 587 | DD CB D8 10 | RL (IX-28h),B; *** 588 | DD CB 8D 11 | RL (IX-73h),C; *** 589 | DD CB 27 12 | RL (IX+27h),D; *** 590 | DD CB FD 13 | RL (IX-3h),E; *** 591 | DD CB 74 14 | RL (IX+74h),H; *** 592 | DD CB DA 15 | RL (IX-26h),L; *** 593 | DD CB D8 16 | RL (IX-28h) 594 | DD CB 37 17 | RL (IX+37h),A; *** 595 | DD CB 5D 18 | RR (IX+5Dh),B; *** 596 | DD CB B3 19 | RR (IX-4Dh),C; *** 597 | DD CB 4A 1A | RR (IX+4Ah),D; *** 598 | DD CB 1B 1B | RR (IX+1Bh),E; *** 599 | DD CB 95 1C | RR (IX-6Bh),H; *** 600 | DD CB EE 1D | RR (IX-12h),L; *** 601 | DD CB 72 1E | RR (IX+72h) 602 | DD CB 01 1F | RR (IX+1h),A; *** 603 | DD CB B3 20 | SLA (IX-4Dh),B; *** 604 | DD CB 89 21 | SLA (IX-77h),C; *** 605 | DD CB 93 22 | SLA (IX-6Dh),D; *** 606 | DD CB 84 23 | SLA (IX-7Ch),E; *** 607 | DD CB 51 24 | SLA (IX+51h),H; *** 608 | DD CB 54 25 | SLA (IX+54h),L; *** 609 | DD CB 05 26 | SLA (IX+5h) 610 | DD CB 0F 27 | SLA (IX+Fh),A; *** 611 | DD CB 36 28 | SRA (IX+36h),B; *** 612 | DD CB 80 29 | SRA (IX-80h),C; *** 613 | DD CB C1 2A | SRA (IX-3Fh),D; *** 614 | DD CB BC 2B | SRA (IX-44h),E; *** 615 | DD CB 44 2C | SRA (IX+44h),H; *** 616 | DD CB 9B 2D | SRA (IX-65h),L; *** 617 | DD CB 74 2E | SRA (IX+74h) 618 | DD CB 4F 2F | SRA (IX+4Fh),A; *** 619 | DD CB 7F 30 | SLL (IX+7Fh),B; *** 620 | DD CB 56 31 | SLL (IX+56h),C; *** 621 | DD CB 8E 32 | SLL (IX-72h),D; *** 622 | DD CB 59 33 | SLL (IX+59h),E; *** 623 | DD CB 2D 34 | SLL (IX+2Dh),H; *** 624 | DD CB 85 35 | SLL (IX-7Bh),L; *** 625 | DD CB 00 36 | SLL (IX+0h); *** 626 | DD CB 4F 37 | SLL (IX+4Fh),A; *** 627 | DD CB C0 38 | SRL (IX-40h),B; *** 628 | DD CB 8C 39 | SRL (IX-74h),C; *** 629 | DD CB D7 3A | SRL (IX-29h),D; *** 630 | DD CB 64 3B | SRL (IX+64h),E; *** 631 | DD CB 9D 3C | SRL (IX-63h),H; *** 632 | DD CB 17 3D | SRL (IX+17h),L; *** 633 | DD CB 67 3E | SRL (IX+67h) 634 | DD CB 57 3F | SRL (IX+57h),A; *** 635 | DD CB 99 40 | BIT 0,(IX-67h); *** 636 | DD CB 13 41 | BIT 0,(IX+13h); *** 637 | DD CB 13 42 | BIT 0,(IX+13h); *** 638 | DD CB F9 43 | BIT 0,(IX-7h); *** 639 | DD CB 68 44 | BIT 0,(IX+68h); *** 640 | DD CB AA 45 | BIT 0,(IX-56h); *** 641 | DD CB 70 46 | BIT 0,(IX+70h) 642 | DD CB 7B 47 | BIT 0,(IX+7Bh); *** 643 | DD CB D6 48 | BIT 1,(IX-2Ah); *** 644 | DD CB D4 49 | BIT 1,(IX-2Ch); *** 645 | DD CB 66 4A | BIT 1,(IX+66h); *** 646 | DD CB 28 4B | BIT 1,(IX+28h); *** 647 | DD CB 61 4C | BIT 1,(IX+61h); *** 648 | DD CB 44 4D | BIT 1,(IX+44h); *** 649 | DD CB C2 4E | BIT 1,(IX-3Eh) 650 | DD CB 6A 4F | BIT 1,(IX+6Ah); *** 651 | DD CB D2 50 | BIT 2,(IX-2Eh); *** 652 | DD CB E5 51 | BIT 2,(IX-1Bh); *** 653 | DD CB BC 52 | BIT 2,(IX-44h); *** 654 | DD CB 51 53 | BIT 2,(IX+51h); *** 655 | DD CB 84 54 | BIT 2,(IX-7Ch); *** 656 | DD CB BA 55 | BIT 2,(IX-46h); *** 657 | DD CB 87 56 | BIT 2,(IX-79h) 658 | DD CB 89 57 | BIT 2,(IX-77h); *** 659 | DD CB BF 58 | BIT 3,(IX-41h); *** 660 | 661 | ; Page 39 662 | 663 | DD CB FF 59 | BIT 3,(IX-1h); *** 664 | DD CB 9E 5A | BIT 3,(IX-62h); *** 665 | DD CB 87 5B | BIT 3,(IX-79h); *** 666 | DD CB 12 5C | BIT 3,(IX+12h); *** 667 | DD CB 4F 5D | BIT 3,(IX+4Fh); *** 668 | DD CB 4F 5E | BIT 3,(IX+4Fh) 669 | DD CB E5 5F | BIT 3,(IX-1Bh); *** 670 | DD CB 02 60 | BIT 4,(IX+2h); *** 671 | DD CB FD 61 | BIT 4,(IX-3h); *** 672 | DD CB A6 62 | BIT 4,(IX-5Ah); *** 673 | DD CB 08 63 | BIT 4,(IX+8h); *** 674 | DD CB 45 64 | BIT 4,(IX+45h); *** 675 | DD CB 06 65 | BIT 4,(IX+6h); *** 676 | DD CB AF 66 | BIT 4,(IX-51h) 677 | DD CB 1A 67 | BIT 4,(IX+1Ah); *** 678 | DD CB 64 68 | BIT 5,(IX+64h); *** 679 | DD CB 85 69 | BIT 5,(IX-7Bh); *** 680 | DD CB 2B 6A | BIT 5,(IX+2Bh); *** 681 | DD CB D3 6B | BIT 5,(IX-2Dh); *** 682 | DD CB 57 6C | BIT 5,(IX+57h); *** 683 | DD CB A7 6D | BIT 5,(IX-59h); *** 684 | DD CB DB 6E | BIT 5,(IX-25h) 685 | DD CB 57 6F | BIT 5,(IX+57h); *** 686 | DD CB B8 70 | BIT 6,(IX-48h); *** 687 | DD CB 43 71 | BIT 6,(IX+43h); *** 688 | DD CB 49 72 | BIT 6,(IX+49h); *** 689 | DD CB C5 73 | BIT 6,(IX-3Bh); *** 690 | DD CB CE 74 | BIT 6,(IX-32h); *** 691 | DD CB 12 75 | BIT 6,(IX+12h); *** 692 | DD CB 53 76 | BIT 6,(IX+53h) 693 | DD CB F8 77 | BIT 6,(IX-8h); *** 694 | DD CB 8C 78 | BIT 7,(IX-74h); *** 695 | DD CB 8D 79 | BIT 7,(IX-73h); *** 696 | DD CB C9 7A | BIT 7,(IX-37h); *** 697 | DD CB 79 7B | BIT 7,(IX+79h); *** 698 | DD CB 75 7C | BIT 7,(IX+75h); *** 699 | DD CB B7 7D | BIT 7,(IX-49h); *** 700 | DD CB 26 7E | BIT 7,(IX+26h) 701 | DD CB 22 7F | BIT 7,(IX+22h); *** 702 | DD CB BF 80 | RES 0,(IX-41h),B; *** 703 | DD CB 5A 81 | RES 0,(IX+5Ah),C; *** 704 | DD CB 52 82 | RES 0,(IX+52h),D; *** 705 | DD CB 36 83 | RES 0,(IX+36h),E; *** 706 | DD CB BC 84 | RES 0,(IX-44h),H; *** 707 | DD CB 80 85 | RES 0,(IX-80h),L; *** 708 | DD CB DF 86 | RES 0,(IX-21h) 709 | DD CB 97 87 | RES 0,(IX-69h),A; *** 710 | DD CB 7C 88 | RES 1,(IX+7Ch),B; *** 711 | DD CB 0D 89 | RES 1,(IX+Dh),C; *** 712 | DD CB F5 8A | RES 1,(IX-Bh),D; *** 713 | DD CB 2B 8B | RES 1,(IX+2Bh),E; *** 714 | DD CB 18 8C | RES 1,(IX+18h),H; *** 715 | DD CB 42 8D | RES 1,(IX+42h),L; *** 716 | DD CB A1 8E | RES 1,(IX-5Fh) 717 | DD CB 57 8F | RES 1,(IX+57h),A; *** 718 | DD CB C1 90 | RES 2,(IX-3Fh),B; *** 719 | DD CB 2F 91 | RES 2,(IX+2Fh),C; *** 720 | DD CB B7 92 | RES 2,(IX-49h),D; *** 721 | DD CB F8 93 | RES 2,(IX-8h),E; *** 722 | DD CB EA 94 | RES 2,(IX-16h),H; *** 723 | DD CB 64 95 | RES 2,(IX+64h),L; *** 724 | DD CB 59 96 | RES 2,(IX+59h) 725 | DD CB 4F 97 | RES 2,(IX+4Fh),A; *** 726 | DD CB D9 98 | RES 3,(IX-27h),B; *** 727 | DD CB 8B 99 | RES 3,(IX-75h),C; *** 728 | DD CB 0D 9A | RES 3,(IX+Dh),D; *** 729 | DD CB 44 9B | RES 3,(IX+44h),E; *** 730 | DD CB 3F 9C | RES 3,(IX+3Fh),H; *** 731 | DD CB 07 9D | RES 3,(IX+7h),L; *** 732 | DD CB 0B 9E | RES 3,(IX+Bh) 733 | DD CB 46 9F | RES 3,(IX+46h),A; *** 734 | DD CB 37 A0 | RES 4,(IX+37h),B; *** 735 | DD CB 18 A1 | RES 4,(IX+18h),C; *** 736 | DD CB B9 A2 | RES 4,(IX-47h),D; *** 737 | DD CB 56 A3 | RES 4,(IX+56h),E; *** 738 | DD CB 38 A4 | RES 4,(IX+38h),H; *** 739 | DD CB B3 A5 | RES 4,(IX-4Dh),L; *** 740 | DD CB AB A6 | RES 4,(IX-55h) 741 | DD CB CE A7 | RES 4,(IX-32h),A; *** 742 | DD CB C1 A8 | RES 5,(IX-3Fh),B; *** 743 | DD CB BC A9 | RES 5,(IX-44h),C; *** 744 | DD CB B7 AA | RES 5,(IX-49h),D; *** 745 | DD CB F4 AB | RES 5,(IX-Ch),E; *** 746 | DD CB F4 AC | RES 5,(IX-Ch),H; *** 747 | DD CB E1 AD | RES 5,(IX-1Fh),L; *** 748 | DD CB 54 AE | RES 5,(IX+54h) 749 | DD CB AB AF | RES 5,(IX-55h),A; *** 750 | DD CB B0 B0 | RES 6,(IX-50h),B; *** 751 | DD CB 19 B1 | RES 6,(IX+19h),C; *** 752 | DD CB 66 B2 | RES 6,(IX+66h),D; *** 753 | DD CB 0E B3 | RES 6,(IX+Eh),E; *** 754 | DD CB 17 B4 | RES 6,(IX+17h),H; *** 755 | DD CB 54 B5 | RES 6,(IX+54h),L; *** 756 | DD CB 88 B6 | RES 6,(IX-78h) 757 | DD CB 92 B7 | RES 6,(IX-6Eh),A; *** 758 | DD CB 11 B8 | RES 7,(IX+11h),B; *** 759 | DD CB F2 B9 | RES 7,(IX-Eh),C; *** 760 | DD CB D9 BA | RES 7,(IX-27h),D; *** 761 | DD CB 80 BB | RES 7,(IX-80h),E; *** 762 | DD CB 54 BC | RES 7,(IX+54h),H; *** 763 | DD CB 24 BD | RES 7,(IX+24h),L; *** 764 | DD CB B9 BE | RES 7,(IX-47h) 765 | DD CB FF BF | RES 7,(IX-1h),A; *** 766 | DD CB F6 C0 | SET 0,(IX-Ah),B; *** 767 | DD CB A0 C1 | SET 0,(IX-60h),C; *** 768 | DD CB A5 C2 | SET 0,(IX-5Bh),D; *** 769 | DD CB 78 C3 | SET 0,(IX+78h),E; *** 770 | DD CB 0C C4 | SET 0,(IX+Ch),H; *** 771 | DD CB AB C5 | SET 0,(IX-55h),L; *** 772 | DD CB B2 C6 | SET 0,(IX-4Eh) 773 | DD CB 87 C7 | SET 0,(IX-79h),A; *** 774 | DD CB 4A C8 | SET 1,(IX+4Ah),B; *** 775 | DD CB E2 C9 | SET 1,(IX-1Eh),C; *** 776 | DD CB 96 CA | SET 1,(IX-6Ah),D; *** 777 | DD CB 88 CB | SET 1,(IX-78h),E; *** 778 | DD CB 1E CC | SET 1,(IX+1Eh),H; *** 779 | DD CB 92 CD | SET 1,(IX-6Eh),L; *** 780 | DD CB B8 CE | SET 1,(IX-48h) 781 | DD CB 9A CF | SET 1,(IX-66h),A; *** 782 | DD CB 5B D0 | SET 2,(IX+5Bh),B; *** 783 | DD CB 30 D1 | SET 2,(IX+30h),C; *** 784 | DD CB 87 D2 | SET 2,(IX-79h),D; *** 785 | DD CB 70 D3 | SET 2,(IX+70h),E; *** 786 | DD CB F5 D4 | SET 2,(IX-Bh),H; *** 787 | DD CB 87 D5 | SET 2,(IX-79h),L; *** 788 | DD CB E5 D6 | SET 2,(IX-1Bh) 789 | DD CB 76 D7 | SET 2,(IX+76h),A; *** 790 | DD CB 96 D8 | SET 3,(IX-6Ah),B; *** 791 | DD CB ED D9 | SET 3,(IX-13h),C; *** 792 | DD CB 16 DA | SET 3,(IX+16h),D; *** 793 | DD CB 2F DB | SET 3,(IX+2Fh),E; *** 794 | DD CB C6 DC | SET 3,(IX-3Ah),H; *** 795 | DD CB 4F DD | SET 3,(IX+4Fh),L; *** 796 | DD CB A8 DE | SET 3,(IX-58h) 797 | DD CB 5E DF | SET 3,(IX+5Eh),A; *** 798 | DD CB 3A E0 | SET 4,(IX+3Ah),B; *** 799 | DD CB 6D E1 | SET 4,(IX+6Dh),C; *** 800 | DD CB 67 E2 | SET 4,(IX+67h),D; *** 801 | DD CB CC E3 | SET 4,(IX-34h),E; *** 802 | DD CB 19 E4 | SET 4,(IX+19h),H; *** 803 | DD CB 5C E5 | SET 4,(IX+5Ch),L; *** 804 | DD CB 8E E6 | SET 4,(IX-72h) 805 | DD CB D9 E7 | SET 4,(IX-27h),A; *** 806 | DD CB 7B E8 | SET 5,(IX+7Bh),B; *** 807 | DD CB 3B E9 | SET 5,(IX+3Bh),C; *** 808 | DD CB AD EA | SET 5,(IX-53h),D; *** 809 | DD CB E6 EB | SET 5,(IX-1Ah),E; *** 810 | DD CB CB EC | SET 5,(IX-35h),H; *** 811 | DD CB 88 ED | SET 5,(IX-78h),L; *** 812 | DD CB A1 EE | SET 5,(IX-5Fh) 813 | DD CB 89 EF | SET 5,(IX-77h),A; *** 814 | DD CB 8C F0 | SET 6,(IX-74h),B; *** 815 | DD CB 1D F1 | SET 6,(IX+1Dh),C; *** 816 | DD CB 5B F2 | SET 6,(IX+5Bh),D; *** 817 | DD CB 80 F3 | SET 6,(IX-80h),E; *** 818 | DD CB F5 F4 | SET 6,(IX-Bh),H; *** 819 | DD CB 39 F5 | SET 6,(IX+39h),L; *** 820 | DD CB 44 F6 | SET 6,(IX+44h) 821 | DD CB 30 F7 | SET 6,(IX+30h),A; *** 822 | DD CB B3 F8 | SET 7,(IX-4Dh),B; *** 823 | DD CB E9 F9 | SET 7,(IX-17h),C; *** 824 | DD CB 38 FA | SET 7,(IX+38h),D; *** 825 | DD CB CA FB | SET 7,(IX-36h),E; *** 826 | DD CB C4 FC | SET 7,(IX-3Ch),H; *** 827 | DD CB C2 FD | SET 7,(IX-3Eh),L; *** 828 | DD CB 55 FE | SET 7,(IX+55h) 829 | DD CB C1 FF | SET 7,(IX-3Fh),A; *** 830 | DD E1 | POP IX 831 | DD E3 | EX (SP),IX 832 | DD E5 | PUSH IX 833 | DD E9 | JP (IX) 834 | DD F9 | LD SP,IX 835 | DE 1F | SBC A,1Fh 836 | DF | RST 18h 837 | E0 | RET PO 838 | E1 | POP HL 839 | E2 6E 2F | JP PO,2F6Eh 840 | E3 | EX (SP),HL 841 | E4 8C D8 | CALL PO,D88Ch 842 | E5 | PUSH HL 843 | E6 66 | AND 66h 844 | E7 | RST 20h 845 | E8 | RET PE 846 | E9 | JP (HL) 847 | EA 32 30 | JP PE,3032h 848 | EB | EX DE,HL 849 | EC D5 CB | CALL PE,CBD5h 850 | ED 40 | IN B,(C) 851 | ED 41 | OUT (C),B 852 | ED 42 | SBC HL,BC 853 | ED 43 B9 78 | LD (78B9h),BC 854 | ED 44 | NEG 855 | ED 45 | RETN 856 | ED 46 | IM 0 857 | ED 47 | LD I,A 858 | ED 48 | IN C,(C) 859 | ED 49 | OUT (C),C 860 | ED 4A | ADC HL,BC 861 | ED 4B 10 05 | LD BC,(510h) 862 | ED 4C | NEG; *** 863 | ED 4D | RETI 864 | ED 4E | IM 0; *** 865 | ED 4F | LD R,A 866 | ED 50 | IN D,(C) 867 | ED 51 | OUT (C),D 868 | ED 52 | SBC HL,DE 869 | ED 53 13 44 | LD (4413h),DE 870 | ED 54 | NEG; *** 871 | ED 55 | RETN; *** 872 | ED 56 | IM 1 873 | ED 57 | LD A,I 874 | ED 58 | IN E,(C) 875 | ED 59 | OUT (C),E 876 | ED 5A | ADC HL,DE 877 | ED 5B D4 96 | LD DE,(96D4h) 878 | ED 5C | NEG; *** 879 | ED 5D | RETN; *** 880 | ED 5E | IM 2 881 | ED 5F | LD A,R 882 | ED 60 | IN H,(C) 883 | ED 61 | OUT (C),H 884 | ED 62 | SBC HL,HL 885 | ED 63 6A B9 | LD (B96Ah),HL 886 | ED 64 | NEG; *** 887 | ED 65 | RETN; *** 888 | ED 66 | IM 0; *** 889 | ED 67 | RRD 890 | ED 68 | IN L,(C) 891 | ED 69 | OUT (C),L 892 | ED 6A | ADC HL,HL 893 | ED 6B 2D 90 | LD HL,(902Dh) 894 | ED 6C | NEG; *** 895 | ED 6D | RETN; *** 896 | ED 6E | IM 0; *** 897 | ED 6F | RLD 898 | ED 70 | IN (C); *** 899 | ED 71 | OUT (C),0; *** 900 | ED 72 | SBC HL,SP 901 | ED 73 38 CF | LD (CF38h),SP 902 | ED 74 | NEG; *** 903 | 904 | ; Page 40 905 | 906 | ED 75 | RETN; *** 907 | ED 76 | IM 1; *** 908 | ED 78 | IN A,(C) 909 | ED 79 | OUT (C),A 910 | ED 7A | ADC HL,SP 911 | ED 7B E7 E3 | LD SP,(E3E7h) 912 | ED 7C | NEG; *** 913 | ED 7D | RETN; *** 914 | ED 7E | IM 2; *** 915 | ED A0 | LDI 916 | ED A1 | CPI 917 | ED A2 | INI 918 | ED A3 | OUTI 919 | ED A8 | LDD 920 | ED A9 | CPD 921 | ED AA | IND 922 | ED AB | OUTD 923 | ED B0 | LDIR 924 | ED B1 | CPIR 925 | ED B2 | INIR 926 | ED B3 | OTIR 927 | ED B8 | LDDR 928 | ED B9 | CPDR 929 | ED BA | INDR 930 | ED BB | OTDR 931 | EE 12 | XOR 12h 932 | EF | RST 28h 933 | F0 | RET P 934 | F1 | POP AF 935 | F2 2E E8 | JP P,E82Eh 936 | F3 | DI 937 | F4 05 65 | CALL P,6505h 938 | F5 | PUSH AF 939 | F6 7D | OR 7Dh 940 | F7 | RST 30h 941 | F8 | RET M 942 | F9 | LD SP,HL 943 | FA 15 E8 | JP M,E815h 944 | FB | EI 945 | FC BF A2 | CALL M,A2BFh 946 | FD 09 | ADD IY,BC 947 | FD 19 | ADD IY,DE 948 | FD 21 54 4A | LD IY,4A54h 949 | FD 22 A2 34 | LD (34A2h),IY 950 | FD 23 | INC IY 951 | FD 24 | INC IYH; *** 952 | FD 25 | DEC IYH; *** 953 | FD 26 24 | LD IYH,24h; *** 954 | FD 29 | ADD IY,IY 955 | FD 2A B4 5C | LD IY,(5CB4h) 956 | FD 2B | DEC IY 957 | FD 2C | INC IYL; *** 958 | FD 2D | DEC IYL; *** 959 | FD 2E 42 | LD IYL,42h; *** 960 | FD 34 1A | INC (IY+1Ah) 961 | FD 35 8B | DEC (IY-75h) 962 | FD 36 0B EF | LD (IY+Bh),EFh 963 | FD 39 | ADD IY,SP 964 | FD 44 | LD B,IYH; *** 965 | FD 45 | LD B,IYL; *** 966 | FD 46 14 | LD B,(IY+14h) 967 | FD 4C | LD C,IYH; *** 968 | FD 4D | LD C,IYL; *** 969 | FD 4E 1C | LD C,(IY+1Ch) 970 | FD 54 | LD D,IYH; *** 971 | FD 55 | LD D,IYL; *** 972 | FD 56 7E | LD D,(IY+7Eh) 973 | FD 5C | LD E,IYH; *** 974 | FD 5D | LD E,IYL; *** 975 | FD 5E F1 | LD E,(IY-Fh) 976 | FD 60 | LD IYH,B; *** 977 | FD 61 | LD IYH,C; *** 978 | FD 62 | LD IYH,D; *** 979 | FD 63 | LD IYH,E; *** 980 | FD 64 | LD IYH,IYH; *** 981 | FD 65 | LD IYH,IYL; *** 982 | FD 66 4A | LD H,(IY+4Ah) 983 | FD 67 | LD IYH,A; *** 984 | FD 68 | LD IYL,B; *** 985 | FD 69 | LD IYL,C; *** 986 | FD 6A | LD IYL,D; *** 987 | FD 6B | LD IYL,E; *** 988 | FD 6C | LD IYL,IYH; *** 989 | FD 6D | LD IYL,IYL; *** 990 | FD 6E 7E | LD L,(IY+7Eh) 991 | FD 6F | LD IYL,A; *** 992 | FD 70 34 | LD (IY+34h),B 993 | FD 71 A2 | LD (IY-5Eh),C 994 | FD 72 09 | LD (IY+9h),D 995 | FD 73 94 | LD (IY-6Ch),E 996 | FD 74 ED | LD (IY-13h),H 997 | FD 75 49 | LD (IY+49h),L 998 | FD 77 39 | LD (IY+39h),A 999 | FD 7C | LD A,IYH; *** 1000 | FD 7D | LD A,IYL; *** 1001 | FD 7E 1D | LD A,(IY+1Dh) 1002 | FD 84 | ADD A,IYH; *** 1003 | FD 85 | ADD A,IYL; *** 1004 | FD 86 0F | ADD A,(IY+Fh) 1005 | FD 8C | ADC A,IYH; *** 1006 | FD 8D | ADC A,IYL; *** 1007 | FD 8E 0D | ADC A,(IY+Dh) 1008 | FD 94 | SUB IYH; *** 1009 | FD 95 | SUB IYL; *** 1010 | FD 96 72 | SUB (IY+72h) 1011 | FD 9C | SBC A,IYH; *** 1012 | FD 9D | SBC A,IYL; *** 1013 | FD 9E 5C | SBC A,(IY+5Ch) 1014 | FD A4 | AND IYH; *** 1015 | FD A5 | AND IYL; *** 1016 | FD A6 78 | AND (IY+78h) 1017 | FD AC | XOR IYH; *** 1018 | FD AD | XOR IYL; *** 1019 | FD AE 56 | XOR (IY+56h) 1020 | FD B4 | OR IYH; *** 1021 | FD B5 | OR IYL; *** 1022 | FD B6 AE | OR (IY-52h) 1023 | FD BC | CP IYH; *** 1024 | FD BD | CP IYL; *** 1025 | FD BE 81 | CP (IY-7Fh) 1026 | FD CB 50 00 | RLC (IY+50h),B; *** 1027 | FD CB 74 01 | RLC (IY+74h),C; *** 1028 | FD CB 3B 02 | RLC (IY+3Bh),D; *** 1029 | FD CB 4F 03 | RLC (IY+4Fh),E; *** 1030 | FD CB AE 04 | RLC (IY-52h),H; *** 1031 | FD CB 66 05 | RLC (IY+66h),L; *** 1032 | FD CB 30 06 | RLC (IY+30h) 1033 | FD CB 13 07 | RLC (IY+13h),A; *** 1034 | FD CB 05 08 | RRC (IY+5h),B; *** 1035 | FD CB E2 09 | RRC (IY-1Eh),C; *** 1036 | FD CB 45 0A | RRC (IY+45h),D; *** 1037 | FD CB 5D 0B | RRC (IY+5Dh),E; *** 1038 | FD CB C3 0C | RRC (IY-3Dh),H; *** 1039 | FD CB 44 0D | RRC (IY+44h),L; *** 1040 | FD CB D0 0E | RRC (IY-30h) 1041 | FD CB CA 0F | RRC (IY-36h),A; *** 1042 | FD CB 19 10 | RL (IY+19h),B; *** 1043 | FD CB 73 11 | RL (IY+73h),C; *** 1044 | FD CB 7B 12 | RL (IY+7Bh),D; *** 1045 | FD CB D2 13 | RL (IY-2Eh),E; *** 1046 | FD CB E5 14 | RL (IY-1Bh),H; *** 1047 | FD CB 6B 15 | RL (IY+6Bh),L; *** 1048 | FD CB E7 16 | RL (IY-19h) 1049 | FD CB 50 17 | RL (IY+50h),A; *** 1050 | FD CB 9E 18 | RR (IY-62h),B; *** 1051 | FD CB 1F 19 | RR (IY+1Fh),C; *** 1052 | FD CB 16 1A | RR (IY+16h),D; *** 1053 | FD CB 94 1B | RR (IY-6Ch),E; *** 1054 | FD CB 07 1C | RR (IY+7h),H; *** 1055 | FD CB D8 1D | RR (IY-28h),L; *** 1056 | FD CB 8D 1E | RR (IY-73h) 1057 | FD CB 52 1F | RR (IY+52h),A; *** 1058 | FD CB FE 20 | SLA (IY-2h),B; *** 1059 | FD CB 03 21 | SLA (IY+3h),C; *** 1060 | FD CB C1 22 | SLA (IY-3Fh),D; *** 1061 | FD CB EB 23 | SLA (IY-15h),E; *** 1062 | FD CB 89 24 | SLA (IY-77h),H; *** 1063 | FD CB B3 25 | SLA (IY-4Dh),L; *** 1064 | FD CB 72 26 | SLA (IY+72h) 1065 | FD CB 07 27 | SLA (IY+7h),A; *** 1066 | FD CB 7F 28 | SRA (IY+7Fh),B; *** 1067 | FD CB 10 29 | SRA (IY+10h),C; *** 1068 | FD CB 19 2A | SRA (IY+19h),D; *** 1069 | FD CB AC 2B | SRA (IY-54h),E; *** 1070 | FD CB B4 2C | SRA (IY-4Ch),H; *** 1071 | FD CB 7D 2D | SRA (IY+7Dh),L; *** 1072 | FD CB 4A 2E | SRA (IY+4Ah) 1073 | FD CB 8B 2F | SRA (IY-75h),A; *** 1074 | FD CB 2C 30 | SLL (IY+2Ch),B; *** 1075 | FD CB 5A 31 | SLL (IY+5Ah),C; *** 1076 | FD CB D8 32 | SLL (IY-28h),D; *** 1077 | FD CB 9E 33 | SLL (IY-62h),E; *** 1078 | FD CB 60 34 | SLL (IY+60h),H; *** 1079 | FD CB 93 35 | SLL (IY-6Dh),L; *** 1080 | FD CB DA 36 | SLL (IY-26h); *** 1081 | FD CB A0 37 | SLL (IY-60h),A; *** 1082 | FD CB D0 38 | SRL (IY-30h),B; *** 1083 | FD CB 07 39 | SRL (IY+7h),C; *** 1084 | FD CB 52 3A | SRL (IY+52h),D; *** 1085 | FD CB DF 3B | SRL (IY-21h),E; *** 1086 | FD CB AF 3C | SRL (IY-51h),H; *** 1087 | FD CB 3F 3D | SRL (IY+3Fh),L; *** 1088 | FD CB 83 3E | SRL (IY-7Dh) 1089 | FD CB E3 3F | SRL (IY-1Dh),A; *** 1090 | FD CB 6E 40 | BIT 0,(IY+6Eh); *** 1091 | FD CB A3 41 | BIT 0,(IY-5Dh); *** 1092 | FD CB F3 42 | BIT 0,(IY-Dh); *** 1093 | FD CB AD 43 | BIT 0,(IY-53h); *** 1094 | FD CB 92 44 | BIT 0,(IY-6Eh); *** 1095 | FD CB 14 45 | BIT 0,(IY+14h); *** 1096 | FD CB 02 46 | BIT 0,(IY+2h) 1097 | FD CB 9E 47 | BIT 0,(IY-62h); *** 1098 | FD CB 58 48 | BIT 1,(IY+58h); *** 1099 | FD CB 52 49 | BIT 1,(IY+52h); *** 1100 | FD CB 50 4A | BIT 1,(IY+50h); *** 1101 | FD CB 55 4B | BIT 1,(IY+55h); *** 1102 | FD CB F1 4C | BIT 1,(IY-Fh); *** 1103 | FD CB 3D 4D | BIT 1,(IY+3Dh); *** 1104 | FD CB BA 4E | BIT 1,(IY-46h) 1105 | FD CB 0F 4F | BIT 1,(IY+Fh); *** 1106 | FD CB E7 50 | BIT 2,(IY-19h); *** 1107 | FD CB DF 51 | BIT 2,(IY-21h); *** 1108 | FD CB 6C 52 | BIT 2,(IY+6Ch); *** 1109 | FD CB FA 53 | BIT 2,(IY-6h); *** 1110 | FD CB EB 54 | BIT 2,(IY-15h); *** 1111 | FD CB EF 55 | BIT 2,(IY-11h); *** 1112 | FD CB 68 56 | BIT 2,(IY+68h) 1113 | FD CB FF 57 | BIT 2,(IY-1h); *** 1114 | FD CB 40 58 | BIT 3,(IY+40h); *** 1115 | FD CB 6F 59 | BIT 3,(IY+6Fh); *** 1116 | FD CB 3D 5A | BIT 3,(IY+3Dh); *** 1117 | FD CB D1 5B | BIT 3,(IY-2Fh); *** 1118 | FD CB F0 5C | BIT 3,(IY-10h); *** 1119 | FD CB E0 5D | BIT 3,(IY-20h); *** 1120 | FD CB 48 5E | BIT 3,(IY+48h) 1121 | FD CB E1 5F | BIT 3,(IY-1Fh); *** 1122 | FD CB 99 60 | BIT 4,(IY-67h); *** 1123 | FD CB EA 61 | BIT 4,(IY-16h); *** 1124 | FD CB F7 62 | BIT 4,(IY-9h); *** 1125 | FD CB C3 63 | BIT 4,(IY-3Dh); *** 1126 | FD CB 39 64 | BIT 4,(IY+39h); *** 1127 | FD CB F4 65 | BIT 4,(IY-Ch); *** 1128 | FD CB 83 66 | BIT 4,(IY-7Dh) 1129 | FD CB AD 67 | BIT 4,(IY-53h); *** 1130 | FD CB 9B 68 | BIT 5,(IY-65h); *** 1131 | FD CB 40 69 | BIT 5,(IY+40h); *** 1132 | FD CB 2A 6A | BIT 5,(IY+2Ah); *** 1133 | FD CB EF 6B | BIT 5,(IY-11h); *** 1134 | FD CB 8F 6C | BIT 5,(IY-71h); *** 1135 | FD CB 47 6D | BIT 5,(IY+47h); *** 1136 | FD CB D8 6E | BIT 5,(IY-28h) 1137 | FD CB D4 6F | BIT 5,(IY-2Ch); *** 1138 | FD CB AE 70 | BIT 6,(IY-52h); *** 1139 | FD CB 79 71 | BIT 6,(IY+79h); *** 1140 | FD CB D0 72 | BIT 6,(IY-30h); *** 1141 | FD CB 2D 73 | BIT 6,(IY+2Dh); *** 1142 | FD CB 95 74 | BIT 6,(IY-6Bh); *** 1143 | FD CB 9E 75 | BIT 6,(IY-62h); *** 1144 | FD CB BB 76 | BIT 6,(IY-45h) 1145 | FD CB E0 77 | BIT 6,(IY-20h); *** 1146 | 1147 | ; Page 41 1148 | 1149 | FD CB 25 78 | BIT 7,(IY+25h); *** 1150 | FD CB A0 79 | BIT 7,(IY-60h); *** 1151 | FD CB 0E 7A | BIT 7,(IY+Eh); *** 1152 | FD CB 71 7B | BIT 7,(IY+71h); *** 1153 | FD CB F7 7C | BIT 7,(IY-9h); *** 1154 | FD CB C8 7D | BIT 7,(IY-38h); *** 1155 | FD CB 92 7E | BIT 7,(IY-6Eh) 1156 | FD CB 41 7F | BIT 7,(IY+41h); *** 1157 | FD CB 89 80 | RES 0,(IY-77h),B; *** 1158 | FD CB 9A 81 | RES 0,(IY-66h),C; *** 1159 | FD CB CF 82 | RES 0,(IY-31h),D; *** 1160 | FD CB 49 83 | RES 0,(IY+49h),E; *** 1161 | FD CB 75 84 | RES 0,(IY+75h),H; *** 1162 | FD CB 65 85 | RES 0,(IY+65h),L; *** 1163 | FD CB 5F 86 | RES 0,(IY+5Fh) 1164 | FD CB 67 87 | RES 0,(IY+67h),A; *** 1165 | FD CB CD 88 | RES 1,(IY-33h),B; *** 1166 | FD CB 91 89 | RES 1,(IY-6Fh),C; *** 1167 | FD CB C0 8A | RES 1,(IY-40h),D; *** 1168 | FD CB 94 8B | RES 1,(IY-6Ch),E; *** 1169 | FD CB 3F 8C | RES 1,(IY+3Fh),H; *** 1170 | FD CB 79 8D | RES 1,(IY+79h),L; *** 1171 | FD CB 35 8E | RES 1,(IY+35h) 1172 | FD CB 7B 8F | RES 1,(IY+7Bh),A; *** 1173 | FD CB 74 90 | RES 2,(IY+74h),B; *** 1174 | FD CB 0D 91 | RES 2,(IY+Dh),C; *** 1175 | FD CB 18 92 | RES 2,(IY+18h),D; *** 1176 | FD CB 97 93 | RES 2,(IY-69h),E; *** 1177 | FD CB 5A 94 | RES 2,(IY+5Ah),H; *** 1178 | FD CB DA 95 | RES 2,(IY-26h),L; *** 1179 | FD CB 7B 96 | RES 2,(IY+7Bh) 1180 | FD CB D9 97 | RES 2,(IY-27h),A; *** 1181 | FD CB 09 98 | RES 3,(IY+9h),B; *** 1182 | FD CB B9 99 | RES 3,(IY-47h),C; *** 1183 | FD CB B8 9A | RES 3,(IY-48h),D; *** 1184 | FD CB 4B 9B | RES 3,(IY+4Bh),E; *** 1185 | FD CB E2 9C | RES 3,(IY-1Eh),H; *** 1186 | FD CB 26 9D | RES 3,(IY+26h),L; *** 1187 | FD CB FC 9E | RES 3,(IY-4h) 1188 | FD CB 78 9F | RES 3,(IY+78h),A; *** 1189 | FD CB D6 A0 | RES 4,(IY-2Ah),B; *** 1190 | FD CB 69 A1 | RES 4,(IY+69h),C; *** 1191 | FD CB 14 A2 | RES 4,(IY+14h),D; *** 1192 | FD CB 60 A3 | RES 4,(IY+60h),E; *** 1193 | FD CB 0F A4 | RES 4,(IY+Fh),H; *** 1194 | FD CB 64 A5 | RES 4,(IY+64h),L; *** 1195 | FD CB 69 A6 | RES 4,(IY+69h) 1196 | FD CB 04 A7 | RES 4,(IY+4h),A; *** 1197 | FD CB 2C A8 | RES 5,(IY+2Ch),B; *** 1198 | FD CB 27 A9 | RES 5,(IY+27h),C; *** 1199 | FD CB 7C AA | RES 5,(IY+7Ch),D; *** 1200 | FD CB F7 AB | RES 5,(IY-9h),E; *** 1201 | FD CB E8 AC | RES 5,(IY-18h),H; *** 1202 | FD CB 60 AD | RES 5,(IY+60h),L; *** 1203 | FD CB 45 AE | RES 5,(IY+45h) 1204 | FD CB 83 AF | RES 5,(IY-7Dh),A; *** 1205 | FD CB 2D B0 | RES 6,(IY+2Dh),B; *** 1206 | FD CB 1F B1 | RES 6,(IY+1Fh),C; *** 1207 | FD CB 39 B2 | RES 6,(IY+39h),D; *** 1208 | FD CB DA B3 | RES 6,(IY-26h),E; *** 1209 | FD CB BA B4 | RES 6,(IY-46h),H; *** 1210 | FD CB 4A B5 | RES 6,(IY+4Ah),L; *** 1211 | FD CB 5A B6 | RES 6,(IY+5Ah) 1212 | FD CB A2 B7 | RES 6,(IY-5Eh),A; *** 1213 | FD CB CD B8 | RES 7,(IY-33h),B; *** 1214 | FD CB 8F B9 | RES 7,(IY-71h),C; *** 1215 | FD CB 3C BA | RES 7,(IY+3Ch),D; *** 1216 | FD CB 6A BB | RES 7,(IY+6Ah),E; *** 1217 | FD CB D4 BC | RES 7,(IY-2Ch),H; *** 1218 | FD CB 24 BD | RES 7,(IY+24h),L; *** 1219 | FD CB 00 BE | RES 7,(IY+0h) 1220 | FD CB C9 BF | RES 7,(IY-37h),A; *** 1221 | FD CB 90 C0 | SET 0,(IY-70h),B; *** 1222 | FD CB B7 C1 | SET 0,(IY-49h),C; *** 1223 | FD CB 9A C2 | SET 0,(IY-66h),D; *** 1224 | FD CB 16 C3 | SET 0,(IY+16h),E; *** 1225 | FD CB 25 C4 | SET 0,(IY+25h),H; *** 1226 | FD CB 87 C5 | SET 0,(IY-79h),L; *** 1227 | FD CB DA C6 | SET 0,(IY-26h) 1228 | FD CB 7F C7 | SET 0,(IY+7Fh),A; *** 1229 | FD CB 89 C8 | SET 1,(IY-77h),B; *** 1230 | FD CB 6A C9 | SET 1,(IY+6Ah),C; *** 1231 | FD CB F2 CA | SET 1,(IY-Eh),D; *** 1232 | FD CB 73 CB | SET 1,(IY+73h),E; *** 1233 | FD CB 68 CC | SET 1,(IY+68h),H; *** 1234 | FD CB C6 CD | SET 1,(IY-3Ah),L; *** 1235 | FD CB 06 CE | SET 1,(IY+6h) 1236 | FD CB 62 CF | SET 1,(IY+62h),A; *** 1237 | FD CB 0D D0 | SET 2,(IY+Dh),B; *** 1238 | FD CB 96 D1 | SET 2,(IY-6Ah),C; *** 1239 | FD CB AA D2 | SET 2,(IY-56h),D; *** 1240 | FD CB C8 D3 | SET 2,(IY-38h),E; *** 1241 | FD CB 3A D4 | SET 2,(IY+3Ah),H; *** 1242 | FD CB 6A D5 | SET 2,(IY+6Ah),L; *** 1243 | FD CB D8 D6 | SET 2,(IY-28h) 1244 | FD CB 6F D7 | SET 2,(IY+6Fh),A; *** 1245 | FD CB 68 D8 | SET 3,(IY+68h),B; *** 1246 | FD CB 3A D9 | SET 3,(IY+3Ah),C; *** 1247 | FD CB 08 DA | SET 3,(IY+8h),D; *** 1248 | FD CB 75 DB | SET 3,(IY+75h),E; *** 1249 | FD CB 45 DC | SET 3,(IY+45h),H; *** 1250 | FD CB E2 DD | SET 3,(IY-1Eh),L; *** 1251 | FD CB 5B DE | SET 3,(IY+5Bh) 1252 | FD CB 1E DF | SET 3,(IY+1Eh),A; *** 1253 | FD CB 29 E0 | SET 4,(IY+29h),B; *** 1254 | FD CB BB E1 | SET 4,(IY-45h),C; *** 1255 | FD CB 17 E2 | SET 4,(IY+17h),D; *** 1256 | FD CB BC E3 | SET 4,(IY-44h),E; *** 1257 | FD CB 7C E4 | SET 4,(IY+7Ch),H; *** 1258 | FD CB C4 E5 | SET 4,(IY-3Ch),L; *** 1259 | FD CB FB E6 | SET 4,(IY-5h) 1260 | FD CB BA E7 | SET 4,(IY-46h),A; *** 1261 | FD CB 1D E8 | SET 5,(IY+1Dh),B; *** 1262 | FD CB 76 E9 | SET 5,(IY+76h),C; *** 1263 | FD CB 09 EA | SET 5,(IY+9h),D; *** 1264 | FD CB 1A EB | SET 5,(IY+1Ah),E; *** 1265 | FD CB 89 EC | SET 5,(IY-77h),H; *** 1266 | FD CB 9D ED | SET 5,(IY-63h),L; *** 1267 | FD CB D0 EE | SET 5,(IY-30h) 1268 | FD CB A0 EF | SET 5,(IY-60h),A; *** 1269 | FD CB F1 F0 | SET 6,(IY-Fh),B; *** 1270 | FD CB FC F1 | SET 6,(IY-4h),C; *** 1271 | FD CB F9 F2 | SET 6,(IY-7h),D; *** 1272 | FD CB DC F3 | SET 6,(IY-24h),E; *** 1273 | FD CB 9C F4 | SET 6,(IY-64h),H; *** 1274 | FD CB C5 F5 | SET 6,(IY-3Bh),L; *** 1275 | FD CB 53 F6 | SET 6,(IY+53h) 1276 | FD CB 65 F7 | SET 6,(IY+65h),A; *** 1277 | FD CB 1C F8 | SET 7,(IY+1Ch),B; *** 1278 | FD CB 80 F9 | SET 7,(IY-80h),C; *** 1279 | FD CB 80 FA | SET 7,(IY-80h),D; *** 1280 | FD CB E4 FB | SET 7,(IY-1Ch),E; *** 1281 | FD CB BD FC | SET 7,(IY-43h),H; *** 1282 | FD CB 36 FD | SET 7,(IY+36h),L; *** 1283 | FD CB 7A FE | SET 7,(IY+7Ah) 1284 | FD CB 69 FF | SET 7,(IY+69h),A; *** 1285 | FD E1 | POP IY 1286 | FD E3 | EX (SP),IY 1287 | FD E5 | PUSH IY 1288 | FD E9 | JP (IY) 1289 | FD F9 | LD SP,IY 1290 | FE B1 | CP B1h 1291 | FF | RST 38h 1292 | -------------------------------------------------------------------------------- /librespectrum/tests/exerciser/zexall.src: -------------------------------------------------------------------------------- 1 | title 'Z80 full instruction set exerciser for Spectrum' 2 | 3 | ; zexall.src - Z80 full instruction set exerciser 4 | ; Copyright (C) 1994 Frank D. Cringle 5 | ; 6 | ; 03-Nov-2002: Modified to assemble with ZMAC and MAXAM 7 | ; Copyright (C) 2002 J.G.Harston 8 | ; + labels on equates mustn't have trailing colon 9 | ; + macros don't understand <...> sequence, so parameters are passed 10 | ; explicitly 11 | ; + ds n,c not supported, so strings are set to full explicit length 12 | ; + nonstandard 'cp a,' and 'and a,' changed to 'cp ' and 'and ' 13 | ; 14 | ; 03-Nov-2002: Modified to run on Spectrum 15 | ; Copyright (C) 2002 J.G.Harston 16 | ; 17 | ; 23-May-2007: Updated reference CRCs and fixed use of EI/DI 18 | ; (by Stuart Brady) 19 | ; 20 | ; This program is free software; you can redistribute it and/or 21 | ; modify it under the terms of the GNU General Public License 22 | ; as published by the Free Software Foundation; either version 2 23 | ; of the License, or (at your option) any later version. 24 | ; 25 | ; This program is distributed in the hope that it will be useful, 26 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 | ; GNU General Public License for more details. 29 | ; 30 | ; You should have received a copy of the GNU General Public License 31 | ; along with this program; if not, write to the Free Software 32 | ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 33 | 34 | aseg 35 | org 8000h 36 | 37 | jp start 38 | 39 | ; machine state before test (needs to be at predictably constant address) 40 | msbt: ds 14 41 | spbt: ds 2 42 | msbthi equ msbt / 0100h 43 | msbtlo equ msbt & 0ffh 44 | 45 | 46 | ; For the purposes of this test program, the machine state consists of: 47 | ; a 2 byte memory operand, followed by 48 | ; the registers iy,ix,hl,de,bc,af,sp 49 | ; for a total of 16 bytes. 50 | 51 | ; The program tests instructions (or groups of similar instructions) 52 | ; by cycling through a sequence of machine states, executing the test 53 | ; instruction for each one and running a 32-bit crc over the resulting 54 | ; machine states. At the end of the sequence the crc is compared to 55 | ; an expected value that was found empirically on a real Z80. 56 | 57 | ; A test case is defined by a descriptor which consists of: 58 | ; a flag mask byte, 59 | ; the base case, 60 | ; the incement vector, 61 | ; the shift vector, 62 | ; the expected crc, 63 | ; a short descriptive message. 64 | ; 65 | ; The flag mask byte is used to prevent undefined flag bits from 66 | ; influencing the results. Documented flags are as per Mostek Z80 67 | ; Technical Manual. 68 | ; 69 | ; The next three parts of the descriptor are 20 byte vectors 70 | ; corresponding to a 4 byte instruction and a 16 byte machine state. 71 | ; The first part is the base case, which is the first test case of 72 | ; the sequence. This base is then modified according to the next 2 73 | ; vectors. Each 1 bit in the increment vector specifies a bit to be 74 | ; cycled in the form of a binary counter. For instance, if the byte 75 | ; corresponding to the accumulator is set to 0ffh in the increment 76 | ; vector, the test will be repeated for all 256 values of the 77 | ; accumulator. Note that 1 bits don't have to be contiguous. The 78 | ; number of test cases 'caused' by the increment vector is equal to 79 | ; 2^(number of 1 bits). The shift vector is similar, but specifies a 80 | ; set of bits in the test case that are to be successively inverted. 81 | ; Thus the shift vector 'causes' a number of test cases equal to the 82 | ; number of 1 bits in it. 83 | 84 | ; The total number of test cases is the product of those caused by the 85 | ; counter and shift vectors and can easily become unweildy. Each 86 | ; individual test case can take a few milliseconds to execute, due to 87 | ; the overhead of test setup and crc calculation, so test design is a 88 | ; compromise between coverage and execution time. 89 | 90 | ; This program is designed to detect differences between 91 | ; implementations and is not ideal for diagnosing the causes of any 92 | ; discrepancies. However, provided a reference implementation (or 93 | ; real system) is available, a failing test case can be isolated by 94 | ; hand using a binary search of the test space. 95 | 96 | 97 | start: ; ld hl,(6) 98 | ld hl,08000h ; put stack at top of 32k 99 | ld sp,hl 100 | ld a,2 101 | call 01601h ; open stream 2 (screen) 102 | di ; ensure INTs don't mess with test 103 | ld de,msg1 104 | ld c,9 105 | call bdos 106 | 107 | ld hl,tests ; first test case 108 | loop: ld a,(hl) ; end of list ? 109 | inc hl 110 | or (hl) 111 | jp z,done 112 | dec hl 113 | call stt 114 | jp loop 115 | 116 | done: ld de,msg2 117 | ld c,9 118 | call bdos 119 | stop: 120 | jp stop ; wait for reset 121 | 122 | tests: 123 | dw adc16 124 | dw add16 125 | dw add16x 126 | dw add16y 127 | dw alu8i 128 | dw alu8r 129 | dw alu8rx 130 | dw alu8x 131 | dw bitx 132 | dw bitz80 133 | dw cpd1 134 | dw cpi1 135 | dw daaop ; can't use opcode as label 136 | dw inca 137 | dw incb 138 | dw incbc 139 | dw incc 140 | dw incd 141 | dw incde 142 | dw ince 143 | dw inch 144 | dw inchl 145 | dw incix 146 | dw inciy 147 | dw incl 148 | dw incm 149 | dw incsp 150 | dw incx 151 | dw incxh 152 | dw incxl 153 | dw incyh 154 | dw incyl 155 | dw ld161 156 | dw ld162 157 | dw ld163 158 | dw ld164 159 | dw ld165 160 | dw ld166 161 | dw ld167 162 | dw ld168 163 | dw ld16im 164 | dw ld16ix 165 | dw ld8bd 166 | dw ld8im 167 | dw ld8imx 168 | dw ld8ix1 169 | dw ld8ix2 170 | dw ld8ix3 171 | dw ld8ixy 172 | dw ld8rr 173 | dw ld8rrx 174 | dw lda 175 | dw ldd1 176 | dw ldd2 177 | dw ldi1 178 | dw ldi2 179 | dw negop ; jgh: can't use opcode as label 180 | dw rldop ; jgh: can't use opcode as label 181 | dw rot8080 182 | dw rotxy 183 | dw rotz80 184 | dw srz80 185 | dw srzx 186 | dw st8ix1 187 | dw st8ix2 188 | dw st8ix3 189 | dw stabd 190 | dw 0 191 | 192 | ; jgh: macro syntax changed for ZMAC and MAXAM 193 | ; can't use opcodes as labels 194 | ; ZMAC allows &nn as hex, so & removed from local labels 195 | ; 196 | tstr macro insn1,insn2,insn3,insn4,memop,riy,rix,rhl,rde,rbc,flags,acc,rsp,?lab 197 | ?lab: db insn1,insn2,insn3,insn4 198 | dw memop,riy,rix,rhl,rde,rbc 199 | db flags 200 | db acc 201 | dw rsp 202 | if $-?lab ne 20 203 | error 'missing parameter' 204 | endif 205 | endm 206 | 207 | tmsg macro msg,?lab 208 | ?lab: db 'msg' 209 | if $ ge ?lab+31 210 | error 'message too long' 211 | else 212 | ; ds ?lab+30-$,'.' ; jgh: ZMAC/MAXAM don't have char parameter 213 | endif 214 | db '$' 215 | endm 216 | 217 | ; jgh: ZMAC/MAXAM don't recognise syntax for macros, so full parameters given 218 | ; jgh: each tmsg has full string, as ZMAC/MAXAM don't have ds n,c pseudo-op 219 | 220 | ; hl, (38,912 cycles) 221 | adc16: db 0ffh ; flag mask 222 | tstr 0edh,042h,0,0,0832ch,04f88h,0f22bh,0b339h,07e1fh,01563h,0d3h,089h,0465eh 223 | tstr 0,038h,0,0,0,0,0,0f821h,0,0,0,0,0 ; (1024 cycles) 224 | tstr 0,0,0,0,0,0,0,-1,-1,-1,0d7h,0,-1 ; (38 cycles) 225 | db 0d4h,08ah,0d5h,019h ; expected crc 226 | tmsg ' hl,....' 227 | 228 | ; add hl, (19,456 cycles) 229 | add16: db 0ffh ; flag mask 230 | tstr 9,0,0,0,0c4a5h,0c4c7h,0d226h,0a050h,058eah,08566h,0c6h,0deh,09bc9h 231 | tstr 030h,0,0,0,0,0,0,0f821h,0,0,0,0,0 ; (512 cycles) 232 | tstr 0,0,0,0,0,0,0,-1,-1,-1,0d7h,0,-1 ; (38 cycles) 233 | db 0d9h,0a4h,0cah,005h ; expected crc 234 | tmsg 'add hl,..........' 235 | 236 | ; add ix, (19,456 cycles) 237 | add16x: db 0ffh ; flag mask 238 | tstr 0ddh,9,0,0,0ddach,0c294h,0635bh,033d3h,06a76h,0fa20h,094h,068h,036f5h 239 | tstr 0,030h,0,0,0,0,0f821h,0,0,0,0,0,0 ; (512 cycles) 240 | tstr 0,0,0,0,0,0,-1,0,-1,-1,0d7h,0,-1 ; (38 cycles) 241 | db 0b1h,0dfh,08eh,0c0h ; expected crc 242 | tmsg 'add ix,..........' 243 | 244 | ; add iy, (19,456 cycles) 245 | add16y: db 0ffh ; flag mask 246 | tstr 0fdh,9,0,0,0c7c2h,0f407h,051c1h,03e96h,00bf4h,0510fh,092h,01eh,071eah 247 | tstr 0,030h,0,0,0,0f821h,0,0,0,0,0,0,0 ; (512 cycles) 248 | tstr 0,0,0,0,0,-1,0,0,-1,-1,0d7h,0,-1 ; (38 cycles) 249 | db 039h,0c8h,058h,09bh ; expected crc 250 | tmsg 'add iy,..........' 251 | 252 | ; aluop a,nn (28,672 cycles) 253 | alu8i: db 0ffh ; flag mask 254 | tstr 0c6h,0,0,0,009140h,07e3ch,07a67h,0df6dh,05b61h,00b29h,010h,066h,085b2h 255 | tstr 038h,0,0,0,0,0,0,0,0,0,0,-1,0 ; (2048 cycles) 256 | tstr 0,-1,0,0,0,0,0,0,0,0,0d7h,0,0 ; (14 cycles) 257 | db 051h,0c1h,09ch,02eh ; expected crc 258 | tmsg 'aluop a,nn....................' 259 | 260 | ; aluop a, (753,664 cycles) 261 | alu8r: db 0ffh ; flag mask 262 | tstr 080h,0,0,0,0c53eh,0573ah,04c4dh,msbt,0e309h,0a666h,0d0h,03bh,0adbbh 263 | tstr 03fh,0,0,0,0,0,0,0,0,0,0,-1,0 ; (16,384 cycles) 264 | tstr 0,0,0,0,0ffh,0,0,0,-1,-1,0d7h,0,0 ; (46 cycles) 265 | db 01dh,0fdh,078h,064h ; expected crc 266 | tmsg 'aluop a,..' 267 | 268 | ; aluop a, (376,832 cycles) 269 | alu8rx: db 0ffh ; flag mask 270 | tstr 0ddh,084h,0,0,0d6f7h,0c76eh,0accfh,02847h,022ddh,0c035h,0c5h,038h,0234bh 271 | tstr 020h,039h,0,0,0,0,0,0,0,0,0,-1,0 ; (8,192 cycles) 272 | tstr 0,0,0,0,0ffh,0,0,0,-1,-1,0d7h,0,0 ; (46 cycles) 273 | db 0a8h,086h,0cch,044h ; expected crc 274 | tmsg 'aluop a,.....' 275 | 276 | ; aluop a,(+1) (229,376 cycles) 277 | alu8x: db 0ffh ; flag mask 278 | tstr 0ddh,086h,1,0,090b7h,msbt-1,msbt-1,032fdh,0406eh,0c1dch,045h,06eh,0e5fah 279 | tstr 020h,038h,0,0,0,1,1,0,0,0,0,-1,0 ; (16,384 cycles) 280 | tstr 0,0,0,0,0ffh,0,0,0,0,0,0d7h,0,0 ; (14 cycles) 281 | db 052h,018h,05eh,0a5h ; expected crc 282 | tmsg 'aluop a,(+1)...........' 283 | 284 | ; bit n,(+1) (2048 cycles) 285 | bitx: db 0ffh ; flag mask 286 | tstr 0ddh,0cbh,1,046h,02075h,msbt-1,msbt-1,03cfch,0a79ah,03d74h,051h,027h,0ca14h 287 | tstr 020h,0,0,038h,0,0,0,0,0,0,053h,0,0 ; (256 cycles) 288 | tstr 0,0,0,0,0ffh,0,0,0,0,0,0,0,0 ; (8 cycles) 289 | db 0e1h,0d4h,0a8h,096h ; expected crc 290 | tmsg 'bit n,(+1).............' 291 | 292 | ; bit n, (49,152 cycles) 293 | bitz80: db 0ffh ; flag mask 294 | tstr 0cbh,040h,0,0,03ef1h,09dfch,07acch,msbt,0be61h,07a86h,050h,024h,01998h 295 | tstr 0,03fh,0,0,0,0,0,0,0,0,053h,0,0 ; (1024 cycles) 296 | tstr 0,0,0,0,0ffh,0,0,0,-1,-1,0,-1,0 ; (48 cycles) 297 | db 0f5h,054h,0d7h,042h ; expected crc 298 | tmsg 'bit n,....' 299 | 300 | ; cpd (1) (6144 cycles) 301 | cpd1: db 0ffh ; flag mask 302 | tstr 0edh,0a9h,0,0,0c7b6h,072b4h,018f6h,msbt+17,08dbdh,1,0c0h,030h,094a3h 303 | tstr 0,010h,0,0,0,0,0,0,0,010,0,-1,0 ; (1024 cycles) 304 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 305 | db 0d0h,06bh,09ch,015h ; expected crc 306 | tmsg 'cpd........................' 307 | 308 | ; cpi (1) (6144 cycles) 309 | cpi1: db 0ffh ; flag mask 310 | tstr 0edh,0a1h,0,0,04d48h,0af4ah,0906bh,msbt,04e71h,1,093h,06ah,0907ch 311 | tstr 0,010h,0,0,0,0,0,0,0,010,0,-1,0 ; (1024 cycles) 312 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 313 | db 068h,03eh,07bh,02ah ; expected crc 314 | tmsg 'cpi........................' 315 | 316 | ; 317 | daaop: db 0ffh ; flag mask 318 | tstr 027h,0,0,0,02141h,009fah,01d60h,0a559h,08d5bh,09079h,004h,08eh,0299dh 319 | tstr 018h,0,0,0,0,0,0,0,0,0,0d7h,-1,0 ; (65,536 cycles) 320 | tstr 0,0,0,0,0,0,0,0,0,0,0,0,0 ; (1 cycle) 321 | db 06dh,02dh,0d2h,013h ; expected crc 322 | tmsg '.............' 323 | 324 | ; a (3072 cycles) 325 | inca: db 0ffh ; flag mask 326 | tstr 03ch,0,0,0,04adfh,0d5d8h,0e598h,08a2bh,0a7b0h,0431bh,044h,05ah,0d030h 327 | tstr 001h,0,0,0,0,0,0,0,0,0,0,-1,0 ; (512 cycles) 328 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 329 | db 081h,0fah,081h,000h ; expected crc 330 | tmsg ' a...................' 331 | 332 | ; b (3072 cycles) 333 | incb: db 0ffh ; flag mask 334 | tstr 004h,0,0,0,0d623h,0432dh,07a61h,08180h,05a86h,01e85h,086h,058h,09bbbh 335 | tstr 001h,0,0,0,0,0,0,0,0,0ff00h,0,0,0 ; (512 cycles) 336 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 337 | db 077h,0f3h,05ah,073h ; expected crc 338 | tmsg ' b...................' 339 | 340 | ; bc (1536 cycles) 341 | incbc: db 0ffh ; flag mask 342 | tstr 003h,0,0,0,0cd97h,044abh,08dc9h,0e3e3h,011cch,0e8a4h,002h,049h,02a4dh 343 | tstr 008h,0,0,0,0,0,0,0,0,0f821h,0,0,0 ; (256 cycles) 344 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 345 | db 0d2h,0aeh,03bh,0ech ; expected crc 346 | tmsg ' bc..................' 347 | 348 | ; c (3072 cycles) 349 | incc: db 0ffh ; flag mask 350 | tstr 00ch,0,0,0,0d789h,00935h,0055bh,09f85h,08b27h,0d208h,095h,005h,00660h 351 | tstr 001h,0,0,0,0,0,0,0,0,0ffh,0,0,0 ; (512 cycles) 352 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 353 | db 01ah,0f6h,012h,0a7h ; expected crc 354 | tmsg ' c...................' 355 | 356 | ; d (3072 cycles) 357 | incd: db 0ffh ; flag mask 358 | tstr 014h,0,0,0,0a0eah,05fbah,065fbh,0981ch,038cch,0debch,043h,05ch,003bdh 359 | tstr 001h,0,0,0,0,0,0,0,0ff00h,0,0,0,0 ; (512 cycles) 360 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 361 | db 0d1h,046h,0bfh,051h ; expected crc 362 | tmsg ' d...................' 363 | 364 | ; de (1536 cycles) 365 | incde: db 0ffh ; flag mask 366 | tstr 013h,0,0,0,0342eh,0131dh,028c9h,00acah,09967h,03a2eh,092h,0f6h,09d54h 367 | tstr 008h,0,0,0,0,0,0,0,0f821h,0,0,0,0 ; (256 cycles) 368 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 369 | db 0aeh,0c6h,0d4h,02ch ; expected crc 370 | tmsg ' de..................' 371 | 372 | ; e (3072 cycles) 373 | ince: db 0ffh ; flag mask 374 | tstr 01ch,0,0,0,0602fh,04c0dh,02402h,0e2f5h,0a0f4h,0a10ah,013h,032h,05925h 375 | tstr 001h,0,0,0,0,0,0,0,0ffh,0,0,0,0 ; (512 cycles) 376 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 377 | db 0cah,08ch,06ah,0c2h ; expected crc 378 | tmsg ' e...................' 379 | 380 | ; h (3072 cycles) 381 | inch: db 0ffh ; flag mask 382 | tstr 024h,0,0,0,01506h,0f2ebh,0e8ddh,0262bh,011a6h,0bc1ah,017h,006h,02818h 383 | tstr 001h,0,0,0,0,0,0,0ff00h,0,0,0,0,0 ; (512 cycles) 384 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 385 | db 056h,00fh,095h,05eh ; expected crc 386 | tmsg ' h...................' 387 | 388 | ; hl (1536 cycles) 389 | inchl: db 0ffh ; flag mask 390 | tstr 023h,0,0,0,0c3f4h,007a5h,01b6dh,04f04h,0e2c2h,0822ah,057h,0e0h,0c3e1h 391 | tstr 008h,0,0,0,0,0,0,0f821h,0,0,0,0,0 ; (256 cycles) 392 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 393 | db 0fch,00dh,06dh,04ah ; expected crc 394 | tmsg ' hl..................' 395 | 396 | ; ix (1536 cycles) 397 | incix: db 0ffh ; flag mask 398 | tstr 0ddh,023h,0,0,0bc3ch,00d9bh,0e081h,0adfdh,09a7fh,096e5h,013h,085h,00be2h 399 | tstr 0,8,0,0,0,0,0f821h,0,0,0,0,0,0 ; (256 cycles) 400 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 401 | db 0a5h,04dh,0beh,031h ; expected crc 402 | tmsg ' ix..................' 403 | 404 | ; iy (1536 cycles) 405 | inciy: db 0ffh ; flag mask 406 | tstr 0fdh,023h,0,0,09402h,0637ah,03182h,0c65ah,0b2e9h,0abb4h,016h,0f2h,06d05h 407 | tstr 0,8,0,0,0,0f821h,0,0,0,0,0,0,0 ; (256 cycles) 408 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 409 | db 050h,05dh,051h,0a3h ; expected crc 410 | tmsg ' iy..................' 411 | 412 | ; l (3072 cycles) 413 | incl: db 0ffh ; flag mask 414 | tstr 02ch,0,0,0,08031h,0a520h,04356h,0b409h,0f4c1h,0dfa2h,0d1h,03ch,03ea2h 415 | tstr 001h,0,0,0,0,0,0,0ffh,0,0,0,0,0 ; (512 cycles) 416 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 417 | db 0a0h,0a1h,0b4h,09fh ; expected crc 418 | tmsg ' l...................' 419 | 420 | ; (hl) (3072 cycles) 421 | incm: db 0ffh ; flag mask 422 | tstr 034h,0,0,0,0b856h,00c7ch,0e53eh,msbt,0877eh,0da58h,015h,05ch,01f37h 423 | tstr 001h,0,0,0,0ffh,0,0,0,0,0,0,0,0 ; (512 cycles) 424 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 425 | db 01ch,0a0h,0ech,0e9h ; expected crc 426 | tmsg ' (hl)................' 427 | 428 | ; sp (1536 cycles) 429 | incsp: db 0ffh ; flag mask 430 | tstr 033h,0,0,0,0346fh,0d482h,0d169h,0deb6h,0a494h,0f476h,053h,002h,0855bh 431 | tstr 008h,0,0,0,0,0,0,0,0,0,0,0,0f821h ; (256 cycles) 432 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 433 | db 05dh,0ach,0d5h,027h ; expected crc 434 | tmsg ' sp..................' 435 | 436 | ; (+1) (6144 cycles) 437 | incx: db 0ffh ; flag mask 438 | tstr 0ddh,034h,1,0,0fa6eh,msbt-1,msbt-1,02c28h,08894h,05057h,016h,033h,0286fh 439 | tstr 020h,1,0,0,0ffh,0,0,0,0,0,0,0,0 ; (1024 cycles) 440 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 441 | db 0ffh,060h,016h,065h ; expected crc 442 | tmsg ' (+1).........' 443 | 444 | ; ixh (3072 cycles) 445 | incxh: db 0ffh ; flag mask 446 | tstr 0ddh,024h,0,0,0b838h,0316ch,0c6d4h,03e01h,08358h,015b4h,081h,0deh,04259h 447 | tstr 0,1,0,0,0,0ff00h,0,0,0,0,0,0,0 ; (512 cycles) 448 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 449 | db 06fh,046h,036h,062h ; expected crc 450 | tmsg ' ixh.................' 451 | 452 | ; ixl (3072 cycles) 453 | incxl: db 0ffh ; flag mask 454 | tstr 0ddh,02ch,0,0,04d14h,07460h,076d4h,006e7h,032a2h,0213ch,0d6h,0d7h,099a5h 455 | tstr 0,1,0,0,0,0ffh,0,0,0,0,0,0,0 ; (512 cycles) 456 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 457 | db 002h,07bh,0efh,02ch ; expected crc 458 | tmsg ' ixl.................' 459 | 460 | ; iyh (3072 cycles) 461 | incyh: db 0ffh ; flag mask 462 | tstr 0ddh,024h,0,0,02836h,09f6fh,09116h,061b9h,082cbh,0e219h,092h,073h,0a98ch 463 | tstr 0,1,0,0,0ff00h,0,0,0,0,0,0,0,0 ; (512 cycles) 464 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 465 | db 02dh,096h,06ch,0f3h ; expected crc 466 | tmsg ' iyh.................' 467 | 468 | ; iyl (3072 cycles) 469 | incyl: db 0ffh ; flag mask 470 | tstr 0ddh,02ch,0,0,0d7c6h,062d5h,0a09eh,07039h,03e7eh,09f12h,090h,0d9h,0220fh 471 | tstr 0,1,0,0,0ffh,0,0,0,0,0,0,0,0 ; (512 cycles) 472 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 473 | db 036h,0c1h,01eh,075h ; expected crc 474 | tmsg ' iyl.................' 475 | 476 | ; ld ,(nnnn) (32 cycles) 477 | ld161: db 0ffh ; flag mask 478 | tstr 0edh,04bh,msbtlo,msbthi,0f9a8h,0f559h,093a4h,0f5edh,06f96h,0d968h,086h,0e6h,04bd8h 479 | tstr 0,010h,0,0,0,0,0,0,0,0,0,0,0 ; (2 cycles) 480 | tstr 0,0,0,0,-1,0,0,0,0,0,0,0,0 ; (16 cycles) 481 | db 04dh,045h,0a9h,0ach ; expected crc 482 | tmsg 'ld ,(nnnn).............' 483 | 484 | ; ld hl,(nnnn) (16 cycles) 485 | ld162: db 0ffh ; flag mask 486 | tstr 02ah,msbtlo,msbthi,0,09863h,07830h,02077h,0b1feh,0b9fah,0abb8h,004h,006h,06015h 487 | tstr 0,0,0,0,0,0,0,0,0,0,0,0,0 ; (1 cycle) 488 | tstr 0,0,0,0,-1,0,0,0,0,0,0,0,0 ; (16 cycles) 489 | db 05fh,097h,024h,087h ; expected crc 490 | tmsg 'ld hl,(nnnn)..................' 491 | 492 | ; ld sp,(nnnn) (16 cycles) 493 | ld163: db 0ffh ; flag mask 494 | tstr 0edh,07bh,msbtlo,msbthi,08dfch,057d7h,02161h,0ca18h,0c185h,027dah,083h,01eh,0f460h 495 | tstr 0,0,0,0,0,0,0,0,0,0,0,0,0 ; (1 cycles) 496 | tstr 0,0,0,0,-1,0,0,0,0,0,0,0,0 ; (16 cycles) 497 | db 07ah,0ceh,0a1h,01bh ; expected crc 498 | tmsg 'ld sp,(nnnn)..................' 499 | 500 | ; ld ,(nnnn) (32 cycles) 501 | ld164: db 0ffh ; flag mask 502 | tstr 0ddh,02ah,msbtlo,msbthi,0ded7h,0a6fah,0f780h,0244ch,087deh,0bcc2h,016h,063h,04c96h 503 | tstr 020h,0,0,0,0,0,0,0,0,0,0,0,0 ; (2 cycles) 504 | tstr 0,0,0,0,-1,0,0,0,0,0,0,0,0 ; (16 cycles) 505 | db 085h,08bh,0f1h,06dh ; expected crc 506 | tmsg 'ld ,(nnnn).............' 507 | 508 | ; ld (nnnn), (64 cycles) 509 | ld165: db 0ffh ; flag mask 510 | tstr 0edh,043h,msbtlo,msbthi,01f98h,0844dh,0e8ach,0c9edh,0c95dh,08f61h,080h,03fh,0c7bfh 511 | tstr 0,010h,0,0,0,0,0,0,0,0,0,0,0 ; (2 cycles) 512 | tstr 0,0,0,0,0,0,0,0,-1,-1,0,0,0 ; (32 cycles) 513 | db 064h,01eh,087h,015h ; expected crc 514 | tmsg 'ld (nnnn),.............' 515 | 516 | ; ld (nnnn),hl (16 cycles) 517 | ld166: db 0ffh ; flag mask 518 | tstr 022h,msbtlo,msbthi,0,0d003h,07772h,07f53h,03f72h,064eah,0e180h,010h,02dh,035e9h 519 | tstr 0,0,0,0,0,0,0,0,0,0,0,0,0 ; (1 cycle) 520 | tstr 0,0,0,0,0,0,0,-1,0,0,0,0,0 ; (16 cycles) 521 | db 0a3h,060h,08bh,047h ; expected crc 522 | tmsg 'ld (nnnn),hl..................' 523 | 524 | ; ld (nnnn),sp (16 cycles) 525 | ld167: db 0ffh ; flag mask 526 | tstr 0edh,073h,msbtlo,msbthi,0c0dch,0d1d6h,0ed5ah,0f356h,0afdah,06ca7h,044h,09fh,03f0ah 527 | tstr 0,0,0,0,0,0,0,0,0,0,0,0,0 ; (1 cycle) 528 | tstr 0,0,0,0,0,0,0,0,0,0,0,0,-1 ; (16 cycles) 529 | db 016h,058h,05fh,0d7h ; expected crc 530 | tmsg 'ld (nnnn),sp..................' 531 | 532 | ; ld (nnnn), (64 cycles) 533 | ld168: db 0ffh ; flag mask 534 | tstr 0ddh,022h,msbtlo,msbthi,06cc3h,00d91h,06900h,08ef8h,0e3d6h,0c3f7h,0c6h,0d9h,0c2dfh 535 | tstr 020h,0,0,0,0,0,0,0,0,0,0,0,0 ; (2 cycles) 536 | tstr 0,0,0,0,0,-1,-1,0,0,0,0,0,0 ; (32 cycles) 537 | db 0bah,010h,02ah,06bh ; expected crc 538 | tmsg 'ld (nnnn),.............' 539 | 540 | ; ld ,nnnn (64 cycles) 541 | ld16im: db 0ffh ; flag mask 542 | tstr 1,0,0,0,05c1ch,02d46h,08eb9h,06078h,074b1h,0b30eh,046h,0d1h,030cch 543 | tstr 030h,0,0,0,0,0,0,0,0,0,0,0,0 ; (4 cycles) 544 | tstr 0,0ffh,0ffh,0,0,0,0,0,0,0,0,0,0 ; (16 cycles) 545 | db 0deh,039h,019h,069h ; expected crc 546 | tmsg 'ld ,nnnn.........' 547 | 548 | ; ld ,nnnn (32 cycles) 549 | ld16ix: db 0ffh ; flag mask 550 | tstr 0ddh,021h,0,0,087e8h,02006h,0bd12h,0b69bh,07253h,0a1e5h,051h,013h,0f1bdh 551 | tstr 020h,0,0,0,0,0,0,0,0,0,0,0,0 ; (2 cycles) 552 | tstr 0,0,0ffh,0ffh,0,0,0,0,0,0,0,0,0 ; (16 cycles) 553 | db 022h,07dh,0d5h,025h ; expected crc 554 | tmsg 'ld ,nnnn...............' 555 | 556 | ; ld a,<(bc),(de)> (44 cycles) 557 | ld8bd: db 0ffh ; flag mask 558 | tstr 00ah,0,0,0,0b3a8h,01d2ah,07f8eh,042ach,msbt,msbt,0c6h,0b1h,0ef8eh 559 | tstr 010h,0,0,0,0,0,0,0,0,0,0,0,0 ; (2 cycles) 560 | tstr 0,0,0,0,0ffh,0,0,0,0,0,0d7h,-1,0 ; (22 cycles) 561 | db 03bh,0c6h,06ah,071h ; expected crc 562 | tmsg 'ld a,<(bc),(de)>..............' 563 | 564 | ; ld ,nn (64 cycles) 565 | ld8im: db 0ffh ; flag mask 566 | tstr 6,0,0,0,0c407h,0f49dh,0d13dh,00339h,0de89h,07455h,053h,0c0h,05509h 567 | tstr 038h,0,0,0,0,0,0,0,0,0,0,0,0 ; (8 cycles) 568 | tstr 0,0,0,0,0,0,0,0,0,0,0,-1,0 ; (8 cycles) 569 | db 0f1h,0dah,0b5h,056h ; expected crc 570 | tmsg 'ld ,nn....' 571 | 572 | ; ld (+1),nn (32 cycles) 573 | ld8imx: db 0ffh ; flag mask 574 | tstr 0ddh,036h,1,0,01b45h,msbt-1,msbt-1,0d5c1h,061c7h,0bdc4h,0c0h,085h,0cd16h 575 | tstr 020h,0,0,0,0,0,0,0,0,0,0,0,0 ; (2 cycles) 576 | tstr 0,0,0,-1,0,0,0,0,0,0,0,-1,0 ; (16 cycles) 577 | db 0e8h,00ch,0ffh,011h ; expected crc 578 | tmsg 'ld (+1),nn.............' 579 | 580 | ; ld ,(+1) (512 cycles) 581 | ld8ix1: db 0ffh ; flag mask 582 | tstr 0ddh,046h,1,0,0d016h,msbt-1,msbt-1,04260h,07f39h,00404h,097h,04ah,0d085h 583 | tstr 020h,018h,0,0,0,1,1,0,0,0,0,0,0 ; (32 cycles) 584 | tstr 0,0,0,0,-1,0,0,0,0,0,0,0,0 ; (16 cycles) 585 | db 036h,00fh,001h,082h ; expected crc 586 | tmsg 'ld ,(+1)......' 587 | 588 | ; ld ,(+1) (256 cycles) 589 | ld8ix2: db 0ffh ; flag mask 590 | tstr 0ddh,066h,1,0,084e0h,msbt-1,msbt-1,09c52h,0a799h,049b6h,093h,000h,0eeadh 591 | tstr 020h,008h,0,0,0,1,1,0,0,0,0,0,0 ; (16 cycles) 592 | tstr 0,0,0,0,-1,0,0,0,0,0,0,0,0 ; (16 cycles) 593 | db 0b7h,0bah,00ch,09ch ; expected crc 594 | tmsg 'ld ,(+1)..........' 595 | 596 | ; ld a,(+1) (128 cycles) 597 | ld8ix3: db 0ffh ; flag mask 598 | tstr 0ddh,07eh,1,0,0d8b6h,msbt-1,msbt-1,0c612h,0df07h,09cd0h,043h,0a6h,0a0e5h 599 | tstr 020h,0,0,0,0,1,1,0,0,0,0,0,0 ; (8 cycles) 600 | tstr 0,0,0,0,-1,0,0,0,0,0,0,0,0 ; (16 cycles) 601 | db 0e7h,05ah,01eh,063h ; expected crc 602 | tmsg 'ld a,(+1)..............' 603 | 604 | ; ld ,nn (32 cycles) 605 | ld8ixy: db 0ffh ; flag mask 606 | tstr 0ddh,026h,0,0,03c53h,04640h,0e179h,07711h,0c107h,01afah,081h,0adh,05d9bh 607 | tstr 020h,8,0,0,0,0,0,0,0,0,0,0,0 ; (4 cycles) 608 | tstr 0,0,0,0,0,0,0,0,0,0,0,-1,0 ; (8 cycles) 609 | db 024h,0e8h,082h,08bh ; expected crc 610 | tmsg 'ld ,nn.......' 611 | 612 | ; ld , (3456 cycles) 613 | ld8rr: db 0ffh ; flag mask 614 | tstr 040h,0,0,0,072a4h,0a024h,061ach,msbt,082c7h,0718fh,097h,08fh,0ef8eh 615 | tstr 03fh,0,0,0,0,0,0,0,0,0,0,0,0 ; (64 cycles) 616 | tstr 0,0,0,0,0ffh,0,0,0,-1,-1,0d7h,-1,0 ; (54 cycles) 617 | db 043h,075h,089h,0beh ; expected crc 618 | tmsg 'ld ,........' 619 | 620 | ; ld , (6912 cycles) 621 | ld8rrx: db 0ffh ; flag mask 622 | tstr 0ddh,040h,0,0,0bcc5h,msbt,msbt,msbt,02fc2h,098c0h,083h,01fh,03bcdh 623 | tstr 020h,03fh,0,0,0,0,0,0,0,0,0,0,0 ; (128 cycles) 624 | tstr 0,0,0,0,0ffh,0,0,0,-1,-1,0d7h,-1,0 ; (54 cycles) 625 | db 0f1h,063h,0aeh,01ah ; expected crc 626 | tmsg 'ld ,........' 627 | 628 | ; ld a,(nnnn) / ld (nnnn),a (44 cycles) 629 | lda: db 0ffh ; flag mask 630 | tstr 032h,msbtlo,msbthi,0,0fd68h,0f4ech,044a0h,0b543h,00653h,0cdbah,0d2h,04fh,01fd8h 631 | tstr 008h,0,0,0,0,0,0,0,0,0,0,0,0 ; (2 cycle) 632 | tstr 0,0,0,0,0ffh,0,0,0,0,0,0d7h,-1,0 ; (22 cycles) 633 | db 0c9h,026h,02dh,0e5h ; expected crc 634 | tmsg 'ld a,(nnnn) / ld (nnnn),a.....' 635 | 636 | ; ldd (1) (44 cycles) 637 | ldd1: db 0ffh ; flag mask 638 | tstr 0edh,0a8h,0,0,09852h,068fah,066a1h,msbt+3,msbt+1,1,0c1h,068h,020b7h 639 | tstr 0,010h,0,0,0,0,0,0,0,0,0,0,0 ; (2 cycles) 640 | tstr 0,0,0,0,-1,0,0,0,0,0,0d7h,0,0 ; (22 cycles) 641 | db 039h,0e2h,095h,0edh ; expected crc 642 | tmsg 'ldd (1)....................' 643 | 644 | ; ldd (2) (44 cycles) 645 | ldd2: db 0ffh ; flag mask 646 | tstr 0edh,0a8h,0,0,0f12eh,0eb2ah,0d5bah,msbt+3,msbt+1,2,047h,0ffh,0fbe4h 647 | tstr 0,010h,0,0,0,0,0,0,0,0,0,0,0 ; (2 cycles) 648 | tstr 0,0,0,0,-1,0,0,0,0,0,0d7h,0,0 ; (22 cycles) 649 | db 094h,0cbh,08fh,065h ; expected crc 650 | tmsg 'ldd (2)....................' 651 | 652 | ; ldi (1) (44 cycles) 653 | ldi1: db 0ffh ; flag mask 654 | tstr 0edh,0a0h,0,0,0fe30h,003cdh,06058h,msbt+2,msbt,1,004h,060h,02688h 655 | tstr 0,010h,0,0,0,0,0,0,0,0,0,0,0 ; (2 cycles) 656 | tstr 0,0,0,0,-1,0,0,0,0,0,0d7h,0,0 ; (22 cycles) 657 | db 05ah,094h,002h,055h ; expected crc 658 | tmsg 'ldi (1)....................' 659 | 660 | ; ldi (2) (44 cycles) 661 | ldi2: db 0ffh ; flag mask 662 | tstr 0edh,0a0h,0,0,04aceh,0c26eh,0b188h,msbt+2,msbt,2,014h,02dh,0a39fh 663 | tstr 0,010h,0,0,0,0,0,0,0,0,0,0,0 ; (2 cycles) 664 | tstr 0,0,0,0,-1,0,0,0,0,0,0d7h,0,0 ; (22 cycles) 665 | db 044h,0fch,062h,02ah ; expected crc 666 | tmsg 'ldi (2)....................' 667 | 668 | ; neg (16,384 cycles) 669 | negop: db 0ffh ; flag mask 670 | tstr 0edh,044h,0,0,038a2h,05f6bh,0d934h,057e4h,0d2d6h,04642h,043h,05ah,009cch 671 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,-1,0 ; (16,384 cycles) 672 | tstr 0,0,0,0,0,0,0,0,0,0,0,0,0 ; (1 cycle) 673 | db 0d6h,038h,0ddh,06ah ; expected crc 674 | tmsg 'neg...........................' 675 | 676 | ; (7168 cycles) 677 | rldop: db 0ffh ; flag mask 678 | tstr 0edh,067h,0,0,091cbh,0c48bh,0fa62h,msbt,0e720h,0b479h,040h,006h,08ae2h 679 | tstr 0,8,0,0,0ffh,0,0,0,0,0,0,0,0 ; (512 cycles) 680 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,-1,0 ; (14 cycles) 681 | db 01bh,0bch,005h,051h ; expected crc 682 | tmsg '.....................' 683 | 684 | ; (6144 cycles) 685 | rot8080: db 0ffh ; flag mask 686 | tstr 7,0,0,0,0cb92h,06d43h,00a90h,0c284h,00c53h,0f50eh,091h,0ebh,040fch 687 | tstr 018h,0,0,0,0,0,0,0,0,0,0,-1,0 ; (1024 cycles) 688 | tstr 0,0,0,0,0,0,0,0,0,0,0d7h,0,0 ; (6 cycles) 689 | db 09bh,0a3h,080h,07ch ; expected crc 690 | tmsg '...........' 691 | 692 | ; shift/rotate (+1) (416 cycles) 693 | rotxy: db 0ffh ; flag mask 694 | tstr 0ddh,0cbh,1,6,0ddafh,msbt-1,msbt-1,0ff3ch,0dbf6h,094f4h,082h,080h,061d9h 695 | tstr 020h,0,0,038h,0,0,0,0,0,0,080h,0,0 ; (32 cycles) 696 | tstr 0,0,0,0,0ffh,0,0,0,0,0,057h,0,0 ; (13 cycles) 697 | db 04ch,006h,053h,0f1h ; expected crc 698 | tmsg 'shf/rot (+1)...........' 699 | 700 | ; shift/rotate (6784 cycles) 701 | rotz80: db 0ffh ; flag mask 702 | tstr 0cbh,0,0,0,0ccebh,05d4ah,0e007h,msbt,01395h,030eeh,043h,078h,03dadh 703 | tstr 0,03fh,0,0,0,0,0,0,0,0,080h,0,0 ; (128 cycles) 704 | tstr 0,0,0,0,0ffh,0,0,0,-1,-1,057h,-1,0 ; (53 cycles) 705 | db 036h,0bch,01fh,0c3h ; expected crc 706 | tmsg 'shf/rot ..' 707 | 708 | ; n, (7936 cycles) 709 | srz80: db 0ffh ; flag mask 710 | tstr 0cbh,080h,0,0,02cd5h,097abh,039ffh,msbt,0d14bh,06ab2h,053h,027h,0b538h 711 | tstr 0,07fh,0,0,0,0,0,0,0,0,0,0,0 ; (128 cycles) 712 | tstr 0,0,0,0,0ffh,0,0,0,-1,-1,0d7h,-1,0 ; (62 cycles) 713 | db 077h,05eh,089h,075h ; expected crc 714 | tmsg ' n,.....' 715 | 716 | ; n,(+1) (1792 cycles) 717 | srzx: db 0ffh ; flag mask 718 | tstr 0ddh,0cbh,1,086h,0fb44h,msbt-1,msbt-1,0ba09h,068beh,032d8h,010h,05eh,0a867h 719 | tstr 020h,0,0,078h,0,0,0,0,0,0,0,0,0 ; (128 cycles) 720 | tstr 0,0,0,0,0ffh,0,0,0,0,0,0d7h,0,0 ;(14 cycles) 721 | db 0fah,09fh,051h,0d4h ; expected crc 722 | tmsg ' n,(+1).......' 723 | 724 | ; ld (+1), (1024 cycles) 725 | st8ix1: db 0ffh ; flag mask 726 | tstr 0ddh,070h,1,0,0270dh,msbt-1,msbt-1,0b73ah,0887bh,099eeh,086h,070h,0ca07h 727 | tstr 020h,003h,0,0,0,1,1,0,0,0,0,0,0 ; (32 cycles) 728 | tstr 0,0,0,0,0,0,0,0,-1,-1,0,0,0 ; (32 cycles) 729 | db 0dch,096h,020h,025h ; expected crc 730 | tmsg 'ld (+1),......' 731 | 732 | ; ld (+1), (256 cycles) 733 | st8ix2: db 0ffh ; flag mask 734 | tstr 0ddh,074h,1,0,0b664h,msbt-1,msbt-1,0e8ach,0b5f5h,0aafeh,012h,010h,09566h 735 | tstr 020h,001h,0,0,0,1,1,0,0,0,0,0,0 ; (16 cycles) 736 | tstr 0,0,0,0,0,0,0,-1,0,0,0,0,0 ; (32 cycles) 737 | db 027h,08ah,0c9h,0aeh ; expected crc 738 | tmsg 'ld (+1),..........' 739 | 740 | ; ld (+1),a (64 cycles) 741 | st8ix3: db 0ffh ; flag mask 742 | tstr 0ddh,077h,1,0,067afh,msbt-1,msbt-1,04f13h,00644h,0bcd7h,050h,0ach,05fafh 743 | tstr 020h,0,0,0,0,1,1,0,0,0,0,0,0 ; (8 cycles) 744 | tstr 0,0,0,0,0,0,0,0,0,0,0,-1,0 ; (8 cycles) 745 | db 0d1h,002h,02ah,013h ; expected crc 746 | tmsg 'ld (+1),a..............' 747 | 748 | ; ld (),a (96 cycles) 749 | stabd: db 0ffh ; flag mask 750 | tstr 2,0,0,0,00c3bh,0b592h,06cffh,0959eh,msbt,msbt+1,0c1h,021h,0bde7h 751 | tstr 018h,0,0,0,0,0,0,0,0,0,0,0,0 ; (4 cycles) 752 | tstr 0,0,0,0,-1,0,0,0,0,0,0,-1,0 ; (24 cycles) 753 | db 019h,0cah,0edh,02fh ; expected crc 754 | tmsg 'ld (),a................' 755 | 756 | ; start test pointed to by (hl) 757 | stt: push hl 758 | ld a,(hl) ; get pointer to test 759 | inc hl 760 | ld h,(hl) 761 | ld l,a 762 | ld a,(hl) ; flag mask 763 | ld (flgmsk+1),a 764 | inc hl 765 | push hl 766 | ld de,20 767 | add hl,de ; point to incmask 768 | ld de,counter 769 | call initmask 770 | pop hl 771 | push hl 772 | ld de,20+20 773 | add hl,de ; point to scanmask 774 | ld de,shifter 775 | call initmask 776 | ld hl,shifter 777 | ld (hl),1 ; first bit 778 | pop hl 779 | push hl 780 | ld de,iut ; copy initial instruction under test 781 | ld bc,4 782 | ldir 783 | ld de,msbt ; copy initial machine state 784 | ld bc,16 785 | ldir 786 | ld de,20+20+4 ; skip incmask, scanmask and expcrc 787 | add hl,de 788 | ex de,hl 789 | ld c,9 790 | call bdos ; show test name 791 | call initcrc ; initialise crc 792 | ; test loop 793 | tlp: ld a,(iut) 794 | cp 076h ; pragmatically avoid halt intructions 795 | jp z,tlp2 796 | and 0dfh 797 | cp 0ddh 798 | jp nz,tlp1 799 | ld a,(iut+1) 800 | cp 076h 801 | tlp1: call nz,test ; execute the test instruction 802 | tlp2: call count ; increment the counter 803 | call nz,shift ; shift the scan bit 804 | pop hl ; pointer to test case 805 | jp z,tlp3 ; done if shift returned NZ 806 | ld de,20+20+20 807 | add hl,de ; point to expected crc 808 | call cmpcrc 809 | ld de,okmsg 810 | jp z,tlpok 811 | push hl ; save pointer to crc 812 | ld hl,crcval 813 | ld de,ermsg1 ; jgh: swap crc= and expected= messages 814 | ld c,9 815 | call bdos 816 | call phex8 817 | ld de,ermsg2 818 | ld c,9 819 | call bdos 820 | pop hl ; get pointer to crc back 821 | call phex8 822 | ld de,crlf 823 | tlpok: ld c,9 824 | call bdos 825 | pop hl 826 | inc hl 827 | inc hl 828 | ret 829 | 830 | tlp3: push hl 831 | ld a,1 ; initialise count and shift scanners 832 | ld (cntbit),a 833 | ld (shfbit),a 834 | ld hl,counter 835 | ld (cntbyt),hl 836 | ld hl,shifter 837 | ld (shfbyt),hl 838 | 839 | ld b,4 ; bytes in iut field 840 | pop hl ; pointer to test case 841 | push hl 842 | ld de,iut 843 | call setup ; setup iut 844 | ld b,16 ; bytes in machine state 845 | ld de,msbt 846 | call setup ; setup machine state 847 | jp tlp 848 | 849 | ; set up a field of the test case 850 | ; b = number of bytes 851 | ; hl = pointer to base case 852 | ; de = destination 853 | setup: call subyte 854 | inc hl 855 | dec b 856 | jp nz,setup 857 | ret 858 | 859 | subyte: push bc 860 | push de 861 | push hl 862 | ld c,(hl) ; get base byte 863 | ld de,20 864 | add hl,de ; point to incmask 865 | ld a,(hl) 866 | cp 0 867 | jp z,subshf 868 | ld b,8 ; 8 bits 869 | subclp: rrca 870 | push af 871 | ld a,0 872 | call c,nxtcbit ; get next counter bit if mask bit was set 873 | xor c ; flip bit if counter bit was set 874 | rrca 875 | ld c,a 876 | pop af 877 | dec b 878 | jp nz,subclp 879 | ld b,8 880 | subshf: ld de,20 881 | add hl,de ; point to shift mask 882 | ld a,(hl) 883 | cp 0 884 | jp z,substr 885 | ld b,8 ; 8 bits 886 | sbshf1: rrca 887 | push af 888 | ld a,0 889 | call c,nxtsbit ; get next shifter bit if mask bit was set 890 | xor c ; flip bit if shifter bit was set 891 | rrca 892 | ld c,a 893 | pop af 894 | dec b 895 | jp nz,sbshf1 896 | substr: pop hl 897 | pop de 898 | ld a,c 899 | ld (de),a ; mangled byte to destination 900 | inc de 901 | pop bc 902 | ret 903 | 904 | ; get next counter bit in low bit of a 905 | cntbit: ds 1 906 | cntbyt: ds 2 907 | 908 | nxtcbit: push bc 909 | push hl 910 | ld hl,(cntbyt) 911 | ld b,(hl) 912 | ld hl,cntbit 913 | ld a,(hl) 914 | ld c,a 915 | rlca 916 | ld (hl),a 917 | cp 1 918 | jp nz,ncb1 919 | ld hl,(cntbyt) 920 | inc hl 921 | ld (cntbyt),hl 922 | ncb1: ld a,b 923 | and c 924 | pop hl 925 | pop bc 926 | ret z 927 | ld a,1 928 | ret 929 | 930 | ; get next shifter bit in low bit of a 931 | shfbit: ds 1 932 | shfbyt: ds 2 933 | 934 | nxtsbit: push bc 935 | push hl 936 | ld hl,(shfbyt) 937 | ld b,(hl) 938 | ld hl,shfbit 939 | ld a,(hl) 940 | ld c,a 941 | rlca 942 | ld (hl),a 943 | cp 1 944 | jp nz,nsb1 945 | ld hl,(shfbyt) 946 | inc hl 947 | ld (shfbyt),hl 948 | nsb1: ld a,b 949 | and c 950 | pop hl 951 | pop bc 952 | ret z 953 | ld a,1 954 | ret 955 | 956 | 957 | ; clear memory at hl, bc bytes 958 | clrmem: push af 959 | push bc 960 | push de 961 | push hl 962 | ld (hl),0 963 | ld d,h 964 | ld e,l 965 | inc de 966 | dec bc 967 | ldir 968 | pop hl 969 | pop de 970 | pop bc 971 | pop af 972 | ret 973 | 974 | ; initialise counter or shifter 975 | ; de = pointer to work area for counter or shifter 976 | ; hl = pointer to mask 977 | initmask: 978 | push de 979 | ex de,hl 980 | ld bc,20+20 981 | call clrmem ; clear work area 982 | ex de,hl 983 | ld b,20 ; byte counter 984 | ld c,1 ; first bit 985 | ld d,0 ; bit counter 986 | imlp: ld e,(hl) 987 | imlp1: ld a,e 988 | and c 989 | jp z,imlp2 990 | inc d 991 | imlp2: ld a,c 992 | rlca 993 | ld c,a 994 | cp 1 995 | jp nz,imlp1 996 | inc hl 997 | dec b 998 | jp nz,imlp 999 | ; got number of 1-bits in mask in reg d 1000 | ld a,d 1001 | and 0f8h 1002 | rrca 1003 | rrca 1004 | rrca ; divide by 8 (get byte offset) 1005 | ld l,a 1006 | ld h,0 1007 | ld a,d 1008 | and 7 ; bit offset 1009 | inc a 1010 | ld b,a 1011 | ld a,080h 1012 | imlp3: rlca 1013 | dec b 1014 | jp nz,imlp3 1015 | pop de 1016 | add hl,de 1017 | ld de,20 1018 | add hl,de 1019 | ld (hl),a 1020 | ret 1021 | 1022 | ; multi-byte counter 1023 | count: push bc 1024 | push de 1025 | push hl 1026 | ld hl,counter ; 20 byte counter starts here 1027 | ld de,20 ; somewhere in here is the stop bit 1028 | ex de,hl 1029 | add hl,de 1030 | ex de,hl 1031 | cntlp: inc (hl) 1032 | ld a,(hl) 1033 | cp 0 1034 | jp z,cntlp1 ; overflow to next byte 1035 | ld b,a 1036 | ld a,(de) 1037 | and b ; test for terminal value 1038 | jp z,cntend 1039 | ld (hl),0 ; reset to zero 1040 | cntend: pop bc 1041 | pop de 1042 | pop hl 1043 | ret 1044 | 1045 | cntlp1: inc hl 1046 | inc de 1047 | jp cntlp 1048 | 1049 | 1050 | ; multi-byte shifter 1051 | shift: push bc 1052 | push de 1053 | push hl 1054 | ld hl,shifter ; 20 byte shift register starts here 1055 | ld de,20 ; somewhere in here is the stop bit 1056 | ex de,hl 1057 | add hl,de 1058 | ex de,hl 1059 | shflp: ld a,(hl) 1060 | or a 1061 | jp z,shflp1 1062 | ld b,a 1063 | ld a,(de) 1064 | and b 1065 | jp nz,shlpe 1066 | ld a,b 1067 | rlca 1068 | cp 1 1069 | jp nz,shflp2 1070 | ld (hl),0 1071 | inc hl 1072 | inc de 1073 | shflp2: ld (hl),a 1074 | xor a ; set Z 1075 | shlpe: pop hl 1076 | pop de 1077 | pop bc 1078 | ret 1079 | shflp1: inc hl 1080 | inc de 1081 | jp shflp 1082 | 1083 | counter: ds 2*20 1084 | shifter: ds 2*20 1085 | 1086 | ; test harness 1087 | test: push af 1088 | push bc 1089 | push de 1090 | push hl 1091 | if 0 1092 | ld de,crlf 1093 | ld c,9 1094 | call bdos 1095 | ld hl,iut 1096 | ld b,4 1097 | call hexstr 1098 | ld e,' ' 1099 | ld c,2 1100 | call bdos 1101 | ld b,16 1102 | ld hl,msbt 1103 | call hexstr 1104 | endif 1105 | di ; disable interrupts 1106 | push iy ; jgh: Spectrum INTs change (IY+x) 1107 | ld (spsav),sp ; save stack pointer 1108 | ld sp,msbt+2 ; point to test-case machine state 1109 | pop iy ; and load all regs 1110 | pop ix 1111 | pop hl 1112 | pop de 1113 | pop bc 1114 | pop af 1115 | ld sp,(spbt) 1116 | iut: ds 4 ; max 4 byte instruction under test 1117 | ld (spat),sp ; save stack pointer 1118 | ld sp,spat 1119 | push af ; save other registers 1120 | push bc 1121 | push de 1122 | push hl 1123 | push ix 1124 | push iy 1125 | ld sp,(spsav) ; restore stack pointer 1126 | pop iy ; jgh: Restore Spectrum's IY 1127 | ei ; enable interrupts 1128 | ld hl,(msbt) ; copy memory operand 1129 | ld (msat),hl 1130 | ld hl,flgsat ; flags after test 1131 | ld a,(hl) 1132 | flgmsk: and 0d7h ; mask-out irrelevant bits (self-modified code!) 1133 | ld (hl),a 1134 | ld b,16 ; total of 16 bytes of state 1135 | ld de,msat 1136 | ld hl,crcval 1137 | tcrc: ld a,(de) 1138 | inc de 1139 | call updcrc ; accumulate crc of this test case 1140 | dec b 1141 | jp nz,tcrc 1142 | if 0 1143 | ld e,' ' 1144 | ld c,2 1145 | call bdos 1146 | ld hl,crcval 1147 | call phex8 1148 | ld de,crlf 1149 | ld c,9 1150 | call bdos 1151 | ld hl,msat 1152 | ld b,16 1153 | call hexstr 1154 | ld de,crlf 1155 | ld c,9 1156 | call bdos 1157 | endif 1158 | pop hl 1159 | pop de 1160 | pop bc 1161 | pop af 1162 | ret 1163 | 1164 | ; machine state after test 1165 | msat: ds 14 ; memop,iy,ix,hl,de,bc,af 1166 | spat: ds 2 ; stack pointer after test 1167 | ; ZMAC/MAXAM doesn't like ':' after label with EQUs 1168 | flgsat equ spat-2 ; flags 1169 | 1170 | spsav: ds 2 ; saved stack pointer 1171 | 1172 | ; display hex string (pointer in hl, byte count in b) 1173 | hexstr: ld a,(hl) 1174 | call phex2 1175 | inc hl 1176 | dec b 1177 | jp nz,hexstr 1178 | ret 1179 | 1180 | ; display hex 1181 | ; display the big-endian 32-bit value pointed to by hl 1182 | phex8: push af 1183 | push bc 1184 | push hl 1185 | ld b,4 1186 | ph8lp: ld a,(hl) 1187 | call phex2 1188 | inc hl 1189 | dec b 1190 | jp nz,ph8lp 1191 | pop hl 1192 | pop bc 1193 | pop af 1194 | ret 1195 | 1196 | ; display byte in a 1197 | phex2: push af 1198 | rrca 1199 | rrca 1200 | rrca 1201 | rrca 1202 | call phex1 1203 | pop af 1204 | ; fall through 1205 | 1206 | ; display low nibble in a 1207 | phex1: push af 1208 | push bc 1209 | push de 1210 | push hl 1211 | and 0fh 1212 | cp 10 1213 | jp c,ph11 1214 | add a,'a'-'9'-1 1215 | ph11: add a,'0' 1216 | ld e,a 1217 | ld c,2 1218 | call bdos 1219 | pop hl 1220 | pop de 1221 | pop bc 1222 | pop af 1223 | ret 1224 | 1225 | bdos: push af 1226 | push bc 1227 | push de 1228 | push hl 1229 | push iy ; jgh: save IY 1230 | ld iy,5c3ah ; jgh: set IY for Spectrum 1231 | ld (iy+82),255 ; jgh: reset SCRCNT 1232 | ei 1233 | ; Accept call 2 (print char) and call 9 (print string) 1234 | ; Ignore all others 1235 | ld a,c 1236 | cp 2 1237 | jr z,prchar 1238 | cp 9 1239 | jr z,prstring 1240 | bdosdone: 1241 | di ; ensure INTs remain disabled 1242 | pop iy ; jgh: restore IY 1243 | pop hl 1244 | pop de 1245 | pop bc 1246 | pop af 1247 | ret 1248 | 1249 | ; Pass bdos calls to Spectrum system 1250 | prchar: 1251 | ld a,e ; get char 1252 | cp 10 ; ignore LF as CR auto-LFs 1253 | jr z,bdosdone 1254 | rst 010h 1255 | jr bdosdone 1256 | 1257 | prstring: 1258 | ld a,(de) 1259 | cp '$' 1260 | jr z,bdosdone 1261 | cp 10 1262 | call nz,010h 1263 | inc de 1264 | jr prstring 1265 | 1266 | 1267 | msg1: db 'Z80all instruction exerciser',10,13,10,13,'$' 1268 | msg2: db 'Tests complete$' 1269 | okmsg: db 'OK',10,13,'$' 1270 | ermsg1: db ' CRC:$' ; was ERROR: 1271 | ermsg2: db ' expected:$' 1272 | crlf: db 10,13,'$' 1273 | 1274 | ; compare crc 1275 | ; hl points to value to compare to crcval 1276 | cmpcrc: push bc 1277 | push de 1278 | push hl 1279 | ld de,crcval 1280 | ld b,4 1281 | cclp: ld a,(de) 1282 | cp (hl) 1283 | jp nz,cce 1284 | inc hl 1285 | inc de 1286 | dec b 1287 | jp nz,cclp 1288 | cce: pop hl 1289 | pop de 1290 | pop bc 1291 | ret 1292 | 1293 | ; 32-bit crc routine 1294 | ; entry: a contains next byte, hl points to crc 1295 | ; exit: crc updated 1296 | updcrc: push af 1297 | push bc 1298 | push de 1299 | push hl 1300 | push hl 1301 | ld de,3 1302 | add hl,de ; point to low byte of old crc 1303 | xor (hl) ; xor with new byte 1304 | ld l,a 1305 | ld h,0 1306 | add hl,hl ; use result as index into table of 4 byte entries 1307 | add hl,hl 1308 | ex de,hl 1309 | ld hl,crctab 1310 | add hl,de ; point to selected entry in crctab 1311 | ex de,hl 1312 | pop hl 1313 | ld bc,4 ; c = byte count, b = accumulator 1314 | crclp: ld a,(de) 1315 | xor b 1316 | ld b,(hl) 1317 | ld (hl),a 1318 | inc de 1319 | inc hl 1320 | dec c 1321 | jp nz,crclp 1322 | if 0 1323 | ld hl,crcval 1324 | call phex8 1325 | ld de,crlf 1326 | ld c,9 1327 | call bdos 1328 | endif 1329 | pop hl 1330 | pop de 1331 | pop bc 1332 | pop af 1333 | ret 1334 | 1335 | initcrc:push af 1336 | push bc 1337 | push hl 1338 | ld hl,crcval 1339 | ld a,0ffh 1340 | ld b,4 1341 | icrclp: ld (hl),a 1342 | inc hl 1343 | dec b 1344 | jp nz,icrclp 1345 | pop hl 1346 | pop bc 1347 | pop af 1348 | ret 1349 | 1350 | crcval ds 4 1351 | 1352 | crctab: db 000h,000h,000h,000h 1353 | db 077h,007h,030h,096h 1354 | db 0eeh,00eh,061h,02ch 1355 | db 099h,009h,051h,0bah 1356 | db 007h,06dh,0c4h,019h 1357 | db 070h,06ah,0f4h,08fh 1358 | db 0e9h,063h,0a5h,035h 1359 | db 09eh,064h,095h,0a3h 1360 | db 00eh,0dbh,088h,032h 1361 | db 079h,0dch,0b8h,0a4h 1362 | db 0e0h,0d5h,0e9h,01eh 1363 | db 097h,0d2h,0d9h,088h 1364 | db 009h,0b6h,04ch,02bh 1365 | db 07eh,0b1h,07ch,0bdh 1366 | db 0e7h,0b8h,02dh,007h 1367 | db 090h,0bfh,01dh,091h 1368 | db 01dh,0b7h,010h,064h 1369 | db 06ah,0b0h,020h,0f2h 1370 | db 0f3h,0b9h,071h,048h 1371 | db 084h,0beh,041h,0deh 1372 | db 01ah,0dah,0d4h,07dh 1373 | db 06dh,0ddh,0e4h,0ebh 1374 | db 0f4h,0d4h,0b5h,051h 1375 | db 083h,0d3h,085h,0c7h 1376 | db 013h,06ch,098h,056h 1377 | db 064h,06bh,0a8h,0c0h 1378 | db 0fdh,062h,0f9h,07ah 1379 | db 08ah,065h,0c9h,0ech 1380 | db 014h,001h,05ch,04fh 1381 | db 063h,006h,06ch,0d9h 1382 | db 0fah,00fh,03dh,063h 1383 | db 08dh,008h,00dh,0f5h 1384 | db 03bh,06eh,020h,0c8h 1385 | db 04ch,069h,010h,05eh 1386 | db 0d5h,060h,041h,0e4h 1387 | db 0a2h,067h,071h,072h 1388 | db 03ch,003h,0e4h,0d1h 1389 | db 04bh,004h,0d4h,047h 1390 | db 0d2h,00dh,085h,0fdh 1391 | db 0a5h,00ah,0b5h,06bh 1392 | db 035h,0b5h,0a8h,0fah 1393 | db 042h,0b2h,098h,06ch 1394 | db 0dbh,0bbh,0c9h,0d6h 1395 | db 0ach,0bch,0f9h,040h 1396 | db 032h,0d8h,06ch,0e3h 1397 | db 045h,0dfh,05ch,075h 1398 | db 0dch,0d6h,00dh,0cfh 1399 | db 0abh,0d1h,03dh,059h 1400 | db 026h,0d9h,030h,0ach 1401 | db 051h,0deh,000h,03ah 1402 | db 0c8h,0d7h,051h,080h 1403 | db 0bfh,0d0h,061h,016h 1404 | db 021h,0b4h,0f4h,0b5h 1405 | db 056h,0b3h,0c4h,023h 1406 | db 0cfh,0bah,095h,099h 1407 | db 0b8h,0bdh,0a5h,00fh 1408 | db 028h,002h,0b8h,09eh 1409 | db 05fh,005h,088h,008h 1410 | db 0c6h,00ch,0d9h,0b2h 1411 | db 0b1h,00bh,0e9h,024h 1412 | db 02fh,06fh,07ch,087h 1413 | db 058h,068h,04ch,011h 1414 | db 0c1h,061h,01dh,0abh 1415 | db 0b6h,066h,02dh,03dh 1416 | db 076h,0dch,041h,090h 1417 | db 001h,0dbh,071h,006h 1418 | db 098h,0d2h,020h,0bch 1419 | db 0efh,0d5h,010h,02ah 1420 | db 071h,0b1h,085h,089h 1421 | db 006h,0b6h,0b5h,01fh 1422 | db 09fh,0bfh,0e4h,0a5h 1423 | db 0e8h,0b8h,0d4h,033h 1424 | db 078h,007h,0c9h,0a2h 1425 | db 00fh,000h,0f9h,034h 1426 | db 096h,009h,0a8h,08eh 1427 | db 0e1h,00eh,098h,018h 1428 | db 07fh,06ah,00dh,0bbh 1429 | db 008h,06dh,03dh,02dh 1430 | db 091h,064h,06ch,097h 1431 | db 0e6h,063h,05ch,001h 1432 | db 06bh,06bh,051h,0f4h 1433 | db 01ch,06ch,061h,062h 1434 | db 085h,065h,030h,0d8h 1435 | db 0f2h,062h,000h,04eh 1436 | db 06ch,006h,095h,0edh 1437 | db 01bh,001h,0a5h,07bh 1438 | db 082h,008h,0f4h,0c1h 1439 | db 0f5h,00fh,0c4h,057h 1440 | db 065h,0b0h,0d9h,0c6h 1441 | db 012h,0b7h,0e9h,050h 1442 | db 08bh,0beh,0b8h,0eah 1443 | db 0fch,0b9h,088h,07ch 1444 | db 062h,0ddh,01dh,0dfh 1445 | db 015h,0dah,02dh,049h 1446 | db 08ch,0d3h,07ch,0f3h 1447 | db 0fbh,0d4h,04ch,065h 1448 | db 04dh,0b2h,061h,058h 1449 | db 03ah,0b5h,051h,0ceh 1450 | db 0a3h,0bch,000h,074h 1451 | db 0d4h,0bbh,030h,0e2h 1452 | db 04ah,0dfh,0a5h,041h 1453 | db 03dh,0d8h,095h,0d7h 1454 | db 0a4h,0d1h,0c4h,06dh 1455 | db 0d3h,0d6h,0f4h,0fbh 1456 | db 043h,069h,0e9h,06ah 1457 | db 034h,06eh,0d9h,0fch 1458 | db 0adh,067h,088h,046h 1459 | db 0dah,060h,0b8h,0d0h 1460 | db 044h,004h,02dh,073h 1461 | db 033h,003h,01dh,0e5h 1462 | db 0aah,00ah,04ch,05fh 1463 | db 0ddh,00dh,07ch,0c9h 1464 | db 050h,005h,071h,03ch 1465 | db 027h,002h,041h,0aah 1466 | db 0beh,00bh,010h,010h 1467 | db 0c9h,00ch,020h,086h 1468 | db 057h,068h,0b5h,025h 1469 | db 020h,06fh,085h,0b3h 1470 | db 0b9h,066h,0d4h,009h 1471 | db 0ceh,061h,0e4h,09fh 1472 | db 05eh,0deh,0f9h,00eh 1473 | db 029h,0d9h,0c9h,098h 1474 | db 0b0h,0d0h,098h,022h 1475 | db 0c7h,0d7h,0a8h,0b4h 1476 | db 059h,0b3h,03dh,017h 1477 | db 02eh,0b4h,00dh,081h 1478 | db 0b7h,0bdh,05ch,03bh 1479 | db 0c0h,0bah,06ch,0adh 1480 | db 0edh,0b8h,083h,020h 1481 | db 09ah,0bfh,0b3h,0b6h 1482 | db 003h,0b6h,0e2h,00ch 1483 | db 074h,0b1h,0d2h,09ah 1484 | db 0eah,0d5h,047h,039h 1485 | db 09dh,0d2h,077h,0afh 1486 | db 004h,0dbh,026h,015h 1487 | db 073h,0dch,016h,083h 1488 | db 0e3h,063h,00bh,012h 1489 | db 094h,064h,03bh,084h 1490 | db 00dh,06dh,06ah,03eh 1491 | db 07ah,06ah,05ah,0a8h 1492 | db 0e4h,00eh,0cfh,00bh 1493 | db 093h,009h,0ffh,09dh 1494 | db 00ah,000h,0aeh,027h 1495 | db 07dh,007h,09eh,0b1h 1496 | db 0f0h,00fh,093h,044h 1497 | db 087h,008h,0a3h,0d2h 1498 | db 01eh,001h,0f2h,068h 1499 | db 069h,006h,0c2h,0feh 1500 | db 0f7h,062h,057h,05dh 1501 | db 080h,065h,067h,0cbh 1502 | db 019h,06ch,036h,071h 1503 | db 06eh,06bh,006h,0e7h 1504 | db 0feh,0d4h,01bh,076h 1505 | db 089h,0d3h,02bh,0e0h 1506 | db 010h,0dah,07ah,05ah 1507 | db 067h,0ddh,04ah,0cch 1508 | db 0f9h,0b9h,0dfh,06fh 1509 | db 08eh,0beh,0efh,0f9h 1510 | db 017h,0b7h,0beh,043h 1511 | db 060h,0b0h,08eh,0d5h 1512 | db 0d6h,0d6h,0a3h,0e8h 1513 | db 0a1h,0d1h,093h,07eh 1514 | db 038h,0d8h,0c2h,0c4h 1515 | db 04fh,0dfh,0f2h,052h 1516 | db 0d1h,0bbh,067h,0f1h 1517 | db 0a6h,0bch,057h,067h 1518 | db 03fh,0b5h,006h,0ddh 1519 | db 048h,0b2h,036h,04bh 1520 | db 0d8h,00dh,02bh,0dah 1521 | db 0afh,00ah,01bh,04ch 1522 | db 036h,003h,04ah,0f6h 1523 | db 041h,004h,07ah,060h 1524 | db 0dfh,060h,0efh,0c3h 1525 | db 0a8h,067h,0dfh,055h 1526 | db 031h,06eh,08eh,0efh 1527 | db 046h,069h,0beh,079h 1528 | db 0cbh,061h,0b3h,08ch 1529 | db 0bch,066h,083h,01ah 1530 | db 025h,06fh,0d2h,0a0h 1531 | db 052h,068h,0e2h,036h 1532 | db 0cch,00ch,077h,095h 1533 | db 0bbh,00bh,047h,003h 1534 | db 022h,002h,016h,0b9h 1535 | db 055h,005h,026h,02fh 1536 | db 0c5h,0bah,03bh,0beh 1537 | db 0b2h,0bdh,00bh,028h 1538 | db 02bh,0b4h,05ah,092h 1539 | db 05ch,0b3h,06ah,004h 1540 | db 0c2h,0d7h,0ffh,0a7h 1541 | db 0b5h,0d0h,0cfh,031h 1542 | db 02ch,0d9h,09eh,08bh 1543 | db 05bh,0deh,0aeh,01dh 1544 | db 09bh,064h,0c2h,0b0h 1545 | db 0ech,063h,0f2h,026h 1546 | db 075h,06ah,0a3h,09ch 1547 | db 002h,06dh,093h,00ah 1548 | db 09ch,009h,006h,0a9h 1549 | db 0ebh,00eh,036h,03fh 1550 | db 072h,007h,067h,085h 1551 | db 005h,000h,057h,013h 1552 | db 095h,0bfh,04ah,082h 1553 | db 0e2h,0b8h,07ah,014h 1554 | db 07bh,0b1h,02bh,0aeh 1555 | db 00ch,0b6h,01bh,038h 1556 | db 092h,0d2h,08eh,09bh 1557 | db 0e5h,0d5h,0beh,00dh 1558 | db 07ch,0dch,0efh,0b7h 1559 | db 00bh,0dbh,0dfh,021h 1560 | db 086h,0d3h,0d2h,0d4h 1561 | db 0f1h,0d4h,0e2h,042h 1562 | db 068h,0ddh,0b3h,0f8h 1563 | db 01fh,0dah,083h,06eh 1564 | db 081h,0beh,016h,0cdh 1565 | db 0f6h,0b9h,026h,05bh 1566 | db 06fh,0b0h,077h,0e1h 1567 | db 018h,0b7h,047h,077h 1568 | db 088h,008h,05ah,0e6h 1569 | db 0ffh,00fh,06ah,070h 1570 | db 066h,006h,03bh,0cah 1571 | db 011h,001h,00bh,05ch 1572 | db 08fh,065h,09eh,0ffh 1573 | db 0f8h,062h,0aeh,069h 1574 | db 061h,06bh,0ffh,0d3h 1575 | db 016h,06ch,0cfh,045h 1576 | db 0a0h,00ah,0e2h,078h 1577 | db 0d7h,00dh,0d2h,0eeh 1578 | db 04eh,004h,083h,054h 1579 | db 039h,003h,0b3h,0c2h 1580 | db 0a7h,067h,026h,061h 1581 | db 0d0h,060h,016h,0f7h 1582 | db 049h,069h,047h,04dh 1583 | db 03eh,06eh,077h,0dbh 1584 | db 0aeh,0d1h,06ah,04ah 1585 | db 0d9h,0d6h,05ah,0dch 1586 | db 040h,0dfh,00bh,066h 1587 | db 037h,0d8h,03bh,0f0h 1588 | db 0a9h,0bch,0aeh,053h 1589 | db 0deh,0bbh,09eh,0c5h 1590 | db 047h,0b2h,0cfh,07fh 1591 | db 030h,0b5h,0ffh,0e9h 1592 | db 0bdh,0bdh,0f2h,01ch 1593 | db 0cah,0bah,0c2h,08ah 1594 | db 053h,0b3h,093h,030h 1595 | db 024h,0b4h,0a3h,0a6h 1596 | db 0bah,0d0h,036h,005h 1597 | db 0cdh,0d7h,006h,093h 1598 | db 054h,0deh,057h,029h 1599 | db 023h,0d9h,067h,0bfh 1600 | db 0b3h,066h,07ah,02eh 1601 | db 0c4h,061h,04ah,0b8h 1602 | db 05dh,068h,01bh,002h 1603 | db 02ah,06fh,02bh,094h 1604 | db 0b4h,00bh,0beh,037h 1605 | db 0c3h,00ch,08eh,0a1h 1606 | db 05ah,005h,0dfh,01bh 1607 | db 02dh,002h,0efh,08dh 1608 | 1609 | --------------------------------------------------------------------------------