Compare commits
No commits in common. "24def29fc848fe81618111ac326535f2b86a12c7" and "75ba9a4bf942171f7c0a0eacbcab4a7f8f27f32b" have entirely different histories.
24def29fc8
...
75ba9a4bf9
13
src/Bus.zig
13
src/Bus.zig
|
@ -21,6 +21,8 @@ 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;
|
||||||
|
|
||||||
pak: GamePak,
|
pak: GamePak,
|
||||||
bios: Bios,
|
bios: Bios,
|
||||||
ppu: Ppu,
|
ppu: Ppu,
|
||||||
|
@ -71,7 +73,7 @@ pub fn handleDMATransfers(self: *Self) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn isDmaRunning(self: *const Self) bool {
|
fn isDmaRunning(self: *const Self) bool {
|
||||||
return self.dma._0.active or
|
return self.dma._0.active or
|
||||||
self.dma._1.active or
|
self.dma._1.active or
|
||||||
self.dma._2.active or
|
self.dma._2.active or
|
||||||
|
@ -196,3 +198,12 @@ fn alignAddress(comptime T: type, address: u32) u32 {
|
||||||
else => @compileError("Bus: Invalid read/write type"),
|
else => @compileError("Bus: Invalid read/write type"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn undRead(comptime format: []const u8, args: anytype) u8 {
|
||||||
|
if (panic_on_und_bus) std.debug.panic(format, args) else log.warn(format, args);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn undWrite(comptime format: []const u8, args: anytype) void {
|
||||||
|
if (panic_on_und_bus) std.debug.panic(format, args) else log.warn(format, args);
|
||||||
|
}
|
||||||
|
|
10
src/apu.zig
10
src/apu.zig
|
@ -192,7 +192,7 @@ pub const Apu = struct {
|
||||||
const right = (chA_right + chB_right + (psg_right * 0.05)) / 3;
|
const right = (chA_right + chB_right + (psg_right * 0.05)) / 3;
|
||||||
|
|
||||||
if (self.sampling_cycle != self.bias.sampling_cycle.read()) {
|
if (self.sampling_cycle != self.bias.sampling_cycle.read()) {
|
||||||
log.info("Sampling Cycle changed from {} to {}", .{ self.sampling_cycle, self.bias.sampling_cycle.read() });
|
log.warn("Sampling Cycle changed from {} to {}", .{ self.sampling_cycle, self.bias.sampling_cycle.read() });
|
||||||
|
|
||||||
// Sample Rate Changed, Create a new Resampler since i can't figure out how to change
|
// Sample Rate Changed, Create a new Resampler since i can't figure out how to change
|
||||||
// the parameters of the old one
|
// the parameters of the old one
|
||||||
|
@ -203,17 +203,17 @@ pub const Apu = struct {
|
||||||
self.stream = SDL.SDL_NewAudioStream(SDL.AUDIO_F32, 2, @intCast(c_int, self.sampleRate()), SDL.AUDIO_F32, 2, host_sample_rate) orelse unreachable;
|
self.stream = SDL.SDL_NewAudioStream(SDL.AUDIO_F32, 2, @intCast(c_int, self.sampleRate()), SDL.AUDIO_F32, 2, host_sample_rate) orelse unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// while (SDL.SDL_AudioStreamAvailable(self.stream) > (@sizeOf(f32) * 2 * 0x800)) {}
|
while (SDL.SDL_AudioStreamAvailable(self.stream) > (@sizeOf(f32) * 2 * 0x800)) {}
|
||||||
|
|
||||||
_ = SDL.SDL_AudioStreamPut(self.stream, &[2]f32{ left, right }, 2 * @sizeOf(f32));
|
_ = SDL.SDL_AudioStreamPut(self.stream, &[2]f32{ left, right }, 2 * @sizeOf(f32));
|
||||||
self.sched.push(.SampleAudio, self.sched.now() + self.sampleTicks() - late);
|
self.sched.push(.SampleAudio, self.sched.now() + self.sampleTicks() - late);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sampleTicks(self: *const Self) u64 {
|
inline fn sampleTicks(self: *const Self) u64 {
|
||||||
return (1 << 24) / self.sampleRate();
|
return (1 << 24) / self.sampleRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sampleRate(self: *const Self) u64 {
|
inline fn sampleRate(self: *const Self) u64 {
|
||||||
return @as(u64, 1) << (15 + @as(u6, self.bias.sampling_cycle.read()));
|
return @as(u64, 1) << (15 + @as(u6, self.bias.sampling_cycle.read()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1046,7 +1046,7 @@ const WaveDevice = struct {
|
||||||
u8 => {
|
u8 => {
|
||||||
self.buf[base + addr - 0x0400_0090] = value;
|
self.buf[base + addr - 0x0400_0090] = value;
|
||||||
},
|
},
|
||||||
else => @compileError("Ch3 WAVERAM: Unsupported write width"),
|
else => log.err("Unhandled {} write to Ch3 Wave RAM", .{T}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub fn deinit(self: Self) void {
|
||||||
pub fn read(self: *const Self, comptime T: type, addr: usize) T {
|
pub fn read(self: *const Self, comptime T: type, addr: usize) T {
|
||||||
if (self.buf) |buf| {
|
if (self.buf) |buf| {
|
||||||
if (addr > buf.len) {
|
if (addr > buf.len) {
|
||||||
log.debug("Tried to read {} from {X:0>8} (open bus)", .{ T, addr });
|
log.err("Tried to read {} from {X:0>8} (open bus)", .{ T, addr });
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,5 +48,5 @@ pub fn read(self: *const Self, comptime T: type, addr: usize) T {
|
||||||
|
|
||||||
pub fn write(_: *Self, comptime T: type, addr: usize, value: T) void {
|
pub fn write(_: *Self, comptime T: type, addr: usize, value: T) void {
|
||||||
@setCold(true);
|
@setCold(true);
|
||||||
log.debug("Tried to write {} 0x{X:} to 0x{X:0>8} ", .{ T, value, addr });
|
log.err("Tried to write {} 0x{X:} to 0x{X:0>8} ", .{ T, value, addr });
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ pub const Backup = struct {
|
||||||
return log.info("Loaded Save from {s}", .{file_path});
|
return log.info("Loaded Save from {s}", .{file_path});
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("{s} is {} bytes, but we expected {} bytes", .{ file_path, file_buf.len, self.buf.len });
|
log.err("{s} is {} bytes, but we expected {} bytes", .{ file_path, file_buf.len, self.buf.len });
|
||||||
},
|
},
|
||||||
else => return SaveError.UnsupportedBackupKind,
|
else => return SaveError.UnsupportedBackupKind,
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,13 @@ fn DmaController(comptime id: u2) type {
|
||||||
self.writeCntHigh(@truncate(u16, word >> 16));
|
self.writeCntHigh(@truncate(u16, word >> 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub inline fn check(self: *Self, bus: *Bus) bool {
|
||||||
|
if (!self.active) return false; // FIXME: Check CNT register?
|
||||||
|
|
||||||
|
self.step(bus);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn step(self: *Self, bus: *Bus) bool {
|
pub fn step(self: *Self, bus: *Bus) bool {
|
||||||
if (!self.active) return false;
|
if (!self.active) return false;
|
||||||
|
|
||||||
|
|
114
src/bus/io.zig
114
src/bus/io.zig
|
@ -1,5 +1,4 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
|
||||||
|
|
||||||
const Bit = @import("bitfield").Bit;
|
const Bit = @import("bitfield").Bit;
|
||||||
const Bitfield = @import("bitfield").Bitfield;
|
const Bitfield = @import("bitfield").Bitfield;
|
||||||
|
@ -7,6 +6,8 @@ const Bus = @import("../Bus.zig");
|
||||||
const DmaController = @import("dma.zig").DmaController;
|
const DmaController = @import("dma.zig").DmaController;
|
||||||
const Scheduler = @import("../scheduler.zig").Scheduler;
|
const Scheduler = @import("../scheduler.zig").Scheduler;
|
||||||
|
|
||||||
|
const panic_on_und_io: bool = false;
|
||||||
|
|
||||||
const log = std.log.scoped(.@"I/O");
|
const log = std.log.scoped(.@"I/O");
|
||||||
|
|
||||||
pub const Io = struct {
|
pub const Io = struct {
|
||||||
|
@ -60,7 +61,7 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
|
||||||
// Interrupts
|
// Interrupts
|
||||||
0x0400_0200 => @as(T, bus.io.irq.raw) << 16 | bus.io.ie.raw,
|
0x0400_0200 => @as(T, bus.io.irq.raw) << 16 | bus.io.ie.raw,
|
||||||
0x0400_0208 => @boolToInt(bus.io.ime),
|
0x0400_0208 => @boolToInt(bus.io.ime),
|
||||||
else => undefinedRead("Tried to read {} from 0x{X:0>8}", .{ T, address }),
|
else => undRead("Tried to read {} from 0x{X:0>8}", .{ T, address }),
|
||||||
},
|
},
|
||||||
u16 => switch (address) {
|
u16 => switch (address) {
|
||||||
// Display
|
// Display
|
||||||
|
@ -102,7 +103,7 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
|
||||||
0x0400_0202 => bus.io.irq.raw,
|
0x0400_0202 => bus.io.irq.raw,
|
||||||
0x0400_0204 => unimplementedRead("Read halfword from WAITCNT", .{}),
|
0x0400_0204 => unimplementedRead("Read halfword from WAITCNT", .{}),
|
||||||
0x0400_0208 => @boolToInt(bus.io.ime),
|
0x0400_0208 => @boolToInt(bus.io.ime),
|
||||||
else => undefinedRead("Tried to read {} from 0x{X:0>8}", .{ T, address }),
|
else => undRead("Tried to read {} from 0x{X:0>8}", .{ T, address }),
|
||||||
},
|
},
|
||||||
u8 => return switch (address) {
|
u8 => return switch (address) {
|
||||||
// Display
|
// Display
|
||||||
|
@ -126,7 +127,7 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
|
||||||
// Interrupts
|
// Interrupts
|
||||||
0x0400_0200 => @truncate(T, bus.io.ie.raw),
|
0x0400_0200 => @truncate(T, bus.io.ie.raw),
|
||||||
0x0400_0300 => @enumToInt(bus.io.postflg),
|
0x0400_0300 => @enumToInt(bus.io.postflg),
|
||||||
else => undefinedRead("Tried to read {} from 0x{X:0>8}", .{ T, address }),
|
else => undRead("Tried to read byte from 0x{X:0>8}", .{address}),
|
||||||
},
|
},
|
||||||
else => @compileError("I/O: Unsupported read width"),
|
else => @compileError("I/O: Unsupported read width"),
|
||||||
};
|
};
|
||||||
|
@ -178,13 +179,13 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
0x0400_010C => bus.tim._3.writeCnt(value),
|
0x0400_010C => bus.tim._3.writeCnt(value),
|
||||||
|
|
||||||
// Serial Communication 1
|
// Serial Communication 1
|
||||||
0x0400_0120 => log.debug("Wrote 0x{X:0>8} to SIODATA32", .{value}),
|
0x0400_0120 => log.warn("Wrote 0x{X:0>8} to SIODATA32", .{value}),
|
||||||
|
|
||||||
// Interrupts
|
// Interrupts
|
||||||
0x0400_0200 => bus.io.setIrqs(value),
|
0x0400_0200 => bus.io.setIrqs(value),
|
||||||
0x0400_0204 => log.debug("Wrote 0x{X:0>8} to WAITCNT", .{value}),
|
0x0400_0204 => log.warn("Wrote 0x{X:0>8} to WAITCNT", .{value}),
|
||||||
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
||||||
else => undefinedWrite("Tried to write {} 0x{X:0>8} to 0x{X:0>8}", .{ T, value, address }),
|
else => undWrite("Tried to write {} 0x{X:0>8} to 0x{X:0>8}", .{ T, value, address }),
|
||||||
},
|
},
|
||||||
u16 => switch (address) {
|
u16 => switch (address) {
|
||||||
// Display
|
// Display
|
||||||
|
@ -203,32 +204,32 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
0x0400_001A => bus.ppu.bg[2].vofs.raw = value,
|
0x0400_001A => bus.ppu.bg[2].vofs.raw = value,
|
||||||
0x0400_001C => bus.ppu.bg[3].hofs.raw = value,
|
0x0400_001C => bus.ppu.bg[3].hofs.raw = value,
|
||||||
0x0400_001E => bus.ppu.bg[3].vofs.raw = value,
|
0x0400_001E => bus.ppu.bg[3].vofs.raw = value,
|
||||||
0x0400_0020 => log.debug("Wrote 0x{X:0>4} to BG2PA", .{value}),
|
0x0400_0020 => log.warn("Wrote 0x{X:0>4} to BG2PA", .{value}),
|
||||||
0x0400_0022 => log.debug("Wrote 0x{X:0>4} to BG2PB", .{value}),
|
0x0400_0022 => log.warn("Wrote 0x{X:0>4} to BG2PB", .{value}),
|
||||||
0x0400_0024 => log.debug("Wrote 0x{X:0>4} to BG2PC", .{value}),
|
0x0400_0024 => log.warn("Wrote 0x{X:0>4} to BG2PC", .{value}),
|
||||||
0x0400_0026 => log.debug("Wrote 0x{X:0>4} to BG2PD", .{value}),
|
0x0400_0026 => log.warn("Wrote 0x{X:0>4} to BG2PD", .{value}),
|
||||||
0x0400_0028 => log.debug("Wrote 0x{X:0>4} to BG2X_L", .{value}),
|
0x0400_0028 => log.warn("Wrote 0x{X:0>4} to BG2X_L", .{value}),
|
||||||
0x0400_002A => log.debug("Wrote 0x{X:0>4} to BG2X_H", .{value}),
|
0x0400_002A => log.warn("Wrote 0x{X:0>4} to BG2X_H", .{value}),
|
||||||
0x0400_002C => log.debug("Wrote 0x{X:0>4} to BG2Y_L", .{value}),
|
0x0400_002C => log.warn("Wrote 0x{X:0>4} to BG2Y_L", .{value}),
|
||||||
0x0400_002E => log.debug("Wrote 0x{X:0>4} to BG2Y_H", .{value}),
|
0x0400_002E => log.warn("Wrote 0x{X:0>4} to BG2Y_H", .{value}),
|
||||||
0x0400_0030 => log.debug("Wrote 0x{X:0>4} to BG3PA", .{value}),
|
0x0400_0030 => log.warn("Wrote 0x{X:0>4} to BG3PA", .{value}),
|
||||||
0x0400_0032 => log.debug("Wrote 0x{X:0>4} to BG3PB", .{value}),
|
0x0400_0032 => log.warn("Wrote 0x{X:0>4} to BG3PB", .{value}),
|
||||||
0x0400_0034 => log.debug("Wrote 0x{X:0>4} to BG3PC", .{value}),
|
0x0400_0034 => log.warn("Wrote 0x{X:0>4} to BG3PC", .{value}),
|
||||||
0x0400_0036 => log.debug("Wrote 0x{X:0>4} to BG3PD", .{value}),
|
0x0400_0036 => log.warn("Wrote 0x{X:0>4} to BG3PD", .{value}),
|
||||||
0x0400_0038 => log.debug("Wrote 0x{X:0>4} to BG3X_L", .{value}),
|
0x0400_0038 => log.warn("Wrote 0x{X:0>4} to BG3X_L", .{value}),
|
||||||
0x0400_003A => log.debug("Wrote 0x{X:0>4} to BG3X_H", .{value}),
|
0x0400_003A => log.warn("Wrote 0x{X:0>4} to BG3X_H", .{value}),
|
||||||
0x0400_003C => log.debug("Wrote 0x{X:0>4} to BG3Y_L", .{value}),
|
0x0400_003C => log.warn("Wrote 0x{X:0>4} to BG3Y_L", .{value}),
|
||||||
0x0400_003E => log.debug("Wrote 0x{X:0>4} to BG3Y_H", .{value}),
|
0x0400_003E => log.warn("Wrote 0x{X:0>4} to BG3Y_H", .{value}),
|
||||||
0x0400_0040 => log.debug("Wrote 0x{X:0>4} to WIN0H", .{value}),
|
0x0400_0040 => log.warn("Wrote 0x{X:0>4} to WIN0H", .{value}),
|
||||||
0x0400_0042 => log.debug("Wrote 0x{X:0>4} to WIN1H", .{value}),
|
0x0400_0042 => log.warn("Wrote 0x{X:0>4} to WIN1H", .{value}),
|
||||||
0x0400_0044 => log.debug("Wrote 0x{X:0>4} to WIN0V", .{value}),
|
0x0400_0044 => log.warn("Wrote 0x{X:0>4} to WIN0V", .{value}),
|
||||||
0x0400_0046 => log.debug("Wrote 0x{X:0>4} to WIN1V", .{value}),
|
0x0400_0046 => log.warn("Wrote 0x{X:0>4} to WIN1V", .{value}),
|
||||||
0x0400_0048 => log.debug("Wrote 0x{X:0>4} to WININ", .{value}),
|
0x0400_0048 => log.warn("Wrote 0x{X:0>4} to WININ", .{value}),
|
||||||
0x0400_004A => log.debug("Wrote 0x{X:0>4} to WINOUT", .{value}),
|
0x0400_004A => log.warn("Wrote 0x{X:0>4} to WINOUT", .{value}),
|
||||||
0x0400_004C => log.debug("Wrote 0x{X:0>4} to MOSAIC", .{value}),
|
0x0400_004C => log.warn("Wrote 0x{X:0>4} to MOSAIC", .{value}),
|
||||||
0x0400_0050 => log.debug("Wrote 0x{X:0>4} to BLDCNT", .{value}),
|
0x0400_0050 => log.warn("Wrote 0x{X:0>4} to BLDCNT", .{value}),
|
||||||
0x0400_0052 => log.debug("Wrote 0x{X:0>4} to BLDALPHA", .{value}),
|
0x0400_0052 => log.warn("Wrote 0x{X:0>4} to BLDALPHA", .{value}),
|
||||||
0x0400_0054 => log.debug("Wrote 0x{X:0>4} to BLDY", .{value}),
|
0x0400_0054 => log.warn("Wrote 0x{X:0>4} to BLDY", .{value}),
|
||||||
0x0400_004E, 0x0400_0056 => {}, // Not used
|
0x0400_004E, 0x0400_0056 => {}, // Not used
|
||||||
|
|
||||||
// Sound
|
// Sound
|
||||||
|
@ -293,30 +294,30 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
0x0400_0110 => {}, // Not Used
|
0x0400_0110 => {}, // Not Used
|
||||||
|
|
||||||
// Serial Communication 1
|
// Serial Communication 1
|
||||||
0x0400_0120 => log.debug("Wrote 0x{X:0>4} to SIOMULTI0", .{value}),
|
0x0400_0120 => log.warn("Wrote 0x{X:0>4} to SIOMULTI0", .{value}),
|
||||||
0x0400_0122 => log.debug("Wrote 0x{X:0>4} to SIOMULTI1", .{value}),
|
0x0400_0122 => log.warn("Wrote 0x{X:0>4} to SIOMULTI1", .{value}),
|
||||||
0x0400_0124 => log.debug("Wrote 0x{X:0>4} to SIOMULTI2", .{value}),
|
0x0400_0124 => log.warn("Wrote 0x{X:0>4} to SIOMULTI2", .{value}),
|
||||||
0x0400_0126 => log.debug("Wrote 0x{X:0>4} to SIOMULTI3", .{value}),
|
0x0400_0126 => log.warn("Wrote 0x{X:0>4} to SIOMULTI3", .{value}),
|
||||||
0x0400_0128 => log.debug("Wrote 0x{X:0>4} to SIOCNT", .{value}),
|
0x0400_0128 => log.warn("Wrote 0x{X:0>4} to SIOCNT", .{value}),
|
||||||
0x0400_012A => log.debug("Wrote 0x{X:0>4} to SIOMLT_SEND", .{value}),
|
0x0400_012A => log.warn("Wrote 0x{X:0>4} to SIOMLT_SEND", .{value}),
|
||||||
|
|
||||||
// Keypad Input
|
// Keypad Input
|
||||||
0x0400_0130 => log.debug("Wrote 0x{X:0>4} to KEYINPUT. Ignored", .{value}),
|
0x0400_0130 => log.warn("Wrote 0x{X:0>4} to KEYINPUT. Ignored", .{value}),
|
||||||
0x0400_0132 => log.debug("Wrote 0x{X:0>4} to KEYCNT", .{value}),
|
0x0400_0132 => log.warn("Wrote 0x{X:0>4} to KEYCNT", .{value}),
|
||||||
|
|
||||||
// Serial Communication 2
|
// Serial Communication 2
|
||||||
0x0400_0134 => log.debug("Wrote 0x{X:0>4} to RCNT", .{value}),
|
0x0400_0134 => log.warn("Wrote 0x{X:0>4} to RCNT", .{value}),
|
||||||
0x0400_0140 => log.debug("Wrote 0x{X:0>4} to JOYCNT", .{value}),
|
0x0400_0140 => log.warn("Wrote 0x{X:0>4} to JOYCNT", .{value}),
|
||||||
0x0400_0158 => log.debug("Wrote 0x{X:0>4} to JOYSTAT", .{value}),
|
0x0400_0158 => log.warn("Wrote 0x{X:0>4} to JOYSTAT", .{value}),
|
||||||
0x0400_0142, 0x0400_015A => {}, // Not Used
|
0x0400_0142, 0x0400_015A => {}, // Not Used
|
||||||
|
|
||||||
// Interrupts
|
// Interrupts
|
||||||
0x0400_0200 => bus.io.ie.raw = value,
|
0x0400_0200 => bus.io.ie.raw = value,
|
||||||
0x0400_0202 => bus.io.irq.raw &= ~value,
|
0x0400_0202 => bus.io.irq.raw &= ~value,
|
||||||
0x0400_0204 => log.debug("Wrote 0x{X:0>4} to WAITCNT", .{value}),
|
0x0400_0204 => log.warn("Wrote 0x{X:0>4} to WAITCNT", .{value}),
|
||||||
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
||||||
0x0400_0206, 0x0400_020A => {}, // Not Used
|
0x0400_0206, 0x0400_020A => {}, // Not Used
|
||||||
else => undefinedWrite("Tried to write {} 0x{X:0>4} to 0x{X:0>8}", .{ T, value, address }),
|
else => undWrite("Tried to write {} 0x{X:0>4} to 0x{X:0>8}", .{ T, value, address }),
|
||||||
},
|
},
|
||||||
u8 => switch (address) {
|
u8 => switch (address) {
|
||||||
// Display
|
// Display
|
||||||
|
@ -353,37 +354,32 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
0x0400_0090...0x0400_009F => bus.apu.ch3.wave_dev.write(T, bus.apu.ch3.select, address, value),
|
0x0400_0090...0x0400_009F => bus.apu.ch3.wave_dev.write(T, bus.apu.ch3.select, address, value),
|
||||||
|
|
||||||
// Serial Communication 1
|
// Serial Communication 1
|
||||||
0x0400_0128 => log.debug("Wrote 0x{X:0>2} to SIOCNT (low)", .{value}),
|
0x0400_0128 => log.warn("Wrote 0x{X:0>2} to SIOCNT (low)", .{value}),
|
||||||
|
|
||||||
// Serial Communication 2
|
// Serial Communication 2
|
||||||
0x0400_0140 => log.debug("Wrote 0x{X:0>2} to JOYCNT (low)", .{value}),
|
0x0400_0140 => log.warn("Wrote 0x{X:0>2} to JOYCNT (low)", .{value}),
|
||||||
|
|
||||||
// Interrupts
|
// Interrupts
|
||||||
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
||||||
0x0400_0301 => bus.io.haltcnt = if (value >> 7 & 1 == 0) .Halt else std.debug.panic("TODO: Implement STOP", .{}),
|
0x0400_0301 => bus.io.haltcnt = if (value >> 7 & 1 == 0) .Halt else std.debug.panic("TODO: Implement STOP", .{}),
|
||||||
else => undefinedWrite("Tried to write {} 0x{X:0>2} to 0x{X:0>8}", .{ T, value, address }),
|
else => undWrite("Tried to write 0x{X:0>2} to 0x{X:0>8}", .{ value, address }),
|
||||||
},
|
},
|
||||||
else => @compileError("I/O: Unsupported write width"),
|
else => @compileError("I/O: Unsupported write width"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn undefinedRead(comptime format: []const u8, args: anytype) u8 {
|
fn undRead(comptime format: []const u8, args: anytype) u8 {
|
||||||
log.debug(format, args);
|
if (panic_on_und_io) std.debug.panic(format, args) else log.warn(format, args);
|
||||||
if (builtin.mode == .Debug) std.debug.panic("TODO: Implement I/O Register", .{});
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unimplementedRead(comptime format: []const u8, args: anytype) u8 {
|
fn unimplementedRead(comptime format: []const u8, args: anytype) u8 {
|
||||||
log.debug(format, args);
|
log.warn(format, args);
|
||||||
if (builtin.mode == .Debug) std.debug.panic("TODO: Implement I/O Register", .{});
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn undefinedWrite(comptime format: []const u8, args: anytype) void {
|
fn undWrite(comptime format: []const u8, args: anytype) void {
|
||||||
log.debug(format, args);
|
if (panic_on_und_io) std.debug.panic(format, args) else log.warn(format, args);
|
||||||
if (builtin.mode == .Debug) std.debug.panic("TODO: Implement I/O Register", .{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read / Write
|
/// Read / Write
|
||||||
|
|
19
src/cpu.zig
19
src/cpu.zig
|
@ -253,12 +253,12 @@ pub const Arm7tdmi = struct {
|
||||||
|
|
||||||
pub fn step(self: *Self) void {
|
pub fn step(self: *Self) void {
|
||||||
if (self.cpsr.t.read()) {
|
if (self.cpsr.t.read()) {
|
||||||
const opcode = self.fetch(u16);
|
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(u32);
|
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))) {
|
||||||
|
@ -272,12 +272,12 @@ pub const Arm7tdmi = struct {
|
||||||
|
|
||||||
if (should_handle != 0) {
|
if (should_handle != 0) {
|
||||||
self.bus.io.haltcnt = .Execute;
|
self.bus.io.haltcnt = .Execute;
|
||||||
// log.debug("An Interrupt was Fired!", .{});
|
// log.info("An Interrupt was Fired!", .{});
|
||||||
|
|
||||||
// Either IME is not true or I in CPSR is true
|
// Either IME is not true or I in CPSR is true
|
||||||
// Don't handle interrupts
|
// Don't handle interrupts
|
||||||
if (!self.bus.io.ime or self.cpsr.i.read()) return;
|
if (!self.bus.io.ime or self.cpsr.i.read()) return;
|
||||||
// log.debug("An interrupt was Handled!", .{});
|
// log.info("An interrupt was Handled!", .{});
|
||||||
|
|
||||||
// retAddr.gba says r15 on it's own is off by -04h in both ARM and THUMB mode
|
// retAddr.gba says r15 on it's own is off by -04h in both ARM and THUMB mode
|
||||||
const r15 = self.r[15] + 4;
|
const r15 = self.r[15] + 4;
|
||||||
|
@ -293,11 +293,14 @@ pub const Arm7tdmi = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn fetch(self: *Self, comptime T: type) T {
|
fn thumbFetch(self: *Self) u16 {
|
||||||
comptime std.debug.assert(T == u32 or T == u16); // Opcode may be 32-bit (ARM) or 16-bit (THUMB)
|
defer self.r[15] += 2;
|
||||||
defer self.r[15] += if (T == u32) 4 else 2;
|
return self.bus.read(u16, self.r[15]);
|
||||||
|
}
|
||||||
|
|
||||||
return self.bus.read(T, self.r[15]);
|
fn fetch(self: *Self) u32 {
|
||||||
|
defer self.r[15] += 4;
|
||||||
|
return self.bus.read(u32, self.r[15]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fakePC(self: *const Self) u32 {
|
pub fn fakePC(self: *const Self) u32 {
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub fn psrTransfer(comptime I: bool, comptime R: bool, comptime kind: u2) InstrF
|
||||||
// MRS
|
// MRS
|
||||||
const rd = opcode >> 12 & 0xF;
|
const rd = opcode >> 12 & 0xF;
|
||||||
|
|
||||||
if (R and !cpu.hasSPSR()) log.err("Tried to read SPSR from User/System Mode", .{});
|
if (R and !cpu.hasSPSR()) log.warn("Tried to read SPSR from User/System Mode", .{});
|
||||||
cpu.r[rd] = if (R) cpu.spsr.raw else cpu.cpsr.raw;
|
cpu.r[rd] = if (R) cpu.spsr.raw else cpu.cpsr.raw;
|
||||||
},
|
},
|
||||||
0b10 => {
|
0b10 => {
|
||||||
|
@ -26,7 +26,7 @@ pub fn psrTransfer(comptime I: bool, comptime R: bool, comptime kind: u2) InstrF
|
||||||
const rm_idx = opcode & 0xF;
|
const rm_idx = opcode & 0xF;
|
||||||
const right = if (I) rotr(u32, opcode & 0xFF, (opcode >> 8 & 0xF) << 1) else cpu.r[rm_idx];
|
const right = if (I) rotr(u32, opcode & 0xFF, (opcode >> 8 & 0xF) << 1) else cpu.r[rm_idx];
|
||||||
|
|
||||||
if (R and !cpu.hasSPSR()) log.err("Tried to write to SPSR in User/System Mode", .{});
|
if (R and !cpu.hasSPSR()) log.warn("Tried to write to SPSR in User/System Mode", .{});
|
||||||
|
|
||||||
if (R) {
|
if (R) {
|
||||||
if (cpu.isPrivileged()) cpu.spsr.raw = fieldMask(&cpu.spsr, field_mask, right);
|
if (cpu.isPrivileged()) cpu.spsr.raw = fieldMask(&cpu.spsr, field_mask, right);
|
||||||
|
|
10
src/emu.zig
10
src/emu.zig
|
@ -57,12 +57,12 @@ pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runUnsync(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi) void {
|
pub fn runUnsync(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
log.info("Start unsynchronized emu thread", .{});
|
log.info("Unsynchronized EmuThread has begun", .{});
|
||||||
while (!quit.load(.Unordered)) runFrame(sched, cpu);
|
while (!quit.load(.Unordered)) runFrame(sched, cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runSync(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi) void {
|
pub fn runSync(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
log.info("Start synchronized emu thread", .{});
|
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;
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ pub fn runSync(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runUnsyncFps(quit: *Atomic(bool), fps: *FpsAverage, sched: *Scheduler, cpu: *Arm7tdmi) void {
|
pub fn runUnsyncFps(quit: *Atomic(bool), fps: *FpsAverage, sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
log.info("Start unsynchronized emu thread w/ fps tracking", .{});
|
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)) {
|
||||||
|
@ -89,7 +89,7 @@ pub fn runUnsyncFps(quit: *Atomic(bool), fps: *FpsAverage, sched: *Scheduler, cp
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runSyncFps(quit: *Atomic(bool), fps: *FpsAverage, sched: *Scheduler, cpu: *Arm7tdmi) void {
|
pub fn runSyncFps(quit: *Atomic(bool), fps: *FpsAverage, sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
log.info("Start synchronized emu thread w/ fps tracking", .{});
|
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;
|
||||||
|
@ -110,7 +110,7 @@ pub fn runSyncFps(quit: *Atomic(bool), fps: *FpsAverage, sched: *Scheduler, cpu:
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runBusyLoop(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi) void {
|
pub fn runBusyLoop(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
log.info("Start synchronized emu thread using busy loop", .{});
|
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;
|
||||||
|
|
||||||
|
|
12
src/main.zig
12
src/main.zig
|
@ -65,9 +65,9 @@ pub fn main() anyerror!void {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Determine Save Directory
|
// Determine Save Directory
|
||||||
const save_dir = try setupSavePath(alloc);
|
const save_path = try setupSavePath(alloc);
|
||||||
defer if (save_dir) |path| alloc.free(path);
|
defer if (save_path) |path| alloc.free(path);
|
||||||
log.info("Found save directory: {s}", .{save_dir});
|
log.info("Save Path: {s}", .{save_path});
|
||||||
|
|
||||||
// Initialize SDL
|
// Initialize SDL
|
||||||
_ = initSdl2();
|
_ = initSdl2();
|
||||||
|
@ -77,7 +77,7 @@ pub fn main() anyerror!void {
|
||||||
var scheduler = Scheduler.init(alloc);
|
var scheduler = Scheduler.init(alloc);
|
||||||
defer scheduler.deinit();
|
defer scheduler.deinit();
|
||||||
|
|
||||||
const paths = .{ .bios = bios_path, .rom = rom_path, .save = save_dir };
|
const paths = .{ .bios = bios_path, .rom = rom_path, .save = save_path };
|
||||||
var cpu = try Arm7tdmi.init(alloc, &scheduler, paths);
|
var cpu = try Arm7tdmi.init(alloc, &scheduler, paths);
|
||||||
defer cpu.deinit();
|
defer cpu.deinit();
|
||||||
|
|
||||||
|
@ -266,5 +266,7 @@ fn initAudio(apu: *Apu) SDL.SDL_AudioDeviceID {
|
||||||
|
|
||||||
export fn audioCallback(userdata: ?*anyopaque, stream: [*c]u8, len: c_int) void {
|
export fn audioCallback(userdata: ?*anyopaque, stream: [*c]u8, len: c_int) void {
|
||||||
const apu = @ptrCast(*Apu, @alignCast(8, userdata));
|
const apu = @ptrCast(*Apu, @alignCast(8, userdata));
|
||||||
_ = SDL.SDL_AudioStreamGet(apu.stream, stream, len);
|
const result = SDL.SDL_AudioStreamGet(apu.stream, stream, len);
|
||||||
|
|
||||||
|
if (result < 0) log.err("Audio Callback Underflow", .{});
|
||||||
}
|
}
|
||||||
|
|
20
src/ppu.zig
20
src/ppu.zig
|
@ -645,43 +645,43 @@ const Sprite = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn x(self: *const Self) u9 {
|
inline fn x(self: *const Self) u9 {
|
||||||
return self.attr1.x.read();
|
return self.attr1.x.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn y(self: *const Self) u8 {
|
inline fn y(self: *const Self) u8 {
|
||||||
return self.attr0.y.read();
|
return self.attr0.y.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_8bpp(self: *const Self) bool {
|
inline fn is_8bpp(self: *const Self) bool {
|
||||||
return self.attr0.is_8bpp.read();
|
return self.attr0.is_8bpp.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shape(self: *const Self) u2 {
|
inline fn shape(self: *const Self) u2 {
|
||||||
return self.attr0.shape.read();
|
return self.attr0.shape.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(self: *const Self) u2 {
|
inline fn size(self: *const Self) u2 {
|
||||||
return self.attr1.size.read();
|
return self.attr1.size.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tile_id(self: *const Self) u10 {
|
inline fn tile_id(self: *const Self) u10 {
|
||||||
return self.attr2.tile_id.read();
|
return self.attr2.tile_id.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pal_bank(self: *const Self) u4 {
|
inline fn pal_bank(self: *const Self) u4 {
|
||||||
return self.attr2.pal_bank.read();
|
return self.attr2.pal_bank.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn h_flip(self: *const Self) bool {
|
inline fn h_flip(self: *const Self) bool {
|
||||||
return self.attr1.h_flip.read();
|
return self.attr1.h_flip.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn v_flip(self: *const Self) bool {
|
inline fn v_flip(self: *const Self) bool {
|
||||||
return self.attr1.v_flip.read();
|
return self.attr1.v_flip.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn priority(self: *const Self) u2 {
|
inline fn priority(self: *const Self) u2 {
|
||||||
return self.attr2.rel_prio.read();
|
return self.attr2.rel_prio.read();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,7 +35,7 @@ pub const Scheduler = struct {
|
||||||
|
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
.HeatDeath => {
|
.HeatDeath => {
|
||||||
log.err("u64 overflow. This *actually* should never happen.", .{});
|
log.err("A u64 overflowered. This *actually* should never happen.", .{});
|
||||||
unreachable;
|
unreachable;
|
||||||
},
|
},
|
||||||
.Draw => {
|
.Draw => {
|
||||||
|
|
Loading…
Reference in New Issue