Merge pull request #2 from transistorfet/transistor/integrate-emulator-hal

Integrate emulator-hal traits
This commit is contained in:
transistor fet 2024-03-16 18:49:50 -07:00 committed by GitHub
commit a2372d1355
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
56 changed files with 1393 additions and 1039 deletions

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "emulator/libraries/femtos"] [submodule "emulator/libraries/femtos"]
path = emulator/libraries/femtos path = emulator/libraries/femtos
url = git@github.com:transistorfet/femtos.git url = git@github.com:transistorfet/femtos.git
[submodule "emulator/libraries/emulator-hal"]
path = emulator/libraries/emulator-hal
url = git@github.com:transistorfet/emulator-hal.git

32
Cargo.lock generated
View File

@ -420,6 +420,17 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]] [[package]]
name = "emulator-hal" name = "emulator-hal"
version = "0.1.0" version = "0.1.0"
dependencies = [
"femtos",
"fugit",
]
[[package]]
name = "emulator-hal-memory"
version = "0.1.0"
dependencies = [
"emulator-hal",
]
[[package]] [[package]]
name = "env_logger" name = "env_logger"
@ -480,6 +491,21 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "fugit"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7"
dependencies = [
"gcd",
]
[[package]]
name = "gcd"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
[[package]] [[package]]
name = "glob" name = "glob"
version = "0.3.1" version = "0.3.1"
@ -491,9 +517,10 @@ name = "harte-tests"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"clap 3.2.25", "clap 3.2.25",
"emulator-hal",
"emulator-hal-memory",
"femtos", "femtos",
"flate2", "flate2",
"moa-core",
"moa-m68k", "moa-m68k",
"serde", "serde",
"serde_derive", "serde_derive",
@ -755,6 +782,8 @@ dependencies = [
"log", "log",
"moa-common", "moa-common",
"moa-core", "moa-core",
"moa-debugger",
"moa-host",
"moa-m68k", "moa-m68k",
"moa-peripherals-generic", "moa-peripherals-generic",
"moa-peripherals-motorola", "moa-peripherals-motorola",
@ -794,6 +823,7 @@ name = "moa-m68k"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"emulator-hal", "emulator-hal",
"emulator-hal-memory",
"femtos", "femtos",
"log", "log",
"moa-core", "moa-core",

View File

@ -1,4 +1,5 @@
[workspace] [workspace]
resolver = "2"
members = [ members = [
"emulator/core", "emulator/core",
"emulator/frontends/common", "emulator/frontends/common",
@ -10,6 +11,8 @@ members = [
exclude = [ exclude = [
"emulator/frontends/pixels", "emulator/frontends/pixels",
"emulator/frontends/macroquad", "emulator/frontends/macroquad",
"emulator/libraries/femtos",
"emulator/libraries/emulator-hal",
] ]
default-members = ["emulator/frontends/minifb"] default-members = ["emulator/frontends/minifb"]
@ -18,5 +21,6 @@ opt-level = 3
[profile.release] [profile.release]
debug = true debug = true
# TODO there are many overflow errors, which could be bugs
#overflow-checks = true #overflow-checks = true

View File

@ -479,3 +479,15 @@ General Work
- So far it's going quite well. I really like the pattern of making the cycle be like a transaction, - 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 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 - next step is to push System up from the interrupt handling code
2024-03-10
- the emulator-hal conversion is going well. I'm thinking it makes more sense for the Address of
BusAccess to be a generic instead of an associated type, but I'll need to finish converting
everything to get a better sense of it. There's a lot of cleanup to do
2024-03-14
- I finally took a look at a flamegraph of the harte_test runner, and almost the entirety of the time
spent running tests was in zeroing of the array of memory at the start of each test. I really
should use MaybeUninit, but I instead used Vec::with_capacity/.set_len(). It went from 15-24 minutes
down to 6 seconds.

View File

@ -8,4 +8,4 @@ log = "0.4"
femtos = "0.1" femtos = "0.1"
thiserror = "1.0" thiserror = "1.0"
moa-host = { path = "../libraries/host" } moa-host = { path = "../libraries/host" }
emulator-hal = { path = "/media/work/projects/emulator-hal/emulator-hal" } emulator-hal = { path = "../libraries/emulator-hal/emulator-hal" }

View File

@ -171,7 +171,7 @@ pub trait Debuggable {
fn remove_breakpoint(&mut self, addr: Address); fn remove_breakpoint(&mut self, addr: Address);
fn print_current_step(&mut self, system: &System) -> Result<(), Error>; fn print_current_step(&mut self, system: &System) -> Result<(), Error>;
fn print_disassembly(&mut self, addr: Address, count: usize); fn print_disassembly(&mut self, system: &System, addr: Address, count: usize);
fn run_command(&mut self, system: &System, args: &[&str]) -> Result<bool, Error>; fn run_command(&mut self, system: &System, args: &[&str]) -> Result<bool, Error>;
} }

View File

@ -1,6 +1,5 @@
use std::fmt; use std::fmt;
use std::error::{Error as StdError};
use moa_host::HostError; use moa_host::HostError;
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@ -75,8 +74,8 @@ impl fmt::Display for Error {
} }
impl<E> From<HostError<E>> for Error { impl<E> From<HostError<E>> for Error {
fn from(err: HostError<E>) -> Self { fn from(_err: HostError<E>) -> Self {
Self::Other(format!("other")) Self::Other("other".to_string())
} }
} }

View File

@ -26,11 +26,11 @@ impl InterruptController {
Ok(()) Ok(())
} }
pub fn check(&mut self) -> (bool, u8) { pub fn check(&mut self) -> (bool, u8, u8) {
if self.highest > 0 { if self.highest > 0 {
(true, self.highest) (true, self.highest, self.interrupts[self.highest as usize].1)
} else { } else {
(false, 0) (false, 0, 0)
} }
} }

View File

@ -11,7 +11,7 @@ pub use crate::devices::{Address, Addressable, Steppable, Interruptable, Debugga
pub use crate::devices::{read_beu16, read_beu32, read_leu16, read_leu32, write_beu16, write_beu32, write_leu16, write_leu32, wrap_transmutable}; pub use crate::devices::{read_beu16, read_beu32, read_leu16, read_leu32, write_beu16, write_beu32, write_leu16, write_leu32, wrap_transmutable};
pub use crate::error::Error; pub use crate::error::Error;
pub use crate::interrupts::InterruptController; pub use crate::interrupts::InterruptController;
pub use crate::memory::{MemoryBlock, AddressTranslator, AddressRepeater, Bus, BusPort, dump_slice}; pub use crate::memory::{MemoryBlock, AddressTranslator, AddressRepeater, Bus, BusPort, dump_slice, dump_memory};
pub use crate::system::System; pub use crate::system::System;
pub use emulator_hal::bus::{BusAccess}; pub use emulator_hal::bus::{BusAccess};

View File

@ -358,3 +358,47 @@ pub fn dump_slice(data: &[u8], mut count: usize) {
} }
} }
pub fn dump_memory<Bus, Address, Instant>(bus: &mut Bus, clock: Instant, addr: Address, count: Address)
where
Bus: BusAccess<Address, Instant>,
Address: From<u64> + Into<u64> + Copy,
Instant: Copy,
{
let mut addr = addr.into();
let mut count = count.into();
while count > 0 {
let mut line = format!("{:#010x}: ", addr);
let to = if count < 16 { count / 2 } else { 8 };
for _ in 0..to {
let word = bus.read_beu16(clock, Address::from(addr));
if word.is_err() {
println!("{}", line);
return;
}
write!(line, "{:#06x} ", word.unwrap()).unwrap();
addr += 2;
count -= 2;
}
println!("{}", line);
}
}
use emulator_hal::bus::{self, BusAccess};
impl bus::Error for Error {}
impl BusAccess<u64, Instant> for &mut dyn Addressable {
type Error = Error;
fn read(&mut self, now: Instant, addr: Address, data: &mut [u8]) -> Result<usize, Self::Error> {
(*self).read(now, addr, data)?;
Ok(data.len())
}
fn write(&mut self, now: Instant, addr: Address, data: &[u8]) -> Result<usize, Self::Error> {
(*self).write(now, addr, data)?;
Ok(data.len())
}
}

View File

@ -7,6 +7,13 @@ edition = "2021"
log = "0.4" log = "0.4"
thiserror = "1.0" thiserror = "1.0"
femtos = "0.1" femtos = "0.1"
moa-core = { path = "../../core" }
moa-parsing = { path = "../../libraries/parsing" } moa-parsing = { path = "../../libraries/parsing" }
emulator-hal = { path = "/media/work/projects/emulator-hal/emulator-hal" } emulator-hal = { path = "../../libraries/emulator-hal/emulator-hal" }
moa-core = { path = "../../core", optional = true }
[dev-dependencies]
emulator-hal-memory = { path = "../../libraries/emulator-hal/emulator-hal-memory" }
[features]
moa = ["moa-core"]

View File

