Buggy and sound unimplemented but most opcodes should work

This commit is contained in:
BayThylacine 2025-02-16 17:27:43 +11:00
parent 96ea02db89
commit 5fdde760ce
4 changed files with 460 additions and 23 deletions

2
.gitignore vendored
View File

@ -1,3 +1,3 @@
/target /target
/IBMLogo.ch8 /roms
/.idea /.idea

102
Cargo.lock generated
View File

@ -9,6 +9,7 @@ dependencies = [
"clap", "clap",
"clap_derive", "clap_derive",
"pixels", "pixels",
"rand",
"tao", "tao",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
@ -23,7 +24,7 @@ dependencies = [
"cfg-if", "cfg-if",
"once_cell", "once_cell",
"version_check", "version_check",
"zerocopy", "zerocopy 0.7.35",
] ]
[[package]] [[package]]
@ -189,6 +190,12 @@ version = "1.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.10.0" version = "1.10.0"
@ -717,6 +724,18 @@ dependencies = [
"x11", "x11",
] ]
[[package]]
name = "getrandom"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"libc",
"wasi",
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "gio" name = "gio"
version = "0.18.4" version = "0.18.4"
@ -1526,6 +1545,15 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
[[package]]
name = "ppv-lite86"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy 0.7.35",
]
[[package]] [[package]]
name = "presser" name = "presser"
version = "0.3.1" version = "0.3.1"
@ -1600,6 +1628,37 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
dependencies = [
"rand_chacha",
"rand_core",
"zerocopy 0.8.18",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff"
dependencies = [
"getrandom",
"zerocopy 0.8.18",
]
[[package]] [[package]]
name = "range-alloc" name = "range-alloc"
version = "0.1.4" version = "0.1.4"
@ -2163,6 +2222,15 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
dependencies = [
"wit-bindgen-rt",
]
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.100" version = "0.2.100"
@ -2702,6 +2770,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
"bitflags 2.8.0",
]
[[package]] [[package]]
name = "write16" name = "write16"
version = "1.0.0" version = "1.0.0"
@ -2771,7 +2848,17 @@ version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [ dependencies = [
"zerocopy-derive", "byteorder",
"zerocopy-derive 0.7.35",
]
[[package]]
name = "zerocopy"
version = "0.8.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79386d31a42a4996e3336b0919ddb90f81112af416270cff95b5f5af22b839c2"
dependencies = [
"zerocopy-derive 0.8.18",
] ]
[[package]] [[package]]
@ -2785,6 +2872,17 @@ dependencies = [
"syn 2.0.98", "syn 2.0.98",
] ]
[[package]]
name = "zerocopy-derive"
version = "0.8.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76331675d372f91bf8d17e13afbd5fe639200b73d01f0fc748bb059f9cca2db7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.98",
]
[[package]] [[package]]
name = "zerofrom" name = "zerofrom"
version = "0.1.5" version = "0.1.5"

View File

@ -9,4 +9,5 @@ clap_derive = "4.5.28"
pixels = "0.15.0" pixels = "0.15.0"
tao = "0.31.1" tao = "0.31.1"
tracing = "0.1.41" tracing = "0.1.41"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
rand = "0.9.0"

View File

