From 3a277b66102829df9d78f4f4b4665563a3f435ee Mon Sep 17 00:00:00 2001 From: Yushi OMOTE Date: Sat, 14 Dec 2019 13:24:38 +0900 Subject: [PATCH] Add docs --- codegen/templates/root.rs | 2 + core/examples/pc/hardware.rs | 4 +- core/src/cpu.rs | 57 ++++++++++++++- core/src/debug.rs | 12 ++++ core/src/device.rs | 10 +++ core/src/hardware.rs | 34 ++++++++- core/src/inst.rs | 2 + core/src/lib.rs | 132 ++++++++++++++++++++++++++++++++--- core/src/mmu.rs | 23 ++++++ core/src/system.rs | 21 ++++-- 10 files changed, 277 insertions(+), 20 deletions(-) diff --git a/codegen/templates/root.rs b/codegen/templates/root.rs index 61e583f..591edfa 100644 --- a/codegen/templates/root.rs +++ b/codegen/templates/root.rs @@ -201,10 +201,12 @@ fn op_{{i.code | hex}}(arg: u16, cpu: &mut Cpu, mmu: &mut Mmu) -> (usize, usize) } {% endfor %} +/// Return the mnemonic string for the given opcode. pub fn mnem(code: u16) -> &'static str { MNEMONICS.get(&code).unwrap_or(&"(unknown opcode)") } +/// Decodes the opecode and actually executes one instruction. pub fn decode(code: u16, arg: u16, cpu: &mut Cpu, mmu: &mut Mmu) -> (usize, usize) { trace!("{:04x}: {:04x}: {}", cpu.get_pc(), code, mnem(code)); diff --git a/core/examples/pc/hardware.rs b/core/examples/pc/hardware.rs index 9243e1c..52a7358 100644 --- a/core/examples/pc/hardware.rs +++ b/core/examples/pc/hardware.rs @@ -11,7 +11,7 @@ use std::sync::{ }; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use rgy::hardware::{self, Key, Stream, VRAM_HEIGHT, VRAM_WIDTH}; +use rgy::{Key, Stream, VRAM_HEIGHT, VRAM_WIDTH}; #[derive(Clone)] pub struct Hardware { @@ -153,7 +153,7 @@ impl Hardware { } } -impl hardware::Hardware for Hardware { +impl rgy::Hardware for Hardware { fn vram_update(&mut self, line: usize, buf: &[u32]) { let mut vram = self.vram.lock().unwrap(); for i in 0..buf.len() { diff --git a/core/src/cpu.rs b/core/src/cpu.rs index d5a5fbc..23bd0b0 100644 --- a/core/src/cpu.rs +++ b/core/src/cpu.rs @@ -6,6 +6,7 @@ use log::*; use alloc::fmt; +/// Represents CPU state. #[derive(Clone)] pub struct Cpu { a: u8, @@ -53,6 +54,7 @@ impl fmt::Display for Cpu { } impl Cpu { + /// Create a new CPU state. pub fn new() -> Cpu { Cpu { a: 0, @@ -70,11 +72,18 @@ impl Cpu { } } + /// Switch the CPU state to halting. pub fn halt(&mut self) { debug!("Halted"); - // self.halt = true; + // TODO: self.halt = true; } + /// Execute a single instruction. + /// + /// The function fetches an instruction code from the memory, + /// decodes it, and updates the CPU/memory state accordingly. + /// The return value is the number of clock cycles consumed by the instruction. + /// If the CPU is in the halt state, the function does nothing but returns a fixed clock cycle. pub fn execute(&mut self, mmu: &mut Mmu) -> usize { if self.halt { 4 @@ -86,16 +95,20 @@ impl Cpu { } } + /// Disable interrupts to this CPU. pub fn disable_interrupt(&mut self) { debug!("Disable interrupt"); self.ime = false; } + /// Enable interrupts to this CPU. pub fn enable_interrupt(&mut self) { debug!("Enable interrupt"); self.ime = true; } + /// Check if pending interrupts in the interrupt controller, + /// and process them if any. pub fn check_interrupt(&mut self, mmu: &mut Mmu, ic: &Device) -> usize { if !self.ime { if self.halt { @@ -131,24 +144,32 @@ impl Cpu { self.set_pc(value as u16); } - pub fn stop(&self) {} + /// Stop the CPU. + pub fn stop(&self) { + // TODO: Stop. + } + /// Gets the value of `z` flag in the flag register. pub fn get_zf(&self) -> bool { self.f & 0x80 == 0x80 } + /// Gets the value of `n` flag in the flag register. pub fn get_nf(&self) -> bool { self.f & 0x40 == 0x40 } + /// Gets the value of `h` flag in the flag register. pub fn get_hf(&self) -> bool { self.f & 0x20 == 0x20 } + /// Gets the value of `c` flag in the flag register. pub fn get_cf(&self) -> bool { self.f & 0x10 == 0x10 } + /// Updates the value of `z` flag in the flag register. pub fn set_zf(&mut self, v: bool) { if v { self.f = self.f | 0x80 @@ -157,6 +178,7 @@ impl Cpu { } } + /// Updates the value of `n` flag in the flag register. pub fn set_nf(&mut self, v: bool) { if v { self.f = self.f | 0x40 @@ -165,6 +187,7 @@ impl Cpu { } } + /// Updates the value of `h` flag in the flag register. pub fn set_hf(&mut self, v: bool) { if v { self.f = self.f | 0x20 @@ -173,6 +196,7 @@ impl Cpu { } } + /// Updates the value of `c` flag in the flag register. pub fn set_cf(&mut self, v: bool) { if v { self.f = self.f | 0x10 @@ -181,126 +205,155 @@ impl Cpu { } } + /// Updates the value of `a` register. pub fn set_a(&mut self, v: u8) { self.a = v } + /// Updates the value of `b` register. pub fn set_b(&mut self, v: u8) { self.b = v } + /// Updates the value of `c` register. pub fn set_c(&mut self, v: u8) { self.c = v } + /// Updates the value of `d` register. pub fn set_d(&mut self, v: u8) { self.d = v } + /// Updates the value of `e` register. pub fn set_e(&mut self, v: u8) { self.e = v } + /// Updates the value of `h` register. pub fn set_h(&mut self, v: u8) { self.h = v } + /// Updates the value of `l` register. pub fn set_l(&mut self, v: u8) { self.l = v } + /// Updates the value of `a` and `f` register as a single 16-bit register. pub fn set_af(&mut self, v: u16) { self.a = (v >> 8) as u8; self.f = (v & 0xf0) as u8; } + /// Updates the value of `b` and `c` register as a single 16-bit register. pub fn set_bc(&mut self, v: u16) { self.b = (v >> 8) as u8; self.c = v as u8; } + /// Updates the value of `d` and `e` register as a single 16-bit register pub fn set_de(&mut self, v: u16) { self.d = (v >> 8) as u8; self.e = v as u8; } + /// Updates the value of `h` and `l` register as a single 16-bit register. pub fn set_hl(&mut self, v: u16) { self.h = (v >> 8) as u8; self.l = v as u8; } + /// Gets the value of `a` register. pub fn get_a(&self) -> u8 { self.a } + /// Gets the value of `b` register. pub fn get_b(&self) -> u8 { self.b } + /// Gets the value of `c` register. pub fn get_c(&self) -> u8 { self.c } + /// Gets the value of `d` register. pub fn get_d(&self) -> u8 { self.d } + /// Gets the value of `e` register. pub fn get_e(&self) -> u8 { self.e } + /// Gets the value of `h` register. pub fn get_h(&self) -> u8 { self.h } + /// Gets the value of `l` register. pub fn get_l(&self) -> u8 { self.l } + /// Gets the value of `a` and `f` register as a single 16-bit register. pub fn get_af(&self) -> u16 { (self.a as u16) << 8 | self.f as u16 } + /// Gets the value of `b` and `c` register as a single 16-bit register. pub fn get_bc(&self) -> u16 { (self.b as u16) << 8 | self.c as u16 } + /// Gets the value of `d` and `e` register as a single 16-bit register. pub fn get_de(&self) -> u16 { (self.d as u16) << 8 | self.e as u16 } + /// Gets the value of `h` and `l` register as a single 16-bit register. pub fn get_hl(&self) -> u16 { (self.h as u16) << 8 | self.l as u16 } + /// Gets the value of the program counter. pub fn get_pc(&self) -> u16 { self.pc } + /// Updates the value of the program counter. pub fn set_pc(&mut self, v: u16) { self.pc = v } + /// Gets the value of the stack pointer register. pub fn get_sp(&self) -> u16 { self.sp } + /// Updates the value of the stack pointer register. pub fn set_sp(&mut self, v: u16) { self.sp = v } + /// Pushes a 16-bit value to the stack, updating the stack pointer register. pub fn push(&mut self, mmu: &mut Mmu, v: u16) { let p = self.get_sp().wrapping_sub(2); self.set_sp(self.get_sp().wrapping_sub(2)); mmu.set16(p, v) } + /// Pops a 16-bit value from the stack, updating the stack pointer register. pub fn pop(&mut self, mmu: &mut Mmu) -> u16 { let p = self.get_sp(); self.set_sp(self.get_sp().wrapping_add(2)); mmu.get16(p) } + /// Fetches an opcode from the memory and returns it with its length. pub fn fetch(&self, mmu: &Mmu) -> (u16, u16) { let pc = self.get_pc(); diff --git a/core/src/debug.rs b/core/src/debug.rs index eaf8f42..4b8562a 100644 --- a/core/src/debug.rs +++ b/core/src/debug.rs @@ -2,19 +2,31 @@ use crate::cpu::Cpu; use crate::device::IoHandler; use crate::mmu::{MemRead, MemWrite, Mmu}; +/// Debugger interface. +/// +/// The users of this library can implement this interface to inspect the state of the emulator. pub trait Debugger: IoHandler { + /// The function is called on the initialization phase. fn init(&mut self, mmu: &Mmu); + + /// The function is called right before the emulator starts executing an instruction. Deprecated. fn take_cpu_snapshot(&mut self, cpu: Cpu); + + /// Decode an instruction. fn on_decode(&mut self, mmu: &Mmu); + + /// Check if the external signal is triggered. Deprecated. fn check_signal(&mut self); } impl dyn Debugger { + /// Create an empty debugger. pub fn empty() -> NullDebugger { NullDebugger } } +/// Empty debugger which does nothing. pub struct NullDebugger; impl Debugger for NullDebugger { diff --git a/core/src/device.rs b/core/src/device.rs index 7582b4e..71bfcb0 100644 --- a/core/src/device.rs +++ b/core/src/device.rs @@ -3,13 +3,16 @@ use core::cell::{Ref, RefCell, RefMut}; use crate::mmu::{MemHandler, MemRead, MemWrite, Mmu}; +/// The wrapper type for I/O handlers to register to MMU. pub struct Device(Rc>, bool); impl Device { + /// Create a new device. pub fn new(inner: T) -> Self { Self::inner(inner, false) } + /// Create a new mediater device. pub fn mediate(inner: T) -> Self { Self::inner(inner, true) } @@ -18,27 +21,34 @@ impl Device { Self(Rc::new(RefCell::new(inner)), debug) } + /// Immutably borrow the underlying I/O handler. pub fn borrow<'a>(&'a self) -> Ref<'a, T> { self.0.borrow() } + /// Mutabully borrow the underlying I/O handler. pub fn borrow_mut<'a>(&'a self) -> RefMut<'a, T> { self.0.borrow_mut() } } impl Device { + /// Return the memory-mapped I/O handler of the device. pub fn handler(&self) -> IoMemHandler { IoMemHandler(self.0.clone(), self.1) } } +/// The trait which allows to hook I/O access from the CPU. pub trait IoHandler { + /// The function is called when the CPU attempts to read the memory-mapped I/O. fn on_read(&mut self, mmu: &Mmu, addr: u16) -> MemRead; + /// The function is called when the CPU attempts to write the memory-mapped I/O. fn on_write(&mut self, mmu: &Mmu, addr: u16, value: u8) -> MemWrite; } +/// The handler to intercept memory-mapped I/O. pub struct IoMemHandler(Rc>, bool); impl MemHandler for IoMemHandler { diff --git a/core/src/hardware.rs b/core/src/hardware.rs index 84e046a..163161e 100644 --- a/core/src/hardware.rs +++ b/core/src/hardware.rs @@ -3,25 +3,40 @@ use alloc::rc::Rc; use alloc::vec::Vec; use core::cell::RefCell; +/// The width of the VRAM. pub const VRAM_WIDTH: usize = 160; + +/// The height of the VRAM. pub const VRAM_HEIGHT: usize = 144; +/// Represents a key of the joypad. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum Key { + /// Cursor right key. Right, + /// Cursor left key. Left, + /// Cursor up key. Up, + /// Cursor down key. Down, + /// A key. A, + /// B key. B, + /// Select key. Select, + /// Start key. Start, } +/// Sound wave stream which generates the wave to be played by the sound device. pub trait Stream: Send + 'static { + /// The maximum value of the amplitude returned by this stream. fn max(&self) -> u16; - // Return value is in range 0 - 16 + /// The argument takes the sample rate, and the return value indicates the amplitude, + /// whose max value is determined by [`Stream::max`][]. fn next(&mut self, rate: u32) -> u16; } @@ -38,25 +53,40 @@ impl HardwareHandle { } } +/// The interface to abstracts the OS-specific functions. +/// +/// The users of this emulator library need to implement this trait, +/// providing OS-specific functions. pub trait Hardware { + /// Called when one horizontal line in the display is updated. fn vram_update(&mut self, line: usize, buffer: &[u32]); + /// Called when the emulator checks if the key is pressed. fn joypad_pressed(&mut self, key: Key) -> bool; + /// Called when the emulator plays a sound. + /// The stream in the argument is the stream which keeps returning wave patterns. fn sound_play(&mut self, stream: Box); - /// Epoch in microseconds + /// Clock source used by the emulator. + /// The return value needs to be epoch time in microseconds. fn clock(&mut self) -> u64; + /// Send one byte to the serial port. fn send_byte(&mut self, b: u8); + /// Try receiving one byte from the serial port. fn recv_byte(&mut self) -> Option; + /// Called every time the CPU executes one instruction. + /// Returning `false` stops the emulator. fn sched(&mut self) -> bool { true } + /// Called when the CPU attempts to write save data to the cartridge battery-backed RAM. fn load_ram(&mut self, size: usize) -> Vec; + /// Called when the CPU attempts to read save data from the cartridge battery-backed RAM. fn save_ram(&mut self, ram: &[u8]); } diff --git a/core/src/inst.rs b/core/src/inst.rs index f282b2e..b13aa8e 100644 --- a/core/src/inst.rs +++ b/core/src/inst.rs @@ -6469,10 +6469,12 @@ fn op_cbff(arg: u16, cpu: &mut Cpu, mmu: &mut Mmu) -> (usize, usize) { (8, 2) } +/// Return the mnemonic string for the given opcode. pub fn mnem(code: u16) -> &'static str { MNEMONICS.get(&code).unwrap_or(&"(unknown opcode)") } +/// Decodes the opecode and actually executes one instruction. pub fn decode(code: u16, arg: u16, cpu: &mut Cpu, mmu: &mut Mmu) -> (usize, usize) { trace!("{:04x}: {:04x}: {}", cpu.get_pc(), code, mnem(code)); diff --git a/core/src/lib.rs b/core/src/lib.rs index 3a3bed6..f2e2b16 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,9 +1,111 @@ //! //! `rgy` is no-std cross-platform Rust GameBoy emulator library. //! +//! The users of this library only needs to implement [`Hardware`][] trait, which abstracts OS-specific function. +//! Once it's implemented, the emulator works. +//! +//! The following code is the example which just implements `Hardware`. The implementation does nothing. +//! You can replace the body of each function with the actual meaningful logic. +//! +//! ```rust,no_run +//! use rgy::{Config, Key, Stream, VRAM_HEIGHT, VRAM_WIDTH}; +//! +//! struct Hardware { +//! dummy_display: Vec>, +//! } +//! +//! impl Hardware { +//! fn new() -> Self { +//! // Create a frame buffer with the size VRAM_WIDTH * VRAM_HEIGHT. +//! let dummy_display = vec![vec![0u32; VRAM_HEIGHT]; VRAM_WIDTH]; +//! +//! Self { dummy_display } +//! } +//! } +//! +//! impl rgy::Hardware for Hardware { +//! // Called when a horizontal line in the display is updated by the emulator. +//! fn vram_update(&mut self, line: usize, buffer: &[u32]) { +//! // `line` corresponds to the y coordinate. +//! let y = line; +//! +//! for (x, col) in buffer.iter().enumerate() { +//! // TODO: Update the pixels in the actual display here. +//! self.dummy_display[x][y] = *col; +//! } +//! } +//! +//! // Called when the emulator checks if a key is pressed or not. +//! fn joypad_pressed(&mut self, key: Key) -> bool { +//! println!("Is {:?} pressed?", key); +//! +//! // TODO: Read a keyboard device and check if the `key` is pressed or not. +//! +//! false +//! } +//! +//! // Called when the emulator plays a sound. +//! fn sound_play(&mut self, _stream: Box) { +//! // TODO: Play the wave pattern provided `Stream`. +//! } +//! +//! // Provides clock for the emulator. +//! fn clock(&mut self) -> u64 { +//! // TODO: Return the epoch in microseconds. +//! let epoch = std::time::SystemTime::now() +//! .duration_since(std::time::UNIX_EPOCH) +//! .expect("Couldn't get epoch"); +//! epoch.as_micros() as u64 +//! } +//! +//! // Called when the emulator sends a byte to the serial port. +//! fn send_byte(&mut self, _b: u8) { +//! // TODO: Send a byte to a serial port. +//! } +//! +//! // Called when the emulator peeks a byte from the serial port. +//! fn recv_byte(&mut self) -> Option { +//! // TODO: Check the status of the serial port and read a byte if any. +//! None +//! } +//! +//! // Called every time the emulator executes an instruction. +//! fn sched(&mut self) -> bool { +//! // TODO: Do some periodic jobs if any. Return `true` to continue, `false` to stop the emulator. +//! println!("It's running!"); +//! true +//! } +//! +//! // Called when the emulator stores the save data to the battery-backed RAM. +//! fn load_ram(&mut self, size: usize) -> Vec { +//! // TODO: Return save data. +//! vec![0; size] +//! } +//! +//! // Called when the emulator loads the save data from the battery-backed RAM. +//! fn save_ram(&mut self, _ram: &[u8]) { +//! // TODO: Store save data. +//! } +//! } +//! +//! fn main() { +//! // Create the default config. +//! let cfg = Config::new(); +//! +//! // Create the hardware instance. +//! let hw = Hardware::new(); +//! +//! // TODO: The content of a ROM file, which can be downloaded from the Internet. +//! let rom = vec![0u8; 1024]; +//! +//! // Run the emulator. +//! rgy::run(cfg, &rom, hw); +//! } +//! ``` #![no_std] #![cfg_attr(feature = "readme", feature(external_doc))] +#![warn(missing_docs)] #[cfg_attr(feature = "readme", doc(include = "../../README.md"))] type _Doctest = (); @@ -12,24 +114,34 @@ extern crate alloc; mod alu; mod cgb; -pub mod cpu; -pub mod debug; -pub mod device; mod dma; mod fc; mod gpu; -pub mod hardware; mod ic; -pub mod inst; mod joypad; mod mbc; -pub mod mmu; mod serial; mod sound; mod system; mod timer; -pub use crate::{ - hardware::{Hardware, Key, Stream, VRAM_HEIGHT, VRAM_WIDTH}, - system::{run, run_debug, Config, System}, -}; +/// CPU state. +pub mod cpu; + +/// Debugger interface. +pub mod debug; + +/// Adaptor to register devices to MMU. +pub mod device; + +/// Decoder which evaluates each CPU instructions. +pub mod inst; + +/// Handles memory and I/O port access from the CPU. +pub mod mmu; + +/// Hardware interface, which abstracts OS-specific functions. +mod hardware; + +pub use crate::hardware::{Hardware, Key, Stream, VRAM_HEIGHT, VRAM_WIDTH}; +pub use crate::system::{run, run_debug, Config, System}; diff --git a/core/src/mmu.rs b/core/src/mmu.rs index bc2e1e6..9fd9594 100644 --- a/core/src/mmu.rs +++ b/core/src/mmu.rs @@ -2,26 +2,42 @@ use alloc::rc::Rc; use alloc::{vec, vec::Vec}; use hashbrown::HashMap; +/// The variants to control memory read access from the CPU. pub enum MemRead { + /// Replaces the value passed from the memory to the CPU. Replace(u8), + /// Shows the actual value passed from the memory to the CPU. PassThrough, } +/// The variants to control memory write access from the CPU. pub enum MemWrite { + /// Replaces the value to be written by the CPU to the memory. Replace(u8), + /// Allows to write the original value from the CPU to the memory. PassThrough, + /// Discard the write access from the CPU. Block, } +/// The handler to intercept memory access from the CPU. pub trait MemHandler { + /// The function is called when the CPU attempts to read from the memory. fn on_read(&self, mmu: &Mmu, addr: u16) -> MemRead; + /// The function is called when the CPU attempts to write to the memory. fn on_write(&self, mmu: &Mmu, addr: u16, value: u8) -> MemWrite; } +/// The handle of a memory handler. #[derive(Clone, PartialEq, Eq, Hash)] pub struct Handle(u64); +/// The memory management unit (MMU) +/// +/// This unit holds a memory byte array which represents address space of the memory. +/// It provides the logic to intercept access from the CPU to the memory byte array, +/// and to modify the memory access behaviour. pub struct Mmu { ram: Vec, handles: HashMap, @@ -30,6 +46,7 @@ pub struct Mmu { } impl Mmu { + /// Create a new MMU instance. pub fn new() -> Mmu { Mmu { ram: vec![0u8; 0x10000], @@ -47,6 +64,7 @@ impl Mmu { Handle(handle) } + /// Add a new memory handler. pub fn add_handler(&mut self, range: (u16, u16), handler: T) -> Handle where T: MemHandler + 'static, @@ -71,6 +89,7 @@ impl Mmu { handle } + /// Remove a memory handler. #[allow(unused)] pub fn remove_handler(&mut self, handle: &Handle) where @@ -89,6 +108,7 @@ impl Mmu { } } + /// Reads one byte from the given address in the memory. pub fn get8(&self, addr: u16) -> u8 { if let Some(handlers) = self.handlers.get(&addr) { for (_, handler) in handlers { @@ -107,6 +127,7 @@ impl Mmu { } } + /// Writes one byte at the given address in the memory. pub fn set8(&mut self, addr: u16, v: u8) { if let Some(handlers) = self.handlers.get(&addr) { for (_, handler) in handlers { @@ -129,12 +150,14 @@ impl Mmu { } } + /// Reads two bytes from the given addresss in the memory. pub fn get16(&self, addr: u16) -> u16 { let l = self.get8(addr); let h = self.get8(addr + 1); (h as u16) << 8 | l as u16 } + /// Writes two bytes at the given address in the memory. pub fn set16(&mut self, addr: u16, v: u16) { self.set8(addr, v as u8); self.set8(addr + 1, (v >> 8) as u8); diff --git a/core/src/system.rs b/core/src/system.rs index 65f87e1..44cb900 100644 --- a/core/src/system.rs +++ b/core/src/system.rs @@ -15,18 +15,20 @@ use crate::sound::Sound; use crate::timer::Timer; use log::*; +/// Configuration of the emulator. pub struct Config { - /// CPU frequency + /// CPU frequency. pub(crate) freq: u64, - /// Cycle sampling count in CPU frequency controller + /// Cycle sampling count in the CPU frequency controller. pub(crate) sample: u64, - /// Delay unit in CPU frequency controller + /// Delay unit in CPU frequency controller. pub(crate) delay_unit: u64, - /// Don't adjust CPU frequency + /// Don't adjust CPU frequency. pub(crate) native_speed: bool, } impl Config { + /// Create the default configuration. pub fn new() -> Self { let freq = 4194300; // 4.1943 MHz Self { @@ -37,27 +39,32 @@ impl Config { } } + /// Set the CPU frequency. pub fn freq(mut self, freq: u64) -> Self { self.freq = freq; self } + /// Set the sampling count of the CPU frequency controller. pub fn sample(mut self, sample: u64) -> Self { self.sample = sample; self } + /// Set the delay unit. pub fn delay_unit(mut self, delay: u64) -> Self { self.delay_unit = delay; self } + /// Set the flag to run at native speed. pub fn native_speed(mut self, native: bool) -> Self { self.native_speed = native; self } } +/// Represents the entire emulator context. pub struct System { cfg: Config, hw: HardwareHandle, @@ -77,6 +84,7 @@ impl System where D: Debugger + 'static, { + /// Create a new emulator context. pub fn new(cfg: Config, rom: &[u8], hw: T, dbg: D) -> Self where T: Hardware + 'static, @@ -174,6 +182,9 @@ where mmu } + /// Run a single step of emulation. + /// This function needs to be called repeatedly until it returns `false`. + /// Returning `false` indicates the end of emulation, and the functions shouldn't be called again. pub fn poll(&mut self) -> bool { if !self.hw.get().borrow_mut().sched() { return false; @@ -186,10 +197,12 @@ where } } +/// Run the emulator with the given configuration. pub fn run(cfg: Config, rom: &[u8], hw: T) { run_inner(cfg, rom, hw, Debugger::empty()) } +/// Run the emulator with the given configuration and debugger. pub fn run_debug( cfg: Config, rom: &[u8],