feat: integrate eui and pixels-rs for debug info

This commit is contained in:
2021-06-02 01:50:16 -05:00
parent 96cf705966
commit b1bf6c5868
7 changed files with 790 additions and 182 deletions

View File

@@ -11,6 +11,7 @@ pub struct Cpu {
reg: Registers,
flags: Flags,
ime: ImeState,
// TODO: Merge halted and state properties
halted: Option<HaltState>,
state: State,
}
@@ -89,7 +90,10 @@ impl Cpu {
pub fn step(&mut self) -> Cycle {
// if !self.bus.boot_enabled() {
// self.log_state().unwrap();
// let out = std::io::stdout();
// let handle = out.lock();
// self.log_state(handle).unwrap();
// }
let cycles = match self.halted() {
@@ -123,13 +127,13 @@ impl Cpu {
impl Cpu {
pub fn read_imm_byte(&mut self, addr: u16) -> u8 {
self.inc_pc();
self.inc_pc(); // NB: the addr read in the line below will be equal to PC - 1 after this function call
self.bus.read_byte(addr)
}
pub fn read_imm_word(&mut self, addr: u16) -> u16 {
self.inc_pc();
self.inc_pc();
self.inc_pc(); // NB: the addr read in the line below will be equal to PC - 2 after this function call
self.bus.read_word(addr)
}
@@ -335,27 +339,22 @@ impl Cpu {
}
impl Cpu {
fn log_state(&self) -> std::io::Result<()> {
use std::io::Write;
let out = std::io::stdout();
let mut handle = out.lock();
write!(handle, "A: {:02X} ", self.reg.a)?;
write!(handle, "F: {:02X} ", u8::from(self.flags))?;
write!(handle, "B: {:02X} ", self.reg.b)?;
write!(handle, "C: {:02X} ", self.reg.c)?;
write!(handle, "D: {:02X} ", self.reg.d)?;
write!(handle, "E: {:02X} ", self.reg.e)?;
write!(handle, "H: {:02X} ", self.reg.h)?;
write!(handle, "L: {:02X} ", self.reg.l)?;
write!(handle, "SP: {:04X} ", self.reg.sp)?;
write!(handle, "PC: 00:{:04X} ", self.reg.pc)?;
write!(handle, "({:02X} ", self.read_byte(self.reg.pc))?;
write!(handle, "{:02X} ", self.read_byte(self.reg.pc + 1))?;
write!(handle, "{:02X} ", self.read_byte(self.reg.pc + 2))?;
writeln!(handle, "{:02X})", self.read_byte(self.reg.pc + 3))?;
handle.flush()?;
pub fn log_state(&self, mut writer: impl std::io::Write) -> std::io::Result<()> {
write!(writer, "A: {:02X} ", self.reg.a)?;
write!(writer, "F: {:02X} ", u8::from(self.flags))?;
write!(writer, "B: {:02X} ", self.reg.b)?;
write!(writer, "C: {:02X} ", self.reg.c)?;
write!(writer, "D: {:02X} ", self.reg.d)?;
write!(writer, "E: {:02X} ", self.reg.e)?;
write!(writer, "H: {:02X} ", self.reg.h)?;
write!(writer, "L: {:02X} ", self.reg.l)?;
write!(writer, "SP: {:04X} ", self.reg.sp)?;
write!(writer, "PC: 00:{:04X} ", self.reg.pc)?;
write!(writer, "({:02X} ", self.read_byte(self.reg.pc))?;
write!(writer, "{:02X} ", self.read_byte(self.reg.pc + 1))?;
write!(writer, "{:02X} ", self.read_byte(self.reg.pc + 2))?;
writeln!(writer, "{:02X})", self.read_byte(self.reg.pc + 3))?;
writer.flush()?;
Ok(())
}

356
src/gui.rs Normal file
View File

@@ -0,0 +1,356 @@
use crate::cpu::Register;
use crate::cpu::RegisterPair;
use crate::LR35902;
use egui::{ClippedMesh, FontDefinitions};
use egui_wgpu_backend::{RenderPass, ScreenDescriptor};
use egui_winit_platform::{Platform, PlatformDescriptor};
use pixels::{wgpu, PixelsContext};
use std::time::Instant;
use wgpu::TextureFormat::Bgra8UnormSrgb;
// Boilerplate code from: https://github.com/parasyte/pixels/blob/0.3.0/examples/egui-winit/src/gui.rs
/// Manages all state required for rendering egui over `Pixels`.
pub struct Egui {
// State for egui.
start_time: Instant,
platform: Platform,
screen_desc: ScreenDescriptor,
render_pass: RenderPass,
paint_jobs: Vec<ClippedMesh>,
pub config: Configuration,
show_flags: bool,
show_cpu_info: bool,
show_registers: bool,
#[cfg(feature = "debug")]
show_disasm: bool,
#[cfg(feature = "debug")]
pub break_point: Option<u16>,
}
impl Egui {
/// Create egui.
pub fn new(width: u32, height: u32, scale_factor: f64, context: &PixelsContext) -> Self {
let platform = Platform::new(PlatformDescriptor {
physical_width: width,
physical_height: height,
scale_factor,
font_definitions: FontDefinitions::default(),
style: Default::default(),
});
let screen_desc = ScreenDescriptor {
physical_width: width,
physical_height: height,
scale_factor: scale_factor as f32,
};
let render_pass = RenderPass::new(&context.device, Bgra8UnormSrgb);
Self {
start_time: Instant::now(),
platform,
screen_desc,
render_pass,
paint_jobs: Vec::new(),
config: Default::default(),
show_flags: false,
show_cpu_info: false,
show_registers: false,
#[cfg(feature = "debug")]
show_disasm: false,
#[cfg(feature = "debug")]
break_point: None,
}
}
/// Handle input events from the window manager.
pub fn handle_event(&mut self, event: &winit::event::Event<'_, ()>) {
self.platform.handle_event(event);
}
/// Resize egui.
pub fn resize(&mut self, width: u32, height: u32) {
self.screen_desc.physical_width = width;
self.screen_desc.physical_height = height;
}
/// Update scaling factor.
pub fn scale_factor(&mut self, scale_factor: f64) {
self.screen_desc.scale_factor = scale_factor as f32;
}
/// Prepare egui.
pub fn prepare(&mut self, game_boy: &LR35902) {
self.platform
.update_time(self.start_time.elapsed().as_secs_f64());
// Begin the egui frame.
self.platform.begin_frame();
// Draw the demo application.
self.ui(&self.platform.context(), game_boy);
// End the egui frame and create all paint jobs to prepare for rendering.
let (_output, paint_commands) = self.platform.end_frame();
self.paint_jobs = self.platform.context().tessellate(paint_commands);
}
/// Create the UI using egui.
fn ui(&mut self, ctx: &egui::CtxRef, game_boy: &LR35902) {
egui::TopPanel::top("menubar_container").show(ctx, |ui| {
egui::menu::bar(ui, |ui| {
egui::menu::menu(ui, "File", |ui| {
if ui.button("Configuration").clicked() {
self.config.show = true;
}
});
egui::menu::menu(ui, "Status", |ui| {
if ui.button("Flags").clicked() {
self.show_flags = true;
}
if ui.button("CPU Information").clicked() {
self.show_cpu_info = true;
}
if ui.button("Registers").clicked() {
self.show_registers = true;
}
});
#[cfg(feature = "debug")]
if ui.button("Disassembly").clicked() {
self.show_disasm = true;
}
});
});
egui::Window::new("Cpu Flags")
.open(&mut self.show_flags)
.show(ctx, |ui| {
let flags = game_boy.flags();
ui.horizontal(|ui| {
let _ = ui.selectable_label(flags.z(), "Zero");
let _ = ui.selectable_label(flags.n(), "Negative");
let _ = ui.selectable_label(flags.h(), "Half-Carry");
let _ = ui.selectable_label(flags.c(), "Carry");
});
});
egui::Window::new("Registers")
.open(&mut self.show_registers)
.show(ctx, |ui| {
ui.horizontal(|ui| {
ui.label("A");
ui.monospace(format!("{:#04X}", game_boy.register(Register::A)));
ui.label("F");
let flag: u8 = game_boy.register(Register::Flag).into();
ui.monospace(format!("{:#04X}", flag));
});
ui.horizontal(|ui| {
ui.label("B");
ui.monospace(format!("{:#04X}", game_boy.register(Register::B)));
ui.label("C");
ui.monospace(format!("{:#04X}", game_boy.register(Register::C)));
});
ui.horizontal(|ui| {
ui.label("D");
ui.monospace(format!("{:#04X}", game_boy.register(Register::D)));
ui.label("E");
ui.monospace(format!("{:#04X}", game_boy.register(Register::E)));
});
ui.horizontal(|ui| {
ui.label("H");
ui.monospace(format!("{:#04X}", game_boy.register(Register::H)));
ui.label("L");
ui.monospace(format!("{:#04X}", game_boy.register(Register::L)));
});
ui.horizontal(|ui| {
ui.label("AF");
ui.monospace(format!("{:#06X}", game_boy.register_pair(RegisterPair::AF)));
});
ui.horizontal(|ui| {
ui.label("BC");
ui.monospace(format!("{:#06X}", game_boy.register_pair(RegisterPair::BC)));
});
ui.horizontal(|ui| {
ui.label("DE");
ui.monospace(format!("{:#06X}", game_boy.register_pair(RegisterPair::DE)));
});
ui.horizontal(|ui| {
ui.label("HL");
ui.monospace(format!("{:#06X}", game_boy.register_pair(RegisterPair::HL)));
});
});
egui::Window::new("Cpu Information")
.open(&mut self.show_cpu_info)
.show(ctx, |ui| {
ui.horizontal(|ui| {
ui.label("PC");
ui.monospace(format!("{:#06X}", game_boy.register_pair(RegisterPair::PC)));
});
ui.horizontal(|ui| {
ui.label("SP");
ui.monospace(format!("{:#06X}", game_boy.register_pair(RegisterPair::SP)));
});
ui.horizontal(|ui| {
ui.label("IME");
ui.label(format!("{:?}", game_boy.ime()));
});
ui.horizontal(|ui| {
ui.label("HALT");
ui.label(format!("{:?}", game_boy.halted()));
});
});
#[cfg(feature = "debug")]
{
let mut show_disasm = self.show_disasm;
egui::Window::new("Disassembler")
.open(&mut show_disasm)
.show(ctx, |ui| {
// FIXME: This can't be efficient at all
// To fix this, maybe we should make Instruction::from_byte not mutate the state of the Cpu (which we SHOULD have done to begin with)
let mut emu_clone = game_boy.clone();
for i in 0..10 {
let pc = emu_clone.register_pair(RegisterPair::PC);
let opcode = emu_clone.fetch();
emu_clone.inc_pc();
let instr = emu_clone.decode(opcode);
let res = ui.selectable_label(i == 0, format!("{:#06X}: {:?}", pc, instr));
if res.clicked() {
self.break_point = Some(pc);
}
}
ui.horizontal(|ui| {
if ui.button("Reset Breakpoint").clicked() {
self.break_point = None;
}
ui.selectable_label(
self.break_point.is_some(),
format!("{:04X?}", self.break_point),
)
});
});
self.show_disasm = show_disasm;
}
egui::Window::new("IRQ Information").show(ctx, |ui| {
let req = game_boy.read_byte(0xFF0F);
let enabled = game_boy.read_byte(0xFFFF);
ui.heading("Interrupt Requests");
ui.horizontal(|ui| {
let _ = ui.selectable_label(req & 0x01 == 0x01, "VBLANK");
let _ = ui.selectable_label((req >> 1) & 0x01 == 0x01, "LCD STAT");
let _ = ui.selectable_label((req >> 2) & 0x01 == 0x01, "TIMER");
let _ = ui.selectable_label((req >> 3) & 0x01 == 0x01, "SERIAL");
let _ = ui.selectable_label((req >> 4) & 0x01 == 0x01, "JOYPAD");
});
ui.heading("Interrupt Enable");
ui.horizontal(|ui| {
let _ = ui.selectable_label(enabled & 0x01 == 0x01, "VBLANK");
let _ = ui.selectable_label((enabled >> 1) & 0x01 == 0x01, "LCD STAT");
let _ = ui.selectable_label((enabled >> 2) & 0x01 == 0x01, "TIMER");
let _ = ui.selectable_label((enabled >> 3) & 0x01 == 0x01, "SERIAL");
let _ = ui.selectable_label((enabled >> 4) & 0x01 == 0x01, "JOYPAD");
});
});
#[cfg(feature = "debug")]
let mut spacebar_step = self.config.spacebar_step;
egui::Window::new("Configuration")
.open(&mut self.config.show)
.show(ctx, |ui| {
#[cfg(feature = "debug")]
ui.horizontal(|ui| {
ui.label("Spacebar Steps");
ui.add(egui::Slider::u16(&mut spacebar_step, 0..=std::u16::MAX));
});
});
#[cfg(feature = "debug")]
{
self.config.spacebar_step = spacebar_step;
}
}
/// Render egui.
pub fn render(
&mut self,
encoder: &mut wgpu::CommandEncoder,
render_target: &wgpu::TextureView,
context: &PixelsContext,
) {
// Upload all resources to the GPU.
self.render_pass.update_texture(
&context.device,
&context.queue,
&self.platform.context().texture(),
);
self.render_pass
.update_user_textures(&context.device, &context.queue);
self.render_pass.update_buffers(
&context.device,
&context.queue,
&self.paint_jobs,
&self.screen_desc,
);
// Record all render passes.
self.render_pass.execute(
encoder,
render_target,
&self.paint_jobs,
&self.screen_desc,
None,
);
}
}
pub struct Configuration {
/// Show Configuration egui menu
show: bool,
/// How many [`LR35902`] .step() do we want to do at once
/// when pressing the spacebar key?
#[cfg(feature = "debug")]
pub spacebar_step: u16,
}
impl Default for Configuration {
fn default() -> Self {
Self {
show: false,
#[cfg(feature = "debug")]
spacebar_step: 1,
}
}
}

View File

@@ -1,7 +1,7 @@
use super::cpu::{Cpu, Flags, HaltState, ImeState, Register, RegisterPair};
use std::{convert::TryFrom, fmt::Debug};
#[derive(Debug, Copy, Clone)]
#[derive(Copy, Clone)]
#[allow(clippy::upper_case_acronyms)]
pub enum Instruction {
NOP,
@@ -93,7 +93,7 @@ pub enum InstrRegisterPair {
DecrementHL,
}
#[derive(Debug, Copy, Clone)]
#[derive(Copy, Clone)]
pub enum InstrRegister {
A,
B,
@@ -105,7 +105,7 @@ pub enum InstrRegister {
IndirectHL, // (HL)
}
#[derive(Debug, Copy, Clone)]
#[derive(Copy, Clone)]
pub enum JumpCondition {
NotZero,
Zero,
@@ -2017,15 +2017,15 @@ impl std::fmt::Debug for JPTarget {
impl std::fmt::Debug for LDTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
LDTarget::IndirectC => f.write_str("IndirectC"),
LDTarget::IndirectC => f.write_str("(0xFF00 + C)"),
LDTarget::Register(reg) => write!(f, "{:?}", reg),
LDTarget::IndirectRegister(pair) => write!(f, "[{:?}]", pair),
LDTarget::ByteAtAddress(addr) => write!(f, "[{:#06X}]", addr),
LDTarget::IndirectRegister(pair) => write!(f, "({:?})", pair),
LDTarget::ByteAtAddress(addr) => write!(f, "({:#06X})", addr),
LDTarget::ImmediateWord(word) => write!(f, "{:#06X}", word),
LDTarget::ImmediateByte(byte) => write!(f, "{:#04X}", byte),
LDTarget::RegisterPair(pair) => write!(f, "{:?}", pair),
LDTarget::ByteAtAddressWithOffset(byte) => {
write!(f, "[0xFF00 + {:#04X}]", byte)
write!(f, "(0xFF00 + {:#04X})", byte)
}
}
}
@@ -2065,6 +2065,90 @@ impl std::fmt::Debug for Registers {
}
}
impl std::fmt::Debug for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Instruction::*;
match *self {
NOP => f.write_str("NOP"),
LD(left, right) => write!(f, "LD {:?}, {:?}", left, right),
STOP => f.write_str("STOP"),
JR(cond, dist) => write!(f, "JR {:?}, {:?}", cond, dist),
ADD(left, right) => write!(f, "ADD {:?}, {:?}", left, right),
INC(register) => write!(f, "INC {:?}", register),
DEC(register) => write!(f, "DEC {:?}", register),
RLCA => f.write_str("RLCA"),
RRCA => f.write_str("RRCA"),
RLA => f.write_str("RLA"),
RRA => f.write_str("RRA"),
DAA => f.write_str("DAA"),
CPL => f.write_str("CPL"),
SCF => f.write_str("SCF"),
CCF => f.write_str("CCF"),
HALT => f.write_str("HALT"),
ADC(target) => write!(f, "ADC {:?}", target),
SUB(target) => write!(f, "SUB {:?}", target),
SBC(target) => write!(f, "SBC {:?}", target),
AND(target) => write!(f, "AND {:?}", target),
XOR(target) => write!(f, "XOR {:?}", target),
OR(target) => write!(f, "OR {:?}", target),
CP(target) => write!(f, "CP {:?}", target),
RET(cond) => write!(f, "RET {:?}", cond),
LDHL(value) => write!(f, "LDHL {:?}", value),
POP(pair) => write!(f, "POP {:?}", pair),
RETI => f.write_str("RETI"),
JP(cond, target) => write!(f, "JP {:?}, {:?}", cond, target),
DI => f.write_str("DI"),
EI => f.write_str("EI"),
CALL(cond, addr) => write!(f, "CALL {:?}, {:?}", cond, addr),
PUSH(pair) => write!(f, "PUSH {:?}", pair),
RST(vector) => write!(f, "RST {:?}", vector),
RLC(register) => write!(f, "RLC {:?}", register),
RRC(register) => write!(f, "RRC {:?}", register),
RL(register) => write!(f, "RL {:?}", register),
RR(register) => write!(f, "RR {:?}", register),
SLA(register) => write!(f, "SLA {:?}", register),
SRA(register) => write!(f, "SRA {:?}", register),
SWAP(register) => write!(f, "SWAP {:?}", register),
SRL(register) => write!(f, "SRL {:?}", register),
BIT(bit, register) => write!(f, "BIT {:?}, {:?}", bit, register),
RES(bit, register) => write!(f, "RES {:?}, {:?}", bit, register),
SET(bit, register) => write!(f, "SET {:?}, {:?}", bit, register),
}
}
}
impl std::fmt::Debug for JumpCondition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use JumpCondition::*;
match *self {
NotZero => f.write_str("NZ"),
Zero => f.write_str("Z"),
NotCarry => f.write_str("NC"),
Carry => f.write_str("C"),
Always => f.write_str(""),
}
}
}
impl std::fmt::Debug for InstrRegister {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use InstrRegister::*;
match *self {
A => f.write_str("A"),
B => f.write_str("B"),
C => f.write_str("C"),
D => f.write_str("D"),
E => f.write_str("E"),
H => f.write_str("H"),
L => f.write_str("L"),
IndirectHL => f.write_str("(HL)"),
}
}
}
impl Cycle {
pub const fn new(num: u32) -> Self {
Self(num)

View File

@@ -1,7 +1,11 @@
pub use cpu::Cpu as LR35902;
pub use gui::Egui;
pub use instruction::Cycle;
pub use joypad::ButtonState;
#[cfg(feature = "debug")]
pub use cpu::RegisterPair;
pub const GB_WIDTH: usize = 160;
pub const GB_HEIGHT: usize = 144;
pub const LR35902_CLOCK_SPEED: u32 = 0x400000; // Hz | 4.194304Mhz
@@ -9,6 +13,7 @@ pub const LR35902_CLOCK_SPEED: u32 = 0x400000; // Hz | 4.194304Mhz
mod bus;
mod cartridge;
mod cpu;
mod gui;
mod high_ram;
mod instruction;
mod interrupt;

View File

@@ -1,8 +1,9 @@
use anyhow::{anyhow, Result};
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
use gb::Egui;
use gb::LR35902_CLOCK_SPEED;
use gb::{ButtonState, Cycle, LR35902};
use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType, Gamepad, Gilrs};
use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType, Gilrs};
use pixels::{Pixels, SurfaceTexture};
use std::time::{Duration, Instant};
use winit::dpi::LogicalSize;
@@ -11,6 +12,9 @@ use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::{Window, WindowBuilder};
use winit_input_helper::WinitInputHelper;
#[cfg(feature = "debug")]
use gb::RegisterPair;
// 160 x 144
const GB_WIDTH: u32 = 160;
const GB_HEIGHT: u32 = 144;
@@ -19,6 +23,9 @@ const SCALE: f64 = 5.0;
const LR35902_CYCLE_TIME: f64 = 1.0f64 / LR35902_CLOCK_SPEED as f64;
const CYCLES_IN_FRAME: Cycle = Cycle::new(70224);
#[cfg(feature = "debug")]
const STEP_MODE_BY_DEFAULT: bool = true;
fn main() -> Result<()> {
let app = App::new(crate_name!())
.version(crate_version!())
@@ -64,20 +71,46 @@ fn main() -> Result<()> {
// Initialize Gamepad Support
let mut gilrs = Gilrs::new().expect("Failed to initialize Gilrs");
let mut active_gamepad = None;
// Initialize GUI
let event_loop = EventLoop::new();
let mut input = WinitInputHelper::new();
let window = create_window(&event_loop, cartridge_title)?;
let mut pixels = create_pixels(&window)?;
let (mut pixels, mut egui) = {
let size = window.inner_size();
let scale_factor = window.scale_factor();
let surface_texture = SurfaceTexture::new(size.width, size.height, &window);
let pixels = Pixels::new(GB_WIDTH, GB_HEIGHT, surface_texture)?;
let egui = Egui::new(size.width, size.height, scale_factor, pixels.context());
(pixels, egui)
};
let mut now = Instant::now();
let mut cycles_in_frame: Cycle = Default::default();
#[cfg(feature = "debug")]
let mut step_mode = STEP_MODE_BY_DEFAULT;
event_loop.run(move |event, _, control_flow| {
// Update egui
egui.handle_event(&event);
if let Event::RedrawRequested(_) = event {
if pixels
.render()
// Prepare egui
egui.prepare(&game_boy);
// Render everything together
let render_result = pixels.render_with(|encoder, target, ctx| {
// Render the texture
ctx.scaling_renderer.render(encoder, target);
// Render egui
egui.render(encoder, target, ctx);
});
if render_result
.map_err(|e| anyhow!("pixels.render() failed: {}", e))
.is_err()
{
@@ -92,39 +125,75 @@ fn main() -> Result<()> {
return;
}
if let Some(size) = input.window_resized() {
pixels.resize_surface(size.width, size.height);
#[cfg(feature = "debug")]
if input.key_pressed(VirtualKeyCode::S) {
step_mode = !step_mode;
}
if active_gamepad.is_none() {
active_gamepad = gilrs.next_event().map(|e| e.id);
if let Some(scale_factor) = input.scale_factor() {
egui.scale_factor(scale_factor);
}
if let Some(size) = input.window_resized() {
pixels.resize_surface(size.width, size.height);
egui.resize(size.width, size.height);
}
// Emulate Game Boy
let mut elapsed_cycles: Cycle = Default::default();
let delta = now.elapsed().subsec_nanos();
now = Instant::now();
#[cfg(feature = "debug")]
if step_mode {
if input.key_pressed(VirtualKeyCode::Space) {
if let Some(event) = gilrs.next_event() {
handle_gamepad_input(&mut game_boy, event);
}
for _ in 0..egui.config.spacebar_step {
elapsed_cycles += game_boy.step();
}
cycles_in_frame %= CYCLES_IN_FRAME;
}
game_boy.get_ppu().copy_to_gui(pixels.get_frame());
window.request_redraw();
return;
}
let cycle_time = Duration::from_secs_f64(LR35902_CYCLE_TIME).subsec_nanos();
let pending_cycles = Cycle::new(delta / cycle_time);
let mut elapsed_cycles: Cycle = Default::default();
while elapsed_cycles <= pending_cycles {
if let Some(event) = gilrs.next_event() {
handle_gamepad_input(&mut game_boy, event);
}
elapsed_cycles += game_boy.step();
#[cfg(feature = "debug")]
{
let pc = game_boy.register_pair(RegisterPair::PC);
if let Some(break_point) = egui.break_point {
if pc == break_point {
step_mode = true;
break;
}
}
}
}
cycles_in_frame += elapsed_cycles;
if cycles_in_frame >= CYCLES_IN_FRAME {
let ppu = game_boy.get_ppu();
let frame = pixels.get_frame();
ppu.copy_to_gui(frame);
window.request_redraw();
// Redraw
cycles_in_frame = Default::default();
cycles_in_frame = Default::default()
game_boy.get_ppu().copy_to_gui(pixels.get_frame());
window.request_redraw();
}
}
});
@@ -139,12 +208,6 @@ fn create_window(event_loop: &EventLoop<()>, title: &str) -> Result<Window> {
.build(&event_loop)?)
}
fn create_pixels(window: &Window) -> Result<Pixels> {
let window_size = window.inner_size();
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, window);
Ok(Pixels::new(GB_WIDTH, GB_HEIGHT, surface_texture)?)
}
fn handle_gamepad_input(game_boy: &mut LR35902, event: GamepadEvent) {
use GamepadEventType::*;
let joypad_status = &mut game_boy.bus.joypad.status;