Work
This commit is contained in:
parent
94684dbc4f
commit
e73dd88635
10
src/disas.rs
10
src/disas.rs
@ -53,9 +53,9 @@ enum MiscMode {
|
|||||||
|
|
||||||
pub fn disasm<T>(
|
pub fn disasm<T>(
|
||||||
pc: u32,
|
pc: u32,
|
||||||
byte_read: &mut dyn FnMut(u32) -> Result<u8, T>,
|
word_read: &mut dyn FnMut(u32) -> Result<u16, T>,
|
||||||
) -> Result<(Instruction, u32), DisassemblyError<T>> {
|
) -> Result<(Instruction, u32), DisassemblyError<T>> {
|
||||||
let mut disasm = Disasm { pc, byte_read };
|
let mut disasm = Disasm { pc, word_read };
|
||||||
Ok((disasm.disasm()?, disasm.pc))
|
Ok((disasm.disasm()?, disasm.pc))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ impl<T: Display> Display for DisassemblyError<T> {
|
|||||||
|
|
||||||
struct Disasm<'a, T> {
|
struct Disasm<'a, T> {
|
||||||
pc: u32,
|
pc: u32,
|
||||||
byte_read: &'a mut dyn FnMut(u32) -> Result<u8, T>,
|
word_read: &'a mut dyn FnMut(u32) -> Result<u16, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Disasm<'_, T> {
|
impl<T> Disasm<'_, T> {
|
||||||
@ -926,9 +926,7 @@ impl<T> Disasm<'_, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_prog_word(&mut self) -> Result<u16, DisassemblyError<T>> {
|
fn read_prog_word(&mut self) -> Result<u16, DisassemblyError<T>> {
|
||||||
let word = ((self.byte_read)(self.pc).map_err(|e| DisassemblyError::ReadError(e))? as u16)
|
let word = (self.word_read)(self.pc).map_err(|e| DisassemblyError::ReadError(e))?;
|
||||||
<< 8
|
|
||||||
| ((self.byte_read)(self.pc + 1).map_err(|e| DisassemblyError::ReadError(e))? as u16);
|
|
||||||
self.pc += 2;
|
self.pc += 2;
|
||||||
Ok(word)
|
Ok(word)
|
||||||
}
|
}
|
||||||
|
377
src/m68k.rs
377
src/m68k.rs
@ -24,7 +24,7 @@ impl Display for BusError {
|
|||||||
|
|
||||||
impl Error for BusError {}
|
impl Error for BusError {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum BusErrorCause {
|
pub enum BusErrorCause {
|
||||||
ReadingByte,
|
ReadingByte,
|
||||||
ReadingWord,
|
ReadingWord,
|
||||||
@ -45,7 +45,7 @@ impl Display for BusErrorCause {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct DetailedBusError {
|
pub struct DetailedBusError {
|
||||||
address: u32,
|
address: u32,
|
||||||
cause: BusErrorCause,
|
cause: BusErrorCause,
|
||||||
@ -62,6 +62,66 @@ impl Display for DetailedBusError {
|
|||||||
|
|
||||||
impl Error for DetailedBusError {}
|
impl Error for DetailedBusError {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum MemCycleInfo {
|
||||||
|
ReadByte {
|
||||||
|
address: u32,
|
||||||
|
result: Result<u8, DetailedBusError>,
|
||||||
|
},
|
||||||
|
ReadWord {
|
||||||
|
address: u32,
|
||||||
|
result: Result<u16, DetailedBusError>,
|
||||||
|
},
|
||||||
|
WriteByte {
|
||||||
|
address: u32,
|
||||||
|
data: u8,
|
||||||
|
result: Result<(), DetailedBusError>,
|
||||||
|
},
|
||||||
|
WriteWord {
|
||||||
|
address: u32,
|
||||||
|
data: u16,
|
||||||
|
result: Result<(), DetailedBusError>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemCycleInfo {
|
||||||
|
fn is_err(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
MemCycleInfo::ReadByte { result, .. } => result.is_err(),
|
||||||
|
MemCycleInfo::ReadWord { result, .. } => result.is_err(),
|
||||||
|
MemCycleInfo::WriteByte { result, .. } => result.is_err(),
|
||||||
|
MemCycleInfo::WriteWord { result, .. } => result.is_err(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unwrap_err(&self) -> DetailedBusError {
|
||||||
|
match self {
|
||||||
|
MemCycleInfo::ReadByte { result, .. } => result.unwrap_err(),
|
||||||
|
MemCycleInfo::ReadWord { result, .. } => result.unwrap_err(),
|
||||||
|
MemCycleInfo::WriteByte { result, .. } => result.unwrap_err(),
|
||||||
|
MemCycleInfo::WriteWord { result, .. } => result.unwrap_err(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn address(&self) -> u32 {
|
||||||
|
*(match self {
|
||||||
|
MemCycleInfo::ReadByte { address, .. } => address,
|
||||||
|
MemCycleInfo::ReadWord { address, .. } => address,
|
||||||
|
MemCycleInfo::WriteByte { address, .. } => address,
|
||||||
|
MemCycleInfo::WriteWord { address, .. } => address,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_get_write_data(&self) -> Option<u16> {
|
||||||
|
match self {
|
||||||
|
MemCycleInfo::ReadByte { .. } => None,
|
||||||
|
MemCycleInfo::ReadWord { .. } => None,
|
||||||
|
MemCycleInfo::WriteByte { data, .. } => Some(*data as u16),
|
||||||
|
MemCycleInfo::WriteWord { data, .. } => Some(*data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct M68K {
|
pub struct M68K {
|
||||||
dregs: [u32; 8],
|
dregs: [u32; 8],
|
||||||
@ -73,6 +133,10 @@ pub struct M68K {
|
|||||||
bus: Backplane,
|
bus: Backplane,
|
||||||
pub stopped: bool,
|
pub stopped: bool,
|
||||||
initialized: bool,
|
initialized: bool,
|
||||||
|
stored_mem_cycles: Vec<MemCycleInfo>,
|
||||||
|
use_stored_mem_cycles: bool,
|
||||||
|
store_mem_cycles: bool,
|
||||||
|
handling_bus_error: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for M68K {
|
impl Display for M68K {
|
||||||
@ -120,6 +184,10 @@ impl M68K {
|
|||||||
bus,
|
bus,
|
||||||
stopped: false,
|
stopped: false,
|
||||||
initialized: false,
|
initialized: false,
|
||||||
|
stored_mem_cycles: Vec::new(),
|
||||||
|
use_stored_mem_cycles: false,
|
||||||
|
store_mem_cycles: true,
|
||||||
|
handling_bus_error: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +202,10 @@ impl M68K {
|
|||||||
self.pc = (u32::from(pc_high) << 16) | u32::from(pc_low);
|
self.pc = (u32::from(pc_high) << 16) | u32::from(pc_low);
|
||||||
self.sr = 0x2700 | self.sr & 0xFF;
|
self.sr = 0x2700 | self.sr & 0xFF;
|
||||||
self.stopped = false;
|
self.stopped = false;
|
||||||
|
self.stored_mem_cycles.clear();
|
||||||
|
self.use_stored_mem_cycles = false;
|
||||||
|
self.handling_bus_error = false;
|
||||||
|
self.store_mem_cycles = true;
|
||||||
self.initialized = true;
|
self.initialized = true;
|
||||||
} else {
|
} else {
|
||||||
self.stopped = true;
|
self.stopped = true;
|
||||||
@ -145,14 +217,57 @@ impl M68K {
|
|||||||
loc: u32,
|
loc: u32,
|
||||||
) -> Result<(Instruction, u32), DisassemblyError<DetailedBusError>> {
|
) -> Result<(Instruction, u32), DisassemblyError<DetailedBusError>> {
|
||||||
disas::disasm(loc, &mut |addr| {
|
disas::disasm(loc, &mut |addr| {
|
||||||
self.bus.read_byte(addr).map_err(|_| DetailedBusError {
|
self.read_word(addr).map_err(|err| DetailedBusError {
|
||||||
address: addr,
|
|
||||||
cause: BusErrorCause::ReadingInstruction,
|
cause: BusErrorCause::ReadingInstruction,
|
||||||
|
..err
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(&mut self) -> Result<(), DetailedBusError> {
|
pub fn step(&mut self) {
|
||||||
|
let Err(bus_error) = self.step_ret_berr() else {
|
||||||
|
self.stored_mem_cycles.clear();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if self.handling_bus_error {
|
||||||
|
self.stopped = true;
|
||||||
|
self.stored_mem_cycles.clear();
|
||||||
|
self.use_stored_mem_cycles = false;
|
||||||
|
self.handling_bus_error = false;
|
||||||
|
self.store_mem_cycles = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.handling_bus_error = true;
|
||||||
|
self.store_mem_cycles = false;
|
||||||
|
let (write, ins, byte_access) = match bus_error.cause {
|
||||||
|
BusErrorCause::ReadingByte => (false, false, true),
|
||||||
|
BusErrorCause::WritingByte => (true, false, true),
|
||||||
|
BusErrorCause::ReadingWord => (false, false, false),
|
||||||
|
BusErrorCause::WritingWord => (true, false, false),
|
||||||
|
BusErrorCause::ReadingInstruction => (false, true, false),
|
||||||
|
};
|
||||||
|
let stored_mem_cycles_len = self.stored_mem_cycles.len();
|
||||||
|
let last_cycle = &self.stored_mem_cycles[stored_mem_cycles_len - 1];
|
||||||
|
if self
|
||||||
|
.berr_trap(
|
||||||
|
write,
|
||||||
|
ins,
|
||||||
|
byte_access,
|
||||||
|
bus_error.address,
|
||||||
|
last_cycle.try_get_write_data().unwrap_or(0),
|
||||||
|
)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
self.stopped = true;
|
||||||
|
self.stored_mem_cycles.clear();
|
||||||
|
self.use_stored_mem_cycles = false;
|
||||||
|
self.handling_bus_error = false;
|
||||||
|
self.store_mem_cycles = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step_ret_berr(&mut self) -> Result<(), DetailedBusError> {
|
||||||
if self.stopped {
|
if self.stopped {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -471,11 +586,87 @@ impl M68K {
|
|||||||
Instruction::Rte => {
|
Instruction::Rte => {
|
||||||
self.sr = self.pop(Size::Word)? as u16;
|
self.sr = self.pop(Size::Word)? as u16;
|
||||||
self.pc = self.pop(Size::Long)?;
|
self.pc = self.pop(Size::Long)?;
|
||||||
self.pop(Size::Long)?; //Discard format + vector offset
|
let format = (self.pop(Size::Long)? & 0xf000) >> 12;
|
||||||
|
if format == 8 {
|
||||||
|
let special_status_word = self.pop(Size::Long)?;
|
||||||
|
let fault_address = self.pop(Size::Long)?;
|
||||||
|
let _ = self.pop(Size::Word)?;
|
||||||
|
let _data_output_buf = self.pop(Size::Word)? as u16;
|
||||||
|
let _ = self.pop(Size::Word)?;
|
||||||
|
let data_input_buf = self.pop(Size::Word)? as u16;
|
||||||
|
let _ = self.pop(Size::Word)?;
|
||||||
|
let instruction_input_buf = self.pop(Size::Word)? as u16;
|
||||||
|
// Internal info
|
||||||
|
let _ = self.pop(Size::Long)?;
|
||||||
|
let _ = self.pop(Size::Long)?;
|
||||||
|
let _ = self.pop(Size::Long)?;
|
||||||
|
let _ = self.pop(Size::Long)?;
|
||||||
|
let _ = self.pop(Size::Long)?;
|
||||||
|
let _ = self.pop(Size::Long)?;
|
||||||
|
let _ = self.pop(Size::Long)?;
|
||||||
|
let _ = self.pop(Size::Long)?;
|
||||||
|
let ssw_write = (special_status_word >> 8) > 1;
|
||||||
|
let ssw_byte_access = (special_status_word >> 9) > 1;
|
||||||
|
let ssw_highbyte = (special_status_word >> 10) > 1;
|
||||||
|
let ssw_ins = (special_status_word >> 13) > 1;
|
||||||
|
let ssw_rerrun = (special_status_word >> 15) > 1;
|
||||||
|
let stored_mem_cycles_len = self.stored_mem_cycles.len();
|
||||||
|
let last_cycle = &mut self.stored_mem_cycles[stored_mem_cycles_len - 1];
|
||||||
|
if !ssw_rerrun {
|
||||||
|
if last_cycle.is_err() {
|
||||||
|
self.stored_mem_cycles.pop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if fault_address != last_cycle.address() {
|
||||||
|
panic!("Recorded fault address did not match cycle address");
|
||||||
|
};
|
||||||
|
let (expected_write, expected_ins, expected_byte_access) =
|
||||||
|
match last_cycle.unwrap_err().cause {
|
||||||
|
BusErrorCause::ReadingByte => (false, false, true),
|
||||||
|
BusErrorCause::WritingByte => (true, false, true),
|
||||||
|
BusErrorCause::ReadingWord => (false, false, false),
|
||||||
|
BusErrorCause::WritingWord => (true, false, false),
|
||||||
|
BusErrorCause::ReadingInstruction => (false, true, false),
|
||||||
|
};
|
||||||
|
if ssw_write != expected_write {
|
||||||
|
panic!("Write flag in the SSW did not match kind of cycle when returning from handling bus error (got {}, expected {})", ssw_write, expected_write);
|
||||||
|
};
|
||||||
|
if ssw_ins != expected_ins {
|
||||||
|
panic!("Instruction flag in the SSW did not match kind of cycle when returning from handling bus error (got {}, expected {})", ssw_ins, expected_ins);
|
||||||
|
};
|
||||||
|
if ssw_byte_access != expected_byte_access {
|
||||||
|
panic!("Byte access flag in the SSW did not match kind of cycle when returning from handling bus error (got {}, expected {})", ssw_byte_access, expected_byte_access);
|
||||||
|
};
|
||||||
|
let input_buf = if ssw_ins {
|
||||||
|
instruction_input_buf
|
||||||
|
} else {
|
||||||
|
data_input_buf
|
||||||
|
};
|
||||||
|
match last_cycle {
|
||||||
|
MemCycleInfo::ReadByte { ref mut result, .. } => {
|
||||||
|
*result = Ok(if ssw_highbyte {
|
||||||
|
(input_buf >> 8) as u8
|
||||||
|
} else {
|
||||||
|
input_buf as u8
|
||||||
|
});
|
||||||
|
}
|
||||||
|
MemCycleInfo::WriteByte { ref mut result, .. } => {
|
||||||
|
*result = Ok(());
|
||||||
|
}
|
||||||
|
MemCycleInfo::ReadWord { ref mut result, .. } => {
|
||||||
|
*result = Ok(input_buf);
|
||||||
|
}
|
||||||
|
MemCycleInfo::WriteWord { ref mut result, .. } => {
|
||||||
|
*result = Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Instruction::Rts => self.pc = self.pop(Size::Long)?,
|
Instruction::Rts => self.pc = self.pop(Size::Long)?,
|
||||||
Instruction::Trapv => {
|
Instruction::Trapv => {
|
||||||
if self.sr & 0x2 > 0 {
|
if self.sr & 0x2 > 0 {
|
||||||
|
let _ = self.pop(Size::Long);
|
||||||
self.trap(7)?;
|
self.trap(7)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1218,20 +1409,73 @@ impl M68K {
|
|||||||
|
|
||||||
fn read_byte(&mut self, address: u32) -> Result<u8, DetailedBusError> {
|
fn read_byte(&mut self, address: u32) -> Result<u8, DetailedBusError> {
|
||||||
let address = address & 0xFF_FFFF;
|
let address = address & 0xFF_FFFF;
|
||||||
self.bus.read_byte(address).map_err(|_| DetailedBusError {
|
if self.use_stored_mem_cycles {
|
||||||
|
let cycle = self.stored_mem_cycles.remove(0);
|
||||||
|
if let MemCycleInfo::ReadByte {
|
||||||
|
address: stored_address,
|
||||||
|
result,
|
||||||
|
} = cycle
|
||||||
|
{
|
||||||
|
if stored_address == address {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
panic!("Stored bus cycle has wrong address when reading byte (reading {:#x}, stored {:#x})", address, stored_address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.stored_mem_cycles.is_empty() {
|
||||||
|
self.store_mem_cycles = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let result = self.bus.read_byte(address).map_err(|_| DetailedBusError {
|
||||||
address,
|
address,
|
||||||
cause: BusErrorCause::ReadingByte,
|
cause: BusErrorCause::ReadingByte,
|
||||||
})
|
});
|
||||||
|
if self.store_mem_cycles {
|
||||||
|
self.stored_mem_cycles.push(MemCycleInfo::ReadByte {
|
||||||
|
address,
|
||||||
|
result: result.clone(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_byte(&mut self, address: u32, data: u8) -> Result<(), DetailedBusError> {
|
fn write_byte(&mut self, address: u32, data: u8) -> Result<(), DetailedBusError> {
|
||||||
let address = address & 0xFF_FFFF;
|
let address = address & 0xFF_FFFF;
|
||||||
self.bus
|
if self.use_stored_mem_cycles {
|
||||||
|
let cycle = self.stored_mem_cycles.remove(0);
|
||||||
|
if let MemCycleInfo::WriteByte {
|
||||||
|
address: stored_address,
|
||||||
|
data: stored_data,
|
||||||
|
result,
|
||||||
|
} = cycle
|
||||||
|
{
|
||||||
|
if stored_address == address && stored_data == data {
|
||||||
|
return result;
|
||||||
|
} else if stored_address != address {
|
||||||
|
panic!("Stored bus cycle has wrong address when writing byte (reading {:#x}, stored {:#x})", address, stored_address);
|
||||||
|
} else if stored_data != data {
|
||||||
|
panic!("Stored bus cycle has wrong data when writing byte (writing {:#x}, stored {:#x})", data, stored_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.stored_mem_cycles.is_empty() {
|
||||||
|
self.store_mem_cycles = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let result = self
|
||||||
|
.bus
|
||||||
.write_byte(address, data)
|
.write_byte(address, data)
|
||||||
.map_err(|_| DetailedBusError {
|
.map_err(|_| DetailedBusError {
|
||||||
address,
|
address,
|
||||||
cause: BusErrorCause::WritingByte,
|
cause: BusErrorCause::WritingByte,
|
||||||
})
|
});
|
||||||
|
if self.store_mem_cycles {
|
||||||
|
self.stored_mem_cycles.push(MemCycleInfo::WriteByte {
|
||||||
|
address,
|
||||||
|
data,
|
||||||
|
result: result.clone(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_word(&mut self, address: u32) -> Result<u16, DetailedBusError> {
|
fn read_word(&mut self, address: u32) -> Result<u16, DetailedBusError> {
|
||||||
@ -1239,23 +1483,73 @@ impl M68K {
|
|||||||
if address & 0x1 != 0 {
|
if address & 0x1 != 0 {
|
||||||
self.trap(3)?;
|
self.trap(3)?;
|
||||||
}
|
}
|
||||||
self.bus.read_word(address).map_err(|_| DetailedBusError {
|
if self.use_stored_mem_cycles {
|
||||||
|
let cycle = self.stored_mem_cycles.remove(0);
|
||||||
|
if let MemCycleInfo::ReadWord {
|
||||||
|
address: stored_address,
|
||||||
|
result,
|
||||||
|
} = cycle
|
||||||
|
{
|
||||||
|
if stored_address == address {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
panic!("Stored bus cycle has wrong address when reading word (reading {:#x}, stored {:#x})", address, stored_address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.stored_mem_cycles.is_empty() {
|
||||||
|
self.store_mem_cycles = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let result = self.bus.read_word(address).map_err(|_| DetailedBusError {
|
||||||
address,
|
address,
|
||||||
cause: BusErrorCause::ReadingWord,
|
cause: BusErrorCause::ReadingWord,
|
||||||
})
|
});
|
||||||
|
if self.store_mem_cycles {
|
||||||
|
self.stored_mem_cycles.push(MemCycleInfo::ReadWord {
|
||||||
|
address,
|
||||||
|
result: result.clone(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_word(&mut self, address: u32, data: u16) -> Result<(), DetailedBusError> {
|
fn write_word(&mut self, address: u32, data: u16) -> Result<(), DetailedBusError> {
|
||||||
let address = address & 0xFF_FFFF;
|
let address = address & 0xFF_FFFF;
|
||||||
if address & 0x1 != 0 {
|
if self.use_stored_mem_cycles {
|
||||||
self.trap(3)?;
|
let cycle = self.stored_mem_cycles.remove(0);
|
||||||
|
if let MemCycleInfo::WriteWord {
|
||||||
|
address: stored_address,
|
||||||
|
data: stored_data,
|
||||||
|
result,
|
||||||
|
} = cycle
|
||||||
|
{
|
||||||
|
if stored_address == address && stored_data == data {
|
||||||
|
return result;
|
||||||
|
} else if stored_address != address {
|
||||||
|
panic!("Stored bus cycle has wrong address when writing word (reading {:#x}, stored {:#x})", address, stored_address);
|
||||||
|
} else if stored_data != data {
|
||||||
|
panic!("Stored bus cycle has wrong data when writing word (writing {:#x}, stored {:#x})", data, stored_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.stored_mem_cycles.is_empty() {
|
||||||
|
self.store_mem_cycles = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.bus
|
let result = self
|
||||||
|
.bus
|
||||||
.write_word(address, data)
|
.write_word(address, data)
|
||||||
.map_err(|_| DetailedBusError {
|
.map_err(|_| DetailedBusError {
|
||||||
address,
|
address,
|
||||||
cause: BusErrorCause::WritingWord,
|
cause: BusErrorCause::WritingWord,
|
||||||
})
|
});
|
||||||
|
if self.store_mem_cycles {
|
||||||
|
self.stored_mem_cycles.push(MemCycleInfo::WriteWord {
|
||||||
|
address,
|
||||||
|
data,
|
||||||
|
result: result.clone(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trim_excess(num: u32, size: Size) -> u32 {
|
fn trim_excess(num: u32, size: Size) -> u32 {
|
||||||
@ -1335,6 +1629,57 @@ impl M68K {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn berr_trap(
|
||||||
|
&mut self,
|
||||||
|
write: bool,
|
||||||
|
ins: bool,
|
||||||
|
byte_access: bool,
|
||||||
|
fault_addr: u32,
|
||||||
|
write_data: u16,
|
||||||
|
) -> Result<(), DetailedBusError> {
|
||||||
|
let new_pc = self.read_address(2 * 4, Size::Long)?;
|
||||||
|
// Version & internal information
|
||||||
|
self.push(0, Size::Long)?;
|
||||||
|
self.push(0, Size::Long)?;
|
||||||
|
self.push(0, Size::Long)?;
|
||||||
|
self.push(0, Size::Long)?;
|
||||||
|
|
||||||
|
self.push(0, Size::Long)?;
|
||||||
|
self.push(0, Size::Long)?;
|
||||||
|
self.push(0, Size::Long)?;
|
||||||
|
self.push(0, Size::Long)?;
|
||||||
|
// Instruction input buffer
|
||||||
|
self.push(0, Size::Word)?;
|
||||||
|
// Unused
|
||||||
|
self.push(0, Size::Word)?;
|
||||||
|
// Data input buffer
|
||||||
|
self.push(0, Size::Word)?;
|
||||||
|
// Unused
|
||||||
|
self.push(0, Size::Word)?;
|
||||||
|
// Data output buffer
|
||||||
|
self.push(write_data as u32, Size::Word)?;
|
||||||
|
// Unused
|
||||||
|
self.push(0, Size::Word)?;
|
||||||
|
// Fault address
|
||||||
|
self.push(fault_addr, Size::Long)?;
|
||||||
|
// Special status word
|
||||||
|
let special_status_word = ((write as u16) << 8)
|
||||||
|
| ((byte_access as u16) << 9)
|
||||||
|
| (((fault_addr & 0x1) as u16) << 10)
|
||||||
|
| (!ins as u16)
|
||||||
|
| (ins as u16);
|
||||||
|
self.push(u32::from(special_status_word), Size::Word)?;
|
||||||
|
// Format & vector
|
||||||
|
self.push(0x8002, Size::Word)?;
|
||||||
|
// Program counter
|
||||||
|
self.push(self.pc, Size::Long)?;
|
||||||
|
// Status register
|
||||||
|
self.push(u32::from(self.sr), Size::Word)?;
|
||||||
|
self.sr |= 0x2000;
|
||||||
|
self.pc = new_pc;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn bus(&self) -> &Backplane {
|
pub fn bus(&self) -> &Backplane {
|
||||||
&self.bus
|
&self.bus
|
||||||
|
26
src/main.rs
26
src/main.rs
@ -89,13 +89,7 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
};
|
};
|
||||||
if args.run {
|
if args.run {
|
||||||
while !state.cpu.stopped {
|
while !state.cpu.stopped {
|
||||||
match state.cpu.step() {
|
state.cpu.step();
|
||||||
Ok(()) => (),
|
|
||||||
Err(e) => {
|
|
||||||
println!("{e}");
|
|
||||||
state.cpu.stopped = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
println!("CPU stopped at PC {:#x}\n{}", state.cpu.pc(), state.cpu);
|
println!("CPU stopped at PC {:#x}\n{}", state.cpu.pc(), state.cpu);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -175,14 +169,7 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
let pc = state.cpu.pc();
|
let pc = state.cpu.pc();
|
||||||
out += &disas_fmt(&mut state.cpu, pc, &state.symbol_tables).0;
|
out += &disas_fmt(&mut state.cpu, pc, &state.symbol_tables).0;
|
||||||
}
|
}
|
||||||
match state.cpu.step() {
|
state.cpu.step();
|
||||||
Ok(_) => (),
|
|
||||||
Err(bus_error) => {
|
|
||||||
println!("{bus_error}");
|
|
||||||
state.cpu.stopped=true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if args.get_flag("print_regs") {
|
if args.get_flag("print_regs") {
|
||||||
out += &format!("{}\n", state.cpu);
|
out += &format!("{}\n", state.cpu);
|
||||||
@ -225,14 +212,7 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
let pc = state.cpu.pc();
|
let pc = state.cpu.pc();
|
||||||
out += &disas_fmt(&mut state.cpu, pc, &state.symbol_tables).0;
|
out += &disas_fmt(&mut state.cpu, pc, &state.symbol_tables).0;
|
||||||
}
|
}
|
||||||
match state.cpu.step() {
|
state.cpu.step();
|
||||||
Ok(_) => (),
|
|
||||||
Err(bus_error) => {
|
|
||||||
println!("{bus_error}");
|
|
||||||
state.cpu.stopped=true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if state.cpu.stopped {
|
if state.cpu.stopped {
|
||||||
out += &format!("CPU stopped at PC {:#x}\n", state.cpu.pc());
|
out += &format!("CPU stopped at PC {:#x}\n", state.cpu.pc());
|
||||||
|
154
src/mmu.rs
154
src/mmu.rs
@ -11,45 +11,34 @@ use crate::{
|
|||||||
|
|
||||||
const ID: u16 = 5;
|
const ID: u16 = 5;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
const TLB_MAPPING_FLAG_VALID: u32 = 0x800;
|
||||||
struct PagingEntry {
|
const MAPPING_FLAG_USER: u32 = 0x4;
|
||||||
frame: u32,
|
const MAPPING_FLAG_WRITABLE: u32 = 0x2;
|
||||||
present: bool,
|
const MAPPING_FLAG_PRESENT: u32 = 0x1;
|
||||||
writable: bool,
|
|
||||||
#[allow(unused)]
|
|
||||||
user: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u32> for PagingEntry {
|
|
||||||
fn from(entry: u32) -> Self {
|
|
||||||
Self {
|
|
||||||
frame: entry >> 12,
|
|
||||||
present: entry & 0x1 == 1,
|
|
||||||
writable: entry & 0x2 == 2,
|
|
||||||
user: entry & 0x4 == 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MmuCard {
|
pub struct MmuCard {
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
cache: [Option<PagingEntry>; 4096],
|
tlb: [u32; 4096],
|
||||||
map_frames: [u32; 4],
|
map_frames: [u32; 4],
|
||||||
map_frames_enabled: [bool; 4],
|
map_frames_enabled: [bool; 4],
|
||||||
tlb_clear_entry: u16,
|
|
||||||
print_debug: bool,
|
print_debug: bool,
|
||||||
|
tlb_high_write_word: u16,
|
||||||
|
tlb_mem_start: u32,
|
||||||
|
tlb_mem_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Card for MmuCard {
|
impl Card for MmuCard {
|
||||||
fn new(_data: toml::Value) -> anyhow::Result<Self> {
|
fn new(_data: toml::Value) -> anyhow::Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
cache: [None; 4096],
|
tlb: [0; 4096],
|
||||||
map_frames: [0; 4],
|
map_frames: [0; 4],
|
||||||
map_frames_enabled: [false; 4],
|
map_frames_enabled: [false; 4],
|
||||||
tlb_clear_entry: 0,
|
|
||||||
print_debug: false,
|
print_debug: false,
|
||||||
|
tlb_high_write_word: 0,
|
||||||
|
tlb_mem_start: 0,
|
||||||
|
tlb_mem_enabled: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +46,38 @@ impl Card for MmuCard {
|
|||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_byte(&mut self, address: u32) -> NullableResult<u8, BusError> {
|
||||||
|
if !self.tlb_mem_enabled || address >> 15 != self.tlb_mem_start {
|
||||||
|
return NullableResult::Null;
|
||||||
|
}
|
||||||
|
let address = address & 0x7FFF;
|
||||||
|
NullableResult::Ok(u32_get_be_byte(
|
||||||
|
self.tlb[(address as usize) >> 2],
|
||||||
|
address as u8 & 0x2,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_byte(&mut self, address: u32, _data: u8) -> NullableResult<(), BusError> {
|
||||||
|
if !self.tlb_mem_enabled || address >> 15 != self.tlb_mem_start {
|
||||||
|
return NullableResult::Null;
|
||||||
|
}
|
||||||
|
NullableResult::Err(BusError)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_word(&mut self, address: u32, data: u16) -> NullableResult<(), BusError> {
|
||||||
|
if !self.tlb_mem_enabled || address >> 15 != self.tlb_mem_start {
|
||||||
|
return NullableResult::Null;
|
||||||
|
}
|
||||||
|
if address & 0x1 == 0 {
|
||||||
|
self.tlb_high_write_word = data;
|
||||||
|
NullableResult::Ok(())
|
||||||
|
} else {
|
||||||
|
self.tlb[(address >> 1) as usize] =
|
||||||
|
((self.tlb_high_write_word as u32) << 16) | (data as u32);
|
||||||
|
NullableResult::Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn read_byte_io(&mut self, address: u8) -> NullableResult<u8, BusError> {
|
fn read_byte_io(&mut self, address: u8) -> NullableResult<u8, BusError> {
|
||||||
match address {
|
match address {
|
||||||
(0x0..=0xF) => {
|
(0x0..=0xF) => {
|
||||||
@ -90,36 +111,36 @@ impl Card for MmuCard {
|
|||||||
3 => {
|
3 => {
|
||||||
self.map_frames_enabled[map_no] = (data & 0x1) > 0;
|
self.map_frames_enabled[map_no] = (data & 0x1) > 0;
|
||||||
for i in map_no * 1024..(map_no + 1) * 1024 {
|
for i in map_no * 1024..(map_no + 1) * 1024 {
|
||||||
self.cache[i] = None;
|
self.tlb[i] = 0x0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(0x10..=0x13) => {
|
// (0x10..=0x13) => {
|
||||||
let offset = (address - 0x10) % 4;
|
// let offset = (address - 0x10) % 4;
|
||||||
match offset {
|
// match offset {
|
||||||
0 => (),
|
// 0 => (),
|
||||||
1 => {
|
// 1 => {
|
||||||
self.tlb_clear_entry = (self.tlb_clear_entry & 0xF) | ((data as u16) << 4);
|
// self.tlb_clear_entry = (self.tlb_clear_entry & 0xF) | ((data as u16) << 4);
|
||||||
}
|
// }
|
||||||
2 => {
|
// 2 => {
|
||||||
self.tlb_clear_entry =
|
// self.tlb_clear_entry =
|
||||||
(self.tlb_clear_entry & 0xFF0) | (((data as u16) & 0xF0) >> 4);
|
// (self.tlb_clear_entry & 0xFF0) | (((data as u16) & 0xF0) >> 4);
|
||||||
}
|
// }
|
||||||
3 => {
|
// 3 => {
|
||||||
if self.print_debug {
|
// if self.print_debug {
|
||||||
println!(
|
// println!(
|
||||||
"Clearing TLB entry {:#x} ( page start {:#x} )",
|
// "Clearing TLB entry {:#x} ( page start {:#x} )",
|
||||||
self.tlb_clear_entry,
|
// self.tlb_clear_entry,
|
||||||
(self.tlb_clear_entry as u32) << 12
|
// (self.tlb_clear_entry as u32) << 12
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
self.cache[self.tlb_clear_entry as usize] = None;
|
// self.tlb[self.tlb_clear_entry as usize] = 0x0;
|
||||||
}
|
// }
|
||||||
_ => unreachable!(),
|
// _ => unreachable!(),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
0x15 => {
|
0x15 => {
|
||||||
self.enabled = (data & 0x1) > 0;
|
self.enabled = (data & 0x1) > 0;
|
||||||
}
|
}
|
||||||
@ -187,14 +208,15 @@ impl Mmu for MmuCard {
|
|||||||
}
|
}
|
||||||
let page = address >> 12;
|
let page = address >> 12;
|
||||||
let offset = address & 0xFFF;
|
let offset = address & 0xFFF;
|
||||||
let entry = if let Some(entry) = self.cache[page as usize] {
|
let tlb_mapping = self.tlb[page as usize];
|
||||||
|
let mapping = if tlb_mapping & TLB_MAPPING_FLAG_VALID > 0 {
|
||||||
if print_debug {
|
if print_debug {
|
||||||
println!("TLB hit");
|
println!("TLB hit");
|
||||||
}
|
}
|
||||||
entry
|
tlb_mapping
|
||||||
} else {
|
} else {
|
||||||
if print_debug {
|
if print_debug {
|
||||||
println!("TLB miss, fetching entry");
|
println!("TLB miss, fetching mapping");
|
||||||
}
|
}
|
||||||
if !self.map_frames_enabled[(page >> 10) as usize] {
|
if !self.map_frames_enabled[(page >> 10) as usize] {
|
||||||
if print_debug {
|
if print_debug {
|
||||||
@ -203,36 +225,34 @@ impl Mmu for MmuCard {
|
|||||||
return NullableResult::Null;
|
return NullableResult::Null;
|
||||||
}
|
}
|
||||||
let map_frame = self.map_frames[(page >> 10) as usize];
|
let map_frame = self.map_frames[(page >> 10) as usize];
|
||||||
let entry_address = (map_frame) | ((page & 0x3FF) << 2);
|
let mapping_address = (map_frame) | ((page & 0x3FF) << 2);
|
||||||
if print_debug {
|
if print_debug {
|
||||||
println!("Entry is at {entry_address:#x}");
|
println!("Mapping is at {mapping_address:#x}");
|
||||||
}
|
}
|
||||||
let entry_hi = backplane.read_word_phys(entry_address)?;
|
let mapping_hi = backplane.read_word_phys(mapping_address)?;
|
||||||
let entry_lo = backplane.read_word_phys(entry_address + 2)?;
|
let mapping_lo = backplane.read_word_phys(mapping_address + 2)?;
|
||||||
let entry = PagingEntry::from((entry_hi as u32) << 16 | entry_lo as u32);
|
let mapping =
|
||||||
self.cache[page as usize] = Some(entry);
|
((mapping_hi as u32) << 16 | mapping_lo as u32) | TLB_MAPPING_FLAG_VALID;
|
||||||
if print_debug {
|
self.tlb[page as usize] = mapping;
|
||||||
println!("Fetched entry {entry:#?}");
|
mapping
|
||||||
}
|
|
||||||
entry
|
|
||||||
};
|
};
|
||||||
if entry.present {
|
if mapping & MAPPING_FLAG_PRESENT > 0 {
|
||||||
if write && !entry.writable {
|
if write && (mapping & MAPPING_FLAG_WRITABLE == 0) {
|
||||||
if print_debug {
|
if print_debug {
|
||||||
println!("Entry not writable");
|
println!("Page not writable");
|
||||||
}
|
}
|
||||||
return NullableResult::Err(BusError);
|
return NullableResult::Err(BusError);
|
||||||
}
|
}
|
||||||
if print_debug {
|
if print_debug {
|
||||||
println!(
|
println!(
|
||||||
"Translation success, translated to {:#x}",
|
"Translation success, translated to {:#x}",
|
||||||
(entry.frame << 12) | offset
|
(mapping & !0xFFF) | offset
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
NullableResult::Ok((entry.frame << 12) | offset)
|
NullableResult::Ok((mapping & !0xFFF) | offset)
|
||||||
} else {
|
} else {
|
||||||
if print_debug {
|
if print_debug {
|
||||||
println!("Entry not present");
|
println!("Page not present");
|
||||||
}
|
}
|
||||||
NullableResult::Null
|
NullableResult::Null
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user