Skip to content

Commit

Permalink
move ppu into the mmu
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanballs committed Sep 9, 2024
1 parent 5d70f75 commit 401ffc2
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 91 deletions.
7 changes: 5 additions & 2 deletions src/gameboy/debugger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ impl GameBoy {
"d" | "debug" => println!("{:#?}", &self),

"f" | "flush" => {
self.ppu.flush();
println!("OK")
//pub fn flush(&self) {
// self.tx.send(self.clone()).unwrap();
//}
//self.ppu.flush();
println!("UNSUPPORTED")
}

"ro" | "rom" => println!("{:#?}", &GBCHeader::new(&self.mmu.rom)),
Expand Down
85 changes: 12 additions & 73 deletions src/gameboy/mod.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,23 @@
mod debugger;

use colored::*;
use minifb::Key;
use std::collections::HashSet;
use std::process::exit;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{Receiver, Sender};
use std::sync::Arc;

use crate::mmu::MMU;
use crate::{
instructions::{parse, r16::R16, r8::R8, Instruction},
ppu::PPU,
registers::Registers,
};

pub struct GameBoy {
key_rx: Receiver<(bool, Key)>,

// debugger
pub debugger_enabled: bool,
breakpoints: HashSet<u16>,
memory_breakpoints: HashSet<u16>,

// state
pub registers: Registers,
pub ppu: PPU,

mmu: MMU,
pub mmu: MMU,

// interrupts
ime: bool,
Expand All @@ -41,12 +32,11 @@ pub struct GameBoy {
}

impl GameBoy {
pub fn new(rom_data: Vec<u8>, tx: Sender<PPU>, rx: Receiver<(bool, Key)>) -> GameBoy {
pub fn new(rom_data: Vec<u8>) -> GameBoy {
GameBoy {
debugger_enabled: false,

registers: Registers::new(),
ppu: PPU::new(tx),

breakpoints: HashSet::with_capacity(10),
memory_breakpoints: HashSet::with_capacity(10),
Expand All @@ -61,35 +51,6 @@ impl GameBoy {
tima: 0,
tma: 0,
tac: 0,

key_rx: rx,
}
}

pub fn start(&mut self) {
let paused = Arc::new(AtomicBool::new(false));
let p = paused.clone();

ctrlc::set_handler(move || {
if p.load(Ordering::SeqCst) {
// If already paused, stop the emulator
println!("{}", "\nSo long space cowboy".red());
exit(-1);
} else {
// If running, pause the emulator
p.store(true, Ordering::SeqCst);
println!("Received Ctrl+C! Pausing at the end of this step...");
}
})
.expect("Error setting Ctrl-C handler");

loop {
if paused.load(Ordering::SeqCst) {
self.debugger_enabled = true;
}
paused.store(false, Ordering::SeqCst);

self.step();
}
}

Expand All @@ -98,18 +59,7 @@ impl GameBoy {
let arg_1 = self.get_memory_byte(self.registers.pc + 1);
let arg_2 = self.get_memory_byte(self.registers.pc + 2);
let mut just_set_ei = false;

let (instruction, mut bytes, cycles) = parse(opcode, arg_1, arg_2);

// Handle keyboard input
loop {
match self.key_rx.try_recv() {
Ok((true, key)) => self.mmu.joypad.handle_key_down(key),
Ok((false, key)) => self.mmu.joypad.handle_key_up(key),
_ => break,
}
}

// Enable the debugger immediately upon encountering a breakpoint
if self.breakpoints.contains(&self.registers.pc) {
self.debugger_enabled = true;
Expand Down Expand Up @@ -539,7 +489,7 @@ impl GameBoy {
};

self.registers.pc += bytes as u16;
self.ppu.do_cycle(cycles as u32 / 4);
self.mmu.ppu.do_cycle(cycles as u32 / 4);

// Increase DIV register
if 0xff - self.cycles_since_div >= cycles {
Expand All @@ -549,9 +499,9 @@ impl GameBoy {

// Handle interrupts
if self.ime && !just_set_ei {
if self.get_memory_byte(0xFF0F) & 1 > 0 && self.ppu.vblank_irq {
if self.get_memory_byte(0xFF0F) & 1 > 0 && self.mmu.ppu.vblank_irq {
// Call 0x40
self.ppu.vblank_irq = false;
self.mmu.ppu.vblank_irq = false;
self.ime = false;

self.set_memory_word(self.registers.sp - 2, self.registers.pc + 3);
Expand All @@ -578,7 +528,7 @@ impl GameBoy {
0x0..=0x7FFF => self.mmu.read_byte(addr),

// VRAM
0x8000..=0x9FFF => self.ppu.get_byte(addr),
0x8000..=0x9FFF => self.mmu.read_byte(addr),

// External RAM
0xA000..=0xBFFF => {
Expand Down Expand Up @@ -611,14 +561,10 @@ impl GameBoy {
0xFF06 => self.tma,
0xFF07 => self.tac,

0xFF0F => self.ppu.vblank_irq as u8,
0xFF50 => self.mmu.read_byte(addr), // boot rom status
0xFFFF => self.ie,

// Delegate OAM and I/O Registers to PPU
0xFE00..=0xFF7F => self.ppu.get_byte(addr),

// HRam
0xFE00..=0xFF7F => self.mmu.read_byte(addr),
0xFF80..=0xFFFE => self.mmu.read_byte(addr),
}
}
Expand All @@ -644,7 +590,7 @@ impl GameBoy {
}

// VRAM
0x8000..=0x9FFF => self.ppu.set_byte(addr, byte),
0x8000..=0x9FFF => self.mmu.write_byte(addr, byte),

// External RAM
0xA000..=0xBFFF => {
Expand All @@ -653,16 +599,14 @@ impl GameBoy {
}

// Work RAM
0x8000..=0xDFFF => self.mmu.write_byte(addr, byte),
0xC000..=0xDFFF => self.mmu.write_byte(addr, byte),

// Echo RAM
0xE000..=0xFDFF => unreachable!(),

// Enable/disable boot rom
0xFF50 => self.mmu.write_byte(addr, byte),
0xFF0F => {
self.ppu.vblank_irq = byte & 0x1 > 0;
}
0xFF0F => self.mmu.write_byte(addr, byte),

// Joy pad input
0xFF00 => self.mmu.write_byte(addr, byte),
Expand Down Expand Up @@ -694,12 +638,7 @@ impl GameBoy {
0xFEA0..=0xFEFF => (),

// Delegate OAM and I/O Registers to PPU
0xFE00..=0xFF7F => {
//if addr == 0xFF7F {
// self.debugger_cli();
//}
self.ppu.set_byte(addr, byte)
}
0xFE00..=0xFF7F => self.mmu.write_byte(addr, byte),

// HRam
0xFF80..=0xFFFE => self.mmu.write_byte(addr, byte),
Expand Down
47 changes: 42 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
mod gameboy;
pub mod instructions;
pub mod mmu;
mod ppu;
mod registers;
mod renderer;

use colored::*;
use std::fs::File;
use std::io::Read;
use std::sync::mpsc;
use std::process::exit;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{mpsc, Arc};
use std::thread;

use gameboy::GameBoy;
use minifb::Key;
use ppu::PPU;
use mmu::ppu::PPU;
use renderer::window_loop;

fn read_file_to_bytes(filename: &str) -> Result<Vec<u8>, std::io::Error> {
Expand All @@ -28,8 +30,43 @@ fn main() {

let emulator_loop = thread::spawn(move || {
let rom_data = read_file_to_bytes("roms/tetris.gb").unwrap();
let mut gameboy = GameBoy::new(rom_data, tx.clone(), rx_key);
gameboy.start()
let mut gameboy = GameBoy::new(rom_data);

let paused = Arc::new(AtomicBool::new(false));
let p = paused.clone();

ctrlc::set_handler(move || {
if p.load(Ordering::SeqCst) {
// If already paused, stop the emulator
println!("{}", "\nSo long space cowboy".red());
exit(-1);
} else {
// If running, pause the emulator
p.store(true, Ordering::SeqCst);
println!("Received Ctrl+C! Pausing at the end of this step...");
}
})
.expect("Error setting Ctrl-C handler");

loop {
if paused.load(Ordering::SeqCst) {
gameboy.debugger_enabled = true;
}
paused.store(false, Ordering::SeqCst);

gameboy.step();
if gameboy.mmu.ppu.get_and_reset_frame_available() {
let _ = tx.send(gameboy.mmu.ppu.clone());
}

loop {
match rx_key.try_recv() {
Ok((true, key)) => gameboy.mmu.joypad.handle_key_down(key),
Ok((false, key)) => gameboy.mmu.joypad.handle_key_up(key),
_ => break,
}
}
}
});

window_loop(rx, tx_key);
Expand Down
14 changes: 13 additions & 1 deletion src/mmu/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use bootrom::BOOT_ROM;
use joypad::Joypad;
use ppu::PPU;

pub mod bootrom;
pub mod joypad;
pub mod ppu;
pub mod rom;

pub struct MMU {
boot_rom_enabled: bool,
pub rom: Vec<u8>,
pub ram: [u8; 0xFFFF],
pub joypad: Joypad,
pub ppu: PPU,
}

impl MMU {
Expand All @@ -18,6 +21,7 @@ impl MMU {
boot_rom_enabled: true,
joypad: Joypad::new(),
ram: [0x0; 0xFFFF],
ppu: PPU::new(),
rom,
}
}
Expand All @@ -33,9 +37,12 @@ impl MMU {
}
}
0x100..=0x7FFF => *self.rom.get(addr as usize).unwrap_or(&0),
0x8000..=0x9FFF => self.ppu.get_byte(addr),
0xC000..=0xDFFF => *self.ram.get((addr - 0x8000) as usize).unwrap_or(&0),
0xFF00 => self.joypad.read_byte(addr),
0xFF50 => self.boot_rom_enabled as u8,
0xFF0F => self.ppu.vblank_irq as u8,
0xFE00..=0xFF7F => self.ppu.get_byte(addr),

// HRam
0xFF80..=0xFFFE => *self.ram.get((addr - 0x8000) as usize).unwrap_or(&0),
Expand All @@ -45,9 +52,14 @@ impl MMU {

pub fn write_byte(&mut self, addr: u16, value: u8) {
match addr {
0x8000..=0xDFFF => self.ram[addr as usize - 0x8000] = value,
0x8000..=0x9FFF => self.ppu.set_byte(addr, value),
0xC000..=0xDFFF => self.ram[addr as usize - 0x8000] = value,
0xFF00 => self.joypad.write_byte(addr, value),
0xFF0F => {
self.ppu.vblank_irq = value & 0x1 > 0;
}
0xFF50 => self.boot_rom_enabled = value == 0,
0xFE00..=0xFF7F => self.ppu.set_byte(addr, value),

0xFF80..=0xFFFE => self.ram[(addr - 0x8000) as usize] = value,
_ => unreachable!(),
Expand Down
19 changes: 10 additions & 9 deletions src/ppu.rs → src/mmu/ppu.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::{
sync::mpsc::Sender,
thread,
time::{Duration, Instant},
};
Expand All @@ -11,7 +10,7 @@ pub type Tile = [[u8; 8]; 8];

#[derive(Debug, Clone)]
pub struct PPU {
tx: Sender<PPU>,
frame_available: bool,
frame_number: u32,
last_frame_time: Instant,
pub vblank_irq: bool,
Expand All @@ -36,8 +35,9 @@ pub struct PPU {
}

impl PPU {
pub fn new(tx: Sender<PPU>) -> PPU {
pub fn new() -> PPU {
PPU {
frame_available: false,
frame_number: 1,
last_frame_time: Instant::now(),
vblank_irq: false,
Expand All @@ -58,7 +58,6 @@ impl PPU {

vram: [0; VRAM_SIZE],
voam: [0; VOAM_SIZE],
tx,
}
}

Expand Down Expand Up @@ -94,16 +93,12 @@ impl PPU {
self.last_frame_time = Instant::now();

self.frame_number += 1;
self.flush();
self.frame_available = true;
}
}
}
}

pub fn flush(&self) {
self.tx.send(self.clone()).unwrap();
}

pub fn get_byte(&self, addr: u16) -> u8 {
match addr {
// VRAM
Expand Down Expand Up @@ -164,6 +159,12 @@ impl PPU {
}
}

pub fn get_and_reset_frame_available(&mut self) -> bool {
let result = self.frame_available;
self.frame_available = false;
return result;
}

pub fn get_tile(&self, tile_index: usize) -> Tile {
let start_address = 0x8000 + (tile_index * 16) as u16;
let mut ret = [[0u8; 8]; 8];
Expand Down
Loading

0 comments on commit 401ffc2

Please sign in to comment.