Compare commits

...

5 Commits

6 changed files with 317 additions and 171 deletions

View File

@ -12,9 +12,8 @@ const Noise = @import("apu/Noise.zig");
const SoundFifo = std.fifo.LinearFifo(u8, .{ .Static = 0x20 });
const setHi = util.setHi;
const setLo = util.setLo;
const shift = util.shift;
const getHalf = util.getHalf;
const setHalf = util.setHalf;
const intToBytes = util.intToBytes;
const log = std.log.scoped(.APU);
@ -23,10 +22,10 @@ pub const host_rate = @import("../platform.zig").sample_rate;
pub const host_format = @import("../platform.zig").sample_format;
pub fn read(comptime T: type, apu: *const Apu, addr: u32) ?T {
const byte = @truncate(u8, addr);
const byte_addr = @truncate(u8, addr);
return switch (T) {
u32 => switch (byte) {
u32 => switch (byte_addr) {
0x60 => @as(T, apu.ch1.sound1CntH()) << 16 | apu.ch1.sound1CntL(),
0x64 => apu.ch1.sound1CntX(),
0x68 => apu.ch2.sound2CntL(),
@ -39,12 +38,12 @@ pub fn read(comptime T: type, apu: *const Apu, addr: u32) ?T {
0x84 => apu.soundCntX(),
0x88 => apu.bias.raw, // SOUNDBIAS, high is unused
0x8C => null,
0x90, 0x94, 0x8, 0x9C => apu.ch3.wave_dev.read(T, apu.ch3.select, addr),
0x90, 0x94, 0x98, 0x9C => apu.ch3.wave_dev.read(T, apu.ch3.select, addr),
0xA0 => null, // FIFO_A
0xA4 => null, // FIFO_B
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
},
u16 => switch (byte) {
u16 => switch (byte_addr) {
0x60 => apu.ch1.sound1CntL(),
0x62 => apu.ch1.sound1CntH(),
0x64 => apu.ch1.sound1CntX(),
@ -73,28 +72,28 @@ pub fn read(comptime T: type, apu: *const Apu, addr: u32) ?T {
0xA4, 0xA6 => null, // FIFO_B
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
},
u8 => switch (byte) {
0x60, 0x61 => @truncate(T, @as(u16, apu.ch1.sound1CntL()) >> shift(byte)),
0x62, 0x63 => @truncate(T, apu.ch1.sound1CntH() >> shift(byte)),
0x64, 0x65 => @truncate(T, apu.ch1.sound1CntX() >> shift(byte)),
u8 => switch (byte_addr) {
0x60, 0x61 => @truncate(T, @as(u16, apu.ch1.sound1CntL()) >> getHalf(byte_addr)),
0x62, 0x63 => @truncate(T, apu.ch1.sound1CntH() >> getHalf(byte_addr)),
0x64, 0x65 => @truncate(T, apu.ch1.sound1CntX() >> getHalf(byte_addr)),
0x66, 0x67 => 0x00, // assuming behaviour is identical to that of 16-bit reads
0x68, 0x69 => @truncate(T, apu.ch2.sound2CntL() >> shift(byte)),
0x68, 0x69 => @truncate(T, apu.ch2.sound2CntL() >> getHalf(byte_addr)),
0x6A, 0x6B => 0x00,
0x6C, 0x6D => @truncate(T, apu.ch2.sound2CntH() >> shift(byte)),
0x6C, 0x6D => @truncate(T, apu.ch2.sound2CntH() >> getHalf(byte_addr)),
0x6E, 0x6F => 0x00,
0x70, 0x71 => @truncate(T, @as(u16, apu.ch3.sound3CntL()) >> shift(byte)), // SOUND3CNT_L
0x72, 0x73 => @truncate(T, apu.ch3.sound3CntH() >> shift(byte)),
0x74, 0x75 => @truncate(T, apu.ch3.sound3CntX() >> shift(byte)), // SOUND3CNT_L
0x70, 0x71 => @truncate(T, @as(u16, apu.ch3.sound3CntL()) >> getHalf(byte_addr)), // SOUND3CNT_L
0x72, 0x73 => @truncate(T, apu.ch3.sound3CntH() >> getHalf(byte_addr)),
0x74, 0x75 => @truncate(T, apu.ch3.sound3CntX() >> getHalf(byte_addr)), // SOUND3CNT_L
0x76, 0x77 => 0x00,
0x78, 0x79 => @truncate(T, apu.ch4.sound4CntL() >> shift(byte)),
0x78, 0x79 => @truncate(T, apu.ch4.sound4CntL() >> getHalf(byte_addr)),
0x7A, 0x7B => 0x00,
0x7C, 0x7D => @truncate(T, apu.ch4.sound4CntH() >> shift(byte)),
0x7C, 0x7D => @truncate(T, apu.ch4.sound4CntH() >> getHalf(byte_addr)),
0x7E, 0x7F => 0x00,
0x80, 0x81 => @truncate(T, apu.soundCntL() >> shift(byte)), // SOUNDCNT_L
0x82, 0x83 => @truncate(T, apu.soundCntH() >> shift(byte)), // SOUNDCNT_H
0x84, 0x85 => @truncate(T, @as(u16, apu.soundCntX()) >> shift(byte)),
0x80, 0x81 => @truncate(T, apu.soundCntL() >> getHalf(byte_addr)), // SOUNDCNT_L
0x82, 0x83 => @truncate(T, apu.soundCntH() >> getHalf(byte_addr)), // SOUNDCNT_H
0x84, 0x85 => @truncate(T, @as(u16, apu.soundCntX()) >> getHalf(byte_addr)),
0x86, 0x87 => 0x00,
0x88, 0x89 => @truncate(T, apu.bias.raw >> shift(byte)), // SOUNDBIAS
0x88, 0x89 => @truncate(T, apu.bias.raw >> getHalf(byte_addr)), // SOUNDBIAS
0x8A, 0x8B => 0x00,
0x8C...0x8F => null,
0x90...0x9F => apu.ch3.wave_dev.read(T, apu.ch3.select, addr),
@ -107,79 +106,107 @@ 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 {
const byte = @truncate(u8, addr);
const byte_addr = @truncate(u8, addr);
switch (T) {
u32 => switch (byte) {
u32 => switch (byte_addr) {
0x60 => apu.ch1.setSound1Cnt(value),
0x64 => apu.ch1.setSound1CntX(&apu.fs, @truncate(u16, value)),
0x68 => apu.ch2.setSound2CntL(@truncate(u16, value)),
0x6C => apu.ch2.setSound2CntH(&apu.fs, @truncate(u16, value)),
0x70 => apu.ch3.setSound3Cnt(value),
0x74 => apu.ch3.setSound3CntX(&apu.fs, @truncate(u16, value)),
0x78 => apu.ch4.setSound4CntL(@truncate(u16, value)),
0x7C => apu.ch4.setSound4CntH(&apu.fs, @truncate(u16, value)),
0x80 => apu.setSoundCnt(value),
// WAVE_RAM
0x90...0x9F => apu.ch3.wave_dev.write(T, apu.ch3.select, addr, value),
0x84 => apu.setSoundCntX(value >> 7 & 1 == 1),
0x88 => apu.bias.raw = @truncate(u16, value),
0x8C => {},
0x90, 0x94, 0x98, 0x9C => apu.ch3.wave_dev.write(T, apu.ch3.select, addr, value),
0xA0 => apu.chA.push(value), // FIFO_A
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 }),
},
u16 => switch (byte) {
u16 => switch (byte_addr) {
0x60 => apu.ch1.setSound1CntL(@truncate(u8, value)), // SOUND1CNT_L
0x62 => apu.ch1.setSound1CntH(value),
0x64 => apu.ch1.setSound1CntX(&apu.fs, value),
0x66 => {},
0x68 => apu.ch2.setSound2CntL(value),
0x6A => {},
0x6C => apu.ch2.setSound2CntH(&apu.fs, value),
0x6E => {},
0x70 => apu.ch3.setSound3CntL(@truncate(u8, value)),
0x72 => apu.ch3.setSound3CntH(value),
0x74 => apu.ch3.setSound3CntX(&apu.fs, value),
0x76 => {},
0x78 => apu.ch4.setSound4CntL(value),
0x7A => {},
0x7C => apu.ch4.setSound4CntH(&apu.fs, value),
0x7E => {},
0x80 => apu.psg_cnt.raw = value, // SOUNDCNT_L
0x80 => apu.setSoundCntL(value),
0x82 => apu.setSoundCntH(value),
0x84 => apu.setSoundCntX(value >> 7 & 1 == 1),
0x86 => {},
0x88 => apu.bias.raw = value, // SOUNDBIAS
// WAVE_RAM
0x90...0x9F => apu.ch3.wave_dev.write(T, apu.ch3.select, addr, value),
0x8A, 0x8C, 0x8E => {},
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 }),
},
u8 => switch (byte) {
u8 => switch (byte_addr) {
0x60 => apu.ch1.setSound1CntL(value),
0x61 => {},
0x62 => apu.ch1.setNr11(value),
0x63 => apu.ch1.setNr12(value),
0x64 => apu.ch1.setNr13(value),
0x65 => apu.ch1.setNr14(&apu.fs, value),
0x66, 0x67 => {},
0x68 => apu.ch2.setNr21(value),
0x69 => apu.ch2.setNr22(value),
0x6A, 0x6B => {},
0x6C => apu.ch2.setNr23(value),
0x6D => apu.ch2.setNr24(&apu.fs, value),
0x6E, 0x6F => {},
0x70 => apu.ch3.setSound3CntL(value), // NR30
0x71 => {},
0x72 => apu.ch3.setNr31(value),
0x73 => apu.ch3.vol.raw = value, // NR32
0x74 => apu.ch3.setNr33(value),
0x75 => apu.ch3.setNr34(&apu.fs, value),
0x76, 0x77 => {},
0x78 => apu.ch4.setNr41(value),
0x79 => apu.ch4.setNr42(value),
0x7A, 0x7B => {},
0x7C => apu.ch4.poly.raw = value, // NR 43
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),
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 => @compileError("APU: Unsupported write width"),
@ -256,7 +283,7 @@ pub const Apu = struct {
/// SOUNDCNT
fn setSoundCnt(self: *Self, value: u32) void {
self.psg_cnt.raw = @truncate(u16, value);
self.setSoundCntL(@truncate(u16, value));
self.setSoundCntH(@truncate(u16, value >> 16));
}
@ -265,6 +292,11 @@ pub const Apu = struct {
return self.psg_cnt.raw & 0xFF77;
}
/// SOUNDCNT_L
pub fn setSoundCntL(self: *Self, value: u16) void {
self.psg_cnt.raw = value;
}
/// SOUNDCNT_H
pub fn setSoundCntH(self: *Self, value: u16) void {
const new: io.DmaSoundControl = .{ .raw = value };
@ -312,20 +344,6 @@ pub const Apu = struct {
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 {
self.sched.push(.SampleAudio, self.interval() -| late);

View File

@ -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) });
const log = std.log.scoped(.DmaTransfer);
const setHi = util.setHi;
const setLo = util.setLo;
const shift = util.shift;
const getHalf = util.getHalf;
const setHalf = util.setHalf;
const setQuart = util.setQuart;
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 {
const byte = @truncate(u8, addr);
const byte_addr = @truncate(u8, addr);
return switch (T) {
u32 => switch (byte) {
u32 => switch (byte_addr) {
0xB0, 0xB4 => null, // DMA0SAD, DMA0DAD,
0xB8 => @as(T, dma.*[0].dmacntH()) << 16, // DMA0CNT_L is write-only
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
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
},
u16 => switch (byte) {
u16 => switch (byte_addr) {
0xB0, 0xB2, 0xB4, 0xB6 => null, // DMA0SAD, DMA0DAD
0xB8 => 0x0000, // DMA0CNT_L, suite.gba expects 0x0000 instead of 0xDEAD
0xBA => dma.*[0].dmacntH(),
@ -51,22 +51,22 @@ pub fn read(comptime T: type, dma: *const DmaTuple, addr: u32) ?T {
0xDE => dma.*[3].dmacntH(),
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
},
u8 => switch (byte) {
u8 => switch (byte_addr) {
0xB0...0xB7 => null, // DMA0SAD, DMA0DAD
0xB8, 0xB9 => 0x00, // DMA0CNT_L
0xBA, 0xBB => @truncate(T, dma.*[0].dmacntH() >> shift(byte)),
0xBA, 0xBB => @truncate(T, dma.*[0].dmacntH() >> getHalf(byte_addr)),
0xBC...0xC3 => null, // DMA1SAD, DMA1DAD
0xC4, 0xC5 => 0x00, // DMA1CNT_L
0xC6, 0xC7 => @truncate(T, dma.*[1].dmacntH() >> shift(byte)),
0xC6, 0xC7 => @truncate(T, dma.*[1].dmacntH() >> getHalf(byte_addr)),
0xC8...0xCF => null, // DMA2SAD, DMA2DAD
0xD0, 0xD1 => 0x00, // DMA2CNT_L
0xD2, 0xD3 => @truncate(T, dma.*[2].dmacntH() >> shift(byte)),
0xD2, 0xD3 => @truncate(T, dma.*[2].dmacntH() >> getHalf(byte_addr)),
0xD4...0xDB => null, // DMA3SAD, DMA3DAD
0xDC, 0xDD => 0x00, // DMA3CNT_L
0xDE, 0xDF => @truncate(T, dma.*[3].dmacntH() >> shift(byte)),
0xDE, 0xDF => @truncate(T, dma.*[3].dmacntH() >> getHalf(byte_addr)),
else => util.io.read.err(T, log, "unexpected {} read from 0x{X:0>8}", .{ T, addr }),
},
else => @compileError("DMA: Unsupported read width"),
@ -74,55 +74,71 @@ 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 {
const byte = @truncate(u8, addr);
const byte_addr = @truncate(u8, addr);
switch (T) {
u32 => switch (byte) {
u32 => switch (byte_addr) {
0xB0 => dma.*[0].setDmasad(value),
0xB4 => dma.*[0].setDmadad(value),
0xB8 => dma.*[0].setDmacnt(value),
0xBC => dma.*[1].setDmasad(value),
0xC0 => dma.*[1].setDmadad(value),
0xC4 => dma.*[1].setDmacnt(value),
0xC8 => dma.*[2].setDmasad(value),
0xCC => dma.*[2].setDmadad(value),
0xD0 => dma.*[2].setDmacnt(value),
0xD4 => dma.*[3].setDmasad(value),
0xD8 => dma.*[3].setDmadad(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 }),
},
u16 => switch (byte) {
0xB0 => dma.*[0].setDmasad(setLo(u32, dma.*[0].sad, 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)),
u16 => switch (byte_addr) {
0xB0, 0xB2 => dma.*[0].setDmasad(setHalf(u32, dma.*[0].sad, byte_addr, value)),
0xB4, 0xB6 => dma.*[0].setDmadad(setHalf(u32, dma.*[0].dad, byte_addr, value)),
0xB8 => dma.*[0].setDmacntL(value),
0xBA => dma.*[0].setDmacntH(value),
0xBC => dma.*[1].setDmasad(setLo(u32, dma.*[1].sad, 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)),
0xBC, 0xBE => dma.*[1].setDmasad(setHalf(u32, dma.*[1].sad, byte_addr, value)),
0xC0, 0xC2 => dma.*[1].setDmadad(setHalf(u32, dma.*[1].dad, byte_addr, value)),
0xC4 => dma.*[1].setDmacntL(value),
0xC6 => dma.*[1].setDmacntH(value),
0xC8 => dma.*[2].setDmasad(setLo(u32, dma.*[2].sad, 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)),
0xC8, 0xCA => dma.*[2].setDmasad(setHalf(u32, dma.*[2].sad, byte_addr, value)),
0xCC, 0xCE => dma.*[2].setDmadad(setHalf(u32, dma.*[2].dad, byte_addr, value)),
0xD0 => dma.*[2].setDmacntL(value),
0xD2 => dma.*[2].setDmacntH(value),
0xD4 => dma.*[3].setDmasad(setLo(u32, dma.*[3].sad, 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)),
0xD4, 0xD6 => dma.*[3].setDmasad(setHalf(u32, dma.*[3].sad, byte_addr, value)),
0xD8, 0xDA => dma.*[3].setDmadad(setHalf(u32, dma.*[3].dad, byte_addr, value)),
0xDC => dma.*[3].setDmacntL(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 }),
},
u8 => util.io.write.undef(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, addr }),
u8 => switch (byte_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"),
}
}

View File

@ -179,7 +179,7 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
0x0400_0056 => {}, // Not used
// Sound
0x0400_0060...0x0400_009E => apu.write(T, &bus.apu, address, value),
0x0400_0060...0x0400_00A6 => apu.write(T, &bus.apu, address, value),
// Dma Transfers
0x0400_00B0...0x0400_00DE => dma.write(T, &bus.dma, address, value),
@ -217,11 +217,17 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
},
u8 => switch (address) {
// Display
0x0400_0000...0x0400_0054 => ppu.write(T, &bus.ppu, address, value),
0x0400_0000...0x0400_0055 => ppu.write(T, &bus.ppu, address, value),
// Sound
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
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}),

View File

@ -8,71 +8,93 @@ const Arm7tdmi = @import("../cpu.zig").Arm7tdmi;
pub const TimerTuple = std.meta.Tuple(&[_]type{ Timer(0), Timer(1), Timer(2), Timer(3) });
const log = std.log.scoped(.Timer);
const shift = util.shift;
const getHalf = util.getHalf;
const setHalf = util.setHalf;
pub fn create(sched: *Scheduler) TimerTuple {
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 {
const nybble = @truncate(u4, addr);
const nybble_addr = @truncate(u4, addr);
return switch (T) {
u32 => switch (nybble) {
u32 => switch (nybble_addr) {
0x0 => @as(T, tim.*[0].cnt.raw) << 16 | tim.*[0].timcntL(),
0x4 => @as(T, tim.*[1].cnt.raw) << 16 | tim.*[1].timcntL(),
0x8 => @as(T, tim.*[2].cnt.raw) << 16 | tim.*[2].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 }),
},
u16 => switch (nybble) {
u16 => switch (nybble_addr) {
0x0 => tim.*[0].timcntL(),
0x2 => tim.*[0].cnt.raw,
0x4 => tim.*[1].timcntL(),
0x6 => tim.*[1].cnt.raw,
0x8 => tim.*[2].timcntL(),
0xA => tim.*[2].cnt.raw,
0xC => tim.*[3].timcntL(),
0xE => tim.*[3].cnt.raw,
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
},
u8 => switch (nybble) {
0x0, 0x1 => @truncate(T, tim.*[0].timcntL() >> shift(nybble)),
0x2, 0x3 => @truncate(T, tim.*[0].cnt.raw >> shift(nybble)),
0x4, 0x5 => @truncate(T, tim.*[1].timcntL() >> shift(nybble)),
0x6, 0x7 => @truncate(T, tim.*[1].cnt.raw >> shift(nybble)),
0x8, 0x9 => @truncate(T, tim.*[2].timcntL() >> shift(nybble)),
0xA, 0xB => @truncate(T, tim.*[2].cnt.raw >> shift(nybble)),
0xC, 0xD => @truncate(T, tim.*[3].timcntL() >> shift(nybble)),
0xE, 0xF => @truncate(T, tim.*[3].cnt.raw >> shift(nybble)),
u8 => switch (nybble_addr) {
0x0, 0x1 => @truncate(T, tim.*[0].timcntL() >> getHalf(nybble_addr)),
0x2, 0x3 => @truncate(T, tim.*[0].cnt.raw >> getHalf(nybble_addr)),
0x4, 0x5 => @truncate(T, tim.*[1].timcntL() >> getHalf(nybble_addr)),
0x6, 0x7 => @truncate(T, tim.*[1].cnt.raw >> getHalf(nybble_addr)),
0x8, 0x9 => @truncate(T, tim.*[2].timcntL() >> getHalf(nybble_addr)),
0xA, 0xB => @truncate(T, tim.*[2].cnt.raw >> getHalf(nybble_addr)),
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"),
};
}
pub fn write(comptime T: type, tim: *TimerTuple, addr: u32, value: T) void {
const nybble = @truncate(u4, addr);
const nybble_addr = @truncate(u4, addr);
return switch (T) {
u32 => switch (nybble) {
u32 => switch (nybble_addr) {
0x0 => tim.*[0].setTimcnt(value),
0x4 => tim.*[1].setTimcnt(value),
0x8 => tim.*[2].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 }),
},
u16 => switch (nybble) {
u16 => switch (nybble_addr) {
0x0 => tim.*[0].setTimcntL(value),
0x2 => tim.*[0].setTimcntH(value),
0x4 => tim.*[1].setTimcntL(value),
0x6 => tim.*[1].setTimcntH(value),
0x8 => tim.*[2].setTimcntL(value),
0xA => tim.*[2].setTimcntH(value),
0xC => tim.*[3].setTimcntL(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 }),
},
u8 => util.io.write.undef(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, addr }),
u8 => switch (nybble_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"),
};
}

View File

@ -11,9 +11,9 @@ const Bitfield = @import("bitfield").Bitfield;
const Allocator = std.mem.Allocator;
const log = std.log.scoped(.PPU);
const setHi = util.setHi;
const setLo = util.setLo;
const shift = util.shift;
const getHalf = util.getHalf;
const setHalf = util.setHalf;
const setQuart = util.setQuart;
const pollDmaOnBlank = @import("bus/dma.zig").pollDmaOnBlank;
pub const width = 240;
@ -21,24 +21,25 @@ pub const height = 160;
pub const framebuf_pitch = width * @sizeOf(u32);
pub fn read(comptime T: type, ppu: *const Ppu, addr: u32) ?T {
const byte = @truncate(u8, addr);
const byte_addr = @truncate(u8, addr);
return switch (T) {
u32 => switch (byte) {
u32 => switch (byte_addr) {
0x00 => ppu.dispcnt.raw, // Green Swap is in high half-word
0x04 => @as(T, ppu.vcount.raw) << 16 | ppu.dispstat.raw,
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,
0x10...0x1C => null, // BGXHOFS/VOFS
0x20...0x3C => null, // BG2/3 Rot Scaling Registers
0x40...0x44 => null, // WINXH/V Registers
0x48 => @as(T, ppu.win.out()) << 16 | ppu.win.in(),
0x10, 0x14, 0x18, 0x1C => null, // BGXHOFS/VOFS
0x20, 0x24, 0x28, 0x2C => null, // BG2 Rot/Scaling
0x30, 0x34, 0x38, 0x3C => null, // BG3 Rot/Scaling
0x40, 0x44 => null, // WINXH/V Registers
0x48 => @as(T, ppu.win.getOut()) << 16 | ppu.win.getIn(),
0x4C => null, // MOSAIC, undefined in high byte
0x50 => @as(T, ppu.bld.getAlpha()) << 16 | ppu.bld.getCnt(),
0x54 => null, // BLDY, undefined in high half-wrd
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
},
u16 => switch (byte) {
u16 => switch (byte_addr) {
0x00 => ppu.dispcnt.raw,
0x02 => null, // Green Swap
0x04 => ppu.dispstat.raw,
@ -47,35 +48,39 @@ pub fn read(comptime T: type, ppu: *const Ppu, addr: u32) ?T {
0x0A => ppu.bg[1].bg1Cnt(),
0x0C => ppu.bg[2].cnt.raw,
0x0E => ppu.bg[3].cnt.raw,
0x10...0x1E => null, // BGXHOFS/VOFS
0x20...0x3E => null, // BG2/3 Rot Scaling Registers
0x40...0x46 => null, // WINXH/V Registers
0x48 => ppu.win.in(),
0x4A => ppu.win.out(),
0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E => null, // BGXHOFS/VOFS
0x20, 0x22, 0x24, 0x26, 0x28, 0x2A, 0x2C, 0x2E => null, // BG2 Rot/Scaling
0x30, 0x32, 0x34, 0x36, 0x38, 0x3A, 0x3C, 0x3E => null, // BG3 Rot/Scaling
0x40, 0x42, 0x44, 0x46 => null, // WINXH/V Registers
0x48 => ppu.win.getIn(),
0x4A => ppu.win.getOut(),
0x4C => null, // MOSAIC
0x4E => null,
0x50 => ppu.bld.getCnt(),
0x52 => ppu.bld.getAlpha(),
0x54 => null, // BLDY
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
},
u8 => switch (byte) {
0x00, 0x01 => @truncate(T, ppu.dispcnt.raw >> shift(byte)),
0x02...0x03 => null,
0x04, 0x05 => @truncate(T, ppu.dispstat.raw >> shift(byte)),
0x06, 0x07 => @truncate(T, ppu.vcount.raw >> shift(byte)),
0x08, 0x09 => @truncate(T, ppu.bg[0].bg0Cnt() >> shift(byte)),
0x0A, 0x0B => @truncate(T, ppu.bg[1].bg1Cnt() >> shift(byte)),
0x0C, 0x0D => @truncate(T, ppu.bg[2].cnt.raw >> shift(byte)),
0x0E, 0x0F => @truncate(T, ppu.bg[3].cnt.raw >> shift(byte)),
u8 => switch (byte_addr) {
0x00, 0x01 => @truncate(T, ppu.dispcnt.raw >> getHalf(byte_addr)),
0x02, 0x03 => null,
0x04, 0x05 => @truncate(T, ppu.dispstat.raw >> getHalf(byte_addr)),
0x06, 0x07 => @truncate(T, ppu.vcount.raw >> getHalf(byte_addr)),
0x08, 0x09 => @truncate(T, ppu.bg[0].bg0Cnt() >> getHalf(byte_addr)),
0x0A, 0x0B => @truncate(T, ppu.bg[1].bg1Cnt() >> getHalf(byte_addr)),
0x0C, 0x0D => @truncate(T, ppu.bg[2].cnt.raw >> getHalf(byte_addr)),
0x0E, 0x0F => @truncate(T, ppu.bg[3].cnt.raw >> getHalf(byte_addr)),
0x10...0x1F => null, // BGXHOFS/VOFS
0x20...0x3F => null, // BG2/3 Rot Scaling Registers
0x20...0x2F => null, // BG2 Rot/Scaling
0x30...0x3F => null, // BG3 Rot/Scaling
0x40...0x47 => null, // WINXH/V Registers
0x48, 0x49 => @truncate(T, ppu.win.in() >> shift(byte)),
0x4A, 0x4B => @truncate(T, ppu.win.out() >> shift(byte)),
0x4C...0x4D => null, // MOSAIC
0x50, 0x51 => @truncate(T, ppu.bld.getCnt() >> shift(byte)),
0x52, 0x53 => @truncate(T, ppu.bld.getAlpha() >> shift(byte)),
0x54...0x55 => null, // BLDY
0x48, 0x49 => @truncate(T, ppu.win.getIn() >> getHalf(byte_addr)),
0x4A, 0x4B => @truncate(T, ppu.win.getOut() >> getHalf(byte_addr)),
0x4C, 0x4D => null, // MOSAIC
0x4E, 0x4F => null,
0x50, 0x51 => @truncate(T, ppu.bld.getCnt() >> getHalf(byte_addr)),
0x52, 0x53 => @truncate(T, ppu.bld.getAlpha() >> getHalf(byte_addr)),
0x54, 0x55 => null, // BLDY
else => util.io.read.err(T, log, "unexpected {} read from 0x{X:0>8}", .{ T, addr }),
},
else => @compileError("PPU: Unsupported read width"),
@ -83,10 +88,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 {
const byte = @truncate(u8, addr); // prefixed with 0x0400_00
const byte_addr = @truncate(u8, addr); // prefixed with 0x0400_00
switch (T) {
u32 => switch (byte) {
u32 => switch (byte_addr) {
0x00 => ppu.dispcnt.raw = @truncate(u16, value),
0x04 => {
ppu.dispstat.raw = @truncate(u16, value);
@ -94,38 +99,45 @@ pub fn write(comptime T: type, ppu: *Ppu, addr: u32, value: T) void {
},
0x08 => ppu.setAdjCnts(0, value),
0x0C => ppu.setAdjCnts(2, value),
0x10 => ppu.setBgOffsets(0, value),
0x14 => ppu.setBgOffsets(1, value),
0x18 => ppu.setBgOffsets(2, value),
0x1C => ppu.setBgOffsets(3, value),
0x20 => ppu.aff_bg[0].writePaPb(value),
0x24 => ppu.aff_bg[0].writePcPd(value),
0x28 => ppu.aff_bg[0].setX(ppu.dispstat.vblank.read(), value),
0x2C => ppu.aff_bg[0].setY(ppu.dispstat.vblank.read(), value),
0x30 => ppu.aff_bg[1].writePaPb(value),
0x34 => ppu.aff_bg[1].writePcPd(value),
0x38 => ppu.aff_bg[1].setX(ppu.dispstat.vblank.read(), value),
0x3C => ppu.aff_bg[1].setY(ppu.dispstat.vblank.read(), value),
0x40 => ppu.win.setH(value),
0x44 => ppu.win.setV(value),
0x48 => ppu.win.setIo(value),
0x4C => log.debug("Wrote 0x{X:0>8} to MOSAIC", .{value}),
0x50 => {
ppu.bld.cnt.raw = @truncate(u16, value);
ppu.bld.alpha.raw = @truncate(u16, value >> 16);
},
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 }),
},
u16 => switch (byte) {
u16 => switch (byte_addr) {
0x00 => ppu.dispcnt.raw = value,
0x02 => {}, // Green Swap
0x04 => ppu.dispstat.raw = value,
0x06 => {}, // vcount is read-only
0x06 => {}, // VCOUNT
0x08 => ppu.bg[0].cnt.raw = value,
0x0A => ppu.bg[1].cnt.raw = value,
0x0C => ppu.bg[2].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?
0x12 => ppu.bg[0].vofs.raw = value,
0x14 => ppu.bg[1].hofs.raw = value,
@ -134,45 +146,87 @@ pub fn write(comptime T: type, ppu: *Ppu, addr: u32, value: T) void {
0x1A => ppu.bg[2].vofs.raw = value,
0x1C => ppu.bg[3].hofs.raw = value,
0x1E => ppu.bg[3].vofs.raw = value,
0x20 => ppu.aff_bg[0].pa = @bitCast(i16, value),
0x22 => ppu.aff_bg[0].pb = @bitCast(i16, value),
0x24 => ppu.aff_bg[0].pc = @bitCast(i16, value),
0x26 => ppu.aff_bg[0].pd = @bitCast(i16, value),
0x28 => ppu.aff_bg[0].x = @bitCast(i32, setLo(u32, @bitCast(u32, ppu.aff_bg[0].x), 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)),
0x28, 0x2A => ppu.aff_bg[0].x = @bitCast(i32, setHalf(u32, @bitCast(u32, ppu.aff_bg[0].x), byte_addr, value)),
0x2C, 0x2E => ppu.aff_bg[0].y = @bitCast(i32, setHalf(u32, @bitCast(u32, ppu.aff_bg[0].y), byte_addr, value)),
0x30 => ppu.aff_bg[1].pa = @bitCast(i16, value),
0x32 => ppu.aff_bg[1].pb = @bitCast(i16, value),
0x34 => ppu.aff_bg[1].pc = @bitCast(i16, value),
0x36 => ppu.aff_bg[1].pd = @bitCast(i16, value),
0x38 => ppu.aff_bg[1].x = @bitCast(i32, setLo(u32, @bitCast(u32, ppu.aff_bg[1].x), 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)),
0x38, 0x3A => ppu.aff_bg[1].x = @bitCast(i32, setHalf(u32, @bitCast(u32, ppu.aff_bg[1].x), byte_addr, value)),
0x3C, 0x3E => ppu.aff_bg[1].y = @bitCast(i32, setHalf(u32, @bitCast(u32, ppu.aff_bg[1].y), byte_addr, value)),
0x40 => ppu.win.h[0].raw = value,
0x42 => ppu.win.h[1].raw = value,
0x44 => ppu.win.v[0].raw = value,
0x46 => ppu.win.v[1].raw = value,
0x48 => ppu.win._in.raw = value,
0x4A => ppu.win._out.raw = value,
0x48 => ppu.win.in.raw = value,
0x4A => ppu.win.out.raw = value,
0x4C => log.debug("Wrote 0x{X:0>4} to MOSAIC", .{value}),
0x4E => {},
0x50 => ppu.bld.cnt.raw = value,
0x52 => ppu.bld.alpha.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 }),
},
u8 => switch (byte) {
0x04 => ppu.dispstat.raw = setLo(u16, ppu.dispstat.raw, value),
0x05 => ppu.dispstat.raw = setHi(u16, ppu.dispstat.raw, value),
0x08 => ppu.bg[0].cnt.raw = setLo(u16, ppu.bg[0].cnt.raw, value),
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),
0x0B => ppu.bg[1].cnt.raw = setHi(u16, ppu.bg[1].cnt.raw, value),
0x48 => ppu.win._in.raw = setLo(u16, ppu.win._in.raw, value),
0x49 => ppu.win._in.raw = setHi(u16, ppu.win._in.raw, value),
0x4A => ppu.win._out.raw = setLo(u16, ppu.win._out.raw, value),
0x54 => ppu.bld.y.raw = setLo(u16, ppu.bld.y.raw, value),
u8 => switch (byte_addr) {
0x00, 0x01 => ppu.dispcnt.raw = setHalf(u16, ppu.dispcnt.raw, byte_addr, value),
0x02, 0x03 => {}, // Green Swap
0x04, 0x05 => ppu.dispstat.raw = setHalf(u16, ppu.dispstat.raw, byte_addr, value),
0x06, 0x07 => {}, // VCOUNT
// BGXCNT
0x08, 0x09 => ppu.bg[0].cnt.raw = setHalf(u16, ppu.bg[0].cnt.raw, byte_addr, value),
0x0A, 0x0B => ppu.bg[1].cnt.raw = setHalf(u16, ppu.bg[1].cnt.raw, byte_addr, value),
0x0C, 0x0D => ppu.bg[2].cnt.raw = setHalf(u16, ppu.bg[2].cnt.raw, byte_addr, value),
0x0E, 0x0F => ppu.bg[3].cnt.raw = setHalf(u16, ppu.bg[3].cnt.raw, byte_addr, 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 => @compileError("PPU: Unsupported write width"),
@ -961,25 +1015,25 @@ const Window = struct {
h: [2]io.WinH,
v: [2]io.WinV,
_out: io.WinOut,
_in: io.WinIn,
out: io.WinOut,
in: io.WinIn,
fn init() Self {
return .{
.h = [_]io.WinH{.{ .raw = 0 }} ** 2,
.v = [_]io.WinV{.{ .raw = 0 }} ** 2,
._out = .{ .raw = 0 },
._in = .{ .raw = 0 },
.out = .{ .raw = 0 },
.in = .{ .raw = 0 },
};
}
pub fn in(self: *const Self) u16 {
return self._in.raw & 0x3F3F;
pub fn getIn(self: *const Self) u16 {
return self.in.raw & 0x3F3F;
}
pub fn out(self: *const Self) u16 {
return self._out.raw & 0x3F3F;
pub fn getOut(self: *const Self) u16 {
return self.out.raw & 0x3F3F;
}
pub fn setH(self: *Self, value: u32) void {
@ -993,8 +1047,8 @@ const Window = struct {
}
pub fn setIo(self: *Self, value: u32) void {
self._in.raw = @truncate(u16, value);
self._out.raw = @truncate(u16, value >> 16);
self.in.raw = @truncate(u16, value);
self.out.raw = @truncate(u16, value >> 16);
}
};

View File

@ -283,13 +283,43 @@ 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
///
/// TODO: Rename this
pub inline fn shift(byte: u8) u4 {
/// TODO: Support u16 reads of u32 values?
pub inline fn getHalf(byte: u8) u4 {
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
pub inline fn setLo(comptime T: type, left: T, right: HalfInt(T)) T {
return switch (T) {