Compare commits
5 Commits
68012f84d3
...
5df023fb41
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | 5df023fb41 | |
Rekai Nyangadzayi Musuka | 46ac1542a6 | |
Rekai Nyangadzayi Musuka | c2f55e0bfb | |
Rekai Nyangadzayi Musuka | 12f9bb51c1 | |
Rekai Nyangadzayi Musuka | 41558c9103 |
|
@ -63,6 +63,9 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
|
||||||
// Keypad Input
|
// Keypad Input
|
||||||
0x0400_0130 => unimplementedRead("Read {} from KEYINPUT", .{T}),
|
0x0400_0130 => unimplementedRead("Read {} from KEYINPUT", .{T}),
|
||||||
|
|
||||||
|
// Serial Communication 2
|
||||||
|
0x0400_0150 => unimplementedRead("Read {} from JOY_RECV", .{T}),
|
||||||
|
|
||||||
// Interrupts
|
// Interrupts
|
||||||
0x0400_0200 => @as(T, bus.io.irq.raw) << 16 | bus.io.ie.raw,
|
0x0400_0200 => @as(T, bus.io.irq.raw) << 16 | bus.io.ie.raw,
|
||||||
0x0400_0208 => @boolToInt(bus.io.ime),
|
0x0400_0208 => @boolToInt(bus.io.ime),
|
||||||
|
@ -77,6 +80,7 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
|
||||||
0x0400_000A => bus.ppu.bg[1].cnt.raw,
|
0x0400_000A => bus.ppu.bg[1].cnt.raw,
|
||||||
0x0400_000C => bus.ppu.bg[2].cnt.raw,
|
0x0400_000C => bus.ppu.bg[2].cnt.raw,
|
||||||
0x0400_000E => bus.ppu.bg[3].cnt.raw,
|
0x0400_000E => bus.ppu.bg[3].cnt.raw,
|
||||||
|
0x0400_004C => unimplementedRead("Read {} from MOSAIC", .{T}),
|
||||||
|
|
||||||
// Sound
|
// Sound
|
||||||
0x0400_0088 => bus.apu.bias.raw,
|
0x0400_0088 => bus.apu.bias.raw,
|
||||||
|
@ -117,7 +121,12 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
|
||||||
// Display
|
// Display
|
||||||
0x0400_0000 => @truncate(T, bus.ppu.dispcnt.raw),
|
0x0400_0000 => @truncate(T, bus.ppu.dispcnt.raw),
|
||||||
0x0400_0004 => @truncate(T, bus.ppu.dispstat.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_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 => bus.apu.ch1.sweep.raw,
|
0x0400_0060 => bus.apu.ch1.sweep.raw,
|
||||||
|
@ -133,6 +142,12 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
|
||||||
// Serial Communication 1
|
// Serial Communication 1
|
||||||
0x0400_0128 => unimplementedRead("Read {} from SIOCNT_L", .{T}),
|
0x0400_0128 => unimplementedRead("Read {} from SIOCNT_L", .{T}),
|
||||||
|
|
||||||
|
// Keypad Input
|
||||||
|
0x0400_0130 => unimplementedRead("read {} from KEYINPUT_L", .{T}),
|
||||||
|
|
||||||
|
// Serial Communication 2
|
||||||
|
0x0400_0135 => unimplementedRead("Read {} from RCNT_H", .{T}),
|
||||||
|
|
||||||
// Interrupts
|
// Interrupts
|
||||||
0x0400_0200 => @truncate(T, bus.io.ie.raw),
|
0x0400_0200 => @truncate(T, bus.io.ie.raw),
|
||||||
0x0400_0300 => @enumToInt(bus.io.postflg),
|
0x0400_0300 => @enumToInt(bus.io.postflg),
|
||||||
|
@ -365,6 +380,10 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
// Display
|
// Display
|
||||||
0x0400_0004 => bus.ppu.dispstat.raw = (bus.ppu.dispstat.raw & 0xFF00) | value,
|
0x0400_0004 => bus.ppu.dispstat.raw = (bus.ppu.dispstat.raw & 0xFF00) | value,
|
||||||
0x0400_0005 => bus.ppu.dispstat.raw = (@as(u16, value) << 8) | (bus.ppu.dispstat.raw & 0xFF),
|
0x0400_0005 => bus.ppu.dispstat.raw = (@as(u16, value) << 8) | (bus.ppu.dispstat.raw & 0xFF),
|
||||||
|
0x0400_0008 => bus.ppu.bg[0].cnt.raw = (bus.ppu.bg[0].cnt.raw & 0xFF00) | value,
|
||||||
|
0x0400_0009 => bus.ppu.bg[0].cnt.raw = (@as(u16, value) << 8) | (bus.ppu.bg[0].cnt.raw & 0xFF),
|
||||||
|
0x0400_000A => bus.ppu.bg[1].cnt.raw = (bus.ppu.bg[1].cnt.raw & 0xFF00) | value,
|
||||||
|
0x0400_000B => bus.ppu.bg[1].cnt.raw = (@as(u16, value) << 8) | (bus.ppu.bg[1].cnt.raw & 0xFF),
|
||||||
0x0400_0054 => log.debug("Wrote 0x{X:0>2} to BLDY_L", .{value}),
|
0x0400_0054 => log.debug("Wrote 0x{X:0>2} to BLDY_L", .{value}),
|
||||||
|
|
||||||
// Sound
|
// Sound
|
||||||
|
@ -401,10 +420,13 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
0x0400_0128 => log.debug("Wrote 0x{X:0>2} to SIOCNT_L", .{value}),
|
0x0400_0128 => log.debug("Wrote 0x{X:0>2} to SIOCNT_L", .{value}),
|
||||||
|
|
||||||
// Serial Communication 2
|
// Serial Communication 2
|
||||||
|
0x0400_0135 => log.debug("Wrote 0x{X:0>2} to RCNT_H", .{value}),
|
||||||
0x0400_0140 => log.debug("Wrote 0x{X:0>2} to JOYCNT_L", .{value}),
|
0x0400_0140 => log.debug("Wrote 0x{X:0>2} to JOYCNT_L", .{value}),
|
||||||
|
|
||||||
// Interrupts
|
// Interrupts
|
||||||
|
0x0400_0202 => bus.io.irq.raw &= ~@as(u16, value),
|
||||||
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
||||||
|
0x0400_0300 => bus.io.postflg = std.meta.intToEnum(PostFlag, value & 1) catch unreachable,
|
||||||
0x0400_0301 => bus.io.haltcnt = if (value >> 7 & 1 == 0) .Halt else std.debug.panic("TODO: Implement STOP", .{}),
|
0x0400_0301 => bus.io.haltcnt = if (value >> 7 & 1 == 0) .Halt else std.debug.panic("TODO: Implement STOP", .{}),
|
||||||
|
|
||||||
0x0400_0410 => log.debug("Wrote 0x{X:0>2} to the common yet undocumented 0x{X:0>8}", .{ value, address }),
|
0x0400_0410 => log.debug("Wrote 0x{X:0>2} to the common yet undocumented 0x{X:0>8}", .{ value, address }),
|
||||||
|
|
|
@ -25,7 +25,7 @@ pub fn format78(comptime op: u2, comptime T: bool) InstrFn {
|
||||||
const rb = opcode >> 3 & 0x7;
|
const rb = opcode >> 3 & 0x7;
|
||||||
const rd = opcode & 0x7;
|
const rd = opcode & 0x7;
|
||||||
|
|
||||||
const address = cpu.r[rb] + cpu.r[ro];
|
const address = cpu.r[rb] +% cpu.r[ro];
|
||||||
|
|
||||||
if (T) {
|
if (T) {
|
||||||
// Format 8
|
// Format 8
|
||||||
|
|
138
src/ppu.zig
138
src/ppu.zig
|
@ -135,6 +135,7 @@ pub const Ppu = struct {
|
||||||
if (maybe_sprites) |sprite| {
|
if (maybe_sprites) |sprite| {
|
||||||
// Move on to the next sprite If its of a different priority
|
// Move on to the next sprite If its of a different priority
|
||||||
if (sprite.priority() != prio) continue :sprite_loop;
|
if (sprite.priority() != prio) continue :sprite_loop;
|
||||||
|
if (sprite.attr0.is_affine.read()) continue :sprite_loop; // TODO: Affine Sprites
|
||||||
|
|
||||||
var i: u9 = 0;
|
var i: u9 = 0;
|
||||||
px_loop: while (i < sprite.width) : (i += 1) {
|
px_loop: while (i < sprite.width) : (i += 1) {
|
||||||
|
@ -281,31 +282,76 @@ pub const Ppu = struct {
|
||||||
const fb_base = framebuf_pitch * @as(usize, scanline);
|
const fb_base = framebuf_pitch * @as(usize, scanline);
|
||||||
if (obj_enable) self.fetchSprites();
|
if (obj_enable) self.fetchSprites();
|
||||||
|
|
||||||
var i: usize = 0;
|
var layer: usize = 0;
|
||||||
while (i < 4) : (i += 1) {
|
while (layer < 4) : (layer += 1) {
|
||||||
// Draw Sprites Here
|
self.drawSprites(@truncate(u2, layer));
|
||||||
self.drawSprites(@truncate(u2, i));
|
if (layer == self.bg[0].cnt.priority.read() and bg_enable & 1 == 1) self.drawBackround(0);
|
||||||
if (i == self.bg[0].cnt.priority.read() and bg_enable & 1 == 1) self.drawBackround(0);
|
if (layer == self.bg[1].cnt.priority.read() and bg_enable >> 1 & 1 == 1) self.drawBackround(1);
|
||||||
if (i == self.bg[1].cnt.priority.read() and bg_enable >> 1 & 1 == 1) self.drawBackround(1);
|
if (layer == self.bg[2].cnt.priority.read() and bg_enable >> 2 & 1 == 1) self.drawBackround(2);
|
||||||
if (i == self.bg[2].cnt.priority.read() and bg_enable >> 2 & 1 == 1) self.drawBackround(2);
|
if (layer == self.bg[3].cnt.priority.read() and bg_enable >> 3 & 1 == 1) self.drawBackround(3);
|
||||||
if (i == self.bg[3].cnt.priority.read() and bg_enable >> 3 & 1 == 1) self.drawBackround(3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy Drawn Scanline to Frame Buffer
|
// Copy Drawn Scanline to Frame Buffer
|
||||||
// If there are any nulls present in self.scanline_buf it means that no background drew a pixel there, so draw backdrop
|
// If there are any nulls present in self.scanline_buf it means that no background drew a pixel there, so draw backdrop
|
||||||
for (self.scanline_buf) |maybe_px, j| {
|
for (self.scanline_buf) |maybe_px, i| {
|
||||||
const bgr555 = if (maybe_px) |px| px else self.palette.getBackdrop();
|
const bgr555 = if (maybe_px) |px| px else self.palette.getBackdrop();
|
||||||
std.mem.copy(u8, self.framebuf[fb_base + j * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
std.mem.copy(u8, self.framebuf[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset Scanline Buffer
|
// Reset Current Scanline Pixel Buffer and list of fetched sprites
|
||||||
|
// in prep for next scanline
|
||||||
|
std.mem.set(?u16, &self.scanline_buf, null);
|
||||||
|
std.mem.set(?Sprite, &self.scanline_sprites, null);
|
||||||
|
},
|
||||||
|
0x1 => {
|
||||||
|
const fb_base = framebuf_pitch * @as(usize, scanline);
|
||||||
|
if (obj_enable) self.fetchSprites();
|
||||||
|
|
||||||
|
var layer: usize = 0;
|
||||||
|
while (layer < 4) : (layer += 1) {
|
||||||
|
self.drawSprites(@truncate(u2, layer));
|
||||||
|
if (layer == self.bg[0].cnt.priority.read() and bg_enable & 1 == 1) self.drawBackround(0);
|
||||||
|
if (layer == self.bg[1].cnt.priority.read() and bg_enable >> 1 & 1 == 1) self.drawBackround(1);
|
||||||
|
// TODO: Implement Affine BG2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy Drawn Scanline to Frame Buffer
|
||||||
|
// If there are any nulls present in self.scanline_buf it means that no background drew a pixel there, so draw backdrop
|
||||||
|
for (self.scanline_buf) |maybe_px, i| {
|
||||||
|
const bgr555 = if (maybe_px) |px| px else self.palette.getBackdrop();
|
||||||
|
std.mem.copy(u8, self.framebuf[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset Current Scanline Pixel Buffer and list of fetched sprites
|
||||||
|
// in prep for next scanline
|
||||||
|
std.mem.set(?u16, &self.scanline_buf, null);
|
||||||
|
std.mem.set(?Sprite, &self.scanline_sprites, null);
|
||||||
|
},
|
||||||
|
0x2 => {
|
||||||
|
const fb_base = framebuf_pitch * @as(usize, scanline);
|
||||||
|
if (obj_enable) self.fetchSprites();
|
||||||
|
|
||||||
|
var layer: usize = 0;
|
||||||
|
while (layer < 4) : (layer += 1) {
|
||||||
|
self.drawSprites(@truncate(u2, layer));
|
||||||
|
// TODO: Implement Affine BG2, BG3
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy Drawn Scanline to Frame Buffer
|
||||||
|
// If there are any nulls present in self.scanline_buf it means that no background drew a pixel there, so draw backdrop
|
||||||
|
for (self.scanline_buf) |maybe_px, i| {
|
||||||
|
const bgr555 = if (maybe_px) |px| px else self.palette.getBackdrop();
|
||||||
|
std.mem.copy(u8, self.framebuf[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset Current Scanline Pixel Buffer and list of fetched sprites
|
||||||
|
// in prep for next scanline
|
||||||
std.mem.set(?u16, &self.scanline_buf, null);
|
std.mem.set(?u16, &self.scanline_buf, null);
|
||||||
// Reset List of Sprites
|
|
||||||
std.mem.set(?Sprite, &self.scanline_sprites, null);
|
std.mem.set(?Sprite, &self.scanline_sprites, null);
|
||||||
},
|
},
|
||||||
0x3 => {
|
0x3 => {
|
||||||
const fb_base = framebuf_pitch * @as(usize, scanline);
|
|
||||||
const vram_base = width * @sizeOf(u16) * @as(usize, scanline);
|
const vram_base = width * @sizeOf(u16) * @as(usize, scanline);
|
||||||
|
const fb_base = framebuf_pitch * @as(usize, scanline);
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < width) : (i += 1) {
|
while (i < width) : (i += 1) {
|
||||||
|
@ -314,14 +360,29 @@ pub const Ppu = struct {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
0x4 => {
|
0x4 => {
|
||||||
const select = self.dispcnt.frame_select.read();
|
const sel = self.dispcnt.frame_select.read();
|
||||||
|
const vram_base = width * @as(usize, scanline) + if (sel) 0xA000 else @as(usize, 0);
|
||||||
const fb_base = framebuf_pitch * @as(usize, scanline);
|
const fb_base = framebuf_pitch * @as(usize, scanline);
|
||||||
const vram_base = width * @as(usize, scanline) + if (select) 0xA000 else @as(usize, 0);
|
|
||||||
|
|
||||||
// Render Current Scanline
|
// Render Current Scanline
|
||||||
for (self.vram.buf[vram_base .. vram_base + width]) |byte, i| {
|
for (self.vram.buf[vram_base .. vram_base + width]) |byte, i| {
|
||||||
const pal_id = @as(u16, byte) * @sizeOf(u16);
|
const bgr555 = self.palette.read(u16, @as(u16, byte) * @sizeOf(u16));
|
||||||
const bgr555 = self.palette.read(u16, pal_id);
|
std.mem.copy(u8, self.framebuf[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
0x5 => {
|
||||||
|
const m5_width = 160;
|
||||||
|
const m5_height = 128;
|
||||||
|
|
||||||
|
const sel = self.dispcnt.frame_select.read();
|
||||||
|
const vram_base = m5_width * @sizeOf(u16) * @as(usize, scanline) + if (sel) 0xA000 else @as(usize, 0);
|
||||||
|
const fb_base = framebuf_pitch * @as(usize, scanline);
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < width) : (i += 1) {
|
||||||
|
// If we're outside of the bounds of mode 5, draw the background colour
|
||||||
|
const bgr555 =
|
||||||
|
if (scanline < m5_height and i < m5_width) self.vram.read(u16, vram_base + i * @sizeOf(u16)) else self.palette.getBackdrop();
|
||||||
|
|
||||||
std.mem.copy(u8, self.framebuf[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
std.mem.copy(u8, self.framebuf[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
||||||
}
|
}
|
||||||
|
@ -686,9 +747,32 @@ const Sprite = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AffineSprite = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
attr0: AffineAttr0,
|
||||||
|
attr1: AffineAttr1,
|
||||||
|
attr2: Attr2,
|
||||||
|
|
||||||
|
width: u8,
|
||||||
|
height: u8,
|
||||||
|
|
||||||
|
fn init(attr0: AffineAttr0, attr1: AffineAttr1, attr2: Attr2) Self {
|
||||||
|
const d = spriteDimensions(attr0.shape.read(), attr1.size.read());
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.attr0 = attr0,
|
||||||
|
.attr1 = attr1,
|
||||||
|
.attr2 = attr2,
|
||||||
|
.width = d[0],
|
||||||
|
.height = d[1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const Attr0 = extern union {
|
const Attr0 = extern union {
|
||||||
y: Bitfield(u16, 0, 8),
|
y: Bitfield(u16, 0, 8),
|
||||||
rot_scaling: Bit(u16, 8), // This SBZ
|
is_affine: Bit(u16, 8), // This SBZ
|
||||||
disabled: Bit(u16, 9),
|
disabled: Bit(u16, 9),
|
||||||
mode: Bitfield(u16, 10, 2),
|
mode: Bitfield(u16, 10, 2),
|
||||||
mosaic: Bit(u16, 12),
|
mosaic: Bit(u16, 12),
|
||||||
|
@ -697,6 +781,17 @@ const Attr0 = extern union {
|
||||||
raw: u16,
|
raw: u16,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AffineAttr0 = extern union {
|
||||||
|
y: Bitfield(u16, 0, 8),
|
||||||
|
rot_scaling: Bit(u16, 8), // This SB1
|
||||||
|
double_size: Bit(u16, 9),
|
||||||
|
mode: Bitfield(u16, 10, 2),
|
||||||
|
mosaic: Bit(u16, 12),
|
||||||
|
is_8bpp: Bit(u16, 13),
|
||||||
|
shape: Bitfield(u16, 14, 2),
|
||||||
|
raw: u16,
|
||||||
|
};
|
||||||
|
|
||||||
const Attr1 = extern union {
|
const Attr1 = extern union {
|
||||||
x: Bitfield(u16, 0, 9),
|
x: Bitfield(u16, 0, 9),
|
||||||
h_flip: Bit(u16, 12),
|
h_flip: Bit(u16, 12),
|
||||||
|
@ -705,6 +800,13 @@ const Attr1 = extern union {
|
||||||
raw: u16,
|
raw: u16,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AffineAttr1 = extern union {
|
||||||
|
x: Bitfield(u16, 0, 9),
|
||||||
|
aff_sel: Bitfield(u16, 9, 5),
|
||||||
|
size: Bitfield(u16, 14, 2),
|
||||||
|
raw: u16,
|
||||||
|
};
|
||||||
|
|
||||||
const Attr2 = extern union {
|
const Attr2 = extern union {
|
||||||
tile_id: Bitfield(u16, 0, 10),
|
tile_id: Bitfield(u16, 0, 10),
|
||||||
rel_prio: Bitfield(u16, 10, 2),
|
rel_prio: Bitfield(u16, 10, 2),
|
||||||
|
|
Loading…
Reference in New Issue