Compare commits

...

6 Commits

13 changed files with 157 additions and 33 deletions

View File

@ -27,11 +27,13 @@ const format4 = @import("cpu/thumb/format4.zig").format4;
const format5 = @import("cpu/thumb/format5.zig").format5; const format5 = @import("cpu/thumb/format5.zig").format5;
const format6 = @import("cpu/thumb/format6.zig").format6; const format6 = @import("cpu/thumb/format6.zig").format6;
const format9 = @import("cpu/thumb/format9.zig").format9; const format9 = @import("cpu/thumb/format9.zig").format9;
const format10 = @import("cpu/thumb/format10.zig").format10;
const format12 = @import("cpu/thumb/format12.zig").format12; const format12 = @import("cpu/thumb/format12.zig").format12;
const format13 = @import("cpu/thumb/format13.zig").format13; const format13 = @import("cpu/thumb/format13.zig").format13;
const format14 = @import("cpu/thumb/format14.zig").format14; const format14 = @import("cpu/thumb/format14.zig").format14;
const format15 = @import("cpu/thumb/format15.zig").format15; const format15 = @import("cpu/thumb/format15.zig").format15;
const format16 = @import("cpu/thumb/format16.zig").format16; const format16 = @import("cpu/thumb/format16.zig").format16;
const format18 = @import("cpu/thumb/format18.zig").format18;
const format19 = @import("cpu/thumb/format19.zig").format19; const format19 = @import("cpu/thumb/format19.zig").format19;
pub const ArmInstrFn = fn (*Arm7tdmi, *Bus, u32) void; pub const ArmInstrFn = fn (*Arm7tdmi, *Bus, u32) void;
@ -108,14 +110,16 @@ pub const Arm7tdmi = struct {
} }
pub inline fn hasSPSR(self: *const Self) bool { pub inline fn hasSPSR(self: *const Self) bool {
return switch (getMode(self.cpsr.mode.read())) { const mode = getMode(self.cpsr.mode.read()) orelse unreachable;
return switch (mode) {
.System, .User => false, .System, .User => false,
else => true, else => true,
}; };
} }
pub inline fn isPrivileged(self: *const Self) bool { pub inline fn isPrivileged(self: *const Self) bool {
return switch (getMode(self.cpsr.mode.read())) { const mode = getMode(self.cpsr.mode.read()) orelse unreachable;
return switch (mode) {
.User => false, .User => false,
else => true, else => true,
}; };
@ -127,11 +131,12 @@ pub const Arm7tdmi = struct {
} }
fn changeModeFromIdx(self: *Self, next: u5) void { fn changeModeFromIdx(self: *Self, next: u5) void {
self.changeMode(getMode(next)); const mode = getMode(next) orelse unreachable;
self.changeMode(mode);
} }
pub fn changeMode(self: *Self, next: Mode) void { pub fn changeMode(self: *Self, next: Mode) void {
const now = getMode(self.cpsr.mode.read()); const now = getMode(self.cpsr.mode.read()) orelse unreachable;
// Bank R8 -> r12 // Bank R8 -> r12
var r: usize = 8; var r: usize = 8;
@ -232,6 +237,33 @@ pub const Arm7tdmi = struct {
} }
} }
pub fn panic(self: *const Self, comptime format: []const u8, args: anytype) noreturn {
var i: usize = 0;
while (i < 16) : (i += 4) {
std.debug.print("R{}: 0x{X:0>8}\tR{}: 0x{X:0>8}\tR{}: 0x{X:0>8}\tR{}: 0x{X:0>8}\n", .{ i, self.r[i], i + 1, self.r[i + 1], i + 2, self.r[i + 2], i + 3, self.r[i + 3] });
}
std.debug.print("cpsr: 0x{X:0>8}\tspsr: 0x{X:0>8}\n", .{ self.cpsr.raw, self.spsr.raw });
std.debug.print("tick: {}\n\n", .{self.sched.tick});
std.debug.panic(format, args);
}
fn prettyPrintPsr(psr: *PSR) void {
std.debug.print("[", .{});
if (psr.n.read()) std.debug.print("N", .{}) else std.debug.print("-", .{});
if (psr.z.read()) std.debug.print("Z", .{}) else std.debug.print("-", .{});
if (psr.c.read()) std.debug.print("C", .{}) else std.debug.print("-", .{});
if (psr.v.read()) std.debug.print("V", .{}) else std.debug.print("-", .{});
if (psr.i.read()) std.debug.print("I", .{}) else std.debug.print("-", .{});
if (psr.f.read()) std.debug.print("F", .{}) else std.debug.print("-", .{});
if (psr.T.read()) std.debug.print("T", .{}) else std.debug.print("-", .{});
std.debug.print("|", .{});
if (getMode(psr.mode.read())) |mode| std.debug.print("{}", mode) else std.debug.print("---");
std.debug.print("]", .{});
}
fn skyLog(self: *const Self, file: *const File) !void { fn skyLog(self: *const Self, file: *const File) !void {
var buf: [18 * @sizeOf(u32)]u8 = undefined; var buf: [18 * @sizeOf(u32)]u8 = undefined;
@ -373,6 +405,13 @@ fn thumbPopulate() [0x400]ThumbInstrFn {
lut[i] = format6(rd); lut[i] = format6(rd);
} }
if (i >> 6 & 0xF == 0b1000) {
const L = i >> 5 & 1 == 1;
const offset = i & 0x1F;
lut[i] = format10(L, offset);
}
if (i >> 7 & 0x7 == 0b011) { if (i >> 7 & 0x7 == 0b011) {
const B = i >> 6 & 1 == 1; const B = i >> 6 & 1 == 1;
const L = i >> 5 & 1 == 1; const L = i >> 5 & 1 == 1;
@ -414,6 +453,10 @@ fn thumbPopulate() [0x400]ThumbInstrFn {
lut[i] = format16(cond); lut[i] = format16(cond);
} }
if (i >> 5 & 0x1F == 0b11100) {
lut[i] = format18();
}
if (i >> 6 & 0xF == 0b1111) { if (i >> 6 & 0xF == 0b1111) {
const is_low = i >> 5 & 1 == 1; const is_low = i >> 5 & 1 == 1;
@ -525,16 +568,16 @@ const Mode = enum(u5) {
System = 0b11111, System = 0b11111,
}; };
pub fn getMode(bits: u5) Mode { pub fn getMode(bits: u5) ?Mode {
return std.meta.intToEnum(Mode, bits) catch unreachable; return std.meta.intToEnum(Mode, bits) catch null;
} }
fn armUndefined(_: *Arm7tdmi, _: *Bus, opcode: u32) void { fn armUndefined(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void {
const id = armIdx(opcode); const id = armIdx(opcode);
std.debug.panic("[CPU:ARM] ID: 0x{X:0>3} 0x{X:0>8} is an illegal opcode", .{ id, opcode }); cpu.panic("[CPU:ARM] ID: 0x{X:0>3} 0x{X:0>8} is an illegal opcode", .{ id, opcode });
} }
fn thumbUndefined(_: *Arm7tdmi, _: *Bus, opcode: u16) void { fn thumbUndefined(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
const id = thumbIdx(opcode); const id = thumbIdx(opcode);
std.debug.panic("[CPU:THUMB] ID: 0b{b:0>10} 0x{X:0>2} is an illegal opcode", .{ id, opcode }); cpu.panic("[CPU:THUMB] ID: 0b{b:0>10} 0x{X:0>2} is an illegal opcode", .{ id, opcode });
} }

View File

@ -10,7 +10,7 @@ pub fn blockDataTransfer(comptime P: bool, comptime U: bool, comptime S: bool, c
const rn = opcode >> 16 & 0xF; const rn = opcode >> 16 & 0xF;
const base = cpu.r[rn]; const base = cpu.r[rn];
if (S and opcode >> 15 & 1 == 0) std.debug.panic("[CPU] TODO: STM/LDM with S set but R15 not in transfer list", .{}); if (S and opcode >> 15 & 1 == 0) cpu.panic("[CPU] TODO: STM/LDM with S set but R15 not in transfer list", .{});
var address: u32 = undefined; var address: u32 = undefined;
if (U) { if (U) {
@ -45,14 +45,14 @@ pub fn blockDataTransfer(comptime P: bool, comptime U: bool, comptime S: bool, c
fn transfer(cpu: *Arm7tdmi, bus: *Bus, i: u5, address: u32) void { fn transfer(cpu: *Arm7tdmi, bus: *Bus, i: u5, address: u32) void {
if (L) { if (L) {
cpu.r[i] = bus.read32(address); cpu.r[i] = bus.read32(address);
if (S and i == 0xF) std.debug.panic("[CPU] TODO: SPSR_<mode> is transferred to CPSR", .{}); if (S and i == 0xF) cpu.panic("[CPU] TODO: SPSR_<mode> is transferred to CPSR", .{});
} else { } else {
if (i == 0xF) { if (i == 0xF) {
if (!S) { if (!S) {
// TODO: Assure that this is Address of STM instruction + 12 // TODO: Assure that this is Address of STM instruction + 12
bus.write32(address, cpu.r[i] + (12 - 4)); bus.write32(address, cpu.r[i] + (12 - 4));
} else { } else {
std.debug.panic("[CPU] TODO: STM with S set and R15 in transfer list", .{}); cpu.panic("[CPU] TODO: STM with S set and R15 in transfer list", .{});
} }
} else { } else {
bus.write32(address, cpu.r[i]); bus.write32(address, cpu.r[i]);

View File

@ -35,7 +35,9 @@ pub fn halfAndSignedDataTransfer(comptime P: bool, comptime U: bool, comptime I:
switch (@truncate(u2, opcode >> 5)) { switch (@truncate(u2, opcode >> 5)) {
0b00 => { 0b00 => {
// SWP // SWP
std.debug.panic("[CPU] TODO: Implement SWP", .{}); const value = bus.read32(cpu.r[rn]);
const tmp = std.math.rotr(u32, value, 8 * (cpu.r[rn] & 0x3));
bus.write32(cpu.r[rm], tmp);
}, },
0b01 => { 0b01 => {
// LDRH // LDRH
@ -45,12 +47,12 @@ pub fn halfAndSignedDataTransfer(comptime P: bool, comptime U: bool, comptime I:
0b10 => { 0b10 => {
// LDRSB // LDRSB
cpu.r[rd] = util.u32SignExtend(8, @as(u32, bus.read8(address))); cpu.r[rd] = util.u32SignExtend(8, @as(u32, bus.read8(address)));
std.debug.panic("TODO: Affect the CPSR", .{}); cpu.panic("[CPU|ARM|LDRSB] TODO: Affect the CPSR", .{});
}, },
0b11 => { 0b11 => {
// LDRSH // LDRSH
cpu.r[rd] = util.u32SignExtend(16, @as(u32, bus.read16(address))); cpu.r[rd] = util.u32SignExtend(16, @as(u32, bus.read16(address)));
std.debug.panic("TODO: Affect the CPSR", .{}); cpu.panic("[CPU|ARM|LDRSH] TODO: Affect the CPSR", .{});
}, },
} }
} else { } else {

View File

@ -12,7 +12,8 @@ pub fn multiply(comptime A: bool, comptime S: bool) InstrFn {
const rs = opcode >> 8 & 0xF; const rs = opcode >> 8 & 0xF;
const rm = opcode & 0xF; const rm = opcode & 0xF;
const result = cpu.r[rm] * cpu.r[rs] + if (A) cpu.r[rn] else 0; const temp: u64 = @as(u64, cpu.r[rm]) * @as(u64, cpu.r[rs]) + if (A) cpu.r[rn] else 0;
const result = @truncate(u32, temp);
cpu.r[rd] = result; cpu.r[rd] = result;
if (S) { if (S) {

View File

@ -30,7 +30,7 @@ pub fn psrTransfer(comptime I: bool, comptime R: bool, comptime kind: u2) InstrF
if (cpu.isPrivileged()) cpu.setCpsr(fieldMask(&cpu.cpsr, field_mask, right)); if (cpu.isPrivileged()) cpu.setCpsr(fieldMask(&cpu.cpsr, field_mask, right));
} }
}, },
else => std.debug.panic("[CPU/PSR Transfer] Bits 21:220 of {X:0>8} are undefined", .{opcode}), else => cpu.panic("[CPU/PSR Transfer] Bits 21:220 of {X:0>8} are undefined", .{opcode}),
} }
} }
}.inner; }.inner;

View File

@ -17,7 +17,7 @@ pub fn format1(comptime op: u2, comptime offset: u5) InstrFn {
0b00 => shifter.logicalLeft(true, &cpu.cpsr, cpu.r[rs], offset), // LSL 0b00 => shifter.logicalLeft(true, &cpu.cpsr, cpu.r[rs], offset), // LSL
0b01 => shifter.logicalRight(true, &cpu.cpsr, cpu.r[rs], offset), // LSR 0b01 => shifter.logicalRight(true, &cpu.cpsr, cpu.r[rs], offset), // LSR
0b10 => shifter.arithmeticRight(true, &cpu.cpsr, cpu.r[rs], offset), // ASR 0b10 => shifter.arithmeticRight(true, &cpu.cpsr, cpu.r[rs], offset), // ASR
else => std.debug.panic("[CPU|THUMB|Fmt1] {} is an invalid op", .{op}), else => cpu.panic("[CPU|THUMB|Fmt1] {} is an invalid op", .{op}),
}; };
// Equivalent to an ARM MOVS // Equivalent to an ARM MOVS

View File

@ -0,0 +1,24 @@
const std = @import("std");
const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
pub fn format10(comptime L: bool, comptime offset: u5) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
const rb = opcode >> 3 & 0x7;
const rd = opcode & 0x7;
const address = cpu.r[rb] + (offset << 1);
if (L) {
// LDRH
cpu.r[rd] = bus.read16(address & 0xFFFF_FFFE);
} else {
// STRH
bus.write16(address & 0xFFFF_FFFE, @truncate(u16, cpu.r[rd]));
}
}
}.inner;
}

View File

@ -6,8 +6,8 @@ const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
pub fn format13(comptime _: bool) InstrFn { pub fn format13(comptime _: bool) InstrFn {
return struct { return struct {
fn inner(_: *Arm7tdmi, _: *Bus, _: u16) void { fn inner(cpu: *Arm7tdmi, _: *Bus, _: u16) void {
std.debug.panic("[CPU|THUMB|Fmt13] Implement Format 13 THUMB Instructions", .{}); cpu.panic("[CPU|THUMB|Fmt13] Implement Format 13 THUMB Instructions", .{});
} }
}.inner; }.inner;
} }

View File

@ -14,7 +14,7 @@ pub fn format16(comptime cond: u4) InstrFn {
const offset = u32SignExtend(8, opcode & 0xFF) << 1; const offset = u32SignExtend(8, opcode & 0xFF) << 1;
const should_execute = switch (cond) { const should_execute = switch (cond) {
0xE, 0xF => std.debug.panic("[CPU/THUMB] Undefined conditional branch with condition {}", .{cond}), 0xE, 0xF => cpu.panic("[CPU/THUMB] Undefined conditional branch with condition {}", .{cond}),
else => checkCond(cpu.cpsr, cond), else => checkCond(cpu.cpsr, cond),
}; };

View File

@ -0,0 +1,15 @@
const std = @import("std");
const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
const u32SignExtend = @import("../../util.zig").u32SignExtend;
pub fn format18() InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
const offset = u32SignExtend(11, opcode & 0x7FF) << 1;
cpu.r[15] = (cpu.r[15] + 2) +% offset;
}
}.inner;
}

View File

@ -20,7 +20,7 @@ pub fn format5(comptime op: u2, comptime h1: u1, comptime h2: u1) InstrFn {
cpu.cpsr.t.write(cpu.r[src] & 1 == 1); cpu.cpsr.t.write(cpu.r[src] & 1 == 1);
cpu.r[15] = cpu.r[src] & 0xFFFF_FFFE; cpu.r[15] = cpu.r[src] & 0xFFFF_FFFE;
}, },
else => std.debug.panic("[CPU|THUMB|Fmt5] {} is an invalid op", .{op}), else => cpu.panic("[CPU|THUMB|Fmt5] {} is an invalid op", .{op}),
} }
} }
}.inner; }.inner;

