1use std::fmt;
2
3use crate::chips::{Rom, ROM_SIZE};
4
5const ADDR_BOUND: u32 = 0x100000;
6
7const SYSTEM_RAM: usize = 0x40000;
8const IO_CHANNEL_RAM: usize = 0x60000;
9
10const CASSETTE_BASIC_ROMS: usize = 4;
11
12#[derive(Clone, Copy)]
17pub(super) struct EffectiveAddress(u16);
18
19impl fmt::Debug for EffectiveAddress {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 f.debug_tuple("EffectiveAddress")
22 .field(&format_args!("{:#06x}", self.0))
23 .finish()
24 }
25}
26
27impl From<u16> for EffectiveAddress {
28 fn from(offset: u16) -> Self {
29 EffectiveAddress(offset)
30 }
31}
32
33impl EffectiveAddress {
34 pub(super) fn to_inner(self) -> u16 {
35 self.0
36 }
37}
38
39#[derive(Clone, Copy)]
41pub(super) struct PhysicalAddress(u32);
42
43impl fmt::Debug for PhysicalAddress {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 f.debug_tuple("PhysicalAddress")
46 .field(&format_args!("{:#07x}", self.0))
47 .finish()
48 }
49}
50
51impl fmt::Display for PhysicalAddress {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 write!(f, "{:#07x}", self.0)
54 }
55}
56
57impl PhysicalAddress {
58 pub(super) fn new(segment: u16, offset: EffectiveAddress) -> Self {
61 let addr = ((segment as u32) << 4) + (offset.0 as u32);
62 assert!(addr < ADDR_BOUND);
63 PhysicalAddress(addr)
64 }
65}
66
67impl IntoIterator for PhysicalAddress {
68 type Item = Self;
69 type IntoIter = PhysicalAddressIterator;
70
71 fn into_iter(self) -> Self::IntoIter {
72 PhysicalAddressIterator { next: Some(self) }
73 }
74}
75
76pub(super) struct PhysicalAddressIterator {
78 next: Option<PhysicalAddress>,
79}
80
81impl Iterator for PhysicalAddressIterator {
82 type Item = PhysicalAddress;
83
84 fn next(&mut self) -> Option<Self::Item> {
85 let ret = self.next;
86 self.next = self
87 .next
88 .and_then(|addr| addr.0.checked_add(1))
89 .map(PhysicalAddress);
90 ret
91 }
92}
93
94fn map_memory<R>(
100 addr: PhysicalAddress,
101 system_ram: impl FnOnce(usize) -> R,
102 io_channel_ram: impl FnOnce(usize) -> R,
103 cassette_basic: impl FnOnce(usize) -> R,
104 bios_rom: impl FnOnce(usize) -> R,
105) -> R {
106 const SYSTEM_RAM_START: u32 = 0;
107 const SYSTEM_RAM_END: u32 = SYSTEM_RAM_START + (SYSTEM_RAM as u32) - 1;
108
109 const IO_CHANNEL_RAM_START: u32 = SYSTEM_RAM_END + 1;
110 const IO_CHANNEL_RAM_END: u32 = IO_CHANNEL_RAM_START + (IO_CHANNEL_RAM as u32) - 1;
111
112 const OPTIONAL_MB_ROM_START: u32 = CASSETTE_BASIC_START - (ROM_SIZE as u32);
113 const OPTIONAL_MB_ROM_END: u32 = CASSETTE_BASIC_START - 1;
114
115 const CASSETTE_BASIC_START: u32 = BIOS_ROM_START - (CASSETTE_BASIC_ROMS * ROM_SIZE) as u32;
116 const CASSETTE_BASIC_END: u32 = BIOS_ROM_START - 1;
117
118 const BIOS_ROM_START: u32 = ADDR_BOUND - (ROM_SIZE as u32);
119 const BIOS_ROM_END: u32 = ADDR_BOUND - 1;
120
121 match addr.0 {
122 SYSTEM_RAM_START..=SYSTEM_RAM_END => system_ram(addr.0 as usize),
123 IO_CHANNEL_RAM_START..=IO_CHANNEL_RAM_END => {
124 io_channel_ram((addr.0 - IO_CHANNEL_RAM_START) as usize)
125 }
126 0xB0000..=0xB3FFF => panic!("Monochrome"),
127 0xB8000..=0xBBFFF => panic!("Color/Graphics"),
128 0xA0000..=0xBFFFF => panic!("128K Reserved"),
129 0xC0000..=0xC7FFF => panic!("Reserved for any video card's BIOS expansion ROM"),
130 0xC8000..=0xCBFFF => panic!("Fixed Disk Control"),
131 0xCC000..=0xEFFFF => panic!("ROM Expansion and Control"),
132 0xF0000..=0xF3FFF => panic!("Reserved"),
133 OPTIONAL_MB_ROM_START..=OPTIONAL_MB_ROM_END => panic!("Optional motherboard ROM"),
134 CASSETTE_BASIC_START..=CASSETTE_BASIC_END => {
135 cassette_basic((addr.0 - CASSETTE_BASIC_START) as usize)
136 }
137 BIOS_ROM_START..=BIOS_ROM_END => bios_rom((addr.0 - BIOS_ROM_START) as usize),
138 ADDR_BOUND.. => unreachable!(),
139 }
140}
141
142pub(super) struct Memory {
144 system_ram: [u8; SYSTEM_RAM],
146 io_channel_ram: [u8; IO_CHANNEL_RAM],
150 cassette_basic: Option<[Rom; CASSETTE_BASIC_ROMS]>,
152 bios_rom: Rom,
154}
155
156impl Memory {
157 pub(super) fn new(bios_rom: Rom, cassette_basic: Option<[Rom; CASSETTE_BASIC_ROMS]>) -> Self {
159 Self {
160 system_ram: [0; SYSTEM_RAM],
161 io_channel_ram: [0; IO_CHANNEL_RAM],
162 cassette_basic,
163 bios_rom,
164 }
165 }
166
167 #[cfg(test)]
169 pub(super) fn without_bios() -> Self {
170 Self::new(Rom::new([0; ROM_SIZE]), None)
171 }
172
173 pub(super) fn view(&self, addr: PhysicalAddress) -> &[u8] {
181 map_memory(
182 addr,
183 |offset| &self.system_ram[offset..],
184 |offset| &self.io_channel_ram[offset..],
185 |offset| {
186 let rom_idx = offset / ROM_SIZE;
187 let rom_offset = offset % ROM_SIZE;
188 self.cassette_basic.as_ref().expect("Cassette BASIC")[rom_idx].view(rom_offset)
189 },
190 |offset| self.bios_rom.view(offset),
191 )
192 }
193
194 pub(super) fn read(&self, addr: PhysicalAddress) -> u8 {
196 map_memory(
197 addr,
198 |offset| self.system_ram[offset],
199 |offset| self.io_channel_ram[offset],
200 |offset| {
201 let rom_idx = offset / ROM_SIZE;
202 let rom_offset = offset % ROM_SIZE;
203 self.cassette_basic.as_ref().expect("Cassette BASIC")[rom_idx].read(rom_offset)
204 },
205 |offset| self.bios_rom.read(offset),
206 )
207 }
208
209 pub(super) fn write(&mut self, addr: PhysicalAddress, data: u8) {
211 map_memory(
212 addr,
213 |offset| self.system_ram[offset] = data,
214 |offset| self.io_channel_ram[offset] = data,
215 |_| panic!("Cannot write to Cassette BASIC"),
216 |_| panic!("Cannot write to BIOS"),
217 )
218 }
219}