Skip to content

Commit cda14b6

Browse files
committed
reading from ROM in .nes file
1 parent 462ac75 commit cda14b6

File tree

4 files changed

+109
-0
lines changed

4 files changed

+109
-0
lines changed

roms/snake.nes

32 KB
Binary file not shown.

src/bus.rs

+12
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@ impl Bus {
2828
}
2929
}
3030

31+
// TODO: More mappings..
32+
33+
// That RAM is accessible via [0x0000 … 0x2000] address space.
34+
35+
// Access to [0x2000 … 0x4020] is redirected to other available NES hardware modules: PPU, APU, GamePads, etc. (more on this later)
36+
37+
// Access to [0x4020 .. 0x6000] is a special space that different generations of cartridges used differently. It might be mapped to RAM, ROM, or nothing at all. The space is controlled by so-called mappers - special circuitry on a cartridge. We will ignore this space.
38+
39+
// Access to [0x6000 .. 0x8000] is reserved to a RAM space on a cartridge if a cartridge has one. It was used in games like Zelda for storing and retrieving the game state. We will ignore this space as well.
40+
41+
// Access to [0x8000 … 0xFFFF] is mapped to Program ROM (PRG ROM) space on a cartridge.
42+
3143
impl Mem for Bus {
3244
fn mem_read(&self, addr: u16) -> u8 {
3345
if (RAM..RAM_MIRROR_END).contains(&addr) {

src/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::time::Duration;
66

77
mod bus;
88
mod core;
9+
mod rom;
910

1011
use rand::random;
1112
use sdl2::event::Event;

src/rom.rs

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#[derive(PartialEq, Eq, Debug)]
2+
enum Mirroring {
3+
FourScreen,
4+
Vertical,
5+
Horizontal,
6+
}
7+
8+
#[derive(PartialEq, Eq, Debug)]
9+
enum Mapper {
10+
Zero,
11+
}
12+
13+
pub struct Rom {
14+
prg_rom: Vec<u8>,
15+
chr_rom: Vec<u8>,
16+
mapper: Mapper,
17+
mirroring: Mirroring,
18+
}
19+
20+
const CHR_ROM_PAGE_SIZE: usize = 256;
21+
const PRG_ROM_PAGE_SIZE: usize = 256;
22+
23+
impl Rom {
24+
pub fn new(bytes: &[u8]) -> Self {
25+
if bytes.len() < 16 {
26+
panic!("invalid rom: less than 16 bytes long")
27+
}
28+
let header = &bytes[0..16];
29+
30+
// check if NES^Z is set
31+
let nes_file_prefix: [u8; 4] = [0x4E, 0x45, 0x53, 0x1A];
32+
if !header[0..4].eq(&nes_file_prefix) {
33+
panic!("invalid rom: lacks the 'NES^Z' prefix")
34+
}
35+
36+
let prg_rom_banks = header[4];
37+
let chr_rom_banks = header[5];
38+
let control_byte_1 = header[6];
39+
let control_byte_2 = header[7];
40+
// let size_of_prg_ram_times_8kb = header[8];
41+
42+
let has_trainer_bytes = control_byte_1 ^ (1 << 2) > 0;
43+
let is_four_screen = control_byte_1 & (1 << 3) > 0;
44+
let is_vertical_screen = control_byte_1 & 1 > 0;
45+
46+
let lo = (control_byte_1 & 0b1111_0000) >> 4;
47+
let hi = control_byte_2 & 0b1111_0000;
48+
let rom_mapper_type = hi + lo;
49+
50+
let mapper = match rom_mapper_type {
51+
0 => Mapper::Zero,
52+
_ => todo!(),
53+
};
54+
55+
let mirroring = match (is_four_screen, is_vertical_screen) {
56+
(true, _) => Mirroring::FourScreen,
57+
(false, true) => Mirroring::Vertical,
58+
(false, false) => Mirroring::Horizontal,
59+
};
60+
61+
let prg_rom_len = (prg_rom_banks as usize) * PRG_ROM_PAGE_SIZE;
62+
let chr_rom_len = (chr_rom_banks as usize) * CHR_ROM_PAGE_SIZE;
63+
64+
let header_size: usize = 16;
65+
let trainer_size = if has_trainer_bytes { 512 } else { 0 };
66+
let prg_rom_start = header_size + trainer_size;
67+
let prg_rom: Vec<u8> = bytes[prg_rom_start..prg_rom_start + prg_rom_len].to_vec();
68+
69+
let chr_rom_start = prg_rom_start + prg_rom_len;
70+
let chr_rom = bytes[chr_rom_start..chr_rom_start + chr_rom_len].to_vec();
71+
72+
Rom {
73+
prg_rom,
74+
chr_rom,
75+
mapper,
76+
mirroring,
77+
}
78+
}
79+
}
80+
81+
#[cfg(test)]
82+
mod test {
83+
use std::fs;
84+
85+
use super::*;
86+
87+
#[test]
88+
fn test_snake_rom_from_file() {
89+
let contents = fs::read("roms/snake.nes").unwrap();
90+
let rom = Rom::new(&contents);
91+
assert_eq!(rom.mapper, Mapper::Zero);
92+
assert_eq!(rom.mirroring, Mirroring::Vertical);
93+
assert_eq!(rom.prg_rom.len(), 2 * PRG_ROM_PAGE_SIZE);
94+
assert_eq!(rom.chr_rom.len(), 0);
95+
}
96+
}

0 commit comments

Comments
 (0)