Compare commits

...

6 Commits

Author SHA1 Message Date
transistor 35e746f0af Rewrote core into libraries/system, using the emulator-hal interfaces
This is the proof-of-concept using computie, since it only needs one
peripheral and the m68k cpu, which was already rewritten and tested
2024-04-13 14:35:04 -07:00
transistor 90e617c5de Fixed genesis Z80 bus issue 2024-04-13 11:35:43 -07:00
transistor 471695aff5 Fixed timing tests and added no io tests option 2024-04-07 20:16:55 -07:00
transistor 1c5ad3999a Minor fixes 2024-04-07 19:52:10 -07:00
transistor 59199533eb Added a hacky Signalable trait to replace the Z80 signals 2024-04-07 10:38:41 -07:00
transistor 1bb66e3308 Updated emulator-hal 2024-03-31 22:33:51 -07:00
39 changed files with 1981 additions and 306 deletions

22
Cargo.lock generated
View File

@ -804,13 +804,12 @@ dependencies = [
"log", "log",
"moa-common", "moa-common",
"moa-core", "moa-core",
"moa-debugger",
"moa-host", "moa-host",
"moa-m68k", "moa-m68k",
"moa-peripherals-generic", "moa-peripherals-generic",
"moa-peripherals-motorola", "moa-peripherals-motorola",
"moa-system",
"moa-systems-computie", "moa-systems-computie",
"moa-systems-genesis",
"simple_logger", "simple_logger",
] ]
@ -884,9 +883,11 @@ dependencies = [
name = "moa-peripherals-generic" name = "moa-peripherals-generic"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"emulator-hal",
"femtos", "femtos",
"log", "log",
"moa-core", "moa-core",
"moa-system",
] ]
[[package]] [[package]]
@ -903,10 +904,12 @@ dependencies = [
name = "moa-peripherals-motorola" name = "moa-peripherals-motorola"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"emulator-hal",
"femtos", "femtos",
"log", "log",
"moa-core", "moa-core",
"moa-host", "moa-host",
"moa-system",
] ]
[[package]] [[package]]
@ -937,6 +940,17 @@ dependencies = [
"femtos", "femtos",
] ]
[[package]]
name = "moa-system"
version = "0.1.0"
dependencies = [
"emulator-hal",
"femtos",
"log",
"moa-host",
"thiserror",
]
[[package]] [[package]]
name = "moa-systems-computie" name = "moa-systems-computie"
version = "0.1.0" version = "0.1.0"
@ -948,6 +962,7 @@ dependencies = [
"moa-m68k", "moa-m68k",
"moa-peripherals-generic", "moa-peripherals-generic",
"moa-peripherals-motorola", "moa-peripherals-motorola",
"moa-system",
] ]
[[package]] [[package]]
@ -1247,9 +1262,10 @@ name = "rad-tests"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"clap 3.2.25", "clap 3.2.25",
"emulator-hal",
"emulator-hal-memory",
"femtos", "femtos",
"flate2", "flate2",
"moa-core",
"moa-z80", "moa-z80",
"serde", "serde",
"serde_derive", "serde_derive",

View File

@ -2,6 +2,7 @@
resolver = "2" resolver = "2"
members = [ members = [
"emulator/core", "emulator/core",
"emulator/libraries/system",
"emulator/frontends/common", "emulator/frontends/common",
"emulator/frontends/console", "emulator/frontends/console",
"emulator/frontends/minifb", "emulator/frontends/minifb",

View File

@ -21,8 +21,10 @@ For more about the Sega Genesis support, check out this series I wrote about imp
I've also generated rustdocs of the workspace. All the various crates within moa I've also generated rustdocs of the workspace. All the various crates within moa
are listed in the crates section in the sidebar on the left. There's not a lot are listed in the crates section in the sidebar on the left. There's not a lot
of doc comments in the code yet but I plan to eventually write more: of doc comments in the code yet but I plan to eventually write more:
[rustdocs for moa_core](http://jabberwocky.ca/moa/doc/moa_core/) [moa_core](http://jabberwocky.ca/moa/doc/moa_core/)
[rustdocs for ym2612](http://jabberwocky.ca/moa/doc/moa_peripherals_yamaha/ym2612/index.html) [moa_m68k](http://jabberwocky.ca/moa/doc/moa_m68k/)
[moa_z80](http://jabberwocky.ca/moa/doc/moa_z80/)
[ym2612](http://jabberwocky.ca/moa/doc/moa_peripherals_yamaha/ym2612/index.html)
This repository uses submodules, so make sure to clone with This repository uses submodules, so make sure to clone with
```sh ```sh

View File

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

View File

@ -101,6 +101,31 @@ pub trait Addressable {
} }
} }
impl<T> Addressable for &mut T
where
T: Addressable + ?Sized,
{
#[inline]
fn size(&self) -> usize {
T::size(self)
}
#[inline]
fn read(
&mut self,
now: Instant,
addr: Address,
data: &mut [u8],
) -> Result<(), Error> {
T::read(self, now, addr, data)
}
#[inline]
fn write(&mut self, now: Instant, addr: Address, data: &[u8]) -> Result<(), Error> {
T::write(self, now, addr, data)
}
}
#[inline] #[inline]
pub fn read_beu16(data: &[u8]) -> u16 { pub fn read_beu16(data: &[u8]) -> u16 {
(data[0] as u16) << 8 | (data[1] as u16) (data[0] as u16) << 8 | (data[1] as u16)
@ -171,6 +196,16 @@ pub trait Inspectable {
fn inspect(&mut self, system: &System, args: &[&str]) -> Result<(), Error>; fn inspect(&mut self, system: &System, args: &[&str]) -> Result<(), Error>;
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Signal {
Reset,
BusRequest,
}
pub trait Signalable {
fn set_signal(&mut self, signal: Signal, flag: bool) -> Result<(), Error>;
fn signal(&mut self, signal: Signal) -> Option<bool>;
}
pub trait Transmutable { pub trait Transmutable {
#[inline] #[inline]
@ -197,6 +232,11 @@ pub trait Transmutable {
fn as_inspectable(&mut self) -> Option<&mut dyn Inspectable> { fn as_inspectable(&mut self) -> Option<&mut dyn Inspectable> {
None None
} }
#[inline]
fn as_signalable(&mut self) -> Option<&mut dyn Signalable> {
None
}
} }
pub type TransmutableBox = Rc<RefCell<Box<dyn Transmutable>>>; pub type TransmutableBox = Rc<RefCell<Box<dyn Transmutable>>>;
@ -205,6 +245,7 @@ pub fn wrap_transmutable<T: Transmutable + 'static>(value: T) -> TransmutableBox
Rc::new(RefCell::new(Box::new(value))) Rc::new(RefCell::new(Box::new(value)))
} }
static NEXT_ID: AtomicUsize = AtomicUsize::new(1); static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]

View File

@ -71,3 +71,9 @@ impl<E> From<HostError<E>> for Error {
Self::Other("other".to_string()) Self::Other("other".to_string())
} }
} }
impl From<fmt::Error> for Error {
fn from(err: fmt::Error) -> Self {
Self::Other(format!("{:?}", err))
}
}

View File

@ -7,7 +7,8 @@ mod memory;
mod system; mod system;
pub use crate::devices::{ pub use crate::devices::{
Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Transmutable, TransmutableBox, Device, Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Signalable, Signal, Transmutable, TransmutableBox,
Device,
}; };
pub use crate::devices::{ pub use crate::devices::{
read_beu16, read_beu32, read_leu16, read_leu32, write_beu16, write_beu32, write_leu16, write_leu32, wrap_transmutable, read_beu16, read_beu32, read_leu16, read_leu32, write_beu16, write_beu32, write_leu16, write_leu32, wrap_transmutable,
@ -17,4 +18,4 @@ pub use crate::interrupts::InterruptController;
pub use crate::memory::{MemoryBlock, AddressTranslator, AddressRepeater, Bus, BusPort, dump_slice, dump_memory}; pub use crate::memory::{MemoryBlock, AddressTranslator, AddressRepeater, Bus, BusPort, dump_slice, dump_memory};
pub use crate::system::System; pub use crate::system::System;
pub use emulator_hal::bus::{BusAccess}; pub use emulator_hal::BusAccess;

View File

@ -4,6 +4,7 @@ use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt::Write; use std::fmt::Write;
use femtos::Instant; use femtos::Instant;
use emulator_hal::{self, BusAccess, Error as EmuError};
use crate::error::Error; use crate::error::Error;
use crate::devices::{Address, Addressable, Transmutable, Device, read_beu16}; use crate::devices::{Address, Addressable, Transmutable, Device, read_beu16};
@ -236,7 +237,7 @@ impl Bus {
let to = if count < 16 { count / 2 } else { 8 }; let to = if count < 16 { count / 2 } else { 8 };
for _ in 0..to { for _ in 0..to {
let word = self.read_beu16(clock, addr); let word = Addressable::read_beu16(self, clock, addr);
if word.is_err() { if word.is_err() {
println!("{}", line); println!("{}", line);
return; return;
@ -353,7 +354,7 @@ impl Addressable for BusPort {
for i in (0..data.len()).step_by(self.data_width as usize) { for i in (0..data.len()).step_by(self.data_width as usize) {
let addr_index = (addr + i as Address) & self.address_mask; let addr_index = (addr + i as Address) & self.address_mask;
let end = cmp::min(i + self.data_width as usize, data.len()); let end = cmp::min(i + self.data_width as usize, data.len());
subdevice.read(clock, addr_index, &mut data[i..end])?; Addressable::read(&mut *subdevice, clock, addr_index, &mut data[i..end])?;
} }
Ok(()) Ok(())
} }
@ -364,7 +365,7 @@ impl Addressable for BusPort {
for i in (0..data.len()).step_by(self.data_width as usize) { for i in (0..data.len()).step_by(self.data_width as usize) {
let addr_index = (addr + i as Address) & self.address_mask; let addr_index = (addr + i as Address) & self.address_mask;
let end = cmp::min(i + self.data_width as usize, data.len()); let end = cmp::min(i + self.data_width as usize, data.len());
subdevice.write(clock, addr_index, &data[i..end])?; Addressable::write(&mut *subdevice, clock, addr_index, &data[i..end])?;
} }
Ok(()) Ok(())
} }
@ -412,9 +413,7 @@ where
} }
} }
use emulator_hal::bus::{self, BusAccess}; impl BusError for Error {}
impl bus::Error for Error {}
impl BusAccess<u64> for &mut dyn Addressable { impl BusAccess<u64> for &mut dyn Addressable {
type Instant = Instant; type Instant = Instant;
@ -430,3 +429,18 @@ impl BusAccess<u64> for &mut dyn Addressable {
Ok(data.len()) Ok(data.len())
} }
} }
impl BusAccess<u64> for Bus {
type Instant = Instant;
type Error = Error;
fn read(&mut self, now: Instant, addr: Address, data: &mut [u8]) -> Result<usize, Self::Error> {
Addressable::read(self, now, addr, data)?;
Ok(data.len())
}
fn write(&mut self, now: Instant, addr: Address, data: &[u8]) -> Result<usize, Self::Error> {
Addressable::write(self, now, addr, data)?;
Ok(data.len())
}
}

View File

@ -86,11 +86,18 @@ impl Debuggable for M68k<Instant> {
} }
} }
fn print_current_step(&mut self, _system: &System) -> Result<(), Error> { fn print_current_step(&mut self, system: &System) -> Result<(), Error> {
let mut bus = system.bus.borrow_mut();
let mut adapter: BusAdapter<u32, u64, &mut dyn Addressable, Error> =
BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err);
// TODO this is called by the debugger, but should be called some other way // 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); let mut decoder = M68kDecoder::new(self.info.chip, true, self.state.pc);
//self.decoder.dump_decoded(&mut self.bus); decoder.decode_at(&mut adapter, &mut M68kBusPort::default(), true, self.state.pc)?;
//self.dump_state(); decoder.dump_decoded(system.clock, &mut adapter);
let mut writer = String::new();
self.dump_state(&mut writer)?;
println!("{}", writer);
Ok(()) Ok(())
} }

View File

