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 storage: T,
29 mbc_ctrl: Mbc,
31}
32
33impl<T: Deref<Target=[u8]>> Rom<T> {
34 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 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 pub fn header(&self) -> &[u8] {
66 &self.storage[HEADER_TITLE_START..HEADER_HEADER_CHECKSUM]
67 }
68
69 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 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 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 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 pub fn is_sgb(&self) -> bool {
118 self.storage[HEADER_SGB_FLAG] == 0x03
119 }
120
121 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 pub fn is_jp(&self) -> bool {
158 self.storage[HEADER_DESTINATION_CODE] == 0x00
159 }
160
161 pub fn version(&self) -> u8 {
163 self.storage[HEADER_VERSION]
164 }
165
166 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 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}