Migrated display to custom crate

Things still need to be ironed out, mainly in the display crate, but the system
is working
This commit is contained in:
Steins7 2024-03-02 23:09:32 +01:00
parent 7a13325eb0
commit 27585d6440
7 changed files with 1378 additions and 796 deletions

1698
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -9,8 +9,11 @@ edition = "2021"
log = "0.4.14" log = "0.4.14"
chrono = "0.4.19" chrono = "0.4.19"
fern = "0.6.0" fern = "0.6.0"
winit = "0.24.0"
mini_gl_fb = "0.9.0"
bitflags = "1.3.2" bitflags = "1.3.2"
rusttype = "0.9.2" rusttype = "0.9.2"
canvas = "0.1.0"
[patch.crates-io]
canvas = { git = "https://git.steins7.ovh/Steins7/canvas.git", branch = "dev" }
cgmath = { path = "cgmath" }

View File

@ -1,120 +1,241 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use std::{
rc::Rc,
cell::RefCell,
time::Instant,
fmt::Write,
};
mod cpu; mod cpu;
use cpu::Cpu; use cpu::Cpu;
mod bus; mod bus;
mod peripherals; mod peripherals;
mod utils;
fn main() -> Result<(), &'static str> {
use utils::{Pixel, PixelBuffer, FontRenderer, DisplayBuffer};
use winit::{
event_loop::EventLoop,
dpi::LogicalSize,
};
use mini_gl_fb::config;
use std::{
fmt::Write,
rc::Rc,
cell::RefCell,
time::Instant,
};
use peripherals::mapper::Nrom128; use peripherals::mapper::Nrom128;
// setup mod utils;
setup_logger().map_err(|_| "Failed to setup logger")?;
let mut event_loop = EventLoop::new(); use canvas::{
let config = config! { Application,
window_title: "Test".to_string(), Canvas,
window_size: LogicalSize::new(1280.0, 720.0), sprite::{Sprite, TextureSprite, TextSprite, Center},
invert_y: false, utils::{Size, Color, Position},
}; };
let mut fb = mini_gl_fb::get_fancy(config, &event_loop); use utils::DisplayBuffer;
let mut buffer = PixelBuffer::new(1280, 720);
let mut renderer = FontRenderer::new(20.0, [0u8, 0, 0]); struct EmulatorState {
pub screen: Rc<RefCell<DisplayBuffer>>,
pub screen_sprite: TextureSprite,
pub pattern_table: Rc<RefCell<DisplayBuffer>>,
pub pattern_sprite: TextureSprite,
pub fps_text: TextSprite,
pub debug_text: TextSprite,
pub cpu: Cpu<Nrom128>,
pub start: Instant,
}
struct NesEmulator {}
impl Application<EmulatorState> for NesEmulator {
fn init(canvas: &mut Canvas) -> Result<EmulatorState, &'static str> {
let start = Instant::now();
let screen_texture = canvas
.create_texture(Size {w: 256, h: 240}, Some(Color::RED.into()))
.unwrap();
let mut screen_sprite = canvas.create_texture_sprite(Size {w: 256 * 2, h: 240 * 2});
screen_sprite.set_texture(screen_texture.clone(), None, 0.5);
screen_sprite.set_center(Center::BotLeft);
screen_sprite.set_position(Position {x: 100, y: 200});
let screen = Rc::new(RefCell::new(DisplayBuffer::from_texture(screen_texture)));
let pattern_texture = canvas
.create_texture(Size {w: 256, h: 128}, Some(Color::BLUE.into()))
.unwrap();
let mut pattern_sprite = canvas.create_texture_sprite(Size {w: 256, h: 128});
pattern_sprite.set_texture(pattern_texture.clone(), None, 1.0);
pattern_sprite.set_center(Center::BotLeft);
pattern_sprite.set_position(Position {x: 100, y: 50});
let pattern_table = Rc::new(RefCell::new(DisplayBuffer::from_texture(pattern_texture)));
let mut fps_text = canvas.create_text_sprite("00", Size {w: 20, h: 20}, 20.0);
fps_text.set_center(Center::TopLeft);
fps_text.set_position(Position {x:0, y:720});
let mut debug_text = canvas.create_text_sprite("00", Size {w: 300, h: 600}, 20.0);
debug_text.set_center(Center::BotLeft);
debug_text.set_position(Position {x:700, y:50});
// first image
let mapper = Rc::new(RefCell::new(Nrom128::new(false))); let mapper = Rc::new(RefCell::new(Nrom128::new(false)));
let mut screen = Rc::new(RefCell::new(DisplayBuffer::new(256, 240))); let cpu = Cpu::new(mapper, screen.clone(), pattern_table.clone());
let pattern_table = Rc::new(RefCell::new(DisplayBuffer::new(256, 128)));
let mut cpu = Cpu::new(mapper, screen.clone(), pattern_table.clone());
let mut debug_str = String::new();
//write!(debug_str, "{:#?}", cpu).unwrap();
buffer.fill([0, 0], [1280, 720], Pixel::rgba(255, 255, 255, 255));
//renderer.draw(&mut buffer, &debug_str, [20, 20], [760, 560]);
let vec: Vec<[u8; 4]> = (&buffer).into(); canvas.clear();
fb.update_buffer(&vec); canvas.update();
let mut update = false;
let mut refresh = true;
let mut start = Instant::now();
// event loop Ok(EmulatorState {
fb.glutin_handle_basic_input(&mut event_loop, |fb, input| { screen,
use winit::event::VirtualKeyCode; screen_sprite,
pattern_table,
// wait for input before updating pattern_sprite,
fps_text,
// close if escape is pressed debug_text,
if input.key_pressed(VirtualKeyCode::Escape) { cpu,
return false start,
})
} }
// execute next cpu instruction (step mode) fn tick(state: &mut EmulatorState, canvas: &mut Canvas) -> Result<(), &'static str> {
if input.key_pressed(VirtualKeyCode::S) {
cpu.tick(); let frame_time = state.start.elapsed().as_secs_f32();
refresh = true; state.start = Instant::now();
canvas.clear();
state.cpu.tick();
if state.screen.borrow_mut().is_ready {
state.screen.borrow_mut().is_ready = false;
} }
if input.key_pressed(VirtualKeyCode::R) { canvas.draw(&mut state.screen_sprite);
update = true; canvas.draw(&mut state.pattern_sprite);
}
if input.key_pressed(VirtualKeyCode::C) { let mut fps_str = String::new();
update = false; write!(fps_str, "{} fps", 1.0/frame_time).unwrap();
refresh = true; state.fps_text.set_text(&fps_str);
} canvas.draw(&mut state.fps_text);
if update {
cpu.tick();
if screen.borrow_mut().is_ready {
screen.borrow_mut().is_ready = false;
refresh = true;
}
input.wait = false;
} else {
input.wait = true;
}
if refresh {
refresh = false;
let frame_time = start.elapsed().as_secs_f32();
start = Instant::now();
let mut debug_str = String::new(); let mut debug_str = String::new();
buffer.fill([0, 0], [1280, 720], Pixel::rgba(255, 255, 255, 255)); write!(debug_str, "{:#?}", state.cpu).unwrap();
write!(debug_str, "{} fps", 1.0/frame_time); state.debug_text.set_text(&debug_str);
renderer.draw(&mut buffer, &debug_str, [1, 1], [20, 20]); canvas.draw(&mut state.debug_text);
debug_str = String::new();
write!(debug_str, "{:#?}", cpu).unwrap();
renderer.draw(&mut buffer, &debug_str, [600, 20], [760, 560]);
buffer.embbed([20, 20], &screen.borrow().buffer, 2);
let vec: Vec<[u8; 4]> = (&buffer).into(); canvas.update();
fb.update_buffer(&vec);
}
true
});
Ok(()) Ok(())
} }
}
fn main() -> Result<(), &'static str> {
setup_logger()
.map_err(|_| "Failed to setup logger")?;
canvas::run_canvas("NES emulator", Size {w: 1280, h: 720}, NesEmulator {});
}
// use utils::{Pixel, PixelBuffer, FontRenderer, DisplayBuffer};
// use glutin::{
// dpi::LogicalSize,
// event_loop::EventLoop,
// };
// use mini_gl_fb::config;
// use std::{
// fmt::Write,
// rc::Rc,
// cell::RefCell,
// time::Instant,
// };
// use peripherals::mapper::Nrom128;
// use std::ops::Deref;
//
// // setup
// setup_logger().map_err(|_| "Failed to setup logger")?;
//
// let mut event_loop = EventLoop::new();
// let config = config! {
// window_title: "Test".to_string(),
// window_size: LogicalSize::new(1280.0, 720.0),
// invert_y: false,
// };
// let mut fb = mini_gl_fb::get_fancy(config, event_loop.deref());
// let mut buffer = PixelBuffer::new(1280, 720);
// let mut renderer = FontRenderer::new(20.0, [0u8, 0, 0]);
//
// // first image
// let mapper = Rc::new(RefCell::new(Nrom128::new(false)));
// let mut screen = Rc::new(RefCell::new(DisplayBuffer::new(256, 240)));
// let pattern_table = Rc::new(RefCell::new(DisplayBuffer::new(256, 128)));
// let mut cpu = Cpu::new(mapper, screen.clone(), pattern_table.clone());
// let mut debug_str = String::new();
// //write!(debug_str, "{:#?}", cpu).unwrap();
// buffer.fill([0, 0], [1280, 720], Pixel::rgba(255, 255, 255, 255));
// //renderer.draw(&mut buffer, &debug_str, [20, 20], [760, 560]);
//
// let vec: Vec<[u8; 4]> = (&buffer).into();
// fb.update_buffer(&vec);
// let mut update = false;
// let mut refresh = true;
// let mut start = Instant::now();
//
// // event loop
// fb.glutin_handle_basic_input(&mut event_loop, |fb, input| {
// use glutin::event::VirtualKeyCode;
//
// // wait for input before updating
//
// // close if escape is pressed
// if input.key_pressed(VirtualKeyCode::Escape) {
// return false
// }
//
// // execute next cpu instruction (step mode)
// if input.key_pressed(VirtualKeyCode::S) {
// cpu.tick();
// refresh = true;
// }
//
// if input.key_pressed(VirtualKeyCode::R) {
// update = true;
// }
//
// if input.key_pressed(VirtualKeyCode::C) {
// update = false;
// refresh = true;
// }
//
// if update {
// cpu.tick();
//
// if screen.borrow_mut().is_ready {
// screen.borrow_mut().is_ready = false;
// refresh = true;
// }
//
// input.wait = false;
// } else {
// input.wait = true;
// }
//
// if refresh {
// refresh = false;
//
// let frame_time = start.elapsed().as_secs_f32();
// start = Instant::now();
//
// let mut debug_str = String::new();
// buffer.fill([0, 0], [1280, 720], Pixel::rgba(255, 255, 255, 255));
// write!(debug_str, "{} fps", 1.0/frame_time);
// renderer.draw(&mut buffer, &debug_str, [1, 1], [20, 20]);
// debug_str = String::new();
// write!(debug_str, "{:#?}", cpu).unwrap();
// renderer.draw(&mut buffer, &debug_str, [600, 20], [760, 560]);
// buffer.embbed([20, 20], &screen.borrow().buffer, 2);
//
// let vec: Vec<[u8; 4]> = (&buffer).into();
// fb.update_buffer(&vec);
// }
//
// true
// });
//
// Ok(())
//}
fn setup_logger() -> Result<(), fern::InitError> { fn setup_logger() -> Result<(), fern::InitError> {
@ -129,7 +250,7 @@ fn setup_logger() -> Result<(), fern::InitError> {
message message
)) ))
}) })
.level(log::LevelFilter::Debug) .level(log::LevelFilter::Error)
.chain(std::io::stdout()) .chain(std::io::stdout())
.chain(fern::log_file("output.log")?) .chain(fern::log_file("output.log")?)
.apply()?; .apply()?;

