feat: implement ARM read open bus
This commit is contained in:
parent
40f3600de2
commit
c03c142b14
35
src/Bus.zig
35
src/Bus.zig
|
@ -1,6 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const AudioDeviceId = @import("sdl2").SDL_AudioDeviceID;
|
const AudioDeviceId = @import("sdl2").SDL_AudioDeviceID;
|
||||||
|
const Arm7tdmi = @import("cpu.zig").Arm7tdmi;
|
||||||
const Bios = @import("bus/Bios.zig");
|
const Bios = @import("bus/Bios.zig");
|
||||||
const Ewram = @import("bus/Ewram.zig");
|
const Ewram = @import("bus/Ewram.zig");
|
||||||
const GamePak = @import("bus/GamePak.zig");
|
const GamePak = @import("bus/GamePak.zig");
|
||||||
|
@ -18,7 +19,6 @@ const Allocator = std.mem.Allocator;
|
||||||
const log = std.log.scoped(.Bus);
|
const log = std.log.scoped(.Bus);
|
||||||
|
|
||||||
const rotr = @import("util.zig").rotr;
|
const rotr = @import("util.zig").rotr;
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
const panic_on_und_bus: bool = false;
|
const panic_on_und_bus: bool = false;
|
||||||
|
@ -33,19 +33,21 @@ iwram: Iwram,
|
||||||
ewram: Ewram,
|
ewram: Ewram,
|
||||||
io: Io,
|
io: Io,
|
||||||
|
|
||||||
|
cpu: ?*Arm7tdmi,
|
||||||
sched: *Scheduler,
|
sched: *Scheduler,
|
||||||
|
|
||||||
pub fn init(alloc: Allocator, sched: *Scheduler, dev: AudioDeviceId, paths: FilePaths) !Self {
|
pub fn init(alloc: Allocator, sched: *Scheduler, paths: FilePaths) !Self {
|
||||||
return Self{
|
return Self{
|
||||||
.pak = try GamePak.init(alloc, paths.rom, paths.save),
|
.pak = try GamePak.init(alloc, paths.rom, paths.save),
|
||||||
.bios = try Bios.init(alloc, paths.bios),
|
.bios = try Bios.init(alloc, paths.bios),
|
||||||
.ppu = try Ppu.init(alloc, sched),
|
.ppu = try Ppu.init(alloc, sched),
|
||||||
.apu = Apu.init(dev),
|
.apu = Apu.init(),
|
||||||
.iwram = try Iwram.init(alloc),
|
.iwram = try Iwram.init(alloc),
|
||||||
.ewram = try Ewram.init(alloc),
|
.ewram = try Ewram.init(alloc),
|
||||||
.dma = DmaControllers.init(),
|
.dma = DmaControllers.init(),
|
||||||
.tim = Timers.init(sched),
|
.tim = Timers.init(sched),
|
||||||
.io = Io.init(),
|
.io = Io.init(),
|
||||||
|
.cpu = null,
|
||||||
.sched = sched,
|
.sched = sched,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -74,6 +76,29 @@ fn isDmaRunning(self: *const Self) bool {
|
||||||
self.dma._3.active;
|
self.dma._3.active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn debugRead(self: *const Self, comptime T: type, address: u32) T {
|
||||||
|
const cached = self.sched.tick;
|
||||||
|
defer self.sched.tick = cached;
|
||||||
|
|
||||||
|
return self.read(T, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readOpenBus(self: *const Self, comptime T: type, address: u32) T {
|
||||||
|
if (self.cpu.?.cpsr.t.read()) {
|
||||||
|
log.err("TODO: {} open bus read in THUMB", .{T});
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const word = self.debugRead(u32, self.cpu.?.r[15] + 4);
|
||||||
|
return @truncate(T, rotr(u32, word, 8 * (address & 3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readBios(self: *const Self, comptime T: type, address: u32) T {
|
||||||
|
if (address < Bios.size) return self.bios.read(T, alignAddress(T, address));
|
||||||
|
|
||||||
|
return self.readOpenBus(T, address);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read(self: *const Self, comptime T: type, address: u32) T {
|
pub fn read(self: *const Self, comptime T: type, address: u32) T {
|
||||||
const page = @truncate(u8, address >> 24);
|
const page = @truncate(u8, address >> 24);
|
||||||
const align_addr = alignAddress(T, address);
|
const align_addr = alignAddress(T, address);
|
||||||
|
@ -81,7 +106,7 @@ pub fn read(self: *const Self, comptime T: type, address: u32) T {
|
||||||
|
|
||||||
return switch (page) {
|
return switch (page) {
|
||||||
// General Internal Memory
|
// General Internal Memory
|
||||||
0x00 => self.bios.read(T, align_addr),
|
0x00 => self.readBios(T, address),
|
||||||
0x02 => self.ewram.read(T, align_addr),
|
0x02 => self.ewram.read(T, align_addr),
|
||||||
0x03 => self.iwram.read(T, align_addr),
|
0x03 => self.iwram.read(T, align_addr),
|
||||||
0x04 => io.read(self, T, align_addr),
|
0x04 => io.read(self, T, align_addr),
|
||||||
|
@ -105,7 +130,7 @@ pub fn read(self: *const Self, comptime T: type, address: u32) T {
|
||||||
|
|
||||||
break :blk @as(T, value) * multiplier;
|
break :blk @as(T, value) * multiplier;
|
||||||
},
|
},
|
||||||
else => undRead("Tried to read {} from 0x{X:0>8}", .{ T, address }),
|
else => readOpenBus(self, T, address),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
12
src/apu.zig
12
src/apu.zig
|
@ -24,9 +24,9 @@ pub const Apu = struct {
|
||||||
dma_cnt: io.DmaSoundControl,
|
dma_cnt: io.DmaSoundControl,
|
||||||
cnt: io.SoundControl,
|
cnt: io.SoundControl,
|
||||||
|
|
||||||
dev: AudioDeviceId,
|
dev: ?AudioDeviceId,
|
||||||
|
|
||||||
pub fn init(dev: AudioDeviceId) Self {
|
pub fn init() Self {
|
||||||
return .{
|
return .{
|
||||||
.ch1 = ToneSweep.init(),
|
.ch1 = ToneSweep.init(),
|
||||||
.ch2 = Tone.init(),
|
.ch2 = Tone.init(),
|
||||||
|
@ -40,10 +40,14 @@ pub const Apu = struct {
|
||||||
.cnt = .{ .raw = 0 },
|
.cnt = .{ .raw = 0 },
|
||||||
.bias = .{ .raw = 0x0200 },
|
.bias = .{ .raw = 0x0200 },
|
||||||
|
|
||||||
.dev = dev,
|
.dev = null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn attachAudioDevice(self: *Self, dev: AudioDeviceId) void {
|
||||||
|
self.dev = dev;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn setDmaCnt(self: *Self, value: u16) void {
|
pub fn setDmaCnt(self: *Self, value: u16) void {
|
||||||
const new: io.DmaSoundControl = .{ .raw = value };
|
const new: io.DmaSoundControl = .{ .raw = value };
|
||||||
|
|
||||||
|
@ -83,7 +87,7 @@ pub const Apu = struct {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
_ = SDL.SDL_QueueAudio(self.dev, &samples, 2);
|
if (self.dev) |dev| _ = SDL.SDL_QueueAudio(dev, &samples, 2);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,9 @@ const std = @import("std");
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const log = std.log.scoped(.Bios);
|
const log = std.log.scoped(.Bios);
|
||||||
|
|
||||||
|
/// Size of the BIOS in bytes
|
||||||
|
pub const size = 0x4000;
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
buf: ?[]u8,
|
buf: ?[]u8,
|
||||||
|
|
20
src/cpu.zig
20
src/cpu.zig
|
@ -5,7 +5,9 @@ const Bus = @import("Bus.zig");
|
||||||
const Bit = @import("bitfield").Bit;
|
const Bit = @import("bitfield").Bit;
|
||||||
const Bitfield = @import("bitfield").Bitfield;
|
const Bitfield = @import("bitfield").Bitfield;
|
||||||
const Scheduler = @import("scheduler.zig").Scheduler;
|
const Scheduler = @import("scheduler.zig").Scheduler;
|
||||||
|
const FilePaths = @import("util.zig").FilePaths;
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
const File = std.fs.File;
|
const File = std.fs.File;
|
||||||
|
|
||||||
// ARM Instruction Groups
|
// ARM Instruction Groups
|
||||||
|
@ -59,7 +61,7 @@ pub const Arm7tdmi = struct {
|
||||||
|
|
||||||
r: [16]u32,
|
r: [16]u32,
|
||||||
sched: *Scheduler,
|
sched: *Scheduler,
|
||||||
bus: *Bus,
|
bus: Bus,
|
||||||
cpsr: PSR,
|
cpsr: PSR,
|
||||||
spsr: PSR,
|
spsr: PSR,
|
||||||
|
|
||||||
|
@ -77,11 +79,11 @@ pub const Arm7tdmi = struct {
|
||||||
log_buf: [0x100]u8,
|
log_buf: [0x100]u8,
|
||||||
binary_log: bool,
|
binary_log: bool,
|
||||||
|
|
||||||
pub fn init(sched: *Scheduler, bus: *Bus) Self {
|
pub fn init(alloc: Allocator, sched: *Scheduler, paths: FilePaths) !Self {
|
||||||
return .{
|
var cpu: Arm7tdmi = .{
|
||||||
.r = [_]u32{0x00} ** 16,
|
.r = [_]u32{0x00} ** 16,
|
||||||
.sched = sched,
|
.sched = sched,
|
||||||
.bus = bus,
|
.bus = try Bus.init(alloc, sched, paths),
|
||||||
.cpsr = .{ .raw = 0x0000_001F },
|
.cpsr = .{ .raw = 0x0000_001F },
|
||||||
.spsr = .{ .raw = 0x0000_0000 },
|
.spsr = .{ .raw = 0x0000_0000 },
|
||||||
.banked_fiq = [_]u32{0x00} ** 10,
|
.banked_fiq = [_]u32{0x00} ** 10,
|
||||||
|
@ -91,6 +93,12 @@ pub const Arm7tdmi = struct {
|
||||||
.log_buf = undefined,
|
.log_buf = undefined,
|
||||||
.binary_log = false,
|
.binary_log = false,
|
||||||
};
|
};
|
||||||
|
cpu.bus.cpu = &cpu;
|
||||||
|
return cpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: Self) void {
|
||||||
|
self.bus.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn useLogger(self: *Self, file: *const File, is_binary: bool) void {
|
pub fn useLogger(self: *Self, file: *const File, is_binary: bool) void {
|
||||||
|
@ -250,13 +258,13 @@ pub const Arm7tdmi = struct {
|
||||||
const opcode = self.thumbFetch();
|
const opcode = self.thumbFetch();
|
||||||
if (enable_logging) if (self.log_file) |file| self.debug_log(file, opcode);
|
if (enable_logging) if (self.log_file) |file| self.debug_log(file, opcode);
|
||||||
|
|
||||||
thumb_lut[thumbIdx(opcode)](self, self.bus, opcode);
|
thumb_lut[thumbIdx(opcode)](self, &self.bus, opcode);
|
||||||
} else {
|
} else {
|
||||||
const opcode = self.fetch();
|
const opcode = self.fetch();
|
||||||
if (enable_logging) if (self.log_file) |file| self.debug_log(file, opcode);
|
if (enable_logging) if (self.log_file) |file| self.debug_log(file, opcode);
|
||||||
|
|
||||||
if (checkCond(self.cpsr, @truncate(u4, opcode >> 28))) {
|
if (checkCond(self.cpsr, @truncate(u4, opcode >> 28))) {
|
||||||
arm_lut[armIdx(opcode)](self, self.bus, opcode);
|
arm_lut[armIdx(opcode)](self, &self.bus, opcode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
42
src/emu.zig
42
src/emu.zig
|
@ -32,42 +32,42 @@ const RunKind = enum {
|
||||||
LimitedBusy,
|
LimitedBusy,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(kind: RunKind, quit: *Atomic(bool), fps: *FpsAverage, sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void {
|
pub fn run(kind: RunKind, quit: *Atomic(bool), fps: *FpsAverage, sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
.Unlimited => runUnsync(quit, sched, cpu, bus),
|
.Unlimited => runUnsync(quit, sched, cpu),
|
||||||
.Limited => runSync(quit, sched, cpu, bus),
|
.Limited => runSync(quit, sched, cpu),
|
||||||
.UnlimitedFPS => runUnsyncFps(quit, fps, sched, cpu, bus),
|
.UnlimitedFPS => runUnsyncFps(quit, fps, sched, cpu),
|
||||||
.LimitedFPS => runSyncFps(quit, fps, sched, cpu, bus),
|
.LimitedFPS => runSyncFps(quit, fps, sched, cpu),
|
||||||
.LimitedBusy => runBusyLoop(quit, sched, cpu, bus),
|
.LimitedBusy => runBusyLoop(quit, sched, cpu),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void {
|
pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
const frame_end = sched.tick + cycles_per_frame;
|
const frame_end = sched.tick + cycles_per_frame;
|
||||||
|
|
||||||
while (sched.tick < frame_end) {
|
while (sched.tick < frame_end) {
|
||||||
if (bus.io.haltcnt == .Halt) sched.tick += 1;
|
if (cpu.bus.io.haltcnt == .Halt) sched.tick += 1;
|
||||||
if (bus.io.haltcnt == .Execute) cpu.step();
|
if (cpu.bus.io.haltcnt == .Execute) cpu.step();
|
||||||
bus.handleDMATransfers();
|
cpu.bus.handleDMATransfers();
|
||||||
|
|
||||||
while (sched.tick >= sched.nextTimestamp()) {
|
while (sched.tick >= sched.nextTimestamp()) {
|
||||||
sched.handleEvent(cpu, bus);
|
sched.handleEvent(cpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runUnsync(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void {
|
pub fn runUnsync(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
log.info("Unsynchronized EmuThread has begun", .{});
|
log.info("Unsynchronized EmuThread has begun", .{});
|
||||||
while (!quit.load(.Unordered)) runFrame(sched, cpu, bus);
|
while (!quit.load(.Unordered)) runFrame(sched, cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runSync(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void {
|
pub fn runSync(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
log.info("Synchronized EmuThread has begun", .{});
|
log.info("Synchronized EmuThread has begun", .{});
|
||||||
var timer = Timer.start() catch unreachable;
|
var timer = Timer.start() catch unreachable;
|
||||||
var wake_time: u64 = frame_period;
|
var wake_time: u64 = frame_period;
|
||||||
|
|
||||||
while (!quit.load(.Unordered)) {
|
while (!quit.load(.Unordered)) {
|
||||||
runFrame(sched, cpu, bus);
|
runFrame(sched, cpu);
|
||||||
|
|
||||||
// Put the Thread to Sleep + Backup Spin Loop
|
// Put the Thread to Sleep + Backup Spin Loop
|
||||||
// This saves on resource usage when frame limiting
|
// This saves on resource usage when frame limiting
|
||||||
|
@ -78,24 +78,24 @@ pub fn runSync(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runUnsyncFps(quit: *Atomic(bool), fps: *FpsAverage, sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void {
|
pub fn runUnsyncFps(quit: *Atomic(bool), fps: *FpsAverage, sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
log.info("Unsynchronized EmuThread with FPS Tracking has begun", .{});
|
log.info("Unsynchronized EmuThread with FPS Tracking has begun", .{});
|
||||||
var fps_timer = Timer.start() catch unreachable;
|
var fps_timer = Timer.start() catch unreachable;
|
||||||
|
|
||||||
while (!quit.load(.Unordered)) {
|
while (!quit.load(.Unordered)) {
|
||||||
runFrame(sched, cpu, bus);
|
runFrame(sched, cpu);
|
||||||
fps.add(fps_timer.lap());
|
fps.add(fps_timer.lap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runSyncFps(quit: *Atomic(bool), fps: *FpsAverage, sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void {
|
pub fn runSyncFps(quit: *Atomic(bool), fps: *FpsAverage, sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
log.info("Synchronized EmuThread has begun", .{});
|
log.info("Synchronized EmuThread has begun", .{});
|
||||||
var timer = Timer.start() catch unreachable;
|
var timer = Timer.start() catch unreachable;
|
||||||
var fps_timer = Timer.start() catch unreachable;
|
var fps_timer = Timer.start() catch unreachable;
|
||||||
var wake_time: u64 = frame_period;
|
var wake_time: u64 = frame_period;
|
||||||
|
|
||||||
while (!quit.load(.Unordered)) {
|
while (!quit.load(.Unordered)) {
|
||||||
runFrame(sched, cpu, bus);
|
runFrame(sched, cpu);
|
||||||
|
|
||||||
// Put the Thread to Sleep + Backup Spin Loop
|
// Put the Thread to Sleep + Backup Spin Loop
|
||||||
// This saves on resource usage when frame limiting
|
// This saves on resource usage when frame limiting
|
||||||
|
@ -109,13 +109,13 @@ pub fn runSyncFps(quit: *Atomic(bool), fps: *FpsAverage, sched: *Scheduler, cpu:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runBusyLoop(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void {
|
pub fn runBusyLoop(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
log.info("Run EmuThread with spin-loop sync", .{});
|
log.info("Run EmuThread with spin-loop sync", .{});
|
||||||
var timer = Timer.start() catch unreachable;
|
var timer = Timer.start() catch unreachable;
|
||||||
var wake_time: u64 = frame_period;
|
var wake_time: u64 = frame_period;
|
||||||
|
|
||||||
while (!quit.load(.Unordered)) {
|
while (!quit.load(.Unordered)) {
|
||||||
runFrame(sched, cpu, bus);
|
runFrame(sched, cpu);
|
||||||
spinLoop(&timer, wake_time);
|
spinLoop(&timer, wake_time);
|
||||||
|
|
||||||
// Update to the new wake time
|
// Update to the new wake time
|
||||||
|
|
54
src/main.zig
54
src/main.zig
|
@ -92,10 +92,10 @@ pub fn main() anyerror!void {
|
||||||
defer scheduler.deinit();
|
defer scheduler.deinit();
|
||||||
|
|
||||||
const paths = .{ .bios = bios_path, .rom = rom_path, .save = save_path };
|
const paths = .{ .bios = bios_path, .rom = rom_path, .save = save_path };
|
||||||
var bus = try Bus.init(alloc, &scheduler, audio_dev, paths);
|
var cpu = try Arm7tdmi.init(alloc, &scheduler, paths);
|
||||||
defer bus.deinit();
|
defer cpu.deinit();
|
||||||
|
|
||||||
var cpu = Arm7tdmi.init(&scheduler, &bus);
|
cpu.bus.apu.attachAudioDevice(audio_dev);
|
||||||
cpu.fastBoot();
|
cpu.fastBoot();
|
||||||
|
|
||||||
const log_file: ?File = if (enable_logging) blk: {
|
const log_file: ?File = if (enable_logging) blk: {
|
||||||
|
@ -110,10 +110,10 @@ pub fn main() anyerror!void {
|
||||||
var emu_rate = FpsAverage.init();
|
var emu_rate = FpsAverage.init();
|
||||||
|
|
||||||
// Create Emulator Thread
|
// Create Emulator Thread
|
||||||
const emu_thread = try Thread.spawn(.{}, emu.run, .{ .LimitedFPS, &quit, &emu_rate, &scheduler, &cpu, &bus });
|
const emu_thread = try Thread.spawn(.{}, emu.run, .{ .LimitedFPS, &quit, &emu_rate, &scheduler, &cpu });
|
||||||
defer emu_thread.join();
|
defer emu_thread.join();
|
||||||
|
|
||||||
const title = correctTitle(bus.pak.title);
|
const title = correctTitle(cpu.bus.pak.title);
|
||||||
|
|
||||||
var title_buf: [0x20]u8 = std.mem.zeroes([0x20]u8);
|
var title_buf: [0x20]u8 = std.mem.zeroes([0x20]u8);
|
||||||
const window_title = try std.fmt.bufPrint(&title_buf, "ZBA | {s}", .{title});
|
const window_title = try std.fmt.bufPrint(&title_buf, "ZBA | {s}", .{title});
|
||||||
|
@ -145,36 +145,38 @@ pub fn main() anyerror!void {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
SDL.SDL_QUIT => break :emu_loop,
|
SDL.SDL_QUIT => break :emu_loop,
|
||||||
SDL.SDL_KEYDOWN => {
|
SDL.SDL_KEYDOWN => {
|
||||||
|
const io = &cpu.bus.io;
|
||||||
const key_code = event.key.keysym.sym;
|
const key_code = event.key.keysym.sym;
|
||||||
|
|
||||||
switch (key_code) {
|
switch (key_code) {
|
||||||
SDL.SDLK_UP => bus.io.keyinput.up.unset(),
|
SDL.SDLK_UP => io.keyinput.up.unset(),
|
||||||
SDL.SDLK_DOWN => bus.io.keyinput.down.unset(),
|
SDL.SDLK_DOWN => io.keyinput.down.unset(),
|
||||||
SDL.SDLK_LEFT => bus.io.keyinput.left.unset(),
|
SDL.SDLK_LEFT => io.keyinput.left.unset(),
|
||||||
SDL.SDLK_RIGHT => bus.io.keyinput.right.unset(),
|
SDL.SDLK_RIGHT => io.keyinput.right.unset(),
|
||||||
SDL.SDLK_x => bus.io.keyinput.a.unset(),
|
SDL.SDLK_x => io.keyinput.a.unset(),
|
||||||
SDL.SDLK_z => bus.io.keyinput.b.unset(),
|
SDL.SDLK_z => io.keyinput.b.unset(),
|
||||||
SDL.SDLK_a => bus.io.keyinput.shoulder_l.unset(),
|
SDL.SDLK_a => io.keyinput.shoulder_l.unset(),
|
||||||
SDL.SDLK_s => bus.io.keyinput.shoulder_r.unset(),
|
SDL.SDLK_s => io.keyinput.shoulder_r.unset(),
|
||||||
SDL.SDLK_RETURN => bus.io.keyinput.start.unset(),
|
SDL.SDLK_RETURN => io.keyinput.start.unset(),
|
||||||
SDL.SDLK_RSHIFT => bus.io.keyinput.select.unset(),
|
SDL.SDLK_RSHIFT => io.keyinput.select.unset(),
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
SDL.SDL_KEYUP => {
|
SDL.SDL_KEYUP => {
|
||||||
|
const io = &cpu.bus.io;
|
||||||
const key_code = event.key.keysym.sym;
|
const key_code = event.key.keysym.sym;
|
||||||
|
|
||||||
switch (key_code) {
|
switch (key_code) {
|
||||||
SDL.SDLK_UP => bus.io.keyinput.up.set(),
|
SDL.SDLK_UP => io.keyinput.up.set(),
|
||||||
SDL.SDLK_DOWN => bus.io.keyinput.down.set(),
|
SDL.SDLK_DOWN => io.keyinput.down.set(),
|
||||||
SDL.SDLK_LEFT => bus.io.keyinput.left.set(),
|
SDL.SDLK_LEFT => io.keyinput.left.set(),
|
||||||
SDL.SDLK_RIGHT => bus.io.keyinput.right.set(),
|
SDL.SDLK_RIGHT => io.keyinput.right.set(),
|
||||||
SDL.SDLK_x => bus.io.keyinput.a.set(),
|
SDL.SDLK_x => io.keyinput.a.set(),
|
||||||
SDL.SDLK_z => bus.io.keyinput.b.set(),
|
SDL.SDLK_z => io.keyinput.b.set(),
|
||||||
SDL.SDLK_a => bus.io.keyinput.shoulder_l.set(),
|
SDL.SDLK_a => io.keyinput.shoulder_l.set(),
|
||||||
SDL.SDLK_s => bus.io.keyinput.shoulder_r.set(),
|
SDL.SDLK_s => io.keyinput.shoulder_r.set(),
|
||||||
SDL.SDLK_RETURN => bus.io.keyinput.start.set(),
|
SDL.SDLK_RETURN => io.keyinput.start.set(),
|
||||||
SDL.SDLK_RSHIFT => bus.io.keyinput.select.set(),
|
SDL.SDLK_RSHIFT => io.keyinput.select.set(),
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -183,7 +185,7 @@ pub fn main() anyerror!void {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Is it OK just to copy the Emulator's Frame Buffer to SDL?
|
// FIXME: Is it OK just to copy the Emulator's Frame Buffer to SDL?
|
||||||
const buf_ptr = bus.ppu.framebuf.ptr;
|
const buf_ptr = cpu.bus.ppu.framebuf.ptr;
|
||||||
_ = SDL.SDL_UpdateTexture(texture, null, buf_ptr, framebuf_pitch);
|
_ = SDL.SDL_UpdateTexture(texture, null, buf_ptr, framebuf_pitch);
|
||||||
_ = SDL.SDL_RenderCopy(renderer, texture, null, null);
|
_ = SDL.SDL_RenderCopy(renderer, texture, null, null);
|
||||||
SDL.SDL_RenderPresent(renderer);
|
SDL.SDL_RenderPresent(renderer);
|
||||||
|
|
|
@ -366,7 +366,7 @@ pub const Ppu = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if HBlank DMA is present and not enabled
|
// See if HBlank DMA is present and not enabled
|
||||||
pollBlankingDma(cpu.bus, .HBlank);
|
pollBlankingDma(&cpu.bus, .HBlank);
|
||||||
|
|
||||||
self.dispstat.hblank.set();
|
self.dispstat.hblank.set();
|
||||||
self.sched.push(.HBlank, self.sched.now() + (68 * 4) - late);
|
self.sched.push(.HBlank, self.sched.now() + (68 * 4) - late);
|
||||||
|
@ -403,7 +403,7 @@ pub const Ppu = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if Vblank DMA is present and not enabled
|
// See if Vblank DMA is present and not enabled
|
||||||
pollBlankingDma(cpu.bus, .VBlank);
|
pollBlankingDma(&cpu.bus, .VBlank);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scanline == 227) self.dispstat.vblank.unset();
|
if (scanline == 227) self.dispstat.vblank.unset();
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub const Scheduler = struct {
|
||||||
return self.tick;
|
return self.tick;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handleEvent(self: *Self, cpu: *Arm7tdmi, bus: *Bus) void {
|
pub fn handleEvent(self: *Self, cpu: *Arm7tdmi) void {
|
||||||
if (self.queue.removeOrNull()) |event| {
|
if (self.queue.removeOrNull()) |event| {
|
||||||
const late = self.tick - event.tick;
|
const late = self.tick - event.tick;
|
||||||
|
|
||||||
|
@ -40,19 +40,19 @@ pub const Scheduler = struct {
|
||||||
},
|
},
|
||||||
.Draw => {
|
.Draw => {
|
||||||
// The end of a VDraw
|
// The end of a VDraw
|
||||||
bus.ppu.drawScanline();
|
cpu.bus.ppu.drawScanline();
|
||||||
bus.ppu.handleHDrawEnd(cpu, late);
|
cpu.bus.ppu.handleHDrawEnd(cpu, late);
|
||||||
},
|
},
|
||||||
.TimerOverflow => |id| {
|
.TimerOverflow => |id| {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
0 => bus.tim._0.handleOverflow(cpu, late),
|
0 => cpu.bus.tim._0.handleOverflow(cpu, late),
|
||||||
1 => bus.tim._1.handleOverflow(cpu, late),
|
1 => cpu.bus.tim._1.handleOverflow(cpu, late),
|
||||||
2 => bus.tim._2.handleOverflow(cpu, late),
|
2 => cpu.bus.tim._2.handleOverflow(cpu, late),
|
||||||
3 => bus.tim._3.handleOverflow(cpu, late),
|
3 => cpu.bus.tim._3.handleOverflow(cpu, late),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.HBlank => bus.ppu.handleHBlankEnd(cpu, late), // The end of a HBlank
|
.HBlank => cpu.bus.ppu.handleHBlankEnd(cpu, late), // The end of a HBlank
|
||||||
.VBlank => bus.ppu.handleHDrawEnd(cpu, late), // The end of a VBlank
|
.VBlank => cpu.bus.ppu.handleHDrawEnd(cpu, late), // The end of a VBlank
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue