gotta take a break

This commit is contained in:
Trevor Merritt 2024-08-18 18:15:42 -04:00
parent 6dc9ce3721
commit 1a59524f02
18 changed files with 1939 additions and 532 deletions

1143
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -6,3 +6,11 @@ autobenches = true
[dependencies] [dependencies]
ratatui = "0.28.0" ratatui = "0.28.0"
glium = { version = "0.34.0", default-features = true }
image = "0.23"
imgui = { version ="0.12.0", features = ["tables-api"] }
imgui-glium-renderer = { version = "0.12.0" }
imgui-winit-support = { version = "0.12.0" }
winit = { version = "0.27", features = ["x11", "mint"] }
pretty_env_logger = "0.5.0"
copypasta = "0.8"

View File

@ -1,6 +1,6 @@
use std::io::{stdout, Result}; use std::io::{stdout, Result};
use font::load_fonts_into_memory; use emmaemu::{chip8::{computer::Chip8Computer, video::Chip8Video}, constants::{CHIP8_MEMORY_SIZE, CHIP8_REGISTER_COUNT, CHIP8_ROM_SIZE, CHIP8_VIDEO_MEMORY}};
use ratatui::{ use ratatui::{
backend::CrosstermBackend, backend::CrosstermBackend,
crossterm::{ crossterm::{
@ -9,82 +9,11 @@ use ratatui::{
ExecutableCommand, ExecutableCommand,
}, },
layout::{Alignment, Rect}, layout::{Alignment, Rect},
style::Stylize, style::{Style, Stylize},
widgets::{List, Paragraph}, widgets::{List, Paragraph, Widget},
Frame, Terminal, Frame, Terminal,
}; };
// nnn or addr - A 12-bit value, the lowest 12 bits of the instruction
pub fn read_addr_from_instruction(instruction_to_read_from: u16) -> u16 {
instruction_to_read_from & 0b0000111111111111
}
// n or nibble - A 4-bit value, the lowest 4 bits of the instruction
pub fn read_nibble_from_instruction(instruction_to_read_from: u16) -> u16 {
instruction_to_read_from & 0b0000000000001111
}
// x - A 4-bit value, the lower 4 bits of the high byte of the instruction
pub fn read_x_from_instruction(instruction_to_read_from: u16) -> u16 {
(instruction_to_read_from & 0b0000111100000000).rotate_right(8)
}
// y - A 4-bit value, the upper 4 bits of the low byte of the instruction
pub fn read_y_from_instruction(instruction_to_read_from: u16) -> u16 {
(instruction_to_read_from & 0b0000000011110000).rotate_right(4)
}
// kk or byte - An 8-bit value, the lowest 8 bits of the instruction
pub fn read_byte_from_instruction(instruction_to_read_from: u16) -> u16 {
(instruction_to_read_from & 0b0000000011111111)
}
mod font;
#[derive(Clone)]
struct Chip8CpuData {
pub pc: u16,
pub sp: u8,
pub memory: [u8; CHIP8_MEMORY_SIZE as usize],
pub video_memory: [bool; CHIP8_VIDEO_MEMORY],
pub registers: [u8; 16],
pub sound_timer: u8,
pub delay_timer: u8,
pub i_register: u16,
}
const CHIP8_REGISTER_COUNT: i32 = 16;
const CHIP8_MEMORY_SIZE: i32 = 2048i32;
const CHIP8_VIDEO_WIDTH: i32 = 64i32;
const CHIP8_VIDEO_HEIGHT: i32 = 32i32;
const CHIP8_VIDEO_MEMORY: usize = (CHIP8_VIDEO_HEIGHT * CHIP8_VIDEO_WIDTH) as usize;
const CHIP8_ROM_SIZE: usize = 512;
impl Default for Chip8CpuData {
fn default() -> Self {
let mut memory: [u8; 2048] = [0; 2048];
memory = load_fonts_into_memory(memory);
dump_memory_to_console(memory);
Self {
pc: 0x0200,
sp: 0x00,
memory: [0; 2048],
video_memory: [false; CHIP8_VIDEO_MEMORY],
registers: [0; CHIP8_REGISTER_COUNT as usize],
sound_timer: 0xFF,
delay_timer: 0xFF,
i_register: 0x0000,
}
}
}
impl Chip8CpuData {
pub fn new() -> Self {
Chip8CpuData::default()
}
}
fn system_memory_to_text_render(data_to_dump: [u8; 2048]) -> String { fn system_memory_to_text_render(data_to_dump: [u8; 2048]) -> String {
let mut to_return = String::new(); let mut to_return = String::new();
@ -107,311 +36,25 @@ fn dump_memory_to_console(data_to_dump: [u8; 2048]) {
panic!("DONE DUMPING"); panic!("DONE DUMPING");
} }
fn display_data_to_text_render(data_display: [bool; 2048]) -> String { struct ControlKeyboard {
let mut output = String::new();
for row in 0..32 {
let row_offset = row * 32;
for column in 0..64 {
let data_position = row_offset + column;
println!("DP {} {} {} {}", data_position, row, row_offset, column);
output += if data_display[data_position] {
"*"
} else {
" "
};
}
output += "\n";
}
output
} }
fn render_display(display_data: [bool; 2048]) { impl Widget for ControlKeyboard {
println!("{}", display_data_to_text_render(display_data)); fn render(self, area: Rect, buf: &mut ratatui::prelude::Buffer)
} where
Self: Sized {
#[derive(Clone)] let style = Style::new();
enum Chip8CpuStates { buf.set_string(0, 0, "F1 to cycle foreground - F2 to cycle background", style)
WaitingForInstruction,
ExecutingInstruction,
Error,
}
struct Chip8CpuState {
state: Chip8CpuStates,
cpu: Chip8CpuData
}
enum Chip8CpuInstructions {
SysAddr(u16), // 0x0nnn Exit to System Call
CLS, // 0x00E0 Clear Screen
RET, // 0x00EE Return from Subroutine
JpAddr(u16), // 0x1nnn Jump to Address
CallAddr(u16), // 0x2nnn Call Subroutine
SeVxByte(u16, u16), // 0x3xkk Skip next instruction if Vx = kk.
SneVxByte(u16, u16), // 0x4xkk Skip next instruction if Vx != kk
SeVxVy(u16, u16), // 0x5xy0 Skip next instruction if Vx == Vy
LdVxByte(u16, u16), // 0x6xkk Set Vx = kk
AddVxByte(u16, u16), // 0x7xkk Set Vx = Vx + kk
LdVxVy(u16, u16), // 0x8xy0 Set value of Vy in Vx
OrVxVy(u16, u16), // 0x8xy1 Set Vx = Vx OR Vy
AndVxVy(u16, u16), // 0x8xy2 Set Vx = Vx AND Vy
XorVxVy(u16, u16), // 0x8xy3 Set Vx = Vx XOR Vy
AddVxVy(u16, u16), // 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)
SubVxVy(u16, u16), // 0x8xy5 Set Vx = Vx - Vy (Set VF NOT Borrow)
ShrVxVy(u16, u16), // 0x8xy6 Set Vx = Vx SHR 1 (Shift Rotated Right 1)
SubnVxVy(u16, u16), // 0x8xy7 Set Vx = Vy - Vx (Set VF NOT Borrow)
ShlVxVy(u16, u16), // 0x8xyE Shift Left
SneVxVy(u16, u16), // 0x9xy0 Skip next instruction if Vx != Vy
LdIAddr(u16), // 0xAnnn VI = nnn
JpV0Addr(u16), // 0xBnnn Jump to nnn+V0
RndVxByte(u16, u16), // 0xCxkk Vx = random byte AND kk
DrawVxVyNibble(u16, u16, u16), // 0xDxyn Display N byte sprite starting at Vx to Vy
SkpVx(u16), // 0xE09E Skip next instruction if key in Vx pressed
SnkpVx(u16), // 0xE0A1 Skip next instruction if key in Vx NOT pressed
LdVxDt(u16), // 0xFx07 Set Vx = Delay timer
LdVxK(u16), // 0xFx0A Wait for key, put in Vx
LdDtVx(u16), // 0xFx15 Set Delay Timer
LdStVx(u16), // 0xFx18 Set Sount Timer
AddIVx(u16), // 0xFx1E I = I + Vx
LdFVu(u16), // 0xFx29 Set I = Location of sprite for Digit Vx
LdBVx(u16), // 0xFx33 Store BCD of Vx in I, I+1, I+2
LdIVx(u16), // 0xFx55 Store V0 to Vx in memory starting at I
LdVxI(u16), // 0xFx65 Load V0 to Vx in memory starting at I
XXXXERRORINSTRUCTION,
}
fn bytes_to_instruction(to_read: u16) -> Chip8CpuInstructions {
let mut decoded_instruction = Chip8CpuInstructions::XXXXERRORINSTRUCTION;
match to_read {
0x00E0 => {
// 00E0 - CLS
// Clear the display.
decoded_instruction = Chip8CpuInstructions::CLS;
}
0x00EE => {
// 00EE - RET
// Return from a subroutine.
decoded_instruction = Chip8CpuInstructions::RET;
}
0x0000..=0x0FFF => {
// 0nnn - SYS addr
// Jump to a machine code routine at nnn.
decoded_instruction =
Chip8CpuInstructions::SysAddr(read_addr_from_instruction(to_read));
}
0x1000..=0x1FFF => {
// 1nnn - JP addr
// Jump to location nnn.
decoded_instruction = Chip8CpuInstructions::JpAddr(read_addr_from_instruction(to_read));
}
0x2000..=0x2FFF => {
// 2nnn - CALL addr
// Call subroutine at nnn.
decoded_instruction =
Chip8CpuInstructions::CallAddr(read_addr_from_instruction(to_read));
}
0x3000..=0x3FFF => {
// 3xkk - SE Vx, byte
// Skip next instruction if Vx = kk.
decoded_instruction = Chip8CpuInstructions::SeVxByte(
read_x_from_instruction(to_read),
read_byte_from_instruction(to_read),
)
}
0x4000..=0x4FFF => {
// 4xkk - SNE Vx, byte
// Skip next instruction if Vx != kk.
decoded_instruction = Chip8CpuInstructions::SneVxByte(
read_x_from_instruction(to_read),
read_byte_from_instruction(to_read),
);
}
0x5000..=0x5FF0 => {
// 5xy0 - SE Vx, Vy
// Skip next instruction if Vx = Vy.
decoded_instruction = Chip8CpuInstructions::SeVxVy(
read_x_from_instruction(to_read),
read_y_from_instruction(to_read),
)
}
0x6000..=0x6FFF => {
// 6xkk - LD Vx, byte
// Set Vx = kk.
decoded_instruction = Chip8CpuInstructions::LdVxVy(
read_x_from_instruction(to_read),
read_byte_from_instruction(to_read),
);
}
0x7000..=0x7FFF => {
// ADD Vx, Byte
decoded_instruction = Chip8CpuInstructions::AddVxByte(
read_x_from_instruction(to_read),
read_byte_from_instruction(to_read),
);
}
0x8000..=0x8FFE => {
// 0x8000 Series
let last_nibble = to_read | 0x8000;
match last_nibble {
0x0 => {
// LD Vx, Vy
decoded_instruction = Chip8CpuInstructions::LdVxVy(
read_x_from_instruction(to_read),
read_y_from_instruction(to_read),
);
}
0x1 => {
// OR Vx, Vy
decoded_instruction = Chip8CpuInstructions::OrVxVy(
read_x_from_instruction(to_read),
read_y_from_instruction(to_read),
);
}
0x2 => {
// AND Vx, Vy
decoded_instruction = Chip8CpuInstructions::AndVxVy(
read_x_from_instruction(to_read),
read_y_from_instruction(to_read),
);
}
0x3 => {
// XOR Vx, Vy
decoded_instruction = Chip8CpuInstructions::XorVxVy(
read_x_from_instruction(to_read),
read_y_from_instruction(to_read),
);
}
0x4 => {
// AND Vx, Vy
decoded_instruction = Chip8CpuInstructions::AndVxVy(
read_x_from_instruction(to_read),
read_y_from_instruction(to_read),
);
}
0x5 => {
// SUB Vx, Vy
decoded_instruction = Chip8CpuInstructions::SubnVxVy(
read_x_from_instruction(to_read),
read_y_from_instruction(to_read),
);
}
0x6 => {
// SHR Vx, {, Vy }
decoded_instruction = Chip8CpuInstructions::ShrVxVy(
read_x_from_instruction(to_read),
read_y_from_instruction(to_read),
);
}
0x7 => {
// SUBN Vx, Vy
decoded_instruction = Chip8CpuInstructions::SubnVxVy(
read_x_from_instruction(to_read),
read_y_from_instruction(to_read),
);
}
0xE => {
// SHL Vx, {, Vy}
decoded_instruction = Chip8CpuInstructions::ShlVxVy(
read_x_from_instruction(to_read),
read_y_from_instruction(to_read),
);
}
_ => {
panic!("UNABLE TO DECODE 0x8000 SERIES INSTRUCTION");
}
}
}
0x9000..=0x9FF0 => {
// SNE Vx, Vy
decoded_instruction = Chip8CpuInstructions::SneVxVy(
read_x_from_instruction(to_read),
read_y_from_instruction(to_read),
);
}
0xA000..=0xAFFF => {
// LD I, Addr
decoded_instruction =
Chip8CpuInstructions::LdIAddr(read_addr_from_instruction(to_read));
}
0xB000..=0xBFFF => {
// JP V0, Addr
}
0xC000..=0xCFFF => {
// RND Vx, byte
}
0xD000..0xDFFF => {
// DRAW Vx, Vy, nibble
}
0xE09E..=0xEFA1 => {}
_ => {
panic!("UNABLE TO DECODE INSTRUCTION")
}
}
return decoded_instruction;
}
impl Chip8CpuState {
fn execute(&mut self, event: Chip8CpuInstructions) {
match (self.state.clone(), event) {
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SysAddr(new_address)) => {
self.cpu.pc = new_address;
},
_ => ()
}
} }
} }
#[derive(Clone)]
struct Chip8System {
pub system_memory: [u8; 2048],
pub rom: [u8; 512],
pub sound_timer: u8,
pub system_timer: u8,
}
impl Default for Chip8System {
fn default() -> Self {
// init by loading the fonts...
let mut working_system_memory: [u8; CHIP8_MEMORY_SIZE as usize] =
[0; CHIP8_MEMORY_SIZE as usize];
// Load fonts to memory
working_system_memory = load_fonts_into_memory(working_system_memory);
Self {
system_memory: working_system_memory,
rom: [0; CHIP8_ROM_SIZE],
sound_timer: 0,
system_timer: 0,
}
}
}
impl Chip8System {
fn add_fonts_to_memory(
to_add_fonts_to: [u8; CHIP8_MEMORY_SIZE as usize],
) -> [u8; CHIP8_MEMORY_SIZE as usize] {
panic!("DONT USE THIS USE \"load_fonts_into_memory\" INSTEAD");
}
pub fn new() -> Self {
Chip8System {
system_memory: [0; CHIP8_MEMORY_SIZE as usize],
rom: [0; CHIP8_ROM_SIZE as usize],
sound_timer: 0,
system_timer: 0,
}
}
}
#[derive(Clone)] #[derive(Clone)]
struct AppState { struct AppState {
menu_items: Vec<String>, pub menu_items: Vec<String>,
selected_menu_item: i32, pub selected_menu_item: i32,
system: Chip8CpuData, pub system: Chip8Computer,
} }
impl Default for AppState { impl Default for AppState {
@ -424,7 +67,7 @@ impl Default for AppState {
"Item 3".into(), "Item 3".into(),
], ],
selected_menu_item: 0, selected_menu_item: 0,
system: Chip8CpuData { system: Chip8Computer {
..Default::default() ..Default::default()
}, },
} }
@ -432,21 +75,22 @@ impl Default for AppState {
} }
fn main() -> Result<()> { fn main() -> Result<()> {
// stdout().execute(EnterAlternateScreen)?; stdout().execute(EnterAlternateScreen)?;
// enable_raw_mode()?; enable_raw_mode()?;
// let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?; let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
// terminal.clear()?; terminal.clear()?;
let app = AppState::default(); let app = AppState::default();
loop { loop {
// Draw Ui... // Draw Ui...
terminal.draw(|frame| {
// terminal.draw(|frame| { frame.render_widget(app.system.memory, frame.area());
// frame.render_widget(app.control_keyboard, area);
//render_cpu_state(app.system.clone(), frame);
// render_video_state(app.system.video_memory, frame);
// render_menu_list(app.clone(), frame); // render_menu_list(app.clone(), frame);
// render_cpu_state(app.system.clone(), frame); })?;
// })?;
// ...handle Events. // ...handle Events.
if event::poll(std::time::Duration::from_millis(16))? { if event::poll(std::time::Duration::from_millis(16))? {
if let event::Event::Key(key) = event::read()? { if let event::Event::Key(key) = event::read()? {
@ -458,13 +102,11 @@ fn main() -> Result<()> {
KeyCode::F(12) => { KeyCode::F(12) => {
println!("Resetting CPU"); println!("Resetting CPU");
} }
KeyCode::Char('q') => { break; }
_ => (), _ => (),
}, },
_ => (), _ => (),
} }
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
break;
}
} }
} }
} }
@ -476,13 +118,19 @@ fn main() -> Result<()> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use emmaemu::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};
use crate::*; use crate::*;
#[test] #[test]
fn blank_screen_renders_to_text() { fn blank_screen_renders_to_text() {
let test_video = Chip8Video::default();
let blank_data: [bool; CHIP8_VIDEO_MEMORY] = [false; CHIP8_VIDEO_MEMORY]; let blank_data: [bool; CHIP8_VIDEO_MEMORY] = [false; CHIP8_VIDEO_MEMORY];
let blank_screen = (" ".repeat(CHIP8_VIDEO_WIDTH.try_into().unwrap()) + "\n") let blank_screen = (" ".repeat(CHIP8_VIDEO_WIDTH.try_into().unwrap()) + "\n")
.repeat(CHIP8_VIDEO_HEIGHT.try_into().unwrap()); .repeat(CHIP8_VIDEO_HEIGHT.try_into().unwrap());
assert_eq!(display_data_to_text_render(blank_data), blank_screen);
assert_eq!(Chip8Video::new(blank_data).format_as_string(), blank_screen);
} }
#[test] #[test]
@ -490,7 +138,7 @@ mod test {
let filled_data: [bool; CHIP8_VIDEO_MEMORY] = [true; CHIP8_VIDEO_MEMORY]; let filled_data: [bool; CHIP8_VIDEO_MEMORY] = [true; CHIP8_VIDEO_MEMORY];
let filled_screen = ("*".repeat(CHIP8_VIDEO_WIDTH.try_into().unwrap()) + "\n") let filled_screen = ("*".repeat(CHIP8_VIDEO_WIDTH.try_into().unwrap()) + "\n")
.repeat(CHIP8_VIDEO_HEIGHT.try_into().unwrap()); .repeat(CHIP8_VIDEO_HEIGHT.try_into().unwrap());
assert_eq!(display_data_to_text_render(filled_data), filled_screen); assert_eq!(Chip8Video::new(filled_data).format_as_string(), filled_screen);
} }
#[test] #[test]
@ -511,6 +159,6 @@ mod test {
} }
} }
assert_eq!(display_data_to_text_render(grid_data), expected_data); assert_eq!(Chip8Video::new(grid_data).format_as_string(), expected_data);
} }
} }

