package simulator import "core:fmt" import "core:log" import rl "vendor:raylib" import emu "../machine" import tfd "../../external/tinyfiledialogs" Instruction :: struct { address: u16, raw: u16, str: string } gui_file_loader :: proc(rect: rl.Rectangle, sim: ^Simulator) { rl.DrawRectangleLinesEx(rect, 1, rl.GRAY) // FILE SECTION: // --------------------------------------------- top_bounds_height := (rect.height - (PADDING_Y * 2)) / 3 bounds := rl.Rectangle { x = rect.x + PADDING_X, y = rect.y + PADDING_Y, width = rect.width - (PADDING_X * 2), height = top_bounds_height } rl.GuiPanel(bounds, nil) btn_rect := rl.Rectangle { bounds.x, bounds.y, rect.width - (PADDING_X * 2), BUTTON_HEIGHT, } if rl.GuiButton(btn_rect, "Open ROM") { ret := tfd.openFileDialog("Open File Dialog", nil, 0, nil, nil, 0,) rom_path := string(ret) if rom_path == "" do return // Reset machine state emu.reset_machine(sim.machine) // load new rom rom_size, err := emu.load_rom(sim.machine, rom_path) if err != nil { // @TODO: update status bar here panic("failed to load rom!") } sim.rom_loaded = true // Free old instructions on sim delete(sim.disasm) sim.disasm = disassemble_rom(sim.machine.memory[:], 0x200, 0x200 + u16(rom_size)) } // drop-zone occupies the panel's content area, minus space for the button drop_zone := rl.Rectangle { bounds.x + PADDING_X, bounds.y + btn_rect.height + PADDING_Y * 2, bounds.width - (PADDING_X * 2), bounds.height - (btn_rect.height) - (PADDING_Y * 4) } rl.DrawRectangleLinesEx(drop_zone, 1, rl.LIGHTGRAY) // centered drop-zone text text: cstring = "Drop a CHIP-8 ROM here" text_width := rl.MeasureText(text, BIG_FONT_SIZE) text_x := drop_zone.x + (drop_zone.width - f32(text_width)) text_y := drop_zone.y + (drop_zone.height - f32(BIG_FONT_SIZE)) / 2 rl.DrawTextEx(sim.font, text, {text_x, text_y}, BIG_FONT_SIZE, 1, rl.WHITE) // Handle file drop if rl.IsFileDropped() { dropped_file := rl.LoadDroppedFiles() if dropped_file.count > 0 { mouse := rl.GetMousePosition() if rl.CheckCollisionPointRec(mouse, drop_zone) { path_str := string(dropped_file.paths[0]) log.info("file dropped: ", path_str) } else { log.info("File dropped outside drop zone, ignoring") } } rl.UnloadDroppedFiles(dropped_file) } // FOLDER SECTION: // --------------------------------------------- bottom_bounds := rl.Rectangle { x = bounds.x, y = bounds.y + top_bounds_height, width = rect.width - (PADDING_X * 2), height = rect.height - top_bounds_height - (PADDING_X * 2) } rl.GuiPanel(bottom_bounds, nil) bottom_btn_rect := rl.Rectangle { bottom_bounds.x, bottom_bounds.y, rect.width - (PADDING_X * 2), BUTTON_HEIGHT, } rl.GuiButton(bottom_btn_rect, "Open ROM Folder") } disassemble_rom :: proc(memory: []u8, start: u16, end_addr: u16) -> []Instruction { entries := make([dynamic]Instruction) i := start for i < end_addr { if int(i) + 1 >= len(memory) do break addr := i high_byte := memory[i] low_byte := memory[i + 1] i += 2 // Get full opcode from the 2 bytes opcode := (u16(high_byte) << 8) | u16(low_byte) // Pre-decode common components of opcodes first_nibble := (opcode & 0xF000) >> 12 vx := (opcode & 0x0F00) >> 8 // A 4-bit value, the lower 4 bits of the high byte of the instruction vy := (opcode & 0x00F0) >> 4 // A 4-bit value, the upper 4 bits of the low byte of the instruction last_nibble := (opcode & 0x000F) // A 4-bit value, the lowest 4 bits of the instruction kk := u8(opcode & 0x00FF) // An 8-bit value, the lowest 8 bits of the instruction nnn := (opcode & 0x0FFF) // A 12-bit value, the lowest 12 bits of the instruction str: string switch first_nibble { case 0x0: switch opcode { case 0x00E0: str = "CLS" case 0x00EE: str = "RET" case: str = fmt.tprintf("DATA 0x%04X", opcode) } case 0x1: str = fmt.tprintf("JP 0x%03X", nnn) case 0x2: str = fmt.tprintf("CALL 0x%03X", nnn) case 0x3: str = fmt.tprintf("SE V%X, 0x%02X", vx, kk) case 0x4: str = fmt.tprintf("SNE V%X, 0x%02X", vx, kk) case 0x5: str = fmt.tprintf("SE V%X, V%X", vx, vy) case 0x6: str = fmt.tprintf("LD V%X, 0x%02X", vx, kk) case 0x7: str = fmt.tprintf("ADD V%X, 0x%02X", vx, kk) case 0x8: switch last_nibble { case 0x0: str = fmt.tprintf("LD V%X, V%X", vx, vy) case 0x1: str = fmt.tprintf("OR V%X, V%X", vx, vy) case 0x2: str = fmt.tprintf("AND V%X, V%X", vx, vy) case 0x3: str = fmt.tprintf("XOR V%X, V%X", vx, vy) case 0x4: str = fmt.tprintf("ADD V%X, V%X", vx, vy) case 0x5: str = fmt.tprintf("SUB V%X, V%X", vx, vy) case 0x6: str = fmt.tprintf("SHR V%X", vx) case 0x7: str = fmt.tprintf("SUBN V%X, V%X", vx, vy) case 0xE: str = fmt.tprintf("SHL V%X", vx) case: str = fmt.tprintf("DATA 0x%04X", opcode) } case 0x9: str = fmt.tprintf("SNE V%X, V%X", vx, vy) case 0xA: str = fmt.tprintf("LD I, 0x%03X", nnn) case 0xB: str = fmt.tprintf("JP V0, 0x%03X", nnn) case 0xC: str = fmt.tprintf("RND V%X, 0x%02X", vx, kk) case 0xD: str = fmt.tprintf("DRW V%X, V%X, %X", vx, vy, last_nibble) case 0xE: switch kk { case 0x9E: str = fmt.tprintf("SKP V%X", vx) case 0xA1: str = fmt.tprintf("SKNP V%X", vx) case: str = fmt.tprintf("DATA 0x%04X", opcode) } case 0xF: switch kk { case 0x07: str = fmt.tprintf("LD V%X, DT", vx) case 0x0A: str = fmt.tprintf("LD V%X, K", vx) case 0x15: str = fmt.tprintf("LD DT, V%X", vx) case 0x18: str = fmt.tprintf("LD ST, V%X", vx) case 0x1E: str = fmt.tprintf("ADD I, V%X", vx) case 0x29: str = fmt.tprintf("LD F, V%X", vx) case 0x33: str = fmt.tprintf("LD B, V%X", vx) case 0x55: str = fmt.tprintf("LD [I], V%X", vx) case 0x65: str = fmt.tprintf("LD V%X, [I]", vx) case: str = fmt.tprintf("DATA 0x%04X", opcode) } case: str = fmt.tprintf("DATA 0x%04X", opcode) } append(&entries, Instruction{address = addr, raw = opcode, str = str}) } return entries[:] }