Skip to content

Commit

Permalink
feat: hacky step sideways, to get tileset rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
nathanleiby committed Dec 11, 2024
1 parent 9920f25 commit f15ef0b
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 44 deletions.
118 changes: 74 additions & 44 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod ppu;
mod rom;
mod utility;

use ppu::Frame;
use rand::random;
use rom::Rom;
use sdl2::event::Event;
Expand All @@ -27,45 +28,74 @@ fn main() -> Result<(), Box<dyn Error>> {
let sdl_context = sdl2::init()?;
let video_subsystem = sdl_context.video()?;
let window = video_subsystem
.window("", 320, 320)
.window("Tile Viewer", (256.0 * 3.0) as u32, (240.0 * 3.0) as u32)
.position_centered()
.build()?;

let mut canvas = window.into_canvas().present_vsync().build()?;
let mut event_pump = sdl_context.event_pump()?;
canvas.set_scale(10., 10.)?;
canvas.set_scale(3., 3.)?;

let creator = canvas.texture_creator();
let mut texture = creator.create_texture_target(PixelFormatEnum::RGB24, 32, 32)?;
let mut texture = creator.create_texture_target(PixelFormatEnum::RGB24, 256, 240)?;

let mut screen_state = [0_u8; 32 * 3 * 32];
// let mut screen_state = [0_u8; 32 * 3 * 32];

// snake game, for basic testing
// let program = fs::read("roms/snake.nes").unwrap();
let program = fs::read("roms/nestest.nes").unwrap();
// TODO: get program name from args at CLI
let program = fs::read("roms/Alter_Ego.nes").unwrap();
// let program = fs::read("roms/pacman.nes").unwrap();
// let program = fs::read("roms/Ms_pacman.nes").unwrap();

let mut cpu = Cpu::new();
cpu.load_rom(Rom::new(&program));
cpu.reset();
cpu.run_with_callback(move |cpu| {
println!("{}", cpu.trace());

// read user input and write it to mem[0xFF]
handle_user_input(cpu, &mut event_pump);

// update mem[0xFE] with a new random number
cpu.mem_write(0xFE, random::<u8>());

// read mem mapped screen state
if read_screen_state(cpu, &mut screen_state) {
// redraw the screen
texture.update(None, &screen_state, 32 * 3).unwrap();
canvas.copy(&texture, None, None).unwrap();
canvas.present();
let rom = Rom::new(&program);

let mut frame = Frame::new();

let tiles_per_row = 24;
let tile_size = 8 + 1;
for bank in 0..=1 {
for tile_n in 0..256 {
let x = tile_n % tiles_per_row;
let y = tile_n / tiles_per_row + bank * 12;

frame.draw_tile(&rom.chr_rom, bank, tile_n, (x * tile_size, y * tile_size));
}
}

// if read_screen_state(cpu, &mut screen_state.data) {
// // redraw the screen
// texture.update(None, &screen_state.d, 32 * 3).unwrap();
// canvas.copy(&texture, None, None).unwrap();
// canvas.present();
// }
texture.update(None, &frame.data, 256 * 3).unwrap();
canvas.copy(&texture, None, None).unwrap();
canvas.present();

loop {
handle_user_input(&mut cpu, &mut event_pump);
}
// cpu.load_rom(rom);
// cpu.reset();
// cpu.run_with_callback(move |cpu| {
// println!("{}", cpu.trace());

// // read user input and write it to mem[0xFF]
// handle_user_input(cpu, &mut event_pump);

sleep(Duration::new(0, 70_000));
});
// // update mem[0xFE] with a new random number
// cpu.mem_write(0xFE, random::<u8>());

// // read mem mapped screen state
// if read_screen_state(cpu, &mut screen_state) {
// // redraw the screen
// texture.update(None, &screen_state, 32 * 3).unwrap();
// canvas.copy(&texture, None, None).unwrap();
// canvas.present();
// }

// sleep(Duration::new(0, 70_000));
// });

Ok(())
}
Expand Down Expand Up @@ -106,21 +136,21 @@ fn color(byte: u8) -> Color {
}
}

fn read_screen_state(cpu: &mut Cpu, screen: &mut [u8; 32 * 3 * 32]) -> bool {
let mut screen_idx = 0;
let mut updated = false;
for i in 0x0200..0x0600 {
let color_idx = cpu.mem_read(i as u16);
let (b1, b2, b3) = color(color_idx).rgb();
if screen[screen_idx] != b1 || screen[screen_idx + 1] != b2 || screen[screen_idx + 2] != b3
{
screen[screen_idx] = b1;
screen[screen_idx + 1] = b2;
screen[screen_idx + 2] = b3;
updated = true
}
screen_idx += 3;
}

updated
}
// fn read_screen_state(cpu: &mut Cpu, screen: &mut [u8; 32 * 3 * 32]) -> bool {
// let mut screen_idx = 0;
// let mut updated = false;
// for i in 0x0200..0x0600 {
// let color_idx = cpu.mem_read(i as u16);
// let (b1, b2, b3) = color(color_idx).rgb();
// if screen[screen_idx] != b1 || screen[screen_idx + 1] != b2 || screen[screen_idx + 2] != b3
// {
// screen[screen_idx] = b1;
// screen[screen_idx + 1] = b2;
// screen[screen_idx + 2] = b3;
// updated = true
// }
// screen_idx += 3;
// }

