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}
84
85impl RomTypeByte {
86    pub fn from_byte(byte: u8) -> Result<Self, Error> {
87        match byte {
88            0x00 => Ok(RomTypeByte::Type2364),
89            0x01 => Ok(RomTypeByte::Type2332),
90            0x02 => Ok(RomTypeByte::Type2316),
91            _ => Err(Error::ParseError),
92        }
93    }
94
95    pub fn to_byte(&self) -> u8 {
96        *self as u8
97    }
98}
99
100/// Supported types of ROMs.  This type includes the chip select behaviour of
101/// the ROM, which was mask programmed at factory for the original ROM chips.
102#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
103pub enum RomType {
104    /// A 2364 ROM
105    Type2364 { cs: CsActive },
106
107    /// A 2332 ROM
108    Type2332 { cs1: CsActive, cs2: CsActive },
109
110    /// A 2316 ROM
111    Type2316 {
112        cs1: CsActive,
113        cs2: CsActive,
114        cs3: CsActive,
115    },
116}
117
118impl core::fmt::Display for RomType {
119    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
120        write!(f, "{} ({})", self.type_str(), self.cs_str())
121    }
122}
123
124impl RomType {
125    const CS_2364_ADDR: usize = 13;
126    const CS1_2332_ADDR: usize = 13;
127    const CS2_2332_ADDR: usize = 12;
128    const CS1_2316_ADDR: usize = 13;
129    const CS2_2316_ADDR: usize = 11;
130    const CS3_2316_ADDR: usize = 12;
131
132    /// Returns the max size of ROM supported by this object.
133    pub const fn max_size() -> usize {
134        8192
135    }
136
137    /// Returns the size of this ROM.
138    pub const fn size(&self) -> usize {
139        match self {
140            RomType::Type2364 { .. } => 8192,
141            RomType::Type2332 { .. } => 4096,
142            RomType::Type2316 { .. } => 2048,
143        }
144    }
145
146    /// Returns the active CS mask for this ROM type.
147    pub fn cs_active_mask(&self) -> usize {
148        match self {
149            RomType::Type2364 { cs } => cs.bit() << Self::CS_2364_ADDR,
150            RomType::Type2332 { cs1, cs2 } => {
151                cs1.bit() << Self::CS1_2332_ADDR | cs2.bit() << Self::CS2_2332_ADDR
152            }
153            RomType::Type2316 { cs1, cs2, cs3 } => {
154                cs1.bit() << Self::CS1_2316_ADDR
155                    | cs2.bit() << Self::CS2_2316_ADDR
156                    | cs3.bit() << Self::CS3_2316_ADDR
157            }
158        }
159    }
160
161    /// Returns all supported ROM types.
162    pub const fn all() -> &'static [RomType] {
163        &ALL_ROM_TYPES
164    }
165
166    pub const fn type_str(&self) -> &'static str {
167        match self {
168            RomType::Type2364 { .. } => "2364",
169            RomType::Type2332 { .. } => "2332",
170            RomType::Type2316 { .. } => "2316",
171        }
172    }
173
174    pub const fn cs_str(&self) -> &'static str {
175        match self {
176            RomType::Type2364 { cs } => match cs {
177                CsActive::Low => "CS Low",
178                CsActive::High => "CS High",
179            },
180            RomType::Type2332 { cs1, cs2 } => match (cs1, cs2) {
181                (CsActive::Low, CsActive::Low) => "CS1 Low, CS2 Low",
182                (CsActive::Low, CsActive::High) => "CS1 Low, CS2 High",
183                (CsActive::High, CsActive::Low) => "CS1 High, CS2 Low",
184                (CsActive::High, CsActive::High) => "CS1 High, CS2 High",
185            },
186            RomType::Type2316 { cs1, cs2, cs3 } => match (cs1, cs2, cs3) {
187                (CsActive::Low, CsActive::Low, CsActive::Low) => "CS1 Low, CS2 Low, CS3 Low",
188                (CsActive::Low, CsActive::Low, CsActive::High) => "CS1 Low, CS2 Low, CS3 High",
189                (CsActive::Low, CsActive::High, CsActive::Low) => "CS1 Low, CS2 High, CS3 Low",
190                (CsActive::Low, CsActive::High, CsActive::High) => "CS1 Low, CS2 High, CS3 High",
191                (CsActive::High, CsActive::Low, CsActive::Low) => "CS1 High, CS2 Low, CS3 Low",
192                (CsActive::High, CsActive::Low, CsActive::High) => "CS1 High, CS2 Low, CS3 High",
193                (CsActive::High, CsActive::High, CsActive::Low) => "CS1 High, CS2 High, CS3 Low",
194                (CsActive::High, CsActive::High, CsActive::High) => "CS1 High, CS2 High, CS3 High",
195            },
196        }
197    }
198
199    /// Retrieves the binary size of this structure when binary serialized.
200    pub const fn binary_size() -> usize {
201        4
202    }
203
204    /// Format is RomTypeByte followed by up to 3 CsActiveByte values.
205    pub fn from_bytes(buf: &[u8]) -> Result<Self, Error> {
206        if buf.len() < Self::binary_size() {
207            return Err(Error::ParseError);
208        }
209        match RomTypeByte::from_byte(buf[0])? {
210            RomTypeByte::Type2364 => {
211                let cs = CsActiveByte::from_byte(buf[1])?.into();
212                Ok(RomType::Type2364 { cs })
213            }
214            RomTypeByte::Type2332 => {
215                let cs1 = CsActiveByte::from_byte(buf[1])?.into();
216                let cs2 = CsActiveByte::from_byte(buf[2])?.into();
217                Ok(RomType::Type2332 { cs1, cs2 })
218            }
219            RomTypeByte::Type2316 => {
220                let cs1 = CsActiveByte::from_byte(buf[1])?.into();
221                let cs2 = CsActiveByte::from_byte(buf[2])?.into();
222                let cs3 = CsActiveByte::from_byte(buf[3])?.into();
223                Ok(RomType::Type2316 { cs1, cs2, cs3 })
224            }
225        }
226    }
227
228    /// Serializes this structure into the given buffer.
229    pub fn to_bytes(&self, buf: &mut [u8]) -> Result<(), Error> {
230        if buf.len() < Self::binary_size() {
231            return Err(Error::ParseError);
232        }
233        match self {
234            RomType::Type2364 { cs } => {
235                buf[0] = RomTypeByte::Type2364.to_byte();
236                buf[1] = CsActiveByte::from(*cs).to_byte();
237                buf[2] = 0;
238                buf[3] = 0;
239            }
240            RomType::Type2332 { cs1, cs2 } => {
241                buf[0] = RomTypeByte::Type2332.to_byte();
242                buf[1] = CsActiveByte::from(*cs1).to_byte();
243                buf[2] = CsActiveByte::from(*cs2).to_byte();
244                buf[3] = 0;
245            }
246            RomType::Type2316 { cs1, cs2, cs3 } => {
247                buf[0] = RomTypeByte::Type2316.to_byte();
248                buf[1] = CsActiveByte::from(*cs1).to_byte();
249                buf[2] = CsActiveByte::from(*cs2).to_byte();
250                buf[3] = CsActiveByte::from(*cs3).to_byte();
251            }
252        }
253        Ok(())
254    }
255}
256
257// Enumeration of all possible ROM types.
258const NUM_ROM_TYPES: usize = 14;
259const ALL_ROM_TYPES: [RomType; NUM_ROM_TYPES] = [
260    RomType::Type2364 { cs: CsActive::Low },
261    RomType::Type2364 { cs: CsActive::High },
262    RomType::Type2332 {
263        cs1: CsActive::Low,
264        cs2: CsActive::Low,
265    },
266    RomType::Type2332 {
267        cs1: CsActive::Low,
268        cs2: CsActive::High,
269    },
270    RomType::Type2332 {
271        cs1: CsActive::High,
272        cs2: CsActive::Low,
273    },
274    RomType::Type2332 {
275        cs1: CsActive::High,
276        cs2: CsActive::High,
277    },
278    RomType::Type2316 {
279        cs1: CsActive::Low,
280        cs2: CsActive::Low,
281        cs3: CsActive::Low,
282    },
283    RomType::Type2316 {
284        cs1: CsActive::Low,
285        cs2: CsActive::Low,
286        cs3: CsActive::High,
287    },
288    RomType::Type2316 {
289        cs1: CsActive::Low,
290        cs2: CsActive::High,
291        cs3: CsActive::Low,
292    },
293    RomType::Type2316 {
294        cs1: CsActive::Low,
295        cs2: CsActive::High,
296        cs3: CsActive::High,
297    },
298    RomType::Type2316 {
299        cs1: CsActive::High,
300        cs2: CsActive::Low,
301        cs3: CsActive::Low,
302    },
303    RomType::Type2316 {
304        cs1: CsActive::High,
305        cs2: CsActive::Low,
306        cs3: CsActive::High,
307    },
308    RomType::Type2316 {
309        cs1: CsActive::High,
310        cs2: CsActive::High,
311        cs3: CsActive::Low,
312    },
313    RomType::Type2316 {
314        cs1: CsActive::High,
315        cs2: CsActive::High,
316        cs3: CsActive::High,
317    },
318];