mirror of
https://github.com/transistorfet/moa.git
synced 2024-12-24 09:30:31 +00:00
Modified to use a nanosecond clock
This commit is contained in:
parent
2ed528a140
commit
fd894f0638
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
use crate::system::System;
|
use crate::system::System;
|
||||||
use crate::error::{ErrorType, Error};
|
use crate::error::{ErrorType, Error};
|
||||||
use crate::devices::{Clock, Address, Steppable, Interruptable, Addressable, Transmutable};
|
use crate::devices::{ClockElapsed, Address, Steppable, Interruptable, Addressable, Transmutable};
|
||||||
|
|
||||||
use super::instructions::{
|
use super::instructions::{
|
||||||
Register,
|
Register,
|
||||||
@ -27,9 +27,9 @@ use super::state::{M68k, M68kType, Status, Flags, Exceptions, InterruptPriority}
|
|||||||
|
|
||||||
|
|
||||||
impl Steppable for M68k {
|
impl Steppable for M68k {
|
||||||
fn step(&mut self, system: &System) -> Result<Clock, Error> {
|
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
||||||
self.step_internal(system)?;
|
self.step_internal(system)?;
|
||||||
Ok(1)
|
Ok(1_000_000_000 / 8_000_000)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_error(&mut self, system: &System) {
|
fn on_error(&mut self, system: &System) {
|
||||||
@ -86,7 +86,7 @@ impl M68k {
|
|||||||
//Err(Error { err: ErrorType::Processor, native, .. }) => {
|
//Err(Error { err: ErrorType::Processor, native, .. }) => {
|
||||||
// TODO temporary: we are passing illegal instructions upward in order to fix them
|
// TODO temporary: we are passing illegal instructions upward in order to fix them
|
||||||
Err(Error { err: ErrorType::Processor, native, .. }) if native != Exceptions::IllegalInstruction as u32 => {
|
Err(Error { err: ErrorType::Processor, native, .. }) if native != Exceptions::IllegalInstruction as u32 => {
|
||||||
self.exception(system, native as u8)?;
|
self.exception(system, native as u8, false)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
@ -126,7 +126,7 @@ impl M68k {
|
|||||||
if (pending_ipl >= priority_mask || pending_ipl == 7) && pending_ipl >= current_ipl {
|
if (pending_ipl >= priority_mask || pending_ipl == 7) && pending_ipl >= current_ipl {
|
||||||
debug!("{} interrupt: {} {}", DEV_NAME, pending_ipl, priority_mask);
|
debug!("{} interrupt: {} {}", DEV_NAME, pending_ipl, priority_mask);
|
||||||
self.state.current_ipl = self.state.pending_ipl;
|
self.state.current_ipl = self.state.pending_ipl;
|
||||||
self.exception(system, self.state.ipl_ack_num)?;
|
self.exception(system, self.state.ipl_ack_num, true)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,7 +138,7 @@ impl M68k {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exception(&mut self, system: &System, number: u8) -> Result<(), Error> {
|
pub fn exception(&mut self, system: &System, number: u8, update_ipl: bool) -> Result<(), Error> {
|
||||||
debug!("{}: raising exception {}", DEV_NAME, number);
|
debug!("{}: raising exception {}", DEV_NAME, number);
|
||||||
let offset = (number as u16) << 2;
|
let offset = (number as u16) << 2;
|
||||||
self.push_word(system, offset)?;
|
self.push_word(system, offset)?;
|
||||||
@ -146,6 +146,9 @@ impl M68k {
|
|||||||
self.push_word(system, self.state.sr)?;
|
self.push_word(system, self.state.sr)?;
|
||||||
self.set_flag(Flags::Supervisor, true);
|
self.set_flag(Flags::Supervisor, true);
|
||||||
self.set_flag(Flags::Tracing, false);
|
self.set_flag(Flags::Tracing, false);
|
||||||
|
if update_ipl {
|
||||||
|
self.state.sr = (self.state.sr & !(Flags::IntMask as u16)) | ((self.state.current_ipl as u16) << 8);
|
||||||
|
}
|
||||||
self.state.pc = system.get_bus().read_beu32((self.state.vbr + offset as u32) as Address)?;
|
self.state.pc = system.get_bus().read_beu32((self.state.vbr + offset as u32) as Address)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -343,7 +346,7 @@ impl M68k {
|
|||||||
Instruction::DIVW(src, dest, sign) => {
|
Instruction::DIVW(src, dest, sign) => {
|
||||||
let value = self.get_target_value(system, src, Size::Word)?;
|
let value = self.get_target_value(system, src, Size::Word)?;
|
||||||
if value == 0 {
|
if value == 0 {
|
||||||
self.exception(system, Exceptions::ZeroDivide as u8)?;
|
self.exception(system, Exceptions::ZeroDivide as u8, false)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,7 +365,7 @@ impl M68k {
|
|||||||
Instruction::DIVL(src, dest_h, dest_l, sign) => {
|
Instruction::DIVL(src, dest_h, dest_l, sign) => {
|
||||||
let value = self.get_target_value(system, src, Size::Long)?;
|
let value = self.get_target_value(system, src, Size::Long)?;
|
||||||
if value == 0 {
|
if value == 0 {
|
||||||
self.exception(system, Exceptions::ZeroDivide as u8)?;
|
self.exception(system, Exceptions::ZeroDivide as u8, false)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -641,11 +644,11 @@ impl M68k {
|
|||||||
self.set_logic_flags(value, size);
|
self.set_logic_flags(value, size);
|
||||||
},
|
},
|
||||||
Instruction::TRAP(number) => {
|
Instruction::TRAP(number) => {
|
||||||
self.exception(system, 32 + number)?;
|
self.exception(system, 32 + number, false)?;
|
||||||
},
|
},
|
||||||
Instruction::TRAPV => {
|
Instruction::TRAPV => {
|
||||||
if self.get_flag(Flags::Overflow) {
|
if self.get_flag(Flags::Overflow) {
|
||||||
self.exception(system, Exceptions::TrapvInstruction as u8)?;
|
self.exception(system, Exceptions::TrapvInstruction as u8, false)?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::UNLK(reg) => {
|
Instruction::UNLK(reg) => {
|
||||||
|
@ -8,7 +8,13 @@ use crate::system::System;
|
|||||||
|
|
||||||
pub const MAX_READ: usize = 4;
|
pub const MAX_READ: usize = 4;
|
||||||
|
|
||||||
|
/// The time in nanoseconds that have elapsed since the start of the simulation
|
||||||
pub type Clock = u64;
|
pub type Clock = u64;
|
||||||
|
|
||||||
|
/// The time in nanoseconds until the `step()` method should be called again
|
||||||
|
pub type ClockElapsed = u64;
|
||||||
|
|
||||||
|
/// A universal memory address used by the Addressable trait
|
||||||
pub type Address = u64;
|
pub type Address = u64;
|
||||||
|
|
||||||
|
|
||||||
@ -17,7 +23,7 @@ pub type Address = u64;
|
|||||||
/// with any device, the `on_error()` method will be called to display any state
|
/// with any device, the `on_error()` method will be called to display any state
|
||||||
/// information that might be helpful for debugging.
|
/// information that might be helpful for debugging.
|
||||||
pub trait Steppable {
|
pub trait Steppable {
|
||||||
fn step(&mut self, system: &System) -> Result<Clock, Error>;
|
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error>;
|
||||||
fn on_error(&mut self, _system: &System) { }
|
fn on_error(&mut self, _system: &System) { }
|
||||||
fn on_debug(&mut self) { }
|
fn on_debug(&mut self) { }
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::system::System;
|
use crate::system::System;
|
||||||
use crate::devices::{Clock, Address, Steppable, Addressable, Transmutable, MAX_READ};
|
use crate::devices::{ClockElapsed, Address, Steppable, Addressable, Transmutable, MAX_READ};
|
||||||
|
|
||||||
use crate::host::traits::Tty;
|
use crate::host::traits::Tty;
|
||||||
|
|
||||||
@ -206,7 +206,17 @@ impl MC68681 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step_internal(&mut self, system: &System) -> Result<(), Error> {
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Steppable for MC68681 {
|
||||||
|
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
||||||
if self.port_a.check_rx()? {
|
if self.port_a.check_rx()? {
|
||||||
self.set_interrupt_flag(ISR_CH_A_RX_READY_FULL, true);
|
self.set_interrupt_flag(ISR_CH_A_RX_READY_FULL, true);
|
||||||
}
|
}
|
||||||
@ -242,15 +252,7 @@ impl MC68681 {
|
|||||||
self.set_interrupt_flag(ISR_CH_B_TX_READY, true);
|
self.set_interrupt_flag(ISR_CH_B_TX_READY, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(1_000_000_000 / 3_646_800)
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,13 +373,6 @@ impl Addressable for MC68681 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steppable for MC68681 {
|
|
||||||
fn step(&mut self, system: &System) -> Result<Clock, Error> {
|
|
||||||
self.step_internal(system)?;
|
|
||||||
Ok(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Transmutable for MC68681 {
|
impl Transmutable for MC68681 {
|
||||||
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
|
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
|
||||||
Some(self)
|
Some(self)
|
||||||
|
100
src/system.rs
100
src/system.rs
@ -1,15 +1,18 @@
|
|||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::cell::{RefCell, RefMut};
|
use std::cell::{RefCell, RefMut};
|
||||||
|
use std::collections::BinaryHeap;
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::memory::Bus;
|
use crate::memory::Bus;
|
||||||
use crate::interrupts::InterruptController;
|
use crate::interrupts::InterruptController;
|
||||||
use crate::devices::{Clock, Address, TransmutableBox};
|
use crate::devices::{Clock, ClockElapsed, Address, TransmutableBox};
|
||||||
|
|
||||||
|
|
||||||
pub struct System {
|
pub struct System {
|
||||||
pub clock: Clock,
|
pub clock: Clock,
|
||||||
pub devices: Vec<TransmutableBox>,
|
pub devices: Vec<TransmutableBox>,
|
||||||
|
pub event_queue: BinaryHeap<EventDevice>,
|
||||||
pub bus: RefCell<Bus>,
|
pub bus: RefCell<Bus>,
|
||||||
pub interrupt_controller: RefCell<InterruptController>,
|
pub interrupt_controller: RefCell<InterruptController>,
|
||||||
}
|
}
|
||||||
@ -19,6 +22,7 @@ impl System {
|
|||||||
System {
|
System {
|
||||||
clock: 0,
|
clock: 0,
|
||||||
devices: vec![],
|
devices: vec![],
|
||||||
|
event_queue: BinaryHeap::new(),
|
||||||
bus: RefCell::new(Bus::new()),
|
bus: RefCell::new(Bus::new()),
|
||||||
interrupt_controller: RefCell::new(InterruptController::new()),
|
interrupt_controller: RefCell::new(InterruptController::new()),
|
||||||
}
|
}
|
||||||
@ -32,31 +36,57 @@ impl System {
|
|||||||
self.interrupt_controller.borrow_mut()
|
self.interrupt_controller.borrow_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn add_addressable_device(&mut self, addr: Address, device: TransmutableBox) -> Result<(), Error> {
|
pub fn add_addressable_device(&mut self, addr: Address, device: TransmutableBox) -> Result<(), Error> {
|
||||||
let length = device.borrow_mut().as_addressable().unwrap().len();
|
let length = device.borrow_mut().as_addressable().unwrap().len();
|
||||||
self.bus.borrow_mut().insert(addr, length, device.clone());
|
self.bus.borrow_mut().insert(addr, length, device.clone());
|
||||||
|
self.insert_steppable_device(device.clone());
|
||||||
self.devices.push(device);
|
self.devices.push(device);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_interruptable_device(&mut self, device: TransmutableBox) -> Result<(), Error> {
|
pub fn add_interruptable_device(&mut self, device: TransmutableBox) -> Result<(), Error> {
|
||||||
self.interrupt_controller.borrow_mut().set_target(device.clone())?;
|
self.interrupt_controller.borrow_mut().set_target(device.clone())?;
|
||||||
|
self.insert_steppable_device(device.clone());
|
||||||
self.devices.push(device);
|
self.devices.push(device);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn insert_steppable_device(&mut self, device: TransmutableBox) -> Result<(), Error> {
|
||||||
|
if device.borrow_mut().as_steppable().is_some() {
|
||||||
|
let event_device = EventDevice::new(device);
|
||||||
|
self.event_queue.push(event_device);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn step(&mut self) -> Result<(), Error> {
|
pub fn step(&mut self) -> Result<(), Error> {
|
||||||
self.clock += 1;
|
let mut event_device = self.event_queue.pop().unwrap();
|
||||||
for dev in &self.devices {
|
self.clock = event_device.next_clock;
|
||||||
match dev.borrow_mut().as_steppable() {
|
event_device.next_clock = self.clock + event_device.device.borrow_mut().as_steppable().unwrap().step(&self)?;
|
||||||
Some(dev) => { dev.step(&self)?; },
|
self.event_queue.push(event_device);
|
||||||
None => { },
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_for(&mut self, clocks: Clock) -> Result<(), Error> {
|
||||||
|
let target = self.clock + clocks;
|
||||||
|
|
||||||
|
while self.clock < target {
|
||||||
|
match self.step() {
|
||||||
|
Ok(()) => { }
|
||||||
|
Err(err) => {
|
||||||
|
self.exit_error();
|
||||||
|
println!("{:?}", err);
|
||||||
|
return Err(err);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn run_loop(&mut self) {
|
||||||
|
self.run_for(u64::MAX);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn exit_error(&mut self) {
|
pub fn exit_error(&mut self) {
|
||||||
for dev in &self.devices {
|
for dev in &self.devices {
|
||||||
match dev.borrow_mut().as_steppable() {
|
match dev.borrow_mut().as_steppable() {
|
||||||
@ -75,33 +105,41 @@ impl System {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run_loop(&mut self) {
|
|
||||||
loop {
|
|
||||||
match self.step() {
|
|
||||||
Ok(()) => { },
|
|
||||||
Err(err) => {
|
|
||||||
self.exit_error();
|
|
||||||
println!("{:?}", err);
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_for(&mut self, clocks: Clock) -> Result<(), Error> {
|
pub struct EventDevice {
|
||||||
let target = self.clock + clocks;
|
pub next_clock: Clock,
|
||||||
while self.clock < target {
|
pub device: TransmutableBox,
|
||||||
match self.step() {
|
}
|
||||||
Ok(()) => { },
|
|
||||||
Err(err) => {
|
impl EventDevice {
|
||||||
self.exit_error();
|
pub fn new(device: TransmutableBox) -> Self {
|
||||||
println!("{:?}", err);
|
Self {
|
||||||
return Err(err);
|
next_clock: 0,
|
||||||
},
|
device,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for EventDevice {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
// NOTE this is reversed so that an event with a lower clock will be higher
|
||||||
|
other.next_clock.cmp(&self.next_clock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for EventDevice {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for EventDevice {}
|
||||||
|
|
||||||
|
impl PartialEq for EventDevice {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.next_clock == other.next_clock
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
14
todo.txt
14
todo.txt
@ -1,19 +1,8 @@
|
|||||||
|
|
||||||
|
* should you simulate clock ticks, and only step devices when they next request it
|
||||||
|
|
||||||
* generics for the frontend that's passed to the builder functions is much better than a trait object
|
|
||||||
* you could possibly use a backend representation that is given to the frontened, rather than vice versa
|
|
||||||
* you could use callbacks in some way (ie. callbacks from the gui loop to the system)
|
|
||||||
|
|
||||||
|
|
||||||
* should the frontend and simulator parts be separated so that the simulator part can be a library package?
|
|
||||||
* it should be call something other than canvas because it'll have input as well
|
|
||||||
* how will you get the canvas/app shared object between the sim thread and the io thread? It kind of has to be passed through the system-creation
|
|
||||||
functions (an only if they need them), which raises questions about how to configure things. It'd be nice if you could still run the same hardware
|
|
||||||
simulated without using a frontend
|
|
||||||
|
|
||||||
* can you eventually make the system connections all configurable via a config file?
|
* can you eventually make the system connections all configurable via a config file?
|
||||||
* test using mpsc to pass messages with the tty IO thread, and test if it's slower than what you have now
|
|
||||||
|
|
||||||
* make it possible to break out of the current execution, into the debugger, by using a certain keystroke
|
* make it possible to break out of the current execution, into the debugger, by using a certain keystroke
|
||||||
|
|
||||||
* make tests for each instruction
|
* make tests for each instruction
|
||||||
@ -28,7 +17,6 @@
|
|||||||
* Coprocessor instructions: cpBcc, cpDBcc, cpGEN, cpScc, cpTRAPcc
|
* Coprocessor instructions: cpBcc, cpDBcc, cpGEN, cpScc, cpTRAPcc
|
||||||
|
|
||||||
|
|
||||||
* should you simulate clock ticks, and only step devices when they next request it
|
|
||||||
* how can you have multple CPUs
|
* how can you have multple CPUs
|
||||||
* should you simulate bus arbitration?
|
* should you simulate bus arbitration?
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user