Introduce janky trace log debugging
New functions trace_on and trace_off
This commit is contained in:
parent
c9161324b5
commit
e8f8584e64
|
@ -1,2 +1,2 @@
|
||||||
max_width = 132
|
max_width = 100
|
||||||
use_small_heuristics = "Off"
|
use_small_heuristics = "Off"
|
31
src/bus.rs
31
src/bus.rs
|
@ -4,8 +4,9 @@ use crate::duart::Duart;
|
||||||
use crate::err::BusError;
|
use crate::err::BusError;
|
||||||
use crate::mem::Mem;
|
use crate::mem::Mem;
|
||||||
use crate::mouse::Mouse;
|
use crate::mouse::Mouse;
|
||||||
use std::fmt::Debug;
|
use std::io::Write;
|
||||||
use std::ops::Range;
|
use std::{fmt, fs::File, ops::Range};
|
||||||
|
use std::{fmt::Debug, fs::OpenOptions};
|
||||||
|
|
||||||
const NVRAM_SIZE: usize = 8192;
|
const NVRAM_SIZE: usize = 8192;
|
||||||
|
|
||||||
|
@ -62,6 +63,7 @@ pub struct Bus {
|
||||||
vid: Mem, // TODO: Figure out what device this really is
|
vid: Mem, // TODO: Figure out what device this really is
|
||||||
bbram: Mem, // TODO: change to BBRAM when implemented
|
bbram: Mem, // TODO: change to BBRAM when implemented
|
||||||
ram: Mem,
|
ram: Mem,
|
||||||
|
trace_log: Option<File>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bus {
|
impl Bus {
|
||||||
|
@ -73,6 +75,7 @@ impl Bus {
|
||||||
vid: Mem::new(0x500000, 0x2, false),
|
vid: Mem::new(0x500000, 0x2, false),
|
||||||
bbram: Mem::new(0x600000, 0x2000, false),
|
bbram: Mem::new(0x600000, 0x2000, false),
|
||||||
ram: Mem::new(0x700000, mem_size, false),
|
ram: Mem::new(0x700000, mem_size, false),
|
||||||
|
trace_log: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +107,30 @@ impl Bus {
|
||||||
Err(BusError::NoDevice(address as u32))
|
Err(BusError::NoDevice(address as u32))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn trace_on(&mut self, name: &str) -> std::io::Result<()> {
|
||||||
|
let mut out = OpenOptions::new().write(true).open(name)?;
|
||||||
|
write!(out, "TRACE START")?;
|
||||||
|
self.trace_log = Some(out);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trace_enabled(&self) -> bool {
|
||||||
|
self.trace_log.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trace_off(&mut self) {
|
||||||
|
self.trace_log = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trace<T>(&mut self, step: u64, object: &T)
|
||||||
|
where
|
||||||
|
T: fmt::Display,
|
||||||
|
{
|
||||||
|
if let Some(trace_log) = &mut self.trace_log {
|
||||||
|
let _ = writeln!(trace_log, "{:08}: {}", step, object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_byte(&mut self, address: usize, access: AccessCode) -> Result<u8, BusError> {
|
pub fn read_byte(&mut self, address: usize, access: AccessCode) -> Result<u8, BusError> {
|
||||||
self.get_device(address)?.read_byte(address, access)
|
self.get_device(address)?.read_byte(address, access)
|
||||||
}
|
}
|
||||||
|
|
537
src/cpu.rs
537
src/cpu.rs
|
@ -3,6 +3,7 @@
|
||||||
use crate::bus::{AccessCode, Bus};
|
use crate::bus::{AccessCode, Bus};
|
||||||
use crate::err::*;
|
use crate::err::*;
|
||||||
use crate::instr::*;
|
use crate::instr::*;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// PSW Flags and Offsets
|
/// PSW Flags and Offsets
|
||||||
|
@ -39,8 +40,9 @@ const R_ISP: usize = 14;
|
||||||
const R_PC: usize = 15;
|
const R_PC: usize = 15;
|
||||||
|
|
||||||
const IPL_TABLE: [u32; 64] = [
|
const IPL_TABLE: [u32; 64] = [
|
||||||
0, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
0, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||||
|
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||||
];
|
];
|
||||||
|
|
||||||
const WE32100_VERSION: u32 = 0x1a;
|
const WE32100_VERSION: u32 = 0x1a;
|
||||||
|
@ -169,9 +171,30 @@ pub struct Instruction {
|
||||||
pub operands: [Operand; 4],
|
pub operands: [Operand; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instruction {
|
impl fmt::Display for Operand {
|
||||||
pub fn decode(&self) -> String {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
format!("{}\t0x{:x}", self.name, 1000)
|
match self.data_type {
|
||||||
|
Data::None => Ok(()),
|
||||||
|
Data::Byte | Data::SByte => {
|
||||||
|
write!(f, "0x{:02x}", self.data as u8)
|
||||||
|
}
|
||||||
|
Data::Half | Data::UHalf => {
|
||||||
|
write!(f, "0x{:04x}", self.data as u16)
|
||||||
|
}
|
||||||
|
Data::Word | Data::UWord => {
|
||||||
|
write!(f, "0x{:08x}", self.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Instruction {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{:5} {}{}{}{}",
|
||||||
|
self.name, self.operands[0], self.operands[1], self.operands[2], self.operands[3]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,17 +481,62 @@ static BYTE_MNEMONICS: [Option<Mnemonic>; 256] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
static HALFWORD_MNEMONICS: [Option<Mnemonic>; HALFWORD_MNEMONIC_COUNT] = [
|
static HALFWORD_MNEMONICS: [Option<Mnemonic>; HALFWORD_MNEMONIC_COUNT] = [
|
||||||
Some(mn!(0x3009, Data::None, "MVERNO", [OpType::None, OpType::None, OpType::None, OpType::None])),
|
Some(mn!(
|
||||||
Some(mn!(0x300d, Data::None, "ENBVJMP", [OpType::None, OpType::None, OpType::None, OpType::None])),
|
0x3009,
|
||||||
Some(mn!(0x3013, Data::None, "DISVJMP", [OpType::None, OpType::None, OpType::None, OpType::None])),
|
Data::None,
|
||||||
Some(mn!(0x3019, Data::None, "MOVBLW", [OpType::None, OpType::None, OpType::None, OpType::None])),
|
"MVERNO",
|
||||||
Some(mn!(0x301f, Data::None, "STREND", [OpType::None, OpType::None, OpType::None, OpType::None])),
|
[OpType::None, OpType::None, OpType::None, OpType::None]
|
||||||
Some(mn!(0x302f, Data::None, "INTACK", [OpType::None, OpType::None, OpType::None, OpType::None])),
|
)),
|
||||||
Some(mn!(0x303f, Data::None, "STRCPY", [OpType::None, OpType::None, OpType::None, OpType::None])),
|
Some(mn!(
|
||||||
|
0x300d,
|
||||||
|
Data::None,
|
||||||
|
"ENBVJMP",
|
||||||
|
[OpType::None, OpType::None, OpType::None, OpType::None]
|
||||||
|
)),
|
||||||
|
Some(mn!(
|
||||||
|
0x3013,
|
||||||
|
Data::None,
|
||||||
|
"DISVJMP",
|
||||||
|
[OpType::None, OpType::None, OpType::None, OpType::None]
|
||||||
|
)),
|
||||||
|
Some(mn!(
|
||||||
|
0x3019,
|
||||||
|
Data::None,
|
||||||
|
"MOVBLW",
|
||||||
|
[OpType::None, OpType::None, OpType::None, OpType::None]
|
||||||
|
)),
|
||||||
|
Some(mn!(
|
||||||
|
0x301f,
|
||||||
|
Data::None,
|
||||||
|
"STREND",
|
||||||
|
[OpType::None, OpType::None, OpType::None, OpType::None]
|
||||||
|
)),
|
||||||
|
Some(mn!(
|
||||||
|
0x302f,
|
||||||
|
Data::None,
|
||||||
|
"INTACK",
|
||||||
|
[OpType::None, OpType::None, OpType::None, OpType::None]
|
||||||
|
)),
|
||||||
|
Some(mn!(
|
||||||
|
0x303f,
|
||||||
|
Data::None,
|
||||||
|
"STRCPY",
|
||||||
|
[OpType::None, OpType::None, OpType::None, OpType::None]
|
||||||
|
)),
|
||||||
Some(mn!(0x3045, Data::None, "RETG", [OpType::None, OpType::None, OpType::None, OpType::None])),
|
Some(mn!(0x3045, Data::None, "RETG", [OpType::None, OpType::None, OpType::None, OpType::None])),
|
||||||
Some(mn!(0x3061, Data::None, "GATE", [OpType::None, OpType::None, OpType::None, OpType::None])),
|
Some(mn!(0x3061, Data::None, "GATE", [OpType::None, OpType::None, OpType::None, OpType::None])),
|
||||||
Some(mn!(0x30ac, Data::None, "CALLPS", [OpType::None, OpType::None, OpType::None, OpType::None])),
|
Some(mn!(
|
||||||
Some(mn!(0x30c8, Data::None, "RETPS", [OpType::None, OpType::None, OpType::None, OpType::None])),
|
0x30ac,
|
||||||
|
Data::None,
|
||||||
|
"CALLPS",
|
||||||
|
[OpType::None, OpType::None, OpType::None, OpType::None]
|
||||||
|
)),
|
||||||
|
Some(mn!(
|
||||||
|
0x30c8,
|
||||||
|
Data::None,
|
||||||
|
"RETPS",
|
||||||
|
[OpType::None, OpType::None, OpType::None, OpType::None]
|
||||||
|
)),
|
||||||
];
|
];
|
||||||
|
|
||||||
static NULL_MNEMONIC: Option<Mnemonic> = None;
|
static NULL_MNEMONIC: Option<Mnemonic> = None;
|
||||||
|
@ -560,7 +628,9 @@ impl Cpu {
|
||||||
self.r[r]
|
self.r[r]
|
||||||
}
|
}
|
||||||
AddrMode::Absolute => embedded,
|
AddrMode::Absolute => embedded,
|
||||||
AddrMode::AbsoluteDeferred => bus.read_word(embedded as usize, AccessCode::AddressFetch)?,
|
AddrMode::AbsoluteDeferred => {
|
||||||
|
bus.read_word(embedded as usize, AccessCode::AddressFetch)?
|
||||||
|
}
|
||||||
AddrMode::FpShortOffset => add_offset(self.r[R_FP], sign_extend_byte(embedded as u8)),
|
AddrMode::FpShortOffset => add_offset(self.r[R_FP], sign_extend_byte(embedded as u8)),
|
||||||
AddrMode::ApShortOffset => add_offset(self.r[R_AP], sign_extend_byte(embedded as u8)),
|
AddrMode::ApShortOffset => add_offset(self.r[R_AP], sign_extend_byte(embedded as u8)),
|
||||||
AddrMode::WordDisplacement => {
|
AddrMode::WordDisplacement => {
|
||||||
|
@ -589,7 +659,10 @@ impl Cpu {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
|
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
|
||||||
};
|
};
|
||||||
bus.read_word((add_offset(self.r[r], sign_extend_halfword(embedded as u16))) as usize, AccessCode::AddressFetch)?
|
bus.read_word(
|
||||||
|
(add_offset(self.r[r], sign_extend_halfword(embedded as u16))) as usize,
|
||||||
|
AccessCode::AddressFetch,
|
||||||
|
)?
|
||||||
}
|
}
|
||||||
AddrMode::ByteDisplacement => {
|
AddrMode::ByteDisplacement => {
|
||||||
let r = match register {
|
let r = match register {
|
||||||
|
@ -603,7 +676,10 @@ impl Cpu {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
|
None => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
|
||||||
};
|
};
|
||||||
bus.read_word(add_offset(self.r[r], sign_extend_byte(embedded as u8)) as usize, AccessCode::AddressFetch)?
|
bus.read_word(
|
||||||
|
add_offset(self.r[r], sign_extend_byte(embedded as u8)) as usize,
|
||||||
|
AccessCode::AddressFetch,
|
||||||
|
)?
|
||||||
}
|
}
|
||||||
_ => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
|
_ => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
|
||||||
};
|
};
|
||||||
|
@ -633,18 +709,26 @@ impl Cpu {
|
||||||
_ => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
|
_ => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AddrMode::PositiveLiteral | AddrMode::NegativeLiteral => sign_extend_byte(op.embedded as u8),
|
AddrMode::PositiveLiteral | AddrMode::NegativeLiteral => {
|
||||||
|
sign_extend_byte(op.embedded as u8)
|
||||||
|
}
|
||||||
AddrMode::WordImmediate => op.embedded,
|
AddrMode::WordImmediate => op.embedded,
|
||||||
AddrMode::HalfwordImmediate => sign_extend_halfword(op.embedded as u16),
|
AddrMode::HalfwordImmediate => sign_extend_halfword(op.embedded as u16),
|
||||||
AddrMode::ByteImmediate => sign_extend_byte(op.embedded as u8),
|
AddrMode::ByteImmediate => sign_extend_byte(op.embedded as u8),
|
||||||
_ => {
|
_ => {
|
||||||
let eff = self.effective_address(bus, index)?;
|
let eff = self.effective_address(bus, index)?;
|
||||||
match op.data_type() {
|
match op.data_type() {
|
||||||
Data::UWord | Data::Word => bus.read_word(eff as usize, AccessCode::InstrFetch)?,
|
Data::UWord | Data::Word => {
|
||||||
Data::Half => sign_extend_halfword(bus.read_half(eff as usize, AccessCode::InstrFetch)?),
|
bus.read_word(eff as usize, AccessCode::InstrFetch)?
|
||||||
|
}
|
||||||
|
Data::Half => {
|
||||||
|
sign_extend_halfword(bus.read_half(eff as usize, AccessCode::InstrFetch)?)
|
||||||
|
}
|
||||||
Data::UHalf => u32::from(bus.read_half(eff as usize, AccessCode::InstrFetch)?),
|
Data::UHalf => u32::from(bus.read_half(eff as usize, AccessCode::InstrFetch)?),
|
||||||
Data::Byte => u32::from(bus.read_byte(eff as usize, AccessCode::InstrFetch)?),
|
Data::Byte => u32::from(bus.read_byte(eff as usize, AccessCode::InstrFetch)?),
|
||||||
Data::SByte => sign_extend_byte(bus.read_byte(eff as usize, AccessCode::InstrFetch)?),
|
Data::SByte => {
|
||||||
|
sign_extend_byte(bus.read_byte(eff as usize, AccessCode::InstrFetch)?)
|
||||||
|
}
|
||||||
_ => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
|
_ => return Err(CpuError::Exception(CpuException::IllegalOpcode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -836,7 +920,9 @@ impl Cpu {
|
||||||
|
|
||||||
// TODO: Remove unwraps
|
// TODO: Remove unwraps
|
||||||
fn on_interrupt(&mut self, bus: &mut Bus, vector: u8) {
|
fn on_interrupt(&mut self, bus: &mut Bus, vector: u8) {
|
||||||
let new_pcbp = bus.read_word((0x8c + (4 * u32::from(vector))) as usize, AccessCode::AddressFetch).unwrap();
|
let new_pcbp = bus
|
||||||
|
.read_word((0x8c + (4 * u32::from(vector))) as usize, AccessCode::AddressFetch)
|
||||||
|
.unwrap();
|
||||||
self.irq_push(bus, self.r[R_PCBP]).unwrap();
|
self.irq_push(bus, self.r[R_PCBP]).unwrap();
|
||||||
|
|
||||||
self.r[R_PSW] &= !(F_ISC | F_TM | F_ET);
|
self.r[R_PSW] &= !(F_ISC | F_TM | F_ET);
|
||||||
|
@ -869,6 +955,8 @@ impl Cpu {
|
||||||
self.decode_instruction(bus)?;
|
self.decode_instruction(bus)?;
|
||||||
let mut pc_increment: i32 = i32::from(self.ir.bytes);
|
let mut pc_increment: i32 = i32::from(self.ir.bytes);
|
||||||
|
|
||||||
|
bus.trace(self.steps, &self.ir);
|
||||||
|
|
||||||
match self.ir.opcode {
|
match self.ir.opcode {
|
||||||
NOP => {
|
NOP => {
|
||||||
pc_increment = 1;
|
pc_increment = 1;
|
||||||
|
@ -1547,7 +1635,8 @@ impl Cpu {
|
||||||
self.r[R_SP] = a;
|
self.r[R_SP] = a;
|
||||||
}
|
}
|
||||||
RETG => {
|
RETG => {
|
||||||
let mut new_psw = bus.read_word(self.r[R_SP] as usize - 4, AccessCode::AddressFetch)?;
|
let mut new_psw =
|
||||||
|
bus.read_word(self.r[R_SP] as usize - 4, AccessCode::AddressFetch)?;
|
||||||
let new_pc = bus.read_word(self.r[R_SP] as usize - 8, AccessCode::AddressFetch)?;
|
let new_pc = bus.read_word(self.r[R_SP] as usize - 8, AccessCode::AddressFetch)?;
|
||||||
|
|
||||||
// TODO: Check for illegal level change
|
// TODO: Check for illegal level change
|
||||||
|
@ -1643,17 +1732,28 @@ impl Cpu {
|
||||||
self.context_switch_3(bus)?;
|
self.context_switch_3(bus)?;
|
||||||
|
|
||||||
if self.r[R_PSW] & F_R != 0 {
|
if self.r[R_PSW] & F_R != 0 {
|
||||||
self.r[R_FP] = bus.read_word((new_pcbp + 24) as usize, AccessCode::AddressFetch)?;
|
self.r[R_FP] =
|
||||||
self.r[0] = bus.read_word((new_pcbp + 28) as usize, AccessCode::AddressFetch)?;
|
bus.read_word((new_pcbp + 24) as usize, AccessCode::AddressFetch)?;
|
||||||
self.r[1] = bus.read_word((new_pcbp + 32) as usize, AccessCode::AddressFetch)?;
|
self.r[0] =
|
||||||
self.r[2] = bus.read_word((new_pcbp + 36) as usize, AccessCode::AddressFetch)?;
|
bus.read_word((new_pcbp + 28) as usize, AccessCode::AddressFetch)?;
|
||||||
self.r[3] = bus.read_word((new_pcbp + 40) as usize, AccessCode::AddressFetch)?;
|
self.r[1] =
|
||||||
self.r[4] = bus.read_word((new_pcbp + 44) as usize, AccessCode::AddressFetch)?;
|
bus.read_word((new_pcbp + 32) as usize, AccessCode::AddressFetch)?;
|
||||||
self.r[5] = bus.read_word((new_pcbp + 48) as usize, AccessCode::AddressFetch)?;
|
self.r[2] =
|
||||||
self.r[6] = bus.read_word((new_pcbp + 52) as usize, AccessCode::AddressFetch)?;
|
bus.read_word((new_pcbp + 36) as usize, AccessCode::AddressFetch)?;
|
||||||
self.r[7] = bus.read_word((new_pcbp + 56) as usize, AccessCode::AddressFetch)?;
|
self.r[3] =
|
||||||
self.r[8] = bus.read_word((new_pcbp + 60) as usize, AccessCode::AddressFetch)?;
|
bus.read_word((new_pcbp + 40) as usize, AccessCode::AddressFetch)?;
|
||||||
self.r[R_AP] = bus.read_word((new_pcbp + 20) as usize, AccessCode::AddressFetch)?;
|
self.r[4] =
|
||||||
|
bus.read_word((new_pcbp + 44) as usize, AccessCode::AddressFetch)?;
|
||||||
|
self.r[5] =
|
||||||
|
bus.read_word((new_pcbp + 48) as usize, AccessCode::AddressFetch)?;
|
||||||
|
self.r[6] =
|
||||||
|
bus.read_word((new_pcbp + 52) as usize, AccessCode::AddressFetch)?;
|
||||||
|
self.r[7] =
|
||||||
|
bus.read_word((new_pcbp + 56) as usize, AccessCode::AddressFetch)?;
|
||||||
|
self.r[8] =
|
||||||
|
bus.read_word((new_pcbp + 60) as usize, AccessCode::AddressFetch)?;
|
||||||
|
self.r[R_AP] =
|
||||||
|
bus.read_word((new_pcbp + 20) as usize, AccessCode::AddressFetch)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
pc_increment = 0;
|
pc_increment = 0;
|
||||||
|
@ -1857,7 +1957,13 @@ impl Cpu {
|
||||||
///
|
///
|
||||||
/// These operands belong to only certain instructions, where a word without
|
/// These operands belong to only certain instructions, where a word without
|
||||||
/// a descriptor byte immediately follows the opcode.
|
/// a descriptor byte immediately follows the opcode.
|
||||||
fn decode_literal_operand(&mut self, bus: &mut Bus, index: usize, mn: &Mnemonic, addr: usize) -> Result<(), CpuError> {
|
fn decode_literal_operand(
|
||||||
|
&mut self,
|
||||||
|
bus: &mut Bus,
|
||||||
|
index: usize,
|
||||||
|
mn: &Mnemonic,
|
||||||
|
addr: usize,
|
||||||
|
) -> Result<(), CpuError> {
|
||||||
match mn.dtype {
|
match mn.dtype {
|
||||||
Data::Byte => {
|
Data::Byte => {
|
||||||
let b: u8 = bus.read_byte(addr, AccessCode::OperandFetch)?;
|
let b: u8 = bus.read_byte(addr, AccessCode::OperandFetch)?;
|
||||||
|
@ -1903,18 +2009,42 @@ impl Cpu {
|
||||||
match m {
|
match m {
|
||||||
0 | 1 | 2 | 3 => {
|
0 | 1 | 2 | 3 => {
|
||||||
// Positive Literal
|
// Positive Literal
|
||||||
self.set_operand(index, dsize, AddrMode::PositiveLiteral, dtype, etype, None, u32::from(descriptor_byte));
|
self.set_operand(
|
||||||
|
index,
|
||||||
|
dsize,
|
||||||
|
AddrMode::PositiveLiteral,
|
||||||
|
dtype,
|
||||||
|
etype,
|
||||||
|
None,
|
||||||
|
u32::from(descriptor_byte),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
4 => {
|
4 => {
|
||||||
match r {
|
match r {
|
||||||
15 => {
|
15 => {
|
||||||
// Word Immediate
|
// Word Immediate
|
||||||
let w = bus.read_op_word(addr + 1)?;
|
let w = bus.read_op_word(addr + 1)?;
|
||||||
self.set_operand(index, dsize + 4, AddrMode::WordImmediate, dtype, etype, None, w);
|
self.set_operand(
|
||||||
|
index,
|
||||||
|
dsize + 4,
|
||||||
|
AddrMode::WordImmediate,
|
||||||
|
dtype,
|
||||||
|
etype,
|
||||||
|
None,
|
||||||
|
w,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Register
|
// Register
|
||||||
self.set_operand(index, dsize, AddrMode::Register, dtype, etype, Some(r as usize), 0);
|
self.set_operand(
|
||||||
|
index,
|
||||||
|
dsize,
|
||||||
|
AddrMode::Register,
|
||||||
|
dtype,
|
||||||
|
etype,
|
||||||
|
Some(r as usize),
|
||||||
|
0,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1923,7 +2053,15 @@ impl Cpu {
|
||||||
15 => {
|
15 => {
|
||||||
// Halfword Immediate
|
// Halfword Immediate
|
||||||
let h = bus.read_op_half(addr + 1)?;
|
let h = bus.read_op_half(addr + 1)?;
|
||||||
self.set_operand(index, dsize + 2, AddrMode::HalfwordImmediate, dtype, etype, None, u32::from(h));
|
self.set_operand(
|
||||||
|
index,
|
||||||
|
dsize + 2,
|
||||||
|
AddrMode::HalfwordImmediate,
|
||||||
|
dtype,
|
||||||
|
etype,
|
||||||
|
None,
|
||||||
|
u32::from(h),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
11 => {
|
11 => {
|
||||||
// Illegal
|
// Illegal
|
||||||
|
@ -1931,7 +2069,15 @@ impl Cpu {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Register Deferred Mode
|
// Register Deferred Mode
|
||||||
self.set_operand(index, dsize, AddrMode::RegisterDeferred, dtype, etype, Some(r as usize), 0);
|
self.set_operand(
|
||||||
|
index,
|
||||||
|
dsize,
|
||||||
|
AddrMode::RegisterDeferred,
|
||||||
|
dtype,
|
||||||
|
etype,
|
||||||
|
Some(r as usize),
|
||||||
|
0,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1940,11 +2086,27 @@ impl Cpu {
|
||||||
15 => {
|
15 => {
|
||||||
// Byte Immediate
|
// Byte Immediate
|
||||||
let b = bus.read_byte(addr + 1, AccessCode::OperandFetch)?;
|
let b = bus.read_byte(addr + 1, AccessCode::OperandFetch)?;
|
||||||
self.set_operand(index, dsize + 1, AddrMode::ByteImmediate, dtype, etype, None, u32::from(b));
|
self.set_operand(
|
||||||
|
index,
|
||||||
|
dsize + 1,
|
||||||
|
AddrMode::ByteImmediate,
|
||||||
|
dtype,
|
||||||
|
etype,
|
||||||
|
None,
|
||||||
|
u32::from(b),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// FP Short Offset
|
// FP Short Offset
|
||||||
self.set_operand(index, dsize, AddrMode::FpShortOffset, dtype, etype, Some(R_FP), u32::from(r));
|
self.set_operand(
|
||||||
|
index,
|
||||||
|
dsize,
|
||||||
|
AddrMode::FpShortOffset,
|
||||||
|
dtype,
|
||||||
|
etype,
|
||||||
|
Some(R_FP),
|
||||||
|
u32::from(r),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1953,11 +2115,27 @@ impl Cpu {
|
||||||
15 => {
|
15 => {
|
||||||
// Absolute
|
// Absolute
|
||||||
let w = bus.read_op_word(addr + 1)?;
|
let w = bus.read_op_word(addr + 1)?;
|
||||||
self.set_operand(index, dsize + 4, AddrMode::Absolute, dtype, etype, None, w);
|
self.set_operand(
|
||||||
|
index,
|
||||||
|
dsize + 4,
|
||||||
|
AddrMode::Absolute,
|
||||||
|
dtype,
|
||||||
|
etype,
|
||||||
|
None,
|
||||||
|
w,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// AP Short Offset
|
// AP Short Offset
|
||||||
self.set_operand(index, dsize, AddrMode::ApShortOffset, dtype, etype, Some(R_AP), u32::from(r));
|
self.set_operand(
|
||||||
|
index,
|
||||||
|
dsize,
|
||||||
|
AddrMode::ApShortOffset,
|
||||||
|
dtype,
|
||||||
|
etype,
|
||||||
|
Some(R_AP),
|
||||||
|
u32::from(r),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1967,7 +2145,15 @@ impl Cpu {
|
||||||
_ => {
|
_ => {
|
||||||
// Word Displacement
|
// Word Displacement
|
||||||
let disp = bus.read_op_word(addr + 1)?;
|
let disp = bus.read_op_word(addr + 1)?;
|
||||||
self.set_operand(index, dsize + 4, AddrMode::WordDisplacement, dtype, etype, Some(r as usize), disp);
|
self.set_operand(
|
||||||
|
index,
|
||||||
|
dsize + 4,
|
||||||
|
AddrMode::WordDisplacement,
|
||||||
|
dtype,
|
||||||
|
etype,
|
||||||
|
Some(r as usize),
|
||||||
|
disp,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2062,15 +2248,65 @@ impl Cpu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
14 => match r {
|
14 => match r {
|
||||||
0 => self.decode_descriptor_operand(bus, index, dtype, Some(Data::UWord), addr + 1, true)?,
|
0 => self.decode_descriptor_operand(
|
||||||
2 => self.decode_descriptor_operand(bus, index, dtype, Some(Data::UHalf), addr + 1, true)?,
|
bus,
|
||||||
3 => self.decode_descriptor_operand(bus, index, dtype, Some(Data::Byte), addr + 1, true)?,
|
index,
|
||||||
4 => self.decode_descriptor_operand(bus, index, dtype, Some(Data::Word), addr + 1, true)?,
|
dtype,
|
||||||
6 => self.decode_descriptor_operand(bus, index, dtype, Some(Data::Half), addr + 1, true)?,
|
Some(Data::UWord),
|
||||||
7 => self.decode_descriptor_operand(bus, index, dtype, Some(Data::SByte), addr + 1, true)?,
|
addr + 1,
|
||||||
|
true,
|
||||||
|
)?,
|
||||||
|
2 => self.decode_descriptor_operand(
|
||||||
|
bus,
|
||||||
|
index,
|
||||||
|
dtype,
|
||||||
|
Some(Data::UHalf),
|
||||||
|
addr + 1,
|
||||||
|
true,
|
||||||
|
)?,
|
||||||
|
3 => self.decode_descriptor_operand(
|
||||||
|
bus,
|
||||||
|
index,
|
||||||
|
dtype,
|
||||||
|
Some(Data::Byte),
|
||||||
|
addr + 1,
|
||||||
|
true,
|
||||||
|
)?,
|
||||||
|
4 => self.decode_descriptor_operand(
|
||||||
|
bus,
|
||||||
|
index,
|
||||||
|
dtype,
|
||||||
|
Some(Data::Word),
|
||||||
|
addr + 1,
|
||||||
|
true,
|
||||||
|
)?,
|
||||||
|
6 => self.decode_descriptor_operand(
|
||||||
|
bus,
|
||||||
|
index,
|
||||||
|
dtype,
|
||||||
|
Some(Data::Half),
|
||||||
|
addr + 1,
|
||||||
|
true,
|
||||||
|
)?,
|
||||||
|
7 => self.decode_descriptor_operand(
|
||||||
|
bus,
|
||||||
|
index,
|
||||||
|
dtype,
|
||||||
|
Some(Data::SByte),
|
||||||
|
addr + 1,
|
||||||
|
true,
|
||||||
|
)?,
|
||||||
15 => {
|
15 => {
|
||||||
let w = bus.read_op_word(addr + 1)?;
|
let w = bus.read_op_word(addr + 1)?;
|
||||||
self.set_operand(index, dsize + 4, AddrMode::AbsoluteDeferred, dtype, etype, None, w);
|
self.set_operand(
|
||||||
|
index,
|
||||||
|
dsize + 4,
|
||||||
|
AddrMode::AbsoluteDeferred,
|
||||||
|
dtype,
|
||||||
|
etype,
|
||||||
|
None,
|
||||||
|
w,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(CpuError::Exception(CpuException::IllegalOpcode));
|
return Err(CpuError::Exception(CpuException::IllegalOpcode));
|
||||||
|
@ -2078,7 +2314,15 @@ impl Cpu {
|
||||||
},
|
},
|
||||||
15 => {
|
15 => {
|
||||||
// Negative Literal
|
// Negative Literal
|
||||||
self.set_operand(index, 1, AddrMode::NegativeLiteral, dtype, etype, None, u32::from(descriptor_byte));
|
self.set_operand(
|
||||||
|
index,
|
||||||
|
1,
|
||||||
|
AddrMode::NegativeLiteral,
|
||||||
|
dtype,
|
||||||
|
etype,
|
||||||
|
None,
|
||||||
|
u32::from(descriptor_byte),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(CpuError::Exception(CpuException::IllegalOpcode));
|
return Err(CpuError::Exception(CpuException::IllegalOpcode));
|
||||||
|
@ -2100,7 +2344,9 @@ impl Cpu {
|
||||||
) -> Result<(), CpuError> {
|
) -> Result<(), CpuError> {
|
||||||
match ot {
|
match ot {
|
||||||
OpType::Lit => self.decode_literal_operand(bus, index, mn, addr),
|
OpType::Lit => self.decode_literal_operand(bus, index, mn, addr),
|
||||||
OpType::Src | OpType::Dest => self.decode_descriptor_operand(bus, index, mn.dtype, etype, addr, false),
|
OpType::Src | OpType::Dest => {
|
||||||
|
self.decode_descriptor_operand(bus, index, mn.dtype, etype, addr, false)
|
||||||
|
}
|
||||||
OpType::None => Ok(()),
|
OpType::None => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2385,8 +2631,17 @@ mod tests {
|
||||||
let program: [u8; 2] = [0x4f, 0x06]; // BLEB 0x6
|
let program: [u8; 2] = [0x4f, 0x06]; // BLEB 0x6
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_literal_operand(&mut bus, 0, BYTE_MNEMONICS[0x4F as usize].as_ref().unwrap(), BASE + 1).unwrap();
|
cpu.decode_literal_operand(
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(1, AddrMode::None, Data::Byte, None, None, 6));
|
&mut bus,
|
||||||
|
0,
|
||||||
|
BYTE_MNEMONICS[0x4F as usize].as_ref().unwrap(),
|
||||||
|
BASE + 1,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(1, AddrMode::None, Data::Byte, None, None, 6)
|
||||||
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2395,8 +2650,17 @@ mod tests {
|
||||||
let program: [u8; 3] = [0x4e, 0xff, 0x0f]; // BLEH 0xfff
|
let program: [u8; 3] = [0x4e, 0xff, 0x0f]; // BLEH 0xfff
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_literal_operand(&mut bus, 0, BYTE_MNEMONICS[0x4e as usize].as_ref().unwrap(), BASE + 1).unwrap();
|
cpu.decode_literal_operand(
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(2, AddrMode::None, Data::Half, None, None, 0xfff));
|
&mut bus,
|
||||||
|
0,
|
||||||
|
BYTE_MNEMONICS[0x4e as usize].as_ref().unwrap(),
|
||||||
|
BASE + 1,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(2, AddrMode::None, Data::Half, None, None, 0xfff)
|
||||||
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2405,8 +2669,17 @@ mod tests {
|
||||||
let program: [u8; 5] = [0x32, 0xff, 0x4f, 0x00, 0x00]; // SPOP 0x4fff
|
let program: [u8; 5] = [0x32, 0xff, 0x4f, 0x00, 0x00]; // SPOP 0x4fff
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_literal_operand(&mut bus, 0, BYTE_MNEMONICS[0x32 as usize].as_ref().unwrap(), BASE + 1).unwrap();
|
cpu.decode_literal_operand(
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(4, AddrMode::None, Data::Word, None, None, 0x4fff));
|
&mut bus,
|
||||||
|
0,
|
||||||
|
BYTE_MNEMONICS[0x32 as usize].as_ref().unwrap(),
|
||||||
|
BASE + 1,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(4, AddrMode::None, Data::Word, None, None, 0x4fff)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2416,7 +2689,10 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(1, AddrMode::PositiveLiteral, Data::Byte, None, None, 0x04));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(1, AddrMode::PositiveLiteral, Data::Byte, None, None, 0x04)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2426,7 +2702,10 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Word, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Word, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(5, AddrMode::WordImmediate, Data::Word, None, None, 0x12345678));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(5, AddrMode::WordImmediate, Data::Word, None, None, 0x12345678)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2436,7 +2715,10 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 2, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 2, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(1, AddrMode::Register, Data::Byte, None, Some(4), 0));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(1, AddrMode::Register, Data::Byte, None, Some(4), 0)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2446,7 +2728,10 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Word, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Word, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(3, AddrMode::HalfwordImmediate, Data::Word, None, None, 0x1234,));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(3, AddrMode::HalfwordImmediate, Data::Word, None, None, 0x1234,)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2456,7 +2741,10 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Half, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Half, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(1, AddrMode::RegisterDeferred, Data::Half, None, Some(2), 0));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(1, AddrMode::RegisterDeferred, Data::Half, None, Some(2), 0)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2466,7 +2754,10 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Word, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Word, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(2, AddrMode::ByteImmediate, Data::Word, None, None, 40));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(2, AddrMode::ByteImmediate, Data::Word, None, None, 40)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2476,7 +2767,10 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Word, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Word, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(1, AddrMode::FpShortOffset, Data::Word, None, Some(R_FP), 12));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(1, AddrMode::FpShortOffset, Data::Word, None, Some(R_FP), 12)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2486,7 +2780,10 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(5, AddrMode::Absolute, Data::Byte, None, None, 0x00000100));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(5, AddrMode::Absolute, Data::Byte, None, None, 0x00000100)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2496,7 +2793,10 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(5, AddrMode::AbsoluteDeferred, Data::Byte, None, None, 0x00000100));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(5, AddrMode::AbsoluteDeferred, Data::Byte, None, None, 0x00000100)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2506,7 +2806,10 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Word, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Word, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(1, AddrMode::ApShortOffset, Data::Word, None, Some(R_AP), 4));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(1, AddrMode::ApShortOffset, Data::Word, None, Some(R_AP), 4)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2516,7 +2819,10 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(5, AddrMode::WordDisplacement, Data::Byte, None, Some(2), 0x1234,));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(5, AddrMode::WordDisplacement, Data::Byte, None, Some(2), 0x1234,)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2526,7 +2832,17 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(5, AddrMode::WordDisplacementDeferred, Data::Byte, None, Some(2), 0x4050,));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(
|
||||||
|
5,
|
||||||
|
AddrMode::WordDisplacementDeferred,
|
||||||
|
Data::Byte,
|
||||||
|
None,
|
||||||
|
Some(2),
|
||||||
|
0x4050,
|
||||||
|
)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2536,7 +2852,10 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(3, AddrMode::HalfwordDisplacement, Data::Byte, None, Some(2), 0x1234,));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(3, AddrMode::HalfwordDisplacement, Data::Byte, None, Some(2), 0x1234,)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2548,7 +2867,14 @@ mod tests {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cpu.ir.operands[0],
|
cpu.ir.operands[0],
|
||||||
Operand::new(3, AddrMode::HalfwordDisplacementDeferred, Data::Byte, None, Some(2), 0x4050,)
|
Operand::new(
|
||||||
|
3,
|
||||||
|
AddrMode::HalfwordDisplacementDeferred,
|
||||||
|
Data::Byte,
|
||||||
|
None,
|
||||||
|
Some(2),
|
||||||
|
0x4050,
|
||||||
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2559,7 +2885,10 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(2, AddrMode::ByteDisplacement, Data::Byte, None, Some(1), 6));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(2, AddrMode::ByteDisplacement, Data::Byte, None, Some(1), 6)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2569,7 +2898,17 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(2, AddrMode::ByteDisplacementDeferred, Data::Byte, None, Some(2), 0x30));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(
|
||||||
|
2,
|
||||||
|
AddrMode::ByteDisplacementDeferred,
|
||||||
|
Data::Byte,
|
||||||
|
None,
|
||||||
|
Some(2),
|
||||||
|
0x30
|
||||||
|
)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2581,8 +2920,21 @@ mod tests {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
||||||
cpu.decode_descriptor_operand(&mut bus, 1, Data::Byte, None, BASE + 3, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 1, Data::Byte, None, BASE + 3, false).unwrap();
|
||||||
|
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(2, AddrMode::Register, Data::Byte, Some(Data::SByte), Some(0), 0,));
|
assert_eq!(
|
||||||
assert_eq!(cpu.ir.operands[1], Operand::new(3, AddrMode::ByteDisplacement, Data::Byte, Some(Data::UHalf), Some(1), 4,));
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(2, AddrMode::Register, Data::Byte, Some(Data::SByte), Some(0), 0,)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cpu.ir.operands[1],
|
||||||
|
Operand::new(
|
||||||
|
3,
|
||||||
|
AddrMode::ByteDisplacement,
|
||||||
|
Data::Byte,
|
||||||
|
Some(Data::UHalf),
|
||||||
|
Some(1),
|
||||||
|
4,
|
||||||
|
)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2592,7 +2944,10 @@ mod tests {
|
||||||
|
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
||||||
assert_eq!(cpu.ir.operands[0], Operand::new(1, AddrMode::NegativeLiteral, Data::Byte, None, None, 0xff));
|
assert_eq!(
|
||||||
|
cpu.ir.operands[0],
|
||||||
|
Operand::new(1, AddrMode::NegativeLiteral, Data::Byte, None, None, 0xff)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2625,7 +2980,14 @@ mod tests {
|
||||||
cpu.decode_instruction(bus).unwrap();
|
cpu.decode_instruction(bus).unwrap();
|
||||||
let expected_operands = vec![
|
let expected_operands = vec![
|
||||||
Operand::new(2, AddrMode::Register, Data::Byte, Some(Data::SByte), Some(0), 0),
|
Operand::new(2, AddrMode::Register, Data::Byte, Some(Data::SByte), Some(0), 0),
|
||||||
Operand::new(3, AddrMode::ByteDisplacement, Data::Byte, Some(Data::UHalf), Some(1), 4),
|
Operand::new(
|
||||||
|
3,
|
||||||
|
AddrMode::ByteDisplacement,
|
||||||
|
Data::Byte,
|
||||||
|
Some(Data::UHalf),
|
||||||
|
Some(1),
|
||||||
|
4,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
assert_instruction(cpu, 0x87, 6, "MOVB", Data::Byte);
|
assert_instruction(cpu, 0x87, 6, "MOVB", Data::Byte);
|
||||||
assert_eq!(cpu.ir.operands[0], expected_operands[0]);
|
assert_eq!(cpu.ir.operands[0], expected_operands[0]);
|
||||||
|
@ -2635,7 +2997,14 @@ mod tests {
|
||||||
cpu.set_pc((BASE + 6) as u32);
|
cpu.set_pc((BASE + 6) as u32);
|
||||||
cpu.decode_instruction(bus).unwrap();
|
cpu.decode_instruction(bus).unwrap();
|
||||||
let expected_operands = vec![
|
let expected_operands = vec![
|
||||||
Operand::new(2, AddrMode::ByteDisplacementDeferred, Data::Byte, None, Some(2), 0x30),
|
Operand::new(
|
||||||
|
2,
|
||||||
|
AddrMode::ByteDisplacementDeferred,
|
||||||
|
Data::Byte,
|
||||||
|
None,
|
||||||
|
Some(2),
|
||||||
|
0x30,
|
||||||
|
),
|
||||||
Operand::new(1, AddrMode::Register, Data::Byte, None, Some(3), 0),
|
Operand::new(1, AddrMode::Register, Data::Byte, None, Some(3), 0),
|
||||||
];
|
];
|
||||||
assert_instruction(cpu, 0x87, 4, "MOVB", Data::Byte);
|
assert_instruction(cpu, 0x87, 4, "MOVB", Data::Byte);
|
||||||
|
@ -2651,7 +3020,8 @@ mod tests {
|
||||||
let program = [0x87, 0xe7, 0x40, 0xe2, 0x41]; // MOVB {sbyte}%r0,{uhalf}%r1
|
let program = [0x87, 0xe7, 0x40, 0xe2, 0x41]; // MOVB {sbyte}%r0,{uhalf}%r1
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.r[0] = 0xff;
|
cpu.r[0] = 0xff;
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(0xffffffff, cpu.read_op(bus, 0).unwrap());
|
assert_eq!(0xffffffff, cpu.read_op(bus, 0).unwrap());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2660,7 +3030,8 @@ mod tests {
|
||||||
let program = [0x87, 0x40, 0x41]; // MOVB %r0,%r1
|
let program = [0x87, 0x40, 0x41]; // MOVB %r0,%r1
|
||||||
do_with_program(&program, |cpu, mut bus| {
|
do_with_program(&program, |cpu, mut bus| {
|
||||||
cpu.r[0] = 0xff;
|
cpu.r[0] = 0xff;
|
||||||
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false).unwrap();
|
cpu.decode_descriptor_operand(&mut bus, 0, Data::Byte, None, BASE + 1, false)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(0xff, cpu.read_op(bus, 0).unwrap());
|
assert_eq!(0xff, cpu.read_op(bus, 0).unwrap());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
134
src/dmd.rs
134
src/dmd.rs
|
@ -1,14 +1,14 @@
|
||||||
#![allow(clippy::unreadable_literal)]
|
#![allow(clippy::unreadable_literal)]
|
||||||
|
|
||||||
use crate::bus::{Bus, AccessCode};
|
use crate::bus::{AccessCode, Bus};
|
||||||
use crate::cpu::Cpu;
|
use crate::cpu::Cpu;
|
||||||
use crate::err::BusError;
|
use crate::err::BusError;
|
||||||
use crate::rom_hi::HI_ROM;
|
use crate::rom_hi::HI_ROM;
|
||||||
use crate::rom_lo::LO_ROM;
|
use crate::rom_lo::LO_ROM;
|
||||||
|
|
||||||
use libc::*;
|
use libc::*;
|
||||||
use std::ptr;
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use std::{ffi::CStr, ptr};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref DMD: Mutex<Dmd> = Mutex::new(Dmd::new());
|
static ref DMD: Mutex<Dmd> = Mutex::new(Dmd::new());
|
||||||
|
@ -40,6 +40,14 @@ impl Dmd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn trace_on(&mut self, name: &str) -> std::io::Result<()> {
|
||||||
|
self.bus.trace_on(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trace_off(&mut self) {
|
||||||
|
self.bus.trace_off()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self) -> Result<(), BusError> {
|
pub fn reset(&mut self) -> Result<(), BusError> {
|
||||||
self.bus.load(0, &LO_ROM)?;
|
self.bus.load(0, &LO_ROM)?;
|
||||||
self.bus.load(0x10000, &HI_ROM)?;
|
self.bus.load(0x10000, &HI_ROM)?;
|
||||||
|
@ -140,23 +148,19 @@ impl Dmd {
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
fn dmd_reset() -> c_int {
|
fn dmd_reset() -> c_int {
|
||||||
match DMD.lock() {
|
match DMD.lock() {
|
||||||
Ok(mut dmd) => {
|
Ok(mut dmd) => match dmd.reset() {
|
||||||
match dmd.reset() {
|
Ok(()) => SUCCESS,
|
||||||
Ok(()) => SUCCESS,
|
Err(_) => ERROR,
|
||||||
Err(_) => ERROR
|
},
|
||||||
}
|
Err(_) => ERROR,
|
||||||
}
|
|
||||||
Err(_) => ERROR
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
fn dmd_video_ram() -> *const u8 {
|
fn dmd_video_ram() -> *const u8 {
|
||||||
match DMD.lock() {
|
match DMD.lock() {
|
||||||
Ok(dmd) => {
|
Ok(dmd) => dmd.video_ram().as_ptr(),
|
||||||
dmd.video_ram().as_ptr()
|
Err(_) => ptr::null(),
|
||||||
}
|
|
||||||
Err(_) => ptr::null()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +171,21 @@ fn dmd_step() -> c_int {
|
||||||
dmd.step();
|
dmd.step();
|
||||||
SUCCESS
|
SUCCESS
|
||||||
}
|
}
|
||||||
Err(_) => ERROR
|
Err(_) => ERROR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
fn dmd_trace_on(file_name: &CStr) -> c_int {
|
||||||
|
match DMD.lock() {
|
||||||
|
Ok(mut dmd) => match file_name.to_str() {
|
||||||
|
Ok(file_name) => match dmd.trace_on(file_name) {
|
||||||
|
Ok(()) => SUCCESS,
|
||||||
|
Err(_) => ERROR,
|
||||||
|
},
|
||||||
|
Err(_) => ERROR,
|
||||||
|
},
|
||||||
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,8 +195,8 @@ fn dmd_step_loop(steps: usize) -> c_int {
|
||||||
Ok(mut dmd) => {
|
Ok(mut dmd) => {
|
||||||
dmd.run(steps);
|
dmd.run(steps);
|
||||||
SUCCESS
|
SUCCESS
|
||||||
},
|
}
|
||||||
Err(_) => ERROR
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,8 +206,8 @@ fn dmd_get_pc(pc: &mut u32) -> c_int {
|
||||||
Ok(dmd) => {
|
Ok(dmd) => {
|
||||||
*pc = dmd.get_pc();
|
*pc = dmd.get_pc();
|
||||||
SUCCESS
|
SUCCESS
|
||||||
},
|
}
|
||||||
Err(_) => ERROR
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,40 +217,36 @@ fn dmd_get_register(reg: u8, val: &mut u32) -> c_int {
|
||||||
Ok(dmd) => {
|
Ok(dmd) => {
|
||||||
*val = dmd.get_register(reg);
|
*val = dmd.get_register(reg);
|
||||||
SUCCESS
|
SUCCESS
|
||||||
},
|
}
|
||||||
Err(_) => ERROR
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
fn dmd_read_word(addr: u32, val: &mut u32) -> c_int {
|
fn dmd_read_word(addr: u32, val: &mut u32) -> c_int {
|
||||||
match DMD.lock() {
|
match DMD.lock() {
|
||||||
Ok(mut dmd) => {
|
Ok(mut dmd) => match dmd.read_word(addr as usize) {
|
||||||
match dmd.read_word(addr as usize) {
|
Some(word) => {
|
||||||
Some(word) => {
|
*val = word;
|
||||||
*val = word;
|
SUCCESS
|
||||||
SUCCESS
|
|
||||||
},
|
|
||||||
None => ERROR
|
|
||||||
}
|
}
|
||||||
|
None => ERROR,
|
||||||
},
|
},
|
||||||
Err(_) => ERROR
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
fn dmd_read_byte(addr: u32, val: &mut u8) -> c_int {
|
fn dmd_read_byte(addr: u32, val: &mut u8) -> c_int {
|
||||||
match DMD.lock() {
|
match DMD.lock() {
|
||||||
Ok(mut dmd) => {
|
Ok(mut dmd) => match dmd.read_byte(addr as usize) {
|
||||||
match dmd.read_byte(addr as usize) {
|
Some(byte) => {
|
||||||
Some(byte) => {
|
*val = byte;
|
||||||
*val = byte;
|
SUCCESS
|
||||||
SUCCESS
|
|
||||||
},
|
|
||||||
None => ERROR
|
|
||||||
}
|
}
|
||||||
|
None => ERROR,
|
||||||
},
|
},
|
||||||
Err(_) => ERROR
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +257,7 @@ fn dmd_get_duart_output_port(oport: &mut u8) -> c_int {
|
||||||
*oport = dmd.duart_output();
|
*oport = dmd.duart_output();
|
||||||
SUCCESS
|
SUCCESS
|
||||||
}
|
}
|
||||||
Err(_) => ERROR
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +268,7 @@ fn dmd_rx_char(c: u8) -> c_int {
|
||||||
dmd.rx_char(c as u8);
|
dmd.rx_char(c as u8);
|
||||||
SUCCESS
|
SUCCESS
|
||||||
}
|
}
|
||||||
Err(_) => ERROR
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,7 +279,7 @@ fn dmd_rx_keyboard(c: u8) -> c_int {
|
||||||
dmd.rx_keyboard(c);
|
dmd.rx_keyboard(c);
|
||||||
SUCCESS
|
SUCCESS
|
||||||
}
|
}
|
||||||
Err(_) => ERROR
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,7 +290,7 @@ fn dmd_mouse_move(x: u16, y: u16) -> c_int {
|
||||||
dmd.mouse_move(x, y);
|
dmd.mouse_move(x, y);
|
||||||
SUCCESS
|
SUCCESS
|
||||||
}
|
}
|
||||||
Err(_) => ERROR
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +301,7 @@ fn dmd_mouse_down(button: u8) -> c_int {
|
||||||
dmd.mouse_down(button);
|
dmd.mouse_down(button);
|
||||||
SUCCESS
|
SUCCESS
|
||||||
}
|
}
|
||||||
Err(_) => ERROR
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,39 +312,35 @@ fn dmd_mouse_up(button: u8) -> c_int {
|
||||||
dmd.mouse_up(button);
|
dmd.mouse_up(button);
|
||||||
SUCCESS
|
SUCCESS
|
||||||
}
|
}
|
||||||
Err(_) => ERROR
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
fn dmd_rs232_tx_poll(tx_char: &mut u8) -> c_int {
|
fn dmd_rs232_tx_poll(tx_char: &mut u8) -> c_int {
|
||||||
match DMD.lock() {
|
match DMD.lock() {
|
||||||
Ok(mut dmd) => {
|
Ok(mut dmd) => match dmd.rs232_tx_poll() {
|
||||||
match dmd.rs232_tx_poll() {
|
Some(c) => {
|
||||||
Some(c) => {
|
*tx_char = c;
|
||||||
*tx_char = c;
|
SUCCESS
|
||||||
SUCCESS
|
|
||||||
}
|
|
||||||
None => BUSY
|
|
||||||
}
|
}
|
||||||
}
|
None => BUSY,
|
||||||
Err(_) => ERROR
|
},
|
||||||
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
fn dmd_kb_tx_poll(tx_char: &mut u8) -> c_int {
|
fn dmd_kb_tx_poll(tx_char: &mut u8) -> c_int {
|
||||||
match DMD.lock() {
|
match DMD.lock() {
|
||||||
Ok(mut dmd) => {
|
Ok(mut dmd) => match dmd.kb_tx_poll() {
|
||||||
match dmd.kb_tx_poll() {
|
Some(c) => {
|
||||||
Some(c) => {
|
*tx_char = c;
|
||||||
*tx_char = c;
|
SUCCESS
|
||||||
SUCCESS
|
|
||||||
}
|
|
||||||
None => BUSY
|
|
||||||
}
|
}
|
||||||
}
|
None => BUSY,
|
||||||
Err(_) => ERROR
|
},
|
||||||
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,7 +351,7 @@ fn dmd_set_nvram(nvram: &[u8; 8192]) -> c_int {
|
||||||
dmd.set_nvram(nvram);
|
dmd.set_nvram(nvram);
|
||||||
SUCCESS
|
SUCCESS
|
||||||
}
|
}
|
||||||
Err(_) => ERROR
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,7 +362,7 @@ fn dmd_get_nvram(nvram: &mut [u8; 8192]) -> c_int {
|
||||||
nvram.clone_from_slice(dmd.get_nvram());
|
nvram.clone_from_slice(dmd.get_nvram());
|
||||||
SUCCESS
|
SUCCESS
|
||||||
}
|
}
|
||||||
Err(_) => ERROR
|
Err(_) => ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
53
src/duart.rs
53
src/duart.rs
|
@ -4,33 +4,31 @@ use crate::bus::AccessCode;
|
||||||
use crate::bus::Device;
|
use crate::bus::Device;
|
||||||
use crate::err::BusError;
|
use crate::err::BusError;
|
||||||
|
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::fmt::Error;
|
use std::fmt::Error;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::collections::VecDeque;
|
|
||||||
|
|
||||||
const START_ADDR: usize = 0x200000;
|
const START_ADDR: usize = 0x200000;
|
||||||
const END_ADDR: usize = 0x2000040;
|
const END_ADDR: usize = 0x2000040;
|
||||||
const ADDRESS_RANGE: Range<usize> = START_ADDR..END_ADDR;
|
const ADDRESS_RANGE: Range<usize> = START_ADDR..END_ADDR;
|
||||||
|
|
||||||
// Vertical blanks should occur at 60Hz. This value is in nanoseconds
|
// Vertical blanks should occur at 60Hz. This value is in nanoseconds
|
||||||
const VERTICAL_BLANK_DELAY: u32 = 16_666_666; // 60 Hz
|
const VERTICAL_BLANK_DELAY: u32 = 16_666_666; // 60 Hz
|
||||||
|
|
||||||
// Delay rates, in nanoseconds, selected when ACR[7] = 0
|
// Delay rates, in nanoseconds, selected when ACR[7] = 0
|
||||||
const DELAY_RATES_A: [u32;13] = [
|
const DELAY_RATES_A: [u32; 13] = [
|
||||||
160000000, 72727272, 59259260, 40000000,
|
160000000, 72727272, 59259260, 40000000, 26666668, 13333334, 6666667, 7619047, 3333333,
|
||||||
26666668, 13333334, 6666667, 7619047,
|
1666666, 1111111, 833333, 208333,
|
||||||
3333333, 1666666, 1111111, 833333, 208333
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Delay rates, in nanoseconds, selected when ACR[7] = 1
|
// Delay rates, in nanoseconds, selected when ACR[7] = 1
|
||||||
const DELAY_RATES_B: [u32;13] = [
|
const DELAY_RATES_B: [u32; 13] = [
|
||||||
106666672, 72727272, 59259260, 53333336,
|
106666672, 72727272, 59259260, 53333336, 26666668, 13333334, 6666667, 4000000, 3333333,
|
||||||
26666668, 13333334, 6666667, 4000000,
|
1666666, 4444444, 833333, 416666,
|
||||||
3333333, 1666666, 4444444, 833333, 416666
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const PORT_0: usize = 0;
|
const PORT_0: usize = 0;
|
||||||
|
@ -53,7 +51,6 @@ const IP_OPCR: u8 = 0x37;
|
||||||
const OPBITS_SET: u8 = 0x3b;
|
const OPBITS_SET: u8 = 0x3b;
|
||||||
const OPBITS_RESET: u8 = 0x3f;
|
const OPBITS_RESET: u8 = 0x3f;
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Port Configuration Bits
|
// Port Configuration Bits
|
||||||
//
|
//
|
||||||
|
@ -96,7 +93,7 @@ const TX_INT: u8 = 0x10;
|
||||||
const RX_INT: u8 = 0x20;
|
const RX_INT: u8 = 0x20;
|
||||||
|
|
||||||
struct Port {
|
struct Port {
|
||||||
mode: [u8;2],
|
mode: [u8; 2],
|
||||||
stat: u8,
|
stat: u8,
|
||||||
conf: u8,
|
conf: u8,
|
||||||
rx_data: u8,
|
rx_data: u8,
|
||||||
|
@ -142,7 +139,7 @@ pub struct Duart {
|
||||||
istat: u8,
|
istat: u8,
|
||||||
imr: u8,
|
imr: u8,
|
||||||
ivec: u8,
|
ivec: u8,
|
||||||
next_vblank: Instant
|
next_vblank: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Duart {
|
impl Default for Duart {
|
||||||
|
@ -154,10 +151,7 @@ impl Default for Duart {
|
||||||
impl Duart {
|
impl Duart {
|
||||||
pub fn new() -> Duart {
|
pub fn new() -> Duart {
|
||||||
Duart {
|
Duart {
|
||||||
ports: [
|
ports: [Port::new(), Port::new()],
|
||||||
Port::new(),
|
|
||||||
Port::new(),
|
|
||||||
],
|
|
||||||
acr: 0,
|
acr: 0,
|
||||||
ipcr: 0x40,
|
ipcr: 0x40,
|
||||||
inprt: 0xb,
|
inprt: 0xb,
|
||||||
|
@ -216,9 +210,10 @@ impl Duart {
|
||||||
_ => (ISTS_TBI, ISTS_RBI),
|
_ => (ISTS_TBI, ISTS_RBI),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ctx.conf & CNF_ETX) != 0 &&
|
if (ctx.conf & CNF_ETX) != 0
|
||||||
(ctx.stat & STS_TXR) == 0 &&
|
&& (ctx.stat & STS_TXR) == 0
|
||||||
(ctx.stat & STS_TXE) == 0 && Instant::now() >= ctx.next_tx
|
&& (ctx.stat & STS_TXE) == 0
|
||||||
|
&& Instant::now() >= ctx.next_tx
|
||||||
{
|
{
|
||||||
let c = ctx.tx_data;
|
let c = ctx.tx_data;
|
||||||
ctx.stat |= STS_TXR;
|
ctx.stat |= STS_TXR;
|
||||||
|
@ -395,7 +390,7 @@ impl Duart {
|
||||||
ctx.stat |= STS_TXE;
|
ctx.stat |= STS_TXE;
|
||||||
ctx.conf &= !CNF_ETX;
|
ctx.conf &= !CNF_ETX;
|
||||||
}
|
}
|
||||||
4 => ctx.stat &= !(STS_FER|STS_PER|STS_OER),
|
4 => ctx.stat &= !(STS_FER | STS_PER | STS_OER),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -428,9 +423,7 @@ impl Device for Duart {
|
||||||
ctx.mode_ptr = (ctx.mode_ptr + 1) % 2;
|
ctx.mode_ptr = (ctx.mode_ptr + 1) % 2;
|
||||||
Ok(val)
|
Ok(val)
|
||||||
}
|
}
|
||||||
CSRA => {
|
CSRA => Ok(self.ports[PORT_0].stat),
|
||||||
Ok(self.ports[PORT_0].stat)
|
|
||||||
}
|
|
||||||
THRA => {
|
THRA => {
|
||||||
let mut ctx = &mut self.ports[PORT_0];
|
let mut ctx = &mut self.ports[PORT_0];
|
||||||
ctx.stat &= !STS_RXR;
|
ctx.stat &= !STS_RXR;
|
||||||
|
@ -445,18 +438,14 @@ impl Device for Duart {
|
||||||
self.istat &= !ISTS_IPC;
|
self.istat &= !ISTS_IPC;
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
ISR_MASK => {
|
ISR_MASK => Ok(self.istat),
|
||||||
Ok(self.istat)
|
|
||||||
}
|
|
||||||
MR12B => {
|
MR12B => {
|
||||||
let mut ctx = &mut self.ports[PORT_1];
|
let mut ctx = &mut self.ports[PORT_1];
|
||||||
let val = ctx.mode[ctx.mode_ptr];
|
let val = ctx.mode[ctx.mode_ptr];
|
||||||
ctx.mode_ptr = (ctx.mode_ptr + 1) % 2;
|
ctx.mode_ptr = (ctx.mode_ptr + 1) % 2;
|
||||||
Ok(val)
|
Ok(val)
|
||||||
}
|
}
|
||||||
CSRB => {
|
CSRB => Ok(self.ports[PORT_1].stat),
|
||||||
Ok(self.ports[PORT_1].stat)
|
|
||||||
}
|
|
||||||
THRB => {
|
THRB => {
|
||||||
let mut ctx = &mut self.ports[PORT_1];
|
let mut ctx = &mut self.ports[PORT_1];
|
||||||
ctx.stat &= !STS_RXR;
|
ctx.stat &= !STS_RXR;
|
||||||
|
@ -464,9 +453,7 @@ impl Device for Duart {
|
||||||
self.ivec &= !KEYBOARD_INT;
|
self.ivec &= !KEYBOARD_INT;
|
||||||
Ok(ctx.rx_data)
|
Ok(ctx.rx_data)
|
||||||
}
|
}
|
||||||
IP_OPCR => {
|
IP_OPCR => Ok(self.inprt),
|
||||||
Ok(self.inprt)
|
|
||||||
}
|
|
||||||
_ => Ok(0),
|
_ => Ok(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
pub mod bus;
|
pub mod bus;
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
pub mod dmd;
|
pub mod dmd;
|
||||||
|
pub mod duart;
|
||||||
pub mod err;
|
pub mod err;
|
||||||
pub mod instr;
|
pub mod instr;
|
||||||
pub mod mem;
|
pub mod mem;
|
||||||
pub mod duart;
|
|
||||||
pub mod mouse;
|
pub mod mouse;
|
||||||
pub mod rom_hi;
|
pub mod rom_hi;
|
||||||
pub mod rom_lo;
|
pub mod rom_lo;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
|
@ -6,9 +6,9 @@ use crate::err::BusError;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::fmt::Error;
|
use std::fmt::Error;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
|
use std::ops::Range;
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use std::ops::Range;
|
|
||||||
|
|
||||||
pub struct Mem {
|
pub struct Mem {
|
||||||
address_range: Range<usize>,
|
address_range: Range<usize>,
|
||||||
|
@ -21,7 +21,7 @@ pub struct Mem {
|
||||||
impl Mem {
|
impl Mem {
|
||||||
pub fn new(start_address: usize, len: usize, is_read_only: bool) -> Mem {
|
pub fn new(start_address: usize, len: usize, is_read_only: bool) -> Mem {
|
||||||
Mem {
|
Mem {
|
||||||
address_range: start_address..start_address+len,
|
address_range: start_address..start_address + len,
|
||||||
len,
|
len,
|
||||||
ram: vec![0; len],
|
ram: vec![0; len],
|
||||||
is_read_only,
|
is_read_only,
|
||||||
|
@ -91,7 +91,7 @@ impl Device for Mem {
|
||||||
u32::from(self.ram[offset]).wrapping_shl(24)
|
u32::from(self.ram[offset]).wrapping_shl(24)
|
||||||
| u32::from(self.ram[offset + 1]).wrapping_shl(16)
|
| u32::from(self.ram[offset + 1]).wrapping_shl(16)
|
||||||
| u32::from(self.ram[offset + 2]).wrapping_shl(8)
|
| u32::from(self.ram[offset + 2]).wrapping_shl(8)
|
||||||
| u32::from(self.ram[offset + 3])
|
| u32::from(self.ram[offset + 3]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
27
src/mouse.rs
27
src/mouse.rs
|
@ -1,7 +1,7 @@
|
||||||
#![allow(clippy::unreadable_literal)]
|
#![allow(clippy::unreadable_literal)]
|
||||||
|
|
||||||
use crate::bus::Device;
|
|
||||||
use crate::bus::AccessCode;
|
use crate::bus::AccessCode;
|
||||||
|
use crate::bus::Device;
|
||||||
use crate::err::BusError;
|
use crate::err::BusError;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ impl Device for Mouse {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_half(&mut self, address: usize, _access: AccessCode) -> Result<u16, BusError> {
|
fn read_half(&mut self, address: usize, _access: AccessCode) -> Result<u16, BusError> {
|
||||||
match address-START_ADDRESS {
|
match address - START_ADDRESS {
|
||||||
0 => Ok(self.y),
|
0 => Ok(self.y),
|
||||||
2 => Ok(self.x),
|
2 => Ok(self.x),
|
||||||
_ => Err(BusError::NoDevice(address as u32)),
|
_ => Err(BusError::NoDevice(address as u32)),
|
||||||
|
@ -59,19 +59,34 @@ impl Device for Mouse {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_byte(&mut self, _address: usize, _val: u8, _access: AccessCode) -> Result<(), BusError> {
|
fn write_byte(
|
||||||
|
&mut self,
|
||||||
|
_address: usize,
|
||||||
|
_val: u8,
|
||||||
|
_access: AccessCode,
|
||||||
|
) -> Result<(), BusError> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_half(&mut self, _address: usize, _val: u16, _access: AccessCode) -> Result<(), BusError> {
|
fn write_half(
|
||||||
|
&mut self,
|
||||||
|
_address: usize,
|
||||||
|
_val: u16,
|
||||||
|
_access: AccessCode,
|
||||||
|
) -> Result<(), BusError> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_word(&mut self, _address: usize, _val: u32, _access: AccessCode) -> Result<(), BusError> {
|
fn write_word(
|
||||||
|
&mut self,
|
||||||
|
_address: usize,
|
||||||
|
_val: u32,
|
||||||
|
_access: AccessCode,
|
||||||
|
) -> Result<(), BusError> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(&mut self, _address: usize, _data: &[u8]) -> Result<(), BusError> {
|
fn load(&mut self, _address: usize, _data: &[u8]) -> Result<(), BusError> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
7215
src/rom_hi.rs
7215
src/rom_hi.rs
File diff suppressed because it is too large
Load Diff
7215
src/rom_lo.rs
7215
src/rom_lo.rs
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue