Compare commits
2 commits
a289a10798
...
edaca0a9fd
Author | SHA1 | Date | |
---|---|---|---|
edaca0a9fd | |||
337b1ab05b |
17 changed files with 95 additions and 71 deletions
|
@ -2,7 +2,8 @@
|
||||||
<module type="EMPTY_MODULE" version="4">
|
<module type="EMPTY_MODULE" version="4">
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<sourceFolder url="file://$MODULE_DIR$/emulator/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/chip8_core/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/chip8_tui/src" isTestSource="false" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
|
|
28
Cargo.lock
generated
28
Cargo.lock
generated
|
@ -111,11 +111,23 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chip8_tui"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"clap",
|
||||||
|
"crossterm",
|
||||||
|
"emulator",
|
||||||
|
"env_logger",
|
||||||
|
"ratatui",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.23"
|
version = "4.5.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
@ -123,9 +135,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.23"
|
version = "4.5.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
|
checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
|
@ -135,9 +147,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.18"
|
version = "4.5.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -248,13 +260,9 @@ name = "emulator"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
|
||||||
"crossterm",
|
|
||||||
"env_logger",
|
|
||||||
"log",
|
"log",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"rand",
|
"rand",
|
||||||
"ratatui",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -2,5 +2,6 @@
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
members = [
|
members = [
|
||||||
"emulator",
|
"chip8_tui",
|
||||||
|
"chip8_core",
|
||||||
]
|
]
|
|
@ -50,7 +50,7 @@ The project is written in Rust and it's organized in the following modules:
|
||||||
│ └── ibm-logo.ch8 # IBM Logo Test ROM
|
│ └── ibm-logo.ch8 # IBM Logo Test ROM
|
||||||
├── src
|
├── src
|
||||||
│ ├── display.rs # The screen / display module.
|
│ ├── display.rs # The screen / display module.
|
||||||
│ ├── emulator.rs # The emulator logic which emulates the CPU.
|
│ ├── chip8_core.rs # The chip8_core logic which emulates the CPU.
|
||||||
│ ├── input.rs # The input logic.
|
│ ├── input.rs # The input logic.
|
||||||
│ ├── instruction.rs # The instruction decoding logic.
|
│ ├── instruction.rs # The instruction decoding logic.
|
||||||
│ ├── main.rs # The main file. This is the entrypoint.
|
│ ├── main.rs # The main file. This is the entrypoint.
|
||||||
|
|
|
@ -6,18 +6,10 @@ edition = "2021"
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
dead_code = "allow"
|
dead_code = "allow"
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
lto = true
|
|
||||||
codegen-units = 1
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
env_logger = "0.11.5"
|
|
||||||
anyhow = "1.0.93"
|
anyhow = "1.0.93"
|
||||||
ratatui = "0.29.0"
|
|
||||||
crossterm = "0.28.1"
|
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
clap = { version = "4.5.23", features = ["derive"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.4.1"
|
pretty_assertions = "1.4.1"
|
13
chip8_core/src/display.rs
Normal file
13
chip8_core/src/display.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/// Represents the display's width in pixels.
|
||||||
|
pub const DISPLAY_WIDTH: usize = 64;
|
||||||
|
|
||||||
|
/// Represents the display's height pixels.
|
||||||
|
pub const DISPLAY_HEIGHT: usize = 32;
|
||||||
|
|
||||||
|
/// Display trait
|
||||||
|
pub trait Display {
|
||||||
|
/// Re-draws the display.
|
||||||
|
fn clear(&mut self);
|
||||||
|
/// Renders the display data on screen.
|
||||||
|
fn render(&mut self, display_data: &[bool; DISPLAY_WIDTH * DISPLAY_HEIGHT]);
|
||||||
|
}
|
|
@ -42,7 +42,7 @@ where
|
||||||
S: SoundModule,
|
S: SoundModule,
|
||||||
I: InputModule,
|
I: InputModule,
|
||||||
{
|
{
|
||||||
/// Memory represents the emulator's memory.
|
/// Memory represents the chip8_core's memory.
|
||||||
memory: [u8; MEMORY_SIZE],
|
memory: [u8; MEMORY_SIZE],
|
||||||
/// Registers holds the general purpose registers.
|
/// Registers holds the general purpose registers.
|
||||||
registers: [u8; NUMBER_OF_REGISTERS],
|
registers: [u8; NUMBER_OF_REGISTERS],
|
||||||
|
@ -63,7 +63,7 @@ where
|
||||||
sound_module: S,
|
sound_module: S,
|
||||||
/// The module responsible for receiving user input.
|
/// The module responsible for receiving user input.
|
||||||
input_module: I,
|
input_module: I,
|
||||||
/// The stack of the emulator.
|
/// The stack of the chip8_core.
|
||||||
stack: Stack<u16>,
|
stack: Stack<u16>,
|
||||||
/// Holds the display data, each bit corresponds to a pixel.
|
/// Holds the display data, each bit corresponds to a pixel.
|
||||||
display_data: [bool; DISPLAY_WIDTH * DISPLAY_HEIGHT],
|
display_data: [bool; DISPLAY_WIDTH * DISPLAY_HEIGHT],
|
||||||
|
@ -467,7 +467,7 @@ where
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads the ROM found at the rom path in the emulator's RAM memory.
|
/// Loads the ROM found at the rom path in the chip8_core's RAM memory.
|
||||||
fn load_rom<T>(&mut self, mut rom: T) -> Result<(), anyhow::Error>
|
fn load_rom<T>(&mut self, mut rom: T) -> Result<(), anyhow::Error>
|
||||||
where
|
where
|
||||||
T: Read,
|
T: Read,
|
16
chip8_core/src/input.rs
Normal file
16
chip8_core/src/input.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/// InputModule retrieves the keys from the hardware or software input control module.
|
||||||
|
pub trait InputModule {
|
||||||
|
/// Returns the key value of the corresponding pressed key.
|
||||||
|
/// None if no key is pressed.
|
||||||
|
fn get_key_pressed(&mut self) -> Option<u16>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// NoInput always returns none when queried for input.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct NoInput;
|
||||||
|
|
||||||
|
impl InputModule for NoInput {
|
||||||
|
fn get_key_pressed(&mut self) -> Option<u16> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
6
chip8_core/src/lib.rs
Normal file
6
chip8_core/src/lib.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
pub mod display;
|
||||||
|
pub mod emulator;
|
||||||
|
pub mod input;
|
||||||
|
pub mod instruction;
|
||||||
|
pub mod sound;
|
||||||
|
pub mod stack;
|
5
chip8_core/src/sound.rs
Normal file
5
chip8_core/src/sound.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/// SoundModule represents a module which can produce sound.
|
||||||
|
pub trait SoundModule {
|
||||||
|
/// beep makes a beep sound.
|
||||||
|
fn beep(&mut self);
|
||||||
|
}
|
|
@ -23,7 +23,7 @@ impl<T: Debug> Stack<T> {
|
||||||
self.storage.pop()
|
self.storage.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
/// Returns the top element from the stack without removing it.
|
||||||
pub fn peek(&self) -> Option<&T> {
|
pub fn peek(&self) -> Option<&T> {
|
||||||
self.storage.last()
|
self.storage.last()
|
||||||
}
|
}
|
16
chip8_tui/Cargo.toml
Normal file
16
chip8_tui/Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "chip8_tui"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
emulator = { path = "../chip8_core" }
|
||||||
|
clap = { version = "4.5.26", features = ["derive"] }
|
||||||
|
anyhow = "1.0.95"
|
||||||
|
env_logger = "0.11.6"
|
||||||
|
ratatui = "0.29.0"
|
||||||
|
crossterm = "0.28.1"
|
|
@ -3,23 +3,12 @@ use ratatui::style::{Style, Stylize};
|
||||||
use ratatui::widgets::{Block, Borders};
|
use ratatui::widgets::{Block, Borders};
|
||||||
use ratatui::DefaultTerminal;
|
use ratatui::DefaultTerminal;
|
||||||
|
|
||||||
/// Represents the display's width in pixels.
|
use emulator::display::{Display, DISPLAY_HEIGHT, DISPLAY_WIDTH};
|
||||||
pub const DISPLAY_WIDTH: usize = 64;
|
|
||||||
|
|
||||||
/// Represents the display's height pixels.
|
/// Simple terminal display for the Chip8's chip8_core.
|
||||||
pub const DISPLAY_HEIGHT: usize = 32;
|
|
||||||
|
|
||||||
/// Display trait
|
|
||||||
pub trait Display {
|
|
||||||
/// Re-draws the display.
|
|
||||||
fn clear(&mut self);
|
|
||||||
/// Renders the display data on screen.
|
|
||||||
fn render(&mut self, display_data: &[bool; DISPLAY_WIDTH * DISPLAY_HEIGHT]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Simple terminal display for the Chip8's emulator.
|
|
||||||
pub struct TerminalDisplay {}
|
pub struct TerminalDisplay {}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
impl TerminalDisplay {
|
impl TerminalDisplay {
|
||||||
pub fn new() -> TerminalDisplay {
|
pub fn new() -> TerminalDisplay {
|
||||||
TerminalDisplay {}
|
TerminalDisplay {}
|
|
@ -1,24 +1,8 @@
|
||||||
use crossterm::event::{poll, read, Event, KeyCode};
|
use crossterm::event::{poll, read, Event, KeyCode};
|
||||||
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
|
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
|
||||||
|
use emulator::input::InputModule;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
/// InputModule retrieves the keys from the hardware or software input control module.
|
|
||||||
pub trait InputModule {
|
|
||||||
/// Returns the key value of the corresponding pressed key.
|
|
||||||
/// None if no key is pressed.
|
|
||||||
fn get_key_pressed(&mut self) -> Option<u16>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// NoInput always returns none when queried for input.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct NoInput;
|
|
||||||
|
|
||||||
impl InputModule for NoInput {
|
|
||||||
fn get_key_pressed(&mut self) -> Option<u16> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// CrossTermInput implements input events via the crossterm crate.
|
/// CrossTermInput implements input events via the crossterm crate.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CrossTermInput {
|
pub struct CrossTermInput {
|
|
@ -1,21 +1,18 @@
|
||||||
|
mod display;
|
||||||
|
mod input;
|
||||||
|
mod sound;
|
||||||
|
|
||||||
use crate::display::RatatuiDisplay;
|
use crate::display::RatatuiDisplay;
|
||||||
use crate::emulator::Emulator;
|
|
||||||
use crate::input::CrossTermInput;
|
use crate::input::CrossTermInput;
|
||||||
use crate::sound::TerminalSound;
|
use crate::sound::TerminalSound;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use emulator::emulator::Emulator;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|
||||||
mod display;
|
|
||||||
mod emulator;
|
|
||||||
mod input;
|
|
||||||
mod instruction;
|
|
||||||
mod sound;
|
|
||||||
mod stack;
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(
|
#[command(
|
||||||
version = "1.0",
|
version = "1.0",
|
||||||
about = "A Chip8 emulator.",
|
about = "A Chip8 chip8_core.",
|
||||||
long_about = "A program which emulates the Chip8 system."
|
long_about = "A program which emulates the Chip8 system."
|
||||||
)]
|
)]
|
||||||
struct CliArgs {
|
struct CliArgs {
|
|
@ -1,8 +1,4 @@
|
||||||
/// SoundModule represents a module which can produce sound.
|
use emulator::sound::SoundModule;
|
||||||
pub trait SoundModule {
|
|
||||||
/// beep makes a beep sound.
|
|
||||||
fn beep(&mut self);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TerminalSound is a simple module that makes terminal beep sound.
|
/// TerminalSound is a simple module that makes terminal beep sound.
|
||||||
pub struct TerminalSound;
|
pub struct TerminalSound;
|
Loading…
Reference in a new issue