@ -6,7 +6,7 @@ use std::num::NonZeroU32;
use std::rc::Rc; use std::rc::Rc;
use pixels::{Error, Pixels, SurfaceTexture}; use pixels::{Error, Pixels, SurfaceTexture};
use tao::{ use tao::{
event::{Event, KeyEvent, WindowEvent}, event::{Event, KeyEvent, WindowEvent, ElementState},
event_loop::{ControlFlow, EventLoop}, event_loop::{ControlFlow, EventLoop},
window::WindowBuilder, window::WindowBuilder,
keyboard::KeyCode, keyboard::KeyCode,
@ -15,7 +15,13 @@ use tao::dpi::LogicalSize;
use tracing::{info, error}; use tracing::{info, error};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use std::sync::Arc; use std::sync::Arc;
use rand::prelude::*;
use tao::keyboard::Key;
const MODERNSHIFT: bool = true;
const OLDJUMP: bool = true;
const MODERNLOADSAVE: bool = true;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
struct Args { struct Args {
@ -32,8 +38,12 @@ struct Chip8 {
delay_timer: u8, delay_timer: u8,
ram: [u8; 4096], ram: [u8; 4096],
pc: u16, pc: u16,
stack: [u8; 64], stack: [u16; 64],
stack_pointer: u8, stack_pointer: u8,
keys: [bool; 16],
get_key_pause: bool,
keys_released: [bool; 16],
keys_pressed: [bool; 16],
} }
impl Chip8 { impl Chip8 {
@ -48,6 +58,10 @@ impl Chip8 {
pc: 0x200, pc: 0x200,
stack: [0; 64], stack: [0; 64],
stack_pointer: 0, stack_pointer: 0,
keys: [false; 16],
get_key_pause: false,
keys_released: [false; 16],
keys_pressed: [false; 16],
} }
} }
@ -61,7 +75,13 @@ impl Chip8 {
self self
} }
fn draw(self, frame: &mut [u8], dimensions: (u32, u32)) -> Self { fn draw(&mut self, frame: &mut [u8], dimensions: (u32, u32)) -> Self {
if self.sound_timer != 0 {
self.sound_timer -= 1;
}
if self.delay_timer != 0 {
self.delay_timer -= 1;
}
for(i, pixel) in frame.chunks_exact_mut(4).enumerate() { for(i, pixel) in frame.chunks_exact_mut(4).enumerate() {
let x = i % dimensions.0 as usize; let x = i % dimensions.0 as usize;
let y = i / dimensions.0 as usize; let y = i / dimensions.0 as usize;
@ -78,7 +98,7 @@ impl Chip8 {
pixel.copy_from_slice(&rgba); pixel.copy_from_slice(&rgba);
} }
info!("done"); info!("done");
self *self
} }
fn update(&mut self, instructions: u32) -> Self { fn update(&mut self, instructions: u32) -> Self {
@ -102,13 +122,27 @@ impl Chip8 {
0x00 => { 0x00 => {
self.clear_screen(); self.clear_screen();
}, },
_ => {} 0x0e => {
if self.stack_pointer != 0 {
self.pc = self.stack[self.stack_pointer as usize];
self.stack_pointer -= 1;
} else {
error!("at root subroutine")
}
},
_ => {
error!("Does not support machine code");
}
} }
}, },
_ => {} _ => {
error!("Does not support machine code");
}
} }
}, },
_ => {} _ => {
error!("Does not support machine code");
}
} }
}, },
0x01 => { 0x01 => {
@ -116,16 +150,33 @@ impl Chip8 {
self.pc = position; self.pc = position;
}, },
0x02 => { 0x02 => {
if self.stack_pointer != 63 {
self.stack_pointer += 1;
self.stack[self.stack_pointer as usize] = self.pc;
self.pc = ((opcodes[1] as u16) << 8) | ((opcodes[2] as u16) << 4) | opcodes[3] as u16;
} else {
error!("reached the limit of the stack")
}
}, },
0x03 => { 0x03 => {
if (self.registers[opcodes[1] as usize]) == (((opcodes[2]) << 4) + opcodes[3]) {
self.pc += 2;
}
}, },
0x04 => { 0x04 => {
if (self.registers[opcodes[1] as usize]) != (((opcodes[2]) << 4) + opcodes[3]) {
self.pc += 2;
}
}, },
0x05 => { 0x05 => {
match opcodes[3] {
0x00 => {
if self.registers[opcodes[1] as usize] == self.registers[opcodes[2] as usize] {
self.pc += 2;
}
},
_ => {error!("unknown opcode")}
}
}, },
0x06 => { 0x06 => {
self.registers[opcodes[1] as usize] = opcodes[2] << 4 | opcodes[3]; self.registers[opcodes[1] as usize] = opcodes[2] << 4 | opcodes[3];
@ -135,19 +186,94 @@ impl Chip8 {
self.registers[opcodes[1] as usize] = (((self.registers[opcodes[1] as usize] as u16) + value) & 255) as u8; self.registers[opcodes[1] as usize] = (((self.registers[opcodes[1] as usize] as u16) + value) & 255) as u8;
}, },
0x08 => { 0x08 => {
match opcodes[3] {
0x00 => {
self.registers[opcodes[1] as usize] = self.registers[opcodes[2] as usize];
},
0x01 => {
self.registers[opcodes[1] as usize] = self.registers[opcodes[1] as usize] | self.registers[opcodes[2] as usize];
},
0x02 => {
self.registers[opcodes[1] as usize] = self.registers[opcodes[1] as usize] & self.registers[opcodes[2] as usize];
},
0x03 => {
self.registers[opcodes[1] as usize] = self.registers[opcodes[1] as usize] ^ self.registers[opcodes[2] as usize];
},
0x04 => {
let added_base = (self.registers[opcodes[1] as usize] as u16) + (self.registers[opcodes[2] as usize] as u16);
if added_base > 255 {
self.registers[0x0f] = 1;
} else {
self.registers[0x0f] = 0;
}
self.registers[opcodes[1] as usize] = (added_base & 255) as u8;
},
0x05 => {
info!("{},{}", self.registers[opcodes[1] as usize], self.registers[opcodes[2] as usize]);
let no_underflow = (self.registers[opcodes[1] as usize] >= self.registers[opcodes[2] as usize]);
self.registers[0x0f] = no_underflow as u8;
if no_underflow {
self.registers[opcodes[1] as usize] -= self.registers[opcodes[2] as usize];
} else {
self.registers[opcodes[1] as usize] = 255 - ((self.registers[opcodes[2] as usize] - self.registers[opcodes[1] as usize]) - 1);
}
},
0x06 => {
if MODERNSHIFT {
self.registers[0x0f] = ((self.registers[opcodes[1] as usize] & 1) != 0) as u8;
self.registers[opcodes[1] as usize] = self.registers[opcodes[1] as usize] >> 1;
} else {
self.registers[opcodes[1] as usize] = self.registers[opcodes[2] as usize];
self.registers[0x0f] = ((self.registers[opcodes[1] as usize] & 1) != 0) as u8;
self.registers[opcodes[1] as usize] = self.registers[opcodes[1] as usize] >> 1;
}
},
0x07 => {
let no_underflow = (self.registers[opcodes[2] as usize] >= self.registers[opcodes[1] as usize]);
self.registers[0x0f] = no_underflow as u8;
if no_underflow {
self.registers[opcodes[1] as usize] = self.registers[opcodes[2] as usize] - self.registers[opcodes[1] as usize];
} else {
self.registers[opcodes[1] as usize] = 255 - ((self.registers[opcodes[1] as usize] - self.registers[opcodes[2] as usize]) - 1);
}
},
0x0e => {
if MODERNSHIFT {
self.registers[0x0f] = ((self.registers[opcodes[1] as usize] & 128) != 0) as u8;
self.registers[opcodes[1] as usize] = self.registers[opcodes[1] as usize] << 1;
} else {
self.registers[opcodes[1] as usize] = self.registers[opcodes[2] as usize];
self.registers[0x0f] = ((self.registers[opcodes[1] as usize] & 128) != 0) as u8;
self.registers[opcodes[1] as usize] = self.registers[opcodes[1] as usize] << 1;
}
},
_ => {error!("unknown opcode")}
}
}, },
0x09 => { 0x09 => {
match opcodes[3] {
0x00 => {
if self.registers[opcodes[1] as usize] != self.registers[opcodes[2] as usize] {
self.pc += 2;
}
},
_ => {error!("unknown opcode")}
}
}, },
0x0a => { 0x0a => {
self.index_register = (opcodes[1] as u16) << 8 | (opcodes[2] as u16) << 4 | (opcodes[3] as u16); self.index_register = (opcodes[1] as u16) << 8 | (opcodes[2] as u16) << 4 | (opcodes[3] as u16);
}, },
0x0b => { 0x0b => {
if OLDJUMP {
self.pc = ((opcodes[1] as u16) << 8) + ((opcodes[2] as u16) << 4) + (opcodes[3] as u16) + (self.registers[0] as u16);
} else {
self.pc = ((opcodes[1] as u16) << 8) + ((opcodes[2] as u16) << 4) + (opcodes[3] as u16) + (self.registers[opcodes[1] as usize] as u16)
}
}, },
0x0c => { 0x0c => {
let mut rng = rand::rng();
let random = rng.random::<u8>();
self.registers[opcodes[1] as usize] = random & (opcodes[2] << 4 | opcodes[3]);
}, },
0x0d => { 0x0d => {
let x = self.registers[opcodes[1] as usize] % 64; let x = self.registers[opcodes[1] as usize] % 64;
@ -159,15 +285,158 @@ impl Chip8 {
for i in 0..8 { for i in 0..8 {
let bit = get_bit_u8(spritedata, i); let bit = get_bit_u8(spritedata, i);
checkdata += (bit as u8) << (7 - i); checkdata += (bit as u8) << (7 - i);
self.display[(y + n) as usize] = self.display[(y + n) as usize] ^ ((bit as u64) << (x + i)); if (x+i <= 63) && (y+n <= 31) {
self.display[(y + n) as usize] = self.display[(y + n) as usize] ^ ((bit as u64) << (x + i));
}
} }
//info!("{},{}", spritedata, checkdata); //info!("{},{}", spritedata, checkdata);
} }
}, },
0x0e => { 0x0e => {
match opcodes[2] {
0x09 => {
match opcodes[3] {
0x0e => {
info!("23: {}", self.registers[opcodes[1] as usize]);
if self.registers[opcodes[1] as usize] < 16{
if self.keys[(self.registers[opcodes[1] as usize] & 15) as usize] {
self.pc += 2;
}
} else {
error!("invalid keycode");
}
}
_ => {}
}
},
0x0a => {
match opcodes[3] {
0x01 => {
info!("a1 {}", self.registers[opcodes[1] as usize]);
if self.registers[opcodes[1] as usize] < 16{
if !self.keys[(self.registers[opcodes[1] as usize] & 15) as usize] {
self.pc += 2;
}
} else {
error!("invalid keycode");
}
},
_ => {error!("invalid opcode");}
}
},
_ => {error!("unknown opcode")}
}
}, },
0x0f => { 0x0f => {
match opcodes[2] {
0x00 => {
match opcodes[3] {
0x07 => {
self.registers[opcodes[1] as usize] = self.delay_timer;
},
0x0a => {
let mut keypress = false;
if !self.get_key_pause {
self.get_key_pause = true;
for i in 0..16 {
self.keys_released[i] = !self.keys[i];
self.keys_pressed[i] = false;
}
} else {
for i in 0..16 {
if !keypress {
if self.keys_pressed[i] && !self.keys[i] {
self.registers[opcodes[1] as usize] = i as u8;
self.get_key_pause = false;
keypress = true;
} else if self.keys_released[i] && self.keys[i] {
self.keys_pressed[i] = true;
} else if !self.keys_released[i] && !self.keys[i] {
self.keys_released[i] = true;
}
}
}
}
if !keypress {
self.pc -= 2;
}
},
_ => {
error!("unknown opcode");
}
}
}
0x01 => {
match opcodes[3] {
0x05 => {
self.delay_timer = self.registers[opcodes[1] as usize];
},
0x08 => {
self.sound_timer = self.registers[opcodes[1] as usize];
}
0x0e => {
if self.index_register + self.registers[opcodes[1] as usize] as u16 >= 0x1000 {
self.registers[0x0f] = 1;
}
self.index_register = (self.index_register + self.registers[opcodes[1] as usize] as u16) & 0x0FFF;
},
_ => {error!("unknown opcode");}
}
},
0x02 => {
match opcodes[3] {
0x09 => {
self.index_register = 0x050 + (((self.registers[opcodes[1] as usize] & 0x0F) as u16) * 5);
},
_ => {error!("unknown opcode");}
}
},
0x03 => {
match opcodes[3] {
0x03 => {
self.ram[self.index_register as usize] = self.registers[opcodes[1] as usize] / 100;
self.ram[self.index_register as usize + 1] = (self.registers[opcodes[1] as usize] / 10) % 10;
self.ram[self.index_register as usize + 2] = self.registers[opcodes[1] as usize] % 10;
},
_ => {error!("unknown opcode");}
}
}
0x05 => {
match opcodes[3] {
0x05 => {
if MODERNLOADSAVE {
for i in 0..=opcodes[1] {
self.ram[(self.index_register + i as u16) as usize] = self.registers[i as usize];
}
} else {
for i in 0..=opcodes[1] {
self.ram[self.index_register as usize] = self.registers[i as usize];
self.index_register += i as u16;
}
}
},
_ => {error!("unknown opcode");}
}
},
0x06 => {
match opcodes[3] {
0x05 => {
if MODERNLOADSAVE {
for i in 0..=opcodes[1] {
self.registers[i as usize] = self.ram[(self.index_register + i as u16) as usize];
}
} else {
for i in 0..=opcodes[1] {
self.registers[i as usize] = self.ram[self.index_register as usize];
self.index_register += 1;
}
}
},
_ => {error!("unknown opcode");}
}
},
_ => {error!("unknown opcode")}
}
}, },
_ => { _ => {
@ -179,9 +448,9 @@ impl Chip8 {
*self *self
} }
fn clear_screen(mut self) -> Self { fn clear_screen(&mut self) -> Self {
self.display = [0;32]; self.display = [0;32];
self *self
} }
} }
@ -229,6 +498,67 @@ fn main() {
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
} }
}, },
WindowEvent::KeyboardInput {
event:
KeyEvent {
logical_key,
state,
..
},
..
} => {
match logical_key {
Key::Character("1") => {
set_keystate(&mut chip8.keys, 1, state);
},
Key::Character("2") => {
set_keystate(&mut chip8.keys, 2, state);
},
Key::Character("3") => {
set_keystate(&mut chip8.keys, 3, state);
},
Key::Character("4") => {
set_keystate(&mut chip8.keys, 0x0c, state);
},
Key::Character("q") => {
set_keystate(&mut chip8.keys, 4, state);
},
Key::Character("w") => {
set_keystate(&mut chip8.keys, 5, state);
},
Key::Character("e") => {
set_keystate(&mut chip8.keys, 6, state);
},
Key::Character("r") => {
set_keystate(&mut chip8.keys, 0x0d, state);
},
Key::Character("a") => {
set_keystate(&mut chip8.keys, 7, state);
},
Key::Character("s") => {
set_keystate(&mut chip8.keys, 8, state);
},
Key::Character("d") => {
set_keystate(&mut chip8.keys, 9, state);
},
Key::Character("f") => {
set_keystate(&mut chip8.keys, 0x0e, state);
},
Key::Character("z") => {
set_keystate(&mut chip8.keys, 0x0a, state);
},
Key::Character("x") => {
set_keystate(&mut chip8.keys, 0, state);
},
Key::Character("c") => {
set_keystate(&mut chip8.keys, 0x0b, state);
},
Key::Character("v") => {
set_keystate(&mut chip8.keys, 0x0f, state);
},
_ => {}
}
}
_ => {} _ => {}
}, },
Event::MainEventsCleared => { Event::MainEventsCleared => {
@ -310,4 +640,12 @@ fn get_bit_u8(num: u8, bit: u8) -> bool {
} else { } else {
false false
} }
}
fn set_keystate(keys: &mut [bool; 16], key: u8, state: ElementState) {
if state == ElementState::Pressed {
keys[key as usize] = true;
} else if state == ElementState::Released {
keys[key as usize] = false;
}
} }