1
0

add simple memory viewer

This commit is contained in:
Rokas Puzonas 2023-09-13 23:55:06 +03:00
parent df4dadc29d
commit 8a1f40cb62
9 changed files with 689 additions and 61 deletions

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "libs/raylib-zig"]
path = libs/raylib-zig
url = git@github.com:Not-Nik/raylib-zig.git
[submodule "libs/raylib"]
path = libs/raylib
url = https://github.com/ryupold/raylib.zig

View File

@ -1,5 +1,6 @@
const std = @import("std");
const rl = @import("libs/raylib-zig/build.zig");
// const rl = @import("libs/raylib-zig/build.zig");
const raylib = @import("libs/raylib/build.zig");
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
@ -31,9 +32,11 @@ pub fn build(b: *std.Build) !void {
const exe = b.addExecutable(.{ .name = "chip8-zig", .root_source_file = .{ .path = "src/main.zig" }, .optimize = optimize, .target = target });
rl.link(b, exe, target, optimize);
exe.addModule("raylib", rl.getModule(b, "libs/raylib-zig"));
exe.addModule("raylib-math", rl.math.getModule(b, "libs/raylib-zig"));
raylib.addTo(b, exe, target, optimize);
// rl.link(b, exe, target, optimize);
// exe.addModule("raylib", rl.getModule(b, "libs/raylib-zig"));
// exe.addModule("raylib-math", rl.math.getModule(b, "libs/raylib-zig"));
const run_cmd = b.addRunArtifact(exe);
const run_step = b.step("run", "Run chip8-zig");

View File

@ -1,5 +1,5 @@
.{
.name = "zig-raylib",
.name = "chip8-zig",
.version = "0.1.0",
.dependencies = .{
.raylib = .{

1
libs/raylib Submodule

@ -0,0 +1 @@
Subproject commit 642458242078d4e3299ea31c9a6c415c65a4e907

BIN
src/ROMs/br8kout.ch8 Normal file

Binary file not shown.

View File

@ -50,13 +50,15 @@ pub fn init(allocator: *const Allocator) !Self {
const display_width = 64;
const display_height = 32;
const memory_size = 4096;
const memory = try allocator.alloc(u8, memory_size);
@memset(memory, 0);
var self = Self {
.allocator = allocator,
.display = try allocator.alloc(bool, display_width * display_height),
.display_width = display_width,
.display_height = display_height,
.memory = try allocator.alloc(u8, memory_size),
.memory = memory,
.stack = [1]u16{0} ** 16,
.V = [1]u8{0} ** 16,
.I = 0,
@ -388,12 +390,11 @@ pub fn run_instruction(self: *Self, inst: u16) !void {
self.V[x] = (self.V[x] >> 1);
} else if (inst & 0x000F == 0x7) { // 8xy7 - SUBN Vx, Vy
const Vx = get_inst_x(inst);
const Vy = get_inst_y(inst);
const sub: i16 = self.V[Vy] - self.V[Vx];
const not_borrow = @intFromBool(sub > 0);
self.V[Vx] = @intCast(sub & 0x00FF);
self.V[0xF] = not_borrow;
const x = get_inst_x(inst);
const y = get_inst_y(inst);
const result = @subWithOverflow(self.V[y], self.V[x]);
self.V[x] = result[0];
self.V[0xF] = (1 - result[1]);
} else if (inst & 0x000F == 0xE) { // 8xyE - Vx SHR 1
const Vx = get_inst_x(inst);

BIN
src/fonts/generic-mono.otf Normal file

Binary file not shown.

View File

@ -1,11 +1,17 @@
const rl = @import("raylib");
const std = @import("std");
const print = std.debug.print;
const Allocator = std.mem.Allocator;
const ChipContext = @import("chip.zig");
const RaylibChip = @import("raylib-chip.zig");
const assert = std.debug.assert;
const Tab = enum {
MemoryView
};
pub fn gen_sin_wave(wave: *rl.Wave, frequency: f32) void {
assert(wave.sampleSize == 16); // Only 16 bits are supported
@ -25,6 +31,574 @@ fn megabytes(amount: usize) usize {
return amount * 1024 * 1024;
}
fn nibble_to_char(nibble: u4) u8 {
if (0 <= nibble and nibble <= 9) {
return '0' + @as(u8, nibble);
} else {
return 'A' + @as(u8, nibble - 10);
}
}
fn hex_to_strz(str: [:0]u8, number: u32) void {
var i: i32 = @intCast(str.len-1);
var leftover = number;
while (leftover > 0 and i >= 0): (leftover >>= 4) {
const nibble: u4 = @intCast(leftover & 0b1111);
str[@intCast(i)] = nibble_to_char(nibble);
i -= 1;
}
while (i >= 0): (i -= 1) {
str[@intCast(i)] = '0';
}
}
fn is_point_inside(px: f32, py: f32, x: f32, y: f32, width: f32, height: f32) bool {
return (x <= px and px < x+width) and (y <= py and y < y+height);
}
fn is_point_inside_rect(px: f32, py: f32, rect: rl.Rectangle) bool {
return is_point_inside(px, py, rect.x, rect.y, rect.width, rect.height);
}
fn clamp(value: f32, min: f32, max: f32) f32 {
return @min(@max(value, min), max);
}
const UI = struct {
const TransformFrame = struct {
ox: f32 = 0,
oy: f32 = 0,
sx: f32 = 1,
sy: f32 = 1,
};
frames: [16]TransformFrame,
top_frame: u32,
mouse: rl.Vector2,
mouse_delta: rl.Vector2,
pub fn init() UI {
return UI{
.frames = [1]TransformFrame{ TransformFrame{} } ** 16,
.top_frame = 0,
.mouse = rl.Vector2.zero(),
.mouse_delta = rl.Vector2.zero(),
};
}
pub fn pushTransform(self: *UI) void {
assert(self.top_frame < self.frames.len-1);
rl.rlPushMatrix();
self.top_frame += 1;
self.frames[self.top_frame] = self.frames[self.top_frame - 1];
}
pub fn pushTransformT(self: *UI, transform: TransformFrame) void {
self.pushTransform();
self.translate(transform.ox, transform.oy);
self.scale(transform.sx, transform.sy);
}
pub fn translate(self: *UI, x: f32, y: f32) void {
const top_frame = &self.frames[self.top_frame];
top_frame.ox += x * top_frame.sx;
top_frame.oy += y * top_frame.sy;
rl.rlTranslatef(x, y, 0);
}
pub fn scale(self: *UI, x: f32, y: f32) void {
const top_frame = &self.frames[self.top_frame];
top_frame.sx *= x;
top_frame.sy *= y;
rl.rlScalef(x, y, 0);
}
pub fn popTransform(self: *UI) void {
rl.rlPopMatrix();
self.top_frame -= 1;
}
pub fn update(self: *UI) void {
assert(self.top_frame == 0); // Check if 'pushTransform()' and 'popTransform()' are paired
self.frames[0] = .{};
self.mouse = rl.GetMousePosition();
self.mouse_delta = rl.GetMouseDelta();
}
/// Screen space -> UI space
pub fn apply_transform(self: *UI, vec2: rl.Vector2) rl.Vector2 {
const top_frame = &self.frames[self.top_frame];
return rl.Vector2{
.x = (vec2.x - top_frame.ox) * top_frame.sx,
.y = (vec2.y - top_frame.oy) * top_frame.sy
};
}
pub fn apply_scale(self: *UI, vec2: rl.Vector2) rl.Vector2 {
const top_frame = &self.frames[self.top_frame];
return rl.Vector2{
.x = vec2.x * top_frame.sx,
.y = vec2.y * top_frame.sy,
};
}
pub fn get_mouse(self: *UI) rl.Vector2 {
return self.apply_transform(self.mouse);
}
pub fn get_mouse_delta(self: *UI) rl.Vector2 {
return self.apply_scale(self.mouse_delta);
}
pub fn is_mouse_inside(self: *UI, x: f32, y: f32, width: f32, height: f32) bool {
const mouse = self.get_mouse();
return (x <= mouse.x and mouse.x < x+width) and (y <= mouse.y and mouse.y < y+height);
}
pub fn is_mouse_inside_rect(self: *UI, rect: rl.Rectangle) bool {
return self.is_mouse_inside(rect.x, rect.y, rect.width, rect.height);
}
pub fn is_mouse_down() bool {
return rl.IsMouseButtonDown(rl.MouseButton.MOUSE_BUTTON_LEFT);
}
pub fn is_mouse_up() bool {
return rl.IsMouseButtonUp(rl.MouseButton.MOUSE_BUTTON_LEFT);
}
pub fn was_secondary_mouse_pressed() bool {
return rl.IsMouseButtonPressed(rl.MouseButton.MOUSE_BUTTON_RIGHT);
}
pub fn is_holding_mouse(self: *UI, rect: rl.Rectangle, state: *bool) bool {
if (!state.* and UI.is_mouse_down() and self.is_mouse_inside_rect(rect)) {
state.* = true;
return true;
}
if (UI.is_mouse_up()) {
state.* = false;
return true;
}
return false;
}
};
const UIBox = struct {
x: f32,
y: f32,
width: f32,
height: f32,
vert_margin: f32,
horz_margin: f32,
pub fn init(x: f32, y: f32, width: f32, height: f32) UIBox {
return UIBox {
.x = x,
.y = y,
.width = width,
.height = height,
.vert_margin = 0,
.horz_margin = 0,
};
}
pub fn init_rect(rectangle: rl.Rectangle) UIBox {
return UIBox.init(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
}
pub fn body_x(self: *UIBox) f32 {
return self.x + self.horz_margin;
}
pub fn body_y(self: *UIBox) f32 {
return self.y + self.vert_margin;
}
pub fn body_width(self: *UIBox) f32 {
return self.width - 2*self.horz_margin;
}
pub fn body_height(self: *UIBox) f32 {
return self.height - 2*self.vert_margin;
}
pub fn body_rect(self: *UIBox) rl.Rectangle {
return rl.Rectangle{
.x = self.body_x(),
.y = self.body_y(),
.width = self.body_width(),
.height = self.body_height()
};
}
pub fn rect(self: *UIBox) rl.Rectangle {
return rl.Rectangle{
.x = self.x,
.y = self.y,
.width = self.width,
.height = self.height
};
}
};
const HorizontalLayout = struct {
x: f32,
y: f32,
width: f32,
height: f32,
used_width: f32,
pub fn init(x: f32, y: f32, width: f32, height: f32) HorizontalLayout {
return HorizontalLayout{
.x = x,
.y = y,
.width = width,
.height = height,
.used_width = 0
};
}
pub fn next_x(self: *HorizontalLayout) f32 {
return self.x + self.used_width;
}
pub fn next_y(self: *HorizontalLayout) f32 {
return self.y;
}
pub fn push_rect(self: *HorizontalLayout, width: f32) rl.Rectangle {
const rect = rl.Rectangle{
.x = self.next_x(),
.y = self.next_y(),
.width = width,
.height = self.height
};
self.push(width);
return rect;
}
pub fn push(self: *HorizontalLayout, width: f32) void {
self.used_width += width;
}
pub fn used_size(self: *HorizontalLayout) rl.Vector2 {
return rl.Vector2{ .x = self.used_width, .y = self.height };
}
};
const Range = struct { start: u32 = 0, end: u32 = 0 };
const MemoryView = struct {
const Highlight = struct {
range: Range,
color: rl.Color
};
font: *const rl.Font,
font_size: f32,
base_address: u32,
memory: []u8,
scroll: f32 = 0,
scrolling: bool = false,
selecting: bool = false,
editing: bool = false,
editing_byte: u32 = 0,
selection_pivot: u32 = 0,
allocator: *const Allocator,
grey_out_zeros: bool = true,
text_color: rl.Color = rl.BLACK,
dim_text_color: rl.Color = rl.GRAY,
row_width: u5 = 16,
pub fn init(memory: []u8, font: *const rl.Font, font_size: f32, allocator: *const Allocator) MemoryView {
return MemoryView{
.allocator = allocator,
.base_address = 0,
.memory = memory,
.font = font,
.font_size = font_size,
};
}
pub fn get_memory_row_count(self: *const MemoryView) f32 {
return @ceil(@as(f32, @floatFromInt(self.memory.len)) / @as(f32, @floatFromInt(self.row_width)));
}
pub fn get_visible_row_count(self: *const MemoryView, height: f32) f32 {
return @min(height / self.font_size, self.get_memory_row_count());
}
pub fn get_max_scroll(self: *const MemoryView, height: f32) f32 {
return @max(self.get_memory_row_count() - self.get_visible_row_count(height), 0);
}
pub fn show(self: *MemoryView, ui: *UI, x: f32, y: f32, width: f32, height: f32, selection: *Range) !void {
if (ui.is_mouse_inside(x, y, width, height)) {
self.scroll -= rl.GetMouseWheelMove();
}
self.scroll = clamp(self.scroll, 0, self.get_max_scroll(height));
const from_row: u32 = @intFromFloat(@floor(self.scroll));
const to_row: u32 = @intFromFloat(@ceil(self.scroll + self.get_visible_row_count(height)));
const scroll_offset = @rem(self.scroll, 1) * self.font_size;
var layout = HorizontalLayout.init(x, y - scroll_offset, width, height);
layout.push(try self.show_address_column(ui, layout.next_x(), layout.next_y(), from_row, to_row));
layout.push(self.show_hex_column(ui, layout.next_x(), layout.next_y(), selection, from_row, to_row));
layout.push(self.show_ascii_column(ui, layout.next_x(), layout.next_y(), selection, from_row, to_row));
layout.push(self.show_scrollbar(ui, layout.next_x(), y, height));
}
pub fn show_address_column(self: *MemoryView, ui: *UI, x: f32, y: f32, from_row: u32, to_row: u32) !f32 {
const font = self.font.*;
const font_size = self.font_size;
const margin = font_size/2;
const memory_size_f32: f32 = @floatFromInt(self.memory.len);
const memory_size_log10: u32 = @intFromFloat(@floor(@log2(memory_size_f32)/2));
const address_column_max_chars: u32 = memory_size_log10 + 1;
const row_count = (to_row - from_row);
var label_buf = try self.allocator.allocSentinel(u8, address_column_max_chars, 0);
defer self.allocator.free(label_buf);
hex_to_strz(label_buf, 0);
const column_width = rl.MeasureTextEx(font, label_buf, font_size, 0).x + 2*margin;
ui.pushTransformT(.{ .ox = x, .oy = y });
for (0..row_count) |i| {
const row = from_row + @as(u32, @intCast(i));
const row_address = self.base_address + row*self.row_width;
hex_to_strz(label_buf, row_address);
const cell_rect = rl.Rectangle {
.x = 0,
.y = self.font_size * @as(f32, @floatFromInt(i)),
.height = self.font_size,
.width = column_width
};
if (ui.is_mouse_inside_rect(cell_rect)) {
rl.DrawRectangleRec(cell_rect, rl.RED);
}
const text_pos = rl.Vector2{ .x = cell_rect.x + margin, .y = cell_rect.y };
rl.DrawTextEx(font, label_buf, text_pos, font_size, 0, self.text_color);
}
ui.popTransform();
return column_width;
}
fn is_in_range(x: u32, from: u32, to: u32) bool {
return from <= x and x < to;
}
pub fn show_hex_column(self: *MemoryView, ui: *UI, x: f32, y: f32, selection: *Range, from_row: u32, to_row: u32) f32 {
assert(self.row_width <= 16);
const font = self.font.*;
const font_size = self.font_size;
const margin = font_size/6;
var middle_margin: f32 = 0.0;
if (self.row_width == 16) {
middle_margin = font_size/2;
}
const cell_width = rl.MeasureTextEx(font, "00", font_size, 0).x;
const row_count = (to_row - from_row);
const highlights = [_]Highlight{
Highlight{ .range = .{ .start = 16, .end = 17, }, .color = rl.RED },
Highlight{ .range = selection.*, .color = rl.RED },
};
ui.pushTransformT(.{ .ox = x, .oy = y });
var cell_bufz = [_:0]u8{0} ** 2;
for (0..row_count) |i| {
const row = from_row + @as(u32, @intCast(i));
const row_memory_idx: u32 = @intCast(row*self.row_width);
var cell_rects: [16]rl.Rectangle = undefined;
const to_column = @min(self.memory.len - row*self.row_width, self.row_width);
for (0..to_column) |column| {
var cell_rect = rl.Rectangle {
.x = (cell_width + 2*margin) * @as(f32, @floatFromInt(column)),
.y = 0,
.width = cell_width + 2*margin,
.height = self.font_size
};
if (column >= self.row_width/2) {
cell_rect.x += middle_margin;
}
cell_rects[column] = cell_rect;
}
for (highlights) |highlight| {
const highlight_start = highlight.range.start;
const highlight_end = highlight.range.end;
if (row_memory_idx >= highlight_end) continue;
if (row_memory_idx+self.row_width <= highlight_start) continue;
var highlight_from_column: u32 = undefined;
if (row_memory_idx > highlight_start) {
highlight_from_column = 0;
} else {
highlight_from_column = @mod(highlight_start, self.row_width);
}
var highlight_to_column: u32 = undefined;
if (row_memory_idx+self.row_width <= highlight_end) {
highlight_to_column = self.row_width-1;
} else {
highlight_to_column = @mod(highlight_end-1, self.row_width);
}
const from_cell = cell_rects[highlight_from_column];
const to_cell = cell_rects[highlight_to_column];
rl.DrawRectangleRec(rl.Rectangle{
.x = from_cell.x,
.y = from_cell.y,
.width = (to_cell.x+to_cell.width) - from_cell.x,
.height = (to_cell.y+to_cell.height) - from_cell.y,
}, highlight.color);
}
for (0..to_column) |column| {
const memory_idx: u32 = row_memory_idx + @as(u32, @intCast(column));
if (ui.is_holding_mouse(cell_rects[column], &self.selecting)) {
if (self.selecting) {
self.selection_pivot = memory_idx;
selection.start = memory_idx;
selection.end = memory_idx+1;
}
}
if (self.selecting) {
if (ui.is_mouse_inside_rect(cell_rects[column])) {
if (memory_idx > self.selection_pivot) {
selection.start = self.selection_pivot;
selection.end = memory_idx+1;
} else {
selection.start = memory_idx;
selection.end = self.selection_pivot+1;
}
}
}
const text_pos = rl.Vector2{ .x = cell_rects[column].x + margin };
const value = self.memory[memory_idx];
hex_to_strz(&cell_bufz, value);
var color = self.text_color;
if (self.grey_out_zeros and value == 0) {
color = self.dim_text_color;
}
rl.DrawTextEx(font, &cell_bufz, text_pos, font_size, 0, color);
}
ui.translate(0, font_size);
}
ui.popTransform();
return (cell_width + 2*margin)*@as(f32, @floatFromInt(self.row_width)) + middle_margin;
}
pub fn show_ascii_column(self: *MemoryView, ui: *UI, x: f32, y: f32, selection: *Range, from_row: u32, to_row: u32) f32 {
const font = self.font.*;
const font_size = self.font_size;
const margin = font_size/2;
const cell_width = rl.MeasureTextEx(font, ".", font_size, 0).x;
const row_count = (to_row - from_row);
ui.pushTransformT(.{ .ox = x+margin, .oy = y });
for (0..row_count) |i| {
const row = from_row + @as(u32, @intCast(i));
const to_column = @min(self.memory.len - row*self.row_width, self.row_width);
for (0..to_column) |column| {
const memory_idx: u32 = @intCast(row*self.row_width + column);
const value = self.memory[memory_idx];
var char: u8 = 'A';
if (std.ascii.isPrint(value)) {
char = value;
} else {
char = '.';
}
const pos_x = cell_width * @as(f32, @floatFromInt(column));
if (selection.start <= memory_idx and memory_idx < selection.end) {
const cell_rect = rl.Rectangle{
.x = pos_x,
.y = 0,
.height = self.font_size,
.width = cell_width
};
rl.DrawRectangleRec(cell_rect, rl.RED);
}
const char_str = [2]u8 { char, 0 };
rl.DrawTextEx(font, char_str[0..1 :0], rl.Vector2{ .x = pos_x }, font_size, 0, self.text_color);
}
ui.translate(0, font_size);
}
ui.popTransform();
return cell_width*@as(f32, @floatFromInt(self.row_width)) + 2*margin;
}
pub fn show_scrollbar(self: *MemoryView, ui: *UI, x: f32, y: f32, height: f32) f32 {
const max_scroll = self.get_max_scroll(height);
if (max_scroll == 0) {
return 0;
}
const visible_row_count = self.get_visible_row_count(height);
const memory_row_count = self.get_memory_row_count();
const visible_percent = visible_row_count / memory_row_count;
const scroll_percent = self.scroll / max_scroll;
const scrollbar_height = visible_percent * height;
const scrollbar = rl.Rectangle {
.x = x,
.y = y + scroll_percent * (height - scrollbar_height),
.width = self.font_size,
.height = scrollbar_height
};
var color = rl.BLACK;
_ = ui.is_holding_mouse(scrollbar, &self.scrolling);
if (self.scrolling) {
const mouse_dy = rl.GetMouseDelta().y;
self.scroll += (mouse_dy / (height - scrollbar_height) * max_scroll);
self.scroll = clamp(self.scroll, 0, max_scroll);
color = rl.DARKGRAY;
}
rl.DrawRectangleRec(scrollbar, color);
return scrollbar.width;
}
};
pub fn main() anyerror!void {
var program_memory = try std.heap.page_allocator.alloc(u8, megabytes(2));
var fba = std.heap.FixedBufferAllocator.init(program_memory);
@ -33,7 +607,7 @@ pub fn main() anyerror!void {
var chip = try ChipContext.init(&allocator);
defer chip.deinit();
chip.set_memory(0x200, @embedFile("ROMs/morse_demo.ch8"));
chip.set_memory(0x200, @embedFile("ROMs/br8kout.ch8"));
{
// const file = try std.fs.cwd().openFile("ROMs/morse_demo.ch8", .{ .mode = .read_only });
@ -42,16 +616,22 @@ pub fn main() anyerror!void {
}
const pixel_size = 20;
const screen_width: i32 = @as(i32, chip.display_width) * pixel_size;
const screen_height: i32 = @as(i32, chip.display_height) * pixel_size;
const initial_screen_width: i32 = @as(i32, chip.display_width) * pixel_size;
const initial_screen_height: i32 = @as(i32, chip.display_height) * pixel_size;
rl.initWindow(screen_width, screen_height, "CHIP-8");
defer rl.closeWindow();
rl.SetConfigFlags(rl.ConfigFlags{ .FLAG_WINDOW_RESIZABLE = true });
rl.InitWindow(initial_screen_width, initial_screen_height, "CHIP-8");
defer rl.CloseWindow();
rl.initAudioDevice();
defer rl.closeAudioDevice();
rl.InitAudioDevice();
defer rl.CloseAudioDevice();
rl.setTargetFPS(60);
rl.SetTargetFPS(60);
const font_size = 24;
const font_ttf_default_numchars = 95; // TTF font generation default charset: 95 glyphs (ASCII 32..126)
const font = rl.LoadFontEx("src/fonts/generic-mono.otf", font_size, null, font_ttf_default_numchars);
defer rl.UnloadFont(font);
const sample_rate = 44100;
var data = try allocator.alloc(i16, sample_rate);
@ -64,24 +644,63 @@ pub fn main() anyerror!void {
.data = @ptrCast(data.ptr),
};
gen_sin_wave(&chip_wave, 440);
var chip_sound = rl.loadSoundFromWave(chip_wave);
defer rl.unloadSound(chip_sound);
rl.setSoundVolume(chip_sound, 0.2);
var chip_sound = rl.LoadSoundFromWave(chip_wave);
defer rl.UnloadSound(chip_sound);
rl.SetSoundVolume(chip_sound, 0.2);
var raylib_chip = RaylibChip.init(&chip, &chip_sound);
// var raylib_chip = RaylibChip.init(&chip, null);
raylib_chip.tick_speed = 500;
raylib_chip.timer_speed = 60;
while (!rl.windowShouldClose()) {
var dt = rl.getFrameTime();
var edit_mode = false;
var tab: Tab = .MemoryView;
// var temp_mem = [1]u8{0xAA} ** (16*80 + 10);
// var memory_view = MemoryView.init(&temp_mem, &font, 32);
var memory_view = MemoryView.init(chip.memory, &font, font_size, &allocator);
var selected_memory = Range{};
var ui = UI.init();
while (!rl.WindowShouldClose()) {
const screen_width = rl.GetScreenWidth();
const screen_height = rl.GetScreenHeight();
var dt = rl.GetFrameTime();
raylib_chip.update(dt);
{
rl.beginDrawing();
defer rl.endDrawing();
if (rl.IsKeyPressed(rl.KeyboardKey.KEY_TAB)) {
edit_mode = !edit_mode;
}
rl.clearBackground(rl.Color.white);
raylib_chip.render(0, 0, screen_width, screen_height);
if (edit_mode) {
if (rl.IsKeyPressed(rl.KeyboardKey.KEY_ONE)) {
tab = .MemoryView;
}
}
rl.BeginDrawing();
defer rl.EndDrawing();
if (!edit_mode) {
rl.ClearBackground(rl.DARKGRAY);
const scale_x = @divFloor(screen_width, chip.display_width);
const scale_y = @divFloor(screen_height, chip.display_height);
const min_scale = @min(scale_x, scale_y);
const display_width = chip.display_width * min_scale;
const display_height = chip.display_height * min_scale;
const display_x = @divFloor(screen_width - display_width, 2);
const display_y = @divFloor(screen_height - display_height, 2);
raylib_chip.render(display_x, display_y, display_width, display_height);
} else {
rl.ClearBackground(rl.RAYWHITE);
ui.update();
if (tab == .MemoryView) {
try memory_view.show(&ui, 0, 0, @floatFromInt(screen_width), @floatFromInt(screen_height), &selected_memory);
}
}
}
}

View File

@ -8,16 +8,16 @@ on_color: rl.Color,
off_color: rl.Color,
timer_speed: f32,
tick_speed: f32,
beep_sound: *rl.Sound,
beep_sound: ?*rl.Sound,
tick_time: f32,
timer_time: f32,
pub fn init(chip: *ChipContext, beep_sound: *rl.Sound) Self {
pub fn init(chip: *ChipContext, beep_sound: ?*rl.Sound) Self {
return Self{
.chip = chip,
.off_color = rl.Color.black,
.on_color = rl.Color.ray_white,
.off_color = rl.BLACK,
.on_color = rl.RAYWHITE,
.timer_speed = 60,
.tick_speed = 500,
.tick_time = 0,
@ -28,26 +28,26 @@ pub fn init(chip: *ChipContext, beep_sound: *rl.Sound) Self {
pub fn update_input(self: *Self) void {
const keys = [16]rl.KeyboardKey{
.key_x,
.key_one,
.key_two,
.key_three,
.key_q,
.key_w,
.key_e,
.key_a,
.key_s,
.key_d,
.key_z,
.key_c,
.key_four,
.key_r,
.key_f,
.key_v,
.KEY_X,
.KEY_ONE,
.KEY_TWO,
.KEY_THREE,
.KEY_Q,
.KEY_W,
.KEY_E,
.KEY_A,
.KEY_S,
.KEY_D,
.KEY_Z,
.KEY_C,
.KEY_FOUR,
.KEY_R,
.KEY_F,
.KEY_V,
};
for (0.., keys) |i, key| {
self.chip.input[i] = rl.isKeyDown(key);
self.chip.input[i] = rl.IsKeyDown(key);
}
}
@ -70,14 +70,15 @@ pub fn update(self: *Self, dt: f32) void {
self.timer_time -= 1/self.timer_speed;
}
const beep_sound = self.beep_sound.*;
if (self.chip.ST > 0) {
if (!rl.isSoundPlaying(beep_sound)) {
rl.playSound(beep_sound);
}
} else {
if (rl.isSoundPlaying(beep_sound)) {
rl.stopSound(beep_sound);
if (self.beep_sound) |beep_sound| {
if (self.chip.ST > 0) {
if (!rl.IsSoundPlaying(beep_sound.*)) {
rl.PlaySound(beep_sound.*);
}
} else {
if (rl.IsSoundPlaying(beep_sound.*)) {
rl.StopSound(beep_sound.*);
}
}
}
}
@ -86,14 +87,14 @@ pub fn render(self: *Self, x: i32, y: i32, width: i32, height: i32) void {
const pixel_width = @divFloor(width, self.chip.display_width);
const pixel_height = @divFloor(height, self.chip.display_height);
rl.drawRectangle(0, 0, width, height, self.off_color);
rl.DrawRectangle(x, y, width, height, self.off_color);
for (0..self.chip.display_height) |oy| {
for (0..self.chip.display_width) |ox| {
if (self.chip.display_get(@intCast(ox), @intCast(oy))) {
const ix = x + @as(i32, @intCast(ox)) * pixel_width;
const iy = y + @as(i32, @intCast(oy)) * pixel_height;
rl.drawRectangle(ix, iy, pixel_width, pixel_height, self.on_color);
rl.DrawRectangle(ix, iy, pixel_width, pixel_height, self.on_color);
}
}
}