Compare commits
9 Commits
d859cee365
...
7debdc490d
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | 7debdc490d | |
Rekai Nyangadzayi Musuka | 58375795bf | |
Rekai Nyangadzayi Musuka | f0dca29836 | |
Rekai Nyangadzayi Musuka | c75682dbd4 | |
Rekai Nyangadzayi Musuka | 36832ba1fb | |
Rekai Nyangadzayi Musuka | 647bd83224 | |
Rekai Nyangadzayi Musuka | c831f67d1a | |
Rekai Nyangadzayi Musuka | 268961262d | |
Rekai Nyangadzayi Musuka | 3e62feacba |
|
@ -112,6 +112,7 @@ pub fn dbgRead(self: *const Self, comptime T: type, address: u32) T {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO: Should open bus read addresses be force-aligned?
|
||||||
fn readIo(self: *const Self, comptime T: type, unaligned_address: u32) T {
|
fn readIo(self: *const Self, comptime T: type, unaligned_address: u32) T {
|
||||||
const maybe_value = io.read(self, T, forceAlign(T, unaligned_address));
|
const maybe_value = io.read(self, T, forceAlign(T, unaligned_address));
|
||||||
return if (maybe_value) |value| value else self.openBus(T, unaligned_address);
|
return if (maybe_value) |value| value else self.openBus(T, unaligned_address);
|
||||||
|
|
101
src/core/apu.zig
101
src/core/apu.zig
|
@ -12,9 +12,10 @@ const Noise = @import("apu/Noise.zig");
|
||||||
|
|
||||||
const SoundFifo = std.fifo.LinearFifo(u8, .{ .Static = 0x20 });
|
const SoundFifo = std.fifo.LinearFifo(u8, .{ .Static = 0x20 });
|
||||||
|
|
||||||
const intToBytes = @import("../util.zig").intToBytes;
|
const setHi = util.setHi;
|
||||||
const setHi = @import("../util.zig").setHi;
|
const setLo = util.setLo;
|
||||||
const setLo = @import("../util.zig").setLo;
|
const shift = util.shift;
|
||||||
|
const intToBytes = util.intToBytes;
|
||||||
|
|
||||||
const log = std.log.scoped(.APU);
|
const log = std.log.scoped(.APU);
|
||||||
|
|
||||||
|
@ -25,42 +26,82 @@ pub fn read(comptime T: type, apu: *const Apu, addr: u32) ?T {
|
||||||
const byte = @truncate(u8, addr);
|
const byte = @truncate(u8, addr);
|
||||||
|
|
||||||
return switch (T) {
|
return switch (T) {
|
||||||
|
u32 => switch (byte) {
|
||||||
|
0x60 => @as(T, apu.ch1.sound1CntH()) << 16 | apu.ch1.sound1CntL(),
|
||||||
|
0x64 => apu.ch1.sound1CntX(),
|
||||||
|
0x68 => apu.ch2.sound2CntL(),
|
||||||
|
0x6C => apu.ch2.sound2CntH(),
|
||||||
|
0x70 => @as(T, apu.ch3.sound3CntH()) << 16 | apu.ch3.sound3CntL(),
|
||||||
|
0x74 => apu.ch3.sound3CntX(),
|
||||||
|
0x78 => apu.ch4.sound4CntL(),
|
||||||
|
0x7C => apu.ch4.sound4CntH(),
|
||||||
|
0x80 => @as(T, apu.dma_cnt.raw) << 16 | apu.psg_cnt.raw, // SOUNDCNT_H, SOUNDCNT_L
|
||||||
|
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),
|
||||||
|
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) {
|
||||||
0x60 => apu.ch1.sound1CntL(),
|
0x60 => apu.ch1.sound1CntL(),
|
||||||
0x62 => apu.ch1.sound1CntH(),
|
0x62 => apu.ch1.sound1CntH(),
|
||||||
0x64 => apu.ch1.sound1CntX(),
|
0x64 => apu.ch1.sound1CntX(),
|
||||||
|
0x66 => 0x0000, // suite.gba expects 0x0000, not 0xDEAD
|
||||||
0x68 => apu.ch2.sound2CntL(),
|
0x68 => apu.ch2.sound2CntL(),
|
||||||
|
0x6A => 0x0000,
|
||||||
0x6C => apu.ch2.sound2CntH(),
|
0x6C => apu.ch2.sound2CntH(),
|
||||||
|
0x6E => 0x0000,
|
||||||
0x70 => apu.ch3.select.raw & 0xE0, // SOUND3CNT_L
|
0x70 => apu.ch3.sound3CntL(),
|
||||||
0x72 => apu.ch3.sound3CntH(),
|
0x72 => apu.ch3.sound3CntH(),
|
||||||
0x74 => apu.ch3.freq.raw & 0x4000, // SOUND3CNT_X
|
0x74 => apu.ch3.sound3CntX(),
|
||||||
|
0x76 => 0x0000,
|
||||||
0x78 => apu.ch4.sound4CntL(),
|
0x78 => apu.ch4.sound4CntL(),
|
||||||
|
0x7A => 0x0000,
|
||||||
0x7C => apu.ch4.sound4CntH(),
|
0x7C => apu.ch4.sound4CntH(),
|
||||||
|
0x7E => 0x0000,
|
||||||
0x80 => apu.psg_cnt.raw & 0xFF77, // SOUNDCNT_L
|
0x80 => apu.soundCntL(),
|
||||||
0x82 => apu.dma_cnt.raw & 0x770F, // SOUNDCNT_H
|
0x82 => apu.soundCntH(),
|
||||||
0x84 => apu.soundCntX(),
|
0x84 => apu.soundCntX(),
|
||||||
|
0x86 => 0x0000,
|
||||||
0x88 => apu.bias.raw, // SOUNDBIAS
|
0x88 => apu.bias.raw, // SOUNDBIAS
|
||||||
0x90...0x9F => apu.ch3.wave_dev.read(T, apu.ch3.select, addr),
|
0x8A => 0x0000,
|
||||||
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }),
|
0x8C, 0x8E => null,
|
||||||
|
0x90, 0x92, 0x94, 0x96, 0x98, 0x9A, 0x9C, 0x9E => apu.ch3.wave_dev.read(T, apu.ch3.select, addr),
|
||||||
|
0xA0, 0xA2 => null, // FIFO_A
|
||||||
|
0xA4, 0xA6 => null, // FIFO_B
|
||||||
|
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
||||||
},
|
},
|
||||||
u8 => switch (byte) {
|
u8 => switch (byte) {
|
||||||
0x60 => apu.ch1.sound1CntL(), // NR10
|
0x60, 0x61 => @truncate(T, @as(u16, apu.ch1.sound1CntL()) >> shift(byte)),
|
||||||
0x62 => apu.ch1.duty.raw, // NR11
|
0x62, 0x63 => @truncate(T, apu.ch1.sound1CntH() >> shift(byte)),
|
||||||
0x63 => apu.ch1.envelope.raw, // NR12
|
0x64, 0x65 => @truncate(T, apu.ch1.sound1CntX() >> shift(byte)),
|
||||||
0x68 => apu.ch2.duty.raw, // NR21
|
0x66, 0x67 => 0x00, // assuming behaviour is identical to that of 16-bit reads
|
||||||
0x69 => apu.ch2.envelope.raw, // NR22
|
0x68, 0x69 => @truncate(T, apu.ch2.sound2CntL() >> shift(byte)),
|
||||||
0x73 => apu.ch3.vol.raw, // NR32
|
0x6A, 0x6B => 0x00,
|
||||||
0x79 => apu.ch4.envelope.raw, // NR42
|
0x6C, 0x6D => @truncate(T, apu.ch2.sound2CntH() >> shift(byte)),
|
||||||
0x7C => apu.ch4.poly.raw, // NR43
|
0x6E, 0x6F => 0x00,
|
||||||
0x81 => @truncate(u8, apu.psg_cnt.raw >> 8), // NR51
|
0x70, 0x71 => @truncate(T, @as(u16, apu.ch3.sound3CntL()) >> shift(byte)), // SOUND3CNT_L
|
||||||
0x84 => apu.soundCntX(),
|
0x72, 0x73 => @truncate(T, apu.ch3.sound3CntH() >> shift(byte)),
|
||||||
0x89 => @truncate(u8, apu.bias.raw >> 8), // SOUNDBIAS_H
|
0x74, 0x75 => @truncate(T, apu.ch3.sound3CntX() >> shift(byte)), // SOUND3CNT_L
|
||||||
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }),
|
0x76, 0x77 => 0x00,
|
||||||
|
0x78, 0x79 => @truncate(T, apu.ch4.sound4CntL() >> shift(byte)),
|
||||||
|
0x7A, 0x7B => 0x00,
|
||||||
|
0x7C, 0x7D => @truncate(T, apu.ch4.sound4CntH() >> shift(byte)),
|
||||||
|
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)),
|
||||||
|
0x86, 0x87 => 0x00,
|
||||||
|
0x88, 0x89 => @truncate(T, apu.bias.raw >> shift(byte)), // SOUNDBIAS
|
||||||
|
0x8A, 0x8B => 0x00,
|
||||||
|
0x8C...0x8F => null,
|
||||||
|
0x90...0x9F => apu.ch3.wave_dev.read(T, apu.ch3.select, addr),
|
||||||
|
0xA0, 0xA1, 0xA2, 0xA3 => null, // FIFO_A
|
||||||
|
0xA4, 0xA5, 0xA6, 0xA7 => null, // FIFO_B
|
||||||
|
else => util.io.read.err(T, log, "unexpected {} read from 0x{X:0>8}", .{ T, addr }),
|
||||||
},
|
},
|
||||||
u32 => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }),
|
|
||||||
else => @compileError("APU: Unsupported read width"),
|
else => @compileError("APU: Unsupported read width"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -219,6 +260,11 @@ pub const Apu = struct {
|
||||||
self.setSoundCntH(@truncate(u16, value >> 16));
|
self.setSoundCntH(@truncate(u16, value >> 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// SOUNDCNT_L
|
||||||
|
pub fn soundCntL(self: *const Self) u16 {
|
||||||
|
return self.psg_cnt.raw & 0xFF77;
|
||||||
|
}
|
||||||
|
|
||||||
/// 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 };
|
||||||
|
@ -231,6 +277,11 @@ pub const Apu = struct {
|
||||||
self.dma_cnt = new;
|
self.dma_cnt = new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// SOUNDCNT_H
|
||||||
|
pub fn soundCntH(self: *const Self) u16 {
|
||||||
|
return self.dma_cnt.raw & 0x770F;
|
||||||
|
}
|
||||||
|
|
||||||
/// NR52
|
/// NR52
|
||||||
pub fn setSoundCntX(self: *Self, value: bool) void {
|
pub fn setSoundCntX(self: *Self, value: bool) void {
|
||||||
self.cnt.apu_enable.write(value);
|
self.cnt.apu_enable.write(value);
|
||||||
|
|
|
@ -71,6 +71,11 @@ pub fn setSound3CntL(self: *Self, value: u8) void {
|
||||||
if (!self.select.enabled.read()) self.enabled = false;
|
if (!self.select.enabled.read()) self.enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// NR30
|
||||||
|
pub fn sound3CntL(self: *const Self) u8 {
|
||||||
|
return self.select.raw & 0xE0;
|
||||||
|
}
|
||||||
|
|
||||||
/// NR31, NR32
|
/// NR31, NR32
|
||||||
pub fn sound3CntH(self: *const Self) u16 {
|
pub fn sound3CntH(self: *const Self) u16 {
|
||||||
return @as(u16, self.length & 0xE0) << 8;
|
return @as(u16, self.length & 0xE0) << 8;
|
||||||
|
@ -94,6 +99,11 @@ pub fn setSound3CntX(self: *Self, fs: *const FrameSequencer, value: u16) void {
|
||||||
self.setNr34(fs, @truncate(u8, value >> 8));
|
self.setNr34(fs, @truncate(u8, value >> 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// NR33, NR34
|
||||||
|
pub fn sound3CntX(self: *const Self) u16 {
|
||||||
|
return self.freq.raw & 0x4000;
|
||||||
|
}
|
||||||
|
|
||||||
/// NR33
|
/// NR33
|
||||||
pub fn setNr33(self: *Self, byte: u8) void {
|
pub fn setNr33(self: *Self, byte: u8) void {
|
||||||
self.freq.raw = (self.freq.raw & 0xFF00) | byte;
|
self.freq.raw = (self.freq.raw & 0xFF00) | byte;
|
||||||
|
|
|
@ -10,6 +10,7 @@ const log = std.log.scoped(.DmaTransfer);
|
||||||
|
|
||||||
const setHi = util.setHi;
|
const setHi = util.setHi;
|
||||||
const setLo = util.setLo;
|
const setLo = util.setLo;
|
||||||
|
const shift = util.shift;
|
||||||
|
|
||||||
const rotr = @import("../../util.zig").rotr;
|
const rotr = @import("../../util.zig").rotr;
|
||||||
|
|
||||||
|
@ -22,20 +23,52 @@ pub fn read(comptime T: type, dma: *const DmaTuple, addr: u32) ?T {
|
||||||
|
|
||||||
return switch (T) {
|
return switch (T) {
|
||||||
u32 => switch (byte) {
|
u32 => switch (byte) {
|
||||||
0xB8 => @as(T, dma.*[0].cnt.raw) << 16,
|
0xB0, 0xB4 => null, // DMA0SAD, DMA0DAD,
|
||||||
0xC4 => @as(T, dma.*[1].cnt.raw) << 16,
|
0xB8 => @as(T, dma.*[0].dmacntH()) << 16, // DMA0CNT_L is write-only
|
||||||
0xD0 => @as(T, dma.*[2].cnt.raw) << 16,
|
0xBC, 0xC0 => null, // DMA1SAD, DMA1DAD
|
||||||
0xDC => @as(T, dma.*[3].cnt.raw) << 16,
|
0xC4 => @as(T, dma.*[1].dmacntH()) << 16, // DMA1CNT_L is write-only
|
||||||
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }),
|
0xC8, 0xCC => null, // DMA2SAD, DMA2DAD
|
||||||
|
0xD0 => @as(T, dma.*[2].dmacntH()) << 16, // DMA2CNT_L is write-only
|
||||||
|
0xD4, 0xD8 => null, // DMA3SAD, DMA3DAD
|
||||||
|
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) {
|
||||||
0xBA => dma.*[0].cnt.raw,
|
0xB0, 0xB2, 0xB4, 0xB6 => null, // DMA0SAD, DMA0DAD
|
||||||
0xC6 => dma.*[1].cnt.raw,
|
0xB8 => 0x0000, // DMA0CNT_L, suite.gba expects 0x0000 instead of 0xDEAD
|
||||||
0xD2 => dma.*[2].cnt.raw,
|
0xBA => dma.*[0].dmacntH(),
|
||||||
0xDE => dma.*[3].cnt.raw,
|
|
||||||
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }),
|
0xBC, 0xBE, 0xC0, 0xC2 => null, // DMA1SAD, DMA1DAD
|
||||||
|
0xC4 => 0x0000, // DMA1CNT_L
|
||||||
|
0xC6 => dma.*[1].dmacntH(),
|
||||||
|
|
||||||
|
0xC8, 0xCA, 0xCC, 0xCE => null, // DMA2SAD, DMA2DAD
|
||||||
|
0xD0 => 0x0000, // DMA2CNT_L
|
||||||
|
0xD2 => dma.*[2].dmacntH(),
|
||||||
|
|
||||||
|
0xD4, 0xD6, 0xD8, 0xDA => null, // DMA3SAD, DMA3DAD
|
||||||
|
0xDC => 0x0000, // DMA3CNT_L
|
||||||
|
0xDE => dma.*[3].dmacntH(),
|
||||||
|
else => util.io.read.err(T, log, "unaligned {} read from 0x{X:0>8}", .{ T, addr }),
|
||||||
|
},
|
||||||
|
u8 => switch (byte) {
|
||||||
|
0xB0...0xB7 => null, // DMA0SAD, DMA0DAD
|
||||||
|
0xB8, 0xB9 => 0x00, // DMA0CNT_L
|
||||||
|
0xBA, 0xBB => @truncate(T, dma.*[0].dmacntH() >> shift(byte)),
|
||||||
|
|
||||||
|
0xBC...0xC3 => null, // DMA1SAD, DMA1DAD
|
||||||
|
0xC4, 0xC5 => 0x00, // DMA1CNT_L
|
||||||
|
0xC6, 0xC7 => @truncate(T, dma.*[1].dmacntH() >> shift(byte)),
|
||||||
|
|
||||||
|
0xC8...0xCF => null, // DMA2SAD, DMA2DAD
|
||||||
|
0xD0, 0xD1 => 0x00, // DMA2CNT_L
|
||||||
|
0xD2, 0xD3 => @truncate(T, dma.*[2].dmacntH() >> shift(byte)),
|
||||||
|
|
||||||
|
0xD4...0xDB => null, // DMA3SAD, DMA3DAD
|
||||||
|
0xDC, 0xDD => 0x00, // DMA3CNT_L
|
||||||
|
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 }),
|
||||||
},
|
},
|
||||||
u8 => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }),
|
|
||||||
else => @compileError("DMA: Unsupported read width"),
|
else => @compileError("DMA: Unsupported read width"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -158,6 +191,10 @@ fn DmaController(comptime id: u2) type {
|
||||||
self.word_count = @truncate(@TypeOf(self.word_count), halfword);
|
self.word_count = @truncate(@TypeOf(self.word_count), halfword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dmacntH(self: *const Self) u16 {
|
||||||
|
return self.cnt.raw & if (id == 3) 0xFFE0 else 0xF7E0;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn setDmacntH(self: *Self, halfword: u16) void {
|
pub fn setDmacntH(self: *Self, halfword: u16) void {
|
||||||
const new = DmaControl{ .raw = halfword };
|
const new = DmaControl{ .raw = halfword };
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,9 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) ?T {
|
||||||
// Display
|
// Display
|
||||||
0x0400_0000...0x0400_0054 => ppu.read(T, &bus.ppu, address),
|
0x0400_0000...0x0400_0054 => ppu.read(T, &bus.ppu, address),
|
||||||
|
|
||||||
|
// Sound
|
||||||
|
0x0400_0060...0x0400_00A4 => apu.read(T, &bus.apu, address),
|
||||||
|
|
||||||
// DMA Transfers
|
// DMA Transfers
|
||||||
0x0400_00B0...0x0400_00DC => dma.read(T, &bus.dma, address),
|
0x0400_00B0...0x0400_00DC => dma.read(T, &bus.dma, address),
|
||||||
|
|
||||||
|
@ -70,7 +73,7 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) ?T {
|
||||||
0x0400_0000...0x0400_0054 => ppu.read(T, &bus.ppu, address),
|
0x0400_0000...0x0400_0054 => ppu.read(T, &bus.ppu, address),
|
||||||
|
|
||||||
// Sound
|
// Sound
|
||||||
0x0400_0060...0x0400_009E => apu.read(T, &bus.apu, address),
|
0x0400_0060...0x0400_00A6 => apu.read(T, &bus.apu, address),
|
||||||
|
|
||||||
// DMA Transfers
|
// DMA Transfers
|
||||||
0x0400_00B0...0x0400_00DE => dma.read(T, &bus.dma, address),
|
0x0400_00B0...0x0400_00DE => dma.read(T, &bus.dma, address),
|
||||||
|
@ -96,11 +99,17 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) ?T {
|
||||||
},
|
},
|
||||||
u8 => return switch (address) {
|
u8 => return switch (address) {
|
||||||
// Display
|
// Display
|
||||||
0x0400_0000...0x0400_0054 => ppu.read(T, &bus.ppu, address),
|
0x0400_0000...0x0400_0055 => ppu.read(T, &bus.ppu, address),
|
||||||
|
|
||||||
// Sound
|
// Sound
|
||||||
0x0400_0060...0x0400_00A7 => apu.read(T, &bus.apu, address),
|
0x0400_0060...0x0400_00A7 => apu.read(T, &bus.apu, address),
|
||||||
|
|
||||||
|
// DMA Transfers
|
||||||
|
0x0400_00B0...0x0400_00DF => dma.read(T, &bus.dma, address),
|
||||||
|
|
||||||
|
// Timers
|
||||||
|
0x0400_0100...0x0400_010F => timer.read(T, &bus.tim, address),
|
||||||
|
|
||||||
// Serial Communication 1
|
// Serial Communication 1
|
||||||
0x0400_0128 => util.io.read.todo(log, "Read {} from SIOCNT_L", .{T}),
|
0x0400_0128 => util.io.read.todo(log, "Read {} from SIOCNT_L", .{T}),
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ 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 shift = util.shift;
|
||||||
|
|
||||||
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) };
|
||||||
}
|
}
|
||||||
|
@ -21,7 +23,7 @@ pub fn read(comptime T: type, tim: *const TimerTuple, addr: u32) ?T {
|
||||||
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.undef(T, log, "Tried to perform a {} read to 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) {
|
u16 => switch (nybble) {
|
||||||
0x0 => tim.*[0].timcntL(),
|
0x0 => tim.*[0].timcntL(),
|
||||||
|
@ -32,9 +34,18 @@ pub fn read(comptime T: type, tim: *const TimerTuple, addr: u32) ?T {
|
||||||
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.undef(T, log, "Tried to perform a {} read to 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) {
|
||||||
|
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 => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }),
|
|
||||||
else => @compileError("TIM: Unsupported read width"),
|
else => @compileError("TIM: Unsupported read width"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
173
src/core/ppu.zig
173
src/core/ppu.zig
|
@ -13,6 +13,7 @@ const log = std.log.scoped(.PPU);
|
||||||
|
|
||||||
const setHi = util.setHi;
|
const setHi = util.setHi;
|
||||||
const setLo = util.setLo;
|
const setLo = util.setLo;
|
||||||
|
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;
|
||||||
|
@ -24,35 +25,58 @@ pub fn read(comptime T: type, ppu: *const Ppu, addr: u32) ?T {
|
||||||
|
|
||||||
return switch (T) {
|
return switch (T) {
|
||||||
u32 => switch (byte) {
|
u32 => switch (byte) {
|
||||||
0x00 => ppu.dispcnt.raw,
|
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,
|
||||||
0x06 => @as(T, ppu.bg[0].cnt.raw) << 16 | ppu.vcount.raw,
|
0x08 => @as(T, ppu.bg[1].bg1Cnt()) << 16 | ppu.bg[0].bg0Cnt(),
|
||||||
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }),
|
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(),
|
||||||
|
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) {
|
||||||
0x00 => ppu.dispcnt.raw,
|
0x00 => ppu.dispcnt.raw,
|
||||||
|
0x02 => null, // Green Swap
|
||||||
0x04 => ppu.dispstat.raw,
|
0x04 => ppu.dispstat.raw,
|
||||||
0x06 => ppu.vcount.raw,
|
0x06 => ppu.vcount.raw,
|
||||||
0x08 => ppu.bg[0].cnt.raw,
|
0x08 => ppu.bg[0].bg0Cnt(),
|
||||||
0x0A => ppu.bg[1].cnt.raw,
|
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,
|
||||||
0x4C => util.io.read.todo(log, "Read {} from MOSAIC", .{T}),
|
0x10...0x1E => null, // BGXHOFS/VOFS
|
||||||
0x50 => ppu.bldcnt.raw,
|
0x20...0x3E => null, // BG2/3 Rot Scaling Registers
|
||||||
0x52 => ppu.bldalpha.raw,
|
0x40...0x46 => null, // WINXH/V Registers
|
||||||
0x54 => ppu.bldy.raw,
|
0x48 => ppu.win.in(),
|
||||||
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }),
|
0x4A => ppu.win.out(),
|
||||||
|
0x4C => null, // MOSAIC
|
||||||
|
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) {
|
u8 => switch (byte) {
|
||||||
0x00 => @truncate(T, ppu.dispcnt.raw),
|
0x00, 0x01 => @truncate(T, ppu.dispcnt.raw >> shift(byte)),
|
||||||
0x04 => @truncate(T, ppu.dispstat.raw),
|
0x02...0x03 => null,
|
||||||
0x05 => @truncate(T, ppu.dispcnt.raw >> 8),
|
0x04, 0x05 => @truncate(T, ppu.dispstat.raw >> shift(byte)),
|
||||||
0x06 => @truncate(T, ppu.vcount.raw),
|
0x06, 0x07 => @truncate(T, ppu.vcount.raw >> shift(byte)),
|
||||||
0x08 => @truncate(T, ppu.bg[0].cnt.raw),
|
0x08, 0x09 => @truncate(T, ppu.bg[0].bg0Cnt() >> shift(byte)),
|
||||||
0x09 => @truncate(T, ppu.bg[0].cnt.raw >> 8),
|
0x0A, 0x0B => @truncate(T, ppu.bg[1].bg1Cnt() >> shift(byte)),
|
||||||
0x0A => @truncate(T, ppu.bg[1].cnt.raw),
|
0x0C, 0x0D => @truncate(T, ppu.bg[2].cnt.raw >> shift(byte)),
|
||||||
0x0B => @truncate(T, ppu.bg[1].cnt.raw >> 8),
|
0x0E, 0x0F => @truncate(T, ppu.bg[3].cnt.raw >> shift(byte)),
|
||||||
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }),
|
0x10...0x1F => null, // BGXHOFS/VOFS
|
||||||
|
0x20...0x3F => null, // BG2/3 Rot Scaling Registers
|
||||||
|
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
|
||||||
|
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"),
|
||||||
};
|
};
|
||||||
|
@ -87,10 +111,10 @@ pub fn write(comptime T: type, ppu: *Ppu, addr: u32, value: T) void {
|
||||||
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.bldcnt.raw = @truncate(u16, value);
|
ppu.bld.cnt.raw = @truncate(u16, value);
|
||||||
ppu.bldalpha.raw = @truncate(u16, value >> 16);
|
ppu.bld.alpha.raw = @truncate(u16, value >> 16);
|
||||||
},
|
},
|
||||||
0x54 => ppu.bldy.raw = @truncate(u16, value),
|
0x54 => ppu.bld.y.raw = @truncate(u16, value),
|
||||||
0x58...0x5C => {}, // Unused
|
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 }),
|
||||||
},
|
},
|
||||||
|
@ -130,12 +154,12 @@ pub fn write(comptime T: type, ppu: *Ppu, addr: u32, value: T) void {
|
||||||
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}),
|
||||||
0x50 => ppu.bldcnt.raw = value,
|
0x50 => ppu.bld.cnt.raw = value,
|
||||||
0x52 => ppu.bldalpha.raw = value,
|
0x52 => ppu.bld.alpha.raw = value,
|
||||||
0x54 => ppu.bldy.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) {
|
u8 => switch (byte) {
|
||||||
|
@ -145,10 +169,10 @@ pub fn write(comptime T: type, ppu: *Ppu, addr: u32, value: T) void {
|
||||||
0x09 => ppu.bg[0].cnt.raw = setHi(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),
|
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),
|
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),
|
0x48 => ppu.win._in.raw = setLo(u16, ppu.win._in.raw, value),
|
||||||
0x49 => ppu.win.in.raw = setHi(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),
|
0x4A => ppu.win._out.raw = setLo(u16, ppu.win._out.raw, value),
|
||||||
0x54 => ppu.bldy.raw = setLo(u16, ppu.bldy.raw, value),
|
0x54 => ppu.bld.y.raw = setLo(u16, ppu.bld.y.raw, 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"),
|
||||||
|
@ -168,9 +192,7 @@ pub const Ppu = struct {
|
||||||
dispstat: io.DisplayStatus,
|
dispstat: io.DisplayStatus,
|
||||||
vcount: io.VCount,
|
vcount: io.VCount,
|
||||||
|
|
||||||
bldcnt: io.BldCnt,
|
bld: Blend,
|
||||||
bldalpha: io.BldAlpha,
|
|
||||||
bldy: io.BldY,
|
|
||||||
|
|
||||||
vram: Vram,
|
vram: Vram,
|
||||||
palette: Palette,
|
palette: Palette,
|
||||||
|
@ -201,12 +223,10 @@ pub const Ppu = struct {
|
||||||
.win = Window.init(),
|
.win = Window.init(),
|
||||||
.bg = [_]Background{Background.init()} ** 4,
|
.bg = [_]Background{Background.init()} ** 4,
|
||||||
.aff_bg = [_]AffineBackground{AffineBackground.init()} ** 2,
|
.aff_bg = [_]AffineBackground{AffineBackground.init()} ** 2,
|
||||||
|
.bld = Blend.create(),
|
||||||
.dispcnt = .{ .raw = 0x0000 },
|
.dispcnt = .{ .raw = 0x0000 },
|
||||||
.dispstat = .{ .raw = 0x0000 },
|
.dispstat = .{ .raw = 0x0000 },
|
||||||
.vcount = .{ .raw = 0x0000 },
|
.vcount = .{ .raw = 0x0000 },
|
||||||
.bldcnt = .{ .raw = 0x0000 },
|
|
||||||
.bldalpha = .{ .raw = 0x0000 },
|
|
||||||
.bldy = .{ .raw = 0x0000 },
|
|
||||||
|
|
||||||
.scanline = try Scanline.init(allocator),
|
.scanline = try Scanline.init(allocator),
|
||||||
.scanline_sprites = sprites,
|
.scanline_sprites = sprites,
|
||||||
|
@ -301,7 +321,7 @@ pub const Ppu = struct {
|
||||||
const x = (sprite.x() +% i) % width;
|
const x = (sprite.x() +% i) % width;
|
||||||
const ix = @bitCast(i9, x);
|
const ix = @bitCast(i9, x);
|
||||||
|
|
||||||
if (!shouldDrawSprite(self.bldcnt, &self.scanline, x)) continue;
|
if (!shouldDrawSprite(self.bld.cnt, &self.scanline, x)) continue;
|
||||||
|
|
||||||
const sprite_start = sprite.x();
|
const sprite_start = sprite.x();
|
||||||
const isprite_start = @bitCast(i9, sprite_start);
|
const isprite_start = @bitCast(i9, sprite_start);
|
||||||
|
@ -330,7 +350,7 @@ pub const Ppu = struct {
|
||||||
// Sprite Palette starts at 0x0500_0200
|
// Sprite Palette starts at 0x0500_0200
|
||||||
if (pal_id != 0) {
|
if (pal_id != 0) {
|
||||||
const bgr555 = self.palette.read(u16, 0x200 + pal_id * 2);
|
const bgr555 = self.palette.read(u16, 0x200 + pal_id * 2);
|
||||||
copyToSpriteBuffer(self.bldcnt, &self.scanline, x, bgr555);
|
copyToSpriteBuffer(self.bld.cnt, &self.scanline, x, bgr555);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -351,7 +371,7 @@ pub const Ppu = struct {
|
||||||
const x = (sprite.x() +% i) % width;
|
const x = (sprite.x() +% i) % width;
|
||||||
const ix = @bitCast(i9, x);
|
const ix = @bitCast(i9, x);
|
||||||
|
|
||||||
if (!shouldDrawSprite(self.bldcnt, &self.scanline, x)) continue;
|
if (!shouldDrawSprite(self.bld.cnt, &self.scanline, x)) continue;
|
||||||
|
|
||||||
const sprite_start = sprite.x();
|
const sprite_start = sprite.x();
|
||||||
const isprite_start = @bitCast(i9, sprite_start);
|
const isprite_start = @bitCast(i9, sprite_start);
|
||||||
|
@ -386,7 +406,7 @@ pub const Ppu = struct {
|
||||||
// Sprite Palette starts at 0x0500_0200
|
// Sprite Palette starts at 0x0500_0200
|
||||||
if (pal_id != 0) {
|
if (pal_id != 0) {
|
||||||
const bgr555 = self.palette.read(u16, 0x200 + pal_id * 2);
|
const bgr555 = self.palette.read(u16, 0x200 + pal_id * 2);
|
||||||
copyToSpriteBuffer(self.bldcnt, &self.scanline, x, bgr555);
|
copyToSpriteBuffer(self.bld.cnt, &self.scanline, x, bgr555);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -413,7 +433,7 @@ pub const Ppu = struct {
|
||||||
aff_x += self.aff_bg[n - 2].pa;
|
aff_x += self.aff_bg[n - 2].pa;
|
||||||
aff_y += self.aff_bg[n - 2].pc;
|
aff_y += self.aff_bg[n - 2].pc;
|
||||||
|
|
||||||
if (!shouldDrawBackground(n, self.bldcnt, &self.scanline, i)) continue;
|
if (!shouldDrawBackground(n, self.bld.cnt, &self.scanline, i)) continue;
|
||||||
|
|
||||||
if (self.bg[n].cnt.display_overflow.read()) {
|
if (self.bg[n].cnt.display_overflow.read()) {
|
||||||
ix = if (ix > px_width) @rem(ix, px_width) else if (ix < 0) px_width + @rem(ix, px_width) else ix;
|
ix = if (ix > px_width) @rem(ix, px_width) else if (ix < 0) px_width + @rem(ix, px_width) else ix;
|
||||||
|
@ -432,7 +452,7 @@ pub const Ppu = struct {
|
||||||
|
|
||||||
if (pal_id != 0) {
|
if (pal_id != 0) {
|
||||||
const bgr555 = self.palette.read(u16, pal_id * 2);
|
const bgr555 = self.palette.read(u16, pal_id * 2);
|
||||||
copyToBackgroundBuffer(n, self.bldcnt, &self.scanline, i, bgr555);
|
copyToBackgroundBuffer(n, self.bld.cnt, &self.scanline, i, bgr555);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,7 +481,7 @@ pub const Ppu = struct {
|
||||||
|
|
||||||
var i: u32 = 0;
|
var i: u32 = 0;
|
||||||
while (i < width) : (i += 1) {
|
while (i < width) : (i += 1) {
|
||||||
if (!shouldDrawBackground(n, self.bldcnt, &self.scanline, i)) continue;
|
if (!shouldDrawBackground(n, self.bld.cnt, &self.scanline, i)) continue;
|
||||||
|
|
||||||
const x = hofs + i;
|
const x = hofs + i;
|
||||||
|
|
||||||
|
@ -489,7 +509,7 @@ pub const Ppu = struct {
|
||||||
|
|
||||||
if (pal_id != 0) {
|
if (pal_id != 0) {
|
||||||
const bgr555 = self.palette.read(u16, pal_id * 2);
|
const bgr555 = self.palette.read(u16, pal_id * 2);
|
||||||
copyToBackgroundBuffer(n, self.bldcnt, &self.scanline, i, bgr555);
|
copyToBackgroundBuffer(n, self.bld.cnt, &self.scanline, i, bgr555);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -633,11 +653,11 @@ pub const Ppu = struct {
|
||||||
|
|
||||||
fn getBgr555(self: *Self, maybe_top: ?u16, maybe_btm: ?u16) u16 {
|
fn getBgr555(self: *Self, maybe_top: ?u16, maybe_btm: ?u16) u16 {
|
||||||
if (maybe_btm) |btm| {
|
if (maybe_btm) |btm| {
|
||||||
return switch (self.bldcnt.mode.read()) {
|
return switch (self.bld.cnt.mode.read()) {
|
||||||
0b00 => if (maybe_top) |top| top else btm,
|
0b00 => if (maybe_top) |top| top else btm,
|
||||||
0b01 => if (maybe_top) |top| alphaBlend(btm, top, self.bldalpha) else btm,
|
0b01 => if (maybe_top) |top| alphaBlend(btm, top, self.bld.alpha) else btm,
|
||||||
0b10 => blk: {
|
0b10 => blk: {
|
||||||
const evy: u16 = self.bldy.evy.read();
|
const evy: u16 = self.bld.y.evy.read();
|
||||||
|
|
||||||
const r = btm & 0x1F;
|
const r = btm & 0x1F;
|
||||||
const g = (btm >> 5) & 0x1F;
|
const g = (btm >> 5) & 0x1F;
|
||||||
|
@ -650,7 +670,7 @@ pub const Ppu = struct {
|
||||||
break :blk (bld_b << 10) | (bld_g << 5) | bld_r;
|
break :blk (bld_b << 10) | (bld_g << 5) | bld_r;
|
||||||
},
|
},
|
||||||
0b11 => blk: {
|
0b11 => blk: {
|
||||||
const evy: u16 = self.bldy.evy.read();
|
const evy: u16 = self.bld.y.evy.read();
|
||||||
|
|
||||||
const btm_r = btm & 0x1F;
|
const btm_r = btm & 0x1F;
|
||||||
const btm_g = (btm >> 5) & 0x1F;
|
const btm_g = (btm >> 5) & 0x1F;
|
||||||
|
@ -911,25 +931,57 @@ const Oam = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Blend = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
cnt: io.BldCnt,
|
||||||
|
alpha: io.BldAlpha,
|
||||||
|
y: io.BldY,
|
||||||
|
|
||||||
|
pub fn create() Self {
|
||||||
|
return .{
|
||||||
|
.cnt = .{ .raw = 0x000 },
|
||||||
|
.alpha = .{ .raw = 0x000 },
|
||||||
|
.y = .{ .raw = 0x000 },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getCnt(self: *const Self) u16 {
|
||||||
|
return self.cnt.raw & 0x3FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getAlpha(self: *const Self) u16 {
|
||||||
|
return self.alpha.raw & 0x1F1F;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const Window = struct {
|
const Window = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
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 in(self: *const Self) u16 {
|
||||||
|
return self._in.raw & 0x3F3F;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn out(self: *const Self) u16 {
|
||||||
|
return self._out.raw & 0x3F3F;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn setH(self: *Self, value: u32) void {
|
pub fn setH(self: *Self, value: u32) void {
|
||||||
self.h[0].raw = @truncate(u16, value);
|
self.h[0].raw = @truncate(u16, value);
|
||||||
self.h[1].raw = @truncate(u16, value >> 16);
|
self.h[1].raw = @truncate(u16, value >> 16);
|
||||||
|
@ -941,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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -963,6 +1015,17 @@ const Background = struct {
|
||||||
.vofs = .{ .raw = 0x0000 },
|
.vofs = .{ .raw = 0x0000 },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// For whatever reason, some higher bits of BG0CNT
|
||||||
|
/// are masked out
|
||||||
|
pub inline fn bg0Cnt(self: *const Self) u16 {
|
||||||
|
return self.cnt.raw & 0xDFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// BG1CNT inherits the same mask as BG0CNTs
|
||||||
|
pub inline fn bg1Cnt(self: *const Self) u16 {
|
||||||
|
return self.bg0Cnt();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const AffineBackground = struct {
|
const AffineBackground = struct {
|
||||||
|
|
22
src/util.zig
22
src/util.zig
|
@ -12,9 +12,9 @@ pub fn sext(comptime T: type, comptime U: type, value: T) T {
|
||||||
|
|
||||||
const iT = std.meta.Int(.signed, @typeInfo(T).Int.bits);
|
const iT = std.meta.Int(.signed, @typeInfo(T).Int.bits);
|
||||||
const ExtU = if (@typeInfo(U).Int.signedness == .unsigned) T else iT;
|
const ExtU = if (@typeInfo(U).Int.signedness == .unsigned) T else iT;
|
||||||
const shift = @intCast(Log2Int(T), @typeInfo(T).Int.bits - @typeInfo(U).Int.bits);
|
const shift_amt = @intCast(Log2Int(T), @typeInfo(T).Int.bits - @typeInfo(U).Int.bits);
|
||||||
|
|
||||||
return @bitCast(T, @bitCast(iT, @as(ExtU, @truncate(U, value)) << shift) >> shift);
|
return @bitCast(T, @bitCast(iT, @as(ExtU, @truncate(U, value)) << shift_amt) >> shift_amt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See https://godbolt.org/z/W3en9Eche
|
/// See https://godbolt.org/z/W3en9Eche
|
||||||
|
@ -143,7 +143,9 @@ pub const io = struct {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn undef(comptime T: type, log: anytype, comptime format: []const u8, args: anytype) ?T {
|
pub fn undef(comptime T: type, comptime log: anytype, comptime format: []const u8, args: anytype) ?T {
|
||||||
|
@setCold(true);
|
||||||
|
|
||||||
const unhandled_io = config.config().debug.unhandled_io;
|
const unhandled_io = config.config().debug.unhandled_io;
|
||||||
|
|
||||||
log.warn(format, args);
|
log.warn(format, args);
|
||||||
|
@ -151,6 +153,13 @@ pub const io = struct {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn err(comptime T: type, comptime log: anytype, comptime format: []const u8, args: anytype) ?T {
|
||||||
|
@setCold(true);
|
||||||
|
|
||||||
|
log.err(format, args);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const write = struct {
|
pub const write = struct {
|
||||||
|
@ -274,6 +283,13 @@ pub const audio = struct {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Calculates the correct shift offset for an aligned/unaligned u8 read
|
||||||
|
///
|
||||||
|
/// TODO: Rename this
|
||||||
|
pub inline fn shift(byte: u8) u4 {
|
||||||
|
return @truncate(u4, byte & 1) << 3;
|
||||||
|
}
|
||||||
|
|
||||||
/// 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