ppu draw in lines + fps
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -5,6 +5,7 @@ name = "NESemu"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"fps_counter 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gfx_core 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gfx_core 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gfx_device_gl 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gfx_device_gl 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gfx_glyph 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gfx_glyph 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -324,6 +325,11 @@ name = "foreign-types-shared"
|
|||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fps_counter"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
@@ -1547,6 +1553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
|
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
|
||||||
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||||
"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
"checksum fps_counter 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e87181dcb1c9f3297845af26d5d78c4936ff12f8abe1c2a5507459d45f5236a6"
|
||||||
"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
|
"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
|
||||||
"checksum gfx 0.18.2 (registry+https://github.com/rust-lang/crates.io-index)" = "01de46f9508a5c259aef105f0bff760ceddca832ea9c87ce03d1923e22ee155b"
|
"checksum gfx 0.18.2 (registry+https://github.com/rust-lang/crates.io-index)" = "01de46f9508a5c259aef105f0bff760ceddca832ea9c87ce03d1923e22ee155b"
|
||||||
"checksum gfx_core 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "75fbddaef2e12b4995900539d7209d947b988a3d87ee8737484d049b526e5441"
|
"checksum gfx_core 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "75fbddaef2e12b4995900539d7209d947b988a3d87ee8737484d049b526e5441"
|
||||||
|
|||||||
@@ -20,3 +20,4 @@ gfx_device_gl = "0.16.*"
|
|||||||
gfx_glyph = "0.16.*"
|
gfx_glyph = "0.16.*"
|
||||||
image = "0.22.*"
|
image = "0.22.*"
|
||||||
rand = "0.7.2"
|
rand = "0.7.2"
|
||||||
|
fps_counter = "1.0.0"
|
||||||
64
src/main.rs
64
src/main.rs
@@ -6,6 +6,7 @@ extern crate simple_logger;
|
|||||||
extern crate phf;
|
extern crate phf;
|
||||||
extern crate piston_window;
|
extern crate piston_window;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
|
extern crate fps_counter;
|
||||||
|
|
||||||
mod nes;
|
mod nes;
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ use nes::disasm::*;
|
|||||||
use opengl_graphics::OpenGL;
|
use opengl_graphics::OpenGL;
|
||||||
use log::Level;
|
use log::Level;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
|
use fps_counter::FPSCounter;
|
||||||
use gfx_glyph::{Section, GlyphBrushBuilder,GlyphBrush, Scale};
|
use gfx_glyph::{Section, GlyphBrushBuilder,GlyphBrush, Scale};
|
||||||
use gfx_device_gl::{Resources,Factory};
|
use gfx_device_gl::{Resources,Factory};
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ lazy_static! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Error> {
|
fn main() -> Result<(), Error> {
|
||||||
simple_logger::init_with_level(Level::Info).unwrap();
|
simple_logger::init_with_level(Level::Warn).unwrap();
|
||||||
|
|
||||||
let mut nes = NES::new();
|
let mut nes = NES::new();
|
||||||
let cartridge = Path::new("test_roms/nestest.nes");
|
let cartridge = Path::new("test_roms/nestest.nes");
|
||||||
@@ -47,19 +48,25 @@ fn main() -> Result<(), Error> {
|
|||||||
let disasm = Disasm::disassemble(&nes.memory, 0xC000, 0xFFFF).unwrap();
|
let disasm = Disasm::disassemble(&nes.memory, 0xC000, 0xFFFF).unwrap();
|
||||||
|
|
||||||
// Prepare window and drawing resources
|
// Prepare window and drawing resources
|
||||||
let mut window: PistonWindow = WindowSettings::new("xXx NESemu xXx", [256*3, 240*2+50])
|
|
||||||
|
// debugger + scaled nes resolution + border
|
||||||
|
let window_width = 300 + 256 * 2 + 5;
|
||||||
|
let window_height = 40 + 240*2 + 5;
|
||||||
|
let mut window: PistonWindow = WindowSettings::new("xXx NESemu xXx", [window_width, window_height])
|
||||||
.exit_on_esc(true).graphics_api(OpenGL::V3_2).build().unwrap();
|
.exit_on_esc(true).graphics_api(OpenGL::V3_2).build().unwrap();
|
||||||
let mut event_settings = EventSettings::new();
|
let mut event_settings = EventSettings::new();
|
||||||
event_settings.max_fps = 60;
|
event_settings.max_fps = 60;
|
||||||
event_settings.ups = 107400;
|
|
||||||
let mut events = Events::new(event_settings);
|
let mut events = Events::new(event_settings);
|
||||||
|
|
||||||
// font for debug screens
|
// font for debug screens
|
||||||
let font: &[u8] = include_bytes!("../resources/fonts/PressStart2P.ttf");
|
let font: &[u8] = include_bytes!("../resources/fonts/PressStart2P.ttf");
|
||||||
let mut glyph_brush: GlyphBrush<Resources, Factory> = GlyphBrushBuilder::using_font_bytes(font)
|
let mut glyphs: GlyphBrush<Resources, Factory> = GlyphBrushBuilder::using_font_bytes(font)
|
||||||
.initial_cache_size((1024, 1024))
|
.initial_cache_size((1024, 1024))
|
||||||
.build(window.factory.clone());
|
.build(window.factory.clone());
|
||||||
|
|
||||||
|
// fps counter
|
||||||
|
let mut fps = FPSCounter::new();
|
||||||
|
|
||||||
// main screen texture
|
// main screen texture
|
||||||
let mut texture_ctx = TextureContext {
|
let mut texture_ctx = TextureContext {
|
||||||
factory: window.factory.clone(),
|
factory: window.factory.clone(),
|
||||||
@@ -67,7 +74,7 @@ fn main() -> Result<(), Error> {
|
|||||||
};
|
};
|
||||||
let mut texture: G2dTexture = Texture::from_image(
|
let mut texture: G2dTexture = Texture::from_image(
|
||||||
&mut texture_ctx,
|
&mut texture_ctx,
|
||||||
&nes.ppu.canvas_main,
|
&nes.ppu.borrow().canvas_main,
|
||||||
&TextureSettings::new()
|
&TextureSettings::new()
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
@@ -75,31 +82,37 @@ fn main() -> Result<(), Error> {
|
|||||||
// Main loop
|
// Main loop
|
||||||
let mut run = true;
|
let mut run = true;
|
||||||
while let Some(event) = events.next(&mut window) {
|
while let Some(event) = events.next(&mut window) {
|
||||||
if let Some(_) = event.update_args() {
|
|
||||||
// if cpu.regs.pc == 0xC6A2 { run = false }
|
|
||||||
if run {
|
|
||||||
nes.clock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(_) = event.render_args() {
|
if let Some(_) = event.render_args() {
|
||||||
texture.update(&mut texture_ctx, &nes.ppu.canvas_main).unwrap();
|
// Run enough clocks to render the next frame
|
||||||
|
if run { nes.clock_frame(); }
|
||||||
|
|
||||||
|
texture.update(&mut texture_ctx, &nes.ppu.borrow().canvas_main).unwrap();
|
||||||
window.draw_2d(&event, |c, g, d| {
|
window.draw_2d(&event, |c, g, d| {
|
||||||
clear(BG_COLOR, g);
|
clear(BG_COLOR, g);
|
||||||
texture_ctx.encoder.flush(d);
|
texture_ctx.encoder.flush(d);
|
||||||
let transform = c.transform.trans(256.0, 50.0).scale(2.0, 2.0);
|
let transform = c.transform.trans(300.0, 40.0).scale(2.0, 2.0);
|
||||||
image(&texture, transform, g);
|
image(&texture, transform, g);
|
||||||
|
|
||||||
|
// fps
|
||||||
|
let fps = fps.tick();
|
||||||
|
let position = (410.0, 10.0 + FT_SIZE_PX);
|
||||||
|
let color = if fps < 60 { FT_COLOR_RED } else { FT_COLOR_WHITE };
|
||||||
|
glyphs.queue(Section {
|
||||||
|
text: &format!("FPS: {}", fps),
|
||||||
|
scale: *FT_SCALE,
|
||||||
|
screen_position: position,
|
||||||
|
color: color,
|
||||||
|
..Section::default()
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
render_debug(&mut window, &event, &mut glyph_brush, &nes.cpu, &nes, &disasm);
|
render_debug(&mut window, &event, &mut glyphs, &nes, &disasm);
|
||||||
}
|
}
|
||||||
if let Some(Button::Keyboard(key)) = event.press_args() {
|
if let Some(Button::Keyboard(key)) = event.press_args() {
|
||||||
match key {
|
match key {
|
||||||
Key::C => nes.clock(), // advance one clock
|
Key::C => nes.clock(), // advance one clock
|
||||||
Key::S => { // advance to next instruction
|
Key::S => { nes.clock_instruction() }
|
||||||
nes.clock();
|
Key::F => { nes.clock_frame() }
|
||||||
while nes.cpu.is_ahead() {
|
|
||||||
nes.clock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Key::R => nes.reset(),
|
Key::R => nes.reset(),
|
||||||
Key::Space => run = !run,
|
Key::Space => run = !run,
|
||||||
Key::Up => {
|
Key::Up => {
|
||||||
@@ -121,14 +134,14 @@ fn main() -> Result<(), Error> {
|
|||||||
|
|
||||||
fn render_debug(window: &mut PistonWindow, event: &Event,
|
fn render_debug(window: &mut PistonWindow, event: &Event,
|
||||||
glyphs: &mut GlyphBrush<Resources, Factory>,
|
glyphs: &mut GlyphBrush<Resources, Factory>,
|
||||||
cpu: &CPU, _nes: &NES, disasm: &Disasm) {
|
nes: &NES, disasm: &Disasm) {
|
||||||
|
|
||||||
|
|
||||||
window.draw_2d(event, |_c, _g, _d| {
|
window.draw_2d(event, |_c, _g, _d| {
|
||||||
let debug_offset = [10.0, 10.0];
|
let debug_offset = [10.0, 10.0];
|
||||||
render_cpu(glyphs, cpu, debug_offset);
|
render_cpu(glyphs, &nes.cpu, debug_offset);
|
||||||
render_disasm(glyphs, disasm, cpu.regs.pc,
|
render_disasm(glyphs, disasm, nes.cpu.regs.pc,
|
||||||
[debug_offset[0], debug_offset[1] + (7.0 * (FT_LINE_DISTANCE+FT_SIZE_PX))]);
|
[debug_offset[0], debug_offset[1] + (8.0 * (FT_LINE_DISTANCE+FT_SIZE_PX))]);
|
||||||
// render_memory(glyphs, nes,
|
// render_memory(glyphs, nes,
|
||||||
// [debug_offset[0] + 400.0, debug_offset[1]]);
|
// [debug_offset[0] + 400.0, debug_offset[1]]);
|
||||||
});
|
});
|
||||||
@@ -236,6 +249,7 @@ fn render_cpu(glyphs: &mut GlyphBrush<Resources, Factory>, cpu: &CPU, offset: [f
|
|||||||
&format!("Y: {0:#x} ({0})", cpu.regs.y),
|
&format!("Y: {0:#x} ({0})", cpu.regs.y),
|
||||||
&format!("Stack P: {0:#x} ({0})", cpu.regs.sp),
|
&format!("Stack P: {0:#x} ({0})", cpu.regs.sp),
|
||||||
&format!("Program P: {:#x}", cpu.regs.pc),
|
&format!("Program P: {:#x}", cpu.regs.pc),
|
||||||
|
&format!("#CPU Cycles: {}", cpu.cycles),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (i, &text) in cpu_register_texts.iter().enumerate() {
|
for (i, &text) in cpu_register_texts.iter().enumerate() {
|
||||||
|
|||||||
37
src/nes.rs
37
src/nes.rs
@@ -1,3 +1,5 @@
|
|||||||
|
use core::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
pub use crate::nes::cartridge::Cartridge;
|
pub use crate::nes::cartridge::Cartridge;
|
||||||
pub use crate::nes::ppu::PPU;
|
pub use crate::nes::ppu::PPU;
|
||||||
pub use crate::nes::cpu::CPU;
|
pub use crate::nes::cpu::CPU;
|
||||||
@@ -17,17 +19,23 @@ pub mod ppu;
|
|||||||
// as the mediator between the different components and hold the RAM
|
// as the mediator between the different components and hold the RAM
|
||||||
pub struct NES {
|
pub struct NES {
|
||||||
pub cpu: CPU,
|
pub cpu: CPU,
|
||||||
pub ppu: PPU,
|
pub ppu: Rc<RefCell<PPU>>,
|
||||||
pub memory: NESMemory,
|
pub memory: NESMemory,
|
||||||
pub clock_count: u64,
|
pub clock_count: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NES {
|
impl NES {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
// The memory module needs access to some parts of the nes like the ppu
|
||||||
|
// to forward cpu reads to it. Because the NES also requires access
|
||||||
|
// to the PPU for clocking, we use shared ownership. Otherwise, all
|
||||||
|
// components except the CPU would have to live inside the memory
|
||||||
|
// instance, which is conceptually not what we are looking for.
|
||||||
|
let ppu = Rc::new(RefCell::new(PPU::new()));
|
||||||
NES {
|
NES {
|
||||||
cpu: CPU::new(),
|
cpu: CPU::new(),
|
||||||
ppu: PPU::new(),
|
ppu: ppu.clone(),
|
||||||
memory: NESMemory::new(),
|
memory: NESMemory::new(ppu.clone()),
|
||||||
clock_count: 0,
|
clock_count: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,7 +63,28 @@ impl NES {
|
|||||||
if self.clock_count % 3 == 0 {
|
if self.clock_count % 3 == 0 {
|
||||||
self.cpu.clock(&mut self.memory);
|
self.cpu.clock(&mut self.memory);
|
||||||
}
|
}
|
||||||
self.ppu.clock(&mut self.memory);
|
self.ppu.borrow_mut().clock(&mut self.memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// clock until the next cpu instruction is run
|
||||||
|
pub fn clock_instruction(&mut self) {
|
||||||
|
if !self.cpu.is_ahead() {
|
||||||
|
while !self.cpu.is_ahead() {
|
||||||
|
self.clock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while self.cpu.is_ahead() {
|
||||||
|
self.clock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clock until the next frame is ready
|
||||||
|
pub fn clock_frame(&mut self) {
|
||||||
|
while !self.ppu.borrow().frame_ready {
|
||||||
|
self.clock();
|
||||||
|
}
|
||||||
|
self.ppu.borrow_mut().frame_ready = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ impl Debug for Registers {
|
|||||||
pub struct CPU {
|
pub struct CPU {
|
||||||
pub regs: Registers,
|
pub regs: Registers,
|
||||||
curr_op: Byte, // current operation
|
curr_op: Byte, // current operation
|
||||||
cycles: u64, // number of clock clycles the CPU is ahead of global clock
|
pub cycles: u64, // number of clock clycles the CPU is ahead of global clock
|
||||||
cycles_ahead: u8,
|
cycles_ahead: u8,
|
||||||
stopped: bool,
|
stopped: bool,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,29 @@
|
|||||||
|
use crate::nes::ppu::PPU;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use core::cell::RefCell;
|
||||||
use crate::nes::cartridge::Cartridge;
|
use crate::nes::cartridge::Cartridge;
|
||||||
use crate::nes::types::*;
|
use crate::nes::types::*;
|
||||||
|
|
||||||
const RAM_SIZE: usize = 0x0800;
|
pub const RAM_SIZE: usize = 0x0800;
|
||||||
const RAM_ADDR_RANGE: [Addr; 2] = [0x0000, 0x1fff];
|
pub const RAM_ADDR_RANGE: [Addr; 2] = [0x0000, 0x1fff];
|
||||||
const RAM_PHYS_RANGE: [Addr; 2] = [0x0000, 0x07ff];
|
pub const RAM_PHYS_RANGE: [Addr; 2] = [0x0000, 0x07ff];
|
||||||
const PPU_ADDR_RANGE: [Addr; 2] = [0x2000, 0x3fff];
|
pub const PPU_ADDR_RANGE: [Addr; 2] = [0x2000, 0x3fff];
|
||||||
const PPU_PHYS_RANGE: [Addr; 2] = [0x2000, 0x2007];
|
pub const PPU_PHYS_RANGE: [Addr; 2] = [0x2000, 0x2007];
|
||||||
const CART_ADDR_RANGE: [Addr; 2] = [0x4020, 0xffff];
|
pub const CART_ADDR_RANGE: [Addr; 2] = [0x4020, 0xffff];
|
||||||
|
|
||||||
// NES memory: Contains data from RAM, cartridge...
|
// NES memory: Contains data from RAM, cartridge...
|
||||||
pub struct NESMemory {
|
pub struct NESMemory {
|
||||||
ram: [Byte; RAM_SIZE], // 2kb
|
ram: [Byte; RAM_SIZE], // 2kb
|
||||||
cartridge: Option<Cartridge>,
|
cartridge: Option<Cartridge>,
|
||||||
|
ppu: Rc<RefCell<PPU>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NESMemory {
|
impl NESMemory {
|
||||||
pub fn new() -> Self {
|
pub fn new(ppu: Rc<RefCell<PPU>>) -> Self {
|
||||||
NESMemory {
|
NESMemory {
|
||||||
ram: [0; RAM_SIZE],
|
ram: [0; RAM_SIZE],
|
||||||
cartridge: None,
|
cartridge: None,
|
||||||
|
ppu: ppu,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,15 +53,14 @@ impl Memory for NESMemory {
|
|||||||
return cartridge.readb(addr)
|
return cartridge.readb(addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if RAM_ADDR_RANGE[0] <= addr && addr <= RAM_ADDR_RANGE[1] {
|
if RAM_ADDR_RANGE[0] <= addr && addr <= RAM_ADDR_RANGE[1] {
|
||||||
// Ram is 3x mirrored after 0x07ff
|
// Ram is 3x mirrored after 0x07ff
|
||||||
return self.ram[(addr & RAM_PHYS_RANGE[1]) as usize]
|
return self.ram[(addr & RAM_PHYS_RANGE[1]) as usize]
|
||||||
}
|
}
|
||||||
// if PPU_ADDR_RANGE[0] <= addr && addr <= PPU_ADDR_RANGE[1] {
|
if PPU_ADDR_RANGE[0] <= addr && addr <= PPU_ADDR_RANGE[1] {
|
||||||
// // PPU memory is mirrored after 0x2007 to 0x3fff
|
let ppu = self.ppu.borrow();
|
||||||
// return self.ram[(addr & PPU_PHYS_RANGE[1]) as usize]
|
return ppu.readb(addr & PPU_PHYS_RANGE[1]);
|
||||||
// }
|
}
|
||||||
0x0000 // generic response
|
0x0000 // generic response
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,10 +74,10 @@ impl Memory for NESMemory {
|
|||||||
// Ram is 3x mirrored after 07ff
|
// Ram is 3x mirrored after 07ff
|
||||||
self.ram[(addr & RAM_PHYS_RANGE[1]) as usize] = data
|
self.ram[(addr & RAM_PHYS_RANGE[1]) as usize] = data
|
||||||
}
|
}
|
||||||
// if PPU_ADDR_RANGE[0] <= addr && addr <= PPU_ADDR_RANGE[1] {
|
if PPU_ADDR_RANGE[0] <= addr && addr <= PPU_ADDR_RANGE[1] {
|
||||||
// // PPU memory is mirrored after 0x2007 to 0x3fff
|
let mut ppu = self.ppu.borrow_mut();
|
||||||
// self.ram[(addr & PPU_PHYS_RANGE[1]) as usize] = data
|
ppu.writeb(addr & PPU_PHYS_RANGE[1], data);
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
153
src/nes/ppu.rs
153
src/nes/ppu.rs
@@ -1,11 +1,85 @@
|
|||||||
|
use phf::{Map,phf_map};
|
||||||
|
use crate::nes::types::*;
|
||||||
use crate::nes::memory::Memory;
|
use crate::nes::memory::Memory;
|
||||||
use image::{ImageBuffer, Rgba};
|
use image::{ImageBuffer, Rgba};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
|
static PALETTE: Map<u8, Rgba<u8>> = phf_map! {
|
||||||
|
// 0x00
|
||||||
|
0x00u8 => Rgba([84, 84, 84, 255]),
|
||||||
|
0x01u8 => Rgba([0, 30, 116, 255]),
|
||||||
|
0x02u8 => Rgba([8, 16, 144, 255]),
|
||||||
|
0x03u8 => Rgba([48, 0, 136, 255]),
|
||||||
|
0x04u8 => Rgba([68, 0, 100, 255]),
|
||||||
|
0x05u8 => Rgba([92, 0, 48, 255]),
|
||||||
|
0x06u8 => Rgba([84, 4, 0, 255]),
|
||||||
|
0x07u8 => Rgba([60, 24, 0, 255]),
|
||||||
|
0x08u8 => Rgba([32, 42, 0, 255]),
|
||||||
|
0x09u8 => Rgba([8, 58, 0, 255]),
|
||||||
|
0x0au8 => Rgba([0, 64, 0, 255]),
|
||||||
|
0x0bu8 => Rgba([0, 60, 0, 255]),
|
||||||
|
0x0cu8 => Rgba([0, 50, 60, 255]),
|
||||||
|
0x0du8 => Rgba([0, 0, 0, 255]),
|
||||||
|
0x0eu8 => Rgba([0, 0, 0, 255]),
|
||||||
|
0x0fu8 => Rgba([0, 0, 0, 255]),
|
||||||
|
// 0x10
|
||||||
|
0x10u8 => Rgba([152, 150, 152, 255]),
|
||||||
|
0x11u8 => Rgba([8, 76, 196, 255]),
|
||||||
|
0x12u8 => Rgba([48, 50, 236, 255]),
|
||||||
|
0x13u8 => Rgba([92, 30, 228, 255]),
|
||||||
|
0x14u8 => Rgba([136, 20, 176, 255]),
|
||||||
|
0x15u8 => Rgba([160, 20, 100, 255]),
|
||||||
|
0x16u8 => Rgba([152, 34, 32, 255]),
|
||||||
|
0x17u8 => Rgba([120, 60, 0, 255]),
|
||||||
|
0x18u8 => Rgba([84, 90, 0, 255]),
|
||||||
|
0x19u8 => Rgba([40, 114, 0, 255]),
|
||||||
|
0x1au8 => Rgba([8, 124, 0, 255]),
|
||||||
|
0x1bu8 => Rgba([0, 118, 40, 255]),
|
||||||
|
0x1cu8 => Rgba([0, 102, 120, 255]),
|
||||||
|
0x1du8 => Rgba([0, 0, 0, 255]),
|
||||||
|
0x1eu8 => Rgba([0, 0, 0, 255]),
|
||||||
|
0x1fu8 => Rgba([0, 0, 0, 255]),
|
||||||
|
// 0x20
|
||||||
|
0x20u8 => Rgba([236, 238, 236, 255]),
|
||||||
|
0x21u8 => Rgba([76, 154, 236, 255]),
|
||||||
|
0x22u8 => Rgba([120, 124, 236, 255]),
|
||||||
|
0x23u8 => Rgba([176, 98, 236, 255]),
|
||||||
|
0x24u8 => Rgba([228, 84, 236, 255]),
|
||||||
|
0x25u8 => Rgba([236, 88, 180, 255]),
|
||||||
|
0x26u8 => Rgba([236, 106, 100, 255]),
|
||||||
|
0x27u8 => Rgba([212, 136, 32, 255]),
|
||||||
|
0x28u8 => Rgba([160, 170, 0, 255]),
|
||||||
|
0x29u8 => Rgba([116, 196, 0, 255]),
|
||||||
|
0x2au8 => Rgba([76, 208, 32, 255]),
|
||||||
|
0x2bu8 => Rgba([56, 204, 108, 255]),
|
||||||
|
0x2cu8 => Rgba([56, 180, 204, 255]),
|
||||||
|
0x2du8 => Rgba([60, 60, 60, 255]),
|
||||||
|
0x2eu8 => Rgba([0, 0, 0, 255]),
|
||||||
|
0x2fu8 => Rgba([0, 0, 0, 255]),
|
||||||
|
// 0x30
|
||||||
|
0x30u8 => Rgba([236, 238, 236, 255]),
|
||||||
|
0x31u8 => Rgba([168, 204, 236, 255]),
|
||||||
|
0x32u8 => Rgba([188, 188, 236, 255]),
|
||||||
|
0x33u8 => Rgba([212, 178, 236, 255]),
|
||||||
|
0x34u8 => Rgba([236, 174, 236, 255]),
|
||||||
|
0x35u8 => Rgba([236, 174, 212, 255]),
|
||||||
|
0x36u8 => Rgba([236, 180, 176, 255]),
|
||||||
|
0x37u8 => Rgba([228, 196, 144, 255]),
|
||||||
|
0x38u8 => Rgba([204, 210, 120, 255]),
|
||||||
|
0x39u8 => Rgba([180, 222, 120, 255]),
|
||||||
|
0x3au8 => Rgba([168, 226, 144, 255]),
|
||||||
|
0x3bu8 => Rgba([152, 226, 180, 255]),
|
||||||
|
0x3cu8 => Rgba([160, 214, 228, 255]),
|
||||||
|
0x3du8 => Rgba([160, 162, 160, 255]),
|
||||||
|
0xe3u8 => Rgba([0, 0, 0, 255]),
|
||||||
|
0x3fu8 => Rgba([0, 0, 0, 255]),
|
||||||
|
};
|
||||||
|
|
||||||
pub struct PPU {
|
pub struct PPU {
|
||||||
pub cycle: i16,
|
pub cycle: u16,
|
||||||
pub scanline: i16,
|
pub scanline: u16,
|
||||||
pub canvas_main: ImageBuffer<Rgba<u8>, Vec<u8>>
|
pub canvas_main: ImageBuffer<Rgba<u8>, Vec<u8>>,
|
||||||
|
pub frame_ready: bool
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,22 +89,73 @@ impl PPU {
|
|||||||
cycle: 0,
|
cycle: 0,
|
||||||
scanline: 0,
|
scanline: 0,
|
||||||
canvas_main: ImageBuffer::new(256, 240),
|
canvas_main: ImageBuffer::new(256, 240),
|
||||||
|
frame_ready: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clock<T: Memory>(&mut self, _mem: &mut T) {
|
pub fn clock<T: Memory>(&mut self, _mem: &mut T) {
|
||||||
// random noise
|
if (self.cycle < self.canvas_main.width() as u16) &&
|
||||||
let mut rng = rand::thread_rng();
|
(self.scanline < self.canvas_main.height() as u16) {
|
||||||
let x = rng.gen_range(0, 256);
|
// random noise
|
||||||
let y = rng.gen_range(0, 240);
|
let mut rng = rand::thread_rng();
|
||||||
|
let x = self.cycle;
|
||||||
|
let y = self.scanline;
|
||||||
|
|
||||||
let color = rng.gen::<bool>();
|
let color = rng.gen::<bool>();
|
||||||
let px = if color {
|
let px = if color {
|
||||||
Rgba([255, 255, 255, 255])
|
PALETTE[&0x0f]
|
||||||
} else {
|
} else {
|
||||||
Rgba([0, 0, 0, 255])
|
PALETTE[&0x20]
|
||||||
};
|
};
|
||||||
|
|
||||||
self.canvas_main.put_pixel(x as u32, y as u32, px);
|
self.canvas_main.put_pixel(x as u32, y as u32, px);
|
||||||
|
}
|
||||||
|
|
||||||
|
// increment cycle/scanline
|
||||||
|
self.cycle += 1;
|
||||||
|
if self.cycle > 340 {
|
||||||
|
self.cycle = 0;
|
||||||
|
self.scanline += 1;
|
||||||
|
if self.scanline >= 261 {
|
||||||
|
self.scanline = 0;
|
||||||
|
self.frame_ready = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Memory for PPU {
|
||||||
|
fn readb(&self, addr: Addr) -> Byte {
|
||||||
|
// Only some of the PPU registers can actually by read
|
||||||
|
match addr {
|
||||||
|
// PPU status
|
||||||
|
0x2002 => { 0x00 },
|
||||||
|
// OAM data (object attribute memory = list of 64 sprites)
|
||||||
|
0x2004 => { unimplemented!() },
|
||||||
|
// ppu data
|
||||||
|
0x2007 => { unimplemented!() },
|
||||||
|
_ => 0x00
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
fn writeb(&mut self, addr: Addr, data: Byte) {
|
||||||
|
// Only some of the PPU registers can be written to
|
||||||
|
match addr {
|
||||||
|
// Control
|
||||||
|
0x2000 => { unimplemented!() },
|
||||||
|
// Mask
|
||||||
|
0x2001 => { unimplemented!() },
|
||||||
|
// OAM address
|
||||||
|
0x2003 => { unimplemented!() },
|
||||||
|
// OAM data
|
||||||
|
0x2004 => { unimplemented!() },
|
||||||
|
// Scroll
|
||||||
|
0x2005 => { unimplemented!() },
|
||||||
|
// Address
|
||||||
|
0x2006 => { unimplemented!() },
|
||||||
|
// ppu data
|
||||||
|
0x2007 => { unimplemented!() },
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user