feat(ppu): implement all i/o writes

This commit is contained in:
Rekai Nyangadzayi Musuka 2022-10-30 02:36:46 -03:00
parent c9a423d094
commit 472215b4c2
5 changed files with 81 additions and 33 deletions

View File

@ -12,7 +12,7 @@ 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.shift; const getHalf = util.getHalf;
const setHalf = util.setHalf; const setHalf = util.setHalf;
const intToBytes = util.intToBytes; const intToBytes = util.intToBytes;

View File

@ -8,7 +8,7 @@ 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.shift; const getHalf = util.getHalf;
const setHalf = util.setHalf; const setHalf = util.setHalf;
const setQuart = util.setQuart; const setQuart = util.setQuart;

View File

@ -8,7 +8,7 @@ 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.shift; const getHalf = util.getHalf;
const setHalf = util.setHalf; const setHalf = util.setHalf;
pub fn create(sched: *Scheduler) TimerTuple { pub fn create(sched: *Scheduler) TimerTuple {

View File

@ -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 setHi = util.setHi; const getHalf = util.getHalf;
const setLo = util.setLo; const setHalf = util.setHalf;
const getHalf = util.shift; const setQuart = util.setQuart;
const pollDmaOnBlank = @import("bus/dma.zig").pollDmaOnBlank; const pollDmaOnBlank = @import("bus/dma.zig").pollDmaOnBlank;
pub const width = 240; pub const width = 240;
@ -88,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 { 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) { switch (T) {
u32 => switch (byte) { u32 => switch (byte_addr) {
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,38 +99,45 @@ 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) { u16 => switch (byte_addr) {
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 is read-only 0x06 => {}, // VCOUNT
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,
@ -139,22 +146,21 @@ 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 => ppu.aff_bg[0].x = @bitCast(i32, setLo(u32, @bitCast(u32, ppu.aff_bg[0].x), value)), 0x28, 0x2A => ppu.aff_bg[0].x = @bitCast(i32, setHalf(u32, @bitCast(u32, ppu.aff_bg[0].x), byte_addr, value)),
0x2A => ppu.aff_bg[0].x = @bitCast(i32, setHi(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)),
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 => ppu.aff_bg[1].x = @bitCast(i32, setLo(u32, @bitCast(u32, ppu.aff_bg[1].x), value)), 0x38, 0x3A => ppu.aff_bg[1].x = @bitCast(i32, setHalf(u32, @bitCast(u32, ppu.aff_bg[1].x), byte_addr, value)),
0x3A => ppu.aff_bg[1].x = @bitCast(i32, setHi(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)),
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,
@ -162,22 +168,65 @@ pub fn write(comptime T: type, ppu: *Ppu, addr: u32, value: T) void {
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) { u8 => switch (byte_addr) {
0x04 => ppu.dispstat.raw = setLo(u16, ppu.dispstat.raw, value), 0x00, 0x01 => ppu.dispcnt.raw = setHalf(u16, ppu.dispcnt.raw, byte_addr, value),
0x05 => ppu.dispstat.raw = setHi(u16, ppu.dispstat.raw, value), 0x02, 0x03 => {}, // Green Swap
0x08 => ppu.bg[0].cnt.raw = setLo(u16, ppu.bg[0].cnt.raw, value), 0x04, 0x05 => ppu.dispstat.raw = setHalf(u16, ppu.dispstat.raw, byte_addr, value),
0x09 => ppu.bg[0].cnt.raw = setHi(u16, ppu.bg[0].cnt.raw, value), 0x06, 0x07 => {}, // VCOUNT
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), // BGXCNT
0x48 => ppu.win.in.raw = setLo(u16, ppu.win.in.raw, value), 0x08, 0x09 => ppu.bg[0].cnt.raw = setHalf(u16, ppu.bg[0].cnt.raw, byte_addr, value),
0x49 => ppu.win.in.raw = setHi(u16, ppu.win.in.raw, value), 0x0A, 0x0B => ppu.bg[1].cnt.raw = setHalf(u16, ppu.bg[1].cnt.raw, byte_addr, value),
0x4A => ppu.win.out.raw = setLo(u16, ppu.win.out.raw, value), 0x0C, 0x0D => ppu.bg[2].cnt.raw = setHalf(u16, ppu.bg[2].cnt.raw, byte_addr, value),
0x54 => ppu.bld.y.raw = setLo(u16, ppu.bld.y.raw, 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 => 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"),

View File

@ -297,9 +297,8 @@ pub inline fn setQuart(left: u32, addr: u8, right: u8) u32 {
/// Calculates the correct shift offset for an aligned/unaligned u8 read /// Calculates the correct shift offset for an aligned/unaligned u8 read
/// ///
/// TODO: Rename this /// TODO: Support u16 reads of u32 values?
/// TODO: Support u16 reads of u32 values 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;
} }