Initial commit

This commit is contained in:
2024-07-22 14:07:35 -04:00
commit ceb62d2c53
16 changed files with 3245 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
pub const CHIP8_MEMORY_SIZE: u16 = 0x1000;
pub const CHIP8_MEMORY_SIZE_USIZE: usize = 0x1000;
pub const FONT_0: [u8; 5] = [0xF0, 0x90, 0x90, 0x90, 0xF0];
pub const FONT_1: [u8; 5] = [0x20, 0x60, 0x20, 0x20, 0x70];
pub const FONT_2: [u8; 5] = [0xF0, 0x10, 0xF0, 0x80, 0xF0];
pub const FONT_3: [u8; 5] = [0xF0, 0x10, 0xF0, 0x10, 0xF0];
pub const FONT_4: [u8; 5] = [0x90, 0x90, 0xF0, 0x10, 0x10];
pub const FONT_5: [u8; 5] = [0xF0, 0x80, 0xF0, 0x10, 0xF0];
pub const FONT_6: [u8; 5] = [0xF0, 0x80, 0xF0, 0x90, 0xF0];
pub const FONT_7: [u8; 5] = [0xF0, 0x10, 0x20, 0x40, 0x40];
pub const FONT_8: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0xF0];
pub const FONT_9: [u8; 5] = [0xF0, 0x90, 0xF0, 0x10, 0xF0];
pub const FONT_A: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0x90];
pub const FONT_B: [u8; 5] = [0xE0, 0x90, 0xE0, 0x90, 0xE0];
pub const FONT_C: [u8; 5] = [0xF0, 0x80, 0x80, 0x80, 0xF0];
pub const FONT_D: [u8; 5] = [0xE0, 0x90, 0x90, 0x90, 0xE0];
pub const FONT_E: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0xF0];
pub const FONT_F: [u8; 5] = [0xF0, 0x80, 0xf0, 0x80, 0x80];
+195
View File
@@ -0,0 +1,195 @@
use bitmask::bitmask;
use crate::{chip8_constants::CHIP8_MEMORY_SIZE, parts::Display::Chip8Display};
enum Chip8StartOffset {
STANDARD,
ETI600,
OTHER,
}
struct Chip8Registers {
V: [u8; 16],
I: u16,
DelayTimer: u16,
SoundTimer: u16,
StackPointer: u8,
ProgramCounter: u16,
}
impl Default for Chip8Registers {
fn default() -> Self {
Chip8Registers {
DelayTimer: 60,
V: [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
],
I: 0x00,
SoundTimer: 60,
StackPointer: 255,
ProgramCounter: 0x200,
}
}
}
enum Chip8Instruction {
SYS(u16),
CLS,
RET,
JMP(u16),
CALL(u16),
SNEQ(u8, u16),
SNNE(u8, u8),
SNRE(u8, u8),
STOR(u8, u16),
ADD(u8, u16),
MOV(u8, u8),
OR(u8, u8),
AND(u8, u8),
XOR(u8, u8),
ADC(u8, u8),
SBC(u8, u8),
RSR(u8, u8),
LSR(u8, u8),
SNE(u8, u8),
STO(i16),
JMPI(u16),
RND(u8, u8),
SETD(u8),
SETT(u8),
BCD(u8),
}
enum Chip8Keys {
KEY0,
KEY1,
KEY2,
KEY3,
KEY4,
KEY5,
KEY6,
KEY7,
KEY8,
KEY9,
KEYA,
KEYB,
KEYC,
KEYD,
KEYE,
KEYF,
}
pub fn display_video_memory(system_memory: [u8; CHIP8_MEMORY_SIZE as usize]) {
// Assumes memory addresses from
}
const ZERO: u16 = 0b0000000000000000;
const BOTTOM_BYTE: u16 = 0b0000000011111111;
const TOP_BYTE: u16 = 0b1111111100000000;
const NIBBLE0: u16 = 0b1111000000000000;
const NIBBLE1: u16 = 0b0000111100000000;
const NIBBLE2: u16 = 0b0000000011110000;
const NIBBLE3: u16 = 0b0000000000001111;
const NIBBLE0_BALANCE: u16 = 0x0FFF;
const PMSK_0000: i16 = 0x0000;
const PMSK_00X0: i16 = 0x00F0;
const PMSK_0X00: i16 = 0x0F00;
const PMSK_0XXX: i16 = 0x0FFF;
// AND against the possible operand to find which one we have
bitmask! {
mask Chip8Instructions: u16 where flags Chip8InstructionFlags {
SYS = 0x00,
CLR = 0b0000000011100000,
RTS = 0b0000000011101110,
JUMP = 0b0001000000000000,
CALL = 0b0010000000000000,
SKE = 0b0011000000000000,
SKNE = 0b0100000000000000,
SKRE = 0b0101000000000000,
LOAD = 0b0110000000000000,
ADD = 0b0111000000000000,
MOVE = 0b1000000000000000,
OR = 0b1000000000000001,
AND = 0b1000000000000010,
XOR = 0b1000000000000011,
ADDR = 0b1000000000000100,
SUB = 0b1000000000000101,
SHR = 0b1000000000000110,
SHL = 0b1000000000001110,
SKRNE = 0b1001000000000000,
LOADI = 0b1010000000000000,
JUMPI = 0b1011000000000000,
RAND = 0b1100000000000000,
DRAW = 0b1101000000000000,
SKPR = 0b1110000000001110,
SKUP = 0b1111000010100001,
MOVED = 0b1111000000000111,
KEYD = 0b1111000000001010,
LOADD = 0b1111000000010101,
LOADS = 0b1111000000011000,
ADDI = 0b1111000000011110,
LDSPR = 0b1111000000101001,
BCD = 0b1111000000110011,
STOR = 0b1111000001010101,
MEMCPY = 0b1111000001100101
}
}
enum Chip8Asm {
SYS(u16),
CLR(),
RTS(),
JUMP(u16),
CALL(u16),
SKE(u8, u8),
SKNE(u8, u8),
SKRE(u8, u8),
LOAD(u8, u8),
ADD(u8, u8),
MOVE(u8, u8),
OR(u8, u8),
AND(u8, u8),
XOR(u8, u8),
ADDR(u8, u8),
SUB(u8, u8),
SHR(u8, u8),
SHL(u8, u8),
SKRNE(u8, u8),
LOADI(u16),
JUMPI(u16),
RAND(u8, u8),
DRAW(u8, u8, u8),
SKPR(u8),
SKUP(u8),
MOVED(u8),
KEYD(u8),
LOADD(u8),
LOADS(u8),
ADDI(u8),
LDSPR(u8),
BCD(u8),
STOR(u8),
READ(u8),
}
struct Chip8Cpu {}
struct Chip8System {
registers: Chip8Registers,
screen: Chip8Display,
}
impl Chip8System {
pub fn tick(self: Self) {
println!(" Ticking Chip8System");
// Fetch...
// ...Decode...
// ...Execute
// self.registers.tick();
self.screen.tick();
}
}
+8
View File
@@ -0,0 +1,8 @@
pub mod chip8_mnemonics;
pub mod chip8_constants;
pub mod parts {
pub mod CPU;
pub mod Display;
pub mod Keyboard;
}
+449
View File
@@ -0,0 +1,449 @@
struct Chip8Registers {
}
impl Chip8Registers {
pub fn tick(self: &Self) {
println!("Ticking Registers");
}
}
pub struct Chip8InstructionParameter {
mask: u16
}
struct Chip8Instruction {
id: String,
mask: u16,
pattern: u16,
arguments: Vec<Chip8InstructionParameter>,
description: Box<str>
}
pub fn fill_chip8_instructions() {
let full_instruction_set = vec![
Chip8Instruction {
id: "SYS".to_string(),
mask: 0x00,
pattern: 0x00,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0FFF
}
],
description: "Execute method at address referenced by parameter".into()
},
Chip8Instruction {
id: "CLR".to_string(),
mask: 0x00E0,
pattern: 0x00E0,
arguments: vec![],
description: "Clear the Screen".into()
},
Chip8Instruction {
id: "RET".to_string(),
mask: 0x00EE,
pattern: 0x00EE,
arguments: vec![],
description: "Return from Subroutine".into()
},
Chip8Instruction {
id: "JMP".to_string(),
mask: 0x1FFF,
pattern: 0x1000,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0fff
}
],
description: "Jump to specified location".into()
},
Chip8Instruction {
id: "SEQ".to_string(),
mask: 0x3FFF,
pattern: 0x3000,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00ff
}
],
description: "Skip next instruction if Register does equal parameter".into()
},
Chip8Instruction {
id: "SNE".to_string(),
mask: 0x4FFF,
pattern: 0x4000,
arguments: vec! [
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00ff
}
],
description: "Skip next instruction if Register doesn't equal parameter".into()
},
Chip8Instruction {
id: "SREQ".to_string(),
mask: 0x5FF0,
pattern: 0x5000,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00f0
}
],
description: "TBD".into()
},
Chip8Instruction {
id: "STO".to_string(),
mask: 0x6FFF,
pattern: 0x6000,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00ff
}
],
description: "Store value into register".into()
},
Chip8Instruction {
id: "ADD".to_string(),
mask: 0x7FFF,
pattern: 0x7000,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00ff
}
],
description: "TBD".into()
},
Chip8Instruction {
id: "CPY".to_string(),
mask: 0x8FF0,
pattern: 0x8000,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00f0
}
],
description: "TBD".into()
},
Chip8Instruction {
id: "OR".to_string(),
mask: 0x8FF1,
pattern: 0x8001,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00f0
}
],
description: "Logical OR of registers placing result in first register".into()
},
Chip8Instruction {
id: "AND".to_string(),
mask: 0x8FF2,
pattern: 0x8002,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00f0
}
],
description: "Logical AND of registers placing result in first register".into()
},
Chip8Instruction {
id: "XOR".to_string(),
mask: 0x8FF3,
pattern: 0x8003,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00f0
}
],
description: "Logical XOR of registers placing result in first register".into()
},
Chip8Instruction {
id: "ADC".to_string(),
mask: 0x8FF4,
pattern: 0x8004,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00f0
}
],
description: "Add 2 Registers with carry flag".into()
},
Chip8Instruction {
id: "SUBC".to_string(),
mask: 0x8FF5,
pattern: 0x8005,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00f0
}
],
description: "Subtract 2 Registers with carry flag".into()
},
Chip8Instruction {
id: "RSR".to_string(),
mask: 0x8FF6,
pattern: 0x8006,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00f0
}
],
description: "Register Shifted Right".into()
},
Chip8Instruction {
id: "SUBC".to_string(),
mask: 0x8FF7,
pattern: 0x8007,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00f0
}
],
description: "Subtract 2 Regiters with Carry".into()
},
Chip8Instruction {
id: "RSL".to_string(),
mask: 0x8FFE,
pattern: 0x800E,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00f0
}
],
description: "Register Shifted Left".into()
},
Chip8Instruction {
id: "SRNE".to_string(),
mask: 0x9FF0,
pattern: 0x9000,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00f0
}
],
description: "Skip next instruction if registers not equal".into()
},
Chip8Instruction {
id: "LDI".to_string(),
mask: 0xAFFF,
pattern: 0xA000,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0fff
}
],
description: "Load Data from Memory Address".into()
},
Chip8Instruction {
id: "JMPI".to_string(),
mask: 0xBFFF,
pattern: 0xB000,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0fff
}
],
description: "Jump to Memory Address".into()
},
Chip8Instruction {
id: "RNG".to_string(),
mask: 0xCFFF,
pattern: 0xC000,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00ff
}
],
description: "Random number Generator".into()
},
Chip8Instruction {
id: "SPR".to_string(),
mask: 0xDFFF,
pattern: 0xD000,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
},
Chip8InstructionParameter {
mask: 0x00f0
},
Chip8InstructionParameter {
mask: 0x000f
}
],
description: "TBD".into()
},
Chip8Instruction {
id: "JNK".to_string(),
mask: 0xEF9E,
pattern: 0xE09E,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
}
],
description: "Jump over next instruction if Key Pressed".into()
},
Chip8Instruction {
id: "JKP".to_string(),
mask: 0xEFA1,
pattern: 0xE0A1,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
}
],
description: "Jump over next instruction if Key Not Pressed".into()
},
Chip8Instruction {
id: "SDT".to_string(),
mask: 0xFF07,
pattern: 0xF007,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0F00
}
],
description: "TBD".into()
},
Chip8Instruction {
id: "WKP".to_string(),
mask: 0xFF0A,
pattern: 0xF00A,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
}
],
description: "TBD".into()
},
Chip8Instruction {
id: "LDT".to_string(),
mask: 0xFF15,
pattern: 0xF015,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
}
],
description: "TBD".into()
},
Chip8Instruction {
id: "LST".to_string(),
mask: 0xFF18,
pattern: 0xF018,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
}
],
description: "TBD".into()
},
Chip8Instruction {
id: "ADDI".to_string(),
mask: 0xFF1E,
pattern: 0xF01E,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
}
],
description: "TBD".into()
},
Chip8Instruction {
id: "SETI".to_string(),
mask: 0xFF29,
pattern: 0xF029,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
}
],
description: "TBD".into()
},
Chip8Instruction{
id: "BCD".to_string(),
mask: 0xFF33,
pattern: 0xF033,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
}
],
description: "Binary Coded Decimal of register".into()
},
Chip8Instruction{
id: "MSTO".to_string(),
mask: 0xFF55,
pattern: 0xF055,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
}
],
description: "TBD".into()
},
Chip8Instruction {
id: "MLOAD".to_string(),
mask: 0xFF65,
pattern: 0xF065,
arguments: vec![
Chip8InstructionParameter {
mask: 0x0f00
}
],
description: "TBD".into()
}
];
}
+28
View File
@@ -0,0 +1,28 @@
pub struct Chip8Display {
// 64w x 32h
pub memory: [bool; 512],
}
impl Chip8Display {
pub fn tick(self: &Self) {
println!("Ticking the display");
}
pub fn render_chip8_display(to_render: Chip8Display) {
// 32 rows...
for index_row in 0..=32 {
// ...64 columns
for index_col in 0..=64 {
let offset = (index_row * 64) + index_col;
if (to_render.memory[offset]) {
print!("*")
} else {
print!(" ")
};
}
println!("");
}
}
}
+82
View File
@@ -0,0 +1,82 @@
#[derive(Clone)]
struct Chip8Keyboard {
keys_state: [bool; 16]
}
impl Default for Chip8Keyboard {
fn default() -> Self {
Self { keys_state: [
false, false, false, false,
false, false, false, false,
false, false, false, false,
false, false, false, false
] }
}
}
impl Chip8Keyboard {
fn display_keyboard_key(self: Self, key_id: u8) {
let filler = if self.is_pressed(key_id) { "*" } else { " " };
print!("{}{}{}", filler, key_id, filler)
}
fn display_keyboard_horizontal_line(self: Self, row_values: [u8; 4]) {
print!("|");
for current in row_values {
self.clone().display_keyboard_key(current);
print!("|");
}
println!();
}
fn display_keyboard_seperator_line(self: Self) {
println!("+---+---+---+---+");
}
pub fn display_keyboard(self: Self) {
self.clone().display_keyboard_seperator_line();
self.clone().display_keyboard_horizontal_line(
[1,2,3,0xC]
);
println!("+---+---+---+---+");
println!("| 4 | 5 | 6 | D |");
println!("+---+---+---+---+");
println!("| 7 | 8 | 9 | E |");
println!("+---+---+---+---+");
println!("| A | 0 | B | F |");
println!("+---+---+---+---+");
}
pub fn press_key(self: &mut Self, key_id: u8) {
self.keys_state[key_id as usize] = true;
}
pub fn release_key(self: &mut Self, key_id: u8) {
self.keys_state[key_id as usize] = false;
}
pub fn is_pressed(self: Self, key_id: u8) -> bool {
self.keys_state[key_id as usize]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn smoke_test() {
assert_eq!(true, true);
}
#[test]
fn basic_keyboard_display_works() {
let basic_keyboard = Chip8Keyboard::default();
}
}