adds list of test roms and ability to load as desired passes the Corax+ opcode test now
671 lines
582 KiB
HTML
671 lines
582 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","chip8_toy","chip8_core","src","chip8_constants.rs"],"content":"\npub const CHIP8_MEMORY_SIZE: u16 = 0x1000;\npub const CHIP8_MEMORY_SIZE_USIZE: usize = 0x1000;\n\npub const FONT_0: [u8; 5] = [0xF0, 0x90, 0x90, 0x90, 0xF0];\npub const FONT_1: [u8; 5] = [0x20, 0x60, 0x20, 0x20, 0x70];\npub const FONT_2: [u8; 5] = [0xF0, 0x10, 0xF0, 0x80, 0xF0];\npub const FONT_3: [u8; 5] = [0xF0, 0x10, 0xF0, 0x10, 0xF0];\npub const FONT_4: [u8; 5] = [0x90, 0x90, 0xF0, 0x10, 0x10];\npub const FONT_5: [u8; 5] = [0xF0, 0x80, 0xF0, 0x10, 0xF0];\npub const FONT_6: [u8; 5] = [0xF0, 0x80, 0xF0, 0x90, 0xF0];\npub const FONT_7: [u8; 5] = [0xF0, 0x10, 0x20, 0x40, 0x40];\npub const FONT_8: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0xF0];\npub const FONT_9: [u8; 5] = [0xF0, 0x90, 0xF0, 0x10, 0xF0];\npub const FONT_A: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0x90];\npub const FONT_B: [u8; 5] = [0xE0, 0x90, 0xE0, 0x90, 0xE0];\npub const FONT_C: [u8; 5] = [0xF0, 0x80, 0x80, 0x80, 0xF0];\npub const FONT_D: [u8; 5] = [0xE0, 0x90, 0x90, 0x90, 0xE0];\npub const FONT_E: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0xF0];\npub const FONT_F: [u8; 5] = [0xF0, 0x80, 0xf0, 0x80, 0x80];\n\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_core","src","chip8_mnemonics.rs"],"content":"use bitmask::bitmask;\n\nuse crate::{chip8_constants::CHIP8_MEMORY_SIZE, parts::Display::Chip8Display};\n\nenum Chip8StartOffset {\n STANDARD,\n ETI600,\n OTHER,\n}\n\nstruct Chip8Registers {\n V: [u8; 16],\n I: u16,\n DelayTimer: u16,\n SoundTimer: u16,\n StackPointer: u8,\n ProgramCounter: u16,\n}\n\nimpl Default for Chip8Registers {\n fn default() -\u003e Self {\n Chip8Registers {\n DelayTimer: 60,\n V: [\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00,\n ],\n I: 0x00,\n SoundTimer: 60,\n StackPointer: 255,\n ProgramCounter: 0x200,\n }\n }\n}\n\nenum Chip8Instruction {\n SYS(u16),\n CLS,\n RET,\n JMP(u16),\n CALL(u16),\n SNEQ(u8, u16),\n SNNE(u8, u8),\n SNRE(u8, u8),\n STOR(u8, u16),\n ADD(u8, u16),\n MOV(u8, u8),\n OR(u8, u8),\n AND(u8, u8),\n XOR(u8, u8),\n ADC(u8, u8),\n SBC(u8, u8),\n RSR(u8, u8),\n LSR(u8, u8),\n SNE(u8, u8),\n STO(i16),\n JMPI(u16),\n RND(u8, u8),\n SETD(u8),\n SETT(u8),\n BCD(u8),\n}\n\nenum Chip8Keys {\n KEY0,\n KEY1,\n KEY2,\n KEY3,\n KEY4,\n KEY5,\n KEY6,\n KEY7,\n KEY8,\n KEY9,\n KEYA,\n KEYB,\n KEYC,\n KEYD,\n KEYE,\n KEYF,\n}\n\npub fn display_video_memory(system_memory: [u8; CHIP8_MEMORY_SIZE as usize]) {\n // Assumes memory addresses from\n}\n\nconst ZERO: u16 = 0b0000000000000000;\nconst BOTTOM_BYTE: u16 = 0b0000000011111111;\nconst TOP_BYTE: u16 = 0b1111111100000000;\nconst NIBBLE0: u16 = 0b1111000000000000;\nconst NIBBLE1: u16 = 0b0000111100000000;\nconst NIBBLE2: u16 = 0b0000000011110000;\nconst NIBBLE3: u16 = 0b0000000000001111;\nconst NIBBLE0_BALANCE: u16 = 0x0FFF;\nconst PMSK_0000: i16 = 0x0000;\nconst PMSK_00X0: i16 = 0x00F0;\nconst PMSK_0X00: i16 = 0x0F00;\nconst PMSK_0XXX: i16 = 0x0FFF;\n\n// AND against the possible operand to find which one we have\nbitmask! {\n mask Chip8Instructions: u16 where flags Chip8InstructionFlags {\n SYS = 0x00,\n CLR = 0b0000000011100000,\n RTS = 0b0000000011101110,\n JUMP = 0b0001000000000000,\n CALL = 0b0010000000000000,\n SKE = 0b0011000000000000,\n SKNE = 0b0100000000000000,\n SKRE = 0b0101000000000000,\n LOAD = 0b0110000000000000,\n ADD = 0b0111000000000000,\n MOVE = 0b1000000000000000,\n OR = 0b1000000000000001,\n AND = 0b1000000000000010,\n XOR = 0b1000000000000011,\n ADDR = 0b1000000000000100,\n SUB = 0b1000000000000101,\n SHR = 0b1000000000000110,\n SHL = 0b1000000000001110,\n SKRNE = 0b1001000000000000,\n LOADI = 0b1010000000000000,\n JUMPI = 0b1011000000000000,\n RAND = 0b1100000000000000,\n DRAW = 0b1101000000000000,\n SKPR = 0b1110000000001110,\n SKUP = 0b1111000010100001,\n MOVED = 0b1111000000000111, \n KEYD = 0b1111000000001010, \n LOADD = 0b1111000000010101, \n LOADS = 0b1111000000011000, \n ADDI = 0b1111000000011110, \n LDSPR = 0b1111000000101001, \n BCD = 0b1111000000110011, \n STOR = 0b1111000001010101, \n MEMCPY = 0b1111000001100101 \n }\n}\n\nenum Chip8Asm {\n SYS(u16),\n CLR(),\n RTS(),\n JUMP(u16),\n CALL(u16),\n SKE(u8, u8),\n SKNE(u8, u8),\n SKRE(u8, u8),\n LOAD(u8, u8),\n ADD(u8, u8),\n MOVE(u8, u8),\n OR(u8, u8),\n AND(u8, u8),\n XOR(u8, u8),\n ADDR(u8, u8),\n SUB(u8, u8),\n SHR(u8, u8),\n SHL(u8, u8),\n SKRNE(u8, u8),\n LOADI(u16),\n JUMPI(u16),\n RAND(u8, u8),\n DRAW(u8, u8, u8),\n SKPR(u8),\n SKUP(u8),\n MOVED(u8),\n KEYD(u8),\n LOADD(u8),\n LOADS(u8),\n ADDI(u8),\n LDSPR(u8),\n BCD(u8),\n STOR(u8),\n READ(u8),\n}\n\nstruct Chip8Cpu {}\n\nstruct Chip8System {\n registers: Chip8Registers,\n system_memory: [u8; 2048]\n}\n\nimpl Chip8System {\n pub fn tick(mut self) {\n println!(\" Ticking Chip8System\");\n\n let next_instruction = self.system_memory[self.registers.ProgramCounter as usize] as u16;\n println!(\"READ INSTRUCTION {next_instruction}\");\n self.registers.ProgramCounter += 0x2;\n\n \u0026self.delay_timer_tick();\n \u0026self.sound_timer_tick();\n\n // self.screen.tick();\n }\n\n fn delay_timer_tick(\u0026mut self) {\n self.registers.DelayTimer = self.registers.DelayTimer - 1;\n\n }\n\n fn sound_timer_tick(\u0026mut self) {\n self.registers.SoundTimer = self.registers.SoundTimer - 1;\n\n }\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_core","src","lib.rs"],"content":"pub mod chip8_mnemonics;\npub mod chip8_constants;\n\npub mod parts {\n pub mod CPU;\n pub mod Display;\n pub mod Keyboard;\n}","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_core","src","parts","CPU.rs"],"content":"struct Chip8Registers {\n\n}\n\n\nimpl Chip8Registers {\n pub fn tick(self: \u0026Self) {\n println!(\"Ticking Registers\");\n }\n}\n\npub struct Chip8InstructionParameter {\n pub mask: u16\n}\n\npub struct Chip8Instruction {\n pub id: String,\n pub mask: u16,\n pub pattern: u16,\n pub arguments: Vec\u003cChip8InstructionParameter\u003e,\n pub description: Box\u003cstr\u003e\n}\n\npub fn fill_chip8_instructions() -\u003e [Chip8Instruction; 34] {\n [\n Chip8Instruction {\n id: \"SYS\".to_string(),\n mask: 0x00,\n pattern: 0x00,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0FFF\n }\n ],\n description: \"Execute method at address referenced by parameter\".into()\n },\n Chip8Instruction {\n id: \"CLR\".to_string(),\n mask: 0x00E0,\n pattern: 0x00E0,\n arguments: vec![],\n description: \"Clear the Screen\".into()\n },\n Chip8Instruction {\n id: \"RET\".to_string(),\n mask: 0x00EE,\n pattern: 0x00EE,\n arguments: vec![],\n description: \"Return from Subroutine\".into()\n },\n Chip8Instruction {\n id: \"JMP\".to_string(),\n mask: 0x1FFF,\n pattern: 0x1000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0fff\n }\n ],\n description: \"Jump to specified location\".into()\n },\n Chip8Instruction {\n id: \"SEQ\".to_string(),\n mask: 0x3FFF,\n pattern: 0x3000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00ff\n }\n ],\n description: \"Skip next instruction if Register does equal parameter\".into()\n },\n Chip8Instruction {\n id: \"SNE\".to_string(),\n mask: 0x4FFF,\n pattern: 0x4000,\n arguments: vec! [\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00ff\n }\n ],\n description: \"Skip next instruction if Register doesn't equal parameter\".into()\n },\n Chip8Instruction {\n id: \"SREQ\".to_string(),\n mask: 0x5FF0,\n pattern: 0x5000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"TBD\".into()\n },\n Chip8Instruction {\n id: \"STO\".to_string(),\n mask: 0x6FFF,\n pattern: 0x6000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00ff\n }\n ],\n description: \"Store value into register\".into()\n },\n Chip8Instruction {\n id: \"ADD\".to_string(),\n mask: 0x7FFF,\n pattern: 0x7000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00ff\n }\n ],\n description: \"Add value in register 0x0f00 with value 0x00ff\".into()\n },\n Chip8Instruction {\n id: \"CPY\".to_string(),\n mask: 0x8FF0,\n pattern: 0x8000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Copy value between Register X and Y\".into()\n },\n Chip8Instruction {\n id: \"OR\".to_string(),\n mask: 0x8FF1,\n pattern: 0x8001,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Logical OR of registers placing result in first register\".into()\n },\n Chip8Instruction {\n id: \"AND\".to_string(),\n mask: 0x8FF2,\n pattern: 0x8002,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Logical AND of registers placing result in first register\".into()\n },\n Chip8Instruction {\n id: \"XOR\".to_string(),\n mask: 0x8FF3,\n pattern: 0x8003,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Logical XOR of registers placing result in first register\".into()\n },\n Chip8Instruction {\n id: \"ADC\".to_string(),\n mask: 0x8FF4,\n pattern: 0x8004,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Add 2 Registers with carry flag\".into()\n },\n Chip8Instruction {\n id: \"SUBC\".to_string(),\n mask: 0x8FF5,\n pattern: 0x8005,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Subtract 2 Registers with carry flag\".into()\n },\n Chip8Instruction {\n id: \"RSR\".to_string(),\n mask: 0x8FF6,\n pattern: 0x8006,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Register Shifted Right\".into()\n },\n Chip8Instruction {\n id: \"SUBC\".to_string(),\n mask: 0x8FF7,\n pattern: 0x8007,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Subtract 2 Regiters with Carry\".into()\n },\n Chip8Instruction {\n id: \"RSL\".to_string(),\n mask: 0x8FFE,\n pattern: 0x800E,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Register Shifted Left\".into()\n },\n Chip8Instruction {\n id: \"SRNE\".to_string(),\n mask: 0x9FF0,\n pattern: 0x9000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Skip next instruction if registers not equal\".into()\n },\n Chip8Instruction {\n id: \"LDI\".to_string(),\n mask: 0xAFFF,\n pattern: 0xA000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0fff\n }\n ],\n description: \"Load Data from Memory Address\".into()\n },\n Chip8Instruction {\n id: \"JMPI\".to_string(),\n mask: 0xBFFF,\n pattern: 0xB000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0fff\n }\n ],\n description: \"Jump to Memory Address\".into()\n },\n Chip8Instruction {\n id: \"RNG\".to_string(),\n mask: 0xCFFF,\n pattern: 0xC000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00ff\n }\n ],\n description: \"Random number Generator\".into()\n },\n Chip8Instruction {\n id: \"SPR\".to_string(),\n mask: 0xDFFF,\n pattern: 0xD000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n },\n Chip8InstructionParameter {\n mask: 0x000f\n }\n \n ],\n description: \"TBD\".into()\n },\n Chip8Instruction {\n id: \"JNK\".to_string(),\n mask: 0xEF9E,\n pattern: 0xE09E,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Jump over next instruction if Key Pressed\".into()\n },\n Chip8Instruction {\n id: \"JKP\".to_string(),\n mask: 0xEFA1,\n pattern: 0xE0A1,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Jump over next instruction if Key Not Pressed\".into()\n },\n Chip8Instruction {\n id: \"SDT\".to_string(),\n mask: 0xFF07,\n pattern: 0xF007,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0F00\n }\n ],\n description: \"TBD\".into()\n },\n Chip8Instruction {\n id: \"WKP\".to_string(),\n mask: 0xFF0A,\n pattern: 0xF00A,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Wait for a Key to be pressed\".into()\n },\n Chip8Instruction {\n id: \"LDT\".to_string(),\n mask: 0xFF15,\n pattern: 0xF015,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Load Data Timer\".into()\n },\n Chip8Instruction {\n id: \"LST\".to_string(),\n mask: 0xFF18,\n pattern: 0xF018,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Load Sound Timer\".into()\n },\n Chip8Instruction {\n id: \"ADDI\".to_string(),\n mask: 0xFF1E,\n pattern: 0xF01E,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Add register with value at I\".into()\n },\n Chip8Instruction {\n id: \"SETI\".to_string(),\n mask: 0xFF29,\n pattern: 0xF029,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"TBD\".into()\n },\n Chip8Instruction{\n id: \"BCD\".to_string(),\n mask: 0xFF33,\n pattern: 0xF033,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Binary Coded Decimal of register\".into()\n },\n Chip8Instruction{\n id: \"MSTO\".to_string(),\n mask: 0xFF55,\n pattern: 0xF055,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Store value in register X at offset I\".into()\n },\n Chip8Instruction {\n id: \"MLOAD\".to_string(),\n mask: 0xFF65,\n pattern: 0xF065,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Load value into register X at offset I\".into()\n }\n ]\n}\n\n\nstruct Chip8System {\n\n}\n\nimpl Chip8System {\n \n}\n\ntrait CpuInstruction {\n fn execute(input: Chip8System) -\u003e Chip8System;\n}","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_core","src","parts","Display.rs"],"content":"\npub struct Chip8Display {\n // 64w x 32h\n pub memory: [bool; 512],\n}\n\nimpl Chip8Display {\n pub fn tick(self: \u0026Self) {\n println!(\"Ticking the display\");\n }\n\n pub fn render_chip8_display(to_render: Chip8Display) {\n // 32 rows...\n for index_row in 0..=32 {\n // ...64 columns\n for index_col in 0..=64 {\n let offset = (index_row * 64) + index_col;\n if to_render.memory[offset] {\n print!(\"*\")\n } else {\n print!(\" \")\n };\n }\n println!();\n }\n }\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_core","src","parts","Keyboard.rs"],"content":"\n#[derive(Clone)]\nstruct Chip8Keyboard {\n keys_state: [bool; 16]\n}\n\nimpl Default for Chip8Keyboard {\n fn default() -\u003e Self {\n Self { keys_state: [ \n false, false, false, false,\n false, false, false, false,\n false, false, false, false,\n false, false, false, false\n ] }\n }\n}\n\nimpl Chip8Keyboard {\n\n fn display_keyboard_key(self: Self, key_id: u8) {\n let filler = if self.is_pressed(key_id) { \"*\" } else { \" \" };\n print!(\"{}{}{}\", filler, key_id, filler)\n }\n\n fn display_keyboard_horizontal_line(self: Self, row_values: [u8; 4]) {\n print!(\"|\");\n for current in row_values {\n self.clone().display_keyboard_key(current);\n print!(\"|\");\n }\n println!();\n } \n\n fn display_keyboard_seperator_line(self: Self) {\n println!(\"+---+---+---+---+\");\n\n }\n\n pub fn display_keyboard(self: Self) {\n self.clone().display_keyboard_seperator_line();\n self.clone().display_keyboard_horizontal_line(\n [1,2,3,0xC]\n );\n println!(\"+---+---+---+---+\");\n println!(\"| 4 | 5 | 6 | D |\");\n println!(\"+---+---+---+---+\");\n println!(\"| 7 | 8 | 9 | E |\");\n println!(\"+---+---+---+---+\");\n println!(\"| A | 0 | B | F |\");\n println!(\"+---+---+---+---+\");\n }\n \n pub fn press_key(self: \u0026mut Self, key_id: u8) {\n self.keys_state[key_id as usize] = true;\n }\n\n pub fn release_key(self: \u0026mut Self, key_id: u8) {\n self.keys_state[key_id as usize] = false;\n }\n\n pub fn is_pressed(self: Self, key_id: u8) -\u003e bool {\n self.keys_state[key_id as usize]\n }\n}\n\n\n#[cfg(test)]\nmod tests {\n use super::*;\n \n #[test]\n fn smoke_test() {\n assert_eq!(true, true);\n }\n \n #[test]\n fn basic_keyboard_display_works() {\n let basic_keyboard = Chip8Keyboard::default();\n\n \n }\n}","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_toy","src","app.rs"],"content":"/*\n\n#[derive(Default)]\npub struct App;\n\nimpl UiBuilder for App {\n fn do_ui(\u0026mut self, ui: \u0026Ui\u003cSelf\u003e) {\n #[cfg(feature = \"docking\")]\n {\n ui.dock_space_over_viewport(0, imgui::DockNodeFlags::None);\n }\n ui.show_demo_window(None);\n\n }\n}*/","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_toy","src","gui","display.rs"],"content":"use winit::{application::ApplicationHandler, event_loop::ActiveEventLoop, window::{Window, WindowAttributes}};\n\n/*\npub struct Chip8Display {\n pub window: MainWindowWithRenderer,\n}\n\nimpl Chip8Display {\n pub fn new(event_loop: \u0026ActiveEventLoop) -\u003e Chip8Display {\n Chip8Display { window: MainWindowWithRenderer::new(\n MainWindow::new::\u003c()\u003e(\u0026event_loop, Window::default_attributes().with_title(\"Chip8 Display\")).unwrap())\n }\n }\n\n pub fn render\u003cM\u003e(ui: \u0026Ui\u003cM\u003e) {\n \n }\n}\n\nimpl UiBuilder for Chip8AppHandler {\n fn do_ui(\u0026mut self, ui: \u0026Ui\u003cSelf\u003e) {\n\n ui.text(\"This is where the Chip8 UI Goes\");\n ui.text(\"Controls for Start/Stop/Load/Run Maybe?\");\n\n\n }\n}\n\n#[derive(Default)]\npub struct Chip8AppHandler {\n pub windows: Vec\u003cMainWindowWithRenderer\u003e,\n // pub display: Chip8Display\n}\n\nimpl ApplicationHandler for Chip8AppHandler {\n fn suspended(\u0026mut self, _event_loop: \u0026ActiveEventLoop) {\n self.windows.clear();\n }\n\n fn resumed(\u0026mut self, event_loop: \u0026ActiveEventLoop) {\n self.windows.push(Chip8Display::new(event_loop).window);\n }\n\n fn window_event(\n \u0026mut self,\n event_loop: \u0026ActiveEventLoop,\n window_id: winit::window::WindowId,\n event: winit::event::WindowEvent,\n ) {\n for window in \u0026mut self.windows {\n if window.main_window().window().id() != window_id {\n continue;\n }\n }\n }\n fn new_events(\u0026mut self, _event_loop: \u0026ActiveEventLoop, _cause: winit::event::StartCause) {\n for window in \u0026mut self.windows {\n window.new_events();\n }\n }\n fn about_to_wait(\u0026mut self, _event_loop: \u0026ActiveEventLoop) {\n for window in \u0026mut self.windows {\n window.about_to_wait();\n }\n }\n\n}\n*/","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_toy","src","lib.rs"],"content":"pub mod app;\n\npub mod gui {\n pub mod display;\n}\n\npub mod support;","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_toy","src","main.rs"],"content":"use trevors_chip8_core::parts::CPU::fill_chip8_instructions;\nuse trevors_chip8_toy::support;\nuse winit::{event::WindowEvent, event_loop::EventLoop};\n\nuse imgui::*;\n/*\npub fn display_instruction_table(current_index: u32, ui: \u0026Ui\u003cApp\u003e) -\u003e u32 {\n let to_display = fill_chip8_instructions();\n let mut return_index = current_index;\n let current_instruction = to_display.get(return_index as usize).unwrap();\n ui.text(\"Instructions\");\n ui.set_next_item_width(100f32);\n ui.with_group(|| {\n ui.text(format!(\"{}\", current_instruction.description).as_str());\n if current_instruction.arguments.is_empty() {\n ui.text(\"No Parameters\");\n } else {\n for current_parameter in current_instruction.arguments {\n ui.text(format!(\"{}\", current_parameter.mask).as_str());\n }\n }\n ui.text(format!(\"{:#}\", current_instruction.arguments).as_str());\n ui.text(\"This is where cool stuff happens part 2\");\n });\n ui.same_line();\n ui.with_group(|| {\n for (index, current) in to_display.iter().enumerate() {\n if ui\n .selectable_config(current.id.clone())\n .selected(index == return_index as usize)\n .build()\n {\n return_index = index as u32;\n }\n }\n });\n return_index\n}\n*/\n\nfn chip8_instructions(initial_index: u32, ui: \u0026mut Ui) -\u003e u32 {\n let to_display = fill_chip8_instructions();\n let mut return_index = initial_index;\n let instruction_to_display = to_display.get(return_index as usize).unwrap();\n let instruction_window = ui\n .window(\"Instructions\")\n .size([400.0, 400.0], Condition::FirstUseEver)\n .build(|| {\n ui.text(\"This should be my current instruction example\");\n ui.same_line();\n ui.set_next_item_width(-1.0f32);\n for (index, instruction) in to_display.iter().enumerate() {\n if ui\n .selectable_config(instruction.id.to_string())\n .selected(index as u32 == return_index)\n .build()\n {\n return_index = index as u32;\n println!(\"RETURN INDEX {return_index}\");\n }\n }\n });\n return_index\n}\n\nfn main() {\n let mut value = 0;\n let choices = [\"test test this is 1\", \"test test this is 2\"];\n\n let mut selected_instruction = 0;\n\n support::simple_init(file!(), move |_, ui| {\n selected_instruction = chip8_instructions(selected_instruction, ui);\n\n ui.window(\"Hello world\")\n .size([300.0, 110.0], Condition::FirstUseEver)\n .build(|| {\n ui.text_wrapped(\"Hello world!\");\n if ui.button(choices[value]) {\n value += 1;\n value %= 2;\n }\n\n ui.button(\"This...is...imgui-rs!\");\n ui.separator();\n let mouse_pos = ui.io().mouse_pos;\n ui.text(format!(\n \"Mouse Position: ({:.1},{:.1})\",\n mouse_pos[0], mouse_pos[1]\n ));\n });\n });\n}\n/*\nfn main() {\n let event_loop = EventLoop::new().unwrap();\n\n let mut main = AppHandler::\u003cApp\u003e::default();\n main.attributes().title = String::from(\"Example-420\");\n\n event_loop.run_app(\u0026mut main).unwrap();\n}\n\n#[derive(Default)]\npub struct App {\n pub selected_instruction_index: u32,\n}\n\nimpl App {}\n\nimpl Application for App {\n type UserEvent = ();\n type Data = ();\n fn new(_: Args\u003c()\u003e) -\u003e App {\n App {\n selected_instruction_index: 0,\n }\n }\n fn window_event(\u0026mut self, args: Args\u003c()\u003e, _event: WindowEvent, res: EventResult) {\n if res.window_closed {\n args.event_loop.exit();\n }\n }\n}\n\nimpl imgui::UiBuilder for App {\n fn do_ui(\u0026mut self, ui: \u0026imgui::Ui\u003cSelf\u003e) {\n #[cfg(feature = \"docking\")]\n {\n ui.dock_space_over_viewport(0, imgui::DockNodeFlags::None);\n }\n\n let x = ui.collapsing_header_config(\"collapsing header\");\n\n ui.with_group(|| {\n self.selected_instruction_index =\n display_instruction_table(self.selected_instruction_index, ui);\n });\n\n ui.show_demo_window(None);\n }\n}\n*/\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_toy","src","support","clipboard.rs"],"content":"use copypasta::{ClipboardContext, ClipboardProvider};\nuse imgui::ClipboardBackend;\n\npub struct ClipboardSupport(pub ClipboardContext);\n\npub fn 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","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_toy","src","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::dpi::LogicalSize;\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\nmod clipboard;\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_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":18,"address":[],"length":0,"stats":{"Line":0}},{"line":19,"address":[],"length":0,"stats":{"Line":0}},{"line":27,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[],"length":0,"stats":{"Line":0}},{"line":30,"address":[],"length":0,"stats":{"Line":0}},{"line":31,"address":[],"length":0,"stats":{"Line":0}},{"line":33,"address":[],"length":0,"stats":{"Line":0}},{"line":35,"address":[],"length":0,"stats":{"Line":0}},{"line":36,"address":[],"length":0,"stats":{"Line":0}},{"line":37,"address":[],"length":0,"stats":{"Line":0}},{"line":38,"address":[],"length":0,"stats":{"Line":0}},{"line":39,"address":[],"length":0,"stats":{"Line":0}},{"line":40,"address":[],"length":0,"stats":{"Line":0}},{"line":41,"address":[],"length":0,"stats":{"Line":0}},{"line":43,"address":[],"length":0,"stats":{"Line":0}},{"line":44,"address":[],"length":0,"stats":{"Line":0}},{"line":46,"address":[],"length":0,"stats":{"Line":0}},{"line":49,"address":[],"length":0,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":53,"address":[],"length":0,"stats":{"Line":0}},{"line":54,"address":[],"length":0,"stats":{"Line":0}},{"line":55,"address":[],"length":0,"stats":{"Line":0}},{"line":58,"address":[],"length":0,"stats":{"Line":0}},{"line":61,"address":[],"length":0,"stats":{"Line":0}},{"line":64,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":0}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":69,"address":[],"length":0,"stats":{"Line":0}},{"line":70,"address":[],"length":0,"stats":{"Line":0}},{"line":71,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":0}},{"line":73,"address":[],"length":0,"stats":{"Line":0}},{"line":75,"address":[],"length":0,"stats":{"Line":0}},{"line":76,"address":[],"length":0,"stats":{"Line":0}},{"line":77,"address":[],"length":0,"stats":{"Line":0}},{"line":78,"address":[],"length":0,"stats":{"Line":0}},{"line":79,"address":[],"length":0,"stats":{"Line":0}},{"line":81,"address":[],"length":0,"stats":{"Line":0}},{"line":82,"address":[],"length":0,"stats":{"Line":0}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":84,"address":[],"length":0,"stats":{"Line":0}},{"line":85,"address":[],"length":0,"stats":{"Line":0}},{"line":87,"address":[],"length":0,"stats":{"Line":0}},{"line":88,"address":[],"length":0,"stats":{"Line":0}},{"line":89,"address":[],"length":0,"stats":{"Line":0}},{"line":90,"address":[],"length":0,"stats":{"Line":0}},{"line":93,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":0}},{"line":95,"address":[],"length":0,"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":107,"address":[],"length":0,"stats":{"Line":0}},{"line":109,"address":[],"length":0,"stats":{"Line":0}},{"line":111,"address":[],"length":0,"stats":{"Line":0}},{"line":112,"address":[],"length":0,"stats":{"Line":0}},{"line":113,"address":[],"length":0,"stats":{"Line":0}},{"line":114,"address":[],"length":0,"stats":{"Line":0}},{"line":115,"address":[],"length":0,"stats":{"Line":0}},{"line":116,"address":[],"length":0,"stats":{"Line":0}}],"covered":0,"coverable":67},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","bin","emma.rs"],"content":"use std::io::{stdout, Result};\n/*\nuse emmaemu::{chip8::{computer::Chip8Computer, video::Chip8Video}, constants::{CHIP8_MEMORY_SIZE, CHIP8_REGISTER_COUNT, CHIP8_ROM_SIZE, CHIP8_VIDEO_MEMORY}};\nuse ratatui::{\n backend::CrosstermBackend,\n crossterm::{\n event::{self, KeyCode, KeyEventKind},\n terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},\n ExecutableCommand,\n },\n layout::{Alignment, Rect},\n style::{Style, Stylize},\n widgets::{List, Paragraph, Widget},\n Frame, Terminal,\n};\n\n\nfn system_memory_to_text_render(data_to_dump: [u8; 2048]) -\u003e String {\n let mut to_return = String::new();\n\n for i in 0..256 {\n to_return += \u0026format!(\"{:x}\\t\", data_to_dump[i as usize]).to_string();\n\n if ((i + 1) % CHIP8_REGISTER_COUNT) == 0 {\n to_return += \"\\n\";\n }\n }\n\n to_return\n}\n\nfn dump_memory_to_console(data_to_dump: [u8; 2048]) {\n println!(\"STARTING TO DUMP MEMORY TO CONSOLE\");\n println!(\"{}\", system_memory_to_text_render(data_to_dump));\n println!(\"DONE DUMPING!\");\n panic!(\"DONE DUMPING\");\n}\n\nstruct ControlKeyboard {\n\n}\n\nimpl Widget for ControlKeyboard {\n fn render(self, area: Rect, buf: \u0026mut ratatui::prelude::Buffer)\n where\n Self: Sized {\n let style = Style::new();\n buf.set_string(0, 0, \"F1 to cycle foreground - F2 to cycle background\", style)\n }\n}\n\n\n#[derive(Clone)]\nstruct AppState {\n pub menu_items: Vec\u003cString\u003e,\n pub selected_menu_item: i32,\n pub system: Chip8Computer,\n}\n\nimpl Default for AppState {\n fn default() -\u003e Self {\n println!(\"Creating new AppState\");\n Self {\n menu_items: vec![\n \"Step CPU \u003cF5\u003e\".into(),\n \"Reset CPU \u003cF12\u003e\".into(),\n \"Item 3\".into(),\n ],\n selected_menu_item: 0,\n system: Chip8Computer {\n ..Default::default()\n },\n }\n }\n}\n\nfn main() -\u003e Result\u003c()\u003e {\n stdout().execute(EnterAlternateScreen)?;\n enable_raw_mode()?;\n let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;\n terminal.clear()?;\n\n let app = AppState::default();\n\n loop {\n // Draw Ui...\n terminal.draw(|frame| {\n frame.render_widget(app.system.memory, frame.area());\n // frame.render_widget(app.control_keyboard, area);\n //render_cpu_state(app.system.clone(), frame);\n // render_video_state(app.system.video_memory, frame);\n // render_menu_list(app.clone(), frame);\n })?;\n // ...handle Events.\n if event::poll(std::time::Duration::from_millis(16))? {\n if let event::Event::Key(key) = event::read()? {\n match key.kind {\n KeyEventKind::Press =\u003e match key.code {\n KeyCode::F(5) =\u003e {\n println!(\"Execute Next Instruction\");\n }\n KeyCode::F(12) =\u003e {\n println!(\"Resetting CPU\");\n }\n KeyCode::Char('q') =\u003e { break; }\n _ =\u003e (),\n },\n _ =\u003e (),\n }\n }\n }\n }\n\n // stdout().execute(LeaveAlternateScreen)?;\n // disable_raw_mode()?;\n Ok(())\n}\n\n#[cfg(test)]\nmod test {\n use emmaemu::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};\n\n use crate::*;\n #[test]\n fn blank_screen_renders_to_text() {\n let test_video = Chip8Video::default();\n\n\n let blank_data: [bool; CHIP8_VIDEO_MEMORY] = [false; CHIP8_VIDEO_MEMORY];\n let blank_screen = (\" \".repeat(CHIP8_VIDEO_WIDTH.try_into().unwrap()) + \"\\n\")\n .repeat(CHIP8_VIDEO_HEIGHT.try_into().unwrap());\n \n assert_eq!(Chip8Video::new(blank_data).format_as_string(), blank_screen);\n }\n\n #[test]\n fn filled_screen_renders_to_text() {\n let filled_data: [bool; CHIP8_VIDEO_MEMORY] = [true; CHIP8_VIDEO_MEMORY];\n let filled_screen = (\"*\".repeat(CHIP8_VIDEO_WIDTH.try_into().unwrap()) + \"\\n\")\n .repeat(CHIP8_VIDEO_HEIGHT.try_into().unwrap());\n assert_eq!(Chip8Video::new(filled_data).format_as_string(), filled_screen);\n }\n\n #[test]\n fn grid_pattern_renders_to_text() {\n let mut grid_data: [bool; CHIP8_VIDEO_MEMORY] = [false; CHIP8_VIDEO_MEMORY];\n let mut expected_data = String::new();\n for i in 0..CHIP8_VIDEO_MEMORY {\n grid_data[i] = (i % 2 == 0);\n if i % 2 == 0 {\n grid_data[i] = true;\n expected_data += \"*\";\n } else {\n grid_data[i] = false;\n expected_data += \" \";\n }\n if (i as i32 % CHIP8_VIDEO_WIDTH as i32 == CHIP8_VIDEO_WIDTH - 1) {\n expected_data += \"\\n\";\n }\n }\n\n assert_eq!(Chip8Video::new(grid_data).format_as_string(), expected_data);\n }\n}\n*/\nfn main() {\n println!(\"Taxation is theft\");\n}","traces":[{"line":166,"address":[119248],"length":1,"stats":{"Line":0}},{"line":167,"address":[119252],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":2},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","bin","emmagui.rs"],"content":"use std::default::Default;\nuse std::fs::DirEntry;\nuse std::time::Instant;\nuse emmaemu::{\n chip8::computer::Chip8Computer,\n constants::{CHIP8_MEMORY_SIZE, CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH},\n};\nuse imgui::*;\nuse ratatui::symbols::half_block;\nuse sys::{ImColor, ImVec2, ImVector_ImU32};\nuse rand::random;\nuse emmaemu::chip8::system_memory::Chip8SystemMemory;\nuse support::emmagui_support::EmmaGui;\nmod support;\n\n\nstruct UiState {\n pub show_registers: bool,\n pub show_memory: bool,\n pub show_video: 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: f32\n}\n\nimpl Clone for UiState {\n fn clone(\u0026self) -\u003e Self {\n UiState {\n show_registers: self.show_registers,\n show_memory: self.show_memory,\n show_video: self.show_video,\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 }\n }\n}\n\nimpl Default for UiState {\n fn default() -\u003e Self {\n UiState {\n show_registers: false,\n show_memory: false,\n show_video: 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: 32.0\n }\n }\n}\n\nfn main() {\n pretty_env_logger::init();\n let mut system = Chip8Computer::default();\n let mut ui_state = UiState::default();\n let mut last_tick_time = Instant::now();\n\n support::simple_init(file!(), move |_, ui| {\n let current_time = Instant::now();\n let time_since_last_tick = current_time.duration_since(last_tick_time).as_millis();\n if ui_state.is_running \u0026\u0026 time_since_last_tick \u003e ui_state.frame_time as u128 {\n system.step_system();\n last_tick_time = current_time;\n }\n\n EmmaGui::system_controls(\u0026mut system, \u0026mut ui_state, ui);\n\n if ui_state.show_registers {\n EmmaGui::registers_view(\u0026system, ui);\n }\n\n if ui_state.show_video {\n EmmaGui::video_display(\u0026system, \u0026ui_state, ui);\n }\n\n if ui_state.show_memory {\n let active_instruction = system.registers.peek_pc();\n EmmaGui::hex_memory_display(system.memory.clone(), (0x100, 0x10), active_instruction as i16, ui);\n }\n });\n}\n","traces":[{"line":29,"address":[4555552],"length":1,"stats":{"Line":0}},{"line":31,"address":[4555576],"length":1,"stats":{"Line":0}},{"line":32,"address":[4555583],"length":1,"stats":{"Line":0}},{"line":33,"address":[4555590],"length":1,"stats":{"Line":0}},{"line":34,"address":[4555597],"length":1,"stats":{"Line":0}},{"line":35,"address":[4555637],"length":1,"stats":{"Line":0}},{"line":36,"address":[4555641],"length":1,"stats":{"Line":0}},{"line":37,"address":[4555645],"length":1,"stats":{"Line":0}},{"line":38,"address":[4555649],"length":1,"stats":{"Line":0}},{"line":44,"address":[4555911,4555728],"length":1,"stats":{"Line":0}},{"line":49,"address":[4555747],"length":1,"stats":{"Line":0}},{"line":50,"address":[4555759],"length":1,"stats":{"Line":0}},{"line":51,"address":[4555814],"length":1,"stats":{"Line":0}},{"line":58,"address":[4556356,4555936],"length":1,"stats":{"Line":0}},{"line":59,"address":[4555988],"length":1,"stats":{"Line":0}},{"line":60,"address":[4556013],"length":1,"stats":{"Line":0}},{"line":61,"address":[4556043],"length":1,"stats":{"Line":0}},{"line":62,"address":[4556166,4556091],"length":1,"stats":{"Line":0}},{"line":64,"address":[4556181],"length":1,"stats":{"Line":0}},{"line":65,"address":[4817912],"length":1,"stats":{"Line":0}},{"line":66,"address":[4817935],"length":1,"stats":{"Line":0}},{"line":67,"address":[4818018,4818218,4818071],"length":1,"stats":{"Line":0}},{"line":68,"address":[4818181],"length":1,"stats":{"Line":0}},{"line":69,"address":[4818196],"length":1,"stats":{"Line":0}},{"line":72,"address":[4818034],"length":1,"stats":{"Line":0}},{"line":74,"address":[4818051],"length":1,"stats":{"Line":0}},{"line":75,"address":[4818246],"length":1,"stats":{"Line":0}},{"line":78,"address":[4818228],"length":1,"stats":{"Line":0}},{"line":79,"address":[4818280],"length":1,"stats":{"Line":0}},{"line":82,"address":[4818262],"length":1,"stats":{"Line":0}},{"line":83,"address":[4818307],"length":1,"stats":{"Line":0}},{"line":84,"address":[4818342],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":32},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","bin","font","text_rendering.rs"],"content":"use ratatui::{style::Stylize, widgets::{List, Paragraph}, Frame};\n\nuse crate::{AppState, Chip8Computer, CHIP8_REGISTER_COUNT, CHIP8_VIDEO_MEMORY};\n\n\npub fn render_cpu_state(cpu_state: Chip8Computer, frame: \u0026mut Frame) {\n let mut area = frame.area();\n\n let mut current_state: Vec\u003cString\u003e = vec![];\n\n current_state.push(format!(\"Delay Timer {:X}\", cpu_state.delay_timer));\n current_state.push(format!(\"Sound Timer {:X}\", cpu_state.sound_timer));\n current_state.push(format!(\"PC {:X}\", cpu_state.pc));\n for i in 0 .. CHIP8_REGISTER_COUNT {\n current_state.push(format!(\"V{}: {}\", i, cpu_state.registers[i as usize]));\n }\n\n frame.render_widget(\n List::new(current_state).blue().on_white(), area)\n}\n\npub fn render_menu_list(state: AppState, frame: \u0026mut Frame) {\n let mut area = frame.area();\n\n frame.render_widget(List::new(state.menu_items).blue().on_white(), area);\n}\n\npub fn render_hello_world(frame: \u0026mut Frame) {\n let area = frame.area();\n frame.render_widget(\n Paragraph::new(\"Hello Ratatui! (press 'q' to quit)\")\n .white()\n .on_blue(),\n area,\n );\n}\n\npub fn render_video_state(video_memory: [bool; CHIP8_VIDEO_MEMORY], frame: \u0026mut Frame) {\n \n}","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","bin","support","clipboard.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","bin","support","emmagui_support.rs"],"content":"use std::fs::File;\nuse std::io::Read;\nuse std::path::{Path, PathBuf};\nuse std::thread::sleep;\nuse std::time::Duration;\nuse imgui::{Condition, ImColor32, Ui};\nuse log::debug;\nuse emmaemu::chip8::computer::Chip8Computer;\nuse emmaemu::chip8::system_memory::Chip8SystemMemory;\nuse emmaemu::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};\nuse crate::UiState;\n\n\npub struct EmmaGui {}\n\nconst CELL_WIDTH: i32 = 5i32;\nconst CELL_HEIGHT: i32 = 5i32;\n\nstruct 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 for (index, entry) in std::fs::read_dir(root.as_path()).unwrap().enumerate() {\n let filename = entry.unwrap().file_name();\n let filename = filename.to_str().unwrap();\n let mut working_select = ui.selectable_config(format!(\"{}\", filename));\n if filename == selected_filename {\n working_select = working_select.selected(true);\n }\n if working_select.build() {\n debug!(\"SELECTED {index} / {filename}\");\n working_filename = filename.to_string();\n };\n }\n working_filename\n }\n}\n\n\nimpl EmmaGui {\n pub fn video_display(system_to_control: \u0026Chip8Computer, gui_state: \u0026UiState, ui: \u0026Ui) {\n // draw area size\n let draw_area_size = ui.io().display_size;\n let cell_width = ((draw_area_size[0] as i32 / 64) * 6) / 10;\n let cell_height = ((draw_area_size[1] as i32 / 32) * 6) / 10;\n\n ui.window(format!(\"Display {cell_width}x{cell_height}\"))\n .size([300.0, 300.0], Condition::FirstUseEver)\n .build(|| {\n let origin = ui.cursor_screen_pos();\n let fg = ui.get_window_draw_list();\n ui.text(\"This is the video display.\");\n for current_row in 0..=31 {\n for current_column in 0..=63 {\n let x_offset = origin[0] as i32 + (current_column * cell_width);\n let y_offset = origin[1] as i32 + (current_row * cell_height);\n let current_origin = [x_offset as f32, y_offset as f32];\n let current_limit = [(x_offset + cell_width) as f32, (y_offset + cell_height) as f32];\n let memory_offset = (current_row * 64 + 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(current_origin, current_limit, color, color, color, color);\n }\n }\n });\n }\n pub fn system_controls(system_to_control: \u0026mut Chip8Computer, gui_state: \u0026mut UiState, ui: \u0026Ui) {\n ui.window(\"!!!! CONTROLS !!!!\")\n .size([345.0, 200.0], Condition::FirstUseEver)\n .build(|| {\n /* ROM Lister */\n let new_filename = GuiFileList::display_path(PathBuf::from(\"resources/roms\"), \u0026gui_state.filename_to_load, ui);\n if !new_filename.is_empty() {\n if new_filename != gui_state.filename_to_load {\n println!(\"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::new();\n\n println!(\"PREPARING TO LOAD {}\", gui_state.filename_to_load);\n // let mut input_file = File::open(Path::new(\"./1-chip8-logo.ch8\")).expect(\"put 1-chip8-logo.ch8 in this directory\");\n let mut input_file = File::open(Path::new(\u0026(\"resources/roms/\".to_string() + \u0026gui_state.filename_to_load))).expect(\"put 1-chip8-logo.ch8 in this directory\");\n input_file.read_to_end(\u0026mut buffer).expect(\"unable to read file\");\n system_to_control.load_bytes_to_memory(0x200, buffer.into());\n }\n }\n ui.separator();\n if ui.button(\"Step\") {\n system_to_control.step_system();\n };\n if ui.button(\"Run\") {\n gui_state.is_running = true;\n println!(\"STARTING THE SYSTEM\");\n }\n if ui.button(\"Stop\") {\n gui_state.is_running = false;\n println!(\"STOPPING THE SYSTEM\");\n }\n ui.same_line();\n if ui.button(\"Reset\") {\n *system_to_control = Chip8Computer::new();\n }\n if ui.button(\"Dump Video Memory\") {\n println!(\"{}\", system_to_control.dump_video_to_string());\n }\n ui.same_line();\n if ui.button(\"Dump Keypad State\") {\n println!(\"{}\", system_to_control.dump_keypad_to_string());\n }\n ui.same_line();\n if ui.button(\"Dump Registers\") {\n println!(\"{}\", system_to_control.dump_registers_to_string());\n }\n ui.separator();\n\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 });\n }\n\n pub fn registers_view(system: \u0026Chip8Computer, ui: \u0026Ui) {\n ui.window(\"Registers\")\n .size([400.0, 500.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(bytes: Chip8SystemMemory, position: (i32, i32), active: i16, ui: \u0026Ui) {\n let rows = position.0;\n let cols = position.1;\n ui.window(\"System Memory\")\n .size([400.0, 300.0], Condition::FirstUseEver)\n .position([500.0, 300.0], Condition::Always)\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(text_location, [bounding_box.x, bounding_box.y]);\n let is_active = data_offset == active as i32;\n\n ui.text_colored(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 }, 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 println!(\"Offset: [{}] [0x{:02x}] Value: [{}]\", data_offset, data_offset, formatted_text.clone());\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!(\"Offset 0x{:03x}\", current_x_hover * cols + current_y_hover));\n });\n }\n\n pub fn system_memory_render(memory: Chip8SystemMemory, ui: \u0026Ui) {\n ui.window(\"System Memory\")\n .size([300.0, 100.0], Condition::FirstUseEver)\n .build(|| {\n ui.text(\"Rendering System Memory Here\");\n let draw_list = ui.get_foreground_draw_list();\n let mut idx = 0;\n for row in 0..CHIP8_VIDEO_HEIGHT {\n for column in 0..CHIP8_VIDEO_WIDTH {\n let x_offset = column * CELL_WIDTH;\n let y_offset = row * CELL_WIDTH;\n let start_point = [x_offset as f32, y_offset as f32];\n let end_point = [(x_offset + CELL_WIDTH) as f32,\n (y_offset + CELL_HEIGHT) as f32\n ];\n let memory_offset = (row * CHIP8_VIDEO_WIDTH) + column;\n let target_color = if memory.peek(memory_offset as u16) == 0 {\n ImColor32::BLACK\n } else {\n ImColor32::WHITE\n };\n draw_list.add_rect([x_offset as f32, y_offset as f32],\n [(x_offset + CELL_WIDTH) as f32, (y_offset + CELL_HEIGHT) as f32],\n target_color).build();\n idx += 1;\n }\n }\n });\n }\n}","traces":[{"line":22,"address":[4559392,4562465],"length":1,"stats":{"Line":0}},{"line":23,"address":[4559447],"length":1,"stats":{"Line":0}},{"line":24,"address":[4559866,4559646,4559534,4560003],"length":1,"stats":{"Line":0}},{"line":25,"address":[4560285,4560489,4560460,4560018],"length":1,"stats":{"Line":0}},{"line":26,"address":[4560813],"length":1,"stats":{"Line":0}},{"line":27,"address":[4561110,4560947],"length":1,"stats":{"Line":0}},{"line":28,"address":[4561381,4561236],"length":1,"stats":{"Line":0}},{"line":29,"address":[4561472,4561396,4561671],"length":1,"stats":{"Line":0}},{"line":30,"address":[4561553],"length":1,"stats":{"Line":0}},{"line":32,"address":[4562442,4561478,4561680],"length":1,"stats":{"Line":0}},{"line":33,"address":[4562113,4561844,4561716],"length":1,"stats":{"Line":0}},{"line":34,"address":[4561768,4562300],"length":1,"stats":{"Line":0}},{"line":37,"address":[4560756],"length":1,"stats":{"Line":0}},{"line":43,"address":[4562496],"length":1,"stats":{"Line":0}},{"line":45,"address":[4562555],"length":1,"stats":{"Line":0}},{"line":46,"address":[4562570,4562781],"length":1,"stats":{"Line":0}},{"line":47,"address":[4562801,4563473,4562724],"length":1,"stats":{"Line":0}},{"line":49,"address":[4563332,4563102,4563447,4563269],"length":1,"stats":{"Line":0}},{"line":50,"address":[4563290],"length":1,"stats":{"Line":0}},{"line":51,"address":[4052367,4051120],"length":1,"stats":{"Line":0}},{"line":52,"address":[4051132],"length":1,"stats":{"Line":0}},{"line":53,"address":[4051170],"length":1,"stats":{"Line":0}},{"line":54,"address":[4051196],"length":1,"stats":{"Line":0}},{"line":55,"address":[4051404,4051255],"length":1,"stats":{"Line":0}},{"line":56,"address":[4051422],"length":1,"stats":{"Line":0}},{"line":57,"address":[4051701,4051590],"length":1,"stats":{"Line":0}},{"line":58,"address":[4051728,4051890,4051699],"length":1,"stats":{"Line":0}},{"line":59,"address":[4051847],"length":1,"stats":{"Line":0}},{"line":60,"address":[4051924,4051873,4052027],"length":1,"stats":{"Line":0}},{"line":61,"address":[4052008,4052169,4052056],"length":1,"stats":{"Line":0}},{"line":62,"address":[4052107,4052194],"length":1,"stats":{"Line":0}},{"line":63,"address":[4052206,4052229],"length":1,"stats":{"Line":0}},{"line":64,"address":[4052236],"length":1,"stats":{"Line":0}},{"line":66,"address":[4052215],"length":1,"stats":{"Line":0}},{"line":68,"address":[4052250],"length":1,"stats":{"Line":0}},{"line":73,"address":[4563504],"length":1,"stats":{"Line":0}},{"line":74,"address":[4563628,4563561,4563777],"length":1,"stats":{"Line":0}},{"line":75,"address":[4563586],"length":1,"stats":{"Line":0}},{"line":76,"address":[4052400,4053887,4055590],"length":1,"stats":{"Line":0}},{"line":78,"address":[4052427],"length":1,"stats":{"Line":0}},{"line":79,"address":[4052590,4052534],"length":1,"stats":{"Line":0}},{"line":80,"address":[4052655,4053050,4052601],"length":1,"stats":{"Line":0}},{"line":81,"address":[4052806],"length":1,"stats":{"Line":0}},{"line":82,"address":[4052880],"length":1,"stats":{"Line":0}},{"line":84,"address":[4053059,4053882,4052666],"length":1,"stats":{"Line":0}},{"line":85,"address":[4053068],"length":1,"stats":{"Line":0}},{"line":87,"address":[4053092,4053233],"length":1,"stats":{"Line":0}},{"line":89,"address":[4053302,4053896],"length":1,"stats":{"Line":0}},{"line":90,"address":[4053671],"length":1,"stats":{"Line":0}},{"line":91,"address":[4053753],"length":1,"stats":{"Line":0}},{"line":94,"address":[4052633],"length":1,"stats":{"Line":0}},{"line":95,"address":[4053945],"length":1,"stats":{"Line":0}},{"line":96,"address":[4054019],"length":1,"stats":{"Line":0}},{"line":98,"address":[4053987,4054040],"length":1,"stats":{"Line":0}},{"line":99,"address":[4054083],"length":1,"stats":{"Line":0}},{"line":100,"address":[4054090],"length":1,"stats":{"Line":0}},{"line":102,"address":[4054051,4054141],"length":1,"stats":{"Line":0}},{"line":103,"address":[4054172],"length":1,"stats":{"Line":0}},{"line":104,"address":[4054179],"length":1,"stats":{"Line":0}},{"line":106,"address":[4054152],"length":1,"stats":{"Line":0}},{"line":107,"address":[4054420,4054231],"length":1,"stats":{"Line":0}},{"line":108,"address":[4054300],"length":1,"stats":{"Line":0}},{"line":110,"address":[4054429,4054273],"length":1,"stats":{"Line":0}},{"line":111,"address":[4054598,4054463],"length":1,"stats":{"Line":0}},{"line":113,"address":[4054440],"length":1,"stats":{"Line":0}},{"line":114,"address":[4054738],"length":1,"stats":{"Line":0}},{"line":115,"address":[4054803],"length":1,"stats":{"Line":0}},{"line":117,"address":[4054780],"length":1,"stats":{"Line":0}},{"line":118,"address":[4055078],"length":1,"stats":{"Line":0}},{"line":119,"address":[4055143,4055278],"length":1,"stats":{"Line":0}},{"line":121,"address":[4055120],"length":1,"stats":{"Line":0}},{"line":123,"address":[4055418],"length":1,"stats":{"Line":0}},{"line":124,"address":[4055450],"length":1,"stats":{"Line":0}},{"line":125,"address":[4055470],"length":1,"stats":{"Line":0}},{"line":126,"address":[4055502],"length":1,"stats":{"Line":0}},{"line":127,"address":[4055522],"length":1,"stats":{"Line":0}},{"line":131,"address":[4563808],"length":1,"stats":{"Line":0}},{"line":132,"address":[4563904,4563840],"length":1,"stats":{"Line":0}},{"line":133,"address":[4563862],"length":1,"stats":{"Line":0}},{"line":134,"address":[4055632],"length":1,"stats":{"Line":0}},{"line":135,"address":[4055649],"length":1,"stats":{"Line":0}},{"line":136,"address":[4055671,4058655],"length":1,"stats":{"Line":0}},{"line":137,"address":[4058914,4059060,4058663,4058774],"length":1,"stats":{"Line":0}},{"line":138,"address":[4059070],"length":1,"stats":{"Line":0}},{"line":139,"address":[4059081],"length":1,"stats":{"Line":0}},{"line":142,"address":[4055735],"length":1,"stats":{"Line":0}},{"line":143,"address":[4056164,4055900,4055754,4056317],"length":1,"stats":{"Line":0}},{"line":144,"address":[4056330],"length":1,"stats":{"Line":0}},{"line":145,"address":[4056750,4056903,4056341,4056486],"length":1,"stats":{"Line":0}},{"line":146,"address":[4056916],"length":1,"stats":{"Line":0}},{"line":147,"address":[4057072,4056927,4057336,4057489],"length":1,"stats":{"Line":0}},{"line":148,"address":[4057912,4057502,4058065,4057648],"length":1,"stats":{"Line":0}},{"line":149,"address":[4058217,4058481,4058634,4058078],"length":1,"stats":{"Line":0}},{"line":153,"address":[4563984],"length":1,"stats":{"Line":0}},{"line":154,"address":[4564033],"length":1,"stats":{"Line":0}},{"line":155,"address":[4564037],"length":1,"stats":{"Line":0}},{"line":156,"address":[4564317,4564041,4564203,4564108],"length":1,"stats":{"Line":0}},{"line":157,"address":[4564066],"length":1,"stats":{"Line":0}},{"line":158,"address":[4564161],"length":1,"stats":{"Line":0}},{"line":159,"address":[4564262],"length":1,"stats":{"Line":0}},{"line":160,"address":[4059134],"length":1,"stats":{"Line":0}},{"line":161,"address":[4059145],"length":1,"stats":{"Line":0}},{"line":163,"address":[4059156,4059280],"length":1,"stats":{"Line":0}},{"line":164,"address":[4060499,4060568,4060346,4060082,4059965,4059298],"length":1,"stats":{"Line":0}},{"line":165,"address":[4060520],"length":1,"stats":{"Line":0}},{"line":166,"address":[4060538,4060584],"length":1,"stats":{"Line":0}},{"line":167,"address":[4061315,4060659],"length":1,"stats":{"Line":0}},{"line":168,"address":[4061113,4060865,4060735],"length":1,"stats":{"Line":0}},{"line":169,"address":[4061295,4061388],"length":1,"stats":{"Line":0}},{"line":170,"address":[4061412],"length":1,"stats":{"Line":0}},{"line":171,"address":[4061586],"length":1,"stats":{"Line":0}},{"line":172,"address":[4061502],"length":1,"stats":{"Line":0}},{"line":173,"address":[4061538],"length":1,"stats":{"Line":0}},{"line":176,"address":[4061604],"length":1,"stats":{"Line":0}},{"line":177,"address":[4061725],"length":1,"stats":{"Line":0}},{"line":179,"address":[4062027,4061846,4061756],"length":1,"stats":{"Line":0}},{"line":180,"address":[4061783],"length":1,"stats":{"Line":0}},{"line":181,"address":[4061773,4061914],"length":1,"stats":{"Line":0}},{"line":182,"address":[4061916],"length":1,"stats":{"Line":0}},{"line":184,"address":[4061851],"length":1,"stats":{"Line":0}},{"line":185,"address":[4061979],"length":1,"stats":{"Line":0}},{"line":189,"address":[4062038],"length":1,"stats":{"Line":0}},{"line":191,"address":[4062094],"length":1,"stats":{"Line":0}},{"line":192,"address":[4062101],"length":1,"stats":{"Line":0}},{"line":195,"address":[4062108],"length":1,"stats":{"Line":0}},{"line":196,"address":[4062512,4063104,4062872,4063324,4062373],"length":1,"stats":{"Line":0}},{"line":202,"address":[4063518,4062052],"length":1,"stats":{"Line":0}},{"line":203,"address":[4063571],"length":1,"stats":{"Line":0}},{"line":207,"address":[4059921,4059942,4059232,4059342,4059501,4059765],"length":1,"stats":{"Line":0}},{"line":211,"address":[4564352],"length":1,"stats":{"Line":0}},{"line":212,"address":[4564444,4564377],"length":1,"stats":{"Line":0}},{"line":213,"address":[4564402],"length":1,"stats":{"Line":0}},{"line":214,"address":[4064760,4063616],"length":1,"stats":{"Line":0}},{"line":215,"address":[4063648],"length":1,"stats":{"Line":0}},{"line":216,"address":[4063670],"length":1,"stats":{"Line":0}},{"line":217,"address":[4063696],"length":1,"stats":{"Line":0}},{"line":218,"address":[4063707,4063778,4063872],"length":1,"stats":{"Line":0}},{"line":219,"address":[4063890,4064734],"length":1,"stats":{"Line":0}},{"line":220,"address":[4064022,4064073],"length":1,"stats":{"Line":0}},{"line":221,"address":[4064101,4064148,4064054],"length":1,"stats":{"Line":0}},{"line":222,"address":[4064108],"length":1,"stats":{"Line":0}},{"line":223,"address":[4064174,4064198,4064134,4064234],"length":1,"stats":{"Line":0}},{"line":224,"address":[4064271,4064184,4064230],"length":1,"stats":{"Line":0}},{"line":226,"address":[4064401,4064252,4064300],"length":1,"stats":{"Line":0}},{"line":227,"address":[4064345,4064426,4064441],"length":1,"stats":{"Line":0}},{"line":228,"address":[4064430],"length":1,"stats":{"Line":0}},{"line":230,"address":[4064443],"length":1,"stats":{"Line":0}},{"line":232,"address":[4064597,4064686,4064462],"length":1,"stats":{"Line":0}},{"line":233,"address":[4064665,4064488],"length":1,"stats":{"Line":0}},{"line":234,"address":[4064589],"length":1,"stats":{"Line":0}},{"line":235,"address":[4064705,4064739],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":151},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","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::dpi::LogicalSize;\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 emmagui_support;\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":19,"address":[4937728],"length":1,"stats":{"Line":0}},{"line":20,"address":[4937745],"length":1,"stats":{"Line":0}},{"line":24,"address":[4937808],"length":1,"stats":{"Line":0}},{"line":25,"address":[4937827],"length":1,"stats":{"Line":0}},{"line":27,"address":[4937872],"length":1,"stats":{"Line":0}},{"line":29,"address":[4937895],"length":1,"stats":{"Line":0}},{"line":36,"address":[],"length":0,"stats":{"Line":0}},{"line":37,"address":[],"length":0,"stats":{"Line":0}},{"line":40,"address":[4678333,4677350,4675264],"length":1,"stats":{"Line":0}},{"line":45,"address":[],"length":0,"stats":{"Line":0}},{"line":47,"address":[4675491,4675584],"length":1,"stats":{"Line":0}},{"line":48,"address":[4675706,4675892],"length":1,"stats":{"Line":0}},{"line":49,"address":[],"length":0,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":53,"address":[],"length":0,"stats":{"Line":0}},{"line":55,"address":[4676047,4676090],"length":1,"stats":{"Line":0}},{"line":57,"address":[],"length":0,"stats":{"Line":0}},{"line":58,"address":[],"length":0,"stats":{"Line":0}},{"line":59,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[4676366,4676414],"length":1,"stats":{"Line":0}},{"line":62,"address":[],"length":0,"stats":{"Line":0}},{"line":63,"address":[],"length":0,"stats":{"Line":0}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":70,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":0}},{"line":73,"address":[],"length":0,"stats":{"Line":0}},{"line":74,"address":[4677272,4677092],"length":1,"stats":{"Line":0}},{"line":77,"address":[],"length":0,"stats":{"Line":0}},{"line":80,"address":[],"length":0,"stats":{"Line":0}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":85,"address":[],"length":0,"stats":{"Line":0}},{"line":87,"address":[],"length":0,"stats":{"Line":0}},{"line":88,"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":[4678860],"length":1,"stats":{"Line":0}},{"line":92,"address":[4678975],"length":1,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":0}},{"line":95,"address":[],"length":0,"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":100,"address":[],"length":0,"stats":{"Line":0}},{"line":101,"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":106,"address":[],"length":0,"stats":{"Line":0}},{"line":107,"address":[],"length":0,"stats":{"Line":0}},{"line":108,"address":[],"length":0,"stats":{"Line":0}},{"line":109,"address":[],"length":0,"stats":{"Line":0}},{"line":112,"address":[],"length":0,"stats":{"Line":0}},{"line":113,"address":[4679347],"length":1,"stats":{"Line":0}},{"line":114,"address":[],"length":0,"stats":{"Line":0}},{"line":115,"address":[],"length":0,"stats":{"Line":0}},{"line":116,"address":[4679517],"length":1,"stats":{"Line":0}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":118,"address":[],"length":0,"stats":{"Line":0}},{"line":119,"address":[4679581],"length":1,"stats":{"Line":0}},{"line":121,"address":[],"length":0,"stats":{"Line":0}},{"line":122,"address":[],"length":0,"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":128,"address":[4679110,4679214],"length":1,"stats":{"Line":0}},{"line":130,"address":[],"length":0,"stats":{"Line":0}},{"line":131,"address":[],"length":0,"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":142,"address":[4937952,4939500],"length":1,"stats":{"Line":0}},{"line":143,"address":[4937969],"length":1,"stats":{"Line":0}},{"line":149,"address":[4939094,4937993],"length":1,"stats":{"Line":0}},{"line":150,"address":[4938392],"length":1,"stats":{"Line":0}},{"line":153,"address":[4938069],"length":1,"stats":{"Line":0}},{"line":163,"address":[4938050],"length":1,"stats":{"Line":0}},{"line":166,"address":[4938960],"length":1,"stats":{"Line":0}},{"line":169,"address":[4938645],"length":1,"stats":{"Line":0}},{"line":175,"address":[4938526],"length":1,"stats":{"Line":0}},{"line":176,"address":[4938611],"length":1,"stats":{"Line":0}},{"line":180,"address":[4939439],"length":1,"stats":{"Line":0}},{"line":182,"address":[4939472],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":85},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","computer.rs"],"content":"use log::{debug, error};\nuse crate::chip8::delay_timer::DelayTimer;\nuse crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;\nuse crate::chip8::keypad::Keypad;\nuse crate::chip8::registers::Chip8Registers;\nuse crate::chip8::sound_timer::SoundTimer;\nuse crate::chip8::stack::Chip8Stack;\nuse crate::chip8::util::InstructionUtil;\nuse crate::constants::{CHIP8_MEMORY_SIZE, CHIP8_REGISTER_COUNT};\n\nuse super::{\n cpu_states::Chip8CpuStates, instructions::Chip8CpuInstructions, system_memory::Chip8SystemMemory, video::Chip8Video,\n};\n\nconst STACK_POINTER_DEFAULT: i16 = 0x100;\n\n#[derive(Clone)]\npub struct Chip8Computer {\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}\nimpl Default for Chip8Computer {\n fn default() -\u003e Self {\n Self {\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 }\n }\n}\n\nimpl Chip8Computer {\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.video_memory.format_as_string()\n }\n\n pub fn new_with_program(new_program: Box\u003cVec\u003cu16\u003e\u003e) -\u003e Self {\n let mut working = Chip8Computer::new();\n for i in 0..new_program.len() {\n let high_byte = (new_program[i as usize] \u003e\u003e 8) as u8;\n let low_byte = (new_program[i] \u0026 0xff) as u8;\n let base_offset = i * 2;\n working.memory.poke(base_offset as u16, high_byte);\n working.memory.poke((base_offset + 1) as u16, low_byte);\n }\n working\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: Box\u003cVec\u003cu8\u003e\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 }\n\n pub fn step_system(\u0026mut self) -\u003e \u0026mut Chip8Computer {\n debug!(\"Stepping System 1 Step\");\n // read the next instruction\n\n let mut working_instruction: u16 = 0b0000000000000000;\n let start_pc = self.registers.peek_pc();\n let high_byte = (self.memory.clone().peek(start_pc) as u16).rotate_left(8);\n let low_byte = self.memory.clone().peek(start_pc + 1) as u16;\n let result = high_byte | low_byte;\n let decoded_instruction =\n Chip8CpuInstructions::decode(result);\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 self.sound_timer.tick();\n self.delay_timer.tick();\n }\n Chip8CpuStates::WaitingForKey =\u003e {\n println!(\"waiting for a key press...\");\n }\n _ =\u003e {}\n }\n self\n }\n}\n\n#[cfg(test)]\nmod test {\n use rand::random;\n use crate::constants::CHIP8_VIDEO_MEMORY;\n use super::*;\n\n #[test]\n fn smoke() {\n assert!(true)\n }\n\n}\n\n","traces":[{"line":29,"address":[19711456],"length":1,"stats":{"Line":4}},{"line":31,"address":[1703104],"length":1,"stats":{"Line":4}},{"line":32,"address":[1703114],"length":1,"stats":{"Line":4}},{"line":33,"address":[1703127],"length":1,"stats":{"Line":4}},{"line":34,"address":[1703140],"length":1,"stats":{"Line":4}},{"line":35,"address":[1703149],"length":1,"stats":{"Line":4}},{"line":36,"address":[19711547],"length":1,"stats":{"Line":4}},{"line":37,"address":[1703167],"length":1,"stats":{"Line":4}},{"line":38,"address":[1703180],"length":1,"stats":{"Line":4}},{"line":45,"address":[1703424],"length":1,"stats":{"Line":0}},{"line":46,"address":[1703441],"length":1,"stats":{"Line":0}},{"line":49,"address":[1703472],"length":1,"stats":{"Line":0}},{"line":50,"address":[1703489],"length":1,"stats":{"Line":0}},{"line":53,"address":[1703520],"length":1,"stats":{"Line":0}},{"line":54,"address":[1703545],"length":1,"stats":{"Line":0}},{"line":57,"address":[1703600,1704237],"length":1,"stats":{"Line":0}},{"line":58,"address":[1703648],"length":1,"stats":{"Line":0}},{"line":59,"address":[1703761,1703695,1703927],"length":1,"stats":{"Line":0}},{"line":60,"address":[1703948],"length":1,"stats":{"Line":0}},{"line":61,"address":[19712416],"length":1,"stats":{"Line":0}},{"line":62,"address":[1704079,1704134],"length":1,"stats":{"Line":0}},{"line":63,"address":[1704114],"length":1,"stats":{"Line":0}},{"line":64,"address":[1704159],"length":1,"stats":{"Line":0}},{"line":66,"address":[19712279],"length":1,"stats":{"Line":0}},{"line":68,"address":[1704272],"length":1,"stats":{"Line":4}},{"line":69,"address":[19712680],"length":1,"stats":{"Line":4}},{"line":72,"address":[1704672,1704304],"length":1,"stats":{"Line":0}},{"line":73,"address":[1704336,1704397],"length":1,"stats":{"Line":0}},{"line":74,"address":[19712805,19712923],"length":1,"stats":{"Line":0}},{"line":75,"address":[1704538],"length":1,"stats":{"Line":0}},{"line":76,"address":[19713051,19712996],"length":1,"stats":{"Line":0}},{"line":77,"address":[1704630,1704667],"length":1,"stats":{"Line":0}},{"line":81,"address":[19713104],"length":1,"stats":{"Line":0}},{"line":82,"address":[1705019,1704776],"length":1,"stats":{"Line":0}},{"line":85,"address":[19713161],"length":1,"stats":{"Line":0}},{"line":86,"address":[1704816],"length":1,"stats":{"Line":0}},{"line":87,"address":[1704936,1704846],"length":1,"stats":{"Line":0}},{"line":88,"address":[1704944,1705164,1705320],"length":1,"stats":{"Line":0}},{"line":89,"address":[19713605],"length":1,"stats":{"Line":0}},{"line":90,"address":[1705215],"length":1,"stats":{"Line":0}},{"line":93,"address":[1705269],"length":1,"stats":{"Line":0}},{"line":95,"address":[1705288],"length":1,"stats":{"Line":0}},{"line":97,"address":[1705354],"length":1,"stats":{"Line":0}},{"line":98,"address":[1705371],"length":1,"stats":{"Line":0}},{"line":101,"address":[1705385],"length":1,"stats":{"Line":0}}],"covered":11,"coverable":45},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","cpu_states.rs"],"content":"#[derive(Clone, Copy, Default)]\npub enum Chip8CpuStates {\n #[default]\n WaitingForInstruction,\n WaitingForKey,\n ExecutingInstruction,\n Error,\n}\n\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","delay_timer.rs"],"content":"#[derive(Clone, Copy)]\npub struct DelayTimer {\n counter: i32\n}\n\nimpl DelayTimer {\n pub fn current(\u0026self) -\u003e i32 {\n self.counter\n }\n\n pub fn new() -\u003e Self {\n DelayTimer {\n counter: 0xff\n }\n }\n pub fn set_timer(\u0026mut self, new_value: i32) {\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\n#[cfg(test)]\nmod test {\n use crate::chip8::sound_timer::SoundTimer;\n use super::*;\n\n #[test]\n fn smoke() {\n assert!(true)\n }\n\n #[test]\n fn 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]\n fn 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}","traces":[{"line":7,"address":[1838736],"length":1,"stats":{"Line":1}},{"line":8,"address":[1838741],"length":1,"stats":{"Line":1}},{"line":16,"address":[1838768],"length":1,"stats":{"Line":1}},{"line":17,"address":[1838777],"length":1,"stats":{"Line":2}},{"line":20,"address":[1838784],"length":1,"stats":{"Line":1}},{"line":21,"address":[1838837,1838798],"length":1,"stats":{"Line":2}},{"line":22,"address":[1838839,1838813],"length":1,"stats":{"Line":1}}],"covered":7,"coverable":7},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","instructions.rs"],"content":"use std::ops::{Shl, Shr};\nuse imgui::ColorPicker3;\nuse log::debug;\nuse rand::random;\nuse crate::chip8::computer::{Chip8Computer};\nuse crate::chip8::cpu_states::Chip8CpuStates::WaitingForKey;\nuse crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;\nuse crate::chip8::util::InstructionUtil;\nuse crate::chip8::video::Chip8Video;\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)]\npub enum Chip8CpuInstructions {\n /// 0nnn\n /// Exit to System Call at nnn\n SysAddr(i16),\n /// Clear Screen\n CLS,\n /// Return from Subroutine\n RET,\n /// 1nnn\n /// Jump to Address nnn\n JpAddr(i16),\n /// 2nnn\n /// Call Subroutine at nnn\n CallAddr(i16),\n /// 0x3xkk\n /// Skip next instruction if Vx == kk\n SeVxByte(i16, i16),\n /// 4xkk\n /// Skip next instruction if Vx != kk\n SneVxByte(i16, i16),\n /// 5xy0\n /// Skip next instruction if Vx == Vy\n SeVxVy(u16, u16),\n /// 6xkk\n /// Set Vx = kk\n LdVxByte(u16, u16),\n /// 7xkk\n /// Set Vx = Vx + kk\n AddVxByte(u16, u16),\n /// 8xy0\n /// Set Vx = Vy\n LdVxVy(u16, u16),\n /// 8xy1\n /// Set Vx = Vx OR Vy\n OrVxVy(u16, u16),\n /// 8xy2\n /// Set Vx = Vx AND Vy\n AndVxVy(u16, u16),\n /// 8xy3\n /// Set Vx = Vx XOR Vy\n XorVxVy(u16, u16),\n /// 8xy4\n /// Set Vx = Vx + Vy\n /// Set VF=1 if Carry\n AddVxVy(u16, u16),\n /// 8xy5\n /// Set Vx = Vx - Vy\n /// Set VF=1 if No Borrow\n SubVxVy(u16, u16),\n /// 8xy6\n /// Set Vx = Vx SHR 1\n ShrVxVy(u16, u16),\n /// 8xy7\n /// Set Vx = Vy - Vx\n /// Set VF=1 if No Borrow\n SubnVxVy(u16, u16),\n /// 8xye\n /// Set Vx = Vx SHL 1\n ShlVxVy(u16, u16),\n /// 9xy0\n /// Skip next instruction if Vx != Vy\n SneVxVy(u16, u16),\n /// Annn\n /// Load I register with NNN\n LdIAddr(u16),\n /// Bnnn\n /// Jump to nnn+V0\n JpV0Addr(u16),\n /// Cxkk\n /// Set Vx = Random u8 AND kk\n RndVxByte(u16, u16),\n /// Dxyn\n /// Display N byte tall sprite starting at Vx, Vy\n DrawVxVyNibble(u16, u16, u16),\n /// Ex9E\n /// Skip next instruction of key in Vx pressed\n SkpVx(u16),\n /// ExA1\n /// Skip Next If Key Not Pressed\n SnkpVx(u16),\n /// Fx07\n /// Set Vx = Dt\n LdVxDt(u16),\n /// Fx0A\n /// Wait for Key to be pressed and store\n /// in Vx\n LdVxK(u16),\n /// Fx15\n /// Load Value in Delay Timer to Vx\n LdDtVx(u16), // 0xFx15 Set Delay Timer\n /// Fx18\n /// Set Dt = Vx\n LdStVx(u16),\n AddIVx(u16), // 0xFx1E I = I + Vx\n LdFVx(u16), // 0xFx29 Set I = Location of sprite for Digit Vx\n LdBVx(u16), // 0xFx33 Store BCD of Vx in I, I+1, I+2\n LdIVx(u16), // 0xFx55 Store V0 to Vx in memory starting at I\n LdVxI(u16), // 0xFx65 Load V0 to Vx in memory starting at I\n XXXXERRORINSTRUCTION,\n}\n\nimpl Chip8CpuInstructions {\n pub fn encode(\u0026self) -\u003e u16 {\n match self {\n Chip8CpuInstructions::SysAddr(target) =\u003e {\n (0x0000 | (target \u0026 0x0FFF)) as u16\n }\n Chip8CpuInstructions::CLS =\u003e {\n 0x00E0\n }\n Chip8CpuInstructions::RET =\u003e {\n 0x00EE\n }\n Chip8CpuInstructions::JpAddr(new_addr) =\u003e {\n 0x1000 | (new_addr \u0026 0x0FFF) as u16\n }\n Chip8CpuInstructions::CallAddr(address) =\u003e {\n (0x2000 | (address \u0026 0x0FFF)) as u16\n }\n Chip8CpuInstructions::SeVxByte(vx_register, byte) =\u003e {\n (0x3000 | (vx_register \u003c\u003c 8 | byte) as u16)\n }\n Chip8CpuInstructions::SneVxByte(vx_register, byte) =\u003e {\n (0x4000i16 | vx_register \u003c\u003c 8 | byte) as u16\n }\n Chip8CpuInstructions::SeVxVy(x_register, y_register) =\u003e {\n 0x5000u16 | (x_register \u003c\u003c 8 | y_register \u003c\u003c 4)\n }\n Chip8CpuInstructions::LdVxByte(x_register, byte) =\u003e {\n 0x6000u16 | x_register \u003c\u003c 8 | byte\n }\n Chip8CpuInstructions::AddVxByte(x_register, byte) =\u003e {\n 0x7000u16 | x_register \u003c\u003c 8 | byte\n }\n Chip8CpuInstructions::LdVxVy(x_register, y_register) =\u003e {\n 0x8000u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::OrVxVy(x_register, y_register) =\u003e {\n 0x8001u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::AndVxVy(x_register, y_register) =\u003e {\n 0x8002u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::XorVxVy(x_register, y_register) =\u003e {\n 0x8003u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::AddVxVy(x_register, y_register) =\u003e {\n 0x8004u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::SubVxVy(x_register, y_register) =\u003e {\n 0x8005u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::ShrVxVy(x_register, y_register) =\u003e {\n 0x8006u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::SubnVxVy(x_register, y_register) =\u003e {\n 0x8007u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::ShlVxVy(x_register, y_register) =\u003e {\n 0x800Eu16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::SneVxVy(x_register, y_register) =\u003e {\n 0x9000u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::LdIAddr(addr) =\u003e {\n 0xA000u16 | addr\n }\n Chip8CpuInstructions::JpV0Addr(addr) =\u003e {\n 0xB000u16 | addr\n }\n Chip8CpuInstructions::RndVxByte(x_register, byte) =\u003e {\n 0xC000u16 | x_register \u003c\u003c 8 | byte\n }\n Chip8CpuInstructions::DrawVxVyNibble(x_register, y_register, height) =\u003e {\n 0xD000u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4 | height\n }\n Chip8CpuInstructions::SkpVx(x_register) =\u003e {\n 0xE09Eu16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::SnkpVx(x_register) =\u003e {\n 0xE0A1u16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdVxDt(x_register) =\u003e {\n 0xF007u16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdVxK(x_register) =\u003e {\n 0xF00Au16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdDtVx(x_register) =\u003e {\n 0xF015u16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdStVx(x_register) =\u003e {\n 0xF018u16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::AddIVx(x_register) =\u003e {\n 0xF01Eu16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdFVx(x_register) =\u003e {\n 0xF029u16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdBVx(x_register) =\u003e {\n 0xf033u16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdIVx(x_register) =\u003e {\n 0xf055u16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdVxI(x_register) =\u003e {\n 0xf065u16 | x_register \u003c\u003c 8\n }\n XXXXERRORINSTRUCTION =\u003e {\n 0xffff\n }\n }\n }\n pub fn decode(input: u16) -\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;\n\n match input {\n 0x00E0 =\u003e {\n // 00E0 - CLS\n // Clear the display.\n Chip8CpuInstructions::CLS\n }\n 0x00EE =\u003e {\n // 00EE - RET\n // Return from a subroutine.\n Chip8CpuInstructions::RET\n }\n 0x0000..=0x0FFF =\u003e {\n // 0nnn - SYS addr\n // Jump to a machine code routine at nnn.\n Chip8CpuInstructions::SysAddr(addr_param as i16)\n }\n 0x1000..=0x1FFF =\u003e {\n // 1nnn - JP addr\n // Jump to location nnn.\n Chip8CpuInstructions::JpAddr(addr_param as i16)\n }\n 0x2000..=0x2FFF =\u003e {\n // 2nnn - CALL addr\n // Call subroutine at nnn.\n Chip8CpuInstructions::CallAddr(addr_param as i16)\n }\n 0x3000..=0x3FFF =\u003e {\n // 3xkk - SE Vx, byte\n // Skip next instruction if Vx = kk.\n Chip8CpuInstructions::SeVxByte(x_param as i16, byte_param as i16)\n }\n 0x4000..=0x4FFF =\u003e {\n // 4xkk - SNE Vx, byte\n // Skip next instruction if Vx != kk.\n Chip8CpuInstructions::SneVxByte(x_param as i16, byte_param as i16)\n }\n 0x5000..=0x5FF0 =\u003e {\n // 5xy0 - SE Vx, Vy\n // Skip next instruction if Vx = Vy.\n // if the last nibble isn't 0...\n if 0xf \u0026 input \u003e 0 {\n // ... we have a problem.\n Chip8CpuInstructions::XXXXERRORINSTRUCTION\n } else {\n Chip8CpuInstructions::SeVxVy(x_param, y_param)\n }\n }\n 0x6000..=0x6FFF =\u003e {\n // 6xkk - LD Vx, byte\n // Set Vx = kk.\n Chip8CpuInstructions::LdVxByte(x_param, byte_param)\n }\n 0x7000..=0x7FFF =\u003e {\n // ADD Vx, Byte\n Chip8CpuInstructions::AddVxByte(x_param, byte_param)\n }\n 0x8000..=0x8FFE =\u003e {\n // 0x8000 Series\n let last_nibble = input \u0026 0xF;\n match last_nibble {\n 0x0 =\u003e {\n // LD Vx, Vy\n Chip8CpuInstructions::LdVxVy(x_param, y_param)\n }\n 0x1 =\u003e {\n // OR Vx, Vy\n Chip8CpuInstructions::OrVxVy(x_param, y_param)\n }\n 0x2 =\u003e {\n // AND Vx, Vy\n Chip8CpuInstructions::AndVxVy(x_param, y_param)\n }\n 0x3 =\u003e {\n // XOR Vx, Vy\n Chip8CpuInstructions::XorVxVy(x_param, y_param)\n }\n 0x4 =\u003e {\n // ADD Vx, Vy\n Chip8CpuInstructions::AddVxVy(x_param, y_param)\n }\n 0x5 =\u003e {\n // SUB Vx, Vy\n Chip8CpuInstructions::SubVxVy(x_param, y_param)\n }\n 0x6 =\u003e {\n // SHR Vx, {, Vy }\n Chip8CpuInstructions::ShrVxVy(x_param, y_param)\n }\n 0x7 =\u003e {\n // SUBN Vx, Vy\n Chip8CpuInstructions::SubnVxVy(x_param, y_param)\n }\n 0xE =\u003e {\n // SHL Vx, {, Vy}\n Chip8CpuInstructions::ShlVxVy(x_param, y_param)\n }\n _ =\u003e {\n Chip8CpuInstructions::XXXXERRORINSTRUCTION\n }\n }\n }\n 0x9000..=0x9FF0 =\u003e {\n // SNE Vx, Vy\n if 0xf \u0026 input \u003e 0 {\n XXXXERRORINSTRUCTION\n } else {\n Chip8CpuInstructions::SneVxVy(x_param, y_param)\n }\n }\n 0xA000..=0xAFFF =\u003e {\n // LD I, Addr\n Chip8CpuInstructions::LdIAddr(addr_param)\n }\n 0xB000..=0xBFFF =\u003e {\n // JP V0, Addr\n Chip8CpuInstructions::JpV0Addr(addr_param)\n }\n 0xC000..=0xCFFF =\u003e {\n // RND Vx, byte\n Chip8CpuInstructions::RndVxByte(x_param, byte_param)\n }\n 0xD000..=0xDFFF =\u003e {\n // DRAW Vx, Vy, nibble\n Chip8CpuInstructions::DrawVxVyNibble(x_param, y_param, nibble_param)\n }\n 0xE09E..=0xEFA1 =\u003e {\n match last_byte {\n 0x9E =\u003e {\n Chip8CpuInstructions::SkpVx(ubln)\n }\n 0xA1 =\u003e {\n Chip8CpuInstructions::SnkpVx(ubln)\n }\n _ =\u003e {\n XXXXERRORINSTRUCTION\n }\n }\n }\n 0xF007..=0xFF65 =\u003e {\n match last_byte {\n 0x07 =\u003e {\n Chip8CpuInstructions::LdVxDt(ubln)\n }\n 0x0A =\u003e {\n Chip8CpuInstructions::LdVxK(ubln)\n }\n 0x15 =\u003e {\n Chip8CpuInstructions::LdDtVx(ubln)\n }\n 0x18 =\u003e {\n Chip8CpuInstructions::LdStVx(ubln)\n }\n 0x1E =\u003e {\n Chip8CpuInstructions::AddIVx(ubln)\n }\n 0x29 =\u003e {\n Chip8CpuInstructions::LdFVx(ubln)\n }\n 0x33 =\u003e {\n Chip8CpuInstructions::LdBVx(ubln)\n }\n 0x55 =\u003e {\n Chip8CpuInstructions::LdIVx(ubln)\n }\n 0x65 =\u003e {\n Chip8CpuInstructions::LdVxI(ubln)\n }\n _ =\u003e {\n XXXXERRORINSTRUCTION\n }\n }\n }\n _ =\u003e {\n XXXXERRORINSTRUCTION\n }\n }\n }\n\n pub fn execute(\u0026self, mut input: \u0026mut Chip8Computer) -\u003e Chip8Computer {\n let start_pc = input.registers.peek_pc();\n input.registers.poke_pc(start_pc + 2);\n let _ = match self {\n // 0x0nnn Exit to System Call\n Chip8CpuInstructions::SysAddr(new_address) =\u003e {\n debug!(\"SysAddr [0x{new_address:3x}]\");\n\n input.registers.poke_pc(*new_address as u16);\n }\n // * 0x00E0 Clear Screen\n Chip8CpuInstructions::CLS =\u003e {\n for i in 0..(64 * 32) {\n input.video_memory.poke(i, false);\n }\n }\n // 0x00EE Return from Subroutine\n Chip8CpuInstructions::RET =\u003e {\n let return_address = input.stack.pop();\n debug!(\"Returning from subroutine to {return_address:03x}\");\n input.registers.poke_pc(return_address);\n }\n // 0x1nnn Jump to Address\n Chip8CpuInstructions::JpAddr(new_address) =\u003e {\n debug!(\"JpAddr [0x{new_address:3x}]\");\n input.registers.poke_pc(*new_address as u16);\n }\n // 0x2nnn Call Subroutine\n Chip8CpuInstructions::CallAddr(new_address) =\u003e {\n debug!(\"CALL ADDR 0x{new_address:3x}\");\n let return_address = input.registers.peek_pc();\n input.registers.poke_pc(*new_address as u16);\n input.stack.push(\u0026return_address);\n }\n // 0x3xkk Skip next instruction if Vx = kk.\n Chip8CpuInstructions::SeVxByte(vx_register, byte) =\u003e {\n if input.registers.peek(*vx_register as u8) == *byte as u8 {\n input.registers.advance_pc();\n }\n debug!(\"SeVxByte [0x{vx_register:1x}] [0x{byte:2x}]\");\n }\n // 0x4xkk Skip next instruction if Vx != kk\n Chip8CpuInstructions::SneVxByte(x, byte) =\u003e {\n if input.registers.peek(*x as u8) != *byte as u8 {\n input.registers.advance_pc();\n }\n debug!(\"SneVxByte [0x{x:1x}] [0x{byte:2x}\");\n }\n // 0x5xy0 Skip next instruction if Vx == Vy\n Chip8CpuInstructions::SeVxVy(x, y) =\u003e {\n let lhs = input.registers.peek(*x as u8);\n let rhs = input.registers.peek(*y as u8);\n\n if lhs == rhs {\n input.registers.advance_pc();\n }\n debug!(\"SeVxVy [0x{x:1x}] [0x{y:1x}]\");\n }\n // 0x6xkk Set Vx = kk\n Chip8CpuInstructions::LdVxByte(register, byte) =\u003e {\n let byte_value = *byte as u8;\n input.registers.poke(*register as u8, byte_value);\n debug!(\"LdVxByte [0x{register:1x}] [0x{byte:2.0x}]\");\n }\n // 0x7xkk Set Vx = Vx + kk\n Chip8CpuInstructions::AddVxByte(vx_register, byte) =\u003e {\n let x_value: u16 = input.registers.peek(*vx_register as u8) as u16;\n let value_to_poke = (x_value + *byte) as u8;\n input.registers.poke(*vx_register as u8, value_to_poke);\n debug!(\"AddVxByte [0x{vx_register:1x}] [0x{byte:02x}] [0x{value_to_poke:02x}\");\n }\n // 0x8xy0 Set value of Vy in Vx\n Chip8CpuInstructions::LdVxVy(x, y) =\u003e {\n input.registers.poke(*x as u8, input.registers.peek(*y as u8));\n debug!(\"LdVxVy [0x{x:1x}] [0x{y:1x}]\");\n }\n // 0x8xy1 Set Vx = Vx OR Vy\n Chip8CpuInstructions::OrVxVy(x, y) =\u003e {\n // shift them to 16 bit\n let working_16_x = input.registers.peek(*x as u8) as u16;\n let working_16_y = input.registers.peek(*y as u8) as u16;\n // OR them\n let working_16_or = working_16_x | working_16_y;\n // shift them back to 8 bit.\n let to_set = working_16_or as u8;\n input.registers.poke(*x as u8, to_set);\n debug!(\"OrVxVy [0x{x:1x}] [0x{y:1x}]\")\n }\n // 0x8xy2 Set Vx = Vx AND Vy\n Chip8CpuInstructions::AndVxVy(x, y) =\u003e {\n let lhs_16 = input.registers.peek(*x as u8) as u16;\n let rhs_16 = input.registers.peek(*y as u8) as u16;\n let and_16 = lhs_16 \u0026 rhs_16;\n\n input.registers.poke(*x as u8, and_16 as u8);\n debug!(\"AndVxVy [0x{x:02x}] [0x{y:02x}] [0b{and_16:08b}\");\n }\n // 0x8xy3 Set Vx = Vx XOR Vy\n Chip8CpuInstructions::XorVxVy(x, y) =\u003e {\n let lhs_16 = input.registers.peek(*x as u8) as u16;\n let rhs_16 = input.registers.peek(*y as u8) as u16;\n let xor_16 = lhs_16 ^ rhs_16;\n input.registers.poke(*x as u8, xor_16 as u8);\n }\n // 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)\n Chip8CpuInstructions::AddVxVy(x, y) =\u003e {\n let lhs = input.registers.peek(*x as u8) as i16;\n let rhs = input.registers.peek(*y as u8) as i16;\n let working = lhs + rhs;\n if working \u003e 0xff {\n input.registers.poke(0xf, 0x01);\n }\n input.registers.poke(*x as u8, working as u8);\n }\n Chip8CpuInstructions::SubVxVy(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 let mut x_value: u16 = input.registers.peek(*x as u8) as u16;\n let y_value = input.registers.peek(*y as u8);\n // do we borrow?\n if y_value \u003e= x_value as u8 {\n x_value += 256;\n input.registers.poke(0xf, 1);\n } else {\n input.registers.poke(0xf, 0);\n }\n let result = (x_value - y_value as u16) as u8;\n input.registers.poke(*x as u8, result);\n }\n Chip8CpuInstructions::ShrVxVy(x, _) =\u003e {\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, otherwise 0. Then Vx is divided by 2.\n let initial_value = input.registers.peek(*x as u8);\n if 0xb1 \u0026 initial_value == 1 {\n input.registers.poke(0xf, 1);\n }\n input.registers.poke(*x as u8, initial_value.shr(1));\n }\n Chip8CpuInstructions::SubnVxVy(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 as u8);\n let x_register = input.registers.peek(*x as u8);\n let new_value = if y_register \u003c= x_register { 1 } else { 0 };\n let value_to_poke = if y_register \u003c= x_register {\n ((y_register as u16 + 256) - x_register as u16) as u8\n } else {\n y_register - x_register\n };\n input.registers.poke(0xf, new_value);\n input.registers.poke(*x as u8, value_to_poke);\n }\n\n Chip8CpuInstructions::ShlVxVy(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 as u8);\n if 0x80 \u0026 initial_value == 0x80 {\n input.registers.poke(0xf, 1);\n }\n input.registers.poke(*x as u8, initial_value.shl(1));\n }\n Chip8CpuInstructions::SneVxVy(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 as u8);\n let y_reg_value = input.registers.peek(*vy_register as u8);\n if x_reg_value != y_reg_value {\n input.registers.advance_pc();\n }\n }\n Chip8CpuInstructions::LdIAddr(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::JpV0Addr(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.registers.poke_pc(input.registers.peek(0) as u16 + addr);\n }\n Chip8CpuInstructions::RndVxByte(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 new_value: u8 = random();\n input.registers.poke(*x as u8, (new_value \u0026 *byte as u8))\n }\n Chip8CpuInstructions::DrawVxVyNibble(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\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\n let x_offset = input.registers.peek(*x as u8);\n let y_offset = input.registers.peek(*y as u8);\n\n println!(\"X_OFFSET = {x_offset} / y_offset = {y_offset}\");\n let target_memory_offset = x_offset as u16 * 64 + y_offset as u16;\n\n println!(\"CHIP8CPUINSTRUCTION:DRAWVXNIBBLE -\u003e STARTING AT {source_memory_offset} WRITING TO {target_memory_offset}\");\n let num_bytes_to_read = *n;\n println!(\"CHIP8CPUINSTRUCTION:DRAWVXNIBBLE -\u003e PREPARING TO READ {num_bytes_to_read} BYTES FROM MEMORY TO VIDEO\");\n for byte_index in 0..num_bytes_to_read {\n let current_byte = input.memory.peek(byte_index + source_memory_offset);\n println!(\"CHIP8CPUINSTRUCTION:DRAWVXNIBBLE -\u003e READ BYTE [0x{byte_index:2x}]\\t{current_byte:02x}\\t{current_byte:08b}\");\n for bit_index in 0..8 {\n let data_offset = ((x_offset as u16 + byte_index) * 64 ) + (y_offset + bit_index) as u16;\n let current_bit = (current_byte.shr(7 - bit_index) \u0026 0x1u8) == 0x1u8;\n input.video_memory.poke(data_offset, current_bit);\n }\n }\n\n println!(\"PPOOSSTT -\u003e CHIP8CPUINSTRUCTION:DRAWVXNIBBLE -\u003e {}\", input.video_memory.format_as_string());\n\n let mut did_change: bool = false;\n\n if did_change {\n input.registers.poke(0xf, 1u8);\n } else {\n input.registers.poke(0xf, 0u8);\n }\n }\n Chip8CpuInstructions::SkpVx(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 as u8);\n let is_pressed = input.keypad.pressed(key_to_check);\n if is_pressed {\n input.registers.advance_pc();\n }\n }\n Chip8CpuInstructions::SnkpVx(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 as u8);\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::LdVxDt(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 as u8, value_to_set as u8);\n }\n Chip8CpuInstructions::LdVxK(x) =\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::LdDtVx(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 as u8);\n input.delay_timer.set_timer(new_time as i32);\n }\n Chip8CpuInstructions::LdStVx(new_time) =\u003e {\n let new_value = input.registers.peek(*new_time as u8);\n input.sound_timer.set_timer(new_value as i32);\n }\n Chip8CpuInstructions::AddIVx(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 as u8);\n input.registers.poke_i(base + x_value as u16);\n }\n Chip8CpuInstructions::LdFVx(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 to_offset = input.registers.peek(*x as u8) - 1;\n let real_offset = to_offset as u16 * 5;\n input.registers.poke_i(real_offset as u16);\n }\n Chip8CpuInstructions::LdBVx(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 as u8);\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 // Convert to BCD\n let result = ((hundreds as u16) \u003c\u003c 8) | units as u16;\n (tens \u003c\u003c 4) | units;\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::LdIVx(x) =\u003e {\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 offset = input.registers.peek_i();\n for i in 0..*x {\n input.memory.poke(offset + i, input.registers.peek(i as u8));\n }\n input.registers.poke_i(offset + 1);\n }\n Chip8CpuInstructions::LdVxI(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 println!(\"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 as u8, src_value);\n println!(\"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 };\n input.clone()\n }\n}\n\n#[cfg(test)]\nmod test {\n use dimensioned::typenum::assert_type;\n use ratatui::crossterm::execute;\n use crate::chip8::cpu_states::Chip8CpuStates;\n use crate::chip8::system_memory::{CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_9};\n use super::*;\n\n #[test]\n fn smoke() {\n assert!(true)\n }\n\n #[test]\n fn encode_decode_test() {\n assert_eq!(Chip8CpuInstructions::CLS.encode(), 0x00E0);\n assert_eq!(Chip8CpuInstructions::RET.encode(), 0x00EE);\n assert_eq!(Chip8CpuInstructions::SysAddr(0x123).encode(), 0x0123);\n assert_eq!(Chip8CpuInstructions::JpAddr(0x234).encode(), 0x1234);\n assert_eq!(Chip8CpuInstructions::CallAddr(0x345).encode(), 0x2345);\n assert_eq!(Chip8CpuInstructions::SeVxByte(0x4, 0x56).encode(), 0x3456);\n assert_eq!(Chip8CpuInstructions::SneVxByte(0xa, 0xbc).encode(), 0x4abc);\n assert_eq!(Chip8CpuInstructions::SeVxVy(0xa, 0xb).encode(), 0x5ab0);\n assert_eq!(Chip8CpuInstructions::LdVxByte(0xa, 0xff).encode(), 0x6aff);\n assert_eq!(Chip8CpuInstructions::AddVxByte(0xa, 0xbc).encode(), 0x7abc);\n assert_eq!(Chip8CpuInstructions::LdVxVy(0xa, 0xb).encode(), 0x8ab0);\n assert_eq!(Chip8CpuInstructions::OrVxVy(0xb, 0xa).encode(), 0x8ba1);\n assert_eq!(Chip8CpuInstructions::AndVxVy(0xc, 0xd).encode(), 0x8cd2);\n assert_eq!(Chip8CpuInstructions::XorVxVy(0xd, 0xe).encode(), 0x8de3);\n assert_eq!(Chip8CpuInstructions::AddVxVy(0xe, 0xf).encode(), 0x8ef4);\n assert_eq!(Chip8CpuInstructions::SubVxVy(0xf, 0x0).encode(), 0x8f05);\n assert_eq!(Chip8CpuInstructions::ShrVxVy(0x0, 0x1).encode(), 0x8016);\n assert_eq!(Chip8CpuInstructions::SubnVxVy(0x1, 0x2).encode(), 0x8127);\n assert_eq!(Chip8CpuInstructions::ShlVxVy(0x3, 0x4).encode(), 0x834e);\n assert_eq!(Chip8CpuInstructions::SneVxVy(0xa, 0xb).encode(), 0x9ab0);\n assert_eq!(Chip8CpuInstructions::LdIAddr(0x123).encode(), 0xa123);\n assert_eq!(Chip8CpuInstructions::JpV0Addr(0x234).encode(), 0xb234);\n assert_eq!(Chip8CpuInstructions::RndVxByte(0xa, 0xca).encode(), 0xcaca);\n assert_eq!(Chip8CpuInstructions::DrawVxVyNibble(0xa, 0xb, 0x4).encode(), 0xdab4);\n assert_eq!(Chip8CpuInstructions::SkpVx(0x1).encode(), 0xe19e);\n assert_eq!(Chip8CpuInstructions::SnkpVx(0x2).encode(), 0xe2a1);\n assert_eq!(Chip8CpuInstructions::LdVxDt(0x1).encode(), 0xf107);\n assert_eq!(Chip8CpuInstructions::LdVxK(0x4).encode(), 0xf40a);\n assert_eq!(Chip8CpuInstructions::LdDtVx(0x6).encode(), 0xf615);\n assert_eq!(Chip8CpuInstructions::LdStVx(0xb).encode(), 0xfb18);\n assert_eq!(Chip8CpuInstructions::AddIVx(0xd).encode(), 0xfd1e);\n assert_eq!(Chip8CpuInstructions::LdFVx(0xc).encode(), 0xfc29);\n assert_eq!(Chip8CpuInstructions::LdBVx(0xd).encode(), 0xfd33);\n assert_eq!(Chip8CpuInstructions::LdIVx(0xe).encode(), 0xfe55);\n assert_eq!(Chip8CpuInstructions::LdVxI(0x3).encode(), 0xf365);\n assert!(matches!(Chip8CpuInstructions::decode(0x00E0u16), Chip8CpuInstructions::CLS));\n assert!(matches!(Chip8CpuInstructions::decode(0x00EEu16), Chip8CpuInstructions::RET));\n assert!(matches!(Chip8CpuInstructions::decode(0x0123), Chip8CpuInstructions::SysAddr(0x123)));\n assert!(matches!(Chip8CpuInstructions::decode(0x0FFF), Chip8CpuInstructions::SysAddr(0xfff)));\n assert!(matches!(Chip8CpuInstructions::decode(0x1002), Chip8CpuInstructions::JpAddr(0x2)));\n assert!(matches!(Chip8CpuInstructions::decode(0x1FF0), Chip8CpuInstructions::JpAddr(0xFF0)));\n assert!(matches!(Chip8CpuInstructions::decode(0x2002), Chip8CpuInstructions::CallAddr(0x2)));\n assert!(matches!(Chip8CpuInstructions::decode(0x3123), Chip8CpuInstructions::SeVxByte(0x1, 0x23)));\n assert!(matches!(Chip8CpuInstructions::decode(0x4abc), Chip8CpuInstructions::SneVxByte(0xa, 0xbc)));\n assert!(matches!(Chip8CpuInstructions::decode(0x5ab0), Chip8CpuInstructions::SeVxVy(0xa, 0xb)));\n assert!(matches!(Chip8CpuInstructions::decode(0x6aff), Chip8CpuInstructions::LdVxByte(0xa, 0xff)));\n assert!(matches!(Chip8CpuInstructions::decode(0x7abc), Chip8CpuInstructions::AddVxByte(0xa, 0xbc)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8ab0), Chip8CpuInstructions::LdVxVy(0xa, 0xb)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8ba1), Chip8CpuInstructions::OrVxVy(0xb, 0xa)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8cd2), Chip8CpuInstructions::AndVxVy(0xc, 0xd)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8de3), Chip8CpuInstructions::XorVxVy(0xd, 0xe)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8ef4), Chip8CpuInstructions::AddVxVy(0xe, 0xf)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8f05), Chip8CpuInstructions::SubVxVy(0xf, 0x0)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8016), Chip8CpuInstructions::ShrVxVy(0x0, 0x1)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8127), Chip8CpuInstructions::SubnVxVy(0x1, 0x2)));\n assert!(matches!(Chip8CpuInstructions::decode(0x834e), Chip8CpuInstructions::ShlVxVy(0x3, 0x4)));\n assert!(matches!(Chip8CpuInstructions::decode(0x9ab0), Chip8CpuInstructions::SneVxVy(0xa, 0xb)));\n assert!(matches!(Chip8CpuInstructions::decode(0xa123), Chip8CpuInstructions::LdIAddr(0x123)));\n assert!(matches!(Chip8CpuInstructions::decode(0xb234), Chip8CpuInstructions::JpV0Addr(0x234)));\n assert!(matches!(Chip8CpuInstructions::decode(0xcaca), Chip8CpuInstructions::RndVxByte(0xa, 0xca)));\n assert!(matches!(Chip8CpuInstructions::decode(0xdab4), Chip8CpuInstructions::DrawVxVyNibble(0xa, 0xb, 0x4)));\n assert!(matches!(Chip8CpuInstructions::decode(0xe19e), Chip8CpuInstructions::SkpVx(0x1)));\n assert!(matches!(Chip8CpuInstructions::decode(0xe2a1), Chip8CpuInstructions::SnkpVx(0x2)));\n assert!(matches!(Chip8CpuInstructions::decode(0xf107), Chip8CpuInstructions::LdVxDt(0x1)));\n assert!(matches!(Chip8CpuInstructions::decode(0xf40a), Chip8CpuInstructions::LdVxK(0x4)));\n assert!(matches!(Chip8CpuInstructions::decode(0xf615), Chip8CpuInstructions::LdDtVx(0x6)));\n assert!(matches!(Chip8CpuInstructions::decode(0xfb18), Chip8CpuInstructions::LdStVx(0xb)));\n assert!(matches!(Chip8CpuInstructions::decode(0xfd1e), Chip8CpuInstructions::AddIVx(0xd)));\n assert!(matches!(Chip8CpuInstructions::decode(0xfc29), Chip8CpuInstructions::LdFVx(0xc)));\n assert!(matches!(Chip8CpuInstructions::decode(0xfd33), Chip8CpuInstructions::LdBVx(0xd)));\n assert!(matches!(Chip8CpuInstructions::decode(0xfe55), Chip8CpuInstructions::LdIVx(0xe)));\n assert!(matches!(Chip8CpuInstructions::decode(0xf365), Chip8CpuInstructions::LdVxI(0x3)));\n }\n\n #[test]\n fn decoder_test_invalid_instructions() {\n let invalid_to_encode = [\n 0x5ab1, 0x5abf, 0x8ab8, 0x8abd, 0x8abf,\n 0x9ab1, 0x9abf, 0xea9d, 0xea9f, 0xeaa0,\n 0xeaa2, 0xf006, 0xf008\n ];\n\n for i in invalid_to_encode {\n assert_eq!(Chip8CpuInstructions::decode(i).encode(), 0xffff);\n assert!(matches!(Chip8CpuInstructions::decode(i), Chip8CpuInstructions::XXXXERRORINSTRUCTION));\n }\n }\n\n /// START OF THE EXECUTION TESTS\n #[test]\n fn sys_test() {\n // 0x0nnn Exit to System Call\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::SysAddr(0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::SysAddr(0xFA0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0xFA0);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::SysAddr(0x0AF).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x0AF);\n }\n\n #[test]\n fn jpaddr_test() {\n // 0x1nnn Jump to Address\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::JpAddr(0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::JpAddr(0xABC).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0xABC);\n }\n\n // ** test moved up so it can be used later\n #[test]\n fn ld_vx_byte_test() {\n // 0x6xkk Set Vx = kk\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::LdVxByte(1, 0x12).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0x12);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::LdVxByte(2, 0x21).execute(\u0026mut x);\n assert_eq!(x.registers.peek(2), 0x21);\n }\n\n #[test]\n fn sevxbyte_match_test() {\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::SeVxByte(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::SeVxByte(1, 0x84).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204);\n }\n\n #[test]\n fn se_vx_vy_test() {\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::SeVxVy(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::SeVxVy(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n }\n\n #[test]\n fn ld_vx_vy_test() {\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::LdVxVy(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0x02);\n }\n\n #[test]\n fn or_vx_vy_test() {\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::OrVxVy(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0b01011010);\n }\n\n #[test]\n fn and_vx_vy_test() {\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::AndVxVy(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0xC8);\n }\n\n #[test]\n fn xor_vx_vy_test() {\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::XorVxVy(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0b00110110);\n }\n\n #[test]\n fn add_vx_vy_test() {\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::AddVxVy(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::AddVxVy(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0xf), 1);\n assert_eq!(x.registers.peek(1), 0);\n }\n\n #[test]\n fn shr_vx_vy_test() {\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::ShrVxVy(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::ShrVxVy(0x1, 0x2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0b00000100);\n assert_eq!(x.registers.peek(0xf), 1);\n }\n\n #[test]\n fn ldi_addr_test() {\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::LdIAddr(0x123).execute(\u0026mut x);\n assert_eq!(x.registers.peek_i(), 0x123);\n assert_eq!(x.registers.peek_pc(), 0x202);\n }\n\n #[test]\n fn jp_v0addr_test() {\n let mut x = Chip8Computer::new();\n /// jump to I + nnn\n x.registers.poke(0x0, 0xff);\n Chip8CpuInstructions::JpV0Addr(0x100).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x1FF);\n }\n\n #[test]\n fn cls_test() {\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::CLS.execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n }\n\n #[test]\n fn skip_next_instruction_ne_text() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xf0);\n Chip8CpuInstructions::SneVxByte(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::SneVxByte(0x1, 0xf0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n }\n\n #[test]\n fn addivx_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke_i(0xabc);\n x.registers.poke(0x0, 0x10);\n Chip8CpuInstructions::AddIVx(0x0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_i(), 0xacc);\n }\n\n #[test]\n fn ldstvt_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xf0);\n Chip8CpuInstructions::LdStVx(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]\n fn rnd_vx_byte_text() {\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::RndVxByte(0x1, 0x0f).execute(\u0026mut x);\n let new_value = x.registers.peek(0x1);\n assert!(new_value \u003c 0x10);\n }\n\n #[test]\n fn 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::AddVxByte(0x1, 0x10).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0xbb);\n }\n\n #[test]\n fn 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::SubVxVy(0x1, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0xf), 0);\n assert_eq!(x.registers.peek(0x1), 0x8);\n assert_eq!(x.registers.peek_pc(), 0x202);\n }\n\n #[test]\n fn sne_vx_vy_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0x10);\n x.registers.poke(0x2, 0x10);\n Chip8CpuInstructions::SneVxVy(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::SneVxVy(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204)\n }\n\n #[test]\n fn ld_dt_vx_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0x10);\n Chip8CpuInstructions::LdDtVx(0x1).execute(\u0026mut x);\n assert_eq!(x.delay_timer.current(), 0x10);\n for i in 0..0x20 {\n x.delay_timer.tick();\n }\n assert_eq!(x.delay_timer.current(), 0);\n }\n\n #[test]\n fn ld_vx_dt_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xf0);\n Chip8CpuInstructions::LdDtVx(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]\n fn 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::SubnVxVy(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), 0x0);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0xab);\n x.registers.poke(0x02, 0xa0);\n Chip8CpuInstructions::SubnVxVy(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), 0x1);\n }\n\n #[test]\n fn shl_vx_vy_test_1() {\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::ShlVxVy(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::ShlVxVy(0x1, 0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x1), 0b01010100);\n assert_eq!(x.registers.peek(0xf), 0x1);\n }\n\n #[test]\n fn ld_f_vx_test() {\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::LdFVx(0x1).execute(\u0026mut x);\n\n assert_eq!(x.registers.peek_i(), 0x5);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x06);\n Chip8CpuInstructions::LdFVx(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_i(), 25);\n }\n\n #[test]\n fn ld_b_vx_test() {\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::LdBVx(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\n #[test]\n fn ld_i_vx_test() {\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::LdIVx(to_load.len() as u16).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\n #[test]\n fn ld_vx_i_test() {\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::LdVxI(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 }\n\n #[test]\n fn Sknkpvx_test() {\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::SnkpVx(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n x.keypad.release_key(0x5);\n Chip8CpuInstructions::SnkpVx(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x206);\n }\n\n #[test]\n fn skpvx_test() {\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::SkpVx(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204);\n\n x.keypad.release_key(0x5);\n Chip8CpuInstructions::SkpVx(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x206);\n }\n\n #[test]\n fn draw_nibble_vx_vy_n_test() {\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::DrawVxVyNibble(x_register as u16, y_register as u16, 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 = (x_offset\n 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 println!(\"ROWDATA = \\t\\t[{row_data:08b}]\\tBIT IN BYTE = \\t[{bit_in_byte}]\\tONE_SHIFT_SET = [{one_shift_set}]\\tSHIFTED ONE = [{shifted_one:08b}]\");\n println!(\"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]\n fn sub_test() {\n // 2nnn\n // Call a subroutine at 2nnn\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::CallAddr(0x124).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x124);\n assert_eq!(x.stack.depth(), 1);\n Chip8CpuInstructions::CallAddr(0x564).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x564);\n assert_eq!(x.stack.depth(), 2);\n }\n\n #[test]\n fn ret_test() {\n // Return from a subroutine.\n let mut x = Chip8Computer::new();\n x.stack.push(\u00260x132);\n x.stack.push(\u00260xabc);\n Chip8CpuInstructions::RET.execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0xabc);\n assert_eq!(x.stack.depth(), 1);\n Chip8CpuInstructions::RET.execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x132);\n assert_eq!(x.stack.depth(), 0);\n }\n\n\n #[test]\n fn ldvxk_test() {\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::LdVxByte(0x1, 0x1).execute(\u0026mut x);\n Chip8CpuInstructions::LdVxK(0x1).execute(\u0026mut x);\n assert!(matches!(x.state, WaitingForKey));\n }\n}\n","traces":[{"line":122,"address":[19714432],"length":1,"stats":{"Line":1}},{"line":123,"address":[1798375],"length":1,"stats":{"Line":1}},{"line":124,"address":[1798420],"length":1,"stats":{"Line":1}},{"line":125,"address":[1798432],"length":1,"stats":{"Line":1}},{"line":128,"address":[1798466],"length":1,"stats":{"Line":1}},{"line":131,"address":[1798481],"length":1,"stats":{"Line":1}},{"line":133,"address":[1798504],"length":1,"stats":{"Line":1}},{"line":134,"address":[1798516],"length":1,"stats":{"Line":1}},{"line":136,"address":[1798558],"length":1,"stats":{"Line":1}},{"line":137,"address":[1798570],"length":1,"stats":{"Line":1}},{"line":139,"address":[1798612],"length":1,"stats":{"Line":1}},{"line":140,"address":[1798647],"length":1,"stats":{"Line":1}},{"line":142,"address":[19714792],"length":1,"stats":{"Line":1}},{"line":143,"address":[1798747],"length":1,"stats":{"Line":1}},{"line":145,"address":[1798812],"length":1,"stats":{"Line":1}},{"line":146,"address":[19714927],"length":1,"stats":{"Line":1}},{"line":148,"address":[1798936],"length":1,"stats":{"Line":1}},{"line":149,"address":[19715051],"length":1,"stats":{"Line":1}},{"line":151,"address":[1799036],"length":1,"stats":{"Line":1}},{"line":152,"address":[1799071],"length":1,"stats":{"Line":1}},{"line":154,"address":[1799136],"length":1,"stats":{"Line":1}},{"line":155,"address":[1799171],"length":1,"stats":{"Line":1}},{"line":157,"address":[1799260],"length":1,"stats":{"Line":1}},{"line":158,"address":[1799295],"length":1,"stats":{"Line":1}},{"line":160,"address":[1799384],"length":1,"stats":{"Line":1}},{"line":161,"address":[1799419],"length":1,"stats":{"Line":1}},{"line":163,"address":[1799508],"length":1,"stats":{"Line":1}},{"line":164,"address":[1799543],"length":1,"stats":{"Line":1}},{"line":166,"address":[1799632],"length":1,"stats":{"Line":1}},{"line":167,"address":[1799664],"length":1,"stats":{"Line":1}},{"line":169,"address":[1799744],"length":1,"stats":{"Line":1}},{"line":170,"address":[1799776],"length":1,"stats":{"Line":1}},{"line":172,"address":[1799856],"length":1,"stats":{"Line":1}},{"line":173,"address":[19715968],"length":1,"stats":{"Line":1}},{"line":175,"address":[1799968],"length":1,"stats":{"Line":1}},{"line":176,"address":[1800000],"length":1,"stats":{"Line":1}},{"line":178,"address":[1800080],"length":1,"stats":{"Line":1}},{"line":179,"address":[1800112],"length":1,"stats":{"Line":1}},{"line":181,"address":[1800192],"length":1,"stats":{"Line":1}},{"line":182,"address":[1800224],"length":1,"stats":{"Line":1}},{"line":184,"address":[1800304],"length":1,"stats":{"Line":1}},{"line":185,"address":[1800316],"length":1,"stats":{"Line":1}},{"line":187,"address":[1800354],"length":1,"stats":{"Line":1}},{"line":188,"address":[1800366],"length":1,"stats":{"Line":1}},{"line":190,"address":[1800404],"length":1,"stats":{"Line":1}},{"line":191,"address":[19716516],"length":1,"stats":{"Line":1}},{"line":193,"address":[1800498],"length":1,"stats":{"Line":1}},{"line":194,"address":[1800549],"length":1,"stats":{"Line":1}},{"line":196,"address":[1800648],"length":1,"stats":{"Line":1}},{"line":197,"address":[1800660],"length":1,"stats":{"Line":1}},{"line":199,"address":[1800702],"length":1,"stats":{"Line":1}},{"line":200,"address":[19716794],"length":1,"stats":{"Line":1}},{"line":202,"address":[1800756],"length":1,"stats":{"Line":1}},{"line":203,"address":[19716848],"length":1,"stats":{"Line":1}},{"line":205,"address":[1800810],"length":1,"stats":{"Line":1}},{"line":206,"address":[1800822],"length":1,"stats":{"Line":1}},{"line":208,"address":[1800864],"length":1,"stats":{"Line":1}},{"line":209,"address":[1800876],"length":1,"stats":{"Line":1}},{"line":211,"address":[1800918],"length":1,"stats":{"Line":1}},{"line":212,"address":[1800930],"length":1,"stats":{"Line":1}},{"line":214,"address":[1800972],"length":1,"stats":{"Line":1}},{"line":215,"address":[19717064],"length":1,"stats":{"Line":1}},{"line":217,"address":[1801026],"length":1,"stats":{"Line":1}},{"line":218,"address":[1801038],"length":1,"stats":{"Line":1}},{"line":220,"address":[1801080],"length":1,"stats":{"Line":1}},{"line":221,"address":[1801092],"length":1,"stats":{"Line":1}},{"line":223,"address":[1801131],"length":1,"stats":{"Line":1}},{"line":224,"address":[1801143],"length":1,"stats":{"Line":1}},{"line":226,"address":[1801182],"length":1,"stats":{"Line":1}},{"line":227,"address":[1801194],"length":1,"stats":{"Line":1}},{"line":230,"address":[1801225],"length":1,"stats":{"Line":1}},{"line":234,"address":[1801264],"length":1,"stats":{"Line":1}},{"line":235,"address":[1801288],"length":1,"stats":{"Line":1}},{"line":236,"address":[1801310],"length":1,"stats":{"Line":1}},{"line":237,"address":[1801332],"length":1,"stats":{"Line":1}},{"line":238,"address":[19717446],"length":1,"stats":{"Line":1}},{"line":239,"address":[1801376],"length":1,"stats":{"Line":1}},{"line":240,"address":[19717498],"length":1,"stats":{"Line":1}},{"line":241,"address":[1801428],"length":1,"stats":{"Line":1}},{"line":243,"address":[1801441],"length":1,"stats":{"Line":1}},{"line":247,"address":[1801484],"length":1,"stats":{"Line":1}},{"line":252,"address":[1801493],"length":1,"stats":{"Line":1}},{"line":254,"address":[1801531,1801475],"length":1,"stats":{"Line":2}},{"line":257,"address":[1801542],"length":1,"stats":{"Line":1}},{"line":259,"address":[1801515,1801577],"length":1,"stats":{"Line":2}},{"line":262,"address":[1801588],"length":1,"stats":{"Line":1}},{"line":264,"address":[1801561,1801623],"length":1,"stats":{"Line":2}},{"line":267,"address":[19717738],"length":1,"stats":{"Line":1}},{"line":269,"address":[1801672,1801607],"length":1,"stats":{"Line":2}},{"line":272,"address":[1801688],"length":1,"stats":{"Line":1}},{"line":274,"address":[1801656,1801731],"length":1,"stats":{"Line":2}},{"line":277,"address":[1801747],"length":1,"stats":{"Line":1}},{"line":279,"address":[1801715,1801790],"length":1,"stats":{"Line":2}},{"line":283,"address":[1801801,1803180,1803192],"length":1,"stats":{"Line":3}},{"line":285,"address":[1803185],"length":1,"stats":{"Line":1}},{"line":287,"address":[1803163],"length":1,"stats":{"Line":1}},{"line":290,"address":[1801774,1801841],"length":1,"stats":{"Line":2}},{"line":293,"address":[1801857],"length":1,"stats":{"Line":1}},{"line":295,"address":[1801900,1801825],"length":1,"stats":{"Line":2}},{"line":297,"address":[19718020],"length":1,"stats":{"Line":1}},{"line":299,"address":[19717988,19718063],"length":1,"stats":{"Line":2}},{"line":301,"address":[1801970],"length":1,"stats":{"Line":1}},{"line":302,"address":[19718092],"length":1,"stats":{"Line":1}},{"line":305,"address":[19718979],"length":1,"stats":{"Line":1}},{"line":309,"address":[19719011],"length":1,"stats":{"Line":1}},{"line":313,"address":[1802939],"length":1,"stats":{"Line":1}},{"line":317,"address":[1802971],"length":1,"stats":{"Line":1}},{"line":321,"address":[1803003],"length":1,"stats":{"Line":1}},{"line":325,"address":[19719139],"length":1,"stats":{"Line":1}},{"line":329,"address":[19719171],"length":1,"stats":{"Line":1}},{"line":333,"address":[19719203],"length":1,"stats":{"Line":1}},{"line":337,"address":[1803131],"length":1,"stats":{"Line":1}},{"line":340,"address":[1802853],"length":1,"stats":{"Line":1}},{"line":344,"address":[1802039,1801943],"length":1,"stats":{"Line":2}},{"line":346,"address":[1802050,1802836,1802848],"length":1,"stats":{"Line":3}},{"line":347,"address":[19718945],"length":1,"stats":{"Line":1}},{"line":349,"address":[1802819],"length":1,"stats":{"Line":1}},{"line":352,"address":[19718127,19718194],"length":1,"stats":{"Line":2}},{"line":354,"address":[1802101],"length":1,"stats":{"Line":1}},{"line":356,"address":[1802074,1802139],"length":1,"stats":{"Line":2}},{"line":358,"address":[1802150],"length":1,"stats":{"Line":1}},{"line":360,"address":[1802123,1802188],"length":1,"stats":{"Line":2}},{"line":362,"address":[1802204],"length":1,"stats":{"Line":1}},{"line":364,"address":[1802172,1802247],"length":1,"stats":{"Line":2}},{"line":366,"address":[1802268],"length":1,"stats":{"Line":1}},{"line":368,"address":[1802231,1802316],"length":1,"stats":{"Line":2}},{"line":369,"address":[1802327],"length":1,"stats":{"Line":1}},{"line":371,"address":[1802770],"length":1,"stats":{"Line":1}},{"line":374,"address":[1802792],"length":1,"stats":{"Line":1}},{"line":377,"address":[19718857],"length":1,"stats":{"Line":1}},{"line":381,"address":[1802384,1802300],"length":1,"stats":{"Line":2}},{"line":382,"address":[1802395],"length":1,"stats":{"Line":1}},{"line":384,"address":[1802560],"length":1,"stats":{"Line":1}},{"line":387,"address":[1802582],"length":1,"stats":{"Line":1}},{"line":390,"address":[1802604],"length":1,"stats":{"Line":1}},{"line":393,"address":[1802626],"length":1,"stats":{"Line":1}},{"line":396,"address":[1802648],"length":1,"stats":{"Line":1}},{"line":399,"address":[1802670],"length":1,"stats":{"Line":1}},{"line":402,"address":[1802692],"length":1,"stats":{"Line":1}},{"line":405,"address":[1802714],"length":1,"stats":{"Line":1}},{"line":408,"address":[19718840],"length":1,"stats":{"Line":1}},{"line":411,"address":[1802543],"length":1,"stats":{"Line":1}},{"line":416,"address":[1802367],"length":1,"stats":{"Line":1}},{"line":421,"address":[1823024,1803200],"length":1,"stats":{"Line":4}},{"line":422,"address":[19719476],"length":1,"stats":{"Line":4}},{"line":423,"address":[19719610,19719505],"length":1,"stats":{"Line":4}},{"line":424,"address":[1803458,1803509],"length":1,"stats":{"Line":5}},{"line":426,"address":[1803519],"length":1,"stats":{"Line":1}},{"line":427,"address":[19724299,19719648,19724445,19724709],"length":1,"stats":{"Line":2}},{"line":429,"address":[1808077],"length":1,"stats":{"Line":1}},{"line":433,"address":[1808739,1803577],"length":1,"stats":{"Line":2}},{"line":434,"address":[19725025],"length":1,"stats":{"Line":1}},{"line":439,"address":[19719737],"length":1,"stats":{"Line":1}},{"line":440,"address":[1809090,1803633,1808944,1809354],"length":1,"stats":{"Line":2}},{"line":441,"address":[1808891],"length":1,"stats":{"Line":1}},{"line":444,"address":[19719805],"length":1,"stats":{"Line":1}},{"line":445,"address":[19719817,19725779,19725925,19726189],"length":1,"stats":{"Line":2}},{"line":446,"address":[1809551],"length":1,"stats":{"Line":1}},{"line":449,"address":[1803753],"length":1,"stats":{"Line":1}},{"line":450,"address":[1810321,1810734,1810470,1803765],"length":1,"stats":{"Line":2}},{"line":451,"address":[1810216],"length":1,"stats":{"Line":1}},{"line":452,"address":[19726417],"length":1,"stats":{"Line":1}},{"line":453,"address":[1810275],"length":1,"stats":{"Line":1}},{"line":456,"address":[19719945],"length":1,"stats":{"Line":1}},{"line":457,"address":[19719972],"length":1,"stats":{"Line":1}},{"line":458,"address":[1810973],"length":1,"stats":{"Line":1}},{"line":460,"address":[1810923,1811274,1811802,1811570,1811015],"length":1,"stats":{"Line":2}},{"line":463,"address":[19720039],"length":1,"stats":{"Line":1}},{"line":464,"address":[19720066],"length":1,"stats":{"Line":1}},{"line":465,"address":[1812073],"length":1,"stats":{"Line":1}},{"line":467,"address":[1812115,1812670,1812374,1812902,1812023],"length":1,"stats":{"Line":2}},{"line":470,"address":[1804013],"length":1,"stats":{"Line":1}},{"line":471,"address":[19720160],"length":1,"stats":{"Line":1}},{"line":472,"address":[19720209],"length":1,"stats":{"Line":1}},{"line":474,"address":[19720252],"length":1,"stats":{"Line":1}},{"line":475,"address":[19729348],"length":1,"stats":{"Line":1}},{"line":477,"address":[1813121,1813768,1813472,1813213,1814000],"length":1,"stats":{"Line":2}},{"line":480,"address":[1804159],"length":1,"stats":{"Line":1}},{"line":481,"address":[19720308],"length":1,"stats":{"Line":1}},{"line":482,"address":[1804204],"length":1,"stats":{"Line":1}},{"line":483,"address":[19730982,19730427,19730686,19720356,19731226],"length":1,"stats":{"Line":2}},{"line":486,"address":[19720418],"length":1,"stats":{"Line":1}},{"line":487,"address":[19720445],"length":1,"stats":{"Line":1}},{"line":488,"address":[19720483,19731463,19731547],"length":1,"stats":{"Line":2}},{"line":489,"address":[1815292],"length":1,"stats":{"Line":1}},{"line":490,"address":[19731960,19731591,19731505,19732288,19732520,19732752],"length":1,"stats":{"Line":2}},{"line":493,"address":[1804410],"length":1,"stats":{"Line":1}},{"line":494,"address":[19720561],"length":1,"stats":{"Line":1}},{"line":495,"address":[19733584,19733288,19720650,19733029,19733816],"length":1,"stats":{"Line":2}},{"line":498,"address":[1804586],"length":1,"stats":{"Line":1}},{"line":500,"address":[19720739],"length":1,"stats":{"Line":1}},{"line":501,"address":[19720793],"length":1,"stats":{"Line":1}},{"line":503,"address":[19720849],"length":1,"stats":{"Line":1}},{"line":505,"address":[1804732],"length":1,"stats":{"Line":1}},{"line":506,"address":[19720867],"length":1,"stats":{"Line":1}},{"line":507,"address":[1818673,1818145,1804768,1817886,1818441],"length":1,"stats":{"Line":2}},{"line":510,"address":[1804830],"length":1,"stats":{"Line":1}},{"line":511,"address":[1804857],"length":1,"stats":{"Line":1}},{"line":512,"address":[19721040],"length":1,"stats":{"Line":1}},{"line":513,"address":[19721096],"length":1,"stats":{"Line":1}},{"line":515,"address":[19721107],"length":1,"stats":{"Line":1}},{"line":516,"address":[1819291,1805015,1818922,1819619,1819851,1820083],"length":1,"stats":{"Line":2}},{"line":519,"address":[1805077],"length":1,"stats":{"Line":1}},{"line":520,"address":[1805107],"length":1,"stats":{"Line":1}},{"line":521,"address":[19721296],"length":1,"stats":{"Line":1}},{"line":522,"address":[1805219],"length":1,"stats":{"Line":1}},{"line":523,"address":[19721364],"length":1,"stats":{"Line":1}},{"line":526,"address":[19721408],"length":1,"stats":{"Line":1}},{"line":527,"address":[19721446],"length":1,"stats":{"Line":1}},{"line":528,"address":[19721503],"length":1,"stats":{"Line":1}},{"line":529,"address":[19736535,19721544,19736519],"length":1,"stats":{"Line":2}},{"line":530,"address":[1820348],"length":1,"stats":{"Line":1}},{"line":531,"address":[1820430],"length":1,"stats":{"Line":1}},{"line":533,"address":[1820396],"length":1,"stats":{"Line":1}},{"line":535,"address":[1805450],"length":1,"stats":{"Line":1}},{"line":540,"address":[19721625],"length":1,"stats":{"Line":1}},{"line":541,"address":[19721674],"length":1,"stats":{"Line":1}},{"line":543,"address":[1805569],"length":1,"stats":{"Line":1}},{"line":544,"address":[1820599,1820483,1820567],"length":1,"stats":{"Line":0}},{"line":545,"address":[19736757],"length":1,"stats":{"Line":0}},{"line":547,"address":[1820462],"length":1,"stats":{"Line":1}},{"line":549,"address":[1820519,1820639,1820672],"length":1,"stats":{"Line":2}},{"line":550,"address":[19736829],"length":1,"stats":{"Line":1}},{"line":552,"address":[1805606],"length":1,"stats":{"Line":1}},{"line":557,"address":[1805629],"length":1,"stats":{"Line":1}},{"line":558,"address":[19721802],"length":1,"stats":{"Line":1}},{"line":559,"address":[19736880],"length":1,"stats":{"Line":1}},{"line":561,"address":[19736926],"length":1,"stats":{"Line":1}},{"line":563,"address":[19721833],"length":1,"stats":{"Line":1}},{"line":568,"address":[19721871],"length":1,"stats":{"Line":1}},{"line":569,"address":[1805782],"length":1,"stats":{"Line":1}},{"line":570,"address":[19721966,19736996],"length":1,"stats":{"Line":2}},{"line":571,"address":[19737112,19737028,19737282],"length":1,"stats":{"Line":3}},{"line":572,"address":[1820884,1821039,1821099],"length":1,"stats":{"Line":2}},{"line":574,"address":[19737105,19737114,19737046],"length":1,"stats":{"Line":2}},{"line":576,"address":[19737138],"length":1,"stats":{"Line":1}},{"line":577,"address":[1820993],"length":1,"stats":{"Line":1}},{"line":580,"address":[1805853],"length":1,"stats":{"Line":1}},{"line":585,"address":[19722018],"length":1,"stats":{"Line":1}},{"line":586,"address":[19722052],"length":1,"stats":{"Line":1}},{"line":587,"address":[1821123],"length":1,"stats":{"Line":1}},{"line":589,"address":[1821168],"length":1,"stats":{"Line":1}},{"line":591,"address":[1805940],"length":1,"stats":{"Line":1}},{"line":597,"address":[19722113],"length":1,"stats":{"Line":1}},{"line":598,"address":[1806021],"length":1,"stats":{"Line":1}},{"line":599,"address":[19722201],"length":1,"stats":{"Line":1}},{"line":600,"address":[19737429],"length":1,"stats":{"Line":1}},{"line":603,"address":[19722222],"length":1,"stats":{"Line":1}},{"line":608,"address":[19737922,19722234,19737512,19737658],"length":1,"stats":{"Line":2}},{"line":609,"address":[1821264],"length":1,"stats":{"Line":1}},{"line":612,"address":[19722296],"length":1,"stats":{"Line":1}},{"line":617,"address":[1806171],"length":1,"stats":{"Line":1}},{"line":619,"address":[1806257],"length":1,"stats":{"Line":1}},{"line":626,"address":[1806284],"length":1,"stats":{"Line":1}},{"line":627,"address":[1806312],"length":1,"stats":{"Line":1}},{"line":629,"address":[1806361],"length":1,"stats":{"Line":1}},{"line":642,"address":[19722560],"length":1,"stats":{"Line":1}},{"line":644,"address":[19722600],"length":1,"stats":{"Line":1}},{"line":645,"address":[1806494],"length":1,"stats":{"Line":1}},{"line":647,"address":[19722892],"length":1,"stats":{"Line":1}},{"line":648,"address":[19722999,19738121,19738744],"length":1,"stats":{"Line":2}},{"line":650,"address":[19738399],"length":1,"stats":{"Line":1}},{"line":651,"address":[19738514],"length":1,"stats":{"Line":1}},{"line":652,"address":[19738635],"length":1,"stats":{"Line":1}},{"line":653,"address":[1822790,1822568,1822518],"length":1,"stats":{"Line":3}},{"line":654,"address":[19739245,19738999,19740559],"length":1,"stats":{"Line":2}},{"line":655,"address":[19739395,19739489,19739583,19739269,19739911,19740143,19740375],"length":1,"stats":{"Line":7}},{"line":656,"address":[1824380,1824335],"length":1,"stats":{"Line":2}},{"line":657,"address":[1824638,1824450],"length":1,"stats":{"Line":1}},{"line":658,"address":[1824622,1824658,1824740],"length":1,"stats":{"Line":2}},{"line":659,"address":[19740901],"length":1,"stats":{"Line":1}},{"line":663,"address":[1822624,1822865],"length":1,"stats":{"Line":1}},{"line":665,"address":[19719452],"length":1,"stats":{"Line":4}},{"line":670,"address":[1823000],"length":1,"stats":{"Line":1}},{"line":673,"address":[1806902],"length":1,"stats":{"Line":1}},{"line":678,"address":[19723069],"length":1,"stats":{"Line":1}},{"line":679,"address":[1806951],"length":1,"stats":{"Line":1}},{"line":680,"address":[1806975],"length":1,"stats":{"Line":1}},{"line":681,"address":[1824764],"length":1,"stats":{"Line":1}},{"line":684,"address":[1807004],"length":1,"stats":{"Line":1}},{"line":692,"address":[19723170],"length":1,"stats":{"Line":1}},{"line":693,"address":[1807057],"length":1,"stats":{"Line":1}},{"line":694,"address":[19741029,19723241,19741175,19741439],"length":1,"stats":{"Line":2}},{"line":695,"address":[1824788],"length":1,"stats":{"Line":1}},{"line":696,"address":[19741638],"length":1,"stats":{"Line":1}},{"line":699,"address":[1807147],"length":1,"stats":{"Line":0}},{"line":704,"address":[1807159],"length":1,"stats":{"Line":0}},{"line":705,"address":[1807194],"length":1,"stats":{"Line":0}},{"line":707,"address":[1807237],"length":1,"stats":{"Line":1}},{"line":712,"address":[1807249],"length":1,"stats":{"Line":1}},{"line":714,"address":[19723449],"length":1,"stats":{"Line":1}},{"line":719,"address":[19723464],"length":1,"stats":{"Line":1}},{"line":720,"address":[1807340],"length":1,"stats":{"Line":1}},{"line":722,"address":[1807376],"length":1,"stats":{"Line":1}},{"line":723,"address":[1807391],"length":1,"stats":{"Line":1}},{"line":724,"address":[1807425],"length":1,"stats":{"Line":1}},{"line":726,"address":[19723623],"length":1,"stats":{"Line":1}},{"line":731,"address":[19723635],"length":1,"stats":{"Line":1}},{"line":732,"address":[19723683],"length":1,"stats":{"Line":1}},{"line":733,"address":[1807564,1825471],"length":1,"stats":{"Line":2}},{"line":735,"address":[1807625],"length":1,"stats":{"Line":1}},{"line":743,"address":[19741709,19741740,19723804],"length":1,"stats":{"Line":2}},{"line":744,"address":[19741798,19741716,19741769],"length":1,"stats":{"Line":2}},{"line":745,"address":[19741777],"length":1,"stats":{"Line":1}},{"line":747,"address":[19723865],"length":1,"stats":{"Line":1}},{"line":755,"address":[19723880],"length":1,"stats":{"Line":1}},{"line":758,"address":[1807748],"length":1,"stats":{"Line":1}},{"line":760,"address":[19742042,19723945,19741828],"length":1,"stats":{"Line":2}},{"line":762,"address":[1825685],"length":1,"stats":{"Line":1}},{"line":765,"address":[1825718],"length":1,"stats":{"Line":1}},{"line":768,"address":[19741942],"length":1,"stats":{"Line":1}},{"line":769,"address":[1825782],"length":1,"stats":{"Line":1}},{"line":770,"address":[1825810,1825918,1825867],"length":1,"stats":{"Line":2}},{"line":771,"address":[1825948,1825891],"length":1,"stats":{"Line":2}},{"line":773,"address":[1807846],"length":1,"stats":{"Line":1}},{"line":778,"address":[1807858],"length":1,"stats":{"Line":1}},{"line":779,"address":[1825980,1826086,1807897],"length":1,"stats":{"Line":3}},{"line":780,"address":[1826107,1826185],"length":1,"stats":{"Line":2}},{"line":782,"address":[1826143,1826040],"length":1,"stats":{"Line":2}},{"line":784,"address":[1807946],"length":1,"stats":{"Line":1}},{"line":788,"address":[1807966],"length":1,"stats":{"Line":1}},{"line":789,"address":[1826674,1807986,1826938,1826525],"length":1,"stats":{"Line":2}},{"line":790,"address":[19742460],"length":1,"stats":{"Line":1}},{"line":791,"address":[1826385],"length":1,"stats":{"Line":1}},{"line":792,"address":[1826460,1827224,1827127],"length":1,"stats":{"Line":3}},{"line":793,"address":[1827340,1827240,1828684],"length":1,"stats":{"Line":2}},{"line":794,"address":[1827371],"length":1,"stats":{"Line":1}},{"line":795,"address":[1828524,1827732,1828060,1828292],"length":1,"stats":{"Line":4}},{"line":797,"address":[1827179,1827306],"length":1,"stats":{"Line":2}},{"line":801,"address":[1808048],"length":1,"stats":{"Line":1}}],"covered":325,"coverable":330},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","keypad.rs"],"content":"use imgui::Key;\n\n\n#[derive(Clone, Copy)]\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 let keys = [\n [0x1, 0x2, 0x3, 0xc],[0x4, 0x5, 0x6, 0xd],[0x7, 0x8, 0x9, 0xe],[0xa, 0x0, 0xb, 0xf]\n ];\n\n for row in keys.iter() {\n for (index, key) in row.iter().enumerate() {\n let is_lit = if self.keys[*key] { \"*\".to_string() } else { char::from_digit(*key as u32, 16).unwrap_or(' ').to_string() };\n match index {\n 3 =\u003e {\n // last in col\n return_value += format!(\"|{}|\\n\", is_lit).as_str();\n }\n _=\u003e {\n return_value += format!(\"|{}\", is_lit).as_str();\n }\n }\n }\n }\n\n return_value\n }\n}\n\nimpl Default for Keypad {\n fn default() -\u003e Self {\n Keypad {\n keys: [ false; 16]\n }\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\n#[cfg(test)]\nmod test {\n use super::*;\n\n #[test]\n fn smoke() { assert!(true) }\n\n #[test]\n fn 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}","traces":[{"line":10,"address":[19767608,19767854,19765968],"length":1,"stats":{"Line":0}},{"line":11,"address":[1791903],"length":1,"stats":{"Line":0}},{"line":13,"address":[1792100],"length":1,"stats":{"Line":0}},{"line":14,"address":[1791908],"length":1,"stats":{"Line":0}},{"line":17,"address":[1792488,1792241,1792324],"length":1,"stats":{"Line":0}},{"line":18,"address":[1792504],"length":1,"stats":{"Line":0}},{"line":19,"address":[1792764,1792952],"length":1,"stats":{"Line":0}},{"line":20,"address":[1792944],"length":1,"stats":{"Line":0}},{"line":23,"address":[1793228,1793357],"length":1,"stats":{"Line":0}},{"line":26,"address":[1793622,1793493],"length":1,"stats":{"Line":0}},{"line":32,"address":[1792445],"length":1,"stats":{"Line":0}},{"line":37,"address":[1793744],"length":1,"stats":{"Line":4}},{"line":39,"address":[19767886],"length":1,"stats":{"Line":4}},{"line":45,"address":[1793808],"length":1,"stats":{"Line":1}},{"line":46,"address":[1793867,1793829],"length":1,"stats":{"Line":1}},{"line":49,"address":[1793888],"length":1,"stats":{"Line":1}},{"line":50,"address":[1793947,1793909],"length":1,"stats":{"Line":1}},{"line":53,"address":[1793968],"length":1,"stats":{"Line":1}},{"line":54,"address":[19768117,19768159],"length":1,"stats":{"Line":1}},{"line":57,"address":[1794064],"length":1,"stats":{"Line":1}},{"line":58,"address":[1794072],"length":1,"stats":{"Line":1}},{"line":61,"address":[1794096],"length":1,"stats":{"Line":1}},{"line":62,"address":[1794112],"length":1,"stats":{"Line":1}},{"line":64,"address":[1794144],"length":1,"stats":{"Line":1}},{"line":65,"address":[1794160],"length":1,"stats":{"Line":1}}],"covered":14,"coverable":25},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","registers.rs"],"content":"use log::debug;\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)]\npub struct Chip8Registers {\n registers: [u8; 16],\n i_register: u16,\n pc: u16,\n sp: u16,\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 sp: 0x100,\n }\n }\n}\n\nimpl Chip8Registers {\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\n#[cfg(test)]\nmod test {\n use crate::chip8::registers::Chip8Registers;\n\n #[test]\n fn smoke() { assert!(true) }\n\n #[test]\n fn 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]\n fn 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]\n fn invalid_register() {\n let mut x = Chip8Registers::default();\n x.poke(0x10, 0xff);\n }\n\n #[test]\n fn 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}","traces":[{"line":14,"address":[1780368],"length":1,"stats":{"Line":1}},{"line":15,"address":[1780419,1780381],"length":1,"stats":{"Line":1}},{"line":20,"address":[1780448],"length":1,"stats":{"Line":4}},{"line":22,"address":[1780462],"length":1,"stats":{"Line":4}},{"line":31,"address":[1780544],"length":1,"stats":{"Line":4}},{"line":32,"address":[1780557],"length":1,"stats":{"Line":4}},{"line":34,"address":[1780576],"length":1,"stats":{"Line":2}},{"line":35,"address":[1780581],"length":1,"stats":{"Line":2}},{"line":38,"address":[1780592],"length":1,"stats":{"Line":1}},{"line":39,"address":[1780605],"length":1,"stats":{"Line":1}},{"line":41,"address":[1780624],"length":1,"stats":{"Line":3}},{"line":42,"address":[1780682,1780645],"length":1,"stats":{"Line":3}},{"line":45,"address":[1780704],"length":1,"stats":{"Line":2}},{"line":46,"address":[1780739],"length":1,"stats":{"Line":2}},{"line":47,"address":[1780763],"length":1,"stats":{"Line":1}},{"line":49,"address":[1780747,1780824,1780837],"length":1,"stats":{"Line":8}},{"line":52,"address":[1780864],"length":1,"stats":{"Line":4}},{"line":53,"address":[1780869],"length":1,"stats":{"Line":4}},{"line":56,"address":[1780880],"length":1,"stats":{"Line":1}},{"line":57,"address":[1780893],"length":1,"stats":{"Line":1}},{"line":60,"address":[1780912],"length":1,"stats":{"Line":1}},{"line":61,"address":[1781782,1787356,1781503,1784804,1783412,1782061,1785732,1782616,1781131,1782433,1786660,1787124,1783876,1785036,1781038,1781596,1786196,1786892,1785964,1785268,1782154,1782340,1781224,1781689,1785500,1784108,1784572,1784340,1782247,1781317,1781875,1783644,1786428,1782526,1781968,1781410],"length":1,"stats":{"Line":36}}],"covered":22,"coverable":22},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","sound_timer.rs"],"content":"use std::{thread, time};\nuse beep::beep;\nuse log::trace;\n\n#[derive(Clone, Copy)]\npub struct SoundTimer {\n counter: i32\n}\n\nimpl SoundTimer {\n pub fn current(\u0026self) -\u003e i32 {\n self.counter\n }\n pub fn new() -\u003e Self {\n SoundTimer {\n counter: 0\n }\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!(\"TICKING SOUND FROM {} to {}\", self.counter, self.counter - 1);\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\n#[cfg(test)]\nmod test {\n use super::*;\n\n #[test]\n fn smoke() {\n assert!(true)\n }\n\n #[test]\n fn 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]\n fn 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}","traces":[{"line":11,"address":[1755072],"length":1,"stats":{"Line":1}},{"line":12,"address":[1755077],"length":1,"stats":{"Line":1}},{"line":19,"address":[1755104],"length":1,"stats":{"Line":1}},{"line":20,"address":[1755206,1755328,1755128],"length":1,"stats":{"Line":2}},{"line":21,"address":[1755164],"length":1,"stats":{"Line":1}},{"line":24,"address":[1755472],"length":1,"stats":{"Line":1}},{"line":25,"address":[1755492,1755570,1755685,1755823],"length":1,"stats":{"Line":2}},{"line":26,"address":[1755528,1756052],"length":1,"stats":{"Line":2}},{"line":27,"address":[1756028,1756054],"length":1,"stats":{"Line":1}}],"covered":9,"coverable":9},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","stack.rs"],"content":"#[derive(Clone)]\npub struct Chip8Stack {\n items: Vec\u003cu16\u003e\n}\n\nimpl Default for Chip8Stack {\n fn default() -\u003e Self {\n Chip8Stack {\n items: vec![],\n }\n }\n}\n\nimpl Chip8Stack {\n pub fn push(\u0026mut self, new_value: \u0026u16) {\n if self.depth() == 16 {\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 {\n items: vec![]\n }\n }\n}\n\n#[cfg(test)]\nmod test {\n use rand::random;\n use super::*;\n\n #[test]\n fn smoke() { assert!(true) }\n\n #[test]\n fn 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]\n fn 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]\n fn stack_underflow_test() {\n let mut x = Chip8Stack::new();\n x.pop();\n }\n\n #[test]\n fn lots_of_subs() {\n let mut x = Chip8Stack::new();\n let stack_contents = [0x123, 0x321, 0xabc, 0xdef,\n 0xbad, 0xbef, 0xfed, 0xcab,\n 0xbed, 0xcad, 0xfeb, 0xcab,\n 0xfff, 0x000, 0x001];\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}","traces":[{"line":7,"address":[19710288],"length":1,"stats":{"Line":4}},{"line":9,"address":[1760845],"length":1,"stats":{"Line":4}},{"line":15,"address":[1760896],"length":1,"stats":{"Line":1}},{"line":16,"address":[1760920],"length":1,"stats":{"Line":1}},{"line":17,"address":[1760931],"length":1,"stats":{"Line":1}},{"line":19,"address":[1760986],"length":1,"stats":{"Line":1}},{"line":21,"address":[1761008],"length":1,"stats":{"Line":1}},{"line":22,"address":[1761022],"length":1,"stats":{"Line":1}},{"line":23,"address":[1761094],"length":1,"stats":{"Line":1}},{"line":25,"address":[1761037],"length":1,"stats":{"Line":1}},{"line":27,"address":[1761152],"length":1,"stats":{"Line":1}},{"line":28,"address":[1761157],"length":1,"stats":{"Line":1}},{"line":31,"address":[1761168],"length":1,"stats":{"Line":3}},{"line":33,"address":[1761181],"length":1,"stats":{"Line":1}}],"covered":14,"coverable":14},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","system_memory.rs"],"content":"use glium::RawUniformValue::Vec2;\nuse image::load;\nuse imgui::sys::ImColor;\nuse imgui::{ImColor32, Ui};\nuse log::{debug, trace};\nuse ratatui::{style::Style, widgets::Widget};\n\nuse crate::constants::{CHIP8_MEMORY_SIZE, CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};\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\n#[derive(Clone, Copy)]\npub struct Chip8SystemMemory {\n memory: [u8; CHIP8_MEMORY_SIZE as usize],\n}\n\nimpl Default for Chip8SystemMemory {\n fn default() -\u003e Self {\n\n let mut x = Chip8SystemMemory {\n memory: [0x00; CHIP8_MEMORY_SIZE as usize],\n };\n\n x.load_fonts_to_memory();\n x\n }\n}\n\nconst cell_width: i32 = 5i32;\nconst cell_height: i32 = 5i32;\n\nimpl Chip8SystemMemory {\n \n pub fn new() -\u003e Self {\n Chip8SystemMemory {\n memory: [0x00; CHIP8_MEMORY_SIZE as usize],\n }\n }\n\n pub fn peek(self, address: u16) -\u003e u8 {\n trace!(\"PEEK: {} / {}\", address, self.memory[address as usize].clone());\n self.memory[address as usize]\n }\n\n pub fn poke(\u0026mut self, address: u16, value: u8) {\n trace!(\"POKE: {} / {} to {}\", address, self.memory[address as usize], value);\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\n#[cfg(test)]\nmod test {\n use super::*;\n\n #[test]\n fn smoke() {\n assert!(true)\n }\n\n #[test]\n fn model_smoke() {\n let m = Chip8SystemMemory::default();\n for i in 0..5 {\n assert_eq!(m.peek(i), CHIP8FONT_0[i as usize]);\n }\n\n assert_eq!(m.peek((CHIP8_MEMORY_SIZE - 1) as u16), 0);\n }\n\n #[test]\n fn known_data_loaded_correctly() {\n let to_load = [ 0x01, 0x02, 0x03, 0x04, 0x05 , 0x06 ];\n let mut x = Chip8SystemMemory::default();\n\n for (index, value) in [1..10].iter().enumerate() {\n assert_ne!(x.peek(0), 0x01);\n x.poke(0, 0x01);\n assert_eq!(x.peek(0), 0x01);\n }\n }\n\n #[test]\n fn verify_load_program() {\n // first line of 1-chip-logo.ch8\n let program_to_load = [0x00e0, 0x6101, 0x6008, 0xa250, 0xd01f, 0x6010, 0xa25f, 0xd01f];\n\n let mut x = Chip8SystemMemory::new();\n\n \n }\n}\n","traces":[{"line":34,"address":[19747712],"length":1,"stats":{"Line":4}},{"line":37,"address":[1788140],"length":1,"stats":{"Line":4}},{"line":40,"address":[1788183],"length":1,"stats":{"Line":4}},{"line":41,"address":[1788198],"length":1,"stats":{"Line":4}},{"line":50,"address":[1788240],"length":1,"stats":{"Line":1}},{"line":52,"address":[1788269],"length":1,"stats":{"Line":1}},{"line":56,"address":[1788320],"length":1,"stats":{"Line":1}},{"line":57,"address":[1788340,1788554,1788704,1788426],"length":1,"stats":{"Line":2}},{"line":58,"address":[1788934,1788371,1788918],"length":1,"stats":{"Line":2}},{"line":61,"address":[1788960],"length":1,"stats":{"Line":4}},{"line":62,"address":[1788994,1789080,1789208,1789430],"length":1,"stats":{"Line":8}},{"line":63,"address":[1789680,1789700,1789025],"length":1,"stats":{"Line":8}},{"line":66,"address":[19749685,19749344],"length":1,"stats":{"Line":0}},{"line":67,"address":[1789935,1789817,1789750],"length":1,"stats":{"Line":0}},{"line":68,"address":[1789953],"length":1,"stats":{"Line":0}},{"line":72,"address":[1790096],"length":1,"stats":{"Line":4}},{"line":73,"address":[1790116],"length":1,"stats":{"Line":4}},{"line":92,"address":[1790623,1790457],"length":1,"stats":{"Line":8}},{"line":93,"address":[1790665],"length":1,"stats":{"Line":4}},{"line":94,"address":[1790919,1790829],"length":1,"stats":{"Line":4}},{"line":95,"address":[1790955,1790911],"length":1,"stats":{"Line":8}}],"covered":18,"coverable":21},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","util.rs"],"content":"pub struct InstructionUtil {}\n\nimpl InstructionUtil {\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 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 = return_value | new_bit\n }\n }\n return_value\n }\n\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 pub fn join_bytes(high: u8, low: u8) -\u003e u16 {\n let result = (high as u16) \u003c\u003c 8 | low as u16;\n result\n }\n\n // nnn or addr - A 12-bit value, the lowest 12 bits of the instruction\n pub fn read_addr_from_instruction(instruction_to_read_from: u16) -\u003e u16 {\n instruction_to_read_from \u0026 0x0FFF\n }\n\n // n or nibble - A 4-bit value, the lowest 4 bits of the instruction\n pub fn read_nibble_from_instruction(instruction_to_read_from: u16) -\u003e u16 {\n instruction_to_read_from \u0026 0x000F\n }\n\n // x - A 4-bit value, the lower 4 bits of the high byte of the instruction\n pub fn read_x_from_instruction(instruction_to_read_from: u16) -\u003e u16 {\n (instruction_to_read_from \u0026 0x0F00).rotate_right(8)\n }\n\n // y - A 4-bit value, the upper 4 bits of the low byte of the instruction\n pub fn read_y_from_instruction(instruction_to_read_from: u16) -\u003e u16 {\n (instruction_to_read_from \u0026 0x00F0).rotate_right(4)\n }\n\n // kk or byte - An 8-bit value, the lowest 8 bits of the instruction\n pub fn read_byte_from_instruction(instruction_to_read_from: u16) -\u003e u16 {\n (instruction_to_read_from \u0026 0x00FF)\n }\n\n pub fn read_upper_byte_lower_nibble(to_read_from: u16) -\u003e u16 {\n (to_read_from \u0026 0x0f00) \u003e\u003e 8\n }\n}\n\n#[cfg(test)]\nmod test {\n use super::*;\n\n #[test]\n fn smoke() {\n assert!(true)\n }\n\n #[test]\n fn 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]\n fn 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]\n fn 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]\n fn 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]\n fn byte_to_bool_changes() {\n assert_eq!(InstructionUtil::byte_to_bools(0b00000000), [false, false, false, false, false, false, false, false]);\n assert_eq!(InstructionUtil::byte_to_bools(0b11111111), [true, true, true, true, true, true, true, true]);\n assert_eq!(InstructionUtil::byte_to_bools(0b11001100), [false, false, true, true, false, false, true, true]);\n assert_eq!(InstructionUtil::byte_to_bools(0b11110000), [false, false, false, false, true, true, true, true]);\n assert_eq!(InstructionUtil::bools_to_byte([false, false, false, false, false, false, false, false]), 0b00000000);\n assert_eq!(InstructionUtil::bools_to_byte([true, true, true, true, true, true, true, true]), 0b11111111);\n assert_eq!(InstructionUtil::bools_to_byte([false, false, true, true, false, false, true, true]), 0b11001100);\n assert_eq!(InstructionUtil::bools_to_byte([false, false, false, false, true, true, true, true]), 0b11110000);\n }\n}\n","traces":[{"line":4,"address":[1702288],"length":1,"stats":{"Line":1}},{"line":5,"address":[1702303],"length":1,"stats":{"Line":1}},{"line":6,"address":[1702491,1702390,1702320],"length":1,"stats":{"Line":3}},{"line":7,"address":[1702402,1702459],"length":1,"stats":{"Line":1}},{"line":8,"address":[19763820,19763877,19763860],"length":1,"stats":{"Line":2}},{"line":10,"address":[1702370],"length":1,"stats":{"Line":1}},{"line":13,"address":[1702528],"length":1,"stats":{"Line":1}},{"line":14,"address":[1702547],"length":1,"stats":{"Line":1}},{"line":15,"address":[1702552,1702615],"length":1,"stats":{"Line":2}},{"line":16,"address":[1702670,1702630],"length":1,"stats":{"Line":1}},{"line":17,"address":[1702691,1702662,1702738],"length":1,"stats":{"Line":3}},{"line":18,"address":[1702730],"length":1,"stats":{"Line":1}},{"line":21,"address":[1702606],"length":1,"stats":{"Line":1}},{"line":24,"address":[1702752],"length":1,"stats":{"Line":1}},{"line":25,"address":[1702789],"length":1,"stats":{"Line":1}},{"line":26,"address":[1702793],"length":1,"stats":{"Line":1}},{"line":31,"address":[19764176],"length":1,"stats":{"Line":1}},{"line":32,"address":[1702814],"length":1,"stats":{"Line":1}},{"line":37,"address":[1702848],"length":1,"stats":{"Line":1}},{"line":38,"address":[1702856],"length":1,"stats":{"Line":1}},{"line":42,"address":[1702864],"length":1,"stats":{"Line":1}},{"line":43,"address":[1702872],"length":1,"stats":{"Line":1}},{"line":47,"address":[1702880],"length":1,"stats":{"Line":1}},{"line":48,"address":[1702888],"length":1,"stats":{"Line":1}},{"line":52,"address":[1702928],"length":1,"stats":{"Line":1}},{"line":53,"address":[1702936],"length":1,"stats":{"Line":1}},{"line":57,"address":[1702976],"length":1,"stats":{"Line":1}},{"line":58,"address":[1702984],"length":1,"stats":{"Line":1}},{"line":61,"address":[1702992],"length":1,"stats":{"Line":1}},{"line":62,"address":[1703000],"length":1,"stats":{"Line":1}}],"covered":30,"coverable":30},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","video.rs"],"content":"use log::{debug, trace};\nuse crate::constants::CHIP8_VIDEO_MEMORY;\n\n#[derive(Clone, Copy)]\npub struct Chip8Video {\n memory: [bool; CHIP8_VIDEO_MEMORY],\n pub has_frame_changed: bool\n}\n\nimpl Chip8Video {\n pub fn cls(\u0026mut self) {\n for i in 0..CHIP8_VIDEO_MEMORY {\n self.memory[i] = 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: [bool; CHIP8_VIDEO_MEMORY]) -\u003e Self {\n Self {\n memory: initial_configuration,\n has_frame_changed: false\n }\n }\n\n pub fn peek(self, address: u16) -\u003e bool {\n self.memory[address as usize]\n }\n\n pub fn poke(\u0026mut self, address: u16, new_value: bool) -\u003e Self {\n trace!(\"OFFSET: {address} - POKING {new_value}\");\n let old_value = self.memory[address as usize];\n if old_value != new_value {\n trace!(\"**VIDEO** TOGGLING\");\n self.has_frame_changed = true;\n } else {\n trace!(\"NOT TOGGLING\");\n }\n self.memory[address as usize] = new_value;\n self.to_owned()\n }\n\n pub fn poke_byte(\u0026mut self, first_address: u16, to_write: u8) -\u003e Self {\n for i in (0..8).rev() {\n let shifted = ((1 \u003c\u003c i) \u0026 to_write) \u003e\u003e i;\n //\n let target_address = first_address + (7 - i);\n let is_set = shifted == 1;\n self.poke(target_address, is_set);\n }\n self.to_owned()\n }\n\n pub fn poke_sprite(\u0026mut self, first_address: u16, to_write: Vec\u003cu8\u003e) -\u003e Self {\n let sprite_length = to_write.len();\n\n for (index, byte) in to_write.iter().enumerate() {\n let real_address = index * 64;\n self.poke_byte(real_address as u16, *byte);\n }\n self.to_owned()\n }\n\n pub fn format_as_string(self) -\u003e String {\n let mut output = String::new();\n for row in 0..32 {\n for column in 0..64 {\n let data_offset = row * 64 + column;\n debug!(\"Rendering {data_offset} with value {}\", self.memory[data_offset]);\n if self.memory[data_offset] {\n output += \"*\"\n } else {\n output += \" \"\n }\n }\n output += \"\\n\";\n }\n\n output\n }\n}\n\nimpl Default for Chip8Video {\n fn default() -\u003e Self {\n Self { memory: [false; CHIP8_VIDEO_MEMORY as usize], has_frame_changed: false }\n }\n}\n\n#[cfg(test)]\nmod test {\n use std::fs::File;\n use std::io::Read;\n use crate::chip8::system_memory::{CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_3, CHIP8FONT_4, CHIP8FONT_5, CHIP8FONT_6, CHIP8FONT_7};\n use crate::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};\n use super::*;\n\n #[test]\n fn smoke() { assert!(true) }\n\n #[test]\n fn default_test() {\n let mut x = Chip8Video::default();\n\n for i in 0..CHIP8_VIDEO_MEMORY {\n assert!(!x.peek(i as u16));\n // then flip the value and test again.\n \u0026x.poke(i as u16, true);\n assert!(x.peek(i as u16));\n }\n }\n\n #[test]\n fn string_test_1() {\n let mut x = Chip8Video::default();\n let mut working_string = String::new();\n for i in 0..32 {\n working_string += \u0026*(\" \".repeat(64) + \"\\n\");\n }\n\n assert_eq!(working_string, x.format_as_string());\n\n let mut working_string = String::new();\n // set a checkerboard...\n for cb_row in 0..32 {\n for cb_col in 0..64 {\n let data_offset = cb_row * 64 + cb_col;\n if data_offset % 2 == 0 {\n x.poke(data_offset, true);\n working_string += \"*\";\n } else {\n x.poke(data_offset, false);\n working_string += \" \";\n }\n }\n working_string += \"\\n\";\n }\n\n assert_eq!(working_string, x.format_as_string());\n }\n\n #[test]\n fn set_initial_memory() {\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]\n fn poke_byte() {\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]\n fn cls() {\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 initial_memory[dof] = true;\n }\n ws += \" \";\n }\n ws += \"\\n\";\n }\n let mut set_x = Chip8Video::new(initial_memory);\n set_x.cls();\n assert_eq!(set_x.format_as_string(), ws);\n }\n\n #[test]\n fn poke_byte_test() {\n let to_poke = 0b10101010;\n let mut v = Chip8Video::default();\n v.poke_byte(0x00, to_poke);\n assert!(v.peek(0x00));\n assert!(v.peek(0x02));\n assert!(v.peek(0x04));\n assert!(v.peek(0x06));\n assert!(!v.peek(0x01));\n assert!(!v.peek(0x03));\n assert!(!v.peek(0x05));\n assert!(!v.peek(0x07));\n for i in 0x8..CHIP8_VIDEO_MEMORY {\n assert!(!v.peek(i as u16));\n }\n }\n\n #[test]\n fn poke_multi_line_test() {\n let mut v = Chip8Video::default();\n let to_poke = [\n 0b00000000,\n 0b11111111,\n 0b10101010,\n 0b01010101\n ];\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.peek(0x40));\n assert!(v.peek(0x41));\n assert!(v.peek(0x42));\n assert!(v.peek(0x43));\n assert!(v.peek(0x44));\n assert!(v.peek(0x45));\n assert!(v.peek(0x46));\n assert!(v.peek(0x47));\n\n // row 3 column 1\n assert!(!v.peek(0xC0));\n assert!(v.peek(0xC1));\n assert!(!v.peek(0xC2));\n assert!(v.peek(0xC3));\n assert!(!v.peek(0xC4));\n assert!(v.peek(0xC5));\n assert!(!v.peek(0xC6));\n assert!(v.peek(0xC7));\n }\n }\n\n #[test]\n fn moved_poke_test() {\n let mut v = Chip8Video::default();\n let to_poke = [\n 0b00000000,\n 0b11111111,\n 0b10101010,\n 0b01010101\n ];\n\n let x_offset = 20;\n let y_offset = 5;\n\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.peek(test_offset));\n assert!(!v.peek(test_offset + 1));\n assert!(!v.peek(test_offset + 2));\n assert!(!v.peek(test_offset + 3));\n assert!(!v.peek(test_offset + 4));\n assert!(!v.peek(test_offset + 5));\n assert!(!v.peek(test_offset + 6));\n assert!(!v.peek(test_offset + 7));\n\n let test_offset= test_offset + 0x40;\n assert!(v.peek(test_offset));\n assert!(v.peek(test_offset + 1));\n assert!(v.peek(test_offset + 2));\n assert!(v.peek(test_offset + 3));\n assert!(v.peek(test_offset + 4));\n assert!(v.peek(test_offset + 5));\n assert!(v.peek(test_offset + 6));\n assert!(v.peek(test_offset + 7));\n }\n\n #[test]\n fn poke_sprite_test() {\n let mut v = Chip8Video::default();\n let to_poke = [\n 0b00000000,\n 0b11111111,\n 0b10101010,\n 0b01010101\n ];\n\n v.poke_sprite(0x00, to_poke.into());\n\n assert!(v.peek(0x40));\n assert!(v.peek(0x41));\n assert!(v.peek(0x42));\n assert!(v.peek(0x43));\n assert!(v.peek(0x44));\n assert!(v.peek(0x45));\n assert!(v.peek(0x46));\n assert!(v.peek(0x47));\n\n // row 3 column 1\n assert!(!v.peek(0xC0));\n assert!(v.peek(0xC1));\n assert!(!v.peek(0xC2));\n assert!(v.peek(0xC3));\n assert!(!v.peek(0xC4));\n assert!(v.peek(0xC5));\n assert!(!v.peek(0xC6));\n assert!(v.peek(0xC7));\n }\n\n #[test]\n fn verify_change_registered() {\n let mut v = Chip8Video::default();\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]\n fn write_checkboard() {\n let mut v = Chip8Video::default();\n for current_row in 0..CHIP8_VIDEO_WIDTH {\n for current_col in 0..(CHIP8_VIDEO_HEIGHT / 8){\n let offset = current_row * CHIP8_VIDEO_HEIGHT + current_col;\n println!(\"CHECKBOARD OFFSET = {offset}\");\n v.poke(offset as u16, offset % 2 == 0);\n }\n }\n println!(\"{}\", v.format_as_string());\n println!(\"fsck is a cool tool\");\n }\n\n #[test]\n fn zero_test() {\n let mut x = Chip8Video::default();\n\n x.poke_byte(0x00, CHIP8FONT_0[0]);\n x.poke_byte(0x40, CHIP8FONT_0[1]);\n x.poke_byte(0x80, CHIP8FONT_0[2]);\n x.poke_byte(0xC0, CHIP8FONT_0[3]);\n x.poke_byte(0x100, CHIP8FONT_0[4]);\n\n assert_eq!(std::fs::read_to_string(\"../resources/test/test_video_zero.asc\")\n .unwrap(),\n x.format_as_string());\n }\n\n #[test]\n fn multi_sprite_test() {\n let mut x = Chip8Video::default();\n // draw a row of digits 01234567\n\n let start_offset = 0x00;\n let per_row_skip = 0x40;\n let to_draw = [CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_3, CHIP8FONT_4, CHIP8FONT_5, CHIP8FONT_6, CHIP8FONT_7];\n for (index, sprite) in to_draw.iter().enumerate() {\n let data_base_offset = index * 0x8;\n println!(\"STARTING {index} at 0x{data_base_offset:04x} ({data_base_offset})\");\n x.poke_byte(data_base_offset as u16, sprite[0]);\n x.poke_byte((data_base_offset + 0x40) as u16, sprite[1]);\n x.poke_byte((data_base_offset + 0x80) as u16, sprite[2]);\n x.poke_byte((data_base_offset + 0xC0) as u16, sprite[3]);\n x.poke_byte((data_base_offset + 0x100) as u16, sprite[4]);\n }\n\n\n assert_eq!(std::fs::read_to_string(\"../resources/test/test_multi_sprite.asc\")\n .unwrap(),\n x.format_as_string()); }\n}\n","traces":[{"line":11,"address":[19759680],"length":1,"stats":{"Line":1}},{"line":12,"address":[1756318,1756414,1756377],"length":1,"stats":{"Line":3}},{"line":13,"address":[1756421,1756392],"length":1,"stats":{"Line":1}},{"line":17,"address":[1756448],"length":1,"stats":{"Line":1}},{"line":18,"address":[1756453],"length":1,"stats":{"Line":1}},{"line":21,"address":[1756464],"length":1,"stats":{"Line":1}},{"line":28,"address":[19759904],"length":1,"stats":{"Line":1}},{"line":29,"address":[1756544,1756587],"length":1,"stats":{"Line":1}},{"line":32,"address":[1756608],"length":1,"stats":{"Line":1}},{"line":33,"address":[1756740,1756969,1756654],"length":1,"stats":{"Line":2}},{"line":34,"address":[1757194,1756685,1757164],"length":1,"stats":{"Line":2}},{"line":35,"address":[1757176,1757533],"length":1,"stats":{"Line":2}},{"line":36,"address":[1757566,1757254],"length":1,"stats":{"Line":2}},{"line":37,"address":[1757526],"length":1,"stats":{"Line":1}},{"line":39,"address":[1757215,1757354],"length":1,"stats":{"Line":2}},{"line":41,"address":[19760676,19761123,19761156],"length":1,"stats":{"Line":2}},{"line":42,"address":[1757757],"length":1,"stats":{"Line":1}},{"line":45,"address":[1757808],"length":1,"stats":{"Line":3}},{"line":46,"address":[1757962,1757867],"length":1,"stats":{"Line":6}},{"line":47,"address":[1758095,1757980],"length":1,"stats":{"Line":3}},{"line":49,"address":[1758217,1758072,1758121],"length":1,"stats":{"Line":6}},{"line":50,"address":[1758176],"length":1,"stats":{"Line":3}},{"line":51,"address":[1758193],"length":1,"stats":{"Line":3}},{"line":53,"address":[19761320],"length":1,"stats":{"Line":3}},{"line":56,"address":[1758761,1758240],"length":1,"stats":{"Line":1}},{"line":57,"address":[1758294,1758361],"length":1,"stats":{"Line":2}},{"line":59,"address":[1758369,1758614],"length":1,"stats":{"Line":2}},{"line":60,"address":[1758708,1758650,1758736],"length":1,"stats":{"Line":2}},{"line":61,"address":[19762096,19762140],"length":1,"stats":{"Line":2}},{"line":63,"address":[1758607],"length":1,"stats":{"Line":1}},{"line":66,"address":[19762176,19763504],"length":1,"stats":{"Line":1}},{"line":67,"address":[1758820],"length":1,"stats":{"Line":1}},{"line":68,"address":[1758825,1758921,1759062],"length":1,"stats":{"Line":3}},{"line":69,"address":[1759237,1759083],"length":1,"stats":{"Line":2}},{"line":70,"address":[19762775,19762650,19762685],"length":1,"stats":{"Line":2}},{"line":71,"address":[1759405,1759676,1759341,1759804,1759476],"length":1,"stats":{"Line":3}},{"line":72,"address":[19763390,19762803],"length":1,"stats":{"Line":2}},{"line":73,"address":[1760075,1760107],"length":1,"stats":{"Line":2}},{"line":75,"address":[1760048,1760102],"length":1,"stats":{"Line":2}},{"line":78,"address":[1759278,1759205],"length":1,"stats":{"Line":2}},{"line":81,"address":[1759019],"length":1,"stats":{"Line":1}},{"line":86,"address":[1760144],"length":1,"stats":{"Line":4}},{"line":87,"address":[1760161],"length":1,"stats":{"Line":4}}],"covered":43,"coverable":43},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","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","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","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}\n\npub mod constants;","traces":[],"covered":0,"coverable":0}]};
|
|
var previousData = {"files":[{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_core","src","chip8_constants.rs"],"content":"\npub const CHIP8_MEMORY_SIZE: u16 = 0x1000;\npub const CHIP8_MEMORY_SIZE_USIZE: usize = 0x1000;\n\npub const FONT_0: [u8; 5] = [0xF0, 0x90, 0x90, 0x90, 0xF0];\npub const FONT_1: [u8; 5] = [0x20, 0x60, 0x20, 0x20, 0x70];\npub const FONT_2: [u8; 5] = [0xF0, 0x10, 0xF0, 0x80, 0xF0];\npub const FONT_3: [u8; 5] = [0xF0, 0x10, 0xF0, 0x10, 0xF0];\npub const FONT_4: [u8; 5] = [0x90, 0x90, 0xF0, 0x10, 0x10];\npub const FONT_5: [u8; 5] = [0xF0, 0x80, 0xF0, 0x10, 0xF0];\npub const FONT_6: [u8; 5] = [0xF0, 0x80, 0xF0, 0x90, 0xF0];\npub const FONT_7: [u8; 5] = [0xF0, 0x10, 0x20, 0x40, 0x40];\npub const FONT_8: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0xF0];\npub const FONT_9: [u8; 5] = [0xF0, 0x90, 0xF0, 0x10, 0xF0];\npub const FONT_A: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0x90];\npub const FONT_B: [u8; 5] = [0xE0, 0x90, 0xE0, 0x90, 0xE0];\npub const FONT_C: [u8; 5] = [0xF0, 0x80, 0x80, 0x80, 0xF0];\npub const FONT_D: [u8; 5] = [0xE0, 0x90, 0x90, 0x90, 0xE0];\npub const FONT_E: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0xF0];\npub const FONT_F: [u8; 5] = [0xF0, 0x80, 0xf0, 0x80, 0x80];\n\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_core","src","chip8_mnemonics.rs"],"content":"use bitmask::bitmask;\n\nuse crate::{chip8_constants::CHIP8_MEMORY_SIZE, parts::Display::Chip8Display};\n\nenum Chip8StartOffset {\n STANDARD,\n ETI600,\n OTHER,\n}\n\nstruct Chip8Registers {\n V: [u8; 16],\n I: u16,\n DelayTimer: u16,\n SoundTimer: u16,\n StackPointer: u8,\n ProgramCounter: u16,\n}\n\nimpl Default for Chip8Registers {\n fn default() -\u003e Self {\n Chip8Registers {\n DelayTimer: 60,\n V: [\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n 0x00, 0x00,\n ],\n I: 0x00,\n SoundTimer: 60,\n StackPointer: 255,\n ProgramCounter: 0x200,\n }\n }\n}\n\nenum Chip8Instruction {\n SYS(u16),\n CLS,\n RET,\n JMP(u16),\n CALL(u16),\n SNEQ(u8, u16),\n SNNE(u8, u8),\n SNRE(u8, u8),\n STOR(u8, u16),\n ADD(u8, u16),\n MOV(u8, u8),\n OR(u8, u8),\n AND(u8, u8),\n XOR(u8, u8),\n ADC(u8, u8),\n SBC(u8, u8),\n RSR(u8, u8),\n LSR(u8, u8),\n SNE(u8, u8),\n STO(i16),\n JMPI(u16),\n RND(u8, u8),\n SETD(u8),\n SETT(u8),\n BCD(u8),\n}\n\nenum Chip8Keys {\n KEY0,\n KEY1,\n KEY2,\n KEY3,\n KEY4,\n KEY5,\n KEY6,\n KEY7,\n KEY8,\n KEY9,\n KEYA,\n KEYB,\n KEYC,\n KEYD,\n KEYE,\n KEYF,\n}\n\npub fn display_video_memory(system_memory: [u8; CHIP8_MEMORY_SIZE as usize]) {\n // Assumes memory addresses from\n}\n\nconst ZERO: u16 = 0b0000000000000000;\nconst BOTTOM_BYTE: u16 = 0b0000000011111111;\nconst TOP_BYTE: u16 = 0b1111111100000000;\nconst NIBBLE0: u16 = 0b1111000000000000;\nconst NIBBLE1: u16 = 0b0000111100000000;\nconst NIBBLE2: u16 = 0b0000000011110000;\nconst NIBBLE3: u16 = 0b0000000000001111;\nconst NIBBLE0_BALANCE: u16 = 0x0FFF;\nconst PMSK_0000: i16 = 0x0000;\nconst PMSK_00X0: i16 = 0x00F0;\nconst PMSK_0X00: i16 = 0x0F00;\nconst PMSK_0XXX: i16 = 0x0FFF;\n\n// AND against the possible operand to find which one we have\nbitmask! {\n mask Chip8Instructions: u16 where flags Chip8InstructionFlags {\n SYS = 0x00,\n CLR = 0b0000000011100000,\n RTS = 0b0000000011101110,\n JUMP = 0b0001000000000000,\n CALL = 0b0010000000000000,\n SKE = 0b0011000000000000,\n SKNE = 0b0100000000000000,\n SKRE = 0b0101000000000000,\n LOAD = 0b0110000000000000,\n ADD = 0b0111000000000000,\n MOVE = 0b1000000000000000,\n OR = 0b1000000000000001,\n AND = 0b1000000000000010,\n XOR = 0b1000000000000011,\n ADDR = 0b1000000000000100,\n SUB = 0b1000000000000101,\n SHR = 0b1000000000000110,\n SHL = 0b1000000000001110,\n SKRNE = 0b1001000000000000,\n LOADI = 0b1010000000000000,\n JUMPI = 0b1011000000000000,\n RAND = 0b1100000000000000,\n DRAW = 0b1101000000000000,\n SKPR = 0b1110000000001110,\n SKUP = 0b1111000010100001,\n MOVED = 0b1111000000000111, \n KEYD = 0b1111000000001010, \n LOADD = 0b1111000000010101, \n LOADS = 0b1111000000011000, \n ADDI = 0b1111000000011110, \n LDSPR = 0b1111000000101001, \n BCD = 0b1111000000110011, \n STOR = 0b1111000001010101, \n MEMCPY = 0b1111000001100101 \n }\n}\n\nenum Chip8Asm {\n SYS(u16),\n CLR(),\n RTS(),\n JUMP(u16),\n CALL(u16),\n SKE(u8, u8),\n SKNE(u8, u8),\n SKRE(u8, u8),\n LOAD(u8, u8),\n ADD(u8, u8),\n MOVE(u8, u8),\n OR(u8, u8),\n AND(u8, u8),\n XOR(u8, u8),\n ADDR(u8, u8),\n SUB(u8, u8),\n SHR(u8, u8),\n SHL(u8, u8),\n SKRNE(u8, u8),\n LOADI(u16),\n JUMPI(u16),\n RAND(u8, u8),\n DRAW(u8, u8, u8),\n SKPR(u8),\n SKUP(u8),\n MOVED(u8),\n KEYD(u8),\n LOADD(u8),\n LOADS(u8),\n ADDI(u8),\n LDSPR(u8),\n BCD(u8),\n STOR(u8),\n READ(u8),\n}\n\nstruct Chip8Cpu {}\n\nstruct Chip8System {\n registers: Chip8Registers,\n system_memory: [u8; 2048]\n}\n\nimpl Chip8System {\n pub fn tick(mut self) {\n println!(\" Ticking Chip8System\");\n\n let next_instruction = self.system_memory[self.registers.ProgramCounter as usize] as u16;\n println!(\"READ INSTRUCTION {next_instruction}\");\n self.registers.ProgramCounter += 0x2;\n\n \u0026self.delay_timer_tick();\n \u0026self.sound_timer_tick();\n\n // self.screen.tick();\n }\n\n fn delay_timer_tick(\u0026mut self) {\n self.registers.DelayTimer = self.registers.DelayTimer - 1;\n\n }\n\n fn sound_timer_tick(\u0026mut self) {\n self.registers.SoundTimer = self.registers.SoundTimer - 1;\n\n }\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_core","src","lib.rs"],"content":"pub mod chip8_mnemonics;\npub mod chip8_constants;\n\npub mod parts {\n pub mod CPU;\n pub mod Display;\n pub mod Keyboard;\n}","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_core","src","parts","CPU.rs"],"content":"struct Chip8Registers {\n\n}\n\n\nimpl Chip8Registers {\n pub fn tick(self: \u0026Self) {\n println!(\"Ticking Registers\");\n }\n}\n\npub struct Chip8InstructionParameter {\n pub mask: u16\n}\n\npub struct Chip8Instruction {\n pub id: String,\n pub mask: u16,\n pub pattern: u16,\n pub arguments: Vec\u003cChip8InstructionParameter\u003e,\n pub description: Box\u003cstr\u003e\n}\n\npub fn fill_chip8_instructions() -\u003e [Chip8Instruction; 34] {\n [\n Chip8Instruction {\n id: \"SYS\".to_string(),\n mask: 0x00,\n pattern: 0x00,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0FFF\n }\n ],\n description: \"Execute method at address referenced by parameter\".into()\n },\n Chip8Instruction {\n id: \"CLR\".to_string(),\n mask: 0x00E0,\n pattern: 0x00E0,\n arguments: vec![],\n description: \"Clear the Screen\".into()\n },\n Chip8Instruction {\n id: \"RET\".to_string(),\n mask: 0x00EE,\n pattern: 0x00EE,\n arguments: vec![],\n description: \"Return from Subroutine\".into()\n },\n Chip8Instruction {\n id: \"JMP\".to_string(),\n mask: 0x1FFF,\n pattern: 0x1000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0fff\n }\n ],\n description: \"Jump to specified location\".into()\n },\n Chip8Instruction {\n id: \"SEQ\".to_string(),\n mask: 0x3FFF,\n pattern: 0x3000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00ff\n }\n ],\n description: \"Skip next instruction if Register does equal parameter\".into()\n },\n Chip8Instruction {\n id: \"SNE\".to_string(),\n mask: 0x4FFF,\n pattern: 0x4000,\n arguments: vec! [\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00ff\n }\n ],\n description: \"Skip next instruction if Register doesn't equal parameter\".into()\n },\n Chip8Instruction {\n id: \"SREQ\".to_string(),\n mask: 0x5FF0,\n pattern: 0x5000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"TBD\".into()\n },\n Chip8Instruction {\n id: \"STO\".to_string(),\n mask: 0x6FFF,\n pattern: 0x6000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00ff\n }\n ],\n description: \"Store value into register\".into()\n },\n Chip8Instruction {\n id: \"ADD\".to_string(),\n mask: 0x7FFF,\n pattern: 0x7000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00ff\n }\n ],\n description: \"Add value in register 0x0f00 with value 0x00ff\".into()\n },\n Chip8Instruction {\n id: \"CPY\".to_string(),\n mask: 0x8FF0,\n pattern: 0x8000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Copy value between Register X and Y\".into()\n },\n Chip8Instruction {\n id: \"OR\".to_string(),\n mask: 0x8FF1,\n pattern: 0x8001,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Logical OR of registers placing result in first register\".into()\n },\n Chip8Instruction {\n id: \"AND\".to_string(),\n mask: 0x8FF2,\n pattern: 0x8002,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Logical AND of registers placing result in first register\".into()\n },\n Chip8Instruction {\n id: \"XOR\".to_string(),\n mask: 0x8FF3,\n pattern: 0x8003,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Logical XOR of registers placing result in first register\".into()\n },\n Chip8Instruction {\n id: \"ADC\".to_string(),\n mask: 0x8FF4,\n pattern: 0x8004,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Add 2 Registers with carry flag\".into()\n },\n Chip8Instruction {\n id: \"SUBC\".to_string(),\n mask: 0x8FF5,\n pattern: 0x8005,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Subtract 2 Registers with carry flag\".into()\n },\n Chip8Instruction {\n id: \"RSR\".to_string(),\n mask: 0x8FF6,\n pattern: 0x8006,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Register Shifted Right\".into()\n },\n Chip8Instruction {\n id: \"SUBC\".to_string(),\n mask: 0x8FF7,\n pattern: 0x8007,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Subtract 2 Regiters with Carry\".into()\n },\n Chip8Instruction {\n id: \"RSL\".to_string(),\n mask: 0x8FFE,\n pattern: 0x800E,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Register Shifted Left\".into()\n },\n Chip8Instruction {\n id: \"SRNE\".to_string(),\n mask: 0x9FF0,\n pattern: 0x9000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n }\n ],\n description: \"Skip next instruction if registers not equal\".into()\n },\n Chip8Instruction {\n id: \"LDI\".to_string(),\n mask: 0xAFFF,\n pattern: 0xA000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0fff\n }\n ],\n description: \"Load Data from Memory Address\".into()\n },\n Chip8Instruction {\n id: \"JMPI\".to_string(),\n mask: 0xBFFF,\n pattern: 0xB000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0fff\n }\n ],\n description: \"Jump to Memory Address\".into()\n },\n Chip8Instruction {\n id: \"RNG\".to_string(),\n mask: 0xCFFF,\n pattern: 0xC000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00ff\n }\n ],\n description: \"Random number Generator\".into()\n },\n Chip8Instruction {\n id: \"SPR\".to_string(),\n mask: 0xDFFF,\n pattern: 0xD000,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n },\n Chip8InstructionParameter {\n mask: 0x00f0\n },\n Chip8InstructionParameter {\n mask: 0x000f\n }\n \n ],\n description: \"TBD\".into()\n },\n Chip8Instruction {\n id: \"JNK\".to_string(),\n mask: 0xEF9E,\n pattern: 0xE09E,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Jump over next instruction if Key Pressed\".into()\n },\n Chip8Instruction {\n id: \"JKP\".to_string(),\n mask: 0xEFA1,\n pattern: 0xE0A1,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Jump over next instruction if Key Not Pressed\".into()\n },\n Chip8Instruction {\n id: \"SDT\".to_string(),\n mask: 0xFF07,\n pattern: 0xF007,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0F00\n }\n ],\n description: \"TBD\".into()\n },\n Chip8Instruction {\n id: \"WKP\".to_string(),\n mask: 0xFF0A,\n pattern: 0xF00A,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Wait for a Key to be pressed\".into()\n },\n Chip8Instruction {\n id: \"LDT\".to_string(),\n mask: 0xFF15,\n pattern: 0xF015,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Load Data Timer\".into()\n },\n Chip8Instruction {\n id: \"LST\".to_string(),\n mask: 0xFF18,\n pattern: 0xF018,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Load Sound Timer\".into()\n },\n Chip8Instruction {\n id: \"ADDI\".to_string(),\n mask: 0xFF1E,\n pattern: 0xF01E,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Add register with value at I\".into()\n },\n Chip8Instruction {\n id: \"SETI\".to_string(),\n mask: 0xFF29,\n pattern: 0xF029,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"TBD\".into()\n },\n Chip8Instruction{\n id: \"BCD\".to_string(),\n mask: 0xFF33,\n pattern: 0xF033,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Binary Coded Decimal of register\".into()\n },\n Chip8Instruction{\n id: \"MSTO\".to_string(),\n mask: 0xFF55,\n pattern: 0xF055,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Store value in register X at offset I\".into()\n },\n Chip8Instruction {\n id: \"MLOAD\".to_string(),\n mask: 0xFF65,\n pattern: 0xF065,\n arguments: vec![\n Chip8InstructionParameter {\n mask: 0x0f00\n }\n ],\n description: \"Load value into register X at offset I\".into()\n }\n ]\n}\n\n\nstruct Chip8System {\n\n}\n\nimpl Chip8System {\n \n}\n\ntrait CpuInstruction {\n fn execute(input: Chip8System) -\u003e Chip8System;\n}","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_core","src","parts","Display.rs"],"content":"\npub struct Chip8Display {\n // 64w x 32h\n pub memory: [bool; 512],\n}\n\nimpl Chip8Display {\n pub fn tick(self: \u0026Self) {\n println!(\"Ticking the display\");\n }\n\n pub fn render_chip8_display(to_render: Chip8Display) {\n // 32 rows...\n for index_row in 0..=32 {\n // ...64 columns\n for index_col in 0..=64 {\n let offset = (index_row * 64) + index_col;\n if to_render.memory[offset] {\n print!(\"*\")\n } else {\n print!(\" \")\n };\n }\n println!();\n }\n }\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_core","src","parts","Keyboard.rs"],"content":"\n#[derive(Clone)]\nstruct Chip8Keyboard {\n keys_state: [bool; 16]\n}\n\nimpl Default for Chip8Keyboard {\n fn default() -\u003e Self {\n Self { keys_state: [ \n false, false, false, false,\n false, false, false, false,\n false, false, false, false,\n false, false, false, false\n ] }\n }\n}\n\nimpl Chip8Keyboard {\n\n fn display_keyboard_key(self: Self, key_id: u8) {\n let filler = if self.is_pressed(key_id) { \"*\" } else { \" \" };\n print!(\"{}{}{}\", filler, key_id, filler)\n }\n\n fn display_keyboard_horizontal_line(self: Self, row_values: [u8; 4]) {\n print!(\"|\");\n for current in row_values {\n self.clone().display_keyboard_key(current);\n print!(\"|\");\n }\n println!();\n } \n\n fn display_keyboard_seperator_line(self: Self) {\n println!(\"+---+---+---+---+\");\n\n }\n\n pub fn display_keyboard(self: Self) {\n self.clone().display_keyboard_seperator_line();\n self.clone().display_keyboard_horizontal_line(\n [1,2,3,0xC]\n );\n println!(\"+---+---+---+---+\");\n println!(\"| 4 | 5 | 6 | D |\");\n println!(\"+---+---+---+---+\");\n println!(\"| 7 | 8 | 9 | E |\");\n println!(\"+---+---+---+---+\");\n println!(\"| A | 0 | B | F |\");\n println!(\"+---+---+---+---+\");\n }\n \n pub fn press_key(self: \u0026mut Self, key_id: u8) {\n self.keys_state[key_id as usize] = true;\n }\n\n pub fn release_key(self: \u0026mut Self, key_id: u8) {\n self.keys_state[key_id as usize] = false;\n }\n\n pub fn is_pressed(self: Self, key_id: u8) -\u003e bool {\n self.keys_state[key_id as usize]\n }\n}\n\n\n#[cfg(test)]\nmod tests {\n use super::*;\n \n #[test]\n fn smoke_test() {\n assert_eq!(true, true);\n }\n \n #[test]\n fn basic_keyboard_display_works() {\n let basic_keyboard = Chip8Keyboard::default();\n\n \n }\n}","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_toy","src","app.rs"],"content":"/*\n\n#[derive(Default)]\npub struct App;\n\nimpl UiBuilder for App {\n fn do_ui(\u0026mut self, ui: \u0026Ui\u003cSelf\u003e) {\n #[cfg(feature = \"docking\")]\n {\n ui.dock_space_over_viewport(0, imgui::DockNodeFlags::None);\n }\n ui.show_demo_window(None);\n\n }\n}*/","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_toy","src","gui","display.rs"],"content":"use winit::{application::ApplicationHandler, event_loop::ActiveEventLoop, window::{Window, WindowAttributes}};\n\n/*\npub struct Chip8Display {\n pub window: MainWindowWithRenderer,\n}\n\nimpl Chip8Display {\n pub fn new(event_loop: \u0026ActiveEventLoop) -\u003e Chip8Display {\n Chip8Display { window: MainWindowWithRenderer::new(\n MainWindow::new::\u003c()\u003e(\u0026event_loop, Window::default_attributes().with_title(\"Chip8 Display\")).unwrap())\n }\n }\n\n pub fn render\u003cM\u003e(ui: \u0026Ui\u003cM\u003e) {\n \n }\n}\n\nimpl UiBuilder for Chip8AppHandler {\n fn do_ui(\u0026mut self, ui: \u0026Ui\u003cSelf\u003e) {\n\n ui.text(\"This is where the Chip8 UI Goes\");\n ui.text(\"Controls for Start/Stop/Load/Run Maybe?\");\n\n\n }\n}\n\n#[derive(Default)]\npub struct Chip8AppHandler {\n pub windows: Vec\u003cMainWindowWithRenderer\u003e,\n // pub display: Chip8Display\n}\n\nimpl ApplicationHandler for Chip8AppHandler {\n fn suspended(\u0026mut self, _event_loop: \u0026ActiveEventLoop) {\n self.windows.clear();\n }\n\n fn resumed(\u0026mut self, event_loop: \u0026ActiveEventLoop) {\n self.windows.push(Chip8Display::new(event_loop).window);\n }\n\n fn window_event(\n \u0026mut self,\n event_loop: \u0026ActiveEventLoop,\n window_id: winit::window::WindowId,\n event: winit::event::WindowEvent,\n ) {\n for window in \u0026mut self.windows {\n if window.main_window().window().id() != window_id {\n continue;\n }\n }\n }\n fn new_events(\u0026mut self, _event_loop: \u0026ActiveEventLoop, _cause: winit::event::StartCause) {\n for window in \u0026mut self.windows {\n window.new_events();\n }\n }\n fn about_to_wait(\u0026mut self, _event_loop: \u0026ActiveEventLoop) {\n for window in \u0026mut self.windows {\n window.about_to_wait();\n }\n }\n\n}\n*/","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_toy","src","lib.rs"],"content":"pub mod app;\n\npub mod gui {\n pub mod display;\n}\n\npub mod support;","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_toy","src","main.rs"],"content":"use trevors_chip8_core::parts::CPU::fill_chip8_instructions;\nuse trevors_chip8_toy::support;\nuse winit::{event::WindowEvent, event_loop::EventLoop};\n\nuse imgui::*;\n/*\npub fn display_instruction_table(current_index: u32, ui: \u0026Ui\u003cApp\u003e) -\u003e u32 {\n let to_display = fill_chip8_instructions();\n let mut return_index = current_index;\n let current_instruction = to_display.get(return_index as usize).unwrap();\n ui.text(\"Instructions\");\n ui.set_next_item_width(100f32);\n ui.with_group(|| {\n ui.text(format!(\"{}\", current_instruction.description).as_str());\n if current_instruction.arguments.is_empty() {\n ui.text(\"No Parameters\");\n } else {\n for current_parameter in current_instruction.arguments {\n ui.text(format!(\"{}\", current_parameter.mask).as_str());\n }\n }\n ui.text(format!(\"{:#}\", current_instruction.arguments).as_str());\n ui.text(\"This is where cool stuff happens part 2\");\n });\n ui.same_line();\n ui.with_group(|| {\n for (index, current) in to_display.iter().enumerate() {\n if ui\n .selectable_config(current.id.clone())\n .selected(index == return_index as usize)\n .build()\n {\n return_index = index as u32;\n }\n }\n });\n return_index\n}\n*/\n\nfn chip8_instructions(initial_index: u32, ui: \u0026mut Ui) -\u003e u32 {\n let to_display = fill_chip8_instructions();\n let mut return_index = initial_index;\n let instruction_to_display = to_display.get(return_index as usize).unwrap();\n let instruction_window = ui\n .window(\"Instructions\")\n .size([400.0, 400.0], Condition::FirstUseEver)\n .build(|| {\n ui.text(\"This should be my current instruction example\");\n ui.same_line();\n ui.set_next_item_width(-1.0f32);\n for (index, instruction) in to_display.iter().enumerate() {\n if ui\n .selectable_config(instruction.id.to_string())\n .selected(index as u32 == return_index)\n .build()\n {\n return_index = index as u32;\n println!(\"RETURN INDEX {return_index}\");\n }\n }\n });\n return_index\n}\n\nfn main() {\n let mut value = 0;\n let choices = [\"test test this is 1\", \"test test this is 2\"];\n\n let mut selected_instruction = 0;\n\n support::simple_init(file!(), move |_, ui| {\n selected_instruction = chip8_instructions(selected_instruction, ui);\n\n ui.window(\"Hello world\")\n .size([300.0, 110.0], Condition::FirstUseEver)\n .build(|| {\n ui.text_wrapped(\"Hello world!\");\n if ui.button(choices[value]) {\n value += 1;\n value %= 2;\n }\n\n ui.button(\"This...is...imgui-rs!\");\n ui.separator();\n let mouse_pos = ui.io().mouse_pos;\n ui.text(format!(\n \"Mouse Position: ({:.1},{:.1})\",\n mouse_pos[0], mouse_pos[1]\n ));\n });\n });\n}\n/*\nfn main() {\n let event_loop = EventLoop::new().unwrap();\n\n let mut main = AppHandler::\u003cApp\u003e::default();\n main.attributes().title = String::from(\"Example-420\");\n\n event_loop.run_app(\u0026mut main).unwrap();\n}\n\n#[derive(Default)]\npub struct App {\n pub selected_instruction_index: u32,\n}\n\nimpl App {}\n\nimpl Application for App {\n type UserEvent = ();\n type Data = ();\n fn new(_: Args\u003c()\u003e) -\u003e App {\n App {\n selected_instruction_index: 0,\n }\n }\n fn window_event(\u0026mut self, args: Args\u003c()\u003e, _event: WindowEvent, res: EventResult) {\n if res.window_closed {\n args.event_loop.exit();\n }\n }\n}\n\nimpl imgui::UiBuilder for App {\n fn do_ui(\u0026mut self, ui: \u0026imgui::Ui\u003cSelf\u003e) {\n #[cfg(feature = \"docking\")]\n {\n ui.dock_space_over_viewport(0, imgui::DockNodeFlags::None);\n }\n\n let x = ui.collapsing_header_config(\"collapsing header\");\n\n ui.with_group(|| {\n self.selected_instruction_index =\n display_instruction_table(self.selected_instruction_index, ui);\n });\n\n ui.show_demo_window(None);\n }\n}\n*/\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_toy","src","support","clipboard.rs"],"content":"use copypasta::{ClipboardContext, ClipboardProvider};\nuse imgui::ClipboardBackend;\n\npub struct ClipboardSupport(pub ClipboardContext);\n\npub fn 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","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","chip8_toy","src","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::dpi::LogicalSize;\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\nmod clipboard;\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_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":18,"address":[],"length":0,"stats":{"Line":0}},{"line":19,"address":[],"length":0,"stats":{"Line":0}},{"line":27,"address":[],"length":0,"stats":{"Line":0}},{"line":29,"address":[],"length":0,"stats":{"Line":0}},{"line":30,"address":[],"length":0,"stats":{"Line":0}},{"line":31,"address":[],"length":0,"stats":{"Line":0}},{"line":33,"address":[],"length":0,"stats":{"Line":0}},{"line":35,"address":[],"length":0,"stats":{"Line":0}},{"line":36,"address":[],"length":0,"stats":{"Line":0}},{"line":37,"address":[],"length":0,"stats":{"Line":0}},{"line":38,"address":[],"length":0,"stats":{"Line":0}},{"line":39,"address":[],"length":0,"stats":{"Line":0}},{"line":40,"address":[],"length":0,"stats":{"Line":0}},{"line":41,"address":[],"length":0,"stats":{"Line":0}},{"line":43,"address":[],"length":0,"stats":{"Line":0}},{"line":44,"address":[],"length":0,"stats":{"Line":0}},{"line":46,"address":[],"length":0,"stats":{"Line":0}},{"line":49,"address":[],"length":0,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":53,"address":[],"length":0,"stats":{"Line":0}},{"line":54,"address":[],"length":0,"stats":{"Line":0}},{"line":55,"address":[],"length":0,"stats":{"Line":0}},{"line":58,"address":[],"length":0,"stats":{"Line":0}},{"line":61,"address":[],"length":0,"stats":{"Line":0}},{"line":64,"address":[],"length":0,"stats":{"Line":0}},{"line":66,"address":[],"length":0,"stats":{"Line":0}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":69,"address":[],"length":0,"stats":{"Line":0}},{"line":70,"address":[],"length":0,"stats":{"Line":0}},{"line":71,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":0}},{"line":73,"address":[],"length":0,"stats":{"Line":0}},{"line":75,"address":[],"length":0,"stats":{"Line":0}},{"line":76,"address":[],"length":0,"stats":{"Line":0}},{"line":77,"address":[],"length":0,"stats":{"Line":0}},{"line":78,"address":[],"length":0,"stats":{"Line":0}},{"line":79,"address":[],"length":0,"stats":{"Line":0}},{"line":81,"address":[],"length":0,"stats":{"Line":0}},{"line":82,"address":[],"length":0,"stats":{"Line":0}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":84,"address":[],"length":0,"stats":{"Line":0}},{"line":85,"address":[],"length":0,"stats":{"Line":0}},{"line":87,"address":[],"length":0,"stats":{"Line":0}},{"line":88,"address":[],"length":0,"stats":{"Line":0}},{"line":89,"address":[],"length":0,"stats":{"Line":0}},{"line":90,"address":[],"length":0,"stats":{"Line":0}},{"line":93,"address":[],"length":0,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":0}},{"line":95,"address":[],"length":0,"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":107,"address":[],"length":0,"stats":{"Line":0}},{"line":109,"address":[],"length":0,"stats":{"Line":0}},{"line":111,"address":[],"length":0,"stats":{"Line":0}},{"line":112,"address":[],"length":0,"stats":{"Line":0}},{"line":113,"address":[],"length":0,"stats":{"Line":0}},{"line":114,"address":[],"length":0,"stats":{"Line":0}},{"line":115,"address":[],"length":0,"stats":{"Line":0}},{"line":116,"address":[],"length":0,"stats":{"Line":0}}],"covered":0,"coverable":67},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","bin","emma.rs"],"content":"use std::io::{stdout, Result};\n/*\nuse emmaemu::{chip8::{computer::Chip8Computer, video::Chip8Video}, constants::{CHIP8_MEMORY_SIZE, CHIP8_REGISTER_COUNT, CHIP8_ROM_SIZE, CHIP8_VIDEO_MEMORY}};\nuse ratatui::{\n backend::CrosstermBackend,\n crossterm::{\n event::{self, KeyCode, KeyEventKind},\n terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},\n ExecutableCommand,\n },\n layout::{Alignment, Rect},\n style::{Style, Stylize},\n widgets::{List, Paragraph, Widget},\n Frame, Terminal,\n};\n\n\nfn system_memory_to_text_render(data_to_dump: [u8; 2048]) -\u003e String {\n let mut to_return = String::new();\n\n for i in 0..256 {\n to_return += \u0026format!(\"{:x}\\t\", data_to_dump[i as usize]).to_string();\n\n if ((i + 1) % CHIP8_REGISTER_COUNT) == 0 {\n to_return += \"\\n\";\n }\n }\n\n to_return\n}\n\nfn dump_memory_to_console(data_to_dump: [u8; 2048]) {\n println!(\"STARTING TO DUMP MEMORY TO CONSOLE\");\n println!(\"{}\", system_memory_to_text_render(data_to_dump));\n println!(\"DONE DUMPING!\");\n panic!(\"DONE DUMPING\");\n}\n\nstruct ControlKeyboard {\n\n}\n\nimpl Widget for ControlKeyboard {\n fn render(self, area: Rect, buf: \u0026mut ratatui::prelude::Buffer)\n where\n Self: Sized {\n let style = Style::new();\n buf.set_string(0, 0, \"F1 to cycle foreground - F2 to cycle background\", style)\n }\n}\n\n\n#[derive(Clone)]\nstruct AppState {\n pub menu_items: Vec\u003cString\u003e,\n pub selected_menu_item: i32,\n pub system: Chip8Computer,\n}\n\nimpl Default for AppState {\n fn default() -\u003e Self {\n println!(\"Creating new AppState\");\n Self {\n menu_items: vec![\n \"Step CPU \u003cF5\u003e\".into(),\n \"Reset CPU \u003cF12\u003e\".into(),\n \"Item 3\".into(),\n ],\n selected_menu_item: 0,\n system: Chip8Computer {\n ..Default::default()\n },\n }\n }\n}\n\nfn main() -\u003e Result\u003c()\u003e {\n stdout().execute(EnterAlternateScreen)?;\n enable_raw_mode()?;\n let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;\n terminal.clear()?;\n\n let app = AppState::default();\n\n loop {\n // Draw Ui...\n terminal.draw(|frame| {\n frame.render_widget(app.system.memory, frame.area());\n // frame.render_widget(app.control_keyboard, area);\n //render_cpu_state(app.system.clone(), frame);\n // render_video_state(app.system.video_memory, frame);\n // render_menu_list(app.clone(), frame);\n })?;\n // ...handle Events.\n if event::poll(std::time::Duration::from_millis(16))? {\n if let event::Event::Key(key) = event::read()? {\n match key.kind {\n KeyEventKind::Press =\u003e match key.code {\n KeyCode::F(5) =\u003e {\n println!(\"Execute Next Instruction\");\n }\n KeyCode::F(12) =\u003e {\n println!(\"Resetting CPU\");\n }\n KeyCode::Char('q') =\u003e { break; }\n _ =\u003e (),\n },\n _ =\u003e (),\n }\n }\n }\n }\n\n // stdout().execute(LeaveAlternateScreen)?;\n // disable_raw_mode()?;\n Ok(())\n}\n\n#[cfg(test)]\nmod test {\n use emmaemu::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};\n\n use crate::*;\n #[test]\n fn blank_screen_renders_to_text() {\n let test_video = Chip8Video::default();\n\n\n let blank_data: [bool; CHIP8_VIDEO_MEMORY] = [false; CHIP8_VIDEO_MEMORY];\n let blank_screen = (\" \".repeat(CHIP8_VIDEO_WIDTH.try_into().unwrap()) + \"\\n\")\n .repeat(CHIP8_VIDEO_HEIGHT.try_into().unwrap());\n \n assert_eq!(Chip8Video::new(blank_data).format_as_string(), blank_screen);\n }\n\n #[test]\n fn filled_screen_renders_to_text() {\n let filled_data: [bool; CHIP8_VIDEO_MEMORY] = [true; CHIP8_VIDEO_MEMORY];\n let filled_screen = (\"*\".repeat(CHIP8_VIDEO_WIDTH.try_into().unwrap()) + \"\\n\")\n .repeat(CHIP8_VIDEO_HEIGHT.try_into().unwrap());\n assert_eq!(Chip8Video::new(filled_data).format_as_string(), filled_screen);\n }\n\n #[test]\n fn grid_pattern_renders_to_text() {\n let mut grid_data: [bool; CHIP8_VIDEO_MEMORY] = [false; CHIP8_VIDEO_MEMORY];\n let mut expected_data = String::new();\n for i in 0..CHIP8_VIDEO_MEMORY {\n grid_data[i] = (i % 2 == 0);\n if i % 2 == 0 {\n grid_data[i] = true;\n expected_data += \"*\";\n } else {\n grid_data[i] = false;\n expected_data += \" \";\n }\n if (i as i32 % CHIP8_VIDEO_WIDTH as i32 == CHIP8_VIDEO_WIDTH - 1) {\n expected_data += \"\\n\";\n }\n }\n\n assert_eq!(Chip8Video::new(grid_data).format_as_string(), expected_data);\n }\n}\n*/\nfn main() {\n println!(\"Taxation is theft\");\n}","traces":[{"line":166,"address":[119248],"length":1,"stats":{"Line":0}},{"line":167,"address":[119252],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":2},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","bin","emmagui.rs"],"content":"use std::default::Default;\nuse std::fs::DirEntry;\nuse std::time::Instant;\nuse emmaemu::{\n chip8::computer::Chip8Computer,\n constants::{CHIP8_MEMORY_SIZE, CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH},\n};\nuse imgui::*;\nuse ratatui::symbols::half_block;\nuse sys::{ImColor, ImVec2, ImVector_ImU32};\nuse rand::random;\nuse emmaemu::chip8::system_memory::Chip8SystemMemory;\nuse support::emmagui_support::EmmaGui;\nmod support;\n\n\nstruct UiState {\n pub show_registers: bool,\n pub show_memory: bool,\n pub show_video: 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: f32\n}\n\nimpl Clone for UiState {\n fn clone(\u0026self) -\u003e Self {\n UiState {\n show_registers: self.show_registers,\n show_memory: self.show_memory,\n show_video: self.show_video,\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 }\n }\n}\n\nimpl Default for UiState {\n fn default() -\u003e Self {\n UiState {\n show_registers: false,\n show_memory: false,\n show_video: 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: 32.0\n }\n }\n}\n\nfn main() {\n pretty_env_logger::init();\n let mut system = Chip8Computer::default();\n let mut ui_state = UiState::default();\n let mut last_tick_time = Instant::now();\n\n support::simple_init(file!(), move |_, ui| {\n let current_time = Instant::now();\n let time_since_last_tick = current_time.duration_since(last_tick_time).as_millis();\n if ui_state.is_running \u0026\u0026 time_since_last_tick \u003e ui_state.frame_time as u128 {\n system.step_system();\n last_tick_time = current_time;\n }\n\n EmmaGui::system_controls(\u0026mut system, \u0026mut ui_state, ui);\n\n if ui_state.show_registers {\n EmmaGui::registers_view(\u0026system, ui);\n }\n\n if ui_state.show_video {\n EmmaGui::video_display(\u0026system, \u0026ui_state, ui);\n }\n\n if ui_state.show_memory {\n let active_instruction = system.registers.peek_pc();\n EmmaGui::hex_memory_display(system.memory.clone(), (0x100, 0x10), active_instruction as i16, ui);\n }\n });\n}\n","traces":[{"line":29,"address":[4555552],"length":1,"stats":{"Line":0}},{"line":31,"address":[4555576],"length":1,"stats":{"Line":0}},{"line":32,"address":[4555583],"length":1,"stats":{"Line":0}},{"line":33,"address":[4555590],"length":1,"stats":{"Line":0}},{"line":34,"address":[4555597],"length":1,"stats":{"Line":0}},{"line":35,"address":[4555637],"length":1,"stats":{"Line":0}},{"line":36,"address":[4555641],"length":1,"stats":{"Line":0}},{"line":37,"address":[4555645],"length":1,"stats":{"Line":0}},{"line":38,"address":[4555649],"length":1,"stats":{"Line":0}},{"line":44,"address":[4555728,4555911],"length":1,"stats":{"Line":0}},{"line":49,"address":[4555747],"length":1,"stats":{"Line":0}},{"line":50,"address":[4555759],"length":1,"stats":{"Line":0}},{"line":51,"address":[4555814],"length":1,"stats":{"Line":0}},{"line":58,"address":[4556356,4555936],"length":1,"stats":{"Line":0}},{"line":59,"address":[4555988],"length":1,"stats":{"Line":0}},{"line":60,"address":[4556013],"length":1,"stats":{"Line":0}},{"line":61,"address":[4556043],"length":1,"stats":{"Line":0}},{"line":62,"address":[4556166,4556091],"length":1,"stats":{"Line":0}},{"line":64,"address":[4556181],"length":1,"stats":{"Line":0}},{"line":65,"address":[4817912],"length":1,"stats":{"Line":0}},{"line":66,"address":[4817935],"length":1,"stats":{"Line":0}},{"line":67,"address":[4818018,4818218,4818071],"length":1,"stats":{"Line":0}},{"line":68,"address":[4818181],"length":1,"stats":{"Line":0}},{"line":69,"address":[4818196],"length":1,"stats":{"Line":0}},{"line":72,"address":[4818034],"length":1,"stats":{"Line":0}},{"line":74,"address":[4818051],"length":1,"stats":{"Line":0}},{"line":75,"address":[4818246],"length":1,"stats":{"Line":0}},{"line":78,"address":[4818228],"length":1,"stats":{"Line":0}},{"line":79,"address":[4818280],"length":1,"stats":{"Line":0}},{"line":82,"address":[4818262],"length":1,"stats":{"Line":0}},{"line":83,"address":[4818307],"length":1,"stats":{"Line":0}},{"line":84,"address":[4818342],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":32},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","bin","font","text_rendering.rs"],"content":"use ratatui::{style::Stylize, widgets::{List, Paragraph}, Frame};\n\nuse crate::{AppState, Chip8Computer, CHIP8_REGISTER_COUNT, CHIP8_VIDEO_MEMORY};\n\n\npub fn render_cpu_state(cpu_state: Chip8Computer, frame: \u0026mut Frame) {\n let mut area = frame.area();\n\n let mut current_state: Vec\u003cString\u003e = vec![];\n\n current_state.push(format!(\"Delay Timer {:X}\", cpu_state.delay_timer));\n current_state.push(format!(\"Sound Timer {:X}\", cpu_state.sound_timer));\n current_state.push(format!(\"PC {:X}\", cpu_state.pc));\n for i in 0 .. CHIP8_REGISTER_COUNT {\n current_state.push(format!(\"V{}: {}\", i, cpu_state.registers[i as usize]));\n }\n\n frame.render_widget(\n List::new(current_state).blue().on_white(), area)\n}\n\npub fn render_menu_list(state: AppState, frame: \u0026mut Frame) {\n let mut area = frame.area();\n\n frame.render_widget(List::new(state.menu_items).blue().on_white(), area);\n}\n\npub fn render_hello_world(frame: \u0026mut Frame) {\n let area = frame.area();\n frame.render_widget(\n Paragraph::new(\"Hello Ratatui! (press 'q' to quit)\")\n .white()\n .on_blue(),\n area,\n );\n}\n\npub fn render_video_state(video_memory: [bool; CHIP8_VIDEO_MEMORY], frame: \u0026mut Frame) {\n \n}","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","bin","support","clipboard.rs"],"content":"","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","bin","support","emmagui_support.rs"],"content":"use std::fs::File;\nuse std::io::Read;\nuse std::path::{Path, PathBuf};\nuse std::thread::sleep;\nuse std::time::Duration;\nuse imgui::{Condition, ImColor32, Ui};\nuse log::debug;\nuse emmaemu::chip8::computer::Chip8Computer;\nuse emmaemu::chip8::system_memory::Chip8SystemMemory;\nuse emmaemu::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};\nuse crate::UiState;\n\n\npub struct EmmaGui {}\n\nconst CELL_WIDTH: i32 = 5i32;\nconst CELL_HEIGHT: i32 = 5i32;\n\nstruct 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 for (index, entry) in std::fs::read_dir(root.as_path()).unwrap().enumerate() {\n let filename = entry.unwrap().file_name();\n let filename = filename.to_str().unwrap();\n let mut working_select = ui.selectable_config(format!(\"{}\", filename));\n if filename == selected_filename {\n working_select = working_select.selected(true);\n }\n if working_select.build() {\n debug!(\"SELECTED {index} / {filename}\");\n working_filename = filename.to_string();\n };\n }\n working_filename\n }\n}\n\n\nimpl EmmaGui {\n pub fn video_display(system_to_control: \u0026Chip8Computer, gui_state: \u0026UiState, ui: \u0026Ui) {\n // draw area size\n let draw_area_size = ui.io().display_size;\n let cell_width = ((draw_area_size[0] as i32 / 64) * 6) / 10;\n let cell_height = ((draw_area_size[1] as i32 / 32) * 6) / 10;\n\n ui.window(format!(\"Display {cell_width}x{cell_height}\"))\n .size([300.0, 300.0], Condition::FirstUseEver)\n .build(|| {\n let origin = ui.cursor_screen_pos();\n let fg = ui.get_window_draw_list();\n ui.text(\"This is the video display.\");\n for current_row in 0..=31 {\n for current_column in 0..=63 {\n let x_offset = origin[0] as i32 + (current_column * cell_width);\n let y_offset = origin[1] as i32 + (current_row * cell_height);\n let current_origin = [x_offset as f32, y_offset as f32];\n let current_limit = [(x_offset + cell_width) as f32, (y_offset + cell_height) as f32];\n let memory_offset = (current_row * 64 + 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(current_origin, current_limit, color, color, color, color);\n }\n }\n });\n }\n pub fn system_controls(system_to_control: \u0026mut Chip8Computer, gui_state: \u0026mut UiState, ui: \u0026Ui) {\n ui.window(\"!!!! CONTROLS !!!!\")\n .size([345.0, 200.0], Condition::FirstUseEver)\n .build(|| {\n /* ROM Lister */\n let new_filename = GuiFileList::display_path(PathBuf::from(\"resources/roms\"), \u0026gui_state.filename_to_load, ui);\n if !new_filename.is_empty() {\n if new_filename != gui_state.filename_to_load {\n println!(\"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::new();\n\n println!(\"PREPARING TO LOAD {}\", gui_state.filename_to_load);\n // let mut input_file = File::open(Path::new(\"./1-chip8-logo.ch8\")).expect(\"put 1-chip8-logo.ch8 in this directory\");\n let mut input_file = File::open(Path::new(\u0026(\"resources/roms/\".to_string() + \u0026gui_state.filename_to_load))).expect(\"put 1-chip8-logo.ch8 in this directory\");\n input_file.read_to_end(\u0026mut buffer).expect(\"unable to read file\");\n system_to_control.load_bytes_to_memory(0x200, buffer.into());\n }\n }\n ui.separator();\n if ui.button(\"Step\") {\n system_to_control.step_system();\n };\n if ui.button(\"Run\") {\n gui_state.is_running = true;\n println!(\"STARTING THE SYSTEM\");\n }\n if ui.button(\"Stop\") {\n gui_state.is_running = false;\n println!(\"STOPPING THE SYSTEM\");\n }\n ui.same_line();\n if ui.button(\"Reset\") {\n *system_to_control = Chip8Computer::new();\n }\n if ui.button(\"Dump Video Memory\") {\n println!(\"{}\", system_to_control.dump_video_to_string());\n }\n ui.same_line();\n if ui.button(\"Dump Keypad State\") {\n println!(\"{}\", system_to_control.dump_keypad_to_string());\n }\n ui.same_line();\n if ui.button(\"Dump Registers\") {\n println!(\"{}\", system_to_control.dump_registers_to_string());\n }\n ui.separator();\n\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 });\n }\n\n pub fn registers_view(system: \u0026Chip8Computer, ui: \u0026Ui) {\n ui.window(\"Registers\")\n .size([400.0, 500.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(bytes: Chip8SystemMemory, position: (i32, i32), active: i16, ui: \u0026Ui) {\n let rows = position.0;\n let cols = position.1;\n ui.window(\"System Memory\")\n .size([400.0, 300.0], Condition::FirstUseEver)\n .position([500.0, 300.0], Condition::Always)\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(text_location, [bounding_box.x, bounding_box.y]);\n let is_active = data_offset == active as i32;\n\n ui.text_colored(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 }, 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 println!(\"Offset: [{}] [0x{:02x}] Value: [{}]\", data_offset, data_offset, formatted_text.clone());\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!(\"Offset 0x{:03x}\", current_x_hover * cols + current_y_hover));\n });\n }\n\n pub fn system_memory_render(memory: Chip8SystemMemory, ui: \u0026Ui) {\n ui.window(\"System Memory\")\n .size([300.0, 100.0], Condition::FirstUseEver)\n .build(|| {\n ui.text(\"Rendering System Memory Here\");\n let draw_list = ui.get_foreground_draw_list();\n let mut idx = 0;\n for row in 0..CHIP8_VIDEO_HEIGHT {\n for column in 0..CHIP8_VIDEO_WIDTH {\n let x_offset = column * CELL_WIDTH;\n let y_offset = row * CELL_WIDTH;\n let start_point = [x_offset as f32, y_offset as f32];\n let end_point = [(x_offset + CELL_WIDTH) as f32,\n (y_offset + CELL_HEIGHT) as f32\n ];\n let memory_offset = (row * CHIP8_VIDEO_WIDTH) + column;\n let target_color = if memory.peek(memory_offset as u16) == 0 {\n ImColor32::BLACK\n } else {\n ImColor32::WHITE\n };\n draw_list.add_rect([x_offset as f32, y_offset as f32],\n [(x_offset + CELL_WIDTH) as f32, (y_offset + CELL_HEIGHT) as f32],\n target_color).build();\n idx += 1;\n }\n }\n });\n }\n}","traces":[{"line":22,"address":[4562465,4559392],"length":1,"stats":{"Line":0}},{"line":23,"address":[4559447],"length":1,"stats":{"Line":0}},{"line":24,"address":[4559534,4560003,4559866,4559646],"length":1,"stats":{"Line":0}},{"line":25,"address":[4560460,4560018,4560489,4560285],"length":1,"stats":{"Line":0}},{"line":26,"address":[4560813],"length":1,"stats":{"Line":0}},{"line":27,"address":[4560947,4561110],"length":1,"stats":{"Line":0}},{"line":28,"address":[4561236,4561381],"length":1,"stats":{"Line":0}},{"line":29,"address":[4561396,4561671,4561472],"length":1,"stats":{"Line":0}},{"line":30,"address":[4561553],"length":1,"stats":{"Line":0}},{"line":32,"address":[4562442,4561680,4561478],"length":1,"stats":{"Line":0}},{"line":33,"address":[4561716,4562113,4561844],"length":1,"stats":{"Line":0}},{"line":34,"address":[4562300,4561768],"length":1,"stats":{"Line":0}},{"line":37,"address":[4560756],"length":1,"stats":{"Line":0}},{"line":43,"address":[4562496],"length":1,"stats":{"Line":0}},{"line":45,"address":[4562555],"length":1,"stats":{"Line":0}},{"line":46,"address":[4562781,4562570],"length":1,"stats":{"Line":0}},{"line":47,"address":[4562724,4562801,4563473],"length":1,"stats":{"Line":0}},{"line":49,"address":[4563332,4563102,4563447,4563269],"length":1,"stats":{"Line":0}},{"line":50,"address":[4563290],"length":1,"stats":{"Line":0}},{"line":51,"address":[4051120,4052367],"length":1,"stats":{"Line":0}},{"line":52,"address":[4051132],"length":1,"stats":{"Line":0}},{"line":53,"address":[4051170],"length":1,"stats":{"Line":0}},{"line":54,"address":[4051196],"length":1,"stats":{"Line":0}},{"line":55,"address":[4051404,4051255],"length":1,"stats":{"Line":0}},{"line":56,"address":[4051422],"length":1,"stats":{"Line":0}},{"line":57,"address":[4051701,4051590],"length":1,"stats":{"Line":0}},{"line":58,"address":[4051728,4051890,4051699],"length":1,"stats":{"Line":0}},{"line":59,"address":[4051847],"length":1,"stats":{"Line":0}},{"line":60,"address":[4052027,4051924,4051873],"length":1,"stats":{"Line":0}},{"line":61,"address":[4052169,4052008,4052056],"length":1,"stats":{"Line":0}},{"line":62,"address":[4052194,4052107],"length":1,"stats":{"Line":0}},{"line":63,"address":[4052206,4052229],"length":1,"stats":{"Line":0}},{"line":64,"address":[4052236],"length":1,"stats":{"Line":0}},{"line":66,"address":[4052215],"length":1,"stats":{"Line":0}},{"line":68,"address":[4052250],"length":1,"stats":{"Line":0}},{"line":73,"address":[4563504],"length":1,"stats":{"Line":0}},{"line":74,"address":[4563777,4563628,4563561],"length":1,"stats":{"Line":0}},{"line":75,"address":[4563586],"length":1,"stats":{"Line":0}},{"line":76,"address":[4053887,4052400,4055590],"length":1,"stats":{"Line":0}},{"line":78,"address":[4052427],"length":1,"stats":{"Line":0}},{"line":79,"address":[4052534,4052590],"length":1,"stats":{"Line":0}},{"line":80,"address":[4052655,4052601,4053050],"length":1,"stats":{"Line":0}},{"line":81,"address":[4052806],"length":1,"stats":{"Line":0}},{"line":82,"address":[4052880],"length":1,"stats":{"Line":0}},{"line":84,"address":[4053882,4052666,4053059],"length":1,"stats":{"Line":0}},{"line":85,"address":[4053068],"length":1,"stats":{"Line":0}},{"line":87,"address":[4053092,4053233],"length":1,"stats":{"Line":0}},{"line":89,"address":[4053302,4053896],"length":1,"stats":{"Line":0}},{"line":90,"address":[4053671],"length":1,"stats":{"Line":0}},{"line":91,"address":[4053753],"length":1,"stats":{"Line":0}},{"line":94,"address":[4052633],"length":1,"stats":{"Line":0}},{"line":95,"address":[4053945],"length":1,"stats":{"Line":0}},{"line":96,"address":[4054019],"length":1,"stats":{"Line":0}},{"line":98,"address":[4053987,4054040],"length":1,"stats":{"Line":0}},{"line":99,"address":[4054083],"length":1,"stats":{"Line":0}},{"line":100,"address":[4054090],"length":1,"stats":{"Line":0}},{"line":102,"address":[4054141,4054051],"length":1,"stats":{"Line":0}},{"line":103,"address":[4054172],"length":1,"stats":{"Line":0}},{"line":104,"address":[4054179],"length":1,"stats":{"Line":0}},{"line":106,"address":[4054152],"length":1,"stats":{"Line":0}},{"line":107,"address":[4054420,4054231],"length":1,"stats":{"Line":0}},{"line":108,"address":[4054300],"length":1,"stats":{"Line":0}},{"line":110,"address":[4054429,4054273],"length":1,"stats":{"Line":0}},{"line":111,"address":[4054598,4054463],"length":1,"stats":{"Line":0}},{"line":113,"address":[4054440],"length":1,"stats":{"Line":0}},{"line":114,"address":[4054738],"length":1,"stats":{"Line":0}},{"line":115,"address":[4054803],"length":1,"stats":{"Line":0}},{"line":117,"address":[4054780],"length":1,"stats":{"Line":0}},{"line":118,"address":[4055078],"length":1,"stats":{"Line":0}},{"line":119,"address":[4055278,4055143],"length":1,"stats":{"Line":0}},{"line":121,"address":[4055120],"length":1,"stats":{"Line":0}},{"line":123,"address":[4055418],"length":1,"stats":{"Line":0}},{"line":124,"address":[4055450],"length":1,"stats":{"Line":0}},{"line":125,"address":[4055470],"length":1,"stats":{"Line":0}},{"line":126,"address":[4055502],"length":1,"stats":{"Line":0}},{"line":127,"address":[4055522],"length":1,"stats":{"Line":0}},{"line":131,"address":[4563808],"length":1,"stats":{"Line":0}},{"line":132,"address":[4563840,4563904],"length":1,"stats":{"Line":0}},{"line":133,"address":[4563862],"length":1,"stats":{"Line":0}},{"line":134,"address":[4055632],"length":1,"stats":{"Line":0}},{"line":135,"address":[4055649],"length":1,"stats":{"Line":0}},{"line":136,"address":[4058655,4055671],"length":1,"stats":{"Line":0}},{"line":137,"address":[4058774,4059060,4058663,4058914],"length":1,"stats":{"Line":0}},{"line":138,"address":[4059070],"length":1,"stats":{"Line":0}},{"line":139,"address":[4059081],"length":1,"stats":{"Line":0}},{"line":142,"address":[4055735],"length":1,"stats":{"Line":0}},{"line":143,"address":[4056164,4055754,4056317,4055900],"length":1,"stats":{"Line":0}},{"line":144,"address":[4056330],"length":1,"stats":{"Line":0}},{"line":145,"address":[4056903,4056486,4056341,4056750],"length":1,"stats":{"Line":0}},{"line":146,"address":[4056916],"length":1,"stats":{"Line":0}},{"line":147,"address":[4057072,4057336,4056927,4057489],"length":1,"stats":{"Line":0}},{"line":148,"address":[4057502,4058065,4057912,4057648],"length":1,"stats":{"Line":0}},{"line":149,"address":[4058481,4058634,4058217,4058078],"length":1,"stats":{"Line":0}},{"line":153,"address":[4563984],"length":1,"stats":{"Line":0}},{"line":154,"address":[4564033],"length":1,"stats":{"Line":0}},{"line":155,"address":[4564037],"length":1,"stats":{"Line":0}},{"line":156,"address":[4564108,4564317,4564041,4564203],"length":1,"stats":{"Line":0}},{"line":157,"address":[4564066],"length":1,"stats":{"Line":0}},{"line":158,"address":[4564161],"length":1,"stats":{"Line":0}},{"line":159,"address":[4564262],"length":1,"stats":{"Line":0}},{"line":160,"address":[4059134],"length":1,"stats":{"Line":0}},{"line":161,"address":[4059145],"length":1,"stats":{"Line":0}},{"line":163,"address":[4059156,4059280],"length":1,"stats":{"Line":0}},{"line":164,"address":[4060082,4059298,4060346,4060499,4059965,4060568],"length":1,"stats":{"Line":0}},{"line":165,"address":[4060520],"length":1,"stats":{"Line":0}},{"line":166,"address":[4060538,4060584],"length":1,"stats":{"Line":0}},{"line":167,"address":[4061315,4060659],"length":1,"stats":{"Line":0}},{"line":168,"address":[4061113,4060735,4060865],"length":1,"stats":{"Line":0}},{"line":169,"address":[4061295,4061388],"length":1,"stats":{"Line":0}},{"line":170,"address":[4061412],"length":1,"stats":{"Line":0}},{"line":171,"address":[4061586],"length":1,"stats":{"Line":0}},{"line":172,"address":[4061502],"length":1,"stats":{"Line":0}},{"line":173,"address":[4061538],"length":1,"stats":{"Line":0}},{"line":176,"address":[4061604],"length":1,"stats":{"Line":0}},{"line":177,"address":[4061725],"length":1,"stats":{"Line":0}},{"line":179,"address":[4061756,4061846,4062027],"length":1,"stats":{"Line":0}},{"line":180,"address":[4061783],"length":1,"stats":{"Line":0}},{"line":181,"address":[4061914,4061773],"length":1,"stats":{"Line":0}},{"line":182,"address":[4061916],"length":1,"stats":{"Line":0}},{"line":184,"address":[4061851],"length":1,"stats":{"Line":0}},{"line":185,"address":[4061979],"length":1,"stats":{"Line":0}},{"line":189,"address":[4062038],"length":1,"stats":{"Line":0}},{"line":191,"address":[4062094],"length":1,"stats":{"Line":0}},{"line":192,"address":[4062101],"length":1,"stats":{"Line":0}},{"line":195,"address":[4062108],"length":1,"stats":{"Line":0}},{"line":196,"address":[4063104,4063324,4062872,4062373,4062512],"length":1,"stats":{"Line":0}},{"line":202,"address":[4063518,4062052],"length":1,"stats":{"Line":0}},{"line":203,"address":[4063571],"length":1,"stats":{"Line":0}},{"line":207,"address":[4059942,4059765,4059921,4059342,4059232,4059501],"length":1,"stats":{"Line":0}},{"line":211,"address":[4564352],"length":1,"stats":{"Line":0}},{"line":212,"address":[4564377,4564444],"length":1,"stats":{"Line":0}},{"line":213,"address":[4564402],"length":1,"stats":{"Line":0}},{"line":214,"address":[4064760,4063616],"length":1,"stats":{"Line":0}},{"line":215,"address":[4063648],"length":1,"stats":{"Line":0}},{"line":216,"address":[4063670],"length":1,"stats":{"Line":0}},{"line":217,"address":[4063696],"length":1,"stats":{"Line":0}},{"line":218,"address":[4063778,4063707,4063872],"length":1,"stats":{"Line":0}},{"line":219,"address":[4063890,4064734],"length":1,"stats":{"Line":0}},{"line":220,"address":[4064073,4064022],"length":1,"stats":{"Line":0}},{"line":221,"address":[4064148,4064101,4064054],"length":1,"stats":{"Line":0}},{"line":222,"address":[4064108],"length":1,"stats":{"Line":0}},{"line":223,"address":[4064234,4064174,4064134,4064198],"length":1,"stats":{"Line":0}},{"line":224,"address":[4064271,4064230,4064184],"length":1,"stats":{"Line":0}},{"line":226,"address":[4064300,4064401,4064252],"length":1,"stats":{"Line":0}},{"line":227,"address":[4064441,4064426,4064345],"length":1,"stats":{"Line":0}},{"line":228,"address":[4064430],"length":1,"stats":{"Line":0}},{"line":230,"address":[4064443],"length":1,"stats":{"Line":0}},{"line":232,"address":[4064462,4064597,4064686],"length":1,"stats":{"Line":0}},{"line":233,"address":[4064488,4064665],"length":1,"stats":{"Line":0}},{"line":234,"address":[4064589],"length":1,"stats":{"Line":0}},{"line":235,"address":[4064705,4064739],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":151},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","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::dpi::LogicalSize;\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 emmagui_support;\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":19,"address":[4937728],"length":1,"stats":{"Line":0}},{"line":20,"address":[4937745],"length":1,"stats":{"Line":0}},{"line":24,"address":[4937808],"length":1,"stats":{"Line":0}},{"line":25,"address":[4937827],"length":1,"stats":{"Line":0}},{"line":27,"address":[4937872],"length":1,"stats":{"Line":0}},{"line":29,"address":[4937895],"length":1,"stats":{"Line":0}},{"line":36,"address":[],"length":0,"stats":{"Line":0}},{"line":37,"address":[],"length":0,"stats":{"Line":0}},{"line":40,"address":[4675264,4677350,4678333],"length":1,"stats":{"Line":0}},{"line":45,"address":[],"length":0,"stats":{"Line":0}},{"line":47,"address":[4675584,4675491],"length":1,"stats":{"Line":0}},{"line":48,"address":[4675892,4675706],"length":1,"stats":{"Line":0}},{"line":49,"address":[],"length":0,"stats":{"Line":0}},{"line":51,"address":[],"length":0,"stats":{"Line":0}},{"line":53,"address":[],"length":0,"stats":{"Line":0}},{"line":55,"address":[4676047,4676090],"length":1,"stats":{"Line":0}},{"line":57,"address":[],"length":0,"stats":{"Line":0}},{"line":58,"address":[],"length":0,"stats":{"Line":0}},{"line":59,"address":[],"length":0,"stats":{"Line":0}},{"line":60,"address":[4676414,4676366],"length":1,"stats":{"Line":0}},{"line":62,"address":[],"length":0,"stats":{"Line":0}},{"line":63,"address":[],"length":0,"stats":{"Line":0}},{"line":65,"address":[],"length":0,"stats":{"Line":0}},{"line":68,"address":[],"length":0,"stats":{"Line":0}},{"line":70,"address":[],"length":0,"stats":{"Line":0}},{"line":72,"address":[],"length":0,"stats":{"Line":0}},{"line":73,"address":[],"length":0,"stats":{"Line":0}},{"line":74,"address":[4677272,4677092],"length":1,"stats":{"Line":0}},{"line":77,"address":[],"length":0,"stats":{"Line":0}},{"line":80,"address":[],"length":0,"stats":{"Line":0}},{"line":83,"address":[],"length":0,"stats":{"Line":0}},{"line":85,"address":[],"length":0,"stats":{"Line":0}},{"line":87,"address":[],"length":0,"stats":{"Line":0}},{"line":88,"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":[4678860],"length":1,"stats":{"Line":0}},{"line":92,"address":[4678975],"length":1,"stats":{"Line":0}},{"line":94,"address":[],"length":0,"stats":{"Line":0}},{"line":95,"address":[],"length":0,"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":100,"address":[],"length":0,"stats":{"Line":0}},{"line":101,"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":106,"address":[],"length":0,"stats":{"Line":0}},{"line":107,"address":[],"length":0,"stats":{"Line":0}},{"line":108,"address":[],"length":0,"stats":{"Line":0}},{"line":109,"address":[],"length":0,"stats":{"Line":0}},{"line":112,"address":[],"length":0,"stats":{"Line":0}},{"line":113,"address":[4679347],"length":1,"stats":{"Line":0}},{"line":114,"address":[],"length":0,"stats":{"Line":0}},{"line":115,"address":[],"length":0,"stats":{"Line":0}},{"line":116,"address":[4679517],"length":1,"stats":{"Line":0}},{"line":117,"address":[],"length":0,"stats":{"Line":0}},{"line":118,"address":[],"length":0,"stats":{"Line":0}},{"line":119,"address":[4679581],"length":1,"stats":{"Line":0}},{"line":121,"address":[],"length":0,"stats":{"Line":0}},{"line":122,"address":[],"length":0,"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":128,"address":[4679214,4679110],"length":1,"stats":{"Line":0}},{"line":130,"address":[],"length":0,"stats":{"Line":0}},{"line":131,"address":[],"length":0,"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":142,"address":[4939500,4937952],"length":1,"stats":{"Line":0}},{"line":143,"address":[4937969],"length":1,"stats":{"Line":0}},{"line":149,"address":[4939094,4937993],"length":1,"stats":{"Line":0}},{"line":150,"address":[4938392],"length":1,"stats":{"Line":0}},{"line":153,"address":[4938069],"length":1,"stats":{"Line":0}},{"line":163,"address":[4938050],"length":1,"stats":{"Line":0}},{"line":166,"address":[4938960],"length":1,"stats":{"Line":0}},{"line":169,"address":[4938645],"length":1,"stats":{"Line":0}},{"line":175,"address":[4938526],"length":1,"stats":{"Line":0}},{"line":176,"address":[4938611],"length":1,"stats":{"Line":0}},{"line":180,"address":[4939439],"length":1,"stats":{"Line":0}},{"line":182,"address":[4939472],"length":1,"stats":{"Line":0}}],"covered":0,"coverable":85},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","computer.rs"],"content":"use log::{debug, error};\nuse crate::chip8::delay_timer::DelayTimer;\nuse crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;\nuse crate::chip8::keypad::Keypad;\nuse crate::chip8::registers::Chip8Registers;\nuse crate::chip8::sound_timer::SoundTimer;\nuse crate::chip8::stack::Chip8Stack;\nuse crate::chip8::util::InstructionUtil;\nuse crate::constants::{CHIP8_MEMORY_SIZE, CHIP8_REGISTER_COUNT};\n\nuse super::{\n cpu_states::Chip8CpuStates, instructions::Chip8CpuInstructions, system_memory::Chip8SystemMemory, video::Chip8Video,\n};\n\nconst STACK_POINTER_DEFAULT: i16 = 0x100;\n\n#[derive(Clone)]\npub struct Chip8Computer {\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}\nimpl Default for Chip8Computer {\n fn default() -\u003e Self {\n Self {\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 }\n }\n}\n\nimpl Chip8Computer {\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.video_memory.format_as_string()\n }\n\n pub fn new_with_program(new_program: Box\u003cVec\u003cu16\u003e\u003e) -\u003e Self {\n let mut working = Chip8Computer::new();\n for i in 0..new_program.len() {\n let high_byte = (new_program[i as usize] \u003e\u003e 8) as u8;\n let low_byte = (new_program[i] \u0026 0xff) as u8;\n let base_offset = i * 2;\n working.memory.poke(base_offset as u16, high_byte);\n working.memory.poke((base_offset + 1) as u16, low_byte);\n }\n working\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: Box\u003cVec\u003cu8\u003e\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 }\n\n pub fn step_system(\u0026mut self) -\u003e \u0026mut Chip8Computer {\n debug!(\"Stepping System 1 Step\");\n // read the next instruction\n\n let mut working_instruction: u16 = 0b0000000000000000;\n let start_pc = self.registers.peek_pc();\n let high_byte = (self.memory.clone().peek(start_pc) as u16).rotate_left(8);\n let low_byte = self.memory.clone().peek(start_pc + 1) as u16;\n let result = high_byte | low_byte;\n let decoded_instruction =\n Chip8CpuInstructions::decode(result);\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 self.sound_timer.tick();\n self.delay_timer.tick();\n }\n Chip8CpuStates::WaitingForKey =\u003e {\n println!(\"waiting for a key press...\");\n }\n _ =\u003e {}\n }\n self\n }\n}\n\n#[cfg(test)]\nmod test {\n use rand::random;\n use crate::constants::CHIP8_VIDEO_MEMORY;\n use super::*;\n\n #[test]\n fn smoke() {\n assert!(true)\n }\n\n}\n\n","traces":[{"line":29,"address":[19711456],"length":1,"stats":{"Line":4}},{"line":31,"address":[1703104],"length":1,"stats":{"Line":4}},{"line":32,"address":[1703114],"length":1,"stats":{"Line":4}},{"line":33,"address":[1703127],"length":1,"stats":{"Line":4}},{"line":34,"address":[1703140],"length":1,"stats":{"Line":4}},{"line":35,"address":[1703149],"length":1,"stats":{"Line":4}},{"line":36,"address":[19711547],"length":1,"stats":{"Line":4}},{"line":37,"address":[1703167],"length":1,"stats":{"Line":4}},{"line":38,"address":[1703180],"length":1,"stats":{"Line":4}},{"line":45,"address":[1703424],"length":1,"stats":{"Line":0}},{"line":46,"address":[1703441],"length":1,"stats":{"Line":0}},{"line":49,"address":[1703472],"length":1,"stats":{"Line":0}},{"line":50,"address":[1703489],"length":1,"stats":{"Line":0}},{"line":53,"address":[1703520],"length":1,"stats":{"Line":0}},{"line":54,"address":[1703545],"length":1,"stats":{"Line":0}},{"line":57,"address":[1704237,1703600],"length":1,"stats":{"Line":0}},{"line":58,"address":[1703648],"length":1,"stats":{"Line":0}},{"line":59,"address":[1703695,1703761,1703927],"length":1,"stats":{"Line":0}},{"line":60,"address":[1703948],"length":1,"stats":{"Line":0}},{"line":61,"address":[19712416],"length":1,"stats":{"Line":0}},{"line":62,"address":[1704134,1704079],"length":1,"stats":{"Line":0}},{"line":63,"address":[1704114],"length":1,"stats":{"Line":0}},{"line":64,"address":[1704159],"length":1,"stats":{"Line":0}},{"line":66,"address":[19712279],"length":1,"stats":{"Line":0}},{"line":68,"address":[1704272],"length":1,"stats":{"Line":4}},{"line":69,"address":[19712680],"length":1,"stats":{"Line":4}},{"line":72,"address":[1704304,1704672],"length":1,"stats":{"Line":0}},{"line":73,"address":[1704336,1704397],"length":1,"stats":{"Line":0}},{"line":74,"address":[19712805,19712923],"length":1,"stats":{"Line":0}},{"line":75,"address":[1704538],"length":1,"stats":{"Line":0}},{"line":76,"address":[19713051,19712996],"length":1,"stats":{"Line":0}},{"line":77,"address":[1704630,1704667],"length":1,"stats":{"Line":0}},{"line":81,"address":[19713104],"length":1,"stats":{"Line":0}},{"line":82,"address":[1704776,1705019],"length":1,"stats":{"Line":0}},{"line":85,"address":[19713161],"length":1,"stats":{"Line":0}},{"line":86,"address":[1704816],"length":1,"stats":{"Line":0}},{"line":87,"address":[1704936,1704846],"length":1,"stats":{"Line":0}},{"line":88,"address":[1704944,1705164,1705320],"length":1,"stats":{"Line":0}},{"line":89,"address":[19713605],"length":1,"stats":{"Line":0}},{"line":90,"address":[1705215],"length":1,"stats":{"Line":0}},{"line":93,"address":[1705269],"length":1,"stats":{"Line":0}},{"line":95,"address":[1705288],"length":1,"stats":{"Line":0}},{"line":97,"address":[1705354],"length":1,"stats":{"Line":0}},{"line":98,"address":[1705371],"length":1,"stats":{"Line":0}},{"line":101,"address":[1705385],"length":1,"stats":{"Line":0}}],"covered":11,"coverable":45},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","cpu_states.rs"],"content":"#[derive(Clone, Copy, Default)]\npub enum Chip8CpuStates {\n #[default]\n WaitingForInstruction,\n WaitingForKey,\n ExecutingInstruction,\n Error,\n}\n\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","delay_timer.rs"],"content":"#[derive(Clone, Copy)]\npub struct DelayTimer {\n counter: i32\n}\n\nimpl DelayTimer {\n pub fn current(\u0026self) -\u003e i32 {\n self.counter\n }\n\n pub fn new() -\u003e Self {\n DelayTimer {\n counter: 0xff\n }\n }\n pub fn set_timer(\u0026mut self, new_value: i32) {\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\n#[cfg(test)]\nmod test {\n use crate::chip8::sound_timer::SoundTimer;\n use super::*;\n\n #[test]\n fn smoke() {\n assert!(true)\n }\n\n #[test]\n fn 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]\n fn 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}","traces":[{"line":7,"address":[1838080],"length":1,"stats":{"Line":1}},{"line":8,"address":[1838085],"length":1,"stats":{"Line":1}},{"line":16,"address":[1838112],"length":1,"stats":{"Line":2}},{"line":17,"address":[1838121],"length":1,"stats":{"Line":1}},{"line":20,"address":[1838128],"length":1,"stats":{"Line":1}},{"line":21,"address":[1838142,1838181],"length":1,"stats":{"Line":2}},{"line":22,"address":[1838183,1838157],"length":1,"stats":{"Line":1}}],"covered":7,"coverable":7},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","instructions.rs"],"content":"use std::ops::{Shl, Shr};\nuse imgui::ColorPicker3;\nuse log::debug;\nuse rand::random;\nuse crate::chip8::computer::{Chip8Computer};\nuse crate::chip8::cpu_states::Chip8CpuStates::WaitingForKey;\nuse crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;\nuse crate::chip8::util::InstructionUtil;\nuse crate::chip8::video::Chip8Video;\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)]\npub enum Chip8CpuInstructions {\n /// 0nnn\n /// Exit to System Call at nnn\n SysAddr(i16),\n /// Clear Screen\n CLS,\n /// Return from Subroutine\n RET,\n /// 1nnn\n /// Jump to Address nnn\n JpAddr(i16),\n /// 2nnn\n /// Call Subroutine at nnn\n CallAddr(i16),\n /// 0x3xkk\n /// Skip next instruction if Vx == kk\n SeVxByte(i16, i16),\n /// 4xkk\n /// Skip next instruction if Vx != kk\n SneVxByte(i16, i16),\n /// 5xy0\n /// Skip next instruction if Vx == Vy\n SeVxVy(u16, u16),\n /// 6xkk\n /// Set Vx = kk\n LdVxByte(u16, u16),\n /// 7xkk\n /// Set Vx = Vx + kk\n AddVxByte(u16, u16),\n /// 8xy0\n /// Set Vx = Vy\n LdVxVy(u16, u16),\n /// 8xy1\n /// Set Vx = Vx OR Vy\n OrVxVy(u16, u16),\n /// 8xy2\n /// Set Vx = Vx AND Vy\n AndVxVy(u16, u16),\n /// 8xy3\n /// Set Vx = Vx XOR Vy\n XorVxVy(u16, u16),\n /// 8xy4\n /// Set Vx = Vx + Vy\n /// Set VF=1 if Carry\n AddVxVy(u16, u16),\n /// 8xy5\n /// Set Vx = Vx - Vy\n /// Set VF=1 if No Borrow\n SubVxVy(u16, u16),\n /// 8xy6\n /// Set Vx = Vx SHR 1\n ShrVxVy(u16, u16),\n /// 8xy7\n /// Set Vx = Vy - Vx\n /// Set VF=1 if No Borrow\n SubnVxVy(u16, u16),\n /// 8xye\n /// Set Vx = Vx SHL 1\n ShlVxVy(u16, u16),\n /// 9xy0\n /// Skip next instruction if Vx != Vy\n SneVxVy(u16, u16),\n /// Annn\n /// Load I register with NNN\n LdIAddr(u16),\n /// Bnnn\n /// Jump to nnn+V0\n JpV0Addr(u16),\n /// Cxkk\n /// Set Vx = Random u8 AND kk\n RndVxByte(u16, u16),\n /// Dxyn\n /// Display N byte tall sprite starting at Vx, Vy\n DrawVxVyNibble(u16, u16, u16),\n /// Ex9E\n /// Skip next instruction of key in Vx pressed\n SkpVx(u16),\n /// ExA1\n /// Skip Next If Key Not Pressed\n SnkpVx(u16),\n /// Fx07\n /// Set Vx = Dt\n LdVxDt(u16),\n /// Fx0A\n /// Wait for Key to be pressed and store\n /// in Vx\n LdVxK(u16),\n /// Fx15\n /// Load Value in Delay Timer to Vx\n LdDtVx(u16), // 0xFx15 Set Delay Timer\n /// Fx18\n /// Set Dt = Vx\n LdStVx(u16),\n AddIVx(u16), // 0xFx1E I = I + Vx\n LdFVx(u16), // 0xFx29 Set I = Location of sprite for Digit Vx\n LdBVx(u16), // 0xFx33 Store BCD of Vx in I, I+1, I+2\n LdIVx(u16), // 0xFx55 Store V0 to Vx in memory starting at I\n LdVxI(u16), // 0xFx65 Load V0 to Vx in memory starting at I\n XXXXERRORINSTRUCTION,\n}\n\nimpl Chip8CpuInstructions {\n pub fn encode(\u0026self) -\u003e u16 {\n match self {\n Chip8CpuInstructions::SysAddr(target) =\u003e {\n (0x0000 | (target \u0026 0x0FFF)) as u16\n }\n Chip8CpuInstructions::CLS =\u003e {\n 0x00E0\n }\n Chip8CpuInstructions::RET =\u003e {\n 0x00EE\n }\n Chip8CpuInstructions::JpAddr(new_addr) =\u003e {\n 0x1000 | (new_addr \u0026 0x0FFF) as u16\n }\n Chip8CpuInstructions::CallAddr(address) =\u003e {\n (0x2000 | (address \u0026 0x0FFF)) as u16\n }\n Chip8CpuInstructions::SeVxByte(vx_register, byte) =\u003e {\n (0x3000 | (vx_register \u003c\u003c 8 | byte) as u16)\n }\n Chip8CpuInstructions::SneVxByte(vx_register, byte) =\u003e {\n (0x4000i16 | vx_register \u003c\u003c 8 | byte) as u16\n }\n Chip8CpuInstructions::SeVxVy(x_register, y_register) =\u003e {\n 0x5000u16 | (x_register \u003c\u003c 8 | y_register \u003c\u003c 4)\n }\n Chip8CpuInstructions::LdVxByte(x_register, byte) =\u003e {\n 0x6000u16 | x_register \u003c\u003c 8 | byte\n }\n Chip8CpuInstructions::AddVxByte(x_register, byte) =\u003e {\n 0x7000u16 | x_register \u003c\u003c 8 | byte\n }\n Chip8CpuInstructions::LdVxVy(x_register, y_register) =\u003e {\n 0x8000u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::OrVxVy(x_register, y_register) =\u003e {\n 0x8001u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::AndVxVy(x_register, y_register) =\u003e {\n 0x8002u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::XorVxVy(x_register, y_register) =\u003e {\n 0x8003u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::AddVxVy(x_register, y_register) =\u003e {\n 0x8004u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::SubVxVy(x_register, y_register) =\u003e {\n 0x8005u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::ShrVxVy(x_register, y_register) =\u003e {\n 0x8006u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::SubnVxVy(x_register, y_register) =\u003e {\n 0x8007u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::ShlVxVy(x_register, y_register) =\u003e {\n 0x800Eu16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::SneVxVy(x_register, y_register) =\u003e {\n 0x9000u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4\n }\n Chip8CpuInstructions::LdIAddr(addr) =\u003e {\n 0xA000u16 | addr\n }\n Chip8CpuInstructions::JpV0Addr(addr) =\u003e {\n 0xB000u16 | addr\n }\n Chip8CpuInstructions::RndVxByte(x_register, byte) =\u003e {\n 0xC000u16 | x_register \u003c\u003c 8 | byte\n }\n Chip8CpuInstructions::DrawVxVyNibble(x_register, y_register, height) =\u003e {\n 0xD000u16 | x_register \u003c\u003c 8 | y_register \u003c\u003c 4 | height\n }\n Chip8CpuInstructions::SkpVx(x_register) =\u003e {\n 0xE09Eu16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::SnkpVx(x_register) =\u003e {\n 0xE0A1u16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdVxDt(x_register) =\u003e {\n 0xF007u16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdVxK(x_register) =\u003e {\n 0xF00Au16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdDtVx(x_register) =\u003e {\n 0xF015u16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdStVx(x_register) =\u003e {\n 0xF018u16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::AddIVx(x_register) =\u003e {\n 0xF01Eu16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdFVx(x_register) =\u003e {\n 0xF029u16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdBVx(x_register) =\u003e {\n 0xf033u16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdIVx(x_register) =\u003e {\n 0xf055u16 | x_register \u003c\u003c 8\n }\n Chip8CpuInstructions::LdVxI(x_register) =\u003e {\n 0xf065u16 | x_register \u003c\u003c 8\n }\n XXXXERRORINSTRUCTION =\u003e {\n 0xffff\n }\n }\n }\n pub fn decode(input: u16) -\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;\n\n match input {\n 0x00E0 =\u003e {\n // 00E0 - CLS\n // Clear the display.\n Chip8CpuInstructions::CLS\n }\n 0x00EE =\u003e {\n // 00EE - RET\n // Return from a subroutine.\n Chip8CpuInstructions::RET\n }\n 0x0000..=0x0FFF =\u003e {\n // 0nnn - SYS addr\n // Jump to a machine code routine at nnn.\n Chip8CpuInstructions::SysAddr(addr_param as i16)\n }\n 0x1000..=0x1FFF =\u003e {\n // 1nnn - JP addr\n // Jump to location nnn.\n Chip8CpuInstructions::JpAddr(addr_param as i16)\n }\n 0x2000..=0x2FFF =\u003e {\n // 2nnn - CALL addr\n // Call subroutine at nnn.\n Chip8CpuInstructions::CallAddr(addr_param as i16)\n }\n 0x3000..=0x3FFF =\u003e {\n // 3xkk - SE Vx, byte\n // Skip next instruction if Vx = kk.\n Chip8CpuInstructions::SeVxByte(x_param as i16, byte_param as i16)\n }\n 0x4000..=0x4FFF =\u003e {\n // 4xkk - SNE Vx, byte\n // Skip next instruction if Vx != kk.\n Chip8CpuInstructions::SneVxByte(x_param as i16, byte_param as i16)\n }\n 0x5000..=0x5FF0 =\u003e {\n // 5xy0 - SE Vx, Vy\n // Skip next instruction if Vx = Vy.\n // if the last nibble isn't 0...\n if 0xf \u0026 input \u003e 0 {\n // ... we have a problem.\n Chip8CpuInstructions::XXXXERRORINSTRUCTION\n } else {\n Chip8CpuInstructions::SeVxVy(x_param, y_param)\n }\n }\n 0x6000..=0x6FFF =\u003e {\n // 6xkk - LD Vx, byte\n // Set Vx = kk.\n Chip8CpuInstructions::LdVxByte(x_param, byte_param)\n }\n 0x7000..=0x7FFF =\u003e {\n // ADD Vx, Byte\n Chip8CpuInstructions::AddVxByte(x_param, byte_param)\n }\n 0x8000..=0x8FFE =\u003e {\n // 0x8000 Series\n let last_nibble = input \u0026 0xF;\n match last_nibble {\n 0x0 =\u003e {\n // LD Vx, Vy\n Chip8CpuInstructions::LdVxVy(x_param, y_param)\n }\n 0x1 =\u003e {\n // OR Vx, Vy\n Chip8CpuInstructions::OrVxVy(x_param, y_param)\n }\n 0x2 =\u003e {\n // AND Vx, Vy\n Chip8CpuInstructions::AndVxVy(x_param, y_param)\n }\n 0x3 =\u003e {\n // XOR Vx, Vy\n Chip8CpuInstructions::XorVxVy(x_param, y_param)\n }\n 0x4 =\u003e {\n // ADD Vx, Vy\n Chip8CpuInstructions::AddVxVy(x_param, y_param)\n }\n 0x5 =\u003e {\n // SUB Vx, Vy\n Chip8CpuInstructions::SubVxVy(x_param, y_param)\n }\n 0x6 =\u003e {\n // SHR Vx, {, Vy }\n Chip8CpuInstructions::ShrVxVy(x_param, y_param)\n }\n 0x7 =\u003e {\n // SUBN Vx, Vy\n Chip8CpuInstructions::SubnVxVy(x_param, y_param)\n }\n 0xE =\u003e {\n // SHL Vx, {, Vy}\n Chip8CpuInstructions::ShlVxVy(x_param, y_param)\n }\n _ =\u003e {\n Chip8CpuInstructions::XXXXERRORINSTRUCTION\n }\n }\n }\n 0x9000..=0x9FF0 =\u003e {\n // SNE Vx, Vy\n if 0xf \u0026 input \u003e 0 {\n XXXXERRORINSTRUCTION\n } else {\n Chip8CpuInstructions::SneVxVy(x_param, y_param)\n }\n }\n 0xA000..=0xAFFF =\u003e {\n // LD I, Addr\n Chip8CpuInstructions::LdIAddr(addr_param)\n }\n 0xB000..=0xBFFF =\u003e {\n // JP V0, Addr\n Chip8CpuInstructions::JpV0Addr(addr_param)\n }\n 0xC000..=0xCFFF =\u003e {\n // RND Vx, byte\n Chip8CpuInstructions::RndVxByte(x_param, byte_param)\n }\n 0xD000..=0xDFFF =\u003e {\n // DRAW Vx, Vy, nibble\n Chip8CpuInstructions::DrawVxVyNibble(x_param, y_param, nibble_param)\n }\n 0xE09E..=0xEFA1 =\u003e {\n match last_byte {\n 0x9E =\u003e {\n Chip8CpuInstructions::SkpVx(ubln)\n }\n 0xA1 =\u003e {\n Chip8CpuInstructions::SnkpVx(ubln)\n }\n _ =\u003e {\n XXXXERRORINSTRUCTION\n }\n }\n }\n 0xF007..=0xFF65 =\u003e {\n match last_byte {\n 0x07 =\u003e {\n Chip8CpuInstructions::LdVxDt(ubln)\n }\n 0x0A =\u003e {\n Chip8CpuInstructions::LdVxK(ubln)\n }\n 0x15 =\u003e {\n Chip8CpuInstructions::LdDtVx(ubln)\n }\n 0x18 =\u003e {\n Chip8CpuInstructions::LdStVx(ubln)\n }\n 0x1E =\u003e {\n Chip8CpuInstructions::AddIVx(ubln)\n }\n 0x29 =\u003e {\n Chip8CpuInstructions::LdFVx(ubln)\n }\n 0x33 =\u003e {\n Chip8CpuInstructions::LdBVx(ubln)\n }\n 0x55 =\u003e {\n Chip8CpuInstructions::LdIVx(ubln)\n }\n 0x65 =\u003e {\n Chip8CpuInstructions::LdVxI(ubln)\n }\n _ =\u003e {\n XXXXERRORINSTRUCTION\n }\n }\n }\n _ =\u003e {\n XXXXERRORINSTRUCTION\n }\n }\n }\n\n pub fn execute(\u0026self, mut input: \u0026mut Chip8Computer) -\u003e Chip8Computer {\n let start_pc = input.registers.peek_pc();\n input.registers.poke_pc(start_pc + 2);\n let _ = match self {\n // 0x0nnn Exit to System Call\n Chip8CpuInstructions::SysAddr(new_address) =\u003e {\n debug!(\"SysAddr [0x{new_address:3x}]\");\n\n input.registers.poke_pc(*new_address as u16);\n }\n // * 0x00E0 Clear Screen\n Chip8CpuInstructions::CLS =\u003e {\n for i in 0..(64 * 32) {\n input.video_memory.poke(i, false);\n }\n }\n // 0x00EE Return from Subroutine\n Chip8CpuInstructions::RET =\u003e {\n let return_address = input.stack.pop();\n debug!(\"Returning from subroutine to {return_address:03x}\");\n input.registers.poke_pc(return_address);\n }\n // 0x1nnn Jump to Address\n Chip8CpuInstructions::JpAddr(new_address) =\u003e {\n debug!(\"JpAddr [0x{new_address:3x}]\");\n input.registers.poke_pc(*new_address as u16);\n }\n // 0x2nnn Call Subroutine\n Chip8CpuInstructions::CallAddr(new_address) =\u003e {\n debug!(\"CALL ADDR 0x{new_address:3x}\");\n let return_address = input.registers.peek_pc();\n input.registers.poke_pc(*new_address as u16);\n input.stack.push(\u0026return_address);\n }\n // 0x3xkk Skip next instruction if Vx = kk.\n Chip8CpuInstructions::SeVxByte(vx_register, byte) =\u003e {\n if input.registers.peek(*vx_register as u8) == *byte as u8 {\n input.registers.advance_pc();\n }\n debug!(\"SeVxByte [0x{vx_register:1x}] [0x{byte:2x}]\");\n }\n // 0x4xkk Skip next instruction if Vx != kk\n Chip8CpuInstructions::SneVxByte(x, byte) =\u003e {\n if input.registers.peek(*x as u8) != *byte as u8 {\n input.registers.advance_pc();\n }\n debug!(\"SneVxByte [0x{x:1x}] [0x{byte:2x}\");\n }\n // 0x5xy0 Skip next instruction if Vx == Vy\n Chip8CpuInstructions::SeVxVy(x, y) =\u003e {\n let lhs = input.registers.peek(*x as u8);\n let rhs = input.registers.peek(*y as u8);\n\n if lhs == rhs {\n input.registers.advance_pc();\n }\n debug!(\"SeVxVy [0x{x:1x}] [0x{y:1x}]\");\n }\n // 0x6xkk Set Vx = kk\n Chip8CpuInstructions::LdVxByte(register, byte) =\u003e {\n let byte_value = *byte as u8;\n input.registers.poke(*register as u8, byte_value);\n debug!(\"LdVxByte [0x{register:1x}] [0x{byte:2.0x}]\");\n }\n // 0x7xkk Set Vx = Vx + kk\n Chip8CpuInstructions::AddVxByte(vx_register, byte) =\u003e {\n let x_value: u16 = input.registers.peek(*vx_register as u8) as u16;\n let value_to_poke = (x_value + *byte) as u8;\n input.registers.poke(*vx_register as u8, value_to_poke);\n debug!(\"AddVxByte [0x{vx_register:1x}] [0x{byte:02x}] [0x{value_to_poke:02x}\");\n }\n // 0x8xy0 Set value of Vy in Vx\n Chip8CpuInstructions::LdVxVy(x, y) =\u003e {\n input.registers.poke(*x as u8, input.registers.peek(*y as u8));\n debug!(\"LdVxVy [0x{x:1x}] [0x{y:1x}]\");\n }\n // 0x8xy1 Set Vx = Vx OR Vy\n Chip8CpuInstructions::OrVxVy(x, y) =\u003e {\n // shift them to 16 bit\n let working_16_x = input.registers.peek(*x as u8) as u16;\n let working_16_y = input.registers.peek(*y as u8) as u16;\n // OR them\n let working_16_or = working_16_x | working_16_y;\n // shift them back to 8 bit.\n let to_set = working_16_or as u8;\n input.registers.poke(*x as u8, to_set);\n debug!(\"OrVxVy [0x{x:1x}] [0x{y:1x}]\")\n }\n // 0x8xy2 Set Vx = Vx AND Vy\n Chip8CpuInstructions::AndVxVy(x, y) =\u003e {\n let lhs_16 = input.registers.peek(*x as u8) as u16;\n let rhs_16 = input.registers.peek(*y as u8) as u16;\n let and_16 = lhs_16 \u0026 rhs_16;\n\n input.registers.poke(*x as u8, and_16 as u8);\n debug!(\"AndVxVy [0x{x:02x}] [0x{y:02x}] [0b{and_16:08b}\");\n }\n // 0x8xy3 Set Vx = Vx XOR Vy\n Chip8CpuInstructions::XorVxVy(x, y) =\u003e {\n let lhs_16 = input.registers.peek(*x as u8) as u16;\n let rhs_16 = input.registers.peek(*y as u8) as u16;\n let xor_16 = lhs_16 ^ rhs_16;\n input.registers.poke(*x as u8, xor_16 as u8);\n }\n // 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)\n Chip8CpuInstructions::AddVxVy(x, y) =\u003e {\n let lhs = input.registers.peek(*x as u8) as i16;\n let rhs = input.registers.peek(*y as u8) as i16;\n let working = lhs + rhs;\n if working \u003e 0xff {\n input.registers.poke(0xf, 0x01);\n }\n input.registers.poke(*x as u8, working as u8);\n }\n Chip8CpuInstructions::SubVxVy(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 let mut x_value: u16 = input.registers.peek(*x as u8) as u16;\n let y_value = input.registers.peek(*y as u8);\n // do we borrow?\n if y_value \u003e= x_value as u8 {\n x_value += 256;\n input.registers.poke(0xf, 1);\n } else {\n input.registers.poke(0xf, 0);\n }\n let result = (x_value - y_value as u16) as u8;\n input.registers.poke(*x as u8, result);\n }\n Chip8CpuInstructions::ShrVxVy(x, _) =\u003e {\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, otherwise 0. Then Vx is divided by 2.\n let initial_value = input.registers.peek(*x as u8);\n if 0xb1 \u0026 initial_value == 1 {\n input.registers.poke(0xf, 1);\n }\n input.registers.poke(*x as u8, initial_value.shr(1));\n }\n Chip8CpuInstructions::SubnVxVy(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 as u8);\n let x_register = input.registers.peek(*x as u8);\n let new_value = if y_register \u003c= x_register { 1 } else { 0 };\n let value_to_poke = if y_register \u003c= x_register {\n ((y_register as u16 + 256) - x_register as u16) as u8\n } else {\n y_register - x_register\n };\n input.registers.poke(0xf, new_value);\n input.registers.poke(*x as u8, value_to_poke);\n }\n\n Chip8CpuInstructions::ShlVxVy(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 as u8);\n if 0x80 \u0026 initial_value == 0x80 {\n input.registers.poke(0xf, 1);\n }\n input.registers.poke(*x as u8, initial_value.shl(1));\n }\n Chip8CpuInstructions::SneVxVy(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 as u8);\n let y_reg_value = input.registers.peek(*vy_register as u8);\n if x_reg_value != y_reg_value {\n input.registers.advance_pc();\n }\n }\n Chip8CpuInstructions::LdIAddr(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::JpV0Addr(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.registers.poke_pc(input.registers.peek(0) as u16 + addr);\n }\n Chip8CpuInstructions::RndVxByte(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 new_value: u8 = random();\n input.registers.poke(*x as u8, (new_value \u0026 *byte as u8))\n }\n Chip8CpuInstructions::DrawVxVyNibble(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\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\n let x_offset = input.registers.peek(*x as u8);\n let y_offset = input.registers.peek(*y as u8);\n\n println!(\"X_OFFSET = {x_offset} / y_offset = {y_offset}\");\n let target_memory_offset = x_offset as u16 * 64 + y_offset as u16;\n\n println!(\"CHIP8CPUINSTRUCTION:DRAWVXNIBBLE -\u003e STARTING AT {source_memory_offset} WRITING TO {target_memory_offset}\");\n let num_bytes_to_read = *n;\n println!(\"CHIP8CPUINSTRUCTION:DRAWVXNIBBLE -\u003e PREPARING TO READ {num_bytes_to_read} BYTES FROM MEMORY TO VIDEO\");\n for byte_index in 0..num_bytes_to_read {\n let current_byte = input.memory.peek(byte_index + source_memory_offset);\n println!(\"CHIP8CPUINSTRUCTION:DRAWVXNIBBLE -\u003e READ BYTE [0x{byte_index:2x}]\\t{current_byte:02x}\\t{current_byte:08b}\");\n for bit_index in 0..8 {\n let data_offset = ((x_offset as u16 + byte_index) * 64 ) + (y_offset + bit_index) as u16;\n let current_bit = (current_byte.shr(7 - bit_index) \u0026 0x1u8) == 0x1u8;\n input.video_memory.poke(data_offset, current_bit);\n }\n }\n\n println!(\"PPOOSSTT -\u003e CHIP8CPUINSTRUCTION:DRAWVXNIBBLE -\u003e {}\", input.video_memory.format_as_string());\n\n let mut did_change: bool = false;\n\n if did_change {\n input.registers.poke(0xf, 1u8);\n } else {\n input.registers.poke(0xf, 0u8);\n }\n }\n Chip8CpuInstructions::SkpVx(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 as u8);\n let is_pressed = input.keypad.pressed(key_to_check);\n if is_pressed {\n input.registers.advance_pc();\n }\n }\n Chip8CpuInstructions::SnkpVx(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 as u8);\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::LdVxDt(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 as u8, value_to_set as u8);\n }\n Chip8CpuInstructions::LdVxK(x) =\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::LdDtVx(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 as u8);\n input.delay_timer.set_timer(new_time as i32);\n }\n Chip8CpuInstructions::LdStVx(new_time) =\u003e {\n let new_value = input.registers.peek(*new_time as u8);\n input.sound_timer.set_timer(new_value as i32);\n }\n Chip8CpuInstructions::AddIVx(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 as u8);\n input.registers.poke_i(base + x_value as u16);\n }\n Chip8CpuInstructions::LdFVx(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 to_offset = input.registers.peek(*x as u8) - 1;\n let real_offset = to_offset as u16 * 5;\n input.registers.poke_i(real_offset as u16);\n }\n Chip8CpuInstructions::LdBVx(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 as u8);\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 // Convert to BCD\n let result = ((hundreds as u16) \u003c\u003c 8) | units as u16;\n (tens \u003c\u003c 4) | units;\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::LdIVx(x) =\u003e {\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 offset = input.registers.peek_i();\n for i in 0..*x {\n input.memory.poke(offset + i, input.registers.peek(i as u8));\n }\n input.registers.poke_i(offset + 1);\n }\n Chip8CpuInstructions::LdVxI(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 println!(\"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 as u8, src_value);\n println!(\"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 };\n input.clone()\n }\n}\n\n#[cfg(test)]\nmod test {\n use dimensioned::typenum::assert_type;\n use ratatui::crossterm::execute;\n use crate::chip8::cpu_states::Chip8CpuStates;\n use crate::chip8::system_memory::{CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_9};\n use super::*;\n\n #[test]\n fn smoke() {\n assert!(true)\n }\n\n #[test]\n fn encode_decode_test() {\n assert_eq!(Chip8CpuInstructions::CLS.encode(), 0x00E0);\n assert_eq!(Chip8CpuInstructions::RET.encode(), 0x00EE);\n assert_eq!(Chip8CpuInstructions::SysAddr(0x123).encode(), 0x0123);\n assert_eq!(Chip8CpuInstructions::JpAddr(0x234).encode(), 0x1234);\n assert_eq!(Chip8CpuInstructions::CallAddr(0x345).encode(), 0x2345);\n assert_eq!(Chip8CpuInstructions::SeVxByte(0x4, 0x56).encode(), 0x3456);\n assert_eq!(Chip8CpuInstructions::SneVxByte(0xa, 0xbc).encode(), 0x4abc);\n assert_eq!(Chip8CpuInstructions::SeVxVy(0xa, 0xb).encode(), 0x5ab0);\n assert_eq!(Chip8CpuInstructions::LdVxByte(0xa, 0xff).encode(), 0x6aff);\n assert_eq!(Chip8CpuInstructions::AddVxByte(0xa, 0xbc).encode(), 0x7abc);\n assert_eq!(Chip8CpuInstructions::LdVxVy(0xa, 0xb).encode(), 0x8ab0);\n assert_eq!(Chip8CpuInstructions::OrVxVy(0xb, 0xa).encode(), 0x8ba1);\n assert_eq!(Chip8CpuInstructions::AndVxVy(0xc, 0xd).encode(), 0x8cd2);\n assert_eq!(Chip8CpuInstructions::XorVxVy(0xd, 0xe).encode(), 0x8de3);\n assert_eq!(Chip8CpuInstructions::AddVxVy(0xe, 0xf).encode(), 0x8ef4);\n assert_eq!(Chip8CpuInstructions::SubVxVy(0xf, 0x0).encode(), 0x8f05);\n assert_eq!(Chip8CpuInstructions::ShrVxVy(0x0, 0x1).encode(), 0x8016);\n assert_eq!(Chip8CpuInstructions::SubnVxVy(0x1, 0x2).encode(), 0x8127);\n assert_eq!(Chip8CpuInstructions::ShlVxVy(0x3, 0x4).encode(), 0x834e);\n assert_eq!(Chip8CpuInstructions::SneVxVy(0xa, 0xb).encode(), 0x9ab0);\n assert_eq!(Chip8CpuInstructions::LdIAddr(0x123).encode(), 0xa123);\n assert_eq!(Chip8CpuInstructions::JpV0Addr(0x234).encode(), 0xb234);\n assert_eq!(Chip8CpuInstructions::RndVxByte(0xa, 0xca).encode(), 0xcaca);\n assert_eq!(Chip8CpuInstructions::DrawVxVyNibble(0xa, 0xb, 0x4).encode(), 0xdab4);\n assert_eq!(Chip8CpuInstructions::SkpVx(0x1).encode(), 0xe19e);\n assert_eq!(Chip8CpuInstructions::SnkpVx(0x2).encode(), 0xe2a1);\n assert_eq!(Chip8CpuInstructions::LdVxDt(0x1).encode(), 0xf107);\n assert_eq!(Chip8CpuInstructions::LdVxK(0x4).encode(), 0xf40a);\n assert_eq!(Chip8CpuInstructions::LdDtVx(0x6).encode(), 0xf615);\n assert_eq!(Chip8CpuInstructions::LdStVx(0xb).encode(), 0xfb18);\n assert_eq!(Chip8CpuInstructions::AddIVx(0xd).encode(), 0xfd1e);\n assert_eq!(Chip8CpuInstructions::LdFVx(0xc).encode(), 0xfc29);\n assert_eq!(Chip8CpuInstructions::LdBVx(0xd).encode(), 0xfd33);\n assert_eq!(Chip8CpuInstructions::LdIVx(0xe).encode(), 0xfe55);\n assert_eq!(Chip8CpuInstructions::LdVxI(0x3).encode(), 0xf365);\n assert!(matches!(Chip8CpuInstructions::decode(0x00E0u16), Chip8CpuInstructions::CLS));\n assert!(matches!(Chip8CpuInstructions::decode(0x00EEu16), Chip8CpuInstructions::RET));\n assert!(matches!(Chip8CpuInstructions::decode(0x0123), Chip8CpuInstructions::SysAddr(0x123)));\n assert!(matches!(Chip8CpuInstructions::decode(0x0FFF), Chip8CpuInstructions::SysAddr(0xfff)));\n assert!(matches!(Chip8CpuInstructions::decode(0x1002), Chip8CpuInstructions::JpAddr(0x2)));\n assert!(matches!(Chip8CpuInstructions::decode(0x1FF0), Chip8CpuInstructions::JpAddr(0xFF0)));\n assert!(matches!(Chip8CpuInstructions::decode(0x2002), Chip8CpuInstructions::CallAddr(0x2)));\n assert!(matches!(Chip8CpuInstructions::decode(0x3123), Chip8CpuInstructions::SeVxByte(0x1, 0x23)));\n assert!(matches!(Chip8CpuInstructions::decode(0x4abc), Chip8CpuInstructions::SneVxByte(0xa, 0xbc)));\n assert!(matches!(Chip8CpuInstructions::decode(0x5ab0), Chip8CpuInstructions::SeVxVy(0xa, 0xb)));\n assert!(matches!(Chip8CpuInstructions::decode(0x6aff), Chip8CpuInstructions::LdVxByte(0xa, 0xff)));\n assert!(matches!(Chip8CpuInstructions::decode(0x7abc), Chip8CpuInstructions::AddVxByte(0xa, 0xbc)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8ab0), Chip8CpuInstructions::LdVxVy(0xa, 0xb)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8ba1), Chip8CpuInstructions::OrVxVy(0xb, 0xa)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8cd2), Chip8CpuInstructions::AndVxVy(0xc, 0xd)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8de3), Chip8CpuInstructions::XorVxVy(0xd, 0xe)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8ef4), Chip8CpuInstructions::AddVxVy(0xe, 0xf)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8f05), Chip8CpuInstructions::SubVxVy(0xf, 0x0)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8016), Chip8CpuInstructions::ShrVxVy(0x0, 0x1)));\n assert!(matches!(Chip8CpuInstructions::decode(0x8127), Chip8CpuInstructions::SubnVxVy(0x1, 0x2)));\n assert!(matches!(Chip8CpuInstructions::decode(0x834e), Chip8CpuInstructions::ShlVxVy(0x3, 0x4)));\n assert!(matches!(Chip8CpuInstructions::decode(0x9ab0), Chip8CpuInstructions::SneVxVy(0xa, 0xb)));\n assert!(matches!(Chip8CpuInstructions::decode(0xa123), Chip8CpuInstructions::LdIAddr(0x123)));\n assert!(matches!(Chip8CpuInstructions::decode(0xb234), Chip8CpuInstructions::JpV0Addr(0x234)));\n assert!(matches!(Chip8CpuInstructions::decode(0xcaca), Chip8CpuInstructions::RndVxByte(0xa, 0xca)));\n assert!(matches!(Chip8CpuInstructions::decode(0xdab4), Chip8CpuInstructions::DrawVxVyNibble(0xa, 0xb, 0x4)));\n assert!(matches!(Chip8CpuInstructions::decode(0xe19e), Chip8CpuInstructions::SkpVx(0x1)));\n assert!(matches!(Chip8CpuInstructions::decode(0xe2a1), Chip8CpuInstructions::SnkpVx(0x2)));\n assert!(matches!(Chip8CpuInstructions::decode(0xf107), Chip8CpuInstructions::LdVxDt(0x1)));\n assert!(matches!(Chip8CpuInstructions::decode(0xf40a), Chip8CpuInstructions::LdVxK(0x4)));\n assert!(matches!(Chip8CpuInstructions::decode(0xf615), Chip8CpuInstructions::LdDtVx(0x6)));\n assert!(matches!(Chip8CpuInstructions::decode(0xfb18), Chip8CpuInstructions::LdStVx(0xb)));\n assert!(matches!(Chip8CpuInstructions::decode(0xfd1e), Chip8CpuInstructions::AddIVx(0xd)));\n assert!(matches!(Chip8CpuInstructions::decode(0xfc29), Chip8CpuInstructions::LdFVx(0xc)));\n assert!(matches!(Chip8CpuInstructions::decode(0xfd33), Chip8CpuInstructions::LdBVx(0xd)));\n assert!(matches!(Chip8CpuInstructions::decode(0xfe55), Chip8CpuInstructions::LdIVx(0xe)));\n assert!(matches!(Chip8CpuInstructions::decode(0xf365), Chip8CpuInstructions::LdVxI(0x3)));\n }\n\n #[test]\n fn decoder_test_invalid_instructions() {\n let invalid_to_encode = [\n 0x5ab1, 0x5abf, 0x8ab8, 0x8abd, 0x8abf,\n 0x9ab1, 0x9abf, 0xea9d, 0xea9f, 0xeaa0,\n 0xeaa2, 0xf006, 0xf008\n ];\n\n for i in invalid_to_encode {\n assert_eq!(Chip8CpuInstructions::decode(i).encode(), 0xffff);\n assert!(matches!(Chip8CpuInstructions::decode(i), Chip8CpuInstructions::XXXXERRORINSTRUCTION));\n }\n }\n\n /// START OF THE EXECUTION TESTS\n #[test]\n fn sys_test() {\n // 0x0nnn Exit to System Call\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::SysAddr(0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::SysAddr(0xFA0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0xFA0);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::SysAddr(0x0AF).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x0AF);\n }\n\n #[test]\n fn jpaddr_test() {\n // 0x1nnn Jump to Address\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::JpAddr(0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::JpAddr(0xABC).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0xABC);\n }\n\n // ** test moved up so it can be used later\n #[test]\n fn ld_vx_byte_test() {\n // 0x6xkk Set Vx = kk\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::LdVxByte(1, 0x12).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0x12);\n\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::LdVxByte(2, 0x21).execute(\u0026mut x);\n assert_eq!(x.registers.peek(2), 0x21);\n }\n\n #[test]\n fn sevxbyte_match_test() {\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::SeVxByte(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::SeVxByte(1, 0x84).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204);\n }\n\n #[test]\n fn se_vx_vy_test() {\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::SeVxVy(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::SeVxVy(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n }\n\n #[test]\n fn ld_vx_vy_test() {\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::LdVxVy(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0x02);\n }\n\n #[test]\n fn or_vx_vy_test() {\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::OrVxVy(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0b01011010);\n }\n\n #[test]\n fn and_vx_vy_test() {\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::AndVxVy(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0xC8);\n }\n\n #[test]\n fn xor_vx_vy_test() {\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::XorVxVy(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0b00110110);\n }\n\n #[test]\n fn add_vx_vy_test() {\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::AddVxVy(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::AddVxVy(1, 2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0xf), 1);\n assert_eq!(x.registers.peek(1), 0);\n }\n\n #[test]\n fn shr_vx_vy_test() {\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::ShrVxVy(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::ShrVxVy(0x1, 0x2).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0b00000100);\n assert_eq!(x.registers.peek(0xf), 1);\n }\n\n #[test]\n fn ldi_addr_test() {\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::LdIAddr(0x123).execute(\u0026mut x);\n assert_eq!(x.registers.peek_i(), 0x123);\n assert_eq!(x.registers.peek_pc(), 0x202);\n }\n\n #[test]\n fn jp_v0addr_test() {\n let mut x = Chip8Computer::new();\n /// jump to I + nnn\n x.registers.poke(0x0, 0xff);\n Chip8CpuInstructions::JpV0Addr(0x100).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x1FF);\n }\n\n #[test]\n fn cls_test() {\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::CLS.execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n }\n\n #[test]\n fn skip_next_instruction_ne_text() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xf0);\n Chip8CpuInstructions::SneVxByte(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::SneVxByte(0x1, 0xf0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n }\n\n #[test]\n fn addivx_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke_i(0xabc);\n x.registers.poke(0x0, 0x10);\n Chip8CpuInstructions::AddIVx(0x0).execute(\u0026mut x);\n assert_eq!(x.registers.peek_i(), 0xacc);\n }\n\n #[test]\n fn ldstvt_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xf0);\n Chip8CpuInstructions::LdStVx(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]\n fn rnd_vx_byte_text() {\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::RndVxByte(0x1, 0x0f).execute(\u0026mut x);\n let new_value = x.registers.peek(0x1);\n assert!(new_value \u003c 0x10);\n }\n\n #[test]\n fn 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::AddVxByte(0x1, 0x10).execute(\u0026mut x);\n assert_eq!(x.registers.peek(1), 0xbb);\n }\n\n #[test]\n fn 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::SubVxVy(0x1, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0xf), 0);\n assert_eq!(x.registers.peek(0x1), 0x8);\n assert_eq!(x.registers.peek_pc(), 0x202);\n }\n\n #[test]\n fn sne_vx_vy_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0x10);\n x.registers.poke(0x2, 0x10);\n Chip8CpuInstructions::SneVxVy(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::SneVxVy(0x01, 0x02).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204)\n }\n\n #[test]\n fn ld_dt_vx_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0x10);\n Chip8CpuInstructions::LdDtVx(0x1).execute(\u0026mut x);\n assert_eq!(x.delay_timer.current(), 0x10);\n for i in 0..0x20 {\n x.delay_timer.tick();\n }\n assert_eq!(x.delay_timer.current(), 0);\n }\n\n #[test]\n fn ld_vx_dt_test() {\n let mut x = Chip8Computer::new();\n x.registers.poke(0x1, 0xf0);\n Chip8CpuInstructions::LdDtVx(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]\n fn 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::SubnVxVy(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), 0x0);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0xab);\n x.registers.poke(0x02, 0xa0);\n Chip8CpuInstructions::SubnVxVy(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), 0x1);\n }\n\n #[test]\n fn shl_vx_vy_test_1() {\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::ShlVxVy(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::ShlVxVy(0x1, 0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek(0x1), 0b01010100);\n assert_eq!(x.registers.peek(0xf), 0x1);\n }\n\n #[test]\n fn ld_f_vx_test() {\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::LdFVx(0x1).execute(\u0026mut x);\n\n assert_eq!(x.registers.peek_i(), 0x5);\n\n let mut x = Chip8Computer::new();\n x.registers.poke(0x01, 0x06);\n Chip8CpuInstructions::LdFVx(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_i(), 25);\n }\n\n #[test]\n fn ld_b_vx_test() {\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::LdBVx(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\n #[test]\n fn ld_i_vx_test() {\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::LdIVx(to_load.len() as u16).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\n #[test]\n fn ld_vx_i_test() {\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::LdVxI(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 }\n\n #[test]\n fn Sknkpvx_test() {\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::SnkpVx(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x202);\n x.keypad.release_key(0x5);\n Chip8CpuInstructions::SnkpVx(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x206);\n }\n\n #[test]\n fn skpvx_test() {\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::SkpVx(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x204);\n\n x.keypad.release_key(0x5);\n Chip8CpuInstructions::SkpVx(0x1).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x206);\n }\n\n #[test]\n fn draw_nibble_vx_vy_n_test() {\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::DrawVxVyNibble(x_register as u16, y_register as u16, 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 = (x_offset\n 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 println!(\"ROWDATA = \\t\\t[{row_data:08b}]\\tBIT IN BYTE = \\t[{bit_in_byte}]\\tONE_SHIFT_SET = [{one_shift_set}]\\tSHIFTED ONE = [{shifted_one:08b}]\");\n println!(\"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]\n fn sub_test() {\n // 2nnn\n // Call a subroutine at 2nnn\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::CallAddr(0x124).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x124);\n assert_eq!(x.stack.depth(), 1);\n Chip8CpuInstructions::CallAddr(0x564).execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x564);\n assert_eq!(x.stack.depth(), 2);\n }\n\n #[test]\n fn ret_test() {\n // Return from a subroutine.\n let mut x = Chip8Computer::new();\n x.stack.push(\u00260x132);\n x.stack.push(\u00260xabc);\n Chip8CpuInstructions::RET.execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0xabc);\n assert_eq!(x.stack.depth(), 1);\n Chip8CpuInstructions::RET.execute(\u0026mut x);\n assert_eq!(x.registers.peek_pc(), 0x132);\n assert_eq!(x.stack.depth(), 0);\n }\n\n\n #[test]\n fn ldvxk_test() {\n let mut x = Chip8Computer::new();\n Chip8CpuInstructions::LdVxByte(0x1, 0x1).execute(\u0026mut x);\n Chip8CpuInstructions::LdVxK(0x1).execute(\u0026mut x);\n assert!(matches!(x.state, WaitingForKey));\n }\n}\n","traces":[{"line":122,"address":[19714432],"length":1,"stats":{"Line":1}},{"line":123,"address":[1797783],"length":1,"stats":{"Line":1}},{"line":124,"address":[1797828],"length":1,"stats":{"Line":1}},{"line":125,"address":[1797840],"length":1,"stats":{"Line":1}},{"line":128,"address":[1797874],"length":1,"stats":{"Line":1}},{"line":131,"address":[1797889],"length":1,"stats":{"Line":1}},{"line":133,"address":[1797912],"length":1,"stats":{"Line":1}},{"line":134,"address":[1797924],"length":1,"stats":{"Line":1}},{"line":136,"address":[1797966],"length":1,"stats":{"Line":1}},{"line":137,"address":[1797978],"length":1,"stats":{"Line":1}},{"line":139,"address":[1798020],"length":1,"stats":{"Line":1}},{"line":140,"address":[1798055],"length":1,"stats":{"Line":1}},{"line":142,"address":[19714792],"length":1,"stats":{"Line":1}},{"line":143,"address":[1798155],"length":1,"stats":{"Line":1}},{"line":145,"address":[1798220],"length":1,"stats":{"Line":1}},{"line":146,"address":[19714927],"length":1,"stats":{"Line":1}},{"line":148,"address":[1798344],"length":1,"stats":{"Line":1}},{"line":149,"address":[19715051],"length":1,"stats":{"Line":1}},{"line":151,"address":[1798444],"length":1,"stats":{"Line":1}},{"line":152,"address":[1798479],"length":1,"stats":{"Line":1}},{"line":154,"address":[1798544],"length":1,"stats":{"Line":1}},{"line":155,"address":[1798579],"length":1,"stats":{"Line":1}},{"line":157,"address":[1798668],"length":1,"stats":{"Line":1}},{"line":158,"address":[1798703],"length":1,"stats":{"Line":1}},{"line":160,"address":[1798792],"length":1,"stats":{"Line":1}},{"line":161,"address":[1798827],"length":1,"stats":{"Line":1}},{"line":163,"address":[1798916],"length":1,"stats":{"Line":1}},{"line":164,"address":[1798951],"length":1,"stats":{"Line":1}},{"line":166,"address":[1799040],"length":1,"stats":{"Line":1}},{"line":167,"address":[1799072],"length":1,"stats":{"Line":1}},{"line":169,"address":[1799152],"length":1,"stats":{"Line":1}},{"line":170,"address":[1799184],"length":1,"stats":{"Line":1}},{"line":172,"address":[1799264],"length":1,"stats":{"Line":1}},{"line":173,"address":[19715968],"length":1,"stats":{"Line":1}},{"line":175,"address":[1799376],"length":1,"stats":{"Line":1}},{"line":176,"address":[1799408],"length":1,"stats":{"Line":1}},{"line":178,"address":[1799488],"length":1,"stats":{"Line":1}},{"line":179,"address":[1799520],"length":1,"stats":{"Line":1}},{"line":181,"address":[1799600],"length":1,"stats":{"Line":1}},{"line":182,"address":[1799632],"length":1,"stats":{"Line":1}},{"line":184,"address":[1799712],"length":1,"stats":{"Line":1}},{"line":185,"address":[1799724],"length":1,"stats":{"Line":1}},{"line":187,"address":[1799762],"length":1,"stats":{"Line":1}},{"line":188,"address":[1799774],"length":1,"stats":{"Line":1}},{"line":190,"address":[1799812],"length":1,"stats":{"Line":1}},{"line":191,"address":[19716516],"length":1,"stats":{"Line":1}},{"line":193,"address":[1799906],"length":1,"stats":{"Line":1}},{"line":194,"address":[1799957],"length":1,"stats":{"Line":1}},{"line":196,"address":[1800056],"length":1,"stats":{"Line":1}},{"line":197,"address":[1800068],"length":1,"stats":{"Line":1}},{"line":199,"address":[1800110],"length":1,"stats":{"Line":1}},{"line":200,"address":[19716794],"length":1,"stats":{"Line":1}},{"line":202,"address":[1800164],"length":1,"stats":{"Line":1}},{"line":203,"address":[19716848],"length":1,"stats":{"Line":1}},{"line":205,"address":[1800218],"length":1,"stats":{"Line":1}},{"line":206,"address":[1800230],"length":1,"stats":{"Line":1}},{"line":208,"address":[1800272],"length":1,"stats":{"Line":1}},{"line":209,"address":[1800284],"length":1,"stats":{"Line":1}},{"line":211,"address":[1800326],"length":1,"stats":{"Line":1}},{"line":212,"address":[1800338],"length":1,"stats":{"Line":1}},{"line":214,"address":[1800380],"length":1,"stats":{"Line":1}},{"line":215,"address":[19717064],"length":1,"stats":{"Line":1}},{"line":217,"address":[1800434],"length":1,"stats":{"Line":1}},{"line":218,"address":[1800446],"length":1,"stats":{"Line":1}},{"line":220,"address":[1800488],"length":1,"stats":{"Line":1}},{"line":221,"address":[1800500],"length":1,"stats":{"Line":1}},{"line":223,"address":[1800539],"length":1,"stats":{"Line":1}},{"line":224,"address":[1800551],"length":1,"stats":{"Line":1}},{"line":226,"address":[1800590],"length":1,"stats":{"Line":1}},{"line":227,"address":[1800602],"length":1,"stats":{"Line":1}},{"line":230,"address":[1800633],"length":1,"stats":{"Line":1}},{"line":234,"address":[1800672],"length":1,"stats":{"Line":1}},{"line":235,"address":[1800696],"length":1,"stats":{"Line":1}},{"line":236,"address":[1800718],"length":1,"stats":{"Line":1}},{"line":237,"address":[1800740],"length":1,"stats":{"Line":1}},{"line":238,"address":[19717446],"length":1,"stats":{"Line":1}},{"line":239,"address":[1800784],"length":1,"stats":{"Line":1}},{"line":240,"address":[19717498],"length":1,"stats":{"Line":1}},{"line":241,"address":[1800836],"length":1,"stats":{"Line":1}},{"line":243,"address":[1800849],"length":1,"stats":{"Line":1}},{"line":247,"address":[1800892],"length":1,"stats":{"Line":1}},{"line":252,"address":[1800901],"length":1,"stats":{"Line":1}},{"line":254,"address":[1800939,1800883],"length":1,"stats":{"Line":2}},{"line":257,"address":[1800950],"length":1,"stats":{"Line":1}},{"line":259,"address":[1800923,1800985],"length":1,"stats":{"Line":2}},{"line":262,"address":[1800996],"length":1,"stats":{"Line":1}},{"line":264,"address":[1800969,1801031],"length":1,"stats":{"Line":2}},{"line":267,"address":[19717738],"length":1,"stats":{"Line":1}},{"line":269,"address":[1801080,1801015],"length":1,"stats":{"Line":2}},{"line":272,"address":[1801096],"length":1,"stats":{"Line":1}},{"line":274,"address":[1801064,1801139],"length":1,"stats":{"Line":2}},{"line":277,"address":[1801155],"length":1,"stats":{"Line":1}},{"line":279,"address":[1801198,1801123],"length":1,"stats":{"Line":2}},{"line":283,"address":[1802600,1801209,1802588],"length":1,"stats":{"Line":3}},{"line":285,"address":[1802593],"length":1,"stats":{"Line":1}},{"line":287,"address":[1802571],"length":1,"stats":{"Line":1}},{"line":290,"address":[1801182,1801249],"length":1,"stats":{"Line":2}},{"line":293,"address":[1801265],"length":1,"stats":{"Line":1}},{"line":295,"address":[1801308,1801233],"length":1,"stats":{"Line":2}},{"line":297,"address":[19718020],"length":1,"stats":{"Line":1}},{"line":299,"address":[19717988,19718063],"length":1,"stats":{"Line":2}},{"line":301,"address":[1801378],"length":1,"stats":{"Line":1}},{"line":302,"address":[19718092],"length":1,"stats":{"Line":1}},{"line":305,"address":[19718979],"length":1,"stats":{"Line":1}},{"line":309,"address":[19719011],"length":1,"stats":{"Line":1}},{"line":313,"address":[1802347],"length":1,"stats":{"Line":1}},{"line":317,"address":[1802379],"length":1,"stats":{"Line":1}},{"line":321,"address":[1802411],"length":1,"stats":{"Line":1}},{"line":325,"address":[19719139],"length":1,"stats":{"Line":1}},{"line":329,"address":[19719171],"length":1,"stats":{"Line":1}},{"line":333,"address":[19719203],"length":1,"stats":{"Line":1}},{"line":337,"address":[1802539],"length":1,"stats":{"Line":1}},{"line":340,"address":[1802261],"length":1,"stats":{"Line":1}},{"line":344,"address":[1801447,1801351],"length":1,"stats":{"Line":2}},{"line":346,"address":[1802256,1802244,1801458],"length":1,"stats":{"Line":3}},{"line":347,"address":[19718945],"length":1,"stats":{"Line":1}},{"line":349,"address":[1802227],"length":1,"stats":{"Line":1}},{"line":352,"address":[19718194,19718127],"length":1,"stats":{"Line":2}},{"line":354,"address":[1801509],"length":1,"stats":{"Line":1}},{"line":356,"address":[1801547,1801482],"length":1,"stats":{"Line":2}},{"line":358,"address":[1801558],"length":1,"stats":{"Line":1}},{"line":360,"address":[1801596,1801531],"length":1,"stats":{"Line":2}},{"line":362,"address":[1801612],"length":1,"stats":{"Line":1}},{"line":364,"address":[1801580,1801655],"length":1,"stats":{"Line":2}},{"line":366,"address":[1801676],"length":1,"stats":{"Line":1}},{"line":368,"address":[1801639,1801724],"length":1,"stats":{"Line":2}},{"line":369,"address":[1801735],"length":1,"stats":{"Line":1}},{"line":371,"address":[1802178],"length":1,"stats":{"Line":1}},{"line":374,"address":[1802200],"length":1,"stats":{"Line":1}},{"line":377,"address":[19718857],"length":1,"stats":{"Line":1}},{"line":381,"address":[1801792,1801708],"length":1,"stats":{"Line":2}},{"line":382,"address":[1801803],"length":1,"stats":{"Line":1}},{"line":384,"address":[1801968],"length":1,"stats":{"Line":1}},{"line":387,"address":[1801990],"length":1,"stats":{"Line":1}},{"line":390,"address":[1802012],"length":1,"stats":{"Line":1}},{"line":393,"address":[1802034],"length":1,"stats":{"Line":1}},{"line":396,"address":[1802056],"length":1,"stats":{"Line":1}},{"line":399,"address":[1802078],"length":1,"stats":{"Line":1}},{"line":402,"address":[1802100],"length":1,"stats":{"Line":1}},{"line":405,"address":[1802122],"length":1,"stats":{"Line":1}},{"line":408,"address":[19718840],"length":1,"stats":{"Line":1}},{"line":411,"address":[1801951],"length":1,"stats":{"Line":1}},{"line":416,"address":[1801775],"length":1,"stats":{"Line":1}},{"line":421,"address":[1802608,1822432],"length":1,"stats":{"Line":4}},{"line":422,"address":[19719476],"length":1,"stats":{"Line":4}},{"line":423,"address":[19719505,19719610],"length":1,"stats":{"Line":4}},{"line":424,"address":[1802866,1802917],"length":1,"stats":{"Line":4}},{"line":426,"address":[1802927],"length":1,"stats":{"Line":1}},{"line":427,"address":[19724709,19724445,19719648,19724299],"length":1,"stats":{"Line":2}},{"line":429,"address":[1807485],"length":1,"stats":{"Line":1}},{"line":433,"address":[1808147,1802985],"length":1,"stats":{"Line":2}},{"line":434,"address":[19725025],"length":1,"stats":{"Line":1}},{"line":439,"address":[19719737],"length":1,"stats":{"Line":1}},{"line":440,"address":[1808762,1808352,1803041,1808498],"length":1,"stats":{"Line":2}},{"line":441,"address":[1808299],"length":1,"stats":{"Line":1}},{"line":444,"address":[19719805],"length":1,"stats":{"Line":1}},{"line":445,"address":[19725925,19725779,19726189,19719817],"length":1,"stats":{"Line":2}},{"line":446,"address":[1808959],"length":1,"stats":{"Line":1}},{"line":449,"address":[1803161],"length":1,"stats":{"Line":1}},{"line":450,"address":[1809878,1809729,1810142,1803173],"length":1,"stats":{"Line":2}},{"line":451,"address":[1809624],"length":1,"stats":{"Line":1}},{"line":452,"address":[19726417],"length":1,"stats":{"Line":1}},{"line":453,"address":[1809683],"length":1,"stats":{"Line":1}},{"line":456,"address":[19719945],"length":1,"stats":{"Line":1}},{"line":457,"address":[19719972],"length":1,"stats":{"Line":1}},{"line":458,"address":[1810381],"length":1,"stats":{"Line":1}},{"line":460,"address":[1811210,1810423,1810682,1810978,1810331],"length":1,"stats":{"Line":2}},{"line":463,"address":[19720039],"length":1,"stats":{"Line":1}},{"line":464,"address":[19720066],"length":1,"stats":{"Line":1}},{"line":465,"address":[1811481],"length":1,"stats":{"Line":1}},{"line":467,"address":[1811523,1811782,1812078,1812310,1811431],"length":1,"stats":{"Line":2}},{"line":470,"address":[1803421],"length":1,"stats":{"Line":1}},{"line":471,"address":[19720160],"length":1,"stats":{"Line":1}},{"line":472,"address":[19720209],"length":1,"stats":{"Line":1}},{"line":474,"address":[19720252],"length":1,"stats":{"Line":1}},{"line":475,"address":[19729348],"length":1,"stats":{"Line":1}},{"line":477,"address":[1812880,1812621,1813176,1812529,1813408],"length":1,"stats":{"Line":2}},{"line":480,"address":[1803567],"length":1,"stats":{"Line":1}},{"line":481,"address":[19720308],"length":1,"stats":{"Line":1}},{"line":482,"address":[1803612],"length":1,"stats":{"Line":1}},{"line":483,"address":[19730686,19730982,19720356,19730427,19731226],"length":1,"stats":{"Line":2}},{"line":486,"address":[19720418],"length":1,"stats":{"Line":1}},{"line":487,"address":[19720445],"length":1,"stats":{"Line":1}},{"line":488,"address":[19731463,19720483,19731547],"length":1,"stats":{"Line":2}},{"line":489,"address":[1814700],"length":1,"stats":{"Line":1}},{"line":490,"address":[19732520,19731591,19732752,19731505,19732288,19731960],"length":1,"stats":{"Line":2}},{"line":493,"address":[1803818],"length":1,"stats":{"Line":1}},{"line":494,"address":[19720561],"length":1,"stats":{"Line":1}},{"line":495,"address":[19733029,19733584,19733288,19733816,19720650],"length":1,"stats":{"Line":2}},{"line":498,"address":[1803994],"length":1,"stats":{"Line":1}},{"line":500,"address":[19720739],"length":1,"stats":{"Line":1}},{"line":501,"address":[19720793],"length":1,"stats":{"Line":1}},{"line":503,"address":[19720849],"length":1,"stats":{"Line":1}},{"line":505,"address":[1804140],"length":1,"stats":{"Line":1}},{"line":506,"address":[19720867],"length":1,"stats":{"Line":1}},{"line":507,"address":[1817849,1804176,1817294,1818081,1817553],"length":1,"stats":{"Line":2}},{"line":510,"address":[1804238],"length":1,"stats":{"Line":1}},{"line":511,"address":[1804265],"length":1,"stats":{"Line":1}},{"line":512,"address":[19721040],"length":1,"stats":{"Line":1}},{"line":513,"address":[19721096],"length":1,"stats":{"Line":1}},{"line":515,"address":[19721107],"length":1,"stats":{"Line":1}},{"line":516,"address":[1819259,1819027,1819491,1818699,1804423,1818330],"length":1,"stats":{"Line":2}},{"line":519,"address":[1804485],"length":1,"stats":{"Line":1}},{"line":520,"address":[1804515],"length":1,"stats":{"Line":1}},{"line":521,"address":[19721296],"length":1,"stats":{"Line":1}},{"line":522,"address":[1804627],"length":1,"stats":{"Line":1}},{"line":523,"address":[19721364],"length":1,"stats":{"Line":1}},{"line":526,"address":[19721408],"length":1,"stats":{"Line":1}},{"line":527,"address":[19721446],"length":1,"stats":{"Line":1}},{"line":528,"address":[19721503],"length":1,"stats":{"Line":1}},{"line":529,"address":[19736519,19736535,19721544],"length":1,"stats":{"Line":2}},{"line":530,"address":[1819756],"length":1,"stats":{"Line":1}},{"line":531,"address":[1819838],"length":1,"stats":{"Line":1}},{"line":533,"address":[1819804],"length":1,"stats":{"Line":1}},{"line":535,"address":[1804858],"length":1,"stats":{"Line":1}},{"line":540,"address":[19721625],"length":1,"stats":{"Line":1}},{"line":541,"address":[19721674],"length":1,"stats":{"Line":1}},{"line":543,"address":[1804977],"length":1,"stats":{"Line":1}},{"line":544,"address":[1819975,1819891,1820007],"length":1,"stats":{"Line":0}},{"line":545,"address":[19736757],"length":1,"stats":{"Line":0}},{"line":547,"address":[1819870],"length":1,"stats":{"Line":1}},{"line":549,"address":[1820047,1820080,1819927],"length":1,"stats":{"Line":2}},{"line":550,"address":[19736829],"length":1,"stats":{"Line":1}},{"line":552,"address":[1805014],"length":1,"stats":{"Line":1}},{"line":557,"address":[1805037],"length":1,"stats":{"Line":1}},{"line":558,"address":[19721802],"length":1,"stats":{"Line":1}},{"line":559,"address":[19736880],"length":1,"stats":{"Line":1}},{"line":561,"address":[19736926],"length":1,"stats":{"Line":1}},{"line":563,"address":[19721833],"length":1,"stats":{"Line":1}},{"line":568,"address":[19721871],"length":1,"stats":{"Line":1}},{"line":569,"address":[1805190],"length":1,"stats":{"Line":1}},{"line":570,"address":[19721966,19736996],"length":1,"stats":{"Line":2}},{"line":571,"address":[19737028,19737282,19737112],"length":1,"stats":{"Line":3}},{"line":572,"address":[1820292,1820447,1820507],"length":1,"stats":{"Line":2}},{"line":574,"address":[19737046,19737105,19737114],"length":1,"stats":{"Line":2}},{"line":576,"address":[19737138],"length":1,"stats":{"Line":1}},{"line":577,"address":[1820401],"length":1,"stats":{"Line":1}},{"line":580,"address":[1805261],"length":1,"stats":{"Line":1}},{"line":585,"address":[19722018],"length":1,"stats":{"Line":1}},{"line":586,"address":[19722052],"length":1,"stats":{"Line":1}},{"line":587,"address":[1820531],"length":1,"stats":{"Line":1}},{"line":589,"address":[1820576],"length":1,"stats":{"Line":1}},{"line":591,"address":[1805348],"length":1,"stats":{"Line":1}},{"line":597,"address":[19722113],"length":1,"stats":{"Line":1}},{"line":598,"address":[1805429],"length":1,"stats":{"Line":1}},{"line":599,"address":[19722201],"length":1,"stats":{"Line":1}},{"line":600,"address":[19737429],"length":1,"stats":{"Line":1}},{"line":603,"address":[19722222],"length":1,"stats":{"Line":1}},{"line":608,"address":[19737922,19737658,19737512,19722234],"length":1,"stats":{"Line":2}},{"line":609,"address":[1820672],"length":1,"stats":{"Line":1}},{"line":612,"address":[19722296],"length":1,"stats":{"Line":1}},{"line":617,"address":[1805579],"length":1,"stats":{"Line":1}},{"line":619,"address":[1805665],"length":1,"stats":{"Line":1}},{"line":626,"address":[1805692],"length":1,"stats":{"Line":1}},{"line":627,"address":[1805720],"length":1,"stats":{"Line":1}},{"line":629,"address":[1805769],"length":1,"stats":{"Line":1}},{"line":642,"address":[19722560],"length":1,"stats":{"Line":1}},{"line":644,"address":[19722600],"length":1,"stats":{"Line":1}},{"line":645,"address":[1805902],"length":1,"stats":{"Line":1}},{"line":647,"address":[19722892],"length":1,"stats":{"Line":1}},{"line":648,"address":[19738121,19738744,19722999],"length":1,"stats":{"Line":2}},{"line":650,"address":[19738399],"length":1,"stats":{"Line":1}},{"line":651,"address":[19738514],"length":1,"stats":{"Line":1}},{"line":652,"address":[19738635],"length":1,"stats":{"Line":1}},{"line":653,"address":[1821976,1821926,1822198],"length":1,"stats":{"Line":3}},{"line":654,"address":[19740559,19739245,19738999],"length":1,"stats":{"Line":2}},{"line":655,"address":[19739911,19740375,19740143,19739269,19739395,19739583,19739489],"length":1,"stats":{"Line":7}},{"line":656,"address":[1823788,1823743],"length":1,"stats":{"Line":2}},{"line":657,"address":[1824046,1823858],"length":1,"stats":{"Line":1}},{"line":658,"address":[1824066,1824030,1824148],"length":1,"stats":{"Line":2}},{"line":659,"address":[19740901],"length":1,"stats":{"Line":1}},{"line":663,"address":[1822032,1822273],"length":1,"stats":{"Line":1}},{"line":665,"address":[19719452],"length":1,"stats":{"Line":4}},{"line":670,"address":[1822408],"length":1,"stats":{"Line":1}},{"line":673,"address":[1806310],"length":1,"stats":{"Line":1}},{"line":678,"address":[19723069],"length":1,"stats":{"Line":1}},{"line":679,"address":[1806359],"length":1,"stats":{"Line":1}},{"line":680,"address":[1806383],"length":1,"stats":{"Line":1}},{"line":681,"address":[1824172],"length":1,"stats":{"Line":1}},{"line":684,"address":[1806412],"length":1,"stats":{"Line":1}},{"line":692,"address":[19723170],"length":1,"stats":{"Line":1}},{"line":693,"address":[1806465],"length":1,"stats":{"Line":1}},{"line":694,"address":[19741439,19741029,19741175,19723241],"length":1,"stats":{"Line":2}},{"line":695,"address":[1824196],"length":1,"stats":{"Line":1}},{"line":696,"address":[19741638],"length":1,"stats":{"Line":1}},{"line":699,"address":[1806555],"length":1,"stats":{"Line":0}},{"line":704,"address":[1806567],"length":1,"stats":{"Line":0}},{"line":705,"address":[1806602],"length":1,"stats":{"Line":0}},{"line":707,"address":[1806645],"length":1,"stats":{"Line":1}},{"line":712,"address":[1806657],"length":1,"stats":{"Line":1}},{"line":714,"address":[19723449],"length":1,"stats":{"Line":1}},{"line":719,"address":[19723464],"length":1,"stats":{"Line":1}},{"line":720,"address":[1806748],"length":1,"stats":{"Line":1}},{"line":722,"address":[1806784],"length":1,"stats":{"Line":1}},{"line":723,"address":[1806799],"length":1,"stats":{"Line":1}},{"line":724,"address":[1806833],"length":1,"stats":{"Line":1}},{"line":726,"address":[19723623],"length":1,"stats":{"Line":1}},{"line":731,"address":[19723635],"length":1,"stats":{"Line":1}},{"line":732,"address":[19723683],"length":1,"stats":{"Line":1}},{"line":733,"address":[1824879,1806972],"length":1,"stats":{"Line":2}},{"line":735,"address":[1807033],"length":1,"stats":{"Line":1}},{"line":743,"address":[19741740,19741709,19723804],"length":1,"stats":{"Line":2}},{"line":744,"address":[19741769,19741716,19741798],"length":1,"stats":{"Line":2}},{"line":745,"address":[19741777],"length":1,"stats":{"Line":1}},{"line":747,"address":[19723865],"length":1,"stats":{"Line":1}},{"line":755,"address":[19723880],"length":1,"stats":{"Line":1}},{"line":758,"address":[1807156],"length":1,"stats":{"Line":1}},{"line":760,"address":[19741828,19723945,19742042],"length":1,"stats":{"Line":2}},{"line":762,"address":[1825093],"length":1,"stats":{"Line":1}},{"line":765,"address":[1825126],"length":1,"stats":{"Line":1}},{"line":768,"address":[19741942],"length":1,"stats":{"Line":1}},{"line":769,"address":[1825190],"length":1,"stats":{"Line":1}},{"line":770,"address":[1825218,1825275,1825326],"length":1,"stats":{"Line":2}},{"line":771,"address":[1825356,1825299],"length":1,"stats":{"Line":2}},{"line":773,"address":[1807254],"length":1,"stats":{"Line":1}},{"line":778,"address":[1807266],"length":1,"stats":{"Line":1}},{"line":779,"address":[1807305,1825388,1825494],"length":1,"stats":{"Line":3}},{"line":780,"address":[1825515,1825593],"length":1,"stats":{"Line":2}},{"line":782,"address":[1825448,1825551],"length":1,"stats":{"Line":2}},{"line":784,"address":[1807354],"length":1,"stats":{"Line":1}},{"line":788,"address":[1807374],"length":1,"stats":{"Line":1}},{"line":789,"address":[1826346,1825933,1807394,1826082],"length":1,"stats":{"Line":2}},{"line":790,"address":[19742460],"length":1,"stats":{"Line":1}},{"line":791,"address":[1825793],"length":1,"stats":{"Line":1}},{"line":792,"address":[1826632,1826535,1825868],"length":1,"stats":{"Line":3}},{"line":793,"address":[1826648,1828092,1826748],"length":1,"stats":{"Line":2}},{"line":794,"address":[1826779],"length":1,"stats":{"Line":1}},{"line":795,"address":[1827932,1827700,1827140,1827468],"length":1,"stats":{"Line":4}},{"line":797,"address":[1826587,1826714],"length":1,"stats":{"Line":2}},{"line":801,"address":[1807456],"length":1,"stats":{"Line":1}}],"covered":325,"coverable":330},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","keypad.rs"],"content":"use imgui::Key;\n\n\n#[derive(Clone, Copy)]\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 let keys = [\n [0x1, 0x2, 0x3, 0xc],[0x4, 0x5, 0x6, 0xd],[0x7, 0x8, 0x9, 0xe],[0xa, 0x0, 0xb, 0xf]\n ];\n\n for row in keys.iter() {\n for (index, key) in row.iter().enumerate() {\n let is_lit = if self.keys[*key] { \"*\".to_string() } else { char::from_digit(*key as u32, 16).unwrap_or(' ').to_string() };\n match index {\n 3 =\u003e {\n // last in col\n return_value += format!(\"|{}|\\n\", is_lit).as_str();\n }\n _=\u003e {\n return_value += format!(\"|{}\", is_lit).as_str();\n }\n }\n }\n }\n\n return_value\n }\n}\n\nimpl Default for Keypad {\n fn default() -\u003e Self {\n Keypad {\n keys: [ false; 16]\n }\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\n#[cfg(test)]\nmod test {\n use super::*;\n\n #[test]\n fn smoke() { assert!(true) }\n\n #[test]\n fn 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}","traces":[{"line":10,"address":[19767608,19765968,19767854],"length":1,"stats":{"Line":0}},{"line":11,"address":[1791311],"length":1,"stats":{"Line":0}},{"line":13,"address":[1791508],"length":1,"stats":{"Line":0}},{"line":14,"address":[1791316],"length":1,"stats":{"Line":0}},{"line":17,"address":[1791649,1791896,1791732],"length":1,"stats":{"Line":0}},{"line":18,"address":[1791912],"length":1,"stats":{"Line":0}},{"line":19,"address":[1792360,1792172],"length":1,"stats":{"Line":0}},{"line":20,"address":[1792352],"length":1,"stats":{"Line":0}},{"line":23,"address":[1792765,1792636],"length":1,"stats":{"Line":0}},{"line":26,"address":[1793030,1792901],"length":1,"stats":{"Line":0}},{"line":32,"address":[1791853],"length":1,"stats":{"Line":0}},{"line":37,"address":[1793152],"length":1,"stats":{"Line":4}},{"line":39,"address":[19767886],"length":1,"stats":{"Line":4}},{"line":45,"address":[1793216],"length":1,"stats":{"Line":1}},{"line":46,"address":[1793275,1793237],"length":1,"stats":{"Line":1}},{"line":49,"address":[1793296],"length":1,"stats":{"Line":1}},{"line":50,"address":[1793317,1793355],"length":1,"stats":{"Line":1}},{"line":53,"address":[1793376],"length":1,"stats":{"Line":1}},{"line":54,"address":[19768159,19768117],"length":1,"stats":{"Line":1}},{"line":57,"address":[1793472],"length":1,"stats":{"Line":1}},{"line":58,"address":[1793480],"length":1,"stats":{"Line":1}},{"line":61,"address":[1793504],"length":1,"stats":{"Line":1}},{"line":62,"address":[1793520],"length":1,"stats":{"Line":1}},{"line":64,"address":[1793552],"length":1,"stats":{"Line":1}},{"line":65,"address":[1793568],"length":1,"stats":{"Line":1}}],"covered":14,"coverable":25},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","registers.rs"],"content":"use log::debug;\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)]\npub struct Chip8Registers {\n registers: [u8; 16],\n i_register: u16,\n pc: u16,\n sp: u16,\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 sp: 0x100,\n }\n }\n}\n\nimpl Chip8Registers {\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\n#[cfg(test)]\nmod test {\n use crate::chip8::registers::Chip8Registers;\n\n #[test]\n fn smoke() { assert!(true) }\n\n #[test]\n fn 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]\n fn 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]\n fn invalid_register() {\n let mut x = Chip8Registers::default();\n x.poke(0x10, 0xff);\n }\n\n #[test]\n fn 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}","traces":[{"line":14,"address":[1779776],"length":1,"stats":{"Line":1}},{"line":15,"address":[1779827,1779789],"length":1,"stats":{"Line":1}},{"line":20,"address":[1779856],"length":1,"stats":{"Line":4}},{"line":22,"address":[1779870],"length":1,"stats":{"Line":4}},{"line":31,"address":[1779952],"length":1,"stats":{"Line":4}},{"line":32,"address":[1779965],"length":1,"stats":{"Line":4}},{"line":34,"address":[1779984],"length":1,"stats":{"Line":1}},{"line":35,"address":[1779989],"length":1,"stats":{"Line":1}},{"line":38,"address":[1780000],"length":1,"stats":{"Line":1}},{"line":39,"address":[1780013],"length":1,"stats":{"Line":1}},{"line":41,"address":[1780032],"length":1,"stats":{"Line":3}},{"line":42,"address":[1780053,1780090],"length":1,"stats":{"Line":3}},{"line":45,"address":[1780112],"length":1,"stats":{"Line":2}},{"line":46,"address":[1780147],"length":1,"stats":{"Line":2}},{"line":47,"address":[1780171],"length":1,"stats":{"Line":1}},{"line":49,"address":[1780232,1780245,1780155],"length":1,"stats":{"Line":8}},{"line":52,"address":[1780272],"length":1,"stats":{"Line":4}},{"line":53,"address":[1780277],"length":1,"stats":{"Line":4}},{"line":56,"address":[1780288],"length":1,"stats":{"Line":1}},{"line":57,"address":[1780301],"length":1,"stats":{"Line":1}},{"line":60,"address":[1780320],"length":1,"stats":{"Line":0}},{"line":61,"address":[1786532,1780911,1781097,1784908,1782820,1786764,1784212,1786068,1781004,1781655,1783516,1780446,1780818,1781841,1783748,1783284,1780539,1785372,1785140,1781469,1780632,1784444,1783980,1784676,1780725,1781283,1783052,1781562,1781748,1785604,1782024,1786300,1781934,1785836,1781376,1781190],"length":1,"stats":{"Line":0}}],"covered":20,"coverable":22},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","sound_timer.rs"],"content":"use std::{thread, time};\nuse beep::beep;\nuse log::trace;\n\n#[derive(Clone, Copy)]\npub struct SoundTimer {\n counter: i32\n}\n\nimpl SoundTimer {\n pub fn current(\u0026self) -\u003e i32 {\n self.counter\n }\n pub fn new() -\u003e Self {\n SoundTimer {\n counter: 0\n }\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!(\"TICKING SOUND FROM {} to {}\", self.counter, self.counter - 1);\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\n#[cfg(test)]\nmod test {\n use super::*;\n\n #[test]\n fn smoke() {\n assert!(true)\n }\n\n #[test]\n fn 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]\n fn 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}","traces":[{"line":11,"address":[1754528],"length":1,"stats":{"Line":1}},{"line":12,"address":[1754533],"length":1,"stats":{"Line":1}},{"line":19,"address":[1754560],"length":1,"stats":{"Line":1}},{"line":20,"address":[1754784,1754584,1754662],"length":1,"stats":{"Line":2}},{"line":21,"address":[1754620],"length":1,"stats":{"Line":1}},{"line":24,"address":[1754928],"length":1,"stats":{"Line":1}},{"line":25,"address":[1754948,1755141,1755279,1755026],"length":1,"stats":{"Line":2}},{"line":26,"address":[1755508,1754984],"length":1,"stats":{"Line":2}},{"line":27,"address":[1755484,1755510],"length":1,"stats":{"Line":1}}],"covered":9,"coverable":9},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","stack.rs"],"content":"#[derive(Clone)]\npub struct Chip8Stack {\n items: Vec\u003cu16\u003e\n}\n\nimpl Default for Chip8Stack {\n fn default() -\u003e Self {\n Chip8Stack {\n items: vec![],\n }\n }\n}\n\nimpl Chip8Stack {\n pub fn push(\u0026mut self, new_value: \u0026u16) {\n if self.depth() == 16 {\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 {\n items: vec![]\n }\n }\n}\n\n#[cfg(test)]\nmod test {\n use rand::random;\n use super::*;\n\n #[test]\n fn smoke() { assert!(true) }\n\n #[test]\n fn 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]\n fn 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]\n fn stack_underflow_test() {\n let mut x = Chip8Stack::new();\n x.pop();\n }\n\n #[test]\n fn lots_of_subs() {\n let mut x = Chip8Stack::new();\n let stack_contents = [0x123, 0x321, 0xabc, 0xdef,\n 0xbad, 0xbef, 0xfed, 0xcab,\n 0xbed, 0xcad, 0xfeb, 0xcab,\n 0xfff, 0x000, 0x001];\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}","traces":[{"line":7,"address":[19710288],"length":1,"stats":{"Line":4}},{"line":9,"address":[1760301],"length":1,"stats":{"Line":4}},{"line":15,"address":[1760352],"length":1,"stats":{"Line":1}},{"line":16,"address":[1760376],"length":1,"stats":{"Line":1}},{"line":17,"address":[1760387],"length":1,"stats":{"Line":1}},{"line":19,"address":[1760442],"length":1,"stats":{"Line":1}},{"line":21,"address":[1760464],"length":1,"stats":{"Line":1}},{"line":22,"address":[1760478],"length":1,"stats":{"Line":1}},{"line":23,"address":[1760550],"length":1,"stats":{"Line":1}},{"line":25,"address":[1760493],"length":1,"stats":{"Line":1}},{"line":27,"address":[1760608],"length":1,"stats":{"Line":1}},{"line":28,"address":[1760613],"length":1,"stats":{"Line":1}},{"line":31,"address":[1760624],"length":1,"stats":{"Line":3}},{"line":33,"address":[1760637],"length":1,"stats":{"Line":1}}],"covered":14,"coverable":14},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","system_memory.rs"],"content":"use glium::RawUniformValue::Vec2;\nuse image::load;\nuse imgui::sys::ImColor;\nuse imgui::{ImColor32, Ui};\nuse log::{debug, trace};\nuse ratatui::{style::Style, widgets::Widget};\n\nuse crate::constants::{CHIP8_MEMORY_SIZE, CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};\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\n#[derive(Clone, Copy)]\npub struct Chip8SystemMemory {\n memory: [u8; CHIP8_MEMORY_SIZE as usize],\n}\n\nimpl Default for Chip8SystemMemory {\n fn default() -\u003e Self {\n\n let mut x = Chip8SystemMemory {\n memory: [0x00; CHIP8_MEMORY_SIZE as usize],\n };\n\n x.load_fonts_to_memory();\n x\n }\n}\n\nconst cell_width: i32 = 5i32;\nconst cell_height: i32 = 5i32;\n\nimpl Chip8SystemMemory {\n \n pub fn new() -\u003e Self {\n Chip8SystemMemory {\n memory: [0x00; CHIP8_MEMORY_SIZE as usize],\n }\n }\n\n pub fn peek(self, address: u16) -\u003e u8 {\n trace!(\"PEEK: {} / {}\", address, self.memory[address as usize].clone());\n self.memory[address as usize]\n }\n\n pub fn poke(\u0026mut self, address: u16, value: u8) {\n trace!(\"POKE: {} / {} to {}\", address, self.memory[address as usize], value);\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\n#[cfg(test)]\nmod test {\n use super::*;\n\n #[test]\n fn smoke() {\n assert!(true)\n }\n\n #[test]\n fn model_smoke() {\n let m = Chip8SystemMemory::default();\n for i in 0..5 {\n assert_eq!(m.peek(i), CHIP8FONT_0[i as usize]);\n }\n\n assert_eq!(m.peek((CHIP8_MEMORY_SIZE - 1) as u16), 0);\n }\n\n #[test]\n fn known_data_loaded_correctly() {\n let to_load = [ 0x01, 0x02, 0x03, 0x04, 0x05 , 0x06 ];\n let mut x = Chip8SystemMemory::default();\n\n for (index, value) in [1..10].iter().enumerate() {\n assert_ne!(x.peek(0), 0x01);\n x.poke(0, 0x01);\n assert_eq!(x.peek(0), 0x01);\n }\n }\n\n #[test]\n fn verify_load_program() {\n // first line of 1-chip-logo.ch8\n let program_to_load = [0x00e0, 0x6101, 0x6008, 0xa250, 0xd01f, 0x6010, 0xa25f, 0xd01f];\n\n let mut x = Chip8SystemMemory::new();\n\n \n }\n}\n","traces":[{"line":34,"address":[19747712],"length":1,"stats":{"Line":4}},{"line":37,"address":[1787548],"length":1,"stats":{"Line":4}},{"line":40,"address":[1787591],"length":1,"stats":{"Line":4}},{"line":41,"address":[1787606],"length":1,"stats":{"Line":4}},{"line":50,"address":[1787648],"length":1,"stats":{"Line":1}},{"line":52,"address":[1787677],"length":1,"stats":{"Line":1}},{"line":56,"address":[1787728],"length":1,"stats":{"Line":1}},{"line":57,"address":[1788112,1787962,1787834,1787748],"length":1,"stats":{"Line":2}},{"line":58,"address":[1788326,1787779,1788342],"length":1,"stats":{"Line":2}},{"line":61,"address":[1788368],"length":1,"stats":{"Line":4}},{"line":62,"address":[1788488,1788838,1788402,1788616],"length":1,"stats":{"Line":8}},{"line":63,"address":[1789108,1789088,1788433],"length":1,"stats":{"Line":8}},{"line":66,"address":[19749344,19749685],"length":1,"stats":{"Line":0}},{"line":67,"address":[1789225,1789158,1789343],"length":1,"stats":{"Line":0}},{"line":68,"address":[1789361],"length":1,"stats":{"Line":0}},{"line":72,"address":[1789504],"length":1,"stats":{"Line":4}},{"line":73,"address":[1789524],"length":1,"stats":{"Line":4}},{"line":92,"address":[1789865,1790031],"length":1,"stats":{"Line":8}},{"line":93,"address":[1790073],"length":1,"stats":{"Line":4}},{"line":94,"address":[1790327,1790237],"length":1,"stats":{"Line":4}},{"line":95,"address":[1790319,1790363],"length":1,"stats":{"Line":8}}],"covered":18,"coverable":21},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","util.rs"],"content":"pub struct InstructionUtil {}\n\nimpl InstructionUtil {\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 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 = return_value | new_bit\n }\n }\n return_value\n }\n\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 pub fn join_bytes(high: u8, low: u8) -\u003e u16 {\n let result = (high as u16) \u003c\u003c 8 | low as u16;\n result\n }\n\n // nnn or addr - A 12-bit value, the lowest 12 bits of the instruction\n pub fn read_addr_from_instruction(instruction_to_read_from: u16) -\u003e u16 {\n instruction_to_read_from \u0026 0x0FFF\n }\n\n // n or nibble - A 4-bit value, the lowest 4 bits of the instruction\n pub fn read_nibble_from_instruction(instruction_to_read_from: u16) -\u003e u16 {\n instruction_to_read_from \u0026 0x000F\n }\n\n // x - A 4-bit value, the lower 4 bits of the high byte of the instruction\n pub fn read_x_from_instruction(instruction_to_read_from: u16) -\u003e u16 {\n (instruction_to_read_from \u0026 0x0F00).rotate_right(8)\n }\n\n // y - A 4-bit value, the upper 4 bits of the low byte of the instruction\n pub fn read_y_from_instruction(instruction_to_read_from: u16) -\u003e u16 {\n (instruction_to_read_from \u0026 0x00F0).rotate_right(4)\n }\n\n // kk or byte - An 8-bit value, the lowest 8 bits of the instruction\n pub fn read_byte_from_instruction(instruction_to_read_from: u16) -\u003e u16 {\n (instruction_to_read_from \u0026 0x00FF)\n }\n\n pub fn read_upper_byte_lower_nibble(to_read_from: u16) -\u003e u16 {\n (to_read_from \u0026 0x0f00) \u003e\u003e 8\n }\n}\n\n#[cfg(test)]\nmod test {\n use super::*;\n\n #[test]\n fn smoke() {\n assert!(true)\n }\n\n #[test]\n fn 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]\n fn 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]\n fn 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]\n fn 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]\n fn byte_to_bool_changes() {\n assert_eq!(InstructionUtil::byte_to_bools(0b00000000), [false, false, false, false, false, false, false, false]);\n assert_eq!(InstructionUtil::byte_to_bools(0b11111111), [true, true, true, true, true, true, true, true]);\n assert_eq!(InstructionUtil::byte_to_bools(0b11001100), [false, false, true, true, false, false, true, true]);\n assert_eq!(InstructionUtil::byte_to_bools(0b11110000), [false, false, false, false, true, true, true, true]);\n assert_eq!(InstructionUtil::bools_to_byte([false, false, false, false, false, false, false, false]), 0b00000000);\n assert_eq!(InstructionUtil::bools_to_byte([true, true, true, true, true, true, true, true]), 0b11111111);\n assert_eq!(InstructionUtil::bools_to_byte([false, false, true, true, false, false, true, true]), 0b11001100);\n assert_eq!(InstructionUtil::bools_to_byte([false, false, false, false, true, true, true, true]), 0b11110000);\n }\n}\n","traces":[{"line":4,"address":[1702288],"length":1,"stats":{"Line":1}},{"line":5,"address":[1702303],"length":1,"stats":{"Line":1}},{"line":6,"address":[1702390,1702320,1702491],"length":1,"stats":{"Line":3}},{"line":7,"address":[1702402,1702459],"length":1,"stats":{"Line":1}},{"line":8,"address":[19763877,19763820,19763860],"length":1,"stats":{"Line":2}},{"line":10,"address":[1702370],"length":1,"stats":{"Line":1}},{"line":13,"address":[1702528],"length":1,"stats":{"Line":1}},{"line":14,"address":[1702547],"length":1,"stats":{"Line":1}},{"line":15,"address":[1702615,1702552],"length":1,"stats":{"Line":2}},{"line":16,"address":[1702630,1702670],"length":1,"stats":{"Line":1}},{"line":17,"address":[1702691,1702738,1702662],"length":1,"stats":{"Line":3}},{"line":18,"address":[1702730],"length":1,"stats":{"Line":1}},{"line":21,"address":[1702606],"length":1,"stats":{"Line":1}},{"line":24,"address":[1702752],"length":1,"stats":{"Line":1}},{"line":25,"address":[1702789],"length":1,"stats":{"Line":1}},{"line":26,"address":[1702793],"length":1,"stats":{"Line":1}},{"line":31,"address":[19764176],"length":1,"stats":{"Line":1}},{"line":32,"address":[1702814],"length":1,"stats":{"Line":1}},{"line":37,"address":[1702848],"length":1,"stats":{"Line":1}},{"line":38,"address":[1702856],"length":1,"stats":{"Line":1}},{"line":42,"address":[1702864],"length":1,"stats":{"Line":1}},{"line":43,"address":[1702872],"length":1,"stats":{"Line":1}},{"line":47,"address":[1702880],"length":1,"stats":{"Line":1}},{"line":48,"address":[1702888],"length":1,"stats":{"Line":1}},{"line":52,"address":[1702928],"length":1,"stats":{"Line":1}},{"line":53,"address":[1702936],"length":1,"stats":{"Line":1}},{"line":57,"address":[1702976],"length":1,"stats":{"Line":1}},{"line":58,"address":[1702984],"length":1,"stats":{"Line":1}},{"line":61,"address":[1702992],"length":1,"stats":{"Line":1}},{"line":62,"address":[1703000],"length":1,"stats":{"Line":1}}],"covered":30,"coverable":30},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","src","chip8","video.rs"],"content":"use log::{debug, trace};\nuse crate::constants::CHIP8_VIDEO_MEMORY;\n\n#[derive(Clone, Copy)]\npub struct Chip8Video {\n memory: [bool; CHIP8_VIDEO_MEMORY],\n pub has_frame_changed: bool\n}\n\nimpl Chip8Video {\n pub fn cls(\u0026mut self) {\n for i in 0..CHIP8_VIDEO_MEMORY {\n self.memory[i] = 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: [bool; CHIP8_VIDEO_MEMORY]) -\u003e Self {\n Self {\n memory: initial_configuration,\n has_frame_changed: false\n }\n }\n\n pub fn peek(self, address: u16) -\u003e bool {\n self.memory[address as usize]\n }\n\n pub fn poke(\u0026mut self, address: u16, new_value: bool) -\u003e Self {\n trace!(\"OFFSET: {address} - POKING {new_value}\");\n let old_value = self.memory[address as usize];\n if old_value != new_value {\n trace!(\"**VIDEO** TOGGLING\");\n self.has_frame_changed = true;\n } else {\n trace!(\"NOT TOGGLING\");\n }\n self.memory[address as usize] = new_value;\n self.to_owned()\n }\n\n pub fn poke_byte(\u0026mut self, first_address: u16, to_write: u8) -\u003e Self {\n for i in (0..8).rev() {\n let shifted = ((1 \u003c\u003c i) \u0026 to_write) \u003e\u003e i;\n //\n let target_address = first_address + (7 - i);\n let is_set = shifted == 1;\n self.poke(target_address, is_set);\n }\n self.to_owned()\n }\n\n pub fn poke_sprite(\u0026mut self, first_address: u16, to_write: Vec\u003cu8\u003e) -\u003e Self {\n let sprite_length = to_write.len();\n\n for (index, byte) in to_write.iter().enumerate() {\n let real_address = index * 64;\n self.poke_byte(real_address as u16, *byte);\n }\n self.to_owned()\n }\n\n pub fn format_as_string(self) -\u003e String {\n let mut output = String::new();\n for row in 0..32 {\n for column in 0..64 {\n let data_offset = row * 64 + column;\n debug!(\"Rendering {data_offset} with value {}\", self.memory[data_offset]);\n if self.memory[data_offset] {\n output += \"*\"\n } else {\n output += \" \"\n }\n }\n output += \"\\n\";\n }\n\n output\n }\n}\n\nimpl Default for Chip8Video {\n fn default() -\u003e Self {\n Self { memory: [false; CHIP8_VIDEO_MEMORY as usize], has_frame_changed: false }\n }\n}\n\n#[cfg(test)]\nmod test {\n use std::fs::File;\n use std::io::Read;\n use crate::chip8::system_memory::{CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_3, CHIP8FONT_4, CHIP8FONT_5, CHIP8FONT_6, CHIP8FONT_7};\n use crate::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};\n use super::*;\n\n #[test]\n fn smoke() { assert!(true) }\n\n #[test]\n fn default_test() {\n let mut x = Chip8Video::default();\n\n for i in 0..CHIP8_VIDEO_MEMORY {\n assert!(!x.peek(i as u16));\n // then flip the value and test again.\n \u0026x.poke(i as u16, true);\n assert!(x.peek(i as u16));\n }\n }\n\n #[test]\n fn string_test_1() {\n let mut x = Chip8Video::default();\n let mut working_string = String::new();\n for i in 0..32 {\n working_string += \u0026*(\" \".repeat(64) + \"\\n\");\n }\n\n assert_eq!(working_string, x.format_as_string());\n\n let mut working_string = String::new();\n // set a checkerboard...\n for cb_row in 0..32 {\n for cb_col in 0..64 {\n let data_offset = cb_row * 64 + cb_col;\n if data_offset % 2 == 0 {\n x.poke(data_offset, true);\n working_string += \"*\";\n } else {\n x.poke(data_offset, false);\n working_string += \" \";\n }\n }\n working_string += \"\\n\";\n }\n\n assert_eq!(working_string, x.format_as_string());\n }\n\n #[test]\n fn set_initial_memory() {\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]\n fn poke_byte() {\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]\n fn cls() {\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 initial_memory[dof] = true;\n }\n ws += \" \";\n }\n ws += \"\\n\";\n }\n let mut set_x = Chip8Video::new(initial_memory);\n set_x.cls();\n assert_eq!(set_x.format_as_string(), ws);\n }\n\n #[test]\n fn poke_byte_test() {\n let to_poke = 0b10101010;\n let mut v = Chip8Video::default();\n v.poke_byte(0x00, to_poke);\n assert!(v.peek(0x00));\n assert!(v.peek(0x02));\n assert!(v.peek(0x04));\n assert!(v.peek(0x06));\n assert!(!v.peek(0x01));\n assert!(!v.peek(0x03));\n assert!(!v.peek(0x05));\n assert!(!v.peek(0x07));\n for i in 0x8..CHIP8_VIDEO_MEMORY {\n assert!(!v.peek(i as u16));\n }\n }\n\n #[test]\n fn poke_multi_line_test() {\n let mut v = Chip8Video::default();\n let to_poke = [\n 0b00000000,\n 0b11111111,\n 0b10101010,\n 0b01010101\n ];\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.peek(0x40));\n assert!(v.peek(0x41));\n assert!(v.peek(0x42));\n assert!(v.peek(0x43));\n assert!(v.peek(0x44));\n assert!(v.peek(0x45));\n assert!(v.peek(0x46));\n assert!(v.peek(0x47));\n\n // row 3 column 1\n assert!(!v.peek(0xC0));\n assert!(v.peek(0xC1));\n assert!(!v.peek(0xC2));\n assert!(v.peek(0xC3));\n assert!(!v.peek(0xC4));\n assert!(v.peek(0xC5));\n assert!(!v.peek(0xC6));\n assert!(v.peek(0xC7));\n }\n }\n\n #[test]\n fn moved_poke_test() {\n let mut v = Chip8Video::default();\n let to_poke = [\n 0b00000000,\n 0b11111111,\n 0b10101010,\n 0b01010101\n ];\n\n let x_offset = 20;\n let y_offset = 5;\n\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.peek(test_offset));\n assert!(!v.peek(test_offset + 1));\n assert!(!v.peek(test_offset + 2));\n assert!(!v.peek(test_offset + 3));\n assert!(!v.peek(test_offset + 4));\n assert!(!v.peek(test_offset + 5));\n assert!(!v.peek(test_offset + 6));\n assert!(!v.peek(test_offset + 7));\n\n let test_offset= test_offset + 0x40;\n assert!(v.peek(test_offset));\n assert!(v.peek(test_offset + 1));\n assert!(v.peek(test_offset + 2));\n assert!(v.peek(test_offset + 3));\n assert!(v.peek(test_offset + 4));\n assert!(v.peek(test_offset + 5));\n assert!(v.peek(test_offset + 6));\n assert!(v.peek(test_offset + 7));\n }\n\n #[test]\n fn poke_sprite_test() {\n let mut v = Chip8Video::default();\n let to_poke = [\n 0b00000000,\n 0b11111111,\n 0b10101010,\n 0b01010101\n ];\n\n v.poke_sprite(0x00, to_poke.into());\n\n assert!(v.peek(0x40));\n assert!(v.peek(0x41));\n assert!(v.peek(0x42));\n assert!(v.peek(0x43));\n assert!(v.peek(0x44));\n assert!(v.peek(0x45));\n assert!(v.peek(0x46));\n assert!(v.peek(0x47));\n\n // row 3 column 1\n assert!(!v.peek(0xC0));\n assert!(v.peek(0xC1));\n assert!(!v.peek(0xC2));\n assert!(v.peek(0xC3));\n assert!(!v.peek(0xC4));\n assert!(v.peek(0xC5));\n assert!(!v.peek(0xC6));\n assert!(v.peek(0xC7));\n }\n\n #[test]\n fn verify_change_registered() {\n let mut v = Chip8Video::default();\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]\n fn write_checkboard() {\n let mut v = Chip8Video::default();\n for current_row in 0..CHIP8_VIDEO_WIDTH {\n for current_col in 0..(CHIP8_VIDEO_HEIGHT / 8){\n let offset = current_row * CHIP8_VIDEO_HEIGHT + current_col;\n println!(\"CHECKBOARD OFFSET = {offset}\");\n v.poke(offset as u16, offset % 2 == 0);\n }\n }\n println!(\"{}\", v.format_as_string());\n println!(\"fsck is a cool tool\");\n }\n\n #[test]\n fn zero_test() {\n let mut x = Chip8Video::default();\n\n x.poke_byte(0x00, CHIP8FONT_0[0]);\n x.poke_byte(0x40, CHIP8FONT_0[1]);\n x.poke_byte(0x80, CHIP8FONT_0[2]);\n x.poke_byte(0xC0, CHIP8FONT_0[3]);\n x.poke_byte(0x100, CHIP8FONT_0[4]);\n\n assert_eq!(std::fs::read_to_string(\"../resources/test/test_video_zero.asc\")\n .unwrap(),\n x.format_as_string());\n }\n\n #[test]\n fn multi_sprite_test() {\n let mut x = Chip8Video::default();\n // draw a row of digits 01234567\n\n let start_offset = 0x00;\n let per_row_skip = 0x40;\n let to_draw = [CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_3, CHIP8FONT_4, CHIP8FONT_5, CHIP8FONT_6, CHIP8FONT_7];\n for (index, sprite) in to_draw.iter().enumerate() {\n let data_base_offset = index * 0x8;\n println!(\"STARTING {index} at 0x{data_base_offset:04x} ({data_base_offset})\");\n x.poke_byte(data_base_offset as u16, sprite[0]);\n x.poke_byte((data_base_offset + 0x40) as u16, sprite[1]);\n x.poke_byte((data_base_offset + 0x80) as u16, sprite[2]);\n x.poke_byte((data_base_offset + 0xC0) as u16, sprite[3]);\n x.poke_byte((data_base_offset + 0x100) as u16, sprite[4]);\n }\n\n\n assert_eq!(std::fs::read_to_string(\"../resources/test/test_multi_sprite.asc\")\n .unwrap(),\n x.format_as_string()); }\n}\n","traces":[{"line":11,"address":[19759680],"length":1,"stats":{"Line":1}},{"line":12,"address":[1755833,1755870,1755774],"length":1,"stats":{"Line":3}},{"line":13,"address":[1755877,1755848],"length":1,"stats":{"Line":1}},{"line":17,"address":[1755904],"length":1,"stats":{"Line":1}},{"line":18,"address":[1755909],"length":1,"stats":{"Line":1}},{"line":21,"address":[1755920],"length":1,"stats":{"Line":1}},{"line":28,"address":[19759904],"length":1,"stats":{"Line":1}},{"line":29,"address":[1756043,1756000],"length":1,"stats":{"Line":1}},{"line":32,"address":[1756064],"length":1,"stats":{"Line":1}},{"line":33,"address":[1756196,1756110,1756425],"length":1,"stats":{"Line":2}},{"line":34,"address":[1756650,1756141,1756620],"length":1,"stats":{"Line":2}},{"line":35,"address":[1756632,1756989],"length":1,"stats":{"Line":2}},{"line":36,"address":[1757022,1756710],"length":1,"stats":{"Line":2}},{"line":37,"address":[1756982],"length":1,"stats":{"Line":1}},{"line":39,"address":[1756810,1756671],"length":1,"stats":{"Line":2}},{"line":41,"address":[19761123,19760676,19761156],"length":1,"stats":{"Line":2}},{"line":42,"address":[1757213],"length":1,"stats":{"Line":1}},{"line":45,"address":[1757264],"length":1,"stats":{"Line":4}},{"line":46,"address":[1757418,1757323],"length":1,"stats":{"Line":8}},{"line":47,"address":[1757436,1757551],"length":1,"stats":{"Line":4}},{"line":49,"address":[1757673,1757528,1757577],"length":1,"stats":{"Line":8}},{"line":50,"address":[1757632],"length":1,"stats":{"Line":4}},{"line":51,"address":[1757649],"length":1,"stats":{"Line":4}},{"line":53,"address":[19761320],"length":1,"stats":{"Line":4}},{"line":56,"address":[1757696,1758217],"length":1,"stats":{"Line":1}},{"line":57,"address":[1757750,1757817],"length":1,"stats":{"Line":2}},{"line":59,"address":[1758070,1757825],"length":1,"stats":{"Line":2}},{"line":60,"address":[1758164,1758106,1758192],"length":1,"stats":{"Line":2}},{"line":61,"address":[19762096,19762140],"length":1,"stats":{"Line":2}},{"line":63,"address":[1758063],"length":1,"stats":{"Line":1}},{"line":66,"address":[19762176,19763504],"length":1,"stats":{"Line":1}},{"line":67,"address":[1758276],"length":1,"stats":{"Line":1}},{"line":68,"address":[1758281,1758377,1758518],"length":1,"stats":{"Line":3}},{"line":69,"address":[1758693,1758539],"length":1,"stats":{"Line":2}},{"line":70,"address":[19762650,19762775,19762685],"length":1,"stats":{"Line":2}},{"line":71,"address":[1758861,1759260,1758932,1759132,1758797],"length":1,"stats":{"Line":3}},{"line":72,"address":[19763390,19762803],"length":1,"stats":{"Line":2}},{"line":73,"address":[1759531,1759563],"length":1,"stats":{"Line":2}},{"line":75,"address":[1759558,1759504],"length":1,"stats":{"Line":2}},{"line":78,"address":[1758661,1758734],"length":1,"stats":{"Line":2}},{"line":81,"address":[1758475],"length":1,"stats":{"Line":1}},{"line":86,"address":[1759600],"length":1,"stats":{"Line":4}},{"line":87,"address":[1759617],"length":1,"stats":{"Line":4}}],"covered":43,"coverable":43},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","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","traces":[],"covered":0,"coverable":0},{"path":["/","home","tmerritt","Projects","chip8_toy","emma","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}\n\npub mod constants;","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> |