diff --git a/src/main.rs b/src/main.rs index 3c549ee..f37c352 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,8 +61,8 @@ fn main() -> Result<(), Error> { // Prepare window and drawing resources // debugger + scaled nes resolution + border - let window_width = 300 + 256 * 2 + 5; - let window_height = 40 + 240*2 + 5; + let window_width = 300 + 256 * 3 + 5; + let window_height = 40 + 240 * 3 + 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(); let mut event_settings = EventSettings::new(); @@ -78,33 +78,87 @@ fn main() -> Result<(), Error> { // fps counter let mut fps = FPSCounter::new(); - // main screen texture + // screen textures let mut texture_ctx = TextureContext { factory: window.factory.clone(), encoder: window.factory.create_command_buffer().into(), - }; - let mut texture: G2dTexture = Texture::from_image( + }; + + // main screen + let mut main_texture: G2dTexture = Texture::from_image( &mut texture_ctx, &nes.ppu.borrow().canvas_main, &TextureSettings::new() ).unwrap(); - + + // pattern table textures + let mut pattern_table_textures: Vec = (0..2).map({|i| + Texture::from_image( + &mut texture_ctx, + &nes.ppu.borrow().pattern_tables[i], + &TextureSettings::new() + ).unwrap() + }).collect(); + + // palette textures + let mut palette_textures: Vec = (0..8).map({|i| + Texture::from_image( + &mut texture_ctx, + &nes.ppu.borrow().palettes[i], + &TextureSettings::new() + ).unwrap() + }).collect(); // Main loop - let mut run = false; + let mut run = true; while let Some(event) = events.next(&mut window) { if let Some(_) = event.render_args() { // Run enough clocks to render the next frame // if run { nes.clock_frame(); } - if run { nes.clock_scanline(); } + if run { nes.clock_frame(); } - - texture.update(&mut texture_ctx, &nes.ppu.borrow().canvas_main).unwrap(); + { + let mut ppu = nes.ppu.borrow_mut(); + let ppu_bus = nes.ppu_bus.borrow(); + // main_texture.update(&mut texture_ctx, &ppu.canvas_main).unwrap(); + pattern_table_textures[0].update(&mut texture_ctx, &ppu.get_pattern_table(&*ppu_bus, 0, 1)).unwrap(); + pattern_table_textures[1].update(&mut texture_ctx, &ppu.get_pattern_table(&*ppu_bus, 1, 1)).unwrap(); + palette_textures[0].update(&mut texture_ctx, &ppu.get_palette(&*ppu_bus, 0)).unwrap(); + palette_textures[1].update(&mut texture_ctx, &ppu.get_palette(&*ppu_bus, 1)).unwrap(); + palette_textures[2].update(&mut texture_ctx, &ppu.get_palette(&*ppu_bus, 2)).unwrap(); + palette_textures[3].update(&mut texture_ctx, &ppu.get_palette(&*ppu_bus, 3)).unwrap(); + palette_textures[4].update(&mut texture_ctx, &ppu.get_palette(&*ppu_bus, 4)).unwrap(); + palette_textures[5].update(&mut texture_ctx, &ppu.get_palette(&*ppu_bus, 5)).unwrap(); + palette_textures[6].update(&mut texture_ctx, &ppu.get_palette(&*ppu_bus, 6)).unwrap(); + palette_textures[7].update(&mut texture_ctx, &ppu.get_palette(&*ppu_bus, 7)).unwrap(); + } window.draw_2d(&event, |c, g, d| { clear(BG_COLOR, g); texture_ctx.encoder.flush(d); - let transform = c.transform.trans(300.0, 40.0).scale(2.0, 2.0); - image(&texture, transform, g); + let transform = c.transform.trans(300.0, 40.0).scale(3.0, 3.0); + image(&main_texture, transform, g); + + let mut transform = c.transform.trans(10.0, 480.0).scale(7.0, 7.0); + image(&palette_textures[0], transform, g); + transform = transform.trans(5.0, 0.0); + image(&palette_textures[1], transform, g); + transform = transform.trans(5.0, 0.0); + image(&palette_textures[2], transform, g); + transform = transform.trans(5.0, 0.0); + image(&palette_textures[3], transform, g); + transform = transform.trans(5.0, 0.0); + image(&palette_textures[4], transform, g); + transform = transform.trans(5.0, 0.0); + image(&palette_textures[5], transform, g); + transform = transform.trans(5.0, 0.0); + image(&palette_textures[6], transform, g); + transform = transform.trans(5.0, 0.0); + image(&palette_textures[7], transform, g); + + let mut transform = c.transform.trans(10.0, 490.0).scale(1.0, 1.0); + image(&pattern_table_textures[0], transform, g); + transform = transform.trans(148.0, 0.0).scale(1.0, 1.0); + image(&pattern_table_textures[1], transform, g); // fps let fps = fps.tick(); diff --git a/src/nes.rs b/src/nes.rs index 16c8c4b..1096f82 100644 --- a/src/nes.rs +++ b/src/nes.rs @@ -25,7 +25,7 @@ pub struct NES { pub cpu: CPU, pub bus: Bus, pub ppu: Rc>, - pub ppu_bus: PPUBus, + pub ppu_bus: Rc>, pub clock_count: u64, } @@ -42,7 +42,7 @@ impl NES { cpu: CPU::new(), bus: Bus::new(ppu.clone(), ppu_bus.clone()), ppu: ppu.clone(), - ppu_bus: PPUBus::new(), + ppu_bus: ppu_bus.clone(), clock_count: 0, } } @@ -53,7 +53,7 @@ impl NES { // both buses need to be connected to the cartridge let cart = Rc::new(RefCell::new(cartridge)); self.bus.insert_cartridge(cart.clone()); - self.ppu_bus.insert_cartridge(cart.clone()) + self.ppu_bus.borrow_mut().insert_cartridge(cart.clone()) } // Initializes the NES CPU programm pointer @@ -74,7 +74,7 @@ impl NES { if self.clock_count % 3 == 0 { self.cpu.clock(&mut self.bus); } - self.ppu.borrow_mut().clock(&mut self.ppu_bus); + self.ppu.borrow_mut().clock(&mut *self.ppu_bus.borrow_mut()); if self.clock_count % 100000 == 0 { info!("clock {}", self.clock_count); } diff --git a/src/nes/ppu.rs b/src/nes/ppu.rs index 9b41ed2..ff4d6ba 100644 --- a/src/nes/ppu.rs +++ b/src/nes/ppu.rs @@ -99,7 +99,8 @@ pub struct PPU { pub cycle: u16, pub scanline: u16, pub canvas_main: Sprite, - pattern_table: [Sprite; 2], + pub pattern_tables: [Sprite; 2], + pub palettes: [Sprite; 8], pub frame_ready: bool, addr_latch_set: bool, data_buffer: Byte, @@ -112,7 +113,20 @@ impl PPU { cycle: 0, scanline: 0, canvas_main: ImageBuffer::from_pixel(256, 240, PALETTE[&0x00]), - pattern_table: [ImageBuffer::new(128, 128), ImageBuffer::new(128, 128)], + pattern_tables: [ + ImageBuffer::from_pixel(128, 128, PALETTE[&0x00]), + ImageBuffer::from_pixel(128, 128, PALETTE[&0x00]) + ], + palettes: [ + ImageBuffer::from_pixel(4, 1, PALETTE[&0x00]), + ImageBuffer::from_pixel(4, 1, PALETTE[&0x00]), + ImageBuffer::from_pixel(4, 1, PALETTE[&0x00]), + ImageBuffer::from_pixel(4, 1, PALETTE[&0x00]), + ImageBuffer::from_pixel(4, 1, PALETTE[&0x00]), + ImageBuffer::from_pixel(4, 1, PALETTE[&0x00]), + ImageBuffer::from_pixel(4, 1, PALETTE[&0x00]), + ImageBuffer::from_pixel(4, 1, PALETTE[&0x00]), + ], frame_ready: false, addr_latch_set: false, data_buffer: 0 @@ -207,7 +221,7 @@ impl PPU { // palette memory let data = self.data_buffer; self.data_buffer = mem.readb_ppu(addr); - + if addr > 0x3F00 { // everything above 0x3F00 is palette self.data_buffer } else { @@ -265,6 +279,30 @@ impl PPU { } } + // get a colored pixel using the NES color palette for given palette_id + // and pixel value + fn get_color_from_ram(&self, mem: &T, palette_id: u8, pixel: u8) -> Pixel { + // 0x3F00: Start of palette memory + // palette << 2: Palette size is 4 + // pixel: pixel index is 0,1,2 or 3 + // 0x3F (63): limits reading to PALETTE size + let palette_idx_addr = 0x3F00 + ((palette_id as Word) << 2) + pixel as Word; + // println!(palette_id_addr); + let palette_idx = mem.readb_ppu(palette_idx_addr) & 0x3F; + PALETTE[&palette_idx] + } + + // updates the palette from VRAM and returns a sprite with the 4 colors + pub fn get_palette(&mut self, mem: &T, palette_id: u8) -> &Sprite { + for i in 0..4 { + self.palettes[palette_id as usize].put_pixel( + i, 0, + self.get_color_from_ram(mem, palette_id, i as u8) + ); + } + &self.palettes[palette_id as usize] + } + // Get the correct color for the pixel from the given palette fn get_color(&self, pixel: Byte, _palette: Byte) -> Pixel { // TODO: not implemented yet. returns some black and white color @@ -278,7 +316,7 @@ impl PPU { // Get one of the two pattern tables of the PPU // This also initializes/updates the pattern table - fn get_pattern_table(&mut self, index: usize, _palette: Byte, mem: &T) -> &Sprite { + pub fn get_pattern_table(&mut self, mem: &T, index: usize, palette_id: Byte) -> &Sprite { // 16 x 16 tiles of 8x8px sprites per pattern table => 128x128px for x in 0..16 { // tile row for y in 0..16 { // tile column @@ -298,10 +336,10 @@ impl PPU { let mut tile_msb = mem.readb_ppu(tile_addr + 8); for col in 0..8 { let pixel = (tile_lsb & 0x01) + (tile_msb & 0x01); - self.pattern_table[index].put_pixel( + self.pattern_tables[index].put_pixel( (x * 8 + (7-col)) as u32, // x starts on the right, sprit is from left (y * 8 + row) as u32, - self.get_color(123, pixel)); + self.get_color_from_ram(mem, palette_id, pixel)); tile_lsb >>= 1; tile_msb >>= 1; } @@ -309,14 +347,14 @@ impl PPU { } } - &self.pattern_table[index] + &self.pattern_tables[index] } } #[cfg(test)] mod tests { use super::*; - use crate::nes::memory::PPUBus; + use crate::nes::ppubus::PPUBus; #[test] fn test_set_flags() { diff --git a/src/nes/ppubus.rs b/src/nes/ppubus.rs index 4206e94..4bd90a6 100644 --- a/src/nes/ppubus.rs +++ b/src/nes/ppubus.rs @@ -37,8 +37,13 @@ impl PPUMemory for PPUBus { fn readb_ppu(&self, addr: Addr) -> Byte { // Palette is never mapped to cartridge if PALETTE_ADDR_RANGE[0] <= addr && addr <= PALETTE_ADDR_RANGE[1] { - let rel_addr = addr - 0x3F00; - return self.palette_memory[(rel_addr % 0x0020) as usize] + let mut rel_addr = addr - 0x3F00; + rel_addr = rel_addr % 0x0020; + if rel_addr == 0x0010 { rel_addr = 0x0000 } + if rel_addr == 0x0014 { rel_addr = 0x0004 } + if rel_addr == 0x0018 { rel_addr = 0x0008 } + if rel_addr == 0x001C { rel_addr = 0x000C } + return self.palette_memory[(rel_addr) as usize] } // give the cartridge a chance to handle the rest @@ -71,7 +76,12 @@ impl PPUMemory for PPUBus { fn writeb_ppu(&mut self, addr: Addr, data: Byte) { // Palette is never mapped to cartridge if PALETTE_ADDR_RANGE[0] <= addr && addr <= PALETTE_ADDR_RANGE[1] { - let rel_addr = addr - 0x3F00; + let mut rel_addr = addr - 0x3F00; + rel_addr = rel_addr % 0x0020; + if rel_addr == 0x0010 { rel_addr = 0x0000 } + if rel_addr == 0x0014 { rel_addr = 0x0004 } + if rel_addr == 0x0018 { rel_addr = 0x0008 } + if rel_addr == 0x001C { rel_addr = 0x000C } self.palette_memory[(rel_addr % 0x0020) as usize] = data; } @@ -246,14 +256,29 @@ mod tests { // read/write something to palette memory for (idx, addr) in (0x3F00 .. 0x3F1F + 1).enumerate() { - assert_eq!(0, mem.readb_ppu(addr)); mem.writeb_ppu(addr, idx as Byte); + println!("Written {} to {:#08x}", idx, addr); assert_eq!(idx as Byte, mem.readb_ppu(addr)); } // assert palette are initialized correctly - for (idx, addr) in (0x3F00 .. 0x3F1F + 1).enumerate() { - assert_eq!(idx as Byte, mem.readb_ppu(addr)); + // mirroring makes this look a bit strange, but this is the expected + // result for writing numbers to 0x3F00 to 0x3F1F + let expected = [ + 16, + 1, 2, 3, 20, + 5, 6, 7, 24, + 9, 10, 11, 28, + 13, 14, 15, 16, + 17, 18, 19, 20, + 21, 22, 23, 24, + 25, 26, 27, 28, + 29, 30, 31, + ]; + + for (addr, expected) in (0x3F00 .. 0x3F1F + 1).zip(expected.iter()) { + assert_eq!(expected, &mem.readb_ppu(addr), + "{:#08x}", addr); } } @@ -280,47 +305,83 @@ mod tests { } } + #[test] + fn test_ppu_memory_palette_internal_mirroring() { + + let wired_mirrors = [ + (0x3F10, 0x3F00), + (0x3F14, 0x3F04), + (0x3F18, 0x3F08), + (0x3F1C, 0x3F0C), + ]; + for (idx, (addr, true_addr)) in wired_mirrors.iter().enumerate() { + let mut mem = PPUBus::new(); + mem.writeb_ppu(*true_addr as Addr, idx as Byte); + assert_eq!(idx as Byte, mem.readb_ppu(*true_addr as Addr)); + assert_eq!(idx as Byte, mem.readb_ppu(*addr as Addr)); + mem.writeb_ppu(*true_addr as Addr, idx as Byte + 1); + assert_eq!(idx as Byte + 1, mem.readb_ppu(*true_addr as Addr)); + assert_eq!(idx as Byte + 1, mem.readb_ppu(*addr as Addr)); + } + } + #[test] fn test_ppu_memory_palette_mirroring() { let mut mem = PPUBus::new(); - // read/write something to palette memory + // read/write something to palette memory for (idx, addr) in (0x3F00 .. 0x3F1F + 1).enumerate() { mem.writeb_ppu(addr, idx as Byte); + println!("Written {} to {:#08x}", idx, addr); assert_eq!(idx as Byte, mem.readb_ppu(addr)); } + // assert palette are initialized correctly + // mirroring makes this look a bit strange, but this is the expected + // result for writing numbers to 0x3F00 to 0x3F1F + let expected = [ + 16, + 1, 2, 3, 20, + 5, 6, 7, 24, + 9, 10, 11, 28, + 13, 14, 15, 16, + 17, 18, 19, 20, + 21, 22, 23, 24, + 25, 26, 27, 28, + 29, 30, 31, + ]; + // mirror memory should have the same data for (idx, addr) in (0x3F20 .. 0x3F3F + 1).enumerate() { - assert_eq!(idx as Byte, mem.readb_ppu(addr)); + assert_eq!(expected[idx], mem.readb_ppu(addr)); } for (idx, addr) in (0x3F40 .. 0x3F5F + 1).enumerate() { - assert_eq!(idx as Byte, mem.readb_ppu(addr)); + assert_eq!(expected[idx], mem.readb_ppu(addr)); } for (idx, addr) in (0x3F60 .. 0x3F7F + 1).enumerate() { - assert_eq!(idx as Byte, mem.readb_ppu(addr)); + assert_eq!(expected[idx], mem.readb_ppu(addr)); } for (idx, addr) in (0x3F80 .. 0x3F9F + 1).enumerate() { - assert_eq!(idx as Byte, mem.readb_ppu(addr)); + assert_eq!(expected[idx], mem.readb_ppu(addr)); } for (idx, addr) in (0x3FA0 .. 0x3FBF + 1).enumerate() { - assert_eq!(idx as Byte, mem.readb_ppu(addr)); + assert_eq!(expected[idx], mem.readb_ppu(addr)); } for (idx, addr) in (0x3FC0 .. 0x3FDF + 1).enumerate() { - assert_eq!(idx as Byte, mem.readb_ppu(addr)); + assert_eq!(expected[idx], mem.readb_ppu(addr)); } for (idx, addr) in (0x3FE0 .. 0x3FFF + 1).enumerate() { - assert_eq!(idx as Byte, mem.readb_ppu(addr)); + assert_eq!(expected[idx], mem.readb_ppu(addr)); } // write data to mirrored addr range for (idx, addr) in (0x3FC0 .. 0x3FDF + 1).enumerate() { - mem.writeb_ppu(addr, idx as Byte + 1); + mem.writeb_ppu(addr, expected[idx] + 2); } // start memory should have the same data for (idx, addr) in (0x3F00 .. 0x3F1F + 1).enumerate() { - assert_eq!(idx as Byte + 1, mem.readb_ppu(addr)); + assert_eq!(expected[idx] + 2, mem.readb_ppu(addr)); } } }