Skip to content

Commit

Permalink
Working on startup mem tests
Browse files Browse the repository at this point in the history
  • Loading branch information
TylerBloom committed Sep 26, 2024
1 parent b3c8fbc commit acd3c7c
Show file tree
Hide file tree
Showing 12 changed files with 122 additions and 52 deletions.
12 changes: 6 additions & 6 deletions ghast/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ impl Debugger {
fn tile_map_0<M: 'static>(
gb: &Gameboy,
) -> impl '_ + Iterator<Item = impl Into<Element<'static, M>>> {
tile_map(&gb.mem.vram.vram.0[0x1800..0x1BFF])
tile_map(&gb.mem.vram.vram[0][0x1800..0x1BFF])
}

fn tile_map_1<M: 'static>(
gb: &Gameboy,
) -> impl '_ + Iterator<Item = impl Into<Element<'static, M>>> {
tile_map(&gb.mem.vram.vram.0[0x1C00..0x1FFF])
tile_map(&gb.mem.vram.vram[0][0x1C00..0x1FFF])
}

fn tile_map<M: 'static>(map: &[u8]) -> impl '_ + Iterator<Item = impl Into<Element<'static, M>>> {
Expand Down Expand Up @@ -95,10 +95,10 @@ fn oam_obj_repr<M: 'static>(
fn print_tile_map(gb: &Gameboy) {
let map = if check_bit_const::<3>(gb.mem.io().lcd_control) {
println!("The second tile map:");
&gb.mem.vram.vram.0[0x1C00..0x2000]
&gb.mem.vram.vram[0][0x1C00..0x2000]
} else {
println!("The first tile map:");
&gb.mem.vram.vram.0[0x1800..0x1C00]
&gb.mem.vram.vram[0][0x1800..0x1C00]
};
for i in 0..32 {
print!("[");
Expand All @@ -113,14 +113,14 @@ fn vram_0_to_tiles<M: 'static>(
gb: &Gameboy,
palette: u8,
) -> impl '_ + Iterator<Item = impl Into<Element<'static, M>>> {
vram_bank_to_tiles(gb, palette, &gb.mem.vram.vram.0)
vram_bank_to_tiles(gb, palette, &gb.mem.vram.vram[0])
}

fn vram_1_to_tiles<M: 'static>(
gb: &Gameboy,
palette: u8,
) -> impl '_ + Iterator<Item = impl Into<Element<'static, M>>> {
vram_bank_to_tiles(gb, palette, &gb.mem.vram.vram.1)
vram_bank_to_tiles(gb, palette, &gb.mem.vram.vram[1])
}

