fixes reset from exection completion
adds 'default.oc8' adds control window size to state for load/save of settings maybe? moves TestCompressionTool code into InstructionUtil remove gemmautil and moves into gemma
This commit is contained in:
parent
67ca71ccb7
commit
cddbe0c46e
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -1789,6 +1789,8 @@ dependencies = [
|
|||||||
"flate2",
|
"flate2",
|
||||||
"hex",
|
"hex",
|
||||||
"log",
|
"log",
|
||||||
|
"pest",
|
||||||
|
"pest_derive",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
"rand 0.9.0-alpha.2",
|
"rand 0.9.0-alpha.2",
|
||||||
"serde",
|
"serde",
|
||||||
@ -1845,16 +1847,6 @@ dependencies = [
|
|||||||
"gemma",
|
"gemma",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gemmautil"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"clap",
|
|
||||||
"gemma",
|
|
||||||
"pest",
|
|
||||||
"pest_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.7"
|
version = "0.14.7"
|
||||||
|
|||||||
@ -5,7 +5,6 @@ members = [
|
|||||||
"gemmaimgui",
|
"gemmaimgui",
|
||||||
"gemmatelnet",
|
"gemmatelnet",
|
||||||
"gemmasdl2",
|
"gemmasdl2",
|
||||||
"gemmautil",
|
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
|
|||||||
@ -16,3 +16,5 @@ serde_json.workspace = true
|
|||||||
flate2 = "1.0"
|
flate2 = "1.0"
|
||||||
clap = { version = "4.5.20", features = ["derive"] }
|
clap = { version = "4.5.20", features = ["derive"] }
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
|
pest = { version = "2.7.14" }
|
||||||
|
pest_derive = { version = "2.7.14"}
|
||||||
@ -2,6 +2,7 @@ use gemma::chip8::util::InstructionUtil;
|
|||||||
use gemma::chip8::{instructions::Chip8CpuInstructions, quirk_modes::QuirkMode};
|
use gemma::chip8::{instructions::Chip8CpuInstructions, quirk_modes::QuirkMode};
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use clap::{Arg, Command};
|
||||||
// Ch8Asm
|
// Ch8Asm
|
||||||
// Converts well formed CH8ASM.
|
// Converts well formed CH8ASM.
|
||||||
// no variables.
|
// no variables.
|
||||||
@ -16,9 +17,26 @@ use pest_derive::Parser;
|
|||||||
#[grammar = "chip8_asm.pest"]
|
#[grammar = "chip8_asm.pest"]
|
||||||
pub struct Chip8AsmParser;
|
pub struct Chip8AsmParser;
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Taxation is Theft");
|
println!("Taxation is Theft");
|
||||||
|
|
||||||
|
let matches = Command::new("Chip8Assembler")
|
||||||
|
.version("0.0.1")
|
||||||
|
.author("Trevor Merritt <trevor.merritt@geekback.dev>")
|
||||||
|
.about("Tool to assemble simple ASM into Chip8 Machine code")
|
||||||
|
.arg(
|
||||||
|
Arg::new("input-file")
|
||||||
|
.help("Name of file to assemble")
|
||||||
|
.required(true)
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("output-file")
|
||||||
|
.help("Name of file to create")
|
||||||
|
.required(false)
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
let unparsed = fs::read_to_string("resources/test/gemma_disassembler_manual_document.asc")
|
let unparsed = fs::read_to_string("resources/test/gemma_disassembler_manual_document.asc")
|
||||||
.expect("Unable to read input");
|
.expect("Unable to read input");
|
||||||
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
use clap::{Parser, Subcommand};
|
|
||||||
use flate2::{write::{GzEncoder, GzDecoder}, Compression};
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::io::{self, Write};
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
|
||||||
#[command(author, version, about = "Compress or decompress a string", long_about = None)]
|
|
||||||
struct Cli {
|
|
||||||
/// Subcommand: either 'compress' or 'decompress'
|
|
||||||
#[command(subcommand)]
|
|
||||||
command: Commands,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
enum Commands {
|
|
||||||
/// Compress the input string
|
|
||||||
Compress {
|
|
||||||
/// The string to compress
|
|
||||||
input: String,
|
|
||||||
},
|
|
||||||
/// Decompress the input string (must be compressed format)
|
|
||||||
Decompress {
|
|
||||||
/// The compressed string to decompress, in hex format
|
|
||||||
input: String,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compress_string(input: &str) -> Vec<u8> {
|
|
||||||
let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
|
|
||||||
encoder.write_all(input.as_bytes()).expect("Compression failed");
|
|
||||||
encoder.finish().expect("Failed to finish compression")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decompress_string(input: &[u8]) -> String {
|
|
||||||
let mut decoder = GzDecoder::new(Vec::new());
|
|
||||||
decoder.write_all(input).expect("Decompression failed");
|
|
||||||
let decompressed_data = decoder.finish().expect("Failed to finish decompression");
|
|
||||||
String::from_utf8(decompressed_data).expect("Invalid UTF-8")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let cli = Cli::parse();
|
|
||||||
|
|
||||||
match cli.command {
|
|
||||||
Commands::Compress { input } => {
|
|
||||||
let compressed = compress_string(&input);
|
|
||||||
// Convert to hex format for easier readability in the terminal
|
|
||||||
println!("Compressed (hex): {:?} / from {}b to {}b", hex::encode(compressed.clone()), input.len(), compressed.len());
|
|
||||||
}
|
|
||||||
Commands::Decompress { input } => {
|
|
||||||
// Decode hex string back to bytes
|
|
||||||
let compressed_bytes = hex::decode(input).expect("Invalid hex input");
|
|
||||||
let decompressed = decompress_string(&compressed_bytes);
|
|
||||||
println!("Decompressed: {}", decompressed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
41
gemma/src/bin/tct.rs
Normal file
41
gemma/src/bin/tct.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
use clap::{ArgGroup, Parser};
|
||||||
|
use gemma::chip8::util::InstructionUtil;
|
||||||
|
/// TCT
|
||||||
|
/// Test Compression Tool
|
||||||
|
///
|
||||||
|
/// A Tool to take a plaintext JSON object of the memory state
|
||||||
|
/// for the CHIP-8 emulator and compress the input into compressed
|
||||||
|
/// text. Test sizes go from ~22k to ~1k which makes a difference over
|
||||||
|
/// hundreds of tests
|
||||||
|
/// Written by Trevor Merritt and optimized by ChatGPT
|
||||||
|
|
||||||
|
/// Simple file compression and decompression tool
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(author, version, about)]
|
||||||
|
#[clap(group(
|
||||||
|
ArgGroup::new("mode")
|
||||||
|
.required(true)
|
||||||
|
.args(["compress", "decompress"]),
|
||||||
|
))]
|
||||||
|
struct Cli {
|
||||||
|
/// The filename to process
|
||||||
|
filename: String,
|
||||||
|
|
||||||
|
/// Compress the file
|
||||||
|
#[arg(short = 'c', long = "compress", group = "mode")]
|
||||||
|
compress: bool,
|
||||||
|
|
||||||
|
/// Decompress the file
|
||||||
|
#[arg(short = 'd', long = "decompress", group = "mode")]
|
||||||
|
decompress: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
if cli.compress {
|
||||||
|
InstructionUtil::compress_file(cli.filename).expect("Derp. Can't do that.");
|
||||||
|
} else if cli.decompress {
|
||||||
|
InstructionUtil::decompress_file(cli.filename).expect("Derp. Can't do that.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,8 @@
|
|||||||
|
use super::{
|
||||||
|
cpu_states::Chip8CpuStates, instructions::Chip8CpuInstructions,
|
||||||
|
system_memory::Chip8SystemMemory, video::Chip8Video,
|
||||||
|
};
|
||||||
|
use crate::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction;
|
||||||
use crate::chip8::delay_timer::DelayTimer;
|
use crate::chip8::delay_timer::DelayTimer;
|
||||||
use crate::chip8::keypad::Keypad;
|
use crate::chip8::keypad::Keypad;
|
||||||
use crate::chip8::quirk_modes::QuirkMode;
|
use crate::chip8::quirk_modes::QuirkMode;
|
||||||
@ -7,12 +12,7 @@ use crate::chip8::stack::Chip8Stack;
|
|||||||
use log::debug;
|
use log::debug;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::{
|
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
|
||||||
cpu_states::Chip8CpuStates, instructions::Chip8CpuInstructions,
|
|
||||||
system_memory::Chip8SystemMemory, video::Chip8Video,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
|
||||||
pub struct Chip8Computer {
|
pub struct Chip8Computer {
|
||||||
pub num_cycles: i32,
|
pub num_cycles: i32,
|
||||||
pub memory: Chip8SystemMemory,
|
pub memory: Chip8SystemMemory,
|
||||||
@ -52,6 +52,7 @@ impl Chip8Computer {
|
|||||||
self.stack.reset();
|
self.stack.reset();
|
||||||
self.memory.reset(quirk_mode);
|
self.memory.reset(quirk_mode);
|
||||||
self.quirk_mode = quirk_mode;
|
self.quirk_mode = quirk_mode;
|
||||||
|
self.state = WaitingForInstruction;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dump_keypad_to_string(&self) -> String {
|
pub fn dump_keypad_to_string(&self) -> String {
|
||||||
@ -117,8 +118,19 @@ impl Chip8Computer {
|
|||||||
Chip8CpuStates::WaitingForKey => {
|
Chip8CpuStates::WaitingForKey => {
|
||||||
debug!("waiting for a key press...");
|
debug!("waiting for a key press...");
|
||||||
}
|
}
|
||||||
|
Chip8CpuStates::ExecutionComplete => {
|
||||||
|
debug!("Execution has completed.");
|
||||||
|
self.num_cycles += 1;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn serialize(&self) -> String {
|
||||||
|
serde_json::to_string(&self).expect("Serialization failed")
|
||||||
|
}
|
||||||
|
pub fn deserialize(from: String) -> Chip8Computer {
|
||||||
|
serde_json::from_str(&from).expect("Failed to deserialize")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Copy, Default, Serialize, Deserialize, Debug, PartialEq)]
|
||||||
pub enum Chip8CpuStates {
|
pub enum Chip8CpuStates {
|
||||||
#[default]
|
#[default]
|
||||||
WaitingForInstruction,
|
WaitingForInstruction,
|
||||||
WaitingForKey,
|
WaitingForKey,
|
||||||
ExecutingInstruction,
|
ExecutingInstruction,
|
||||||
Error,
|
Error,
|
||||||
|
ExecutionComplete,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)]
|
||||||
pub struct DelayTimer {
|
pub struct DelayTimer {
|
||||||
counter: u8,
|
counter: u8,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use std::ascii::AsciiExt;
|
|||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::ops::BitAnd;
|
use std::ops::BitAnd;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
use crate::chip8::cpu_states::Chip8CpuStates;
|
||||||
/*
|
/*
|
||||||
nnn or addr - A 12-bit value, the lowest 12 bits of the instruction
|
nnn or addr - A 12-bit value, the lowest 12 bits of the instruction
|
||||||
n or nibble - A 4-bit value, the lowest 4 bits of the instruction
|
n or nibble - A 4-bit value, the lowest 4 bits of the instruction
|
||||||
@ -326,9 +326,9 @@ impl Chip8CpuInstructions {
|
|||||||
Chip8CpuInstructions::HIGH => INST_HIGH,
|
Chip8CpuInstructions::HIGH => INST_HIGH,
|
||||||
Chip8CpuInstructions::ORY(_, _) => INST_ORY,
|
Chip8CpuInstructions::ORY(_, _) => INST_ORY,
|
||||||
JPX(_, _) => INST_JPX,
|
JPX(_, _) => INST_JPX,
|
||||||
|
DW(_) => INST_DW,
|
||||||
XXXXERRORINSTRUCTION => "XX ERROR XX",
|
XXXXERRORINSTRUCTION => "XX ERROR XX",
|
||||||
SCU(_) => INST_SCU,
|
SCU(_) => INST_SCU,
|
||||||
DW(_) => INST_DW,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -681,7 +681,7 @@ impl Chip8CpuInstructions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn execute(&self, input: &mut Chip8Computer) -> Chip8Computer {
|
pub fn execute(&self, input: &mut Chip8Computer) -> Chip8Computer {
|
||||||
// print!("INSTRUCTION {}", self);
|
println!("INSTRUCTION {}", self);
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
let start_pc = input.registers.peek_pc();
|
let start_pc = input.registers.peek_pc();
|
||||||
input.registers.poke_pc(start_pc + 2);
|
input.registers.poke_pc(start_pc + 2);
|
||||||
@ -700,6 +700,12 @@ impl Chip8CpuInstructions {
|
|||||||
}
|
}
|
||||||
// 0x1nnn Jump to Address
|
// 0x1nnn Jump to Address
|
||||||
Chip8CpuInstructions::JPA(new_address) => {
|
Chip8CpuInstructions::JPA(new_address) => {
|
||||||
|
// if the jump is to the same address as we are at, we are in an infinite
|
||||||
|
// loop and the program is effectively done. stop the CPU.
|
||||||
|
if *new_address == start_pc {
|
||||||
|
input.state = Chip8CpuStates::ExecutionComplete;
|
||||||
|
println!("Execution complete.");
|
||||||
|
}
|
||||||
input.registers.poke_pc(*new_address);
|
input.registers.poke_pc(*new_address);
|
||||||
}
|
}
|
||||||
// 0x2nnn Call Subroutine
|
// 0x2nnn Call Subroutine
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::constants::CHIP8_KEYBOARD;
|
use crate::constants::CHIP8_KEYBOARD;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Copy, Default, Serialize, Deserialize, Debug, PartialEq)]
|
||||||
pub struct Keypad {
|
pub struct Keypad {
|
||||||
keys: [bool; 0x10],
|
keys: [bool; 0x10],
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
#[derive(Default, Clone, Serialize, Deserialize, Copy, Debug)]
|
#[derive(Default, Clone, Serialize, Deserialize, Copy, Debug, PartialEq)]
|
||||||
pub enum QuirkMode {
|
pub enum QuirkMode {
|
||||||
Chip8,
|
Chip8,
|
||||||
XOChip,
|
XOChip,
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
/// Registers. numbered 1-16 publicly.
|
/// Registers. numbered 1-16 publicly.
|
||||||
/// Privately using zero base array so -1 to shift from pub to priv.
|
/// Privately using zero base array so -1 to shift from pub to priv.
|
||||||
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)]
|
||||||
pub struct Chip8Registers {
|
pub struct Chip8Registers {
|
||||||
pub registers: [u8; 16],
|
pub registers: [u8; 16],
|
||||||
pub i_register: u16,
|
pub i_register: u16,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use log::trace;
|
use log::trace;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)]
|
||||||
pub struct SoundTimer {
|
pub struct SoundTimer {
|
||||||
counter: i32,
|
counter: i32,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Default, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Default, Serialize, Deserialize, Debug, PartialEq)]
|
||||||
pub struct Chip8Stack {
|
pub struct Chip8Stack {
|
||||||
items: Vec<u16>,
|
items: Vec<u16>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use serde::Serialize;
|
|||||||
use super::quirk_modes;
|
use super::quirk_modes;
|
||||||
use super::quirk_modes::QuirkMode;
|
use super::quirk_modes::QuirkMode;
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
|
||||||
pub struct Chip8SystemMemory {
|
pub struct Chip8SystemMemory {
|
||||||
memory: Vec<u8>,
|
memory: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,11 @@
|
|||||||
|
use flate2::read::ZlibDecoder;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use flate2::write::ZlibEncoder;
|
||||||
|
use flate2::Compression;
|
||||||
|
|
||||||
pub struct InstructionUtil {}
|
pub struct InstructionUtil {}
|
||||||
|
|
||||||
impl InstructionUtil {
|
impl InstructionUtil {
|
||||||
@ -29,7 +37,6 @@ impl InstructionUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn join_bytes(high: u8, low: u8) -> u16 {
|
pub fn join_bytes(high: u8, low: u8) -> u16 {
|
||||||
|
|
||||||
(high as u16) << 8 | low as u16
|
(high as u16) << 8 | low as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,4 +68,122 @@ impl InstructionUtil {
|
|||||||
pub fn read_upper_byte_lower_nibble(to_read_from: u16) -> u8 {
|
pub fn read_upper_byte_lower_nibble(to_read_from: u16) -> u8 {
|
||||||
((to_read_from & 0x0f00) >> 8) as u8
|
((to_read_from & 0x0f00) >> 8) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn compress_file<P: AsRef<Path>>(to_compress: P) -> io::Result<()> {
|
||||||
|
let to_compress_path = to_compress.as_ref();
|
||||||
|
let target_filename = format!("{}.tct", to_compress_path.display());
|
||||||
|
println!(
|
||||||
|
"Compressing file: {} to {}",
|
||||||
|
to_compress_path.display(),
|
||||||
|
target_filename
|
||||||
|
);
|
||||||
|
|
||||||
|
// Open the source file
|
||||||
|
let mut source_file = File::open(&to_compress_path)?;
|
||||||
|
|
||||||
|
// Read the entire file into a buffer
|
||||||
|
let mut read_buffer = Vec::new();
|
||||||
|
source_file.read_to_end(&mut read_buffer)?;
|
||||||
|
|
||||||
|
// Initialize the encoder and compress the read data
|
||||||
|
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
|
||||||
|
encoder.write_all(&read_buffer)?;
|
||||||
|
let encoded_data = encoder.finish()?;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Compressed {} bytes into {} bytes.",
|
||||||
|
read_buffer.len(),
|
||||||
|
encoded_data.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Write the encoded data to the target file
|
||||||
|
let mut target_file = File::create(target_filename)?;
|
||||||
|
target_file.write_all(&encoded_data)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decompress_file_to_string<P: AsRef<Path>>(to_decompress: P) -> io::Result<String> {
|
||||||
|
let to_decompress_path = to_decompress.as_ref();
|
||||||
|
|
||||||
|
// Ensure the file has the expected `.tct` extension
|
||||||
|
if to_decompress_path.extension() != Some("tct".as_ref()) {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"File must have a .tct extension",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Decompressing file: {} to string",
|
||||||
|
to_decompress_path.display()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Open the compressed file and set up the decoder
|
||||||
|
let compressed_file = File::open(&to_decompress_path)?;
|
||||||
|
let mut decoder = ZlibDecoder::new(compressed_file);
|
||||||
|
|
||||||
|
// Read decompressed data
|
||||||
|
let mut decompressed_data = Vec::new();
|
||||||
|
decoder.read_to_end(&mut decompressed_data)?;
|
||||||
|
|
||||||
|
// Convert decompressed byte data to a UTF-8 String
|
||||||
|
match String::from_utf8(decompressed_data) {
|
||||||
|
Ok(decompressed_string) => {
|
||||||
|
// Optionally, write decompressed data to a new file
|
||||||
|
println!(
|
||||||
|
"Decompressed {} bytes into {} bytes.",
|
||||||
|
to_decompress_path.metadata()?.len(),
|
||||||
|
decompressed_string.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(decompressed_string)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// Handle invalid UTF-8 data (if the decompressed data isn't valid UTF-8)
|
||||||
|
Err(io::Error::new(io::ErrorKind::InvalidData, e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn decompress_file<P: AsRef<Path>>(to_decompress: P) -> io::Result<()> {
|
||||||
|
let to_decompress_path = to_decompress.as_ref();
|
||||||
|
|
||||||
|
// Ensure the file has the expected `.tct` extension
|
||||||
|
if to_decompress_path.extension() != Some("tct".as_ref()) {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"File must have a .tct extension",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive the output filename by removing the `.tct` extension
|
||||||
|
let mut output_filename = PathBuf::from(to_decompress_path);
|
||||||
|
output_filename.set_extension("untct"); // Remove .tct extension to restore original file name
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Decompressing file: {} to {}",
|
||||||
|
to_decompress_path.display(),
|
||||||
|
output_filename.display()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Open the compressed file and set up the decoder
|
||||||
|
let compressed_file = File::open(&to_decompress_path)?;
|
||||||
|
let mut decoder = ZlibDecoder::new(compressed_file);
|
||||||
|
|
||||||
|
// Read decompressed data
|
||||||
|
let mut decompressed_data = Vec::new();
|
||||||
|
decoder.read_to_end(&mut decompressed_data)?;
|
||||||
|
|
||||||
|
// Write decompressed data to the output file
|
||||||
|
let mut output_file = File::create(&output_filename)?;
|
||||||
|
output_file.write_all(&decompressed_data)?;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Decompressed {} bytes into {} bytes.",
|
||||||
|
to_decompress_path.metadata()?.len(),
|
||||||
|
decompressed_data.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,13 +6,13 @@ use crate::constants::{
|
|||||||
use log::debug;
|
use log::debug;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)]
|
||||||
pub enum Chip8VideoModes {
|
pub enum Chip8VideoModes {
|
||||||
LowRes,
|
LowRes,
|
||||||
HighRes,
|
HighRes,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
|
||||||
pub struct Chip8Video {
|
pub struct Chip8Video {
|
||||||
memory: Vec<bool>,
|
memory: Vec<bool>,
|
||||||
pub has_frame_changed: bool,
|
pub has_frame_changed: bool,
|
||||||
@ -36,7 +36,8 @@ impl Chip8Video {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_lowres(&mut self) {
|
pub fn set_lowres(&mut self) {
|
||||||
self.current_res = LowRes
|
self.current_res = LowRes;
|
||||||
|
self.cls();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_screen_resolution(&mut self) -> Chip8VideoModes {
|
pub fn get_screen_resolution(&mut self) -> Chip8VideoModes {
|
||||||
@ -152,7 +153,6 @@ impl Chip8Video {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_right(&mut self) {
|
pub fn scroll_right(&mut self) {
|
||||||
println!("SCROLLRIGHTPRE:::[{}]", self.format_as_string());
|
|
||||||
let (width, height) = self.get_resolution();
|
let (width, height) = self.get_resolution();
|
||||||
|
|
||||||
for current_row in 0..height {
|
for current_row in 0..height {
|
||||||
@ -168,11 +168,9 @@ impl Chip8Video {
|
|||||||
// Clear the first 4 pixels in the current row
|
// Clear the first 4 pixels in the current row
|
||||||
self.memory[row_offset..row_offset + 4].fill(false);
|
self.memory[row_offset..row_offset + 4].fill(false);
|
||||||
}
|
}
|
||||||
println!("SCROLLRIGHTPOST:::[{}]", self.format_as_string());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_left(&mut self) {
|
pub fn scroll_left(&mut self) {
|
||||||
println!("SCROLLLEFTPRE:::[{}]", self.format_as_string());
|
|
||||||
let (width, height) = self.get_resolution();
|
let (width, height) = self.get_resolution();
|
||||||
|
|
||||||
for current_row in 0..height {
|
for current_row in 0..height {
|
||||||
@ -181,7 +179,6 @@ impl Chip8Video {
|
|||||||
let target: usize = (row_offset + current_column) as usize;
|
let target: usize = (row_offset + current_column) as usize;
|
||||||
let source: usize = target + 4;
|
let source: usize = target + 4;
|
||||||
self.memory[target] = self.memory[source];
|
self.memory[target] = self.memory[source];
|
||||||
println!("Moving from {source} to {target}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let clear_start: usize = (row_offset + width - 4) as usize;
|
let clear_start: usize = (row_offset + width - 4) as usize;
|
||||||
@ -189,7 +186,6 @@ impl Chip8Video {
|
|||||||
|
|
||||||
self.memory[clear_start..clear_end].fill(false);
|
self.memory[clear_start..clear_end].fill(false);
|
||||||
}
|
}
|
||||||
println!("SCROLLLEFTPOST:::[{}]", self.format_as_string());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_up(&mut self, how_far: &u8) {
|
pub fn scroll_up(&mut self, how_far: &u8) {
|
||||||
@ -240,7 +236,7 @@ impl Default for Chip8Video {
|
|||||||
Chip8Video {
|
Chip8Video {
|
||||||
memory: mem,
|
memory: mem,
|
||||||
has_frame_changed: false,
|
has_frame_changed: false,
|
||||||
current_res: Chip8VideoModes::LowRes,
|
current_res: LowRes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,26 +1,24 @@
|
|||||||
|
use std::fs::File;
|
||||||
use flate2::write::{GzDecoder, GzEncoder};
|
use flate2::write::{GzDecoder, GzEncoder};
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
use gemma::chip8::computer::Chip8Computer;
|
use gemma::chip8::computer::Chip8Computer;
|
||||||
|
use gemma::chip8::util::InstructionUtil;
|
||||||
|
use std::io;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
|
||||||
fn load_result(to_load: &str) -> String {
|
fn load_result(to_load: &str) -> String {
|
||||||
let full_path = format!("resources/test/state/{}", to_load);
|
let full_path = format!("../resources/test/state/{}", to_load);
|
||||||
println!("Loading state => (([{}]))", full_path);
|
println!("Loading state => (([{}]))", full_path);
|
||||||
std::fs::read_to_string(full_path).unwrap()
|
std::fs::read_to_string(full_path).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_compressed_result(file_path: &str) -> io::Result<String> {
|
fn load_compressed_result(file_path: &str) -> io::Result<String> {
|
||||||
// Load the compressed file contents
|
let full_path = format!("../resources/test/state/{}", file_path);
|
||||||
let compressed_data = fs::read(file_path)?;
|
println!(
|
||||||
|
"ATTEMPTING TO LOAD {} AS A COMPRESSED TEST RESULT.",
|
||||||
// Create a GzDecoder to uncompress the data
|
full_path
|
||||||
let mut decoder = GzDecoder::new(&compressed_data[..]);
|
);
|
||||||
let mut decompressed_data = String::new();
|
InstructionUtil::decompress_file_to_string(full_path)
|
||||||
|
|
||||||
// Read the decompressed data directly into a String
|
|
||||||
decoder.read_to_string(&mut decompressed_data)?;
|
|
||||||
|
|
||||||
Ok(decompressed_data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_rom(to_load: &str) -> Vec<u8> {
|
fn load_rom(to_load: &str) -> Vec<u8> {
|
||||||
@ -33,21 +31,21 @@ fn test_serialization_round_trip() {
|
|||||||
let expected_json = load_result("smoke_001_round_trip_serialize_deserialize.json");
|
let expected_json = load_result("smoke_001_round_trip_serialize_deserialize.json");
|
||||||
|
|
||||||
// Serialize the Chip8Computer instance
|
// Serialize the Chip8Computer instance
|
||||||
let serialized = serde_json::to_string(&original_computer).expect("Serialization failed");
|
let serialized = Chip8Computer::serialize(&original_computer);
|
||||||
|
let deserialized = Chip8Computer::deserialize(expected_json.clone());
|
||||||
|
|
||||||
// Compare the serialized output to the expected JSON
|
// Compare the serialized output to the expected JSON
|
||||||
println!("Serialized Output: [{}]", serialized);
|
println!("Serialized Output: [{}]", serialized);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
serialized.trim(),
|
serialized.trim(),
|
||||||
expected_json.trim(),
|
expected_json.clone().trim(),
|
||||||
"Serialized output does not match expected JSON"
|
"Serialized output does not match expected JSON"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Deserialize back to Chip8Computer and assert equality
|
// Deserialize back to Chip8Computer and assert equality
|
||||||
let deserialized_computer: Chip8Computer =
|
|
||||||
serde_json::from_str(&serialized).expect("Deserialization failed");
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
deserialized_computer, original_computer,
|
deserialized, original_computer,
|
||||||
"Deserialized instance does not match the original"
|
"Deserialized instance does not match the original"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -55,9 +53,9 @@ fn test_serialization_round_trip() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn computer_001_system_zero_state() {
|
fn computer_001_system_zero_state() {
|
||||||
let x = Chip8Computer::new();
|
let x = Chip8Computer::new();
|
||||||
let expected_string = load_compressed_result("smoke_002_round_trip_serialize_deserialize.tflt");
|
let expected_string = load_compressed_result("computer_001_system_zero_state.tct")
|
||||||
let serialized = serde_json::to_string(&x).unwrap();
|
.expect("Unable to read result");
|
||||||
|
let serialized = x.serialize();
|
||||||
assert_eq!(serialized, expected_string);
|
assert_eq!(serialized, expected_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@ -35,11 +35,13 @@ fn decoder_test_invalid_instructions() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for i in invalid_to_encode {
|
for i in invalid_to_encode {
|
||||||
assert_eq!(Chip8CpuInstructions::decode(i, &Chip8).encode(), 0xffff);
|
println!("TESTING 0x{i:04x}");
|
||||||
|
println!("DECOODED TO {:?}", Chip8CpuInstructions::decode(i, &Chip8));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
Chip8CpuInstructions::decode(i, &Chip8),
|
Chip8CpuInstructions::decode(i, &Chip8),
|
||||||
Chip8CpuInstructions::XXXXERRORINSTRUCTION
|
Chip8CpuInstructions::DW(i)
|
||||||
));
|
) ||
|
||||||
|
matches!(Chip8CpuInstructions::decode(i, &Chip8), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +74,6 @@ fn main() {
|
|||||||
system.wait_for_instruction();
|
system.wait_for_instruction();
|
||||||
} else {
|
} else {
|
||||||
// do we need to release it?
|
// do we need to release it?
|
||||||
|
|
||||||
if system.is_key_pressed(key_reg) {
|
if system.is_key_pressed(key_reg) {
|
||||||
system.release_key(key_reg);
|
system.release_key(key_reg);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,8 @@ use log::debug;
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use imgui::sys::ImGuiIO;
|
||||||
|
use gemma::chip8::instructions::Chip8CpuInstructions;
|
||||||
|
|
||||||
const ROM_ROOT: &str = "resources/roms";
|
const ROM_ROOT: &str = "resources/roms";
|
||||||
|
|
||||||
@ -43,42 +45,21 @@ impl GemmaImguiSupport {
|
|||||||
pub fn video_display(system_to_control: &Chip8Computer, gui_state: &ImGuiUiState, ui: &Ui) {
|
pub fn video_display(system_to_control: &Chip8Computer, gui_state: &ImGuiUiState, ui: &Ui) {
|
||||||
// draw area size
|
// draw area size
|
||||||
let (width, height) = system_to_control.video_memory.get_resolution();
|
let (width, height) = system_to_control.video_memory.get_resolution();
|
||||||
let draw_area_size = ui.io().display_size;
|
ui.window("Video")
|
||||||
|
.size([gui_state.video_window_size[0] as f32, gui_state.video_window_size[1] as f32], Condition::Once)
|
||||||
|
.build(|| {
|
||||||
|
let draw_area_size = ui.window_size();
|
||||||
|
let draw_offset = ui.window_pos();
|
||||||
|
// now lets move the draw_offset by 0,20 to get it off the window title bar
|
||||||
|
let draw_offset = [draw_offset[0], draw_offset[1] + 20.0];
|
||||||
|
// and reduce the draw area size by the same values. {}
|
||||||
|
let draw_area_size = [draw_area_size[0], draw_area_size[1] - 20.0];
|
||||||
// println!("DRAW_AREA_SIZE = {}x{}", draw_area_size[0], draw_area_size[1]);
|
// println!("DRAW_AREA_SIZE = {}x{}", draw_area_size[0], draw_area_size[1]);
|
||||||
let cell_width = ((draw_area_size[0] as i32 / width) * 6) / 10;
|
let cell_width = ((draw_area_size[0] as i32 / width));
|
||||||
let cell_height = ((draw_area_size[1] as i32 / height) * 6) / 10;
|
let cell_height = ((draw_area_size[1] as i32 / height));
|
||||||
|
|
||||||
let origin = ui.cursor_pos();
|
let origin = draw_offset;
|
||||||
let fg = ui.get_foreground_draw_list();
|
let fg = ui.get_foreground_draw_list();
|
||||||
if system_to_control.video_memory.is_highres() {
|
|
||||||
// ui.text("High Def Video here");
|
|
||||||
for current_row in 0..=height {
|
|
||||||
let y_offset = origin[1] as i32 + (current_row * cell_height);
|
|
||||||
for current_column in 0..=width {
|
|
||||||
let x_offset = origin[0] as i32 + (current_column * cell_width);
|
|
||||||
let current_origin = [x_offset as f32, y_offset as f32];
|
|
||||||
let current_limit = [
|
|
||||||
(x_offset + cell_width) as f32,
|
|
||||||
(y_offset + cell_height) as f32,
|
|
||||||
];
|
|
||||||
let memory_offset = (current_row * width + current_column) as u16;
|
|
||||||
let to_render = system_to_control.video_memory.peek(memory_offset);
|
|
||||||
let color: ImColor32 = if to_render {
|
|
||||||
gui_state.on_colour
|
|
||||||
} else {
|
|
||||||
gui_state.off_colour
|
|
||||||
};
|
|
||||||
fg.add_rect_filled_multicolor(
|
|
||||||
current_origin,
|
|
||||||
current_limit,
|
|
||||||
color,
|
|
||||||
color,
|
|
||||||
color,
|
|
||||||
color,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for current_row in 0..height {
|
for current_row in 0..height {
|
||||||
let y_offset = origin[1] as i32 + (current_row * cell_height);
|
let y_offset = origin[1] as i32 + (current_row * cell_height);
|
||||||
for current_column in 0..width {
|
for current_column in 0..width {
|
||||||
@ -105,20 +86,92 @@ impl GemmaImguiSupport {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}).expect("cant draw the video i guess");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn quirks_picker(system: &mut Chip8ComputerManager,
|
||||||
|
ui: &Ui) {
|
||||||
|
let selectors = [Chip8, SChipModern, XOChip];
|
||||||
|
for current_selector in selectors {
|
||||||
|
let mut working_selector =
|
||||||
|
ui.selectable_config(current_selector.clone().to_string());
|
||||||
|
match system.quirks_mode() {
|
||||||
|
Chip8 => {
|
||||||
|
working_selector = working_selector.selected(true);
|
||||||
|
}
|
||||||
|
SChipModern => {
|
||||||
|
working_selector = working_selector.selected(true);
|
||||||
|
}
|
||||||
|
XOChip => {
|
||||||
|
working_selector = working_selector.selected(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn system_controls(
|
if working_selector.build() {
|
||||||
system_to_control: &mut Chip8ComputerManager,
|
system.reset(current_selector);
|
||||||
|
println!("CLICK ON {}", ¤t_selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn control_pickers(
|
||||||
|
system: &mut Chip8ComputerManager,
|
||||||
gui_state: &mut ImGuiUiState,
|
gui_state: &mut ImGuiUiState,
|
||||||
ui: &Ui,
|
ui: &Ui,
|
||||||
) {
|
) {
|
||||||
// let mut state: Chip8Computer = system_to_control;
|
if CollapsingHeader::new("Controls").build(ui) {
|
||||||
ui.window("!!!! CONTROLS !!!!")
|
// if the system has no program loaded hide the buttons.
|
||||||
.position([100.0, 640.0], Condition::FirstUseEver)
|
let bytes: [u8; 2] = [
|
||||||
.build(|| {
|
system.state().memory.peek(0x200),
|
||||||
|
system.state().memory.peek(0x201),
|
||||||
|
];
|
||||||
|
let show_buttons = true; // bytes[0] != 0 || bytes[1] == 0xe0;
|
||||||
|
|
||||||
|
if show_buttons {
|
||||||
|
if ui.button("Step") {
|
||||||
|
system.step();
|
||||||
|
};
|
||||||
|
ui.same_line();
|
||||||
|
if ui.button("Run") {
|
||||||
|
system.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.same_line();
|
||||||
|
if ui.button("Stop") {
|
||||||
|
system.stop();
|
||||||
|
}
|
||||||
|
ui.same_line();
|
||||||
|
if ui.button("Reset") {
|
||||||
|
system.reset(system.quirks_mode());
|
||||||
|
}
|
||||||
|
if ui.button("Dump Video Memory") {
|
||||||
|
println!("{}", system.dump_to_string(Video));
|
||||||
|
}
|
||||||
|
ui.same_line();
|
||||||
|
if ui.button("Dump Keypad State") {
|
||||||
|
println!("{}", system.dump_to_string(Keyboard));
|
||||||
|
}
|
||||||
|
ui.same_line();
|
||||||
|
if ui.button("Dump Registers") {
|
||||||
|
println!("{}", system.dump_to_string(Registers));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn system_summary(
|
||||||
|
system: &Chip8ComputerManager,
|
||||||
|
ui: &Ui,
|
||||||
|
) {
|
||||||
/* System Step Counter */
|
/* System Step Counter */
|
||||||
ui.text(format!("Step {:04x}", system_to_control.num_cycles()).as_str());
|
ui.text(format!("Step {:04x}", system.num_cycles()).as_str());
|
||||||
ui.text(format!("Mode {}", system_to_control.quirks_mode()));
|
ui.text(format!("Mode {}", system.quirks_mode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rom_lister(
|
||||||
|
system: &mut Chip8ComputerManager,
|
||||||
|
gui_state: &mut ImGuiUiState,
|
||||||
|
ui: &Ui,
|
||||||
|
) {
|
||||||
/* ROM Lister */
|
/* ROM Lister */
|
||||||
if CollapsingHeader::new("Roms").build(ui) {
|
if CollapsingHeader::new("Roms").build(ui) {
|
||||||
let new_filename = GuiFileList::display_path(
|
let new_filename = GuiFileList::display_path(
|
||||||
@ -149,49 +202,16 @@ impl GemmaImguiSupport {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.read_to_end(&mut buffer)
|
.read_to_end(&mut buffer)
|
||||||
.expect("Unable to read rom.");
|
.expect("Unable to read rom.");
|
||||||
system_to_control.load_new_program_to_system_memory(buffer);
|
system.load_new_program_to_system_memory(buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if CollapsingHeader::new("Controls").build(ui) {
|
fn display_options(
|
||||||
// if the system has no program loaded hide the buttons.
|
gui_state: &mut ImGuiUiState,
|
||||||
let bytes: [u8; 2] = [
|
ui: &Ui,
|
||||||
system_to_control.state().memory.peek(0x200),
|
) {
|
||||||
system_to_control.state().memory.peek(0x201),
|
|
||||||
];
|
|
||||||
let show_buttons = bytes[0] != 0 || bytes[1] == 0xe0;
|
|
||||||
|
|
||||||
if show_buttons {
|
|
||||||
if ui.button("Step") {
|
|
||||||
system_to_control.step();
|
|
||||||
};
|
|
||||||
ui.same_line();
|
|
||||||
if ui.button("Run") {
|
|
||||||
system_to_control.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ui.same_line();
|
|
||||||
if ui.button("Stop") {
|
|
||||||
system_to_control.stop();
|
|
||||||
}
|
|
||||||
ui.same_line();
|
|
||||||
if ui.button("Reset") {
|
|
||||||
system_to_control.reset(system_to_control.quirks_mode());
|
|
||||||
}
|
|
||||||
if ui.button("Dump Video Memory") {
|
|
||||||
println!("{}", system_to_control.dump_to_string(Video));
|
|
||||||
}
|
|
||||||
ui.same_line();
|
|
||||||
if ui.button("Dump Keypad State") {
|
|
||||||
debug!("{}", system_to_control.dump_to_string(Keyboard));
|
|
||||||
}
|
|
||||||
ui.same_line();
|
|
||||||
if ui.button("Dump Registers") {
|
|
||||||
debug!("{}", system_to_control.dump_to_string(Registers));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if CollapsingHeader::new("Options").build(ui) {
|
if CollapsingHeader::new("Options").build(ui) {
|
||||||
ui.checkbox("Show Memory", &mut gui_state.show_memory);
|
ui.checkbox("Show Memory", &mut gui_state.show_memory);
|
||||||
ui.same_line();
|
ui.same_line();
|
||||||
@ -203,27 +223,26 @@ impl GemmaImguiSupport {
|
|||||||
ui.input_int("Target IPS", &mut gui_state.target_ips)
|
ui.input_int("Target IPS", &mut gui_state.target_ips)
|
||||||
.build();
|
.build();
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let selectors = [Chip8, SChipModern, XOChip];
|
pub fn system_controls(
|
||||||
for current_selector in selectors {
|
system_to_control: &mut Chip8ComputerManager,
|
||||||
let mut working_selector =
|
gui_state: &mut ImGuiUiState,
|
||||||
ui.selectable_config(current_selector.clone().to_string());
|
ui: &Ui,
|
||||||
match system_to_control.quirks_mode() {
|
) {
|
||||||
Chip8 => {
|
// let mut state: Chip8Computer = system_to_control;
|
||||||
working_selector = working_selector.selected(true);
|
ui.window("!!!! CONTROLS !!!!")
|
||||||
}
|
.position([100.0, 640.0], Condition::FirstUseEver)
|
||||||
SChipModern => {
|
.build(|| {
|
||||||
working_selector = working_selector.selected(true);
|
GemmaImguiSupport::system_summary(system_to_control, ui);
|
||||||
}
|
|
||||||
XOChip => {
|
GemmaImguiSupport::rom_lister(system_to_control, gui_state, ui);
|
||||||
working_selector = working_selector.selected(true);
|
|
||||||
}
|
GemmaImguiSupport::control_pickers(system_to_control, gui_state, ui);
|
||||||
}
|
|
||||||
if working_selector.build() {
|
GemmaImguiSupport::display_options(gui_state, ui);
|
||||||
system_to_control.reset(current_selector);
|
|
||||||
println!("CLICK ON {}", ¤t_selector);
|
GemmaImguiSupport::quirks_picker(system_to_control, ui);
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +251,7 @@ impl GemmaImguiSupport {
|
|||||||
.position([100.0, 640.0], Condition::FirstUseEver)
|
.position([100.0, 640.0], Condition::FirstUseEver)
|
||||||
.build(|| {
|
.build(|| {
|
||||||
ui.text("Registers");
|
ui.text("Registers");
|
||||||
for i in 1..0x10 {
|
for i in 0..0x10 {
|
||||||
ui.text(format!("V{:X}: {}", i, system.registers.peek(i)));
|
ui.text(format!("V{:X}: {}", i, system.registers.peek(i)));
|
||||||
if i != 7 {
|
if i != 7 {
|
||||||
ui.same_line();
|
ui.same_line();
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::fs::read_dir;
|
use std::fs::read_dir;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use imgui::Ui;
|
use imgui::{ChildWindow, Ui};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
pub struct GuiFileList {}
|
pub struct GuiFileList {}
|
||||||
@ -20,6 +20,10 @@ impl GuiFileList {
|
|||||||
|
|
||||||
known_files.sort();
|
known_files.sort();
|
||||||
|
|
||||||
|
ui.child_window("Item List")
|
||||||
|
.size([0.0, 200.0])
|
||||||
|
.border(false)
|
||||||
|
.build(|| {
|
||||||
for (index, entry) in known_files.iter().enumerate() {
|
for (index, entry) in known_files.iter().enumerate() {
|
||||||
let mut working_select = ui.selectable_config(entry.clone().into_string().unwrap().to_string());
|
let mut working_select = ui.selectable_config(entry.clone().into_string().unwrap().to_string());
|
||||||
if *entry.to_str().unwrap() == *selected_filename.as_str() {
|
if *entry.to_str().unwrap() == *selected_filename.as_str() {
|
||||||
@ -30,6 +34,8 @@ impl GuiFileList {
|
|||||||
working_filename = entry.clone().into_string().unwrap();
|
working_filename = entry.clone().into_string().unwrap();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
working_filename
|
working_filename
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -179,7 +179,7 @@ pub fn create_context() -> imgui::Context {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
imgui.set_ini_filename(None);
|
// imgui.set_ini_filename(None);
|
||||||
|
|
||||||
imgui
|
imgui
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,9 @@ pub struct ImGuiUiState {
|
|||||||
pub frame_time: u32,
|
pub frame_time: u32,
|
||||||
pub last_frame_instant: Instant,
|
pub last_frame_instant: Instant,
|
||||||
pub target_ips: i32,
|
pub target_ips: i32,
|
||||||
pub roms_root_path: PathBuf
|
pub roms_root_path: PathBuf,
|
||||||
|
pub video_window_size: [i32; 2],
|
||||||
|
pub control_window_size: [i32; 2]
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for ImGuiUiState {
|
impl Clone for ImGuiUiState {
|
||||||
@ -33,7 +35,9 @@ impl Clone for ImGuiUiState {
|
|||||||
frame_time: self.frame_time,
|
frame_time: self.frame_time,
|
||||||
last_frame_instant: self.last_frame_instant,
|
last_frame_instant: self.last_frame_instant,
|
||||||
target_ips: self.target_ips,
|
target_ips: self.target_ips,
|
||||||
roms_root_path: self.roms_root_path.to_owned()
|
roms_root_path: self.roms_root_path.to_owned(),
|
||||||
|
video_window_size: self.video_window_size,
|
||||||
|
control_window_size: self.control_window_size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,8 +55,10 @@ impl Default for ImGuiUiState {
|
|||||||
is_running: false,
|
is_running: false,
|
||||||
frame_time: 16,
|
frame_time: 16,
|
||||||
last_frame_instant: Instant::now(),
|
last_frame_instant: Instant::now(),
|
||||||
target_ips: 200000,
|
target_ips: 20,
|
||||||
roms_root_path: PathBuf::from(ROMPATH_DEFAULT)
|
roms_root_path: PathBuf::from(ROMPATH_DEFAULT),
|
||||||
|
control_window_size: [200, 600],
|
||||||
|
video_window_size: [800, 600]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "gemmautil"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
gemma = { path = "../gemma" }
|
|
||||||
clap.workspace = true
|
|
||||||
pest = "2.7.14"
|
|
||||||
pest_derive = "2.7.14"
|
|
||||||
Binary file not shown.
BIN
resources/custom/sinusoid_schip.ch8
Normal file
BIN
resources/custom/sinusoid_schip.ch8
Normal file
Binary file not shown.
BIN
resources/roms/gradsim.ch8
Normal file
BIN
resources/roms/gradsim.ch8
Normal file
Binary file not shown.
BIN
resources/roms/octo-default.ch8
Normal file
BIN
resources/roms/octo-default.ch8
Normal file
Binary file not shown.
BIN
resources/roms/sinusoid_schip.ch8
Normal file
BIN
resources/roms/sinusoid_schip.ch8
Normal file
Binary file not shown.
BIN
resources/roms/vertical_clip.ch8
Normal file
BIN
resources/roms/vertical_clip.ch8
Normal file
Binary file not shown.
BIN
resources/test/state/computer_001_system_zero_state.tct
Normal file
BIN
resources/test/state/computer_001_system_zero_state.tct
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
|||||||
1f8b08000000000000ffed9bcb8adb301440f7fd162dfc0c136d0b854257dd743104636c4d2212dba924b704e37fafecb13b4e42da4099a10c87631d45575792e56897b8abdb2a2b4ec5415919884a558d39c96eaa1fa324106192fc2e433b8ec47a35d85f61188db170f55c450f63bd0c4dd562021fbac89f9a67a12979eaf66bad92e15af69de5bc4c7e159ad78eaeeb79c9b98c63a3e47cd38bbcab5d2e624309e0d509c7271dff212316e1da1fcc5be387ef331d4a3a9670bdfee7ccf98c44893f0f69348eba9139cd3597f8e6dac1f9da7178e78cc3bddcbb9ff40d3397596f3fe7bdcffdde3c0000000000000000000000000078376c7a61d4565ba78c95ddcbc7c7bf8d133a9bb365208e854cc3a817b669eb3273baf2d1aef08db1bb17a53ae4a7cbb8ff59b2173f74a99aecf20f1b4ff9c12a8131c618638c31c618638c31c618638c31c618638c31c618638c31c618e3ffd81bb1cb6df664f24a65c52eafb7aa94cfdd456b8caa5d6694955f9a9f5f95ed8575b953f25bae9daeb79f1af3b9b6ceb485d34d2df6ea74cc4bd9f9dabefa6b059bf15e8abdecb453955fcf07beb7daecb3aa2995fcb8d3c787fec32fcefd4d09ab520000
|
|
||||||
Loading…
x
Reference in New Issue
Block a user