Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
975 changes: 802 additions & 173 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion crates/chippie-emulator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ edition = "2024"

[dependencies]
rand = "0.9.2"
macroquad = "0.4.4"
rand_chacha = "0.9.0"
2 changes: 0 additions & 2 deletions crates/chippie-emulator/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ pub const DISPLAY_WIDTH: u8 = 64;
pub const DISPLAY_HEIGHT: u8 = 32;
/// The size of ram in bytes
pub const RAM_SIZE: u16 = 4096;
/// How many cycles the cpu advances for every frame. This decides how fast the cpu will run
pub const CYCLES_PER_FRAME: usize = 5;
/// For the regular chip 8 roms
pub const ROM_START_ADDRESS: u16 = 0x200;
/// Amount of registers CHIP-8 has
Expand Down
10 changes: 8 additions & 2 deletions crates/chippie-emulator/src/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,15 +334,18 @@ impl Cpu {
self.keyboard[key as usize] = state;
}

/// A single cpu cycle, fetches, decodes, executes opcodes and
/// decrements the timers if relevant. also updates the program counter
/// A single cpu cycle, fetches, decodes, executes opcodes
/// Also updates the program counter
pub fn cycle(&mut self) {
let opcode = self.fetch();
self.program_counter += 2;

let instruction = Instruction::new(opcode);
self.execute(&instruction);
}

/// Needs to be called exactly 60 times a second
pub fn decrement_timers(&mut self) {
self.registers.decrement_sound_timer();
self.registers.decrement_delay_timer();
}
Expand Down Expand Up @@ -822,6 +825,7 @@ mod tests {
);
cpu.registers.set_delay_timer(0x10);
cpu.cycle();
cpu.decrement_timers();
//should be equal to delay timer
assert!(cpu.registers.get_register(0x0) == 0x10);
}
Expand Down Expand Up @@ -851,6 +855,7 @@ mod tests {
);
cpu.registers.set_register(0, 125);
cpu.cycle();
cpu.decrement_timers();
let val = cpu.registers.get_delay_timer();
//the value is one less than the actual value, because during the cycle the delay timer
//also gets decremented by one..
Expand All @@ -867,6 +872,7 @@ mod tests {
);
cpu.registers.set_register(0, 125);
cpu.cycle();
cpu.decrement_timers();
let val = cpu.registers.get_sound_timer();
//the value is one less than the actual value, because during the cycle the delay timer
//also gets decremented by one..
Expand Down
8 changes: 0 additions & 8 deletions crates/chippie-emulator/src/registers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,6 @@ pub struct Registers {
}

impl Registers {
/*pub fn new() -> Self {
Registers {
register: [0u8; 16],
vindex: 0,
delay_timer: 0,
sound_timer: 0,
}
}*/
pub fn set_index_register(&mut self, value: u16) {
self.vindex = value;
}
Expand Down
3 changes: 2 additions & 1 deletion crates/chippie-gui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ edition = "2024"

[dependencies]
iced = { version = "0.13.1", features = [ "canvas", "async-std" ] }

iced_aw = { version = "0.12.2", features = [ "menu" ] }
chippie-emulator = { path = "../chippie-emulator" }
rfd = { version = "0.16.0" }
5 changes: 4 additions & 1 deletion crates/chippie-gui/src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use iced::time::Duration;

pub const APP_NAME: &str = "Chippie";
pub const TICK_INTERVAL: Duration = Duration::new(1, 0);
// 60 times a second (kind of, it should have been 16.667 )
pub const TICK_INTERVAL: Duration = Duration::from_millis(17);
/// How many cycles the cpu advances for every frame. This decides how fast the cpu will run
pub const CYCLES_PER_FRAME: usize = 5;
65 changes: 56 additions & 9 deletions crates/chippie-gui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ use std::rc::Rc;

use iced::keyboard;
use iced::time;
use iced::widget::column;
use iced::{Element, Fill, Subscription};
use iced::widget::{button, column};
use iced::{Element, Fill, Subscription, Task};
use iced_aw::menu::{Item, Menu, MenuBar};
use rfd::{AsyncFileDialog, FileHandle};

use chippie_emulator::{Cpu, DISPLAY_HEIGHT, DISPLAY_WIDTH, NUM_KEYS, RomBuffer};

mod constants;
use constants::CYCLES_PER_FRAME;
mod widgets;

/// Messages that are used for communication between iced widgets.
Expand All @@ -22,12 +25,15 @@ pub enum Message {
Tick,
KeyPressed(keyboard::Key),
KeyReleased(keyboard::Key),
FileSelectButtonClicked,
FileSelected(Option<FileHandle>),
}

/// The main application struct, which constructs GUI and reacts on messages
pub struct Application {
cpu: Cpu,
display: widgets::Display,
running: bool,
}

impl Application {
Expand All @@ -48,27 +54,67 @@ impl Application {

/// Creates a full view of the main window
pub fn view(&self) -> Element<'_, Message> {
column![self.display.view(),]
// Create a menu bar, used to control the state of the emulator
let bar = MenuBar::new(vec![Item::with_menu(
button("File"),
Menu::new(vec![Item::new(
button("Select Rom")
.on_press(Message::FileSelectButtonClicked)
.width(Fill),
)])
.width(180.0),
)]);

column![bar, self.display.view()]
.width(Fill)
.height(Fill)
.into()
}

/// The function, called by iced when there is a message, queued for this application
pub fn update(&mut self, message: Message) {
pub fn update(&mut self, message: Message) -> iced::Task<Message> {
match message {
Message::Tick => self.cpu.cycle(),
Message::Tick => {
if self.running {
for _ in 0..CYCLES_PER_FRAME {
self.cpu.cycle();
}
self.cpu.decrement_timers();
}
}
Message::KeyPressed(key) => {
if let Some(index) = Application::to_index(key) {
self.cpu.set_key_state(index, true);
if let Some(i) = Self::to_index(key) {
self.cpu.set_key_state(i, true)
}
}
Message::KeyReleased(key) => {
if let Some(index) = Application::to_index(key) {
self.cpu.set_key_state(index, false);
if let Some(i) = Self::to_index(key) {
self.cpu.set_key_state(i, false)
}
}
Message::FileSelectButtonClicked => {
return Task::perform(
AsyncFileDialog::new()
.add_filter("Chip8 ROM files".to_string(), &["ch8", "8o"])
.pick_file(),
Message::FileSelected,
);
}
Message::FileSelected(Some(file)) => {
let framebuffer = Rc::new(RefCell::new(
[[false; DISPLAY_WIDTH as usize]; DISPLAY_HEIGHT as usize],
));
let rom = RomBuffer::new(file.path().to_str().unwrap());
self.cpu = Cpu::new(&rom, Rc::clone(&framebuffer));
self.display =
widgets::Display::new(DISPLAY_HEIGHT.into(), DISPLAY_WIDTH.into(), framebuffer);

self.running = true;
}
_ => {}
}

Task::none()
}

/// Creates a specific task, that is run asynchronously by iced
Expand Down Expand Up @@ -109,6 +155,7 @@ impl Default for Application {
DISPLAY_WIDTH.into(),
framebuffer,
),
running: false,
}
}
}