Started implemented PPU
Current solution used for display and input management is far from being powerfull enough, developpement will resume when a better solution is found
This commit is contained in:
parent
50ba57db74
commit
7a13325eb0
@ -1 +1 @@
|
|||||||
<mxfile host="Electron" modified="2022-05-21T12:58:33.623Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/17.4.2 Chrome/96.0.4664.174 Electron/16.2.2 Safari/537.36" etag="4GrUG8G62nWZSU5-rLoW" version="17.4.2" type="device"><diagram id="ump8jJu03wSbJk_0SnHp" name="Page-1">7VvbcqM4EP0aqmYeMgXiYvzoS7JTtfFuKs5sdh4Vo9hMMPIK2bHn61eAxE2YYGOMJ5OnWE1LQPfp090SUfTRcvsHgavFBDvIU4DqbBV9rACgaT2d/Qklu1jSA1YsmBPX4UqpYOr+RFyocunadVCQU6QYe9Rd5YUz7PtoRnMySAh+zas9Yy9/1xWcI0kwnUFPlj66Dl2I97L66YWvyJ0v+K1t0IsvLKFQ5m8SLKCDXzMi/VrRRwRjGv9abkfIC40n7BLPu9lzNXkwgnxaZ4L3aNkb76/5K/J3wx9TePsyxFfhhOjh6E68MXKYAfgQE7rAc+xD7zqVDgle+w4Kl1XZKNW5xXjFhBoT/kCU7rg34ZpiJlrQpcevoq1L/w2nfwEmH37nq4W/x9vsYCcGPiW77Kxw/D17MZ0XjcTEgEJCByEWmMDHPhKyG9fzEh2CXxIPM98MY7uExthrby4K8JrMUIWRBW4hmSNa5YwEFSycEF4i9hZsHkEepO4m/xyQ43qe6PGp7EXhLqOwwq5Pg8zKd6GAKfAQtTk8eXxaBRAVtI1+lTr7Ed9ejDLvkYoiXB6CUf0DoxeDUe0XAKmtnh+kuhHfcgO9NTcCdBxSitxb+MSyZA5t0HPnPvs9Y45DhAk2iFCXpaEBv7B0HScGNgrcn/ApWi9EBTcdW9wcKuY4QUW4ANrm3MFzJJ+cZqYsXioiUHY6X56B3TCsnMkFng6DRepJoYKfnwOGx7z7TuOwj8x3OaxiXj6pgL55dlLRJE4Z3X2TYBu8uksPRs7dG/qS8/YGs563iia49DWtfoGQLTKFb2LOMv/kgvfgSJWsMKi2QgRxHoKMumqaZb8H9trKKNjKkG2lgRJbJcKT2woY3bLaUaR2PKfNPBgE7qxAa9ppSQzULY26IDGzgEG7msU0vUq9pfrd6hKTWhaRadZ9C5Q5SGbS7qVm2togbVq/N6Mn+wMKFwMFuwu+sgpVl2ZWE1ZR39B7lfo9UKnfUvNnSSXKO2r+9uAkaf408/KbPbXUF53wznG0cxDr7C+L8sTTUaHUDfFYeWIQTcxe4inoG2b/DETSk4jEgRS+DyLR1XK/J0Si2ma+xwaXzitActdd6w26Uew6a3foaltdpzisy9jhig2ZLUYP97cKsDz2CMMnlhCtOY2sEEscd1MUBSvoCxlfYzKY/ikus8fLamTEJWvVW376MHj4Nm3xBuPBw6DF5Qfj8f2ByxfwybBIy4nEQ8+0GY2EWTGIE2Saf0bYwyStjp9ZfiqIasXJIYdoYpV87Ohy6PRLIqe9rS1NcsbvuV3Tal1i1qxLmm7gNIKCKVHo4/1gwiSfwMvnllMKsN5OKcnmZjYwrLYCw5as8c/ZrCG1kJohW8MqsUa/tfwqHwTkckTGFtZ/6/CDkmHI6Vecxln2USMmT65WpRUfsocOOT04Pmmd9IEgpcR9WlMUxfAFPdgKeoxnD36c2tn3FGX82fNv9W5Bvr4vSb9aWeVqtnZcYn7k31NnW+1c5yXNKi85376fThvsOU/ny1+pX3SRUkSOUy690+51GqnKETt4mvLL7eDVjt2mp0in2cLjQVx3C6+w5dfSFp78ucIs6DbL5D410uqBt0maqXXo1RVym24+Nyvk5Q3DETMScUMTnLerKevx9LKPVUCvtbZG/mbyjuA5gUsmvP970tgiB52Ai1640PzV/KSnvd5PPltkkIEzKmx0wxtj+wSN8QkMlnTG3XXL8iFKez0gdb2wL41rx4voSkHkGqAC04pgsbU/K/H/qXw0qnJgNdgpLuUBvTVUy1ti2cBXP+EVdcMCoRsW0IrHpTV3EFtjAeGcjL0mcLViMO2EJLW3WbLs5OEI87Bh+k9lcVWb/muefv0/</diagram></mxfile>
|
<mxfile host="Electron" modified="2022-05-26T11:39:44.986Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/17.4.2 Chrome/96.0.4664.174 Electron/16.2.2 Safari/537.36" etag="qDAsjD8odGcVAzsI0HnM" version="17.4.2" type="device" pages="2"><diagram id="ump8jJu03wSbJk_0SnHp" name="Page-1">7Vtbc5s6EP41zLQP6XA3fvQl6ZlpfE4mTk9OHxUj2zQYuUJO7P76I0AyCGGCbTBkmqdYyyLE7rff7kpEMUar7VcM1ssJcqGv6Kq7VYyxouuaqTr0TyTZJRKnpyWCBfZcppQKpt5vyIQqk248F4aCIkHIJ95aFM5QEMAZEWQAY/Qqqs2RLz51DRZQEkxnwJelj55Llkyq2f30wl/QWyzZox29l1xYAa7M3iRcAhe9ZkTGtWKMMEIk+bXajqAfGY/bJbnv5sDV/cIwDEiVG/xH23nx/168wmA3/DkFt89DdBXdEC+O7PgbQ5cagA0RJku0QAHwr1PpEKNN4MJoWpWOUp1bhNZUqFHhT0jIjnkTbAiioiVZ+ewq3Hrkv+j2L7rFhj/YbNHv8TY72PFBQPAue1c0/pG9mN4Xj/iNIQGYDCIsUEGAAshlN57v73Uwet57mPpmKJuXWTxEGzyDJTblMAV4AUmZ7RO9yN6ZBzDnfYVoBelbUAUMfUC8FxGQgOF6sddjt9IXBbuMwhp5AQkzM99FAqrAQtRh8GTxaedAlNM2+2Xq9EfyeD7KvEcqinF5DEaND4y2hVHtPYLUUS8PUsNMHvkC/A0zAnBdXIjcW/BEs6SANuB7i4D+nlFPQkwFLxATj6ahAbuw8lw3ATYMvd/gKZ4vQgUzHZ3cGirWuBAVpWEVPQluBb+xZMqeIuQrwensLgp207QFk3OAHQeL1JNcBc3nIQWo6L56HPaR+VpjFesdkorety5OKprEKaO77xJsw1dv5YPYuQddJwX5wWA2RKtonEtf0+pX57JlpvDdm7PIP0LwHh2pkhUG5VaIIc5CkLJcZUY82lZmzlambCtNL7DVXli7rXSzXVY7idRO57SZD8LQm+VoTTuLxPSqpVEnSMzKYdApZzHNKFNvqH6328SklkVkmnXfAqUAyUza7UimrQzS2uv38+jJ+YBCW1BwOsFXdq7q0qxywsrrm0avVL+nl+o31PzZUonS9ebPOVDrnND8aVb3mz210Bet8M5ptHMU6xwui0TiuUyh1BHisUVi4E3MQeLJ6ZtW/wJE0pOIxAUEdJhIeGTVQSSqY4k9tt51XtEld9013qCb+a6zcoeuNtV18sO6jB2u6JDaYvRwf6votk+XMHyiCdFekNgKicT1XvKicA0CLmNzTAbTb/wyXV5WIyMumKva9NOHwcP3aYMPGA8eBg1OPxiP74+cPodPCjxSTCQ+nJPzaCTKimGSINP8M0I+wml1PKf5KSeqWsYcGzyaGDuGHDr9gshpbmtLk5zxZ27X1FmXWBXrkto3cM6CgiVR6OP9YEIln/Tnzw2nFN1+O6XsNzezgWE3FRiOZI1/L2YNqYXUTNkadoE1+o3lV/kgQMgRGVvYvzbRByXDiNOvGI3T7KPGTL6/WpZWAkAXHXF6eHrSqnVBgBDsPW0IjIO6QwtbA5/y7NHLqZx96yjjm82/h3YRDsaWWN8XpF+tqHK1GjsusT7y75nZlu8wv7N0a8j5tuOdNsfq+Z32lfrF4CmF5zil6512r9VIVU7YwdOUru/gVY7d2k+R6tnCY0FcdQsvt+XX0Bae/LnCLGw3ywifGmnVwHtOmql06HUh5Na++XxeIS9vGI6okbAXLe+yXU1Rj2cUfayi9xpra+RvJu8wWmCwosL7fybHWaTc4NV74VzzV/GTnuZ6P/lskUIGzAi30Q1rjJ1jG+NmDLbvjNvrluVDlOZ6QOL5UV+a1I6d6Er12DW6qlt2DIut81lJ/k/lD25U91FU405xIQ8YjaFa3hLLBr76Ca2JFxUI7bCAlj8urbiD2BgLcOdk7DUB6zWFaSskqb3NkkUnDyeYhw7TfypLqtr0X/OM6/8B</diagram><diagram id="rIACK3iZetm4nWkpG2b9" name="Page-2">7Vtdb9owFP01PDLhhPDxSGjXVqMaK5P28VK5iSFunThzTIH++tnBIaQJlHYQV6slJPDJdXx9z73OsYGGPQyXFwzGwTX1EWlYLX/ZsM8algXarZ54k8hqjfS6YA3MGPaVUQ5M8BNSYEuhc+yjpGDIKSUcx0XQo1GEPF7AIGN0UTSbUlIcNYYzVAImHiRl9Af2eaBQ0OnnFy4RngVq6J7VXV8IYWasZpIE0KeLLcg+b9hDRilffwqXQ0Rk8LK4rPt93nF14xhDET+kQ3QJXfjEJqM/36+a3gTcXE0HTTWLR0jmasLKWb7KIoB8ERDVpIwHdEYjSM5z1GV0HvlIDtMSrdxmRGksQCDAe8T5SrEL55wKKOAhUVfXY8qBds5NQQmdMw/tmVCWI5DNEN9j52wYEKmLaIg4W4l+DBHI8WPRD6hyaLaxy8MsPqhIvyLqoBT1YTwvBT5Z4JDASEZ4SiOecSBj7AWY+CO4onM5x4RD7yFruQFl+EnYwyy64jLjKvZWR94NEzKkhDIBRDQdIO80kTdTwzCUiG7jjAbwDLqGy4LhCCY8c5ASAuME36Uuy46hYARHLuWchspIRQExjpb7iS8TlXWwVXWp5QV0VXuxVaxthQVbdQpa7RORa1WUlCuXHozk2jOQ2bmKUYluEQSessXoA3pGTwVjkOBZJJoETWU3GUUslq2BgrksPTeJoYej2Si1OWvnyI2KhISo6Dsl6dIUYN9HUVrSHHJ4t0m4mOKIp5FyXPESAR22PjkNRzg+FG2Qt8VLmjM+pJGYC8QpkUikxgLJ9DiM9d1lU04FRb1I7YOYz+yOTrxtiNdCvGNpJr5tiNdCfLdXH/FfuccnvY6HV63J7Zef4/hxcdvs/m/qySmrp8qJWzWpp8rBgfNRww5a/xj3tOuAMbjaMlCVnt95LIFcYzmgqLEs29nm8EV7u+8843ztQZ4Bm6kclBRX4cL1+uyx/Xuw/HbRXNxH8a9m5wPkROXEdyyR7zslHOe4KbEvglsp4c6TUlaYXdbLuyzbeesuK9ueHV1zVdW70VzH0Fw7TkY07LKqn4GGeC3Ea99lVYltQ/zpia9zl1XpXs8Qr4X4zRGptrW+fJR6A0Oj4N6g4JyuTgVXSa45Lj1CWb/mdOa9KDhzXKqH+DoVXKV7Ved2hvjTE6/9nNxs1vUQr1/BZed+W9SPzU8djiLhbFu7hAPmMOZUhb3rq573IuJAeW9mqK+Feu0yDpidmybqtQs5YPZumqivVcrt/qK5QP3AKLmjKLl2v04lV0mueZofoa5f8/sMDTqu0j3zLNdDfJ0qrtI98yTXQ3ydGq7SPXMKq4f4Uyo40cz/SLb+/Vz+dzz7/C8=</diagram></mxfile>
|
||||||
19
src/bus.rs
19
src/bus.rs
@ -4,11 +4,9 @@ use std::{
|
|||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::peripherals::{
|
use crate::{
|
||||||
Peripheral,
|
utils::DisplayBuffer,
|
||||||
Ram,
|
peripherals::{Peripheral, Ram, Mapper, Ppu},
|
||||||
Mapper,
|
|
||||||
Ppu,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Bus<M: Mapper> {
|
pub struct Bus<M: Mapper> {
|
||||||
@ -52,7 +50,10 @@ impl<M: Mapper> Peripheral for Bus<M> {
|
|||||||
|
|
||||||
impl<M: Mapper> Bus<M> {
|
impl<M: Mapper> Bus<M> {
|
||||||
|
|
||||||
pub fn new(mapper: Rc<RefCell<M>>) -> Self {
|
pub fn new(mapper: Rc<RefCell<M>>,
|
||||||
|
screen: Rc<RefCell<DisplayBuffer>>,
|
||||||
|
pattern_table: Rc<RefCell<DisplayBuffer>>)
|
||||||
|
-> Self {
|
||||||
|
|
||||||
let mut ram = Ram::<0x800>::new();
|
let mut ram = Ram::<0x800>::new();
|
||||||
|
|
||||||
@ -115,11 +116,15 @@ impl<M: Mapper> Bus<M> {
|
|||||||
|
|
||||||
Bus {
|
Bus {
|
||||||
ram,
|
ram,
|
||||||
ppu: Ppu::new(mapper.clone()),
|
ppu: Ppu::new(mapper.clone(), screen, pattern_table),
|
||||||
apu: Ram::<0x1F>::new(),
|
apu: Ram::<0x1F>::new(),
|
||||||
cartridge: mapper,
|
cartridge: mapper,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tick(&mut self) {
|
||||||
|
self.ppu.tick();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// special Bus only used for testing the emulator
|
/// special Bus only used for testing the emulator
|
||||||
|
|||||||
@ -11,6 +11,7 @@ use bitflags::bitflags;
|
|||||||
use crate::{
|
use crate::{
|
||||||
bus::Bus,
|
bus::Bus,
|
||||||
peripherals::{Peripheral, Mapper},
|
peripherals::{Peripheral, Mapper},
|
||||||
|
utils::DisplayBuffer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -170,10 +171,13 @@ impl<M: Mapper> fmt::Debug for Cpu<M> {
|
|||||||
|
|
||||||
impl<M: Mapper> Cpu<M> {
|
impl<M: Mapper> Cpu<M> {
|
||||||
|
|
||||||
pub fn new(mapper: Rc<RefCell<M>>) -> Self {
|
pub fn new(mapper: Rc<RefCell<M>>,
|
||||||
|
screen: Rc<RefCell<DisplayBuffer>>,
|
||||||
|
pattern_table: Rc<RefCell<DisplayBuffer>>)
|
||||||
|
-> Self {
|
||||||
|
|
||||||
// read initial PC value from reset vector
|
// read initial PC value from reset vector
|
||||||
let bus = Bus::new(mapper);
|
let bus = Bus::new(mapper, screen, pattern_table);
|
||||||
let pc_lo = bus.read_addr(0xFFFC) as u16;
|
let pc_lo = bus.read_addr(0xFFFC) as u16;
|
||||||
let pc_hi = bus.read_addr(0xFFFD) as u16;
|
let pc_hi = bus.read_addr(0xFFFD) as u16;
|
||||||
|
|
||||||
@ -191,6 +195,7 @@ impl<M: Mapper> Cpu<M> {
|
|||||||
pub fn tick(&mut self) {
|
pub fn tick(&mut self) {
|
||||||
|
|
||||||
self.execute();
|
self.execute();
|
||||||
|
self.bus.tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&mut self) {
|
fn execute(&mut self) {
|
||||||
|
|||||||
35
src/main.rs
35
src/main.rs
@ -9,7 +9,7 @@ mod peripherals;
|
|||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
fn main() -> Result<(), &'static str> {
|
fn main() -> Result<(), &'static str> {
|
||||||
use utils::{Pixel, PixelBuffer, FontRenderer};
|
use utils::{Pixel, PixelBuffer, FontRenderer, DisplayBuffer};
|
||||||
use winit::{
|
use winit::{
|
||||||
event_loop::EventLoop,
|
event_loop::EventLoop,
|
||||||
dpi::LogicalSize,
|
dpi::LogicalSize,
|
||||||
@ -19,6 +19,7 @@ fn main() -> Result<(), &'static str> {
|
|||||||
fmt::Write,
|
fmt::Write,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
|
time::Instant,
|
||||||
};
|
};
|
||||||
use peripherals::mapper::Nrom128;
|
use peripherals::mapper::Nrom128;
|
||||||
|
|
||||||
@ -28,26 +29,28 @@ fn main() -> Result<(), &'static str> {
|
|||||||
let mut event_loop = EventLoop::new();
|
let mut event_loop = EventLoop::new();
|
||||||
let config = config! {
|
let config = config! {
|
||||||
window_title: "Test".to_string(),
|
window_title: "Test".to_string(),
|
||||||
window_size: LogicalSize::new(800.0, 600.0),
|
window_size: LogicalSize::new(1280.0, 720.0),
|
||||||
invert_y: false,
|
invert_y: false,
|
||||||
};
|
};
|
||||||
let mut fb = mini_gl_fb::get_fancy(config, &event_loop);
|
let mut fb = mini_gl_fb::get_fancy(config, &event_loop);
|
||||||
let mut buffer = PixelBuffer::new(800, 600);
|
let mut buffer = PixelBuffer::new(1280, 720);
|
||||||
let mut renderer = FontRenderer::new(20.0, [0u8, 0, 0]);
|
let mut renderer = FontRenderer::new(20.0, [0u8, 0, 0]);
|
||||||
|
|
||||||
// first image
|
// first image
|
||||||
let mapper = Rc::new(RefCell::new(Nrom128::new(false)));
|
let mapper = Rc::new(RefCell::new(Nrom128::new(false)));
|
||||||
let mut cpu = Cpu::new(mapper);
|
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();
|
let mut debug_str = String::new();
|
||||||
write!(debug_str, "{:#?}", cpu).unwrap();
|
//write!(debug_str, "{:#?}", cpu).unwrap();
|
||||||
buffer.fill([20, 20], [760, 560], Pixel::rgba(255, 255, 255, 255));
|
buffer.fill([0, 0], [1280, 720], Pixel::rgba(255, 255, 255, 255));
|
||||||
renderer.draw(&mut buffer, &debug_str, [20, 20], [760, 560]);
|
//renderer.draw(&mut buffer, &debug_str, [20, 20], [760, 560]);
|
||||||
|
|
||||||
let vec: Vec<[u8; 4]> = (&buffer).into();
|
let vec: Vec<[u8; 4]> = (&buffer).into();
|
||||||
fb.update_buffer(&vec);
|
fb.update_buffer(&vec);
|
||||||
let mut update = false;
|
let mut update = false;
|
||||||
let mut refresh = true;
|
let mut refresh = true;
|
||||||
let mut frame_counter = 0;
|
let mut start = Instant::now();
|
||||||
|
|
||||||
// event loop
|
// event loop
|
||||||
fb.glutin_handle_basic_input(&mut event_loop, |fb, input| {
|
fb.glutin_handle_basic_input(&mut event_loop, |fb, input| {
|
||||||
@ -78,9 +81,8 @@ fn main() -> Result<(), &'static str> {
|
|||||||
if update {
|
if update {
|
||||||
cpu.tick();
|
cpu.tick();
|
||||||
|
|
||||||
frame_counter += 1;
|
if screen.borrow_mut().is_ready {
|
||||||
if frame_counter > 1000 {
|
screen.borrow_mut().is_ready = false;
|
||||||
frame_counter = 0;
|
|
||||||
refresh = true;
|
refresh = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,10 +94,17 @@ fn main() -> Result<(), &'static str> {
|
|||||||
if refresh {
|
if refresh {
|
||||||
refresh = false;
|
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, "{} fps", 1.0/frame_time);
|
||||||
|
renderer.draw(&mut buffer, &debug_str, [1, 1], [20, 20]);
|
||||||
|
debug_str = String::new();
|
||||||
write!(debug_str, "{:#?}", cpu).unwrap();
|
write!(debug_str, "{:#?}", cpu).unwrap();
|
||||||
buffer.fill([20, 20], [760, 560], Pixel::rgba(255, 255, 255, 255));
|
renderer.draw(&mut buffer, &debug_str, [600, 20], [760, 560]);
|
||||||
renderer.draw(&mut buffer, &debug_str, [20, 20], [760, 560]);
|
buffer.embbed([20, 20], &screen.borrow().buffer, 2);
|
||||||
|
|
||||||
let vec: Vec<[u8; 4]> = (&buffer).into();
|
let vec: Vec<[u8; 4]> = (&buffer).into();
|
||||||
fb.update_buffer(&vec);
|
fb.update_buffer(&vec);
|
||||||
|
|||||||
@ -20,7 +20,7 @@ impl Peripheral for Nrom128 {
|
|||||||
|
|
||||||
match addr {
|
match addr {
|
||||||
0x6000..=0x7FFF => self.prg_ram.read_addr(addr - 0x6000),
|
0x6000..=0x7FFF => self.prg_ram.read_addr(addr - 0x6000),
|
||||||
0x8000..=0xBFFF => self.prg_rom.read_addr((addr - 0x6000) % 0x4000),
|
0x8000..=0xFFFF => self.prg_rom.read_addr((addr - 0x8000) % 0x4000),
|
||||||
//mirrored every 16KiB
|
//mirrored every 16KiB
|
||||||
_ => panic!("{}: Invalid address", addr),
|
_ => panic!("{}: Invalid address", addr),
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@ impl Peripheral for Nrom128 {
|
|||||||
0x6000..=0x7FFF => self.prg_ram.write_addr(addr - 0x6000, data),
|
0x6000..=0x7FFF => self.prg_ram.write_addr(addr - 0x6000, data),
|
||||||
_ => (), //writes are ignored for rom or invald addresses
|
_ => (), //writes are ignored for rom or invald addresses
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mapper for Nrom128 {
|
impl Mapper for Nrom128 {
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
#[allow(unused_imports)]
|
||||||
|
use log::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
@ -7,11 +10,12 @@ use bitflags::bitflags;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
peripherals::{Peripheral, Ram, Mapper},
|
peripherals::{Peripheral, Ram, Mapper},
|
||||||
|
utils::DisplayBuffer,
|
||||||
};
|
};
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
bitflags! {
|
bitflags! {
|
||||||
struct Ctrl: u8 {
|
struct PpuCtrl: u8 {
|
||||||
const Nl = 0x1 << 0;
|
const Nl = 0x1 << 0;
|
||||||
const Nh = 0x1 << 1;
|
const Nh = 0x1 << 1;
|
||||||
const I = 0x1 << 2;
|
const I = 0x1 << 2;
|
||||||
@ -24,7 +28,7 @@ bitflags! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
struct Mask: u8 {
|
struct PpuMask: u8 {
|
||||||
const Gr = 0x1 << 0;
|
const Gr = 0x1 << 0;
|
||||||
const m = 0x1 << 1;
|
const m = 0x1 << 1;
|
||||||
const M = 0x1 << 2;
|
const M = 0x1 << 2;
|
||||||
@ -37,7 +41,7 @@ bitflags! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
struct Status: u8 {
|
struct PpuStatus: u8 {
|
||||||
const O = 0x1 << 5;
|
const O = 0x1 << 5;
|
||||||
const S = 0x1 << 6;
|
const S = 0x1 << 6;
|
||||||
const V = 0x1 << 7;
|
const V = 0x1 << 7;
|
||||||
@ -46,9 +50,9 @@ bitflags! {
|
|||||||
|
|
||||||
pub struct Ppu<M: Mapper> {
|
pub struct Ppu<M: Mapper> {
|
||||||
vbus: Rc<RefCell<M>>,
|
vbus: Rc<RefCell<M>>,
|
||||||
ctrl: Ctrl,
|
ppu_ctrl: PpuCtrl,
|
||||||
mask: Mask,
|
ppu_mask: PpuMask,
|
||||||
status: Status,
|
ppu_status: PpuStatus,
|
||||||
scroll: u8,
|
scroll: u8,
|
||||||
addr: u8,
|
addr: u8,
|
||||||
data: u8,
|
data: u8,
|
||||||
@ -57,6 +61,13 @@ pub struct Ppu<M: Mapper> {
|
|||||||
oam_dma: u8,
|
oam_dma: u8,
|
||||||
oam: Ram<0x100>,
|
oam: Ram<0x100>,
|
||||||
blanking: bool,
|
blanking: bool,
|
||||||
|
line: u16,
|
||||||
|
dot: u16,
|
||||||
|
nmi: bool,
|
||||||
|
|
||||||
|
// outputs
|
||||||
|
screen: Rc<RefCell<DisplayBuffer>>,
|
||||||
|
pattern_table: Rc<RefCell<DisplayBuffer>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: Mapper> Peripheral for Ppu<M> {
|
impl<M: Mapper> Peripheral for Ppu<M> {
|
||||||
@ -65,14 +76,98 @@ impl<M: Mapper> Peripheral for Ppu<M> {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_addr(&mut self, _addr: u16, _data: u8) {
|
fn write_addr(&mut self, addr: u16, data: u8) {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: Mapper> Ppu<M> {
|
impl<M: Mapper> Ppu<M> {
|
||||||
|
|
||||||
pub fn new(_mapper: Rc<RefCell<M>>) -> Self {
|
pub fn new(mapper: Rc<RefCell<M>>,
|
||||||
unimplemented!();
|
screen: Rc<RefCell<DisplayBuffer>>,
|
||||||
|
pattern_table: Rc<RefCell<DisplayBuffer>>)
|
||||||
|
-> Self {
|
||||||
|
|
||||||
|
Self {
|
||||||
|
vbus: mapper,
|
||||||
|
ppu_ctrl: PpuCtrl::from_bits(0).unwrap(),
|
||||||
|
ppu_mask: PpuMask::from_bits(0).unwrap(),
|
||||||
|
ppu_status: PpuStatus::from_bits(0).unwrap(),
|
||||||
|
scroll: 0,
|
||||||
|
addr: 0,
|
||||||
|
data: 0,
|
||||||
|
oam_addr: 0,
|
||||||
|
oam_data: 0,
|
||||||
|
oam_dma: 0,
|
||||||
|
oam: Ram::<0x100>::new(),
|
||||||
|
blanking: false,
|
||||||
|
line: 0,
|
||||||
|
dot: 0,
|
||||||
|
nmi: false,
|
||||||
|
|
||||||
|
screen,
|
||||||
|
pattern_table,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn poll_nmi(&mut self) -> bool {
|
||||||
|
let ret = self.nmi;
|
||||||
|
if self.nmi {
|
||||||
|
self.nmi = false;
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick(&mut self) {
|
||||||
|
use crate::utils::Pixel;
|
||||||
|
|
||||||
|
self.screen.borrow_mut()
|
||||||
|
.buffer.put_pixel(
|
||||||
|
self.dot.into(),
|
||||||
|
self.line.into(),
|
||||||
|
Pixel::rgba(self.data.into(), 0, 0, 255));
|
||||||
|
|
||||||
|
self.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute(&mut self) {
|
||||||
|
|
||||||
|
match self.line {
|
||||||
|
0..=239 => self.render_frame(),
|
||||||
|
241 => {
|
||||||
|
if self.dot == 1 {
|
||||||
|
self.ppu_status.set(PpuStatus::V, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
261 => {
|
||||||
|
if self.dot == 1 {
|
||||||
|
self.ppu_status.set(PpuStatus::V | PpuStatus::S | PpuStatus::O, false);
|
||||||
|
}
|
||||||
|
self.render_frame();
|
||||||
|
|
||||||
|
//last line, got back to first line
|
||||||
|
self.line = 0;
|
||||||
|
}
|
||||||
|
240..=260 => (), //vertical blank
|
||||||
|
_ => panic!("{}: Invalid line number", self.line),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dot = (self.dot + 1) % 256;
|
||||||
|
if self.dot == 0 {
|
||||||
|
self.line = (self.line + 1) % 240;
|
||||||
|
if self.line == 0 {
|
||||||
|
self.screen.borrow_mut().is_ready = true;
|
||||||
|
self.data = (self.data + 1) % 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_frame(&mut self) {
|
||||||
|
|
||||||
|
match self.dot {
|
||||||
|
0 => (),
|
||||||
|
33..=40 => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
src/utils.rs
38
src/utils.rs
@ -1,5 +1,11 @@
|
|||||||
|
#[allow(unused_imports)]
|
||||||
|
use log::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
use rusttype::{point, Font, Scale, VMetrics};
|
use rusttype::{point, Font, Scale, VMetrics};
|
||||||
|
|
||||||
|
mod display_buffer;
|
||||||
|
pub use display_buffer::DisplayBuffer;
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
/* Pixel struct */
|
/* Pixel struct */
|
||||||
|
|
||||||
@ -54,34 +60,60 @@ impl From<&Pixel> for [u8; 4] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<[u8; 4]> for Pixel {
|
||||||
|
fn from(array: [u8; 4]) -> Self {
|
||||||
|
Pixel::rgba(array[0], array[1], array[2], array[3])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
/* PixelBuffer struct */
|
/* PixelBuffer struct */
|
||||||
|
|
||||||
pub struct PixelBuffer {
|
pub struct PixelBuffer {
|
||||||
buffer: Vec<Pixel>,
|
buffer: Vec<Pixel>,
|
||||||
|
w: usize,
|
||||||
h: usize,
|
h: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PixelBuffer {
|
impl PixelBuffer {
|
||||||
|
|
||||||
pub fn new(h: usize, w: usize) -> Self {
|
pub fn new(w: usize, h: usize) -> Self {
|
||||||
PixelBuffer {
|
PixelBuffer {
|
||||||
buffer: vec![Pixel::rgba(255u8, 255u8, 255u8, 255); h * w],
|
buffer: vec![Pixel::rgba(255u8, 255u8, 255u8, 255); h * w],
|
||||||
|
w,
|
||||||
h,
|
h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_pixel(&mut self, x: usize, y: usize, pixel: Pixel) {
|
pub fn put_pixel(&mut self, x: usize, y: usize, pixel: Pixel) {
|
||||||
self.buffer[x + y*self.h] += pixel
|
self.buffer[x + y*self.w] += pixel
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_pixel(&self, x: usize, y: usize) -> Pixel {
|
||||||
|
self.buffer[x + y*self.w]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill(&mut self, origin: [usize; 2], size: [usize; 2], pixel: Pixel) {
|
pub fn fill(&mut self, origin: [usize; 2], size: [usize; 2], pixel: Pixel) {
|
||||||
for x in origin[0]..(origin[0] + size[0]) {
|
for x in origin[0]..(origin[0] + size[0]) {
|
||||||
for y in origin[1]..(origin[1] + size[1]) {
|
for y in origin[1]..(origin[1] + size[1]) {
|
||||||
self.buffer[x + y*self.h] += pixel;
|
self.buffer[x + y*self.w] = pixel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn embbed(&mut self, origin: [usize; 2], buffer: &Self, scale: usize) {
|
||||||
|
let [w, h] = buffer.get_dimensions();
|
||||||
|
|
||||||
|
for y in 0..(h*scale) {
|
||||||
|
for x in 0..(w*scale) {
|
||||||
|
self.put_pixel(x + origin[0], y + origin[1], buffer.get_pixel(x/scale, y/scale));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_dimensions(&self) -> [usize; 2] {
|
||||||
|
[self.w, self.h]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&PixelBuffer> for Vec<[u8; 4]> {
|
impl From<&PixelBuffer> for Vec<[u8; 4]> {
|
||||||
|
|||||||
19
src/utils/display_buffer.rs
Normal file
19
src/utils/display_buffer.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use super::PixelBuffer;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
pub struct DisplayBuffer {
|
||||||
|
pub buffer: PixelBuffer,
|
||||||
|
pub is_ready: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DisplayBuffer {
|
||||||
|
|
||||||
|
pub fn new(w: usize, h: usize) -> Self {
|
||||||
|
|
||||||
|
Self {
|
||||||
|
buffer: PixelBuffer::new(w, h),
|
||||||
|
is_ready: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user