35
emma/src/bin/emmagui.rs Normal file
View File

@ -0,0 +1,35 @@
use emmaemu::{
chip8::computer::Chip8Computer,
constants::{CHIP8_MEMORY_SIZE, CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH},
};
use imgui::*;
use ratatui::symbols::half_block;
use sys::{ImColor, ImVec2, ImVector_ImU32};
mod support;
fn main() {
let system = Chip8Computer::default();
let mut value = 1;
let choices = ["test test this is 1", "test test this is 2"];
support::simple_init(file!(), move |_, ui| {
ui.window("EmmaGui")
.size([300.0, 110.0], Condition::FirstUseEver)
.build(|| {
system.memory.gui_render(ui);
ui.text_wrapped("Hello world!");
ui.text_wrapped("こんにちは世界!");
if ui.button(choices[value]) {
value += 1;
value %= 2;
}
ui.button("This...is...imgui-rs!");
ui.separator();
let mouse_pos = ui.io().mouse_pos;
ui.text(format!(
"Mouse Position: ({:.1},{:.1})",
mouse_pos[0], mouse_pos[1]
));
});
});
}

View File

@ -1,55 +0,0 @@
use crate::CHIP8_MEMORY_SIZE;
pub mod text_rendering;
pub const CHIP8FONT_0: [u8; 5] = [0xF0, 0x90, 0x90, 0x90, 0xF0];
pub const CHIP8FONT_1: [u8; 5] = [0x20, 0x60, 0x20, 0x20, 0x70];
pub const CHIP8FONT_2: [u8; 5] = [0xF0, 0x10, 0xF0, 0x80, 0xF0];
pub const CHIP8FONT_3: [u8; 5] = [0xF0, 0x10, 0xF0, 0x10, 0xF0];
pub const CHIP8FONT_4: [u8; 5] = [0x90, 0x90, 0xF0, 0x10, 0x10];
pub const CHIP8FONT_5: [u8; 5] = [0xF0, 0x80, 0xF0, 0x10, 0xF0];
pub const CHIP8FONT_6: [u8; 5] = [0xF0, 0x80, 0xF0, 0x90, 0xF0];
pub const CHIP8FONT_7: [u8; 5] = [0xF0, 0x10, 0x20, 0x40, 0x40];
pub const CHIP8FONT_8: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0xF0];
pub const CHIP8FONT_9: [u8; 5] = [0xF0, 0x90, 0xF0, 0x10, 0xF0];
pub const CHIP8FONT_A: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0x90];
pub const CHIP8FONT_B: [u8; 5] = [0xE0, 0x90, 0xE0, 0x90, 0xE0];
pub const CHIP8FONT_C: [u8; 5] = [0xF0, 0x80, 0x80, 0x80, 0xF0];
pub const CHIP8FONT_D: [u8; 5] = [0xE0, 0x90, 0x90, 0x90, 0xE0];
pub const CHIP8FONT_E: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0xF0];
pub const CHIP8FONT_F: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0x80];
pub fn load_fonts_into_memory(to_add_fonts_to: [u8; CHIP8_MEMORY_SIZE as usize])
-> [u8; CHIP8_MEMORY_SIZE as usize] {
let mut memory = to_add_fonts_to.clone();
let all_font_characters = [
CHIP8FONT_0,
CHIP8FONT_1,
CHIP8FONT_2,
CHIP8FONT_3,
CHIP8FONT_4,
CHIP8FONT_5,
CHIP8FONT_6,
CHIP8FONT_7,
CHIP8FONT_8,
CHIP8FONT_9,
CHIP8FONT_A,
CHIP8FONT_B,
CHIP8FONT_C,
CHIP8FONT_D,
CHIP8FONT_E,
CHIP8FONT_F,
];
for (font_index, current_font) in all_font_characters.iter().enumerate() {
for font_mem_offset in 0..=4 {
let real_offset = font_index * 5 + font_mem_offset;
memory[real_offset] = current_font[font_mem_offset];
}
}
memory
}

