diff --git a/src/bus/io.zig b/src/bus/io.zig index b6fef98..4ec3ddd 100644 --- a/src/bus/io.zig +++ b/src/bus/io.zig @@ -182,14 +182,14 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { 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].x = @bitCast(i32, value), - 0x0400_002C => bus.ppu.aff.bg[0].y = @bitCast(i32, 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].x = @bitCast(i32, value), - 0x0400_003C => bus.ppu.aff.bg[1].y = @bitCast(i32, 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].x = @bitCast(i32, value), + 0x0400_002C => bus.ppu.aff_bg[0].y = @bitCast(i32, 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].x = @bitCast(i32, value), + 0x0400_003C => bus.ppu.aff_bg[1].y = @bitCast(i32, value), 0x0400_0040 => log.debug("Wrote 0x{X:0>8} to WIN0H and WIN1H", .{value}), 0x0400_0044 => log.debug("Wrote 0x{X:0>8} to WIN0V and WIN1V", .{value}), 0x0400_0048 => log.debug("Wrote 0x{X:0>8} to WININ and WINOUT", .{value}), @@ -270,22 +270,22 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { 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, @bitCast(u32, bus.ppu.aff.bg[0].x) & 0xFFFF_0000 | value), - 0x0400_002A => bus.ppu.aff.bg[0].x = @bitCast(i32, @bitCast(u32, bus.ppu.aff.bg[0].x) & 0x0000_FFFF | (@as(u32, value) << 16)), - 0x0400_002C => bus.ppu.aff.bg[0].y = @bitCast(i32, @bitCast(u32, bus.ppu.aff.bg[0].y) & 0xFFFF_0000 | value), - 0x0400_002E => bus.ppu.aff.bg[0].y = @bitCast(i32, @bitCast(u32, bus.ppu.aff.bg[0].y) & 0x0000_FFFF | (@as(u32, value) << 16)), - 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, @bitCast(u32, bus.ppu.aff.bg[1].x) & 0xFFFF_0000 | value), - 0x0400_003A => bus.ppu.aff.bg[1].x = @bitCast(i32, @bitCast(u32, bus.ppu.aff.bg[1].x) & 0x0000_FFFF | (@as(u32, value) << 16)), - 0x0400_003C => bus.ppu.aff.bg[1].y = @bitCast(i32, @bitCast(u32, bus.ppu.aff.bg[1].y) & 0xFFFF_0000 | value), - 0x0400_003E => bus.ppu.aff.bg[1].y = @bitCast(i32, @bitCast(u32, bus.ppu.aff.bg[1].y) & 0x0000_FFFF | (@as(u32, value) << 16)), + 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, @bitCast(u32, bus.ppu.aff_bg[0].x) & 0xFFFF_0000 | value), + 0x0400_002A => bus.ppu.aff_bg[0].x = @bitCast(i32, @bitCast(u32, bus.ppu.aff_bg[0].x) & 0x0000_FFFF | (@as(u32, value) << 16)), + 0x0400_002C => bus.ppu.aff_bg[0].y = @bitCast(i32, @bitCast(u32, bus.ppu.aff_bg[0].y) & 0xFFFF_0000 | value), + 0x0400_002E => bus.ppu.aff_bg[0].y = @bitCast(i32, @bitCast(u32, bus.ppu.aff_bg[0].y) & 0x0000_FFFF | (@as(u32, value) << 16)), + 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, @bitCast(u32, bus.ppu.aff_bg[1].x) & 0xFFFF_0000 | value), + 0x0400_003A => bus.ppu.aff_bg[1].x = @bitCast(i32, @bitCast(u32, bus.ppu.aff_bg[1].x) & 0x0000_FFFF | (@as(u32, value) << 16)), + 0x0400_003C => bus.ppu.aff_bg[1].y = @bitCast(i32, @bitCast(u32, bus.ppu.aff_bg[1].y) & 0xFFFF_0000 | value), + 0x0400_003E => bus.ppu.aff_bg[1].y = @bitCast(i32, @bitCast(u32, bus.ppu.aff_bg[1].y) & 0x0000_FFFF | (@as(u32, value) << 16)), 0x0400_0040 => log.debug("Wrote 0x{X:0>4} to WIN0H", .{value}), 0x0400_0042 => log.debug("Wrote 0x{X:0>4} to WIN1H", .{value}), 0x0400_0044 => log.debug("Wrote 0x{X:0>4} to WIN0V", .{value}), diff --git a/src/ppu.zig b/src/ppu.zig index 036354b..7f58420 100644 --- a/src/ppu.zig +++ b/src/ppu.zig @@ -25,7 +25,7 @@ pub const Ppu = struct { // Registers bg: [4]Background, - aff: AffineBackground, + aff_bg: [2]AffineBackground, dispcnt: io.DisplayControl, dispstat: io.DisplayStatus, @@ -58,7 +58,7 @@ pub const Ppu = struct { // Registers .bg = [_]Background{Background.init()} ** 4, - .aff = AffineBackground.init(), + .aff_bg = [_]AffineBackground{AffineBackground.init()} ** 2, .dispcnt = .{ .raw = 0x0000 }, .dispstat = .{ .raw = 0x0000 }, .vcount = .{ .raw = 0x0000 }, @@ -212,16 +212,16 @@ pub const Ppu = struct { const px_width = tile_width << 3; const px_height = px_width; - var aff_x = self.aff.bg[n - 2].x_latch.?; - var aff_y = self.aff.bg[n - 2].y_latch.?; + var aff_x = self.aff_bg[n - 2].x_latch.?; + var aff_y = self.aff_bg[n - 2].y_latch.?; var i: u32 = 0; while (i < width) : (i += 1) { var ix = aff_x >> 8; var iy = aff_y >> 8; - aff_x += self.aff.bg[n - 2].pa; - aff_y += self.aff.bg[n - 2].pc; + aff_x += self.aff_bg[n - 2].pa; + aff_y += self.aff_bg[n - 2].pc; if (self.scanline_buf[@as(usize, i)] != null) continue; @@ -244,17 +244,15 @@ pub const Ppu = struct { } // Update BGxX and BGxY - self.aff.bg[n - 2].x_latch.? += self.aff.bg[n - 2].pb; // PB is added to BGxX - self.aff.bg[n - 2].y_latch.? += self.aff.bg[n - 2].pd; // PD is added to BGxY + self.aff_bg[n - 2].x_latch.? += self.aff_bg[n - 2].pb; // PB is added to BGxX + self.aff_bg[n - 2].y_latch.? += self.aff_bg[n - 2].pd; // PD is added to BGxY } fn drawBackround(self: *Self, comptime n: u3) void { // A Tile in a charblock is a byte, while a Screen Entry is a halfword - const charblock_len: u32 = 0x4000; - const screenblock_len: u32 = 0x800; - const cbb: u2 = self.bg[n].cnt.char_base.read(); // Char Block Base - const sbb: u5 = self.bg[n].cnt.screen_base.read(); // Screen Block Base + const char_base = 0x4000 * @as(u32, self.bg[n].cnt.char_base.read()); + const screen_base = 0x800 * @as(u32, self.bg[n].cnt.screen_base.read()); const is_8bpp: bool = self.bg[n].cnt.colour_mode.read(); // Colour Mode const size: u2 = self.bg[n].cnt.size.read(); // Background Size @@ -263,10 +261,6 @@ pub const Ppu = struct { const tile_len = if (is_8bpp) @as(u32, 0x40) else 0x20; const tile_row_offset = if (is_8bpp) @as(u32, 0x8) else 0x4; - // 0x0600_000 is implied because we can access VRAM without the Bus - const char_base: u32 = charblock_len * @as(u32, cbb); - const screen_base: u32 = screenblock_len * @as(u32, sbb); - const vofs: u32 = self.bg[n].vofs.offset.read(); const hofs: u32 = self.bg[n].hofs.offset.read(); @@ -286,14 +280,15 @@ pub const Ppu = struct { // Calculate the Address of the Tile in the designated Charblock // We also take this opportunity to flip tiles if necessary const tile_id: u32 = entry.tile_id.read(); - const row = if (entry.v_flip.read()) 7 - (y % 8) else y % 8; // Determine on which row in a tile we're on - const tile_addr = char_base + (tile_len * tile_id) + (tile_row_offset * row); - // Calculate on which column in a tile we're on - // Similarly to when we calculated the row, if we're in 4bpp we want to account - // for 1 byte consisting of two pixels + // Calculate row and column offsets. Understand that + // `tile_len`, `tile_row_offset` and `col` are subject to different + // values depending on whether we are in 4bpp or 8bpp mode. + const row = @truncate(u3, y) ^ if (entry.v_flip.read()) 7 else @as(u3, 0); const col = @truncate(u3, x) ^ if (entry.h_flip.read()) 7 else @as(u3, 0); - const tile = self.vram.buf[tile_addr + if (is_8bpp) col else col >> 1]; + const tile_addr = char_base + (tile_id * tile_len) + (row * tile_row_offset) + if (is_8bpp) col else col >> 1; + + const tile = self.vram.buf[tile_addr]; // If we're in 8bpp, then the tile value is an index into the palette, // If we're in 4bpp, we have to account for a pal bank value in the Screen entry @@ -432,6 +427,7 @@ pub const Ppu = struct { } } + // TODO: Comment this + get a better understanding fn tilemapOffset(size: u2, x: u32, y: u32) u32 { // Current Row: (y % PIXEL_COUNT) / 8 // Current COlumn: (x % PIXEL_COUNT) / 8 @@ -506,8 +502,8 @@ pub const Ppu = struct { cpu.handleInterrupt(); } - self.aff.bg[0].latchRefPoints(); - self.aff.bg[1].latchRefPoints(); + self.aff_bg[0].latchRefPoints(); + self.aff_bg[1].latchRefPoints(); // See if Vblank DMA is present and not enabled pollBlankingDma(&cpu.bus, .VBlank); @@ -695,18 +691,6 @@ const Background = struct { const AffineBackground = struct { const Self = @This(); - bg: [2]AffineBackgroundRegisters, - - fn init() Self { - return .{ - .bg = [_]AffineBackgroundRegisters{AffineBackgroundRegisters.init()} ** 2, - }; - } -}; - -const AffineBackgroundRegisters = struct { - const Self = @This(); - x: i32, y: i32,