1use core::fmt;
10use core::ops::{Bound, Range, RangeBounds};
11use std::rc::Rc;
12use std::io::{self, Read};
13
14mod extension;
15#[cfg(feature = "snapshot")] pub mod arrays;
16#[cfg(feature = "snapshot")] pub mod serde;
17
18pub use extension::*;
19
20pub const MEM16K_SIZE : usize = 0x4000;
21pub const MEM32K_SIZE : usize = 2 * MEM16K_SIZE;
22pub const MEM48K_SIZE : usize = 3 * MEM16K_SIZE;
23pub const MEM64K_SIZE : usize = 4 * MEM16K_SIZE;
24pub const MEM128K_SIZE: usize = 8 * MEM16K_SIZE;
25pub const MEM8K_SIZE : usize = MEM16K_SIZE / 2;
26pub const SCREEN_SIZE: u16 = 0x1B00;
27
28pub type ScreenArray = [u8;SCREEN_SIZE as usize];
30
31pub type ExRom = Rc<[u8]>;
33
34#[non_exhaustive]
35#[derive(Debug)]
36pub enum ZxMemoryError {
37 InvalidPageIndex,
38 InvalidBankIndex,
39 UnsupportedAddressRange,
40 UnsupportedExRomPaging,
41 InvalidExRomSize,
42 Io(io::Error)
43}
44
45impl std::error::Error for ZxMemoryError {}
46
47impl fmt::Display for ZxMemoryError {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 write!(f, "{}", match self {
50 ZxMemoryError::InvalidPageIndex => "Memory page index is out of range",
51 ZxMemoryError::InvalidBankIndex => "Memory bank index is out of range",
52 ZxMemoryError::UnsupportedAddressRange => "Address range is not supported",
53 ZxMemoryError::UnsupportedExRomPaging => "EX-ROM mapping is not supported",
54 ZxMemoryError::InvalidExRomSize => "EX-ROM size is smaller than the memory page size",
55 ZxMemoryError::Io(err) => return err.fmt(f)
56 })
57 }
58}
59
60impl From<ZxMemoryError> for io::Error {
61 fn from(err: ZxMemoryError) -> Self {
62 match err {
63 ZxMemoryError::Io(err) => err,
64 e => io::Error::new(io::ErrorKind::InvalidInput, e)
65 }
66 }
67}
68
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
70#[repr(u8)]
71pub enum MemoryKind {
72 Rom,
73 Ram
74}
75
76#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78pub struct MemPageOffset {
79 pub kind: MemoryKind,
81 pub index: u8,
83 pub offset: u16
85}
86
87#[derive(Debug, PartialEq, Eq)]
89pub enum PageMutSlice<'a> {
90 Rom(&'a mut [u8]),
91 Ram(&'a mut [u8])
92}
93
94impl<'a> PageMutSlice<'a> {
95 pub fn as_mut_slice(&mut self) -> &mut [u8] {
96 match self {
97 PageMutSlice::Rom(slice)|PageMutSlice::Ram(slice) => slice
98 }
99 }
100
101 pub fn into_mut_slice(self) -> &'a mut[u8] {
102 match self {
103 PageMutSlice::Rom(slice)|PageMutSlice::Ram(slice) => slice
104 }
105 }
106
107 pub fn is_rom(&self) -> bool {
108 matches!(self, PageMutSlice::Rom(..))
109 }
110
111 pub fn as_mut_rom(&mut self) -> Option<&mut [u8]> {
112 match self {
113 PageMutSlice::Rom(slice) => Some(slice),
114 _ => None
115 }
116 }
117
118 pub fn into_mut_rom(self) -> Option<&'a mut [u8]> {
119 match self {
120 PageMutSlice::Rom(slice) => Some(slice),
121 _ => None
122 }
123 }
124
125 pub fn is_ram(&self) -> bool {
126 matches!(self, PageMutSlice::Ram(..))
127 }
128
129 pub fn as_mut_ram(&mut self) -> Option<&mut [u8]> {
130 match self {
131 PageMutSlice::Ram(slice) => Some(slice),
132 _ => None
133 }
134 }
135
136 pub fn into_mut_ram(self) -> Option<&'a mut [u8]> {
137 match self {
138 PageMutSlice::Ram(slice) => Some(slice),
139 _ => None
140 }
141 }
142}
143
144pub type Result<T> = core::result::Result<T, ZxMemoryError>;
146
147pub trait ZxMemory {
149 const PAGE_SIZE: usize = 0x4000;
151 const ROM_SIZE: usize;
153 const RAMTOP: u16;
155 const PAGES_MAX: u8;
157 const SCR_BANKS_MAX: usize;
159 const ROM_BANKS_MAX: usize;
161 const RAM_BANKS_MAX: usize;
163
164 fn reset(&mut self);
166 fn read(&self, addr: u16) -> u8;
168 fn read16(&self, addr: u16) -> u16;
170 fn read_screen(&self, screen_bank: usize, addr: u16) -> u8;
182 fn write(&mut self, addr: u16, val: u8);
184 fn write16(&mut self, addr: u16, val: u16);
186 fn mem_ref(&self) -> &[u8];
188 fn mem_mut(&mut self) -> &mut[u8];
190 fn screen_ref(&self, screen_bank: usize) -> Result<&ScreenArray>;
194 fn screen_mut(&mut self, screen_bank: usize) -> Result<&mut ScreenArray>;
198 fn page_kind(&self, page: u8) -> Result<MemoryKind>;
205 fn page_bank(&self, page: u8) -> Result<(MemoryKind, usize)>;
215 fn page_ref(&self, page: u8) -> Result<&[u8]>;
217 fn page_mut(&mut self, page: u8) -> Result<&mut[u8]>;
219 fn rom_bank_ref(&self, rom_bank: usize) -> Result<&[u8]>;
221 fn rom_bank_mut(&mut self, rom_bank: usize) -> Result<&mut[u8]>;
223 fn ram_bank_ref(&self, ram_bank: usize) -> Result<&[u8]>;
225 fn ram_bank_mut(&mut self, ram_bank: usize) -> Result<&mut[u8]>;
227 fn map_rom_bank(&mut self, rom_bank: usize, page: u8) -> Result<()>;
229 fn map_exrom(&mut self, _exrom_bank: ExRom, _page: u8) -> Result<()> {
239 Err(ZxMemoryError::UnsupportedExRomPaging)
240 }
241 fn unmap_exrom(&mut self, _exrom_bank: &ExRom) { }
244 fn is_exrom_at(&self, _page: u8) -> bool { false }
246 fn has_mapped_exrom(&self, _exrom_bank: &ExRom) -> bool { false }
248 fn map_ram_bank(&mut self, ram_bank: usize, page: u8) -> Result<()>;
250 fn page_index_at(&self, address: u16) -> Result<MemPageOffset> {
252 let index = (address / Self::PAGE_SIZE as u16) as u8;
253 let offset = address % Self::PAGE_SIZE as u16;
254 let kind = self.page_kind(index)?;
255 Ok(MemPageOffset {kind, index, offset})
256 }
257 fn rom_ref(&self) -> &[u8] {
259 &self.mem_ref()[0..Self::ROM_SIZE]
260 }
261 fn rom_mut(&mut self) -> &mut [u8] {
263 &mut self.mem_mut()[0..Self::ROM_SIZE]
264 }
265 fn ram_ref(&self) -> &[u8] {
267 &self.mem_ref()[Self::ROM_SIZE..]
268 }
269 fn ram_mut(&mut self) -> &mut [u8] {
271 &mut self.mem_mut()[Self::ROM_SIZE..]
272 }
273 fn load_into_rom<R: Read>(&mut self, mut rd: R) -> Result<()> {
276 let slice = self.rom_mut();
277 rd.read_exact(slice).map_err(ZxMemoryError::Io)
278 }
279 fn load_into_rom_bank<R: Read>(&mut self, rom_bank: usize, mut rd: R) -> Result<()> {
281 let slice = self.rom_bank_mut(rom_bank)?;
282 rd.read_exact(slice).map_err(ZxMemoryError::Io)
283 }
284 fn iter_pages<A: RangeBounds<u16>>(
289 &self,
290 address_range: A,
291 ) -> Result<MemPageRefIter<'_, Self>> {
292 let range = normalize_address_range(address_range, 0, Self::RAMTOP)
293 .map_err(|_| ZxMemoryError::UnsupportedAddressRange)?;
294 let cursor = range.start;
295 let end = range.end;
296 Ok(MemPageRefIter { mem: self, cursor, end })
297 }
298 fn for_each_page_mut<A: RangeBounds<u16>, F>(
304 &mut self,
305 address_range: A,
306 mut f: F
307 ) -> Result<()>
308 where for<'a> F: FnMut(PageMutSlice<'a>) -> Result<()>
309 {
310 let range = normalize_address_range(address_range, 0, Self::RAMTOP)
311 .map_err(|_| ZxMemoryError::UnsupportedAddressRange)?;
312 let cursor = range.start;
313 let end = range.end;
314 let iter = MemPageMutIter { mem: self, cursor, end };
315 for page in iter {
316 f(page)?
317 }
318 Ok(())
319 }
320 fn load_into_mem<A: RangeBounds<u16>, R: Read>(&mut self, address_range: A, mut rd: R) -> Result<()> {
322 self.for_each_page_mut(address_range, |page| {
323 match page {
324 PageMutSlice::Rom(slice)|PageMutSlice::Ram(slice) => {
325 rd.read_exact(slice).map_err(ZxMemoryError::Io)
326 }
327 }
328 })
329 }
330 fn fill_mem<R, F>(&mut self, address_range: R, mut f: F) -> Result<()>
338 where R: RangeBounds<u16>, F: FnMut() -> u8
339 {
340 self.for_each_page_mut(address_range, |page| {
341 for p in page.into_mut_slice().iter_mut() {
342 *p = f()
343 }
344 Ok(())
345 })
346 }
347}
348
349pub struct MemPageRefIter<'a, Z: ?Sized> {
350 mem: &'a Z,
351 cursor: usize,
352 end: usize
353}
354
355impl<'a, Z: ZxMemory + ?Sized> Iterator for MemPageRefIter<'a, Z> {
356 type Item = &'a [u8];
357 fn next(&mut self) -> Option<Self::Item> {
358 let cursor = self.cursor;
359 let end = self.end;
360 if cursor < end {
361 let MemPageOffset { index, offset, .. } = self.mem.page_index_at(cursor as u16).unwrap();
362 let offset = offset as usize;
363 let page = self.mem.page_ref(index).unwrap();
364 let read_len = end - cursor;
365 let read_end = page.len().min(offset + read_len);
366 let read_page = &page[offset..read_end];
367 self.cursor += read_page.len();
368 Some(read_page)
369 }
370 else {
371 None
372 }
373 }
374}
375
376struct MemPageMutIter<'a, Z: ?Sized> {
377 mem: &'a mut Z,
378 cursor: usize,
379 end: usize
380}
381
382impl<'a, Z: ZxMemory + ?Sized> Iterator for MemPageMutIter<'a, Z> {
383 type Item = PageMutSlice<'a>;
384 fn next(&mut self) -> Option<Self::Item> {
385 let cursor = self.cursor;
386 let end = self.end;
387 if cursor < end {
388 let MemPageOffset { kind, index, offset } = self.mem.page_index_at(cursor as u16).unwrap();
389 let offset = offset as usize;
390 let page = self.mem.page_mut(index).unwrap();
391 let page = unsafe { core::mem::transmute::<&mut[u8], &'a mut[u8]>(page) };
395 let read_len = end - cursor;
396 let read_end = page.len().min(offset + read_len);
397 let read_page = &mut page[offset..read_end];
398 self.cursor += read_page.len();
399 match kind {
400 MemoryKind::Rom => {
401 Some(PageMutSlice::Rom(read_page))
402 },
403 MemoryKind::Ram => {
404 Some(PageMutSlice::Ram(read_page))
405 }
406 }
407 }
408 else {
409 None
410 }
411 }
412}
413
414enum AddressRangeError {
415 StartBoundTooLow,
416 EndBoundTooHigh
417}
418
419fn normalize_address_range<R: RangeBounds<u16>>(
420 range: R,
421 min_inclusive: u16,
422 max_inclusive: u16
423 ) -> core::result::Result<Range<usize>, AddressRangeError>
424{
425 let start = match range.start_bound() {
426 Bound::Included(start) => if *start < min_inclusive {
427 return Err(AddressRangeError::StartBoundTooLow)
428 } else { *start as usize },
429 Bound::Excluded(start) => if *start < min_inclusive.saturating_sub(1) {
430 return Err(AddressRangeError::StartBoundTooLow)
431 } else { *start as usize + 1 },
432 Bound::Unbounded => min_inclusive as usize
433 };
434 let end = match range.end_bound() {
435 Bound::Included(end) => if *end > max_inclusive {
436 return Err(AddressRangeError::EndBoundTooHigh)
437 } else { *end as usize + 1},
438 Bound::Excluded(end) => if *end > max_inclusive.saturating_add(1) {
439 return Err(AddressRangeError::EndBoundTooHigh)
440 } else { *end as usize },
441 Bound::Unbounded => max_inclusive as usize + 1
442 };
443 Ok(start..end)
444}