// updated
// }
116 changes: 116 additions & 0 deletions src/ppu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,122 @@ use crate::{
utility::{addr_from, split_addr},
};

pub static SYSTEM_PALLETE: [(u8, u8, u8); 64] = [
(0x80, 0x80, 0x80),
(0x00, 0x3D, 0xA6),
(0x00, 0x12, 0xB0),
(0x44, 0x00, 0x96),
(0xA1, 0x00, 0x5E),
(0xC7, 0x00, 0x28),
(0xBA, 0x06, 0x00),
(0x8C, 0x17, 0x00),
(0x5C, 0x2F, 0x00),
(0x10, 0x45, 0x00),
(0x05, 0x4A, 0x00),
(0x00, 0x47, 0x2E),
(0x00, 0x41, 0x66),
(0x00, 0x00, 0x00),
(0x05, 0x05, 0x05),
(0x05, 0x05, 0x05),
(0xC7, 0xC7, 0xC7),
(0x00, 0x77, 0xFF),
(0x21, 0x55, 0xFF),
(0x82, 0x37, 0xFA),
(0xEB, 0x2F, 0xB5),
(0xFF, 0x29, 0x50),
(0xFF, 0x22, 0x00),
(0xD6, 0x32, 0x00),
(0xC4, 0x62, 0x00),
(0x35, 0x80, 0x00),
(0x05, 0x8F, 0x00),
(0x00, 0x8A, 0x55),
(0x00, 0x99, 0xCC),
(0x21, 0x21, 0x21),
(0x09, 0x09, 0x09),
(0x09, 0x09, 0x09),
(0xFF, 0xFF, 0xFF),
(0x0F, 0xD7, 0xFF),
(0x69, 0xA2, 0xFF),
(0xD4, 0x80, 0xFF),
(0xFF, 0x45, 0xF3),
(0xFF, 0x61, 0x8B),
(0xFF, 0x88, 0x33),
(0xFF, 0x9C, 0x12),
(0xFA, 0xBC, 0x20),
(0x9F, 0xE3, 0x0E),
(0x2B, 0xF0, 0x35),
(0x0C, 0xF0, 0xA4),
(0x05, 0xFB, 0xFF),
(0x5E, 0x5E, 0x5E),
(0x0D, 0x0D, 0x0D),
(0x0D, 0x0D, 0x0D),
(0xFF, 0xFF, 0xFF),
(0xA6, 0xFC, 0xFF),
(0xB3, 0xEC, 0xFF),
(0xDA, 0xAB, 0xEB),
(0xFF, 0xA8, 0xF9),
(0xFF, 0xAB, 0xB3),
(0xFF, 0xD2, 0xB0),
(0xFF, 0xEF, 0xA6),
(0xFF, 0xF7, 0x9C),
(0xD7, 0xE8, 0x95),
(0xA6, 0xED, 0xAF),
(0xA2, 0xF2, 0xDA),
(0x99, 0xFF, 0xFC),
(0xDD, 0xDD, 0xDD),
(0x11, 0x11, 0x11),
(0x11, 0x11, 0x11),
];

// TODO: move UI rendering stuff that ties to SDL2 out of PPU
pub struct Frame {
pub data: Vec<u8>,
}

impl Frame {
const WIDTH: usize = 256;
const HEIGHT: usize = 240;

pub fn new() -> Self {
Self {
data: vec![0; Frame::WIDTH * Frame::HEIGHT * 3],
}
}

pub fn set_pixel(&mut self, x: usize, y: usize, rgb: (u8, u8, u8)) {
let base = y * 3 * Frame::WIDTH + x * 3;
if base + 2 < self.data.len() {
self.data[base] = rgb.0;
self.data[base + 1] = rgb.1;
self.data[base + 2] = rgb.2
}
}

pub fn draw_tile(&mut self, chr_rom: &[u8], bank: usize, tile_n: usize, pos: (usize, usize)) {
assert!(bank <= 1);

let tile_size_bytes = 16;
let bank_size_bytes: usize = 4096;

let (x, y) = pos;
for row in 0..8 {
let first_byte = chr_rom[bank * bank_size_bytes + tile_n * tile_size_bytes + row];
let second_byte = chr_rom[bank * bank_size_bytes + tile_n * tile_size_bytes + 8 + row];

for col in 0..8 {
let which_bit = 1 << (7 - col);
let lo_bit = first_byte & which_bit > 0;
let hi_bit = second_byte & which_bit > 0;
let palette_idx: u8 = (hi_bit as u8) << 1 + (lo_bit as u8);
// TODO: lookup palette color
let color = SYSTEM_PALLETE[palette_idx as usize];

self.set_pixel(x + col, y + row, color);
}
}
}
}

#[derive(Default)]
pub struct PpuRegisters {
/// Controller (0x2000) - instructs PPU on general logic flow
Expand Down
1 change: 1 addition & 0 deletions src/rom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ impl Rom {
let mapper = match rom_mapper_type {
0 => Mapper::Zero,
_ => todo!(),
// _ => Mapper::Zero, // TODO: Hack to get tiles viewing.. but should revert to TODO
};

let mirroring = match (is_four_screen, is_vertical_screen) {
Expand Down

0 comments on commit f15ef0b

Please sign in to comment.