External memory fault handling
This commit is contained in:
parent
048cfae67b
commit
9a3ca2e84f
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "dmd_core"
|
||||
description = "AT&T / Teletype DMD 5620 Terminal Emulator - Core Library"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
authors = ["Seth Morabito <web@loomcom.com>"]
|
||||
homepage = "https://github.com/sethm/dmd_core"
|
||||
repository = "https://github.com/sethm/dmd_core"
|
||||
|
|
11
README.md
11
README.md
|
@ -25,6 +25,17 @@ Note that there is no user interface: This is a back-end library only.
|
|||
It may be used as a component to build a fully-fledged emulator,
|
||||
however.
|
||||
|
||||
## Changelog
|
||||
|
||||
0.3.1: Added exception handling for memory errors, and a `run` function
|
||||
to free-run the CPU for a given number of steps.
|
||||
|
||||
0.3.0: Breaking change. Charater RX from RS-232 and keyboard are now
|
||||
handled by internal queues, and no longer return `DuartError`
|
||||
on failure.
|
||||
|
||||
0.2.1: Initial release.
|
||||
|
||||
## Emulator Reference Implementation
|
||||
|
||||
For a reference implementation emulator that uses this library,
|
||||
|
|
|
@ -57,7 +57,6 @@ pub trait Device: Send + Sync + Debug {
|
|||
pub struct Bus {
|
||||
rom: Mem,
|
||||
duart: Duart,
|
||||
scc: Mem, // TODO: Remove
|
||||
mouse: Mouse,
|
||||
vid: Mem, // TODO: Figure out what device this really is
|
||||
bbram: Mem, // TODO: change to BBRAM when implemented
|
||||
|
@ -69,7 +68,6 @@ impl Bus {
|
|||
Bus {
|
||||
rom: Mem::new(0, 0x20000, true),
|
||||
duart: Duart::new(),
|
||||
scc: Mem::new(0x300000, 0x100, false),
|
||||
mouse: Mouse::new(),
|
||||
vid: Mem::new(0x500000, 0x2, false),
|
||||
bbram: Mem::new(0x600000, 0x2000, false),
|
||||
|
@ -86,10 +84,6 @@ impl Bus {
|
|||
return Ok(&mut self.duart);
|
||||
}
|
||||
|
||||
if address >= 0x300000 && address < 0x300100 {
|
||||
return Ok(&mut self.scc);
|
||||
}
|
||||
|
||||
if address >= 0x400000 && address < 0x400004 {
|
||||
return Ok(&mut self.mouse);
|
||||
}
|
||||
|
|
71
src/cpu.rs
71
src/cpu.rs
|
@ -13,12 +13,14 @@ const F_I: u32 = 0x00000080;
|
|||
const F_R: u32 = 0x00000100;
|
||||
const F_PM: u32 = 0x00000600;
|
||||
const F_CM: u32 = 0x00001800;
|
||||
const F_IPL: u32 = 0x0001e000;
|
||||
const F_C: u32 = 0x00040000;
|
||||
const F_V: u32 = 0x00080000;
|
||||
const F_Z: u32 = 0x00100000;
|
||||
const F_N: u32 = 0x00200000;
|
||||
|
||||
const O_ET: u32 = 0;
|
||||
const O_TM: u32 = 2;
|
||||
const O_ISC: u32 = 3;
|
||||
|
||||
///
|
||||
|
@ -45,6 +47,10 @@ const IPL_TABLE: [u32; 64] = [
|
|||
|
||||
const WE32100_VERSION: u32 = 0x1a;
|
||||
|
||||
pub enum ExceptionType {
|
||||
ExternlMemory
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||
pub enum AddrMode {
|
||||
None,
|
||||
|
@ -1629,6 +1635,67 @@ impl Cpu {
|
|||
Ok(pc_increment)
|
||||
}
|
||||
|
||||
fn gate(&mut self, bus: &mut Bus, index1: usize, index2: usize) -> Result<(), CpuException> {
|
||||
let gate_l2 = self.read_w(index1, AccessCode::AddressFetch)? + index2;
|
||||
|
||||
let new_psw = self.read_w(gate_l2, AccessCode::AddressFetch)?;
|
||||
|
||||
// Clear state in the new PSw
|
||||
new_psw &= !(F_PM | F_IPL | F_R | F_ISC | F_TM | F_ET);
|
||||
|
||||
// Set new state
|
||||
new_psw |= (self.r[R_PSW] & F_CM) >> 2; // PM (set from CM)
|
||||
new_psw |= self.r[R_PSW] & F_IPL;
|
||||
new_psw |= self.r[R_PSW] & F_R;
|
||||
|
||||
// Set new ISC, TM, and ET to 7, 1, 3 respectively
|
||||
new_psw |= 7 << O_ISC;
|
||||
new_psw |= 1 << O_TM;
|
||||
new_psw |= 3 << O_ET;
|
||||
|
||||
// Finally, set the new PC and PSW
|
||||
self.r[R_PC] = bus.read_w(gate_l2 + 4, AccessCode::AddressFetch)?;
|
||||
self.r[R_PSW] = new_psw;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_exception(&mut self, bus: &mut Bus, exc: ExceptionType) -> Result<(), CpuException>) {
|
||||
let (et, isc) = match exc {
|
||||
Exception::ExternalMemory => (3, 5)
|
||||
};
|
||||
|
||||
// TODO: Don't trap integer overflow
|
||||
self.r[R_PSW] &= !F_ET;
|
||||
self.r[R_PSW] &= !F_ISC;
|
||||
self.r[R_PSW] |= et;
|
||||
self.r[R_PSW] |= isc << O_ISC;
|
||||
|
||||
// Handle the exception
|
||||
match exc {
|
||||
Exception::ExternalMemory => {
|
||||
// TODO: Check for stac-bounds exception here
|
||||
|
||||
// Push the address of the next instruction to the stack.
|
||||
bus.write_word(self.r[R_SP] as usize, self.r[R_PC])?;
|
||||
|
||||
// Write 0, 3 to the PSW ET and ISC fields
|
||||
self.r[R_PSW] &= !(F_ET | F_ISC);
|
||||
self.r[R_PSW] |= 3 << O_ISC;
|
||||
|
||||
// Push the PSW to the stack
|
||||
bus.write_w(self.r[R_SP] as usize + 4, self.r[R_PSW])?;
|
||||
|
||||
self.gate(bus, 0usize, isc as usize << O_ISC)?;
|
||||
|
||||
// Finish stack push
|
||||
self.r[R_SP] += 8;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Step the CPU by one instruction.
|
||||
pub fn step(&mut self, bus: &mut Bus) {
|
||||
// TODO: On CPU Exception or Bus Error, handle each error with the appropriate exception handler routine
|
||||
|
@ -1638,7 +1705,9 @@ impl Cpu {
|
|||
Err(CpuError::Bus(BusError::Permission)) => {}
|
||||
Err(CpuError::Bus(BusError::NoDevice(_)))
|
||||
| Err(CpuError::Bus(BusError::Read(_)))
|
||||
| Err(CpuError::Bus(BusError::Write(_))) => {}
|
||||
| Err(CpuError::Bus(BusError::Write(_))) => {
|
||||
self.on_exception(bus, Exception::ExternalMemory)
|
||||
}
|
||||
Err(CpuError::Exception(CpuException::IllegalOpcode)) => {}
|
||||
Err(CpuError::Exception(CpuException::InvalidDescriptor)) => {}
|
||||
Err(CpuError::Exception(CpuException::PrivilegedOpcode)) => {}
|
||||
|
|
|
@ -87,6 +87,15 @@ impl Dmd {
|
|||
self.cpu.step(&mut self.bus);
|
||||
}
|
||||
|
||||
///
|
||||
/// Free-run the CPU for a given number of steps.
|
||||
///
|
||||
pub fn run(&mut self, count: usize) {
|
||||
for _ in 0..count {
|
||||
self.cpu.step(&mut self.bus);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Step the terminal's CPU one time, returning any error that may have occured.
|
||||
/// Useful for debugging.
|
||||
|
|
Loading…
Reference in New Issue