ppu draw in lines + fps

This commit is contained in:
Daniel Bauer
2020-01-05 12:57:31 +01:00
parent 47043b42a2
commit b3395b0f32
7 changed files with 241 additions and 61 deletions

7
Cargo.lock generated
View File

@@ -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"

View File

@@ -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"

View File

@@ -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() {

View File

@@ -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;
} }
} }

View File

@@ -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,
} }

View File

@@ -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);
// } }
} }
} }

View File

@@ -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) {
if (self.cycle < self.canvas_main.width() as u16) &&
(self.scanline < self.canvas_main.height() as u16) {
// random noise // random noise
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let x = rng.gen_range(0, 256); let x = self.cycle;
let y = rng.gen_range(0, 240); 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!() },
_ => { }
}
}
} }