mirror of
https://github.com/transistorfet/moa.git
synced 2024-12-03 07:49:17 +00:00
Merge pull request #2 from transistorfet/transistor/integrate-emulator-hal
Integrate emulator-hal traits
This commit is contained in:
commit
a2372d1355
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +1,6 @@
|
||||
[submodule "emulator/libraries/femtos"]
|
||||
path = emulator/libraries/femtos
|
||||
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
32
Cargo.lock
generated
@ -420,6 +420,17 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||
[[package]]
|
||||
name = "emulator-hal"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"femtos",
|
||||
"fugit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "emulator-hal-memory"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"emulator-hal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
@ -480,6 +491,21 @@ dependencies = [
|
||||
"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]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
@ -491,9 +517,10 @@ name = "harte-tests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap 3.2.25",
|
||||
"emulator-hal",
|
||||
"emulator-hal-memory",
|
||||
"femtos",
|
||||
"flate2",
|
||||
"moa-core",
|
||||
"moa-m68k",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@ -755,6 +782,8 @@ dependencies = [
|
||||
"log",
|
||||
"moa-common",
|
||||
"moa-core",
|
||||
"moa-debugger",
|
||||
"moa-host",
|
||||
"moa-m68k",
|
||||
"moa-peripherals-generic",
|
||||
"moa-peripherals-motorola",
|
||||
@ -794,6 +823,7 @@ name = "moa-m68k"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"emulator-hal",
|
||||
"emulator-hal-memory",
|
||||
"femtos",
|
||||
"log",
|
||||
"moa-core",
|
||||
|
@ -1,4 +1,5 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"emulator/core",
|
||||
"emulator/frontends/common",
|
||||
@ -10,6 +11,8 @@ members = [
|
||||
exclude = [
|
||||
"emulator/frontends/pixels",
|
||||
"emulator/frontends/macroquad",
|
||||
"emulator/libraries/femtos",
|
||||
"emulator/libraries/emulator-hal",
|
||||
]
|
||||
default-members = ["emulator/frontends/minifb"]
|
||||
|
||||
@ -18,5 +21,6 @@ opt-level = 3
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
# TODO there are many overflow errors, which could be bugs
|
||||
#overflow-checks = true
|
||||
|
||||
|
12
docs/log.txt
12
docs/log.txt
@ -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,
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
|
@ -8,4 +8,4 @@ log = "0.4"
|
||||
femtos = "0.1"
|
||||
thiserror = "1.0"
|
||||
moa-host = { path = "../libraries/host" }
|
||||
emulator-hal = { path = "/media/work/projects/emulator-hal/emulator-hal" }
|
||||
emulator-hal = { path = "../libraries/emulator-hal/emulator-hal" }
|
||||
|
@ -171,7 +171,7 @@ pub trait Debuggable {
|
||||
fn remove_breakpoint(&mut self, addr: Address);
|
||||
|
||||
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>;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
|
||||
use std::fmt;
|
||||
use std::error::{Error as StdError};
|
||||
use moa_host::HostError;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
@ -75,8 +74,8 @@ impl fmt::Display for Error {
|
||||
}
|
||||
|
||||
impl<E> From<HostError<E>> for Error {
|
||||
fn from(err: HostError<E>) -> Self {
|
||||
Self::Other(format!("other"))
|
||||
fn from(_err: HostError<E>) -> Self {
|
||||
Self::Other("other".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,11 +26,11 @@ impl InterruptController {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn check(&mut self) -> (bool, u8) {
|
||||
pub fn check(&mut self) -> (bool, u8, u8) {
|
||||
if self.highest > 0 {
|
||||
(true, self.highest)
|
||||
(true, self.highest, self.interrupts[self.highest as usize].1)
|
||||
} else {
|
||||
(false, 0)
|
||||
(false, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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::error::Error;
|
||||
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 emulator_hal::bus::{BusAccess};
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,13 @@ edition = "2021"
|
||||
log = "0.4"
|
||||
thiserror = "1.0"
|
||||
femtos = "0.1"
|
||||
moa-core = { path = "../../core" }
|
||||
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"]
|
||||
|
@ -1,12 +1,26 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use moa_core::Error;
|
||||
use moa_parsing::{self as parser, AssemblyLine, AssemblyOperand, AssemblyParser};
|
||||
use moa_parsing::{self as parser, AssemblyLine, AssemblyOperand, AssemblyParser, ParserError};
|
||||
|
||||
use super::state::M68kType;
|
||||
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)]
|
||||
#[derive(Copy, Clone)]
|
||||
@ -114,7 +128,7 @@ impl M68kAssembler {
|
||||
|
||||
fn parse(&mut self, text: &str) -> Result<Vec<(usize, AssemblyLine)>, Error> {
|
||||
let mut parser = AssemblyParser::new(text);
|
||||
parser.parse()
|
||||
Ok(parser.parse()?)
|
||||
}
|
||||
|
||||
fn apply_relocations(&mut self) -> Result<(), Error> {
|
||||
|
@ -17,7 +17,7 @@ fn main() {
|
||||
for word in words.iter() {
|
||||
print!("{:04x} ", word);
|
||||
}
|
||||
println!("");
|
||||
println!();
|
||||
},
|
||||
Err(err) => {
|
||||
println!("{:?}", err);
|
||||
|
@ -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::decode::M68kDecoder;
|
||||
use super::state::M68kError;
|
||||
use super::execute::M68kCycleExecutor;
|
||||
use super::memory::M68kAddress;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct StackTracer {
|
||||
@ -25,54 +26,16 @@ impl StackTracer {
|
||||
pub struct M68kDebugger {
|
||||
pub(crate) skip_breakpoint: usize,
|
||||
pub(crate) breakpoints: Vec<u32>,
|
||||
#[allow(dead_code)]
|
||||
pub(crate) step_until_return: Option<usize>,
|
||||
pub(crate) stack_tracer: StackTracer,
|
||||
}
|
||||
|
||||
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.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> {
|
||||
impl<'a, Bus, BusError> M68kCycleExecutor<'a, Bus>
|
||||
where
|
||||
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
|
||||
{
|
||||
pub fn check_breakpoints(&mut self) -> Result<(), M68kError<BusError>> {
|
||||
for breakpoint in &self.debugger.breakpoints {
|
||||
if *breakpoint == self.state.pc {
|
||||
if self.debugger.skip_breakpoint > 0 {
|
||||
@ -80,7 +43,7 @@ impl<'a> M68kCycleExecutor<'a> {
|
||||
return Ok(());
|
||||
} else {
|
||||
self.debugger.skip_breakpoint = 1;
|
||||
return Err(Error::breakpoint(format!("breakpoint reached: {:08x}", *breakpoint)));
|
||||
return Err(M68kError::Breakpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
|
||||
use femtos::Instant;
|
||||
|
||||
use moa_core::{Address, Addressable, BusPort};
|
||||
use emulator_hal::bus::BusAccess;
|
||||
|
||||
use crate::state::{M68kType, M68kError, Exceptions};
|
||||
use crate::memory::M68kBusPort;
|
||||
use crate::memory::{M68kBusPort, M68kAddress};
|
||||
use crate::instructions::{
|
||||
Size,
|
||||
Sign,
|
||||
@ -49,10 +48,13 @@ pub struct M68kDecoder {
|
||||
pub instruction: Instruction,
|
||||
}
|
||||
|
||||
pub struct InstructionDecoding<'a> {
|
||||
port: &'a mut BusPort,
|
||||
memory: &'a mut M68kBusPort,
|
||||
decoder: &'a mut M68kDecoder,
|
||||
pub struct InstructionDecoding<'a, Bus>
|
||||
where
|
||||
Bus: BusAccess<M68kAddress, Instant>,
|
||||
{
|
||||
pub(crate) bus: &'a mut Bus,
|
||||
pub(crate) memory: &'a mut M68kBusPort,
|
||||
pub(crate) decoder: &'a mut M68kDecoder,
|
||||
}
|
||||
|
||||
impl M68kDecoder {
|
||||
@ -76,10 +78,13 @@ impl M68kDecoder {
|
||||
}
|
||||
|
||||
#[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);
|
||||
let mut decoding = InstructionDecoding {
|
||||
port,
|
||||
bus,
|
||||
memory,
|
||||
decoder: self,
|
||||
};
|
||||
@ -87,21 +92,22 @@ impl M68kDecoder {
|
||||
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;
|
||||
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(()) => {
|
||||
self.dump_decoded(memory.current_clock, port);
|
||||
self.dump_decoded(memory.current_clock, bus);
|
||||
next = self.end;
|
||||
},
|
||||
Err(err) => {
|
||||
println!("{:?}", err);
|
||||
match err {
|
||||
M68kError::Exception(ex) if ex == Exceptions::IllegalInstruction => {
|
||||
println!(" at {:08x}: {:04x}", self.start, port.read_beu16(memory.current_clock, self.start as Address).unwrap());
|
||||
},
|
||||
_ => { },
|
||||
if let M68kError::Exception(Exceptions::IllegalInstruction) = err {
|
||||
println!(" at {:08x}: {:04x}", self.start, bus.read_beu16(memory.current_clock, self.start).unwrap());
|
||||
}
|
||||
return;
|
||||
},
|
||||
@ -109,18 +115,24 @@ impl M68kDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dump_decoded(&mut self, clock: Instant, port: &mut BusPort) {
|
||||
let ins_data: Result<String, M68kError> =
|
||||
pub fn dump_decoded<Bus>(&mut self, clock: Instant, bus: &mut Bus)
|
||||
where
|
||||
Bus: BusAccess<M68kAddress, Instant>,
|
||||
{
|
||||
let ins_data: Result<String, M68kError<Bus::Error>> =
|
||||
(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();
|
||||
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]
|
||||
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()?;
|
||||
self.decoder.instruction_word = ins;
|
||||
|
||||
@ -146,7 +158,7 @@ impl<'a> InstructionDecoding<'a> {
|
||||
}
|
||||
|
||||
#[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;
|
||||
|
||||
if (ins & 0x13F) == 0x03C {
|
||||
@ -221,14 +233,14 @@ impl<'a> InstructionDecoding<'a> {
|
||||
}
|
||||
|
||||
#[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 dest = self.decode_upper_effective_address(ins, Some(Size::Byte))?;
|
||||
Ok(Instruction::MOVE(src, dest, Size::Byte))
|
||||
}
|
||||
|
||||
#[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 dest = self.decode_upper_effective_address(ins, Some(Size::Long))?;
|
||||
if let Target::DirectAReg(reg) = dest {
|
||||
@ -239,7 +251,7 @@ impl<'a> InstructionDecoding<'a> {
|
||||
}
|
||||
|
||||
#[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 dest = self.decode_upper_effective_address(ins, Some(Size::Word))?;
|
||||
if let Target::DirectAReg(reg) = dest {
|
||||
@ -250,7 +262,7 @@ impl<'a> InstructionDecoding<'a> {
|
||||
}
|
||||
|
||||
#[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_00f0 = ins & 0x0F0;
|
||||
|
||||
@ -421,7 +433,7 @@ impl<'a> InstructionDecoding<'a> {
|
||||
}
|
||||
|
||||
#[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) {
|
||||
Some(size) => {
|
||||
let target = self.decode_lower_effective_address(ins, Some(size))?;
|
||||
@ -459,7 +471,7 @@ impl<'a> InstructionDecoding<'a> {
|
||||
}
|
||||
|
||||
#[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;
|
||||
if disp == 0 {
|
||||
disp = (self.read_instruction_word()? as i16) as i32;
|
||||
@ -475,7 +487,7 @@ impl<'a> InstructionDecoding<'a> {
|
||||
}
|
||||
|
||||
#[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 {
|
||||
return Err(M68kError::Exception(Exceptions::IllegalInstruction));
|
||||
}
|
||||
@ -485,7 +497,7 @@ impl<'a> InstructionDecoding<'a> {
|
||||
}
|
||||
|
||||
#[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);
|
||||
|
||||
if (ins & 0x1F0) == 0x100 {
|
||||
@ -509,7 +521,7 @@ impl<'a> InstructionDecoding<'a> {
|
||||
}
|
||||
|
||||
#[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 dir = (ins & 0x0100) >> 8;
|
||||
let size = get_size(ins);
|
||||
@ -540,7 +552,7 @@ impl<'a> InstructionDecoding<'a> {
|
||||
}
|
||||
|
||||
#[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 optype = (ins & 0x0100) >> 8;
|
||||
let size = get_size(ins);
|
||||
@ -567,7 +579,7 @@ impl<'a> InstructionDecoding<'a> {
|
||||
}
|
||||
|
||||
#[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);
|
||||
|
||||
if (ins & 0b0001_1111_0000) == 0b0001_0000_0000 {
|
||||
@ -600,7 +612,7 @@ impl<'a> InstructionDecoding<'a> {
|
||||
}
|
||||
|
||||
#[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 dir = (ins & 0x0100) >> 8;
|
||||
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) {
|
||||
Some(size) => {
|
||||
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> {
|
||||
let word = self.memory.read_instruction_word(self.port, self.decoder.is_supervisor, self.decoder.end)?;
|
||||
fn read_instruction_word(&mut self) -> Result<u16, M68kError<Bus::Error>> {
|
||||
let word = self.memory.read_instruction_word(self.bus, self.decoder.is_supervisor, self.decoder.end)?;
|
||||
self.decoder.end += 2;
|
||||
Ok(word)
|
||||
}
|
||||
|
||||
fn read_instruction_long(&mut self) -> Result<u32, M68kError> {
|
||||
let word = self.memory.read_instruction_long(self.port, self.decoder.is_supervisor, self.decoder.end)?;
|
||||
fn read_instruction_long(&mut self) -> Result<u32, M68kError<Bus::Error>> {
|
||||
let word = self.memory.read_instruction_long(self.bus, self.decoder.is_supervisor, self.decoder.end)?;
|
||||
self.decoder.end += 4;
|
||||
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 mode = get_low_mode(ins);
|
||||
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 mode = get_high_mode(ins);
|
||||
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 {
|
||||
0b00 | 0b01 => 0,
|
||||
0b10 => sign_extend_to_long(self.read_instruction_word()? as u32, Size::Word),
|
||||
@ -750,7 +762,7 @@ impl<'a> InstructionDecoding<'a> {
|
||||
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 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 {
|
||||
0b000 => Target::DirectDReg(reg),
|
||||
0b001 => Target::DirectAReg(reg),
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,5 +9,9 @@ pub mod memory;
|
||||
pub mod timing;
|
||||
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};
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
|
||||
use core::cmp;
|
||||
use core::fmt::Write;
|
||||
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, Exceptions};
|
||||
use crate::state::{M68k, M68kError, CpuInfo, Exceptions};
|
||||
use crate::instructions::Size;
|
||||
|
||||
#[repr(u8)]
|
||||
@ -76,7 +76,7 @@ impl Default for 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.code = FunctionCode::program(is_supervisor);
|
||||
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)]
|
||||
pub struct InstructionRequest {
|
||||
@ -110,8 +113,9 @@ pub struct InstructionRequest {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct M68kBusPort {
|
||||
//pub port: BusPort,
|
||||
pub request: MemoryRequest,
|
||||
pub data_bytewidth: usize,
|
||||
pub address_mask: u32,
|
||||
pub cycle_start_clock: Instant,
|
||||
pub current_clock: Instant,
|
||||
}
|
||||
@ -122,10 +126,11 @@ impl M68k {
|
||||
}
|
||||
|
||||
impl Default for M68kBusPort {
|
||||
fn default(/* port: BusPort */) -> Self {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
//port,
|
||||
request: Default::default(),
|
||||
data_bytewidth: 32 / 8,
|
||||
address_mask: 0xFFFF_FFFF,
|
||||
cycle_start_clock: Instant::START,
|
||||
current_clock: Instant::START,
|
||||
}
|
||||
@ -133,43 +138,101 @@ impl Default for M68kBusPort {
|
||||
}
|
||||
|
||||
impl M68kBusPort {
|
||||
pub fn new(clock: Instant) -> Self {
|
||||
pub fn from_info(info: &CpuInfo, clock: Instant) -> Self {
|
||||
Self {
|
||||
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,
|
||||
current_clock: clock,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn read_data_sized(&mut self, port: &mut BusPort, is_supervisor: bool, addr: Address, size: Size) -> Result<u32, M68kError> {
|
||||
self.start_request(is_supervisor, addr as u32, size, MemAccess::Read, MemType::Data, false)?;
|
||||
Ok(match size {
|
||||
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),
|
||||
Size::Long => port.read_beu32(self.current_clock, addr),
|
||||
}?)
|
||||
fn read<Bus, BusError>(&mut self, bus: &mut Bus, clock: Instant, addr: M68kAddress, data: &mut [u8]) -> Result<(), M68kError<BusError>>
|
||||
where
|
||||
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
|
||||
{
|
||||
let addr = addr & self.address_mask;
|
||||
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> {
|
||||
self.start_request(is_supervisor, addr as u32, size, MemAccess::Write, MemType::Data, false)?;
|
||||
Ok(match size {
|
||||
Size::Byte => port.write_u8(self.current_clock, addr, value as u8),
|
||||
Size::Word => port.write_beu16(self.current_clock, addr, value as u16),
|
||||
Size::Long => port.write_beu32(self.current_clock, addr, value),
|
||||
}?)
|
||||
fn write<Bus, BusError>(&mut self, bus: &mut Bus, clock: Instant, addr: M68kAddress, data: &[u8]) -> Result<(), M68kError<BusError>>
|
||||
where
|
||||
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
|
||||
{
|
||||
let addr = addr & self.address_mask;
|
||||
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)?;
|
||||
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)?;
|
||||
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.code = match mtype {
|
||||
MemType::Program => FunctionCode::program(is_supervisor),
|
||||
@ -185,13 +248,9 @@ impl M68kBusPort {
|
||||
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 {
|
||||
Ok(addr)
|
||||
} else {
|
||||
@ -199,22 +258,31 @@ fn validate_address(addr: u32) -> Result<u32, M68kError> {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl BusType for M68kBusPort {
|
||||
type Instant = Instant;
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl BusAccess<u32> for M68kBusPort {
|
||||
fn read(&mut self, now: Self::Instant, addr: Address, data: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
self.
|
||||
}
|
||||
|
||||
fn write(&mut self, now: Self::Instant, addr: Address, data: &[u8]) -> Result<usize, Self::Error> {
|
||||
pub fn dump_memory<Bus, Address, Instant>(bus: &mut Bus, clock: Instant, addr: Address, count: Address)
|
||||
where
|
||||
Bus: BusAccess<Address, Instant>,
|
||||
Address: From<u32> + Into<u32> + 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);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub(crate) struct TargetAccess {
|
||||
|
132
emulator/cpus/m68k/src/moa.rs
Normal file
132
emulator/cpus/m68k/src/moa.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,8 @@
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use femtos::{Instant, Frequency};
|
||||
|
||||
use moa_core::{Address, Bus, BusPort};
|
||||
use core::fmt::{self, Write};
|
||||
use femtos::{Duration, Frequency};
|
||||
|
||||
use crate::debugger::M68kDebugger;
|
||||
use crate::memory::M68kBusPort;
|
||||
use crate::instructions::Target;
|
||||
use crate::execute::M68kCycle;
|
||||
|
||||
@ -81,7 +77,7 @@ impl From<M68kType> for CoreType {
|
||||
}
|
||||
|
||||
impl CpuInfo {
|
||||
fn from(cputype: M68kType, frequency: Frequency) -> Self {
|
||||
pub fn from_type(cputype: M68kType, frequency: Frequency) -> Self {
|
||||
match cputype {
|
||||
M68kType::MC68008 => Self {
|
||||
chip: cputype,
|
||||
@ -178,7 +174,7 @@ pub struct M68kState {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
pub enum M68kError {
|
||||
pub enum M68kError<BusError> {
|
||||
#[error("cpu halted")]
|
||||
Halted,
|
||||
#[error("processor exception {0:?}")]
|
||||
@ -189,16 +185,35 @@ pub enum M68kError {
|
||||
Breakpoint,
|
||||
#[error("invalid instruction target, direct value used as a pointer: {0:?}")]
|
||||
InvalidTarget(Target),
|
||||
#[error("bus error")]
|
||||
BusError(BusError),
|
||||
#[error("error: {0}")]
|
||||
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)]
|
||||
pub struct M68k {
|
||||
pub info: CpuInfo,
|
||||
pub state: M68kState,
|
||||
pub debugger: M68kDebugger,
|
||||
pub port: BusPort,
|
||||
pub stats: M68kStatistics,
|
||||
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 {
|
||||
pub fn new(info: CpuInfo, port: BusPort) -> M68k {
|
||||
pub fn new(info: CpuInfo) -> Self {
|
||||
M68k {
|
||||
info,
|
||||
state: M68kState::default(),
|
||||
debugger: M68kDebugger::default(),
|
||||
port,
|
||||
stats: Default::default(),
|
||||
cycle: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_type(cputype: M68kType, frequency: Frequency, bus: Rc<RefCell<Bus>>, addr_offset: Address) -> Self {
|
||||
let info = CpuInfo::from(cputype, frequency);
|
||||
Self::new(info, BusPort::new(addr_offset, info.address_width as u8, info.data_width as u8, bus))
|
||||
pub fn from_type(cputype: M68kType, freq: Frequency) -> Self {
|
||||
Self::new(CpuInfo::from_type(cputype, freq))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,31 +1,30 @@
|
||||
|
||||
#[cfg(test)]
|
||||
mod decode_unit_tests {
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use femtos::Instant;
|
||||
|
||||
use moa_core::{Bus, BusPort, Address, Addressable, MemoryBlock, Device};
|
||||
use emulator_hal::bus::BusAccess;
|
||||
use emulator_hal_memory::MemoryBlock;
|
||||
|
||||
use crate::M68kType;
|
||||
use crate::instructions::{Target, Size, XRegister, BaseRegister, IndexRegister};
|
||||
use crate::decode::M68kDecoder;
|
||||
use crate::decode::{M68kDecoder, InstructionDecoding};
|
||||
use crate::memory::M68kBusPort;
|
||||
|
||||
const INIT_ADDR: Address = 0x00000000;
|
||||
const INIT_ADDR: u32 = 0x00000000;
|
||||
|
||||
fn init_decode_test(cputype: M68kType) -> (M68kBusPort, M68kDecoder) {
|
||||
let bus = Rc::new(RefCell::new(Bus::default()));
|
||||
let mem = MemoryBlock::new(vec![0; 0x0000100]);
|
||||
bus.borrow_mut().insert(0x00000000, Device::new(mem));
|
||||
|
||||
let port = if cputype <= M68kType::MC68010 {
|
||||
M68kBusPort::new(BusPort::new(0, 24, 16, bus))
|
||||
} else {
|
||||
M68kBusPort::new(BusPort::new(0, 32, 32, bus))
|
||||
fn run_decode_test<F>(cputype: M68kType, mut test_func: F)
|
||||
where
|
||||
F: FnMut(&mut InstructionDecoding<'_, MemoryBlock<u32, Instant>>),
|
||||
{
|
||||
let mut memory = MemoryBlock::from(vec![0; 0x0000100]);
|
||||
let mut decoder = M68kDecoder::new(cputype, true, 0);
|
||||
let mut decoding = InstructionDecoding {
|
||||
bus: &mut memory,
|
||||
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]
|
||||
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(&mut port, 0b000, 0b001, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::DirectDReg(1));
|
||||
let target = decoder.get_mode_as_target(0b000, 0b001, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::DirectDReg(1));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
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(&mut port, 0b001, 0b010, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::DirectAReg(2));
|
||||
let target = decoder.get_mode_as_target(0b001, 0b010, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::DirectAReg(2));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
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;
|
||||
let expected = 0x12345678;
|
||||
decoder.bus.write_beu32(Instant::START, INIT_ADDR, expected).unwrap();
|
||||
|
||||
port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap();
|
||||
|
||||
let target = decoder.get_mode_as_target(&mut port, 0b010, 0b010, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectAReg(2));
|
||||
let target = decoder.get_mode_as_target(0b010, 0b010, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectAReg(2));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
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;
|
||||
let expected = 0x12345678;
|
||||
decoder.bus.write_beu32(Instant::START, INIT_ADDR, expected).unwrap();
|
||||
|
||||
port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap();
|
||||
|
||||
let target = decoder.get_mode_as_target(&mut port, 0b011, 0b010, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectARegInc(2));
|
||||
let target = decoder.get_mode_as_target(0b011, 0b010, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectARegInc(2));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
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;
|
||||
let expected = 0x12345678;
|
||||
decoder.bus.write_beu32(Instant::START, INIT_ADDR, expected).unwrap();
|
||||
|
||||
port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap();
|
||||
|
||||
let target = decoder.get_mode_as_target(&mut port, 0b100, 0b010, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectARegDec(2));
|
||||
let target = decoder.get_mode_as_target(0b100, 0b010, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectARegDec(2));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
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;
|
||||
let offset = -8;
|
||||
decoder.bus.write_beu16(Instant::START, INIT_ADDR, (offset as i16) as u16).unwrap();
|
||||
|
||||
port.port.write_beu16(Instant::START, INIT_ADDR, (offset as i16) as u16).unwrap();
|
||||
|
||||
let target = decoder.get_mode_as_target(&mut port, 0b101, 0b100, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(4), None, offset));
|
||||
let target = decoder.get_mode_as_target(0b101, 0b100, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(4), None, offset));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
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;
|
||||
let offset = -8;
|
||||
let brief_extension = 0x3800 | (((offset as i8) as u8) as u16);
|
||||
decoder.bus.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
|
||||
decoder.bus.write_beu16(Instant::START, INIT_ADDR + 2, (offset as i16) as u16).unwrap();
|
||||
|
||||
port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
|
||||
port.port.write_beu16(Instant::START, INIT_ADDR + 2, (offset as i16) as u16).unwrap();
|
||||
|
||||
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));
|
||||
let target = decoder.get_mode_as_target(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]
|
||||
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;
|
||||
let offset = -1843235 as i32;
|
||||
let brief_extension = 0xF330;
|
||||
decoder.bus.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
|
||||
decoder.bus.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap();
|
||||
|
||||
port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
|
||||
port.port.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap();
|
||||
|
||||
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));
|
||||
let target = decoder.get_mode_as_target(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]
|
||||
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;
|
||||
let offset = -1843235 as i32;
|
||||
let brief_extension = 0xF3B0;
|
||||
decoder.bus.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
|
||||
decoder.bus.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap();
|
||||
|
||||
port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
|
||||
port.port.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap();
|
||||
|
||||
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));
|
||||
let target = decoder.get_mode_as_target(0b110, 0b010, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::None, Some(IndexRegister { xreg: XRegister::AReg(7), scale: 1, size: size }), offset));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
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;
|
||||
let offset = -1843235 as i32;
|
||||
let brief_extension = 0xF370;
|
||||
decoder.bus.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
|
||||
decoder.bus.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap();
|
||||
|
||||
port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
|
||||
port.port.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap();
|
||||
|
||||
let target = decoder.get_mode_as_target(&mut port, 0b110, 0b010, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(2), None, offset));
|
||||
let target = decoder.get_mode_as_target(0b110, 0b010, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(2), None, offset));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
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;
|
||||
let offset = -8;
|
||||
decoder.bus.write_beu16(Instant::START, INIT_ADDR, (offset as i16) as u16).unwrap();
|
||||
|
||||
port.port.write_beu16(Instant::START, INIT_ADDR, (offset as i16) as u16).unwrap();
|
||||
|
||||
let target = decoder.get_mode_as_target(&mut port, 0b111, 0b010, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, None, offset));
|
||||
let target = decoder.get_mode_as_target(0b111, 0b010, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, None, offset));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
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;
|
||||
let offset = -8;
|
||||
let brief_extension = 0x3000 | (((offset as i8) as u8) as u16);
|
||||
decoder.bus.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
|
||||
decoder.bus.write_beu16(Instant::START, INIT_ADDR + 2, (offset as i16) as u16).unwrap();
|
||||
|
||||
port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
|
||||
port.port.write_beu16(Instant::START, INIT_ADDR + 2, (offset as i16) as u16).unwrap();
|
||||
|
||||
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));
|
||||
let target = decoder.get_mode_as_target(0b111, 0b011, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, Some(IndexRegister { xreg: XRegister::DReg(3), scale: 0, size: size }), offset));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
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;
|
||||
let offset = -1843235 as i32;
|
||||
let brief_extension = 0xF330;
|
||||
decoder.bus.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
|
||||
decoder.bus.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap();
|
||||
|
||||
port.port.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
|
||||
port.port.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap();
|
||||
|
||||
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));
|
||||
let target = decoder.get_mode_as_target(0b111, 0b011, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, Some(IndexRegister { xreg: XRegister::AReg(7), scale: 1, size: size }), offset));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
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;
|
||||
let expected = 0x1234;
|
||||
decoder.bus.write_beu16(Instant::START, INIT_ADDR, expected as u16).unwrap();
|
||||
|
||||
port.port.write_beu16(Instant::START, INIT_ADDR, expected as u16).unwrap();
|
||||
|
||||
let target = decoder.get_mode_as_target(&mut port, 0b111, 0b000, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectMemory(expected, Size::Word));
|
||||
let target = decoder.get_mode_as_target(0b111, 0b000, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectMemory(expected, Size::Word));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
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;
|
||||
let expected = 0x12345678;
|
||||
decoder.bus.write_beu32(Instant::START, INIT_ADDR, expected).unwrap();
|
||||
|
||||
port.port.write_beu32(Instant::START, INIT_ADDR, expected).unwrap();
|
||||
|
||||
let target = decoder.get_mode_as_target(&mut port, 0b111, 0b001, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectMemory(expected, Size::Long));
|
||||
let target = decoder.get_mode_as_target(0b111, 0b001, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::IndirectMemory(expected, Size::Long));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
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;
|
||||
let expected = 0x1234;
|
||||
decoder.bus.write_beu16(Instant::START, INIT_ADDR, expected as u16).unwrap();
|
||||
|
||||
port.port.write_beu16(Instant::START, INIT_ADDR, expected as u16).unwrap();
|
||||
|
||||
let target = decoder.get_mode_as_target(&mut port, 0b111, 0b100, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::Immediate(expected));
|
||||
let target = decoder.get_mode_as_target(0b111, 0b100, Some(size)).unwrap();
|
||||
assert_eq!(target, Target::Immediate(expected));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod execute_unit_tests {
|
||||
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::execute::{Used, M68kCycle, M68kCycleExecutor};
|
||||
use crate::instructions::{Instruction, Target, Size};
|
||||
|
||||
const INIT_STACK: Address = 0x00002000;
|
||||
const INIT_ADDR: Address = 0x00000010;
|
||||
const INIT_STACK: u32 = 0x00002000;
|
||||
const INIT_ADDR: u32 = 0x00000010;
|
||||
|
||||
#[allow(clippy::uninit_vec)]
|
||||
fn run_execute_test<F>(cputype: M68kType, mut test_func: F)
|
||||
where
|
||||
F: FnMut(M68kCycleExecutor),
|
||||
F: FnMut(M68kCycleExecutor<&mut MemoryBlock<u32, Instant>>),
|
||||
{
|
||||
let mut system = System::default();
|
||||
|
||||
// Insert basic initialization
|
||||
let data = vec![0; 0x00100000];
|
||||
let mem = MemoryBlock::new(data);
|
||||
system.add_addressable_device(0x00000000, Device::new(mem)).unwrap();
|
||||
system.get_bus().write_beu32(system.clock, 0, INIT_STACK as u32).unwrap();
|
||||
system.get_bus().write_beu32(system.clock, 4, INIT_ADDR as u32).unwrap();
|
||||
let len = 0x10_0000;
|
||||
let mut data = Vec::with_capacity(len);
|
||||
unsafe { data.set_len(len); }
|
||||
let mut memory = MemoryBlock::from(data);
|
||||
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);
|
||||
cpu.step(&system).unwrap();
|
||||
let mut cycle = M68kCycle::new(&mut cpu, system.clock);
|
||||
let mut executor = cycle.begin(&mut cpu);
|
||||
let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10));
|
||||
cpu.step(Instant::START, &mut memory).unwrap();
|
||||
let cycle = M68kCycle::new(&mut cpu, Instant::START);
|
||||
|
||||
let mut executor = cycle.begin(&mut cpu, &mut memory);
|
||||
executor.cycle.decoder.init(true, executor.state.pc);
|
||||
assert_eq!(executor.state.pc, INIT_ADDR as u32);
|
||||
assert_eq!(executor.state.ssp, INIT_STACK as u32);
|
||||
@ -322,7 +323,7 @@ mod execute_unit_tests {
|
||||
let size = Size::Long;
|
||||
let expected = 0x12345678;
|
||||
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;
|
||||
let result = cycle.get_target_value(target, size, Used::Once).unwrap();
|
||||
@ -336,7 +337,7 @@ mod execute_unit_tests {
|
||||
let size = Size::Long;
|
||||
let expected = 0x12345678;
|
||||
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;
|
||||
let result = cycle.get_target_value(target, size, Used::Once).unwrap();
|
||||
@ -351,7 +352,7 @@ mod execute_unit_tests {
|
||||
let size = Size::Long;
|
||||
let expected = 0x12345678;
|
||||
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;
|
||||
let result = cycle.get_target_value(target, size, Used::Once).unwrap();
|
||||
@ -374,5 +375,3 @@ mod execute_unit_tests {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -9,6 +9,9 @@ pub struct M68kInstructionTiming {
|
||||
pub cputype: M68kType,
|
||||
pub bus_size: Size,
|
||||
|
||||
pub branched: bool,
|
||||
pub reps: u16,
|
||||
|
||||
pub accesses: u8,
|
||||
pub internal: u8,
|
||||
pub on_branch: u8,
|
||||
@ -22,6 +25,9 @@ impl M68kInstructionTiming {
|
||||
cputype,
|
||||
bus_size,
|
||||
|
||||
branched: false,
|
||||
reps: 0,
|
||||
|
||||
accesses: 0,
|
||||
internal: 0,
|
||||
on_branch: 0,
|
||||
@ -338,12 +344,27 @@ impl M68kInstructionTiming {
|
||||
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);
|
||||
(self.accesses as ClockCycles * 4)
|
||||
+ self.internal as ClockCycles
|
||||
+ (if branched { self.on_branch as ClockCycles } else { 0 })
|
||||
+ self.per_rep as ClockCycles * reps
|
||||
+ (if self.branched { self.on_branch as ClockCycles } else { 0 })
|
||||
+ self.per_rep as ClockCycles * self.reps
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -1,15 +1,15 @@
|
||||
|
||||
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};
|
||||
use moa_m68k::{M68k, M68kType, M68kAddress};
|
||||
use moa_m68k::instructions::{Instruction, Target, Size, Sign, XRegister, BaseRegister, IndexRegister, Direction};
|
||||
use moa_m68k::assembler::M68kAssembler;
|
||||
use moa_m68k::execute::M68kCycle;
|
||||
|
||||
const INIT_STACK: Address = 0x00002000;
|
||||
const INIT_ADDR: Address = 0x00000010;
|
||||
const INIT_STACK: M68kAddress = 0x00002000;
|
||||
const INIT_ADDR: M68kAddress = 0x00000010;
|
||||
|
||||
struct TestCase {
|
||||
cpu: M68kType,
|
||||
@ -17,6 +17,7 @@ struct TestCase {
|
||||
ins: Option<Instruction>,
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
const DECODE_TESTS: &'static [TestCase] = &[
|
||||
// MC68000
|
||||
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) {
|
||||
let mut system = System::default();
|
||||
|
||||
fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock<u32, Instant>) {
|
||||
// Insert basic initialization
|
||||
let data = vec![0; 0x00100000];
|
||||
let mem = MemoryBlock::new(data);
|
||||
system.add_addressable_device(0x00000000, Device::new(mem)).unwrap();
|
||||
system.get_bus().write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap();
|
||||
system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap();
|
||||
let len = 0x2000;
|
||||
let mut data = Vec::with_capacity(len);
|
||||
unsafe { data.set_len(len); }
|
||||
let mut memory = MemoryBlock::from(data);
|
||||
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
|
||||
let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0);
|
||||
//cpu.reset_cpu().unwrap();
|
||||
assert_eq!(cpu.state.pc, INIT_ADDR as u32);
|
||||
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)
|
||||
let cpu = M68k::from_type(cputype, Frequency::from_mhz(10));
|
||||
let cycle = M68kCycle::new(&cpu, Instant::START);
|
||||
(cpu, cycle, memory)
|
||||
}
|
||||
|
||||
fn load_memory(system: &System, data: &[u16]) {
|
||||
fn load_memory<Bus: BusAccess<u32, Instant>>(memory: &mut Bus, data: &[u16]) {
|
||||
let mut addr = INIT_ADDR;
|
||||
for word in data {
|
||||
system.get_bus().write_beu16(system.clock, addr, *word).unwrap();
|
||||
memory.write_beu16(Instant::START, addr, *word).unwrap();
|
||||
addr += 2;
|
||||
}
|
||||
}
|
||||
|
||||
fn run_decode_test(case: &TestCase) {
|
||||
let (mut cpu, cycle, system) = init_decode_test(case.cpu);
|
||||
load_memory(&system, case.data);
|
||||
let (mut cpu, cycle, mut memory) = init_decode_test(case.cpu);
|
||||
load_memory(&mut memory, case.data);
|
||||
|
||||
match &case.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();
|
||||
assert_eq!(executor.cycle.decoder.instruction, ins.clone());
|
||||
},
|
||||
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();
|
||||
println!("{:?}", executor.cycle.decoder.instruction);
|
||||
assert!(next.is_err());
|
||||
@ -121,6 +122,7 @@ pub fn run_decode_tests() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
pub fn run_assembler_tests() {
|
||||
let mut tests = 0;
|
||||
let mut errors = 0;
|
||||
|
@ -1,15 +1,16 @@
|
||||
|
||||
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};
|
||||
use moa_m68k::{M68k, M68kType, M68kAddress};
|
||||
use moa_m68k::state::M68kState;
|
||||
use moa_m68k::execute::{M68kCycle, M68kCycleExecutor};
|
||||
use moa_m68k::instructions::{Instruction, Target, Size, Sign, Direction, Condition};
|
||||
|
||||
const INIT_STACK: Address = 0x00002000;
|
||||
const INIT_ADDR: Address = 0x00000010;
|
||||
const INIT_STACK: M68kAddress = 0x00002000;
|
||||
const INIT_ADDR: M68kAddress = 0x00000010;
|
||||
|
||||
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)
|
||||
where
|
||||
F: FnMut(M68kCycleExecutor, System),
|
||||
F: FnMut(M68kCycleExecutor<&mut MemoryBlock<u32, Instant>>),
|
||||
{
|
||||
let mut system = System::default();
|
||||
|
||||
// Insert basic initialization
|
||||
let data = vec![0; 0x00100000];
|
||||
let mem = MemoryBlock::new(data);
|
||||
system.add_addressable_device(0x00000000, Device::new(mem)).unwrap();
|
||||
system.get_bus().write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap();
|
||||
system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap();
|
||||
let len = 0x10_0000;
|
||||
let mut data = Vec::with_capacity(len);
|
||||
unsafe { data.set_len(len); }
|
||||
let mut memory = MemoryBlock::from(data);
|
||||
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);
|
||||
cpu.step(&system).unwrap();
|
||||
let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10));
|
||||
cpu.step(Instant::START, &mut memory).unwrap();
|
||||
|
||||
let cycle = M68kCycle::new(&cpu, system.clock);
|
||||
let mut executor = cycle.begin(&mut cpu);
|
||||
let cycle = M68kCycle::new(&cpu, Instant::START);
|
||||
let executor = cycle.begin(&mut cpu, &mut memory);
|
||||
|
||||
assert_eq!(executor.state.pc, INIT_ADDR as u32);
|
||||
assert_eq!(executor.state.ssp, INIT_STACK as u32);
|
||||
assert_eq!(executor.state.pc, INIT_ADDR);
|
||||
assert_eq!(executor.state.ssp, INIT_STACK);
|
||||
assert_eq!(executor.cycle.decoder.instruction, Instruction::NOP);
|
||||
|
||||
test_func(executor, system)
|
||||
test_func(executor)
|
||||
}
|
||||
|
||||
fn build_state(state: &TestState) -> M68kState {
|
||||
@ -74,19 +75,19 @@ fn build_state(state: &TestState) -> M68kState {
|
||||
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() {
|
||||
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) {
|
||||
run_execute_test(case.cputype, |mut executor, system| {
|
||||
run_execute_test(case.cputype, |mut executor| {
|
||||
let init_state = build_state(&case.init);
|
||||
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.decode_next().unwrap();
|
||||
@ -95,7 +96,7 @@ fn run_test(case: &TestCase) {
|
||||
executor.execute_current().unwrap();
|
||||
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);
|
||||
});
|
||||
}
|
||||
@ -109,6 +110,7 @@ pub fn run_execute_tests() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
pub fn run_assembler_tests() {
|
||||
use moa_m68k::assembler::M68kAssembler;
|
||||
|
||||
@ -150,6 +152,7 @@ fn format_hex(data: &[u16]) -> String {
|
||||
.join(", ")
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
const TEST_CASES: &'static [TestCase] = &[
|
||||
TestCase {
|
||||
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 },
|
||||
},
|
||||
TestCase {
|
||||
name: "addx with extend",
|
||||
name: "addx with extend; zero flag not set",
|
||||
ins: Instruction::ADDX(Target::DirectDReg(1), Target::DirectDReg(0), Size::Byte),
|
||||
data: &[ 0xD101 ],
|
||||
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 },
|
||||
},
|
||||
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),
|
||||
data: &[ 0xD101 ],
|
||||
cputype: M68kType::MC68010,
|
||||
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 },
|
||||
},
|
||||
TestCase {
|
||||
@ -237,7 +256,15 @@ const TEST_CASES: &'static [TestCase] = &[
|
||||
data: &[ 0x027C, 0xF8FF ],
|
||||
cputype: M68kType::MC68010,
|
||||
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 {
|
||||
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 },
|
||||
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 {
|
||||
name: "movep long from even memory upper",
|
||||
ins: Instruction::MOVEP(0, 0, 0, Size::Long, Direction::FromTarget),
|
||||
data: &[ 0x0148, 0x0000 ],
|
||||
cputype: M68kType::MC68010,
|
||||
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 {
|
||||
name: "movep long from even memory lower",
|
||||
@ -601,7 +629,7 @@ const TEST_CASES: &'static [TestCase] = &[
|
||||
data: &[ 0x007C, 0x00AA ],
|
||||
cputype: M68kType::MC68010,
|
||||
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 },
|
||||
},
|
||||
|
||||
|
||||
|
@ -1,56 +1,57 @@
|
||||
|
||||
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};
|
||||
use moa_m68k::{M68k, M68kType, M68kAddress};
|
||||
use moa_m68k::instructions::{Instruction, Target, Size, Sign, Condition, XRegister, BaseRegister, IndexRegister, Direction};
|
||||
use moa_m68k::timing::M68kInstructionTiming;
|
||||
use moa_m68k::execute::M68kCycle;
|
||||
|
||||
|
||||
const INIT_STACK: Address = 0x00002000;
|
||||
const INIT_ADDR: Address = 0x00000010;
|
||||
|
||||
fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, System) {
|
||||
let mut system = System::default();
|
||||
const INIT_STACK: M68kAddress = 0x00002000;
|
||||
const INIT_ADDR: M68kAddress = 0x00000010;
|
||||
|
||||
#[allow(clippy::uninit_vec)]
|
||||
fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock<u32, Instant>) {
|
||||
// Insert basic initialization
|
||||
let data = vec![0; 0x00100000];
|
||||
let mem = MemoryBlock::new(data);
|
||||
system.add_addressable_device(0x00000000, Device::new(mem)).unwrap();
|
||||
system.get_bus().write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap();
|
||||
system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap();
|
||||
let len = 0x10_0000;
|
||||
let mut data = Vec::with_capacity(len);
|
||||
unsafe { data.set_len(len); }
|
||||
let mut memory = MemoryBlock::from(data);
|
||||
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
|
||||
let cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0);
|
||||
let cycle = M68kCycle::new(&cpu, system.clock);
|
||||
assert_eq!(cpu.state.pc, INIT_ADDR as u32);
|
||||
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)
|
||||
let cpu = M68k::from_type(cputype, Frequency::from_mhz(10));
|
||||
let cycle = M68kCycle::new(&cpu, Instant::START);
|
||||
(cpu, cycle, memory)
|
||||
}
|
||||
|
||||
fn load_memory(system: &System, data: &[u16]) {
|
||||
fn load_memory<Bus: BusAccess<u32, Instant>>(bus: &mut Bus, data: &[u16]) {
|
||||
let mut addr = INIT_ADDR;
|
||||
for word in data {
|
||||
system.get_bus().write_beu16(system.clock, addr, *word).unwrap();
|
||||
bus.write_beu16(Instant::START, addr, *word).unwrap();
|
||||
addr += 2;
|
||||
}
|
||||
}
|
||||
|
||||
fn run_timing_test(case: &TimingCase) -> Result<(), Error> {
|
||||
let (mut cpu, cycle, system) = init_decode_test(case.cpu);
|
||||
let mut executor = cycle.begin(&mut cpu);
|
||||
fn run_timing_test(case: &TimingCase) -> Result<(), String> {
|
||||
let (mut cpu, cycle, mut memory) = init_decode_test(case.cpu);
|
||||
load_memory(&mut memory, case.data);
|
||||
|
||||
let mut executor = cycle.begin(&mut cpu, &mut memory);
|
||||
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();
|
||||
assert_eq!(executor.cycle.decoder.instruction, case.ins.clone());
|
||||
|
||||
timing.add_instruction(&executor.cycle.decoder.instruction);
|
||||
let result = timing.calculate_clocks(false, 1);
|
||||
let result = timing.calculate_clocks();
|
||||
let expected = match case.cpu {
|
||||
M68kType::MC68000 => case.timing.0,
|
||||
M68kType::MC68010 => case.timing.1,
|
||||
@ -62,20 +63,20 @@ fn run_timing_test(case: &TimingCase) -> Result<(), Error> {
|
||||
Ok(())
|
||||
} else {
|
||||
println!("{:?}", timing);
|
||||
Err(Error::new(format!("expected {} but found {}", expected, result)))
|
||||
Err(format!("expected {} but found {}", expected, result))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
pub fn run_timing_tests() {
|
||||
let mut errors = 0;
|
||||
for case in TIMING_TESTS {
|
||||
// NOTE switched to only show the failures rather than all tests
|
||||
//print!("Testing for {:?}...", case.ins);
|
||||
//match run_timing_test(case) {
|
||||
// Ok(()) => println!("ok"),
|
||||
// Err(err) => { println!("{}", err.msg); errors += 1 },
|
||||
//}
|
||||
print!("Testing for {:?}...", case.ins);
|
||||
match run_timing_test(case) {
|
||||
Ok(()) => println!("ok"),
|
||||
Err(err) => { println!("{:?}", err); errors += 1 },
|
||||
}
|
||||
|
||||
if let Err(_) = run_timing_test(case) {
|
||||
errors += 1;
|
||||
@ -94,6 +95,7 @@ pub struct TimingCase {
|
||||
pub ins: Instruction,
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
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: &[0xF000], timing: ( 4, 4, 4), ins: Instruction::UnimplementedF(0xF000) },
|
||||
|
@ -1,15 +1,15 @@
|
||||
|
||||
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};
|
||||
use moa_m68k::{M68k, M68kType, M68kAddress};
|
||||
use moa_m68k::instructions::{Instruction, Target, Size};
|
||||
use moa_m68k::timing::M68kInstructionTiming;
|
||||
use moa_m68k::execute::M68kCycle;
|
||||
|
||||
const INIT_STACK: Address = 0x00002000;
|
||||
const INIT_ADDR: Address = 0x00000010;
|
||||
const INIT_STACK: M68kAddress = 0x00002000;
|
||||
const INIT_ADDR: M68kAddress = 0x00000010;
|
||||
|
||||
|
||||
struct TimingCase {
|
||||
@ -24,47 +24,45 @@ const TIMING_TESTS: &'static [TimingCase] = &[
|
||||
];
|
||||
|
||||
|
||||
fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, System) {
|
||||
let mut system = System::default();
|
||||
|
||||
fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock<u32, Instant>) {
|
||||
// Insert basic initialization
|
||||
let data = vec![0; 0x00100000];
|
||||
let mem = MemoryBlock::new(data);
|
||||
system.add_addressable_device(0x00000000, Device::new(mem)).unwrap();
|
||||
system.get_bus().write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap();
|
||||
system.get_bus().write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap();
|
||||
let len = 0x10_0000;
|
||||
let mut data = Vec::with_capacity(len);
|
||||
unsafe { data.set_len(len); }
|
||||
let mut memory = MemoryBlock::from(data);
|
||||
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
|
||||
let cpu = M68k::from_type(cputype, Frequency::from_mhz(10), system.bus.clone(), 0);
|
||||
//cpu.reset_cpu().unwrap();
|
||||
assert_eq!(cpu.state.pc, INIT_ADDR as u32);
|
||||
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)
|
||||
let cpu = M68k::from_type(cputype, Frequency::from_mhz(10));
|
||||
let cycle = M68kCycle::new(&cpu, Instant::START);
|
||||
(cpu, cycle, memory)
|
||||
}
|
||||
|
||||
fn load_memory(system: &System, data: &[u16]) {
|
||||
fn load_memory<Bus: BusAccess<u32, Instant>>(bus: &mut Bus, data: &[u16]) {
|
||||
let mut addr = INIT_ADDR;
|
||||
for word in data {
|
||||
system.get_bus().write_beu16(system.clock, addr, *word).unwrap();
|
||||
bus.write_beu16(Instant::START, addr, *word).unwrap();
|
||||
addr += 2;
|
||||
}
|
||||
}
|
||||
|
||||
fn run_timing_test(case: &TimingCase) -> Result<(), Error> {
|
||||
let (mut cpu, cycle, system) = init_decode_test(case.cpu);
|
||||
let mut executor = cycle.begin(&mut cpu);
|
||||
fn run_timing_test(case: &TimingCase) -> Result<(), String> {
|
||||
let (mut cpu, cycle, mut memory) = init_decode_test(case.cpu);
|
||||
load_memory(&mut memory, case.data);
|
||||
|
||||
let mut executor = cycle.begin(&mut cpu, &mut memory);
|
||||
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();
|
||||
assert_eq!(executor.cycle.decoder.instruction, case.ins.clone());
|
||||
|
||||
timing.add_instruction(&executor.cycle.decoder.instruction);
|
||||
let result = timing.calculate_clocks(false, 1);
|
||||
let result = timing.calculate_clocks();
|
||||
let expected = match case.cpu {
|
||||
M68kType::MC68000 => case.timing.0,
|
||||
M68kType::MC68010 => case.timing.1,
|
||||
@ -76,7 +74,7 @@ fn run_timing_test(case: &TimingCase) -> Result<(), Error> {
|
||||
Ok(())
|
||||
} else {
|
||||
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() {
|
||||
let mut errors = 0;
|
||||
for case in TIMING_TESTS {
|
||||
// NOTE switched to only show the failures rather than all tests
|
||||
//print!("Testing for {:?}...", case.ins);
|
||||
//match run_timing_test(case) {
|
||||
// Ok(()) => println!("ok"),
|
||||
// Err(err) => { println!("{}", err.msg); errors += 1 },
|
||||
//}
|
||||
print!("Testing for {:?}...", case.ins);
|
||||
match run_timing_test(case) {
|
||||
Ok(()) => println!("ok"),
|
||||
Err(err) => { println!("{:?}", err); errors += 1 },
|
||||
}
|
||||
|
||||
if let Err(_) = run_timing_test(case) {
|
||||
errors += 1;
|
||||
|
@ -9,4 +9,4 @@ thiserror = "1.0"
|
||||
femtos = "0.1"
|
||||
moa-core = { path = "../../core" }
|
||||
moa-signals = { path = "../../libraries/signals" }
|
||||
emulator-hal = { path = "/media/work/projects/emulator-hal/emulator-hal" }
|
||||
emulator-hal = { path = "../../libraries/emulator-hal/emulator-hal" }
|
||||
|
@ -30,7 +30,7 @@ impl Debuggable for Z80 {
|
||||
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();
|
||||
decoder.dump_disassembly(&mut self.port, addr as u16, count as u16);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
|
||||
use core::fmt::Write;
|
||||
use femtos::Instant;
|
||||
|
||||
use moa_core::{Address, Addressable};
|
||||
@ -6,17 +7,9 @@ use moa_core::{Address, Addressable};
|
||||
use crate::state::Z80Error;
|
||||
use crate::instructions::{Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target, LoadTarget, UndocumentedCopy, Instruction};
|
||||
|
||||
use emulator_hal::bus::{BusType, BusAccess};
|
||||
|
||||
struct Z80Bus;
|
||||
|
||||
type Z80Address = (bool, u16);
|
||||
|
||||
impl BusType for Z80Bus {
|
||||
//type Address = (bool, u16);
|
||||
type Error = Z80Error;
|
||||
type Instant = Instant;
|
||||
}
|
||||
//use emulator_hal::bus::BusAccess;
|
||||
//
|
||||
//type Z80Address = (bool, u16);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Z80Decoder {
|
||||
@ -560,10 +553,10 @@ impl Z80Decoder {
|
||||
}
|
||||
|
||||
pub fn format_instruction_bytes(&mut self, memory: &mut dyn Addressable) -> String {
|
||||
let ins_data: String =
|
||||
(0..self.end.saturating_sub(self.start)).map(|offset|
|
||||
format!("{:02x} ", memory.read_u8(self.clock, (self.start + offset) as Address).unwrap())
|
||||
).collect();
|
||||
let mut ins_data = String::new();
|
||||
for offset in 0..self.end.saturating_sub(self.start) {
|
||||
write!(ins_data, "{:02x} ", memory.read_u8(self.clock, (self.start + offset) as Address).unwrap()).unwrap()
|
||||
}
|
||||
ins_data
|
||||
}
|
||||
|
||||
|
@ -8,8 +8,6 @@ use crate::state::{Z80, Z80Error, Status, Flags};
|
||||
use crate::timing::Z80InstructionCycles;
|
||||
|
||||
|
||||
const DEV_NAME: &str = "z80-cpu";
|
||||
|
||||
const FLAGS_NUMERIC: u8 = 0xC0;
|
||||
const FLAGS_ARITHMETIC: u8 = 0x17;
|
||||
const FLAGS_CARRY_HALF_CARRY: u8 = 0x11;
|
||||
@ -70,8 +68,8 @@ impl From<Error> for Z80Error {
|
||||
fn from(err: Error) -> Self {
|
||||
match err {
|
||||
Error::Processor(ex) => Z80Error::BusError(format!("processor error {}", ex)),
|
||||
Error::Breakpoint(msg) => Z80Error::Breakpoint,
|
||||
Error::Other(msg) | Error::Assertion(msg) | Error::Emulator(_, msg) => Z80Error::BusError(format!("{}", msg)),
|
||||
Error::Breakpoint(_) => Z80Error::Breakpoint,
|
||||
Error::Other(msg) | Error::Assertion(msg) | Error::Emulator(_, msg) => Z80Error::BusError(msg.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use femtos::{Instant, Frequency};
|
||||
|
@ -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};
|
||||
|
||||
@ -22,7 +22,7 @@ impl CpalAudioOutput {
|
||||
.with_sample_rate(SampleRate(SAMPLE_RATE as u32))
|
||||
.into();
|
||||
|
||||
let data_callback = move |data: &mut [f32], info: &OutputCallbackInfo| {
|
||||
let data_callback = move |data: &mut [f32], _info: &OutputCallbackInfo| {
|
||||
let mut index = 0;
|
||||
while index < data.len() {
|
||||
if let Some((clock, mut frame)) = output.receive() {
|
||||
|
@ -9,10 +9,15 @@ use nix::fcntl::OFlag;
|
||||
use nix::pty::{self, PtyMaster};
|
||||
use nix::fcntl::{fcntl, FcntlArg};
|
||||
|
||||
use moa_core::Error;
|
||||
use moa_core::host::Tty;
|
||||
use moa_host::Tty;
|
||||
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum SimplePtyError {
|
||||
Open,
|
||||
PtsName,
|
||||
}
|
||||
|
||||
pub struct SimplePty {
|
||||
pub name: String,
|
||||
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| {
|
||||
pty::grantpt(&pty)?;
|
||||
pty::unlockpt(&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 (output_tx, output_rx) = mpsc::channel();
|
||||
let shared = SimplePty::new(name.clone(), input_rx, output_tx);
|
||||
|
@ -11,10 +11,12 @@ simple_logger = "^2"
|
||||
femtos = "0.1"
|
||||
|
||||
moa-core = { path = "../../core" }
|
||||
moa-host = { path = "../../libraries/host" }
|
||||
moa-common = { path = "../common", features = ["tty"] }
|
||||
|
||||
moa-debugger = { path = "../../libraries/debugger" }
|
||||
moa-systems-genesis = { path = "../../systems/genesis" }
|
||||
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-motorola = { path = "../../peripherals/motorola" }
|
||||
|
@ -3,7 +3,7 @@ use std::thread;
|
||||
use std::time::Duration;
|
||||
use femtos::Frequency;
|
||||
|
||||
use moa_core::{System, MemoryBlock, BusPort, Device};
|
||||
use moa_core::{System, MemoryBlock, Device};
|
||||
|
||||
use moa_m68k::{M68k, M68kType};
|
||||
use moa_peripherals_generic::AtaDevice;
|
||||
@ -28,7 +28,7 @@ fn main() {
|
||||
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.add_breakpoint(0x10781a);
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
use clap::{Arg, ArgAction};
|
||||
use clap::Arg;
|
||||
|
||||
use moa_console::ConsoleFrontend;
|
||||
use moa_systems_computie::{build_computie, ComputieOptions};
|
||||
@ -18,9 +18,9 @@ fn main() {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ fn main() {
|
||||
.help("ROM file to load (must be flat binary)"))
|
||||
.get_matches();
|
||||
|
||||
let mut frontend = ConsoleFrontend::new();
|
||||
let mut frontend = ConsoleFrontend;
|
||||
|
||||
let mut options = SegaGenesisOptions::default();
|
||||
if let Some(filename) = matches.get_one::<String>("ROM") {
|
||||
|
@ -3,8 +3,9 @@ use clap::{Command, Arg, ArgAction, ArgMatches};
|
||||
use std::io::{self, Write};
|
||||
use femtos::Duration;
|
||||
|
||||
use moa_core::{Error, System, DebugControl, Debugger};
|
||||
use moa_core::host::{Host, Tty, ControllerEvent, Audio, DummyAudio, FrameReceiver, EventSender};
|
||||
use moa_core::{Error, System};
|
||||
use moa_debugger::{Debugger, DebugControl};
|
||||
use moa_host::{Host, HostError, Tty, ControllerEvent, Audio, DummyAudio, FrameReceiver, EventSender};
|
||||
|
||||
pub struct ConsoleFrontend;
|
||||
|
||||
@ -13,7 +14,7 @@ impl Host for ConsoleFrontend {
|
||||
|
||||
fn add_pty(&self) -> Result<Box<dyn Tty>, HostError<Self::Error>> {
|
||||
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>> {
|
||||
@ -32,11 +33,13 @@ impl Host for ConsoleFrontend {
|
||||
}
|
||||
}
|
||||
|
||||
impl ConsoleFrontend {
|
||||
pub fn new() -> Self {
|
||||
impl Default for ConsoleFrontend {
|
||||
fn default() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl ConsoleFrontend {
|
||||
pub fn args(application_name: &'static str) -> Command {
|
||||
Command::new(application_name)
|
||||
.arg(Arg::new("log-level")
|
||||
|
@ -154,7 +154,7 @@ impl Debugger {
|
||||
};
|
||||
|
||||
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" => {
|
||||
|
1
emulator/libraries/emulator-hal
Submodule
1
emulator/libraries/emulator-hal
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 84e665ce5749187d0c323f77971c288d0964fa96
|
@ -69,7 +69,7 @@ impl MouseState {
|
||||
|
||||
let events: Vec<MouseEvent> = self
|
||||
.buttons.into_iter()
|
||||
.zip(next_state.buttons.into_iter())
|
||||
.zip(next_state.buttons)
|
||||
.enumerate()
|
||||
.filter_map(|(i, (prev, next))| {
|
||||
if prev != next {
|
||||
|
@ -24,8 +24,11 @@ pub enum HostError<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 {
|
||||
match self {
|
||||
HostError::TTYNotSupported => write!(f, "This frontend doesn't support PTYs"),
|
||||
@ -38,7 +41,6 @@ impl<E> fmt::Display for HostError<E> {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
pub trait Host {
|
||||
type Error: Error;
|
||||
|
@ -2,8 +2,14 @@
|
||||
use std::str::Chars;
|
||||
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)]
|
||||
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![];
|
||||
loop {
|
||||
let lineno = self.lexer.get_next_lineno();
|
||||
@ -47,7 +53,7 @@ impl<'input> AssemblyParser<'input> {
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn parse_line(&mut self) -> Result<Option<AssemblyLine>, Error> {
|
||||
fn parse_line(&mut self) -> Result<Option<AssemblyLine>, ParserError> {
|
||||
let token = loop {
|
||||
match self.lexer.get_next() {
|
||||
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))
|
||||
}
|
||||
|
||||
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![];
|
||||
|
||||
// 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![];
|
||||
|
||||
// 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()?;
|
||||
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") {
|
||||
(16, s)
|
||||
} 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)
|
||||
};
|
||||
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()
|
||||
}
|
||||
|
||||
pub fn expect_next(&mut self) -> Result<String, Error> {
|
||||
self.get_next().ok_or_else(|| Error::new(format!("unexpected end of input at line {}", self.lineno)))
|
||||
pub fn expect_next(&mut self) -> Result<String, ParserError> {
|
||||
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()?;
|
||||
if token == expected {
|
||||
Ok(())
|
||||
} 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();
|
||||
if token.is_none() || token.as_ref().unwrap() == "\n" {
|
||||
Ok(())
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
} else if *ch == ' ' || *ch == '\t' || *ch == '\r' {
|
||||
self.chars.next();
|
||||
@ -301,28 +305,28 @@ fn is_digit(ch: char) -> bool {
|
||||
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 {
|
||||
Ok(())
|
||||
} 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)?;
|
||||
if let AssemblyOperand::Label(name) = &args[0] {
|
||||
Ok(name.clone())
|
||||
} 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 {
|
||||
Ok(*value)
|
||||
} 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)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -725,13 +725,18 @@ pub struct Ym2612 {
|
||||
channels: Vec<Channel>,
|
||||
dac: Dac,
|
||||
|
||||
// TODO the timer hasn't been implemented yet
|
||||
#[allow(dead_code)]
|
||||
timer_a_enable: bool,
|
||||
timer_a: u16,
|
||||
#[allow(dead_code)]
|
||||
timer_a_current: u16,
|
||||
timer_a_overflow: bool,
|
||||
|
||||
#[allow(dead_code)]
|
||||
timer_b_enable: bool,
|
||||
timer_b: u8,
|
||||
#[allow(dead_code)]
|
||||
timer_b_current: u8,
|
||||
timer_b_overflow: bool,
|
||||
|
||||
@ -856,8 +861,8 @@ impl Ym2612 {
|
||||
0x28 => {
|
||||
let num = (data as usize) & 0x07;
|
||||
let ch = match num {
|
||||
0 | 1 | 2 => num,
|
||||
4 | 5 | 6 => num - 1,
|
||||
0..=2 => num,
|
||||
4..=6 => num - 1,
|
||||
_ => {
|
||||
log::warn!("{}: attempted key on/off to invalid channel {}", DEV_NAME, num);
|
||||
return;
|
||||
@ -1025,7 +1030,7 @@ impl Addressable for Ym2612 {
|
||||
|
||||
fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
|
||||
match addr {
|
||||
0 | 1 | 2 | 3 => {
|
||||
0..=3 => {
|
||||
// Read the status byte (busy/overflow)
|
||||
data[0] = ((self.timer_a_overflow as u8) << 1) | (self.timer_b_overflow as u8);
|
||||
}
|
||||
|
@ -8,6 +8,6 @@ log = "0.4"
|
||||
femtos = "0.1"
|
||||
moa-core = { path = "../../core" }
|
||||
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-motorola = { path = "../../peripherals/motorola" }
|
||||
|
@ -45,7 +45,7 @@ pub fn build_computie<H: Host>(host: &H, options: ComputieOptions) -> Result<Sys
|
||||
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.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))?;
|
||||
|
||||
|
||||
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.add_breakpoint(0x10781a);
|
||||
|
@ -10,6 +10,6 @@ moa-core = { path = "../../core" }
|
||||
moa-signals = { path = "../../libraries/signals" }
|
||||
moa-host = { path = "../../libraries/host" }
|
||||
moa-peripherals-yamaha = { path = "../../peripherals/yamaha" }
|
||||
moa-m68k = { path = "../../cpus/m68k" }
|
||||
moa-m68k = { path = "../../cpus/m68k", features = ["moa"] }
|
||||
moa-z80 = { path = "../../cpus/z80" }
|
||||
|
||||
|
@ -818,7 +818,7 @@ impl Addressable for Ym7101 {
|
||||
0x00 | 0x02 => self.state.memory.read_data_port(addr, data)?,
|
||||
|
||||
// Read from Control Port
|
||||
0x04 | 0x05 | 0x06 | 0x07 => {
|
||||
0x04..=0x07 => {
|
||||
log::debug!("{}: read status byte {:x}", DEV_NAME, self.state.status);
|
||||
for item in data {
|
||||
*item = if (addr % 2) == 0 {
|
||||
|
@ -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)?;
|
||||
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))?;
|
||||
|
||||
Ok(system)
|
||||
|
@ -9,6 +9,6 @@ femtos = "0.1"
|
||||
moa-core = { path = "../../core" }
|
||||
moa-host = { path = "../../libraries/host" }
|
||||
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-zilog = { path = "../../peripherals/zilog" }
|
||||
|
@ -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))?;
|
||||
|
||||
|
||||
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();
|
||||
//system.enable_debugging();
|
||||
|
@ -5,9 +5,10 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
femtos = "0.1"
|
||||
|
||||
moa-core = { path = "../../emulator/core" }
|
||||
emulator-hal = { path = "../../emulator/libraries/emulator-hal/emulator-hal" }
|
||||
emulator-hal-memory = { path = "../../emulator/libraries/emulator-hal/emulator-hal-memory" }
|
||||
moa-m68k = { path = "../../emulator/cpus/m68k" }
|
||||
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
|
@ -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
|
||||
ADD.b.json completed, all passed!
|
||||
ADD.l.json completed: 7736 passed, 329 FAILED
|
||||
ADD.w.json completed: 7712 passed, 353 FAILED
|
||||
ADDA.l.json completed, all passed!
|
||||
ADDA.w.json completed, all passed!
|
||||
ADDX.b.json completed, all passed!
|
||||
ADDX.l.json completed: 5472 passed, 2593 FAILED
|
||||
ADDX.w.json completed, all passed!
|
||||
AND.b.json completed, all passed!
|
||||
AND.l.json completed: 7779 passed, 286 FAILED
|
||||
AND.w.json completed: 7764 passed, 301 FAILED
|
||||
ANDItoCCR.json completed, all passed!
|
||||
ANDItoSR.json completed, all passed!
|
||||
ASL.b.json completed: 7238 passed, 827 FAILED
|
||||
ASL.l.json completed: 6471 passed, 1594 FAILED
|
||||
ASL.w.json completed: 7053 passed, 1012 FAILED
|
||||
ASR.b.json completed: 7547 passed, 518 FAILED
|
||||
ASR.l.json completed: 7092 passed, 973 FAILED
|
||||
ASR.w.json completed: 7513 passed, 552 FAILED
|
||||
BCHG.json completed, all passed!
|
||||
BCLR.json completed, all passed!
|
||||
BSET.json completed, all passed!
|
||||
BSR.json completed, all passed!
|
||||
BTST.json completed: 8052 passed, 13 FAILED
|
||||
Bcc.json completed, all passed!
|
||||
CHK.json completed: 7744 passed, 321 FAILED
|
||||
CLR.b.json completed, all passed!
|
||||
CLR.l.json completed: 7472 passed, 593 FAILED
|
||||
CLR.w.json completed: 7465 passed, 600 FAILED
|
||||
CMP.b.json completed, all passed!
|
||||
CMP.l.json completed, all passed!
|
||||
CMP.w.json completed, all passed!
|
||||
CMPA.l.json completed, all passed!
|
||||
CMPA.w.json completed, all passed!
|
||||
DBcc.json completed, all passed!
|
||||
DIVS.json completed, all passed!
|
||||
DIVU.json completed: 8064 passed, 1 FAILED
|
||||
EOR.b.json completed, all passed!
|
||||
EOR.l.json completed: 7519 passed, 546 FAILED
|
||||
EOR.w.json completed: 7525 passed, 540 FAILED
|
||||
EORItoCCR.json completed, all passed!
|
||||
EORItoSR.json completed, all passed!
|
||||
EXG.json completed, all passed!
|
||||
EXT.l.json completed, all passed!
|
||||
EXT.w.json completed, all passed!
|
||||
JMP.json completed, all passed!
|
||||
JSR.json completed, all passed!
|
||||
LEA.json completed, all passed!
|
||||
LINK.json completed, all passed!
|
||||
LSL.b.json completed: 7809 passed, 256 FAILED
|
||||
LSL.l.json completed: 7056 passed, 1009 FAILED
|
||||
LSL.w.json completed: 7523 passed, 542 FAILED
|
||||
LSR.b.json completed: 7817 passed, 248 FAILED
|
||||
LSR.l.json completed: 7072 passed, 993 FAILED
|
||||
LSR.w.json completed: 7541 passed, 524 FAILED
|
||||
MOVE.b.json completed, all passed!
|
||||
MOVE.l.json completed: 5827 passed, 2238 FAILED
|
||||
MOVE.q.json completed, all passed!
|
||||
MOVE.w.json completed: 5855 passed, 2210 FAILED
|
||||
MOVEA.l.json completed, all passed!
|
||||
MOVEA.w.json completed, all passed!
|
||||
MOVEM.l.json completed: 6035 passed, 2030 FAILED
|
||||
MOVEM.w.json completed: 6431 passed, 1634 FAILED
|
||||
MOVEP.l.json completed: 4036 passed, 4029 FAILED
|
||||
MOVEP.w.json completed: 4046 passed, 4019 FAILED
|
||||
MOVEfromSR.json completed: 6896 passed, 1169 FAILED
|
||||
MOVEfromUSP.json completed, all passed!
|
||||
MOVEtoCCR.json completed, all passed!
|
||||
MOVEtoSR.json completed, all passed!
|
||||
MOVEtoUSP.json completed, all passed!
|
||||
MULS.json completed, all passed!
|
||||
MULU.json completed, all passed!
|
||||
NBCD.json completed: 8037 passed, 28 FAILED
|
||||
NEG.b.json completed, all passed!
|
||||
NEG.l.json completed: 7552 passed, 513 FAILED
|
||||
NEG.w.json completed: 7531 passed, 534 FAILED
|
||||
NEGX.b.json completed, all passed!
|
||||
NEGX.l.json completed: 7520 passed, 545 FAILED
|
||||
NEGX.w.json completed: 7510 passed, 555 FAILED
|
||||
NOP.json completed, all passed!
|
||||
NOT.b.json completed, all passed!
|
||||
NOT.l.json completed: 7512 passed, 553 FAILED
|
||||
NOT.w.json completed: 7530 passed, 535 FAILED
|
||||
OR.b.json completed, all passed!
|
||||
OR.l.json completed: 7756 passed, 309 FAILED
|
||||
OR.w.json completed: 7765 passed, 300 FAILED
|
||||
ORItoCCR.json completed, all passed!
|
||||
ORItoSR.json completed, all passed!
|
||||
PEA.json completed, all passed!
|
||||
RESET.json completed, all passed!
|
||||
ROL.b.json completed, all passed!
|
||||
ROL.l.json completed, all passed!
|
||||
ROL.w.json completed: 7882 passed, 183 FAILED
|
||||
ROR.b.json completed, all passed!
|
||||
ROR.l.json completed, all passed!
|
||||
ROR.w.json completed: 7907 passed, 158 FAILED
|
||||
ROXL.b.json completed: 8039 passed, 26 FAILED
|
||||
ROXL.l.json completed: 8029 passed, 36 FAILED
|
||||
ROXL.w.json completed: 7892 passed, 173 FAILED
|
||||
ROXR.b.json completed: 8037 passed, 28 FAILED
|
||||
ROXR.l.json completed: 8022 passed, 43 FAILED
|
||||
ROXR.w.json completed: 7880 passed, 185 FAILED
|
||||
RTE.json completed, all passed!
|
||||
RTR.json completed, all passed!
|
||||
RTS.json completed, all passed!
|
||||
SBCD.json completed: 6809 passed, 1256 FAILED
|
||||
SUB.b.json completed, all passed!
|
||||
SUB.l.json completed: 7747 passed, 318 FAILED
|
||||
SUB.w.json completed: 7716 passed, 349 FAILED
|
||||
SUBA.l.json completed, all passed!
|
||||
SUBA.w.json completed, all passed!
|
||||
SUBX.b.json completed, all passed!
|
||||
SUBX.l.json completed: 5481 passed, 2584 FAILED
|
||||
SUBX.w.json completed, all passed!
|
||||
SWAP.json completed, all passed!
|
||||
Scc.json completed, all passed!
|
||||
TAS.json completed, all passed!
|
||||
TRAP.json completed, all passed!
|
||||
TRAPV.json completed, all passed!
|
||||
TST.b.json completed, all passed!
|
||||
TST.l.json completed, all passed!
|
||||
TST.w.json completed, all passed!
|
||||
UNLINK.json completed, all passed!
|
||||
ABCD.json.gz completed: 7993 passed, 72 FAILED
|
||||
ADD.b.json.gz completed, all passed!
|
||||
ADD.l.json.gz completed: 7736 passed, 329 FAILED
|
||||
ADD.w.json.gz completed: 7712 passed, 353 FAILED
|
||||
ADDA.l.json.gz completed, all passed!
|
||||
ADDA.w.json.gz completed, all passed!
|
||||
ADDX.b.json.gz completed, all passed!
|
||||
ADDX.l.json.gz completed: 5472 passed, 2593 FAILED
|
||||
ADDX.w.json.gz completed, all passed!
|
||||
AND.b.json.gz completed, all passed!
|
||||
AND.l.json.gz completed: 7779 passed, 286 FAILED
|
||||
AND.w.json.gz completed: 7764 passed, 301 FAILED
|
||||
ANDItoCCR.json.gz completed, all passed!
|
||||
ANDItoSR.json.gz completed, all passed!
|
||||
ASL.b.json.gz completed: 8063 passed, 2 FAILED
|
||||
ASL.l.json.gz completed, all passed!
|
||||
ASL.w.json.gz completed: 7896 passed, 169 FAILED
|
||||
ASR.b.json.gz completed: 7783 passed, 282 FAILED
|
||||
ASR.l.json.gz completed: 8029 passed, 36 FAILED
|
||||
ASR.w.json.gz completed: 7891 passed, 174 FAILED
|
||||
BCHG.json.gz completed, all passed!
|
||||
BCLR.json.gz completed, all passed!
|
||||
BSET.json.gz completed, all passed!
|
||||
BSR.json.gz completed, all passed!
|
||||
BTST.json.gz completed: 8051 passed, 14 FAILED
|
||||
Bcc.json.gz completed, all passed!
|
||||
CHK.json.gz completed: 7744 passed, 321 FAILED
|
||||
CLR.b.json.gz completed, all passed!
|
||||
CLR.l.json.gz completed: 7472 passed, 593 FAILED
|
||||
CLR.w.json.gz completed: 7465 passed, 600 FAILED
|
||||
CMP.b.json.gz completed, all passed!
|
||||
CMP.l.json.gz completed, all passed!
|
||||
CMP.w.json.gz completed, all passed!
|
||||
CMPA.l.json.gz completed, all passed!
|
||||
CMPA.w.json.gz completed, all passed!
|
||||
DBcc.json.gz completed, all passed!
|
||||
DIVS.json.gz completed, all passed!
|
||||
DIVU.json.gz completed: 8064 passed, 1 FAILED
|
||||
EOR.b.json.gz completed, all passed!
|
||||
EOR.l.json.gz completed: 7519 passed, 546 FAILED
|
||||
EOR.w.json.gz completed: 7525 passed, 540 FAILED
|
||||
EORItoCCR.json.gz completed, all passed!
|
||||
EORItoSR.json.gz completed, all passed!
|
||||
EXG.json.gz completed, all passed!
|
||||
EXT.l.json.gz completed, all passed!
|
||||
EXT.w.json.gz completed, all passed!
|
||||
JMP.json.gz completed, all passed!
|
||||
JSR.json.gz completed, all passed!
|
||||
LEA.json.gz completed, all passed!
|
||||
LINK.json.gz completed, all passed!
|
||||
LSL.b.json.gz completed, all passed!
|
||||
LSL.l.json.gz completed, all passed!
|
||||
LSL.w.json.gz completed: 7910 passed, 155 FAILED
|
||||
LSR.b.json.gz completed, all passed!
|
||||
LSR.l.json.gz completed, all passed!
|
||||
LSR.w.json.gz completed: 7909 passed, 156 FAILED
|
||||
MOVE.b.json.gz completed, all passed!
|
||||
MOVE.l.json.gz completed: 5827 passed, 2238 FAILED
|
||||
MOVE.q.json.gz completed, all passed!
|
||||
MOVE.w.json.gz completed: 5855 passed, 2210 FAILED
|
||||
MOVEA.l.json.gz completed, all passed!
|
||||
MOVEA.w.json.gz completed, all passed!
|
||||
MOVEM.l.json.gz completed: 6035 passed, 2030 FAILED
|
||||
MOVEM.w.json.gz completed: 6431 passed, 1634 FAILED
|
||||
MOVEP.l.json.gz completed: 4036 passed, 4029 FAILED
|
||||
MOVEP.w.json.gz completed: 4046 passed, 4019 FAILED
|
||||
MOVEfromSR.json.gz completed: 6896 passed, 1169 FAILED
|
||||
MOVEfromUSP.json.gz completed, all passed!
|
||||
MOVEtoCCR.json.gz completed, all passed!
|
||||
MOVEtoSR.json.gz completed, all passed!
|
||||
MOVEtoUSP.json.gz completed, all passed!
|
||||
MULS.json.gz completed, all passed!
|
||||
MULU.json.gz completed, all passed!
|
||||
NBCD.json.gz completed: 8037 passed, 28 FAILED
|
||||
NEG.b.json.gz completed, all passed!
|
||||
NEG.l.json.gz completed: 7552 passed, 513 FAILED
|
||||
NEG.w.json.gz completed: 7531 passed, 534 FAILED
|
||||
NEGX.b.json.gz completed, all passed!
|
||||
NEGX.l.json.gz completed: 7520 passed, 545 FAILED
|
||||
NEGX.w.json.gz completed: 7510 passed, 555 FAILED
|
||||
NOP.json.gz completed, all passed!
|
||||
NOT.b.json.gz completed, all passed!
|
||||
NOT.l.json.gz completed: 7512 passed, 553 FAILED
|
||||
NOT.w.json.gz completed: 7530 passed, 535 FAILED
|
||||
OR.b.json.gz completed, all passed!
|
||||
OR.l.json.gz completed: 7756 passed, 309 FAILED
|
||||
OR.w.json.gz completed: 7765 passed, 300 FAILED
|
||||
ORItoCCR.json.gz completed, all passed!
|
||||
ORItoSR.json.gz completed, all passed!
|
||||
PEA.json.gz completed, all passed!
|
||||
RESET.json.gz completed, all passed!
|
||||
ROL.b.json.gz completed, all passed!
|
||||
ROL.l.json.gz completed, all passed!
|
||||
ROL.w.json.gz completed: 7898 passed, 167 FAILED
|
||||
ROR.b.json.gz completed, all passed!
|
||||
ROR.l.json.gz completed, all passed!
|
||||
ROR.w.json.gz completed: 7932 passed, 133 FAILED
|
||||
ROXL.b.json.gz completed: 8032 passed, 33 FAILED
|
||||
ROXL.l.json.gz completed: 8029 passed, 36 FAILED
|
||||
ROXL.w.json.gz completed: 7890 passed, 175 FAILED
|
||||
ROXR.b.json.gz completed: 8027 passed, 38 FAILED
|
||||
ROXR.l.json.gz completed: 8039 passed, 26 FAILED
|
||||
ROXR.w.json.gz completed: 7880 passed, 185 FAILED
|
||||
RTE.json.gz completed, all passed!
|
||||
RTR.json.gz completed, all passed!
|
||||
RTS.json.gz completed, all passed!
|
||||
SBCD.json.gz completed: 6809 passed, 1256 FAILED
|
||||
SUB.b.json.gz completed, all passed!
|
||||
SUB.l.json.gz completed: 7747 passed, 318 FAILED
|
||||
SUB.w.json.gz completed: 7716 passed, 349 FAILED
|
||||
SUBA.l.json.gz completed, all passed!
|
||||
SUBA.w.json.gz completed, all passed!
|
||||
SUBX.b.json.gz completed, all passed!
|
||||
SUBX.l.json.gz completed: 5481 passed, 2584 FAILED
|
||||
SUBX.w.json.gz completed, all passed!
|
||||
SWAP.json.gz completed, all passed!
|
||||
Scc.json.gz completed, all passed!
|
||||
TAS.json.gz completed, all passed!
|
||||
TRAP.json.gz completed, all passed!
|
||||
TRAPV.json.gz completed, all passed!
|
||||
TST.b.json.gz completed, all passed!
|
||||
TST.l.json.gz completed, all passed!
|
||||
TST.w.json.gz completed, all passed!
|
||||
UNLINK.json.gz completed, all passed!
|
||||
|
||||
passed: 957924, failed: 42136, total 96%
|
||||
completed in 24m 47s
|
||||
passed: 966036, failed: 34024, total 97%
|
||||
completed in 0m 7s
|
||||
|
@ -2,7 +2,7 @@
|
||||
const DEFAULT_HARTE_TESTS: &str = "tests/ProcessorTests/680x0/68000/v1/";
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::fmt::{Debug, UpperHex};
|
||||
use std::fmt::{Write, Debug, UpperHex};
|
||||
use std::path::PathBuf;
|
||||
use std::time::SystemTime;
|
||||
use std::fs::{self, File};
|
||||
@ -10,13 +10,22 @@ use std::fs::{self, File};
|
||||
use clap::{Parser, ArgEnum};
|
||||
use flate2::read::GzDecoder;
|
||||
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::state::Status;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum Error {
|
||||
Assertion(String),
|
||||
Bus(String),
|
||||
Step(String),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, ArgEnum)]
|
||||
enum Selection {
|
||||
Include,
|
||||
@ -106,7 +115,7 @@ impl TestState {
|
||||
for word in self.prefetch.iter() {
|
||||
print!("{:04x} ", *word);
|
||||
}
|
||||
println!("");
|
||||
println!();
|
||||
|
||||
println!("ram: ");
|
||||
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> {
|
||||
let mut system = System::default();
|
||||
|
||||
#[allow(clippy::uninit_vec)]
|
||||
fn init_execute_test(cputype: M68kType, state: &TestState) -> Result<(M68k, MemoryBlock<u32, Instant>), Error> {
|
||||
// Insert basic initialization
|
||||
let data = vec![0; 0x01000000];
|
||||
let mem = MemoryBlock::new(data);
|
||||
system.add_addressable_device(0x00000000, Device::new(mem)).unwrap();
|
||||
let len = 0x100_0000;
|
||||
let mut data = Vec::with_capacity(len);
|
||||
unsafe { data.set_len(len); }
|
||||
let mut memory = MemoryBlock::<u32, Instant>::from(data);
|
||||
|
||||
let port = if cputype <= M68kType::MC68010 {
|
||||
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);
|
||||
let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10));
|
||||
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>
|
||||
@ -165,11 +169,11 @@ where
|
||||
if actual == expected {
|
||||
Ok(())
|
||||
} 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[1] = initial.d1;
|
||||
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
|
||||
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
|
||||
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(())
|
||||
}
|
||||
|
||||
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[1], expected.d1, "d1")?;
|
||||
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.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
|
||||
for (i, ins) in expected.prefetch.iter().enumerate() {
|
||||
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))?;
|
||||
}
|
||||
|
||||
// Load data bytes into memory
|
||||
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))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn step_cpu_and_assert(cpu: &mut M68k, system: &System, case: &TestCase, test_timing: bool) -> Result<(), Error> {
|
||||
let clock_elapsed = cpu.step(&system)?;
|
||||
let cycles = clock_elapsed / cpu.frequency.period_duration();
|
||||
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(Instant::START, memory)
|
||||
.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 {
|
||||
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> {
|
||||
let (mut cpu, system) = init_execute_test(M68kType::MC68000, &case.initial_state).unwrap();
|
||||
let mut initial_cpu = cpu.clone();
|
||||
let (mut cpu, mut memory) = init_execute_test(M68kType::MC68000, &case.initial_state).unwrap();
|
||||
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 {
|
||||
Ok(()) => Ok(()),
|
||||
Err(err) => {
|
||||
if !args.quiet {
|
||||
let mut writer = String::new();
|
||||
if args.debug {
|
||||
case.dump();
|
||||
println!("");
|
||||
initial_cpu.dump_state();
|
||||
cpu.dump_state();
|
||||
writeln!(writer).unwrap();
|
||||
initial_cpu.dump_state(&mut writer).unwrap();
|
||||
cpu.dump_state(&mut writer).unwrap();
|
||||
}
|
||||
println!("FAILED: {:?}", err);
|
||||
writeln!(writer, "FAILED: {:?}", err).unwrap();
|
||||
println!("{}", writer);
|
||||
}
|
||||
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
|
||||
if case.is_extended_exception_case() && args.exceptions == Selection::ExcludeAddr {
|
||||
continue;
|
||||
} else if case.is_exception_case() && args.exceptions == Selection::Exclude {
|
||||
continue;
|
||||
} else if !case.is_exception_case() && args.exceptions == Selection::Only {
|
||||
if case.is_extended_exception_case() && args.exceptions == Selection::ExcludeAddr
|
||||
|| case.is_exception_case() && args.exceptions == Selection::Exclude
|
||||
|| !case.is_exception_case() && args.exceptions == Selection::Only {
|
||||
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!("completed in {}m {}s", elapsed_secs / 60, elapsed_secs % 60);
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ where
|
||||
if actual == expected {
|
||||
Ok(())
|
||||
} 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();
|
||||
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.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> {
|
||||
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 {
|
||||
let cycles = clock_elapsed / cpu.frequency.period_duration();
|
||||
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.debug {
|
||||
case.dump();
|
||||
println!("");
|
||||
println!();
|
||||
initial_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!("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]) {
|
||||
(0xCB, op) => {
|
||||
op >= 0x30 && op <= 0x37
|
||||
(0x30..=0x37).contains(&op)
|
||||
},
|
||||
(0xDD, 0xCB) |
|
||||
(0xFD, 0xCB) => {
|
||||
@ -449,10 +449,8 @@ fn is_undocumented_instruction(name: &str) -> bool {
|
||||
(0xFD, op) => {
|
||||
let upper = op & 0xF0;
|
||||
let lower = op & 0x0F;
|
||||
!(lower == 0x06 && upper >= 0x30 && upper <= 0xB0 && upper != 0x70) &&
|
||||
!(lower == 0x0E && upper >= 0x40 && upper <= 0xB0) &&
|
||||
!(op >= 0x70 && op <= 0x77 && op != 0x76) &&
|
||||
!(op >= 0x21 && op <= 0x23 && op >= 0x34 && op <= 0x36 && op >= 0x29 && op <= 0x2B) &&
|
||||
!(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) &&
|
||||
!((0x21..=0x23).contains(&op) || (0x34..=0x36).contains(&op) || (0x29..=0x2B).contains(&op)) &&
|
||||
!(lower == 0x09 && upper <= 0x30) &&
|
||||
!(op == 0xE1 || op == 0xE3 || op == 0xE5 || op == 0xE9 || op == 0xF9)
|
||||
},
|
||||
|
21
todo.txt
21
todo.txt
@ -1,24 +1,18 @@
|
||||
|
||||
* I want to push System, and BusPort into only the step function
|
||||
* first I need to make Decoder take &mut Addressable, and still function like it does
|
||||
* next I need to make Executor only access through a &mut Addressable
|
||||
* fix dump_state everywhere, which now requires a writer. Is there an easier way? Is there a way that doesn't require std
|
||||
* can you clean it up more?
|
||||
* 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 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
|
||||
|
||||
|
||||
|
||||
* 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
|
||||
* 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?
|
||||
@ -47,7 +41,6 @@
|
||||
* add rust runtime checks for math to look for overflow errors
|
||||
* fix the watchers in the Bus, maybe make them manual
|
||||
* 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?
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user