Compare commits
3 Commits
2bce02baaa
...
472457b9f3
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | 472457b9f3 | |
Rekai Nyangadzayi Musuka | 2ef4bb7dcc | |
Rekai Nyangadzayi Musuka | 9a732ea6f8 |
|
@ -335,10 +335,10 @@ fn DmaController(comptime id: u2) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pollDmaOnBlank(bus: *Bus, comptime kind: DmaKind) void {
|
pub fn pollDmaOnBlank(bus: *Bus, comptime kind: DmaKind) void {
|
||||||
bus.dma[0].poll(kind);
|
comptime var i: usize = 0;
|
||||||
bus.dma[1].poll(kind);
|
inline while (i < 4) : (i += 1) {
|
||||||
bus.dma[2].poll(kind);
|
bus.dma[i].poll(kind);
|
||||||
bus.dma[3].poll(kind);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Adjustment = enum(u2) {
|
const Adjustment = enum(u2) {
|
||||||
|
|
|
@ -9,6 +9,9 @@ const Bit = @import("bitfield").Bit;
|
||||||
const Bitfield = @import("bitfield").Bitfield;
|
const Bitfield = @import("bitfield").Bitfield;
|
||||||
const Bus = @import("../Bus.zig");
|
const Bus = @import("../Bus.zig");
|
||||||
|
|
||||||
|
const getHalf = util.getHalf;
|
||||||
|
const setHalf = util.setHalf;
|
||||||
|
|
||||||
const log = std.log.scoped(.@"I/O");
|
const log = std.log.scoped(.@"I/O");
|
||||||
|
|
||||||
pub const Io = struct {
|
pub const Io = struct {
|
||||||
|
@ -19,6 +22,7 @@ pub const Io = struct {
|
||||||
ie: InterruptEnable,
|
ie: InterruptEnable,
|
||||||
irq: InterruptRequest,
|
irq: InterruptRequest,
|
||||||
postflg: PostFlag,
|
postflg: PostFlag,
|
||||||
|
waitcnt: WaitControl,
|
||||||
haltcnt: HaltControl,
|
haltcnt: HaltControl,
|
||||||
keyinput: KeyInput,
|
keyinput: KeyInput,
|
||||||
|
|
||||||
|
@ -28,6 +32,7 @@ pub const Io = struct {
|
||||||
.ie = .{ .raw = 0x0000 },
|
.ie = .{ .raw = 0x0000 },
|
||||||
.irq = .{ .raw = 0x0000 },
|
.irq = .{ .raw = 0x0000 },
|
||||||
.keyinput = .{ .raw = 0x03FF },
|
.keyinput = .{ .raw = 0x03FF },
|
||||||
|
.waitcnt = .{ .raw = 0x0000_0000 },
|
||||||
.postflg = .FirstBoot,
|
.postflg = .FirstBoot,
|
||||||
.haltcnt = .Execute,
|
.haltcnt = .Execute,
|
||||||
};
|
};
|
||||||
|
@ -64,8 +69,10 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) ?T {
|
||||||
0x0400_0150 => util.io.read.todo(log, "Read {} from JOY_RECV", .{T}),
|
0x0400_0150 => util.io.read.todo(log, "Read {} from JOY_RECV", .{T}),
|
||||||
|
|
||||||
// Interrupts
|
// Interrupts
|
||||||
0x0400_0200 => @as(T, bus.io.irq.raw) << 16 | bus.io.ie.raw,
|
0x0400_0200 => @as(u32, bus.io.irq.raw) << 16 | bus.io.ie.raw,
|
||||||
|
0x0400_0204 => bus.io.waitcnt.raw,
|
||||||
0x0400_0208 => @boolToInt(bus.io.ime),
|
0x0400_0208 => @boolToInt(bus.io.ime),
|
||||||
|
0x0400_0300 => @enumToInt(bus.io.postflg),
|
||||||
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, address }),
|
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, address }),
|
||||||
},
|
},
|
||||||
u16 => switch (address) {
|
u16 => switch (address) {
|
||||||
|
@ -93,8 +100,11 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) ?T {
|
||||||
// Interrupts
|
// Interrupts
|
||||||
0x0400_0200 => bus.io.ie.raw,
|
0x0400_0200 => bus.io.ie.raw,
|
||||||
0x0400_0202 => bus.io.irq.raw,
|
0x0400_0202 => bus.io.irq.raw,
|
||||||
0x0400_0204 => util.io.read.todo(log, "Read {} from WAITCNT", .{T}),
|
0x0400_0204 => bus.io.waitcnt.raw,
|
||||||
|
0x0400_0206 => null,
|
||||||
0x0400_0208 => @boolToInt(bus.io.ime),
|
0x0400_0208 => @boolToInt(bus.io.ime),
|
||||||
|
0x0400_020A => null,
|
||||||
|
0x0400_0300 => @enumToInt(bus.io.postflg),
|
||||||
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, address }),
|
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, address }),
|
||||||
},
|
},
|
||||||
u8 => return switch (address) {
|
u8 => return switch (address) {
|
||||||
|
@ -120,7 +130,12 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) ?T {
|
||||||
0x0400_0135 => util.io.read.todo(log, "Read {} from RCNT_H", .{T}),
|
0x0400_0135 => util.io.read.todo(log, "Read {} from RCNT_H", .{T}),
|
||||||
|
|
||||||
// Interrupts
|
// Interrupts
|
||||||
0x0400_0200 => @truncate(T, bus.io.ie.raw),
|
0x0400_0200, 0x0400_0201 => @truncate(T, bus.io.ie.raw >> getHalf(@truncate(u8, address))),
|
||||||
|
0x0400_0202, 0x0400_0203 => @truncate(T, bus.io.irq.raw >> getHalf(@truncate(u8, address))),
|
||||||
|
0x0400_0204, 0x0400_0205 => @truncate(T, bus.io.waitcnt.raw >> getHalf(@truncate(u8, address))),
|
||||||
|
0x0400_0206, 0x0400_0207 => null,
|
||||||
|
0x0400_0208, 0x0400_0209 => @truncate(T, @as(u16, @boolToInt(bus.io.ime)) >> getHalf(@truncate(u8, address))),
|
||||||
|
0x0400_020A, 0x0400_020B => null,
|
||||||
0x0400_0300 => @enumToInt(bus.io.postflg),
|
0x0400_0300 => @enumToInt(bus.io.postflg),
|
||||||
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, address }),
|
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, address }),
|
||||||
},
|
},
|
||||||
|
@ -168,9 +183,12 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
|
|
||||||
// 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 => bus.io.waitcnt.raw = @truncate(u16, value),
|
||||||
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
||||||
0x0400_020C...0x0400_021C => {}, // Unused
|
0x0400_0300 => {
|
||||||
|
bus.io.postflg = @intToEnum(PostFlag, value & 1);
|
||||||
|
bus.io.haltcnt = if (value >> 15 & 1 == 0) .Halt else @panic("TODO: Implement STOP");
|
||||||
|
},
|
||||||
else => util.io.write.undef(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, address }),
|
else => util.io.write.undef(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, address }),
|
||||||
},
|
},
|
||||||
u16 => switch (address) {
|
u16 => switch (address) {
|
||||||
|
@ -210,9 +228,14 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
// 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 => bus.io.waitcnt.raw = value,
|
||||||
|
0x0400_0206 => {},
|
||||||
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
||||||
0x0400_0206, 0x0400_020A => {}, // Not Used
|
0x0400_020A => {},
|
||||||
|
0x0400_0300 => {
|
||||||
|
bus.io.postflg = @intToEnum(PostFlag, value & 1);
|
||||||
|
bus.io.haltcnt = if (value >> 15 & 1 == 0) .Halt else @panic("TODO: Implement STOP");
|
||||||
|
},
|
||||||
else => util.io.write.undef(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, address }),
|
else => util.io.write.undef(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, address }),
|
||||||
},
|
},
|
||||||
u8 => switch (address) {
|
u8 => switch (address) {
|
||||||
|
@ -237,9 +260,16 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
0x0400_0140 => log.debug("Wrote 0x{X:0>2} to JOYCNT_L", .{value}),
|
0x0400_0140 => log.debug("Wrote 0x{X:0>2} to JOYCNT_L", .{value}),
|
||||||
|
|
||||||
// Interrupts
|
// Interrupts
|
||||||
|
0x0400_0200, 0x0400_0201 => bus.io.ie.raw = setHalf(u16, bus.io.ie.raw, @truncate(u8, address), value),
|
||||||
0x0400_0202 => bus.io.irq.raw &= ~@as(u16, value),
|
0x0400_0202 => bus.io.irq.raw &= ~@as(u16, value),
|
||||||
|
0x0400_0203 => bus.io.irq.raw &= ~@as(u16, value) << 8, // TODO: Is this good?
|
||||||
|
0x0400_0204, 0x0400_0205 => bus.io.waitcnt.raw = setHalf(u16, @truncate(u16, bus.io.waitcnt.raw), @truncate(u8, address), value),
|
||||||
|
0x0400_0206, 0x0400_0207 => {},
|
||||||
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
||||||
0x0400_0300 => bus.io.postflg = std.meta.intToEnum(PostFlag, value & 1) catch unreachable,
|
0x0400_0209 => {},
|
||||||
|
0x0400_020A, 0x0400_020B => {},
|
||||||
|
|
||||||
|
0x0400_0300 => bus.io.postflg = @intToEnum(PostFlag, value & 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", .{}),
|
||||||
|
|
||||||
0x0400_0410 => log.debug("Wrote 0x{X:0>2} to the common yet undocumented 0x{X:0>8}", .{ value, address }),
|
0x0400_0410 => log.debug("Wrote 0x{X:0>2} to the common yet undocumented 0x{X:0>8}", .{ value, address }),
|
||||||
|
@ -576,3 +606,19 @@ pub const SoundBias = extern union {
|
||||||
sampling_cycle: Bitfield(u16, 14, 2),
|
sampling_cycle: Bitfield(u16, 14, 2),
|
||||||
raw: u16,
|
raw: u16,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Read / Write
|
||||||
|
pub const WaitControl = extern union {
|
||||||
|
sram_cnt: Bitfield(u16, 0, 2),
|
||||||
|
s0_first: Bitfield(u16, 2, 2),
|
||||||
|
s0_second: Bit(u16, 4),
|
||||||
|
s1_first: Bitfield(u16, 5, 2),
|
||||||
|
s1_second: Bit(u16, 7),
|
||||||
|
s2_first: Bitfield(u16, 8, 2),
|
||||||
|
s2_second: Bit(u16, 10),
|
||||||
|
phi_out: Bitfield(u16, 11, 2),
|
||||||
|
|
||||||
|
prefetch_enable: Bit(u16, 14),
|
||||||
|
pak_kind: Bit(u16, 15),
|
||||||
|
raw: u16,
|
||||||
|
};
|
||||||
|
|
|
@ -190,19 +190,15 @@ fn Timer(comptime id: u2) type {
|
||||||
|
|
||||||
// Perform Cascade Behaviour
|
// Perform Cascade Behaviour
|
||||||
switch (id) {
|
switch (id) {
|
||||||
0 => if (cpu.bus.tim[1].cnt.cascade.read()) {
|
inline 0, 1, 2 => |idx| {
|
||||||
cpu.bus.tim[1]._counter +%= 1;
|
const next = idx + 1;
|
||||||
if (cpu.bus.tim[1]._counter == 0) cpu.bus.tim[1].onTimerExpire(cpu, late);
|
|
||||||
|
if (cpu.bus.tim[next].cnt.cascade.read()) {
|
||||||
|
cpu.bus.tim[next]._counter +%= 1;
|
||||||
|
if (cpu.bus.tim[next]._counter == 0) cpu.bus.tim[next].onTimerExpire(cpu, late);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
1 => if (cpu.bus.tim[2].cnt.cascade.read()) {
|
3 => {}, // THere is no timer for TIM3 to cascade to
|
||||||
cpu.bus.tim[2]._counter +%= 1;
|
|
||||||
if (cpu.bus.tim[2]._counter == 0) cpu.bus.tim[2].onTimerExpire(cpu, late);
|
|
||||||
},
|
|
||||||
2 => if (cpu.bus.tim[3].cnt.cascade.read()) {
|
|
||||||
cpu.bus.tim[3]._counter +%= 1;
|
|
||||||
if (cpu.bus.tim[3]._counter == 0) cpu.bus.tim[3].onTimerExpire(cpu, late);
|
|
||||||
},
|
|
||||||
3 => {}, // There is no Timer for TIM3 to "cascade" to,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reschedule Timer if we're not cascading
|
// Reschedule Timer if we're not cascading
|
||||||
|
|
|
@ -455,29 +455,12 @@ pub const Arm7tdmi = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stepDmaTransfer(self: *Self) bool {
|
pub fn stepDmaTransfer(self: *Self) bool {
|
||||||
const dma0 = &self.bus.dma[0];
|
comptime var i: usize = 0;
|
||||||
const dma1 = &self.bus.dma[1];
|
inline while (i < 4) : (i += 1) {
|
||||||
const dma2 = &self.bus.dma[2];
|
if (self.bus.dma[i].in_progress) {
|
||||||
const dma3 = &self.bus.dma[3];
|
self.bus.dma[i].step(self);
|
||||||
|
|
||||||
if (dma0.in_progress) {
|
|
||||||
dma0.step(self);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dma1.in_progress) {
|
|
||||||
dma1.step(self);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dma2.in_progress) {
|
|
||||||
dma2.step(self);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dma3.in_progress) {
|
|
||||||
dma3.step(self);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -105,8 +105,8 @@ pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn audioSync(audio_sync: bool, stream: *SDL.SDL_AudioStream, is_buffer_full: *bool) void {
|
fn audioSync(audio_sync: bool, stream: *SDL.SDL_AudioStream, is_buffer_full: *bool) void {
|
||||||
comptime std.debug.assert(@import("../platform.zig").sample_format == SDL.AUDIO_F32);
|
comptime std.debug.assert(@import("../platform.zig").sample_format == SDL.AUDIO_U16);
|
||||||
const sample_size = 2 * @sizeOf(f32);
|
const sample_size = 2 * @sizeOf(u16);
|
||||||
const max_buf_size: c_int = 0x400;
|
const max_buf_size: c_int = 0x400;
|
||||||
|
|
||||||
// Determine whether the APU is busy right at this moment
|
// Determine whether the APU is busy right at this moment
|
||||||
|
|
|
@ -46,10 +46,7 @@ pub const Scheduler = struct {
|
||||||
},
|
},
|
||||||
.TimerOverflow => |id| {
|
.TimerOverflow => |id| {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
0 => cpu.bus.tim[0].onTimerExpire(cpu, late),
|
inline 0...3 => |idx| cpu.bus.tim[idx].onTimerExpire(cpu, late),
|
||||||
1 => cpu.bus.tim[1].onTimerExpire(cpu, late),
|
|
||||||
2 => cpu.bus.tim[2].onTimerExpire(cpu, late),
|
|
||||||
3 => cpu.bus.tim[3].onTimerExpire(cpu, late),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.ApuChannel => |id| {
|
.ApuChannel => |id| {
|
||||||
|
|
|
@ -14,8 +14,8 @@ const span = @import("util.zig").span;
|
||||||
const gba_width = @import("core/ppu.zig").width;
|
const gba_width = @import("core/ppu.zig").width;
|
||||||
const gba_height = @import("core/ppu.zig").height;
|
const gba_height = @import("core/ppu.zig").height;
|
||||||
|
|
||||||
pub const sample_rate = 44100;
|
pub const sample_rate = 1 << 15;
|
||||||
pub const sample_format = SDL.AUDIO_F32;
|
pub const sample_format = SDL.AUDIO_U16;
|
||||||
|
|
||||||
const default_title: []const u8 = "ZBA";
|
const default_title: []const u8 = "ZBA";
|
||||||
|
|
||||||
|
@ -203,8 +203,8 @@ pub const Gui = struct {
|
||||||
SDL.SDLK_RETURN => io.keyinput.start.set(),
|
SDL.SDLK_RETURN => io.keyinput.start.set(),
|
||||||
SDL.SDLK_RSHIFT => io.keyinput.select.set(),
|
SDL.SDLK_RSHIFT => io.keyinput.select.set(),
|
||||||
SDL.SDLK_i => {
|
SDL.SDLK_i => {
|
||||||
comptime std.debug.assert(sample_format == SDL.AUDIO_F32);
|
comptime std.debug.assert(sample_format == SDL.AUDIO_U16);
|
||||||
log.err("Sample Count: {}", .{@intCast(u32, SDL.SDL_AudioStreamAvailable(cpu.bus.apu.stream)) / (2 * @sizeOf(f32))});
|
log.err("Sample Count: {}", .{@intCast(u32, SDL.SDL_AudioStreamAvailable(cpu.bus.apu.stream)) / (2 * @sizeOf(u16))});
|
||||||
},
|
},
|
||||||
SDL.SDLK_j => log.err("Scheduler Capacity: {} | Scheduler Event Count: {}", .{ scheduler.queue.capacity(), scheduler.queue.count() }),
|
SDL.SDLK_j => log.err("Scheduler Capacity: {} | Scheduler Event Count: {}", .{ scheduler.queue.capacity(), scheduler.queue.count() }),
|
||||||
SDL.SDLK_k => {
|
SDL.SDLK_k => {
|
||||||
|
@ -271,8 +271,8 @@ const Audio = struct {
|
||||||
want.callback = Self.callback;
|
want.callback = Self.callback;
|
||||||
want.userdata = apu;
|
want.userdata = apu;
|
||||||
|
|
||||||
std.debug.assert(sample_format == SDL.AUDIO_F32);
|
std.debug.assert(sample_format == SDL.AUDIO_U16);
|
||||||
log.info("Host Sample Rate: {}Hz, Host Format: SDL.AUDIO_F32", .{sample_rate});
|
log.info("Host Sample Rate: {}Hz, Host Format: SDL.AUDIO_U16", .{sample_rate});
|
||||||
|
|
||||||
const device = SDL.SDL_OpenAudioDevice(null, 0, &want, &have, 0);
|
const device = SDL.SDL_OpenAudioDevice(null, 0, &want, &have, 0);
|
||||||
if (device == 0) panic();
|
if (device == 0) panic();
|
||||||
|
|
Loading…
Reference in New Issue