dmd_core/
bus.rs

1#![allow(clippy::unreadable_literal)]
2
3use crate::err::BusError;
4use crate::mem::Mem;
5use crate::duart::Duart;
6use crate::mouse::Mouse;
7use std::fmt::Debug;
8use std::ops::Range;
9
10const NVRAM_SIZE: usize = 8192;
11
12/// Access Status Code
13pub enum AccessCode {
14    MoveTranslated,
15    CoprDataWrite,
16    AutoVectorIrqAck,
17    CoprDataFetch,
18    StopAck,
19    CoprBroadcast,
20    CoprStatusFetch,
21    ReadInterlocked,
22    AddressFetch,
23    OperandFetch,
24    Write,
25    IrqAck,
26    IFAfterPCDisc,
27    InstrPrefetch,
28    InstrFetch,
29    NoOp,
30}
31
32
33/// A virtual device on the bus.
34pub trait Device: Send + Sync + Debug {
35    fn address_range(&self) -> &Range<usize>;
36    fn name(&self) -> &str;
37    fn is_read_only(&self) -> bool;
38    fn read_byte(&mut self, address: usize, access: AccessCode) -> Result<u8, BusError>;
39    fn read_half(&mut self, address: usize, access: AccessCode) -> Result<u16, BusError>;
40    fn read_word(&mut self, address: usize, access: AccessCode) -> Result<u32, BusError>;
41    fn write_byte(&mut self, address: usize, val: u8, access: AccessCode) -> Result<(), BusError>;
42    fn write_half(&mut self, address: usize, val: u16, access: AccessCode) -> Result<(), BusError>;
43    fn write_word(&mut self, address: usize, val: u32, access: AccessCode) -> Result<(), BusError>;
44    fn load(&mut self, address: usize, data: &[u8]) -> Result<(), BusError>;
45}
46
47//
48// Bus Memory Map
49//
50//  0x000000..0x01ffff     ROM
51//  0x200000..0x20003f     DUART (Port A: host, Port B: keyboard/printer)
52//  0x300000..0x3000ff     8530 SCC on optional I/O board
53//  0x400000..0x400003     Mouse X/Y data
54//  0x500000..0x500001     Display starting addr
55//  0x600000..0x601fff     BBRAM (Non-volatile RAM)
56//  0x700000..0x7fffff     RAM (256K or 1M)
57//
58
59pub struct Bus {
60    rom: Mem,
61    duart: Duart,
62    mouse: Mouse,
63    vid: Mem,      // TODO: Figure out what device this really is
64    bbram: Mem,    // TODO: change to BBRAM when implemented
65    ram: Mem,
66}
67
68impl Bus {
69    pub fn new(mem_size: usize) -> Bus {
70        Bus {
71            rom: Mem::new(0, 0x20000, true),
72            duart: Duart::new(),
73            mouse: Mouse::new(),
74            vid: Mem::new(0x500000, 0x2, false),
75            bbram: Mem::new(0x600000, 0x2000, false),
76            ram: Mem::new(0x700000, mem_size, false),
77        }
78    }
79
80    fn get_device(&mut self, address: usize) -> Result<&mut Device, BusError> {
81        if address < 0x20000 {
82            return Ok(&mut self.rom);
83        }
84
85        if address >= 0x200000 && address < 0x200040 {
86            return Ok(&mut self.duart);
87        }
88
89        if address >= 0x400000 && address < 0x400004 {
90            return Ok(&mut self.mouse);
91        }
92
93        if address >= 0x500000 && address < 0x500002 {
94            return Ok(&mut self.vid);
95        }
96
97        if address >= 0x600000 && address < 0x602000 {
98            return Ok(&mut self.bbram);
99        }
100
101        if address >= 0x700000 && address < 0x800000 {
102            return Ok(&mut self.ram);
103        }
104
105        Err(BusError::NoDevice(address as u32))
106    }
107
108    pub fn read_byte(&mut self, address: usize, access: AccessCode) -> Result<u8, BusError> {
109        self.get_device(address)?.read_byte(address, access)
110    }
111
112    pub fn read_half(&mut self, address: usize, access: AccessCode) -> Result<u16, BusError> {
113        if address & 1 != 0 {
114            return Err(BusError::Alignment);
115        }
116        self.get_device(address)?.read_half(address, access)
117    }
118
119    pub fn read_word(&mut self, address: usize, access: AccessCode) -> Result<u32, BusError> {
120        if address & 3 != 0 {
121            return Err(BusError::Alignment);
122        }
123        self.get_device(address)?.read_word(address, access)
124    }
125
126    pub fn read_op_half(&mut self, address: usize) -> Result<u16, BusError> {
127        let m = self.get_device(address)?;
128
129        Ok(u16::from(m.read_byte(address, AccessCode::OperandFetch)?)
130            | u16::from(m.read_byte(address + 1, AccessCode::OperandFetch)?).wrapping_shl(8))
131    }
132
133    pub fn read_op_word(&mut self, address: usize) -> Result<u32, BusError> {
134        let m = self.get_device(address)?;
135
136        Ok(u32::from(m.read_byte(address, AccessCode::OperandFetch)?)
137            | u32::from(m.read_byte(address + 1, AccessCode::OperandFetch)?).wrapping_shl(8)
138            | u32::from(m.read_byte(address + 2, AccessCode::OperandFetch)?).wrapping_shl(16)
139            | u32::from(m.read_byte(address + 3, AccessCode::OperandFetch)?).wrapping_shl(24))
140    }
141
142    pub fn write_byte(&mut self, address: usize, val: u8) -> Result<(), BusError> {
143        self.get_device(address)?.write_byte(address, val, AccessCode::Write)
144    }
145
146    pub fn write_half(&mut self, address: usize, val: u16) -> Result<(), BusError> {
147        if address & 1 != 0 {
148            return Err(BusError::Alignment);
149        }
150        self.get_device(address)?.write_half(address, val, AccessCode::Write)
151    }
152
153    pub fn write_word(&mut self, address: usize, val: u32) -> Result<(), BusError> {
154        if address & 3 != 0 {
155            return Err(BusError::Alignment);
156        }
157        self.get_device(address)?.write_word(address, val, AccessCode::Write)
158    }
159
160    pub fn load(&mut self, address: usize, data: &[u8]) -> Result<(), BusError> {
161        self.get_device(address)?.load(address, data)
162    }
163
164    pub fn video_ram(&self) -> &[u8] {
165        let vid_register = (u16::from(self.vid[0]) << 8 | u16::from(self.vid[1])) as usize;
166        let start = vid_register * 4;
167        let end = start + 0x19000;
168        self.ram.as_slice(start..end)
169    }
170
171    pub fn service(&mut self) {
172        self.duart.service();
173    }
174
175    pub fn get_interrupts(&mut self) -> Option<u8> {
176        self.duart.get_interrupt()
177    }
178
179    pub fn mouse_move(&mut self, x: u16, y: u16) {
180        self.mouse.x = x;
181        self.mouse.y = y;
182    }
183
184    pub fn mouse_down(&mut self, button: u8) {
185        self.duart.mouse_down(button);
186    }
187
188    pub fn mouse_up(&mut self, button: u8) {
189        self.duart.mouse_up(button);
190    }
191
192    pub fn rs232_tx_poll(&mut self) -> Option<u8> {
193        self.duart.rs232_tx_poll()
194    }
195
196    pub fn kb_tx_poll(&mut self) -> Option<u8> {
197        self.duart.kb_tx_poll()
198    }
199
200    pub fn rx_char(&mut self, char: u8) {
201        self.duart.rx_char(char);
202    }
203
204    pub fn rx_keyboard(&mut self, keycode: u8) {
205        self.duart.rx_keyboard(keycode);
206    }
207
208    pub fn duart_output(&self) -> u8 {
209        self.duart.output_port()
210    }
211
212    pub fn get_nvram(&self) -> &[u8] {
213        self.bbram.as_slice(0..NVRAM_SIZE)
214    }
215
216    pub fn set_nvram(&mut self, nvram: &[u8]) {
217        for (i, b) in nvram.into_iter().enumerate() {
218            self.bbram[i] = *b;
219        }
220    }
221}
222
223#[cfg(test)]
224mod tests {
225    use super::*;
226
227    #[test]
228    fn should_fail_on_alignment_errors() {
229        let mut bus: Bus = Bus::new(0x10000);
230
231        assert!(bus.write_byte(0x700000, 0x1f).is_ok());
232        assert!(bus.write_half(0x700000, 0x1f1f).is_ok());
233        assert!(bus.write_word(0x700000, 0x1f1f1f1f).is_ok());
234        assert!(bus.write_half(0x700001, 0x1f1f).is_err());
235        assert!(bus.write_half(0x700002, 0x1f1f).is_ok());
236        assert!(bus.write_word(0x700001, 0x1f1f1f1f).is_err());
237        assert!(bus.write_word(0x700002, 0x1f1f1f1f).is_err());
238        assert!(bus.write_word(0x700003, 0x1f1f1f1f).is_err());
239        assert!(bus.write_word(0x700004, 0x1f1f1f1f).is_ok());
240    }
241}