Compare commits
No commits in common. "7e5ab9932923cae255e9ca494314ee8a2fc294d8" and "ee5504111b2a465a9ca9bcbebfb22b0522fce95b" have entirely different histories.
7e5ab99329
...
ee5504111b
14
src/cpu.rs
14
src/cpu.rs
|
@ -1,10 +1,10 @@
|
||||||
use crate::apu::Apu;
|
use crate::apu::Apu;
|
||||||
use crate::bus::{Bus, BusIo};
|
use crate::bus::{Bus, BusIo};
|
||||||
|
use crate::instruction::cycle::Cycle;
|
||||||
use crate::instruction::Instruction;
|
use crate::instruction::Instruction;
|
||||||
use crate::interrupt::{InterruptEnable, InterruptFlag};
|
use crate::interrupt::{InterruptEnable, InterruptFlag};
|
||||||
use crate::joypad::Joypad;
|
use crate::joypad::Joypad;
|
||||||
use crate::ppu::Ppu;
|
use crate::ppu::Ppu;
|
||||||
use crate::Cycle;
|
|
||||||
use bitfield::bitfield;
|
use bitfield::bitfield;
|
||||||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ impl Cpu {
|
||||||
self.bus.clock();
|
self.bus.clock();
|
||||||
|
|
||||||
let elapsed = match kind {
|
let elapsed = match kind {
|
||||||
ImeEnabled | NonePending => 4,
|
ImeEnabled | NonePending => Cycle::new(4),
|
||||||
SomePending => todo!("Implement HALT bug"),
|
SomePending => todo!("Implement HALT bug"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -146,11 +146,11 @@ impl Cpu {
|
||||||
self.handle_ei();
|
self.handle_ei();
|
||||||
|
|
||||||
// For use in Blargg's Test ROMs
|
// For use in Blargg's Test ROMs
|
||||||
if self.read_byte(0xFF02) == 0x81 {
|
// if self.read_byte(0xFF02) == 0x81 {
|
||||||
let c = self.read_byte(0xFF01) as char;
|
// let c = self.read_byte(0xFF01) as char;
|
||||||
self.write_byte(0xFF02, 0x00);
|
// self.write_byte(0xFF02, 0x00);
|
||||||
eprint!("{}", c);
|
// eprint!("{}", c);
|
||||||
}
|
// }
|
||||||
|
|
||||||
elapsed
|
elapsed
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::cpu::Cpu as SM83;
|
use crate::cpu::Cpu as SM83;
|
||||||
|
use crate::instruction::cycle::Cycle;
|
||||||
use crate::joypad;
|
use crate::joypad;
|
||||||
use crate::ppu::Ppu;
|
use crate::ppu::Ppu;
|
||||||
use crate::Cycle;
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gilrs::Gilrs;
|
use gilrs::Gilrs;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use winit_input_helper::WinitInputHelper;
|
use winit_input_helper::WinitInputHelper;
|
||||||
|
|
||||||
pub const SM83_CYCLE_TIME: Duration = Duration::from_nanos(1_000_000_000 / SM83_CLOCK_SPEED);
|
pub const SM83_CYCLE_TIME: Duration = Duration::from_nanos(1_000_000_000 / SM83_CLOCK_SPEED);
|
||||||
pub const CYCLES_IN_FRAME: Cycle = 456 * 154; // 456 Cycles times 154 scanlines
|
pub const CYCLES_IN_FRAME: Cycle = Cycle::new(456 * 154); // 456 Cycles times 154 scanlines
|
||||||
pub(crate) const SM83_CLOCK_SPEED: u64 = 0x40_0000; // Hz which is 4.194304Mhz
|
pub(crate) const SM83_CLOCK_SPEED: u64 = 0x40_0000; // Hz which is 4.194304Mhz
|
||||||
const DEFAULT_TITLE: &str = "DMG-01 Emulator";
|
const DEFAULT_TITLE: &str = "DMG-01 Emulator";
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ pub fn run(
|
||||||
input: &WinitInputHelper,
|
input: &WinitInputHelper,
|
||||||
target: Cycle,
|
target: Cycle,
|
||||||
) -> Cycle {
|
) -> Cycle {
|
||||||
let mut elapsed = 0;
|
let mut elapsed = Cycle::new(0);
|
||||||
|
|
||||||
if let Some(event) = gamepad.next_event() {
|
if let Some(event) = gamepad.next_event() {
|
||||||
joypad::handle_gamepad_input(game_boy.joypad_mut(), event);
|
joypad::handle_gamepad_input(game_boy.joypad_mut(), event);
|
||||||
|
@ -49,7 +49,7 @@ pub fn run(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_frame(game_boy: &mut SM83, gamepad: &mut Gilrs, input: &WinitInputHelper) -> Cycle {
|
pub fn run_frame(game_boy: &mut SM83, gamepad: &mut Gilrs, input: &WinitInputHelper) -> Cycle {
|
||||||
let mut elapsed = 0;
|
let mut elapsed = Cycle::new(0);
|
||||||
|
|
||||||
if let Some(event) = gamepad.next_event() {
|
if let Some(event) = gamepad.next_event() {
|
||||||
joypad::handle_gamepad_input(game_boy.joypad_mut(), event);
|
joypad::handle_gamepad_input(game_boy.joypad_mut(), event);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
||||||
pub use apu::gen::AudioSPSC;
|
pub use apu::gen::AudioSPSC;
|
||||||
pub type Cycle = u64;
|
pub use instruction::cycle::Cycle;
|
||||||
|
|
||||||
pub const GB_WIDTH: usize = 160;
|
pub const GB_WIDTH: usize = 160;
|
||||||
pub const GB_HEIGHT: usize = 144;
|
pub const GB_HEIGHT: usize = 144;
|
||||||
|
|
40
src/main.rs
40
src/main.rs
|
@ -4,14 +4,14 @@ use gb::{AudioSPSC, Cycle, GB_HEIGHT, GB_WIDTH};
|
||||||
use gilrs::Gilrs;
|
use gilrs::Gilrs;
|
||||||
use pixels::{PixelsBuilder, SurfaceTexture};
|
use pixels::{PixelsBuilder, SurfaceTexture};
|
||||||
use rodio::{OutputStream, Sink};
|
use rodio::{OutputStream, Sink};
|
||||||
use winit::dpi::{LogicalSize, PhysicalSize};
|
use winit::dpi::LogicalSize;
|
||||||
use winit::event::{Event, VirtualKeyCode};
|
use winit::event::{Event, VirtualKeyCode};
|
||||||
use winit::event_loop::{ControlFlow, EventLoop};
|
use winit::event_loop::{ControlFlow, EventLoop};
|
||||||
use winit::window::{Window, WindowBuilder};
|
use winit::window::{Window, WindowBuilder};
|
||||||
use winit_input_helper::WinitInputHelper;
|
use winit_input_helper::WinitInputHelper;
|
||||||
|
|
||||||
const WINDOW_SCALE: usize = 3;
|
const WINDOW_SCALE: f64 = 2.0;
|
||||||
const AUDIO_ENABLED: bool = false;
|
const AUDIO_ENABLED: bool = true;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let app = App::new(crate_name!())
|
let app = App::new(crate_name!())
|
||||||
|
@ -114,17 +114,37 @@ fn main() -> Result<()> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
fn create_window(event_loop: &EventLoop<()>, title: &str) -> Result<Window> {
|
fn create_window(event_loop: &EventLoop<()>, title: &str) -> Result<Window> {
|
||||||
let logical = LogicalSize::new(GB_WIDTH as f64, GB_HEIGHT as f64);
|
let size = LogicalSize::new(
|
||||||
let physical = PhysicalSize::new(
|
(GB_WIDTH as f64) * WINDOW_SCALE,
|
||||||
(GB_WIDTH * WINDOW_SCALE) as f32,
|
(GB_HEIGHT as f64) * WINDOW_SCALE,
|
||||||
(GB_HEIGHT * WINDOW_SCALE) as f32,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(WindowBuilder::new()
|
Ok(WindowBuilder::new()
|
||||||
.with_title(title)
|
.with_title(title)
|
||||||
.with_min_inner_size(logical)
|
.with_inner_size(size)
|
||||||
.with_inner_size(physical)
|
.with_min_inner_size(size)
|
||||||
.with_resizable(true)
|
.with_resizable(true)
|
||||||
|
.with_decorations(true)
|
||||||
|
.with_transparent(false)
|
||||||
|
.build(event_loop)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn create_window(event_loop: &EventLoop<()>, title: &str) -> Result<Window> {
|
||||||
|
use winit::platform::windows::WindowBuilderExtWindows;
|
||||||
|
|
||||||
|
let size = LogicalSize::new(
|
||||||
|
(GB_WIDTH as f64) * WINDOW_SCALE,
|
||||||
|
(GB_HEIGHT as f64) * WINDOW_SCALE,
|
||||||
|
);
|
||||||
|
Ok(WindowBuilder::new()
|
||||||
|
.with_title(title)
|
||||||
|
.with_inner_size(size)
|
||||||
|
.with_min_inner_size(size)
|
||||||
|
.with_resizable(true)
|
||||||
|
.with_decorations(true)
|
||||||
|
.with_transparent(false)
|
||||||
|
.with_drag_and_drop(false)
|
||||||
.build(event_loop)?)
|
.build(event_loop)?)
|
||||||
}
|
}
|
||||||
|
|
14
src/ppu.rs
14
src/ppu.rs
|
@ -1,5 +1,5 @@
|
||||||
use crate::bus::BusIo;
|
use crate::bus::BusIo;
|
||||||
use crate::Cycle;
|
use crate::instruction::cycle::Cycle;
|
||||||
use crate::GB_HEIGHT;
|
use crate::GB_HEIGHT;
|
||||||
use crate::GB_WIDTH;
|
use crate::GB_WIDTH;
|
||||||
use dma::DirectMemoryAccess;
|
use dma::DirectMemoryAccess;
|
||||||
|
@ -79,7 +79,7 @@ impl Ppu {
|
||||||
|
|
||||||
match self.stat.mode() {
|
match self.stat.mode() {
|
||||||
PpuMode::OamScan => {
|
PpuMode::OamScan => {
|
||||||
if self.cycle >= 80 {
|
if self.cycle >= 80.into() {
|
||||||
self.stat.set_mode(PpuMode::Drawing);
|
self.stat.set_mode(PpuMode::Drawing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ impl Ppu {
|
||||||
PpuMode::Drawing => {
|
PpuMode::Drawing => {
|
||||||
if self.ctrl.lcd_enabled() {
|
if self.ctrl.lcd_enabled() {
|
||||||
// Only Draw when the LCD Is Enabled
|
// Only Draw when the LCD Is Enabled
|
||||||
self.draw(self.cycle);
|
self.draw(self.cycle.into());
|
||||||
} else {
|
} else {
|
||||||
self.reset();
|
self.reset();
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ impl Ppu {
|
||||||
PpuMode::HBlank => {
|
PpuMode::HBlank => {
|
||||||
// This mode will always end at 456 cycles
|
// This mode will always end at 456 cycles
|
||||||
|
|
||||||
if self.cycle >= 456 {
|
if self.cycle >= 456.into() {
|
||||||
self.cycle %= 456;
|
self.cycle %= 456;
|
||||||
self.pos.line_y += 1;
|
self.pos.line_y += 1;
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ impl Ppu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PpuMode::VBlank => {
|
PpuMode::VBlank => {
|
||||||
if self.cycle > 456 {
|
if self.cycle > 456.into() {
|
||||||
self.cycle %= 456;
|
self.cycle %= 456;
|
||||||
self.pos.line_y += 1;
|
self.pos.line_y += 1;
|
||||||
|
|
||||||
|
@ -228,7 +228,7 @@ impl Ppu {
|
||||||
self.scan_state.next();
|
self.scan_state.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, _cycle: Cycle) {
|
fn draw(&mut self, _cycle: u32) {
|
||||||
use FetcherState::*;
|
use FetcherState::*;
|
||||||
|
|
||||||
let mut iter = self.obj_buffer.iter_mut();
|
let mut iter = self.obj_buffer.iter_mut();
|
||||||
|
@ -470,7 +470,7 @@ impl Default for Ppu {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
vram: Box::new([0u8; VRAM_SIZE]),
|
vram: Box::new([0u8; VRAM_SIZE]),
|
||||||
cycle: Default::default(),
|
cycle: Cycle::new(0),
|
||||||
frame_buf: Box::new([0; GB_WIDTH * GB_HEIGHT * 4]),
|
frame_buf: Box::new([0; GB_WIDTH * GB_HEIGHT * 4]),
|
||||||
int: Default::default(),
|
int: Default::default(),
|
||||||
ctrl: Default::default(),
|
ctrl: Default::default(),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::Cycle;
|
use crate::instruction::cycle::Cycle;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub(crate) struct DirectMemoryAccess {
|
pub(crate) struct DirectMemoryAccess {
|
||||||
|
@ -56,7 +56,7 @@ impl DirectMemoryAccess {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
self.cycle = 0;
|
self.cycle = Cycle::new(0);
|
||||||
self.state = DmaState::Disabled;
|
self.state = DmaState::Disabled;
|
||||||
self.start.0 = None;
|
self.start.0 = None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
use crate::Cycle;
|
|
||||||
use std::collections::BinaryHeap;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct Scheduler {
|
|
||||||
timestamp: Cycle,
|
|
||||||
queue: BinaryHeap<Event>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Scheduler {
|
|
||||||
pub(crate) fn init() -> Self {
|
|
||||||
let mut scheduler = Self {
|
|
||||||
timestamp: Default::default(),
|
|
||||||
queue: Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
scheduler.push(Event {
|
|
||||||
kind: EventKind::TimestampOverflow,
|
|
||||||
timestamp: Cycle::MAX,
|
|
||||||
cb: |_delay| panic!("Reached Cycle::MAX"),
|
|
||||||
});
|
|
||||||
|
|
||||||
scheduler
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn push(&mut self, event: Event) {
|
|
||||||
self.queue.push(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn step(&mut self, cycles: Cycle) {
|
|
||||||
self.timestamp += cycles;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let should_pop = match self.queue.peek() {
|
|
||||||
Some(event) => self.timestamp >= event.timestamp,
|
|
||||||
None => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if !should_pop {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let event = self.queue.pop().expect("Pop Event from Scheduler Queue");
|
|
||||||
|
|
||||||
(event.cb)(self.timestamp - event.timestamp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct Event {
|
|
||||||
kind: EventKind,
|
|
||||||
cb: fn(Cycle),
|
|
||||||
pub(crate) timestamp: Cycle,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Event {}
|
|
||||||
impl PartialEq for Event {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.kind == other.kind && self.timestamp == other.timestamp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for Event {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for Event {
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
self.timestamp.cmp(&other.timestamp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub(crate) enum EventKind {
|
|
||||||
TimestampOverflow,
|
|
||||||
}
|
|
Loading…
Reference in New Issue