Compare commits
No commits in common. "0d4c850218c10343fd4ba5a895bd596177748792" and "ead6d1ce4908d015846bf586eafaebff48d35f4a" have entirely different histories.
0d4c850218
...
ead6d1ce49
19
src/Bus.zig
19
src/Bus.zig
|
@ -7,15 +7,14 @@ const Ppu = @import("ppu.zig").Ppu;
|
||||||
const Scheduler = @import("scheduler.zig").Scheduler;
|
const Scheduler = @import("scheduler.zig").Scheduler;
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pak: GamePak,
|
pak: GamePak,
|
||||||
bios: Bios,
|
bios: Bios,
|
||||||
ppu: Ppu,
|
ppu: Ppu,
|
||||||
io: Io,
|
io: Io,
|
||||||
|
|
||||||
pub fn init(alloc: Allocator, sched: *Scheduler, path: []const u8) !Self {
|
pub fn init(alloc: Allocator, sched: *Scheduler, path: []const u8) !@This() {
|
||||||
return Self{
|
return @This(){
|
||||||
.pak = try GamePak.init(alloc, path),
|
.pak = try GamePak.init(alloc, path),
|
||||||
.bios = try Bios.init(alloc, "./bin/gba_bios.bin"), // TODO: don't hardcode this + bundle open-sorce Boot ROM
|
.bios = try Bios.init(alloc, "./bin/gba_bios.bin"), // TODO: don't hardcode this + bundle open-sorce Boot ROM
|
||||||
.ppu = try Ppu.init(alloc, sched),
|
.ppu = try Ppu.init(alloc, sched),
|
||||||
|
@ -23,13 +22,13 @@ pub fn init(alloc: Allocator, sched: *Scheduler, path: []const u8) !Self {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: Self) void {
|
pub fn deinit(self: @This()) void {
|
||||||
self.pak.deinit();
|
self.pak.deinit();
|
||||||
self.bios.deinit();
|
self.bios.deinit();
|
||||||
self.ppu.deinit();
|
self.ppu.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read32(self: *const Self, addr: u32) u32 {
|
pub fn read32(self: *const @This(), addr: u32) u32 {
|
||||||
return switch (addr) {
|
return switch (addr) {
|
||||||
// General Internal Memory
|
// General Internal Memory
|
||||||
0x0000_0000...0x0000_3FFF => self.bios.get32(@as(usize, addr)),
|
0x0000_0000...0x0000_3FFF => self.bios.get32(@as(usize, addr)),
|
||||||
|
@ -54,7 +53,7 @@ pub fn read32(self: *const Self, addr: u32) u32 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write32(self: *Self, addr: u32, word: u32) void {
|
pub fn write32(self: *@This(), addr: u32, word: u32) void {
|
||||||
// TODO: write32 can write to GamePak Flash
|
// TODO: write32 can write to GamePak Flash
|
||||||
|
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
|
@ -72,7 +71,7 @@ pub fn write32(self: *Self, addr: u32, word: u32) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read16(self: *const Self, addr: u32) u16 {
|
pub fn read16(self: *const @This(), addr: u32) u16 {
|
||||||
return switch (addr) {
|
return switch (addr) {
|
||||||
// General Internal Memory
|
// General Internal Memory
|
||||||
0x0000_0000...0x0000_3FFF => self.bios.get16(@as(usize, addr)),
|
0x0000_0000...0x0000_3FFF => self.bios.get16(@as(usize, addr)),
|
||||||
|
@ -97,7 +96,7 @@ pub fn read16(self: *const Self, addr: u32) u16 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write16(self: *Self, addr: u32, halfword: u16) void {
|
pub fn write16(self: *@This(), addr: u32, halfword: u16) void {
|
||||||
// TODO: write16 can write to GamePak Flash
|
// TODO: write16 can write to GamePak Flash
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
// General Internal Memory
|
// General Internal Memory
|
||||||
|
@ -114,7 +113,7 @@ pub fn write16(self: *Self, addr: u32, halfword: u16) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read8(self: *const Self, addr: u32) u8 {
|
pub fn read8(self: *const @This(), addr: u32) u8 {
|
||||||
return switch (addr) {
|
return switch (addr) {
|
||||||
// General Internal Memory
|
// General Internal Memory
|
||||||
0x0000_0000...0x0000_3FFF => self.bios.get8(@as(usize, addr)),
|
0x0000_0000...0x0000_3FFF => self.bios.get8(@as(usize, addr)),
|
||||||
|
@ -140,7 +139,7 @@ pub fn read8(self: *const Self, addr: u32) u8 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write8(_: *Self, addr: u32, byte: u8) void {
|
pub fn write8(_: *@This(), addr: u32, byte: u8) void {
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
// General Internal Memory
|
// General Internal Memory
|
||||||
0x0200_0000...0x0203_FFFF => std.debug.panic("[Bus:8] write 0x{X:} to 0x{X:} in IWRAM", .{ byte, addr }),
|
0x0200_0000...0x0203_FFFF => std.debug.panic("[Bus:8] write 0x{X:} to 0x{X:} in IWRAM", .{ byte, addr }),
|
||||||
|
|
|
@ -22,17 +22,17 @@ pub fn deinit(self: Self) void {
|
||||||
self.alloc.free(self.buf);
|
self.alloc.free(self.buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get32(self: *const Self, idx: usize) u32 {
|
pub inline fn get32(self: *const Self, idx: usize) u32 {
|
||||||
std.debug.panic("[BIOS] TODO: BIOS is not implemented", .{});
|
std.debug.panic("[BIOS] TODO: BIOS is not implemented", .{});
|
||||||
return (@as(u32, self.buf[idx + 3]) << 24) | (@as(u32, self.buf[idx + 2]) << 16) | (@as(u32, self.buf[idx + 1]) << 8) | (@as(u32, self.buf[idx]));
|
return (@as(u32, self.buf[idx + 3]) << 24) | (@as(u32, self.buf[idx + 2]) << 16) | (@as(u32, self.buf[idx + 1]) << 8) | (@as(u32, self.buf[idx]));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get16(self: *const Self, idx: usize) u16 {
|
pub inline fn get16(self: *const Self, idx: usize) u16 {
|
||||||
std.debug.panic("[BIOS] TODO: BIOS is not implemented", .{});
|
std.debug.panic("[BIOS] TODO: BIOS is not implemented", .{});
|
||||||
return (@as(u16, self.buf[idx + 1]) << 8) | @as(u16, self.buf[idx]);
|
return (@as(u16, self.buf[idx + 1]) << 8) | @as(u16, self.buf[idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get8(self: *const Self, idx: usize) u8 {
|
pub inline fn get8(self: *const Self, idx: usize) u8 {
|
||||||
std.debug.panic("[BIOS] TODO: BIOS is not implemented", .{});
|
std.debug.panic("[BIOS] TODO: BIOS is not implemented", .{});
|
||||||
return self.buf[idx];
|
return self.buf[idx];
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,14 +22,14 @@ pub fn deinit(self: Self) void {
|
||||||
self.alloc.free(self.buf);
|
self.alloc.free(self.buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get32(self: *const Self, idx: usize) u32 {
|
pub inline fn get32(self: *const Self, idx: usize) u32 {
|
||||||
return (@as(u32, self.get16(idx + 2)) << 16) | @as(u32, self.get16(idx));
|
return (@as(u32, self.get16(idx + 2)) << 16) | @as(u32, self.get16(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get16(self: *const Self, idx: usize) u16 {
|
pub inline fn get16(self: *const Self, idx: usize) u16 {
|
||||||
return (@as(u16, self.buf[idx + 1]) << 8) | @as(u16, self.buf[idx]);
|
return (@as(u16, self.buf[idx + 1]) << 8) | @as(u16, self.buf[idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get8(self: *const Self, idx: usize) u8 {
|
pub inline fn get8(self: *const Self, idx: usize) u8 {
|
||||||
return self.buf[idx];
|
return self.buf[idx];
|
||||||
}
|
}
|
||||||
|
|
14
src/cpu.zig
14
src/cpu.zig
|
@ -16,14 +16,12 @@ pub const InstrFn = fn (*Arm7tdmi, *Bus, u32) void;
|
||||||
const arm_lut: [0x1000]InstrFn = populate();
|
const arm_lut: [0x1000]InstrFn = populate();
|
||||||
|
|
||||||
pub const Arm7tdmi = struct {
|
pub const Arm7tdmi = struct {
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
r: [16]u32,
|
r: [16]u32,
|
||||||
sched: *Scheduler,
|
sched: *Scheduler,
|
||||||
bus: *Bus,
|
bus: *Bus,
|
||||||
cpsr: CPSR,
|
cpsr: CPSR,
|
||||||
|
|
||||||
pub fn init(sched: *Scheduler, bus: *Bus) Self {
|
pub fn init(sched: *Scheduler, bus: *Bus) @This() {
|
||||||
return .{
|
return .{
|
||||||
.r = [_]u32{0x00} ** 16,
|
.r = [_]u32{0x00} ** 16,
|
||||||
.sched = sched,
|
.sched = sched,
|
||||||
|
@ -32,7 +30,7 @@ pub const Arm7tdmi = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skipBios(self: *Self) void {
|
pub fn skipBios(self: *@This()) void {
|
||||||
self.r[0] = 0x08000000;
|
self.r[0] = 0x08000000;
|
||||||
self.r[1] = 0x000000EA;
|
self.r[1] = 0x000000EA;
|
||||||
// GPRs 2 -> 12 *should* already be 0 initialized
|
// GPRs 2 -> 12 *should* already be 0 initialized
|
||||||
|
@ -45,7 +43,7 @@ pub const Arm7tdmi = struct {
|
||||||
self.cpsr.raw = 0x6000001F;
|
self.cpsr.raw = 0x6000001F;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(self: *Self) u64 {
|
pub inline fn step(self: *@This()) u64 {
|
||||||
const opcode = self.fetch();
|
const opcode = self.fetch();
|
||||||
// self.mgbaLog(opcode);
|
// self.mgbaLog(opcode);
|
||||||
|
|
||||||
|
@ -53,17 +51,17 @@ pub const Arm7tdmi = struct {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(self: *Self) u32 {
|
fn fetch(self: *@This()) u32 {
|
||||||
const word = self.bus.read32(self.r[15]);
|
const word = self.bus.read32(self.r[15]);
|
||||||
self.r[15] += 4;
|
self.r[15] += 4;
|
||||||
return word;
|
return word;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fakePC(self: *const Self) u32 {
|
pub fn fakePC(self: *const @This()) u32 {
|
||||||
return self.r[15] + 4;
|
return self.r[15] + 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mgbaLog(self: *const Self, opcode: u32) void {
|
fn mgbaLog(self: *const @This(), opcode: u32) void {
|
||||||
const stderr = std.io.getStdErr().writer();
|
const stderr = std.io.getStdErr().writer();
|
||||||
std.debug.getStderrMutex().lock();
|
std.debug.getStderrMutex().lock();
|
||||||
defer std.debug.getStderrMutex().unlock();
|
defer std.debug.getStderrMutex().unlock();
|
||||||
|
|
|
@ -3,7 +3,7 @@ const std = @import("std");
|
||||||
const Arm7tdmi = @import("../cpu.zig").Arm7tdmi;
|
const Arm7tdmi = @import("../cpu.zig").Arm7tdmi;
|
||||||
const CPSR = @import("../cpu.zig").CPSR;
|
const CPSR = @import("../cpu.zig").CPSR;
|
||||||
|
|
||||||
pub fn exec(comptime S: bool, cpu: *Arm7tdmi, opcode: u32) u32 {
|
pub inline fn exec(comptime S: bool, cpu: *Arm7tdmi, opcode: u32) u32 {
|
||||||
var shift_amt: u8 = undefined;
|
var shift_amt: u8 = undefined;
|
||||||
if (opcode >> 4 & 1 == 1) {
|
if (opcode >> 4 & 1 == 1) {
|
||||||
shift_amt = @truncate(u8, cpu.r[opcode >> 8 & 0xF]);
|
shift_amt = @truncate(u8, cpu.r[opcode >> 8 & 0xF]);
|
||||||
|
@ -31,7 +31,7 @@ pub fn exec(comptime S: bool, cpu: *Arm7tdmi, opcode: u32) u32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn logical_left(cpsr: *CPSR, rm: u32, shift_byte: u8) u32 {
|
pub inline fn logical_left(cpsr: *CPSR, rm: u32, shift_byte: u8) u32 {
|
||||||
const shift_amt = @truncate(u5, shift_byte);
|
const shift_amt = @truncate(u5, shift_byte);
|
||||||
const bit_count: u8 = @typeInfo(u32).Int.bits;
|
const bit_count: u8 = @typeInfo(u32).Int.bits;
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ pub fn logical_left(cpsr: *CPSR, rm: u32, shift_byte: u8) u32 {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn logical_right(cpsr: *CPSR, rm: u32, shift_byte: u8) u32 {
|
pub inline fn logical_right(cpsr: *CPSR, rm: u32, shift_byte: u8) u32 {
|
||||||
const shift_amt = @truncate(u5, shift_byte);
|
const shift_amt = @truncate(u5, shift_byte);
|
||||||
const bit_count: u8 = @typeInfo(u32).Int.bits;
|
const bit_count: u8 = @typeInfo(u32).Int.bits;
|
||||||
|
|
||||||
|
|
10
src/main.zig
10
src/main.zig
|
@ -6,8 +6,6 @@ const Bus = @import("Bus.zig");
|
||||||
const Arm7tdmi = @import("cpu.zig").Arm7tdmi;
|
const Arm7tdmi = @import("cpu.zig").Arm7tdmi;
|
||||||
const Scheduler = @import("scheduler.zig").Scheduler;
|
const Scheduler = @import("scheduler.zig").Scheduler;
|
||||||
|
|
||||||
const Timer = std.time.Timer;
|
|
||||||
|
|
||||||
const buf_pitch = @import("ppu.zig").buf_pitch;
|
const buf_pitch = @import("ppu.zig").buf_pitch;
|
||||||
|
|
||||||
pub fn main() anyerror!void {
|
pub fn main() anyerror!void {
|
||||||
|
@ -61,10 +59,6 @@ pub fn main() anyerror!void {
|
||||||
const texture = SDL.SDL_CreateTexture(renderer, SDL.SDL_PIXELFORMAT_BGR555, SDL.SDL_TEXTUREACCESS_STREAMING, 240, 160) orelse sdlPanic();
|
const texture = SDL.SDL_CreateTexture(renderer, SDL.SDL_PIXELFORMAT_BGR555, SDL.SDL_TEXTUREACCESS_STREAMING, 240, 160) orelse sdlPanic();
|
||||||
defer SDL.SDL_DestroyTexture(texture);
|
defer SDL.SDL_DestroyTexture(texture);
|
||||||
|
|
||||||
// Init FPS Timer
|
|
||||||
var timer = Timer.start() catch unreachable;
|
|
||||||
var title_buf: [0x30]u8 = [_]u8{0x00} ** 0x30;
|
|
||||||
|
|
||||||
emu_loop: while (true) {
|
emu_loop: while (true) {
|
||||||
emu.runFrame(&scheduler, &cpu, &bus);
|
emu.runFrame(&scheduler, &cpu, &bus);
|
||||||
|
|
||||||
|
@ -80,10 +74,6 @@ pub fn main() anyerror!void {
|
||||||
_ = SDL.SDL_UpdateTexture(texture, null, buf_ptr, buf_pitch);
|
_ = SDL.SDL_UpdateTexture(texture, null, buf_ptr, buf_pitch);
|
||||||
_ = SDL.SDL_RenderCopy(renderer, texture, null, null);
|
_ = SDL.SDL_RenderCopy(renderer, texture, null, null);
|
||||||
SDL.SDL_RenderPresent(renderer);
|
SDL.SDL_RenderPresent(renderer);
|
||||||
|
|
||||||
const fps = std.time.ns_per_s / timer.lap();
|
|
||||||
const title = std.fmt.bufPrint(&title_buf, "Gameboy Advance Emulator FPS: {d}", .{fps}) catch unreachable;
|
|
||||||
SDL.SDL_SetWindowTitle(window, title.ptr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
48
src/ppu.zig
48
src/ppu.zig
|
@ -11,19 +11,17 @@ pub const buf_pitch = width * @sizeOf(u16);
|
||||||
const buf_len = buf_pitch * height;
|
const buf_len = buf_pitch * height;
|
||||||
|
|
||||||
pub const Ppu = struct {
|
pub const Ppu = struct {
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
vram: Vram,
|
vram: Vram,
|
||||||
palette: Palette,
|
palette: Palette,
|
||||||
sched: *Scheduler,
|
sched: *Scheduler,
|
||||||
frame_buf: []u8,
|
frame_buf: []u8,
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
|
||||||
pub fn init(alloc: Allocator, sched: *Scheduler) !Self {
|
pub fn init(alloc: Allocator, sched: *Scheduler) !@This() {
|
||||||
// Queue first Hblank
|
// Queue first Hblank
|
||||||
sched.push(.Draw, sched.tick + (240 * 4));
|
sched.push(.{ .kind = .Draw, .tick = sched.tick + 240 * 4 });
|
||||||
|
|
||||||
return Self{
|
return @This(){
|
||||||
.vram = try Vram.init(alloc),
|
.vram = try Vram.init(alloc),
|
||||||
.palette = try Palette.init(alloc),
|
.palette = try Palette.init(alloc),
|
||||||
.sched = sched,
|
.sched = sched,
|
||||||
|
@ -32,13 +30,13 @@ pub const Ppu = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: Self) void {
|
pub fn deinit(self: @This()) void {
|
||||||
self.alloc.free(self.frame_buf);
|
self.alloc.free(self.frame_buf);
|
||||||
self.vram.deinit();
|
self.vram.deinit();
|
||||||
self.palette.deinit();
|
self.palette.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drawScanline(self: *Self, io: *const Io) void {
|
pub fn drawScanline(self: *@This(), io: *const Io) void {
|
||||||
const bg_mode = io.dispcnt.bg_mode.read();
|
const bg_mode = io.dispcnt.bg_mode.read();
|
||||||
const scanline = io.vcount.scanline.read();
|
const scanline = io.vcount.scanline.read();
|
||||||
|
|
||||||
|
@ -56,81 +54,77 @@ pub const Ppu = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Palette = struct {
|
const Palette = struct {
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
buf: []u8,
|
buf: []u8,
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
|
||||||
fn init(alloc: Allocator) !Self {
|
fn init(alloc: Allocator) !@This() {
|
||||||
return Self{
|
return @This(){
|
||||||
.buf = try alloc.alloc(u8, 0x400),
|
.buf = try alloc.alloc(u8, 0x400),
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: Self) void {
|
fn deinit(self: @This()) void {
|
||||||
self.alloc.free(self.buf);
|
self.alloc.free(self.buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get32(self: *const Self, idx: usize) u32 {
|
pub inline fn get32(self: *const @This(), idx: usize) u32 {
|
||||||
return (@as(u32, self.get16(idx + 2)) << 16) | @as(u32, self.get16(idx));
|
return (@as(u32, self.get16(idx + 2)) << 16) | @as(u32, self.get16(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set32(self: *Self, idx: usize, word: u32) void {
|
pub inline fn set32(self: *@This(), idx: usize, word: u32) void {
|
||||||
self.set16(idx + 2, @truncate(u16, word >> 16));
|
self.set16(idx + 2, @truncate(u16, word >> 16));
|
||||||
self.set16(idx, @truncate(u16, word));
|
self.set16(idx, @truncate(u16, word));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get16(self: *const Self, idx: usize) u16 {
|
pub inline fn get16(self: *const @This(), idx: usize) u16 {
|
||||||
return (@as(u16, self.buf[idx + 1]) << 8) | @as(u16, self.buf[idx]);
|
return (@as(u16, self.buf[idx + 1]) << 8) | @as(u16, self.buf[idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set16(self: *Self, idx: usize, halfword: u16) void {
|
pub inline fn set16(self: *@This(), idx: usize, halfword: u16) void {
|
||||||
self.buf[idx + 1] = @truncate(u8, halfword >> 8);
|
self.buf[idx + 1] = @truncate(u8, halfword >> 8);
|
||||||
self.buf[idx] = @truncate(u8, halfword);
|
self.buf[idx] = @truncate(u8, halfword);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get8(self: *const Self, idx: usize) u8 {
|
pub inline fn get8(self: *const @This(), idx: usize) u8 {
|
||||||
return self.buf[idx];
|
return self.buf[idx];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Vram = struct {
|
const Vram = struct {
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
buf: []u8,
|
buf: []u8,
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
|
||||||
fn init(alloc: Allocator) !Self {
|
fn init(alloc: Allocator) !@This() {
|
||||||
return Self{
|
return @This(){
|
||||||
.buf = try alloc.alloc(u8, 0x18000),
|
.buf = try alloc.alloc(u8, 0x18000),
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: Self) void {
|
fn deinit(self: @This()) void {
|
||||||
self.alloc.free(self.buf);
|
self.alloc.free(self.buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get32(self: *const Self, idx: usize) u32 {
|
pub inline fn get32(self: *const @This(), idx: usize) u32 {
|
||||||
return (@as(u32, self.get16(idx + 2)) << 16) | @as(u32, self.get16(idx));
|
return (@as(u32, self.get16(idx + 2)) << 16) | @as(u32, self.get16(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set32(self: *Self, idx: usize, word: u32) void {
|
pub inline fn set32(self: *@This(), idx: usize, word: u32) void {
|
||||||
self.set16(idx + 2, @truncate(u16, word >> 16));
|
self.set16(idx + 2, @truncate(u16, word >> 16));
|
||||||
self.set16(idx, @truncate(u16, word));
|
self.set16(idx, @truncate(u16, word));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get16(self: *const Self, idx: usize) u16 {
|
pub inline fn get16(self: *const @This(), idx: usize) u16 {
|
||||||
return (@as(u16, self.buf[idx + 1]) << 8) | @as(u16, self.buf[idx]);
|
return (@as(u16, self.buf[idx + 1]) << 8) | @as(u16, self.buf[idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set16(self: *Self, idx: usize, halfword: u16) void {
|
pub inline fn set16(self: *@This(), idx: usize, halfword: u16) void {
|
||||||
self.buf[idx + 1] = @truncate(u8, halfword >> 8);
|
self.buf[idx + 1] = @truncate(u8, halfword >> 8);
|
||||||
self.buf[idx] = @truncate(u8, halfword);
|
self.buf[idx] = @truncate(u8, halfword);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get8(self: *const Self, idx: usize) u8 {
|
pub inline fn get8(self: *const @This(), idx: usize) u8 {
|
||||||
return self.buf[idx];
|
return self.buf[idx];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,28 +8,29 @@ const PriorityQueue = std.PriorityQueue;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
pub const Scheduler = struct {
|
pub const Scheduler = struct {
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
tick: u64,
|
tick: u64,
|
||||||
queue: PriorityQueue(Event, void, lessThan),
|
queue: PriorityQueue(Event, void, lessThan),
|
||||||
|
|
||||||
pub fn init(alloc: Allocator) Self {
|
pub fn init(alloc: Allocator) @This() {
|
||||||
var sched = Self{ .tick = 0, .queue = PriorityQueue(Event, void, lessThan).init(alloc, {}) };
|
var scheduler = Scheduler{ .tick = 0, .queue = PriorityQueue(Event, void, lessThan).init(alloc, {}) };
|
||||||
sched.push(.HeatDeath, std.math.maxInt(u64));
|
|
||||||
|
|
||||||
return sched;
|
scheduler.queue.add(.{
|
||||||
|
.kind = EventKind.HeatDeath,
|
||||||
|
.tick = std.math.maxInt(u64),
|
||||||
|
}) catch unreachable;
|
||||||
|
|
||||||
|
return scheduler;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: Self) void {
|
pub fn deinit(self: @This()) void {
|
||||||
self.queue.deinit();
|
self.queue.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handleEvent(self: *Self, _: *Arm7tdmi, bus: *Bus) void {
|
pub fn handleEvent(self: *@This(), _: *Arm7tdmi, bus: *Bus) void {
|
||||||
const should_handle = if (self.queue.peek()) |e| self.tick >= e.tick else false;
|
const should_handle = if (self.queue.peek()) |e| self.tick >= e.tick else false;
|
||||||
|
|
||||||
if (should_handle) {
|
if (should_handle) {
|
||||||
const event = self.queue.remove();
|
const event = self.queue.remove();
|
||||||
// std.log.info("[Scheduler] Handle {} at {} ticks", .{ event.kind, self.tick });
|
|
||||||
|
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
.HeatDeath => {
|
.HeatDeath => {
|
||||||
|
@ -48,11 +49,11 @@ pub const Scheduler = struct {
|
||||||
|
|
||||||
if (new_scanline < 160) {
|
if (new_scanline < 160) {
|
||||||
// Transitioning to another Draw
|
// Transitioning to another Draw
|
||||||
self.push(.Draw, self.tick + (240 * 4));
|
self.push(.{ .kind = .Draw, .tick = self.tick + (240 * 4) });
|
||||||
} else {
|
} else {
|
||||||
// Transitioning to a Vblank
|
// Transitioning to a Vblank
|
||||||
bus.io.dispstat.vblank.set();
|
bus.io.dispstat.vblank.set();
|
||||||
self.push(.VBlank, self.tick + (308 * 4));
|
self.push(.{ .kind = .VBlank, .tick = self.tick + (308 * 4) });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.Draw => {
|
.Draw => {
|
||||||
|
@ -60,7 +61,7 @@ pub const Scheduler = struct {
|
||||||
|
|
||||||
// Transitioning to a Hblank
|
// Transitioning to a Hblank
|
||||||
bus.io.dispstat.hblank.set();
|
bus.io.dispstat.hblank.set();
|
||||||
self.push(.HBlank, self.tick + (68 * 4));
|
self.push(.{ .kind = .HBlank, .tick = self.tick + (68 * 4) });
|
||||||
},
|
},
|
||||||
.VBlank => {
|
.VBlank => {
|
||||||
// The end of a Vblank
|
// The end of a Vblank
|
||||||
|
@ -71,24 +72,24 @@ pub const Scheduler = struct {
|
||||||
|
|
||||||
if (new_scanline < 228) {
|
if (new_scanline < 228) {
|
||||||
// Transition to another Vblank
|
// Transition to another Vblank
|
||||||
self.push(.VBlank, self.tick + (308 * 4));
|
self.push(.{ .kind = .VBlank, .tick = self.tick + (308 * 4) });
|
||||||
} else {
|
} else {
|
||||||
// Transition to another Draw
|
// Transition to another Draw
|
||||||
bus.io.vcount.scanline.write(0); // Reset Scanline
|
bus.io.vcount.scanline.write(0); // Reset Scanline
|
||||||
|
|
||||||
bus.io.dispstat.vblank.unset();
|
bus.io.dispstat.vblank.unset();
|
||||||
self.push(.Draw, self.tick + (240 * 4));
|
self.push(.{ .kind = .Draw, .tick = self.tick + (240 * 4) });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(self: *Self, kind: EventKind, end: u64) void {
|
pub inline fn push(self: *@This(), event: Event) void {
|
||||||
self.queue.add(.{ .kind = kind, .tick = end }) catch unreachable;
|
self.queue.add(event) catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nextTimestamp(self: *Self) u64 {
|
pub inline fn nextTimestamp(self: *@This()) u64 {
|
||||||
if (self.queue.peek()) |e| {
|
if (self.queue.peek()) |e| {
|
||||||
return e.tick;
|
return e.tick;
|
||||||
} else unreachable;
|
} else unreachable;
|
||||||
|
|
Loading…
Reference in New Issue