@ -1,7 +1,7 @@
use core::fmt::Write; use core::fmt::Write;
use emulator_hal::{BusAccess, Instant as EmuInstant}; use emulator_hal::{BusAccess, Instant as EmuInstant};
use crate::state::{Z80Error, Z80Address} ; use crate::state::{Z80Error, Z80Address};
use crate::instructions::{ use crate::instructions::{
Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target, Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target,
LoadTarget, UndocumentedCopy, Instruction, LoadTarget, UndocumentedCopy, Instruction,
@ -55,27 +55,16 @@ impl Z80Decoder {
Ok(decoder.decoder) Ok(decoder.decoder)
} }
/* pub fn dump_disassembly<Bus>(bus: &mut Bus, start: Z80Address, length: Z80Address)
pub fn format_instruction_bytes(&mut self) -> String { where
let mut ins_data = String::new(); Bus: BusAccess<Z80Address>,
for offset in 0..self.decoder.end.saturating_sub(self.decoder.start) { {
write!(ins_data, "{:02x} ", self.bus.read_u8(self.clock, self.decoder.start + offset).unwrap()).unwrap()
}
ins_data
}
pub fn dump_decoded(&mut self) {
let ins_data = self.format_instruction_bytes();
println!("{:#06x}: {}\n\t{:?}\n", self.decoder.start, ins_data, self.decoder.instruction);
}
pub fn dump_disassembly(&mut self, start: Z80Address, length: Z80Address) {
let mut next = start; let mut next = start;
while next < (start + length) { while next < (start + length) {
match self.decode_at(self.clock, next) { match Z80Decoder::decode_at(bus, Bus::Instant::START, next) {
Ok(()) => { Ok(mut decoder) => {
self.dump_decoded(); decoder.dump_decoded(bus);
next = self.decoder.end; next = decoder.end;
}, },
Err(err) => { Err(err) => {
println!("{:?}", err); println!("{:?}", err);
@ -84,7 +73,25 @@ impl Z80Decoder {
} }
} }
} }
*/
pub fn dump_decoded<Bus>(&mut self, bus: &mut Bus)
where
Bus: BusAccess<Z80Address>,
{
let ins_data = self.format_instruction_bytes(bus);
println!("{:#06x}: {}\n\t{:?}\n", self.start, ins_data, self.instruction);
}
pub fn format_instruction_bytes<Bus>(&mut self, bus: &mut Bus) -> String
where
Bus: BusAccess<Z80Address>,
{
let mut ins_data = String::new();
for offset in 0..self.end.saturating_sub(self.start) {
write!(ins_data, "{:02x} ", bus.read_u8(Bus::Instant::START, self.start + offset).unwrap()).unwrap()
}
ins_data
}
} }
pub struct DecodeNext<'a, Bus, Instant> pub struct DecodeNext<'a, Bus, Instant>
@ -107,11 +114,7 @@ where
Ok(()) Ok(())
} }
pub fn decode_bare( pub fn decode_bare(&mut self, ins: u8, extra_instruction_bytes: u16) -> Result<Instruction, Z80Error> {
&mut self,
ins: u8,
extra_instruction_bytes: u16,
) -> Result<Instruction, Z80Error> {
self.decoder.extra_instruction_bytes = extra_instruction_bytes; self.decoder.extra_instruction_bytes = extra_instruction_bytes;
match get_ins_x(ins) { match get_ins_x(ins) {
0 => match get_ins_z(ins) { 0 => match get_ins_z(ins) {
@ -559,11 +562,7 @@ where
} }
} }
fn decode_index_target( fn decode_index_target(&mut self, index_reg: IndexRegister, z: u8) -> Result<Option<Target>, Z80Error> {
&mut self,
index_reg: IndexRegister,
z: u8,
) -> Result<Option<Target>, Z80Error> {
let result = match z { let result = match z {
4 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 0))), 4 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 0))),
5 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 1))), 5 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 1))),
@ -578,7 +577,9 @@ where
fn read_instruction_byte(&mut self) -> Result<u8, Z80Error> { fn read_instruction_byte(&mut self) -> Result<u8, Z80Error> {
let byte = self.bus.read_u8(self.clock, self.decoder.end) let byte = self
.bus
.read_u8(self.clock, self.decoder.end)
.map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?;
self.decoder.end = self.decoder.end.wrapping_add(1); self.decoder.end = self.decoder.end.wrapping_add(1);
Ok(byte) Ok(byte)
@ -587,7 +588,9 @@ where
fn read_instruction_word(&mut self) -> Result<u16, Z80Error> { fn read_instruction_word(&mut self) -> Result<u16, Z80Error> {
let mut bytes = [0; 2]; let mut bytes = [0; 2];
for byte in bytes.iter_mut() { for byte in bytes.iter_mut() {
*byte = self.bus.read_u8(self.clock, self.decoder.end & 0xFFFF) *byte = self
.bus
.read_u8(self.clock, self.decoder.end & 0xFFFF)
.map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?;
self.decoder.end = self.decoder.end.wrapping_add(1); self.decoder.end = self.decoder.end.wrapping_add(1);
} }

View File

@ -1,4 +1,3 @@
use emulator_hal::{BusAccess, Instant as EmuInstant, Error as EmuError, Step, Inspect, Debug, IntoAddress}; use emulator_hal::{BusAccess, Instant as EmuInstant, Error as EmuError, Step, Inspect, Debug, IntoAddress};
use crate::state::{Z80, Z80Error, Z80Address, Status}; use crate::state::{Z80, Z80Error, Z80Address, Status};
@ -23,10 +22,9 @@ where
fn step(&mut self, now: Self::Instant, bus: &mut Bus) -> Result<Self::Instant, Self::Error> { fn step(&mut self, now: Self::Instant, bus: &mut Bus) -> Result<Self::Instant, Self::Error> {
let mut executor = self.begin(now, bus)?; let mut executor = self.begin(now, bus)?;
executor.step_one()?; let clocks = executor.step_one()?;
self.previous_cycle = executor.end(); self.previous_cycle = executor.end();
// TODO fix this Ok(now + Instant::hertz_to_duration(self.frequency.as_hz() as u64) * clocks as u32)
Ok(now)
} }
} }
@ -51,11 +49,9 @@ where
fn step(&mut self, now: Self::Instant, bus: (&mut MemBus, &mut IoBus)) -> Result<Self::Instant, Self::Error> { fn step(&mut self, now: Self::Instant, bus: (&mut MemBus, &mut IoBus)) -> Result<Self::Instant, Self::Error> {
let executor = self.begin(now, bus)?; let executor = self.begin(now, bus)?;
executor.step_one()?; let clocks = executor.step_one()?;
self.previous_cycle = executor.end(); self.previous_cycle = executor.end();
// TODO fix this Ok(now + Instant::hertz_to_duration(self.frequency.as_hz() as u64) * clocks as u32)
Ok(now)
} }
} }
*/ */

View File