View File

@ -122,7 +122,7 @@ impl<M: Mapper> Ppu<M> {
use crate::utils::Pixel; use crate::utils::Pixel;
self.screen.borrow_mut() self.screen.borrow_mut()
.buffer.put_pixel( .set_pixel(
self.dot.into(), self.dot.into(),
self.line.into(), self.line.into(),
Pixel::rgba(self.data.into(), 0, 0, 255)); Pixel::rgba(self.data.into(), 0, 0, 255));

View File

@ -1,19 +1,33 @@
use super::PixelBuffer; use super::Pixel;
use canvas::texture::TextureHandle;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
pub struct DisplayBuffer { pub struct DisplayBuffer {
pub buffer: PixelBuffer,
pub is_ready: bool, pub is_ready: bool,
texture: TextureHandle,
} }
impl DisplayBuffer { impl DisplayBuffer {
pub fn new(w: usize, h: usize) -> Self { pub fn from_texture(texture: TextureHandle) -> Self {
Self { Self {
buffer: PixelBuffer::new(w, h), texture,
is_ready: false, is_ready: false,
} }
} }
pub fn set_pixel(&mut self, x: usize, y: usize, pixel: Pixel) {
self.texture.set_pixel(
canvas::utils::Position {x: x.try_into().unwrap(), y: y.try_into().unwrap()},
canvas::utils::Pixel {
r: pixel.r,
g: pixel.g,
b: pixel.b,
a: pixel.a,
});
}
} }