mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-23 10:12:11 +00:00
Rendering a basic window with pixels
This commit is contained in:
parent
1444e8b63d
commit
886fc3cd4a
1719
Cargo.lock
generated
1719
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
@ -5,4 +5,14 @@ edition = "2021"
|
|||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
optimize = ["log/release_max_level_warn"]
|
||||||
|
default = ["optimize"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
rand = "0.8"
|
||||||
|
env_logger = "0.9"
|
||||||
|
log = "0.4"
|
||||||
|
pixels = "0.7"
|
||||||
|
winit = "0.25"
|
||||||
|
winit_input_helper = "0.10"
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use rmg_001::rom::ROM;
|
use rmg_001::render::start_eventloop;
|
||||||
use rmg_001::console::Console;
|
|
||||||
|
|
||||||
fn main() -> std::io::Result<()> {
|
fn main() -> std::io::Result<()> {
|
||||||
let mut console = Console::new();
|
start_eventloop();
|
||||||
console.cpu_run();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
23
src/cpu.rs
23
src/cpu.rs
@ -1,3 +1,4 @@
|
|||||||
|
use std::env;
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
BitIndex,
|
BitIndex,
|
||||||
get_bit,
|
get_bit,
|
||||||
@ -805,14 +806,17 @@ pub enum Opcode {
|
|||||||
PrefixCB(Box<Opcode>),
|
PrefixCB(Box<Opcode>),
|
||||||
IllegalInstruction,
|
IllegalInstruction,
|
||||||
}
|
}
|
||||||
|
// Frequency un Hz
|
||||||
|
const FREQUENCY: f64 = 4194.304;
|
||||||
|
|
||||||
// Store cycles in M
|
// Store cycles in M
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct Cycles(usize);
|
pub struct Cycles(pub usize);
|
||||||
|
|
||||||
pub struct CPU {
|
pub struct CPU {
|
||||||
registers: Registers,
|
registers: Registers,
|
||||||
cycles: Cycles,
|
cycles: Cycles,
|
||||||
|
last_op_cycles: Cycles,
|
||||||
exec_calls_count: usize,
|
exec_calls_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -821,6 +825,7 @@ impl CPU {
|
|||||||
Self {
|
Self {
|
||||||
registers: Registers::new(),
|
registers: Registers::new(),
|
||||||
cycles: Cycles(0),
|
cycles: Cycles(0),
|
||||||
|
last_op_cycles: Cycles(0),
|
||||||
exec_calls_count: 0,
|
exec_calls_count: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -846,6 +851,14 @@ impl CPU {
|
|||||||
self.cycles
|
self.cycles
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_last_op_cycles(&mut self, cycles_start: Cycles, cycles_end: Cycles) {
|
||||||
|
self.last_op_cycles = Cycles(cycles_end.0 - cycles_start.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_last_op_cycles(&self) -> Cycles {
|
||||||
|
self.last_op_cycles
|
||||||
|
}
|
||||||
|
|
||||||
fn log(&self, parameter_bytes: OpcodeParameterBytes) {
|
fn log(&self, parameter_bytes: OpcodeParameterBytes) {
|
||||||
println!("A: {:02X} F: {:02X} B: {:02X} C: {:02X} D: {:02X} E: {:02X} H: {:02X} L: {:02X} SP: {:04X} PC: 00:{:04X} ({:02X} {:02X} {:02X} {:02X})",
|
println!("A: {:02X} F: {:02X} B: {:02X} C: {:02X} D: {:02X} E: {:02X} H: {:02X} L: {:02X} SP: {:04X} PC: 00:{:04X} ({:02X} {:02X} {:02X} {:02X})",
|
||||||
self.registers.get(Register::A),
|
self.registers.get(Register::A),
|
||||||
@ -866,12 +879,18 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, bus: &mut Bus) {
|
pub fn run(&mut self, bus: &mut Bus) {
|
||||||
|
let cycles_start = self.get_cycles();
|
||||||
let program_counter = self.registers.get(Register::PC);
|
let program_counter = self.registers.get(Register::PC);
|
||||||
let parameter_bytes = OpcodeParameterBytes::from_address(program_counter, bus);
|
let parameter_bytes = OpcodeParameterBytes::from_address(program_counter, bus);
|
||||||
let (opcode, cycles) = parameter_bytes.parse_opcode();
|
let (opcode, cycles) = parameter_bytes.parse_opcode();
|
||||||
// self.log(parameter_bytes);
|
if !env::var("CPU_LOG").is_err() {
|
||||||
|
self.log(parameter_bytes);
|
||||||
|
}
|
||||||
self.increment_cycles(cycles);
|
self.increment_cycles(cycles);
|
||||||
self.exec(opcode, bus);
|
self.exec(opcode, bus);
|
||||||
|
let cycles_end = self.get_cycles();
|
||||||
|
|
||||||
|
self.set_last_op_cycles(cycles_start, cycles_end);
|
||||||
// self.increment_exec_calls_count();
|
// self.increment_exec_calls_count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
use std::{thread, time};
|
use std::{thread, time};
|
||||||
|
|
||||||
use crate::cpu::CPU;
|
use crate::cpu::{CPU, Cycles};
|
||||||
use crate::ppu::PPU;
|
use crate::ppu::PPU;
|
||||||
use crate::bus::Bus;
|
use crate::bus::Bus;
|
||||||
|
|
||||||
pub struct Console {
|
pub struct Emulator {
|
||||||
cpu: CPU,
|
cpu: CPU,
|
||||||
ppu: PPU,
|
ppu: PPU,
|
||||||
bus: Bus,
|
bus: Bus,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Console {
|
impl Emulator {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
cpu: CPU::new(),
|
cpu: CPU::new(),
|
||||||
@ -19,7 +19,23 @@ impl Console {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cpu_run(&mut self) {
|
pub fn draw(&mut self, frame: &mut [u8]) {
|
||||||
|
self.ppu.draw_background(&self.bus);
|
||||||
|
let ppu_frame = self.ppu.get_rgba_frame(&self.bus);
|
||||||
|
for (i, pixel) in frame.chunks_exact_mut(4).enumerate() {
|
||||||
|
let rgba = [0x5e, 0x48, 0xe8, 0xff];
|
||||||
|
pixel.copy_from_slice(&ppu_frame[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self, cpu_cycles: Cycles) {
|
||||||
|
self.cpu.reset_cycles();
|
||||||
|
while self.cpu.get_cycles().0 <= cpu_cycles.0 {
|
||||||
|
self.cpu.run(&mut self.bus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cpu_loop(&mut self) {
|
||||||
let mut exit = false;
|
let mut exit = false;
|
||||||
while !exit {
|
while !exit {
|
||||||
self.cpu.run(&mut self.bus);
|
self.cpu.run(&mut self.bus);
|
@ -3,4 +3,5 @@ pub mod cpu;
|
|||||||
pub mod ppu;
|
pub mod ppu;
|
||||||
pub mod rom;
|
pub mod rom;
|
||||||
pub mod bus;
|
pub mod bus;
|
||||||
pub mod console;
|
pub mod emulator;
|
||||||
|
pub mod render;
|
||||||
|
57
src/ppu.rs
57
src/ppu.rs
@ -3,7 +3,8 @@ use crate::utils::{
|
|||||||
get_bit,
|
get_bit,
|
||||||
set_bit,
|
set_bit,
|
||||||
};
|
};
|
||||||
use crate::bus::{Bus, BANK_ZERO};
|
use crate::bus::{Bus, BANK_ZERO, VIDEO_RAM};
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
struct ColorPalette(u8, u8, u8, u8);
|
struct ColorPalette(u8, u8, u8, u8);
|
||||||
|
|
||||||
@ -42,6 +43,10 @@ pub enum LCDStatus {
|
|||||||
ModeFlag(LCDStatusModeFlag),
|
ModeFlag(LCDStatusModeFlag),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const WIDTH: u32 = 160;
|
||||||
|
pub const HEIGHT: u32 = 144;
|
||||||
|
pub const FRAME_BUFFER_LENGTH: u32 = WIDTH * HEIGHT;
|
||||||
|
|
||||||
const LCD_CONTROL_ADDRESS: u16 = 0xFF40;
|
const LCD_CONTROL_ADDRESS: u16 = 0xFF40;
|
||||||
const LCD_STATUS_ADDRESS: u16 = 0xFF41;
|
const LCD_STATUS_ADDRESS: u16 = 0xFF41;
|
||||||
|
|
||||||
@ -82,11 +87,14 @@ impl Window {
|
|||||||
|
|
||||||
pub struct PPU {
|
pub struct PPU {
|
||||||
window: Window,
|
window: Window,
|
||||||
|
|
||||||
|
rgba_frame: [[u8; 4]; FRAME_BUFFER_LENGTH as usize],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PPU {
|
impl PPU {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
rgba_frame: [[0, 0, 0xFF, 0]; FRAME_BUFFER_LENGTH as usize],
|
||||||
window: Window::new(),
|
window: Window::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,4 +182,51 @@ impl PPU {
|
|||||||
};
|
};
|
||||||
bus.write(LCD_STATUS_ADDRESS, byte);
|
bus.write(LCD_STATUS_ADDRESS, byte);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_rgba_pixel(two_bit_pixel: u8) -> [u8; 4] {
|
||||||
|
match two_bit_pixel {
|
||||||
|
0x00 => [255, 255, 255, 255],
|
||||||
|
0x01 => [192, 192, 192, 0],
|
||||||
|
0x10 => [128, 128, 128, 0],
|
||||||
|
0x11 => [0, 0, 0, 0],
|
||||||
|
_ => [0, 0, 0, 0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_background(&mut self, bus: &Bus) {
|
||||||
|
let mut pointer = VIDEO_RAM.begin();
|
||||||
|
let mut pixels_drawn: usize = 0;
|
||||||
|
while pixels_drawn < FRAME_BUFFER_LENGTH as usize {
|
||||||
|
let byte1 = bus.read(pointer);
|
||||||
|
let byte2 = bus.read(pointer + 1);
|
||||||
|
let pixels = PPU::get_byte_pixels(byte1, byte2);
|
||||||
|
self.rgba_frame[pixels_drawn] = PPU::get_rgba_pixel(pixels[0]);
|
||||||
|
self.rgba_frame[pixels_drawn + 1] = PPU::get_rgba_pixel(pixels[1]);
|
||||||
|
self.rgba_frame[pixels_drawn + 2] = PPU::get_rgba_pixel(pixels[2]);
|
||||||
|
self.rgba_frame[pixels_drawn + 3] = PPU::get_rgba_pixel(pixels[3]);
|
||||||
|
self.rgba_frame[pixels_drawn + 4] = PPU::get_rgba_pixel(pixels[4]);
|
||||||
|
self.rgba_frame[pixels_drawn + 5] = PPU::get_rgba_pixel(pixels[5]);
|
||||||
|
self.rgba_frame[pixels_drawn + 6] = PPU::get_rgba_pixel(pixels[6]);
|
||||||
|
self.rgba_frame[pixels_drawn + 7] = PPU::get_rgba_pixel(pixels[7]);
|
||||||
|
pixels_drawn += 8;
|
||||||
|
pointer += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_byte_pixels(byte1: u8, byte2: u8) -> [u8; 8] {
|
||||||
|
let mut pixels: [u8; 8] = [0; 8];
|
||||||
|
pixels[0] = ((get_bit(byte1, BitIndex::I7) as u8) << 1) | (get_bit(byte2, BitIndex::I7) as u8);
|
||||||
|
pixels[1] = ((get_bit(byte1, BitIndex::I6) as u8) << 1) | (get_bit(byte2, BitIndex::I6) as u8);
|
||||||
|
pixels[2] = ((get_bit(byte1, BitIndex::I5) as u8) << 1) | (get_bit(byte2, BitIndex::I5) as u8);
|
||||||
|
pixels[3] = ((get_bit(byte1, BitIndex::I4) as u8) << 1) | (get_bit(byte2, BitIndex::I4) as u8);
|
||||||
|
pixels[4] = ((get_bit(byte1, BitIndex::I3) as u8) << 1) | (get_bit(byte2, BitIndex::I3) as u8);
|
||||||
|
pixels[5] = ((get_bit(byte1, BitIndex::I2) as u8) << 1) | (get_bit(byte2, BitIndex::I2) as u8);
|
||||||
|
pixels[6] = ((get_bit(byte1, BitIndex::I1) as u8) << 1) | (get_bit(byte2, BitIndex::I1) as u8);
|
||||||
|
pixels[7] = ((get_bit(byte1, BitIndex::I0) as u8) << 1) | (get_bit(byte2, BitIndex::I0) as u8);
|
||||||
|
pixels
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_rgba_frame(&self, bus: &Bus) -> &[[u8; 4]; FRAME_BUFFER_LENGTH as usize] {
|
||||||
|
&self.rgba_frame
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
81
src/render.rs
Normal file
81
src/render.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
use crate::emulator::Emulator;
|
||||||
|
use crate::cpu::{CPU, Cycles};
|
||||||
|
use crate::ppu::{WIDTH, HEIGHT};
|
||||||
|
|
||||||
|
use log::error;
|
||||||
|
use pixels::{Error, Pixels, SurfaceTexture};
|
||||||
|
use winit::dpi::LogicalSize;
|
||||||
|
use winit::event::{Event, VirtualKeyCode, WindowEvent};
|
||||||
|
use winit::event_loop::{ControlFlow, EventLoop};
|
||||||
|
use winit::window::{Window, WindowBuilder};
|
||||||
|
use winit_input_helper::WinitInputHelper;
|
||||||
|
|
||||||
|
pub fn create_pixels(width: u32, height: u32, window: &Window) -> Pixels {
|
||||||
|
let window_size = window.inner_size();
|
||||||
|
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, window);
|
||||||
|
Pixels::new(width, height, surface_texture).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_window<T>(width: u32, height: u32, title: String, event_loop: &EventLoop<T>) -> Window {
|
||||||
|
let size = LogicalSize::new(width as f64, height as f64);
|
||||||
|
WindowBuilder::new()
|
||||||
|
.with_title(title)
|
||||||
|
.with_inner_size(size)
|
||||||
|
.with_min_inner_size(size)
|
||||||
|
.build(event_loop)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_eventloop() {
|
||||||
|
env_logger::init();
|
||||||
|
let event_loop = EventLoop::new();
|
||||||
|
let mut input = WinitInputHelper::new();
|
||||||
|
|
||||||
|
let window = create_window(WIDTH, HEIGHT, "rmg-001".to_string(), &event_loop);
|
||||||
|
let mut pixels = create_pixels(WIDTH, HEIGHT, &window);
|
||||||
|
|
||||||
|
let mut emulator = Emulator::new();
|
||||||
|
|
||||||
|
let mut count: usize = 0;
|
||||||
|
event_loop.run(move |event, _, control_flow| {
|
||||||
|
// Handle input events
|
||||||
|
if input.update(&event) {
|
||||||
|
// Close events
|
||||||
|
if input.key_pressed(VirtualKeyCode::Escape) || input.quit() {
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize the window
|
||||||
|
if let Some(size) = input.window_resized() {
|
||||||
|
pixels.resize_surface(size.width, size.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
println!("The close button was pressed; stopping");
|
||||||
|
*control_flow = ControlFlow::Exit
|
||||||
|
},
|
||||||
|
Event::MainEventsCleared => {
|
||||||
|
emulator.run(Cycles(70224));
|
||||||
|
window.request_redraw();
|
||||||
|
},
|
||||||
|
Event::RedrawRequested(_) => {
|
||||||
|
emulator.draw(pixels.get_frame());
|
||||||
|
if pixels
|
||||||
|
.render()
|
||||||
|
.map_err(|e| error!("pixels.render() failed: {}", e))
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user