Fixed m68k tests after refactor
This commit is contained in:
parent
cff6a48cc7
commit
54ebcce94c
13
docs/log.txt
13
docs/log.txt
|
@ -466,3 +466,16 @@ General Work
|
||||||
shift instructions pass now, but there are lots but fewer than before failures for the asl and
|
shift instructions pass now, but there are lots but fewer than before failures for the asl and
|
||||||
asr instructions, some movem and movep errors and some bcd instruction errors, mostly
|
asr instructions, some movem and movep errors and some bcd instruction errors, mostly
|
||||||
|
|
||||||
|
2024-03-02
|
||||||
|
- I'm trying to extract the memory/bus interface, and pass it in at the start of each cycle instead
|
||||||
|
of having the BusPort permanently embedded, which will allow migrating to emulator-hal.
|
||||||
|
|
||||||
|
- The functional way would be argument drilling; passing an extra argument to each function in the
|
||||||
|
entire execution core. The problem is that it's messy, so a solution that is still functional is to
|
||||||
|
implement all of the execution logic on a newtype that contains a reference to the mutable state and
|
||||||
|
the owned cycle data, and at the end of the cycle, decompose the M68kCycleGuard that holds the
|
||||||
|
reference, and keep the cycle data for debugging purposes.
|
||||||
|
|
||||||
|
- So far it's going quite well. I really like the pattern of making the cycle be like a transaction,
|
||||||
|
and making it possible to decompose it, especially for testing. I still need to fix the tests
|
||||||
|
- next step is to push System up from the interrupt handling code
|
||||||
|
|
|
@ -50,7 +50,7 @@ impl M68kCycle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(cpu: &mut M68k, clock: Instant) -> Self {
|
pub fn new(cpu: &M68k, clock: Instant) -> Self {
|
||||||
let is_supervisor = cpu.state.sr & (Flags:: Supervisor as u16) != 0;
|
let is_supervisor = cpu.state.sr & (Flags:: Supervisor as u16) != 0;
|
||||||
let pc = cpu.state.pc;
|
let pc = cpu.state.pc;
|
||||||
let data_width = cpu.port.data_width();
|
let data_width = cpu.port.data_width();
|
||||||
|
@ -99,17 +99,17 @@ impl<'a> M68kCycleGuard<'a> {
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finalize(self) -> M68kCycle {
|
pub fn end(self) -> M68kCycle {
|
||||||
self.cycle
|
self.cycle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steppable for M68k {
|
impl Steppable for M68k {
|
||||||
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
||||||
let mut cycle = M68kCycle::new(self, system.clock);
|
let cycle = M68kCycle::new(self, system.clock);
|
||||||
let mut execution = cycle.begin(self);
|
let mut execution = cycle.begin(self);
|
||||||
let clocks = execution.step(system)?;
|
let clocks = execution.step(system)?;
|
||||||
self.cycle = execution.finalize();
|
self.cycle = execution.end();
|
||||||
Ok(self.frequency.period_duration() * clocks as u64)
|
Ok(self.frequency.period_duration() * clocks as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,8 +193,7 @@ impl<'a> M68kCycleGuard<'a> {
|
||||||
pub fn cycle_one(&mut self, system: &System) -> Result<ClockCycles, M68kError> {
|
pub fn cycle_one(&mut self, system: &System) -> Result<ClockCycles, M68kError> {
|
||||||
self.check_breakpoints()?;
|
self.check_breakpoints()?;
|
||||||
|
|
||||||
self.decode_next()?;
|
self.decode_and_execute()?;
|
||||||
self.execute_current()?;
|
|
||||||
|
|
||||||
self.check_pending_interrupts(system)?;
|
self.check_pending_interrupts(system)?;
|
||||||
Ok(self.cycle.timing.calculate_clocks(false, 1))
|
Ok(self.cycle.timing.calculate_clocks(false, 1))
|
||||||
|
@ -299,6 +298,13 @@ impl<'a> M68kCycleGuard<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn decode_and_execute(&mut self) -> Result<(), M68kError> {
|
||||||
|
self.decode_next()?;
|
||||||
|
self.execute_current()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn decode_next(&mut self) -> Result<(), M68kError> {
|
pub fn decode_next(&mut self) -> Result<(), M68kError> {
|
||||||
let is_supervisor = self.is_supervisor();
|
let is_supervisor = self.is_supervisor();
|
||||||
|
|
|
@ -80,7 +80,7 @@ fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, System) {
|
||||||
assert_eq!(cpu.state.pc, INIT_ADDR as u32);
|
assert_eq!(cpu.state.pc, INIT_ADDR as u32);
|
||||||
assert_eq!(cpu.state.ssp, INIT_STACK as u32);
|
assert_eq!(cpu.state.ssp, INIT_STACK as u32);
|
||||||
|
|
||||||
let cycle = M68kCycle::new(cpu, system.clock);
|
let cycle = M68kCycle::new(&cpu, system.clock);
|
||||||
assert_eq!(cycle.decoder.start, INIT_ADDR as u32);
|
assert_eq!(cycle.decoder.start, INIT_ADDR as u32);
|
||||||
assert_eq!(cycle.decoder.instruction, Instruction::NOP);
|
assert_eq!(cycle.decoder.instruction, Instruction::NOP);
|
||||||
(cpu, cycle, system)
|
(cpu, cycle, system)
|
||||||
|
@ -95,18 +95,18 @@ fn load_memory(system: &System, data: &[u16]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_decode_test(case: &TestCase) {
|
fn run_decode_test(case: &TestCase) {
|
||||||
let (mut cpu, mut cycle, system) = init_decode_test(case.cpu);
|
let (mut cpu, cycle, system) = init_decode_test(case.cpu);
|
||||||
load_memory(&system, case.data);
|
load_memory(&system, case.data);
|
||||||
match &case.ins {
|
match &case.ins {
|
||||||
Some(ins) => {
|
Some(ins) => {
|
||||||
let mut execution = cycle.begin(cpu);
|
let mut executor = cycle.begin(&mut cpu);
|
||||||
execution.decode_next().unwrap();
|
executor.decode_next().unwrap();
|
||||||
assert_eq!(cpu.decoder.instruction, ins.clone());
|
assert_eq!(executor.cycle.decoder.instruction, ins.clone());
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
let mut execution = cycle.begin(cpu);
|
let mut executor = cycle.begin(&mut cpu);
|
||||||
let next = execution.decode_next();
|
let next = executor.decode_next();
|
||||||
println!("{:?}", cpu.decoder.instruction);
|
println!("{:?}", executor.cycle.decoder.instruction);
|
||||||
assert!(next.is_err());
|
assert!(next.is_err());
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Steppable, De
|
||||||
|
|
||||||
use moa_m68k::{M68k, M68kType};
|
use moa_m68k::{M68k, M68kType};
|
||||||
use moa_m68k::state::M68kState;
|
use moa_m68k::state::M68kState;
|
||||||
use moa_m68k::execute::M68kCycle;
|
use moa_m68k::execute::{M68kCycle, M68kCycleGuard};
|
||||||
use moa_m68k::instructions::{Instruction, Target, Size, Sign, Direction, Condition};
|
use moa_m68k::instructions::{Instruction, Target, Size, Sign, Direction, Condition};
|
||||||
|
|
||||||
const INIT_STACK: Address = 0x00002000;
|
const INIT_STACK: Address = 0x00002000;
|
||||||
|
@ -37,7 +37,7 @@ struct TestCase {
|
||||||
|
|
||||||
fn run_execute_test<F>(cputype: M68kType, mut test_func: F)
|
fn run_execute_test<F>(cputype: M68kType, mut test_func: F)
|
||||||
where
|
where
|
||||||
F: FnMut(M68kCycle, System),
|
F: FnMut(M68kCycleGuard, System),
|
||||||
{
|
{
|
||||||
let mut system = System::default();
|
let mut system = System::default();
|
||||||
|
|
||||||
|
@ -51,12 +51,14 @@ where
|
||||||
let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0);
|
let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0);
|
||||||
cpu.step(&system).unwrap();
|
cpu.step(&system).unwrap();
|
||||||
|
|
||||||
let cycle = M68kCycle::new(cpu);
|
let cycle = M68kCycle::new(&cpu, system.clock);
|
||||||
assert_eq!(cycle.state.pc, INIT_ADDR as u32);
|
let mut executor = cycle.begin(&mut cpu);
|
||||||
assert_eq!(cycle.state.ssp, INIT_STACK as u32);
|
|
||||||
assert_eq!(cycle.decoder.instruction, Instruction::NOP);
|
|
||||||
|
|
||||||
test_func(cycle, system)
|
assert_eq!(executor.state.pc, INIT_ADDR as u32);
|
||||||
|
assert_eq!(executor.state.ssp, INIT_STACK as u32);
|
||||||
|
assert_eq!(executor.cycle.decoder.instruction, Instruction::NOP);
|
||||||
|
|
||||||
|
test_func(executor, system)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_state(state: &TestState) -> M68kState {
|
fn build_state(state: &TestState) -> M68kState {
|
||||||
|
@ -79,19 +81,19 @@ fn load_memory(system: &System, data: &[u16]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_test(case: &TestCase) {
|
fn run_test(case: &TestCase) {
|
||||||
run_execute_test(case.cputype, |mut cycle, system| {
|
run_execute_test(case.cputype, |mut executor, system| {
|
||||||
let init_state = build_state(&case.init);
|
let init_state = build_state(&case.init);
|
||||||
let expected_state = build_state(&case.fini);
|
let expected_state = build_state(&case.fini);
|
||||||
system.get_bus().write_beu32(system.clock, MEM_ADDR as Address, case.init.mem).unwrap();
|
system.get_bus().write_beu32(system.clock, MEM_ADDR as Address, case.init.mem).unwrap();
|
||||||
|
|
||||||
load_memory(&system, case.data);
|
load_memory(&system, case.data);
|
||||||
*cycle.state = init_state;
|
*executor.state = init_state;
|
||||||
|
|
||||||
cycle.decode_next().unwrap();
|
executor.decode_next().unwrap();
|
||||||
assert_eq!(cycle.decoder.instruction, case.ins);
|
assert_eq!(executor.cycle.decoder.instruction, case.ins);
|
||||||
|
|
||||||
cycle.execute_current().unwrap();
|
executor.execute_current().unwrap();
|
||||||
assert_eq!(*cycle.state, expected_state);
|
assert_eq!(*executor.state, expected_state);
|
||||||
|
|
||||||
let mem = system.get_bus().read_beu32(system.clock, MEM_ADDR as Address).unwrap();
|
let mem = system.get_bus().read_beu32(system.clock, MEM_ADDR as Address).unwrap();
|
||||||
assert_eq!(mem, case.fini.mem);
|
assert_eq!(mem, case.fini.mem);
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
|
|
||||||
use femtos::{Instant, Frequency};
|
use femtos::{Instant, Frequency};
|
||||||
|
|
||||||
use moa_core::{System, Error, MemoryBlock, BusPort, Address, Addressable, Device};
|
use moa_core::{System, Error, MemoryBlock, Address, Addressable, Device};
|
||||||
|
|
||||||
use moa_m68k::{M68k, M68kType};
|
use moa_m68k::{M68k, M68kType};
|
||||||
use moa_m68k::instructions::{Instruction, Target, Size, Sign, Condition, XRegister, BaseRegister, IndexRegister, Direction};
|
use moa_m68k::instructions::{Instruction, Target, Size, Sign, Condition, XRegister, BaseRegister, IndexRegister, Direction};
|
||||||
use moa_m68k::timing::M68kInstructionTiming;
|
use moa_m68k::timing::M68kInstructionTiming;
|
||||||
|
use moa_m68k::execute::M68kCycle;
|
||||||
|
|
||||||
|
|
||||||
const INIT_STACK: Address = 0x00002000;
|
const INIT_STACK: Address = 0x00002000;
|
||||||
const INIT_ADDR: Address = 0x00000010;
|
const INIT_ADDR: Address = 0x00000010;
|
||||||
|
|
||||||
fn init_decode_test(cputype: M68kType) -> (M68k, System) {
|
fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, System) {
|
||||||
let mut system = System::default();
|
let mut system = System::default();
|
||||||
|
|
||||||
// Insert basic initialization
|
// Insert basic initialization
|
||||||
|
@ -22,15 +23,13 @@ fn init_decode_test(cputype: M68kType) -> (M68k, System) {
|
||||||
system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap();
|
system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap();
|
||||||
|
|
||||||
// Initialize the CPU and make sure it's in the expected state
|
// Initialize the CPU and make sure it's in the expected state
|
||||||
let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0);
|
let cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0);
|
||||||
cpu.init_cycle(Instant::START);
|
let cycle = M68kCycle::new(&cpu, system.clock);
|
||||||
assert_eq!(cpu.state.pc, INIT_ADDR as u32);
|
assert_eq!(cpu.state.pc, INIT_ADDR as u32);
|
||||||
assert_eq!(cpu.state.ssp, INIT_STACK as u32);
|
assert_eq!(cpu.state.ssp, INIT_STACK as u32);
|
||||||
|
assert_eq!(cycle.decoder.start, INIT_ADDR as u32);
|
||||||
cpu.decoder.init(true, INIT_ADDR as u32);
|
assert_eq!(cycle.decoder.instruction, Instruction::NOP);
|
||||||
assert_eq!(cpu.decoder.start, INIT_ADDR as u32);
|
(cpu, cycle, system)
|
||||||
assert_eq!(cpu.decoder.instruction, Instruction::NOP);
|
|
||||||
(cpu, system)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_memory(system: &System, data: &[u16]) {
|
fn load_memory(system: &System, data: &[u16]) {
|
||||||
|
@ -42,14 +41,15 @@ fn load_memory(system: &System, data: &[u16]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_timing_test(case: &TimingCase) -> Result<(), Error> {
|
fn run_timing_test(case: &TimingCase) -> Result<(), Error> {
|
||||||
let (mut cpu, system) = init_decode_test(case.cpu);
|
let (mut cpu, cycle, system) = init_decode_test(case.cpu);
|
||||||
|
let mut executor = cycle.begin(&mut cpu);
|
||||||
let mut timing = M68kInstructionTiming::new(case.cpu, 16);
|
let mut timing = M68kInstructionTiming::new(case.cpu, 16);
|
||||||
|
|
||||||
load_memory(&system, case.data);
|
load_memory(&system, case.data);
|
||||||
cpu.decode_next().unwrap();
|
executor.decode_next().unwrap();
|
||||||
assert_eq!(cpu.decoder.instruction, case.ins.clone());
|
assert_eq!(executor.cycle.decoder.instruction, case.ins.clone());
|
||||||
|
|
||||||
timing.add_instruction(&cpu.decoder.instruction);
|
timing.add_instruction(&executor.cycle.decoder.instruction);
|
||||||
let result = timing.calculate_clocks(false, 1);
|
let result = timing.calculate_clocks(false, 1);
|
||||||
let expected = match case.cpu {
|
let expected = match case.cpu {
|
||||||
M68kType::MC68000 => case.timing.0,
|
M68kType::MC68000 => case.timing.0,
|
||||||
|
|
|
@ -6,6 +6,7 @@ use moa_core::{System, Error, MemoryBlock, BusPort, Address, Addressable, Device
|
||||||
use moa_m68k::{M68k, M68kType};
|
use moa_m68k::{M68k, M68kType};
|
||||||
use moa_m68k::instructions::{Instruction, Target, Size};
|
use moa_m68k::instructions::{Instruction, Target, Size};
|
||||||
use moa_m68k::timing::M68kInstructionTiming;
|
use moa_m68k::timing::M68kInstructionTiming;
|
||||||
|
use moa_m68k::execute::M68kCycle;
|
||||||
|
|
||||||
const INIT_STACK: Address = 0x00002000;
|
const INIT_STACK: Address = 0x00002000;
|
||||||
const INIT_ADDR: Address = 0x00000010;
|
const INIT_ADDR: Address = 0x00000010;
|
||||||
|
@ -23,7 +24,7 @@ const TIMING_TESTS: &'static [TimingCase] = &[
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
fn init_decode_test(cputype: M68kType) -> (M68k, System) {
|
fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, System) {
|
||||||
let mut system = System::default();
|
let mut system = System::default();
|
||||||
|
|
||||||
// Insert basic initialization
|
// Insert basic initialization
|
||||||
|
@ -34,15 +35,15 @@ fn init_decode_test(cputype: M68kType) -> (M68k, System) {
|
||||||
system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap();
|
system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap();
|
||||||
|
|
||||||
// Initialize the CPU and make sure it's in the expected state
|
// Initialize the CPU and make sure it's in the expected state
|
||||||
let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0);
|
let cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0);
|
||||||
cpu.reset_cpu().unwrap();
|
//cpu.reset_cpu().unwrap();
|
||||||
assert_eq!(cpu.state.pc, INIT_ADDR as u32);
|
assert_eq!(cpu.state.pc, INIT_ADDR as u32);
|
||||||
assert_eq!(cpu.state.ssp, INIT_STACK as u32);
|
assert_eq!(cpu.state.ssp, INIT_STACK as u32);
|
||||||
|
|
||||||
cpu.decoder.init(true, INIT_ADDR as u32);
|
let cycle = M68kCycle::new(&cpu, system.clock);
|
||||||
assert_eq!(cpu.decoder.start, INIT_ADDR as u32);
|
assert_eq!(cycle.decoder.start, INIT_ADDR as u32);
|
||||||
assert_eq!(cpu.decoder.instruction, Instruction::NOP);
|
assert_eq!(cycle.decoder.instruction, Instruction::NOP);
|
||||||
(cpu, system)
|
(cpu, cycle, system)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_memory(system: &System, data: &[u16]) {
|
fn load_memory(system: &System, data: &[u16]) {
|
||||||
|
@ -54,14 +55,15 @@ fn load_memory(system: &System, data: &[u16]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_timing_test(case: &TimingCase) -> Result<(), Error> {
|
fn run_timing_test(case: &TimingCase) -> Result<(), Error> {
|
||||||
let (mut cpu, system) = init_decode_test(case.cpu);
|
let (mut cpu, cycle, system) = init_decode_test(case.cpu);
|
||||||
|
let mut executor = cycle.begin(&mut cpu);
|
||||||
let mut timing = M68kInstructionTiming::new(case.cpu, 16);
|
let mut timing = M68kInstructionTiming::new(case.cpu, 16);
|
||||||
|
|
||||||
load_memory(&system, case.data);
|
load_memory(&system, case.data);
|
||||||
cpu.decode_next().unwrap();
|
executor.decode_next().unwrap();
|
||||||
assert_eq!(cpu.decoder.instruction, case.ins.clone());
|
assert_eq!(executor.cycle.decoder.instruction, case.ins.clone());
|
||||||
|
|
||||||
timing.add_instruction(&cpu.decoder.instruction);
|
timing.add_instruction(&executor.cycle.decoder.instruction);
|
||||||
let result = timing.calculate_clocks(false, 1);
|
let result = timing.calculate_clocks(false, 1);
|
||||||
let expected = match case.cpu {
|
let expected = match case.cpu {
|
||||||
M68kType::MC68000 => case.timing.0,
|
M68kType::MC68000 => case.timing.0,
|
||||||
|
|
Loading…
Reference in New Issue