diff --git a/src/disas.rs b/src/disas.rs index e4af1cf..6cf5a0e 100644 --- a/src/disas.rs +++ b/src/disas.rs @@ -53,9 +53,9 @@ enum MiscMode { pub fn disasm( pc: u32, - byte_read: &mut dyn FnMut(u32) -> Result, + word_read: &mut dyn FnMut(u32) -> Result, ) -> Result<(Instruction, u32), DisassemblyError> { - let mut disasm = Disasm { pc, byte_read }; + let mut disasm = Disasm { pc, word_read }; Ok((disasm.disasm()?, disasm.pc)) } @@ -76,7 +76,7 @@ impl Display for DisassemblyError { struct Disasm<'a, T> { pc: u32, - byte_read: &'a mut dyn FnMut(u32) -> Result, + word_read: &'a mut dyn FnMut(u32) -> Result, } impl Disasm<'_, T> { @@ -926,9 +926,7 @@ impl Disasm<'_, T> { } fn read_prog_word(&mut self) -> Result> { - let word = ((self.byte_read)(self.pc).map_err(|e| DisassemblyError::ReadError(e))? as u16) - << 8 - | ((self.byte_read)(self.pc + 1).map_err(|e| DisassemblyError::ReadError(e))? as u16); + let word = (self.word_read)(self.pc).map_err(|e| DisassemblyError::ReadError(e))?; self.pc += 2; Ok(word) } diff --git a/src/m68k.rs b/src/m68k.rs index 2e4ac13..3f5540d 100644 --- a/src/m68k.rs +++ b/src/m68k.rs @@ -24,7 +24,7 @@ impl Display for BusError { impl Error for BusError {} -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub enum BusErrorCause { ReadingByte, ReadingWord, @@ -45,7 +45,7 @@ impl Display for BusErrorCause { } } -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub struct DetailedBusError { address: u32, cause: BusErrorCause, @@ -62,6 +62,66 @@ impl Display for DetailedBusError { impl Error for DetailedBusError {} +#[derive(Debug)] +enum MemCycleInfo { + ReadByte { + address: u32, + result: Result, + }, + ReadWord { + address: u32, + result: Result, + }, + 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 { + match self { + MemCycleInfo::ReadByte { .. } => None, + MemCycleInfo::ReadWord { .. } => None, + MemCycleInfo::WriteByte { data, .. } => Some(*data as u16), + MemCycleInfo::WriteWord { data, .. } => Some(*data), + } + } +} + #[derive(Debug)] pub struct M68K { dregs: [u32; 8], @@ -73,6 +133,10 @@ pub struct M68K { bus: Backplane, pub stopped: bool, initialized: bool, + stored_mem_cycles: Vec, + use_stored_mem_cycles: bool, + store_mem_cycles: bool, + handling_bus_error: bool, } impl Display for M68K { @@ -120,6 +184,10 @@ impl M68K { bus, stopped: 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.sr = 0x2700 | self.sr & 0xFF; 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; } else { self.stopped = true; @@ -145,14 +217,57 @@ impl M68K { loc: u32, ) -> Result<(Instruction, u32), DisassemblyError> { disas::disasm(loc, &mut |addr| { - self.bus.read_byte(addr).map_err(|_| DetailedBusError { - address: addr, + self.read_word(addr).map_err(|err| DetailedBusError { 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 { return Ok(()); } @@ -471,11 +586,87 @@ impl M68K { Instruction::Rte => { self.sr = self.pop(Size::Word)? as u16; 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::Trapv => { if self.sr & 0x2 > 0 { + let _ = self.pop(Size::Long); self.trap(7)?; } } @@ -1218,20 +1409,73 @@ impl M68K { fn read_byte(&mut self, address: u32) -> Result { 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, 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> { 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) .map_err(|_| DetailedBusError { address, 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 { @@ -1239,23 +1483,73 @@ impl M68K { if address & 0x1 != 0 { 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, 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> { let address = address & 0xFF_FFFF; - if address & 0x1 != 0 { - self.trap(3)?; + if self.use_stored_mem_cycles { + 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) .map_err(|_| DetailedBusError { address, 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 { @@ -1335,6 +1629,57 @@ impl M68K { 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)] pub fn bus(&self) -> &Backplane { &self.bus diff --git a/src/main.rs b/src/main.rs index 545ff9e..b6e4188 100644 --- a/src/main.rs +++ b/src/main.rs @@ -89,13 +89,7 @@ fn main() -> Result<(), anyhow::Error> { }; if args.run { while !state.cpu.stopped { - match state.cpu.step() { - Ok(()) => (), - Err(e) => { - println!("{e}"); - state.cpu.stopped = true; - } - } + state.cpu.step(); } println!("CPU stopped at PC {:#x}\n{}", state.cpu.pc(), state.cpu); return Ok(()); @@ -175,14 +169,7 @@ fn main() -> Result<(), anyhow::Error> { let pc = state.cpu.pc(); out += &disas_fmt(&mut state.cpu, pc, &state.symbol_tables).0; } - match state.cpu.step() { - Ok(_) => (), - Err(bus_error) => { - println!("{bus_error}"); - state.cpu.stopped=true; - break; - } - } + state.cpu.step(); } if args.get_flag("print_regs") { out += &format!("{}\n", state.cpu); @@ -225,14 +212,7 @@ fn main() -> Result<(), anyhow::Error> { let pc = state.cpu.pc(); out += &disas_fmt(&mut state.cpu, pc, &state.symbol_tables).0; } - match state.cpu.step() { - Ok(_) => (), - Err(bus_error) => { - println!("{bus_error}"); - state.cpu.stopped=true; - break; - } - } + state.cpu.step(); } if state.cpu.stopped { out += &format!("CPU stopped at PC {:#x}\n", state.cpu.pc()); diff --git a/src/mmu.rs b/src/mmu.rs index 481e5ea..39f2b56 100644 --- a/src/mmu.rs +++ b/src/mmu.rs @@ -11,45 +11,34 @@ use crate::{ const ID: u16 = 5; -#[derive(Debug, Copy, Clone)] -struct PagingEntry { - frame: u32, - present: bool, - writable: bool, - #[allow(unused)] - user: bool, -} - -impl From for PagingEntry { - fn from(entry: u32) -> Self { - Self { - frame: entry >> 12, - present: entry & 0x1 == 1, - writable: entry & 0x2 == 2, - user: entry & 0x4 == 4, - } - } -} +const TLB_MAPPING_FLAG_VALID: u32 = 0x800; +const MAPPING_FLAG_USER: u32 = 0x4; +const MAPPING_FLAG_WRITABLE: u32 = 0x2; +const MAPPING_FLAG_PRESENT: u32 = 0x1; #[derive(Debug)] pub struct MmuCard { enabled: bool, - cache: [Option; 4096], + tlb: [u32; 4096], map_frames: [u32; 4], map_frames_enabled: [bool; 4], - tlb_clear_entry: u16, print_debug: bool, + tlb_high_write_word: u16, + tlb_mem_start: u32, + tlb_mem_enabled: bool, } impl Card for MmuCard { fn new(_data: toml::Value) -> anyhow::Result { Ok(Self { enabled: false, - cache: [None; 4096], + tlb: [0; 4096], map_frames: [0; 4], map_frames_enabled: [false; 4], - tlb_clear_entry: 0, 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) } + fn read_byte(&mut self, address: u32) -> NullableResult { + 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 { match address { (0x0..=0xF) => { @@ -90,36 +111,36 @@ impl Card for MmuCard { 3 => { self.map_frames_enabled[map_no] = (data & 0x1) > 0; for i in map_no * 1024..(map_no + 1) * 1024 { - self.cache[i] = None; + self.tlb[i] = 0x0; } } _ => unreachable!(), }; } - (0x10..=0x13) => { - let offset = (address - 0x10) % 4; - match offset { - 0 => (), - 1 => { - self.tlb_clear_entry = (self.tlb_clear_entry & 0xF) | ((data as u16) << 4); - } - 2 => { - self.tlb_clear_entry = - (self.tlb_clear_entry & 0xFF0) | (((data as u16) & 0xF0) >> 4); - } - 3 => { - if self.print_debug { - println!( - "Clearing TLB entry {:#x} ( page start {:#x} )", - self.tlb_clear_entry, - (self.tlb_clear_entry as u32) << 12 - ); - } - self.cache[self.tlb_clear_entry as usize] = None; - } - _ => unreachable!(), - } - } + // (0x10..=0x13) => { + // let offset = (address - 0x10) % 4; + // match offset { + // 0 => (), + // 1 => { + // self.tlb_clear_entry = (self.tlb_clear_entry & 0xF) | ((data as u16) << 4); + // } + // 2 => { + // self.tlb_clear_entry = + // (self.tlb_clear_entry & 0xFF0) | (((data as u16) & 0xF0) >> 4); + // } + // 3 => { + // if self.print_debug { + // println!( + // "Clearing TLB entry {:#x} ( page start {:#x} )", + // self.tlb_clear_entry, + // (self.tlb_clear_entry as u32) << 12 + // ); + // } + // self.tlb[self.tlb_clear_entry as usize] = 0x0; + // } + // _ => unreachable!(), + // } + // } 0x15 => { self.enabled = (data & 0x1) > 0; } @@ -187,14 +208,15 @@ impl Mmu for MmuCard { } let page = address >> 12; 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 { println!("TLB hit"); } - entry + tlb_mapping } else { if print_debug { - println!("TLB miss, fetching entry"); + println!("TLB miss, fetching mapping"); } if !self.map_frames_enabled[(page >> 10) as usize] { if print_debug { @@ -203,36 +225,34 @@ impl Mmu for MmuCard { return NullableResult::Null; } 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 { - println!("Entry is at {entry_address:#x}"); + println!("Mapping is at {mapping_address:#x}"); } - let entry_hi = backplane.read_word_phys(entry_address)?; - let entry_lo = backplane.read_word_phys(entry_address + 2)?; - let entry = PagingEntry::from((entry_hi as u32) << 16 | entry_lo as u32); - self.cache[page as usize] = Some(entry); - if print_debug { - println!("Fetched entry {entry:#?}"); - } - entry + let mapping_hi = backplane.read_word_phys(mapping_address)?; + let mapping_lo = backplane.read_word_phys(mapping_address + 2)?; + let mapping = + ((mapping_hi as u32) << 16 | mapping_lo as u32) | TLB_MAPPING_FLAG_VALID; + self.tlb[page as usize] = mapping; + mapping }; - if entry.present { - if write && !entry.writable { + if mapping & MAPPING_FLAG_PRESENT > 0 { + if write && (mapping & MAPPING_FLAG_WRITABLE == 0) { if print_debug { - println!("Entry not writable"); + println!("Page not writable"); } return NullableResult::Err(BusError); } if print_debug { println!( "Translation success, translated to {:#x}", - (entry.frame << 12) | offset + (mapping & !0xFFF) | offset ); } - NullableResult::Ok((entry.frame << 12) | offset) + NullableResult::Ok((mapping & !0xFFF) | offset) } else { if print_debug { - println!("Entry not present"); + println!("Page not present"); } NullableResult::Null }