Fighting the wasm build...

This commit is contained in:
2026-07-02 10:20:35 +02:00
parent 747702d256
commit ec8bcdb5ed
8 changed files with 225 additions and 43 deletions
+14 -13
View File
@@ -1,7 +1,5 @@
package machine
import "core:log"
// System struct, init, constants, fontset
System :: struct {
@@ -48,7 +46,7 @@ FONT_SET := [80]u8 {
}
init :: proc() -> System {
log.info("Booting chip 8 cpu")
// log.info("Booting chip 8 cpu")
// Structs are zero initialized so timers, sp etc are good.
s := System { pc = 0x200 }
@@ -69,17 +67,20 @@ run_machine :: proc(s: ^System, cycles: int) {
}
}
new_machine :: proc() -> System {
s: System
s.pc = 0x200
s.current_key = -1
// load fonts into the memory
reset_machine :: proc(s: ^System) {
s.memory = {}
s.v = {}
s.stack = {}
s.sp = 0
s.i = 0
s.pc = 0x200
s.display = {}
s.keypad = {}
s.current_key = -1
s.delay_timer = 0
s.sound_timer = 0
for v, i in FONT_SET {
s.memory[i] = v
}
return s
}
reset_machine :: proc(s: ^System) {
s^ = new_machine()
}
+2 -2
View File
@@ -34,10 +34,10 @@ main :: proc() {
sim.init(&s)
for sim.should_run() {
sim.update(&s)
sim.update()
}
sim.shutdown(&s)
sim.shutdown()
when DEV {
if len(track.allocation_map) > 0 {
+8 -9
View File
@@ -21,7 +21,7 @@ main_start :: proc "c" () {
// emscripten. There is some kind of conflict with how the manage memory.
// So this sets up an allocator that uses emscripten's malloc.
context.allocator = emscripten_allocator()
runtime.init_global_temporary_allocator(1*mem.Megabyte)
runtime.init_global_temporary_allocator(4*mem.Megabyte)
// Since we now use js_wasm32 we should be able to remove this and use
// context.logger = log.create_console_logger(). However, that one produces
@@ -30,25 +30,25 @@ main_start :: proc "c" () {
web_context = context
// Init the emu 8 "cpu"
system := emu.init()
s := sim.Simulator {
machine = &system,
system := new(emu.System)
system^ = emu.init()
s := new(sim.Simulator)
s^ = sim.Simulator {
machine = system,
rom_loaded = false,
paused = true,
step = false,
cpu_hz = 700,
disasm_follow = true,
}
sim.init(&s)
sim.init(s)
}
@export
main_update :: proc "c" () -> bool {
context = web_context
// TODO
sim.update()
return sim.should_run()
}
@@ -56,7 +56,6 @@ main_update :: proc "c" () -> bool {
@export
main_end :: proc "c" () {
context = web_context
// TODO
sim.shutdown()
}
+8 -4
View File
@@ -3,6 +3,8 @@ package simulator
import emu "../machine"
import rl "vendor:raylib"
sim_run: bool
// Window
WINDOW_WIDTH :: 1920
WINDOW_HEIGHT :: 1080
@@ -40,6 +42,7 @@ Layout :: struct {
init :: proc(sim: ^Simulator) {
_sim = sim
sim_run = true
// desktop
when ODIN_OS != .JS {
@@ -49,7 +52,7 @@ init :: proc(sim: ^Simulator) {
// web
when ODIN_OS == .JS {
rl.SetConfigFlags({.VSYNC_HINT})
// rl.SetConfigFlags({.VSYNC_HINT})
}
rl.InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Octal Cookie - Chip 8 Simulator")
@@ -86,8 +89,8 @@ update :: proc() {
rl.BeginDrawing()
rl.ClearBackground(rl.Color{0x18, 0x18, 0x18, 0xFF})
cycles := int(sim.cpu_hz / SIM_FPS)
if (!sim.paused) {
cycles := int(sim.cpu_hz / SIM_FPS)
// Cycle the machine to update memory etc
emu.run_machine(sim.machine, cycles)
tick_timers(sim)
@@ -106,7 +109,7 @@ update :: proc() {
// Left
// ------------------------------------------
when ODIN_OS != .JS { gui_file_loader(layout.file_loader, sim)}
gui_file_loader(layout.file_loader, sim)
gui_key_pad(layout.keypad, sim.machine.keypad, sim.font)
// Center
@@ -142,7 +145,8 @@ should_run :: proc() -> bool {
when ODIN_OS != .JS {
return !rl.WindowShouldClose()
}
return true // web loop is controlled by JS requestAnimationFrame
// web loop is controlled by JS requestAnimationFrame
return true
}
tick_timers :: proc(sim: ^Simulator) {
+2 -6
View File
@@ -1,9 +1,7 @@
package simulator
import "core:fmt"
import "core:log"
import emu "../machine"
import rl "vendor:raylib"
gui_control_bar :: proc(rect: rl.Rectangle, sim: ^Simulator) {
@@ -19,7 +17,7 @@ gui_control_bar :: proc(rect: rl.Rectangle, sim: ^Simulator) {
if sim.rom_loaded {
sim.paused = false
} else {
log.info("no rom selected, can't run")
// log.info("no rom selected, can't run")
}
}
@@ -32,9 +30,7 @@ gui_control_bar :: proc(rect: rl.Rectangle, sim: ^Simulator) {
}
if btn(&cursor, rect, BUTTON_HEIGHT, BUTTON_WIDTH, PADDING_X, "RESET") {
sim.paused = true
sim.disasm_count = 0
emu.reset_machine(sim.machine)
reset_sim(sim)
}
slider_rect := rl.Rectangle{
+7 -8
View File
@@ -3,7 +3,6 @@ package simulator
import "core:strings"
import "core:fmt"
import "core:log"
import rl "vendor:raylib"
import emu "../machine"
@@ -60,10 +59,10 @@ gui_file_loader :: proc(rect: rl.Rectangle, sim: ^Simulator) {
mouse := rl.GetMousePosition()
if rl.CheckCollisionPointRec(mouse, drop_zone) {
path_str := string(dropped_file.paths[0])
log.info("file dropped: ", path_str)
// path_str := string(dropped_file.paths[0])
// log.info("file dropped: ", path_str)
} else {
log.info("File dropped outside drop zone, ignoring")
// log.info("File dropped outside drop zone, ignoring")
}
}
rl.UnloadDroppedFiles(dropped_file)
@@ -136,22 +135,22 @@ gui_file_loader :: proc(rect: rl.Rectangle, sim: ^Simulator) {
}
load_selected_rom :: proc(sim: ^Simulator, rom_path: string) {
emu.reset_machine(sim.machine)
reset_sim(sim)
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.paused = true
sim.disasm_count = disassemble_rom_n(sim.machine.memory[:], 0x200, 0x200 + u16(rom_size), sim.disasm[:])
}
load_embedded_rom :: proc(sim: ^Simulator, path: string, data: []byte) {
emu.reset_machine(sim.machine)
reset_sim(sim)
copy(sim.machine.memory[0x200:], data)
sim.rom_loaded = true
sim.paused = true
sim.disasm_count = disassemble_rom_n(sim.machine.memory[:], 0x200, 0x200 + u16(len(data)), sim.disasm[:])
}
+174
View File
@@ -0,0 +1,174 @@
#+build js
package simulator
import "core:strings"
import "core:fmt"
import rl "vendor:raylib"
import emu "../machine"
gui_file_loader :: proc(rect: rl.Rectangle, sim: ^Simulator) {
rl.DrawRectangleLinesEx(rect, 1, rl.GRAY)
// Embedded Rom Section:
// ---------------------------------------------
bottom_bounds := rl.Rectangle {
x = rect.x,
y = rect.y,
width = rect.width - (PADDING_X * 2),
height = rect.height - (PADDING_X * 2)
}
rl.GuiPanel(bottom_bounds, "ROM List")
panel_rect := rl.Rectangle {
x = bottom_bounds.x,
y = bottom_bounds.y + PANEL_HEADER,
width = rect.width - (PADDING_X * 2),
height = rect.height - (PADDING_X * 2) - PANEL_HEADER
}
content_rect := rl.Rectangle {
x = panel_rect.x,
y = panel_rect.y - PANEL_HEADER,
width = panel_rect.width - 15,
height = f32(len(roms)) * LINE_HEIGHT
}
view: rl.Rectangle
rl.GuiScrollPanel(panel_rect, nil, content_rect, &sim.rompick_scroll, &view)
rl.BeginScissorMode(i32(view.x), i32(view.y), i32(view.width), i32(view.height))
defer rl.EndScissorMode()
for path, index in roms {
y_pos := panel_rect.y + f32(index * LINE_HEIGHT) + sim.rompick_scroll.y
row_rect := rl.Rectangle {
x = view.x,
y = y_pos,
width = view.width,
height = LINE_HEIGHT,
}
// make selected visible
if path.name == sim.selected_rom {
rl.DrawRectangleRec(row_rect, rl.ColorAlpha(rl.SKYBLUE, 0.25))
}
// hover rows
mouse := rl.GetMousePosition()
if rl.CheckCollisionPointRec(mouse, row_rect) {
rl.DrawRectangleRec(row_rect, rl.ColorAlpha(rl.WHITE, 0.08))
if rl.IsMouseButtonPressed(.LEFT) {
sim.selected_rom = path.name
load_embedded_rom(sim, path.name, roms[index].data)
}
}
txt := fmt.tprintf("%v: %v", (index + 1), path.name)
rl.DrawTextEx(
sim.font,
strings.clone_to_cstring(txt, context.temp_allocator),
{panel_rect.x + PADDING_X + sim.rompick_scroll.x, y_pos + (LINE_HEIGHT - 18) * 0.5},
18, 1,
rl.WHITE
)
}
}
load_embedded_rom :: proc(sim: ^Simulator, path: string, data: []byte) {
reset_sim(sim)
copy(sim.machine.memory[0x200:], data)
sim.rom_loaded = true
sim.disasm_count = disassemble_rom_n(sim.machine.memory[:], 0x200, 0x200 + u16(len(data)), sim.disasm[:])
}
disassemble_rom_n :: proc(memory: []u8, start_addr: u16, end_addr: u16, out: []Instruction) -> int {
count := 0
i := start_addr
for i < end_addr {
if int(i) + 1 >= len(memory) do break
if count >= len(out) 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
instruction := &out[count]
instruction.address = addr
instruction.raw = opcode
switch first_nibble {
case 0x0:
switch opcode {
//case 0x00E0: str = "CLS"
case 0x00E0: fmt.bprintf(instruction.str[:], "CLS")
case 0x00EE: fmt.bprintf(instruction.str[:], "RET")
case: fmt.bprintf(instruction.str[:], "DATA 0x%04X", opcode)
}
case 0x1: fmt.bprintf(instruction.str[:], "JP 0x%03X", nnn)
case 0x2: fmt.bprintf(instruction.str[:], "CALL 0x%03X", nnn)
case 0x3: fmt.bprintf(instruction.str[:], "SE V%X, 0x%02X", vx, kk)
case 0x4: fmt.bprintf(instruction.str[:], "SNE V%X, 0x%02X", vx, kk)
case 0x5: fmt.bprintf(instruction.str[:], "SE V%X, V%X", vx, vy)
case 0x6: fmt.bprintf(instruction.str[:], "LD V%X, 0x%02X", vx, kk)
case 0x7: fmt.bprintf(instruction.str[:], "ADD V%X, 0x%02X", vx, kk)
case 0x8:
switch last_nibble {
case 0x0: fmt.bprintf(instruction.str[:], "LD V%X, V%X", vx, vy)
case 0x1: fmt.bprintf(instruction.str[:], "OR V%X, V%X", vx, vy)
case 0x2: fmt.bprintf(instruction.str[:], "AND V%X, V%X", vx, vy)
case 0x3: fmt.bprintf(instruction.str[:], "XOR V%X, V%X", vx, vy)
case 0x4: fmt.bprintf(instruction.str[:], "ADD V%X, V%X", vx, vy)
case 0x5: fmt.bprintf(instruction.str[:], "SUB V%X, V%X", vx, vy)
case 0x6: fmt.bprintf(instruction.str[:], "SHR V%X", vx)
case 0x7: fmt.bprintf(instruction.str[:], "SUBN V%X, V%X", vx, vy)
case 0xE: fmt.bprintf(instruction.str[:], "SHL V%X", vx)
case: fmt.bprintf(instruction.str[:], "DATA 0x%04X", opcode)
}
case 0x9: fmt.bprintf(instruction.str[:], "SNE V%X, V%X", vx, vy)
case 0xA: fmt.bprintf(instruction.str[:], "LD I, 0x%03X", nnn)
case 0xB: fmt.bprintf(instruction.str[:], "JP V0, 0x%03X", nnn)
case 0xC: fmt.bprintf(instruction.str[:], "RND V%X, 0x%02X", vx, kk)
case 0xD: fmt.bprintf(instruction.str[:], "DRW V%X, V%X, %X", vx, vy, last_nibble)
case 0xE:
switch kk {
case 0x9E: fmt.bprintf(instruction.str[:], "SKP V%X", vx)
case 0xA1: fmt.bprintf(instruction.str[:], "SKNP V%X", vx)
case: fmt.bprintf(instruction.str[:], "DATA 0x%04X", opcode)
}
case 0xF:
switch kk {
case 0x07: fmt.bprintf(instruction.str[:], "LD V%X, DT", vx)
case 0x0A: fmt.bprintf(instruction.str[:], "LD V%X, K", vx)
case 0x15: fmt.bprintf(instruction.str[:], "LD DT, V%X", vx)
case 0x18: fmt.bprintf(instruction.str[:], "LD ST, V%X", vx)
case 0x1E: fmt.bprintf(instruction.str[:], "ADD I, V%X", vx)
case 0x29: fmt.bprintf(instruction.str[:], "LD F, V%X", vx)
case 0x33: fmt.bprintf(instruction.str[:], "LD B, V%X", vx)
case 0x55: fmt.bprintf(instruction.str[:], "LD [I], V%X", vx)
case 0x65: fmt.bprintf(instruction.str[:], "LD V%X, [I]", vx)
case: fmt.bprintf(instruction.str[:], "DATA 0x%04X", opcode)
}
case: fmt.bprintf(instruction.str[:], "DATA 0x%04X", opcode)
}
count += 1
}
return count
}
+10 -1
View File
@@ -34,7 +34,6 @@ Simulator :: struct {
// GUI
sound: rl.Sound,
font: rl.Font,
active_tab: i32,
mem_scroll: rl.Vector2,
rompick_scroll: rl.Vector2,
selected_rom: string,
@@ -46,3 +45,13 @@ Simulator :: struct {
disasm_count: int,
disasm_scroll: rl.Vector2,
}
reset_sim :: proc(sim: ^Simulator) {
sim.paused = true
sim.disasm_count = 0
sim.rom_loaded = false
sim.selected_rom = ""
emu.reset_machine(sim.machine)
}