Added/updated components to be isolated rectangles.
This commit is contained in:
+81
-47
@@ -3,29 +3,36 @@ package simulator
|
|||||||
import emu "../machine"
|
import emu "../machine"
|
||||||
import rl "vendor:raylib"
|
import rl "vendor:raylib"
|
||||||
|
|
||||||
// Initial window size
|
// @ Window
|
||||||
WINDOW_WIDTH :: 1920
|
WINDOW_WIDTH :: 1920
|
||||||
WINDOW_HEIGHT :: 1080
|
WINDOW_HEIGHT :: 1080
|
||||||
|
|
||||||
// @TODO: If this grows lets move it into its own file
|
// @ Layout proportions
|
||||||
// ─── Layout constants ───────────────────────────────────────────────────
|
// Sidebar takes 20% of screen width on each side; display takes the rest.
|
||||||
SIDEBAR_PERCENT :: 0.20
|
// The center column splits vertically: 40% display, 60% debug panel.
|
||||||
DISPLAY_PERCENT :: 0.30
|
SIDEBAR_PERCENT :: f32(0.20)
|
||||||
|
DISPLAY_V_RATIO :: f32(0.40)
|
||||||
|
// @ Fixed heights
|
||||||
CONTROL_BAR_H :: f32(50)
|
CONTROL_BAR_H :: f32(50)
|
||||||
STATUS_BAR_H :: f32(30)
|
STATUS_BAR_H :: f32(30)
|
||||||
PANEL_PADDING :: 10
|
// @ Layout constants
|
||||||
PANEL_HEADER :: 24
|
PADDING_X :: f32(10)
|
||||||
|
PADDING_Y :: f32(8)
|
||||||
|
PANEL_HEADER :: f32(24)
|
||||||
|
// @ Buttons
|
||||||
BUTTON_HEIGHT :: 30
|
BUTTON_HEIGHT :: 30
|
||||||
BUTTON_WIDTH :: 120
|
BUTTON_WIDTH :: 120
|
||||||
|
// @ Fonts
|
||||||
BIG_FONT_SIZE :: 20
|
BIG_FONT_SIZE :: 20
|
||||||
KEYPAD_FONT_SIZE :: 18
|
KEYPAD_FONT_SIZE :: 18
|
||||||
|
|
||||||
Layout :: struct {
|
Layout :: struct {
|
||||||
control_bar : rl.Rectangle,
|
control_bar : rl.Rectangle,
|
||||||
left_panel : rl.Rectangle,
|
file_loader : rl.Rectangle,
|
||||||
|
keypad : rl.Rectangle,
|
||||||
display : rl.Rectangle,
|
display : rl.Rectangle,
|
||||||
bottom_panel : rl.Rectangle,
|
bottom_panel : rl.Rectangle,
|
||||||
right_panel : rl.Rectangle,
|
cpu : rl.Rectangle,
|
||||||
status_bar : rl.Rectangle,
|
status_bar : rl.Rectangle,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +53,7 @@ run_gui :: proc(sim: ^Simulator) {
|
|||||||
rl.GuiLoadStyle("./assets/raygui_styles/style_dark.rgs")
|
rl.GuiLoadStyle("./assets/raygui_styles/style_dark.rgs")
|
||||||
|
|
||||||
rl.GuiSetFont(font)
|
rl.GuiSetFont(font)
|
||||||
rl.GuiSetStyle(.DEFAULT, cast(i32)rl.GuiDefaultProperty.TEXT_SIZE, 18)
|
rl.GuiSetStyle(.DEFAULT, i32(rl.GuiDefaultProperty.TEXT_SIZE), 18)
|
||||||
|
|
||||||
// Draw each of the components in its own window within the main window
|
// Draw each of the components in its own window within the main window
|
||||||
for !rl.WindowShouldClose() {
|
for !rl.WindowShouldClose() {
|
||||||
@@ -79,9 +86,26 @@ run_gui :: proc(sim: ^Simulator) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Top
|
||||||
|
// ------------------------------------------
|
||||||
gui_control_bar(layout.control_bar, sim)
|
gui_control_bar(layout.control_bar, sim)
|
||||||
gui_left_panel(layout.left_panel, sim)
|
|
||||||
|
// Left
|
||||||
|
// ------------------------------------------
|
||||||
|
gui_file_loader(layout.file_loader, sim)
|
||||||
|
gui_key_pad(layout.keypad, sim.machine.keypad, sim.font)
|
||||||
|
|
||||||
|
// Center
|
||||||
|
// ------------------------------------------
|
||||||
gui_screen(layout.display, sim)
|
gui_screen(layout.display, sim)
|
||||||
|
|
||||||
|
// Right
|
||||||
|
// ------------------------------------------
|
||||||
|
gui_cpu(layout.cpu, sim)
|
||||||
|
|
||||||
|
// Bottom
|
||||||
|
// ------------------------------------------
|
||||||
|
gui_bottom_tabs(layout.bottom_panel, sim)
|
||||||
gui_status_bar(layout.status_bar, sim)
|
gui_status_bar(layout.status_bar, sim)
|
||||||
|
|
||||||
rl.EndDrawing()
|
rl.EndDrawing()
|
||||||
@@ -95,64 +119,74 @@ run_gui :: proc(sim: ^Simulator) {
|
|||||||
|
|
||||||
// @TODO: If this grows lets move it into its own file
|
// @TODO: If this grows lets move it into its own file
|
||||||
calc_layout :: proc(screen_width: f32, screen_height: f32) -> Layout {
|
calc_layout :: proc(screen_width: f32, screen_height: f32) -> Layout {
|
||||||
top_h := CONTROL_BAR_H
|
// Control bar is a fixed height frozen at top of gui, all items start below it.
|
||||||
bottom_h := STATUS_BAR_H
|
y_pos := CONTROL_BAR_H
|
||||||
content_h := screen_height - top_h - bottom_h
|
x_pos := f32(0)
|
||||||
content_y := top_h
|
|
||||||
|
|
||||||
sidebar_w := screen_width * SIDEBAR_PERCENT
|
// Usable gui vertical space
|
||||||
|
visible_height := screen_height - CONTROL_BAR_H - STATUS_BAR_H
|
||||||
|
sidebar_width := screen_width * SIDEBAR_PERCENT
|
||||||
|
display_width := screen_width - (sidebar_width * 2) - sidebar_width
|
||||||
|
display_height := visible_height * DISPLAY_V_RATIO
|
||||||
|
|
||||||
screen_h_ratio := 0.40
|
x_center := sidebar_width
|
||||||
bottom_panel_h := content_h * f32(1.0 - screen_h_ratio)
|
y_center := screen_width - sidebar_width * 2
|
||||||
screen_h := content_h * f32(screen_h_ratio)
|
|
||||||
memory_h := content_h - screen_h
|
|
||||||
|
|
||||||
center_x := sidebar_w
|
|
||||||
center_w := screen_width - sidebar_w * 2
|
|
||||||
|
|
||||||
return Layout {
|
return Layout {
|
||||||
|
// Left Area
|
||||||
control_bar = rl.Rectangle{
|
control_bar = rl.Rectangle{
|
||||||
x = 0,
|
x = 0,
|
||||||
y = 0,
|
y = 0,
|
||||||
width = screen_width,
|
width = screen_width,
|
||||||
height = top_h,
|
height = CONTROL_BAR_H,
|
||||||
},
|
},
|
||||||
|
file_loader = rl.Rectangle{
|
||||||
left_panel = rl.Rectangle{
|
|
||||||
x = 0,
|
x = 0,
|
||||||
y = top_h,
|
y = y_pos,
|
||||||
width = sidebar_w,
|
width = sidebar_width,
|
||||||
height = content_h,
|
height = visible_height / 3
|
||||||
|
},
|
||||||
|
keypad = rl.Rectangle{
|
||||||
|
x = 0,
|
||||||
|
y = (y_pos + visible_height - visible_height / 2),
|
||||||
|
width = sidebar_width,
|
||||||
|
height = visible_height / 2
|
||||||
},
|
},
|
||||||
|
|
||||||
// CHIP-8 screen (top center)
|
// ------------------------------------------
|
||||||
|
// Center Area
|
||||||
display = rl.Rectangle{
|
display = rl.Rectangle{
|
||||||
x = center_x,
|
x = sidebar_width,
|
||||||
y = top_h,
|
y = y_pos,
|
||||||
width = center_w,
|
width = display_width,
|
||||||
height = screen_h,
|
height = display_height,
|
||||||
},
|
},
|
||||||
|
|
||||||
// MEMORY / DEBUG panel (bottom center)
|
// ------------------------------------------
|
||||||
|
// Bottom Area
|
||||||
bottom_panel = rl.Rectangle{
|
bottom_panel = rl.Rectangle{
|
||||||
x = center_x,
|
x = sidebar_width,
|
||||||
y = top_h + screen_h,
|
y = y_pos + display_height,
|
||||||
width = center_w,
|
width = screen_width - sidebar_width,
|
||||||
height = memory_h,
|
height = visible_height - display_height,
|
||||||
},
|
},
|
||||||
|
|
||||||
right_panel = rl.Rectangle{
|
// ------------------------------------------
|
||||||
x = screen_width - sidebar_w,
|
// Right Area
|
||||||
y = top_h,
|
cpu = rl.Rectangle {
|
||||||
width = sidebar_w,
|
x = sidebar_width + display_width,
|
||||||
height = content_h,
|
y = y_pos,
|
||||||
|
width = sidebar_width * 2,
|
||||||
|
height = display_height
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ------------------------------------------
|
||||||
|
// Bottom Area
|
||||||
status_bar = rl.Rectangle{
|
status_bar = rl.Rectangle{
|
||||||
x = 0,
|
x = x_pos,
|
||||||
y = screen_height - bottom_h,
|
y = y_pos + visible_height,
|
||||||
width = screen_width,
|
width = screen_width,
|
||||||
height = bottom_h,
|
height = STATUS_BAR_H,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package simulator
|
||||||
|
|
||||||
|
import rl "vendor:raylib"
|
||||||
|
|
||||||
|
gui_bottom_tabs :: proc(rect: rl.Rectangle, sim: ^Simulator) {
|
||||||
|
rl.DrawRectangleLinesEx(rect, 1, rl.GRAY)
|
||||||
|
|
||||||
|
// Setup tab bar
|
||||||
|
tabs := [?]cstring{ "Memory", "Disassembly", "Log" }
|
||||||
|
|
||||||
|
tab_bar_rect := rl.Rectangle{
|
||||||
|
rect.x,
|
||||||
|
rect.y,
|
||||||
|
rect.width,
|
||||||
|
BUTTON_HEIGHT + PADDING_Y
|
||||||
|
}
|
||||||
|
|
||||||
|
// inside draw loop:
|
||||||
|
rl.GuiTabBar(tab_bar_rect, &tabs[0], i32(len(tabs)), &sim.active_tab)
|
||||||
|
|
||||||
|
bounds := rl.Rectangle {
|
||||||
|
x = rect.x + PADDING_X,
|
||||||
|
y = rect.y + PADDING_Y + tab_bar_rect.height,
|
||||||
|
width = rect.width - (PADDING_X * 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)
|
||||||
|
case 1: // draw memory panel
|
||||||
|
case 2: // draw display panel
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package simulator
|
||||||
|
|
||||||
|
import rl "vendor:raylib"
|
||||||
|
|
||||||
|
CPU_SECTION_H :: 100 // enough for 3 rows of labels
|
||||||
|
REG_CELL_H :: 40 // fixed, won't grow with the panel
|
||||||
|
|
||||||
|
gui_cpu :: proc(rect: rl.Rectangle, sim: ^Simulator) {
|
||||||
|
rl.DrawRectangleLinesEx(rect, 1, rl.GRAY)
|
||||||
|
|
||||||
|
bounds := rl.Rectangle {
|
||||||
|
x = rect.x + PADDING_X,
|
||||||
|
y = rect.y + PADDING_Y,
|
||||||
|
width = rect.width - (PADDING_X * 2),
|
||||||
|
height = rect.height - (PADDING_Y * 2),
|
||||||
|
}
|
||||||
|
rl.GuiPanel(bounds, "CPU / Registers")
|
||||||
|
|
||||||
|
cpu_rect := rl.Rectangle {
|
||||||
|
x = bounds.x + PADDING_X,
|
||||||
|
y = bounds.y + PADDING_Y + PANEL_HEADER,
|
||||||
|
width = bounds.width - (PADDING_X * 2),
|
||||||
|
height = CPU_SECTION_H - (PADDING_Y * 2),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grid constants
|
||||||
|
COLS :: 2
|
||||||
|
ROWS :: 3
|
||||||
|
LRATIO :: f32(0.6)
|
||||||
|
|
||||||
|
cell_w := cpu_rect.width / COLS
|
||||||
|
cell_h := cpu_rect.height / ROWS
|
||||||
|
|
||||||
|
labels := [5]string{ "Progam Counter", "Increment pointer", "Stack Pointer", "Delay Timer", "Sound Timer" }
|
||||||
|
values := [5]u16{
|
||||||
|
sim.machine.pc,
|
||||||
|
sim.machine.i,
|
||||||
|
u16(sim.machine.sp),
|
||||||
|
u16(sim.machine.delay_timer),
|
||||||
|
u16(sim.machine.sound_timer),
|
||||||
|
}
|
||||||
|
|
||||||
|
for index in 0..<5 {
|
||||||
|
col := index % COLS
|
||||||
|
row := index / COLS
|
||||||
|
|
||||||
|
cell_x := cpu_rect.x + f32(col) * cell_w
|
||||||
|
cell_y := cpu_rect.y + f32(row) * cell_h
|
||||||
|
|
||||||
|
label_rect := rl.Rectangle{cell_x + 20, cell_y, cell_w,cell_h}
|
||||||
|
box_rect := rl.Rectangle{cell_x + cell_w * LRATIO, cell_y, cell_w * (1 - LRATIO), cell_h}
|
||||||
|
|
||||||
|
rl.GuiLabel(label_rect, rl.TextFormat("%s", labels[index]))
|
||||||
|
rl.DrawRectangleLinesEx(box_rect, 1, rl.DARKGRAY)
|
||||||
|
|
||||||
|
// Right-aligned value
|
||||||
|
value_text := rl.TextFormat("0x%04X", values[index])
|
||||||
|
text_w := rl.MeasureText(value_text, 18)
|
||||||
|
value_x := box_rect.x + box_rect.width - f32(text_w) - 4
|
||||||
|
rl.GuiLabel({value_x, box_rect.y, box_rect.width, box_rect.height}, value_text)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
register_bounds := rl.Rectangle {
|
||||||
|
x = bounds.x,
|
||||||
|
y = bounds.y + PANEL_HEADER + PADDING_Y + CPU_SECTION_H,
|
||||||
|
width = bounds.width,
|
||||||
|
height = bounds.height - PANEL_HEADER - PADDING_Y - CPU_SECTION_H,
|
||||||
|
}
|
||||||
|
rl.GuiPanel(register_bounds, "Registers")
|
||||||
|
|
||||||
|
register_rect := rl.Rectangle {
|
||||||
|
x = register_bounds.x + PADDING_X,
|
||||||
|
y = register_bounds.y + PADDING_Y + PANEL_HEADER,
|
||||||
|
width = register_bounds.width - (PADDING_X * 2),
|
||||||
|
height = register_bounds.height - (PADDING_Y * 2) - PANEL_HEADER,
|
||||||
|
}
|
||||||
|
|
||||||
|
// V registers grid (V0–VF, 16 registers)
|
||||||
|
REG_COLS :: 4
|
||||||
|
REG_ROWS :: 4
|
||||||
|
|
||||||
|
reg_cell_w := register_rect.width / REG_COLS
|
||||||
|
|
||||||
|
for index in 0..<16 {
|
||||||
|
col := index % REG_COLS
|
||||||
|
row := index / REG_COLS
|
||||||
|
|
||||||
|
cell_x := register_rect.x + f32(col) * reg_cell_w
|
||||||
|
cell_y := register_rect.y + f32(row) * REG_CELL_H
|
||||||
|
|
||||||
|
label_rect := rl.Rectangle{cell_x + 20, cell_y, reg_cell_w, REG_CELL_H}
|
||||||
|
box_rect := rl.Rectangle{cell_x + reg_cell_w * LRATIO, cell_y, reg_cell_w * (1 - LRATIO), REG_CELL_H}
|
||||||
|
|
||||||
|
rl.GuiLabel(label_rect, rl.TextFormat("V%X", index))
|
||||||
|
rl.DrawRectangleLinesEx(box_rect, 1, rl.DARKGRAY)
|
||||||
|
|
||||||
|
// Right-aligned value
|
||||||
|
value_text := rl.TextFormat("0x%02X", sim.machine.v[index])
|
||||||
|
text_w := rl.MeasureText(value_text, 18)
|
||||||
|
value_x := box_rect.x + box_rect.width - f32(text_w) - 4
|
||||||
|
rl.GuiLabel({value_x, box_rect.y, box_rect.width, box_rect.height}, value_text)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package simulator
|
||||||
|
|
||||||
|
import "core:log"
|
||||||
|
import rl "vendor:raylib"
|
||||||
|
|
||||||
|
import emu "../machine"
|
||||||
|
import tfd "../../external/tinyfiledialogs"
|
||||||
|
|
||||||
|
gui_file_loader :: proc(rect: rl.Rectangle, sim: ^Simulator) {
|
||||||
|
rl.DrawRectangleLinesEx(rect, 1, rl.GRAY)
|
||||||
|
|
||||||
|
bounds := rl.Rectangle {
|
||||||
|
x = rect.x + PADDING_X,
|
||||||
|
y = rect.y + PADDING_Y,
|
||||||
|
width = rect.width - (PADDING_X * 2),
|
||||||
|
height = rect.height - (PADDING_Y * 2),
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
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
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
// @TODO: Stop sim, reset mem etc, load new rom
|
||||||
|
} else {
|
||||||
|
log.info("File dropped outside drop zone, ignoring")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rl.UnloadDroppedFiles(dropped_file)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package simulator
|
||||||
|
|
||||||
|
import "core:strings"
|
||||||
|
import rl "vendor:raylib"
|
||||||
|
|
||||||
|
|
||||||
|
gui_key_pad :: proc(rect: rl.Rectangle, display: [16]bool, font: rl.Font) {
|
||||||
|
rl.DrawRectangleLinesEx(rect, 1, rl.GRAY)
|
||||||
|
|
||||||
|
bounds := rl.Rectangle {
|
||||||
|
x = rect.x + PADDING_X,
|
||||||
|
y = rect.y + PADDING_Y,
|
||||||
|
width = rect.width - (PADDING_X * 2),
|
||||||
|
height = rect.height - (PADDING_Y * 2),
|
||||||
|
}
|
||||||
|
rl.GuiPanel(bounds, "Input / Keypad")
|
||||||
|
|
||||||
|
content := rl.Rectangle {
|
||||||
|
bounds.x,
|
||||||
|
bounds.y + PANEL_HEADER,
|
||||||
|
bounds.width,
|
||||||
|
bounds.height - PANEL_HEADER,
|
||||||
|
}
|
||||||
|
|
||||||
|
Key :: struct {
|
||||||
|
label: string,
|
||||||
|
index: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := [16]Key {
|
||||||
|
{"1", 1}, {"2", 2}, {"3", 3}, {"C", 12},
|
||||||
|
{"4", 4}, {"5", 5}, {"6", 6}, {"D", 13},
|
||||||
|
{"7", 7}, {"8", 8}, {"9", 9}, {"E", 14},
|
||||||
|
{"A", 10}, {"0", 0}, {"B", 11}, {"F", 15},
|
||||||
|
}
|
||||||
|
|
||||||
|
btn_width := content.width / 4
|
||||||
|
btn_height := content.height / 4
|
||||||
|
|
||||||
|
for val, idx in keys {
|
||||||
|
str := strings.clone_to_cstring(val.label)
|
||||||
|
defer delete(str)
|
||||||
|
|
||||||
|
ri := idx / 4
|
||||||
|
ci := idx % 4
|
||||||
|
irect := rl.Rectangle {
|
||||||
|
x = content.x + btn_width * f32(ci),
|
||||||
|
y = content.y + btn_height * f32(ri),
|
||||||
|
width = btn_width,
|
||||||
|
height = btn_height,
|
||||||
|
}
|
||||||
|
|
||||||
|
if display[val.index] { rl.DrawRectangleRec(irect, rl.BLACK) }
|
||||||
|
|
||||||
|
rl.DrawRectangleLinesEx(irect, 1, rl.GRAY)
|
||||||
|
rl.DrawTextEx(
|
||||||
|
font,
|
||||||
|
str,
|
||||||
|
rl.Vector2{irect.x + btn_width / 2, irect.y + btn_height / 2},
|
||||||
|
KEYPAD_FONT_SIZE,
|
||||||
|
1,
|
||||||
|
rl.WHITE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
package simulator
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:strings"
|
||||||
|
import rl "vendor:raylib"
|
||||||
|
|
||||||
|
MEM_INDICATOR_W :: 14
|
||||||
|
MEM_ADDRESS_W :: 72
|
||||||
|
MEM_ROW_H :: 20
|
||||||
|
MEM_HEADER_H :: 24
|
||||||
|
MEM_BYTES_PER_ROW :: 16
|
||||||
|
MEM_TOTAL_ROWS :: 256
|
||||||
|
MEM_FONT_ROWS :: 5
|
||||||
|
MEM_ROM_START :: 512
|
||||||
|
MEM_VIRTUAL_ROWS :: MEM_FONT_ROWS + 1 + (256 - 32)
|
||||||
|
|
||||||
|
MEM_COL_LABELS := [16]string{"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"}
|
||||||
|
|
||||||
|
byte_col_w :: proc(rect: rl.Rectangle) -> f32 {
|
||||||
|
used := f32(MEM_INDICATOR_W + MEM_ADDRESS_W + 12)
|
||||||
|
return (rect.width - used) / 16
|
||||||
|
}
|
||||||
|
|
||||||
|
gui_tab_memory :: proc(rect: rl.Rectangle, sim: ^Simulator) {
|
||||||
|
rl.DrawRectangleRec(rect, rl.DARKGRAY)
|
||||||
|
|
||||||
|
// Header background
|
||||||
|
rl.DrawRectangle(i32(rect.x), i32(rect.y), i32(rect.width), MEM_HEADER_H, rl.BLACK)
|
||||||
|
|
||||||
|
// Header: Address label
|
||||||
|
rl.DrawTextEx(sim.font, "Address", {rect.x + MEM_INDICATOR_W, rect.y + 4}, 16, 1, rl.GRAY)
|
||||||
|
|
||||||
|
// Header: column labels 0-F
|
||||||
|
col_w := byte_col_w(rect)
|
||||||
|
for col in 0..<16 {
|
||||||
|
x := rect.x + MEM_INDICATOR_W + MEM_ADDRESS_W + f32(col) * col_w
|
||||||
|
label := strings.clone_to_cstring(MEM_COL_LABELS[col], context.temp_allocator)
|
||||||
|
rl.DrawTextEx(sim.font, label, {x, rect.y + 4}, 16, 1, rl.GRAY)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header separator
|
||||||
|
rl.DrawLine(
|
||||||
|
i32(rect.x), i32(rect.y + MEM_HEADER_H),
|
||||||
|
i32(rect.x + rect.width), i32(rect.y + MEM_HEADER_H),
|
||||||
|
rl.GRAY,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Scroll panel area (below header)
|
||||||
|
panel_rect := rl.Rectangle{
|
||||||
|
rect.x,
|
||||||
|
rect.y + MEM_HEADER_H,
|
||||||
|
rect.width,
|
||||||
|
rect.height - MEM_HEADER_H,
|
||||||
|
}
|
||||||
|
|
||||||
|
content_rect := rl.Rectangle{
|
||||||
|
0, 0,
|
||||||
|
rect.width - 12,
|
||||||
|
f32(MEM_VIRTUAL_ROWS) * MEM_ROW_H,
|
||||||
|
}
|
||||||
|
|
||||||
|
view : rl.Rectangle
|
||||||
|
rl.GuiScrollPanel(panel_rect, nil, content_rect, &sim.mem_scroll, &view)
|
||||||
|
|
||||||
|
rl.BeginScissorMode(i32(view.x), i32(view.y), i32(view.width), i32(view.height))
|
||||||
|
|
||||||
|
draw_row := 0
|
||||||
|
|
||||||
|
// --- Font rows (0x000 - 0x04F) ---
|
||||||
|
for row in 0..<MEM_FONT_ROWS {
|
||||||
|
addr := row * MEM_BYTES_PER_ROW
|
||||||
|
row_y := panel_rect.y + f32(draw_row) * MEM_ROW_H + sim.mem_scroll.y
|
||||||
|
draw_memory_row(rect, sim, draw_row, addr, row_y)
|
||||||
|
draw_row += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Divider ---
|
||||||
|
divider_y := panel_rect.y + f32(draw_row) * MEM_ROW_H + sim.mem_scroll.y
|
||||||
|
rl.DrawRectangle(i32(rect.x), i32(divider_y), i32(rect.width), MEM_ROW_H, rl.ColorAlpha(rl.BLACK, 0.4))
|
||||||
|
rl.DrawTextEx(
|
||||||
|
sim.font,
|
||||||
|
"---- reserved (0x0050 - 0x01FF) ----",
|
||||||
|
{rect.x + MEM_INDICATOR_W, divider_y + 2},
|
||||||
|
16, 1, rl.DARKGRAY,
|
||||||
|
)
|
||||||
|
draw_row += 1
|
||||||
|
|
||||||
|
// --- ROM rows (0x200 onwards) ---
|
||||||
|
for i := 0; MEM_ROM_START + i * MEM_BYTES_PER_ROW < 4096; i += 1 {
|
||||||
|
addr := MEM_ROM_START + i * MEM_BYTES_PER_ROW
|
||||||
|
row_y := panel_rect.y + f32(draw_row) * MEM_ROW_H + sim.mem_scroll.y
|
||||||
|
draw_memory_row(rect, sim, draw_row, addr, row_y)
|
||||||
|
draw_row += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.EndScissorMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_memory_row :: proc(rect: rl.Rectangle, sim: ^Simulator, visual_row: int, addr: int, row_y: f32) {
|
||||||
|
col_w := byte_col_w(rect)
|
||||||
|
|
||||||
|
if visual_row % 2 == 0 {
|
||||||
|
rl.DrawRectangle(i32(rect.x), i32(row_y), i32(rect.width), MEM_ROW_H, rl.ColorAlpha(rl.BLACK, 0.2))
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_str := fmt.tprintf("%04X", addr)
|
||||||
|
rl.DrawTextEx(
|
||||||
|
sim.font,
|
||||||
|
strings.clone_to_cstring(addr_str, context.temp_allocator),
|
||||||
|
{rect.x + MEM_INDICATOR_W, row_y + 2},
|
||||||
|
16, 1, rl.RAYWHITE,
|
||||||
|
)
|
||||||
|
|
||||||
|
for col in 0..<16 {
|
||||||
|
byte_val := sim.machine.memory[addr + col]
|
||||||
|
byte_str := fmt.tprintf("%02X", byte_val)
|
||||||
|
x := rect.x + MEM_INDICATOR_W + MEM_ADDRESS_W + f32(col) * col_w
|
||||||
|
color := rl.RAYWHITE if byte_val != 0 else rl.GRAY
|
||||||
|
rl.DrawTextEx(
|
||||||
|
sim.font,
|
||||||
|
strings.clone_to_cstring(byte_str, context.temp_allocator),
|
||||||
|
{x, row_y + 2},
|
||||||
|
16, 1, color,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user