Compare commits

..

No commits in common. "d859cee365bb9e6bfae281ff4b0f51e60373dc0c" and "10aec67ee0c5a84b6f4e7f430a77f01ecaa5ed16" have entirely different histories.

3 changed files with 106 additions and 149 deletions

View File

@ -2,13 +2,15 @@ const std = @import("std");
const timer = @import("timer.zig"); const timer = @import("timer.zig");
const dma = @import("dma.zig"); const dma = @import("dma.zig");
const apu = @import("../apu.zig"); const apu = @import("../apu.zig");
const ppu = @import("../ppu.zig");
const util = @import("../../util.zig"); const util = @import("../../util.zig");
const Bit = @import("bitfield").Bit; const Bit = @import("bitfield").Bit;
const Bitfield = @import("bitfield").Bitfield; const Bitfield = @import("bitfield").Bitfield;
const Bus = @import("../Bus.zig"); const Bus = @import("../Bus.zig");
const setHi = util.setHi;
const setLo = util.setLo;
const log = std.log.scoped(.@"I/O"); const log = std.log.scoped(.@"I/O");
pub const Io = struct { pub const Io = struct {
@ -43,7 +45,9 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) ?T {
return switch (T) { return switch (T) {
u32 => switch (address) { u32 => switch (address) {
// Display // Display
0x0400_0000...0x0400_0054 => ppu.read(T, &bus.ppu, address), 0x0400_0000 => bus.ppu.dispcnt.raw,
0x0400_0004 => @as(T, bus.ppu.vcount.raw) << 16 | bus.ppu.dispstat.raw,
0x0400_0006 => @as(T, bus.ppu.bg[0].cnt.raw) << 16 | bus.ppu.vcount.raw,
// DMA Transfers // DMA Transfers
0x0400_00B0...0x0400_00DC => dma.read(T, &bus.dma, address), 0x0400_00B0...0x0400_00DC => dma.read(T, &bus.dma, address),
@ -67,7 +71,17 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) ?T {
}, },
u16 => switch (address) { u16 => switch (address) {
// Display // Display
0x0400_0000...0x0400_0054 => ppu.read(T, &bus.ppu, address), 0x0400_0000 => bus.ppu.dispcnt.raw,
0x0400_0004 => bus.ppu.dispstat.raw,
0x0400_0006 => bus.ppu.vcount.raw,
0x0400_0008 => bus.ppu.bg[0].cnt.raw,
0x0400_000A => bus.ppu.bg[1].cnt.raw,
0x0400_000C => bus.ppu.bg[2].cnt.raw,
0x0400_000E => bus.ppu.bg[3].cnt.raw,
0x0400_004C => util.io.read.todo(log, "Read {} from MOSAIC", .{T}),
0x0400_0050 => bus.ppu.bldcnt.raw,
0x0400_0052 => bus.ppu.bldalpha.raw,
0x0400_0054 => bus.ppu.bldy.raw,
// Sound // Sound
0x0400_0060...0x0400_009E => apu.read(T, &bus.apu, address), 0x0400_0060...0x0400_009E => apu.read(T, &bus.apu, address),
@ -96,7 +110,14 @@ 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 => @truncate(T, bus.ppu.dispcnt.raw),
0x0400_0004 => @truncate(T, bus.ppu.dispstat.raw),
0x0400_0005 => @truncate(T, bus.ppu.dispcnt.raw >> 8),
0x0400_0006 => @truncate(T, bus.ppu.vcount.raw),
0x0400_0008 => @truncate(T, bus.ppu.bg[0].cnt.raw),
0x0400_0009 => @truncate(T, bus.ppu.bg[0].cnt.raw >> 8),
0x0400_000A => @truncate(T, bus.ppu.bg[1].cnt.raw),
0x0400_000B => @truncate(T, bus.ppu.bg[1].cnt.raw >> 8),
// Sound // Sound
0x0400_0060...0x0400_00A7 => apu.read(T, &bus.apu, address), 0x0400_0060...0x0400_00A7 => apu.read(T, &bus.apu, address),
@ -123,7 +144,34 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
return switch (T) { return switch (T) {
u32 => switch (address) { u32 => switch (address) {
// Display // Display
0x0400_0000...0x0400_0054 => ppu.write(T, &bus.ppu, address, value), 0x0400_0000 => bus.ppu.dispcnt.raw = @truncate(u16, value),
0x0400_0004 => {
bus.ppu.dispstat.raw = @truncate(u16, value);
bus.ppu.vcount.raw = @truncate(u16, value >> 16);
},
0x0400_0008 => bus.ppu.setAdjCnts(0, value),
0x0400_000C => bus.ppu.setAdjCnts(2, value),
0x0400_0010 => bus.ppu.setBgOffsets(0, value),
0x0400_0014 => bus.ppu.setBgOffsets(1, value),
0x0400_0018 => bus.ppu.setBgOffsets(2, value),
0x0400_001C => bus.ppu.setBgOffsets(3, value),
0x0400_0020 => bus.ppu.aff_bg[0].writePaPb(value),
0x0400_0024 => bus.ppu.aff_bg[0].writePcPd(value),
0x0400_0028 => bus.ppu.aff_bg[0].setX(bus.ppu.dispstat.vblank.read(), value),
0x0400_002C => bus.ppu.aff_bg[0].setY(bus.ppu.dispstat.vblank.read(), value),
0x0400_0030 => bus.ppu.aff_bg[1].writePaPb(value),
0x0400_0034 => bus.ppu.aff_bg[1].writePcPd(value),
0x0400_0038 => bus.ppu.aff_bg[1].setX(bus.ppu.dispstat.vblank.read(), value),
0x0400_003C => bus.ppu.aff_bg[1].setY(bus.ppu.dispstat.vblank.read(), value),
0x0400_0040 => bus.ppu.win.setH(value),
0x0400_0044 => bus.ppu.win.setV(value),
0x0400_0048 => bus.ppu.win.setIo(value),
0x0400_004C => log.debug("Wrote 0x{X:0>8} to MOSAIC", .{value}),
0x0400_0050 => {
bus.ppu.bldcnt.raw = @truncate(u16, value);
bus.ppu.bldalpha.raw = @truncate(u16, value >> 16);
},
0x0400_0054 => bus.ppu.bldy.raw = @truncate(u16, value),
0x0400_0058...0x0400_005C => {}, // Unused 0x0400_0058...0x0400_005C => {}, // Unused
// Sound // Sound
@ -166,8 +214,48 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
}, },
u16 => switch (address) { u16 => switch (address) {
// Display // Display
0x0400_0000...0x0400_0054 => ppu.write(T, &bus.ppu, address, value), 0x0400_0000 => bus.ppu.dispcnt.raw = value,
0x0400_0056 => {}, // Not used 0x0400_0004 => bus.ppu.dispstat.raw = value,
0x0400_0006 => {}, // vcount is read-only
0x0400_0008 => bus.ppu.bg[0].cnt.raw = value,
0x0400_000A => bus.ppu.bg[1].cnt.raw = value,
0x0400_000C => bus.ppu.bg[2].cnt.raw = value,
0x0400_000E => bus.ppu.bg[3].cnt.raw = value,
0x0400_0010 => bus.ppu.bg[0].hofs.raw = value, // TODO: Don't write out every HOFS / VOFS?
0x0400_0012 => bus.ppu.bg[0].vofs.raw = value,
0x0400_0014 => bus.ppu.bg[1].hofs.raw = value,
0x0400_0016 => bus.ppu.bg[1].vofs.raw = value,
0x0400_0018 => bus.ppu.bg[2].hofs.raw = value,
0x0400_001A => bus.ppu.bg[2].vofs.raw = value,
0x0400_001C => bus.ppu.bg[3].hofs.raw = value,
0x0400_001E => bus.ppu.bg[3].vofs.raw = value,
0x0400_0020 => bus.ppu.aff_bg[0].pa = @bitCast(i16, value),
0x0400_0022 => bus.ppu.aff_bg[0].pb = @bitCast(i16, value),
0x0400_0024 => bus.ppu.aff_bg[0].pc = @bitCast(i16, value),
0x0400_0026 => bus.ppu.aff_bg[0].pd = @bitCast(i16, value),
0x0400_0028 => bus.ppu.aff_bg[0].x = @bitCast(i32, setLo(u32, @bitCast(u32, bus.ppu.aff_bg[0].x), value)),
0x0400_002A => bus.ppu.aff_bg[0].x = @bitCast(i32, setHi(u32, @bitCast(u32, bus.ppu.aff_bg[0].x), value)),
0x0400_002C => bus.ppu.aff_bg[0].y = @bitCast(i32, setLo(u32, @bitCast(u32, bus.ppu.aff_bg[0].y), value)),
0x0400_002E => bus.ppu.aff_bg[0].y = @bitCast(i32, setHi(u32, @bitCast(u32, bus.ppu.aff_bg[0].y), value)),
0x0400_0030 => bus.ppu.aff_bg[1].pa = @bitCast(i16, value),
0x0400_0032 => bus.ppu.aff_bg[1].pb = @bitCast(i16, value),
0x0400_0034 => bus.ppu.aff_bg[1].pc = @bitCast(i16, value),
0x0400_0036 => bus.ppu.aff_bg[1].pd = @bitCast(i16, value),
0x0400_0038 => bus.ppu.aff_bg[1].x = @bitCast(i32, setLo(u32, @bitCast(u32, bus.ppu.aff_bg[1].x), value)),
0x0400_003A => bus.ppu.aff_bg[1].x = @bitCast(i32, setHi(u32, @bitCast(u32, bus.ppu.aff_bg[1].x), value)),
0x0400_003C => bus.ppu.aff_bg[1].y = @bitCast(i32, setLo(u32, @bitCast(u32, bus.ppu.aff_bg[1].y), value)),
0x0400_003E => bus.ppu.aff_bg[1].y = @bitCast(i32, setHi(u32, @bitCast(u32, bus.ppu.aff_bg[1].y), value)),
0x0400_0040 => bus.ppu.win.h[0].raw = value,
0x0400_0042 => bus.ppu.win.h[1].raw = value,
0x0400_0044 => bus.ppu.win.v[0].raw = value,
0x0400_0046 => bus.ppu.win.v[1].raw = value,
0x0400_0048 => bus.ppu.win.in.raw = value,
0x0400_004A => bus.ppu.win.out.raw = value,
0x0400_004C => log.debug("Wrote 0x{X:0>4} to MOSAIC", .{value}),
0x0400_0050 => bus.ppu.bldcnt.raw = value,
0x0400_0052 => bus.ppu.bldalpha.raw = value,
0x0400_0054 => bus.ppu.bldy.raw = value,
0x0400_004E, 0x0400_0056 => {}, // Not used
// Sound // Sound
0x0400_0060...0x0400_009E => apu.write(T, &bus.apu, address, value), 0x0400_0060...0x0400_009E => apu.write(T, &bus.apu, address, value),
@ -208,7 +296,16 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
}, },
u8 => switch (address) { u8 => switch (address) {
// Display // Display
0x0400_0000...0x0400_0054 => ppu.write(T, &bus.ppu, address, value), 0x0400_0004 => bus.ppu.dispstat.raw = setLo(u16, bus.ppu.dispstat.raw, value),
0x0400_0005 => bus.ppu.dispstat.raw = setHi(u16, bus.ppu.dispstat.raw, value),
0x0400_0008 => bus.ppu.bg[0].cnt.raw = setLo(u16, bus.ppu.bg[0].cnt.raw, value),
0x0400_0009 => bus.ppu.bg[0].cnt.raw = setHi(u16, bus.ppu.bg[0].cnt.raw, value),
0x0400_000A => bus.ppu.bg[1].cnt.raw = setLo(u16, bus.ppu.bg[1].cnt.raw, value),
0x0400_000B => bus.ppu.bg[1].cnt.raw = setHi(u16, bus.ppu.bg[1].cnt.raw, value),
0x0400_0048 => bus.ppu.win.in.raw = setLo(u16, bus.ppu.win.in.raw, value),
0x0400_0049 => bus.ppu.win.in.raw = setHi(u16, bus.ppu.win.in.raw, value),
0x0400_004A => bus.ppu.win.out.raw = setLo(u16, bus.ppu.win.out.raw, value),
0x0400_0054 => bus.ppu.bldy.raw = setLo(u16, bus.ppu.bldy.raw, 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),

View File

@ -1,6 +1,5 @@
const std = @import("std"); const std = @import("std");
const io = @import("bus/io.zig"); const io = @import("bus/io.zig");
const util = @import("../util.zig");
const Scheduler = @import("scheduler.zig").Scheduler; const Scheduler = @import("scheduler.zig").Scheduler;
const Arm7tdmi = @import("cpu.zig").Arm7tdmi; const Arm7tdmi = @import("cpu.zig").Arm7tdmi;
@ -10,151 +9,12 @@ 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 setLo = util.setLo;
const pollDmaOnBlank = @import("bus/dma.zig").pollDmaOnBlank; const pollDmaOnBlank = @import("bus/dma.zig").pollDmaOnBlank;
pub const width = 240; pub const width = 240;
pub const height = 160; 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 {
const byte = @truncate(u8, addr);
return switch (T) {
u32 => switch (byte) {
0x00 => ppu.dispcnt.raw,
0x04 => @as(T, ppu.vcount.raw) << 16 | ppu.dispstat.raw,
0x06 => @as(T, ppu.bg[0].cnt.raw) << 16 | ppu.vcount.raw,
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }),
},
u16 => switch (byte) {
0x00 => ppu.dispcnt.raw,
0x04 => ppu.dispstat.raw,
0x06 => ppu.vcount.raw,
0x08 => ppu.bg[0].cnt.raw,
0x0A => ppu.bg[1].cnt.raw,
0x0C => ppu.bg[2].cnt.raw,
0x0E => ppu.bg[3].cnt.raw,
0x4C => util.io.read.todo(log, "Read {} from MOSAIC", .{T}),
0x50 => ppu.bldcnt.raw,
0x52 => ppu.bldalpha.raw,
0x54 => ppu.bldy.raw,
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }),
},
u8 => switch (byte) {
0x00 => @truncate(T, ppu.dispcnt.raw),
0x04 => @truncate(T, ppu.dispstat.raw),
0x05 => @truncate(T, ppu.dispcnt.raw >> 8),
0x06 => @truncate(T, ppu.vcount.raw),
0x08 => @truncate(T, ppu.bg[0].cnt.raw),
0x09 => @truncate(T, ppu.bg[0].cnt.raw >> 8),
0x0A => @truncate(T, ppu.bg[1].cnt.raw),
0x0B => @truncate(T, ppu.bg[1].cnt.raw >> 8),
else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }),
},
else => @compileError("PPU: Unsupported read width"),
};
}
pub fn write(comptime T: type, ppu: *Ppu, addr: u32, value: T) void {
const byte = @truncate(u8, addr); // prefixed with 0x0400_00
switch (T) {
u32 => switch (byte) {
0x00 => ppu.dispcnt.raw = @truncate(u16, value),
0x04 => {
ppu.dispstat.raw = @truncate(u16, value);
ppu.vcount.raw = @truncate(u16, value >> 16);
},
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.bldcnt.raw = @truncate(u16, value);
ppu.bldalpha.raw = @truncate(u16, value >> 16);
},
0x54 => ppu.bldy.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) {
0x00 => ppu.dispcnt.raw = value,
0x04 => ppu.dispstat.raw = value,
0x06 => {}, // vcount is read-only
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,
0x16 => ppu.bg[1].vofs.raw = value,
0x18 => ppu.bg[2].hofs.raw = value,
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)),
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)),
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,
0x4C => log.debug("Wrote 0x{X:0>4} to MOSAIC", .{value}),
0x50 => ppu.bldcnt.raw = value,
0x52 => ppu.bldalpha.raw = value,
0x54 => ppu.bldy.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.bldy.raw = setLo(u16, ppu.bldy.raw, 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"),
}
}
pub const Ppu = struct { pub const Ppu = struct {
const Self = @This(); const Self = @This();

View File

@ -126,7 +126,7 @@ fn ensureDirectoriesExist(data_path: []const u8) !void {
// (~/.local/share/zba/save for linux, ??? for macOS) // (~/.local/share/zba/save for linux, ??? for macOS)
// Will recursively create directories // Will recursively create directories
try dir.makePath("zba" ++ std.fs.path.sep_str ++ "save"); try dir.makePath("zba" ++ [_]u8{std.fs.path.sep} ++ "save");
} }
fn romPath(result: *const clap.Result(clap.Help, &params, clap.parsers.default)) []const u8 { fn romPath(result: *const clap.Result(clap.Help, &params, clap.parsers.default)) []const u8 {