Compare commits
4 Commits
53191b0eeb
...
2c8616f610
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | 2c8616f610 | |
Rekai Nyangadzayi Musuka | 53eec5c3ff | |
Rekai Nyangadzayi Musuka | c397b7069d | |
Rekai Nyangadzayi Musuka | 9d037fdc3e |
479
src/core/cpu.zig
479
src/core/cpu.zig
|
@ -6,54 +6,227 @@ const Bit = @import("bitfield").Bit;
|
|||
const Bitfield = @import("bitfield").Bitfield;
|
||||
const Scheduler = @import("scheduler.zig").Scheduler;
|
||||
const FilePaths = @import("util.zig").FilePaths;
|
||||
const Logger = @import("util.zig").Logger;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const File = std.fs.File;
|
||||
|
||||
// ARM Instruction Groups
|
||||
const dataProcessing = @import("cpu/arm/data_processing.zig").dataProcessing;
|
||||
// ARM Instructions
|
||||
pub const arm = struct {
|
||||
pub const InstrFn = fn (*Arm7tdmi, *Bus, u32) void;
|
||||
const lut: [0x1000]InstrFn = populate();
|
||||
|
||||
const processing = @import("cpu/arm/data_processing.zig").dataProcessing;
|
||||
const psrTransfer = @import("cpu/arm/psr_transfer.zig").psrTransfer;
|
||||
const singleDataTransfer = @import("cpu/arm/single_data_transfer.zig").singleDataTransfer;
|
||||
const halfAndSignedDataTransfer = @import("cpu/arm/half_signed_data_transfer.zig").halfAndSignedDataTransfer;
|
||||
const blockDataTransfer = @import("cpu/arm/block_data_transfer.zig").blockDataTransfer;
|
||||
const transfer = @import("cpu/arm/single_data_transfer.zig").singleDataTransfer;
|
||||
const halfSignedTransfer = @import("cpu/arm/half_signed_data_transfer.zig").halfAndSignedDataTransfer;
|
||||
const blockTransfer = @import("cpu/arm/block_data_transfer.zig").blockDataTransfer;
|
||||
const branch = @import("cpu/arm/branch.zig").branch;
|
||||
const branchAndExchange = @import("cpu/arm/branch.zig").branchAndExchange;
|
||||
const armSoftwareInterrupt = @import("cpu/arm/software_interrupt.zig").armSoftwareInterrupt;
|
||||
const singleDataSwap = @import("cpu/arm/single_data_swap.zig").singleDataSwap;
|
||||
const branchExchange = @import("cpu/arm/branch.zig").branchAndExchange;
|
||||
const swi = @import("cpu/arm/software_interrupt.zig").armSoftwareInterrupt;
|
||||
const swap = @import("cpu/arm/single_data_swap.zig").singleDataSwap;
|
||||
|
||||
const multiply = @import("cpu/arm/multiply.zig").multiply;
|
||||
const multiplyLong = @import("cpu/arm/multiply.zig").multiplyLong;
|
||||
|
||||
// THUMB Instruction Groups
|
||||
const format1 = @import("cpu/thumb/data_processing.zig").format1;
|
||||
const format2 = @import("cpu/thumb/data_processing.zig").format2;
|
||||
const format3 = @import("cpu/thumb/data_processing.zig").format3;
|
||||
const format12 = @import("cpu/thumb/data_processing.zig").format12;
|
||||
const format13 = @import("cpu/thumb/data_processing.zig").format13;
|
||||
// Undefined ARM Instruction handler
|
||||
fn und(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void {
|
||||
const id = armIdx(opcode);
|
||||
cpu.panic("[CPU/Decode] ID: 0x{X:0>3} 0x{X:0>8} is an illegal opcode", .{ id, opcode });
|
||||
}
|
||||
|
||||
const format4 = @import("cpu/thumb/alu.zig").format4;
|
||||
const format5 = @import("cpu/thumb/processing_branch.zig").format5;
|
||||
fn populate() [0x1000]InstrFn {
|
||||
return comptime {
|
||||
@setEvalBranchQuota(0xE000);
|
||||
var ret = [_]InstrFn{und} ** 0x1000;
|
||||
|
||||
const format6 = @import("cpu/thumb/data_transfer.zig").format6;
|
||||
const format78 = @import("cpu/thumb/data_transfer.zig").format78;
|
||||
const format9 = @import("cpu/thumb/data_transfer.zig").format9;
|
||||
const format10 = @import("cpu/thumb/data_transfer.zig").format10;
|
||||
const format11 = @import("cpu/thumb/data_transfer.zig").format11;
|
||||
const format14 = @import("cpu/thumb/block_data_transfer.zig").format14;
|
||||
const format15 = @import("cpu/thumb/block_data_transfer.zig").format15;
|
||||
var i: usize = 0;
|
||||
while (i < ret.len) : (i += 1) {
|
||||
ret[i] = switch (@as(u2, i >> 10)) {
|
||||
0b00 => if (i == 0x121) blk: {
|
||||
break :blk branchExchange;
|
||||
} else if (i & 0xFCF == 0x009) blk: {
|
||||
const A = i >> 5 & 1 == 1;
|
||||
const S = i >> 4 & 1 == 1;
|
||||
break :blk multiply(A, S);
|
||||
} else if (i & 0xFBF == 0x109) blk: {
|
||||
const B = i >> 6 & 1 == 1;
|
||||
break :blk swap(B);
|
||||
} else if (i & 0xF8F == 0x089) blk: {
|
||||
const U = i >> 6 & 1 == 1;
|
||||
const A = i >> 5 & 1 == 1;
|
||||
const S = i >> 4 & 1 == 1;
|
||||
break :blk multiplyLong(U, A, S);
|
||||
} else if (i & 0xE49 == 0x009 or i & 0xE49 == 0x049) blk: {
|
||||
const P = i >> 8 & 1 == 1;
|
||||
const U = i >> 7 & 1 == 1;
|
||||
const I = i >> 6 & 1 == 1;
|
||||
const W = i >> 5 & 1 == 1;
|
||||
const L = i >> 4 & 1 == 1;
|
||||
break :blk halfSignedTransfer(P, U, I, W, L);
|
||||
} else if (i & 0xD90 == 0x100) blk: {
|
||||
const I = i >> 9 & 1 == 1;
|
||||
const R = i >> 6 & 1 == 1;
|
||||
const kind = i >> 4 & 0x3;
|
||||
break :blk psrTransfer(I, R, kind);
|
||||
} else blk: {
|
||||
const I = i >> 9 & 1 == 1;
|
||||
const S = i >> 4 & 1 == 1;
|
||||
const instrKind = i >> 5 & 0xF;
|
||||
break :blk processing(I, S, instrKind);
|
||||
},
|
||||
0b01 => if (i >> 9 & 1 == 1 and i & 1 == 1) und else blk: {
|
||||
const I = i >> 9 & 1 == 1;
|
||||
const P = i >> 8 & 1 == 1;
|
||||
const U = i >> 7 & 1 == 1;
|
||||
const B = i >> 6 & 1 == 1;
|
||||
const W = i >> 5 & 1 == 1;
|
||||
const L = i >> 4 & 1 == 1;
|
||||
break :blk transfer(I, P, U, B, W, L);
|
||||
},
|
||||
else => switch (@as(u2, i >> 9 & 0x3)) {
|
||||
// MSB is guaranteed to be 1
|
||||
0b00 => blk: {
|
||||
const P = i >> 8 & 1 == 1;
|
||||
const U = i >> 7 & 1 == 1;
|
||||
const S = i >> 6 & 1 == 1;
|
||||
const W = i >> 5 & 1 == 1;
|
||||
const L = i >> 4 & 1 == 1;
|
||||
break :blk blockTransfer(P, U, S, W, L);
|
||||
},
|
||||
0b01 => blk: {
|
||||
const L = i >> 8 & 1 == 1;
|
||||
break :blk branch(L);
|
||||
},
|
||||
0b10 => und, // COP Data Transfer
|
||||
0b11 => if (i >> 8 & 1 == 1) swi() else und, // COP Data Operation + Register Transfer
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const format16 = @import("cpu/thumb/branch.zig").format16;
|
||||
const format18 = @import("cpu/thumb/branch.zig").format18;
|
||||
const format19 = @import("cpu/thumb/branch.zig").format19;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const thumbSoftwareInterrupt = @import("cpu/thumb/software_interrupt.zig").thumbSoftwareInterrupt;
|
||||
// THUMB Instructions
|
||||
pub const thumb = struct {
|
||||
pub const InstrFn = fn (*Arm7tdmi, *Bus, u16) void;
|
||||
const lut: [0x400]InstrFn = populate();
|
||||
|
||||
pub const ArmInstrFn = fn (*Arm7tdmi, *Bus, u32) void;
|
||||
pub const ThumbInstrFn = fn (*Arm7tdmi, *Bus, u16) void;
|
||||
const arm_lut: [0x1000]ArmInstrFn = armPopulate();
|
||||
const thumb_lut: [0x400]ThumbInstrFn = thumbPopulate();
|
||||
const processing = @import("cpu/thumb/data_processing.zig");
|
||||
const alu = @import("cpu/thumb/alu.zig").fmt4;
|
||||
const transfer = @import("cpu/thumb/data_transfer.zig");
|
||||
const block_transfer = @import("cpu/thumb/block_data_transfer.zig");
|
||||
const swi = @import("cpu/thumb/software_interrupt.zig").fmt17;
|
||||
const branch = @import("cpu/thumb/branch.zig");
|
||||
|
||||
const enable_logging = false;
|
||||
/// Undefined THUMB Instruction Handler
|
||||
fn und(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
|
||||
const id = thumbIdx(opcode);
|
||||
cpu.panic("[CPU/Decode] ID: 0b{b:0>10} 0x{X:0>2} is an illegal opcode", .{ id, opcode });
|
||||
}
|
||||
|
||||
fn populate() [0x400]InstrFn {
|
||||
return comptime {
|
||||
@setEvalBranchQuota(5025); // This is exact
|
||||
var ret = [_]InstrFn{und} ** 0x400;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < ret.len) : (i += 1) {
|
||||
ret[i] = switch (@as(u3, i >> 7 & 0x7)) {
|
||||
0b000 => if (i >> 5 & 0x3 == 0b11) blk: {
|
||||
const I = i >> 4 & 1 == 1;
|
||||
const is_sub = i >> 3 & 1 == 1;
|
||||
const rn = i & 0x7;
|
||||
break :blk processing.fmt2(I, is_sub, rn);
|
||||
} else blk: {
|
||||
const op = i >> 5 & 0x3;
|
||||
const offset = i & 0x1F;
|
||||
break :blk processing.fmt1(op, offset);
|
||||
},
|
||||
0b001 => blk: {
|
||||
const op = i >> 5 & 0x3;
|
||||
const rd = i >> 2 & 0x7;
|
||||
break :blk processing.fmt3(op, rd);
|
||||
},
|
||||
0b010 => switch (@as(u2, i >> 5 & 0x3)) {
|
||||
0b00 => if (i >> 4 & 1 == 1) blk: {
|
||||
const op = i >> 2 & 0x3;
|
||||
const h1 = i >> 1 & 1;
|
||||
const h2 = i & 1;
|
||||
break :blk processing.fmt5(op, h1, h2);
|
||||
} else blk: {
|
||||
const op = i & 0xF;
|
||||
break :blk alu(op);
|
||||
},
|
||||
0b01 => blk: {
|
||||
const rd = i >> 2 & 0x7;
|
||||
break :blk transfer.fmt6(rd);
|
||||
},
|
||||
else => blk: {
|
||||
const op = i >> 4 & 0x3;
|
||||
const T = i >> 3 & 1 == 1;
|
||||
break :blk transfer.fmt78(op, T);
|
||||
},
|
||||
},
|
||||
0b011 => blk: {
|
||||
const B = i >> 6 & 1 == 1;
|
||||
const L = i >> 5 & 1 == 1;
|
||||
const offset = i & 0x1F;
|
||||
break :blk transfer.fmt9(B, L, offset);
|
||||
},
|
||||
else => switch (@as(u3, i >> 6 & 0x7)) {
|
||||
// MSB is guaranteed to be 1
|
||||
0b000 => blk: {
|
||||
const L = i >> 5 & 1 == 1;
|
||||
const offset = i & 0x1F;
|
||||
break :blk transfer.fmt10(L, offset);
|
||||
},
|
||||
0b001 => blk: {
|
||||
const L = i >> 5 & 1 == 1;
|
||||
const rd = i >> 2 & 0x7;
|
||||
break :blk transfer.fmt11(L, rd);
|
||||
},
|
||||
0b010 => blk: {
|
||||
const isSP = i >> 5 & 1 == 1;
|
||||
const rd = i >> 2 & 0x7;
|
||||
break :blk processing.fmt12(isSP, rd);
|
||||
},
|
||||
0b011 => if (i >> 4 & 1 == 1) blk: {
|
||||
const L = i >> 5 & 1 == 1;
|
||||
const R = i >> 2 & 1 == 1;
|
||||
break :blk block_transfer.fmt14(L, R);
|
||||
} else blk: {
|
||||
const S = i >> 1 & 1 == 1;
|
||||
break :blk processing.fmt13(S);
|
||||
},
|
||||
0b100 => blk: {
|
||||
const L = i >> 5 & 1 == 1;
|
||||
const rb = i >> 2 & 0x7;
|
||||
|
||||
break :blk block_transfer.fmt15(L, rb);
|
||||
},
|
||||
0b101 => if (i >> 2 & 0xF == 0b1111) blk: {
|
||||
break :blk thumb.swi();
|
||||
} else blk: {
|
||||
const cond = i >> 2 & 0xF;
|
||||
break :blk branch.fmt16(cond);
|
||||
},
|
||||
0b110 => branch.fmt18(),
|
||||
0b111 => blk: {
|
||||
const is_low = i >> 5 & 1 == 1;
|
||||
break :blk branch.fmt19(is_low);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const cpu_logging = @import("emu.zig").cpu_logging;
|
||||
const log = std.log.scoped(.Arm7Tdmi);
|
||||
|
||||
pub const Arm7tdmi = struct {
|
||||
|
@ -61,7 +234,7 @@ pub const Arm7tdmi = struct {
|
|||
|
||||
r: [16]u32,
|
||||
sched: *Scheduler,
|
||||
bus: Bus,
|
||||
bus: *Bus,
|
||||
cpsr: PSR,
|
||||
spsr: PSR,
|
||||
|
||||
|
@ -75,33 +248,24 @@ pub const Arm7tdmi = struct {
|
|||
|
||||
banked_spsr: [5]PSR,
|
||||
|
||||
log_file: ?*const File,
|
||||
log_buf: [0x100]u8,
|
||||
binary_log: bool,
|
||||
logger: ?Logger,
|
||||
|
||||
pub fn init(alloc: Allocator, sched: *Scheduler, paths: FilePaths) !Self {
|
||||
pub fn init(sched: *Scheduler, bus: *Bus) Self {
|
||||
return Self{
|
||||
.r = [_]u32{0x00} ** 16,
|
||||
.sched = sched,
|
||||
.bus = try Bus.init(alloc, sched, paths),
|
||||
.bus = bus,
|
||||
.cpsr = .{ .raw = 0x0000_001F },
|
||||
.spsr = .{ .raw = 0x0000_0000 },
|
||||
.banked_fiq = [_]u32{0x00} ** 10,
|
||||
.banked_r = [_]u32{0x00} ** 12,
|
||||
.banked_spsr = [_]PSR{.{ .raw = 0x0000_0000 }} ** 5,
|
||||
.log_file = null,
|
||||
.log_buf = undefined,
|
||||
.binary_log = false,
|
||||
.logger = null,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self) void {
|
||||
self.bus.deinit();
|
||||
}
|
||||
|
||||
pub fn useLogger(self: *Self, file: *const File, is_binary: bool) void {
|
||||
self.log_file = file;
|
||||
self.binary_log = is_binary;
|
||||
pub fn attach(self: *Self, log_file: std.fs.File) void {
|
||||
self.logger = Logger.init(log_file);
|
||||
}
|
||||
|
||||
inline fn bankedIdx(mode: Mode, kind: BankedKind) usize {
|
||||
|
@ -258,15 +422,15 @@ pub const Arm7tdmi = struct {
|
|||
pub fn step(self: *Self) void {
|
||||
if (self.cpsr.t.read()) {
|
||||
const opcode = self.fetch(u16);
|
||||
if (enable_logging) if (self.log_file) |file| self.debug_log(file, opcode);
|
||||
if (cpu_logging) self.logger.?.mgbaLog(self, opcode);
|
||||
|
||||
thumb_lut[thumbIdx(opcode)](self, &self.bus, opcode);
|
||||
thumb.lut[thumbIdx(opcode)](self, self.bus, opcode);
|
||||
} else {
|
||||
const opcode = self.fetch(u32);
|
||||
if (enable_logging) if (self.log_file) |file| self.debug_log(file, opcode);
|
||||
if (cpu_logging) self.logger.?.mgbaLog(self, opcode);
|
||||
|
||||
if (checkCond(self.cpsr, @truncate(u4, opcode >> 28))) {
|
||||
arm_lut[armIdx(opcode)](self, &self.bus, opcode);
|
||||
arm.lut[armIdx(opcode)](self, self.bus, opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -341,14 +505,6 @@ pub const Arm7tdmi = struct {
|
|||
return self.r[15] + 4;
|
||||
}
|
||||
|
||||
fn debug_log(self: *const Self, file: *const File, opcode: u32) void {
|
||||
if (self.binary_log) {
|
||||
self.skyLog(file) catch unreachable;
|
||||
} else {
|
||||
self.mgbaLog(file, opcode) catch unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn panic(self: *const Self, comptime format: []const u8, args: anytype) noreturn {
|
||||
var i: usize = 0;
|
||||
while (i < 16) : (i += 4) {
|
||||
|
@ -406,25 +562,6 @@ pub const Arm7tdmi = struct {
|
|||
};
|
||||
}
|
||||
|
||||
fn skyLog(self: *const Self, file: *const File) !void {
|
||||
var buf: [18 * @sizeOf(u32)]u8 = undefined;
|
||||
|
||||
// Write Registers
|
||||
var i: usize = 0;
|
||||
while (i < 0x10) : (i += 1) {
|
||||
skyWrite(&buf, i, self.r[i]);
|
||||
}
|
||||
|
||||
skyWrite(&buf, 0x10, self.cpsr.raw);
|
||||
skyWrite(&buf, 0x11, if (self.hasSPSR()) self.spsr.raw else self.cpsr.raw);
|
||||
_ = try file.writeAll(&buf);
|
||||
}
|
||||
|
||||
fn skyWrite(buf: []u8, i: usize, num: u32) void {
|
||||
const j = @sizeOf(u32) * i;
|
||||
std.mem.writeIntSliceNative(u32, buf[j..(j + @sizeOf(u32))], num);
|
||||
}
|
||||
|
||||
fn mgbaLog(self: *const Self, file: *const File, opcode: u32) !void {
|
||||
const thumb_fmt = "{X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} cpsr: {X:0>8} | {X:0>4}:\n";
|
||||
const arm_fmt = "{X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} cpsr: {X:0>8} | {X:0>8}:\n";
|
||||
|
@ -497,178 +634,6 @@ pub fn checkCond(cpsr: PSR, cond: u4) bool {
|
|||
};
|
||||
}
|
||||
|
||||
fn thumbPopulate() [0x400]ThumbInstrFn {
|
||||
return comptime {
|
||||
@setEvalBranchQuota(5025); // This is exact
|
||||
var lut = [_]ThumbInstrFn{thumbUndefined} ** 0x400;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < lut.len) : (i += 1) {
|
||||
lut[i] = switch (@as(u3, i >> 7 & 0x7)) {
|
||||
0b000 => if (i >> 5 & 0x3 == 0b11) blk: {
|
||||
const I = i >> 4 & 1 == 1;
|
||||
const is_sub = i >> 3 & 1 == 1;
|
||||
const rn = i & 0x7;
|
||||
break :blk format2(I, is_sub, rn);
|
||||
} else blk: {
|
||||
const op = i >> 5 & 0x3;
|
||||
const offset = i & 0x1F;
|
||||
break :blk format1(op, offset);
|
||||
},
|
||||
0b001 => blk: {
|
||||
const op = i >> 5 & 0x3;
|
||||
const rd = i >> 2 & 0x7;
|
||||
break :blk format3(op, rd);
|
||||
},
|
||||
0b010 => switch (@as(u2, i >> 5 & 0x3)) {
|
||||
0b00 => if (i >> 4 & 1 == 1) blk: {
|
||||
const op = i >> 2 & 0x3;
|
||||
const h1 = i >> 1 & 1;
|
||||
const h2 = i & 1;
|
||||
break :blk format5(op, h1, h2);
|
||||
} else blk: {
|
||||
const op = i & 0xF;
|
||||
break :blk format4(op);
|
||||
},
|
||||
0b01 => blk: {
|
||||
const rd = i >> 2 & 0x7;
|
||||
break :blk format6(rd);
|
||||
},
|
||||
else => blk: {
|
||||
const op = i >> 4 & 0x3;
|
||||
const T = i >> 3 & 1 == 1;
|
||||
break :blk format78(op, T);
|
||||
},
|
||||
},
|
||||
0b011 => blk: {
|
||||
const B = i >> 6 & 1 == 1;
|
||||
const L = i >> 5 & 1 == 1;
|
||||
const offset = i & 0x1F;
|
||||
break :blk format9(B, L, offset);
|
||||
},
|
||||
else => switch (@as(u3, i >> 6 & 0x7)) {
|
||||
// MSB is guaranteed to be 1
|
||||
0b000 => blk: {
|
||||
const L = i >> 5 & 1 == 1;
|
||||
const offset = i & 0x1F;
|
||||
break :blk format10(L, offset);
|
||||
},
|
||||
0b001 => blk: {
|
||||
const L = i >> 5 & 1 == 1;
|
||||
const rd = i >> 2 & 0x7;
|
||||
break :blk format11(L, rd);
|
||||
},
|
||||
0b010 => blk: {
|
||||
const isSP = i >> 5 & 1 == 1;
|
||||
const rd = i >> 2 & 0x7;
|
||||
break :blk format12(isSP, rd);
|
||||
},
|
||||
0b011 => if (i >> 4 & 1 == 1) blk: {
|
||||
const L = i >> 5 & 1 == 1;
|
||||
const R = i >> 2 & 1 == 1;
|
||||
break :blk format14(L, R);
|
||||
} else blk: {
|
||||
const S = i >> 1 & 1 == 1;
|
||||
break :blk format13(S);
|
||||
},
|
||||
0b100 => blk: {
|
||||
const L = i >> 5 & 1 == 1;
|
||||
const rb = i >> 2 & 0x7;
|
||||
|
||||
break :blk format15(L, rb);
|
||||
},
|
||||
0b101 => if (i >> 2 & 0xF == 0b1111) blk: {
|
||||
break :blk thumbSoftwareInterrupt();
|
||||
} else blk: {
|
||||
const cond = i >> 2 & 0xF;
|
||||
break :blk format16(cond);
|
||||
},
|
||||
0b110 => format18(),
|
||||
0b111 => blk: {
|
||||
const is_low = i >> 5 & 1 == 1;
|
||||
break :blk format19(is_low);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return lut;
|
||||
};
|
||||
}
|
||||
|
||||
fn armPopulate() [0x1000]ArmInstrFn {
|
||||
return comptime {
|
||||
@setEvalBranchQuota(0xE000);
|
||||
var lut = [_]ArmInstrFn{armUndefined} ** 0x1000;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < lut.len) : (i += 1) {
|
||||
lut[i] = switch (@as(u2, i >> 10)) {
|
||||
0b00 => if (i == 0x121) blk: {
|
||||
break :blk branchAndExchange;
|
||||
} else if (i & 0xFCF == 0x009) blk: {
|
||||
const A = i >> 5 & 1 == 1;
|
||||
const S = i >> 4 & 1 == 1;
|
||||
break :blk multiply(A, S);
|
||||
} else if (i & 0xFBF == 0x109) blk: {
|
||||
const B = i >> 6 & 1 == 1;
|
||||
break :blk singleDataSwap(B);
|
||||
} else if (i & 0xF8F == 0x089) blk: {
|
||||
const U = i >> 6 & 1 == 1;
|
||||
const A = i >> 5 & 1 == 1;
|
||||
const S = i >> 4 & 1 == 1;
|
||||
break :blk multiplyLong(U, A, S);
|
||||
} else if (i & 0xE49 == 0x009 or i & 0xE49 == 0x049) blk: {
|
||||
const P = i >> 8 & 1 == 1;
|
||||
const U = i >> 7 & 1 == 1;
|
||||
const I = i >> 6 & 1 == 1;
|
||||
const W = i >> 5 & 1 == 1;
|
||||
const L = i >> 4 & 1 == 1;
|
||||
break :blk halfAndSignedDataTransfer(P, U, I, W, L);
|
||||
} else if (i & 0xD90 == 0x100) blk: {
|
||||
const I = i >> 9 & 1 == 1;
|
||||
const R = i >> 6 & 1 == 1;
|
||||
const kind = i >> 4 & 0x3;
|
||||
break :blk psrTransfer(I, R, kind);
|
||||
} else blk: {
|
||||
const I = i >> 9 & 1 == 1;
|
||||
const S = i >> 4 & 1 == 1;
|
||||
const instrKind = i >> 5 & 0xF;
|
||||
break :blk dataProcessing(I, S, instrKind);
|
||||
},
|
||||
0b01 => if (i >> 9 & 1 == 1 and i & 1 == 1) armUndefined else blk: {
|
||||
const I = i >> 9 & 1 == 1;
|
||||
const P = i >> 8 & 1 == 1;
|
||||
const U = i >> 7 & 1 == 1;
|
||||
const B = i >> 6 & 1 == 1;
|
||||
const W = i >> 5 & 1 == 1;
|
||||
const L = i >> 4 & 1 == 1;
|
||||
break :blk singleDataTransfer(I, P, U, B, W, L);
|
||||
},
|
||||
else => switch (@as(u2, i >> 9 & 0x3)) {
|
||||
// MSB is guaranteed to be 1
|
||||
0b00 => blk: {
|
||||
const P = i >> 8 & 1 == 1;
|
||||
const U = i >> 7 & 1 == 1;
|
||||
const S = i >> 6 & 1 == 1;
|
||||
const W = i >> 5 & 1 == 1;
|
||||
const L = i >> 4 & 1 == 1;
|
||||
break :blk blockDataTransfer(P, U, S, W, L);
|
||||
},
|
||||
0b01 => blk: {
|
||||
const L = i >> 8 & 1 == 1;
|
||||
break :blk branch(L);
|
||||
},
|
||||
0b10 => armUndefined, // COP Data Transfer
|
||||
0b11 => if (i >> 8 & 1 == 1) armSoftwareInterrupt() else armUndefined, // COP Data Operation + Register Transfer
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return lut;
|
||||
};
|
||||
}
|
||||
|
||||
pub const PSR = extern union {
|
||||
mode: Bitfield(u32, 0, 5),
|
||||
t: Bit(u32, 5),
|
||||
|
@ -703,13 +668,3 @@ fn getMode(bits: u5) ?Mode {
|
|||
fn getModeChecked(cpu: *const Arm7tdmi, bits: u5) Mode {
|
||||
return getMode(bits) orelse cpu.panic("[CPU/CPSR] 0b{b:0>5} is an invalid CPU mode", .{bits});
|
||||
}
|
||||
|
||||
fn armUndefined(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void {
|
||||
const id = armIdx(opcode);
|
||||
cpu.panic("[CPU/Decode] ID: 0x{X:0>3} 0x{X:0>8} is an illegal opcode", .{ id, opcode });
|
||||
}
|
||||
|
||||
fn thumbUndefined(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
|
||||
const id = thumbIdx(opcode);
|
||||
cpu.panic("[CPU/Decode] ID: 0b{b:0>10} 0x{X:0>2} is an illegal opcode", .{ id, opcode });
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
const InstrFn = @import("../../cpu.zig").arm.InstrFn;
|
||||
|
||||
pub fn blockDataTransfer(comptime P: bool, comptime U: bool, comptime S: bool, comptime W: bool, comptime L: bool) InstrFn {
|
||||
return struct {
|
||||
|
|
|
@ -2,7 +2,7 @@ const std = @import("std");
|
|||
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
const InstrFn = @import("../../cpu.zig").arm.InstrFn;
|
||||
|
||||
const sext = @import("../../util.zig").sext;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
const InstrFn = @import("../../cpu.zig").arm.InstrFn;
|
||||
|
||||
const rotateRight = @import("../barrel_shifter.zig").rotateRight;
|
||||
const execute = @import("../barrel_shifter.zig").execute;
|
||||
|
|
|
@ -2,7 +2,7 @@ const std = @import("std");
|
|||
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
const InstrFn = @import("../../cpu.zig").arm.InstrFn;
|
||||
|
||||
const sext = @import("../../util.zig").sext;
|
||||
const rotr = @import("../../util.zig").rotr;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
const InstrFn = @import("../../cpu.zig").arm.InstrFn;
|
||||
|
||||
pub fn multiply(comptime A: bool, comptime S: bool) InstrFn {
|
||||
return struct {
|
||||
|
|
|
@ -2,7 +2,7 @@ const std = @import("std");
|
|||
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
const InstrFn = @import("../../cpu.zig").arm.InstrFn;
|
||||
const PSR = @import("../../cpu.zig").PSR;
|
||||
|
||||
const log = std.log.scoped(.PsrTransfer);
|
||||
|
|
|
@ -2,7 +2,7 @@ const std = @import("std");
|
|||
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
const InstrFn = @import("../../cpu.zig").arm.InstrFn;
|
||||
|
||||
const rotr = @import("../../util.zig").rotr;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ const util = @import("../../util.zig");
|
|||
const shifter = @import("../barrel_shifter.zig");
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
const InstrFn = @import("../../cpu.zig").arm.InstrFn;
|
||||
|
||||
const rotr = @import("../../util.zig").rotr;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
const InstrFn = @import("../../cpu.zig").arm.InstrFn;
|
||||
|
||||
pub fn armSoftwareInterrupt() InstrFn {
|
||||
return struct {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
|
||||
const InstrFn = @import("../../cpu.zig").thumb.InstrFn;
|
||||
|
||||
const adc = @import("../arm/data_processing.zig").adc;
|
||||
const sbc = @import("../arm/data_processing.zig").sbc;
|
||||
|
@ -15,7 +15,7 @@ const logicalRight = @import("../barrel_shifter.zig").logicalRight;
|
|||
const arithmeticRight = @import("../barrel_shifter.zig").arithmeticRight;
|
||||
const rotateRight = @import("../barrel_shifter.zig").rotateRight;
|
||||
|
||||
pub fn format4(comptime op: u4) InstrFn {
|
||||
pub fn fmt4(comptime op: u4) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
|
||||
const rs = opcode >> 3 & 0x7;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
|
||||
const InstrFn = @import("../../cpu.zig").thumb.InstrFn;
|
||||
|
||||
pub fn format14(comptime L: bool, comptime R: bool) InstrFn {
|
||||
pub fn fmt14(comptime L: bool, comptime R: bool) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
|
||||
const count = @boolToInt(R) + countRlist(opcode);
|
||||
|
@ -45,7 +45,7 @@ pub fn format14(comptime L: bool, comptime R: bool) InstrFn {
|
|||
}.inner;
|
||||
}
|
||||
|
||||
pub fn format15(comptime L: bool, comptime rb: u3) InstrFn {
|
||||
pub fn fmt15(comptime L: bool, comptime rb: u3) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
|
||||
var address = cpu.r[rb];
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
|
||||
const InstrFn = @import("../../cpu.zig").thumb.InstrFn;
|
||||
|
||||
const checkCond = @import("../../cpu.zig").checkCond;
|
||||
const sext = @import("../../util.zig").sext;
|
||||
|
||||
pub fn format16(comptime cond: u4) InstrFn {
|
||||
pub fn fmt16(comptime cond: u4) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
|
||||
// B
|
||||
|
@ -23,7 +23,7 @@ pub fn format16(comptime cond: u4) InstrFn {
|
|||
}.inner;
|
||||
}
|
||||
|
||||
pub fn format18() InstrFn {
|
||||
pub fn fmt18() InstrFn {
|
||||
return struct {
|
||||
// B but conditional
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
|
||||
|
@ -33,7 +33,7 @@ pub fn format18() InstrFn {
|
|||
}.inner;
|
||||
}
|
||||
|
||||
pub fn format19(comptime is_low: bool) InstrFn {
|
||||
pub fn fmt19(comptime is_low: bool) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
|
||||
// BL
|
||||
|
|
|
@ -2,7 +2,7 @@ const std = @import("std");
|
|||
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
|
||||
const InstrFn = @import("../../cpu.zig").thumb.InstrFn;
|
||||
const shifter = @import("../barrel_shifter.zig");
|
||||
|
||||
const add = @import("../arm/data_processing.zig").add;
|
||||
|
@ -12,7 +12,7 @@ const setLogicOpFlags = @import("../arm/data_processing.zig").setLogicOpFlags;
|
|||
|
||||
const log = std.log.scoped(.Thumb1);
|
||||
|
||||
pub fn format1(comptime op: u2, comptime offset: u5) InstrFn {
|
||||
pub fn fmt1(comptime op: u2, comptime offset: u5) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
|
||||
const rs = opcode >> 3 & 0x7;
|
||||
|
@ -55,7 +55,37 @@ pub fn format1(comptime op: u2, comptime offset: u5) InstrFn {
|
|||
}.inner;
|
||||
}
|
||||
|
||||
pub fn format2(comptime I: bool, is_sub: bool, rn: u3) InstrFn {
|
||||
pub fn fmt5(comptime op: u2, comptime h1: u1, comptime h2: u1) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
|
||||
const src_idx = @as(u4, h2) << 3 | (opcode >> 3 & 0x7);
|
||||
const dst_idx = @as(u4, h1) << 3 | (opcode & 0x7);
|
||||
|
||||
const src = if (src_idx == 0xF) (cpu.r[src_idx] + 2) & 0xFFFF_FFFE else cpu.r[src_idx];
|
||||
const dst = if (dst_idx == 0xF) (cpu.r[dst_idx] + 2) & 0xFFFF_FFFE else cpu.r[dst_idx];
|
||||
|
||||
switch (op) {
|
||||
0b00 => {
|
||||
// ADD
|
||||
const sum = add(false, cpu, dst, src);
|
||||
cpu.r[dst_idx] = if (dst_idx == 0xF) sum & 0xFFFF_FFFE else sum;
|
||||
},
|
||||
0b01 => cmp(cpu, dst, src), // CMP
|
||||
0b10 => {
|
||||
// MOV
|
||||
cpu.r[dst_idx] = if (dst_idx == 0xF) src & 0xFFFF_FFFE else src;
|
||||
},
|
||||
0b11 => {
|
||||
// BX
|
||||
cpu.cpsr.t.write(src & 1 == 1);
|
||||
cpu.r[15] = src & 0xFFFF_FFFE;
|
||||
},
|
||||
}
|
||||
}
|
||||
}.inner;
|
||||
}
|
||||
|
||||
pub fn fmt2(comptime I: bool, is_sub: bool, rn: u3) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
|
||||
const rs = opcode >> 3 & 0x7;
|
||||
|
@ -80,7 +110,7 @@ pub fn format2(comptime I: bool, is_sub: bool, rn: u3) InstrFn {
|
|||
}.inner;
|
||||
}
|
||||
|
||||
pub fn format3(comptime op: u2, comptime rd: u3) InstrFn {
|
||||
pub fn fmt3(comptime op: u2, comptime rd: u3) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
|
||||
const offset = @truncate(u8, opcode);
|
||||
|
@ -99,7 +129,7 @@ pub fn format3(comptime op: u2, comptime rd: u3) InstrFn {
|
|||
}.inner;
|
||||
}
|
||||
|
||||
pub fn format12(comptime isSP: bool, comptime rd: u3) InstrFn {
|
||||
pub fn fmt12(comptime isSP: bool, comptime rd: u3) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
|
||||
// ADD
|
||||
|
@ -111,7 +141,7 @@ pub fn format12(comptime isSP: bool, comptime rd: u3) InstrFn {
|
|||
}.inner;
|
||||
}
|
||||
|
||||
pub fn format13(comptime S: bool) InstrFn {
|
||||
pub fn fmt13(comptime S: bool) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
|
||||
// ADD
|
||||
|
|
|
@ -2,11 +2,11 @@ const std = @import("std");
|
|||
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
|
||||
const InstrFn = @import("../../cpu.zig").thumb.InstrFn;
|
||||
|
||||
const rotr = @import("../../util.zig").rotr;
|
||||
|
||||
pub fn format6(comptime rd: u3) InstrFn {
|
||||
pub fn fmt6(comptime rd: u3) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
|
||||
// LDR
|
||||
|
@ -18,7 +18,7 @@ pub fn format6(comptime rd: u3) InstrFn {
|
|||
|
||||
const sext = @import("../../util.zig").sext;
|
||||
|
||||
pub fn format78(comptime op: u2, comptime T: bool) InstrFn {
|
||||
pub fn fmt78(comptime op: u2, comptime T: bool) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
|
||||
const ro = opcode >> 6 & 0x7;
|
||||
|
@ -78,7 +78,7 @@ pub fn format78(comptime op: u2, comptime T: bool) InstrFn {
|
|||
}.inner;
|
||||
}
|
||||
|
||||
pub fn format9(comptime B: bool, comptime L: bool, comptime offset: u5) InstrFn {
|
||||
pub fn fmt9(comptime B: bool, comptime L: bool, comptime offset: u5) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
|
||||
const rb = opcode >> 3 & 0x7;
|
||||
|
@ -110,7 +110,7 @@ pub fn format9(comptime B: bool, comptime L: bool, comptime offset: u5) InstrFn
|
|||
}.inner;
|
||||
}
|
||||
|
||||
pub fn format10(comptime L: bool, comptime offset: u5) InstrFn {
|
||||
pub fn fmt10(comptime L: bool, comptime offset: u5) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
|
||||
const rb = opcode >> 3 & 0x7;
|
||||
|
@ -130,7 +130,7 @@ pub fn format10(comptime L: bool, comptime offset: u5) InstrFn {
|
|||
}.inner;
|
||||
}
|
||||
|
||||
pub fn format11(comptime L: bool, comptime rd: u3) InstrFn {
|
||||
pub fn fmt11(comptime L: bool, comptime rd: u3) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
|
||||
const offset = (opcode & 0xFF) << 2;
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
|
||||
|
||||
const cmp = @import("../arm/data_processing.zig").cmp;
|
||||
const add = @import("../arm/data_processing.zig").add;
|
||||
|
||||
pub fn format5(comptime op: u2, comptime h1: u1, comptime h2: u1) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
|
||||
const src_idx = @as(u4, h2) << 3 | (opcode >> 3 & 0x7);
|
||||
const dst_idx = @as(u4, h1) << 3 | (opcode & 0x7);
|
||||
|
||||
const src = if (src_idx == 0xF) (cpu.r[src_idx] + 2) & 0xFFFF_FFFE else cpu.r[src_idx];
|
||||
const dst = if (dst_idx == 0xF) (cpu.r[dst_idx] + 2) & 0xFFFF_FFFE else cpu.r[dst_idx];
|
||||
|
||||
switch (op) {
|
||||
0b00 => {
|
||||
// ADD
|
||||
const sum = add(false, cpu, dst, src);
|
||||
cpu.r[dst_idx] = if (dst_idx == 0xF) sum & 0xFFFF_FFFE else sum;
|
||||
},
|
||||
0b01 => cmp(cpu, dst, src), // CMP
|
||||
0b10 => {
|
||||
// MOV
|
||||
cpu.r[dst_idx] = if (dst_idx == 0xF) src & 0xFFFF_FFFE else src;
|
||||
},
|
||||
0b11 => {
|
||||
// BX
|
||||
cpu.cpsr.t.write(src & 1 == 1);
|
||||
cpu.r[15] = src & 0xFFFF_FFFE;
|
||||
},
|
||||
}
|
||||
}
|
||||
}.inner;
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
|
||||
const InstrFn = @import("../../cpu.zig").thumb.InstrFn;
|
||||
|
||||
pub fn thumbSoftwareInterrupt() InstrFn {
|
||||
pub fn fmt17() InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, _: u16) void {
|
||||
// Copy Values from Current Mode
|
||||
|
|
|
@ -14,6 +14,7 @@ const Allocator = std.mem.Allocator;
|
|||
|
||||
const sync_audio = false;
|
||||
const sync_video: RunKind = .UnlimitedFPS;
|
||||
pub const cpu_logging = false;
|
||||
|
||||
// 228 Lines which consist of 308 dots (which are 4 cycles long)
|
||||
const cycles_per_frame: u64 = 228 * (308 * 4); //280896
|
||||
|
|
|
@ -573,7 +573,7 @@ pub const Ppu = struct {
|
|||
// See if HBlank DMA is present and not enabled
|
||||
|
||||
if (!self.dispstat.vblank.read())
|
||||
pollBlankingDma(&cpu.bus, .HBlank);
|
||||
pollBlankingDma(cpu.bus, .HBlank);
|
||||
|
||||
self.dispstat.hblank.set();
|
||||
self.sched.push(.HBlank, 68 * 4 -| late);
|
||||
|
@ -615,7 +615,7 @@ pub const Ppu = struct {
|
|||
self.aff_bg[1].latchRefPoints();
|
||||
|
||||
// See if Vblank DMA is present and not enabled
|
||||
pollBlankingDma(&cpu.bus, .VBlank);
|
||||
pollBlankingDma(cpu.bus, .VBlank);
|
||||
}
|
||||
|
||||
if (scanline == 227) self.dispstat.vblank.unset();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Log2Int = std.math.Log2Int;
|
||||
const Arm7tdmi = @import("cpu.zig").Arm7tdmi;
|
||||
|
||||
// Sign-Extend value of type `T` to type `U`
|
||||
pub fn sext(comptime T: type, comptime U: type, value: T) T {
|
||||
|
@ -112,3 +113,64 @@ pub fn writeUndefined(log: anytype, comptime format: []const u8, args: anytype)
|
|||
log.warn(format, args);
|
||||
if (builtin.mode == .Debug) std.debug.panic("TODO: Implement I/O Register", .{});
|
||||
}
|
||||
|
||||
pub const Logger = struct {
|
||||
const Self = @This();
|
||||
|
||||
buf: std.io.BufferedWriter(4096 << 2, std.fs.File.Writer),
|
||||
|
||||
pub fn init(file: std.fs.File) Self {
|
||||
return .{
|
||||
.buf = .{ .unbuffered_writer = file.writer() },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn print(self: *Self, comptime format: []const u8, args: anytype) !void {
|
||||
try self.buf.writer().print(format, args);
|
||||
}
|
||||
|
||||
pub fn mgbaLog(self: *Self, arm7tdmi: *const Arm7tdmi, opcode: u32) void {
|
||||
const fmt_base = "{X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} cpsr: {X:0>8} | ";
|
||||
const thumb_fmt = fmt_base ++ "{X:0>4}:\n";
|
||||
const arm_fmt = fmt_base ++ "{X:0>8}:\n";
|
||||
|
||||
if (arm7tdmi.cpsr.t.read()) {
|
||||
if (opcode >> 11 == 0x1E) {
|
||||
// Instruction 1 of a BL Opcode, print in ARM mode
|
||||
const low = arm7tdmi.bus.debugRead(u16, arm7tdmi.r[15]);
|
||||
const bl_opcode = @as(u32, opcode) << 16 | low;
|
||||
|
||||
self.print(arm_fmt, Self.fmtArgs(arm7tdmi, bl_opcode)) catch @panic("failed to write to log file");
|
||||
} else {
|
||||
self.print(thumb_fmt, Self.fmtArgs(arm7tdmi, opcode)) catch @panic("failed to write to log file");
|
||||
}
|
||||
} else {
|
||||
self.print(arm_fmt, Self.fmtArgs(arm7tdmi, opcode)) catch @panic("failed to write to log file");
|
||||
}
|
||||
}
|
||||
|
||||
fn fmtArgs(arm7tdmi: *const Arm7tdmi, opcode: u32) FmtArgTuple {
|
||||
return .{
|
||||
arm7tdmi.r[0],
|
||||
arm7tdmi.r[1],
|
||||
arm7tdmi.r[2],
|
||||
arm7tdmi.r[3],
|
||||
arm7tdmi.r[4],
|
||||
arm7tdmi.r[5],
|
||||
arm7tdmi.r[6],
|
||||
arm7tdmi.r[7],
|
||||
arm7tdmi.r[8],
|
||||
arm7tdmi.r[9],
|
||||
arm7tdmi.r[10],
|
||||
arm7tdmi.r[11],
|
||||
arm7tdmi.r[12],
|
||||
arm7tdmi.r[13],
|
||||
arm7tdmi.r[14],
|
||||
arm7tdmi.r[15],
|
||||
arm7tdmi.cpsr.raw,
|
||||
opcode,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const FmtArgTuple = std.meta.Tuple(&.{ u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32 });
|
||||
|
|
21
src/main.zig
21
src/main.zig
|
@ -5,6 +5,7 @@ const known_folders = @import("known_folders");
|
|||
const clap = @import("clap");
|
||||
|
||||
const Gui = @import("Gui.zig");
|
||||
const Bus = @import("core/Bus.zig");
|
||||
const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi;
|
||||
const Scheduler = @import("core/scheduler.zig").Scheduler;
|
||||
const FilePaths = @import("core/util.zig").FilePaths;
|
||||
|
@ -13,6 +14,7 @@ const Allocator = std.mem.Allocator;
|
|||
const log = std.log.scoped(.CLI);
|
||||
const width = @import("core/ppu.zig").width;
|
||||
const height = @import("core/ppu.zig").height;
|
||||
const arm7tdmi_logging = @import("core/emu.zig").cpu_logging;
|
||||
pub const log_level = if (builtin.mode != .Debug) .info else std.log.default_level;
|
||||
|
||||
// TODO: Reimpl Logging
|
||||
|
@ -42,13 +44,20 @@ pub fn main() anyerror!void {
|
|||
var scheduler = Scheduler.init(allocator);
|
||||
defer scheduler.deinit();
|
||||
|
||||
var arm7tdmi = try Arm7tdmi.init(allocator, &scheduler, paths);
|
||||
arm7tdmi.bus.attach(&arm7tdmi);
|
||||
if (paths.bios == null) arm7tdmi.fastBoot();
|
||||
defer arm7tdmi.deinit();
|
||||
var bus = try Bus.init(allocator, &scheduler, paths);
|
||||
defer bus.deinit();
|
||||
|
||||
var gui = Gui.init(arm7tdmi.bus.pak.title, width, height);
|
||||
gui.initAudio(&arm7tdmi.bus.apu);
|
||||
var arm7tdmi = Arm7tdmi.init(&scheduler, &bus);
|
||||
|
||||
const log_file: ?std.fs.File = if (arm7tdmi_logging) try std.fs.cwd().createFile("zba.log", .{}) else null;
|
||||
defer if (log_file) |file| file.close();
|
||||
|
||||
if (log_file) |file| arm7tdmi.attach(file);
|
||||
bus.attach(&arm7tdmi); // TODO: Shrink Surface (only CPSR and r15?)
|
||||
if (paths.bios == null) arm7tdmi.fastBoot();
|
||||
|
||||
var gui = Gui.init(bus.pak.title, width, height);
|
||||
gui.initAudio(&bus.apu);
|
||||
defer gui.deinit();
|
||||
|
||||
try gui.run(&arm7tdmi, &scheduler);
|
||||
|
|
Loading…
Reference in New Issue