diff --git a/emulator/core/src/memory.rs b/emulator/core/src/memory.rs index ccd3d91..fb70813 100644 --- a/emulator/core/src/memory.rs +++ b/emulator/core/src/memory.rs @@ -6,7 +6,7 @@ use std::cell::RefCell; use std::fmt::Write; use crate::info; -use crate::error::{Error, EmulatorErrorKind}; +use crate::error::Error; use crate::clock::ClockTime; use crate::devices::{Address, Addressable, Transmutable, TransmutableBox, read_beu16}; @@ -275,7 +275,6 @@ pub struct BusPort { offset: Address, address_mask: Address, data_width: u8, - error_on_alignment: bool, subdevice: Rc>, } @@ -285,7 +284,6 @@ impl BusPort { offset, address_mask: (1 << address_bits) - 1, data_width: data_bits / 8, - error_on_alignment: false, subdevice: bus, } } @@ -294,10 +292,12 @@ impl BusPort { self.subdevice.borrow_mut().dump_memory(clock, self.offset + (addr & self.address_mask), count) } + #[inline] pub fn address_mask(&self) -> Address { self.address_mask } + #[inline] pub fn data_width(&self) -> u8 { self.data_width } @@ -309,10 +309,6 @@ impl Addressable for BusPort { } fn read(&mut self, clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { - if self.error_on_alignment && addr % self.data_width as Address != 0 { - return Err(Error::emulator(EmulatorErrorKind::MemoryAlignment, format!("misaligned memory access at {:x}", addr))); - } - let addr = self.offset + (addr & self.address_mask); let mut subdevice = self.subdevice.borrow_mut(); for i in (0..data.len()).step_by(self.data_width as usize) { @@ -324,10 +320,6 @@ impl Addressable for BusPort { } fn write(&mut self, clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { - if self.error_on_alignment && addr % self.data_width as Address != 0 { - return Err(Error::emulator(EmulatorErrorKind::MemoryAlignment, format!("misaligned memory access at {:x}", addr))); - } - let addr = self.offset + (addr & self.address_mask); let mut subdevice = self.subdevice.borrow_mut(); for i in (0..data.len()).step_by(self.data_width as usize) { diff --git a/emulator/cpus/m68k/src/debugger.rs b/emulator/cpus/m68k/src/debugger.rs index 77d5fb5..a68fe07 100644 --- a/emulator/cpus/m68k/src/debugger.rs +++ b/emulator/cpus/m68k/src/debugger.rs @@ -1,5 +1,5 @@ -use moa_core::{System, Error, ClockTime, Address, Addressable, Debuggable}; +use moa_core::{System, Error, Address, Addressable, Debuggable}; use super::state::M68k; use super::decode::M68kDecoder; @@ -50,15 +50,15 @@ impl Debuggable for M68k { } } - fn print_current_step(&mut self, system: &System) -> Result<(), Error> { - self.decoder.decode_at(&mut self.port, system.clock, self.state.pc)?; + fn print_current_step(&mut self, _system: &System) -> Result<(), Error> { + self.decoder.decode_at(&mut self.port, true, self.state.pc)?; self.decoder.dump_decoded(&mut self.port); - self.dump_state(system.clock); + self.dump_state(); Ok(()) } fn print_disassembly(&mut self, addr: Address, count: usize) { - let mut decoder = M68kDecoder::new(self.cputype, ClockTime::START, 0); + let mut decoder = M68kDecoder::new(self.cputype, true, 0); decoder.dump_disassembly(&mut self.port, addr as u32, count as u32); } @@ -67,7 +67,7 @@ impl Debuggable for M68k { "ds" | "stack" | "dumpstack" => { println!("Stack:"); for addr in &self.debugger.stack_tracer.calls { - println!(" {:08x}", self.port.read_beu32(system.clock, *addr as Address)?); + println!(" {:08x}", self.port.port.read_beu32(system.clock, *addr as Address)?); } }, "so" | "stepout" => { diff --git a/emulator/cpus/m68k/src/decode.rs b/emulator/cpus/m68k/src/decode.rs index d31f7bb..6a603df 100644 --- a/emulator/cpus/m68k/src/decode.rs +++ b/emulator/cpus/m68k/src/decode.rs @@ -1,8 +1,9 @@ -use moa_core::{Error, ClockTime, Address, Addressable}; +use moa_core::{Error, Address, Addressable}; -use super::state::{M68kType, Exceptions}; -use super::instructions::{ +use crate::state::{M68kType, Exceptions}; +use crate::memory::M68kBusPort; +use crate::instructions::{ Size, Sign, Direction, @@ -39,7 +40,7 @@ const OPCG_FLINE: u8 = 0xF; #[derive(Clone)] pub struct M68kDecoder { pub cputype: M68kType, - pub clock: ClockTime, + pub is_supervisor: bool, pub start: u32, pub end: u32, pub instruction_word: u16, @@ -47,10 +48,10 @@ pub struct M68kDecoder { } impl M68kDecoder { - pub fn new(cputype: M68kType, clock: ClockTime, start: u32) -> M68kDecoder { + pub fn new(cputype: M68kType, is_supervisor: bool, start: u32) -> M68kDecoder { M68kDecoder { cputype, - clock, + is_supervisor, start, end: start, instruction_word: 0, @@ -59,19 +60,19 @@ impl M68kDecoder { } #[inline(always)] - pub fn init(&mut self, clock: ClockTime, start: u32) { - self.clock = clock; + pub fn init(&mut self, is_supervisor: bool, start: u32) { + self.is_supervisor = is_supervisor; self.start = start; self.end = start; } - pub fn decode_at(&mut self, memory: &mut dyn Addressable, clock: ClockTime, start: u32) -> Result<(), Error> { - self.init(clock, start); - self.instruction = self.decode_one(memory)?; + pub fn decode_at(&mut self, memory: &mut M68kBusPort, is_supervisor: bool, start: u32) -> Result<(), Error> { + self.init(is_supervisor, start); + self.instruction = self.decode_next(memory)?; Ok(()) } - pub fn decode_one(&mut self, memory: &mut dyn Addressable) -> Result { + pub fn decode_next(&mut self, memory: &mut M68kBusPort) -> Result { let ins = self.read_instruction_word(memory)?; self.instruction_word = ins; @@ -97,7 +98,7 @@ impl M68kDecoder { } #[inline] - fn decode_group_bit_ops(&mut self, memory: &mut dyn Addressable, ins: u16) -> Result { + fn decode_group_bit_ops(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result { let optype = (ins & 0x0F00) >> 8; if (ins & 0x13F) == 0x03C { @@ -172,14 +173,14 @@ impl M68kDecoder { } #[inline] - fn decode_group_move_byte(&mut self, memory: &mut dyn Addressable, ins: u16) -> Result { + fn decode_group_move_byte(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result { let src = self.decode_lower_effective_address(memory, ins, Some(Size::Byte))?; let dest = self.decode_upper_effective_address(memory, ins, Some(Size::Byte))?; Ok(Instruction::MOVE(src, dest, Size::Byte)) } #[inline] - fn decode_group_move_long(&mut self, memory: &mut dyn Addressable, ins: u16) -> Result { + fn decode_group_move_long(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result { let src = self.decode_lower_effective_address(memory, ins, Some(Size::Long))?; let dest = self.decode_upper_effective_address(memory, ins, Some(Size::Long))?; if let Target::DirectAReg(reg) = dest { @@ -190,7 +191,7 @@ impl M68kDecoder { } #[inline] - fn decode_group_move_word(&mut self, memory: &mut dyn Addressable, ins: u16) -> Result { + fn decode_group_move_word(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result { let src = self.decode_lower_effective_address(memory, ins, Some(Size::Word))?; let dest = self.decode_upper_effective_address(memory, ins, Some(Size::Word))?; if let Target::DirectAReg(reg) = dest { @@ -201,7 +202,7 @@ impl M68kDecoder { } #[inline] - fn decode_group_misc(&mut self, memory: &mut dyn Addressable, ins: u16) -> Result { + fn decode_group_misc(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result { let ins_0f00 = ins & 0xF00; let ins_00f0 = ins & 0x0F0; @@ -372,7 +373,7 @@ impl M68kDecoder { } #[inline] - fn decode_group_addq_subq(&mut self, memory: &mut dyn Addressable, ins: u16) -> Result { + fn decode_group_addq_subq(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result { match get_size(ins) { Some(size) => { let target = self.decode_lower_effective_address(memory, ins, Some(size))?; @@ -410,7 +411,7 @@ impl M68kDecoder { } #[inline] - fn decode_group_branch(&mut self, memory: &mut dyn Addressable, ins: u16) -> Result { + fn decode_group_branch(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result { let mut disp = ((ins & 0xFF) as i8) as i32; if disp == 0 { disp = (self.read_instruction_word(memory)? as i16) as i32; @@ -426,7 +427,7 @@ impl M68kDecoder { } #[inline] - fn decode_group_moveq(&mut self, _memory: &mut dyn Addressable, ins: u16) -> Result { + fn decode_group_moveq(&mut self, _memory: &mut M68kBusPort, ins: u16) -> Result { if (ins & 0x0100) != 0 { return Err(Error::processor(Exceptions::IllegalInstruction as u32)); } @@ -436,7 +437,7 @@ impl M68kDecoder { } #[inline] - fn decode_group_div_or(&mut self, memory: &mut dyn Addressable, ins: u16) -> Result { + fn decode_group_div_or(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result { let size = get_size(ins); if (ins & 0x1F0) == 0x100 { @@ -460,7 +461,7 @@ impl M68kDecoder { } #[inline] - fn decode_group_sub(&mut self, memory: &mut dyn Addressable, ins: u16) -> Result { + fn decode_group_sub(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result { let reg = get_high_reg(ins); let dir = (ins & 0x0100) >> 8; let size = get_size(ins); @@ -491,7 +492,7 @@ impl M68kDecoder { } #[inline] - fn decode_group_cmp_eor(&mut self, memory: &mut dyn Addressable, ins: u16) -> Result { + fn decode_group_cmp_eor(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result { let reg = get_high_reg(ins); let optype = (ins & 0x0100) >> 8; let size = get_size(ins); @@ -518,7 +519,7 @@ impl M68kDecoder { } #[inline] - fn decode_group_mul_and(&mut self, memory: &mut dyn Addressable, ins: u16) -> Result { + fn decode_group_mul_and(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result { let size = get_size(ins); if (ins & 0b0001_1111_0000) == 0b0001_0000_0000 { @@ -551,7 +552,7 @@ impl M68kDecoder { } #[inline] - fn decode_group_add(&mut self, memory: &mut dyn Addressable, ins: u16) -> Result { + fn decode_group_add(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result { let reg = get_high_reg(ins); let dir = (ins & 0x0100) >> 8; let size = get_size(ins); @@ -581,7 +582,7 @@ impl M68kDecoder { } } - fn decode_group_shift(&mut self, memory: &mut dyn Addressable, ins: u16) -> Result { + fn decode_group_shift(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result { match get_size(ins) { Some(size) => { let target = Target::DirectDReg(get_low_reg(ins)); @@ -667,31 +668,31 @@ impl M68kDecoder { } } - fn read_instruction_word(&mut self, memory: &mut dyn Addressable) -> Result { - let word = memory.read_beu16(self.clock, self.end as Address)?; + fn read_instruction_word(&mut self, memory: &mut M68kBusPort) -> Result { + let word = memory.read_instruction_word(self.is_supervisor, self.end)?; self.end += 2; Ok(word) } - fn read_instruction_long(&mut self, memory: &mut dyn Addressable) -> Result { - let word = memory.read_beu32(self.clock, self.end as Address)?; + fn read_instruction_long(&mut self, memory: &mut M68kBusPort) -> Result { + let word = memory.read_instruction_long(self.is_supervisor, self.end)?; self.end += 4; Ok(word) } - fn decode_lower_effective_address(&mut self, memory: &mut dyn Addressable, ins: u16, size: Option) -> Result { + fn decode_lower_effective_address(&mut self, memory: &mut M68kBusPort, ins: u16, size: Option) -> Result { let reg = get_low_reg(ins); let mode = get_low_mode(ins); self.get_mode_as_target(memory, mode, reg, size) } - fn decode_upper_effective_address(&mut self, memory: &mut dyn Addressable, ins: u16, size: Option) -> Result { + fn decode_upper_effective_address(&mut self, memory: &mut M68kBusPort, ins: u16, size: Option) -> Result { let reg = get_high_reg(ins); let mode = get_high_mode(ins); self.get_mode_as_target(memory, mode, reg, size) } - fn get_extension_displacement(&mut self, memory: &mut dyn Addressable, select: u16) -> Result { + fn get_extension_displacement(&mut self, memory: &mut M68kBusPort, select: u16) -> Result { let result = match select { 0b00 | 0b01 => 0, 0b10 => sign_extend_to_long(self.read_instruction_word(memory)? as u32, Size::Word), @@ -701,7 +702,7 @@ impl M68kDecoder { Ok(result) } - fn decode_extension_word(&mut self, memory: &mut dyn Addressable, areg: Option) -> Result { + fn decode_extension_word(&mut self, memory: &mut M68kBusPort, areg: Option) -> Result { let brief_extension = self.read_instruction_word(memory)?; let use_brief = (brief_extension & 0x0100) == 0; @@ -754,7 +755,7 @@ impl M68kDecoder { } } - pub(super) fn get_mode_as_target(&mut self, memory: &mut dyn Addressable, mode: u8, reg: u8, size: Option) -> Result { + pub(super) fn get_mode_as_target(&mut self, memory: &mut M68kBusPort, mode: u8, reg: u8, size: Option) -> Result { let value = match mode { 0b000 => Target::DirectDReg(reg), 0b001 => Target::DirectAReg(reg), @@ -801,10 +802,10 @@ impl M68kDecoder { Ok(value) } - pub fn dump_disassembly(&mut self, memory: &mut dyn Addressable, start: u32, length: u32) { + pub fn dump_disassembly(&mut self, memory: &mut M68kBusPort, start: u32, length: u32) { let mut next = start; while next < (start + length) { - match self.decode_at(memory, self.clock, next) { + match self.decode_at(memory, self.is_supervisor, next) { Ok(()) => { self.dump_decoded(memory); next = self.end; @@ -813,7 +814,7 @@ impl M68kDecoder { println!("{:?}", err); match err { Error { native, .. } if native == Exceptions::IllegalInstruction as u32 => { - println!(" at {:08x}: {:04x}", self.start, memory.read_beu16(self.clock, self.start as Address).unwrap()); + println!(" at {:08x}: {:04x}", self.start, memory.port.read_beu16(memory.current_clock, self.start as Address).unwrap()); }, _ => { }, } @@ -823,10 +824,10 @@ impl M68kDecoder { } } - pub fn dump_decoded(&mut self, memory: &mut dyn Addressable) { + pub fn dump_decoded(&mut self, memory: &mut M68kBusPort) { let ins_data: Result = (0..((self.end - self.start) / 2)).map(|offset| - Ok(format!("{:04x} ", memory.read_beu16(self.clock, (self.start + (offset * 2)) as Address).unwrap())) + Ok(format!("{:04x} ", memory.port.read_beu16(memory.current_clock, (self.start + (offset * 2)) as Address).unwrap())) ).collect(); println!("{:#010x}: {}\n\t{}\n", self.start, ins_data.unwrap(), self.instruction); } diff --git a/emulator/cpus/m68k/src/execute.rs b/emulator/cpus/m68k/src/execute.rs index 2f92a8c..3987fbc 100644 --- a/emulator/cpus/m68k/src/execute.rs +++ b/emulator/cpus/m68k/src/execute.rs @@ -1,8 +1,11 @@ use moa_core::debug; -use moa_core::{System, Error, ErrorType, ClockDuration, Address, Steppable, Interruptable, Addressable, Debuggable, Transmutable}; +use moa_core::{System, Error, ErrorType, ClockTime, ClockDuration, Address, Steppable, Interruptable, Addressable, Debuggable, Transmutable}; -use crate::state::{M68k, M68kType, Status, Flags, Exceptions, InterruptPriority, FunctionCode, MemType, MemAccess}; +use crate::state::{M68k, M68kType, ClockCycles, Status, Flags, Exceptions, InterruptPriority}; +use crate::memory::{MemType, MemAccess}; +use crate::decode::M68kDecoder; +use crate::timing::M68kInstructionTiming; use crate::instructions::{ Register, Size, @@ -30,11 +33,12 @@ pub enum Used { impl Steppable for M68k { fn step(&mut self, system: &System) -> Result { - self.step_internal(system) + let clocks = self.step_internal(system)?; + Ok(self.frequency.period_duration() * clocks as u64) } - fn on_error(&mut self, system: &System) { - self.dump_state(system.clock); + fn on_error(&mut self, _system: &System) { + self.dump_state(); } } @@ -56,15 +60,11 @@ impl Transmutable for M68k { impl M68k { - #[allow(dead_code)] - pub fn is_running(&self) -> bool { - self.state.status != Status::Stopped - } - - pub fn step_internal(&mut self, system: &System) -> Result { - self.current_clock = system.clock; + pub fn step_internal(&mut self, system: &System) -> Result { + //self.current_clock = system.clock; + self.init_cycle(system.clock); match self.state.status { - Status::Init => self.init(), + Status::Init => self.reset_cpu(), Status::Stopped => Err(Error::new("CPU stopped")), Status::Running => { match self.cycle_one(system) { @@ -73,7 +73,7 @@ impl M68k { // TODO match arm conditional is temporary: illegal instructions generate a top level error in order to debug and fix issues with decode //Err(Error { err: ErrorType::Processor, native, .. }) if native != Exceptions::IllegalInstruction as u32 => { self.exception(native as u8, false)?; - Ok(self.frequency.period_duration() * 4) + Ok(4) }, Err(err) => Err(err), } @@ -81,20 +81,27 @@ impl M68k { } } - pub fn init(&mut self) -> Result { - self.state.ssp = self.port.read_beu32(self.current_clock, 0)?; - self.state.pc = self.port.read_beu32(self.current_clock, 4)?; - self.state.status = Status::Running; - Ok(self.frequency.period_duration() * 16) + pub fn init_cycle(&mut self, clock: ClockTime) { + self.current_clock = clock; + self.decoder = M68kDecoder::new(self.cputype, self.is_supervisor(), self.state.pc); + self.timing = M68kInstructionTiming::new(self.cputype, self.port.data_width()); + self.port.init_cycle(clock); } - pub fn cycle_one(&mut self, system: &System) -> Result { + pub fn reset_cpu(&mut self) -> Result { + self.state.ssp = self.get_address_sized(0, Size::Long)?; + self.state.pc = self.get_address_sized(4, Size::Long)?; + self.state.status = Status::Running; + Ok(16) + } + + pub fn cycle_one(&mut self, system: &System) -> Result { self.decode_next()?; self.execute_current()?; self.check_pending_interrupts(system)?; self.check_breakpoints(system); - Ok(self.frequency.period_duration() * self.timing.calculate_clocks(false, 1) as u64) + Ok(self.timing.calculate_clocks(false, 1)) } pub fn check_pending_interrupts(&mut self, system: &System) -> Result<(), Error> { @@ -144,9 +151,9 @@ impl M68k { pub fn setup_group0_exception(&mut self, number: u8) -> Result<(), Error> { let sr = self.state.sr; let ins_word = self.decoder.instruction_word; - let extra_code = self.state.request.get_type_code(); - let fault_size = self.state.request.size.in_bytes(); - let fault_address = self.state.request.address; + let extra_code = self.port.request.get_type_code(); + let fault_size = self.port.request.size.in_bytes(); + let fault_address = self.port.request.address; // Changes to the flags must happen after the previous value has been pushed to the stack self.set_flag(Flags::Supervisor, true); @@ -164,7 +171,7 @@ impl M68k { self.push_word((ins_word & 0xFFF0) | extra_code)?; let vector = self.state.vbr + offset as u32; - let addr = self.port.read_beu32(self.current_clock, vector as Address)?; + let addr = self.get_address_sized(vector as Address, Size::Long)?; self.set_pc(addr)?; Ok(()) @@ -172,7 +179,7 @@ impl M68k { pub fn setup_normal_exception(&mut self, number: u8, is_interrupt: bool) -> Result<(), Error> { let sr = self.state.sr; - self.state.request.i_n_bit = true; + self.port.request.i_n_bit = true; // Changes to the flags must happen after the previous value has been pushed to the stack self.set_flag(Flags::Supervisor, true); @@ -189,7 +196,7 @@ impl M68k { self.push_word(sr)?; let vector = self.state.vbr + offset as u32; - let addr = self.port.read_beu32(self.current_clock, vector as Address)?; + let addr = self.get_address_sized(vector as Address, Size::Long)?; self.set_pc(addr)?; Ok(()) @@ -198,8 +205,8 @@ impl M68k { pub fn decode_next(&mut self) -> Result<(), Error> { self.timing.reset(); - self.start_instruction_request(self.state.pc)?; - self.decoder.decode_at(&mut self.port, self.current_clock, self.state.pc)?; + let is_supervisor = self.is_supervisor(); + self.decoder.decode_at(&mut self.port, is_supervisor, self.state.pc)?; self.timing.add_instruction(&self.decoder.instruction); @@ -281,7 +288,7 @@ impl M68k { Instruction::PEA(target) => self.execute_pea(target), Instruction::RESET => self.execute_reset(), Instruction::ROL(count, target, size) => self.execute_rol(count, target, size), - Instruction::ROR(count, target, size) => self.execute_rol(count, target, size), + Instruction::ROR(count, target, size) => self.execute_ror(count, target, size), Instruction::ROXL(count, target, size) => self.execute_roxl(count, target, size), Instruction::ROXR(count, target, size) => self.execute_roxr(count, target, size), Instruction::RTE => self.execute_rte(), @@ -400,7 +407,7 @@ impl M68k { let mut pair = (value, false); let mut previous_msb = get_msb(pair.0, size); for _ in 0..count { - pair = shift_left(pair.0, size, true); + pair = shift_left(pair.0, size); if get_msb(pair.0, size) != previous_msb { overflow = true; } @@ -798,7 +805,7 @@ impl M68k { let count = self.get_target_value(count, size, Used::Once)? % 64; let mut pair = (self.get_target_value(target, size, Used::Twice)?, false); for _ in 0..count { - pair = shift_left(pair.0, size, false); + pair = shift_left(pair.0, size); } self.set_target_value(target, pair.0, size, Used::Twice)?; @@ -989,8 +996,8 @@ impl M68k { let mut shift = (size.in_bits() as i32) - 8; let mut addr = (*self.get_a_reg_mut(areg)).wrapping_add_signed(offset as i32) as Address; while shift >= 0 { - let byte = (self.state.d_reg[dreg as usize] >> shift) as u8; - self.port.write_u8(self.current_clock, addr, byte)?; + let byte = self.state.d_reg[dreg as usize] >> shift; + self.set_address_sized(addr, byte, Size::Byte)?; addr += 2; shift -= 8; } @@ -999,7 +1006,7 @@ impl M68k { let mut shift = (size.in_bits() as i32) - 8; let mut addr = (*self.get_a_reg_mut(areg)).wrapping_add_signed(offset as i32) as Address; while shift >= 0 { - let byte = self.port.read_u8(self.current_clock, addr)?; + let byte = self.get_address_sized(addr, Size::Byte)?; self.state.d_reg[dreg as usize] |= (byte as u32) << shift; addr += 2; shift -= 8; @@ -1506,60 +1513,24 @@ impl M68k { } fn get_address_sized(&mut self, addr: Address, size: Size) -> Result { - self.start_request(addr as u32, size, MemAccess::Read, MemType::Data, false)?; - match size { - Size::Byte => self.port.read_u8(self.current_clock, addr).map(|value| value as u32), - Size::Word => self.port.read_beu16(self.current_clock, addr).map(|value| value as u32), - Size::Long => self.port.read_beu32(self.current_clock, addr), - } + self.port.read_data_sized(self.is_supervisor(), addr, size) } fn set_address_sized(&mut self, addr: Address, value: u32, size: Size) -> Result<(), Error> { - self.start_request(addr as u32, size, MemAccess::Write, MemType::Data, false)?; - match size { - Size::Byte => self.port.write_u8(self.current_clock, addr, value as u8), - Size::Word => self.port.write_beu16(self.current_clock, addr, value as u16), - Size::Long => self.port.write_beu32(self.current_clock, addr, value), - } - } - - fn start_instruction_request(&mut self, addr: u32) -> Result { - self.state.request.i_n_bit = false; - self.state.request.code = FunctionCode::program(self.state.sr); - self.state.request.access = MemAccess::Read; - self.state.request.address = addr; - - validate_address(addr) - } - - fn start_request(&mut self, addr: u32, size: Size, access: MemAccess, mtype: MemType, i_n_bit: bool) -> Result { - self.state.request.i_n_bit = i_n_bit; - self.state.request.code = match mtype { - MemType::Program => FunctionCode::program(self.state.sr), - MemType::Data => FunctionCode::data(self.state.sr), - }; - - self.state.request.access = access; - self.state.request.address = addr; - - if size == Size::Byte { - Ok(addr) - } else { - validate_address(addr) - } + self.port.write_data_sized(self.is_supervisor(), addr, value, size) } fn push_word(&mut self, value: u16) -> Result<(), Error> { *self.get_stack_pointer_mut() -= 2; let addr = *self.get_stack_pointer_mut(); - self.start_request(addr, Size::Word, MemAccess::Write, MemType::Data, false)?; - self.port.write_beu16(self.current_clock, addr as Address, value) + self.port.start_request(self.is_supervisor(), addr, Size::Word, MemAccess::Write, MemType::Data, false)?; + self.port.port.write_beu16(self.current_clock, addr as Address, value) } fn pop_word(&mut self) -> Result { let addr = *self.get_stack_pointer_mut(); - let value = self.port.read_beu16(self.current_clock, addr as Address)?; - self.start_request(addr, Size::Word, MemAccess::Read, MemType::Data, false)?; + self.port.start_request(self.is_supervisor(), addr, Size::Word, MemAccess::Read, MemType::Data, false)?; + let value = self.port.port.read_beu16(self.current_clock, addr as Address)?; *self.get_stack_pointer_mut() += 2; Ok(value) } @@ -1567,21 +1538,21 @@ impl M68k { fn push_long(&mut self, value: u32) -> Result<(), Error> { *self.get_stack_pointer_mut() -= 4; let addr = *self.get_stack_pointer_mut(); - self.start_request(addr, Size::Long, MemAccess::Write, MemType::Data, false)?; - self.port.write_beu32(self.current_clock, addr as Address, value) + self.port.start_request(self.is_supervisor(), addr, Size::Long, MemAccess::Write, MemType::Data, false)?; + self.port.port.write_beu32(self.current_clock, addr as Address, value) } fn pop_long(&mut self) -> Result { let addr = *self.get_stack_pointer_mut(); - let value = self.port.read_beu32(self.current_clock, addr as Address)?; - self.start_request(addr, Size::Long, MemAccess::Read, MemType::Data, false)?; + self.port.start_request(self.is_supervisor(), addr, Size::Long, MemAccess::Read, MemType::Data, false)?; + let value = self.port.port.read_beu32(self.current_clock, addr as Address)?; *self.get_stack_pointer_mut() += 4; Ok(value) } fn set_pc(&mut self, value: u32) -> Result<(), Error> { self.state.pc = value; - self.start_request(self.state.pc, Size::Word, MemAccess::Read, MemType::Program, true)?; + self.port.start_request(self.is_supervisor(), self.state.pc, Size::Word, MemAccess::Read, MemType::Program, true)?; Ok(()) } @@ -1751,14 +1722,6 @@ impl M68k { } } -fn validate_address(addr: u32) -> Result { - if addr & 0x1 == 0 { - Ok(addr) - } else { - Err(Error::processor(Exceptions::AddressError as u32)) - } -} - fn overflowing_add_sized(operand1: u32, operand2: u32, size: Size) -> (u32, bool) { match size { Size::Byte => { @@ -1804,7 +1767,7 @@ fn overflowing_sub_signed_sized(operand1: u32, operand2: u32, size: Size) -> (u3 } } -fn shift_left(value: u32, size: Size, arithmetic: bool) -> (u32, bool) { +fn shift_left(value: u32, size: Size) -> (u32, bool) { let bit = get_msb(value, size); match size { Size::Byte => (((value as u8) << 1) as u32, bit), diff --git a/emulator/cpus/m68k/src/lib.rs b/emulator/cpus/m68k/src/lib.rs index 033ccac..39e56ff 100644 --- a/emulator/cpus/m68k/src/lib.rs +++ b/emulator/cpus/m68k/src/lib.rs @@ -5,6 +5,7 @@ pub mod decode; pub mod execute; pub mod debugger; pub mod instructions; +pub mod memory; pub mod timing; pub mod tests; diff --git a/emulator/cpus/m68k/src/memory.rs b/emulator/cpus/m68k/src/memory.rs new file mode 100644 index 0000000..8704865 --- /dev/null +++ b/emulator/cpus/m68k/src/memory.rs @@ -0,0 +1,300 @@ + +use moa_core::{Error, ClockTime, Address, Addressable, BusPort}; + +use crate::state::{M68k, Exceptions}; +use crate::instructions::{Target, Size}; + +#[repr(u8)] +#[allow(dead_code)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum FunctionCode { + Reserved0 = 0, + UserData = 1, + UserProgram = 2, + Reserved3 = 3, + Reserved4 = 4, + SupervisorData = 5, + SupervisorProgram = 6, + CpuSpace = 7, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum MemType { + Program, + Data, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum MemAccess { + Read, + Write, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +// TODO change to MemoryState or RequestState or AccessState or maybe even BusState +pub struct MemoryRequest { + pub i_n_bit: bool, + pub access: MemAccess, + pub code: FunctionCode, + pub size: Size, + pub address: u32, +} + +impl FunctionCode { + pub fn program(is_supervisor: bool) -> Self { + if is_supervisor { + FunctionCode::SupervisorProgram + } else { + FunctionCode::UserProgram + } + } + + pub fn data(is_supervisor: bool) -> Self { + if is_supervisor { + FunctionCode::SupervisorData + } else { + FunctionCode::UserData + } + } +} + +impl Default for MemoryRequest { + fn default() -> Self { + Self { + i_n_bit: false, + access: MemAccess::Read, + code: FunctionCode::Reserved0, + size: Size::Word, + address: 0, + } + } +} + +impl MemoryRequest { + pub fn get_type_code(&self) -> u16 { + let ins = match self.i_n_bit { + false => 0x0000, + true => 0x0008, + }; + + let rw = match self.access { + MemAccess::Write => 0x0000, + MemAccess::Read => 0x0010, + }; + + ins | rw | (self.code as u16) + } +} + +#[derive(Clone)] +pub struct M68kBusPort { + pub port: BusPort, + pub request: MemoryRequest, + pub cycle_start_clock: ClockTime, + pub current_clock: ClockTime, +} + + +impl M68k { + // TODO should some of the ones from execute.rs move here +} + +impl M68kBusPort { + pub fn new(port: BusPort) -> Self { + Self { + port, + request: Default::default(), + cycle_start_clock: ClockTime::START, + current_clock: ClockTime::START, + } + } + + pub fn data_width(&self) -> u8 { + self.port.data_width() + } + + pub fn init_cycle(&mut self, clock: ClockTime) { + self.cycle_start_clock = clock; + self.current_clock = clock; + } + + pub(crate) fn read_instruction_word(&mut self, is_supervisor: bool, addr: u32) -> Result { + self.start_instruction_request(is_supervisor, addr)?; + self.port.read_beu16(self.current_clock, addr as Address) + } + + pub(crate) fn read_instruction_long(&mut self, is_supervisor: bool, addr: u32) -> Result { + self.start_instruction_request(is_supervisor, addr)?; + self.port.read_beu32(self.current_clock, addr as Address) + } + + pub(crate) fn read_data_sized(&mut self, is_supervisor: bool, addr: Address, size: Size) -> Result { + self.start_request(is_supervisor, addr as u32, size, MemAccess::Read, MemType::Data, false)?; + match size { + Size::Byte => self.port.read_u8(self.current_clock, addr).map(|value| value as u32), + Size::Word => self.port.read_beu16(self.current_clock, addr).map(|value| value as u32), + Size::Long => self.port.read_beu32(self.current_clock, addr), + } + } + + pub(crate) fn write_data_sized(&mut self, is_supervisor: bool, addr: Address, value: u32, size: Size) -> Result<(), Error> { + self.start_request(is_supervisor, addr as u32, size, MemAccess::Write, MemType::Data, false)?; + match size { + Size::Byte => self.port.write_u8(self.current_clock, addr, value as u8), + Size::Word => self.port.write_beu16(self.current_clock, addr, value as u16), + Size::Long => self.port.write_beu32(self.current_clock, addr, value), + } + } + + pub(crate) fn start_instruction_request(&mut self, is_supervisor: bool, addr: u32) -> Result { + self.request.i_n_bit = false; + self.request.code = FunctionCode::program(is_supervisor); + self.request.access = MemAccess::Read; + self.request.address = addr; + + validate_address(addr) + } + + pub(crate) fn start_request(&mut self, is_supervisor: bool, addr: u32, size: Size, access: MemAccess, mtype: MemType, i_n_bit: bool) -> Result { + self.request.i_n_bit = i_n_bit; + self.request.code = match mtype { + MemType::Program => FunctionCode::program(is_supervisor), + MemType::Data => FunctionCode::data(is_supervisor), + }; + + self.request.access = access; + self.request.address = addr; + + if size == Size::Byte { + Ok(addr) + } else { + validate_address(addr) + } + } + + pub(crate) fn dump_memory(&mut self, addr: u32, length: usize) { + self.port.dump_memory(self.current_clock, addr as Address, length as u64); + } +} + +fn validate_address(addr: u32) -> Result { + if addr & 0x1 == 0 { + Ok(addr) + } else { + Err(Error::processor(Exceptions::AddressError as u32)) + } +} + + + +/* +pub(crate) struct TargetAccess { + must_read: bool, + must_write: bool, + size: Size, + target: Target, +} + +impl TargetAccess { + pub(crate) fn read_only(size: Size) -> Self { + + } + + pub(crate) fn read_update(size: Size) -> Self { + + } + + pub(crate) fn updated_only(size: Size) -> Self { + + } + + pub(crate) fn get(&mut self, cpu: &M68k) -> Result { + + } + + pub(crate) fn set(&mut self, cpu: &M68k, value: u32) -> Result<(), Error> { + + } + + pub(crate) fn complete(&self) -> Result { + + } +} + + +impl Target { + pub(crate) fn read_once(self, size: Size) -> ReadOnceAccess { + ReadOnceAccess { + size, + target: self, + accessed: false, + } + } + + pub(crate) fn read_update(self, size: Size) -> ReadUpdateAccess { + ReadUpdateAccess { + size, + target: self, + } + } + + pub(crate) fn write_once(self, size: Size) -> WriteOnceAccess { + WriteOnceAccess { + size, + target: self, + } + } +} + + + +pub(crate) struct ReadOnceAccess { + size: Size, + target: Target, + accessed: bool, +} + +impl ReadOnceAccess { + pub(crate) fn get(&mut self, cpu: &M68k) -> Result { + + } + + pub(crate) fn complete(&self) -> Result { + + } +} + +pub(crate) struct ReadUpdateAccess { + size: Size, + target: Target, +} + +impl ReadUpdateAccess { + pub(crate) fn get(&mut self, cpu: &M68k) -> Result { + + } + + pub(crate) fn set(&mut self, cpu: &M68k, value: u32) -> Result<(), Error> { + + } + + pub(crate) fn complete(&self) -> Result { + + } +} + +pub(crate) struct WriteOnceAccess { + size: Size, + target: Target, +} + +impl WriteOnceAccess { + pub(crate) fn set(&mut self, cpu: &M68k, value: u32) -> Result<(), Error> { + + } + + pub(crate) fn complete(&self) -> Result { + + } +} +*/ diff --git a/emulator/cpus/m68k/src/state.rs b/emulator/cpus/m68k/src/state.rs index 03b7e58..fa0535d 100644 --- a/emulator/cpus/m68k/src/state.rs +++ b/emulator/cpus/m68k/src/state.rs @@ -1,12 +1,14 @@ -use moa_core::{ClockTime, Address, BusPort, Frequency}; +use moa_core::{ClockTime, BusPort, Frequency}; -use crate::instructions::Size; use crate::decode::M68kDecoder; use crate::debugger::M68kDebugger; +use crate::memory::M68kBusPort; use crate::timing::M68kInstructionTiming; +pub type ClockCycles = u16; + #[allow(dead_code)] #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum M68kType { @@ -49,20 +51,6 @@ pub enum Exceptions { LineFEmulator = 11, } -#[repr(u8)] -#[allow(dead_code)] -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum FunctionCode { - Reserved0 = 0, - UserData = 1, - UserProgram = 2, - Reserved3 = 3, - Reserved4 = 4, - SupervisorData = 5, - SupervisorProgram = 6, - CpuSpace = 7, -} - #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Status { Init, @@ -83,31 +71,9 @@ pub enum InterruptPriority { Level7 = 7, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum MemType { - Program, - Data, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum MemAccess { - Read, - Write, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct MemoryRequest { - pub i_n_bit: bool, - pub access: MemAccess, - pub code: FunctionCode, - pub size: Size, - pub address: u32, -} - #[derive(Clone, Debug, PartialEq, Eq)] pub struct M68kState { pub status: Status, - pub request: MemoryRequest, pub current_ipl: InterruptPriority, pub pending_ipl: InterruptPriority, @@ -129,7 +95,7 @@ pub struct M68k { pub decoder: M68kDecoder, pub timing: M68kInstructionTiming, pub debugger: M68kDebugger, - pub port: BusPort, + pub port: M68kBusPort, pub current_clock: ClockTime, } @@ -137,7 +103,6 @@ impl Default for M68kState { fn default() -> M68kState { M68kState { status: Status::Init, - request: MemoryRequest::default(), current_ipl: InterruptPriority::NoInterrupt, pending_ipl: InterruptPriority::NoInterrupt, @@ -159,23 +124,15 @@ impl M68k { cputype, frequency, state: M68kState::default(), - decoder: M68kDecoder::new(cputype, ClockTime::START, 0), + decoder: M68kDecoder::new(cputype, true, 0), timing: M68kInstructionTiming::new(cputype, port.data_width()), debugger: M68kDebugger::default(), - port, + port: M68kBusPort::new(port), current_clock: ClockTime::START, } } - #[allow(dead_code)] - pub fn reset(&mut self) { - self.state = M68kState::default(); - self.decoder = M68kDecoder::new(self.cputype, ClockTime::START, 0); - self.timing = M68kInstructionTiming::new(self.cputype, self.port.data_width()); - self.debugger = M68kDebugger::default(); - } - - pub fn dump_state(&mut self, clock: ClockTime) { + pub fn dump_state(&mut self) { println!("Status: {:?}", self.state.status); println!("PC: {:#010x}", self.state.pc); println!("SR: {:#06x}", self.state.sr); @@ -187,7 +144,7 @@ impl M68k { println!("Current Instruction: {:#010x} {:?}", self.decoder.start, self.decoder.instruction); println!(); - self.port.dump_memory(clock, self.state.ssp as Address, 0x40); + self.port.dump_memory(self.state.ssp, 0x40); println!(); } } @@ -207,49 +164,3 @@ impl InterruptPriority { } } -impl FunctionCode { - pub fn program(sr: u16) -> Self { - if sr & Flags::Supervisor as u16 != 0 { - FunctionCode::SupervisorProgram - } else { - FunctionCode::UserProgram - } - } - - pub fn data(sr: u16) -> Self { - if sr & Flags::Supervisor as u16 != 0 { - FunctionCode::SupervisorData - } else { - FunctionCode::UserData - } - } -} - -impl Default for MemoryRequest { - fn default() -> Self { - Self { - i_n_bit: false, - access: MemAccess::Read, - code: FunctionCode::Reserved0, - size: Size::Word, - address: 0, - } - } -} - -impl MemoryRequest { - pub fn get_type_code(&self) -> u16 { - let ins = match self.i_n_bit { - false => 0x0000, - true => 0x0008, - }; - - let rw = match self.access { - MemAccess::Write => 0x0000, - MemAccess::Read => 0x0010, - }; - - ins | rw | (self.code as u16) - } -} - diff --git a/emulator/cpus/m68k/src/timing.rs b/emulator/cpus/m68k/src/timing.rs index f1ec942..1c45c9c 100644 --- a/emulator/cpus/m68k/src/timing.rs +++ b/emulator/cpus/m68k/src/timing.rs @@ -1,5 +1,6 @@ use crate::M68kType; +use crate::state::ClockCycles; use crate::instructions::{Size, Sign, Direction, Target, Instruction}; @@ -337,12 +338,12 @@ impl M68kInstructionTiming { self.add_internal(4) } - pub fn calculate_clocks(&self, branched: bool, reps: u16) -> u16 { + pub fn calculate_clocks(&self, branched: bool, reps: u16) -> ClockCycles { //println!("{:?}", self); - (self.accesses as u16 * 4) - + self.internal as u16 - + (if branched { self.on_branch as u16 } else { 0 }) - + self.per_rep as u16 * reps + (self.accesses as ClockCycles * 4) + + self.internal as ClockCycles + + (if branched { self.on_branch as ClockCycles } else { 0 }) + + self.per_rep as ClockCycles * reps } #[inline(always)] diff --git a/emulator/cpus/m68k/tests/decode_tests.rs b/emulator/cpus/m68k/tests/decode_tests.rs index 312f2ee..15af6c0 100644 --- a/emulator/cpus/m68k/tests/decode_tests.rs +++ b/emulator/cpus/m68k/tests/decode_tests.rs @@ -46,10 +46,10 @@ const DECODE_TESTS: &'static [TestCase] = &[ TestCase { cpu: M68kType::MC68000, data: &[0x0148, 0x1234], ins: Some(Instruction::MOVEP(0, 0, 0x1234, Size::Long, Direction::FromTarget)) }, TestCase { cpu: M68kType::MC68000, data: &[0x0188, 0x1234], ins: Some(Instruction::MOVEP(0, 0, 0x1234, Size::Word, Direction::ToTarget)) }, TestCase { cpu: M68kType::MC68000, data: &[0x01C8, 0x1234], ins: Some(Instruction::MOVEP(0, 0, 0x1234, Size::Long, Direction::ToTarget)) }, - TestCase { cpu: M68kType::MC68000, data: &[0xE300], ins: Some(Instruction::ASd(Target::Immediate(1), Target::DirectDReg(0), Size::Byte, ShiftDirection::Left)) }, - TestCase { cpu: M68kType::MC68000, data: &[0xE200], ins: Some(Instruction::ASd(Target::Immediate(1), Target::DirectDReg(0), Size::Byte, ShiftDirection::Right)) }, - TestCase { cpu: M68kType::MC68000, data: &[0xE318], ins: Some(Instruction::ROd(Target::Immediate(1), Target::DirectDReg(0), Size::Byte, ShiftDirection::Left)) }, - TestCase { cpu: M68kType::MC68000, data: &[0xE218], ins: Some(Instruction::ROd(Target::Immediate(1), Target::DirectDReg(0), Size::Byte, ShiftDirection::Right)) }, + TestCase { cpu: M68kType::MC68000, data: &[0xE300], ins: Some(Instruction::ASL(Target::Immediate(1), Target::DirectDReg(0), Size::Byte)) }, + TestCase { cpu: M68kType::MC68000, data: &[0xE200], ins: Some(Instruction::ASR(Target::Immediate(1), Target::DirectDReg(0), Size::Byte)) }, + TestCase { cpu: M68kType::MC68000, data: &[0xE318], ins: Some(Instruction::ROL(Target::Immediate(1), Target::DirectDReg(0), Size::Byte)) }, + TestCase { cpu: M68kType::MC68000, data: &[0xE218], ins: Some(Instruction::ROR(Target::Immediate(1), Target::DirectDReg(0), Size::Byte)) }, TestCase { cpu: M68kType::MC68000, data: &[0xA000], ins: Some(Instruction::UnimplementedA(0xA000)) }, TestCase { cpu: M68kType::MC68000, data: &[0xFFFF], ins: Some(Instruction::UnimplementedF(0xFFFF)) }, diff --git a/emulator/cpus/m68k/tests/musashi_timing_tests.rs b/emulator/cpus/m68k/tests/musashi_timing_tests.rs index 87a25c9..43ac9f1 100644 --- a/emulator/cpus/m68k/tests/musashi_timing_tests.rs +++ b/emulator/cpus/m68k/tests/musashi_timing_tests.rs @@ -26,7 +26,7 @@ fn init_decode_test(cputype: M68kType) -> (M68k, System) { BusPort::new(0, 24, 16, system.bus.clone()) }; let mut cpu = M68k::new(cputype, Frequency::from_mhz(10), port); - cpu.init().unwrap(); + cpu.init_cycle().unwrap(); assert_eq!(cpu.state.pc, INIT_ADDR as u32); assert_eq!(cpu.state.ssp, INIT_STACK as u32); diff --git a/emulator/peripherals/yamaha/src/ym2612.rs b/emulator/peripherals/yamaha/src/ym2612.rs index 7814c15..c4b09a3 100644 --- a/emulator/peripherals/yamaha/src/ym2612.rs +++ b/emulator/peripherals/yamaha/src/ym2612.rs @@ -829,7 +829,7 @@ impl Ym2612 { pub fn set_register(&mut self, clock: ClockTime, bank: u8, reg: u8, data: u8) { // Keep a copy for debugging purposes, and if the original values are needed self.registers[bank as usize * 256 + reg as usize] = data; - //println!("set {:x} to {:x}", bank as usize * 256 + reg as usize, data); + println!("set {:x} to {:x}", bank as usize * 256 + reg as usize, data); //warn!("{}: set reg {}{:x} to {:x}", DEV_NAME, bank, reg, data); match reg { diff --git a/tests/harte_tests/src/main.rs b/tests/harte_tests/src/main.rs index ed5e3ee..e5aec5a 100644 --- a/tests/harte_tests/src/main.rs +++ b/tests/harte_tests/src/main.rs @@ -225,7 +225,7 @@ fn assert_state(cpu: &M68k, system: &System, expected: &TestState) -> Result<(), assert_value(cpu.state.sr, expected.sr, "sr")?; assert_value(cpu.state.pc, expected.pc, "pc")?; - let addr_mask = cpu.port.address_mask(); + let addr_mask = cpu.port.port.address_mask(); // Load instructions into memory for (i, ins) in expected.prefetch.iter().enumerate() { @@ -268,8 +268,8 @@ fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> { if args.debug { case.dump(); println!(""); - initial_cpu.dump_state(system.clock); - cpu.dump_state(system.clock); + initial_cpu.dump_state(); + cpu.dump_state(); } println!("FAILED: {}", err.msg); } diff --git a/tests/rad_tests/run_all.sh b/tests/rad_tests/run_all.sh index b2ea62e..8f4650d 100755 --- a/tests/rad_tests/run_all.sh +++ b/tests/rad_tests/run_all.sh @@ -7,5 +7,5 @@ RESULTS=latest.txt cd $LOCATION echo "Last run on $DATE at commit $COMMIT" | tee $RESULTS echo "" | tee -a $RESULTS - cargo run -- -q --testsuite "../jsmoo/misc/tests/GeneratedTests/z80/v1/" --check-timings | tee -a $RESULTS + cargo run -- -q --testsuite "../jsmoo/misc/tests/GeneratedTests/z80/v1/" --check-undocumented | tee -a $RESULTS } diff --git a/todo.txt b/todo.txt index 2f20555..d3fb087 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,8 @@ +* I want to make some kind of memory transaction object that does everything in a contained but logical way, including handling exception + information needed about the last access, and adjusting the pre/post inc/dec + +* the debug dump things should not used the clocked addressing, but use a debugging mode thing of some kind so as not to influence the sim state * the way you're doing debugging is so bad, and something's broken with the Z80 * debugger should return a breakpoint error to the frontend, so that the frontend still runs, instead of suspending the current execution * can you make the debugger workable in the web ui in some way? So you can have a debug window open while playing the game or something