Compare commits

...

2 Commits

2 changed files with 86 additions and 40 deletions

View File

@ -84,12 +84,31 @@ pub fn debugRead(self: *const Self, comptime T: type, address: u32) T {
}
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 r15 = self.cpu.?.r[15];
const word = if (self.cpu.?.cpsr.t.read()) blk: {
const page = @truncate(u8, r15 >> 24);
switch (page) {
// EWRAM, PALRAM, VRAM, and Game ROM (16-bit)
0x02, 0x05, 0x06, 0x08...0x0D => {
const halfword = self.debugRead(u16, r15 + 2);
break :blk @as(u32, halfword) << 16 | halfword;
},
// BIOS or OAM (32-bit)
0x00, 0x07 => {
const offset: u32 = if (address & 3 == 0b00) 2 else 0;
break :blk @as(u32, self.debugRead(u16, (r15 + 2) + offset)) << 16 | self.debugRead(u16, r15 + offset);
},
// IWRAM (16-bit but special)
0x03 => {
const offset: u32 = if (address & 3 == 0b00) 2 else 0;
break :blk @as(u32, self.debugRead(u16, (r15 + 2) - offset)) << 16 | self.debugRead(u16, r15 + offset);
},
else => unreachable,
}
} else self.debugRead(u32, r15 + 4);
const word = self.debugRead(u32, self.cpu.?.r[15] + 4);
return @truncate(T, rotr(u32, word, 8 * (address & 3)));
}
@ -152,7 +171,7 @@ pub fn write(self: *Self, comptime T: type, address: u32, value: T) void {
0x07 => self.ppu.oam.write(T, align_addr, value),
// External Memory (Game Pak)
0x08...0x0D => {},
0x08...0x0D => {}, // EEPROM
0x0E...0x0F => {
const rotate_by = switch (T) {
u32 => address & 3,
@ -163,7 +182,7 @@ pub fn write(self: *Self, comptime T: type, address: u32, value: T) void {
self.pak.backup.write(address, @truncate(u8, rotr(T, value, 8 * rotate_by)));
},
else => undWrite("Tried to write {} 0x{X:} to 0x{X:0>8}", .{ T, value, address }),
else => {},
}
}

View File

@ -67,25 +67,12 @@ pub fn main() anyerror!void {
log.info("Save Path: {s}", .{save_path});
// Initialize SDL
const status = SDL.SDL_Init(SDL.SDL_INIT_VIDEO | SDL.SDL_INIT_EVENTS | SDL.SDL_INIT_AUDIO | SDL.SDL_INIT_GAMECONTROLLER);
_ = initSdl2();
defer SDL.SDL_Quit();
if (status < 0) sdlPanic();
// Initialize SDL Audio
var have: SDL.SDL_AudioSpec = undefined;
var want = std.mem.zeroes(SDL.SDL_AudioSpec);
want.freq = 32768;
want.format = SDL.AUDIO_S8;
want.channels = 2;
want.samples = 0x200;
want.callback = null;
const audio_dev = SDL.SDL_OpenAudioDevice(null, 0, &want, &have, 0);
const audio_dev = initAudio();
defer SDL.SDL_CloseAudioDevice(audio_dev);
if (audio_dev == 0) sdlPanic();
// Start Playback on the Audio evice
SDL.SDL_PauseAudioDevice(audio_dev, 0);
// Initialize Emulator
var scheduler = Scheduler.init(alloc);
@ -113,25 +100,16 @@ pub fn main() anyerror!void {
const emu_thread = try Thread.spawn(.{}, emu.run, .{ .LimitedFPS, &quit, &emu_rate, &scheduler, &cpu });
defer emu_thread.join();
const title = correctTitle(cpu.bus.pak.title);
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}", .{correctTitle(cpu.bus.pak.title)});
var window = SDL.SDL_CreateWindow(
window_title.ptr,
SDL.SDL_WINDOWPOS_CENTERED,
SDL.SDL_WINDOWPOS_CENTERED,
gba_width * window_scale,
gba_height * window_scale,
SDL.SDL_WINDOW_SHOWN,
) orelse sdlPanic();
const window = createWindow(window_title, gba_width, gba_height);
defer SDL.SDL_DestroyWindow(window);
const renderer = SDL.SDL_CreateRenderer(window, -1, SDL.SDL_RENDERER_ACCELERATED | SDL.SDL_RENDERER_PRESENTVSYNC) orelse sdlPanic();
const renderer = createRenderer(window);
defer SDL.SDL_DestroyRenderer(renderer);
const texture = SDL.SDL_CreateTexture(renderer, SDL.SDL_PIXELFORMAT_RGBA8888, SDL.SDL_TEXTUREACCESS_STREAMING, 240, 160) orelse sdlPanic();
const texture = createTexture(renderer, gba_width, gba_height);
defer SDL.SDL_DestroyTexture(texture);
// Init FPS Timer
@ -198,16 +176,16 @@ pub fn main() anyerror!void {
quit.store(true, .Unordered); // Terminate Emulator Thread
}
fn sdlPanic() noreturn {
const str = @as(?[*:0]const u8, SDL.SDL_GetError()) orelse "unknown error";
@panic(std.mem.sliceTo(str, 0));
}
const CliError = error{
InsufficientOptions,
UnneededOptions,
};
fn sdlPanic() noreturn {
const str = @as(?[*:0]const u8, SDL.SDL_GetError()) orelse "unknown error";
@panic(std.mem.sliceTo(str, 0));
}
// FIXME: Superfluous allocations?
fn setupSavePath(alloc: std.mem.Allocator) !?[]const u8 {
const save_subpath = try std.fs.path.join(alloc, &[_][]const u8{ "zba", "save" });
@ -227,3 +205,52 @@ fn setupSavePath(alloc: std.mem.Allocator) !?[]const u8 {
return save_path;
}
fn initSdl2() c_int {
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();
return status;
}
fn createWindow(title: []u8, width: c_int, height: c_int) *SDL.SDL_Window {
return SDL.SDL_CreateWindow(
title.ptr,
SDL.SDL_WINDOWPOS_CENTERED,
SDL.SDL_WINDOWPOS_CENTERED,
width * window_scale,
height * window_scale,
SDL.SDL_WINDOW_SHOWN,
) orelse sdlPanic();
}
fn createRenderer(window: *SDL.SDL_Window) *SDL.SDL_Renderer {
return SDL.SDL_CreateRenderer(window, -1, SDL.SDL_RENDERER_ACCELERATED | SDL.SDL_RENDERER_PRESENTVSYNC) orelse sdlPanic();
}
fn createTexture(renderer: *SDL.SDL_Renderer, width: c_int, height: c_int) *SDL.SDL_Texture {
return SDL.SDL_CreateTexture(
renderer,
SDL.SDL_PIXELFORMAT_RGBA8888,
SDL.SDL_TEXTUREACCESS_STREAMING,
width,
height,
) orelse sdlPanic();
}
fn initAudio() SDL.SDL_AudioDeviceID {
var have: SDL.SDL_AudioSpec = undefined;
var want = std.mem.zeroes(SDL.SDL_AudioSpec);
want.freq = 32768;
want.format = SDL.AUDIO_S8;
want.channels = 2;
want.samples = 0x200;
want.callback = null;
const dev = SDL.SDL_OpenAudioDevice(null, 0, &want, &have, 0);
if (dev == 0) sdlPanic();
// Start Playback on the Audio evice
SDL.SDL_PauseAudioDevice(dev, 0);
return dev;
}