Compare commits
6 Commits
fd056c8d7b
...
35e746f0af
Author | SHA1 | Date |
---|---|---|
transistor | 35e746f0af | |
transistor | 90e617c5de | |
transistor | 471695aff5 | |
transistor | 1c5ad3999a | |
transistor | 59199533eb | |
transistor | 1bb66e3308 |
|
@ -804,13 +804,12 @@ dependencies = [
|
|||
"log",
|
||||
"moa-common",
|
||||
"moa-core",
|
||||
"moa-debugger",
|
||||
"moa-host",
|
||||
"moa-m68k",
|
||||
"moa-peripherals-generic",
|
||||
"moa-peripherals-motorola",
|
||||
"moa-system",
|
||||
"moa-systems-computie",
|
||||
"moa-systems-genesis",
|
||||
"simple_logger",
|
||||
]
|
||||
|
||||
|
@ -884,9 +883,11 @@ dependencies = [
|
|||
name = "moa-peripherals-generic"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"emulator-hal",
|
||||
"femtos",
|
||||
"log",
|
||||
"moa-core",
|
||||
"moa-system",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -903,10 +904,12 @@ dependencies = [
|
|||
name = "moa-peripherals-motorola"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"emulator-hal",
|
||||
"femtos",
|
||||
"log",
|
||||
"moa-core",
|
||||
"moa-host",
|
||||
"moa-system",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -937,6 +940,17 @@ dependencies = [
|
|||
"femtos",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moa-system"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"emulator-hal",
|
||||
"femtos",
|
||||
"log",
|
||||
"moa-host",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moa-systems-computie"
|
||||
version = "0.1.0"
|
||||
|
@ -948,6 +962,7 @@ dependencies = [
|
|||
"moa-m68k",
|
||||
"moa-peripherals-generic",
|
||||
"moa-peripherals-motorola",
|
||||
"moa-system",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1247,9 +1262,10 @@ name = "rad-tests"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap 3.2.25",
|
||||
"emulator-hal",
|
||||
"emulator-hal-memory",
|
||||
"femtos",
|
||||
"flate2",
|
||||
"moa-core",
|
||||
"moa-z80",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
resolver = "2"
|
||||
members = [
|
||||
"emulator/core",
|
||||
"emulator/libraries/system",
|
||||
"emulator/frontends/common",
|
||||
"emulator/frontends/console",
|
||||
"emulator/frontends/minifb",
|
||||
|
|
|
@ -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
|
||||
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:
|
||||
[rustdocs for moa_core](http://jabberwocky.ca/moa/doc/moa_core/)
|
||||
[rustdocs for ym2612](http://jabberwocky.ca/moa/doc/moa_peripherals_yamaha/ym2612/index.html)
|
||||
[moa_core](http://jabberwocky.ca/moa/doc/moa_core/)
|
||||
[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
|
||||
```sh
|
||||
|
|
|
@ -8,4 +8,4 @@ log = "0.4"
|
|||
femtos = "0.1"
|
||||
thiserror = "1.0"
|
||||
moa-host = { path = "../libraries/host" }
|
||||
emulator-hal = { path = "../libraries/emulator-hal/emulator-hal" }
|
||||
emulator-hal = { path = "../libraries/emulator-hal/emulator-hal", features = ["femtos"] }
|
||||
|
|
|
@ -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]
|
||||
pub fn read_beu16(data: &[u8]) -> 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>;
|
||||
}
|
||||
|
||||
#[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 {
|
||||
#[inline]
|
||||
|
@ -197,6 +232,11 @@ pub trait Transmutable {
|
|||
fn as_inspectable(&mut self) -> Option<&mut dyn Inspectable> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_signalable(&mut self) -> Option<&mut dyn Signalable> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
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)))
|
||||
}
|
||||
|
||||
|
||||
static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
|
|
|
@ -71,3 +71,9 @@ impl<E> From<HostError<E>> for Error {
|
|||
Self::Other("other".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<fmt::Error> for Error {
|
||||
fn from(err: fmt::Error) -> Self {
|
||||
Self::Other(format!("{:?}", err))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@ mod memory;
|
|||
mod system;
|
||||
|
||||
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::{
|
||||
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::system::System;
|
||||
|
||||
pub use emulator_hal::bus::{BusAccess};
|
||||
pub use emulator_hal::BusAccess;
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::rc::Rc;
|
|||
use std::cell::RefCell;
|
||||
use std::fmt::Write;
|
||||
use femtos::Instant;
|
||||
use emulator_hal::{self, BusAccess, Error as EmuError};
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::devices::{Address, Addressable, Transmutable, Device, read_beu16};
|
||||
|
@ -236,7 +237,7 @@ impl Bus {
|
|||
|
||||
let to = if count < 16 { count / 2 } else { 8 };
|
||||
for _ in 0..to {
|
||||
let word = self.read_beu16(clock, addr);
|
||||
let word = Addressable::read_beu16(self, clock, addr);
|
||||
if word.is_err() {
|
||||
println!("{}", line);
|
||||
return;
|
||||
|
@ -353,7 +354,7 @@ impl Addressable for BusPort {
|
|||
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());
|
||||
subdevice.read(clock, addr_index, &mut data[i..end])?;
|
||||
Addressable::read(&mut *subdevice, clock, addr_index, &mut data[i..end])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -364,7 +365,7 @@ impl Addressable for BusPort {
|
|||
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());
|
||||
subdevice.write(clock, addr_index, &data[i..end])?;
|
||||
Addressable::write(&mut *subdevice, clock, addr_index, &data[i..end])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -412,9 +413,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
use emulator_hal::bus::{self, BusAccess};
|
||||
|
||||
impl bus::Error for Error {}
|
||||
impl BusError for Error {}
|
||||
|
||||
impl BusAccess<u64> for &mut dyn Addressable {
|
||||
type Instant = Instant;
|
||||
|
@ -430,3 +429,18 @@ impl BusAccess<u64> for &mut dyn Addressable {
|
|||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
//let _ = self.decoder.decode_at(&mut self.bus, true, self.state.pc);
|
||||
//self.decoder.dump_decoded(&mut self.bus);
|
||||
//self.dump_state();
|
||||
let mut decoder = M68kDecoder::new(self.info.chip, true, self.state.pc);
|
||||
decoder.decode_at(&mut adapter, &mut M68kBusPort::default(), true, self.state.pc)?;
|
||||
decoder.dump_decoded(system.clock, &mut adapter);
|
||||
let mut writer = String::new();
|
||||
self.dump_state(&mut writer)?;
|
||||
println!("{}", writer);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use core::fmt::Write;
|
||||
use emulator_hal::{BusAccess, Instant as EmuInstant};
|
||||
|
||||
use crate::state::{Z80Error, Z80Address} ;
|
||||
use crate::state::{Z80Error, Z80Address};
|
||||
use crate::instructions::{
|
||||
Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target,
|
||||
LoadTarget, UndocumentedCopy, Instruction,
|
||||
|
@ -55,27 +55,16 @@ impl Z80Decoder {
|
|||
Ok(decoder.decoder)
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn format_instruction_bytes(&mut self) -> String {
|
||||
let mut ins_data = String::new();
|
||||
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) {
|
||||
pub fn dump_disassembly<Bus>(bus: &mut Bus, start: Z80Address, length: Z80Address)
|
||||
where
|
||||
Bus: BusAccess<Z80Address>,
|
||||
{
|
||||
let mut next = start;
|
||||
while next < (start + length) {
|
||||
match self.decode_at(self.clock, next) {
|
||||
Ok(()) => {
|
||||
self.dump_decoded();
|
||||
next = self.decoder.end;
|
||||
match Z80Decoder::decode_at(bus, Bus::Instant::START, next) {
|
||||
Ok(mut decoder) => {
|
||||
decoder.dump_decoded(bus);
|
||||
next = decoder.end;
|
||||
},
|
||||
Err(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>
|
||||
|
@ -107,11 +114,7 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn decode_bare(
|
||||
&mut self,
|
||||
ins: u8,
|
||||
extra_instruction_bytes: u16,
|
||||
) -> Result<Instruction, Z80Error> {
|
||||
pub fn decode_bare(&mut self, ins: u8, extra_instruction_bytes: u16) -> Result<Instruction, Z80Error> {
|
||||
self.decoder.extra_instruction_bytes = extra_instruction_bytes;
|
||||
match get_ins_x(ins) {
|
||||
0 => match get_ins_z(ins) {
|
||||
|
@ -559,11 +562,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn decode_index_target(
|
||||
&mut self,
|
||||
index_reg: IndexRegister,
|
||||
z: u8,
|
||||
) -> Result<Option<Target>, Z80Error> {
|
||||
fn decode_index_target(&mut self, index_reg: IndexRegister, z: u8) -> Result<Option<Target>, Z80Error> {
|
||||
let result = match z {
|
||||
4 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 0))),
|
||||
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> {
|
||||
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)))?;
|
||||
self.decoder.end = self.decoder.end.wrapping_add(1);
|
||||
Ok(byte)
|
||||
|
@ -587,7 +588,9 @@ where
|
|||
fn read_instruction_word(&mut self) -> Result<u16, Z80Error> {
|
||||
let mut bytes = [0; 2];
|
||||
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)))?;
|
||||
self.decoder.end = self.decoder.end.wrapping_add(1);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
use emulator_hal::{BusAccess, Instant as EmuInstant, Error as EmuError, Step, Inspect, Debug, IntoAddress};
|
||||
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> {
|
||||
let mut executor = self.begin(now, bus)?;
|
||||
executor.step_one()?;
|
||||
let clocks = executor.step_one()?;
|
||||
self.previous_cycle = executor.end();
|
||||
// TODO fix this
|
||||
Ok(now)
|
||||
Ok(now + Instant::hertz_to_duration(self.frequency.as_hz() as u64) * clocks as u32)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,11 +49,9 @@ where
|
|||
|
||||
fn step(&mut self, now: Self::Instant, bus: (&mut MemBus, &mut IoBus)) -> Result<Self::Instant, Self::Error> {
|
||||
let executor = self.begin(now, bus)?;
|
||||
executor.step_one()?;
|
||||
let clocks = executor.step_one()?;
|
||||
self.previous_cycle = executor.end();
|
||||
// TODO fix this
|
||||
Ok(now)
|
||||
Ok(now + Instant::hertz_to_duration(self.frequency.as_hz() as u64) * clocks as u32)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::instructions::{
|
|||
Condition, Instruction, LoadTarget, Target, Register, InterruptMode, RegisterPair, IndexRegister, SpecialRegister,
|
||||
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::debugger::Z80Debugger;
|
||||
|
||||
|
@ -41,12 +41,17 @@ impl<Instant> Z80<Instant>
|
|||
where
|
||||
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
|
||||
Bus: BusAccess<Z80Address, Instant = Instant>,
|
||||
{
|
||||
let executor = ExecuteNext {
|
||||
state: &mut self.state,
|
||||
signals: &mut self.signals,
|
||||
debugger: &mut self.debugger,
|
||||
cycle: Z80Cycle::at_time(clock),
|
||||
bus,
|
||||
|
@ -61,6 +66,7 @@ where
|
|||
Bus: BusAccess<Z80Address, Instant = Instant>,
|
||||
{
|
||||
state: &'a mut Z80State,
|
||||
signals: &'a mut Z80Signals,
|
||||
debugger: &'a mut Z80Debugger,
|
||||
cycle: Z80Cycle<Instant>,
|
||||
bus: Bus,
|
||||
|
@ -71,28 +77,22 @@ where
|
|||
Bus: BusAccess<Z80Address, Instant = Instant>,
|
||||
Instant: EmuInstant,
|
||||
{
|
||||
pub(crate) fn end(mut self) -> Z80Cycle<Instant> {
|
||||
pub(crate) fn end(self) -> Z80Cycle<Instant> {
|
||||
self.cycle
|
||||
}
|
||||
|
||||
pub(crate) fn step_one(&mut self) -> Result<u16, Z80Error> {
|
||||
// TODO restore the reset and bus request signals
|
||||
//let clocks = if self.reset.get() {
|
||||
// self.reset()?
|
||||
//} else if self.bus_request.get() {
|
||||
// 4
|
||||
//} else {
|
||||
// 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)?;
|
||||
let clocks = if self.signals.reset.get() {
|
||||
self.reset()?
|
||||
} else if self.signals.bus_request.get() {
|
||||
4
|
||||
} else {
|
||||
self.step_internal()?
|
||||
};
|
||||
Ok(clocks)
|
||||
}
|
||||
|
||||
fn step_internal(&mut self, clock: Instant) -> Result<u16, Z80Error> {
|
||||
fn step_internal(&mut self) -> Result<u16, Z80Error> {
|
||||
match self.state.status {
|
||||
Status::Init => self.init(),
|
||||
Status::Halted => Err(Z80Error::Halted),
|
||||
|
@ -630,7 +630,9 @@ where
|
|||
let parity = if count != 0 { Flags::Parity as u8 } else { 0 };
|
||||
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.state.pc -= 2;
|
||||
}
|
||||
|
@ -1126,13 +1128,17 @@ where
|
|||
|
||||
fn read_port_u8(&mut self, addr: u16) -> Result<u8, Z80Error> {
|
||||
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)))?)
|
||||
}
|
||||
|
||||
fn write_port_u8(&mut self, addr: u16, value: u8) -> Result<(), Z80Error> {
|
||||
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)))?)
|
||||
}
|
||||
|
||||
|
@ -1144,7 +1150,9 @@ where
|
|||
let mut bytes = [0; 2];
|
||||
for byte in bytes.iter_mut() {
|
||||
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)))?;
|
||||
addr = addr.wrapping_add(1);
|
||||
}
|
||||
|
@ -1159,7 +1167,8 @@ where
|
|||
let mut bytes = value.to_le_bytes();
|
||||
for byte in bytes.iter_mut() {
|
||||
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)))?;
|
||||
addr = addr.wrapping_add(1);
|
||||
}
|
||||
|
@ -1172,7 +1181,7 @@ where
|
|||
//if let Some(io) = self.ioport.as_mut() {
|
||||
// Ok(io.read_u8(self.cycle.current_clock, addr)?)
|
||||
//} else {
|
||||
Ok(0)
|
||||
Ok(0)
|
||||
//}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
mod debugger;
|
||||
mod decode;
|
||||
mod emuhal;
|
||||
mod execute;
|
||||
mod instructions;
|
||||
mod state;
|
||||
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::decode::Z80Decoder;
|
||||
|
|
|
@ -1,35 +1,64 @@
|
|||
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use femtos::{Instant, Duration};
|
||||
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::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
|
||||
Instant: EmuInstant,
|
||||
{
|
||||
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 executor = self.begin(system.clock, &mut adapter)?;
|
||||
let mut executor = self.cpu.begin(system.clock, &mut adapter)?;
|
||||
let clocks = executor.step_one()?;
|
||||
self.previous_cycle = executor.end();
|
||||
Ok(Instant::hertz_to_duration(self.frequency.as_hz() as u64) * clocks as u32)
|
||||
self.cpu.previous_cycle = executor.end();
|
||||
Ok(Instant::hertz_to_duration(self.cpu.frequency.as_hz() as u64) * clocks as u32)
|
||||
}
|
||||
|
||||
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> {
|
||||
Some(self)
|
||||
}
|
||||
|
@ -41,6 +70,11 @@ impl Transmutable for Z80<Instant> {
|
|||
fn as_debuggable(&mut self) -> Option<&mut dyn Debuggable> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
//#[inline]
|
||||
//fn as_signalable(&mut self) -> Option<&mut dyn Signalable> {
|
||||
// Some(self)
|
||||
//}
|
||||
}
|
||||
|
||||
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) {
|
||||
self.debugger.breakpoints.push(addr as u16);
|
||||
self.cpu.debugger.breakpoints.push(addr as u16);
|
||||
}
|
||||
|
||||
fn remove_breakpoint(&mut self, addr: Address) {
|
||||
if let Some(index) = self.debugger.breakpoints.iter().position(|a| *a == addr as u16) {
|
||||
self.debugger.breakpoints.remove(index);
|
||||
if let Some(index) = self.cpu.debugger.breakpoints.iter().position(|a| *a == addr as u16) {
|
||||
self.cpu.debugger.breakpoints.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,22 +113,22 @@ impl Debuggable for Z80<Instant> {
|
|||
let bus = &mut *system.bus.borrow_mut();
|
||||
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)?;
|
||||
// TODO disabled until decoder is fixed
|
||||
//self.decoder.dump_decoded(&mut self.port);
|
||||
self.dump_state(system.clock);
|
||||
let decoder = Z80Decoder::decode_at(&mut adapter, system.clock, self.cpu.state.pc)?;
|
||||
self.cpu.previous_cycle.decoder.dump_decoded(&mut adapter);
|
||||
self.cpu.dump_state(system.clock, &mut adapter);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_disassembly(&mut self, _system: &System, addr: Address, count: usize) {
|
||||
// TODO disabled until decoder is fixed
|
||||
//let mut decoder = Z80Decoder::default();
|
||||
//decoder.dump_disassembly(&mut self.port, addr as u16, count as u16);
|
||||
fn print_disassembly(&mut self, system: &System, addr: Address, count: usize) {
|
||||
let bus = &mut *system.bus.borrow_mut();
|
||||
let mut adapter = BusAdapter::new(bus, |addr| addr as u64, |err| Z80Error::BusError(format!("{:?}", err)));
|
||||
|
||||
Z80Decoder::dump_disassembly(&mut adapter, addr as u16, count as u16);
|
||||
}
|
||||
|
||||
fn run_command(&mut self, _system: &System, args: &[&str]) -> Result<bool, Error> {
|
||||
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);
|
||||
},
|
||||
|
@ -102,5 +136,3 @@ impl Debuggable for Z80<Instant> {
|
|||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
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_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)]
|
||||
pub enum Z80Error /* <B: fmt::Display> */ {
|
||||
#[error("cpu halted")]
|
||||
|
@ -120,8 +128,7 @@ pub struct Z80<Instant> {
|
|||
pub state: Z80State,
|
||||
pub debugger: Z80Debugger,
|
||||
pub previous_cycle: Z80Cycle<Instant>,
|
||||
//pub port: BusPort,
|
||||
//pub ioport: Option<BusPort>,
|
||||
pub signals: Z80Signals,
|
||||
// TODO activate later
|
||||
//pub reset: Signal<bool>,
|
||||
//pub bus_request: Signal<bool>,
|
||||
|
@ -131,34 +138,22 @@ impl<Instant> Z80<Instant>
|
|||
where
|
||||
Instant: EmuInstant,
|
||||
{
|
||||
pub fn new(cputype: Z80Type, frequency: Frequency /*, port: BusPort, ioport: Option<BusPort>*/) -> Self {
|
||||
pub fn new(cputype: Z80Type, frequency: Frequency) -> Self {
|
||||
Self {
|
||||
cputype,
|
||||
frequency,
|
||||
state: Z80State::default(),
|
||||
debugger: Z80Debugger::default(),
|
||||
previous_cycle: Z80Cycle::at_time(Instant::START),
|
||||
//port,
|
||||
//ioport,
|
||||
signals: Z80Signals::default(),
|
||||
//reset: Signal::new(false),
|
||||
//bus_request: Signal::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_type(
|
||||
cputype: Z80Type,
|
||||
frequency: Frequency,
|
||||
bus: Rc<RefCell<Bus>>,
|
||||
addr_offset: Address,
|
||||
io_bus: Option<(Rc<RefCell<Bus>>, Address)>,
|
||||
) -> Self {
|
||||
pub fn from_type(cputype: Z80Type, frequency: Frequency) -> Self {
|
||||
match cputype {
|
||||
Z80Type::Z80 => Self::new(
|
||||
cputype,
|
||||
frequency,
|
||||
//BusPort::new(addr_offset, 16, 8, bus),
|
||||
//io_bus.map(|(io_bus, io_offset)| BusPort::new(io_offset, 16, 8, io_bus)),
|
||||
),
|
||||
Z80Type::Z80 => Self::new(cputype, frequency),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,7 +163,10 @@ where
|
|||
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!("PC: {:#06x}", self.state.pc);
|
||||
println!("SP: {:#06x}", self.state.sp);
|
||||
|
@ -207,12 +205,11 @@ where
|
|||
println!("I: {:#04x} R: {:#04x}", self.state.i, self.state.r);
|
||||
println!("IM: {:?} IFF1: {:?} IFF2: {:?}", self.state.im, self.state.iff1, self.state.iff2);
|
||||
|
||||
// TODO disabled until function is reimplemented
|
||||
//println!(
|
||||
// "Current Instruction: {} {:?}",
|
||||
// self.decoder.format_instruction_bytes(&mut self.port),
|
||||
// self.decoder.instruction
|
||||
//);
|
||||
println!(
|
||||
"Current Instruction: {} {:?}",
|
||||
self.previous_cycle.decoder.format_instruction_bytes(bus),
|
||||
self.previous_cycle.decoder.instruction
|
||||
);
|
||||
println!("Previous Instruction: {:?}", self.previous_cycle.decoder.instruction);
|
||||
println!();
|
||||
// TODO disabled until function is reimplemented
|
||||
|
|
|
@ -11,11 +11,12 @@ simple_logger = "4"
|
|||
femtos = "0.1"
|
||||
|
||||
moa-core = { path = "../../core" }
|
||||
moa-system = { path = "../../libraries/system" }
|
||||
moa-host = { path = "../../libraries/host" }
|
||||
moa-common = { path = "../common", features = ["tty"] }
|
||||
|
||||
moa-debugger = { path = "../../libraries/debugger" }
|
||||
moa-systems-genesis = { path = "../../systems/genesis" }
|
||||
#moa-debugger = { path = "../../libraries/debugger" }
|
||||
#moa-systems-genesis = { path = "../../systems/genesis" }
|
||||
moa-systems-computie = { path = "../../systems/computie" }
|
||||
moa-m68k = { path = "../../cpus/m68k", features = ["moa"] }
|
||||
moa-peripherals-generic = { path = "../../peripherals/generic" }
|
||||
|
|
|
@ -2,8 +2,8 @@ use clap::{Command, Arg, ArgAction, ArgMatches};
|
|||
use std::io::{self, Write};
|
||||
use femtos::Duration;
|
||||
|
||||
use moa_core::{Error, System};
|
||||
use moa_debugger::{Debugger, DebugControl};
|
||||
use moa_system::{Error, System};
|
||||
//use moa_debugger::{Debugger, DebugControl};
|
||||
use moa_host::{Host, HostError, Tty, ControllerEvent, Audio, DummyAudio, FrameReceiver, EventSender};
|
||||
|
||||
pub struct ConsoleFrontend;
|
||||
|
@ -75,9 +75,10 @@ impl ConsoleFrontend {
|
|||
.unwrap();
|
||||
|
||||
// Run the main loop
|
||||
let mut debugger = Debugger::default();
|
||||
//let mut debugger = Debugger::default();
|
||||
let mut run_debugger = matches.get_flag("debugger");
|
||||
loop {
|
||||
/*
|
||||
if run_debugger {
|
||||
run_debugger = false;
|
||||
|
||||
|
@ -99,6 +100,7 @@ impl ConsoleFrontend {
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
match system.run_for_duration(Duration::MAX - system.clock.as_duration()) {
|
||||
Ok(()) => {},
|
||||
|
|
|
@ -8,7 +8,7 @@ fn main() {
|
|||
Arg::new("ROM")
|
||||
.short('r')
|
||||
.long("rom")
|
||||
.action(ArgAction::SetTrue)
|
||||
.action(ArgAction::Set)
|
||||
.value_name("FILE")
|
||||
.help("ROM file to load at the start of memory"),
|
||||
)
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 2391a324376bdd9fa1ae9801bbe3d12f2e69fa62
|
||||
Subproject commit c17e364ed472fff774b1b38323a2c72936c7224c
|
|
@ -19,7 +19,7 @@ type Input<T> = Signal<T>;
|
|||
#[allow(dead_code)]
|
||||
type TriState<T> = Signal<T>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Signal<T: Copy>(Rc<Cell<T>>);
|
||||
|
||||
impl<T: Copy> Signal<T> {
|
||||
|
|
|
@ -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 = []
|
|
@ -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>;
|
||||
}
|
||||
*/
|
|
@ -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())
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -6,4 +6,6 @@ edition = "2021"
|
|||
[dependencies]
|
||||
log = "0.4"
|
||||
femtos = "0.1"
|
||||
emulator-hal = { path = "../../libraries/emulator-hal/emulator-hal", features = ["femtos"] }
|
||||
moa-system = { path = "../../libraries/system" }
|
||||
moa-core = { path = "../../core" }
|
||||
|
|
|
@ -1,30 +1,36 @@
|
|||
use std::fs;
|
||||
use std::io;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::convert::Infallible;
|
||||
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]
|
||||
mod reg {
|
||||
use super::Address;
|
||||
pub(super) const DATA_WORD: Address = 0x20;
|
||||
pub(super) const DATA_BYTE: Address = 0x21;
|
||||
pub(super) const FEATURE: Address = 0x23;
|
||||
pub(super) const ERROR: Address = 0x23;
|
||||
pub(super) const SECTOR_COUNT: Address = 0x25;
|
||||
pub(super) const SECTOR_NUM: Address = 0x27;
|
||||
pub(super) const CYL_LOW: Address = 0x29;
|
||||
pub(super) const CYL_HIGH: Address = 0x2B;
|
||||
pub(super) const DRIVE_HEAD: Address = 0x2D;
|
||||
pub(super) const STATUS: Address = 0x2F;
|
||||
pub(super) const COMMAND: Address = 0x2F;
|
||||
use super::DeviceAddress;
|
||||
pub(super) const DATA_WORD: u8 = 0x20;
|
||||
pub(super) const DATA_BYTE: u8 = 0x21;
|
||||
pub(super) const FEATURE: u8 = 0x23;
|
||||
pub(super) const ERROR: u8 = 0x23;
|
||||
pub(super) const SECTOR_COUNT: u8 = 0x25;
|
||||
pub(super) const SECTOR_NUM: u8 = 0x27;
|
||||
pub(super) const CYL_LOW: u8 = 0x29;
|
||||
pub(super) const CYL_HIGH: u8 = 0x2B;
|
||||
pub(super) const DRIVE_HEAD: u8 = 0x2D;
|
||||
pub(super) const STATUS: u8 = 0x2F;
|
||||
pub(super) const COMMAND: u8 = 0x2F;
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
mod cmd {
|
||||
pub(super) const READ_SECTORS: u8 = 0x20;
|
||||
pub(super) const WRITE_SECTORS: u8 = 0x30;
|
||||
pub(super) const IDENTIFY: u8 = 0xEC;
|
||||
pub(super) const SET_FEATURE: u8 = 0xEF;
|
||||
pub(super) const READ_SECTORS: u8 = 0x20;
|
||||
pub(super) const WRITE_SECTORS: u8 = 0x30;
|
||||
pub(super) const IDENTIFY: u8 = 0xEC;
|
||||
pub(super) const SET_FEATURE: u8 = 0xEF;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -38,32 +44,47 @@ const ATA_SECTOR_SIZE: u32 = 512;
|
|||
|
||||
const DEV_NAME: &str = "ata";
|
||||
|
||||
pub struct DeviceAddress(u8);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AtaDevice {
|
||||
pub struct AtaDevice<Error>
|
||||
where
|
||||
Error: Default,
|
||||
{
|
||||
selected_sector: u32,
|
||||
selected_count: u32,
|
||||
last_error: u8,
|
||||
contents: Vec<u8>,
|
||||
error: PhantomData<Error>,
|
||||
}
|
||||
|
||||
impl AtaDevice {
|
||||
pub fn load(&mut self, filename: &str) -> Result<(), Error> {
|
||||
match fs::read(filename) {
|
||||
Ok(contents) => {
|
||||
self.contents = contents;
|
||||
Ok(())
|
||||
},
|
||||
Err(_) => Err(Error::new(format!("Error reading contents of {}", filename))),
|
||||
}
|
||||
impl<Error> AtaDevice<Error>
|
||||
where
|
||||
Error: Default,
|
||||
{
|
||||
pub fn load(&mut self, filename: &str) -> Result<(), io::Error> {
|
||||
let contents = fs::read(filename)?;
|
||||
self.contents = contents;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Addressable for AtaDevice {
|
||||
fn size(&self) -> usize {
|
||||
pub fn address_space(&self) -> usize {
|
||||
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 {
|
||||
reg::DATA_WORD => {
|
||||
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]);
|
||||
match addr {
|
||||
reg::DRIVE_HEAD => {
|
||||
|
@ -139,12 +163,76 @@ impl Addressable for AtaDevice {
|
|||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Transmutable for AtaDevice {
|
||||
impl Transmutable for AtaDevice<u64> {
|
||||
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -6,5 +6,8 @@ edition = "2021"
|
|||
[dependencies]
|
||||
log = "0.4"
|
||||
femtos = "0.1"
|
||||
emulator-hal = { path = "../../libraries/emulator-hal/emulator-hal", features = ["femtos"] }
|
||||
|
||||
moa-system = { path = "../../libraries/system" }
|
||||
moa-core = { path = "../../core" }
|
||||
moa-host = { path = "../../libraries/host" }
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
mod mc68681;
|
||||
pub use crate::mc68681::MC68681;
|
||||
pub use crate::mc68681::{MC68681, MoaMC68681};
|
||||
|
|
|
@ -1,39 +1,46 @@
|
|||
use core::marker::PhantomData;
|
||||
use core::convert::Infallible;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
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_system::{DeviceInterface, Error, MoaBus, MoaStep};
|
||||
|
||||
const REG_MR1A_MR2A: Address = 0x01;
|
||||
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;
|
||||
type DeviceAddress = u64;
|
||||
|
||||
const REG_MR1B_MR2B: Address = 0x11;
|
||||
const REG_SRB_RD: Address = 0x13;
|
||||
const REG_CSRB_WR: Address = 0x13;
|
||||
const REG_CRB_WR: Address = 0x15;
|
||||
const REG_TBB_WR: Address = 0x17;
|
||||
const REG_RBB_RD: Address = 0x17;
|
||||
const REG_MR1A_MR2A: DeviceAddress = 0x01;
|
||||
const REG_SRA_RD: DeviceAddress = 0x03;
|
||||
const REG_CSRA_WR: DeviceAddress = 0x03;
|
||||
const REG_CRA_WR: DeviceAddress = 0x05;
|
||||
const REG_TBA_WR: DeviceAddress = 0x07;
|
||||
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_CTLR_WR: Address = 0x0F;
|
||||
const REG_START_RD: Address = 0x1D;
|
||||
const REG_STOP_RD: Address = 0x1F;
|
||||
const REG_ACR_WR: DeviceAddress = 0x09;
|
||||
|
||||
const REG_IPCR_RD: Address = 0x09;
|
||||
const REG_OPCR_WR: Address = 0x1B;
|
||||
const REG_INPUT_RD: Address = 0x1B;
|
||||
const REG_OUT_SET: Address = 0x1D;
|
||||
const REG_OUT_RESET: Address = 0x1F;
|
||||
const REG_CTUR_WR: DeviceAddress = 0x0D;
|
||||
const REG_CTLR_WR: DeviceAddress = 0x0F;
|
||||
const REG_START_RD: DeviceAddress = 0x1D;
|
||||
const REG_STOP_RD: DeviceAddress = 0x1F;
|
||||
|
||||
const REG_ISR_RD: Address = 0x0B;
|
||||
const REG_IMR_WR: Address = 0x0B;
|
||||
const REG_IVR_WR: Address = 0x19;
|
||||
const REG_IPCR_RD: DeviceAddress = 0x09;
|
||||
const REG_OPCR_WR: DeviceAddress = 0x1B;
|
||||
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)
|
||||
|
@ -80,11 +87,11 @@ pub struct 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();
|
||||
println!("{}: opening pts {}", DEV_NAME, name);
|
||||
self.tty = Some(pty);
|
||||
Ok(name)
|
||||
name
|
||||
}
|
||||
|
||||
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() {
|
||||
let tty = self.tty.as_mut().unwrap();
|
||||
let result = tty.read();
|
||||
if let Some(input) = result {
|
||||
self.input = input;
|
||||
self.set_rx_status(true);
|
||||
return Ok(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
false
|
||||
}
|
||||
|
||||
pub fn check_tx(&mut self) -> bool {
|
||||
|
@ -155,7 +162,7 @@ impl MC68681Port {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct MC68681 {
|
||||
pub struct MC68681<Address, Instant, Error> {
|
||||
frequency: Frequency,
|
||||
|
||||
acr: u8,
|
||||
|
@ -175,11 +182,15 @@ pub struct MC68681 {
|
|||
input_state: u8,
|
||||
output_conf: 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 {
|
||||
MC68681 {
|
||||
Self {
|
||||
frequency: Frequency::from_hz(3_686_400),
|
||||
|
||||
acr: 0,
|
||||
|
@ -199,29 +210,51 @@ impl Default for MC68681 {
|
|||
input_state: 0,
|
||||
output_conf: 0,
|
||||
output_state: 0,
|
||||
|
||||
address: PhantomData,
|
||||
instant: PhantomData,
|
||||
error: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MC68681 {
|
||||
fn set_interrupt_flag(&mut self, flag: u8, value: bool) {
|
||||
impl<Address, Instant, Error> MC68681<Address, Instant, Error> {
|
||||
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 });
|
||||
}
|
||||
|
||||
fn check_interrupt_state(&mut self, system: &System) -> Result<(), Error> {
|
||||
system
|
||||
.get_interrupt_controller()
|
||||
.set((self.int_status & self.int_mask) != 0, 4, self.int_vector)
|
||||
pub fn get_interrupt_flag(&mut self) -> (bool, u8, u8) {
|
||||
((self.int_status & self.int_mask) != 0, 4, self.int_vector)
|
||||
}
|
||||
}
|
||||
|
||||
impl Steppable for MC68681 {
|
||||
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
||||
if self.port_a.check_rx()? {
|
||||
impl<Address, Instant, Error, Bus> Step<Bus> for MC68681<Address, Instant, Error>
|
||||
where
|
||||
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);
|
||||
}
|
||||
|
||||
if self.port_b.check_rx()? {
|
||||
if self.port_b.check_rx() {
|
||||
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() {
|
||||
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);
|
||||
}
|
||||
|
||||
Ok(self.frequency.period_duration())
|
||||
Ok(now + Instant::hertz_to_duration(self.frequency.as_hz() as u64))
|
||||
}
|
||||
}
|
||||
|
||||
impl Addressable for MC68681 {
|
||||
fn size(&self) -> usize {
|
||||
0x30
|
||||
}
|
||||
impl<Address, Instant, Error> BusAccess<Address> for MC68681<Address, Instant, Error>
|
||||
where
|
||||
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 {
|
||||
REG_SRA_RD => data[0] = self.port_a.status,
|
||||
REG_RBA_RD => {
|
||||
|
@ -306,10 +347,13 @@ impl Addressable for MC68681 {
|
|||
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);
|
||||
match addr {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
@ -374,3 +483,4 @@ impl Transmutable for MC68681 {
|
|||
Some(self)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -7,6 +7,7 @@ edition = "2021"
|
|||
log = "0.4"
|
||||
femtos = "0.1"
|
||||
moa-core = { path = "../../core" }
|
||||
moa-system = { path = "../../libraries/system" }
|
||||
moa-host = { path = "../../libraries/host" }
|
||||
moa-m68k = { path = "../../cpus/m68k", features = ["moa"] }
|
||||
moa-peripherals-generic = { path = "../../peripherals/generic" }
|
||||
|
|
|
@ -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_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> {
|
||||
let mut system = System::default();
|
||||
|
||||
let mut rom = MemoryBlock::new(vec![0; 0x10000]);
|
||||
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]);
|
||||
ram.load_at(0, "binaries/computie/kernel.bin")?;
|
||||
system.add_addressable_device(0x00100000, Device::new(ram))?;
|
||||
ram.load_at(0, "binaries/computie/kernel.bin")
|
||||
.map_err(|err| Error::new(format!("{}", err)))?;
|
||||
system.add_addressable_device(0x00100000, ram.size(), Device::new(ram))?;
|
||||
|
||||
let mut ata = AtaDevice::default();
|
||||
ata.load("binaries/computie/disk-with-partition-table.img")?;
|
||||
system.add_addressable_device(0x00600000, Device::new(ata))?;
|
||||
ata.load("binaries/computie/disk-with-partition-table.img")
|
||||
.map_err(|err| Error::new(format!("{}", err)))?;
|
||||
system.add_addressable_device(0x00600000, ata.address_space(), Device::new(ata))?;
|
||||
|
||||
let mut serial = MC68681::default();
|
||||
launch_terminal_emulator(serial.port_a.connect(host.add_pty()?)?);
|
||||
launch_slip_connection(serial.port_b.connect(host.add_pty()?)?);
|
||||
system.add_addressable_device(0x00700000, Device::new(serial))?;
|
||||
launch_terminal_emulator(serial.port_a.connect(host.add_pty()?));
|
||||
launch_slip_connection(serial.port_b.connect(host.add_pty()?));
|
||||
system.add_addressable_device(0x00700000, serial.address_space(), Device::new(serial))?;
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -57,25 +112,27 @@ pub fn build_computie_k30<H: Host>(host: &H) -> Result<System, Error> {
|
|||
let mut system = System::default();
|
||||
|
||||
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]);
|
||||
ram.load_at(0, "binaries/computie/kernel-68030.bin")?;
|
||||
system.add_addressable_device(0x00100000, Device::new(ram))?;
|
||||
ram.load_at(0, "binaries/computie/kernel-68030.bin")
|
||||
.map_err(|err| Error::new(format!("{}", err)))?;
|
||||
system.add_addressable_device(0x00100000, ram.size(), Device::new(ram))?;
|
||||
|
||||
let mut ata = AtaDevice::default();
|
||||
ata.load("binaries/computie/disk-with-partition-table.img")?;
|
||||
system.add_addressable_device(0x00600000, Device::new(ata))?;
|
||||
ata.load("binaries/computie/disk-with-partition-table.img")
|
||||
.map_err(|err| Error::new(format!("{}", err)))?;
|
||||
system.add_addressable_device(0x00600000, ata.address_space(), Device::new(ata))?;
|
||||
|
||||
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()?)?);
|
||||
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));
|
||||
|
||||
system.add_interruptable_device("cpu", Device::new(cpu))?;
|
||||
system.add_interruptable_device("cpu", Device::new(MoaM68k::new(cpu)))?;
|
||||
|
||||
Ok(system)
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@ use std::mem;
|
|||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use femtos::Frequency;
|
||||
use femtos::{Instant, Frequency};
|
||||
|
||||
use moa_core::{System, Error, MemoryBlock, Bus, Address, Addressable, Device};
|
||||
use moa_host::Host;
|
||||
|
||||
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::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(0x7f11, coproc_sn_sound.clone());
|
||||
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 mut reset = coproc.reset.clone();
|
||||
let mut bus_request = coproc.bus_request.clone();
|
||||
let coproc = Z80::from_type(Z80Type::Z80, Frequency::from_hz(3_579_545));
|
||||
let coproc = MoaZ80 {
|
||||
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);
|
||||
bus_request.set(true);
|
||||
let coproc = Device::new(coproc);
|
||||
|
||||
// Add coprocessor devices to the system bus so the 68000 can access them too
|
||||
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(0x00c00010, coproc_sn_sound)?;
|
||||
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)?;
|
||||
|
|
|
@ -3,7 +3,7 @@ use femtos::Frequency;
|
|||
use moa_core::{System, Error, MemoryBlock, Device};
|
||||
use moa_host::Host;
|
||||
|
||||
use moa_z80::{Z80, Z80Type};
|
||||
use moa_z80::{MoaZ80, Z80, Z80Type};
|
||||
|
||||
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();
|
||||
|
||||
// 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))?;
|
||||
|
||||
|
|
|
@ -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!
|
||||
01.json completed, all passed!
|
||||
|
@ -614,7 +614,7 @@ dd 82.json completed, all passed!
|
|||
dd 83.json completed, all passed!
|
||||
dd 84.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 88.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 8c.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 90.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 94.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 98.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 9c.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 a0.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 a4.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 a8.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 ac.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 b0.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 b4.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 b8.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 bc.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 c0.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 61.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 65.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 69.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 6d.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 75.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 79.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 7d.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 a1.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 84.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 88.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 8c.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 90.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 94.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 98.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 9c.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 a0.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 a4.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 a8.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 ac.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 b0.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 b4.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 b8.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 bc.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 c0.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!
|
||||
ff.json completed, all passed!
|
||||
|
||||
passed: 1584670, failed: 25330, total 98%
|
||||
completed in 0m 8s
|
||||
passed: 1564670, failed: 45330, total 97%
|
||||
completed in 0m 9s
|
||||
|
|
|
@ -46,6 +46,9 @@ struct Args {
|
|||
/// Check instruction timings
|
||||
#[clap(short = 't', long)]
|
||||
check_timings: bool,
|
||||
/// Don't check I/O instructions
|
||||
#[clap(short = 'i', long)]
|
||||
no_check_io: bool,
|
||||
/// Directory to the test suite to run
|
||||
#[clap(long, default_value = DEFAULT_RAD_TESTS)]
|
||||
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
|
||||
let len = 0x1_0000;
|
||||
let mut data = Vec::with_capacity(len);
|
||||
|
@ -220,7 +227,8 @@ fn load_state(
|
|||
|
||||
// Load data bytes into memory
|
||||
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)))?;
|
||||
}
|
||||
|
||||
|
@ -279,17 +287,19 @@ fn assert_state(
|
|||
assert_value(cpu.state.iff1 as u8, expected.iff1, "iff1")?;
|
||||
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() {
|
||||
let actual = memory.read_u8(Instant::START, *addr)
|
||||
let actual = memory
|
||||
.read_u8(Instant::START, *addr)
|
||||
.map_err(|err| Error::Bus(format!("{:?}", err)))?;
|
||||
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() {
|
||||
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)))?;
|
||||
assert_value(actual, port.value, &format!("port value at {:x}", port.addr))?;
|
||||
}
|
||||
|
@ -306,20 +316,20 @@ fn step_cpu_and_assert(
|
|||
args: &Args,
|
||||
) -> Result<(), Error> {
|
||||
//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)))?;
|
||||
|
||||
assert_state(cpu, memory, io, &case.final_state, args.check_extra_flags, &case.ports)?;
|
||||
if args.check_timings {
|
||||
// TODO re-enable. not sure why it can't divide here
|
||||
//let cycles = clock_elapsed / cpu.frequency.period_duration();
|
||||
//if cycles != case.cycles.len() {
|
||||
// return Err(Error::Assertion(format!(
|
||||
// "expected instruction to take {} cycles, but took {}",
|
||||
// case.cycles.len(),
|
||||
// cycles
|
||||
// )));
|
||||
//}
|
||||
let cycles = clock_elapsed.as_duration() / cpu.frequency.period_duration();
|
||||
if cycles != case.cycles.len() as u64 {
|
||||
return Err(Error::Assertion(format!(
|
||||
"expected instruction to take {} cycles, but took {}",
|
||||
case.cycles.len(),
|
||||
cycles
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -338,8 +348,8 @@ fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> {
|
|||
if args.debug {
|
||||
case.dump();
|
||||
println!();
|
||||
initial_cpu.dump_state(Instant::START);
|
||||
cpu.dump_state(Instant::START);
|
||||
initial_cpu.dump_state(Instant::START, &mut memory);
|
||||
cpu.dump_state(Instant::START, &mut memory);
|
||||
}
|
||||
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
|
||||
if args.debug {
|
||||
case.initial_state.ram.sort_by_key(|(addr, _)| *addr);
|
||||
|
|
18
todo.txt
18
todo.txt
|
@ -1,14 +1,20 @@
|
|||
|
||||
* decide if you should continue expecting Instant to usable through the trait alone, despite issues
|
||||
* 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
|
||||
* 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 Z80 reset and bus_request signals
|
||||
* the emulator_hal_memory should throw an error when an access will straddle the end of memory? Or should it autowrap?
|
||||
* 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
|
||||
* 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 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
|
||||
* remove the custom moa impls from m68k if possible at this point
|
||||
* publish the emulator-hal crate
|
||||
|
@ -79,7 +85,7 @@
|
|||
* add doc strings everywhere
|
||||
* 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?
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue