padme_core/rom/
rom.rs

1#[cfg(debug_assertions)]
2use core::fmt;
3use core::ops::Deref;
4use core::str;
5
6use crate::region::*;
7use crate::Error;
8use super::{CgbMode, CartridgeType, Licensee};
9use super::mbc::*;
10
11const HEADER_TITLE_START: usize         = 0x0134;
12const HEADER_TITLE_END: usize           = 0x0143;
13const HEADER_CGB_FLAG: usize            = 0x0143;
14const HEADER_NEW_LICENSEE_CODE: usize   = 0x0144;
15const HEADER_SGB_FLAG: usize            = 0x0146;
16const HEADER_CARTRIDGE_TYPE: usize      = 0x0147;
17const HEADER_ROM_SIZE: usize            = 0x0148;
18const HEADER_RAM_SIZE: usize            = 0x0149;
19const HEADER_DESTINATION_CODE: usize    = 0x014A;
20const HEADER_OLD_LICENSEE_CODE: usize   = 0x014B;
21const HEADER_VERSION: usize             = 0x014C;
22const HEADER_HEADER_CHECKSUM: usize     = 0x014D;
23
24pub struct Rom<T: Deref<Target=[u8]>> {
25    /// Cartridge data, this is provided by the user depending on their platform
26    /// This can be a Vec<u8>, a static array,
27    /// Or generally any kind of structure that can be dereferenced to a u8
28    storage: T,
29    /// Support for Mbc0, Mbc1, etc
30    mbc_ctrl: Mbc,
31}
32
33impl<T: Deref<Target=[u8]>> Rom<T> {
34    /// Build a rom from a sequence of storage
35    pub fn load(storage: T) -> Result<Self, Error> {
36        if storage.len() < ROM_REGION_SIZE {
37            Err(Error::InvalidRomSize(storage.len()))
38        } else {
39            let mut rom = Self {
40                storage,
41                mbc_ctrl: Mbc::from(Mbc0),
42            };
43            // MBC can be a dynamically dispatched on the stack
44            // which is awesome in a no_std / no alloc environment
45            // This still can be improved by extracting the Rom header
46            // which would allow setting the mbc controller before creating the rom instance
47            rom.mbc_ctrl = match rom.cartridge_type() {
48                CartridgeType::RomOnly => Mbc::from(Mbc0),
49                CartridgeType::Mbc1 |
50                CartridgeType::Mbc1Ram |
51                CartridgeType::Mbc1RamBattery => Mbc::from(Mbc1::new()),
52                CartridgeType::Mbc3 |
53                CartridgeType::Mbc3Ram |
54                CartridgeType::Mbc3RamBattery |
55                CartridgeType::Mbc3TimerBattery |
56                CartridgeType::Mbc3TimerRamBattery => Mbc::from(Mbc3::new()),
57                _ => unimplemented!(),
58            };
59
60            Ok(rom)
61        }
62    }
63
64    /// Shortcut to retrieve header part
65    pub fn header(&self) -> &[u8] {
66        &self.storage[HEADER_TITLE_START..HEADER_HEADER_CHECKSUM]
67    }
68
69    /// Shortcut to retrieve the location of the title
70    pub fn title(&self) -> Result<&str, str::Utf8Error> {
71        let title_part = &self.storage[HEADER_TITLE_START..=HEADER_TITLE_END];
72        for (i, &byte) in title_part.iter().enumerate() {
73            if byte == 0x00 {
74                return str::from_utf8(
75                    &self.storage[HEADER_TITLE_START..(HEADER_TITLE_START + i)]
76                );
77            }
78        }
79
80        str::from_utf8(title_part)
81    }
82
83    /// Shortcut to retrieve the cgb mode from the header
84    pub fn cgb_mode(&self) -> CgbMode {
85        let cgb_flag = self.storage[HEADER_CGB_FLAG];
86
87        match cgb_flag {
88            0xC0 => CgbMode::Cgb,
89            cgb_flag if cgb_flag & 0x80 == 0x80 => CgbMode::Both,
90            _ => CgbMode::None,
91        }
92    }
93
94    /// Shortcut to retrieve the rom size from the header
95    pub fn size(&self) -> u16 {
96        let n = self.storage[HEADER_ROM_SIZE];
97
98        match n {
99            0x00..=0x08 => (32 << n) as u16,
100            _ => 0u16,
101        }
102    }
103
104    /// Shortcut to retrieve the ram size from the header
105    pub fn ram_size(&self) -> u16 {
106        match self.storage[HEADER_RAM_SIZE] {
107            0x00 => 0u16,
108            0x02 => 8u16,
109            0x03 => 32u16,
110            0x04 => 128u16,
111            0x05 => 64u16,
112            _ => 0x0u16,
113        }
114    }
115
116    /// Shortcut to retrieve if the rom supports sgb from the header
117    pub fn is_sgb(&self) -> bool {
118        self.storage[HEADER_SGB_FLAG] == 0x03
119    }
120
121    /// Shortcut to retrieve the cartridge type from the header
122    pub fn cartridge_type(&self) -> CartridgeType {
123        match self.storage[HEADER_CARTRIDGE_TYPE] {
124            0x00 => CartridgeType::RomOnly,
125            0x01 => CartridgeType::Mbc1,
126            0x02 => CartridgeType::Mbc1Ram,
127            0x03 => CartridgeType::Mbc1RamBattery,
128            0x05 => CartridgeType::Mbc2,
129            0x06 => CartridgeType::Mbc2Battery,
130            0x08 => CartridgeType::RomRam,
131            0x09 => CartridgeType::RomRamBattery,
132            0x0B => CartridgeType::Mmm01,
133            0x0C => CartridgeType::Mmm01Ram,
134            0x0D => CartridgeType::Mmm01RamBattery,
135            0x0F => CartridgeType::Mbc3TimerBattery,
136            0x10 => CartridgeType::Mbc3TimerRamBattery,
137            0x11 => CartridgeType::Mbc3,
138            0x12 => CartridgeType::Mbc3Ram,
139            0x13 => CartridgeType::Mbc3RamBattery,
140            0x19 => CartridgeType::Mbc5,
141            0x1A => CartridgeType::Mbc5Ram,
142            0x1B => CartridgeType::Mbc5RamBattery,
143            0x1C => CartridgeType::Mbc5Rumble,
144            0x1D => CartridgeType::Mbc5RumbleRam,
145            0x1E => CartridgeType::Mbc5RumbleRamBattery,
146            0x20 => CartridgeType::Mbc6,
147            0x22 => CartridgeType::Mbc7SensorRumbleRamBattery,
148            0xFC => CartridgeType::PocketCamera,
149            0xFD => CartridgeType::BandaiTama5,
150            0xFE => CartridgeType::HuC3,
151            0xFF => CartridgeType::HuC1RamBattery,
152            _ => CartridgeType::Unknown,
153        }
154    }
155
156    /// Shortcut to retrieve if the cartridge is japanese from the header
157    pub fn is_jp(&self) -> bool {
158        self.storage[HEADER_DESTINATION_CODE] == 0x00
159    }
160
161    /// Shortcut to retrieve the version from the header
162    pub fn version(&self) -> u8 {
163        self.storage[HEADER_VERSION]
164    }
165
166    /// Verify the checksum from the header
167    pub fn verify_header_checksum(&self) -> bool {
168        let mut x = 0u8;
169
170        for &byte in self.header().iter() {
171            x = x.wrapping_sub(byte).wrapping_sub(1);
172        }
173
174        x == self.storage[HEADER_HEADER_CHECKSUM]
175    }
176
177    /// Shortcut to retrieve the licensee from the header
178    pub fn licensee(&self) -> Licensee {
179        let old_licensee_code = self.storage[HEADER_OLD_LICENSEE_CODE];
180
181        match old_licensee_code {
182            0x00 => Licensee::None,
183            0x01 => Licensee::Nintendo,
184            0x0C => Licensee::EliteSystems,
185            0x13 => Licensee::ElectronicArts,
186            0x18 => Licensee::HudsonSoft,
187            0x19 => Licensee::ItcEntertainment,
188            0x1A => Licensee::Yanoman,
189            0x1D => Licensee::Clary,
190            0x1F => Licensee::Virgin,
191            0x24 => Licensee::PcmComplete,
192            0x25 => Licensee::SanX,
193            0x28 => Licensee::KotobukiSystems,
194            0x29 => Licensee::Seta,
195            0x30 => Licensee::Infogrames,
196            0x31 => Licensee::Nintendo,
197            0x32 => Licensee::Bandai,
198            0x34 => Licensee::Konami,
199            0x35 => Licensee::Hector,
200            0x38 => Licensee::Capcom,
201            0x39 => Licensee::Banpresto,
202            0x3C => Licensee::EntertainmentI,
203            0x3E => Licensee::Gremlin,
204            0x41 => Licensee::Ubisoft,
205            0x42 => Licensee::Atlus,
206            0x44 => Licensee::Malibu,
207            0x46 => Licensee::Angel,
208            0x47 => Licensee::SpectrumHoloby,
209            0x49 => Licensee::Irem,
210            0x4A => Licensee::Virgin,
211            0x4D => Licensee::Malibu,
212            0x4F => Licensee::UsGold,
213            0x50 => Licensee::Absolute,
214            0x51 => Licensee::Acclaim,
215            0x52 => Licensee::Activision,
216            0x53 => Licensee::AmericanSammy,
217            0x54 => Licensee::Gametek,
218            0x55 => Licensee::ParkPlace,
219            0x56 => Licensee::Ljn,
220            0x57 => Licensee::Matchbox,
221            0x59 => Licensee::MiltonBradley,
222            0x5A => Licensee::Mindscape,
223            0x5B => Licensee::Romstar,
224            0x5C => Licensee::NaxatSoft,
225            0x5D => Licensee::Tradewest,
226            0x60 => Licensee::Titus,
227            0x61 => Licensee::Virgin,
228            0x67 => Licensee::Ocean,
229            0x69 => Licensee::ElectronicArts,
230            0x6E => Licensee::EliteSystems,
231            0x6F => Licensee::ElectroBrain,
232            0x70 => Licensee::Infogrames,
233            0x71 => Licensee::Interplay,
234            0x72 => Licensee::Broderbund,
235            0x73 => Licensee::SculpteredSoft,
236            0x75 => Licensee::TheSalesCurve,
237            0x78 => Licensee::Thq,
238            0x79 => Licensee::Accolade,
239            0x7A => Licensee::TriffixEntertainment,
240            0x7C => Licensee::Microprose,
241            0x7F => Licensee::Kemco,
242            0x80 => Licensee::Misawa,
243            0x83 => Licensee::Lozc,
244            0x86 => Licensee::TokumaShoten,
245            0x8B => Licensee::BulletProof,
246            0x8C => Licensee::VicTokai,
247            0x8E => Licensee::Ape,
248            0x8F => Licensee::Imax,
249            0x91 => Licensee::ChunSoft,
250            0x92 => Licensee::VideoSystem,
251            0x93 => Licensee::Tsuburava,
252            0x95 => Licensee::Varie,
253            0x96 => Licensee::YonezawaSpal,
254            0x97 => Licensee::Kaneko,
255            0x99 => Licensee::Arc,
256            0x9A => Licensee::NihonBussan,
257            0x9B => Licensee::Tecmo,
258            0x9C => Licensee::Imagineer,
259            0x9D => Licensee::Banpresto,
260            0x9F => Licensee::Nova,
261            0xA1 => Licensee::HoriElectric,
262            0xA2 => Licensee::Bandai,
263            0xA4 => Licensee::Konami,
264            0xA6 => Licensee::Kawada,
265            0xA7 => Licensee::Takara,
266            0xA9 => Licensee::TechnosJapan,
267            0xAA => Licensee::Broderbund,
268            0xAC => Licensee::ToeiAnimation,
269            0xAD => Licensee::Toho,
270            0xAF => Licensee::Namco,
271            0xB0 => Licensee::Acclaim,
272            0xB1 => Licensee::Nexoft,
273            0xB2 => Licensee::Bandai,
274            0xB4 => Licensee::Enix,
275            0xB6 => Licensee::Hal,
276            0xB7 => Licensee::Snk,
277            0xB9 => Licensee::PonyCanyon,
278            0xBA => Licensee::CultureBrain,
279            0xBB => Licensee::Sunsoft,
280            0xBD => Licensee::Sony,
281            0xBF => Licensee::Sammy,
282            0xC0 => Licensee::Taito,
283            0xC2 => Licensee::Kemco,
284            0xC3 => Licensee::SquareSoft,
285            0xC4 => Licensee::TokumaShotenIntermedia,
286            0xC5 => Licensee::DataEast,
287            0xC6 => Licensee::TonkinHouse,
288            0xC8 => Licensee::Koei,
289            0xC9 => Licensee::Ufl,
290            0xCA => Licensee::Ultra,
291            0xCB => Licensee::Vap,
292            0xCC => Licensee::Use,
293            0xCD => Licensee::Meldac,
294            0xCE => Licensee::PonyCanyon,
295            0xCF => Licensee::Angel,
296            0xD0 => Licensee::Taito,
297            0xD1 => Licensee::Sofel,
298            0xD2 => Licensee::Quest,
299            0xD3 => Licensee::Sigma,
300            0xD4 => Licensee::AskKodansha,
301            0xD6 => Licensee::NaxatSoft,
302            0xD7 => Licensee::CopyaSystems,
303            0xD9 => Licensee::Banpresto,
304            0xDA => Licensee::Tomy,
305            0xDB => Licensee::Ljn,
306            0xDD => Licensee::Ncs,
307            0xDE => Licensee::Human,
308            0xDF => Licensee::Altron,
309            0xE0 => Licensee::Jaleco,
310            0xE1 => Licensee::Towachiki,
311            0xE2 => Licensee::Uutaka,
312            0xE3 => Licensee::Varie,
313            0xE5 => Licensee::Epoch,
314            0xE7 => Licensee::Athena,
315            0xE8 => Licensee::Asmik,
316            0xE9 => Licensee::Natsume,
317            0xEA => Licensee::KingRecords,
318            0xEB => Licensee::Atlus,
319            0xEC => Licensee::EpicSonyRecords,
320            0xEE => Licensee::Igs,
321            0xF0 => Licensee::AWave,
322            0xF3 => Licensee::ExtremeEntertainment,
323            0xFF => Licensee::Ljn,
324            0x33 => {
325                let new_licensee_code = make_u16!(
326                    self.storage[HEADER_NEW_LICENSEE_CODE],
327                    self.storage[HEADER_NEW_LICENSEE_CODE + 1]
328                );
329                match new_licensee_code {
330                    0x3030 => Licensee::None,
331                    0x3031 => Licensee::Nintendo,
332                    0x3038 => Licensee::Capcom,
333                    0x3133 => Licensee::ElectronicArts,
334                    0x3138 => Licensee::HudsonSoft,
335                    0x3139 => Licensee::BAi,
336                    0x3230 => Licensee::Kss,
337                    0x3232 => Licensee::Pow,
338                    0x3234 => Licensee::PcmComplete,
339                    0x3235 => Licensee::SanX,
340                    0x3238 => Licensee::KemcoJapan,
341                    0x3239 => Licensee::Seta,
342                    0x3330 => Licensee::Viacom,
343                    0x3331 => Licensee::Nintendo,
344                    0x3332 => Licensee::Bandai,
345                    0x3333 => Licensee::OceanAcclaim,
346                    0x3334 => Licensee::Konami,
347                    0x3335 => Licensee::Hector,
348                    0x3337 => Licensee::Taito,
349                    0x3338 => Licensee::Hudson,
350                    0x3339 => Licensee::Banpresto,
351                    0x3431 => Licensee::Ubisoft,
352                    0x3432 => Licensee::Atlus,
353                    0x3434 => Licensee::Malibu,
354                    0x3436 => Licensee::Angel,
355                    0x3437 => Licensee::BulletProof,
356                    0x3439 => Licensee::Irem,
357                    0x3530 => Licensee::Absolute,
358                    0x3531 => Licensee::Acclaim,
359                    0x3532 => Licensee::Activision,
360                    0x3533 => Licensee::AmericanSammy,
361                    0x3534 => Licensee::Konami,
362                    0x3535 => Licensee::HitechEntertainment,
363                    0x3536 => Licensee::Ljn,
364                    0x3537 => Licensee::Matchbox,
365                    0x3538 => Licensee::Mattel,
366                    0x3539 => Licensee::MiltonBradley,
367                    0x3630 => Licensee::Titus,
368                    0x3631 => Licensee::Virgin,
369                    0x3634 => Licensee::LucasArts,
370                    0x3637 => Licensee::Ocean,
371                    0x3639 => Licensee::ElectronicArts,
372                    0x3730 => Licensee::Infogrames,
373                    0x3731 => Licensee::Interplay,
374                    0x3732 => Licensee::Broderbund,
375                    0x3733 => Licensee::Sculptured,
376                    0x3735 => Licensee::Sci,
377                    0x3738 => Licensee::Thq,
378                    0x3739 => Licensee::Accolade,
379                    0x3830 => Licensee::Misawa,
380                    0x3833 => Licensee::Lozc,
381                    0x3836 => Licensee::TokumaShotenIntermedia,
382                    0x3837 => Licensee::TsukudaOriginal,
383                    0x3931 => Licensee::Chunsoft,
384                    0x3932 => Licensee::VideoSystem,
385                    0x3933 => Licensee::OceanAcclaim,
386                    0x3935 => Licensee::Varie,
387                    0x3936 => Licensee::YonezawaSpal,
388                    0x3937 => Licensee::Kaneko,
389                    0x3939 => Licensee::PackInSoft,
390                    0x4134 => Licensee::Konami,
391                    _ => Licensee::Unknown,
392                }
393            },
394            _ => Licensee::Unknown,
395        }
396    }
397}
398
399impl<T: Deref<Target=[u8]>> MemoryRegion for Rom<T> {
400    fn read(&self, address: u16) -> u8 {
401        self.mbc_ctrl.read(&self.storage, address)
402    }
403
404    fn write(&mut self, address: u16, value: u8) {
405        self.mbc_ctrl.write(address, value)
406    }
407}
408
409#[cfg(debug_assertions)]
410impl<T: Deref<Target=[u8]>> fmt::Debug for Rom<T> {
411    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
412        write!(f, "ROM \n\
413                   ---\n\
414                   Cartridge type: {:?}\n\
415                   Licensee: {:?}\n\
416                   Size: {} | Ram size: {}\n\
417                   CGB: {:?} | SGB: {} | Japanese: {}\n\
418                   Version: {}\n\
419                   Checksum: {}\n\
420                   ",
421               self.cartridge_type(), self.licensee(), self.size(), self.ram_size(),
422               self.cgb_mode(), self.is_sgb(), self.is_jp(), self.version(),
423               self.verify_header_checksum(),
424        )
425    }
426}