palette and pattern tables

This commit is contained in:
Daniel Bauer
2020-01-19 19:50:13 +01:00
parent 15e00db88e
commit acd014991d
4 changed files with 193 additions and 40 deletions

View File

@@ -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<G2dTexture> = (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<G2dTexture> = (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();

View File

@@ -25,7 +25,7 @@ pub struct NES {
pub cpu: CPU,
pub bus: Bus,
pub ppu: Rc<RefCell<PPU>>,
pub ppu_bus: PPUBus,
pub ppu_bus: Rc<RefCell<PPUBus>>,
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);
}

View File

@@ -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<T: PPUMemory>(&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<T: PPUMemory>(&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<T: PPUMemory>(&mut self, index: usize, _palette: Byte, mem: &T) -> &Sprite {
pub fn get_pattern_table<T: PPUMemory>(&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() {

View File

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