@ -1,12 +1,26 @@
use std::collections::HashMap; use std::collections::HashMap;
use moa_core::Error; use moa_parsing::{self as parser, AssemblyLine, AssemblyOperand, AssemblyParser, ParserError};
use moa_parsing::{self as parser, AssemblyLine, AssemblyOperand, AssemblyParser};
use super::state::M68kType; use super::state::M68kType;
use super::instructions::Size; use super::instructions::Size;
#[derive(Clone, Debug)]
pub struct Error(String);
impl Error {
pub fn new(msg: String) -> Self {
Self(msg)
}
}
impl From<ParserError> for Error {
fn from(err: ParserError) -> Self {
Self(err.0)
}
}
#[repr(usize)] #[repr(usize)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -114,7 +128,7 @@ impl M68kAssembler {
fn parse(&mut self, text: &str) -> Result<Vec<(usize, AssemblyLine)>, Error> { fn parse(&mut self, text: &str) -> Result<Vec<(usize, AssemblyLine)>, Error> {
let mut parser = AssemblyParser::new(text); let mut parser = AssemblyParser::new(text);
parser.parse() Ok(parser.parse()?)
} }
fn apply_relocations(&mut self) -> Result<(), Error> { fn apply_relocations(&mut self) -> Result<(), Error> {

View File

@ -17,7 +17,7 @@ fn main() {
for word in words.iter() { for word in words.iter() {
print!("{:04x} ", word); print!("{:04x} ", word);
} }
println!(""); println!();
}, },
Err(err) => { Err(err) => {
println!("{:?}", err); println!("{:?}", err);

View File

@ -1,9 +1,10 @@
use moa_core::{System, Error, Address, Addressable, Debuggable}; use femtos::Instant;
use emulator_hal::bus::BusAccess;
use super::state::M68k; use super::state::M68kError;
use super::decode::M68kDecoder;
use super::execute::M68kCycleExecutor; use super::execute::M68kCycleExecutor;
use super::memory::M68kAddress;
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct StackTracer { pub struct StackTracer {
@ -25,54 +26,16 @@ impl StackTracer {
pub struct M68kDebugger { pub struct M68kDebugger {
pub(crate) skip_breakpoint: usize, pub(crate) skip_breakpoint: usize,
pub(crate) breakpoints: Vec<u32>, pub(crate) breakpoints: Vec<u32>,
#[allow(dead_code)]
pub(crate) step_until_return: Option<usize>, pub(crate) step_until_return: Option<usize>,
pub(crate) stack_tracer: StackTracer, pub(crate) stack_tracer: StackTracer,
} }
impl Debuggable for M68k { impl<'a, Bus, BusError> M68kCycleExecutor<'a, Bus>
fn add_breakpoint(&mut self, addr: Address) { where
self.debugger.breakpoints.push(addr as u32); Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
} {
pub fn check_breakpoints(&mut self) -> Result<(), M68kError<BusError>> {
fn remove_breakpoint(&mut self, addr: Address) {
if let Some(index) = self.debugger.breakpoints.iter().position(|a| *a == addr as u32) {
self.debugger.breakpoints.remove(index);
}
}
fn print_current_step(&mut self, _system: &System) -> Result<(), Error> {
// TODO this is called by the debugger, but should be called some other way
//let _ = self.decoder.decode_at(&mut self.port, true, self.state.pc);
//self.decoder.dump_decoded(&mut self.port);
//self.dump_state();
Ok(())
}
fn print_disassembly(&mut self, addr: Address, count: usize) {
let mut decoder = M68kDecoder::new(self.info.chip, true, 0);
// TODO temporarily disabled
//decoder.dump_disassembly(&mut self.port, addr as u32, count as u32);
}
fn run_command(&mut self, system: &System, args: &[&str]) -> Result<bool, Error> {
match args[0] {
"ds" | "stack" | "dumpstack" => {
println!("Stack:");
for addr in &self.debugger.stack_tracer.calls {
println!(" {:08x}", self.port.read_beu32(system.clock, *addr as Address)?);
}
},
"so" | "stepout" => {
self.debugger.step_until_return = Some(self.debugger.stack_tracer.calls.len() - 1);
},
_ => { return Ok(true); },
}
Ok(false)
}
}
impl<'a> M68kCycleExecutor<'a> {
pub fn check_breakpoints(&mut self) -> Result<(), Error> {
for breakpoint in &self.debugger.breakpoints { for breakpoint in &self.debugger.breakpoints {
if *breakpoint == self.state.pc { if *breakpoint == self.state.pc {
if self.debugger.skip_breakpoint > 0 { if self.debugger.skip_breakpoint > 0 {
@ -80,7 +43,7 @@ impl<'a> M68kCycleExecutor<'a> {
return Ok(()); return Ok(());
} else { } else {
self.debugger.skip_breakpoint = 1; self.debugger.skip_breakpoint = 1;
return Err(Error::breakpoint(format!("breakpoint reached: {:08x}", *breakpoint))); return Err(M68kError::Breakpoint);
} }
} }
} }

View File

@ -1,10 +1,9 @@
use femtos::Instant; use femtos::Instant;
use emulator_hal::bus::BusAccess;
use moa_core::{Address, Addressable, BusPort};
use crate::state::{M68kType, M68kError, Exceptions}; use crate::state::{M68kType, M68kError, Exceptions};
use crate::memory::M68kBusPort; use crate::memory::{M68kBusPort, M68kAddress};
use crate::instructions::{ use crate::instructions::{
Size, Size,
Sign, Sign,
@ -49,10 +48,13 @@ pub struct M68kDecoder {
pub instruction: Instruction, pub instruction: Instruction,
} }
pub struct InstructionDecoding<'a> { pub struct InstructionDecoding<'a, Bus>
port: &'a mut BusPort, where
memory: &'a mut M68kBusPort, Bus: BusAccess<M68kAddress, Instant>,
decoder: &'a mut M68kDecoder, {
pub(crate) bus: &'a mut Bus,
pub(crate) memory: &'a mut M68kBusPort,
pub(crate) decoder: &'a mut M68kDecoder,
} }
impl M68kDecoder { impl M68kDecoder {
@ -76,10 +78,13 @@ impl M68kDecoder {
} }
#[inline] #[inline]
pub fn decode_at(&mut self, port: &mut BusPort, memory: &mut M68kBusPort, is_supervisor: bool, start: u32) -> Result<(), M68kError> { pub fn decode_at<Bus>(&mut self, bus: &mut Bus, memory: &mut M68kBusPort, is_supervisor: bool, start: u32) -> Result<(), M68kError<Bus::Error>>
where
Bus: BusAccess<M68kAddress, Instant>,
{
self.init(is_supervisor, start); self.init(is_supervisor, start);
let mut decoding = InstructionDecoding { let mut decoding = InstructionDecoding {
port, bus,
memory, memory,
decoder: self, decoder: self,
}; };
@ -87,21 +92,22 @@ impl M68kDecoder {
Ok(()) Ok(())
} }
pub fn dump_disassembly(&mut self, port: &mut BusPort, memory: &mut M68kBusPort, start: u32, length: u32) { pub fn dump_disassembly<Bus>(&mut self, bus: &mut Bus, start: u32, length: u32)
where
Bus: BusAccess<M68kAddress, Instant>,
{
let mut memory = M68kBusPort::default();
let mut next = start; let mut next = start;
while next < (start + length) { while next < (start + length) {
match self.decode_at(port, memory, self.is_supervisor, next) { match self.decode_at(bus, &mut memory, self.is_supervisor, next) {
Ok(()) => { Ok(()) => {
self.dump_decoded(memory.current_clock, port); self.dump_decoded(memory.current_clock, bus);
next = self.end; next = self.end;
}, },
Err(err) => { Err(err) => {
println!("{:?}", err); println!("{:?}", err);
match err { if let M68kError::Exception(Exceptions::IllegalInstruction) = err {
M68kError::Exception(ex) if ex == Exceptions::IllegalInstruction => { println!(" at {:08x}: {:04x}", self.start, bus.read_beu16(memory.current_clock, self.start).unwrap());
println!(" at {:08x}: {:04x}", self.start, port.read_beu16(memory.current_clock, self.start as Address).unwrap());
},
_ => { },
} }
return; return;
}, },
@ -109,18 +115,24 @@ impl M68kDecoder {
} }
} }
pub fn dump_decoded(&mut self, clock: Instant, port: &mut BusPort) { pub fn dump_decoded<Bus>(&mut self, clock: Instant, bus: &mut Bus)
let ins_data: Result<String, M68kError> = where
Bus: BusAccess<M68kAddress, Instant>,
{
let ins_data: Result<String, M68kError<Bus::Error>> =
(0..((self.end - self.start) / 2)).map(|offset| (0..((self.end - self.start) / 2)).map(|offset|
Ok(format!("{:04x} ", port.read_beu16(clock, (self.start + (offset * 2)) as Address).unwrap())) Ok(format!("{:04x} ", bus.read_beu16(clock, self.start + (offset * 2)).unwrap()))
).collect(); ).collect();
println!("{:#010x}: {}\n\t{}\n", self.start, ins_data.unwrap(), self.instruction); println!("{:#010x}: {}\n\t{}\n", self.start, ins_data.unwrap(), self.instruction);
} }
} }
impl<'a> InstructionDecoding<'a> { impl<'a, Bus> InstructionDecoding<'a, Bus>
where
Bus: BusAccess<M68kAddress, Instant>,
{
#[inline] #[inline]
pub fn decode_next(&mut self) -> Result<Instruction, M68kError> { pub fn decode_next(&mut self) -> Result<Instruction, M68kError<Bus::Error>> {
let ins = self.read_instruction_word()?; let ins = self.read_instruction_word()?;
self.decoder.instruction_word = ins; self.decoder.instruction_word = ins;
@ -146,7 +158,7 @@ impl<'a> InstructionDecoding<'a> {
} }
#[inline] #[inline]
fn decode_group_bit_ops(&mut self, ins: u16) -> Result<Instruction, M68kError> { fn decode_group_bit_ops(&mut self, ins: u16) -> Result<Instruction, M68kError<Bus::Error>> {
let optype = (ins & 0x0F00) >> 8; let optype = (ins & 0x0F00) >> 8;
if (ins & 0x13F) == 0x03C { if (ins & 0x13F) == 0x03C {
@ -221,14 +233,14 @@ impl<'a> InstructionDecoding<'a> {
} }
#[inline] #[inline]
fn decode_group_move_byte(&mut self, ins: u16) -> Result<Instruction, M68kError> { fn decode_group_move_byte(&mut self, ins: u16) -> Result<Instruction, M68kError<Bus::Error>> {
let src = self.decode_lower_effective_address(ins, Some(Size::Byte))?; let src = self.decode_lower_effective_address(ins, Some(Size::Byte))?;
let dest = self.decode_upper_effective_address(ins, Some(Size::Byte))?; let dest = self.decode_upper_effective_address(ins, Some(Size::Byte))?;
Ok(Instruction::MOVE(src, dest, Size::Byte)) Ok(Instruction::MOVE(src, dest, Size::Byte))
} }
#[inline] #[inline]
fn decode_group_move_long(&mut self, ins: u16) -> Result<Instruction, M68kError> { fn decode_group_move_long(&mut self, ins: u16) -> Result<Instruction, M68kError<Bus::Error>> {
let src = self.decode_lower_effective_address(ins, Some(Size::Long))?; let src = self.decode_lower_effective_address(ins, Some(Size::Long))?;
let dest = self.decode_upper_effective_address(ins, Some(Size::Long))?; let dest = self.decode_upper_effective_address(ins, Some(Size::Long))?;
if let Target::DirectAReg(reg) = dest { if let Target::DirectAReg(reg) = dest {
@ -239,7 +251,7 @@ impl<'a> InstructionDecoding<'a> {
} }
#[inline] #[inline]
fn decode_group_move_word(&mut self, ins: u16) -> Result<Instruction, M68kError> { fn decode_group_move_word(&mut self, ins: u16) -> Result<Instruction, M68kError<Bus::Error>> {
let src = self.decode_lower_effective_address(ins, Some(Size::Word))?; let src = self.decode_lower_effective_address(ins, Some(Size::Word))?;
let dest = self.decode_upper_effective_address(ins, Some(Size::Word))?; let dest = self.decode_upper_effective_address(ins, Some(Size::Word))?;
if let Target::DirectAReg(reg) = dest { if let Target::DirectAReg(reg) = dest {
@ -250,7 +262,7 @@ impl<'a> InstructionDecoding<'a> {
} }
#[inline] #[inline]
fn decode_group_misc(&mut self, ins: u16) -> Result<Instruction, M68kError> { fn decode_group_misc(&mut self, ins: u16) -> Result<Instruction, M68kError<Bus::Error>> {
let ins_0f00 = ins & 0xF00; let ins_0f00 = ins & 0xF00;
let ins_00f0 = ins & 0x0F0; let ins_00f0 = ins & 0x0F0;
@ -421,7 +433,7 @@ impl<'a> InstructionDecoding<'a> {
} }
#[inline] #[inline]
fn decode_group_addq_subq(&mut self, ins: u16) -> Result<Instruction, M68kError> { fn decode_group_addq_subq(&mut self, ins: u16) -> Result<Instruction, M68kError<Bus::Error>> {
match get_size(ins) { match get_size(ins) {
Some(size) => { Some(size) => {
let target = self.decode_lower_effective_address(ins, Some(size))?; let target = self.decode_lower_effective_address(ins, Some(size))?;
@ -459,7 +471,7 @@ impl<'a> InstructionDecoding<'a> {
} }
#[inline] #[inline]
fn decode_group_branch(&mut self, ins: u16) -> Result<Instruction, M68kError> { fn decode_group_branch(&mut self, ins: u16) -> Result<Instruction, M68kError<Bus::Error>> {
let mut disp = ((ins & 0xFF) as i8) as i32; let mut disp = ((ins & 0xFF) as i8) as i32;
if disp == 0 { if disp == 0 {
disp = (self.read_instruction_word()? as i16) as i32; disp = (self.read_instruction_word()? as i16) as i32;
@ -475,7 +487,7 @@ impl<'a> InstructionDecoding<'a> {
} }
#[inline] #[inline]
fn decode_group_moveq(&mut self, ins: u16) -> Result<Instruction, M68kError> { fn decode_group_moveq(&mut self, ins: u16) -> Result<Instruction, M68kError<Bus::Error>> {
if (ins & 0x0100) != 0 { if (ins & 0x0100) != 0 {
return Err(M68kError::Exception(Exceptions::IllegalInstruction)); return Err(M68kError::Exception(Exceptions::IllegalInstruction));
} }
@ -485,7 +497,7 @@ impl<'a> InstructionDecoding<'a> {
} }
#[inline] #[inline]
fn decode_group_div_or(&mut self, ins: u16) -> Result<Instruction, M68kError> { fn decode_group_div_or(&mut self, ins: u16) -> Result<Instruction, M68kError<Bus::Error>> {
let size = get_size(ins); let size = get_size(ins);
if (ins & 0x1F0) == 0x100 { if (ins & 0x1F0) == 0x100 {
@ -509,7 +521,7 @@ impl<'a> InstructionDecoding<'a> {
} }
#[inline] #[inline]
fn decode_group_sub(&mut self, ins: u16) -> Result<Instruction, M68kError> { fn decode_group_sub(&mut self, ins: u16) -> Result<Instruction, M68kError<Bus::Error>> {
let reg = get_high_reg(ins); let reg = get_high_reg(ins);
let dir = (ins & 0x0100) >> 8; let dir = (ins & 0x0100) >> 8;
let size = get_size(ins); let size = get_size(ins);
@ -540,7 +552,7 @@ impl<'a> InstructionDecoding<'a> {
} }
#[inline] #[inline]
fn decode_group_cmp_eor(&mut self, ins: u16) -> Result<Instruction, M68kError> { fn decode_group_cmp_eor(&mut self, ins: u16) -> Result<Instruction, M68kError<Bus::Error>> {
let reg = get_high_reg(ins); let reg = get_high_reg(ins);
let optype = (ins & 0x0100) >> 8; let optype = (ins & 0x0100) >> 8;
let size = get_size(ins); let size = get_size(ins);
@ -567,7 +579,7 @@ impl<'a> InstructionDecoding<'a> {
} }
#[inline] #[inline]
fn decode_group_mul_and(&mut self, ins: u16) -> Result<Instruction, M68kError> { fn decode_group_mul_and(&mut self, ins: u16) -> Result<Instruction, M68kError<Bus::Error>> {
let size = get_size(ins); let size = get_size(ins);
if (ins & 0b0001_1111_0000) == 0b0001_0000_0000 { if (ins & 0b0001_1111_0000) == 0b0001_0000_0000 {
@ -600,7 +612,7 @@ impl<'a> InstructionDecoding<'a> {
} }
#[inline] #[inline]
fn decode_group_add(&mut self, ins: u16) -> Result<Instruction, M68kError> { fn decode_group_add(&mut self, ins: u16) -> Result<Instruction, M68kError<Bus::Error>> {
let reg = get_high_reg(ins); let reg = get_high_reg(ins);
let dir = (ins & 0x0100) >> 8; let dir = (ins & 0x0100) >> 8;
let size = get_size(ins); let size = get_size(ins);
@ -630,7 +642,7 @@ impl<'a> InstructionDecoding<'a> {
} }
} }
fn decode_group_shift(&mut self, ins: u16) -> Result<Instruction, M68kError> { fn decode_group_shift(&mut self, ins: u16) -> Result<Instruction, M68kError<Bus::Error>> {
match get_size(ins) { match get_size(ins) {
Some(size) => { Some(size) => {
let target = Target::DirectDReg(get_low_reg(ins)); let target = Target::DirectDReg(get_low_reg(ins));
@ -716,31 +728,31 @@ impl<'a> InstructionDecoding<'a> {
} }
} }
fn read_instruction_word(&mut self) -> Result<u16, M68kError> { fn read_instruction_word(&mut self) -> Result<u16, M68kError<Bus::Error>> {
let word = self.memory.read_instruction_word(self.port, self.decoder.is_supervisor, self.decoder.end)?; let word = self.memory.read_instruction_word(self.bus, self.decoder.is_supervisor, self.decoder.end)?;
self.decoder.end += 2; self.decoder.end += 2;
Ok(word) Ok(word)
} }
fn read_instruction_long(&mut self) -> Result<u32, M68kError> { fn read_instruction_long(&mut self) -> Result<u32, M68kError<Bus::Error>> {
let word = self.memory.read_instruction_long(self.port, self.decoder.is_supervisor, self.decoder.end)?; let word = self.memory.read_instruction_long(self.bus, self.decoder.is_supervisor, self.decoder.end)?;
self.decoder.end += 4; self.decoder.end += 4;
Ok(word) Ok(word)
} }
fn decode_lower_effective_address(&mut self, ins: u16, size: Option<Size>) -> Result<Target, M68kError> { fn decode_lower_effective_address(&mut self, ins: u16, size: Option<Size>) -> Result<Target, M68kError<Bus::Error>> {
let reg = get_low_reg(ins); let reg = get_low_reg(ins);
let mode = get_low_mode(ins); let mode = get_low_mode(ins);
self.get_mode_as_target(mode, reg, size) self.get_mode_as_target(mode, reg, size)
} }
fn decode_upper_effective_address(&mut self, ins: u16, size: Option<Size>) -> Result<Target, M68kError> { fn decode_upper_effective_address(&mut self, ins: u16, size: Option<Size>) -> Result<Target, M68kError<Bus::Error>> {
let reg = get_high_reg(ins); let reg = get_high_reg(ins);
let mode = get_high_mode(ins); let mode = get_high_mode(ins);
self.get_mode_as_target(mode, reg, size) self.get_mode_as_target(mode, reg, size)
} }
fn get_extension_displacement(&mut self, select: u16) -> Result<i32, M68kError> { fn get_extension_displacement(&mut self, select: u16) -> Result<i32, M68kError<Bus::Error>> {
let result = match select { let result = match select {
0b00 | 0b01 => 0, 0b00 | 0b01 => 0,
0b10 => sign_extend_to_long(self.read_instruction_word()? as u32, Size::Word), 0b10 => sign_extend_to_long(self.read_instruction_word()? as u32, Size::Word),
@ -750,7 +762,7 @@ impl<'a> InstructionDecoding<'a> {
Ok(result) Ok(result)
} }
fn decode_extension_word(&mut self, areg: Option<u8>) -> Result<Target, M68kError> { fn decode_extension_word(&mut self, areg: Option<u8>) -> Result<Target, M68kError<Bus::Error>> {
let brief_extension = self.read_instruction_word()?; let brief_extension = self.read_instruction_word()?;
let use_brief = (brief_extension & 0x0100) == 0; let use_brief = (brief_extension & 0x0100) == 0;
@ -803,7 +815,7 @@ impl<'a> InstructionDecoding<'a> {
} }
} }
pub(super) fn get_mode_as_target(&mut self, mode: u8, reg: u8, size: Option<Size>) -> Result<Target, M68kError> { pub(super) fn get_mode_as_target(&mut self, mode: u8, reg: u8, size: Option<Size>) -> Result<Target, M68kError<Bus::Error>> {
let value = match mode { let value = match mode {
0b000 => Target::DirectDReg(reg), 0b000 => Target::DirectDReg(reg),
0b001 => Target::DirectAReg(reg), 0b001 => Target::DirectAReg(reg),

File diff suppressed because it is too large Load Diff

View File

@ -9,5 +9,9 @@ pub mod memory;
pub mod timing; pub mod timing;
pub mod tests; pub mod tests;
pub use self::state::{M68k, M68kType, M68kError}; #[cfg(feature = "moa")]
pub mod moa;
pub use crate::state::{M68k, M68kType, M68kError};
pub use crate::memory::{M68kAddress, M68kAddressSpace};

View File

@ -1,10 +1,10 @@
use core::cmp;
use core::fmt::Write;
use femtos::Instant; use femtos::Instant;
use emulator_hal::bus::{BusType, BusAccess}; use emulator_hal::bus::BusAccess;
use moa_core::{Address, Addressable, BusPort}; use crate::state::{M68k, M68kError, CpuInfo, Exceptions};
use crate::state::{M68k, M68kError, Exceptions};
use crate::instructions::Size; use crate::instructions::Size;
#[repr(u8)] #[repr(u8)]
@ -76,7 +76,7 @@ impl Default for MemoryRequest {
} }
impl MemoryRequest { impl MemoryRequest {
pub(crate) fn instruction(&mut self, is_supervisor: bool, addr: u32) -> Result<u32, M68kError> { pub(crate) fn instruction<BusError>(&mut self, is_supervisor: bool, addr: u32) -> Result<u32, M68kError<BusError>> {
self.i_n_bit = false; self.i_n_bit = false;
self.code = FunctionCode::program(is_supervisor); self.code = FunctionCode::program(is_supervisor);
self.access = MemAccess::Read; self.access = MemAccess::Read;
@ -101,6 +101,9 @@ impl MemoryRequest {
} }
} }
//pub type M68kAddress = (FunctionCode, u32);
pub type M68kAddress = u32;
pub type M68kAddressSpace = (FunctionCode, u32);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct InstructionRequest { pub struct InstructionRequest {
@ -110,8 +113,9 @@ pub struct InstructionRequest {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct M68kBusPort { pub struct M68kBusPort {
//pub port: BusPort,
pub request: MemoryRequest, pub request: MemoryRequest,
pub data_bytewidth: usize,
pub address_mask: u32,
pub cycle_start_clock: Instant, pub cycle_start_clock: Instant,
pub current_clock: Instant, pub current_clock: Instant,
} }
@ -122,10 +126,11 @@ impl M68k {
} }
impl Default for M68kBusPort { impl Default for M68kBusPort {
fn default(/* port: BusPort */) -> Self { fn default() -> Self {
Self { Self {
//port,
request: Default::default(), request: Default::default(),
data_bytewidth: 32 / 8,
address_mask: 0xFFFF_FFFF,
cycle_start_clock: Instant::START, cycle_start_clock: Instant::START,
current_clock: Instant::START, current_clock: Instant::START,
} }
@ -133,43 +138,101 @@ impl Default for M68kBusPort {
} }
impl M68kBusPort { impl M68kBusPort {
pub fn new(clock: Instant) -> Self { pub fn from_info(info: &CpuInfo, clock: Instant) -> Self {
Self { Self {
request: Default::default(), request: Default::default(),
data_bytewidth: info.data_width as usize / 8,
address_mask: 1_u32.checked_shl(info.address_width as u32).unwrap_or(0).wrapping_sub(1),
cycle_start_clock: clock, cycle_start_clock: clock,
current_clock: clock, current_clock: clock,
} }
} }
pub(crate) fn read_data_sized(&mut self, port: &mut BusPort, is_supervisor: bool, addr: Address, size: Size) -> Result<u32, M68kError> { fn read<Bus, BusError>(&mut self, bus: &mut Bus, clock: Instant, addr: M68kAddress, data: &mut [u8]) -> Result<(), M68kError<BusError>>
self.start_request(is_supervisor, addr as u32, size, MemAccess::Read, MemType::Data, false)?; where
Ok(match size { Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
Size::Byte => port.read_u8(self.current_clock, addr).map(|value| value as u32), {
Size::Word => port.read_beu16(self.current_clock, addr).map(|value| value as u32), let addr = addr & self.address_mask;
Size::Long => port.read_beu32(self.current_clock, addr), for i in (0..data.len()).step_by(self.data_bytewidth) {
}?) let addr_index = (addr + i as M68kAddress) & self.address_mask;
let end = cmp::min(i + self.data_bytewidth, data.len());
bus.read(clock, addr_index, &mut data[i..end])
.map_err(|err| M68kError::BusError(err))?;
}
Ok(())
} }
pub(crate) fn write_data_sized(&mut self, port: &mut BusPort, is_supervisor: bool, addr: Address, value: u32, size: Size) -> Result<(), M68kError> { fn write<Bus, BusError>(&mut self, bus: &mut Bus, clock: Instant, addr: M68kAddress, data: &[u8]) -> Result<(), M68kError<BusError>>
self.start_request(is_supervisor, addr as u32, size, MemAccess::Write, MemType::Data, false)?; where
Ok(match size { Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
Size::Byte => port.write_u8(self.current_clock, addr, value as u8), {
Size::Word => port.write_beu16(self.current_clock, addr, value as u16), let addr = addr & self.address_mask;
Size::Long => port.write_beu32(self.current_clock, addr, value), for i in (0..data.len()).step_by(self.data_bytewidth) {
}?) let addr_index = (addr + i as M68kAddress) & self.address_mask;
let end = cmp::min(i + self.data_bytewidth, data.len());
bus.write(clock, addr_index, &data[i..end])
.map_err(|err| M68kError::BusError(err))?;
}
Ok(())
} }
pub(crate) fn read_instruction_word(&mut self, port: &mut BusPort, is_supervisor: bool, addr: u32) -> Result<u16, M68kError> { fn read_sized<Bus, BusError>(&mut self, bus: &mut Bus, addr: M68kAddress, size: Size) -> Result<u32, M68kError<BusError>>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
{
let mut data = [0; 4];
match size {
Size::Byte => self.read(bus, self.current_clock, addr, &mut data[3..4]),
Size::Word => self.read(bus, self.current_clock, addr, &mut data[2..4]),
Size::Long => self.read(bus, self.current_clock, addr, &mut data[0..4]),
}.map(|_| u32::from_be_bytes(data))
}
fn write_sized<Bus, BusError>(&mut self, bus: &mut Bus, addr: M68kAddress, size: Size, value: u32) -> Result<(), M68kError<BusError>>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
{
let data = value.to_be_bytes();
match size {
Size::Byte => self.write(bus, self.current_clock, addr, &data[3..4]),
Size::Word => self.write(bus, self.current_clock, addr, &data[2..4]),
Size::Long => self.write(bus, self.current_clock, addr, &data[0..4]),
}
}
pub(crate) fn read_data_sized<Bus, BusError>(&mut self, bus: &mut Bus, is_supervisor: bool, addr: M68kAddress, size: Size) -> Result<u32, M68kError<BusError>>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
{
self.start_request(is_supervisor, addr, size, MemAccess::Read, MemType::Data, false)?;
self.read_sized(bus, addr, size)
}
pub(crate) fn write_data_sized<Bus, BusError>(&mut self, bus: &mut Bus, is_supervisor: bool, addr: M68kAddress, size: Size, value: u32) -> Result<(), M68kError<BusError>>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
{
self.start_request(is_supervisor, addr, size, MemAccess::Write, MemType::Data, false)?;
self.write_sized(bus, addr, size, value)
}
pub(crate) fn read_instruction_word<Bus, BusError>(&mut self, bus: &mut Bus, is_supervisor: bool, addr: u32) -> Result<u16, M68kError<BusError>>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
{
self.request.instruction(is_supervisor, addr)?; self.request.instruction(is_supervisor, addr)?;
Ok(port.read_beu16(self.current_clock, addr as Address)?) Ok(self.read_sized(bus, addr, Size::Word)? as u16)
} }
pub(crate) fn read_instruction_long(&mut self, port: &mut BusPort, is_supervisor: bool, addr: u32) -> Result<u32, M68kError> { pub(crate) fn read_instruction_long<Bus, BusError>(&mut self, bus: &mut Bus, is_supervisor: bool, addr: u32) -> Result<u32, M68kError<BusError>>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
{
self.request.instruction(is_supervisor, addr)?; self.request.instruction(is_supervisor, addr)?;
Ok(port.read_beu32(self.current_clock, addr as Address)?) self.read_sized(bus, addr, Size::Long)
} }
pub(crate) fn start_request(&mut self, is_supervisor: bool, addr: u32, size: Size, access: MemAccess, mtype: MemType, i_n_bit: bool) -> Result<u32, M68kError> { pub(crate) fn start_request<BusError>(&mut self, is_supervisor: bool, addr: u32, size: Size, access: MemAccess, mtype: MemType, i_n_bit: bool) -> Result<u32, M68kError<BusError>> {
self.request.i_n_bit = i_n_bit; self.request.i_n_bit = i_n_bit;
self.request.code = match mtype { self.request.code = match mtype {
MemType::Program => FunctionCode::program(is_supervisor), MemType::Program => FunctionCode::program(is_supervisor),
@ -185,13 +248,9 @@ impl M68kBusPort {
validate_address(addr) validate_address(addr)
} }
} }
pub(crate) fn dump_memory(&mut self, port: &mut BusPort, addr: u32, length: usize) {
port.dump_memory(self.current_clock, addr as Address, length as u64);
}
} }
fn validate_address(addr: u32) -> Result<u32, M68kError> { fn validate_address<BusError>(addr: u32) -> Result<u32, M68kError<BusError>> {
if addr & 0x1 == 0 { if addr & 0x1 == 0 {
Ok(addr) Ok(addr)
} else { } else {
@ -199,22 +258,31 @@ fn validate_address(addr: u32) -> Result<u32, M68kError> {
} }
} }
/* pub fn dump_memory<Bus, Address, Instant>(bus: &mut Bus, clock: Instant, addr: Address, count: Address)
impl BusType for M68kBusPort { where
type Instant = Instant; Bus: BusAccess<Address, Instant>,
type Error = Error; Address: From<u32> + Into<u32> + Copy,
} Instant: Copy,
{
impl BusAccess<u32> for M68kBusPort { let mut addr = addr.into();
fn read(&mut self, now: Self::Instant, addr: Address, data: &mut [u8]) -> Result<usize, Self::Error> { let mut count = count.into();
self. while count > 0 {
} let mut line = format!("{:#010x}: ", addr);
fn write(&mut self, now: Self::Instant, addr: Address, data: &[u8]) -> Result<usize, Self::Error> {
let to = if count < 16 { count / 2 } else { 8 };
for _ in 0..to {
let word = bus.read_beu16(clock, Address::from(addr));
if word.is_err() {
println!("{}", line);
return;
}
write!(line, "{:#06x} ", word.unwrap()).unwrap();
addr += 2;
count -= 2;
}
println!("{}", line);
} }
} }
*/
/* /*
pub(crate) struct TargetAccess { pub(crate) struct TargetAccess {

View File

@ -0,0 +1,132 @@
use femtos::{Instant, Duration};
use emulator_hal::bus;
use moa_core::{System, Error, Address, Steppable, Interruptable, Addressable, Debuggable, Transmutable};
use crate::state::{M68k, M68kError};
use crate::decode::M68kDecoder;
use crate::execute::M68kCycle;
impl Steppable for M68k {
fn step(&mut self, system: &System) -> Result<Duration, Error> {
let cycle = M68kCycle::new(self, system.clock);
let mut bus = system.bus.borrow_mut();
let mut adapter: bus::BusAdapter<u32, u64, Instant, &mut dyn Addressable, Error> = bus::BusAdapter::new(
&mut *bus,
|addr| addr as u64,
|err| err,
);
let mut executor = cycle.begin(self, &mut adapter);
executor.check_breakpoints()?;
executor.step()?;
let interrupt = system.get_interrupt_controller().check();
if let (priority, Some(_)) = executor.check_pending_interrupts(interrupt)? {
log::debug!("interrupt: {:?} @ {} ns", priority, system.clock.as_duration().as_nanos());
system.get_interrupt_controller().acknowledge(priority as u8)?;
}
self.cycle = Some(executor.end());
Ok(self.last_cycle_duration())
}
fn on_error(&mut self, _system: &System) {
let mut output = String::with_capacity(256);
let _ = self.dump_state(&mut output);
println!("{}", output);
}
}
impl Interruptable for M68k { }
impl Transmutable for M68k {
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
Some(self)
}
fn as_interruptable(&mut self) -> Option<&mut dyn Interruptable> {
Some(self)
}
fn as_debuggable(&mut self) -> Option<&mut dyn Debuggable> {
Some(self)
}
}
impl<BusError> From<Error> for M68kError<BusError> {
fn from(err: Error) -> Self {
match err {
Error::Processor(ex) => M68kError::Interrupt(ex as u8),
Error::Breakpoint(_) => M68kError::Breakpoint,
Error::Other(msg) | Error::Assertion(msg) | Error::Emulator(_, msg) => M68kError::Other(msg.to_string()),
}
}
}
impl<BusError: bus::Error> From<M68kError<BusError>> for Error {
fn from(err: M68kError<BusError>) -> Self {
match err {
M68kError::Halted => Self::Other("cpu halted".to_string()),
M68kError::Exception(ex) => Self::Processor(ex as u32),
M68kError::Interrupt(num) => Self::Processor(num as u32),
M68kError::Breakpoint => Self::Breakpoint("breakpoint".to_string()),
M68kError::InvalidTarget(target) => Self::new(target.to_string()),
M68kError::BusError(msg) => Self::Other(format!("{:?}", msg)),
M68kError::Other(msg) => Self::Other(msg),
}
}
}
impl Debuggable for M68k {
fn add_breakpoint(&mut self, addr: Address) {
self.debugger.breakpoints.push(addr as u32);
}
fn remove_breakpoint(&mut self, addr: Address) {
if let Some(index) = self.debugger.breakpoints.iter().position(|a| *a == addr as u32) {
self.debugger.breakpoints.remove(index);
}
}
fn print_current_step(&mut self, _system: &System) -> Result<(), Error> {
// TODO this is called by the debugger, but should be called some other way
//let _ = self.decoder.decode_at(&mut self.bus, true, self.state.pc);
//self.decoder.dump_decoded(&mut self.bus);
//self.dump_state();
Ok(())
}
fn print_disassembly(&mut self, system: &System, addr: Address, count: usize) {
let mut decoder = M68kDecoder::new(self.info.chip, true, 0);
let mut bus = system.bus.borrow_mut();
let mut adapter: bus::BusAdapter<u32, u64, Instant, &mut dyn Addressable, Error> = bus::BusAdapter::new(
&mut *bus,
|addr| addr as u64,
|err| err,
);
decoder.dump_disassembly(&mut adapter, addr as u32, count as u32);
}
fn run_command(&mut self, system: &System, args: &[&str]) -> Result<bool, Error> {
match args[0] {
"ds" | "stack" | "dumpstack" => {
println!("Stack:");
for addr in &self.debugger.stack_tracer.calls {
println!(" {:08x}", system.bus.borrow_mut().read_beu32(system.clock, *addr as Address)?);
}
},
"so" | "stepout" => {
self.debugger.step_until_return = Some(self.debugger.stack_tracer.calls.len() - 1);
},
_ => { return Ok(true); },
}
Ok(false)
}
}

View File

@ -1,12 +1,8 @@
use std::rc::Rc; use core::fmt::{self, Write};
use std::cell::RefCell; use femtos::{Duration, Frequency};
use femtos::{Instant, Frequency};
use moa_core::{Address, Bus, BusPort};
use crate::debugger::M68kDebugger; use crate::debugger::M68kDebugger;
use crate::memory::M68kBusPort;
use crate::instructions::Target; use crate::instructions::Target;
use crate::execute::M68kCycle; use crate::execute::M68kCycle;
@ -81,7 +77,7 @@ impl From<M68kType> for CoreType {
} }
impl CpuInfo { impl CpuInfo {
fn from(cputype: M68kType, frequency: Frequency) -> Self { pub fn from_type(cputype: M68kType, frequency: Frequency) -> Self {
match cputype { match cputype {
M68kType::MC68008 => Self { M68kType::MC68008 => Self {
chip: cputype, chip: cputype,
@ -178,7 +174,7 @@ pub struct M68kState {
} }
#[derive(Clone, Debug, thiserror::Error)] #[derive(Clone, Debug, thiserror::Error)]
pub enum M68kError { pub enum M68kError<BusError> {
#[error("cpu halted")] #[error("cpu halted")]
Halted, Halted,
#[error("processor exception {0:?}")] #[error("processor exception {0:?}")]
@ -189,16 +185,35 @@ pub enum M68kError {
Breakpoint, Breakpoint,
#[error("invalid instruction target, direct value used as a pointer: {0:?}")] #[error("invalid instruction target, direct value used as a pointer: {0:?}")]
InvalidTarget(Target), InvalidTarget(Target),
#[error("bus error")]
BusError(BusError),
#[error("error: {0}")] #[error("error: {0}")]
Other(String), Other(String),
} }
#[derive(Clone)]
pub struct M68kStatistics {
pub cycle_number: usize,
pub last_update: usize,
pub last_time: std::time::SystemTime,
}
impl Default for M68kStatistics {
fn default() -> Self {
Self {
cycle_number: 0,
last_update: 0,
last_time: std::time::SystemTime::now(),
}
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct M68k { pub struct M68k {
pub info: CpuInfo, pub info: CpuInfo,
pub state: M68kState, pub state: M68kState,
pub debugger: M68kDebugger, pub debugger: M68kDebugger,
pub port: BusPort, pub stats: M68kStatistics,
pub cycle: Option<M68kCycle>, pub cycle: Option<M68kCycle>,
} }
@ -221,20 +236,51 @@ impl Default for M68kState {
} }
} }
impl M68kState {
pub fn dump_state<W: Write>(&self, writer: &mut W) -> Result<(), fmt::Error> {
writeln!(writer, "Status: {:?}", self.status)?;
writeln!(writer, "PC: {:#010x}", self.pc)?;
writeln!(writer, "SR: {:#06x}", self.sr)?;
for i in 0..7 {
writeln!(writer, "D{}: {:#010x} A{}: {:#010x}", i, self.d_reg[i as usize], i, self.a_reg[i as usize])?;
}
writeln!(writer, "D7: {:#010x} USP: {:#010x}", self.d_reg[7], self.usp)?;
writeln!(writer, " SSP: {:#010x}", self.ssp)?;
Ok(())
}
}
impl M68k { impl M68k {
pub fn new(info: CpuInfo, port: BusPort) -> M68k { pub fn new(info: CpuInfo) -> Self {
M68k { M68k {
info, info,
state: M68kState::default(), state: M68kState::default(),
debugger: M68kDebugger::default(), debugger: M68kDebugger::default(),
port, stats: Default::default(),
cycle: None, cycle: None,
} }
} }
pub fn from_type(cputype: M68kType, frequency: Frequency, bus: Rc<RefCell<Bus>>, addr_offset: Address) -> Self { pub fn from_type(cputype: M68kType, freq: Frequency) -> Self {
let info = CpuInfo::from(cputype, frequency); Self::new(CpuInfo::from_type(cputype, freq))
Self::new(info, BusPort::new(addr_offset, info.address_width as u8, info.data_width as u8, bus)) }
pub fn dump_state<W: Write>(&self, writer: &mut W) -> Result<(), fmt::Error> {
self.state.dump_state(writer)?;
if let Some(cycle) = self.cycle.as_ref() {
writeln!(writer, "Current Instruction: {:#010x} {:?}", cycle.decoder.start, cycle.decoder.instruction)?;
writeln!(writer)?;
}
//memory::dump_memory(&mut self.bus, self.cycle.current_clock, self.state.ssp, 0x40);
writeln!(writer)?;
Ok(())
}
#[inline]
pub fn last_cycle_duration(&self) -> Duration {
let clocks = self.cycle.as_ref().map(|cycle| cycle.timing.calculate_clocks()).unwrap_or(4);
self.info.frequency.period_duration() * clocks as u64
} }
} }

View File

@ -1,31 +1,30 @@
#[cfg(test)] #[cfg(test)]
mod decode_unit_tests { mod decode_unit_tests {
use std::rc::Rc;
use std::cell::RefCell;
use femtos::Instant; use femtos::Instant;
use emulator_hal::bus::BusAccess;
use moa_core::{Bus, BusPort, Address, Addressable, MemoryBlock, Device}; use emulator_hal_memory::MemoryBlock;
use crate::M68kType; use crate::M68kType;
use crate::instructions::{Target, Size, XRegister, BaseRegister, IndexRegister}; use crate::instructions::{Target, Size, XRegister, BaseRegister, IndexRegister};
use crate::decode::M68kDecoder; use crate::decode::{M68kDecoder, InstructionDecoding};
use crate::memory::M68kBusPort; use crate::memory::M68kBusPort;
const INIT_ADDR: Address = 0x00000000; const INIT_ADDR: u32 = 0x00000000;
fn init_decode_test(cputype: M68kType) -> (M68kBusPort, M68kDecoder) { fn run_decode_test<F>(cputype: M68kType, mut test_func: F)
let bus = Rc::new(RefCell::new(Bus::default())); where
let mem = MemoryBlock::new(vec![0; 0x0000100]); F: FnMut(&mut InstructionDecoding<'_, MemoryBlock<u32, Instant>>),
bus.borrow_mut().insert(0x00000000, Device::new(mem)); {
let mut memory = MemoryBlock::from(vec![0; 0x0000100]);
let port = if cputype <= M68kType::MC68010 { let mut decoder = M68kDecoder::new(cputype, true, 0);
M68kBusPort::new(BusPort::new(0, 24, 16, bus)) let mut decoding = InstructionDecoding {
} else { bus: &mut memory,
M68kBusPort::new(BusPort::new(0, 32, 32, bus)) memory: &mut M68kBusPort::default(),
decoder: &mut decoder,
}; };
let decoder = M68kDecoder::new(cputype, true, 0);
(port, decoder) test_func(&mut decoding);
} }
// //
@ -34,250 +33,252 @@ mod decode_unit_tests {
#[test] #[test]
fn target_direct_d() { fn target_direct_d() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68010); run_decode_test(M68kType::MC68010, |decoder| {
let size = Size::Word;
let size = Size::Word; let target = decoder.get_mode_as_target(0b000, 0b001, Some(size)).unwrap();
assert_eq!(target, Target::DirectDReg(1));
let target = decoder.get_mode_as_target(&mut port, 0b000, 0b001, Some(size)).unwrap(); });
assert_eq!(target, Target::DirectDReg(1));
} }
#[test] #[test]
fn target_direct_a() { fn target_direct_a() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68010); run_decode_test(M68kType::MC68010, |decoder| {
let size = Size::Word;
let size = Size::Word; let target = decoder.get_mode_as_target(0b001, 0b010, Some(size)).unwrap();
assert_eq!(target, Target::DirectAReg(2));
let target = decoder.get_mode_as_target(&mut port, 0b001, 0b010, Some(size)).unwrap(); });
assert_eq!(target, Target::DirectAReg(2));
} }
#[test] #[test]
fn target_indirect_a() { fn target_indirect_a() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68010); run_decode_test(M68kType::MC68010, |decoder| {
let size = Size::Long;
let expected = 0x12345678;
let size = Size::Long; decoder.bus.write_beu32(Instant::START, INIT_ADDR, expected).unwrap();
let expected = 0x12345678;
port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap(); let target = decoder.get_mode_as_target(0b010, 0b010, Some(size)).unwrap();
assert_eq!(target, Target::IndirectAReg(2));
let target = decoder.get_mode_as_target(&mut port, 0b010, 0b010, Some(size)).unwrap(); });
assert_eq!(target, Target::IndirectAReg(2));
} }
#[test] #[test]
fn target_indirect_a_inc() { fn target_indirect_a_inc() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68010); run_decode_test(M68kType::MC68010, |decoder| {
let size = Size::Long;
let expected = 0x12345678;
let size = Size::Long; decoder.bus.write_beu32(Instant::START, INIT_ADDR, expected).unwrap();
let expected = 0x12345678;
port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap(); let target = decoder.get_mode_as_target(0b011, 0b010, Some(size)).unwrap();
assert_eq!(target, Target::IndirectARegInc(2));
let target = decoder.get_mode_as_target(&mut port, 0b011, 0b010, Some(size)).unwrap(); });
assert_eq!(target, Target::IndirectARegInc(2));
} }
#[test] #[test]
fn target_indirect_a_dec() { fn target_indirect_a_dec() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68010); run_decode_test(M68kType::MC68010, |decoder| {
let size = Size::Long;
let expected = 0x12345678;
let size = Size::Long; decoder.bus.write_beu32(Instant::START, INIT_ADDR, expected).unwrap();
let expected = 0x12345678;
port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap(); let target = decoder.get_mode_as_target(0b100, 0b010, Some(size)).unwrap();
assert_eq!(target, Target::IndirectARegDec(2));
let target = decoder.get_mode_as_target(&mut port, 0b100, 0b010, Some(size)).unwrap(); });
assert_eq!(target, Target::IndirectARegDec(2));
} }
#[test] #[test]
fn target_indirect_a_reg_offset() { fn target_indirect_a_reg_offset() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68010); run_decode_test(M68kType::MC68010, |decoder| {
let size = Size::Long;
let offset = -8;
let size = Size::Long; decoder.bus.write_beu16(Instant::START, INIT_ADDR, (offset as i16) as u16).unwrap();
let offset = -8;
port.port.write_beu16(Instant::START, INIT_ADDR, (offset as i16) as u16).unwrap(); let target = decoder.get_mode_as_target(0b101, 0b100, Some(size)).unwrap();
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(4), None, offset));
let target = decoder.get_mode_as_target(&mut port, 0b101, 0b100, Some(size)).unwrap(); });
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(4), None, offset));
} }
#[test] #[test]
fn target_indirect_a_reg_brief_extension_word() { fn target_indirect_a_reg_brief_extension_word() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68010); run_decode_test(M68kType::MC68010, |decoder| {
let size = Size::Long;
let offset = -8;
let brief_extension = 0x3800 | (((offset as i8) as u8) as u16);
let size = Size::Long; decoder.bus.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
let offset = -8; decoder.bus.write_beu16(Instant::START, INIT_ADDR + 2, (offset as i16) as u16).unwrap();
let brief_extension = 0x3800 | (((offset as i8) as u8) as u16);
port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap(); let target = decoder.get_mode_as_target(0b110, 0b010, Some(size)).unwrap();
port.port.write_beu16(Instant::START, INIT_ADDR + 2, (offset as i16) as u16).unwrap(); assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(2), Some(IndexRegister { xreg: XRegister::DReg(3), scale: 0, size: size }), offset));
});
let target = decoder.get_mode_as_target(&mut port, 0b110, 0b010, Some(size)).unwrap();
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(2), Some(IndexRegister { xreg: XRegister::DReg(3), scale: 0, size: size }), offset));
} }
#[test] #[test]
fn target_indirect_a_reg_full_extension_word() { fn target_indirect_a_reg_full_extension_word() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68020); run_decode_test(M68kType::MC68020, |decoder| {
let size = Size::Word;
let offset = -1843235 as i32;
let brief_extension = 0xF330;
let size = Size::Word; decoder.bus.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
let offset = -1843235 as i32; decoder.bus.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap();
let brief_extension = 0xF330;
port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap(); let target = decoder.get_mode_as_target(0b110, 0b010, Some(size)).unwrap();
port.port.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap(); assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(2), Some(IndexRegister { xreg: XRegister::AReg(7), scale: 1, size: size }), offset));
});
let target = decoder.get_mode_as_target(&mut port, 0b110, 0b010, Some(size)).unwrap();
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(2), Some(IndexRegister { xreg: XRegister::AReg(7), scale: 1, size: size }), offset));
} }
#[test] #[test]
fn target_indirect_a_reg_full_extension_word_no_base() { fn target_indirect_a_reg_full_extension_word_no_base() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68020); run_decode_test(M68kType::MC68020, |decoder| {
let size = Size::Word;
let offset = -1843235 as i32;
let brief_extension = 0xF3B0;
let size = Size::Word; decoder.bus.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
let offset = -1843235 as i32; decoder.bus.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap();
let brief_extension = 0xF3B0;
port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap(); let target = decoder.get_mode_as_target(0b110, 0b010, Some(size)).unwrap();
port.port.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap(); assert_eq!(target, Target::IndirectRegOffset(BaseRegister::None, Some(IndexRegister { xreg: XRegister::AReg(7), scale: 1, size: size }), offset));
});
let target = decoder.get_mode_as_target(&mut port, 0b110, 0b010, Some(size)).unwrap();
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::None, Some(IndexRegister { xreg: XRegister::AReg(7), scale: 1, size: size }), offset));
} }
#[test] #[test]
fn target_indirect_a_reg_full_extension_word_no_index() { fn target_indirect_a_reg_full_extension_word_no_index() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68020); run_decode_test(M68kType::MC68020, |decoder| {
let size = Size::Word;
let offset = -1843235 as i32;
let brief_extension = 0xF370;
let size = Size::Word; decoder.bus.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
let offset = -1843235 as i32; decoder.bus.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap();
let brief_extension = 0xF370;
port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap(); let target = decoder.get_mode_as_target(0b110, 0b010, Some(size)).unwrap();
port.port.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap(); assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(2), None, offset));
});
let target = decoder.get_mode_as_target(&mut port, 0b110, 0b010, Some(size)).unwrap();
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(2), None, offset));
} }
#[test] #[test]
fn target_indirect_pc_offset() { fn target_indirect_pc_offset() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68010); run_decode_test(M68kType::MC68010, |decoder| {
let size = Size::Long;
let offset = -8;
let size = Size::Long; decoder.bus.write_beu16(Instant::START, INIT_ADDR, (offset as i16) as u16).unwrap();
let offset = -8;
port.port.write_beu16(Instant::START, INIT_ADDR, (offset as i16) as u16).unwrap(); let target = decoder.get_mode_as_target(0b111, 0b010, Some(size)).unwrap();
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, None, offset));
let target = decoder.get_mode_as_target(&mut port, 0b111, 0b010, Some(size)).unwrap(); });
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, None, offset));
} }
#[test] #[test]
fn target_indirect_pc_brief_extension_word() { fn target_indirect_pc_brief_extension_word() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68010); run_decode_test(M68kType::MC68010, |decoder| {
let size = Size::Word;
let offset = -8;
let brief_extension = 0x3000 | (((offset as i8) as u8) as u16);
let size = Size::Word; decoder.bus.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
let offset = -8; decoder.bus.write_beu16(Instant::START, INIT_ADDR + 2, (offset as i16) as u16).unwrap();
let brief_extension = 0x3000 | (((offset as i8) as u8) as u16);
port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap(); let target = decoder.get_mode_as_target(0b111, 0b011, Some(size)).unwrap();
port.port.write_beu16(Instant::START, INIT_ADDR + 2, (offset as i16) as u16).unwrap(); assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, Some(IndexRegister { xreg: XRegister::DReg(3), scale: 0, size: size }), offset));
});
let target = decoder.get_mode_as_target(&mut port, 0b111, 0b011, Some(size)).unwrap();
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, Some(IndexRegister { xreg: XRegister::DReg(3), scale: 0, size: size }), offset));
} }
#[test] #[test]
fn target_indirect_pc_full_extension_word() { fn target_indirect_pc_full_extension_word() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68020); run_decode_test(M68kType::MC68020, |decoder| {
let size = Size::Word;
let offset = -1843235 as i32;
let brief_extension = 0xF330;
let size = Size::Word; decoder.bus.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
let offset = -1843235 as i32; decoder.bus.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap();
let brief_extension = 0xF330;
port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap(); let target = decoder.get_mode_as_target(0b111, 0b011, Some(size)).unwrap();
port.port.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap(); assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, Some(IndexRegister { xreg: XRegister::AReg(7), scale: 1, size: size }), offset));
});
let target = decoder.get_mode_as_target(&mut port, 0b111, 0b011, Some(size)).unwrap();
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, Some(IndexRegister { xreg: XRegister::AReg(7), scale: 1, size: size }), offset));
} }
#[test] #[test]
fn target_indirect_immediate_word() { fn target_indirect_immediate_word() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68010); run_decode_test(M68kType::MC68010, |decoder| {
let size = Size::Word;
let expected = 0x1234;
let size = Size::Word; decoder.bus.write_beu16(Instant::START, INIT_ADDR, expected as u16).unwrap();
let expected = 0x1234;
port.port.write_beu16(Instant::START, INIT_ADDR, expected as u16).unwrap(); let target = decoder.get_mode_as_target(0b111, 0b000, Some(size)).unwrap();
assert_eq!(target, Target::IndirectMemory(expected, Size::Word));
let target = decoder.get_mode_as_target(&mut port, 0b111, 0b000, Some(size)).unwrap(); });
assert_eq!(target, Target::IndirectMemory(expected, Size::Word));
} }
#[test] #[test]
fn target_indirect_immediate_long() { fn target_indirect_immediate_long() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68010); run_decode_test(M68kType::MC68010, |decoder| {
let size = Size::Word;
let expected = 0x12345678;
let size = Size::Word; decoder.bus.write_beu32(Instant::START, INIT_ADDR, expected).unwrap();
let expected = 0x12345678;
port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap(); let target = decoder.get_mode_as_target(0b111, 0b001, Some(size)).unwrap();
assert_eq!(target, Target::IndirectMemory(expected, Size::Long));
let target = decoder.get_mode_as_target(&mut port, 0b111, 0b001, Some(size)).unwrap(); });
assert_eq!(target, Target::IndirectMemory(expected, Size::Long));
} }
#[test] #[test]
fn target_immediate() { fn target_immediate() {
let (mut port, mut decoder) = init_decode_test(M68kType::MC68010); run_decode_test(M68kType::MC68010, |decoder| {
let size = Size::Word;
let expected = 0x1234;
let size = Size::Word; decoder.bus.write_beu16(Instant::START, INIT_ADDR, expected as u16).unwrap();
let expected = 0x1234;
port.port.write_beu16(Instant::START, INIT_ADDR, expected as u16).unwrap(); let target = decoder.get_mode_as_target(0b111, 0b100, Some(size)).unwrap();
assert_eq!(target, Target::Immediate(expected));
let target = decoder.get_mode_as_target(&mut port, 0b111, 0b100, Some(size)).unwrap(); });
assert_eq!(target, Target::Immediate(expected));
} }
} }
#[cfg(test)] #[cfg(test)]
mod execute_unit_tests { mod execute_unit_tests {
use femtos::{Instant, Frequency}; use femtos::{Instant, Frequency};
use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Steppable, Device}; use emulator_hal::bus::BusAccess;
use emulator_hal::step::Step;
use emulator_hal_memory::MemoryBlock;
use crate::{M68k, M68kType}; use crate::{M68k, M68kType};
use crate::execute::{Used, M68kCycle, M68kCycleExecutor}; use crate::execute::{Used, M68kCycle, M68kCycleExecutor};
use crate::instructions::{Instruction, Target, Size}; use crate::instructions::{Instruction, Target, Size};
const INIT_STACK: Address = 0x00002000; const INIT_STACK: u32 = 0x00002000;
const INIT_ADDR: Address = 0x00000010; const INIT_ADDR: u32 = 0x00000010;
#[allow(clippy::uninit_vec)]
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(M68kCycleExecutor), F: FnMut(M68kCycleExecutor<&mut MemoryBlock<u32, Instant>>),
{ {
let mut system = System::default();
// Insert basic initialization // Insert basic initialization
let data = vec![0; 0x00100000]; let len = 0x10_0000;
let mem = MemoryBlock::new(data); let mut data = Vec::with_capacity(len);
system.add_addressable_device(0x00000000, Device::new(mem)).unwrap(); unsafe { data.set_len(len); }
system.get_bus().write_beu32(system.clock, 0, INIT_STACK as u32).unwrap(); let mut memory = MemoryBlock::from(data);
system.get_bus().write_beu32(system.clock, 4, INIT_ADDR as u32).unwrap(); memory.write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap();
memory.write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap();
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));
cpu.step(&system).unwrap(); cpu.step(Instant::START, &mut memory).unwrap();
let mut cycle = M68kCycle::new(&mut cpu, system.clock); let cycle = M68kCycle::new(&mut cpu, Instant::START);
let mut executor = cycle.begin(&mut cpu);
let mut executor = cycle.begin(&mut cpu, &mut memory);
executor.cycle.decoder.init(true, executor.state.pc); executor.cycle.decoder.init(true, executor.state.pc);
assert_eq!(executor.state.pc, INIT_ADDR as u32); assert_eq!(executor.state.pc, INIT_ADDR as u32);
assert_eq!(executor.state.ssp, INIT_STACK as u32); assert_eq!(executor.state.ssp, INIT_STACK as u32);
@ -322,7 +323,7 @@ mod execute_unit_tests {
let size = Size::Long; let size = Size::Long;
let expected = 0x12345678; let expected = 0x12345678;
let target = Target::IndirectAReg(2); let target = Target::IndirectAReg(2);
cycle.port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap(); cycle.bus.write_beu32(Instant::START, INIT_ADDR, expected).unwrap();
cycle.state.a_reg[2] = INIT_ADDR as u32; cycle.state.a_reg[2] = INIT_ADDR as u32;
let result = cycle.get_target_value(target, size, Used::Once).unwrap(); let result = cycle.get_target_value(target, size, Used::Once).unwrap();
@ -336,7 +337,7 @@ mod execute_unit_tests {
let size = Size::Long; let size = Size::Long;
let expected = 0x12345678; let expected = 0x12345678;
let target = Target::IndirectARegInc(2); let target = Target::IndirectARegInc(2);
cycle.port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap(); cycle.bus.write_beu32(Instant::START, INIT_ADDR, expected).unwrap();
cycle.state.a_reg[2] = INIT_ADDR as u32; cycle.state.a_reg[2] = INIT_ADDR as u32;
let result = cycle.get_target_value(target, size, Used::Once).unwrap(); let result = cycle.get_target_value(target, size, Used::Once).unwrap();
@ -351,7 +352,7 @@ mod execute_unit_tests {
let size = Size::Long; let size = Size::Long;
let expected = 0x12345678; let expected = 0x12345678;
let target = Target::IndirectARegDec(2); let target = Target::IndirectARegDec(2);
cycle.port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap(); cycle.bus.write_beu32(Instant::START, INIT_ADDR, expected).unwrap();
cycle.state.a_reg[2] = (INIT_ADDR as u32) + 4; cycle.state.a_reg[2] = (INIT_ADDR as u32) + 4;
let result = cycle.get_target_value(target, size, Used::Once).unwrap(); let result = cycle.get_target_value(target, size, Used::Once).unwrap();
@ -374,5 +375,3 @@ mod execute_unit_tests {
}); });
} }
} }

View File

@ -9,6 +9,9 @@ pub struct M68kInstructionTiming {
pub cputype: M68kType, pub cputype: M68kType,
pub bus_size: Size, pub bus_size: Size,
pub branched: bool,
pub reps: u16,
pub accesses: u8, pub accesses: u8,
pub internal: u8, pub internal: u8,
pub on_branch: u8, pub on_branch: u8,
@ -22,6 +25,9 @@ impl M68kInstructionTiming {
cputype, cputype,
bus_size, bus_size,
branched: false,
reps: 0,
accesses: 0, accesses: 0,
internal: 0, internal: 0,
on_branch: 0, on_branch: 0,
@ -338,12 +344,27 @@ impl M68kInstructionTiming {
self.add_internal(4) self.add_internal(4)
} }
pub fn calculate_clocks(&self, branched: bool, reps: u16) -> ClockCycles { pub fn performed_reset(&mut self) {
self.internal = 0;
self.accesses = 4;
self.branched = false;
self.reps = 0;
}
pub fn increase_reps(&mut self, reps: u16) {
self.reps += reps;
}
pub fn branch_taken(&mut self) {
self.branched = true;
}
pub fn calculate_clocks(&self) -> ClockCycles {
//println!("{:?}", self); //println!("{:?}", self);
(self.accesses as ClockCycles * 4) (self.accesses as ClockCycles * 4)
+ self.internal as ClockCycles + self.internal as ClockCycles
+ (if branched { self.on_branch as ClockCycles } else { 0 }) + (if self.branched { self.on_branch as ClockCycles } else { 0 })
+ self.per_rep as ClockCycles * reps + self.per_rep as ClockCycles * self.reps
} }
#[inline(always)] #[inline(always)]

View File

@ -1,15 +1,15 @@
use femtos::{Instant, Frequency}; use femtos::{Instant, Frequency};
use emulator_hal::bus::BusAccess;
use emulator_hal_memory::MemoryBlock;
use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Device}; use moa_m68k::{M68k, M68kType, M68kAddress};
use moa_m68k::{M68k, M68kType};
use moa_m68k::instructions::{Instruction, Target, Size, Sign, XRegister, BaseRegister, IndexRegister, Direction}; use moa_m68k::instructions::{Instruction, Target, Size, Sign, XRegister, BaseRegister, IndexRegister, Direction};
use moa_m68k::assembler::M68kAssembler; use moa_m68k::assembler::M68kAssembler;
use moa_m68k::execute::M68kCycle; use moa_m68k::execute::M68kCycle;
const INIT_STACK: Address = 0x00002000; const INIT_STACK: M68kAddress = 0x00002000;
const INIT_ADDR: Address = 0x00000010; const INIT_ADDR: M68kAddress = 0x00000010;
struct TestCase { struct TestCase {
cpu: M68kType, cpu: M68kType,
@ -17,6 +17,7 @@ struct TestCase {
ins: Option<Instruction>, ins: Option<Instruction>,
} }
#[rustfmt::skip]
const DECODE_TESTS: &'static [TestCase] = &[ const DECODE_TESTS: &'static [TestCase] = &[
// MC68000 // MC68000
TestCase { cpu: M68kType::MC68000, data: &[0x4e71], ins: Some(Instruction::NOP) }, TestCase { cpu: M68kType::MC68000, data: &[0x4e71], ins: Some(Instruction::NOP) },
@ -64,47 +65,47 @@ const DECODE_TESTS: &'static [TestCase] = &[
]; ];
fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, System) { fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock<u32, Instant>) {
let mut system = System::default();
// Insert basic initialization // Insert basic initialization
let data = vec![0; 0x00100000]; let len = 0x2000;
let mem = MemoryBlock::new(data); let mut data = Vec::with_capacity(len);
system.add_addressable_device(0x00000000, Device::new(mem)).unwrap(); unsafe { data.set_len(len); }
system.get_bus().write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap(); let mut memory = MemoryBlock::from(data);
system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap(); memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
memory.write_beu32(Instant::START, 4, INIT_ADDR).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));
//cpu.reset_cpu().unwrap(); let cycle = M68kCycle::new(&cpu, Instant::START);
assert_eq!(cpu.state.pc, INIT_ADDR as u32); (cpu, cycle, memory)
assert_eq!(cpu.state.ssp, INIT_STACK as u32);
let cycle = M68kCycle::new(&cpu, system.clock);
assert_eq!(cycle.decoder.start, INIT_ADDR as u32);
assert_eq!(cycle.decoder.instruction, Instruction::NOP);
(cpu, cycle, system)
} }
fn load_memory(system: &System, data: &[u16]) { fn load_memory<Bus: BusAccess<u32, Instant>>(memory: &mut Bus, data: &[u16]) {
let mut addr = INIT_ADDR; let mut addr = INIT_ADDR;
for word in data { for word in data {
system.get_bus().write_beu16(system.clock, addr, *word).unwrap(); memory.write_beu16(Instant::START, addr, *word).unwrap();
addr += 2; addr += 2;
} }
} }
fn run_decode_test(case: &TestCase) { fn run_decode_test(case: &TestCase) {
let (mut cpu, cycle, system) = init_decode_test(case.cpu); let (mut cpu, cycle, mut memory) = init_decode_test(case.cpu);
load_memory(&system, case.data); load_memory(&mut memory, case.data);
match &case.ins { match &case.ins {
Some(ins) => { Some(ins) => {
let mut executor = cycle.begin(&mut cpu); let mut executor = cycle.begin(&mut cpu, &mut memory);
executor.reset_cpu().unwrap();
assert_eq!(executor.state.pc, INIT_ADDR);
assert_eq!(executor.state.ssp, INIT_STACK);
executor.decode_next().unwrap(); executor.decode_next().unwrap();
assert_eq!(executor.cycle.decoder.instruction, ins.clone()); assert_eq!(executor.cycle.decoder.instruction, ins.clone());
}, },
None => { None => {
let mut executor = cycle.begin(&mut cpu); let mut executor = cycle.begin(&mut cpu, &mut memory);
executor.reset_cpu().unwrap();
assert_eq!(executor.state.pc, INIT_ADDR);
assert_eq!(executor.state.ssp, INIT_STACK);
let next = executor.decode_next(); let next = executor.decode_next();
println!("{:?}", executor.cycle.decoder.instruction); println!("{:?}", executor.cycle.decoder.instruction);
assert!(next.is_err()); assert!(next.is_err());
@ -121,6 +122,7 @@ pub fn run_decode_tests() {
} }
#[test] #[test]
#[ignore]
pub fn run_assembler_tests() { pub fn run_assembler_tests() {
let mut tests = 0; let mut tests = 0;
let mut errors = 0; let mut errors = 0;

View File

@ -1,15 +1,16 @@
use femtos::{Instant, Frequency}; use femtos::{Instant, Frequency};
use emulator_hal::bus::BusAccess;
use emulator_hal::step::Step;
use emulator_hal_memory::MemoryBlock;
use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Steppable, Device}; use moa_m68k::{M68k, M68kType, M68kAddress};
use moa_m68k::{M68k, M68kType};
use moa_m68k::state::M68kState; use moa_m68k::state::M68kState;
use moa_m68k::execute::{M68kCycle, M68kCycleExecutor}; use moa_m68k::execute::{M68kCycle, M68kCycleExecutor};
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: M68kAddress = 0x00002000;
const INIT_ADDR: Address = 0x00000010; const INIT_ADDR: M68kAddress = 0x00000010;
const MEM_ADDR: u32 = 0x00001234; const MEM_ADDR: u32 = 0x00001234;
@ -35,30 +36,30 @@ struct TestCase {
} }
#[allow(clippy::uninit_vec)]
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(M68kCycleExecutor, System), F: FnMut(M68kCycleExecutor<&mut MemoryBlock<u32, Instant>>),
{ {
let mut system = System::default();
// Insert basic initialization // Insert basic initialization
let data = vec![0; 0x00100000]; let len = 0x10_0000;
let mem = MemoryBlock::new(data); let mut data = Vec::with_capacity(len);
system.add_addressable_device(0x00000000, Device::new(mem)).unwrap(); unsafe { data.set_len(len); }
system.get_bus().write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap(); let mut memory = MemoryBlock::from(data);
system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap(); memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
memory.write_beu32(Instant::START, 4, INIT_ADDR).unwrap();
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));
cpu.step(&system).unwrap(); cpu.step(Instant::START, &mut memory).unwrap();
let cycle = M68kCycle::new(&cpu, system.clock); let cycle = M68kCycle::new(&cpu, Instant::START);
let mut executor = cycle.begin(&mut cpu); let executor = cycle.begin(&mut cpu, &mut memory);
assert_eq!(executor.state.pc, INIT_ADDR as u32); assert_eq!(executor.state.pc, INIT_ADDR);
assert_eq!(executor.state.ssp, INIT_STACK as u32); assert_eq!(executor.state.ssp, INIT_STACK);
assert_eq!(executor.cycle.decoder.instruction, Instruction::NOP); assert_eq!(executor.cycle.decoder.instruction, Instruction::NOP);
test_func(executor, system) test_func(executor)
} }
fn build_state(state: &TestState) -> M68kState { fn build_state(state: &TestState) -> M68kState {
@ -74,19 +75,19 @@ fn build_state(state: &TestState) -> M68kState {
new_state new_state
} }
fn load_memory(system: &System, data: &[u16]) { fn load_memory<Bus: BusAccess<u32, Instant>>(bus: &mut Bus, data: &[u16]) {
for i in 0..data.len() { for i in 0..data.len() {
system.get_bus().write_beu16(system.clock, (i << 1) as Address, data[i]).unwrap(); bus.write_beu16(Instant::START, (i << 1) as u32, data[i]).unwrap();
} }
} }
fn run_test(case: &TestCase) { fn run_test(case: &TestCase) {
run_execute_test(case.cputype, |mut executor, system| { run_execute_test(case.cputype, |mut executor| {
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(); executor.bus.write_beu32(Instant::START, MEM_ADDR, case.init.mem).unwrap();
load_memory(&system, case.data); load_memory(&mut executor.bus, case.data);
*executor.state = init_state; *executor.state = init_state;
executor.decode_next().unwrap(); executor.decode_next().unwrap();
@ -95,7 +96,7 @@ fn run_test(case: &TestCase) {
executor.execute_current().unwrap(); executor.execute_current().unwrap();
assert_eq!(*executor.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 = executor.bus.read_beu32(Instant::START, MEM_ADDR).unwrap();
assert_eq!(mem, case.fini.mem); assert_eq!(mem, case.fini.mem);
}); });
} }
@ -109,6 +110,7 @@ pub fn run_execute_tests() {
} }
#[test] #[test]
#[ignore]
pub fn run_assembler_tests() { pub fn run_assembler_tests() {
use moa_m68k::assembler::M68kAssembler; use moa_m68k::assembler::M68kAssembler;
@ -150,6 +152,7 @@ fn format_hex(data: &[u16]) -> String {
.join(", ") .join(", ")
} }
#[rustfmt::skip]
const TEST_CASES: &'static [TestCase] = &[ const TEST_CASES: &'static [TestCase] = &[
TestCase { TestCase {
name: "nop", name: "nop",
@ -216,7 +219,7 @@ const TEST_CASES: &'static [TestCase] = &[
fini: TestState { pc: 0x00000002, ssp: 0x00000000, usp: 0x00000000, d0: 0x000000FE, d1: 0x0000007F, a0: 0x00000000, a1: 0x00000000, sr: 0x270A, mem: 0x00000000 }, fini: TestState { pc: 0x00000002, ssp: 0x00000000, usp: 0x00000000, d0: 0x000000FE, d1: 0x0000007F, a0: 0x00000000, a1: 0x00000000, sr: 0x270A, mem: 0x00000000 },
}, },
TestCase { TestCase {
name: "addx with extend", name: "addx with extend; zero flag not set",
ins: Instruction::ADDX(Target::DirectDReg(1), Target::DirectDReg(0), Size::Byte), ins: Instruction::ADDX(Target::DirectDReg(1), Target::DirectDReg(0), Size::Byte),
data: &[ 0xD101 ], data: &[ 0xD101 ],
cputype: M68kType::MC68010, cputype: M68kType::MC68010,
@ -224,11 +227,27 @@ const TEST_CASES: &'static [TestCase] = &[
fini: TestState { pc: 0x00000002, ssp: 0x00000000, usp: 0x00000000, d0: 0x000000FF, d1: 0x0000007F, a0: 0x00000000, a1: 0x00000000, sr: 0x270A, mem: 0x00000000 }, fini: TestState { pc: 0x00000002, ssp: 0x00000000, usp: 0x00000000, d0: 0x000000FF, d1: 0x0000007F, a0: 0x00000000, a1: 0x00000000, sr: 0x270A, mem: 0x00000000 },
}, },
TestCase { TestCase {
name: "addx with extend and carry", name: "addx with extend; zero flag set",
ins: Instruction::ADDX(Target::DirectDReg(1), Target::DirectDReg(0), Size::Byte),
data: &[ 0xD101 ],
cputype: M68kType::MC68010,
init: TestState { pc: 0x00000000, ssp: 0x00000000, usp: 0x00000000, d0: 0x0000007F, d1: 0x0000007F, a0: 0x00000000, a1: 0x00000000, sr: 0x2714, mem: 0x00000000 },
fini: TestState { pc: 0x00000002, ssp: 0x00000000, usp: 0x00000000, d0: 0x000000FF, d1: 0x0000007F, a0: 0x00000000, a1: 0x00000000, sr: 0x270A, mem: 0x00000000 },
},
TestCase {
name: "addx with extend and carry; zero flag not set",
ins: Instruction::ADDX(Target::DirectDReg(1), Target::DirectDReg(0), Size::Byte), ins: Instruction::ADDX(Target::DirectDReg(1), Target::DirectDReg(0), Size::Byte),
data: &[ 0xD101 ], data: &[ 0xD101 ],
cputype: M68kType::MC68010, cputype: M68kType::MC68010,
init: TestState { pc: 0x00000000, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000080, d1: 0x0000007F, a0: 0x00000000, a1: 0x00000000, sr: 0x2710, mem: 0x00000000 }, init: TestState { pc: 0x00000000, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000080, d1: 0x0000007F, a0: 0x00000000, a1: 0x00000000, sr: 0x2710, mem: 0x00000000 },
fini: TestState { pc: 0x00000002, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x0000007F, a0: 0x00000000, a1: 0x00000000, sr: 0x2711, mem: 0x00000000 },
},
TestCase {
name: "addx with extend and carry; zero flag set",
ins: Instruction::ADDX(Target::DirectDReg(1), Target::DirectDReg(0), Size::Byte),
data: &[ 0xD101 ],
cputype: M68kType::MC68010,
init: TestState { pc: 0x00000000, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000080, d1: 0x0000007F, a0: 0x00000000, a1: 0x00000000, sr: 0x2714, mem: 0x00000000 },
fini: TestState { pc: 0x00000002, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x0000007F, a0: 0x00000000, a1: 0x00000000, sr: 0x2715, mem: 0x00000000 }, fini: TestState { pc: 0x00000002, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x0000007F, a0: 0x00000000, a1: 0x00000000, sr: 0x2715, mem: 0x00000000 },
}, },
TestCase { TestCase {
@ -237,7 +256,15 @@ const TEST_CASES: &'static [TestCase] = &[
data: &[ 0x027C, 0xF8FF ], data: &[ 0x027C, 0xF8FF ],
cputype: M68kType::MC68010, cputype: M68kType::MC68010,
init: TestState { pc: 0x00000000, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x00000000, a0: 0x00000000, a1: 0x00000000, sr: 0xA7AA, mem: 0x00000000 }, init: TestState { pc: 0x00000000, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x00000000, a0: 0x00000000, a1: 0x00000000, sr: 0xA7AA, mem: 0x00000000 },
fini: TestState { pc: 0x00000004, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x00000000, a0: 0x00000000, a1: 0x00000000, sr: 0xA0AA, mem: 0x00000000 }, fini: TestState { pc: 0x00000004, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x00000000, a0: 0x00000000, a1: 0x00000000, sr: 0xA00A, mem: 0x00000000 },
},
TestCase {
name: "andi with sr 2",
ins: Instruction::ANDtoSR(0xF8FF),
data: &[ 0x027C, 0xF8FF ],
cputype: M68kType::MC68010,
init: TestState { pc: 0x00000000, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x00000000, a0: 0x00000000, a1: 0x00000000, sr: 0xA7FA, mem: 0x00000000 },
fini: TestState { pc: 0x00000004, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x00000000, a0: 0x00000000, a1: 0x00000000, sr: 0xA01A, mem: 0x00000000 },
}, },
TestCase { TestCase {
name: "asl", name: "asl",
@ -558,13 +585,14 @@ const TEST_CASES: &'static [TestCase] = &[
init: TestState { pc: 0x00000000, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x00000000, a0: MEM_ADDR, a1: 0x00000000, sr: 0x27FF, mem: 0xFF55FFAA }, init: TestState { pc: 0x00000000, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x00000000, a0: MEM_ADDR, a1: 0x00000000, sr: 0x27FF, mem: 0xFF55FFAA },
fini: TestState { pc: 0x00000004, ssp: 0x00000000, usp: 0x00000000, d0: 0x000055AA, d1: 0x00000000, a0: MEM_ADDR, a1: 0x00000000, sr: 0x27FF, mem: 0xFF55FFAA }, fini: TestState { pc: 0x00000004, ssp: 0x00000000, usp: 0x00000000, d0: 0x000055AA, d1: 0x00000000, a0: MEM_ADDR, a1: 0x00000000, sr: 0x27FF, mem: 0xFF55FFAA },
}, },
// TODO not sure if these cases are correct
TestCase { TestCase {
name: "movep long from even memory upper", name: "movep long from even memory upper",
ins: Instruction::MOVEP(0, 0, 0, Size::Long, Direction::FromTarget), ins: Instruction::MOVEP(0, 0, 0, Size::Long, Direction::FromTarget),
data: &[ 0x0148, 0x0000 ], data: &[ 0x0148, 0x0000 ],
cputype: M68kType::MC68010, cputype: M68kType::MC68010,
init: TestState { pc: 0x00000000, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x00000000, a0: MEM_ADDR, a1: 0x00000000, sr: 0x27FF, mem: 0xAAFFBBFF }, init: TestState { pc: 0x00000000, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x00000000, a0: MEM_ADDR, a1: 0x00000000, sr: 0x27FF, mem: 0xAAFFBBFF },
fini: TestState { pc: 0x00000004, ssp: 0x00000000, usp: 0x00000000, d0: 0xAABB0000, d1: 0x00000000, a0: MEM_ADDR, a1: 0x00000000, sr: 0x27FF, mem: 0xAAFFBBFF }, fini: TestState { pc: 0x00000004, ssp: 0x00000000, usp: 0x00000000, d0: 0xAABBCCDD, d1: 0x00000000, a0: MEM_ADDR, a1: 0x00000000, sr: 0x27FF, mem: 0xAAFFBBFF },
}, },
TestCase { TestCase {
name: "movep long from even memory lower", name: "movep long from even memory lower",
@ -601,7 +629,7 @@ const TEST_CASES: &'static [TestCase] = &[
data: &[ 0x007C, 0x00AA ], data: &[ 0x007C, 0x00AA ],
cputype: M68kType::MC68010, cputype: M68kType::MC68010,
init: TestState { pc: 0x00000000, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x00000000, a0: 0x00000000, a1: 0x00000000, sr: 0xA755, mem: 0x00000000 }, init: TestState { pc: 0x00000000, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x00000000, a0: 0x00000000, a1: 0x00000000, sr: 0xA755, mem: 0x00000000 },
fini: TestState { pc: 0x00000004, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x00000000, a0: 0x00000000, a1: 0x00000000, sr: 0xA7FF, mem: 0x00000000 }, fini: TestState { pc: 0x00000004, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000000, d1: 0x00000000, a0: 0x00000000, a1: 0x00000000, sr: 0xA71F, mem: 0x00000000 },
}, },

View File

@ -1,56 +1,57 @@
use femtos::{Instant, Frequency}; use femtos::{Instant, Frequency};
use emulator_hal::bus::BusAccess;
use emulator_hal_memory::MemoryBlock;
use moa_core::{System, Error, MemoryBlock, Address, Addressable, Device}; use moa_m68k::{M68k, M68kType, M68kAddress};
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; use moa_m68k::execute::M68kCycle;
const INIT_STACK: Address = 0x00002000; const INIT_STACK: M68kAddress = 0x00002000;
const INIT_ADDR: Address = 0x00000010; const INIT_ADDR: M68kAddress = 0x00000010;
fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, System) {
let mut system = System::default();
#[allow(clippy::uninit_vec)]
fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock<u32, Instant>) {
// Insert basic initialization // Insert basic initialization
let data = vec![0; 0x00100000]; let len = 0x10_0000;
let mem = MemoryBlock::new(data); let mut data = Vec::with_capacity(len);
system.add_addressable_device(0x00000000, Device::new(mem)).unwrap(); unsafe { data.set_len(len); }
system.get_bus().write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap(); let mut memory = MemoryBlock::from(data);
system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap(); memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
memory.write_beu32(Instant::START, 4, INIT_ADDR).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 cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0); let cpu = M68k::from_type(cputype, Frequency::from_mhz(10));
let cycle = M68kCycle::new(&cpu, system.clock); let cycle = M68kCycle::new(&cpu, Instant::START);
assert_eq!(cpu.state.pc, INIT_ADDR as u32); (cpu, cycle, memory)
assert_eq!(cpu.state.ssp, INIT_STACK as u32);
assert_eq!(cycle.decoder.start, INIT_ADDR as u32);
assert_eq!(cycle.decoder.instruction, Instruction::NOP);
(cpu, cycle, system)
} }
fn load_memory(system: &System, data: &[u16]) { fn load_memory<Bus: BusAccess<u32, Instant>>(bus: &mut Bus, data: &[u16]) {
let mut addr = INIT_ADDR; let mut addr = INIT_ADDR;
for word in data { for word in data {
system.get_bus().write_beu16(system.clock, addr, *word).unwrap(); bus.write_beu16(Instant::START, addr, *word).unwrap();
addr += 2; addr += 2;
} }
} }
fn run_timing_test(case: &TimingCase) -> Result<(), Error> { fn run_timing_test(case: &TimingCase) -> Result<(), String> {
let (mut cpu, cycle, system) = init_decode_test(case.cpu); let (mut cpu, cycle, mut memory) = init_decode_test(case.cpu);
let mut executor = cycle.begin(&mut cpu); load_memory(&mut memory, case.data);
let mut executor = cycle.begin(&mut cpu, &mut memory);
let mut timing = M68kInstructionTiming::new(case.cpu, 16); let mut timing = M68kInstructionTiming::new(case.cpu, 16);
load_memory(&system, case.data); executor.reset_cpu().unwrap();
assert_eq!(executor.state.pc, INIT_ADDR);
assert_eq!(executor.state.ssp, INIT_STACK);
executor.decode_next().unwrap(); executor.decode_next().unwrap();
assert_eq!(executor.cycle.decoder.instruction, case.ins.clone()); assert_eq!(executor.cycle.decoder.instruction, case.ins.clone());
timing.add_instruction(&executor.cycle.decoder.instruction); timing.add_instruction(&executor.cycle.decoder.instruction);
let result = timing.calculate_clocks(false, 1); let result = timing.calculate_clocks();
let expected = match case.cpu { let expected = match case.cpu {
M68kType::MC68000 => case.timing.0, M68kType::MC68000 => case.timing.0,
M68kType::MC68010 => case.timing.1, M68kType::MC68010 => case.timing.1,
@ -62,20 +63,20 @@ fn run_timing_test(case: &TimingCase) -> Result<(), Error> {
Ok(()) Ok(())
} else { } else {
println!("{:?}", timing); println!("{:?}", timing);
Err(Error::new(format!("expected {} but found {}", expected, result))) Err(format!("expected {} but found {}", expected, result))
} }
} }
#[test] #[test]
#[ignore]
pub fn run_timing_tests() { pub fn run_timing_tests() {
let mut errors = 0; let mut errors = 0;
for case in TIMING_TESTS { for case in TIMING_TESTS {
// NOTE switched to only show the failures rather than all tests print!("Testing for {:?}...", case.ins);
//print!("Testing for {:?}...", case.ins); match run_timing_test(case) {
//match run_timing_test(case) { Ok(()) => println!("ok"),
// Ok(()) => println!("ok"), Err(err) => { println!("{:?}", err); errors += 1 },
// Err(err) => { println!("{}", err.msg); errors += 1 }, }
//}
if let Err(_) = run_timing_test(case) { if let Err(_) = run_timing_test(case) {
errors += 1; errors += 1;
@ -94,6 +95,7 @@ pub struct TimingCase {
pub ins: Instruction, pub ins: Instruction,
} }
#[rustfmt::skip]
pub const TIMING_TESTS: &'static [TimingCase] = &[ pub const TIMING_TESTS: &'static [TimingCase] = &[
TimingCase { cpu: M68kType::MC68000, data: &[0xA000], timing: ( 4, 4, 4), ins: Instruction::UnimplementedA(0xA000) }, TimingCase { cpu: M68kType::MC68000, data: &[0xA000], timing: ( 4, 4, 4), ins: Instruction::UnimplementedA(0xA000) },
TimingCase { cpu: M68kType::MC68000, data: &[0xF000], timing: ( 4, 4, 4), ins: Instruction::UnimplementedF(0xF000) }, TimingCase { cpu: M68kType::MC68000, data: &[0xF000], timing: ( 4, 4, 4), ins: Instruction::UnimplementedF(0xF000) },

View File

@ -1,15 +1,15 @@
use femtos::{Instant, Frequency}; use femtos::{Instant, Frequency};
use emulator_hal::bus::BusAccess;
use emulator_hal_memory::MemoryBlock;
use moa_core::{System, Error, MemoryBlock, BusPort, Address, Addressable, Device}; use moa_m68k::{M68k, M68kType, M68kAddress};
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; use moa_m68k::execute::M68kCycle;
const INIT_STACK: Address = 0x00002000; const INIT_STACK: M68kAddress = 0x00002000;
const INIT_ADDR: Address = 0x00000010; const INIT_ADDR: M68kAddress = 0x00000010;
struct TimingCase { struct TimingCase {
@ -24,47 +24,45 @@ const TIMING_TESTS: &'static [TimingCase] = &[
]; ];
fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, System) { fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock<u32, Instant>) {
let mut system = System::default();
// Insert basic initialization // Insert basic initialization
let data = vec![0; 0x00100000]; let len = 0x10_0000;
let mem = MemoryBlock::new(data); let mut data = Vec::with_capacity(len);
system.add_addressable_device(0x00000000, Device::new(mem)).unwrap(); unsafe { data.set_len(len); }
system.get_bus().write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap(); let mut memory = MemoryBlock::from(data);
system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap(); memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
memory.write_beu32(Instant::START, 4, INIT_ADDR).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 cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0); let cpu = M68k::from_type(cputype, Frequency::from_mhz(10));
//cpu.reset_cpu().unwrap(); let cycle = M68kCycle::new(&cpu, Instant::START);
assert_eq!(cpu.state.pc, INIT_ADDR as u32); (cpu, cycle, memory)
assert_eq!(cpu.state.ssp, INIT_STACK as u32);
let cycle = M68kCycle::new(&cpu, system.clock);
assert_eq!(cycle.decoder.start, INIT_ADDR as u32);
assert_eq!(cycle.decoder.instruction, Instruction::NOP);
(cpu, cycle, system)
} }
fn load_memory(system: &System, data: &[u16]) { fn load_memory<Bus: BusAccess<u32, Instant>>(bus: &mut Bus, data: &[u16]) {
let mut addr = INIT_ADDR; let mut addr = INIT_ADDR;
for word in data { for word in data {
system.get_bus().write_beu16(system.clock, addr, *word).unwrap(); bus.write_beu16(Instant::START, addr, *word).unwrap();
addr += 2; addr += 2;
} }
} }
fn run_timing_test(case: &TimingCase) -> Result<(), Error> { fn run_timing_test(case: &TimingCase) -> Result<(), String> {
let (mut cpu, cycle, system) = init_decode_test(case.cpu); let (mut cpu, cycle, mut memory) = init_decode_test(case.cpu);
let mut executor = cycle.begin(&mut cpu); load_memory(&mut memory, case.data);
let mut executor = cycle.begin(&mut cpu, &mut memory);
let mut timing = M68kInstructionTiming::new(case.cpu, 16); let mut timing = M68kInstructionTiming::new(case.cpu, 16);
load_memory(&system, case.data); executor.reset_cpu().unwrap();
assert_eq!(executor.state.pc, INIT_ADDR);
assert_eq!(executor.state.ssp, INIT_STACK);
executor.decode_next().unwrap(); executor.decode_next().unwrap();
assert_eq!(executor.cycle.decoder.instruction, case.ins.clone()); assert_eq!(executor.cycle.decoder.instruction, case.ins.clone());
timing.add_instruction(&executor.cycle.decoder.instruction); timing.add_instruction(&executor.cycle.decoder.instruction);
let result = timing.calculate_clocks(false, 1); let result = timing.calculate_clocks();
let expected = match case.cpu { let expected = match case.cpu {
M68kType::MC68000 => case.timing.0, M68kType::MC68000 => case.timing.0,
M68kType::MC68010 => case.timing.1, M68kType::MC68010 => case.timing.1,
@ -76,7 +74,7 @@ fn run_timing_test(case: &TimingCase) -> Result<(), Error> {
Ok(()) Ok(())
} else { } else {
println!("{:?}", timing); println!("{:?}", timing);
Err(Error::new(format!("expected {} but found {}", expected, result))) Err(format!("expected {} but found {}", expected, result))
} }
} }
@ -84,12 +82,11 @@ fn run_timing_test(case: &TimingCase) -> Result<(), Error> {
pub fn run_timing_tests() { pub fn run_timing_tests() {
let mut errors = 0; let mut errors = 0;
for case in TIMING_TESTS { for case in TIMING_TESTS {
// NOTE switched to only show the failures rather than all tests print!("Testing for {:?}...", case.ins);
//print!("Testing for {:?}...", case.ins); match run_timing_test(case) {
//match run_timing_test(case) { Ok(()) => println!("ok"),
// Ok(()) => println!("ok"), Err(err) => { println!("{:?}", err); errors += 1 },
// Err(err) => { println!("{}", err.msg); errors += 1 }, }
//}
if let Err(_) = run_timing_test(case) { if let Err(_) = run_timing_test(case) {
errors += 1; errors += 1;

View File

@ -9,4 +9,4 @@ thiserror = "1.0"
femtos = "0.1" femtos = "0.1"
moa-core = { path = "../../core" } moa-core = { path = "../../core" }
moa-signals = { path = "../../libraries/signals" } moa-signals = { path = "../../libraries/signals" }
emulator-hal = { path = "/media/work/projects/emulator-hal/emulator-hal" } emulator-hal = { path = "../../libraries/emulator-hal/emulator-hal" }

View File

@ -30,7 +30,7 @@ impl Debuggable for Z80 {
Ok(()) Ok(())
} }
fn print_disassembly(&mut self, addr: Address, count: usize) { fn print_disassembly(&mut self, _system: &System, addr: Address, count: usize) {
let mut decoder = Z80Decoder::default(); let mut decoder = Z80Decoder::default();
decoder.dump_disassembly(&mut self.port, addr as u16, count as u16); decoder.dump_disassembly(&mut self.port, addr as u16, count as u16);
} }

View File

@ -1,4 +1,5 @@
use core::fmt::Write;
use femtos::Instant; use femtos::Instant;
use moa_core::{Address, Addressable}; use moa_core::{Address, Addressable};
@ -6,17 +7,9 @@ use moa_core::{Address, Addressable};
use crate::state::Z80Error; use crate::state::Z80Error;
use crate::instructions::{Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target, LoadTarget, UndocumentedCopy, Instruction}; use crate::instructions::{Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target, LoadTarget, UndocumentedCopy, Instruction};
use emulator_hal::bus::{BusType, BusAccess}; //use emulator_hal::bus::BusAccess;
//
struct Z80Bus; //type Z80Address = (bool, u16);
type Z80Address = (bool, u16);
impl BusType for Z80Bus {
//type Address = (bool, u16);
type Error = Z80Error;
type Instant = Instant;
}
#[derive(Clone)] #[derive(Clone)]
pub struct Z80Decoder { pub struct Z80Decoder {
@ -560,10 +553,10 @@ impl Z80Decoder {
} }
pub fn format_instruction_bytes(&mut self, memory: &mut dyn Addressable) -> String { pub fn format_instruction_bytes(&mut self, memory: &mut dyn Addressable) -> String {
let ins_data: String = let mut ins_data = String::new();
(0..self.end.saturating_sub(self.start)).map(|offset| for offset in 0..self.end.saturating_sub(self.start) {
format!("{:02x} ", memory.read_u8(self.clock, (self.start + offset) as Address).unwrap()) write!(ins_data, "{:02x} ", memory.read_u8(self.clock, (self.start + offset) as Address).unwrap()).unwrap()
).collect(); }
ins_data ins_data
} }

View File

@ -8,8 +8,6 @@ use crate::state::{Z80, Z80Error, Status, Flags};
use crate::timing::Z80InstructionCycles; use crate::timing::Z80InstructionCycles;
const DEV_NAME: &str = "z80-cpu";
const FLAGS_NUMERIC: u8 = 0xC0; const FLAGS_NUMERIC: u8 = 0xC0;
const FLAGS_ARITHMETIC: u8 = 0x17; const FLAGS_ARITHMETIC: u8 = 0x17;
const FLAGS_CARRY_HALF_CARRY: u8 = 0x11; const FLAGS_CARRY_HALF_CARRY: u8 = 0x11;
@ -70,8 +68,8 @@ impl From<Error> for Z80Error {
fn from(err: Error) -> Self { fn from(err: Error) -> Self {
match err { match err {
Error::Processor(ex) => Z80Error::BusError(format!("processor error {}", ex)), Error::Processor(ex) => Z80Error::BusError(format!("processor error {}", ex)),
Error::Breakpoint(msg) => Z80Error::Breakpoint, Error::Breakpoint(_) => Z80Error::Breakpoint,
Error::Other(msg) | Error::Assertion(msg) | Error::Emulator(_, msg) => Z80Error::BusError(format!("{}", msg)), Error::Other(msg) | Error::Assertion(msg) | Error::Emulator(_, msg) => Z80Error::BusError(msg.to_string()),
} }
} }

View File

@ -1,5 +1,4 @@
use std::fmt;
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use femtos::{Instant, Frequency}; use femtos::{Instant, Frequency};

View File

@ -1,5 +1,5 @@
use cpal::{Stream, SampleRate, SampleFormat, StreamConfig, StreamInstant, OutputCallbackInfo, traits::{DeviceTrait, HostTrait, StreamTrait}}; use cpal::{Stream, SampleRate, SampleFormat, StreamConfig, OutputCallbackInfo, traits::{DeviceTrait, HostTrait, StreamTrait}};
use crate::audio::{AudioOutput, SAMPLE_RATE}; use crate::audio::{AudioOutput, SAMPLE_RATE};
@ -22,7 +22,7 @@ impl CpalAudioOutput {
.with_sample_rate(SampleRate(SAMPLE_RATE as u32)) .with_sample_rate(SampleRate(SAMPLE_RATE as u32))
.into(); .into();
let data_callback = move |data: &mut [f32], info: &OutputCallbackInfo| { let data_callback = move |data: &mut [f32], _info: &OutputCallbackInfo| {
let mut index = 0; let mut index = 0;
while index < data.len() { while index < data.len() {
if let Some((clock, mut frame)) = output.receive() { if let Some((clock, mut frame)) = output.receive() {

View File

@ -9,10 +9,15 @@ use nix::fcntl::OFlag;
use nix::pty::{self, PtyMaster}; use nix::pty::{self, PtyMaster};
use nix::fcntl::{fcntl, FcntlArg}; use nix::fcntl::{fcntl, FcntlArg};
use moa_core::Error; use moa_host::Tty;
use moa_core::host::Tty;
#[derive(Debug, PartialEq, Eq)]
pub enum SimplePtyError {
Open,
PtsName,
}
pub struct SimplePty { pub struct SimplePty {
pub name: String, pub name: String,
input: mpsc::Receiver<u8>, input: mpsc::Receiver<u8>,
@ -28,14 +33,14 @@ impl SimplePty {
} }
} }
pub fn open() -> Result<SimplePty, Error> { pub fn open() -> Result<SimplePty, SimplePtyError> {
let pty = pty::posix_openpt(OFlag::O_RDWR).and_then(|pty| { let pty = pty::posix_openpt(OFlag::O_RDWR).and_then(|pty| {
pty::grantpt(&pty)?; pty::grantpt(&pty)?;
pty::unlockpt(&pty)?; pty::unlockpt(&pty)?;
Ok(pty) Ok(pty)
}).map_err(|_| Error::new("Error opening new pseudoterminal"))?; }).map_err(|_| SimplePtyError::Open)?;
let name = unsafe { pty::ptsname(&pty).map_err(|_| Error::new("Unable to get pty name"))? }; let name = unsafe { pty::ptsname(&pty).map_err(|_| SimplePtyError::PtsName)? };
let (input_tx, input_rx) = mpsc::channel(); let (input_tx, input_rx) = mpsc::channel();
let (output_tx, output_rx) = mpsc::channel(); let (output_tx, output_rx) = mpsc::channel();
let shared = SimplePty::new(name.clone(), input_rx, output_tx); let shared = SimplePty::new(name.clone(), input_rx, output_tx);

View File

@ -11,10 +11,12 @@ simple_logger = "^2"
femtos = "0.1" femtos = "0.1"
moa-core = { path = "../../core" } moa-core = { path = "../../core" }
moa-host = { path = "../../libraries/host" }
moa-common = { path = "../common", features = ["tty"] } moa-common = { path = "../common", features = ["tty"] }
moa-debugger = { path = "../../libraries/debugger" }
moa-systems-genesis = { path = "../../systems/genesis" } moa-systems-genesis = { path = "../../systems/genesis" }
moa-systems-computie = { path = "../../systems/computie" } moa-systems-computie = { path = "../../systems/computie" }
moa-m68k = { path = "../../cpus/m68k" } moa-m68k = { path = "../../cpus/m68k", features = ["moa"] }
moa-peripherals-generic = { path = "../../peripherals/generic" } moa-peripherals-generic = { path = "../../peripherals/generic" }
moa-peripherals-motorola = { path = "../../peripherals/motorola" } moa-peripherals-motorola = { path = "../../peripherals/motorola" }

View File

@ -3,7 +3,7 @@ use std::thread;
use std::time::Duration; use std::time::Duration;
use femtos::Frequency; use femtos::Frequency;
use moa_core::{System, MemoryBlock, BusPort, Device}; use moa_core::{System, MemoryBlock, Device};
use moa_m68k::{M68k, M68kType}; use moa_m68k::{M68k, M68kType};
use moa_peripherals_generic::AtaDevice; use moa_peripherals_generic::AtaDevice;
@ -28,7 +28,7 @@ fn main() {
system.add_addressable_device(0x00700000, Device::new(serial)).unwrap(); system.add_addressable_device(0x00700000, Device::new(serial)).unwrap();
let cpu = M68k::from_type(M68kType::MC68010, Frequency::from_mhz(8), system.bus.clone(), 0); let cpu = M68k::from_type(M68kType::MC68010, Frequency::from_mhz(8));
//cpu.enable_tracing(); //cpu.enable_tracing();
//cpu.add_breakpoint(0x10781a); //cpu.add_breakpoint(0x10781a);

View File

@ -1,5 +1,5 @@
use clap::{Arg, ArgAction}; use clap::Arg;
use moa_console::ConsoleFrontend; use moa_console::ConsoleFrontend;
use moa_systems_computie::{build_computie, ComputieOptions}; use moa_systems_computie::{build_computie, ComputieOptions};
@ -18,9 +18,9 @@ fn main() {
options.rom = filename.to_string(); options.rom = filename.to_string();
} }
let mut frontend = ConsoleFrontend::new(); let frontend = ConsoleFrontend;
let system = build_computie(&mut frontend, options).unwrap(); let system = build_computie(&frontend, options).unwrap();
frontend.start(matches, system); frontend.start(matches, system);
} }

View File

@ -10,7 +10,7 @@ fn main() {
.help("ROM file to load (must be flat binary)")) .help("ROM file to load (must be flat binary)"))
.get_matches(); .get_matches();
let mut frontend = ConsoleFrontend::new(); let mut frontend = ConsoleFrontend;
let mut options = SegaGenesisOptions::default(); let mut options = SegaGenesisOptions::default();
if let Some(filename) = matches.get_one::<String>("ROM") { if let Some(filename) = matches.get_one::<String>("ROM") {

View File

@ -3,8 +3,9 @@ use clap::{Command, Arg, ArgAction, ArgMatches};
use std::io::{self, Write}; use std::io::{self, Write};
use femtos::Duration; use femtos::Duration;
use moa_core::{Error, System, DebugControl, Debugger}; use moa_core::{Error, System};
use moa_core::host::{Host, Tty, ControllerEvent, Audio, DummyAudio, FrameReceiver, EventSender}; use moa_debugger::{Debugger, DebugControl};
use moa_host::{Host, HostError, Tty, ControllerEvent, Audio, DummyAudio, FrameReceiver, EventSender};
pub struct ConsoleFrontend; pub struct ConsoleFrontend;
@ -13,7 +14,7 @@ impl Host for ConsoleFrontend {
fn add_pty(&self) -> Result<Box<dyn Tty>, HostError<Self::Error>> { fn add_pty(&self) -> Result<Box<dyn Tty>, HostError<Self::Error>> {
use moa_common::tty::SimplePty; use moa_common::tty::SimplePty;
Ok(Box::new(SimplePty::open()?)) Ok(Box::new(SimplePty::open().map_err(|_| HostError::TTYNotSupported)?)) //.map_err(|err| Error::new(format!("console: error opening pty: {:?}", err)))?))
} }
fn add_video_source(&mut self, _receiver: FrameReceiver) -> Result<(), HostError<Self::Error>> { fn add_video_source(&mut self, _receiver: FrameReceiver) -> Result<(), HostError<Self::Error>> {
@ -32,11 +33,13 @@ impl Host for ConsoleFrontend {
} }
} }
impl ConsoleFrontend { impl Default for ConsoleFrontend {
pub fn new() -> Self { fn default() -> Self {
Self Self
} }
}
impl ConsoleFrontend {
pub fn args(application_name: &'static str) -> Command { pub fn args(application_name: &'static str) -> Command {
Command::new(application_name) Command::new(application_name)
.arg(Arg::new("log-level") .arg(Arg::new("log-level")

View File

@ -154,7 +154,7 @@ impl Debugger {
}; };
if let Some(device) = system.get_next_debuggable_device() { if let Some(device) = system.get_next_debuggable_device() {
device.borrow_mut().as_debuggable().unwrap().print_disassembly(addr, count); device.borrow_mut().as_debuggable().unwrap().print_disassembly(system, addr, count);
} }
}, },
"c" | "continue" => { "c" | "continue" => {

@ -0,0 +1 @@
Subproject commit 84e665ce5749187d0c323f77971c288d0964fa96

View File

@ -69,7 +69,7 @@ impl MouseState {
let events: Vec<MouseEvent> = self let events: Vec<MouseEvent> = self
.buttons.into_iter() .buttons.into_iter()
.zip(next_state.buttons.into_iter()) .zip(next_state.buttons)
.enumerate() .enumerate()
.filter_map(|(i, (prev, next))| { .filter_map(|(i, (prev, next))| {
if prev != next { if prev != next {

View File

@ -24,8 +24,11 @@ pub enum HostError<E> {
Specific(E), Specific(E),
} }
/*
impl<E> fmt::Display for HostError<E> { impl<E> fmt::Display for HostError<E>
where
E: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
HostError::TTYNotSupported => write!(f, "This frontend doesn't support PTYs"), HostError::TTYNotSupported => write!(f, "This frontend doesn't support PTYs"),
@ -38,7 +41,6 @@ impl<E> fmt::Display for HostError<E> {
} }
} }
} }
*/
pub trait Host { pub trait Host {
type Error: Error; type Error: Error;

View File

@ -2,8 +2,14 @@
use std::str::Chars; use std::str::Chars;
use std::iter::Peekable; use std::iter::Peekable;
use moa_core::Error;
pub struct ParserError(pub String);
impl ParserError {
pub fn new(msg: String) -> Self {
Self(msg)
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum AssemblyLine { pub enum AssemblyLine {
@ -34,7 +40,7 @@ impl<'input> AssemblyParser<'input> {
} }
} }
pub fn parse(&mut self) -> Result<Vec<(usize, AssemblyLine)>, Error> { pub fn parse(&mut self) -> Result<Vec<(usize, AssemblyLine)>, ParserError> {
let mut output = vec![]; let mut output = vec![];
loop { loop {
let lineno = self.lexer.get_next_lineno(); let lineno = self.lexer.get_next_lineno();
@ -47,7 +53,7 @@ impl<'input> AssemblyParser<'input> {
Ok(output) Ok(output)
} }
fn parse_line(&mut self) -> Result<Option<AssemblyLine>, Error> { fn parse_line(&mut self) -> Result<Option<AssemblyLine>, ParserError> {
let token = loop { let token = loop {
match self.lexer.get_next() { match self.lexer.get_next() {
Some(token) if token == "\n" => { }, Some(token) if token == "\n" => { },
@ -73,7 +79,7 @@ impl<'input> AssemblyParser<'input> {
} }
}, },
_ => { _ => {
return Err(Error::new(format!("parse error at line {}: expected word, found {:?}", self.lexer.lineno(), token))); return Err(ParserError::new(format!("parse error at line {}: expected word, found {:?}", self.lexer.lineno(), token)));
}, },
}; };
@ -81,7 +87,7 @@ impl<'input> AssemblyParser<'input> {
Ok(Some(result)) Ok(Some(result))
} }
fn parse_list_of_words(&mut self) -> Result<Vec<String>, Error> { fn parse_list_of_words(&mut self) -> Result<Vec<String>, ParserError> {
let mut list = vec![]; let mut list = vec![];
// If we're already at the end of the line, then it's an empty list, so return // If we're already at the end of the line, then it's an empty list, so return
@ -101,7 +107,7 @@ impl<'input> AssemblyParser<'input> {
} }
} }
fn parse_list_of_operands(&mut self) -> Result<Vec<AssemblyOperand>, Error> { fn parse_list_of_operands(&mut self) -> Result<Vec<AssemblyOperand>, ParserError> {
let mut list = vec![]; let mut list = vec![];
// If we're already at the end of the line, then it's an empty list, so return // If we're already at the end of the line, then it's an empty list, so return
@ -121,7 +127,7 @@ impl<'input> AssemblyParser<'input> {
} }
} }
fn parse_operand(&mut self) -> Result<AssemblyOperand, Error> { fn parse_operand(&mut self) -> Result<AssemblyOperand, ParserError> {
let token = self.lexer.expect_next()?; let token = self.lexer.expect_next()?;
match token.as_str() { match token.as_str() {
"%" => { "%" => {
@ -163,7 +169,7 @@ impl<'input> AssemblyParser<'input> {
} }
} }
fn parse_any_number(lineno: usize, string: &str) -> Result<usize, Error> { fn parse_any_number(lineno: usize, string: &str) -> Result<usize, ParserError> {
let (radix, numeric) = if let Some(s) = string.strip_prefix("0x") { let (radix, numeric) = if let Some(s) = string.strip_prefix("0x") {
(16, s) (16, s)
} else if let Some(s) = string.strip_prefix("0b") { } else if let Some(s) = string.strip_prefix("0b") {
@ -174,7 +180,7 @@ fn parse_any_number(lineno: usize, string: &str) -> Result<usize, Error> {
(10, string) (10, string)
}; };
usize::from_str_radix(numeric, radix) usize::from_str_radix(numeric, radix)
.map_err(|_| Error::new(format!("parse error at line {}: expected number after #, but found {:?}", lineno, string))) .map_err(|_| ParserError::new(format!("parse error at line {}: expected number after #, but found {:?}", lineno, string)))
} }
@ -230,25 +236,25 @@ impl<'input> AssemblyLexer<'input> {
self.peeked.clone() self.peeked.clone()
} }
pub fn expect_next(&mut self) -> Result<String, Error> { pub fn expect_next(&mut self) -> Result<String, ParserError> {
self.get_next().ok_or_else(|| Error::new(format!("unexpected end of input at line {}", self.lineno))) self.get_next().ok_or_else(|| ParserError::new(format!("unexpected end of input at line {}", self.lineno)))
} }
pub fn expect_token(&mut self, expected: &str) -> Result<(), Error> { pub fn expect_token(&mut self, expected: &str) -> Result<(), ParserError> {
let token = self.expect_next()?; let token = self.expect_next()?;
if token == expected { if token == expected {
Ok(()) Ok(())
} else { } else {
Err(Error::new(format!("parse error at line {}: expected {:?}, found {:?}", self.lineno, expected, token))) Err(ParserError::new(format!("parse error at line {}: expected {:?}, found {:?}", self.lineno, expected, token)))
} }
} }
pub fn expect_end(&mut self) -> Result<(), Error> { pub fn expect_end(&mut self) -> Result<(), ParserError> {
let token = self.get_next(); let token = self.get_next();
if token.is_none() || token.as_ref().unwrap() == "\n" { if token.is_none() || token.as_ref().unwrap() == "\n" {
Ok(()) Ok(())
} else { } else {
Err(Error::new(format!("expected end of line at {}: found {:?}", self.lineno, token))) Err(ParserError::new(format!("expected end of line at {}: found {:?}", self.lineno, token)))
} }
} }
@ -266,8 +272,6 @@ impl<'input> AssemblyLexer<'input> {
break; break;
} }
} }
} else {
} }
} else if *ch == ' ' || *ch == '\t' || *ch == '\r' { } else if *ch == ' ' || *ch == '\t' || *ch == '\r' {
self.chars.next(); self.chars.next();
@ -301,28 +305,28 @@ fn is_digit(ch: char) -> bool {
ch.is_ascii_digit() ch.is_ascii_digit()
} }
pub fn expect_args(lineno: usize, args: &[AssemblyOperand], expected: usize) -> Result<(), Error> { pub fn expect_args(lineno: usize, args: &[AssemblyOperand], expected: usize) -> Result<(), ParserError> {
if args.len() == expected { if args.len() == expected {
Ok(()) Ok(())
} else { } else {
Err(Error::new(format!("error at line {}: expected {} args, but found {}", lineno, expected, args.len()))) Err(ParserError::new(format!("error at line {}: expected {} args, but found {}", lineno, expected, args.len())))
} }
} }
pub fn expect_label(lineno: usize, args: &[AssemblyOperand]) -> Result<String, Error> { pub fn expect_label(lineno: usize, args: &[AssemblyOperand]) -> Result<String, ParserError> {
expect_args(lineno, args, 1)?; expect_args(lineno, args, 1)?;
if let AssemblyOperand::Label(name) = &args[0] { if let AssemblyOperand::Label(name) = &args[0] {
Ok(name.clone()) Ok(name.clone())
} else { } else {
Err(Error::new(format!("error at line {}: expected a label, but found {:?}", lineno, args))) Err(ParserError::new(format!("error at line {}: expected a label, but found {:?}", lineno, args)))
} }
} }
pub fn expect_immediate(lineno: usize, operand: &AssemblyOperand) -> Result<usize, Error> { pub fn expect_immediate(lineno: usize, operand: &AssemblyOperand) -> Result<usize, ParserError> {
if let AssemblyOperand::Immediate(value) = operand { if let AssemblyOperand::Immediate(value) = operand {
Ok(*value) Ok(*value)
} else { } else {
Err(Error::new(format!("error at line {}: expected an immediate value, but found {:?}", lineno, operand))) Err(ParserError::new(format!("error at line {}: expected an immediate value, but found {:?}", lineno, operand)))
} }
} }

View File

@ -725,13 +725,18 @@ pub struct Ym2612 {
channels: Vec<Channel>, channels: Vec<Channel>,
dac: Dac, dac: Dac,
// TODO the timer hasn't been implemented yet
#[allow(dead_code)]
timer_a_enable: bool, timer_a_enable: bool,
timer_a: u16, timer_a: u16,
#[allow(dead_code)]
timer_a_current: u16, timer_a_current: u16,
timer_a_overflow: bool, timer_a_overflow: bool,
#[allow(dead_code)]
timer_b_enable: bool, timer_b_enable: bool,
timer_b: u8, timer_b: u8,
#[allow(dead_code)]
timer_b_current: u8, timer_b_current: u8,
timer_b_overflow: bool, timer_b_overflow: bool,
@ -856,8 +861,8 @@ impl Ym2612 {
0x28 => { 0x28 => {
let num = (data as usize) & 0x07; let num = (data as usize) & 0x07;
let ch = match num { let ch = match num {
0 | 1 | 2 => num, 0..=2 => num,
4 | 5 | 6 => num - 1, 4..=6 => num - 1,
_ => { _ => {
log::warn!("{}: attempted key on/off to invalid channel {}", DEV_NAME, num); log::warn!("{}: attempted key on/off to invalid channel {}", DEV_NAME, num);
return; return;
@ -1025,7 +1030,7 @@ impl Addressable for Ym2612 {
fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> { fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
match addr { match addr {
0 | 1 | 2 | 3 => { 0..=3 => {
// Read the status byte (busy/overflow) // Read the status byte (busy/overflow)
data[0] = ((self.timer_a_overflow as u8) << 1) | (self.timer_b_overflow as u8); data[0] = ((self.timer_a_overflow as u8) << 1) | (self.timer_b_overflow as u8);
} }

View File

@ -8,6 +8,6 @@ log = "0.4"
femtos = "0.1" femtos = "0.1"
moa-core = { path = "../../core" } moa-core = { path = "../../core" }
moa-host = { path = "../../libraries/host" } moa-host = { path = "../../libraries/host" }
moa-m68k = { path = "../../cpus/m68k" } moa-m68k = { path = "../../cpus/m68k", features = ["moa"] }
moa-peripherals-generic = { path = "../../peripherals/generic" } moa-peripherals-generic = { path = "../../peripherals/generic" }
moa-peripherals-motorola = { path = "../../peripherals/motorola" } moa-peripherals-motorola = { path = "../../peripherals/motorola" }

View File

@ -45,7 +45,7 @@ pub fn build_computie<H: Host>(host: &H, options: ComputieOptions) -> Result<Sys
system.add_addressable_device(0x00700000, Device::new(serial))?; system.add_addressable_device(0x00700000, Device::new(serial))?;
let mut cpu = M68k::from_type(M68kType::MC68010, options.frequency, system.bus.clone(), 0); let mut cpu = M68k::from_type(M68kType::MC68010, options.frequency);
//cpu.enable_tracing(); //cpu.enable_tracing();
//cpu.add_breakpoint(0x10781a); //cpu.add_breakpoint(0x10781a);
@ -83,7 +83,7 @@ pub fn build_computie_k30<H: Host>(host: &H) -> Result<System, Error> {
system.add_addressable_device(0x00700000, Device::new(serial))?; system.add_addressable_device(0x00700000, Device::new(serial))?;
let cpu = M68k::from_type(M68kType::MC68030, Frequency::from_hz(10_000_000), system.bus.clone(), 0); let cpu = M68k::from_type(M68kType::MC68030, Frequency::from_hz(10_000_000));
//cpu.enable_tracing(); //cpu.enable_tracing();
//cpu.add_breakpoint(0x10781a); //cpu.add_breakpoint(0x10781a);

View File

@ -10,6 +10,6 @@ moa-core = { path = "../../core" }
moa-signals = { path = "../../libraries/signals" } moa-signals = { path = "../../libraries/signals" }
moa-host = { path = "../../libraries/host" } moa-host = { path = "../../libraries/host" }
moa-peripherals-yamaha = { path = "../../peripherals/yamaha" } moa-peripherals-yamaha = { path = "../../peripherals/yamaha" }
moa-m68k = { path = "../../cpus/m68k" } moa-m68k = { path = "../../cpus/m68k", features = ["moa"] }
moa-z80 = { path = "../../cpus/z80" } moa-z80 = { path = "../../cpus/z80" }

View File

@ -818,7 +818,7 @@ impl Addressable for Ym7101 {
0x00 | 0x02 => self.state.memory.read_data_port(addr, data)?, 0x00 | 0x02 => self.state.memory.read_data_port(addr, data)?,
// Read from Control Port // Read from Control Port
0x04 | 0x05 | 0x06 | 0x07 => { 0x04..=0x07 => {
log::debug!("{}: read status byte {:x}", DEV_NAME, self.state.status); log::debug!("{}: read status byte {:x}", DEV_NAME, self.state.status);
for item in data { for item in data {
*item = if (addr % 2) == 0 { *item = if (addr % 2) == 0 {

View File

@ -94,7 +94,7 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
let vdp = Ym7101::new(host, interrupt, coproc_sn_sound)?; let vdp = Ym7101::new(host, interrupt, coproc_sn_sound)?;
system.add_peripheral("vdp", 0x00c00000, Device::new(vdp))?; system.add_peripheral("vdp", 0x00c00000, Device::new(vdp))?;
let cpu = M68k::from_type(M68kType::MC68000, Frequency::from_hz(7_670_454), system.bus.clone(), 0); let cpu = M68k::from_type(M68kType::MC68000, Frequency::from_hz(7_670_454));
system.add_interruptable_device("cpu", Device::new(cpu))?; system.add_interruptable_device("cpu", Device::new(cpu))?;
Ok(system) Ok(system)

View File

@ -9,6 +9,6 @@ femtos = "0.1"
moa-core = { path = "../../core" } moa-core = { path = "../../core" }
moa-host = { path = "../../libraries/host" } moa-host = { path = "../../libraries/host" }
moa-signals = { path = "../../libraries/signals" } moa-signals = { path = "../../libraries/signals" }
moa-m68k = { path = "../../cpus/m68k" } moa-m68k = { path = "../../cpus/m68k", features = ["moa"] }
moa-peripherals-mos = { path = "../../peripherals/mos" } moa-peripherals-mos = { path = "../../peripherals/mos" }
moa-peripherals-zilog = { path = "../../peripherals/zilog" } moa-peripherals-zilog = { path = "../../peripherals/zilog" }

View File

@ -71,7 +71,7 @@ pub fn build_macintosh_512k<H: Host>(host: &mut H) -> Result<System, Error> {
system.add_addressable_device(0x00000000, Device::new(mainboard))?; system.add_addressable_device(0x00000000, Device::new(mainboard))?;
let mut cpu = M68k::from_type(M68kType::MC68000, Frequency::from_hz(7_833_600), system.bus.clone(), 0); let mut cpu = M68k::from_type(M68kType::MC68000, Frequency::from_hz(7_833_600));
//cpu.enable_tracing(); //cpu.enable_tracing();
//system.enable_debugging(); //system.enable_debugging();

View File

@ -5,9 +5,10 @@ edition = "2021"
[dependencies] [dependencies]
femtos = "0.1" femtos = "0.1"
emulator-hal = { path = "../../emulator/libraries/emulator-hal/emulator-hal" }
moa-core = { path = "../../emulator/core" } emulator-hal-memory = { path = "../../emulator/libraries/emulator-hal/emulator-hal-memory" }
moa-m68k = { path = "../../emulator/cpus/m68k" } moa-m68k = { path = "../../emulator/cpus/m68k" }
serde = "1.0" serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
serde_derive = "1.0" serde_derive = "1.0"

View File

@ -1,129 +1,129 @@
Last run on 2022-09-18 at commit 94d3e1d3894e6588ff6daa55f0ba82473b1e74c7 Last run on 2024-03-16 at commit c20d7afe6e8005ab272602953154280f0e1aa944
ABCD.json completed: 7993 passed, 72 FAILED ABCD.json.gz completed: 7993 passed, 72 FAILED
ADD.b.json completed, all passed! ADD.b.json.gz completed, all passed!
ADD.l.json completed: 7736 passed, 329 FAILED ADD.l.json.gz completed: 7736 passed, 329 FAILED
ADD.w.json completed: 7712 passed, 353 FAILED ADD.w.json.gz completed: 7712 passed, 353 FAILED
ADDA.l.json completed, all passed! ADDA.l.json.gz completed, all passed!
ADDA.w.json completed, all passed! ADDA.w.json.gz completed, all passed!
ADDX.b.json completed, all passed! ADDX.b.json.gz completed, all passed!
ADDX.l.json completed: 5472 passed, 2593 FAILED ADDX.l.json.gz completed: 5472 passed, 2593 FAILED
ADDX.w.json completed, all passed! ADDX.w.json.gz completed, all passed!
AND.b.json completed, all passed! AND.b.json.gz completed, all passed!
AND.l.json completed: 7779 passed, 286 FAILED AND.l.json.gz completed: 7779 passed, 286 FAILED
AND.w.json completed: 7764 passed, 301 FAILED AND.w.json.gz completed: 7764 passed, 301 FAILED
ANDItoCCR.json completed, all passed! ANDItoCCR.json.gz completed, all passed!
ANDItoSR.json completed, all passed! ANDItoSR.json.gz completed, all passed!
ASL.b.json completed: 7238 passed, 827 FAILED ASL.b.json.gz completed: 8063 passed, 2 FAILED
ASL.l.json completed: 6471 passed, 1594 FAILED ASL.l.json.gz completed, all passed!
ASL.w.json completed: 7053 passed, 1012 FAILED ASL.w.json.gz completed: 7896 passed, 169 FAILED
ASR.b.json completed: 7547 passed, 518 FAILED ASR.b.json.gz completed: 7783 passed, 282 FAILED
ASR.l.json completed: 7092 passed, 973 FAILED ASR.l.json.gz completed: 8029 passed, 36 FAILED
ASR.w.json completed: 7513 passed, 552 FAILED ASR.w.json.gz completed: 7891 passed, 174 FAILED
BCHG.json completed, all passed! BCHG.json.gz completed, all passed!
BCLR.json completed, all passed! BCLR.json.gz completed, all passed!
BSET.json completed, all passed! BSET.json.gz completed, all passed!
BSR.json completed, all passed! BSR.json.gz completed, all passed!
BTST.json completed: 8052 passed, 13 FAILED BTST.json.gz completed: 8051 passed, 14 FAILED
Bcc.json completed, all passed! Bcc.json.gz completed, all passed!
CHK.json completed: 7744 passed, 321 FAILED CHK.json.gz completed: 7744 passed, 321 FAILED
CLR.b.json completed, all passed! CLR.b.json.gz completed, all passed!
CLR.l.json completed: 7472 passed, 593 FAILED CLR.l.json.gz completed: 7472 passed, 593 FAILED
CLR.w.json completed: 7465 passed, 600 FAILED CLR.w.json.gz completed: 7465 passed, 600 FAILED
CMP.b.json completed, all passed! CMP.b.json.gz completed, all passed!
CMP.l.json completed, all passed! CMP.l.json.gz completed, all passed!
CMP.w.json completed, all passed! CMP.w.json.gz completed, all passed!
CMPA.l.json completed, all passed! CMPA.l.json.gz completed, all passed!
CMPA.w.json completed, all passed! CMPA.w.json.gz completed, all passed!
DBcc.json completed, all passed! DBcc.json.gz completed, all passed!
DIVS.json completed, all passed! DIVS.json.gz completed, all passed!
DIVU.json completed: 8064 passed, 1 FAILED DIVU.json.gz completed: 8064 passed, 1 FAILED
EOR.b.json completed, all passed! EOR.b.json.gz completed, all passed!
EOR.l.json completed: 7519 passed, 546 FAILED EOR.l.json.gz completed: 7519 passed, 546 FAILED
EOR.w.json completed: 7525 passed, 540 FAILED EOR.w.json.gz completed: 7525 passed, 540 FAILED
EORItoCCR.json completed, all passed! EORItoCCR.json.gz completed, all passed!
EORItoSR.json completed, all passed! EORItoSR.json.gz completed, all passed!
EXG.json completed, all passed! EXG.json.gz completed, all passed!
EXT.l.json completed, all passed! EXT.l.json.gz completed, all passed!
EXT.w.json completed, all passed! EXT.w.json.gz completed, all passed!
JMP.json completed, all passed! JMP.json.gz completed, all passed!
JSR.json completed, all passed! JSR.json.gz completed, all passed!
LEA.json completed, all passed! LEA.json.gz completed, all passed!
LINK.json completed, all passed! LINK.json.gz completed, all passed!
LSL.b.json completed: 7809 passed, 256 FAILED LSL.b.json.gz completed, all passed!
LSL.l.json completed: 7056 passed, 1009 FAILED LSL.l.json.gz completed, all passed!
LSL.w.json completed: 7523 passed, 542 FAILED LSL.w.json.gz completed: 7910 passed, 155 FAILED
LSR.b.json completed: 7817 passed, 248 FAILED LSR.b.json.gz completed, all passed!
LSR.l.json completed: 7072 passed, 993 FAILED LSR.l.json.gz completed, all passed!
LSR.w.json completed: 7541 passed, 524 FAILED LSR.w.json.gz completed: 7909 passed, 156 FAILED
MOVE.b.json completed, all passed! MOVE.b.json.gz completed, all passed!
MOVE.l.json completed: 5827 passed, 2238 FAILED MOVE.l.json.gz completed: 5827 passed, 2238 FAILED
MOVE.q.json completed, all passed! MOVE.q.json.gz completed, all passed!
MOVE.w.json completed: 5855 passed, 2210 FAILED MOVE.w.json.gz completed: 5855 passed, 2210 FAILED
MOVEA.l.json completed, all passed! MOVEA.l.json.gz completed, all passed!
MOVEA.w.json completed, all passed! MOVEA.w.json.gz completed, all passed!
MOVEM.l.json completed: 6035 passed, 2030 FAILED MOVEM.l.json.gz completed: 6035 passed, 2030 FAILED
MOVEM.w.json completed: 6431 passed, 1634 FAILED MOVEM.w.json.gz completed: 6431 passed, 1634 FAILED
MOVEP.l.json completed: 4036 passed, 4029 FAILED MOVEP.l.json.gz completed: 4036 passed, 4029 FAILED
MOVEP.w.json completed: 4046 passed, 4019 FAILED MOVEP.w.json.gz completed: 4046 passed, 4019 FAILED
MOVEfromSR.json completed: 6896 passed, 1169 FAILED MOVEfromSR.json.gz completed: 6896 passed, 1169 FAILED
MOVEfromUSP.json completed, all passed! MOVEfromUSP.json.gz completed, all passed!
MOVEtoCCR.json completed, all passed! MOVEtoCCR.json.gz completed, all passed!
MOVEtoSR.json completed, all passed! MOVEtoSR.json.gz completed, all passed!
MOVEtoUSP.json completed, all passed! MOVEtoUSP.json.gz completed, all passed!
MULS.json completed, all passed! MULS.json.gz completed, all passed!
MULU.json completed, all passed! MULU.json.gz completed, all passed!
NBCD.json completed: 8037 passed, 28 FAILED NBCD.json.gz completed: 8037 passed, 28 FAILED
NEG.b.json completed, all passed! NEG.b.json.gz completed, all passed!
NEG.l.json completed: 7552 passed, 513 FAILED NEG.l.json.gz completed: 7552 passed, 513 FAILED
NEG.w.json completed: 7531 passed, 534 FAILED NEG.w.json.gz completed: 7531 passed, 534 FAILED
NEGX.b.json completed, all passed! NEGX.b.json.gz completed, all passed!
NEGX.l.json completed: 7520 passed, 545 FAILED NEGX.l.json.gz completed: 7520 passed, 545 FAILED
NEGX.w.json completed: 7510 passed, 555 FAILED NEGX.w.json.gz completed: 7510 passed, 555 FAILED
NOP.json completed, all passed! NOP.json.gz completed, all passed!
NOT.b.json completed, all passed! NOT.b.json.gz completed, all passed!
NOT.l.json completed: 7512 passed, 553 FAILED NOT.l.json.gz completed: 7512 passed, 553 FAILED
NOT.w.json completed: 7530 passed, 535 FAILED NOT.w.json.gz completed: 7530 passed, 535 FAILED
OR.b.json completed, all passed! OR.b.json.gz completed, all passed!
OR.l.json completed: 7756 passed, 309 FAILED OR.l.json.gz completed: 7756 passed, 309 FAILED
OR.w.json completed: 7765 passed, 300 FAILED OR.w.json.gz completed: 7765 passed, 300 FAILED
ORItoCCR.json completed, all passed! ORItoCCR.json.gz completed, all passed!
ORItoSR.json completed, all passed! ORItoSR.json.gz completed, all passed!
PEA.json completed, all passed! PEA.json.gz completed, all passed!
RESET.json completed, all passed! RESET.json.gz completed, all passed!
ROL.b.json completed, all passed! ROL.b.json.gz completed, all passed!
ROL.l.json completed, all passed! ROL.l.json.gz completed, all passed!
ROL.w.json completed: 7882 passed, 183 FAILED ROL.w.json.gz completed: 7898 passed, 167 FAILED
ROR.b.json completed, all passed! ROR.b.json.gz completed, all passed!
ROR.l.json completed, all passed! ROR.l.json.gz completed, all passed!
ROR.w.json completed: 7907 passed, 158 FAILED ROR.w.json.gz completed: 7932 passed, 133 FAILED
ROXL.b.json completed: 8039 passed, 26 FAILED ROXL.b.json.gz completed: 8032 passed, 33 FAILED
ROXL.l.json completed: 8029 passed, 36 FAILED ROXL.l.json.gz completed: 8029 passed, 36 FAILED
ROXL.w.json completed: 7892 passed, 173 FAILED ROXL.w.json.gz completed: 7890 passed, 175 FAILED
ROXR.b.json completed: 8037 passed, 28 FAILED ROXR.b.json.gz completed: 8027 passed, 38 FAILED
ROXR.l.json completed: 8022 passed, 43 FAILED ROXR.l.json.gz completed: 8039 passed, 26 FAILED
ROXR.w.json completed: 7880 passed, 185 FAILED ROXR.w.json.gz completed: 7880 passed, 185 FAILED
RTE.json completed, all passed! RTE.json.gz completed, all passed!
RTR.json completed, all passed! RTR.json.gz completed, all passed!
RTS.json completed, all passed! RTS.json.gz completed, all passed!
SBCD.json completed: 6809 passed, 1256 FAILED SBCD.json.gz completed: 6809 passed, 1256 FAILED
SUB.b.json completed, all passed! SUB.b.json.gz completed, all passed!
SUB.l.json completed: 7747 passed, 318 FAILED SUB.l.json.gz completed: 7747 passed, 318 FAILED
SUB.w.json completed: 7716 passed, 349 FAILED SUB.w.json.gz completed: 7716 passed, 349 FAILED
SUBA.l.json completed, all passed! SUBA.l.json.gz completed, all passed!
SUBA.w.json completed, all passed! SUBA.w.json.gz completed, all passed!
SUBX.b.json completed, all passed! SUBX.b.json.gz completed, all passed!
SUBX.l.json completed: 5481 passed, 2584 FAILED SUBX.l.json.gz completed: 5481 passed, 2584 FAILED
SUBX.w.json completed, all passed! SUBX.w.json.gz completed, all passed!
SWAP.json completed, all passed! SWAP.json.gz completed, all passed!
Scc.json completed, all passed! Scc.json.gz completed, all passed!
TAS.json completed, all passed! TAS.json.gz completed, all passed!
TRAP.json completed, all passed! TRAP.json.gz completed, all passed!
TRAPV.json completed, all passed! TRAPV.json.gz completed, all passed!
TST.b.json completed, all passed! TST.b.json.gz completed, all passed!
TST.l.json completed, all passed! TST.l.json.gz completed, all passed!
TST.w.json completed, all passed! TST.w.json.gz completed, all passed!
UNLINK.json completed, all passed! UNLINK.json.gz completed, all passed!
passed: 957924, failed: 42136, total 96% passed: 966036, failed: 34024, total 97%
completed in 24m 47s completed in 0m 7s

View File

@ -2,7 +2,7 @@
const DEFAULT_HARTE_TESTS: &str = "tests/ProcessorTests/680x0/68000/v1/"; const DEFAULT_HARTE_TESTS: &str = "tests/ProcessorTests/680x0/68000/v1/";
use std::io::prelude::*; use std::io::prelude::*;
use std::fmt::{Debug, UpperHex}; use std::fmt::{Write, Debug, UpperHex};
use std::path::PathBuf; use std::path::PathBuf;
use std::time::SystemTime; use std::time::SystemTime;
use std::fs::{self, File}; use std::fs::{self, File};
@ -10,13 +10,22 @@ use std::fs::{self, File};
use clap::{Parser, ArgEnum}; use clap::{Parser, ArgEnum};
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use serde_derive::Deserialize; use serde_derive::Deserialize;
use femtos::Frequency; use femtos::{Instant, Frequency};
use moa_core::{System, Error, MemoryBlock, BusPort, Address, Addressable, Steppable, Device}; use emulator_hal::bus::BusAccess;
use emulator_hal::step::Step;
use emulator_hal_memory::MemoryBlock;
use moa_m68k::{M68k, M68kType}; use moa_m68k::{M68k, M68kType};
use moa_m68k::state::Status; use moa_m68k::state::Status;
#[derive(Clone, Debug)]
enum Error {
Assertion(String),
Bus(String),
Step(String),
}
#[derive(Copy, Clone, PartialEq, Eq, ArgEnum)] #[derive(Copy, Clone, PartialEq, Eq, ArgEnum)]
enum Selection { enum Selection {
Include, Include,
@ -106,7 +115,7 @@ impl TestState {
for word in self.prefetch.iter() { for word in self.prefetch.iter() {
print!("{:04x} ", *word); print!("{:04x} ", *word);
} }
println!(""); println!();
println!("ram: "); println!("ram: ");
for (addr, byte) in self.ram.iter() { for (addr, byte) in self.ram.iter() {
@ -137,25 +146,20 @@ impl TestCase {
} }
fn init_execute_test(cputype: M68kType, state: &TestState) -> Result<(M68k, System), Error> { #[allow(clippy::uninit_vec)]
let mut system = System::default(); fn init_execute_test(cputype: M68kType, state: &TestState) -> Result<(M68k, MemoryBlock<u32, Instant>), Error> {
// Insert basic initialization // Insert basic initialization
let data = vec![0; 0x01000000]; let len = 0x100_0000;
let mem = MemoryBlock::new(data); let mut data = Vec::with_capacity(len);
system.add_addressable_device(0x00000000, Device::new(mem)).unwrap(); unsafe { data.set_len(len); }
let mut memory = MemoryBlock::<u32, Instant>::from(data);
let port = if cputype <= M68kType::MC68010 { let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10));
BusPort::new(0, 24, 16, system.bus.clone())
} else {
BusPort::new(0, 32, 32, system.bus.clone())
};
let mut cpu = M68k::new(cputype, Frequency::from_mhz(10), port);
cpu.state.status = Status::Running; cpu.state.status = Status::Running;
load_state(&mut cpu, &mut system, state)?; load_state(&mut cpu, &mut memory, state)?;
Ok((cpu, system)) Ok((cpu, memory))
} }
fn assert_value<T>(actual: T, expected: T, message: &str) -> Result<(), Error> fn assert_value<T>(actual: T, expected: T, message: &str) -> Result<(), Error>
@ -165,11 +169,11 @@ where
if actual == expected { if actual == expected {
Ok(()) Ok(())
} else { } else {
Err(Error::assertion(&format!("{:#X} != {:#X}, {}", actual, expected, message))) Err(Error::Assertion(format!("{:#X} != {:#X}, {}", actual, expected, message)))
} }
} }
fn load_state(cpu: &mut M68k, system: &mut System, initial: &TestState) -> Result<(), Error> { fn load_state(cpu: &mut M68k, memory: &mut MemoryBlock<u32, Instant>, initial: &TestState) -> Result<(), Error> {
cpu.state.d_reg[0] = initial.d0; cpu.state.d_reg[0] = initial.d0;
cpu.state.d_reg[1] = initial.d1; cpu.state.d_reg[1] = initial.d1;
cpu.state.d_reg[2] = initial.d2; cpu.state.d_reg[2] = initial.d2;
@ -193,18 +197,20 @@ fn load_state(cpu: &mut M68k, system: &mut System, initial: &TestState) -> Resul
// Load instructions into memory // Load instructions into memory
for (i, ins) in initial.prefetch.iter().enumerate() { for (i, ins) in initial.prefetch.iter().enumerate() {
system.get_bus().write_beu16(system.clock, (initial.pc + (i as u32 * 2)) as u64, *ins)?; memory.write_beu16(Instant::START, initial.pc + (i as u32 * 2), *ins)
.map_err(|err| Error::Bus(format!("{:?}", err)))?;
} }
// Load data bytes into memory // Load data bytes into memory
for (addr, byte) in initial.ram.iter() { for (addr, byte) in initial.ram.iter() {
system.get_bus().write_u8(system.clock, *addr as u64, *byte)?; memory.write_u8(Instant::START, *addr, *byte)
.map_err(|err| Error::Bus(format!("{:?}", err)))?;
} }
Ok(()) Ok(())
} }
fn assert_state(cpu: &M68k, system: &System, expected: &TestState) -> Result<(), Error> { fn assert_state(cpu: &M68k, memory: &mut MemoryBlock<u32, Instant>, expected: &TestState) -> Result<(), Error> {
assert_value(cpu.state.d_reg[0], expected.d0, "d0")?; assert_value(cpu.state.d_reg[0], expected.d0, "d0")?;
assert_value(cpu.state.d_reg[1], expected.d1, "d1")?; assert_value(cpu.state.d_reg[1], expected.d1, "d1")?;
assert_value(cpu.state.d_reg[2], expected.d2, "d2")?; assert_value(cpu.state.d_reg[2], expected.d2, "d2")?;
@ -226,29 +232,32 @@ fn assert_state(cpu: &M68k, system: &System, expected: &TestState) -> Result<(),
assert_value(cpu.state.sr, expected.sr, "sr")?; assert_value(cpu.state.sr, expected.sr, "sr")?;
assert_value(cpu.state.pc, expected.pc, "pc")?; assert_value(cpu.state.pc, expected.pc, "pc")?;
let addr_mask = cpu.port.port.address_mask(); let addr_mask = 1_u32.wrapping_shl(cpu.info.address_width as u32).wrapping_sub(1);
// Load instructions into memory // Load instructions into memory
for (i, ins) in expected.prefetch.iter().enumerate() { for (i, ins) in expected.prefetch.iter().enumerate() {
let addr = expected.pc + (i as u32 * 2); let addr = expected.pc + (i as u32 * 2);
let actual = system.get_bus().read_beu16(system.clock, addr as Address & addr_mask)?; let actual = memory.read_beu16(Instant::START, addr & addr_mask)
.map_err(|err| Error::Bus(format!("{:?}", err)))?;
assert_value(actual, *ins, &format!("prefetch at {:x}", addr))?; assert_value(actual, *ins, &format!("prefetch at {:x}", addr))?;
} }
// Load data bytes into memory // Load data bytes into memory
for (addr, byte) in expected.ram.iter() { for (addr, byte) in expected.ram.iter() {
let actual = system.get_bus().read_u8(system.clock, *addr as Address & addr_mask)?; let actual = memory.read_u8(Instant::START, *addr & addr_mask)
.map_err(|err| Error::Bus(format!("{:?}", err)))?;
assert_value(actual, *byte, &format!("ram at {:x}", addr))?; assert_value(actual, *byte, &format!("ram at {:x}", addr))?;
} }
Ok(()) Ok(())
} }
fn step_cpu_and_assert(cpu: &mut M68k, system: &System, case: &TestCase, test_timing: bool) -> Result<(), Error> { fn step_cpu_and_assert(cpu: &mut M68k, memory: &mut MemoryBlock<u32, Instant>, case: &TestCase, test_timing: bool) -> Result<(), Error> {
let clock_elapsed = cpu.step(&system)?; let clock_elapsed = cpu.step(Instant::START, memory)
let cycles = clock_elapsed / cpu.frequency.period_duration(); .map_err(|err| Error::Step(format!("{:?}", err)))?;
let cycles = clock_elapsed.as_duration() / cpu.info.frequency.period_duration();
assert_state(&cpu, &system, &case.final_state)?; assert_state(cpu, memory, &case.final_state)?;
if test_timing { if test_timing {
assert_value(cycles, case.length as u64, "clock cycles")?; assert_value(cycles, case.length as u64, "clock cycles")?;
@ -257,22 +266,24 @@ fn step_cpu_and_assert(cpu: &mut M68k, system: &System, case: &TestCase, test_ti
} }
fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> { fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> {
let (mut cpu, system) = init_execute_test(M68kType::MC68000, &case.initial_state).unwrap(); let (mut cpu, mut memory) = init_execute_test(M68kType::MC68000, &case.initial_state).unwrap();
let mut initial_cpu = cpu.clone(); let initial_cpu = cpu.clone();
let result = step_cpu_and_assert(&mut cpu, &system, case, args.timing); let result = step_cpu_and_assert(&mut cpu, &mut memory, case, args.timing);
match result { match result {
Ok(()) => Ok(()), Ok(()) => Ok(()),
Err(err) => { Err(err) => {
if !args.quiet { if !args.quiet {
let mut writer = String::new();
if args.debug { if args.debug {
case.dump(); case.dump();
println!(""); writeln!(writer).unwrap();
initial_cpu.dump_state(); initial_cpu.dump_state(&mut writer).unwrap();
cpu.dump_state(); cpu.dump_state(&mut writer).unwrap();
} }
println!("FAILED: {:?}", err); writeln!(writer, "FAILED: {:?}", err).unwrap();
println!("{}", writer);
} }
Err(err) Err(err)
}, },
@ -303,11 +314,9 @@ fn test_json_file(path: PathBuf, args: &Args) -> (usize, usize, String) {
} }
// Only run the test if it's selected by the exceptions flag // Only run the test if it's selected by the exceptions flag
if case.is_extended_exception_case() && args.exceptions == Selection::ExcludeAddr { if case.is_extended_exception_case() && args.exceptions == Selection::ExcludeAddr
continue; || case.is_exception_case() && args.exceptions == Selection::Exclude
} else if case.is_exception_case() && args.exceptions == Selection::Exclude { || !case.is_exception_case() && args.exceptions == Selection::Only {
continue;
} else if !case.is_exception_case() && args.exceptions == Selection::Only {
continue; continue;
} }
@ -391,7 +400,7 @@ fn run_all_tests(args: &Args) {
} }
} }
println!(""); println!();
println!("passed: {}, failed: {}, total {:.0}%", passed, failed, ((passed as f32) / (passed as f32 + failed as f32)) * 100.0); println!("passed: {}, failed: {}, total {:.0}%", passed, failed, ((passed as f32) / (passed as f32 + failed as f32)) * 100.0);
println!("completed in {}m {}s", elapsed_secs / 60, elapsed_secs % 60); println!("completed in {}m {}s", elapsed_secs / 60, elapsed_secs % 60);
} }

View File

@ -176,7 +176,7 @@ where
if actual == expected { if actual == expected {
Ok(()) Ok(())
} else { } else {
Err(Error::assertion(&format!("{:#X} != {:#X}, {}", actual, expected, message))) Err(Error::assertion(format!("{:#X} != {:#X}, {}", actual, expected, message)))
} }
} }
@ -255,7 +255,7 @@ fn assert_state(cpu: &Z80, system: &System, io_bus: Rc<RefCell<Bus>>, expected:
let expected_im: InterruptMode = expected.im.into(); let expected_im: InterruptMode = expected.im.into();
if cpu.state.im != expected_im { if cpu.state.im != expected_im {
return Err(Error::assertion(&format!("{:?} != {:?}, im", cpu.state.im, expected_im))); return Err(Error::assertion(format!("{:?} != {:?}, im", cpu.state.im, expected_im)));
} }
assert_value(cpu.state.iff1 as u8, expected.iff1, "iff1")?; assert_value(cpu.state.iff1 as u8, expected.iff1, "iff1")?;
assert_value(cpu.state.iff2 as u8, expected.iff2, "iff2")?; assert_value(cpu.state.iff2 as u8, expected.iff2, "iff2")?;
@ -280,13 +280,13 @@ fn assert_state(cpu: &Z80, system: &System, io_bus: Rc<RefCell<Bus>>, expected:
} }
fn step_cpu_and_assert(cpu: &mut Z80, system: &System, io_bus: Rc<RefCell<Bus>>, case: &TestCase, args: &Args) -> Result<(), Error> { fn step_cpu_and_assert(cpu: &mut Z80, system: &System, io_bus: Rc<RefCell<Bus>>, case: &TestCase, args: &Args) -> Result<(), Error> {
let clock_elapsed = cpu.step(&system)?; let clock_elapsed = cpu.step(system)?;
assert_state(&cpu, &system, io_bus, &case.final_state, args.check_extra_flags, &case.ports)?; assert_state(cpu, system, io_bus, &case.final_state, args.check_extra_flags, &case.ports)?;
if args.check_timings { if args.check_timings {
let cycles = clock_elapsed / cpu.frequency.period_duration(); let cycles = clock_elapsed / cpu.frequency.period_duration();
if cycles != case.cycles.len() as Address { if cycles != case.cycles.len() as Address {
return Err(Error::assertion(&format!("expected instruction to take {} cycles, but took {}", case.cycles.len(), cycles))); return Err(Error::assertion(format!("expected instruction to take {} cycles, but took {}", case.cycles.len(), cycles)));
} }
} }
@ -305,7 +305,7 @@ fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> {
if !args.quiet { if !args.quiet {
if args.debug { if args.debug {
case.dump(); case.dump();
println!(""); println!();
initial_cpu.dump_state(system.clock); initial_cpu.dump_state(system.clock);
cpu.dump_state(system.clock); cpu.dump_state(system.clock);
} }
@ -425,7 +425,7 @@ fn run_all_tests(args: &Args) {
} }
} }
println!(""); println!();
println!("passed: {}, failed: {}, total {:.0}%", passed, failed, ((passed as f32) / (passed as f32 + failed as f32)) * 100.0); println!("passed: {}, failed: {}, total {:.0}%", passed, failed, ((passed as f32) / (passed as f32 + failed as f32)) * 100.0);
println!("completed in {}m {}s", elapsed_secs / 60, elapsed_secs % 60); println!("completed in {}m {}s", elapsed_secs / 60, elapsed_secs % 60);
} }
@ -439,7 +439,7 @@ fn is_undocumented_instruction(name: &str) -> bool {
match (opcodes[0], opcodes[1]) { match (opcodes[0], opcodes[1]) {
(0xCB, op) => { (0xCB, op) => {
op >= 0x30 && op <= 0x37 (0x30..=0x37).contains(&op)
}, },
(0xDD, 0xCB) | (0xDD, 0xCB) |
(0xFD, 0xCB) => { (0xFD, 0xCB) => {
@ -449,10 +449,8 @@ fn is_undocumented_instruction(name: &str) -> bool {
(0xFD, op) => { (0xFD, op) => {
let upper = op & 0xF0; let upper = op & 0xF0;
let lower = op & 0x0F; let lower = op & 0x0F;
!(lower == 0x06 && upper >= 0x30 && upper <= 0xB0 && upper != 0x70) && !(lower == 0x0E && (0x40..=0xB0).contains(&upper) || (0x70..=0x77).contains(&op) && op != 0x76 || op != 0x76 && (0x70..=0x77).contains(&op) || lower == 0x06 && (0x30..=0xB0).contains(&upper) && upper != 0x70) &&
!(lower == 0x0E && upper >= 0x40 && upper <= 0xB0) && !((0x21..=0x23).contains(&op) || (0x34..=0x36).contains(&op) || (0x29..=0x2B).contains(&op)) &&
!(op >= 0x70 && op <= 0x77 && op != 0x76) &&
!(op >= 0x21 && op <= 0x23 && op >= 0x34 && op <= 0x36 && op >= 0x29 && op <= 0x2B) &&
!(lower == 0x09 && upper <= 0x30) && !(lower == 0x09 && upper <= 0x30) &&
!(op == 0xE1 || op == 0xE3 || op == 0xE5 || op == 0xE9 || op == 0xF9) !(op == 0xE1 || op == 0xE3 || op == 0xE5 || op == 0xE9 || op == 0xF9)
}, },

View File

@ -1,24 +1,18 @@
* I want to push System, and BusPort into only the step function * fix dump_state everywhere, which now requires a writer. Is there an easier way? Is there a way that doesn't require std
* first I need to make Decoder take &mut Addressable, and still function like it does * can you clean it up more?
* next I need to make Executor only access through a &mut Addressable * implement the inspect and debug traits
* move the interrupt controller logic to the step() function only, and have a customish interrupt interface into the sim * move the interrupt controller logic to the step() function only, and have a customish interrupt interface into the sim
* move the impls for Step, Transmutable, etc into a moa.rs file or something
* the remaining code should really use Addressable, and then we can swap it for BusAccess
* the idea would be, instead of argument drilling, you create an object that is short lived, that lasts one instruction, or possibly even parts of one instruction, and
it has some references instead of "moving" data (or if you move, you move and move out without cloning), such that you can bundle everything up, call a method on the
bundle, with the execution context and state all part of or reference by the bundle, all instructions would be implemented on the bundle and not the state alone, and
after the instruction, or when transitioning from one phase to the next, you'd decompose the bundle back into its parts, and return before being called again to
repeat the process with the next instruction
* do the Z80? Should that be another PR?
* fix the tests
* fix all the clippy issues
* it doesn't work when using debug due to math checks, so fix them * it doesn't work when using debug due to math checks, so fix them
* change all the inspection and debugging things to return a struct which can then be printed by the frontend * change all the inspection and debugging things to return a struct which can then be printed by the frontend
* there are many issues with the coprocessor address space, and the VDP * there are many issues with the coprocessor address space, and the VDP
* I mapped the sn sound chip into 0xC00010, in the middle of the VDP's address space, and didn't get a runtime error!!! needs fixing * I mapped the sn sound chip into 0xC00010, in the middle of the VDP's address space, and didn't get a runtime error!!! needs fixing
* there should be a better way of aliasing addresses. Can you make the actual Z80 bus get mapped into 0xA00000? * there should be a better way of aliasing addresses. Can you make the actual Z80 bus get mapped into 0xA00000?
@ -47,7 +41,6 @@
* add rust runtime checks for math to look for overflow errors * add rust runtime checks for math to look for overflow errors
* fix the watchers in the Bus, maybe make them manual * fix the watchers in the Bus, maybe make them manual
* make it possible to compile without audio support (minifb frontend requires it atm) * make it possible to compile without audio support (minifb frontend requires it atm)
* does Z80 need a customized Z80BusPort like the 68k?
* can you make it so you don't need borrow_mut() so much? * can you make it so you don't need borrow_mut() so much?