bus.rsannotatedbus.rssource176 lines · 5.1 KB · raw

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

6use crate::debug::Sh2Debugger;
7use crate::disassemble;
8use crate::disassemble::DisassembledInstruction;
9use crate::instructions::OpcodeTable;
10use bincode::{Decode, Encode};
11use std::fmt::{Display, Formatter};
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)

41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum OpSizeEnum {
43    Byte,
44    Word,
45    Longword,
46}
48impl OpSize {
49    pub const BYTE: u8 = 0;
50    pub const WORD: u8 = 1;
51    pub const LONGWORD: u8 = 2;

Panics

Panics if SIZE is not a valid OpSize value

56    #[must_use]
57    #[inline(always)]
58    pub fn display<const SIZE: u8>() -> &'static str {
59        match SIZE {
60            Self::BYTE => "byte",
61            Self::WORD => "word",
62            Self::LONGWORD => "longword",
63            _ => panic!("invalid size {SIZE}"),
64        }
65    }

Panics

Panics if SIZE is not a valid OpSize value

70    #[must_use]
71    #[inline(always)]
72    pub fn mask<const SIZE: u8>() -> u32 {
73        match SIZE {
74            Self::BYTE => 0xFF,
75            Self::WORD => 0xFFFF,
76            Self::LONGWORD => 0xFFFFFFFF,
77            _ => panic!("invalid size {SIZE}"),
78        }
79    }

Panics

Panics if SIZE is not a valid OpSize value

84    #[must_use]
85    #[inline(always)]
86    pub fn enum_value<const SIZE: u8>() -> OpSizeEnum {
87        match SIZE {
88            Self::BYTE => OpSizeEnum::Byte,
89            Self::WORD => OpSizeEnum::Word,
90            Self::LONGWORD => OpSizeEnum::Longword,
91            _ => panic!("invalid size {SIZE}"),
92        }
93    }
94}
96pub trait BusInterface: Sized {

Debug view type; if not implemented, set to [crate::debug::DummySh2Debugger]

98    type DebugView<'a>: Sh2Debugger
99    where
100        Self: 'a;
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;