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]
|
[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"
|
||||||
|
|||||||
@ -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());
|
||||||
// render_menu_list(app.clone(), frame);
|
// frame.render_widget(app.control_keyboard, area);
|
||||||
// render_cpu_state(app.system.clone(), frame);
|
//render_cpu_state(app.system.clone(), frame);
|
||||||
// })?;
|
// render_video_state(app.system.video_memory, frame);
|
||||||
|
// render_menu_list(app.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,21 +118,27 @@ 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]
|
||||||
fn filled_screen_renders_to_text() {
|
fn filled_screen_renders_to_text() {
|
||||||
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
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 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) {
|
||||||
|
|
||||||
|
}
|
||||||
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