SH-2 bus interface
Implementations can assume that all addresses are masked to the lowest 29 bits (address & 0x1FFFFFFF)
because the highest 3 bits are only used internally
13#[derive(Debug, Clone, Copy, Encode, Decode)] 14pub enum AccessContext { 15 Fetch, 16 Data { pc: u32, opcode: u16 }, 17 InterruptVector, 18 Dma { channel: usize }, 19} 20 21impl Display for AccessContext { 22 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 23 match self { 24 Self::Fetch => { 25 write!(f, "Opcode fetch") 26 } 27 &Self::Data { pc, opcode } => { 28 let mut instruction = DisassembledInstruction::new(); 29 disassemble::disassemble_into(pc, opcode, &mut instruction); 30 write!(f, "PC={pc:08X}, opcode={opcode:04X}, instruction='{}'", instruction.text) 31 } 32 Self::InterruptVector => write!(f, "Interrupt vector fetch"), 33 Self::Dma { channel } => write!(f, "DMA channel {channel}"), 34 } 35 } 36} 37 38pub struct OpSize;
For cases where size can't be passed as a const generic (e.g. dyn-compatible trait methods)
Panics
Panics if SIZE is not a valid OpSize value
Panics
Panics if SIZE is not a valid OpSize value
Panics
Panics if SIZE is not a valid OpSize value
96pub trait BusInterface: Sized {
Debug view type; if not implemented, set to [crate::debug::DummySh2Debugger]
102 fn read<const SIZE: u8>(&mut self, address: u32, ctx: AccessContext) -> u32; 103 104 fn read_byte(&mut self, address: u32, ctx: AccessContext) -> u8 { 105 self.read::<{ OpSize::BYTE }>(address, ctx) as u8 106 } 107 108 fn read_word(&mut self, address: u32, ctx: AccessContext) -> u16 { 109 self.read::<{ OpSize::WORD }>(address, ctx) as u16 110 } 111 112 fn read_longword(&mut self, address: u32, ctx: AccessContext) -> u32 { 113 self.read::<{ OpSize::LONGWORD }>(address, ctx) 114 } 115 116 fn read_cache_line(&mut self, address: u32, ctx: AccessContext) -> [u16; 8]; 117 118 fn write<const SIZE: u8>(&mut self, address: u32, value: u32, ctx: AccessContext); 119 120 fn write_byte(&mut self, address: u32, value: u8, ctx: AccessContext) { 121 self.write::<{ OpSize::BYTE }>(address, value.into(), ctx); 122 } 123 124 fn write_word(&mut self, address: u32, value: u16, ctx: AccessContext) { 125 self.write::<{ OpSize::WORD }>(address, value.into(), ctx); 126 } 127 128 fn write_longword(&mut self, address: u32, value: u32, ctx: AccessContext) { 129 self.write::<{ OpSize::LONGWORD }>(address, value, ctx); 130 }
The CPU will halt while this is true and then reset when it changes from true to false
133 fn reset(&self) -> bool;
Current external interrupt level from 0 to 15; 0 indicates no interrupt
136 fn interrupt_level(&self) -> u8;
DREQ line for DMA channel 0
139 fn dma_request_0(&self) -> bool;
DREQ line for DMA channel 1
142 fn dma_request_1(&self) -> bool;
144 fn acknowledge_dreq_1(&mut self);
Receive a byte from the serial interface, if any
147 fn serial_rx(&mut self) -> Option<u8>;
Transmit a byte to the serial interface
150 fn serial_tx(&mut self, value: u8);
152 fn increment_cycle_counter(&mut self, cycles: u64); 153 154 fn should_stop_execution(&self) -> bool; 155 156 fn debug_view(&mut self) -> Option<Self::DebugView<'_>> { 157 None 158 } 159 160 fn opcode_table<'a>() -> &'a OpcodeTable<Self>; 161} 162 163#[macro_export] 164macro_rules! impl_sh2_opcode_table { 165 ($bus:ident) => { 166 #[inline] 167 fn opcode_table<'a>() -> &'a $crate::OpcodeTable<$bus> { 168 static TABLE: ::std::sync::LazyLock<$crate::OpcodeTable<$bus>> = 169 ::std::sync::LazyLock::new(|| $crate::OpcodeTable::new()); 170 171 &*TABLE 172 } 173 }; 174} 175 176pub use impl_sh2_opcode_table;