trevors_chip8_toy/coverage/tarpaulin-report.html
2025-05-27 10:02:13 -04:00

671 lines
893 KiB
HTML

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>html, body {
margin: 0;
padding: 0;
}
.app {
margin: 10px;
padding: 0;
}
.files-list {
margin: 10px 0 0;
width: 100%;
border-collapse: collapse;
}
.files-list__head {
border: 1px solid #999;
}
.files-list__head > tr > th {
padding: 10px;
border: 1px solid #999;
text-align: left;
font-weight: normal;
background: #ddd;
}
.files-list__body {
}
.files-list__file {
cursor: pointer;
}
.files-list__file:hover {
background: #ccf;
}
.files-list__file > td {
padding: 10px;
border: 1px solid #999;
}
.files-list__file > td:first-child::before {
content: '\01F4C4';
margin-right: 1em;
}
.files-list__file_low {
background: #fcc;
}
.files-list__file_medium {
background: #ffc;
}
.files-list__file_high {
background: #cfc;
}
.files-list__file_folder > td:first-child::before {
content: '\01F4C1';
margin-right: 1em;
}
.file-header {
border: 1px solid #999;
display: flex;
justify-content: space-between;
align-items: center;
position: sticky;
top: 0;
background: white;
}
.file-header__back {
margin: 10px;
cursor: pointer;
flex-shrink: 0;
flex-grow: 0;
text-decoration: underline;
color: #338;
}
.file-header__name {
margin: 10px;
flex-shrink: 2;
flex-grow: 2;
}
.file-header__stat {
margin: 10px;
flex-shrink: 0;
flex-grow: 0;
}
.file-content {
margin: 10px 0 0;
border: 1px solid #999;
padding: 10px;
counter-reset: line;
display: flex;
flex-direction: column;
}
.code-line::before {
content: counter(line);
margin-right: 10px;
}
.code-line {
margin: 0;
padding: 0.3em;
height: 1em;
counter-increment: line;
}
.code-line_covered {
background: #cfc;
}
.code-line_uncovered {
background: #fcc;
}
</style>
</head>
<body>
<div id="root"></div>
<script>
var data = {"files":[{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","bin","speedtest.rs"],"content":"use gemma::chip8::computer_manager::Chip8ComputerManager;\nuse std::fs::File;\nuse std::io::Read;\nuse std::ops::Add;\nuse std::path::Path;\nuse std::time::{Duration, Instant};\n\nconst ROM_ROOT: \u0026str = \"resources/roms\";\n\nfn main() {\n let mut x = Chip8ComputerManager::new();\n\n let mut buffer = Vec::new();\n let mut input_file =\n File::open(Path::new(\u0026(ROM_ROOT.to_string() + \"/mandelbrot_bench.ch8\"))).expect(\"derp\");\n input_file\n .read_to_end(\u0026mut buffer)\n .expect(\"unable to read file\");\n x.load_new_program_to_system_memory((\u0026*buffer).into());\n\n for _ in 0..10 {\n let start_time = Instant::now();\n let mut num_cycles = 0;\n // how many instructions in 10s.\n let end_time = start_time.add(Duration::from_secs(10));\n while Instant::now().le(\u0026end_time) {\n num_cycles += 1;\n x.step();\n }\n\n let num = num_cycles\n .to_string()\n .as_bytes()\n .rchunks(3)\n .rev()\n .map(std::str::from_utf8)\n .collect::\u003cResult\u003cVec\u003c\u0026str\u003e, _\u003e\u003e()\n .unwrap()\n .join(\",\"); // separator\n\n let num_ips = num_cycles / 1000000;\n println!(\"Completed at {num_ips} Mips. ({num})\");\n }\n}\n","traces":[{"line":10,"address":[458832,460780,460845],"length":1,"stats":{"Line":0}},{"line":11,"address":[458839],"length":1,"stats":{"Line":0}},{"line":13,"address":[458856],"length":1,"stats":{"Line":0}},{"line":14,"address":[458919,458991],"length":1,"stats":{"Line":0}},{"line":16,"address":[459251],"length":1,"stats":{"Line":0}},{"line":19,"address":[459336],"length":1,"stats":{"Line":0}},{"line":21,"address":[459436],"length":1,"stats":{"Line":0}},{"line":22,"address":[459589,459671],"length":1,"stats":{"Line":0}},{"line":23,"address":[459700],"length":1,"stats":{"Line":0}},{"line":25,"address":[459716],"length":1,"stats":{"Line":0}},{"line":26,"address":[459794],"length":1,"stats":{"Line":0}},{"line":27,"address":[460819,459898,460793],"length":1,"stats":{"Line":0}},{"line":28,"address":[460800,460840],"length":1,"stats":{"Line":0}},{"line":31,"address":[459891,460323,459933,460024,460390],"length":1,"stats":{"Line":0}},{"line":41,"address":[460537,460626],"length":1,"stats":{"Line":0}},{"line":42,"address":[460599,460646],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":16},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","bin","testcompression.rs"],"content":"use std::fs;\nuse std::fs::File;\nuse clap::{Parser, Subcommand};\nuse flate2::{write::{GzEncoder, GzDecoder}, Compression};\nuse std::io::{Cursor, Read, Write};\nuse std::path::PathBuf;\nuse std::io::prelude::*;\n\n#[derive(Parser)]\n#[command(author, version, about = \"Compress or decompress a string\", long_about = None)]\nstruct Cli {\n /// Subcommand: either 'compress' or 'decompress'\n #[command(subcommand)]\n command: Commands,\n}\n\n#[derive(Subcommand)]\nenum Commands {\n /// Compress the input string\n Compress {\n /// The string to compress\n input: String,\n },\n /// Decompress the input string (must be compressed format)\n Decompress {\n /// The compressed string to decompress, in hex format\n input: String,\n },\n CompressFile {\n input: PathBuf\n },\n DecompressFile {\n input: PathBuf\n }\n}\n\nfn compress_string(input: \u0026str) -\u003e Vec\u003cu8\u003e {\n let mut encoder = GzEncoder::new(Vec::new(), Compression::default());\n encoder.write_all(input.as_bytes()).expect(\"Compression failed\");\n encoder.finish().expect(\"Failed to finish compression\")\n}\n\nfn decompress_string(input: \u0026[u8]) -\u003e String {\n let mut decoder = GzDecoder::new(Vec::new());\n decoder.write_all(input).expect(\"Decompression failed\");\n let decompressed_data = decoder.finish().expect(\"Failed to finish decompression\");\n String::from_utf8(decompressed_data).expect(\"Invalid UTF-8\")\n}\n\nfn compress_file(input_path: \u0026PathBuf) {\n let target_file_name = format!(\"{}.tflt\", input_path.display());\n println!(\"TARGET_FILE_NAME: {}\", target_file_name);\n let input_data = fs::read(input_path).expect(\"Failed to read input file\");\n let mut encoder = GzEncoder::new(Vec::new(), Compression::default());\n encoder.write_all(\u0026input_data).expect(\"Compression failed\");\n let compressed_data = encoder.finish().expect(\"Failed to finish compression\");\n let target_file = File::create(target_file_name);\n target_file.unwrap().write_all(\u0026compressed_data).unwrap()\n}\n\nfn decompress_file(input_path: \u0026PathBuf) {\n let target_file_name = format!(\"{}.uncompressed\", input_path.display());\n println!(\"Writing decompressed data to {}\", target_file_name);\n let input_data = fs::read(input_path).expect(\"Failed to read compressed file.\");\n let mut decompressed_data = vec![];\n let cursor = Cursor::new(input_data);\n let mut decoder = GzDecoder::new(cursor);\n decoder.read_to_end(\u0026mut decompressed_data).expect(\"Decompression failed.\");\n let mut target_file = File::create(\u0026target_file_name).expect(format!(\"Unable to create uncompressed file -\u003e {}\", target_file_name.clone()).as_str());\n target_file.write_all(\u0026decompressed_data).expect(format!(\"Unable to write uncompressed file -\u003e {}\", target_file_name).as_str());\n println!(\"Decompressed: {:?}\", String::from_utf8_lossy(\u0026decompressed_data));\n}\n\nfn main() {\n let cli = Cli::parse();\n\n match cli.command {\n Commands::Compress { input } =\u003e {\n let compressed = compress_string(\u0026input);\n // Convert to hex format for easier readability in the terminal\n println!(\"Compressed (hex): {:?} / from {}b to {}b\", hex::encode(compressed.clone()), input.len(), compressed.len());\n }\n Commands::Decompress { input } =\u003e {\n // Decode hex string back to bytes\n let compressed_bytes = hex::decode(input).expect(\"Invalid hex input\");\n let decompressed = decompress_string(\u0026compressed_bytes);\n println!(\"Decompressed: {}\", decompressed);\n }\n Commands::DecompressFile { input } =\u003e {\n decompress_file(\u0026input);\n }\n Commands::CompressFile { input } =\u003e {\n compress_file(\u0026input);\n }\n }\n}\n","traces":[{"line":37,"address":[319671,319120,319646],"length":1,"stats":{"Line":0}},{"line":38,"address":[319163,319680],"length":1,"stats":{"Line":0}},{"line":39,"address":[319398],"length":1,"stats":{"Line":0}},{"line":40,"address":[319439],"length":1,"stats":{"Line":0}},{"line":43,"address":[320116,319712,320091],"length":1,"stats":{"Line":0}},{"line":44,"address":[319755],"length":1,"stats":{"Line":0}},{"line":45,"address":[319824,319882],"length":1,"stats":{"Line":0}},{"line":46,"address":[319908],"length":1,"stats":{"Line":0}},{"line":47,"address":[320024],"length":1,"stats":{"Line":0}},{"line":50,"address":[320128,321794],"length":1,"stats":{"Line":0}},{"line":51,"address":[320325,320148],"length":1,"stats":{"Line":0}},{"line":52,"address":[320333,320398],"length":1,"stats":{"Line":0}},{"line":53,"address":[320480],"length":1,"stats":{"Line":0}},{"line":54,"address":[320588,321834,320537],"length":1,"stats":{"Line":0}},{"line":55,"address":[320700,320788],"length":1,"stats":{"Line":0}},{"line":56,"address":[320831],"length":1,"stats":{"Line":0}},{"line":57,"address":[321032],"length":1,"stats":{"Line":0}},{"line":58,"address":[321335,321453],"length":1,"stats":{"Line":0}},{"line":61,"address":[321872,323944,323835],"length":1,"stats":{"Line":0}},{"line":62,"address":[321892],"length":1,"stats":{"Line":0}},{"line":63,"address":[322134,322069],"length":1,"stats":{"Line":0}},{"line":64,"address":[322216],"length":1,"stats":{"Line":0}},{"line":65,"address":[322281],"length":1,"stats":{"Line":0}},{"line":66,"address":[322329],"length":1,"stats":{"Line":0}},{"line":67,"address":[322449],"length":1,"stats":{"Line":0}},{"line":68,"address":[322472,322536],"length":1,"stats":{"Line":0}},{"line":69,"address":[323885,322575,322962],"length":1,"stats":{"Line":0}},{"line":70,"address":[323142,323844,323419],"length":1,"stats":{"Line":0}},{"line":71,"address":[323544],"length":1,"stats":{"Line":0}},{"line":74,"address":[323968,324784],"length":1,"stats":{"Line":0}},{"line":75,"address":[323980],"length":1,"stats":{"Line":0}},{"line":77,"address":[323985],"length":1,"stats":{"Line":0}},{"line":78,"address":[324018],"length":1,"stats":{"Line":0}},{"line":79,"address":[324046,324336],"length":1,"stats":{"Line":0}},{"line":81,"address":[324343,324434],"length":1,"stats":{"Line":0}},{"line":83,"address":[324066],"length":1,"stats":{"Line":0}},{"line":85,"address":[324113],"length":1,"stats":{"Line":0}},{"line":86,"address":[324165,324865],"length":1,"stats":{"Line":0}},{"line":87,"address":[324872,324940],"length":1,"stats":{"Line":0}},{"line":89,"address":[324233],"length":1,"stats":{"Line":0}},{"line":90,"address":[324267],"length":1,"stats":{"Line":0}},{"line":92,"address":[324189],"length":1,"stats":{"Line":0}},{"line":93,"address":[324223],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":43},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","computer.rs"],"content":"use crate::chip8::delay_timer::DelayTimer;\nuse crate::chip8::keypad::Keypad;\nuse crate::chip8::quirk_modes::QuirkMode;\nuse crate::chip8::registers::Chip8Registers;\nuse crate::chip8::sound_timer::SoundTimer;\nuse crate::chip8::stack::Chip8Stack;\nuse log::debug;\nuse serde::{Deserialize, Serialize};\nuse crate::chip8::util::InstructionUtil;\nuse super::{\n cpu_states::Chip8CpuStates, instructions::Chip8CpuInstructions,\n system_memory::Chip8SystemMemory, video::Chip8Video,\n};\n\n#[derive(Clone, Serialize, Deserialize, Debug)]\npub struct Chip8Computer {\n pub num_cycles: i32,\n pub memory: Chip8SystemMemory,\n pub registers: Chip8Registers,\n pub sound_timer: SoundTimer,\n pub delay_timer: DelayTimer,\n pub video_memory: Chip8Video,\n pub state: Chip8CpuStates,\n pub keypad: Keypad,\n pub stack: Chip8Stack,\n pub quirk_mode: QuirkMode,\n}\n\nimpl PartialEq for Chip8Computer {\n fn eq(\u0026self, other: \u0026Self) -\u003e bool {\n self.video_memory.format_as_string() == other.video_memory.format_as_string() \u0026\u0026\n self.keypad.format_as_string() == other.keypad.format_as_string() \u0026\u0026\n self.num_cycles == other.num_cycles \u0026\u0026\n self.memory == other.memory\n }\n}\n\nimpl Default for Chip8Computer {\n fn default() -\u003e Self {\n Self {\n num_cycles: 0,\n memory: Chip8SystemMemory::default(),\n video_memory: Chip8Video::default(),\n registers: Chip8Registers::default(),\n sound_timer: SoundTimer::new(),\n delay_timer: DelayTimer::new(),\n state: Chip8CpuStates::default(),\n keypad: Keypad::default(),\n stack: Chip8Stack::default(),\n quirk_mode: QuirkMode::default(),\n }\n }\n}\n\nimpl Chip8Computer {\n pub fn reset(\u0026mut self, quirk_mode: QuirkMode) {\n self.video_memory.reset();\n self.num_cycles = 0;\n self.registers.reset();\n self.delay_timer.reset();\n self.sound_timer.reset();\n self.stack.reset();\n self.memory.reset(quirk_mode);\n self.quirk_mode = quirk_mode;\n }\n\n pub fn dump_keypad_to_string(\u0026self) -\u003e String {\n self.keypad.format_as_string()\n }\n\n pub fn dump_registers_to_string(\u0026self) -\u003e String {\n self.registers.format_as_string()\n }\n\n pub fn dump_video_to_string(\u0026self) -\u003e String {\n self.clone().video_memory.format_as_string()\n }\n\n pub fn new() -\u003e Self {\n Chip8Computer::default()\n }\n\n pub fn load_bytes_to_memory(\u0026mut self, offset: u16, to_load: \u0026Vec\u003cu8\u003e) {\n let total_len = to_load.len() as u16;\n for current_index in 0..total_len {\n let new_value = to_load[current_index as usize];\n let new_location = current_index + offset;\n self.memory.poke(new_location, new_value);\n }\n self.registers.set_pc(offset);\n }\n\n\n pub fn step_system(\u0026mut self) -\u003e \u0026mut Chip8Computer {\n println!(\"Stepping System 1 Step\");\n // read the next instruction\n let local_memory = \u0026self.memory;\n\n // let mut working_instruction: u16 = 0b0000000000000000;\n let start_pc = self.registers.peek_pc();\n let high_byte = (local_memory.peek(start_pc) as u16).rotate_left(8);\n let low_byte = local_memory.peek(start_pc + 1) as u16;\n let result = high_byte | low_byte;\n let decoded_instruction = Chip8CpuInstructions::decode(result, \u0026self.quirk_mode);\n // todo: THIS IS BAD AND IS A SIDE EFFECT\n decoded_instruction.execute(self);\n\n match self.state {\n Chip8CpuStates::WaitingForInstruction =\u003e {\n // println!(\"Ticking sound, delay, video\");\n self.sound_timer.tick();\n self.delay_timer.tick();\n self.video_memory.tick();\n self.num_cycles += 1;\n }\n Chip8CpuStates::WaitingForKey =\u003e {\n debug!(\"waiting for a key press...\");\n }\n _ =\u003e {}\n }\n self\n }\n}\n","traces":[{"line":30,"address":[1714512,1715067],"length":1,"stats":{"Line":1}},{"line":31,"address":[821895],"length":1,"stats":{"Line":1}},{"line":32,"address":[2490139],"length":1,"stats":{"Line":1}},{"line":33,"address":[822363],"length":1,"stats":{"Line":1}},{"line":34,"address":[2490401],"length":1,"stats":{"Line":1}},{"line":39,"address":[814800,815267],"length":1,"stats":{"Line":7}},{"line":42,"address":[41428993],"length":1,"stats":{"Line":12}},{"line":43,"address":[814831],"length":1,"stats":{"Line":11}},{"line":44,"address":[1334568],"length":1,"stats":{"Line":11}},{"line":45,"address":[1774526],"length":1,"stats":{"Line":6}},{"line":46,"address":[1774541],"length":1,"stats":{"Line":11}},{"line":47,"address":[1618732],"length":1,"stats":{"Line":6}},{"line":48,"address":[1618748],"length":1,"stats":{"Line":11}},{"line":49,"address":[1774587],"length":1,"stats":{"Line":6}},{"line":50,"address":[806546],"length":1,"stats":{"Line":11}},{"line":56,"address":[1715600],"length":1,"stats":{"Line":2}},{"line":57,"address":[1335001],"length":1,"stats":{"Line":2}},{"line":58,"address":[41429512],"length":1,"stats":{"Line":4}},{"line":59,"address":[4039567],"length":1,"stats":{"Line":4}},{"line":60,"address":[1774942],"length":1,"stats":{"Line":4}},{"line":61,"address":[2491037],"length":1,"stats":{"Line":4}},{"line":62,"address":[1715692],"length":1,"stats":{"Line":4}},{"line":63,"address":[1026511],"length":1,"stats":{"Line":4}},{"line":64,"address":[551163],"length":1,"stats":{"Line":4}},{"line":67,"address":[901280],"length":1,"stats":{"Line":1}},{"line":68,"address":[4039681],"length":1,"stats":{"Line":1}},{"line":71,"address":[1715792],"length":1,"stats":{"Line":1}},{"line":72,"address":[4039729],"length":1,"stats":{"Line":1}},{"line":75,"address":[807072,807188],"length":1,"stats":{"Line":2}},{"line":76,"address":[1335245],"length":1,"stats":{"Line":2}},{"line":79,"address":[551424],"length":1,"stats":{"Line":7}},{"line":80,"address":[1619448],"length":1,"stats":{"Line":9}},{"line":83,"address":[807248],"length":1,"stats":{"Line":4}},{"line":84,"address":[901597],"length":1,"stats":{"Line":4}},{"line":85,"address":[1619528,1619614],"length":1,"stats":{"Line":8}},{"line":86,"address":[1619629],"length":1,"stats":{"Line":4}},{"line":87,"address":[807497,807446],"length":1,"stats":{"Line":4}},{"line":88,"address":[1619704],"length":1,"stats":{"Line":4}},{"line":90,"address":[807367],"length":1,"stats":{"Line":4}},{"line":94,"address":[901824],"length":1,"stats":{"Line":5}},{"line":95,"address":[4040228],"length":1,"stats":{"Line":5}},{"line":97,"address":[1619798],"length":1,"stats":{"Line":5}},{"line":100,"address":[816046],"length":1,"stats":{"Line":5}},{"line":101,"address":[1775721,1775658],"length":1,"stats":{"Line":10}},{"line":102,"address":[1027439,1027249],"length":1,"stats":{"Line":5}},{"line":103,"address":[823859],"length":1,"stats":{"Line":5}},{"line":104,"address":[807763],"length":1,"stats":{"Line":5}},{"line":106,"address":[1335943],"length":1,"stats":{"Line":3}},{"line":108,"address":[41430482],"length":1,"stats":{"Line":5}},{"line":109,"address":[808017],"length":1,"stats":{"Line":5}},{"line":111,"address":[1336049],"length":1,"stats":{"Line":5}},{"line":112,"address":[2492048],"length":1,"stats":{"Line":5}},{"line":113,"address":[552123],"length":1,"stats":{"Line":5}},{"line":114,"address":[1027590,1027518,1027582],"length":1,"stats":{"Line":10}},{"line":117,"address":[1620290,1620190],"length":1,"stats":{"Line":2}}],"covered":55,"coverable":55},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","computer_manager.rs"],"content":"use std::fs::File;\nuse std::io::{BufReader, Read};\nuse std::path::Path;\nuse crate::chip8::computer::Chip8Computer;\nuse crate::chip8::cpu_states::Chip8CpuStates;\nuse crate::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction;\nuse crate::chip8::quirk_modes::QuirkMode;\nuse std::thread;\nuse std::thread::sleep;\nuse std::time::{Duration, Instant};\n\npub enum ManagerDumpables {\n Video,\n Registers,\n Keyboard,\n}\n\npub struct Chip8ComputerManager {\n pub core_should_run: bool,\n one_step: bool,\n core_last_cycle_start: Instant,\n computer: Chip8Computer,\n}\n\nimpl Default for Chip8ComputerManager {\n fn default() -\u003e Self {\n Chip8ComputerManager {\n core_should_run: false,\n one_step: false,\n core_last_cycle_start: Instant::now(),\n computer: Chip8Computer::new(),\n }\n }\n}\n\nimpl Chip8ComputerManager {\n\n /// Builds a string that can be displayed at a console giving the user\n /// an idea of the internal state of the Chip-8 Computer\n pub fn status_as_string(\u0026self) -\u003e String {\n // build a string\n format!(\"Should Run: {}\\nLast Cycle Start: {:?}\\nNum Cycles: {}\\nRegisters\\n{}\\nTimers: {}/{}\\nKeypad: \\n{}\\nVideo:\\n{}\",\n self.core_should_run,\n self.core_last_cycle_start,\n self.computer.num_cycles,\n self.computer.registers.format_as_string(),\n self.computer.delay_timer.current(),\n self.computer.sound_timer.current(),\n self.computer.keypad.format_as_string(),\n self.computer.video_memory.format_as_string()\n )\n }\n\n pub fn quirks_mode(\u0026self) -\u003e QuirkMode {\n self.computer.quirk_mode.clone()\n }\n\n pub fn reset(\u0026mut self, mode: QuirkMode) {\n self.one_step = false;\n self.core_should_run = false;\n self.computer.reset(mode);\n }\n pub fn new() -\u003e Chip8ComputerManager {\n let _ = thread::spawn(move || loop {\n let start_time = Instant::now();\n let sleep_time = Instant::now().duration_since(start_time).as_millis();\n sleep(Duration::from_millis((16 - sleep_time) as u64));\n });\n\n Chip8ComputerManager::default()\n }\n\n pub fn start(\u0026mut self) {\n self.core_should_run = true;\n }\n\n pub fn stop(\u0026mut self) {\n self.core_should_run = false\n }\n\n pub fn step(\u0026mut self) {\n self.one_step = true;\n }\n\n pub fn state(\u0026mut self) -\u003e \u0026Chip8Computer {\n \u0026self.computer\n }\n\n pub fn tick(\u0026mut self) -\u003e bool {\n // println!(\"STARTING TICK\");\n let mut did_tick: bool = false;\n if self.one_step | self.core_should_run {\n if let WaitingForInstruction = self.computer.state {\n self.core_last_cycle_start = Instant::now();\n self.computer.step_system();\n did_tick = true\n // println!(\"SYSTEM STEP\");\n }\n };\n if self.one_step {\n println!(\"SYSTEM HALTED AFTER 1 STEP\");\n // stop the CPU for the next cycle, we are only\n // wanting one step.\n self.one_step = false;\n did_tick = true;\n }\n did_tick\n }\n\n pub fn press_key(\u0026mut self, key_index: u8) {\n self.computer.keypad.push_key(key_index);\n if matches!(self.computer.state, Chip8CpuStates::WaitingForKey) {\n self.computer.state = WaitingForInstruction\n }\n }\n pub fn release_key(\u0026mut self, key_index: u8) {\n self.computer.keypad.release_key(key_index);\n }\n\n pub fn sound(managed: \u0026Chip8ComputerManager) -\u003e bool {\n managed.computer.sound_timer.current() \u003e 0\n }\n\n pub fn wait_for_instruction(\u0026mut self) {\n self.computer.state = WaitingForInstruction;\n }\n\n pub fn is_key_pressed(\u0026self, key_index: u8) -\u003e bool {\n self.computer.keypad.pressed(key_index)\n }\n\n pub fn num_cycles(\u0026self) -\u003e i32 {\n self.computer.num_cycles\n }\n\n pub fn load_new_program_to_system_memory(\u0026mut self, bytes_to_load: Vec\u003cu8\u003e) {\n self.reset(self.computer.quirk_mode.clone());\n self.computer.load_bytes_to_memory(0x200, \u0026bytes_to_load);\n }\n\n pub fn dump_to_string(\u0026self, dump_type: ManagerDumpables) -\u003e String {\n match dump_type {\n ManagerDumpables::Video =\u003e self.computer.video_memory.format_as_string(),\n ManagerDumpables::Registers =\u003e self.computer.registers.format_as_string(),\n ManagerDumpables::Keyboard =\u003e self.computer.keypad.format_as_string(),\n }\n }\n\n pub fn load_new_program_from_disk_to_system_memory(\u0026mut self, file_to_load: \u0026Path) {\n // load the file into bytes...\n let file = File::open(file_to_load).unwrap();\n let mut reader = BufReader::new(file);\n let mut chunks = Vec::new();\n\n loop {\n let mut buffer = vec![0u8; 1];\n let bytes_read = reader.read(\u0026mut buffer).unwrap_or(0);\n if bytes_read == 0 {\n break;\n }\n buffer.truncate(bytes_read);\n chunks.push(buffer[0]);\n }\n\n self.load_new_program_to_system_memory(chunks);\n // ...then use the existing method\n self.core_should_run = true;\n }\n}\n","traces":[{"line":26,"address":[783248],"length":1,"stats":{"Line":3}},{"line":30,"address":[41395393],"length":1,"stats":{"Line":4}},{"line":31,"address":[774816],"length":1,"stats":{"Line":3}},{"line":40,"address":[1681616,1682567],"length":1,"stats":{"Line":0}},{"line":42,"address":[775352,774926,775136,775038,775198,775253],"length":1,"stats":{"Line":0}},{"line":46,"address":[4006223],"length":1,"stats":{"Line":0}},{"line":47,"address":[2452370],"length":1,"stats":{"Line":0}},{"line":48,"address":[473372],"length":1,"stats":{"Line":0}},{"line":49,"address":[2452494],"length":1,"stats":{"Line":0}},{"line":50,"address":[783793],"length":1,"stats":{"Line":0}},{"line":54,"address":[2453136],"length":1,"stats":{"Line":1}},{"line":55,"address":[41396469],"length":1,"stats":{"Line":1}},{"line":58,"address":[2453152],"length":1,"stats":{"Line":2}},{"line":59,"address":[41396496],"length":1,"stats":{"Line":2}},{"line":60,"address":[785159],"length":1,"stats":{"Line":2}},{"line":61,"address":[785166],"length":1,"stats":{"Line":2}},{"line":63,"address":[785184],"length":1,"stats":{"Line":1}},{"line":64,"address":[474142],"length":1,"stats":{"Line":2}},{"line":65,"address":[1836740],"length":1,"stats":{"Line":1}},{"line":66,"address":[1589020],"length":1,"stats":{"Line":1}},{"line":67,"address":[557370],"length":1,"stats":{"Line":1}},{"line":70,"address":[2453241],"length":1,"stats":{"Line":1}},{"line":73,"address":[41396592],"length":1,"stats":{"Line":1}},{"line":74,"address":[2453269],"length":1,"stats":{"Line":1}},{"line":77,"address":[776016],"length":1,"stats":{"Line":1}},{"line":78,"address":[776021],"length":1,"stats":{"Line":1}},{"line":81,"address":[41396624],"length":1,"stats":{"Line":1}},{"line":82,"address":[474229],"length":1,"stats":{"Line":1}},{"line":85,"address":[776048],"length":1,"stats":{"Line":1}},{"line":89,"address":[776064],"length":1,"stats":{"Line":0}},{"line":91,"address":[41396669],"length":1,"stats":{"Line":0}},{"line":92,"address":[474274],"length":1,"stats":{"Line":0}},{"line":93,"address":[41396755,41396711],"length":1,"stats":{"Line":0}},{"line":94,"address":[4007345],"length":1,"stats":{"Line":0}},{"line":95,"address":[785400],"length":1,"stats":{"Line":0}},{"line":96,"address":[784622],"length":1,"stats":{"Line":0}},{"line":100,"address":[784685,784568],"length":1,"stats":{"Line":0}},{"line":101,"address":[784640],"length":1,"stats":{"Line":0}},{"line":104,"address":[2453473],"length":1,"stats":{"Line":0}},{"line":105,"address":[784680],"length":1,"stats":{"Line":0}},{"line":107,"address":[41396757],"length":1,"stats":{"Line":0}},{"line":110,"address":[776224],"length":1,"stats":{"Line":1}},{"line":111,"address":[1682965],"length":1,"stats":{"Line":1}},{"line":112,"address":[2453527],"length":1,"stats":{"Line":1}},{"line":113,"address":[785526],"length":1,"stats":{"Line":0}},{"line":116,"address":[41396896],"length":1,"stats":{"Line":1}},{"line":117,"address":[776320],"length":1,"stats":{"Line":1}},{"line":120,"address":[785600],"length":1,"stats":{"Line":0}},{"line":121,"address":[41396949],"length":1,"stats":{"Line":0}},{"line":124,"address":[2453648],"length":1,"stats":{"Line":0}},{"line":125,"address":[784853],"length":1,"stats":{"Line":0}},{"line":128,"address":[41397008],"length":1,"stats":{"Line":1}},{"line":129,"address":[1683152],"length":1,"stats":{"Line":1}},{"line":132,"address":[785712],"length":1,"stats":{"Line":1}},{"line":133,"address":[784933],"length":1,"stats":{"Line":1}},{"line":136,"address":[2453744,2453875],"length":1,"stats":{"Line":2}},{"line":137,"address":[474691,474747],"length":1,"stats":{"Line":4}},{"line":138,"address":[785827],"length":1,"stats":{"Line":2}},{"line":141,"address":[1683360],"length":1,"stats":{"Line":0}},{"line":142,"address":[2453934],"length":1,"stats":{"Line":0}},{"line":143,"address":[785962],"length":1,"stats":{"Line":0}},{"line":144,"address":[2454000],"length":1,"stats":{"Line":0}},{"line":145,"address":[41397350],"length":1,"stats":{"Line":0}},{"line":149,"address":[2454800,2454831,2454048],"length":1,"stats":{"Line":1}},{"line":151,"address":[4008036,4008202],"length":1,"stats":{"Line":4}},{"line":152,"address":[41397585],"length":1,"stats":{"Line":2}},{"line":153,"address":[4008223],"length":1,"stats":{"Line":2}},{"line":155,"address":[777067],"length":1,"stats":{"Line":2}},{"line":156,"address":[475238],"length":1,"stats":{"Line":2}},{"line":157,"address":[777146,777226],"length":1,"stats":{"Line":4}},{"line":158,"address":[1684015],"length":1,"stats":{"Line":2}},{"line":161,"address":[41397917],"length":1,"stats":{"Line":2}},{"line":162,"address":[777447],"length":1,"stats":{"Line":2}},{"line":165,"address":[777349],"length":1,"stats":{"Line":2}},{"line":167,"address":[4008629],"length":1,"stats":{"Line":2}}],"covered":46,"coverable":75},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","cpu_states.rs"],"content":"use serde::{Deserialize, Serialize};\n\n#[derive(Clone, Copy, Default, Serialize, Deserialize, Debug)]\npub enum Chip8CpuStates {\n #[default]\n WaitingForInstruction,\n WaitingForKey,\n ExecutingInstruction,\n Error,\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","delay_timer.rs"],"content":"use serde::{Deserialize, Serialize};\n\n#[derive(Clone, Copy, Serialize, Deserialize, Debug)]\npub struct DelayTimer {\n counter: u8,\n}\n\nimpl Default for DelayTimer {\n fn default() -\u003e Self {\n Self::new()\n }\n}\n\nimpl DelayTimer {\n pub fn current(\u0026self) -\u003e u8 {\n self.counter\n }\n\n pub fn new() -\u003e Self {\n DelayTimer { counter: 0xff }\n }\n\n pub fn reset(\u0026mut self) {\n self.counter = 0xff;\n }\n\n pub fn set_timer(\u0026mut self, new_value: u8) {\n self.counter = new_value\n }\n\n pub fn tick(\u0026mut self) {\n if self.counter \u003e 0 {\n self.counter -= 1;\n }\n }\n}\n","traces":[{"line":9,"address":[1712816],"length":1,"stats":{"Line":1}},{"line":10,"address":[813713],"length":1,"stats":{"Line":1}},{"line":15,"address":[1615936],"length":1,"stats":{"Line":1}},{"line":16,"address":[805269],"length":1,"stats":{"Line":1}},{"line":23,"address":[821408],"length":1,"stats":{"Line":4}},{"line":24,"address":[4036789],"length":1,"stats":{"Line":4}},{"line":27,"address":[1772160],"length":1,"stats":{"Line":2}},{"line":28,"address":[805324],"length":1,"stats":{"Line":2}},{"line":31,"address":[41426768],"length":1,"stats":{"Line":6}},{"line":32,"address":[821495,821454],"length":1,"stats":{"Line":11}},{"line":33,"address":[821469,821497],"length":1,"stats":{"Line":5}}],"covered":11,"coverable":11},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","instructions.rs"],"content":"use crate::chip8::computer::Chip8Computer;\nuse crate::chip8::cpu_states::Chip8CpuStates::WaitingForKey;\nuse crate::chip8::instructions::Chip8CpuInstructions::*;\nuse crate::chip8::quirk_modes::QuirkMode;\nuse crate::chip8::util::InstructionUtil;\nuse crate::constants::*;\nuse log::debug;\nuse rand::Rng;\nuse serde::{Deserialize, Serialize};\nuse std::fmt::{Debug, Display, Formatter};\nuse std::ops::BitAnd;\n\n/*\nnnn or addr - A 12-bit value, the lowest 12 bits of the instruction\nn or nibble - A 4-bit value, the lowest 4 bits of the instruction\nx - A 4-bit value, the lower 4 bits of the high byte of the instruction\ny - A 4-bit value, the upper 4 bits of the low byte of the instruction\nkk or byte - An 8-bit value, the lowest 8 bits of the instruction\n */\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub enum Chip8CpuInstructions {\n /// 0nnn\n /// Exit to System Call at nnn\n SYS(u16),\n /// 00E0\n /// Clear the display.\n CLS,\n /// 00EE\n /// Return from a subroutine.\n ///\n /// The interpreter sets the program counter to the address at the top of the stack,\n /// then subtracts 1 from the stack pointer.\n RET,\n /// 1nnn\n /// Jump to location nnn.\n ///\n /// The interpreter sets the program counter to nnn.\n JPA(u16),\n /// 2nnn\n /// Call subroutine at nnn.\n ///\n /// The interpreter increments the stack pointer, then puts the current PC on the top\n /// of the stack. The PC is then set to nnn.\n CALL(u16),\n /// 0x3xkk\n /// Skip next instruction if Vx = kk.\n ///\n /// The interpreter compares register Vx to kk, and if they are equal, increments the\n /// program counter by 2.\n SEX(u8, u8),\n /// 4xkk\n /// Skip next instruction if Vx != kk.\n ///\n /// The interpreter compares register Vx to kk, and if they are not equal, increments\n /// the program counter by 2.\n SNEB(u8, u8),\n /// 5xy0\n /// Skip next instruction if Vx = Vy.\n ///\n /// The interpreter compares register Vx to register Vy, and if they are equal, increments\n /// the program counter by 2.\n SEY(u8, u8),\n /// 6xkk\n /// Set Vx = kk\n LDR(u8, u8),\n /// 7xkk\n /// The interpreter puts the value kk into register Vx.\n ADD(u8, u8),\n /// 8xy0\n /// Adds the value kk to the value of register Vx, then stores the result in Vx.\n LDRY(u8, u8),\n /// 8xy1\n /// Stores the value of register Vy in register Vx.\n OR(u8, u8),\n /// 8xy2\n /// Set Vx = Vx OR Vy.\n ///\n /// Performs a bitwise OR on the values of Vx and Vy, then stores the result in Vx.\n /// A bitwise OR compares the corrseponding bits from two values, and if either\n /// bit is 1, then the same bit in the result is also 1. Otherwise, it is 0.\n AND(u8, u8),\n /// 8xy3\n /// Set Vx = Vx AND Vy.\n ///\n /// Performs a bitwise AND on the values of Vx and Vy, then stores the result in Vx.\n /// A bitwise AND compares the corrseponding bits from two values, and if both bits\n /// are 1, then the same bit in the result is also 1. Otherwise, it is 0.\n ORY(u8, u8),\n /// 8xy4\n /// Set Vx = Vx XOR Vy.\n ///\n /// Performs a bitwise exclusive OR on the values of Vx and Vy, then stores the\n /// result in Vx. An exclusive OR compares the corrseponding bits from two values,\n /// and if the bits are not both the same, then the corresponding bit in the result\n /// is set to 1. Otherwise, it is 0.\n ADDR(u8, u8),\n /// 8xy5\n /// Set Vx = Vx + Vy, set VF = carry.\n ///\n /// The values of Vx and Vy are added together. If the result is greater than 8 bits\n /// (i.e., \u003e 255,) VF is set to 1, otherwise 0. Only the lowest 8 bits of the result\n /// are kept, and stored in Vx.\n SUB(u8, u8),\n /// 8xy6\n /// Set Vx = Vx - Vy, set VF = NOT borrow.\n ///\n /// If Vx \u003e Vy, then VF is set to 1, otherwise 0. Then Vy is subtracted from Vx, and the\n /// results stored in Vx.\n SHR(u8, u8),\n /// 8xy7\n /// If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0. Then Vx\n /// is divided by 2.\n SUBC(u8, u8),\n /// 8xye\n /// Set Vx = Vy - Vx, set VF = NOT borrow.\n //\n /// If Vy \u003e Vx, then VF is set to 1, otherwise 0. Then Vx is subtracted from Vy, and the\n /// results stored in Vx.\n SHL(u8, u8),\n /// 9xy0\n /// Skip next instruction if Vx != Vy.\n ///\n /// The values of Vx and Vy are compared, and if they are not equal, the program\n /// counter is increased by 2.\n SNEY(u8, u8),\n /// Annn\n /// Set I = nnn.\n ///\n /// The value of register I is set to nnn\n LDIA(u16),\n /// Bnnn\n /// Jump to location nnn + V0.\n ///\n /// The program counter is set to nnn plus the value of V0.\n JPI(u16),\n /// Cxkk\n /// Set Vx = random byte AND kk.\n ///\n /// The interpreter generates a random number from 0 to 255, which is then\n /// ANDed with the value kk. The results are stored in Vx. See instruction\n /// 8xy2 for more information on AND.\n RND(u8, u8),\n /// Dxyn\n /// Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision.\n ///\n /// In Chip-8,\n ///\n /// The interpreter reads n bytes from memory, starting at the address stored in I.\n /// These bytes are then displayed as sprites on screen at coordinates (Vx, Vy).\n /// Sprites are XORed onto the existing screen. If this causes any pixels to be erased,\n /// VF is set to 1, otherwise it is set to 0. If the sprite is positioned so part of\n /// it is outside the coordinates of the display, it wraps around to the opposite side\n /// of the screen. See instruction 8xy3 for more information on XOR, and section 2.4,\n /// Display, for more information on the Chip-8 screen and sprites.\n ///\n /// In SCHIP\n ///\n /// Show N-byte sprite from M(I) at Coords (VX,VY) VF = Collision.\n /// If N=0 AND ExtendedMode, 16x16 sprite\n DRW(u8, u8, u8),\n /// Ex9E\n /// Skip next instruction if key with the value of Vx is pressed.\n ///\n /// Checks the keyboard, and if the key corresponding to the value of Vx is currently\n /// in the down position, PC is increased by 2.\n SKP(u8),\n /// ExA1\n /// Skip next instruction if key with the value of Vx is not pressed.\n ///\n /// Checks the keyboard, and if the key corresponding to the value of Vx is currently in\n /// the up position, PC is increased by 2.\n /// On XO Chip, Skips 2 more bytes when the instruction is 4 bytes long\n SKNP(u8),\n /// Fx07\n /// The value of DT is placed into Vx.\n LDRD(u8),\n /// Fx0A\n /// Wait for a key press, store the value of the key in Vx.\n ///\n /// All execution stops until a key is pressed, then the value of that key is stored in Vx.\n LDRK(u8),\n /// Fx15\n /// Set delay timer = Vx.\n ///\n /// DT is set equal to the value of Vx.\n LDD(u8), // 0xFx15 Set Delay Timer\n /// Fx18\n /// Set sound timer = Vx.\n ///\n /// ST is set equal to the value of Vx.\n LDIS(u8),\n /// Fx1E - ADD I, Vx\n /// Set I = I + Vx.\n ///\n /// The values of I and Vx are added, and the results are stored in I.\n ADDI(u8),\n /// Fx29 - LD F, Vx\n /// Set I = location of sprite for digit Vx.\n ///\n /// The value of I is set to the location for the hexadecimal sprite\n /// corresponding to the value of Vx. See section 2.4, Display, for\n /// more information on the Chip-8 hexadecimal font.\n LDFX(u8),\n /// Fx33 - LD B, Vx\n /// Store BCD representation of Vx in memory locations I, I+1, and I+2.\n ///\n /// The interpreter takes the decimal value of Vx, and places the hundreds\n /// digit in memory at location in I, the tens digit at location I+1, and the\n /// ones digit at location I+2.\n BCD(u8),\n /// Fx55 - LD [I], Vx\n /// Store registers V0 through Vx in memory starting at location I.\n ///\n /// The interpreter copies the values of registers V0 through Vx into memory,\n /// starting at the address in I.\n LDIX(u8),\n /// Fx65 - LD Vx, [I]\n /// Read registers V0 through Vx from memory starting at location I.\n ///\n /// The interpreter reads values from memory starting at location I into registers\n /// V0 through Vx.\n LDRI(u8),\n XXXXERRORINSTRUCTION,\n // 00Dn - CHIP8 - SCHIP * XOCHIP\n /* START OF SCHIP-8 */\n /// 00CN - CHIP8 * SCHIP * XOCHIP\n ///\n /// Scrolll Display N Lines Down\n SCD(u8),\n /// 00FB\n ///\n /// Scroll 4 lines Right\n SCR,\n /// 00FC\n ///\n /// Scroll 4 lines Left\n SCL,\n /// 00FE\n ///\n /// Disable Extended Mode\n LOW,\n /// 00FF\n ///\n /// Enable Extended Mode\n HIGH,\n /// 00FD\n ///\n /// Exit App\n EXIT,\n /// FX30\n ///\n /// Point I to 10 yte font sprite for digit VX (0..9)\n LDF2(u8),\n /// FX75\n ///\n /// Store V0..VX in RPL user flags (X \u003c= 7\n STR(u8),\n /// FX85\n ///\n /// Load V0..VX from RPL user flags (X \u003c= 7)\n LIDR(u8),\n /// 0xBxNN\n ///\n /// Jump to Address XNN+Vx\n JPX(u8, u16),\n /// 0x00Cn\n ///\n /// scroll screen content down N pixel, in XO-CHIP only selected bit\n /// planes are scrolled (Quirks are HP48 specific)\n SCU(u8),\n /// 0xNNNN\n ///\n /// data word\n /// used for specifying data to be used in system memory\n DW(u16),\n}\n\nimpl Chip8CpuInstructions {\n pub fn name(\u0026self) -\u003e \u0026str {\n match self {\n ADDI(_) =\u003e INST_ADDI,\n ADD(_, _) =\u003e INST_ADD,\n ADDR(_, _) =\u003e INST_ADDR,\n AND(_, _) =\u003e INST_AND,\n CLS =\u003e INST_CLS,\n CALL(_) =\u003e INST_CALL,\n DRW(_, _, _) =\u003e INST_DRW,\n EXIT =\u003e INST_EXIT,\n JPA(_) =\u003e INST_JPA,\n JPI(_) =\u003e INST_JPI,\n BCD(_) =\u003e INST_BCD,\n LDD(_) =\u003e INST_LDD,\n LDFX(_) =\u003e INST_LDF,\n LDF2(_) =\u003e INST_LDF2,\n LDIA(_) =\u003e INST_LDIA,\n LDIX(_) =\u003e INST_LDIX,\n LIDR(_) =\u003e INST_LIDR,\n LDIS(_) =\u003e INST_LDIS,\n LDR(_, _) =\u003e INST_LDR,\n LDRD(_) =\u003e INST_LDRD,\n LDRI(_) =\u003e INST_LDRI,\n LDRK(_) =\u003e INST_LDRK,\n LDRY(_, _) =\u003e INST_LDRY,\n OR(_, _) =\u003e INST_OR,\n RET =\u003e INST_RET,\n RND(_, _) =\u003e INST_RND,\n SCD(_) =\u003e INST_SCD,\n SCL =\u003e INST_SCL,\n SCR =\u003e INST_SCR,\n SEX(_, _) =\u003e INST_SEX,\n SEY(_, _) =\u003e INST_SEY,\n SHL(_, _) =\u003e INST_SHL,\n SHR(_, _) =\u003e INST_SHR,\n SKP(_) =\u003e INST_SKP,\n SNEB(_, _) =\u003e INST_SNEB,\n SNEY(_, _) =\u003e INST_SNEY,\n SKNP(_) =\u003e INST_SKNP,\n STR(_) =\u003e INST_STR,\n SUB(_, _) =\u003e INST_SUB,\n SUBC(_, _) =\u003e INST_SUBC,\n SYS(_) =\u003e INST_SYS,\n LOW =\u003e INST_LOW,\n HIGH =\u003e INST_HIGH,\n ORY(_, _) =\u003e INST_ORY,\n JPX(_, _) =\u003e INST_JPX,\n XXXXERRORINSTRUCTION =\u003e \"XX ERROR XX\",\n SCU(_) =\u003e INST_SCU,\n DW(_) =\u003e INST_DW,\n }\n }\n\n pub fn operands(\u0026self) -\u003e String {\n match self {\n JPX(x, addr) =\u003e {\n let addr_for_display = (*x as u16) \u003c\u003c 8 | *addr;\n format!(\"0x{x:02x}, 0x{addr_for_display:04x}\")\n }\n Chip8CpuInstructions::DW(addr)\n | Chip8CpuInstructions::SYS(addr)\n | Chip8CpuInstructions::JPI(addr)\n | Chip8CpuInstructions::JPA(addr)\n | Chip8CpuInstructions::LDIA(addr)\n | Chip8CpuInstructions::CALL(addr) =\u003e {\n format!(\"0x{addr:04x}\")\n }\n Chip8CpuInstructions::SEX(x, byte)\n | Chip8CpuInstructions::SNEB(x, byte)\n | Chip8CpuInstructions::LDR(x, byte)\n | Chip8CpuInstructions::RND(x, byte)\n | Chip8CpuInstructions::ADD(x, byte) =\u003e {\n format!(\"0x{x:02x}, 0x{byte:02x}\")\n }\n // Reg, Reg\n SEY(x, y)\n | Chip8CpuInstructions::LDRY(x, y)\n | Chip8CpuInstructions::OR(x, y)\n | Chip8CpuInstructions::AND(x, y)\n | Chip8CpuInstructions::ORY(x, y)\n | Chip8CpuInstructions::ADDR(x, y)\n | Chip8CpuInstructions::SUB(x, y)\n | Chip8CpuInstructions::SHR(x, y)\n | Chip8CpuInstructions::SUBC(x, y)\n | Chip8CpuInstructions::SHL(x, y)\n | Chip8CpuInstructions::SNEY(x, y) =\u003e {\n format!(\"0x{x:01x}, 0x{y:01x}\")\n }\n // Reg, Reg, Nibble\n Chip8CpuInstructions::DRW(x, y, nibble) =\u003e {\n format!(\"0x{x:02x}, 0x{y:02x}, 0x{nibble:02x}\")\n }\n // Registers. 0-F\n Chip8CpuInstructions::SCU(x)\n | Chip8CpuInstructions::LDD(x)\n | Chip8CpuInstructions::LDIS(x)\n | Chip8CpuInstructions::ADDI(x)\n | Chip8CpuInstructions::LDFX(x)\n | Chip8CpuInstructions::BCD(x)\n | Chip8CpuInstructions::LDIX(x)\n | Chip8CpuInstructions::LDRD(x)\n | Chip8CpuInstructions::LDRK(x)\n | Chip8CpuInstructions::LDRI(x)\n | Chip8CpuInstructions::LDF2(x)\n | Chip8CpuInstructions::STR(x)\n | Chip8CpuInstructions::LIDR(x)\n | Chip8CpuInstructions::SCD(x)\n | Chip8CpuInstructions::SKNP(x)\n | Chip8CpuInstructions::SKP(x) =\u003e {\n format!(\"0x{x:02x}\")\n }\n Chip8CpuInstructions::EXIT\n | Chip8CpuInstructions::HIGH\n | Chip8CpuInstructions::LOW\n | Chip8CpuInstructions::SCL\n | Chip8CpuInstructions::XXXXERRORINSTRUCTION\n | Chip8CpuInstructions::SCR\n | Chip8CpuInstructions::CLS\n | Chip8CpuInstructions::RET =\u003e String::new(),\n }\n }\n}\n\nimpl Display for Chip8CpuInstructions {\n fn fmt(\u0026self, f: \u0026mut Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n let ops = self.operands();\n let space = if ops.is_empty() { \"\" } else { \" \" };\n write!(f, \"{}{}{}\", self.name(), space, ops)\n }\n}\n\nimpl Chip8CpuInstructions {\n pub fn from_str(input: \u0026str) -\u003e Chip8CpuInstructions {\n let mut parts = input.split(\" \");\n // print!(\"THERE ARE {} PARTS\", parts.clone().count());\n let first_part = parts.next().unwrap_or(\"\").to_ascii_uppercase();\n // take the next value...\n // ...strip off the extra...\n // ...convert it to an integer from base 16\n let param1 = u16::from_str_radix(\n parts\n .next()\n .unwrap_or(\"0\")\n .to_ascii_lowercase()\n .trim_start_matches(\"0x\")\n .trim_end_matches(\",\"),\n 16,\n )\n .unwrap_or(0);\n let param2 = u16::from_str_radix(\n parts\n .next()\n .unwrap_or(\"0\")\n .to_ascii_lowercase()\n .trim_start_matches(\"0x\")\n .trim_end_matches(\",\"),\n 16,\n )\n .unwrap_or(0);\n let param3 = u16::from_str_radix(\n parts\n .next()\n .unwrap_or(\"0\")\n .to_ascii_lowercase()\n .trim_start_matches(\"0x\")\n .trim_end_matches(\",\"),\n 16,\n )\n .unwrap_or(0);\n println!(\n \"\\tFirst part is {:?} / {:?} / {:?} / {:?}\",\n first_part, param1, param2, param3\n );\n match first_part.as_str() {\n INST_ADDI =\u003e ADDI(param1 as u8),\n INST_ADD =\u003e ADD(param1 as u8, param2 as u8),\n INST_CLS =\u003e CLS,\n INST_DRW =\u003e DRW(param1 as u8, param2 as u8, param3 as u8),\n INST_CALL =\u003e CALL(param1),\n INST_SYS =\u003e SYS(param1),\n INST_RET =\u003e RET,\n INST_JPA =\u003e JPA(param1),\n INST_JPI =\u003e JPI(param1),\n INST_SEX =\u003e SEX(param1 as u8, param2 as u8),\n INST_SNEB =\u003e SNEB(param1 as u8, param2 as u8),\n INST_SCD =\u003e SCD(param1 as u8),\n INST_STR =\u003e STR(param1 as u8),\n INST_SCL =\u003e SCL,\n INST_EXIT =\u003e EXIT,\n INST_LOW =\u003e LOW,\n INST_HIGH =\u003e HIGH,\n INST_SEY =\u003e SEY(param1 as u8, param2 as u8),\n INST_LDRY =\u003e LDRY(param1 as u8, param2 as u8),\n INST_LDR =\u003e LDR(param1 as u8, param2 as u8),\n INST_OR =\u003e OR(param1 as u8, param2 as u8),\n INST_AND =\u003e AND(param1 as u8, param2 as u8),\n INST_ORY =\u003e ORY(param1 as u8, param2 as u8),\n INST_ADDR =\u003e ADDR(param1 as u8, param2 as u8),\n INST_SUB =\u003e SUB(param1 as u8, param2 as u8),\n INST_SHR =\u003e SHR(param1 as u8, param2 as u8),\n INST_SHL =\u003e SHL(param1 as u8, param2 as u8),\n INST_SUBC =\u003e SUBC(param1 as u8, param2 as u8),\n INST_SNEY =\u003e SNEY(param1 as u8, param2 as u8),\n INST_LDIA =\u003e LDIA(param1),\n INST_RND =\u003e RND(param1 as u8, param2 as u8),\n INST_SKP =\u003e SKP(param1 as u8),\n INST_SKNP =\u003e SKNP(param1 as u8),\n INST_LDRD =\u003e LDRD(param1 as u8),\n INST_LDRK =\u003e LDRK(param1 as u8),\n INST_LDRI =\u003e LDRI(param1 as u8),\n INST_BCD =\u003e BCD(param1 as u8),\n INST_LDF =\u003e LDFX(param1 as u8),\n INST_LDF2 =\u003e LDF2(param1 as u8),\n INST_LDIX =\u003e LDIX(param1 as u8),\n INST_LIDR =\u003e LIDR(param1 as u8),\n INST_LDIS =\u003e LDIS(param1 as u8),\n INST_LDD =\u003e LDD(param1 as u8),\n INST_JPX =\u003e JPX(param1 as u8, param2),\n INST_DW =\u003e DW(param1 as u16),\n _ =\u003e XXXXERRORINSTRUCTION,\n }\n }\n\n pub fn encode(\u0026self) -\u003e u16 {\n match self {\n SYS(target) =\u003e target \u0026 0x0FFF,\n CLS =\u003e 0x00E0,\n RET =\u003e 0x00EE,\n JPA(new_addr) =\u003e 0x1000 | (new_addr \u0026 0x0FFF),\n CALL(address) =\u003e 0x2000 | (address \u0026 0x0FFF),\n SEX(vx_register, byte) =\u003e {\n 0x3000 | ((*vx_register as u16) \u003c\u003c 8) | (*byte as u16)\n }\n SNEB(vx_register, byte) =\u003e {\n 0x4000 | ((*vx_register as u16) \u003c\u003c 8) | (*byte as u16)\n }\n Chip8CpuInstructions::SEY(x_register, y_register) =\u003e {\n 0x5000 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::LDR(x_register, byte) =\u003e {\n 0x6000 | ((*x_register as u16) \u003c\u003c 8) | (*byte as u16)\n }\n Chip8CpuInstructions::ADD(x_register, byte) =\u003e {\n 0x7000 | ((*x_register as u16) \u003c\u003c 8) | (*byte as u16)\n }\n Chip8CpuInstructions::LDRY(x_register, y_register) =\u003e {\n 0x8000 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::OR(x_register, y_register) =\u003e {\n 0x8001 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::AND(x_register, y_register) =\u003e {\n 0x8002 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::ORY(x_register, y_register) =\u003e {\n 0x8003 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::ADDR(x_register, y_register) =\u003e {\n 0x8004 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::SUB(x_register, y_register) =\u003e {\n 0x8005 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::SHR(x_register, y_register) =\u003e {\n 0x8006 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::SUBC(x_register, y_register) =\u003e {\n 0x8007 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::SHL(x_register, y_register) =\u003e {\n 0x800E | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::SNEY(x_register, y_register) =\u003e {\n 0x9000 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::LDIA(addr) =\u003e 0xA000 | addr,\n Chip8CpuInstructions::JPI(addr) =\u003e 0xB000 | addr,\n JPX(x_register, addr) =\u003e (0xb000 | (*x_register as u16) \u003c\u003c 8) | *addr,\n Chip8CpuInstructions::RND(x_register, byte) =\u003e {\n 0xC000 | ((*x_register as u16) \u003c\u003c 8) | (*byte as u16)\n }\n Chip8CpuInstructions::DRW(x_register, y_register, height) =\u003e {\n 0xD000\n | ((*x_register as u16) \u003c\u003c 8)\n | ((*y_register as u16) \u003c\u003c 4)\n | (*height as u16)\n }\n Chip8CpuInstructions::SKP(x_register) =\u003e 0xE09E | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::SKNP(x_register) =\u003e 0xE0A1 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LDRD(x_register) =\u003e 0xF007 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LDRK(x_register) =\u003e 0xF00A | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LDD(x_register) =\u003e 0xF015 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LDIS(x_register) =\u003e 0xF018 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::ADDI(x_register) =\u003e 0xF01E | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LDFX(x_register) =\u003e 0xF029 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::BCD(x_register) =\u003e 0xF033 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LDIX(x_register) =\u003e 0xF055 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LDRI(x_register) =\u003e 0xF065 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::SCD(x_register) =\u003e 0x00C0 | (*x_register as u16),\n Chip8CpuInstructions::SCR =\u003e 0x00FB,\n Chip8CpuInstructions::SCL =\u003e 0x00FC,\n Chip8CpuInstructions::LOW =\u003e 0x00FE,\n Chip8CpuInstructions::HIGH =\u003e 0x00FF,\n Chip8CpuInstructions::EXIT =\u003e 0x00FD,\n Chip8CpuInstructions::LDF2(x_register) =\u003e 0xF030 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::STR(x_register) =\u003e 0xF075 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LIDR(x_register) =\u003e 0xF085 | ((*x_register as u16) \u003c\u003c 8),\n SCU(x_register) =\u003e 0x00D0 | (*x_register as u16),\n DW(address) =\u003e *address as u16,\n XXXXERRORINSTRUCTION =\u003e XXXXERRORINSTRUCTION_ENCODED,\n }\n }\n pub fn decode(input: u16, quirk_mode: \u0026QuirkMode) -\u003e Chip8CpuInstructions {\n let x_param = InstructionUtil::read_x_from_instruction(input);\n let y_param = InstructionUtil::read_y_from_instruction(input);\n let addr_param = InstructionUtil::read_addr_from_instruction(input);\n let byte_param = InstructionUtil::read_byte_from_instruction(input);\n let nibble_param = InstructionUtil::read_nibble_from_instruction(input);\n let ubln = InstructionUtil::read_upper_byte_lower_nibble(input);\n let last_byte = (input \u0026 0xFF) as u8;\n let last_nibble = (input \u0026 0xF) as u8;\n\n match input {\n 0x00C0..=0x00CF =\u003e match quirk_mode {\n QuirkMode::Chip8 =\u003e XXXXERRORINSTRUCTION,\n QuirkMode::XOChip =\u003e SCD(last_nibble),\n QuirkMode::SChipModern =\u003e SCD(last_nibble),\n },\n 0x00E0 =\u003e Chip8CpuInstructions::CLS,\n 0x00EE =\u003e Chip8CpuInstructions::RET,\n 0x00FB =\u003e {\n match quirk_mode {\n QuirkMode::Chip8 =\u003e {\n // does not exist on Chip8\n XXXXERRORINSTRUCTION\n }\n QuirkMode::XOChip =\u003e Chip8CpuInstructions::SCR,\n QuirkMode::SChipModern =\u003e Chip8CpuInstructions::SCR,\n }\n }\n 0x00FC =\u003e Chip8CpuInstructions::SCL,\n 0x00FD =\u003e Chip8CpuInstructions::EXIT,\n 0x00FE =\u003e match quirk_mode {\n QuirkMode::Chip8 =\u003e XXXXERRORINSTRUCTION,\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e LOW,\n },\n 0x00FF =\u003e match quirk_mode {\n QuirkMode::Chip8 =\u003e XXXXERRORINSTRUCTION,\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e HIGH,\n },\n 0x0000..=0x0FFF =\u003e match quirk_mode {\n QuirkMode::Chip8 =\u003e Chip8CpuInstructions::SYS(addr_param),\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e XXXXERRORINSTRUCTION,\n },\n 0x1000..=0x1FFF =\u003e JPA(addr_param),\n 0x2000..=0x2FFF =\u003e CALL(addr_param),\n 0x3000..=0x3FFF =\u003e SEX(x_param, byte_param),\n 0x4000..=0x4FFF =\u003e SNEB(x_param, byte_param),\n 0x5000..=0x5FF0 if input \u0026 0x01 == 0 =\u003e SEY(x_param, y_param),\n 0x6000..=0x6FFF =\u003e LDR(x_param, byte_param),\n 0x7000..=0x7FFF =\u003e ADD(x_param, byte_param),\n 0x8000..=0x8FFE =\u003e match last_nibble {\n 0x0 =\u003e Chip8CpuInstructions::LDRY(x_param, y_param),\n 0x1 =\u003e Chip8CpuInstructions::OR(x_param, y_param),\n 0x2 =\u003e Chip8CpuInstructions::AND(x_param, y_param),\n 0x3 =\u003e Chip8CpuInstructions::ORY(x_param, y_param),\n 0x4 =\u003e Chip8CpuInstructions::ADDR(x_param, y_param),\n 0x5 =\u003e Chip8CpuInstructions::SUB(x_param, y_param),\n 0x6 =\u003e Chip8CpuInstructions::SHR(x_param, y_param),\n 0x7 =\u003e Chip8CpuInstructions::SUBC(x_param, y_param),\n 0xE =\u003e Chip8CpuInstructions::SHL(x_param, y_param),\n _ =\u003e XXXXERRORINSTRUCTION,\n },\n 0x9000..=0x9FF0 if input \u0026 0x01 == 0 =\u003e Chip8CpuInstructions::SNEY(x_param, y_param),\n 0xA000..=0xAFFF =\u003e Chip8CpuInstructions::LDIA(addr_param),\n 0xB000..=0xBFFF =\u003e Chip8CpuInstructions::JPI(addr_param),\n 0xC000..=0xCFFF =\u003e Chip8CpuInstructions::RND(x_param, byte_param),\n 0xD000..=0xDFFF =\u003e Chip8CpuInstructions::DRW(x_param, y_param, nibble_param),\n 0xE09E..=0xEFA1 =\u003e match last_byte {\n 0x9E =\u003e Chip8CpuInstructions::SKP(ubln),\n 0xA1 =\u003e Chip8CpuInstructions::SKNP(ubln),\n _ =\u003e XXXXERRORINSTRUCTION,\n },\n 0xF007..=0xFF65 =\u003e match last_byte {\n 0x07 =\u003e Chip8CpuInstructions::LDRD(ubln),\n 0x0A =\u003e Chip8CpuInstructions::LDRK(ubln),\n 0x15 =\u003e Chip8CpuInstructions::LDD(ubln),\n 0x18 =\u003e Chip8CpuInstructions::LDIS(ubln),\n 0x1E =\u003e Chip8CpuInstructions::ADDI(ubln),\n 0x29 =\u003e Chip8CpuInstructions::LDFX(ubln),\n 0x30 =\u003e Chip8CpuInstructions::LDF2(ubln),\n 0x33 =\u003e Chip8CpuInstructions::BCD(ubln),\n 0x55 =\u003e Chip8CpuInstructions::LDIX(ubln),\n 0x65 =\u003e Chip8CpuInstructions::LDRI(ubln),\n 0x75 =\u003e Chip8CpuInstructions::STR(ubln),\n 0x85 =\u003e Chip8CpuInstructions::LIDR(ubln),\n _ =\u003e XXXXERRORINSTRUCTION,\n },\n _ =\u003e DW(addr_param),\n }\n }\n pub fn execute(\u0026self, input: \u0026mut Chip8Computer) -\u003e Chip8Computer {\n // print!(\"INSTRUCTION {}\", self);\n // let start_time = Instant::now();\n let start_pc = input.registers.peek_pc();\n input.registers.poke_pc(start_pc + 2);\n match self {\n // 0x0nnn Exit to System Call\n Chip8CpuInstructions::SYS(new_address) =\u003e {\n input.registers.poke_pc(*new_address);\n }\n // * 0x00E0 Clear Screen\n Chip8CpuInstructions::CLS =\u003e {\n input.video_memory.cls();\n }\n // 0x00EE Return from Subroutine\n Chip8CpuInstructions::RET =\u003e {\n input.registers.poke_pc(input.stack.pop());\n }\n // 0x1nnn Jump to Address\n Chip8CpuInstructions::JPA(new_address) =\u003e {\n input.registers.poke_pc(*new_address);\n }\n // 0x2nnn Call Subroutine\n Chip8CpuInstructions::CALL(new_address) =\u003e {\n let return_address = input.registers.peek_pc();\n input.registers.poke_pc(*new_address);\n input.stack.push(\u0026return_address);\n }\n // 0x3xkk Skip next instruction if Vx = kk.\n Chip8CpuInstructions::SEX(vx_register, byte) =\u003e {\n if input.registers.peek(*vx_register) == { *byte } {\n input.registers.advance_pc();\n }\n }\n // 0x4xkk Skip next instruction if Vx != kk\n Chip8CpuInstructions::SNEB(x, byte) =\u003e {\n // 4xkk - SNE Vx, byte\n // Skip next instruction if Vx != kk.\n //\n // The interpreter compares register Vx to kk, and if they are not equal,\n // increments the program counter by 2.\n if input.registers.peek(*x) != *byte {\n input.registers.advance_pc();\n }\n }\n // 0x5xy0 Skip next instruction if Vx == Vy\n Chip8CpuInstructions::SEY(x, y) =\u003e {\n if input.registers.peek(*x) == input.registers.peek(*y) {\n input.registers.advance_pc();\n }\n }\n // 0x6xkk Set Vx = kk\n Chip8CpuInstructions::LDR(register, byte) =\u003e {\n input.registers.poke(*register, *byte);\n }\n // 0x7xkk Set Vx = Vx + kk\n Chip8CpuInstructions::ADD(vx_register, byte) =\u003e {\n input.registers.poke(\n *vx_register,\n (input.registers.peek(*vx_register) as u16 + *byte as u16) as u8,\n );\n }\n // 0x8xy0 Set value of Vy in Vx\n Chip8CpuInstructions::LDRY(x, y) =\u003e {\n input.registers.poke(*x, input.registers.peek(*y));\n }\n // 0x8xy1 Set Vx = Vx OR Vy\n Chip8CpuInstructions::OR(x, y) =\u003e {\n // shift them to 16 bit\n let working_16_x = input.registers.peek(*x) as u16;\n let working_16_y = input.registers.peek(*y) as u16;\n // OR them\n let working_16_or = working_16_x | working_16_y;\n // shift them back to 8 bit.\n input.registers.poke(*x, working_16_or as u8);\n // reset of VF quirk\n input.registers.poke(0x0f, 0x00);\n debug!(\"OrVxVy [0x{x:1x}] [0x{y:1x}]\")\n }\n // 0x8xy2 Set Vx = Vx AND Vy\n AND(x, y) =\u003e {\n let lhs_16 = input.registers.peek(*x) as u16;\n let rhs_16 = input.registers.peek(*y) as u16;\n // reset of VF quirk\n input.registers.poke(0x0f, 0x00);\n input.registers.poke(*x, (lhs_16 \u0026 rhs_16) as u8);\n }\n // 0x8xy3 Set Vx = Vx XOR Vy\n ORY(x, y) =\u003e {\n let lhs_16 = input.registers.peek(*x) as u16;\n let rhs_16 = input.registers.peek(*y) as u16;\n // reset of VF quirk\n input.registers.poke(0x0f, 0x00);\n input.registers.poke(*x, (lhs_16 ^ rhs_16) as u8);\n }\n // 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)\n ADDR(x, y) =\u003e {\n let lhs = input.registers.peek(*x) as i16;\n let rhs = input.registers.peek(*y) as i16;\n let working = lhs + rhs;\n input.registers.poke(*x, working as u8);\n\n if working \u003e= 0x100 {\n input.registers.poke(0xf, 0x01);\n } else {\n input.registers.poke(0x0f, 0x00);\n }\n }\n SUB(x, y) =\u003e {\n // 8xy5 - SUB Vx, Vy\n // Set Vx = Vx - Vy, set VF = NOT borrow.\n //\n // If Vx \u003e Vy, then VF is set to 1, otherwise 0. Then Vy is subtracted from Vx, and the results stored in Vx.\n\n let lhs = input.registers.peek(*x);\n let rhs = input.registers.peek(*y);\n\n let result;\n\n let borrow_flag: u8 = if rhs \u003e lhs {\n result = (lhs as u16 + 0x100) - rhs as u16;\n 0\n } else {\n result = lhs as u16 - rhs as u16;\n 1\n };\n\n input.registers.poke(*x, result as u8);\n input.registers.poke(0x0f, borrow_flag);\n }\n Chip8CpuInstructions::SHR(x, _) =\u003e {\n // 8xy6 - SHR Vx {, Vy}\n // Set Vx = Vx SHR 1.\n // SHIFT 1 Bit ----\u003e\n // If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0. Then Vx is divided by 2.\n let initial_value = input.registers.peek(*x);\n\n // overflow check\n let rotated = initial_value \u003e\u003e 1;\n input.registers.poke(*x, rotated);\n if initial_value.bitand(0b1) == 1 {\n input.registers.poke(0x0f, 0x01);\n } else {\n input.registers.poke(0x0f, 0x00);\n }\n }\n Chip8CpuInstructions::SUBC(x, y) =\u003e {\n // 8xy7 - SUBN Vx, Vy\n // Set Vx = Vy - Vx, set VF = NOT borrow.\n //\n // If Vy \u003e Vx, then VF is set to 1, otherwise 0. Then Vx is subtracted from Vy, and the results stored in Vx.\n let y_register = input.registers.peek(*y);\n let x_register = input.registers.peek(*x);\n\n let (new_value, value_to_poke) = if y_register \u003c x_register {\n (0, (y_register as u16 + 256) - x_register as u16)\n } else {\n (1, (y_register - x_register) as u16)\n };\n input.registers.poke(*x, value_to_poke as u8);\n input.registers.poke(0xf, new_value);\n }\n\n Chip8CpuInstructions::SHL(x, _) =\u003e {\n // 8xyE - SHL Vx {, Vy}\n // Set Vx = Vx SHL 1.\n //\n // If the most-significant bit of Vx is 1, then VF is set to 1, otherwise to 0. Then Vx is multiplied by 2.\n let initial_value = input.registers.peek(*x);\n\n // overflow check\n let rotated = initial_value \u003c\u003c 1;\n input.registers.poke(*x, rotated);\n if initial_value.bitand(0b10000000) == 0b10000000 {\n input.registers.poke(0x0f, 0x01);\n } else {\n input.registers.poke(0x0f, 0x00);\n }\n }\n Chip8CpuInstructions::SNEY(vx_register, vy_register) =\u003e {\n // 9xy0 - SNE Vx, Vy\n // Skip next instruction if Vx != Vy.\n //\n // The values of Vx and Vy are compared, and if they are not equal, the program counter is increased by 2.\n\n let x_reg_value = input.registers.peek(*vx_register);\n let y_reg_value = input.registers.peek(*vy_register);\n if x_reg_value != y_reg_value {\n input.registers.advance_pc();\n }\n }\n Chip8CpuInstructions::LDIA(new_index) =\u003e {\n // Annn - LD I, addr\n // Set I = nnn.\n //\n // The value of register I is set to nnn.\n // debug!(\"LdiAddr [0x{new_index:3x}]\");\n input.registers.poke_i(*new_index);\n }\n // 0xBnnn Jump to nnn+V0\n Chip8CpuInstructions::JPI(addr) =\u003e {\n // Bnnn - JP V0, addr\n // Jump to location nnn + V0.\n //\n // The program counter is set to nnn plus the value of V0.\n input\n .registers\n .poke_pc(input.registers.peek(0) as u16 + addr);\n }\n // 0xBxnn Jump to Xnn+Vx\n JPX(vx_register, addr) =\u003e {\n let x_reg_value: u16 = input.registers.peek(*vx_register) as u16;\n let shifted_x_reg = x_reg_value \u003c\u003c 8;\n let added_addr = shifted_x_reg | addr;\n let final_addr = added_addr + shifted_x_reg;\n println!(\"JPX -\u003e {x_reg_value:02x} {shifted_x_reg:04x} {added_addr:04x} {final_addr:04x}\");\n input.registers.poke_i(final_addr);\n }\n Chip8CpuInstructions::RND(x, byte) =\u003e {\n // Cxkk - RND Vx, byte\n // Set Vx = random byte AND kk.\n //\n // The interpreter generates a random number from 0 to 255,\n // which is then ANDed with the value kk.\n // The results are stored in Vx.\n let mut rng = rand::rng();\n let new_value: u8 = rng.random();\n let and_value: u8 = *byte;\n let result = new_value \u0026 and_value;\n debug!(\"RANDOM: [{new_value:02x}] AND: [{and_value:02x} Result: [{result:02x}]\");\n input.registers.poke(*x, new_value \u0026 { *byte })\n }\n Chip8CpuInstructions::DRW(y, x, n) =\u003e {\n // Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision.\n\n // The interpreter reads n bytes from memory, starting at the address stored in I.\n\n // These bytes are then displayed as sprites on screen at coordinates (Vx, Vy).\n // Sprites are XORed onto the existing screen.\n // If this causes any pixels to be erased, VF is set to 1,\n // otherwise it is set to 0.\n // If the sprite is positioned so part of it is outside the coordinates of the display,\n // it wraps around to the opposite side of the screen.\n\n let source_memory_offset = input.registers.peek_i();\n let x_offset = input.registers.peek(*x) as u16;\n let y_offset = input.registers.peek(*y) as u16;\n if input.video_memory.is_highres() {\n // if n == 0 we have a 16 row sprite (font maybe)\n let actual_num_loops = if *n == 0u8 { 16 } else { *n };\n for byte_index in 0..actual_num_loops {\n let current_byte =\n input.memory.peek(byte_index as u16 + source_memory_offset);\n let next_byte = input\n .memory\n .peek(byte_index as u16 + 1u16 + source_memory_offset);\n let x_offset = (x_offset + byte_index as u16) * 64;\n for bit_index in 0..8 {\n input.video_memory.poke(\n x_offset + (y_offset + bit_index as u16),\n (current_byte \u0026 (0x80 \u003e\u003e bit_index)) != 0,\n );\n input.video_memory.poke(\n x_offset + (y_offset + bit_index as u16) + 8,\n (next_byte \u0026 (0x80 \u003e\u003e bit_index)) != 0,\n );\n }\n }\n } else {\n for byte_index in 0..*n {\n let current_byte =\n input.memory.peek(byte_index as u16 + source_memory_offset);\n let x_offset: u16 = (x_offset + byte_index as u16) * 64;\n for bit_index in 0..8 {\n input.video_memory.poke(\n x_offset + (y_offset + bit_index as u16),\n (current_byte \u0026 (0x80 \u003e\u003e bit_index)) != 0,\n );\n }\n }\n }\n\n let target = if input.video_memory.has_frame_changed {\n 1u8\n } else {\n 0u8\n };\n\n input.registers.poke(0xf, target);\n }\n Chip8CpuInstructions::SKP(x) =\u003e {\n // Ex9E - SKP Vx\n // Skip next instruction if key with the value of Vx is pressed.\n //\n // Checks the keyboard, and if the key corresponding to the value of Vx is currently in the down position, PC is increased by 2.\n let key_to_check = input.registers.peek(*x);\n let is_pressed = input.keypad.pressed(key_to_check);\n if is_pressed {\n input.registers.advance_pc();\n }\n }\n Chip8CpuInstructions::SKNP(x) =\u003e {\n // ExA1 - SKNP Vx\n // Skip next instruction if key with the value of Vx is not pressed.\n //\n\n // Checks the keyboard,\n // and if the key corresponding to the value of Vx is currently in the up position,\n // PC is increased by 2.\n let target_key = input.registers.peek(*x);\n let is_pressed = input.keypad.pressed(target_key);\n debug!(\"SnKpVx [{x:1x}]\");\n if !is_pressed {\n input.registers.advance_pc();\n }\n }\n Chip8CpuInstructions::LDRD(x) =\u003e {\n // Fx07 - LD Vx, DT\n // Set Vx = delay timer value.\n //\n // The value of DT is placed into Vx.\n let value_to_set = input.delay_timer.current();\n input.registers.poke(*x, value_to_set);\n }\n Chip8CpuInstructions::LDRK(_) =\u003e {\n // Fx0A - LD Vx, K\n // Wait for a key press, store the value of the key in Vx.\n //\n // All execution stops until a key is pressed, then the value of that key is stored in Vx.\n input.state = WaitingForKey;\n }\n Chip8CpuInstructions::LDD(source_register) =\u003e {\n // Fx15 - LD DT, Vx\n // Set delay timer = Vx.\n //\n // DT is set equal to the value of Vx.\n let new_time = input.registers.peek(*source_register);\n input.delay_timer.set_timer(new_time);\n }\n Chip8CpuInstructions::LDIS(new_time) =\u003e {\n let new_value = input.registers.peek(*new_time);\n input.sound_timer.set_timer(new_value as i32);\n }\n Chip8CpuInstructions::ADDI(x) =\u003e {\n // Fx1E - ADD I, Vx\n // Set I = I + Vx.\n //\n // The values of I and Vx are added, and the results are stored in I.\n let base = input.registers.peek_i();\n let x_value = input.registers.peek(*x);\n input.registers.poke_i(base + x_value as u16);\n }\n Chip8CpuInstructions::LDFX(x) =\u003e {\n // Fx29 - LD F, Vx\n // Set I = location of sprite for digit Vx.\n //\n // The value of I is set to the location for the hexadecimal sprite corresponding\n // to the value of Vx. See section 2.4, Display, for more information on\n // the Chip-8 hexadecimal font.\n\n let x_value: u8 = input.registers.peek(*x);\n\n let real_offset = x_value as u16 * 5;\n input.registers.poke_i(real_offset);\n }\n Chip8CpuInstructions::BCD(x) =\u003e {\n // Fx33 - LD B, Vx\n // Store BCD representation of Vx in memory locations I, I+1, and I+2.\n //\n // The interpreter takes the decimal value of Vx, and places the hundreds\n // digit in memory at location in I, the tens digit at location I+1,\n // and the ones digit at location I+2.\n\n let to_convert = input.registers.peek(*x);\n\n // how many hundreds\n let hundreds = to_convert / 100;\n // how many tens, minus the hundreds\n let tens = (to_convert / 10) - (hundreds * 10);\n // whats the leftover when dividing by 10\n let units = to_convert % 10;\n\n // write them to the memory pointed to by I, I+1, and I+2\n let target_start_offset = input.registers.peek_i();\n input.memory.poke(target_start_offset, hundreds);\n input.memory.poke(target_start_offset + 1, tens);\n input.memory.poke(target_start_offset + 2, units);\n }\n Chip8CpuInstructions::LDIX(x) =\u003e {\n // Store registers V0 through Vx in memory starting at location I.\n //\n // The interpreter copi=es the values of registers V0 through Vx into memory,\n // starting at the address in I.\n let offset = input.registers.peek_i();\n for i in 0..=*x {\n input\n .memory\n .poke(offset + i as u16, input.registers.peek(i));\n }\n input.registers.poke_i(offset + 1);\n }\n Chip8CpuInstructions::LDRI(x) =\u003e {\n // Read registers V0 through Vx from memory starting at location I.\n //\n // The interpreter reads values from memory starting at location I into registers V0 through Vx.\n let offset = input.registers.peek_i();\n debug!(\"STARTING TO READ AT {offset:03x}\");\n let num_loops = x + 1;\n debug!(\"WILL READ {num_loops:x} BYTES\");\n for index in 0..num_loops {\n let src_value = input.memory.peek(index as u16 + offset);\n input.registers.poke(index, src_value);\n debug!(\"POKING Register 0x{index:02x} with 0x{src_value:04x} using offset 0x{offset:04x}\");\n }\n input.registers.poke_i(offset + 1);\n }\n Chip8CpuInstructions::XXXXERRORINSTRUCTION =\u003e {}\n // SCHIP1.1\n Chip8CpuInstructions::SCD(x) =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"Attempt to execute SCD in Chip8 Mode\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n input.video_memory.scroll_down(*x as i32);\n }\n },\n // SCHIP 1.1\n Chip8CpuInstructions::SCR =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"Attempt to execute SCR in Chip8 Mode\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n input.video_memory.scroll_right();\n }\n },\n // SCHIP 1.1\n Chip8CpuInstructions::SCL =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"Attempt to execute SCL in Chip8 Mode\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n input.video_memory.scroll_left();\n }\n },\n // SCHIP 1.0\n Chip8CpuInstructions::LOW =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"ATTEMPT TO SET LOWRES IN CHIP8MODE\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n input.video_memory.set_lowres();\n }\n },\n // SCHIP 1.0\n Chip8CpuInstructions::HIGH =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"ATTEMPT TO SET HIGHRES IN CHIP8MODE\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n input.video_memory.set_highres();\n }\n },\n Chip8CpuInstructions::EXIT =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"ATTEMPT TO EXIT FROM CHIP8 INTERPRETER\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n println!(\"EXIT INTERPRETER\");\n }\n },\n Chip8CpuInstructions::LDF2(x) =\u003e {\n match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"ATTEMPT TO LDF2 IN CHIP8MODE\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n println!(\"POINTING TO FONT AT {x:02x}\");\n // base = 0x100 + 0x0A*X\n input.registers.poke_i(0x100 + (0xA * x) as u16);\n }\n }\n }\n Chip8CpuInstructions::STR(x) =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"ATTEMPT TO STORE RPL IN CHIP8MODE\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n println!(\"STORING FROM RPL FOR {x}\");\n }\n },\n Chip8CpuInstructions::LIDR(x) =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"ATTEMPT TO LOAD RPL IN CHIP8MODE\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n println!(\"LOADING FROM RPL FOR {x}\");\n }\n },\n SCU(x) =\u003e {\n println!(\"SCROLL SCREEN UP {x} ROWS\");\n match input.quirk_mode {\n QuirkMode::Chip8 | QuirkMode::SChipModern =\u003e {\n debug!(\"Attempt to run SCU outside XO mode\");\n }\n QuirkMode::XOChip =\u003e {\n input.video_memory.scroll_up(x);\n }\n }\n }\n DW(_) =\u003e {\n println!(\"DATA WORD FOUND...\");\n }\n };\n // let cycle_time = Instant::now().duration_since(start_time).as_nanos();\n // println!(\"\\t\\tTook {cycle_time}ms\");\n input.to_owned()\n }\n\n pub fn from_string(input: \u0026str) -\u003e Chip8CpuInstructions {\n let parts = input.split(\" \");\n println!(\"Parts -\u003e {:?}\", parts);\n\n Chip8CpuInstructions::XXXXERRORINSTRUCTION\n }\n}\n","traces":[{"line":280,"address":[810288],"length":1,"stats":{"Line":1}},{"line":281,"address":[796373],"length":1,"stats":{"Line":1}},{"line":282,"address":[1529696],"length":1,"stats":{"Line":1}},{"line":283,"address":[1529150],"length":1,"stats":{"Line":1}},{"line":284,"address":[819152],"length":1,"stats":{"Line":1}},{"line":285,"address":[1719404],"length":1,"stats":{"Line":1}},{"line":286,"address":[4043038],"length":1,"stats":{"Line":1}},{"line":287,"address":[796508],"length":1,"stats":{"Line":1}},{"line":288,"address":[797002],"length":1,"stats":{"Line":1}},{"line":289,"address":[921966],"length":1,"stats":{"Line":1}},{"line":290,"address":[1338546],"length":1,"stats":{"Line":1}},{"line":291,"address":[921446],"length":1,"stats":{"Line":1}},{"line":292,"address":[921732],"length":1,"stats":{"Line":1}},{"line":293,"address":[4043740],"length":1,"stats":{"Line":1}},{"line":294,"address":[797210],"length":1,"stats":{"Line":1}},{"line":295,"address":[41434056],"length":1,"stats":{"Line":1}},{"line":296,"address":[1529436],"length":1,"stats":{"Line":1}},{"line":297,"address":[41433822],"length":1,"stats":{"Line":1}},{"line":298,"address":[797542],"length":1,"stats":{"Line":1}},{"line":299,"address":[1719846],"length":1,"stats":{"Line":1}},{"line":300,"address":[826644],"length":1,"stats":{"Line":1}},{"line":301,"address":[1529592],"length":1,"stats":{"Line":1}},{"line":302,"address":[1339352],"length":1,"stats":{"Line":1}},{"line":303,"address":[2495154],"length":1,"stats":{"Line":1}},{"line":304,"address":[1529176],"length":1,"stats":{"Line":1}},{"line":305,"address":[2494738],"length":1,"stats":{"Line":1}},{"line":306,"address":[810376],"length":1,"stats":{"Line":1}},{"line":307,"address":[1778944],"length":1,"stats":{"Line":1}},{"line":308,"address":[1529852],"length":1,"stats":{"Line":1}},{"line":309,"address":[797392],"length":1,"stats":{"Line":1}},{"line":310,"address":[508182],"length":1,"stats":{"Line":1}},{"line":311,"address":[810454],"length":1,"stats":{"Line":1}},{"line":312,"address":[41433146],"length":1,"stats":{"Line":1}},{"line":313,"address":[41433432],"length":1,"stats":{"Line":1}},{"line":314,"address":[1719508],"length":1,"stats":{"Line":1}},{"line":315,"address":[2495076],"length":1,"stats":{"Line":1}},{"line":316,"address":[2494608],"length":1,"stats":{"Line":1}},{"line":317,"address":[810818],"length":1,"stats":{"Line":1}},{"line":318,"address":[921550],"length":1,"stats":{"Line":1}},{"line":319,"address":[1779487],"length":1,"stats":{"Line":1}},{"line":320,"address":[4043402],"length":1,"stats":{"Line":1}},{"line":321,"address":[507662],"length":1,"stats":{"Line":1}},{"line":322,"address":[4043012],"length":1,"stats":{"Line":1}},{"line":323,"address":[1529930],"length":1,"stats":{"Line":1}},{"line":324,"address":[921940],"length":1,"stats":{"Line":1}},{"line":325,"address":[1719430],"length":1,"stats":{"Line":1}},{"line":326,"address":[508381],"length":1,"stats":{"Line":1}},{"line":327,"address":[41433874],"length":1,"stats":{"Line":0}},{"line":328,"address":[922084],"length":1,"stats":{"Line":1}},{"line":329,"address":[1339675],"length":1,"stats":{"Line":0}},{"line":333,"address":[820032],"length":1,"stats":{"Line":2}},{"line":334,"address":[799534,799380,798007,798081,799424,799556,799490,798303,797763,798362,799292,797678,797822,799446,798384,798192,799336,797933,798266,799248,799270,799314,797726,799402,799512,798229,797859,797785,798340,798421,799468,798044,798155,798118,800152,797970,799358,797896],"length":1,"stats":{"Line":44}},{"line":335,"address":[1781534],"length":1,"stats":{"Line":1}},{"line":336,"address":[1781561],"length":1,"stats":{"Line":1}},{"line":337,"address":[2497662,2498178],"length":1,"stats":{"Line":2}},{"line":339,"address":[797751],"length":1,"stats":{"Line":2}},{"line":345,"address":[830206,830533],"length":1,"stats":{"Line":4}},{"line":347,"address":[1780362],"length":1,"stats":{"Line":1}},{"line":352,"address":[1782474,1783014],"length":1,"stats":{"Line":2}},{"line":355,"address":[1720668],"length":1,"stats":{"Line":1}},{"line":366,"address":[1783019],"length":1,"stats":{"Line":1}},{"line":369,"address":[1780399],"length":1,"stats":{"Line":1}},{"line":370,"address":[923722,922969],"length":1,"stats":{"Line":2}},{"line":373,"address":[1532056],"length":1,"stats":{"Line":1}},{"line":389,"address":[815851,815524],"length":1,"stats":{"Line":2}},{"line":391,"address":[508552],"length":1,"stats":{"Line":1}},{"line":404,"address":[4048963,4048544],"length":1,"stats":{"Line":1}},{"line":405,"address":[2500023],"length":1,"stats":{"Line":1}},{"line":406,"address":[832031,832087],"length":1,"stats":{"Line":2}},{"line":407,"address":[1784076],"length":1,"stats":{"Line":1}},{"line":412,"address":[824768,829992],"length":1,"stats":{"Line":1}},{"line":413,"address":[802413],"length":1,"stats":{"Line":1}},{"line":415,"address":[1725155],"length":1,"stats":{"Line":1}},{"line":420,"address":[825005,825166,824925],"length":1,"stats":{"Line":3}},{"line":430,"address":[41439788,41439576],"length":1,"stats":{"Line":2}},{"line":440,"address":[927958,928170],"length":1,"stats":{"Line":2}},{"line":449,"address":[1785846],"length":1,"stats":{"Line":1}},{"line":453,"address":[2502159],"length":1,"stats":{"Line":1}},{"line":454,"address":[818140,818074],"length":1,"stats":{"Line":2}},{"line":455,"address":[818172,818211,818117],"length":1,"stats":{"Line":3}},{"line":456,"address":[4050987,4050948,4050876],"length":1,"stats":{"Line":3}},{"line":457,"address":[41440995,41440956,41440916],"length":1,"stats":{"Line":3}},{"line":458,"address":[1727100,1727189,1727228],"length":1,"stats":{"Line":3}},{"line":459,"address":[1727205,1727261,1727300],"length":1,"stats":{"Line":3}},{"line":460,"address":[929180,929141,929085],"length":1,"stats":{"Line":3}},{"line":461,"address":[41441221,41441261,41441300],"length":1,"stats":{"Line":3}},{"line":462,"address":[1786741,1786685,1786780],"length":1,"stats":{"Line":3}},{"line":463,"address":[41441349,41441444,41441405],"length":1,"stats":{"Line":3}},{"line":464,"address":[835004,834893,834965],"length":1,"stats":{"Line":3}},{"line":465,"address":[1537533,1537461,1537572],"length":1,"stats":{"Line":3}},{"line":466,"address":[2503179,2503085,2503140],"length":1,"stats":{"Line":3}},{"line":467,"address":[1787170,1787131,1787076],"length":1,"stats":{"Line":3}},{"line":468,"address":[1727946,1727867,1727907],"length":1,"stats":{"Line":3}},{"line":469,"address":[1787203,1787282,1787243],"length":1,"stats":{"Line":3}},{"line":470,"address":[929866,929827,929787],"length":1,"stats":{"Line":3}},{"line":471,"address":[4052034,4051955,4051995],"length":1,"stats":{"Line":3}},{"line":472,"address":[1728091,1728163,1728202],"length":1,"stats":{"Line":3}},{"line":473,"address":[1347627,1347555,1347666],"length":1,"stats":{"Line":3}},{"line":474,"address":[4052298,4052187,4052259],"length":1,"stats":{"Line":3}},{"line":475,"address":[41442299,41442338,41442227],"length":1,"stats":{"Line":3}},{"line":476,"address":[1538339,1538378,1538267],"length":1,"stats":{"Line":3}},{"line":477,"address":[1728531,1728642,1728603],"length":1,"stats":{"Line":3}},{"line":478,"address":[4052611,4052539,4052650],"length":1,"stats":{"Line":3}},{"line":479,"address":[516714,516786,516825],"length":1,"stats":{"Line":3}},{"line":480,"address":[4052787,4052826,4052715],"length":1,"stats":{"Line":3}},{"line":481,"address":[820187,820226,820115],"length":1,"stats":{"Line":3}},{"line":482,"address":[1348347,1348419,1348458],"length":1,"stats":{"Line":3}},{"line":483,"address":[806482,806371,806443],"length":1,"stats":{"Line":3}},{"line":484,"address":[1788522,1788427,1788483],"length":1,"stats":{"Line":3}},{"line":485,"address":[836563,836674,836635],"length":1,"stats":{"Line":3}},{"line":486,"address":[41443273,41443234,41443179],"length":1,"stats":{"Line":3}},{"line":487,"address":[4053298,4053353,4053392],"length":1,"stats":{"Line":3}},{"line":488,"address":[1729504,1729449,1729543],"length":1,"stats":{"Line":3}},{"line":489,"address":[2504880,2504935,2504974],"length":1,"stats":{"Line":3}},{"line":490,"address":[2505006,2505045,2504951],"length":1,"stats":{"Line":3}},{"line":491,"address":[931525,931564,931470],"length":1,"stats":{"Line":3}},{"line":492,"address":[1729733,1729788,1729827],"length":1,"stats":{"Line":3}},{"line":493,"address":[2505219,2505258,2505164],"length":1,"stats":{"Line":3}},{"line":494,"address":[807281,807242,807187],"length":1,"stats":{"Line":3}},{"line":495,"address":[41443873,41443818,41443912],"length":1,"stats":{"Line":3}},{"line":496,"address":[829713,829807,829768],"length":1,"stats":{"Line":3}},{"line":497,"address":[41443960,41444015,41444054],"length":1,"stats":{"Line":3}},{"line":498,"address":[837503,837571,837587],"length":1,"stats":{"Line":2}},{"line":499,"address":[1349609],"length":1,"stats":{"Line":1}},{"line":503,"address":[837664],"length":1,"stats":{"Line":1}},{"line":504,"address":[932144],"length":1,"stats":{"Line":1}},{"line":505,"address":[4054291],"length":1,"stats":{"Line":1}},{"line":506,"address":[1349783],"length":1,"stats":{"Line":1}},{"line":507,"address":[807731],"length":1,"stats":{"Line":1}},{"line":508,"address":[821667],"length":1,"stats":{"Line":1}},{"line":509,"address":[2505839],"length":1,"stats":{"Line":1}},{"line":510,"address":[518539],"length":1,"stats":{"Line":1}},{"line":511,"address":[2505907],"length":1,"stats":{"Line":1}},{"line":513,"address":[1349956],"length":1,"stats":{"Line":1}},{"line":514,"address":[4054524],"length":1,"stats":{"Line":1}},{"line":516,"address":[932445],"length":1,"stats":{"Line":1}},{"line":517,"address":[4054581],"length":1,"stats":{"Line":1}},{"line":519,"address":[1350074],"length":1,"stats":{"Line":1}},{"line":520,"address":[821954],"length":1,"stats":{"Line":1}},{"line":522,"address":[932563],"length":1,"stats":{"Line":1}},{"line":523,"address":[2506142],"length":1,"stats":{"Line":1}},{"line":525,"address":[932623],"length":1,"stats":{"Line":1}},{"line":526,"address":[822077],"length":1,"stats":{"Line":1}},{"line":528,"address":[1730882],"length":1,"stats":{"Line":1}},{"line":529,"address":[808224],"length":1,"stats":{"Line":1}},{"line":531,"address":[4054869],"length":1,"stats":{"Line":1}},{"line":532,"address":[41444851],"length":1,"stats":{"Line":1}},{"line":534,"address":[932824],"length":1,"stats":{"Line":1}},{"line":535,"address":[1540870],"length":1,"stats":{"Line":1}},{"line":537,"address":[519099],"length":1,"stats":{"Line":1}},{"line":538,"address":[838457],"length":1,"stats":{"Line":1}},{"line":540,"address":[822382],"length":1,"stats":{"Line":1}},{"line":541,"address":[830876],"length":1,"stats":{"Line":1}},{"line":543,"address":[838561],"length":1,"stats":{"Line":1}},{"line":544,"address":[1731247],"length":1,"stats":{"Line":1}},{"line":546,"address":[838628],"length":1,"stats":{"Line":1}},{"line":547,"address":[41445186],"length":1,"stats":{"Line":1}},{"line":549,"address":[933159],"length":1,"stats":{"Line":1}},{"line":550,"address":[4055301],"length":1,"stats":{"Line":1}},{"line":552,"address":[1731418],"length":1,"stats":{"Line":1}},{"line":553,"address":[1731448],"length":1,"stats":{"Line":1}},{"line":555,"address":[4055405],"length":1,"stats":{"Line":1}},{"line":556,"address":[519544],"length":1,"stats":{"Line":1}},{"line":557,"address":[1351779],"length":1,"stats":{"Line":1}},{"line":558,"address":[822803],"length":1,"stats":{"Line":1}},{"line":559,"address":[831297],"length":1,"stats":{"Line":1}},{"line":561,"address":[1351010],"length":1,"stats":{"Line":1}},{"line":562,"address":[1790986,1790967,1790979],"length":1,"stats":{"Line":3}},{"line":563,"address":[2507039],"length":1,"stats":{"Line":1}},{"line":564,"address":[1731691],"length":1,"stats":{"Line":1}},{"line":565,"address":[1790982],"length":1,"stats":{"Line":1}},{"line":567,"address":[519739],"length":1,"stats":{"Line":1}},{"line":568,"address":[809076],"length":1,"stats":{"Line":1}},{"line":569,"address":[823037],"length":1,"stats":{"Line":1}},{"line":570,"address":[519862],"length":1,"stats":{"Line":1}},{"line":571,"address":[41445759],"length":1,"stats":{"Line":1}},{"line":572,"address":[2507288],"length":1,"stats":{"Line":1}},{"line":573,"address":[1731969],"length":1,"stats":{"Line":1}},{"line":574,"address":[520026],"length":1,"stats":{"Line":1}},{"line":575,"address":[1732051],"length":1,"stats":{"Line":1}},{"line":576,"address":[41445964],"length":1,"stats":{"Line":1}},{"line":577,"address":[831829],"length":1,"stats":{"Line":1}},{"line":578,"address":[1542010],"length":1,"stats":{"Line":1}},{"line":579,"address":[4056139],"length":1,"stats":{"Line":1}},{"line":580,"address":[520247],"length":1,"stats":{"Line":1}},{"line":581,"address":[1791523],"length":1,"stats":{"Line":1}},{"line":582,"address":[823487],"length":1,"stats":{"Line":1}},{"line":583,"address":[41446139],"length":1,"stats":{"Line":1}},{"line":584,"address":[831979],"length":1,"stats":{"Line":1}},{"line":585,"address":[41446196],"length":1,"stats":{"Line":1}},{"line":586,"address":[1542189],"length":1,"stats":{"Line":1}},{"line":587,"address":[934268],"length":1,"stats":{"Line":1}},{"line":588,"address":[41446366],"length":1,"stats":{"Line":0}},{"line":589,"address":[1351546],"length":1,"stats":{"Line":0}},{"line":592,"address":[1351920],"length":1,"stats":{"Line":6}},{"line":593,"address":[41446443],"length":1,"stats":{"Line":6}},{"line":594,"address":[823827],"length":1,"stats":{"Line":6}},{"line":595,"address":[934427],"length":1,"stats":{"Line":6}},{"line":596,"address":[1791926],"length":1,"stats":{"Line":6}},{"line":597,"address":[832366],"length":1,"stats":{"Line":6}},{"line":598,"address":[1542518],"length":1,"stats":{"Line":6}},{"line":599,"address":[934524],"length":1,"stats":{"Line":6}},{"line":600,"address":[1732731],"length":1,"stats":{"Line":6}},{"line":602,"address":[832456],"length":1,"stats":{"Line":6}},{"line":603,"address":[810058,810120],"length":1,"stats":{"Line":12}},{"line":604,"address":[41448498],"length":1,"stats":{"Line":1}},{"line":605,"address":[1544464],"length":1,"stats":{"Line":1}},{"line":606,"address":[1793938],"length":1,"stats":{"Line":1}},{"line":608,"address":[520891],"length":1,"stats":{"Line":3}},{"line":609,"address":[520898],"length":1,"stats":{"Line":3}},{"line":611,"address":[1792180],"length":1,"stats":{"Line":1}},{"line":614,"address":[520999],"length":1,"stats":{"Line":1}},{"line":616,"address":[810308],"length":1,"stats":{"Line":1}},{"line":617,"address":[832699],"length":1,"stats":{"Line":1}},{"line":620,"address":[810242],"length":1,"stats":{"Line":1}},{"line":621,"address":[41446809],"length":1,"stats":{"Line":1}},{"line":622,"address":[520959],"length":1,"stats":{"Line":1}},{"line":623,"address":[2508370],"length":1,"stats":{"Line":1}},{"line":624,"address":[810329],"length":1,"stats":{"Line":1}},{"line":626,"address":[840310],"length":1,"stats":{"Line":1}},{"line":627,"address":[840368],"length":1,"stats":{"Line":1}},{"line":628,"address":[832727],"length":1,"stats":{"Line":1}},{"line":630,"address":[1542689,1542883],"length":1,"stats":{"Line":8}},{"line":631,"address":[522611],"length":1,"stats":{"Line":2}},{"line":632,"address":[2509976],"length":1,"stats":{"Line":1}},{"line":634,"address":[1352419,1352486],"length":1,"stats":{"Line":8}},{"line":635,"address":[1792437,1792374],"length":1,"stats":{"Line":12}},{"line":636,"address":[1733141,1733204],"length":1,"stats":{"Line":12}},{"line":637,"address":[1352564,1352633],"length":1,"stats":{"Line":12}},{"line":638,"address":[935049,935118,936386],"length":1,"stats":{"Line":14}},{"line":639,"address":[1352670,1352737],"length":1,"stats":{"Line":12}},{"line":640,"address":[1792625,1792694],"length":1,"stats":{"Line":6}},{"line":641,"address":[2508827,2508758],"length":1,"stats":{"Line":6}},{"line":642,"address":[1544168],"length":1,"stats":{"Line":2}},{"line":643,"address":[1353746],"length":1,"stats":{"Line":2}},{"line":644,"address":[1544220],"length":1,"stats":{"Line":3}},{"line":645,"address":[2509782],"length":1,"stats":{"Line":3}},{"line":646,"address":[811760],"length":1,"stats":{"Line":3}},{"line":647,"address":[834170],"length":1,"stats":{"Line":3}},{"line":648,"address":[841844],"length":1,"stats":{"Line":2}},{"line":649,"address":[1793806],"length":1,"stats":{"Line":2}},{"line":650,"address":[811864],"length":1,"stats":{"Line":2}},{"line":651,"address":[825558],"length":1,"stats":{"Line":0}},{"line":653,"address":[1734308,1733451,1733533],"length":1,"stats":{"Line":9}},{"line":654,"address":[833280,833213],"length":1,"stats":{"Line":12}},{"line":655,"address":[4057488,4057551],"length":1,"stats":{"Line":12}},{"line":656,"address":[521688,521625],"length":1,"stats":{"Line":12}},{"line":657,"address":[935470,935539],"length":1,"stats":{"Line":12}},{"line":658,"address":[41447664,41447587],"length":1,"stats":{"Line":8}},{"line":659,"address":[936076],"length":1,"stats":{"Line":1}},{"line":660,"address":[1793566],"length":1,"stats":{"Line":1}},{"line":661,"address":[2509614],"length":1,"stats":{"Line":0}},{"line":663,"address":[825008,825086],"length":1,"stats":{"Line":8}},{"line":664,"address":[522052],"length":1,"stats":{"Line":1}},{"line":665,"address":[2509420],"length":1,"stats":{"Line":2}},{"line":666,"address":[1793358],"length":1,"stats":{"Line":1}},{"line":667,"address":[935904],"length":1,"stats":{"Line":1}},{"line":668,"address":[1353490],"length":1,"stats":{"Line":2}},{"line":669,"address":[1793412],"length":1,"stats":{"Line":1}},{"line":670,"address":[1543974],"length":1,"stats":{"Line":1}},{"line":671,"address":[1734168],"length":1,"stats":{"Line":2}},{"line":672,"address":[825418],"length":1,"stats":{"Line":3}},{"line":673,"address":[522214],"length":1,"stats":{"Line":2}},{"line":674,"address":[522232],"length":1,"stats":{"Line":1}},{"line":675,"address":[841584],"length":1,"stats":{"Line":1}},{"line":676,"address":[833724],"length":1,"stats":{"Line":1}},{"line":678,"address":[1733834],"length":1,"stats":{"Line":0}},{"line":681,"address":[944586,936480],"length":1,"stats":{"Line":3}},{"line":684,"address":[1734753],"length":1,"stats":{"Line":3}},{"line":685,"address":[522794,522892],"length":1,"stats":{"Line":8}},{"line":686,"address":[826078],"length":1,"stats":{"Line":5}},{"line":688,"address":[812227],"length":1,"stats":{"Line":1}},{"line":689,"address":[936738],"length":1,"stats":{"Line":1}},{"line":693,"address":[4058877],"length":1,"stats":{"Line":3}},{"line":697,"address":[4058900],"length":1,"stats":{"Line":3}},{"line":700,"address":[1354419],"length":1,"stats":{"Line":3}},{"line":701,"address":[41448930],"length":1,"stats":{"Line":3}},{"line":704,"address":[826325],"length":1,"stats":{"Line":2}},{"line":705,"address":[812417],"length":1,"stats":{"Line":2}},{"line":706,"address":[826374],"length":1,"stats":{"Line":2}},{"line":707,"address":[523168],"length":1,"stats":{"Line":2}},{"line":710,"address":[826435],"length":1,"stats":{"Line":2}},{"line":711,"address":[41449105],"length":1,"stats":{"Line":2}},{"line":712,"address":[839551],"length":1,"stats":{"Line":2}},{"line":716,"address":[842629],"length":1,"stats":{"Line":2}},{"line":722,"address":[523317],"length":1,"stats":{"Line":2}},{"line":723,"address":[941686],"length":1,"stats":{"Line":2}},{"line":727,"address":[1545191],"length":1,"stats":{"Line":2}},{"line":728,"address":[4059317],"length":1,"stats":{"Line":2}},{"line":729,"address":[831133],"length":1,"stats":{"Line":1}},{"line":733,"address":[835184],"length":1,"stats":{"Line":4}},{"line":734,"address":[812830],"length":1,"stats":{"Line":4}},{"line":737,"address":[1545383],"length":1,"stats":{"Line":2}},{"line":738,"address":[2515299,2510949],"length":1,"stats":{"Line":4}},{"line":739,"address":[1354980],"length":1,"stats":{"Line":2}},{"line":740,"address":[527879,523612],"length":1,"stats":{"Line":2}},{"line":744,"address":[843030],"length":1,"stats":{"Line":2}},{"line":745,"address":[843060],"length":1,"stats":{"Line":2}},{"line":748,"address":[843147],"length":1,"stats":{"Line":2}},{"line":750,"address":[523825],"length":1,"stats":{"Line":2}},{"line":751,"address":[1735878],"length":1,"stats":{"Line":2}},{"line":753,"address":[4059848],"length":1,"stats":{"Line":2}},{"line":755,"address":[937747],"length":1,"stats":{"Line":2}},{"line":757,"address":[937781],"length":1,"stats":{"Line":2}},{"line":758,"address":[2515360,2511350],"length":1,"stats":{"Line":5}},{"line":761,"address":[835748],"length":1,"stats":{"Line":3}},{"line":762,"address":[1795362],"length":1,"stats":{"Line":3}},{"line":763,"address":[813445],"length":1,"stats":{"Line":3}},{"line":765,"address":[41450046],"length":1,"stats":{"Line":3}},{"line":766,"address":[1736223],"length":1,"stats":{"Line":3}},{"line":769,"address":[843610],"length":1,"stats":{"Line":3}},{"line":770,"address":[1546120],"length":1,"stats":{"Line":3}},{"line":771,"address":[1546171],"length":1,"stats":{"Line":3}},{"line":773,"address":[4060308],"length":1,"stats":{"Line":3}},{"line":774,"address":[938245],"length":1,"stats":{"Line":3}},{"line":777,"address":[836176],"length":1,"stats":{"Line":3}},{"line":778,"address":[1546342],"length":1,"stats":{"Line":3}},{"line":779,"address":[1736569],"length":1,"stats":{"Line":3}},{"line":780,"address":[827836,831880,831922],"length":1,"stats":{"Line":6}},{"line":781,"address":[528576],"length":1,"stats":{"Line":3}},{"line":783,"address":[41454554],"length":1,"stats":{"Line":3}},{"line":784,"address":[818056],"length":1,"stats":{"Line":2}},{"line":786,"address":[1360090],"length":1,"stats":{"Line":2}},{"line":789,"address":[827879],"length":1,"stats":{"Line":3}},{"line":795,"address":[4060605],"length":1,"stats":{"Line":3}},{"line":796,"address":[524711],"length":1,"stats":{"Line":3}},{"line":800,"address":[2516229,2512126,2516404],"length":1,"stats":{"Line":6}},{"line":801,"address":[840676,840745,840513],"length":1,"stats":{"Line":2}},{"line":802,"address":[1741036],"length":1,"stats":{"Line":1}},{"line":804,"address":[1740871,1740853,1740783],"length":1,"stats":{"Line":4}},{"line":805,"address":[1800141],"length":1,"stats":{"Line":2}},{"line":808,"address":[2516263],"length":1,"stats":{"Line":3}},{"line":809,"address":[840633],"length":1,"stats":{"Line":3}},{"line":811,"address":[814107],"length":1,"stats":{"Line":2}},{"line":816,"address":[1546634],"length":1,"stats":{"Line":2}},{"line":819,"address":[1796134],"length":1,"stats":{"Line":3}},{"line":820,"address":[4060783],"length":1,"stats":{"Line":3}},{"line":821,"address":[828119],"length":1,"stats":{"Line":3}},{"line":822,"address":[528988],"length":1,"stats":{"Line":2}},{"line":824,"address":[840802],"length":1,"stats":{"Line":2}},{"line":827,"address":[828161],"length":1,"stats":{"Line":2}},{"line":832,"address":[938775],"length":1,"stats":{"Line":2}},{"line":833,"address":[844357],"length":1,"stats":{"Line":2}},{"line":835,"address":[1796330,1800537,1800709,1800503],"length":1,"stats":{"Line":8}},{"line":836,"address":[1551189,1551258,1550990],"length":1,"stats":{"Line":4}},{"line":838,"address":[848548,848569,848486],"length":1,"stats":{"Line":4}},{"line":840,"address":[848638],"length":1,"stats":{"Line":2}},{"line":841,"address":[2516687],"length":1,"stats":{"Line":2}},{"line":844,"address":[2512439],"length":1,"stats":{"Line":2}},{"line":849,"address":[814406],"length":1,"stats":{"Line":2}},{"line":852,"address":[2512498],"length":1,"stats":{"Line":2}},{"line":853,"address":[1737147],"length":1,"stats":{"Line":2}},{"line":854,"address":[2512531],"length":1,"stats":{"Line":2}},{"line":855,"address":[529369],"length":1,"stats":{"Line":3}},{"line":857,"address":[841187],"length":1,"stats":{"Line":2}},{"line":860,"address":[814525],"length":1,"stats":{"Line":2}},{"line":866,"address":[525216],"length":1,"stats":{"Line":2}},{"line":867,"address":[41451161],"length":1,"stats":{"Line":2}},{"line":868,"address":[939127],"length":1,"stats":{"Line":2}},{"line":869,"address":[848865],"length":1,"stats":{"Line":1}},{"line":872,"address":[1737348],"length":1,"stats":{"Line":5}},{"line":878,"address":[844707],"length":1,"stats":{"Line":5}},{"line":881,"address":[837094],"length":1,"stats":{"Line":1}},{"line":886,"address":[1737479,1737418],"length":1,"stats":{"Line":2}},{"line":888,"address":[1796713],"length":1,"stats":{"Line":1}},{"line":891,"address":[816787],"length":1,"stats":{"Line":1}},{"line":892,"address":[1358889],"length":1,"stats":{"Line":1}},{"line":893,"address":[2514906],"length":1,"stats":{"Line":1}},{"line":894,"address":[1798846],"length":1,"stats":{"Line":1}},{"line":895,"address":[827817,816906,828780],"length":1,"stats":{"Line":2}},{"line":896,"address":[850209],"length":1,"stats":{"Line":1}},{"line":897,"address":[2526805],"length":1,"stats":{"Line":1}},{"line":899,"address":[41451373],"length":1,"stats":{"Line":3}},{"line":906,"address":[814855],"length":1,"stats":{"Line":2}},{"line":907,"address":[1547384,1551419],"length":1,"stats":{"Line":6}},{"line":908,"address":[832834],"length":1,"stats":{"Line":3}},{"line":909,"address":[2516971],"length":1,"stats":{"Line":2}},{"line":910,"address":[849143,848978],"length":1,"stats":{"Line":6}},{"line":911,"address":[849060],"length":1,"stats":{"Line":3}},{"line":913,"address":[525569],"length":1,"stats":{"Line":5}},{"line":925,"address":[41451527],"length":1,"stats":{"Line":5}},{"line":926,"address":[41451572],"length":1,"stats":{"Line":5}},{"line":927,"address":[4061671],"length":1,"stats":{"Line":5}},{"line":928,"address":[939600],"length":1,"stats":{"Line":5}},{"line":930,"address":[944651,945252],"length":1,"stats":{"Line":0}},{"line":931,"address":[945279],"length":1,"stats":{"Line":0}},{"line":932,"address":[2519073,2519026,2518939],"length":1,"stats":{"Line":0}},{"line":934,"address":[851132],"length":1,"stats":{"Line":0}},{"line":936,"address":[851036,851086,851186],"length":1,"stats":{"Line":0}},{"line":937,"address":[2519223,2519182,2519302],"length":1,"stats":{"Line":0}},{"line":938,"address":[821225,821270],"length":1,"stats":{"Line":0}},{"line":939,"address":[1744037,1744163],"length":1,"stats":{"Line":0}},{"line":940,"address":[1553870,1553939],"length":1,"stats":{"Line":0}},{"line":941,"address":[945916,946035,945952],"length":1,"stats":{"Line":0}},{"line":943,"address":[41458073,41458256],"length":1,"stats":{"Line":0}},{"line":944,"address":[532078,532121,532204],"length":1,"stats":{"Line":0}},{"line":945,"address":[851751,851673,851709],"length":1,"stats":{"Line":0}},{"line":950,"address":[944616,944665,944744],"length":1,"stats":{"Line":15}},{"line":951,"address":[41456929,41456829],"length":1,"stats":{"Line":5}},{"line":953,"address":[4067086,4066954,4067001],"length":1,"stats":{"Line":10}},{"line":954,"address":[834369,834414],"length":1,"stats":{"Line":10}},{"line":955,"address":[1802541,1802685],"length":1,"stats":{"Line":11}},{"line":956,"address":[4067193,4067271],"length":1,"stats":{"Line":5}},{"line":957,"address":[945194,945152,945236],"length":1,"stats":{"Line":12}},{"line":963,"address":[4067527,4066833],"length":1,"stats":{"Line":6}},{"line":964,"address":[1802889],"length":1,"stats":{"Line":6}},{"line":966,"address":[531476],"length":1,"stats":{"Line":0}},{"line":969,"address":[1802905],"length":1,"stats":{"Line":6}},{"line":971,"address":[1737831],"length":1,"stats":{"Line":1}},{"line":976,"address":[815158],"length":1,"stats":{"Line":1}},{"line":977,"address":[1737875],"length":1,"stats":{"Line":1}},{"line":978,"address":[1357276],"length":1,"stats":{"Line":1}},{"line":979,"address":[1803711],"length":1,"stats":{"Line":1}},{"line":982,"address":[939737],"length":1,"stats":{"Line":1}},{"line":990,"address":[829173],"length":1,"stats":{"Line":1}},{"line":991,"address":[845321],"length":1,"stats":{"Line":1}},{"line":992,"address":[41458366,41451878],"length":1,"stats":{"Line":2}},{"line":993,"address":[1744453],"length":1,"stats":{"Line":1}},{"line":994,"address":[836145],"length":1,"stats":{"Line":1}},{"line":997,"address":[4061988],"length":1,"stats":{"Line":1}},{"line":1002,"address":[837776],"length":1,"stats":{"Line":1}},{"line":1003,"address":[939921],"length":1,"stats":{"Line":1}},{"line":1005,"address":[939969],"length":1,"stats":{"Line":2}},{"line":1010,"address":[2513503],"length":1,"stats":{"Line":2}},{"line":1012,"address":[1357558],"length":1,"stats":{"Line":1}},{"line":1017,"address":[1797477],"length":1,"stats":{"Line":2}},{"line":1018,"address":[526179],"length":1,"stats":{"Line":2}},{"line":1020,"address":[845604],"length":1,"stats":{"Line":1}},{"line":1021,"address":[815587],"length":1,"stats":{"Line":1}},{"line":1022,"address":[940112],"length":1,"stats":{"Line":1}},{"line":1024,"address":[41452210],"length":1,"stats":{"Line":2}},{"line":1029,"address":[41452222],"length":1,"stats":{"Line":2}},{"line":1030,"address":[41452267],"length":1,"stats":{"Line":2}},{"line":1031,"address":[940242,946752],"length":1,"stats":{"Line":4}},{"line":1033,"address":[845836],"length":1,"stats":{"Line":1}},{"line":1041,"address":[4062427],"length":1,"stats":{"Line":1}},{"line":1043,"address":[41452400,41458862,41458888],"length":1,"stats":{"Line":2}},{"line":1044,"address":[532857],"length":1,"stats":{"Line":1}},{"line":1046,"address":[940386],"length":1,"stats":{"Line":2}},{"line":1054,"address":[845937],"length":1,"stats":{"Line":2}},{"line":1057,"address":[2513981],"length":1,"stats":{"Line":2}},{"line":1059,"address":[852549,845996,852390],"length":1,"stats":{"Line":4}},{"line":1061,"address":[852450],"length":1,"stats":{"Line":2}},{"line":1064,"address":[822441],"length":1,"stats":{"Line":2}},{"line":1065,"address":[2520530],"length":1,"stats":{"Line":2}},{"line":1066,"address":[1804518,1804467,1804553],"length":1,"stats":{"Line":4}},{"line":1067,"address":[1804535,1804586],"length":1,"stats":{"Line":4}},{"line":1069,"address":[41452591],"length":1,"stats":{"Line":3}},{"line":1074,"address":[1738730],"length":1,"stats":{"Line":3}},{"line":1075,"address":[1745438,1738773,1745339],"length":1,"stats":{"Line":9}},{"line":1076,"address":[1804834],"length":1,"stats":{"Line":3}},{"line":1078,"address":[533421,533309,533380],"length":1,"stats":{"Line":6}},{"line":1080,"address":[4069406,4069323],"length":1,"stats":{"Line":6}},{"line":1082,"address":[1739001],"length":1,"stats":{"Line":2}},{"line":1086,"address":[4062941],"length":1,"stats":{"Line":2}},{"line":1087,"address":[1739039,1745693],"length":1,"stats":{"Line":4}},{"line":1088,"address":[852939],"length":1,"stats":{"Line":2}},{"line":1089,"address":[1364995,1365547],"length":1,"stats":{"Line":4}},{"line":1090,"address":[853835,853452,853744],"length":1,"stats":{"Line":6}},{"line":1091,"address":[534435,534527,534349],"length":1,"stats":{"Line":4}},{"line":1092,"address":[4070536],"length":1,"stats":{"Line":2}},{"line":1093,"address":[41460602,41460516],"length":1,"stats":{"Line":4}},{"line":1095,"address":[2521909,2521816],"length":1,"stats":{"Line":4}},{"line":1099,"address":[830371],"length":1,"stats":{"Line":1}},{"line":1101,"address":[1367051,1366947],"length":1,"stats":{"Line":0}},{"line":1104,"address":[824941],"length":1,"stats":{"Line":1}},{"line":1108,"address":[830418],"length":1,"stats":{"Line":1}},{"line":1110,"address":[825250,825157],"length":1,"stats":{"Line":0}},{"line":1113,"address":[1557719],"length":1,"stats":{"Line":1}},{"line":1117,"address":[830446],"length":1,"stats":{"Line":1}},{"line":1119,"address":[1748201,1748108],"length":1,"stats":{"Line":0}},{"line":1122,"address":[1807438],"length":1,"stats":{"Line":1}},{"line":1126,"address":[838938],"length":1,"stats":{"Line":1}},{"line":1128,"address":[1367840,1367747],"length":1,"stats":{"Line":0}},{"line":1131,"address":[1558245],"length":1,"stats":{"Line":1}},{"line":1135,"address":[4063190],"length":1,"stats":{"Line":1}},{"line":1137,"address":[1558458,1558551],"length":1,"stats":{"Line":0}},{"line":1140,"address":[1558508],"length":1,"stats":{"Line":1}},{"line":1143,"address":[941106],"length":1,"stats":{"Line":0}},{"line":1145,"address":[1558831,1558721],"length":1,"stats":{"Line":0}},{"line":1148,"address":[1558763],"length":1,"stats":{"Line":0}},{"line":1151,"address":[839030],"length":1,"stats":{"Line":0}},{"line":1152,"address":[846689],"length":1,"stats":{"Line":0}},{"line":1154,"address":[1368979,1368553],"length":1,"stats":{"Line":0}},{"line":1157,"address":[1559047],"length":1,"stats":{"Line":0}},{"line":1159,"address":[537607,537338],"length":1,"stats":{"Line":0}},{"line":1163,"address":[1358757],"length":1,"stats":{"Line":0}},{"line":1165,"address":[857157,857329],"length":1,"stats":{"Line":0}},{"line":1168,"address":[2525215],"length":1,"stats":{"Line":0}},{"line":1171,"address":[830660],"length":1,"stats":{"Line":0}},{"line":1173,"address":[841559,841387],"length":1,"stats":{"Line":0}},{"line":1176,"address":[857541],"length":1,"stats":{"Line":0}},{"line":1179,"address":[41453514],"length":1,"stats":{"Line":0}},{"line":1180,"address":[527600],"length":1,"stats":{"Line":0}},{"line":1181,"address":[839455],"length":1,"stats":{"Line":0}},{"line":1183,"address":[4075505,4075404],"length":1,"stats":{"Line":0}},{"line":1186,"address":[1810814],"length":1,"stats":{"Line":0}},{"line":1191,"address":[2515167],"length":1,"stats":{"Line":0}},{"line":1196,"address":[838797],"length":1,"stats":{"Line":3}},{"line":1199,"address":[851456],"length":1,"stats":{"Line":0}},{"line":1200,"address":[843021],"length":1,"stats":{"Line":0}},{"line":1201,"address":[829125],"length":1,"stats":{"Line":0}},{"line":1203,"address":[953715],"length":1,"stats":{"Line":0}}],"covered":453,"coverable":503},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","keypad.rs"],"content":"use serde::{Deserialize, Serialize};\n\nuse crate::constants::CHIP8_KEYBOARD;\n\n#[derive(Clone, Copy, Default, Serialize, Deserialize, Debug)]\npub struct Keypad {\n keys: [bool; 0x10],\n}\n\nimpl Keypad {\n pub fn format_as_string(\u0026self) -\u003e String {\n let mut return_value = String::new();\n // draw a 4x4 grid showing the keys with * filling the cells that are depressed\n for row in CHIP8_KEYBOARD.iter() {\n for (index, key) in row.iter().enumerate() {\n let is_lit = if self.keys[*key as usize] {\n \"*\".to_string()\n } else {\n char::from_digit(*key as u32, 16).unwrap_or(' ').to_string()\n };\n\n if index == 3 {\n return_value += format!(\"|{}|\\n\", is_lit).as_str();\n } else {\n return_value += format!(\"|{}\", is_lit).as_str();\n }\n }\n }\n\n return_value\n }\n}\n\nimpl Keypad {\n pub fn push_key(\u0026mut self, key_index: u8) {\n self.keys[key_index as usize] = true;\n }\n\n pub fn release_key(\u0026mut self, key_index: u8) {\n self.keys[key_index as usize] = false;\n }\n\n pub fn key_state(\u0026self, key_index: u8) -\u003e bool {\n self.keys[key_index as usize]\n }\n\n pub fn new() -\u003e Keypad {\n Keypad::default()\n }\n\n pub fn pressed(\u0026self, key: u8) -\u003e bool {\n self.key_state(key)\n }\n pub fn released(\u0026self, key: u8) -\u003e bool {\n !self.key_state(key)\n }\n}\n","traces":[{"line":11,"address":[489181,487824,488935],"length":1,"stats":{"Line":1}},{"line":12,"address":[41477919],"length":1,"stats":{"Line":1}},{"line":14,"address":[839351,839252,839523],"length":1,"stats":{"Line":3}},{"line":15,"address":[1383747],"length":1,"stats":{"Line":1}},{"line":16,"address":[41478471],"length":1,"stats":{"Line":1}},{"line":17,"address":[839996,839888],"length":1,"stats":{"Line":2}},{"line":19,"address":[1764723,1764669],"length":1,"stats":{"Line":2}},{"line":22,"address":[4088740],"length":1,"stats":{"Line":1}},{"line":23,"address":[839998,840099,840228],"length":1,"stats":{"Line":3}},{"line":25,"address":[4089116,4088777,4089245],"length":1,"stats":{"Line":3}},{"line":30,"address":[1383688],"length":1,"stats":{"Line":1}},{"line":35,"address":[856624],"length":1,"stats":{"Line":2}},{"line":36,"address":[840645,840683],"length":1,"stats":{"Line":2}},{"line":39,"address":[2540864],"length":1,"stats":{"Line":1}},{"line":40,"address":[41479397,41479435],"length":1,"stats":{"Line":1}},{"line":43,"address":[2540944],"length":1,"stats":{"Line":1}},{"line":44,"address":[872949,872988],"length":1,"stats":{"Line":1}},{"line":47,"address":[865344],"length":1,"stats":{"Line":1}},{"line":48,"address":[865352],"length":1,"stats":{"Line":1}},{"line":51,"address":[1571680],"length":1,"stats":{"Line":1}},{"line":52,"address":[856928],"length":1,"stats":{"Line":1}},{"line":54,"address":[41479616],"length":1,"stats":{"Line":1}},{"line":55,"address":[856960],"length":1,"stats":{"Line":1}}],"covered":23,"coverable":23},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","quirk_modes.rs"],"content":"use serde::{Deserialize, Serialize};\nuse std::fmt::Display;\nuse crate::constants::{LABEL_QUIRK_CHIP8, LABEL_QUIRK_SCHIP, LABEL_QUIRK_XOCHIP};\n\n#[derive(Default, Clone, Serialize, Deserialize, Copy, Debug, PartialEq)]\npub enum QuirkMode {\n #[default]\n Chip8,\n XOChip,\n SChipModern,\n}\n\nimpl Display for QuirkMode {\n fn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n let variant = match \u0026self {\n QuirkMode::Chip8 =\u003e LABEL_QUIRK_CHIP8.to_string(),\n QuirkMode::XOChip =\u003e LABEL_QUIRK_XOCHIP.to_string(),\n QuirkMode::SChipModern =\u003e LABEL_QUIRK_SCHIP.to_string(),\n };\n write!(f, \"{}\", variant)\n }\n}\n","traces":[{"line":14,"address":[2552336,2552620],"length":1,"stats":{"Line":1}},{"line":15,"address":[1629705],"length":1,"stats":{"Line":1}},{"line":16,"address":[41382174],"length":1,"stats":{"Line":1}},{"line":17,"address":[2552423],"length":1,"stats":{"Line":1}},{"line":18,"address":[4098992],"length":1,"stats":{"Line":1}},{"line":20,"address":[884517,884455],"length":1,"stats":{"Line":2}}],"covered":6,"coverable":6},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","registers.rs"],"content":"use serde::{Deserialize, Serialize};\n\n/// Registers. numbered 1-16 publicly.\n/// Privately using zero base array so -1 to shift from pub to priv.\n#[derive(Clone, Copy, Serialize, Deserialize, Debug)]\npub struct Chip8Registers {\n pub registers: [u8; 16],\n pub i_register: u16,\n pub pc: u16,\n}\n\nimpl PartialEq for Chip8Registers {\n fn eq(\u0026self, other: \u0026Self) -\u003e bool {\n self.pc == other.pc \u0026\u0026 self.registers == other.registers \u0026\u0026 self.i_register == other.i_register\n }\n}\n\nimpl Chip8Registers {\n pub fn advance_pc(\u0026mut self) {\n self.pc += 2;\n }\n}\n\nimpl Default for Chip8Registers {\n fn default() -\u003e Self {\n Chip8Registers {\n registers: [0x00; 16],\n i_register: 0x00,\n pc: 0x200,\n }\n }\n}\n\nimpl Chip8Registers {\n pub fn reset(\u0026mut self) {\n self.registers = [0x00; 16];\n self.i_register = 0x00;\n self.pc = 0x200;\n }\n\n pub fn poke_pc(\u0026mut self, new_pc: u16) {\n self.pc = new_pc\n }\n pub fn peek_i(\u0026self) -\u003e u16 {\n self.i_register\n }\n\n pub fn poke_i(\u0026mut self, new_value: u16) {\n self.i_register = new_value;\n }\n pub fn peek(\u0026self, register_number: u8) -\u003e u8 {\n self.registers[register_number as usize]\n }\n\n pub fn poke(\u0026mut self, register_number: u8, value: u8) {\n if register_number \u003e 0xf {\n panic!(\"INVALID REGISTER\");\n }\n self.registers[register_number as usize] = value;\n }\n\n pub fn peek_pc(\u0026self) -\u003e u16 {\n self.pc\n }\n\n pub fn set_pc(\u0026mut self, new_pc: u16) {\n self.pc = new_pc\n }\n\n pub fn format_as_string(\u0026self) -\u003e String {\n format!(\"Vx: 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x}\\n 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x}\\nI: 0x{:04x}\\tPC: 0x{:04x}\",\n self.registers[0x0],\n self.registers[0x1],\n self.registers[0x2],\n self.registers[0x3],\n self.registers[0x4],\n self.registers[0x5],\n self.registers[0x6],\n self.registers[0x7],\n self.registers[0x8],\n self.registers[0x9],\n self.registers[0xa],\n self.registers[0xb],\n self.registers[0xc],\n self.registers[0xd],\n self.registers[0xe],\n self.registers[0xf],\n self.i_register,\n self.pc\n )\n }\n}\n","traces":[{"line":13,"address":[41420144],"length":1,"stats":{"Line":1}},{"line":14,"address":[813895],"length":1,"stats":{"Line":1}},{"line":19,"address":[41420256],"length":1,"stats":{"Line":2}},{"line":20,"address":[1765107,1765069],"length":1,"stats":{"Line":2}},{"line":25,"address":[41420336],"length":1,"stats":{"Line":7}},{"line":27,"address":[500878],"length":1,"stats":{"Line":7}},{"line":35,"address":[814144],"length":1,"stats":{"Line":4}},{"line":36,"address":[807694],"length":1,"stats":{"Line":4}},{"line":37,"address":[1608319],"length":1,"stats":{"Line":4}},{"line":38,"address":[2482197],"length":1,"stats":{"Line":4}},{"line":41,"address":[1015392],"length":1,"stats":{"Line":8}},{"line":42,"address":[1608349],"length":1,"stats":{"Line":8}},{"line":44,"address":[1327440],"length":1,"stats":{"Line":5}},{"line":45,"address":[799301],"length":1,"stats":{"Line":5}},{"line":48,"address":[1608384],"length":1,"stats":{"Line":5}},{"line":49,"address":[2482269],"length":1,"stats":{"Line":5}},{"line":51,"address":[814272],"length":1,"stats":{"Line":6}},{"line":52,"address":[1015530,1015493],"length":1,"stats":{"Line":5}},{"line":55,"address":[1608496],"length":1,"stats":{"Line":5}},{"line":56,"address":[2482403],"length":1,"stats":{"Line":6}},{"line":57,"address":[1015611],"length":1,"stats":{"Line":1}},{"line":59,"address":[1765467,1765557,1765544],"length":1,"stats":{"Line":14}},{"line":62,"address":[4030832],"length":1,"stats":{"Line":5}},{"line":63,"address":[1706917],"length":1,"stats":{"Line":5}},{"line":66,"address":[41420800],"length":1,"stats":{"Line":5}},{"line":67,"address":[1706941],"length":1,"stats":{"Line":5}},{"line":70,"address":[890496],"length":1,"stats":{"Line":1}},{"line":71,"address":[1327806],"length":1,"stats":{"Line":1}}],"covered":28,"coverable":28},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","sound_timer.rs"],"content":"use log::trace;\nuse serde::{Deserialize, Serialize};\n\n#[derive(Clone, Copy, Serialize, Deserialize, Debug)]\npub struct SoundTimer {\n counter: i32,\n}\n\nimpl Default for SoundTimer {\n fn default() -\u003e Self {\n Self::new()\n }\n}\n\nimpl SoundTimer {\n pub fn current(\u0026self) -\u003e i32 {\n self.counter\n }\n pub fn new() -\u003e Self {\n SoundTimer { counter: 0 }\n }\n pub fn set_timer(\u0026mut self, new_value: i32) {\n trace!(\"SETTING SOUND TIMER TO {new_value}\");\n self.counter = new_value\n }\n\n pub fn tick(\u0026mut self) {\n trace!(\n \"TICKING SOUND FROM {} to {}\",\n self.counter,\n self.counter - 1\n );\n if self.counter \u003e 0 {\n self.counter -= 1;\n /*\n todo: this breaks tests. maybe its wrong?\n if let good_thread = thread::spawn(|| {\n beep(440).expect(\"Unable to beep\");\n thread::sleep(time::Duration::from_millis(500));\n }).join().unwrap().\n */\n }\n }\n\n pub fn reset(\u0026mut self) {\n self.counter = 0x00;\n }\n}\n","traces":[{"line":10,"address":[4035264],"length":1,"stats":{"Line":1}},{"line":11,"address":[804017],"length":1,"stats":{"Line":1}},{"line":16,"address":[812496],"length":1,"stats":{"Line":1}},{"line":17,"address":[450773],"length":1,"stats":{"Line":1}},{"line":22,"address":[1711392],"length":1,"stats":{"Line":1}},{"line":23,"address":[1406196,1406119],"length":1,"stats":{"Line":2}},{"line":24,"address":[812586],"length":1,"stats":{"Line":1}},{"line":27,"address":[1770352],"length":1,"stats":{"Line":5}},{"line":28,"address":[812836,812914,812990],"length":1,"stats":{"Line":8}},{"line":33,"address":[1712105,1711736],"length":1,"stats":{"Line":6}},{"line":34,"address":[4036027,4036001],"length":1,"stats":{"Line":1}},{"line":45,"address":[813264],"length":1,"stats":{"Line":4}},{"line":46,"address":[895669],"length":1,"stats":{"Line":4}}],"covered":13,"coverable":13},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","stack.rs"],"content":"use serde::{Deserialize, Serialize};\n\n#[derive(Clone, Default, Serialize, Deserialize, Debug)]\npub struct Chip8Stack {\n items: Vec\u003cu16\u003e,\n}\n\nimpl Chip8Stack {\n pub fn push(\u0026mut self, new_value: \u0026u16) {\n if self.depth() == 16 {\n // println!(\"Deep deep stack?\");\n panic!(\"Stack Overflow\");\n }\n self.items.push(*new_value);\n }\n pub fn pop(\u0026mut self) -\u003e u16 {\n if self.items.is_empty() {\n panic!(\"Stack Underflow\");\n }\n self.items.pop().unwrap()\n }\n pub fn depth(\u0026self) -\u003e u16 {\n self.items.len() as u16\n }\n\n pub fn new() -\u003e Self {\n Chip8Stack { items: vec![] }\n }\n\n pub fn reset(\u0026mut self) {\n self.items = vec![]\n }\n}\n","traces":[{"line":9,"address":[557440],"length":1,"stats":{"Line":4}},{"line":10,"address":[894440],"length":1,"stats":{"Line":4}},{"line":12,"address":[41394580],"length":1,"stats":{"Line":1}},{"line":14,"address":[989115],"length":1,"stats":{"Line":4}},{"line":16,"address":[884800],"length":1,"stats":{"Line":4}},{"line":17,"address":[876222],"length":1,"stats":{"Line":4}},{"line":18,"address":[1742359],"length":1,"stats":{"Line":1}},{"line":20,"address":[884829],"length":1,"stats":{"Line":3}},{"line":22,"address":[864016],"length":1,"stats":{"Line":4}},{"line":23,"address":[1680933],"length":1,"stats":{"Line":4}},{"line":26,"address":[989296],"length":1,"stats":{"Line":3}},{"line":27,"address":[1680957],"length":1,"stats":{"Line":3}},{"line":30,"address":[864096,864159],"length":1,"stats":{"Line":4}},{"line":31,"address":[894765,894845],"length":1,"stats":{"Line":8}}],"covered":14,"coverable":14},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","system_memory.rs"],"content":"use log::trace;\n\nuse crate::constants::*;\nuse serde::Deserialize;\nuse serde::Serialize;\n\nuse super::quirk_modes::QuirkMode;\n\n#[derive(Clone, Serialize, Deserialize, Debug)]\npub struct Chip8SystemMemory {\n memory: Vec\u003cu8\u003e,\n}\n\nimpl PartialEq for Chip8SystemMemory {\n fn eq(\u0026self, other: \u0026Self) -\u003e bool {\n self.memory == other.memory\n }\n}\n\nimpl Default for Chip8SystemMemory {\n fn default() -\u003e Self {\n let mut x = Chip8SystemMemory::new();\n\n x.load_fonts_to_memory();\n x.load_schip_fonts_to_memory();\n x\n }\n}\n\nimpl Chip8SystemMemory {\n fn empty_memory() -\u003e Vec\u003cu8\u003e {\n let mut working_memory = vec![];\n\n for _ in 0..CHIP8_MEMORY_SIZE {\n working_memory.push(0x00);\n }\n working_memory\n }\n\n pub fn reset(\u0026mut self, quirk_modes: QuirkMode) {\n self.memory = Chip8SystemMemory::empty_memory();\n match quirk_modes {\n QuirkMode::Chip8 =\u003e {\n self.load_fonts_to_memory();\n }\n QuirkMode::XOChip =\u003e {\n println!(\"NO XO FONT LOADING DONE YET\");\n }\n QuirkMode::SChipModern =\u003e {\n self.load_schip_fonts_to_memory();\n }\n }\n }\n\n pub fn new() -\u003e Self {\n Chip8SystemMemory {\n memory: Chip8SystemMemory::empty_memory(),\n }\n }\n\n pub fn peek(\u0026self, address: u16) -\u003e u8 {\n trace!(\n \"PEEK: {} / {}\",\n address,\n self.memory[address as usize].clone()\n );\n let effective = address as i32 % CHIP8_MEMORY_SIZE;\n self.memory[effective as usize]\n }\n\n pub fn poke(\u0026mut self, address: u16, value: u8) {\n trace!(\n \"POKE: {} / {} to {}\",\n address,\n self.memory[address as usize],\n value\n );\n self.memory[address as usize] = value;\n }\n\n pub fn load_program(\u0026mut self, program_to_load: Box\u003cVec\u003cu8\u003e\u003e) {\n for load_index in 0..program_to_load.len() {\n self.poke((load_index + 0x200) as u16, program_to_load[load_index]);\n }\n }\n\n pub fn load_fonts_to_memory(\u0026mut self) {\n let all_font_characters = [\n CHIP8FONT_0,\n CHIP8FONT_1,\n CHIP8FONT_2,\n CHIP8FONT_3,\n CHIP8FONT_4,\n CHIP8FONT_5,\n CHIP8FONT_6,\n CHIP8FONT_7,\n CHIP8FONT_8,\n CHIP8FONT_9,\n CHIP8FONT_A,\n CHIP8FONT_B,\n CHIP8FONT_C,\n CHIP8FONT_D,\n CHIP8FONT_E,\n CHIP8FONT_F,\n ];\n\n for (font_index, current_font) in all_font_characters.iter().enumerate() {\n for font_mem_offset in 0..=4 {\n let real_offset = font_index * 5 + font_mem_offset;\n self.poke(real_offset as u16, current_font[font_mem_offset]);\n }\n }\n }\n\n pub fn load_schip_fonts_to_memory(\u0026mut self) {\n let all_font_characters = [\n SCHIPFONT_0,\n SCHIPFONT_1,\n SCHIPFONT_2,\n SCHIPFONT_3,\n SCHIPFONT_4,\n SCHIPFONT_5,\n SCHIPFONT_6,\n SCHIPFONT_7,\n SCHIPFONT_8,\n SCHIPFONT_9,\n SCHIPFONT_A,\n SCHIPFONT_B,\n SCHIPFONT_C,\n SCHIPFONT_D,\n SCHIPFONT_E,\n SCHIPFONT_F,\n ];\n for (font_index, current_font) in all_font_characters.iter().enumerate() {\n let base_offset = SCHIPFONT_OFFSET;\n for font_mem_offset in 0..=9 {\n let real_offset = base_offset + (font_index * 0x0a) as u32 + font_mem_offset;\n self.poke(real_offset as u16, current_font[font_mem_offset as usize]);\n }\n }\n }\n}\n","traces":[{"line":15,"address":[983728],"length":1,"stats":{"Line":1}},{"line":16,"address":[870814],"length":1,"stats":{"Line":1}},{"line":21,"address":[559520,559642],"length":1,"stats":{"Line":9}},{"line":22,"address":[1675486],"length":1,"stats":{"Line":12}},{"line":24,"address":[1736998],"length":1,"stats":{"Line":11}},{"line":25,"address":[1737041],"length":1,"stats":{"Line":6}},{"line":26,"address":[870939],"length":1,"stats":{"Line":6}},{"line":31,"address":[984141,983920],"length":1,"stats":{"Line":11}},{"line":32,"address":[1300814],"length":1,"stats":{"Line":12}},{"line":34,"address":[2557297,2557228],"length":1,"stats":{"Line":23}},{"line":35,"address":[4001282],"length":1,"stats":{"Line":11}},{"line":37,"address":[871155],"length":1,"stats":{"Line":12}},{"line":40,"address":[2557440,2557509],"length":1,"stats":{"Line":4}},{"line":41,"address":[41389898,41389972],"length":1,"stats":{"Line":8}},{"line":42,"address":[41389989],"length":1,"stats":{"Line":4}},{"line":44,"address":[1301194],"length":1,"stats":{"Line":3}},{"line":47,"address":[560049],"length":1,"stats":{"Line":1}},{"line":50,"address":[1583126],"length":1,"stats":{"Line":1}},{"line":55,"address":[4001552],"length":1,"stats":{"Line":11}},{"line":57,"address":[1737597],"length":1,"stats":{"Line":12}},{"line":61,"address":[1583216],"length":1,"stats":{"Line":5}},{"line":62,"address":[1737767,1737844,1737676],"length":1,"stats":{"Line":2}},{"line":67,"address":[1301387,1301772,1301731],"length":1,"stats":{"Line":10}},{"line":68,"address":[871938],"length":1,"stats":{"Line":5}},{"line":71,"address":[1301792],"length":1,"stats":{"Line":6}},{"line":72,"address":[984945,985115,985042],"length":1,"stats":{"Line":7}},{"line":78,"address":[890244],"length":1,"stats":{"Line":11}},{"line":81,"address":[890965,890624],"length":1,"stats":{"Line":1}},{"line":82,"address":[41391161,41391279,41391094],"length":1,"stats":{"Line":3}},{"line":83,"address":[890849],"length":1,"stats":{"Line":1}},{"line":87,"address":[561424],"length":1,"stats":{"Line":12}},{"line":88,"address":[41391460],"length":1,"stats":{"Line":11}},{"line":107,"address":[873161,873331],"length":1,"stats":{"Line":17}},{"line":108,"address":[1739501],"length":1,"stats":{"Line":6}},{"line":109,"address":[882001,882091],"length":1,"stats":{"Line":11}},{"line":110,"address":[1739747,1739791],"length":1,"stats":{"Line":17}},{"line":115,"address":[4003808],"length":1,"stats":{"Line":11}},{"line":116,"address":[873743],"length":1,"stats":{"Line":11}},{"line":134,"address":[1740318,1740488],"length":1,"stats":{"Line":17}},{"line":135,"address":[4003828],"length":1,"stats":{"Line":6}},{"line":136,"address":[41393042],"length":1,"stats":{"Line":6}},{"line":137,"address":[2560878,2560751],"length":1,"stats":{"Line":11}},{"line":138,"address":[883119,883170],"length":1,"stats":{"Line":17}}],"covered":43,"coverable":43},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","util.rs"],"content":"pub struct InstructionUtil {}\n\nimpl InstructionUtil {\n /// Byte To Bools\n /// Convert a u8 byte to an array of bool\n pub fn byte_to_bools(to_convert: u8) -\u003e [bool; 8] {\n let mut return_values = [false; 8];\n for i in 0..8 {\n let new_value = to_convert \u003e\u003e i \u0026 0x1 == 1;\n return_values[i as usize] = new_value;\n }\n return_values\n }\n\n /// Byte To Bools\n /// Convert an array of bool to a u8\n pub fn bools_to_byte(to_convert: [bool; 8]) -\u003e u8 {\n let mut return_value = 0u8;\n for i in 0..to_convert.len() {\n let new_bit = 0x1 \u003c\u003c i;\n if to_convert[i] {\n return_value |= new_bit\n }\n }\n return_value\n }\n\n /// Split Bytes\n /// Split a u16 into a pair of u8 values for high and low byte\n pub fn split_bytes(to_split: u16) -\u003e (u8, u8) {\n let high = to_split.rotate_left(8) as u8;\n let low = (to_split \u0026 0xff) as u8;\n\n (high, low)\n }\n\n /// Join Bytes\n /// Joins 2 u8 into a single u16 with high and low\n /// bytes provided\n pub fn join_bytes(high: u8, low: u8) -\u003e u16 {\n (high as u16) \u003c\u003c 8 | low as u16\n }\n\n /// Swap Endian\n /// Swaps the endianess of a value\n /// 0xabcd -\u003e 0xcdab\n pub fn swap_endian(to_swap:u16) -\u003e u16 {\n let low = to_swap \u0026 0xff;\n let high = to_swap \u0026 0xff00;\n low \u003c\u003c 8 | high \u003e\u003e 8\n }\n\n /// Split Bytes Swap Endian\n /// Swaps endianness and return both bytes separately\n pub fn split_bytes_swap_endian(to_split: u16) -\u003e (u8, u8) {\n InstructionUtil::split_bytes(InstructionUtil::swap_endian(to_split))\n }\n\n /// Join Bytes Swap Endian\n /// Swaps endianness and returns the combined bytes\n pub fn join_bytes_swap_endian(high :u8, low: u8) -\u003e u16 {\n InstructionUtil::swap_endian(InstructionUtil::join_bytes(high, low))\n }\n\n /// Read Address from Instruction\n /// Reads the 'address' portion of an instruction\n /// Returns the 12 LSB\n /// 0xabcd -\u003e 0x0bcd\n pub fn read_addr_from_instruction(instruction_to_read_from: u16) -\u003e u16 {\n instruction_to_read_from \u0026 0x0FFF\n }\n\n /// Read Nibble from Instruction\n /// Returns the lowest 4 bits of the instruction\n /// 0xabcd -\u003e 0x0d\n pub fn read_nibble_from_instruction(instruction_to_read_from: u16) -\u003e u8 {\n ( instruction_to_read_from \u0026 0x000F )as u8\n }\n\n /// Read X From Instruction\n /// Returns the 4 LSB of the high byte of the instruction\n /// Returns bits 9-13\n /// 0xabcd -\u003e 0x0b\n pub fn read_x_from_instruction(instruction_to_read_from: u16) -\u003e u8 {\n (instruction_to_read_from \u0026 0x0F00).rotate_right(8) as u8\n }\n\n /// Read Y from Instruction\n /// Returns the 4 MSB from the low byte of the instruction\n /// Returns bits 5-8\n /// 0xabcd -\u003e 0x0c\n pub fn read_y_from_instruction(instruction_to_read_from: u16) -\u003e u8 {\n (instruction_to_read_from \u0026 0x00F0).rotate_right(4) as u8\n }\n\n /// Read Byte from Instruction\n /// Returns the 8 LSB from the instruction\n /// Returns bits 0-7\n /// 0xabcd -\u003e 0xcd\n pub fn read_byte_from_instruction(instruction_to_read_from: u16) -\u003e u8 {\n (instruction_to_read_from \u0026 0x00FF) as u8\n }\n\n /// Alias for read_x_from_instruction\n pub fn read_upper_byte_lower_nibble(to_read_from: u16) -\u003e u8 {\n InstructionUtil::read_x_from_instruction(to_read_from)\n }\n\n pub fn read_bits_from_instruction(instruction_bytes: u16, first_bit: u8, last_bit: u8) -\u003e u16 {\n // shift the value over by the 'first bit' bits\n let working = instruction_bytes.rotate_right(first_bit as u32);\n let mut working_mask: i32 = 0x00;\n // build the mask\n for _ in first_bit..=last_bit {\n working_mask = working_mask.rotate_left(1) | 0x01;\n }\n\n // apply the mask to the working value\n working \u0026 working_mask as u16\n }\n}\n","traces":[{"line":6,"address":[4092224],"length":1,"stats":{"Line":2}},{"line":7,"address":[1045727],"length":1,"stats":{"Line":2}},{"line":8,"address":[1827584,1827755,1827654],"length":1,"stats":{"Line":6}},{"line":9,"address":[1574411,1574354],"length":1,"stats":{"Line":2}},{"line":10,"address":[1827765,1827708,1827748],"length":1,"stats":{"Line":4}},{"line":12,"address":[969346],"length":1,"stats":{"Line":2}},{"line":17,"address":[1387920],"length":1,"stats":{"Line":2}},{"line":18,"address":[1387939],"length":1,"stats":{"Line":2}},{"line":19,"address":[969528,969591],"length":1,"stats":{"Line":4}},{"line":20,"address":[868318,868278],"length":1,"stats":{"Line":2}},{"line":21,"address":[868310,868339,868386],"length":1,"stats":{"Line":6}},{"line":22,"address":[969706],"length":1,"stats":{"Line":2}},{"line":25,"address":[1387998],"length":1,"stats":{"Line":2}},{"line":30,"address":[878128],"length":1,"stats":{"Line":2}},{"line":31,"address":[41482645],"length":1,"stats":{"Line":2}},{"line":32,"address":[1574745],"length":1,"stats":{"Line":2}},{"line":40,"address":[1768784],"length":1,"stats":{"Line":2}},{"line":41,"address":[1388206],"length":1,"stats":{"Line":2}},{"line":47,"address":[4092768],"length":1,"stats":{"Line":1}},{"line":48,"address":[868488],"length":1,"stats":{"Line":1}},{"line":49,"address":[449524],"length":1,"stats":{"Line":1}},{"line":50,"address":[969838],"length":1,"stats":{"Line":1}},{"line":55,"address":[878256],"length":1,"stats":{"Line":1}},{"line":56,"address":[1768873],"length":1,"stats":{"Line":1}},{"line":61,"address":[1574880],"length":1,"stats":{"Line":1}},{"line":62,"address":[1388335],"length":1,"stats":{"Line":1}},{"line":69,"address":[878352],"length":1,"stats":{"Line":7}},{"line":70,"address":[1828248],"length":1,"stats":{"Line":7}},{"line":76,"address":[1388384],"length":1,"stats":{"Line":7}},{"line":77,"address":[4092936],"length":1,"stats":{"Line":7}},{"line":84,"address":[2546400],"length":1,"stats":{"Line":7}},{"line":85,"address":[1388408],"length":1,"stats":{"Line":7}},{"line":92,"address":[41482912],"length":1,"stats":{"Line":7}},{"line":93,"address":[868712],"length":1,"stats":{"Line":7}},{"line":100,"address":[843952],"length":1,"stats":{"Line":7}},{"line":101,"address":[2546504],"length":1,"stats":{"Line":7}},{"line":105,"address":[2546512],"length":1,"stats":{"Line":7}},{"line":106,"address":[1388521],"length":1,"stats":{"Line":7}},{"line":109,"address":[1046576],"length":1,"stats":{"Line":1}},{"line":111,"address":[844058],"length":1,"stats":{"Line":1}},{"line":112,"address":[1388607],"length":1,"stats":{"Line":1}},{"line":114,"address":[41483079,41483307],"length":1,"stats":{"Line":2}},{"line":115,"address":[1388806,1388836],"length":1,"stats":{"Line":2}},{"line":119,"address":[860586],"length":1,"stats":{"Line":1}}],"covered":44,"coverable":44},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","video.rs"],"content":"use crate::chip8::video::Chip8VideoModes::{HighRes, LowRes};\nuse crate::constants::{\n CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_MEMORY, CHIP8_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT,\n SCHIP_VIDEO_WIDTH, SCHIP_VIDE_MEMORY,\n};\nuse log::debug;\nuse serde::{Deserialize, Serialize};\n\n#[derive(Clone, Copy, Serialize, Deserialize, Debug)]\npub enum Chip8VideoModes {\n LowRes,\n HighRes,\n}\n\n#[derive(Clone, Serialize, Deserialize, Debug)]\npub struct Chip8Video {\n memory: Vec\u003cbool\u003e,\n pub has_frame_changed: bool,\n current_res: Chip8VideoModes,\n}\n\nimpl Chip8Video {\n pub fn reset(\u0026mut self) {\n self.cls();\n self.set_lowres();\n self.start_frame();\n }\n\n pub fn is_highres(\u0026self) -\u003e bool {\n matches!(self.current_res, HighRes)\n }\n\n pub fn set_highres(\u0026mut self) {\n self.current_res = HighRes;\n self.cls();\n }\n\n pub fn set_lowres(\u0026mut self) {\n self.current_res = LowRes\n }\n\n pub fn get_screen_resolution(\u0026mut self) -\u003e Chip8VideoModes {\n self.current_res\n }\n\n pub fn cls(\u0026mut self) {\n self.memory.clear();\n let num_loops = match self.current_res {\n LowRes =\u003e CHIP8_VIDEO_MEMORY,\n HighRes =\u003e SCHIP_VIDE_MEMORY,\n };\n for _ in 0..num_loops {\n self.memory.push(false);\n }\n }\n\n pub fn start_frame(\u0026mut self) {\n self.has_frame_changed = false;\n }\n\n pub fn new(initial_configuration: Box\u003cVec\u003cbool\u003e\u003e) -\u003e Self {\n Self {\n memory: *initial_configuration,\n has_frame_changed: false,\n current_res: Chip8VideoModes::LowRes,\n }\n }\n\n pub fn peek(\u0026self, address: u16) -\u003e bool {\n let loop_value: u16 = if self.is_highres() {\n SCHIP_VIDE_MEMORY as u16\n } else {\n CHIP8_VIDEO_MEMORY as u16\n };\n let effective_address = if address \u003e= loop_value {\n address % loop_value\n } else {\n address\n };\n self.memory[effective_address as usize]\n }\n\n pub fn poke(\u0026mut self, address: u16, new_value: bool) {\n let loop_value: u16 = self.get_memory_size() as u16;\n // Loop the address\n let effective_address = address % loop_value;\n\n let old_value = self.memory[effective_address as usize];\n let xored_value = new_value ^ old_value; // XOR of the video\n // if the frame has already changed we dont care if it changed again.\n if !self.has_frame_changed \u0026\u0026 old_value != xored_value {\n self.has_frame_changed = true\n }\n\n // println!(\"VIDEO POKE COMPLETE WITH {effective_address} SET TO {xored_value}\");\n self.memory[effective_address as usize] = xored_value;\n }\n\n pub fn poke_byte(\u0026mut self, first_address: u16, to_write: u8) {\n for i in (0..8).rev() {\n self.poke(first_address + (7 - i), (to_write \u0026 (1 \u003c\u003c i)) != 0);\n }\n }\n\n pub fn poke_2byte(\u0026mut self, first_address: u16, to_write: [u8; 2]) {\n for (idx, _) in to_write.iter().enumerate() {\n for i in (0..8).rev() {\n self.poke(\n first_address + (idx * 8) as u16 + (7 - i),\n (to_write[idx] \u0026 (1 \u003c\u003c i)) != 0,\n );\n }\n }\n }\n\n pub fn format_as_string(\u0026self) -\u003e String {\n let (width, height) = self.get_resolution();\n // println!(\"FORMATTING {width}x{height}\");\n let mut output = String::new();\n for row in 0..height {\n for column in 0..width {\n let data_offset = row * width + column;\n debug!(\n \"Rendering {data_offset} with value {}\",\n self.memory[data_offset as usize]\n );\n if self.memory[data_offset as usize] {\n output += \"*\"\n } else {\n output += \" \"\n }\n }\n output += \"\\n\";\n }\n output\n }\n\n pub fn get_resolution(\u0026self) -\u003e (i32, i32) {\n if self.is_highres() {\n (SCHIP_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT)\n } else {\n (CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT)\n }\n }\n fn get_memory_size(\u0026self) -\u003e i32 {\n let (width, height) = self.get_resolution();\n width * height\n }\n\n pub fn tick(\u0026mut self) {\n self.has_frame_changed = false;\n }\n\n pub fn scroll_right(\u0026mut self) {\n // println!(\"SCROLLRIGHTPRE:::[{}]\", self.format_as_string());\n let (width, height) = self.get_resolution();\n\n for current_row in 0..height {\n let row_offset: usize = (current_row * width) as usize;\n\n // Shift pixels to the right by 4 in the current row\n for current_column in (0..(width - 4)).rev() {\n let source_address = row_offset + current_column as usize;\n let target_address = source_address + 4;\n self.memory[target_address] = self.memory[source_address];\n }\n\n // Clear the first 4 pixels in the current row\n self.memory[row_offset..row_offset + 4].fill(false);\n }\n // println!(\"SCROLLRIGHTPOST:::[{}]\", self.format_as_string());\n }\n\n pub fn scroll_left(\u0026mut self) {\n // println!(\"SCROLLLEFTPRE:::[{}]\", self.format_as_string());\n let (width, height) = self.get_resolution();\n\n for current_row in 0..height {\n let row_offset = current_row * width;\n for current_column in 0..width - 4 {\n let target: usize = (row_offset + current_column) as usize;\n let source: usize = target + 4;\n self.memory[target] = self.memory[source];\n // println!(\"Moving from {source} to {target}\");\n }\n\n let clear_start: usize = (row_offset + width - 4) as usize;\n let clear_end: usize = clear_start + 4;\n\n self.memory[clear_start..clear_end].fill(false);\n }\n // println!(\"SCROLLLEFTPOST:::[{}]\", self.format_as_string());\n }\n\n pub fn scroll_up(\u0026mut self, how_far: \u0026u8) {\n let how_far = *how_far as i32;\n let (width, height) = self.get_resolution();\n let row_shift = how_far * width;\n\n for current_source_row in how_far..height {\n let current_source_offset = current_source_row * width;\n for current_source_column in 0..width {\n let base_offset: usize = (current_source_offset + current_source_column) as usize;\n let shifted_offset: usize = base_offset - row_shift as usize;\n self.memory[shifted_offset] = self.memory[base_offset];\n }\n }\n\n // Clear the new bottom rows after shifting\n let clear_start = ((height - how_far) * width) as usize;\n self.memory[clear_start..].fill(false);\n }\n\n pub fn scroll_down(\u0026mut self, how_far: i32) {\n let (width, height) = self.get_resolution();\n let row_shift = how_far * width;\n\n let max_source_row = height - how_far;\n for current_source_row in (0..max_source_row).rev() {\n let current_source_offset = current_source_row * width;\n for current_source_column in 0..width {\n let base_offset: usize = (current_source_offset + current_source_column) as usize;\n let extended_offset: usize = base_offset + row_shift as usize;\n self.memory[extended_offset] = self.memory[base_offset];\n }\n }\n\n // Clear the new top rows after shifting\n let clear_end = (how_far * width) as usize;\n self.memory[0..clear_end].fill(false);\n }\n}\n\nimpl Default for Chip8Video {\n fn default() -\u003e Self {\n let mut mem = vec![];\n for _ in 0..CHIP8_VIDEO_MEMORY {\n mem.push(false);\n }\n Chip8Video {\n memory: mem,\n has_frame_changed: false,\n current_res: LowRes,\n }\n }\n}\n","traces":[{"line":23,"address":[1315696],"length":1,"stats":{"Line":2}},{"line":24,"address":[798366],"length":1,"stats":{"Line":2}},{"line":25,"address":[41408777],"length":1,"stats":{"Line":4}},{"line":26,"address":[1002692],"length":1,"stats":{"Line":4}},{"line":29,"address":[877440],"length":1,"stats":{"Line":2}},{"line":30,"address":[1593365],"length":1,"stats":{"Line":2}},{"line":33,"address":[4018880],"length":1,"stats":{"Line":1}},{"line":34,"address":[459897],"length":1,"stats":{"Line":1}},{"line":35,"address":[877495],"length":1,"stats":{"Line":1}},{"line":38,"address":[459936],"length":1,"stats":{"Line":4}},{"line":39,"address":[41408885],"length":1,"stats":{"Line":4}},{"line":42,"address":[41408912],"length":1,"stats":{"Line":1}},{"line":43,"address":[1695045],"length":1,"stats":{"Line":1}},{"line":46,"address":[787728],"length":1,"stats":{"Line":2}},{"line":47,"address":[796205],"length":1,"stats":{"Line":2}},{"line":48,"address":[41408951],"length":1,"stats":{"Line":2}},{"line":49,"address":[1315909],"length":1,"stats":{"Line":2}},{"line":50,"address":[798576],"length":1,"stats":{"Line":1}},{"line":52,"address":[877625],"length":1,"stats":{"Line":2}},{"line":53,"address":[877688],"length":1,"stats":{"Line":2}},{"line":57,"address":[41409072],"length":1,"stats":{"Line":4}},{"line":58,"address":[787877],"length":1,"stats":{"Line":4}},{"line":61,"address":[41409088],"length":1,"stats":{"Line":1}},{"line":63,"address":[1593667,1593794],"length":1,"stats":{"Line":1}},{"line":69,"address":[788064],"length":1,"stats":{"Line":2}},{"line":70,"address":[41409308,41409291],"length":1,"stats":{"Line":4}},{"line":71,"address":[2466926],"length":1,"stats":{"Line":1}},{"line":73,"address":[877941],"length":1,"stats":{"Line":2}},{"line":75,"address":[796586,796677,796603],"length":1,"stats":{"Line":5}},{"line":76,"address":[2467019,2467031,2466957],"length":1,"stats":{"Line":2}},{"line":78,"address":[798934],"length":1,"stats":{"Line":2}},{"line":80,"address":[1003268],"length":1,"stats":{"Line":2}},{"line":83,"address":[796704],"length":1,"stats":{"Line":2}},{"line":84,"address":[1316423],"length":1,"stats":{"Line":2}},{"line":86,"address":[1754399,1754294],"length":1,"stats":{"Line":2}},{"line":88,"address":[1316474],"length":1,"stats":{"Line":2}},{"line":89,"address":[799178],"length":1,"stats":{"Line":2}},{"line":91,"address":[4019639,4019733,4019671],"length":1,"stats":{"Line":6}},{"line":92,"address":[799281],"length":1,"stats":{"Line":2}},{"line":96,"address":[796908],"length":1,"stats":{"Line":2}},{"line":99,"address":[788496],"length":1,"stats":{"Line":1}},{"line":100,"address":[41409733,41409805],"length":1,"stats":{"Line":3}},{"line":101,"address":[1594380],"length":1,"stats":{"Line":2}},{"line":105,"address":[1754800],"length":1,"stats":{"Line":1}},{"line":106,"address":[799794,799654],"length":1,"stats":{"Line":2}},{"line":107,"address":[2467831],"length":1,"stats":{"Line":1}},{"line":108,"address":[800193],"length":1,"stats":{"Line":1}},{"line":109,"address":[1317274,1317435],"length":1,"stats":{"Line":1}},{"line":110,"address":[4020664,4020531,4020565],"length":1,"stats":{"Line":2}},{"line":116,"address":[1004544,1005668],"length":1,"stats":{"Line":2}},{"line":117,"address":[41410678],"length":1,"stats":{"Line":2}},{"line":119,"address":[1755519],"length":1,"stats":{"Line":2}},{"line":120,"address":[1755528,1755601,1755734],"length":1,"stats":{"Line":6}},{"line":121,"address":[789752,789892],"length":1,"stats":{"Line":4}},{"line":122,"address":[879750,879777,879863],"length":1,"stats":{"Line":4}},{"line":123,"address":[2469041,2468861,2468948,2468797],"length":1,"stats":{"Line":0}},{"line":127,"address":[1697384,1697823],"length":1,"stats":{"Line":4}},{"line":128,"address":[1697855,1697887],"length":1,"stats":{"Line":4}},{"line":130,"address":[1596314,1596260],"length":1,"stats":{"Line":4}},{"line":133,"address":[1697252,1697185],"length":1,"stats":{"Line":4}},{"line":135,"address":[1697015],"length":1,"stats":{"Line":2}},{"line":138,"address":[1005696],"length":1,"stats":{"Line":2}},{"line":139,"address":[462793,462818],"length":1,"stats":{"Line":4}},{"line":140,"address":[2469445],"length":1,"stats":{"Line":1}},{"line":142,"address":[790611],"length":1,"stats":{"Line":2}},{"line":145,"address":[41411872],"length":1,"stats":{"Line":2}},{"line":146,"address":[880521],"length":1,"stats":{"Line":2}},{"line":147,"address":[1698023,1698044],"length":1,"stats":{"Line":2}},{"line":150,"address":[1005840],"length":1,"stats":{"Line":5}},{"line":151,"address":[462933],"length":1,"stats":{"Line":5}},{"line":154,"address":[799216],"length":1,"stats":{"Line":1}},{"line":156,"address":[1005873],"length":1,"stats":{"Line":1}},{"line":158,"address":[1596549,1596606],"length":1,"stats":{"Line":2}},{"line":159,"address":[41412101,41412054],"length":1,"stats":{"Line":1}},{"line":162,"address":[463413,463076,463110,463206],"length":1,"stats":{"Line":4}},{"line":163,"address":[4022365,4022279,4022389],"length":1,"stats":{"Line":2}},{"line":164,"address":[881012,881076,880965],"length":1,"stats":{"Line":2}},{"line":165,"address":[1596940],"length":1,"stats":{"Line":1}},{"line":169,"address":[1698327,1698392],"length":1,"stats":{"Line":2}},{"line":174,"address":[2470080],"length":1,"stats":{"Line":1}},{"line":176,"address":[881121],"length":1,"stats":{"Line":1}},{"line":178,"address":[791307,791364],"length":1,"stats":{"Line":2}},{"line":179,"address":[463589,463548],"length":1,"stats":{"Line":1}},{"line":180,"address":[881242,881671,881276,881364],"length":1,"stats":{"Line":4}},{"line":181,"address":[1698863,1699043,1699074],"length":1,"stats":{"Line":2}},{"line":182,"address":[802572,802636,802530],"length":1,"stats":{"Line":2}},{"line":183,"address":[1597540],"length":1,"stats":{"Line":1}},{"line":187,"address":[1757559,1757624,1757507],"length":1,"stats":{"Line":2}},{"line":188,"address":[1319799,1319839,1319752],"length":1,"stats":{"Line":2}},{"line":190,"address":[800127],"length":1,"stats":{"Line":1}},{"line":195,"address":[1597616],"length":1,"stats":{"Line":1}},{"line":196,"address":[1320022],"length":1,"stats":{"Line":1}},{"line":197,"address":[464065],"length":1,"stats":{"Line":1}},{"line":198,"address":[1699245,1699289],"length":1,"stats":{"Line":1}},{"line":200,"address":[1320121,1320175,1320089],"length":1,"stats":{"Line":3}},{"line":201,"address":[792177,792202,792042],"length":1,"stats":{"Line":2}},{"line":202,"address":[1007515,1007288,1007322],"length":1,"stats":{"Line":3}},{"line":203,"address":[1699649,1699594],"length":1,"stats":{"Line":1}},{"line":204,"address":[1699744,1699680,1699628],"length":1,"stats":{"Line":2}},{"line":205,"address":[1320504],"length":1,"stats":{"Line":1}},{"line":210,"address":[1758153,1758014,1758068],"length":1,"stats":{"Line":2}},{"line":211,"address":[800582],"length":1,"stats":{"Line":1}},{"line":214,"address":[41413632],"length":1,"stats":{"Line":1}},{"line":215,"address":[1007561],"length":1,"stats":{"Line":1}},{"line":216,"address":[1320627,1320668],"length":1,"stats":{"Line":1}},{"line":218,"address":[1320655,1320688,1320720],"length":1,"stats":{"Line":2}},{"line":219,"address":[2471364,2471408,2471463],"length":1,"stats":{"Line":3}},{"line":220,"address":[792658,792755,792780],"length":1,"stats":{"Line":2}},{"line":221,"address":[801260,801450,801226],"length":1,"stats":{"Line":3}},{"line":222,"address":[4024144,4024092],"length":1,"stats":{"Line":1}},{"line":223,"address":[1598638,1598687,1598751],"length":1,"stats":{"Line":2}},{"line":224,"address":[4024183],"length":1,"stats":{"Line":1}},{"line":229,"address":[803429,803481,803531],"length":1,"stats":{"Line":2}},{"line":230,"address":[1320836],"length":1,"stats":{"Line":1}},{"line":235,"address":[1321152,1321455],"length":1,"stats":{"Line":6}},{"line":236,"address":[803825],"length":1,"stats":{"Line":11}},{"line":237,"address":[803920,803839],"length":1,"stats":{"Line":17}},{"line":238,"address":[801745],"length":1,"stats":{"Line":6}}],"covered":117,"coverable":118},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","add.rs"],"content":"use crate::chip8::instructions::Chip8CpuInstructions;\nuse crate::chip8::registers::Chip8Registers;\nuse crate::chip8_2::instructions::instruction_trait::InstructionTrait;\n\nstruct Add {\n pub relative: bool,\n pub source: Option\u003cChip8Registers\u003e,\n pub destination: Chip8Registers,\n pub literal: Option\u003cu8\u003e\n}\n\nimpl InstructionTrait for Add {\n fn length(\u0026self) -\u003e u8 {\n 1\n }\n\n fn encoded(\u0026self) -\u003e Chip8CpuInstructions {\n }\n\n fn decode(from: Vec\u003cu8\u003e) -\u003e \u0026dyn InstructionTrait {\n\n todo!()\n }\n}","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","call.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","cls.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","drw.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","exit.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","high.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","jp.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","ld.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","low.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","ret.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","rnd.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","scd.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","scl.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","scr.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","se.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","sknp.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","skp.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","sne.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","sys.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","constants.rs"],"content":"pub const CHIP8_REGISTER_COUNT: i32 = 16;\npub const CHIP8_MEMORY_SIZE: i32 = 4096i32;\npub const CHIP8_VIDEO_WIDTH: i32 = 64i32;\npub const CHIP8_VIDEO_HEIGHT: i32 = 32i32;\npub const CHIP8_VIDEO_MEMORY: usize = (CHIP8_VIDEO_HEIGHT * CHIP8_VIDEO_WIDTH) as usize;\npub const CHIP8_ROM_SIZE: usize = 512;\n\npub const LABEL_QUIRK_CHIP8: \u0026str = \"Chip8\";\npub const LABEL_QUIRK_XOCHIP: \u0026str = \"XO Chip\";\npub const LABEL_QUIRK_SCHIP: \u0026str = \"SChip-Modern\";\n\npub const RESOURCES_ROOT: \u0026str = \"../resources\";\npub const TESTS_ROOT: \u0026str = \"../resources/test/\";\npub const TEST_ROM_ROOT: \u0026str = \"../resources/test/roms\";\n\npub const CHIP8_KEYBOARD: [[u8; 4]; 4] = [\n [0x01, 0x02, 0x03, 0x0C],\n [0x04, 0x05, 0x06, 0x0D],\n [0x07, 0x08, 0x09, 0x0E],\n [0x0A, 0x00, 0x0B, 0x0F],\n];\n\npub const SCHIP_VIDEO_HEIGHT: i32 = 64;\npub const SCHIP_VIDEO_WIDTH: i32 = 128;\npub const SCHIP_VIDE_MEMORY: usize = (SCHIP_VIDEO_HEIGHT * SCHIP_VIDEO_WIDTH) as usize;\n\npub const INST_ADDI: \u0026str = \"ADDI\";\npub const INST_ADD: \u0026str = \"ADD\";\npub const INST_ADDR: \u0026str = \"ADDR\";\npub const INST_AND: \u0026str = \"AND\";\npub const INST_CLS: \u0026str = \"CLS\";\npub const INST_CALL: \u0026str = \"CALL\";\npub const INST_DRW: \u0026str = \"DRW\";\npub const INST_EXIT: \u0026str = \"EXIT\";\npub const INST_JPA: \u0026str = \"JPA\";\npub const INST_JPI: \u0026str = \"JPI\";\npub const INST_JPX: \u0026str = \"JPX\";\npub const INST_BCD: \u0026str = \"BCD\";\npub const INST_LDD: \u0026str = \"LDD\";\npub const INST_LDF: \u0026str = \"LDF\";\npub const INST_LDF2: \u0026str = \"LDF2\";\npub const INST_LDIA: \u0026str = \"LDIA\";\npub const INST_LDIX: \u0026str = \"LDIX\";\npub const INST_LIDR: \u0026str = \"LIDR\";\npub const INST_LDIS: \u0026str = \"LDIS\";\npub const INST_LDR: \u0026str = \"LDR\";\npub const INST_LDRD: \u0026str = \"LDRD\";\npub const INST_LDRI: \u0026str = \"LDRI\";\npub const INST_LDRK: \u0026str = \"LDRK\";\npub const INST_LDRY: \u0026str = \"LDRY\";\npub const INST_OR: \u0026str = \"OR\";\npub const INST_RET: \u0026str = \"RET\";\npub const INST_RND: \u0026str = \"RND\";\npub const INST_SCD: \u0026str = \"SCD\";\npub const INST_SCR: \u0026str = \"SCR\";\npub const INST_SCL: \u0026str = \"SCL\";\npub const INST_SEX: \u0026str = \"SEX\";\npub const INST_SEY: \u0026str = \"SEY\";\npub const INST_SHL: \u0026str = \"SHL\";\npub const INST_SHR: \u0026str = \"SHR\";\npub const INST_SKP: \u0026str = \"SKP\";\npub const INST_SKNP: \u0026str = \"SKNP\";\npub const INST_SNEB: \u0026str = \"SNEB\";\npub const INST_SNEY: \u0026str = \"SNEY\";\npub const INST_STR: \u0026str = \"STR\";\npub const INST_SUB: \u0026str = \"SUB\";\npub const INST_SUBC: \u0026str = \"SUBC\";\npub const INST_SYS: \u0026str = \"SYS\";\npub const INST_LOW: \u0026str = \"LOW\";\npub const INST_HIGH: \u0026str = \"HIGH\";\npub const INST_ORY: \u0026str = \"ORY\";\npub const INST_SCU: \u0026str = \"SCU\";\n/// Data Word\n/// Data to be loaded to memory for application use\npub const INST_DW: \u0026str = \"DW\";\n\npub const XXXXERRORINSTRUCTION_ENCODED: u16 = 0x0000;\n\npub const CHIP8_PROGRAM_LOAD_OFFSET: i32 = 0x200;\npub const CHIP8FONT_0: [u8; 5] = [0xF0, 0x90, 0x90, 0x90, 0xF0];\npub const CHIP8FONT_1: [u8; 5] = [0x20, 0x60, 0x20, 0x20, 0x70];\npub const CHIP8FONT_2: [u8; 5] = [0xF0, 0x10, 0xF0, 0x80, 0xF0];\npub const CHIP8FONT_3: [u8; 5] = [0xF0, 0x10, 0xF0, 0x10, 0xF0];\npub const CHIP8FONT_4: [u8; 5] = [0x90, 0x90, 0xF0, 0x10, 0x10];\npub const CHIP8FONT_5: [u8; 5] = [0xF0, 0x80, 0xF0, 0x10, 0xF0];\npub const CHIP8FONT_6: [u8; 5] = [0xF0, 0x80, 0xF0, 0x90, 0xF0];\npub const CHIP8FONT_7: [u8; 5] = [0xF0, 0x10, 0x20, 0x40, 0x40];\npub const CHIP8FONT_8: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0xF0];\npub const CHIP8FONT_9: [u8; 5] = [0xF0, 0x90, 0xF0, 0x10, 0xF0];\npub const CHIP8FONT_A: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0x90];\npub const CHIP8FONT_B: [u8; 5] = [0xE0, 0x90, 0xE0, 0x90, 0xE0];\npub const CHIP8FONT_C: [u8; 5] = [0xF0, 0x80, 0x80, 0x80, 0xF0];\npub const CHIP8FONT_D: [u8; 5] = [0xE0, 0x90, 0x90, 0x90, 0xE0];\npub const CHIP8FONT_E: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0xF0];\npub const CHIP8FONT_F: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0x80];\n\npub const SCHIPFONT_OFFSET: u32 = 0x81;\npub const SCHIPFONT_0: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xF0];\npub const SCHIPFONT_1: [u8; 0x0A] = [0x20, 0x60, 0xA0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70];\npub const SCHIPFONT_2: [u8; 0x0A] = [0xF0, 0x10, 0x10, 0x10, 0xF0, 0x80, 0x80, 0x80, 0x80, 0xF0];\npub const SCHIPFONT_3: [u8; 0x0A] = [0xF0, 0x10, 0x10, 0x10, 0xF0, 0x10, 0x10, 0x10, 0x10, 0xF0];\npub const SCHIPFONT_4: [u8; 0x0A] = [0x90, 0x90, 0x90, 0x90, 0xF0, 0x10, 0x10, 0x10, 0x10, 0x10];\npub const SCHIPFONT_5: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x10, 0x10, 0x10, 0x10, 0xF0];\npub const SCHIPFONT_6: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x90, 0x90, 0x90, 0x90, 0xF0];\npub const SCHIPFONT_7: [u8; 0x0A] = [0xF0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10];\npub const SCHIPFONT_8: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0xF0, 0x90, 0x90, 0x90, 0x90, 0xF0];\npub const SCHIPFONT_9: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0xF0, 0x10, 0x10, 0x10, 0x10, 0xF0];\npub const SCHIPFONT_A: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0xF0, 0x90, 0x90, 0x90, 0x90, 0x90];\npub const SCHIPFONT_B: [u8; 0x0A] = [0xE0, 0x90, 0x90, 0x90, 0xE0, 0x90, 0x90, 0x90, 0x90, 0xE0];\npub const SCHIPFONT_C: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF0];\npub const SCHIPFONT_D: [u8; 0x0A] = [0xE0, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xE0];\npub const SCHIPFONT_E: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80, 0x80, 0xF0];\npub const SCHIPFONT_F: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80, 0x80, 0x80];\n\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","lib.rs"],"content":"pub mod chip8 {\n pub mod video;\n pub mod sound_timer;\n pub mod delay_timer;\n pub mod keypad;\n pub mod computer;\n pub mod system_memory;\n pub mod instructions;\n pub mod cpu_states;\n pub mod util;\n pub mod registers;\n\n pub mod stack;\n pub mod computer_manager;\n pub mod quirk_modes;\n}\n\npub mod constants;","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","tests","computer_tests.rs"],"content":"use std::path::Path;\nuse std::time::Instant;\nuse gemma::chip8::computer::Chip8Computer;\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\nuse gemma::chip8::quirk_modes::QuirkMode;\nuse gemma::chip8::quirk_modes::QuirkMode::{Chip8, SChipModern, XOChip};\nuse gemma::chip8::registers::Chip8Registers;\nuse gemma::constants::{CHIP8_VIDEO_MEMORY, TESTS_ROOT};\n\n#[test]\nfn smoke() {\n assert!(true)\n}\n\nfn load_result(to_load: \u0026str) -\u003e String {\n std::fs::read_to_string(format!(\"../resources/test/{}\", to_load)).unwrap()\n}\n\nfn load_rom(to_load: \u0026str) -\u003e Vec\u003cu8\u003e {\n std::fs::read(format!(\"../resources/roms/{}\", to_load)).unwrap()\n}\n\n#[test]\nfn reset_clears_video() {\n let mut x = Chip8Computer::new();\n\n for i in 0..CHIP8_VIDEO_MEMORY {\n x.video_memory.poke(i as u16, i % 2 == 0);\n }\n\n x.reset(gemma::chip8::quirk_modes::QuirkMode::Chip8);\n x.video_memory.reset();\n\n assert_eq!(x.dump_video_to_string(), x.video_memory.format_as_string());\n\n for i in 0..CHIP8_VIDEO_MEMORY {\n assert!(!x.video_memory.peek(i as u16));\n }\n}\n\n#[test]\nfn level1_test() {\n let mut x = Chip8Computer::new();\n let level_1_rom = load_rom(\"1-chip8-logo.ch8\");\n x.load_bytes_to_memory(0x200, \u0026level_1_rom);\n\n // run for 0x40 cycles\n while x.num_cycles \u003c 0x40 {\n x.step_system();\n }\n assert_eq!(\n x.dump_video_to_string(),\n load_result(\"gemma_integration_level_1_test.asc\")\n );\n}\n\n#[test]\nfn level2_test() {\n let mut x = Chip8Computer::new();\n // Load the IBM rom and run it.\n // it takes 39 cycles to get to the end so lets run it 40.\n let test_rom_to_run = load_rom(\"2-ibm-logo.ch8\");\n x.load_bytes_to_memory(0x200, \u0026test_rom_to_run);\n for _ in 0..40 {\n x.step_system();\n }\n // ...then verify that the current video memory of the chip-8\n // simulator matches what we expect it to be\n\n assert_eq!(\n x.dump_video_to_string(),\n load_result(\"gemma_integration_ibm_rom_output.asc\")\n );\n}\n\n#[test]\nfn level3_test() {\n let mut x = Chip8Computer::new();\n\n x.load_bytes_to_memory(0x200, \u0026load_rom(\"3-corax+.ch8\"));\n for i in 0..0x180 {\n x.step_system();\n }\n\n assert_eq!(\n x.dump_video_to_string(),\n load_result(\"gemma_integration_corax_plus.asc\")\n );\n}\n\n#[test]\nfn rps_test() {\n let mut x = Chip8Computer::new();\n x.load_bytes_to_memory(0x200, \u0026load_rom(\"RPS.ch8\"));\n for _ in 0..0xF0 {\n x.step_system();\n }\n assert_eq!(\n x.dump_video_to_string(),\n load_result(\"gemma_integration_rps_stage1.asc\")\n );\n x.keypad.push_key(0x01);\n for _ in 0..0x200 {\n x.step_system();\n }\n assert_eq!(\n x.dump_video_to_string(),\n load_result(\"gemma_integration_rps_stage2.asc\")\n );\n}\n\n#[test]\nfn level4_test() {\n // flags\n let mut x = Chip8Computer::new();\n x.load_bytes_to_memory(0x200, \u0026load_rom(\"4-flags.ch8\"));\n for _ in 0..0x400 {\n x.step_system();\n }\n\n assert_eq!(\n x.dump_video_to_string(),\n load_result(\"gemma_integration_flags.asc\")\n );\n}\n\n#[test]\nfn registers_equality() {\n let data_set: Vec\u003c(Chip8Registers, Chip8Registers, bool)\u003e = vec![\n (Chip8Registers::default(), Chip8Registers::default(), true),\n (Chip8Registers {\n registers: [0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,],\n i_register: 0,\n pc: 0,\n },\n Chip8Registers {\n registers:[0x01, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,],\n i_register: 0,\n pc: 0,\n }, false)\n ];\n\n for (first, second, matches) in data_set.iter() {\n assert_eq!(first == second, *matches)\n }\n}\n\n#[test]\nfn default_test() {\n let new_manager = Chip8ComputerManager::default();\n\n assert_eq!(new_manager.core_should_run, false);\n assert_eq!(new_manager.num_cycles(), 0);\n assert_eq!(new_manager.quirks_mode(), Chip8);\n}\n\n#[test]\nfn quirks_mode_test() {\n let mut new_manager = Chip8ComputerManager::default();\n\n assert_eq!(new_manager.quirks_mode(), Chip8);\n\n new_manager.reset(QuirkMode::XOChip);\n\n assert_eq!(new_manager.quirks_mode(), XOChip);\n\n new_manager.reset(QuirkMode::SChipModern);\n\n assert_eq!(new_manager.quirks_mode(), SChipModern);\n\n new_manager.reset(Chip8);\n\n assert_eq!(new_manager.quirks_mode(), Chip8);\n}\n\n#[test]\nfn load_rom_allows_starting() {\n let mut new_manager = Chip8ComputerManager::default();\n assert_eq!(new_manager.core_should_run, false);\n let p = format!(\"{}/../resources/test/roms/1-chip8-logo.ch8\" , std::env::current_dir().unwrap().display());\n let full_path = Path::new(p.as_str());\n new_manager.load_new_program_from_disk_to_system_memory(full_path);\n assert_eq!(new_manager.core_should_run, true)\n}\n\n#[test]\nfn reset_clears_run_state() {\n let mut new_manager = Chip8ComputerManager::default();\n let p = format!(\"{}/../resources/test/roms/1-chip8-logo.ch8\", std::env::current_dir().unwrap().display());\n new_manager.load_new_program_from_disk_to_system_memory(Path::new(p.as_str()));\n new_manager.reset(QuirkMode::Chip8);\n assert_eq!(new_manager.core_should_run, false);\n}\n","traces":[{"line":15,"address":[462224],"length":1,"stats":{"Line":1}},{"line":16,"address":[462251,462373],"length":1,"stats":{"Line":2}},{"line":19,"address":[462592],"length":1,"stats":{"Line":4}},{"line":20,"address":[462619,462741],"length":1,"stats":{"Line":8}}],"covered":4,"coverable":4},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","tests","state_tests.rs"],"content":"use std::fs;\nuse std::io;\nuse flate2::write::GzDecoder;\nuse gemma::chip8::computer::Chip8Computer;\nuse std::io::prelude::*;\n\nfn load_result(to_load: \u0026str) -\u003e String {\n let full_path = format!(\"{}/../resources/test/state/{}\", std::env::current_dir().unwrap().display(), to_load);\n println!(\"CURRENT DIR: {:?}\", std::env::current_dir());\n println!(\"Loading state =\u003e (([{}]))\", full_path);\n std::fs::read_to_string(full_path).unwrap()\n}\n\n// fn load_compressed_result(file_path: \u0026str) -\u003e io::Result\u003cString\u003e {\n// // Load the compressed file contents\n// let compressed_data = fs::read(file_path)?;\n//\n// // Create a GzDecoder to uncompress the data\n// let mut decoder = GzDecoder::new(\u0026mut compressed_data[..]);\n// let mut decompressed_data = String::new();\n//\n// // Read the decompressed data directly into a String\n// decoder.read_to_string(\u0026mut decompressed_data)?;\n//\n// Ok(decompressed_data)\n// }\n\nfn load_rom(to_load: \u0026str) -\u003e Vec\u003cu8\u003e {\n fs::read(format!(\"resources/test/roms/{}\", to_load)).unwrap()\n}\n\n#[test]\n#[ignore]\nfn serialization_round_trip() {\n let original_computer = Chip8Computer::new();\n let expected_json = load_result(\"smoke_001_round_trip_serialize_deserialize.json\");\n\n // Serialize the Chip8Computer instance\n let serialized = serde_json::to_string(\u0026original_computer).expect(\"Serialization failed\");\n\n // Compare the serialized output to the expected JSON\n println!(\"Serialized Output: [{}]\", serialized);\n assert_eq!(\n serialized.trim(),\n expected_json.trim(),\n \"Serialized output does not match expected JSON\"\n );\n\n // Deserialize back to Chip8Computer and assert equality\n let deserialized_computer: Chip8Computer =\n serde_json::from_str(\u0026serialized).expect(\"Deserialization failed\");\n assert_eq!(\n deserialized_computer, original_computer,\n \"Deserialized instance does not match the original\"\n );\n}\n//\n// #[test]\n// fn computer_001_system_zero_state() {\n// let x = Chip8Computer::new();\n// let expected_string = load_compressed_result(\"smoke_002_round_trip_serialize_deserialize.tflt\").unwrap();\n// let serialized = serde_json::to_string(\u0026x).unwrap();\n// assert_eq!(serialized, expected_string);\n// }\n//\n","traces":[{"line":7,"address":[700144,701386],"length":1,"stats":{"Line":0}},{"line":8,"address":[700764,700401,700171],"length":1,"stats":{"Line":0}},{"line":9,"address":[700772,700868],"length":1,"stats":{"Line":0}},{"line":10,"address":[700996],"length":1,"stats":{"Line":0}},{"line":11,"address":[701089],"length":1,"stats":{"Line":0}},{"line":28,"address":[701440],"length":1,"stats":{"Line":0}},{"line":29,"address":[701467,701589],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":7},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","tests","unit_tests.rs"],"content":"use std::fs::File;\nuse std::io::Read;\nuse gemma::chip8::computer::Chip8Computer;\nuse gemma::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction;\nuse gemma::chip8::delay_timer::DelayTimer;\nuse gemma::chip8::instructions::Chip8CpuInstructions;\nuse gemma::chip8::instructions::Chip8CpuInstructions::JPX;\nuse gemma::chip8::keypad::Keypad;\nuse gemma::chip8::quirk_modes::QuirkMode::{self, Chip8, SChipModern, XOChip};\nuse gemma::chip8::registers::Chip8Registers;\nuse gemma::chip8::sound_timer::SoundTimer;\nuse gemma::chip8::stack::Chip8Stack;\nuse gemma::chip8::system_memory::Chip8SystemMemory;\nuse gemma::chip8::util::InstructionUtil;\nuse gemma::chip8::video::{Chip8Video, Chip8VideoModes};\nuse gemma::constants::*;\nuse log::debug;\nuse rand::random;\nuse serde::Serialize;\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\n\nconst TEST_OUTPUT_SAMPLE_DIR: \u0026str = \"../resources/test/\";\n\nfn read_test_result(suffix: \u0026str) -\u003e String {\n println!(\"SITTING IN {:?}\", std::env::current_dir());\n println!(\"ATTEMPT TO READ RESULT {suffix}\");\n std::fs::read_to_string(TEST_OUTPUT_SAMPLE_DIR.to_owned() + suffix).unwrap()\n}\n\n#[test]\nfn smoke() {\n assert!(true)\n}\n\n#[test]\n#[ignore]\nfn decoder_test_invalid_instructions() {\n let invalid_to_encode = [\n 0x5ab1, 0x5abf, 0x8ab8, 0x8abd, 0x8abf, 0x9ab1, 0x9abf, 0xea9d, 0xea9f, 0xeaa0, 0xeaa2,\n 0xf006, 0xf008,\n ];\n\n for i in invalid_to_encode {\n assert_eq!(Chip8CpuInstructions::decode(i, \u0026Chip8).encode(), XXXXERRORINSTRUCTION_ENCODED);\n assert!(matches!(\n Chip8CpuInstructions::decode(i, \u0026Chip8),\n Chip8CpuInstructions::XXXXERRORINSTRUCTION\n ));\n }\n}\n\n/// START OF THE EXECUTION TESTS\n#[test]\nfn instruction_tests() {\n // 0x0nnn Exit to System Call\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::SYS(0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::SYS(0xFA0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0xFA0);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::SYS(0x0AF).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x0AF);\n\n // 0x1nnn Jump to Address\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::JPA(0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::JPA(0xABC).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0xABC);\n\n // 0x6xkk Set Vx = kk\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::LDR(1, 0x12).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0x12);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::LDR(2, 0x21).execute(\u0026mut x);\n assert_eq!(x.registers.peek(2), 0x21);\n\n // 0x3xkk Skip next instruction if Vx = kk.\n // The interpreter compares register Vx to kk,\n // and if they are equal, increments the program counter by 2.\n\n // test setup: Load value 0x84 into V1\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0x84);\n Chip8CpuInstructions::SEX(1, 0x48).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0x84);\n Chip8CpuInstructions::SEX(1, 0x84).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204);\n\n // 0x4xkk Skip next instruction if Vx != kk\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x84);\n x.registers.poke(0x2, 0x84);\n // skip, compare 0x84 to 0x84\n Chip8CpuInstructions::SEY(0x1, 0x2).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x84);\n x.registers.poke(0x2, 0x48);\n Chip8CpuInstructions::SEY(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n\n // 0x8xy0 Set value of Vy in Vx\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x01);\n x.registers.poke(0x02, 0x02);\n Chip8CpuInstructions::LDRY(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0x02);\n\n // 0x8xy1 Set Vx = Vx OR Vy\n // 0b0101 0000 (0x50)\n // | 0b0000 1010 (0x0A)\n // 0b0101 1010 (0x5A)\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0b01010000);\n x.registers.poke(0x02, 0b00001010);\n Chip8CpuInstructions::OR(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0b01011010);\n\n // 0x8xy2 Set Vx = Vx AND Vy\n // 0b1111 1100 (0xFC)\n // \u0026 0b1100 1010 (0xCA)\n // 0b1100 1000 (0xC8)\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0xFC);\n x.registers.poke(0x02, 0xCA);\n Chip8CpuInstructions::AND(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0xC8);\n\n // 0x8xy3 Set Vx = Vx XOR Vy\n // 0b1111 1100 (0xFC)\n // ^ 0b1100 1010 (0xCA)\n // 0b0011 0110 (0x36)\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0b11111100);\n x.registers.poke(0x02, 0b11001010);\n Chip8CpuInstructions::ORY(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0b00110110);\n\n // 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)\n // T1 T2: Judgement Test\n // 0x01 0xFF\n // + 0x01 0x01\n // 0x02 F0 0x00 F1\n let mut x = Chip8Computer::new();\n x.registers.poke(0x0f, 00);\n x.registers.poke(0x01, 0x01);\n x.registers.poke(0x02, 0x01);\n Chip8CpuInstructions::ADDR(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0xf), 0x00);\n assert_eq!(x.registers.peek(0x01), 0x02);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x0f, 0x00);\n x.registers.poke(0x01, 0xff);\n x.registers.poke(0x02, 0x01);\n Chip8CpuInstructions::ADDR(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0xf), 1);\n assert_eq!(x.registers.peek(1), 0);\n\n /*\n Set Vx = Vx SHR 1.\n\n If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0. Then Vx is divided by 2.\n */\n let mut x = Chip8Computer::new();\n x.registers.poke(0x0f, 0x00);\n x.registers.poke(0x01, 0b00001000);\n x.registers.poke(0x02, 0b00000000);\n Chip8CpuInstructions::SHR(0x1, 0x2).execute(\u0026mut x); // 0b0000 0010 (0x02) (Not Set)\n assert_eq!(x.registers.peek(1), 0b00000100);\n assert_eq!(x.registers.peek(0xf), 0);\n\n x = Chip8Computer::new();\n x.registers.poke(0x0f, 0x00);\n x.registers.poke(0x01, 0b00001001);\n Chip8CpuInstructions::SHR(0x1, 0x2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0b00000100);\n assert_eq!(x.registers.peek(0xf), 1);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::LDIA(0x123).execute(\u0026mut x);\n assert_eq!(x.registers.peek_i(), 0x123);\n assert_eq!(x.registers.peek_pc(), 0x202);\n}\n\n#[test]\nfn jp_v0addr_test() {\n let mut x = Chip8Computer::new();\n /// jump to I + nnn\n x.registers.poke(0x0, 0xff);\n Chip8CpuInstructions::JPI(0x100).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x1FF);\n}\n\n#[test]\nfn cls_test() {\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::CLS.execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n for i in 0..CHIP8_VIDEO_MEMORY {\n assert!(!x.video_memory.peek(i as u16));\n }\n // draw some thing to the video memory\n x.video_memory.poke(0x01, true);\n x.video_memory.poke(0x03, true);\n x.video_memory.poke(0x05, true);\n\n Chip8CpuInstructions::CLS.execute(\u0026mut x);\n\n for i in 0..CHIP8_VIDEO_MEMORY {\n assert!(!x.video_memory.peek(i as u16));\n }\n}\n\n#[test]\nfn skip_next_instruction_ne_text() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xf0);\n Chip8CpuInstructions::SNEB(0x1, 0x0f).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xf0);\n Chip8CpuInstructions::SNEB(0x1, 0xf0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n}\n\n#[test]\nfn addivx_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke_i(0xabc);\n x.registers.poke(0x0, 0x10);\n Chip8CpuInstructions::ADDI(0x0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_i(), 0xacc);\n}\n\n#[test]\nfn ldstvt_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xf0);\n Chip8CpuInstructions::LDIS(0x01).execute(\u0026mut x);\n assert_eq!(x.sound_timer.current(), 0xf0);\n x.sound_timer.tick();\n x.sound_timer.tick();\n x.sound_timer.tick();\n assert_eq!(x.sound_timer.current(), 0xed);\n}\n\n#[test]\nfn rnd_vx_byte_text() {\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::RND(0x1, 0x0f).execute(\u0026mut x);\n let new_value = x.registers.peek(0x1);\n assert!(new_value \u003c 0x10);\n}\n\n#[test]\nfn add_vx_byte_test() {\n let mut x = Chip8Computer::new();\n // set a value in the register\n x.registers.poke(0x01, 0xab);\n // add 0x10 to register\n Chip8CpuInstructions::ADD(0x1, 0x10).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0xbb);\n}\n\n#[test]\nfn sub_vx_vy_test() {\n let mut x = Chip8Computer::new();\n // load values in 2 registers\n x.registers.poke(0x1, 0x10);\n x.registers.poke(0x2, 0x08);\n Chip8CpuInstructions::SUB(0x1, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0xf), 1);\n assert_eq!(x.registers.peek(0x1), 0x8);\n assert_eq!(x.registers.peek_pc(), 0x202);\n}\n\n#[test]\nfn sne_vx_vy_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0x10);\n x.registers.poke(0x2, 0x10);\n Chip8CpuInstructions::SNEY(0x1, 0x2).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0x10);\n x.registers.poke(0x2, 0x00);\n Chip8CpuInstructions::SNEY(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204)\n}\n\n#[test]\nfn ld_dt_vx_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0x10);\n Chip8CpuInstructions::LDD(0x1).execute(\u0026mut x);\n assert_eq!(x.delay_timer.current(), 0x10);\n for _ in 0..0x20 {\n x.delay_timer.tick();\n }\n assert_eq!(x.delay_timer.current(), 0);\n}\n\n#[test]\nfn ld_vx_dt_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xf0);\n Chip8CpuInstructions::LDD(0x1).execute(\u0026mut x);\n x.delay_timer.tick();\n x.delay_timer.tick();\n x.delay_timer.tick();\n assert_eq!(x.delay_timer.current(), 0xed);\n}\n\n#[test]\nfn subn_vx_vy_test() {\n // This instruction subtracts the value in\n // register Vx from the value in register Vy and stores the result in register Vx.\n // The subtraction is performed as follows: Vx = Vy - Vx. If Vy is less than Vx,\n // the result will wrap around (due to the 8-bit nature of the registers).\n // The carry flag (VF) is set to 1 if there is no borrow (i.e., Vy is greater\n // than or equal to Vx), and it is set to 0 if there is a borrow.\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xa0);\n x.registers.poke(0x2, 0xab);\n Chip8CpuInstructions::SUBC(0x1, 0x2).execute(\u0026mut x);\n // expect the result to be 0x0b\n assert_eq!(x.registers.peek(0x1), 0x0b);\n // expect the vf register to be set to 1 as there was overflow\n assert_eq!(x.registers.peek(0xf), 0x1);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0xab);\n x.registers.poke(0x02, 0xa0);\n Chip8CpuInstructions::SUBC(0x1, 0x2).execute(\u0026mut x);\n\n // expect the result to be 11110101, -0xB, -11, 245, 0xF5\n assert_eq!(x.registers.peek(0x1), 0xf5);\n assert_eq!(x.registers.peek(0xf), 0x0);\n\n // 8xyE - SHL Vx {, Vy}\n // Set Vx = Vx SHL 1.\n //\n // If the most-significant bit of Vx is 1, then VF is set to 1, otherwise to 0. Then Vx is multiplied by 2.\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0b00100000);\n Chip8CpuInstructions::SHL(0x1, 0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x1), 0b01000000);\n assert_eq!(x.registers.peek(0xf), 0x0);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0b10101010);\n Chip8CpuInstructions::SHL(0x1, 0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x1), 0b01010100);\n assert_eq!(x.registers.peek(0xf), 0x1);\n\n // Fx29 - LD F, Vx\n // Set I = location of sprite for digit Vx.\n //\n // The value of I is set to the location for the hexadecimal sprite corresponding to the value of Vx. See section 2.4, Display, for more information on the Chip-8 hexadecimal font.\n let mut x = Chip8Computer::new();\n // target_sprite = 2\n // target_offset = 0x5\n x.registers.poke(0x1, 0x2);\n Chip8CpuInstructions::LDFX(0x1).execute(\u0026mut x);\n\n assert_eq!(x.registers.peek_i(), 10);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x06);\n Chip8CpuInstructions::LDFX(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_i(), 30);\n\n // Fx33 - LD B, Vx\n // Store BCD representation of Vx in memory locations I, I+1, and I+2.\n //\n // The interpreter takes the decimal value of Vx, and places the hundreds digit\n // in memory at location in I, the tens digit at location I+1,\n // and the ones digit at location I+2.\n let mut x = Chip8Computer::new();\n\n // load the value 123 (0x7b)\n x.registers.poke(0x1, 0x7b);\n x.registers.poke_i(0x500);\n Chip8CpuInstructions::BCD(0x1).execute(\u0026mut x);\n assert_eq!(x.memory.peek(0x500), 0x1);\n assert_eq!(x.memory.peek(0x501), 0x2);\n assert_eq!(x.memory.peek(0x502), 0x3);\n\n // Store registers V0 through Vx in memory starting at location I.\n //\n // The interpreter copies the values of registers V0 through Vx into memory,\n // starting at the address in I.\n let mut x = Chip8Computer::new();\n\n // Load Registers.\n let to_load = [0xab, 0xba, 0xca, 0xca, 0xbe, 0xef];\n for (idx, val) in to_load.iter().enumerate() {\n x.registers.poke(idx as u8, *val);\n }\n x.registers.poke_i(0x500);\n\n Chip8CpuInstructions::LDIX(to_load.len() as u8).execute(\u0026mut x);\n\n // Verify the values are in memory from 0x500 to 0x507\n for (idx, value) in to_load.iter().enumerate() {\n assert_eq!(x.memory.peek(0x500 + idx as u16), *value);\n }\n\n // Read registers V0 through Vx from memory starting at location I.\n //\n // The interpreter reads values from memory starting at location I into registers V0 through Vx.\n\n let mut x = Chip8Computer::new();\n\n let base_offset = 0x500;\n let to_load = [0xab, 0xba, 0xca, 0xca, 0xbe, 0xef];\n\n // start by setting values in memory\n for (idx, memory) in to_load.iter().enumerate() {\n let target_address = base_offset + idx;\n let target_value = *memory;\n x.memory.poke(target_address as u16, target_value);\n }\n // where to load from\n x.registers.poke_i(0x500);\n // how much to load\n x.registers.poke(0x6, to_load.len() as u8);\n\n // then copying them values memory to registers\n Chip8CpuInstructions::LDRI(0x6).execute(\u0026mut x);\n\n // now check that we have the right values in our registers\n for (idx, value) in to_load.iter().enumerate() {\n assert_eq!(x.registers.peek(idx as u8), *value);\n }\n\n // ExA1 - SKNP Vx\n // Skip next instruction if key with the value of Vx is not pressed.\n //\n // Checks the keyboard,\n // and if the key corresponding to the value of Vx is currently in the up position,\n // PC is increased by 2.\n let mut x = Chip8Computer::new();\n x.keypad.push_key(0x5);\n x.registers.poke(0x1, 0x5);\n Chip8CpuInstructions::SKNP(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n x.keypad.release_key(0x5);\n Chip8CpuInstructions::SKNP(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x206);\n\n // Ex9E - SKP Vx\n // Skip next instruction if key with the value of Vx is pressed.\n //\n // Checks the keyboard, and if the key corresponding to the value of Vx is currently in the down position, PC is increased by 2.\n let mut x = Chip8Computer::new();\n x.keypad.push_key(0x5);\n x.registers.poke(0x1, 0x5);\n Chip8CpuInstructions::SKP(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204);\n\n x.keypad.release_key(0x5);\n Chip8CpuInstructions::SKP(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x206);\n}\n\nfn draw_nibble_vx_vy_n_test_hd() {\n let mut x = Chip8Computer::new();\n\n let x_register = 0x01;\n let x_offset = 0x03;\n let y_register = 0x02;\n let y_offset = 0x04;\n let char_offset = 0x100;\n x.registers.poke(x_register, x_offset);\n x.registers.poke(y_register, y_offset);\n x.video_memory.set_highres();\n x.registers.poke_i(char_offset);\n Chip8CpuInstructions::DRW(x_register, y_register, 0).execute(\u0026mut x);\n\n println!(\"[[{}]]\", x.video_memory.format_as_string());\n\n assert_eq!(read_test_result(\"\"), x.video_memory.format_as_string());\n}\n\n#[test]\nfn draw_nibble_vx_vy_n_test_sd() {\n let mut x = Chip8Computer::new();\n let x_register = 0x1;\n let y_register = 0x2;\n let x_offset = 1;\n let y_offset = 2;\n let char_offset = 0x0A;\n\n // now lets set the X and Y to 1,2\n x.registers.poke(x_register, x_offset);\n x.registers.poke(y_register, y_offset);\n x.registers.poke_i(char_offset);\n // we are using 5 rows.\n Chip8CpuInstructions::DRW(x_register, y_register, 5).execute(\u0026mut x);\n\n // now check that video memory has the values at\n // 1,2-\u003e1,9\n // 2,2-\u003e2,9\n // 3,2-\u003e3,9\n // 4,2-\u003e4,9\n // 5,2-\u003e5,9\n // let byte_to_check = CHIP8FONT_0[0];\n for row_in_sprite in 0..5 {\n let row_data = CHIP8FONT_2[row_in_sprite];\n for bit_in_byte in 0..8 {\n let data_offset =\n (x_offset as u16 + row_in_sprite as u16) * 64 + (bit_in_byte + y_offset) as u16;\n let real_bit_in_byte = 7 - bit_in_byte;\n let shifted_one = 0x01 \u003c\u003c real_bit_in_byte;\n let one_shift_set = (shifted_one \u0026 row_data) \u003e 0;\n debug!(\"ROWDATA = \\t\\t[{row_data:08b}]\\tBIT IN BYTE = \\t[{bit_in_byte}]\\tONE_SHIFT_SET = [{one_shift_set}]\\tSHIFTED ONE = [{shifted_one:08b}]\");\n debug!(\"DATA_OFFSET FOR SOURCE DATA {}x{} is {} / offset by {}x{} and should be {} working with byte {:08b}\",\n bit_in_byte, row_in_sprite, data_offset, x_offset, y_offset, one_shift_set, row_data);\n }\n }\n}\n\n#[test]\nfn sub_test() {\n // 2nnn\n // Call a subroutine at 2nnn\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::CALL(0x124).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x124);\n assert_eq!(x.stack.depth(), 1);\n Chip8CpuInstructions::CALL(0x564).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x564);\n assert_eq!(x.stack.depth(), 2);\n\n // SETUP\n // Return from a subroutine.\n let mut x = Chip8Computer::new();\n x.stack.push(\u00260x132);\n x.stack.push(\u00260xabc);\n // EXECUTE\n Chip8CpuInstructions::RET.execute(\u0026mut x);\n // VERIFY\n assert_eq!(x.registers.peek_pc(), 0xabc);\n assert_eq!(x.stack.depth(), 1);\n // EXECUTE\n Chip8CpuInstructions::RET.execute(\u0026mut x);\n // VERIFY\n assert_eq!(x.registers.peek_pc(), 0x132);\n assert_eq!(x.stack.depth(), 0);\n}\n\n#[test]\nfn ldvxk_test() {\n // SETUP\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x01);\n Chip8CpuInstructions::LDRK(0x1).execute(\u0026mut x);\n assert!(matches!(\n x.state,\n gemma::chip8::cpu_states::Chip8CpuStates::WaitingForKey\n ));\n}\n\n#[test]\nfn series8xy4_corex_tests() {\n /// 8xy4\n /// Set Vx = Vx + Vy\n /// Set VF=1 if Carry\n ///\n // 1 + 1\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x01);\n x.registers.poke(0x02, 0x01);\n Chip8CpuInstructions::ADDR(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0x02);\n assert_eq!(x.registers.peek(0x0f), 0x00);\n\n // 255+1\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0xff);\n x.registers.poke(0x02, 0x01);\n Chip8CpuInstructions::ADDR(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0x00);\n assert_eq!(x.registers.peek(0x0f), 0x01);\n\n // 128+192\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 128);\n x.registers.poke(0x02, 192);\n Chip8CpuInstructions::ADDR(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 64);\n assert_eq!(x.registers.peek(0x0f), 1);\n\n // 8xy6 - SHR Vx {, Vy}\n // Set Vx = Vx SHR 1.\n //\n // If the least-significant bit of Vx is 1, then VF is set to 1,\n // otherwise 0. Then Vx is divided by 2.\n let mut x = Chip8Computer::new();\n // 0b10101010 -\u003e 0b01010101\n x.registers.poke(0x01, 0b10101010);\n x.registers.poke(0x0f, 0x0);\n\n Chip8CpuInstructions::SHL(0x01, 0x00).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0b01010100);\n assert_eq!(x.registers.peek(0x0f), 1);\n\n Chip8CpuInstructions::SHL(0x01, 0x00).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0b10101000);\n assert_eq!(x.registers.peek(0x0f), 0x00);\n\n Chip8CpuInstructions::SHL(0x01, 0x00).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0b01010000);\n assert_eq!(x.registers.peek(0x0f), 0x01);\n\n Chip8CpuInstructions::SHL(0x01, 0x00).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0b10100000);\n assert_eq!(x.registers.peek(0x0f), 0x00);\n\n Chip8CpuInstructions::SHL(0x01, 0x00).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0b01000000);\n assert_eq!(x.registers.peek(0x0f), 0x01);\n}\n\n#[test]\nfn random_produces_different_numbers() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x00);\n let first_number = Chip8CpuInstructions::RND(0x01, 0xff)\n .execute(\u0026mut x)\n .registers\n .peek(0x01);\n let second_number = Chip8CpuInstructions::RND(0x01, 0xff)\n .execute(\u0026mut x)\n .registers\n .peek(0x01);\n assert_ne!(first_number, second_number);\n}\n\n#[test]\nfn delay_timer_ticks_reduce_time() {\n let mut st = DelayTimer::new();\n st.set_timer(100);\n st.tick();\n st.tick();\n st.tick();\n assert_eq!(st.current(), 97);\n}\n\n#[test]\nfn delay_timer_out_of_ticks_works() {\n let mut st = DelayTimer::new();\n st.set_timer(0);\n st.tick();\n st.tick();\n st.tick();\n assert_eq!(st.current(), 0);\n}\n\n#[test]\nfn keypad_keys_check() {\n let mut k = Keypad::new();\n\n for i in 0..16 {\n assert!(!k.key_state(i));\n }\n\n // press a key\n k.push_key(1);\n k.push_key(2);\n assert!(k.pressed(1));\n assert!(k.pressed(2));\n k.release_key(1);\n assert!(k.released(1));\n}\n\n#[test]\nfn keypad_string_format_test() {\n let k = Keypad::new();\n\n assert_eq!(\n k.format_as_string(),\n read_test_result(\"gemma_keypad_string_result.asc\")\n );\n}\n\n#[test]\nfn register_rw_test() {\n let mut x = Chip8Registers::default();\n x.poke(0x0, 0xff);\n x.poke(0x1, 0xab);\n assert_eq!(x.peek(0x0), 0xff);\n assert_eq!(x.peek(0x1), 0xab);\n}\n\n#[test]\nfn pc_test() {\n let mut x = Chip8Registers::default();\n x.set_pc(0x300);\n assert_eq!(x.peek_pc(), 0x300);\n}\n\n#[test]\n#[should_panic]\nfn invalid_register() {\n let mut x = Chip8Registers::default();\n x.poke(0x10, 0xff);\n}\n\n#[test]\nfn format_as_string_looks_right() {\n let mut x = Chip8Registers::default();\n for i in 0..0x10 {\n x.registers[i] = i as u8;\n }\n x.pc = 0xabc;\n x.i_register = 0xcab;\n let result_string = x.format_as_string();\n assert_eq!(result_string, String::from(\"Vx: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07\\n 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f\\nI: 0x0cab\\tPC: 0x0abc\"));\n}\n\n#[test]\nfn reset_clears_registers() {\n let mut x = Chip8Registers::default();\n\n for register in 0..0x10 {\n x.poke(register, random::\u003cu8\u003e());\n }\n x.reset();\n for register in 0..0x10 {\n assert_eq!(x.peek(register), 0x00);\n }\n}\n\n#[test]\nfn sound_timer_ticks_reduce_time() {\n let mut st = SoundTimer::new();\n st.set_timer(100);\n st.tick();\n st.tick();\n st.tick();\n assert_eq!(st.current(), 97);\n}\n\n#[test]\nfn sound_timer_out_of_ticks_works() {\n let mut st = SoundTimer::new();\n st.set_timer(0);\n st.tick();\n st.tick();\n st.tick();\n assert_eq!(st.current(), 0);\n}\n\n#[test]\nfn stack_push_pop_test() {\n let mut x = Chip8Stack::new();\n\n // lets see if we can push and pop a bunch\n x.push(\u00260xabcu16);\n x.push(\u00260xcdeu16);\n x.pop();\n assert_eq!(x.depth(), 1);\n}\n\n#[test]\n#[should_panic]\nfn stack_overflow_test() {\n let mut x = Chip8Stack::new();\n for i in 0..17 {\n x.push(\u0026i);\n }\n}\n\n#[test]\n#[should_panic]\nfn stack_underflow_test() {\n let mut x = Chip8Stack::new();\n x.pop();\n}\n\n#[test]\nfn stack_lots_of_subs() {\n let mut x = Chip8Stack::new();\n let stack_contents = [\n 0x123, 0x321, 0xabc, 0xdef, 0xbad, 0xbef, 0xfed, 0xcab, 0xbed, 0xcad, 0xfeb, 0xcab, 0xfff,\n 0x000, 0x001,\n ];\n for i in stack_contents {\n x.push(\u0026i);\n }\n\n assert_eq!(x.depth(), 15);\n\n // up to 50 random loops\n let num_loops: u8 = random::\u003cu8\u003e() % 50;\n for i in 0..num_loops {\n let start_count = x.depth();\n let num_pop = random::\u003cu8\u003e() % x.depth() as u8;\n for current_pop in 0..num_pop {\n x.pop();\n }\n\n let post_pop_count = x.depth();\n assert_eq!(post_pop_count as u8, start_count as u8 - num_pop);\n for current_push in 0..num_pop {\n x.push(\u0026stack_contents[(current_push + post_pop_count as u8) as usize]);\n }\n assert_eq!(x.depth(), 15);\n }\n}\n\n#[test]\nfn video_split_bytes() {\n // from 0xABCD we should have AB high, CD low\n let (low, high) = InstructionUtil::split_bytes(0xabcd);\n assert_eq!(low, 0xAB);\n assert_eq!(high, 0xCD);\n}\n\n#[test]\nfn video_join_bytes() {\n // from 0xAB low and 0xCD high we get 0xABCD\n let merged = InstructionUtil::join_bytes(0xcd, 0xab);\n assert_eq!(merged, 0xcdab);\n}\n\n#[test]\nfn instruction_read_from_instruction() {\n // from 0xABCD\n let source = 0xABCD;\n assert_eq!(InstructionUtil::read_addr_from_instruction(source), 0xBCD);\n assert_eq!(InstructionUtil::read_nibble_from_instruction(source), 0xD);\n assert_eq!(InstructionUtil::read_x_from_instruction(source), 0xB);\n assert_eq!(InstructionUtil::read_y_from_instruction(source), 0xC);\n assert_eq!(InstructionUtil::read_byte_from_instruction(source), 0xCD);\n}\n\n#[test]\nfn instruction_ubln() {\n // from 0xABCD we should see B\n assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0xABCD), 0xB);\n assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0x0123), 0x1);\n assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0x0000), 0x0);\n}\n\n#[test]\nfn instruction_byte_to_bool_changes() {\n assert_eq!(\n InstructionUtil::byte_to_bools(0b00000000),\n [false, false, false, false, false, false, false, false]\n );\n assert_eq!(\n InstructionUtil::byte_to_bools(0b11111111),\n [true, true, true, true, true, true, true, true]\n );\n assert_eq!(\n InstructionUtil::byte_to_bools(0b11001100),\n [false, false, true, true, false, false, true, true]\n );\n assert_eq!(\n InstructionUtil::byte_to_bools(0b11110000),\n [false, false, false, false, true, true, true, true]\n );\n assert_eq!(\n InstructionUtil::bools_to_byte([false, false, false, false, false, false, false, false]),\n 0b00000000\n );\n assert_eq!(\n InstructionUtil::bools_to_byte([true, true, true, true, true, true, true, true]),\n 0b11111111\n );\n assert_eq!(\n InstructionUtil::bools_to_byte([false, false, true, true, false, false, true, true]),\n 0b11001100\n );\n assert_eq!(\n InstructionUtil::bools_to_byte([false, false, false, false, true, true, true, true]),\n 0b11110000\n );\n}\n\nfn real_build_checkboard(in_hd: bool) -\u003e Chip8Video {\n let mut r = Chip8Video::default();\n let (width, height) = if in_hd {\n r.set_highres();\n (SCHIP_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT)\n } else {\n (CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT)\n };\n\n println!(\"BUILDING BOARD WITH SIZE OF {width}x{height}\");\n\n for row in 0..height {\n let data_offset = row * width;\n\n for col in 0..width {\n // XOR row and column indices to alternate in a checkerboard pattern\n let to_poke = (row % 2) ^ (col % 2) == 1;\n let local_offset: u16 = (data_offset + col) as u16;\n\n r.poke(local_offset, to_poke);\n }\n }\n r\n}\n\nfn build_checkboard_hd() -\u003e Chip8Video {\n real_build_checkboard(true)\n}\n\nfn build_checkerboard() -\u003e Chip8Video {\n real_build_checkboard(false)\n}\n\n#[test]\nfn video_default_test() {\n let mut x = Chip8Video::default();\n\n for i in 0..CHIP8_VIDEO_MEMORY {\n assert!(!x.clone().peek(i as u16));\n // then flip the value and test again.\n x.poke(i as u16, true);\n assert!(x.clone().peek(i as u16));\n }\n}\n\n#[test]\nfn video_set_initial_memory_sd() {\n let mut x = Chip8Video::default();\n // let mut initial_memory = [false; CHIP8_VIDEO_MEMORY];\n let mut ws = String::new();\n // set our checkerboard\n for cbr in 0..32 {\n for cbc in 0..64 {\n let dof = cbr * 64 + cbc;\n if (dof as i32 % 2) == 0 {\n x.poke(dof, true);\n ws += \"*\";\n } else {\n ws += \" \";\n }\n }\n ws += \"\\n\";\n }\n assert_eq!(x.format_as_string(), ws);\n}\n\n#[test]\nfn video_poke_byte_test() {\n let to_poke = 0b11001111;\n let mut x = Chip8Video::default();\n x.poke_byte(0x05, to_poke);\n let mut expected = String::new();\n expected = \" ** **** \\n\".to_string();\n for i in 0..31 {\n expected += \u0026*(\" \".repeat(64) + \"\\n\");\n }\n assert_eq!(x.format_as_string(), expected);\n}\n\n#[test]\nfn video_poke_2byte_test() {\n let to_poke: [u8; 2] = [0b11001111, 0b00111100];\n\n let mut x = Chip8Video::default();\n x.poke_2byte(0x00, to_poke);\n\n let mut expected = String::new();\n expected = \"** **** **** \".to_string() + \u0026*\" \".repeat(64 - 16).to_string() + \"\\n\";\n for i in 0..31 {\n expected += \u0026*((*\" \".repeat(64)).to_string() + \"\\n\");\n }\n\n assert_eq!(expected, x.format_as_string());\n}\n\n#[test]\nfn video_poke_multirow_2_byte_sprite() {\n // take 2 rows of 16bits and write them to memory\n}\n\n#[test]\nfn video_cls_stddef() {\n let width = 64;\n let height = 32;\n let initial_memory = vec![];\n let mut ws = String::new();\n let mut set_x = Chip8Video::new(initial_memory.into());\n for cbr in 0..32 {\n ws += \u0026*\" \".repeat(width);\n ws += \"\\n\";\n }\n set_x.cls();\n\n assert_eq!(set_x.format_as_string(), ws);\n}\n\n#[test]\nfn video_poke_byte_test_2() {\n let to_poke = 0b10101010;\n let mut v = Chip8Video::default();\n v.poke_byte(0x00, to_poke);\n assert!(v.clone().peek(0x00));\n assert!(!v.clone().peek(0x01));\n assert!(v.clone().peek(0x02));\n assert!(!v.clone().peek(0x03));\n assert!(v.clone().peek(0x04));\n assert!(!v.clone().peek(0x05));\n assert!(v.clone().peek(0x06));\n assert!(!v.clone().peek(0x07));\n for i in 0x8..CHIP8_VIDEO_MEMORY {\n assert!(!v.clone().peek(i as u16));\n }\n}\n\n#[test]\nfn video_poke_multi_line_test() {\n let mut v = Chip8Video::default();\n let to_poke = [0b00000000, 0b11111111, 0b10101010, 0b01010101];\n\n for (byte_in_set, byte_to_poke) in to_poke.iter().enumerate() {\n let base_offset = byte_in_set * 64;\n v.poke_byte(base_offset as u16, *byte_to_poke);\n }\n\n // row 2 column 1\n {\n assert!(v.clone().peek(0x40));\n assert!(v.clone().peek(0x41));\n assert!(v.clone().peek(0x42));\n assert!(v.clone().peek(0x43));\n assert!(v.clone().peek(0x44));\n assert!(v.clone().peek(0x45));\n assert!(v.clone().peek(0x46));\n assert!(v.clone().peek(0x47));\n\n // row 3 column 1\n assert!(!v.clone().peek(0xC0));\n assert!(v.clone().peek(0xC1));\n assert!(!v.clone().peek(0xC2));\n assert!(v.clone().peek(0xC3));\n assert!(!v.clone().peek(0xC4));\n assert!(v.clone().peek(0xC5));\n assert!(!v.clone().peek(0xC6));\n assert!(v.clone().peek(0xC7));\n }\n}\n\n#[test]\nfn video_moved_poke_test() {\n let mut v = Chip8Video::default();\n let to_poke = [0b00000000, 0b11111111, 0b10101010, 0b01010101];\n\n let x_offset = 20;\n let y_offset = 5;\n\n for (byte_in_set, byte_to_poke) in to_poke.iter().enumerate() {\n let base_offset = (x_offset + byte_in_set) * 64 + y_offset;\n v.poke_byte(base_offset as u16, *byte_to_poke);\n }\n\n let test_offset = (x_offset * 64 + y_offset) as u16;\n assert!(!v.clone().peek(test_offset));\n assert!(!v.clone().peek(test_offset + 1));\n assert!(!v.clone().peek(test_offset + 2));\n assert!(!v.clone().peek(test_offset + 3));\n assert!(!v.clone().peek(test_offset + 4));\n assert!(!v.clone().peek(test_offset + 5));\n assert!(!v.clone().peek(test_offset + 6));\n assert!(!v.clone().peek(test_offset + 7));\n\n let test_offset = test_offset + 0x40;\n assert!(v.clone().peek(test_offset));\n assert!(v.clone().peek(test_offset + 1));\n assert!(v.clone().peek(test_offset + 2));\n assert!(v.clone().peek(test_offset + 3));\n assert!(v.clone().peek(test_offset + 4));\n assert!(v.clone().peek(test_offset + 5));\n assert!(v.clone().peek(test_offset + 6));\n assert!(v.clone().peek(test_offset + 7));\n}\n\n#[test]\nfn video_verify_change_registered() {\n let mut v = Chip8Video::default();\n v.poke(0x01, true);\n v.poke(0x01, true);\n assert!(v.has_frame_changed);\n\n v.start_frame();\n assert!(!v.has_frame_changed);\n}\n\n#[test]\nfn video_write_checkboard() {\n let v = build_checkerboard();\n assert_eq!(\n v.clone().format_as_string(),\n read_test_result(\"test_video_write_checkerboard.asc\")\n );\n}\n\n#[test]\nfn video_zero_test() {\n let mut x = Chip8Video::default();\n\n for (byte_index, data_offset) in (0..=0x100).step_by(0x40).enumerate() {\n x.poke_byte(data_offset as u16, CHIP8FONT_0[byte_index]);\n }\n\n assert_eq!(\n read_test_result(\"test_video_zero.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_multi_sprite_test() {\n let mut x = Chip8Video::default();\n // draw a row of digits 01234567\n let to_draw = [\n CHIP8FONT_0,\n CHIP8FONT_1,\n CHIP8FONT_2,\n CHIP8FONT_3,\n CHIP8FONT_4,\n CHIP8FONT_5,\n CHIP8FONT_6,\n CHIP8FONT_7,\n ];\n for (index, sprite) in to_draw.iter().enumerate() {\n let data_base_offset = index * 0x8;\n for (index, offset) in (0..=0x100).step_by(0x40).enumerate() {\n x.poke_byte((data_base_offset + offset) as u16, sprite[index]);\n }\n }\n\n assert_eq!(\n read_test_result(\"test_multi_sprite.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_reset_test() {\n let mut x = build_checkerboard();\n x.reset();\n assert_eq!(\n x.format_as_string(),\n read_test_result(\"test_reset_clears_video.asc\")\n );\n}\n\n#[test]\nfn video_collision_test() {\n // Setup: Set 0xFF to 0x00 with a new frame ready\n // Action: Run Poke to the same area\n // Test: Verify the 'changed' flag is tripped\n let mut x = Chip8Video::default();\n x.poke_byte(0x00, 0xff);\n x.tick();\n // set the cell thats already set...\n x.poke(0x00, true);\n // it becomes unset and theres a frame changed\n assert!(!x.peek(0x00));\n\n assert!(x.clone().has_frame_changed);\n}\n\n#[test]\nfn video_collision_test2() {\n let mut x = Chip8Video::default();\n x.poke_byte(0x00, 0b11110000);\n assert!(x.has_frame_changed);\n x.tick();\n assert!(!x.has_frame_changed);\n // clear the 'has changed' flag\n\n // now set a no-collision value\n x.poke_byte(0x00, 0b00001111);\n assert!(x.has_frame_changed);\n}\n\n#[test]\nfn video_collision_test3() {\n // draw a couple sprites that do not overlap.\n // goal being drawing without triggering the collision\n // detection.\n let mut x = Chip8Video::default();\n x.poke_byte(0x00, 0b11110000);\n}\n\n#[test]\nfn video_peek_out_of_bounds_doesnt_panic() {\n let x = Chip8Video::default();\n\n let y = x.clone().peek(2049);\n let y = x.clone().peek(0);\n\n // if we got here we didn't panic\n assert!(true);\n}\n\n#[test]\nfn video_scroll_down_1_row_test() {\n let mut x = build_checkerboard();\n x.scroll_down(1);\n assert_eq!(\n read_test_result(\"test_video_scroll_down_1.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_down_10_row_test() {\n let mut x = build_checkerboard();\n x.scroll_down(10);\n assert_eq!(\n read_test_result(\"test_video_scroll_down_10.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_high_res_has_right_resolution() {\n let x = build_checkboard_hd();\n println!(\"[{}]\", x.format_as_string());\n assert_eq!(\n read_test_result(\"test_video_highdef.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_down_1_row_test_schip() {\n let mut x = build_checkboard_hd();\n x.scroll_down(1);\n\n println!(\"[{}]\", x.format_as_string());\n println!(\"[{}]\", read_test_result(\"test_scroll_down_1_hd.asc\"));\n\n assert_eq!(\n read_test_result(\"test_scroll_down_1_hd.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_down_10_row_test_schip() {\n let mut x = build_checkboard_hd();\n x.scroll_down(10);\n assert_eq!(\n read_test_result(\"test_scroll_down_10_hd.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_left_4_row_test_std_def() {\n let mut x = build_checkerboard();\n x.scroll_left();\n assert_eq!(\n read_test_result(\"test_scroll_left_4.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_left_4_row_test_high_def() {\n let mut x = build_checkboard_hd();\n x.scroll_left();\n assert_eq!(\n read_test_result(\"test_scroll_left_4_hd.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_right_4_row_test_std_def() {\n let mut x = build_checkerboard();\n x.scroll_right();\n assert_eq!(\n read_test_result(\"test_scroll_right_4.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_right_4_row_test_high_def() {\n let mut x = build_checkboard_hd();\n x.scroll_right();\n assert_eq!(\n read_test_result(\"test_scroll_right_4_hd.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn instructions_operands_tests() {\n assert_eq!(Chip8CpuInstructions::SYS(0x000).operands(), \"0x0000\");\n assert_eq!(Chip8CpuInstructions::JPI(0x123).operands(), \"0x0123\");\n assert_eq!(Chip8CpuInstructions::JPA(0x234).operands(), \"0x0234\");\n assert_eq!(Chip8CpuInstructions::LDIA(0x345).operands(), \"0x0345\");\n assert_eq!(Chip8CpuInstructions::CALL(0x456).operands(), \"0x0456\");\n}\n\n#[test]\nfn instruction_ena_dis_tests() {\n let mut x = Chip8Computer::new();\n for quirk in [SChipModern, XOChip] {\n x.quirk_mode = quirk;\n assert!(!x.video_memory.is_highres());\n Chip8CpuInstructions::HIGH.execute(\u0026mut x);\n assert!(x.video_memory.is_highres());\n Chip8CpuInstructions::HIGH.execute(\u0026mut x);\n assert!(x.video_memory.is_highres());\n Chip8CpuInstructions::LOW.execute(\u0026mut x);\n assert!(!x.video_memory.is_highres());\n }\n}\n\n#[test]\nfn instruction_test_scrolling_lowres() {\n for quirk in [SChipModern, XOChip] {\n let mut x = Chip8Computer::new();\n x.video_memory = build_checkerboard();\n x.quirk_mode = quirk.clone();\n Chip8CpuInstructions::SCR.execute(\u0026mut x);\n assert_eq!(\n read_test_result(\"test_scroll_right_4.asc\"),\n x.dump_video_to_string()\n );\n\n x = Chip8Computer::new();\n x.video_memory = build_checkerboard();\n x.quirk_mode = quirk.clone();\n Chip8CpuInstructions::SCL.execute(\u0026mut x);\n\n assert_eq!(\n read_test_result(\"test_scroll_left_4.asc\"),\n x.dump_video_to_string()\n );\n\n x = Chip8Computer::new();\n x.video_memory = build_checkerboard();\n x.quirk_mode = quirk.clone();\n Chip8CpuInstructions::SCD(0x01).execute(\u0026mut x);\n assert_eq!(\n read_test_result(\"test_video_scroll_down_1.asc\"),\n x.dump_video_to_string()\n );\n\n x = Chip8Computer::new();\n x.video_memory = build_checkerboard();\n x.quirk_mode = quirk.clone();\n Chip8CpuInstructions::SCD(0xA).execute(\u0026mut x);\n assert_eq!(\n read_test_result(\"test_video_scroll_down_10.asc\"),\n x.dump_video_to_string()\n );\n }\n}\n\n#[test]\nfn computer_dump_keypad_to_string() {\n let mut x = Chip8Computer::new();\n x.keypad.push_key(0x1);\n x.keypad.push_key(0x2);\n assert_eq!(\n read_test_result(\"test_keypad_to_string.asc\"),\n x.dump_keypad_to_string()\n );\n}\n\n#[test]\nfn computer_dump_registers_to_string() {\n let mut x = Chip8Computer::new();\n let values_to_set = [\n 0x0b, 0xad, 0xbe, 0xef, 0xca, 0xb0, 0x7a, 0xc0, 0xca, 0x70, 0xba, 0xdb, 0xed, 0x00, 0x00,\n 0x00,\n ];\n let expected_value = \"Vx: 0x0b 0xad 0xbe 0xef 0xca 0xb0 0x7a 0xc0\\n 0xca 0x70 0xba 0xdb 0xed 0x00 0x00 0x00\\nI: 0x0000\\tPC: 0x0200\";\n\n for i in 0..16 {\n x.registers.poke(i, values_to_set[i as usize]);\n }\n\n // now verify.\n assert_eq!(expected_value, x.dump_registers_to_string());\n}\n\n#[test]\nfn video_scroll_up_tests_sd() {\n let mut x = build_checkerboard();\n let distance = 1u8;\n x.scroll_up(\u0026distance);\n assert_eq!(\n read_test_result(\"test_video_scroll_up_test_sd.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_up_tests_hd() {\n let mut x = build_checkboard_hd();\n let distance = 1u8;\n x.scroll_up(\u0026distance);\n assert_eq!(\n read_test_result(\"test_video_scroll_up_test_hd.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_resolution_changing() {\n let mut x = Chip8Video::default();\n\n x.set_highres();\n assert_eq!(x.get_resolution(), (SCHIP_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT));\n assert!(matches!(\n x.get_screen_resolution(),\n Chip8VideoModes::HighRes\n ));\n x.set_lowres();\n assert_eq!(x.get_resolution(), (CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT));\n assert!(matches!(x.get_screen_resolution(), Chip8VideoModes::LowRes));\n}\n\n#[test]\nfn delay_timer_default() {\n let t = DelayTimer::default();\n assert_eq!(t.current(), 0xff);\n}\n\n#[test]\nfn instruction_jpx() {\n // JPX -\u003e 0xBXnn\n // Jump to Xnn+Vx\n let mut x = Chip8Computer::new();\n // set X1 to 4...\n x.registers.poke(0x01, 0x04);\n // ...use (x1)+0x134\n let to_execute = JPX(0x01, 0x34);\n // expect to set PC to 0x834\n to_execute.execute(\u0026mut x);\n assert_eq!(x.registers.peek_i(), 0x834);\n}\n\n#[test]\nfn instruction_ldrd() {\n let mut x = Chip8Computer::new();\n x.state = WaitingForInstruction;\n x.delay_timer.set_timer(0x01);\n Chip8CpuInstructions::LDRD(0x01).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0x01);\n x.delay_timer.set_timer(0xff);\n Chip8CpuInstructions::LDRD(0x0).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x00), 0xff);\n x.step_system();\n x.step_system();\n x.step_system();\n x.step_system();\n Chip8CpuInstructions::LDRD(0x0).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x0), 0xfb);\n}\n\n#[test]\nfn video_hires_loop_check() {\n let max_address = SCHIP_VIDE_MEMORY;\n let x = build_checkboard_hd();\n x.peek((max_address + 1) as u16);\n // if we got here we didn't explode!\n assert!(true);\n}\n\n#[test]\nfn sound_timer_default() {\n let x = SoundTimer::default();\n assert_eq!(x.current(), 0);\n}\n\n#[test]\nfn system_memory_new() {\n let x = Chip8SystemMemory::new();\n assert!(true);\n}\n\n#[test]\nfn video_lowres_schip_draw_chip8_sprite() {\n let mut x = Chip8Computer::new();\n x.quirk_mode = QuirkMode::SChipModern;\n x.video_memory.set_lowres();\n // point at the 1 from chip8\n x.registers.poke_i(0x0005);\n // point to 1,2 for the drawing\n x.registers.poke(0x01, 0x01);\n x.registers.poke(0x02, 0x02);\n Chip8CpuInstructions::DRW(0x01, 0x01, 0x08).execute(\u0026mut x);\n let expected_state = read_test_result(\"state/video_lowres_schip_draw_chip8_sprite_result.json\");\n let actual_state = serde_json::to_string(\u0026x).unwrap();\n assert_eq!(expected_state, actual_state);\n}\n\n#[test]\nfn video_lowres_schip_draw_schip_sprite() {}\n#[test]\nfn video_highres_schip_draw_chip8_sprite() {}\n#[test]\nfn video_highres_schip_draw_schip_sprite() {}\n\n\n#[test]\nfn partial_eq_chip8computer() {\n let x = Chip8Computer::new();\n let y = Chip8Computer::new();\n assert_eq!(x, y)\n}\n\n#[test]\nfn quirk_mode_labels() {\n assert_eq!(format!(\"{}\", Chip8), LABEL_QUIRK_CHIP8);\n assert_eq!(format!(\"{}\", XOChip), LABEL_QUIRK_XOCHIP);\n assert_eq!(format!(\"{}\", SChipModern), LABEL_QUIRK_SCHIP);\n}\n\n#[test]\nfn system_memory_load_program() {\n let mut m = Chip8SystemMemory::new();\n let mut program_to_load = vec![];\n let file_to_load = format!(\"{}/2-ibm-logo.ch8\", TEST_ROM_ROOT);\n println!(\"Attempt to load {} from {}\", file_to_load, std::env::current_dir().unwrap().display());\n let mut file_to_load_from = File::open(file_to_load).expect(\"Unable to load sample rom\");\n file_to_load_from.read_to_end(\u0026mut program_to_load).expect(\"Unable to read sample rom\");\n m.load_program(program_to_load.clone().into());\n let expected_at_200 = program_to_load[0];\n assert_eq!(m.peek(0x200), expected_at_200);\n}\n\n#[test]\nfn start_stop_computer() {\n let mut computer = Chip8ComputerManager::new();\n\n assert_eq!(computer.core_should_run, false);\n computer.start();\n assert_eq!(computer.core_should_run, true);\n computer.step();\n assert_eq!(computer.core_should_run, true);\n computer.stop();\n assert_eq!(computer.core_should_run, false);\n computer.reset(Chip8);\n assert_eq!(computer.core_should_run, false);\n}\n\n#[test]\nfn state_default_matches() {\n let computer = Chip8Computer::default();\n let mut manager = Chip8ComputerManager::default();\n assert_eq!(computer, *manager.state());\n}\n\n#[test]\nfn keys_test_manager() {\n let mut manager = Chip8ComputerManager::default();\n\n for i in 0..16 {\n assert_eq!(manager.is_key_pressed(i), false);\n }\n\n // press key 5\n manager.press_key(5);\n assert_eq!(manager.is_key_pressed(5), true);\n manager.release_key(5);\n assert_eq!(manager.is_key_pressed(5), false);\n}\n\n","traces":[{"line":24,"address":[524173,523616],"length":1,"stats":{"Line":1}},{"line":25,"address":[523643],"length":1,"stats":{"Line":1}},{"line":26,"address":[523789],"length":1,"stats":{"Line":1}},{"line":27,"address":[523883],"length":1,"stats":{"Line":1}},{"line":485,"address":[524978,524208],"length":1,"stats":{"Line":0}},{"line":486,"address":[524241],"length":1,"stats":{"Line":0}},{"line":488,"address":[524255],"length":1,"stats":{"Line":0}},{"line":489,"address":[524215],"length":1,"stats":{"Line":0}},{"line":490,"address":[524263],"length":1,"stats":{"Line":0}},{"line":491,"address":[524223],"length":1,"stats":{"Line":0}},{"line":492,"address":[524231],"length":1,"stats":{"Line":0}},{"line":493,"address":[524271],"length":1,"stats":{"Line":0}},{"line":494,"address":[524338],"length":1,"stats":{"Line":0}},{"line":495,"address":[524364],"length":1,"stats":{"Line":0}},{"line":496,"address":[524380],"length":1,"stats":{"Line":0}},{"line":497,"address":[524401],"length":1,"stats":{"Line":0}},{"line":499,"address":[524484],"length":1,"stats":{"Line":0}},{"line":501,"address":[524684],"length":1,"stats":{"Line":0}},{"line":902,"address":[525008,525880],"length":1,"stats":{"Line":1}},{"line":903,"address":[525041],"length":1,"stats":{"Line":1}},{"line":904,"address":[525076,525191,525094,525056],"length":1,"stats":{"Line":4}},{"line":905,"address":[525078],"length":1,"stats":{"Line":1}},{"line":906,"address":[525175],"length":1,"stats":{"Line":1}},{"line":908,"address":[525060],"length":1,"stats":{"Line":1}},{"line":911,"address":[525110,525193],"length":1,"stats":{"Line":2}},{"line":913,"address":[525302,525464],"length":1,"stats":{"Line":2}},{"line":914,"address":[525532,525482],"length":1,"stats":{"Line":1}},{"line":916,"address":[525560,525507],"length":1,"stats":{"Line":2}},{"line":918,"address":[525662,525792],"length":1,"stats":{"Line":1}},{"line":919,"address":[525821,525854,525779],"length":1,"stats":{"Line":2}},{"line":921,"address":[525875,525832],"length":1,"stats":{"Line":2}},{"line":924,"address":[525421],"length":1,"stats":{"Line":1}},{"line":927,"address":[525904],"length":1,"stats":{"Line":2}},{"line":928,"address":[525912],"length":1,"stats":{"Line":1}},{"line":931,"address":[525936],"length":1,"stats":{"Line":1}},{"line":932,"address":[525944],"length":1,"stats":{"Line":1}}],"covered":22,"coverable":36},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","tests","unit_tests_instructions.rs"],"content":"use gemma::chip8::instructions::Chip8CpuInstructions;\nuse gemma::chip8::instructions::Chip8CpuInstructions::*;\nuse gemma::chip8::quirk_modes::QuirkMode::{Chip8, SChipModern, XOChip};\nuse gemma::constants::*;\n\n#[derive(Clone)]\nstruct InstructionTestQuirks {\n chip8: Chip8CpuInstructions,\n schip: Chip8CpuInstructions,\n xochip: Chip8CpuInstructions,\n}\n\n#[derive(Clone)]\nstruct InstructionTest {\n name: String,\n instruction: Chip8CpuInstructions,\n operands: String,\n asm: String,\n encoded: u16,\n quirks: InstructionTestQuirks,\n}\n\n#[test]\nfn instructions_encode_decode_tests_with_quirks() {\n let it = vec![\n InstructionTest {\n name: INST_SYS.to_string(),\n instruction: SYS(0x123),\n operands: \"0x0123\".to_string(),\n asm: \"SYS 0x0123\".to_string(),\n encoded: 0x0123,\n quirks: InstructionTestQuirks {\n chip8: SYS(0x123),\n schip: XXXXERRORINSTRUCTION,\n xochip: XXXXERRORINSTRUCTION,\n },\n },\n InstructionTest {\n name: INST_CLS.to_string(),\n instruction: CLS,\n asm: \"CLS\".to_string(),\n operands: \"\".to_string(),\n encoded: 0x00E0,\n quirks: InstructionTestQuirks {\n chip8: CLS,\n schip: CLS,\n xochip: CLS,\n },\n },\n InstructionTest {\n name: INST_RET.to_string(),\n instruction: RET,\n asm: \"RET\".to_string(),\n operands: \"\".to_string(),\n encoded: 0x00ee,\n quirks: InstructionTestQuirks {\n chip8: RET,\n schip: RET,\n xochip: RET,\n },\n },\n InstructionTest {\n name: INST_JPX.to_string(),\n instruction: JPX(0x1, 0xab),\n operands: \"0x01, 0x01ab\".to_string(),\n asm: \"JPX 0x01, 0x01ab\".to_string(),\n encoded: 0xb1ab,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: JPX(0x1, 0xab),\n xochip: XXXXERRORINSTRUCTION,\n },\n },\n InstructionTest {\n name: INST_CALL.to_string(),\n instruction: CALL(0x123),\n asm: \"CALL 0x0123\".to_string(),\n encoded: 0x2123,\n operands: \"0x0123\".to_string(),\n quirks: InstructionTestQuirks {\n chip8: CALL(0x123),\n schip: CALL(0x123),\n xochip: CALL(0x123),\n },\n },\n InstructionTest {\n name: INST_DRW.to_string(),\n instruction: DRW(0x01, 0x02, 0x03),\n operands: \"0x01, 0x02, 0x03\".to_string(),\n asm: \"DRW 0x01, 0x02, 0x03\".to_string(),\n encoded: 0xd123,\n quirks: InstructionTestQuirks {\n chip8: DRW(0x1, 0x2, 0x3),\n schip: DRW(0x1, 0x2, 0x3),\n xochip: DRW(0x1, 0x2, 0x3),\n },\n },\n InstructionTest {\n name: INST_JPI.to_string(),\n instruction: JPI(0x321),\n operands: \"0x0321\".to_string(),\n asm: \"JPI 0x0321\".to_string(),\n encoded: 0xb321,\n quirks: InstructionTestQuirks {\n chip8: JPI(0x321),\n schip: XXXXERRORINSTRUCTION,\n xochip: JPI(0x321),\n },\n },\n InstructionTest {\n name: INST_SCD.to_string(),\n instruction: SCD(0x01),\n operands: \"0x01\".to_string(),\n asm: \"SCD 0x01\".to_string(),\n encoded: 0x00c1,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: SCD(0x1),\n xochip: SCD(0x1),\n },\n },\n InstructionTest {\n name: INST_SCR.to_string(),\n instruction: Chip8CpuInstructions::SCR,\n operands: \"\".to_string(),\n asm: \"SCR\".to_string(),\n encoded: 0x00FB,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: SCR,\n xochip: SCR,\n },\n },\n InstructionTest {\n name: INST_SCL.to_string(),\n instruction: SCL,\n operands: \"\".to_string(),\n asm: \"SCL\".to_string(),\n encoded: 0x00FC,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: SCL,\n xochip: SCL,\n },\n },\n InstructionTest {\n name: INST_EXIT.to_string(),\n instruction: EXIT,\n operands: \"\".to_string(),\n asm: \"EXIT\".to_string(),\n encoded: 0x00FD,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: EXIT,\n xochip: EXIT,\n },\n },\n InstructionTest {\n name: INST_LOW.to_string(),\n instruction: LOW,\n operands: \"\".to_string(),\n asm: \"LOW\".to_string(),\n encoded: 0x00FE,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: LOW,\n xochip: LOW,\n },\n },\n InstructionTest {\n name: INST_HIGH.to_string(),\n instruction: HIGH,\n operands: \"\".to_string(),\n asm: \"HIGH\".to_string(),\n encoded: 0x00FF,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: HIGH,\n xochip: HIGH,\n },\n },\n InstructionTest {\n name: INST_SEX.to_string(),\n instruction: Chip8CpuInstructions::SEX(0x01, 0xfa),\n operands: \"0x01, 0xfa\".to_string(),\n asm: \"SEX 0x01, 0xfa\".to_string(),\n encoded: 0x31fa,\n quirks: InstructionTestQuirks {\n chip8: Chip8CpuInstructions::SEX(0x1, 0xfa),\n schip: Chip8CpuInstructions::SEX(0x1, 0xfa),\n xochip: Chip8CpuInstructions::SEX(0x1, 0xfa),\n },\n },\n InstructionTest {\n name: INST_SNEB.to_string(),\n instruction: Chip8CpuInstructions::SNEB(0x01, 0xab),\n operands: \"0x01, 0xab\".to_string(),\n asm: \"SNEB 0x01, 0xab\".to_string(),\n encoded: 0x41ab,\n quirks: InstructionTestQuirks {\n chip8: SNEB(0x01, 0xab),\n schip: SNEB(0x01, 0xab),\n xochip: SNEB(0x01, 0xab),\n },\n },\n InstructionTest {\n name: INST_SEY.to_string(),\n instruction: Chip8CpuInstructions::SEY(0x1, 0x2),\n operands: \"0x1, 0x2\".to_string(),\n asm: \"SEY 0x1, 0x2\".to_string(),\n encoded: 0x5120,\n quirks: InstructionTestQuirks {\n chip8: SEY(0x1, 0x2),\n schip: SEY(0x1, 0x2),\n xochip: SEY(0x1, 0x2),\n },\n },\n InstructionTest {\n name: INST_LDR.to_string(),\n instruction: LDR(0xa, 0xbe),\n operands: \"0x0a, 0xbe\".to_string(),\n asm: \"LDR 0x0a, 0xbe\".to_string(),\n encoded: 0x6abe,\n quirks: InstructionTestQuirks {\n chip8: LDR(0xa, 0xbe),\n schip: LDR(0xa, 0xbe),\n xochip: LDR(0xa, 0xbe),\n },\n },\n InstructionTest {\n name: INST_ADD.to_string(),\n instruction: Chip8CpuInstructions::ADD(0x01, 0xab),\n operands: \"0x01, 0xab\".to_string(),\n asm: \"ADD 0x01, 0xab\".to_string(),\n encoded: 0x71ab,\n quirks: InstructionTestQuirks {\n chip8: ADD(0x01, 0xab),\n schip: ADD(0x01, 0xab),\n xochip: ADD(0x01, 0xab),\n },\n },\n InstructionTest {\n name: INST_LDRK.to_string(),\n instruction: Chip8CpuInstructions::LDRK(0x6),\n operands: \"0x06\".to_string(),\n asm: \"LDRK 0x06\".to_string(),\n encoded: 0xF60A,\n quirks: InstructionTestQuirks {\n chip8: Chip8CpuInstructions::LDRK(0x6),\n schip: Chip8CpuInstructions::LDRK(0x6),\n xochip: Chip8CpuInstructions::LDRK(0x6),\n },\n },\n InstructionTest {\n name: INST_ADDI.to_string(),\n instruction: Chip8CpuInstructions::ADDI(0x02),\n operands: \"0x02\".to_string(),\n asm: \"ADDI 0x02\".to_string(),\n encoded: 0xF21E,\n quirks: InstructionTestQuirks {\n chip8: ADDI(0x02),\n schip: ADDI(0x02),\n xochip: ADDI(0x02),\n },\n },\n InstructionTest {\n name: INST_ADD.to_string(),\n instruction: Chip8CpuInstructions::ADD(0x02, 0x12),\n operands: \"0x02, 0x12\".to_string(),\n asm: \"ADD 0x02, 0x12\".to_string(),\n encoded: 0x7212,\n quirks: InstructionTestQuirks {\n chip8: ADD(0x02, 0x12),\n schip: ADD(0x02, 0x12),\n xochip: ADD(0x02, 0x12),\n },\n },\n InstructionTest {\n name: INST_SCU.to_string(),\n instruction: Chip8CpuInstructions::SCU(0x04),\n operands: \"0x04\".to_string(),\n asm: \"SCU 0x04\".to_string(),\n encoded: 0x00D4,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: XXXXERRORINSTRUCTION,\n xochip: SCU(0x04),\n },\n },\n InstructionTest {\n name: INST_ADDR.to_string(),\n instruction: ADDR(0x01, 0x02),\n operands: \"0x1, 0x2\".to_string(),\n asm: \"ADDR 0x1, 0x2\".to_string(),\n encoded: 0x8124,\n quirks: InstructionTestQuirks {\n chip8: ADDR(0x01, 0x02),\n schip: ADDR(0x01, 0x02),\n xochip: ADDR(0x01, 0x02),\n },\n },\n InstructionTest {\n name: INST_AND.to_string(),\n instruction: AND(0x1, 0x2),\n operands: \"0x1, 0x2\".to_string(),\n asm: \"AND 0x1, 0x2\".to_string(),\n encoded: 0x8122,\n quirks: InstructionTestQuirks {\n chip8: AND(0x1, 0x2),\n schip: AND(0x1, 0x2),\n xochip: AND(0x1, 0x2),\n },\n },\n InstructionTest {\n name: INST_JPA.to_string(),\n instruction: JPA(0x345),\n operands: \"0x0345\".to_string(),\n asm: \"JPA 0x0345\".to_string(),\n encoded: 0x1345,\n\n quirks: InstructionTestQuirks {\n chip8: JPA(0x345),\n schip: JPA(0x345),\n xochip: JPA(0x345),\n },\n },\n InstructionTest {\n name: INST_BCD.to_string(),\n instruction: BCD(0xc),\n operands: \"0x0c\".to_string(),\n asm: \"BCD 0x0c\".to_string(),\n encoded: 0xfc33,\n quirks: InstructionTestQuirks {\n chip8: BCD(0xcd),\n schip: BCD(0xcd),\n xochip: BCD(0xcd),\n },\n },\n InstructionTest {\n name: INST_LDD.to_string(),\n instruction: LDD(0xfc),\n operands: \"0xfc\".to_string(),\n asm: \"LDD 0xfc\".to_string(),\n encoded: 0xfc15,\n quirks: InstructionTestQuirks {\n chip8: LDD(0xfc),\n schip: LDD(0xfc),\n xochip: LDD(0xfc),\n },\n },\n InstructionTest {\n name: INST_ORY.to_string(),\n instruction: ORY(0x01, 0x3),\n operands: \"0x1, 0x3\".to_string(),\n asm: \"ORY 0x1, 0x3\".to_string(),\n encoded: 0x8133,\n quirks: InstructionTestQuirks {\n chip8: ORY(0x01, 0x3),\n schip: ORY(0x01, 0x3),\n xochip: ORY(0x01, 0x3),\n },\n },\n InstructionTest {\n name: INST_SUBC.to_string(),\n instruction: Chip8CpuInstructions::SUBC(0x1, 0x2),\n operands: \"0x1, 0x2\".to_string(),\n asm: \"SUBC 0x1, 0x2\".to_string(),\n encoded: 0x8127,\n quirks: InstructionTestQuirks {\n chip8: Chip8CpuInstructions::SUBC(0x1, 0x2),\n schip: Chip8CpuInstructions::SUBC(0x1, 0x2),\n xochip: Chip8CpuInstructions::SUBC(0x1, 0x2),\n },\n },\n InstructionTest {\n name: INST_SUB.to_string(),\n instruction: Chip8CpuInstructions::SUB(0x1, 0x2),\n operands: \"0x1, 0x2\".to_string(),\n asm: \"SUB 0x1, 0x2\".to_string(),\n encoded: 0x8125,\n quirks: InstructionTestQuirks {\n chip8: Chip8CpuInstructions::SUB(0x1, 0x2),\n schip: Chip8CpuInstructions::SUB(0x1, 0x2),\n xochip: Chip8CpuInstructions::SUB(0x1, 0x2),\n },\n },\n InstructionTest {\n name: INST_STR.to_string(),\n instruction: Chip8CpuInstructions::STR(0x06),\n operands: \"0x06\".to_string(),\n asm: \"STR 0x06\".to_string(),\n encoded: 0xf675,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: Chip8CpuInstructions::STR(0x06),\n xochip: Chip8CpuInstructions::STR(0x06),\n },\n },\n InstructionTest {\n name: INST_OR.to_string(),\n instruction: OR(0x01, 0x02),\n operands: \"0x1, 0x2\".to_string(),\n asm: \"OR 0x1, 0x2\".to_string(),\n encoded: 0x8121,\n quirks: InstructionTestQuirks {\n chip8: OR(0x01, 0x02),\n schip: OR(0x01, 0x02),\n xochip: OR(0x01, 0x02),\n },\n },\n InstructionTest {\n name: INST_SHR.to_string(),\n instruction: SHR(0x04, 0x4),\n operands: \"0x4, 0x4\".to_string(),\n asm: \"SHR 0x4, 0x4\".to_string(),\n encoded: 0x8446,\n quirks: InstructionTestQuirks {\n chip8: SHR(0x04, 0x4),\n schip: SHR(0x04, 0x4),\n xochip: SHR(0x04, 0x4),\n },\n },\n InstructionTest {\n name: INST_SHL.to_string(),\n instruction: SHL(0x04, 0x4),\n operands: \"0x4, 0x4\".to_string(),\n asm: \"SHL 0x4, 0x4\".to_string(),\n encoded: 0x844e,\n quirks: InstructionTestQuirks {\n chip8: SHL(0x04, 0x4),\n schip: SHL(0x04, 0x4),\n xochip: SHL(0x04, 0x4),\n },\n },\n InstructionTest {\n name: INST_RND.to_string(),\n instruction: RND(0x01, 0xff),\n operands: \"0x01, 0xff\".to_string(),\n asm: \"RND 0x01, 0xff\".to_string(),\n encoded: 0xc1ff,\n quirks: InstructionTestQuirks {\n chip8: RND(0x01, 0xff),\n schip: RND(0x01, 0xff),\n xochip: RND(0x01, 0xff),\n },\n },\n InstructionTest {\n name: INST_LDRY.to_string(),\n instruction: LDRY(0x01, 0x02),\n operands: \"0x1, 0x2\".to_string(),\n asm: \"LDRY 0x1, 0x2\".to_string(),\n encoded: 0x8120,\n quirks: InstructionTestQuirks {\n chip8: LDRY(0x01, 0x02),\n schip: LDRY(0x01, 0x02),\n xochip: LDRY(0x01, 0x02),\n },\n },\n InstructionTest {\n name: INST_LDIS.to_string(),\n instruction: LDIS(0x01),\n operands: \"0x01\".to_string(),\n asm: \"LDIS 0x01\".to_string(),\n encoded: 0xf118,\n quirks: InstructionTestQuirks {\n chip8: LDIS(0x01),\n schip: LDIS(0x01),\n xochip: LDIS(0x01),\n },\n },\n InstructionTest {\n name: INST_LIDR.to_string(),\n instruction: LIDR(0x01),\n operands: \"0x01\".to_string(),\n asm: \"LIDR 0x01\".to_string(),\n encoded: 0xf185,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: LIDR(0x01),\n xochip: LIDR(0x01),\n },\n },\n InstructionTest {\n name: INST_LDF2.to_string(),\n instruction: LDF2(0x01),\n operands: \"0x01\".to_string(),\n asm: \"LDF2 0x01\".to_string(),\n encoded: 0xf130,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: LDF2(0x01),\n xochip: LDF2(0x01),\n },\n },\n InstructionTest {\n name: INST_LDF.to_string(),\n instruction: LDFX(0x01),\n operands: \"0x01\".to_string(),\n asm: \"LDF 0x01\".to_string(),\n encoded: 0xf129,\n quirks: InstructionTestQuirks {\n chip8: LDFX(0x01),\n schip: LDFX(0x01),\n xochip: LDFX(0x01),\n },\n },\n InstructionTest {\n name: INST_LDIA.to_string(),\n instruction: LDIA(0x01),\n operands: \"0x0001\".to_string(),\n asm: \"LDIA 0x0001\".to_string(),\n encoded: 0xa001,\n quirks: InstructionTestQuirks {\n chip8: LDIA(0x01),\n schip: LDIA(0x01),\n xochip: LDIA(0x01),\n },\n },\n InstructionTest {\n name: INST_LDIX.to_string(),\n instruction: LDIX(0x01),\n operands: \"0x01\".to_string(),\n asm: \"LDIX 0x01\".to_string(),\n encoded: 0xf155,\n quirks: InstructionTestQuirks {\n chip8: LDIX(0x01),\n schip: LDIX(0x01),\n xochip: LDIX(0x01),\n },\n },\n InstructionTest {\n name: INST_LDRD.to_string(),\n instruction: Chip8CpuInstructions::LDRD(0x01),\n operands: \"0x01\".to_string(),\n asm: \"LDRD 0x01\".to_string(),\n encoded: 0xf107,\n quirks: InstructionTestQuirks {\n chip8: Chip8CpuInstructions::LDRD(0x01),\n schip: Chip8CpuInstructions::LDRD(0x01),\n xochip: Chip8CpuInstructions::LDRD(0x01),\n },\n },\n InstructionTest {\n name: INST_LDRI.to_string(),\n instruction: Chip8CpuInstructions::LDRI(0x01),\n operands: \"0x01\".to_string(),\n asm: \"LDRI 0x01\".to_string(),\n encoded: 0xf165,\n quirks: InstructionTestQuirks {\n chip8: Chip8CpuInstructions::LDRI(0x01),\n schip: Chip8CpuInstructions::LDRI(0x01),\n xochip: Chip8CpuInstructions::LDRI(0x01),\n },\n },\n InstructionTest {\n name: INST_SKP.to_string(),\n instruction: SKP(0x01),\n operands: \"0x01\".to_string(),\n asm: \"SKP 0x01\".to_string(),\n encoded: 0xe19e,\n quirks: InstructionTestQuirks {\n chip8: SKP(0x01),\n schip: SKP(0x01),\n xochip: SKP(0x01),\n },\n },\n InstructionTest {\n name: INST_SNEY.to_string(),\n instruction: SNEY(0x01, 0x1),\n operands: \"0x1, 0x1\".to_string(),\n asm: \"SNEY 0x1, 0x1\".to_string(),\n encoded: 0x9110,\n quirks: InstructionTestQuirks {\n chip8: SNEY(0x01, 0x1),\n schip: SNEY(0x01, 0x1),\n xochip: SNEY(0x01, 0x1),\n },\n },\n InstructionTest {\n name: INST_SKNP.to_string(),\n instruction: SKNP(0x01),\n operands: \"0x01\".to_string(),\n asm: \"SKNP 0x01\".to_string(),\n encoded: 0xe1a1,\n quirks: InstructionTestQuirks {\n chip8: SKNP(0x01),\n schip: SKNP(0x01),\n xochip: SKNP(0x01),\n },\n },\n ];\n\n for current in it {\n let instruction = current.clone().instruction;\n let asm = Chip8CpuInstructions::from_str(\u0026current.asm);\n let as_chip8 = Chip8CpuInstructions::decode(current.encoded, \u0026Chip8);\n let quirks_chip8 = current.quirks.chip8;\n let as_schip = Chip8CpuInstructions::decode(current.encoded, \u0026SChipModern);\n let quirks_schip = current.quirks.schip;\n let as_xochip = Chip8CpuInstructions::decode(current.encoded, \u0026XOChip);\n let quirks_xochip = current.quirks.xochip;\n\n // ** CONVERSION **\n // -\u003e Integer to Instruction\n assert!(matches!(\n Chip8CpuInstructions::decode(current.encoded, \u0026Chip8),\n i\n ));\n // -\u003e Instruction to Integer\n println!(\n \"TESTING INSTRUCTION TO INTEGER FOR {:?} / {:04x} {:04x}\",\n current.instruction,\n current.encoded,\n instruction.encode()\n );\n assert_eq!(current.encoded, instruction.encode());\n // -\u003e Instruction to String\n assert_eq!(instruction.to_string(), current.asm);\n assert_eq!(instruction.operands(), current.operands);\n assert_eq!(current.instruction.name(), current.name);\n // -\u003e String to Instruction\n assert!(matches!(Chip8CpuInstructions::from_str(\u0026current.name), asm));\n // ** QUIRKS **\n assert!(matches!(as_chip8, quirks_chip8));\n assert!(matches!(as_schip, quirks_schip));\n assert!(matches!(as_xochip, quirks_xochip));\n }\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","tests","util_tests.rs"],"content":"use std::collections::BTreeMap;\nuse gemma::chip8::util::InstructionUtil;\n\n#[test]\nfn byte_to_bools() {\n let data_set: BTreeMap\u003cu8, [bool; 8]\u003e = BTreeMap::from(\n [\n (0x00, [false, false, false, false, false, false, false, false]),\n (0x0f, [true, true, true, true, false, false, false, false]),\n (0xaa, [false, true, false, true, false, true, false, true]),\n (0xf0, [false, false, false, false, true, true, true, true]),\n (0xff, [true, true, true, true, true, true, true, true]),\n ]\n );\n\n for (key, value) in data_set.iter() {\n let result_array = InstructionUtil::byte_to_bools(*key);\n for i in 0..8 {\n assert_eq!(result_array[i], value[i]);\n }\n }\n}\n\n\n#[test]\nfn bools_to_byte() {\n let data_set: BTreeMap\u003cu8, [bool; 8]\u003e = BTreeMap::from(\n [\n (0x00, [false, false, false, false, false, false, false, false]),\n (0x0f, [true, true, true, true, false, false, false, false]),\n (0xaa, [false, true, false, true, false, true, false, true]),\n (0xf0, [false, false, false, false, true, true, true, true]),\n (0xff, [true, true, true, true, true, true, true, true]),\n ]\n );\n\n for (key, value) in data_set.iter() {\n let result = InstructionUtil::bools_to_byte(*value);\n assert_eq!(result, *key);\n }\n}\n\n#[test]\nfn split_bytes() {\n let data_set: BTreeMap\u003cu16, (u8, u8)\u003e = BTreeMap::from(\n [\n (0xff00, (0xff, 0x00)),\n (0x00ff, (0x00, 0xff)),\n (0xabcd, (0xab, 0xcd)),\n (0xcdab, (0xcd, 0xab))\n ]\n );\n\n for (key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::split_bytes(*key), *value);\n }\n}\n\n#[test]\nfn join_bytes() {\n let data_set: BTreeMap\u003cu16, (u8, u8)\u003e = BTreeMap::from(\n [\n (0xff00, (0xff, 0x00)),\n (0x00ff, (0x00, 0xff)),\n (0xabcd, (0xab, 0xcd)),\n (0xcdab, (0xcd, 0xab))\n ]\n );\n\n for (key, (low, high )) in data_set.iter() {\n assert_eq!(InstructionUtil::join_bytes(*low, *high), *key);\n }\n}\n\n#[test]\nfn swap_endian() {\n let data_set: BTreeMap\u003cu16, u16\u003e = BTreeMap::from(\n [\n (0xabcd, 0xcdab),\n (0x00ff, 0xff00),\n (0xff00, 0x00ff),\n (0xffff, 0xffff),\n (0x0000, 0x0000)\n ]\n );\n\n for (key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::swap_endian(*key), *value);\n }\n}\n\n#[test]\nfn split_bytes_swap_endian() {\n let data_set: BTreeMap\u003cu16, (u8, u8)\u003e = BTreeMap::from([\n (0x0000, (0x00, 0x00)),\n (0xffff, (0xff, 0xff)),\n (0xff00, (0x00, 0xff)),\n (0x00ff, (0xff, 0x00)),\n (0xabcd, (0xcd, 0xab))\n ]);\n\n for (key, result) in data_set.iter() {\n assert_eq!(InstructionUtil::split_bytes_swap_endian(*key), *result)\n }\n}\n\n#[test]\nfn join_bytes_swap_endian() {\n let data_set: BTreeMap\u003cu16, (u8, u8)\u003e = BTreeMap::from([\n (0xffff, (0xff, 0xff)),\n (0x0000, (0x00, 0x00)),\n (0xff00, (0x00, 0xff)),\n (0x00ff, (0xff, 0x00)),\n (0xabcd, (0xcd, 0xab))\n ]);\n\n for (key, (low, high)) in data_set.iter() {\n assert_eq!(InstructionUtil::join_bytes_swap_endian(*low, *high), *key);\n }\n\n}\n\n#[test]\nfn read_address_from_instruction() {\n let data_set: BTreeMap\u003cu16, u16\u003e = BTreeMap::from([\n (0xabcd, 0x0bcd),\n (0xffff, 0x0fff),\n (0x0000, 0x0000),\n (0x8123, 0x0123)\n ]) ;\n\n for (key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::read_addr_from_instruction(*key), *value);\n }\n}\n\n#[test]\nfn read_nibble_from_instruction() {\n let data_set: BTreeMap\u003cu16, u8\u003e = BTreeMap::from([\n (0xffff, 0x0f),\n (0x0000, 0x00),\n (0xabcd, 0x0d),\n ]);\n\n for(key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::read_nibble_from_instruction(*key), *value);\n }\n}\n\n#[test]\nfn read_x_from_instruction() {\n let data_set: BTreeMap\u003cu16, u8\u003e = BTreeMap::from([\n (0xffff, 0x0f),\n (0x0000, 0x00),\n (0x0a00, 0x0a),\n (0x0b00, 0x0b)\n ]);\n\n for (key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::read_x_from_instruction(*key), *value);\n }\n}\n\n#[test]\nfn read_y_from_instruction() {\n let data_set: BTreeMap\u003cu16, u8\u003e = BTreeMap::from([\n (0xffff, 0x0f),\n (0x0000, 0x00),\n (0x00a0, 0x0a),\n (0x00b0, 0x0b)\n ]);\n\n for (key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::read_y_from_instruction(*key), *value);\n }\n}\n\n#[test]\nfn read_byte_from_instruction() {\n let data_set: BTreeMap\u003cu16, u8\u003e = BTreeMap::from([\n (0xffff, 0xff),\n (0x0000, 0x00),\n (0xabcd, 0xcd),\n (0xcdab, 0xab)\n ]);\n\n for (key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::read_byte_from_instruction(*key), *value);\n }\n}\n\n#[test]\nfn read_upper_byte_lower_nibble() {\n let data_set: BTreeMap\u003cu16, u8\u003e = BTreeMap::from([\n (0x0000, 0x00),\n (0xabcd, 0x0b),\n (0xffff, 0x0f),\n (0xcdab, 0x0d)\n ]);\n\n for (key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(*key), *value);\n }\n}\n\n#[test]\nfn read_bits_from_instruction() {\n let data_set: BTreeMap\u003cu16, (u8, u8, u16)\u003e = BTreeMap::from([\n (0x0000, (0, 7, 0x00)),\n (0x0000, (8, 15, 0x00)),\n (0xffff, (0, 7, 0xff)),\n (0xffff, (8, 15, 0xff)),\n ]);\n\n for (key, (start, end, value)) in data_set.iter() {\n let result = InstructionUtil::read_bits_from_instruction(*key, *start, *end);\n assert_eq!(result, *value);\n }\n}","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaegui","src","bin","gemmaegui.rs"],"content":"use crate::support::gemma_egui_support::{EGuiFileList, GemmaEguiSupport};\nuse eframe::egui;\nuse egui::Key;\nuse gemma::chip8::computer::Chip8Computer;\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\nuse std::path::PathBuf;\nuse std::time::Instant;\n\nmod support;\n\nconst LIN_KEYS: [[(Key, u8); 4]; 4] = [\n [\n (Key::Num1, 0x01),\n (Key::Num2, 0x02),\n (Key::Num3, 0x03),\n (Key::Num4, 0x0c),\n ],\n [\n (Key::Q, 0x04),\n (Key::W, 0x05),\n (Key::E, 0x06),\n (Key::R, 0x0d),\n ],\n [\n (Key::A, 0x07),\n (Key::S, 0x08),\n (Key::D, 0x09),\n (Key::F, 0x0e),\n ],\n [\n (Key::Z, 0x0a),\n (Key::X, 0x00),\n (Key::C, 0x0b),\n (Key::V, 0x0F),\n ],\n];\n\n#[derive(Default)]\nstruct DisplayOptions {\n pub video: bool,\n pub registers: bool,\n pub memory: bool,\n}\npub struct GemmaViewerState {\n pub selected_file_index: i32,\n pub selected_filename: String,\n pub display_options: DisplayOptions,\n}\n\nimpl Default for GemmaViewerState {\n fn default() -\u003e Self {\n GemmaViewerState {\n selected_file_index: -1,\n selected_filename: String::new(),\n display_options: Default::default(),\n }\n }\n}\n\nimpl GemmaViewerState {\n pub fn new() -\u003e GemmaViewerState {\n GemmaViewerState {\n ..Default::default()\n }\n }\n}\n\nfn main() -\u003e eframe::Result {\n println!(\"Taxation is Theft\");\n\n let mut computer = Chip8ComputerManager::new();\n let mut state = GemmaViewerState::new();\n\n let options = eframe::NativeOptions {\n viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),\n ..Default::default()\n };\n\n eframe::run_simple_native(\"EGUI Gemma\", options, move |ctx, _frame| {\n catppuccin_egui::set_theme(ctx, catppuccin_egui::MOCHA);\n egui::CentralPanel::default().show(ctx, |ui| {\n let frame_start_time = Instant::now();\n\n let local_computer = computer.state();\n //if state.display_video {\n GemmaEguiSupport::video_view(local_computer, ui);\n // }\n\n ui.heading(\"EGUI Gemma\");\n\n // if state.display_memory {\n GemmaEguiSupport::memory_view(local_computer, ui);\n // }\n\n // if state.display_registers {\n GemmaEguiSupport::registers_view(local_computer, ui);\n // }\n ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {\n if ui.button(\"Start\").clicked() {\n computer.start();\n }\n if ui.button(\"Step\").clicked() {\n computer.step();\n }\n\n if ui.button(\"Stop\").clicked() {\n computer.stop();\n // state.is_running = false;\n }\n if ui.button(\"Reset\").clicked() {\n computer.reset(computer.quirks_mode());\n // state.is_running = false;\n }\n });\n\n if ui\n .button(format!(\"Load {}\", state.selected_filename))\n .clicked()\n {\n // println!(\"Should load the bin now\");\n let read_bin = std::fs::read(PathBuf::from(format!(\n \"resources/roms/{}\",\n state.selected_filename\n )))\n .unwrap();\n computer.load_new_program_to_system_memory(read_bin);\n };\n EGuiFileList::display_path(\n PathBuf::from(\"resources/roms\"),\n \u0026mut state.selected_filename,\n ui,\n );\n ctx.input(|input| {\n // loop through the keys we are checking...\n for row in LIN_KEYS {\n for (keyboard_key, keypad_key) in row {\n if input.key_pressed(keyboard_key) {\n computer.press_key(keypad_key);\n // println!(\"KEY {keypad_key:02x} DOWN\");\n } else {\n // release it if the user just did\n if computer.is_key_pressed(keypad_key) {\n computer.release_key(keypad_key);\n // println!(\"KEY {keypad_key:02x} up\");\n }\n }\n }\n }\n });\n\n // run our target number of ticks in the amount of available time.\n let time_consumed = Instant::now().duration_since(frame_start_time).as_millis();\n let mut num_ticks = 0;\n while num_ticks \u003c 1000 {\n computer.tick();\n num_ticks += 1;\n }\n ctx.request_repaint();\n });\n })\n}\n","traces":[{"line":51,"address":[3676112,3676280],"length":1,"stats":{"Line":0}},{"line":54,"address":[3676131],"length":1,"stats":{"Line":0}},{"line":55,"address":[3676136,3676193],"length":1,"stats":{"Line":0}},{"line":61,"address":[3676304],"length":1,"stats":{"Line":0}},{"line":68,"address":[3676400,3677415,3677446],"length":1,"stats":{"Line":0}},{"line":69,"address":[3676417],"length":1,"stats":{"Line":0}},{"line":71,"address":[3676481],"length":1,"stats":{"Line":0}},{"line":72,"address":[3676511],"length":1,"stats":{"Line":0}},{"line":75,"address":[3676575,3676620],"length":1,"stats":{"Line":0}},{"line":79,"address":[3654016],"length":1,"stats":{"Line":0}},{"line":80,"address":[3654056],"length":1,"stats":{"Line":0}},{"line":81,"address":[3654100,3654208,3655711],"length":1,"stats":{"Line":0}},{"line":82,"address":[3654233],"length":1,"stats":{"Line":0}},{"line":84,"address":[3654276],"length":1,"stats":{"Line":0}},{"line":86,"address":[3654309],"length":1,"stats":{"Line":0}},{"line":89,"address":[3654319],"length":1,"stats":{"Line":0}},{"line":92,"address":[3654370],"length":1,"stats":{"Line":0}},{"line":96,"address":[3654385],"length":1,"stats":{"Line":0}},{"line":98,"address":[3654499,3656665,3655744],"length":1,"stats":{"Line":0}},{"line":99,"address":[3655883,3655769],"length":1,"stats":{"Line":0}},{"line":100,"address":[3655958],"length":1,"stats":{"Line":0}},{"line":102,"address":[3656097,3655974],"length":1,"stats":{"Line":0}},{"line":103,"address":[3656181],"length":1,"stats":{"Line":0}},{"line":106,"address":[3656320,3656197],"length":1,"stats":{"Line":0}},{"line":107,"address":[3656404],"length":1,"stats":{"Line":0}},{"line":110,"address":[3656420,3656543],"length":1,"stats":{"Line":0}},{"line":111,"address":[3656624],"length":1,"stats":{"Line":0}},{"line":116,"address":[3654950,3654783],"length":1,"stats":{"Line":0}},{"line":117,"address":[3654630],"length":1,"stats":{"Line":0}},{"line":118,"address":[3654902,3654956],"length":1,"stats":{"Line":0}},{"line":121,"address":[3655142,3654994],"length":1,"stats":{"Line":0}},{"line":126,"address":[3655405],"length":1,"stats":{"Line":0}},{"line":128,"address":[3655475],"length":1,"stats":{"Line":0}},{"line":129,"address":[3655422],"length":1,"stats":{"Line":0}},{"line":130,"address":[3655471],"length":1,"stats":{"Line":0}},{"line":133,"address":[3656688,3657346,3655485],"length":1,"stats":{"Line":0}},{"line":135,"address":[3656937,3656713,3656870],"length":1,"stats":{"Line":0}},{"line":136,"address":[3656953,3657127,3657197],"length":1,"stats":{"Line":0}},{"line":137,"address":[3657189,3657225],"length":1,"stats":{"Line":0}},{"line":138,"address":[3657281,3657341],"length":1,"stats":{"Line":0}},{"line":142,"address":[3657254,3657304],"length":1,"stats":{"Line":0}},{"line":143,"address":[3657317],"length":1,"stats":{"Line":0}},{"line":152,"address":[3655497],"length":1,"stats":{"Line":0}},{"line":153,"address":[3655596],"length":1,"stats":{"Line":0}},{"line":154,"address":[3655607,3655693],"length":1,"stats":{"Line":0}},{"line":155,"address":[3655655],"length":1,"stats":{"Line":0}},{"line":156,"address":[3655664,3655695],"length":1,"stats":{"Line":0}},{"line":158,"address":[3655625],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":48},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaegui","src","bin","support","gemma_egui_state.rs"],"content":"use std::ops::Range;\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\n\npub struct GemmaEGuiState {\n pub display_video: bool,\n pub display_memory: bool,\n pub display_registers: bool,\n pub memory_view: Range\u003ci32\u003e,\n pub computer: Chip8ComputerManager,\n pub selected_rom_filename: String\n}\n\nimpl Default for GemmaEGuiState {\n fn default() -\u003e Self {\n Self {\n display_video: true,\n display_memory: true,\n display_registers: true,\n memory_view: 0x00..0x200,\n computer: Chip8ComputerManager::new(),\n selected_rom_filename: String::new()\n }\n }\n}\n","traces":[{"line":14,"address":[3690256,3690467],"length":1,"stats":{"Line":0}},{"line":20,"address":[3690273],"length":1,"stats":{"Line":0}},{"line":21,"address":[3690295],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":3},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaegui","src","bin","support","gemma_egui_support.rs"],"content":"use crate::Chip8Computer;\nuse egui::Rect;\nuse egui::Ui;\nuse egui::Vec2;\nuse egui::{Align, Color32, ComboBox, Pos2};\nuse std::fs::read_dir;\nuse std::path::PathBuf;\n\nconst CELL_WIDTH: f32 = 5.0;\nconst CELL_HEIGHT: f32 = 5.0;\n\npub struct EGuiFileList {}\nimpl EGuiFileList {\n pub fn display_path(root: PathBuf, selected_filename: \u0026mut String, ui: \u0026mut Ui) {\n let working_filename = selected_filename.clone();\n ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {\n // ui.label(format!(\"Displaying {}\", root.to_str().unwrap_or(\"Unable to Load Path\")));\n ComboBox::from_label(\"Select ROM\")\n .selected_text(selected_filename.clone())\n .show_ui(ui, |ui| {\n let mut sorted_options = vec![];\n for option in read_dir(root.as_path()).unwrap() {\n let to_push = option\n .unwrap()\n .file_name()\n .into_string()\n .unwrap_or(String::new());\n sorted_options.push(to_push);\n }\n\n sorted_options.sort();\n for item in sorted_options {\n // Add each option to the ComboBox\n if ui\n .selectable_label(selected_filename.eq(\u0026item.as_str()), item.clone())\n .clicked()\n {\n *selected_filename = item;\n }\n }\n });\n // Display the selected option\n });\n }\n}\n\npub struct GemmaEguiSupport {}\n\nimpl GemmaEguiSupport {\n pub fn controls_view(system: \u0026mut Chip8Computer, ui: \u0026mut Ui) {\n ui.with_layout(egui::Layout::left_to_right(Align::TOP), |ui| {\n // ui.checkbox(\u0026mut state.display_memory, \"Display Memory\");\n // ui.checkbox(\u0026mut state.display_video, \"Display Video\");\n // ui.checkbox(\u0026mut state.display_registers, \"Display Registers\");\n });\n }\n\n pub fn registers_view(system: \u0026Chip8Computer, ui: \u0026mut Ui) {\n ui.label(format!(\n \"V0-7: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} \",\n system.registers.peek(0x00),\n system.registers.peek(0x01),\n system.registers.peek(0x02),\n system.registers.peek(0x03),\n system.registers.peek(0x04),\n system.registers.peek(0x05),\n system.registers.peek(0x06),\n system.registers.peek(0x07)\n ));\n ui.label(format!(\n \"V8-F: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} \",\n system.registers.peek(0x08),\n system.registers.peek(0x09),\n system.registers.peek(0x0A),\n system.registers.peek(0x0B),\n system.registers.peek(0x0C),\n system.registers.peek(0x0D),\n system.registers.peek(0x0E),\n system.registers.peek(0x0F)\n ));\n ui.label(format!(\n \"PC: {:04x}\\tI: {:04x}\",\n system.registers.peek_pc(),\n system.registers.peek_i()\n ));\n }\n\n pub fn video_view(system: \u0026Chip8Computer, ui: \u0026mut Ui) {\n let (_resp, painter) = ui.allocate_painter(Vec2::new(350.0, 165.0), egui::Sense::hover());\n for current_row in 0..32 {\n for current_col in 0..64 {\n let data_offset = current_row * 64 + current_col;\n let x_offset = current_col as f32 * CELL_WIDTH;\n let y_offset = current_row as f32 * CELL_HEIGHT;\n let origin = Pos2::new(x_offset, y_offset);\n let colour = if system.video_memory.peek(data_offset) {\n Color32::RED\n } else {\n Color32::WHITE\n };\n let rect = Rect::from_min_size(origin, Vec2::new(CELL_WIDTH, CELL_HEIGHT));\n painter.rect_filled(rect, 0.0, colour);\n // println!(\"Cell {current_col}x{current_row} at {}x{} -\u003e {}\",\n // origin.x, origin.y,\n // system.video_memory.peek(data_offset));\n }\n }\n // thread::sleep(Duration::from_secs(1));\n }\n\n pub fn memory_view(system: \u0026Chip8Computer, ui: \u0026mut Ui) {\n ui.label(\"Memory View\");\n\n for i in (0..=0x200).step_by(16) {\n ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {\n for y in 0..16 {\n ui.label(format!(\"{:02x}\", system.memory.peek((i + y) as u16)).as_str());\n }\n });\n }\n ui.label(\"Should have **something** to adjust the 'memory window'\");\n }\n}\n","traces":[{"line":14,"address":[3682720,3683153],"length":1,"stats":{"Line":0}},{"line":15,"address":[3682750],"length":1,"stats":{"Line":0}},{"line":16,"address":[3666064,3666307,3666339],"length":1,"stats":{"Line":0}},{"line":18,"address":[3666270,3666094,3666204],"length":1,"stats":{"Line":0}},{"line":19,"address":[3666320,3666139,3666252],"length":1,"stats":{"Line":0}},{"line":20,"address":[3666352,3668143,3668571,3666260],"length":1,"stats":{"Line":0}},{"line":21,"address":[3666388],"length":1,"stats":{"Line":0}},{"line":22,"address":[3666947,3666549,3668522,3666775,3666445,3666914],"length":1,"stats":{"Line":0}},{"line":23,"address":[3668261,3668329,3668174],"length":1,"stats":{"Line":0}},{"line":26,"address":[3668268],"length":1,"stats":{"Line":0}},{"line":27,"address":[3668284,3668549,3668201,3668368],"length":1,"stats":{"Line":0}},{"line":28,"address":[3668440],"length":1,"stats":{"Line":0}},{"line":31,"address":[3667189],"length":1,"stats":{"Line":0}},{"line":32,"address":[3668119,3667463,3667223,3667413],"length":1,"stats":{"Line":0}},{"line":34,"address":[3668106,3667684,3667862],"length":1,"stats":{"Line":0}},{"line":35,"address":[3667613,3667503],"length":1,"stats":{"Line":0}},{"line":36,"address":[3667868,3667814],"length":1,"stats":{"Line":0}},{"line":38,"address":[3667925],"length":1,"stats":{"Line":0}},{"line":50,"address":[3683184],"length":1,"stats":{"Line":0}},{"line":51,"address":[3668608],"length":1,"stats":{"Line":0}},{"line":58,"address":[3683424],"length":1,"stats":{"Line":0}},{"line":59,"address":[3683733,3685525,3683635,3683488,3683684,3683586,3683831,3683537,3683782],"length":1,"stats":{"Line":0}},{"line":61,"address":[3683469],"length":1,"stats":{"Line":0}},{"line":62,"address":[3683515],"length":1,"stats":{"Line":0}},{"line":63,"address":[3683564],"length":1,"stats":{"Line":0}},{"line":64,"address":[3683613],"length":1,"stats":{"Line":0}},{"line":65,"address":[3683662],"length":1,"stats":{"Line":0}},{"line":66,"address":[3683711],"length":1,"stats":{"Line":0}},{"line":67,"address":[3683760],"length":1,"stats":{"Line":0}},{"line":68,"address":[3683809],"length":1,"stats":{"Line":0}},{"line":70,"address":[3685582,3687619,3685827,3685631,3685876,3685925,3685680,3685729,3685778],"length":1,"stats":{"Line":0}},{"line":72,"address":[3685560],"length":1,"stats":{"Line":0}},{"line":73,"address":[3685609],"length":1,"stats":{"Line":0}},{"line":74,"address":[3685658],"length":1,"stats":{"Line":0}},{"line":75,"address":[3685707],"length":1,"stats":{"Line":0}},{"line":76,"address":[3685756],"length":1,"stats":{"Line":0}},{"line":77,"address":[3685805],"length":1,"stats":{"Line":0}},{"line":78,"address":[3685854],"length":1,"stats":{"Line":0}},{"line":79,"address":[3685903],"length":1,"stats":{"Line":0}},{"line":81,"address":[3688254,3687678,3687723],"length":1,"stats":{"Line":0}},{"line":83,"address":[3687660],"length":1,"stats":{"Line":0}},{"line":84,"address":[3687705],"length":1,"stats":{"Line":0}},{"line":88,"address":[3688304,3689696],"length":1,"stats":{"Line":0}},{"line":89,"address":[3688359],"length":1,"stats":{"Line":0}},{"line":90,"address":[3688753,3688857,3688671],"length":1,"stats":{"Line":0}},{"line":91,"address":[3688878,3688976],"length":1,"stats":{"Line":0}},{"line":92,"address":[3689260,3689091],"length":1,"stats":{"Line":0}},{"line":93,"address":[3689180],"length":1,"stats":{"Line":0}},{"line":94,"address":[3689214],"length":1,"stats":{"Line":0}},{"line":95,"address":[3689300],"length":1,"stats":{"Line":0}},{"line":96,"address":[3689336,3689381],"length":1,"stats":{"Line":0}},{"line":97,"address":[3689383],"length":1,"stats":{"Line":0}},{"line":99,"address":[3689368],"length":1,"stats":{"Line":0}},{"line":102,"address":[3689610],"length":1,"stats":{"Line":0}},{"line":111,"address":[3689728],"length":1,"stats":{"Line":0}},{"line":112,"address":[3689760],"length":1,"stats":{"Line":0}},{"line":114,"address":[3689794,3690006],"length":1,"stats":{"Line":0}},{"line":115,"address":[3669272,3668624],"length":1,"stats":{"Line":0}},{"line":116,"address":[3668654,3668712],"length":1,"stats":{"Line":0}},{"line":117,"address":[3669132,3668723],"length":1,"stats":{"Line":0}},{"line":121,"address":[3689949],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":61},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaegui","src","bin","support","mod.rs"],"content":"pub mod gemma_egui_state;\npub mod gemma_egui_support;\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaimgui","src","bin","gemmaimgui.rs"],"content":"use clap::{Parser, Subcommand};\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\nuse std::path::Path;\nuse std::time::Instant;\nuse std::{default::Default, path::PathBuf};\nuse support::ui_state::ROMPATH_DEFAULT;\nuse support::{emmagui_support::GemmaImguiSupport, ui_state::ImGuiUiState};\n\nmod support;\n\n/// Keypad Mappings for my Linux box\n/// 1 2 3 C\n/// 4 5 6 D\n/// 7 8 9 E\n/// A 0 B F\n\nconst LIN_KEYS: [(u16, u8); 0x10] = [\n (537, 0x01),\n (538, 0x02),\n (539, 0x03),\n (540, 0x0c),\n (562, 0x04),\n (568, 0x05),\n (550, 0x06),\n (563, 0x0d),\n (546, 0x7),\n (564, 0x8),\n (549, 0x9),\n (551, 0xe),\n (571, 0xa),\n (569, 0x0),\n (548, 0xb),\n (567, 0xf),\n];\n\n#[derive(Parser)]\n#[command(version, about, long_about = None)]\nstruct GemmaImguiCLIOptions {\n roms_directory: Option\u003cPathBuf\u003e,\n}\n\nfn main() {\n pretty_env_logger::init();\n\n let cli_options = GemmaImguiCLIOptions::parse();\n\n let mut system = Chip8ComputerManager::default();\n println!(\"GOT A ROMS_DIRECTORY =\u003e [{:?}]\", cli_options.roms_directory);\n println!(\"{:?}\", std::env::var(\"PWD\"));\n let mut ui_state = if cli_options.roms_directory.is_none() {\n ImGuiUiState::new(Some(PathBuf::from(ROMPATH_DEFAULT)))\n } else {\n ImGuiUiState::new(cli_options.roms_directory)\n };\n\n support::simple_init(file!(), move |_, ui| {\n let current_time = Instant::now();\n let mut num_cycles = 0;\n\n // Key Checks\n let down_keys = ui.io().keys_down;\n\n // START DEBUG CODE TO DISPLAY WHAT KEYS WE TRAPPED\n for (idx, val) in down_keys.iter().enumerate() {\n if *val {\n println!(\"{idx} = {val}\");\n }\n }\n // END DEBUG CODE\n\n for (key_code, key_reg) in LIN_KEYS {\n if down_keys[key_code as usize] {\n system.press_key(key_reg);\n system.wait_for_instruction();\n } else {\n // do we need to release it?\n\n if system.is_key_pressed(key_reg) {\n system.release_key(key_reg);\n }\n }\n }\n\n let target_ms = ui_state.frame_time;\n let loop_start_time = Instant::now();\n while Instant::now().duration_since(current_time).as_millis() \u003c target_ms as u128\n \u0026\u0026 num_cycles \u003c ui_state.target_ips\n {\n if system.tick() {\n num_cycles += 1;\n }\n }\n let cycles_time = Instant::now().duration_since(loop_start_time);\n if num_cycles \u003e 0 {\n println!(\n \"Ran for {}ms and executed {}/{} cycles.\",\n cycles_time.as_millis(),\n num_cycles,\n ui_state.target_ips\n );\n }\n // GUI Parts\n if ui_state.show_video {\n GemmaImguiSupport::video_display(system.state(), \u0026ui_state, ui);\n }\n\n GemmaImguiSupport::system_controls(\u0026mut system, \u0026mut ui_state, ui);\n\n if ui_state.show_registers {\n GemmaImguiSupport::registers_view(system.state(), ui);\n }\n\n if ui_state.show_memory {\n let active_instruction = system.state().registers.peek_pc();\n GemmaImguiSupport::hex_memory_display(\n system.state().memory.clone(),\n (0x100, 0x10),\n active_instruction as i16,\n ui,\n );\n }\n\n if ui_state.show_keypad {\n GemmaImguiSupport::keypad_display(system.state(), ui);\n }\n });\n}\n","traces":[{"line":42,"address":[4454496,4455336,4455308],"length":1,"stats":{"Line":0}},{"line":43,"address":[4454503],"length":1,"stats":{"Line":0}},{"line":45,"address":[4454528],"length":1,"stats":{"Line":0}},{"line":47,"address":[4454546],"length":1,"stats":{"Line":0}},{"line":48,"address":[4454669,4454603],"length":1,"stats":{"Line":0}},{"line":49,"address":[4454738],"length":1,"stats":{"Line":0}},{"line":50,"address":[4454929],"length":1,"stats":{"Line":0}},{"line":51,"address":[4455201,4455007],"length":1,"stats":{"Line":0}},{"line":53,"address":[4454950,4455041],"length":1,"stats":{"Line":0}},{"line":56,"address":[4344512,4346125],"length":1,"stats":{"Line":0}},{"line":57,"address":[4344553],"length":1,"stats":{"Line":0}},{"line":58,"address":[4344588],"length":1,"stats":{"Line":0}},{"line":61,"address":[4344596],"length":1,"stats":{"Line":0}},{"line":64,"address":[4344627,4344862],"length":1,"stats":{"Line":0}},{"line":65,"address":[4344894],"length":1,"stats":{"Line":0}},{"line":66,"address":[4346147],"length":1,"stats":{"Line":0}},{"line":71,"address":[4344788,4344981,4344924,4345113],"length":1,"stats":{"Line":0}},{"line":72,"address":[4345147,4345974],"length":1,"stats":{"Line":0}},{"line":73,"address":[4346049],"length":1,"stats":{"Line":0}},{"line":74,"address":[4346109],"length":1,"stats":{"Line":0}},{"line":78,"address":[4346022,4346067],"length":1,"stats":{"Line":0}},{"line":79,"address":[4346085],"length":1,"stats":{"Line":0}},{"line":84,"address":[4345064],"length":1,"stats":{"Line":0}},{"line":85,"address":[4345081],"length":1,"stats":{"Line":0}},{"line":86,"address":[4345172],"length":1,"stats":{"Line":0}},{"line":87,"address":[4345350],"length":1,"stats":{"Line":0}},{"line":89,"address":[4345367,4345948],"length":1,"stats":{"Line":0}},{"line":90,"address":[4345925,4345953],"length":1,"stats":{"Line":0}},{"line":93,"address":[4345272],"length":1,"stats":{"Line":0}},{"line":94,"address":[4345336],"length":1,"stats":{"Line":0}},{"line":95,"address":[4345438],"length":1,"stats":{"Line":0}},{"line":97,"address":[4345417],"length":1,"stats":{"Line":0}},{"line":103,"address":[4345391],"length":1,"stats":{"Line":0}},{"line":104,"address":[4345698],"length":1,"stats":{"Line":0}},{"line":107,"address":[4345662],"length":1,"stats":{"Line":0}},{"line":109,"address":[4345682],"length":1,"stats":{"Line":0}},{"line":110,"address":[4345752],"length":1,"stats":{"Line":0}},{"line":113,"address":[4345736],"length":1,"stats":{"Line":0}},{"line":114,"address":[4345794],"length":1,"stats":{"Line":0}},{"line":115,"address":[4345863],"length":1,"stats":{"Line":0}},{"line":116,"address":[4345831],"length":1,"stats":{"Line":0}},{"line":123,"address":[4345778],"length":1,"stats":{"Line":0}},{"line":124,"address":[4345904],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":43},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaimgui","src","bin","support","emmagui_support.rs"],"content":"use crate::support::gui_file_list::GuiFileList;\nuse crate::ImGuiUiState;\nuse dimensioned::ucum::f32consts::CUP_M;\nuse gemma::chip8::computer::Chip8Computer;\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\nuse gemma::chip8::computer_manager::ManagerDumpables::{Keyboard, Registers, Video};\nuse gemma::chip8::quirk_modes::QuirkMode::{self, *};\nuse gemma::chip8::system_memory::Chip8SystemMemory;\nuse gemma::constants::CHIP8_KEYBOARD;\nuse imgui::{CollapsingHeader, Condition, ImColor32, Ui};\nuse log::debug;\nuse std::fs::File;\nuse std::io::Read;\nuse std::path::{Path, PathBuf};\n\nconst ROM_ROOT: \u0026str = \"resources/roms\";\n\npub struct GemmaImguiSupport {}\n\nconst CELL_WIDTH: i32 = 5i32;\nconst CELL_HEIGHT: i32 = 5i32;\n\nimpl GemmaImguiSupport {\n pub fn keypad_display(system_to_display: \u0026Chip8Computer, ui: \u0026Ui) {\n ui.window(\"Keypad\".to_string())\n .position([100.0, 640.0], Condition::FirstUseEver)\n .build(|| {\n for row in CHIP8_KEYBOARD {\n for key in row {\n let label = if system_to_display.keypad.pressed(key) {\n format!(\"*{:1x}*\", key)\n } else {\n format!(\"{:1x}\", key)\n };\n ui.text(\u0026label);\n ui.same_line();\n }\n ui.text(\"\");\n }\n });\n }\n\n pub fn video_display(system_to_control: \u0026Chip8Computer, gui_state: \u0026ImGuiUiState, ui: \u0026Ui) {\n // draw area size\n let (width, height) = system_to_control.video_memory.get_resolution();\n let draw_area_size = ui.io().display_size;\n // println!(\"DRAW_AREA_SIZE = {}x{}\", draw_area_size[0], draw_area_size[1]);\n let cell_width = ((draw_area_size[0] as i32 / width) * 6) / 10;\n let cell_height = ((draw_area_size[1] as i32 / height) * 6) / 10;\n\n let origin = ui.cursor_pos();\n let fg = ui.get_foreground_draw_list();\n if system_to_control.video_memory.is_highres() {\n // ui.text(\"High Def Video here\");\n for current_row in 0..=height {\n let y_offset = origin[1] as i32 + (current_row * cell_height);\n for current_column in 0..=width {\n let x_offset = origin[0] as i32 + (current_column * cell_width);\n let current_origin = [x_offset as f32, y_offset as f32];\n let current_limit = [\n (x_offset + cell_width) as f32,\n (y_offset + cell_height) as f32,\n ];\n let memory_offset = (current_row * width + current_column) as u16;\n let to_render = system_to_control.video_memory.peek(memory_offset);\n let color: ImColor32 = if to_render {\n gui_state.on_colour\n } else {\n gui_state.off_colour\n };\n fg.add_rect_filled_multicolor(\n current_origin,\n current_limit,\n color,\n color,\n color,\n color,\n );\n }\n }\n } else {\n for current_row in 0..height {\n let y_offset = origin[1] as i32 + (current_row * cell_height);\n for current_column in 0..width {\n let x_offset = origin[0] as i32 + (current_column * cell_width);\n let current_origin = [x_offset as f32, y_offset as f32];\n let current_limit = [\n (x_offset + cell_width) as f32,\n (y_offset + cell_height) as f32,\n ];\n let memory_offset = (current_row * width + current_column) as u16;\n let to_render = system_to_control.video_memory.peek(memory_offset);\n let color: ImColor32 = if to_render {\n gui_state.on_colour\n } else {\n gui_state.off_colour\n };\n fg.add_rect_filled_multicolor(\n current_origin,\n current_limit,\n color,\n color,\n color,\n color,\n );\n }\n }\n }\n }\n pub fn system_controls(\n system_to_control: \u0026mut Chip8ComputerManager,\n gui_state: \u0026mut ImGuiUiState,\n ui: \u0026Ui,\n ) {\n // let mut state: Chip8Computer = system_to_control;\n ui.window(\"!!!! CONTROLS !!!!\")\n .position([100.0, 640.0], Condition::FirstUseEver)\n .build(|| {\n /* System Step Counter */\n ui.text(format!(\"Step {:04x}\", system_to_control.num_cycles()).as_str());\n ui.text(format!(\"Mode {}\", system_to_control.quirks_mode()));\n /* ROM Lister */\n if CollapsingHeader::new(\"Roms\").build(ui) {\n let new_filename = GuiFileList::display_path(\n gui_state.roms_root_path.clone(),\n \u0026gui_state.filename_to_load,\n ui,\n );\n if !new_filename.is_empty() {\n if new_filename != gui_state.filename_to_load {\n debug!(\"NEW FILENAME SELECTED -\u003e {new_filename}\");\n gui_state.filename_to_load = new_filename;\n }\n if ui.button(\"Load Program\") {\n let mut buffer: Vec\u003cu8\u003e = Vec::new();\n let full_name = format!(\n \"{}/{}\",\n gui_state\n .roms_root_path\n .clone()\n .as_os_str()\n .to_string_lossy(),\n gui_state.filename_to_load.to_string()\n );\n debug!(\"PREPARING TO LOAD {}\", full_name);\n\n let input_file = File::open(Path::new(\u0026full_name));\n input_file\n .unwrap()\n .read_to_end(\u0026mut buffer)\n .expect(\"Unable to read rom.\");\n system_to_control.load_new_program_to_system_memory(buffer);\n }\n }\n }\n\n if CollapsingHeader::new(\"Controls\").build(ui) {\n // if the system has no program loaded hide the buttons.\n let bytes: [u8; 2] = [\n system_to_control.state().memory.peek(0x200),\n system_to_control.state().memory.peek(0x201),\n ];\n let show_buttons = bytes[0] != 0 || bytes[1] == 0xe0;\n\n if show_buttons {\n if ui.button(\"Step\") {\n system_to_control.step();\n };\n ui.same_line();\n if ui.button(\"Run\") {\n system_to_control.start();\n }\n }\n ui.same_line();\n if ui.button(\"Stop\") {\n system_to_control.stop();\n }\n ui.same_line();\n if ui.button(\"Reset\") {\n system_to_control.reset(system_to_control.quirks_mode());\n }\n if ui.button(\"Dump Video Memory\") {\n println!(\"{}\", system_to_control.dump_to_string(Video));\n }\n ui.same_line();\n if ui.button(\"Dump Keypad State\") {\n debug!(\"{}\", system_to_control.dump_to_string(Keyboard));\n }\n ui.same_line();\n if ui.button(\"Dump Registers\") {\n debug!(\"{}\", system_to_control.dump_to_string(Registers));\n }\n }\n\n if CollapsingHeader::new(\"Options\").build(ui) {\n ui.checkbox(\"Show Memory\", \u0026mut gui_state.show_memory);\n ui.same_line();\n ui.checkbox(\"Show Video\", \u0026mut gui_state.show_video);\n ui.same_line();\n ui.checkbox(\"Show Registers\", \u0026mut gui_state.show_registers);\n ui.same_line();\n ui.checkbox(\"Show Keypad\", \u0026mut gui_state.show_keypad);\n ui.input_int(\"Target IPS\", \u0026mut gui_state.target_ips)\n .build();\n };\n\n let selectors = [Chip8, SChipModern, XOChip];\n for current_selector in selectors {\n let mut working_selector =\n ui.selectable_config(current_selector.clone().to_string());\n match system_to_control.quirks_mode() {\n Chip8 =\u003e {\n working_selector = working_selector.selected(true);\n }\n SChipModern =\u003e {\n working_selector = working_selector.selected(true);\n }\n XOChip =\u003e {\n working_selector = working_selector.selected(true);\n }\n }\n if working_selector.build() {\n system_to_control.reset(current_selector);\n println!(\"CLICK ON {}\", \u0026current_selector);\n }\n }\n });\n }\n\n pub fn registers_view(system: \u0026Chip8Computer, ui: \u0026Ui) {\n ui.window(\"Registers\")\n .position([100.0, 640.0], Condition::FirstUseEver)\n .build(|| {\n ui.text(\"Registers\");\n for i in 1..0x10 {\n ui.text(format!(\"V{:X}: {}\", i, system.registers.peek(i)));\n if i != 7 {\n ui.same_line();\n }\n }\n ui.text(\"\");\n ui.text(format!(\"I: {:03X}\", system.registers.peek_i()));\n ui.same_line();\n ui.text(format!(\"ST: {:02X}\", system.sound_timer.current()));\n ui.same_line();\n ui.text(format!(\"DT: {:02X}\", system.delay_timer.current()));\n ui.text(format!(\"PC: {:02X}\", system.registers.peek_pc()));\n ui.text(format!(\"SP: {:02X}\", system.stack.depth()));\n });\n }\n\n pub fn hex_memory_display(\n bytes: Chip8SystemMemory,\n position: (i32, i32),\n active: i16,\n ui: \u0026Ui,\n ) {\n let rows = position.0;\n let cols = position.1;\n ui.window(\"System Memory\")\n .position([100.0, 640.0], Condition::FirstUseEver)\n .build(|| {\n let mut current_x_hover: i32 = 0;\n let mut current_y_hover: i32 = 0;\n // display a block of data\n for current_row in 0..rows {\n ui.text(format!(\"{:02x}\", current_row * cols));\n ui.same_line();\n for current_column in 0..cols {\n let data_offset = current_row * cols + current_column;\n let formatted_text = format!(\"{:02x}\", bytes.peek(data_offset as u16));\n let text_location = ui.cursor_screen_pos();\n let text_size = ui.calc_text_size(formatted_text.clone());\n let bounding_box = imgui::sys::ImVec2 {\n x: text_location[0] + text_size[0],\n y: text_location[1] + text_size[1],\n };\n\n let hovering = ui.is_mouse_hovering_rect(\n text_location,\n [bounding_box.x, bounding_box.y],\n );\n let is_active = data_offset == active as i32;\n\n ui.text_colored(\n if hovering {\n [0., 1., 1., 1.]\n } else if is_active {\n [1., 0., 1., 1.]\n } else {\n [1., 1., 0., 1.]\n },\n formatted_text.clone(),\n );\n\n // if we are hovering show that at the bottom...\n if hovering {\n // Optionally change the text color to indicate it's interactable\n current_x_hover = current_column;\n current_y_hover = current_row;\n\n // Check if the left mouse button is clicked while hovering over the text\n if ui.is_mouse_clicked(imgui::MouseButton::Left) {\n debug!(\n \"Offset: [{}] [0x{:02x}] Value: [{}]\",\n data_offset,\n data_offset,\n formatted_text.clone()\n );\n // Perform any action here, e.g., call a function, trigger an event, etc.\n }\n }\n\n // are we on the same line?\n if current_column != (cols - 1) {\n ui.same_line();\n }\n }\n }\n ui.text(format!(\n \"Offset 0x{:03x}\",\n current_x_hover * cols + current_y_hover\n ));\n });\n }\n}\n","traces":[{"line":24,"address":[4686944],"length":1,"stats":{"Line":0}},{"line":25,"address":[4687071,4686977],"length":1,"stats":{"Line":0}},{"line":26,"address":[4687029],"length":1,"stats":{"Line":0}},{"line":27,"address":[4840304,4841710],"length":1,"stats":{"Line":0}},{"line":28,"address":[4840321,4840546,4840468],"length":1,"stats":{"Line":0}},{"line":29,"address":[4840729,4840560,4840779],"length":1,"stats":{"Line":0}},{"line":30,"address":[4840793,4840855],"length":1,"stats":{"Line":0}},{"line":31,"address":[4841282,4840888,4841623],"length":1,"stats":{"Line":0}},{"line":33,"address":[4841257,4840861,4840918],"length":1,"stats":{"Line":0}},{"line":35,"address":[4841259],"length":1,"stats":{"Line":0}},{"line":36,"address":[4841672],"length":1,"stats":{"Line":0}},{"line":38,"address":[4840825],"length":1,"stats":{"Line":0}},{"line":43,"address":[4687152,4690125],"length":1,"stats":{"Line":0}},{"line":45,"address":[4687207],"length":1,"stats":{"Line":0}},{"line":46,"address":[4687253],"length":1,"stats":{"Line":0}},{"line":48,"address":[4687572,4687281],"length":1,"stats":{"Line":0}},{"line":49,"address":[4687518,4687885,4687602],"length":1,"stats":{"Line":0}},{"line":51,"address":[4687790],"length":1,"stats":{"Line":0}},{"line":52,"address":[4687831],"length":1,"stats":{"Line":0}},{"line":53,"address":[4687863,4687948],"length":1,"stats":{"Line":0}},{"line":55,"address":[4689099,4688005],"length":1,"stats":{"Line":0}},{"line":56,"address":[4689360,4689227],"length":1,"stats":{"Line":0}},{"line":57,"address":[4689353,4689397],"length":1,"stats":{"Line":0}},{"line":58,"address":[4689525,4689684],"length":1,"stats":{"Line":0}},{"line":59,"address":[4689645],"length":1,"stats":{"Line":0}},{"line":60,"address":[4689789],"length":1,"stats":{"Line":0}},{"line":61,"address":[4689743,4689671,4689720],"length":1,"stats":{"Line":0}},{"line":62,"address":[4689785,4689821,4689730],"length":1,"stats":{"Line":0}},{"line":64,"address":[4689807,4689850,4689926],"length":1,"stats":{"Line":0}},{"line":65,"address":[4689907,4689951],"length":1,"stats":{"Line":0}},{"line":66,"address":[4689985,4689963],"length":1,"stats":{"Line":0}},{"line":67,"address":[4689995],"length":1,"stats":{"Line":0}},{"line":69,"address":[4689975],"length":1,"stats":{"Line":0}},{"line":71,"address":[4690013],"length":1,"stats":{"Line":0}},{"line":74,"address":[4690005],"length":1,"stats":{"Line":0}},{"line":82,"address":[4687961,4688029,4688138],"length":1,"stats":{"Line":0}},{"line":83,"address":[4688333,4688173],"length":1,"stats":{"Line":0}},{"line":84,"address":[4688365,4688306],"length":1,"stats":{"Line":0}},{"line":85,"address":[4688636,4688474],"length":1,"stats":{"Line":0}},{"line":86,"address":[4688597],"length":1,"stats":{"Line":0}},{"line":87,"address":[4688747],"length":1,"stats":{"Line":0}},{"line":88,"address":[4688623,4688698,4688675],"length":1,"stats":{"Line":0}},{"line":89,"address":[4688743,4688779,4688685],"length":1,"stats":{"Line":0}},{"line":91,"address":[4688884,4688765,4688808],"length":1,"stats":{"Line":0}},{"line":92,"address":[4688909,4688865],"length":1,"stats":{"Line":0}},{"line":93,"address":[4688921,4688943],"length":1,"stats":{"Line":0}},{"line":94,"address":[4688953],"length":1,"stats":{"Line":0}},{"line":96,"address":[4688933],"length":1,"stats":{"Line":0}},{"line":98,"address":[4688971],"length":1,"stats":{"Line":0}},{"line":101,"address":[4688963],"length":1,"stats":{"Line":0}},{"line":110,"address":[4690160],"length":1,"stats":{"Line":0}},{"line":116,"address":[4690463,4690217,4690284],"length":1,"stats":{"Line":0}},{"line":117,"address":[4690242],"length":1,"stats":{"Line":0}},{"line":118,"address":[4848363,4841744,4845187],"length":1,"stats":{"Line":0}},{"line":120,"address":[4841759,4842170],"length":1,"stats":{"Line":0}},{"line":121,"address":[4842290,4842477],"length":1,"stats":{"Line":0}},{"line":123,"address":[4845226,4842490],"length":1,"stats":{"Line":0}},{"line":124,"address":[4842677],"length":1,"stats":{"Line":0}},{"line":125,"address":[4842613],"length":1,"stats":{"Line":0}},{"line":126,"address":[4842657],"length":1,"stats":{"Line":0}},{"line":127,"address":[4842654],"length":1,"stats":{"Line":0}},{"line":129,"address":[4842760,4842698],"length":1,"stats":{"Line":0}},{"line":130,"address":[4842774,4843433,4842830],"length":1,"stats":{"Line":0}},{"line":131,"address":[4842876,4843048],"length":1,"stats":{"Line":0}},{"line":132,"address":[4842942,4843320],"length":1,"stats":{"Line":0}},{"line":134,"address":[4842844,4845182,4843445],"length":1,"stats":{"Line":0}},{"line":135,"address":[4843454],"length":1,"stats":{"Line":0}},{"line":136,"address":[4843691,4843580,4843481,4843789],"length":1,"stats":{"Line":0}},{"line":138,"address":[4843489,4843637,4843557],"length":1,"stats":{"Line":0}},{"line":143,"address":[4843770],"length":1,"stats":{"Line":0}},{"line":145,"address":[4844353,4844445,4844225],"length":1,"stats":{"Line":0}},{"line":147,"address":[4844359,4844725],"length":1,"stats":{"Line":0}},{"line":148,"address":[4845039,4844935],"length":1,"stats":{"Line":0}},{"line":151,"address":[4845065,4844982],"length":1,"stats":{"Line":0}},{"line":152,"address":[4845092],"length":1,"stats":{"Line":0}},{"line":157,"address":[4842543],"length":1,"stats":{"Line":0}},{"line":159,"address":[4845383],"length":1,"stats":{"Line":0}},{"line":160,"address":[4845317],"length":1,"stats":{"Line":0}},{"line":161,"address":[4845355],"length":1,"stats":{"Line":0}},{"line":163,"address":[4845397],"length":1,"stats":{"Line":0}},{"line":165,"address":[4845437],"length":1,"stats":{"Line":0}},{"line":166,"address":[4845513],"length":1,"stats":{"Line":0}},{"line":167,"address":[4845599],"length":1,"stats":{"Line":0}},{"line":169,"address":[4845545],"length":1,"stats":{"Line":0}},{"line":170,"address":[4845562],"length":1,"stats":{"Line":0}},{"line":171,"address":[4845619],"length":1,"stats":{"Line":0}},{"line":174,"address":[4845455],"length":1,"stats":{"Line":0}},{"line":175,"address":[4845472],"length":1,"stats":{"Line":0}},{"line":176,"address":[4845693],"length":1,"stats":{"Line":0}},{"line":178,"address":[4845642],"length":1,"stats":{"Line":0}},{"line":179,"address":[4845659],"length":1,"stats":{"Line":0}},{"line":180,"address":[4845747],"length":1,"stats":{"Line":0}},{"line":182,"address":[4845713],"length":1,"stats":{"Line":0}},{"line":183,"address":[4845848],"length":1,"stats":{"Line":0}},{"line":185,"address":[4845790],"length":1,"stats":{"Line":0}},{"line":186,"address":[4845807],"length":1,"stats":{"Line":0}},{"line":187,"address":[4846104,4846171],"length":1,"stats":{"Line":0}},{"line":189,"address":[4846054],"length":1,"stats":{"Line":0}},{"line":190,"address":[4846071],"length":1,"stats":{"Line":0}},{"line":191,"address":[4846585,4846515],"length":1,"stats":{"Line":0}},{"line":195,"address":[4845247],"length":1,"stats":{"Line":0}},{"line":196,"address":[4847150],"length":1,"stats":{"Line":0}},{"line":197,"address":[4847182],"length":1,"stats":{"Line":0}},{"line":198,"address":[4847199],"length":1,"stats":{"Line":0}},{"line":199,"address":[4847231],"length":1,"stats":{"Line":0}},{"line":200,"address":[4847248],"length":1,"stats":{"Line":0}},{"line":201,"address":[4847280],"length":1,"stats":{"Line":0}},{"line":202,"address":[4847297],"length":1,"stats":{"Line":0}},{"line":203,"address":[4847329],"length":1,"stats":{"Line":0}},{"line":207,"address":[4846929],"length":1,"stats":{"Line":0}},{"line":208,"address":[4847387,4847442,4846995,4847503,4848196],"length":1,"stats":{"Line":0}},{"line":210,"address":[4847517],"length":1,"stats":{"Line":0}},{"line":211,"address":[4847686,4847622],"length":1,"stats":{"Line":0}},{"line":213,"address":[4847994,4847733],"length":1,"stats":{"Line":0}},{"line":215,"address":[4848173],"length":1,"stats":{"Line":0}},{"line":216,"address":[4847907,4848139],"length":1,"stats":{"Line":0}},{"line":218,"address":[4848137],"length":1,"stats":{"Line":0}},{"line":219,"address":[4848103,4847820],"length":1,"stats":{"Line":0}},{"line":222,"address":[4848182,4848028],"length":1,"stats":{"Line":0}},{"line":223,"address":[4848209],"length":1,"stats":{"Line":0}},{"line":224,"address":[4848240],"length":1,"stats":{"Line":0}},{"line":230,"address":[4690496],"length":1,"stats":{"Line":0}},{"line":231,"address":[4690592,4690528],"length":1,"stats":{"Line":0}},{"line":232,"address":[4690550],"length":1,"stats":{"Line":0}},{"line":233,"address":[4848400],"length":1,"stats":{"Line":0}},{"line":234,"address":[4848417],"length":1,"stats":{"Line":0}},{"line":235,"address":[4850476,4848439],"length":1,"stats":{"Line":0}},{"line":236,"address":[4850484,4850714],"length":1,"stats":{"Line":0}},{"line":237,"address":[4850724],"length":1,"stats":{"Line":0}},{"line":238,"address":[4850735],"length":1,"stats":{"Line":0}},{"line":241,"address":[4848503],"length":1,"stats":{"Line":0}},{"line":242,"address":[4848522,4848894],"length":1,"stats":{"Line":0}},{"line":243,"address":[4848907],"length":1,"stats":{"Line":0}},{"line":244,"address":[4849289,4848918],"length":1,"stats":{"Line":0}},{"line":245,"address":[4849302],"length":1,"stats":{"Line":0}},{"line":246,"address":[4849313,4849685],"length":1,"stats":{"Line":0}},{"line":247,"address":[4850070,4849698],"length":1,"stats":{"Line":0}},{"line":248,"address":[4850083,4850455],"length":1,"stats":{"Line":0}},{"line":252,"address":[4690672,4690985],"length":1,"stats":{"Line":0}},{"line":258,"address":[4690723],"length":1,"stats":{"Line":0}},{"line":259,"address":[4690727],"length":1,"stats":{"Line":0}},{"line":260,"address":[4690829,4690959,4690731],"length":1,"stats":{"Line":0}},{"line":261,"address":[4690799],"length":1,"stats":{"Line":0}},{"line":262,"address":[4690896],"length":1,"stats":{"Line":0}},{"line":263,"address":[4850767],"length":1,"stats":{"Line":0}},{"line":264,"address":[4850778],"length":1,"stats":{"Line":0}},{"line":266,"address":[4850789,4850913],"length":1,"stats":{"Line":0}},{"line":267,"address":[4851422,4851771,4851840,4850934],"length":1,"stats":{"Line":0}},{"line":268,"address":[4851792],"length":1,"stats":{"Line":0}},{"line":269,"address":[4851856,4851810],"length":1,"stats":{"Line":0}},{"line":270,"address":[4851937,4852429],"length":1,"stats":{"Line":0}},{"line":271,"address":[4852028],"length":1,"stats":{"Line":0}},{"line":272,"address":[4852409,4852502],"length":1,"stats":{"Line":0}},{"line":273,"address":[4852526],"length":1,"stats":{"Line":0}},{"line":274,"address":[4852700],"length":1,"stats":{"Line":0}},{"line":275,"address":[4852616],"length":1,"stats":{"Line":0}},{"line":276,"address":[4852652],"length":1,"stats":{"Line":0}},{"line":279,"address":[4852718,4852740],"length":1,"stats":{"Line":0}},{"line":281,"address":[4852722],"length":1,"stats":{"Line":0}},{"line":283,"address":[4852839],"length":1,"stats":{"Line":0}},{"line":285,"address":[4853141,4852870],"length":1,"stats":{"Line":0}},{"line":286,"address":[4852960,4852879],"length":1,"stats":{"Line":0}},{"line":287,"address":[4852897],"length":1,"stats":{"Line":0}},{"line":288,"address":[4853028,4852887],"length":1,"stats":{"Line":0}},{"line":289,"address":[4853030],"length":1,"stats":{"Line":0}},{"line":291,"address":[4852965],"length":1,"stats":{"Line":0}},{"line":293,"address":[4853093],"length":1,"stats":{"Line":0}},{"line":297,"address":[4853152],"length":1,"stats":{"Line":0}},{"line":299,"address":[4853214],"length":1,"stats":{"Line":0}},{"line":300,"address":[4853221],"length":1,"stats":{"Line":0}},{"line":303,"address":[4853228],"length":1,"stats":{"Line":0}},{"line":304,"address":[4853370,4853499,4853273],"length":1,"stats":{"Line":0}},{"line":308,"address":[4853472],"length":1,"stats":{"Line":0}},{"line":315,"address":[4853166,4854382],"length":1,"stats":{"Line":0}},{"line":316,"address":[4854435],"length":1,"stats":{"Line":0}},{"line":320,"address":[4851378,4850865,4851036],"length":1,"stats":{"Line":0}},{"line":322,"address":[4851399,4850981,4850877],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":177},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaimgui","src","bin","support","gui_file_list.rs"],"content":"use std::ffi::OsString;\nuse std::fs::read_dir;\nuse std::path::PathBuf;\nuse imgui::Ui;\nuse log::debug;\n\npub struct GuiFileList {}\n\nimpl GuiFileList {\n pub fn display_path(root: PathBuf, selected_filename: \u0026String, ui: \u0026Ui) -\u003e String {\n let mut working_filename = selected_filename.clone();\n ui.text(format!(\"Displaying {}\", root.to_str().unwrap_or(\"Unable to parse path\")));\n\n let mut known_files: Vec\u003cOsString\u003e = vec![];\n\n // println!(\"STARTING AT {:?}\", std::env::current_dir());\n for entry in read_dir(root.as_path()).unwrap() {\n known_files.push(entry.unwrap().file_name());\n }\n\n known_files.sort();\n\n for (index, entry) in known_files.iter().enumerate() {\n let mut working_select = ui.selectable_config(entry.clone().into_string().unwrap().to_string());\n if *entry.to_str().unwrap() == *selected_filename.as_str() {\n working_select = working_select.selected(true);\n }\n if working_select.build() {\n debug!(\"SELECTED {index} / {entry:?}\");\n working_filename = entry.clone().into_string().unwrap();\n };\n }\n working_filename\n }\n}\n","traces":[{"line":10,"address":[3799608,3796000,3799758],"length":1,"stats":{"Line":0}},{"line":11,"address":[3796074,3796161],"length":1,"stats":{"Line":0}},{"line":12,"address":[3796246,3796520,3796169],"length":1,"stats":{"Line":0}},{"line":14,"address":[3796527],"length":1,"stats":{"Line":0}},{"line":17,"address":[3797141,3796554,3797112,3796709,3796953],"length":1,"stats":{"Line":0}},{"line":18,"address":[3799617],"length":1,"stats":{"Line":0}},{"line":21,"address":[3797389],"length":1,"stats":{"Line":0}},{"line":23,"address":[3797435,3798765,3797769],"length":1,"stats":{"Line":0}},{"line":24,"address":[3798126,3797801,3797878],"length":1,"stats":{"Line":0}},{"line":25,"address":[3798302,3798742,3798491],"length":1,"stats":{"Line":0}},{"line":26,"address":[3798624],"length":1,"stats":{"Line":0}},{"line":28,"address":[3798549,3798751,3799585],"length":1,"stats":{"Line":0}},{"line":29,"address":[3798770,3798890],"length":1,"stats":{"Line":0}},{"line":30,"address":[3799427,3799198,3798822],"length":1,"stats":{"Line":0}},{"line":33,"address":[3797719],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":15},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaimgui","src","bin","support","mod.rs"],"content":"use glium::glutin::surface::WindowSurface;\nuse glium::{Display, Surface};\nuse imgui::{Context, FontConfig, FontGlyphRanges, FontSource, Ui};\nuse imgui_glium_renderer::Renderer;\nuse imgui_winit_support::winit::event::{Event, WindowEvent};\nuse imgui_winit_support::winit::event_loop::EventLoop;\nuse imgui_winit_support::winit::window::WindowBuilder;\nuse imgui_winit_support::{HiDpiMode, WinitPlatform};\nuse std::path::Path;\nuse std::time::Instant;\n\npub mod ui_state;\npub mod emmagui_support;\nmod gui_file_list;\n\nuse copypasta::{ClipboardContext, ClipboardProvider};\nuse imgui::ClipboardBackend;\n\npub struct ClipboardSupport(pub ClipboardContext);\n\npub fn clipboard_init() -\u003e Option\u003cClipboardSupport\u003e {\n ClipboardContext::new().ok().map(ClipboardSupport)\n}\n\nimpl ClipboardBackend for ClipboardSupport {\n fn get(\u0026mut self) -\u003e Option\u003cString\u003e {\n self.0.get_contents().ok()\n }\n fn set(\u0026mut self, text: \u0026str) {\n // ignore errors?\n let _ = self.0.set_contents(text.to_owned());\n }\n}\n\npub const FONT_SIZE: f32 = 13.0;\n\n#[allow(dead_code)] // annoyingly, RA yells that this is unusued\npub fn simple_init\u003cF: FnMut(\u0026mut bool, \u0026mut Ui) + 'static\u003e(title: \u0026str, run_ui: F) {\n init_with_startup(title, |_, _, _| {}, run_ui);\n}\n\npub fn init_with_startup\u003cFInit, FUi\u003e(title: \u0026str, mut startup: FInit, mut run_ui: FUi)\nwhere\n FInit: FnMut(\u0026mut Context, \u0026mut Renderer, \u0026Display\u003cWindowSurface\u003e) + 'static,\n FUi: FnMut(\u0026mut bool, \u0026mut Ui) + 'static,\n{\n let mut imgui = create_context();\n\n let title = match Path::new(\u0026title).file_name() {\n Some(file_name) =\u003e file_name.to_str().unwrap(),\n None =\u003e title,\n };\n let event_loop = EventLoop::new().expect(\"Failed to create EventLoop\");\n\n let builder = WindowBuilder::new()\n .with_maximized(true)\n .with_title(title);\n // .with_inner_size(LogicalSize::new(1024, 768));\n let (window, display) = glium::backend::glutin::SimpleWindowBuilder::new()\n .set_window_builder(builder)\n .build(\u0026event_loop);\n let mut renderer = Renderer::init(\u0026mut imgui, \u0026display).expect(\"Failed to initialize renderer\");\n\n if let Some(backend) = clipboard_init() {\n imgui.set_clipboard_backend(backend);\n } else {\n eprintln!(\"Failed to initialize clipboard\");\n }\n\n let mut platform = WinitPlatform::init(\u0026mut imgui);\n {\n let dpi_mode = if let Ok(factor) = std::env::var(\"IMGUI_EXAMPLE_FORCE_DPI_FACTOR\") {\n // Allow forcing of HiDPI factor for debugging purposes\n match factor.parse::\u003cf64\u003e() {\n Ok(f) =\u003e HiDpiMode::Locked(f),\n Err(e) =\u003e panic!(\"Invalid scaling factor: {}\", e),\n }\n } else {\n HiDpiMode::Default\n };\n\n platform.attach_window(imgui.io_mut(), \u0026window, dpi_mode);\n }\n\n let mut last_frame = Instant::now();\n\n startup(\u0026mut imgui, \u0026mut renderer, \u0026display);\n\n event_loop\n .run(move |event, window_target| match event {\n Event::NewEvents(_) =\u003e {\n let now = Instant::now();\n imgui.io_mut().update_delta_time(now - last_frame);\n last_frame = now;\n }\n Event::AboutToWait =\u003e {\n platform\n .prepare_frame(imgui.io_mut(), \u0026window)\n .expect(\"Failed to prepare frame\");\n window.request_redraw();\n }\n Event::WindowEvent {\n event: WindowEvent::RedrawRequested,\n ..\n } =\u003e {\n let ui = imgui.frame();\n\n let mut run = true;\n run_ui(\u0026mut run, ui);\n if !run {\n window_target.exit();\n }\n\n let mut target = display.draw();\n target.clear_color_srgb(1.0, 1.0, 1.0, 1.0);\n platform.prepare_render(ui, \u0026window);\n let draw_data = imgui.render();\n renderer\n .render(\u0026mut target, draw_data)\n .expect(\"Rendering failed\");\n target.finish().expect(\"Failed to swap buffers\");\n }\n Event::WindowEvent {\n event: WindowEvent::Resized(new_size),\n ..\n } =\u003e {\n if new_size.width \u003e 0 \u0026\u0026 new_size.height \u003e 0 {\n display.resize((new_size.width, new_size.height));\n }\n platform.handle_event(imgui.io_mut(), \u0026window, \u0026event);\n }\n Event::WindowEvent {\n event: WindowEvent::CloseRequested,\n ..\n } =\u003e window_target.exit(),\n event =\u003e {\n platform.handle_event(imgui.io_mut(), \u0026window, \u0026event);\n }\n })\n .expect(\"EventLoop error\");\n}\n\n/// Creates the imgui context\npub fn create_context() -\u003e imgui::Context {\n let mut imgui = Context::create();\n // Fixed font size. Note imgui_winit_support uses \"logical\n // pixels\", which are physical pixels scaled by the devices\n // scaling factor. Meaning, 13.0 pixels should look the same size\n // on two different screens, and thus we do not need to scale this\n // value (as the scaling is handled by winit)\n imgui.fonts().add_font(\u0026[\n FontSource::TtfData {\n data: include_bytes!(\"../../../../resources/Roboto-Regular.ttf\"),\n size_pixels: FONT_SIZE,\n config: Some(FontConfig {\n // As imgui-glium-renderer isn't gamma-correct with\n // it's font rendering, we apply an arbitrary\n // multiplier to make the font a bit \"heavier\". With\n // default imgui-glow-renderer this is unnecessary.\n rasterizer_multiply: 1.5,\n // Oversampling font helps improve text rendering at\n // expense of larger font atlas texture.\n oversample_h: 4,\n oversample_v: 4,\n ..FontConfig::default()\n }),\n },\n FontSource::TtfData {\n data: include_bytes!(\"../../../../resources/mplus-1p-regular.ttf\"),\n size_pixels: FONT_SIZE,\n config: Some(FontConfig {\n // Oversampling font helps improve text rendering at\n // expense of larger font atlas texture.\n oversample_h: 4,\n oversample_v: 4,\n // Range of glyphs to rasterize\n glyph_ranges: FontGlyphRanges::japanese(),\n ..FontConfig::default()\n }),\n },\n ]);\n imgui.set_ini_filename(None);\n\n imgui\n}\n","traces":[{"line":21,"address":[4572528],"length":1,"stats":{"Line":0}},{"line":22,"address":[4572545],"length":1,"stats":{"Line":0}},{"line":26,"address":[4572608],"length":1,"stats":{"Line":0}},{"line":27,"address":[4572627],"length":1,"stats":{"Line":0}},{"line":29,"address":[4572672],"length":1,"stats":{"Line":0}},{"line":31,"address":[4572695],"length":1,"stats":{"Line":0}},{"line":38,"address":[],"length":0,"stats":{"Line":0}},{"line":39,"address":[],"length":0,"stats":{"Line":0}},{"line":42,"address":[4554402,4551440,4553419],"length":1,"stats":{"Line":0}},{"line":47,"address":[],"length":0,"stats":{"Line":0}},{"line":49,"address":[4551652,4551745],"length":1,"stats":{"Line":0}},{"line":50,"address":[4551867,4552053],"length":1,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":53,"address":[],"length":0,"stats":{"Line":0}},{"line":55,"address":[],"length":0,"stats":{"Line":0}},{"line":57,"address":[4552208,4552251],"length":1,"stats":{"Line":0}},{"line":59,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":0}},{"line":61,"address":[],"length":0,"stats":{"Line":0}},{"line":62,"address":[4552527,4552575],"length":1,"stats":{"Line":0}},{"line":64,"address":[],"length":0,"stats":{"Line":0}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":67,"address":[],"length":0,"stats":{"Line":0}},{"line":70,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":0}},{"line":74,"address":[],"length":0,"stats":{"Line":0}},{"line":75,"address":[],"length":0,"stats":{"Line":0}},{"line":76,"address":[4553250,4553341],"length":1,"stats":{"Line":0}},{"line":79,"address":[],"length":0,"stats":{"Line":0}},{"line":82,"address":[],"length":0,"stats":{"Line":0}},{"line":85,"address":[],"length":0,"stats":{"Line":0}},{"line":87,"address":[],"length":0,"stats":{"Line":0}},{"line":89,"address":[],"length":0,"stats":{"Line":0}},{"line":90,"address":[],"length":0,"stats":{"Line":0}},{"line":91,"address":[],"length":0,"stats":{"Line":0}},{"line":92,"address":[],"length":0,"stats":{"Line":0}},{"line":93,"address":[4554930],"length":1,"stats":{"Line":0}},{"line":94,"address":[4555045],"length":1,"stats":{"Line":0}},{"line":96,"address":[],"length":0,"stats":{"Line":0}},{"line":97,"address":[],"length":0,"stats":{"Line":0}},{"line":98,"address":[],"length":0,"stats":{"Line":0}},{"line":99,"address":[],"length":0,"stats":{"Line":0}},{"line":100,"address":[],"length":0,"stats":{"Line":0}},{"line":102,"address":[],"length":0,"stats":{"Line":0}},{"line":103,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[],"length":0,"stats":{"Line":0}},{"line":105,"address":[],"length":0,"stats":{"Line":0}},{"line":106,"address":[],"length":0,"stats":{"Line":0}},{"line":108,"address":[],"length":0,"stats":{"Line":0}},{"line":109,"address":[],"length":0,"stats":{"Line":0}},{"line":110,"address":[],"length":0,"stats":{"Line":0}},{"line":111,"address":[],"length":0,"stats":{"Line":0}},{"line":114,"address":[],"length":0,"stats":{"Line":0}},{"line":115,"address":[4555417],"length":1,"stats":{"Line":0}},{"line":116,"address":[],"length":0,"stats":{"Line":0}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":118,"address":[4555587],"length":1,"stats":{"Line":0}},{"line":119,"address":[],"length":0,"stats":{"Line":0}},{"line":120,"address":[],"length":0,"stats":{"Line":0}},{"line":121,"address":[4555651],"length":1,"stats":{"Line":0}},{"line":123,"address":[],"length":0,"stats":{"Line":0}},{"line":124,"address":[],"length":0,"stats":{"Line":0}},{"line":125,"address":[],"length":0,"stats":{"Line":0}},{"line":126,"address":[],"length":0,"stats":{"Line":0}},{"line":127,"address":[],"length":0,"stats":{"Line":0}},{"line":128,"address":[],"length":0,"stats":{"Line":0}},{"line":130,"address":[4555180,4555284],"length":1,"stats":{"Line":0}},{"line":132,"address":[],"length":0,"stats":{"Line":0}},{"line":133,"address":[],"length":0,"stats":{"Line":0}},{"line":134,"address":[],"length":0,"stats":{"Line":0}},{"line":135,"address":[],"length":0,"stats":{"Line":0}},{"line":136,"address":[],"length":0,"stats":{"Line":0}},{"line":137,"address":[],"length":0,"stats":{"Line":0}},{"line":144,"address":[4572752,4574300],"length":1,"stats":{"Line":0}},{"line":145,"address":[4572769],"length":1,"stats":{"Line":0}},{"line":151,"address":[4572793,4573894],"length":1,"stats":{"Line":0}},{"line":152,"address":[4573192],"length":1,"stats":{"Line":0}},{"line":155,"address":[4572869],"length":1,"stats":{"Line":0}},{"line":165,"address":[4572850],"length":1,"stats":{"Line":0}},{"line":168,"address":[4573760],"length":1,"stats":{"Line":0}},{"line":171,"address":[4573445],"length":1,"stats":{"Line":0}},{"line":177,"address":[4573326],"length":1,"stats":{"Line":0}},{"line":178,"address":[4573411],"length":1,"stats":{"Line":0}},{"line":182,"address":[4574239],"length":1,"stats":{"Line":0}},{"line":184,"address":[4574272],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":85},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaimgui","src","bin","support","ui_state.rs"],"content":"use imgui::ImColor32;\nuse std::path::PathBuf;\nuse std::time::Instant;\n\npub const ROMPATH_DEFAULT: \u0026str = \"resources/roms\";\n\npub struct ImGuiUiState {\n pub show_registers: bool,\n pub show_memory: bool,\n pub show_video: bool,\n pub show_keypad: bool,\n pub filename_to_load: String,\n pub on_colour: ImColor32,\n pub off_colour: ImColor32,\n pub is_running: bool,\n pub frame_time: u32,\n pub last_frame_instant: Instant,\n pub target_ips: i32,\n pub roms_root_path: PathBuf\n}\n\nimpl Clone for ImGuiUiState {\n fn clone(\u0026self) -\u003e Self {\n ImGuiUiState {\n show_registers: self.show_registers,\n show_memory: self.show_memory,\n show_video: self.show_video,\n show_keypad: self.show_keypad,\n filename_to_load: self.filename_to_load.to_string(),\n on_colour: self.on_colour,\n off_colour: self.off_colour,\n is_running: self.is_running,\n frame_time: self.frame_time,\n last_frame_instant: self.last_frame_instant,\n target_ips: self.target_ips,\n roms_root_path: self.roms_root_path.to_owned()\n }\n }\n}\n\nimpl Default for ImGuiUiState {\n fn default() -\u003e Self {\n ImGuiUiState {\n show_registers: false,\n show_memory: false,\n show_video: true,\n show_keypad: true,\n filename_to_load: String::new(),\n on_colour: ImColor32::from_rgb(0xff, 0xff, 0x00),\n off_colour: ImColor32::from_rgb(0x00, 0xff, 0xff),\n is_running: false,\n frame_time: 16,\n last_frame_instant: Instant::now(),\n target_ips: 200000,\n roms_root_path: PathBuf::from(ROMPATH_DEFAULT)\n }\n }\n}\n\nimpl ImGuiUiState {\n pub fn new(rom_path: Option\u003cPathBuf\u003e) -\u003e ImGuiUiState {\n let unwrapped = rom_path.clone().unwrap_or(PathBuf::from(ROMPATH_DEFAULT));\n\n println!(\"UNWRAPPED =\u003e [{:?}]\", unwrapped);\n\n ImGuiUiState {\n roms_root_path: rom_path.unwrap_or(PathBuf::from(ROMPATH_DEFAULT)),\n ..Default::default()\n }\n }\n}\n","traces":[{"line":23,"address":[4275344,4275712],"length":1,"stats":{"Line":0}},{"line":25,"address":[4275376],"length":1,"stats":{"Line":0}},{"line":26,"address":[4275383],"length":1,"stats":{"Line":0}},{"line":27,"address":[4275390],"length":1,"stats":{"Line":0}},{"line":28,"address":[4275397],"length":1,"stats":{"Line":0}},{"line":29,"address":[4275409],"length":1,"stats":{"Line":0}},{"line":30,"address":[4275419],"length":1,"stats":{"Line":0}},{"line":31,"address":[4275426],"length":1,"stats":{"Line":0}},{"line":32,"address":[4275433],"length":1,"stats":{"Line":0}},{"line":33,"address":[4275440],"length":1,"stats":{"Line":0}},{"line":34,"address":[4275447],"length":1,"stats":{"Line":0}},{"line":35,"address":[4275463],"length":1,"stats":{"Line":0}},{"line":36,"address":[4275470],"length":1,"stats":{"Line":0}},{"line":42,"address":[4275744,4276041],"length":1,"stats":{"Line":0}},{"line":48,"address":[4275763],"length":1,"stats":{"Line":0}},{"line":49,"address":[4275775],"length":1,"stats":{"Line":0}},{"line":50,"address":[4275830],"length":1,"stats":{"Line":0}},{"line":53,"address":[4275843],"length":1,"stats":{"Line":0}},{"line":55,"address":[4275880],"length":1,"stats":{"Line":0}},{"line":61,"address":[4276064,4276902,4276952],"length":1,"stats":{"Line":0}},{"line":62,"address":[4276174,4276933,4276085],"length":1,"stats":{"Line":0}},{"line":64,"address":[4276347,4276299],"length":1,"stats":{"Line":0}},{"line":67,"address":[4276414,4276911],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":23},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmasdl2","src","bin","gemmasdl2.rs"],"content":"mod support;\nuse crate::support::gemma_egui_support::GemmaEguiSupport;\nuse anyhow::Context;\nuse egui::TextBuffer;\nuse egui_glow::glow::{HasContext, COLOR_BUFFER_BIT};\nuse egui_sdl2_platform::sdl2;\nuse gemma::chip8::computer::Chip8Computer;\nuse sdl2::event::{Event, WindowEvent};\nuse std::{sync::Arc, time::Instant};\nuse support::timestep::TimeStep;\n\nconst SCREEN_WIDTH: u32 = 800;\nconst SCREEN_HEIGHT: u32 = 480;\n\nasync fn run() -\u003e anyhow::Result\u003cString\u003e {\n // Initialize SDL2 and video subsystem\n let sdl = sdl2::init().map_err(|e| anyhow::anyhow!(\"Failed to create SDL context: {}\", e))?;\n let mut video = sdl\n .video()\n .map_err(|e| anyhow::anyhow!(\"Failed to initialize SDL video subsystem: {}\", e))?;\n\n // Create SDL2 window and OpenGL context\n let window = video\n .window(\"Window\", SCREEN_WIDTH, SCREEN_HEIGHT)\n .opengl()\n .position_centered()\n .build()?;\n let _gl_context = window\n .gl_create_context()\n .expect(\"Failed to create GL context\");\n\n // Load OpenGL functions\n let gl = unsafe {\n egui_glow::painter::Context::from_loader_function(|name| {\n video.gl_get_proc_address(name) as *const _\n })\n };\n let mut painter = egui_glow::Painter::new(Arc::new(gl), \"\", None)?;\n\n // Setup Egui and SDL2 platform\n let mut platform = egui_sdl2_platform::Platform::new(window.size())?;\n let mut event_pump = sdl\n .event_pump()\n .map_err(|e| anyhow::anyhow!(\"Failed to get SDL event pump: {}\", e))?;\n\n // Initial settings\n let color = [0.0, 0.0, 0.0, 1.0]; // Background color\n let start_time = Instant::now();\n let mut timestep = TimeStep::new();\n\n let mut computer = Chip8Computer::new();\n let mut is_running: bool = false;\n computer.load_bytes_to_memory(0x200, \u0026std::fs::read(\"resources/roms/3-corax+.ch8\")?);\n\n // Main loop\n 'main: loop {\n // Update the Egui platform with the current time\n platform.update_time(start_time.elapsed().as_secs_f64());\n\n // Begin Egui frame\n let ctx = platform.context();\n\n if is_running {\n computer.step_system();\n }\n\n egui::Window::new(\"Hello, world!\").show(\u0026ctx, |ui| {\n GemmaEguiSupport::video_view(\u0026computer, ui);\n GemmaEguiSupport::memory_view(\u0026computer, ui);\n GemmaEguiSupport::registers_view(\u0026computer, ui);\n });\n\n // Process Egui frame\n let full_output = platform.end_frame(\u0026mut video)?;\n let paint_jobs = platform.tessellate(\u0026full_output);\n\n // Clear the screen with the current color\n unsafe {\n painter.gl().clear_color(color[0], color[1], color[2], 1.0);\n painter.gl().clear(COLOR_BUFFER_BIT);\n }\n\n // Paint Egui outputs and update textures\n let size = window.size();\n painter.paint_and_update_textures(\n [size.0, size.1],\n 1.0,\n paint_jobs.as_slice(),\n \u0026full_output.textures_delta,\n );\n window.gl_swap_window();\n\n // Run the timestep logic\n timestep.run_this(|_| {});\n\n // Handle SDL2 events\n for event in event_pump.poll_iter() {\n match event {\n Event::Quit { .. }\n | Event::KeyDown {\n keycode: Some(sdl2::keyboard::Keycode::Escape),\n ..\n } =\u003e break 'main,\n Event::Window {\n window_id,\n win_event,\n ..\n } if window_id == window.id() =\u003e {\n if let WindowEvent::Close = win_event {\n break 'main;\n }\n }\n Event::KeyUp {\n keycode: Some(sdl2::keyboard::Keycode::F3),\n ..\n } =\u003e {\n println!(\"USER PRESSED F3 -\u003e running\");\n is_running = true;\n }\n Event::KeyUp {\n keycode: Some(sdl2::keyboard::Keycode::F4),\n ..\n } =\u003e {\n println!(\"USER PRESSED F4 -\u003e stopping\");\n is_running = false;\n }\n Event::KeyDown {\n keycode: Some(sdl2::keyboard::Keycode::F5),\n ..\n } =\u003e {\n println!(\"USER PRESSED F5 -\u003e Step\");\n computer.step_system();\n }\n Event::KeyDown {\n keycode: Some(sdl2::keyboard::Keycode::F6),\n ..\n } =\u003e {\n println!(\"USER PRESSED F6 -\u003e RESET\");\n computer.reset(computer.quirk_mode.clone());\n }\n Event::ControllerButtonDown { which, .. } =\u003e {\n println!(\"PLAYER {which} DOWN\");\n }\n Event::JoyButtonDown { button_idx, .. } =\u003e {\n println!(\"JoyButtonDown {}\", button_idx);\n }\n Event::JoyAxisMotion {\n which, axis_idx, ..\n } =\u003e {\n println!(\"JoyAxismotion {which} {axis_idx}\");\n }\n _ =\u003e platform.handle_event(\u0026event, \u0026sdl, \u0026video),\n }\n }\n\n // Optionally log the frame rate\n if let Some(fps) = timestep.frame_rate() {\n println!(\"{:?} fps\", fps);\n }\n\n let num_js = sdl.joystick().unwrap().num_joysticks().unwrap();\n println!(\"NUM JS = {num_js}\");\n }\n\n Ok((\"\").parse()?)\n}\n\nfn main() -\u003e anyhow::Result\u003c()\u003e {\n pollster::block_on(run())?;\n Ok(())\n}\n","traces":[{"line":15,"address":[907230,911746,907173,913940,907104],"length":1,"stats":{"Line":0}},{"line":17,"address":[914147,913999,907412,907223,907306,913935,913984],"length":1,"stats":{"Line":0}},{"line":18,"address":[907405,913933,907684,907513],"length":1,"stats":{"Line":0}},{"line":20,"address":[914224,914387,914239,907668],"length":1,"stats":{"Line":0}},{"line":23,"address":[908008,907617,907829,907753],"length":1,"stats":{"Line":0}},{"line":28,"address":[908085],"length":1,"stats":{"Line":0}},{"line":34,"address":[914464,908186],"length":1,"stats":{"Line":0}},{"line":35,"address":[914482],"length":1,"stats":{"Line":0}},{"line":38,"address":[908490,908245,913852],"length":1,"stats":{"Line":0}},{"line":41,"address":[908613,908792,908457,913831],"length":1,"stats":{"Line":0}},{"line":42,"address":[913810,908785,908893,909083],"length":1,"stats":{"Line":0}},{"line":44,"address":[914511,914659,909067,914496],"length":1,"stats":{"Line":0}},{"line":47,"address":[908997],"length":1,"stats":{"Line":0}},{"line":48,"address":[909041,909167],"length":1,"stats":{"Line":0}},{"line":49,"address":[909190],"length":1,"stats":{"Line":0}},{"line":51,"address":[909197],"length":1,"stats":{"Line":0}},{"line":52,"address":[909216],"length":1,"stats":{"Line":0}},{"line":53,"address":[909224,909311,913770],"length":1,"stats":{"Line":0}},{"line":58,"address":[909553],"length":1,"stats":{"Line":0}},{"line":61,"address":[909669],"length":1,"stats":{"Line":0}},{"line":63,"address":[909712],"length":1,"stats":{"Line":0}},{"line":64,"address":[909812,909749],"length":1,"stats":{"Line":0}},{"line":67,"address":[914736,909722,909846],"length":1,"stats":{"Line":0}},{"line":68,"address":[914754],"length":1,"stats":{"Line":0}},{"line":69,"address":[914768],"length":1,"stats":{"Line":0}},{"line":70,"address":[914782],"length":1,"stats":{"Line":0}},{"line":74,"address":[909868,910096],"length":1,"stats":{"Line":0}},{"line":75,"address":[910069],"length":1,"stats":{"Line":0}},{"line":79,"address":[910289,910181],"length":1,"stats":{"Line":0}},{"line":80,"address":[910382],"length":1,"stats":{"Line":0}},{"line":84,"address":[910448],"length":1,"stats":{"Line":0}},{"line":85,"address":[910558],"length":1,"stats":{"Line":0}},{"line":86,"address":[910497],"length":1,"stats":{"Line":0}},{"line":88,"address":[910511],"length":1,"stats":{"Line":0}},{"line":89,"address":[910550],"length":1,"stats":{"Line":0}},{"line":91,"address":[910609],"length":1,"stats":{"Line":0}},{"line":94,"address":[910636,914811,914800],"length":1,"stats":{"Line":0}},{"line":97,"address":[910643,910746],"length":1,"stats":{"Line":0}},{"line":98,"address":[911892,913310,910794,912227],"length":1,"stats":{"Line":0}},{"line":104,"address":[912156,911839],"length":1,"stats":{"Line":0}},{"line":109,"address":[912186],"length":1,"stats":{"Line":0}},{"line":113,"address":[913425],"length":1,"stats":{"Line":0}},{"line":117,"address":[913346,913398],"length":1,"stats":{"Line":0}},{"line":118,"address":[913417],"length":1,"stats":{"Line":0}},{"line":120,"address":[913457],"length":1,"stats":{"Line":0}},{"line":124,"address":[913430,913372],"length":1,"stats":{"Line":0}},{"line":125,"address":[913449],"length":1,"stats":{"Line":0}},{"line":127,"address":[913236],"length":1,"stats":{"Line":0}},{"line":131,"address":[912278,913198],"length":1,"stats":{"Line":0}},{"line":132,"address":[913217],"length":1,"stats":{"Line":0}},{"line":134,"address":[913305],"length":1,"stats":{"Line":0}},{"line":138,"address":[913241,912307],"length":1,"stats":{"Line":0}},{"line":139,"address":[913260],"length":1,"stats":{"Line":0}},{"line":141,"address":[912038],"length":1,"stats":{"Line":0}},{"line":142,"address":[912052,913653],"length":1,"stats":{"Line":0}},{"line":144,"address":[911994],"length":1,"stats":{"Line":0}},{"line":145,"address":[912008,913579],"length":1,"stats":{"Line":0}},{"line":147,"address":[911936],"length":1,"stats":{"Line":0}},{"line":150,"address":[911964,913462],"length":1,"stats":{"Line":0}},{"line":152,"address":[911755,913727],"length":1,"stats":{"Line":0}},{"line":157,"address":[910739,910854],"length":1,"stats":{"Line":0}},{"line":158,"address":[910911,910941],"length":1,"stats":{"Line":0}},{"line":161,"address":[911251,910934,911564],"length":1,"stats":{"Line":0}},{"line":162,"address":[911590],"length":1,"stats":{"Line":0}},{"line":165,"address":[912410,912477],"length":1,"stats":{"Line":0}},{"line":168,"address":[903568],"length":1,"stats":{"Line":0}},{"line":169,"address":[903744,903575],"length":1,"stats":{"Line":0}},{"line":170,"address":[903733],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":68},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmasdl2","src","bin","support","gemma_egui_support.rs"],"content":"use crate::Chip8Computer;\nuse egui::Rect;\nuse egui::Ui;\nuse egui::Vec2;\nuse egui::{Align, Color32, ComboBox, Pos2};\nuse std::fs::read_dir;\nuse std::path::PathBuf;\n\nconst CELL_WIDTH: f32 = 5.0;\nconst CELL_HEIGHT: f32 = 5.0;\n\npub struct EGuiFileList {}\nimpl EGuiFileList {\n pub fn display_path(root: PathBuf, selected_filename: \u0026mut String, ui: \u0026mut Ui) {\n let working_filename = selected_filename.clone();\n ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {\n // ui.label(format!(\"Displaying {}\", root.to_str().unwrap_or(\"Unable to Load Path\")));\n ComboBox::from_label(\"Select ROM\")\n .selected_text(selected_filename.clone())\n .show_ui(ui, |ui| {\n let mut sorted_options = vec![];\n for option in read_dir(root.as_path()).unwrap() {\n let to_push = option\n .unwrap()\n .file_name()\n .into_string()\n .unwrap_or(String::new());\n sorted_options.push(to_push);\n }\n\n sorted_options.sort();\n for item in sorted_options {\n // Add each option to the ComboBox\n if ui\n .selectable_label(selected_filename.eq(\u0026item.as_str()), item.clone())\n .clicked()\n {\n *selected_filename = item;\n }\n }\n });\n // Display the selected option\n });\n }\n}\n\npub struct GemmaEguiSupport {}\n\nimpl GemmaEguiSupport {\n pub fn controls_view(system: \u0026mut Chip8Computer, ui: \u0026mut Ui) {\n ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {\n if ui.button(\"Start\").clicked() {\n println!(\"Start\");\n // state.is_running = true;\n }\n if ui.button(\"Step\").clicked() {\n system.step_system();\n }\n\n if ui.button(\"Stop\").clicked() {\n println!(\"STOP\");\n // state.is_running = false;\n }\n if ui.button(\"Reset\").clicked() {\n system.reset(system.quirk_mode.clone());\n // state.is_running = false;\n }\n });\n\n // if ui.button(format!(\"Load {}\", state.selected_rom_filename)).clicked() {\n // load the bin...\n // let read_bin = std::fs::read(PathBuf::from(format!(\"resources/roms/{}\", state.selected_rom_filename))).unwrap();\n // ...then feed the system.\n // system.load_bytes_to_memory(0x200, \u0026read_bin);\n // println!(\"Loaded {}\", state.selected_rom_filename);\n // }\n // EGuiFileList::display_path(PathBuf::from(\"resources/roms\"), \u0026mut state.selected_rom_filename, ui);\n\n ui.with_layout(egui::Layout::left_to_right(Align::TOP), |ui| {\n // ui.checkbox(\u0026mut state.display_memory, \"Display Memory\");\n // ui.checkbox(\u0026mut state.display_video, \"Display Video\");\n // ui.checkbox(\u0026mut state.display_registers, \"Display Registers\");\n });\n }\n\n pub fn registers_view(system: \u0026Chip8Computer, ui: \u0026mut Ui) {\n ui.label(format!(\n \"V0-7: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} \",\n system.registers.peek(0x00),\n system.registers.peek(0x01),\n system.registers.peek(0x02),\n system.registers.peek(0x03),\n system.registers.peek(0x04),\n system.registers.peek(0x05),\n system.registers.peek(0x06),\n system.registers.peek(0x07)\n ));\n ui.label(format!(\n \"V8-F: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} \",\n system.registers.peek(0x08),\n system.registers.peek(0x09),\n system.registers.peek(0x0A),\n system.registers.peek(0x0B),\n system.registers.peek(0x0C),\n system.registers.peek(0x0D),\n system.registers.peek(0x0E),\n system.registers.peek(0x0F)\n ));\n ui.label(format!(\n \"PC: {:04x}\\tI: {:04x}\",\n system.registers.peek_pc(),\n system.registers.peek_i()\n ));\n }\n\n pub fn video_view(system: \u0026Chip8Computer, ui: \u0026mut Ui) {\n let (_resp, painter) = ui.allocate_painter(Vec2::new(350.0, 165.0), egui::Sense::hover());\n for current_row in 0..32 {\n for current_col in 0..64 {\n let data_offset = current_row * 64 + current_col;\n let x_offset = current_col as f32 * CELL_WIDTH;\n let y_offset = current_row as f32 * CELL_HEIGHT;\n let origin = Pos2::new(x_offset, y_offset);\n let colour = if system.video_memory.peek(data_offset) {\n Color32::RED\n } else {\n Color32::WHITE\n };\n let rect = Rect::from_min_size(origin, Vec2::new(CELL_WIDTH, CELL_HEIGHT));\n painter.rect_filled(rect, 0.0, colour);\n // println!(\"Cell {current_col}x{current_row} at {}x{} -\u003e {}\",\n // origin.x, origin.y,\n // system.video_memory.peek(data_offset));\n }\n }\n // thread::sleep(Duration::from_secs(1));\n }\n\n pub fn memory_view(system: \u0026Chip8Computer, ui: \u0026mut Ui) {\n ui.label(\"Memory View\");\n\n for i in (0..=0x200).step_by(16) {\n ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {\n for y in 0..16 {\n ui.label(format!(\"{:02x}\", system.memory.peek((i + y) as u16)).as_str());\n }\n });\n }\n ui.label(\"Should have **something** to adjust the 'memory window'\");\n }\n}\n","traces":[{"line":14,"address":[978208,978639],"length":1,"stats":{"Line":0}},{"line":15,"address":[978238],"length":1,"stats":{"Line":0}},{"line":16,"address":[945456,945729,945697],"length":1,"stats":{"Line":0}},{"line":18,"address":[945485,945595,945660],"length":1,"stats":{"Line":0}},{"line":19,"address":[945642,945710,945530],"length":1,"stats":{"Line":0}},{"line":20,"address":[945744,947939,945650,947527],"length":1,"stats":{"Line":0}},{"line":21,"address":[945780],"length":1,"stats":{"Line":0}},{"line":22,"address":[946167,946302,945941,946331,947894,945837],"length":1,"stats":{"Line":0}},{"line":23,"address":[947558,947641,947709],"length":1,"stats":{"Line":0}},{"line":26,"address":[947648],"length":1,"stats":{"Line":0}},{"line":27,"address":[947593,947929,947664,947748],"length":1,"stats":{"Line":0}},{"line":28,"address":[947816],"length":1,"stats":{"Line":0}},{"line":31,"address":[946573],"length":1,"stats":{"Line":0}},{"line":32,"address":[946607,947503,946797,946847],"length":1,"stats":{"Line":0}},{"line":34,"address":[947246,947490,947068],"length":1,"stats":{"Line":0}},{"line":35,"address":[946887,946997],"length":1,"stats":{"Line":0}},{"line":36,"address":[947198,947252],"length":1,"stats":{"Line":0}},{"line":38,"address":[947309],"length":1,"stats":{"Line":0}},{"line":50,"address":[978672],"length":1,"stats":{"Line":0}},{"line":51,"address":[947984,948953],"length":1,"stats":{"Line":0}},{"line":52,"address":[948009,948120],"length":1,"stats":{"Line":0}},{"line":53,"address":[948195],"length":1,"stats":{"Line":0}},{"line":56,"address":[948235,948358],"length":1,"stats":{"Line":0}},{"line":57,"address":[948442],"length":1,"stats":{"Line":0}},{"line":60,"address":[948581,948458],"length":1,"stats":{"Line":0}},{"line":61,"address":[948665],"length":1,"stats":{"Line":0}},{"line":64,"address":[948705,948828],"length":1,"stats":{"Line":0}},{"line":65,"address":[948909],"length":1,"stats":{"Line":0}},{"line":79,"address":[948976],"length":1,"stats":{"Line":0}},{"line":86,"address":[979152],"length":1,"stats":{"Line":0}},{"line":87,"address":[979400,979449,979547,979498,979253,979302,979351,979204,981241],"length":1,"stats":{"Line":0}},{"line":89,"address":[979185],"length":1,"stats":{"Line":0}},{"line":90,"address":[979231],"length":1,"stats":{"Line":0}},{"line":91,"address":[979280],"length":1,"stats":{"Line":0}},{"line":92,"address":[979329],"length":1,"stats":{"Line":0}},{"line":93,"address":[979378],"length":1,"stats":{"Line":0}},{"line":94,"address":[979427],"length":1,"stats":{"Line":0}},{"line":95,"address":[979476],"length":1,"stats":{"Line":0}},{"line":96,"address":[979525],"length":1,"stats":{"Line":0}},{"line":98,"address":[981347,981298,981494,981396,981543,981592,981641,981445,983335],"length":1,"stats":{"Line":0}},{"line":100,"address":[981276],"length":1,"stats":{"Line":0}},{"line":101,"address":[981325],"length":1,"stats":{"Line":0}},{"line":102,"address":[981374],"length":1,"stats":{"Line":0}},{"line":103,"address":[981423],"length":1,"stats":{"Line":0}},{"line":104,"address":[981472],"length":1,"stats":{"Line":0}},{"line":105,"address":[981521],"length":1,"stats":{"Line":0}},{"line":106,"address":[981570],"length":1,"stats":{"Line":0}},{"line":107,"address":[981619],"length":1,"stats":{"Line":0}},{"line":109,"address":[983394,983970,983439],"length":1,"stats":{"Line":0}},{"line":111,"address":[983376],"length":1,"stats":{"Line":0}},{"line":112,"address":[983421],"length":1,"stats":{"Line":0}},{"line":116,"address":[985392,984016],"length":1,"stats":{"Line":0}},{"line":117,"address":[984071],"length":1,"stats":{"Line":0}},{"line":118,"address":[984553,984367,984449],"length":1,"stats":{"Line":0}},{"line":119,"address":[984574,984672],"length":1,"stats":{"Line":0}},{"line":120,"address":[984956,984787],"length":1,"stats":{"Line":0}},{"line":121,"address":[984876],"length":1,"stats":{"Line":0}},{"line":122,"address":[984910],"length":1,"stats":{"Line":0}},{"line":123,"address":[984996],"length":1,"stats":{"Line":0}},{"line":124,"address":[985077,985032],"length":1,"stats":{"Line":0}},{"line":125,"address":[985079],"length":1,"stats":{"Line":0}},{"line":127,"address":[985064],"length":1,"stats":{"Line":0}},{"line":130,"address":[985306],"length":1,"stats":{"Line":0}},{"line":139,"address":[985424],"length":1,"stats":{"Line":0}},{"line":140,"address":[985457],"length":1,"stats":{"Line":0}},{"line":142,"address":[985491,985704],"length":1,"stats":{"Line":0}},{"line":143,"address":[949640,948992],"length":1,"stats":{"Line":0}},{"line":144,"address":[949080,949022],"length":1,"stats":{"Line":0}},{"line":145,"address":[949500,949091],"length":1,"stats":{"Line":0}},{"line":149,"address":[985646],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":70},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmasdl2","src","bin","support","mod.rs"],"content":"pub mod timestep;\npub mod gemma_egui_support;\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmasdl2","src","bin","support","timestep.rs"],"content":"\n//! Gameplay speed control. It attempts to mimic/limit progression to\n//! the 30 tics per second Doom used.\n\nuse std::{fmt, time::Instant};\n\nconst MS_PER_UPDATE: f32 = 28.57;\n\n#[derive(Debug)]\npub struct TimeStep {\n last_time: Instant,\n delta_time: f32,\n frame_count: u32,\n frame_time: f32,\n run_tics: u32,\n last_tics: u32,\n lag: f32,\n}\n\n#[derive(Debug)]\npub struct FrameData {\n pub tics: u32,\n pub frames: u32,\n}\n\nimpl fmt::Display for FrameData {\n fn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n f.write_fmt(format_args!(\n \"FrameData (per-second):\\n - tics: {}\\n - fps: {}\",\n self.tics, self.frames\n ))\n }\n}\n\nimpl TimeStep {\n pub fn new() -\u003e TimeStep {\n TimeStep {\n last_time: Instant::now(),\n delta_time: 0.0,\n frame_count: 0,\n frame_time: 0.0,\n run_tics: 0,\n last_tics: 0,\n lag: 0.0,\n }\n }\n\n pub fn delta(\u0026mut self) -\u003e f32 {\n let current_time = Instant::now();\n let delta = current_time.duration_since(self.last_time).as_micros() as f32 * 0.001;\n self.last_time = current_time;\n self.delta_time = delta;\n delta\n }\n\n /// Increments self time and returns current lag. `run_this` is run only for\n /// `n` tics available.\n pub fn run_this(\u0026mut self, mut run_this: impl FnMut(f32)) {\n let dt = self.delta();\n self.lag += dt;\n while self.lag \u003e= MS_PER_UPDATE {\n run_this(dt);\n self.lag -= MS_PER_UPDATE;\n self.run_tics += 1;\n }\n }\n\n pub fn frame_rate(\u0026mut self) -\u003e Option\u003cFrameData\u003e {\n self.frame_count += 1;\n self.frame_time += self.delta_time;\n let tmp;\n let tmp2;\n // per second\n if self.frame_time \u003e= 1000.0 {\n tmp = self.frame_count;\n tmp2 = self.last_tics;\n self.frame_count = 0;\n self.frame_time = 0.0;\n self.last_tics = self.run_tics;\n return Some(FrameData {\n tics: self.run_tics - tmp2,\n frames: tmp,\n });\n }\n\n None\n }\n}\n\nimpl Default for TimeStep {\n // shutup clippy!\n fn default() -\u003e Self {\n Self::new()\n }\n}","traces":[{"line":27,"address":[905936],"length":1,"stats":{"Line":0}},{"line":28,"address":[905975],"length":1,"stats":{"Line":0}},{"line":36,"address":[906112],"length":1,"stats":{"Line":0}},{"line":38,"address":[906126],"length":1,"stats":{"Line":0}},{"line":48,"address":[906208],"length":1,"stats":{"Line":0}},{"line":49,"address":[906221],"length":1,"stats":{"Line":0}},{"line":50,"address":[906243],"length":1,"stats":{"Line":0}},{"line":51,"address":[906319],"length":1,"stats":{"Line":0}},{"line":52,"address":[906334],"length":1,"stats":{"Line":0}},{"line":58,"address":[],"length":0,"stats":{"Line":0}},{"line":59,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":0}},{"line":61,"address":[],"length":0,"stats":{"Line":0}},{"line":62,"address":[],"length":0,"stats":{"Line":0}},{"line":63,"address":[],"length":0,"stats":{"Line":0}},{"line":64,"address":[],"length":0,"stats":{"Line":0}},{"line":68,"address":[906352],"length":1,"stats":{"Line":0}},{"line":69,"address":[906376,906438],"length":1,"stats":{"Line":0}},{"line":70,"address":[906403],"length":1,"stats":{"Line":0}},{"line":74,"address":[906418],"length":1,"stats":{"Line":0}},{"line":75,"address":[906472],"length":1,"stats":{"Line":0}},{"line":76,"address":[906482],"length":1,"stats":{"Line":0}},{"line":77,"address":[906489],"length":1,"stats":{"Line":0}},{"line":78,"address":[906496],"length":1,"stats":{"Line":0}},{"line":79,"address":[906504],"length":1,"stats":{"Line":0}},{"line":80,"address":[906549],"length":1,"stats":{"Line":0}},{"line":81,"address":[906563,906510],"length":1,"stats":{"Line":0}},{"line":86,"address":[906459],"length":1,"stats":{"Line":0}},{"line":92,"address":[906592],"length":1,"stats":{"Line":0}},{"line":93,"address":[906600],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":30},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmatelnet","src","bin","gemmatelnetd.rs"],"content":"use std::io::{BufRead, BufReader, Write};\nuse std::net::{TcpListener, TcpStream};\nuse std::thread;\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\nuse gemma::chip8::quirk_modes::QuirkMode;\nuse gemmatelnet::client_handler::handle_client;\nuse log::info;\nuse clap::Parser;\n\nconst LISTEN_HOST: \u0026str = \"0.0.0.0\";\nconst LISTEN_PORT: u32 = 1420;\n\n#[derive(Parser, Debug)]\n#[command(name = \"myapp\")]\n#[command(about = \"Does awesome things\", long_about = None)]\nstruct Cli {\n #[arg(long, default_value = \"0.0.0.0\")]\n host: Option\u003cString\u003e,\n #[arg(long, default_value = \"1420\")]\n port: Option\u003cu32\u003e,\n}\n\nimpl Cli {\n pub fn host(\u0026self) -\u003e String {\n self.host.clone().unwrap_or(LISTEN_HOST.into())\n }\n pub fn port(\u0026self) -\u003e u32 {\n self.port.unwrap_or(LISTEN_PORT.into())\n }\n}\n\nfn main() -\u003e std::io::Result\u003c()\u003e {\n let config = Cli::parse();\n\n let listener = TcpListener::bind(format!(\"{}:{}\", config.host(), config.port()))?;\n println!(\"Listening to {}:{}\", LISTEN_HOST, LISTEN_PORT);\n\n for stream in listener.incoming() {\n match stream {\n Ok(stream) =\u003e {\n thread::spawn(|| handle_client(stream));\n }\n Err(e) =\u003e {\n eprintln!(\"Connection Failed -\u003e {}\", e);\n }\n }\n }\n\n Ok(())\n}","traces":[{"line":24,"address":[591307,591136,591281],"length":1,"stats":{"Line":0}},{"line":25,"address":[591155,591291],"length":1,"stats":{"Line":0}},{"line":27,"address":[591328],"length":1,"stats":{"Line":0}},{"line":28,"address":[591337],"length":1,"stats":{"Line":0}},{"line":32,"address":[591392,592523,592557],"length":1,"stats":{"Line":0}},{"line":33,"address":[591409],"length":1,"stats":{"Line":0}},{"line":35,"address":[592537,591923,591471,591848,591427],"length":1,"stats":{"Line":0}},{"line":36,"address":[591999,591897],"length":1,"stats":{"Line":0}},{"line":38,"address":[592228,592115],"length":1,"stats":{"Line":0}},{"line":39,"address":[592260],"length":1,"stats":{"Line":0}},{"line":40,"address":[592298],"length":1,"stats":{"Line":0}},{"line":41,"address":[592320,592366],"length":1,"stats":{"Line":0}},{"line":43,"address":[592327],"length":1,"stats":{"Line":0}},{"line":44,"address":[592359,592432],"length":1,"stats":{"Line":0}},{"line":49,"address":[592207],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":15},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmatelnet","src","client_handler.rs"],"content":"use std::io::{BufRead, BufReader};\nuse std::net::TcpStream;\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\nuse gemma::chip8::quirk_modes::QuirkMode;\nuse crate::telnet_utils::list_of_files_in_directory;\nuse std::io::Write;\nuse std::path::Path;\nuse gemma::constants::RESOURCES_ROOT;\nuse crate::constants::*;\n\nconst ERROR_CANT_NOTIFY: \u0026str = \"Unable to notify client\";\nconst MENU_TEXT: \u0026str = r#\"\n---------------------------\nstatus -\u003e Current status of the Instance\nnew -\u003e Start New Instance\n* list -\u003e list available files to load into Chip-8 Instance\n* load \u003cfilename\u003e -\u003e Load Chip-8 Program from server\nhelp -\u003e display this help\nstep -\u003e step chip-8 machine 1 step\n* steps \u003cnumber\u003e -\u003e Step a fixed number of steps\n* memory -\u003e dump current memory from chip-8 instance\n* video -\u003e dump current video from chip-8 instance\n* window \u003cstart\u003e \u003clength\u003e -\u003e display a window of memory\ndisconnect -\u003e close connection\n---------------------------\n\"#;\n\npub fn handle_client(mut stream: TcpStream) {\n // each client gets their own instance of a Chip-8 system\n let mut chip8 = Chip8ComputerManager::default();\n\n let peer_addr = stream.peer_addr().unwrap();\n println!(\"New CX From {}\", peer_addr);\n\n let mut reader = BufReader::new(stream.try_clone().unwrap());\n let mut line = String::new();\n\n write!(stream, \"Welcome to Gemma Telnet Server\\n\").unwrap();\n write!(stream, \"{}\", MENU_TEXT).unwrap();\n\n let mut time_to_die: bool = false;\n\n while !time_to_die {\n\n // clear the buffer\n line.clear();\n\n // read from the client to the buffer\n let bytes = reader.read_line(\u0026mut line).unwrap();\n if bytes == 0 {\n println!(\"Connection closed by {}\", peer_addr);\n break;\n }\n\n // display the message received\n eprint!(\"RX {}: {}\", peer_addr, line);\n\n // split the line into \u003ccommand\u003e \u003cparam1\u003e \u003cparam2\u003e (if params exist)\n\n let mut result = line.split(\" \");\n let command = result.next().unwrap();\n let p1 = result.next();\n let p2 = result.next();\n\n let mut message_to_send: String = \"Unrecognized Command\".to_string();\n\n match command.trim() {\n CMD_STATUS =\u003e {\n message_to_send = chip8.status_as_string();\n }\n CMD_DISCONNECT =\u003e {\n message_to_send = \"Disconnecting User\".to_string();\n time_to_die = true;\n break;\n }\n CMD_NEW =\u003e {\n message_to_send = \"Starting new Chip-8 Instance\".to_string();\n chip8.reset(QuirkMode::Chip8);\n }\n CMD_LIST =\u003e {\n message_to_send =\n format!(\"Listing files in path: \\n{}\",\n list_of_files_in_directory(\n \u0026mut Path::new(\"/home/tmerritt/Projects/trevors_chip8_toy/resources/roms\".into())\n )\n );\n }\n CMD_LOAD =\u003e {\n let file_to_use = p1.unwrap_or(\"default.ch8\").trim();\n // chip8 = Chip8ComputerManager::from(chip8).load_new_program_to_system_memory()\n message_to_send = format!(\"Preparing to load {} from disk.\",\n file_to_use);\n chip8.load_new_program_from_disk_to_system_memory(\n Path::new(format!(\"{}/roms/{}\", RESOURCES_ROOT, file_to_use).as_str())\n );\n }\n CMD_HELP =\u003e {\n message_to_send = MENU_TEXT.to_string();\n }\n CMD_STEPS =\u003e {\n if chip8.core_should_run {\n let num_steps = p1.unwrap_or(\"0\").trim();\n message_to_send = format!(\"Advancing {} steps.\", num_steps);\n for _ in 0..num_steps.parse().unwrap_or(0) {\n chip8.tick();\n }\n } else {\n message_to_send = \"Not Ready to Step\".to_string();\n }\n }\n CMD_STEP =\u003e {\n if chip8.core_should_run {\n message_to_send = \"Advancing 1 step.\".to_string();\n chip8.tick();\n } else {\n message_to_send = \"Not Ready To Step\".to_string();\n }\n }\n CMD_WINDOW =\u003e {\n message_to_send = format!(\"Memory window from {} to {}\",\n p1.unwrap_or(\"0x0000\").trim(),\n p2.unwrap_or(\"0xFFFF\").trim());\n }\n _ =\u003e {}\n }\n\n write!(stream, \"{}\\n\", message_to_send).expect(ERROR_CANT_NOTIFY);\n\n stream.flush().unwrap();\n }\n}\n","traces":[{"line":28,"address":[2419536,2427085,2425658],"length":1,"stats":{"Line":0}},{"line":30,"address":[2419550],"length":1,"stats":{"Line":0}},{"line":32,"address":[2419613],"length":1,"stats":{"Line":0}},{"line":33,"address":[2419985],"length":1,"stats":{"Line":0}},{"line":35,"address":[2420299,2420081],"length":1,"stats":{"Line":0}},{"line":36,"address":[2420326],"length":1,"stats":{"Line":0}},{"line":38,"address":[2420377,2420546],"length":1,"stats":{"Line":0}},{"line":39,"address":[2420768],"length":1,"stats":{"Line":0}},{"line":41,"address":[2421066],"length":1,"stats":{"Line":0}},{"line":43,"address":[473082],"length":1,"stats":{"Line":0}},{"line":46,"address":[2421092],"length":1,"stats":{"Line":0}},{"line":49,"address":[473145,473393],"length":1,"stats":{"Line":0}},{"line":50,"address":[2421397],"length":1,"stats":{"Line":0}},{"line":51,"address":[2421457,2421403],"length":1,"stats":{"Line":0}},{"line":56,"address":[2421531,2421430],"length":1,"stats":{"Line":0}},{"line":60,"address":[2421651],"length":1,"stats":{"Line":0}},{"line":61,"address":[2421722,2421894],"length":1,"stats":{"Line":0}},{"line":62,"address":[2421932],"length":1,"stats":{"Line":0}},{"line":63,"address":[2422021],"length":1,"stats":{"Line":0}},{"line":65,"address":[2422110],"length":1,"stats":{"Line":0}},{"line":67,"address":[2422272,2422157],"length":1,"stats":{"Line":0}},{"line":68,"address":[2426679,2422294],"length":1,"stats":{"Line":0}},{"line":69,"address":[2426537,2422375],"length":1,"stats":{"Line":0}},{"line":71,"address":[474408,474345],"length":1,"stats":{"Line":0}},{"line":72,"address":[2422460,2426381],"length":1,"stats":{"Line":0}},{"line":73,"address":[2426507],"length":1,"stats":{"Line":0}},{"line":76,"address":[2422501,2422434],"length":1,"stats":{"Line":0}},{"line":77,"address":[2426215,2422549],"length":1,"stats":{"Line":0}},{"line":78,"address":[2426341],"length":1,"stats":{"Line":0}},{"line":80,"address":[2422523,2426210,2422590],"length":1,"stats":{"Line":0}},{"line":81,"address":[2426068,2426162],"length":1,"stats":{"Line":0}},{"line":82,"address":[2425765],"length":1,"stats":{"Line":0}},{"line":83,"address":[2425746],"length":1,"stats":{"Line":0}},{"line":84,"address":[2422638,2425683],"length":1,"stats":{"Line":0}},{"line":88,"address":[2422612,2422694],"length":1,"stats":{"Line":0}},{"line":89,"address":[2425000,2422758],"length":1,"stats":{"Line":0}},{"line":91,"address":[477175,477027,477269],"length":1,"stats":{"Line":0}},{"line":92,"address":[2425258],"length":1,"stats":{"Line":0}},{"line":93,"address":[2425617],"length":1,"stats":{"Line":0}},{"line":94,"address":[2425515,2425586,2425317],"length":1,"stats":{"Line":0}},{"line":97,"address":[474799,474708,476963],"length":1,"stats":{"Line":0}},{"line":98,"address":[2424837,2422855],"length":1,"stats":{"Line":0}},{"line":100,"address":[474888,474821],"length":1,"stats":{"Line":0}},{"line":101,"address":[2422944,2424263],"length":1,"stats":{"Line":0}},{"line":102,"address":[476071,476276],"length":1,"stats":{"Line":0}},{"line":103,"address":[2424475,2424327],"length":1,"stats":{"Line":0}},{"line":104,"address":[2424601],"length":1,"stats":{"Line":0}},{"line":105,"address":[2424816],"length":1,"stats":{"Line":0}},{"line":108,"address":[476024,476113],"length":1,"stats":{"Line":0}},{"line":111,"address":[2422970,2422918],"length":1,"stats":{"Line":0}},{"line":112,"address":[475869,475010],"length":1,"stats":{"Line":0}},{"line":113,"address":[2423701,2423882],"length":1,"stats":{"Line":0}},{"line":114,"address":[476000],"length":1,"stats":{"Line":0}},{"line":116,"address":[2423735,2423670],"length":1,"stats":{"Line":0}},{"line":119,"address":[2423665,2422992,2423044],"length":1,"stats":{"Line":0}},{"line":120,"address":[2423523,2423206,2423617,2423359],"length":1,"stats":{"Line":0}},{"line":121,"address":[2423096],"length":1,"stats":{"Line":0}},{"line":122,"address":[2423590,2423249],"length":1,"stats":{"Line":0}},{"line":127,"address":[2426684,2423050],"length":1,"stats":{"Line":0}},{"line":129,"address":[2426809],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":60},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmatelnet","src","constants.rs"],"content":"pub const CMD_NEW: \u0026str = \"new\";\npub const CMD_LIST: \u0026str = \"list\";\npub const CMD_LOAD: \u0026str = \"load\";\npub const CMD_STATUS: \u0026str = \"status\";\npub const CMD_DISCONNECT: \u0026str = \"disconnect\";\npub const CMD_HELP: \u0026str = \"help\";\npub const CMD_STEPS: \u0026str = \"steps\";\npub const CMD_STEP: \u0026str = \"step\";\npub const CMD_WINDOW: \u0026str = \"window\";\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmatelnet","src","lib.rs"],"content":"pub mod telnet_utils;\npub mod client_handler;\npub mod constants;\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmatelnet","src","telnet_utils.rs"],"content":"use std::{fs, io};\nuse std::path::Path;\n\npub fn list_of_files_in_directory(root_directory: \u0026Path) -\u003e String {\n\n let mut return_value = String::new();\n\n if root_directory.is_dir() {\n for entry in fs::read_dir(root_directory).unwrap() {\n let entry = entry.unwrap();\n let path = entry.path();\n if path.is_file() {\n return_value = format!(\"{}\\n{}\", return_value, path.file_name().unwrap().to_string_lossy());\n }\n }\n }\n return_value\n}","traces":[{"line":4,"address":[2436802,2435008],"length":1,"stats":{"Line":0}},{"line":6,"address":[2435071],"length":1,"stats":{"Line":0}},{"line":8,"address":[2435181,2435092],"length":1,"stats":{"Line":0}},{"line":9,"address":[2435643,2435262,2435500,2435676],"length":1,"stats":{"Line":0}},{"line":11,"address":[2435905],"length":1,"stats":{"Line":0}},{"line":12,"address":[2436052,2435984,2436775],"length":1,"stats":{"Line":0}},{"line":13,"address":[2436099,2436307,2436633],"length":1,"stats":{"Line":0}},{"line":17,"address":[2435203],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":8},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmautil","src","bin","bin2hex.rs"],"content":"use clap::{Arg, Command};\nuse std::fs::File;\nuse std::io;\nuse std::io::{BufReader, Read};\n\n#[derive(Debug)]\nstruct CliArgs {\n input: String,\n}\n\nfn main() {\n println!(\"Taxation is Theft!\");\n // Set up the command line arguments\n let matches = Command::new(\"my_program\")\n .about(\"Processes an input file and outputs it with a specified bit width\")\n .arg(\n Arg::new(\"input\")\n .help(\"The input file to process\")\n .required(true)\n .index(1),\n )\n .get_matches();\n\n // Parse the command-line arguments\n let args = CliArgs {\n input: matches.get_one::\u003cString\u003e(\"input\").unwrap().to_string(),\n };\n\n // Use the parsed arguments\n println!(\"Input file: {}\", args.input);\n\n // behave like a shift register and load each character from the file 1 by 1.\n let results = read_file_to_bools(\u0026args.input);\n for result in results.unwrap().bytes() {\n print!(\"0x{:02x}, \", result.unwrap());\n }\n}\nfn read_file_to_bools(file_path: \u0026str) -\u003e io::Result\u003cVec\u003cu8\u003e\u003e {\n // Open the file\n let file = File::open(file_path)?;\n let mut reader = BufReader::new(file);\n\n let mut bytes = Vec::new();\n reader.read_to_end(\u0026mut bytes)?;\n\n let mut output = Vec::new();\n let mut current_byte = 0u8;\n let mut bit_index = 0;\n\n for \u0026byte in \u0026bytes {\n // Convert ASCII character '1' or '0' to boolean, skip any other characters\n let bit = match byte {\n b'1' =\u003e true,\n b'0' =\u003e false,\n _ =\u003e continue, // Skip non-'1' or '0' characters\n };\n\n // Set the appropriate bit in the current byte\n if bit {\n current_byte |= 1 \u003c\u003c (7 - bit_index); // Set the bit at the correct position\n }\n\n bit_index += 1;\n\n // Once we have filled 8 bits, push the byte and reset\n if bit_index == 8 {\n output.push(current_byte);\n current_byte = 0;\n bit_index = 0;\n }\n }\n\n // If there are remaining bits, push the last byte (it will be partially filled)\n if bit_index \u003e 0 {\n output.push(current_byte);\n }\n\n Ok(output)\n}\n","traces":[{"line":11,"address":[253408,255306],"length":1,"stats":{"Line":0}},{"line":12,"address":[253430],"length":1,"stats":{"Line":0}},{"line":14,"address":[253487,253736,253811],"length":1,"stats":{"Line":0}},{"line":17,"address":[253641,253569],"length":1,"stats":{"Line":0}},{"line":26,"address":[253999,253824],"length":1,"stats":{"Line":0}},{"line":30,"address":[254131,254038],"length":1,"stats":{"Line":0}},{"line":33,"address":[254208],"length":1,"stats":{"Line":0}},{"line":34,"address":[254480,254583,254723],"length":1,"stats":{"Line":0}},{"line":35,"address":[254978],"length":1,"stats":{"Line":0}},{"line":38,"address":[256462,256518,255360],"length":1,"stats":{"Line":0}},{"line":40,"address":[255393,255491],"length":1,"stats":{"Line":0}},{"line":41,"address":[255459],"length":1,"stats":{"Line":0}},{"line":43,"address":[255570],"length":1,"stats":{"Line":0}},{"line":44,"address":[255630,255698,255792],"length":1,"stats":{"Line":0}},{"line":46,"address":[255780],"length":1,"stats":{"Line":0}},{"line":47,"address":[255825],"length":1,"stats":{"Line":0}},{"line":48,"address":[255833],"length":1,"stats":{"Line":0}},{"line":50,"address":[255923,256017,255852],"length":1,"stats":{"Line":0}},{"line":52,"address":[256038],"length":1,"stats":{"Line":0}},{"line":53,"address":[256213],"length":1,"stats":{"Line":0}},{"line":54,"address":[256223],"length":1,"stats":{"Line":0}},{"line":59,"address":[256340,256231],"length":1,"stats":{"Line":0}},{"line":60,"address":[256342,256261],"length":1,"stats":{"Line":0}},{"line":63,"address":[256364,256241,256386],"length":1,"stats":{"Line":0}},{"line":66,"address":[256457,256371],"length":1,"stats":{"Line":0}},{"line":67,"address":[256404],"length":1,"stats":{"Line":0}},{"line":68,"address":[256438],"length":1,"stats":{"Line":0}},{"line":69,"address":[256446],"length":1,"stats":{"Line":0}},{"line":74,"address":[256005],"length":1,"stats":{"Line":0}},{"line":75,"address":[256141],"length":1,"stats":{"Line":0}},{"line":78,"address":[256067],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":31},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmautil","src","bin","ch8asm.rs"],"content":"use gemma::chip8::util::InstructionUtil;\nuse gemma::chip8::{instructions::Chip8CpuInstructions, quirk_modes::QuirkMode};\nuse std::fs::{self, File};\nuse std::io::Write;\n// Ch8Asm\n// Converts well formed CH8ASM.\n// no variables.\n// no labels.\n// nothing fun.\n// VALID FORMAT\n// \u003cINSTRUCTION\u003e \u003c0x|0X\u003e\u003cparameter\u003e[, ][\u003c0x|OX\u003e\u003cparameter\u003e[, ][\u003c0x|OX\u003e\u003cparameter\u003e]][; comment]\nuse pest::Parser;\nuse pest_derive::Parser;\n#[derive(Parser)]\n#[grammar = \"chip8_asm.pest\"]\npub struct Chip8AsmParser;\n\nfn main() {\n println!(\"Taxation is Theft\");\n\n let unparsed = fs::read_to_string(\"resources/test/gemma_disassembler_manual_document.asc\")\n .expect(\"Unable to read input\");\n\n let file = Chip8AsmParser::parse(Rule::file, \u0026unparsed)\n .expect(\"Unable to parse. Try again.\")\n .next()\n .unwrap();\n\n println!(\"PREPARING TO WRITE THE TARGET FILE...\");\n let mut target_file = File::create(\"thisismytarget.ch8\").expect(\"Unable to create target\");\n\n for record in file.into_inner() {\n // let mut working_instruction = Chip8CpuInstructions::XXXXERRORINSTRUCTION;\n match record.as_rule() {\n Rule::record =\u003e {\n print!(\"record = {:?}\\t\", record.as_str());\n let x = Chip8CpuInstructions::from_str(record.as_str());\n println!(\"DECODED TO {:?} {:04x}\", x, x.encode());\n let (high, low) = InstructionUtil::split_bytes(x.encode());\n target_file\n .write_all(\u0026[high, low])\n .expect(\"Unable to write to the file.\");\n }\n _ =\u003e {}\n }\n }\n}\n","traces":[{"line":18,"address":[703472,705501,705528],"length":1,"stats":{"Line":0}},{"line":19,"address":[703479],"length":1,"stats":{"Line":0}},{"line":21,"address":[703530],"length":1,"stats":{"Line":0}},{"line":24,"address":[703692,703607],"length":1,"stats":{"Line":0}},{"line":29,"address":[703951],"length":1,"stats":{"Line":0}},{"line":30,"address":[703996],"length":1,"stats":{"Line":0}},{"line":32,"address":[704072,704211,704307,704351],"length":1,"stats":{"Line":0}},{"line":34,"address":[704511,704407],"length":1,"stats":{"Line":0}},{"line":36,"address":[704540,704585],"length":1,"stats":{"Line":0}},{"line":37,"address":[704705],"length":1,"stats":{"Line":0}},{"line":38,"address":[704788],"length":1,"stats":{"Line":0}},{"line":39,"address":[705345],"length":1,"stats":{"Line":0}},{"line":40,"address":[705453],"length":1,"stats":{"Line":0}},{"line":41,"address":[705418],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":14},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmautil","src","bin","ch8disasm.rs"],"content":"use std::path::Path;\nuse clap::{command, Parser};\nuse gemma::chip8::instructions::Chip8CpuInstructions;\nuse gemma::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;\nuse gemma::chip8::quirk_modes::QuirkMode::Chip8;\n\n/// ch8disasm\n///\n/// Provide disassembled version of the CH8 file provided.\n#[derive(Parser)]\n#[command(version, about, long_about = None)]\nstruct DisassemblerApp {\n #[arg(short)]\n input_file: Box\u003cPath\u003e,\n #[arg(short)]\n output_file: Option\u003cBox\u003cPath\u003e\u003e,\n}\n\nstruct Disassembler {}\nimpl Disassembler {\n pub fn disassemble(from_data: Vec\u003cu8\u003e) -\u003e String {\n let mut working_instruction: u16 = 0x0000;\n\n // read the input data and loop through it byte by byte.\n let mut output_string = String::new();\n\n for (offset, byte) in from_data.iter().enumerate() {\n working_instruction = (working_instruction \u003c\u003c 8) | (*byte as u16);\n if offset % 2 != 0 {\n let decoded = Chip8CpuInstructions::decode(working_instruction, \u0026Chip8);\n let decoded_string = decoded.to_string();\n let mut current_parts = String::new();\n\n match decoded {\n XXXXERRORINSTRUCTION =\u003e {\n current_parts = format!(\"DW 0x{:04x}\", working_instruction);\n }\n _ =\u003e {\n current_parts = format!(\"{}\", decoded);\n }\n };\n\n let target_length: i32 = 25;\n let spacing_length = target_length.saturating_sub(current_parts.len() as i32);\n\n // now add the rest after the string\n let x = spacing_length as usize;\n current_parts = format!(\"{}{:\u003cx$}; Bytes [0x{:04x}] offset [0x{:04x}]\\n\", current_parts, \" \", working_instruction, offset -1);\n // println!(\"SHOULD OUTPUT: [{current_parts}]\");\n output_string = output_string.to_string() + \u0026*current_parts.to_string();\n\n working_instruction = 0x0000;\n }\n }\n\n output_string\n }\n}\n\nfn main() {\n println!(\"Taxation is Theft\");\n let result = DisassemblerApp::parse();\n println!(\"PREPARING TO DISASSEMBLE {:?}\", result.input_file.file_name());\n\n\n let source_file = result.input_file.to_str().unwrap();\n let decompiled_program = Disassembler::disassemble(\n std::fs::read(source_file).unwrap()\n );\n\n match result.output_file {\n None =\u003e {\n println!(\"Output to console.\");\n println!(\"OS: \\n\\n{}\", decompiled_program);\n }\n Some(target) =\u003e {\n println!(\"Output to {:?}\", target);\n std::fs::write(target, decompiled_program).unwrap();\n }\n }\n}","traces":[{"line":21,"address":[602279,598848],"length":1,"stats":{"Line":0}},{"line":22,"address":[598890],"length":1,"stats":{"Line":0}},{"line":25,"address":[598916],"length":1,"stats":{"Line":0}},{"line":27,"address":[599059,599341,598975],"length":1,"stats":{"Line":0}},{"line":28,"address":[599383],"length":1,"stats":{"Line":0}},{"line":29,"address":[599429],"length":1,"stats":{"Line":0}},{"line":30,"address":[599443],"length":1,"stats":{"Line":0}},{"line":31,"address":[599514],"length":1,"stats":{"Line":0}},{"line":32,"address":[599529],"length":1,"stats":{"Line":0}},{"line":34,"address":[599580],"length":1,"stats":{"Line":0}},{"line":36,"address":[599691,600034,599594],"length":1,"stats":{"Line":0}},{"line":38,"address":[600462],"length":1,"stats":{"Line":0}},{"line":39,"address":[599637,600320,600199],"length":1,"stats":{"Line":0}},{"line":43,"address":[598879],"length":1,"stats":{"Line":0}},{"line":44,"address":[600184,600472,600540],"length":1,"stats":{"Line":0}},{"line":47,"address":[600547],"length":1,"stats":{"Line":0}},{"line":48,"address":[601706,600557],"length":1,"stats":{"Line":0}},{"line":50,"address":[601848,602238],"length":1,"stats":{"Line":0}},{"line":52,"address":[602187],"length":1,"stats":{"Line":0}},{"line":56,"address":[599284],"length":1,"stats":{"Line":0}},{"line":60,"address":[602304,603835,603905],"length":1,"stats":{"Line":0}},{"line":61,"address":[602311],"length":1,"stats":{"Line":0}},{"line":62,"address":[602375],"length":1,"stats":{"Line":0}},{"line":63,"address":[602396,602503],"length":1,"stats":{"Line":0}},{"line":66,"address":[602611,602763],"length":1,"stats":{"Line":0}},{"line":68,"address":[602787],"length":1,"stats":{"Line":0}},{"line":71,"address":[603052],"length":1,"stats":{"Line":0}},{"line":73,"address":[603081,603222],"length":1,"stats":{"Line":0}},{"line":74,"address":[603241],"length":1,"stats":{"Line":0}},{"line":76,"address":[603107],"length":1,"stats":{"Line":0}},{"line":77,"address":[603419,603171],"length":1,"stats":{"Line":0}},{"line":78,"address":[603488],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":32},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmautil","tests","assmber_tests.rs"],"content":"","traces":[],"covered":0,"coverable":0}]};
var previousData = {"files":[{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","bin","speedtest.rs"],"content":"use gemma::chip8::computer_manager::Chip8ComputerManager;\nuse std::fs::File;\nuse std::io::Read;\nuse std::ops::Add;\nuse std::path::Path;\nuse std::time::{Duration, Instant};\n\nconst ROM_ROOT: \u0026str = \"resources/roms\";\n\nfn main() {\n let mut x = Chip8ComputerManager::new();\n\n let mut buffer = Vec::new();\n let mut input_file =\n File::open(Path::new(\u0026(ROM_ROOT.to_string() + \"/mandelbrot_bench.ch8\"))).expect(\"derp\");\n input_file\n .read_to_end(\u0026mut buffer)\n .expect(\"unable to read file\");\n x.load_new_program_to_system_memory((\u0026*buffer).into());\n\n for _ in 0..10 {\n let start_time = Instant::now();\n let mut num_cycles = 0;\n // how many instructions in 10s.\n let end_time = start_time.add(Duration::from_secs(10));\n while Instant::now().le(\u0026end_time) {\n num_cycles += 1;\n x.step();\n }\n\n let num = num_cycles\n .to_string()\n .as_bytes()\n .rchunks(3)\n .rev()\n .map(std::str::from_utf8)\n .collect::\u003cResult\u003cVec\u003c\u0026str\u003e, _\u003e\u003e()\n .unwrap()\n .join(\",\"); // separator\n\n let num_ips = num_cycles / 1000000;\n println!(\"Completed at {num_ips} Mips. ({num})\");\n }\n}\n","traces":[{"line":10,"address":[460780,460845,458832],"length":1,"stats":{"Line":0}},{"line":11,"address":[458839],"length":1,"stats":{"Line":0}},{"line":13,"address":[458856],"length":1,"stats":{"Line":0}},{"line":14,"address":[458919,458991],"length":1,"stats":{"Line":0}},{"line":16,"address":[459251],"length":1,"stats":{"Line":0}},{"line":19,"address":[459336],"length":1,"stats":{"Line":0}},{"line":21,"address":[459436],"length":1,"stats":{"Line":0}},{"line":22,"address":[459589,459671],"length":1,"stats":{"Line":0}},{"line":23,"address":[459700],"length":1,"stats":{"Line":0}},{"line":25,"address":[459716],"length":1,"stats":{"Line":0}},{"line":26,"address":[459794],"length":1,"stats":{"Line":0}},{"line":27,"address":[459898,460793,460819],"length":1,"stats":{"Line":0}},{"line":28,"address":[460800,460840],"length":1,"stats":{"Line":0}},{"line":31,"address":[459933,460390,459891,460323,460024],"length":1,"stats":{"Line":0}},{"line":41,"address":[460537,460626],"length":1,"stats":{"Line":0}},{"line":42,"address":[460599,460646],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":16},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","bin","testcompression.rs"],"content":"use std::fs;\nuse std::fs::File;\nuse clap::{Parser, Subcommand};\nuse flate2::{write::{GzEncoder, GzDecoder}, Compression};\nuse std::io::{Cursor, Read, Write};\nuse std::path::PathBuf;\nuse std::io::prelude::*;\n\n#[derive(Parser)]\n#[command(author, version, about = \"Compress or decompress a string\", long_about = None)]\nstruct Cli {\n /// Subcommand: either 'compress' or 'decompress'\n #[command(subcommand)]\n command: Commands,\n}\n\n#[derive(Subcommand)]\nenum Commands {\n /// Compress the input string\n Compress {\n /// The string to compress\n input: String,\n },\n /// Decompress the input string (must be compressed format)\n Decompress {\n /// The compressed string to decompress, in hex format\n input: String,\n },\n CompressFile {\n input: PathBuf\n },\n DecompressFile {\n input: PathBuf\n }\n}\n\nfn compress_string(input: \u0026str) -\u003e Vec\u003cu8\u003e {\n let mut encoder = GzEncoder::new(Vec::new(), Compression::default());\n encoder.write_all(input.as_bytes()).expect(\"Compression failed\");\n encoder.finish().expect(\"Failed to finish compression\")\n}\n\nfn decompress_string(input: \u0026[u8]) -\u003e String {\n let mut decoder = GzDecoder::new(Vec::new());\n decoder.write_all(input).expect(\"Decompression failed\");\n let decompressed_data = decoder.finish().expect(\"Failed to finish decompression\");\n String::from_utf8(decompressed_data).expect(\"Invalid UTF-8\")\n}\n\nfn compress_file(input_path: \u0026PathBuf) {\n let target_file_name = format!(\"{}.tflt\", input_path.display());\n println!(\"TARGET_FILE_NAME: {}\", target_file_name);\n let input_data = fs::read(input_path).expect(\"Failed to read input file\");\n let mut encoder = GzEncoder::new(Vec::new(), Compression::default());\n encoder.write_all(\u0026input_data).expect(\"Compression failed\");\n let compressed_data = encoder.finish().expect(\"Failed to finish compression\");\n let target_file = File::create(target_file_name);\n target_file.unwrap().write_all(\u0026compressed_data).unwrap()\n}\n\nfn decompress_file(input_path: \u0026PathBuf) {\n let target_file_name = format!(\"{}.uncompressed\", input_path.display());\n println!(\"Writing decompressed data to {}\", target_file_name);\n let input_data = fs::read(input_path).expect(\"Failed to read compressed file.\");\n let mut decompressed_data = vec![];\n let cursor = Cursor::new(input_data);\n let mut decoder = GzDecoder::new(cursor);\n decoder.read_to_end(\u0026mut decompressed_data).expect(\"Decompression failed.\");\n let mut target_file = File::create(\u0026target_file_name).expect(format!(\"Unable to create uncompressed file -\u003e {}\", target_file_name.clone()).as_str());\n target_file.write_all(\u0026decompressed_data).expect(format!(\"Unable to write uncompressed file -\u003e {}\", target_file_name).as_str());\n println!(\"Decompressed: {:?}\", String::from_utf8_lossy(\u0026decompressed_data));\n}\n\nfn main() {\n let cli = Cli::parse();\n\n match cli.command {\n Commands::Compress { input } =\u003e {\n let compressed = compress_string(\u0026input);\n // Convert to hex format for easier readability in the terminal\n println!(\"Compressed (hex): {:?} / from {}b to {}b\", hex::encode(compressed.clone()), input.len(), compressed.len());\n }\n Commands::Decompress { input } =\u003e {\n // Decode hex string back to bytes\n let compressed_bytes = hex::decode(input).expect(\"Invalid hex input\");\n let decompressed = decompress_string(\u0026compressed_bytes);\n println!(\"Decompressed: {}\", decompressed);\n }\n Commands::DecompressFile { input } =\u003e {\n decompress_file(\u0026input);\n }\n Commands::CompressFile { input } =\u003e {\n compress_file(\u0026input);\n }\n }\n}\n","traces":[{"line":37,"address":[319646,319671,319120],"length":1,"stats":{"Line":0}},{"line":38,"address":[319163,319680],"length":1,"stats":{"Line":0}},{"line":39,"address":[319398],"length":1,"stats":{"Line":0}},{"line":40,"address":[319439],"length":1,"stats":{"Line":0}},{"line":43,"address":[319712,320091,320116],"length":1,"stats":{"Line":0}},{"line":44,"address":[319755],"length":1,"stats":{"Line":0}},{"line":45,"address":[319882,319824],"length":1,"stats":{"Line":0}},{"line":46,"address":[319908],"length":1,"stats":{"Line":0}},{"line":47,"address":[320024],"length":1,"stats":{"Line":0}},{"line":50,"address":[320128,321794],"length":1,"stats":{"Line":0}},{"line":51,"address":[320148,320325],"length":1,"stats":{"Line":0}},{"line":52,"address":[320333,320398],"length":1,"stats":{"Line":0}},{"line":53,"address":[320480],"length":1,"stats":{"Line":0}},{"line":54,"address":[320537,320588,321834],"length":1,"stats":{"Line":0}},{"line":55,"address":[320700,320788],"length":1,"stats":{"Line":0}},{"line":56,"address":[320831],"length":1,"stats":{"Line":0}},{"line":57,"address":[321032],"length":1,"stats":{"Line":0}},{"line":58,"address":[321335,321453],"length":1,"stats":{"Line":0}},{"line":61,"address":[323944,323835,321872],"length":1,"stats":{"Line":0}},{"line":62,"address":[321892],"length":1,"stats":{"Line":0}},{"line":63,"address":[322069,322134],"length":1,"stats":{"Line":0}},{"line":64,"address":[322216],"length":1,"stats":{"Line":0}},{"line":65,"address":[322281],"length":1,"stats":{"Line":0}},{"line":66,"address":[322329],"length":1,"stats":{"Line":0}},{"line":67,"address":[322449],"length":1,"stats":{"Line":0}},{"line":68,"address":[322472,322536],"length":1,"stats":{"Line":0}},{"line":69,"address":[322575,323885,322962],"length":1,"stats":{"Line":0}},{"line":70,"address":[323142,323419,323844],"length":1,"stats":{"Line":0}},{"line":71,"address":[323544],"length":1,"stats":{"Line":0}},{"line":74,"address":[324784,323968],"length":1,"stats":{"Line":0}},{"line":75,"address":[323980],"length":1,"stats":{"Line":0}},{"line":77,"address":[323985],"length":1,"stats":{"Line":0}},{"line":78,"address":[324018],"length":1,"stats":{"Line":0}},{"line":79,"address":[324336,324046],"length":1,"stats":{"Line":0}},{"line":81,"address":[324434,324343],"length":1,"stats":{"Line":0}},{"line":83,"address":[324066],"length":1,"stats":{"Line":0}},{"line":85,"address":[324113],"length":1,"stats":{"Line":0}},{"line":86,"address":[324865,324165],"length":1,"stats":{"Line":0}},{"line":87,"address":[324940,324872],"length":1,"stats":{"Line":0}},{"line":89,"address":[324233],"length":1,"stats":{"Line":0}},{"line":90,"address":[324267],"length":1,"stats":{"Line":0}},{"line":92,"address":[324189],"length":1,"stats":{"Line":0}},{"line":93,"address":[324223],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":43},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","computer.rs"],"content":"use crate::chip8::delay_timer::DelayTimer;\nuse crate::chip8::keypad::Keypad;\nuse crate::chip8::quirk_modes::QuirkMode;\nuse crate::chip8::registers::Chip8Registers;\nuse crate::chip8::sound_timer::SoundTimer;\nuse crate::chip8::stack::Chip8Stack;\nuse log::debug;\nuse serde::{Deserialize, Serialize};\nuse crate::chip8::util::InstructionUtil;\nuse super::{\n cpu_states::Chip8CpuStates, instructions::Chip8CpuInstructions,\n system_memory::Chip8SystemMemory, video::Chip8Video,\n};\n\n#[derive(Clone, Serialize, Deserialize, Debug)]\npub struct Chip8Computer {\n pub num_cycles: i32,\n pub memory: Chip8SystemMemory,\n pub registers: Chip8Registers,\n pub sound_timer: SoundTimer,\n pub delay_timer: DelayTimer,\n pub video_memory: Chip8Video,\n pub state: Chip8CpuStates,\n pub keypad: Keypad,\n pub stack: Chip8Stack,\n pub quirk_mode: QuirkMode,\n}\n\nimpl PartialEq for Chip8Computer {\n fn eq(\u0026self, other: \u0026Self) -\u003e bool {\n self.video_memory.format_as_string() == other.video_memory.format_as_string() \u0026\u0026\n self.keypad.format_as_string() == other.keypad.format_as_string() \u0026\u0026\n self.num_cycles == other.num_cycles \u0026\u0026\n self.memory == other.memory\n }\n}\n\nimpl Default for Chip8Computer {\n fn default() -\u003e Self {\n Self {\n num_cycles: 0,\n memory: Chip8SystemMemory::default(),\n video_memory: Chip8Video::default(),\n registers: Chip8Registers::default(),\n sound_timer: SoundTimer::new(),\n delay_timer: DelayTimer::new(),\n state: Chip8CpuStates::default(),\n keypad: Keypad::default(),\n stack: Chip8Stack::default(),\n quirk_mode: QuirkMode::default(),\n }\n }\n}\n\nimpl Chip8Computer {\n pub fn reset(\u0026mut self, quirk_mode: QuirkMode) {\n self.video_memory.reset();\n self.num_cycles = 0;\n self.registers.reset();\n self.delay_timer.reset();\n self.sound_timer.reset();\n self.stack.reset();\n self.memory.reset(quirk_mode);\n self.quirk_mode = quirk_mode;\n }\n\n pub fn dump_keypad_to_string(\u0026self) -\u003e String {\n self.keypad.format_as_string()\n }\n\n pub fn dump_registers_to_string(\u0026self) -\u003e String {\n self.registers.format_as_string()\n }\n\n pub fn dump_video_to_string(\u0026self) -\u003e String {\n self.clone().video_memory.format_as_string()\n }\n\n pub fn new() -\u003e Self {\n Chip8Computer::default()\n }\n\n pub fn load_bytes_to_memory(\u0026mut self, offset: u16, to_load: \u0026Vec\u003cu8\u003e) {\n let total_len = to_load.len() as u16;\n for current_index in 0..total_len {\n let new_value = to_load[current_index as usize];\n let new_location = current_index + offset;\n self.memory.poke(new_location, new_value);\n }\n self.registers.set_pc(offset);\n }\n\n\n pub fn step_system(\u0026mut self) -\u003e \u0026mut Chip8Computer {\n println!(\"Stepping System 1 Step\");\n // read the next instruction\n let local_memory = \u0026self.memory;\n\n // let mut working_instruction: u16 = 0b0000000000000000;\n let start_pc = self.registers.peek_pc();\n let high_byte = (local_memory.peek(start_pc) as u16).rotate_left(8);\n let low_byte = local_memory.peek(start_pc + 1) as u16;\n let result = high_byte | low_byte;\n let decoded_instruction = Chip8CpuInstructions::decode(result, \u0026self.quirk_mode);\n // todo: THIS IS BAD AND IS A SIDE EFFECT\n decoded_instruction.execute(self);\n\n match self.state {\n Chip8CpuStates::WaitingForInstruction =\u003e {\n // println!(\"Ticking sound, delay, video\");\n self.sound_timer.tick();\n self.delay_timer.tick();\n self.video_memory.tick();\n self.num_cycles += 1;\n }\n Chip8CpuStates::WaitingForKey =\u003e {\n debug!(\"waiting for a key press...\");\n }\n _ =\u003e {}\n }\n self\n }\n}\n","traces":[{"line":30,"address":[1713632,1714187],"length":1,"stats":{"Line":1}},{"line":31,"address":[814247],"length":1,"stats":{"Line":1}},{"line":32,"address":[41428651],"length":1,"stats":{"Line":1}},{"line":33,"address":[814715],"length":1,"stats":{"Line":1}},{"line":34,"address":[41428913],"length":1,"stats":{"Line":1}},{"line":39,"address":[806336,806803],"length":1,"stats":{"Line":5}},{"line":42,"address":[1618577],"length":1,"stats":{"Line":6}},{"line":43,"address":[806367],"length":1,"stats":{"Line":11}},{"line":44,"address":[1334568],"length":1,"stats":{"Line":12}},{"line":45,"address":[1774526],"length":1,"stats":{"Line":7}},{"line":46,"address":[1774541],"length":1,"stats":{"Line":12}},{"line":47,"address":[1026076],"length":1,"stats":{"Line":7}},{"line":48,"address":[1026092],"length":1,"stats":{"Line":13}},{"line":49,"address":[1774587],"length":1,"stats":{"Line":7}},{"line":50,"address":[900850],"length":1,"stats":{"Line":13}},{"line":56,"address":[1714720],"length":1,"stats":{"Line":2}},{"line":57,"address":[1335001],"length":1,"stats":{"Line":2}},{"line":58,"address":[1619096],"length":1,"stats":{"Line":4}},{"line":59,"address":[4039567],"length":1,"stats":{"Line":4}},{"line":60,"address":[1774942],"length":1,"stats":{"Line":4}},{"line":61,"address":[41429549],"length":1,"stats":{"Line":4}},{"line":62,"address":[1714812],"length":1,"stats":{"Line":4}},{"line":63,"address":[2491071],"length":1,"stats":{"Line":4}},{"line":64,"address":[551163],"length":1,"stats":{"Line":4}},{"line":67,"address":[823088],"length":1,"stats":{"Line":1}},{"line":68,"address":[4039681],"length":1,"stats":{"Line":1}},{"line":71,"address":[1714912],"length":1,"stats":{"Line":1}},{"line":72,"address":[4039729],"length":1,"stats":{"Line":1}},{"line":75,"address":[901492,901376],"length":1,"stats":{"Line":2}},{"line":76,"address":[1335245],"length":1,"stats":{"Line":2}},{"line":79,"address":[551424],"length":1,"stats":{"Line":5}},{"line":80,"address":[1026792],"length":1,"stats":{"Line":9}},{"line":83,"address":[901552],"length":1,"stats":{"Line":3}},{"line":84,"address":[823405],"length":1,"stats":{"Line":1}},{"line":85,"address":[1026958,1026872],"length":1,"stats":{"Line":4}},{"line":86,"address":[1026973],"length":1,"stats":{"Line":3}},{"line":87,"address":[901750,901801],"length":1,"stats":{"Line":1}},{"line":88,"address":[1027048],"length":1,"stats":{"Line":3}},{"line":90,"address":[901671],"length":1,"stats":{"Line":1}},{"line":94,"address":[823632],"length":1,"stats":{"Line":4}},{"line":95,"address":[4040228],"length":1,"stats":{"Line":2}},{"line":97,"address":[1027142],"length":1,"stats":{"Line":4}},{"line":100,"address":[807582],"length":1,"stats":{"Line":2}},{"line":101,"address":[1775658,1775721],"length":1,"stats":{"Line":6}},{"line":102,"address":[2491999,2491809],"length":1,"stats":{"Line":4}},{"line":103,"address":[816211],"length":1,"stats":{"Line":2}},{"line":104,"address":[902067],"length":1,"stats":{"Line":4}},{"line":106,"address":[1335943],"length":1,"stats":{"Line":2}},{"line":108,"address":[1620066],"length":1,"stats":{"Line":2}},{"line":109,"address":[902321],"length":1,"stats":{"Line":2}},{"line":111,"address":[1336049],"length":1,"stats":{"Line":2}},{"line":112,"address":[41430560],"length":1,"stats":{"Line":2}},{"line":113,"address":[552123],"length":1,"stats":{"Line":4}},{"line":114,"address":[2492150,2492078,2492142],"length":1,"stats":{"Line":6}},{"line":117,"address":[1027634,1027534],"length":1,"stats":{"Line":2}}],"covered":55,"coverable":55},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","computer_manager.rs"],"content":"use std::fs::File;\nuse std::io::{BufReader, Read};\nuse std::path::Path;\nuse crate::chip8::computer::Chip8Computer;\nuse crate::chip8::cpu_states::Chip8CpuStates;\nuse crate::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction;\nuse crate::chip8::quirk_modes::QuirkMode;\nuse std::thread;\nuse std::thread::sleep;\nuse std::time::{Duration, Instant};\n\npub enum ManagerDumpables {\n Video,\n Registers,\n Keyboard,\n}\n\npub struct Chip8ComputerManager {\n pub core_should_run: bool,\n one_step: bool,\n core_last_cycle_start: Instant,\n computer: Chip8Computer,\n}\n\nimpl Default for Chip8ComputerManager {\n fn default() -\u003e Self {\n Chip8ComputerManager {\n core_should_run: false,\n one_step: false,\n core_last_cycle_start: Instant::now(),\n computer: Chip8Computer::new(),\n }\n }\n}\n\nimpl Chip8ComputerManager {\n\n /// Builds a string that can be displayed at a console giving the user\n /// an idea of the internal state of the Chip-8 Computer\n pub fn status_as_string(\u0026self) -\u003e String {\n // build a string\n format!(\"Should Run: {}\\nLast Cycle Start: {:?}\\nNum Cycles: {}\\nRegisters\\n{}\\nTimers: {}/{}\\nKeypad: \\n{}\\nVideo:\\n{}\",\n self.core_should_run,\n self.core_last_cycle_start,\n self.computer.num_cycles,\n self.computer.registers.format_as_string(),\n self.computer.delay_timer.current(),\n self.computer.sound_timer.current(),\n self.computer.keypad.format_as_string(),\n self.computer.video_memory.format_as_string()\n )\n }\n\n pub fn quirks_mode(\u0026self) -\u003e QuirkMode {\n self.computer.quirk_mode.clone()\n }\n\n pub fn reset(\u0026mut self, mode: QuirkMode) {\n self.one_step = false;\n self.core_should_run = false;\n self.computer.reset(mode);\n }\n pub fn new() -\u003e Chip8ComputerManager {\n let _ = thread::spawn(move || loop {\n let start_time = Instant::now();\n let sleep_time = Instant::now().duration_since(start_time).as_millis();\n sleep(Duration::from_millis((16 - sleep_time) as u64));\n });\n\n Chip8ComputerManager::default()\n }\n\n pub fn start(\u0026mut self) {\n self.core_should_run = true;\n }\n\n pub fn stop(\u0026mut self) {\n self.core_should_run = false\n }\n\n pub fn step(\u0026mut self) {\n self.one_step = true;\n }\n\n pub fn state(\u0026mut self) -\u003e \u0026Chip8Computer {\n \u0026self.computer\n }\n\n pub fn tick(\u0026mut self) -\u003e bool {\n // println!(\"STARTING TICK\");\n let mut did_tick: bool = false;\n if self.one_step | self.core_should_run {\n if let WaitingForInstruction = self.computer.state {\n self.core_last_cycle_start = Instant::now();\n self.computer.step_system();\n did_tick = true\n // println!(\"SYSTEM STEP\");\n }\n };\n if self.one_step {\n println!(\"SYSTEM HALTED AFTER 1 STEP\");\n // stop the CPU for the next cycle, we are only\n // wanting one step.\n self.one_step = false;\n did_tick = true;\n }\n did_tick\n }\n\n pub fn press_key(\u0026mut self, key_index: u8) {\n self.computer.keypad.push_key(key_index);\n if matches!(self.computer.state, Chip8CpuStates::WaitingForKey) {\n self.computer.state = WaitingForInstruction\n }\n }\n pub fn release_key(\u0026mut self, key_index: u8) {\n self.computer.keypad.release_key(key_index);\n }\n\n pub fn sound(managed: \u0026Chip8ComputerManager) -\u003e bool {\n managed.computer.sound_timer.current() \u003e 0\n }\n\n pub fn wait_for_instruction(\u0026mut self) {\n self.computer.state = WaitingForInstruction;\n }\n\n pub fn is_key_pressed(\u0026self, key_index: u8) -\u003e bool {\n self.computer.keypad.pressed(key_index)\n }\n\n pub fn num_cycles(\u0026self) -\u003e i32 {\n self.computer.num_cycles\n }\n\n pub fn load_new_program_to_system_memory(\u0026mut self, bytes_to_load: Vec\u003cu8\u003e) {\n self.reset(self.computer.quirk_mode.clone());\n self.computer.load_bytes_to_memory(0x200, \u0026bytes_to_load);\n }\n\n pub fn dump_to_string(\u0026self, dump_type: ManagerDumpables) -\u003e String {\n match dump_type {\n ManagerDumpables::Video =\u003e self.computer.video_memory.format_as_string(),\n ManagerDumpables::Registers =\u003e self.computer.registers.format_as_string(),\n ManagerDumpables::Keyboard =\u003e self.computer.keypad.format_as_string(),\n }\n }\n\n pub fn load_new_program_from_disk_to_system_memory(\u0026mut self, file_to_load: \u0026Path) {\n // load the file into bytes...\n let file = File::open(file_to_load).unwrap();\n let mut reader = BufReader::new(file);\n let mut chunks = Vec::new();\n\n loop {\n let mut buffer = vec![0u8; 1];\n let bytes_read = reader.read(\u0026mut buffer).unwrap_or(0);\n if bytes_read == 0 {\n break;\n }\n buffer.truncate(bytes_read);\n chunks.push(buffer[0]);\n }\n\n self.load_new_program_to_system_memory(chunks);\n // ...then use the existing method\n self.core_should_run = true;\n }\n}\n","traces":[{"line":26,"address":[784032],"length":1,"stats":{"Line":3}},{"line":30,"address":[4006017],"length":1,"stats":{"Line":4}},{"line":31,"address":[2452080],"length":1,"stats":{"Line":3}},{"line":40,"address":[1680736,1681687],"length":1,"stats":{"Line":0}},{"line":42,"address":[4006254,4006414,4006352,4006142,4006568,4006469],"length":1,"stats":{"Line":0}},{"line":46,"address":[4006223],"length":1,"stats":{"Line":0}},{"line":47,"address":[2452370],"length":1,"stats":{"Line":0}},{"line":48,"address":[473372],"length":1,"stats":{"Line":0}},{"line":49,"address":[4006446],"length":1,"stats":{"Line":0}},{"line":50,"address":[775329],"length":1,"stats":{"Line":0}},{"line":54,"address":[785120],"length":1,"stats":{"Line":1}},{"line":55,"address":[2453141],"length":1,"stats":{"Line":1}},{"line":58,"address":[2453152],"length":1,"stats":{"Line":2}},{"line":59,"address":[2453168],"length":1,"stats":{"Line":2}},{"line":60,"address":[784375],"length":1,"stats":{"Line":2}},{"line":61,"address":[2453182],"length":1,"stats":{"Line":2}},{"line":63,"address":[2453200],"length":1,"stats":{"Line":1}},{"line":64,"address":[474142],"length":1,"stats":{"Line":2}},{"line":65,"address":[1836740],"length":1,"stats":{"Line":1}},{"line":66,"address":[896796],"length":1,"stats":{"Line":1}},{"line":67,"address":[557370],"length":1,"stats":{"Line":1}},{"line":70,"address":[41396569],"length":1,"stats":{"Line":1}},{"line":73,"address":[776000],"length":1,"stats":{"Line":1}},{"line":74,"address":[2453269],"length":1,"stats":{"Line":1}},{"line":77,"address":[776016],"length":1,"stats":{"Line":1}},{"line":78,"address":[2453285],"length":1,"stats":{"Line":1}},{"line":81,"address":[41396624],"length":1,"stats":{"Line":1}},{"line":82,"address":[474229],"length":1,"stats":{"Line":1}},{"line":85,"address":[776048],"length":1,"stats":{"Line":1}},{"line":89,"address":[2453328],"length":1,"stats":{"Line":0}},{"line":91,"address":[784541],"length":1,"stats":{"Line":0}},{"line":92,"address":[474274],"length":1,"stats":{"Line":0}},{"line":93,"address":[785411,785367],"length":1,"stats":{"Line":0}},{"line":94,"address":[776129],"length":1,"stats":{"Line":0}},{"line":95,"address":[785400],"length":1,"stats":{"Line":0}},{"line":96,"address":[2453422],"length":1,"stats":{"Line":0}},{"line":100,"address":[785469,785352],"length":1,"stats":{"Line":0}},{"line":101,"address":[2453440],"length":1,"stats":{"Line":0}},{"line":104,"address":[784673],"length":1,"stats":{"Line":0}},{"line":105,"address":[4007432],"length":1,"stats":{"Line":0}},{"line":107,"address":[776165],"length":1,"stats":{"Line":0}},{"line":110,"address":[776224],"length":1,"stats":{"Line":0}},{"line":111,"address":[1682085],"length":1,"stats":{"Line":0}},{"line":112,"address":[2453527],"length":1,"stats":{"Line":0}},{"line":113,"address":[784742],"length":1,"stats":{"Line":0}},{"line":116,"address":[785552],"length":1,"stats":{"Line":0}},{"line":117,"address":[784784],"length":1,"stats":{"Line":0}},{"line":120,"address":[785600],"length":1,"stats":{"Line":0}},{"line":121,"address":[4007573],"length":1,"stats":{"Line":0}},{"line":124,"address":[2453648],"length":1,"stats":{"Line":0}},{"line":125,"address":[4007605],"length":1,"stats":{"Line":0}},{"line":128,"address":[776416],"length":1,"stats":{"Line":0}},{"line":129,"address":[1682272],"length":1,"stats":{"Line":0}},{"line":132,"address":[776464],"length":1,"stats":{"Line":1}},{"line":133,"address":[41397061],"length":1,"stats":{"Line":1}},{"line":136,"address":[785859,785728],"length":1,"stats":{"Line":2}},{"line":137,"address":[474747,474691],"length":1,"stats":{"Line":4}},{"line":138,"address":[785043],"length":1,"stats":{"Line":2}},{"line":141,"address":[1682480],"length":1,"stats":{"Line":0}},{"line":142,"address":[776670],"length":1,"stats":{"Line":0}},{"line":143,"address":[785178],"length":1,"stats":{"Line":0}},{"line":144,"address":[4007952],"length":1,"stats":{"Line":0}},{"line":145,"address":[2454022],"length":1,"stats":{"Line":0}},{"line":149,"address":[4008000,4008752,4008783],"length":1,"stats":{"Line":2}},{"line":151,"address":[2454250,2454084],"length":1,"stats":{"Line":4}},{"line":152,"address":[41397585],"length":1,"stats":{"Line":2}},{"line":153,"address":[777007],"length":1,"stats":{"Line":2}},{"line":155,"address":[786315],"length":1,"stats":{"Line":2}},{"line":156,"address":[475238],"length":1,"stats":{"Line":2}},{"line":157,"address":[786474,786394],"length":1,"stats":{"Line":4}},{"line":158,"address":[1683135],"length":1,"stats":{"Line":2}},{"line":161,"address":[41397917],"length":1,"stats":{"Line":2}},{"line":162,"address":[777447],"length":1,"stats":{"Line":2}},{"line":165,"address":[777349],"length":1,"stats":{"Line":2}},{"line":167,"address":[4008629],"length":1,"stats":{"Line":2}}],"covered":39,"coverable":75},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","cpu_states.rs"],"content":"use serde::{Deserialize, Serialize};\n\n#[derive(Clone, Copy, Default, Serialize, Deserialize, Debug)]\npub enum Chip8CpuStates {\n #[default]\n WaitingForInstruction,\n WaitingForKey,\n ExecutingInstruction,\n Error,\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","delay_timer.rs"],"content":"use serde::{Deserialize, Serialize};\n\n#[derive(Clone, Copy, Serialize, Deserialize, Debug)]\npub struct DelayTimer {\n counter: u8,\n}\n\nimpl Default for DelayTimer {\n fn default() -\u003e Self {\n Self::new()\n }\n}\n\nimpl DelayTimer {\n pub fn current(\u0026self) -\u003e u8 {\n self.counter\n }\n\n pub fn new() -\u003e Self {\n DelayTimer { counter: 0xff }\n }\n\n pub fn reset(\u0026mut self) {\n self.counter = 0xff;\n }\n\n pub fn set_timer(\u0026mut self, new_value: u8) {\n self.counter = new_value\n }\n\n pub fn tick(\u0026mut self) {\n if self.counter \u003e 0 {\n self.counter -= 1;\n }\n }\n}\n","traces":[{"line":9,"address":[1711936],"length":1,"stats":{"Line":1}},{"line":10,"address":[805249],"length":1,"stats":{"Line":1}},{"line":15,"address":[1023184],"length":1,"stats":{"Line":1}},{"line":16,"address":[897925],"length":1,"stats":{"Line":1}},{"line":23,"address":[813760],"length":1,"stats":{"Line":4}},{"line":24,"address":[4036789],"length":1,"stats":{"Line":4}},{"line":27,"address":[1772160],"length":1,"stats":{"Line":2}},{"line":28,"address":[897980],"length":1,"stats":{"Line":2}},{"line":31,"address":[1616000],"length":1,"stats":{"Line":5}},{"line":32,"address":[813806,813847],"length":1,"stats":{"Line":5}},{"line":33,"address":[813821,813849],"length":1,"stats":{"Line":4}}],"covered":11,"coverable":11},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","instructions.rs"],"content":"use crate::chip8::computer::Chip8Computer;\nuse crate::chip8::cpu_states::Chip8CpuStates::WaitingForKey;\nuse crate::chip8::instructions::Chip8CpuInstructions::*;\nuse crate::chip8::quirk_modes::QuirkMode;\nuse crate::chip8::util::InstructionUtil;\nuse crate::constants::*;\nuse log::debug;\nuse rand::Rng;\nuse serde::{Deserialize, Serialize};\nuse std::fmt::{Debug, Display, Formatter};\nuse std::ops::BitAnd;\n\n/*\nnnn or addr - A 12-bit value, the lowest 12 bits of the instruction\nn or nibble - A 4-bit value, the lowest 4 bits of the instruction\nx - A 4-bit value, the lower 4 bits of the high byte of the instruction\ny - A 4-bit value, the upper 4 bits of the low byte of the instruction\nkk or byte - An 8-bit value, the lowest 8 bits of the instruction\n */\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub enum Chip8CpuInstructions {\n /// 0nnn\n /// Exit to System Call at nnn\n SYS(u16),\n /// 00E0\n /// Clear the display.\n CLS,\n /// 00EE\n /// Return from a subroutine.\n ///\n /// The interpreter sets the program counter to the address at the top of the stack,\n /// then subtracts 1 from the stack pointer.\n RET,\n /// 1nnn\n /// Jump to location nnn.\n ///\n /// The interpreter sets the program counter to nnn.\n JPA(u16),\n /// 2nnn\n /// Call subroutine at nnn.\n ///\n /// The interpreter increments the stack pointer, then puts the current PC on the top\n /// of the stack. The PC is then set to nnn.\n CALL(u16),\n /// 0x3xkk\n /// Skip next instruction if Vx = kk.\n ///\n /// The interpreter compares register Vx to kk, and if they are equal, increments the\n /// program counter by 2.\n SEX(u8, u8),\n /// 4xkk\n /// Skip next instruction if Vx != kk.\n ///\n /// The interpreter compares register Vx to kk, and if they are not equal, increments\n /// the program counter by 2.\n SNEB(u8, u8),\n /// 5xy0\n /// Skip next instruction if Vx = Vy.\n ///\n /// The interpreter compares register Vx to register Vy, and if they are equal, increments\n /// the program counter by 2.\n SEY(u8, u8),\n /// 6xkk\n /// Set Vx = kk\n LDR(u8, u8),\n /// 7xkk\n /// The interpreter puts the value kk into register Vx.\n ADD(u8, u8),\n /// 8xy0\n /// Adds the value kk to the value of register Vx, then stores the result in Vx.\n LDRY(u8, u8),\n /// 8xy1\n /// Stores the value of register Vy in register Vx.\n OR(u8, u8),\n /// 8xy2\n /// Set Vx = Vx OR Vy.\n ///\n /// Performs a bitwise OR on the values of Vx and Vy, then stores the result in Vx.\n /// A bitwise OR compares the corrseponding bits from two values, and if either\n /// bit is 1, then the same bit in the result is also 1. Otherwise, it is 0.\n AND(u8, u8),\n /// 8xy3\n /// Set Vx = Vx AND Vy.\n ///\n /// Performs a bitwise AND on the values of Vx and Vy, then stores the result in Vx.\n /// A bitwise AND compares the corrseponding bits from two values, and if both bits\n /// are 1, then the same bit in the result is also 1. Otherwise, it is 0.\n ORY(u8, u8),\n /// 8xy4\n /// Set Vx = Vx XOR Vy.\n ///\n /// Performs a bitwise exclusive OR on the values of Vx and Vy, then stores the\n /// result in Vx. An exclusive OR compares the corrseponding bits from two values,\n /// and if the bits are not both the same, then the corresponding bit in the result\n /// is set to 1. Otherwise, it is 0.\n ADDR(u8, u8),\n /// 8xy5\n /// Set Vx = Vx + Vy, set VF = carry.\n ///\n /// The values of Vx and Vy are added together. If the result is greater than 8 bits\n /// (i.e., \u003e 255,) VF is set to 1, otherwise 0. Only the lowest 8 bits of the result\n /// are kept, and stored in Vx.\n SUB(u8, u8),\n /// 8xy6\n /// Set Vx = Vx - Vy, set VF = NOT borrow.\n ///\n /// If Vx \u003e Vy, then VF is set to 1, otherwise 0. Then Vy is subtracted from Vx, and the\n /// results stored in Vx.\n SHR(u8, u8),\n /// 8xy7\n /// If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0. Then Vx\n /// is divided by 2.\n SUBC(u8, u8),\n /// 8xye\n /// Set Vx = Vy - Vx, set VF = NOT borrow.\n //\n /// If Vy \u003e Vx, then VF is set to 1, otherwise 0. Then Vx is subtracted from Vy, and the\n /// results stored in Vx.\n SHL(u8, u8),\n /// 9xy0\n /// Skip next instruction if Vx != Vy.\n ///\n /// The values of Vx and Vy are compared, and if they are not equal, the program\n /// counter is increased by 2.\n SNEY(u8, u8),\n /// Annn\n /// Set I = nnn.\n ///\n /// The value of register I is set to nnn\n LDIA(u16),\n /// Bnnn\n /// Jump to location nnn + V0.\n ///\n /// The program counter is set to nnn plus the value of V0.\n JPI(u16),\n /// Cxkk\n /// Set Vx = random byte AND kk.\n ///\n /// The interpreter generates a random number from 0 to 255, which is then\n /// ANDed with the value kk. The results are stored in Vx. See instruction\n /// 8xy2 for more information on AND.\n RND(u8, u8),\n /// Dxyn\n /// Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision.\n ///\n /// In Chip-8,\n ///\n /// The interpreter reads n bytes from memory, starting at the address stored in I.\n /// These bytes are then displayed as sprites on screen at coordinates (Vx, Vy).\n /// Sprites are XORed onto the existing screen. If this causes any pixels to be erased,\n /// VF is set to 1, otherwise it is set to 0. If the sprite is positioned so part of\n /// it is outside the coordinates of the display, it wraps around to the opposite side\n /// of the screen. See instruction 8xy3 for more information on XOR, and section 2.4,\n /// Display, for more information on the Chip-8 screen and sprites.\n ///\n /// In SCHIP\n ///\n /// Show N-byte sprite from M(I) at Coords (VX,VY) VF = Collision.\n /// If N=0 AND ExtendedMode, 16x16 sprite\n DRW(u8, u8, u8),\n /// Ex9E\n /// Skip next instruction if key with the value of Vx is pressed.\n ///\n /// Checks the keyboard, and if the key corresponding to the value of Vx is currently\n /// in the down position, PC is increased by 2.\n SKP(u8),\n /// ExA1\n /// Skip next instruction if key with the value of Vx is not pressed.\n ///\n /// Checks the keyboard, and if the key corresponding to the value of Vx is currently in\n /// the up position, PC is increased by 2.\n /// On XO Chip, Skips 2 more bytes when the instruction is 4 bytes long\n SKNP(u8),\n /// Fx07\n /// The value of DT is placed into Vx.\n LDRD(u8),\n /// Fx0A\n /// Wait for a key press, store the value of the key in Vx.\n ///\n /// All execution stops until a key is pressed, then the value of that key is stored in Vx.\n LDRK(u8),\n /// Fx15\n /// Set delay timer = Vx.\n ///\n /// DT is set equal to the value of Vx.\n LDD(u8), // 0xFx15 Set Delay Timer\n /// Fx18\n /// Set sound timer = Vx.\n ///\n /// ST is set equal to the value of Vx.\n LDIS(u8),\n /// Fx1E - ADD I, Vx\n /// Set I = I + Vx.\n ///\n /// The values of I and Vx are added, and the results are stored in I.\n ADDI(u8),\n /// Fx29 - LD F, Vx\n /// Set I = location of sprite for digit Vx.\n ///\n /// The value of I is set to the location for the hexadecimal sprite\n /// corresponding to the value of Vx. See section 2.4, Display, for\n /// more information on the Chip-8 hexadecimal font.\n LDFX(u8),\n /// Fx33 - LD B, Vx\n /// Store BCD representation of Vx in memory locations I, I+1, and I+2.\n ///\n /// The interpreter takes the decimal value of Vx, and places the hundreds\n /// digit in memory at location in I, the tens digit at location I+1, and the\n /// ones digit at location I+2.\n BCD(u8),\n /// Fx55 - LD [I], Vx\n /// Store registers V0 through Vx in memory starting at location I.\n ///\n /// The interpreter copies the values of registers V0 through Vx into memory,\n /// starting at the address in I.\n LDIX(u8),\n /// Fx65 - LD Vx, [I]\n /// Read registers V0 through Vx from memory starting at location I.\n ///\n /// The interpreter reads values from memory starting at location I into registers\n /// V0 through Vx.\n LDRI(u8),\n XXXXERRORINSTRUCTION,\n // 00Dn - CHIP8 - SCHIP * XOCHIP\n /* START OF SCHIP-8 */\n /// 00CN - CHIP8 * SCHIP * XOCHIP\n ///\n /// Scrolll Display N Lines Down\n SCD(u8),\n /// 00FB\n ///\n /// Scroll 4 lines Right\n SCR,\n /// 00FC\n ///\n /// Scroll 4 lines Left\n SCL,\n /// 00FE\n ///\n /// Disable Extended Mode\n LOW,\n /// 00FF\n ///\n /// Enable Extended Mode\n HIGH,\n /// 00FD\n ///\n /// Exit App\n EXIT,\n /// FX30\n ///\n /// Point I to 10 yte font sprite for digit VX (0..9)\n LDF2(u8),\n /// FX75\n ///\n /// Store V0..VX in RPL user flags (X \u003c= 7\n STR(u8),\n /// FX85\n ///\n /// Load V0..VX from RPL user flags (X \u003c= 7)\n LIDR(u8),\n /// 0xBxNN\n ///\n /// Jump to Address XNN+Vx\n JPX(u8, u16),\n /// 0x00Cn\n ///\n /// scroll screen content down N pixel, in XO-CHIP only selected bit\n /// planes are scrolled (Quirks are HP48 specific)\n SCU(u8),\n /// 0xNNNN\n ///\n /// data word\n /// used for specifying data to be used in system memory\n DW(u16),\n}\n\nimpl Chip8CpuInstructions {\n pub fn name(\u0026self) -\u003e \u0026str {\n match self {\n ADDI(_) =\u003e INST_ADDI,\n ADD(_, _) =\u003e INST_ADD,\n ADDR(_, _) =\u003e INST_ADDR,\n AND(_, _) =\u003e INST_AND,\n CLS =\u003e INST_CLS,\n CALL(_) =\u003e INST_CALL,\n DRW(_, _, _) =\u003e INST_DRW,\n EXIT =\u003e INST_EXIT,\n JPA(_) =\u003e INST_JPA,\n JPI(_) =\u003e INST_JPI,\n BCD(_) =\u003e INST_BCD,\n LDD(_) =\u003e INST_LDD,\n LDFX(_) =\u003e INST_LDF,\n LDF2(_) =\u003e INST_LDF2,\n LDIA(_) =\u003e INST_LDIA,\n LDIX(_) =\u003e INST_LDIX,\n LIDR(_) =\u003e INST_LIDR,\n LDIS(_) =\u003e INST_LDIS,\n LDR(_, _) =\u003e INST_LDR,\n LDRD(_) =\u003e INST_LDRD,\n LDRI(_) =\u003e INST_LDRI,\n LDRK(_) =\u003e INST_LDRK,\n LDRY(_, _) =\u003e INST_LDRY,\n OR(_, _) =\u003e INST_OR,\n RET =\u003e INST_RET,\n RND(_, _) =\u003e INST_RND,\n SCD(_) =\u003e INST_SCD,\n SCL =\u003e INST_SCL,\n SCR =\u003e INST_SCR,\n SEX(_, _) =\u003e INST_SEX,\n SEY(_, _) =\u003e INST_SEY,\n SHL(_, _) =\u003e INST_SHL,\n SHR(_, _) =\u003e INST_SHR,\n SKP(_) =\u003e INST_SKP,\n SNEB(_, _) =\u003e INST_SNEB,\n SNEY(_, _) =\u003e INST_SNEY,\n SKNP(_) =\u003e INST_SKNP,\n STR(_) =\u003e INST_STR,\n SUB(_, _) =\u003e INST_SUB,\n SUBC(_, _) =\u003e INST_SUBC,\n SYS(_) =\u003e INST_SYS,\n LOW =\u003e INST_LOW,\n HIGH =\u003e INST_HIGH,\n ORY(_, _) =\u003e INST_ORY,\n JPX(_, _) =\u003e INST_JPX,\n XXXXERRORINSTRUCTION =\u003e \"XX ERROR XX\",\n SCU(_) =\u003e INST_SCU,\n DW(_) =\u003e INST_DW,\n }\n }\n\n pub fn operands(\u0026self) -\u003e String {\n match self {\n JPX(x, addr) =\u003e {\n let addr_for_display = (*x as u16) \u003c\u003c 8 | *addr;\n format!(\"0x{x:02x}, 0x{addr_for_display:04x}\")\n }\n Chip8CpuInstructions::DW(addr)\n | Chip8CpuInstructions::SYS(addr)\n | Chip8CpuInstructions::JPI(addr)\n | Chip8CpuInstructions::JPA(addr)\n | Chip8CpuInstructions::LDIA(addr)\n | Chip8CpuInstructions::CALL(addr) =\u003e {\n format!(\"0x{addr:04x}\")\n }\n Chip8CpuInstructions::SEX(x, byte)\n | Chip8CpuInstructions::SNEB(x, byte)\n | Chip8CpuInstructions::LDR(x, byte)\n | Chip8CpuInstructions::RND(x, byte)\n | Chip8CpuInstructions::ADD(x, byte) =\u003e {\n format!(\"0x{x:02x}, 0x{byte:02x}\")\n }\n // Reg, Reg\n SEY(x, y)\n | Chip8CpuInstructions::LDRY(x, y)\n | Chip8CpuInstructions::OR(x, y)\n | Chip8CpuInstructions::AND(x, y)\n | Chip8CpuInstructions::ORY(x, y)\n | Chip8CpuInstructions::ADDR(x, y)\n | Chip8CpuInstructions::SUB(x, y)\n | Chip8CpuInstructions::SHR(x, y)\n | Chip8CpuInstructions::SUBC(x, y)\n | Chip8CpuInstructions::SHL(x, y)\n | Chip8CpuInstructions::SNEY(x, y) =\u003e {\n format!(\"0x{x:01x}, 0x{y:01x}\")\n }\n // Reg, Reg, Nibble\n Chip8CpuInstructions::DRW(x, y, nibble) =\u003e {\n format!(\"0x{x:02x}, 0x{y:02x}, 0x{nibble:02x}\")\n }\n // Registers. 0-F\n Chip8CpuInstructions::SCU(x)\n | Chip8CpuInstructions::LDD(x)\n | Chip8CpuInstructions::LDIS(x)\n | Chip8CpuInstructions::ADDI(x)\n | Chip8CpuInstructions::LDFX(x)\n | Chip8CpuInstructions::BCD(x)\n | Chip8CpuInstructions::LDIX(x)\n | Chip8CpuInstructions::LDRD(x)\n | Chip8CpuInstructions::LDRK(x)\n | Chip8CpuInstructions::LDRI(x)\n | Chip8CpuInstructions::LDF2(x)\n | Chip8CpuInstructions::STR(x)\n | Chip8CpuInstructions::LIDR(x)\n | Chip8CpuInstructions::SCD(x)\n | Chip8CpuInstructions::SKNP(x)\n | Chip8CpuInstructions::SKP(x) =\u003e {\n format!(\"0x{x:02x}\")\n }\n Chip8CpuInstructions::EXIT\n | Chip8CpuInstructions::HIGH\n | Chip8CpuInstructions::LOW\n | Chip8CpuInstructions::SCL\n | Chip8CpuInstructions::XXXXERRORINSTRUCTION\n | Chip8CpuInstructions::SCR\n | Chip8CpuInstructions::CLS\n | Chip8CpuInstructions::RET =\u003e String::new(),\n }\n }\n}\n\nimpl Display for Chip8CpuInstructions {\n fn fmt(\u0026self, f: \u0026mut Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n let ops = self.operands();\n let space = if ops.is_empty() { \"\" } else { \" \" };\n write!(f, \"{}{}{}\", self.name(), space, ops)\n }\n}\n\nimpl Chip8CpuInstructions {\n pub fn from_str(input: \u0026str) -\u003e Chip8CpuInstructions {\n let mut parts = input.split(\" \");\n // print!(\"THERE ARE {} PARTS\", parts.clone().count());\n let first_part = parts.next().unwrap_or(\"\").to_ascii_uppercase();\n // take the next value...\n // ...strip off the extra...\n // ...convert it to an integer from base 16\n let param1 = u16::from_str_radix(\n parts\n .next()\n .unwrap_or(\"0\")\n .to_ascii_lowercase()\n .trim_start_matches(\"0x\")\n .trim_end_matches(\",\"),\n 16,\n )\n .unwrap_or(0);\n let param2 = u16::from_str_radix(\n parts\n .next()\n .unwrap_or(\"0\")\n .to_ascii_lowercase()\n .trim_start_matches(\"0x\")\n .trim_end_matches(\",\"),\n 16,\n )\n .unwrap_or(0);\n let param3 = u16::from_str_radix(\n parts\n .next()\n .unwrap_or(\"0\")\n .to_ascii_lowercase()\n .trim_start_matches(\"0x\")\n .trim_end_matches(\",\"),\n 16,\n )\n .unwrap_or(0);\n println!(\n \"\\tFirst part is {:?} / {:?} / {:?} / {:?}\",\n first_part, param1, param2, param3\n );\n match first_part.as_str() {\n INST_ADDI =\u003e ADDI(param1 as u8),\n INST_ADD =\u003e ADD(param1 as u8, param2 as u8),\n INST_CLS =\u003e CLS,\n INST_DRW =\u003e DRW(param1 as u8, param2 as u8, param3 as u8),\n INST_CALL =\u003e CALL(param1),\n INST_SYS =\u003e SYS(param1),\n INST_RET =\u003e RET,\n INST_JPA =\u003e JPA(param1),\n INST_JPI =\u003e JPI(param1),\n INST_SEX =\u003e SEX(param1 as u8, param2 as u8),\n INST_SNEB =\u003e SNEB(param1 as u8, param2 as u8),\n INST_SCD =\u003e SCD(param1 as u8),\n INST_STR =\u003e STR(param1 as u8),\n INST_SCL =\u003e SCL,\n INST_EXIT =\u003e EXIT,\n INST_LOW =\u003e LOW,\n INST_HIGH =\u003e HIGH,\n INST_SEY =\u003e SEY(param1 as u8, param2 as u8),\n INST_LDRY =\u003e LDRY(param1 as u8, param2 as u8),\n INST_LDR =\u003e LDR(param1 as u8, param2 as u8),\n INST_OR =\u003e OR(param1 as u8, param2 as u8),\n INST_AND =\u003e AND(param1 as u8, param2 as u8),\n INST_ORY =\u003e ORY(param1 as u8, param2 as u8),\n INST_ADDR =\u003e ADDR(param1 as u8, param2 as u8),\n INST_SUB =\u003e SUB(param1 as u8, param2 as u8),\n INST_SHR =\u003e SHR(param1 as u8, param2 as u8),\n INST_SHL =\u003e SHL(param1 as u8, param2 as u8),\n INST_SUBC =\u003e SUBC(param1 as u8, param2 as u8),\n INST_SNEY =\u003e SNEY(param1 as u8, param2 as u8),\n INST_LDIA =\u003e LDIA(param1),\n INST_RND =\u003e RND(param1 as u8, param2 as u8),\n INST_SKP =\u003e SKP(param1 as u8),\n INST_SKNP =\u003e SKNP(param1 as u8),\n INST_LDRD =\u003e LDRD(param1 as u8),\n INST_LDRK =\u003e LDRK(param1 as u8),\n INST_LDRI =\u003e LDRI(param1 as u8),\n INST_BCD =\u003e BCD(param1 as u8),\n INST_LDF =\u003e LDFX(param1 as u8),\n INST_LDF2 =\u003e LDF2(param1 as u8),\n INST_LDIX =\u003e LDIX(param1 as u8),\n INST_LIDR =\u003e LIDR(param1 as u8),\n INST_LDIS =\u003e LDIS(param1 as u8),\n INST_LDD =\u003e LDD(param1 as u8),\n INST_JPX =\u003e JPX(param1 as u8, param2),\n INST_DW =\u003e DW(param1 as u16),\n _ =\u003e XXXXERRORINSTRUCTION,\n }\n }\n\n pub fn encode(\u0026self) -\u003e u16 {\n match self {\n SYS(target) =\u003e target \u0026 0x0FFF,\n CLS =\u003e 0x00E0,\n RET =\u003e 0x00EE,\n JPA(new_addr) =\u003e 0x1000 | (new_addr \u0026 0x0FFF),\n CALL(address) =\u003e 0x2000 | (address \u0026 0x0FFF),\n SEX(vx_register, byte) =\u003e {\n 0x3000 | ((*vx_register as u16) \u003c\u003c 8) | (*byte as u16)\n }\n SNEB(vx_register, byte) =\u003e {\n 0x4000 | ((*vx_register as u16) \u003c\u003c 8) | (*byte as u16)\n }\n Chip8CpuInstructions::SEY(x_register, y_register) =\u003e {\n 0x5000 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::LDR(x_register, byte) =\u003e {\n 0x6000 | ((*x_register as u16) \u003c\u003c 8) | (*byte as u16)\n }\n Chip8CpuInstructions::ADD(x_register, byte) =\u003e {\n 0x7000 | ((*x_register as u16) \u003c\u003c 8) | (*byte as u16)\n }\n Chip8CpuInstructions::LDRY(x_register, y_register) =\u003e {\n 0x8000 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::OR(x_register, y_register) =\u003e {\n 0x8001 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::AND(x_register, y_register) =\u003e {\n 0x8002 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::ORY(x_register, y_register) =\u003e {\n 0x8003 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::ADDR(x_register, y_register) =\u003e {\n 0x8004 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::SUB(x_register, y_register) =\u003e {\n 0x8005 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::SHR(x_register, y_register) =\u003e {\n 0x8006 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::SUBC(x_register, y_register) =\u003e {\n 0x8007 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::SHL(x_register, y_register) =\u003e {\n 0x800E | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::SNEY(x_register, y_register) =\u003e {\n 0x9000 | ((*x_register as u16) \u003c\u003c 8) | ((*y_register as u16) \u003c\u003c 4)\n }\n Chip8CpuInstructions::LDIA(addr) =\u003e 0xA000 | addr,\n Chip8CpuInstructions::JPI(addr) =\u003e 0xB000 | addr,\n JPX(x_register, addr) =\u003e (0xb000 | (*x_register as u16) \u003c\u003c 8) | *addr,\n Chip8CpuInstructions::RND(x_register, byte) =\u003e {\n 0xC000 | ((*x_register as u16) \u003c\u003c 8) | (*byte as u16)\n }\n Chip8CpuInstructions::DRW(x_register, y_register, height) =\u003e {\n 0xD000\n | ((*x_register as u16) \u003c\u003c 8)\n | ((*y_register as u16) \u003c\u003c 4)\n | (*height as u16)\n }\n Chip8CpuInstructions::SKP(x_register) =\u003e 0xE09E | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::SKNP(x_register) =\u003e 0xE0A1 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LDRD(x_register) =\u003e 0xF007 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LDRK(x_register) =\u003e 0xF00A | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LDD(x_register) =\u003e 0xF015 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LDIS(x_register) =\u003e 0xF018 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::ADDI(x_register) =\u003e 0xF01E | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LDFX(x_register) =\u003e 0xF029 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::BCD(x_register) =\u003e 0xF033 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LDIX(x_register) =\u003e 0xF055 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LDRI(x_register) =\u003e 0xF065 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::SCD(x_register) =\u003e 0x00C0 | (*x_register as u16),\n Chip8CpuInstructions::SCR =\u003e 0x00FB,\n Chip8CpuInstructions::SCL =\u003e 0x00FC,\n Chip8CpuInstructions::LOW =\u003e 0x00FE,\n Chip8CpuInstructions::HIGH =\u003e 0x00FF,\n Chip8CpuInstructions::EXIT =\u003e 0x00FD,\n Chip8CpuInstructions::LDF2(x_register) =\u003e 0xF030 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::STR(x_register) =\u003e 0xF075 | ((*x_register as u16) \u003c\u003c 8),\n Chip8CpuInstructions::LIDR(x_register) =\u003e 0xF085 | ((*x_register as u16) \u003c\u003c 8),\n SCU(x_register) =\u003e 0x00D0 | (*x_register as u16),\n DW(address) =\u003e *address as u16,\n XXXXERRORINSTRUCTION =\u003e XXXXERRORINSTRUCTION_ENCODED,\n }\n }\n pub fn decode(input: u16, quirk_mode: \u0026QuirkMode) -\u003e Chip8CpuInstructions {\n let x_param = InstructionUtil::read_x_from_instruction(input);\n let y_param = InstructionUtil::read_y_from_instruction(input);\n let addr_param = InstructionUtil::read_addr_from_instruction(input);\n let byte_param = InstructionUtil::read_byte_from_instruction(input);\n let nibble_param = InstructionUtil::read_nibble_from_instruction(input);\n let ubln = InstructionUtil::read_upper_byte_lower_nibble(input);\n let last_byte = (input \u0026 0xFF) as u8;\n let last_nibble = (input \u0026 0xF) as u8;\n\n match input {\n 0x00C0..=0x00CF =\u003e match quirk_mode {\n QuirkMode::Chip8 =\u003e XXXXERRORINSTRUCTION,\n QuirkMode::XOChip =\u003e SCD(last_nibble),\n QuirkMode::SChipModern =\u003e SCD(last_nibble),\n },\n 0x00E0 =\u003e Chip8CpuInstructions::CLS,\n 0x00EE =\u003e Chip8CpuInstructions::RET,\n 0x00FB =\u003e {\n match quirk_mode {\n QuirkMode::Chip8 =\u003e {\n // does not exist on Chip8\n XXXXERRORINSTRUCTION\n }\n QuirkMode::XOChip =\u003e Chip8CpuInstructions::SCR,\n QuirkMode::SChipModern =\u003e Chip8CpuInstructions::SCR,\n }\n }\n 0x00FC =\u003e Chip8CpuInstructions::SCL,\n 0x00FD =\u003e Chip8CpuInstructions::EXIT,\n 0x00FE =\u003e match quirk_mode {\n QuirkMode::Chip8 =\u003e XXXXERRORINSTRUCTION,\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e LOW,\n },\n 0x00FF =\u003e match quirk_mode {\n QuirkMode::Chip8 =\u003e XXXXERRORINSTRUCTION,\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e HIGH,\n },\n 0x0000..=0x0FFF =\u003e match quirk_mode {\n QuirkMode::Chip8 =\u003e Chip8CpuInstructions::SYS(addr_param),\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e XXXXERRORINSTRUCTION,\n },\n 0x1000..=0x1FFF =\u003e JPA(addr_param),\n 0x2000..=0x2FFF =\u003e CALL(addr_param),\n 0x3000..=0x3FFF =\u003e SEX(x_param, byte_param),\n 0x4000..=0x4FFF =\u003e SNEB(x_param, byte_param),\n 0x5000..=0x5FF0 if input \u0026 0x01 == 0 =\u003e SEY(x_param, y_param),\n 0x6000..=0x6FFF =\u003e LDR(x_param, byte_param),\n 0x7000..=0x7FFF =\u003e ADD(x_param, byte_param),\n 0x8000..=0x8FFE =\u003e match last_nibble {\n 0x0 =\u003e Chip8CpuInstructions::LDRY(x_param, y_param),\n 0x1 =\u003e Chip8CpuInstructions::OR(x_param, y_param),\n 0x2 =\u003e Chip8CpuInstructions::AND(x_param, y_param),\n 0x3 =\u003e Chip8CpuInstructions::ORY(x_param, y_param),\n 0x4 =\u003e Chip8CpuInstructions::ADDR(x_param, y_param),\n 0x5 =\u003e Chip8CpuInstructions::SUB(x_param, y_param),\n 0x6 =\u003e Chip8CpuInstructions::SHR(x_param, y_param),\n 0x7 =\u003e Chip8CpuInstructions::SUBC(x_param, y_param),\n 0xE =\u003e Chip8CpuInstructions::SHL(x_param, y_param),\n _ =\u003e XXXXERRORINSTRUCTION,\n },\n 0x9000..=0x9FF0 if input \u0026 0x01 == 0 =\u003e Chip8CpuInstructions::SNEY(x_param, y_param),\n 0xA000..=0xAFFF =\u003e Chip8CpuInstructions::LDIA(addr_param),\n 0xB000..=0xBFFF =\u003e Chip8CpuInstructions::JPI(addr_param),\n 0xC000..=0xCFFF =\u003e Chip8CpuInstructions::RND(x_param, byte_param),\n 0xD000..=0xDFFF =\u003e Chip8CpuInstructions::DRW(x_param, y_param, nibble_param),\n 0xE09E..=0xEFA1 =\u003e match last_byte {\n 0x9E =\u003e Chip8CpuInstructions::SKP(ubln),\n 0xA1 =\u003e Chip8CpuInstructions::SKNP(ubln),\n _ =\u003e XXXXERRORINSTRUCTION,\n },\n 0xF007..=0xFF65 =\u003e match last_byte {\n 0x07 =\u003e Chip8CpuInstructions::LDRD(ubln),\n 0x0A =\u003e Chip8CpuInstructions::LDRK(ubln),\n 0x15 =\u003e Chip8CpuInstructions::LDD(ubln),\n 0x18 =\u003e Chip8CpuInstructions::LDIS(ubln),\n 0x1E =\u003e Chip8CpuInstructions::ADDI(ubln),\n 0x29 =\u003e Chip8CpuInstructions::LDFX(ubln),\n 0x30 =\u003e Chip8CpuInstructions::LDF2(ubln),\n 0x33 =\u003e Chip8CpuInstructions::BCD(ubln),\n 0x55 =\u003e Chip8CpuInstructions::LDIX(ubln),\n 0x65 =\u003e Chip8CpuInstructions::LDRI(ubln),\n 0x75 =\u003e Chip8CpuInstructions::STR(ubln),\n 0x85 =\u003e Chip8CpuInstructions::LIDR(ubln),\n _ =\u003e XXXXERRORINSTRUCTION,\n },\n _ =\u003e DW(addr_param),\n }\n }\n pub fn execute(\u0026self, input: \u0026mut Chip8Computer) -\u003e Chip8Computer {\n // print!(\"INSTRUCTION {}\", self);\n // let start_time = Instant::now();\n let start_pc = input.registers.peek_pc();\n input.registers.poke_pc(start_pc + 2);\n match self {\n // 0x0nnn Exit to System Call\n Chip8CpuInstructions::SYS(new_address) =\u003e {\n input.registers.poke_pc(*new_address);\n }\n // * 0x00E0 Clear Screen\n Chip8CpuInstructions::CLS =\u003e {\n input.video_memory.cls();\n }\n // 0x00EE Return from Subroutine\n Chip8CpuInstructions::RET =\u003e {\n input.registers.poke_pc(input.stack.pop());\n }\n // 0x1nnn Jump to Address\n Chip8CpuInstructions::JPA(new_address) =\u003e {\n input.registers.poke_pc(*new_address);\n }\n // 0x2nnn Call Subroutine\n Chip8CpuInstructions::CALL(new_address) =\u003e {\n let return_address = input.registers.peek_pc();\n input.registers.poke_pc(*new_address);\n input.stack.push(\u0026return_address);\n }\n // 0x3xkk Skip next instruction if Vx = kk.\n Chip8CpuInstructions::SEX(vx_register, byte) =\u003e {\n if input.registers.peek(*vx_register) == { *byte } {\n input.registers.advance_pc();\n }\n }\n // 0x4xkk Skip next instruction if Vx != kk\n Chip8CpuInstructions::SNEB(x, byte) =\u003e {\n // 4xkk - SNE Vx, byte\n // Skip next instruction if Vx != kk.\n //\n // The interpreter compares register Vx to kk, and if they are not equal,\n // increments the program counter by 2.\n if input.registers.peek(*x) != *byte {\n input.registers.advance_pc();\n }\n }\n // 0x5xy0 Skip next instruction if Vx == Vy\n Chip8CpuInstructions::SEY(x, y) =\u003e {\n if input.registers.peek(*x) == input.registers.peek(*y) {\n input.registers.advance_pc();\n }\n }\n // 0x6xkk Set Vx = kk\n Chip8CpuInstructions::LDR(register, byte) =\u003e {\n input.registers.poke(*register, *byte);\n }\n // 0x7xkk Set Vx = Vx + kk\n Chip8CpuInstructions::ADD(vx_register, byte) =\u003e {\n input.registers.poke(\n *vx_register,\n (input.registers.peek(*vx_register) as u16 + *byte as u16) as u8,\n );\n }\n // 0x8xy0 Set value of Vy in Vx\n Chip8CpuInstructions::LDRY(x, y) =\u003e {\n input.registers.poke(*x, input.registers.peek(*y));\n }\n // 0x8xy1 Set Vx = Vx OR Vy\n Chip8CpuInstructions::OR(x, y) =\u003e {\n // shift them to 16 bit\n let working_16_x = input.registers.peek(*x) as u16;\n let working_16_y = input.registers.peek(*y) as u16;\n // OR them\n let working_16_or = working_16_x | working_16_y;\n // shift them back to 8 bit.\n input.registers.poke(*x, working_16_or as u8);\n // reset of VF quirk\n input.registers.poke(0x0f, 0x00);\n debug!(\"OrVxVy [0x{x:1x}] [0x{y:1x}]\")\n }\n // 0x8xy2 Set Vx = Vx AND Vy\n AND(x, y) =\u003e {\n let lhs_16 = input.registers.peek(*x) as u16;\n let rhs_16 = input.registers.peek(*y) as u16;\n // reset of VF quirk\n input.registers.poke(0x0f, 0x00);\n input.registers.poke(*x, (lhs_16 \u0026 rhs_16) as u8);\n }\n // 0x8xy3 Set Vx = Vx XOR Vy\n ORY(x, y) =\u003e {\n let lhs_16 = input.registers.peek(*x) as u16;\n let rhs_16 = input.registers.peek(*y) as u16;\n // reset of VF quirk\n input.registers.poke(0x0f, 0x00);\n input.registers.poke(*x, (lhs_16 ^ rhs_16) as u8);\n }\n // 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)\n ADDR(x, y) =\u003e {\n let lhs = input.registers.peek(*x) as i16;\n let rhs = input.registers.peek(*y) as i16;\n let working = lhs + rhs;\n input.registers.poke(*x, working as u8);\n\n if working \u003e= 0x100 {\n input.registers.poke(0xf, 0x01);\n } else {\n input.registers.poke(0x0f, 0x00);\n }\n }\n SUB(x, y) =\u003e {\n // 8xy5 - SUB Vx, Vy\n // Set Vx = Vx - Vy, set VF = NOT borrow.\n //\n // If Vx \u003e Vy, then VF is set to 1, otherwise 0. Then Vy is subtracted from Vx, and the results stored in Vx.\n\n let lhs = input.registers.peek(*x);\n let rhs = input.registers.peek(*y);\n\n let result;\n\n let borrow_flag: u8 = if rhs \u003e lhs {\n result = (lhs as u16 + 0x100) - rhs as u16;\n 0\n } else {\n result = lhs as u16 - rhs as u16;\n 1\n };\n\n input.registers.poke(*x, result as u8);\n input.registers.poke(0x0f, borrow_flag);\n }\n Chip8CpuInstructions::SHR(x, _) =\u003e {\n // 8xy6 - SHR Vx {, Vy}\n // Set Vx = Vx SHR 1.\n // SHIFT 1 Bit ----\u003e\n // If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0. Then Vx is divided by 2.\n let initial_value = input.registers.peek(*x);\n\n // overflow check\n let rotated = initial_value \u003e\u003e 1;\n input.registers.poke(*x, rotated);\n if initial_value.bitand(0b1) == 1 {\n input.registers.poke(0x0f, 0x01);\n } else {\n input.registers.poke(0x0f, 0x00);\n }\n }\n Chip8CpuInstructions::SUBC(x, y) =\u003e {\n // 8xy7 - SUBN Vx, Vy\n // Set Vx = Vy - Vx, set VF = NOT borrow.\n //\n // If Vy \u003e Vx, then VF is set to 1, otherwise 0. Then Vx is subtracted from Vy, and the results stored in Vx.\n let y_register = input.registers.peek(*y);\n let x_register = input.registers.peek(*x);\n\n let (new_value, value_to_poke) = if y_register \u003c x_register {\n (0, (y_register as u16 + 256) - x_register as u16)\n } else {\n (1, (y_register - x_register) as u16)\n };\n input.registers.poke(*x, value_to_poke as u8);\n input.registers.poke(0xf, new_value);\n }\n\n Chip8CpuInstructions::SHL(x, _) =\u003e {\n // 8xyE - SHL Vx {, Vy}\n // Set Vx = Vx SHL 1.\n //\n // If the most-significant bit of Vx is 1, then VF is set to 1, otherwise to 0. Then Vx is multiplied by 2.\n let initial_value = input.registers.peek(*x);\n\n // overflow check\n let rotated = initial_value \u003c\u003c 1;\n input.registers.poke(*x, rotated);\n if initial_value.bitand(0b10000000) == 0b10000000 {\n input.registers.poke(0x0f, 0x01);\n } else {\n input.registers.poke(0x0f, 0x00);\n }\n }\n Chip8CpuInstructions::SNEY(vx_register, vy_register) =\u003e {\n // 9xy0 - SNE Vx, Vy\n // Skip next instruction if Vx != Vy.\n //\n // The values of Vx and Vy are compared, and if they are not equal, the program counter is increased by 2.\n\n let x_reg_value = input.registers.peek(*vx_register);\n let y_reg_value = input.registers.peek(*vy_register);\n if x_reg_value != y_reg_value {\n input.registers.advance_pc();\n }\n }\n Chip8CpuInstructions::LDIA(new_index) =\u003e {\n // Annn - LD I, addr\n // Set I = nnn.\n //\n // The value of register I is set to nnn.\n // debug!(\"LdiAddr [0x{new_index:3x}]\");\n input.registers.poke_i(*new_index);\n }\n // 0xBnnn Jump to nnn+V0\n Chip8CpuInstructions::JPI(addr) =\u003e {\n // Bnnn - JP V0, addr\n // Jump to location nnn + V0.\n //\n // The program counter is set to nnn plus the value of V0.\n input\n .registers\n .poke_pc(input.registers.peek(0) as u16 + addr);\n }\n // 0xBxnn Jump to Xnn+Vx\n JPX(vx_register, addr) =\u003e {\n let x_reg_value: u16 = input.registers.peek(*vx_register) as u16;\n let shifted_x_reg = x_reg_value \u003c\u003c 8;\n let added_addr = shifted_x_reg | addr;\n let final_addr = added_addr + shifted_x_reg;\n println!(\"JPX -\u003e {x_reg_value:02x} {shifted_x_reg:04x} {added_addr:04x} {final_addr:04x}\");\n input.registers.poke_i(final_addr);\n }\n Chip8CpuInstructions::RND(x, byte) =\u003e {\n // Cxkk - RND Vx, byte\n // Set Vx = random byte AND kk.\n //\n // The interpreter generates a random number from 0 to 255,\n // which is then ANDed with the value kk.\n // The results are stored in Vx.\n let mut rng = rand::rng();\n let new_value: u8 = rng.random();\n let and_value: u8 = *byte;\n let result = new_value \u0026 and_value;\n debug!(\"RANDOM: [{new_value:02x}] AND: [{and_value:02x} Result: [{result:02x}]\");\n input.registers.poke(*x, new_value \u0026 { *byte })\n }\n Chip8CpuInstructions::DRW(y, x, n) =\u003e {\n // Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision.\n\n // The interpreter reads n bytes from memory, starting at the address stored in I.\n\n // These bytes are then displayed as sprites on screen at coordinates (Vx, Vy).\n // Sprites are XORed onto the existing screen.\n // If this causes any pixels to be erased, VF is set to 1,\n // otherwise it is set to 0.\n // If the sprite is positioned so part of it is outside the coordinates of the display,\n // it wraps around to the opposite side of the screen.\n\n let source_memory_offset = input.registers.peek_i();\n let x_offset = input.registers.peek(*x) as u16;\n let y_offset = input.registers.peek(*y) as u16;\n if input.video_memory.is_highres() {\n // if n == 0 we have a 16 row sprite (font maybe)\n let actual_num_loops = if *n == 0u8 { 16 } else { *n };\n for byte_index in 0..actual_num_loops {\n let current_byte =\n input.memory.peek(byte_index as u16 + source_memory_offset);\n let next_byte = input\n .memory\n .peek(byte_index as u16 + 1u16 + source_memory_offset);\n let x_offset = (x_offset + byte_index as u16) * 64;\n for bit_index in 0..8 {\n input.video_memory.poke(\n x_offset + (y_offset + bit_index as u16),\n (current_byte \u0026 (0x80 \u003e\u003e bit_index)) != 0,\n );\n input.video_memory.poke(\n x_offset + (y_offset + bit_index as u16) + 8,\n (next_byte \u0026 (0x80 \u003e\u003e bit_index)) != 0,\n );\n }\n }\n } else {\n for byte_index in 0..*n {\n let current_byte =\n input.memory.peek(byte_index as u16 + source_memory_offset);\n let x_offset: u16 = (x_offset + byte_index as u16) * 64;\n for bit_index in 0..8 {\n input.video_memory.poke(\n x_offset + (y_offset + bit_index as u16),\n (current_byte \u0026 (0x80 \u003e\u003e bit_index)) != 0,\n );\n }\n }\n }\n\n let target = if input.video_memory.has_frame_changed {\n 1u8\n } else {\n 0u8\n };\n\n input.registers.poke(0xf, target);\n }\n Chip8CpuInstructions::SKP(x) =\u003e {\n // Ex9E - SKP Vx\n // Skip next instruction if key with the value of Vx is pressed.\n //\n // Checks the keyboard, and if the key corresponding to the value of Vx is currently in the down position, PC is increased by 2.\n let key_to_check = input.registers.peek(*x);\n let is_pressed = input.keypad.pressed(key_to_check);\n if is_pressed {\n input.registers.advance_pc();\n }\n }\n Chip8CpuInstructions::SKNP(x) =\u003e {\n // ExA1 - SKNP Vx\n // Skip next instruction if key with the value of Vx is not pressed.\n //\n\n // Checks the keyboard,\n // and if the key corresponding to the value of Vx is currently in the up position,\n // PC is increased by 2.\n let target_key = input.registers.peek(*x);\n let is_pressed = input.keypad.pressed(target_key);\n debug!(\"SnKpVx [{x:1x}]\");\n if !is_pressed {\n input.registers.advance_pc();\n }\n }\n Chip8CpuInstructions::LDRD(x) =\u003e {\n // Fx07 - LD Vx, DT\n // Set Vx = delay timer value.\n //\n // The value of DT is placed into Vx.\n let value_to_set = input.delay_timer.current();\n input.registers.poke(*x, value_to_set);\n }\n Chip8CpuInstructions::LDRK(_) =\u003e {\n // Fx0A - LD Vx, K\n // Wait for a key press, store the value of the key in Vx.\n //\n // All execution stops until a key is pressed, then the value of that key is stored in Vx.\n input.state = WaitingForKey;\n }\n Chip8CpuInstructions::LDD(source_register) =\u003e {\n // Fx15 - LD DT, Vx\n // Set delay timer = Vx.\n //\n // DT is set equal to the value of Vx.\n let new_time = input.registers.peek(*source_register);\n input.delay_timer.set_timer(new_time);\n }\n Chip8CpuInstructions::LDIS(new_time) =\u003e {\n let new_value = input.registers.peek(*new_time);\n input.sound_timer.set_timer(new_value as i32);\n }\n Chip8CpuInstructions::ADDI(x) =\u003e {\n // Fx1E - ADD I, Vx\n // Set I = I + Vx.\n //\n // The values of I and Vx are added, and the results are stored in I.\n let base = input.registers.peek_i();\n let x_value = input.registers.peek(*x);\n input.registers.poke_i(base + x_value as u16);\n }\n Chip8CpuInstructions::LDFX(x) =\u003e {\n // Fx29 - LD F, Vx\n // Set I = location of sprite for digit Vx.\n //\n // The value of I is set to the location for the hexadecimal sprite corresponding\n // to the value of Vx. See section 2.4, Display, for more information on\n // the Chip-8 hexadecimal font.\n\n let x_value: u8 = input.registers.peek(*x);\n\n let real_offset = x_value as u16 * 5;\n input.registers.poke_i(real_offset);\n }\n Chip8CpuInstructions::BCD(x) =\u003e {\n // Fx33 - LD B, Vx\n // Store BCD representation of Vx in memory locations I, I+1, and I+2.\n //\n // The interpreter takes the decimal value of Vx, and places the hundreds\n // digit in memory at location in I, the tens digit at location I+1,\n // and the ones digit at location I+2.\n\n let to_convert = input.registers.peek(*x);\n\n // how many hundreds\n let hundreds = to_convert / 100;\n // how many tens, minus the hundreds\n let tens = (to_convert / 10) - (hundreds * 10);\n // whats the leftover when dividing by 10\n let units = to_convert % 10;\n\n // write them to the memory pointed to by I, I+1, and I+2\n let target_start_offset = input.registers.peek_i();\n input.memory.poke(target_start_offset, hundreds);\n input.memory.poke(target_start_offset + 1, tens);\n input.memory.poke(target_start_offset + 2, units);\n }\n Chip8CpuInstructions::LDIX(x) =\u003e {\n // Store registers V0 through Vx in memory starting at location I.\n //\n // The interpreter copi=es the values of registers V0 through Vx into memory,\n // starting at the address in I.\n let offset = input.registers.peek_i();\n for i in 0..=*x {\n input\n .memory\n .poke(offset + i as u16, input.registers.peek(i));\n }\n input.registers.poke_i(offset + 1);\n }\n Chip8CpuInstructions::LDRI(x) =\u003e {\n // Read registers V0 through Vx from memory starting at location I.\n //\n // The interpreter reads values from memory starting at location I into registers V0 through Vx.\n let offset = input.registers.peek_i();\n debug!(\"STARTING TO READ AT {offset:03x}\");\n let num_loops = x + 1;\n debug!(\"WILL READ {num_loops:x} BYTES\");\n for index in 0..num_loops {\n let src_value = input.memory.peek(index as u16 + offset);\n input.registers.poke(index, src_value);\n debug!(\"POKING Register 0x{index:02x} with 0x{src_value:04x} using offset 0x{offset:04x}\");\n }\n input.registers.poke_i(offset + 1);\n }\n Chip8CpuInstructions::XXXXERRORINSTRUCTION =\u003e {}\n // SCHIP1.1\n Chip8CpuInstructions::SCD(x) =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"Attempt to execute SCD in Chip8 Mode\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n input.video_memory.scroll_down(*x as i32);\n }\n },\n // SCHIP 1.1\n Chip8CpuInstructions::SCR =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"Attempt to execute SCR in Chip8 Mode\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n input.video_memory.scroll_right();\n }\n },\n // SCHIP 1.1\n Chip8CpuInstructions::SCL =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"Attempt to execute SCL in Chip8 Mode\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n input.video_memory.scroll_left();\n }\n },\n // SCHIP 1.0\n Chip8CpuInstructions::LOW =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"ATTEMPT TO SET LOWRES IN CHIP8MODE\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n input.video_memory.set_lowres();\n }\n },\n // SCHIP 1.0\n Chip8CpuInstructions::HIGH =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"ATTEMPT TO SET HIGHRES IN CHIP8MODE\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n input.video_memory.set_highres();\n }\n },\n Chip8CpuInstructions::EXIT =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"ATTEMPT TO EXIT FROM CHIP8 INTERPRETER\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n println!(\"EXIT INTERPRETER\");\n }\n },\n Chip8CpuInstructions::LDF2(x) =\u003e {\n match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"ATTEMPT TO LDF2 IN CHIP8MODE\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n println!(\"POINTING TO FONT AT {x:02x}\");\n // base = 0x100 + 0x0A*X\n input.registers.poke_i(0x100 + (0xA * x) as u16);\n }\n }\n }\n Chip8CpuInstructions::STR(x) =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"ATTEMPT TO STORE RPL IN CHIP8MODE\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n println!(\"STORING FROM RPL FOR {x}\");\n }\n },\n Chip8CpuInstructions::LIDR(x) =\u003e match input.quirk_mode {\n QuirkMode::Chip8 =\u003e {\n debug!(\"ATTEMPT TO LOAD RPL IN CHIP8MODE\");\n }\n QuirkMode::XOChip | QuirkMode::SChipModern =\u003e {\n println!(\"LOADING FROM RPL FOR {x}\");\n }\n },\n SCU(x) =\u003e {\n println!(\"SCROLL SCREEN UP {x} ROWS\");\n match input.quirk_mode {\n QuirkMode::Chip8 | QuirkMode::SChipModern =\u003e {\n debug!(\"Attempt to run SCU outside XO mode\");\n }\n QuirkMode::XOChip =\u003e {\n input.video_memory.scroll_up(x);\n }\n }\n }\n DW(_) =\u003e {\n println!(\"DATA WORD FOUND...\");\n }\n };\n // let cycle_time = Instant::now().duration_since(start_time).as_nanos();\n // println!(\"\\t\\tTook {cycle_time}ms\");\n input.to_owned()\n }\n\n pub fn from_string(input: \u0026str) -\u003e Chip8CpuInstructions {\n let parts = input.split(\" \");\n println!(\"Parts -\u003e {:?}\", parts);\n\n Chip8CpuInstructions::XXXXERRORINSTRUCTION\n }\n}\n","traces":[{"line":280,"address":[796368],"length":1,"stats":{"Line":1}},{"line":281,"address":[826405],"length":1,"stats":{"Line":1}},{"line":282,"address":[921680],"length":1,"stats":{"Line":1}},{"line":283,"address":[921134],"length":1,"stats":{"Line":1}},{"line":284,"address":[810688],"length":1,"stats":{"Line":1}},{"line":285,"address":[1718524],"length":1,"stats":{"Line":1}},{"line":286,"address":[4043038],"length":1,"stats":{"Line":1}},{"line":287,"address":[826540],"length":1,"stats":{"Line":1}},{"line":288,"address":[827034],"length":1,"stats":{"Line":1}},{"line":289,"address":[2495518],"length":1,"stats":{"Line":1}},{"line":290,"address":[1338546],"length":1,"stats":{"Line":1}},{"line":291,"address":[2494998],"length":1,"stats":{"Line":1}},{"line":292,"address":[2495284],"length":1,"stats":{"Line":1}},{"line":293,"address":[4043740],"length":1,"stats":{"Line":1}},{"line":294,"address":[827242],"length":1,"stats":{"Line":1}},{"line":295,"address":[1530008],"length":1,"stats":{"Line":1}},{"line":296,"address":[921420],"length":1,"stats":{"Line":1}},{"line":297,"address":[1529774],"length":1,"stats":{"Line":1}},{"line":298,"address":[827574],"length":1,"stats":{"Line":1}},{"line":299,"address":[1718966],"length":1,"stats":{"Line":1}},{"line":300,"address":[818996],"length":1,"stats":{"Line":1}},{"line":301,"address":[921576],"length":1,"stats":{"Line":1}},{"line":302,"address":[1339352],"length":1,"stats":{"Line":1}},{"line":303,"address":[41433666],"length":1,"stats":{"Line":1}},{"line":304,"address":[921160],"length":1,"stats":{"Line":1}},{"line":305,"address":[41433250],"length":1,"stats":{"Line":1}},{"line":306,"address":[796456],"length":1,"stats":{"Line":1}},{"line":307,"address":[1778944],"length":1,"stats":{"Line":1}},{"line":308,"address":[921836],"length":1,"stats":{"Line":1}},{"line":309,"address":[827424],"length":1,"stats":{"Line":1}},{"line":310,"address":[508182],"length":1,"stats":{"Line":1}},{"line":311,"address":[796534],"length":1,"stats":{"Line":1}},{"line":312,"address":[1529098],"length":1,"stats":{"Line":1}},{"line":313,"address":[1529384],"length":1,"stats":{"Line":1}},{"line":314,"address":[1718628],"length":1,"stats":{"Line":1}},{"line":315,"address":[41433588],"length":1,"stats":{"Line":1}},{"line":316,"address":[41433120],"length":1,"stats":{"Line":1}},{"line":317,"address":[796898],"length":1,"stats":{"Line":1}},{"line":318,"address":[2495102],"length":1,"stats":{"Line":1}},{"line":319,"address":[1779487],"length":1,"stats":{"Line":1}},{"line":320,"address":[4043402],"length":1,"stats":{"Line":1}},{"line":321,"address":[507662],"length":1,"stats":{"Line":1}},{"line":322,"address":[4043012],"length":1,"stats":{"Line":1}},{"line":323,"address":[921914],"length":1,"stats":{"Line":1}},{"line":324,"address":[2495492],"length":1,"stats":{"Line":1}},{"line":325,"address":[1718550],"length":1,"stats":{"Line":1}},{"line":326,"address":[508381],"length":1,"stats":{"Line":1}},{"line":327,"address":[1529826],"length":1,"stats":{"Line":0}},{"line":328,"address":[2495636],"length":1,"stats":{"Line":1}},{"line":329,"address":[1339675],"length":1,"stats":{"Line":0}},{"line":333,"address":[811568],"length":1,"stats":{"Line":2}},{"line":334,"address":[828298,828261,829390,828372,827891,828039,827854,827965,829588,830184,829412,829302,827795,827758,829368,827710,828224,828113,828335,829280,829324,828416,828187,828394,829500,829456,829478,828002,828453,829544,829434,827817,829346,828150,829522,827928,828076,829566],"length":1,"stats":{"Line":44}},{"line":335,"address":[1781534],"length":1,"stats":{"Line":1}},{"line":336,"address":[1781561],"length":1,"stats":{"Line":1}},{"line":337,"address":[41436174,41436690],"length":1,"stats":{"Line":2}},{"line":339,"address":[828382],"length":1,"stats":{"Line":2}},{"line":345,"address":[822558,822885],"length":1,"stats":{"Line":4}},{"line":347,"address":[1780362],"length":1,"stats":{"Line":1}},{"line":352,"address":[1782474,1783014],"length":1,"stats":{"Line":2}},{"line":355,"address":[1719788],"length":1,"stats":{"Line":1}},{"line":366,"address":[1783019],"length":1,"stats":{"Line":1}},{"line":369,"address":[1780399],"length":1,"stats":{"Line":1}},{"line":370,"address":[2497274,2496521],"length":1,"stats":{"Line":2}},{"line":373,"address":[924018],"length":1,"stats":{"Line":1}},{"line":389,"address":[801604,801931],"length":1,"stats":{"Line":2}},{"line":391,"address":[508552],"length":1,"stats":{"Line":1}},{"line":404,"address":[4048544,4048963],"length":1,"stats":{"Line":1}},{"line":405,"address":[41438535],"length":1,"stats":{"Line":1}},{"line":406,"address":[824439,824383],"length":1,"stats":{"Line":2}},{"line":407,"address":[1784076],"length":1,"stats":{"Line":1}},{"line":412,"address":[816304,821528],"length":1,"stats":{"Line":1}},{"line":413,"address":[832445],"length":1,"stats":{"Line":1}},{"line":415,"address":[1724275],"length":1,"stats":{"Line":1}},{"line":420,"address":[816461,816702,816541],"length":1,"stats":{"Line":3}},{"line":430,"address":[1535740,1535528],"length":1,"stats":{"Line":2}},{"line":440,"address":[2501510,2501722],"length":1,"stats":{"Line":2}},{"line":449,"address":[1785846],"length":1,"stats":{"Line":1}},{"line":453,"address":[41440671],"length":1,"stats":{"Line":1}},{"line":454,"address":[804154,804220],"length":1,"stats":{"Line":2}},{"line":455,"address":[804252,804197,804291],"length":1,"stats":{"Line":3}},{"line":456,"address":[4050948,4050876,4050987],"length":1,"stats":{"Line":3}},{"line":457,"address":[1536908,1536947,1536868],"length":1,"stats":{"Line":3}},{"line":458,"address":[1726348,1726220,1726309],"length":1,"stats":{"Line":3}},{"line":459,"address":[1726381,1726420,1726325],"length":1,"stats":{"Line":3}},{"line":460,"address":[2502732,2502693,2502637],"length":1,"stats":{"Line":3}},{"line":461,"address":[1537213,1537252,1537173],"length":1,"stats":{"Line":3}},{"line":462,"address":[1786741,1786780,1786685],"length":1,"stats":{"Line":3}},{"line":463,"address":[1537301,1537396,1537357],"length":1,"stats":{"Line":3}},{"line":464,"address":[827356,827245,827317],"length":1,"stats":{"Line":3}},{"line":465,"address":[929556,929445,929517],"length":1,"stats":{"Line":3}},{"line":466,"address":[41441597,41441652,41441691],"length":1,"stats":{"Line":3}},{"line":467,"address":[1787076,1787170,1787131],"length":1,"stats":{"Line":3}},{"line":468,"address":[1727066,1727027,1726987],"length":1,"stats":{"Line":3}},{"line":469,"address":[1787203,1787243,1787282],"length":1,"stats":{"Line":3}},{"line":470,"address":[2503379,2503339,2503418],"length":1,"stats":{"Line":3}},{"line":471,"address":[4052034,4051995,4051955],"length":1,"stats":{"Line":3}},{"line":472,"address":[1727283,1727322,1727211],"length":1,"stats":{"Line":3}},{"line":473,"address":[1347555,1347627,1347666],"length":1,"stats":{"Line":3}},{"line":474,"address":[4052187,4052298,4052259],"length":1,"stats":{"Line":3}},{"line":475,"address":[1538251,1538179,1538290],"length":1,"stats":{"Line":3}},{"line":476,"address":[930362,930251,930323],"length":1,"stats":{"Line":3}},{"line":477,"address":[1727651,1727723,1727762],"length":1,"stats":{"Line":3}},{"line":478,"address":[4052539,4052650,4052611],"length":1,"stats":{"Line":3}},{"line":479,"address":[516786,516714,516825],"length":1,"stats":{"Line":3}},{"line":480,"address":[4052715,4052787,4052826],"length":1,"stats":{"Line":3}},{"line":481,"address":[806195,806306,806267],"length":1,"stats":{"Line":3}},{"line":482,"address":[1348347,1348419,1348458],"length":1,"stats":{"Line":3}},{"line":483,"address":[836403,836514,836475],"length":1,"stats":{"Line":3}},{"line":484,"address":[1788522,1788427,1788483],"length":1,"stats":{"Line":3}},{"line":485,"address":[828987,829026,828915],"length":1,"stats":{"Line":3}},{"line":486,"address":[1539225,1539186,1539131],"length":1,"stats":{"Line":3}},{"line":487,"address":[4053353,4053298,4053392],"length":1,"stats":{"Line":3}},{"line":488,"address":[1728663,1728624,1728569],"length":1,"stats":{"Line":3}},{"line":489,"address":[41443486,41443392,41443447],"length":1,"stats":{"Line":3}},{"line":490,"address":[41443518,41443463,41443557],"length":1,"stats":{"Line":3}},{"line":491,"address":[2505116,2505077,2505022],"length":1,"stats":{"Line":3}},{"line":492,"address":[1728947,1728908,1728853],"length":1,"stats":{"Line":3}},{"line":493,"address":[41443731,41443770,41443676],"length":1,"stats":{"Line":3}},{"line":494,"address":[837274,837219,837313],"length":1,"stats":{"Line":3}},{"line":495,"address":[1539864,1539825,1539770],"length":1,"stats":{"Line":3}},{"line":496,"address":[821304,821343,821249],"length":1,"stats":{"Line":3}},{"line":497,"address":[1539967,1539912,1540006],"length":1,"stats":{"Line":3}},{"line":498,"address":[829939,829923,829855],"length":1,"stats":{"Line":2}},{"line":499,"address":[1349609],"length":1,"stats":{"Line":1}},{"line":503,"address":[830016],"length":1,"stats":{"Line":1}},{"line":504,"address":[2505696],"length":1,"stats":{"Line":1}},{"line":505,"address":[4054291],"length":1,"stats":{"Line":1}},{"line":506,"address":[1349783],"length":1,"stats":{"Line":1}},{"line":507,"address":[837763],"length":1,"stats":{"Line":1}},{"line":508,"address":[807747],"length":1,"stats":{"Line":1}},{"line":509,"address":[41444351],"length":1,"stats":{"Line":1}},{"line":510,"address":[518539],"length":1,"stats":{"Line":1}},{"line":511,"address":[41444419],"length":1,"stats":{"Line":1}},{"line":513,"address":[1349956],"length":1,"stats":{"Line":1}},{"line":514,"address":[4054524],"length":1,"stats":{"Line":1}},{"line":516,"address":[2505997],"length":1,"stats":{"Line":1}},{"line":517,"address":[4054581],"length":1,"stats":{"Line":1}},{"line":519,"address":[1350074],"length":1,"stats":{"Line":1}},{"line":520,"address":[808034],"length":1,"stats":{"Line":1}},{"line":522,"address":[2506115],"length":1,"stats":{"Line":1}},{"line":523,"address":[41444654],"length":1,"stats":{"Line":1}},{"line":525,"address":[2506175],"length":1,"stats":{"Line":1}},{"line":526,"address":[808157],"length":1,"stats":{"Line":1}},{"line":528,"address":[1730002],"length":1,"stats":{"Line":1}},{"line":529,"address":[838256],"length":1,"stats":{"Line":1}},{"line":531,"address":[4054869],"length":1,"stats":{"Line":1}},{"line":532,"address":[1540803],"length":1,"stats":{"Line":1}},{"line":534,"address":[2506376],"length":1,"stats":{"Line":1}},{"line":535,"address":[932854],"length":1,"stats":{"Line":1}},{"line":537,"address":[519099],"length":1,"stats":{"Line":1}},{"line":538,"address":[830809],"length":1,"stats":{"Line":1}},{"line":540,"address":[808462],"length":1,"stats":{"Line":1}},{"line":541,"address":[822412],"length":1,"stats":{"Line":1}},{"line":543,"address":[830913],"length":1,"stats":{"Line":1}},{"line":544,"address":[1730367],"length":1,"stats":{"Line":1}},{"line":546,"address":[830980],"length":1,"stats":{"Line":1}},{"line":547,"address":[1541138],"length":1,"stats":{"Line":1}},{"line":549,"address":[2506711],"length":1,"stats":{"Line":1}},{"line":550,"address":[4055301],"length":1,"stats":{"Line":1}},{"line":552,"address":[1730538],"length":1,"stats":{"Line":1}},{"line":553,"address":[1730568],"length":1,"stats":{"Line":1}},{"line":555,"address":[4055405],"length":1,"stats":{"Line":1}},{"line":556,"address":[519544],"length":1,"stats":{"Line":1}},{"line":557,"address":[1351779],"length":1,"stats":{"Line":1}},{"line":558,"address":[808883],"length":1,"stats":{"Line":1}},{"line":559,"address":[822833],"length":1,"stats":{"Line":1}},{"line":561,"address":[1351010],"length":1,"stats":{"Line":1}},{"line":562,"address":[1790986,1790967,1790979],"length":1,"stats":{"Line":3}},{"line":563,"address":[41445551],"length":1,"stats":{"Line":1}},{"line":564,"address":[1730811],"length":1,"stats":{"Line":1}},{"line":565,"address":[1790982],"length":1,"stats":{"Line":1}},{"line":567,"address":[519739],"length":1,"stats":{"Line":1}},{"line":568,"address":[839108],"length":1,"stats":{"Line":1}},{"line":569,"address":[809117],"length":1,"stats":{"Line":1}},{"line":570,"address":[519862],"length":1,"stats":{"Line":1}},{"line":571,"address":[1541711],"length":1,"stats":{"Line":1}},{"line":572,"address":[41445800],"length":1,"stats":{"Line":1}},{"line":573,"address":[1731089],"length":1,"stats":{"Line":1}},{"line":574,"address":[520026],"length":1,"stats":{"Line":1}},{"line":575,"address":[1731171],"length":1,"stats":{"Line":1}},{"line":576,"address":[1541916],"length":1,"stats":{"Line":1}},{"line":577,"address":[823365],"length":1,"stats":{"Line":1}},{"line":578,"address":[933994],"length":1,"stats":{"Line":1}},{"line":579,"address":[4056139],"length":1,"stats":{"Line":1}},{"line":580,"address":[520247],"length":1,"stats":{"Line":1}},{"line":581,"address":[1791523],"length":1,"stats":{"Line":1}},{"line":582,"address":[809567],"length":1,"stats":{"Line":1}},{"line":583,"address":[1542091],"length":1,"stats":{"Line":1}},{"line":584,"address":[823515],"length":1,"stats":{"Line":1}},{"line":585,"address":[1542148],"length":1,"stats":{"Line":1}},{"line":586,"address":[934173],"length":1,"stats":{"Line":1}},{"line":587,"address":[2507820],"length":1,"stats":{"Line":1}},{"line":588,"address":[1542318],"length":1,"stats":{"Line":0}},{"line":589,"address":[1351546],"length":1,"stats":{"Line":0}},{"line":592,"address":[1351920],"length":1,"stats":{"Line":3}},{"line":593,"address":[1542395],"length":1,"stats":{"Line":5}},{"line":594,"address":[809907],"length":1,"stats":{"Line":3}},{"line":595,"address":[2507979],"length":1,"stats":{"Line":5}},{"line":596,"address":[1791926],"length":1,"stats":{"Line":3}},{"line":597,"address":[823902],"length":1,"stats":{"Line":5}},{"line":598,"address":[934502],"length":1,"stats":{"Line":3}},{"line":599,"address":[2508076],"length":1,"stats":{"Line":5}},{"line":600,"address":[1731851],"length":1,"stats":{"Line":3}},{"line":602,"address":[823992],"length":1,"stats":{"Line":5}},{"line":603,"address":[840152,840090],"length":1,"stats":{"Line":8}},{"line":604,"address":[1544450],"length":1,"stats":{"Line":1}},{"line":605,"address":[936448],"length":1,"stats":{"Line":1}},{"line":606,"address":[1793938],"length":1,"stats":{"Line":1}},{"line":608,"address":[520891],"length":1,"stats":{"Line":2}},{"line":609,"address":[520898],"length":1,"stats":{"Line":2}},{"line":611,"address":[1792180],"length":1,"stats":{"Line":1}},{"line":614,"address":[520999],"length":1,"stats":{"Line":1}},{"line":616,"address":[840340],"length":1,"stats":{"Line":1}},{"line":617,"address":[824235],"length":1,"stats":{"Line":1}},{"line":620,"address":[840274],"length":1,"stats":{"Line":1}},{"line":621,"address":[1542761],"length":1,"stats":{"Line":1}},{"line":622,"address":[520959],"length":1,"stats":{"Line":1}},{"line":623,"address":[41446882],"length":1,"stats":{"Line":1}},{"line":624,"address":[840361],"length":1,"stats":{"Line":1}},{"line":626,"address":[832662],"length":1,"stats":{"Line":1}},{"line":627,"address":[832720],"length":1,"stats":{"Line":1}},{"line":628,"address":[824263],"length":1,"stats":{"Line":1}},{"line":630,"address":[934867,934673],"length":1,"stats":{"Line":8}},{"line":631,"address":[522611],"length":1,"stats":{"Line":2}},{"line":632,"address":[41448488],"length":1,"stats":{"Line":1}},{"line":634,"address":[1352419,1352486],"length":1,"stats":{"Line":8}},{"line":635,"address":[1792374,1792437],"length":1,"stats":{"Line":8}},{"line":636,"address":[1732261,1732324],"length":1,"stats":{"Line":8}},{"line":637,"address":[1352633,1352564],"length":1,"stats":{"Line":8}},{"line":638,"address":[2508601,2509938,2508670],"length":1,"stats":{"Line":10}},{"line":639,"address":[1352670,1352737],"length":1,"stats":{"Line":8}},{"line":640,"address":[1792694,1792625],"length":1,"stats":{"Line":6}},{"line":641,"address":[41447339,41447270],"length":1,"stats":{"Line":6}},{"line":642,"address":[936152],"length":1,"stats":{"Line":2}},{"line":643,"address":[1353746],"length":1,"stats":{"Line":2}},{"line":644,"address":[936204],"length":1,"stats":{"Line":2}},{"line":645,"address":[41448294],"length":1,"stats":{"Line":2}},{"line":646,"address":[841792],"length":1,"stats":{"Line":3}},{"line":647,"address":[825706],"length":1,"stats":{"Line":3}},{"line":648,"address":[834196],"length":1,"stats":{"Line":2}},{"line":649,"address":[1793806],"length":1,"stats":{"Line":2}},{"line":650,"address":[841896],"length":1,"stats":{"Line":2}},{"line":651,"address":[811638],"length":1,"stats":{"Line":0}},{"line":653,"address":[1732571,1733428,1732653],"length":1,"stats":{"Line":9}},{"line":654,"address":[824749,824816],"length":1,"stats":{"Line":8}},{"line":655,"address":[4057488,4057551],"length":1,"stats":{"Line":8}},{"line":656,"address":[521625,521688],"length":1,"stats":{"Line":8}},{"line":657,"address":[2509091,2509022],"length":1,"stats":{"Line":8}},{"line":658,"address":[1543616,1543539],"length":1,"stats":{"Line":6}},{"line":659,"address":[2509628],"length":1,"stats":{"Line":1}},{"line":660,"address":[1793566],"length":1,"stats":{"Line":1}},{"line":661,"address":[41448126],"length":1,"stats":{"Line":0}},{"line":663,"address":[811166,811088],"length":1,"stats":{"Line":6}},{"line":664,"address":[522052],"length":1,"stats":{"Line":1}},{"line":665,"address":[41447932],"length":1,"stats":{"Line":2}},{"line":666,"address":[1793358],"length":1,"stats":{"Line":1}},{"line":667,"address":[2509456],"length":1,"stats":{"Line":1}},{"line":668,"address":[1353490],"length":1,"stats":{"Line":2}},{"line":669,"address":[1793412],"length":1,"stats":{"Line":1}},{"line":670,"address":[935958],"length":1,"stats":{"Line":1}},{"line":671,"address":[1733288],"length":1,"stats":{"Line":2}},{"line":672,"address":[811498],"length":1,"stats":{"Line":3}},{"line":673,"address":[522214],"length":1,"stats":{"Line":2}},{"line":674,"address":[522232],"length":1,"stats":{"Line":1}},{"line":675,"address":[833936],"length":1,"stats":{"Line":1}},{"line":676,"address":[825260],"length":1,"stats":{"Line":1}},{"line":678,"address":[1732954],"length":1,"stats":{"Line":0}},{"line":681,"address":[2518138,2510032],"length":1,"stats":{"Line":2}},{"line":684,"address":[1733873],"length":1,"stats":{"Line":2}},{"line":685,"address":[522892,522794],"length":1,"stats":{"Line":5}},{"line":686,"address":[812158],"length":1,"stats":{"Line":2}},{"line":688,"address":[842259],"length":1,"stats":{"Line":1}},{"line":689,"address":[2510290],"length":1,"stats":{"Line":1}},{"line":693,"address":[4058877],"length":1,"stats":{"Line":2}},{"line":697,"address":[4058900],"length":1,"stats":{"Line":2}},{"line":700,"address":[1354419],"length":1,"stats":{"Line":3}},{"line":701,"address":[1544882],"length":1,"stats":{"Line":3}},{"line":704,"address":[812405],"length":1,"stats":{"Line":2}},{"line":705,"address":[842449],"length":1,"stats":{"Line":2}},{"line":706,"address":[812454],"length":1,"stats":{"Line":2}},{"line":707,"address":[523168],"length":1,"stats":{"Line":2}},{"line":710,"address":[812515],"length":1,"stats":{"Line":2}},{"line":711,"address":[1545057],"length":1,"stats":{"Line":2}},{"line":712,"address":[831087],"length":1,"stats":{"Line":2}},{"line":716,"address":[834981],"length":1,"stats":{"Line":2}},{"line":722,"address":[523317],"length":1,"stats":{"Line":2}},{"line":723,"address":[2515238],"length":1,"stats":{"Line":2}},{"line":727,"address":[937175],"length":1,"stats":{"Line":2}},{"line":728,"address":[4059317],"length":1,"stats":{"Line":2}},{"line":729,"address":[817213],"length":1,"stats":{"Line":1}},{"line":733,"address":[826720],"length":1,"stats":{"Line":4}},{"line":734,"address":[842862],"length":1,"stats":{"Line":4}},{"line":737,"address":[937367],"length":1,"stats":{"Line":2}},{"line":738,"address":[41449461,41453811],"length":1,"stats":{"Line":4}},{"line":739,"address":[1354980],"length":1,"stats":{"Line":2}},{"line":740,"address":[523612,527879],"length":1,"stats":{"Line":2}},{"line":744,"address":[835382],"length":1,"stats":{"Line":2}},{"line":745,"address":[835412],"length":1,"stats":{"Line":2}},{"line":748,"address":[835499],"length":1,"stats":{"Line":2}},{"line":750,"address":[523825],"length":1,"stats":{"Line":2}},{"line":751,"address":[1734998],"length":1,"stats":{"Line":2}},{"line":753,"address":[4059848],"length":1,"stats":{"Line":2}},{"line":755,"address":[2511299],"length":1,"stats":{"Line":2}},{"line":757,"address":[2511333],"length":1,"stats":{"Line":2}},{"line":758,"address":[41453872,41449862],"length":1,"stats":{"Line":4}},{"line":761,"address":[827284],"length":1,"stats":{"Line":2}},{"line":762,"address":[1795362],"length":1,"stats":{"Line":2}},{"line":763,"address":[843477],"length":1,"stats":{"Line":2}},{"line":765,"address":[1545998],"length":1,"stats":{"Line":2}},{"line":766,"address":[1735343],"length":1,"stats":{"Line":2}},{"line":769,"address":[835962],"length":1,"stats":{"Line":2}},{"line":770,"address":[938104],"length":1,"stats":{"Line":2}},{"line":771,"address":[938155],"length":1,"stats":{"Line":2}},{"line":773,"address":[4060308],"length":1,"stats":{"Line":3}},{"line":774,"address":[2511797],"length":1,"stats":{"Line":3}},{"line":777,"address":[827712],"length":1,"stats":{"Line":3}},{"line":778,"address":[938326],"length":1,"stats":{"Line":3}},{"line":779,"address":[1735689],"length":1,"stats":{"Line":3}},{"line":780,"address":[818002,817960,813916],"length":1,"stats":{"Line":6}},{"line":781,"address":[528576],"length":1,"stats":{"Line":3}},{"line":783,"address":[1550506],"length":1,"stats":{"Line":3}},{"line":784,"address":[848088],"length":1,"stats":{"Line":2}},{"line":786,"address":[1360090],"length":1,"stats":{"Line":2}},{"line":789,"address":[813959],"length":1,"stats":{"Line":3}},{"line":795,"address":[4060605],"length":1,"stats":{"Line":3}},{"line":796,"address":[524711],"length":1,"stats":{"Line":3}},{"line":800,"address":[41454741,41454916,41450638],"length":1,"stats":{"Line":6}},{"line":801,"address":[832212,832049,832281],"length":1,"stats":{"Line":2}},{"line":802,"address":[1740156],"length":1,"stats":{"Line":1}},{"line":804,"address":[1739973,1739903,1739991],"length":1,"stats":{"Line":4}},{"line":805,"address":[1800141],"length":1,"stats":{"Line":2}},{"line":808,"address":[41454775],"length":1,"stats":{"Line":3}},{"line":809,"address":[832169],"length":1,"stats":{"Line":3}},{"line":811,"address":[844139],"length":1,"stats":{"Line":2}},{"line":816,"address":[938618],"length":1,"stats":{"Line":2}},{"line":819,"address":[1796134],"length":1,"stats":{"Line":3}},{"line":820,"address":[4060783],"length":1,"stats":{"Line":3}},{"line":821,"address":[814199],"length":1,"stats":{"Line":3}},{"line":822,"address":[528988],"length":1,"stats":{"Line":2}},{"line":824,"address":[832338],"length":1,"stats":{"Line":2}},{"line":827,"address":[814241],"length":1,"stats":{"Line":2}},{"line":832,"address":[2512327],"length":1,"stats":{"Line":2}},{"line":833,"address":[836709],"length":1,"stats":{"Line":2}},{"line":835,"address":[1800709,1800503,1800537,1796330],"length":1,"stats":{"Line":8}},{"line":836,"address":[943173,942974,943242],"length":1,"stats":{"Line":4}},{"line":838,"address":[840838,840900,840921],"length":1,"stats":{"Line":4}},{"line":840,"address":[840990],"length":1,"stats":{"Line":2}},{"line":841,"address":[41455199],"length":1,"stats":{"Line":2}},{"line":844,"address":[41450951],"length":1,"stats":{"Line":2}},{"line":849,"address":[844438],"length":1,"stats":{"Line":2}},{"line":852,"address":[41451010],"length":1,"stats":{"Line":2}},{"line":853,"address":[1736267],"length":1,"stats":{"Line":2}},{"line":854,"address":[41451043],"length":1,"stats":{"Line":2}},{"line":855,"address":[529369],"length":1,"stats":{"Line":3}},{"line":857,"address":[832723],"length":1,"stats":{"Line":2}},{"line":860,"address":[844557],"length":1,"stats":{"Line":2}},{"line":866,"address":[525216],"length":1,"stats":{"Line":2}},{"line":867,"address":[1547113],"length":1,"stats":{"Line":2}},{"line":868,"address":[2512679],"length":1,"stats":{"Line":2}},{"line":869,"address":[841217],"length":1,"stats":{"Line":1}},{"line":872,"address":[1736468],"length":1,"stats":{"Line":4}},{"line":878,"address":[837059],"length":1,"stats":{"Line":2}},{"line":881,"address":[828630],"length":1,"stats":{"Line":1}},{"line":886,"address":[1736538,1736599],"length":1,"stats":{"Line":2}},{"line":888,"address":[1796713],"length":1,"stats":{"Line":1}},{"line":891,"address":[846819],"length":1,"stats":{"Line":1}},{"line":892,"address":[1358889],"length":1,"stats":{"Line":1}},{"line":893,"address":[41453418],"length":1,"stats":{"Line":1}},{"line":894,"address":[1798846],"length":1,"stats":{"Line":1}},{"line":895,"address":[846938,857849,858812],"length":1,"stats":{"Line":2}},{"line":896,"address":[841745],"length":1,"stats":{"Line":1}},{"line":897,"address":[41465317],"length":1,"stats":{"Line":1}},{"line":899,"address":[1547325],"length":1,"stats":{"Line":2}},{"line":906,"address":[844887],"length":1,"stats":{"Line":2}},{"line":907,"address":[939368,943403],"length":1,"stats":{"Line":4}},{"line":908,"address":[818914],"length":1,"stats":{"Line":2}},{"line":909,"address":[41455483],"length":1,"stats":{"Line":2}},{"line":910,"address":[841495,841330],"length":1,"stats":{"Line":4}},{"line":911,"address":[841412],"length":1,"stats":{"Line":2}},{"line":913,"address":[525569],"length":1,"stats":{"Line":4}},{"line":925,"address":[1547479],"length":1,"stats":{"Line":2}},{"line":926,"address":[1547524],"length":1,"stats":{"Line":4}},{"line":927,"address":[4061671],"length":1,"stats":{"Line":2}},{"line":928,"address":[2513152],"length":1,"stats":{"Line":4}},{"line":930,"address":[2518203,2518804],"length":1,"stats":{"Line":0}},{"line":931,"address":[2518831],"length":1,"stats":{"Line":0}},{"line":932,"address":[41457538,41457585,41457451],"length":1,"stats":{"Line":0}},{"line":934,"address":[843484],"length":1,"stats":{"Line":0}},{"line":936,"address":[843388,843438,843538],"length":1,"stats":{"Line":0}},{"line":937,"address":[41457735,41457694,41457814],"length":1,"stats":{"Line":0}},{"line":938,"address":[851257,851302],"length":1,"stats":{"Line":0}},{"line":939,"address":[1743283,1743157],"length":1,"stats":{"Line":0}},{"line":940,"address":[945854,945923],"length":1,"stats":{"Line":0}},{"line":941,"address":[2519468,2519587,2519504],"length":1,"stats":{"Line":0}},{"line":943,"address":[1554025,1554208],"length":1,"stats":{"Line":0}},{"line":944,"address":[532078,532204,532121],"length":1,"stats":{"Line":0}},{"line":945,"address":[844103,844061,844025],"length":1,"stats":{"Line":0}},{"line":950,"address":[2518217,2518168,2518296],"length":1,"stats":{"Line":8}},{"line":951,"address":[1552781,1552881],"length":1,"stats":{"Line":4}},{"line":953,"address":[4067001,4067086,4066954],"length":1,"stats":{"Line":6}},{"line":954,"address":[820494,820449],"length":1,"stats":{"Line":6}},{"line":955,"address":[1802685,1802541],"length":1,"stats":{"Line":4}},{"line":956,"address":[4067271,4067193],"length":1,"stats":{"Line":4}},{"line":957,"address":[2518704,2518746,2518788],"length":1,"stats":{"Line":6}},{"line":963,"address":[4067527,4066833],"length":1,"stats":{"Line":4}},{"line":964,"address":[1802889],"length":1,"stats":{"Line":2}},{"line":966,"address":[531476],"length":1,"stats":{"Line":0}},{"line":969,"address":[1802905],"length":1,"stats":{"Line":4}},{"line":971,"address":[1736951],"length":1,"stats":{"Line":1}},{"line":976,"address":[845190],"length":1,"stats":{"Line":1}},{"line":977,"address":[1736995],"length":1,"stats":{"Line":1}},{"line":978,"address":[1357276],"length":1,"stats":{"Line":1}},{"line":979,"address":[1803711],"length":1,"stats":{"Line":1}},{"line":982,"address":[2513289],"length":1,"stats":{"Line":1}},{"line":990,"address":[815253],"length":1,"stats":{"Line":1}},{"line":991,"address":[837673],"length":1,"stats":{"Line":1}},{"line":992,"address":[1554318,1547830],"length":1,"stats":{"Line":2}},{"line":993,"address":[1743573],"length":1,"stats":{"Line":1}},{"line":994,"address":[822225],"length":1,"stats":{"Line":1}},{"line":997,"address":[4061988],"length":1,"stats":{"Line":1}},{"line":1002,"address":[829312],"length":1,"stats":{"Line":1}},{"line":1003,"address":[2513473],"length":1,"stats":{"Line":1}},{"line":1005,"address":[2513521],"length":1,"stats":{"Line":2}},{"line":1010,"address":[41452015],"length":1,"stats":{"Line":2}},{"line":1012,"address":[1357558],"length":1,"stats":{"Line":1}},{"line":1017,"address":[1797477],"length":1,"stats":{"Line":1}},{"line":1018,"address":[526179],"length":1,"stats":{"Line":1}},{"line":1020,"address":[837956],"length":1,"stats":{"Line":1}},{"line":1021,"address":[845619],"length":1,"stats":{"Line":1}},{"line":1022,"address":[2513664],"length":1,"stats":{"Line":1}},{"line":1024,"address":[1548162],"length":1,"stats":{"Line":3}},{"line":1029,"address":[1548174],"length":1,"stats":{"Line":3}},{"line":1030,"address":[1548219],"length":1,"stats":{"Line":3}},{"line":1031,"address":[2520304,2513794],"length":1,"stats":{"Line":6}},{"line":1033,"address":[838188],"length":1,"stats":{"Line":1}},{"line":1041,"address":[4062427],"length":1,"stats":{"Line":1}},{"line":1043,"address":[1548352,1554840,1554814],"length":1,"stats":{"Line":2}},{"line":1044,"address":[532857],"length":1,"stats":{"Line":1}},{"line":1046,"address":[2513938],"length":1,"stats":{"Line":2}},{"line":1054,"address":[838289],"length":1,"stats":{"Line":2}},{"line":1057,"address":[41452493],"length":1,"stats":{"Line":2}},{"line":1059,"address":[844901,844742,838348],"length":1,"stats":{"Line":4}},{"line":1061,"address":[844802],"length":1,"stats":{"Line":2}},{"line":1064,"address":[852473],"length":1,"stats":{"Line":2}},{"line":1065,"address":[41459042],"length":1,"stats":{"Line":2}},{"line":1066,"address":[1804553,1804518,1804467],"length":1,"stats":{"Line":4}},{"line":1067,"address":[1804586,1804535],"length":1,"stats":{"Line":4}},{"line":1069,"address":[1548543],"length":1,"stats":{"Line":3}},{"line":1074,"address":[1737850],"length":1,"stats":{"Line":3}},{"line":1075,"address":[1737893,1744558,1744459],"length":1,"stats":{"Line":9}},{"line":1076,"address":[1804834],"length":1,"stats":{"Line":3}},{"line":1078,"address":[533309,533380,533421],"length":1,"stats":{"Line":6}},{"line":1080,"address":[4069406,4069323],"length":1,"stats":{"Line":6}},{"line":1082,"address":[1738121],"length":1,"stats":{"Line":2}},{"line":1086,"address":[4062941],"length":1,"stats":{"Line":2}},{"line":1087,"address":[1738159,1744813],"length":1,"stats":{"Line":4}},{"line":1088,"address":[845291],"length":1,"stats":{"Line":2}},{"line":1089,"address":[1364995,1365547],"length":1,"stats":{"Line":4}},{"line":1090,"address":[846187,846096,845804],"length":1,"stats":{"Line":6}},{"line":1091,"address":[534527,534349,534435],"length":1,"stats":{"Line":4}},{"line":1092,"address":[4070536],"length":1,"stats":{"Line":2}},{"line":1093,"address":[1556554,1556468],"length":1,"stats":{"Line":4}},{"line":1095,"address":[41460421,41460328],"length":1,"stats":{"Line":4}},{"line":1099,"address":[816451],"length":1,"stats":{"Line":1}},{"line":1101,"address":[1367051,1366947],"length":1,"stats":{"Line":0}},{"line":1104,"address":[854973],"length":1,"stats":{"Line":1}},{"line":1108,"address":[816498],"length":1,"stats":{"Line":1}},{"line":1110,"address":[855282,855189],"length":1,"stats":{"Line":0}},{"line":1113,"address":[949703],"length":1,"stats":{"Line":1}},{"line":1117,"address":[816526],"length":1,"stats":{"Line":1}},{"line":1119,"address":[1747321,1747228],"length":1,"stats":{"Line":0}},{"line":1122,"address":[1807438],"length":1,"stats":{"Line":1}},{"line":1126,"address":[830474],"length":1,"stats":{"Line":1}},{"line":1128,"address":[1367747,1367840],"length":1,"stats":{"Line":0}},{"line":1131,"address":[950229],"length":1,"stats":{"Line":1}},{"line":1135,"address":[4063190],"length":1,"stats":{"Line":1}},{"line":1137,"address":[950535,950442],"length":1,"stats":{"Line":0}},{"line":1140,"address":[950492],"length":1,"stats":{"Line":1}},{"line":1143,"address":[2514658],"length":1,"stats":{"Line":0}},{"line":1145,"address":[950815,950705],"length":1,"stats":{"Line":0}},{"line":1148,"address":[950747],"length":1,"stats":{"Line":0}},{"line":1151,"address":[830566],"length":1,"stats":{"Line":0}},{"line":1152,"address":[839041],"length":1,"stats":{"Line":0}},{"line":1154,"address":[1368979,1368553],"length":1,"stats":{"Line":0}},{"line":1157,"address":[951031],"length":1,"stats":{"Line":0}},{"line":1159,"address":[537607,537338],"length":1,"stats":{"Line":0}},{"line":1163,"address":[1358757],"length":1,"stats":{"Line":0}},{"line":1165,"address":[849509,849681],"length":1,"stats":{"Line":0}},{"line":1168,"address":[41463727],"length":1,"stats":{"Line":0}},{"line":1171,"address":[816740],"length":1,"stats":{"Line":0}},{"line":1173,"address":[827639,827467],"length":1,"stats":{"Line":0}},{"line":1176,"address":[849893],"length":1,"stats":{"Line":0}},{"line":1179,"address":[1549466],"length":1,"stats":{"Line":0}},{"line":1180,"address":[527600],"length":1,"stats":{"Line":0}},{"line":1181,"address":[830991],"length":1,"stats":{"Line":0}},{"line":1183,"address":[4075404,4075505],"length":1,"stats":{"Line":0}},{"line":1186,"address":[1810814],"length":1,"stats":{"Line":0}},{"line":1191,"address":[41453679],"length":1,"stats":{"Line":0}},{"line":1196,"address":[830333],"length":1,"stats":{"Line":2}},{"line":1199,"address":[842992],"length":1,"stats":{"Line":0}},{"line":1200,"address":[829101],"length":1,"stats":{"Line":0}},{"line":1201,"address":[859157],"length":1,"stats":{"Line":0}},{"line":1203,"address":[2527267],"length":1,"stats":{"Line":0}}],"covered":453,"coverable":503},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","keypad.rs"],"content":"use serde::{Deserialize, Serialize};\n\nuse crate::constants::CHIP8_KEYBOARD;\n\n#[derive(Clone, Copy, Default, Serialize, Deserialize, Debug)]\npub struct Keypad {\n keys: [bool; 0x10],\n}\n\nimpl Keypad {\n pub fn format_as_string(\u0026self) -\u003e String {\n let mut return_value = String::new();\n // draw a 4x4 grid showing the keys with * filling the cells that are depressed\n for row in CHIP8_KEYBOARD.iter() {\n for (index, key) in row.iter().enumerate() {\n let is_lit = if self.keys[*key as usize] {\n \"*\".to_string()\n } else {\n char::from_digit(*key as u32, 16).unwrap_or(' ').to_string()\n };\n\n if index == 3 {\n return_value += format!(\"|{}|\\n\", is_lit).as_str();\n } else {\n return_value += format!(\"|{}\", is_lit).as_str();\n }\n }\n }\n\n return_value\n }\n}\n\nimpl Keypad {\n pub fn push_key(\u0026mut self, key_index: u8) {\n self.keys[key_index as usize] = true;\n }\n\n pub fn release_key(\u0026mut self, key_index: u8) {\n self.keys[key_index as usize] = false;\n }\n\n pub fn key_state(\u0026self, key_index: u8) -\u003e bool {\n self.keys[key_index as usize]\n }\n\n pub fn new() -\u003e Keypad {\n Keypad::default()\n }\n\n pub fn pressed(\u0026self, key: u8) -\u003e bool {\n self.key_state(key)\n }\n pub fn released(\u0026self, key: u8) -\u003e bool {\n !self.key_state(key)\n }\n}\n","traces":[{"line":11,"address":[489181,488935,487824],"length":1,"stats":{"Line":1}},{"line":12,"address":[1570015],"length":1,"stats":{"Line":1}},{"line":14,"address":[871667,871396,871495],"length":1,"stats":{"Line":3}},{"line":15,"address":[1383747],"length":1,"stats":{"Line":1}},{"line":16,"address":[1570567],"length":1,"stats":{"Line":1}},{"line":17,"address":[872140,872032],"length":1,"stats":{"Line":2}},{"line":19,"address":[1763789,1763843],"length":1,"stats":{"Line":2}},{"line":22,"address":[4088740],"length":1,"stats":{"Line":1}},{"line":23,"address":[872243,872372,872142],"length":1,"stats":{"Line":3}},{"line":25,"address":[4089245,4089116,4088777],"length":1,"stats":{"Line":3}},{"line":30,"address":[1383688],"length":1,"stats":{"Line":1}},{"line":35,"address":[840624],"length":1,"stats":{"Line":2}},{"line":36,"address":[872827,872789],"length":1,"stats":{"Line":2}},{"line":39,"address":[41479376],"length":1,"stats":{"Line":1}},{"line":40,"address":[1571493,1571531],"length":1,"stats":{"Line":1}},{"line":43,"address":[41479456],"length":1,"stats":{"Line":1}},{"line":44,"address":[865308,865269],"length":1,"stats":{"Line":1}},{"line":47,"address":[856880],"length":1,"stats":{"Line":1}},{"line":48,"address":[856888],"length":1,"stats":{"Line":1}},{"line":51,"address":[967040],"length":1,"stats":{"Line":1}},{"line":52,"address":[840928],"length":1,"stats":{"Line":1}},{"line":54,"address":[1571712],"length":1,"stats":{"Line":1}},{"line":55,"address":[840960],"length":1,"stats":{"Line":1}}],"covered":23,"coverable":23},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","quirk_modes.rs"],"content":"use serde::{Deserialize, Serialize};\nuse std::fmt::Display;\nuse crate::constants::{LABEL_QUIRK_CHIP8, LABEL_QUIRK_SCHIP, LABEL_QUIRK_XOCHIP};\n\n#[derive(Default, Clone, Serialize, Deserialize, Copy, Debug, PartialEq)]\npub enum QuirkMode {\n #[default]\n Chip8,\n XOChip,\n SChipModern,\n}\n\nimpl Display for QuirkMode {\n fn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n let variant = match \u0026self {\n QuirkMode::Chip8 =\u003e LABEL_QUIRK_CHIP8.to_string(),\n QuirkMode::XOChip =\u003e LABEL_QUIRK_XOCHIP.to_string(),\n QuirkMode::SChipModern =\u003e LABEL_QUIRK_SCHIP.to_string(),\n };\n write!(f, \"{}\", variant)\n }\n}\n","traces":[{"line":14,"address":[41382112,41382396],"length":1,"stats":{"Line":1}},{"line":15,"address":[1033273],"length":1,"stats":{"Line":1}},{"line":16,"address":[1629742],"length":1,"stats":{"Line":1}},{"line":17,"address":[41382199],"length":1,"stats":{"Line":1}},{"line":18,"address":[4098992],"length":1,"stats":{"Line":1}},{"line":20,"address":[874727,874789],"length":1,"stats":{"Line":2}}],"covered":6,"coverable":6},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","registers.rs"],"content":"use serde::{Deserialize, Serialize};\n\n/// Registers. numbered 1-16 publicly.\n/// Privately using zero base array so -1 to shift from pub to priv.\n#[derive(Clone, Copy, Serialize, Deserialize, Debug)]\npub struct Chip8Registers {\n pub registers: [u8; 16],\n pub i_register: u16,\n pub pc: u16,\n}\n\nimpl PartialEq for Chip8Registers {\n fn eq(\u0026self, other: \u0026Self) -\u003e bool {\n self.pc == other.pc \u0026\u0026 self.registers == other.registers \u0026\u0026 self.i_register == other.i_register\n }\n}\n\nimpl Chip8Registers {\n pub fn advance_pc(\u0026mut self) {\n self.pc += 2;\n }\n}\n\nimpl Default for Chip8Registers {\n fn default() -\u003e Self {\n Chip8Registers {\n registers: [0x00; 16],\n i_register: 0x00,\n pc: 0x200,\n }\n }\n}\n\nimpl Chip8Registers {\n pub fn reset(\u0026mut self) {\n self.registers = [0x00; 16];\n self.i_register = 0x00;\n self.pc = 0x200;\n }\n\n pub fn poke_pc(\u0026mut self, new_pc: u16) {\n self.pc = new_pc\n }\n pub fn peek_i(\u0026self) -\u003e u16 {\n self.i_register\n }\n\n pub fn poke_i(\u0026mut self, new_value: u16) {\n self.i_register = new_value;\n }\n pub fn peek(\u0026self, register_number: u8) -\u003e u8 {\n self.registers[register_number as usize]\n }\n\n pub fn poke(\u0026mut self, register_number: u8, value: u8) {\n if register_number \u003e 0xf {\n panic!(\"INVALID REGISTER\");\n }\n self.registers[register_number as usize] = value;\n }\n\n pub fn peek_pc(\u0026self) -\u003e u16 {\n self.pc\n }\n\n pub fn set_pc(\u0026mut self, new_pc: u16) {\n self.pc = new_pc\n }\n\n pub fn format_as_string(\u0026self) -\u003e String {\n format!(\"Vx: 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x}\\n 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x}\\nI: 0x{:04x}\\tPC: 0x{:04x}\",\n self.registers[0x0],\n self.registers[0x1],\n self.registers[0x2],\n self.registers[0x3],\n self.registers[0x4],\n self.registers[0x5],\n self.registers[0x6],\n self.registers[0x7],\n self.registers[0x8],\n self.registers[0x9],\n self.registers[0xa],\n self.registers[0xb],\n self.registers[0xc],\n self.registers[0xd],\n self.registers[0xe],\n self.registers[0xf],\n self.i_register,\n self.pc\n )\n }\n}\n","traces":[{"line":13,"address":[1608016],"length":1,"stats":{"Line":1}},{"line":14,"address":[807431],"length":1,"stats":{"Line":1}},{"line":19,"address":[1608128],"length":1,"stats":{"Line":2}},{"line":20,"address":[1765069,1765107],"length":1,"stats":{"Line":2}},{"line":25,"address":[1608208],"length":1,"stats":{"Line":2}},{"line":27,"address":[500878],"length":1,"stats":{"Line":2}},{"line":35,"address":[807680],"length":1,"stats":{"Line":4}},{"line":36,"address":[799230],"length":1,"stats":{"Line":4}},{"line":37,"address":[1015375],"length":1,"stats":{"Line":4}},{"line":38,"address":[41420453],"length":1,"stats":{"Line":4}},{"line":41,"address":[2482208],"length":1,"stats":{"Line":5}},{"line":42,"address":[1015405],"length":1,"stats":{"Line":5}},{"line":44,"address":[1327440],"length":1,"stats":{"Line":4}},{"line":45,"address":[890165],"length":1,"stats":{"Line":2}},{"line":48,"address":[1015440],"length":1,"stats":{"Line":4}},{"line":49,"address":[41420525],"length":1,"stats":{"Line":2}},{"line":51,"address":[807808],"length":1,"stats":{"Line":3}},{"line":52,"address":[2482346,2482309],"length":1,"stats":{"Line":4}},{"line":55,"address":[1015552],"length":1,"stats":{"Line":6}},{"line":56,"address":[41420659],"length":1,"stats":{"Line":6}},{"line":57,"address":[2482427],"length":1,"stats":{"Line":1}},{"line":59,"address":[1765544,1765557,1765467],"length":1,"stats":{"Line":14}},{"line":62,"address":[4030832],"length":1,"stats":{"Line":4}},{"line":63,"address":[1706037],"length":1,"stats":{"Line":2}},{"line":66,"address":[1608672],"length":1,"stats":{"Line":4}},{"line":67,"address":[1706061],"length":1,"stats":{"Line":2}},{"line":70,"address":[814560],"length":1,"stats":{"Line":1}},{"line":71,"address":[1327806],"length":1,"stats":{"Line":1}}],"covered":28,"coverable":28},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","sound_timer.rs"],"content":"use log::trace;\nuse serde::{Deserialize, Serialize};\n\n#[derive(Clone, Copy, Serialize, Deserialize, Debug)]\npub struct SoundTimer {\n counter: i32,\n}\n\nimpl Default for SoundTimer {\n fn default() -\u003e Self {\n Self::new()\n }\n}\n\nimpl SoundTimer {\n pub fn current(\u0026self) -\u003e i32 {\n self.counter\n }\n pub fn new() -\u003e Self {\n SoundTimer { counter: 0 }\n }\n pub fn set_timer(\u0026mut self, new_value: i32) {\n trace!(\"SETTING SOUND TIMER TO {new_value}\");\n self.counter = new_value\n }\n\n pub fn tick(\u0026mut self) {\n trace!(\n \"TICKING SOUND FROM {} to {}\",\n self.counter,\n self.counter - 1\n );\n if self.counter \u003e 0 {\n self.counter -= 1;\n /*\n todo: this breaks tests. maybe its wrong?\n if let good_thread = thread::spawn(|| {\n beep(440).expect(\"Unable to beep\");\n thread::sleep(time::Duration::from_millis(500));\n }).join().unwrap().\n */\n }\n }\n\n pub fn reset(\u0026mut self) {\n self.counter = 0x00;\n }\n}\n","traces":[{"line":10,"address":[4035264],"length":1,"stats":{"Line":1}},{"line":11,"address":[894881],"length":1,"stats":{"Line":1}},{"line":16,"address":[804032],"length":1,"stats":{"Line":1}},{"line":17,"address":[450773],"length":1,"stats":{"Line":1}},{"line":22,"address":[1710512],"length":1,"stats":{"Line":1}},{"line":23,"address":[1406119,1406196],"length":1,"stats":{"Line":2}},{"line":24,"address":[804122],"length":1,"stats":{"Line":1}},{"line":27,"address":[1770352],"length":1,"stats":{"Line":2}},{"line":28,"address":[804450,804526,804372],"length":1,"stats":{"Line":6}},{"line":33,"address":[1710856,1711225],"length":1,"stats":{"Line":5}},{"line":34,"address":[4036001,4036027],"length":1,"stats":{"Line":1}},{"line":45,"address":[804800],"length":1,"stats":{"Line":4}},{"line":46,"address":[819733],"length":1,"stats":{"Line":4}}],"covered":13,"coverable":13},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","stack.rs"],"content":"use serde::{Deserialize, Serialize};\n\n#[derive(Clone, Default, Serialize, Deserialize, Debug)]\npub struct Chip8Stack {\n items: Vec\u003cu16\u003e,\n}\n\nimpl Chip8Stack {\n pub fn push(\u0026mut self, new_value: \u0026u16) {\n if self.depth() == 16 {\n // println!(\"Deep deep stack?\");\n panic!(\"Stack Overflow\");\n }\n self.items.push(*new_value);\n }\n pub fn pop(\u0026mut self) -\u003e u16 {\n if self.items.is_empty() {\n panic!(\"Stack Underflow\");\n }\n self.items.pop().unwrap()\n }\n pub fn depth(\u0026self) -\u003e u16 {\n self.items.len() as u16\n }\n\n pub fn new() -\u003e Self {\n Chip8Stack { items: vec![] }\n }\n\n pub fn reset(\u0026mut self) {\n self.items = vec![]\n }\n}\n","traces":[{"line":9,"address":[557440],"length":1,"stats":{"Line":4}},{"line":10,"address":[884712],"length":1,"stats":{"Line":4}},{"line":12,"address":[1587828],"length":1,"stats":{"Line":1}},{"line":14,"address":[2562523],"length":1,"stats":{"Line":4}},{"line":16,"address":[876208],"length":1,"stats":{"Line":2}},{"line":17,"address":[863886],"length":1,"stats":{"Line":2}},{"line":18,"address":[1742359],"length":1,"stats":{"Line":1}},{"line":20,"address":[876237],"length":1,"stats":{"Line":4}},{"line":22,"address":[894672],"length":1,"stats":{"Line":2}},{"line":23,"address":[1680053],"length":1,"stats":{"Line":2}},{"line":26,"address":[2562704],"length":1,"stats":{"Line":4}},{"line":27,"address":[1680077],"length":1,"stats":{"Line":4}},{"line":30,"address":[894815,894752],"length":1,"stats":{"Line":4}},{"line":31,"address":[885117,885037],"length":1,"stats":{"Line":8}}],"covered":14,"coverable":14},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","system_memory.rs"],"content":"use log::trace;\n\nuse crate::constants::*;\nuse serde::Deserialize;\nuse serde::Serialize;\n\nuse super::quirk_modes::QuirkMode;\n\n#[derive(Clone, Serialize, Deserialize, Debug)]\npub struct Chip8SystemMemory {\n memory: Vec\u003cu8\u003e,\n}\n\nimpl PartialEq for Chip8SystemMemory {\n fn eq(\u0026self, other: \u0026Self) -\u003e bool {\n self.memory == other.memory\n }\n}\n\nimpl Default for Chip8SystemMemory {\n fn default() -\u003e Self {\n let mut x = Chip8SystemMemory::new();\n\n x.load_fonts_to_memory();\n x.load_schip_fonts_to_memory();\n x\n }\n}\n\nimpl Chip8SystemMemory {\n fn empty_memory() -\u003e Vec\u003cu8\u003e {\n let mut working_memory = vec![];\n\n for _ in 0..CHIP8_MEMORY_SIZE {\n working_memory.push(0x00);\n }\n working_memory\n }\n\n pub fn reset(\u0026mut self, quirk_modes: QuirkMode) {\n self.memory = Chip8SystemMemory::empty_memory();\n match quirk_modes {\n QuirkMode::Chip8 =\u003e {\n self.load_fonts_to_memory();\n }\n QuirkMode::XOChip =\u003e {\n println!(\"NO XO FONT LOADING DONE YET\");\n }\n QuirkMode::SChipModern =\u003e {\n self.load_schip_fonts_to_memory();\n }\n }\n }\n\n pub fn new() -\u003e Self {\n Chip8SystemMemory {\n memory: Chip8SystemMemory::empty_memory(),\n }\n }\n\n pub fn peek(\u0026self, address: u16) -\u003e u8 {\n trace!(\n \"PEEK: {} / {}\",\n address,\n self.memory[address as usize].clone()\n );\n let effective = address as i32 % CHIP8_MEMORY_SIZE;\n self.memory[effective as usize]\n }\n\n pub fn poke(\u0026mut self, address: u16, value: u8) {\n trace!(\n \"POKE: {} / {} to {}\",\n address,\n self.memory[address as usize],\n value\n );\n self.memory[address as usize] = value;\n }\n\n pub fn load_program(\u0026mut self, program_to_load: Box\u003cVec\u003cu8\u003e\u003e) {\n for load_index in 0..program_to_load.len() {\n self.poke((load_index + 0x200) as u16, program_to_load[load_index]);\n }\n }\n\n pub fn load_fonts_to_memory(\u0026mut self) {\n let all_font_characters = [\n CHIP8FONT_0,\n CHIP8FONT_1,\n CHIP8FONT_2,\n CHIP8FONT_3,\n CHIP8FONT_4,\n CHIP8FONT_5,\n CHIP8FONT_6,\n CHIP8FONT_7,\n CHIP8FONT_8,\n CHIP8FONT_9,\n CHIP8FONT_A,\n CHIP8FONT_B,\n CHIP8FONT_C,\n CHIP8FONT_D,\n CHIP8FONT_E,\n CHIP8FONT_F,\n ];\n\n for (font_index, current_font) in all_font_characters.iter().enumerate() {\n for font_mem_offset in 0..=4 {\n let real_offset = font_index * 5 + font_mem_offset;\n self.poke(real_offset as u16, current_font[font_mem_offset]);\n }\n }\n }\n\n pub fn load_schip_fonts_to_memory(\u0026mut self) {\n let all_font_characters = [\n SCHIPFONT_0,\n SCHIPFONT_1,\n SCHIPFONT_2,\n SCHIPFONT_3,\n SCHIPFONT_4,\n SCHIPFONT_5,\n SCHIPFONT_6,\n SCHIPFONT_7,\n SCHIPFONT_8,\n SCHIPFONT_9,\n SCHIPFONT_A,\n SCHIPFONT_B,\n SCHIPFONT_C,\n SCHIPFONT_D,\n SCHIPFONT_E,\n SCHIPFONT_F,\n ];\n for (font_index, current_font) in all_font_characters.iter().enumerate() {\n let base_offset = SCHIPFONT_OFFSET;\n for font_mem_offset in 0..=9 {\n let real_offset = base_offset + (font_index * 0x0a) as u32 + font_mem_offset;\n self.poke(real_offset as u16, current_font[font_mem_offset as usize]);\n }\n }\n }\n}\n","traces":[{"line":15,"address":[2557008],"length":1,"stats":{"Line":1}},{"line":16,"address":[858478],"length":1,"stats":{"Line":1}},{"line":21,"address":[559520,559642],"length":1,"stats":{"Line":10}},{"line":22,"address":[1674606],"length":1,"stats":{"Line":6}},{"line":24,"address":[1736998],"length":1,"stats":{"Line":10}},{"line":25,"address":[1737041],"length":1,"stats":{"Line":6}},{"line":26,"address":[858603],"length":1,"stats":{"Line":6}},{"line":31,"address":[2557421,2557200],"length":1,"stats":{"Line":10}},{"line":32,"address":[1300814],"length":1,"stats":{"Line":6}},{"line":34,"address":[41389660,41389729],"length":1,"stats":{"Line":15}},{"line":35,"address":[4001282],"length":1,"stats":{"Line":10}},{"line":37,"address":[858819],"length":1,"stats":{"Line":6}},{"line":40,"address":[41389872,41389941],"length":1,"stats":{"Line":4}},{"line":41,"address":[1583028,1582954],"length":1,"stats":{"Line":8}},{"line":42,"address":[1583045],"length":1,"stats":{"Line":4}},{"line":44,"address":[1301194],"length":1,"stats":{"Line":3}},{"line":47,"address":[560049],"length":1,"stats":{"Line":1}},{"line":50,"address":[984358],"length":1,"stats":{"Line":1}},{"line":55,"address":[4001552],"length":1,"stats":{"Line":8}},{"line":57,"address":[1737597],"length":1,"stats":{"Line":6}},{"line":61,"address":[984448],"length":1,"stats":{"Line":2}},{"line":62,"address":[1737815],"length":1,"stats":{"Line":2}},{"line":67,"address":[1301731,1301387,1301772],"length":1,"stats":{"Line":6}},{"line":68,"address":[859602],"length":1,"stats":{"Line":4}},{"line":71,"address":[1301792],"length":1,"stats":{"Line":6}},{"line":72,"address":[2558372],"length":1,"stats":{"Line":7}},{"line":78,"address":[880516],"length":1,"stats":{"Line":11}},{"line":81,"address":[880896,881237],"length":1,"stats":{"Line":1}},{"line":82,"address":[1584335,1584150,1584217],"length":1,"stats":{"Line":3}},{"line":83,"address":[881121],"length":1,"stats":{"Line":1}},{"line":87,"address":[561424],"length":1,"stats":{"Line":6}},{"line":88,"address":[1584516],"length":1,"stats":{"Line":11}},{"line":107,"address":[860825,860995],"length":1,"stats":{"Line":16}},{"line":108,"address":[1739501],"length":1,"stats":{"Line":6}},{"line":109,"address":[873537,873627],"length":1,"stats":{"Line":11}},{"line":110,"address":[1739747,1739791],"length":1,"stats":{"Line":16}},{"line":115,"address":[4003808],"length":1,"stats":{"Line":11}},{"line":116,"address":[861407],"length":1,"stats":{"Line":11}},{"line":134,"address":[1740318,1740488],"length":1,"stats":{"Line":17}},{"line":135,"address":[4003828],"length":1,"stats":{"Line":6}},{"line":136,"address":[1586098],"length":1,"stats":{"Line":6}},{"line":137,"address":[41393183,41393310],"length":1,"stats":{"Line":11}},{"line":138,"address":[874655,874706],"length":1,"stats":{"Line":17}}],"covered":43,"coverable":43},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","util.rs"],"content":"pub struct InstructionUtil {}\n\nimpl InstructionUtil {\n /// Byte To Bools\n /// Convert a u8 byte to an array of bool\n pub fn byte_to_bools(to_convert: u8) -\u003e [bool; 8] {\n let mut return_values = [false; 8];\n for i in 0..8 {\n let new_value = to_convert \u003e\u003e i \u0026 0x1 == 1;\n return_values[i as usize] = new_value;\n }\n return_values\n }\n\n /// Byte To Bools\n /// Convert an array of bool to a u8\n pub fn bools_to_byte(to_convert: [bool; 8]) -\u003e u8 {\n let mut return_value = 0u8;\n for i in 0..to_convert.len() {\n let new_bit = 0x1 \u003c\u003c i;\n if to_convert[i] {\n return_value |= new_bit\n }\n }\n return_value\n }\n\n /// Split Bytes\n /// Split a u16 into a pair of u8 values for high and low byte\n pub fn split_bytes(to_split: u16) -\u003e (u8, u8) {\n let high = to_split.rotate_left(8) as u8;\n let low = (to_split \u0026 0xff) as u8;\n\n (high, low)\n }\n\n /// Join Bytes\n /// Joins 2 u8 into a single u16 with high and low\n /// bytes provided\n pub fn join_bytes(high: u8, low: u8) -\u003e u16 {\n (high as u16) \u003c\u003c 8 | low as u16\n }\n\n /// Swap Endian\n /// Swaps the endianess of a value\n /// 0xabcd -\u003e 0xcdab\n pub fn swap_endian(to_swap:u16) -\u003e u16 {\n let low = to_swap \u0026 0xff;\n let high = to_swap \u0026 0xff00;\n low \u003c\u003c 8 | high \u003e\u003e 8\n }\n\n /// Split Bytes Swap Endian\n /// Swaps endianness and return both bytes separately\n pub fn split_bytes_swap_endian(to_split: u16) -\u003e (u8, u8) {\n InstructionUtil::split_bytes(InstructionUtil::swap_endian(to_split))\n }\n\n /// Join Bytes Swap Endian\n /// Swaps endianness and returns the combined bytes\n pub fn join_bytes_swap_endian(high :u8, low: u8) -\u003e u16 {\n InstructionUtil::swap_endian(InstructionUtil::join_bytes(high, low))\n }\n\n /// Read Address from Instruction\n /// Reads the 'address' portion of an instruction\n /// Returns the 12 LSB\n /// 0xabcd -\u003e 0x0bcd\n pub fn read_addr_from_instruction(instruction_to_read_from: u16) -\u003e u16 {\n instruction_to_read_from \u0026 0x0FFF\n }\n\n /// Read Nibble from Instruction\n /// Returns the lowest 4 bits of the instruction\n /// 0xabcd -\u003e 0x0d\n pub fn read_nibble_from_instruction(instruction_to_read_from: u16) -\u003e u8 {\n ( instruction_to_read_from \u0026 0x000F )as u8\n }\n\n /// Read X From Instruction\n /// Returns the 4 LSB of the high byte of the instruction\n /// Returns bits 9-13\n /// 0xabcd -\u003e 0x0b\n pub fn read_x_from_instruction(instruction_to_read_from: u16) -\u003e u8 {\n (instruction_to_read_from \u0026 0x0F00).rotate_right(8) as u8\n }\n\n /// Read Y from Instruction\n /// Returns the 4 MSB from the low byte of the instruction\n /// Returns bits 5-8\n /// 0xabcd -\u003e 0x0c\n pub fn read_y_from_instruction(instruction_to_read_from: u16) -\u003e u8 {\n (instruction_to_read_from \u0026 0x00F0).rotate_right(4) as u8\n }\n\n /// Read Byte from Instruction\n /// Returns the 8 LSB from the instruction\n /// Returns bits 0-7\n /// 0xabcd -\u003e 0xcd\n pub fn read_byte_from_instruction(instruction_to_read_from: u16) -\u003e u8 {\n (instruction_to_read_from \u0026 0x00FF) as u8\n }\n\n /// Alias for read_x_from_instruction\n pub fn read_upper_byte_lower_nibble(to_read_from: u16) -\u003e u8 {\n InstructionUtil::read_x_from_instruction(to_read_from)\n }\n\n pub fn read_bits_from_instruction(instruction_bytes: u16, first_bit: u8, last_bit: u8) -\u003e u16 {\n // shift the value over by the 'first bit' bits\n let working = instruction_bytes.rotate_right(first_bit as u32);\n let mut working_mask: i32 = 0x00;\n // build the mask\n for _ in first_bit..=last_bit {\n working_mask = working_mask.rotate_left(1) | 0x01;\n }\n\n // apply the mask to the working value\n working \u0026 working_mask as u16\n }\n}\n","traces":[{"line":6,"address":[4092224],"length":1,"stats":{"Line":2}},{"line":7,"address":[877679],"length":1,"stats":{"Line":2}},{"line":8,"address":[1827584,1827755,1827654],"length":1,"stats":{"Line":6}},{"line":9,"address":[969378,969435],"length":1,"stats":{"Line":2}},{"line":10,"address":[1827748,1827708,1827765],"length":1,"stats":{"Line":4}},{"line":12,"address":[2545762],"length":1,"stats":{"Line":2}},{"line":17,"address":[1387920],"length":1,"stats":{"Line":2}},{"line":18,"address":[1387939],"length":1,"stats":{"Line":2}},{"line":19,"address":[2546007,2545944],"length":1,"stats":{"Line":4}},{"line":20,"address":[859854,859814],"length":1,"stats":{"Line":2}},{"line":21,"address":[859875,859846,859922],"length":1,"stats":{"Line":6}},{"line":22,"address":[2546122],"length":1,"stats":{"Line":2}},{"line":25,"address":[1387998],"length":1,"stats":{"Line":2}},{"line":30,"address":[868400],"length":1,"stats":{"Line":2}},{"line":31,"address":[1574741],"length":1,"stats":{"Line":2}},{"line":32,"address":[969769],"length":1,"stats":{"Line":3}},{"line":40,"address":[1767904],"length":1,"stats":{"Line":2}},{"line":41,"address":[1388206],"length":1,"stats":{"Line":2}},{"line":47,"address":[4092768],"length":1,"stats":{"Line":1}},{"line":48,"address":[860024],"length":1,"stats":{"Line":1}},{"line":49,"address":[449524],"length":1,"stats":{"Line":1}},{"line":50,"address":[2546254],"length":1,"stats":{"Line":1}},{"line":55,"address":[868528],"length":1,"stats":{"Line":1}},{"line":56,"address":[1767993],"length":1,"stats":{"Line":1}},{"line":61,"address":[969904],"length":1,"stats":{"Line":1}},{"line":62,"address":[1388335],"length":1,"stats":{"Line":1}},{"line":69,"address":[868624],"length":1,"stats":{"Line":4}},{"line":70,"address":[1828248],"length":1,"stats":{"Line":6}},{"line":76,"address":[1388384],"length":1,"stats":{"Line":4}},{"line":77,"address":[4092936],"length":1,"stats":{"Line":6}},{"line":84,"address":[41482864],"length":1,"stats":{"Line":4}},{"line":85,"address":[1388408],"length":1,"stats":{"Line":6}},{"line":92,"address":[1575008],"length":1,"stats":{"Line":6}},{"line":93,"address":[860248],"length":1,"stats":{"Line":4}},{"line":100,"address":[1046528],"length":1,"stats":{"Line":6}},{"line":101,"address":[41482968],"length":1,"stats":{"Line":4}},{"line":105,"address":[41482976],"length":1,"stats":{"Line":6}},{"line":106,"address":[1388521],"length":1,"stats":{"Line":4}},{"line":109,"address":[878528],"length":1,"stats":{"Line":1}},{"line":111,"address":[1046634],"length":1,"stats":{"Line":1}},{"line":112,"address":[1388607],"length":1,"stats":{"Line":1}},{"line":114,"address":[1575403,1575175],"length":1,"stats":{"Line":2}},{"line":115,"address":[1388836,1388806],"length":1,"stats":{"Line":2}},{"line":119,"address":[844250],"length":1,"stats":{"Line":1}}],"covered":44,"coverable":44},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8","video.rs"],"content":"use crate::chip8::video::Chip8VideoModes::{HighRes, LowRes};\nuse crate::constants::{\n CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_MEMORY, CHIP8_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT,\n SCHIP_VIDEO_WIDTH, SCHIP_VIDE_MEMORY,\n};\nuse log::debug;\nuse serde::{Deserialize, Serialize};\n\n#[derive(Clone, Copy, Serialize, Deserialize, Debug)]\npub enum Chip8VideoModes {\n LowRes,\n HighRes,\n}\n\n#[derive(Clone, Serialize, Deserialize, Debug)]\npub struct Chip8Video {\n memory: Vec\u003cbool\u003e,\n pub has_frame_changed: bool,\n current_res: Chip8VideoModes,\n}\n\nimpl Chip8Video {\n pub fn reset(\u0026mut self) {\n self.cls();\n self.set_lowres();\n self.start_frame();\n }\n\n pub fn is_highres(\u0026self) -\u003e bool {\n matches!(self.current_res, HighRes)\n }\n\n pub fn set_highres(\u0026mut self) {\n self.current_res = HighRes;\n self.cls();\n }\n\n pub fn set_lowres(\u0026mut self) {\n self.current_res = LowRes\n }\n\n pub fn get_screen_resolution(\u0026mut self) -\u003e Chip8VideoModes {\n self.current_res\n }\n\n pub fn cls(\u0026mut self) {\n self.memory.clear();\n let num_loops = match self.current_res {\n LowRes =\u003e CHIP8_VIDEO_MEMORY,\n HighRes =\u003e SCHIP_VIDE_MEMORY,\n };\n for _ in 0..num_loops {\n self.memory.push(false);\n }\n }\n\n pub fn start_frame(\u0026mut self) {\n self.has_frame_changed = false;\n }\n\n pub fn new(initial_configuration: Box\u003cVec\u003cbool\u003e\u003e) -\u003e Self {\n Self {\n memory: *initial_configuration,\n has_frame_changed: false,\n current_res: Chip8VideoModes::LowRes,\n }\n }\n\n pub fn peek(\u0026self, address: u16) -\u003e bool {\n let loop_value: u16 = if self.is_highres() {\n SCHIP_VIDE_MEMORY as u16\n } else {\n CHIP8_VIDEO_MEMORY as u16\n };\n let effective_address = if address \u003e= loop_value {\n address % loop_value\n } else {\n address\n };\n self.memory[effective_address as usize]\n }\n\n pub fn poke(\u0026mut self, address: u16, new_value: bool) {\n let loop_value: u16 = self.get_memory_size() as u16;\n // Loop the address\n let effective_address = address % loop_value;\n\n let old_value = self.memory[effective_address as usize];\n let xored_value = new_value ^ old_value; // XOR of the video\n // if the frame has already changed we dont care if it changed again.\n if !self.has_frame_changed \u0026\u0026 old_value != xored_value {\n self.has_frame_changed = true\n }\n\n // println!(\"VIDEO POKE COMPLETE WITH {effective_address} SET TO {xored_value}\");\n self.memory[effective_address as usize] = xored_value;\n }\n\n pub fn poke_byte(\u0026mut self, first_address: u16, to_write: u8) {\n for i in (0..8).rev() {\n self.poke(first_address + (7 - i), (to_write \u0026 (1 \u003c\u003c i)) != 0);\n }\n }\n\n pub fn poke_2byte(\u0026mut self, first_address: u16, to_write: [u8; 2]) {\n for (idx, _) in to_write.iter().enumerate() {\n for i in (0..8).rev() {\n self.poke(\n first_address + (idx * 8) as u16 + (7 - i),\n (to_write[idx] \u0026 (1 \u003c\u003c i)) != 0,\n );\n }\n }\n }\n\n pub fn format_as_string(\u0026self) -\u003e String {\n let (width, height) = self.get_resolution();\n // println!(\"FORMATTING {width}x{height}\");\n let mut output = String::new();\n for row in 0..height {\n for column in 0..width {\n let data_offset = row * width + column;\n debug!(\n \"Rendering {data_offset} with value {}\",\n self.memory[data_offset as usize]\n );\n if self.memory[data_offset as usize] {\n output += \"*\"\n } else {\n output += \" \"\n }\n }\n output += \"\\n\";\n }\n output\n }\n\n pub fn get_resolution(\u0026self) -\u003e (i32, i32) {\n if self.is_highres() {\n (SCHIP_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT)\n } else {\n (CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT)\n }\n }\n fn get_memory_size(\u0026self) -\u003e i32 {\n let (width, height) = self.get_resolution();\n width * height\n }\n\n pub fn tick(\u0026mut self) {\n self.has_frame_changed = false;\n }\n\n pub fn scroll_right(\u0026mut self) {\n // println!(\"SCROLLRIGHTPRE:::[{}]\", self.format_as_string());\n let (width, height) = self.get_resolution();\n\n for current_row in 0..height {\n let row_offset: usize = (current_row * width) as usize;\n\n // Shift pixels to the right by 4 in the current row\n for current_column in (0..(width - 4)).rev() {\n let source_address = row_offset + current_column as usize;\n let target_address = source_address + 4;\n self.memory[target_address] = self.memory[source_address];\n }\n\n // Clear the first 4 pixels in the current row\n self.memory[row_offset..row_offset + 4].fill(false);\n }\n // println!(\"SCROLLRIGHTPOST:::[{}]\", self.format_as_string());\n }\n\n pub fn scroll_left(\u0026mut self) {\n // println!(\"SCROLLLEFTPRE:::[{}]\", self.format_as_string());\n let (width, height) = self.get_resolution();\n\n for current_row in 0..height {\n let row_offset = current_row * width;\n for current_column in 0..width - 4 {\n let target: usize = (row_offset + current_column) as usize;\n let source: usize = target + 4;\n self.memory[target] = self.memory[source];\n // println!(\"Moving from {source} to {target}\");\n }\n\n let clear_start: usize = (row_offset + width - 4) as usize;\n let clear_end: usize = clear_start + 4;\n\n self.memory[clear_start..clear_end].fill(false);\n }\n // println!(\"SCROLLLEFTPOST:::[{}]\", self.format_as_string());\n }\n\n pub fn scroll_up(\u0026mut self, how_far: \u0026u8) {\n let how_far = *how_far as i32;\n let (width, height) = self.get_resolution();\n let row_shift = how_far * width;\n\n for current_source_row in how_far..height {\n let current_source_offset = current_source_row * width;\n for current_source_column in 0..width {\n let base_offset: usize = (current_source_offset + current_source_column) as usize;\n let shifted_offset: usize = base_offset - row_shift as usize;\n self.memory[shifted_offset] = self.memory[base_offset];\n }\n }\n\n // Clear the new bottom rows after shifting\n let clear_start = ((height - how_far) * width) as usize;\n self.memory[clear_start..].fill(false);\n }\n\n pub fn scroll_down(\u0026mut self, how_far: i32) {\n let (width, height) = self.get_resolution();\n let row_shift = how_far * width;\n\n let max_source_row = height - how_far;\n for current_source_row in (0..max_source_row).rev() {\n let current_source_offset = current_source_row * width;\n for current_source_column in 0..width {\n let base_offset: usize = (current_source_offset + current_source_column) as usize;\n let extended_offset: usize = base_offset + row_shift as usize;\n self.memory[extended_offset] = self.memory[base_offset];\n }\n }\n\n // Clear the new top rows after shifting\n let clear_end = (how_far * width) as usize;\n self.memory[0..clear_end].fill(false);\n }\n}\n\nimpl Default for Chip8Video {\n fn default() -\u003e Self {\n let mut mem = vec![];\n for _ in 0..CHIP8_VIDEO_MEMORY {\n mem.push(false);\n }\n Chip8Video {\n memory: mem,\n has_frame_changed: false,\n current_res: LowRes,\n }\n }\n}\n","traces":[{"line":23,"address":[1315696],"length":1,"stats":{"Line":2}},{"line":24,"address":[796030],"length":1,"stats":{"Line":2}},{"line":25,"address":[1593337],"length":1,"stats":{"Line":4}},{"line":26,"address":[2466404],"length":1,"stats":{"Line":4}},{"line":29,"address":[798400],"length":1,"stats":{"Line":2}},{"line":30,"address":[1002709],"length":1,"stats":{"Line":2}},{"line":33,"address":[4018880],"length":1,"stats":{"Line":1}},{"line":34,"address":[459897],"length":1,"stats":{"Line":1}},{"line":35,"address":[798455],"length":1,"stats":{"Line":1}},{"line":38,"address":[459936],"length":1,"stats":{"Line":4}},{"line":39,"address":[1593445],"length":1,"stats":{"Line":4}},{"line":42,"address":[1593472],"length":1,"stats":{"Line":1}},{"line":43,"address":[1694165],"length":1,"stats":{"Line":1}},{"line":46,"address":[877568],"length":1,"stats":{"Line":2}},{"line":47,"address":[787741],"length":1,"stats":{"Line":2}},{"line":48,"address":[1593511],"length":1,"stats":{"Line":2}},{"line":49,"address":[1315909],"length":1,"stats":{"Line":2}},{"line":50,"address":[796240],"length":1,"stats":{"Line":1}},{"line":52,"address":[798585],"length":1,"stats":{"Line":2}},{"line":53,"address":[798648],"length":1,"stats":{"Line":2}},{"line":57,"address":[1593632],"length":1,"stats":{"Line":4}},{"line":58,"address":[877717],"length":1,"stats":{"Line":4}},{"line":61,"address":[1593648],"length":1,"stats":{"Line":1}},{"line":63,"address":[1003138,1003011],"length":1,"stats":{"Line":1}},{"line":69,"address":[877904],"length":1,"stats":{"Line":2}},{"line":70,"address":[1593851,1593868],"length":1,"stats":{"Line":4}},{"line":71,"address":[41409310],"length":1,"stats":{"Line":1}},{"line":73,"address":[798901],"length":1,"stats":{"Line":2}},{"line":75,"address":[788139,788213,788122],"length":1,"stats":{"Line":5}},{"line":76,"address":[41409403,41409415,41409341],"length":1,"stats":{"Line":2}},{"line":78,"address":[796598],"length":1,"stats":{"Line":2}},{"line":80,"address":[2466980],"length":1,"stats":{"Line":2}},{"line":83,"address":[788240],"length":1,"stats":{"Line":2}},{"line":84,"address":[1316423],"length":1,"stats":{"Line":2}},{"line":86,"address":[1754294,1754399],"length":1,"stats":{"Line":2}},{"line":88,"address":[1316474],"length":1,"stats":{"Line":2}},{"line":89,"address":[796842],"length":1,"stats":{"Line":2}},{"line":91,"address":[4019671,4019733,4019639],"length":1,"stats":{"Line":6}},{"line":92,"address":[796945],"length":1,"stats":{"Line":2}},{"line":96,"address":[788444],"length":1,"stats":{"Line":2}},{"line":99,"address":[878336],"length":1,"stats":{"Line":3}},{"line":100,"address":[1594365,1594293],"length":1,"stats":{"Line":6}},{"line":101,"address":[1003724],"length":1,"stats":{"Line":3}},{"line":105,"address":[1754800],"length":1,"stats":{"Line":1}},{"line":106,"address":[797318,797458],"length":1,"stats":{"Line":2}},{"line":107,"address":[41410215],"length":1,"stats":{"Line":1}},{"line":108,"address":[797857],"length":1,"stats":{"Line":1}},{"line":109,"address":[1317274,1317435],"length":1,"stats":{"Line":1}},{"line":110,"address":[4020664,4020531,4020565],"length":1,"stats":{"Line":2}},{"line":116,"address":[2469380,2468256],"length":1,"stats":{"Line":2}},{"line":117,"address":[1595238],"length":1,"stats":{"Line":2}},{"line":119,"address":[1755519],"length":1,"stats":{"Line":2}},{"line":120,"address":[1755601,1755734,1755528],"length":1,"stats":{"Line":6}},{"line":121,"address":[879732,879592],"length":1,"stats":{"Line":4}},{"line":122,"address":[800823,800710,800737],"length":1,"stats":{"Line":4}},{"line":123,"address":[41411389],"length":1,"stats":{"Line":3}},{"line":127,"address":[1696943,1696504],"length":1,"stats":{"Line":4}},{"line":128,"address":[1697007,1696975],"length":1,"stats":{"Line":4}},{"line":130,"address":[1005658,1005604],"length":1,"stats":{"Line":4}},{"line":133,"address":[1696305,1696372],"length":1,"stats":{"Line":4}},{"line":135,"address":[1696135],"length":1,"stats":{"Line":2}},{"line":138,"address":[2469408],"length":1,"stats":{"Line":2}},{"line":139,"address":[462818,462793],"length":1,"stats":{"Line":4}},{"line":140,"address":[41411829],"length":1,"stats":{"Line":2}},{"line":142,"address":[880451],"length":1,"stats":{"Line":2}},{"line":145,"address":[1596432],"length":1,"stats":{"Line":2}},{"line":146,"address":[801481],"length":1,"stats":{"Line":2}},{"line":147,"address":[1697143,1697164],"length":1,"stats":{"Line":2}},{"line":150,"address":[2469552],"length":1,"stats":{"Line":2}},{"line":151,"address":[462933],"length":1,"stats":{"Line":4}},{"line":154,"address":[790752],"length":1,"stats":{"Line":1}},{"line":156,"address":[2469585],"length":1,"stats":{"Line":1}},{"line":158,"address":[1005893,1005950],"length":1,"stats":{"Line":2}},{"line":159,"address":[1596661,1596614],"length":1,"stats":{"Line":1}},{"line":162,"address":[463110,463413,463076,463206],"length":1,"stats":{"Line":4}},{"line":163,"address":[4022279,4022389,4022365],"length":1,"stats":{"Line":2}},{"line":164,"address":[801972,802036,801925],"length":1,"stats":{"Line":2}},{"line":165,"address":[1006284],"length":1,"stats":{"Line":1}},{"line":169,"address":[1697447,1697512],"length":1,"stats":{"Line":2}},{"line":174,"address":[41412464],"length":1,"stats":{"Line":1}},{"line":176,"address":[802081],"length":1,"stats":{"Line":1}},{"line":178,"address":[881204,881147],"length":1,"stats":{"Line":2}},{"line":179,"address":[463589,463548],"length":1,"stats":{"Line":1}},{"line":180,"address":[802236,802631,802202,802324],"length":1,"stats":{"Line":4}},{"line":181,"address":[1698163,1697983,1698194],"length":1,"stats":{"Line":2}},{"line":182,"address":[800300,800236,800194],"length":1,"stats":{"Line":2}},{"line":183,"address":[1006884],"length":1,"stats":{"Line":1}},{"line":187,"address":[1757507,1757559,1757624],"length":1,"stats":{"Line":2}},{"line":188,"address":[1319799,1319752,1319839],"length":1,"stats":{"Line":2}},{"line":190,"address":[791663],"length":1,"stats":{"Line":1}},{"line":195,"address":[1006960],"length":1,"stats":{"Line":2}},{"line":196,"address":[1320022],"length":1,"stats":{"Line":1}},{"line":197,"address":[464065],"length":1,"stats":{"Line":2}},{"line":198,"address":[1698365,1698409],"length":1,"stats":{"Line":1}},{"line":200,"address":[1320089,1320175,1320121],"length":1,"stats":{"Line":5}},{"line":201,"address":[882042,882017,881882],"length":1,"stats":{"Line":3}},{"line":202,"address":[2471034,2471227,2471000],"length":1,"stats":{"Line":4}},{"line":203,"address":[1698769,1698714],"length":1,"stats":{"Line":1}},{"line":204,"address":[1698864,1698748,1698800],"length":1,"stats":{"Line":3}},{"line":205,"address":[1320504],"length":1,"stats":{"Line":2}},{"line":210,"address":[1758153,1758014,1758068],"length":1,"stats":{"Line":3}},{"line":211,"address":[792118],"length":1,"stats":{"Line":2}},{"line":214,"address":[1598192],"length":1,"stats":{"Line":1}},{"line":215,"address":[2471273],"length":1,"stats":{"Line":1}},{"line":216,"address":[1320627,1320668],"length":1,"stats":{"Line":1}},{"line":218,"address":[1320655,1320688,1320720],"length":1,"stats":{"Line":2}},{"line":219,"address":[41413792,41413847,41413748],"length":1,"stats":{"Line":3}},{"line":220,"address":[882595,882498,882620],"length":1,"stats":{"Line":2}},{"line":221,"address":[792796,792986,792762],"length":1,"stats":{"Line":3}},{"line":222,"address":[4024144,4024092],"length":1,"stats":{"Line":1}},{"line":223,"address":[1007982,1008031,1008095],"length":1,"stats":{"Line":2}},{"line":224,"address":[4024183],"length":1,"stats":{"Line":1}},{"line":229,"address":[801145,801195,801093],"length":1,"stats":{"Line":2}},{"line":230,"address":[1320836],"length":1,"stats":{"Line":1}},{"line":235,"address":[1321152,1321455],"length":1,"stats":{"Line":6}},{"line":236,"address":[801489],"length":1,"stats":{"Line":11}},{"line":237,"address":[801503,801584],"length":1,"stats":{"Line":19}},{"line":238,"address":[793281],"length":1,"stats":{"Line":7}}],"covered":118,"coverable":118},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","add.rs"],"content":"use crate::chip8::instructions::Chip8CpuInstructions;\nuse crate::chip8::registers::Chip8Registers;\nuse crate::chip8_2::instructions::instruction_trait::InstructionTrait;\n\nstruct Add {\n pub relative: bool,\n pub source: Option\u003cChip8Registers\u003e,\n pub destination: Chip8Registers,\n pub literal: Option\u003cu8\u003e\n}\n\nimpl InstructionTrait for Add {\n fn length(\u0026self) -\u003e u8 {\n 1\n }\n\n fn encoded(\u0026self) -\u003e Chip8CpuInstructions {\n }\n\n fn decode(from: Vec\u003cu8\u003e) -\u003e \u0026dyn InstructionTrait {\n\n todo!()\n }\n}","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","call.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","cls.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","drw.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","exit.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","high.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","jp.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","ld.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","low.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","ret.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","rnd.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","scd.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","scl.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","scr.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","se.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","sknp.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","skp.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","sne.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","chip8_2","instructions","sys.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","constants.rs"],"content":"pub const CHIP8_REGISTER_COUNT: i32 = 16;\npub const CHIP8_MEMORY_SIZE: i32 = 4096i32;\npub const CHIP8_VIDEO_WIDTH: i32 = 64i32;\npub const CHIP8_VIDEO_HEIGHT: i32 = 32i32;\npub const CHIP8_VIDEO_MEMORY: usize = (CHIP8_VIDEO_HEIGHT * CHIP8_VIDEO_WIDTH) as usize;\npub const CHIP8_ROM_SIZE: usize = 512;\n\npub const LABEL_QUIRK_CHIP8: \u0026str = \"Chip8\";\npub const LABEL_QUIRK_XOCHIP: \u0026str = \"XO Chip\";\npub const LABEL_QUIRK_SCHIP: \u0026str = \"SChip-Modern\";\n\npub const RESOURCES_ROOT: \u0026str = \"../resources\";\npub const TESTS_ROOT: \u0026str = \"../resources/test/\";\npub const TEST_ROM_ROOT: \u0026str = \"../resources/test/roms\";\n\npub const CHIP8_KEYBOARD: [[u8; 4]; 4] = [\n [0x01, 0x02, 0x03, 0x0C],\n [0x04, 0x05, 0x06, 0x0D],\n [0x07, 0x08, 0x09, 0x0E],\n [0x0A, 0x00, 0x0B, 0x0F],\n];\n\npub const SCHIP_VIDEO_HEIGHT: i32 = 64;\npub const SCHIP_VIDEO_WIDTH: i32 = 128;\npub const SCHIP_VIDE_MEMORY: usize = (SCHIP_VIDEO_HEIGHT * SCHIP_VIDEO_WIDTH) as usize;\n\npub const INST_ADDI: \u0026str = \"ADDI\";\npub const INST_ADD: \u0026str = \"ADD\";\npub const INST_ADDR: \u0026str = \"ADDR\";\npub const INST_AND: \u0026str = \"AND\";\npub const INST_CLS: \u0026str = \"CLS\";\npub const INST_CALL: \u0026str = \"CALL\";\npub const INST_DRW: \u0026str = \"DRW\";\npub const INST_EXIT: \u0026str = \"EXIT\";\npub const INST_JPA: \u0026str = \"JPA\";\npub const INST_JPI: \u0026str = \"JPI\";\npub const INST_JPX: \u0026str = \"JPX\";\npub const INST_BCD: \u0026str = \"BCD\";\npub const INST_LDD: \u0026str = \"LDD\";\npub const INST_LDF: \u0026str = \"LDF\";\npub const INST_LDF2: \u0026str = \"LDF2\";\npub const INST_LDIA: \u0026str = \"LDIA\";\npub const INST_LDIX: \u0026str = \"LDIX\";\npub const INST_LIDR: \u0026str = \"LIDR\";\npub const INST_LDIS: \u0026str = \"LDIS\";\npub const INST_LDR: \u0026str = \"LDR\";\npub const INST_LDRD: \u0026str = \"LDRD\";\npub const INST_LDRI: \u0026str = \"LDRI\";\npub const INST_LDRK: \u0026str = \"LDRK\";\npub const INST_LDRY: \u0026str = \"LDRY\";\npub const INST_OR: \u0026str = \"OR\";\npub const INST_RET: \u0026str = \"RET\";\npub const INST_RND: \u0026str = \"RND\";\npub const INST_SCD: \u0026str = \"SCD\";\npub const INST_SCR: \u0026str = \"SCR\";\npub const INST_SCL: \u0026str = \"SCL\";\npub const INST_SEX: \u0026str = \"SEX\";\npub const INST_SEY: \u0026str = \"SEY\";\npub const INST_SHL: \u0026str = \"SHL\";\npub const INST_SHR: \u0026str = \"SHR\";\npub const INST_SKP: \u0026str = \"SKP\";\npub const INST_SKNP: \u0026str = \"SKNP\";\npub const INST_SNEB: \u0026str = \"SNEB\";\npub const INST_SNEY: \u0026str = \"SNEY\";\npub const INST_STR: \u0026str = \"STR\";\npub const INST_SUB: \u0026str = \"SUB\";\npub const INST_SUBC: \u0026str = \"SUBC\";\npub const INST_SYS: \u0026str = \"SYS\";\npub const INST_LOW: \u0026str = \"LOW\";\npub const INST_HIGH: \u0026str = \"HIGH\";\npub const INST_ORY: \u0026str = \"ORY\";\npub const INST_SCU: \u0026str = \"SCU\";\n/// Data Word\n/// Data to be loaded to memory for application use\npub const INST_DW: \u0026str = \"DW\";\n\npub const XXXXERRORINSTRUCTION_ENCODED: u16 = 0x0000;\n\npub const CHIP8_PROGRAM_LOAD_OFFSET: i32 = 0x200;\npub const CHIP8FONT_0: [u8; 5] = [0xF0, 0x90, 0x90, 0x90, 0xF0];\npub const CHIP8FONT_1: [u8; 5] = [0x20, 0x60, 0x20, 0x20, 0x70];\npub const CHIP8FONT_2: [u8; 5] = [0xF0, 0x10, 0xF0, 0x80, 0xF0];\npub const CHIP8FONT_3: [u8; 5] = [0xF0, 0x10, 0xF0, 0x10, 0xF0];\npub const CHIP8FONT_4: [u8; 5] = [0x90, 0x90, 0xF0, 0x10, 0x10];\npub const CHIP8FONT_5: [u8; 5] = [0xF0, 0x80, 0xF0, 0x10, 0xF0];\npub const CHIP8FONT_6: [u8; 5] = [0xF0, 0x80, 0xF0, 0x90, 0xF0];\npub const CHIP8FONT_7: [u8; 5] = [0xF0, 0x10, 0x20, 0x40, 0x40];\npub const CHIP8FONT_8: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0xF0];\npub const CHIP8FONT_9: [u8; 5] = [0xF0, 0x90, 0xF0, 0x10, 0xF0];\npub const CHIP8FONT_A: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0x90];\npub const CHIP8FONT_B: [u8; 5] = [0xE0, 0x90, 0xE0, 0x90, 0xE0];\npub const CHIP8FONT_C: [u8; 5] = [0xF0, 0x80, 0x80, 0x80, 0xF0];\npub const CHIP8FONT_D: [u8; 5] = [0xE0, 0x90, 0x90, 0x90, 0xE0];\npub const CHIP8FONT_E: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0xF0];\npub const CHIP8FONT_F: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0x80];\n\npub const SCHIPFONT_OFFSET: u32 = 0x81;\npub const SCHIPFONT_0: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xF0];\npub const SCHIPFONT_1: [u8; 0x0A] = [0x20, 0x60, 0xA0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70];\npub const SCHIPFONT_2: [u8; 0x0A] = [0xF0, 0x10, 0x10, 0x10, 0xF0, 0x80, 0x80, 0x80, 0x80, 0xF0];\npub const SCHIPFONT_3: [u8; 0x0A] = [0xF0, 0x10, 0x10, 0x10, 0xF0, 0x10, 0x10, 0x10, 0x10, 0xF0];\npub const SCHIPFONT_4: [u8; 0x0A] = [0x90, 0x90, 0x90, 0x90, 0xF0, 0x10, 0x10, 0x10, 0x10, 0x10];\npub const SCHIPFONT_5: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x10, 0x10, 0x10, 0x10, 0xF0];\npub const SCHIPFONT_6: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x90, 0x90, 0x90, 0x90, 0xF0];\npub const SCHIPFONT_7: [u8; 0x0A] = [0xF0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10];\npub const SCHIPFONT_8: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0xF0, 0x90, 0x90, 0x90, 0x90, 0xF0];\npub const SCHIPFONT_9: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0xF0, 0x10, 0x10, 0x10, 0x10, 0xF0];\npub const SCHIPFONT_A: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0xF0, 0x90, 0x90, 0x90, 0x90, 0x90];\npub const SCHIPFONT_B: [u8; 0x0A] = [0xE0, 0x90, 0x90, 0x90, 0xE0, 0x90, 0x90, 0x90, 0x90, 0xE0];\npub const SCHIPFONT_C: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF0];\npub const SCHIPFONT_D: [u8; 0x0A] = [0xE0, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xE0];\npub const SCHIPFONT_E: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80, 0x80, 0xF0];\npub const SCHIPFONT_F: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80, 0x80, 0x80];\n\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","src","lib.rs"],"content":"pub mod chip8 {\n pub mod video;\n pub mod sound_timer;\n pub mod delay_timer;\n pub mod keypad;\n pub mod computer;\n pub mod system_memory;\n pub mod instructions;\n pub mod cpu_states;\n pub mod util;\n pub mod registers;\n\n pub mod stack;\n pub mod computer_manager;\n pub mod quirk_modes;\n}\n\npub mod constants;","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","tests","computer_tests.rs"],"content":"use std::path::Path;\nuse std::time::Instant;\nuse gemma::chip8::computer::Chip8Computer;\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\nuse gemma::chip8::quirk_modes::QuirkMode;\nuse gemma::chip8::quirk_modes::QuirkMode::{Chip8, SChipModern, XOChip};\nuse gemma::chip8::registers::Chip8Registers;\nuse gemma::constants::{CHIP8_VIDEO_MEMORY, TESTS_ROOT};\n\n#[test]\nfn smoke() {\n assert!(true)\n}\n\nfn load_result(to_load: \u0026str) -\u003e String {\n std::fs::read_to_string(format!(\"../resources/test/{}\", to_load)).unwrap()\n}\n\nfn load_rom(to_load: \u0026str) -\u003e Vec\u003cu8\u003e {\n std::fs::read(format!(\"../resources/roms/{}\", to_load)).unwrap()\n}\n\n#[test]\nfn reset_clears_video() {\n let mut x = Chip8Computer::new();\n\n for i in 0..CHIP8_VIDEO_MEMORY {\n x.video_memory.poke(i as u16, i % 2 == 0);\n }\n\n x.reset(gemma::chip8::quirk_modes::QuirkMode::Chip8);\n x.video_memory.reset();\n\n assert_eq!(x.dump_video_to_string(), x.video_memory.format_as_string());\n\n for i in 0..CHIP8_VIDEO_MEMORY {\n assert!(!x.video_memory.peek(i as u16));\n }\n}\n\n#[test]\nfn level1_test() {\n let mut x = Chip8Computer::new();\n let level_1_rom = load_rom(\"1-chip8-logo.ch8\");\n x.load_bytes_to_memory(0x200, \u0026level_1_rom);\n\n // run for 0x40 cycles\n while x.num_cycles \u003c 0x40 {\n x.step_system();\n }\n assert_eq!(\n x.dump_video_to_string(),\n load_result(\"gemma_integration_level_1_test.asc\")\n );\n}\n\n#[test]\nfn level2_test() {\n let mut x = Chip8Computer::new();\n // Load the IBM rom and run it.\n // it takes 39 cycles to get to the end so lets run it 40.\n let test_rom_to_run = load_rom(\"2-ibm-logo.ch8\");\n x.load_bytes_to_memory(0x200, \u0026test_rom_to_run);\n for _ in 0..40 {\n x.step_system();\n }\n // ...then verify that the current video memory of the chip-8\n // simulator matches what we expect it to be\n\n assert_eq!(\n x.dump_video_to_string(),\n load_result(\"gemma_integration_ibm_rom_output.asc\")\n );\n}\n\n#[test]\nfn level3_test() {\n let mut x = Chip8Computer::new();\n\n x.load_bytes_to_memory(0x200, \u0026load_rom(\"3-corax+.ch8\"));\n for i in 0..0x180 {\n x.step_system();\n }\n\n assert_eq!(\n x.dump_video_to_string(),\n load_result(\"gemma_integration_corax_plus.asc\")\n );\n}\n\n#[test]\nfn rps_test() {\n let mut x = Chip8Computer::new();\n x.load_bytes_to_memory(0x200, \u0026load_rom(\"RPS.ch8\"));\n for _ in 0..0xF0 {\n x.step_system();\n }\n assert_eq!(\n x.dump_video_to_string(),\n load_result(\"gemma_integration_rps_stage1.asc\")\n );\n x.keypad.push_key(0x01);\n for _ in 0..0x200 {\n x.step_system();\n }\n assert_eq!(\n x.dump_video_to_string(),\n load_result(\"gemma_integration_rps_stage2.asc\")\n );\n}\n\n#[test]\nfn level4_test() {\n // flags\n let mut x = Chip8Computer::new();\n x.load_bytes_to_memory(0x200, \u0026load_rom(\"4-flags.ch8\"));\n for _ in 0..0x400 {\n x.step_system();\n }\n\n assert_eq!(\n x.dump_video_to_string(),\n load_result(\"gemma_integration_flags.asc\")\n );\n}\n\n#[test]\nfn registers_equality() {\n let data_set: Vec\u003c(Chip8Registers, Chip8Registers, bool)\u003e = vec![\n (Chip8Registers::default(), Chip8Registers::default(), true),\n (Chip8Registers {\n registers: [0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,],\n i_register: 0,\n pc: 0,\n },\n Chip8Registers {\n registers:[0x01, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,],\n i_register: 0,\n pc: 0,\n }, false)\n ];\n\n for (first, second, matches) in data_set.iter() {\n assert_eq!(first == second, *matches)\n }\n}\n\n#[test]\nfn default_test() {\n let new_manager = Chip8ComputerManager::default();\n\n assert_eq!(new_manager.core_should_run, false);\n assert_eq!(new_manager.num_cycles(), 0);\n assert_eq!(new_manager.quirks_mode(), Chip8);\n}\n\n#[test]\nfn quirks_mode_test() {\n let mut new_manager = Chip8ComputerManager::default();\n\n assert_eq!(new_manager.quirks_mode(), Chip8);\n\n new_manager.reset(QuirkMode::XOChip);\n\n assert_eq!(new_manager.quirks_mode(), XOChip);\n\n new_manager.reset(QuirkMode::SChipModern);\n\n assert_eq!(new_manager.quirks_mode(), SChipModern);\n\n new_manager.reset(Chip8);\n\n assert_eq!(new_manager.quirks_mode(), Chip8);\n}\n\n#[test]\nfn load_rom_allows_starting() {\n let mut new_manager = Chip8ComputerManager::default();\n assert_eq!(new_manager.core_should_run, false);\n let p = format!(\"{}/../resources/test/roms/1-chip8-logo.ch8\" , std::env::current_dir().unwrap().display());\n let full_path = Path::new(p.as_str());\n new_manager.load_new_program_from_disk_to_system_memory(full_path);\n assert_eq!(new_manager.core_should_run, true)\n}\n\n#[test]\nfn reset_clears_run_state() {\n let mut new_manager = Chip8ComputerManager::default();\n let p = format!(\"{}/../resources/test/roms/1-chip8-logo.ch8\", std::env::current_dir().unwrap().display());\n new_manager.load_new_program_from_disk_to_system_memory(Path::new(p.as_str()));\n new_manager.reset(QuirkMode::Chip8);\n assert_eq!(new_manager.core_should_run, false);\n}\n","traces":[{"line":15,"address":[462224],"length":1,"stats":{"Line":1}},{"line":16,"address":[462373,462251],"length":1,"stats":{"Line":2}},{"line":19,"address":[462592],"length":1,"stats":{"Line":1}},{"line":20,"address":[462619,462741],"length":1,"stats":{"Line":4}}],"covered":4,"coverable":4},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","tests","state_tests.rs"],"content":"use std::fs;\nuse std::io;\nuse flate2::write::GzDecoder;\nuse gemma::chip8::computer::Chip8Computer;\nuse std::io::prelude::*;\n\nfn load_result(to_load: \u0026str) -\u003e String {\n let full_path = format!(\"{}/../resources/test/state/{}\", std::env::current_dir().unwrap().display(), to_load);\n println!(\"CURRENT DIR: {:?}\", std::env::current_dir());\n println!(\"Loading state =\u003e (([{}]))\", full_path);\n std::fs::read_to_string(full_path).unwrap()\n}\n\n// fn load_compressed_result(file_path: \u0026str) -\u003e io::Result\u003cString\u003e {\n// // Load the compressed file contents\n// let compressed_data = fs::read(file_path)?;\n//\n// // Create a GzDecoder to uncompress the data\n// let mut decoder = GzDecoder::new(\u0026mut compressed_data[..]);\n// let mut decompressed_data = String::new();\n//\n// // Read the decompressed data directly into a String\n// decoder.read_to_string(\u0026mut decompressed_data)?;\n//\n// Ok(decompressed_data)\n// }\n\nfn load_rom(to_load: \u0026str) -\u003e Vec\u003cu8\u003e {\n fs::read(format!(\"resources/test/roms/{}\", to_load)).unwrap()\n}\n\n#[test]\n#[ignore]\nfn serialization_round_trip() {\n let original_computer = Chip8Computer::new();\n let expected_json = load_result(\"smoke_001_round_trip_serialize_deserialize.json\");\n\n // Serialize the Chip8Computer instance\n let serialized = serde_json::to_string(\u0026original_computer).expect(\"Serialization failed\");\n\n // Compare the serialized output to the expected JSON\n println!(\"Serialized Output: [{}]\", serialized);\n assert_eq!(\n serialized.trim(),\n expected_json.trim(),\n \"Serialized output does not match expected JSON\"\n );\n\n // Deserialize back to Chip8Computer and assert equality\n let deserialized_computer: Chip8Computer =\n serde_json::from_str(\u0026serialized).expect(\"Deserialization failed\");\n assert_eq!(\n deserialized_computer, original_computer,\n \"Deserialized instance does not match the original\"\n );\n}\n//\n// #[test]\n// fn computer_001_system_zero_state() {\n// let x = Chip8Computer::new();\n// let expected_string = load_compressed_result(\"smoke_002_round_trip_serialize_deserialize.tflt\").unwrap();\n// let serialized = serde_json::to_string(\u0026x).unwrap();\n// assert_eq!(serialized, expected_string);\n// }\n//\n","traces":[{"line":7,"address":[701386,700144],"length":1,"stats":{"Line":0}},{"line":8,"address":[700764,700171,700401],"length":1,"stats":{"Line":0}},{"line":9,"address":[700868,700772],"length":1,"stats":{"Line":0}},{"line":10,"address":[700996],"length":1,"stats":{"Line":0}},{"line":11,"address":[701089],"length":1,"stats":{"Line":0}},{"line":28,"address":[701440],"length":1,"stats":{"Line":0}},{"line":29,"address":[701467,701589],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":7},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","tests","unit_tests.rs"],"content":"use std::fs::File;\nuse std::io::Read;\nuse gemma::chip8::computer::Chip8Computer;\nuse gemma::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction;\nuse gemma::chip8::delay_timer::DelayTimer;\nuse gemma::chip8::instructions::Chip8CpuInstructions;\nuse gemma::chip8::instructions::Chip8CpuInstructions::JPX;\nuse gemma::chip8::keypad::Keypad;\nuse gemma::chip8::quirk_modes::QuirkMode::{self, Chip8, SChipModern, XOChip};\nuse gemma::chip8::registers::Chip8Registers;\nuse gemma::chip8::sound_timer::SoundTimer;\nuse gemma::chip8::stack::Chip8Stack;\nuse gemma::chip8::system_memory::Chip8SystemMemory;\nuse gemma::chip8::util::InstructionUtil;\nuse gemma::chip8::video::{Chip8Video, Chip8VideoModes};\nuse gemma::constants::*;\nuse log::debug;\nuse rand::random;\nuse serde::Serialize;\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\n\nconst TEST_OUTPUT_SAMPLE_DIR: \u0026str = \"../resources/test/\";\n\nfn read_test_result(suffix: \u0026str) -\u003e String {\n println!(\"SITTING IN {:?}\", std::env::current_dir());\n println!(\"ATTEMPT TO READ RESULT {suffix}\");\n std::fs::read_to_string(TEST_OUTPUT_SAMPLE_DIR.to_owned() + suffix).unwrap()\n}\n\n#[test]\nfn smoke() {\n assert!(true)\n}\n\n#[test]\n#[ignore]\nfn decoder_test_invalid_instructions() {\n let invalid_to_encode = [\n 0x5ab1, 0x5abf, 0x8ab8, 0x8abd, 0x8abf, 0x9ab1, 0x9abf, 0xea9d, 0xea9f, 0xeaa0, 0xeaa2,\n 0xf006, 0xf008,\n ];\n\n for i in invalid_to_encode {\n assert_eq!(Chip8CpuInstructions::decode(i, \u0026Chip8).encode(), XXXXERRORINSTRUCTION_ENCODED);\n assert!(matches!(\n Chip8CpuInstructions::decode(i, \u0026Chip8),\n Chip8CpuInstructions::XXXXERRORINSTRUCTION\n ));\n }\n}\n\n/// START OF THE EXECUTION TESTS\n#[test]\nfn instruction_tests() {\n // 0x0nnn Exit to System Call\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::SYS(0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::SYS(0xFA0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0xFA0);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::SYS(0x0AF).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x0AF);\n\n // 0x1nnn Jump to Address\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::JPA(0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::JPA(0xABC).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0xABC);\n\n // 0x6xkk Set Vx = kk\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::LDR(1, 0x12).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0x12);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::LDR(2, 0x21).execute(\u0026mut x);\n assert_eq!(x.registers.peek(2), 0x21);\n\n // 0x3xkk Skip next instruction if Vx = kk.\n // The interpreter compares register Vx to kk,\n // and if they are equal, increments the program counter by 2.\n\n // test setup: Load value 0x84 into V1\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0x84);\n Chip8CpuInstructions::SEX(1, 0x48).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0x84);\n Chip8CpuInstructions::SEX(1, 0x84).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204);\n\n // 0x4xkk Skip next instruction if Vx != kk\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x84);\n x.registers.poke(0x2, 0x84);\n // skip, compare 0x84 to 0x84\n Chip8CpuInstructions::SEY(0x1, 0x2).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x84);\n x.registers.poke(0x2, 0x48);\n Chip8CpuInstructions::SEY(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n\n // 0x8xy0 Set value of Vy in Vx\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x01);\n x.registers.poke(0x02, 0x02);\n Chip8CpuInstructions::LDRY(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0x02);\n\n // 0x8xy1 Set Vx = Vx OR Vy\n // 0b0101 0000 (0x50)\n // | 0b0000 1010 (0x0A)\n // 0b0101 1010 (0x5A)\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0b01010000);\n x.registers.poke(0x02, 0b00001010);\n Chip8CpuInstructions::OR(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0b01011010);\n\n // 0x8xy2 Set Vx = Vx AND Vy\n // 0b1111 1100 (0xFC)\n // \u0026 0b1100 1010 (0xCA)\n // 0b1100 1000 (0xC8)\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0xFC);\n x.registers.poke(0x02, 0xCA);\n Chip8CpuInstructions::AND(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0xC8);\n\n // 0x8xy3 Set Vx = Vx XOR Vy\n // 0b1111 1100 (0xFC)\n // ^ 0b1100 1010 (0xCA)\n // 0b0011 0110 (0x36)\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0b11111100);\n x.registers.poke(0x02, 0b11001010);\n Chip8CpuInstructions::ORY(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0b00110110);\n\n // 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)\n // T1 T2: Judgement Test\n // 0x01 0xFF\n // + 0x01 0x01\n // 0x02 F0 0x00 F1\n let mut x = Chip8Computer::new();\n x.registers.poke(0x0f, 00);\n x.registers.poke(0x01, 0x01);\n x.registers.poke(0x02, 0x01);\n Chip8CpuInstructions::ADDR(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0xf), 0x00);\n assert_eq!(x.registers.peek(0x01), 0x02);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x0f, 0x00);\n x.registers.poke(0x01, 0xff);\n x.registers.poke(0x02, 0x01);\n Chip8CpuInstructions::ADDR(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0xf), 1);\n assert_eq!(x.registers.peek(1), 0);\n\n /*\n Set Vx = Vx SHR 1.\n\n If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0. Then Vx is divided by 2.\n */\n let mut x = Chip8Computer::new();\n x.registers.poke(0x0f, 0x00);\n x.registers.poke(0x01, 0b00001000);\n x.registers.poke(0x02, 0b00000000);\n Chip8CpuInstructions::SHR(0x1, 0x2).execute(\u0026mut x); // 0b0000 0010 (0x02) (Not Set)\n assert_eq!(x.registers.peek(1), 0b00000100);\n assert_eq!(x.registers.peek(0xf), 0);\n\n x = Chip8Computer::new();\n x.registers.poke(0x0f, 0x00);\n x.registers.poke(0x01, 0b00001001);\n Chip8CpuInstructions::SHR(0x1, 0x2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0b00000100);\n assert_eq!(x.registers.peek(0xf), 1);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::LDIA(0x123).execute(\u0026mut x);\n assert_eq!(x.registers.peek_i(), 0x123);\n assert_eq!(x.registers.peek_pc(), 0x202);\n}\n\n#[test]\nfn jp_v0addr_test() {\n let mut x = Chip8Computer::new();\n /// jump to I + nnn\n x.registers.poke(0x0, 0xff);\n Chip8CpuInstructions::JPI(0x100).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x1FF);\n}\n\n#[test]\nfn cls_test() {\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::CLS.execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n for i in 0..CHIP8_VIDEO_MEMORY {\n assert!(!x.video_memory.peek(i as u16));\n }\n // draw some thing to the video memory\n x.video_memory.poke(0x01, true);\n x.video_memory.poke(0x03, true);\n x.video_memory.poke(0x05, true);\n\n Chip8CpuInstructions::CLS.execute(\u0026mut x);\n\n for i in 0..CHIP8_VIDEO_MEMORY {\n assert!(!x.video_memory.peek(i as u16));\n }\n}\n\n#[test]\nfn skip_next_instruction_ne_text() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xf0);\n Chip8CpuInstructions::SNEB(0x1, 0x0f).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xf0);\n Chip8CpuInstructions::SNEB(0x1, 0xf0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n}\n\n#[test]\nfn addivx_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke_i(0xabc);\n x.registers.poke(0x0, 0x10);\n Chip8CpuInstructions::ADDI(0x0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_i(), 0xacc);\n}\n\n#[test]\nfn ldstvt_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xf0);\n Chip8CpuInstructions::LDIS(0x01).execute(\u0026mut x);\n assert_eq!(x.sound_timer.current(), 0xf0);\n x.sound_timer.tick();\n x.sound_timer.tick();\n x.sound_timer.tick();\n assert_eq!(x.sound_timer.current(), 0xed);\n}\n\n#[test]\nfn rnd_vx_byte_text() {\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::RND(0x1, 0x0f).execute(\u0026mut x);\n let new_value = x.registers.peek(0x1);\n assert!(new_value \u003c 0x10);\n}\n\n#[test]\nfn add_vx_byte_test() {\n let mut x = Chip8Computer::new();\n // set a value in the register\n x.registers.poke(0x01, 0xab);\n // add 0x10 to register\n Chip8CpuInstructions::ADD(0x1, 0x10).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0xbb);\n}\n\n#[test]\nfn sub_vx_vy_test() {\n let mut x = Chip8Computer::new();\n // load values in 2 registers\n x.registers.poke(0x1, 0x10);\n x.registers.poke(0x2, 0x08);\n Chip8CpuInstructions::SUB(0x1, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0xf), 1);\n assert_eq!(x.registers.peek(0x1), 0x8);\n assert_eq!(x.registers.peek_pc(), 0x202);\n}\n\n#[test]\nfn sne_vx_vy_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0x10);\n x.registers.poke(0x2, 0x10);\n Chip8CpuInstructions::SNEY(0x1, 0x2).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0x10);\n x.registers.poke(0x2, 0x00);\n Chip8CpuInstructions::SNEY(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204)\n}\n\n#[test]\nfn ld_dt_vx_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0x10);\n Chip8CpuInstructions::LDD(0x1).execute(\u0026mut x);\n assert_eq!(x.delay_timer.current(), 0x10);\n for _ in 0..0x20 {\n x.delay_timer.tick();\n }\n assert_eq!(x.delay_timer.current(), 0);\n}\n\n#[test]\nfn ld_vx_dt_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xf0);\n Chip8CpuInstructions::LDD(0x1).execute(\u0026mut x);\n x.delay_timer.tick();\n x.delay_timer.tick();\n x.delay_timer.tick();\n assert_eq!(x.delay_timer.current(), 0xed);\n}\n\n#[test]\nfn subn_vx_vy_test() {\n // This instruction subtracts the value in\n // register Vx from the value in register Vy and stores the result in register Vx.\n // The subtraction is performed as follows: Vx = Vy - Vx. If Vy is less than Vx,\n // the result will wrap around (due to the 8-bit nature of the registers).\n // The carry flag (VF) is set to 1 if there is no borrow (i.e., Vy is greater\n // than or equal to Vx), and it is set to 0 if there is a borrow.\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xa0);\n x.registers.poke(0x2, 0xab);\n Chip8CpuInstructions::SUBC(0x1, 0x2).execute(\u0026mut x);\n // expect the result to be 0x0b\n assert_eq!(x.registers.peek(0x1), 0x0b);\n // expect the vf register to be set to 1 as there was overflow\n assert_eq!(x.registers.peek(0xf), 0x1);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0xab);\n x.registers.poke(0x02, 0xa0);\n Chip8CpuInstructions::SUBC(0x1, 0x2).execute(\u0026mut x);\n\n // expect the result to be 11110101, -0xB, -11, 245, 0xF5\n assert_eq!(x.registers.peek(0x1), 0xf5);\n assert_eq!(x.registers.peek(0xf), 0x0);\n\n // 8xyE - SHL Vx {, Vy}\n // Set Vx = Vx SHL 1.\n //\n // If the most-significant bit of Vx is 1, then VF is set to 1, otherwise to 0. Then Vx is multiplied by 2.\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0b00100000);\n Chip8CpuInstructions::SHL(0x1, 0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x1), 0b01000000);\n assert_eq!(x.registers.peek(0xf), 0x0);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0b10101010);\n Chip8CpuInstructions::SHL(0x1, 0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x1), 0b01010100);\n assert_eq!(x.registers.peek(0xf), 0x1);\n\n // Fx29 - LD F, Vx\n // Set I = location of sprite for digit Vx.\n //\n // The value of I is set to the location for the hexadecimal sprite corresponding to the value of Vx. See section 2.4, Display, for more information on the Chip-8 hexadecimal font.\n let mut x = Chip8Computer::new();\n // target_sprite = 2\n // target_offset = 0x5\n x.registers.poke(0x1, 0x2);\n Chip8CpuInstructions::LDFX(0x1).execute(\u0026mut x);\n\n assert_eq!(x.registers.peek_i(), 10);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x06);\n Chip8CpuInstructions::LDFX(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_i(), 30);\n\n // Fx33 - LD B, Vx\n // Store BCD representation of Vx in memory locations I, I+1, and I+2.\n //\n // The interpreter takes the decimal value of Vx, and places the hundreds digit\n // in memory at location in I, the tens digit at location I+1,\n // and the ones digit at location I+2.\n let mut x = Chip8Computer::new();\n\n // load the value 123 (0x7b)\n x.registers.poke(0x1, 0x7b);\n x.registers.poke_i(0x500);\n Chip8CpuInstructions::BCD(0x1).execute(\u0026mut x);\n assert_eq!(x.memory.peek(0x500), 0x1);\n assert_eq!(x.memory.peek(0x501), 0x2);\n assert_eq!(x.memory.peek(0x502), 0x3);\n\n // Store registers V0 through Vx in memory starting at location I.\n //\n // The interpreter copies the values of registers V0 through Vx into memory,\n // starting at the address in I.\n let mut x = Chip8Computer::new();\n\n // Load Registers.\n let to_load = [0xab, 0xba, 0xca, 0xca, 0xbe, 0xef];\n for (idx, val) in to_load.iter().enumerate() {\n x.registers.poke(idx as u8, *val);\n }\n x.registers.poke_i(0x500);\n\n Chip8CpuInstructions::LDIX(to_load.len() as u8).execute(\u0026mut x);\n\n // Verify the values are in memory from 0x500 to 0x507\n for (idx, value) in to_load.iter().enumerate() {\n assert_eq!(x.memory.peek(0x500 + idx as u16), *value);\n }\n\n // Read registers V0 through Vx from memory starting at location I.\n //\n // The interpreter reads values from memory starting at location I into registers V0 through Vx.\n\n let mut x = Chip8Computer::new();\n\n let base_offset = 0x500;\n let to_load = [0xab, 0xba, 0xca, 0xca, 0xbe, 0xef];\n\n // start by setting values in memory\n for (idx, memory) in to_load.iter().enumerate() {\n let target_address = base_offset + idx;\n let target_value = *memory;\n x.memory.poke(target_address as u16, target_value);\n }\n // where to load from\n x.registers.poke_i(0x500);\n // how much to load\n x.registers.poke(0x6, to_load.len() as u8);\n\n // then copying them values memory to registers\n Chip8CpuInstructions::LDRI(0x6).execute(\u0026mut x);\n\n // now check that we have the right values in our registers\n for (idx, value) in to_load.iter().enumerate() {\n assert_eq!(x.registers.peek(idx as u8), *value);\n }\n\n // ExA1 - SKNP Vx\n // Skip next instruction if key with the value of Vx is not pressed.\n //\n // Checks the keyboard,\n // and if the key corresponding to the value of Vx is currently in the up position,\n // PC is increased by 2.\n let mut x = Chip8Computer::new();\n x.keypad.push_key(0x5);\n x.registers.poke(0x1, 0x5);\n Chip8CpuInstructions::SKNP(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n x.keypad.release_key(0x5);\n Chip8CpuInstructions::SKNP(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x206);\n\n // Ex9E - SKP Vx\n // Skip next instruction if key with the value of Vx is pressed.\n //\n // Checks the keyboard, and if the key corresponding to the value of Vx is currently in the down position, PC is increased by 2.\n let mut x = Chip8Computer::new();\n x.keypad.push_key(0x5);\n x.registers.poke(0x1, 0x5);\n Chip8CpuInstructions::SKP(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204);\n\n x.keypad.release_key(0x5);\n Chip8CpuInstructions::SKP(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x206);\n}\n\nfn draw_nibble_vx_vy_n_test_hd() {\n let mut x = Chip8Computer::new();\n\n let x_register = 0x01;\n let x_offset = 0x03;\n let y_register = 0x02;\n let y_offset = 0x04;\n let char_offset = 0x100;\n x.registers.poke(x_register, x_offset);\n x.registers.poke(y_register, y_offset);\n x.video_memory.set_highres();\n x.registers.poke_i(char_offset);\n Chip8CpuInstructions::DRW(x_register, y_register, 0).execute(\u0026mut x);\n\n println!(\"[[{}]]\", x.video_memory.format_as_string());\n\n assert_eq!(read_test_result(\"\"), x.video_memory.format_as_string());\n}\n\n#[test]\nfn draw_nibble_vx_vy_n_test_sd() {\n let mut x = Chip8Computer::new();\n let x_register = 0x1;\n let y_register = 0x2;\n let x_offset = 1;\n let y_offset = 2;\n let char_offset = 0x0A;\n\n // now lets set the X and Y to 1,2\n x.registers.poke(x_register, x_offset);\n x.registers.poke(y_register, y_offset);\n x.registers.poke_i(char_offset);\n // we are using 5 rows.\n Chip8CpuInstructions::DRW(x_register, y_register, 5).execute(\u0026mut x);\n\n // now check that video memory has the values at\n // 1,2-\u003e1,9\n // 2,2-\u003e2,9\n // 3,2-\u003e3,9\n // 4,2-\u003e4,9\n // 5,2-\u003e5,9\n // let byte_to_check = CHIP8FONT_0[0];\n for row_in_sprite in 0..5 {\n let row_data = CHIP8FONT_2[row_in_sprite];\n for bit_in_byte in 0..8 {\n let data_offset =\n (x_offset as u16 + row_in_sprite as u16) * 64 + (bit_in_byte + y_offset) as u16;\n let real_bit_in_byte = 7 - bit_in_byte;\n let shifted_one = 0x01 \u003c\u003c real_bit_in_byte;\n let one_shift_set = (shifted_one \u0026 row_data) \u003e 0;\n debug!(\"ROWDATA = \\t\\t[{row_data:08b}]\\tBIT IN BYTE = \\t[{bit_in_byte}]\\tONE_SHIFT_SET = [{one_shift_set}]\\tSHIFTED ONE = [{shifted_one:08b}]\");\n debug!(\"DATA_OFFSET FOR SOURCE DATA {}x{} is {} / offset by {}x{} and should be {} working with byte {:08b}\",\n bit_in_byte, row_in_sprite, data_offset, x_offset, y_offset, one_shift_set, row_data);\n }\n }\n}\n\n#[test]\nfn sub_test() {\n // 2nnn\n // Call a subroutine at 2nnn\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::CALL(0x124).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x124);\n assert_eq!(x.stack.depth(), 1);\n Chip8CpuInstructions::CALL(0x564).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x564);\n assert_eq!(x.stack.depth(), 2);\n\n // SETUP\n // Return from a subroutine.\n let mut x = Chip8Computer::new();\n x.stack.push(\u00260x132);\n x.stack.push(\u00260xabc);\n // EXECUTE\n Chip8CpuInstructions::RET.execute(\u0026mut x);\n // VERIFY\n assert_eq!(x.registers.peek_pc(), 0xabc);\n assert_eq!(x.stack.depth(), 1);\n // EXECUTE\n Chip8CpuInstructions::RET.execute(\u0026mut x);\n // VERIFY\n assert_eq!(x.registers.peek_pc(), 0x132);\n assert_eq!(x.stack.depth(), 0);\n}\n\n#[test]\nfn ldvxk_test() {\n // SETUP\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x01);\n Chip8CpuInstructions::LDRK(0x1).execute(\u0026mut x);\n assert!(matches!(\n x.state,\n gemma::chip8::cpu_states::Chip8CpuStates::WaitingForKey\n ));\n}\n\n#[test]\nfn series8xy4_corex_tests() {\n /// 8xy4\n /// Set Vx = Vx + Vy\n /// Set VF=1 if Carry\n ///\n // 1 + 1\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x01);\n x.registers.poke(0x02, 0x01);\n Chip8CpuInstructions::ADDR(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0x02);\n assert_eq!(x.registers.peek(0x0f), 0x00);\n\n // 255+1\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0xff);\n x.registers.poke(0x02, 0x01);\n Chip8CpuInstructions::ADDR(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0x00);\n assert_eq!(x.registers.peek(0x0f), 0x01);\n\n // 128+192\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 128);\n x.registers.poke(0x02, 192);\n Chip8CpuInstructions::ADDR(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 64);\n assert_eq!(x.registers.peek(0x0f), 1);\n\n // 8xy6 - SHR Vx {, Vy}\n // Set Vx = Vx SHR 1.\n //\n // If the least-significant bit of Vx is 1, then VF is set to 1,\n // otherwise 0. Then Vx is divided by 2.\n let mut x = Chip8Computer::new();\n // 0b10101010 -\u003e 0b01010101\n x.registers.poke(0x01, 0b10101010);\n x.registers.poke(0x0f, 0x0);\n\n Chip8CpuInstructions::SHL(0x01, 0x00).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0b01010100);\n assert_eq!(x.registers.peek(0x0f), 1);\n\n Chip8CpuInstructions::SHL(0x01, 0x00).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0b10101000);\n assert_eq!(x.registers.peek(0x0f), 0x00);\n\n Chip8CpuInstructions::SHL(0x01, 0x00).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0b01010000);\n assert_eq!(x.registers.peek(0x0f), 0x01);\n\n Chip8CpuInstructions::SHL(0x01, 0x00).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0b10100000);\n assert_eq!(x.registers.peek(0x0f), 0x00);\n\n Chip8CpuInstructions::SHL(0x01, 0x00).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0b01000000);\n assert_eq!(x.registers.peek(0x0f), 0x01);\n}\n\n#[test]\nfn random_produces_different_numbers() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x00);\n let first_number = Chip8CpuInstructions::RND(0x01, 0xff)\n .execute(\u0026mut x)\n .registers\n .peek(0x01);\n let second_number = Chip8CpuInstructions::RND(0x01, 0xff)\n .execute(\u0026mut x)\n .registers\n .peek(0x01);\n assert_ne!(first_number, second_number);\n}\n\n#[test]\nfn delay_timer_ticks_reduce_time() {\n let mut st = DelayTimer::new();\n st.set_timer(100);\n st.tick();\n st.tick();\n st.tick();\n assert_eq!(st.current(), 97);\n}\n\n#[test]\nfn delay_timer_out_of_ticks_works() {\n let mut st = DelayTimer::new();\n st.set_timer(0);\n st.tick();\n st.tick();\n st.tick();\n assert_eq!(st.current(), 0);\n}\n\n#[test]\nfn keypad_keys_check() {\n let mut k = Keypad::new();\n\n for i in 0..16 {\n assert!(!k.key_state(i));\n }\n\n // press a key\n k.push_key(1);\n k.push_key(2);\n assert!(k.pressed(1));\n assert!(k.pressed(2));\n k.release_key(1);\n assert!(k.released(1));\n}\n\n#[test]\nfn keypad_string_format_test() {\n let k = Keypad::new();\n\n assert_eq!(\n k.format_as_string(),\n read_test_result(\"gemma_keypad_string_result.asc\")\n );\n}\n\n#[test]\nfn register_rw_test() {\n let mut x = Chip8Registers::default();\n x.poke(0x0, 0xff);\n x.poke(0x1, 0xab);\n assert_eq!(x.peek(0x0), 0xff);\n assert_eq!(x.peek(0x1), 0xab);\n}\n\n#[test]\nfn pc_test() {\n let mut x = Chip8Registers::default();\n x.set_pc(0x300);\n assert_eq!(x.peek_pc(), 0x300);\n}\n\n#[test]\n#[should_panic]\nfn invalid_register() {\n let mut x = Chip8Registers::default();\n x.poke(0x10, 0xff);\n}\n\n#[test]\nfn format_as_string_looks_right() {\n let mut x = Chip8Registers::default();\n for i in 0..0x10 {\n x.registers[i] = i as u8;\n }\n x.pc = 0xabc;\n x.i_register = 0xcab;\n let result_string = x.format_as_string();\n assert_eq!(result_string, String::from(\"Vx: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07\\n 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f\\nI: 0x0cab\\tPC: 0x0abc\"));\n}\n\n#[test]\nfn reset_clears_registers() {\n let mut x = Chip8Registers::default();\n\n for register in 0..0x10 {\n x.poke(register, random::\u003cu8\u003e());\n }\n x.reset();\n for register in 0..0x10 {\n assert_eq!(x.peek(register), 0x00);\n }\n}\n\n#[test]\nfn sound_timer_ticks_reduce_time() {\n let mut st = SoundTimer::new();\n st.set_timer(100);\n st.tick();\n st.tick();\n st.tick();\n assert_eq!(st.current(), 97);\n}\n\n#[test]\nfn sound_timer_out_of_ticks_works() {\n let mut st = SoundTimer::new();\n st.set_timer(0);\n st.tick();\n st.tick();\n st.tick();\n assert_eq!(st.current(), 0);\n}\n\n#[test]\nfn stack_push_pop_test() {\n let mut x = Chip8Stack::new();\n\n // lets see if we can push and pop a bunch\n x.push(\u00260xabcu16);\n x.push(\u00260xcdeu16);\n x.pop();\n assert_eq!(x.depth(), 1);\n}\n\n#[test]\n#[should_panic]\nfn stack_overflow_test() {\n let mut x = Chip8Stack::new();\n for i in 0..17 {\n x.push(\u0026i);\n }\n}\n\n#[test]\n#[should_panic]\nfn stack_underflow_test() {\n let mut x = Chip8Stack::new();\n x.pop();\n}\n\n#[test]\nfn stack_lots_of_subs() {\n let mut x = Chip8Stack::new();\n let stack_contents = [\n 0x123, 0x321, 0xabc, 0xdef, 0xbad, 0xbef, 0xfed, 0xcab, 0xbed, 0xcad, 0xfeb, 0xcab, 0xfff,\n 0x000, 0x001,\n ];\n for i in stack_contents {\n x.push(\u0026i);\n }\n\n assert_eq!(x.depth(), 15);\n\n // up to 50 random loops\n let num_loops: u8 = random::\u003cu8\u003e() % 50;\n for i in 0..num_loops {\n let start_count = x.depth();\n let num_pop = random::\u003cu8\u003e() % x.depth() as u8;\n for current_pop in 0..num_pop {\n x.pop();\n }\n\n let post_pop_count = x.depth();\n assert_eq!(post_pop_count as u8, start_count as u8 - num_pop);\n for current_push in 0..num_pop {\n x.push(\u0026stack_contents[(current_push + post_pop_count as u8) as usize]);\n }\n assert_eq!(x.depth(), 15);\n }\n}\n\n#[test]\nfn video_split_bytes() {\n // from 0xABCD we should have AB high, CD low\n let (low, high) = InstructionUtil::split_bytes(0xabcd);\n assert_eq!(low, 0xAB);\n assert_eq!(high, 0xCD);\n}\n\n#[test]\nfn video_join_bytes() {\n // from 0xAB low and 0xCD high we get 0xABCD\n let merged = InstructionUtil::join_bytes(0xcd, 0xab);\n assert_eq!(merged, 0xcdab);\n}\n\n#[test]\nfn instruction_read_from_instruction() {\n // from 0xABCD\n let source = 0xABCD;\n assert_eq!(InstructionUtil::read_addr_from_instruction(source), 0xBCD);\n assert_eq!(InstructionUtil::read_nibble_from_instruction(source), 0xD);\n assert_eq!(InstructionUtil::read_x_from_instruction(source), 0xB);\n assert_eq!(InstructionUtil::read_y_from_instruction(source), 0xC);\n assert_eq!(InstructionUtil::read_byte_from_instruction(source), 0xCD);\n}\n\n#[test]\nfn instruction_ubln() {\n // from 0xABCD we should see B\n assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0xABCD), 0xB);\n assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0x0123), 0x1);\n assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0x0000), 0x0);\n}\n\n#[test]\nfn instruction_byte_to_bool_changes() {\n assert_eq!(\n InstructionUtil::byte_to_bools(0b00000000),\n [false, false, false, false, false, false, false, false]\n );\n assert_eq!(\n InstructionUtil::byte_to_bools(0b11111111),\n [true, true, true, true, true, true, true, true]\n );\n assert_eq!(\n InstructionUtil::byte_to_bools(0b11001100),\n [false, false, true, true, false, false, true, true]\n );\n assert_eq!(\n InstructionUtil::byte_to_bools(0b11110000),\n [false, false, false, false, true, true, true, true]\n );\n assert_eq!(\n InstructionUtil::bools_to_byte([false, false, false, false, false, false, false, false]),\n 0b00000000\n );\n assert_eq!(\n InstructionUtil::bools_to_byte([true, true, true, true, true, true, true, true]),\n 0b11111111\n );\n assert_eq!(\n InstructionUtil::bools_to_byte([false, false, true, true, false, false, true, true]),\n 0b11001100\n );\n assert_eq!(\n InstructionUtil::bools_to_byte([false, false, false, false, true, true, true, true]),\n 0b11110000\n );\n}\n\nfn real_build_checkboard(in_hd: bool) -\u003e Chip8Video {\n let mut r = Chip8Video::default();\n let (width, height) = if in_hd {\n r.set_highres();\n (SCHIP_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT)\n } else {\n (CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT)\n };\n\n println!(\"BUILDING BOARD WITH SIZE OF {width}x{height}\");\n\n for row in 0..height {\n let data_offset = row * width;\n\n for col in 0..width {\n // XOR row and column indices to alternate in a checkerboard pattern\n let to_poke = (row % 2) ^ (col % 2) == 1;\n let local_offset: u16 = (data_offset + col) as u16;\n\n r.poke(local_offset, to_poke);\n }\n }\n r\n}\n\nfn build_checkboard_hd() -\u003e Chip8Video {\n real_build_checkboard(true)\n}\n\nfn build_checkerboard() -\u003e Chip8Video {\n real_build_checkboard(false)\n}\n\n#[test]\nfn video_default_test() {\n let mut x = Chip8Video::default();\n\n for i in 0..CHIP8_VIDEO_MEMORY {\n assert!(!x.clone().peek(i as u16));\n // then flip the value and test again.\n x.poke(i as u16, true);\n assert!(x.clone().peek(i as u16));\n }\n}\n\n#[test]\nfn video_set_initial_memory_sd() {\n let mut x = Chip8Video::default();\n // let mut initial_memory = [false; CHIP8_VIDEO_MEMORY];\n let mut ws = String::new();\n // set our checkerboard\n for cbr in 0..32 {\n for cbc in 0..64 {\n let dof = cbr * 64 + cbc;\n if (dof as i32 % 2) == 0 {\n x.poke(dof, true);\n ws += \"*\";\n } else {\n ws += \" \";\n }\n }\n ws += \"\\n\";\n }\n assert_eq!(x.format_as_string(), ws);\n}\n\n#[test]\nfn video_poke_byte_test() {\n let to_poke = 0b11001111;\n let mut x = Chip8Video::default();\n x.poke_byte(0x05, to_poke);\n let mut expected = String::new();\n expected = \" ** **** \\n\".to_string();\n for i in 0..31 {\n expected += \u0026*(\" \".repeat(64) + \"\\n\");\n }\n assert_eq!(x.format_as_string(), expected);\n}\n\n#[test]\nfn video_poke_2byte_test() {\n let to_poke: [u8; 2] = [0b11001111, 0b00111100];\n\n let mut x = Chip8Video::default();\n x.poke_2byte(0x00, to_poke);\n\n let mut expected = String::new();\n expected = \"** **** **** \".to_string() + \u0026*\" \".repeat(64 - 16).to_string() + \"\\n\";\n for i in 0..31 {\n expected += \u0026*((*\" \".repeat(64)).to_string() + \"\\n\");\n }\n\n assert_eq!(expected, x.format_as_string());\n}\n\n#[test]\nfn video_poke_multirow_2_byte_sprite() {\n // take 2 rows of 16bits and write them to memory\n}\n\n#[test]\nfn video_cls_stddef() {\n let width = 64;\n let height = 32;\n let initial_memory = vec![];\n let mut ws = String::new();\n let mut set_x = Chip8Video::new(initial_memory.into());\n for cbr in 0..32 {\n ws += \u0026*\" \".repeat(width);\n ws += \"\\n\";\n }\n set_x.cls();\n\n assert_eq!(set_x.format_as_string(), ws);\n}\n\n#[test]\nfn video_poke_byte_test_2() {\n let to_poke = 0b10101010;\n let mut v = Chip8Video::default();\n v.poke_byte(0x00, to_poke);\n assert!(v.clone().peek(0x00));\n assert!(!v.clone().peek(0x01));\n assert!(v.clone().peek(0x02));\n assert!(!v.clone().peek(0x03));\n assert!(v.clone().peek(0x04));\n assert!(!v.clone().peek(0x05));\n assert!(v.clone().peek(0x06));\n assert!(!v.clone().peek(0x07));\n for i in 0x8..CHIP8_VIDEO_MEMORY {\n assert!(!v.clone().peek(i as u16));\n }\n}\n\n#[test]\nfn video_poke_multi_line_test() {\n let mut v = Chip8Video::default();\n let to_poke = [0b00000000, 0b11111111, 0b10101010, 0b01010101];\n\n for (byte_in_set, byte_to_poke) in to_poke.iter().enumerate() {\n let base_offset = byte_in_set * 64;\n v.poke_byte(base_offset as u16, *byte_to_poke);\n }\n\n // row 2 column 1\n {\n assert!(v.clone().peek(0x40));\n assert!(v.clone().peek(0x41));\n assert!(v.clone().peek(0x42));\n assert!(v.clone().peek(0x43));\n assert!(v.clone().peek(0x44));\n assert!(v.clone().peek(0x45));\n assert!(v.clone().peek(0x46));\n assert!(v.clone().peek(0x47));\n\n // row 3 column 1\n assert!(!v.clone().peek(0xC0));\n assert!(v.clone().peek(0xC1));\n assert!(!v.clone().peek(0xC2));\n assert!(v.clone().peek(0xC3));\n assert!(!v.clone().peek(0xC4));\n assert!(v.clone().peek(0xC5));\n assert!(!v.clone().peek(0xC6));\n assert!(v.clone().peek(0xC7));\n }\n}\n\n#[test]\nfn video_moved_poke_test() {\n let mut v = Chip8Video::default();\n let to_poke = [0b00000000, 0b11111111, 0b10101010, 0b01010101];\n\n let x_offset = 20;\n let y_offset = 5;\n\n for (byte_in_set, byte_to_poke) in to_poke.iter().enumerate() {\n let base_offset = (x_offset + byte_in_set) * 64 + y_offset;\n v.poke_byte(base_offset as u16, *byte_to_poke);\n }\n\n let test_offset = (x_offset * 64 + y_offset) as u16;\n assert!(!v.clone().peek(test_offset));\n assert!(!v.clone().peek(test_offset + 1));\n assert!(!v.clone().peek(test_offset + 2));\n assert!(!v.clone().peek(test_offset + 3));\n assert!(!v.clone().peek(test_offset + 4));\n assert!(!v.clone().peek(test_offset + 5));\n assert!(!v.clone().peek(test_offset + 6));\n assert!(!v.clone().peek(test_offset + 7));\n\n let test_offset = test_offset + 0x40;\n assert!(v.clone().peek(test_offset));\n assert!(v.clone().peek(test_offset + 1));\n assert!(v.clone().peek(test_offset + 2));\n assert!(v.clone().peek(test_offset + 3));\n assert!(v.clone().peek(test_offset + 4));\n assert!(v.clone().peek(test_offset + 5));\n assert!(v.clone().peek(test_offset + 6));\n assert!(v.clone().peek(test_offset + 7));\n}\n\n#[test]\nfn video_verify_change_registered() {\n let mut v = Chip8Video::default();\n v.poke(0x01, true);\n v.poke(0x01, true);\n assert!(v.has_frame_changed);\n\n v.start_frame();\n assert!(!v.has_frame_changed);\n}\n\n#[test]\nfn video_write_checkboard() {\n let v = build_checkerboard();\n assert_eq!(\n v.clone().format_as_string(),\n read_test_result(\"test_video_write_checkerboard.asc\")\n );\n}\n\n#[test]\nfn video_zero_test() {\n let mut x = Chip8Video::default();\n\n for (byte_index, data_offset) in (0..=0x100).step_by(0x40).enumerate() {\n x.poke_byte(data_offset as u16, CHIP8FONT_0[byte_index]);\n }\n\n assert_eq!(\n read_test_result(\"test_video_zero.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_multi_sprite_test() {\n let mut x = Chip8Video::default();\n // draw a row of digits 01234567\n let to_draw = [\n CHIP8FONT_0,\n CHIP8FONT_1,\n CHIP8FONT_2,\n CHIP8FONT_3,\n CHIP8FONT_4,\n CHIP8FONT_5,\n CHIP8FONT_6,\n CHIP8FONT_7,\n ];\n for (index, sprite) in to_draw.iter().enumerate() {\n let data_base_offset = index * 0x8;\n for (index, offset) in (0..=0x100).step_by(0x40).enumerate() {\n x.poke_byte((data_base_offset + offset) as u16, sprite[index]);\n }\n }\n\n assert_eq!(\n read_test_result(\"test_multi_sprite.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_reset_test() {\n let mut x = build_checkerboard();\n x.reset();\n assert_eq!(\n x.format_as_string(),\n read_test_result(\"test_reset_clears_video.asc\")\n );\n}\n\n#[test]\nfn video_collision_test() {\n // Setup: Set 0xFF to 0x00 with a new frame ready\n // Action: Run Poke to the same area\n // Test: Verify the 'changed' flag is tripped\n let mut x = Chip8Video::default();\n x.poke_byte(0x00, 0xff);\n x.tick();\n // set the cell thats already set...\n x.poke(0x00, true);\n // it becomes unset and theres a frame changed\n assert!(!x.peek(0x00));\n\n assert!(x.clone().has_frame_changed);\n}\n\n#[test]\nfn video_collision_test2() {\n let mut x = Chip8Video::default();\n x.poke_byte(0x00, 0b11110000);\n assert!(x.has_frame_changed);\n x.tick();\n assert!(!x.has_frame_changed);\n // clear the 'has changed' flag\n\n // now set a no-collision value\n x.poke_byte(0x00, 0b00001111);\n assert!(x.has_frame_changed);\n}\n\n#[test]\nfn video_collision_test3() {\n // draw a couple sprites that do not overlap.\n // goal being drawing without triggering the collision\n // detection.\n let mut x = Chip8Video::default();\n x.poke_byte(0x00, 0b11110000);\n}\n\n#[test]\nfn video_peek_out_of_bounds_doesnt_panic() {\n let x = Chip8Video::default();\n\n let y = x.clone().peek(2049);\n let y = x.clone().peek(0);\n\n // if we got here we didn't panic\n assert!(true);\n}\n\n#[test]\nfn video_scroll_down_1_row_test() {\n let mut x = build_checkerboard();\n x.scroll_down(1);\n assert_eq!(\n read_test_result(\"test_video_scroll_down_1.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_down_10_row_test() {\n let mut x = build_checkerboard();\n x.scroll_down(10);\n assert_eq!(\n read_test_result(\"test_video_scroll_down_10.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_high_res_has_right_resolution() {\n let x = build_checkboard_hd();\n println!(\"[{}]\", x.format_as_string());\n assert_eq!(\n read_test_result(\"test_video_highdef.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_down_1_row_test_schip() {\n let mut x = build_checkboard_hd();\n x.scroll_down(1);\n\n println!(\"[{}]\", x.format_as_string());\n println!(\"[{}]\", read_test_result(\"test_scroll_down_1_hd.asc\"));\n\n assert_eq!(\n read_test_result(\"test_scroll_down_1_hd.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_down_10_row_test_schip() {\n let mut x = build_checkboard_hd();\n x.scroll_down(10);\n assert_eq!(\n read_test_result(\"test_scroll_down_10_hd.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_left_4_row_test_std_def() {\n let mut x = build_checkerboard();\n x.scroll_left();\n assert_eq!(\n read_test_result(\"test_scroll_left_4.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_left_4_row_test_high_def() {\n let mut x = build_checkboard_hd();\n x.scroll_left();\n assert_eq!(\n read_test_result(\"test_scroll_left_4_hd.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_right_4_row_test_std_def() {\n let mut x = build_checkerboard();\n x.scroll_right();\n assert_eq!(\n read_test_result(\"test_scroll_right_4.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_right_4_row_test_high_def() {\n let mut x = build_checkboard_hd();\n x.scroll_right();\n assert_eq!(\n read_test_result(\"test_scroll_right_4_hd.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn instructions_operands_tests() {\n assert_eq!(Chip8CpuInstructions::SYS(0x000).operands(), \"0x0000\");\n assert_eq!(Chip8CpuInstructions::JPI(0x123).operands(), \"0x0123\");\n assert_eq!(Chip8CpuInstructions::JPA(0x234).operands(), \"0x0234\");\n assert_eq!(Chip8CpuInstructions::LDIA(0x345).operands(), \"0x0345\");\n assert_eq!(Chip8CpuInstructions::CALL(0x456).operands(), \"0x0456\");\n}\n\n#[test]\nfn instruction_ena_dis_tests() {\n let mut x = Chip8Computer::new();\n for quirk in [SChipModern, XOChip] {\n x.quirk_mode = quirk;\n assert!(!x.video_memory.is_highres());\n Chip8CpuInstructions::HIGH.execute(\u0026mut x);\n assert!(x.video_memory.is_highres());\n Chip8CpuInstructions::HIGH.execute(\u0026mut x);\n assert!(x.video_memory.is_highres());\n Chip8CpuInstructions::LOW.execute(\u0026mut x);\n assert!(!x.video_memory.is_highres());\n }\n}\n\n#[test]\nfn instruction_test_scrolling_lowres() {\n for quirk in [SChipModern, XOChip] {\n let mut x = Chip8Computer::new();\n x.video_memory = build_checkerboard();\n x.quirk_mode = quirk.clone();\n Chip8CpuInstructions::SCR.execute(\u0026mut x);\n assert_eq!(\n read_test_result(\"test_scroll_right_4.asc\"),\n x.dump_video_to_string()\n );\n\n x = Chip8Computer::new();\n x.video_memory = build_checkerboard();\n x.quirk_mode = quirk.clone();\n Chip8CpuInstructions::SCL.execute(\u0026mut x);\n\n assert_eq!(\n read_test_result(\"test_scroll_left_4.asc\"),\n x.dump_video_to_string()\n );\n\n x = Chip8Computer::new();\n x.video_memory = build_checkerboard();\n x.quirk_mode = quirk.clone();\n Chip8CpuInstructions::SCD(0x01).execute(\u0026mut x);\n assert_eq!(\n read_test_result(\"test_video_scroll_down_1.asc\"),\n x.dump_video_to_string()\n );\n\n x = Chip8Computer::new();\n x.video_memory = build_checkerboard();\n x.quirk_mode = quirk.clone();\n Chip8CpuInstructions::SCD(0xA).execute(\u0026mut x);\n assert_eq!(\n read_test_result(\"test_video_scroll_down_10.asc\"),\n x.dump_video_to_string()\n );\n }\n}\n\n#[test]\nfn computer_dump_keypad_to_string() {\n let mut x = Chip8Computer::new();\n x.keypad.push_key(0x1);\n x.keypad.push_key(0x2);\n assert_eq!(\n read_test_result(\"test_keypad_to_string.asc\"),\n x.dump_keypad_to_string()\n );\n}\n\n#[test]\nfn computer_dump_registers_to_string() {\n let mut x = Chip8Computer::new();\n let values_to_set = [\n 0x0b, 0xad, 0xbe, 0xef, 0xca, 0xb0, 0x7a, 0xc0, 0xca, 0x70, 0xba, 0xdb, 0xed, 0x00, 0x00,\n 0x00,\n ];\n let expected_value = \"Vx: 0x0b 0xad 0xbe 0xef 0xca 0xb0 0x7a 0xc0\\n 0xca 0x70 0xba 0xdb 0xed 0x00 0x00 0x00\\nI: 0x0000\\tPC: 0x0200\";\n\n for i in 0..16 {\n x.registers.poke(i, values_to_set[i as usize]);\n }\n\n // now verify.\n assert_eq!(expected_value, x.dump_registers_to_string());\n}\n\n#[test]\nfn video_scroll_up_tests_sd() {\n let mut x = build_checkerboard();\n let distance = 1u8;\n x.scroll_up(\u0026distance);\n assert_eq!(\n read_test_result(\"test_video_scroll_up_test_sd.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_scroll_up_tests_hd() {\n let mut x = build_checkboard_hd();\n let distance = 1u8;\n x.scroll_up(\u0026distance);\n assert_eq!(\n read_test_result(\"test_video_scroll_up_test_hd.asc\"),\n x.format_as_string()\n );\n}\n\n#[test]\nfn video_resolution_changing() {\n let mut x = Chip8Video::default();\n\n x.set_highres();\n assert_eq!(x.get_resolution(), (SCHIP_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT));\n assert!(matches!(\n x.get_screen_resolution(),\n Chip8VideoModes::HighRes\n ));\n x.set_lowres();\n assert_eq!(x.get_resolution(), (CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT));\n assert!(matches!(x.get_screen_resolution(), Chip8VideoModes::LowRes));\n}\n\n#[test]\nfn delay_timer_default() {\n let t = DelayTimer::default();\n assert_eq!(t.current(), 0xff);\n}\n\n#[test]\nfn instruction_jpx() {\n // JPX -\u003e 0xBXnn\n // Jump to Xnn+Vx\n let mut x = Chip8Computer::new();\n // set X1 to 4...\n x.registers.poke(0x01, 0x04);\n // ...use (x1)+0x134\n let to_execute = JPX(0x01, 0x34);\n // expect to set PC to 0x834\n to_execute.execute(\u0026mut x);\n assert_eq!(x.registers.peek_i(), 0x834);\n}\n\n#[test]\nfn instruction_ldrd() {\n let mut x = Chip8Computer::new();\n x.state = WaitingForInstruction;\n x.delay_timer.set_timer(0x01);\n Chip8CpuInstructions::LDRD(0x01).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x01), 0x01);\n x.delay_timer.set_timer(0xff);\n Chip8CpuInstructions::LDRD(0x0).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x00), 0xff);\n x.step_system();\n x.step_system();\n x.step_system();\n x.step_system();\n Chip8CpuInstructions::LDRD(0x0).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x0), 0xfb);\n}\n\n#[test]\nfn video_hires_loop_check() {\n let max_address = SCHIP_VIDE_MEMORY;\n let x = build_checkboard_hd();\n x.peek((max_address + 1) as u16);\n // if we got here we didn't explode!\n assert!(true);\n}\n\n#[test]\nfn sound_timer_default() {\n let x = SoundTimer::default();\n assert_eq!(x.current(), 0);\n}\n\n#[test]\nfn system_memory_new() {\n let x = Chip8SystemMemory::new();\n assert!(true);\n}\n\n#[test]\nfn video_lowres_schip_draw_chip8_sprite() {\n let mut x = Chip8Computer::new();\n x.quirk_mode = QuirkMode::SChipModern;\n x.video_memory.set_lowres();\n // point at the 1 from chip8\n x.registers.poke_i(0x0005);\n // point to 1,2 for the drawing\n x.registers.poke(0x01, 0x01);\n x.registers.poke(0x02, 0x02);\n Chip8CpuInstructions::DRW(0x01, 0x01, 0x08).execute(\u0026mut x);\n let expected_state = read_test_result(\"state/video_lowres_schip_draw_chip8_sprite_result.json\");\n let actual_state = serde_json::to_string(\u0026x).unwrap();\n assert_eq!(expected_state, actual_state);\n}\n\n#[test]\nfn video_lowres_schip_draw_schip_sprite() {}\n#[test]\nfn video_highres_schip_draw_chip8_sprite() {}\n#[test]\nfn video_highres_schip_draw_schip_sprite() {}\n\n\n#[test]\nfn partial_eq_chip8computer() {\n let x = Chip8Computer::new();\n let y = Chip8Computer::new();\n assert_eq!(x, y)\n}\n\n#[test]\nfn quirk_mode_labels() {\n assert_eq!(format!(\"{}\", Chip8), LABEL_QUIRK_CHIP8);\n assert_eq!(format!(\"{}\", XOChip), LABEL_QUIRK_XOCHIP);\n assert_eq!(format!(\"{}\", SChipModern), LABEL_QUIRK_SCHIP);\n}\n\n#[test]\nfn system_memory_load_program() {\n let mut m = Chip8SystemMemory::new();\n let mut program_to_load = vec![];\n let file_to_load = format!(\"{}/2-ibm-logo.ch8\", TEST_ROM_ROOT);\n println!(\"Attempt to load {} from {}\", file_to_load, std::env::current_dir().unwrap().display());\n let mut file_to_load_from = File::open(file_to_load).expect(\"Unable to load sample rom\");\n file_to_load_from.read_to_end(\u0026mut program_to_load).expect(\"Unable to read sample rom\");\n m.load_program(program_to_load.clone().into());\n let expected_at_200 = program_to_load[0];\n assert_eq!(m.peek(0x200), expected_at_200);\n}\n\n#[test]\nfn start_stop_computer() {\n let mut computer = Chip8ComputerManager::new();\n\n assert_eq!(computer.core_should_run, false);\n computer.start();\n assert_eq!(computer.core_should_run, true);\n computer.step();\n assert_eq!(computer.core_should_run, true);\n computer.stop();\n assert_eq!(computer.core_should_run, false);\n computer.reset(Chip8);\n assert_eq!(computer.core_should_run, false);\n}\n\n#[test]\nfn state_default_matches() {\n let computer = Chip8Computer::default();\n let mut manager = Chip8ComputerManager::default();\n assert_eq!(computer, *manager.state());\n}\n\n#[test]\nfn keys_test_manager() {\n let mut manager = Chip8ComputerManager::default();\n\n for i in 0..16 {\n assert_eq!(manager.is_key_pressed(i), false);\n }\n\n // press key 5\n manager.press_key(5);\n assert_eq!(manager.is_key_pressed(5), true);\n manager.release_key(5);\n assert_eq!(manager.is_key_pressed(5), false);\n}\n\n","traces":[{"line":24,"address":[524173,523616],"length":1,"stats":{"Line":1}},{"line":25,"address":[523643],"length":1,"stats":{"Line":1}},{"line":26,"address":[523789],"length":1,"stats":{"Line":1}},{"line":27,"address":[523883],"length":1,"stats":{"Line":1}},{"line":485,"address":[524208,524978],"length":1,"stats":{"Line":0}},{"line":486,"address":[524241],"length":1,"stats":{"Line":0}},{"line":488,"address":[524255],"length":1,"stats":{"Line":0}},{"line":489,"address":[524215],"length":1,"stats":{"Line":0}},{"line":490,"address":[524263],"length":1,"stats":{"Line":0}},{"line":491,"address":[524223],"length":1,"stats":{"Line":0}},{"line":492,"address":[524231],"length":1,"stats":{"Line":0}},{"line":493,"address":[524271],"length":1,"stats":{"Line":0}},{"line":494,"address":[524338],"length":1,"stats":{"Line":0}},{"line":495,"address":[524364],"length":1,"stats":{"Line":0}},{"line":496,"address":[524380],"length":1,"stats":{"Line":0}},{"line":497,"address":[524401],"length":1,"stats":{"Line":0}},{"line":499,"address":[524484],"length":1,"stats":{"Line":0}},{"line":501,"address":[524684],"length":1,"stats":{"Line":0}},{"line":902,"address":[525880,525008],"length":1,"stats":{"Line":1}},{"line":903,"address":[525041],"length":1,"stats":{"Line":1}},{"line":904,"address":[525191,525076,525094,525056],"length":1,"stats":{"Line":5}},{"line":905,"address":[525078],"length":1,"stats":{"Line":1}},{"line":906,"address":[525175],"length":1,"stats":{"Line":2}},{"line":908,"address":[525060],"length":1,"stats":{"Line":1}},{"line":911,"address":[525110,525193],"length":1,"stats":{"Line":2}},{"line":913,"address":[525302,525464],"length":1,"stats":{"Line":2}},{"line":914,"address":[525482,525532],"length":1,"stats":{"Line":1}},{"line":916,"address":[525560,525507],"length":1,"stats":{"Line":2}},{"line":918,"address":[525662,525792],"length":1,"stats":{"Line":1}},{"line":919,"address":[525821,525779,525854],"length":1,"stats":{"Line":2}},{"line":921,"address":[525832,525875],"length":1,"stats":{"Line":2}},{"line":924,"address":[525421],"length":1,"stats":{"Line":1}},{"line":927,"address":[525904],"length":1,"stats":{"Line":1}},{"line":928,"address":[525912],"length":1,"stats":{"Line":2}},{"line":931,"address":[525936],"length":1,"stats":{"Line":1}},{"line":932,"address":[525944],"length":1,"stats":{"Line":1}}],"covered":22,"coverable":36},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","tests","unit_tests_instructions.rs"],"content":"use gemma::chip8::instructions::Chip8CpuInstructions;\nuse gemma::chip8::instructions::Chip8CpuInstructions::*;\nuse gemma::chip8::quirk_modes::QuirkMode::{Chip8, SChipModern, XOChip};\nuse gemma::constants::*;\n\n#[derive(Clone)]\nstruct InstructionTestQuirks {\n chip8: Chip8CpuInstructions,\n schip: Chip8CpuInstructions,\n xochip: Chip8CpuInstructions,\n}\n\n#[derive(Clone)]\nstruct InstructionTest {\n name: String,\n instruction: Chip8CpuInstructions,\n operands: String,\n asm: String,\n encoded: u16,\n quirks: InstructionTestQuirks,\n}\n\n#[test]\nfn instructions_encode_decode_tests_with_quirks() {\n let it = vec![\n InstructionTest {\n name: INST_SYS.to_string(),\n instruction: SYS(0x123),\n operands: \"0x0123\".to_string(),\n asm: \"SYS 0x0123\".to_string(),\n encoded: 0x0123,\n quirks: InstructionTestQuirks {\n chip8: SYS(0x123),\n schip: XXXXERRORINSTRUCTION,\n xochip: XXXXERRORINSTRUCTION,\n },\n },\n InstructionTest {\n name: INST_CLS.to_string(),\n instruction: CLS,\n asm: \"CLS\".to_string(),\n operands: \"\".to_string(),\n encoded: 0x00E0,\n quirks: InstructionTestQuirks {\n chip8: CLS,\n schip: CLS,\n xochip: CLS,\n },\n },\n InstructionTest {\n name: INST_RET.to_string(),\n instruction: RET,\n asm: \"RET\".to_string(),\n operands: \"\".to_string(),\n encoded: 0x00ee,\n quirks: InstructionTestQuirks {\n chip8: RET,\n schip: RET,\n xochip: RET,\n },\n },\n InstructionTest {\n name: INST_JPX.to_string(),\n instruction: JPX(0x1, 0xab),\n operands: \"0x01, 0x01ab\".to_string(),\n asm: \"JPX 0x01, 0x01ab\".to_string(),\n encoded: 0xb1ab,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: JPX(0x1, 0xab),\n xochip: XXXXERRORINSTRUCTION,\n },\n },\n InstructionTest {\n name: INST_CALL.to_string(),\n instruction: CALL(0x123),\n asm: \"CALL 0x0123\".to_string(),\n encoded: 0x2123,\n operands: \"0x0123\".to_string(),\n quirks: InstructionTestQuirks {\n chip8: CALL(0x123),\n schip: CALL(0x123),\n xochip: CALL(0x123),\n },\n },\n InstructionTest {\n name: INST_DRW.to_string(),\n instruction: DRW(0x01, 0x02, 0x03),\n operands: \"0x01, 0x02, 0x03\".to_string(),\n asm: \"DRW 0x01, 0x02, 0x03\".to_string(),\n encoded: 0xd123,\n quirks: InstructionTestQuirks {\n chip8: DRW(0x1, 0x2, 0x3),\n schip: DRW(0x1, 0x2, 0x3),\n xochip: DRW(0x1, 0x2, 0x3),\n },\n },\n InstructionTest {\n name: INST_JPI.to_string(),\n instruction: JPI(0x321),\n operands: \"0x0321\".to_string(),\n asm: \"JPI 0x0321\".to_string(),\n encoded: 0xb321,\n quirks: InstructionTestQuirks {\n chip8: JPI(0x321),\n schip: XXXXERRORINSTRUCTION,\n xochip: JPI(0x321),\n },\n },\n InstructionTest {\n name: INST_SCD.to_string(),\n instruction: SCD(0x01),\n operands: \"0x01\".to_string(),\n asm: \"SCD 0x01\".to_string(),\n encoded: 0x00c1,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: SCD(0x1),\n xochip: SCD(0x1),\n },\n },\n InstructionTest {\n name: INST_SCR.to_string(),\n instruction: Chip8CpuInstructions::SCR,\n operands: \"\".to_string(),\n asm: \"SCR\".to_string(),\n encoded: 0x00FB,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: SCR,\n xochip: SCR,\n },\n },\n InstructionTest {\n name: INST_SCL.to_string(),\n instruction: SCL,\n operands: \"\".to_string(),\n asm: \"SCL\".to_string(),\n encoded: 0x00FC,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: SCL,\n xochip: SCL,\n },\n },\n InstructionTest {\n name: INST_EXIT.to_string(),\n instruction: EXIT,\n operands: \"\".to_string(),\n asm: \"EXIT\".to_string(),\n encoded: 0x00FD,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: EXIT,\n xochip: EXIT,\n },\n },\n InstructionTest {\n name: INST_LOW.to_string(),\n instruction: LOW,\n operands: \"\".to_string(),\n asm: \"LOW\".to_string(),\n encoded: 0x00FE,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: LOW,\n xochip: LOW,\n },\n },\n InstructionTest {\n name: INST_HIGH.to_string(),\n instruction: HIGH,\n operands: \"\".to_string(),\n asm: \"HIGH\".to_string(),\n encoded: 0x00FF,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: HIGH,\n xochip: HIGH,\n },\n },\n InstructionTest {\n name: INST_SEX.to_string(),\n instruction: Chip8CpuInstructions::SEX(0x01, 0xfa),\n operands: \"0x01, 0xfa\".to_string(),\n asm: \"SEX 0x01, 0xfa\".to_string(),\n encoded: 0x31fa,\n quirks: InstructionTestQuirks {\n chip8: Chip8CpuInstructions::SEX(0x1, 0xfa),\n schip: Chip8CpuInstructions::SEX(0x1, 0xfa),\n xochip: Chip8CpuInstructions::SEX(0x1, 0xfa),\n },\n },\n InstructionTest {\n name: INST_SNEB.to_string(),\n instruction: Chip8CpuInstructions::SNEB(0x01, 0xab),\n operands: \"0x01, 0xab\".to_string(),\n asm: \"SNEB 0x01, 0xab\".to_string(),\n encoded: 0x41ab,\n quirks: InstructionTestQuirks {\n chip8: SNEB(0x01, 0xab),\n schip: SNEB(0x01, 0xab),\n xochip: SNEB(0x01, 0xab),\n },\n },\n InstructionTest {\n name: INST_SEY.to_string(),\n instruction: Chip8CpuInstructions::SEY(0x1, 0x2),\n operands: \"0x1, 0x2\".to_string(),\n asm: \"SEY 0x1, 0x2\".to_string(),\n encoded: 0x5120,\n quirks: InstructionTestQuirks {\n chip8: SEY(0x1, 0x2),\n schip: SEY(0x1, 0x2),\n xochip: SEY(0x1, 0x2),\n },\n },\n InstructionTest {\n name: INST_LDR.to_string(),\n instruction: LDR(0xa, 0xbe),\n operands: \"0x0a, 0xbe\".to_string(),\n asm: \"LDR 0x0a, 0xbe\".to_string(),\n encoded: 0x6abe,\n quirks: InstructionTestQuirks {\n chip8: LDR(0xa, 0xbe),\n schip: LDR(0xa, 0xbe),\n xochip: LDR(0xa, 0xbe),\n },\n },\n InstructionTest {\n name: INST_ADD.to_string(),\n instruction: Chip8CpuInstructions::ADD(0x01, 0xab),\n operands: \"0x01, 0xab\".to_string(),\n asm: \"ADD 0x01, 0xab\".to_string(),\n encoded: 0x71ab,\n quirks: InstructionTestQuirks {\n chip8: ADD(0x01, 0xab),\n schip: ADD(0x01, 0xab),\n xochip: ADD(0x01, 0xab),\n },\n },\n InstructionTest {\n name: INST_LDRK.to_string(),\n instruction: Chip8CpuInstructions::LDRK(0x6),\n operands: \"0x06\".to_string(),\n asm: \"LDRK 0x06\".to_string(),\n encoded: 0xF60A,\n quirks: InstructionTestQuirks {\n chip8: Chip8CpuInstructions::LDRK(0x6),\n schip: Chip8CpuInstructions::LDRK(0x6),\n xochip: Chip8CpuInstructions::LDRK(0x6),\n },\n },\n InstructionTest {\n name: INST_ADDI.to_string(),\n instruction: Chip8CpuInstructions::ADDI(0x02),\n operands: \"0x02\".to_string(),\n asm: \"ADDI 0x02\".to_string(),\n encoded: 0xF21E,\n quirks: InstructionTestQuirks {\n chip8: ADDI(0x02),\n schip: ADDI(0x02),\n xochip: ADDI(0x02),\n },\n },\n InstructionTest {\n name: INST_ADD.to_string(),\n instruction: Chip8CpuInstructions::ADD(0x02, 0x12),\n operands: \"0x02, 0x12\".to_string(),\n asm: \"ADD 0x02, 0x12\".to_string(),\n encoded: 0x7212,\n quirks: InstructionTestQuirks {\n chip8: ADD(0x02, 0x12),\n schip: ADD(0x02, 0x12),\n xochip: ADD(0x02, 0x12),\n },\n },\n InstructionTest {\n name: INST_SCU.to_string(),\n instruction: Chip8CpuInstructions::SCU(0x04),\n operands: \"0x04\".to_string(),\n asm: \"SCU 0x04\".to_string(),\n encoded: 0x00D4,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: XXXXERRORINSTRUCTION,\n xochip: SCU(0x04),\n },\n },\n InstructionTest {\n name: INST_ADDR.to_string(),\n instruction: ADDR(0x01, 0x02),\n operands: \"0x1, 0x2\".to_string(),\n asm: \"ADDR 0x1, 0x2\".to_string(),\n encoded: 0x8124,\n quirks: InstructionTestQuirks {\n chip8: ADDR(0x01, 0x02),\n schip: ADDR(0x01, 0x02),\n xochip: ADDR(0x01, 0x02),\n },\n },\n InstructionTest {\n name: INST_AND.to_string(),\n instruction: AND(0x1, 0x2),\n operands: \"0x1, 0x2\".to_string(),\n asm: \"AND 0x1, 0x2\".to_string(),\n encoded: 0x8122,\n quirks: InstructionTestQuirks {\n chip8: AND(0x1, 0x2),\n schip: AND(0x1, 0x2),\n xochip: AND(0x1, 0x2),\n },\n },\n InstructionTest {\n name: INST_JPA.to_string(),\n instruction: JPA(0x345),\n operands: \"0x0345\".to_string(),\n asm: \"JPA 0x0345\".to_string(),\n encoded: 0x1345,\n\n quirks: InstructionTestQuirks {\n chip8: JPA(0x345),\n schip: JPA(0x345),\n xochip: JPA(0x345),\n },\n },\n InstructionTest {\n name: INST_BCD.to_string(),\n instruction: BCD(0xc),\n operands: \"0x0c\".to_string(),\n asm: \"BCD 0x0c\".to_string(),\n encoded: 0xfc33,\n quirks: InstructionTestQuirks {\n chip8: BCD(0xcd),\n schip: BCD(0xcd),\n xochip: BCD(0xcd),\n },\n },\n InstructionTest {\n name: INST_LDD.to_string(),\n instruction: LDD(0xfc),\n operands: \"0xfc\".to_string(),\n asm: \"LDD 0xfc\".to_string(),\n encoded: 0xfc15,\n quirks: InstructionTestQuirks {\n chip8: LDD(0xfc),\n schip: LDD(0xfc),\n xochip: LDD(0xfc),\n },\n },\n InstructionTest {\n name: INST_ORY.to_string(),\n instruction: ORY(0x01, 0x3),\n operands: \"0x1, 0x3\".to_string(),\n asm: \"ORY 0x1, 0x3\".to_string(),\n encoded: 0x8133,\n quirks: InstructionTestQuirks {\n chip8: ORY(0x01, 0x3),\n schip: ORY(0x01, 0x3),\n xochip: ORY(0x01, 0x3),\n },\n },\n InstructionTest {\n name: INST_SUBC.to_string(),\n instruction: Chip8CpuInstructions::SUBC(0x1, 0x2),\n operands: \"0x1, 0x2\".to_string(),\n asm: \"SUBC 0x1, 0x2\".to_string(),\n encoded: 0x8127,\n quirks: InstructionTestQuirks {\n chip8: Chip8CpuInstructions::SUBC(0x1, 0x2),\n schip: Chip8CpuInstructions::SUBC(0x1, 0x2),\n xochip: Chip8CpuInstructions::SUBC(0x1, 0x2),\n },\n },\n InstructionTest {\n name: INST_SUB.to_string(),\n instruction: Chip8CpuInstructions::SUB(0x1, 0x2),\n operands: \"0x1, 0x2\".to_string(),\n asm: \"SUB 0x1, 0x2\".to_string(),\n encoded: 0x8125,\n quirks: InstructionTestQuirks {\n chip8: Chip8CpuInstructions::SUB(0x1, 0x2),\n schip: Chip8CpuInstructions::SUB(0x1, 0x2),\n xochip: Chip8CpuInstructions::SUB(0x1, 0x2),\n },\n },\n InstructionTest {\n name: INST_STR.to_string(),\n instruction: Chip8CpuInstructions::STR(0x06),\n operands: \"0x06\".to_string(),\n asm: \"STR 0x06\".to_string(),\n encoded: 0xf675,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: Chip8CpuInstructions::STR(0x06),\n xochip: Chip8CpuInstructions::STR(0x06),\n },\n },\n InstructionTest {\n name: INST_OR.to_string(),\n instruction: OR(0x01, 0x02),\n operands: \"0x1, 0x2\".to_string(),\n asm: \"OR 0x1, 0x2\".to_string(),\n encoded: 0x8121,\n quirks: InstructionTestQuirks {\n chip8: OR(0x01, 0x02),\n schip: OR(0x01, 0x02),\n xochip: OR(0x01, 0x02),\n },\n },\n InstructionTest {\n name: INST_SHR.to_string(),\n instruction: SHR(0x04, 0x4),\n operands: \"0x4, 0x4\".to_string(),\n asm: \"SHR 0x4, 0x4\".to_string(),\n encoded: 0x8446,\n quirks: InstructionTestQuirks {\n chip8: SHR(0x04, 0x4),\n schip: SHR(0x04, 0x4),\n xochip: SHR(0x04, 0x4),\n },\n },\n InstructionTest {\n name: INST_SHL.to_string(),\n instruction: SHL(0x04, 0x4),\n operands: \"0x4, 0x4\".to_string(),\n asm: \"SHL 0x4, 0x4\".to_string(),\n encoded: 0x844e,\n quirks: InstructionTestQuirks {\n chip8: SHL(0x04, 0x4),\n schip: SHL(0x04, 0x4),\n xochip: SHL(0x04, 0x4),\n },\n },\n InstructionTest {\n name: INST_RND.to_string(),\n instruction: RND(0x01, 0xff),\n operands: \"0x01, 0xff\".to_string(),\n asm: \"RND 0x01, 0xff\".to_string(),\n encoded: 0xc1ff,\n quirks: InstructionTestQuirks {\n chip8: RND(0x01, 0xff),\n schip: RND(0x01, 0xff),\n xochip: RND(0x01, 0xff),\n },\n },\n InstructionTest {\n name: INST_LDRY.to_string(),\n instruction: LDRY(0x01, 0x02),\n operands: \"0x1, 0x2\".to_string(),\n asm: \"LDRY 0x1, 0x2\".to_string(),\n encoded: 0x8120,\n quirks: InstructionTestQuirks {\n chip8: LDRY(0x01, 0x02),\n schip: LDRY(0x01, 0x02),\n xochip: LDRY(0x01, 0x02),\n },\n },\n InstructionTest {\n name: INST_LDIS.to_string(),\n instruction: LDIS(0x01),\n operands: \"0x01\".to_string(),\n asm: \"LDIS 0x01\".to_string(),\n encoded: 0xf118,\n quirks: InstructionTestQuirks {\n chip8: LDIS(0x01),\n schip: LDIS(0x01),\n xochip: LDIS(0x01),\n },\n },\n InstructionTest {\n name: INST_LIDR.to_string(),\n instruction: LIDR(0x01),\n operands: \"0x01\".to_string(),\n asm: \"LIDR 0x01\".to_string(),\n encoded: 0xf185,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: LIDR(0x01),\n xochip: LIDR(0x01),\n },\n },\n InstructionTest {\n name: INST_LDF2.to_string(),\n instruction: LDF2(0x01),\n operands: \"0x01\".to_string(),\n asm: \"LDF2 0x01\".to_string(),\n encoded: 0xf130,\n quirks: InstructionTestQuirks {\n chip8: XXXXERRORINSTRUCTION,\n schip: LDF2(0x01),\n xochip: LDF2(0x01),\n },\n },\n InstructionTest {\n name: INST_LDF.to_string(),\n instruction: LDFX(0x01),\n operands: \"0x01\".to_string(),\n asm: \"LDF 0x01\".to_string(),\n encoded: 0xf129,\n quirks: InstructionTestQuirks {\n chip8: LDFX(0x01),\n schip: LDFX(0x01),\n xochip: LDFX(0x01),\n },\n },\n InstructionTest {\n name: INST_LDIA.to_string(),\n instruction: LDIA(0x01),\n operands: \"0x0001\".to_string(),\n asm: \"LDIA 0x0001\".to_string(),\n encoded: 0xa001,\n quirks: InstructionTestQuirks {\n chip8: LDIA(0x01),\n schip: LDIA(0x01),\n xochip: LDIA(0x01),\n },\n },\n InstructionTest {\n name: INST_LDIX.to_string(),\n instruction: LDIX(0x01),\n operands: \"0x01\".to_string(),\n asm: \"LDIX 0x01\".to_string(),\n encoded: 0xf155,\n quirks: InstructionTestQuirks {\n chip8: LDIX(0x01),\n schip: LDIX(0x01),\n xochip: LDIX(0x01),\n },\n },\n InstructionTest {\n name: INST_LDRD.to_string(),\n instruction: Chip8CpuInstructions::LDRD(0x01),\n operands: \"0x01\".to_string(),\n asm: \"LDRD 0x01\".to_string(),\n encoded: 0xf107,\n quirks: InstructionTestQuirks {\n chip8: Chip8CpuInstructions::LDRD(0x01),\n schip: Chip8CpuInstructions::LDRD(0x01),\n xochip: Chip8CpuInstructions::LDRD(0x01),\n },\n },\n InstructionTest {\n name: INST_LDRI.to_string(),\n instruction: Chip8CpuInstructions::LDRI(0x01),\n operands: \"0x01\".to_string(),\n asm: \"LDRI 0x01\".to_string(),\n encoded: 0xf165,\n quirks: InstructionTestQuirks {\n chip8: Chip8CpuInstructions::LDRI(0x01),\n schip: Chip8CpuInstructions::LDRI(0x01),\n xochip: Chip8CpuInstructions::LDRI(0x01),\n },\n },\n InstructionTest {\n name: INST_SKP.to_string(),\n instruction: SKP(0x01),\n operands: \"0x01\".to_string(),\n asm: \"SKP 0x01\".to_string(),\n encoded: 0xe19e,\n quirks: InstructionTestQuirks {\n chip8: SKP(0x01),\n schip: SKP(0x01),\n xochip: SKP(0x01),\n },\n },\n InstructionTest {\n name: INST_SNEY.to_string(),\n instruction: SNEY(0x01, 0x1),\n operands: \"0x1, 0x1\".to_string(),\n asm: \"SNEY 0x1, 0x1\".to_string(),\n encoded: 0x9110,\n quirks: InstructionTestQuirks {\n chip8: SNEY(0x01, 0x1),\n schip: SNEY(0x01, 0x1),\n xochip: SNEY(0x01, 0x1),\n },\n },\n InstructionTest {\n name: INST_SKNP.to_string(),\n instruction: SKNP(0x01),\n operands: \"0x01\".to_string(),\n asm: \"SKNP 0x01\".to_string(),\n encoded: 0xe1a1,\n quirks: InstructionTestQuirks {\n chip8: SKNP(0x01),\n schip: SKNP(0x01),\n xochip: SKNP(0x01),\n },\n },\n ];\n\n for current in it {\n let instruction = current.clone().instruction;\n let asm = Chip8CpuInstructions::from_str(\u0026current.asm);\n let as_chip8 = Chip8CpuInstructions::decode(current.encoded, \u0026Chip8);\n let quirks_chip8 = current.quirks.chip8;\n let as_schip = Chip8CpuInstructions::decode(current.encoded, \u0026SChipModern);\n let quirks_schip = current.quirks.schip;\n let as_xochip = Chip8CpuInstructions::decode(current.encoded, \u0026XOChip);\n let quirks_xochip = current.quirks.xochip;\n\n // ** CONVERSION **\n // -\u003e Integer to Instruction\n assert!(matches!(\n Chip8CpuInstructions::decode(current.encoded, \u0026Chip8),\n i\n ));\n // -\u003e Instruction to Integer\n println!(\n \"TESTING INSTRUCTION TO INTEGER FOR {:?} / {:04x} {:04x}\",\n current.instruction,\n current.encoded,\n instruction.encode()\n );\n assert_eq!(current.encoded, instruction.encode());\n // -\u003e Instruction to String\n assert_eq!(instruction.to_string(), current.asm);\n assert_eq!(instruction.operands(), current.operands);\n assert_eq!(current.instruction.name(), current.name);\n // -\u003e String to Instruction\n assert!(matches!(Chip8CpuInstructions::from_str(\u0026current.name), asm));\n // ** QUIRKS **\n assert!(matches!(as_chip8, quirks_chip8));\n assert!(matches!(as_schip, quirks_schip));\n assert!(matches!(as_xochip, quirks_xochip));\n }\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemma","tests","util_tests.rs"],"content":"use std::collections::BTreeMap;\nuse gemma::chip8::util::InstructionUtil;\n\n#[test]\nfn byte_to_bools() {\n let data_set: BTreeMap\u003cu8, [bool; 8]\u003e = BTreeMap::from(\n [\n (0x00, [false, false, false, false, false, false, false, false]),\n (0x0f, [true, true, true, true, false, false, false, false]),\n (0xaa, [false, true, false, true, false, true, false, true]),\n (0xf0, [false, false, false, false, true, true, true, true]),\n (0xff, [true, true, true, true, true, true, true, true]),\n ]\n );\n\n for (key, value) in data_set.iter() {\n let result_array = InstructionUtil::byte_to_bools(*key);\n for i in 0..8 {\n assert_eq!(result_array[i], value[i]);\n }\n }\n}\n\n\n#[test]\nfn bools_to_byte() {\n let data_set: BTreeMap\u003cu8, [bool; 8]\u003e = BTreeMap::from(\n [\n (0x00, [false, false, false, false, false, false, false, false]),\n (0x0f, [true, true, true, true, false, false, false, false]),\n (0xaa, [false, true, false, true, false, true, false, true]),\n (0xf0, [false, false, false, false, true, true, true, true]),\n (0xff, [true, true, true, true, true, true, true, true]),\n ]\n );\n\n for (key, value) in data_set.iter() {\n let result = InstructionUtil::bools_to_byte(*value);\n assert_eq!(result, *key);\n }\n}\n\n#[test]\nfn split_bytes() {\n let data_set: BTreeMap\u003cu16, (u8, u8)\u003e = BTreeMap::from(\n [\n (0xff00, (0xff, 0x00)),\n (0x00ff, (0x00, 0xff)),\n (0xabcd, (0xab, 0xcd)),\n (0xcdab, (0xcd, 0xab))\n ]\n );\n\n for (key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::split_bytes(*key), *value);\n }\n}\n\n#[test]\nfn join_bytes() {\n let data_set: BTreeMap\u003cu16, (u8, u8)\u003e = BTreeMap::from(\n [\n (0xff00, (0xff, 0x00)),\n (0x00ff, (0x00, 0xff)),\n (0xabcd, (0xab, 0xcd)),\n (0xcdab, (0xcd, 0xab))\n ]\n );\n\n for (key, (low, high )) in data_set.iter() {\n assert_eq!(InstructionUtil::join_bytes(*low, *high), *key);\n }\n}\n\n#[test]\nfn swap_endian() {\n let data_set: BTreeMap\u003cu16, u16\u003e = BTreeMap::from(\n [\n (0xabcd, 0xcdab),\n (0x00ff, 0xff00),\n (0xff00, 0x00ff),\n (0xffff, 0xffff),\n (0x0000, 0x0000)\n ]\n );\n\n for (key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::swap_endian(*key), *value);\n }\n}\n\n#[test]\nfn split_bytes_swap_endian() {\n let data_set: BTreeMap\u003cu16, (u8, u8)\u003e = BTreeMap::from([\n (0x0000, (0x00, 0x00)),\n (0xffff, (0xff, 0xff)),\n (0xff00, (0x00, 0xff)),\n (0x00ff, (0xff, 0x00)),\n (0xabcd, (0xcd, 0xab))\n ]);\n\n for (key, result) in data_set.iter() {\n assert_eq!(InstructionUtil::split_bytes_swap_endian(*key), *result)\n }\n}\n\n#[test]\nfn join_bytes_swap_endian() {\n let data_set: BTreeMap\u003cu16, (u8, u8)\u003e = BTreeMap::from([\n (0xffff, (0xff, 0xff)),\n (0x0000, (0x00, 0x00)),\n (0xff00, (0x00, 0xff)),\n (0x00ff, (0xff, 0x00)),\n (0xabcd, (0xcd, 0xab))\n ]);\n\n for (key, (low, high)) in data_set.iter() {\n assert_eq!(InstructionUtil::join_bytes_swap_endian(*low, *high), *key);\n }\n\n}\n\n#[test]\nfn read_address_from_instruction() {\n let data_set: BTreeMap\u003cu16, u16\u003e = BTreeMap::from([\n (0xabcd, 0x0bcd),\n (0xffff, 0x0fff),\n (0x0000, 0x0000),\n (0x8123, 0x0123)\n ]) ;\n\n for (key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::read_addr_from_instruction(*key), *value);\n }\n}\n\n#[test]\nfn read_nibble_from_instruction() {\n let data_set: BTreeMap\u003cu16, u8\u003e = BTreeMap::from([\n (0xffff, 0x0f),\n (0x0000, 0x00),\n (0xabcd, 0x0d),\n ]);\n\n for(key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::read_nibble_from_instruction(*key), *value);\n }\n}\n\n#[test]\nfn read_x_from_instruction() {\n let data_set: BTreeMap\u003cu16, u8\u003e = BTreeMap::from([\n (0xffff, 0x0f),\n (0x0000, 0x00),\n (0x0a00, 0x0a),\n (0x0b00, 0x0b)\n ]);\n\n for (key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::read_x_from_instruction(*key), *value);\n }\n}\n\n#[test]\nfn read_y_from_instruction() {\n let data_set: BTreeMap\u003cu16, u8\u003e = BTreeMap::from([\n (0xffff, 0x0f),\n (0x0000, 0x00),\n (0x00a0, 0x0a),\n (0x00b0, 0x0b)\n ]);\n\n for (key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::read_y_from_instruction(*key), *value);\n }\n}\n\n#[test]\nfn read_byte_from_instruction() {\n let data_set: BTreeMap\u003cu16, u8\u003e = BTreeMap::from([\n (0xffff, 0xff),\n (0x0000, 0x00),\n (0xabcd, 0xcd),\n (0xcdab, 0xab)\n ]);\n\n for (key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::read_byte_from_instruction(*key), *value);\n }\n}\n\n#[test]\nfn read_upper_byte_lower_nibble() {\n let data_set: BTreeMap\u003cu16, u8\u003e = BTreeMap::from([\n (0x0000, 0x00),\n (0xabcd, 0x0b),\n (0xffff, 0x0f),\n (0xcdab, 0x0d)\n ]);\n\n for (key, value) in data_set.iter() {\n assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(*key), *value);\n }\n}\n\n#[test]\nfn read_bits_from_instruction() {\n let data_set: BTreeMap\u003cu16, (u8, u8, u16)\u003e = BTreeMap::from([\n (0x0000, (0, 7, 0x00)),\n (0x0000, (8, 15, 0x00)),\n (0xffff, (0, 7, 0xff)),\n (0xffff, (8, 15, 0xff)),\n ]);\n\n for (key, (start, end, value)) in data_set.iter() {\n let result = InstructionUtil::read_bits_from_instruction(*key, *start, *end);\n assert_eq!(result, *value);\n }\n}","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaegui","src","bin","gemmaegui.rs"],"content":"use crate::support::gemma_egui_support::{EGuiFileList, GemmaEguiSupport};\nuse eframe::egui;\nuse egui::Key;\nuse gemma::chip8::computer::Chip8Computer;\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\nuse std::path::PathBuf;\nuse std::time::Instant;\n\nmod support;\n\nconst LIN_KEYS: [[(Key, u8); 4]; 4] = [\n [\n (Key::Num1, 0x01),\n (Key::Num2, 0x02),\n (Key::Num3, 0x03),\n (Key::Num4, 0x0c),\n ],\n [\n (Key::Q, 0x04),\n (Key::W, 0x05),\n (Key::E, 0x06),\n (Key::R, 0x0d),\n ],\n [\n (Key::A, 0x07),\n (Key::S, 0x08),\n (Key::D, 0x09),\n (Key::F, 0x0e),\n ],\n [\n (Key::Z, 0x0a),\n (Key::X, 0x00),\n (Key::C, 0x0b),\n (Key::V, 0x0F),\n ],\n];\n\n#[derive(Default)]\nstruct DisplayOptions {\n pub video: bool,\n pub registers: bool,\n pub memory: bool,\n}\npub struct GemmaViewerState {\n pub selected_file_index: i32,\n pub selected_filename: String,\n pub display_options: DisplayOptions,\n}\n\nimpl Default for GemmaViewerState {\n fn default() -\u003e Self {\n GemmaViewerState {\n selected_file_index: -1,\n selected_filename: String::new(),\n display_options: Default::default(),\n }\n }\n}\n\nimpl GemmaViewerState {\n pub fn new() -\u003e GemmaViewerState {\n GemmaViewerState {\n ..Default::default()\n }\n }\n}\n\nfn main() -\u003e eframe::Result {\n println!(\"Taxation is Theft\");\n\n let mut computer = Chip8ComputerManager::new();\n let mut state = GemmaViewerState::new();\n\n let options = eframe::NativeOptions {\n viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),\n ..Default::default()\n };\n\n eframe::run_simple_native(\"EGUI Gemma\", options, move |ctx, _frame| {\n catppuccin_egui::set_theme(ctx, catppuccin_egui::MOCHA);\n egui::CentralPanel::default().show(ctx, |ui| {\n let frame_start_time = Instant::now();\n\n let local_computer = computer.state();\n //if state.display_video {\n GemmaEguiSupport::video_view(local_computer, ui);\n // }\n\n ui.heading(\"EGUI Gemma\");\n\n // if state.display_memory {\n GemmaEguiSupport::memory_view(local_computer, ui);\n // }\n\n // if state.display_registers {\n GemmaEguiSupport::registers_view(local_computer, ui);\n // }\n ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {\n if ui.button(\"Start\").clicked() {\n computer.start();\n }\n if ui.button(\"Step\").clicked() {\n computer.step();\n }\n\n if ui.button(\"Stop\").clicked() {\n computer.stop();\n // state.is_running = false;\n }\n if ui.button(\"Reset\").clicked() {\n computer.reset(computer.quirks_mode());\n // state.is_running = false;\n }\n });\n\n if ui\n .button(format!(\"Load {}\", state.selected_filename))\n .clicked()\n {\n // println!(\"Should load the bin now\");\n let read_bin = std::fs::read(PathBuf::from(format!(\n \"resources/roms/{}\",\n state.selected_filename\n )))\n .unwrap();\n computer.load_new_program_to_system_memory(read_bin);\n };\n EGuiFileList::display_path(\n PathBuf::from(\"resources/roms\"),\n \u0026mut state.selected_filename,\n ui,\n );\n ctx.input(|input| {\n // loop through the keys we are checking...\n for row in LIN_KEYS {\n for (keyboard_key, keypad_key) in row {\n if input.key_pressed(keyboard_key) {\n computer.press_key(keypad_key);\n // println!(\"KEY {keypad_key:02x} DOWN\");\n } else {\n // release it if the user just did\n if computer.is_key_pressed(keypad_key) {\n computer.release_key(keypad_key);\n // println!(\"KEY {keypad_key:02x} up\");\n }\n }\n }\n }\n });\n\n // run our target number of ticks in the amount of available time.\n let time_consumed = Instant::now().duration_since(frame_start_time).as_millis();\n let mut num_ticks = 0;\n while num_ticks \u003c 1000 {\n computer.tick();\n num_ticks += 1;\n }\n ctx.request_repaint();\n });\n })\n}\n","traces":[{"line":51,"address":[3676112,3676280],"length":1,"stats":{"Line":0}},{"line":54,"address":[3676131],"length":1,"stats":{"Line":0}},{"line":55,"address":[3676193,3676136],"length":1,"stats":{"Line":0}},{"line":61,"address":[3676304],"length":1,"stats":{"Line":0}},{"line":68,"address":[3677415,3676400,3677446],"length":1,"stats":{"Line":0}},{"line":69,"address":[3676417],"length":1,"stats":{"Line":0}},{"line":71,"address":[3676481],"length":1,"stats":{"Line":0}},{"line":72,"address":[3676511],"length":1,"stats":{"Line":0}},{"line":75,"address":[3676620,3676575],"length":1,"stats":{"Line":0}},{"line":79,"address":[3654016],"length":1,"stats":{"Line":0}},{"line":80,"address":[3654056],"length":1,"stats":{"Line":0}},{"line":81,"address":[3654208,3655711,3654100],"length":1,"stats":{"Line":0}},{"line":82,"address":[3654233],"length":1,"stats":{"Line":0}},{"line":84,"address":[3654276],"length":1,"stats":{"Line":0}},{"line":86,"address":[3654309],"length":1,"stats":{"Line":0}},{"line":89,"address":[3654319],"length":1,"stats":{"Line":0}},{"line":92,"address":[3654370],"length":1,"stats":{"Line":0}},{"line":96,"address":[3654385],"length":1,"stats":{"Line":0}},{"line":98,"address":[3654499,3655744,3656665],"length":1,"stats":{"Line":0}},{"line":99,"address":[3655883,3655769],"length":1,"stats":{"Line":0}},{"line":100,"address":[3655958],"length":1,"stats":{"Line":0}},{"line":102,"address":[3656097,3655974],"length":1,"stats":{"Line":0}},{"line":103,"address":[3656181],"length":1,"stats":{"Line":0}},{"line":106,"address":[3656197,3656320],"length":1,"stats":{"Line":0}},{"line":107,"address":[3656404],"length":1,"stats":{"Line":0}},{"line":110,"address":[3656543,3656420],"length":1,"stats":{"Line":0}},{"line":111,"address":[3656624],"length":1,"stats":{"Line":0}},{"line":116,"address":[3654950,3654783],"length":1,"stats":{"Line":0}},{"line":117,"address":[3654630],"length":1,"stats":{"Line":0}},{"line":118,"address":[3654956,3654902],"length":1,"stats":{"Line":0}},{"line":121,"address":[3654994,3655142],"length":1,"stats":{"Line":0}},{"line":126,"address":[3655405],"length":1,"stats":{"Line":0}},{"line":128,"address":[3655475],"length":1,"stats":{"Line":0}},{"line":129,"address":[3655422],"length":1,"stats":{"Line":0}},{"line":130,"address":[3655471],"length":1,"stats":{"Line":0}},{"line":133,"address":[3655485,3656688,3657346],"length":1,"stats":{"Line":0}},{"line":135,"address":[3656937,3656870,3656713],"length":1,"stats":{"Line":0}},{"line":136,"address":[3657127,3657197,3656953],"length":1,"stats":{"Line":0}},{"line":137,"address":[3657189,3657225],"length":1,"stats":{"Line":0}},{"line":138,"address":[3657341,3657281],"length":1,"stats":{"Line":0}},{"line":142,"address":[3657254,3657304],"length":1,"stats":{"Line":0}},{"line":143,"address":[3657317],"length":1,"stats":{"Line":0}},{"line":152,"address":[3655497],"length":1,"stats":{"Line":0}},{"line":153,"address":[3655596],"length":1,"stats":{"Line":0}},{"line":154,"address":[3655607,3655693],"length":1,"stats":{"Line":0}},{"line":155,"address":[3655655],"length":1,"stats":{"Line":0}},{"line":156,"address":[3655664,3655695],"length":1,"stats":{"Line":0}},{"line":158,"address":[3655625],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":48},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaegui","src","bin","support","gemma_egui_state.rs"],"content":"use std::ops::Range;\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\n\npub struct GemmaEGuiState {\n pub display_video: bool,\n pub display_memory: bool,\n pub display_registers: bool,\n pub memory_view: Range\u003ci32\u003e,\n pub computer: Chip8ComputerManager,\n pub selected_rom_filename: String\n}\n\nimpl Default for GemmaEGuiState {\n fn default() -\u003e Self {\n Self {\n display_video: true,\n display_memory: true,\n display_registers: true,\n memory_view: 0x00..0x200,\n computer: Chip8ComputerManager::new(),\n selected_rom_filename: String::new()\n }\n }\n}\n","traces":[{"line":14,"address":[3690467,3690256],"length":1,"stats":{"Line":0}},{"line":20,"address":[3690273],"length":1,"stats":{"Line":0}},{"line":21,"address":[3690295],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":3},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaegui","src","bin","support","gemma_egui_support.rs"],"content":"use crate::Chip8Computer;\nuse egui::Rect;\nuse egui::Ui;\nuse egui::Vec2;\nuse egui::{Align, Color32, ComboBox, Pos2};\nuse std::fs::read_dir;\nuse std::path::PathBuf;\n\nconst CELL_WIDTH: f32 = 5.0;\nconst CELL_HEIGHT: f32 = 5.0;\n\npub struct EGuiFileList {}\nimpl EGuiFileList {\n pub fn display_path(root: PathBuf, selected_filename: \u0026mut String, ui: \u0026mut Ui) {\n let working_filename = selected_filename.clone();\n ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {\n // ui.label(format!(\"Displaying {}\", root.to_str().unwrap_or(\"Unable to Load Path\")));\n ComboBox::from_label(\"Select ROM\")\n .selected_text(selected_filename.clone())\n .show_ui(ui, |ui| {\n let mut sorted_options = vec![];\n for option in read_dir(root.as_path()).unwrap() {\n let to_push = option\n .unwrap()\n .file_name()\n .into_string()\n .unwrap_or(String::new());\n sorted_options.push(to_push);\n }\n\n sorted_options.sort();\n for item in sorted_options {\n // Add each option to the ComboBox\n if ui\n .selectable_label(selected_filename.eq(\u0026item.as_str()), item.clone())\n .clicked()\n {\n *selected_filename = item;\n }\n }\n });\n // Display the selected option\n });\n }\n}\n\npub struct GemmaEguiSupport {}\n\nimpl GemmaEguiSupport {\n pub fn controls_view(system: \u0026mut Chip8Computer, ui: \u0026mut Ui) {\n ui.with_layout(egui::Layout::left_to_right(Align::TOP), |ui| {\n // ui.checkbox(\u0026mut state.display_memory, \"Display Memory\");\n // ui.checkbox(\u0026mut state.display_video, \"Display Video\");\n // ui.checkbox(\u0026mut state.display_registers, \"Display Registers\");\n });\n }\n\n pub fn registers_view(system: \u0026Chip8Computer, ui: \u0026mut Ui) {\n ui.label(format!(\n \"V0-7: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} \",\n system.registers.peek(0x00),\n system.registers.peek(0x01),\n system.registers.peek(0x02),\n system.registers.peek(0x03),\n system.registers.peek(0x04),\n system.registers.peek(0x05),\n system.registers.peek(0x06),\n system.registers.peek(0x07)\n ));\n ui.label(format!(\n \"V8-F: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} \",\n system.registers.peek(0x08),\n system.registers.peek(0x09),\n system.registers.peek(0x0A),\n system.registers.peek(0x0B),\n system.registers.peek(0x0C),\n system.registers.peek(0x0D),\n system.registers.peek(0x0E),\n system.registers.peek(0x0F)\n ));\n ui.label(format!(\n \"PC: {:04x}\\tI: {:04x}\",\n system.registers.peek_pc(),\n system.registers.peek_i()\n ));\n }\n\n pub fn video_view(system: \u0026Chip8Computer, ui: \u0026mut Ui) {\n let (_resp, painter) = ui.allocate_painter(Vec2::new(350.0, 165.0), egui::Sense::hover());\n for current_row in 0..32 {\n for current_col in 0..64 {\n let data_offset = current_row * 64 + current_col;\n let x_offset = current_col as f32 * CELL_WIDTH;\n let y_offset = current_row as f32 * CELL_HEIGHT;\n let origin = Pos2::new(x_offset, y_offset);\n let colour = if system.video_memory.peek(data_offset) {\n Color32::RED\n } else {\n Color32::WHITE\n };\n let rect = Rect::from_min_size(origin, Vec2::new(CELL_WIDTH, CELL_HEIGHT));\n painter.rect_filled(rect, 0.0, colour);\n // println!(\"Cell {current_col}x{current_row} at {}x{} -\u003e {}\",\n // origin.x, origin.y,\n // system.video_memory.peek(data_offset));\n }\n }\n // thread::sleep(Duration::from_secs(1));\n }\n\n pub fn memory_view(system: \u0026Chip8Computer, ui: \u0026mut Ui) {\n ui.label(\"Memory View\");\n\n for i in (0..=0x200).step_by(16) {\n ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {\n for y in 0..16 {\n ui.label(format!(\"{:02x}\", system.memory.peek((i + y) as u16)).as_str());\n }\n });\n }\n ui.label(\"Should have **something** to adjust the 'memory window'\");\n }\n}\n","traces":[{"line":14,"address":[3683153,3682720],"length":1,"stats":{"Line":0}},{"line":15,"address":[3682750],"length":1,"stats":{"Line":0}},{"line":16,"address":[3666307,3666064,3666339],"length":1,"stats":{"Line":0}},{"line":18,"address":[3666270,3666094,3666204],"length":1,"stats":{"Line":0}},{"line":19,"address":[3666252,3666139,3666320],"length":1,"stats":{"Line":0}},{"line":20,"address":[3666352,3668143,3668571,3666260],"length":1,"stats":{"Line":0}},{"line":21,"address":[3666388],"length":1,"stats":{"Line":0}},{"line":22,"address":[3666445,3668522,3666549,3666947,3666914,3666775],"length":1,"stats":{"Line":0}},{"line":23,"address":[3668329,3668174,3668261],"length":1,"stats":{"Line":0}},{"line":26,"address":[3668268],"length":1,"stats":{"Line":0}},{"line":27,"address":[3668201,3668549,3668284,3668368],"length":1,"stats":{"Line":0}},{"line":28,"address":[3668440],"length":1,"stats":{"Line":0}},{"line":31,"address":[3667189],"length":1,"stats":{"Line":0}},{"line":32,"address":[3667223,3668119,3667463,3667413],"length":1,"stats":{"Line":0}},{"line":34,"address":[3667684,3667862,3668106],"length":1,"stats":{"Line":0}},{"line":35,"address":[3667503,3667613],"length":1,"stats":{"Line":0}},{"line":36,"address":[3667814,3667868],"length":1,"stats":{"Line":0}},{"line":38,"address":[3667925],"length":1,"stats":{"Line":0}},{"line":50,"address":[3683184],"length":1,"stats":{"Line":0}},{"line":51,"address":[3668608],"length":1,"stats":{"Line":0}},{"line":58,"address":[3683424],"length":1,"stats":{"Line":0}},{"line":59,"address":[3683733,3683635,3683684,3683488,3683586,3685525,3683537,3683831,3683782],"length":1,"stats":{"Line":0}},{"line":61,"address":[3683469],"length":1,"stats":{"Line":0}},{"line":62,"address":[3683515],"length":1,"stats":{"Line":0}},{"line":63,"address":[3683564],"length":1,"stats":{"Line":0}},{"line":64,"address":[3683613],"length":1,"stats":{"Line":0}},{"line":65,"address":[3683662],"length":1,"stats":{"Line":0}},{"line":66,"address":[3683711],"length":1,"stats":{"Line":0}},{"line":67,"address":[3683760],"length":1,"stats":{"Line":0}},{"line":68,"address":[3683809],"length":1,"stats":{"Line":0}},{"line":70,"address":[3687619,3685582,3685925,3685631,3685876,3685680,3685729,3685827,3685778],"length":1,"stats":{"Line":0}},{"line":72,"address":[3685560],"length":1,"stats":{"Line":0}},{"line":73,"address":[3685609],"length":1,"stats":{"Line":0}},{"line":74,"address":[3685658],"length":1,"stats":{"Line":0}},{"line":75,"address":[3685707],"length":1,"stats":{"Line":0}},{"line":76,"address":[3685756],"length":1,"stats":{"Line":0}},{"line":77,"address":[3685805],"length":1,"stats":{"Line":0}},{"line":78,"address":[3685854],"length":1,"stats":{"Line":0}},{"line":79,"address":[3685903],"length":1,"stats":{"Line":0}},{"line":81,"address":[3687723,3688254,3687678],"length":1,"stats":{"Line":0}},{"line":83,"address":[3687660],"length":1,"stats":{"Line":0}},{"line":84,"address":[3687705],"length":1,"stats":{"Line":0}},{"line":88,"address":[3688304,3689696],"length":1,"stats":{"Line":0}},{"line":89,"address":[3688359],"length":1,"stats":{"Line":0}},{"line":90,"address":[3688671,3688753,3688857],"length":1,"stats":{"Line":0}},{"line":91,"address":[3688878,3688976],"length":1,"stats":{"Line":0}},{"line":92,"address":[3689260,3689091],"length":1,"stats":{"Line":0}},{"line":93,"address":[3689180],"length":1,"stats":{"Line":0}},{"line":94,"address":[3689214],"length":1,"stats":{"Line":0}},{"line":95,"address":[3689300],"length":1,"stats":{"Line":0}},{"line":96,"address":[3689381,3689336],"length":1,"stats":{"Line":0}},{"line":97,"address":[3689383],"length":1,"stats":{"Line":0}},{"line":99,"address":[3689368],"length":1,"stats":{"Line":0}},{"line":102,"address":[3689610],"length":1,"stats":{"Line":0}},{"line":111,"address":[3689728],"length":1,"stats":{"Line":0}},{"line":112,"address":[3689760],"length":1,"stats":{"Line":0}},{"line":114,"address":[3689794,3690006],"length":1,"stats":{"Line":0}},{"line":115,"address":[3669272,3668624],"length":1,"stats":{"Line":0}},{"line":116,"address":[3668712,3668654],"length":1,"stats":{"Line":0}},{"line":117,"address":[3669132,3668723],"length":1,"stats":{"Line":0}},{"line":121,"address":[3689949],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":61},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaegui","src","bin","support","mod.rs"],"content":"pub mod gemma_egui_state;\npub mod gemma_egui_support;\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaimgui","src","bin","gemmaimgui.rs"],"content":"use clap::{Parser, Subcommand};\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\nuse std::path::Path;\nuse std::time::Instant;\nuse std::{default::Default, path::PathBuf};\nuse support::ui_state::ROMPATH_DEFAULT;\nuse support::{emmagui_support::GemmaImguiSupport, ui_state::ImGuiUiState};\n\nmod support;\n\n/// Keypad Mappings for my Linux box\n/// 1 2 3 C\n/// 4 5 6 D\n/// 7 8 9 E\n/// A 0 B F\n\nconst LIN_KEYS: [(u16, u8); 0x10] = [\n (537, 0x01),\n (538, 0x02),\n (539, 0x03),\n (540, 0x0c),\n (562, 0x04),\n (568, 0x05),\n (550, 0x06),\n (563, 0x0d),\n (546, 0x7),\n (564, 0x8),\n (549, 0x9),\n (551, 0xe),\n (571, 0xa),\n (569, 0x0),\n (548, 0xb),\n (567, 0xf),\n];\n\n#[derive(Parser)]\n#[command(version, about, long_about = None)]\nstruct GemmaImguiCLIOptions {\n roms_directory: Option\u003cPathBuf\u003e,\n}\n\nfn main() {\n pretty_env_logger::init();\n\n let cli_options = GemmaImguiCLIOptions::parse();\n\n let mut system = Chip8ComputerManager::default();\n println!(\"GOT A ROMS_DIRECTORY =\u003e [{:?}]\", cli_options.roms_directory);\n println!(\"{:?}\", std::env::var(\"PWD\"));\n let mut ui_state = if cli_options.roms_directory.is_none() {\n ImGuiUiState::new(Some(PathBuf::from(ROMPATH_DEFAULT)))\n } else {\n ImGuiUiState::new(cli_options.roms_directory)\n };\n\n support::simple_init(file!(), move |_, ui| {\n let current_time = Instant::now();\n let mut num_cycles = 0;\n\n // Key Checks\n let down_keys = ui.io().keys_down;\n\n // START DEBUG CODE TO DISPLAY WHAT KEYS WE TRAPPED\n for (idx, val) in down_keys.iter().enumerate() {\n if *val {\n println!(\"{idx} = {val}\");\n }\n }\n // END DEBUG CODE\n\n for (key_code, key_reg) in LIN_KEYS {\n if down_keys[key_code as usize] {\n system.press_key(key_reg);\n system.wait_for_instruction();\n } else {\n // do we need to release it?\n\n if system.is_key_pressed(key_reg) {\n system.release_key(key_reg);\n }\n }\n }\n\n let target_ms = ui_state.frame_time;\n let loop_start_time = Instant::now();\n while Instant::now().duration_since(current_time).as_millis() \u003c target_ms as u128\n \u0026\u0026 num_cycles \u003c ui_state.target_ips\n {\n if system.tick() {\n num_cycles += 1;\n }\n }\n let cycles_time = Instant::now().duration_since(loop_start_time);\n if num_cycles \u003e 0 {\n println!(\n \"Ran for {}ms and executed {}/{} cycles.\",\n cycles_time.as_millis(),\n num_cycles,\n ui_state.target_ips\n );\n }\n // GUI Parts\n if ui_state.show_video {\n GemmaImguiSupport::video_display(system.state(), \u0026ui_state, ui);\n }\n\n GemmaImguiSupport::system_controls(\u0026mut system, \u0026mut ui_state, ui);\n\n if ui_state.show_registers {\n GemmaImguiSupport::registers_view(system.state(), ui);\n }\n\n if ui_state.show_memory {\n let active_instruction = system.state().registers.peek_pc();\n GemmaImguiSupport::hex_memory_display(\n system.state().memory.clone(),\n (0x100, 0x10),\n active_instruction as i16,\n ui,\n );\n }\n\n if ui_state.show_keypad {\n GemmaImguiSupport::keypad_display(system.state(), ui);\n }\n });\n}\n","traces":[{"line":42,"address":[4455308,4454496,4455336],"length":1,"stats":{"Line":0}},{"line":43,"address":[4454503],"length":1,"stats":{"Line":0}},{"line":45,"address":[4454528],"length":1,"stats":{"Line":0}},{"line":47,"address":[4454546],"length":1,"stats":{"Line":0}},{"line":48,"address":[4454603,4454669],"length":1,"stats":{"Line":0}},{"line":49,"address":[4454738],"length":1,"stats":{"Line":0}},{"line":50,"address":[4454929],"length":1,"stats":{"Line":0}},{"line":51,"address":[4455007,4455201],"length":1,"stats":{"Line":0}},{"line":53,"address":[4454950,4455041],"length":1,"stats":{"Line":0}},{"line":56,"address":[4346125,4344512],"length":1,"stats":{"Line":0}},{"line":57,"address":[4344553],"length":1,"stats":{"Line":0}},{"line":58,"address":[4344588],"length":1,"stats":{"Line":0}},{"line":61,"address":[4344596],"length":1,"stats":{"Line":0}},{"line":64,"address":[4344627,4344862],"length":1,"stats":{"Line":0}},{"line":65,"address":[4344894],"length":1,"stats":{"Line":0}},{"line":66,"address":[4346147],"length":1,"stats":{"Line":0}},{"line":71,"address":[4344788,4344981,4345113,4344924],"length":1,"stats":{"Line":0}},{"line":72,"address":[4345147,4345974],"length":1,"stats":{"Line":0}},{"line":73,"address":[4346049],"length":1,"stats":{"Line":0}},{"line":74,"address":[4346109],"length":1,"stats":{"Line":0}},{"line":78,"address":[4346022,4346067],"length":1,"stats":{"Line":0}},{"line":79,"address":[4346085],"length":1,"stats":{"Line":0}},{"line":84,"address":[4345064],"length":1,"stats":{"Line":0}},{"line":85,"address":[4345081],"length":1,"stats":{"Line":0}},{"line":86,"address":[4345172],"length":1,"stats":{"Line":0}},{"line":87,"address":[4345350],"length":1,"stats":{"Line":0}},{"line":89,"address":[4345367,4345948],"length":1,"stats":{"Line":0}},{"line":90,"address":[4345953,4345925],"length":1,"stats":{"Line":0}},{"line":93,"address":[4345272],"length":1,"stats":{"Line":0}},{"line":94,"address":[4345336],"length":1,"stats":{"Line":0}},{"line":95,"address":[4345438],"length":1,"stats":{"Line":0}},{"line":97,"address":[4345417],"length":1,"stats":{"Line":0}},{"line":103,"address":[4345391],"length":1,"stats":{"Line":0}},{"line":104,"address":[4345698],"length":1,"stats":{"Line":0}},{"line":107,"address":[4345662],"length":1,"stats":{"Line":0}},{"line":109,"address":[4345682],"length":1,"stats":{"Line":0}},{"line":110,"address":[4345752],"length":1,"stats":{"Line":0}},{"line":113,"address":[4345736],"length":1,"stats":{"Line":0}},{"line":114,"address":[4345794],"length":1,"stats":{"Line":0}},{"line":115,"address":[4345863],"length":1,"stats":{"Line":0}},{"line":116,"address":[4345831],"length":1,"stats":{"Line":0}},{"line":123,"address":[4345778],"length":1,"stats":{"Line":0}},{"line":124,"address":[4345904],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":43},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaimgui","src","bin","support","emmagui_support.rs"],"content":"use crate::support::gui_file_list::GuiFileList;\nuse crate::ImGuiUiState;\nuse dimensioned::ucum::f32consts::CUP_M;\nuse gemma::chip8::computer::Chip8Computer;\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\nuse gemma::chip8::computer_manager::ManagerDumpables::{Keyboard, Registers, Video};\nuse gemma::chip8::quirk_modes::QuirkMode::{self, *};\nuse gemma::chip8::system_memory::Chip8SystemMemory;\nuse gemma::constants::CHIP8_KEYBOARD;\nuse imgui::{CollapsingHeader, Condition, ImColor32, Ui};\nuse log::debug;\nuse std::fs::File;\nuse std::io::Read;\nuse std::path::{Path, PathBuf};\n\nconst ROM_ROOT: \u0026str = \"resources/roms\";\n\npub struct GemmaImguiSupport {}\n\nconst CELL_WIDTH: i32 = 5i32;\nconst CELL_HEIGHT: i32 = 5i32;\n\nimpl GemmaImguiSupport {\n pub fn keypad_display(system_to_display: \u0026Chip8Computer, ui: \u0026Ui) {\n ui.window(\"Keypad\".to_string())\n .position([100.0, 640.0], Condition::FirstUseEver)\n .build(|| {\n for row in CHIP8_KEYBOARD {\n for key in row {\n let label = if system_to_display.keypad.pressed(key) {\n format!(\"*{:1x}*\", key)\n } else {\n format!(\"{:1x}\", key)\n };\n ui.text(\u0026label);\n ui.same_line();\n }\n ui.text(\"\");\n }\n });\n }\n\n pub fn video_display(system_to_control: \u0026Chip8Computer, gui_state: \u0026ImGuiUiState, ui: \u0026Ui) {\n // draw area size\n let (width, height) = system_to_control.video_memory.get_resolution();\n let draw_area_size = ui.io().display_size;\n // println!(\"DRAW_AREA_SIZE = {}x{}\", draw_area_size[0], draw_area_size[1]);\n let cell_width = ((draw_area_size[0] as i32 / width) * 6) / 10;\n let cell_height = ((draw_area_size[1] as i32 / height) * 6) / 10;\n\n let origin = ui.cursor_pos();\n let fg = ui.get_foreground_draw_list();\n if system_to_control.video_memory.is_highres() {\n // ui.text(\"High Def Video here\");\n for current_row in 0..=height {\n let y_offset = origin[1] as i32 + (current_row * cell_height);\n for current_column in 0..=width {\n let x_offset = origin[0] as i32 + (current_column * cell_width);\n let current_origin = [x_offset as f32, y_offset as f32];\n let current_limit = [\n (x_offset + cell_width) as f32,\n (y_offset + cell_height) as f32,\n ];\n let memory_offset = (current_row * width + current_column) as u16;\n let to_render = system_to_control.video_memory.peek(memory_offset);\n let color: ImColor32 = if to_render {\n gui_state.on_colour\n } else {\n gui_state.off_colour\n };\n fg.add_rect_filled_multicolor(\n current_origin,\n current_limit,\n color,\n color,\n color,\n color,\n );\n }\n }\n } else {\n for current_row in 0..height {\n let y_offset = origin[1] as i32 + (current_row * cell_height);\n for current_column in 0..width {\n let x_offset = origin[0] as i32 + (current_column * cell_width);\n let current_origin = [x_offset as f32, y_offset as f32];\n let current_limit = [\n (x_offset + cell_width) as f32,\n (y_offset + cell_height) as f32,\n ];\n let memory_offset = (current_row * width + current_column) as u16;\n let to_render = system_to_control.video_memory.peek(memory_offset);\n let color: ImColor32 = if to_render {\n gui_state.on_colour\n } else {\n gui_state.off_colour\n };\n fg.add_rect_filled_multicolor(\n current_origin,\n current_limit,\n color,\n color,\n color,\n color,\n );\n }\n }\n }\n }\n pub fn system_controls(\n system_to_control: \u0026mut Chip8ComputerManager,\n gui_state: \u0026mut ImGuiUiState,\n ui: \u0026Ui,\n ) {\n // let mut state: Chip8Computer = system_to_control;\n ui.window(\"!!!! CONTROLS !!!!\")\n .position([100.0, 640.0], Condition::FirstUseEver)\n .build(|| {\n /* System Step Counter */\n ui.text(format!(\"Step {:04x}\", system_to_control.num_cycles()).as_str());\n ui.text(format!(\"Mode {}\", system_to_control.quirks_mode()));\n /* ROM Lister */\n if CollapsingHeader::new(\"Roms\").build(ui) {\n let new_filename = GuiFileList::display_path(\n gui_state.roms_root_path.clone(),\n \u0026gui_state.filename_to_load,\n ui,\n );\n if !new_filename.is_empty() {\n if new_filename != gui_state.filename_to_load {\n debug!(\"NEW FILENAME SELECTED -\u003e {new_filename}\");\n gui_state.filename_to_load = new_filename;\n }\n if ui.button(\"Load Program\") {\n let mut buffer: Vec\u003cu8\u003e = Vec::new();\n let full_name = format!(\n \"{}/{}\",\n gui_state\n .roms_root_path\n .clone()\n .as_os_str()\n .to_string_lossy(),\n gui_state.filename_to_load.to_string()\n );\n debug!(\"PREPARING TO LOAD {}\", full_name);\n\n let input_file = File::open(Path::new(\u0026full_name));\n input_file\n .unwrap()\n .read_to_end(\u0026mut buffer)\n .expect(\"Unable to read rom.\");\n system_to_control.load_new_program_to_system_memory(buffer);\n }\n }\n }\n\n if CollapsingHeader::new(\"Controls\").build(ui) {\n // if the system has no program loaded hide the buttons.\n let bytes: [u8; 2] = [\n system_to_control.state().memory.peek(0x200),\n system_to_control.state().memory.peek(0x201),\n ];\n let show_buttons = bytes[0] != 0 || bytes[1] == 0xe0;\n\n if show_buttons {\n if ui.button(\"Step\") {\n system_to_control.step();\n };\n ui.same_line();\n if ui.button(\"Run\") {\n system_to_control.start();\n }\n }\n ui.same_line();\n if ui.button(\"Stop\") {\n system_to_control.stop();\n }\n ui.same_line();\n if ui.button(\"Reset\") {\n system_to_control.reset(system_to_control.quirks_mode());\n }\n if ui.button(\"Dump Video Memory\") {\n println!(\"{}\", system_to_control.dump_to_string(Video));\n }\n ui.same_line();\n if ui.button(\"Dump Keypad State\") {\n debug!(\"{}\", system_to_control.dump_to_string(Keyboard));\n }\n ui.same_line();\n if ui.button(\"Dump Registers\") {\n debug!(\"{}\", system_to_control.dump_to_string(Registers));\n }\n }\n\n if CollapsingHeader::new(\"Options\").build(ui) {\n ui.checkbox(\"Show Memory\", \u0026mut gui_state.show_memory);\n ui.same_line();\n ui.checkbox(\"Show Video\", \u0026mut gui_state.show_video);\n ui.same_line();\n ui.checkbox(\"Show Registers\", \u0026mut gui_state.show_registers);\n ui.same_line();\n ui.checkbox(\"Show Keypad\", \u0026mut gui_state.show_keypad);\n ui.input_int(\"Target IPS\", \u0026mut gui_state.target_ips)\n .build();\n };\n\n let selectors = [Chip8, SChipModern, XOChip];\n for current_selector in selectors {\n let mut working_selector =\n ui.selectable_config(current_selector.clone().to_string());\n match system_to_control.quirks_mode() {\n Chip8 =\u003e {\n working_selector = working_selector.selected(true);\n }\n SChipModern =\u003e {\n working_selector = working_selector.selected(true);\n }\n XOChip =\u003e {\n working_selector = working_selector.selected(true);\n }\n }\n if working_selector.build() {\n system_to_control.reset(current_selector);\n println!(\"CLICK ON {}\", \u0026current_selector);\n }\n }\n });\n }\n\n pub fn registers_view(system: \u0026Chip8Computer, ui: \u0026Ui) {\n ui.window(\"Registers\")\n .position([100.0, 640.0], Condition::FirstUseEver)\n .build(|| {\n ui.text(\"Registers\");\n for i in 1..0x10 {\n ui.text(format!(\"V{:X}: {}\", i, system.registers.peek(i)));\n if i != 7 {\n ui.same_line();\n }\n }\n ui.text(\"\");\n ui.text(format!(\"I: {:03X}\", system.registers.peek_i()));\n ui.same_line();\n ui.text(format!(\"ST: {:02X}\", system.sound_timer.current()));\n ui.same_line();\n ui.text(format!(\"DT: {:02X}\", system.delay_timer.current()));\n ui.text(format!(\"PC: {:02X}\", system.registers.peek_pc()));\n ui.text(format!(\"SP: {:02X}\", system.stack.depth()));\n });\n }\n\n pub fn hex_memory_display(\n bytes: Chip8SystemMemory,\n position: (i32, i32),\n active: i16,\n ui: \u0026Ui,\n ) {\n let rows = position.0;\n let cols = position.1;\n ui.window(\"System Memory\")\n .position([100.0, 640.0], Condition::FirstUseEver)\n .build(|| {\n let mut current_x_hover: i32 = 0;\n let mut current_y_hover: i32 = 0;\n // display a block of data\n for current_row in 0..rows {\n ui.text(format!(\"{:02x}\", current_row * cols));\n ui.same_line();\n for current_column in 0..cols {\n let data_offset = current_row * cols + current_column;\n let formatted_text = format!(\"{:02x}\", bytes.peek(data_offset as u16));\n let text_location = ui.cursor_screen_pos();\n let text_size = ui.calc_text_size(formatted_text.clone());\n let bounding_box = imgui::sys::ImVec2 {\n x: text_location[0] + text_size[0],\n y: text_location[1] + text_size[1],\n };\n\n let hovering = ui.is_mouse_hovering_rect(\n text_location,\n [bounding_box.x, bounding_box.y],\n );\n let is_active = data_offset == active as i32;\n\n ui.text_colored(\n if hovering {\n [0., 1., 1., 1.]\n } else if is_active {\n [1., 0., 1., 1.]\n } else {\n [1., 1., 0., 1.]\n },\n formatted_text.clone(),\n );\n\n // if we are hovering show that at the bottom...\n if hovering {\n // Optionally change the text color to indicate it's interactable\n current_x_hover = current_column;\n current_y_hover = current_row;\n\n // Check if the left mouse button is clicked while hovering over the text\n if ui.is_mouse_clicked(imgui::MouseButton::Left) {\n debug!(\n \"Offset: [{}] [0x{:02x}] Value: [{}]\",\n data_offset,\n data_offset,\n formatted_text.clone()\n );\n // Perform any action here, e.g., call a function, trigger an event, etc.\n }\n }\n\n // are we on the same line?\n if current_column != (cols - 1) {\n ui.same_line();\n }\n }\n }\n ui.text(format!(\n \"Offset 0x{:03x}\",\n current_x_hover * cols + current_y_hover\n ));\n });\n }\n}\n","traces":[{"line":24,"address":[4686944],"length":1,"stats":{"Line":0}},{"line":25,"address":[4686977,4687071],"length":1,"stats":{"Line":0}},{"line":26,"address":[4687029],"length":1,"stats":{"Line":0}},{"line":27,"address":[4841710,4840304],"length":1,"stats":{"Line":0}},{"line":28,"address":[4840468,4840546,4840321],"length":1,"stats":{"Line":0}},{"line":29,"address":[4840560,4840779,4840729],"length":1,"stats":{"Line":0}},{"line":30,"address":[4840793,4840855],"length":1,"stats":{"Line":0}},{"line":31,"address":[4841282,4841623,4840888],"length":1,"stats":{"Line":0}},{"line":33,"address":[4840918,4840861,4841257],"length":1,"stats":{"Line":0}},{"line":35,"address":[4841259],"length":1,"stats":{"Line":0}},{"line":36,"address":[4841672],"length":1,"stats":{"Line":0}},{"line":38,"address":[4840825],"length":1,"stats":{"Line":0}},{"line":43,"address":[4687152,4690125],"length":1,"stats":{"Line":0}},{"line":45,"address":[4687207],"length":1,"stats":{"Line":0}},{"line":46,"address":[4687253],"length":1,"stats":{"Line":0}},{"line":48,"address":[4687572,4687281],"length":1,"stats":{"Line":0}},{"line":49,"address":[4687602,4687518,4687885],"length":1,"stats":{"Line":0}},{"line":51,"address":[4687790],"length":1,"stats":{"Line":0}},{"line":52,"address":[4687831],"length":1,"stats":{"Line":0}},{"line":53,"address":[4687863,4687948],"length":1,"stats":{"Line":0}},{"line":55,"address":[4689099,4688005],"length":1,"stats":{"Line":0}},{"line":56,"address":[4689227,4689360],"length":1,"stats":{"Line":0}},{"line":57,"address":[4689397,4689353],"length":1,"stats":{"Line":0}},{"line":58,"address":[4689525,4689684],"length":1,"stats":{"Line":0}},{"line":59,"address":[4689645],"length":1,"stats":{"Line":0}},{"line":60,"address":[4689789],"length":1,"stats":{"Line":0}},{"line":61,"address":[4689720,4689671,4689743],"length":1,"stats":{"Line":0}},{"line":62,"address":[4689730,4689821,4689785],"length":1,"stats":{"Line":0}},{"line":64,"address":[4689850,4689807,4689926],"length":1,"stats":{"Line":0}},{"line":65,"address":[4689951,4689907],"length":1,"stats":{"Line":0}},{"line":66,"address":[4689985,4689963],"length":1,"stats":{"Line":0}},{"line":67,"address":[4689995],"length":1,"stats":{"Line":0}},{"line":69,"address":[4689975],"length":1,"stats":{"Line":0}},{"line":71,"address":[4690013],"length":1,"stats":{"Line":0}},{"line":74,"address":[4690005],"length":1,"stats":{"Line":0}},{"line":82,"address":[4688138,4688029,4687961],"length":1,"stats":{"Line":0}},{"line":83,"address":[4688173,4688333],"length":1,"stats":{"Line":0}},{"line":84,"address":[4688365,4688306],"length":1,"stats":{"Line":0}},{"line":85,"address":[4688636,4688474],"length":1,"stats":{"Line":0}},{"line":86,"address":[4688597],"length":1,"stats":{"Line":0}},{"line":87,"address":[4688747],"length":1,"stats":{"Line":0}},{"line":88,"address":[4688675,4688698,4688623],"length":1,"stats":{"Line":0}},{"line":89,"address":[4688743,4688779,4688685],"length":1,"stats":{"Line":0}},{"line":91,"address":[4688808,4688765,4688884],"length":1,"stats":{"Line":0}},{"line":92,"address":[4688909,4688865],"length":1,"stats":{"Line":0}},{"line":93,"address":[4688943,4688921],"length":1,"stats":{"Line":0}},{"line":94,"address":[4688953],"length":1,"stats":{"Line":0}},{"line":96,"address":[4688933],"length":1,"stats":{"Line":0}},{"line":98,"address":[4688971],"length":1,"stats":{"Line":0}},{"line":101,"address":[4688963],"length":1,"stats":{"Line":0}},{"line":110,"address":[4690160],"length":1,"stats":{"Line":0}},{"line":116,"address":[4690217,4690463,4690284],"length":1,"stats":{"Line":0}},{"line":117,"address":[4690242],"length":1,"stats":{"Line":0}},{"line":118,"address":[4845187,4841744,4848363],"length":1,"stats":{"Line":0}},{"line":120,"address":[4841759,4842170],"length":1,"stats":{"Line":0}},{"line":121,"address":[4842477,4842290],"length":1,"stats":{"Line":0}},{"line":123,"address":[4842490,4845226],"length":1,"stats":{"Line":0}},{"line":124,"address":[4842677],"length":1,"stats":{"Line":0}},{"line":125,"address":[4842613],"length":1,"stats":{"Line":0}},{"line":126,"address":[4842657],"length":1,"stats":{"Line":0}},{"line":127,"address":[4842654],"length":1,"stats":{"Line":0}},{"line":129,"address":[4842760,4842698],"length":1,"stats":{"Line":0}},{"line":130,"address":[4842774,4842830,4843433],"length":1,"stats":{"Line":0}},{"line":131,"address":[4842876,4843048],"length":1,"stats":{"Line":0}},{"line":132,"address":[4843320,4842942],"length":1,"stats":{"Line":0}},{"line":134,"address":[4842844,4843445,4845182],"length":1,"stats":{"Line":0}},{"line":135,"address":[4843454],"length":1,"stats":{"Line":0}},{"line":136,"address":[4843481,4843691,4843789,4843580],"length":1,"stats":{"Line":0}},{"line":138,"address":[4843557,4843489,4843637],"length":1,"stats":{"Line":0}},{"line":143,"address":[4843770],"length":1,"stats":{"Line":0}},{"line":145,"address":[4844445,4844225,4844353],"length":1,"stats":{"Line":0}},{"line":147,"address":[4844725,4844359],"length":1,"stats":{"Line":0}},{"line":148,"address":[4845039,4844935],"length":1,"stats":{"Line":0}},{"line":151,"address":[4845065,4844982],"length":1,"stats":{"Line":0}},{"line":152,"address":[4845092],"length":1,"stats":{"Line":0}},{"line":157,"address":[4842543],"length":1,"stats":{"Line":0}},{"line":159,"address":[4845383],"length":1,"stats":{"Line":0}},{"line":160,"address":[4845317],"length":1,"stats":{"Line":0}},{"line":161,"address":[4845355],"length":1,"stats":{"Line":0}},{"line":163,"address":[4845397],"length":1,"stats":{"Line":0}},{"line":165,"address":[4845437],"length":1,"stats":{"Line":0}},{"line":166,"address":[4845513],"length":1,"stats":{"Line":0}},{"line":167,"address":[4845599],"length":1,"stats":{"Line":0}},{"line":169,"address":[4845545],"length":1,"stats":{"Line":0}},{"line":170,"address":[4845562],"length":1,"stats":{"Line":0}},{"line":171,"address":[4845619],"length":1,"stats":{"Line":0}},{"line":174,"address":[4845455],"length":1,"stats":{"Line":0}},{"line":175,"address":[4845472],"length":1,"stats":{"Line":0}},{"line":176,"address":[4845693],"length":1,"stats":{"Line":0}},{"line":178,"address":[4845642],"length":1,"stats":{"Line":0}},{"line":179,"address":[4845659],"length":1,"stats":{"Line":0}},{"line":180,"address":[4845747],"length":1,"stats":{"Line":0}},{"line":182,"address":[4845713],"length":1,"stats":{"Line":0}},{"line":183,"address":[4845848],"length":1,"stats":{"Line":0}},{"line":185,"address":[4845790],"length":1,"stats":{"Line":0}},{"line":186,"address":[4845807],"length":1,"stats":{"Line":0}},{"line":187,"address":[4846171,4846104],"length":1,"stats":{"Line":0}},{"line":189,"address":[4846054],"length":1,"stats":{"Line":0}},{"line":190,"address":[4846071],"length":1,"stats":{"Line":0}},{"line":191,"address":[4846515,4846585],"length":1,"stats":{"Line":0}},{"line":195,"address":[4845247],"length":1,"stats":{"Line":0}},{"line":196,"address":[4847150],"length":1,"stats":{"Line":0}},{"line":197,"address":[4847182],"length":1,"stats":{"Line":0}},{"line":198,"address":[4847199],"length":1,"stats":{"Line":0}},{"line":199,"address":[4847231],"length":1,"stats":{"Line":0}},{"line":200,"address":[4847248],"length":1,"stats":{"Line":0}},{"line":201,"address":[4847280],"length":1,"stats":{"Line":0}},{"line":202,"address":[4847297],"length":1,"stats":{"Line":0}},{"line":203,"address":[4847329],"length":1,"stats":{"Line":0}},{"line":207,"address":[4846929],"length":1,"stats":{"Line":0}},{"line":208,"address":[4847442,4847387,4847503,4846995,4848196],"length":1,"stats":{"Line":0}},{"line":210,"address":[4847517],"length":1,"stats":{"Line":0}},{"line":211,"address":[4847622,4847686],"length":1,"stats":{"Line":0}},{"line":213,"address":[4847733,4847994],"length":1,"stats":{"Line":0}},{"line":215,"address":[4848173],"length":1,"stats":{"Line":0}},{"line":216,"address":[4847907,4848139],"length":1,"stats":{"Line":0}},{"line":218,"address":[4848137],"length":1,"stats":{"Line":0}},{"line":219,"address":[4847820,4848103],"length":1,"stats":{"Line":0}},{"line":222,"address":[4848028,4848182],"length":1,"stats":{"Line":0}},{"line":223,"address":[4848209],"length":1,"stats":{"Line":0}},{"line":224,"address":[4848240],"length":1,"stats":{"Line":0}},{"line":230,"address":[4690496],"length":1,"stats":{"Line":0}},{"line":231,"address":[4690528,4690592],"length":1,"stats":{"Line":0}},{"line":232,"address":[4690550],"length":1,"stats":{"Line":0}},{"line":233,"address":[4848400],"length":1,"stats":{"Line":0}},{"line":234,"address":[4848417],"length":1,"stats":{"Line":0}},{"line":235,"address":[4850476,4848439],"length":1,"stats":{"Line":0}},{"line":236,"address":[4850484,4850714],"length":1,"stats":{"Line":0}},{"line":237,"address":[4850724],"length":1,"stats":{"Line":0}},{"line":238,"address":[4850735],"length":1,"stats":{"Line":0}},{"line":241,"address":[4848503],"length":1,"stats":{"Line":0}},{"line":242,"address":[4848522,4848894],"length":1,"stats":{"Line":0}},{"line":243,"address":[4848907],"length":1,"stats":{"Line":0}},{"line":244,"address":[4849289,4848918],"length":1,"stats":{"Line":0}},{"line":245,"address":[4849302],"length":1,"stats":{"Line":0}},{"line":246,"address":[4849313,4849685],"length":1,"stats":{"Line":0}},{"line":247,"address":[4849698,4850070],"length":1,"stats":{"Line":0}},{"line":248,"address":[4850455,4850083],"length":1,"stats":{"Line":0}},{"line":252,"address":[4690672,4690985],"length":1,"stats":{"Line":0}},{"line":258,"address":[4690723],"length":1,"stats":{"Line":0}},{"line":259,"address":[4690727],"length":1,"stats":{"Line":0}},{"line":260,"address":[4690731,4690829,4690959],"length":1,"stats":{"Line":0}},{"line":261,"address":[4690799],"length":1,"stats":{"Line":0}},{"line":262,"address":[4690896],"length":1,"stats":{"Line":0}},{"line":263,"address":[4850767],"length":1,"stats":{"Line":0}},{"line":264,"address":[4850778],"length":1,"stats":{"Line":0}},{"line":266,"address":[4850789,4850913],"length":1,"stats":{"Line":0}},{"line":267,"address":[4851771,4850934,4851840,4851422],"length":1,"stats":{"Line":0}},{"line":268,"address":[4851792],"length":1,"stats":{"Line":0}},{"line":269,"address":[4851856,4851810],"length":1,"stats":{"Line":0}},{"line":270,"address":[4852429,4851937],"length":1,"stats":{"Line":0}},{"line":271,"address":[4852028],"length":1,"stats":{"Line":0}},{"line":272,"address":[4852409,4852502],"length":1,"stats":{"Line":0}},{"line":273,"address":[4852526],"length":1,"stats":{"Line":0}},{"line":274,"address":[4852700],"length":1,"stats":{"Line":0}},{"line":275,"address":[4852616],"length":1,"stats":{"Line":0}},{"line":276,"address":[4852652],"length":1,"stats":{"Line":0}},{"line":279,"address":[4852740,4852718],"length":1,"stats":{"Line":0}},{"line":281,"address":[4852722],"length":1,"stats":{"Line":0}},{"line":283,"address":[4852839],"length":1,"stats":{"Line":0}},{"line":285,"address":[4853141,4852870],"length":1,"stats":{"Line":0}},{"line":286,"address":[4852879,4852960],"length":1,"stats":{"Line":0}},{"line":287,"address":[4852897],"length":1,"stats":{"Line":0}},{"line":288,"address":[4852887,4853028],"length":1,"stats":{"Line":0}},{"line":289,"address":[4853030],"length":1,"stats":{"Line":0}},{"line":291,"address":[4852965],"length":1,"stats":{"Line":0}},{"line":293,"address":[4853093],"length":1,"stats":{"Line":0}},{"line":297,"address":[4853152],"length":1,"stats":{"Line":0}},{"line":299,"address":[4853214],"length":1,"stats":{"Line":0}},{"line":300,"address":[4853221],"length":1,"stats":{"Line":0}},{"line":303,"address":[4853228],"length":1,"stats":{"Line":0}},{"line":304,"address":[4853499,4853273,4853370],"length":1,"stats":{"Line":0}},{"line":308,"address":[4853472],"length":1,"stats":{"Line":0}},{"line":315,"address":[4854382,4853166],"length":1,"stats":{"Line":0}},{"line":316,"address":[4854435],"length":1,"stats":{"Line":0}},{"line":320,"address":[4851036,4851378,4850865],"length":1,"stats":{"Line":0}},{"line":322,"address":[4850981,4851399,4850877],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":177},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaimgui","src","bin","support","gui_file_list.rs"],"content":"use std::ffi::OsString;\nuse std::fs::read_dir;\nuse std::path::PathBuf;\nuse imgui::Ui;\nuse log::debug;\n\npub struct GuiFileList {}\n\nimpl GuiFileList {\n pub fn display_path(root: PathBuf, selected_filename: \u0026String, ui: \u0026Ui) -\u003e String {\n let mut working_filename = selected_filename.clone();\n ui.text(format!(\"Displaying {}\", root.to_str().unwrap_or(\"Unable to parse path\")));\n\n let mut known_files: Vec\u003cOsString\u003e = vec![];\n\n // println!(\"STARTING AT {:?}\", std::env::current_dir());\n for entry in read_dir(root.as_path()).unwrap() {\n known_files.push(entry.unwrap().file_name());\n }\n\n known_files.sort();\n\n for (index, entry) in known_files.iter().enumerate() {\n let mut working_select = ui.selectable_config(entry.clone().into_string().unwrap().to_string());\n if *entry.to_str().unwrap() == *selected_filename.as_str() {\n working_select = working_select.selected(true);\n }\n if working_select.build() {\n debug!(\"SELECTED {index} / {entry:?}\");\n working_filename = entry.clone().into_string().unwrap();\n };\n }\n working_filename\n }\n}\n","traces":[{"line":10,"address":[3799758,3799608,3796000],"length":1,"stats":{"Line":0}},{"line":11,"address":[3796074,3796161],"length":1,"stats":{"Line":0}},{"line":12,"address":[3796520,3796169,3796246],"length":1,"stats":{"Line":0}},{"line":14,"address":[3796527],"length":1,"stats":{"Line":0}},{"line":17,"address":[3796709,3797112,3797141,3796554,3796953],"length":1,"stats":{"Line":0}},{"line":18,"address":[3799617],"length":1,"stats":{"Line":0}},{"line":21,"address":[3797389],"length":1,"stats":{"Line":0}},{"line":23,"address":[3797769,3797435,3798765],"length":1,"stats":{"Line":0}},{"line":24,"address":[3797878,3798126,3797801],"length":1,"stats":{"Line":0}},{"line":25,"address":[3798491,3798742,3798302],"length":1,"stats":{"Line":0}},{"line":26,"address":[3798624],"length":1,"stats":{"Line":0}},{"line":28,"address":[3799585,3798751,3798549],"length":1,"stats":{"Line":0}},{"line":29,"address":[3798890,3798770],"length":1,"stats":{"Line":0}},{"line":30,"address":[3799198,3798822,3799427],"length":1,"stats":{"Line":0}},{"line":33,"address":[3797719],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":15},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaimgui","src","bin","support","mod.rs"],"content":"use glium::glutin::surface::WindowSurface;\nuse glium::{Display, Surface};\nuse imgui::{Context, FontConfig, FontGlyphRanges, FontSource, Ui};\nuse imgui_glium_renderer::Renderer;\nuse imgui_winit_support::winit::event::{Event, WindowEvent};\nuse imgui_winit_support::winit::event_loop::EventLoop;\nuse imgui_winit_support::winit::window::WindowBuilder;\nuse imgui_winit_support::{HiDpiMode, WinitPlatform};\nuse std::path::Path;\nuse std::time::Instant;\n\npub mod ui_state;\npub mod emmagui_support;\nmod gui_file_list;\n\nuse copypasta::{ClipboardContext, ClipboardProvider};\nuse imgui::ClipboardBackend;\n\npub struct ClipboardSupport(pub ClipboardContext);\n\npub fn clipboard_init() -\u003e Option\u003cClipboardSupport\u003e {\n ClipboardContext::new().ok().map(ClipboardSupport)\n}\n\nimpl ClipboardBackend for ClipboardSupport {\n fn get(\u0026mut self) -\u003e Option\u003cString\u003e {\n self.0.get_contents().ok()\n }\n fn set(\u0026mut self, text: \u0026str) {\n // ignore errors?\n let _ = self.0.set_contents(text.to_owned());\n }\n}\n\npub const FONT_SIZE: f32 = 13.0;\n\n#[allow(dead_code)] // annoyingly, RA yells that this is unusued\npub fn simple_init\u003cF: FnMut(\u0026mut bool, \u0026mut Ui) + 'static\u003e(title: \u0026str, run_ui: F) {\n init_with_startup(title, |_, _, _| {}, run_ui);\n}\n\npub fn init_with_startup\u003cFInit, FUi\u003e(title: \u0026str, mut startup: FInit, mut run_ui: FUi)\nwhere\n FInit: FnMut(\u0026mut Context, \u0026mut Renderer, \u0026Display\u003cWindowSurface\u003e) + 'static,\n FUi: FnMut(\u0026mut bool, \u0026mut Ui) + 'static,\n{\n let mut imgui = create_context();\n\n let title = match Path::new(\u0026title).file_name() {\n Some(file_name) =\u003e file_name.to_str().unwrap(),\n None =\u003e title,\n };\n let event_loop = EventLoop::new().expect(\"Failed to create EventLoop\");\n\n let builder = WindowBuilder::new()\n .with_maximized(true)\n .with_title(title);\n // .with_inner_size(LogicalSize::new(1024, 768));\n let (window, display) = glium::backend::glutin::SimpleWindowBuilder::new()\n .set_window_builder(builder)\n .build(\u0026event_loop);\n let mut renderer = Renderer::init(\u0026mut imgui, \u0026display).expect(\"Failed to initialize renderer\");\n\n if let Some(backend) = clipboard_init() {\n imgui.set_clipboard_backend(backend);\n } else {\n eprintln!(\"Failed to initialize clipboard\");\n }\n\n let mut platform = WinitPlatform::init(\u0026mut imgui);\n {\n let dpi_mode = if let Ok(factor) = std::env::var(\"IMGUI_EXAMPLE_FORCE_DPI_FACTOR\") {\n // Allow forcing of HiDPI factor for debugging purposes\n match factor.parse::\u003cf64\u003e() {\n Ok(f) =\u003e HiDpiMode::Locked(f),\n Err(e) =\u003e panic!(\"Invalid scaling factor: {}\", e),\n }\n } else {\n HiDpiMode::Default\n };\n\n platform.attach_window(imgui.io_mut(), \u0026window, dpi_mode);\n }\n\n let mut last_frame = Instant::now();\n\n startup(\u0026mut imgui, \u0026mut renderer, \u0026display);\n\n event_loop\n .run(move |event, window_target| match event {\n Event::NewEvents(_) =\u003e {\n let now = Instant::now();\n imgui.io_mut().update_delta_time(now - last_frame);\n last_frame = now;\n }\n Event::AboutToWait =\u003e {\n platform\n .prepare_frame(imgui.io_mut(), \u0026window)\n .expect(\"Failed to prepare frame\");\n window.request_redraw();\n }\n Event::WindowEvent {\n event: WindowEvent::RedrawRequested,\n ..\n } =\u003e {\n let ui = imgui.frame();\n\n let mut run = true;\n run_ui(\u0026mut run, ui);\n if !run {\n window_target.exit();\n }\n\n let mut target = display.draw();\n target.clear_color_srgb(1.0, 1.0, 1.0, 1.0);\n platform.prepare_render(ui, \u0026window);\n let draw_data = imgui.render();\n renderer\n .render(\u0026mut target, draw_data)\n .expect(\"Rendering failed\");\n target.finish().expect(\"Failed to swap buffers\");\n }\n Event::WindowEvent {\n event: WindowEvent::Resized(new_size),\n ..\n } =\u003e {\n if new_size.width \u003e 0 \u0026\u0026 new_size.height \u003e 0 {\n display.resize((new_size.width, new_size.height));\n }\n platform.handle_event(imgui.io_mut(), \u0026window, \u0026event);\n }\n Event::WindowEvent {\n event: WindowEvent::CloseRequested,\n ..\n } =\u003e window_target.exit(),\n event =\u003e {\n platform.handle_event(imgui.io_mut(), \u0026window, \u0026event);\n }\n })\n .expect(\"EventLoop error\");\n}\n\n/// Creates the imgui context\npub fn create_context() -\u003e imgui::Context {\n let mut imgui = Context::create();\n // Fixed font size. Note imgui_winit_support uses \"logical\n // pixels\", which are physical pixels scaled by the devices\n // scaling factor. Meaning, 13.0 pixels should look the same size\n // on two different screens, and thus we do not need to scale this\n // value (as the scaling is handled by winit)\n imgui.fonts().add_font(\u0026[\n FontSource::TtfData {\n data: include_bytes!(\"../../../../resources/Roboto-Regular.ttf\"),\n size_pixels: FONT_SIZE,\n config: Some(FontConfig {\n // As imgui-glium-renderer isn't gamma-correct with\n // it's font rendering, we apply an arbitrary\n // multiplier to make the font a bit \"heavier\". With\n // default imgui-glow-renderer this is unnecessary.\n rasterizer_multiply: 1.5,\n // Oversampling font helps improve text rendering at\n // expense of larger font atlas texture.\n oversample_h: 4,\n oversample_v: 4,\n ..FontConfig::default()\n }),\n },\n FontSource::TtfData {\n data: include_bytes!(\"../../../../resources/mplus-1p-regular.ttf\"),\n size_pixels: FONT_SIZE,\n config: Some(FontConfig {\n // Oversampling font helps improve text rendering at\n // expense of larger font atlas texture.\n oversample_h: 4,\n oversample_v: 4,\n // Range of glyphs to rasterize\n glyph_ranges: FontGlyphRanges::japanese(),\n ..FontConfig::default()\n }),\n },\n ]);\n imgui.set_ini_filename(None);\n\n imgui\n}\n","traces":[{"line":21,"address":[4572528],"length":1,"stats":{"Line":0}},{"line":22,"address":[4572545],"length":1,"stats":{"Line":0}},{"line":26,"address":[4572608],"length":1,"stats":{"Line":0}},{"line":27,"address":[4572627],"length":1,"stats":{"Line":0}},{"line":29,"address":[4572672],"length":1,"stats":{"Line":0}},{"line":31,"address":[4572695],"length":1,"stats":{"Line":0}},{"line":38,"address":[],"length":0,"stats":{"Line":0}},{"line":39,"address":[],"length":0,"stats":{"Line":0}},{"line":42,"address":[4554402,4551440,4553419],"length":1,"stats":{"Line":0}},{"line":47,"address":[],"length":0,"stats":{"Line":0}},{"line":49,"address":[4551652,4551745],"length":1,"stats":{"Line":0}},{"line":50,"address":[4552053,4551867],"length":1,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":53,"address":[],"length":0,"stats":{"Line":0}},{"line":55,"address":[],"length":0,"stats":{"Line":0}},{"line":57,"address":[4552208,4552251],"length":1,"stats":{"Line":0}},{"line":59,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":0}},{"line":61,"address":[],"length":0,"stats":{"Line":0}},{"line":62,"address":[4552575,4552527],"length":1,"stats":{"Line":0}},{"line":64,"address":[],"length":0,"stats":{"Line":0}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":67,"address":[],"length":0,"stats":{"Line":0}},{"line":70,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":0}},{"line":74,"address":[],"length":0,"stats":{"Line":0}},{"line":75,"address":[],"length":0,"stats":{"Line":0}},{"line":76,"address":[4553250,4553341],"length":1,"stats":{"Line":0}},{"line":79,"address":[],"length":0,"stats":{"Line":0}},{"line":82,"address":[],"length":0,"stats":{"Line":0}},{"line":85,"address":[],"length":0,"stats":{"Line":0}},{"line":87,"address":[],"length":0,"stats":{"Line":0}},{"line":89,"address":[],"length":0,"stats":{"Line":0}},{"line":90,"address":[],"length":0,"stats":{"Line":0}},{"line":91,"address":[],"length":0,"stats":{"Line":0}},{"line":92,"address":[],"length":0,"stats":{"Line":0}},{"line":93,"address":[4554930],"length":1,"stats":{"Line":0}},{"line":94,"address":[4555045],"length":1,"stats":{"Line":0}},{"line":96,"address":[],"length":0,"stats":{"Line":0}},{"line":97,"address":[],"length":0,"stats":{"Line":0}},{"line":98,"address":[],"length":0,"stats":{"Line":0}},{"line":99,"address":[],"length":0,"stats":{"Line":0}},{"line":100,"address":[],"length":0,"stats":{"Line":0}},{"line":102,"address":[],"length":0,"stats":{"Line":0}},{"line":103,"address":[],"length":0,"stats":{"Line":0}},{"line":104,"address":[],"length":0,"stats":{"Line":0}},{"line":105,"address":[],"length":0,"stats":{"Line":0}},{"line":106,"address":[],"length":0,"stats":{"Line":0}},{"line":108,"address":[],"length":0,"stats":{"Line":0}},{"line":109,"address":[],"length":0,"stats":{"Line":0}},{"line":110,"address":[],"length":0,"stats":{"Line":0}},{"line":111,"address":[],"length":0,"stats":{"Line":0}},{"line":114,"address":[],"length":0,"stats":{"Line":0}},{"line":115,"address":[4555417],"length":1,"stats":{"Line":0}},{"line":116,"address":[],"length":0,"stats":{"Line":0}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":118,"address":[4555587],"length":1,"stats":{"Line":0}},{"line":119,"address":[],"length":0,"stats":{"Line":0}},{"line":120,"address":[],"length":0,"stats":{"Line":0}},{"line":121,"address":[4555651],"length":1,"stats":{"Line":0}},{"line":123,"address":[],"length":0,"stats":{"Line":0}},{"line":124,"address":[],"length":0,"stats":{"Line":0}},{"line":125,"address":[],"length":0,"stats":{"Line":0}},{"line":126,"address":[],"length":0,"stats":{"Line":0}},{"line":127,"address":[],"length":0,"stats":{"Line":0}},{"line":128,"address":[],"length":0,"stats":{"Line":0}},{"line":130,"address":[4555284,4555180],"length":1,"stats":{"Line":0}},{"line":132,"address":[],"length":0,"stats":{"Line":0}},{"line":133,"address":[],"length":0,"stats":{"Line":0}},{"line":134,"address":[],"length":0,"stats":{"Line":0}},{"line":135,"address":[],"length":0,"stats":{"Line":0}},{"line":136,"address":[],"length":0,"stats":{"Line":0}},{"line":137,"address":[],"length":0,"stats":{"Line":0}},{"line":144,"address":[4574300,4572752],"length":1,"stats":{"Line":0}},{"line":145,"address":[4572769],"length":1,"stats":{"Line":0}},{"line":151,"address":[4572793,4573894],"length":1,"stats":{"Line":0}},{"line":152,"address":[4573192],"length":1,"stats":{"Line":0}},{"line":155,"address":[4572869],"length":1,"stats":{"Line":0}},{"line":165,"address":[4572850],"length":1,"stats":{"Line":0}},{"line":168,"address":[4573760],"length":1,"stats":{"Line":0}},{"line":171,"address":[4573445],"length":1,"stats":{"Line":0}},{"line":177,"address":[4573326],"length":1,"stats":{"Line":0}},{"line":178,"address":[4573411],"length":1,"stats":{"Line":0}},{"line":182,"address":[4574239],"length":1,"stats":{"Line":0}},{"line":184,"address":[4574272],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":85},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmaimgui","src","bin","support","ui_state.rs"],"content":"use imgui::ImColor32;\nuse std::path::PathBuf;\nuse std::time::Instant;\n\npub const ROMPATH_DEFAULT: \u0026str = \"resources/roms\";\n\npub struct ImGuiUiState {\n pub show_registers: bool,\n pub show_memory: bool,\n pub show_video: bool,\n pub show_keypad: bool,\n pub filename_to_load: String,\n pub on_colour: ImColor32,\n pub off_colour: ImColor32,\n pub is_running: bool,\n pub frame_time: u32,\n pub last_frame_instant: Instant,\n pub target_ips: i32,\n pub roms_root_path: PathBuf\n}\n\nimpl Clone for ImGuiUiState {\n fn clone(\u0026self) -\u003e Self {\n ImGuiUiState {\n show_registers: self.show_registers,\n show_memory: self.show_memory,\n show_video: self.show_video,\n show_keypad: self.show_keypad,\n filename_to_load: self.filename_to_load.to_string(),\n on_colour: self.on_colour,\n off_colour: self.off_colour,\n is_running: self.is_running,\n frame_time: self.frame_time,\n last_frame_instant: self.last_frame_instant,\n target_ips: self.target_ips,\n roms_root_path: self.roms_root_path.to_owned()\n }\n }\n}\n\nimpl Default for ImGuiUiState {\n fn default() -\u003e Self {\n ImGuiUiState {\n show_registers: false,\n show_memory: false,\n show_video: true,\n show_keypad: true,\n filename_to_load: String::new(),\n on_colour: ImColor32::from_rgb(0xff, 0xff, 0x00),\n off_colour: ImColor32::from_rgb(0x00, 0xff, 0xff),\n is_running: false,\n frame_time: 16,\n last_frame_instant: Instant::now(),\n target_ips: 200000,\n roms_root_path: PathBuf::from(ROMPATH_DEFAULT)\n }\n }\n}\n\nimpl ImGuiUiState {\n pub fn new(rom_path: Option\u003cPathBuf\u003e) -\u003e ImGuiUiState {\n let unwrapped = rom_path.clone().unwrap_or(PathBuf::from(ROMPATH_DEFAULT));\n\n println!(\"UNWRAPPED =\u003e [{:?}]\", unwrapped);\n\n ImGuiUiState {\n roms_root_path: rom_path.unwrap_or(PathBuf::from(ROMPATH_DEFAULT)),\n ..Default::default()\n }\n }\n}\n","traces":[{"line":23,"address":[4275344,4275712],"length":1,"stats":{"Line":0}},{"line":25,"address":[4275376],"length":1,"stats":{"Line":0}},{"line":26,"address":[4275383],"length":1,"stats":{"Line":0}},{"line":27,"address":[4275390],"length":1,"stats":{"Line":0}},{"line":28,"address":[4275397],"length":1,"stats":{"Line":0}},{"line":29,"address":[4275409],"length":1,"stats":{"Line":0}},{"line":30,"address":[4275419],"length":1,"stats":{"Line":0}},{"line":31,"address":[4275426],"length":1,"stats":{"Line":0}},{"line":32,"address":[4275433],"length":1,"stats":{"Line":0}},{"line":33,"address":[4275440],"length":1,"stats":{"Line":0}},{"line":34,"address":[4275447],"length":1,"stats":{"Line":0}},{"line":35,"address":[4275463],"length":1,"stats":{"Line":0}},{"line":36,"address":[4275470],"length":1,"stats":{"Line":0}},{"line":42,"address":[4275744,4276041],"length":1,"stats":{"Line":0}},{"line":48,"address":[4275763],"length":1,"stats":{"Line":0}},{"line":49,"address":[4275775],"length":1,"stats":{"Line":0}},{"line":50,"address":[4275830],"length":1,"stats":{"Line":0}},{"line":53,"address":[4275843],"length":1,"stats":{"Line":0}},{"line":55,"address":[4275880],"length":1,"stats":{"Line":0}},{"line":61,"address":[4276064,4276902,4276952],"length":1,"stats":{"Line":0}},{"line":62,"address":[4276085,4276933,4276174],"length":1,"stats":{"Line":0}},{"line":64,"address":[4276299,4276347],"length":1,"stats":{"Line":0}},{"line":67,"address":[4276911,4276414],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":23},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmasdl2","src","bin","gemmasdl2.rs"],"content":"mod support;\nuse crate::support::gemma_egui_support::GemmaEguiSupport;\nuse anyhow::Context;\nuse egui::TextBuffer;\nuse egui_glow::glow::{HasContext, COLOR_BUFFER_BIT};\nuse egui_sdl2_platform::sdl2;\nuse gemma::chip8::computer::Chip8Computer;\nuse sdl2::event::{Event, WindowEvent};\nuse std::{sync::Arc, time::Instant};\nuse support::timestep::TimeStep;\n\nconst SCREEN_WIDTH: u32 = 800;\nconst SCREEN_HEIGHT: u32 = 480;\n\nasync fn run() -\u003e anyhow::Result\u003cString\u003e {\n // Initialize SDL2 and video subsystem\n let sdl = sdl2::init().map_err(|e| anyhow::anyhow!(\"Failed to create SDL context: {}\", e))?;\n let mut video = sdl\n .video()\n .map_err(|e| anyhow::anyhow!(\"Failed to initialize SDL video subsystem: {}\", e))?;\n\n // Create SDL2 window and OpenGL context\n let window = video\n .window(\"Window\", SCREEN_WIDTH, SCREEN_HEIGHT)\n .opengl()\n .position_centered()\n .build()?;\n let _gl_context = window\n .gl_create_context()\n .expect(\"Failed to create GL context\");\n\n // Load OpenGL functions\n let gl = unsafe {\n egui_glow::painter::Context::from_loader_function(|name| {\n video.gl_get_proc_address(name) as *const _\n })\n };\n let mut painter = egui_glow::Painter::new(Arc::new(gl), \"\", None)?;\n\n // Setup Egui and SDL2 platform\n let mut platform = egui_sdl2_platform::Platform::new(window.size())?;\n let mut event_pump = sdl\n .event_pump()\n .map_err(|e| anyhow::anyhow!(\"Failed to get SDL event pump: {}\", e))?;\n\n // Initial settings\n let color = [0.0, 0.0, 0.0, 1.0]; // Background color\n let start_time = Instant::now();\n let mut timestep = TimeStep::new();\n\n let mut computer = Chip8Computer::new();\n let mut is_running: bool = false;\n computer.load_bytes_to_memory(0x200, \u0026std::fs::read(\"resources/roms/3-corax+.ch8\")?);\n\n // Main loop\n 'main: loop {\n // Update the Egui platform with the current time\n platform.update_time(start_time.elapsed().as_secs_f64());\n\n // Begin Egui frame\n let ctx = platform.context();\n\n if is_running {\n computer.step_system();\n }\n\n egui::Window::new(\"Hello, world!\").show(\u0026ctx, |ui| {\n GemmaEguiSupport::video_view(\u0026computer, ui);\n GemmaEguiSupport::memory_view(\u0026computer, ui);\n GemmaEguiSupport::registers_view(\u0026computer, ui);\n });\n\n // Process Egui frame\n let full_output = platform.end_frame(\u0026mut video)?;\n let paint_jobs = platform.tessellate(\u0026full_output);\n\n // Clear the screen with the current color\n unsafe {\n painter.gl().clear_color(color[0], color[1], color[2], 1.0);\n painter.gl().clear(COLOR_BUFFER_BIT);\n }\n\n // Paint Egui outputs and update textures\n let size = window.size();\n painter.paint_and_update_textures(\n [size.0, size.1],\n 1.0,\n paint_jobs.as_slice(),\n \u0026full_output.textures_delta,\n );\n window.gl_swap_window();\n\n // Run the timestep logic\n timestep.run_this(|_| {});\n\n // Handle SDL2 events\n for event in event_pump.poll_iter() {\n match event {\n Event::Quit { .. }\n | Event::KeyDown {\n keycode: Some(sdl2::keyboard::Keycode::Escape),\n ..\n } =\u003e break 'main,\n Event::Window {\n window_id,\n win_event,\n ..\n } if window_id == window.id() =\u003e {\n if let WindowEvent::Close = win_event {\n break 'main;\n }\n }\n Event::KeyUp {\n keycode: Some(sdl2::keyboard::Keycode::F3),\n ..\n } =\u003e {\n println!(\"USER PRESSED F3 -\u003e running\");\n is_running = true;\n }\n Event::KeyUp {\n keycode: Some(sdl2::keyboard::Keycode::F4),\n ..\n } =\u003e {\n println!(\"USER PRESSED F4 -\u003e stopping\");\n is_running = false;\n }\n Event::KeyDown {\n keycode: Some(sdl2::keyboard::Keycode::F5),\n ..\n } =\u003e {\n println!(\"USER PRESSED F5 -\u003e Step\");\n computer.step_system();\n }\n Event::KeyDown {\n keycode: Some(sdl2::keyboard::Keycode::F6),\n ..\n } =\u003e {\n println!(\"USER PRESSED F6 -\u003e RESET\");\n computer.reset(computer.quirk_mode.clone());\n }\n Event::ControllerButtonDown { which, .. } =\u003e {\n println!(\"PLAYER {which} DOWN\");\n }\n Event::JoyButtonDown { button_idx, .. } =\u003e {\n println!(\"JoyButtonDown {}\", button_idx);\n }\n Event::JoyAxisMotion {\n which, axis_idx, ..\n } =\u003e {\n println!(\"JoyAxismotion {which} {axis_idx}\");\n }\n _ =\u003e platform.handle_event(\u0026event, \u0026sdl, \u0026video),\n }\n }\n\n // Optionally log the frame rate\n if let Some(fps) = timestep.frame_rate() {\n println!(\"{:?} fps\", fps);\n }\n\n let num_js = sdl.joystick().unwrap().num_joysticks().unwrap();\n println!(\"NUM JS = {num_js}\");\n }\n\n Ok((\"\").parse()?)\n}\n\nfn main() -\u003e anyhow::Result\u003c()\u003e {\n pollster::block_on(run())?;\n Ok(())\n}\n","traces":[{"line":15,"address":[907104,907230,913940,911746,907173],"length":1,"stats":{"Line":0}},{"line":17,"address":[907412,913935,907306,913984,907223,914147,913999],"length":1,"stats":{"Line":0}},{"line":18,"address":[913933,907684,907513,907405],"length":1,"stats":{"Line":0}},{"line":20,"address":[914239,914387,914224,907668],"length":1,"stats":{"Line":0}},{"line":23,"address":[907753,907617,907829,908008],"length":1,"stats":{"Line":0}},{"line":28,"address":[908085],"length":1,"stats":{"Line":0}},{"line":34,"address":[914464,908186],"length":1,"stats":{"Line":0}},{"line":35,"address":[914482],"length":1,"stats":{"Line":0}},{"line":38,"address":[913852,908245,908490],"length":1,"stats":{"Line":0}},{"line":41,"address":[913831,908792,908613,908457],"length":1,"stats":{"Line":0}},{"line":42,"address":[913810,909083,908893,908785],"length":1,"stats":{"Line":0}},{"line":44,"address":[914496,909067,914659,914511],"length":1,"stats":{"Line":0}},{"line":47,"address":[908997],"length":1,"stats":{"Line":0}},{"line":48,"address":[909167,909041],"length":1,"stats":{"Line":0}},{"line":49,"address":[909190],"length":1,"stats":{"Line":0}},{"line":51,"address":[909197],"length":1,"stats":{"Line":0}},{"line":52,"address":[909216],"length":1,"stats":{"Line":0}},{"line":53,"address":[909224,909311,913770],"length":1,"stats":{"Line":0}},{"line":58,"address":[909553],"length":1,"stats":{"Line":0}},{"line":61,"address":[909669],"length":1,"stats":{"Line":0}},{"line":63,"address":[909712],"length":1,"stats":{"Line":0}},{"line":64,"address":[909812,909749],"length":1,"stats":{"Line":0}},{"line":67,"address":[914736,909722,909846],"length":1,"stats":{"Line":0}},{"line":68,"address":[914754],"length":1,"stats":{"Line":0}},{"line":69,"address":[914768],"length":1,"stats":{"Line":0}},{"line":70,"address":[914782],"length":1,"stats":{"Line":0}},{"line":74,"address":[909868,910096],"length":1,"stats":{"Line":0}},{"line":75,"address":[910069],"length":1,"stats":{"Line":0}},{"line":79,"address":[910181,910289],"length":1,"stats":{"Line":0}},{"line":80,"address":[910382],"length":1,"stats":{"Line":0}},{"line":84,"address":[910448],"length":1,"stats":{"Line":0}},{"line":85,"address":[910558],"length":1,"stats":{"Line":0}},{"line":86,"address":[910497],"length":1,"stats":{"Line":0}},{"line":88,"address":[910511],"length":1,"stats":{"Line":0}},{"line":89,"address":[910550],"length":1,"stats":{"Line":0}},{"line":91,"address":[910609],"length":1,"stats":{"Line":0}},{"line":94,"address":[914800,910636,914811],"length":1,"stats":{"Line":0}},{"line":97,"address":[910643,910746],"length":1,"stats":{"Line":0}},{"line":98,"address":[910794,912227,911892,913310],"length":1,"stats":{"Line":0}},{"line":104,"address":[912142,911823],"length":1,"stats":{"Line":0}},{"line":109,"address":[912186],"length":1,"stats":{"Line":0}},{"line":113,"address":[913425],"length":1,"stats":{"Line":0}},{"line":117,"address":[913398,913346],"length":1,"stats":{"Line":0}},{"line":118,"address":[913417],"length":1,"stats":{"Line":0}},{"line":120,"address":[913457],"length":1,"stats":{"Line":0}},{"line":124,"address":[913430,913372],"length":1,"stats":{"Line":0}},{"line":125,"address":[913449],"length":1,"stats":{"Line":0}},{"line":127,"address":[913236],"length":1,"stats":{"Line":0}},{"line":131,"address":[913198,912278],"length":1,"stats":{"Line":0}},{"line":132,"address":[913217],"length":1,"stats":{"Line":0}},{"line":134,"address":[913305],"length":1,"stats":{"Line":0}},{"line":138,"address":[912307,913241],"length":1,"stats":{"Line":0}},{"line":139,"address":[913260],"length":1,"stats":{"Line":0}},{"line":141,"address":[912038],"length":1,"stats":{"Line":0}},{"line":142,"address":[913653,912052],"length":1,"stats":{"Line":0}},{"line":144,"address":[911994],"length":1,"stats":{"Line":0}},{"line":145,"address":[913579,912008],"length":1,"stats":{"Line":0}},{"line":147,"address":[911936],"length":1,"stats":{"Line":0}},{"line":150,"address":[913462,911964],"length":1,"stats":{"Line":0}},{"line":152,"address":[911755,913727],"length":1,"stats":{"Line":0}},{"line":157,"address":[910739,910854],"length":1,"stats":{"Line":0}},{"line":158,"address":[910911,910941],"length":1,"stats":{"Line":0}},{"line":161,"address":[911251,911564,910934],"length":1,"stats":{"Line":0}},{"line":162,"address":[911590],"length":1,"stats":{"Line":0}},{"line":165,"address":[912477,912410],"length":1,"stats":{"Line":0}},{"line":168,"address":[903568],"length":1,"stats":{"Line":0}},{"line":169,"address":[903744,903575],"length":1,"stats":{"Line":0}},{"line":170,"address":[903733],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":68},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmasdl2","src","bin","support","gemma_egui_support.rs"],"content":"use crate::Chip8Computer;\nuse egui::Rect;\nuse egui::Ui;\nuse egui::Vec2;\nuse egui::{Align, Color32, ComboBox, Pos2};\nuse std::fs::read_dir;\nuse std::path::PathBuf;\n\nconst CELL_WIDTH: f32 = 5.0;\nconst CELL_HEIGHT: f32 = 5.0;\n\npub struct EGuiFileList {}\nimpl EGuiFileList {\n pub fn display_path(root: PathBuf, selected_filename: \u0026mut String, ui: \u0026mut Ui) {\n let working_filename = selected_filename.clone();\n ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {\n // ui.label(format!(\"Displaying {}\", root.to_str().unwrap_or(\"Unable to Load Path\")));\n ComboBox::from_label(\"Select ROM\")\n .selected_text(selected_filename.clone())\n .show_ui(ui, |ui| {\n let mut sorted_options = vec![];\n for option in read_dir(root.as_path()).unwrap() {\n let to_push = option\n .unwrap()\n .file_name()\n .into_string()\n .unwrap_or(String::new());\n sorted_options.push(to_push);\n }\n\n sorted_options.sort();\n for item in sorted_options {\n // Add each option to the ComboBox\n if ui\n .selectable_label(selected_filename.eq(\u0026item.as_str()), item.clone())\n .clicked()\n {\n *selected_filename = item;\n }\n }\n });\n // Display the selected option\n });\n }\n}\n\npub struct GemmaEguiSupport {}\n\nimpl GemmaEguiSupport {\n pub fn controls_view(system: \u0026mut Chip8Computer, ui: \u0026mut Ui) {\n ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {\n if ui.button(\"Start\").clicked() {\n println!(\"Start\");\n // state.is_running = true;\n }\n if ui.button(\"Step\").clicked() {\n system.step_system();\n }\n\n if ui.button(\"Stop\").clicked() {\n println!(\"STOP\");\n // state.is_running = false;\n }\n if ui.button(\"Reset\").clicked() {\n system.reset(system.quirk_mode.clone());\n // state.is_running = false;\n }\n });\n\n // if ui.button(format!(\"Load {}\", state.selected_rom_filename)).clicked() {\n // load the bin...\n // let read_bin = std::fs::read(PathBuf::from(format!(\"resources/roms/{}\", state.selected_rom_filename))).unwrap();\n // ...then feed the system.\n // system.load_bytes_to_memory(0x200, \u0026read_bin);\n // println!(\"Loaded {}\", state.selected_rom_filename);\n // }\n // EGuiFileList::display_path(PathBuf::from(\"resources/roms\"), \u0026mut state.selected_rom_filename, ui);\n\n ui.with_layout(egui::Layout::left_to_right(Align::TOP), |ui| {\n // ui.checkbox(\u0026mut state.display_memory, \"Display Memory\");\n // ui.checkbox(\u0026mut state.display_video, \"Display Video\");\n // ui.checkbox(\u0026mut state.display_registers, \"Display Registers\");\n });\n }\n\n pub fn registers_view(system: \u0026Chip8Computer, ui: \u0026mut Ui) {\n ui.label(format!(\n \"V0-7: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} \",\n system.registers.peek(0x00),\n system.registers.peek(0x01),\n system.registers.peek(0x02),\n system.registers.peek(0x03),\n system.registers.peek(0x04),\n system.registers.peek(0x05),\n system.registers.peek(0x06),\n system.registers.peek(0x07)\n ));\n ui.label(format!(\n \"V8-F: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} \",\n system.registers.peek(0x08),\n system.registers.peek(0x09),\n system.registers.peek(0x0A),\n system.registers.peek(0x0B),\n system.registers.peek(0x0C),\n system.registers.peek(0x0D),\n system.registers.peek(0x0E),\n system.registers.peek(0x0F)\n ));\n ui.label(format!(\n \"PC: {:04x}\\tI: {:04x}\",\n system.registers.peek_pc(),\n system.registers.peek_i()\n ));\n }\n\n pub fn video_view(system: \u0026Chip8Computer, ui: \u0026mut Ui) {\n let (_resp, painter) = ui.allocate_painter(Vec2::new(350.0, 165.0), egui::Sense::hover());\n for current_row in 0..32 {\n for current_col in 0..64 {\n let data_offset = current_row * 64 + current_col;\n let x_offset = current_col as f32 * CELL_WIDTH;\n let y_offset = current_row as f32 * CELL_HEIGHT;\n let origin = Pos2::new(x_offset, y_offset);\n let colour = if system.video_memory.peek(data_offset) {\n Color32::RED\n } else {\n Color32::WHITE\n };\n let rect = Rect::from_min_size(origin, Vec2::new(CELL_WIDTH, CELL_HEIGHT));\n painter.rect_filled(rect, 0.0, colour);\n // println!(\"Cell {current_col}x{current_row} at {}x{} -\u003e {}\",\n // origin.x, origin.y,\n // system.video_memory.peek(data_offset));\n }\n }\n // thread::sleep(Duration::from_secs(1));\n }\n\n pub fn memory_view(system: \u0026Chip8Computer, ui: \u0026mut Ui) {\n ui.label(\"Memory View\");\n\n for i in (0..=0x200).step_by(16) {\n ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {\n for y in 0..16 {\n ui.label(format!(\"{:02x}\", system.memory.peek((i + y) as u16)).as_str());\n }\n });\n }\n ui.label(\"Should have **something** to adjust the 'memory window'\");\n }\n}\n","traces":[{"line":14,"address":[978639,978208],"length":1,"stats":{"Line":0}},{"line":15,"address":[978238],"length":1,"stats":{"Line":0}},{"line":16,"address":[945729,945697,945456],"length":1,"stats":{"Line":0}},{"line":18,"address":[945660,945595,945485],"length":1,"stats":{"Line":0}},{"line":19,"address":[945530,945642,945710],"length":1,"stats":{"Line":0}},{"line":20,"address":[947527,947939,945744,945650],"length":1,"stats":{"Line":0}},{"line":21,"address":[945780],"length":1,"stats":{"Line":0}},{"line":22,"address":[947894,946331,946167,945837,946302,945941],"length":1,"stats":{"Line":0}},{"line":23,"address":[947709,947558,947641],"length":1,"stats":{"Line":0}},{"line":26,"address":[947648],"length":1,"stats":{"Line":0}},{"line":27,"address":[947929,947748,947664,947593],"length":1,"stats":{"Line":0}},{"line":28,"address":[947816],"length":1,"stats":{"Line":0}},{"line":31,"address":[946573],"length":1,"stats":{"Line":0}},{"line":32,"address":[946607,947503,946797,946847],"length":1,"stats":{"Line":0}},{"line":34,"address":[947490,947068,947246],"length":1,"stats":{"Line":0}},{"line":35,"address":[946887,946997],"length":1,"stats":{"Line":0}},{"line":36,"address":[947198,947252],"length":1,"stats":{"Line":0}},{"line":38,"address":[947309],"length":1,"stats":{"Line":0}},{"line":50,"address":[978672],"length":1,"stats":{"Line":0}},{"line":51,"address":[947984,948953],"length":1,"stats":{"Line":0}},{"line":52,"address":[948120,948009],"length":1,"stats":{"Line":0}},{"line":53,"address":[948195],"length":1,"stats":{"Line":0}},{"line":56,"address":[948235,948358],"length":1,"stats":{"Line":0}},{"line":57,"address":[948442],"length":1,"stats":{"Line":0}},{"line":60,"address":[948458,948581],"length":1,"stats":{"Line":0}},{"line":61,"address":[948665],"length":1,"stats":{"Line":0}},{"line":64,"address":[948828,948705],"length":1,"stats":{"Line":0}},{"line":65,"address":[948909],"length":1,"stats":{"Line":0}},{"line":79,"address":[948976],"length":1,"stats":{"Line":0}},{"line":86,"address":[979152],"length":1,"stats":{"Line":0}},{"line":87,"address":[979351,979498,979204,979449,979400,981241,979253,979302,979547],"length":1,"stats":{"Line":0}},{"line":89,"address":[979185],"length":1,"stats":{"Line":0}},{"line":90,"address":[979231],"length":1,"stats":{"Line":0}},{"line":91,"address":[979280],"length":1,"stats":{"Line":0}},{"line":92,"address":[979329],"length":1,"stats":{"Line":0}},{"line":93,"address":[979378],"length":1,"stats":{"Line":0}},{"line":94,"address":[979427],"length":1,"stats":{"Line":0}},{"line":95,"address":[979476],"length":1,"stats":{"Line":0}},{"line":96,"address":[979525],"length":1,"stats":{"Line":0}},{"line":98,"address":[981494,981347,981298,981641,981396,981592,983335,981445,981543],"length":1,"stats":{"Line":0}},{"line":100,"address":[981276],"length":1,"stats":{"Line":0}},{"line":101,"address":[981325],"length":1,"stats":{"Line":0}},{"line":102,"address":[981374],"length":1,"stats":{"Line":0}},{"line":103,"address":[981423],"length":1,"stats":{"Line":0}},{"line":104,"address":[981472],"length":1,"stats":{"Line":0}},{"line":105,"address":[981521],"length":1,"stats":{"Line":0}},{"line":106,"address":[981570],"length":1,"stats":{"Line":0}},{"line":107,"address":[981619],"length":1,"stats":{"Line":0}},{"line":109,"address":[983970,983394,983439],"length":1,"stats":{"Line":0}},{"line":111,"address":[983376],"length":1,"stats":{"Line":0}},{"line":112,"address":[983421],"length":1,"stats":{"Line":0}},{"line":116,"address":[984016,985392],"length":1,"stats":{"Line":0}},{"line":117,"address":[984071],"length":1,"stats":{"Line":0}},{"line":118,"address":[984367,984553,984449],"length":1,"stats":{"Line":0}},{"line":119,"address":[984574,984672],"length":1,"stats":{"Line":0}},{"line":120,"address":[984956,984787],"length":1,"stats":{"Line":0}},{"line":121,"address":[984876],"length":1,"stats":{"Line":0}},{"line":122,"address":[984910],"length":1,"stats":{"Line":0}},{"line":123,"address":[984996],"length":1,"stats":{"Line":0}},{"line":124,"address":[985077,985032],"length":1,"stats":{"Line":0}},{"line":125,"address":[985079],"length":1,"stats":{"Line":0}},{"line":127,"address":[985064],"length":1,"stats":{"Line":0}},{"line":130,"address":[985306],"length":1,"stats":{"Line":0}},{"line":139,"address":[985424],"length":1,"stats":{"Line":0}},{"line":140,"address":[985457],"length":1,"stats":{"Line":0}},{"line":142,"address":[985491,985704],"length":1,"stats":{"Line":0}},{"line":143,"address":[948992,949640],"length":1,"stats":{"Line":0}},{"line":144,"address":[949080,949022],"length":1,"stats":{"Line":0}},{"line":145,"address":[949091,949500],"length":1,"stats":{"Line":0}},{"line":149,"address":[985646],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":70},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmasdl2","src","bin","support","mod.rs"],"content":"pub mod timestep;\npub mod gemma_egui_support;\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmasdl2","src","bin","support","timestep.rs"],"content":"\n//! Gameplay speed control. It attempts to mimic/limit progression to\n//! the 30 tics per second Doom used.\n\nuse std::{fmt, time::Instant};\n\nconst MS_PER_UPDATE: f32 = 28.57;\n\n#[derive(Debug)]\npub struct TimeStep {\n last_time: Instant,\n delta_time: f32,\n frame_count: u32,\n frame_time: f32,\n run_tics: u32,\n last_tics: u32,\n lag: f32,\n}\n\n#[derive(Debug)]\npub struct FrameData {\n pub tics: u32,\n pub frames: u32,\n}\n\nimpl fmt::Display for FrameData {\n fn fmt(\u0026self, f: \u0026mut fmt::Formatter\u003c'_\u003e) -\u003e fmt::Result {\n f.write_fmt(format_args!(\n \"FrameData (per-second):\\n - tics: {}\\n - fps: {}\",\n self.tics, self.frames\n ))\n }\n}\n\nimpl TimeStep {\n pub fn new() -\u003e TimeStep {\n TimeStep {\n last_time: Instant::now(),\n delta_time: 0.0,\n frame_count: 0,\n frame_time: 0.0,\n run_tics: 0,\n last_tics: 0,\n lag: 0.0,\n }\n }\n\n pub fn delta(\u0026mut self) -\u003e f32 {\n let current_time = Instant::now();\n let delta = current_time.duration_since(self.last_time).as_micros() as f32 * 0.001;\n self.last_time = current_time;\n self.delta_time = delta;\n delta\n }\n\n /// Increments self time and returns current lag. `run_this` is run only for\n /// `n` tics available.\n pub fn run_this(\u0026mut self, mut run_this: impl FnMut(f32)) {\n let dt = self.delta();\n self.lag += dt;\n while self.lag \u003e= MS_PER_UPDATE {\n run_this(dt);\n self.lag -= MS_PER_UPDATE;\n self.run_tics += 1;\n }\n }\n\n pub fn frame_rate(\u0026mut self) -\u003e Option\u003cFrameData\u003e {\n self.frame_count += 1;\n self.frame_time += self.delta_time;\n let tmp;\n let tmp2;\n // per second\n if self.frame_time \u003e= 1000.0 {\n tmp = self.frame_count;\n tmp2 = self.last_tics;\n self.frame_count = 0;\n self.frame_time = 0.0;\n self.last_tics = self.run_tics;\n return Some(FrameData {\n tics: self.run_tics - tmp2,\n frames: tmp,\n });\n }\n\n None\n }\n}\n\nimpl Default for TimeStep {\n // shutup clippy!\n fn default() -\u003e Self {\n Self::new()\n }\n}","traces":[{"line":27,"address":[905936],"length":1,"stats":{"Line":0}},{"line":28,"address":[905975],"length":1,"stats":{"Line":0}},{"line":36,"address":[906112],"length":1,"stats":{"Line":0}},{"line":38,"address":[906126],"length":1,"stats":{"Line":0}},{"line":48,"address":[906208],"length":1,"stats":{"Line":0}},{"line":49,"address":[906221],"length":1,"stats":{"Line":0}},{"line":50,"address":[906243],"length":1,"stats":{"Line":0}},{"line":51,"address":[906319],"length":1,"stats":{"Line":0}},{"line":52,"address":[906334],"length":1,"stats":{"Line":0}},{"line":58,"address":[],"length":0,"stats":{"Line":0}},{"line":59,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[],"length":0,"stats":{"Line":0}},{"line":61,"address":[],"length":0,"stats":{"Line":0}},{"line":62,"address":[],"length":0,"stats":{"Line":0}},{"line":63,"address":[],"length":0,"stats":{"Line":0}},{"line":64,"address":[],"length":0,"stats":{"Line":0}},{"line":68,"address":[906352],"length":1,"stats":{"Line":0}},{"line":69,"address":[906376,906438],"length":1,"stats":{"Line":0}},{"line":70,"address":[906403],"length":1,"stats":{"Line":0}},{"line":74,"address":[906418],"length":1,"stats":{"Line":0}},{"line":75,"address":[906472],"length":1,"stats":{"Line":0}},{"line":76,"address":[906482],"length":1,"stats":{"Line":0}},{"line":77,"address":[906489],"length":1,"stats":{"Line":0}},{"line":78,"address":[906496],"length":1,"stats":{"Line":0}},{"line":79,"address":[906504],"length":1,"stats":{"Line":0}},{"line":80,"address":[906549],"length":1,"stats":{"Line":0}},{"line":81,"address":[906510,906563],"length":1,"stats":{"Line":0}},{"line":86,"address":[906459],"length":1,"stats":{"Line":0}},{"line":92,"address":[906592],"length":1,"stats":{"Line":0}},{"line":93,"address":[906600],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":30},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmatelnet","src","bin","gemmatelnetd.rs"],"content":"use std::io::{BufRead, BufReader, Write};\nuse std::net::{TcpListener, TcpStream};\nuse std::thread;\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\nuse gemma::chip8::quirk_modes::QuirkMode;\nuse gemmatelnet::client_handler::handle_client;\nuse log::info;\nuse clap::Parser;\n\nconst LISTEN_HOST: \u0026str = \"0.0.0.0\";\nconst LISTEN_PORT: u32 = 1420;\n\n#[derive(Parser, Debug)]\n#[command(name = \"myapp\")]\n#[command(about = \"Does awesome things\", long_about = None)]\nstruct Cli {\n #[arg(long, default_value = \"0.0.0.0\")]\n host: Option\u003cString\u003e,\n #[arg(long, default_value = \"1420\")]\n port: Option\u003cu32\u003e,\n}\n\nimpl Cli {\n pub fn host(\u0026self) -\u003e String {\n self.host.clone().unwrap_or(LISTEN_HOST.into())\n }\n pub fn port(\u0026self) -\u003e u32 {\n self.port.unwrap_or(LISTEN_PORT.into())\n }\n}\n\nfn main() -\u003e std::io::Result\u003c()\u003e {\n let config = Cli::parse();\n\n let listener = TcpListener::bind(format!(\"{}:{}\", config.host(), config.port()))?;\n println!(\"Listening to {}:{}\", LISTEN_HOST, LISTEN_PORT);\n\n for stream in listener.incoming() {\n match stream {\n Ok(stream) =\u003e {\n thread::spawn(|| handle_client(stream));\n }\n Err(e) =\u003e {\n eprintln!(\"Connection Failed -\u003e {}\", e);\n }\n }\n }\n\n Ok(())\n}","traces":[{"line":24,"address":[591136,591281,591307],"length":1,"stats":{"Line":0}},{"line":25,"address":[591291,591155],"length":1,"stats":{"Line":0}},{"line":27,"address":[591328],"length":1,"stats":{"Line":0}},{"line":28,"address":[591337],"length":1,"stats":{"Line":0}},{"line":32,"address":[591392,592557,592523],"length":1,"stats":{"Line":0}},{"line":33,"address":[591409],"length":1,"stats":{"Line":0}},{"line":35,"address":[591923,592537,591427,591848,591471],"length":1,"stats":{"Line":0}},{"line":36,"address":[591897,591999],"length":1,"stats":{"Line":0}},{"line":38,"address":[592228,592115],"length":1,"stats":{"Line":0}},{"line":39,"address":[592260],"length":1,"stats":{"Line":0}},{"line":40,"address":[592298],"length":1,"stats":{"Line":0}},{"line":41,"address":[592366,592320],"length":1,"stats":{"Line":0}},{"line":43,"address":[592327],"length":1,"stats":{"Line":0}},{"line":44,"address":[592359,592432],"length":1,"stats":{"Line":0}},{"line":49,"address":[592207],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":15},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmatelnet","src","client_handler.rs"],"content":"use std::io::{BufRead, BufReader};\nuse std::net::TcpStream;\nuse gemma::chip8::computer_manager::Chip8ComputerManager;\nuse gemma::chip8::quirk_modes::QuirkMode;\nuse crate::telnet_utils::list_of_files_in_directory;\nuse std::io::Write;\nuse std::path::Path;\nuse gemma::constants::RESOURCES_ROOT;\nuse crate::constants::*;\n\nconst ERROR_CANT_NOTIFY: \u0026str = \"Unable to notify client\";\nconst MENU_TEXT: \u0026str = r#\"\n---------------------------\nstatus -\u003e Current status of the Instance\nnew -\u003e Start New Instance\n* list -\u003e list available files to load into Chip-8 Instance\n* load \u003cfilename\u003e -\u003e Load Chip-8 Program from server\nhelp -\u003e display this help\nstep -\u003e step chip-8 machine 1 step\n* steps \u003cnumber\u003e -\u003e Step a fixed number of steps\n* memory -\u003e dump current memory from chip-8 instance\n* video -\u003e dump current video from chip-8 instance\n* window \u003cstart\u003e \u003clength\u003e -\u003e display a window of memory\ndisconnect -\u003e close connection\n---------------------------\n\"#;\n\npub fn handle_client(mut stream: TcpStream) {\n // each client gets their own instance of a Chip-8 system\n let mut chip8 = Chip8ComputerManager::default();\n\n let peer_addr = stream.peer_addr().unwrap();\n println!(\"New CX From {}\", peer_addr);\n\n let mut reader = BufReader::new(stream.try_clone().unwrap());\n let mut line = String::new();\n\n write!(stream, \"Welcome to Gemma Telnet Server\\n\").unwrap();\n write!(stream, \"{}\", MENU_TEXT).unwrap();\n\n let mut time_to_die: bool = false;\n\n while !time_to_die {\n\n // clear the buffer\n line.clear();\n\n // read from the client to the buffer\n let bytes = reader.read_line(\u0026mut line).unwrap();\n if bytes == 0 {\n println!(\"Connection closed by {}\", peer_addr);\n break;\n }\n\n // display the message received\n eprint!(\"RX {}: {}\", peer_addr, line);\n\n // split the line into \u003ccommand\u003e \u003cparam1\u003e \u003cparam2\u003e (if params exist)\n\n let mut result = line.split(\" \");\n let command = result.next().unwrap();\n let p1 = result.next();\n let p2 = result.next();\n\n let mut message_to_send: String = \"Unrecognized Command\".to_string();\n\n match command.trim() {\n CMD_STATUS =\u003e {\n message_to_send = chip8.status_as_string();\n }\n CMD_DISCONNECT =\u003e {\n message_to_send = \"Disconnecting User\".to_string();\n time_to_die = true;\n break;\n }\n CMD_NEW =\u003e {\n message_to_send = \"Starting new Chip-8 Instance\".to_string();\n chip8.reset(QuirkMode::Chip8);\n }\n CMD_LIST =\u003e {\n message_to_send =\n format!(\"Listing files in path: \\n{}\",\n list_of_files_in_directory(\n \u0026mut Path::new(\"/home/tmerritt/Projects/trevors_chip8_toy/resources/roms\".into())\n )\n );\n }\n CMD_LOAD =\u003e {\n let file_to_use = p1.unwrap_or(\"default.ch8\").trim();\n // chip8 = Chip8ComputerManager::from(chip8).load_new_program_to_system_memory()\n message_to_send = format!(\"Preparing to load {} from disk.\",\n file_to_use);\n chip8.load_new_program_from_disk_to_system_memory(\n Path::new(format!(\"{}/roms/{}\", RESOURCES_ROOT, file_to_use).as_str())\n );\n }\n CMD_HELP =\u003e {\n message_to_send = MENU_TEXT.to_string();\n }\n CMD_STEPS =\u003e {\n if chip8.core_should_run {\n let num_steps = p1.unwrap_or(\"0\").trim();\n message_to_send = format!(\"Advancing {} steps.\", num_steps);\n for _ in 0..num_steps.parse().unwrap_or(0) {\n chip8.tick();\n }\n } else {\n message_to_send = \"Not Ready to Step\".to_string();\n }\n }\n CMD_STEP =\u003e {\n if chip8.core_should_run {\n message_to_send = \"Advancing 1 step.\".to_string();\n chip8.tick();\n } else {\n message_to_send = \"Not Ready To Step\".to_string();\n }\n }\n CMD_WINDOW =\u003e {\n message_to_send = format!(\"Memory window from {} to {}\",\n p1.unwrap_or(\"0x0000\").trim(),\n p2.unwrap_or(\"0xFFFF\").trim());\n }\n _ =\u003e {}\n }\n\n write!(stream, \"{}\\n\", message_to_send).expect(ERROR_CANT_NOTIFY);\n\n stream.flush().unwrap();\n }\n}\n","traces":[{"line":28,"address":[2427085,2425658,2419536],"length":1,"stats":{"Line":0}},{"line":30,"address":[2419550],"length":1,"stats":{"Line":0}},{"line":32,"address":[2419613],"length":1,"stats":{"Line":0}},{"line":33,"address":[2419985],"length":1,"stats":{"Line":0}},{"line":35,"address":[2420299,2420081],"length":1,"stats":{"Line":0}},{"line":36,"address":[2420326],"length":1,"stats":{"Line":0}},{"line":38,"address":[2420377,2420546],"length":1,"stats":{"Line":0}},{"line":39,"address":[2420768],"length":1,"stats":{"Line":0}},{"line":41,"address":[2421066],"length":1,"stats":{"Line":0}},{"line":43,"address":[473082],"length":1,"stats":{"Line":0}},{"line":46,"address":[2421092],"length":1,"stats":{"Line":0}},{"line":49,"address":[473393,473145],"length":1,"stats":{"Line":0}},{"line":50,"address":[2421397],"length":1,"stats":{"Line":0}},{"line":51,"address":[2421403,2421457],"length":1,"stats":{"Line":0}},{"line":56,"address":[2421430,2421531],"length":1,"stats":{"Line":0}},{"line":60,"address":[2421651],"length":1,"stats":{"Line":0}},{"line":61,"address":[2421722,2421894],"length":1,"stats":{"Line":0}},{"line":62,"address":[2421932],"length":1,"stats":{"Line":0}},{"line":63,"address":[2422021],"length":1,"stats":{"Line":0}},{"line":65,"address":[2422110],"length":1,"stats":{"Line":0}},{"line":67,"address":[2422272,2422157],"length":1,"stats":{"Line":0}},{"line":68,"address":[2422294,2426679],"length":1,"stats":{"Line":0}},{"line":69,"address":[2422375,2426537],"length":1,"stats":{"Line":0}},{"line":71,"address":[474345,474408],"length":1,"stats":{"Line":0}},{"line":72,"address":[2426381,2422460],"length":1,"stats":{"Line":0}},{"line":73,"address":[2426507],"length":1,"stats":{"Line":0}},{"line":76,"address":[2422501,2422434],"length":1,"stats":{"Line":0}},{"line":77,"address":[2426215,2422549],"length":1,"stats":{"Line":0}},{"line":78,"address":[2426341],"length":1,"stats":{"Line":0}},{"line":80,"address":[2422523,2422590,2426210],"length":1,"stats":{"Line":0}},{"line":81,"address":[2426162,2426068],"length":1,"stats":{"Line":0}},{"line":82,"address":[2425765],"length":1,"stats":{"Line":0}},{"line":83,"address":[2425746],"length":1,"stats":{"Line":0}},{"line":84,"address":[2422638,2425683],"length":1,"stats":{"Line":0}},{"line":88,"address":[2422694,2422612],"length":1,"stats":{"Line":0}},{"line":89,"address":[2422758,2425000],"length":1,"stats":{"Line":0}},{"line":91,"address":[477175,477269,477027],"length":1,"stats":{"Line":0}},{"line":92,"address":[2425258],"length":1,"stats":{"Line":0}},{"line":93,"address":[2425617],"length":1,"stats":{"Line":0}},{"line":94,"address":[2425515,2425317,2425586],"length":1,"stats":{"Line":0}},{"line":97,"address":[476963,474799,474708],"length":1,"stats":{"Line":0}},{"line":98,"address":[2424837,2422855],"length":1,"stats":{"Line":0}},{"line":100,"address":[474821,474888],"length":1,"stats":{"Line":0}},{"line":101,"address":[2422944,2424263],"length":1,"stats":{"Line":0}},{"line":102,"address":[476276,476071],"length":1,"stats":{"Line":0}},{"line":103,"address":[2424475,2424327],"length":1,"stats":{"Line":0}},{"line":104,"address":[2424601],"length":1,"stats":{"Line":0}},{"line":105,"address":[2424816],"length":1,"stats":{"Line":0}},{"line":108,"address":[476113,476024],"length":1,"stats":{"Line":0}},{"line":111,"address":[2422918,2422970],"length":1,"stats":{"Line":0}},{"line":112,"address":[475869,475010],"length":1,"stats":{"Line":0}},{"line":113,"address":[2423882,2423701],"length":1,"stats":{"Line":0}},{"line":114,"address":[476000],"length":1,"stats":{"Line":0}},{"line":116,"address":[2423735,2423670],"length":1,"stats":{"Line":0}},{"line":119,"address":[2422992,2423044,2423665],"length":1,"stats":{"Line":0}},{"line":120,"address":[2423523,2423359,2423206,2423617],"length":1,"stats":{"Line":0}},{"line":121,"address":[2423096],"length":1,"stats":{"Line":0}},{"line":122,"address":[2423590,2423249],"length":1,"stats":{"Line":0}},{"line":127,"address":[2423050,2426684],"length":1,"stats":{"Line":0}},{"line":129,"address":[2426809],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":60},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmatelnet","src","constants.rs"],"content":"pub const CMD_NEW: \u0026str = \"new\";\npub const CMD_LIST: \u0026str = \"list\";\npub const CMD_LOAD: \u0026str = \"load\";\npub const CMD_STATUS: \u0026str = \"status\";\npub const CMD_DISCONNECT: \u0026str = \"disconnect\";\npub const CMD_HELP: \u0026str = \"help\";\npub const CMD_STEPS: \u0026str = \"steps\";\npub const CMD_STEP: \u0026str = \"step\";\npub const CMD_WINDOW: \u0026str = \"window\";\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmatelnet","src","lib.rs"],"content":"pub mod telnet_utils;\npub mod client_handler;\npub mod constants;\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmatelnet","src","telnet_utils.rs"],"content":"use std::{fs, io};\nuse std::path::Path;\n\npub fn list_of_files_in_directory(root_directory: \u0026Path) -\u003e String {\n\n let mut return_value = String::new();\n\n if root_directory.is_dir() {\n for entry in fs::read_dir(root_directory).unwrap() {\n let entry = entry.unwrap();\n let path = entry.path();\n if path.is_file() {\n return_value = format!(\"{}\\n{}\", return_value, path.file_name().unwrap().to_string_lossy());\n }\n }\n }\n return_value\n}","traces":[{"line":4,"address":[2435008,2436802],"length":1,"stats":{"Line":0}},{"line":6,"address":[2435071],"length":1,"stats":{"Line":0}},{"line":8,"address":[2435092,2435181],"length":1,"stats":{"Line":0}},{"line":9,"address":[2435262,2435500,2435643,2435676],"length":1,"stats":{"Line":0}},{"line":11,"address":[2435905],"length":1,"stats":{"Line":0}},{"line":12,"address":[2436775,2436052,2435984],"length":1,"stats":{"Line":0}},{"line":13,"address":[2436307,2436099,2436633],"length":1,"stats":{"Line":0}},{"line":17,"address":[2435203],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":8},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmautil","src","bin","bin2hex.rs"],"content":"use clap::{Arg, Command};\nuse std::fs::File;\nuse std::io;\nuse std::io::{BufReader, Read};\n\n#[derive(Debug)]\nstruct CliArgs {\n input: String,\n}\n\nfn main() {\n println!(\"Taxation is Theft!\");\n // Set up the command line arguments\n let matches = Command::new(\"my_program\")\n .about(\"Processes an input file and outputs it with a specified bit width\")\n .arg(\n Arg::new(\"input\")\n .help(\"The input file to process\")\n .required(true)\n .index(1),\n )\n .get_matches();\n\n // Parse the command-line arguments\n let args = CliArgs {\n input: matches.get_one::\u003cString\u003e(\"input\").unwrap().to_string(),\n };\n\n // Use the parsed arguments\n println!(\"Input file: {}\", args.input);\n\n // behave like a shift register and load each character from the file 1 by 1.\n let results = read_file_to_bools(\u0026args.input);\n for result in results.unwrap().bytes() {\n print!(\"0x{:02x}, \", result.unwrap());\n }\n}\nfn read_file_to_bools(file_path: \u0026str) -\u003e io::Result\u003cVec\u003cu8\u003e\u003e {\n // Open the file\n let file = File::open(file_path)?;\n let mut reader = BufReader::new(file);\n\n let mut bytes = Vec::new();\n reader.read_to_end(\u0026mut bytes)?;\n\n let mut output = Vec::new();\n let mut current_byte = 0u8;\n let mut bit_index = 0;\n\n for \u0026byte in \u0026bytes {\n // Convert ASCII character '1' or '0' to boolean, skip any other characters\n let bit = match byte {\n b'1' =\u003e true,\n b'0' =\u003e false,\n _ =\u003e continue, // Skip non-'1' or '0' characters\n };\n\n // Set the appropriate bit in the current byte\n if bit {\n current_byte |= 1 \u003c\u003c (7 - bit_index); // Set the bit at the correct position\n }\n\n bit_index += 1;\n\n // Once we have filled 8 bits, push the byte and reset\n if bit_index == 8 {\n output.push(current_byte);\n current_byte = 0;\n bit_index = 0;\n }\n }\n\n // If there are remaining bits, push the last byte (it will be partially filled)\n if bit_index \u003e 0 {\n output.push(current_byte);\n }\n\n Ok(output)\n}\n","traces":[{"line":11,"address":[255306,253408],"length":1,"stats":{"Line":0}},{"line":12,"address":[253430],"length":1,"stats":{"Line":0}},{"line":14,"address":[253811,253487,253736],"length":1,"stats":{"Line":0}},{"line":17,"address":[253641,253569],"length":1,"stats":{"Line":0}},{"line":26,"address":[253824,253999],"length":1,"stats":{"Line":0}},{"line":30,"address":[254038,254131],"length":1,"stats":{"Line":0}},{"line":33,"address":[254208],"length":1,"stats":{"Line":0}},{"line":34,"address":[254583,254480,254723],"length":1,"stats":{"Line":0}},{"line":35,"address":[254978],"length":1,"stats":{"Line":0}},{"line":38,"address":[256518,256462,255360],"length":1,"stats":{"Line":0}},{"line":40,"address":[255491,255393],"length":1,"stats":{"Line":0}},{"line":41,"address":[255459],"length":1,"stats":{"Line":0}},{"line":43,"address":[255570],"length":1,"stats":{"Line":0}},{"line":44,"address":[255792,255698,255630],"length":1,"stats":{"Line":0}},{"line":46,"address":[255780],"length":1,"stats":{"Line":0}},{"line":47,"address":[255825],"length":1,"stats":{"Line":0}},{"line":48,"address":[255833],"length":1,"stats":{"Line":0}},{"line":50,"address":[255923,256017,255852],"length":1,"stats":{"Line":0}},{"line":52,"address":[256038],"length":1,"stats":{"Line":0}},{"line":53,"address":[256213],"length":1,"stats":{"Line":0}},{"line":54,"address":[256223],"length":1,"stats":{"Line":0}},{"line":59,"address":[256231,256340],"length":1,"stats":{"Line":0}},{"line":60,"address":[256261,256342],"length":1,"stats":{"Line":0}},{"line":63,"address":[256241,256386,256364],"length":1,"stats":{"Line":0}},{"line":66,"address":[256371,256457],"length":1,"stats":{"Line":0}},{"line":67,"address":[256404],"length":1,"stats":{"Line":0}},{"line":68,"address":[256438],"length":1,"stats":{"Line":0}},{"line":69,"address":[256446],"length":1,"stats":{"Line":0}},{"line":74,"address":[256005],"length":1,"stats":{"Line":0}},{"line":75,"address":[256141],"length":1,"stats":{"Line":0}},{"line":78,"address":[256067],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":31},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmautil","src","bin","ch8asm.rs"],"content":"use gemma::chip8::util::InstructionUtil;\nuse gemma::chip8::{instructions::Chip8CpuInstructions, quirk_modes::QuirkMode};\nuse std::fs::{self, File};\nuse std::io::Write;\n// Ch8Asm\n// Converts well formed CH8ASM.\n// no variables.\n// no labels.\n// nothing fun.\n// VALID FORMAT\n// \u003cINSTRUCTION\u003e \u003c0x|0X\u003e\u003cparameter\u003e[, ][\u003c0x|OX\u003e\u003cparameter\u003e[, ][\u003c0x|OX\u003e\u003cparameter\u003e]][; comment]\nuse pest::Parser;\nuse pest_derive::Parser;\n#[derive(Parser)]\n#[grammar = \"chip8_asm.pest\"]\npub struct Chip8AsmParser;\n\nfn main() {\n println!(\"Taxation is Theft\");\n\n let unparsed = fs::read_to_string(\"resources/test/gemma_disassembler_manual_document.asc\")\n .expect(\"Unable to read input\");\n\n let file = Chip8AsmParser::parse(Rule::file, \u0026unparsed)\n .expect(\"Unable to parse. Try again.\")\n .next()\n .unwrap();\n\n println!(\"PREPARING TO WRITE THE TARGET FILE...\");\n let mut target_file = File::create(\"thisismytarget.ch8\").expect(\"Unable to create target\");\n\n for record in file.into_inner() {\n // let mut working_instruction = Chip8CpuInstructions::XXXXERRORINSTRUCTION;\n match record.as_rule() {\n Rule::record =\u003e {\n print!(\"record = {:?}\\t\", record.as_str());\n let x = Chip8CpuInstructions::from_str(record.as_str());\n println!(\"DECODED TO {:?} {:04x}\", x, x.encode());\n let (high, low) = InstructionUtil::split_bytes(x.encode());\n target_file\n .write_all(\u0026[high, low])\n .expect(\"Unable to write to the file.\");\n }\n _ =\u003e {}\n }\n }\n}\n","traces":[{"line":18,"address":[705528,705501,703472],"length":1,"stats":{"Line":0}},{"line":19,"address":[703479],"length":1,"stats":{"Line":0}},{"line":21,"address":[703530],"length":1,"stats":{"Line":0}},{"line":24,"address":[703692,703607],"length":1,"stats":{"Line":0}},{"line":29,"address":[703951],"length":1,"stats":{"Line":0}},{"line":30,"address":[703996],"length":1,"stats":{"Line":0}},{"line":32,"address":[704072,704351,704211,704307],"length":1,"stats":{"Line":0}},{"line":34,"address":[704407,704511],"length":1,"stats":{"Line":0}},{"line":36,"address":[704585,704540],"length":1,"stats":{"Line":0}},{"line":37,"address":[704705],"length":1,"stats":{"Line":0}},{"line":38,"address":[704788],"length":1,"stats":{"Line":0}},{"line":39,"address":[705345],"length":1,"stats":{"Line":0}},{"line":40,"address":[705453],"length":1,"stats":{"Line":0}},{"line":41,"address":[705418],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":14},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmautil","src","bin","ch8disasm.rs"],"content":"use std::path::Path;\nuse clap::{command, Parser};\nuse gemma::chip8::instructions::Chip8CpuInstructions;\nuse gemma::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;\nuse gemma::chip8::quirk_modes::QuirkMode::Chip8;\n\n/// ch8disasm\n///\n/// Provide disassembled version of the CH8 file provided.\n#[derive(Parser)]\n#[command(version, about, long_about = None)]\nstruct DisassemblerApp {\n #[arg(short)]\n input_file: Box\u003cPath\u003e,\n #[arg(short)]\n output_file: Option\u003cBox\u003cPath\u003e\u003e,\n}\n\nstruct Disassembler {}\nimpl Disassembler {\n pub fn disassemble(from_data: Vec\u003cu8\u003e) -\u003e String {\n let mut working_instruction: u16 = 0x0000;\n\n // read the input data and loop through it byte by byte.\n let mut output_string = String::new();\n\n for (offset, byte) in from_data.iter().enumerate() {\n working_instruction = (working_instruction \u003c\u003c 8) | (*byte as u16);\n if offset % 2 != 0 {\n let decoded = Chip8CpuInstructions::decode(working_instruction, \u0026Chip8);\n let decoded_string = decoded.to_string();\n let mut current_parts = String::new();\n\n match decoded {\n XXXXERRORINSTRUCTION =\u003e {\n current_parts = format!(\"DW 0x{:04x}\", working_instruction);\n }\n _ =\u003e {\n current_parts = format!(\"{}\", decoded);\n }\n };\n\n let target_length: i32 = 25;\n let spacing_length = target_length.saturating_sub(current_parts.len() as i32);\n\n // now add the rest after the string\n let x = spacing_length as usize;\n current_parts = format!(\"{}{:\u003cx$}; Bytes [0x{:04x}] offset [0x{:04x}]\\n\", current_parts, \" \", working_instruction, offset -1);\n // println!(\"SHOULD OUTPUT: [{current_parts}]\");\n output_string = output_string.to_string() + \u0026*current_parts.to_string();\n\n working_instruction = 0x0000;\n }\n }\n\n output_string\n }\n}\n\nfn main() {\n println!(\"Taxation is Theft\");\n let result = DisassemblerApp::parse();\n println!(\"PREPARING TO DISASSEMBLE {:?}\", result.input_file.file_name());\n\n\n let source_file = result.input_file.to_str().unwrap();\n let decompiled_program = Disassembler::disassemble(\n std::fs::read(source_file).unwrap()\n );\n\n match result.output_file {\n None =\u003e {\n println!(\"Output to console.\");\n println!(\"OS: \\n\\n{}\", decompiled_program);\n }\n Some(target) =\u003e {\n println!(\"Output to {:?}\", target);\n std::fs::write(target, decompiled_program).unwrap();\n }\n }\n}","traces":[{"line":21,"address":[598848,602279],"length":1,"stats":{"Line":0}},{"line":22,"address":[598890],"length":1,"stats":{"Line":0}},{"line":25,"address":[598916],"length":1,"stats":{"Line":0}},{"line":27,"address":[598975,599341,599059],"length":1,"stats":{"Line":0}},{"line":28,"address":[599383],"length":1,"stats":{"Line":0}},{"line":29,"address":[599429],"length":1,"stats":{"Line":0}},{"line":30,"address":[599443],"length":1,"stats":{"Line":0}},{"line":31,"address":[599514],"length":1,"stats":{"Line":0}},{"line":32,"address":[599529],"length":1,"stats":{"Line":0}},{"line":34,"address":[599580],"length":1,"stats":{"Line":0}},{"line":36,"address":[599594,600034,599691],"length":1,"stats":{"Line":0}},{"line":38,"address":[600462],"length":1,"stats":{"Line":0}},{"line":39,"address":[599637,600320,600199],"length":1,"stats":{"Line":0}},{"line":43,"address":[598879],"length":1,"stats":{"Line":0}},{"line":44,"address":[600472,600540,600184],"length":1,"stats":{"Line":0}},{"line":47,"address":[600547],"length":1,"stats":{"Line":0}},{"line":48,"address":[600557,601706],"length":1,"stats":{"Line":0}},{"line":50,"address":[602238,601848],"length":1,"stats":{"Line":0}},{"line":52,"address":[602187],"length":1,"stats":{"Line":0}},{"line":56,"address":[599284],"length":1,"stats":{"Line":0}},{"line":60,"address":[603905,602304,603835],"length":1,"stats":{"Line":0}},{"line":61,"address":[602311],"length":1,"stats":{"Line":0}},{"line":62,"address":[602375],"length":1,"stats":{"Line":0}},{"line":63,"address":[602503,602396],"length":1,"stats":{"Line":0}},{"line":66,"address":[602763,602611],"length":1,"stats":{"Line":0}},{"line":68,"address":[602787],"length":1,"stats":{"Line":0}},{"line":71,"address":[603052],"length":1,"stats":{"Line":0}},{"line":73,"address":[603222,603081],"length":1,"stats":{"Line":0}},{"line":74,"address":[603241],"length":1,"stats":{"Line":0}},{"line":76,"address":[603107],"length":1,"stats":{"Line":0}},{"line":77,"address":[603419,603171],"length":1,"stats":{"Line":0}},{"line":78,"address":[603488],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":32},{"path":["/","home","tmerritt","Projects","trevors_chip8_toy","gemmautil","tests","assmber_tests.rs"],"content":"","traces":[],"covered":0,"coverable":0}]};
</script>
<script crossorigin>/** @license React v16.13.1
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';(function(d,r){"object"===typeof exports&&"undefined"!==typeof module?r(exports):"function"===typeof define&&define.amd?define(["exports"],r):(d=d||self,r(d.React={}))})(this,function(d){function r(a){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;c<arguments.length;c++)b+="&args[]="+encodeURIComponent(arguments[c]);return"Minified React error #"+a+"; visit "+b+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}
function w(a,b,c){this.props=a;this.context=b;this.refs=ba;this.updater=c||ca}function da(){}function L(a,b,c){this.props=a;this.context=b;this.refs=ba;this.updater=c||ca}function ea(a,b,c){var g,e={},fa=null,d=null;if(null!=b)for(g in void 0!==b.ref&&(d=b.ref),void 0!==b.key&&(fa=""+b.key),b)ha.call(b,g)&&!ia.hasOwnProperty(g)&&(e[g]=b[g]);var h=arguments.length-2;if(1===h)e.children=c;else if(1<h){for(var k=Array(h),f=0;f<h;f++)k[f]=arguments[f+2];e.children=k}if(a&&a.defaultProps)for(g in h=a.defaultProps,
h)void 0===e[g]&&(e[g]=h[g]);return{$$typeof:x,type:a,key:fa,ref:d,props:e,_owner:M.current}}function va(a,b){return{$$typeof:x,type:a.type,key:b,ref:a.ref,props:a.props,_owner:a._owner}}function N(a){return"object"===typeof a&&null!==a&&a.$$typeof===x}function wa(a){var b={"=":"=0",":":"=2"};return"$"+(""+a).replace(/[=:]/g,function(a){return b[a]})}function ja(a,b,c,g){if(C.length){var e=C.pop();e.result=a;e.keyPrefix=b;e.func=c;e.context=g;e.count=0;return e}return{result:a,keyPrefix:b,func:c,
context:g,count:0}}function ka(a){a.result=null;a.keyPrefix=null;a.func=null;a.context=null;a.count=0;10>C.length&&C.push(a)}function O(a,b,c,g){var e=typeof a;if("undefined"===e||"boolean"===e)a=null;var d=!1;if(null===a)d=!0;else switch(e){case "string":case "number":d=!0;break;case "object":switch(a.$$typeof){case x:case xa:d=!0}}if(d)return c(g,a,""===b?"."+P(a,0):b),1;d=0;b=""===b?".":b+":";if(Array.isArray(a))for(var f=0;f<a.length;f++){e=a[f];var h=b+P(e,f);d+=O(e,h,c,g)}else if(null===a||
"object"!==typeof a?h=null:(h=la&&a[la]||a["@@iterator"],h="function"===typeof h?h:null),"function"===typeof h)for(a=h.call(a),f=0;!(e=a.next()).done;)e=e.value,h=b+P(e,f++),d+=O(e,h,c,g);else if("object"===e)throw c=""+a,Error(r(31,"[object Object]"===c?"object with keys {"+Object.keys(a).join(", ")+"}":c,""));return d}function Q(a,b,c){return null==a?0:O(a,"",b,c)}function P(a,b){return"object"===typeof a&&null!==a&&null!=a.key?wa(a.key):b.toString(36)}function ya(a,b,c){a.func.call(a.context,b,
a.count++)}function za(a,b,c){var g=a.result,e=a.keyPrefix;a=a.func.call(a.context,b,a.count++);Array.isArray(a)?R(a,g,c,function(a){return a}):null!=a&&(N(a)&&(a=va(a,e+(!a.key||b&&b.key===a.key?"":(""+a.key).replace(ma,"$&/")+"/")+c)),g.push(a))}function R(a,b,c,g,e){var d="";null!=c&&(d=(""+c).replace(ma,"$&/")+"/");b=ja(b,d,g,e);Q(a,za,b);ka(b)}function t(){var a=na.current;if(null===a)throw Error(r(321));return a}function S(a,b){var c=a.length;a.push(b);a:for(;;){var g=c-1>>>1,e=a[g];if(void 0!==
e&&0<D(e,b))a[g]=b,a[c]=e,c=g;else break a}}function n(a){a=a[0];return void 0===a?null:a}function E(a){var b=a[0];if(void 0!==b){var c=a.pop();if(c!==b){a[0]=c;a:for(var g=0,e=a.length;g<e;){var d=2*(g+1)-1,f=a[d],h=d+1,k=a[h];if(void 0!==f&&0>D(f,c))void 0!==k&&0>D(k,f)?(a[g]=k,a[h]=c,g=h):(a[g]=f,a[d]=c,g=d);else if(void 0!==k&&0>D(k,c))a[g]=k,a[h]=c,g=h;else break a}}return b}return null}function D(a,b){var c=a.sortIndex-b.sortIndex;return 0!==c?c:a.id-b.id}function F(a){for(var b=n(u);null!==
b;){if(null===b.callback)E(u);else if(b.startTime<=a)E(u),b.sortIndex=b.expirationTime,S(p,b);else break;b=n(u)}}function T(a){y=!1;F(a);if(!v)if(null!==n(p))v=!0,z(U);else{var b=n(u);null!==b&&G(T,b.startTime-a)}}function U(a,b){v=!1;y&&(y=!1,V());H=!0;var c=m;try{F(b);for(l=n(p);null!==l&&(!(l.expirationTime>b)||a&&!W());){var g=l.callback;if(null!==g){l.callback=null;m=l.priorityLevel;var e=g(l.expirationTime<=b);b=q();"function"===typeof e?l.callback=e:l===n(p)&&E(p);F(b)}else E(p);l=n(p)}if(null!==
l)var d=!0;else{var f=n(u);null!==f&&G(T,f.startTime-b);d=!1}return d}finally{l=null,m=c,H=!1}}function oa(a){switch(a){case 1:return-1;case 2:return 250;case 5:return 1073741823;case 4:return 1E4;default:return 5E3}}var f="function"===typeof Symbol&&Symbol.for,x=f?Symbol.for("react.element"):60103,xa=f?Symbol.for("react.portal"):60106,Aa=f?Symbol.for("react.fragment"):60107,Ba=f?Symbol.for("react.strict_mode"):60108,Ca=f?Symbol.for("react.profiler"):60114,Da=f?Symbol.for("react.provider"):60109,
Ea=f?Symbol.for("react.context"):60110,Fa=f?Symbol.for("react.forward_ref"):60112,Ga=f?Symbol.for("react.suspense"):60113,Ha=f?Symbol.for("react.memo"):60115,Ia=f?Symbol.for("react.lazy"):60116,la="function"===typeof Symbol&&Symbol.iterator,pa=Object.getOwnPropertySymbols,Ja=Object.prototype.hasOwnProperty,Ka=Object.prototype.propertyIsEnumerable,I=function(){try{if(!Object.assign)return!1;var a=new String("abc");a[5]="de";if("5"===Object.getOwnPropertyNames(a)[0])return!1;var b={};for(a=0;10>a;a++)b["_"+
String.fromCharCode(a)]=a;if("0123456789"!==Object.getOwnPropertyNames(b).map(function(a){return b[a]}).join(""))return!1;var c={};"abcdefghijklmnopqrst".split("").forEach(function(a){c[a]=a});return"abcdefghijklmnopqrst"!==Object.keys(Object.assign({},c)).join("")?!1:!0}catch(g){return!1}}()?Object.assign:function(a,b){if(null===a||void 0===a)throw new TypeError("Object.assign cannot be called with null or undefined");var c=Object(a);for(var g,e=1;e<arguments.length;e++){var d=Object(arguments[e]);
for(var f in d)Ja.call(d,f)&&(c[f]=d[f]);if(pa){g=pa(d);for(var h=0;h<g.length;h++)Ka.call(d,g[h])&&(c[g[h]]=d[g[h]])}}return c},ca={isMounted:function(a){return!1},enqueueForceUpdate:function(a,b,c){},enqueueReplaceState:function(a,b,c,d){},enqueueSetState:function(a,b,c,d){}},ba={};w.prototype.isReactComponent={};w.prototype.setState=function(a,b){if("object"!==typeof a&&"function"!==typeof a&&null!=a)throw Error(r(85));this.updater.enqueueSetState(this,a,b,"setState")};w.prototype.forceUpdate=
function(a){this.updater.enqueueForceUpdate(this,a,"forceUpdate")};da.prototype=w.prototype;f=L.prototype=new da;f.constructor=L;I(f,w.prototype);f.isPureReactComponent=!0;var M={current:null},ha=Object.prototype.hasOwnProperty,ia={key:!0,ref:!0,__self:!0,__source:!0},ma=/\/+/g,C=[],na={current:null},X;if("undefined"===typeof window||"function"!==typeof MessageChannel){var A=null,qa=null,ra=function(){if(null!==A)try{var a=q();A(!0,a);A=null}catch(b){throw setTimeout(ra,0),b;}},La=Date.now();var q=
function(){return Date.now()-La};var z=function(a){null!==A?setTimeout(z,0,a):(A=a,setTimeout(ra,0))};var G=function(a,b){qa=setTimeout(a,b)};var V=function(){clearTimeout(qa)};var W=function(){return!1};f=X=function(){}}else{var Y=window.performance,sa=window.Date,Ma=window.setTimeout,Na=window.clearTimeout;"undefined"!==typeof console&&(f=window.cancelAnimationFrame,"function"!==typeof window.requestAnimationFrame&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"),
"function"!==typeof f&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"));if("object"===typeof Y&&"function"===typeof Y.now)q=function(){return Y.now()};else{var Oa=sa.now();q=function(){return sa.now()-Oa}}var J=!1,K=null,Z=-1,ta=5,ua=0;W=function(){return q()>=ua};f=function(){};X=function(a){0>a||125<a?console.error("forceFrameRate takes a positive int between 0 and 125, forcing framerates higher than 125 fps is not unsupported"):
ta=0<a?Math.floor(1E3/a):5};var B=new MessageChannel,aa=B.port2;B.port1.onmessage=function(){if(null!==K){var a=q();ua=a+ta;try{K(!0,a)?aa.postMessage(null):(J=!1,K=null)}catch(b){throw aa.postMessage(null),b;}}else J=!1};z=function(a){K=a;J||(J=!0,aa.postMessage(null))};G=function(a,b){Z=Ma(function(){a(q())},b)};V=function(){Na(Z);Z=-1}}var p=[],u=[],Pa=1,l=null,m=3,H=!1,v=!1,y=!1,Qa=0;B={ReactCurrentDispatcher:na,ReactCurrentOwner:M,IsSomeRendererActing:{current:!1},assign:I};I(B,{Scheduler:{__proto__:null,
unstable_ImmediatePriority:1,unstable_UserBlockingPriority:2,unstable_NormalPriority:3,unstable_IdlePriority:5,unstable_LowPriority:4,unstable_runWithPriority:function(a,b){switch(a){case 1:case 2:case 3:case 4:case 5:break;default:a=3}var c=m;m=a;try{return b()}finally{m=c}},unstable_next:function(a){switch(m){case 1:case 2:case 3:var b=3;break;default:b=m}var c=m;m=b;try{return a()}finally{m=c}},unstable_scheduleCallback:function(a,b,c){var d=q();if("object"===typeof c&&null!==c){var e=c.delay;
e="number"===typeof e&&0<e?d+e:d;c="number"===typeof c.timeout?c.timeout:oa(a)}else c=oa(a),e=d;c=e+c;a={id:Pa++,callback:b,priorityLevel:a,startTime:e,expirationTime:c,sortIndex:-1};e>d?(a.sortIndex=e,S(u,a),null===n(p)&&a===n(u)&&(y?V():y=!0,G(T,e-d))):(a.sortIndex=c,S(p,a),v||H||(v=!0,z(U)));return a},unstable_cancelCallback:function(a){a.callback=null},unstable_wrapCallback:function(a){var b=m;return function(){var c=m;m=b;try{return a.apply(this,arguments)}finally{m=c}}},unstable_getCurrentPriorityLevel:function(){return m},
unstable_shouldYield:function(){var a=q();F(a);var b=n(p);return b!==l&&null!==l&&null!==b&&null!==b.callback&&b.startTime<=a&&b.expirationTime<l.expirationTime||W()},unstable_requestPaint:f,unstable_continueExecution:function(){v||H||(v=!0,z(U))},unstable_pauseExecution:function(){},unstable_getFirstCallbackNode:function(){return n(p)},get unstable_now(){return q},get unstable_forceFrameRate(){return X},unstable_Profiling:null},SchedulerTracing:{__proto__:null,__interactionsRef:null,__subscriberRef:null,
unstable_clear:function(a){return a()},unstable_getCurrent:function(){return null},unstable_getThreadID:function(){return++Qa},unstable_trace:function(a,b,c){return c()},unstable_wrap:function(a){return a},unstable_subscribe:function(a){},unstable_unsubscribe:function(a){}}});d.Children={map:function(a,b,c){if(null==a)return a;var d=[];R(a,d,null,b,c);return d},forEach:function(a,b,c){if(null==a)return a;b=ja(null,null,b,c);Q(a,ya,b);ka(b)},count:function(a){return Q(a,function(){return null},null)},
toArray:function(a){var b=[];R(a,b,null,function(a){return a});return b},only:function(a){if(!N(a))throw Error(r(143));return a}};d.Component=w;d.Fragment=Aa;d.Profiler=Ca;d.PureComponent=L;d.StrictMode=Ba;d.Suspense=Ga;d.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=B;d.cloneElement=function(a,b,c){if(null===a||void 0===a)throw Error(r(267,a));var d=I({},a.props),e=a.key,f=a.ref,m=a._owner;if(null!=b){void 0!==b.ref&&(f=b.ref,m=M.current);void 0!==b.key&&(e=""+b.key);if(a.type&&a.type.defaultProps)var h=
a.type.defaultProps;for(k in b)ha.call(b,k)&&!ia.hasOwnProperty(k)&&(d[k]=void 0===b[k]&&void 0!==h?h[k]:b[k])}var k=arguments.length-2;if(1===k)d.children=c;else if(1<k){h=Array(k);for(var l=0;l<k;l++)h[l]=arguments[l+2];d.children=h}return{$$typeof:x,type:a.type,key:e,ref:f,props:d,_owner:m}};d.createContext=function(a,b){void 0===b&&(b=null);a={$$typeof:Ea,_calculateChangedBits:b,_currentValue:a,_currentValue2:a,_threadCount:0,Provider:null,Consumer:null};a.Provider={$$typeof:Da,_context:a};return a.Consumer=
a};d.createElement=ea;d.createFactory=function(a){var b=ea.bind(null,a);b.type=a;return b};d.createRef=function(){return{current:null}};d.forwardRef=function(a){return{$$typeof:Fa,render:a}};d.isValidElement=N;d.lazy=function(a){return{$$typeof:Ia,_ctor:a,_status:-1,_result:null}};d.memo=function(a,b){return{$$typeof:Ha,type:a,compare:void 0===b?null:b}};d.useCallback=function(a,b){return t().useCallback(a,b)};d.useContext=function(a,b){return t().useContext(a,b)};d.useDebugValue=function(a,b){};
d.useEffect=function(a,b){return t().useEffect(a,b)};d.useImperativeHandle=function(a,b,c){return t().useImperativeHandle(a,b,c)};d.useLayoutEffect=function(a,b){return t().useLayoutEffect(a,b)};d.useMemo=function(a,b){return t().useMemo(a,b)};d.useReducer=function(a,b,c){return t().useReducer(a,b,c)};d.useRef=function(a){return t().useRef(a)};d.useState=function(a){return t().useState(a)};d.version="16.13.1"});
</script>
<script crossorigin>/** @license React v16.13.1
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
Modernizr 3.0.0pre (Custom Build) | MIT
*/
'use strict';(function(I,ea){"object"===typeof exports&&"undefined"!==typeof module?ea(exports,require("react")):"function"===typeof define&&define.amd?define(["exports","react"],ea):(I=I||self,ea(I.ReactDOM={},I.React))})(this,function(I,ea){function k(a){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;c<arguments.length;c++)b+="&args[]="+encodeURIComponent(arguments[c]);return"Minified React error #"+a+"; visit "+b+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}
function ji(a,b,c,d,e,f,g,h,m){yb=!1;gc=null;ki.apply(li,arguments)}function mi(a,b,c,d,e,f,g,h,m){ji.apply(this,arguments);if(yb){if(yb){var n=gc;yb=!1;gc=null}else throw Error(k(198));hc||(hc=!0,pd=n)}}function lf(a,b,c){var d=a.type||"unknown-event";a.currentTarget=mf(c);mi(d,b,void 0,a);a.currentTarget=null}function nf(){if(ic)for(var a in cb){var b=cb[a],c=ic.indexOf(a);if(!(-1<c))throw Error(k(96,a));if(!jc[c]){if(!b.extractEvents)throw Error(k(97,a));jc[c]=b;c=b.eventTypes;for(var d in c){var e=
void 0;var f=c[d],g=b,h=d;if(qd.hasOwnProperty(h))throw Error(k(99,h));qd[h]=f;var m=f.phasedRegistrationNames;if(m){for(e in m)m.hasOwnProperty(e)&&of(m[e],g,h);e=!0}else f.registrationName?(of(f.registrationName,g,h),e=!0):e=!1;if(!e)throw Error(k(98,d,a));}}}}function of(a,b,c){if(db[a])throw Error(k(100,a));db[a]=b;rd[a]=b.eventTypes[c].dependencies}function pf(a){var b=!1,c;for(c in a)if(a.hasOwnProperty(c)){var d=a[c];if(!cb.hasOwnProperty(c)||cb[c]!==d){if(cb[c])throw Error(k(102,c));cb[c]=
d;b=!0}}b&&nf()}function qf(a){if(a=rf(a)){if("function"!==typeof sd)throw Error(k(280));var b=a.stateNode;b&&(b=td(b),sd(a.stateNode,a.type,b))}}function sf(a){eb?fb?fb.push(a):fb=[a]:eb=a}function tf(){if(eb){var a=eb,b=fb;fb=eb=null;qf(a);if(b)for(a=0;a<b.length;a++)qf(b[a])}}function ud(){if(null!==eb||null!==fb)vd(),tf()}function uf(a,b,c){if(wd)return a(b,c);wd=!0;try{return vf(a,b,c)}finally{wd=!1,ud()}}function ni(a){if(wf.call(xf,a))return!0;if(wf.call(yf,a))return!1;if(oi.test(a))return xf[a]=
!0;yf[a]=!0;return!1}function pi(a,b,c,d){if(null!==c&&0===c.type)return!1;switch(typeof b){case "function":case "symbol":return!0;case "boolean":if(d)return!1;if(null!==c)return!c.acceptsBooleans;a=a.toLowerCase().slice(0,5);return"data-"!==a&&"aria-"!==a;default:return!1}}function qi(a,b,c,d){if(null===b||"undefined"===typeof b||pi(a,b,c,d))return!0;if(d)return!1;if(null!==c)switch(c.type){case 3:return!b;case 4:return!1===b;case 5:return isNaN(b);case 6:return isNaN(b)||1>b}return!1}function L(a,
b,c,d,e,f){this.acceptsBooleans=2===b||3===b||4===b;this.attributeName=d;this.attributeNamespace=e;this.mustUseProperty=c;this.propertyName=a;this.type=b;this.sanitizeURL=f}function xd(a,b,c,d){var e=E.hasOwnProperty(b)?E[b]:null;var f=null!==e?0===e.type:d?!1:!(2<b.length)||"o"!==b[0]&&"O"!==b[0]||"n"!==b[1]&&"N"!==b[1]?!1:!0;f||(qi(b,c,e,d)&&(c=null),d||null===e?ni(b)&&(null===c?a.removeAttribute(b):a.setAttribute(b,""+c)):e.mustUseProperty?a[e.propertyName]=null===c?3===e.type?!1:"":c:(b=e.attributeName,
d=e.attributeNamespace,null===c?a.removeAttribute(b):(e=e.type,c=3===e||4===e&&!0===c?"":""+c,d?a.setAttributeNS(d,b,c):a.setAttribute(b,c))))}function zb(a){if(null===a||"object"!==typeof a)return null;a=zf&&a[zf]||a["@@iterator"];return"function"===typeof a?a:null}function ri(a){if(-1===a._status){a._status=0;var b=a._ctor;b=b();a._result=b;b.then(function(b){0===a._status&&(b=b.default,a._status=1,a._result=b)},function(b){0===a._status&&(a._status=2,a._result=b)})}}function na(a){if(null==a)return null;
if("function"===typeof a)return a.displayName||a.name||null;if("string"===typeof a)return a;switch(a){case Ma:return"Fragment";case gb:return"Portal";case kc:return"Profiler";case Af:return"StrictMode";case lc:return"Suspense";case yd:return"SuspenseList"}if("object"===typeof a)switch(a.$$typeof){case Bf:return"Context.Consumer";case Cf:return"Context.Provider";case zd:var b=a.render;b=b.displayName||b.name||"";return a.displayName||(""!==b?"ForwardRef("+b+")":"ForwardRef");case Ad:return na(a.type);
case Df:return na(a.render);case Ef:if(a=1===a._status?a._result:null)return na(a)}return null}function Bd(a){var b="";do{a:switch(a.tag){case 3:case 4:case 6:case 7:case 10:case 9:var c="";break a;default:var d=a._debugOwner,e=a._debugSource,f=na(a.type);c=null;d&&(c=na(d.type));d=f;f="";e?f=" (at "+e.fileName.replace(si,"")+":"+e.lineNumber+")":c&&(f=" (created by "+c+")");c="\n in "+(d||"Unknown")+f}b+=c;a=a.return}while(a);return b}function va(a){switch(typeof a){case "boolean":case "number":case "object":case "string":case "undefined":return a;
default:return""}}function Ff(a){var b=a.type;return(a=a.nodeName)&&"input"===a.toLowerCase()&&("checkbox"===b||"radio"===b)}function ti(a){var b=Ff(a)?"checked":"value",c=Object.getOwnPropertyDescriptor(a.constructor.prototype,b),d=""+a[b];if(!a.hasOwnProperty(b)&&"undefined"!==typeof c&&"function"===typeof c.get&&"function"===typeof c.set){var e=c.get,f=c.set;Object.defineProperty(a,b,{configurable:!0,get:function(){return e.call(this)},set:function(a){d=""+a;f.call(this,a)}});Object.defineProperty(a,
b,{enumerable:c.enumerable});return{getValue:function(){return d},setValue:function(a){d=""+a},stopTracking:function(){a._valueTracker=null;delete a[b]}}}}function mc(a){a._valueTracker||(a._valueTracker=ti(a))}function Gf(a){if(!a)return!1;var b=a._valueTracker;if(!b)return!0;var c=b.getValue();var d="";a&&(d=Ff(a)?a.checked?"true":"false":a.value);a=d;return a!==c?(b.setValue(a),!0):!1}function Cd(a,b){var c=b.checked;return M({},b,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:null!=
c?c:a._wrapperState.initialChecked})}function Hf(a,b){var c=null==b.defaultValue?"":b.defaultValue,d=null!=b.checked?b.checked:b.defaultChecked;c=va(null!=b.value?b.value:c);a._wrapperState={initialChecked:d,initialValue:c,controlled:"checkbox"===b.type||"radio"===b.type?null!=b.checked:null!=b.value}}function If(a,b){b=b.checked;null!=b&&xd(a,"checked",b,!1)}function Dd(a,b){If(a,b);var c=va(b.value),d=b.type;if(null!=c)if("number"===d){if(0===c&&""===a.value||a.value!=c)a.value=""+c}else a.value!==
""+c&&(a.value=""+c);else if("submit"===d||"reset"===d){a.removeAttribute("value");return}b.hasOwnProperty("value")?Ed(a,b.type,c):b.hasOwnProperty("defaultValue")&&Ed(a,b.type,va(b.defaultValue));null==b.checked&&null!=b.defaultChecked&&(a.defaultChecked=!!b.defaultChecked)}function Jf(a,b,c){if(b.hasOwnProperty("value")||b.hasOwnProperty("defaultValue")){var d=b.type;if(!("submit"!==d&&"reset"!==d||void 0!==b.value&&null!==b.value))return;b=""+a._wrapperState.initialValue;c||b===a.value||(a.value=
b);a.defaultValue=b}c=a.name;""!==c&&(a.name="");a.defaultChecked=!!a._wrapperState.initialChecked;""!==c&&(a.name=c)}function Ed(a,b,c){if("number"!==b||a.ownerDocument.activeElement!==a)null==c?a.defaultValue=""+a._wrapperState.initialValue:a.defaultValue!==""+c&&(a.defaultValue=""+c)}function ui(a){var b="";ea.Children.forEach(a,function(a){null!=a&&(b+=a)});return b}function Fd(a,b){a=M({children:void 0},b);if(b=ui(b.children))a.children=b;return a}function hb(a,b,c,d){a=a.options;if(b){b={};
for(var e=0;e<c.length;e++)b["$"+c[e]]=!0;for(c=0;c<a.length;c++)e=b.hasOwnProperty("$"+a[c].value),a[c].selected!==e&&(a[c].selected=e),e&&d&&(a[c].defaultSelected=!0)}else{c=""+va(c);b=null;for(e=0;e<a.length;e++){if(a[e].value===c){a[e].selected=!0;d&&(a[e].defaultSelected=!0);return}null!==b||a[e].disabled||(b=a[e])}null!==b&&(b.selected=!0)}}function Gd(a,b){if(null!=b.dangerouslySetInnerHTML)throw Error(k(91));return M({},b,{value:void 0,defaultValue:void 0,children:""+a._wrapperState.initialValue})}
function Kf(a,b){var c=b.value;if(null==c){c=b.children;b=b.defaultValue;if(null!=c){if(null!=b)throw Error(k(92));if(Array.isArray(c)){if(!(1>=c.length))throw Error(k(93));c=c[0]}b=c}null==b&&(b="");c=b}a._wrapperState={initialValue:va(c)}}function Lf(a,b){var c=va(b.value),d=va(b.defaultValue);null!=c&&(c=""+c,c!==a.value&&(a.value=c),null==b.defaultValue&&a.defaultValue!==c&&(a.defaultValue=c));null!=d&&(a.defaultValue=""+d)}function Mf(a,b){b=a.textContent;b===a._wrapperState.initialValue&&""!==
b&&null!==b&&(a.value=b)}function Nf(a){switch(a){case "svg":return"http://www.w3.org/2000/svg";case "math":return"http://www.w3.org/1998/Math/MathML";default:return"http://www.w3.org/1999/xhtml"}}function Hd(a,b){return null==a||"http://www.w3.org/1999/xhtml"===a?Nf(b):"http://www.w3.org/2000/svg"===a&&"foreignObject"===b?"http://www.w3.org/1999/xhtml":a}function nc(a,b){var c={};c[a.toLowerCase()]=b.toLowerCase();c["Webkit"+a]="webkit"+b;c["Moz"+a]="moz"+b;return c}function oc(a){if(Id[a])return Id[a];
if(!ib[a])return a;var b=ib[a],c;for(c in b)if(b.hasOwnProperty(c)&&c in Of)return Id[a]=b[c];return a}function Jd(a){var b=Pf.get(a);void 0===b&&(b=new Map,Pf.set(a,b));return b}function Na(a){var b=a,c=a;if(a.alternate)for(;b.return;)b=b.return;else{a=b;do b=a,0!==(b.effectTag&1026)&&(c=b.return),a=b.return;while(a)}return 3===b.tag?c:null}function Qf(a){if(13===a.tag){var b=a.memoizedState;null===b&&(a=a.alternate,null!==a&&(b=a.memoizedState));if(null!==b)return b.dehydrated}return null}function Rf(a){if(Na(a)!==
a)throw Error(k(188));}function vi(a){var b=a.alternate;if(!b){b=Na(a);if(null===b)throw Error(k(188));return b!==a?null:a}for(var c=a,d=b;;){var e=c.return;if(null===e)break;var f=e.alternate;if(null===f){d=e.return;if(null!==d){c=d;continue}break}if(e.child===f.child){for(f=e.child;f;){if(f===c)return Rf(e),a;if(f===d)return Rf(e),b;f=f.sibling}throw Error(k(188));}if(c.return!==d.return)c=e,d=f;else{for(var g=!1,h=e.child;h;){if(h===c){g=!0;c=e;d=f;break}if(h===d){g=!0;d=e;c=f;break}h=h.sibling}if(!g){for(h=
f.child;h;){if(h===c){g=!0;c=f;d=e;break}if(h===d){g=!0;d=f;c=e;break}h=h.sibling}if(!g)throw Error(k(189));}}if(c.alternate!==d)throw Error(k(190));}if(3!==c.tag)throw Error(k(188));return c.stateNode.current===c?a:b}function Sf(a){a=vi(a);if(!a)return null;for(var b=a;;){if(5===b.tag||6===b.tag)return b;if(b.child)b.child.return=b,b=b.child;else{if(b===a)break;for(;!b.sibling;){if(!b.return||b.return===a)return null;b=b.return}b.sibling.return=b.return;b=b.sibling}}return null}function jb(a,b){if(null==
b)throw Error(k(30));if(null==a)return b;if(Array.isArray(a)){if(Array.isArray(b))return a.push.apply(a,b),a;a.push(b);return a}return Array.isArray(b)?[a].concat(b):[a,b]}function Kd(a,b,c){Array.isArray(a)?a.forEach(b,c):a&&b.call(c,a)}function pc(a){null!==a&&(Ab=jb(Ab,a));a=Ab;Ab=null;if(a){Kd(a,wi);if(Ab)throw Error(k(95));if(hc)throw a=pd,hc=!1,pd=null,a;}}function Ld(a){a=a.target||a.srcElement||window;a.correspondingUseElement&&(a=a.correspondingUseElement);return 3===a.nodeType?a.parentNode:
a}function Tf(a){if(!wa)return!1;a="on"+a;var b=a in document;b||(b=document.createElement("div"),b.setAttribute(a,"return;"),b="function"===typeof b[a]);return b}function Uf(a){a.topLevelType=null;a.nativeEvent=null;a.targetInst=null;a.ancestors.length=0;10>qc.length&&qc.push(a)}function Vf(a,b,c,d){if(qc.length){var e=qc.pop();e.topLevelType=a;e.eventSystemFlags=d;e.nativeEvent=b;e.targetInst=c;return e}return{topLevelType:a,eventSystemFlags:d,nativeEvent:b,targetInst:c,ancestors:[]}}function Wf(a){var b=
a.targetInst,c=b;do{if(!c){a.ancestors.push(c);break}var d=c;if(3===d.tag)d=d.stateNode.containerInfo;else{for(;d.return;)d=d.return;d=3!==d.tag?null:d.stateNode.containerInfo}if(!d)break;b=c.tag;5!==b&&6!==b||a.ancestors.push(c);c=Bb(d)}while(c);for(c=0;c<a.ancestors.length;c++){b=a.ancestors[c];var e=Ld(a.nativeEvent);d=a.topLevelType;var f=a.nativeEvent,g=a.eventSystemFlags;0===c&&(g|=64);for(var h=null,m=0;m<jc.length;m++){var n=jc[m];n&&(n=n.extractEvents(d,b,f,e,g))&&(h=jb(h,n))}pc(h)}}function Md(a,
b,c){if(!c.has(a)){switch(a){case "scroll":Cb(b,"scroll",!0);break;case "focus":case "blur":Cb(b,"focus",!0);Cb(b,"blur",!0);c.set("blur",null);c.set("focus",null);break;case "cancel":case "close":Tf(a)&&Cb(b,a,!0);break;case "invalid":case "submit":case "reset":break;default:-1===Db.indexOf(a)&&w(a,b)}c.set(a,null)}}function xi(a,b){var c=Jd(b);Nd.forEach(function(a){Md(a,b,c)});yi.forEach(function(a){Md(a,b,c)})}function Od(a,b,c,d,e){return{blockedOn:a,topLevelType:b,eventSystemFlags:c|32,nativeEvent:e,
container:d}}function Xf(a,b){switch(a){case "focus":case "blur":xa=null;break;case "dragenter":case "dragleave":ya=null;break;case "mouseover":case "mouseout":za=null;break;case "pointerover":case "pointerout":Eb.delete(b.pointerId);break;case "gotpointercapture":case "lostpointercapture":Fb.delete(b.pointerId)}}function Gb(a,b,c,d,e,f){if(null===a||a.nativeEvent!==f)return a=Od(b,c,d,e,f),null!==b&&(b=Hb(b),null!==b&&Yf(b)),a;a.eventSystemFlags|=d;return a}function zi(a,b,c,d,e){switch(b){case "focus":return xa=
Gb(xa,a,b,c,d,e),!0;case "dragenter":return ya=Gb(ya,a,b,c,d,e),!0;case "mouseover":return za=Gb(za,a,b,c,d,e),!0;case "pointerover":var f=e.pointerId;Eb.set(f,Gb(Eb.get(f)||null,a,b,c,d,e));return!0;case "gotpointercapture":return f=e.pointerId,Fb.set(f,Gb(Fb.get(f)||null,a,b,c,d,e)),!0}return!1}function Ai(a){var b=Bb(a.target);if(null!==b){var c=Na(b);if(null!==c)if(b=c.tag,13===b){if(b=Qf(c),null!==b){a.blockedOn=b;Pd(a.priority,function(){Bi(c)});return}}else if(3===b&&c.stateNode.hydrate){a.blockedOn=
3===c.tag?c.stateNode.containerInfo:null;return}}a.blockedOn=null}function rc(a){if(null!==a.blockedOn)return!1;var b=Qd(a.topLevelType,a.eventSystemFlags,a.container,a.nativeEvent);if(null!==b){var c=Hb(b);null!==c&&Yf(c);a.blockedOn=b;return!1}return!0}function Zf(a,b,c){rc(a)&&c.delete(b)}function Ci(){for(Rd=!1;0<fa.length;){var a=fa[0];if(null!==a.blockedOn){a=Hb(a.blockedOn);null!==a&&Di(a);break}var b=Qd(a.topLevelType,a.eventSystemFlags,a.container,a.nativeEvent);null!==b?a.blockedOn=b:fa.shift()}null!==
xa&&rc(xa)&&(xa=null);null!==ya&&rc(ya)&&(ya=null);null!==za&&rc(za)&&(za=null);Eb.forEach(Zf);Fb.forEach(Zf)}function Ib(a,b){a.blockedOn===b&&(a.blockedOn=null,Rd||(Rd=!0,$f(ag,Ci)))}function bg(a){if(0<fa.length){Ib(fa[0],a);for(var b=1;b<fa.length;b++){var c=fa[b];c.blockedOn===a&&(c.blockedOn=null)}}null!==xa&&Ib(xa,a);null!==ya&&Ib(ya,a);null!==za&&Ib(za,a);b=function(b){return Ib(b,a)};Eb.forEach(b);Fb.forEach(b);for(b=0;b<Jb.length;b++)c=Jb[b],c.blockedOn===a&&(c.blockedOn=null);for(;0<Jb.length&&
(b=Jb[0],null===b.blockedOn);)Ai(b),null===b.blockedOn&&Jb.shift()}function Sd(a,b){for(var c=0;c<a.length;c+=2){var d=a[c],e=a[c+1],f="on"+(e[0].toUpperCase()+e.slice(1));f={phasedRegistrationNames:{bubbled:f,captured:f+"Capture"},dependencies:[d],eventPriority:b};Td.set(d,b);cg.set(d,f);dg[e]=f}}function w(a,b){Cb(b,a,!1)}function Cb(a,b,c){var d=Td.get(b);switch(void 0===d?2:d){case 0:d=Ei.bind(null,b,1,a);break;case 1:d=Fi.bind(null,b,1,a);break;default:d=sc.bind(null,b,1,a)}c?a.addEventListener(b,
d,!0):a.addEventListener(b,d,!1)}function Ei(a,b,c,d){Oa||vd();var e=sc,f=Oa;Oa=!0;try{eg(e,a,b,c,d)}finally{(Oa=f)||ud()}}function Fi(a,b,c,d){Gi(Hi,sc.bind(null,a,b,c,d))}function sc(a,b,c,d){if(tc)if(0<fa.length&&-1<Nd.indexOf(a))a=Od(null,a,b,c,d),fa.push(a);else{var e=Qd(a,b,c,d);if(null===e)Xf(a,d);else if(-1<Nd.indexOf(a))a=Od(e,a,b,c,d),fa.push(a);else if(!zi(e,a,b,c,d)){Xf(a,d);a=Vf(a,d,null,b);try{uf(Wf,a)}finally{Uf(a)}}}}function Qd(a,b,c,d){c=Ld(d);c=Bb(c);if(null!==c){var e=Na(c);if(null===
e)c=null;else{var f=e.tag;if(13===f){c=Qf(e);if(null!==c)return c;c=null}else if(3===f){if(e.stateNode.hydrate)return 3===e.tag?e.stateNode.containerInfo:null;c=null}else e!==c&&(c=null)}}a=Vf(a,d,c,b);try{uf(Wf,a)}finally{Uf(a)}return null}function fg(a,b,c){return null==b||"boolean"===typeof b||""===b?"":c||"number"!==typeof b||0===b||Kb.hasOwnProperty(a)&&Kb[a]?(""+b).trim():b+"px"}function gg(a,b){a=a.style;for(var c in b)if(b.hasOwnProperty(c)){var d=0===c.indexOf("--"),e=fg(c,b[c],d);"float"===
c&&(c="cssFloat");d?a.setProperty(c,e):a[c]=e}}function Ud(a,b){if(b){if(Ii[a]&&(null!=b.children||null!=b.dangerouslySetInnerHTML))throw Error(k(137,a,""));if(null!=b.dangerouslySetInnerHTML){if(null!=b.children)throw Error(k(60));if(!("object"===typeof b.dangerouslySetInnerHTML&&"__html"in b.dangerouslySetInnerHTML))throw Error(k(61));}if(null!=b.style&&"object"!==typeof b.style)throw Error(k(62,""));}}function Vd(a,b){if(-1===a.indexOf("-"))return"string"===typeof b.is;switch(a){case "annotation-xml":case "color-profile":case "font-face":case "font-face-src":case "font-face-uri":case "font-face-format":case "font-face-name":case "missing-glyph":return!1;
default:return!0}}function oa(a,b){a=9===a.nodeType||11===a.nodeType?a:a.ownerDocument;var c=Jd(a);b=rd[b];for(var d=0;d<b.length;d++)Md(b[d],a,c)}function uc(){}function Wd(a){a=a||("undefined"!==typeof document?document:void 0);if("undefined"===typeof a)return null;try{return a.activeElement||a.body}catch(b){return a.body}}function hg(a){for(;a&&a.firstChild;)a=a.firstChild;return a}function ig(a,b){var c=hg(a);a=0;for(var d;c;){if(3===c.nodeType){d=a+c.textContent.length;if(a<=b&&d>=b)return{node:c,
offset:b-a};a=d}a:{for(;c;){if(c.nextSibling){c=c.nextSibling;break a}c=c.parentNode}c=void 0}c=hg(c)}}function jg(a,b){return a&&b?a===b?!0:a&&3===a.nodeType?!1:b&&3===b.nodeType?jg(a,b.parentNode):"contains"in a?a.contains(b):a.compareDocumentPosition?!!(a.compareDocumentPosition(b)&16):!1:!1}function kg(){for(var a=window,b=Wd();b instanceof a.HTMLIFrameElement;){try{var c="string"===typeof b.contentWindow.location.href}catch(d){c=!1}if(c)a=b.contentWindow;else break;b=Wd(a.document)}return b}
function Xd(a){var b=a&&a.nodeName&&a.nodeName.toLowerCase();return b&&("input"===b&&("text"===a.type||"search"===a.type||"tel"===a.type||"url"===a.type||"password"===a.type)||"textarea"===b||"true"===a.contentEditable)}function lg(a,b){switch(a){case "button":case "input":case "select":case "textarea":return!!b.autoFocus}return!1}function Yd(a,b){return"textarea"===a||"option"===a||"noscript"===a||"string"===typeof b.children||"number"===typeof b.children||"object"===typeof b.dangerouslySetInnerHTML&&
null!==b.dangerouslySetInnerHTML&&null!=b.dangerouslySetInnerHTML.__html}function kb(a){for(;null!=a;a=a.nextSibling){var b=a.nodeType;if(1===b||3===b)break}return a}function mg(a){a=a.previousSibling;for(var b=0;a;){if(8===a.nodeType){var c=a.data;if(c===ng||c===Zd||c===$d){if(0===b)return a;b--}else c===og&&b++}a=a.previousSibling}return null}function Bb(a){var b=a[Aa];if(b)return b;for(var c=a.parentNode;c;){if(b=c[Lb]||c[Aa]){c=b.alternate;if(null!==b.child||null!==c&&null!==c.child)for(a=mg(a);null!==
a;){if(c=a[Aa])return c;a=mg(a)}return b}a=c;c=a.parentNode}return null}function Hb(a){a=a[Aa]||a[Lb];return!a||5!==a.tag&&6!==a.tag&&13!==a.tag&&3!==a.tag?null:a}function Pa(a){if(5===a.tag||6===a.tag)return a.stateNode;throw Error(k(33));}function ae(a){return a[vc]||null}function pa(a){do a=a.return;while(a&&5!==a.tag);return a?a:null}function pg(a,b){var c=a.stateNode;if(!c)return null;var d=td(c);if(!d)return null;c=d[b];a:switch(b){case "onClick":case "onClickCapture":case "onDoubleClick":case "onDoubleClickCapture":case "onMouseDown":case "onMouseDownCapture":case "onMouseMove":case "onMouseMoveCapture":case "onMouseUp":case "onMouseUpCapture":case "onMouseEnter":(d=
!d.disabled)||(a=a.type,d=!("button"===a||"input"===a||"select"===a||"textarea"===a));a=!d;break a;default:a=!1}if(a)return null;if(c&&"function"!==typeof c)throw Error(k(231,b,typeof c));return c}function qg(a,b,c){if(b=pg(a,c.dispatchConfig.phasedRegistrationNames[b]))c._dispatchListeners=jb(c._dispatchListeners,b),c._dispatchInstances=jb(c._dispatchInstances,a)}function Ji(a){if(a&&a.dispatchConfig.phasedRegistrationNames){for(var b=a._targetInst,c=[];b;)c.push(b),b=pa(b);for(b=c.length;0<b--;)qg(c[b],
"captured",a);for(b=0;b<c.length;b++)qg(c[b],"bubbled",a)}}function be(a,b,c){a&&c&&c.dispatchConfig.registrationName&&(b=pg(a,c.dispatchConfig.registrationName))&&(c._dispatchListeners=jb(c._dispatchListeners,b),c._dispatchInstances=jb(c._dispatchInstances,a))}function Ki(a){a&&a.dispatchConfig.registrationName&&be(a._targetInst,null,a)}function lb(a){Kd(a,Ji)}function rg(){if(wc)return wc;var a,b=ce,c=b.length,d,e="value"in Ba?Ba.value:Ba.textContent,f=e.length;for(a=0;a<c&&b[a]===e[a];a++);var g=
c-a;for(d=1;d<=g&&b[c-d]===e[f-d];d++);return wc=e.slice(a,1<d?1-d:void 0)}function xc(){return!0}function yc(){return!1}function R(a,b,c,d){this.dispatchConfig=a;this._targetInst=b;this.nativeEvent=c;a=this.constructor.Interface;for(var e in a)a.hasOwnProperty(e)&&((b=a[e])?this[e]=b(c):"target"===e?this.target=d:this[e]=c[e]);this.isDefaultPrevented=(null!=c.defaultPrevented?c.defaultPrevented:!1===c.returnValue)?xc:yc;this.isPropagationStopped=yc;return this}function Li(a,b,c,d){if(this.eventPool.length){var e=
this.eventPool.pop();this.call(e,a,b,c,d);return e}return new this(a,b,c,d)}function Mi(a){if(!(a instanceof this))throw Error(k(279));a.destructor();10>this.eventPool.length&&this.eventPool.push(a)}function sg(a){a.eventPool=[];a.getPooled=Li;a.release=Mi}function tg(a,b){switch(a){case "keyup":return-1!==Ni.indexOf(b.keyCode);case "keydown":return 229!==b.keyCode;case "keypress":case "mousedown":case "blur":return!0;default:return!1}}function ug(a){a=a.detail;return"object"===typeof a&&"data"in
a?a.data:null}function Oi(a,b){switch(a){case "compositionend":return ug(b);case "keypress":if(32!==b.which)return null;vg=!0;return wg;case "textInput":return a=b.data,a===wg&&vg?null:a;default:return null}}function Pi(a,b){if(mb)return"compositionend"===a||!de&&tg(a,b)?(a=rg(),wc=ce=Ba=null,mb=!1,a):null;switch(a){case "paste":return null;case "keypress":if(!(b.ctrlKey||b.altKey||b.metaKey)||b.ctrlKey&&b.altKey){if(b.char&&1<b.char.length)return b.char;if(b.which)return String.fromCharCode(b.which)}return null;
case "compositionend":return xg&&"ko"!==b.locale?null:b.data;default:return null}}function yg(a){var b=a&&a.nodeName&&a.nodeName.toLowerCase();return"input"===b?!!Qi[a.type]:"textarea"===b?!0:!1}function zg(a,b,c){a=R.getPooled(Ag.change,a,b,c);a.type="change";sf(c);lb(a);return a}function Ri(a){pc(a)}function zc(a){var b=Pa(a);if(Gf(b))return a}function Si(a,b){if("change"===a)return b}function Bg(){Mb&&(Mb.detachEvent("onpropertychange",Cg),Nb=Mb=null)}function Cg(a){if("value"===a.propertyName&&
zc(Nb))if(a=zg(Nb,a,Ld(a)),Oa)pc(a);else{Oa=!0;try{ee(Ri,a)}finally{Oa=!1,ud()}}}function Ti(a,b,c){"focus"===a?(Bg(),Mb=b,Nb=c,Mb.attachEvent("onpropertychange",Cg)):"blur"===a&&Bg()}function Ui(a,b){if("selectionchange"===a||"keyup"===a||"keydown"===a)return zc(Nb)}function Vi(a,b){if("click"===a)return zc(b)}function Wi(a,b){if("input"===a||"change"===a)return zc(b)}function Xi(a){var b=this.nativeEvent;return b.getModifierState?b.getModifierState(a):(a=Yi[a])?!!b[a]:!1}function fe(a){return Xi}
function Zi(a,b){return a===b&&(0!==a||1/a===1/b)||a!==a&&b!==b}function Ob(a,b){if(Qa(a,b))return!0;if("object"!==typeof a||null===a||"object"!==typeof b||null===b)return!1;var c=Object.keys(a),d=Object.keys(b);if(c.length!==d.length)return!1;for(d=0;d<c.length;d++)if(!$i.call(b,c[d])||!Qa(a[c[d]],b[c[d]]))return!1;return!0}function Dg(a,b){var c=b.window===b?b.document:9===b.nodeType?b:b.ownerDocument;if(ge||null==nb||nb!==Wd(c))return null;c=nb;"selectionStart"in c&&Xd(c)?c={start:c.selectionStart,
end:c.selectionEnd}:(c=(c.ownerDocument&&c.ownerDocument.defaultView||window).getSelection(),c={anchorNode:c.anchorNode,anchorOffset:c.anchorOffset,focusNode:c.focusNode,focusOffset:c.focusOffset});return Pb&&Ob(Pb,c)?null:(Pb=c,a=R.getPooled(Eg.select,he,a,b),a.type="select",a.target=nb,lb(a),a)}function Ac(a){var b=a.keyCode;"charCode"in a?(a=a.charCode,0===a&&13===b&&(a=13)):a=b;10===a&&(a=13);return 32<=a||13===a?a:0}function q(a,b){0>ob||(a.current=ie[ob],ie[ob]=null,ob--)}function y(a,b,c){ob++;
ie[ob]=a.current;a.current=b}function pb(a,b){var c=a.type.contextTypes;if(!c)return Ca;var d=a.stateNode;if(d&&d.__reactInternalMemoizedUnmaskedChildContext===b)return d.__reactInternalMemoizedMaskedChildContext;var e={},f;for(f in c)e[f]=b[f];d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=b,a.__reactInternalMemoizedMaskedChildContext=e);return e}function N(a){a=a.childContextTypes;return null!==a&&void 0!==a}function Fg(a,b,c){if(B.current!==Ca)throw Error(k(168));y(B,b);y(G,c)}
function Gg(a,b,c){var d=a.stateNode;a=b.childContextTypes;if("function"!==typeof d.getChildContext)return c;d=d.getChildContext();for(var e in d)if(!(e in a))throw Error(k(108,na(b)||"Unknown",e));return M({},c,{},d)}function Bc(a){a=(a=a.stateNode)&&a.__reactInternalMemoizedMergedChildContext||Ca;Ra=B.current;y(B,a);y(G,G.current);return!0}function Hg(a,b,c){var d=a.stateNode;if(!d)throw Error(k(169));c?(a=Gg(a,b,Ra),d.__reactInternalMemoizedMergedChildContext=a,q(G),q(B),y(B,a)):q(G);y(G,c)}function Cc(){switch(aj()){case Dc:return 99;
case Ig:return 98;case Jg:return 97;case Kg:return 96;case Lg:return 95;default:throw Error(k(332));}}function Mg(a){switch(a){case 99:return Dc;case 98:return Ig;case 97:return Jg;case 96:return Kg;case 95:return Lg;default:throw Error(k(332));}}function Da(a,b){a=Mg(a);return bj(a,b)}function Ng(a,b,c){a=Mg(a);return je(a,b,c)}function Og(a){null===qa?(qa=[a],Ec=je(Dc,Pg)):qa.push(a);return Qg}function ha(){if(null!==Ec){var a=Ec;Ec=null;Rg(a)}Pg()}function Pg(){if(!ke&&null!==qa){ke=!0;var a=0;
try{var b=qa;Da(99,function(){for(;a<b.length;a++){var c=b[a];do c=c(!0);while(null!==c)}});qa=null}catch(c){throw null!==qa&&(qa=qa.slice(a+1)),je(Dc,ha),c;}finally{ke=!1}}}function Fc(a,b,c){c/=10;return 1073741821-(((1073741821-a+b/10)/c|0)+1)*c}function aa(a,b){if(a&&a.defaultProps){b=M({},b);a=a.defaultProps;for(var c in a)void 0===b[c]&&(b[c]=a[c])}return b}function le(){Gc=qb=Hc=null}function me(a){var b=Ic.current;q(Ic);a.type._context._currentValue=b}function Sg(a,b){for(;null!==a;){var c=
a.alternate;if(a.childExpirationTime<b)a.childExpirationTime=b,null!==c&&c.childExpirationTime<b&&(c.childExpirationTime=b);else if(null!==c&&c.childExpirationTime<b)c.childExpirationTime=b;else break;a=a.return}}function rb(a,b){Hc=a;Gc=qb=null;a=a.dependencies;null!==a&&null!==a.firstContext&&(a.expirationTime>=b&&(ia=!0),a.firstContext=null)}function W(a,b){if(Gc!==a&&!1!==b&&0!==b){if("number"!==typeof b||1073741823===b)Gc=a,b=1073741823;b={context:a,observedBits:b,next:null};if(null===qb){if(null===
Hc)throw Error(k(308));qb=b;Hc.dependencies={expirationTime:0,firstContext:b,responders:null}}else qb=qb.next=b}return a._currentValue}function ne(a){a.updateQueue={baseState:a.memoizedState,baseQueue:null,shared:{pending:null},effects:null}}function oe(a,b){a=a.updateQueue;b.updateQueue===a&&(b.updateQueue={baseState:a.baseState,baseQueue:a.baseQueue,shared:a.shared,effects:a.effects})}function Ea(a,b){a={expirationTime:a,suspenseConfig:b,tag:Tg,payload:null,callback:null,next:null};return a.next=
a}function Fa(a,b){a=a.updateQueue;if(null!==a){a=a.shared;var c=a.pending;null===c?b.next=b:(b.next=c.next,c.next=b);a.pending=b}}function Ug(a,b){var c=a.alternate;null!==c&&oe(c,a);a=a.updateQueue;c=a.baseQueue;null===c?(a.baseQueue=b.next=b,b.next=b):(b.next=c.next,c.next=b)}function Qb(a,b,c,d){var e=a.updateQueue;Ga=!1;var f=e.baseQueue,g=e.shared.pending;if(null!==g){if(null!==f){var h=f.next;f.next=g.next;g.next=h}f=g;e.shared.pending=null;h=a.alternate;null!==h&&(h=h.updateQueue,null!==h&&
(h.baseQueue=g))}if(null!==f){h=f.next;var m=e.baseState,n=0,k=null,ba=null,l=null;if(null!==h){var p=h;do{g=p.expirationTime;if(g<d){var t={expirationTime:p.expirationTime,suspenseConfig:p.suspenseConfig,tag:p.tag,payload:p.payload,callback:p.callback,next:null};null===l?(ba=l=t,k=m):l=l.next=t;g>n&&(n=g)}else{null!==l&&(l=l.next={expirationTime:1073741823,suspenseConfig:p.suspenseConfig,tag:p.tag,payload:p.payload,callback:p.callback,next:null});Vg(g,p.suspenseConfig);a:{var q=a,r=p;g=b;t=c;switch(r.tag){case 1:q=
r.payload;if("function"===typeof q){m=q.call(t,m,g);break a}m=q;break a;case 3:q.effectTag=q.effectTag&-4097|64;case Tg:q=r.payload;g="function"===typeof q?q.call(t,m,g):q;if(null===g||void 0===g)break a;m=M({},m,g);break a;case Jc:Ga=!0}}null!==p.callback&&(a.effectTag|=32,g=e.effects,null===g?e.effects=[p]:g.push(p))}p=p.next;if(null===p||p===h)if(g=e.shared.pending,null===g)break;else p=f.next=g.next,g.next=h,e.baseQueue=f=g,e.shared.pending=null}while(1)}null===l?k=m:l.next=ba;e.baseState=k;e.baseQueue=
l;Kc(n);a.expirationTime=n;a.memoizedState=m}}function Wg(a,b,c){a=b.effects;b.effects=null;if(null!==a)for(b=0;b<a.length;b++){var d=a[b],e=d.callback;if(null!==e){d.callback=null;d=e;e=c;if("function"!==typeof d)throw Error(k(191,d));d.call(e)}}}function Lc(a,b,c,d){b=a.memoizedState;c=c(d,b);c=null===c||void 0===c?b:M({},b,c);a.memoizedState=c;0===a.expirationTime&&(a.updateQueue.baseState=c)}function Xg(a,b,c,d,e,f,g){a=a.stateNode;return"function"===typeof a.shouldComponentUpdate?a.shouldComponentUpdate(d,
f,g):b.prototype&&b.prototype.isPureReactComponent?!Ob(c,d)||!Ob(e,f):!0}function Yg(a,b,c){var d=!1,e=Ca;var f=b.contextType;"object"===typeof f&&null!==f?f=W(f):(e=N(b)?Ra:B.current,d=b.contextTypes,f=(d=null!==d&&void 0!==d)?pb(a,e):Ca);b=new b(c,f);a.memoizedState=null!==b.state&&void 0!==b.state?b.state:null;b.updater=Mc;a.stateNode=b;b._reactInternalFiber=a;d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=e,a.__reactInternalMemoizedMaskedChildContext=f);return b}function Zg(a,
b,c,d){a=b.state;"function"===typeof b.componentWillReceiveProps&&b.componentWillReceiveProps(c,d);"function"===typeof b.UNSAFE_componentWillReceiveProps&&b.UNSAFE_componentWillReceiveProps(c,d);b.state!==a&&Mc.enqueueReplaceState(b,b.state,null)}function pe(a,b,c,d){var e=a.stateNode;e.props=c;e.state=a.memoizedState;e.refs=$g;ne(a);var f=b.contextType;"object"===typeof f&&null!==f?e.context=W(f):(f=N(b)?Ra:B.current,e.context=pb(a,f));Qb(a,c,e,d);e.state=a.memoizedState;f=b.getDerivedStateFromProps;
"function"===typeof f&&(Lc(a,b,f,c),e.state=a.memoizedState);"function"===typeof b.getDerivedStateFromProps||"function"===typeof e.getSnapshotBeforeUpdate||"function"!==typeof e.UNSAFE_componentWillMount&&"function"!==typeof e.componentWillMount||(b=e.state,"function"===typeof e.componentWillMount&&e.componentWillMount(),"function"===typeof e.UNSAFE_componentWillMount&&e.UNSAFE_componentWillMount(),b!==e.state&&Mc.enqueueReplaceState(e,e.state,null),Qb(a,c,e,d),e.state=a.memoizedState);"function"===
typeof e.componentDidMount&&(a.effectTag|=4)}function Rb(a,b,c){a=c.ref;if(null!==a&&"function"!==typeof a&&"object"!==typeof a){if(c._owner){c=c._owner;if(c){if(1!==c.tag)throw Error(k(309));var d=c.stateNode}if(!d)throw Error(k(147,a));var e=""+a;if(null!==b&&null!==b.ref&&"function"===typeof b.ref&&b.ref._stringRef===e)return b.ref;b=function(a){var b=d.refs;b===$g&&(b=d.refs={});null===a?delete b[e]:b[e]=a};b._stringRef=e;return b}if("string"!==typeof a)throw Error(k(284));if(!c._owner)throw Error(k(290,
a));}return a}function Nc(a,b){if("textarea"!==a.type)throw Error(k(31,"[object Object]"===Object.prototype.toString.call(b)?"object with keys {"+Object.keys(b).join(", ")+"}":b,""));}function ah(a){function b(b,c){if(a){var d=b.lastEffect;null!==d?(d.nextEffect=c,b.lastEffect=c):b.firstEffect=b.lastEffect=c;c.nextEffect=null;c.effectTag=8}}function c(c,d){if(!a)return null;for(;null!==d;)b(c,d),d=d.sibling;return null}function d(a,b){for(a=new Map;null!==b;)null!==b.key?a.set(b.key,b):a.set(b.index,
b),b=b.sibling;return a}function e(a,b){a=Sa(a,b);a.index=0;a.sibling=null;return a}function f(b,c,d){b.index=d;if(!a)return c;d=b.alternate;if(null!==d)return d=d.index,d<c?(b.effectTag=2,c):d;b.effectTag=2;return c}function g(b){a&&null===b.alternate&&(b.effectTag=2);return b}function h(a,b,c,d){if(null===b||6!==b.tag)return b=qe(c,a.mode,d),b.return=a,b;b=e(b,c);b.return=a;return b}function m(a,b,c,d){if(null!==b&&b.elementType===c.type)return d=e(b,c.props),d.ref=Rb(a,b,c),d.return=a,d;d=Oc(c.type,
c.key,c.props,null,a.mode,d);d.ref=Rb(a,b,c);d.return=a;return d}function n(a,b,c,d){if(null===b||4!==b.tag||b.stateNode.containerInfo!==c.containerInfo||b.stateNode.implementation!==c.implementation)return b=re(c,a.mode,d),b.return=a,b;b=e(b,c.children||[]);b.return=a;return b}function l(a,b,c,d,f){if(null===b||7!==b.tag)return b=Ha(c,a.mode,d,f),b.return=a,b;b=e(b,c);b.return=a;return b}function ba(a,b,c){if("string"===typeof b||"number"===typeof b)return b=qe(""+b,a.mode,c),b.return=a,b;if("object"===
typeof b&&null!==b){switch(b.$$typeof){case Pc:return c=Oc(b.type,b.key,b.props,null,a.mode,c),c.ref=Rb(a,null,b),c.return=a,c;case gb:return b=re(b,a.mode,c),b.return=a,b}if(Qc(b)||zb(b))return b=Ha(b,a.mode,c,null),b.return=a,b;Nc(a,b)}return null}function p(a,b,c,d){var e=null!==b?b.key:null;if("string"===typeof c||"number"===typeof c)return null!==e?null:h(a,b,""+c,d);if("object"===typeof c&&null!==c){switch(c.$$typeof){case Pc:return c.key===e?c.type===Ma?l(a,b,c.props.children,d,e):m(a,b,c,
d):null;case gb:return c.key===e?n(a,b,c,d):null}if(Qc(c)||zb(c))return null!==e?null:l(a,b,c,d,null);Nc(a,c)}return null}function t(a,b,c,d,e){if("string"===typeof d||"number"===typeof d)return a=a.get(c)||null,h(b,a,""+d,e);if("object"===typeof d&&null!==d){switch(d.$$typeof){case Pc:return a=a.get(null===d.key?c:d.key)||null,d.type===Ma?l(b,a,d.props.children,e,d.key):m(b,a,d,e);case gb:return a=a.get(null===d.key?c:d.key)||null,n(b,a,d,e)}if(Qc(d)||zb(d))return a=a.get(c)||null,l(b,a,d,e,null);
Nc(b,d)}return null}function q(e,g,h,m){for(var n=null,k=null,l=g,r=g=0,C=null;null!==l&&r<h.length;r++){l.index>r?(C=l,l=null):C=l.sibling;var O=p(e,l,h[r],m);if(null===O){null===l&&(l=C);break}a&&l&&null===O.alternate&&b(e,l);g=f(O,g,r);null===k?n=O:k.sibling=O;k=O;l=C}if(r===h.length)return c(e,l),n;if(null===l){for(;r<h.length;r++)l=ba(e,h[r],m),null!==l&&(g=f(l,g,r),null===k?n=l:k.sibling=l,k=l);return n}for(l=d(e,l);r<h.length;r++)C=t(l,e,r,h[r],m),null!==C&&(a&&null!==C.alternate&&l.delete(null===
C.key?r:C.key),g=f(C,g,r),null===k?n=C:k.sibling=C,k=C);a&&l.forEach(function(a){return b(e,a)});return n}function w(e,g,h,n){var m=zb(h);if("function"!==typeof m)throw Error(k(150));h=m.call(h);if(null==h)throw Error(k(151));for(var l=m=null,r=g,C=g=0,O=null,v=h.next();null!==r&&!v.done;C++,v=h.next()){r.index>C?(O=r,r=null):O=r.sibling;var q=p(e,r,v.value,n);if(null===q){null===r&&(r=O);break}a&&r&&null===q.alternate&&b(e,r);g=f(q,g,C);null===l?m=q:l.sibling=q;l=q;r=O}if(v.done)return c(e,r),m;
if(null===r){for(;!v.done;C++,v=h.next())v=ba(e,v.value,n),null!==v&&(g=f(v,g,C),null===l?m=v:l.sibling=v,l=v);return m}for(r=d(e,r);!v.done;C++,v=h.next())v=t(r,e,C,v.value,n),null!==v&&(a&&null!==v.alternate&&r.delete(null===v.key?C:v.key),g=f(v,g,C),null===l?m=v:l.sibling=v,l=v);a&&r.forEach(function(a){return b(e,a)});return m}return function(a,d,f,h){var m="object"===typeof f&&null!==f&&f.type===Ma&&null===f.key;m&&(f=f.props.children);var n="object"===typeof f&&null!==f;if(n)switch(f.$$typeof){case Pc:a:{n=
f.key;for(m=d;null!==m;){if(m.key===n){switch(m.tag){case 7:if(f.type===Ma){c(a,m.sibling);d=e(m,f.props.children);d.return=a;a=d;break a}break;default:if(m.elementType===f.type){c(a,m.sibling);d=e(m,f.props);d.ref=Rb(a,m,f);d.return=a;a=d;break a}}c(a,m);break}else b(a,m);m=m.sibling}f.type===Ma?(d=Ha(f.props.children,a.mode,h,f.key),d.return=a,a=d):(h=Oc(f.type,f.key,f.props,null,a.mode,h),h.ref=Rb(a,d,f),h.return=a,a=h)}return g(a);case gb:a:{for(m=f.key;null!==d;){if(d.key===m)if(4===d.tag&&d.stateNode.containerInfo===
f.containerInfo&&d.stateNode.implementation===f.implementation){c(a,d.sibling);d=e(d,f.children||[]);d.return=a;a=d;break a}else{c(a,d);break}else b(a,d);d=d.sibling}d=re(f,a.mode,h);d.return=a;a=d}return g(a)}if("string"===typeof f||"number"===typeof f)return f=""+f,null!==d&&6===d.tag?(c(a,d.sibling),d=e(d,f),d.return=a,a=d):(c(a,d),d=qe(f,a.mode,h),d.return=a,a=d),g(a);if(Qc(f))return q(a,d,f,h);if(zb(f))return w(a,d,f,h);n&&Nc(a,f);if("undefined"===typeof f&&!m)switch(a.tag){case 1:case 0:throw a=
a.type,Error(k(152,a.displayName||a.name||"Component"));}return c(a,d)}}function Ta(a){if(a===Sb)throw Error(k(174));return a}function se(a,b){y(Tb,b);y(Ub,a);y(ja,Sb);a=b.nodeType;switch(a){case 9:case 11:b=(b=b.documentElement)?b.namespaceURI:Hd(null,"");break;default:a=8===a?b.parentNode:b,b=a.namespaceURI||null,a=a.tagName,b=Hd(b,a)}q(ja);y(ja,b)}function tb(a){q(ja);q(Ub);q(Tb)}function bh(a){Ta(Tb.current);var b=Ta(ja.current);var c=Hd(b,a.type);b!==c&&(y(Ub,a),y(ja,c))}function te(a){Ub.current===
a&&(q(ja),q(Ub))}function Rc(a){for(var b=a;null!==b;){if(13===b.tag){var c=b.memoizedState;if(null!==c&&(c=c.dehydrated,null===c||c.data===$d||c.data===Zd))return b}else if(19===b.tag&&void 0!==b.memoizedProps.revealOrder){if(0!==(b.effectTag&64))return b}else if(null!==b.child){b.child.return=b;b=b.child;continue}if(b===a)break;for(;null===b.sibling;){if(null===b.return||b.return===a)return null;b=b.return}b.sibling.return=b.return;b=b.sibling}return null}function ue(a,b){return{responder:a,props:b}}
function S(){throw Error(k(321));}function ve(a,b){if(null===b)return!1;for(var c=0;c<b.length&&c<a.length;c++)if(!Qa(a[c],b[c]))return!1;return!0}function we(a,b,c,d,e,f){Ia=f;z=b;b.memoizedState=null;b.updateQueue=null;b.expirationTime=0;Sc.current=null===a||null===a.memoizedState?dj:ej;a=c(d,e);if(b.expirationTime===Ia){f=0;do{b.expirationTime=0;if(!(25>f))throw Error(k(301));f+=1;J=K=null;b.updateQueue=null;Sc.current=fj;a=c(d,e)}while(b.expirationTime===Ia)}Sc.current=Tc;b=null!==K&&null!==K.next;
Ia=0;J=K=z=null;Uc=!1;if(b)throw Error(k(300));return a}function ub(){var a={memoizedState:null,baseState:null,baseQueue:null,queue:null,next:null};null===J?z.memoizedState=J=a:J=J.next=a;return J}function vb(){if(null===K){var a=z.alternate;a=null!==a?a.memoizedState:null}else a=K.next;var b=null===J?z.memoizedState:J.next;if(null!==b)J=b,K=a;else{if(null===a)throw Error(k(310));K=a;a={memoizedState:K.memoizedState,baseState:K.baseState,baseQueue:K.baseQueue,queue:K.queue,next:null};null===J?z.memoizedState=
J=a:J=J.next=a}return J}function Ua(a,b){return"function"===typeof b?b(a):b}function Vc(a,b,c){b=vb();c=b.queue;if(null===c)throw Error(k(311));c.lastRenderedReducer=a;var d=K,e=d.baseQueue,f=c.pending;if(null!==f){if(null!==e){var g=e.next;e.next=f.next;f.next=g}d.baseQueue=e=f;c.pending=null}if(null!==e){e=e.next;d=d.baseState;var h=g=f=null,m=e;do{var n=m.expirationTime;if(n<Ia){var l={expirationTime:m.expirationTime,suspenseConfig:m.suspenseConfig,action:m.action,eagerReducer:m.eagerReducer,eagerState:m.eagerState,
next:null};null===h?(g=h=l,f=d):h=h.next=l;n>z.expirationTime&&(z.expirationTime=n,Kc(n))}else null!==h&&(h=h.next={expirationTime:1073741823,suspenseConfig:m.suspenseConfig,action:m.action,eagerReducer:m.eagerReducer,eagerState:m.eagerState,next:null}),Vg(n,m.suspenseConfig),d=m.eagerReducer===a?m.eagerState:a(d,m.action);m=m.next}while(null!==m&&m!==e);null===h?f=d:h.next=g;Qa(d,b.memoizedState)||(ia=!0);b.memoizedState=d;b.baseState=f;b.baseQueue=h;c.lastRenderedState=d}return[b.memoizedState,
c.dispatch]}function Wc(a,b,c){b=vb();c=b.queue;if(null===c)throw Error(k(311));c.lastRenderedReducer=a;var d=c.dispatch,e=c.pending,f=b.memoizedState;if(null!==e){c.pending=null;var g=e=e.next;do f=a(f,g.action),g=g.next;while(g!==e);Qa(f,b.memoizedState)||(ia=!0);b.memoizedState=f;null===b.baseQueue&&(b.baseState=f);c.lastRenderedState=f}return[f,d]}function xe(a){var b=ub();"function"===typeof a&&(a=a());b.memoizedState=b.baseState=a;a=b.queue={pending:null,dispatch:null,lastRenderedReducer:Ua,
lastRenderedState:a};a=a.dispatch=ch.bind(null,z,a);return[b.memoizedState,a]}function ye(a,b,c,d){a={tag:a,create:b,destroy:c,deps:d,next:null};b=z.updateQueue;null===b?(b={lastEffect:null},z.updateQueue=b,b.lastEffect=a.next=a):(c=b.lastEffect,null===c?b.lastEffect=a.next=a:(d=c.next,c.next=a,a.next=d,b.lastEffect=a));return a}function dh(a){return vb().memoizedState}function ze(a,b,c,d){var e=ub();z.effectTag|=a;e.memoizedState=ye(1|b,c,void 0,void 0===d?null:d)}function Ae(a,b,c,d){var e=vb();
d=void 0===d?null:d;var f=void 0;if(null!==K){var g=K.memoizedState;f=g.destroy;if(null!==d&&ve(d,g.deps)){ye(b,c,f,d);return}}z.effectTag|=a;e.memoizedState=ye(1|b,c,f,d)}function eh(a,b){return ze(516,4,a,b)}function Xc(a,b){return Ae(516,4,a,b)}function fh(a,b){return Ae(4,2,a,b)}function gh(a,b){if("function"===typeof b)return a=a(),b(a),function(){b(null)};if(null!==b&&void 0!==b)return a=a(),b.current=a,function(){b.current=null}}function hh(a,b,c){c=null!==c&&void 0!==c?c.concat([a]):null;
return Ae(4,2,gh.bind(null,b,a),c)}function Be(a,b){}function ih(a,b){ub().memoizedState=[a,void 0===b?null:b];return a}function Yc(a,b){var c=vb();b=void 0===b?null:b;var d=c.memoizedState;if(null!==d&&null!==b&&ve(b,d[1]))return d[0];c.memoizedState=[a,b];return a}function jh(a,b){var c=vb();b=void 0===b?null:b;var d=c.memoizedState;if(null!==d&&null!==b&&ve(b,d[1]))return d[0];a=a();c.memoizedState=[a,b];return a}function Ce(a,b,c){var d=Cc();Da(98>d?98:d,function(){a(!0)});Da(97<d?97:d,function(){var d=
X.suspense;X.suspense=void 0===b?null:b;try{a(!1),c()}finally{X.suspense=d}})}function ch(a,b,c){var d=ka(),e=Vb.suspense;d=Va(d,a,e);e={expirationTime:d,suspenseConfig:e,action:c,eagerReducer:null,eagerState:null,next:null};var f=b.pending;null===f?e.next=e:(e.next=f.next,f.next=e);b.pending=e;f=a.alternate;if(a===z||null!==f&&f===z)Uc=!0,e.expirationTime=Ia,z.expirationTime=Ia;else{if(0===a.expirationTime&&(null===f||0===f.expirationTime)&&(f=b.lastRenderedReducer,null!==f))try{var g=b.lastRenderedState,
h=f(g,c);e.eagerReducer=f;e.eagerState=h;if(Qa(h,g))return}catch(m){}finally{}Ja(a,d)}}function kh(a,b){var c=la(5,null,null,0);c.elementType="DELETED";c.type="DELETED";c.stateNode=b;c.return=a;c.effectTag=8;null!==a.lastEffect?(a.lastEffect.nextEffect=c,a.lastEffect=c):a.firstEffect=a.lastEffect=c}function lh(a,b){switch(a.tag){case 5:var c=a.type;b=1!==b.nodeType||c.toLowerCase()!==b.nodeName.toLowerCase()?null:b;return null!==b?(a.stateNode=b,!0):!1;case 6:return b=""===a.pendingProps||3!==b.nodeType?
null:b,null!==b?(a.stateNode=b,!0):!1;case 13:return!1;default:return!1}}function De(a){if(Wa){var b=Ka;if(b){var c=b;if(!lh(a,b)){b=kb(c.nextSibling);if(!b||!lh(a,b)){a.effectTag=a.effectTag&-1025|2;Wa=!1;ra=a;return}kh(ra,c)}ra=a;Ka=kb(b.firstChild)}else a.effectTag=a.effectTag&-1025|2,Wa=!1,ra=a}}function mh(a){for(a=a.return;null!==a&&5!==a.tag&&3!==a.tag&&13!==a.tag;)a=a.return;ra=a}function Zc(a){if(a!==ra)return!1;if(!Wa)return mh(a),Wa=!0,!1;var b=a.type;if(5!==a.tag||"head"!==b&&"body"!==
b&&!Yd(b,a.memoizedProps))for(b=Ka;b;)kh(a,b),b=kb(b.nextSibling);mh(a);if(13===a.tag){a=a.memoizedState;a=null!==a?a.dehydrated:null;if(!a)throw Error(k(317));a:{a=a.nextSibling;for(b=0;a;){if(8===a.nodeType){var c=a.data;if(c===og){if(0===b){Ka=kb(a.nextSibling);break a}b--}else c!==ng&&c!==Zd&&c!==$d||b++}a=a.nextSibling}Ka=null}}else Ka=ra?kb(a.stateNode.nextSibling):null;return!0}function Ee(){Ka=ra=null;Wa=!1}function T(a,b,c,d){b.child=null===a?Fe(b,null,c,d):wb(b,a.child,c,d)}function nh(a,
b,c,d,e){c=c.render;var f=b.ref;rb(b,e);d=we(a,b,c,d,f,e);if(null!==a&&!ia)return b.updateQueue=a.updateQueue,b.effectTag&=-517,a.expirationTime<=e&&(a.expirationTime=0),sa(a,b,e);b.effectTag|=1;T(a,b,d,e);return b.child}function oh(a,b,c,d,e,f){if(null===a){var g=c.type;if("function"===typeof g&&!Ge(g)&&void 0===g.defaultProps&&null===c.compare&&void 0===c.defaultProps)return b.tag=15,b.type=g,ph(a,b,g,d,e,f);a=Oc(c.type,null,d,null,b.mode,f);a.ref=b.ref;a.return=b;return b.child=a}g=a.child;if(e<
f&&(e=g.memoizedProps,c=c.compare,c=null!==c?c:Ob,c(e,d)&&a.ref===b.ref))return sa(a,b,f);b.effectTag|=1;a=Sa(g,d);a.ref=b.ref;a.return=b;return b.child=a}function ph(a,b,c,d,e,f){return null!==a&&Ob(a.memoizedProps,d)&&a.ref===b.ref&&(ia=!1,e<f)?(b.expirationTime=a.expirationTime,sa(a,b,f)):He(a,b,c,d,f)}function qh(a,b){var c=b.ref;if(null===a&&null!==c||null!==a&&a.ref!==c)b.effectTag|=128}function He(a,b,c,d,e){var f=N(c)?Ra:B.current;f=pb(b,f);rb(b,e);c=we(a,b,c,d,f,e);if(null!==a&&!ia)return b.updateQueue=
a.updateQueue,b.effectTag&=-517,a.expirationTime<=e&&(a.expirationTime=0),sa(a,b,e);b.effectTag|=1;T(a,b,c,e);return b.child}function rh(a,b,c,d,e){if(N(c)){var f=!0;Bc(b)}else f=!1;rb(b,e);if(null===b.stateNode)null!==a&&(a.alternate=null,b.alternate=null,b.effectTag|=2),Yg(b,c,d),pe(b,c,d,e),d=!0;else if(null===a){var g=b.stateNode,h=b.memoizedProps;g.props=h;var m=g.context,n=c.contextType;"object"===typeof n&&null!==n?n=W(n):(n=N(c)?Ra:B.current,n=pb(b,n));var l=c.getDerivedStateFromProps,k="function"===
typeof l||"function"===typeof g.getSnapshotBeforeUpdate;k||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==d||m!==n)&&Zg(b,g,d,n);Ga=!1;var p=b.memoizedState;g.state=p;Qb(b,d,g,e);m=b.memoizedState;h!==d||p!==m||G.current||Ga?("function"===typeof l&&(Lc(b,c,l,d),m=b.memoizedState),(h=Ga||Xg(b,c,h,d,p,m,n))?(k||"function"!==typeof g.UNSAFE_componentWillMount&&"function"!==typeof g.componentWillMount||("function"===typeof g.componentWillMount&&
g.componentWillMount(),"function"===typeof g.UNSAFE_componentWillMount&&g.UNSAFE_componentWillMount()),"function"===typeof g.componentDidMount&&(b.effectTag|=4)):("function"===typeof g.componentDidMount&&(b.effectTag|=4),b.memoizedProps=d,b.memoizedState=m),g.props=d,g.state=m,g.context=n,d=h):("function"===typeof g.componentDidMount&&(b.effectTag|=4),d=!1)}else g=b.stateNode,oe(a,b),h=b.memoizedProps,g.props=b.type===b.elementType?h:aa(b.type,h),m=g.context,n=c.contextType,"object"===typeof n&&null!==
n?n=W(n):(n=N(c)?Ra:B.current,n=pb(b,n)),l=c.getDerivedStateFromProps,(k="function"===typeof l||"function"===typeof g.getSnapshotBeforeUpdate)||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==d||m!==n)&&Zg(b,g,d,n),Ga=!1,m=b.memoizedState,g.state=m,Qb(b,d,g,e),p=b.memoizedState,h!==d||m!==p||G.current||Ga?("function"===typeof l&&(Lc(b,c,l,d),p=b.memoizedState),(l=Ga||Xg(b,c,h,d,m,p,n))?(k||"function"!==typeof g.UNSAFE_componentWillUpdate&&
"function"!==typeof g.componentWillUpdate||("function"===typeof g.componentWillUpdate&&g.componentWillUpdate(d,p,n),"function"===typeof g.UNSAFE_componentWillUpdate&&g.UNSAFE_componentWillUpdate(d,p,n)),"function"===typeof g.componentDidUpdate&&(b.effectTag|=4),"function"===typeof g.getSnapshotBeforeUpdate&&(b.effectTag|=256)):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&m===a.memoizedState||(b.effectTag|=4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&m===
a.memoizedState||(b.effectTag|=256),b.memoizedProps=d,b.memoizedState=p),g.props=d,g.state=p,g.context=n,d=l):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&m===a.memoizedState||(b.effectTag|=4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&m===a.memoizedState||(b.effectTag|=256),d=!1);return Ie(a,b,c,d,f,e)}function Ie(a,b,c,d,e,f){qh(a,b);var g=0!==(b.effectTag&64);if(!d&&!g)return e&&Hg(b,c,!1),sa(a,b,f);d=b.stateNode;gj.current=b;var h=g&&"function"!==typeof c.getDerivedStateFromError?
null:d.render();b.effectTag|=1;null!==a&&g?(b.child=wb(b,a.child,null,f),b.child=wb(b,null,h,f)):T(a,b,h,f);b.memoizedState=d.state;e&&Hg(b,c,!0);return b.child}function sh(a){var b=a.stateNode;b.pendingContext?Fg(a,b.pendingContext,b.pendingContext!==b.context):b.context&&Fg(a,b.context,!1);se(a,b.containerInfo)}function th(a,b,c){var d=b.mode,e=b.pendingProps,f=D.current,g=!1,h;(h=0!==(b.effectTag&64))||(h=0!==(f&2)&&(null===a||null!==a.memoizedState));h?(g=!0,b.effectTag&=-65):null!==a&&null===
a.memoizedState||void 0===e.fallback||!0===e.unstable_avoidThisFallback||(f|=1);y(D,f&1);if(null===a){void 0!==e.fallback&&De(b);if(g){g=e.fallback;e=Ha(null,d,0,null);e.return=b;if(0===(b.mode&2))for(a=null!==b.memoizedState?b.child.child:b.child,e.child=a;null!==a;)a.return=e,a=a.sibling;c=Ha(g,d,c,null);c.return=b;e.sibling=c;b.memoizedState=Je;b.child=e;return c}d=e.children;b.memoizedState=null;return b.child=Fe(b,null,d,c)}if(null!==a.memoizedState){a=a.child;d=a.sibling;if(g){e=e.fallback;
c=Sa(a,a.pendingProps);c.return=b;if(0===(b.mode&2)&&(g=null!==b.memoizedState?b.child.child:b.child,g!==a.child))for(c.child=g;null!==g;)g.return=c,g=g.sibling;d=Sa(d,e);d.return=b;c.sibling=d;c.childExpirationTime=0;b.memoizedState=Je;b.child=c;return d}c=wb(b,a.child,e.children,c);b.memoizedState=null;return b.child=c}a=a.child;if(g){g=e.fallback;e=Ha(null,d,0,null);e.return=b;e.child=a;null!==a&&(a.return=e);if(0===(b.mode&2))for(a=null!==b.memoizedState?b.child.child:b.child,e.child=a;null!==
a;)a.return=e,a=a.sibling;c=Ha(g,d,c,null);c.return=b;e.sibling=c;c.effectTag|=2;e.childExpirationTime=0;b.memoizedState=Je;b.child=e;return c}b.memoizedState=null;return b.child=wb(b,a,e.children,c)}function uh(a,b){a.expirationTime<b&&(a.expirationTime=b);var c=a.alternate;null!==c&&c.expirationTime<b&&(c.expirationTime=b);Sg(a.return,b)}function Ke(a,b,c,d,e,f){var g=a.memoizedState;null===g?a.memoizedState={isBackwards:b,rendering:null,renderingStartTime:0,last:d,tail:c,tailExpiration:0,tailMode:e,
lastEffect:f}:(g.isBackwards=b,g.rendering=null,g.renderingStartTime=0,g.last=d,g.tail=c,g.tailExpiration=0,g.tailMode=e,g.lastEffect=f)}function vh(a,b,c){var d=b.pendingProps,e=d.revealOrder,f=d.tail;T(a,b,d.children,c);d=D.current;if(0!==(d&2))d=d&1|2,b.effectTag|=64;else{if(null!==a&&0!==(a.effectTag&64))a:for(a=b.child;null!==a;){if(13===a.tag)null!==a.memoizedState&&uh(a,c);else if(19===a.tag)uh(a,c);else if(null!==a.child){a.child.return=a;a=a.child;continue}if(a===b)break a;for(;null===a.sibling;){if(null===
a.return||a.return===b)break a;a=a.return}a.sibling.return=a.return;a=a.sibling}d&=1}y(D,d);if(0===(b.mode&2))b.memoizedState=null;else switch(e){case "forwards":c=b.child;for(e=null;null!==c;)a=c.alternate,null!==a&&null===Rc(a)&&(e=c),c=c.sibling;c=e;null===c?(e=b.child,b.child=null):(e=c.sibling,c.sibling=null);Ke(b,!1,e,c,f,b.lastEffect);break;case "backwards":c=null;e=b.child;for(b.child=null;null!==e;){a=e.alternate;if(null!==a&&null===Rc(a)){b.child=e;break}a=e.sibling;e.sibling=c;c=e;e=a}Ke(b,
!0,c,null,f,b.lastEffect);break;case "together":Ke(b,!1,null,null,void 0,b.lastEffect);break;default:b.memoizedState=null}return b.child}function sa(a,b,c){null!==a&&(b.dependencies=a.dependencies);var d=b.expirationTime;0!==d&&Kc(d);if(b.childExpirationTime<c)return null;if(null!==a&&b.child!==a.child)throw Error(k(153));if(null!==b.child){a=b.child;c=Sa(a,a.pendingProps);b.child=c;for(c.return=b;null!==a.sibling;)a=a.sibling,c=c.sibling=Sa(a,a.pendingProps),c.return=b;c.sibling=null}return b.child}
function $c(a,b){switch(a.tailMode){case "hidden":b=a.tail;for(var c=null;null!==b;)null!==b.alternate&&(c=b),b=b.sibling;null===c?a.tail=null:c.sibling=null;break;case "collapsed":c=a.tail;for(var d=null;null!==c;)null!==c.alternate&&(d=c),c=c.sibling;null===d?b||null===a.tail?a.tail=null:a.tail.sibling=null:d.sibling=null}}function hj(a,b,c){var d=b.pendingProps;switch(b.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return null;case 1:return N(b.type)&&(q(G),q(B)),
null;case 3:return tb(),q(G),q(B),c=b.stateNode,c.pendingContext&&(c.context=c.pendingContext,c.pendingContext=null),null!==a&&null!==a.child||!Zc(b)||(b.effectTag|=4),wh(b),null;case 5:te(b);c=Ta(Tb.current);var e=b.type;if(null!==a&&null!=b.stateNode)ij(a,b,e,d,c),a.ref!==b.ref&&(b.effectTag|=128);else{if(!d){if(null===b.stateNode)throw Error(k(166));return null}a=Ta(ja.current);if(Zc(b)){d=b.stateNode;e=b.type;var f=b.memoizedProps;d[Aa]=b;d[vc]=f;switch(e){case "iframe":case "object":case "embed":w("load",
d);break;case "video":case "audio":for(a=0;a<Db.length;a++)w(Db[a],d);break;case "source":w("error",d);break;case "img":case "image":case "link":w("error",d);w("load",d);break;case "form":w("reset",d);w("submit",d);break;case "details":w("toggle",d);break;case "input":Hf(d,f);w("invalid",d);oa(c,"onChange");break;case "select":d._wrapperState={wasMultiple:!!f.multiple};w("invalid",d);oa(c,"onChange");break;case "textarea":Kf(d,f),w("invalid",d),oa(c,"onChange")}Ud(e,f);a=null;for(var g in f)if(f.hasOwnProperty(g)){var h=
f[g];"children"===g?"string"===typeof h?d.textContent!==h&&(a=["children",h]):"number"===typeof h&&d.textContent!==""+h&&(a=["children",""+h]):db.hasOwnProperty(g)&&null!=h&&oa(c,g)}switch(e){case "input":mc(d);Jf(d,f,!0);break;case "textarea":mc(d);Mf(d);break;case "select":case "option":break;default:"function"===typeof f.onClick&&(d.onclick=uc)}c=a;b.updateQueue=c;null!==c&&(b.effectTag|=4)}else{g=9===c.nodeType?c:c.ownerDocument;"http://www.w3.org/1999/xhtml"===a&&(a=Nf(e));"http://www.w3.org/1999/xhtml"===
a?"script"===e?(a=g.createElement("div"),a.innerHTML="<script>\x3c/script>",a=a.removeChild(a.firstChild)):"string"===typeof d.is?a=g.createElement(e,{is:d.is}):(a=g.createElement(e),"select"===e&&(g=a,d.multiple?g.multiple=!0:d.size&&(g.size=d.size))):a=g.createElementNS(a,e);a[Aa]=b;a[vc]=d;jj(a,b,!1,!1);b.stateNode=a;g=Vd(e,d);switch(e){case "iframe":case "object":case "embed":w("load",a);h=d;break;case "video":case "audio":for(h=0;h<Db.length;h++)w(Db[h],a);h=d;break;case "source":w("error",a);
h=d;break;case "img":case "image":case "link":w("error",a);w("load",a);h=d;break;case "form":w("reset",a);w("submit",a);h=d;break;case "details":w("toggle",a);h=d;break;case "input":Hf(a,d);h=Cd(a,d);w("invalid",a);oa(c,"onChange");break;case "option":h=Fd(a,d);break;case "select":a._wrapperState={wasMultiple:!!d.multiple};h=M({},d,{value:void 0});w("invalid",a);oa(c,"onChange");break;case "textarea":Kf(a,d);h=Gd(a,d);w("invalid",a);oa(c,"onChange");break;default:h=d}Ud(e,h);var m=h;for(f in m)if(m.hasOwnProperty(f)){var n=
m[f];"style"===f?gg(a,n):"dangerouslySetInnerHTML"===f?(n=n?n.__html:void 0,null!=n&&xh(a,n)):"children"===f?"string"===typeof n?("textarea"!==e||""!==n)&&Wb(a,n):"number"===typeof n&&Wb(a,""+n):"suppressContentEditableWarning"!==f&&"suppressHydrationWarning"!==f&&"autoFocus"!==f&&(db.hasOwnProperty(f)?null!=n&&oa(c,f):null!=n&&xd(a,f,n,g))}switch(e){case "input":mc(a);Jf(a,d,!1);break;case "textarea":mc(a);Mf(a);break;case "option":null!=d.value&&a.setAttribute("value",""+va(d.value));break;case "select":a.multiple=
!!d.multiple;c=d.value;null!=c?hb(a,!!d.multiple,c,!1):null!=d.defaultValue&&hb(a,!!d.multiple,d.defaultValue,!0);break;default:"function"===typeof h.onClick&&(a.onclick=uc)}lg(e,d)&&(b.effectTag|=4)}null!==b.ref&&(b.effectTag|=128)}return null;case 6:if(a&&null!=b.stateNode)kj(a,b,a.memoizedProps,d);else{if("string"!==typeof d&&null===b.stateNode)throw Error(k(166));c=Ta(Tb.current);Ta(ja.current);Zc(b)?(c=b.stateNode,d=b.memoizedProps,c[Aa]=b,c.nodeValue!==d&&(b.effectTag|=4)):(c=(9===c.nodeType?
c:c.ownerDocument).createTextNode(d),c[Aa]=b,b.stateNode=c)}return null;case 13:q(D);d=b.memoizedState;if(0!==(b.effectTag&64))return b.expirationTime=c,b;c=null!==d;d=!1;null===a?void 0!==b.memoizedProps.fallback&&Zc(b):(e=a.memoizedState,d=null!==e,c||null===e||(e=a.child.sibling,null!==e&&(f=b.firstEffect,null!==f?(b.firstEffect=e,e.nextEffect=f):(b.firstEffect=b.lastEffect=e,e.nextEffect=null),e.effectTag=8)));if(c&&!d&&0!==(b.mode&2))if(null===a&&!0!==b.memoizedProps.unstable_avoidThisFallback||
0!==(D.current&1))F===Xa&&(F=ad);else{if(F===Xa||F===ad)F=bd;0!==Xb&&null!==U&&(Ya(U,P),yh(U,Xb))}if(c||d)b.effectTag|=4;return null;case 4:return tb(),wh(b),null;case 10:return me(b),null;case 17:return N(b.type)&&(q(G),q(B)),null;case 19:q(D);d=b.memoizedState;if(null===d)return null;e=0!==(b.effectTag&64);f=d.rendering;if(null===f)if(e)$c(d,!1);else{if(F!==Xa||null!==a&&0!==(a.effectTag&64))for(f=b.child;null!==f;){a=Rc(f);if(null!==a){b.effectTag|=64;$c(d,!1);e=a.updateQueue;null!==e&&(b.updateQueue=
e,b.effectTag|=4);null===d.lastEffect&&(b.firstEffect=null);b.lastEffect=d.lastEffect;for(d=b.child;null!==d;)e=d,f=c,e.effectTag&=2,e.nextEffect=null,e.firstEffect=null,e.lastEffect=null,a=e.alternate,null===a?(e.childExpirationTime=0,e.expirationTime=f,e.child=null,e.memoizedProps=null,e.memoizedState=null,e.updateQueue=null,e.dependencies=null):(e.childExpirationTime=a.childExpirationTime,e.expirationTime=a.expirationTime,e.child=a.child,e.memoizedProps=a.memoizedProps,e.memoizedState=a.memoizedState,
e.updateQueue=a.updateQueue,f=a.dependencies,e.dependencies=null===f?null:{expirationTime:f.expirationTime,firstContext:f.firstContext,responders:f.responders}),d=d.sibling;y(D,D.current&1|2);return b.child}f=f.sibling}}else{if(!e)if(a=Rc(f),null!==a){if(b.effectTag|=64,e=!0,c=a.updateQueue,null!==c&&(b.updateQueue=c,b.effectTag|=4),$c(d,!0),null===d.tail&&"hidden"===d.tailMode&&!f.alternate)return b=b.lastEffect=d.lastEffect,null!==b&&(b.nextEffect=null),null}else 2*Y()-d.renderingStartTime>d.tailExpiration&&
1<c&&(b.effectTag|=64,e=!0,$c(d,!1),b.expirationTime=b.childExpirationTime=c-1);d.isBackwards?(f.sibling=b.child,b.child=f):(c=d.last,null!==c?c.sibling=f:b.child=f,d.last=f)}return null!==d.tail?(0===d.tailExpiration&&(d.tailExpiration=Y()+500),c=d.tail,d.rendering=c,d.tail=c.sibling,d.lastEffect=b.lastEffect,d.renderingStartTime=Y(),c.sibling=null,b=D.current,y(D,e?b&1|2:b&1),c):null}throw Error(k(156,b.tag));}function lj(a,b){switch(a.tag){case 1:return N(a.type)&&(q(G),q(B)),b=a.effectTag,b&4096?
(a.effectTag=b&-4097|64,a):null;case 3:tb();q(G);q(B);b=a.effectTag;if(0!==(b&64))throw Error(k(285));a.effectTag=b&-4097|64;return a;case 5:return te(a),null;case 13:return q(D),b=a.effectTag,b&4096?(a.effectTag=b&-4097|64,a):null;case 19:return q(D),null;case 4:return tb(),null;case 10:return me(a),null;default:return null}}function Le(a,b){return{value:a,source:b,stack:Bd(b)}}function Me(a,b){var c=b.source,d=b.stack;null===d&&null!==c&&(d=Bd(c));null!==c&&na(c.type);b=b.value;null!==a&&1===a.tag&&
na(a.type);try{console.error(b)}catch(e){setTimeout(function(){throw e;})}}function mj(a,b){try{b.props=a.memoizedProps,b.state=a.memoizedState,b.componentWillUnmount()}catch(c){Za(a,c)}}function zh(a){var b=a.ref;if(null!==b)if("function"===typeof b)try{b(null)}catch(c){Za(a,c)}else b.current=null}function nj(a,b){switch(b.tag){case 0:case 11:case 15:case 22:return;case 1:if(b.effectTag&256&&null!==a){var c=a.memoizedProps,d=a.memoizedState;a=b.stateNode;b=a.getSnapshotBeforeUpdate(b.elementType===
b.type?c:aa(b.type,c),d);a.__reactInternalSnapshotBeforeUpdate=b}return;case 3:case 5:case 6:case 4:case 17:return}throw Error(k(163));}function Ah(a,b){b=b.updateQueue;b=null!==b?b.lastEffect:null;if(null!==b){var c=b=b.next;do{if((c.tag&a)===a){var d=c.destroy;c.destroy=void 0;void 0!==d&&d()}c=c.next}while(c!==b)}}function Bh(a,b){b=b.updateQueue;b=null!==b?b.lastEffect:null;if(null!==b){var c=b=b.next;do{if((c.tag&a)===a){var d=c.create;c.destroy=d()}c=c.next}while(c!==b)}}function oj(a,b,c,d){switch(c.tag){case 0:case 11:case 15:case 22:Bh(3,
c);return;case 1:a=c.stateNode;c.effectTag&4&&(null===b?a.componentDidMount():(d=c.elementType===c.type?b.memoizedProps:aa(c.type,b.memoizedProps),a.componentDidUpdate(d,b.memoizedState,a.__reactInternalSnapshotBeforeUpdate)));b=c.updateQueue;null!==b&&Wg(c,b,a);return;case 3:b=c.updateQueue;if(null!==b){a=null;if(null!==c.child)switch(c.child.tag){case 5:a=c.child.stateNode;break;case 1:a=c.child.stateNode}Wg(c,b,a)}return;case 5:a=c.stateNode;null===b&&c.effectTag&4&&lg(c.type,c.memoizedProps)&&
a.focus();return;case 6:return;case 4:return;case 12:return;case 13:null===c.memoizedState&&(c=c.alternate,null!==c&&(c=c.memoizedState,null!==c&&(c=c.dehydrated,null!==c&&bg(c))));return;case 19:case 17:case 20:case 21:return}throw Error(k(163));}function Ch(a,b,c){"function"===typeof Ne&&Ne(b);switch(b.tag){case 0:case 11:case 14:case 15:case 22:a=b.updateQueue;if(null!==a&&(a=a.lastEffect,null!==a)){var d=a.next;Da(97<c?97:c,function(){var a=d;do{var c=a.destroy;if(void 0!==c){var g=b;try{c()}catch(h){Za(g,
h)}}a=a.next}while(a!==d)})}break;case 1:zh(b);c=b.stateNode;"function"===typeof c.componentWillUnmount&&mj(b,c);break;case 5:zh(b);break;case 4:Dh(a,b,c)}}function Eh(a){var b=a.alternate;a.return=null;a.child=null;a.memoizedState=null;a.updateQueue=null;a.dependencies=null;a.alternate=null;a.firstEffect=null;a.lastEffect=null;a.pendingProps=null;a.memoizedProps=null;a.stateNode=null;null!==b&&Eh(b)}function Fh(a){return 5===a.tag||3===a.tag||4===a.tag}function Gh(a){a:{for(var b=a.return;null!==
b;){if(Fh(b)){var c=b;break a}b=b.return}throw Error(k(160));}b=c.stateNode;switch(c.tag){case 5:var d=!1;break;case 3:b=b.containerInfo;d=!0;break;case 4:b=b.containerInfo;d=!0;break;default:throw Error(k(161));}c.effectTag&16&&(Wb(b,""),c.effectTag&=-17);a:b:for(c=a;;){for(;null===c.sibling;){if(null===c.return||Fh(c.return)){c=null;break a}c=c.return}c.sibling.return=c.return;for(c=c.sibling;5!==c.tag&&6!==c.tag&&18!==c.tag;){if(c.effectTag&2)continue b;if(null===c.child||4===c.tag)continue b;
else c.child.return=c,c=c.child}if(!(c.effectTag&2)){c=c.stateNode;break a}}d?Oe(a,c,b):Pe(a,c,b)}function Oe(a,b,c){var d=a.tag,e=5===d||6===d;if(e)a=e?a.stateNode:a.stateNode.instance,b?8===c.nodeType?c.parentNode.insertBefore(a,b):c.insertBefore(a,b):(8===c.nodeType?(b=c.parentNode,b.insertBefore(a,c)):(b=c,b.appendChild(a)),c=c._reactRootContainer,null!==c&&void 0!==c||null!==b.onclick||(b.onclick=uc));else if(4!==d&&(a=a.child,null!==a))for(Oe(a,b,c),a=a.sibling;null!==a;)Oe(a,b,c),a=a.sibling}
function Pe(a,b,c){var d=a.tag,e=5===d||6===d;if(e)a=e?a.stateNode:a.stateNode.instance,b?c.insertBefore(a,b):c.appendChild(a);else if(4!==d&&(a=a.child,null!==a))for(Pe(a,b,c),a=a.sibling;null!==a;)Pe(a,b,c),a=a.sibling}function Dh(a,b,c){for(var d=b,e=!1,f,g;;){if(!e){e=d.return;a:for(;;){if(null===e)throw Error(k(160));f=e.stateNode;switch(e.tag){case 5:g=!1;break a;case 3:f=f.containerInfo;g=!0;break a;case 4:f=f.containerInfo;g=!0;break a}e=e.return}e=!0}if(5===d.tag||6===d.tag){a:for(var h=
a,m=d,n=c,l=m;;)if(Ch(h,l,n),null!==l.child&&4!==l.tag)l.child.return=l,l=l.child;else{if(l===m)break a;for(;null===l.sibling;){if(null===l.return||l.return===m)break a;l=l.return}l.sibling.return=l.return;l=l.sibling}g?(h=f,m=d.stateNode,8===h.nodeType?h.parentNode.removeChild(m):h.removeChild(m)):f.removeChild(d.stateNode)}else if(4===d.tag){if(null!==d.child){f=d.stateNode.containerInfo;g=!0;d.child.return=d;d=d.child;continue}}else if(Ch(a,d,c),null!==d.child){d.child.return=d;d=d.child;continue}if(d===
b)break;for(;null===d.sibling;){if(null===d.return||d.return===b)return;d=d.return;4===d.tag&&(e=!1)}d.sibling.return=d.return;d=d.sibling}}function Qe(a,b){switch(b.tag){case 0:case 11:case 14:case 15:case 22:Ah(3,b);return;case 1:return;case 5:var c=b.stateNode;if(null!=c){var d=b.memoizedProps,e=null!==a?a.memoizedProps:d;a=b.type;var f=b.updateQueue;b.updateQueue=null;if(null!==f){c[vc]=d;"input"===a&&"radio"===d.type&&null!=d.name&&If(c,d);Vd(a,e);b=Vd(a,d);for(e=0;e<f.length;e+=2){var g=f[e],
h=f[e+1];"style"===g?gg(c,h):"dangerouslySetInnerHTML"===g?xh(c,h):"children"===g?Wb(c,h):xd(c,g,h,b)}switch(a){case "input":Dd(c,d);break;case "textarea":Lf(c,d);break;case "select":b=c._wrapperState.wasMultiple,c._wrapperState.wasMultiple=!!d.multiple,a=d.value,null!=a?hb(c,!!d.multiple,a,!1):b!==!!d.multiple&&(null!=d.defaultValue?hb(c,!!d.multiple,d.defaultValue,!0):hb(c,!!d.multiple,d.multiple?[]:"",!1))}}}return;case 6:if(null===b.stateNode)throw Error(k(162));b.stateNode.nodeValue=b.memoizedProps;
return;case 3:b=b.stateNode;b.hydrate&&(b.hydrate=!1,bg(b.containerInfo));return;case 12:return;case 13:c=b;null===b.memoizedState?d=!1:(d=!0,c=b.child,Re=Y());if(null!==c)a:for(a=c;;){if(5===a.tag)f=a.stateNode,d?(f=f.style,"function"===typeof f.setProperty?f.setProperty("display","none","important"):f.display="none"):(f=a.stateNode,e=a.memoizedProps.style,e=void 0!==e&&null!==e&&e.hasOwnProperty("display")?e.display:null,f.style.display=fg("display",e));else if(6===a.tag)a.stateNode.nodeValue=d?
"":a.memoizedProps;else if(13===a.tag&&null!==a.memoizedState&&null===a.memoizedState.dehydrated){f=a.child.sibling;f.return=a;a=f;continue}else if(null!==a.child){a.child.return=a;a=a.child;continue}if(a===c)break;for(;null===a.sibling;){if(null===a.return||a.return===c)break a;a=a.return}a.sibling.return=a.return;a=a.sibling}Hh(b);return;case 19:Hh(b);return;case 17:return}throw Error(k(163));}function Hh(a){var b=a.updateQueue;if(null!==b){a.updateQueue=null;var c=a.stateNode;null===c&&(c=a.stateNode=
new pj);b.forEach(function(b){var d=qj.bind(null,a,b);c.has(b)||(c.add(b),b.then(d,d))})}}function Ih(a,b,c){c=Ea(c,null);c.tag=3;c.payload={element:null};var d=b.value;c.callback=function(){cd||(cd=!0,Se=d);Me(a,b)};return c}function Jh(a,b,c){c=Ea(c,null);c.tag=3;var d=a.type.getDerivedStateFromError;if("function"===typeof d){var e=b.value;c.payload=function(){Me(a,b);return d(e)}}var f=a.stateNode;null!==f&&"function"===typeof f.componentDidCatch&&(c.callback=function(){"function"!==typeof d&&
(null===La?La=new Set([this]):La.add(this),Me(a,b));var c=b.stack;this.componentDidCatch(b.value,{componentStack:null!==c?c:""})});return c}function ka(){return(p&(ca|ma))!==H?1073741821-(Y()/10|0):0!==dd?dd:dd=1073741821-(Y()/10|0)}function Va(a,b,c){b=b.mode;if(0===(b&2))return 1073741823;var d=Cc();if(0===(b&4))return 99===d?1073741823:1073741822;if((p&ca)!==H)return P;if(null!==c)a=Fc(a,c.timeoutMs|0||5E3,250);else switch(d){case 99:a=1073741823;break;case 98:a=Fc(a,150,100);break;case 97:case 96:a=
Fc(a,5E3,250);break;case 95:a=2;break;default:throw Error(k(326));}null!==U&&a===P&&--a;return a}function ed(a,b){a.expirationTime<b&&(a.expirationTime=b);var c=a.alternate;null!==c&&c.expirationTime<b&&(c.expirationTime=b);var d=a.return,e=null;if(null===d&&3===a.tag)e=a.stateNode;else for(;null!==d;){c=d.alternate;d.childExpirationTime<b&&(d.childExpirationTime=b);null!==c&&c.childExpirationTime<b&&(c.childExpirationTime=b);if(null===d.return&&3===d.tag){e=d.stateNode;break}d=d.return}null!==e&&
(U===e&&(Kc(b),F===bd&&Ya(e,P)),yh(e,b));return e}function fd(a){var b=a.lastExpiredTime;if(0!==b)return b;b=a.firstPendingTime;if(!Kh(a,b))return b;var c=a.lastPingedTime;a=a.nextKnownPendingLevel;a=c>a?c:a;return 2>=a&&b!==a?0:a}function V(a){if(0!==a.lastExpiredTime)a.callbackExpirationTime=1073741823,a.callbackPriority=99,a.callbackNode=Og(Te.bind(null,a));else{var b=fd(a),c=a.callbackNode;if(0===b)null!==c&&(a.callbackNode=null,a.callbackExpirationTime=0,a.callbackPriority=90);else{var d=ka();
1073741823===b?d=99:1===b||2===b?d=95:(d=10*(1073741821-b)-10*(1073741821-d),d=0>=d?99:250>=d?98:5250>=d?97:95);if(null!==c){var e=a.callbackPriority;if(a.callbackExpirationTime===b&&e>=d)return;c!==Qg&&Rg(c)}a.callbackExpirationTime=b;a.callbackPriority=d;b=1073741823===b?Og(Te.bind(null,a)):Ng(d,Lh.bind(null,a),{timeout:10*(1073741821-b)-Y()});a.callbackNode=b}}}function Lh(a,b){dd=0;if(b)return b=ka(),Ue(a,b),V(a),null;var c=fd(a);if(0!==c){b=a.callbackNode;if((p&(ca|ma))!==H)throw Error(k(327));
xb();a===U&&c===P||$a(a,c);if(null!==t){var d=p;p|=ca;var e=Mh();do try{rj();break}catch(h){Nh(a,h)}while(1);le();p=d;gd.current=e;if(F===hd)throw b=id,$a(a,c),Ya(a,c),V(a),b;if(null===t)switch(e=a.finishedWork=a.current.alternate,a.finishedExpirationTime=c,d=F,U=null,d){case Xa:case hd:throw Error(k(345));case Oh:Ue(a,2<c?2:c);break;case ad:Ya(a,c);d=a.lastSuspendedTime;c===d&&(a.nextKnownPendingLevel=Ve(e));if(1073741823===ta&&(e=Re+Ph-Y(),10<e)){if(jd){var f=a.lastPingedTime;if(0===f||f>=c){a.lastPingedTime=
c;$a(a,c);break}}f=fd(a);if(0!==f&&f!==c)break;if(0!==d&&d!==c){a.lastPingedTime=d;break}a.timeoutHandle=We(ab.bind(null,a),e);break}ab(a);break;case bd:Ya(a,c);d=a.lastSuspendedTime;c===d&&(a.nextKnownPendingLevel=Ve(e));if(jd&&(e=a.lastPingedTime,0===e||e>=c)){a.lastPingedTime=c;$a(a,c);break}e=fd(a);if(0!==e&&e!==c)break;if(0!==d&&d!==c){a.lastPingedTime=d;break}1073741823!==Yb?d=10*(1073741821-Yb)-Y():1073741823===ta?d=0:(d=10*(1073741821-ta)-5E3,e=Y(),c=10*(1073741821-c)-e,d=e-d,0>d&&(d=0),d=
(120>d?120:480>d?480:1080>d?1080:1920>d?1920:3E3>d?3E3:4320>d?4320:1960*sj(d/1960))-d,c<d&&(d=c));if(10<d){a.timeoutHandle=We(ab.bind(null,a),d);break}ab(a);break;case Xe:if(1073741823!==ta&&null!==kd){f=ta;var g=kd;d=g.busyMinDurationMs|0;0>=d?d=0:(e=g.busyDelayMs|0,f=Y()-(10*(1073741821-f)-(g.timeoutMs|0||5E3)),d=f<=e?0:e+d-f);if(10<d){Ya(a,c);a.timeoutHandle=We(ab.bind(null,a),d);break}}ab(a);break;default:throw Error(k(329));}V(a);if(a.callbackNode===b)return Lh.bind(null,a)}}return null}function Te(a){var b=
a.lastExpiredTime;b=0!==b?b:1073741823;if((p&(ca|ma))!==H)throw Error(k(327));xb();a===U&&b===P||$a(a,b);if(null!==t){var c=p;p|=ca;var d=Mh();do try{tj();break}catch(e){Nh(a,e)}while(1);le();p=c;gd.current=d;if(F===hd)throw c=id,$a(a,b),Ya(a,b),V(a),c;if(null!==t)throw Error(k(261));a.finishedWork=a.current.alternate;a.finishedExpirationTime=b;U=null;ab(a);V(a)}return null}function uj(){if(null!==bb){var a=bb;bb=null;a.forEach(function(a,c){Ue(c,a);V(c)});ha()}}function Qh(a,b){var c=p;p|=1;try{return a(b)}finally{p=
c,p===H&&ha()}}function Rh(a,b){var c=p;p&=-2;p|=Ye;try{return a(b)}finally{p=c,p===H&&ha()}}function $a(a,b){a.finishedWork=null;a.finishedExpirationTime=0;var c=a.timeoutHandle;-1!==c&&(a.timeoutHandle=-1,vj(c));if(null!==t)for(c=t.return;null!==c;){var d=c;switch(d.tag){case 1:d=d.type.childContextTypes;null!==d&&void 0!==d&&(q(G),q(B));break;case 3:tb();q(G);q(B);break;case 5:te(d);break;case 4:tb();break;case 13:q(D);break;case 19:q(D);break;case 10:me(d)}c=c.return}U=a;t=Sa(a.current,null);
P=b;F=Xa;id=null;Yb=ta=1073741823;kd=null;Xb=0;jd=!1}function Nh(a,b){do{try{le();Sc.current=Tc;if(Uc)for(var c=z.memoizedState;null!==c;){var d=c.queue;null!==d&&(d.pending=null);c=c.next}Ia=0;J=K=z=null;Uc=!1;if(null===t||null===t.return)return F=hd,id=b,t=null;a:{var e=a,f=t.return,g=t,h=b;b=P;g.effectTag|=2048;g.firstEffect=g.lastEffect=null;if(null!==h&&"object"===typeof h&&"function"===typeof h.then){var m=h;if(0===(g.mode&2)){var n=g.alternate;n?(g.updateQueue=n.updateQueue,g.memoizedState=
n.memoizedState,g.expirationTime=n.expirationTime):(g.updateQueue=null,g.memoizedState=null)}var l=0!==(D.current&1),k=f;do{var p;if(p=13===k.tag){var q=k.memoizedState;if(null!==q)p=null!==q.dehydrated?!0:!1;else{var w=k.memoizedProps;p=void 0===w.fallback?!1:!0!==w.unstable_avoidThisFallback?!0:l?!1:!0}}if(p){var y=k.updateQueue;if(null===y){var r=new Set;r.add(m);k.updateQueue=r}else y.add(m);if(0===(k.mode&2)){k.effectTag|=64;g.effectTag&=-2981;if(1===g.tag)if(null===g.alternate)g.tag=17;else{var O=
Ea(1073741823,null);O.tag=Jc;Fa(g,O)}g.expirationTime=1073741823;break a}h=void 0;g=b;var v=e.pingCache;null===v?(v=e.pingCache=new wj,h=new Set,v.set(m,h)):(h=v.get(m),void 0===h&&(h=new Set,v.set(m,h)));if(!h.has(g)){h.add(g);var x=xj.bind(null,e,m,g);m.then(x,x)}k.effectTag|=4096;k.expirationTime=b;break a}k=k.return}while(null!==k);h=Error((na(g.type)||"A React component")+" suspended while rendering, but no fallback UI was specified.\n\nAdd a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display."+
Bd(g))}F!==Xe&&(F=Oh);h=Le(h,g);k=f;do{switch(k.tag){case 3:m=h;k.effectTag|=4096;k.expirationTime=b;var A=Ih(k,m,b);Ug(k,A);break a;case 1:m=h;var u=k.type,B=k.stateNode;if(0===(k.effectTag&64)&&("function"===typeof u.getDerivedStateFromError||null!==B&&"function"===typeof B.componentDidCatch&&(null===La||!La.has(B)))){k.effectTag|=4096;k.expirationTime=b;var H=Jh(k,m,b);Ug(k,H);break a}}k=k.return}while(null!==k)}t=Sh(t)}catch(cj){b=cj;continue}break}while(1)}function Mh(a){a=gd.current;gd.current=
Tc;return null===a?Tc:a}function Vg(a,b){a<ta&&2<a&&(ta=a);null!==b&&a<Yb&&2<a&&(Yb=a,kd=b)}function Kc(a){a>Xb&&(Xb=a)}function tj(){for(;null!==t;)t=Th(t)}function rj(){for(;null!==t&&!yj();)t=Th(t)}function Th(a){var b=zj(a.alternate,a,P);a.memoizedProps=a.pendingProps;null===b&&(b=Sh(a));Uh.current=null;return b}function Sh(a){t=a;do{var b=t.alternate;a=t.return;if(0===(t.effectTag&2048)){b=hj(b,t,P);if(1===P||1!==t.childExpirationTime){for(var c=0,d=t.child;null!==d;){var e=d.expirationTime,
f=d.childExpirationTime;e>c&&(c=e);f>c&&(c=f);d=d.sibling}t.childExpirationTime=c}if(null!==b)return b;null!==a&&0===(a.effectTag&2048)&&(null===a.firstEffect&&(a.firstEffect=t.firstEffect),null!==t.lastEffect&&(null!==a.lastEffect&&(a.lastEffect.nextEffect=t.firstEffect),a.lastEffect=t.lastEffect),1<t.effectTag&&(null!==a.lastEffect?a.lastEffect.nextEffect=t:a.firstEffect=t,a.lastEffect=t))}else{b=lj(t);if(null!==b)return b.effectTag&=2047,b;null!==a&&(a.firstEffect=a.lastEffect=null,a.effectTag|=
2048)}b=t.sibling;if(null!==b)return b;t=a}while(null!==t);F===Xa&&(F=Xe);return null}function Ve(a){var b=a.expirationTime;a=a.childExpirationTime;return b>a?b:a}function ab(a){var b=Cc();Da(99,Aj.bind(null,a,b));return null}function Aj(a,b){do xb();while(null!==Zb);if((p&(ca|ma))!==H)throw Error(k(327));var c=a.finishedWork,d=a.finishedExpirationTime;if(null===c)return null;a.finishedWork=null;a.finishedExpirationTime=0;if(c===a.current)throw Error(k(177));a.callbackNode=null;a.callbackExpirationTime=
0;a.callbackPriority=90;a.nextKnownPendingLevel=0;var e=Ve(c);a.firstPendingTime=e;d<=a.lastSuspendedTime?a.firstSuspendedTime=a.lastSuspendedTime=a.nextKnownPendingLevel=0:d<=a.firstSuspendedTime&&(a.firstSuspendedTime=d-1);d<=a.lastPingedTime&&(a.lastPingedTime=0);d<=a.lastExpiredTime&&(a.lastExpiredTime=0);a===U&&(t=U=null,P=0);1<c.effectTag?null!==c.lastEffect?(c.lastEffect.nextEffect=c,e=c.firstEffect):e=c:e=c.firstEffect;if(null!==e){var f=p;p|=ma;Uh.current=null;Ze=tc;var g=kg();if(Xd(g)){if("selectionStart"in
g)var h={start:g.selectionStart,end:g.selectionEnd};else a:{h=(h=g.ownerDocument)&&h.defaultView||window;var m=h.getSelection&&h.getSelection();if(m&&0!==m.rangeCount){h=m.anchorNode;var n=m.anchorOffset,q=m.focusNode;m=m.focusOffset;try{h.nodeType,q.nodeType}catch(sb){h=null;break a}var ba=0,w=-1,y=-1,B=0,D=0,r=g,z=null;b:for(;;){for(var v;;){r!==h||0!==n&&3!==r.nodeType||(w=ba+n);r!==q||0!==m&&3!==r.nodeType||(y=ba+m);3===r.nodeType&&(ba+=r.nodeValue.length);if(null===(v=r.firstChild))break;z=r;
r=v}for(;;){if(r===g)break b;z===h&&++B===n&&(w=ba);z===q&&++D===m&&(y=ba);if(null!==(v=r.nextSibling))break;r=z;z=r.parentNode}r=v}h=-1===w||-1===y?null:{start:w,end:y}}else h=null}h=h||{start:0,end:0}}else h=null;$e={activeElementDetached:null,focusedElem:g,selectionRange:h};tc=!1;l=e;do try{Bj()}catch(sb){if(null===l)throw Error(k(330));Za(l,sb);l=l.nextEffect}while(null!==l);l=e;do try{for(g=a,h=b;null!==l;){var x=l.effectTag;x&16&&Wb(l.stateNode,"");if(x&128){var A=l.alternate;if(null!==A){var u=
A.ref;null!==u&&("function"===typeof u?u(null):u.current=null)}}switch(x&1038){case 2:Gh(l);l.effectTag&=-3;break;case 6:Gh(l);l.effectTag&=-3;Qe(l.alternate,l);break;case 1024:l.effectTag&=-1025;break;case 1028:l.effectTag&=-1025;Qe(l.alternate,l);break;case 4:Qe(l.alternate,l);break;case 8:n=l,Dh(g,n,h),Eh(n)}l=l.nextEffect}}catch(sb){if(null===l)throw Error(k(330));Za(l,sb);l=l.nextEffect}while(null!==l);u=$e;A=kg();x=u.focusedElem;h=u.selectionRange;if(A!==x&&x&&x.ownerDocument&&jg(x.ownerDocument.documentElement,
x)){null!==h&&Xd(x)&&(A=h.start,u=h.end,void 0===u&&(u=A),"selectionStart"in x?(x.selectionStart=A,x.selectionEnd=Math.min(u,x.value.length)):(u=(A=x.ownerDocument||document)&&A.defaultView||window,u.getSelection&&(u=u.getSelection(),n=x.textContent.length,g=Math.min(h.start,n),h=void 0===h.end?g:Math.min(h.end,n),!u.extend&&g>h&&(n=h,h=g,g=n),n=ig(x,g),q=ig(x,h),n&&q&&(1!==u.rangeCount||u.anchorNode!==n.node||u.anchorOffset!==n.offset||u.focusNode!==q.node||u.focusOffset!==q.offset)&&(A=A.createRange(),
A.setStart(n.node,n.offset),u.removeAllRanges(),g>h?(u.addRange(A),u.extend(q.node,q.offset)):(A.setEnd(q.node,q.offset),u.addRange(A))))));A=[];for(u=x;u=u.parentNode;)1===u.nodeType&&A.push({element:u,left:u.scrollLeft,top:u.scrollTop});"function"===typeof x.focus&&x.focus();for(x=0;x<A.length;x++)u=A[x],u.element.scrollLeft=u.left,u.element.scrollTop=u.top}tc=!!Ze;$e=Ze=null;a.current=c;l=e;do try{for(x=a;null!==l;){var F=l.effectTag;F&36&&oj(x,l.alternate,l);if(F&128){A=void 0;var E=l.ref;if(null!==
E){var G=l.stateNode;switch(l.tag){case 5:A=G;break;default:A=G}"function"===typeof E?E(A):E.current=A}}l=l.nextEffect}}catch(sb){if(null===l)throw Error(k(330));Za(l,sb);l=l.nextEffect}while(null!==l);l=null;Cj();p=f}else a.current=c;if(ld)ld=!1,Zb=a,$b=b;else for(l=e;null!==l;)b=l.nextEffect,l.nextEffect=null,l=b;b=a.firstPendingTime;0===b&&(La=null);1073741823===b?a===af?ac++:(ac=0,af=a):ac=0;"function"===typeof bf&&bf(c.stateNode,d);V(a);if(cd)throw cd=!1,a=Se,Se=null,a;if((p&Ye)!==H)return null;
ha();return null}function Bj(){for(;null!==l;){var a=l.effectTag;0!==(a&256)&&nj(l.alternate,l);0===(a&512)||ld||(ld=!0,Ng(97,function(){xb();return null}));l=l.nextEffect}}function xb(){if(90!==$b){var a=97<$b?97:$b;$b=90;return Da(a,Dj)}}function Dj(){if(null===Zb)return!1;var a=Zb;Zb=null;if((p&(ca|ma))!==H)throw Error(k(331));var b=p;p|=ma;for(a=a.current.firstEffect;null!==a;){try{var c=a;if(0!==(c.effectTag&512))switch(c.tag){case 0:case 11:case 15:case 22:Ah(5,c),Bh(5,c)}}catch(d){if(null===
a)throw Error(k(330));Za(a,d)}c=a.nextEffect;a.nextEffect=null;a=c}p=b;ha();return!0}function Vh(a,b,c){b=Le(c,b);b=Ih(a,b,1073741823);Fa(a,b);a=ed(a,1073741823);null!==a&&V(a)}function Za(a,b){if(3===a.tag)Vh(a,a,b);else for(var c=a.return;null!==c;){if(3===c.tag){Vh(c,a,b);break}else if(1===c.tag){var d=c.stateNode;if("function"===typeof c.type.getDerivedStateFromError||"function"===typeof d.componentDidCatch&&(null===La||!La.has(d))){a=Le(b,a);a=Jh(c,a,1073741823);Fa(c,a);c=ed(c,1073741823);null!==
c&&V(c);break}}c=c.return}}function xj(a,b,c){var d=a.pingCache;null!==d&&d.delete(b);U===a&&P===c?F===bd||F===ad&&1073741823===ta&&Y()-Re<Ph?$a(a,P):jd=!0:Kh(a,c)&&(b=a.lastPingedTime,0!==b&&b<c||(a.lastPingedTime=c,V(a)))}function qj(a,b){var c=a.stateNode;null!==c&&c.delete(b);b=0;0===b&&(b=ka(),b=Va(b,a,null));a=ed(a,b);null!==a&&V(a)}function Ej(a){if("undefined"===typeof __REACT_DEVTOOLS_GLOBAL_HOOK__)return!1;var b=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(b.isDisabled||!b.supportsFiber)return!0;try{var c=
b.inject(a);bf=function(a,e){try{b.onCommitFiberRoot(c,a,void 0,64===(a.current.effectTag&64))}catch(f){}};Ne=function(a){try{b.onCommitFiberUnmount(c,a)}catch(e){}}}catch(d){}return!0}function Fj(a,b,c,d){this.tag=a;this.key=c;this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null;this.index=0;this.ref=null;this.pendingProps=b;this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null;this.mode=d;this.effectTag=0;this.lastEffect=this.firstEffect=this.nextEffect=
null;this.childExpirationTime=this.expirationTime=0;this.alternate=null}function Ge(a){a=a.prototype;return!(!a||!a.isReactComponent)}function Gj(a){if("function"===typeof a)return Ge(a)?1:0;if(void 0!==a&&null!==a){a=a.$$typeof;if(a===zd)return 11;if(a===Ad)return 14}return 2}function Sa(a,b){var c=a.alternate;null===c?(c=la(a.tag,b,a.key,a.mode),c.elementType=a.elementType,c.type=a.type,c.stateNode=a.stateNode,c.alternate=a,a.alternate=c):(c.pendingProps=b,c.effectTag=0,c.nextEffect=null,c.firstEffect=
null,c.lastEffect=null);c.childExpirationTime=a.childExpirationTime;c.expirationTime=a.expirationTime;c.child=a.child;c.memoizedProps=a.memoizedProps;c.memoizedState=a.memoizedState;c.updateQueue=a.updateQueue;b=a.dependencies;c.dependencies=null===b?null:{expirationTime:b.expirationTime,firstContext:b.firstContext,responders:b.responders};c.sibling=a.sibling;c.index=a.index;c.ref=a.ref;return c}function Oc(a,b,c,d,e,f){var g=2;d=a;if("function"===typeof a)Ge(a)&&(g=1);else if("string"===typeof a)g=
5;else a:switch(a){case Ma:return Ha(c.children,e,f,b);case Hj:g=8;e|=7;break;case Af:g=8;e|=1;break;case kc:return a=la(12,c,b,e|8),a.elementType=kc,a.type=kc,a.expirationTime=f,a;case lc:return a=la(13,c,b,e),a.type=lc,a.elementType=lc,a.expirationTime=f,a;case yd:return a=la(19,c,b,e),a.elementType=yd,a.expirationTime=f,a;default:if("object"===typeof a&&null!==a)switch(a.$$typeof){case Cf:g=10;break a;case Bf:g=9;break a;case zd:g=11;break a;case Ad:g=14;break a;case Ef:g=16;d=null;break a;case Df:g=
22;break a}throw Error(k(130,null==a?a:typeof a,""));}b=la(g,c,b,e);b.elementType=a;b.type=d;b.expirationTime=f;return b}function Ha(a,b,c,d){a=la(7,a,d,b);a.expirationTime=c;return a}function qe(a,b,c){a=la(6,a,null,b);a.expirationTime=c;return a}function re(a,b,c){b=la(4,null!==a.children?a.children:[],a.key,b);b.expirationTime=c;b.stateNode={containerInfo:a.containerInfo,pendingChildren:null,implementation:a.implementation};return b}function Ij(a,b,c){this.tag=b;this.current=null;this.containerInfo=
a;this.pingCache=this.pendingChildren=null;this.finishedExpirationTime=0;this.finishedWork=null;this.timeoutHandle=-1;this.pendingContext=this.context=null;this.hydrate=c;this.callbackNode=null;this.callbackPriority=90;this.lastExpiredTime=this.lastPingedTime=this.nextKnownPendingLevel=this.lastSuspendedTime=this.firstSuspendedTime=this.firstPendingTime=0}function Kh(a,b){var c=a.firstSuspendedTime;a=a.lastSuspendedTime;return 0!==c&&c>=b&&a<=b}function Ya(a,b){var c=a.firstSuspendedTime,d=a.lastSuspendedTime;
c<b&&(a.firstSuspendedTime=b);if(d>b||0===c)a.lastSuspendedTime=b;b<=a.lastPingedTime&&(a.lastPingedTime=0);b<=a.lastExpiredTime&&(a.lastExpiredTime=0)}function yh(a,b){b>a.firstPendingTime&&(a.firstPendingTime=b);var c=a.firstSuspendedTime;0!==c&&(b>=c?a.firstSuspendedTime=a.lastSuspendedTime=a.nextKnownPendingLevel=0:b>=a.lastSuspendedTime&&(a.lastSuspendedTime=b+1),b>a.nextKnownPendingLevel&&(a.nextKnownPendingLevel=b))}function Ue(a,b){var c=a.lastExpiredTime;if(0===c||c>b)a.lastExpiredTime=b}
function md(a,b,c,d){var e=b.current,f=ka(),g=Vb.suspense;f=Va(f,e,g);a:if(c){c=c._reactInternalFiber;b:{if(Na(c)!==c||1!==c.tag)throw Error(k(170));var h=c;do{switch(h.tag){case 3:h=h.stateNode.context;break b;case 1:if(N(h.type)){h=h.stateNode.__reactInternalMemoizedMergedChildContext;break b}}h=h.return}while(null!==h);throw Error(k(171));}if(1===c.tag){var m=c.type;if(N(m)){c=Gg(c,m,h);break a}}c=h}else c=Ca;null===b.context?b.context=c:b.pendingContext=c;b=Ea(f,g);b.payload={element:a};d=void 0===
d?null:d;null!==d&&(b.callback=d);Fa(e,b);Ja(e,f);return f}function cf(a){a=a.current;if(!a.child)return null;switch(a.child.tag){case 5:return a.child.stateNode;default:return a.child.stateNode}}function Wh(a,b){a=a.memoizedState;null!==a&&null!==a.dehydrated&&a.retryTime<b&&(a.retryTime=b)}function df(a,b){Wh(a,b);(a=a.alternate)&&Wh(a,b)}function ef(a,b,c){c=null!=c&&!0===c.hydrate;var d=new Ij(a,b,c),e=la(3,null,null,2===b?7:1===b?3:0);d.current=e;e.stateNode=d;ne(e);a[Lb]=d.current;c&&0!==b&&
xi(a,9===a.nodeType?a:a.ownerDocument);this._internalRoot=d}function bc(a){return!(!a||1!==a.nodeType&&9!==a.nodeType&&11!==a.nodeType&&(8!==a.nodeType||" react-mount-point-unstable "!==a.nodeValue))}function Jj(a,b){b||(b=a?9===a.nodeType?a.documentElement:a.firstChild:null,b=!(!b||1!==b.nodeType||!b.hasAttribute("data-reactroot")));if(!b)for(var c;c=a.lastChild;)a.removeChild(c);return new ef(a,0,b?{hydrate:!0}:void 0)}function nd(a,b,c,d,e){var f=c._reactRootContainer;if(f){var g=f._internalRoot;
if("function"===typeof e){var h=e;e=function(){var a=cf(g);h.call(a)}}md(b,g,a,e)}else{f=c._reactRootContainer=Jj(c,d);g=f._internalRoot;if("function"===typeof e){var m=e;e=function(){var a=cf(g);m.call(a)}}Rh(function(){md(b,g,a,e)})}return cf(g)}function Kj(a,b,c){var d=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null;return{$$typeof:gb,key:null==d?null:""+d,children:a,containerInfo:b,implementation:c}}function Xh(a,b){var c=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null;
if(!bc(b))throw Error(k(200));return Kj(a,b,null,c)}if(!ea)throw Error(k(227));var ki=function(a,b,c,d,e,f,g,h,m){var n=Array.prototype.slice.call(arguments,3);try{b.apply(c,n)}catch(C){this.onError(C)}},yb=!1,gc=null,hc=!1,pd=null,li={onError:function(a){yb=!0;gc=a}},td=null,rf=null,mf=null,ic=null,cb={},jc=[],qd={},db={},rd={},wa=!("undefined"===typeof window||"undefined"===typeof window.document||"undefined"===typeof window.document.createElement),M=ea.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.assign,
sd=null,eb=null,fb=null,ee=function(a,b){return a(b)},eg=function(a,b,c,d,e){return a(b,c,d,e)},vd=function(){},vf=ee,Oa=!1,wd=!1,Z=ea.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler,Lj=Z.unstable_cancelCallback,ff=Z.unstable_now,$f=Z.unstable_scheduleCallback,Mj=Z.unstable_shouldYield,Yh=Z.unstable_requestPaint,Pd=Z.unstable_runWithPriority,Nj=Z.unstable_getCurrentPriorityLevel,Oj=Z.unstable_ImmediatePriority,Zh=Z.unstable_UserBlockingPriority,ag=Z.unstable_NormalPriority,Pj=Z.unstable_LowPriority,
Qj=Z.unstable_IdlePriority,oi=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,wf=Object.prototype.hasOwnProperty,yf={},xf={},E={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(a){E[a]=
new L(a,0,!1,a,null,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(a){var b=a[0];E[b]=new L(b,1,!1,a[1],null,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(a){E[a]=new L(a,2,!1,a.toLowerCase(),null,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(a){E[a]=new L(a,2,!1,a,null,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(a){E[a]=
new L(a,3,!1,a.toLowerCase(),null,!1)});["checked","multiple","muted","selected"].forEach(function(a){E[a]=new L(a,3,!0,a,null,!1)});["capture","download"].forEach(function(a){E[a]=new L(a,4,!1,a,null,!1)});["cols","rows","size","span"].forEach(function(a){E[a]=new L(a,6,!1,a,null,!1)});["rowSpan","start"].forEach(function(a){E[a]=new L(a,5,!1,a.toLowerCase(),null,!1)});var gf=/[\-:]([a-z])/g,hf=function(a){return a[1].toUpperCase()};"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(a){var b=
a.replace(gf,hf);E[b]=new L(b,1,!1,a,null,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(a){var b=a.replace(gf,hf);E[b]=new L(b,1,!1,a,"http://www.w3.org/1999/xlink",!1)});["xml:base","xml:lang","xml:space"].forEach(function(a){var b=a.replace(gf,hf);E[b]=new L(b,1,!1,a,"http://www.w3.org/XML/1998/namespace",!1)});["tabIndex","crossOrigin"].forEach(function(a){E[a]=new L(a,1,!1,a.toLowerCase(),null,!1)});E.xlinkHref=new L("xlinkHref",1,
!1,"xlink:href","http://www.w3.org/1999/xlink",!0);["src","href","action","formAction"].forEach(function(a){E[a]=new L(a,1,!1,a.toLowerCase(),null,!0)});var da=ea.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;da.hasOwnProperty("ReactCurrentDispatcher")||(da.ReactCurrentDispatcher={current:null});da.hasOwnProperty("ReactCurrentBatchConfig")||(da.ReactCurrentBatchConfig={suspense:null});var si=/^(.*)[\\\/]/,Q="function"===typeof Symbol&&Symbol.for,Pc=Q?Symbol.for("react.element"):60103,gb=Q?Symbol.for("react.portal"):
60106,Ma=Q?Symbol.for("react.fragment"):60107,Af=Q?Symbol.for("react.strict_mode"):60108,kc=Q?Symbol.for("react.profiler"):60114,Cf=Q?Symbol.for("react.provider"):60109,Bf=Q?Symbol.for("react.context"):60110,Hj=Q?Symbol.for("react.concurrent_mode"):60111,zd=Q?Symbol.for("react.forward_ref"):60112,lc=Q?Symbol.for("react.suspense"):60113,yd=Q?Symbol.for("react.suspense_list"):60120,Ad=Q?Symbol.for("react.memo"):60115,Ef=Q?Symbol.for("react.lazy"):60116,Df=Q?Symbol.for("react.block"):60121,zf="function"===
typeof Symbol&&Symbol.iterator,od,xh=function(a){return"undefined"!==typeof MSApp&&MSApp.execUnsafeLocalFunction?function(b,c,d,e){MSApp.execUnsafeLocalFunction(function(){return a(b,c,d,e)})}:a}(function(a,b){if("http://www.w3.org/2000/svg"!==a.namespaceURI||"innerHTML"in a)a.innerHTML=b;else{od=od||document.createElement("div");od.innerHTML="<svg>"+b.valueOf().toString()+"</svg>";for(b=od.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;b.firstChild;)a.appendChild(b.firstChild)}}),Wb=function(a,
b){if(b){var c=a.firstChild;if(c&&c===a.lastChild&&3===c.nodeType){c.nodeValue=b;return}}a.textContent=b},ib={animationend:nc("Animation","AnimationEnd"),animationiteration:nc("Animation","AnimationIteration"),animationstart:nc("Animation","AnimationStart"),transitionend:nc("Transition","TransitionEnd")},Id={},Of={};wa&&(Of=document.createElement("div").style,"AnimationEvent"in window||(delete ib.animationend.animation,delete ib.animationiteration.animation,delete ib.animationstart.animation),"TransitionEvent"in
window||delete ib.transitionend.transition);var $h=oc("animationend"),ai=oc("animationiteration"),bi=oc("animationstart"),ci=oc("transitionend"),Db="abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange seeked seeking stalled suspend timeupdate volumechange waiting".split(" "),Pf=new ("function"===typeof WeakMap?WeakMap:Map),Ab=null,wi=function(a){if(a){var b=a._dispatchListeners,c=a._dispatchInstances;
if(Array.isArray(b))for(var d=0;d<b.length&&!a.isPropagationStopped();d++)lf(a,b[d],c[d]);else b&&lf(a,b,c);a._dispatchListeners=null;a._dispatchInstances=null;a.isPersistent()||a.constructor.release(a)}},qc=[],Rd=!1,fa=[],xa=null,ya=null,za=null,Eb=new Map,Fb=new Map,Jb=[],Nd="mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput close cancel copy cut paste click change contextmenu reset submit".split(" "),
yi="focus blur dragenter dragleave mouseover mouseout pointerover pointerout gotpointercapture lostpointercapture".split(" "),dg={},cg=new Map,Td=new Map,Rj=["abort","abort",$h,"animationEnd",ai,"animationIteration",bi,"animationStart","canplay","canPlay","canplaythrough","canPlayThrough","durationchange","durationChange","emptied","emptied","encrypted","encrypted","ended","ended","error","error","gotpointercapture","gotPointerCapture","load","load","loadeddata","loadedData","loadedmetadata","loadedMetadata",
"loadstart","loadStart","lostpointercapture","lostPointerCapture","playing","playing","progress","progress","seeking","seeking","stalled","stalled","suspend","suspend","timeupdate","timeUpdate",ci,"transitionEnd","waiting","waiting"];Sd("blur blur cancel cancel click click close close contextmenu contextMenu copy copy cut cut auxclick auxClick dblclick doubleClick dragend dragEnd dragstart dragStart drop drop focus focus input input invalid invalid keydown keyDown keypress keyPress keyup keyUp mousedown mouseDown mouseup mouseUp paste paste pause pause play play pointercancel pointerCancel pointerdown pointerDown pointerup pointerUp ratechange rateChange reset reset seeked seeked submit submit touchcancel touchCancel touchend touchEnd touchstart touchStart volumechange volumeChange".split(" "),
0);Sd("drag drag dragenter dragEnter dragexit dragExit dragleave dragLeave dragover dragOver mousemove mouseMove mouseout mouseOut mouseover mouseOver pointermove pointerMove pointerout pointerOut pointerover pointerOver scroll scroll toggle toggle touchmove touchMove wheel wheel".split(" "),1);Sd(Rj,2);(function(a,b){for(var c=0;c<a.length;c++)Td.set(a[c],b)})("change selectionchange textInput compositionstart compositionend compositionupdate".split(" "),0);var Hi=Zh,Gi=Pd,tc=!0,Kb={animationIterationCount:!0,
borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,
strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Sj=["Webkit","ms","Moz","O"];Object.keys(Kb).forEach(function(a){Sj.forEach(function(b){b=b+a.charAt(0).toUpperCase()+a.substring(1);Kb[b]=Kb[a]})});var Ii=M({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0}),ng="$",og="/$",$d="$?",Zd="$!",Ze=null,$e=null,We="function"===typeof setTimeout?setTimeout:void 0,vj="function"===
typeof clearTimeout?clearTimeout:void 0,jf=Math.random().toString(36).slice(2),Aa="__reactInternalInstance$"+jf,vc="__reactEventHandlers$"+jf,Lb="__reactContainere$"+jf,Ba=null,ce=null,wc=null;M(R.prototype,{preventDefault:function(){this.defaultPrevented=!0;var a=this.nativeEvent;a&&(a.preventDefault?a.preventDefault():"unknown"!==typeof a.returnValue&&(a.returnValue=!1),this.isDefaultPrevented=xc)},stopPropagation:function(){var a=this.nativeEvent;a&&(a.stopPropagation?a.stopPropagation():"unknown"!==
typeof a.cancelBubble&&(a.cancelBubble=!0),this.isPropagationStopped=xc)},persist:function(){this.isPersistent=xc},isPersistent:yc,destructor:function(){var a=this.constructor.Interface,b;for(b in a)this[b]=null;this.nativeEvent=this._targetInst=this.dispatchConfig=null;this.isPropagationStopped=this.isDefaultPrevented=yc;this._dispatchInstances=this._dispatchListeners=null}});R.Interface={type:null,target:null,currentTarget:function(){return null},eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(a){return a.timeStamp||
Date.now()},defaultPrevented:null,isTrusted:null};R.extend=function(a){function b(){return c.apply(this,arguments)}var c=this,d=function(){};d.prototype=c.prototype;d=new d;M(d,b.prototype);b.prototype=d;b.prototype.constructor=b;b.Interface=M({},c.Interface,a);b.extend=c.extend;sg(b);return b};sg(R);var Tj=R.extend({data:null}),Uj=R.extend({data:null}),Ni=[9,13,27,32],de=wa&&"CompositionEvent"in window,cc=null;wa&&"documentMode"in document&&(cc=document.documentMode);var Vj=wa&&"TextEvent"in window&&
!cc,xg=wa&&(!de||cc&&8<cc&&11>=cc),wg=String.fromCharCode(32),ua={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["compositionend","keypress","textInput","paste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:"blur compositionend keydown keypress keyup mousedown".split(" ")},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},
dependencies:"blur compositionstart keydown keypress keyup mousedown".split(" ")},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:"blur compositionupdate keydown keypress keyup mousedown".split(" ")}},vg=!1,mb=!1,Wj={eventTypes:ua,extractEvents:function(a,b,c,d,e){var f;if(de)b:{switch(a){case "compositionstart":var g=ua.compositionStart;break b;case "compositionend":g=ua.compositionEnd;break b;case "compositionupdate":g=
ua.compositionUpdate;break b}g=void 0}else mb?tg(a,c)&&(g=ua.compositionEnd):"keydown"===a&&229===c.keyCode&&(g=ua.compositionStart);g?(xg&&"ko"!==c.locale&&(mb||g!==ua.compositionStart?g===ua.compositionEnd&&mb&&(f=rg()):(Ba=d,ce="value"in Ba?Ba.value:Ba.textContent,mb=!0)),e=Tj.getPooled(g,b,c,d),f?e.data=f:(f=ug(c),null!==f&&(e.data=f)),lb(e),f=e):f=null;(a=Vj?Oi(a,c):Pi(a,c))?(b=Uj.getPooled(ua.beforeInput,b,c,d),b.data=a,lb(b)):b=null;return null===f?b:null===b?f:[f,b]}},Qi={color:!0,date:!0,
datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0},Ag={change:{phasedRegistrationNames:{bubbled:"onChange",captured:"onChangeCapture"},dependencies:"blur change click focus input keydown keyup selectionchange".split(" ")}},Mb=null,Nb=null,kf=!1;wa&&(kf=Tf("input")&&(!document.documentMode||9<document.documentMode));var Xj={eventTypes:Ag,_isInputEventSupported:kf,extractEvents:function(a,b,c,d,e){e=b?Pa(b):window;var f=
e.nodeName&&e.nodeName.toLowerCase();if("select"===f||"input"===f&&"file"===e.type)var g=Si;else if(yg(e))if(kf)g=Wi;else{g=Ui;var h=Ti}else(f=e.nodeName)&&"input"===f.toLowerCase()&&("checkbox"===e.type||"radio"===e.type)&&(g=Vi);if(g&&(g=g(a,b)))return zg(g,c,d);h&&h(a,e,b);"blur"===a&&(a=e._wrapperState)&&a.controlled&&"number"===e.type&&Ed(e,"number",e.value)}},dc=R.extend({view:null,detail:null}),Yi={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"},di=0,ei=0,fi=!1,gi=!1,ec=dc.extend({screenX:null,
screenY:null,clientX:null,clientY:null,pageX:null,pageY:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,getModifierState:fe,button:null,buttons:null,relatedTarget:function(a){return a.relatedTarget||(a.fromElement===a.srcElement?a.toElement:a.fromElement)},movementX:function(a){if("movementX"in a)return a.movementX;var b=di;di=a.screenX;return fi?"mousemove"===a.type?a.screenX-b:0:(fi=!0,0)},movementY:function(a){if("movementY"in a)return a.movementY;var b=ei;ei=a.screenY;return gi?"mousemove"===
a.type?a.screenY-b:0:(gi=!0,0)}}),hi=ec.extend({pointerId:null,width:null,height:null,pressure:null,tangentialPressure:null,tiltX:null,tiltY:null,twist:null,pointerType:null,isPrimary:null}),fc={mouseEnter:{registrationName:"onMouseEnter",dependencies:["mouseout","mouseover"]},mouseLeave:{registrationName:"onMouseLeave",dependencies:["mouseout","mouseover"]},pointerEnter:{registrationName:"onPointerEnter",dependencies:["pointerout","pointerover"]},pointerLeave:{registrationName:"onPointerLeave",dependencies:["pointerout",
"pointerover"]}},Yj={eventTypes:fc,extractEvents:function(a,b,c,d,e){var f="mouseover"===a||"pointerover"===a,g="mouseout"===a||"pointerout"===a;if(f&&0===(e&32)&&(c.relatedTarget||c.fromElement)||!g&&!f)return null;f=d.window===d?d:(f=d.ownerDocument)?f.defaultView||f.parentWindow:window;if(g){if(g=b,b=(b=c.relatedTarget||c.toElement)?Bb(b):null,null!==b){var h=Na(b);if(b!==h||5!==b.tag&&6!==b.tag)b=null}}else g=null;if(g===b)return null;if("mouseout"===a||"mouseover"===a){var m=ec;var n=fc.mouseLeave;
var l=fc.mouseEnter;var k="mouse"}else if("pointerout"===a||"pointerover"===a)m=hi,n=fc.pointerLeave,l=fc.pointerEnter,k="pointer";a=null==g?f:Pa(g);f=null==b?f:Pa(b);n=m.getPooled(n,g,c,d);n.type=k+"leave";n.target=a;n.relatedTarget=f;c=m.getPooled(l,b,c,d);c.type=k+"enter";c.target=f;c.relatedTarget=a;d=g;k=b;if(d&&k)a:{m=d;l=k;g=0;for(a=m;a;a=pa(a))g++;a=0;for(b=l;b;b=pa(b))a++;for(;0<g-a;)m=pa(m),g--;for(;0<a-g;)l=pa(l),a--;for(;g--;){if(m===l||m===l.alternate)break a;m=pa(m);l=pa(l)}m=null}else m=
null;l=m;for(m=[];d&&d!==l;){g=d.alternate;if(null!==g&&g===l)break;m.push(d);d=pa(d)}for(d=[];k&&k!==l;){g=k.alternate;if(null!==g&&g===l)break;d.push(k);k=pa(k)}for(k=0;k<m.length;k++)be(m[k],"bubbled",n);for(k=d.length;0<k--;)be(d[k],"captured",c);return 0===(e&64)?[n]:[n,c]}},Qa="function"===typeof Object.is?Object.is:Zi,$i=Object.prototype.hasOwnProperty,Zj=wa&&"documentMode"in document&&11>=document.documentMode,Eg={select:{phasedRegistrationNames:{bubbled:"onSelect",captured:"onSelectCapture"},
dependencies:"blur contextmenu dragend focus keydown keyup mousedown mouseup selectionchange".split(" ")}},nb=null,he=null,Pb=null,ge=!1,ak={eventTypes:Eg,extractEvents:function(a,b,c,d,e,f){e=f||(d.window===d?d.document:9===d.nodeType?d:d.ownerDocument);if(!(f=!e)){a:{e=Jd(e);f=rd.onSelect;for(var g=0;g<f.length;g++)if(!e.has(f[g])){e=!1;break a}e=!0}f=!e}if(f)return null;e=b?Pa(b):window;switch(a){case "focus":if(yg(e)||"true"===e.contentEditable)nb=e,he=b,Pb=null;break;case "blur":Pb=he=nb=null;
break;case "mousedown":ge=!0;break;case "contextmenu":case "mouseup":case "dragend":return ge=!1,Dg(c,d);case "selectionchange":if(Zj)break;case "keydown":case "keyup":return Dg(c,d)}return null}},bk=R.extend({animationName:null,elapsedTime:null,pseudoElement:null}),ck=R.extend({clipboardData:function(a){return"clipboardData"in a?a.clipboardData:window.clipboardData}}),dk=dc.extend({relatedTarget:null}),ek={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",
Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},fk={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",
224:"Meta"},gk=dc.extend({key:function(a){if(a.key){var b=ek[a.key]||a.key;if("Unidentified"!==b)return b}return"keypress"===a.type?(a=Ac(a),13===a?"Enter":String.fromCharCode(a)):"keydown"===a.type||"keyup"===a.type?fk[a.keyCode]||"Unidentified":""},location:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,repeat:null,locale:null,getModifierState:fe,charCode:function(a){return"keypress"===a.type?Ac(a):0},keyCode:function(a){return"keydown"===a.type||"keyup"===a.type?a.keyCode:0},which:function(a){return"keypress"===
a.type?Ac(a):"keydown"===a.type||"keyup"===a.type?a.keyCode:0}}),hk=ec.extend({dataTransfer:null}),ik=dc.extend({touches:null,targetTouches:null,changedTouches:null,altKey:null,metaKey:null,ctrlKey:null,shiftKey:null,getModifierState:fe}),jk=R.extend({propertyName:null,elapsedTime:null,pseudoElement:null}),kk=ec.extend({deltaX:function(a){return"deltaX"in a?a.deltaX:"wheelDeltaX"in a?-a.wheelDeltaX:0},deltaY:function(a){return"deltaY"in a?a.deltaY:"wheelDeltaY"in a?-a.wheelDeltaY:"wheelDelta"in a?
-a.wheelDelta:0},deltaZ:null,deltaMode:null}),lk={eventTypes:dg,extractEvents:function(a,b,c,d,e){e=cg.get(a);if(!e)return null;switch(a){case "keypress":if(0===Ac(c))return null;case "keydown":case "keyup":a=gk;break;case "blur":case "focus":a=dk;break;case "click":if(2===c.button)return null;case "auxclick":case "dblclick":case "mousedown":case "mousemove":case "mouseup":case "mouseout":case "mouseover":case "contextmenu":a=ec;break;case "drag":case "dragend":case "dragenter":case "dragexit":case "dragleave":case "dragover":case "dragstart":case "drop":a=
hk;break;case "touchcancel":case "touchend":case "touchmove":case "touchstart":a=ik;break;case $h:case ai:case bi:a=bk;break;case ci:a=jk;break;case "scroll":a=dc;break;case "wheel":a=kk;break;case "copy":case "cut":case "paste":a=ck;break;case "gotpointercapture":case "lostpointercapture":case "pointercancel":case "pointerdown":case "pointermove":case "pointerout":case "pointerover":case "pointerup":a=hi;break;default:a=R}b=a.getPooled(e,b,c,d);lb(b);return b}};(function(a){if(ic)throw Error(k(101));
ic=Array.prototype.slice.call(a);nf()})("ResponderEventPlugin SimpleEventPlugin EnterLeaveEventPlugin ChangeEventPlugin SelectEventPlugin BeforeInputEventPlugin".split(" "));(function(a,b,c){td=a;rf=b;mf=c})(ae,Hb,Pa);pf({SimpleEventPlugin:lk,EnterLeaveEventPlugin:Yj,ChangeEventPlugin:Xj,SelectEventPlugin:ak,BeforeInputEventPlugin:Wj});var ie=[],ob=-1,Ca={},B={current:Ca},G={current:!1},Ra=Ca,bj=Pd,je=$f,Rg=Lj,aj=Nj,Dc=Oj,Ig=Zh,Jg=ag,Kg=Pj,Lg=Qj,Qg={},yj=Mj,Cj=void 0!==Yh?Yh:function(){},qa=null,
Ec=null,ke=!1,ii=ff(),Y=1E4>ii?ff:function(){return ff()-ii},Ic={current:null},Hc=null,qb=null,Gc=null,Tg=0,Jc=2,Ga=!1,Vb=da.ReactCurrentBatchConfig,$g=(new ea.Component).refs,Mc={isMounted:function(a){return(a=a._reactInternalFiber)?Na(a)===a:!1},enqueueSetState:function(a,b,c){a=a._reactInternalFiber;var d=ka(),e=Vb.suspense;d=Va(d,a,e);e=Ea(d,e);e.payload=b;void 0!==c&&null!==c&&(e.callback=c);Fa(a,e);Ja(a,d)},enqueueReplaceState:function(a,b,c){a=a._reactInternalFiber;var d=ka(),e=Vb.suspense;
d=Va(d,a,e);e=Ea(d,e);e.tag=1;e.payload=b;void 0!==c&&null!==c&&(e.callback=c);Fa(a,e);Ja(a,d)},enqueueForceUpdate:function(a,b){a=a._reactInternalFiber;var c=ka(),d=Vb.suspense;c=Va(c,a,d);d=Ea(c,d);d.tag=Jc;void 0!==b&&null!==b&&(d.callback=b);Fa(a,d);Ja(a,c)}},Qc=Array.isArray,wb=ah(!0),Fe=ah(!1),Sb={},ja={current:Sb},Ub={current:Sb},Tb={current:Sb},D={current:0},Sc=da.ReactCurrentDispatcher,X=da.ReactCurrentBatchConfig,Ia=0,z=null,K=null,J=null,Uc=!1,Tc={readContext:W,useCallback:S,useContext:S,
useEffect:S,useImperativeHandle:S,useLayoutEffect:S,useMemo:S,useReducer:S,useRef:S,useState:S,useDebugValue:S,useResponder:S,useDeferredValue:S,useTransition:S},dj={readContext:W,useCallback:ih,useContext:W,useEffect:eh,useImperativeHandle:function(a,b,c){c=null!==c&&void 0!==c?c.concat([a]):null;return ze(4,2,gh.bind(null,b,a),c)},useLayoutEffect:function(a,b){return ze(4,2,a,b)},useMemo:function(a,b){var c=ub();b=void 0===b?null:b;a=a();c.memoizedState=[a,b];return a},useReducer:function(a,b,c){var d=
ub();b=void 0!==c?c(b):b;d.memoizedState=d.baseState=b;a=d.queue={pending:null,dispatch:null,lastRenderedReducer:a,lastRenderedState:b};a=a.dispatch=ch.bind(null,z,a);return[d.memoizedState,a]},useRef:function(a){var b=ub();a={current:a};return b.memoizedState=a},useState:xe,useDebugValue:Be,useResponder:ue,useDeferredValue:function(a,b){var c=xe(a),d=c[0],e=c[1];eh(function(){var c=X.suspense;X.suspense=void 0===b?null:b;try{e(a)}finally{X.suspense=c}},[a,b]);return d},useTransition:function(a){var b=
xe(!1),c=b[0];b=b[1];return[ih(Ce.bind(null,b,a),[b,a]),c]}},ej={readContext:W,useCallback:Yc,useContext:W,useEffect:Xc,useImperativeHandle:hh,useLayoutEffect:fh,useMemo:jh,useReducer:Vc,useRef:dh,useState:function(a){return Vc(Ua)},useDebugValue:Be,useResponder:ue,useDeferredValue:function(a,b){var c=Vc(Ua),d=c[0],e=c[1];Xc(function(){var c=X.suspense;X.suspense=void 0===b?null:b;try{e(a)}finally{X.suspense=c}},[a,b]);return d},useTransition:function(a){var b=Vc(Ua),c=b[0];b=b[1];return[Yc(Ce.bind(null,
b,a),[b,a]),c]}},fj={readContext:W,useCallback:Yc,useContext:W,useEffect:Xc,useImperativeHandle:hh,useLayoutEffect:fh,useMemo:jh,useReducer:Wc,useRef:dh,useState:function(a){return Wc(Ua)},useDebugValue:Be,useResponder:ue,useDeferredValue:function(a,b){var c=Wc(Ua),d=c[0],e=c[1];Xc(function(){var c=X.suspense;X.suspense=void 0===b?null:b;try{e(a)}finally{X.suspense=c}},[a,b]);return d},useTransition:function(a){var b=Wc(Ua),c=b[0];b=b[1];return[Yc(Ce.bind(null,b,a),[b,a]),c]}},ra=null,Ka=null,Wa=
!1,gj=da.ReactCurrentOwner,ia=!1,Je={dehydrated:null,retryTime:0};var jj=function(a,b,c,d){for(c=b.child;null!==c;){if(5===c.tag||6===c.tag)a.appendChild(c.stateNode);else if(4!==c.tag&&null!==c.child){c.child.return=c;c=c.child;continue}if(c===b)break;for(;null===c.sibling;){if(null===c.return||c.return===b)return;c=c.return}c.sibling.return=c.return;c=c.sibling}};var wh=function(a){};var ij=function(a,b,c,d,e){var f=a.memoizedProps;if(f!==d){var g=b.stateNode;Ta(ja.current);a=null;switch(c){case "input":f=
Cd(g,f);d=Cd(g,d);a=[];break;case "option":f=Fd(g,f);d=Fd(g,d);a=[];break;case "select":f=M({},f,{value:void 0});d=M({},d,{value:void 0});a=[];break;case "textarea":f=Gd(g,f);d=Gd(g,d);a=[];break;default:"function"!==typeof f.onClick&&"function"===typeof d.onClick&&(g.onclick=uc)}Ud(c,d);var h,m;c=null;for(h in f)if(!d.hasOwnProperty(h)&&f.hasOwnProperty(h)&&null!=f[h])if("style"===h)for(m in g=f[h],g)g.hasOwnProperty(m)&&(c||(c={}),c[m]="");else"dangerouslySetInnerHTML"!==h&&"children"!==h&&"suppressContentEditableWarning"!==
h&&"suppressHydrationWarning"!==h&&"autoFocus"!==h&&(db.hasOwnProperty(h)?a||(a=[]):(a=a||[]).push(h,null));for(h in d){var k=d[h];g=null!=f?f[h]:void 0;if(d.hasOwnProperty(h)&&k!==g&&(null!=k||null!=g))if("style"===h)if(g){for(m in g)!g.hasOwnProperty(m)||k&&k.hasOwnProperty(m)||(c||(c={}),c[m]="");for(m in k)k.hasOwnProperty(m)&&g[m]!==k[m]&&(c||(c={}),c[m]=k[m])}else c||(a||(a=[]),a.push(h,c)),c=k;else"dangerouslySetInnerHTML"===h?(k=k?k.__html:void 0,g=g?g.__html:void 0,null!=k&&g!==k&&(a=a||
[]).push(h,k)):"children"===h?g===k||"string"!==typeof k&&"number"!==typeof k||(a=a||[]).push(h,""+k):"suppressContentEditableWarning"!==h&&"suppressHydrationWarning"!==h&&(db.hasOwnProperty(h)?(null!=k&&oa(e,h),a||g===k||(a=[])):(a=a||[]).push(h,k))}c&&(a=a||[]).push("style",c);e=a;if(b.updateQueue=e)b.effectTag|=4}};var kj=function(a,b,c,d){c!==d&&(b.effectTag|=4)};var pj="function"===typeof WeakSet?WeakSet:Set,wj="function"===typeof WeakMap?WeakMap:Map,sj=Math.ceil,gd=da.ReactCurrentDispatcher,
Uh=da.ReactCurrentOwner,H=0,Ye=8,ca=16,ma=32,Xa=0,hd=1,Oh=2,ad=3,bd=4,Xe=5,p=H,U=null,t=null,P=0,F=Xa,id=null,ta=1073741823,Yb=1073741823,kd=null,Xb=0,jd=!1,Re=0,Ph=500,l=null,cd=!1,Se=null,La=null,ld=!1,Zb=null,$b=90,bb=null,ac=0,af=null,dd=0,Ja=function(a,b){if(50<ac)throw ac=0,af=null,Error(k(185));a=ed(a,b);if(null!==a){var c=Cc();1073741823===b?(p&Ye)!==H&&(p&(ca|ma))===H?Te(a):(V(a),p===H&&ha()):V(a);(p&4)===H||98!==c&&99!==c||(null===bb?bb=new Map([[a,b]]):(c=bb.get(a),(void 0===c||c>b)&&bb.set(a,
b)))}};var zj=function(a,b,c){var d=b.expirationTime;if(null!==a){var e=b.pendingProps;if(a.memoizedProps!==e||G.current)ia=!0;else{if(d<c){ia=!1;switch(b.tag){case 3:sh(b);Ee();break;case 5:bh(b);if(b.mode&4&&1!==c&&e.hidden)return b.expirationTime=b.childExpirationTime=1,null;break;case 1:N(b.type)&&Bc(b);break;case 4:se(b,b.stateNode.containerInfo);break;case 10:d=b.memoizedProps.value;e=b.type._context;y(Ic,e._currentValue);e._currentValue=d;break;case 13:if(null!==b.memoizedState){d=b.child.childExpirationTime;
if(0!==d&&d>=c)return th(a,b,c);y(D,D.current&1);b=sa(a,b,c);return null!==b?b.sibling:null}y(D,D.current&1);break;case 19:d=b.childExpirationTime>=c;if(0!==(a.effectTag&64)){if(d)return vh(a,b,c);b.effectTag|=64}e=b.memoizedState;null!==e&&(e.rendering=null,e.tail=null);y(D,D.current);if(!d)return null}return sa(a,b,c)}ia=!1}}else ia=!1;b.expirationTime=0;switch(b.tag){case 2:d=b.type;null!==a&&(a.alternate=null,b.alternate=null,b.effectTag|=2);a=b.pendingProps;e=pb(b,B.current);rb(b,c);e=we(null,
b,d,a,e,c);b.effectTag|=1;if("object"===typeof e&&null!==e&&"function"===typeof e.render&&void 0===e.$$typeof){b.tag=1;b.memoizedState=null;b.updateQueue=null;if(N(d)){var f=!0;Bc(b)}else f=!1;b.memoizedState=null!==e.state&&void 0!==e.state?e.state:null;ne(b);var g=d.getDerivedStateFromProps;"function"===typeof g&&Lc(b,d,g,a);e.updater=Mc;b.stateNode=e;e._reactInternalFiber=b;pe(b,d,a,c);b=Ie(null,b,d,!0,f,c)}else b.tag=0,T(null,b,e,c),b=b.child;return b;case 16:a:{e=b.elementType;null!==a&&(a.alternate=
null,b.alternate=null,b.effectTag|=2);a=b.pendingProps;ri(e);if(1!==e._status)throw e._result;e=e._result;b.type=e;f=b.tag=Gj(e);a=aa(e,a);switch(f){case 0:b=He(null,b,e,a,c);break a;case 1:b=rh(null,b,e,a,c);break a;case 11:b=nh(null,b,e,a,c);break a;case 14:b=oh(null,b,e,aa(e.type,a),d,c);break a}throw Error(k(306,e,""));}return b;case 0:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:aa(d,e),He(a,b,d,e,c);case 1:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:aa(d,e),rh(a,b,d,e,c);
case 3:sh(b);d=b.updateQueue;if(null===a||null===d)throw Error(k(282));d=b.pendingProps;e=b.memoizedState;e=null!==e?e.element:null;oe(a,b);Qb(b,d,null,c);d=b.memoizedState.element;if(d===e)Ee(),b=sa(a,b,c);else{if(e=b.stateNode.hydrate)Ka=kb(b.stateNode.containerInfo.firstChild),ra=b,e=Wa=!0;if(e)for(c=Fe(b,null,d,c),b.child=c;c;)c.effectTag=c.effectTag&-3|1024,c=c.sibling;else T(a,b,d,c),Ee();b=b.child}return b;case 5:return bh(b),null===a&&De(b),d=b.type,e=b.pendingProps,f=null!==a?a.memoizedProps:
null,g=e.children,Yd(d,e)?g=null:null!==f&&Yd(d,f)&&(b.effectTag|=16),qh(a,b),b.mode&4&&1!==c&&e.hidden?(b.expirationTime=b.childExpirationTime=1,b=null):(T(a,b,g,c),b=b.child),b;case 6:return null===a&&De(b),null;case 13:return th(a,b,c);case 4:return se(b,b.stateNode.containerInfo),d=b.pendingProps,null===a?b.child=wb(b,null,d,c):T(a,b,d,c),b.child;case 11:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:aa(d,e),nh(a,b,d,e,c);case 7:return T(a,b,b.pendingProps,c),b.child;case 8:return T(a,
b,b.pendingProps.children,c),b.child;case 12:return T(a,b,b.pendingProps.children,c),b.child;case 10:a:{d=b.type._context;e=b.pendingProps;g=b.memoizedProps;f=e.value;var h=b.type._context;y(Ic,h._currentValue);h._currentValue=f;if(null!==g)if(h=g.value,f=Qa(h,f)?0:("function"===typeof d._calculateChangedBits?d._calculateChangedBits(h,f):1073741823)|0,0===f){if(g.children===e.children&&!G.current){b=sa(a,b,c);break a}}else for(h=b.child,null!==h&&(h.return=b);null!==h;){var m=h.dependencies;if(null!==
m){g=h.child;for(var l=m.firstContext;null!==l;){if(l.context===d&&0!==(l.observedBits&f)){1===h.tag&&(l=Ea(c,null),l.tag=Jc,Fa(h,l));h.expirationTime<c&&(h.expirationTime=c);l=h.alternate;null!==l&&l.expirationTime<c&&(l.expirationTime=c);Sg(h.return,c);m.expirationTime<c&&(m.expirationTime=c);break}l=l.next}}else g=10===h.tag?h.type===b.type?null:h.child:h.child;if(null!==g)g.return=h;else for(g=h;null!==g;){if(g===b){g=null;break}h=g.sibling;if(null!==h){h.return=g.return;g=h;break}g=g.return}h=
g}T(a,b,e.children,c);b=b.child}return b;case 9:return e=b.type,f=b.pendingProps,d=f.children,rb(b,c),e=W(e,f.unstable_observedBits),d=d(e),b.effectTag|=1,T(a,b,d,c),b.child;case 14:return e=b.type,f=aa(e,b.pendingProps),f=aa(e.type,f),oh(a,b,e,f,d,c);case 15:return ph(a,b,b.type,b.pendingProps,d,c);case 17:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:aa(d,e),null!==a&&(a.alternate=null,b.alternate=null,b.effectTag|=2),b.tag=1,N(d)?(a=!0,Bc(b)):a=!1,rb(b,c),Yg(b,d,e),pe(b,d,e,c),Ie(null,
b,d,!0,a,c);case 19:return vh(a,b,c)}throw Error(k(156,b.tag));};var bf=null,Ne=null,la=function(a,b,c,d){return new Fj(a,b,c,d)};ef.prototype.render=function(a){md(a,this._internalRoot,null,null)};ef.prototype.unmount=function(){var a=this._internalRoot,b=a.containerInfo;md(null,a,null,function(){b[Lb]=null})};var Di=function(a){if(13===a.tag){var b=Fc(ka(),150,100);Ja(a,b);df(a,b)}};var Yf=function(a){13===a.tag&&(Ja(a,3),df(a,3))};var Bi=function(a){if(13===a.tag){var b=ka();b=Va(b,a,null);Ja(a,
b);df(a,b)}};sd=function(a,b,c){switch(b){case "input":Dd(a,c);b=c.name;if("radio"===c.type&&null!=b){for(c=a;c.parentNode;)c=c.parentNode;c=c.querySelectorAll("input[name="+JSON.stringify(""+b)+'][type="radio"]');for(b=0;b<c.length;b++){var d=c[b];if(d!==a&&d.form===a.form){var e=ae(d);if(!e)throw Error(k(90));Gf(d);Dd(d,e)}}}break;case "textarea":Lf(a,c);break;case "select":b=c.value,null!=b&&hb(a,!!c.multiple,b,!1)}};(function(a,b,c,d){ee=a;eg=b;vd=c;vf=d})(Qh,function(a,b,c,d,e){var f=p;p|=4;
try{return Da(98,a.bind(null,b,c,d,e))}finally{p=f,p===H&&ha()}},function(){(p&(1|ca|ma))===H&&(uj(),xb())},function(a,b){var c=p;p|=2;try{return a(b)}finally{p=c,p===H&&ha()}});var mk={Events:[Hb,Pa,ae,pf,qd,lb,function(a){Kd(a,Ki)},sf,tf,sc,pc,xb,{current:!1}]};(function(a){var b=a.findFiberByHostInstance;return Ej(M({},a,{overrideHookState:null,overrideProps:null,setSuspenseHandler:null,scheduleUpdate:null,currentDispatcherRef:da.ReactCurrentDispatcher,findHostInstanceByFiber:function(a){a=Sf(a);
return null===a?null:a.stateNode},findFiberByHostInstance:function(a){return b?b(a):null},findHostInstancesForRefresh:null,scheduleRefresh:null,scheduleRoot:null,setRefreshHandler:null,getCurrentFiber:null}))})({findFiberByHostInstance:Bb,bundleType:0,version:"16.13.1",rendererPackageName:"react-dom"});I.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=mk;I.createPortal=Xh;I.findDOMNode=function(a){if(null==a)return null;if(1===a.nodeType)return a;var b=a._reactInternalFiber;if(void 0===
b){if("function"===typeof a.render)throw Error(k(188));throw Error(k(268,Object.keys(a)));}a=Sf(b);a=null===a?null:a.stateNode;return a};I.flushSync=function(a,b){if((p&(ca|ma))!==H)throw Error(k(187));var c=p;p|=1;try{return Da(99,a.bind(null,b))}finally{p=c,ha()}};I.hydrate=function(a,b,c){if(!bc(b))throw Error(k(200));return nd(null,a,b,!0,c)};I.render=function(a,b,c){if(!bc(b))throw Error(k(200));return nd(null,a,b,!1,c)};I.unmountComponentAtNode=function(a){if(!bc(a))throw Error(k(40));return a._reactRootContainer?
(Rh(function(){nd(null,null,a,!1,function(){a._reactRootContainer=null;a[Lb]=null})}),!0):!1};I.unstable_batchedUpdates=Qh;I.unstable_createPortal=function(a,b){return Xh(a,b,2<arguments.length&&void 0!==arguments[2]?arguments[2]:null)};I.unstable_renderSubtreeIntoContainer=function(a,b,c,d){if(!bc(c))throw Error(k(200));if(null==a||void 0===a._reactInternalFiber)throw Error(k(38));return nd(a,b,c,!1,d)};I.version="16.13.1"});
</script>
<script>const e = React.createElement;
function pathToString(path) {
if (path[0] === '/') {
return '/' + path.slice(1).join('/');
} else {
return path.join('/');
}
}
function findCommonPath(files) {
if (!files || !files.length) {
return [];
}
function isPrefix(arr, prefix) {
if (arr.length < prefix.length) {
return false;
}
for (let i = prefix.length - 1; i >= 0; --i) {
if (arr[i] !== prefix[i]) {
return false;
}
}
return true;
}
let commonPath = files[0].path.slice(0, -1);
while (commonPath.length) {
if (files.every(file => isPrefix(file.path, commonPath))) {
break;
}
commonPath.pop();
}
return commonPath;
}
function findFolders(files) {
if (!files || !files.length) {
return [];
}
let folders = files.filter(file => file.path.length > 1).map(file => file.path[0]);
folders = [...new Set(folders)]; // unique
folders.sort();
folders = folders.map(folder => {
let filesInFolder = files
.filter(file => file.path[0] === folder)
.map(file => ({
...file,
path: file.path.slice(1),
parent: [...file.parent, file.path[0]],
}));
const children = findFolders(filesInFolder); // recursion
return {
is_folder: true,
path: [folder],
parent: files[0].parent,
children,
covered: children.reduce((sum, file) => sum + file.covered, 0),
coverable: children.reduce((sum, file) => sum + file.coverable, 0),
prevRun: {
covered: children.reduce((sum, file) => sum + file.prevRun.covered, 0),
coverable: children.reduce((sum, file) => sum + file.prevRun.coverable, 0),
}
};
});
return [
...folders,
...files.filter(file => file.path.length === 1),
];
}
class App extends React.Component {
constructor(...args) {
super(...args);
this.state = {
current: [],
};
}
componentDidMount() {
this.updateStateFromLocation();
window.addEventListener("hashchange", () => this.updateStateFromLocation(), false);
}
updateStateFromLocation() {
if (window.location.hash.length > 1) {
const current = window.location.hash.substr(1).split('/');
this.setState({current});
} else {
this.setState({current: []});
}
}
getCurrentPath() {
let file = this.props.root;
let path = [file];
for (let p of this.state.current) {
file = file.children.find(file => file.path[0] === p);
if (!file) {
return path;
}
path.push(file);
}
return path;
}
render() {
const path = this.getCurrentPath();
const file = path[path.length - 1];
let w = null;
if (file.is_folder) {
w = e(FilesList, {
folder: file,
onSelectFile: this.selectFile.bind(this),
onBack: path.length > 1 ? this.back.bind(this) : null,
});
} else {
w = e(DisplayFile, {
file,
onBack: this.back.bind(this),
});
}
return e('div', {className: 'app'}, w);
}
selectFile(file) {
this.setState(({current}) => {
return {current: [...current, file.path[0]]};
}, () => this.updateHash());
}
back(file) {
this.setState(({current}) => {
return {current: current.slice(0, current.length - 1)};
}, () => this.updateHash());
}
updateHash() {
if (!this.state.current || !this.state.current.length) {
window.location = '#';
} else {
window.location = '#' + this.state.current.join('/');
}
}
}
function FilesList({folder, onSelectFile, onBack}) {
let files = folder.children;
return e('div', {className: 'display-folder'},
e(FileHeader, {file: folder, onBack}),
e('table', {className: 'files-list'},
e('thead', {className: 'files-list__head'},
e('tr', null,
e('th', null, "Path"),
e('th', null, "Coverage")
)
),
e('tbody', {className: 'files-list__body'},
files.map(file => e(File, {file, onClick: onSelectFile}))
)
)
);
}
function File({file, onClick}) {
const coverage = file.coverable ? file.covered / file.coverable * 100 : -1;
const coverageDelta = file.prevRun &&
(file.covered / file.coverable * 100 - file.prevRun.covered / file.prevRun.coverable * 100);
return e('tr', {
className: 'files-list__file'
+ (coverage >= 0 && coverage < 50 ? ' files-list__file_low': '')
+ (coverage >= 50 && coverage < 80 ? ' files-list__file_medium': '')
+ (coverage >= 80 ? ' files-list__file_high': '')
+ (file.is_folder ? ' files-list__file_folder': ''),
onClick: () => onClick(file),
},
e('td', null, e('a', null, pathToString(file.path))),
e('td', null,
file.covered + ' / ' + file.coverable +
(coverage >= 0 ? ' (' + coverage.toFixed(2) + '%)' : ''),
e('span', {title: 'Change from the previous run'},
(coverageDelta ? ` (${coverageDelta > 0 ? '+' : ''}${coverageDelta.toFixed(2)}%)` : ''))
)
);
}
function DisplayFile({file, onBack}) {
return e('div', {className: 'display-file'},
e(FileHeader, {file, onBack}),
e(FileContent, {file})
);
}
function FileHeader({file, onBack}) {
const coverage = file.covered / file.coverable * 100;
const coverageDelta = file.prevRun && (coverage - file.prevRun.covered / file.prevRun.coverable * 100);
return e('div', {className: 'file-header'},
onBack ? e('a', {className: 'file-header__back', onClick: onBack}, 'Back') : null,
e('div', {className: 'file-header__name'}, pathToString([...file.parent, ...file.path])),
e('div', {className: 'file-header__stat'},
'Covered: ' + file.covered + ' of ' + file.coverable +
(file.coverable ? ' (' + coverage.toFixed(2) + '%)' : ''),
e('span', {title: 'Change from the previous run'},
(coverageDelta ? ` (${coverageDelta > 0 ? '+' : ''}${coverageDelta.toFixed(2)}%)` : ''))
)
);
}
function FileContent({file}) {
return e('pre', {className: 'file-content'},
file.content.split(/\r?\n/).map((line, index) => {
const trace = file.traces.find(trace => trace.line === index + 1);
const covered = trace && trace.stats.Line;
const uncovered = trace && !trace.stats.Line;
return e('code', {
className: 'code-line'
+ (covered ? ' code-line_covered' : '')
+ (uncovered ? ' code-line_uncovered' : ''),
title: trace ? JSON.stringify(trace.stats, null, 2) : null,
}, line);
})
);
}
(function(){
const commonPath = findCommonPath(data.files);
const prevFilesMap = new Map();
previousData && previousData.files.forEach((file) => {
const path = file.path.slice(commonPath.length).join('/');
prevFilesMap.set(path, file);
});
const files = data.files.map((file) => {
const path = file.path.slice(commonPath.length);
const { covered = 0, coverable = 0 } = prevFilesMap.get(path.join('/')) || {};
return {
...file,
path,
parent: commonPath,
prevRun: { covered, coverable },
};
});
const children = findFolders(files);
const root = {
is_folder: true,
children,
path: commonPath,
parent: [],
covered: children.reduce((sum, file) => sum + file.covered, 0),
coverable: children.reduce((sum, file) => sum + file.coverable, 0),
prevRun: {
covered: children.reduce((sum, file) => sum + file.prevRun.covered, 0),
coverable: children.reduce((sum, file) => sum + file.prevRun.coverable, 0),
}
};
ReactDOM.render(e(App, {root, prevFilesMap}), document.getElementById('root'));
}());
</script>
</body>
</html>