View File

@ -1,9 +1,9 @@
use ratatui::{style::Stylize, widgets::{List, Paragraph}, Frame}; use ratatui::{style::Stylize, widgets::{List, Paragraph}, Frame};
use crate::{AppState, Chip8CpuData, CHIP8_REGISTER_COUNT}; use crate::{AppState, Chip8Computer, CHIP8_REGISTER_COUNT, CHIP8_VIDEO_MEMORY};
pub fn render_cpu_state(cpu_state: Chip8CpuData, frame: &mut Frame) { pub fn render_cpu_state(cpu_state: Chip8Computer, frame: &mut Frame) {
let mut area = frame.area(); let mut area = frame.area();
let mut current_state: Vec<String> = vec![]; let mut current_state: Vec<String> = vec![];
@ -34,3 +34,7 @@ pub fn render_hello_world(frame: &mut Frame) {
area, area,
); );
} }
pub fn render_video_state(video_memory: [bool; CHIP8_VIDEO_MEMORY], frame: &mut Frame) {
}

View File

@ -0,0 +1,18 @@
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());
}
}

165
emma/src/bin/support/mod.rs Normal file
View File

@ -0,0 +1,165 @@
use glium::glutin::surface::WindowSurface;
use glium::{Display, Surface};
use imgui::{Context, FontConfig, FontGlyphRanges, FontSource, Ui};
use imgui_glium_renderer::Renderer;
use imgui_winit_support::winit::dpi::LogicalSize;
use imgui_winit_support::winit::event::{Event, WindowEvent};
use imgui_winit_support::winit::event_loop::EventLoop;
use imgui_winit_support::winit::window::WindowBuilder;
use imgui_winit_support::{HiDpiMode, WinitPlatform};
use std::path::Path;
use std::time::Instant;
pub mod clipboard;
pub const FONT_SIZE: f32 = 13.0;
#[allow(dead_code)] // annoyingly, RA yells that this is unusued
pub fn simple_init<F: FnMut(&mut bool, &mut Ui) + 'static>(title: &str, run_ui: F) {
init_with_startup(title, |_, _, _| {}, run_ui);
}
pub fn init_with_startup<FInit, FUi>(title: &str, mut startup: FInit, mut run_ui: FUi)
where
FInit: FnMut(&mut Context, &mut Renderer, &Display<WindowSurface>) + 'static,
FUi: FnMut(&mut bool, &mut Ui) + 'static,
{
let mut imgui = create_context();
let title = match Path::new(&title).file_name() {
Some(file_name) => file_name.to_str().unwrap(),
None => title,
};
let event_loop = EventLoop::new().expect("Failed to create EventLoop");
let builder = WindowBuilder::new()
.with_maximized(true)
.with_title(title);
// .with_inner_size(LogicalSize::new(1024, 768));
let (window, display) = glium::backend::glutin::SimpleWindowBuilder::new()
.set_window_builder(builder)
.build(&event_loop);
let mut renderer = Renderer::init(&mut imgui, &display).expect("Failed to initialize renderer");
if let Some(backend) = clipboard::init() {
imgui.set_clipboard_backend(backend);
} else {
eprintln!("Failed to initialize clipboard");
}
let mut platform = WinitPlatform::init(&mut imgui);
{
let dpi_mode = if let Ok(factor) = std::env::var("IMGUI_EXAMPLE_FORCE_DPI_FACTOR") {
// Allow forcing of HiDPI factor for debugging purposes
match factor.parse::<f64>() {
Ok(f) => HiDpiMode::Locked(f),
Err(e) => panic!("Invalid scaling factor: {}", e),
}
} else {
HiDpiMode::Default
};
platform.attach_window(imgui.io_mut(), &window, dpi_mode);
}
let mut last_frame = Instant::now();
startup(&mut imgui, &mut renderer, &display);
event_loop
.run(move |event, window_target| match event {
Event::NewEvents(_) => {
let now = Instant::now();
imgui.io_mut().update_delta_time(now - last_frame);
last_frame = now;
}
Event::AboutToWait => {
platform
.prepare_frame(imgui.io_mut(), &window)
.expect("Failed to prepare frame");
window.request_redraw();
}
Event::WindowEvent {
event: WindowEvent::RedrawRequested,
..
} => {
let ui = imgui.frame();
let mut run = true;
run_ui(&mut run, ui);
if !run {
window_target.exit();
}
let mut target = display.draw();
target.clear_color_srgb(1.0, 1.0, 1.0, 1.0);
platform.prepare_render(ui, &window);
let draw_data = imgui.render();
renderer
.render(&mut target, draw_data)
.expect("Rendering failed");
target.finish().expect("Failed to swap buffers");
}
Event::WindowEvent {
event: WindowEvent::Resized(new_size),
..
} => {
if new_size.width > 0 && new_size.height > 0 {
display.resize((new_size.width, new_size.height));
}
platform.handle_event(imgui.io_mut(), &window, &event);
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => window_target.exit(),
event => {
platform.handle_event(imgui.io_mut(), &window, &event);
}
})
.expect("EventLoop error");
}
/// Creates the imgui context
pub fn create_context() -> imgui::Context {
let mut imgui = Context::create();
// Fixed font size. Note imgui_winit_support uses "logical
// pixels", which are physical pixels scaled by the devices
// scaling factor. Meaning, 13.0 pixels should look the same size
// on two different screens, and thus we do not need to scale this
// value (as the scaling is handled by winit)
imgui.fonts().add_font(&[
FontSource::TtfData {
data: include_bytes!("../../../../resources/Roboto-Regular.ttf"),
size_pixels: FONT_SIZE,
config: Some(FontConfig {
// As imgui-glium-renderer isn't gamma-correct with
// it's font rendering, we apply an arbitrary
// multiplier to make the font a bit "heavier". With
// default imgui-glow-renderer this is unnecessary.
rasterizer_multiply: 1.5,
// Oversampling font helps improve text rendering at
// expense of larger font atlas texture.
oversample_h: 4,
oversample_v: 4,
..FontConfig::default()
}),
},
FontSource::TtfData {
data: include_bytes!("../../../../resources/mplus-1p-regular.ttf"),
size_pixels: FONT_SIZE,
config: Some(FontConfig {
// Oversampling font helps improve text rendering at
// expense of larger font atlas texture.
oversample_h: 4,
oversample_v: 4,
// Range of glyphs to rasterize
glyph_ranges: FontGlyphRanges::japanese(),
..FontConfig::default()
}),
},
]);
imgui.set_ini_filename(None);
imgui
}

329
emma/src/chip8/computer.rs Normal file
View File

@ -0,0 +1,329 @@
use crate::constants::{CHIP8_MEMORY_SIZE, CHIP8_REGISTER_COUNT};
use super::{
cpu_states::Chip8CpuStates, instructions::Chip8CpuInstructions, system_memory::Chip8SystemMemory, video::Chip8Video
};
#[derive(Clone, Copy)]
pub struct Chip8Computer {
pub pc: u16,
pub sp: u8,
pub memory: Chip8SystemMemory,
pub registers: [u8; 16],
pub sound_timer: u8,
pub delay_timer: u8,
pub i_register: u16,
pub video_memory: Chip8Video,
pub state: Chip8CpuStates
}
impl Default for Chip8Computer {
fn default() -> Self {
Self {
pc: 0x0200,
sp: 0x00,
memory: Chip8SystemMemory::default(),
video_memory: Chip8Video::default(),
registers: [0; CHIP8_REGISTER_COUNT as usize],
sound_timer: 0xFF,
delay_timer: 0xFF,
i_register: 0x0000,
state: Chip8CpuStates::WaitingForInstruction
}
}
}
impl Chip8Computer {
pub fn new() -> Self {
Chip8Computer::default()
}
pub fn step_system(mut self) -> Self {
// read the next instruction
let mut working_instruction: u16 = 0b0000000000000000;
let high_byte = (self.memory.clone().peek(self.pc) as u16).rotate_left(8);
let low_byte = self.memory.clone().peek(self.pc + 1) as u16;
working_instruction = high_byte | low_byte;
let decided_instruction =
Chip8Computer::decode_instruction(self.clone(), working_instruction);
match (self.state, decided_instruction) {
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SysAddr(target_address)) => {
self.pc = target_address;
},
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::CLS) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::RET) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::JpAddr(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::CallAddr(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SeVxByte(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SneVxByte(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SeVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxByte(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddVxByte(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::OrVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AndVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::XorVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SubVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::ShrVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SubnVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::ShlVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SneVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdIAddr(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::JpV0Addr(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::RndVxByte(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::DrawVxVyNibble(_, _, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SkpVx(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SnkpVx(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxDt(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxK(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdDtVx(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdStVx(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddIVx(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdFVu(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdBVx(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdIVx(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxI(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::XXXXERRORINSTRUCTION) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SysAddr(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::CLS) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::RET) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::JpAddr(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::CallAddr(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SeVxByte(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SneVxByte(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SeVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdVxByte(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::AddVxByte(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::OrVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::AndVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::XorVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::AddVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SubVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::ShrVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SubnVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::ShlVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SneVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdIAddr(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::JpV0Addr(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::RndVxByte(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::DrawVxVyNibble(_, _, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SkpVx(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SnkpVx(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdVxDt(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdVxK(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdDtVx(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdStVx(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::AddIVx(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdFVu(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdBVx(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdIVx(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdVxI(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::XXXXERRORINSTRUCTION) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SysAddr(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::CLS) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::RET) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::JpAddr(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::CallAddr(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SeVxByte(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SneVxByte(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SeVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdVxByte(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::AddVxByte(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::OrVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::AndVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::XorVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::AddVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SubVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::ShrVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SubnVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::ShlVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SneVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdIAddr(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::JpV0Addr(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::RndVxByte(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::DrawVxVyNibble(_, _, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SkpVx(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SnkpVx(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdVxDt(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdVxK(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdDtVx(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdStVx(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::AddIVx(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdFVu(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdBVx(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdIVx(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdVxI(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::XXXXERRORINSTRUCTION) => todo!(),
}
self.clone()
}
// nnn or addr - A 12-bit value, the lowest 12 bits of the instruction
pub fn read_addr_from_instruction(&self, instruction_to_read_from: u16) -> u16 {
instruction_to_read_from & 0b0000111111111111
}
// n or nibble - A 4-bit value, the lowest 4 bits of the instruction
pub fn read_nibble_from_instruction(&self, instruction_to_read_from: u16) -> u16 {
instruction_to_read_from & 0b0000000000001111
}
// x - A 4-bit value, the lower 4 bits of the high byte of the instruction
pub fn read_x_from_instruction(&self, instruction_to_read_from: u16) -> u16 {
(instruction_to_read_from & 0b0000111100000000).rotate_right(8)
}
// y - A 4-bit value, the upper 4 bits of the low byte of the instruction
pub fn read_y_from_instruction(&self, instruction_to_read_from: u16) -> u16 {
(instruction_to_read_from & 0b0000000011110000).rotate_right(4)
}
// kk or byte - An 8-bit value, the lowest 8 bits of the instruction
pub fn read_byte_from_instruction(&self, instruction_to_read_from: u16) -> u16 {
(instruction_to_read_from & 0b0000000011111111)
}
fn decode_instruction(self, to_read: u16) -> Chip8CpuInstructions {
let mut decoded_instruction = Chip8CpuInstructions::XXXXERRORINSTRUCTION;
let x_param = self.read_x_from_instruction(to_read);
let y_param = self.read_y_from_instruction(to_read);
let addr_param = self.read_addr_from_instruction(to_read);
let byte_param = self.read_byte_from_instruction(to_read);
let nibble_param = self.read_nibble_from_instruction(to_read);
match to_read {
0x00E0 => {
// 00E0 - CLS
// Clear the display.
decoded_instruction = Chip8CpuInstructions::CLS;
}
0x00EE => {
// 00EE - RET
// Return from a subroutine.
decoded_instruction = Chip8CpuInstructions::RET;
}
0x0000..=0x0FFF => {
// 0nnn - SYS addr
// Jump to a machine code routine at nnn.
decoded_instruction = Chip8CpuInstructions::SysAddr(addr_param);
}
0x1000..=0x1FFF => {
// 1nnn - JP addr
// Jump to location nnn.
decoded_instruction = Chip8CpuInstructions::JpAddr(addr_param);
}
0x2000..=0x2FFF => {
// 2nnn - CALL addr
// Call subroutine at nnn.
decoded_instruction = Chip8CpuInstructions::CallAddr(addr_param);
}
0x3000..=0x3FFF => {
// 3xkk - SE Vx, byte
// Skip next instruction if Vx = kk.
decoded_instruction = Chip8CpuInstructions::SeVxByte(x_param, byte_param)
}
0x4000..=0x4FFF => {
// 4xkk - SNE Vx, byte
// Skip next instruction if Vx != kk.
decoded_instruction = Chip8CpuInstructions::SneVxByte(x_param, byte_param);
}
0x5000..=0x5FF0 => {
// 5xy0 - SE Vx, Vy
// Skip next instruction if Vx = Vy.
decoded_instruction = Chip8CpuInstructions::SeVxVy(x_param, y_param)
}
0x6000..=0x6FFF => {
// 6xkk - LD Vx, byte
// Set Vx = kk.
decoded_instruction = Chip8CpuInstructions::LdVxVy(x_param, byte_param);
}
0x7000..=0x7FFF => {
// ADD Vx, Byte
decoded_instruction = Chip8CpuInstructions::AddVxByte(x_param, byte_param);
}
0x8000..=0x8FFE => {
// 0x8000 Series
let last_nibble = to_read | 0x8000;
match last_nibble {
0x0 => {
// LD Vx, Vy
decoded_instruction = Chip8CpuInstructions::LdVxVy(x_param, y_param);
}
0x1 => {
// OR Vx, Vy
decoded_instruction = Chip8CpuInstructions::OrVxVy(x_param, y_param);
}
0x2 => {
// AND Vx, Vy
decoded_instruction = Chip8CpuInstructions::AndVxVy(x_param, y_param);
}
0x3 => {
// XOR Vx, Vy
decoded_instruction = Chip8CpuInstructions::XorVxVy(x_param, y_param);
}
0x4 => {
// AND Vx, Vy
decoded_instruction = Chip8CpuInstructions::AndVxVy(x_param, y_param);
}
0x5 => {
// SUB Vx, Vy
decoded_instruction = Chip8CpuInstructions::SubnVxVy(x_param, y_param);
}
0x6 => {
// SHR Vx, {, Vy }
decoded_instruction = Chip8CpuInstructions::ShrVxVy(x_param, y_param);
}
0x7 => {
// SUBN Vx, Vy
decoded_instruction = Chip8CpuInstructions::SubnVxVy(x_param, y_param);
}
0xE => {
// SHL Vx, {, Vy}
decoded_instruction = Chip8CpuInstructions::ShlVxVy(x_param, y_param);
}
_ => {
panic!("UNABLE TO DECODE 0x8000 SERIES INSTRUCTION");
}
}
}
0x9000..=0x9FF0 => {
// SNE Vx, Vy
decoded_instruction = Chip8CpuInstructions::SneVxVy(x_param, y_param);
}
0xA000..=0xAFFF => {
// LD I, Addr
decoded_instruction = Chip8CpuInstructions::LdIAddr(addr_param);
}
0xB000..=0xBFFF => {
decoded_instruction = Chip8CpuInstructions::JpV0Addr(addr_param);
// JP V0, Addr
}
0xC000..=0xCFFF => {
// RND Vx, byte
decoded_instruction = Chip8CpuInstructions::RndVxByte(x_param, byte_param);
}
0xD000..0xDFFF => {
// DRAW Vx, Vy, nibble
decoded_instruction =
Chip8CpuInstructions::DrawVxVyNibble(x_param, y_param, nibble_param);
}
0xE09E..=0xEFA1 => {}
_ => {
panic!("UNABLE TO DECODE INSTRUCTION")
}
}
return decoded_instruction;
}
}

View File

@ -0,0 +1,8 @@
#[derive(Clone, Copy, Default)]
pub enum Chip8CpuStates {
#[default]
WaitingForInstruction,
ExecutingInstruction,
Error,
}

View File

View File

@ -0,0 +1,38 @@
pub enum Chip8CpuInstructions {
SysAddr(u16), // 0x0nnn Exit to System Call
CLS, // 0x00E0 Clear Screen
RET, // 0x00EE Return from Subroutine
JpAddr(u16), // 0x1nnn Jump to Address
CallAddr(u16), // 0x2nnn Call Subroutine
SeVxByte(u16, u16), // 0x3xkk Skip next instruction if Vx = kk.
SneVxByte(u16, u16), // 0x4xkk Skip next instruction if Vx != kk
SeVxVy(u16, u16), // 0x5xy0 Skip next instruction if Vx == Vy
LdVxByte(u16, u16), // 0x6xkk Set Vx = kk
AddVxByte(u16, u16), // 0x7xkk Set Vx = Vx + kk
LdVxVy(u16, u16), // 0x8xy0 Set value of Vy in Vx
OrVxVy(u16, u16), // 0x8xy1 Set Vx = Vx OR Vy
AndVxVy(u16, u16), // 0x8xy2 Set Vx = Vx AND Vy
XorVxVy(u16, u16), // 0x8xy3 Set Vx = Vx XOR Vy
AddVxVy(u16, u16), // 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)
SubVxVy(u16, u16), // 0x8xy5 Set Vx = Vx - Vy (Set VF NOT Borrow)
ShrVxVy(u16, u16), // 0x8xy6 Set Vx = Vx SHR 1 (Shift Rotated Right 1)
SubnVxVy(u16, u16), // 0x8xy7 Set Vx = Vy - Vx (Set VF NOT Borrow)
ShlVxVy(u16, u16), // 0x8xyE Shift Left
SneVxVy(u16, u16), // 0x9xy0 Skip next instruction if Vx != Vy
LdIAddr(u16), // 0xAnnn VI = nnn
JpV0Addr(u16), // 0xBnnn Jump to nnn+V0
RndVxByte(u16, u16), // 0xCxkk Vx = random byte AND kk
DrawVxVyNibble(u16, u16, u16), // 0xDxyn Display N byte sprite starting at Vx to Vy
SkpVx(u16), // 0xE09E Skip next instruction if key in Vx pressed
SnkpVx(u16), // 0xE0A1 Skip next instruction if key in Vx NOT pressed
LdVxDt(u16), // 0xFx07 Set Vx = Delay timer
LdVxK(u16), // 0xFx0A Wait for key, put in Vx
LdDtVx(u16), // 0xFx15 Set Delay Timer
LdStVx(u16), // 0xFx18 Set Sount Timer
AddIVx(u16), // 0xFx1E I = I + Vx
LdFVu(u16), // 0xFx29 Set I = Location of sprite for Digit Vx
LdBVx(u16), // 0xFx33 Store BCD of Vx in I, I+1, I+2
LdIVx(u16), // 0xFx55 Store V0 to Vx in memory starting at I
LdVxI(u16), // 0xFx65 Load V0 to Vx in memory starting at I
XXXXERRORINSTRUCTION,
}

37
emma/src/chip8/keypad.rs Normal file
View File

@ -0,0 +1,37 @@
use ratatui::{
style::{Style, Stylize},
widgets::Widget,
};
pub struct Keypad {
keys: [bool; 0x10],
}
impl Keypad {
pub fn push_key(&mut self, key_index: u8) {
self.keys[key_index as usize] = true;
}
pub fn release_key(&mut self, key_index: u8) {
self.keys[key_index as usize] = false;
}
pub fn key_state(&self, key_index: u8) -> bool {
self.keys[key_index as usize]
}
}
impl Widget for Keypad {
fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer)
where
Self: Sized,
{
let mut working_string = String::new();
for i in 0..16 {
working_string += if self.key_state(i) { "X" } else { "O" }
}
let style = Style::new().cyan();
buf.set_string(0, 0, working_string, style);
}
}

View File

View File

@ -0,0 +1,117 @@
use imgui::Ui;
use ratatui::{style::Style, widgets::Widget};
use crate::constants::{CHIP8_MEMORY_SIZE, CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};
pub const CHIP8FONT_0: [u8; 5] = [0xF0, 0x90, 0x90, 0x90, 0xF0];
pub const CHIP8FONT_1: [u8; 5] = [0x20, 0x60, 0x20, 0x20, 0x70];
pub const CHIP8FONT_2: [u8; 5] = [0xF0, 0x10, 0xF0, 0x80, 0xF0];
pub const CHIP8FONT_3: [u8; 5] = [0xF0, 0x10, 0xF0, 0x10, 0xF0];
pub const CHIP8FONT_4: [u8; 5] = [0x90, 0x90, 0xF0, 0x10, 0x10];
pub const CHIP8FONT_5: [u8; 5] = [0xF0, 0x80, 0xF0, 0x10, 0xF0];
pub const CHIP8FONT_6: [u8; 5] = [0xF0, 0x80, 0xF0, 0x90, 0xF0];
pub const CHIP8FONT_7: [u8; 5] = [0xF0, 0x10, 0x20, 0x40, 0x40];
pub const CHIP8FONT_8: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0xF0];
pub const CHIP8FONT_9: [u8; 5] = [0xF0, 0x90, 0xF0, 0x10, 0xF0];
pub const CHIP8FONT_A: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0x90];
pub const CHIP8FONT_B: [u8; 5] = [0xE0, 0x90, 0xE0, 0x90, 0xE0];
pub const CHIP8FONT_C: [u8; 5] = [0xF0, 0x80, 0x80, 0x80, 0xF0];
pub const CHIP8FONT_D: [u8; 5] = [0xE0, 0x90, 0x90, 0x90, 0xE0];
pub const CHIP8FONT_E: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0xF0];
pub const CHIP8FONT_F: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0x80];
#[derive(Clone, Copy)]
pub struct Chip8SystemMemory {
memory: [u8; CHIP8_MEMORY_SIZE as usize],
}
impl Default for Chip8SystemMemory {
fn default() -> Self {
Self {
memory: Chip8SystemMemory::load_to_memory([0x00; CHIP8_MEMORY_SIZE as usize]),
}
}
}
impl Widget for Chip8SystemMemory {
fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer)
where
Self: Sized,
{
// build the text version of the system memory...
// ...and stuff it in the display.
let style = Style::new();
let string = String::new();
buf.set_string(0, 0, string, style)
}
// Display the system memory as a widget
}
const cell_width: i32 = 5i32;
const cell_height: i32 = 5i32;
impl Chip8SystemMemory {
pub fn gui_render(self, ui: &Ui) {
let draw_list = ui.get_foreground_draw_list();
let mut idx = 0;
for row in 0..CHIP8_VIDEO_HEIGHT {
for column in 0..CHIP8_VIDEO_HEIGHT {
let x_offset = (row * cell_width) + column;
let y_offset = (row * cell_width);
let memory_offset = (row * CHIP8_VIDEO_WIDTH) + column;
println!(
"DRAWING IDX: {} {}x{} to {}x{} with {}",
idx,
x_offset,
y_offset,
x_offset + cell_width,
y_offset + cell_height,
self.memory[memory_offset as usize]
);
idx += 1;
}
}
}
pub fn peek(self, address: u16) -> u8 {
self.memory[address as usize]
}
pub fn poke(mut self, address: u16, value: u8) -> Chip8SystemMemory {
self.memory[address as usize] = value;
self
}
pub fn load_to_memory(
to_load_into: [u8; CHIP8_MEMORY_SIZE as usize],
) -> [u8; CHIP8_MEMORY_SIZE as usize] {
let mut working = to_load_into.clone();
let all_font_characters = [
CHIP8FONT_0,
CHIP8FONT_1,
CHIP8FONT_2,
CHIP8FONT_3,
CHIP8FONT_4,
CHIP8FONT_5,
CHIP8FONT_6,
CHIP8FONT_7,
CHIP8FONT_8,
CHIP8FONT_9,
CHIP8FONT_A,
CHIP8FONT_B,
CHIP8FONT_C,
CHIP8FONT_D,
CHIP8FONT_E,
CHIP8FONT_F,
];
for (font_index, current_font) in all_font_characters.iter().enumerate() {
for font_mem_offset in 0..=4 {
let real_offset = font_index * 5 + font_mem_offset;
working[real_offset] = current_font[font_mem_offset];
}
}
println!("__FINISHED LOADING FONTS__");
working
}
}

66
emma/src/chip8/video.rs Normal file
View File

@ -0,0 +1,66 @@
use ratatui::prelude::*;
use ratatui::{layout::Rect, style::Style, widgets::Widget};
use crate::constants::CHIP8_VIDEO_MEMORY;
#[derive(Clone, Copy)]
pub struct Chip8Video {
memory: [bool; CHIP8_VIDEO_MEMORY]
}
impl Chip8Video {
pub fn new(initial_configuration: [bool; CHIP8_VIDEO_MEMORY]) -> Self {
Self {
memory: initial_configuration
}
}
pub fn peek(self, address: u16) -> bool {
self.memory[address as usize]
}
pub fn poke(mut self, address: u16, new_value: bool) -> Self {
self.memory[address as usize] = new_value;
self
}
pub fn format_as_string(self) -> String {
let mut output = String::new();
for row in 0..32 {
let row_offset = row * 32;
for column in 0..64 {
let data_position = row_offset + column;
println!("DP {} {} {} {}", data_position, row, row_offset, column);
output += if self.memory[data_position] {
"*"
} else {
" "
};
}
output += "\n";
}
panic!("{}", output);
output
}
pub fn dump_to_console(self) {
println!("{}", self.format_as_string());
}
}
impl Default for Chip8Video {
fn default() -> Self {
Self { memory: [false; CHIP8_VIDEO_MEMORY as usize] }
}
}
impl Widget for Chip8Video {
fn render(self, area: Rect, buf: &mut ratatui::prelude::Buffer)
where
Self: Sized {
println!("STARTING TO RENDER VIDEO!");
let style = Style::new().on_cyan();
buf.set_string(0, 0, self.format_as_string(), style)
}
}

6
emma/src/constants.rs Normal file
View File

@ -0,0 +1,6 @@
pub const CHIP8_REGISTER_COUNT: i32 = 16;
pub const CHIP8_MEMORY_SIZE: i32 = 2048i32;
pub const CHIP8_VIDEO_WIDTH: i32 = 64i32;
pub const CHIP8_VIDEO_HEIGHT: i32 = 32i32;
pub const CHIP8_VIDEO_MEMORY: usize = (CHIP8_VIDEO_HEIGHT * CHIP8_VIDEO_WIDTH) as usize;
pub const CHIP8_ROM_SIZE: usize = 512;

12
emma/src/lib.rs Normal file
View File

@ -0,0 +1,12 @@
pub mod chip8 {
pub mod video;
pub mod sound_timer;
pub mod delay_timer;
pub mod keypad;
pub mod computer;
pub mod system_memory;
pub mod instructions;
pub mod cpu_states;
}
pub mod constants;