spectrusty/memory/
single_page.rs

1/*
2    Copyright (C) 2020-2022  Rafal Michalski
3
4    This file is part of SPECTRUSTY, a Rust library for building emulators.
5
6    For the full copyright notice, see the lib.rs file.
7*/
8#[cfg(feature = "snapshot")]
9use serde::{Serialize, Deserialize};
10#[cfg(feature = "snapshot")]
11use super::serde::{serialize_mem, deserialize_mem};
12
13use super::{
14    MemPageOffset,
15    MemoryKind,
16    Result,
17    ZxMemory,
18    ZxMemoryError,
19    SCREEN_SIZE,
20    ScreenArray,
21    screen_slice_to_array_ref, screen_slice_to_array_mut
22};
23
24#[derive(Clone)]
25#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
26pub struct SinglePageMemory<const MEM_SIZE: usize, const RAM_BOT: u16> {
27    #[cfg_attr(feature = "snapshot", serde(serialize_with = "serialize_mem", deserialize_with = "deserialize_mem"))]
28    mem: Box<[u8;MEM_SIZE]>
29}
30
31pub const ROM_SIZE: u16 = 0x4000;
32pub const ROM_TOP: u16 = ROM_SIZE - 1;
33
34/// A single page memory type with 16kb RAM.
35pub type Memory16k = SinglePageMemory<0x8000, ROM_SIZE>;
36
37/// A single page memory type with 48kb RAM.
38pub type Memory48k = SinglePageMemory<0x10000, ROM_SIZE>;
39
40/// A single page memory type with 64kb RAM.
41///
42/// The first 16kb of RAM will be also available as ROM, however
43/// the implementation doesn't prevent writes to the ROM area.
44pub type Memory64k = SinglePageMemory<0x10000, 0>;
45
46impl<const MEM_SIZE: usize, const RAM_BOT: u16> Default for SinglePageMemory<MEM_SIZE, RAM_BOT> {
47    fn default() -> Self {
48        Self { mem: Box::new([0; MEM_SIZE]) }
49    }
50}
51
52impl<const MEM_SIZE: usize, const RAM_BOT: u16> ZxMemory for SinglePageMemory<MEM_SIZE, RAM_BOT> {
53    const ROM_SIZE: usize = ROM_SIZE as usize;
54    const RAMTOP: u16 = (MEM_SIZE - 1) as u16;
55    const PAGES_MAX: u8 = 1;
56    const SCR_BANKS_MAX: usize = 1;
57    const ROM_BANKS_MAX: usize = 0;
58    const RAM_BANKS_MAX: usize = 0;
59
60    #[inline(always)]
61    fn reset(&mut self) {}
62
63    #[inline(always)]
64    fn read(&self, addr: u16) -> u8 {
65        self.mem.get(addr as usize).copied().unwrap_or(u8::max_value())
66    }
67
68    // #[allow(clippy::cast_ptr_alignment)]
69    #[inline]
70    fn read16(&self, addr: u16) -> u16 {
71        match addr {
72            a if a < Self::RAMTOP => unsafe {
73                let ptr: *const u8 = self.mem.as_ptr().add(a as usize);
74                let ptr16 = ptr as *const u16;
75                ptr16.read_unaligned().to_le()
76            }
77            a if a == Self::RAMTOP || a == std::u16::MAX => {
78                u16::from_le_bytes([self.read(addr), self.read(addr.wrapping_add(1))])
79            }
80            _ => {
81                u16::max_value()
82            }
83        }
84    }
85
86    #[inline(always)]
87    fn write(&mut self, addr: u16, val: u8) {
88        if addr >= RAM_BOT && addr <= Self::RAMTOP {
89            self.mem[addr as usize] = val;
90        }
91    }
92
93    // #[allow(clippy::cast_ptr_alignment)]
94    #[inline]
95    fn write16(&mut self, addr: u16, val: u16) {
96        match addr {
97            #[allow(unused_comparisons)]
98            a if a >= RAM_BOT && a < Self::RAMTOP => unsafe {
99                let ptr: *mut u8 = self.mem.as_mut_ptr().add(a as usize);
100                let ptr16 = ptr as *mut u16;
101                ptr16.write_unaligned(val.to_le());
102            }
103            a if a == ROM_TOP => {
104                self.write(a.wrapping_add(1), (val >> 8) as u8);
105            }
106            a if a == Self::RAMTOP => {
107                self.write(a, (val & 0xff) as u8);
108                self.write(a.wrapping_add(1), (val >> 8) as u8);
109            }
110            _ => {}
111        }
112    }
113    #[inline]
114    fn read_screen(&self, _screen_bank: usize, addr: u16) -> u8 {
115        if addr < SCREEN_SIZE {
116            self.mem[0x4000 + addr as usize]
117        }
118        else {
119            panic!("trying to read outside of screen area");
120        }
121    }
122    #[inline]
123    fn mem_ref(&self) -> &[u8] {
124        &self.mem[..]
125    }
126    #[inline]
127    fn mem_mut(&mut self) -> &mut[u8] {
128        &mut self.mem[..]
129    }
130    #[inline]
131    fn screen_ref(&self, screen_bank: usize) -> Result<&ScreenArray> {
132        let start = match screen_bank {
133            0 => 0x4000,
134            1 => 0x6000,
135            _ => return Err(ZxMemoryError::InvalidBankIndex)
136        };
137        Ok(screen_slice_to_array_ref(
138            &self.mem[start..start+SCREEN_SIZE as usize]))
139    }
140    #[inline]
141    fn screen_mut(&mut self, screen_bank: usize) -> Result<&mut ScreenArray> {
142        let start = match screen_bank {
143            0 => 0x4000,
144            1 => 0x6000,
145            _ => return Err(ZxMemoryError::InvalidBankIndex)
146        };
147        Ok(screen_slice_to_array_mut(
148            &mut self.mem[start..start+SCREEN_SIZE as usize]))
149    }
150    #[inline]
151    fn page_kind(&self, page: u8) -> Result<MemoryKind> {
152        match page {
153            0 => Ok(MemoryKind::Rom),
154            1 => Ok(MemoryKind::Ram),
155            _ => Err(ZxMemoryError::InvalidPageIndex)
156        }
157    }
158    fn page_bank(&self, page: u8) -> Result<(MemoryKind, usize)> {
159        self.page_kind(page).map(|kind| (kind, 0))
160    }
161    #[inline]
162    fn page_ref(&self, page: u8) -> Result<&[u8]> {
163        match page {
164            0 => Ok(&self.mem[..Self::ROM_SIZE]),
165            1 => Ok(&self.mem[Self::ROM_SIZE..]),
166            _ => Err(ZxMemoryError::InvalidPageIndex)
167        }
168    }
169    #[inline]
170    fn page_mut(&mut self, page: u8) -> Result<&mut[u8]> {
171        match page {
172            0 => Ok(&mut self.mem[..Self::ROM_SIZE]),
173            1 => Ok(&mut self.mem[Self::ROM_SIZE..]),
174            _ => Err(ZxMemoryError::InvalidPageIndex)
175        }
176    }
177    fn rom_bank_ref(&self, rom_bank: usize) -> Result<&[u8]> {
178        if rom_bank > Self::ROM_BANKS_MAX {
179            return Err(ZxMemoryError::InvalidBankIndex)
180        }
181        Ok(&self.mem[..Self::ROM_SIZE])
182    }
183
184    fn rom_bank_mut(&mut self, rom_bank: usize) -> Result<&mut[u8]> {
185        if rom_bank > Self::ROM_BANKS_MAX {
186            return Err(ZxMemoryError::InvalidBankIndex)
187        }
188        Ok(&mut self.mem[..Self::ROM_SIZE])
189    }
190
191    fn ram_bank_ref(&self, ram_bank: usize) -> Result<&[u8]> {
192        if ram_bank > Self::RAM_BANKS_MAX {
193            return Err(ZxMemoryError::InvalidBankIndex)
194        }
195        Ok(&self.mem[Self::ROM_SIZE..=Self::RAMTOP as usize])
196    }
197
198    fn ram_bank_mut(&mut self, ram_bank: usize) -> Result<&mut[u8]> {
199        if ram_bank > Self::RAM_BANKS_MAX {
200            return Err(ZxMemoryError::InvalidBankIndex)
201        }
202        Ok(&mut self.mem[Self::ROM_SIZE..=Self::RAMTOP as usize])
203    }
204
205    fn map_rom_bank(&mut self, rom_bank: usize, page: u8) -> Result<()> {
206        if rom_bank > Self::ROM_BANKS_MAX {
207            return Err(ZxMemoryError::InvalidBankIndex)
208        }
209        if page != 0 {
210            return Err(ZxMemoryError::InvalidPageIndex)
211        }
212        Ok(())
213    }
214
215    fn map_ram_bank(&mut self, ram_bank: usize, page: u8) -> Result<()> {
216        if ram_bank > Self::RAM_BANKS_MAX {
217            return Err(ZxMemoryError::InvalidBankIndex)
218        }
219        if page != 1 {
220            return Err(ZxMemoryError::InvalidPageIndex)
221        }
222        Ok(())
223    }
224
225    fn page_index_at(&self, address: u16) -> Result<MemPageOffset> {
226        #[allow(unreachable_patterns)]
227        if address < Self::ROM_SIZE as u16 {
228            Ok(MemPageOffset {kind: MemoryKind::Rom, index: 0, offset: address})
229        }
230        else if address <= Self::RAMTOP {
231            Ok(MemPageOffset {kind: MemoryKind::Ram, index: 1, offset: address - Self::ROM_SIZE as u16})
232        }
233        else {
234            Err(ZxMemoryError::UnsupportedAddressRange)
235        }
236    }
237}
238
239#[cfg(test)]
240mod tests {
241    use crate::memory::*;
242    use super::*;
243
244    #[test]
245    fn test_memory_single_page_work() {
246       memory_single_page_work::<Memory16k>();
247       memory_single_page_work::<Memory48k>();
248       memory_single_page_work::<Memory64k>();
249
250       memory_single_page_rom::<Memory16k>(true);
251       memory_single_page_rom::<Memory48k>(true);
252       memory_single_page_rom::<Memory64k>(false);
253    }
254
255    fn memory_single_page_rom<M: ZxMemory + Default>(is_ro: bool) {
256        let mut mem = M::default();
257        for addr in 0..=ROM_TOP {
258            assert_eq!(mem.read(addr), 0);
259            mem.write(addr, 201);
260            if is_ro {
261                assert_eq!(mem.read(addr), 0);
262            }
263            else {
264                assert_eq!(mem.read(addr), 201);
265            }
266        }        
267        for addr in ROM_TOP + 1..=M::RAMTOP {
268            assert_eq!(mem.read(addr), 0);
269            mem.write(addr, 201);
270            assert_eq!(mem.read(addr), 201);
271        }        
272    }
273
274    fn memory_single_page_work<M: ZxMemory + Default + Clone>() {
275        assert_eq!(M::ROM_SIZE, 0x4000);
276        assert_eq!(M::PAGE_SIZE, 0x4000);
277        assert_eq!(M::PAGES_MAX, 1);
278        assert_eq!(M::SCR_BANKS_MAX, 1);
279        assert_eq!(M::RAM_BANKS_MAX, 0);
280        assert_eq!(M::ROM_BANKS_MAX, 0);
281        let mut mem = M::default();
282        let mut mem1 = mem.clone();
283        let mut index = 0;
284        mem.for_each_page_mut(.., |page| {
285            if index == 0 {
286                assert!(page.is_rom());
287            }
288            else {
289                assert!(page.is_ram());
290            }
291            assert_eq!(page.into_mut_slice(), mem1.page_mut(index).unwrap());
292            index += 1;
293            Ok(())
294        }).unwrap();
295        assert_eq!(index, 2);
296   }
297}