Refactored memory access a bit to try to isolate it

This commit is contained in:
transistor 2023-05-21 23:14:26 -07:00
parent d87319b605
commit 8c1a89a1fe
14 changed files with 434 additions and 261 deletions

View File

@ -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<RefCell<Bus>>,
}
@ -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) {

View File

@ -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" => {

View File

@ -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<Instruction, Error> {
pub fn decode_next(&mut self, memory: &mut M68kBusPort) -> Result<Instruction, Error> {
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<Instruction, Error> {
fn decode_group_bit_ops(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result<Instruction, Error> {
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<Instruction, Error> {
fn decode_group_move_byte(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result<Instruction, Error> {
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<Instruction, Error> {
fn decode_group_move_long(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result<Instruction, Error> {
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<Instruction, Error> {
fn decode_group_move_word(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result<Instruction, Error> {
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<Instruction, Error> {
fn decode_group_misc(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result<Instruction, Error> {
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<Instruction, Error> {
fn decode_group_addq_subq(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result<Instruction, Error> {
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<Instruction, Error> {
fn decode_group_branch(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result<Instruction, Error> {
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<Instruction, Error> {
fn decode_group_moveq(&mut self, _memory: &mut M68kBusPort, ins: u16) -> Result<Instruction, Error> {
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<Instruction, Error> {
fn decode_group_div_or(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result<Instruction, Error> {
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<Instruction, Error> {
fn decode_group_sub(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result<Instruction, Error> {
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<Instruction, Error> {
fn decode_group_cmp_eor(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result<Instruction, Error> {
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<Instruction, Error> {
fn decode_group_mul_and(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result<Instruction, Error> {
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<Instruction, Error> {
fn decode_group_add(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result<Instruction, Error> {
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<Instruction, Error> {
fn decode_group_shift(&mut self, memory: &mut M68kBusPort, ins: u16) -> Result<Instruction, Error> {
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<u16, Error> {
let word = memory.read_beu16(self.clock, self.end as Address)?;
fn read_instruction_word(&mut self, memory: &mut M68kBusPort) -> Result<u16, Error> {
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<u32, Error> {
let word = memory.read_beu32(self.clock, self.end as Address)?;
fn read_instruction_long(&mut self, memory: &mut M68kBusPort) -> Result<u32, Error> {
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<Size>) -> Result<Target, Error> {
fn decode_lower_effective_address(&mut self, memory: &mut M68kBusPort, ins: u16, size: Option<Size>) -> Result<Target, Error> {
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<Size>) -> Result<Target, Error> {
fn decode_upper_effective_address(&mut self, memory: &mut M68kBusPort, ins: u16, size: Option<Size>) -> Result<Target, Error> {
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<i32, Error> {
fn get_extension_displacement(&mut self, memory: &mut M68kBusPort, select: u16) -> Result<i32, Error> {
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<u8>) -> Result<Target, Error> {
fn decode_extension_word(&mut self, memory: &mut M68kBusPort, areg: Option<u8>) -> Result<Target, Error> {
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<Size>) -> Result<Target, Error> {
pub(super) fn get_mode_as_target(&mut self, memory: &mut M68kBusPort, mode: u8, reg: u8, size: Option<Size>) -> Result<Target, Error> {
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<String, Error> =
(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);
}

View File

@ -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<ClockDuration, Error> {
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<ClockDuration, Error> {
self.current_clock = system.clock;
pub fn step_internal(&mut self, system: &System) -> Result<ClockCycles, Error> {
//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<ClockDuration, Error> {
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<ClockDuration, Error> {
pub fn reset_cpu(&mut self) -> Result<ClockCycles, Error> {
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<ClockCycles, Error> {
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<u32, Error> {
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<u32, Error> {
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<u32, Error> {
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<u16, Error> {
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<u32, Error> {
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<u32, Error> {
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),

View File

@ -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;

View File

@ -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<u16, Error> {
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<u32, Error> {
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<u32, Error> {
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<u32, Error> {
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<u32, Error> {
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<u32, Error> {
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<u32, Error> {
}
pub(crate) fn set(&mut self, cpu: &M68k, value: u32) -> Result<(), Error> {
}
pub(crate) fn complete(&self) -> Result<Self, Error> {
}
}
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<u32, Error> {
}
pub(crate) fn complete(&self) -> Result<Self, Error> {
}
}
pub(crate) struct ReadUpdateAccess {
size: Size,
target: Target,
}
impl ReadUpdateAccess {
pub(crate) fn get(&mut self, cpu: &M68k) -> Result<u32, Error> {
}
pub(crate) fn set(&mut self, cpu: &M68k, value: u32) -> Result<(), Error> {
}
pub(crate) fn complete(&self) -> Result<Self, Error> {
}
}
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<Self, Error> {
}
}
*/

View File

@ -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)
}
}

View File

@ -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)]

View File

@ -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)) },

View File

@ -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);

View File

@ -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 {

View File

@ -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);
}

View File

@ -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
}

View File

@ -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