Initial Commit
This commit is contained in:
commit
5b3b81e4dc
|
@ -0,0 +1,4 @@
|
||||||
|
/.vscode
|
||||||
|
/bin
|
||||||
|
/zig-cache
|
||||||
|
/zig-out
|
|
@ -0,0 +1,34 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.build.Builder) void {
|
||||||
|
// Standard target options allows the person running `zig build` to choose
|
||||||
|
// what target to build for. Here we do not override the defaults, which
|
||||||
|
// means any target is allowed, and the default is native. Other options
|
||||||
|
// for restricting supported target set are available.
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
// Standard release options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||||
|
const mode = b.standardReleaseOptions();
|
||||||
|
|
||||||
|
const exe = b.addExecutable("zba", "src/main.zig");
|
||||||
|
exe.setTarget(target);
|
||||||
|
exe.setBuildMode(mode);
|
||||||
|
exe.install();
|
||||||
|
|
||||||
|
const run_cmd = exe.run();
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
|
||||||
|
const exe_tests = b.addTest("src/main.zig");
|
||||||
|
exe_tests.setTarget(target);
|
||||||
|
exe_tests.setBuildMode(mode);
|
||||||
|
|
||||||
|
const test_step = b.step("test", "Run unit tests");
|
||||||
|
test_step.dependOn(&exe_tests.step);
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const GamePak = @import("pak.zig").GamePak;
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
pub const Bus = struct {
|
||||||
|
pak: GamePak,
|
||||||
|
|
||||||
|
pub fn withPak(alloc: Allocator, path: []const u8) !@This() {
|
||||||
|
return @This(){
|
||||||
|
.pak = try GamePak.fromPath(alloc, path),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readWord(self: *const @This(), addr: u32) u32 {
|
||||||
|
return self.pak.readWord(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeWord(_: *@This(), _: u32, _: u32) void {
|
||||||
|
std.debug.panic("TODO: Implement Bus#writeWord", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readHalfWord(self: *const @This(), addr: u32) u16 {
|
||||||
|
return self.pak.readHalfWord(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeHalfWord(self: *@This(), addr: u32, halfword: u16) void {
|
||||||
|
|
||||||
|
// TODO: Actually implement the memory mmap
|
||||||
|
if (addr >= self.pak.buf.len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pak.writeHalfWord(addr, halfword);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readByte(self: *const @This(), addr: u32) u8 {
|
||||||
|
return self.pak.readByte(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeByte(_: *@This(), _: u32, _: u8) void {
|
||||||
|
std.debug.panic("TODO: Implement Bus#writeByte", .{});
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,152 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Bus = @import("bus.zig").Bus;
|
||||||
|
const Scheduler = @import("scheduler.zig").Scheduler;
|
||||||
|
|
||||||
|
const comptimeDataProcessing = @import("cpu/data_processing.zig").comptimeDataProcessing;
|
||||||
|
const comptimeSingleDataTransfer = @import("cpu/single_data_transfer.zig").comptimeSingleDataTransfer;
|
||||||
|
const comptimeHalfSignedDataTransfer = @import("cpu/half_signed_data_transfer.zig").comptimeHalfSignedDataTransfer;
|
||||||
|
|
||||||
|
pub const InstrFn = fn (*ARM7TDMI, *Bus, u32) void;
|
||||||
|
const ARM_LUT: [0x1000]InstrFn = populate();
|
||||||
|
|
||||||
|
pub const ARM7TDMI = struct {
|
||||||
|
r: [16]u32,
|
||||||
|
sch: *Scheduler,
|
||||||
|
bus: *Bus,
|
||||||
|
cpsr: CPSR,
|
||||||
|
|
||||||
|
pub fn new(scheduler: *Scheduler, bus: *Bus) @This() {
|
||||||
|
const cpsr: u32 = 0x0000_00DF;
|
||||||
|
return .{
|
||||||
|
.r = [_]u32{0x00} ** 16,
|
||||||
|
.sch = scheduler,
|
||||||
|
.bus = bus,
|
||||||
|
.cpsr = @bitCast(CPSR, cpsr),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn step(self: *@This()) u64 {
|
||||||
|
const opcode = self.fetch();
|
||||||
|
// Debug
|
||||||
|
std.debug.print("R15: 0x{X:}\n", .{ opcode });
|
||||||
|
|
||||||
|
ARM_LUT[armIdx(opcode)](self, self.bus, opcode);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fetch(self: *@This()) u32 {
|
||||||
|
const word = self.bus.readWord(self.r[15]);
|
||||||
|
self.r[15] += 4;
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fakePC(self: *const @This()) u32 {
|
||||||
|
return self.r[15] + 4;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn armIdx(opcode: u32) u12 {
|
||||||
|
return @truncate(u12, opcode >> 20 & 0xFF) << 4 | @truncate(u12, opcode >> 8 & 0xF);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn populate() [0x1000]InstrFn {
|
||||||
|
return comptime {
|
||||||
|
@setEvalBranchQuota(0x5000);
|
||||||
|
var lut = [_]InstrFn{undefined_instr} ** 0x1000;
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < lut.len) : (i += 1) {
|
||||||
|
if (i >> 10 & 0x3 == 0b00) {
|
||||||
|
const I = i >> 9 & 0x01 == 0x01;
|
||||||
|
const S = i >> 4 & 0x01 == 0x01;
|
||||||
|
const instrKind = i >> 5 & 0x0F;
|
||||||
|
|
||||||
|
lut[i] = comptimeDataProcessing(I, S, instrKind);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >> 9 & 0x7 == 0b000 and i >> 6 & 0x01 == 0x00 and i & 0xF == 0x0) {
|
||||||
|
// Halfword and Signed Data Transfer with register offset
|
||||||
|
const P = i >> 8 & 0x01 == 0x01;
|
||||||
|
const U = i >> 7 & 0x01 == 0x01;
|
||||||
|
const I = true;
|
||||||
|
const W = i >> 5 & 0x01 == 0x01;
|
||||||
|
const L = i >> 4 & 0x01 == 0x01;
|
||||||
|
|
||||||
|
lut[i] = comptimeHalfSignedDataTransfer(P, U, I, W, L);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >> 9 & 0x7 == 0b000 and i >> 6 & 0x01 == 0x01) {
|
||||||
|
// Halfword and Signed Data Tranfer with immediate offset
|
||||||
|
const P = i >> 8 & 0x01 == 0x01;
|
||||||
|
const U = i >> 7 & 0x01 == 0x01;
|
||||||
|
const I = false;
|
||||||
|
const W = i >> 5 & 0x01 == 0x01;
|
||||||
|
const L = i >> 4 & 0x01 == 0x01;
|
||||||
|
|
||||||
|
lut[i] = comptimeHalfSignedDataTransfer(P, U, I, W, L);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >> 10 & 0x3 == 0b01 and i & 0x01 == 0x00) {
|
||||||
|
const I = i >> 9 & 0x01 == 0x01;
|
||||||
|
const P = i >> 8 & 0x01 == 0x01;
|
||||||
|
const U = i >> 7 & 0x01 == 0x01;
|
||||||
|
const B = i >> 6 & 0x01 == 0x01;
|
||||||
|
const W = i >> 5 & 0x01 == 0x01;
|
||||||
|
const L = i >> 4 & 0x01 == 0x01;
|
||||||
|
|
||||||
|
lut[i] = comptimeSingleDataTransfer(I, P, U, B, W, L);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >> 9 & 0x7 == 0b101) {
|
||||||
|
const L = i >> 8 & 0x01 == 0x01;
|
||||||
|
lut[i] = comptimeBranch(L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lut;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const CPSR = packed struct {
|
||||||
|
n: bool, // Negative / Less Than
|
||||||
|
z: bool, // Zero
|
||||||
|
c: bool, // Carry / Borrow / Extend
|
||||||
|
v: bool, // Overflow
|
||||||
|
_: u20,
|
||||||
|
i: bool, // IRQ Disable
|
||||||
|
f: bool, // FIQ Diable
|
||||||
|
t: bool, // State
|
||||||
|
m: Mode, // Mode
|
||||||
|
};
|
||||||
|
|
||||||
|
const Mode = enum(u5) {
|
||||||
|
User = 0b10000,
|
||||||
|
Fiq = 0b10001,
|
||||||
|
Irq = 0b10010,
|
||||||
|
Supervisor = 0b10011,
|
||||||
|
Abort = 0b10111,
|
||||||
|
Undefined = 0b11011,
|
||||||
|
System = 0b11111,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn undefined_instr(_: *ARM7TDMI, _: *Bus, opcode: u32) void {
|
||||||
|
const id = armIdx(opcode);
|
||||||
|
std.debug.panic("[0x{X:}] 0x{X:} is an illegal opcode", .{ id, opcode });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn comptimeBranch(comptime L: bool) InstrFn {
|
||||||
|
return struct {
|
||||||
|
fn branch(cpu: *ARM7TDMI, _: *Bus, opcode: u32) void {
|
||||||
|
if (L) {
|
||||||
|
cpu.r[14] = cpu.r[15] - 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const offset = @bitCast(i32, (opcode << 2) << 8) >> 8;
|
||||||
|
cpu.r[15] = cpu.fakePC() + @bitCast(u32, offset);
|
||||||
|
}
|
||||||
|
}.branch;
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const cpu_mod = @import("../cpu.zig");
|
||||||
|
|
||||||
|
const Bus = @import("../bus.zig").Bus;
|
||||||
|
const ARM7TDMI = cpu_mod.ARM7TDMI;
|
||||||
|
const InstrFn = cpu_mod.InstrFn;
|
||||||
|
|
||||||
|
pub fn comptimeDataProcessing(comptime I: bool, comptime S: bool, comptime instrKind: u4) InstrFn {
|
||||||
|
return struct {
|
||||||
|
fn dataProcessing(cpu: *ARM7TDMI, _: *Bus, opcode: u32) void {
|
||||||
|
const rd = opcode >> 12 & 0xF;
|
||||||
|
const op1 = opcode >> 16 & 0xF;
|
||||||
|
|
||||||
|
var op2: u32 = undefined;
|
||||||
|
if (I) {
|
||||||
|
op2 = std.math.rotr(u32, opcode & 0xFF, (opcode >> 8 & 0xF) << 1);
|
||||||
|
} else {
|
||||||
|
op2 = reg_op2(cpu, opcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (instrKind) {
|
||||||
|
0x4 => {
|
||||||
|
cpu.r[rd] = cpu.r[op1] + op2;
|
||||||
|
|
||||||
|
if (S) std.debug.panic("TODO: implement ADD condition codes", .{});
|
||||||
|
},
|
||||||
|
0xD => {
|
||||||
|
cpu.r[rd] = op2;
|
||||||
|
|
||||||
|
if (S) std.debug.panic("TODO: implement MOV condition codes", .{});
|
||||||
|
},
|
||||||
|
else => std.debug.panic("TODO: implement data processing type {}", .{instrKind}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.dataProcessing;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_op2(cpu: *const ARM7TDMI, opcode: u32) u32 {
|
||||||
|
var amount: u32 = undefined;
|
||||||
|
if (opcode >> 4 & 0x01 == 0x01) {
|
||||||
|
amount = cpu.r[opcode >> 8 & 0xF] & 0xFF;
|
||||||
|
} else {
|
||||||
|
amount = opcode >> 7 & 0x1F;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rm = opcode & 0xF;
|
||||||
|
const r_val = cpu.r[rm];
|
||||||
|
|
||||||
|
return switch (opcode >> 5 & 0x03) {
|
||||||
|
0b00 => r_val << @truncate(u5, amount),
|
||||||
|
0b01 => r_val >> @truncate(u5, amount),
|
||||||
|
0b10 => @bitCast(u32, @bitCast(i32, r_val) >> @truncate(u5, amount)),
|
||||||
|
0b11 => std.math.rotr(u32, r_val, amount),
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const cpu_mod = @import("../cpu.zig");
|
||||||
|
const util = @import("../util.zig");
|
||||||
|
|
||||||
|
const Bus = @import("../bus.zig").Bus;
|
||||||
|
const ARM7TDMI = cpu_mod.ARM7TDMI;
|
||||||
|
const InstrFn = cpu_mod.InstrFn;
|
||||||
|
|
||||||
|
pub fn comptimeHalfSignedDataTransfer(comptime P: bool, comptime U: bool, comptime I: bool, comptime W: bool, comptime L: bool) InstrFn {
|
||||||
|
return struct {
|
||||||
|
fn halfSignedDataTransfer(cpu: *ARM7TDMI, bus: *Bus, opcode: u32) void {
|
||||||
|
const rn = opcode >> 16 & 0xF;
|
||||||
|
const rd = opcode >> 12 & 0xF;
|
||||||
|
const rm = opcode & 0xF;
|
||||||
|
const imm_offset_high = opcode >> 8 & 0xF;
|
||||||
|
|
||||||
|
const base = cpu.r[rn];
|
||||||
|
|
||||||
|
var offset: u32 = undefined;
|
||||||
|
if (I) {
|
||||||
|
offset = imm_offset_high << 4 | rm;
|
||||||
|
} else {
|
||||||
|
offset = cpu.r[rm];
|
||||||
|
}
|
||||||
|
|
||||||
|
const modified_base = if (U) base + offset else base - offset;
|
||||||
|
var address = if (P) modified_base else base;
|
||||||
|
|
||||||
|
if (L) {
|
||||||
|
switch(@truncate(u2, opcode >> 5)) {
|
||||||
|
0b00 => {
|
||||||
|
// SWP
|
||||||
|
std.debug.panic("TODO: Implement SWP", .{});
|
||||||
|
},
|
||||||
|
0b01 => {
|
||||||
|
// LDRH
|
||||||
|
const halfword = bus.readHalfWord(address);
|
||||||
|
cpu.r[rd] = @as(u32, halfword);
|
||||||
|
},
|
||||||
|
0b10 => {
|
||||||
|
// LDRSB
|
||||||
|
const byte = bus.readByte(address);
|
||||||
|
cpu.r[rd] = util.u32_sign_extend(@as(u32, byte), 8);
|
||||||
|
},
|
||||||
|
0b11 => {
|
||||||
|
// LDRSH
|
||||||
|
const halfword = bus.readHalfWord(address);
|
||||||
|
cpu.r[rd] = util.u32_sign_extend(@as(u32, halfword), 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (opcode >> 5 & 0x01 == 0x01) {
|
||||||
|
// STRH
|
||||||
|
const src = @truncate(u16, cpu.r[rd]);
|
||||||
|
|
||||||
|
bus.writeHalfWord(address + 2, src);
|
||||||
|
bus.writeHalfWord(address, src);
|
||||||
|
} else {
|
||||||
|
std.debug.panic("TODO Figure out if this is also SWP", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address = modified_base;
|
||||||
|
if (W and P) cpu.r[rn] = address;
|
||||||
|
}
|
||||||
|
}.halfSignedDataTransfer;
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const util = @import("../util.zig");
|
||||||
|
const mod_cpu = @import("../cpu.zig");
|
||||||
|
|
||||||
|
const ARM7TDMI = mod_cpu.ARM7TDMI;
|
||||||
|
const InstrFn = mod_cpu.InstrFn;
|
||||||
|
const Bus = @import("../bus.zig").Bus;
|
||||||
|
|
||||||
|
pub fn comptimeSingleDataTransfer(comptime I: bool, comptime P: bool, comptime U: bool, comptime B: bool, comptime W: bool, comptime L: bool) InstrFn {
|
||||||
|
return struct {
|
||||||
|
fn singleDataTransfer(cpu: *ARM7TDMI, bus: *Bus, opcode: u32) void {
|
||||||
|
const rn = opcode >> 16 & 0xF;
|
||||||
|
const rd = opcode >> 12 & 0xF;
|
||||||
|
|
||||||
|
const base = cpu.r[rn];
|
||||||
|
const offset = if (I) opcode & 0xFFF else registerOffset(cpu, opcode);
|
||||||
|
|
||||||
|
const modified_base = if (U) base + offset else base - offset;
|
||||||
|
var address = if (P) modified_base else base;
|
||||||
|
|
||||||
|
if (L) {
|
||||||
|
if (B) {
|
||||||
|
// LDRB
|
||||||
|
cpu.r[rd] = bus.readByte(address);
|
||||||
|
} else {
|
||||||
|
// LDR
|
||||||
|
std.debug.panic("Implement LDR", .{});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (B) {
|
||||||
|
// STRB
|
||||||
|
const src = @truncate(u8, cpu.r[rd]);
|
||||||
|
|
||||||
|
bus.writeByte(address + 3, src);
|
||||||
|
bus.writeByte(address + 2, src);
|
||||||
|
bus.writeByte(address + 1, src);
|
||||||
|
bus.writeByte(address, src);
|
||||||
|
} else {
|
||||||
|
// STR
|
||||||
|
std.debug.panic("Implement STR", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address = modified_base;
|
||||||
|
if (W and P) cpu.r[rn] = address;
|
||||||
|
|
||||||
|
// TODO: W-bit forces non-privledged mode for the transfer
|
||||||
|
}
|
||||||
|
}.singleDataTransfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn registerOffset(cpu: *ARM7TDMI, opcode: u32) u32 {
|
||||||
|
const amount = opcode >> 7 & 0x1F;
|
||||||
|
const rm = opcode & 0xF;
|
||||||
|
const r_val = cpu.r[rm];
|
||||||
|
|
||||||
|
return switch (opcode >> 5 & 0x03) {
|
||||||
|
0b00 => r_val << @truncate(u5, amount),
|
||||||
|
0b01 => r_val >> @truncate(u5, amount),
|
||||||
|
0b10 => @bitCast(u32, @bitCast(i32, r_val) >> @truncate(u5, amount)),
|
||||||
|
0b11 => std.math.rotr(u32, r_val, amount),
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
const _ = @import("std");
|
||||||
|
|
||||||
|
const Scheduler = @import("scheduler.zig").Scheduler;
|
||||||
|
const ARM7TDMI = @import("cpu.zig").ARM7TDMI;
|
||||||
|
const Bus = @import("bus.zig").Bus;
|
||||||
|
|
||||||
|
const CYCLES_PER_FRAME: u64 = 10_000; // TODO: What is this?
|
||||||
|
|
||||||
|
pub fn runFrame(sch: *Scheduler, cpu: *ARM7TDMI, bus: *Bus) void {
|
||||||
|
const frame_end = sch.tick + CYCLES_PER_FRAME;
|
||||||
|
|
||||||
|
while (sch.tick < frame_end) {
|
||||||
|
while (sch.tick < sch.nextTimestamp()) {
|
||||||
|
sch.tick += cpu.step();
|
||||||
|
}
|
||||||
|
|
||||||
|
sch.handleEvent(cpu, bus);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Scheduler = @import("scheduler.zig").Scheduler;
|
||||||
|
const Bus = @import("bus.zig").Bus;
|
||||||
|
const ARM7TDMI = @import("cpu.zig").ARM7TDMI;
|
||||||
|
|
||||||
|
const emu = @import("emu.zig");
|
||||||
|
|
||||||
|
pub fn main() anyerror!void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
const alloc = gpa.allocator();
|
||||||
|
// defer gpa.deinit();
|
||||||
|
|
||||||
|
var bus = try Bus.withPak(alloc, "./bin/demo/beeg/beeg.gba");
|
||||||
|
var scheduler = Scheduler.new(alloc);
|
||||||
|
var cpu = ARM7TDMI.new(&scheduler, &bus);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
emu.runFrame(&scheduler, &cpu, &bus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "basic test" {
|
||||||
|
try std.testing.expectEqual(10, 3 + 7);
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
pub const GamePak = struct {
|
||||||
|
buf: []u8,
|
||||||
|
|
||||||
|
pub fn fromPath(alloc: Allocator, path: []const u8) !@This() {
|
||||||
|
const file = try std.fs.cwd().openFile(path, .{ .read = true });
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
const len = try file.getEndPos();
|
||||||
|
|
||||||
|
return @This(){
|
||||||
|
.buf = try file.readToEndAlloc(alloc, len),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readWord(self: *const @This(), addr: u32) u32 {
|
||||||
|
return (@as(u32, self.buf[addr + 3]) << 24) | (@as(u32, self.buf[addr + 2]) << 16) | (@as(u32, self.buf[addr + 1]) << 8) | (@as(u32, self.buf[addr]));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readHalfWord(self: *const @This(), addr: u32) u16 {
|
||||||
|
return (@as(u16, self.buf[addr + 1]) << 8) | @as(u16, self.buf[addr]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeHalfWord(self: *@This(), addr: u32, halfword: u16) void {
|
||||||
|
self.buf[addr + 1] = @truncate(u8, halfword >> 8);
|
||||||
|
self.buf[addr] = @truncate(u8, halfword);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readByte(self: *const @This(), addr: u32) u8 {
|
||||||
|
return self.buf[addr];
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,57 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const ARM7TDMI = @import("cpu.zig").ARM7TDMI;
|
||||||
|
const Bus = @import("bus.zig").Bus;
|
||||||
|
|
||||||
|
const Order = std.math.Order;
|
||||||
|
const PriorityQueue = std.PriorityQueue;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
pub const Scheduler = struct {
|
||||||
|
tick: u64,
|
||||||
|
queue: PriorityQueue(Event, void, lessThan),
|
||||||
|
|
||||||
|
pub fn new(alloc: Allocator) @This() {
|
||||||
|
var scheduler = Scheduler{ .tick = 0, .queue = PriorityQueue(Event, void, lessThan).init(alloc, {}) };
|
||||||
|
|
||||||
|
scheduler.queue.add(.{
|
||||||
|
.kind = EventKind.HeatDeath,
|
||||||
|
.tick = std.math.maxInt(u64),
|
||||||
|
}) catch unreachable;
|
||||||
|
|
||||||
|
return scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handleEvent(self: *@This(), _: *ARM7TDMI, _: *Bus) void {
|
||||||
|
const should_handle = if (self.queue.peek()) |e| self.tick >= e.tick else false;
|
||||||
|
|
||||||
|
if (should_handle) {
|
||||||
|
const event = self.queue.remove();
|
||||||
|
|
||||||
|
switch (event.kind) {
|
||||||
|
.HeatDeath => {
|
||||||
|
std.debug.panic("Somehow, a u64 overflowed", .{});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn nextTimestamp(self: *@This()) u64 {
|
||||||
|
if (self.queue.peek()) |e| {
|
||||||
|
return e.tick;
|
||||||
|
} else unreachable;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Event = struct {
|
||||||
|
kind: EventKind,
|
||||||
|
tick: u64,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn lessThan(context: void, a: Event, b: Event) Order {
|
||||||
|
_ = context;
|
||||||
|
return std.math.order(a.tick, b.tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const EventKind = enum {
|
||||||
|
HeatDeath,
|
||||||
|
};
|
|
@ -0,0 +1,7 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
|
||||||
|
pub fn u32_sign_extend(value: u32, bitSize: anytype) u32 {
|
||||||
|
const amount: u5 = 32 - bitSize;
|
||||||
|
return @bitCast(u32, @bitCast(i32, value << amount) >> amount);
|
||||||
|
}
|
Loading…
Reference in New Issue