mirror of
https://github.com/transistorfet/moa.git
synced 2024-09-29 15:56:19 +00:00
278 lines
6.7 KiB
Rust
278 lines
6.7 KiB
Rust
|
|
use std::rc::Rc;
|
|
use std::cell::RefCell;
|
|
use std::fmt::Display;
|
|
use femtos::{Instant, Frequency};
|
|
|
|
use moa_core::{Address, Bus, BusPort};
|
|
|
|
use crate::debugger::M68kDebugger;
|
|
use crate::memory::M68kBusPort;
|
|
use crate::instructions::Target;
|
|
use crate::execute::M68kCycle;
|
|
|
|
|
|
pub type ClockCycles = u16;
|
|
|
|
|
|
#[allow(dead_code)]
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
#[repr(u8)]
|
|
pub enum AddressWidth {
|
|
A32 = 32, // MC68020+
|
|
A24 = 24, // MC68000 64-Pin, MC68010
|
|
A22 = 22, // MC68008 52-Pin
|
|
A20 = 20, // MC68008 48-Pin
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
#[repr(u8)]
|
|
pub enum DataWidth {
|
|
D32 = 32,
|
|
D16 = 16,
|
|
D8 = 8,
|
|
}
|
|
|
|
/// The instruction set of the chip
|
|
#[allow(dead_code)]
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub enum CoreType {
|
|
MC68000,
|
|
MC68010,
|
|
MC68020,
|
|
MC68030,
|
|
}
|
|
|
|
/// Complete collection of information about the CPU being simulated
|
|
#[allow(dead_code)]
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub struct CpuInfo {
|
|
pub chip: M68kType,
|
|
pub core_type: CoreType,
|
|
pub address_width: AddressWidth,
|
|
pub data_width: DataWidth,
|
|
pub frequency: Frequency,
|
|
}
|
|
|
|
/// The variant of the 68k family of CPUs that is being emulated
|
|
///
|
|
/// This can be used as a shorthand for creating a CpuInfo that
|
|
/// can be used by the simuation code to determine behaviour
|
|
#[allow(dead_code)]
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub enum M68kType {
|
|
MC68000,
|
|
MC68008,
|
|
MC68010,
|
|
MC68020,
|
|
MC68030,
|
|
}
|
|
|
|
impl From<M68kType> for CoreType {
|
|
fn from(cputype: M68kType) -> Self {
|
|
match cputype {
|
|
M68kType::MC68000 => CoreType::MC68000,
|
|
M68kType::MC68008 => CoreType::MC68000,
|
|
M68kType::MC68010 => CoreType::MC68010,
|
|
M68kType::MC68020 => CoreType::MC68020,
|
|
M68kType::MC68030 => CoreType::MC68030,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CpuInfo {
|
|
fn from(cputype: M68kType, frequency: Frequency) -> Self {
|
|
match cputype {
|
|
M68kType::MC68008 => Self {
|
|
chip: cputype,
|
|
core_type: cputype.into(),
|
|
address_width: AddressWidth::A22,
|
|
data_width: DataWidth::D8,
|
|
frequency,
|
|
},
|
|
M68kType::MC68000 | M68kType::MC68010 => Self {
|
|
chip: cputype,
|
|
core_type: cputype.into(),
|
|
address_width: AddressWidth::A24,
|
|
data_width: DataWidth::D16,
|
|
frequency,
|
|
},
|
|
M68kType::MC68020 | M68kType::MC68030 => Self {
|
|
chip: cputype,
|
|
core_type: cputype.into(),
|
|
address_width: AddressWidth::A32,
|
|
data_width: DataWidth::D32,
|
|
frequency,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const FLAGS_ON_RESET: u16 = 0x2700;
|
|
|
|
#[repr(u16)]
|
|
#[allow(dead_code)]
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
pub enum Flags {
|
|
Carry = 0x0001,
|
|
Overflow = 0x0002,
|
|
Zero = 0x0004,
|
|
Negative = 0x0008,
|
|
Extend = 0x0010,
|
|
IntMask = 0x0700,
|
|
Interrupt = 0x1000,
|
|
Supervisor = 0x2000,
|
|
Tracing = 0x8000,
|
|
}
|
|
|
|
#[repr(u8)]
|
|
#[allow(dead_code)]
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
pub enum Exceptions {
|
|
BusError = 2,
|
|
AddressError = 3,
|
|
IllegalInstruction = 4,
|
|
ZeroDivide = 5,
|
|
ChkInstruction = 6,
|
|
TrapvInstruction = 7,
|
|
PrivilegeViolation = 8,
|
|
Trace = 9,
|
|
LineAEmulator = 10,
|
|
LineFEmulator = 11,
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
pub enum Status {
|
|
Init,
|
|
Running,
|
|
Stopped,
|
|
}
|
|
|
|
#[repr(u8)]
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
pub enum InterruptPriority {
|
|
NoInterrupt = 0,
|
|
Level1 = 1,
|
|
Level2 = 2,
|
|
Level3 = 3,
|
|
Level4 = 4,
|
|
Level5 = 5,
|
|
Level6 = 6,
|
|
Level7 = 7,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct M68kState {
|
|
pub status: Status,
|
|
pub current_ipl: InterruptPriority,
|
|
pub pending_ipl: InterruptPriority,
|
|
|
|
pub pc: u32,
|
|
pub sr: u16,
|
|
pub d_reg: [u32; 8],
|
|
pub a_reg: [u32; 7],
|
|
pub ssp: u32,
|
|
pub usp: u32,
|
|
|
|
pub vbr: u32,
|
|
}
|
|
|
|
#[derive(Clone, Debug, thiserror::Error)]
|
|
pub enum M68kError<BusError> {
|
|
#[error("cpu halted")]
|
|
Halted,
|
|
#[error("processor exception {0:?}")]
|
|
Exception(Exceptions),
|
|
#[error("interrupt vector {0} occurred")]
|
|
Interrupt(u8),
|
|
#[error("breakpoint reached")]
|
|
Breakpoint,
|
|
#[error("invalid instruction target, direct value used as a pointer: {0:?}")]
|
|
InvalidTarget(Target),
|
|
#[error("bus error")]
|
|
BusError(BusError),
|
|
#[error("error: {0}")]
|
|
Other(String),
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct M68kStatistics {
|
|
pub cycle_number: usize,
|
|
pub last_update: usize,
|
|
pub last_time: std::time::SystemTime,
|
|
}
|
|
|
|
impl Default for M68kStatistics {
|
|
fn default() -> Self {
|
|
Self {
|
|
cycle_number: 0,
|
|
last_update: 0,
|
|
last_time: std::time::SystemTime::now(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct M68k {
|
|
pub info: CpuInfo,
|
|
pub state: M68kState,
|
|
pub debugger: M68kDebugger,
|
|
pub port: BusPort,
|
|
pub stats: M68kStatistics,
|
|
pub cycle: Option<M68kCycle>,
|
|
}
|
|
|
|
impl Default for M68kState {
|
|
fn default() -> M68kState {
|
|
M68kState {
|
|
status: Status::Init,
|
|
current_ipl: InterruptPriority::NoInterrupt,
|
|
pending_ipl: InterruptPriority::NoInterrupt,
|
|
|
|
pc: 0,
|
|
sr: FLAGS_ON_RESET,
|
|
d_reg: [0; 8],
|
|
a_reg: [0; 7],
|
|
ssp: 0,
|
|
usp: 0,
|
|
|
|
vbr: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl M68k {
|
|
pub fn new(info: CpuInfo, port: BusPort) -> M68k {
|
|
M68k {
|
|
info,
|
|
state: M68kState::default(),
|
|
debugger: M68kDebugger::default(),
|
|
port,
|
|
stats: Default::default(),
|
|
cycle: None,
|
|
}
|
|
}
|
|
|
|
pub fn from_type(cputype: M68kType, frequency: Frequency, bus: Rc<RefCell<Bus>>, addr_offset: Address) -> Self {
|
|
let info = CpuInfo::from(cputype, frequency);
|
|
Self::new(info, BusPort::new(addr_offset, info.address_width as u8, info.data_width as u8, bus))
|
|
}
|
|
}
|
|
|
|
impl InterruptPriority {
|
|
pub fn from_u8(priority: u8) -> InterruptPriority {
|
|
match priority {
|
|
0 => InterruptPriority::NoInterrupt,
|
|
1 => InterruptPriority::Level1,
|
|
2 => InterruptPriority::Level2,
|
|
3 => InterruptPriority::Level3,
|
|
4 => InterruptPriority::Level4,
|
|
5 => InterruptPriority::Level5,
|
|
6 => InterruptPriority::Level6,
|
|
_ => InterruptPriority::Level7,
|
|
}
|
|
}
|
|
}
|
|
|