From ead6d1ce4908d015846bf586eafaebff48d35f4a Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Sun, 9 Jan 2022 10:22:50 -0400 Subject: [PATCH] feat(ppu): improve timings + implement BG mode 3 bitmap --- src/emu.zig | 2 +- src/ppu.zig | 26 ++++++++++++++++------ src/scheduler.zig | 55 +++++++++++++++++++++++++---------------------- 3 files changed, 49 insertions(+), 34 deletions(-) diff --git a/src/emu.zig b/src/emu.zig index 7e305b5..459120c 100644 --- a/src/emu.zig +++ b/src/emu.zig @@ -2,7 +2,7 @@ const Bus = @import("Bus.zig"); const Scheduler = @import("scheduler.zig").Scheduler; const Arm7tdmi = @import("cpu.zig").Arm7tdmi; -const cycles_per_frame: u64 = 100; // TODO: How many cycles actually? +const cycles_per_frame: u64 = 160 * (308 * 4); pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void { var cycles: u64 = 0; diff --git a/src/ppu.zig b/src/ppu.zig index 86c22b5..e49f4af 100644 --- a/src/ppu.zig +++ b/src/ppu.zig @@ -1,6 +1,7 @@ const std = @import("std"); const EventKind = @import("scheduler.zig").EventKind; +const Io = @import("bus/io.zig").Io; const Scheduler = @import("scheduler.zig").Scheduler; const Allocator = std.mem.Allocator; @@ -18,18 +19,13 @@ pub const Ppu = struct { pub fn init(alloc: Allocator, sched: *Scheduler) !@This() { // Queue first Hblank - sched.push(.{ .kind = .HBlank, .tick = sched.tick + 240 * 4 }); - - // Initialize the Frame Buffer to white - const white_buf: [buf_len]u8 = [_]u8{ 0xFF, 0x7F } ** (buf_len / 2); - const frame_buf = try alloc.alloc(u8, buf_len); - std.mem.copy(u8, frame_buf, &white_buf); + sched.push(.{ .kind = .Draw, .tick = sched.tick + 240 * 4 }); return @This(){ .vram = try Vram.init(alloc), .palette = try Palette.init(alloc), .sched = sched, - .frame_buf = frame_buf, + .frame_buf = try alloc.alloc(u8, buf_len), .alloc = alloc, }; } @@ -39,6 +35,22 @@ pub const Ppu = struct { self.vram.deinit(); self.palette.deinit(); } + + pub fn drawScanline(self: *@This(), io: *const Io) void { + const bg_mode = io.dispcnt.bg_mode.read(); + const scanline = io.vcount.scanline.read(); + + switch (bg_mode) { + 0x3 => { + // Mode 3 + const start = buf_pitch * @as(usize, scanline); + const end = start + buf_pitch; + + std.mem.copy(u8, self.frame_buf[start..end], self.vram.buf[start..end]); + }, + else => std.debug.panic("[PPU] TODO: Implement BG Mode {}", .{bg_mode}), + } + } }; const Palette = struct { diff --git a/src/scheduler.zig b/src/scheduler.zig index 8b57d81..4bda5a6 100644 --- a/src/scheduler.zig +++ b/src/scheduler.zig @@ -37,45 +37,48 @@ pub const Scheduler = struct { std.debug.panic("[Scheduler] Somehow, a u64 overflowed", .{}); }, .HBlank => { - std.log.debug("[Scheduler] tick {}: Hblank", .{self.tick}); - - // We've reached the end of a scanline + // The End of a Hblank const scanline = bus.io.vcount.scanline.read(); - bus.io.vcount.scanline.write(scanline + 1); + const new_scanline = scanline + 1; - bus.io.dispstat.hblank.set(); + // TODO: Should this be done @ end of Draw instead of end of Hblank? + bus.ppu.drawScanline(&bus.io); - if (scanline < 160) { - self.push(.{ .kind = .Visible, .tick = self.tick + (68 * 4) }); + bus.io.vcount.scanline.write(new_scanline); + bus.io.dispstat.hblank.unset(); + + if (new_scanline < 160) { + // Transitioning to another Draw + self.push(.{ .kind = .Draw, .tick = self.tick + (240 * 4) }); } else { - self.push(.{ .kind = .VBlank, .tick = self.tick + (68 * 4) }); + // Transitioning to a Vblank + bus.io.dispstat.vblank.set(); + self.push(.{ .kind = .VBlank, .tick = self.tick + (308 * 4) }); } }, - .Visible => { - std.log.debug("[Scheduler] tick {}: Visible", .{self.tick}); + .Draw => { + // The end of a Draw - // Beginning of a Scanline - bus.io.dispstat.hblank.unset(); - bus.io.dispstat.vblank.unset(); - - self.push(.{ .kind = .HBlank, .tick = self.tick + (240 * 4) }); + // Transitioning to a Hblank + bus.io.dispstat.hblank.set(); + self.push(.{ .kind = .HBlank, .tick = self.tick + (68 * 4) }); }, .VBlank => { - std.log.debug("[Scheduler] tick {}: VBlank", .{self.tick}); - - // Beginning of a Scanline, not visible though - bus.io.dispstat.hblank.unset(); - bus.io.dispstat.vblank.set(); + // The end of a Vblank const scanline = bus.io.vcount.scanline.read(); - bus.io.vcount.scanline.write(scanline + 1); + const new_scanline = scanline + 1; + bus.io.vcount.scanline.write(new_scanline); - if (scanline < 227) { - // Another Vblank Scanline - self.push(.{ .kind = .VBlank, .tick = self.tick + 68 * (308 * 4) }); + if (new_scanline < 228) { + // Transition to another Vblank + self.push(.{ .kind = .VBlank, .tick = self.tick + (308 * 4) }); } else { + // Transition to another Draw bus.io.vcount.scanline.write(0); // Reset Scanline - self.push(.{ .kind = .Visible, .tick = self.tick + 68 * (308 * 4) }); + + bus.io.dispstat.vblank.unset(); + self.push(.{ .kind = .Draw, .tick = self.tick + (240 * 4) }); } }, } @@ -106,5 +109,5 @@ pub const EventKind = enum { HeatDeath, HBlank, VBlank, - Visible, + Draw, };