fn vram_bank_to_tiles<'a, M: 'static>(
Expand Down
4 changes: 2 additions & 2 deletions spirit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
# serde = { version = "1.0", features = ["derive"] }
# serde_json = { version = "1.0" }
# hex = { version = "0.4", features = ["serde"] }
derive_more = "0.99"
array-concat = "0.5.2"
# once_cell = "1.19"
tracing = "0.1.40"
heapless = "0.8.0"
heapless = { version = "0.8.0", features = ["serde"] }
serde = { version = "1.0.210", features = ["derive"] }
serde_with = "3.9.0"

[dev-dependencies]
postcard = { version = "1.0.10", features = ["alloc"] }
Expand Down
1 change: 1 addition & 0 deletions spirit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub mod lookup;
pub mod mem;
pub mod ppu;
pub mod rom;
pub(crate) mod utils;

/// Represents a Gameboy color with a cartridge inserted.
#[derive(Debug, Hash)]
Expand Down
13 changes: 7 additions & 6 deletions spirit/src/mem/io.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use std::ops::{Index, IndexMut};

use serde::{Deserialize, Serialize};
use tracing::trace;

use crate::{cpu::check_bit_const, lookup::InterruptOp, ppu::Pixel, ButtonInput};

use super::{vram::PpuMode, MemoryMap};

#[derive(Debug, Clone, Hash, PartialEq, Eq, Default)]
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct IoRegisters {
/// ADDR FF00
pub(super) joypad: Joypad,
Expand Down Expand Up @@ -292,7 +293,7 @@ impl Index<ObjPaletteIndex> for IoRegisters {
}

/// In GBC mode, there are extra palettes for the colors
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default)]
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct ColorPalettes {
pub(crate) index: u8,
/// This array is indexed into by the index field.
Expand Down Expand Up @@ -352,7 +353,7 @@ impl IndexMut<u16> for ColorPalettes {
}

/// All of the date for one of the 8 palettes that can be held in memory.
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default)]
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct Palette {
pub(crate) colors: [PaletteColor; 4],
}
Expand Down Expand Up @@ -385,7 +386,7 @@ impl IndexMut<u8> for Palette {

/// The colors inside a palette are a bit odd. Each color takes up two bytes and represents each
/// color with 5 bits (in little-endian). The top bit is not used.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct PaletteColor(pub [u8; 2]);

impl PaletteColor {
Expand Down Expand Up @@ -432,7 +433,7 @@ impl IndexMut<u8> for PaletteColor {
}
}

#[derive(Debug, Clone, Hash, PartialEq, Eq, Default)]
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum TimerControl {
#[default]
Disabled,
Expand Down Expand Up @@ -462,7 +463,7 @@ pub enum TimerControl {
/// The solution: Both registers will be synchronized when a tick is processed. Currently, this
/// happens immediately before the instruction is actually processed, but this would also work if
/// it happened immediately after too. As long as the tick is applied in the same order everywhere.
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone)]
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
pub(super) struct Joypad {
main: u8,
dup: u8,
Expand Down
8 changes: 5 additions & 3 deletions spirit/src/mem/mbc/mbc1.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::ops::Range;

#[derive(Debug, Hash, Clone, PartialEq, Eq)]
use serde::{Deserialize, Serialize};

#[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MBC1 {
kind: MBC1Kind,
rom: Vec<u8>,
Expand Down Expand Up @@ -101,13 +103,13 @@ impl MBC1 {
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum MBC1Kind {
Standard,
Rewired,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum BankingMode {
Simple = 0,
Advanced = 1,
Expand Down
7 changes: 6 additions & 1 deletion spirit/src/mem/mbc/mbc2.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
use std::ops::{Index, IndexMut};

use serde::{Deserialize, Serialize};
use serde_with::serde_as;

use crate::cpu::u16_check_bit_const;

#[derive(Debug, Hash, Clone, PartialEq, Eq)]
#[serde_as]
#[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MBC2 {
rom: Vec<u8>,
// TODO: This can probably be replaced with NonZeroUsize
rom_bank: u8,
// TODO: The built-in RAM only uses the lower 4-bits. There is no good way to model this via
// indexing, so we are going to rely on the ROM writers to obey this. It might be the case
// that this needs to be changed.
#[serde_as(as = "serde_with::Bytes")]
ram: Box<[u8; 512]>,
ram_enabled: u8,
dead_byte: u8,
Expand Down
3 changes: 2 additions & 1 deletion spirit/src/mem/mbc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub use mbc1::*;
pub use mbc2::*;
pub use mbc3::*;
pub use mbc5::*;
use serde::{Deserialize, Serialize};
use tracing::error;

static NINTENDO_LOGO: &[u8] = &[
Expand All @@ -23,7 +24,7 @@ static NINTENDO_LOGO: &[u8] = &[
0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC, 0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E,
];

#[derive(Hash, Clone, PartialEq, Eq)]
#[derive(Hash, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum MemoryBankController {
/// There is no external MBC. The game ROM is mapped into the 32 KiB that starts at 0x0000 and
/// extends to 0x7FFF. An additional 8 KiB of RAM could be connected. This 8 KiB starts at
Expand Down
30 changes: 18 additions & 12 deletions spirit/src/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub use mbc::MemoryBankController;
use mbc::*;

use io::IoRegisters;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use tracing::trace;
use vram::PpuMode;

Expand All @@ -26,16 +28,20 @@ pub static START_UP_HEADER: &[u8; 0x900] = include_bytes!("../cgb.bin");

pub type StartUpHeaders = ([u8; 0x100], [u8; 0x700]);

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
#[serde_as]
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct MemoryMap {
// The MBC
mbc: MemoryBankController,
// The video RAM and Object attribute map
pub vram: VRam,
// The working RAM
wram: ([u8; 0x1000], [u8; 0x1000]),
#[serde(serialize_with = "crate::utils::serialize_slices_as_one")]
#[serde(deserialize_with = "crate::utils::deserialize_slices_as_one")]
wram: [[u8; 0x1000]; 2],
io: IoRegisters,
// High RAM
#[serde_as(as = "serde_with::Bytes")]
hr: [u8; 0x7F],
/// The interrupt enable register. Bits 0-4 flag where or not certain interrupt handlers can be
/// called.
Expand All @@ -56,7 +62,7 @@ impl MemoryMap {
Self {
mbc: MemoryBankController::new(cart),
vram: VRam::new(),
wram: ([0; 0x1000], [0; 0x1000]),
wram: [[0; 0x1000]; 2],
dead_byte: 0,
io: IoRegisters::default(),
hr: [0; 0x7F],
Expand Down Expand Up @@ -185,7 +191,7 @@ impl MemoryMap {
Self {
mbc,
vram: VRam::new(),
wram: ([0; 0x1000], [0; 0x1000]),
wram: [[0; 0x1000]; 2],
dead_byte: 0,
io: IoRegisters::default(),
hr: [0; 0x7F],
Expand Down Expand Up @@ -215,11 +221,11 @@ impl Index<u16> for MemoryMap {
0x0000..=0x7FFF => &self.mbc[index],
n @ 0x8000..=0x9FFF => &self.vram[CpuVramIndex(self.io.vram_select == 1, n)],
n @ 0xA000..=0xBFFF => &self.mbc[n],
n @ 0xC000..=0xCFFF => &self.wram.0[n as usize - 0xC000],
n @ 0xD000..=0xDFFF => &self.wram.1[n as usize - 0xD000],
n @ 0xC000..=0xCFFF => &self.wram[0][n as usize - 0xC000],
n @ 0xD000..=0xDFFF => &self.wram[1][n as usize - 0xD000],
// Echo RAM
n @ 0xE000..=0xEFFF => &self.wram.0[n as usize - 0xE000],
n @ 0xF000..=0xFDFF => &self.wram.1[n as usize - 0xF000],
n @ 0xE000..=0xEFFF => &self.wram[0][n as usize - 0xE000],
n @ 0xF000..=0xFDFF => &self.wram[1][n as usize - 0xF000],
n @ 0xFE00..=0xFE9F => &self.vram[CpuOamIndex(n)],
// NOTE: This region *should not* actually be accessed, but, instead of panicking, a
// dead byte will be returned instead.
Expand All @@ -239,11 +245,11 @@ impl IndexMut<u16> for MemoryMap {
n @ 0x0000..=0x7FFF => &mut self.mbc[n],
n @ 0x8000..=0x9FFF => &mut self.vram[CpuVramIndex(self.io.vram_select == 1, n)],
n @ 0xA000..=0xBFFF => &mut self.mbc[n],
n @ 0xC000..=0xCFFF => &mut self.wram.0[n as usize - 0xC000],
n @ 0xD000..=0xDFFF => &mut self.wram.1[n as usize - 0xD000],
n @ 0xC000..=0xCFFF => &mut self.wram[0][n as usize - 0xC000],
n @ 0xD000..=0xDFFF => &mut self.wram[1][n as usize - 0xD000],
// Echo RAM
n @ 0xE000..=0xEFFF => &mut self.wram.0[n as usize - 0xE000],
n @ 0xF000..=0xFDFF => &mut self.wram.1[n as usize - 0xF000],
n @ 0xE000..=0xEFFF => &mut self.wram[0][n as usize - 0xE000],
n @ 0xF000..=0xFDFF => &mut self.wram[1][n as usize - 0xF000],
n @ 0xFE00..=0xFE9F => &mut self.vram[CpuOamIndex(n)],
// NOTE: This region *should not* actually be accessed, but, instead of panicking, a
// dead byte will be returned instead.
Expand Down
41 changes: 29 additions & 12 deletions spirit/src/mem/vram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

use std::ops::{Index, IndexMut};

use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use tracing::{info, trace};

use super::{
Expand All @@ -29,7 +31,18 @@ pub(super) struct CpuOamIndex(pub u16);

#[repr(u8)]
#[derive(
Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, derive_more::IsVariant,
Debug,
Default,
Clone,
Copy,
Hash,
PartialEq,
Eq,
PartialOrd,
Ord,
derive_more::IsVariant,
Serialize,
Deserialize,
)]
pub(crate) enum PpuMode {
/// Also refered to as "Mode 2" in the pandocs.
Expand All @@ -43,12 +56,16 @@ pub(crate) enum PpuMode {
VBlank = 3,
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
#[serde_as]
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct VRam {
// TODO: This is a GBC emulator, show it needs to support switching between banks 0 and 1.
/// The main video RAM. Accessible through the address range 0x8000 through 0x9FFF.
pub vram: ([u8; 0x2000], [u8; 0x2000]),
#[serde(serialize_with = "crate::utils::serialize_slices_as_one")]
#[serde(deserialize_with = "crate::utils::deserialize_slices_as_one")]
pub vram: [[u8; 0x2000]; 2],
/// The Object Attribute Map. Accessible through the address range 0xFE00 through 0xFE9F
#[serde_as(as = "serde_with::Bytes")]
pub oam: [u8; 0xA0],
/// The status that the PPU is currently in. This mode is set when the PPU is ticked and
/// determines how the VRAM and OAM are indexed into.
Expand All @@ -61,7 +78,7 @@ pub struct VRam {
impl VRam {
pub(super) fn new() -> Self {
Self {
vram: ([0; 0x2000], [0; 0x2000]),
vram: [[0; 0x2000]; 2],
oam: [0; 0xA0],
status: PpuMode::default(),
dead_byte: 0xFF,
Expand All @@ -86,9 +103,9 @@ impl Index<CpuVramIndex> for VRam {
&DEAD_READ_ONLY_BYTE
} else {
if bank && index < 0x9C00 {
&self.vram.1[index as usize - 0x8000]
&self.vram[1][index as usize - 0x8000]
} else {
&self.vram.0[index as usize - 0x8000]
&self.vram[0][index as usize - 0x8000]
}
}
}
Expand All @@ -102,9 +119,9 @@ impl IndexMut<CpuVramIndex> for VRam {
&mut self.dead_byte
} else {
if bank && index < 0x9C00 {
&mut self.vram.1[index as usize - 0x8000]
&mut self.vram[1][index as usize - 0x8000]
} else {
&mut self.vram.0[index as usize - 0x8000]
&mut self.vram[0][index as usize - 0x8000]
}
}
}
Expand Down Expand Up @@ -159,7 +176,7 @@ impl Index<(ObjTileDataIndex, bool)> for VRam {
&self,
(ObjTileDataIndex(index, bank), size): (ObjTileDataIndex, bool),
) -> &Self::Output {
let bank = if bank { &self.vram.1 } else { &self.vram.0 };
let bank = if bank { &self.vram[1] } else { &self.vram[0] };
let start = 16 * index as usize;
let end = start + 16 + if size { 16 } else { 0 };
&bank[start..end]
Expand All @@ -176,7 +193,7 @@ impl Index<BgTileMapInnerIndex> for VRam {
let x = x as usize / 8;
let y = y as usize / 8;
let index = 0x1800 + (second_map as usize * 0x400) + (y * 32) + x;
&self.vram.0[index]
&self.vram[0][index]
}
}

Expand All @@ -187,7 +204,7 @@ impl Index<BgTileMapAttrIndex> for VRam {
let x = x as usize / 8;
let y = y as usize / 8;
let index = 0x1800 + (y * 32) + x;
&self.vram.1[index]
&self.vram[1][index]
}
}

Expand All @@ -212,7 +229,7 @@ impl Index<BgTileDataInnerIndex> for VRam {
0x1000 + (16 * index as usize)
}
};
let bank = if bank { &self.vram.1 } else { &self.vram.0 };
let bank = if bank { &self.vram[1] } else { &self.vram[0] };
(&bank[index..index + 16]).try_into().unwrap()
}
}
Loading

0 comments on commit acd3c7c

Please sign in to comment.