gotta take a break
This commit is contained in:
parent
6dc9ce3721
commit
1a59524f02
1143
Cargo.lock
generated
1143
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -6,3 +6,11 @@ autobenches = true
|
||||
|
||||
[dependencies]
|
||||
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"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
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::{
|
||||
backend::CrosstermBackend,
|
||||
crossterm::{
|
||||
@ -9,82 +9,11 @@ use ratatui::{
|
||||
ExecutableCommand,
|
||||
},
|
||||
layout::{Alignment, Rect},
|
||||
style::Stylize,
|
||||
widgets::{List, Paragraph},
|
||||
style::{Style, Stylize},
|
||||
widgets::{List, Paragraph, Widget},
|
||||
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 {
|
||||
let mut to_return = String::new();
|
||||
@ -107,311 +36,25 @@ fn dump_memory_to_console(data_to_dump: [u8; 2048]) {
|
||||
panic!("DONE DUMPING");
|
||||
}
|
||||
|
||||
fn display_data_to_text_render(data_display: [bool; 2048]) -> 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 data_display[data_position] {
|
||||
"*"
|
||||
} else {
|
||||
" "
|
||||
};
|
||||
}
|
||||
output += "\n";
|
||||
}
|
||||
output
|
||||
struct ControlKeyboard {
|
||||
|
||||
}
|
||||
|
||||
fn render_display(display_data: [bool; 2048]) {
|
||||
println!("{}", display_data_to_text_render(display_data));
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Chip8CpuStates {
|
||||
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;
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
impl Widget for ControlKeyboard {
|
||||
fn render(self, area: Rect, buf: &mut ratatui::prelude::Buffer)
|
||||
where
|
||||
Self: Sized {
|
||||
let style = Style::new();
|
||||
buf.set_string(0, 0, "F1 to cycle foreground - F2 to cycle background", style)
|
||||
}
|
||||
}
|
||||
|
||||
#[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)]
|
||||
struct AppState {
|
||||
menu_items: Vec<String>,
|
||||
selected_menu_item: i32,
|
||||
system: Chip8CpuData,
|
||||
pub menu_items: Vec<String>,
|
||||
pub selected_menu_item: i32,
|
||||
pub system: Chip8Computer,
|
||||
}
|
||||
|
||||
impl Default for AppState {
|
||||
@ -424,7 +67,7 @@ impl Default for AppState {
|
||||
"Item 3".into(),
|
||||
],
|
||||
selected_menu_item: 0,
|
||||
system: Chip8CpuData {
|
||||
system: Chip8Computer {
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
@ -432,21 +75,22 @@ impl Default for AppState {
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// stdout().execute(EnterAlternateScreen)?;
|
||||
// enable_raw_mode()?;
|
||||
// let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
|
||||
// terminal.clear()?;
|
||||
stdout().execute(EnterAlternateScreen)?;
|
||||
enable_raw_mode()?;
|
||||
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
|
||||
terminal.clear()?;
|
||||
|
||||
let app = AppState::default();
|
||||
|
||||
loop {
|
||||
// Draw Ui...
|
||||
|
||||
// terminal.draw(|frame| {
|
||||
// render_menu_list(app.clone(), frame);
|
||||
// render_cpu_state(app.system.clone(), 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);
|
||||
})?;
|
||||
// ...handle Events.
|
||||
if event::poll(std::time::Duration::from_millis(16))? {
|
||||
if let event::Event::Key(key) = event::read()? {
|
||||
@ -458,13 +102,11 @@ fn main() -> Result<()> {
|
||||
KeyCode::F(12) => {
|
||||
println!("Resetting CPU");
|
||||
}
|
||||
KeyCode::Char('q') => { break; }
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -476,21 +118,27 @@ fn main() -> Result<()> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use emmaemu::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};
|
||||
|
||||
use crate::*;
|
||||
#[test]
|
||||
fn blank_screen_renders_to_text() {
|
||||
let test_video = Chip8Video::default();
|
||||
|
||||
|
||||
let blank_data: [bool; CHIP8_VIDEO_MEMORY] = [false; CHIP8_VIDEO_MEMORY];
|
||||
let blank_screen = (" ".repeat(CHIP8_VIDEO_WIDTH.try_into().unwrap()) + "\n")
|
||||
.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]
|
||||
fn filled_screen_renders_to_text() {
|
||||
let filled_data: [bool; CHIP8_VIDEO_MEMORY] = [true; CHIP8_VIDEO_MEMORY];
|
||||
let filled_screen = ("*".repeat(CHIP8_VIDEO_WIDTH.try_into().unwrap()) + "\n")
|
||||
.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]
|
||||
@ -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
35
emma/src/bin/emmagui.rs
Normal 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]
|
||||
));
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
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 current_state: Vec<String> = vec![];
|
||||
@ -34,3 +34,7 @@ pub fn render_hello_world(frame: &mut Frame) {
|
||||
area,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn render_video_state(video_memory: [bool; CHIP8_VIDEO_MEMORY], frame: &mut Frame) {
|
||||
|
||||
}
|
||||
18
emma/src/bin/support/clipboard.rs
Normal file
18
emma/src/bin/support/clipboard.rs
Normal 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
165
emma/src/bin/support/mod.rs
Normal 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
329
emma/src/chip8/computer.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
8
emma/src/chip8/cpu_states.rs
Normal file
8
emma/src/chip8/cpu_states.rs
Normal file
@ -0,0 +1,8 @@
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub enum Chip8CpuStates {
|
||||
#[default]
|
||||
WaitingForInstruction,
|
||||
ExecutingInstruction,
|
||||
Error,
|
||||
}
|
||||
|
||||
0
emma/src/chip8/delay_timer.rs
Normal file
0
emma/src/chip8/delay_timer.rs
Normal file
38
emma/src/chip8/instructions.rs
Normal file
38
emma/src/chip8/instructions.rs
Normal 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
37
emma/src/chip8/keypad.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
0
emma/src/chip8/sound_timer.rs
Normal file
0
emma/src/chip8/sound_timer.rs
Normal file
117
emma/src/chip8/system_memory.rs
Normal file
117
emma/src/chip8/system_memory.rs
Normal 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
66
emma/src/chip8/video.rs
Normal 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
6
emma/src/constants.rs
Normal 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
12
emma/src/lib.rs
Normal 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;
|
||||
Loading…
x
Reference in New Issue
Block a user