more dump of "Emma" for Gemma
Video reset now tests ok gemma egui interface now implements stupid workaround for video reset
This commit is contained in:
parent
c9ef6d4e04
commit
bfb615cfe7
215
Cargo.lock
generated
215
Cargo.lock
generated
@ -692,21 +692,6 @@ dependencies = [
|
|||||||
"wayland-client 0.31.5",
|
"wayland-client 0.31.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cassowary"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "castaway"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5"
|
|
||||||
dependencies = [
|
|
||||||
"rustversion",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.6"
|
version = "1.1.6"
|
||||||
@ -901,20 +886,6 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "compact_str"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644"
|
|
||||||
dependencies = [
|
|
||||||
"castaway",
|
|
||||||
"cfg-if",
|
|
||||||
"itoa",
|
|
||||||
"rustversion",
|
|
||||||
"ryu",
|
|
||||||
"static_assertions",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "concurrent-queue"
|
name = "concurrent-queue"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
@ -1069,31 +1040,6 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossterm"
|
|
||||||
version = "0.28.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.6.0",
|
|
||||||
"crossterm_winapi",
|
|
||||||
"mio 1.0.2",
|
|
||||||
"parking_lot",
|
|
||||||
"rustix",
|
|
||||||
"signal-hook",
|
|
||||||
"signal-hook-mio",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossterm_winapi"
|
|
||||||
version = "0.9.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
|
||||||
dependencies = [
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
@ -1649,20 +1595,11 @@ dependencies = [
|
|||||||
name = "gemma"
|
name = "gemma"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"beep",
|
|
||||||
"chrono",
|
"chrono",
|
||||||
"copypasta",
|
|
||||||
"dimensioned",
|
"dimensioned",
|
||||||
"glium",
|
|
||||||
"image 0.23.14",
|
|
||||||
"imgui",
|
|
||||||
"imgui-glium-renderer",
|
|
||||||
"imgui-winit-support",
|
|
||||||
"log",
|
"log",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
"rand 0.9.0-alpha.2",
|
"rand 0.9.0-alpha.2",
|
||||||
"ratatui",
|
|
||||||
"winit 0.27.5",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2013,18 +1950,6 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "heck"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hermit-abi"
|
|
||||||
version = "0.3.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -2203,16 +2128,6 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "instability"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.71",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "instant"
|
name = "instant"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
@ -2231,26 +2146,11 @@ version = "0.4.13"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
|
checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi 0.4.0",
|
"hermit-abi",
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itoa"
|
|
||||||
version = "1.0.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni"
|
name = "jni"
|
||||||
version = "0.21.1"
|
version = "0.21.1"
|
||||||
@ -2384,15 +2284,6 @@ version = "0.4.22"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lru"
|
|
||||||
version = "0.12.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904"
|
|
||||||
dependencies = [
|
|
||||||
"hashbrown",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "malloc_buf"
|
name = "malloc_buf"
|
||||||
version = "0.0.6"
|
version = "0.0.6"
|
||||||
@ -2506,19 +2397,6 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mio"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi 0.3.9",
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"wasi",
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "naga"
|
name = "naga"
|
||||||
version = "22.1.0"
|
version = "22.1.0"
|
||||||
@ -3200,7 +3078,7 @@ checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"concurrent-queue",
|
"concurrent-queue",
|
||||||
"hermit-abi 0.4.0",
|
"hermit-abi",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustix",
|
"rustix",
|
||||||
"tracing",
|
"tracing",
|
||||||
@ -3356,27 +3234,6 @@ dependencies = [
|
|||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ratatui"
|
|
||||||
version = "0.28.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5ba6a365afbe5615999275bea2446b970b10a41102500e27ce7678d50d978303"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.6.0",
|
|
||||||
"cassowary",
|
|
||||||
"compact_str",
|
|
||||||
"crossterm",
|
|
||||||
"instability",
|
|
||||||
"itertools",
|
|
||||||
"lru",
|
|
||||||
"paste",
|
|
||||||
"strum",
|
|
||||||
"strum_macros",
|
|
||||||
"unicode-segmentation",
|
|
||||||
"unicode-truncate",
|
|
||||||
"unicode-width",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "raw-window-handle"
|
name = "raw-window-handle"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@ -3505,18 +3362,6 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustversion"
|
|
||||||
version = "1.0.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ryu"
|
|
||||||
version = "1.0.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "safe_arch"
|
name = "safe_arch"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@ -3654,27 +3499,6 @@ dependencies = [
|
|||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "signal-hook"
|
|
||||||
version = "0.3.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"signal-hook-registry",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "signal-hook-mio"
|
|
||||||
version = "0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"mio 1.0.2",
|
|
||||||
"signal-hook",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@ -3840,28 +3664,6 @@ version = "0.10.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strum"
|
|
||||||
version = "0.26.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
|
||||||
dependencies = [
|
|
||||||
"strum_macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strum_macros"
|
|
||||||
version = "0.26.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
|
||||||
dependencies = [
|
|
||||||
"heck",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"rustversion",
|
|
||||||
"syn 2.0.71",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
@ -4126,17 +3928,6 @@ version = "1.11.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-truncate"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
|
|
||||||
dependencies = [
|
|
||||||
"itertools",
|
|
||||||
"unicode-segmentation",
|
|
||||||
"unicode-width",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
@ -5031,7 +4822,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"mint",
|
"mint",
|
||||||
"mio 0.8.11",
|
"mio",
|
||||||
"ndk 0.7.0",
|
"ndk 0.7.0",
|
||||||
"ndk-glue",
|
"ndk-glue",
|
||||||
"objc",
|
"objc",
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["gemma", "gemmaegui", "gemmaimgui" ]
|
members = ["gemma", "gemmaegui", "gemmaimgui" ]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -5,17 +5,9 @@ edition = "2021"
|
|||||||
autobenches = true
|
autobenches = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ratatui = "0.28.0"
|
|
||||||
glium = { version = "0.34.0", default-features = true }
|
|
||||||
image = "0.23"
|
|
||||||
imgui = { version ="0.12.0", features = ["tables-api"] }
|
|
||||||
imgui-glium-renderer = { version = "0.12.0" }
|
|
||||||
imgui-winit-support = { version = "0.12.0" }
|
|
||||||
winit = { version = "0.27", features = ["x11", "mint"] }
|
|
||||||
pretty_env_logger = "0.5.0"
|
pretty_env_logger = "0.5.0"
|
||||||
copypasta = "0.8"
|
|
||||||
rand = "0.9.0-alpha.2"
|
rand = "0.9.0-alpha.2"
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
beep = "0.3.0"
|
# beep = "0.3.0"
|
||||||
chrono = "0.4.38"
|
chrono = "0.4.38"
|
||||||
dimensioned = "0.8.0"
|
dimensioned = "0.8.0"
|
||||||
|
|||||||
@ -37,7 +37,9 @@ impl Default for Chip8Computer {
|
|||||||
|
|
||||||
impl Chip8Computer {
|
impl Chip8Computer {
|
||||||
pub fn reset(&mut self) -> Self{
|
pub fn reset(&mut self) -> Self{
|
||||||
Self::default()
|
let mut working = Chip8Computer::new();
|
||||||
|
working.video_memory.reset();
|
||||||
|
working
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dump_keypad_to_string(&self) -> String {
|
pub fn dump_keypad_to_string(&self) -> String {
|
||||||
@ -67,7 +69,7 @@ impl Chip8Computer {
|
|||||||
Chip8Computer::default()
|
Chip8Computer::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_bytes_to_memory(&mut self, offset: u16, to_load: Box<Vec<u8>>) {
|
pub fn load_bytes_to_memory(&mut self, offset: u16, to_load: &Vec<u8>) {
|
||||||
let total_len = to_load.len() as u16;
|
let total_len = to_load.len() as u16;
|
||||||
for current_index in 0..total_len {
|
for current_index in 0..total_len {
|
||||||
let new_value = to_load[current_index as usize];
|
let new_value = to_load[current_index as usize];
|
||||||
@ -103,17 +105,3 @@ impl Chip8Computer {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use rand::random;
|
|
||||||
use crate::constants::CHIP8_VIDEO_MEMORY;
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn smoke() {
|
|
||||||
assert!(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::ops::{BitAnd, Shl, Shr};
|
use std::ops::{BitAnd, Shr};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use rand::random;
|
use rand::random;
|
||||||
use crate::chip8::computer::{Chip8Computer};
|
use crate::chip8::computer::{Chip8Computer};
|
||||||
@ -596,7 +596,7 @@ impl Chip8CpuInstructions {
|
|||||||
1
|
1
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("SUB CARRY -> REGISTER 1 = [0x{x:02x}] / [{x_register}] REGISTER 2 = [0x{y:02x}] / [{y_register}] SUB = {value_to_poke} CARRY = {new_value}");
|
debug!("SUB CARRY -> REGISTER 1 = [0x{x:02x}] / [{x_register}] REGISTER 2 = [0x{y:02x}] / [{y_register}] SUB = {value_to_poke} CARRY = {new_value}");
|
||||||
|
|
||||||
input.registers.poke(*x, value_to_poke as u8);
|
input.registers.poke(*x, value_to_poke as u8);
|
||||||
input.registers.poke(0xf, new_value);
|
input.registers.poke(0xf, new_value);
|
||||||
@ -656,7 +656,7 @@ impl Chip8CpuInstructions {
|
|||||||
let new_value: u8 = random();
|
let new_value: u8 = random();
|
||||||
let and_value: u8 = *byte;
|
let and_value: u8 = *byte;
|
||||||
let result = new_value & and_value;
|
let result = new_value & and_value;
|
||||||
println!("RANDOM: [{new_value:02x}] AND: [{and_value:02x} Result: [{result:02x}]");
|
debug!("RANDOM: [{new_value:02x}] AND: [{and_value:02x} Result: [{result:02x}]");
|
||||||
input.registers.poke(*x as u8, new_value & *byte as u8)
|
input.registers.poke(*x as u8, new_value & *byte as u8)
|
||||||
}
|
}
|
||||||
Chip8CpuInstructions::DrawVxVyNibble(y, x, n) => {
|
Chip8CpuInstructions::DrawVxVyNibble(y, x, n) => {
|
||||||
@ -837,9 +837,6 @@ impl Chip8CpuInstructions {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use dimensioned::typenum::assert_type;
|
|
||||||
use ratatui::crossterm::execute;
|
|
||||||
use crate::chip8::cpu_states::Chip8CpuStates;
|
|
||||||
use crate::chip8::system_memory::{CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_9};
|
use crate::chip8::system_memory::{CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_9};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -1297,12 +1294,12 @@ mod test {
|
|||||||
x.registers.poke(0x1, 0x2);
|
x.registers.poke(0x1, 0x2);
|
||||||
Chip8CpuInstructions::LdFVx(0x1).execute(&mut x);
|
Chip8CpuInstructions::LdFVx(0x1).execute(&mut x);
|
||||||
|
|
||||||
assert_eq!(x.registers.peek_i(), 0x5);
|
assert_eq!(x.registers.peek_i(), 10);
|
||||||
|
|
||||||
let mut x = Chip8Computer::new();
|
let mut x = Chip8Computer::new();
|
||||||
x.registers.poke(0x01, 0x06);
|
x.registers.poke(0x01, 0x06);
|
||||||
Chip8CpuInstructions::LdFVx(0x1).execute(&mut x);
|
Chip8CpuInstructions::LdFVx(0x1).execute(&mut x);
|
||||||
assert_eq!(x.registers.peek_i(), 25);
|
assert_eq!(x.registers.peek_i(), 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1444,8 +1441,8 @@ mod test {
|
|||||||
let real_bit_in_byte = 7 - bit_in_byte;
|
let real_bit_in_byte = 7 - bit_in_byte;
|
||||||
let shifted_one = 0x01 << real_bit_in_byte;
|
let shifted_one = 0x01 << real_bit_in_byte;
|
||||||
let one_shift_set = (shifted_one & row_data) > 0;
|
let one_shift_set = (shifted_one & row_data) > 0;
|
||||||
println!("ROWDATA = \t\t[{row_data:08b}]\tBIT IN BYTE = \t[{bit_in_byte}]\tONE_SHIFT_SET = [{one_shift_set}]\tSHIFTED ONE = [{shifted_one:08b}]");
|
debug!("ROWDATA = \t\t[{row_data:08b}]\tBIT IN BYTE = \t[{bit_in_byte}]\tONE_SHIFT_SET = [{one_shift_set}]\tSHIFTED ONE = [{shifted_one:08b}]");
|
||||||
println!("DATA_OFFSET FOR SOURCE DATA {}x{} is {} / offset by {}x{} and should be {} working with byte {:08b}",
|
debug!("DATA_OFFSET FOR SOURCE DATA {}x{} is {} / offset by {}x{} and should be {} working with byte {:08b}",
|
||||||
bit_in_byte, row_in_sprite, data_offset, x_offset, y_offset, one_shift_set, row_data);
|
bit_in_byte, row_in_sprite, data_offset, x_offset, y_offset, one_shift_set, row_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,8 +14,8 @@ impl Default for Chip8Stack {
|
|||||||
impl Chip8Stack {
|
impl Chip8Stack {
|
||||||
pub fn push(&mut self, new_value: &u16) {
|
pub fn push(&mut self, new_value: &u16) {
|
||||||
if self.depth() == 16 {
|
if self.depth() == 16 {
|
||||||
println!("Deep deep stack?");
|
// println!("Deep deep stack?");
|
||||||
// panic!("Stack Overflow");
|
panic!("Stack Overflow");
|
||||||
}
|
}
|
||||||
self.items.push(*new_value );
|
self.items.push(*new_value );
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,25 @@
|
|||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use crate::constants::CHIP8_VIDEO_MEMORY;
|
use crate::constants::{CHIP8_VIDEO_MEMORY, CHIP8_VIDEO_WIDTH};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Chip8Video {
|
pub struct Chip8Video {
|
||||||
memory: [bool; CHIP8_VIDEO_MEMORY],
|
memory: [bool; CHIP8_VIDEO_MEMORY],
|
||||||
pub has_frame_changed: bool
|
pub has_frame_changed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chip8Video {
|
impl Chip8Video {
|
||||||
|
fn int_cls(&self) -> Chip8Video {
|
||||||
|
let mut x = Chip8Video::default();
|
||||||
|
for i in 0..CHIP8_VIDEO_WIDTH {
|
||||||
|
x.poke(i as u16, false);
|
||||||
|
}
|
||||||
|
x
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self) -> Self {
|
||||||
|
self.int_cls()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn cls(&mut self) {
|
pub fn cls(&mut self) {
|
||||||
for i in 0..CHIP8_VIDEO_MEMORY {
|
for i in 0..CHIP8_VIDEO_MEMORY {
|
||||||
self.memory[i] = false;
|
self.memory[i] = false;
|
||||||
@ -21,7 +33,7 @@ impl Chip8Video {
|
|||||||
pub fn new(initial_configuration: [bool; CHIP8_VIDEO_MEMORY]) -> Self {
|
pub fn new(initial_configuration: [bool; CHIP8_VIDEO_MEMORY]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
memory: initial_configuration,
|
memory: initial_configuration,
|
||||||
has_frame_changed: false
|
has_frame_changed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,18 +101,38 @@ impl Chip8Video {
|
|||||||
|
|
||||||
impl Default for Chip8Video {
|
impl Default for Chip8Video {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { memory: [false; CHIP8_VIDEO_MEMORY as usize], has_frame_changed: false }
|
debug!("DEFAULT VIDEO PREPARED");
|
||||||
|
|
||||||
|
let new_struct = Chip8Video { memory: [false; CHIP8_VIDEO_MEMORY], has_frame_changed: false };
|
||||||
|
println!("NEW DEFAULT MEMORY : {}", new_struct.format_as_string());
|
||||||
|
|
||||||
|
new_struct
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use crate::chip8::system_memory::{CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_3, CHIP8FONT_4, CHIP8FONT_5, CHIP8FONT_6, CHIP8FONT_7};
|
use crate::chip8::system_memory::{CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_3, CHIP8FONT_4, CHIP8FONT_5, CHIP8FONT_6, CHIP8FONT_7};
|
||||||
use crate::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
const TEST_OUTPUT_SAMPLE_DIR: &str = "../resources/test/";
|
||||||
|
|
||||||
|
fn build_checkerboard() -> Chip8Video {
|
||||||
|
let mut r = Chip8Video::default();
|
||||||
|
|
||||||
|
for i in 0..CHIP8_VIDEO_MEMORY {
|
||||||
|
r.poke(i as u16, i % 2 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_test_result(suffix: &str) -> String {
|
||||||
|
std::fs::read_to_string(TEST_OUTPUT_SAMPLE_DIR.to_owned() + suffix)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn smoke() { assert!(true) }
|
fn smoke() { assert!(true) }
|
||||||
|
|
||||||
@ -183,7 +215,7 @@ mod test {
|
|||||||
fn cls() {
|
fn cls() {
|
||||||
let mut initial_memory = [false; CHIP8_VIDEO_MEMORY];
|
let mut initial_memory = [false; CHIP8_VIDEO_MEMORY];
|
||||||
let mut ws = String::new();
|
let mut ws = String::new();
|
||||||
// set our checkerboard
|
|
||||||
for cbr in 0..32 {
|
for cbr in 0..32 {
|
||||||
for cbc in 0..64 {
|
for cbc in 0..64 {
|
||||||
let dof = cbr * 64 + cbc;
|
let dof = cbr * 64 + cbc;
|
||||||
@ -205,12 +237,12 @@ mod test {
|
|||||||
let mut v = Chip8Video::default();
|
let mut v = Chip8Video::default();
|
||||||
v.poke_byte(0x00, to_poke);
|
v.poke_byte(0x00, to_poke);
|
||||||
assert!(v.peek(0x00));
|
assert!(v.peek(0x00));
|
||||||
assert!(v.peek(0x02));
|
|
||||||
assert!(v.peek(0x04));
|
|
||||||
assert!(v.peek(0x06));
|
|
||||||
assert!(!v.peek(0x01));
|
assert!(!v.peek(0x01));
|
||||||
|
assert!(v.peek(0x02));
|
||||||
assert!(!v.peek(0x03));
|
assert!(!v.peek(0x03));
|
||||||
|
assert!(v.peek(0x04));
|
||||||
assert!(!v.peek(0x05));
|
assert!(!v.peek(0x05));
|
||||||
|
assert!(v.peek(0x06));
|
||||||
assert!(!v.peek(0x07));
|
assert!(!v.peek(0x07));
|
||||||
for i in 0x8..CHIP8_VIDEO_MEMORY {
|
for i in 0x8..CHIP8_VIDEO_MEMORY {
|
||||||
assert!(!v.peek(i as u16));
|
assert!(!v.peek(i as u16));
|
||||||
@ -339,53 +371,50 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn write_checkboard() {
|
fn write_checkboard() {
|
||||||
let mut v = Chip8Video::default();
|
let mut v = build_checkerboard();
|
||||||
for current_row in 0..CHIP8_VIDEO_WIDTH {
|
|
||||||
for current_col in 0..(CHIP8_VIDEO_HEIGHT / 8){
|
assert_eq!(v.format_as_string(), read_test_result("test_video_write_checkerboard.asc"));
|
||||||
let offset = current_row * CHIP8_VIDEO_HEIGHT + current_col;
|
|
||||||
println!("CHECKBOARD OFFSET = {offset}");
|
|
||||||
v.poke(offset as u16, offset % 2 == 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("{}", v.format_as_string());
|
|
||||||
println!("fsck is a cool tool");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn zero_test() {
|
fn zero_test() {
|
||||||
let mut x = Chip8Video::default();
|
let mut x = Chip8Video::default();
|
||||||
|
|
||||||
x.poke_byte(0x00, CHIP8FONT_0[0]);
|
for (byte_index, data_offset) in (0..=0x100).step_by(0x40).enumerate() {
|
||||||
x.poke_byte(0x40, CHIP8FONT_0[1]);
|
x.poke_byte(data_offset as u16, CHIP8FONT_0[byte_index]);
|
||||||
x.poke_byte(0x80, CHIP8FONT_0[2]);
|
}
|
||||||
x.poke_byte(0xC0, CHIP8FONT_0[3]);
|
|
||||||
x.poke_byte(0x100, CHIP8FONT_0[4]);
|
|
||||||
|
|
||||||
assert_eq!(std::fs::read_to_string("../resources/test/test_video_zero.asc")
|
assert_eq!(read_test_result("test_video_zero.asc"), x.format_as_string());
|
||||||
.unwrap(),
|
|
||||||
x.format_as_string());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn multi_sprite_test() {
|
fn multi_sprite_test() {
|
||||||
let mut x = Chip8Video::default();
|
let mut x = Chip8Video::default();
|
||||||
// draw a row of digits 01234567
|
// draw a row of digits 01234567
|
||||||
|
|
||||||
let start_offset = 0x00;
|
|
||||||
let per_row_skip = 0x40;
|
|
||||||
let to_draw = [CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_3, CHIP8FONT_4, CHIP8FONT_5, CHIP8FONT_6, CHIP8FONT_7];
|
let to_draw = [CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_3, CHIP8FONT_4, CHIP8FONT_5, CHIP8FONT_6, CHIP8FONT_7];
|
||||||
for (index, sprite) in to_draw.iter().enumerate() {
|
for (index, sprite) in to_draw.iter().enumerate() {
|
||||||
let data_base_offset = index * 0x8;
|
let data_base_offset = index * 0x8;
|
||||||
println!("STARTING {index} at 0x{data_base_offset:04x} ({data_base_offset})");
|
for (index, offset) in (0..=0x100).step_by(0x40).enumerate() {
|
||||||
x.poke_byte(data_base_offset as u16, sprite[0]);
|
x.poke_byte((data_base_offset + offset) as u16, sprite[index]);
|
||||||
x.poke_byte((data_base_offset + 0x40) as u16, sprite[1]);
|
}
|
||||||
x.poke_byte((data_base_offset + 0x80) as u16, sprite[2]);
|
|
||||||
x.poke_byte((data_base_offset + 0xC0) as u16, sprite[3]);
|
|
||||||
x.poke_byte((data_base_offset + 0x100) as u16, sprite[4]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert_eq!(read_test_result("test_multi_sprite.asc"), x.format_as_string());
|
||||||
assert_eq!(std::fs::read_to_string("../resources/test/test_multi_sprite.asc")
|
}
|
||||||
.unwrap(),
|
|
||||||
x.format_as_string()); }
|
#[test]
|
||||||
|
fn reset_test() {
|
||||||
|
let mut x = Chip8Video::default();
|
||||||
|
|
||||||
|
let to_draw = [CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_3, CHIP8FONT_4, CHIP8FONT_5, CHIP8FONT_6, CHIP8FONT_7];
|
||||||
|
for (index, sprite) in to_draw.iter().enumerate() {
|
||||||
|
let data_base_offset = index * 0x8;
|
||||||
|
for (index, offset) in (0..=0x100).step_by(0x40).enumerate() {
|
||||||
|
x.poke_byte((data_base_offset + offset) as u16, sprite[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x = x.reset();
|
||||||
|
assert_eq!(x.format_as_string(), read_test_result("test_reset_clears_video.asc"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use gemma::chip8::computer::Chip8Computer;
|
use gemma::chip8::computer::Chip8Computer;
|
||||||
|
use gemma::constants::CHIP8_VIDEO_MEMORY;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn smoke() { assert!(true) }
|
fn smoke() { assert!(true) }
|
||||||
@ -10,7 +11,7 @@ fn test_rom_1_works() {
|
|||||||
// it takes 39 cycles to get to the end so lets run it 40.
|
// it takes 39 cycles to get to the end so lets run it 40.
|
||||||
|
|
||||||
let test_rom_to_run = std::fs::read("../resources/roms/2-ibm-logo.ch8").unwrap();
|
let test_rom_to_run = std::fs::read("../resources/roms/2-ibm-logo.ch8").unwrap();
|
||||||
x.load_bytes_to_memory(0x200, test_rom_to_run.into());
|
x.load_bytes_to_memory(0x200, (&test_rom_to_run).into());
|
||||||
|
|
||||||
for i in 0..40 {
|
for i in 0..40 {
|
||||||
x.step_system();
|
x.step_system();
|
||||||
@ -20,3 +21,21 @@ fn test_rom_1_works() {
|
|||||||
|
|
||||||
assert_eq!(x.dump_video_to_string(), std::fs::read_to_string("../resources/test/gemma_integration_ibm_rom_output.asc").unwrap());
|
assert_eq!(x.dump_video_to_string(), std::fs::read_to_string("../resources/test/gemma_integration_ibm_rom_output.asc").unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reset_clears_video() {
|
||||||
|
let mut x = Chip8Computer::new();
|
||||||
|
|
||||||
|
for i in 0..CHIP8_VIDEO_MEMORY {
|
||||||
|
x.video_memory.poke(i as u16, i % 2 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
x.reset();
|
||||||
|
x.video_memory = x.video_memory.reset();
|
||||||
|
|
||||||
|
assert_eq!(x.dump_video_to_string(), x.video_memory.format_as_string());
|
||||||
|
|
||||||
|
for i in 0..CHIP8_VIDEO_MEMORY {
|
||||||
|
assert!(!x.video_memory.peek(i as u16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use crate::support::GemmaEGuiSupport::GemmaEGui;
|
use crate::support::gemma_egui_support::GemmaEguiSupport;
|
||||||
use crate::support::EmmaEGuiState::EmmaEGuiState;
|
use crate::support::gemma_egui_state::GemmaEGuiState;
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use egui::Ui;
|
use egui::Ui;
|
||||||
use gemma::chip8::computer::Chip8Computer;
|
use gemma::chip8::computer::Chip8Computer;
|
||||||
@ -14,25 +14,24 @@ fn main() -> eframe::Result {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut state = EmmaEGuiState::default();
|
let mut state = GemmaEGuiState::default();
|
||||||
let mut computer = Chip8Computer::new();
|
let mut computer = Chip8Computer::new();
|
||||||
|
|
||||||
eframe::run_simple_native("EGUI Emma", options, move |ctx, _frame| {
|
eframe::run_simple_native("EGUI Emma", options, move |ctx, _frame| {
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
ui.heading("Gemma");
|
ui.heading("EGUI Gemma");
|
||||||
|
|
||||||
GemmaEGui::controls_view(&mut computer, &mut state, ui);
|
GemmaEguiSupport::controls_view(&mut computer, &mut state, ui);
|
||||||
|
|
||||||
if state.display_memory {
|
|
||||||
GemmaEGui::memory_view(&computer, ui);
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.display_video {
|
if state.display_video {
|
||||||
GemmaEGui::video_view(&computer, ui);
|
GemmaEguiSupport::video_view(&computer, ui);
|
||||||
|
}
|
||||||
|
if state.display_memory {
|
||||||
|
GemmaEguiSupport::memory_view(&computer, &mut state, ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.display_registers {
|
if state.display_registers {
|
||||||
GemmaEGui::registers_view(&computer, ui);
|
GemmaEguiSupport::registers_view(&computer, ui);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,75 +0,0 @@
|
|||||||
use egui::Color32;
|
|
||||||
use egui::Rect;
|
|
||||||
use egui::Pos2;
|
|
||||||
use egui::Vec2;
|
|
||||||
use egui::Ui;
|
|
||||||
use crate::support::EmmaEGuiState::EmmaEGuiState;
|
|
||||||
use crate::Chip8Computer;
|
|
||||||
|
|
||||||
pub struct GemmaEGui {}
|
|
||||||
impl GemmaEGui {
|
|
||||||
pub fn controls_view(mut system: &mut Chip8Computer, state: &mut EmmaEGuiState, ui: &mut Ui) {
|
|
||||||
if ui.button("Step").clicked() {
|
|
||||||
system.step_system();
|
|
||||||
}
|
|
||||||
if ui.button("Start").clicked() {
|
|
||||||
println!("Start");
|
|
||||||
}
|
|
||||||
if ui.button("Stop").clicked() {
|
|
||||||
println!("STOP");
|
|
||||||
}
|
|
||||||
if ui.button("Reset").clicked() {}
|
|
||||||
|
|
||||||
ui.checkbox(&mut state.display_memory, "Display Memory");
|
|
||||||
ui.checkbox(&mut state.display_video, "Display Video");
|
|
||||||
ui.checkbox(&mut state.display_registers, "Display Registers");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn registers_view(system: &Chip8Computer, ui: &mut Ui) {
|
|
||||||
ui.label(format!("V0-7: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ",
|
|
||||||
system.registers.peek(0x00),
|
|
||||||
system.registers.peek(0x01),
|
|
||||||
system.registers.peek(0x02),
|
|
||||||
system.registers.peek(0x03),
|
|
||||||
system.registers.peek(0x04),
|
|
||||||
system.registers.peek(0x05),
|
|
||||||
system.registers.peek(0x06),
|
|
||||||
system.registers.peek(0x07)
|
|
||||||
));
|
|
||||||
ui.label(format!("V8-F: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ",
|
|
||||||
system.registers.peek(0x08),
|
|
||||||
system.registers.peek(0x09),
|
|
||||||
system.registers.peek(0x0A),
|
|
||||||
system.registers.peek(0x0B),
|
|
||||||
system.registers.peek(0x0C),
|
|
||||||
system.registers.peek(0x0D),
|
|
||||||
system.registers.peek(0x0E),
|
|
||||||
system.registers.peek(0x0F)
|
|
||||||
));
|
|
||||||
ui.label(format!("PC: {:04x}\tI: {:04x}", system.registers.peek_pc(), system.registers.peek_i()));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn video_view(system: &Chip8Computer, ui: &mut Ui) {
|
|
||||||
ui.label("Video goes here");
|
|
||||||
let (resp, painter) = ui.allocate_painter(Vec2::new(400.0, 500.0), egui::Sense::hover());
|
|
||||||
for current_row in 0..32 {
|
|
||||||
for current_col in 0..64 {
|
|
||||||
let data_offset = current_row * 32 + current_col;
|
|
||||||
let origin = ui.next_widget_position();
|
|
||||||
let colour = if system.video_memory.peek(data_offset) {
|
|
||||||
Color32::RED
|
|
||||||
} else {
|
|
||||||
Color32::BLUE
|
|
||||||
};
|
|
||||||
let rect = Rect::from_min_size(origin, Vec2::new(10.0, 10.0));
|
|
||||||
painter.rect_filled(rect, 0.0, colour);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn memory_view(system: &Chip8Computer, ui: &mut Ui) {
|
|
||||||
ui.label("Memory View");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +1,20 @@
|
|||||||
|
|
||||||
pub struct EmmaEGuiState {
|
pub struct GemmaEGuiState {
|
||||||
pub display_video: bool,
|
pub display_video: bool,
|
||||||
pub display_memory: bool,
|
pub display_memory: bool,
|
||||||
pub display_registers: bool,
|
pub display_registers: bool,
|
||||||
|
pub memory_view_min: i32,
|
||||||
|
pub memory_view_max: i32
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EmmaEGuiState {
|
impl Default for GemmaEGuiState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
display_video: true,
|
display_video: true,
|
||||||
display_memory: true,
|
display_memory: true,
|
||||||
display_registers: true,
|
display_registers: true,
|
||||||
|
memory_view_min: 0x00,
|
||||||
|
memory_view_max: 0x100
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
129
gemmaegui/src/bin/support/gemma_egui_support.rs
Normal file
129
gemmaegui/src/bin/support/gemma_egui_support.rs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
use std::fs::read_dir;
|
||||||
|
use std::ops::Index;
|
||||||
|
use std::path::{Display, PathBuf};
|
||||||
|
use egui::{Align, Color32, ComboBox, Direction, Pos2};
|
||||||
|
use egui::Rect;
|
||||||
|
use egui::Vec2;
|
||||||
|
use egui::Ui;
|
||||||
|
use crate::support::gemma_egui_state::GemmaEGuiState;
|
||||||
|
use crate::Chip8Computer;
|
||||||
|
|
||||||
|
const CELL_WIDTH: f32 = 5.0;
|
||||||
|
const CELL_HEIGHT: f32 = 5.0;
|
||||||
|
|
||||||
|
pub struct EGuiFileList {}
|
||||||
|
impl EGuiFileList {
|
||||||
|
pub fn display_path(root: PathBuf, selected_filename: &mut String, ui: &mut Ui) {
|
||||||
|
let mut working_filename = selected_filename.clone();
|
||||||
|
ui.label(format!("Displaying {}", root.to_str().unwrap_or("Unable to Load Path")));
|
||||||
|
|
||||||
|
egui::ComboBox::from_label(format!(
|
||||||
|
"Currently selected string: {}",
|
||||||
|
selected_filename
|
||||||
|
))
|
||||||
|
.selected_text(selected_filename.clone())
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
for option in read_dir(root.as_path()).unwrap() {
|
||||||
|
ui.label(format!("{:?}", option.unwrap().file_name()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GemmaEguiSupport {}
|
||||||
|
|
||||||
|
impl GemmaEguiSupport {
|
||||||
|
pub fn controls_view(mut system: &mut Chip8Computer, state: &mut GemmaEGuiState, ui: &mut Ui) {
|
||||||
|
ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
|
||||||
|
if ui.button("Start").clicked() {
|
||||||
|
println!("Start");
|
||||||
|
|
||||||
|
}
|
||||||
|
if ui.button("Step").clicked() {
|
||||||
|
system.step_system();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.button("Stop").clicked() {
|
||||||
|
println!("STOP");
|
||||||
|
}
|
||||||
|
if ui.button("Reset").clicked() {
|
||||||
|
system.reset();
|
||||||
|
system.video_memory = system.video_memory.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.button("Load initial rom").clicked() {
|
||||||
|
println!("CLICK ON LOAD");
|
||||||
|
// load the bin...
|
||||||
|
let read_bin = std::fs::read(PathBuf::from("resources/roms/1-chip8-logo.ch8")).unwrap();
|
||||||
|
// ...then feed the system.
|
||||||
|
system.load_bytes_to_memory(0x200, &read_bin);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.checkbox(&mut state.display_memory, "Display Memory");
|
||||||
|
ui.checkbox(&mut state.display_video, "Display Video");
|
||||||
|
ui.checkbox(&mut state.display_registers, "Display Registers");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn registers_view(system: &Chip8Computer, ui: &mut Ui) {
|
||||||
|
ui.label(format!("V0-7: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ",
|
||||||
|
system.registers.peek(0x00),
|
||||||
|
system.registers.peek(0x01),
|
||||||
|
system.registers.peek(0x02),
|
||||||
|
system.registers.peek(0x03),
|
||||||
|
system.registers.peek(0x04),
|
||||||
|
system.registers.peek(0x05),
|
||||||
|
system.registers.peek(0x06),
|
||||||
|
system.registers.peek(0x07)
|
||||||
|
));
|
||||||
|
ui.label(format!("V8-F: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ",
|
||||||
|
system.registers.peek(0x08),
|
||||||
|
system.registers.peek(0x09),
|
||||||
|
system.registers.peek(0x0A),
|
||||||
|
system.registers.peek(0x0B),
|
||||||
|
system.registers.peek(0x0C),
|
||||||
|
system.registers.peek(0x0D),
|
||||||
|
system.registers.peek(0x0E),
|
||||||
|
system.registers.peek(0x0F)
|
||||||
|
));
|
||||||
|
ui.label(format!("PC: {:04x}\tI: {:04x}", system.registers.peek_pc(), system.registers.peek_i()));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn video_view(system: &Chip8Computer, ui: &mut Ui) {
|
||||||
|
ui.label("Video goes here");
|
||||||
|
let (resp, painter) = ui.allocate_painter(Vec2::new(400.0, 500.0), egui::Sense::hover());
|
||||||
|
for current_row in 0..64 {
|
||||||
|
for current_col in 0..32 {
|
||||||
|
let data_offset = current_row * 32 + current_col;
|
||||||
|
let x_offset = current_col as f32 * CELL_WIDTH;
|
||||||
|
let y_offset = current_row as f32 * CELL_HEIGHT;
|
||||||
|
let origin = Pos2::new(x_offset, y_offset);
|
||||||
|
let colour = if system.video_memory.peek(data_offset) {
|
||||||
|
Color32::RED
|
||||||
|
} else {
|
||||||
|
Color32::WHITE
|
||||||
|
};
|
||||||
|
let rect = Rect::from_min_size(origin, Vec2::new(CELL_WIDTH, CELL_HEIGHT));
|
||||||
|
painter.rect_filled(rect, 0.0, colour);
|
||||||
|
// println!("DataOffset: [{:04x}] X_Offset: [{:04x}] Y_Offset: [{:04x}] Origin: [{:04x}]x[{:04x}]",
|
||||||
|
// data_offset as u16,
|
||||||
|
// x_offset as u16,
|
||||||
|
// y_offset as u16,
|
||||||
|
// origin.x as u16, origin.y as u16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn memory_view(system: &Chip8Computer, gui_state: &mut GemmaEGuiState, ui: &mut Ui) {
|
||||||
|
ui.label("Memory View");
|
||||||
|
|
||||||
|
for i in (gui_state.memory_view_min..gui_state.memory_view_max).step_by(16) {
|
||||||
|
ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
|
||||||
|
for y in 0..16 {
|
||||||
|
ui.label(format!("{:02x}", system.memory.peek((i + y) as u16)).as_str());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ui.label("Should have **something** to adjust the 'memory window'");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,2 +1,2 @@
|
|||||||
pub mod EmmaEGuiState;
|
pub mod gemma_egui_state;
|
||||||
pub mod GemmaEGuiSupport;
|
pub mod gemma_egui_support;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use imgui::*;
|
|||||||
use sys::{ImColor, ImVec2, ImVector_ImU32};
|
use sys::{ImColor, ImVec2, ImVector_ImU32};
|
||||||
use rand::random;
|
use rand::random;
|
||||||
use gemma::chip8::system_memory::Chip8SystemMemory;
|
use gemma::chip8::system_memory::Chip8SystemMemory;
|
||||||
use support::{emmagui_support::EmmaGui, ui_state::UiState};
|
use support::{emmagui_support::GemmaImguiSupport, ui_state::ImGuiUiState};
|
||||||
|
|
||||||
mod support;
|
mod support;
|
||||||
|
|
||||||
@ -27,12 +27,12 @@ const LIN_KEYS: [(u16, u8); 0x10] = [(537, 0x01),(538, 0x02),(539, 0x03),(540, 0
|
|||||||
fn main() {
|
fn main() {
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
let mut system = Chip8Computer::default();
|
let mut system = Chip8Computer::default();
|
||||||
let mut ui_state = UiState::default();
|
let mut ui_state = ImGuiUiState::default();
|
||||||
let mut last_tick_time = Instant::now();
|
|
||||||
|
|
||||||
support::simple_init(file!(), move |_, ui| {
|
support::simple_init(file!(), move |_, ui| {
|
||||||
let current_time = Instant::now();
|
let current_time = Instant::now();
|
||||||
|
|
||||||
|
// Key Checks
|
||||||
let down_keys = ui.io().keys_down;
|
let down_keys = ui.io().keys_down;
|
||||||
for (key_code, key_reg) in LIN_KEYS {
|
for (key_code, key_reg) in LIN_KEYS {
|
||||||
if down_keys[key_code as usize] {
|
if down_keys[key_code as usize] {
|
||||||
@ -44,8 +44,9 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// println!("KEYS DOWN = {:?}", ui.io().keys_down);
|
|
||||||
let time_since_last_tick = current_time.duration_since(last_tick_time).as_millis();
|
// Next Tick Check
|
||||||
|
let time_since_last_tick = current_time.duration_since(ui_state.last_frame_instant).as_millis();
|
||||||
if ui_state.is_running && time_since_last_tick > ui_state.frame_time as u128 {
|
if ui_state.is_running && time_since_last_tick > ui_state.frame_time as u128 {
|
||||||
match system.state {
|
match system.state {
|
||||||
gemma::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction => {
|
gemma::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction => {
|
||||||
@ -61,26 +62,27 @@ fn main() {
|
|||||||
panic!("System in undefined state.");
|
panic!("System in undefined state.");
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
last_tick_time = current_time;
|
ui_state.last_frame_instant = current_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
EmmaGui::system_controls(&mut system, &mut ui_state, ui);
|
// GUI Parts
|
||||||
|
GemmaImguiSupport::system_controls(&mut system, &mut ui_state, ui);
|
||||||
|
|
||||||
if ui_state.show_registers {
|
if ui_state.show_registers {
|
||||||
EmmaGui::registers_view(&system, ui);
|
GemmaImguiSupport::registers_view(&system, ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ui_state.show_video {
|
if ui_state.show_video {
|
||||||
EmmaGui::video_display(&system, &ui_state, ui);
|
GemmaImguiSupport::video_display(&system, &ui_state, ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ui_state.show_memory {
|
if ui_state.show_memory {
|
||||||
let active_instruction = system.registers.peek_pc();
|
let active_instruction = system.registers.peek_pc();
|
||||||
EmmaGui::hex_memory_display(system.memory.clone(), (0x100, 0x10), active_instruction as i16, ui);
|
GemmaImguiSupport::hex_memory_display(system.memory.clone(), (0x100, 0x10), active_instruction as i16, ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ui_state.show_keypad {
|
if ui_state.show_keypad {
|
||||||
EmmaGui::keypad_display(&system, ui);
|
GemmaImguiSupport::keypad_display(&system, ui);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,11 +9,11 @@ use log::debug;
|
|||||||
use gemma::chip8::computer::Chip8Computer;
|
use gemma::chip8::computer::Chip8Computer;
|
||||||
use gemma::chip8::system_memory::Chip8SystemMemory;
|
use gemma::chip8::system_memory::Chip8SystemMemory;
|
||||||
use gemma::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};
|
use gemma::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};
|
||||||
use crate::UiState;
|
use crate::ImGuiUiState;
|
||||||
|
|
||||||
use super::ui_state;
|
use super::ui_state;
|
||||||
|
|
||||||
pub struct EmmaGui {}
|
pub struct GemmaImguiSupport {}
|
||||||
|
|
||||||
const CELL_WIDTH: i32 = 5i32;
|
const CELL_WIDTH: i32 = 5i32;
|
||||||
const CELL_HEIGHT: i32 = 5i32;
|
const CELL_HEIGHT: i32 = 5i32;
|
||||||
@ -40,8 +40,7 @@ impl GuiFileList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GemmaImguiSupport {
|
||||||
impl EmmaGui {
|
|
||||||
pub fn keypad_display(system_to_display: &Chip8Computer, ui: &Ui) {
|
pub fn keypad_display(system_to_display: &Chip8Computer, ui: &Ui) {
|
||||||
ui.text("Keypad");
|
ui.text("Keypad");
|
||||||
|
|
||||||
@ -59,9 +58,10 @@ impl EmmaGui {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn video_display(system_to_control: &Chip8Computer, gui_state: &UiState, ui: &Ui) {
|
pub fn video_display(system_to_control: &Chip8Computer, gui_state: &ImGuiUiState, ui: &Ui) {
|
||||||
// draw area size
|
// draw area size
|
||||||
let draw_area_size = ui.io().display_size;
|
let draw_area_size = ui.io().display_size;
|
||||||
|
println!("DRAW_AREA_SIZE = {}x{}", draw_area_size[0], draw_area_size[1]);
|
||||||
let cell_width = ((draw_area_size[0] as i32 / 64) * 6) / 10;
|
let cell_width = ((draw_area_size[0] as i32 / 64) * 6) / 10;
|
||||||
let cell_height = ((draw_area_size[1] as i32 / 32) * 6) / 10;
|
let cell_height = ((draw_area_size[1] as i32 / 32) * 6) / 10;
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ impl EmmaGui {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
pub fn system_controls(system_to_control: &mut Chip8Computer, gui_state: &mut UiState, ui: &Ui) {
|
pub fn system_controls(system_to_control: &mut Chip8Computer, gui_state: &mut ImGuiUiState, ui: &Ui) {
|
||||||
ui.window("!!!! CONTROLS !!!!")
|
ui.window("!!!! CONTROLS !!!!")
|
||||||
.size([345.0, 200.0], Condition::FirstUseEver)
|
.size([345.0, 200.0], Condition::FirstUseEver)
|
||||||
.build(|| {
|
.build(|| {
|
||||||
@ -107,7 +107,7 @@ impl EmmaGui {
|
|||||||
// let mut input_file = File::open(Path::new("./1-chip8-logo.ch8")).expect("put 1-chip8-logo.ch8 in this directory");
|
// let mut input_file = File::open(Path::new("./1-chip8-logo.ch8")).expect("put 1-chip8-logo.ch8 in this directory");
|
||||||
let mut input_file = File::open(Path::new(&("resources/roms/".to_string() + &gui_state.filename_to_load))).expect("put 1-chip8-logo.ch8 in this directory");
|
let mut input_file = File::open(Path::new(&("resources/roms/".to_string() + &gui_state.filename_to_load))).expect("put 1-chip8-logo.ch8 in this directory");
|
||||||
input_file.read_to_end(&mut buffer).expect("unable to read file");
|
input_file.read_to_end(&mut buffer).expect("unable to read file");
|
||||||
system_to_control.load_bytes_to_memory(0x200, buffer.into());
|
system_to_control.load_bytes_to_memory(0x200, (&buffer).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
|
use std::time::Instant;
|
||||||
use imgui::ImColor32;
|
use imgui::ImColor32;
|
||||||
|
|
||||||
|
pub struct ImGuiUiState {
|
||||||
pub struct UiState {
|
|
||||||
pub show_registers: bool,
|
pub show_registers: bool,
|
||||||
pub show_memory: bool,
|
pub show_memory: bool,
|
||||||
pub show_video: bool,
|
pub show_video: bool,
|
||||||
@ -11,11 +11,12 @@ pub struct UiState {
|
|||||||
pub off_colour: ImColor32,
|
pub off_colour: ImColor32,
|
||||||
pub is_running: bool,
|
pub is_running: bool,
|
||||||
pub frame_time: f32,
|
pub frame_time: f32,
|
||||||
|
pub last_frame_instant: Instant
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for UiState {
|
impl Clone for ImGuiUiState {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
UiState {
|
ImGuiUiState {
|
||||||
show_registers: self.show_registers,
|
show_registers: self.show_registers,
|
||||||
show_memory: self.show_memory,
|
show_memory: self.show_memory,
|
||||||
show_video: self.show_video,
|
show_video: self.show_video,
|
||||||
@ -25,13 +26,14 @@ impl Clone for UiState {
|
|||||||
off_colour: self.off_colour,
|
off_colour: self.off_colour,
|
||||||
is_running: self.is_running,
|
is_running: self.is_running,
|
||||||
frame_time: self.frame_time,
|
frame_time: self.frame_time,
|
||||||
|
last_frame_instant: self.last_frame_instant
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for UiState {
|
impl Default for ImGuiUiState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
UiState {
|
ImGuiUiState {
|
||||||
show_registers: false,
|
show_registers: false,
|
||||||
show_memory: false,
|
show_memory: false,
|
||||||
show_video: true,
|
show_video: true,
|
||||||
@ -41,6 +43,7 @@ impl Default for UiState {
|
|||||||
off_colour: ImColor32::from_rgb(0x00, 0xff, 0xff),
|
off_colour: ImColor32::from_rgb(0x00, 0xff, 0xff),
|
||||||
is_running: false,
|
is_running: false,
|
||||||
frame_time: 10.0,
|
frame_time: 10.0,
|
||||||
|
last_frame_instant: Instant::now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
resources/test/test_reset_clears_video.asc
Normal file
32
resources/test/test_reset_clears_video.asc
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
32
resources/test/test_video_write_checkerboard.asc
Normal file
32
resources/test/test_video_write_checkerboard.asc
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
Loading…
x
Reference in New Issue
Block a user