Skip to main content

onerom_database/
types.rs

1//! One ROM Database - Types
2
3// Copyright (c) 2025 Piers Finlayson <piers@piers.rocks>
4//
5// MIT licence
6
7use crate::Error;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
10#[repr(u8)]
11pub enum CsActiveByte {
12    #[default]
13    Low = 0,
14    High = 1,
15}
16
17impl CsActiveByte {
18    pub fn from_byte(byte: u8) -> Result<Self, Error> {
19        match byte {
20            0 => Ok(CsActiveByte::Low),
21            1 => Ok(CsActiveByte::High),
22            _ => Err(Error::ParseError),
23        }
24    }
25
26    pub fn to_byte(&self) -> u8 {
27        *self as u8
28    }
29}
30
31impl From<CsActive> for CsActiveByte {
32    fn from(cs: CsActive) -> Self {
33        match cs {
34            CsActive::Low => CsActiveByte::Low,
35            CsActive::High => CsActiveByte::High,
36        }
37    }
38}
39
40/// Whether a CS line is active low or active high
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
42pub enum CsActive {
43    #[allow(dead_code)]
44    High,
45
46    #[default]
47    Low,
48}
49
50impl core::fmt::Display for CsActive {
51    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
52        match self {
53            CsActive::High => write!(f, "Active High"),
54            CsActive::Low => write!(f, "Active Low"),
55        }
56    }
57}
58
59impl From<CsActiveByte> for CsActive {
60    fn from(byte: CsActiveByte) -> Self {
61        match byte {
62            CsActiveByte::Low => CsActive::Low,
63            CsActiveByte::High => CsActive::High,
64        }
65    }
66}
67
68impl CsActive {
69    fn bit(&self) -> usize {
70        match self {
71            CsActive::High => 1,
72            CsActive::Low => 0,
73        }
74    }
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
78#[repr(u8)]
79pub enum RomTypeByte {
80    Type2364 = 0x00,
81    Type2332 = 0x01,
82    Type2316 = 0x02,
83    Type23128 = 0x03,
84    Type27512 = 0x04,
85}
86
87impl RomTypeByte {
88    pub fn from_byte(byte: u8) -> Result<Self, Error> {
89        match byte {
90            0x00 => Ok(RomTypeByte::Type2364),
91            0x01 => Ok(RomTypeByte::Type2332),
92            0x02 => Ok(RomTypeByte::Type2316),
93            0x03 => Ok(RomTypeByte::Type23128),
94            0x04 => Ok(RomTypeByte::Type27512),
95            _ => Err(Error::ParseError),
96        }
97    }
98
99    pub fn to_byte(&self) -> u8 {
100        *self as u8
101    }
102}
103
104/// Supported types of ROMs.  This type includes the chip select behaviour of
105/// the ROM, which was mask programmed at factory for the original ROM chips.
106#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
107pub enum RomType {
108    /// A 27512 ROM
109    Type27512,
110
111    /// A 23128 ROM
112    Type23128 {
113        cs1: CsActive,
114        cs2: CsActive,
115        cs3: CsActive,
116    },
117
118    /// A 2364 ROM
119    Type2364 { cs: CsActive },
120
121    /// A 2332 ROM
122    Type2332 { cs1: CsActive, cs2: CsActive },
123
124    /// A 2316 ROM
125    Type2316 {
126        cs1: CsActive,
127        cs2: CsActive,
128        cs3: CsActive,
129    },
130}
131
132impl core::fmt::Display for RomType {
133    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
134        write!(f, "{} ({})", self.type_str(), self.cs_str())
135    }
136}
137
138impl RomType {
139    const CS1_23128_ADDR: usize = 17;
140    const CS2_23128_ADDR: usize = 16;
141    const CS3_23128_ADDR: usize = 14;
142    const CS_2364_ADDR: usize = 13;
143    const CS1_2332_ADDR: usize = 13;
144    const CS2_2332_ADDR: usize = 12;
145    const CS1_2316_ADDR: usize = 13;
146    const CS2_2316_ADDR: usize = 11;
147    const CS3_2316_ADDR: usize = 12;
148
149    /// Returns the max size of ROM supported by this object.
150    pub const fn max_size() -> usize {
151        65536
152    }
153
154    /// Returns the size of this ROM.
155    pub const fn size(&self) -> usize {
156        match self {
157            RomType::Type27512 => 65536,
158            RomType::Type23128 { .. } => 16384,
159            RomType::Type2364 { .. } => 8192,
160            RomType::Type2332 { .. } => 4096,
161            RomType::Type2316 { .. } => 2048,
162        }
163    }
164
165    /// Returns number of pins this ROM type has
166    pub const fn rom_pins(&self) -> usize {
167        match self {
168            RomType::Type27512 => 28,
169            RomType::Type23128 { .. } => 28,
170            RomType::Type2364 { .. } => 24,
171            RomType::Type2332 { .. } => 24,
172            RomType::Type2316 { .. } => 24,
173        }
174    }
175
176    pub const fn invalid_addr_lines(&self) -> &'static [u8] {
177        match self {
178            RomType::Type27512 => &[],
179            RomType::Type23128 { .. } => &[15],
180            RomType::Type2364 { .. } => &[],
181            RomType::Type2332 { .. } => &[],
182            RomType::Type2316 { .. } => &[],
183        }
184    }
185
186    /// Returns the active CS mask for this ROM type.
187    pub fn cs_active_mask(&self) -> usize {
188        match self {
189            RomType::Type27512 => 0,
190            RomType::Type23128 { cs1, cs2, cs3 } => {
191                cs1.bit() << Self::CS1_23128_ADDR
192                    | cs2.bit() << Self::CS2_23128_ADDR
193                    | cs3.bit() << Self::CS3_23128_ADDR
194            }
195            RomType::Type2364 { cs } => cs.bit() << Self::CS_2364_ADDR,
196            RomType::Type2332 { cs1, cs2 } => {
197                cs1.bit() << Self::CS1_2332_ADDR | cs2.bit() << Self::CS2_2332_ADDR
198            }
199            RomType::Type2316 { cs1, cs2, cs3 } => {
200                cs1.bit() << Self::CS1_2316_ADDR
201                    | cs2.bit() << Self::CS2_2316_ADDR
202                    | cs3.bit() << Self::CS3_2316_ADDR
203            }
204        }
205    }
206
207    /// Returns all supported ROM types.
208    pub const fn all() -> &'static [RomType] {
209        &ALL_ROM_TYPES
210    }
211
212    pub const fn type_str(&self) -> &'static str {
213        match self {
214            RomType::Type27512 => "27512",
215            RomType::Type23128 { .. } => "23128",
216            RomType::Type2364 { .. } => "2364",
217            RomType::Type2332 { .. } => "2332",
218            RomType::Type2316 { .. } => "2316",
219        }
220    }
221
222    pub const fn cs_str(&self) -> &'static str {
223        match self {
224            RomType::Type27512 => "N/A",
225            RomType::Type23128 { cs1, cs2, cs3 } => match (cs1, cs2, cs3) {
226                (CsActive::Low, CsActive::Low, CsActive::Low) => "CS1 Low, CS2 Low, CS3 Low",
227                (CsActive::Low, CsActive::Low, CsActive::High) => "CS1 Low, CS2 Low, CS3 High",
228                (CsActive::Low, CsActive::High, CsActive::Low) => "CS1 Low, CS2 High, CS3 Low",
229                (CsActive::Low, CsActive::High, CsActive::High) => "CS1 Low, CS2 High, CS3 High",
230                (CsActive::High, CsActive::Low, CsActive::Low) => "CS1 High, CS2 Low, CS3 Low",
231                (CsActive::High, CsActive::Low, CsActive::High) => "CS1 High, CS2 Low, CS3 High",
232                (CsActive::High, CsActive::High, CsActive::Low) => "CS1 High, CS2 High, CS3 Low",
233                (CsActive::High, CsActive::High, CsActive::High) => "CS1 High, CS2 High, CS3 High",
234            },
235            RomType::Type2364 { cs } => match cs {
236                CsActive::Low => "CS Low",
237                CsActive::High => "CS High",
238            },
239            RomType::Type2332 { cs1, cs2 } => match (cs1, cs2) {
240                (CsActive::Low, CsActive::Low) => "CS1 Low, CS2 Low",
241                (CsActive::Low, CsActive::High) => "CS1 Low, CS2 High",
242                (CsActive::High, CsActive::Low) => "CS1 High, CS2 Low",
243                (CsActive::High, CsActive::High) => "CS1 High, CS2 High",
244            },
245            RomType::Type2316 { cs1, cs2, cs3 } => match (cs1, cs2, cs3) {
246                (CsActive::Low, CsActive::Low, CsActive::Low) => "CS1 Low, CS2 Low, CS3 Low",
247                (CsActive::Low, CsActive::Low, CsActive::High) => "CS1 Low, CS2 Low, CS3 High",
248                (CsActive::Low, CsActive::High, CsActive::Low) => "CS1 Low, CS2 High, CS3 Low",
249                (CsActive::Low, CsActive::High, CsActive::High) => "CS1 Low, CS2 High, CS3 High",
250                (CsActive::High, CsActive::Low, CsActive::Low) => "CS1 High, CS2 Low, CS3 Low",
251                (CsActive::High, CsActive::Low, CsActive::High) => "CS1 High, CS2 Low, CS3 High",
252                (CsActive::High, CsActive::High, CsActive::Low) => "CS1 High, CS2 High, CS3 Low",
253                (CsActive::High, CsActive::High, CsActive::High) => "CS1 High, CS2 High, CS3 High",
254            },
255        }
256    }
257
258    /// Retrieves the binary size of this structure when binary serialized.
259    pub const fn binary_size() -> usize {
260        4
261    }
262
263    /// Format is RomTypeByte followed by up to 3 CsActiveByte values.
264    pub fn from_bytes(buf: &[u8]) -> Result<Self, Error> {
265        if buf.len() < Self::binary_size() {
266            return Err(Error::ParseError);
267        }
268        match RomTypeByte::from_byte(buf[0])? {
269            RomTypeByte::Type27512 => Ok(RomType::Type27512 {}),
270            RomTypeByte::Type23128 => {
271                let cs1 = CsActiveByte::from_byte(buf[1])?.into();
272                let cs2 = CsActiveByte::from_byte(buf[2])?.into();
273                let cs3 = CsActiveByte::from_byte(buf[3])?.into();
274                Ok(RomType::Type23128 { cs1, cs2, cs3 })
275            }
276            RomTypeByte::Type2364 => {
277                let cs = CsActiveByte::from_byte(buf[1])?.into();
278                Ok(RomType::Type2364 { cs })
279            }
280            RomTypeByte::Type2332 => {
281                let cs1 = CsActiveByte::from_byte(buf[1])?.into();
282                let cs2 = CsActiveByte::from_byte(buf[2])?.into();
283                Ok(RomType::Type2332 { cs1, cs2 })
284            }
285            RomTypeByte::Type2316 => {
286                let cs1 = CsActiveByte::from_byte(buf[1])?.into();
287                let cs2 = CsActiveByte::from_byte(buf[2])?.into();
288                let cs3 = CsActiveByte::from_byte(buf[3])?.into();
289                Ok(RomType::Type2316 { cs1, cs2, cs3 })
290            }
291        }
292    }
293
294    /// Serializes this structure into the given buffer.
295    pub fn to_bytes(&self, buf: &mut [u8]) -> Result<(), Error> {
296        if buf.len() < Self::binary_size() {
297            return Err(Error::ParseError);
298        }
299        match self {
300            RomType::Type27512 => {
301                buf[0] = RomTypeByte::Type27512.to_byte();
302                buf[1] = 0;
303                buf[2] = 0;
304                buf[3] = 0;
305            }
306            RomType::Type23128 { cs1, cs2, cs3 } => {
307                buf[0] = RomTypeByte::Type23128.to_byte();
308                buf[1] = CsActiveByte::from(*cs1).to_byte();
309                buf[2] = CsActiveByte::from(*cs2).to_byte();
310                buf[3] = CsActiveByte::from(*cs3).to_byte();
311            }
312            RomType::Type2364 { cs } => {
313                buf[0] = RomTypeByte::Type2364.to_byte();
314                buf[1] = CsActiveByte::from(*cs).to_byte();
315                buf[2] = 0;
316                buf[3] = 0;
317            }
318            RomType::Type2332 { cs1, cs2 } => {
319                buf[0] = RomTypeByte::Type2332.to_byte();
320                buf[1] = CsActiveByte::from(*cs1).to_byte();
321                buf[2] = CsActiveByte::from(*cs2).to_byte();
322                buf[3] = 0;
323            }
324            RomType::Type2316 { cs1, cs2, cs3 } => {
325                buf[0] = RomTypeByte::Type2316.to_byte();
326                buf[1] = CsActiveByte::from(*cs1).to_byte();
327                buf[2] = CsActiveByte::from(*cs2).to_byte();
328                buf[3] = CsActiveByte::from(*cs3).to_byte();
329            }
330        }
331        Ok(())
332    }
333}
334
335// Enumeration of all possible ROM types.
336const NUM_ROM_TYPES: usize = 23;
337const ALL_ROM_TYPES: [RomType; NUM_ROM_TYPES] = [
338    RomType::Type27512 {},
339    RomType::Type23128 {
340        cs1: CsActive::Low,
341        cs2: CsActive::Low,
342        cs3: CsActive::Low,
343    },
344    RomType::Type23128 {
345        cs1: CsActive::Low,
346        cs2: CsActive::Low,
347        cs3: CsActive::High,
348    },
349    RomType::Type23128 {
350        cs1: CsActive::Low,
351        cs2: CsActive::High,
352        cs3: CsActive::Low,
353    },
354    RomType::Type23128 {
355        cs1: CsActive::Low,
356        cs2: CsActive::High,
357        cs3: CsActive::High,
358    },
359    RomType::Type23128 {
360        cs1: CsActive::High,
361        cs2: CsActive::Low,
362        cs3: CsActive::Low,
363    },
364    RomType::Type23128 {
365        cs1: CsActive::High,
366        cs2: CsActive::Low,
367        cs3: CsActive::High,
368    },
369    RomType::Type23128 {
370        cs1: CsActive::High,
371        cs2: CsActive::High,
372        cs3: CsActive::Low,
373    },
374    RomType::Type23128 {
375        cs1: CsActive::High,
376        cs2: CsActive::High,
377        cs3: CsActive::High,
378    },
379    RomType::Type2364 { cs: CsActive::Low },
380    RomType::Type2364 { cs: CsActive::High },
381    RomType::Type2332 {
382        cs1: CsActive::Low,
383        cs2: CsActive::Low,
384    },
385    RomType::Type2332 {
386        cs1: CsActive::Low,
387        cs2: CsActive::High,
388    },
389    RomType::Type2332 {
390        cs1: CsActive::High,
391        cs2: CsActive::Low,
392    },
393    RomType::Type2332 {
394        cs1: CsActive::High,
395        cs2: CsActive::High,
396    },
397    RomType::Type2316 {
398        cs1: CsActive::Low,
399        cs2: CsActive::Low,
400        cs3: CsActive::Low,
401    },
402    RomType::Type2316 {
403        cs1: CsActive::Low,
404        cs2: CsActive::Low,
405        cs3: CsActive::High,
406    },
407    RomType::Type2316 {
408        cs1: CsActive::Low,
409        cs2: CsActive::High,
410        cs3: CsActive::Low,
411    },
412    RomType::Type2316 {
413        cs1: CsActive::Low,
414        cs2: CsActive::High,
415        cs3: CsActive::High,
416    },
417    RomType::Type2316 {
418        cs1: CsActive::High,
419        cs2: CsActive::Low,
420        cs3: CsActive::Low,
421    },
422    RomType::Type2316 {
423        cs1: CsActive::High,
424        cs2: CsActive::Low,
425        cs3: CsActive::High,
426    },
427    RomType::Type2316 {
428        cs1: CsActive::High,
429        cs2: CsActive::High,
430        cs3: CsActive::Low,
431    },
432    RomType::Type2316 {
433        cs1: CsActive::High,
434        cs2: CsActive::High,
435        cs3: CsActive::High,
436    },
437];