@ -5,7 +5,7 @@ use crate::instructions::{
Condition, Instruction, LoadTarget, Target, Register, InterruptMode, RegisterPair, IndexRegister, SpecialRegister, Condition, Instruction, LoadTarget, Target, Register, InterruptMode, RegisterPair, IndexRegister, SpecialRegister,
IndexRegisterHalf, Size, Direction, UndocumentedCopy, IndexRegisterHalf, Size, Direction, UndocumentedCopy,
}; };
use crate::state::{Z80, Z80Error, Z80State, Z80Address, Status, Flags}; use crate::state::{Z80, Z80Error, Z80State, Z80Signals, Z80Address, Status, Flags};
use crate::timing::Z80InstructionCycles; use crate::timing::Z80InstructionCycles;
use crate::debugger::Z80Debugger; use crate::debugger::Z80Debugger;
@ -41,12 +41,17 @@ impl<Instant> Z80<Instant>
where where
Instant: EmuInstant, Instant: EmuInstant,
{ {
pub(crate) fn begin<'a, Bus>(&'a mut self, clock: Instant, bus: &'a mut Bus) -> Result<ExecuteNext<'a, &mut Bus, Instant>, Z80Error> pub(crate) fn begin<'a, Bus>(
&'a mut self,
clock: Instant,
bus: &'a mut Bus,
) -> Result<ExecuteNext<'a, &'a mut Bus, Instant>, Z80Error>
where where
Bus: BusAccess<Z80Address, Instant = Instant>, Bus: BusAccess<Z80Address, Instant = Instant>,
{ {
let executor = ExecuteNext { let executor = ExecuteNext {
state: &mut self.state, state: &mut self.state,
signals: &mut self.signals,
debugger: &mut self.debugger, debugger: &mut self.debugger,
cycle: Z80Cycle::at_time(clock), cycle: Z80Cycle::at_time(clock),
bus, bus,
@ -61,6 +66,7 @@ where
Bus: BusAccess<Z80Address, Instant = Instant>, Bus: BusAccess<Z80Address, Instant = Instant>,
{ {
state: &'a mut Z80State, state: &'a mut Z80State,
signals: &'a mut Z80Signals,
debugger: &'a mut Z80Debugger, debugger: &'a mut Z80Debugger,
cycle: Z80Cycle<Instant>, cycle: Z80Cycle<Instant>,
bus: Bus, bus: Bus,
@ -71,28 +77,22 @@ where
Bus: BusAccess<Z80Address, Instant = Instant>, Bus: BusAccess<Z80Address, Instant = Instant>,
Instant: EmuInstant, Instant: EmuInstant,
{ {
pub(crate) fn end(mut self) -> Z80Cycle<Instant> { pub(crate) fn end(self) -> Z80Cycle<Instant> {
self.cycle self.cycle
} }
pub(crate) fn step_one(&mut self) -> Result<u16, Z80Error> { pub(crate) fn step_one(&mut self) -> Result<u16, Z80Error> {
// TODO restore the reset and bus request signals let clocks = if self.signals.reset.get() {
//let clocks = if self.reset.get() { self.reset()?
// self.reset()? } else if self.signals.bus_request.get() {
//} else if self.bus_request.get() { 4
// 4 } else {
//} else { self.step_internal()?
// self.step_internal(self.cycle.current_clock)? };
//};
//Ok(self.frequency.period_duration() * clocks as u64)
// TODO remove this when done
let clocks = self.step_internal(self.cycle.current_clock)?;
Ok(clocks) Ok(clocks)
} }
fn step_internal(&mut self, clock: Instant) -> Result<u16, Z80Error> { fn step_internal(&mut self) -> Result<u16, Z80Error> {
match self.state.status { match self.state.status {
Status::Init => self.init(), Status::Init => self.init(),
Status::Halted => Err(Z80Error::Halted), Status::Halted => Err(Z80Error::Halted),
@ -630,7 +630,9 @@ where
let parity = if count != 0 { Flags::Parity as u8 } else { 0 }; let parity = if count != 0 { Flags::Parity as u8 } else { 0 };
self.set_flags(mask, parity); self.set_flags(mask, parity);
if (self.cycle.decoder.instruction == Instruction::LDIR || self.cycle.decoder.instruction == Instruction::LDDR) && count != 0 { if (self.cycle.decoder.instruction == Instruction::LDIR || self.cycle.decoder.instruction == Instruction::LDDR)
&& count != 0
{
self.cycle.took_branch = true; self.cycle.took_branch = true;
self.state.pc -= 2; self.state.pc -= 2;
} }
@ -1126,13 +1128,17 @@ where
fn read_port_u8(&mut self, addr: u16) -> Result<u8, Z80Error> { fn read_port_u8(&mut self, addr: u16) -> Result<u8, Z80Error> {
self.increment_refresh(1); self.increment_refresh(1);
Ok(self.bus.read_u8(self.cycle.current_clock, addr as Z80Address) Ok(self
.bus
.read_u8(self.cycle.current_clock, addr as Z80Address)
.map_err(|err| Z80Error::BusError(format!("{:?}", err)))?) .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?)
} }
fn write_port_u8(&mut self, addr: u16, value: u8) -> Result<(), Z80Error> { fn write_port_u8(&mut self, addr: u16, value: u8) -> Result<(), Z80Error> {
self.increment_refresh(1); self.increment_refresh(1);
Ok(self.bus.write_u8(self.cycle.current_clock, addr as Z80Address, value) Ok(self
.bus
.write_u8(self.cycle.current_clock, addr as Z80Address, value)
.map_err(|err| Z80Error::BusError(format!("{:?}", err)))?) .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?)
} }
@ -1144,7 +1150,9 @@ where
let mut bytes = [0; 2]; let mut bytes = [0; 2];
for byte in bytes.iter_mut() { for byte in bytes.iter_mut() {
self.increment_refresh(1); self.increment_refresh(1);
*byte = self.bus.read_u8(self.cycle.current_clock, addr & 0xFFFF) *byte = self
.bus
.read_u8(self.cycle.current_clock, addr & 0xFFFF)
.map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?;
addr = addr.wrapping_add(1); addr = addr.wrapping_add(1);
} }
@ -1159,7 +1167,8 @@ where
let mut bytes = value.to_le_bytes(); let mut bytes = value.to_le_bytes();
for byte in bytes.iter_mut() { for byte in bytes.iter_mut() {
self.increment_refresh(1); self.increment_refresh(1);
self.bus.write_u8(self.cycle.current_clock, addr & 0xFFFF, *byte) self.bus
.write_u8(self.cycle.current_clock, addr & 0xFFFF, *byte)
.map_err(|err| Z80Error::BusError(format!("{:?}", err)))?; .map_err(|err| Z80Error::BusError(format!("{:?}", err)))?;
addr = addr.wrapping_add(1); addr = addr.wrapping_add(1);
} }
@ -1172,7 +1181,7 @@ where
//if let Some(io) = self.ioport.as_mut() { //if let Some(io) = self.ioport.as_mut() {
// Ok(io.read_u8(self.cycle.current_clock, addr)?) // Ok(io.read_u8(self.cycle.current_clock, addr)?)
//} else { //} else {
Ok(0) Ok(0)
//} //}
} }

View File

@ -1,11 +1,14 @@
mod debugger; mod debugger;
mod decode; mod decode;
mod emuhal;
mod execute; mod execute;
mod instructions; mod instructions;
mod state; mod state;
mod timing; mod timing;
mod moa;
mod emuhal; //#[cfg(feature = "moa")]
pub mod moa;
pub use crate::moa::MoaZ80;
pub use crate::state::{Z80, Z80Type, Z80Error, Z80State, Status, Flags}; pub use crate::state::{Z80, Z80Type, Z80Error, Z80State, Status, Flags};
pub use crate::decode::Z80Decoder; pub use crate::decode::Z80Decoder;

View File

@ -1,35 +1,64 @@
use std::rc::Rc;
use std::cell::RefCell;
use femtos::{Instant, Duration}; use femtos::{Instant, Duration};
use emulator_hal::{BusAdapter, Instant as EmuInstant}; use emulator_hal::{BusAdapter, Instant as EmuInstant};
use moa_core::{System, Error, Address, Steppable, Addressable, Interruptable, Debuggable, Transmutable}; use moa_core::{System, Error, Bus, Address, Steppable, Addressable, Interruptable, Signalable, Signal, Debuggable, Transmutable};
use crate::{Z80, Z80Error, Z80Decoder}; use crate::{Z80, Z80Error, Z80Decoder};
use crate::instructions::Register; use crate::instructions::Register;
impl Steppable for Z80<Instant> pub struct MoaZ80<Instant>
where
Instant: EmuInstant,
{
pub bus: Rc<RefCell<Bus>>,
pub cpu: Z80<Instant>,
}
impl Steppable for MoaZ80<Instant>
where where
Instant: EmuInstant, Instant: EmuInstant,
{ {
fn step(&mut self, system: &System) -> Result<Duration, Error> { fn step(&mut self, system: &System) -> Result<Duration, Error> {
let bus = &mut *system.bus.borrow_mut(); let mut bus = &mut *self.bus.borrow_mut();
let mut adapter = BusAdapter::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err))); let mut adapter = BusAdapter::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err)));
let mut executor = self.begin(system.clock, &mut adapter)?; let mut executor = self.cpu.begin(system.clock, &mut adapter)?;
let clocks = executor.step_one()?; let clocks = executor.step_one()?;
self.previous_cycle = executor.end(); self.cpu.previous_cycle = executor.end();
Ok(Instant::hertz_to_duration(self.frequency.as_hz() as u64) * clocks as u32) Ok(Instant::hertz_to_duration(self.cpu.frequency.as_hz() as u64) * clocks as u32)
} }
fn on_error(&mut self, system: &System) { fn on_error(&mut self, system: &System) {
self.dump_state(system.clock); let bus = &mut *system.bus.borrow_mut();
let mut adapter = BusAdapter::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err)));
self.cpu.dump_state(system.clock, &mut adapter);
} }
} }
impl Interruptable for Z80<Instant> {} impl Interruptable for MoaZ80<Instant> {}
/*
impl Signalable for Z80<Instant> {
fn set_signal(&mut self, signal: Signal, flag: bool) -> Result<(), Error> {
match signal {
Signal::Reset => self.signals.reset = flag,
Signal::BusRequest => self.signals.bus_request = flag,
}
Ok(())
}
impl Transmutable for Z80<Instant> { fn signal(&mut self, signal: Signal) -> Option<bool> {
match signal {
Signal::Reset => Some(self.signals.reset),
Signal::BusRequest => Some(self.signals.bus_request),
}
}
}
*/
impl Transmutable for MoaZ80<Instant> {
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> { fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
Some(self) Some(self)
} }
@ -41,6 +70,11 @@ impl Transmutable for Z80<Instant> {
fn as_debuggable(&mut self) -> Option<&mut dyn Debuggable> { fn as_debuggable(&mut self) -> Option<&mut dyn Debuggable> {
Some(self) Some(self)
} }
//#[inline]
//fn as_signalable(&mut self) -> Option<&mut dyn Signalable> {
// Some(self)
//}
} }
impl From<Z80Error> for Error { impl From<Z80Error> for Error {
@ -64,14 +98,14 @@ impl From<Error> for Z80Error {
} }
} }
impl Debuggable for Z80<Instant> { impl Debuggable for MoaZ80<Instant> {
fn add_breakpoint(&mut self, addr: Address) { fn add_breakpoint(&mut self, addr: Address) {
self.debugger.breakpoints.push(addr as u16); self.cpu.debugger.breakpoints.push(addr as u16);
} }
fn remove_breakpoint(&mut self, addr: Address) { fn remove_breakpoint(&mut self, addr: Address) {
if let Some(index) = self.debugger.breakpoints.iter().position(|a| *a == addr as u16) { if let Some(index) = self.cpu.debugger.breakpoints.iter().position(|a| *a == addr as u16) {
self.debugger.breakpoints.remove(index); self.cpu.debugger.breakpoints.remove(index);
} }
} }
@ -79,22 +113,22 @@ impl Debuggable for Z80<Instant> {
let bus = &mut *system.bus.borrow_mut(); let bus = &mut *system.bus.borrow_mut();
let mut adapter = BusAdapter::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err))); let mut adapter = BusAdapter::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err)));
let decoder = Z80Decoder::decode_at(&mut adapter, system.clock, self.state.pc)?; let decoder = Z80Decoder::decode_at(&mut adapter, system.clock, self.cpu.state.pc)?;
// TODO disabled until decoder is fixed self.cpu.previous_cycle.decoder.dump_decoded(&mut adapter);
//self.decoder.dump_decoded(&mut self.port); self.cpu.dump_state(system.clock, &mut adapter);
self.dump_state(system.clock);
Ok(()) Ok(())
} }
fn print_disassembly(&mut self, _system: &System, addr: Address, count: usize) { fn print_disassembly(&mut self, system: &System, addr: Address, count: usize) {
// TODO disabled until decoder is fixed let bus = &mut *system.bus.borrow_mut();
//let mut decoder = Z80Decoder::default(); let mut adapter = BusAdapter::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err)));
//decoder.dump_disassembly(&mut self.port, addr as u16, count as u16);
Z80Decoder::dump_disassembly(&mut adapter, addr as u16, count as u16);
} }
fn run_command(&mut self, _system: &System, args: &[&str]) -> Result<bool, Error> { fn run_command(&mut self, _system: &System, args: &[&str]) -> Result<bool, Error> {
match args[0] { match args[0] {
"l" => self.state.reg[Register::L as usize] = 0x05, "l" => self.cpu.state.reg[Register::L as usize] = 0x05,
_ => { _ => {
return Ok(true); return Ok(true);
}, },
@ -102,5 +136,3 @@ impl Debuggable for Z80<Instant> {
Ok(false) Ok(false)
} }
} }

View File

@ -1,7 +1,7 @@
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use femtos::{Instant, Frequency}; use femtos::{Instant, Frequency};
use emulator_hal::Instant as EmuInstant; use emulator_hal::{Instant as EmuInstant, BusAccess};
use moa_core::{Address, Bus, BusPort}; use moa_core::{Address, Bus, BusPort};
use moa_signals::Signal; use moa_signals::Signal;
@ -92,6 +92,14 @@ impl Z80State {
} }
} }
#[derive(Clone, Debug, Default)]
pub struct Z80Signals {
//pub reset: bool,
//pub bus_request: bool,
pub reset: Signal<bool>,
pub bus_request: Signal<bool>,
}
#[derive(Clone, Debug, thiserror::Error)] #[derive(Clone, Debug, thiserror::Error)]
pub enum Z80Error /* <B: fmt::Display> */ { pub enum Z80Error /* <B: fmt::Display> */ {
#[error("cpu halted")] #[error("cpu halted")]
@ -120,8 +128,7 @@ pub struct Z80<Instant> {
pub state: Z80State, pub state: Z80State,
pub debugger: Z80Debugger, pub debugger: Z80Debugger,
pub previous_cycle: Z80Cycle<Instant>, pub previous_cycle: Z80Cycle<Instant>,
//pub port: BusPort, pub signals: Z80Signals,
//pub ioport: Option<BusPort>,
// TODO activate later // TODO activate later
//pub reset: Signal<bool>, //pub reset: Signal<bool>,
//pub bus_request: Signal<bool>, //pub bus_request: Signal<bool>,
@ -131,34 +138,22 @@ impl<Instant> Z80<Instant>
where where
Instant: EmuInstant, Instant: EmuInstant,
{ {
pub fn new(cputype: Z80Type, frequency: Frequency /*, port: BusPort, ioport: Option<BusPort>*/) -> Self { pub fn new(cputype: Z80Type, frequency: Frequency) -> Self {
Self { Self {
cputype, cputype,
frequency, frequency,
state: Z80State::default(), state: Z80State::default(),
debugger: Z80Debugger::default(), debugger: Z80Debugger::default(),
previous_cycle: Z80Cycle::at_time(Instant::START), previous_cycle: Z80Cycle::at_time(Instant::START),
//port, signals: Z80Signals::default(),
//ioport,
//reset: Signal::new(false), //reset: Signal::new(false),
//bus_request: Signal::new(false), //bus_request: Signal::new(false),
} }
} }
pub fn from_type( pub fn from_type(cputype: Z80Type, frequency: Frequency) -> Self {
cputype: Z80Type,
frequency: Frequency,
bus: Rc<RefCell<Bus>>,
addr_offset: Address,
io_bus: Option<(Rc<RefCell<Bus>>, Address)>,
) -> Self {
match cputype { match cputype {
Z80Type::Z80 => Self::new( Z80Type::Z80 => Self::new(cputype, frequency),
cputype,
frequency,
//BusPort::new(addr_offset, 16, 8, bus),
//io_bus.map(|(io_bus, io_offset)| BusPort::new(io_offset, 16, 8, io_bus)),
),
} }
} }
@ -168,7 +163,10 @@ where
self.debugger = Z80Debugger::default(); self.debugger = Z80Debugger::default();
} }
pub fn dump_state(&mut self, clock: Instant) { pub fn dump_state<Bus>(&mut self, clock: Instant, bus: &mut Bus)
where
Bus: BusAccess<Z80Address, Instant = Instant>,
{
println!("Status: {:?}", self.state.status); println!("Status: {:?}", self.state.status);
println!("PC: {:#06x}", self.state.pc); println!("PC: {:#06x}", self.state.pc);
println!("SP: {:#06x}", self.state.sp); println!("SP: {:#06x}", self.state.sp);
@ -207,12 +205,11 @@ where
println!("I: {:#04x} R: {:#04x}", self.state.i, self.state.r); println!("I: {:#04x} R: {:#04x}", self.state.i, self.state.r);
println!("IM: {:?} IFF1: {:?} IFF2: {:?}", self.state.im, self.state.iff1, self.state.iff2); println!("IM: {:?} IFF1: {:?} IFF2: {:?}", self.state.im, self.state.iff1, self.state.iff2);
// TODO disabled until function is reimplemented println!(
//println!( "Current Instruction: {} {:?}",
// "Current Instruction: {} {:?}", self.previous_cycle.decoder.format_instruction_bytes(bus),
// self.decoder.format_instruction_bytes(&mut self.port), self.previous_cycle.decoder.instruction
// self.decoder.instruction );
//);
println!("Previous Instruction: {:?}", self.previous_cycle.decoder.instruction); println!("Previous Instruction: {:?}", self.previous_cycle.decoder.instruction);
println!(); println!();
// TODO disabled until function is reimplemented // TODO disabled until function is reimplemented

View File

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

View File

@ -2,8 +2,8 @@ use clap::{Command, Arg, ArgAction, ArgMatches};
use std::io::{self, Write}; use std::io::{self, Write};
use femtos::Duration; use femtos::Duration;
use moa_core::{Error, System}; use moa_system::{Error, System};
use moa_debugger::{Debugger, DebugControl}; //use moa_debugger::{Debugger, DebugControl};
use moa_host::{Host, HostError, Tty, ControllerEvent, Audio, DummyAudio, FrameReceiver, EventSender}; use moa_host::{Host, HostError, Tty, ControllerEvent, Audio, DummyAudio, FrameReceiver, EventSender};
pub struct ConsoleFrontend; pub struct ConsoleFrontend;
@ -75,9 +75,10 @@ impl ConsoleFrontend {
.unwrap(); .unwrap();
// Run the main loop // Run the main loop
let mut debugger = Debugger::default(); //let mut debugger = Debugger::default();
let mut run_debugger = matches.get_flag("debugger"); let mut run_debugger = matches.get_flag("debugger");
loop { loop {
/*
if run_debugger { if run_debugger {
run_debugger = false; run_debugger = false;
@ -99,6 +100,7 @@ impl ConsoleFrontend {
} }
} }
} }
*/
match system.run_for_duration(Duration::MAX - system.clock.as_duration()) { match system.run_for_duration(Duration::MAX - system.clock.as_duration()) {
Ok(()) => {}, Ok(()) => {},

View File

@ -8,7 +8,7 @@ fn main() {
Arg::new("ROM") Arg::new("ROM")
.short('r') .short('r')
.long("rom") .long("rom")
.action(ArgAction::SetTrue) .action(ArgAction::Set)
.value_name("FILE") .value_name("FILE")
.help("ROM file to load at the start of memory"), .help("ROM file to load at the start of memory"),
) )

@ -1 +1 @@
Subproject commit 2391a324376bdd9fa1ae9801bbe3d12f2e69fa62 Subproject commit c17e364ed472fff774b1b38323a2c72936c7224c

View File

@ -19,7 +19,7 @@ type Input<T> = Signal<T>;
#[allow(dead_code)] #[allow(dead_code)]
type TriState<T> = Signal<T>; type TriState<T> = Signal<T>;
#[derive(Clone, Debug)] #[derive(Clone, Debug, Default)]
pub struct Signal<T: Copy>(Rc<Cell<T>>); pub struct Signal<T: Copy>(Rc<Cell<T>>);
impl<T: Copy> Signal<T> { impl<T: Copy> Signal<T> {

View File

@ -0,0 +1,15 @@
[package]
name = "moa-system"
version = "0.1.0"
edition = "2021"
[dependencies]
log = "0.4"
femtos = "0.1"
thiserror = "1.0"
moa-host = { path = "../host" }
emulator-hal = { path = "../emulator-hal/emulator-hal", features = ["femtos"] }
[features]
default = ["std"]
std = []

View File

@ -0,0 +1,374 @@
use std::fmt;
use std::rc::Rc;
use std::cell::{RefCell, RefMut, BorrowMutError};
use std::sync::atomic::{AtomicUsize, Ordering};
use femtos::{Duration, Instant};
use crate::{Error, System};
/// A universal memory address used by the Addressable trait
pub type Address = u64;
/*
/// A device that can change state over time. The `step()` method will be called
/// by the containing `System` when the system clock advances. If an error occurs
/// with any device, the `on_error()` method will be called to display any state
/// information that might be helpful for debugging.
pub trait Steppable {
fn step(&mut self, system: &System) -> Result<Duration, Error>;
fn on_error(&mut self, _system: &System) {}
}
/// A device that can receive an interrupt. The `interrupt_state_change()` method
/// will be called whenever an interrupt signal changes goes high or low.
pub trait Interruptable {
//fn interrupt_state_change(&mut self, state: bool, priority: u8, number: u8) -> Result<(), Error>;
}
/// A device that can be addressed to read data from or write data to the device.
pub trait Addressable {
fn size(&self) -> usize;
fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error>;
fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error>;
#[inline]
fn read_u8(&mut self, clock: Instant, addr: Address) -> Result<u8, Error> {
let mut data = [0; 1];
self.read(clock, addr, &mut data)?;
Ok(data[0])
}
#[inline]
fn read_beu16(&mut self, clock: Instant, addr: Address) -> Result<u16, Error> {
let mut data = [0; 2];
self.read(clock, addr, &mut data)?;
Ok(read_beu16(&data))
}
#[inline]
fn read_leu16(&mut self, clock: Instant, addr: Address) -> Result<u16, Error> {
let mut data = [0; 2];
self.read(clock, addr, &mut data)?;
Ok(read_leu16(&data))
}
#[inline]
fn read_beu32(&mut self, clock: Instant, addr: Address) -> Result<u32, Error> {
let mut data = [0; 4];
self.read(clock, addr, &mut data)?;
Ok(read_beu32(&data))
}
#[inline]
fn read_leu32(&mut self, clock: Instant, addr: Address) -> Result<u32, Error> {
let mut data = [0; 4];
self.read(clock, addr, &mut data)?;
Ok(read_leu32(&data))
}
#[inline]
fn write_u8(&mut self, clock: Instant, addr: Address, value: u8) -> Result<(), Error> {
let data = [value];
self.write(clock, addr, &data)
}
#[inline]
fn write_beu16(&mut self, clock: Instant, addr: Address, value: u16) -> Result<(), Error> {
let mut data = [0; 2];
write_beu16(&mut data, value);
self.write(clock, addr, &data)
}
#[inline]
fn write_leu16(&mut self, clock: Instant, addr: Address, value: u16) -> Result<(), Error> {
let mut data = [0; 2];
write_leu16(&mut data, value);
self.write(clock, addr, &data)
}
#[inline]
fn write_beu32(&mut self, clock: Instant, addr: Address, value: u32) -> Result<(), Error> {
let mut data = [0; 4];
write_beu32(&mut data, value);
self.write(clock, addr, &data)
}
#[inline]
fn write_leu32(&mut self, clock: Instant, addr: Address, value: u32) -> Result<(), Error> {
let mut data = [0; 4];
write_leu32(&mut data, value);
self.write(clock, addr, &data)
}
}
impl<T> Addressable for &mut T
where
T: Addressable + ?Sized,
{
#[inline]
fn size(&self) -> usize {
T::size(self)
}
#[inline]
fn read(
&mut self,
now: Instant,
addr: Address,
data: &mut [u8],
) -> Result<(), Error> {
T::read(self, now, addr, data)
}
#[inline]
fn write(&mut self, now: Instant, addr: Address, data: &[u8]) -> Result<(), Error> {
T::write(self, now, addr, data)
}
}
#[inline]
pub fn read_beu16(data: &[u8]) -> u16 {
(data[0] as u16) << 8 | (data[1] as u16)
}
#[inline]
pub fn read_leu16(data: &[u8]) -> u16 {
(data[1] as u16) << 8 | (data[0] as u16)
}
#[inline]
pub fn read_beu32(data: &[u8]) -> u32 {
(data[0] as u32) << 24 | (data[1] as u32) << 16 | (data[2] as u32) << 8 | (data[3] as u32)
}
#[inline]
pub fn read_leu32(data: &[u8]) -> u32 {
(data[3] as u32) << 24 | (data[2] as u32) << 16 | (data[1] as u32) << 8 | (data[0] as u32)
}
#[inline]
pub fn write_beu16(data: &mut [u8], value: u16) -> &mut [u8] {
data[0] = (value >> 8) as u8;
data[1] = value as u8;
data
}
#[inline]
pub fn write_leu16(data: &mut [u8], value: u16) -> &mut [u8] {
data[0] = value as u8;
data[1] = (value >> 8) as u8;
data
}
#[inline]
pub fn write_beu32(data: &mut [u8], value: u32) -> &mut [u8] {
data[0] = (value >> 24) as u8;
data[1] = (value >> 16) as u8;
data[2] = (value >> 8) as u8;
data[3] = value as u8;
data
}
#[inline]
pub fn write_leu32(data: &mut [u8], value: u32) -> &mut [u8] {
data[0] = value as u8;
data[1] = (value >> 8) as u8;
data[2] = (value >> 16) as u8;
data[3] = (value >> 24) as u8;
data
}
/// A device (cpu) that can debugged using the built-in debugger
pub trait Debuggable {
fn add_breakpoint(&mut self, addr: Address);
fn remove_breakpoint(&mut self, addr: Address);
fn print_current_step(&mut self, system: &System) -> Result<(), Error>;
fn print_disassembly(&mut self, system: &System, addr: Address, count: usize);
fn run_command(&mut self, system: &System, args: &[&str]) -> Result<bool, Error>;
}
/// A device (peripheral) that can inspected using the built-in debugger
pub trait Inspectable {
fn inspect(&mut self, system: &System, args: &[&str]) -> Result<(), Error>;
}
pub trait Transmutable {
#[inline]
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
None
}
#[inline]
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
None
}
#[inline]
fn as_interruptable(&mut self) -> Option<&mut dyn Interruptable> {
None
}
#[inline]
fn as_debuggable(&mut self) -> Option<&mut dyn Debuggable> {
None
}
#[inline]
fn as_inspectable(&mut self) -> Option<&mut dyn Inspectable> {
None
}
}
pub type TransmutableBox = Rc<RefCell<Box<dyn Transmutable>>>;
pub fn wrap_transmutable<T: Transmutable + 'static>(value: T) -> TransmutableBox {
Rc::new(RefCell::new(Box::new(value)))
}
*/
use emulator_hal::{BusAccess, Step, Inspect, Debug};
pub type MoaBus = dyn BusAccess<u64, Instant = Instant, Error = Error>;
pub type MoaStep = dyn Step<MoaBus, Instant = Instant, Error = Error>;
//pub type MoaInspect<'a> = dyn Inspect<u64, &'a mut MoaBus, String>;
//pub type MoaDebug<'a> = dyn Debug<u64, &'a mut MoaBus, String>;
pub trait DeviceInterface {
#[inline]
fn as_bus_access(&mut self) -> Option<&mut MoaBus> {
None
}
#[inline]
fn as_step(&mut self) -> Option<&mut MoaStep> {
None
}
/*
#[inline]
fn as_inspect(&mut self) -> Option<&mut MoaInspect> {
None
}
#[inline]
fn as_debug(&mut self) -> Option<&mut MoaDebug> {
None
}
*/
}
static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct DeviceId(usize);
impl DeviceId {
pub fn new() -> Self {
let next = NEXT_ID.load(Ordering::Acquire);
NEXT_ID.store(next + 1, Ordering::Release);
Self(next)
}
}
impl Default for DeviceId {
fn default() -> Self {
Self::new()
}
}
pub type BoxedInterface = Rc<RefCell<Box<dyn DeviceInterface>>>;
#[derive(Clone)]
pub struct Device(DeviceId, BoxedInterface);
impl Device {
pub fn new<T>(value: T) -> Self
where
T: DeviceInterface + 'static,
{
Self(DeviceId::new(), Rc::new(RefCell::new(Box::new(value))))
}
pub fn id(&self) -> DeviceId {
self.0
}
pub fn borrow_mut(&self) -> RefMut<'_, Box<dyn DeviceInterface>> {
self.1.borrow_mut()
}
pub fn try_borrow_mut(&self) -> Result<RefMut<'_, Box<dyn DeviceInterface>>, BorrowMutError> {
self.1.try_borrow_mut()
}
}
impl fmt::Debug for Device {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{:?}", self.0)
}
}
/*
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct DeviceId(usize);
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Interrupt {
Number(usize),
}
pub enum InterruptPriority {
NonMaskable,
Number(usize),
}
struct InterruptPort {
id: usize,
controller: TransmutableBox,
}
impl InterruptPort {
fn check_pending(&self) -> Option<Interrupt> {
self.controller.borrow_mut().as_interrupt_controller().check_pending(self.id)
}
fn acknowledge(&self, interrupt: Interrupt) -> Result<(), Error> {
self.controller.borrow_mut().as_interrupt_controller().acknowledge(self.id, interrupt)
}
}
//pub trait InterruptPort {
// fn check_pending(&mut self, id: DeviceId) -> Option<Interrupt>;
// fn acknowledge(&mut self, id: DeviceId, interrupt: Interrupt) -> Result<(), Error>;
//}
//pub trait Interrupter {
// fn trigger(&mut self, id: DeviceId, interrupt: Interrupt) -> Result<(), Error>;
//}
struct Interrupter {
input_id: usize,
interrupt: Interrupt,
controller: Rc<RefCell<TransmutableBox>>,
}
pub trait InterruptController {
fn connect(&mut self, priority: InterruptPriority) -> Result<InterruptPort, Error>;
fn check_pending(&mut self, id: usize) -> Option<Interrupt>;
fn acknowledge(&mut self, id: usize, interrupt: Interrupt) -> Result<(), Error>;
}
*/

View File

@ -0,0 +1,101 @@
use core::fmt;
use core::convert::Infallible;
use moa_host::HostError;
use emulator_hal::Error as EmuError;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EmulatorErrorKind {
Misc,
MemoryAlignment,
}
#[derive(Clone, Debug)]
pub enum Error {
Assertion(String),
Breakpoint(String),
Emulator(EmulatorErrorKind, String),
Processor(u32),
Other(String),
}
impl Default for Error {
fn default() -> Self {
Self::Other("default".to_string())
}
}
impl From<Infallible> for Error {
fn from(err: Infallible) -> Self {
Error::new("infallible".to_string())
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
/*
impl<E> From<E> for Error
where
E: EmuError,
{
fn from(err: E) -> Self {
Error::new(format!("{}", err))
}
}
*/
impl Error {
pub fn new<S>(msg: S) -> Error
where
S: Into<String>,
{
Error::Emulator(EmulatorErrorKind::Misc, msg.into())
}
pub fn emulator<S>(kind: EmulatorErrorKind, msg: S) -> Error
where
S: Into<String>,
{
Error::Emulator(kind, msg.into())
}
pub fn processor(native: u32) -> Error {
Error::Processor(native)
}
pub fn breakpoint<S>(msg: S) -> Error
where
S: Into<String>,
{
Error::Breakpoint(msg.into())
}
pub fn assertion<S>(msg: S) -> Error
where
S: Into<String>,
{
Error::Assertion(msg.into())
}
pub fn msg(&self) -> &str {
match self {
Error::Assertion(msg) | Error::Breakpoint(msg) | Error::Other(msg) | Error::Emulator(_, msg) => msg.as_str(),
Error::Processor(_) => "native exception",
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Assertion(msg) | Error::Breakpoint(msg) | Error::Other(msg) | Error::Emulator(_, msg) => write!(f, "{}", msg),
Error::Processor(_) => write!(f, "native exception"),
}
}
}
impl<E> From<HostError<E>> for Error {
fn from(_err: HostError<E>) -> Self {
Self::Other("other".to_string())
}
}

View File

@ -0,0 +1,44 @@
use crate::error::Error;
pub struct InterruptController {
interrupts: Vec<(bool, u8)>,
highest: u8,
}
impl Default for InterruptController {
fn default() -> InterruptController {
InterruptController {
interrupts: vec![(false, 0); 7],
highest: 0,
}
}
}
impl InterruptController {
pub fn set(&mut self, state: bool, priority: u8, number: u8) -> Result<(), Error> {
self.interrupts[priority as usize].0 = state;
self.interrupts[priority as usize].1 = number;
if state && priority > self.highest {
self.highest = priority;
}
Ok(())
}
pub fn check(&mut self) -> (bool, u8, u8) {
if self.highest > 0 {
(true, self.highest, self.interrupts[self.highest as usize].1)
} else {
(false, 0, 0)
}
}
pub fn acknowledge(&mut self, priority: u8) -> Result<u8, Error> {
let acknowledge = self.interrupts[priority as usize].1;
self.interrupts[priority as usize].0 = false;
while self.highest > 0 && !self.interrupts[self.highest as usize].0 {
self.highest -= 1;
}
Ok(acknowledge)
}
}

View File

@ -0,0 +1,19 @@
#[macro_use]
mod error;
mod devices;
mod interrupts;
mod memory;
mod system;
pub use crate::devices::{
Address, Device,
DeviceInterface, MoaBus, MoaStep,
};
pub use crate::error::Error;
pub use crate::interrupts::InterruptController;
pub use crate::memory::{MemoryBlock, Bus, dump_slice, dump_memory};
pub use crate::system::System;
pub use emulator_hal;
pub use emulator_hal::BusAdapter;

View File

@ -0,0 +1,461 @@
use std::fs;
use std::cmp;
use std::rc::Rc;
use std::cell::RefCell;
use std::fmt::Write;
use femtos::Instant;
use emulator_hal::{BusAccess, Error as BusError};
use crate::error::Error;
use crate::devices::{Address, Device, DeviceInterface, MoaBus};
impl BusError for Error {}
/// A contiguous block of `Addressable` memory, backed by a `Vec`
pub struct MemoryBlock {
read_only: bool,
contents: Vec<u8>,
}
impl MemoryBlock {
pub fn new(contents: Vec<u8>) -> MemoryBlock {
MemoryBlock {
read_only: false,
contents,
}
}
pub fn load(filename: &str) -> Result<MemoryBlock, Error> {
match fs::read(filename) {
Ok(contents) => Ok(MemoryBlock::new(contents)),
Err(_) => Err(Error::new(format!("Error reading contents of {}", filename))),
}
}
pub fn load_at(&mut self, addr: Address, filename: &str) -> Result<(), Error> {
match fs::read(filename) {
Ok(contents) => {
self.contents[(addr as usize)..(addr as usize) + contents.len()].copy_from_slice(&contents);
Ok(())
},
Err(_) => Err(Error::new(format!("Error reading contents of {}", filename))),
}
}
pub fn size(&self) -> usize {
self.contents.len()
}
pub fn read_only(&mut self) {
self.read_only = true;
}
pub fn resize(&mut self, new_size: usize) {
self.contents.resize(new_size, 0);
}
}
impl BusAccess<Address> for MemoryBlock {
type Instant = Instant;
// TODO this is temporary
type Error = Error;
#[inline]
fn read(&mut self, _clock: Self::Instant, addr: Address, data: &mut [u8]) -> Result<usize, Self::Error> {
data.copy_from_slice(&self.contents[(addr as usize)..(addr as usize) + data.len()]);
Ok(data.len())
}
#[inline]
fn write(&mut self, _clock: Self::Instant, addr: Address, data: &[u8]) -> Result<usize, Self::Error> {
if self.read_only {
return Err(Error::breakpoint(format!(
"Attempt to write to read-only memory at {:x} with data {:?}",
addr, data
)));
}
self.contents[(addr as usize)..(addr as usize) + data.len()].copy_from_slice(data);
Ok(data.len())
}
}
impl DeviceInterface for MemoryBlock {
fn as_bus_access(&mut self) -> Option<&mut MoaBus> {
Some(self)
}
}
/*
/// An address adapter that repeats the address space of the subdevice over the given range
pub struct AddressRepeater {
subdevice: Device,
range: Address,
}
impl AddressRepeater {
pub fn new(subdevice: Device, range: Address) -> Self {
Self {
subdevice,
range,
}
}
}
impl Addressable for AddressRepeater {
fn size(&self) -> usize {
self.range as usize
}
fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
let size = self.subdevice.borrow_mut().as_addressable().unwrap().size() as Address;
self.subdevice
.borrow_mut()
.as_addressable()
.unwrap()
.read(clock, addr % size, data)
}
fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> {
let size = self.subdevice.borrow_mut().as_addressable().unwrap().size() as Address;
self.subdevice
.borrow_mut()
.as_addressable()
.unwrap()
.write(clock, addr % size, data)
}
}
impl Transmutable for AddressRepeater {
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
Some(self)
}
}
/// An address adapter that uses a closure to translate the address before accessing the subdevice
pub struct AddressTranslator {
subdevice: Device,
size: usize,
func: Box<dyn Fn(Address) -> Address>,
}
impl AddressTranslator {
pub fn new<F>(subdevice: Device, size: usize, func: F) -> Self
where
F: Fn(Address) -> Address + 'static,
{
Self {
subdevice,
size,
func: Box::new(func),
}
}
}
impl Addressable for AddressTranslator {
fn size(&self) -> usize {
self.size
}
fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
self.subdevice
.borrow_mut()
.as_addressable()
.unwrap()
.read(clock, (self.func)(addr), data)
}
fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> {
self.subdevice
.borrow_mut()
.as_addressable()
.unwrap()
.write(clock, (self.func)(addr), data)
}
}
impl Transmutable for AddressTranslator {
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
Some(self)
}
}
*/
#[derive(Clone)]
pub struct Block {
pub base: Address,
pub size: usize,
pub dev: Device,
}
/// A bus-like collection of `Addressable` `Device`s mapped to different address ranges
///
/// This is the fundamental means of connecting devices together to a CPU implementation.
#[derive(Clone, Default)]
pub struct Bus {
blocks: Vec<Block>,
ignore_unmapped: bool,
watchers: Vec<Address>,
watcher_modified: bool,
}
impl Bus {
pub fn set_ignore_unmapped(&mut self, ignore_unmapped: bool) {
self.ignore_unmapped = ignore_unmapped;
}
pub fn clear_all_bus_devices(&mut self) {
self.blocks.clear();
}
pub fn size(&self) -> usize {
let block = &self.blocks[self.blocks.len() - 1];
(block.base as usize) + block.size
}
pub fn insert(&mut self, base: Address, dev: Device, size: usize) {
let block = Block {
base,
size,
dev,
};
let i = self
.blocks
.iter()
.position(|cur| cur.base > block.base)
.unwrap_or(self.blocks.len());
self.blocks.insert(i, block);
}
pub fn get_device_at(&self, addr: Address, count: usize) -> Result<(Device, Address), Error> {
for block in &self.blocks {
if addr >= block.base && addr < (block.base + block.size as Address) {
let relative_addr = addr - block.base;
if relative_addr as usize + count <= block.size {
return Ok((block.dev.clone(), relative_addr));
} else {
return Err(Error::new(format!("Error reading address {:#010x}", addr)));
}
}
}
Err(Error::new(format!("No segment found at {:#010x}", addr)))
}
pub fn dump_memory(&mut self, clock: Instant, mut addr: Address, mut count: Address) {
while count > 0 {
let mut line = format!("{:#010x}: ", addr);
let to = if count < 16 { count / 2 } else { 8 };
for _ in 0..to {
let word = self.read_beu16(clock, addr);
if word.is_err() {
println!("{}", line);
return;
}
write!(line, "{:#06x} ", word.unwrap()).unwrap();
addr += 2;
count -= 2;
}
println!("{}", line);
}
}
pub fn add_watcher(&mut self, addr: Address) {
self.watchers.push(addr);
}
pub fn remove_watcher(&mut self, addr: Address) {
self.watchers.push(addr);
if let Some(index) = self.watchers.iter().position(|a| *a == addr) {
self.watchers.remove(index);
}
}
pub fn check_and_reset_watcher_modified(&mut self) -> bool {
let result = self.watcher_modified;
self.watcher_modified = false;
result
}
}
impl BusAccess<Address> for Bus {
type Instant = Instant;
type Error = Error;
#[inline]
fn read(&mut self, clock: Self::Instant, addr: Address, data: &mut [u8]) -> Result<usize, Self::Error> {
let (dev, relative_addr) = match self.get_device_at(addr, data.len()) {
Ok(result) => result,
Err(err) if self.ignore_unmapped => {
log::info!("{:?}", err);
return Ok(0);
},
Err(err) => return Err(err),
};
let result = dev.borrow_mut().as_bus_access().unwrap().read(clock, relative_addr, data);
result
}
#[inline]
fn write(&mut self, clock: Self::Instant, addr: Address, data: &[u8]) -> Result<usize, Self::Error> {
if self.watchers.iter().any(|a| *a == addr) {
println!("watch: writing to address {:#06x} with {:?}", addr, data);
self.watcher_modified = true;
}
let (dev, relative_addr) = match self.get_device_at(addr, data.len()) {
Ok(result) => result,
Err(err) if self.ignore_unmapped => {
log::info!("{:?}", err);
return Ok(0);
},
Err(err) => return Err(err),
};
let result = dev.borrow_mut().as_bus_access().unwrap().write(clock, relative_addr, data);
result
}
}
/*
/// An adapter for limiting the access requests of a device (eg. CPU) on a `Bus` to the address
/// and data widths of the device
#[derive(Clone)]
pub struct BusPort {
offset: Address,
address_mask: Address,
data_width: u8,
subdevice: Rc<RefCell<Bus>>,
}
impl BusPort {
pub fn new(offset: Address, address_bits: u8, data_bits: u8, bus: Rc<RefCell<Bus>>) -> Self {
Self {
offset,
address_mask: (1 << address_bits) - 1,
data_width: data_bits / 8,
subdevice: bus,
}
}
pub fn dump_memory(&mut self, clock: Instant, addr: Address, count: Address) {
self.subdevice
.borrow_mut()
.dump_memory(clock, self.offset + (addr & self.address_mask), count)
}
#[inline]
pub fn address_mask(&self) -> Address {
self.address_mask
}
#[inline]
pub fn data_width(&self) -> u8 {
self.data_width
}
}
impl Addressable for BusPort {
fn size(&self) -> usize {
self.subdevice.borrow().size()
}
fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
let addr = self.offset + (addr & self.address_mask);
let mut subdevice = self.subdevice.borrow_mut();
for i in (0..data.len()).step_by(self.data_width as usize) {
let addr_index = (addr + i as Address) & self.address_mask;
let end = cmp::min(i + self.data_width as usize, data.len());
Addressable::read(&mut *subdevice, clock, addr_index, &mut data[i..end])?;
}
Ok(())
}
fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> {
let addr = self.offset + (addr & self.address_mask);
let mut subdevice = self.subdevice.borrow_mut();
for i in (0..data.len()).step_by(self.data_width as usize) {
let addr_index = (addr + i as Address) & self.address_mask;
let end = cmp::min(i + self.data_width as usize, data.len());
Addressable::write(&mut *subdevice, clock, addr_index, &data[i..end])?;
}
Ok(())
}
}
*/
pub fn dump_slice(data: &[u8], mut count: usize) {
let mut addr = 0;
while count > 0 {
let mut line = format!("{:#010x}: ", addr);
let to = if count < 16 { count / 2 } else { 8 };
for _ in 0..to {
let word = u16::from_be_bytes(data[addr..addr + 2].try_into().unwrap());
write!(line, "{:#06x} ", word).unwrap();
addr += 2;
count -= 2;
}
println!("{}", line);
}
}
pub fn dump_memory<Bus, Address, Instant>(bus: &mut Bus, clock: Instant, addr: Address, count: Address)
where
Bus: BusAccess<Address, Instant = 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);
}
}
/*
impl BusError for Error {}
impl BusAccess<u64> for &mut dyn Addressable {
type Instant = Instant;
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())
}
}
impl BusAccess<u64> for Bus {
type Instant = Instant;
type Error = Error;
fn read(&mut self, now: Instant, addr: Address, data: &mut [u8]) -> Result<usize, Self::Error> {
Addressable::read(self, now, addr, data)?;
Ok(data.len())
}
fn write(&mut self, now: Instant, addr: Address, data: &[u8]) -> Result<usize, Self::Error> {
Addressable::write(self, now, addr, data)?;
Ok(data.len())
}
}
*/

View File

@ -0,0 +1,240 @@
use std::rc::Rc;
use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
use femtos::{Instant, Duration};
use crate::{Bus, Error, InterruptController, Address, Device};
pub struct System {
pub clock: Instant,
pub devices: HashMap<String, Device>,
pub event_queue: Vec<NextStep>,
pub debuggables: Vec<Device>,
pub bus: Rc<RefCell<Bus>>,
pub interrupt_controller: RefCell<InterruptController>,
}
impl Default for System {
fn default() -> Self {
Self {
clock: Instant::START,
devices: HashMap::new(),
event_queue: vec![],
debuggables: Vec::new(),
bus: Rc::new(RefCell::new(Bus::default())),
interrupt_controller: RefCell::new(InterruptController::default()),
}
}
}
impl System {
pub fn get_bus(&self) -> RefMut<'_, Bus> {
self.bus.borrow_mut()
}
pub fn get_interrupt_controller(&self) -> RefMut<'_, InterruptController> {
self.interrupt_controller.borrow_mut()
}
pub fn get_device(&self, name: &str) -> Result<Device, Error> {
self.devices
.get(name)
.cloned()
.ok_or_else(|| Error::new(format!("system: no device named {}", name)))
}
pub fn add_device(&mut self, name: &str, device: Device) -> Result<(), Error> {
log::debug!("adding device {:?}", device);
//self.try_add_debuggable(device.clone());
self.try_queue_device(device.clone());
self.devices.insert(name.to_string(), device);
Ok(())
}
pub fn add_addressable_device(&mut self, addr: Address, len: usize, device: Device) -> Result<(), Error> {
self.add_peripheral(&format!("mem{:x}", addr), addr, len, device)
}
pub fn add_peripheral(&mut self, name: &str, addr: Address, len: usize, device: Device) -> Result<(), Error> {
self.bus.borrow_mut().insert(addr, device.clone(), len);
//self.try_add_debuggable(device.clone());
self.add_device(name, device)?;
Ok(())
}
pub fn add_interruptable_device(&mut self, name: &str, device: Device) -> Result<(), Error> {
//self.try_add_debuggable(device.clone());
self.add_device(name, device)?;
Ok(())
}
fn process_one_event(&mut self) -> Result<(), Error> {
let mut event_device = self.event_queue.pop().unwrap();
self.clock = event_device.next_clock;
//println!("{:?}", event_device.device);
let result = match event_device.device.borrow_mut().as_step().unwrap().step(self.clock, &mut *self.bus.borrow_mut()) {
Ok(next) => {
event_device.next_clock = next;
Ok(())
},
Err(err) => Err(err),
};
self.queue_device(event_device);
result
}
/// Step the simulation one event exactly
pub fn step(&mut self) -> Result<(), Error> {
match self.process_one_event() {
Ok(()) => {},
Err(err @ Error::Breakpoint(_)) => {
return Err(err);
},
Err(err) => {
self.exit_error();
log::error!("{:?}", err);
return Err(err);
},
}
Ok(())
}
/// Step through the simulation until the next event is for the given device
pub fn step_until_device(&mut self, device: Device) -> Result<(), Error> {
loop {
self.step()?;
if self.get_next_event_device().id() == device.id() {
break;
}
}
Ok(())
}
/*
/// Step through the simulation until the next event scheduled is for a debuggable device
pub fn step_until_debuggable(&mut self) -> Result<(), Error> {
loop {
self.step()?;
if self.get_next_event_device().borrow_mut().as_debuggable().is_some() {
break;
}
}
Ok(())
}
*/
/// Run the simulation until the given simulation clock time has been reached
pub fn run_until_clock(&mut self, clock: Instant) -> Result<(), Error> {
while self.clock < clock {
self.step()?;
}
Ok(())
}
/// Run the simulation for `elapsed` amount of simulation time
pub fn run_for_duration(&mut self, elapsed: Duration) -> Result<(), Error> {
let target = self.clock + elapsed;
while self.clock < target {
self.step()?;
}
Ok(())
}
/// Run the simulation forever, or until there is an error
pub fn run_forever(&mut self) -> Result<(), Error> {
self.run_until_clock(Instant::FOREVER)
}
pub fn exit_error(&mut self) {
for (_, dev) in self.devices.iter() {
if let Some(dev) = dev.borrow_mut().as_step() {
// TODO no on_error anymore
//dev.on_error(self);
}
}
}
pub fn get_next_event_device(&self) -> Device {
self.event_queue[self.event_queue.len() - 1].device.clone()
}
/*
pub fn get_next_debuggable_device(&self) -> Option<Device> {
for event in self.event_queue.iter().rev() {
if event.device.borrow_mut().as_debuggable().is_some() {
return Some(event.device.clone());
}
}
None
}
fn try_add_debuggable(&mut self, device: Device) {
if device.borrow_mut().as_debuggable().is_some() {
self.debuggables.push(device);
}
}
*/
fn try_queue_device(&mut self, device: Device) {
if device.borrow_mut().as_step().is_some() {
self.queue_device(NextStep::new(device));
}
}
fn queue_device(&mut self, device_step: NextStep) {
for (i, event) in self.event_queue.iter().enumerate().rev() {
if event.next_clock > device_step.next_clock {
self.event_queue.insert(i + 1, device_step);
return;
}
}
self.event_queue.insert(0, device_step);
}
}
pub struct NextStep {
pub next_clock: Instant,
pub device: Device,
}
impl NextStep {
pub fn new(device: Device) -> Self {
Self {
next_clock: Instant::START,
device,
}
}
}
/*
use emulator_hal::bus::{BusType, BusAccess};
impl BusType for System {
type Address = u64;
type Error = Error;
type Instant = Instant;
}
impl BusAccess for System {
fn read(&mut self, _now: Instant, addr: u64, data: &mut [u8]) -> Result<usize, Self::Error> {
let addr = addr as usize;
data.copy_from_slice(&self.0[addr..addr + data.len()]);
Ok(data.len())
}
fn write(&mut self, _now: Instant, addr: u64, data: &[u8]) -> Result<usize, Self::Error> {
let addr = addr as usize;
self.0[addr..addr + data.len()].copy_from_slice(data);
Ok(data.len())
}
}
*/

View File

@ -6,4 +6,6 @@ edition = "2021"
[dependencies] [dependencies]
log = "0.4" log = "0.4"
femtos = "0.1" femtos = "0.1"
emulator-hal = { path = "../../libraries/emulator-hal/emulator-hal", features = ["femtos"] }
moa-system = { path = "../../libraries/system" }
moa-core = { path = "../../core" } moa-core = { path = "../../core" }

View File

@ -1,30 +1,36 @@
use std::fs; use std::fs;
use std::io;
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};
use core::convert::Infallible;
use femtos::Instant; use femtos::Instant;
use emulator_hal::{BusAccess, BusAdapter, Step, FromAddress, IntoAddress, Error as EmuError};
use moa_core::{Error, Address, Addressable, Transmutable}; //use moa_core::{Error, Bus, MoaBus, Address, Addressable, Transmutable, DeviceInterface};
use moa_system::{Error as MoaError, MoaBus, DeviceInterface};
#[rustfmt::skip] #[rustfmt::skip]
mod reg { mod reg {
use super::Address; use super::DeviceAddress;
pub(super) const DATA_WORD: Address = 0x20; pub(super) const DATA_WORD: u8 = 0x20;
pub(super) const DATA_BYTE: Address = 0x21; pub(super) const DATA_BYTE: u8 = 0x21;
pub(super) const FEATURE: Address = 0x23; pub(super) const FEATURE: u8 = 0x23;
pub(super) const ERROR: Address = 0x23; pub(super) const ERROR: u8 = 0x23;
pub(super) const SECTOR_COUNT: Address = 0x25; pub(super) const SECTOR_COUNT: u8 = 0x25;
pub(super) const SECTOR_NUM: Address = 0x27; pub(super) const SECTOR_NUM: u8 = 0x27;
pub(super) const CYL_LOW: Address = 0x29; pub(super) const CYL_LOW: u8 = 0x29;
pub(super) const CYL_HIGH: Address = 0x2B; pub(super) const CYL_HIGH: u8 = 0x2B;
pub(super) const DRIVE_HEAD: Address = 0x2D; pub(super) const DRIVE_HEAD: u8 = 0x2D;
pub(super) const STATUS: Address = 0x2F; pub(super) const STATUS: u8 = 0x2F;
pub(super) const COMMAND: Address = 0x2F; pub(super) const COMMAND: u8 = 0x2F;
} }
#[rustfmt::skip] #[rustfmt::skip]
mod cmd { mod cmd {
pub(super) const READ_SECTORS: u8 = 0x20; pub(super) const READ_SECTORS: u8 = 0x20;
pub(super) const WRITE_SECTORS: u8 = 0x30; pub(super) const WRITE_SECTORS: u8 = 0x30;
pub(super) const IDENTIFY: u8 = 0xEC; pub(super) const IDENTIFY: u8 = 0xEC;
pub(super) const SET_FEATURE: u8 = 0xEF; pub(super) const SET_FEATURE: u8 = 0xEF;
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -38,32 +44,47 @@ const ATA_SECTOR_SIZE: u32 = 512;
const DEV_NAME: &str = "ata"; const DEV_NAME: &str = "ata";
pub struct DeviceAddress(u8);
#[derive(Default)] #[derive(Default)]
pub struct AtaDevice { pub struct AtaDevice<Error>
where
Error: Default,
{
selected_sector: u32, selected_sector: u32,
selected_count: u32, selected_count: u32,
last_error: u8, last_error: u8,
contents: Vec<u8>, contents: Vec<u8>,
error: PhantomData<Error>,
} }
impl AtaDevice { impl<Error> AtaDevice<Error>
pub fn load(&mut self, filename: &str) -> Result<(), Error> { where
match fs::read(filename) { Error: Default,
Ok(contents) => { {
self.contents = contents; pub fn load(&mut self, filename: &str) -> Result<(), io::Error> {
Ok(()) let contents = fs::read(filename)?;
}, self.contents = contents;
Err(_) => Err(Error::new(format!("Error reading contents of {}", filename))), Ok(())
}
} }
}
impl Addressable for AtaDevice { pub fn address_space(&self) -> usize {
fn size(&self) -> usize {
0x30 0x30
} }
}
impl<Address, Error> BusAccess<Address> for AtaDevice<Error>
where
Error: EmuError + Default,
Address: IntoAddress<DeviceAddress> + Copy,
{
type Instant = Instant;
type Error = Error;
#[inline]
fn read(&mut self, _clock: Self::Instant, addr: Address, data: &mut [u8]) -> Result<usize, Self::Error> {
let addr = addr.into_address().0;
fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
match addr { match addr {
reg::DATA_WORD => { reg::DATA_WORD => {
self.selected_count -= 2; self.selected_count -= 2;
@ -95,10 +116,13 @@ impl Addressable for AtaDevice {
}, },
} }
Ok(()) Ok(1)
} }
fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { #[inline]
fn write(&mut self, _clock: Self::Instant, addr: Address, data: &[u8]) -> Result<usize, Self::Error> {
let addr = addr.into_address().0;
log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
match addr { match addr {
reg::DRIVE_HEAD => { reg::DRIVE_HEAD => {
@ -139,12 +163,76 @@ impl Addressable for AtaDevice {
log::debug!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr); log::debug!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr);
}, },
} }
Ok(1)
}
}
impl FromAddress<u64> for DeviceAddress {
fn from_address(address: u64) -> Self {
Self(address as u8)
}
}
impl DeviceInterface for AtaDevice<MoaError> {
fn as_bus_access(&mut self) -> Option<&mut MoaBus> {
Some(self)
}
}
/*
pub struct MoaAtaDevice(BusAdapter<u64, u8, AtaDevice, Error>);
impl Default for MoaAtaDevice {
fn default() -> Self {
MoaAtaDevice(BusAdapter::new(AtaDevice::default(), |addr| addr as u8, |err| Error::new(format!("{:?}", err))))
}
}
impl DeviceInterface for MoaAtaDevice {
fn as_bus_access(&mut self) -> Option<&mut MoaBus> {
Some(&mut self.0)
}
}
impl Deref for MoaAtaDevice {
type Target = AtaDevice;
fn deref(&self) -> &Self::Target {
&self.0.bus
}
}
impl DerefMut for MoaAtaDevice {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0.bus
}
}
*/
//// OLD INTERFACE
/*
impl Addressable for AtaDevice<u64> {
fn size(&self) -> usize {
self.address_space()
}
fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
<Self as BusAccess<u8>>::read(self, clock, addr as u8, data)
.map_err(|err| Error::new(format!("{:?}", err)))?;
Ok(())
}
fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> {
<Self as BusAccess<u8>>::write(self, clock, addr as u8, data)
.map_err(|err| Error::new(format!("{:?}", err)))?;
Ok(()) Ok(())
} }
} }
impl Transmutable for AtaDevice { impl Transmutable for AtaDevice<u64> {
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> { fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
Some(self) Some(self)
} }
} }
*/

View File

@ -6,5 +6,8 @@ edition = "2021"
[dependencies] [dependencies]
log = "0.4" log = "0.4"
femtos = "0.1" femtos = "0.1"
emulator-hal = { path = "../../libraries/emulator-hal/emulator-hal", features = ["femtos"] }
moa-system = { path = "../../libraries/system" }
moa-core = { path = "../../core" } moa-core = { path = "../../core" }
moa-host = { path = "../../libraries/host" } moa-host = { path = "../../libraries/host" }

View File

@ -1,2 +1,2 @@
mod mc68681; mod mc68681;
pub use crate::mc68681::MC68681; pub use crate::mc68681::{MC68681, MoaMC68681};

View File

@ -1,39 +1,46 @@
use core::marker::PhantomData;
use core::convert::Infallible;
use core::ops::{Deref, DerefMut};
use femtos::{Instant, Duration, Frequency}; use femtos::{Instant, Duration, Frequency};
use emulator_hal::{BusAccess, BusAdapter, Step, Instant as EmuInstant, Error as EmuError};
use moa_core::{System, Error, Address, Steppable, Addressable, Transmutable}; use moa_core::{System, Bus, Address, Steppable, Addressable, Transmutable};
use moa_host::Tty; use moa_host::Tty;
use moa_system::{DeviceInterface, Error, MoaBus, MoaStep};
const REG_MR1A_MR2A: Address = 0x01; type DeviceAddress = u64;
const REG_SRA_RD: Address = 0x03;
const REG_CSRA_WR: Address = 0x03;
const REG_CRA_WR: Address = 0x05;
const REG_TBA_WR: Address = 0x07;
const REG_RBA_RD: Address = 0x07;
const REG_MR1B_MR2B: Address = 0x11; const REG_MR1A_MR2A: DeviceAddress = 0x01;
const REG_SRB_RD: Address = 0x13; const REG_SRA_RD: DeviceAddress = 0x03;
const REG_CSRB_WR: Address = 0x13; const REG_CSRA_WR: DeviceAddress = 0x03;
const REG_CRB_WR: Address = 0x15; const REG_CRA_WR: DeviceAddress = 0x05;
const REG_TBB_WR: Address = 0x17; const REG_TBA_WR: DeviceAddress = 0x07;
const REG_RBB_RD: Address = 0x17; const REG_RBA_RD: DeviceAddress = 0x07;
const REG_ACR_WR: Address = 0x09; const REG_MR1B_MR2B: DeviceAddress = 0x11;
const REG_SRB_RD: DeviceAddress = 0x13;
const REG_CSRB_WR: DeviceAddress = 0x13;
const REG_CRB_WR: DeviceAddress = 0x15;
const REG_TBB_WR: DeviceAddress = 0x17;
const REG_RBB_RD: DeviceAddress = 0x17;
const REG_CTUR_WR: Address = 0x0D; const REG_ACR_WR: DeviceAddress = 0x09;
const REG_CTLR_WR: Address = 0x0F;
const REG_START_RD: Address = 0x1D;
const REG_STOP_RD: Address = 0x1F;
const REG_IPCR_RD: Address = 0x09; const REG_CTUR_WR: DeviceAddress = 0x0D;
const REG_OPCR_WR: Address = 0x1B; const REG_CTLR_WR: DeviceAddress = 0x0F;
const REG_INPUT_RD: Address = 0x1B; const REG_START_RD: DeviceAddress = 0x1D;
const REG_OUT_SET: Address = 0x1D; const REG_STOP_RD: DeviceAddress = 0x1F;
const REG_OUT_RESET: Address = 0x1F;
const REG_ISR_RD: Address = 0x0B; const REG_IPCR_RD: DeviceAddress = 0x09;
const REG_IMR_WR: Address = 0x0B; const REG_OPCR_WR: DeviceAddress = 0x1B;
const REG_IVR_WR: Address = 0x19; const REG_INPUT_RD: DeviceAddress = 0x1B;
const REG_OUT_SET: DeviceAddress = 0x1D;
const REG_OUT_RESET: DeviceAddress = 0x1F;
const REG_ISR_RD: DeviceAddress = 0x0B;
const REG_IMR_WR: DeviceAddress = 0x0B;
const REG_IVR_WR: DeviceAddress = 0x19;
// Status Register Bits (SRA/SRB) // Status Register Bits (SRA/SRB)
@ -80,11 +87,11 @@ pub struct MC68681Port {
} }
impl MC68681Port { impl MC68681Port {
pub fn connect(&mut self, pty: Box<dyn Tty>) -> Result<String, Error> { pub fn connect(&mut self, pty: Box<dyn Tty>) -> String {
let name = pty.device_name(); let name = pty.device_name();
println!("{}: opening pts {}", DEV_NAME, name); println!("{}: opening pts {}", DEV_NAME, name);
self.tty = Some(pty); self.tty = Some(pty);
Ok(name) name
} }
pub fn send_byte(&mut self, data: u8) { pub fn send_byte(&mut self, data: u8) {
@ -114,17 +121,17 @@ impl MC68681Port {
} }
} }
pub fn check_rx(&mut self) -> Result<bool, Error> { pub fn check_rx(&mut self) -> bool {
if self.rx_enabled && (self.status & SR_RX_READY) == 0 && self.tty.is_some() { if self.rx_enabled && (self.status & SR_RX_READY) == 0 && self.tty.is_some() {
let tty = self.tty.as_mut().unwrap(); let tty = self.tty.as_mut().unwrap();
let result = tty.read(); let result = tty.read();
if let Some(input) = result { if let Some(input) = result {
self.input = input; self.input = input;
self.set_rx_status(true); self.set_rx_status(true);
return Ok(true); return true;
} }
} }
Ok(false) false
} }
pub fn check_tx(&mut self) -> bool { pub fn check_tx(&mut self) -> bool {
@ -155,7 +162,7 @@ impl MC68681Port {
} }
} }
pub struct MC68681 { pub struct MC68681<Address, Instant, Error> {
frequency: Frequency, frequency: Frequency,
acr: u8, acr: u8,
@ -175,11 +182,15 @@ pub struct MC68681 {
input_state: u8, input_state: u8,
output_conf: u8, output_conf: u8,
output_state: u8, output_state: u8,
address: PhantomData<Address>,
instant: PhantomData<Instant>,
error: PhantomData<Error>,
} }
impl Default for MC68681 { impl<Address, Instant, Error> Default for MC68681<Address, Instant, Error> {
fn default() -> Self { fn default() -> Self {
MC68681 { Self {
frequency: Frequency::from_hz(3_686_400), frequency: Frequency::from_hz(3_686_400),
acr: 0, acr: 0,
@ -199,29 +210,51 @@ impl Default for MC68681 {
input_state: 0, input_state: 0,
output_conf: 0, output_conf: 0,
output_state: 0, output_state: 0,
address: PhantomData,
instant: PhantomData,
error: PhantomData,
} }
} }
} }
impl MC68681 { impl<Address, Instant, Error> MC68681<Address, Instant, Error> {
fn set_interrupt_flag(&mut self, flag: u8, value: bool) { pub fn address_space(&self) -> usize {
0x30
}
pub fn set_interrupt_flag(&mut self, flag: u8, value: bool) {
self.int_status = (self.int_status & !flag) | (if value { flag } else { 0 }); self.int_status = (self.int_status & !flag) | (if value { flag } else { 0 });
} }
fn check_interrupt_state(&mut self, system: &System) -> Result<(), Error> { pub fn get_interrupt_flag(&mut self) -> (bool, u8, u8) {
system ((self.int_status & self.int_mask) != 0, 4, self.int_vector)
.get_interrupt_controller()
.set((self.int_status & self.int_mask) != 0, 4, self.int_vector)
} }
} }
impl Steppable for MC68681 { impl<Address, Instant, Error, Bus> Step<Bus> for MC68681<Address, Instant, Error>
fn step(&mut self, system: &System) -> Result<Duration, Error> { where
if self.port_a.check_rx()? { Address: Into<DeviceAddress> + Copy,
Instant: EmuInstant,
Bus: BusAccess<Address, Instant = Instant> + ?Sized,
{
type Instant = Instant;
type Error = Error;
fn is_running(&mut self) -> bool {
true
}
fn reset(&mut self, _now: Self::Instant, _bus: &mut Bus) -> Result<(), Self::Error> {
Ok(())
}
fn step(&mut self, now: Self::Instant, bus: &mut Bus) -> Result<Self::Instant, Self::Error> {
if self.port_a.check_rx() {
self.set_interrupt_flag(ISR_CH_A_RX_READY_FULL, true); self.set_interrupt_flag(ISR_CH_A_RX_READY_FULL, true);
} }
if self.port_b.check_rx()? { if self.port_b.check_rx() {
self.set_interrupt_flag(ISR_CH_B_RX_READY_FULL, true); self.set_interrupt_flag(ISR_CH_B_RX_READY_FULL, true);
} }
@ -242,7 +275,8 @@ impl Steppable for MC68681 {
} }
} }
self.check_interrupt_state(system)?; // TODO this has been added to the Steppable impl, but isn't handled by Step
//self.check_interrupt_state(system)?;
if self.port_a.check_tx() { if self.port_a.check_tx() {
self.set_interrupt_flag(ISR_CH_A_TX_READY, true); self.set_interrupt_flag(ISR_CH_A_TX_READY, true);
@ -252,16 +286,23 @@ impl Steppable for MC68681 {
self.set_interrupt_flag(ISR_CH_B_TX_READY, true); self.set_interrupt_flag(ISR_CH_B_TX_READY, true);
} }
Ok(self.frequency.period_duration()) Ok(now + Instant::hertz_to_duration(self.frequency.as_hz() as u64))
} }
} }
impl Addressable for MC68681 { impl<Address, Instant, Error> BusAccess<Address> for MC68681<Address, Instant, Error>
fn size(&self) -> usize { where
0x30 Address: Into<DeviceAddress> + Copy,
} Instant: EmuInstant,
Error: EmuError,
{
type Instant = Instant;
type Error = Error;
#[inline]
fn read(&mut self, _clock: Self::Instant, addr: Address, data: &mut [u8]) -> Result<usize, Self::Error> {
let addr = addr.into();
fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
match addr { match addr {
REG_SRA_RD => data[0] = self.port_a.status, REG_SRA_RD => data[0] = self.port_a.status,
REG_RBA_RD => { REG_RBA_RD => {
@ -306,10 +347,13 @@ impl Addressable for MC68681 {
log::debug!("{}: read from {:0x} of {:0x}", DEV_NAME, addr, data[0]); log::debug!("{}: read from {:0x} of {:0x}", DEV_NAME, addr, data[0]);
} }
Ok(()) Ok(1)
} }
fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> { #[inline]
fn write(&mut self, _clock: Self::Instant, addr: Address, data: &[u8]) -> Result<usize, Self::Error> {
let addr = addr.into();
log::debug!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr); log::debug!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr);
match addr { match addr {
REG_MR1A_MR2A | REG_MR1B_MR2B | REG_CSRA_WR | REG_CSRB_WR => { REG_MR1A_MR2A | REG_MR1B_MR2B | REG_CSRA_WR | REG_CSRB_WR => {
@ -361,6 +405,71 @@ impl Addressable for MC68681 {
}, },
_ => {}, _ => {},
} }
Ok(1)
}
}
pub struct MoaMC68681(MC68681<u64, Instant, Error>);
impl Default for MoaMC68681 {
fn default() -> Self {
MoaMC68681(MC68681::default())
}
}
impl DeviceInterface for MC68681<u64, Instant, Error> {
fn as_bus_access(&mut self) -> Option<&mut MoaBus> {
Some(self)
}
fn as_step(&mut self) -> Option<&mut MoaStep> {
Some(self)
}
}
impl Deref for MoaMC68681 {
type Target = MC68681<u64, Instant, Error>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for MoaMC68681 {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
//// OLD INTERFACE
/*
impl Steppable for MC68681 {
fn step(&mut self, system: &System) -> Result<Duration, Error> {
let duration = <Self as Step<u64, Bus>>::step(self, system.clock, &mut *system.bus.borrow_mut())
.map(|next| next.duration_since(system.clock));
let flags = self.get_interrupt_flag();
system
.get_interrupt_controller()
.set(flags.0, flags.1, flags.2)?;
duration
}
}
impl Addressable for MC68681 {
fn size(&self) -> usize {
self.address_space()
}
fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
<Self as BusAccess<u8>>::read(self, clock, addr as u8, data)
.map_err(|err| Error::new(format!("{:?}", err)))?;
Ok(())
}
fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> {
<Self as BusAccess<u8>>::write(self, clock, addr as u8, data)
.map_err(|err| Error::new(format!("{:?}", err)))?;
Ok(()) Ok(())
} }
} }
@ -374,3 +483,4 @@ impl Transmutable for MC68681 {
Some(self) Some(self)
} }
} }
*/

View File

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

View File

@ -1,6 +1,7 @@
use femtos::Frequency; use femtos::{Instant, Frequency};
use moa_core::{System, Error, Debuggable, MemoryBlock, Device}; //use moa_core::{System, Error, Debuggable, MemoryBlock, Device};
use moa_system::{System, Error, MoaBus, MoaStep, MemoryBlock, Device, DeviceInterface, BusAdapter};
use moa_host::Host; use moa_host::Host;
use moa_m68k::{M68k, M68kType}; use moa_m68k::{M68k, M68kType};
@ -23,32 +24,86 @@ impl Default for ComputieOptions {
} }
} }
struct MoaM68k(M68k<Instant>);
use moa_system::emulator_hal::{Step, BusAccess};
impl<Bus> Step<Bus> for MoaM68k
where
Bus: BusAccess<u64, Instant = Instant> + ?Sized,
{
type Instant = Instant;
type Error = Error;
fn is_running(&mut self) -> bool {
true
}
fn reset(&mut self, _now: Self::Instant, _bus: &mut Bus) -> Result<(), Self::Error> {
Ok(())
}
fn step(&mut self, now: Self::Instant, bus: &mut Bus) -> Result<Self::Instant, Self::Error> {
self.0.step(now, &mut BusAdapter::new(bus, |addr| addr as u64, |err| Error::new(format!("{:?}", err))))
.map_err(|err| Error::new(format!("{:?}", err)))
}
}
impl MoaM68k {
pub fn new(cpu: M68k<Instant>) -> Self {
Self(cpu)
}
}
impl DeviceInterface for MoaM68k {
fn as_step(&mut self) -> Option<&mut MoaStep> {
Some(self)
}
}
use core::ops::{Deref, DerefMut};
impl Deref for MoaM68k {
type Target = M68k<Instant>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for MoaM68k {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub fn build_computie<H: Host>(host: &H, options: ComputieOptions) -> Result<System, Error> { pub fn build_computie<H: Host>(host: &H, options: ComputieOptions) -> Result<System, Error> {
let mut system = System::default(); let mut system = System::default();
let mut rom = MemoryBlock::new(vec![0; 0x10000]); let mut rom = MemoryBlock::new(vec![0; 0x10000]);
rom.load_at(0x0000, &options.rom)?; rom.load_at(0x0000, &options.rom)?;
system.add_addressable_device(0x00000000, Device::new(rom))?; system.add_addressable_device(0x00000000, rom.size(), Device::new(rom))?;
let mut ram = MemoryBlock::new(vec![0; options.ram]); let mut ram = MemoryBlock::new(vec![0; options.ram]);
ram.load_at(0, "binaries/computie/kernel.bin")?; ram.load_at(0, "binaries/computie/kernel.bin")
system.add_addressable_device(0x00100000, Device::new(ram))?; .map_err(|err| Error::new(format!("{}", err)))?;
system.add_addressable_device(0x00100000, ram.size(), Device::new(ram))?;
let mut ata = AtaDevice::default(); let mut ata = AtaDevice::default();
ata.load("binaries/computie/disk-with-partition-table.img")?; ata.load("binaries/computie/disk-with-partition-table.img")
system.add_addressable_device(0x00600000, Device::new(ata))?; .map_err(|err| Error::new(format!("{}", err)))?;
system.add_addressable_device(0x00600000, ata.address_space(), Device::new(ata))?;
let mut serial = MC68681::default(); let mut serial = MC68681::default();
launch_terminal_emulator(serial.port_a.connect(host.add_pty()?)?); launch_terminal_emulator(serial.port_a.connect(host.add_pty()?));
launch_slip_connection(serial.port_b.connect(host.add_pty()?)?); launch_slip_connection(serial.port_b.connect(host.add_pty()?));
system.add_addressable_device(0x00700000, Device::new(serial))?; system.add_addressable_device(0x00700000, serial.address_space(), Device::new(serial))?;
let mut cpu = M68k::from_type(M68kType::MC68010, options.frequency); let mut cpu = M68k::from_type(M68kType::MC68010, options.frequency);
cpu.add_breakpoint(0); //cpu.add_breakpoint(0);
system.add_interruptable_device("cpu", Device::new(cpu))?; system.add_interruptable_device("cpu", Device::new(MoaM68k::new(cpu)))?;
Ok(system) Ok(system)
} }
@ -57,25 +112,27 @@ pub fn build_computie_k30<H: Host>(host: &H) -> Result<System, Error> {
let mut system = System::default(); let mut system = System::default();
let monitor = MemoryBlock::load("binaries/computie/monitor-68030.bin")?; let monitor = MemoryBlock::load("binaries/computie/monitor-68030.bin")?;
system.add_addressable_device(0x00000000, Device::new(monitor))?; system.add_addressable_device(0x00000000, monitor.size(), Device::new(monitor))?;
let mut ram = MemoryBlock::new(vec![0; 0x00100000]); let mut ram = MemoryBlock::new(vec![0; 0x00100000]);
ram.load_at(0, "binaries/computie/kernel-68030.bin")?; ram.load_at(0, "binaries/computie/kernel-68030.bin")
system.add_addressable_device(0x00100000, Device::new(ram))?; .map_err(|err| Error::new(format!("{}", err)))?;
system.add_addressable_device(0x00100000, ram.size(), Device::new(ram))?;
let mut ata = AtaDevice::default(); let mut ata = AtaDevice::default();
ata.load("binaries/computie/disk-with-partition-table.img")?; ata.load("binaries/computie/disk-with-partition-table.img")
system.add_addressable_device(0x00600000, Device::new(ata))?; .map_err(|err| Error::new(format!("{}", err)))?;
system.add_addressable_device(0x00600000, ata.address_space(), Device::new(ata))?;
let mut serial = MC68681::default(); let mut serial = MC68681::default();
launch_terminal_emulator(serial.port_a.connect(host.add_pty()?)?); launch_terminal_emulator(serial.port_a.connect(host.add_pty()?));
//launch_slip_connection(serial.port_b.connect(host.add_pty()?)?); //launch_slip_connection(serial.port_b.connect(host.add_pty()?)?);
system.add_addressable_device(0x00700000, Device::new(serial))?; system.add_addressable_device(0x00700000, serial.address_space(), Device::new(serial))?;
let cpu = M68k::from_type(M68kType::MC68030, Frequency::from_hz(10_000_000)); let cpu = M68k::from_type(M68kType::MC68030, Frequency::from_hz(10_000_000));
system.add_interruptable_device("cpu", Device::new(cpu))?; system.add_interruptable_device("cpu", Device::new(MoaM68k::new(cpu)))?;
Ok(system) Ok(system)
} }

View File

@ -2,13 +2,13 @@ use std::mem;
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use femtos::Frequency; use femtos::{Instant, Frequency};
use moa_core::{System, Error, MemoryBlock, Bus, Address, Addressable, Device}; use moa_core::{System, Error, MemoryBlock, Bus, Address, Addressable, Device};
use moa_host::Host; use moa_host::Host;
use moa_m68k::{M68k, M68kType}; use moa_m68k::{M68k, M68kType};
use moa_z80::{Z80, Z80Type}; use moa_z80::{MoaZ80, Z80, Z80Type};
use moa_peripherals_yamaha::Ym2612; use moa_peripherals_yamaha::Ym2612;
use moa_peripherals_yamaha::Sn76489; use moa_peripherals_yamaha::Sn76489;
@ -68,11 +68,16 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
coproc_bus.borrow_mut().insert(0x6000, coproc_register.clone()); coproc_bus.borrow_mut().insert(0x6000, coproc_register.clone());
coproc_bus.borrow_mut().insert(0x7f11, coproc_sn_sound.clone()); coproc_bus.borrow_mut().insert(0x7f11, coproc_sn_sound.clone());
coproc_bus.borrow_mut().insert(0x8000, coproc_area); coproc_bus.borrow_mut().insert(0x8000, coproc_area);
let coproc = Z80::from_type(Z80Type::Z80, Frequency::from_hz(3_579_545), coproc_bus, 0, None); let coproc = Z80::from_type(Z80Type::Z80, Frequency::from_hz(3_579_545));
let mut reset = coproc.reset.clone(); let coproc = MoaZ80 {
let mut bus_request = coproc.bus_request.clone(); bus: coproc_bus,
cpu: coproc,
};
let mut reset = coproc.cpu.signals.reset.clone();
let mut bus_request = coproc.cpu.signals.bus_request.clone();
reset.set(true); reset.set(true);
bus_request.set(true); bus_request.set(true);
let coproc = Device::new(coproc);
// Add coprocessor devices to the system bus so the 68000 can access them too // Add coprocessor devices to the system bus so the 68000 can access them too
system.add_addressable_device(0x00a00000, coproc_ram)?; system.add_addressable_device(0x00a00000, coproc_ram)?;
@ -80,7 +85,7 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
system.add_addressable_device(0x00a06000, coproc_register)?; system.add_addressable_device(0x00a06000, coproc_register)?;
//system.add_addressable_device(0x00c00010, coproc_sn_sound)?; //system.add_addressable_device(0x00c00010, coproc_sn_sound)?;
system.add_device("sn_sound", coproc_sn_sound.clone())?; system.add_device("sn_sound", coproc_sn_sound.clone())?;
system.add_device("coproc", Device::new(coproc))?; system.add_device("coproc", coproc.clone())?;
let controllers = GenesisControllers::new(host)?; let controllers = GenesisControllers::new(host)?;

View File

@ -3,7 +3,7 @@ use femtos::Frequency;
use moa_core::{System, Error, MemoryBlock, Device}; use moa_core::{System, Error, MemoryBlock, Device};
use moa_host::Host; use moa_host::Host;
use moa_z80::{Z80, Z80Type}; use moa_z80::{MoaZ80, Z80, Z80Type};
use crate::peripherals::model1::{Model1Keyboard, Model1Video}; use crate::peripherals::model1::{Model1Keyboard, Model1Video};
@ -44,7 +44,11 @@ pub fn build_trs80<H: Host>(host: &mut H, options: Trs80Options) -> Result<Syste
system.add_addressable_device(0x37E0 + 0x420, Device::new(video)).unwrap(); system.add_addressable_device(0x37E0 + 0x420, Device::new(video)).unwrap();
// TODO the ioport needs to be hooked up // TODO the ioport needs to be hooked up
let cpu = Z80::from_type(Z80Type::Z80, options.frequency, system.bus.clone(), 0, None); let cpu = Z80::from_type(Z80Type::Z80, options.frequency);
let cpu = MoaZ80 {
bus: system.bus.clone(),
cpu,
};
system.add_interruptable_device("cpu", Device::new(cpu))?; system.add_interruptable_device("cpu", Device::new(cpu))?;

View File

@ -1,4 +1,4 @@
Last run on 2024-03-31 at commit 6e7e315808228e03eaf8ad2e8152c087710f1d28 with flags --check-undocumented --check-timings Last run on 2024-04-07 at commit 1c5ad3999afa5591ec8fcbcadf4797514c390031 with flags --check-undocumented --check-timings
00.json completed, all passed! 00.json completed, all passed!
01.json completed, all passed! 01.json completed, all passed!
@ -614,7 +614,7 @@ dd 82.json completed, all passed!
dd 83.json completed, all passed! dd 83.json completed, all passed!
dd 84.json completed, all passed! dd 84.json completed, all passed!
dd 85.json completed, all passed! dd 85.json completed, all passed!
dd 86.json completed, all passed! dd 86.json completed: 0 passed, 1000 FAILED
dd 87.json completed, all passed! dd 87.json completed, all passed!
dd 88.json completed, all passed! dd 88.json completed, all passed!
dd 89.json completed, all passed! dd 89.json completed, all passed!
@ -622,7 +622,7 @@ dd 8a.json completed, all passed!
dd 8b.json completed, all passed! dd 8b.json completed, all passed!
dd 8c.json completed, all passed! dd 8c.json completed, all passed!
dd 8d.json completed, all passed! dd 8d.json completed, all passed!
dd 8e.json completed, all passed! dd 8e.json completed: 0 passed, 1000 FAILED
dd 8f.json completed, all passed! dd 8f.json completed, all passed!
dd 90.json completed, all passed! dd 90.json completed, all passed!
dd 91.json completed, all passed! dd 91.json completed, all passed!
@ -630,7 +630,7 @@ dd 92.json completed, all passed!
dd 93.json completed, all passed! dd 93.json completed, all passed!
dd 94.json completed, all passed! dd 94.json completed, all passed!
dd 95.json completed, all passed! dd 95.json completed, all passed!
dd 96.json completed, all passed! dd 96.json completed: 0 passed, 1000 FAILED
dd 97.json completed, all passed! dd 97.json completed, all passed!
dd 98.json completed, all passed! dd 98.json completed, all passed!
dd 99.json completed, all passed! dd 99.json completed, all passed!
@ -638,7 +638,7 @@ dd 9a.json completed, all passed!
dd 9b.json completed, all passed! dd 9b.json completed, all passed!
dd 9c.json completed, all passed! dd 9c.json completed, all passed!
dd 9d.json completed, all passed! dd 9d.json completed, all passed!
dd 9e.json completed, all passed! dd 9e.json completed: 0 passed, 1000 FAILED
dd 9f.json completed, all passed! dd 9f.json completed, all passed!
dd a0.json completed, all passed! dd a0.json completed, all passed!
dd a1.json completed, all passed! dd a1.json completed, all passed!
@ -646,7 +646,7 @@ dd a2.json completed, all passed!
dd a3.json completed, all passed! dd a3.json completed, all passed!
dd a4.json completed, all passed! dd a4.json completed, all passed!
dd a5.json completed, all passed! dd a5.json completed, all passed!
dd a6.json completed, all passed! dd a6.json completed: 0 passed, 1000 FAILED
dd a7.json completed, all passed! dd a7.json completed, all passed!
dd a8.json completed, all passed! dd a8.json completed, all passed!
dd a9.json completed, all passed! dd a9.json completed, all passed!
@ -654,7 +654,7 @@ dd aa.json completed, all passed!
dd ab.json completed, all passed! dd ab.json completed, all passed!
dd ac.json completed, all passed! dd ac.json completed, all passed!
dd ad.json completed, all passed! dd ad.json completed, all passed!
dd ae.json completed, all passed! dd ae.json completed: 0 passed, 1000 FAILED
dd af.json completed, all passed! dd af.json completed, all passed!
dd b0.json completed, all passed! dd b0.json completed, all passed!
dd b1.json completed, all passed! dd b1.json completed, all passed!
@ -662,7 +662,7 @@ dd b2.json completed, all passed!
dd b3.json completed, all passed! dd b3.json completed, all passed!
dd b4.json completed, all passed! dd b4.json completed, all passed!
dd b5.json completed, all passed! dd b5.json completed, all passed!
dd b6.json completed, all passed! dd b6.json completed: 0 passed, 1000 FAILED
dd b7.json completed, all passed! dd b7.json completed, all passed!
dd b8.json completed, all passed! dd b8.json completed, all passed!
dd b9.json completed, all passed! dd b9.json completed, all passed!
@ -670,7 +670,7 @@ dd ba.json completed, all passed!
dd bb.json completed, all passed! dd bb.json completed, all passed!
dd bc.json completed, all passed! dd bc.json completed, all passed!
dd bd.json completed, all passed! dd bd.json completed, all passed!
dd be.json completed, all passed! dd be.json completed: 0 passed, 1000 FAILED
dd bf.json completed, all passed! dd bf.json completed, all passed!
dd c0.json completed, all passed! dd c0.json completed, all passed!
dd c1.json completed, all passed! dd c1.json completed, all passed!
@ -1038,7 +1038,7 @@ ed 5f.json completed, all passed!
ed 60.json completed: 2 passed, 998 FAILED ed 60.json completed: 2 passed, 998 FAILED
ed 61.json completed, all passed! ed 61.json completed, all passed!
ed 62.json completed, all passed! ed 62.json completed, all passed!
ed 63.json completed, all passed! ed 63.json completed: 0 passed, 1000 FAILED
ed 64.json completed, all passed! ed 64.json completed, all passed!
ed 65.json completed, all passed! ed 65.json completed, all passed!
ed 66.json completed, all passed! ed 66.json completed, all passed!
@ -1046,7 +1046,7 @@ ed 67.json completed, all passed!
ed 68.json completed: 4 passed, 996 FAILED ed 68.json completed: 4 passed, 996 FAILED
ed 69.json completed, all passed! ed 69.json completed, all passed!
ed 6a.json completed, all passed! ed 6a.json completed, all passed!
ed 6b.json completed, all passed! ed 6b.json completed: 0 passed, 1000 FAILED
ed 6c.json completed, all passed! ed 6c.json completed, all passed!
ed 6d.json completed, all passed! ed 6d.json completed, all passed!
ed 6e.json completed, all passed! ed 6e.json completed, all passed!
@ -1058,7 +1058,7 @@ ed 73.json completed, all passed!
ed 74.json completed, all passed! ed 74.json completed, all passed!
ed 75.json completed, all passed! ed 75.json completed, all passed!
ed 76.json completed, all passed! ed 76.json completed, all passed!
ed 77.json completed, all passed! ed 77.json completed: 0 passed, 1000 FAILED
ed 78.json completed: 7 passed, 993 FAILED ed 78.json completed: 7 passed, 993 FAILED
ed 79.json completed, all passed! ed 79.json completed, all passed!
ed 7a.json completed, all passed! ed 7a.json completed, all passed!
@ -1066,7 +1066,7 @@ ed 7b.json completed, all passed!
ed 7c.json completed, all passed! ed 7c.json completed, all passed!
ed 7d.json completed, all passed! ed 7d.json completed, all passed!
ed 7e.json completed, all passed! ed 7e.json completed, all passed!
ed 7f.json completed, all passed! ed 7f.json completed: 0 passed, 1000 FAILED
ed a0.json completed, all passed! ed a0.json completed, all passed!
ed a1.json completed: 0 passed, 1000 FAILED ed a1.json completed: 0 passed, 1000 FAILED
ed a2.json completed: 0 passed, 1000 FAILED ed a2.json completed: 0 passed, 1000 FAILED
@ -1234,7 +1234,7 @@ fd 82.json completed, all passed!
fd 83.json completed, all passed! fd 83.json completed, all passed!
fd 84.json completed, all passed! fd 84.json completed, all passed!
fd 85.json completed, all passed! fd 85.json completed, all passed!
fd 86.json completed, all passed! fd 86.json completed: 0 passed, 1000 FAILED
fd 87.json completed, all passed! fd 87.json completed, all passed!
fd 88.json completed, all passed! fd 88.json completed, all passed!
fd 89.json completed, all passed! fd 89.json completed, all passed!
@ -1242,7 +1242,7 @@ fd 8a.json completed, all passed!
fd 8b.json completed, all passed! fd 8b.json completed, all passed!
fd 8c.json completed, all passed! fd 8c.json completed, all passed!
fd 8d.json completed, all passed! fd 8d.json completed, all passed!
fd 8e.json completed, all passed! fd 8e.json completed: 0 passed, 1000 FAILED
fd 8f.json completed, all passed! fd 8f.json completed, all passed!
fd 90.json completed, all passed! fd 90.json completed, all passed!
fd 91.json completed, all passed! fd 91.json completed, all passed!
@ -1250,7 +1250,7 @@ fd 92.json completed, all passed!
fd 93.json completed, all passed! fd 93.json completed, all passed!
fd 94.json completed, all passed! fd 94.json completed, all passed!
fd 95.json completed, all passed! fd 95.json completed, all passed!
fd 96.json completed, all passed! fd 96.json completed: 0 passed, 1000 FAILED
fd 97.json completed, all passed! fd 97.json completed, all passed!
fd 98.json completed, all passed! fd 98.json completed, all passed!
fd 99.json completed, all passed! fd 99.json completed, all passed!
@ -1258,7 +1258,7 @@ fd 9a.json completed, all passed!
fd 9b.json completed, all passed! fd 9b.json completed, all passed!
fd 9c.json completed, all passed! fd 9c.json completed, all passed!
fd 9d.json completed, all passed! fd 9d.json completed, all passed!
fd 9e.json completed, all passed! fd 9e.json completed: 0 passed, 1000 FAILED
fd 9f.json completed, all passed! fd 9f.json completed, all passed!
fd a0.json completed, all passed! fd a0.json completed, all passed!
fd a1.json completed, all passed! fd a1.json completed, all passed!
@ -1266,7 +1266,7 @@ fd a2.json completed, all passed!
fd a3.json completed, all passed! fd a3.json completed, all passed!
fd a4.json completed, all passed! fd a4.json completed, all passed!
fd a5.json completed, all passed! fd a5.json completed, all passed!
fd a6.json completed, all passed! fd a6.json completed: 0 passed, 1000 FAILED
fd a7.json completed, all passed! fd a7.json completed, all passed!
fd a8.json completed, all passed! fd a8.json completed, all passed!
fd a9.json completed, all passed! fd a9.json completed, all passed!
@ -1274,7 +1274,7 @@ fd aa.json completed, all passed!
fd ab.json completed, all passed! fd ab.json completed, all passed!
fd ac.json completed, all passed! fd ac.json completed, all passed!
fd ad.json completed, all passed! fd ad.json completed, all passed!
fd ae.json completed, all passed! fd ae.json completed: 0 passed, 1000 FAILED
fd af.json completed, all passed! fd af.json completed, all passed!
fd b0.json completed, all passed! fd b0.json completed, all passed!
fd b1.json completed, all passed! fd b1.json completed, all passed!
@ -1282,7 +1282,7 @@ fd b2.json completed, all passed!
fd b3.json completed, all passed! fd b3.json completed, all passed!
fd b4.json completed, all passed! fd b4.json completed, all passed!
fd b5.json completed, all passed! fd b5.json completed, all passed!
fd b6.json completed, all passed! fd b6.json completed: 0 passed, 1000 FAILED
fd b7.json completed, all passed! fd b7.json completed, all passed!
fd b8.json completed, all passed! fd b8.json completed, all passed!
fd b9.json completed, all passed! fd b9.json completed, all passed!
@ -1290,7 +1290,7 @@ fd ba.json completed, all passed!
fd bb.json completed, all passed! fd bb.json completed, all passed!
fd bc.json completed, all passed! fd bc.json completed, all passed!
fd bd.json completed, all passed! fd bd.json completed, all passed!
fd be.json completed, all passed! fd be.json completed: 0 passed, 1000 FAILED
fd bf.json completed, all passed! fd bf.json completed, all passed!
fd c0.json completed, all passed! fd c0.json completed, all passed!
fd c1.json completed, all passed! fd c1.json completed, all passed!
@ -1611,5 +1611,5 @@ fd ff.json completed, all passed!
fe.json completed, all passed! fe.json completed, all passed!
ff.json completed, all passed! ff.json completed, all passed!
passed: 1584670, failed: 25330, total 98% passed: 1564670, failed: 45330, total 97%
completed in 0m 8s completed in 0m 9s

View File

@ -46,6 +46,9 @@ struct Args {
/// Check instruction timings /// Check instruction timings
#[clap(short = 't', long)] #[clap(short = 't', long)]
check_timings: bool, check_timings: bool,
/// Don't check I/O instructions
#[clap(short = 'i', long)]
no_check_io: bool,
/// Directory to the test suite to run /// Directory to the test suite to run
#[clap(long, default_value = DEFAULT_RAD_TESTS)] #[clap(long, default_value = DEFAULT_RAD_TESTS)]
testsuite: String, testsuite: String,
@ -148,7 +151,11 @@ impl TestCase {
} }
fn init_execute_test(cputype: Z80Type, state: &TestState, ports: &[TestPort]) -> Result<(Z80<Instant>, MemoryBlock<Instant>, MemoryBlock<Instant>), Error> { fn init_execute_test(
cputype: Z80Type,
state: &TestState,
ports: &[TestPort],
) -> Result<(Z80<Instant>, MemoryBlock<Instant>, MemoryBlock<Instant>), Error> {
// Insert basic initialization // Insert basic initialization
let len = 0x1_0000; let len = 0x1_0000;
let mut data = Vec::with_capacity(len); let mut data = Vec::with_capacity(len);
@ -220,7 +227,8 @@ fn load_state(
// Load data bytes into memory // Load data bytes into memory
for (addr, byte) in initial.ram.iter() { for (addr, byte) in initial.ram.iter() {
memory.write_u8(Instant::START, *addr, *byte) memory
.write_u8(Instant::START, *addr, *byte)
.map_err(|err| Error::Bus(format!("{:?}", err)))?; .map_err(|err| Error::Bus(format!("{:?}", err)))?;
} }
@ -279,17 +287,19 @@ fn assert_state(
assert_value(cpu.state.iff1 as u8, expected.iff1, "iff1")?; assert_value(cpu.state.iff1 as u8, expected.iff1, "iff1")?;
assert_value(cpu.state.iff2 as u8, expected.iff2, "iff2")?; assert_value(cpu.state.iff2 as u8, expected.iff2, "iff2")?;
// Load data bytes into memory // Compare data bytes in memory
for (addr, byte) in expected.ram.iter() { for (addr, byte) in expected.ram.iter() {
let actual = memory.read_u8(Instant::START, *addr) let actual = memory
.read_u8(Instant::START, *addr)
.map_err(|err| Error::Bus(format!("{:?}", err)))?; .map_err(|err| Error::Bus(format!("{:?}", err)))?;
assert_value(actual, *byte, &format!("ram at {:x}", addr))?; assert_value(actual, *byte, &format!("ram at {:x}", addr))?;
} }
// Load data bytes into io space // Compare data bytes in io space
for port in ports.iter() { for port in ports.iter() {
if port.atype == "w" { if port.atype == "w" {
let actual = io.read_u8(Instant::START, port.addr) let actual = io
.read_u8(Instant::START, port.addr)
.map_err(|err| Error::Bus(format!("{:?}", err)))?; .map_err(|err| Error::Bus(format!("{:?}", err)))?;
assert_value(actual, port.value, &format!("port value at {:x}", port.addr))?; assert_value(actual, port.value, &format!("port value at {:x}", port.addr))?;
} }
@ -306,20 +316,20 @@ fn step_cpu_and_assert(
args: &Args, args: &Args,
) -> Result<(), Error> { ) -> Result<(), Error> {
//let clock_elapsed = cpu.step((memory, io))?; //let clock_elapsed = cpu.step((memory, io))?;
let clock_elapsed = cpu.step(Instant::START, memory) let clock_elapsed = cpu
.step(Instant::START, memory)
.map_err(|err| Error::Step(format!("{:?}", err)))?; .map_err(|err| Error::Step(format!("{:?}", err)))?;
assert_state(cpu, memory, io, &case.final_state, args.check_extra_flags, &case.ports)?; assert_state(cpu, memory, io, &case.final_state, args.check_extra_flags, &case.ports)?;
if args.check_timings { if args.check_timings {
// TODO re-enable. not sure why it can't divide here let cycles = clock_elapsed.as_duration() / cpu.frequency.period_duration();
//let cycles = clock_elapsed / cpu.frequency.period_duration(); if cycles != case.cycles.len() as u64 {
//if cycles != case.cycles.len() { return Err(Error::Assertion(format!(
// return Err(Error::Assertion(format!( "expected instruction to take {} cycles, but took {}",
// "expected instruction to take {} cycles, but took {}", case.cycles.len(),
// case.cycles.len(), cycles
// cycles )));
// ))); }
//}
} }
Ok(()) Ok(())
@ -338,8 +348,8 @@ fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> {
if args.debug { if args.debug {
case.dump(); case.dump();
println!(); println!();
initial_cpu.dump_state(Instant::START); initial_cpu.dump_state(Instant::START, &mut memory);
cpu.dump_state(Instant::START); cpu.dump_state(Instant::START, &mut memory);
} }
println!("FAILED: {:?}", err); println!("FAILED: {:?}", err);
} }
@ -371,6 +381,10 @@ fn test_json_file(path: PathBuf, args: &Args) -> (usize, usize, String) {
} }
} }
if args.no_check_io && !case.ports.is_empty() {
continue;
}
// Sort the ram memory for debugging help // Sort the ram memory for debugging help
if args.debug { if args.debug {
case.initial_state.ram.sort_by_key(|(addr, _)| *addr); case.initial_state.ram.sort_by_key(|(addr, _)| *addr);

View File

@ -1,14 +1,20 @@
* decide if you should continue expecting Instant to usable through the trait alone, despite issues * fix the Z80 reset and bus_request signals
* fix it to use the full 68k address space, and maybe see if it's possible to make the address translation cleaner/nicer/simpler/faster * the emulator_hal_memory should throw an error when an access will straddle the end of memory? Or should it autowrap?
* now that you have Instant as generic in m68k, try making it an associated type to see if it's possible to hide it away * fix the m68k dumping functions
* convert computie system to use the new moa-system library crate to replace the old core crate
* change package names to drop the 's', so moa-systems-computie becomes moa-system-computie
* try using the debug and inspect traits elsewhere in moa
* convert all code to use fmt::Writer instead of println * convert all code to use fmt::Writer instead of println
* fix it to use the full 68k address space, and maybe see if it's possible to make the address translation cleaner/nicer/simpler/faster
* figure out how to do interrupts, and add them to emulator-hal, implement them in m68k
* convert the Z80 * convert the Z80
* convert peripherals to use BusAccess and Step * convert peripherals to use BusAccess and Step
* try using the debug and inspect traits elsewhere in moa
* decide if you should continue expecting Instant to usable through the trait alone, despite issues
* figure out how to do interrupts, and add them to emulator-hal, implement them in m68k
* replace Addressable/Steppable and modify Transmutable to use the emulator-hal traits * replace Addressable/Steppable and modify Transmutable to use the emulator-hal traits
* remove the custom moa impls from m68k if possible at this point * remove the custom moa impls from m68k if possible at this point
* publish the emulator-hal crate * publish the emulator-hal crate
@ -79,7 +85,7 @@
* add doc strings everywhere * add doc strings everywhere
* get rustfmt, rustdoc, and clippy working in some kind of semi-automatic fashion * get rustfmt, rustdoc, and clippy working in some kind of semi-automatic fashion
* you really need a full web-based debugger * you really need a full web-based debugger; look into egui as the basis for it?