spectrusty_peripherals/memory/
zxinterface1.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*/
8use std::rc::Rc;
9use std::io::{self, Read};
10
11use spectrusty_core::memory::{
12    MemoryExtension, ExRom, ZxMemory, ZxMemoryError
13};
14#[cfg(feature = "snapshot")]
15use spectrusty_core::memory::serde::{serialize_mem, deserialize_mem};
16#[cfg(feature = "snapshot")]
17use serde::{Serialize, Deserialize};
18
19/// The ZX Interface 1 memory [extension][MemoryExtension].
20///
21/// Interface 1 ROM is paged in if the processor executes the instruction at address `0x0008` or `0x1708`,
22/// the error handler, and `CLOSE #` routines. It is paged out after the Z80 executes the `RET` at address `0x0700`.
23#[derive(Clone, Debug)]
24#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
25pub struct ZxInterface1MemExt {
26    #[cfg_attr(feature = "snapshot",
27        serde(serialize_with = "serialize_mem", deserialize_with = "deserialize_mem"))]
28    #[cfg_attr(feature = "snapshot", serde(default = "exrom_default"))]
29    exrom: ExRom
30}
31
32impl Default for ZxInterface1MemExt {
33    fn default() -> Self {
34        let exrom = Rc::new([]);
35        ZxInterface1MemExt { exrom }
36    }
37}
38
39impl MemoryExtension for ZxInterface1MemExt {
40    #[inline(always)]
41    fn read_opcode<M: ZxMemory>(&mut self, pc: u16, memory: &mut M) -> u8 {
42        match pc {
43            0x0008|0x1708 => {
44                let _ = memory.map_exrom(Rc::clone(&self.exrom), 0);
45            }
46            0x0700 => {
47                let res = memory.read(pc);
48                memory.unmap_exrom(&self.exrom);
49                return res
50            }
51            _ => {}
52        }
53        memory.read(pc)
54    }
55}
56
57impl ZxInterface1MemExt {
58    /// Provide a reader with 8kb of ZX Interface 1 ROM program code.
59    pub fn load_if1_rom<R: Read>(&mut self, mut rd: R) -> io::Result<()> {
60        let mut exrom = Rc::new([!0u8;0x4000]);
61        let exrom_slice = &mut Rc::get_mut(&mut exrom).unwrap()[0..0x2000];
62        rd.read_exact(exrom_slice)?;
63        self.exrom = exrom;
64        Ok(())
65    }
66    /// Returns a reference to the EX-ROM bank.
67    pub fn exrom(&self) -> &ExRom {
68        &self.exrom
69    }
70    /// Removes data from the EX-ROM bank.
71    ///
72    /// # Note
73    /// If the EX-ROM bank has been paged in, it won't be paged out automatically after the EX-ROM data
74    /// is cleared from the extension.
75    pub fn clear_exrom(&mut self) {
76        self.exrom = Rc::new([]);
77    }
78    /// Maps EX-ROM into `memory` page `0`.
79    ///
80    /// # Errors
81    /// Returns an error if the extension's EX-ROM bank is not populated with ROM data.
82    pub fn map_exrom<M: ZxMemory>(&self, memory: &mut M) -> Result<(), ZxMemoryError> {
83        memory.map_exrom(Rc::clone(&self.exrom), 0)
84    }
85    /// Unmaps EX-ROM from `memory`.
86    pub fn unmap_exrom<M: ZxMemory>(&self, memory: &mut M) {
87        memory.unmap_exrom(&self.exrom)
88    }
89    /// Returns `true` if EX-ROM is currently paged in.
90    pub fn is_mapped_exrom<M: ZxMemory>(&self, memory: &M) -> bool {
91        memory.has_mapped_exrom(&self.exrom)
92    }
93}
94
95#[cfg(feature = "snapshot")]
96fn exrom_default() -> ExRom {
97    Rc::new([])
98}