Compare commits
2 Commits
338122ed43
...
d2d4667f7b
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | d2d4667f7b | |
Rekai Nyangadzayi Musuka | 5835b509e4 |
|
@ -93,13 +93,13 @@ const KeyInput = extern union {
|
|||
|
||||
// Read / Write
|
||||
pub const BackgroundControl = extern union {
|
||||
bg_priority: Bitfield(u16, 0, 2),
|
||||
priority: Bitfield(u16, 0, 2),
|
||||
char_base: Bitfield(u16, 2, 2),
|
||||
mosaic_enable: Bit(u16, 6),
|
||||
palette_type: Bit(u16, 7),
|
||||
colour_mode: Bit(u16, 7),
|
||||
screen_base: Bitfield(u16, 8, 5),
|
||||
display_overflow: Bit(u16, 13),
|
||||
screen_size: Bitfield(u16, 14, 2),
|
||||
size: Bitfield(u16, 14, 2),
|
||||
raw: u16,
|
||||
};
|
||||
|
||||
|
|
83
src/ppu.zig
83
src/ppu.zig
|
@ -4,6 +4,9 @@ const io = @import("bus/io.zig");
|
|||
const EventKind = @import("scheduler.zig").EventKind;
|
||||
const Scheduler = @import("scheduler.zig").Scheduler;
|
||||
|
||||
const Bit = @import("bitfield").Bit;
|
||||
const Bitfield = @import("bitfield").Bitfield;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const width = 240;
|
||||
|
@ -68,8 +71,65 @@ pub const Ppu = struct {
|
|||
|
||||
switch (bg_mode) {
|
||||
0x0 => {
|
||||
// Mode 0
|
||||
// TODO: Consider more than BG0
|
||||
// TODO: Consider Scrolling
|
||||
|
||||
// The Current Scanline which will be copied into
|
||||
// the Framebuffer
|
||||
const start = framebuf_pitch * @as(usize, scanline);
|
||||
var scanline_buf = std.mem.zeroes([framebuf_pitch]u8);
|
||||
|
||||
// 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.bg0.cnt.char_base.read(); // Char Block Base
|
||||
const sbb: u5 = self.bg0.cnt.screen_base.read(); // Screen Block Base
|
||||
const is_8bpp: bool = self.bg0.cnt.colour_mode.read(); // Colour Mode
|
||||
const size: u2 = self.bg0.cnt.size.read(); // Background Size
|
||||
|
||||
// In 4bpp: 1 byte represents two pixels so the length is (8 x 8) / 2
|
||||
// In 8bpp: 1 byte represents one pixel so the length is 8 x 8
|
||||
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 y = @as(u32, scanline);
|
||||
|
||||
var x: u32 = 0;
|
||||
while (x < width) : (x += 1) {
|
||||
// Grab the Screen Entry from VRAM
|
||||
const entry_addr = screen_base + tilemapOffset(size, x, y);
|
||||
const entry = @bitCast(ScreenEntry, @as(u16, self.vram.buf[entry_addr + 1]) << 8 | @as(u16, self.vram.buf[entry_addr]));
|
||||
|
||||
// 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.h_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
|
||||
const col = if (entry.v_flip.read()) 7 - (x % 8) else x % 8;
|
||||
var tile = self.vram.buf[tile_addr + if (is_8bpp) col else col / 2];
|
||||
|
||||
// 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
|
||||
// and then we can index the palette
|
||||
const colour = if (!is_8bpp) blk: {
|
||||
tile = if (col & 1 == 1) tile >> 4 else tile & 0xF;
|
||||
const pal_bank: u8 = @as(u8, entry.palette_bank.read()) << 4;
|
||||
break :blk pal_bank | tile;
|
||||
} else tile;
|
||||
|
||||
std.mem.copy(u8, scanline_buf[x * 2 ..][0..2], self.palette.buf[colour * 2 ..][0..2]);
|
||||
}
|
||||
|
||||
std.mem.copy(u8, self.framebuf[start..][0..framebuf_pitch], &scanline_buf);
|
||||
},
|
||||
0x3 => {
|
||||
const start = framebuf_pitch * @as(usize, scanline);
|
||||
|
@ -94,6 +154,19 @@ pub const Ppu = struct {
|
|||
else => std.debug.panic("[PPU] TODO: Implement BG Mode {}", .{bg_mode}),
|
||||
}
|
||||
}
|
||||
|
||||
fn tilemapOffset(size: u2, x: u32, y: u32) u32 {
|
||||
// Current Row: (y % PIXEL_COUNT) / 8
|
||||
// Current COlumn: (x % PIXEL_COUNT) / 8
|
||||
// Length of 1 row of Screen Entries: 0x40
|
||||
// Length of 1 Screen Entry: 0x2 is the size of a screen entry
|
||||
return switch (size) {
|
||||
0 => (y % 256 / 8) * 0x40 + (x % 256 / 8) * 2, // 256 x 256
|
||||
1 => (y % 512 / 8) * 0x40 + (x % 256 / 8) * 2, // 512 x 256
|
||||
2 => (y % 256 / 8) * 0x40 + (x % 512 / 8) * 2, // 256 x 512
|
||||
3 => (y % 512 / 8) * 0x40 + (x % 512 / 8) * 2, // 512 x 512
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const Palette = struct {
|
||||
|
@ -241,3 +314,11 @@ const Background = struct {
|
|||
};
|
||||
}
|
||||
};
|
||||
|
||||
const ScreenEntry = extern union {
|
||||
tile_id: Bitfield(u16, 0, 10),
|
||||
h_flip: Bit(u16, 10),
|
||||
v_flip: Bit(u16, 11),
|
||||
palette_bank: Bitfield(u16, 12, 4),
|
||||
raw: u16,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue