Compare commits

..

No commits in common. "0f6dc7591336a656e804dc34fdc631adc2dafdea" and "afc597fd05f092cdc4c315922620adcb4b87fe99" have entirely different histories.

3 changed files with 28 additions and 331 deletions

View File

@ -647,7 +647,7 @@ pub const PSR = extern union {
}
};
pub const Mode = enum(u5) {
const Mode = enum(u5) {
User = 0b10000,
Fiq = 0b10001,
Irq = 0b10010,
@ -656,7 +656,7 @@ pub const Mode = enum(u5) {
Undefined = 0b11011,
System = 0b11111,
pub fn toString(self: Mode) []const u8 {
fn toString(self: Mode) []const u8 {
return switch (self) {
.User => "usr",
.Fiq => "fiq",

View File

@ -10,7 +10,6 @@ const Apu = @import("core/apu.zig").Apu;
const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi;
const Scheduler = @import("core/scheduler.zig").Scheduler;
const FpsTracker = @import("util.zig").FpsTracker;
const RingBuffer = @import("util.zig").RingBuffer;
const gba_width = @import("core/ppu.zig").width;
const gba_height = @import("core/ppu.zig").height;
@ -32,25 +31,6 @@ pub const Gui = struct {
const Self = @This();
const log = std.log.scoped(.Gui);
const State = struct {
fps_hist: RingBuffer(u32),
allocator: Allocator,
pub fn init(allocator: Allocator) !@This() {
const history = try allocator.alloc(u32, 0x400);
return .{
.fps_hist = RingBuffer(u32).init(history),
.allocator = allocator,
};
}
fn deinit(self: *@This()) void {
self.fps_hist.deinit(self.allocator);
self.* = undefined;
}
};
// zig fmt: off
const vertices: [32]f32 = [_]f32{
// Positions // Colours // Texture Coords
@ -71,8 +51,6 @@ pub const Gui = struct {
title: []const u8,
audio: Audio,
state: State,
program_id: gl.GLuint,
pub fn init(allocator: Allocator, title: *const [12]u8, apu: *Apu) !Self {
@ -81,6 +59,8 @@ pub const Gui = struct {
if (SDL.SDL_GL_SetAttribute(SDL.SDL_GL_CONTEXT_MAJOR_VERSION, 3) < 0) panic();
if (SDL.SDL_GL_SetAttribute(SDL.SDL_GL_CONTEXT_MAJOR_VERSION, 3) < 0) panic();
// const win_scale = @intCast(c_int, config.config().host.win_scale);
const window = SDL.SDL_CreateWindow(
default_title,
SDL.SDL_WINDOWPOS_CENTERED,
@ -97,37 +77,17 @@ pub const Gui = struct {
if (SDL.SDL_GL_SetSwapInterval(@boolToInt(config.config().host.vsync)) < 0) panic();
zgui.init(allocator);
zgui.plot.init();
zgui.backend.init(window, ctx, "#version 330 core");
zgui.io.setIniFilename(null);
return Self{
.window = window,
.title = std.mem.sliceTo(title, 0),
.ctx = ctx,
.program_id = try compileShaders(),
.audio = Audio.init(apu),
.state = try State.init(allocator),
};
}
pub fn deinit(self: *Self) void {
self.audio.deinit();
self.state.deinit();
zgui.backend.deinit();
zgui.plot.deinit();
zgui.deinit();
gl.deleteProgram(self.program_id);
SDL.SDL_GL_DeleteContext(self.ctx);
SDL.SDL_DestroyWindow(self.window);
SDL.SDL_Quit();
self.* = undefined;
}
fn drawGbaTexture(self: *const Self, obj_ids: struct { GLuint, GLuint, GLuint }, tex_id: GLuint, buf: []const u8) void {
gl.bindTexture(gl.TEXTURE_2D, tex_id);
defer gl.bindTexture(gl.TEXTURE_2D, 0);
@ -259,219 +219,21 @@ pub const Gui = struct {
return fbo_id;
}
fn draw(self: *Self, tex_id: GLuint, cpu: *const Arm7tdmi) void {
const Mode = @import("core/cpu.zig").Mode;
const win_scale = config.config().host.win_scale;
fn draw(self: *Self, tex_id: GLuint) void {
_ = self;
{
const w = @intToFloat(f32, gba_width * win_scale);
const h = @intToFloat(f32, gba_height * win_scale);
_ = zgui.begin("Game Boy Advance Screen", .{ .flags = .{ .no_resize = true, .always_auto_resize = true } });
_ = zgui.begin("Game Boy Advance Screen", .{ .flags = .{ .no_resize = true } });
defer zgui.end();
zgui.image(@intToPtr(*anyopaque, tex_id), .{ .w = w, .h = h, .uv0 = .{ 0, 1 }, .uv1 = .{ 1, 0 } });
}
{
_ = zgui.begin("Information", .{});
defer zgui.end();
{
var i: usize = 0;
while (i < 8) : (i += 1) {
zgui.text("R{}: 0x{X:0>8}", .{ i, cpu.r[i] });
zgui.sameLine(.{});
const prefix = if (8 + i < 10) " " else "";
zgui.text("{s}R{}: 0x{X:0>8}", .{ prefix, 8 + i, cpu.r[8 + i] });
}
}
zgui.separator();
{
const maybe_mode = std.meta.intToEnum(Mode, cpu.cpsr.mode.read()) catch null;
const mode = if (maybe_mode) |mode| mode.toString() else "???";
const w = 9.0;
const h = 15.0;
zgui.text("CPSR: 0x{X:0>8}", .{cpu.cpsr.raw});
zgui.sameLine(.{});
_ = zgui.selectable("N", .{ .w = w, .h = h, .selected = cpu.cpsr.n.read() });
zgui.sameLine(.{});
_ = zgui.selectable("Z", .{ .w = w, .h = h, .selected = cpu.cpsr.z.read() });
zgui.sameLine(.{});
_ = zgui.selectable("C", .{ .w = w, .h = h, .selected = cpu.cpsr.c.read() });
zgui.sameLine(.{});
_ = zgui.selectable("V", .{ .w = w, .h = h, .selected = cpu.cpsr.v.read() });
zgui.sameLine(.{});
zgui.text("{s}", .{mode});
}
{
const maybe_mode = std.meta.intToEnum(Mode, cpu.spsr.mode.read()) catch null;
const mode = if (maybe_mode) |mode| mode.toString() else "???";
const w = 9.0;
const h = 15.0;
zgui.text("SPSR: 0x{X:0>8}", .{cpu.spsr.raw});
zgui.sameLine(.{});
_ = zgui.selectable("N", .{ .w = w, .h = h, .selected = cpu.spsr.n.read() });
zgui.sameLine(.{});
_ = zgui.selectable("Z", .{ .w = w, .h = h, .selected = cpu.spsr.z.read() });
zgui.sameLine(.{});
_ = zgui.selectable("C", .{ .w = w, .h = h, .selected = cpu.spsr.c.read() });
zgui.sameLine(.{});
_ = zgui.selectable("V", .{ .w = w, .h = h, .selected = cpu.spsr.v.read() });
zgui.sameLine(.{});
zgui.text("{s}", .{mode});
}
{
// const ime = cpu.bus.io.ime;
const ir = cpu.bus.io.ie;
// const ie = cpu.bus.io.ie;
const h = 15.0;
const w = 9.0;
const ww = 9.0 * 2 + 3.5;
{
zgui.text("IRQ", .{});
zgui.sameLine(.{});
_ = zgui.selectable("VBL", .{ .w = ww, .h = h, .selected = ir.vblank.read() });
zgui.sameLine(.{});
_ = zgui.selectable("HBL", .{ .w = ww, .h = h, .selected = ir.hblank.read() });
zgui.sameLine(.{});
_ = zgui.selectable("VCT", .{ .w = ww, .h = h, .selected = ir.coincidence.read() });
{
zgui.sameLine(.{});
_ = zgui.text("T", .{});
zgui.sameLine(.{});
_ = zgui.selectable("0", .{ .w = w, .h = h, .selected = ir.tm0_overflow.read() });
zgui.sameLine(.{});
_ = zgui.selectable("1", .{ .w = w, .h = h, .selected = ir.tm1_overflow.read() });
zgui.sameLine(.{});
_ = zgui.selectable("2", .{ .w = w, .h = h, .selected = ir.tm2_overflow.read() });
zgui.sameLine(.{});
_ = zgui.selectable("3", .{ .w = w, .h = h, .selected = ir.tm3_overflow.read() });
}
zgui.sameLine(.{});
_ = zgui.selectable("SRL", .{ .w = ww, .h = h, .selected = ir.serial.read() });
{
zgui.sameLine(.{});
_ = zgui.text("DMA", .{});
zgui.sameLine(.{});
_ = zgui.selectable("0", .{ .w = w, .h = h, .selected = ir.dma0.read() });
zgui.sameLine(.{});
_ = zgui.selectable("1", .{ .w = w, .h = h, .selected = ir.dma1.read() });
zgui.sameLine(.{});
_ = zgui.selectable("2", .{ .w = w, .h = h, .selected = ir.dma2.read() });
zgui.sameLine(.{});
_ = zgui.selectable("3", .{ .w = w, .h = h, .selected = ir.dma3.read() });
}
zgui.sameLine(.{});
_ = zgui.selectable("KPD", .{ .w = ww, .h = h, .selected = ir.keypad.read() });
zgui.sameLine(.{});
_ = zgui.selectable("GPK", .{ .w = ww, .h = h, .selected = ir.game_pak.read() });
}
}
}
zgui.separator();
{
_ = zgui.begin("Performance", .{});
defer zgui.end();
const tmp = blk: {
var buf: [0x400]u32 = undefined;
const len = self.state.fps_hist.copy(&buf);
break :blk .{ buf, len };
};
const values = tmp[0];
const len = tmp[1];
if (len == values.len) _ = self.state.fps_hist.pop();
const sorted = blk: {
var buf: @TypeOf(values) = undefined;
std.mem.copy(u32, buf[0..len], values[0..len]);
std.sort.sort(u32, buf[0..len], {}, std.sort.asc(u32));
break :blk buf;
const args = .{
.w = gba_width,
.h = gba_height,
.uv0 = .{ 0.0, 1.0 },
.uv1 = .{ 1.0, 0.0 },
};
const y_max = 2 * if (len != 0) @intToFloat(f64, sorted[len - 1]) else emu.frame_rate;
const x_max = @intToFloat(f64, values.len);
const y_args = .{ .flags = .{ .no_grid_lines = true } };
const x_args = .{ .flags = .{ .no_grid_lines = true, .no_tick_labels = true, .no_tick_marks = true } };
if (zgui.plot.beginPlot("Emulation FPS", .{ .w = 0.0, .flags = .{ .no_title = true, .no_frame = true } })) {
defer zgui.plot.endPlot();
zgui.plot.setupLegend(.{ .north = true, .east = true }, .{});
zgui.plot.setupAxis(.x1, x_args);
zgui.plot.setupAxis(.y1, y_args);
zgui.plot.setupAxisLimits(.y1, .{ .min = 0.0, .max = y_max, .cond = .always });
zgui.plot.setupAxisLimits(.x1, .{ .min = 0.0, .max = x_max, .cond = .always });
zgui.plot.setupFinish();
zgui.plot.plotLineValues("FPS", u32, .{ .v = values[0..len] });
}
const stats: struct { u32, u32, u32 } = blk: {
if (len == 0) break :blk .{ 0, 0, 0 };
const average = average: {
var sum: u32 = 0;
for (sorted[0..len]) |value| sum += value;
break :average @intCast(u32, sum / len);
};
const median = sorted[len / 2];
const low = sorted[len / 100]; // 1% Low
break :blk .{ average, median, low };
};
zgui.text("Average: {:0>3} fps", .{stats[0]});
zgui.text(" Median: {:0>3} fps", .{stats[1]});
zgui.text(" 1% Low: {:0>3} fps", .{stats[2]});
}
{
zgui.showDemoWindow(null);
zgui.image(@intToPtr(*anyopaque, tex_id), args);
}
}
@ -567,21 +329,31 @@ pub const Gui = struct {
gl.clear(gl.COLOR_BUFFER_BIT);
zgui.backend.newFrame(width, height);
self.draw(out_tex, cpu);
self.draw(out_tex);
zgui.backend.draw();
SDL.SDL_GL_SwapWindow(self.window);
const emu_fps = tracker.value();
self.state.fps_hist.push(emu_fps) catch {};
const dyn_title = std.fmt.bufPrintZ(&title_buf, "ZBA | {s} [Emu: {}fps] ", .{ self.title, emu_fps }) catch unreachable;
const dyn_title = std.fmt.bufPrintZ(&title_buf, "ZBA | {s} [Emu: {}fps] ", .{ self.title, tracker.value() }) catch unreachable;
SDL.SDL_SetWindowTitle(self.window, dyn_title.ptr);
}
quit.store(true, .Monotonic); // Terminate Emulator Thread
}
pub fn deinit(self: *Self) void {
self.audio.deinit();
zgui.backend.deinit();
zgui.deinit();
gl.deleteProgram(self.program_id);
SDL.SDL_GL_DeleteContext(self.ctx);
SDL.SDL_DestroyWindow(self.window);
SDL.SDL_Quit();
self.* = undefined;
}
fn glGetProcAddress(ctx: SDL.SDL_GLContext, proc: [:0]const u8) ?*anyopaque {
_ = ctx;
return SDL.SDL_GL_GetProcAddress(proc.ptr);

View File

@ -317,78 +317,3 @@ pub const FrameBuffer = struct {
return self.layers[if (dev == .Emulator) self.current else ~self.current];
}
};
pub fn RingBuffer(comptime T: type) type {
return struct {
const Self = @This();
const Index = usize;
const max_capacity = (@as(Index, 1) << @typeInfo(Index).Int.bits - 1) - 1; // half the range of index type
const log = std.log.scoped(.RingBuffer);
read: Index,
write: Index,
buf: []T,
const Error = error{buffer_full};
pub fn init(buf: []T) Self {
std.debug.assert(std.math.isPowerOfTwo(buf.len)); // capacity must be a power of two
std.debug.assert(buf.len <= max_capacity);
std.mem.set(T, buf, 0);
return .{ .read = 0, .write = 0, .buf = buf };
}
pub fn deinit(self: *Self, allocator: Allocator) void {
allocator.free(self.buf);
self.* = undefined;
}
pub fn push(self: *Self, value: T) Error!void {
if (self.isFull()) return error.buffer_full;
defer self.write += 1;
self.buf[self.mask(self.write)] = value;
}
pub fn pop(self: *Self) ?T {
if (self.isEmpty()) return null;
defer self.read += 1;
return self.buf[self.mask(self.read)];
}
/// Returns the number of entries read
pub fn copy(self: *const Self, cpy: []T) Index {
const count = std.math.min(self.len(), cpy.len);
var start: Index = self.read;
for (cpy) |*v, i| {
if (i >= count) break;
v.* = self.buf[self.mask(start)];
start += 1;
}
return count;
}
fn len(self: *const Self) Index {
return self.write - self.read;
}
fn isFull(self: *const Self) bool {
return self.len() == self.buf.len;
}
fn isEmpty(self: *const Self) bool {
return self.read == self.write;
}
fn mask(self: *const Self, idx: Index) Index {
return idx & (self.buf.len - 1);
}
};
}