Added disassembly component.
This commit is contained in:
@@ -5,13 +5,13 @@ package machine
|
||||
import "core:log"
|
||||
import "core:os"
|
||||
|
||||
load_rom :: proc(s: ^System, file_path: string) -> os.Error {
|
||||
load_rom :: proc(s: ^System, file_path: string) -> (int, os.Error) {
|
||||
log.info("Loading rom from file")
|
||||
|
||||
data, read_err := os.read_entire_file(file_path, context.allocator)
|
||||
if read_err != os.ERROR_NONE {
|
||||
log.errorf("failed to read rom %v", read_err)
|
||||
return read_err
|
||||
return 0, read_err
|
||||
}
|
||||
defer delete(data)
|
||||
|
||||
@@ -21,5 +21,5 @@ load_rom :: proc(s: ^System, file_path: string) -> os.Error {
|
||||
|
||||
log.infof("First few bytes: %X %X %X %X", s.memory[0x200], s.memory[0x201], s.memory[0x202], s.memory[0x203])
|
||||
|
||||
return nil
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
@@ -18,16 +18,23 @@ gui_bottom_tabs :: proc(rect: rl.Rectangle, sim: ^Simulator) {
|
||||
// inside draw loop:
|
||||
rl.GuiTabBar(tab_bar_rect, &tabs[0], i32(len(tabs)), &sim.active_tab)
|
||||
|
||||
bounds := rl.Rectangle {
|
||||
memory_view_bounds := rl.Rectangle {
|
||||
x = rect.x + PADDING_X,
|
||||
y = rect.y + PADDING_Y + tab_bar_rect.height,
|
||||
width = rect.width - (PADDING_X * 2),
|
||||
width = (rect.width - (PADDING_X * 2)) / 2,
|
||||
height = rect.height - (PADDING_Y * 2) - tab_bar_rect.height,
|
||||
}
|
||||
disasm_view_bounds := rl.Rectangle {
|
||||
x = rect.x + memory_view_bounds.width + PADDING_X,
|
||||
y = rect.y + PADDING_Y + tab_bar_rect.height,
|
||||
width = (rect.width - (PADDING_X * 2)) / 2,
|
||||
height = rect.height - (PADDING_Y * 2) - tab_bar_rect.height,
|
||||
}
|
||||
|
||||
switch sim.active_tab {
|
||||
case 0: // draw registers panel
|
||||
gui_tab_memory(bounds, sim)
|
||||
gui_tab_memory(memory_view_bounds, sim)
|
||||
gui_tab_disasm(disasm_view_bounds, sim)
|
||||
case 1: // draw memory panel
|
||||
case 2: // draw display panel
|
||||
}
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
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)
|
||||
|
||||
@@ -17,26 +24,11 @@ gui_file_loader :: proc(rect: rl.Rectangle, sim: ^Simulator) {
|
||||
}
|
||||
rl.GuiPanel(bounds, "Rom / File")
|
||||
|
||||
// drop-zone occupies the panel's content area, minus space for the button
|
||||
drop_zone := rl.Rectangle {
|
||||
bounds.x + PADDING_X,
|
||||
bounds.y + PADDING_Y,
|
||||
bounds.width - (PADDING_X * 2),
|
||||
bounds.height - ((PADDING_Y * 2) + (BUTTON_HEIGHT + PADDING_Y))
|
||||
}
|
||||
|
||||
// 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)) / 2
|
||||
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)
|
||||
|
||||
// open rom button below drop-zone
|
||||
btn_rect := rl.Rectangle {
|
||||
drop_zone.x,
|
||||
drop_zone.y + drop_zone.height + PADDING_Y,
|
||||
drop_zone.width,
|
||||
bounds.x,
|
||||
bounds.y,
|
||||
rect.width - (PADDING_X * 2),
|
||||
BUTTON_HEIGHT,
|
||||
}
|
||||
|
||||
@@ -49,15 +41,32 @@ gui_file_loader :: proc(rect: rl.Rectangle, sim: ^Simulator) {
|
||||
emu.reset_machine(sim.machine)
|
||||
|
||||
// load new rom
|
||||
err := emu.load_rom(sim.machine, rom_path)
|
||||
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
|
||||
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)) / 2
|
||||
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()
|
||||
@@ -75,3 +84,111 @@ gui_file_loader :: proc(rect: rl.Rectangle, sim: ^Simulator) {
|
||||
rl.UnloadDroppedFiles(dropped_file)
|
||||
}
|
||||
}
|
||||
|
||||
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[:]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package simulator
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strings"
|
||||
import rl "vendor:raylib"
|
||||
|
||||
gui_tab_disasm :: proc(rect: rl.Rectangle, sim: ^Simulator) {
|
||||
rl.DrawRectangleRec(rect, rl.DARKGRAY)
|
||||
rl.GuiPanel(rect, "Disassembly")
|
||||
|
||||
LINE_HEIGHT :: 22
|
||||
|
||||
// Total height of all instructions
|
||||
conten_height := f32(len(sim.disasm)) * LINE_HEIGHT
|
||||
|
||||
content_rect := rl.Rectangle {
|
||||
x = rect.x,
|
||||
y = rect.y,
|
||||
width = rect.width + PANEL_HEADER,
|
||||
height = conten_height,
|
||||
}
|
||||
|
||||
view: rl.Rectangle
|
||||
rl.GuiScrollPanel(rect, nil, content_rect, &sim.disasm_scroll, &view)
|
||||
|
||||
rl.BeginScissorMode(i32(view.x), i32(view.y), i32(view.width), i32(view.height))
|
||||
defer rl.EndScissorMode()
|
||||
|
||||
for entry, i in sim.disasm {
|
||||
y_pos := rect.y + PANEL_HEADER + (f32(i) * LINE_HEIGHT) + sim.disasm_scroll.y
|
||||
|
||||
txt := fmt.tprintf("%d : %s", i, entry.str)
|
||||
bg_color := rl.DARKGRAY if entry.address != sim.machine.pc else rl.BLACK
|
||||
txt_color := rl.WHITE
|
||||
|
||||
rl.DrawRectangleV(
|
||||
{rect.x + sim.disasm_scroll.x, y_pos},
|
||||
{rect.width, LINE_HEIGHT},
|
||||
bg_color,
|
||||
)
|
||||
rl.DrawTextEx(
|
||||
sim.font,
|
||||
strings.clone_to_cstring(txt, context.temp_allocator),
|
||||
{rect.x + PADDING_X + sim.disasm_scroll.x, y_pos},
|
||||
18, 1,
|
||||
txt_color,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,17 @@ import rl "vendor:raylib"
|
||||
|
||||
Simulator :: struct {
|
||||
// Emulator
|
||||
machine: ^emu.System,
|
||||
rom_loaded: bool,
|
||||
running: bool,
|
||||
paused: bool,
|
||||
machine: ^emu.System,
|
||||
rom_loaded: bool,
|
||||
running: bool,
|
||||
paused: bool,
|
||||
cycles_per_second: int,
|
||||
// GUI
|
||||
font: rl.Font,
|
||||
active_tab: i32,
|
||||
mem_scroll : rl.Vector2,
|
||||
font: rl.Font,
|
||||
active_tab: i32,
|
||||
mem_scroll: rl.Vector2,
|
||||
disasm: []Instruction,
|
||||
disasm_scroll: rl.Vector2,
|
||||
}
|
||||
|
||||
// Requires an initilized emulatore System Struct
|
||||
|
||||
Reference in New Issue
Block a user