gameboy_rom/lib.rs
1//! A parser for Gameboy ROMS.
2//!
3//! This crate provides a streaming Gameboy instruction parser as well as some
4//! high-level types like `RomHeader` and `RomType`.
5//!
6//! Basic validation is provided through the `validate` method on `RomHeader`.
7//!
8//! Header logic based on info from the [GB CPU Manual].
9//!
10//! Opcode parsing logic was created with this [opcode table] as a reference.
11//!
12//! Information from other places is and other places is called out in comments in the relevant files
13//!
14//! [GB CPU Manual]: http://marc.rawer.de/Gameboy/Docs/GBCPUman.pdf
15//! [opcode table]: https://www.pastraiser.com/cpu/gameboy/gameboy_opcodes.html
16//!
17//! Basic usage:
18//! ```
19//! # fn example() -> Result<(), String> {
20//! # let rom_buffer = vec![0; 0x100];
21//! let rom = gameboy_rom::GameBoyRom::new(rom_buffer.as_slice());
22//! let rom_header = rom.parse_header()?;
23//! # Ok(())
24//! # }
25//! ```
26
27pub mod header;
28pub mod opcodes;
29mod parser;
30pub mod util;
31
32pub use crate::header::*;
33pub use crate::opcodes::*;
34
35/// Top level type for dealing with GameBoy ROMs.
36#[derive(Debug)]
37pub struct GameBoyRom<'rom> {
38 rom_data: &'rom [u8],
39}
40
41impl<'rom> GameBoyRom<'rom> {
42 /// Create a new instance of the `GameBoyRom`.
43 pub fn new(rom_bytes: &'rom [u8]) -> Self {
44 Self {
45 rom_data: rom_bytes,
46 }
47 }
48
49 /// Parse the ROM header and return a high level type containing its data.
50 pub fn parse_header(&self) -> Result<RomHeader, String> {
51 parser::parse_rom_header(self.rom_data)
52 .map_err(|e| format!("Failed to parse ROM: {:?}", e))
53 .map(|(_, rh)| rh)
54 }
55
56 /// Get an iterator over the instructions starting at the given address.
57 pub fn get_instructions_at(&self, address: usize) -> OpcodeStreamer {
58 OpcodeStreamer::new(self.rom_data, address)
59 }
60}
61
62/// Streaming parser over GameBoy [`Opcode`]s.
63pub struct OpcodeStreamer<'rom> {
64 rom_data: &'rom [u8],
65 current_index: usize,
66}
67
68impl<'rom> OpcodeStreamer<'rom> {
69 pub(crate) fn new(rom_bytes: &'rom [u8], start: usize) -> Self {
70 Self {
71 rom_data: rom_bytes,
72 current_index: start,
73 }
74 }
75}
76
77impl<'rom> Iterator for OpcodeStreamer<'rom> {
78 type Item = Opcode;
79
80 fn next(&mut self) -> Option<Self::Item> {
81 match parser::parse_instruction(&self.rom_data[self.current_index..]) {
82 Ok((i, op)) => {
83 // Compare the pointers to find out how many bytes we read
84 let offset =
85 i.as_ptr() as usize - (&self.rom_data[self.current_index..]).as_ptr() as usize;
86 self.current_index += offset;
87
88 Some(op)
89 }
90 Err(_) => None,
91 }
92 }
93}