Compare commits
No commits in common. "472215b4c20163a5ba08c7bc8aef021d758b0db0" and "7debdc490d6d5eee663c11b4371bac028945f2fa" have entirely different histories.
472215b4c2
...
7debdc490d
122
src/core/apu.zig
122
src/core/apu.zig
|
@ -12,8 +12,9 @@ const Noise = @import("apu/Noise.zig");
|
||||||
|
|
||||||
const SoundFifo = std.fifo.LinearFifo(u8, .{ .Static = 0x20 });
|
const SoundFifo = std.fifo.LinearFifo(u8, .{ .Static = 0x20 });
|
||||||
|
|
||||||
const getHalf = util.getHalf;
|
const setHi = util.setHi;
|
||||||
const setHalf = util.setHalf;
|
const setLo = util.setLo;
|
||||||
|
const shift = util.shift;
|
||||||
const intToBytes = util.intToBytes;
|
const intToBytes = util.intToBytes;
|
||||||
|
|
||||||
const log = std.log.scoped(.APU);
|
const log = std.log.scoped(.APU);
|
||||||
|
@ -22,10 +23,10 @@ pub const host_rate = @import("../platform.zig").sample_rate;
|
||||||
pub const host_format = @import("../platform.zig").sample_format;
|
pub const host_format = @import("../platform.zig").sample_format;
|
||||||
|
|
||||||
pub fn read(comptime T: type, apu: *const Apu, addr: u32) ?T {
|
pub fn read(comptime T: type, apu: *const Apu, addr: u32) ?T {
|
||||||
const byte_addr = @truncate(u8, addr);
|
const byte = @truncate(u8, addr);
|
||||||
|
|
||||||
return switch (T) {
|
return switch (T) {
|
||||||
u32 => switch (byte_addr) {
|
u32 => switch (byte) {
|
||||||
0x60 => @as(T, apu.ch1.sound1CntH()) << 16 | apu.ch1.sound1CntL(),
|
0x60 => @as(T, apu.ch1.sound1CntH()) << 16 | apu.ch1.sound1CntL(),
|
||||||
0x64 => apu.ch1.sound1CntX(),
|
0x64 => apu.ch1.sound1CntX(),
|
||||||
0x68 => apu.ch2.sound2CntL(),
|
0x68 => apu.ch2.sound2CntL(),
|
||||||
|
@ -38,12 +39,12 @@ pub fn read(comptime T: type, apu: *const Apu, addr: u32) ?T {
|
||||||
0x84 => apu.soundCntX(),
|
0x84 => apu.soundCntX(),
|
||||||
0x88 => apu.bias.raw, // SOUNDBIAS, high is unused
|
0x88 => apu.bias.raw, // SOUNDBIAS, high is unused
|
||||||
0x8C => null,
|
0x8C => null,
|
||||||
0x90, 0x94, 0x98, 0x9C => apu.ch3.wave_dev.read(T, apu.ch3.select, addr),
|
0x90, 0x94, 0x8, 0x9C => apu.ch3.wave_dev.read(T, apu.ch3.select, addr),
|
||||||
0xA0 => null, // FIFO_A
|
0xA0 => null, // FIFO_A
|
||||||
0xA4 => null, // FIFO_B
|
0xA4 => null, // FIFO_B
|
||||||
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
||||||
},
|
},
|
||||||
u16 => switch (byte_addr) {
|
u16 => switch (byte) {
|
||||||
0x60 => apu.ch1.sound1CntL(),
|
0x60 => apu.ch1.sound1CntL(),
|
||||||
0x62 => apu.ch1.sound1CntH(),
|
0x62 => apu.ch1.sound1CntH(),
|
||||||
0x64 => apu.ch1.sound1CntX(),
|
0x64 => apu.ch1.sound1CntX(),
|
||||||
|
@ -72,28 +73,28 @@ pub fn read(comptime T: type, apu: *const Apu, addr: u32) ?T {
|
||||||
0xA4, 0xA6 => null, // FIFO_B
|
0xA4, 0xA6 => null, // FIFO_B
|
||||||
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
||||||
},
|
},
|
||||||
u8 => switch (byte_addr) {
|
u8 => switch (byte) {
|
||||||
0x60, 0x61 => @truncate(T, @as(u16, apu.ch1.sound1CntL()) >> getHalf(byte_addr)),
|
0x60, 0x61 => @truncate(T, @as(u16, apu.ch1.sound1CntL()) >> shift(byte)),
|
||||||
0x62, 0x63 => @truncate(T, apu.ch1.sound1CntH() >> getHalf(byte_addr)),
|
0x62, 0x63 => @truncate(T, apu.ch1.sound1CntH() >> shift(byte)),
|
||||||
0x64, 0x65 => @truncate(T, apu.ch1.sound1CntX() >> getHalf(byte_addr)),
|
0x64, 0x65 => @truncate(T, apu.ch1.sound1CntX() >> shift(byte)),
|
||||||
0x66, 0x67 => 0x00, // assuming behaviour is identical to that of 16-bit reads
|
0x66, 0x67 => 0x00, // assuming behaviour is identical to that of 16-bit reads
|
||||||
0x68, 0x69 => @truncate(T, apu.ch2.sound2CntL() >> getHalf(byte_addr)),
|
0x68, 0x69 => @truncate(T, apu.ch2.sound2CntL() >> shift(byte)),
|
||||||
0x6A, 0x6B => 0x00,
|
0x6A, 0x6B => 0x00,
|
||||||
0x6C, 0x6D => @truncate(T, apu.ch2.sound2CntH() >> getHalf(byte_addr)),
|
0x6C, 0x6D => @truncate(T, apu.ch2.sound2CntH() >> shift(byte)),
|
||||||
0x6E, 0x6F => 0x00,
|
0x6E, 0x6F => 0x00,
|
||||||
0x70, 0x71 => @truncate(T, @as(u16, apu.ch3.sound3CntL()) >> getHalf(byte_addr)), // SOUND3CNT_L
|
0x70, 0x71 => @truncate(T, @as(u16, apu.ch3.sound3CntL()) >> shift(byte)), // SOUND3CNT_L
|
||||||
0x72, 0x73 => @truncate(T, apu.ch3.sound3CntH() >> getHalf(byte_addr)),
|
0x72, 0x73 => @truncate(T, apu.ch3.sound3CntH() >> shift(byte)),
|
||||||
0x74, 0x75 => @truncate(T, apu.ch3.sound3CntX() >> getHalf(byte_addr)), // SOUND3CNT_L
|
0x74, 0x75 => @truncate(T, apu.ch3.sound3CntX() >> shift(byte)), // SOUND3CNT_L
|
||||||
0x76, 0x77 => 0x00,
|
0x76, 0x77 => 0x00,
|
||||||
0x78, 0x79 => @truncate(T, apu.ch4.sound4CntL() >> getHalf(byte_addr)),
|
0x78, 0x79 => @truncate(T, apu.ch4.sound4CntL() >> shift(byte)),
|
||||||
0x7A, 0x7B => 0x00,
|
0x7A, 0x7B => 0x00,
|
||||||
0x7C, 0x7D => @truncate(T, apu.ch4.sound4CntH() >> getHalf(byte_addr)),
|
0x7C, 0x7D => @truncate(T, apu.ch4.sound4CntH() >> shift(byte)),
|
||||||
0x7E, 0x7F => 0x00,
|
0x7E, 0x7F => 0x00,
|
||||||
0x80, 0x81 => @truncate(T, apu.soundCntL() >> getHalf(byte_addr)), // SOUNDCNT_L
|
0x80, 0x81 => @truncate(T, apu.soundCntL() >> shift(byte)), // SOUNDCNT_L
|
||||||
0x82, 0x83 => @truncate(T, apu.soundCntH() >> getHalf(byte_addr)), // SOUNDCNT_H
|
0x82, 0x83 => @truncate(T, apu.soundCntH() >> shift(byte)), // SOUNDCNT_H
|
||||||
0x84, 0x85 => @truncate(T, @as(u16, apu.soundCntX()) >> getHalf(byte_addr)),
|
0x84, 0x85 => @truncate(T, @as(u16, apu.soundCntX()) >> shift(byte)),
|
||||||
0x86, 0x87 => 0x00,
|
0x86, 0x87 => 0x00,
|
||||||
0x88, 0x89 => @truncate(T, apu.bias.raw >> getHalf(byte_addr)), // SOUNDBIAS
|
0x88, 0x89 => @truncate(T, apu.bias.raw >> shift(byte)), // SOUNDBIAS
|
||||||
0x8A, 0x8B => 0x00,
|
0x8A, 0x8B => 0x00,
|
||||||
0x8C...0x8F => null,
|
0x8C...0x8F => null,
|
||||||
0x90...0x9F => apu.ch3.wave_dev.read(T, apu.ch3.select, addr),
|
0x90...0x9F => apu.ch3.wave_dev.read(T, apu.ch3.select, addr),
|
||||||
|
@ -106,107 +107,79 @@ pub fn read(comptime T: type, apu: *const Apu, addr: u32) ?T {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(comptime T: type, apu: *Apu, addr: u32, value: T) void {
|
pub fn write(comptime T: type, apu: *Apu, addr: u32, value: T) void {
|
||||||
const byte_addr = @truncate(u8, addr);
|
const byte = @truncate(u8, addr);
|
||||||
|
|
||||||
switch (T) {
|
switch (T) {
|
||||||
u32 => switch (byte_addr) {
|
u32 => switch (byte) {
|
||||||
0x60 => apu.ch1.setSound1Cnt(value),
|
0x60 => apu.ch1.setSound1Cnt(value),
|
||||||
0x64 => apu.ch1.setSound1CntX(&apu.fs, @truncate(u16, value)),
|
0x64 => apu.ch1.setSound1CntX(&apu.fs, @truncate(u16, value)),
|
||||||
|
|
||||||
0x68 => apu.ch2.setSound2CntL(@truncate(u16, value)),
|
0x68 => apu.ch2.setSound2CntL(@truncate(u16, value)),
|
||||||
0x6C => apu.ch2.setSound2CntH(&apu.fs, @truncate(u16, value)),
|
0x6C => apu.ch2.setSound2CntH(&apu.fs, @truncate(u16, value)),
|
||||||
|
|
||||||
0x70 => apu.ch3.setSound3Cnt(value),
|
0x70 => apu.ch3.setSound3Cnt(value),
|
||||||
0x74 => apu.ch3.setSound3CntX(&apu.fs, @truncate(u16, value)),
|
0x74 => apu.ch3.setSound3CntX(&apu.fs, @truncate(u16, value)),
|
||||||
|
|
||||||
0x78 => apu.ch4.setSound4CntL(@truncate(u16, value)),
|
0x78 => apu.ch4.setSound4CntL(@truncate(u16, value)),
|
||||||
0x7C => apu.ch4.setSound4CntH(&apu.fs, @truncate(u16, value)),
|
0x7C => apu.ch4.setSound4CntH(&apu.fs, @truncate(u16, value)),
|
||||||
|
|
||||||
0x80 => apu.setSoundCnt(value),
|
0x80 => apu.setSoundCnt(value),
|
||||||
0x84 => apu.setSoundCntX(value >> 7 & 1 == 1),
|
// WAVE_RAM
|
||||||
0x88 => apu.bias.raw = @truncate(u16, value),
|
0x90...0x9F => apu.ch3.wave_dev.write(T, apu.ch3.select, addr, value),
|
||||||
0x8C => {},
|
|
||||||
|
|
||||||
0x90, 0x94, 0x98, 0x9C => apu.ch3.wave_dev.write(T, apu.ch3.select, addr, value),
|
|
||||||
0xA0 => apu.chA.push(value), // FIFO_A
|
0xA0 => apu.chA.push(value), // FIFO_A
|
||||||
0xA4 => apu.chB.push(value), // FIFO_B
|
0xA4 => apu.chB.push(value), // FIFO_B
|
||||||
else => util.io.write.undef(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, addr }),
|
else => util.io.write.undef(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, addr }),
|
||||||
},
|
},
|
||||||
u16 => switch (byte_addr) {
|
u16 => switch (byte) {
|
||||||
0x60 => apu.ch1.setSound1CntL(@truncate(u8, value)), // SOUND1CNT_L
|
0x60 => apu.ch1.setSound1CntL(@truncate(u8, value)), // SOUND1CNT_L
|
||||||
0x62 => apu.ch1.setSound1CntH(value),
|
0x62 => apu.ch1.setSound1CntH(value),
|
||||||
0x64 => apu.ch1.setSound1CntX(&apu.fs, value),
|
0x64 => apu.ch1.setSound1CntX(&apu.fs, value),
|
||||||
0x66 => {},
|
|
||||||
|
|
||||||
0x68 => apu.ch2.setSound2CntL(value),
|
0x68 => apu.ch2.setSound2CntL(value),
|
||||||
0x6A => {},
|
|
||||||
0x6C => apu.ch2.setSound2CntH(&apu.fs, value),
|
0x6C => apu.ch2.setSound2CntH(&apu.fs, value),
|
||||||
0x6E => {},
|
|
||||||
|
|
||||||
0x70 => apu.ch3.setSound3CntL(@truncate(u8, value)),
|
0x70 => apu.ch3.setSound3CntL(@truncate(u8, value)),
|
||||||
0x72 => apu.ch3.setSound3CntH(value),
|
0x72 => apu.ch3.setSound3CntH(value),
|
||||||
0x74 => apu.ch3.setSound3CntX(&apu.fs, value),
|
0x74 => apu.ch3.setSound3CntX(&apu.fs, value),
|
||||||
0x76 => {},
|
|
||||||
|
|
||||||
0x78 => apu.ch4.setSound4CntL(value),
|
0x78 => apu.ch4.setSound4CntL(value),
|
||||||
0x7A => {},
|
|
||||||
0x7C => apu.ch4.setSound4CntH(&apu.fs, value),
|
0x7C => apu.ch4.setSound4CntH(&apu.fs, value),
|
||||||
0x7E => {},
|
|
||||||
|
|
||||||
0x80 => apu.setSoundCntL(value),
|
0x80 => apu.psg_cnt.raw = value, // SOUNDCNT_L
|
||||||
0x82 => apu.setSoundCntH(value),
|
0x82 => apu.setSoundCntH(value),
|
||||||
0x84 => apu.setSoundCntX(value >> 7 & 1 == 1),
|
0x84 => apu.setSoundCntX(value >> 7 & 1 == 1),
|
||||||
0x86 => {},
|
|
||||||
0x88 => apu.bias.raw = value, // SOUNDBIAS
|
0x88 => apu.bias.raw = value, // SOUNDBIAS
|
||||||
0x8A, 0x8C, 0x8E => {},
|
// WAVE_RAM
|
||||||
|
0x90...0x9F => apu.ch3.wave_dev.write(T, apu.ch3.select, addr, value),
|
||||||
0x90, 0x92, 0x94, 0x96, 0x98, 0x9A, 0x9C, 0x9E => apu.ch3.wave_dev.write(T, apu.ch3.select, addr, value),
|
|
||||||
0xA0, 0xA2 => log.err("Tried to write 0x{X:0>4}{} to FIFO_A", .{ value, T }),
|
|
||||||
0xA4, 0xA6 => log.err("Tried to write 0x{X:0>4}{} to FIFO_B", .{ value, T }),
|
|
||||||
else => util.io.write.undef(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, addr }),
|
else => util.io.write.undef(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, addr }),
|
||||||
},
|
},
|
||||||
u8 => switch (byte_addr) {
|
u8 => switch (byte) {
|
||||||
0x60 => apu.ch1.setSound1CntL(value),
|
0x60 => apu.ch1.setSound1CntL(value),
|
||||||
0x61 => {},
|
|
||||||
0x62 => apu.ch1.setNr11(value),
|
0x62 => apu.ch1.setNr11(value),
|
||||||
0x63 => apu.ch1.setNr12(value),
|
0x63 => apu.ch1.setNr12(value),
|
||||||
0x64 => apu.ch1.setNr13(value),
|
0x64 => apu.ch1.setNr13(value),
|
||||||
0x65 => apu.ch1.setNr14(&apu.fs, value),
|
0x65 => apu.ch1.setNr14(&apu.fs, value),
|
||||||
0x66, 0x67 => {},
|
|
||||||
|
|
||||||
0x68 => apu.ch2.setNr21(value),
|
0x68 => apu.ch2.setNr21(value),
|
||||||
0x69 => apu.ch2.setNr22(value),
|
0x69 => apu.ch2.setNr22(value),
|
||||||
0x6A, 0x6B => {},
|
|
||||||
0x6C => apu.ch2.setNr23(value),
|
0x6C => apu.ch2.setNr23(value),
|
||||||
0x6D => apu.ch2.setNr24(&apu.fs, value),
|
0x6D => apu.ch2.setNr24(&apu.fs, value),
|
||||||
0x6E, 0x6F => {},
|
|
||||||
|
|
||||||
0x70 => apu.ch3.setSound3CntL(value), // NR30
|
0x70 => apu.ch3.setSound3CntL(value), // NR30
|
||||||
0x71 => {},
|
|
||||||
0x72 => apu.ch3.setNr31(value),
|
0x72 => apu.ch3.setNr31(value),
|
||||||
0x73 => apu.ch3.vol.raw = value, // NR32
|
0x73 => apu.ch3.vol.raw = value, // NR32
|
||||||
0x74 => apu.ch3.setNr33(value),
|
0x74 => apu.ch3.setNr33(value),
|
||||||
0x75 => apu.ch3.setNr34(&apu.fs, value),
|
0x75 => apu.ch3.setNr34(&apu.fs, value),
|
||||||
0x76, 0x77 => {},
|
|
||||||
|
|
||||||
0x78 => apu.ch4.setNr41(value),
|
0x78 => apu.ch4.setNr41(value),
|
||||||
0x79 => apu.ch4.setNr42(value),
|
0x79 => apu.ch4.setNr42(value),
|
||||||
0x7A, 0x7B => {},
|
|
||||||
0x7C => apu.ch4.poly.raw = value, // NR 43
|
0x7C => apu.ch4.poly.raw = value, // NR 43
|
||||||
0x7D => apu.ch4.setNr44(&apu.fs, value),
|
0x7D => apu.ch4.setNr44(&apu.fs, value),
|
||||||
0x7E, 0x7F => {},
|
|
||||||
|
|
||||||
0x80, 0x81 => apu.setSoundCntL(setHalf(u16, apu.psg_cnt.raw, byte_addr, value)),
|
|
||||||
0x82, 0x83 => apu.setSoundCntH(setHalf(u16, apu.dma_cnt.raw, byte_addr, value)),
|
|
||||||
0x84 => apu.setSoundCntX(value >> 7 & 1 == 1),
|
|
||||||
0x85 => {},
|
|
||||||
0x86, 0x87 => {},
|
|
||||||
0x88, 0x89 => apu.bias.raw = setHalf(u16, apu.bias.raw, byte_addr, value), // SOUNDBIAS
|
|
||||||
0x8A...0x8F => {},
|
|
||||||
|
|
||||||
|
0x80 => apu.setNr50(value),
|
||||||
|
0x81 => apu.setNr51(value),
|
||||||
|
0x82 => apu.setSoundCntH(setLo(u16, apu.dma_cnt.raw, value)),
|
||||||
|
0x83 => apu.setSoundCntH(setHi(u16, apu.dma_cnt.raw, value)),
|
||||||
|
0x84 => apu.setSoundCntX(value >> 7 & 1 == 1), // NR52
|
||||||
|
0x89 => apu.setSoundBiasH(value),
|
||||||
0x90...0x9F => apu.ch3.wave_dev.write(T, apu.ch3.select, addr, value),
|
0x90...0x9F => apu.ch3.wave_dev.write(T, apu.ch3.select, addr, value),
|
||||||
0xA0...0xA3 => log.err("Tried to write 0x{X:0>2}{} to FIFO_A", .{ value, T }),
|
|
||||||
0xA4...0xA7 => log.err("Tried to write 0x{X:0>2}{} to FIFO_B", .{ value, T }),
|
|
||||||
else => util.io.write.undef(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, addr }),
|
else => util.io.write.undef(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, addr }),
|
||||||
},
|
},
|
||||||
else => @compileError("APU: Unsupported write width"),
|
else => @compileError("APU: Unsupported write width"),
|
||||||
|
@ -283,7 +256,7 @@ pub const Apu = struct {
|
||||||
|
|
||||||
/// SOUNDCNT
|
/// SOUNDCNT
|
||||||
fn setSoundCnt(self: *Self, value: u32) void {
|
fn setSoundCnt(self: *Self, value: u32) void {
|
||||||
self.setSoundCntL(@truncate(u16, value));
|
self.psg_cnt.raw = @truncate(u16, value);
|
||||||
self.setSoundCntH(@truncate(u16, value >> 16));
|
self.setSoundCntH(@truncate(u16, value >> 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,11 +265,6 @@ pub const Apu = struct {
|
||||||
return self.psg_cnt.raw & 0xFF77;
|
return self.psg_cnt.raw & 0xFF77;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// SOUNDCNT_L
|
|
||||||
pub fn setSoundCntL(self: *Self, value: u16) void {
|
|
||||||
self.psg_cnt.raw = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// SOUNDCNT_H
|
/// SOUNDCNT_H
|
||||||
pub fn setSoundCntH(self: *Self, value: u16) void {
|
pub fn setSoundCntH(self: *Self, value: u16) void {
|
||||||
const new: io.DmaSoundControl = .{ .raw = value };
|
const new: io.DmaSoundControl = .{ .raw = value };
|
||||||
|
@ -344,6 +312,20 @@ pub const Apu = struct {
|
||||||
return apu_enable << 7 | ch4_enable << 3 | ch3_enable << 2 | ch2_enable << 1 | ch1_enable;
|
return apu_enable << 7 | ch4_enable << 3 | ch3_enable << 2 | ch2_enable << 1 | ch1_enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// NR50
|
||||||
|
pub fn setNr50(self: *Self, byte: u8) void {
|
||||||
|
self.psg_cnt.raw = (self.psg_cnt.raw & 0xFF00) | byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// NR51
|
||||||
|
pub fn setNr51(self: *Self, byte: u8) void {
|
||||||
|
self.psg_cnt.raw = @as(u16, byte) << 8 | (self.psg_cnt.raw & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setSoundBiasH(self: *Self, byte: u8) void {
|
||||||
|
self.bias.raw = (@as(u16, byte) << 8) | (self.bias.raw & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sampleAudio(self: *Self, late: u64) void {
|
pub fn sampleAudio(self: *Self, late: u64) void {
|
||||||
self.sched.push(.SampleAudio, self.interval() -| late);
|
self.sched.push(.SampleAudio, self.interval() -| late);
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,9 @@ const Arm7tdmi = @import("../cpu.zig").Arm7tdmi;
|
||||||
pub const DmaTuple = std.meta.Tuple(&[_]type{ DmaController(0), DmaController(1), DmaController(2), DmaController(3) });
|
pub const DmaTuple = std.meta.Tuple(&[_]type{ DmaController(0), DmaController(1), DmaController(2), DmaController(3) });
|
||||||
const log = std.log.scoped(.DmaTransfer);
|
const log = std.log.scoped(.DmaTransfer);
|
||||||
|
|
||||||
const getHalf = util.getHalf;
|
const setHi = util.setHi;
|
||||||
const setHalf = util.setHalf;
|
const setLo = util.setLo;
|
||||||
const setQuart = util.setQuart;
|
const shift = util.shift;
|
||||||
|
|
||||||
const rotr = @import("../../util.zig").rotr;
|
const rotr = @import("../../util.zig").rotr;
|
||||||
|
|
||||||
|
@ -19,10 +19,10 @@ pub fn create() DmaTuple {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(comptime T: type, dma: *const DmaTuple, addr: u32) ?T {
|
pub fn read(comptime T: type, dma: *const DmaTuple, addr: u32) ?T {
|
||||||
const byte_addr = @truncate(u8, addr);
|
const byte = @truncate(u8, addr);
|
||||||
|
|
||||||
return switch (T) {
|
return switch (T) {
|
||||||
u32 => switch (byte_addr) {
|
u32 => switch (byte) {
|
||||||
0xB0, 0xB4 => null, // DMA0SAD, DMA0DAD,
|
0xB0, 0xB4 => null, // DMA0SAD, DMA0DAD,
|
||||||
0xB8 => @as(T, dma.*[0].dmacntH()) << 16, // DMA0CNT_L is write-only
|
0xB8 => @as(T, dma.*[0].dmacntH()) << 16, // DMA0CNT_L is write-only
|
||||||
0xBC, 0xC0 => null, // DMA1SAD, DMA1DAD
|
0xBC, 0xC0 => null, // DMA1SAD, DMA1DAD
|
||||||
|
@ -33,7 +33,7 @@ pub fn read(comptime T: type, dma: *const DmaTuple, addr: u32) ?T {
|
||||||
0xDC => @as(T, dma.*[3].dmacntH()) << 16, // DMA3CNT_L is write-only
|
0xDC => @as(T, dma.*[3].dmacntH()) << 16, // DMA3CNT_L is write-only
|
||||||
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
||||||
},
|
},
|
||||||
u16 => switch (byte_addr) {
|
u16 => switch (byte) {
|
||||||
0xB0, 0xB2, 0xB4, 0xB6 => null, // DMA0SAD, DMA0DAD
|
0xB0, 0xB2, 0xB4, 0xB6 => null, // DMA0SAD, DMA0DAD
|
||||||
0xB8 => 0x0000, // DMA0CNT_L, suite.gba expects 0x0000 instead of 0xDEAD
|
0xB8 => 0x0000, // DMA0CNT_L, suite.gba expects 0x0000 instead of 0xDEAD
|
||||||
0xBA => dma.*[0].dmacntH(),
|
0xBA => dma.*[0].dmacntH(),
|
||||||
|
@ -51,22 +51,22 @@ pub fn read(comptime T: type, dma: *const DmaTuple, addr: u32) ?T {
|
||||||
0xDE => dma.*[3].dmacntH(),
|
0xDE => dma.*[3].dmacntH(),
|
||||||
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
||||||
},
|
},
|
||||||
u8 => switch (byte_addr) {
|
u8 => switch (byte) {
|
||||||
0xB0...0xB7 => null, // DMA0SAD, DMA0DAD
|
0xB0...0xB7 => null, // DMA0SAD, DMA0DAD
|
||||||
0xB8, 0xB9 => 0x00, // DMA0CNT_L
|
0xB8, 0xB9 => 0x00, // DMA0CNT_L
|
||||||
0xBA, 0xBB => @truncate(T, dma.*[0].dmacntH() >> getHalf(byte_addr)),
|
0xBA, 0xBB => @truncate(T, dma.*[0].dmacntH() >> shift(byte)),
|
||||||
|
|
||||||
0xBC...0xC3 => null, // DMA1SAD, DMA1DAD
|
0xBC...0xC3 => null, // DMA1SAD, DMA1DAD
|
||||||
0xC4, 0xC5 => 0x00, // DMA1CNT_L
|
0xC4, 0xC5 => 0x00, // DMA1CNT_L
|
||||||
0xC6, 0xC7 => @truncate(T, dma.*[1].dmacntH() >> getHalf(byte_addr)),
|
0xC6, 0xC7 => @truncate(T, dma.*[1].dmacntH() >> shift(byte)),
|
||||||
|
|
||||||
0xC8...0xCF => null, // DMA2SAD, DMA2DAD
|
0xC8...0xCF => null, // DMA2SAD, DMA2DAD
|
||||||
0xD0, 0xD1 => 0x00, // DMA2CNT_L
|
0xD0, 0xD1 => 0x00, // DMA2CNT_L
|
||||||
0xD2, 0xD3 => @truncate(T, dma.*[2].dmacntH() >> getHalf(byte_addr)),
|
0xD2, 0xD3 => @truncate(T, dma.*[2].dmacntH() >> shift(byte)),
|
||||||
|
|
||||||
0xD4...0xDB => null, // DMA3SAD, DMA3DAD
|
0xD4...0xDB => null, // DMA3SAD, DMA3DAD
|
||||||
0xDC, 0xDD => 0x00, // DMA3CNT_L
|
0xDC, 0xDD => 0x00, // DMA3CNT_L
|
||||||
0xDE, 0xDF => @truncate(T, dma.*[3].dmacntH() >> getHalf(byte_addr)),
|
0xDE, 0xDF => @truncate(T, dma.*[3].dmacntH() >> shift(byte)),
|
||||||
else => util.io.read.err(T, log, "unexpected {} read from 0x{X:0>8}", .{ T, addr }),
|
else => util.io.read.err(T, log, "unexpected {} read from 0x{X:0>8}", .{ T, addr }),
|
||||||
},
|
},
|
||||||
else => @compileError("DMA: Unsupported read width"),
|
else => @compileError("DMA: Unsupported read width"),
|
||||||
|
@ -74,71 +74,55 @@ pub fn read(comptime T: type, dma: *const DmaTuple, addr: u32) ?T {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(comptime T: type, dma: *DmaTuple, addr: u32, value: T) void {
|
pub fn write(comptime T: type, dma: *DmaTuple, addr: u32, value: T) void {
|
||||||
const byte_addr = @truncate(u8, addr);
|
const byte = @truncate(u8, addr);
|
||||||
|
|
||||||
switch (T) {
|
switch (T) {
|
||||||
u32 => switch (byte_addr) {
|
u32 => switch (byte) {
|
||||||
0xB0 => dma.*[0].setDmasad(value),
|
0xB0 => dma.*[0].setDmasad(value),
|
||||||
0xB4 => dma.*[0].setDmadad(value),
|
0xB4 => dma.*[0].setDmadad(value),
|
||||||
0xB8 => dma.*[0].setDmacnt(value),
|
0xB8 => dma.*[0].setDmacnt(value),
|
||||||
|
|
||||||
0xBC => dma.*[1].setDmasad(value),
|
0xBC => dma.*[1].setDmasad(value),
|
||||||
0xC0 => dma.*[1].setDmadad(value),
|
0xC0 => dma.*[1].setDmadad(value),
|
||||||
0xC4 => dma.*[1].setDmacnt(value),
|
0xC4 => dma.*[1].setDmacnt(value),
|
||||||
|
|
||||||
0xC8 => dma.*[2].setDmasad(value),
|
0xC8 => dma.*[2].setDmasad(value),
|
||||||
0xCC => dma.*[2].setDmadad(value),
|
0xCC => dma.*[2].setDmadad(value),
|
||||||
0xD0 => dma.*[2].setDmacnt(value),
|
0xD0 => dma.*[2].setDmacnt(value),
|
||||||
|
|
||||||
0xD4 => dma.*[3].setDmasad(value),
|
0xD4 => dma.*[3].setDmasad(value),
|
||||||
0xD8 => dma.*[3].setDmadad(value),
|
0xD8 => dma.*[3].setDmadad(value),
|
||||||
0xDC => dma.*[3].setDmacnt(value),
|
0xDC => dma.*[3].setDmacnt(value),
|
||||||
else => util.io.write.undef(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, addr }),
|
else => util.io.write.undef(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, addr }),
|
||||||
},
|
},
|
||||||
u16 => switch (byte_addr) {
|
u16 => switch (byte) {
|
||||||
0xB0, 0xB2 => dma.*[0].setDmasad(setHalf(u32, dma.*[0].sad, byte_addr, value)),
|
0xB0 => dma.*[0].setDmasad(setLo(u32, dma.*[0].sad, value)),
|
||||||
0xB4, 0xB6 => dma.*[0].setDmadad(setHalf(u32, dma.*[0].dad, byte_addr, value)),
|
0xB2 => dma.*[0].setDmasad(setHi(u32, dma.*[0].sad, value)),
|
||||||
|
0xB4 => dma.*[0].setDmadad(setLo(u32, dma.*[0].dad, value)),
|
||||||
|
0xB6 => dma.*[0].setDmadad(setHi(u32, dma.*[0].dad, value)),
|
||||||
0xB8 => dma.*[0].setDmacntL(value),
|
0xB8 => dma.*[0].setDmacntL(value),
|
||||||
0xBA => dma.*[0].setDmacntH(value),
|
0xBA => dma.*[0].setDmacntH(value),
|
||||||
|
|
||||||
0xBC, 0xBE => dma.*[1].setDmasad(setHalf(u32, dma.*[1].sad, byte_addr, value)),
|
0xBC => dma.*[1].setDmasad(setLo(u32, dma.*[1].sad, value)),
|
||||||
0xC0, 0xC2 => dma.*[1].setDmadad(setHalf(u32, dma.*[1].dad, byte_addr, value)),
|
0xBE => dma.*[1].setDmasad(setHi(u32, dma.*[1].sad, value)),
|
||||||
|
0xC0 => dma.*[1].setDmadad(setLo(u32, dma.*[1].dad, value)),
|
||||||
|
0xC2 => dma.*[1].setDmadad(setHi(u32, dma.*[1].dad, value)),
|
||||||
0xC4 => dma.*[1].setDmacntL(value),
|
0xC4 => dma.*[1].setDmacntL(value),
|
||||||
0xC6 => dma.*[1].setDmacntH(value),
|
0xC6 => dma.*[1].setDmacntH(value),
|
||||||
|
|
||||||
0xC8, 0xCA => dma.*[2].setDmasad(setHalf(u32, dma.*[2].sad, byte_addr, value)),
|
0xC8 => dma.*[2].setDmasad(setLo(u32, dma.*[2].sad, value)),
|
||||||
0xCC, 0xCE => dma.*[2].setDmadad(setHalf(u32, dma.*[2].dad, byte_addr, value)),
|
0xCA => dma.*[2].setDmasad(setHi(u32, dma.*[2].sad, value)),
|
||||||
|
0xCC => dma.*[2].setDmadad(setLo(u32, dma.*[2].dad, value)),
|
||||||
|
0xCE => dma.*[2].setDmadad(setHi(u32, dma.*[2].dad, value)),
|
||||||
0xD0 => dma.*[2].setDmacntL(value),
|
0xD0 => dma.*[2].setDmacntL(value),
|
||||||
0xD2 => dma.*[2].setDmacntH(value),
|
0xD2 => dma.*[2].setDmacntH(value),
|
||||||
|
|
||||||
0xD4, 0xD6 => dma.*[3].setDmasad(setHalf(u32, dma.*[3].sad, byte_addr, value)),
|
0xD4 => dma.*[3].setDmasad(setLo(u32, dma.*[3].sad, value)),
|
||||||
0xD8, 0xDA => dma.*[3].setDmadad(setHalf(u32, dma.*[3].dad, byte_addr, value)),
|
0xD6 => dma.*[3].setDmasad(setHi(u32, dma.*[3].sad, value)),
|
||||||
|
0xD8 => dma.*[3].setDmadad(setLo(u32, dma.*[3].dad, value)),
|
||||||
|
0xDA => dma.*[3].setDmadad(setHi(u32, dma.*[3].dad, value)),
|
||||||
0xDC => dma.*[3].setDmacntL(value),
|
0xDC => dma.*[3].setDmacntL(value),
|
||||||
0xDE => dma.*[3].setDmacntH(value),
|
0xDE => dma.*[3].setDmacntH(value),
|
||||||
else => util.io.write.undef(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, addr }),
|
else => util.io.write.undef(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, addr }),
|
||||||
},
|
},
|
||||||
u8 => switch (byte_addr) {
|
u8 => util.io.write.undef(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, addr }),
|
||||||
0xB0, 0xB1, 0xB2, 0xB3 => dma.*[0].setDmasad(setQuart(dma.*[0].sad, byte_addr, value)),
|
|
||||||
0xB4, 0xB5, 0xB6, 0xB7 => dma.*[0].setDmadad(setQuart(dma.*[0].dad, byte_addr, value)),
|
|
||||||
0xB8, 0xB9 => dma.*[0].setDmacntL(setHalf(u16, dma.*[0].word_count, byte_addr, value)),
|
|
||||||
0xBA, 0xBB => dma.*[0].setDmacntH(setHalf(u16, dma.*[0].cnt.raw, byte_addr, value)),
|
|
||||||
|
|
||||||
0xBC, 0xBD, 0xBE, 0xBF => dma.*[1].setDmasad(setQuart(dma.*[1].sad, byte_addr, value)),
|
|
||||||
0xC0, 0xC1, 0xC2, 0xC3 => dma.*[1].setDmadad(setQuart(dma.*[1].dad, byte_addr, value)),
|
|
||||||
0xC4, 0xC5 => dma.*[1].setDmacntL(setHalf(u16, dma.*[1].word_count, byte_addr, value)),
|
|
||||||
0xC6, 0xC7 => dma.*[1].setDmacntH(setHalf(u16, dma.*[1].cnt.raw, byte_addr, value)),
|
|
||||||
|
|
||||||
0xC8, 0xC9, 0xCA, 0xCB => dma.*[2].setDmasad(setQuart(dma.*[2].sad, byte_addr, value)),
|
|
||||||
0xCC, 0xCD, 0xCE, 0xCF => dma.*[2].setDmadad(setQuart(dma.*[2].dad, byte_addr, value)),
|
|
||||||
0xD0, 0xD1 => dma.*[2].setDmacntL(setHalf(u16, dma.*[2].word_count, byte_addr, value)),
|
|
||||||
0xD2, 0xD3 => dma.*[2].setDmacntH(setHalf(u16, dma.*[2].cnt.raw, byte_addr, value)),
|
|
||||||
|
|
||||||
0xD4, 0xD5, 0xD6, 0xD7 => dma.*[3].setDmasad(setQuart(dma.*[3].sad, byte_addr, value)),
|
|
||||||
0xD8, 0xD9, 0xDA, 0xDB => dma.*[3].setDmadad(setQuart(dma.*[3].dad, byte_addr, value)),
|
|
||||||
0xDC, 0xDD => dma.*[3].setDmacntL(setHalf(u16, dma.*[3].word_count, byte_addr, value)),
|
|
||||||
0xDE, 0xDF => dma.*[3].setDmacntH(setHalf(u16, dma.*[3].cnt.raw, byte_addr, value)),
|
|
||||||
else => util.io.write.undef(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, addr }),
|
|
||||||
},
|
|
||||||
else => @compileError("DMA: Unsupported write width"),
|
else => @compileError("DMA: Unsupported write width"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,7 +179,7 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
0x0400_0056 => {}, // Not used
|
0x0400_0056 => {}, // Not used
|
||||||
|
|
||||||
// Sound
|
// Sound
|
||||||
0x0400_0060...0x0400_00A6 => apu.write(T, &bus.apu, address, value),
|
0x0400_0060...0x0400_009E => apu.write(T, &bus.apu, address, value),
|
||||||
|
|
||||||
// Dma Transfers
|
// Dma Transfers
|
||||||
0x0400_00B0...0x0400_00DE => dma.write(T, &bus.dma, address, value),
|
0x0400_00B0...0x0400_00DE => dma.write(T, &bus.dma, address, value),
|
||||||
|
@ -217,17 +217,11 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
},
|
},
|
||||||
u8 => switch (address) {
|
u8 => switch (address) {
|
||||||
// Display
|
// Display
|
||||||
0x0400_0000...0x0400_0055 => ppu.write(T, &bus.ppu, address, value),
|
0x0400_0000...0x0400_0054 => ppu.write(T, &bus.ppu, address, value),
|
||||||
|
|
||||||
// Sound
|
// Sound
|
||||||
0x0400_0060...0x0400_00A7 => apu.write(T, &bus.apu, address, value),
|
0x0400_0060...0x0400_00A7 => apu.write(T, &bus.apu, address, value),
|
||||||
|
|
||||||
// Dma Transfers
|
|
||||||
0x0400_00B0...0x0400_00DF => dma.write(T, &bus.dma, address, value),
|
|
||||||
|
|
||||||
// Timers
|
|
||||||
0x0400_0100...0x0400_010F => timer.write(T, &bus.tim, address, value),
|
|
||||||
|
|
||||||
// Serial Communication 1
|
// Serial Communication 1
|
||||||
0x0400_0120 => log.debug("Wrote 0x{X:0>2} to SIODATA32_L_L", .{value}),
|
0x0400_0120 => log.debug("Wrote 0x{X:0>2} to SIODATA32_L_L", .{value}),
|
||||||
0x0400_0128 => log.debug("Wrote 0x{X:0>2} to SIOCNT_L", .{value}),
|
0x0400_0128 => log.debug("Wrote 0x{X:0>2} to SIOCNT_L", .{value}),
|
||||||
|
|
|
@ -8,93 +8,71 @@ const Arm7tdmi = @import("../cpu.zig").Arm7tdmi;
|
||||||
pub const TimerTuple = std.meta.Tuple(&[_]type{ Timer(0), Timer(1), Timer(2), Timer(3) });
|
pub const TimerTuple = std.meta.Tuple(&[_]type{ Timer(0), Timer(1), Timer(2), Timer(3) });
|
||||||
const log = std.log.scoped(.Timer);
|
const log = std.log.scoped(.Timer);
|
||||||
|
|
||||||
const getHalf = util.getHalf;
|
const shift = util.shift;
|
||||||
const setHalf = util.setHalf;
|
|
||||||
|
|
||||||
pub fn create(sched: *Scheduler) TimerTuple {
|
pub fn create(sched: *Scheduler) TimerTuple {
|
||||||
return .{ Timer(0).init(sched), Timer(1).init(sched), Timer(2).init(sched), Timer(3).init(sched) };
|
return .{ Timer(0).init(sched), Timer(1).init(sched), Timer(2).init(sched), Timer(3).init(sched) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(comptime T: type, tim: *const TimerTuple, addr: u32) ?T {
|
pub fn read(comptime T: type, tim: *const TimerTuple, addr: u32) ?T {
|
||||||
const nybble_addr = @truncate(u4, addr);
|
const nybble = @truncate(u4, addr);
|
||||||
|
|
||||||
return switch (T) {
|
return switch (T) {
|
||||||
u32 => switch (nybble_addr) {
|
u32 => switch (nybble) {
|
||||||
0x0 => @as(T, tim.*[0].cnt.raw) << 16 | tim.*[0].timcntL(),
|
0x0 => @as(T, tim.*[0].cnt.raw) << 16 | tim.*[0].timcntL(),
|
||||||
0x4 => @as(T, tim.*[1].cnt.raw) << 16 | tim.*[1].timcntL(),
|
0x4 => @as(T, tim.*[1].cnt.raw) << 16 | tim.*[1].timcntL(),
|
||||||
0x8 => @as(T, tim.*[2].cnt.raw) << 16 | tim.*[2].timcntL(),
|
0x8 => @as(T, tim.*[2].cnt.raw) << 16 | tim.*[2].timcntL(),
|
||||||
0xC => @as(T, tim.*[3].cnt.raw) << 16 | tim.*[3].timcntL(),
|
0xC => @as(T, tim.*[3].cnt.raw) << 16 | tim.*[3].timcntL(),
|
||||||
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
||||||
},
|
},
|
||||||
u16 => switch (nybble_addr) {
|
u16 => switch (nybble) {
|
||||||
0x0 => tim.*[0].timcntL(),
|
0x0 => tim.*[0].timcntL(),
|
||||||
0x2 => tim.*[0].cnt.raw,
|
0x2 => tim.*[0].cnt.raw,
|
||||||
|
|
||||||
0x4 => tim.*[1].timcntL(),
|
0x4 => tim.*[1].timcntL(),
|
||||||
0x6 => tim.*[1].cnt.raw,
|
0x6 => tim.*[1].cnt.raw,
|
||||||
|
|
||||||
0x8 => tim.*[2].timcntL(),
|
0x8 => tim.*[2].timcntL(),
|
||||||
0xA => tim.*[2].cnt.raw,
|
0xA => tim.*[2].cnt.raw,
|
||||||
|
|
||||||
0xC => tim.*[3].timcntL(),
|
0xC => tim.*[3].timcntL(),
|
||||||
0xE => tim.*[3].cnt.raw,
|
0xE => tim.*[3].cnt.raw,
|
||||||
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
||||||
},
|
},
|
||||||
u8 => switch (nybble_addr) {
|
u8 => switch (nybble) {
|
||||||
0x0, 0x1 => @truncate(T, tim.*[0].timcntL() >> getHalf(nybble_addr)),
|
0x0, 0x1 => @truncate(T, tim.*[0].timcntL() >> shift(nybble)),
|
||||||
0x2, 0x3 => @truncate(T, tim.*[0].cnt.raw >> getHalf(nybble_addr)),
|
0x2, 0x3 => @truncate(T, tim.*[0].cnt.raw >> shift(nybble)),
|
||||||
|
0x4, 0x5 => @truncate(T, tim.*[1].timcntL() >> shift(nybble)),
|
||||||
0x4, 0x5 => @truncate(T, tim.*[1].timcntL() >> getHalf(nybble_addr)),
|
0x6, 0x7 => @truncate(T, tim.*[1].cnt.raw >> shift(nybble)),
|
||||||
0x6, 0x7 => @truncate(T, tim.*[1].cnt.raw >> getHalf(nybble_addr)),
|
0x8, 0x9 => @truncate(T, tim.*[2].timcntL() >> shift(nybble)),
|
||||||
|
0xA, 0xB => @truncate(T, tim.*[2].cnt.raw >> shift(nybble)),
|
||||||
0x8, 0x9 => @truncate(T, tim.*[2].timcntL() >> getHalf(nybble_addr)),
|
0xC, 0xD => @truncate(T, tim.*[3].timcntL() >> shift(nybble)),
|
||||||
0xA, 0xB => @truncate(T, tim.*[2].cnt.raw >> getHalf(nybble_addr)),
|
0xE, 0xF => @truncate(T, tim.*[3].cnt.raw >> shift(nybble)),
|
||||||
|
|
||||||
0xC, 0xD => @truncate(T, tim.*[3].timcntL() >> getHalf(nybble_addr)),
|
|
||||||
0xE, 0xF => @truncate(T, tim.*[3].cnt.raw >> getHalf(nybble_addr)),
|
|
||||||
},
|
},
|
||||||
else => @compileError("TIM: Unsupported read width"),
|
else => @compileError("TIM: Unsupported read width"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(comptime T: type, tim: *TimerTuple, addr: u32, value: T) void {
|
pub fn write(comptime T: type, tim: *TimerTuple, addr: u32, value: T) void {
|
||||||
const nybble_addr = @truncate(u4, addr);
|
const nybble = @truncate(u4, addr);
|
||||||
|
|
||||||
return switch (T) {
|
return switch (T) {
|
||||||
u32 => switch (nybble_addr) {
|
u32 => switch (nybble) {
|
||||||
0x0 => tim.*[0].setTimcnt(value),
|
0x0 => tim.*[0].setTimcnt(value),
|
||||||
0x4 => tim.*[1].setTimcnt(value),
|
0x4 => tim.*[1].setTimcnt(value),
|
||||||
0x8 => tim.*[2].setTimcnt(value),
|
0x8 => tim.*[2].setTimcnt(value),
|
||||||
0xC => tim.*[3].setTimcnt(value),
|
0xC => tim.*[3].setTimcnt(value),
|
||||||
else => util.io.write.undef(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, addr }),
|
else => util.io.write.undef(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, addr }),
|
||||||
},
|
},
|
||||||
u16 => switch (nybble_addr) {
|
u16 => switch (nybble) {
|
||||||
0x0 => tim.*[0].setTimcntL(value),
|
0x0 => tim.*[0].setTimcntL(value),
|
||||||
0x2 => tim.*[0].setTimcntH(value),
|
0x2 => tim.*[0].setTimcntH(value),
|
||||||
|
|
||||||
0x4 => tim.*[1].setTimcntL(value),
|
0x4 => tim.*[1].setTimcntL(value),
|
||||||
0x6 => tim.*[1].setTimcntH(value),
|
0x6 => tim.*[1].setTimcntH(value),
|
||||||
|
|
||||||
0x8 => tim.*[2].setTimcntL(value),
|
0x8 => tim.*[2].setTimcntL(value),
|
||||||
0xA => tim.*[2].setTimcntH(value),
|
0xA => tim.*[2].setTimcntH(value),
|
||||||
|
|
||||||
0xC => tim.*[3].setTimcntL(value),
|
0xC => tim.*[3].setTimcntL(value),
|
||||||
0xE => tim.*[3].setTimcntH(value),
|
0xE => tim.*[3].setTimcntH(value),
|
||||||
else => util.io.write.undef(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, addr }),
|
else => util.io.write.undef(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, addr }),
|
||||||
},
|
},
|
||||||
u8 => switch (nybble_addr) {
|
u8 => util.io.write.undef(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, addr }),
|
||||||
0x0, 0x1 => tim.*[0].setTimcntL(setHalf(u16, tim.*[0]._reload, nybble_addr, value)),
|
|
||||||
0x2, 0x3 => tim.*[0].setTimcntH(setHalf(u16, tim.*[0].cnt.raw, nybble_addr, value)),
|
|
||||||
|
|
||||||
0x4, 0x5 => tim.*[1].setTimcntL(setHalf(u16, tim.*[1]._reload, nybble_addr, value)),
|
|
||||||
0x6, 0x7 => tim.*[1].setTimcntH(setHalf(u16, tim.*[1].cnt.raw, nybble_addr, value)),
|
|
||||||
|
|
||||||
0x8, 0x9 => tim.*[2].setTimcntL(setHalf(u16, tim.*[2]._reload, nybble_addr, value)),
|
|
||||||
0xA, 0xB => tim.*[2].setTimcntH(setHalf(u16, tim.*[2].cnt.raw, nybble_addr, value)),
|
|
||||||
|
|
||||||
0xC, 0xD => tim.*[3].setTimcntL(setHalf(u16, tim.*[3]._reload, nybble_addr, value)),
|
|
||||||
0xE, 0xF => tim.*[3].setTimcntH(setHalf(u16, tim.*[3].cnt.raw, nybble_addr, value)),
|
|
||||||
},
|
|
||||||
else => @compileError("TIM: Unsupported write width"),
|
else => @compileError("TIM: Unsupported write width"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
188
src/core/ppu.zig
188
src/core/ppu.zig
|
@ -11,9 +11,9 @@ const Bitfield = @import("bitfield").Bitfield;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const log = std.log.scoped(.PPU);
|
const log = std.log.scoped(.PPU);
|
||||||
|
|
||||||
const getHalf = util.getHalf;
|
const setHi = util.setHi;
|
||||||
const setHalf = util.setHalf;
|
const setLo = util.setLo;
|
||||||
const setQuart = util.setQuart;
|
const shift = util.shift;
|
||||||
const pollDmaOnBlank = @import("bus/dma.zig").pollDmaOnBlank;
|
const pollDmaOnBlank = @import("bus/dma.zig").pollDmaOnBlank;
|
||||||
|
|
||||||
pub const width = 240;
|
pub const width = 240;
|
||||||
|
@ -21,25 +21,24 @@ pub const height = 160;
|
||||||
pub const framebuf_pitch = width * @sizeOf(u32);
|
pub const framebuf_pitch = width * @sizeOf(u32);
|
||||||
|
|
||||||
pub fn read(comptime T: type, ppu: *const Ppu, addr: u32) ?T {
|
pub fn read(comptime T: type, ppu: *const Ppu, addr: u32) ?T {
|
||||||
const byte_addr = @truncate(u8, addr);
|
const byte = @truncate(u8, addr);
|
||||||
|
|
||||||
return switch (T) {
|
return switch (T) {
|
||||||
u32 => switch (byte_addr) {
|
u32 => switch (byte) {
|
||||||
0x00 => ppu.dispcnt.raw, // Green Swap is in high half-word
|
0x00 => ppu.dispcnt.raw, // Green Swap is in high half-word
|
||||||
0x04 => @as(T, ppu.vcount.raw) << 16 | ppu.dispstat.raw,
|
0x04 => @as(T, ppu.vcount.raw) << 16 | ppu.dispstat.raw,
|
||||||
0x08 => @as(T, ppu.bg[1].bg1Cnt()) << 16 | ppu.bg[0].bg0Cnt(),
|
0x08 => @as(T, ppu.bg[1].bg1Cnt()) << 16 | ppu.bg[0].bg0Cnt(),
|
||||||
0x0C => @as(T, ppu.bg[3].cnt.raw) << 16 | ppu.bg[2].cnt.raw,
|
0x0C => @as(T, ppu.bg[3].cnt.raw) << 16 | ppu.bg[2].cnt.raw,
|
||||||
0x10, 0x14, 0x18, 0x1C => null, // BGXHOFS/VOFS
|
0x10...0x1C => null, // BGXHOFS/VOFS
|
||||||
0x20, 0x24, 0x28, 0x2C => null, // BG2 Rot/Scaling
|
0x20...0x3C => null, // BG2/3 Rot Scaling Registers
|
||||||
0x30, 0x34, 0x38, 0x3C => null, // BG3 Rot/Scaling
|
0x40...0x44 => null, // WINXH/V Registers
|
||||||
0x40, 0x44 => null, // WINXH/V Registers
|
0x48 => @as(T, ppu.win.out()) << 16 | ppu.win.in(),
|
||||||
0x48 => @as(T, ppu.win.getOut()) << 16 | ppu.win.getIn(),
|
|
||||||
0x4C => null, // MOSAIC, undefined in high byte
|
0x4C => null, // MOSAIC, undefined in high byte
|
||||||
0x50 => @as(T, ppu.bld.getAlpha()) << 16 | ppu.bld.getCnt(),
|
0x50 => @as(T, ppu.bld.getAlpha()) << 16 | ppu.bld.getCnt(),
|
||||||
0x54 => null, // BLDY, undefined in high half-wrd
|
0x54 => null, // BLDY, undefined in high half-wrd
|
||||||
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
||||||
},
|
},
|
||||||
u16 => switch (byte_addr) {
|
u16 => switch (byte) {
|
||||||
0x00 => ppu.dispcnt.raw,
|
0x00 => ppu.dispcnt.raw,
|
||||||
0x02 => null, // Green Swap
|
0x02 => null, // Green Swap
|
||||||
0x04 => ppu.dispstat.raw,
|
0x04 => ppu.dispstat.raw,
|
||||||
|
@ -48,39 +47,35 @@ pub fn read(comptime T: type, ppu: *const Ppu, addr: u32) ?T {
|
||||||
0x0A => ppu.bg[1].bg1Cnt(),
|
0x0A => ppu.bg[1].bg1Cnt(),
|
||||||
0x0C => ppu.bg[2].cnt.raw,
|
0x0C => ppu.bg[2].cnt.raw,
|
||||||
0x0E => ppu.bg[3].cnt.raw,
|
0x0E => ppu.bg[3].cnt.raw,
|
||||||
0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E => null, // BGXHOFS/VOFS
|
0x10...0x1E => null, // BGXHOFS/VOFS
|
||||||
0x20, 0x22, 0x24, 0x26, 0x28, 0x2A, 0x2C, 0x2E => null, // BG2 Rot/Scaling
|
0x20...0x3E => null, // BG2/3 Rot Scaling Registers
|
||||||
0x30, 0x32, 0x34, 0x36, 0x38, 0x3A, 0x3C, 0x3E => null, // BG3 Rot/Scaling
|
0x40...0x46 => null, // WINXH/V Registers
|
||||||
0x40, 0x42, 0x44, 0x46 => null, // WINXH/V Registers
|
0x48 => ppu.win.in(),
|
||||||
0x48 => ppu.win.getIn(),
|
0x4A => ppu.win.out(),
|
||||||
0x4A => ppu.win.getOut(),
|
|
||||||
0x4C => null, // MOSAIC
|
0x4C => null, // MOSAIC
|
||||||
0x4E => null,
|
|
||||||
0x50 => ppu.bld.getCnt(),
|
0x50 => ppu.bld.getCnt(),
|
||||||
0x52 => ppu.bld.getAlpha(),
|
0x52 => ppu.bld.getAlpha(),
|
||||||
0x54 => null, // BLDY
|
0x54 => null, // BLDY
|
||||||
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
||||||
},
|
},
|
||||||
u8 => switch (byte_addr) {
|
u8 => switch (byte) {
|
||||||
0x00, 0x01 => @truncate(T, ppu.dispcnt.raw >> getHalf(byte_addr)),
|
0x00, 0x01 => @truncate(T, ppu.dispcnt.raw >> shift(byte)),
|
||||||
0x02, 0x03 => null,
|
0x02...0x03 => null,
|
||||||
0x04, 0x05 => @truncate(T, ppu.dispstat.raw >> getHalf(byte_addr)),
|
0x04, 0x05 => @truncate(T, ppu.dispstat.raw >> shift(byte)),
|
||||||
0x06, 0x07 => @truncate(T, ppu.vcount.raw >> getHalf(byte_addr)),
|
0x06, 0x07 => @truncate(T, ppu.vcount.raw >> shift(byte)),
|
||||||
0x08, 0x09 => @truncate(T, ppu.bg[0].bg0Cnt() >> getHalf(byte_addr)),
|
0x08, 0x09 => @truncate(T, ppu.bg[0].bg0Cnt() >> shift(byte)),
|
||||||
0x0A, 0x0B => @truncate(T, ppu.bg[1].bg1Cnt() >> getHalf(byte_addr)),
|
0x0A, 0x0B => @truncate(T, ppu.bg[1].bg1Cnt() >> shift(byte)),
|
||||||
0x0C, 0x0D => @truncate(T, ppu.bg[2].cnt.raw >> getHalf(byte_addr)),
|
0x0C, 0x0D => @truncate(T, ppu.bg[2].cnt.raw >> shift(byte)),
|
||||||
0x0E, 0x0F => @truncate(T, ppu.bg[3].cnt.raw >> getHalf(byte_addr)),
|
0x0E, 0x0F => @truncate(T, ppu.bg[3].cnt.raw >> shift(byte)),
|
||||||
0x10...0x1F => null, // BGXHOFS/VOFS
|
0x10...0x1F => null, // BGXHOFS/VOFS
|
||||||
0x20...0x2F => null, // BG2 Rot/Scaling
|
0x20...0x3F => null, // BG2/3 Rot Scaling Registers
|
||||||
0x30...0x3F => null, // BG3 Rot/Scaling
|
|
||||||
0x40...0x47 => null, // WINXH/V Registers
|
0x40...0x47 => null, // WINXH/V Registers
|
||||||
0x48, 0x49 => @truncate(T, ppu.win.getIn() >> getHalf(byte_addr)),
|
0x48, 0x49 => @truncate(T, ppu.win.in() >> shift(byte)),
|
||||||
0x4A, 0x4B => @truncate(T, ppu.win.getOut() >> getHalf(byte_addr)),
|
0x4A, 0x4B => @truncate(T, ppu.win.out() >> shift(byte)),
|
||||||
0x4C, 0x4D => null, // MOSAIC
|
0x4C...0x4D => null, // MOSAIC
|
||||||
0x4E, 0x4F => null,
|
0x50, 0x51 => @truncate(T, ppu.bld.getCnt() >> shift(byte)),
|
||||||
0x50, 0x51 => @truncate(T, ppu.bld.getCnt() >> getHalf(byte_addr)),
|
0x52, 0x53 => @truncate(T, ppu.bld.getAlpha() >> shift(byte)),
|
||||||
0x52, 0x53 => @truncate(T, ppu.bld.getAlpha() >> getHalf(byte_addr)),
|
0x54...0x55 => null, // BLDY
|
||||||
0x54, 0x55 => null, // BLDY
|
|
||||||
else => util.io.read.err(T, log, "unexpected {} read from 0x{X:0>8}", .{ T, addr }),
|
else => util.io.read.err(T, log, "unexpected {} read from 0x{X:0>8}", .{ T, addr }),
|
||||||
},
|
},
|
||||||
else => @compileError("PPU: Unsupported read width"),
|
else => @compileError("PPU: Unsupported read width"),
|
||||||
|
@ -88,10 +83,10 @@ pub fn read(comptime T: type, ppu: *const Ppu, addr: u32) ?T {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(comptime T: type, ppu: *Ppu, addr: u32, value: T) void {
|
pub fn write(comptime T: type, ppu: *Ppu, addr: u32, value: T) void {
|
||||||
const byte_addr = @truncate(u8, addr); // prefixed with 0x0400_00
|
const byte = @truncate(u8, addr); // prefixed with 0x0400_00
|
||||||
|
|
||||||
switch (T) {
|
switch (T) {
|
||||||
u32 => switch (byte_addr) {
|
u32 => switch (byte) {
|
||||||
0x00 => ppu.dispcnt.raw = @truncate(u16, value),
|
0x00 => ppu.dispcnt.raw = @truncate(u16, value),
|
||||||
0x04 => {
|
0x04 => {
|
||||||
ppu.dispstat.raw = @truncate(u16, value);
|
ppu.dispstat.raw = @truncate(u16, value);
|
||||||
|
@ -99,45 +94,38 @@ pub fn write(comptime T: type, ppu: *Ppu, addr: u32, value: T) void {
|
||||||
},
|
},
|
||||||
0x08 => ppu.setAdjCnts(0, value),
|
0x08 => ppu.setAdjCnts(0, value),
|
||||||
0x0C => ppu.setAdjCnts(2, value),
|
0x0C => ppu.setAdjCnts(2, value),
|
||||||
|
|
||||||
0x10 => ppu.setBgOffsets(0, value),
|
0x10 => ppu.setBgOffsets(0, value),
|
||||||
0x14 => ppu.setBgOffsets(1, value),
|
0x14 => ppu.setBgOffsets(1, value),
|
||||||
0x18 => ppu.setBgOffsets(2, value),
|
0x18 => ppu.setBgOffsets(2, value),
|
||||||
0x1C => ppu.setBgOffsets(3, value),
|
0x1C => ppu.setBgOffsets(3, value),
|
||||||
|
|
||||||
0x20 => ppu.aff_bg[0].writePaPb(value),
|
0x20 => ppu.aff_bg[0].writePaPb(value),
|
||||||
0x24 => ppu.aff_bg[0].writePcPd(value),
|
0x24 => ppu.aff_bg[0].writePcPd(value),
|
||||||
0x28 => ppu.aff_bg[0].setX(ppu.dispstat.vblank.read(), value),
|
0x28 => ppu.aff_bg[0].setX(ppu.dispstat.vblank.read(), value),
|
||||||
0x2C => ppu.aff_bg[0].setY(ppu.dispstat.vblank.read(), value),
|
0x2C => ppu.aff_bg[0].setY(ppu.dispstat.vblank.read(), value),
|
||||||
|
|
||||||
0x30 => ppu.aff_bg[1].writePaPb(value),
|
0x30 => ppu.aff_bg[1].writePaPb(value),
|
||||||
0x34 => ppu.aff_bg[1].writePcPd(value),
|
0x34 => ppu.aff_bg[1].writePcPd(value),
|
||||||
0x38 => ppu.aff_bg[1].setX(ppu.dispstat.vblank.read(), value),
|
0x38 => ppu.aff_bg[1].setX(ppu.dispstat.vblank.read(), value),
|
||||||
0x3C => ppu.aff_bg[1].setY(ppu.dispstat.vblank.read(), value),
|
0x3C => ppu.aff_bg[1].setY(ppu.dispstat.vblank.read(), value),
|
||||||
|
|
||||||
0x40 => ppu.win.setH(value),
|
0x40 => ppu.win.setH(value),
|
||||||
0x44 => ppu.win.setV(value),
|
0x44 => ppu.win.setV(value),
|
||||||
0x48 => ppu.win.setIo(value),
|
0x48 => ppu.win.setIo(value),
|
||||||
0x4C => log.debug("Wrote 0x{X:0>8} to MOSAIC", .{value}),
|
0x4C => log.debug("Wrote 0x{X:0>8} to MOSAIC", .{value}),
|
||||||
|
|
||||||
0x50 => {
|
0x50 => {
|
||||||
ppu.bld.cnt.raw = @truncate(u16, value);
|
ppu.bld.cnt.raw = @truncate(u16, value);
|
||||||
ppu.bld.alpha.raw = @truncate(u16, value >> 16);
|
ppu.bld.alpha.raw = @truncate(u16, value >> 16);
|
||||||
},
|
},
|
||||||
0x54 => ppu.bld.y.raw = @truncate(u16, value),
|
0x54 => ppu.bld.y.raw = @truncate(u16, value),
|
||||||
|
0x58...0x5C => {}, // Unused
|
||||||
else => util.io.write.undef(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, addr }),
|
else => util.io.write.undef(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, addr }),
|
||||||
},
|
},
|
||||||
u16 => switch (byte_addr) {
|
u16 => switch (byte) {
|
||||||
0x00 => ppu.dispcnt.raw = value,
|
0x00 => ppu.dispcnt.raw = value,
|
||||||
0x02 => {}, // Green Swap
|
|
||||||
0x04 => ppu.dispstat.raw = value,
|
0x04 => ppu.dispstat.raw = value,
|
||||||
0x06 => {}, // VCOUNT
|
0x06 => {}, // vcount is read-only
|
||||||
|
|
||||||
0x08 => ppu.bg[0].cnt.raw = value,
|
0x08 => ppu.bg[0].cnt.raw = value,
|
||||||
0x0A => ppu.bg[1].cnt.raw = value,
|
0x0A => ppu.bg[1].cnt.raw = value,
|
||||||
0x0C => ppu.bg[2].cnt.raw = value,
|
0x0C => ppu.bg[2].cnt.raw = value,
|
||||||
0x0E => ppu.bg[3].cnt.raw = value,
|
0x0E => ppu.bg[3].cnt.raw = value,
|
||||||
|
|
||||||
0x10 => ppu.bg[0].hofs.raw = value, // TODO: Don't write out every HOFS / VOFS?
|
0x10 => ppu.bg[0].hofs.raw = value, // TODO: Don't write out every HOFS / VOFS?
|
||||||
0x12 => ppu.bg[0].vofs.raw = value,
|
0x12 => ppu.bg[0].vofs.raw = value,
|
||||||
0x14 => ppu.bg[1].hofs.raw = value,
|
0x14 => ppu.bg[1].hofs.raw = value,
|
||||||
|
@ -146,87 +134,45 @@ pub fn write(comptime T: type, ppu: *Ppu, addr: u32, value: T) void {
|
||||||
0x1A => ppu.bg[2].vofs.raw = value,
|
0x1A => ppu.bg[2].vofs.raw = value,
|
||||||
0x1C => ppu.bg[3].hofs.raw = value,
|
0x1C => ppu.bg[3].hofs.raw = value,
|
||||||
0x1E => ppu.bg[3].vofs.raw = value,
|
0x1E => ppu.bg[3].vofs.raw = value,
|
||||||
|
|
||||||
0x20 => ppu.aff_bg[0].pa = @bitCast(i16, value),
|
0x20 => ppu.aff_bg[0].pa = @bitCast(i16, value),
|
||||||
0x22 => ppu.aff_bg[0].pb = @bitCast(i16, value),
|
0x22 => ppu.aff_bg[0].pb = @bitCast(i16, value),
|
||||||
0x24 => ppu.aff_bg[0].pc = @bitCast(i16, value),
|
0x24 => ppu.aff_bg[0].pc = @bitCast(i16, value),
|
||||||
0x26 => ppu.aff_bg[0].pd = @bitCast(i16, value),
|
0x26 => ppu.aff_bg[0].pd = @bitCast(i16, value),
|
||||||
0x28, 0x2A => ppu.aff_bg[0].x = @bitCast(i32, setHalf(u32, @bitCast(u32, ppu.aff_bg[0].x), byte_addr, value)),
|
0x28 => ppu.aff_bg[0].x = @bitCast(i32, setLo(u32, @bitCast(u32, ppu.aff_bg[0].x), value)),
|
||||||
0x2C, 0x2E => ppu.aff_bg[0].y = @bitCast(i32, setHalf(u32, @bitCast(u32, ppu.aff_bg[0].y), byte_addr, value)),
|
0x2A => ppu.aff_bg[0].x = @bitCast(i32, setHi(u32, @bitCast(u32, ppu.aff_bg[0].x), value)),
|
||||||
|
0x2C => ppu.aff_bg[0].y = @bitCast(i32, setLo(u32, @bitCast(u32, ppu.aff_bg[0].y), value)),
|
||||||
|
0x2E => ppu.aff_bg[0].y = @bitCast(i32, setHi(u32, @bitCast(u32, ppu.aff_bg[0].y), value)),
|
||||||
0x30 => ppu.aff_bg[1].pa = @bitCast(i16, value),
|
0x30 => ppu.aff_bg[1].pa = @bitCast(i16, value),
|
||||||
0x32 => ppu.aff_bg[1].pb = @bitCast(i16, value),
|
0x32 => ppu.aff_bg[1].pb = @bitCast(i16, value),
|
||||||
0x34 => ppu.aff_bg[1].pc = @bitCast(i16, value),
|
0x34 => ppu.aff_bg[1].pc = @bitCast(i16, value),
|
||||||
0x36 => ppu.aff_bg[1].pd = @bitCast(i16, value),
|
0x36 => ppu.aff_bg[1].pd = @bitCast(i16, value),
|
||||||
0x38, 0x3A => ppu.aff_bg[1].x = @bitCast(i32, setHalf(u32, @bitCast(u32, ppu.aff_bg[1].x), byte_addr, value)),
|
0x38 => ppu.aff_bg[1].x = @bitCast(i32, setLo(u32, @bitCast(u32, ppu.aff_bg[1].x), value)),
|
||||||
0x3C, 0x3E => ppu.aff_bg[1].y = @bitCast(i32, setHalf(u32, @bitCast(u32, ppu.aff_bg[1].y), byte_addr, value)),
|
0x3A => ppu.aff_bg[1].x = @bitCast(i32, setHi(u32, @bitCast(u32, ppu.aff_bg[1].x), value)),
|
||||||
|
0x3C => ppu.aff_bg[1].y = @bitCast(i32, setLo(u32, @bitCast(u32, ppu.aff_bg[1].y), value)),
|
||||||
|
0x3E => ppu.aff_bg[1].y = @bitCast(i32, setHi(u32, @bitCast(u32, ppu.aff_bg[1].y), value)),
|
||||||
0x40 => ppu.win.h[0].raw = value,
|
0x40 => ppu.win.h[0].raw = value,
|
||||||
0x42 => ppu.win.h[1].raw = value,
|
0x42 => ppu.win.h[1].raw = value,
|
||||||
0x44 => ppu.win.v[0].raw = value,
|
0x44 => ppu.win.v[0].raw = value,
|
||||||
0x46 => ppu.win.v[1].raw = value,
|
0x46 => ppu.win.v[1].raw = value,
|
||||||
0x48 => ppu.win.in.raw = value,
|
0x48 => ppu.win._in.raw = value,
|
||||||
0x4A => ppu.win.out.raw = value,
|
0x4A => ppu.win._out.raw = value,
|
||||||
0x4C => log.debug("Wrote 0x{X:0>4} to MOSAIC", .{value}),
|
0x4C => log.debug("Wrote 0x{X:0>4} to MOSAIC", .{value}),
|
||||||
0x4E => {},
|
|
||||||
|
|
||||||
0x50 => ppu.bld.cnt.raw = value,
|
0x50 => ppu.bld.cnt.raw = value,
|
||||||
0x52 => ppu.bld.alpha.raw = value,
|
0x52 => ppu.bld.alpha.raw = value,
|
||||||
0x54 => ppu.bld.y.raw = value,
|
0x54 => ppu.bld.y.raw = value,
|
||||||
else => util.io.write.undef(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, addr }),
|
else => util.io.write.undef(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, addr }),
|
||||||
},
|
},
|
||||||
u8 => switch (byte_addr) {
|
u8 => switch (byte) {
|
||||||
0x00, 0x01 => ppu.dispcnt.raw = setHalf(u16, ppu.dispcnt.raw, byte_addr, value),
|
0x04 => ppu.dispstat.raw = setLo(u16, ppu.dispstat.raw, value),
|
||||||
0x02, 0x03 => {}, // Green Swap
|
0x05 => ppu.dispstat.raw = setHi(u16, ppu.dispstat.raw, value),
|
||||||
0x04, 0x05 => ppu.dispstat.raw = setHalf(u16, ppu.dispstat.raw, byte_addr, value),
|
0x08 => ppu.bg[0].cnt.raw = setLo(u16, ppu.bg[0].cnt.raw, value),
|
||||||
0x06, 0x07 => {}, // VCOUNT
|
0x09 => ppu.bg[0].cnt.raw = setHi(u16, ppu.bg[0].cnt.raw, value),
|
||||||
|
0x0A => ppu.bg[1].cnt.raw = setLo(u16, ppu.bg[1].cnt.raw, value),
|
||||||
// BGXCNT
|
0x0B => ppu.bg[1].cnt.raw = setHi(u16, ppu.bg[1].cnt.raw, value),
|
||||||
0x08, 0x09 => ppu.bg[0].cnt.raw = setHalf(u16, ppu.bg[0].cnt.raw, byte_addr, value),
|
0x48 => ppu.win._in.raw = setLo(u16, ppu.win._in.raw, value),
|
||||||
0x0A, 0x0B => ppu.bg[1].cnt.raw = setHalf(u16, ppu.bg[1].cnt.raw, byte_addr, value),
|
0x49 => ppu.win._in.raw = setHi(u16, ppu.win._in.raw, value),
|
||||||
0x0C, 0x0D => ppu.bg[2].cnt.raw = setHalf(u16, ppu.bg[2].cnt.raw, byte_addr, value),
|
0x4A => ppu.win._out.raw = setLo(u16, ppu.win._out.raw, value),
|
||||||
0x0E, 0x0F => ppu.bg[3].cnt.raw = setHalf(u16, ppu.bg[3].cnt.raw, byte_addr, value),
|
0x54 => ppu.bld.y.raw = setLo(u16, ppu.bld.y.raw, value),
|
||||||
|
|
||||||
// BGX HOFS/VOFS
|
|
||||||
0x10, 0x11 => ppu.bg[0].hofs.raw = setHalf(u16, ppu.bg[0].hofs.raw, byte_addr, value),
|
|
||||||
0x12, 0x13 => ppu.bg[0].vofs.raw = setHalf(u16, ppu.bg[0].vofs.raw, byte_addr, value),
|
|
||||||
0x14, 0x15 => ppu.bg[1].hofs.raw = setHalf(u16, ppu.bg[1].hofs.raw, byte_addr, value),
|
|
||||||
0x16, 0x17 => ppu.bg[1].vofs.raw = setHalf(u16, ppu.bg[1].vofs.raw, byte_addr, value),
|
|
||||||
0x18, 0x19 => ppu.bg[2].hofs.raw = setHalf(u16, ppu.bg[2].hofs.raw, byte_addr, value),
|
|
||||||
0x1A, 0x1B => ppu.bg[2].vofs.raw = setHalf(u16, ppu.bg[2].vofs.raw, byte_addr, value),
|
|
||||||
0x1C, 0x1D => ppu.bg[3].hofs.raw = setHalf(u16, ppu.bg[3].hofs.raw, byte_addr, value),
|
|
||||||
0x1E, 0x1F => ppu.bg[3].vofs.raw = setHalf(u16, ppu.bg[3].vofs.raw, byte_addr, value),
|
|
||||||
|
|
||||||
// BG2 Rot/Scaling
|
|
||||||
0x20, 0x21 => ppu.aff_bg[0].pa = @bitCast(i16, setHalf(u16, @bitCast(u16, ppu.aff_bg[0].pa), byte_addr, value)),
|
|
||||||
0x22, 0x23 => ppu.aff_bg[0].pb = @bitCast(i16, setHalf(u16, @bitCast(u16, ppu.aff_bg[0].pb), byte_addr, value)),
|
|
||||||
0x24, 0x25 => ppu.aff_bg[0].pc = @bitCast(i16, setHalf(u16, @bitCast(u16, ppu.aff_bg[0].pc), byte_addr, value)),
|
|
||||||
0x26, 0x27 => ppu.aff_bg[0].pd = @bitCast(i16, setHalf(u16, @bitCast(u16, ppu.aff_bg[0].pd), byte_addr, value)),
|
|
||||||
0x28, 0x29, 0x2A, 0x2B => ppu.aff_bg[0].x = @bitCast(i32, setQuart(@bitCast(u32, ppu.aff_bg[0].x), byte_addr, value)),
|
|
||||||
0x2C, 0x2D, 0x2E, 0x2F => ppu.aff_bg[0].y = @bitCast(i32, setQuart(@bitCast(u32, ppu.aff_bg[0].y), byte_addr, value)),
|
|
||||||
|
|
||||||
// BG3 Rot/Scaling
|
|
||||||
0x30, 0x31 => ppu.aff_bg[1].pa = @bitCast(i16, setHalf(u16, @bitCast(u16, ppu.aff_bg[1].pa), byte_addr, value)),
|
|
||||||
0x32, 0x33 => ppu.aff_bg[1].pb = @bitCast(i16, setHalf(u16, @bitCast(u16, ppu.aff_bg[1].pb), byte_addr, value)),
|
|
||||||
0x34, 0x35 => ppu.aff_bg[1].pc = @bitCast(i16, setHalf(u16, @bitCast(u16, ppu.aff_bg[1].pc), byte_addr, value)),
|
|
||||||
0x36, 0x37 => ppu.aff_bg[1].pd = @bitCast(i16, setHalf(u16, @bitCast(u16, ppu.aff_bg[1].pd), byte_addr, value)),
|
|
||||||
0x38, 0x39, 0x3A, 0x3B => ppu.aff_bg[1].x = @bitCast(i32, setQuart(@bitCast(u32, ppu.aff_bg[1].x), byte_addr, value)),
|
|
||||||
0x3C, 0x3D, 0x3E, 0x3F => ppu.aff_bg[1].y = @bitCast(i32, setQuart(@bitCast(u32, ppu.aff_bg[1].y), byte_addr, value)),
|
|
||||||
|
|
||||||
// Window
|
|
||||||
0x40, 0x41 => ppu.win.h[0].raw = setHalf(u16, ppu.win.h[0].raw, byte_addr, value),
|
|
||||||
0x42, 0x43 => ppu.win.h[1].raw = setHalf(u16, ppu.win.h[1].raw, byte_addr, value),
|
|
||||||
0x44, 0x45 => ppu.win.v[0].raw = setHalf(u16, ppu.win.v[0].raw, byte_addr, value),
|
|
||||||
0x46, 0x47 => ppu.win.v[1].raw = setHalf(u16, ppu.win.v[1].raw, byte_addr, value),
|
|
||||||
0x48, 0x49 => ppu.win.in.raw = setHalf(u16, ppu.win.in.raw, byte_addr, value),
|
|
||||||
0x4A, 0x4B => ppu.win.out.raw = setHalf(u16, ppu.win.out.raw, byte_addr, value),
|
|
||||||
0x4C, 0x4D => log.debug("Wrote 0x{X:0>2} to MOSAIC", .{value}),
|
|
||||||
0x4E, 0x4F => {},
|
|
||||||
|
|
||||||
// Blending
|
|
||||||
0x50, 0x51 => ppu.bld.cnt.raw = setHalf(u16, ppu.bld.cnt.raw, byte_addr, value),
|
|
||||||
0x52, 0x53 => ppu.bld.alpha.raw = setHalf(u16, ppu.bld.alpha.raw, byte_addr, value),
|
|
||||||
0x54, 0x55 => ppu.bld.y.raw = setHalf(u16, ppu.bld.y.raw, byte_addr, value),
|
|
||||||
else => util.io.write.undef(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, addr }),
|
else => util.io.write.undef(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, addr }),
|
||||||
},
|
},
|
||||||
else => @compileError("PPU: Unsupported write width"),
|
else => @compileError("PPU: Unsupported write width"),
|
||||||
|
@ -1015,25 +961,25 @@ const Window = struct {
|
||||||
h: [2]io.WinH,
|
h: [2]io.WinH,
|
||||||
v: [2]io.WinV,
|
v: [2]io.WinV,
|
||||||
|
|
||||||
out: io.WinOut,
|
_out: io.WinOut,
|
||||||
in: io.WinIn,
|
_in: io.WinIn,
|
||||||
|
|
||||||
fn init() Self {
|
fn init() Self {
|
||||||
return .{
|
return .{
|
||||||
.h = [_]io.WinH{.{ .raw = 0 }} ** 2,
|
.h = [_]io.WinH{.{ .raw = 0 }} ** 2,
|
||||||
.v = [_]io.WinV{.{ .raw = 0 }} ** 2,
|
.v = [_]io.WinV{.{ .raw = 0 }} ** 2,
|
||||||
|
|
||||||
.out = .{ .raw = 0 },
|
._out = .{ .raw = 0 },
|
||||||
.in = .{ .raw = 0 },
|
._in = .{ .raw = 0 },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getIn(self: *const Self) u16 {
|
pub fn in(self: *const Self) u16 {
|
||||||
return self.in.raw & 0x3F3F;
|
return self._in.raw & 0x3F3F;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getOut(self: *const Self) u16 {
|
pub fn out(self: *const Self) u16 {
|
||||||
return self.out.raw & 0x3F3F;
|
return self._out.raw & 0x3F3F;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setH(self: *Self, value: u32) void {
|
pub fn setH(self: *Self, value: u32) void {
|
||||||
|
@ -1047,8 +993,8 @@ const Window = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setIo(self: *Self, value: u32) void {
|
pub fn setIo(self: *Self, value: u32) void {
|
||||||
self.in.raw = @truncate(u16, value);
|
self._in.raw = @truncate(u16, value);
|
||||||
self.out.raw = @truncate(u16, value >> 16);
|
self._out.raw = @truncate(u16, value >> 16);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
34
src/util.zig
34
src/util.zig
|
@ -283,43 +283,13 @@ pub const audio = struct {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Sets a quarter (8) of the bits of the u32 `left` to the value of u8 `right`
|
|
||||||
pub inline fn setQuart(left: u32, addr: u8, right: u8) u32 {
|
|
||||||
const offset = @truncate(u2, addr);
|
|
||||||
|
|
||||||
return switch (offset) {
|
|
||||||
0b00 => (left & 0xFFFF_FF00) | right,
|
|
||||||
0b01 => (left & 0xFFFF_00FF) | @as(u32, right) << 8,
|
|
||||||
0b10 => (left & 0xFF00_FFFF) | @as(u32, right) << 16,
|
|
||||||
0b11 => (left & 0x00FF_FFFF) | @as(u32, right) << 24,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculates the correct shift offset for an aligned/unaligned u8 read
|
/// Calculates the correct shift offset for an aligned/unaligned u8 read
|
||||||
///
|
///
|
||||||
/// TODO: Support u16 reads of u32 values?
|
/// TODO: Rename this
|
||||||
pub inline fn getHalf(byte: u8) u4 {
|
pub inline fn shift(byte: u8) u4 {
|
||||||
return @truncate(u4, byte & 1) << 3;
|
return @truncate(u4, byte & 1) << 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Maybe combine SetLo and SetHi, use addr alignment to deduplicate code
|
|
||||||
|
|
||||||
pub inline fn setHalf(comptime T: type, left: T, addr: u8, right: HalfInt(T)) T {
|
|
||||||
const offset = @truncate(u1, addr >> if (T == u32) 1 else 0);
|
|
||||||
|
|
||||||
return switch (T) {
|
|
||||||
u32 => switch (offset) {
|
|
||||||
0b0 => (left & 0xFFFF_0000) | right,
|
|
||||||
0b1 => (left & 0x0000_FFFF) | @as(u32, right) << 16,
|
|
||||||
},
|
|
||||||
u16 => switch (offset) {
|
|
||||||
0b0 => (left & 0xFF00) | right,
|
|
||||||
0b1 => (right & 0x00FF) | @as(u16, right) << 8,
|
|
||||||
},
|
|
||||||
else => @compileError("unsupported type"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the high bits of an integer to a value
|
/// Sets the high bits of an integer to a value
|
||||||
pub inline fn setLo(comptime T: type, left: T, right: HalfInt(T)) T {
|
pub inline fn setLo(comptime T: type, left: T, right: HalfInt(T)) T {
|
||||||
return switch (T) {
|
return switch (T) {
|
||||||
|
|
Loading…
Reference in New Issue