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:
tmerritt committed 2024-11-09 20:07:21 -05:00
1 parent 67ca71ccb7
commit cddbe0c46e
41 files changed
+464 -310

No files matched your search

Generated
+2 -10
View File
@@ -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"
-1
View File
@@ -5,7 +5,6 @@ members = [
"gemmaimgui", "gemmaimgui",
"gemmatelnet", "gemmatelnet",
"gemmasdl2", "gemmasdl2",
"gemmautil",
] ]
resolver = "2" resolver = "2"
+2
View File
@@ -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"}
File renamed without changes.
@@ -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");
File renamed without changes.
-57
View File
@@ -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
View 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.");
}
}
+18 -6
View File
@@ -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 -1
View File
@@ -118,7 +118,7 @@ impl Chip8ComputerManager {
self.computer.load_bytes_to_memory(0x200, &bytes_to_load); self.computer.load_bytes_to_memory(0x200, &bytes_to_load);
} }
pub fn dump_to_string(&self, dump_type: ManagerDumpables) -> String { pub fn dump_to_string(&self, dump_type: ManagerDumpables) -> String{
match dump_type { match dump_type {
ManagerDumpables::Video => self.computer.video_memory.format_as_string(), ManagerDumpables::Video => self.computer.video_memory.format_as_string(),
ManagerDumpables::Registers => self.computer.registers.format_as_string(), ManagerDumpables::Registers => self.computer.registers.format_as_string(),
+2 -1
View File
@@ -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 -1
View File
@@ -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,
} }
+9 -3
View File
@@ -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
+1 -1
View File
@@ -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 -1
View File
@@ -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,
+1 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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>,
} }
+1 -1
View File
@@ -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>,
} }
+127 -2
View File
@@ -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
} }
@@ -40,7 +47,7 @@ impl InstructionUtil {
// 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
pub fn read_nibble_from_instruction(instruction_to_read_from: u16) -> u8 { pub fn read_nibble_from_instruction(instruction_to_read_from: u16) -> u8 {
( instruction_to_read_from & 0x000F )as u8 (instruction_to_read_from & 0x000F) as u8
} }
// x - A 4-bit value, the lower 4 bits of the high byte of the instruction // x - A 4-bit value, the lower 4 bits of the high byte of the instruction
@@ -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(())
}
} }
+5 -9
View File
@@ -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,
} }
} }
} }
File renamed without changes.
File renamed without changes.
+18 -20
View File
@@ -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));
} }
} }
-1
View File
@@ -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);
} }
+180 -161
View File
@@ -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,70 +45,186 @@ 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")
// println!("DRAW_AREA_SIZE = {}x{}", draw_area_size[0], draw_area_size[1]); .size([gui_state.video_window_size[0] as f32, gui_state.video_window_size[1] as f32], Condition::Once)
let cell_width = ((draw_area_size[0] as i32 / width) * 6) / 10; .build(|| {
let cell_height = ((draw_area_size[1] as i32 / height) * 6) / 10; 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]);
let cell_width = ((draw_area_size[0] as i32 / width));
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() { for current_row in 0..height {
// ui.text("High Def Video here"); let y_offset = origin[1] as i32 + (current_row * cell_height);
for current_row in 0..=height { for current_column in 0..width {
let y_offset = origin[1] as i32 + (current_row * cell_height); let x_offset = origin[0] as i32 + (current_column * cell_width);
for current_column in 0..=width { let current_origin = [x_offset as f32, y_offset as f32];
let x_offset = origin[0] as i32 + (current_column * cell_width); let current_limit = [
let current_origin = [x_offset as f32, y_offset as f32]; (x_offset + cell_width) as f32,
let current_limit = [ (y_offset + cell_height) as f32,
(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 memory_offset = (current_row * width + current_column) as u16; let color: ImColor32 = if to_render {
let to_render = system_to_control.video_memory.peek(memory_offset); gui_state.on_colour
let color: ImColor32 = if to_render { } else {
gui_state.on_colour gui_state.off_colour
} else { };
gui_state.off_colour fg.add_rect_filled_multicolor(
}; current_origin,
fg.add_rect_filled_multicolor( current_limit,
current_origin, color,
current_limit, color,
color, color,
color, color,
color, );
color, }
); }
}).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);
} }
} }
} else { if working_selector.build() {
for current_row in 0..height { system.reset(current_selector);
let y_offset = origin[1] as i32 + (current_row * cell_height); println!("CLICK ON {}", &current_selector);
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, fn control_pickers(
]; system: &mut Chip8ComputerManager,
let memory_offset = (current_row * width + current_column) as u16; gui_state: &mut ImGuiUiState,
let to_render = system_to_control.video_memory.peek(memory_offset); ui: &Ui,
let color: ImColor32 = if to_render { ) {
gui_state.on_colour if CollapsingHeader::new("Controls").build(ui) {
} else { // if the system has no program loaded hide the buttons.
gui_state.off_colour let bytes: [u8; 2] = [
}; system.state().memory.peek(0x200),
fg.add_rect_filled_multicolor( system.state().memory.peek(0x201),
current_origin, ];
current_limit, let show_buttons = true; // bytes[0] != 0 || bytes[1] == 0xe0;
color,
color, if show_buttons {
color, if ui.button("Step") {
color, 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 */
ui.text(format!("Step {:04x}", system.num_cycles()).as_str());
ui.text(format!("Mode {}", system.quirks_mode()));
}
fn rom_lister(
system: &mut Chip8ComputerManager,
gui_state: &mut ImGuiUiState,
ui: &Ui,
) {
/* ROM Lister */
if CollapsingHeader::new("Roms").build(ui) {
let new_filename = GuiFileList::display_path(
gui_state.roms_root_path.clone(),
&gui_state.filename_to_load,
ui,
);
if !new_filename.is_empty() {
if new_filename != gui_state.filename_to_load {
debug!("NEW FILENAME SELECTED -> {new_filename}");
gui_state.filename_to_load = new_filename;
}
if ui.button("Load Program") {
let mut buffer: Vec<u8> = Vec::new();
let full_name = format!(
"{}/{}",
gui_state
.roms_root_path
.clone()
.as_os_str()
.to_string_lossy(),
gui_state.filename_to_load.to_string()
); );
debug!("PREPARING TO LOAD {}", full_name);
let input_file = File::open(Path::new(&full_name));
input_file
.unwrap()
.read_to_end(&mut buffer)
.expect("Unable to read rom.");
system.load_new_program_to_system_memory(buffer);
} }
} }
} }
} }
fn display_options(
gui_state: &mut ImGuiUiState,
ui: &Ui,
) {
if CollapsingHeader::new("Options").build(ui) {
ui.checkbox("Show Memory", &mut gui_state.show_memory);
ui.same_line();
ui.checkbox("Show Video", &mut gui_state.show_video);
ui.same_line();
ui.checkbox("Show Registers", &mut gui_state.show_registers);
ui.same_line();
ui.checkbox("Show Keypad", &mut gui_state.show_keypad);
ui.input_int("Target IPS", &mut gui_state.target_ips)
.build();
};
}
pub fn system_controls( pub fn system_controls(
system_to_control: &mut Chip8ComputerManager, system_to_control: &mut Chip8ComputerManager,
gui_state: &mut ImGuiUiState, gui_state: &mut ImGuiUiState,
@@ -116,114 +234,15 @@ impl GemmaImguiSupport {
ui.window("!!!! CONTROLS !!!!") ui.window("!!!! CONTROLS !!!!")
.position([100.0, 640.0], Condition::FirstUseEver) .position([100.0, 640.0], Condition::FirstUseEver)
.build(|| { .build(|| {
/* System Step Counter */ GemmaImguiSupport::system_summary(system_to_control, ui);
ui.text(format!("Step {:04x}", system_to_control.num_cycles()).as_str());
ui.text(format!("Mode {}", system_to_control.quirks_mode()));
/* ROM Lister */
if CollapsingHeader::new("Roms").build(ui) {
let new_filename = GuiFileList::display_path(
gui_state.roms_root_path.clone(),
&gui_state.filename_to_load,
ui,
);
if !new_filename.is_empty() {
if new_filename != gui_state.filename_to_load {
debug!("NEW FILENAME SELECTED -> {new_filename}");
gui_state.filename_to_load = new_filename;
}
if ui.button("Load Program") {
let mut buffer: Vec<u8> = Vec::new();
let full_name = format!(
"{}/{}",
gui_state
.roms_root_path
.clone()
.as_os_str()
.to_string_lossy(),
gui_state.filename_to_load.to_string()
);
debug!("PREPARING TO LOAD {}", full_name);
let input_file = File::open(Path::new(&full_name)); GemmaImguiSupport::rom_lister(system_to_control, gui_state, ui);
input_file
.unwrap()
.read_to_end(&mut buffer)
.expect("Unable to read rom.");
system_to_control.load_new_program_to_system_memory(buffer);
}
}
}
if CollapsingHeader::new("Controls").build(ui) { GemmaImguiSupport::control_pickers(system_to_control, gui_state, ui);
// if the system has no program loaded hide the buttons.
let bytes: [u8; 2] = [
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 { GemmaImguiSupport::display_options(gui_state, ui);
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) { GemmaImguiSupport::quirks_picker(system_to_control, ui);
ui.checkbox("Show Memory", &mut gui_state.show_memory);
ui.same_line();
ui.checkbox("Show Video", &mut gui_state.show_video);
ui.same_line();
ui.checkbox("Show Registers", &mut gui_state.show_registers);
ui.same_line();
ui.checkbox("Show Keypad", &mut gui_state.show_keypad);
ui.input_int("Target IPS", &mut gui_state.target_ips)
.build();
};
let selectors = [Chip8, SChipModern, XOChip];
for current_selector in selectors {
let mut working_selector =
ui.selectable_config(current_selector.clone().to_string());
match system_to_control.quirks_mode() {
Chip8 => {
working_selector = working_selector.selected(true);
}
SChipModern => {
working_selector = working_selector.selected(true);
}
XOChip => {
working_selector = working_selector.selected(true);
}
}
if working_selector.build() {
system_to_control.reset(current_selector);
println!("CLICK ON {}", &current_selector);
}
}
}); });
} }
@@ -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();
+17 -11
View File
@@ -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,16 +20,22 @@ impl GuiFileList {
known_files.sort(); known_files.sort();
for (index, entry) in known_files.iter().enumerate() { ui.child_window("Item List")
let mut working_select = ui.selectable_config(entry.clone().into_string().unwrap().to_string()); .size([0.0, 200.0])
if *entry.to_str().unwrap() == *selected_filename.as_str() { .border(false)
working_select = working_select.selected(true); .build(|| {
} for (index, entry) in known_files.iter().enumerate() {
if working_select.build() { let mut working_select = ui.selectable_config(entry.clone().into_string().unwrap().to_string());
debug!("SELECTED {index} / {entry:?}"); if *entry.to_str().unwrap() == *selected_filename.as_str() {
working_filename = entry.clone().into_string().unwrap(); working_select = working_select.selected(true);
}; }
} if working_select.build() {
debug!("SELECTED {index} / {entry:?}");
working_filename = entry.clone().into_string().unwrap();
};
}
});
working_filename working_filename
} }
} }
+1 -1
View File
@@ -179,7 +179,7 @@ pub fn create_context() -> imgui::Context {
}), }),
}, },
]); ]);
imgui.set_ini_filename(None); // imgui.set_ini_filename(None);
imgui imgui
} }
+10 -4
View File
@@ -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]
} }
} }
} }
-10
View File
@@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
{"num_cycles":0,"memory":{"memory":[240,144,144,144,240,32,96,32,32,112,240,16,240,128,240,240,16,240,16,240,144,144,240,16,16,240,128,240,16,240,240,128,240,144,240,240,16,32,64,64,240,144,240,144,240,240,144,240,16,240,240,144,240,144,144,224,144,224,144,224,240,128,128,128,240,224,144,144,144,224,240,128,240,128,240,240,128,240,128,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,128,3,0,0,0,0,0,0,0,0,0,0,0,0,0,3,192,2,0,0,0,0,0,0,0,0,0,0,0,124,254,255,255,199,0,0,0,0,0,0,0,0,0,0,0,124,254,255,255,199,0,0,0,0,0,0,0,0,0,0,0,224,240,248,252,254,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,3,0,0,0,0,0,0,0,0,0,0,0,120,254,255,255,131,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,224,0,0,0,0,0,0,0,0,0,0,0,124,254,255,255,195,0,0,0,0,0,0,0,0,0,0,0,124,254,255,255,195,0,0,0,0,0,0,0,0,0,0,0,124,254,255,255,195,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,195,0,0,0,0,0,0,0,0,0,0,0,124,254,255,255,195,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,195,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,3,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,Line truncated {"num_cycles":0,"memory":{"memory":[240,144,144,144,240,32,96,32,32,112,240,16,240,128,240,240,16,240,16,240,144,144,240,16,16,240,128,240,16,240,240,128,240,144,240,240,16,32,64,64,240,144,240,144,240,240,144,240,16,240,240,144,240,144,144,224,144,224,144,224,240,128,128,128,240,224,144,144,144,224,240,128,240,128,240,240,128,240,128,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,144,144,144,144,144,144,144,144,240,32,96,160,32,32,32,32,32,32,112,240,16,16,16,240,128,128,128,128,240,240,16,16,16,240,16,16,16,16,240,144,144,144,144,240,16,16,16,16,16,240,128,128,128,240,16,16,16,16,240,240,128,128,128,240,144,144,144,144,240,240,16,16,16,16,16,16,16,16,16,240,144,144,144,240,144,144,144,144,240,240,144,144,144,240,16,16,16,16,240,240,144,144,144,240,144,144,144,144,144,224,144,144,144,224,144,144,144,144,224,240,128,128,128,128,128,128,128,128,240,224,144,144,144,144,144,144,144,144,224,240,128,128,128,240,128,128,128,128,240,240,128,128,128,240,128,128,128,128,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,Line truncated
@@ -1 +0,0 @@
1f8b08000000000000ffed9bcb8adb301440f7fd162dfc0c136d0b854257dd743104636c4d2212dba924b704e37fafecb13b4e42da4099a10c87631d45575792e56897b8abdb2a2b4ec5415919884a558d39c96eaa1fa324106192fc2e433b8ec47a35d85f61188db170f55c450f63bd0c4dd562021fbac89f9a67a12979eaf66bad92e15af69de5bc4c7e159ad78eaeeb79c9b98c63a3e47cd38bbcab5d2e624309e0d509c7271dff212316e1da1fcc5be387ef331d4a3a9670bdfee7ccf98c44893f0f69348eba9139cd3597f8e6dac1f9da7178e78cc3bddcbb9ff40d3397596f3fe7bdcffdde3c0000000000000000000000000078376c7a61d4565ba78c95ddcbc7c7bf8d133a9bb365208e854cc3a817b669eb3273baf2d1aef08db1bb17a53ae4a7cbb8ff59b2173f74a99aecf20f1b4ff9c12a8131c618638c31c618638c31c618638c31c618638c31c618638c31c618e3ffd81bb1cb6df664f24a65c52eafb7aa94cfdd456b8caa5d6694955f9a9f5f95ed8575b953f25bae9daeb79f1af3b9b6ceb485d34d2df6ea74cc4bd9f9dabefa6b059bf15e8abdecb453955fcf07beb7daecb3aa2995fcb8d3c787fec32fcefd4d09ab520000
Whitespace-only changes.