Compare commits
10 Commits
e7d3cd5f19
...
ec9b7c4825
Author | SHA1 | Date | |
---|---|---|---|
ec9b7c4825 | |||
5a457da578 | |||
2bb85f7a0b | |||
ced440a302 | |||
9eea36efa5 | |||
867774e367 | |||
df75a881ac | |||
d6e813b927 | |||
9b39560d43 | |||
f56426a9ec |
4
.github/workflows/rust.yml
vendored
4
.github/workflows/rust.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4.3.1
|
uses: actions/upload-artifact@v4.3.1
|
||||||
if: ${{ github.event_name == 'push' }}
|
if: ${{ github.event_name == 'push' }}
|
||||||
with:
|
with:
|
||||||
name: byte-pusher-emu
|
name: porcel8
|
||||||
path: target/release/byte-pusher-emu
|
path: target/release/porcel8
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
overwrite: false
|
overwrite: false
|
||||||
|
110
Cargo.lock
generated
110
Cargo.lock
generated
@@ -18,9 +18,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.6"
|
version = "1.0.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-parse"
|
name = "anstyle-parse"
|
||||||
@@ -56,6 +56,12 @@ version = "1.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -70,9 +76,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.1"
|
version = "4.5.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
|
checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -80,9 +86,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.1"
|
version = "4.5.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
|
checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -92,9 +98,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.0"
|
version = "4.5.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
|
checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -104,9 +110,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.7.0"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
@@ -135,20 +141,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.12"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
|
"windows-targets 0.52.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
@@ -164,15 +171,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.153"
|
version = "0.2.169"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.21"
|
version = "0.4.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-conv"
|
name = "num-conv"
|
||||||
@@ -191,7 +198,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "porcel8"
|
name = "porcel8"
|
||||||
version = "0.2.1"
|
version = "0.3.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -233,20 +240,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
|
||||||
"rand_chacha",
|
"rand_chacha",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_chacha"
|
name = "rand_chacha"
|
||||||
version = "0.3.1"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"ppv-lite86",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
@@ -254,20 +261,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.6.4"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
checksum = "a88e0da7a2c97baa202165137c158d0a2e824ac465d13d81046727b34cb247d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sdl2"
|
name = "sdl2"
|
||||||
version = "0.36.0"
|
version = "0.37.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8356b2697d1ead5a34f40bcc3c5d3620205fe0c7be0a14656223bfeec0258891"
|
checksum = "3b498da7d14d1ad6c839729bd4ad6fc11d90a57583605f3b4df2cd709a9cd380"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 1.3.2",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
"sdl2-sys",
|
"sdl2-sys",
|
||||||
@@ -275,9 +283,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sdl2-sys"
|
name = "sdl2-sys"
|
||||||
version = "0.36.0"
|
version = "0.37.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26bcacfdd45d539fb5785049feb0038a63931aa896c7763a2a12e125ec58bd29"
|
checksum = "951deab27af08ed9c6068b7b0d05a93c91f0a8eb16b6b816a5e73452a43521d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -306,9 +314,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simple_logger"
|
name = "simple_logger"
|
||||||
version = "4.3.3"
|
version = "5.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1"
|
checksum = "e8c5dfa5e08767553704aa0ffd9d9794d527103c736aba9854773851fd7497eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"colored",
|
"colored",
|
||||||
"log",
|
"log",
|
||||||
@@ -386,9 +394,12 @@ checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.13.3+wasi-0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen-rt",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
@@ -521,3 +532,32 @@ name = "windows_x86_64_msvc"
|
|||||||
version = "0.52.4"
|
version = "0.52.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
||||||
|
|
||||||
|
[[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]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dde3bb8c68a8f3f1ed4ac9221aad6b10cece3e60a8e2ea54a6a2dec806d0084c"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eea57037071898bf96a6da35fd626f4f27e9cee3ead2a6c703cf09d472b2e700"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
12
Cargo.toml
12
Cargo.toml
@@ -1,17 +1,17 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "porcel8"
|
name = "porcel8"
|
||||||
version = "0.2.1"
|
version = "0.3.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
clap = { version = "4.5.30", features = ["derive"] }
|
||||||
log = "0.4.20"
|
log = "0.4.25"
|
||||||
simple_logger = "4.3"
|
simple_logger = "5.0.0"
|
||||||
byteorder = "1.5"
|
byteorder = "1.5"
|
||||||
sdl2 = "0.36.0"
|
sdl2 = "0.37.0"
|
||||||
rand = "0.8.5"
|
rand = "0.9.0"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip = true
|
strip = true
|
||||||
|
@@ -13,6 +13,7 @@ pub struct Porcel8ProgramArgs {
|
|||||||
help = "Emulate new behaviour of instructions (As seen in Chip-48 and SuperChip8)",
|
help = "Emulate new behaviour of instructions (As seen in Chip-48 and SuperChip8)",
|
||||||
default_value_t = true
|
default_value_t = true
|
||||||
)]
|
)]
|
||||||
|
/// Use updated CHIP-8 behaviours.
|
||||||
pub new_chip8_behaviour: bool,
|
pub new_chip8_behaviour: bool,
|
||||||
#[arg(
|
#[arg(
|
||||||
short='i',
|
short='i',
|
||||||
@@ -20,5 +21,12 @@ pub struct Porcel8ProgramArgs {
|
|||||||
help = "Halt on invalid instruction",
|
help = "Halt on invalid instruction",
|
||||||
default_value_t = false
|
default_value_t = false
|
||||||
)]
|
)]
|
||||||
|
/// Halt on finding invalid instruction
|
||||||
pub halt_on_invalid: bool,
|
pub halt_on_invalid: bool,
|
||||||
|
/// Enable Instruction Throttling
|
||||||
|
#[arg(short='t', default_value_t=true)]
|
||||||
|
pub do_instruction_throttling: bool,
|
||||||
|
/// Target Instructions per second, if throttling is enabled
|
||||||
|
#[arg(short='r',long,default_value_t=750u64)]
|
||||||
|
pub ips_throttling_rate: u64
|
||||||
}
|
}
|
||||||
|
@@ -1,21 +1,22 @@
|
|||||||
use crate::{device::instruction::Instruction, util::EmulatorError};
|
use crate::{device::instruction::Instruction, util::EmulatorError};
|
||||||
use crate::device::keyboard::Keyboard;
|
use crate::device::keyboard::Keyboard;
|
||||||
use crate::device::timer::DeviceTimerManager;
|
use crate::device::timer::DeviceTimerManager;
|
||||||
use crate::util::EmulatorResult;
|
use crate::util::{DeviceConfig, EmulatorResult};
|
||||||
use rand::random;
|
use rand::random;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use super::registers::RegisterFile;
|
||||||
|
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
pub registers: RegisterFile,
|
pub registers: RegisterFile,
|
||||||
pub memory: Box<[u8; Self::DEVICE_MEMORY_SIZE]>,
|
pub memory: Box<[u8; Self::DEVICE_MEMORY_SIZE]>,
|
||||||
pub timer: DeviceTimerManager,
|
pub timer: DeviceTimerManager,
|
||||||
pub stack: Vec<u16>,
|
pub stack: Vec<u16>,
|
||||||
pub frame_buffer: Arc<Mutex<Box<[bool; 64 * 32]>>>,
|
pub frame_buffer: Arc<Mutex<Box<[bool; 64 * 32]>>>,
|
||||||
pub new_chip8_mode: bool,
|
|
||||||
pub halt_on_invalid: bool,
|
|
||||||
pub device_keyboard: Keyboard,
|
pub device_keyboard: Keyboard,
|
||||||
|
pub device_config: DeviceConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
@@ -27,8 +28,7 @@ impl Device {
|
|||||||
timer: DeviceTimerManager,
|
timer: DeviceTimerManager,
|
||||||
fb: Arc<Mutex<Box<[bool; Device::FRAME_BUFFER_SIZE]>>>,
|
fb: Arc<Mutex<Box<[bool; Device::FRAME_BUFFER_SIZE]>>>,
|
||||||
device_keyboard: Keyboard,
|
device_keyboard: Keyboard,
|
||||||
new_chip8_mode: bool,
|
device_config: DeviceConfig
|
||||||
halt_on_invalid: bool
|
|
||||||
) -> Device {
|
) -> Device {
|
||||||
let memory = vec![0u8; Self::DEVICE_MEMORY_SIZE]
|
let memory = vec![0u8; Self::DEVICE_MEMORY_SIZE]
|
||||||
.into_boxed_slice()
|
.into_boxed_slice()
|
||||||
@@ -36,43 +36,37 @@ impl Device {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
log::trace!("Successfully initiated device memory");
|
log::trace!("Successfully initiated device memory");
|
||||||
Device {
|
Device {
|
||||||
registers: RegisterFile::new(),
|
registers: RegisterFile::default(),
|
||||||
memory,
|
memory,
|
||||||
frame_buffer: fb,
|
frame_buffer: fb,
|
||||||
stack: Vec::with_capacity(16),
|
stack: Vec::with_capacity(16),
|
||||||
halt_on_invalid,
|
|
||||||
timer,
|
timer,
|
||||||
new_chip8_mode,
|
|
||||||
device_keyboard,
|
device_keyboard,
|
||||||
|
device_config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
|
pub const ROM_START: usize = 0x200;
|
||||||
const FONT_HEIGHT: u16 = 5;
|
const FONT_HEIGHT: u16 = 5;
|
||||||
const FONT_DEFAULT_MEM_LOCATION_START: usize = 0x50;
|
const FONT_DEFAULT_MEM_LOCATION_START: usize = 0x50;
|
||||||
const FONT_DEFAULT_MEM_LOCATION_END: usize = 0x9F;
|
const FONT_DEFAULT_MEM_LOCATION_END: usize = 0x9F;
|
||||||
const ROM_START: usize = 0x200;
|
|
||||||
|
|
||||||
// Throttling configuration for cpu
|
|
||||||
const DO_CHIP_CPU_THROTTLING:bool = true;
|
|
||||||
const TARGET_CPU_SPEED_INSTRUCTIONS_PER_SECOND: u64 = 800;
|
|
||||||
const TARGET_CPU_INSTRUCTION_TIME: Duration = Duration::from_micros(1_000_000/Self::TARGET_CPU_SPEED_INSTRUCTIONS_PER_SECOND);
|
|
||||||
|
|
||||||
pub fn cycle(&mut self) -> EmulatorResult<()> {
|
pub fn cycle(&mut self) -> EmulatorResult<()> {
|
||||||
let time_start = std::time::Instant::now();
|
let time_start = std::time::Instant::now();
|
||||||
self.device_keyboard.update_keyboard_registers()?;
|
self.device_keyboard.update_keyboard_registers()?;
|
||||||
|
|
||||||
let pc = self.registers.pc as usize;
|
let pc = self.registers.pc as usize;
|
||||||
let instr_slice = self.memory.get(pc..pc + 2).expect("Failed to get memory");
|
let instr_slice = self.memory.get(pc..pc + 2).expect(format!("Failed to get memory at {}",pc).as_str());
|
||||||
self.registers.pc += 2;
|
self.registers.pc += 2;
|
||||||
|
|
||||||
let instruction = Instruction::decode_instruction(instr_slice);
|
let instruction = Instruction::decode_instruction(instr_slice);
|
||||||
self.execute_instruction(instruction)?;
|
self.execute_instruction(instruction)?;
|
||||||
|
|
||||||
|
if let Some(throttling_duration) = self.device_config.get_throttling_config() {
|
||||||
let instruction_time = time_start.elapsed();
|
let instruction_time = time_start.elapsed();
|
||||||
let time_left_to_sleep_for_instruction = Self::TARGET_CPU_INSTRUCTION_TIME.checked_sub(instruction_time).unwrap_or(Duration::ZERO);
|
let time_left_to_sleep_for_instruction = throttling_duration.checked_sub(instruction_time).unwrap_or(Duration::ZERO);
|
||||||
if Self::DO_CHIP_CPU_THROTTLING {
|
|
||||||
log::trace!("Instruction took {:?}, left with {:?}",instruction_time,time_left_to_sleep_for_instruction);
|
log::trace!("Instruction took {:?}, left with {:?}",instruction_time,time_left_to_sleep_for_instruction);
|
||||||
sleep(time_left_to_sleep_for_instruction);
|
sleep(time_left_to_sleep_for_instruction);
|
||||||
}
|
}
|
||||||
@@ -84,12 +78,11 @@ impl Device {
|
|||||||
y * Self::FRAME_BUFFER_WIDTH + x
|
y * Self::FRAME_BUFFER_WIDTH + x
|
||||||
}
|
}
|
||||||
pub fn execute_instruction(&mut self, instruction: Instruction) -> EmulatorResult<()> {
|
pub fn execute_instruction(&mut self, instruction: Instruction) -> EmulatorResult<()> {
|
||||||
// thread::sleep(Duration::from_millis(250));
|
|
||||||
log::trace!("Executing {:?}, {:?}", &instruction, &self.registers);
|
log::trace!("Executing {:?}, {:?}", &instruction, &self.registers);
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::InvalidInstruction => {
|
Instruction::InvalidInstruction => {
|
||||||
log::info!("Executing passthrough");
|
log::info!("Executing passthrough");
|
||||||
if self.halt_on_invalid {
|
if self.device_config.should_halt_on_invalid() {
|
||||||
return Err(EmulatorError::IOError("Caught Invalid Instruction".to_string()));
|
return Err(EmulatorError::IOError("Caught Invalid Instruction".to_string()));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -152,7 +145,7 @@ impl Device {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instruction::JumpWithOffset(x, num) => {
|
Instruction::JumpWithOffset(x, num) => {
|
||||||
let regnum = if self.new_chip8_mode { x } else { 0 };
|
let regnum = if self.device_config.is_new_chip8() { x } else { 0 };
|
||||||
let new_pc = self.registers.v[regnum] as u16 + num;
|
let new_pc = self.registers.v[regnum] as u16 + num;
|
||||||
self.registers.pc = new_pc;
|
self.registers.pc = new_pc;
|
||||||
}
|
}
|
||||||
@@ -205,7 +198,7 @@ impl Device {
|
|||||||
self.set_flag_register(!is_overflow);
|
self.set_flag_register(!is_overflow);
|
||||||
}
|
}
|
||||||
Instruction::RShift(x, y) => {
|
Instruction::RShift(x, y) => {
|
||||||
if !self.new_chip8_mode {
|
if !self.device_config.is_new_chip8() {
|
||||||
self.registers.v[x] = self.registers.v[y];
|
self.registers.v[x] = self.registers.v[y];
|
||||||
}
|
}
|
||||||
let val = self.registers.v[x];
|
let val = self.registers.v[x];
|
||||||
@@ -214,7 +207,7 @@ impl Device {
|
|||||||
self.set_flag_register(bit_carry);
|
self.set_flag_register(bit_carry);
|
||||||
}
|
}
|
||||||
Instruction::LShift(x, y) => {
|
Instruction::LShift(x, y) => {
|
||||||
if !self.new_chip8_mode {
|
if !self.device_config.is_new_chip8() {
|
||||||
self.registers.v[x] = self.registers.v[y];
|
self.registers.v[x] = self.registers.v[y];
|
||||||
}
|
}
|
||||||
let left = self.registers.v[x];
|
let left = self.registers.v[x];
|
||||||
@@ -239,7 +232,7 @@ impl Device {
|
|||||||
let reg_value = self.registers.v[x];
|
let reg_value = self.registers.v[x];
|
||||||
let index_original = self.registers.i;
|
let index_original = self.registers.i;
|
||||||
// newer instruction set requires wrapping on 12 bit overflow, and setting vf
|
// newer instruction set requires wrapping on 12 bit overflow, and setting vf
|
||||||
let addn_res = if self.new_chip8_mode {
|
let addn_res = if self.device_config.is_new_chip8() {
|
||||||
let overflowing = (reg_value as u16 + index_original) >= 0x1000;
|
let overflowing = (reg_value as u16 + index_original) >= 0x1000;
|
||||||
self.set_flag_register(overflowing);
|
self.set_flag_register(overflowing);
|
||||||
(reg_value as u16 + index_original) % 0x1000
|
(reg_value as u16 + index_original) % 0x1000
|
||||||
@@ -249,8 +242,13 @@ impl Device {
|
|||||||
self.registers.i = addn_res;
|
self.registers.i = addn_res;
|
||||||
}
|
}
|
||||||
Instruction::GetKey(x) => {
|
Instruction::GetKey(x) => {
|
||||||
let key_expected = self.registers.v[x];
|
let mut possible_presses = (0..=0xfu8).filter(|x|{self.device_keyboard.query_key_down(*x)});
|
||||||
if !self.device_keyboard.query_key_down(key_expected) {
|
let pressed = possible_presses.next();
|
||||||
|
if let Some(pressed_key) = pressed {
|
||||||
|
|
||||||
|
self.registers.v[x] = pressed_key;
|
||||||
|
|
||||||
|
} else{
|
||||||
self.registers.pc -= 2;
|
self.registers.pc -= 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -281,7 +279,7 @@ impl Device {
|
|||||||
let index = self.registers.i as usize;
|
let index = self.registers.i as usize;
|
||||||
self.memory[index..=(index + last_reg_to_store)].copy_from_slice(reg_slice);
|
self.memory[index..=(index + last_reg_to_store)].copy_from_slice(reg_slice);
|
||||||
// Old Chip8 used to use i as a incrementing index
|
// Old Chip8 used to use i as a incrementing index
|
||||||
if !self.new_chip8_mode {
|
if !self.device_config.is_new_chip8() {
|
||||||
self.registers.i += last_reg_to_store as u16 + 1;
|
self.registers.i += last_reg_to_store as u16 + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -290,7 +288,7 @@ impl Device {
|
|||||||
let mem_slice = &self.memory[index..=(index + last_reg_to_load)];
|
let mem_slice = &self.memory[index..=(index + last_reg_to_load)];
|
||||||
self.registers.v[0..=last_reg_to_load].copy_from_slice(mem_slice);
|
self.registers.v[0..=last_reg_to_load].copy_from_slice(mem_slice);
|
||||||
// Old Chip8 used to use i as a incrementing index
|
// Old Chip8 used to use i as a incrementing index
|
||||||
if !self.new_chip8_mode {
|
if !self.device_config.is_new_chip8() {
|
||||||
self.registers.i += last_reg_to_load as u16 + 1;
|
self.registers.i += last_reg_to_load as u16 + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -382,23 +380,3 @@ impl Drop for Device {
|
|||||||
self.timer.send_stop_signal()
|
self.timer.send_stop_signal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RegisterFile {
|
|
||||||
pub v: [u8; 0x10],
|
|
||||||
/// program counter - only u12 technically.
|
|
||||||
pub pc: u16,
|
|
||||||
/// stack pointer
|
|
||||||
pub i: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RegisterFile {
|
|
||||||
pub const DEFAULT_PC_VALUE: u16 = Device::ROM_START as u16;
|
|
||||||
pub fn new() -> RegisterFile {
|
|
||||||
RegisterFile {
|
|
||||||
v: [0; 0x10],
|
|
||||||
pc: Self::DEFAULT_PC_VALUE,
|
|
||||||
i: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -186,8 +186,9 @@ impl Instruction {
|
|||||||
let x = (instruction & 0xf00) >> 8;
|
let x = (instruction & 0xf00) >> 8;
|
||||||
Instruction::LoadRegistersFromMemory(x as usize)
|
Instruction::LoadRegistersFromMemory(x as usize)
|
||||||
}
|
}
|
||||||
_ => {
|
instruction_nibble => {
|
||||||
todo!("Unimplemented instruction")
|
log::error!("Unimplemented instruction with nibble {:?}",instruction_nibble);
|
||||||
|
Instruction::InvalidInstruction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -50,7 +50,7 @@ impl Keyboard {
|
|||||||
|
|
||||||
/// Query if key is down
|
/// Query if key is down
|
||||||
pub fn query_key_down(&self, key_num: u8) -> bool {
|
pub fn query_key_down(&self, key_num: u8) -> bool {
|
||||||
(self.bitflags | 1 << key_num) == (1 << key_num)
|
(self.bitflags & (1 << key_num)) == (1 << key_num)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_keyboard_state(&mut self, keyboard_event: KeyboardEvent) {
|
fn update_keyboard_state(&mut self, keyboard_event: KeyboardEvent) {
|
||||||
@@ -67,11 +67,53 @@ impl Keyboard {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Key;
|
use super::{Key, Keyboard};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_key_assignment(){
|
fn test_key_assignment(){
|
||||||
assert_eq!(0,Key::K0 as u16);
|
assert_eq!(0,Key::K0 as u16);
|
||||||
assert_eq!(15,Key::KF as u16);
|
assert_eq!(15,Key::KF as u16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_key_at_nothing_pressed(){
|
||||||
|
let (_sender,receiver) = std::sync::mpsc::sync_channel(1);
|
||||||
|
let keyboard = Keyboard::new(receiver);
|
||||||
|
assert_no_key_pressed(&keyboard);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_key_down_then_up(){
|
||||||
|
let (sender,receiver) = std::sync::mpsc::sync_channel(1);
|
||||||
|
let mut keyboard = Keyboard::new(receiver);
|
||||||
|
assert_no_key_pressed(&keyboard);
|
||||||
|
|
||||||
|
|
||||||
|
sender.try_send(super::KeyboardEvent::KeyDown(Key::K0)).expect("Could not send");
|
||||||
|
keyboard.update_keyboard_registers().expect("Could not update keyboard");
|
||||||
|
|
||||||
|
|
||||||
|
assert_eq!(1,keyboard.bitflags);
|
||||||
|
assert_eq!(true,keyboard.query_key_down(0));
|
||||||
|
for i in 1..=0xF {
|
||||||
|
assert_eq!(false,keyboard.query_key_down(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sender.try_send(super::KeyboardEvent::KeyUp(Key::K0)).expect("Could not send");
|
||||||
|
keyboard.update_keyboard_registers().expect("Could not update keyboard");
|
||||||
|
assert_no_key_pressed(&keyboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_no_key_pressed(keyboard: &Keyboard){
|
||||||
|
assert_eq!(0,keyboard.bitflags);
|
||||||
|
for i in 0..=0xF {
|
||||||
|
assert_eq!(false,keyboard.query_key_down(i),"Failed to match at index {}, bitflags at {}",i,keyboard.bitflags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -2,5 +2,6 @@ pub mod timer;
|
|||||||
pub mod keyboard;
|
pub mod keyboard;
|
||||||
pub mod instruction;
|
pub mod instruction;
|
||||||
mod device;
|
mod device;
|
||||||
|
mod registers;
|
||||||
|
|
||||||
pub use device::*;
|
pub use device::*;
|
||||||
|
22
src/device/registers.rs
Normal file
22
src/device/registers.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
use super::Device;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RegisterFile {
|
||||||
|
pub v: [u8; 0x10],
|
||||||
|
/// program counter - only u12 technically.
|
||||||
|
pub pc: u16,
|
||||||
|
/// stack pointer
|
||||||
|
pub i: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegisterFile {
|
||||||
|
pub const DEFAULT_PC_VALUE: u16 = Device::ROM_START as u16;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RegisterFile{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { v: [0;0x10], pc: Self::DEFAULT_PC_VALUE, i: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
12
src/main.rs
12
src/main.rs
@@ -2,7 +2,6 @@ use std::sync::{Arc, Mutex};
|
|||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::thread::JoinHandle;
|
use std::thread::JoinHandle;
|
||||||
use std::time::Duration;
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use sdl2::audio::{AudioQueue, AudioSpecDesired};
|
use sdl2::audio::{AudioQueue, AudioSpecDesired};
|
||||||
@@ -14,6 +13,7 @@ use sdl2::render::BlendMode;
|
|||||||
|
|
||||||
use sdl2::render::WindowCanvas;
|
use sdl2::render::WindowCanvas;
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
|
use util::DeviceConfig;
|
||||||
|
|
||||||
use crate::args::Porcel8ProgramArgs;
|
use crate::args::Porcel8ProgramArgs;
|
||||||
use crate::device::Device;
|
use crate::device::Device;
|
||||||
@@ -33,7 +33,7 @@ const WINDOW_TITLE: &str = "porcel8";
|
|||||||
|
|
||||||
fn main() -> EmulatorResult<()> {
|
fn main() -> EmulatorResult<()> {
|
||||||
SimpleLogger::new().with_level(LevelFilter::Info).env().init().unwrap();
|
SimpleLogger::new().with_level(LevelFilter::Info).env().init().unwrap();
|
||||||
let Porcel8ProgramArgs { filename, new_chip8_behaviour, draw_scale, halt_on_invalid } = Porcel8ProgramArgs::parse();
|
let Porcel8ProgramArgs { filename, new_chip8_behaviour, draw_scale, halt_on_invalid, do_instruction_throttling, ips_throttling_rate: ipms_throttling_rate } = Porcel8ProgramArgs::parse();
|
||||||
|
|
||||||
log::info!("Started emulator");
|
log::info!("Started emulator");
|
||||||
|
|
||||||
@@ -45,8 +45,8 @@ fn main() -> EmulatorResult<()> {
|
|||||||
let (sdl_kb_adapter, device_keyboard) = SdlKeyboardAdapter::new_keyboard();
|
let (sdl_kb_adapter, device_keyboard) = SdlKeyboardAdapter::new_keyboard();
|
||||||
|
|
||||||
timer.start();
|
timer.start();
|
||||||
|
let device_config = DeviceConfig::new(new_chip8_behaviour, halt_on_invalid, do_instruction_throttling, ipms_throttling_rate);
|
||||||
let device = Device::new(timer, frame_buffer_for_device, device_keyboard, new_chip8_behaviour, halt_on_invalid);
|
let device = Device::new(timer, frame_buffer_for_device, device_keyboard, device_config);
|
||||||
|
|
||||||
let (device_termination_signal_sender, compute_handle) = start_compute_thread(filename, device)?;
|
let (device_termination_signal_sender, compute_handle) = start_compute_thread(filename, device)?;
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ fn main() -> EmulatorResult<()> {
|
|||||||
match event {
|
match event {
|
||||||
Event::Quit { .. } |
|
Event::Quit { .. } |
|
||||||
Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
|
Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
|
||||||
device_termination_signal_sender.send(()).expect("Could not send");
|
device_termination_signal_sender.send(())?;
|
||||||
break 'running;
|
break 'running;
|
||||||
}
|
}
|
||||||
Event::KeyDown { keycode: Some(keycode), repeat: false, .. } => {
|
Event::KeyDown { keycode: Some(keycode), repeat: false, .. } => {
|
||||||
@@ -111,8 +111,6 @@ fn start_compute_thread(filename: String, mut device: Device) -> EmulatorResult<
|
|||||||
panic!("Disconnected");
|
panic!("Disconnected");
|
||||||
}
|
}
|
||||||
device.cycle().expect("Failed to execute");
|
device.cycle().expect("Failed to execute");
|
||||||
// Put a bit of delay to slow down execution
|
|
||||||
thread::sleep(Duration::from_millis(2))
|
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
Ok((device_termination_signal_sender, compute_handle))
|
Ok((device_termination_signal_sender, compute_handle))
|
||||||
|
99
src/util.rs
99
src/util.rs
@@ -1,19 +1,72 @@
|
|||||||
|
use crate::device::keyboard::KeyboardEvent;
|
||||||
|
use sdl2::video::WindowBuildError;
|
||||||
|
use sdl2::IntegerOrSdlError;
|
||||||
|
use std::fmt::Display;
|
||||||
use std::sync::mpsc::SendError;
|
use std::sync::mpsc::SendError;
|
||||||
use std::sync::PoisonError;
|
use std::sync::PoisonError;
|
||||||
use sdl2::IntegerOrSdlError;
|
use std::time::Duration;
|
||||||
use sdl2::video::WindowBuildError;
|
|
||||||
use crate::device::keyboard::KeyboardEvent;
|
|
||||||
|
|
||||||
pub type EmulatorResult<T> = Result<T, EmulatorError>;
|
pub type EmulatorResult<T> = Result<T, EmulatorError>;
|
||||||
|
|
||||||
|
/// CHIP-8 Device configuration
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
pub struct DeviceConfig {
|
||||||
|
is_new_chip8: bool,
|
||||||
|
halt_on_invalid: bool,
|
||||||
|
/// None if disabled, target instruction time otherwise
|
||||||
|
throttling_time: Option<Duration>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeviceConfig {
|
||||||
|
pub fn new(
|
||||||
|
is_new_chip8: bool,
|
||||||
|
halt_on_invalid: bool,
|
||||||
|
do_instruction_throttling: bool,
|
||||||
|
ips_throttling_rate: u64,
|
||||||
|
) -> DeviceConfig {
|
||||||
|
DeviceConfig {
|
||||||
|
is_new_chip8,
|
||||||
|
halt_on_invalid,
|
||||||
|
throttling_time: if do_instruction_throttling {
|
||||||
|
Some(Duration::from_micros(1_000_000 / ips_throttling_rate))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_new_chip8(&self) -> bool {
|
||||||
|
self.is_new_chip8
|
||||||
|
}
|
||||||
|
pub fn should_halt_on_invalid(&self) -> bool {
|
||||||
|
self.halt_on_invalid
|
||||||
|
}
|
||||||
|
pub fn get_throttling_config(&self) -> Option<Duration> {
|
||||||
|
self.throttling_time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum EmulatorError {
|
pub enum EmulatorError {
|
||||||
SdlError(String),
|
SdlError(String),
|
||||||
IOError(String),
|
IOError(String),
|
||||||
MutexInvalidState(String)
|
MutexInvalidState(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for EmulatorError{
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self{
|
||||||
|
EmulatorError::SdlError(err) => write!(f,"Error with SDL: {}",err),
|
||||||
|
EmulatorError::IOError(io_err) => write!(f,"IO Error: {}",io_err),
|
||||||
|
EmulatorError::MutexInvalidState(invalid_mutex_err) => write!(f,"Issue from mutex: {}",invalid_mutex_err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SendError<()>> for EmulatorError{
|
||||||
|
fn from(value: SendError<()>) -> Self {
|
||||||
|
Self::IOError(String::from("Could not update as: ")+value.to_string().as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
impl From<String> for EmulatorError {
|
impl From<String> for EmulatorError {
|
||||||
fn from(value: String) -> Self {
|
fn from(value: String) -> Self {
|
||||||
Self::SdlError(value)
|
Self::SdlError(value)
|
||||||
@@ -29,24 +82,50 @@ impl From<WindowBuildError> for EmulatorError {
|
|||||||
impl From<IntegerOrSdlError> for EmulatorError {
|
impl From<IntegerOrSdlError> for EmulatorError {
|
||||||
fn from(value: IntegerOrSdlError) -> Self {
|
fn from(value: IntegerOrSdlError) -> Self {
|
||||||
match value {
|
match value {
|
||||||
IntegerOrSdlError::IntegerOverflows(x, y) => { Self::SdlError(format!("{} - {}", x, y)) }
|
IntegerOrSdlError::IntegerOverflows(x, y) => Self::SdlError(format!("{} - {}", x, y)),
|
||||||
IntegerOrSdlError::SdlError(str) => { Self::SdlError(str) }
|
IntegerOrSdlError::SdlError(str) => Self::SdlError(str),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<std::io::Error> for EmulatorError{
|
impl From<std::io::Error> for EmulatorError {
|
||||||
fn from(value: std::io::Error) -> Self {
|
fn from(value: std::io::Error) -> Self {
|
||||||
Self::IOError(value.to_string())
|
Self::IOError(value.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T> From<PoisonError<T>> for EmulatorError{
|
impl<T> From<PoisonError<T>> for EmulatorError {
|
||||||
fn from(value: PoisonError<T>) -> Self {
|
fn from(value: PoisonError<T>) -> Self {
|
||||||
Self::MutexInvalidState(value.to_string())
|
Self::MutexInvalidState(value.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SendError<KeyboardEvent>> for EmulatorError{
|
impl From<SendError<KeyboardEvent>> for EmulatorError {
|
||||||
fn from(value: SendError<KeyboardEvent>) -> Self {
|
fn from(value: SendError<KeyboardEvent>) -> Self {
|
||||||
Self::IOError(format!("Failed to communicate keyboard event to main thread: {}",value))
|
Self::IOError(format!(
|
||||||
|
"Failed to communicate keyboard event to main thread: {}",
|
||||||
|
value
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests{
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use super::DeviceConfig;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_device_config_all_false(){
|
||||||
|
let device_config = DeviceConfig::new(false, false, false, 800);
|
||||||
|
assert_eq!(false,device_config.is_new_chip8());
|
||||||
|
assert_eq!(false,device_config.should_halt_on_invalid());
|
||||||
|
assert!(device_config.get_throttling_config().is_none());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_device_config_throttling_enabled_and_new_chip8(){
|
||||||
|
let device_config = DeviceConfig::new(true, false, true, 100);
|
||||||
|
const EXPECTED_INSTRUCTION_TIME_MS:u64 = 10;
|
||||||
|
assert_eq!(true,device_config.is_new_chip8());
|
||||||
|
assert_eq!(false,device_config.should_halt_on_invalid());
|
||||||
|
assert_eq!(Some(Duration::from_millis(EXPECTED_INSTRUCTION_TIME_MS)),device_config.get_throttling_config());
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user