timers closer

display closer
This commit is contained in:
2024-09-24 10:00:13 -04:00
parent d6f7c73de3
commit 9eb735a858
9 changed files with 385 additions and 139 deletions
+21 -2
View File
@@ -35,18 +35,37 @@ fn hello_world_window(ui: &Ui) {
});
}
struct UiState {
pub show_registers: bool,
pub show_memory: bool,
pub show_video: bool
}
impl Default for UiState {
fn default() -> Self {
UiState {
show_registers: true,
show_memory: true,
show_video: true
}
}
}
fn main() {
pretty_env_logger::init();
let mut system = Chip8Computer::default();
let mut ui_state = UiState::default();
support::simple_init(file!(), move |_, ui| {
// hello_world_window(ui);
EmmaGui::system_controls(&mut system, ui);
EmmaGui::registers_view(&system, ui);
EmmaGui::hex_memory_display(system.memory.clone(), 0x100, 0x10,ui);
let active_instruction = system.pc;
EmmaGui::hex_memory_display(system.memory.clone(), (0x100, 0x10), active_instruction as i16, ui);
EmmaGui::video_display(&system, ui);
// EmmaGui::system_memory_render(system.memory, ui);
-18
View File
@@ -1,18 +0,0 @@
use copypasta::{ClipboardContext, ClipboardProvider};
use imgui::ClipboardBackend;
pub struct ClipboardSupport(pub ClipboardContext);
pub fn init() -> Option<ClipboardSupport> {
ClipboardContext::new().ok().map(ClipboardSupport)
}
impl ClipboardBackend for ClipboardSupport {
fn get(&mut self) -> Option<String> {
self.0.get_contents().ok()
}
fn set(&mut self, text: &str) {
// ignore errors?
let _ = self.0.set_contents(text.to_owned());
}
}
+63 -20
View File
@@ -8,12 +8,38 @@ use emmaemu::chip8::computer::Chip8Computer;
use emmaemu::chip8::system_memory::Chip8SystemMemory;
use emmaemu::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};
pub struct EmmaGui {}
const CELL_WIDTH: i32 = 5i32;
const CELL_HEIGHT: i32 = 5i32;
impl EmmaGui {
pub fn video_display(system_to_control: &Chip8Computer, ui: &Ui) {
ui.window("Display")
.size([300.0, 300.0], Condition::FirstUseEver)
.build(|| {
let origin = ui.cursor_screen_pos();
let fg = ui.get_window_draw_list();
ui.text("This is the video display.");
for current_row in 0..31 {
for current_column in 0..64 {
let x_offset = origin[0] as i32 + (current_column * 5);
let y_offset = origin[1] as i32 + (current_row * 5);
let current_origin = [x_offset as f32, y_offset as f32];
let current_limit = [(x_offset + 5) as f32, (y_offset + 5) as f32];
let memory_offset = (current_row * 64 + current_column) as u16;
let to_render = system_to_control.video_memory.peek(memory_offset);
let color: ImColor32 = if to_render {
ImColor32::from_rgb(0xff, 0x00, 0x00)
} else {
ImColor32::from_rgb(0x00, 0x00, 0xff)
};
fg.add_rect_filled_multicolor(current_origin, current_limit, color, color, color, color);
}
}
});
}
pub fn system_controls(system_to_control: &mut Chip8Computer, ui: &Ui) {
ui.window("!!!! CONTROLS !!!!")
.size([200.0, 200.0], Condition::FirstUseEver)
@@ -31,6 +57,9 @@ impl EmmaGui {
input_file.read_to_end(&mut buffer).expect("unable to read file");
system_to_control.load_bytes_to_memory(0x200, buffer.into());
}
if ui.button("Reset") {
*system_to_control = Chip8Computer::new();
}
});
}
@@ -38,24 +67,27 @@ impl EmmaGui {
ui.window("Registers")
.size([400.0, 500.0], Condition::FirstUseEver)
.build(|| {
ui.text("Registers");
for i in 0..0x10 {
ui.text(format!("V{:X}: {}", i, system.registers[i as usize]));
if i != 7 {
ui.text("Registers");
for i in 0..0x10 {
ui.text(format!("V{:X}: {}", i, system.registers[i as usize]));
if i != 7 {
ui.same_line();
}
}
ui.text("");
ui.text(format!("I: {:03X}", system.i_register));
ui.same_line();
}
}
ui.text("");
ui.text(format!("I: {:03X}", system.i_register));
ui.same_line();
ui.text(format!("ST: {:02X}", system.sound_timer));
ui.same_line();
ui.text(format!("DT: {:02X}", system.delay_timer));
ui.text(format!("PC: {:02X}", system.pc));
ui.text(format!("SP: {:02X}", system.sp));});
ui.text(format!("ST: {:02X}", system.sound_timer.current()));
ui.same_line();
ui.text(format!("DT: {:02X}", system.delay_timer.current()));
ui.text(format!("PC: {:02X}", system.pc));
ui.text(format!("SP: {:02X}", system.sp));
});
}
pub fn hex_memory_display(bytes: Chip8SystemMemory, rows: i32, cols: i32, ui: &Ui) {
pub fn hex_memory_display(bytes: Chip8SystemMemory, position: (i32, i32), active: i16, ui: &Ui) {
let rows = position.0;
let cols = position.1;
ui.window("System Memory")
.size([400.0, 300.0], Condition::FirstUseEver)
.build(|| {
@@ -75,9 +107,21 @@ impl EmmaGui {
y: text_location[1] + text_size[1],
};
if ui.is_mouse_hovering_rect(text_location, [bounding_box.x, bounding_box.y]) {
let hovering = ui.is_mouse_hovering_rect(text_location, [bounding_box.x, bounding_box.y]);
let is_active = data_offset == active as i32;
ui.text_colored(if hovering {
[0., 1., 1., 1.]
} else if is_active {
[1., 0., 1., 1.]
} else {
[1., 1., 0., 1.]
}, formatted_text.clone());
// if we are hovering show that at the bottom...
if hovering {
// Optionally change the text color to indicate it's interactable
ui.text_colored([1.0, 0.0, 0.0, 1.0], formatted_text.clone());
current_x_hover = current_column;
current_y_hover = current_row;
@@ -86,10 +130,9 @@ impl EmmaGui {
println!("Offset: [{}] [0x{:02x}] Value: [{}]", data_offset, data_offset, formatted_text.clone());
// Perform any action here, e.g., call a function, trigger an event, etc.
}
} else {
// Default color text when not hovering
ui.text(formatted_text);
}
// are we on the same line?
if current_column != (cols - 1) {
ui.same_line();
}
+19 -2
View File
@@ -10,8 +10,25 @@ use imgui_winit_support::{HiDpiMode, WinitPlatform};
use std::path::Path;
use std::time::Instant;
pub mod clipboard;
pub mod emmagui_support;
use copypasta::{ClipboardContext, ClipboardProvider};
use imgui::ClipboardBackend;
pub struct ClipboardSupport(pub ClipboardContext);
pub fn clipboard_init() -> Option<ClipboardSupport> {
ClipboardContext::new().ok().map(ClipboardSupport)
}
impl ClipboardBackend for ClipboardSupport {
fn get(&mut self) -> Option<String> {
self.0.get_contents().ok()
}
fn set(&mut self, text: &str) {
// ignore errors?
let _ = self.0.set_contents(text.to_owned());
}
}
pub const FONT_SIZE: f32 = 13.0;
@@ -42,7 +59,7 @@ where
.build(&event_loop);
let mut renderer = Renderer::init(&mut imgui, &display).expect("Failed to initialize renderer");
if let Some(backend) = clipboard::init() {
if let Some(backend) = clipboard_init() {
imgui.set_clipboard_backend(backend);
} else {
eprintln!("Failed to initialize clipboard");
+85 -75
View File
@@ -1,5 +1,7 @@
use log::{debug, error};
use crate::chip8::delay_timer::DelayTimer;
use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;
use crate::chip8::sound_timer::SoundTimer;
use crate::constants::{CHIP8_MEMORY_SIZE, CHIP8_REGISTER_COUNT};
use super::{
@@ -13,8 +15,8 @@ pub struct Chip8Computer {
pub sp: u8,
pub memory: Chip8SystemMemory,
pub registers: [u8; 16],
pub sound_timer: u8,
pub delay_timer: u8,
pub sound_timer: SoundTimer,
pub delay_timer: DelayTimer,
pub i_register: u16,
pub video_memory: Chip8Video,
pub state: Chip8CpuStates,
@@ -28,8 +30,8 @@ impl Default for Chip8Computer {
memory: Chip8SystemMemory::default(),
video_memory: Chip8Video::default(),
registers: [0; CHIP8_REGISTER_COUNT as usize],
sound_timer: 0xFF,
delay_timer: 0xFF,
sound_timer: SoundTimer::new(),
delay_timer: DelayTimer::new(),
i_register: 0x0000,
state: Chip8CpuStates::WaitingForInstruction,
}
@@ -50,7 +52,7 @@ impl Chip8Computer {
}
}
pub fn step_system(&mut self) -> Self {
pub fn step_system(&mut self) -> &mut Chip8Computer {
// read the next instruction
let mut working_instruction: u16 = 0b0000000000000000;
@@ -61,7 +63,9 @@ impl Chip8Computer {
let decoded_instruction =
Chip8Computer::decode_instruction(working_instruction);
println!("DECODED INSTRUCTION = {:?}", decoded_instruction);
// println!("DECODED INSTRUCTION = {:?}", decoded_instruction);
// start by moving to the next instruction
self.pc += 2;
match (self.state, decoded_instruction) {
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SysAddr(target_address)) => {
@@ -70,150 +74,136 @@ impl Chip8Computer {
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::CLS) => {
debug!("INST: CLS");
self.pc += 0x2;
for i in 0..(64 * 32) {
self.video_memory.poke(i, false);
}
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::RET) => {
debug!("INST: RET");
debug!("INST* RET");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::JpAddr(new_address)) => {
debug!("INST: JP_ADDR: {new_address}");
debug!("INST: JP_ADDR: {new_address:4x}");
self.pc = new_address as u16;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::CallAddr(sub_address)) => {
debug!("INST: CALL_ADDR: {sub_address}");
self.pc += 0x2;
debug!("INST* CALL_ADDR: {sub_address:4x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SeVxByte(vx_register, byte)) => {
debug!("INST: SeVxByte: {vx_register}/{byte}");
self.pc += 0x2;
debug!("INST: SeVxByte: 0x{vx_register:1x}/0x{byte:2x}");
self.registers[vx_register as usize] = byte as u8;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SneVxByte(vx_register, byte)) => {
debug!("INST: SneVxByte: {vx_register}/{byte}");
self.pc += 0x2;
debug!("INST: SneVxByte: 0x{vx_register:1x}/0x{byte:2x}");
if self.registers[vx_register as usize] == byte as u8 {
self.pc += 2;
}
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SeVxVy(vx_register, vy_register)) => {
debug!("INST: SeVxVy: {vx_register}/{vy_register}");
self.pc += 0x2;
debug!("INST* SeVxVy: 0x{vx_register:1x}/0x{vy_register:1x}");
if self.registers[vx_register as usize] == self.registers[vy_register as usize] {
self.pc += 2;
};
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxByte(vx_register, byte)) => {
debug!("INST: LdVxByte: {vx_register}/{byte}");
self.pc += 0x2;
debug!("INST* LdVxByte: 0x{vx_register:1x}/0x{byte:2x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddVxByte(vx_register, byte)) => {
debug!("INST: AddVxByte: {vx_register}/{byte}");
self.pc += 0x2;
debug!("INST: AddVxByte: 0x{vx_register:1x}/0x{byte:2x}");
self.registers[vx_register as usize] += byte as u8;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxVy(vx_register, vy_register)) => {
debug!("INST: LdVxVy: {vx_register}/{vy_register}");
debug!("INST: LdVxVy: 0x{vx_register:1x}/0x{vy_register:1x}");
self.registers[vx_register as usize] = self.registers[vy_register as usize];
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::OrVxVy(vx_register, vy_register)) => {
debug!("INST: OrVxVy: {vx_register}/{vy_register}");
debug!("INST: OrVxVy: {vx_register}/{vy_register:1x}");
self.registers[vx_register as usize] = self.registers[vx_register as usize] | self.registers[vy_register as usize];
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AndVxVy(vx_register, vy_register)) => {
debug!("INST: AndVxVy: {vx_register}/{vy_register}");
debug!("INST: AndVxVy: {vx_register:1x}/{vy_register:1x}");
self.registers[vx_register as usize] = self.registers[vx_register as usize] & self.registers[vy_register as usize];
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::XorVxVy(vx_register, vy_register)) => {
debug!("INST: XorVxVy: {vx_register}/{vy_register}");
debug!("INST: XorVxVy: {vx_register:1x}/{vy_register:1x}");
self.registers[vx_register as usize] = self.registers[vx_register as usize] ^ self.registers[vy_register as usize];
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddVxVy(vx_register, vy_register)) => {
debug!("INST: AddVxVy: {vx_register}/{vy_register}");
self.pc += 0x2;
debug!("INST: AddVxVy: {vx_register:1x}/{vy_register:1x}");
self.registers[vx_register as usize] += self.registers[vy_register as usize];
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SubVxVy(vx_register, vy_register)) => {
debug!("INST: SubVxVy: {vx_register}/{vy_register}");
self.pc += 0x2;
debug!("INST: SubVxVy: {vx_register:1x}/{vy_register:1x}");
self.registers[vx_register as usize] -= self.registers[vy_register as usize];
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::ShrVxVy(vx_register, vy_register)) => {
debug!("INST: ShrVxVy: {vx_register}/{vy_register}");
self.pc += 0x2;
debug!("INST* ShrVxVy: {vx_register:1x}/{vy_register:1x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SubnVxVy(vx_register, vy_register)) => {
debug!("INST: SubnVxVy: {vx_register}/{vy_register}");
self.pc += 0x2;
debug!("INST* SubnVxVy: {vx_register:1x}/{vy_register:1x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::ShlVxVy(vx_register, vy_register)) => {
debug!("INST: ShlVxVy: {vx_register}/{vy_register}");
self.pc += 0x2;
debug!("INST* ShlVxVy: {vx_register:1x}/{vy_register:1x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SneVxVy(vx_register, vy_register)) => {
debug!("INST: SneVxVy: {vx_register}/{vy_register}");
self.pc += 0x2;
debug!("INST: SneVxVy: {vx_register:1x}/{vy_register:1x}");
if self.registers[vx_register as usize] != self.registers[vy_register as usize] {
self.pc += 02;
}
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdIAddr(addr)) => {
debug!("INST: LdIAddr: {addr}");
self.pc += 0x2;
debug!("INST* LdIAddr: [0x{addr:4x}]");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::JpV0Addr(addr)) => {
debug!("INST: JpV0Addr: {addr}");
self.pc += 0x2;
debug!("INST* JpV0Addr: 0x{addr:4x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::RndVxByte(vx_register, byte)) => {
debug!("INST: RndVxByte: {vx_register}/{byte}");
self.pc += 0x2;
debug!("INST* RndVxByte: 0x{vx_register:1x}/0x{byte:2x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::DrawVxVyNibble(vx_register, vy_register, nibble)) => {
debug!("INST: DrawVxVyNibble: {vx_register}/{vy_register}/{nibble}");
self.pc += 0x2;
debug!("INST* DrawVxVyNibble: 0x{vx_register:1x}/0x{vy_register:1x}/0x{nibble:1x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SkpVx(vx_register)) => {
debug!("INST: SkpVx: {vx_register}");
self.pc += 0x2;
debug!("INST* SkpVx: 0x{vx_register:1x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SnkpVx(vx_register)) => {
debug!("INST: SnkpVx: {vx_register}");
self.pc += 0x2;
debug!("INST* SnkpVx: 0x{vx_register:1x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxDt(vx_register)) => {
debug!("INST: LdVxDt: {vx_register}");
self.pc += 0x2;
debug!("INST* LdVxDt: 0x{vx_register:1x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxK(vx_register)) => {
debug!("INST: LdVxK: {vx_register}");
self.pc += 0x2;
debug!("INST* LdVxK: 0x{vx_register:1x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdDtVx(vx_register)) => {
debug!("INST: LdDtVx: {vx_register}");
self.pc += 0x2;
debug!("INST* LdDtVx: 0x{vx_register:1x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdStVx(vx_register)) => {
debug!("INST: SkpVx: {vx_register}");
self.pc += 0x2;
debug!("INST* SkpVx: 0x{vx_register:1x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddIVx(vx_register)) => {
debug!("INST: AddIVx: {vx_register}");
self.pc += 0x2;
debug!("INST* AddIVx: 0x{vx_register:1x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdFVx(_)) => {
debug!("INST: LdFVu:");
self.pc += 0x2;
debug!("INST* LdFVu:");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdBVx(vx_register)) => {
debug!("INST: LdBVx: {vx_register}");
self.pc += 0x2;
debug!("INST* LdBVx: 0x{vx_register:1x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdIVx(vx_register)) => {
debug!("INST: LdIVx: {vx_register}");
self.pc += 0x2;
debug!("INST* LdIVx: 0x{vx_register:1x}");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxI(vx_register)) => {
debug!("INST: LdVxI: {vx_register}");
self.pc += 0x2;
debug!("INST* LdVxI: 0x{vx_register:1x}");
}
_ => {
error!("UNABLE TO PROCEED. CPU IN UNKNOWN STATE");
}
}
self.clone()
self.sound_timer.tick();
self.delay_timer.tick();
self
}
// nnn or addr - A 12-bit value, the lowest 12 bits of the instruction
@@ -369,7 +359,7 @@ impl Chip8Computer {
// RND Vx, byte
Chip8CpuInstructions::RndVxByte(x_param, byte_param)
}
0xD000..0xDFFF => {
0xD000..=0xDFFF => {
// DRAW Vx, Vy, nibble
Chip8CpuInstructions::DrawVxVyNibble(x_param, y_param, nibble_param)
@@ -387,7 +377,7 @@ impl Chip8Computer {
}
}
}
0xF007..0xFF65 => {
0xF007..=0xFF65 => {
println!("COMPARING LAST BYTE FROM TODECODE: {:2x} to {:4x} with {:2x}", last_byte, to_decode, ubln);
match last_byte {
0x07 => {
@@ -430,6 +420,8 @@ impl Chip8Computer {
#[cfg(test)]
mod test {
use rand::random;
use crate::constants::CHIP8_VIDEO_MEMORY;
use super::*;
#[test]
@@ -486,5 +478,23 @@ mod test {
// assert!(matches!(Chip8Computer::decode_instruction(0x8ab8), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
// assert!(matches!(Chip8Computer::decode_instruction(0xeaba), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
}
#[test]
fn cls_test() {
let mut start_system = Chip8Computer::new();
let mut targets = Vec::new();
for i in 0..10 {
let new_address: i16 = random();
let new_address = new_address % CHIP8_MEMORY_SIZE as i16;
start_system.video_memory.poke(new_address as u16, true);
targets.push(new_address);
}
start_system.memory.poke(0x200, 0x00);
start_system.memory.poke(0x201, 0xe0);
start_system.step_system();
for i in 0..CHIP8_MEMORY_SIZE {
assert!(!start_system.video_memory.peek(i as u16));
}
}
}
+25
View File
@@ -0,0 +1,25 @@
#[derive(Clone, Copy)]
pub struct DelayTimer {
counter: i32
}
impl DelayTimer {
pub fn current(&self) -> i32 {
self.counter
}
pub fn new() -> Self {
DelayTimer {
counter: 0xff
}
}
pub fn set_timer(&mut self, new_value: i32) {
self.counter = new_value
}
pub fn tick(&mut self) {
if self.counter > 0 {
self.counter -= 1;
}
}
}
+33
View File
@@ -0,0 +1,33 @@
use std::{thread, time};
use beep::beep;
use dimensioned::si;
#[derive(Clone, Copy)]
pub struct SoundTimer {
counter: i32
}
impl SoundTimer {
pub fn current(&self) -> i32 {
self.counter
}
pub fn new() -> Self {
SoundTimer {
counter: 0
}
}
pub fn set_timer(&mut self, new_value: i32) {
self.counter = new_value
}
pub fn tick(&mut self) {
if self.counter > 0 {
self.counter -= 1;
thread::spawn(|| {
beep(440).expect("Unable to beep");
thread::sleep(time::Duration::from_millis(500));
}).join().expect("Unable to spawn beep thread");
}
}
}