ya6502/
memory.rs

1use std::error;
2use std::fmt;
3use std::result::Result;
4
5pub trait Memory {
6    /// Writes a byte to given address. Returns error if the location is
7    /// unsupported. In a release build, the errors should be ignored and the
8    /// method should always return a successful result.
9    fn write(&mut self, address: u16, value: u8) -> WriteResult;
10
11    /// Reads a byte from given address. Returns the byte or error if the
12    /// location is unsupported. In a release build, the errors should be
13    /// ignored and the method should always return a successful result.
14    fn read(&self, address: u16) -> ReadResult;
15}
16
17pub type ReadResult = Result<u8, ReadError>;
18
19#[derive(Debug, Clone)]
20pub struct ReadError {
21    pub address: u16,
22}
23
24impl error::Error for ReadError {}
25
26impl fmt::Display for ReadError {
27    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28        write!(f, "Unable to read from address ${:04X}", self.address)
29    }
30}
31
32pub type WriteResult = Result<(), WriteError>;
33
34#[derive(Debug, Clone)]
35pub struct WriteError {
36    pub address: u16,
37    pub value: u8,
38}
39
40impl error::Error for WriteError {}
41
42impl fmt::Display for WriteError {
43    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44        write!(
45            f,
46            "Unable to write ${:02X} to address ${:04X}",
47            self.value, self.address
48        )
49    }
50}
51
52/// A very simple memory structure: just a 64-kilobyte chunk of RAM.
53pub struct SimpleRam {
54    pub bytes: [u8; Self::SIZE],
55}
56
57impl SimpleRam {
58    const SIZE: usize = 0x10000; // 64 kB (64 * 1024)
59
60    pub fn new() -> SimpleRam {
61        SimpleRam {
62            bytes: [0; Self::SIZE], // Fill the entire RAM with 0x00.
63        }
64    }
65
66    pub fn initialized_with(value: u8) -> SimpleRam {
67        SimpleRam {
68            bytes: [value; Self::SIZE],
69        }
70    }
71
72    /// Creates a new `RAM`, putting given `program` at address 0xF000. It also
73    /// sets the reset pointer to 0xF000.
74    pub fn with_test_program(program: &[u8]) -> SimpleRam {
75        Self::with_test_program_at(0xF000, program)
76    }
77
78    /// Creates a new `RAM`, putting given `program` at a given address. It also
79    /// sets the reset pointer to this address.
80    pub fn with_test_program_at(address: u16, program: &[u8]) -> SimpleRam {
81        let mut ram = SimpleRam::new();
82        ram.bytes[address as usize..address as usize + program.len()].copy_from_slice(program);
83        ram.bytes[0xFFFC] = address as u8; // least-significant byte
84        ram.bytes[0xFFFD] = (address >> 8) as u8; // most-significant byte
85        return ram;
86    }
87}
88
89impl Memory for SimpleRam {
90    fn read(&self, address: u16) -> ReadResult {
91        // this arrow means we give u16 they return u8
92        Ok(self.bytes[address as usize])
93    }
94
95    fn write(&mut self, address: u16, value: u8) -> WriteResult {
96        self.bytes[address as usize] = value;
97        Ok(())
98    }
99}
100
101impl fmt::Debug for SimpleRam {
102    /// Prints out only the zero page, because come on, who would scroll through
103    /// a dump of entire 64 kibibytes...
104    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
105        use std::convert::TryInto;
106        let zero_page: [u8; 255] = (&self.bytes[..255]).try_into().unwrap();
107        return f
108            .debug_struct("SimpleRam")
109            .field("zero page", &zero_page)
110            .finish();
111    }
112}
113
114// A 128-byte memory structure that acts as Atari RAM and supports memory space
115// mirroring.
116#[derive(Debug)]
117pub struct AtariRam {
118    bytes: [u8; Self::SIZE],
119}
120
121impl AtariRam {
122    const SIZE: usize = 0x80;
123    pub fn new() -> AtariRam {
124        AtariRam {
125            bytes: [0; Self::SIZE],
126        }
127    }
128}
129
130impl Memory for AtariRam {
131    fn read(&self, address: u16) -> ReadResult {
132        Ok(self.bytes[address as usize & 0b0111_1111])
133    }
134    fn write(&mut self, address: u16, value: u8) -> WriteResult {
135        self.bytes[address as usize & 0b0111_1111] = value;
136        Ok(())
137    }
138}
139
140#[derive(Debug, Clone, PartialEq)]
141pub struct RomSizeError {
142    size: usize,
143}
144impl error::Error for RomSizeError {}
145impl fmt::Display for RomSizeError {
146    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
147        write!(
148            f,
149            "Illegal ROM size: {} bytes. Valid sizes: 2048, 4096",
150            self.size
151        )
152    }
153}
154
155pub struct AtariRom {
156    bytes: Vec<u8>,
157    address_mask: u16,
158}
159
160impl AtariRom {
161    pub fn new(bytes: &[u8]) -> Result<Self, RomSizeError> {
162        match bytes.len() {
163            2048 | 4096 => Ok(AtariRom {
164                bytes: bytes.to_vec(),
165                address_mask: match bytes.len() {
166                    0x1000 => 0b0000_1111_1111_1111,
167                    _ => 0b0000_0111_1111_1111,
168                },
169            }),
170            _ => Err(RomSizeError { size: bytes.len() }),
171        }
172    }
173}
174
175impl Memory for AtariRom {
176    fn read(&self, address: u16) -> ReadResult {
177        Ok(self.bytes[(address & self.address_mask) as usize])
178    }
179    fn write(&mut self, address: u16, value: u8) -> WriteResult {
180        Err(WriteError { address, value })
181    }
182}
183
184impl fmt::Debug for AtariRom {
185    fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result {
186        f.debug_struct("AtariRom")
187            .field("size", &self.bytes.len())
188            .field("address_mask", &self.address_mask)
189            .finish()
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196
197    #[test]
198    fn creating_empty_simple_ram() {
199        let ram = SimpleRam::with_test_program(&[]);
200        assert_eq!(ram.bytes[..0xFFFC], [0u8; 0xFFFC][..]);
201    }
202
203    #[test]
204    fn simple_ram_with_test_program() {
205        let ram = SimpleRam::with_test_program(&[10, 56, 72, 255]);
206        // Bytes until 0xF000 (exclusively) should have been zeroed.
207        assert_eq!(ram.bytes[..0xF000], [0u8; 0xF000][..]);
208        // Next, there should be our program.
209        assert_eq!(ram.bytes[0xF000..0xF004], [10, 56, 72, 255][..]);
210        // The rest, until 0xFFFC, should also be zeroed.
211        assert_eq!(ram.bytes[0xF004..0xFFFC], [0u8; 0xFFFC - 0xF004][..]);
212        // And finally, the reset vector.
213        assert_eq!(ram.bytes[0xFFFC..0xFFFE], [0x00, 0xF0]);
214    }
215
216    #[test]
217    fn simple_ram_with_test_program_at() {
218        let ram = SimpleRam::with_test_program_at(0xF110, &[10, 56, 72, 255]);
219        assert_eq!(ram.bytes[..0xF110], [0u8; 0xF110][..]);
220        assert_eq!(ram.bytes[0xF110..0xF114], [10, 56, 72, 255][..]);
221        assert_eq!(ram.bytes[0xF114..0xFFFC], [0u8; 0xFFFC - 0xF114][..]);
222        assert_eq!(ram.bytes[0xFFFC..0xFFFE], [0x10, 0xF1]);
223    }
224
225    #[test]
226    fn simple_ram_with_test_program_sets_reset_address() {
227        let ram = SimpleRam::with_test_program(&[0xFF; 0x1000]);
228        assert_eq!(ram.bytes[0xFFFC..0xFFFE], [0x00, 0xF0]); // 0xF000
229    }
230
231    #[test]
232    fn atari_ram_read_write() {
233        let mut ram = AtariRam::new();
234        ram.write(0x00AB, 123).unwrap();
235        ram.write(0x00AC, 234).unwrap();
236        assert_eq!(ram.read(0x00AB).unwrap(), 123);
237        assert_eq!(ram.read(0x00AC).unwrap(), 234);
238    }
239
240    #[test]
241    fn atari_ram_mirroring() {
242        let mut ram = AtariRam::new();
243        ram.write(0x0080, 1).unwrap();
244        assert_eq!(ram.read(0x0080).unwrap(), 1);
245        assert_eq!(ram.read(0x2880).unwrap(), 1);
246        assert_eq!(ram.read(0xCD80).unwrap(), 1);
247    }
248
249    #[test]
250    fn atari_rom_4k() {
251        let mut program = [0u8; 0x1000];
252        program[5] = 1;
253        let rom = AtariRom::new(&program).unwrap();
254        assert_eq!(rom.read(0x1000).unwrap(), 0);
255        assert_eq!(rom.read(0x1005).unwrap(), 1);
256        assert_eq!(rom.read(0x3005).unwrap(), 1);
257        assert_eq!(rom.read(0xF005).unwrap(), 1);
258    }
259
260    #[test]
261    fn atari_rom_2k() {
262        let mut program = [0u8; 0x0800];
263        program[5] = 1;
264        let rom = AtariRom::new(&program).unwrap();
265        assert_eq!(rom.read(0x1000).unwrap(), 0);
266        assert_eq!(rom.read(0x1005).unwrap(), 1);
267        assert_eq!(rom.read(0x3005).unwrap(), 1);
268        assert_eq!(rom.read(0xF005).unwrap(), 1);
269        assert_eq!(rom.read(0xF805).unwrap(), 1);
270    }
271
272    #[test]
273    fn atari_rom_illegal_size() {
274        let rom = AtariRom::new(&[0u8; 0x0900]);
275        assert_eq!(rom.err(), Some(RomSizeError { size: 0x900 }));
276    }
277}