Added disassembly component.

This commit is contained in:
2026-06-18 07:40:23 +02:00
parent 19f4593e0e
commit 350a26d1b9
5 changed files with 207 additions and 32 deletions
+136 -19
View File
@@ -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[:]
}