View File

@ -19,8 +19,8 @@ pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void {
} }
} }
pub fn runEmuThread(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void { pub fn runEmuThread(quit: *Atomic(bool), pause: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void {
while (!quit.load(.Unordered)) { while (!quit.load(.Unordered)) {
runFrame(sched, cpu, bus); if (!pause.load(.Unordered)) runFrame(sched, cpu, bus);
} }
} }

View File

@ -61,13 +61,14 @@ pub fn main() anyerror!void {
// Init Atomics // Init Atomics
var quit = Atomic(bool).init(false); var quit = Atomic(bool).init(false);
var pause = Atomic(bool).init(false);
// Create Emulator Thread // Create Emulator Thread
const emu_thread = try Thread.spawn(.{}, emu.runEmuThread, .{ &quit, &scheduler, &cpu, &bus }); const emu_thread = try Thread.spawn(.{}, emu.runEmuThread, .{ &quit, &pause, &scheduler, &cpu, &bus });
defer emu_thread.join(); defer emu_thread.join();
// Initialize SDL // Initialize SDL
const status = SDL.SDL_Init(SDL.SDL_INIT_VIDEO | SDL.SDL_INIT_EVENTS | SDL.SDL_INIT_AUDIO); const status = SDL.SDL_Init(SDL.SDL_INIT_VIDEO | SDL.SDL_INIT_EVENTS | SDL.SDL_INIT_AUDIO | SDL.SDL_INIT_GAMECONTROLLER);
if (status < 0) sdlPanic(); if (status < 0) sdlPanic();
defer SDL.SDL_Quit(); defer SDL.SDL_Quit();
@ -93,16 +94,52 @@ pub fn main() anyerror!void {
emu_loop: while (true) { emu_loop: while (true) {
var event: SDL.SDL_Event = undefined; var event: SDL.SDL_Event = undefined;
_ = SDL.SDL_PollEvent(&event); if (SDL.SDL_PollEvent(&event) != 0) {
// Pause Emulation Thread during Input Writing
pause.store(true, .Unordered);
switch (event.type) { switch (event.type) {
SDL.SDL_QUIT => break :emu_loop, SDL.SDL_QUIT => break :emu_loop,
else => {}, SDL.SDL_KEYDOWN => {
const key_code = event.key.keysym.sym;
switch (key_code) {
SDL.SDLK_UP => bus.io.keyinput.up.unset(),
SDL.SDLK_DOWN => bus.io.keyinput.down.unset(),
SDL.SDLK_LEFT => bus.io.keyinput.left.unset(),
SDL.SDLK_RIGHT => bus.io.keyinput.right.unset(),
SDL.SDLK_x => bus.io.keyinput.a.unset(),
SDL.SDLK_z => bus.io.keyinput.b.unset(),
SDL.SDLK_a => bus.io.keyinput.shoulder_l.unset(),
SDL.SDLK_s => bus.io.keyinput.shoulder_r.unset(),
SDL.SDLK_RETURN => bus.io.keyinput.start.unset(),
SDL.SDLK_RSHIFT => bus.io.keyinput.select.unset(),
else => {},
}
},
SDL.SDL_KEYUP => {
const key_code = event.key.keysym.sym;
switch (key_code) {
SDL.SDLK_UP => bus.io.keyinput.up.set(),
SDL.SDLK_DOWN => bus.io.keyinput.down.set(),
SDL.SDLK_LEFT => bus.io.keyinput.left.set(),
SDL.SDLK_RIGHT => bus.io.keyinput.right.set(),
SDL.SDLK_x => bus.io.keyinput.a.set(),
SDL.SDLK_z => bus.io.keyinput.b.set(),
SDL.SDLK_a => bus.io.keyinput.shoulder_l.set(),
SDL.SDLK_s => bus.io.keyinput.shoulder_r.set(),
SDL.SDLK_RETURN => bus.io.keyinput.start.set(),
SDL.SDLK_RSHIFT => bus.io.keyinput.select.set(),
else => {},
}
},
else => {},
}
} }
// TODO: Make this Thread Safe // FIXME: Is it OK just to copy the Emulator's Frame Buffer to SDL?
const buf_ptr = bus.ppu.frame_buf.ptr; const buf_ptr = bus.ppu.frame_buf.ptr;
_ = 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);
@ -110,6 +147,8 @@ pub fn main() anyerror!void {
// const fps = std.time.ns_per_s / timer.lap(); // const fps = std.time.ns_per_s / timer.lap();
// const title = std.fmt.bufPrint(&title_buf, "ZBA FPS: {d}", .{fps}) catch unreachable; // const title = std.fmt.bufPrint(&title_buf, "ZBA FPS: {d}", .{fps}) catch unreachable;
// SDL.SDL_SetWindowTitle(window, title.ptr); // SDL.SDL_SetWindowTitle(window, title.ptr);
pause.store(false, .Unordered);
} }
quit.store(true, .Unordered); // Terminate Emulator Thread quit.store(true, .Unordered); // Terminate Emulator Thread