Skip to content

Commit

Permalink
Added docs, improved public API and fixes some unused codes
Browse files Browse the repository at this point in the history
Signed-off-by: Amjad Alsharafi <[email protected]>
  • Loading branch information
Amjad50 committed Oct 19, 2024
1 parent 895b78d commit fa027ea
Show file tree
Hide file tree
Showing 13 changed files with 192 additions and 81 deletions.
6 changes: 4 additions & 2 deletions plastic_core/src/apu2a03/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ use std::sync::{Arc, Mutex};
use tone_source::{APUChannel, BufferedChannel, TimedAPUChannel};

// for performance
/// The sample rate expected to get from [`NES::audio_buffer`](crate::NES::audio_buffer)
/// Do note that the audio is mono, i.e. 1 channel
pub const SAMPLE_RATE: u32 = 44100;

// after how many apu clocks a sample should be recorded
Expand Down Expand Up @@ -574,7 +576,7 @@ impl Savable for APU2A03 {
fn save<W: std::io::Write>(&self, writer: &mut W) -> Result<(), SaveError> {
bincode::serialize_into(writer, self).map_err(|err| match *err {
bincode::ErrorKind::Io(err) => SaveError::IoError(err),
_ => SaveError::Others,
_ => SaveError::SerializationError,
})?;

Ok(())
Expand All @@ -583,7 +585,7 @@ impl Savable for APU2A03 {
fn load<R: std::io::Read>(&mut self, reader: &mut R) -> Result<(), SaveError> {
let state: APU2A03 = bincode::deserialize_from(reader).map_err(|err| match *err {
bincode::ErrorKind::Io(err) => SaveError::IoError(err),
_ => SaveError::Others,
_ => SaveError::SerializationError,
})?;

let _ = std::mem::replace(self, state);
Expand Down
22 changes: 12 additions & 10 deletions plastic_core/src/cartridge/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,31 @@ use std::{
io::{Error as ioError, ErrorKind},
};

/// Error happening when loading a NES cartridge.
pub enum CartridgeError {
/// Error with file input/output.
/// Contains an [`io::Error`][ioError] which provides more details about the error.
FileError(ioError),

/// The cartridge header is invalid or corrupted.
HeaderError,

/// The file size is too large.
/// Contains the size of the file in bytes.
TooLargeFile(u64),

/// The file extension is not recognized or supported.
ExtensionError,

/// The mapper type is not implemented.
MapperNotImplemented(u16),
Others,
}

impl CartridgeError {
fn get_message(&self) -> String {
match self {
Self::FileError(err) => format!("FileError: {}", err),
Self::HeaderError => "This is not a valid iNES file".to_owned(),
Self::Others => {
"An unknown error occurred while decoding/reading the cartridge".to_owned()
}
Self::TooLargeFile(size) => format!(
"The cartridge reader read all the data needed, but the file \
still has some data at the end with size {}-bytes",
Expand Down Expand Up @@ -54,12 +62,6 @@ impl From<ioError> for CartridgeError {
}
}

impl Default for CartridgeError {
fn default() -> Self {
Self::Others
}
}

pub enum SramError {
NoSramFileFound,
SramFileSizeDoesNotMatch,
Expand Down
9 changes: 7 additions & 2 deletions plastic_core/src/common/save_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ pub trait Savable {
fn load<R: Read>(&mut self, reader: &mut R) -> Result<(), SaveError>;
}

/// Error happening when saving/loading a state
#[derive(Debug)]
pub enum SaveError {
/// Error with file input/output.
/// Contains an [`io::Error`][ioError] which provides more details about the error.
IoError(ioError),
/// Contain Extra Data after the end of the file
ContainExtraData,
Others,
/// Error happened during serialization/deserialization, faulty data
SerializationError,
}

impl From<ioError> for SaveError {
Expand All @@ -30,7 +35,7 @@ impl Display for SaveError {
SaveError::ContainExtraData => {
write!(f, "Contain Extra Data after the end of the file")
}
SaveError::Others => write!(f, "Others"),
SaveError::SerializationError => write!(f, "Serialization Error"),
}
}
}
23 changes: 16 additions & 7 deletions plastic_core/src/controller/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,24 @@ use crate::common::{Bus, Device};
use bitflags::bitflags;
use std::cell::Cell;

/// Represents the keys on an NES controller.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum StandardNESKey {
pub enum NESKey {
/// The 'A' button.
A = 1 << 0,
/// The 'B' button.
B = 1 << 1,
/// The 'Select' button.
Select = 1 << 2,
/// The 'Start' button.
Start = 1 << 3,
/// The 'Up' directional button.
Up = 1 << 4,
/// The 'Down' directional button.
Down = 1 << 5,
/// The 'Left' directional button.
Left = 1 << 6,
/// The 'Right' directional button.
Right = 1 << 7,
}

Expand All @@ -28,15 +37,15 @@ bitflags! {
}

impl StandardNESControllerState {
fn press(&mut self, key: StandardNESKey) {
fn press(&mut self, key: NESKey) {
self.insert(StandardNESControllerState::from_bits(key as u8).unwrap());
}

fn release(&mut self, key: StandardNESKey) {
fn release(&mut self, key: NESKey) {
self.remove(StandardNESControllerState::from_bits(key as u8).unwrap());
}

pub fn set_state(&mut self, key: StandardNESKey, pressed: bool) {
pub fn set_controller_state(&mut self, key: NESKey, pressed: bool) {
if pressed {
self.press(key);
} else {
Expand All @@ -53,7 +62,7 @@ pub struct Controller {
}

impl Controller {
pub fn new() -> Self {
pub(crate) fn new() -> Self {
Self {
primary_state: StandardNESControllerState::empty(),
polled_state: Cell::new(0),
Expand All @@ -62,8 +71,8 @@ impl Controller {
}
}

pub fn set_state(&mut self, key: StandardNESKey, pressed: bool) {
self.primary_state.set_state(key, pressed);
pub fn set_controller_state(&mut self, key: NESKey, pressed: bool) {
self.primary_state.set_controller_state(key, pressed);
}
}

Expand Down
16 changes: 11 additions & 5 deletions plastic_core/src/cpu6502/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,18 @@ const NMI_VECTOR_ADDRESS: u16 = 0xFFFA;
const RESET_VECTOR_ADDRESS: u16 = 0xFFFC;
const IRQ_VECTOR_ADDRESS: u16 = 0xFFFE;

#[derive(PartialEq)]
/// The state of the CPU after one clock cycle
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CPURunState {
DmaTransfere,
/// Currently running a DMA transfer, i.e. not instructions
DmaTransfer,
/// Waiting for the correct number of cycles to pass before executing the next instruction
Waiting,
/// The CPU is in an infinite loop, the u16 is the address of the loop
InfiniteLoop(u16),
/// The CPU is starting an interrupt, next cycle it will jump to the interrupt vector
StartingInterrupt,
/// The CPU is executing a normal instruction (most common)
NormalInstructionExecution,
}

Expand Down Expand Up @@ -149,7 +155,7 @@ where

// since it should read in one cycle and write in the other cycle
self.cycles_to_wait = 1;
CPURunState::DmaTransfere
CPURunState::DmaTransfer
} else if self.nmi_pin_status
|| (self.irq_pin_status
&& self.reg_status & StatusFlag::InterruptDisable as u8 == 0)
Expand Down Expand Up @@ -1450,7 +1456,7 @@ where
fn save<W: Write>(&self, writer: &mut W) -> Result<(), SaveError> {
let state = SavableCPUState::from_cpu(self);

let data = bincode::serialize(&state).map_err(|_| SaveError::Others)?;
let data = bincode::serialize(&state).map_err(|_| SaveError::SerializationError)?;
writer.write_all(data.as_slice())?;

self.bus.save(writer)?;
Expand All @@ -1465,7 +1471,7 @@ where
let state: SavableCPUState =
bincode::deserialize_from(outer_reader).map_err(|err| match *err {
bincode::ErrorKind::Io(err) => SaveError::IoError(err),
_ => SaveError::Others,
_ => SaveError::SerializationError,
})?;

self.load_serialized_state(state);
Expand Down
2 changes: 1 addition & 1 deletion plastic_core/src/display/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ mod tv;

pub use color::Color;
pub use color::COLORS;
pub use tv::{TV, TV_BUFFER_SIZE, TV_HEIGHT, TV_WIDTH};
pub use tv::{COLOR_BYTES_LEN, TV, TV_BUFFER_SIZE, TV_HEIGHT, TV_WIDTH};
6 changes: 5 additions & 1 deletion plastic_core/src/display/tv.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use super::color::Color;

/// The width of the rendering buffer in pixels
pub const TV_WIDTH: usize = 256;
/// The height of the rendering buffer in pixels
pub const TV_HEIGHT: usize = 240;
const COLOR_BYTES_LEN: usize = 3;
/// The number of bytes in a single pixel
pub const COLOR_BYTES_LEN: usize = 3;
/// The size of the rendering buffer in bytes ([`TV_WIDTH`]* [`TV_HEIGHT`] * [`COLOR_BYTES_LEN`])
pub const TV_BUFFER_SIZE: usize = TV_WIDTH * TV_HEIGHT * COLOR_BYTES_LEN;

pub struct TV {
Expand Down
43 changes: 39 additions & 4 deletions plastic_core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
//! # Plastic NES Core
//!
//! This is the core of the Plastic NES emulator. It contains the CPU, PPU, APU, and other components
//! and can be used standalone to run/emulate NES games if you want to handle your own input/output.
//!
//! A very simple example of how to use this crate:
//! ```no_run
//! use plastic_core::NES;
//! # fn display(_: &[u8]) {}
//! # fn play_audio(_: &[f32]) {}
//!
//! fn main() {
//! let mut nes = NES::new("path/to/rom-file.nes").unwrap();
//!
//! loop {
//! nes.clock_for_frame();
//!
//! let pixel_buffer = nes.pixel_buffer();
//! display(&pixel_buffer);
//!
//! let audio_buffer = nes.audio_buffer();
//! play_audio(&audio_buffer);
//! }
//! }
//! ```
//! In the
#[macro_use]
mod common;
mod apu2a03;
Expand All @@ -7,19 +34,27 @@ mod cpu6502;
mod display;
#[cfg(feature = "frontend_misc")]
pub mod misc;
mod nes;
mod ppu2c02;

#[cfg(test)]
mod tests;

pub mod nes;
pub use cartridge::CartridgeError;
pub use common::save_state::SaveError;
pub use controller::NESKey;
pub use nes::NES;

pub mod nes_controller {
pub use super::controller::{StandardNESControllerState, StandardNESKey};
/// Structures used when interacting with the CPU, see also [`NES::clock`][NES::clock]
pub mod cpu {
pub use super::cpu6502::CPURunState;
}

/// Helper variables related to handling pixel buffers from the emulator
pub mod nes_display {
pub use super::display::{Color, TV_BUFFER_SIZE, TV_HEIGHT, TV_WIDTH};
pub use super::display::{COLOR_BYTES_LEN, TV_BUFFER_SIZE, TV_HEIGHT, TV_WIDTH};
}
/// Helper variables related to handling audio buffers from the emulator
pub mod nes_audio {
pub use super::apu2a03::SAMPLE_RATE;
}
Loading

0 comments on commit fa027ea

Please sign in to comment.