Compare commits
2 Commits
926b6d4dd4
...
b200dd9fa1
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | b200dd9fa1 | |
Rekai Nyangadzayi Musuka | 3b3eb52c48 |
|
@ -163,9 +163,9 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
|||
0x0400_0034 => bus.ppu.aff_bg[1].writePcPd(value),
|
||||
0x0400_0038 => bus.ppu.aff_bg[1].setX(bus.ppu.dispstat.vblank.read(), value),
|
||||
0x0400_003C => bus.ppu.aff_bg[1].setY(bus.ppu.dispstat.vblank.read(), value),
|
||||
0x0400_0040 => log.debug("Wrote 0x{X:0>8} to WIN0H and WIN1H", .{value}),
|
||||
0x0400_0044 => log.debug("Wrote 0x{X:0>8} to WIN0V and WIN1V", .{value}),
|
||||
0x0400_0048 => log.debug("Wrote 0x{X:0>8} to WININ and WINOUT", .{value}),
|
||||
0x0400_0040 => bus.ppu.win.setH(value),
|
||||
0x0400_0044 => bus.ppu.win.setV(value),
|
||||
0x0400_0048 => bus.ppu.win.setIo(value),
|
||||
0x0400_004C => log.debug("Wrote 0x{X:0>8} to MOSAIC", .{value}),
|
||||
0x0400_0050 => {
|
||||
bus.ppu.bldcnt.raw = @truncate(u16, value);
|
||||
|
@ -245,12 +245,12 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
|||
0x0400_003A => bus.ppu.aff_bg[1].x = @bitCast(i32, @bitCast(u32, bus.ppu.aff_bg[1].x) & 0x0000_FFFF | (@as(u32, value) << 16)),
|
||||
0x0400_003C => bus.ppu.aff_bg[1].y = @bitCast(i32, @bitCast(u32, bus.ppu.aff_bg[1].y) & 0xFFFF_0000 | value),
|
||||
0x0400_003E => bus.ppu.aff_bg[1].y = @bitCast(i32, @bitCast(u32, bus.ppu.aff_bg[1].y) & 0x0000_FFFF | (@as(u32, value) << 16)),
|
||||
0x0400_0040 => log.debug("Wrote 0x{X:0>4} to WIN0H", .{value}),
|
||||
0x0400_0042 => log.debug("Wrote 0x{X:0>4} to WIN1H", .{value}),
|
||||
0x0400_0044 => log.debug("Wrote 0x{X:0>4} to WIN0V", .{value}),
|
||||
0x0400_0046 => log.debug("Wrote 0x{X:0>4} to WIN1V", .{value}),
|
||||
0x0400_0048 => log.debug("Wrote 0x{X:0>4} to WININ", .{value}),
|
||||
0x0400_004A => log.debug("Wrote 0x{X:0>4} to WINOUT", .{value}),
|
||||
0x0400_0040 => bus.ppu.win.h[0].raw = value,
|
||||
0x0400_0042 => bus.ppu.win.h[1].raw = value,
|
||||
0x0400_0044 => bus.ppu.win.v[0].raw = value,
|
||||
0x0400_0046 => bus.ppu.win.v[1].raw = value,
|
||||
0x0400_0048 => bus.ppu.win.in.raw = value,
|
||||
0x0400_004A => bus.ppu.win.out.raw = value,
|
||||
0x0400_004C => log.debug("Wrote 0x{X:0>4} to MOSAIC", .{value}),
|
||||
0x0400_0050 => bus.ppu.bldcnt.raw = value,
|
||||
0x0400_0052 => bus.ppu.bldalpha.raw = value,
|
||||
|
@ -302,9 +302,9 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
|||
0x0400_0009 => bus.ppu.bg[0].cnt.raw = (@as(u16, value) << 8) | (bus.ppu.bg[0].cnt.raw & 0xFF),
|
||||
0x0400_000A => bus.ppu.bg[1].cnt.raw = (bus.ppu.bg[1].cnt.raw & 0xFF00) | value,
|
||||
0x0400_000B => bus.ppu.bg[1].cnt.raw = (@as(u16, value) << 8) | (bus.ppu.bg[1].cnt.raw & 0xFF),
|
||||
0x0400_0048 => log.debug("Wrote 0x{X:0>2} to WININ_L", .{value}),
|
||||
0x0400_0049 => log.debug("Wrote 0x{X:0>2} to WININ_H", .{value}),
|
||||
0x0400_004A => log.debug("Wrote 0x{X:0>2} to WINOUT_L", .{value}),
|
||||
0x0400_0048 => bus.ppu.win.setInL(value),
|
||||
0x0400_0049 => bus.ppu.win.setInH(value),
|
||||
0x0400_004A => bus.ppu.win.setOutL(value),
|
||||
0x0400_0054 => bus.ppu.bldy.raw = (bus.ppu.bldy.raw & 0xFF00) | value,
|
||||
|
||||
// Sound
|
||||
|
@ -464,6 +464,40 @@ pub const BldY = extern union {
|
|||
raw: u16,
|
||||
};
|
||||
|
||||
/// Write-only
|
||||
pub const WinH = extern union {
|
||||
x2: Bitfield(u16, 0, 8),
|
||||
x1: Bitfield(u16, 8, 8),
|
||||
raw: u16,
|
||||
};
|
||||
|
||||
/// Write-only
|
||||
pub const WinV = extern union {
|
||||
y2: Bitfield(u16, 0, 8),
|
||||
y1: Bitfield(u16, 8, 8),
|
||||
raw: u16,
|
||||
};
|
||||
|
||||
pub const WinIn = extern union {
|
||||
w0_bg: Bitfield(u16, 0, 4),
|
||||
w0_obj: Bit(u16, 4),
|
||||
w0_colour: Bit(u16, 5),
|
||||
w1_bg: Bitfield(u16, 8, 4),
|
||||
w1_obj: Bit(u16, 12),
|
||||
w1_colour: Bit(u16, 13),
|
||||
raw: u16,
|
||||
};
|
||||
|
||||
pub const WinOut = extern union {
|
||||
out_bg: Bitfield(u16, 0, 4),
|
||||
out_obj: Bit(u16, 4),
|
||||
out_colour: Bit(u16, 5),
|
||||
obj_bg: Bitfield(u16, 8, 4),
|
||||
obj_obj: Bit(u16, 12),
|
||||
obj_colour: Bit(u16, 13),
|
||||
raw: u16,
|
||||
};
|
||||
|
||||
/// Read / Write
|
||||
const InterruptRequest = extern union {
|
||||
vblank: Bit(u16, 0),
|
||||
|
|
37
src/cpu.zig
37
src/cpu.zig
|
@ -60,6 +60,7 @@ pub const Arm7tdmi = struct {
|
|||
const Self = @This();
|
||||
|
||||
r: [16]u32,
|
||||
pipe: [2]u32,
|
||||
sched: *Scheduler,
|
||||
bus: Bus,
|
||||
cpsr: PSR,
|
||||
|
@ -82,6 +83,7 @@ pub const Arm7tdmi = struct {
|
|||
pub fn init(alloc: Allocator, sched: *Scheduler, paths: FilePaths) !Self {
|
||||
return Self{
|
||||
.r = [_]u32{0x00} ** 16,
|
||||
.pipe = [_]u32{0xF000_0000} ** 2,
|
||||
.sched = sched,
|
||||
.bus = try Bus.init(alloc, sched, paths),
|
||||
.cpsr = .{ .raw = 0x0000_001F },
|
||||
|
@ -256,14 +258,14 @@ 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);
|
||||
const opcode = self.pipe[0];
|
||||
if (enable_logging) if (self.log_file) |file| self.debug_log(file, opcode);
|
||||
|
||||
thumb_lut[thumbIdx(opcode)](self, &self.bus, opcode);
|
||||
if (self.cpsr.t.read()) {
|
||||
self.stepPipeline(u16);
|
||||
thumb_lut[thumbIdx(@truncate(u16, opcode))](self, &self.bus, @truncate(u16, opcode));
|
||||
} else {
|
||||
const opcode = self.fetch(u32);
|
||||
if (enable_logging) if (self.log_file) |file| self.debug_log(file, opcode);
|
||||
self.stepPipeline(u32);
|
||||
|
||||
if (checkCond(self.cpsr, @truncate(u4, opcode >> 28))) {
|
||||
arm_lut[armIdx(opcode)](self, &self.bus, opcode);
|
||||
|
@ -326,6 +328,27 @@ pub const Arm7tdmi = struct {
|
|||
}
|
||||
}
|
||||
|
||||
inline fn stepPipeline(self: *Self, comptime T: type) void {
|
||||
self.pipe[0] = self.pipe[1]; // Decoded Instr will be executed next
|
||||
self.pipe[1] = self.fetch(T); // Fetch new opcode to be decoded next
|
||||
}
|
||||
|
||||
pub fn reloadPipeline(self: *Self, comptime T: type) void {
|
||||
switch (T) {
|
||||
u32 => {
|
||||
self.pipe[0] = self.bus.read(u32, self.r[15]);
|
||||
self.pipe[1] = self.bus.read(u32, self.r[15] + 4);
|
||||
self.r[15] += 8;
|
||||
},
|
||||
u16 => {
|
||||
self.pipe[0] = self.bus.read(u32, self.r[15]);
|
||||
self.pipe[1] = self.bus.read(u32, self.r[15] + 2);
|
||||
self.r[15] += 4;
|
||||
},
|
||||
else => @compileError("Pipeline Reload: unsupported read width"),
|
||||
}
|
||||
}
|
||||
|
||||
inline fn fetch(self: *Self, comptime T: type) T {
|
||||
comptime std.debug.assert(T == u32 or T == u16); // Opcode may be 32-bit (ARM) or 16-bit (THUMB)
|
||||
defer self.r[15] += if (T == u32) 4 else 2;
|
||||
|
@ -461,7 +484,7 @@ pub const Arm7tdmi = struct {
|
|||
log_str = try std.fmt.bufPrint(&buf, thumb_fmt, .{ r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, c_psr, opcode });
|
||||
}
|
||||
} else {
|
||||
log_str = try std.fmt.bufPrint(&buf, arm_fmt, .{ r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, c_psr, opcode });
|
||||
log_str = try std.fmt.bufPrint(&buf, arm_fmt, .{ r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15 - 4, c_psr, opcode });
|
||||
}
|
||||
|
||||
_ = try file.writeAll(log_str);
|
||||
|
|
|
@ -9,14 +9,24 @@ const sext = @import("../../util.zig").sext;
|
|||
pub fn branch(comptime L: bool) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void {
|
||||
if (L) cpu.r[14] = cpu.r[15];
|
||||
cpu.r[15] = cpu.fakePC() +% (sext(u32, u24, opcode) << 2);
|
||||
if (L) cpu.r[14] = cpu.r[15] - 4;
|
||||
|
||||
cpu.r[15] = cpu.r[15] +% (sext(u32, u24, opcode) << 2);
|
||||
cpu.reloadPipeline(u32);
|
||||
}
|
||||
}.inner;
|
||||
}
|
||||
|
||||
pub fn branchAndExchange(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void {
|
||||
const rn = opcode & 0xF;
|
||||
cpu.cpsr.t.write(cpu.r[rn] & 1 == 1);
|
||||
cpu.r[15] = cpu.r[rn] & 0xFFFF_FFFE;
|
||||
|
||||
if (cpu.r[rn] & 1 == 1) {
|
||||
cpu.r[15] = cpu.r[rn] & ~@as(u32, 1);
|
||||
cpu.cpsr.t.set();
|
||||
cpu.reloadPipeline(u16);
|
||||
} else {
|
||||
cpu.r[15] = cpu.r[rn] & ~@as(u32, 3);
|
||||
cpu.cpsr.t.unset();
|
||||
cpu.reloadPipeline(u32);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ const expected_rate = @import("emu.zig").frame_rate;
|
|||
|
||||
const sample_rate = @import("apu.zig").host_sample_rate;
|
||||
|
||||
pub const enable_logging: bool = false;
|
||||
pub const enable_logging: bool = true;
|
||||
const is_binary: bool = false;
|
||||
const log = std.log.scoped(.GUI);
|
||||
pub const log_level = if (builtin.mode != .Debug) .info else std.log.default_level;
|
||||
|
@ -72,7 +72,7 @@ pub fn main() anyerror!void {
|
|||
var cpu = try Arm7tdmi.init(alloc, &scheduler, paths);
|
||||
defer cpu.deinit();
|
||||
cpu.bus.attach(&cpu);
|
||||
// cpu.fastBoot(); // Uncomment to skip BIOS
|
||||
cpu.fastBoot(); // Uncomment to skip BIOS
|
||||
|
||||
// Copy ROM title while Emulator still belongs to this thread
|
||||
const title = cpu.bus.pak.title;
|
||||
|
|
49
src/ppu.zig
49
src/ppu.zig
|
@ -24,6 +24,7 @@ pub const Ppu = struct {
|
|||
|
||||
// Registers
|
||||
|
||||
win: Window,
|
||||
bg: [4]Background,
|
||||
aff_bg: [2]AffineBackground,
|
||||
|
||||
|
@ -64,6 +65,7 @@ pub const Ppu = struct {
|
|||
.alloc = alloc,
|
||||
|
||||
// Registers
|
||||
.win = Window.init(),
|
||||
.bg = [_]Background{Background.init()} ** 4,
|
||||
.aff_bg = [_]AffineBackground{AffineBackground.init()} ** 2,
|
||||
.dispcnt = .{ .raw = 0x0000 },
|
||||
|
@ -771,6 +773,53 @@ const Oam = struct {
|
|||
}
|
||||
};
|
||||
|
||||
const Window = struct {
|
||||
const Self = @This();
|
||||
|
||||
h: [2]io.WinH,
|
||||
v: [2]io.WinV,
|
||||
|
||||
out: io.WinOut,
|
||||
in: io.WinIn,
|
||||
|
||||
fn init() Self {
|
||||
return .{
|
||||
.h = [_]io.WinH{.{ .raw = 0 }} ** 2,
|
||||
.v = [_]io.WinV{.{ .raw = 0 }} ** 2,
|
||||
|
||||
.out = .{ .raw = 0 },
|
||||
.in = .{ .raw = 0 },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setH(self: *Self, value: u32) void {
|
||||
self.h[0].raw = @truncate(u16, value);
|
||||
self.h[1].raw = @truncate(u16, value >> 16);
|
||||
}
|
||||
|
||||
pub fn setV(self: *Self, value: u32) void {
|
||||
self.v[0].raw = @truncate(u16, value);
|
||||
self.v[1].raw = @truncate(u16, value >> 16);
|
||||
}
|
||||
|
||||
pub fn setIo(self: *Self, value: u32) void {
|
||||
self.in.raw = @truncate(u16, value);
|
||||
self.out.raw = @truncate(u16, value >> 16);
|
||||
}
|
||||
|
||||
pub fn setInL(self: *Self, value: u8) void {
|
||||
self.in.raw = (self.in.raw & 0xFF00) | value;
|
||||
}
|
||||
|
||||
pub fn setInH(self: *Self, value: u8) void {
|
||||
self.in.raw = (self.in.raw & 0x00FF) | (@as(u16, value) << 8);
|
||||
}
|
||||
|
||||
pub fn setOutL(self: *Self, value: u8) void {
|
||||
self.out.raw = (self.out.raw & 0xFF00) | value;
|
||||
}
|
||||
};
|
||||
|
||||
const Background = struct {
|
||||
const Self = @This();
|
||||
|
||||
|
|
Loading…
Reference in New Issue