Skip to main content

ternaria_dev/
bus.rs

1//! Routes addresses to RAM or to a device.
2
3use crate::{
4    BLOCK_BASE, BlockDevice, CONSOLE_BASE, Console, DEVICE_BASE, DEVICE_STRIDE, Device, TIMER_BASE,
5    Timer,
6};
7use ternaria_arith::{Trit, Tryte, Word};
8use ternaria_mem::{Addressable, Memory, MemoryError, irq};
9
10/// RAM plus the memory-mapped devices.
11///
12/// Addresses at or above zero go to RAM. Addresses inside the device range go
13/// to whichever device owns the slot. Addresses below the device range are
14/// RAM as well, so the negative half is not reserved wholesale.
15pub struct Bus {
16    /// Random access memory.
17    pub ram: Memory,
18    /// The console.
19    pub console: Console,
20    /// The tick counter.
21    pub timer: Timer,
22    /// The block device.
23    pub block: BlockDevice,
24}
25
26impl Bus {
27    /// A bus with empty RAM and the given devices.
28    pub fn new(console: Console, timer: Timer, block: BlockDevice) -> Bus {
29        Bus {
30            ram: Memory::new(),
31            console,
32            timer,
33            block,
34        }
35    }
36
37    /// A bus with a console, a timer, and a block device holding `image`.
38    pub fn with_image(image: impl Into<Vec<u8>>) -> Bus {
39        Bus::new(Console::new(), Timer::new(), BlockDevice::new(image))
40    }
41
42    /// Advances the timer by one tick.
43    pub fn tick(&mut self) {
44        self.timer.tick();
45    }
46
47    /// Which device owns `addr`, and the offset within it.
48    fn route(&mut self, addr: i64) -> Option<(&mut dyn Device, i64)> {
49        if !(DEVICE_BASE..DEVICE_BASE + 3 * DEVICE_STRIDE).contains(&addr) {
50            return None;
51        }
52        if addr < TIMER_BASE {
53            Some((&mut self.console, addr - CONSOLE_BASE))
54        } else if addr < BLOCK_BASE {
55            Some((&mut self.timer, addr - TIMER_BASE))
56        } else {
57            Some((&mut self.block, addr - BLOCK_BASE))
58        }
59    }
60}
61
62impl Addressable for Bus {
63    /// Device addresses need supervisor privilege; RAM does not.
64    fn is_privileged(&self, addr: Word) -> bool {
65        let a = addr.value();
66        (DEVICE_BASE..DEVICE_BASE + 3 * DEVICE_STRIDE).contains(&a)
67    }
68
69    /// One trit per source: positive while that device is asserting.
70    fn interrupts(&self) -> Word {
71        let mut trits = [Trit::Zero; 27];
72        if self.timer.is_firing() {
73            trits[irq::TIMER] = Trit::Pos;
74        }
75        if self.console.has_input() {
76            trits[irq::CONSOLE] = Trit::Pos;
77        }
78        Word::from_trits(trits)
79    }
80
81    fn read_tryte(&mut self, addr: Word) -> Result<Tryte, MemoryError> {
82        let a = addr.value();
83        match self.route(a) {
84            Some((dev, off)) => dev.read(off),
85            None => self.ram.read_tryte(addr),
86        }
87    }
88
89    fn write_tryte(&mut self, addr: Word, value: Tryte) -> Result<(), MemoryError> {
90        let a = addr.value();
91        match self.route(a) {
92            Some((dev, off)) => dev.write(off, value),
93            None => self.ram.write_tryte(addr, value),
94        }
95    }
96
97    fn read_word(&mut self, addr: Word) -> Result<Word, MemoryError> {
98        let a = addr.value();
99        if self.route(a).is_none() {
100            return self.ram.read_word(addr);
101        }
102        // A device word is three consecutive device trytes, composed the same
103        // way memory composes them.
104        if a.rem_euclid(3) != 0 {
105            return Err(MemoryError::Misaligned { addr: a });
106        }
107        let mut trytes = [Tryte::ZERO; Word::TRYTES];
108        for (i, slot) in trytes.iter_mut().enumerate() {
109            *slot = self.read_tryte(Word::from_value(a + i as i64))?;
110        }
111        Ok(Word::from_trytes(trytes))
112    }
113
114    fn write_word(&mut self, addr: Word, value: Word) -> Result<(), MemoryError> {
115        let a = addr.value();
116        if self.route(a).is_none() {
117            return self.ram.write_word(addr, value);
118        }
119        if a.rem_euclid(3) != 0 {
120            return Err(MemoryError::Misaligned { addr: a });
121        }
122        for i in 0..Word::TRYTES {
123            self.write_tryte(Word::from_value(a + i as i64), value.tryte(i))?;
124        }
125        Ok(())
126    }
127}