Compare commits
10 Commits
2c6a6a2ebb
...
5d3a653b2e
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | 5d3a653b2e | |
Rekai Nyangadzayi Musuka | ee4149716a | |
Rekai Nyangadzayi Musuka | 2405ba2f7d | |
Rekai Nyangadzayi Musuka | ce1c670ba1 | |
Rekai Nyangadzayi Musuka | 8b9d06915e | |
Rekai Nyangadzayi Musuka | f1758e8426 | |
Rekai Nyangadzayi Musuka | 91427aa920 | |
Rekai Nyangadzayi Musuka | 03b2648f16 | |
Rekai Nyangadzayi Musuka | f6f9d3eb8d | |
Rekai Nyangadzayi Musuka | 0ccbd02964 |
|
@ -17,7 +17,7 @@ jobs:
|
|||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
runs-on: ${{matrix.os}}
|
||||
steps:
|
||||
- uses: goto-bus-stop/setup-zig@v2
|
||||
- uses: goto-bus-stop/setup-zig@v1
|
||||
with:
|
||||
version: master
|
||||
- name: prepare-linux
|
||||
|
@ -51,7 +51,7 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: goto-bus-stop/setup-zig@v2
|
||||
- uses: goto-bus-stop/setup-zig@v1
|
||||
with:
|
||||
version: master
|
||||
- run: zig fmt src/**/*.zig
|
||||
|
|
|
@ -6,6 +6,7 @@ const Eeprom = @import("backup/eeprom.zig").Eeprom;
|
|||
const Flash = @import("backup/Flash.zig");
|
||||
|
||||
const escape = @import("../../util.zig").escape;
|
||||
const span = @import("../../util.zig").span;
|
||||
|
||||
const Needle = struct { str: []const u8, kind: Backup.Kind };
|
||||
const backup_kinds = [6]Needle{
|
||||
|
@ -194,7 +195,7 @@ pub const Backup = struct {
|
|||
}
|
||||
|
||||
fn saveName(self: *const Self, allocator: Allocator) ![]const u8 {
|
||||
const title_str = std.mem.sliceTo(&escape(self.title), 0);
|
||||
const title_str = span(&escape(self.title));
|
||||
const name = if (title_str.len != 0) title_str else "untitled";
|
||||
|
||||
return try std.mem.concat(allocator, u8, &[_][]const u8{ name, ".sav" });
|
||||
|
|
114
src/core/ppu.zig
114
src/core/ppu.zig
|
@ -573,7 +573,7 @@ pub const Ppu = struct {
|
|||
|
||||
switch (bg_mode) {
|
||||
0x0 => {
|
||||
const framebuf_base = width * @as(usize, scanline);
|
||||
const fb_base = framebuf_pitch * @as(usize, scanline);
|
||||
if (obj_enable) self.fetchSprites();
|
||||
|
||||
var layer: usize = 0;
|
||||
|
@ -585,10 +585,23 @@ pub const Ppu = struct {
|
|||
if (layer == self.bg[3].cnt.priority.read() and bg_enable >> 3 & 1 == 1) self.drawBackground(3);
|
||||
}
|
||||
|
||||
self.drawTextMode(framebuf_base);
|
||||
// Copy Drawn Scanline to Frame Buffer
|
||||
// If there are any nulls present in self.scanline it means that no background drew a pixel there, so draw backdrop
|
||||
for (self.scanline.top()) |maybe_px, i| {
|
||||
const maybe_top = maybe_px;
|
||||
const maybe_btm = self.scanline.btm()[i];
|
||||
|
||||
const bgr555 = self.getBgr555(maybe_top, maybe_btm);
|
||||
std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], rgba888(bgr555));
|
||||
}
|
||||
|
||||
// Reset Current Scanline Pixel Buffer and list of fetched sprites
|
||||
// in prep for next scanline
|
||||
self.scanline.reset();
|
||||
std.mem.set(?Sprite, self.scanline_sprites, null);
|
||||
},
|
||||
0x1 => {
|
||||
const framebuf_base = width * @as(usize, scanline);
|
||||
const fb_base = framebuf_pitch * @as(usize, scanline);
|
||||
if (obj_enable) self.fetchSprites();
|
||||
|
||||
var layer: usize = 0;
|
||||
|
@ -599,10 +612,23 @@ pub const Ppu = struct {
|
|||
if (layer == self.bg[2].cnt.priority.read() and bg_enable >> 2 & 1 == 1) self.drawAffineBackground(2);
|
||||
}
|
||||
|
||||
self.drawTextMode(framebuf_base);
|
||||
// Copy Drawn Scanline to Frame Buffer
|
||||
// If there are any nulls present in self.scanline.top() it means that no background drew a pixel there, so draw backdrop
|
||||
for (self.scanline.top()) |maybe_px, i| {
|
||||
const maybe_top = maybe_px;
|
||||
const maybe_btm = self.scanline.btm()[i];
|
||||
|
||||
const bgr555 = self.getBgr555(maybe_top, maybe_btm);
|
||||
std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], rgba888(bgr555));
|
||||
}
|
||||
|
||||
// Reset Current Scanline Pixel Buffer and list of fetched sprites
|
||||
// in prep for next scanline
|
||||
self.scanline.reset();
|
||||
std.mem.set(?Sprite, self.scanline_sprites, null);
|
||||
},
|
||||
0x2 => {
|
||||
const framebuf_base = width * @as(usize, scanline);
|
||||
const fb_base = framebuf_pitch * @as(usize, scanline);
|
||||
if (obj_enable) self.fetchSprites();
|
||||
|
||||
var layer: usize = 0;
|
||||
|
@ -612,32 +638,40 @@ pub const Ppu = struct {
|
|||
if (layer == self.bg[3].cnt.priority.read() and bg_enable >> 3 & 1 == 1) self.drawAffineBackground(3);
|
||||
}
|
||||
|
||||
self.drawTextMode(framebuf_base);
|
||||
// Copy Drawn Scanline to Frame Buffer
|
||||
// If there are any nulls present in self.scanline.top() it means that no background drew a pixel there, so draw backdrop
|
||||
for (self.scanline.top()) |maybe_px, i| {
|
||||
const maybe_top = maybe_px;
|
||||
const maybe_btm = self.scanline.btm()[i];
|
||||
|
||||
const bgr555 = self.getBgr555(maybe_top, maybe_btm);
|
||||
std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], rgba888(bgr555));
|
||||
}
|
||||
|
||||
// Reset Current Scanline Pixel Buffer and list of fetched sprites
|
||||
// in prep for next scanline
|
||||
self.scanline.reset();
|
||||
std.mem.set(?Sprite, self.scanline_sprites, null);
|
||||
},
|
||||
0x3 => {
|
||||
const vram_base = width * @as(usize, scanline);
|
||||
const framebuf_base = width * @as(usize, scanline);
|
||||
const vram_base = width * @sizeOf(u16) * @as(usize, scanline);
|
||||
const fb_base = framebuf_pitch * @as(usize, scanline);
|
||||
|
||||
// FIXME: @ptrCast between slices changing the length isn't implemented yet
|
||||
const vram_buf = @ptrCast([*]const u16, @alignCast(@alignOf(u16), self.vram.buf));
|
||||
const framebuf = @ptrCast([*]u32, @alignCast(@alignOf(u32), self.framebuf.get(.Emulator)));
|
||||
|
||||
for (vram_buf[vram_base .. vram_base + width]) |bgr555, i| {
|
||||
framebuf[framebuf_base + i] = rgba888(bgr555);
|
||||
var i: usize = 0;
|
||||
while (i < width) : (i += 1) {
|
||||
const bgr555 = self.vram.read(u16, vram_base + i * @sizeOf(u16));
|
||||
std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], rgba888(bgr555));
|
||||
}
|
||||
},
|
||||
0x4 => {
|
||||
const sel = self.dispcnt.frame_select.read();
|
||||
|
||||
const vram_base = width * @as(usize, scanline) + if (sel) 0xA000 else @as(usize, 0);
|
||||
const framebuf_base = width * @as(usize, scanline);
|
||||
const fb_base = framebuf_pitch * @as(usize, scanline);
|
||||
|
||||
// FIXME: @ptrCast between slices changing the length isn't implemented yet
|
||||
const pal_buf = @ptrCast([*]const u16, @alignCast(@alignOf(u16), self.palette.buf));
|
||||
const framebuf = @ptrCast([*]u32, @alignCast(@alignOf(u32), self.framebuf.get(.Emulator)));
|
||||
|
||||
for (self.vram.buf[vram_base .. vram_base + width]) |pal_id, i| {
|
||||
framebuf[framebuf_base + i] = rgba888(pal_buf[pal_id]);
|
||||
// Render Current Scanline
|
||||
for (self.vram.buf[vram_base .. vram_base + width]) |byte, i| {
|
||||
const bgr555 = self.palette.read(u16, @as(u16, byte) * @sizeOf(u16));
|
||||
std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], rgba888(bgr555));
|
||||
}
|
||||
},
|
||||
0x5 => {
|
||||
|
@ -645,44 +679,22 @@ pub const Ppu = struct {
|
|||
const m5_height = 128;
|
||||
|
||||
const sel = self.dispcnt.frame_select.read();
|
||||
const vram_base = m5_width * @as(usize, scanline) + if (sel) 0xA000 else @as(usize, 0);
|
||||
const framebuf_base = width * @as(usize, scanline);
|
||||
|
||||
// FIXME: @ptrCast between slices changing the length isn't implemented yet
|
||||
const vram_buf = @ptrCast([*]const u16, @alignCast(@alignOf(u16), self.vram.buf));
|
||||
const framebuf = @ptrCast([*]u32, @alignCast(@alignOf(u32), self.framebuf.get(.Emulator)));
|
||||
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) {
|
||||
const bgr555 = if (scanline < m5_height and i < m5_width) vram_buf[vram_base + i] else self.palette.backdrop();
|
||||
framebuf[framebuf_base + i] = rgba888(bgr555);
|
||||
// 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.backdrop();
|
||||
|
||||
std.mem.writeIntNative(u32, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..@sizeOf(u32)], rgba888(bgr555));
|
||||
}
|
||||
},
|
||||
else => std.debug.panic("[PPU] TODO: Implement BG Mode {}", .{bg_mode}),
|
||||
}
|
||||
}
|
||||
|
||||
fn drawTextMode(self: *Self, framebuf_base: usize) void {
|
||||
// Copy Drawn Scanline to Frame Buffer
|
||||
// If there are any nulls present in self.scanline.top() it means that no background drew a pixel there, so draw backdrop
|
||||
|
||||
// FIXME: @ptrCast between slices changing the length isn't implemented yet
|
||||
const framebuf = @ptrCast([*]u32, @alignCast(@alignOf(u32), self.framebuf.get(.Emulator)));
|
||||
|
||||
for (self.scanline.top()) |maybe_px, i| {
|
||||
const maybe_top = maybe_px;
|
||||
const maybe_btm = self.scanline.btm()[i];
|
||||
|
||||
const bgr555 = self.getBgr555(maybe_top, maybe_btm);
|
||||
framebuf[framebuf_base + i] = rgba888(bgr555);
|
||||
}
|
||||
|
||||
// Reset Current Scanline Pixel Buffer and list of fetched sprites
|
||||
// in prep for next scanline
|
||||
self.scanline.reset();
|
||||
std.mem.set(?Sprite, self.scanline_sprites, null);
|
||||
}
|
||||
|
||||
fn getBgr555(self: *Self, maybe_top: ?u16, maybe_btm: ?u16) u16 {
|
||||
if (maybe_btm) |btm| {
|
||||
return switch (self.bld.cnt.mode.read()) {
|
||||
|
|
|
@ -42,6 +42,6 @@ pub fn deinit(self: *Self) void {
|
|||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub inline fn backdrop(self: *const Self) u16 {
|
||||
return std.mem.readIntNative(u16, self.buf[0..2]);
|
||||
pub fn backdrop(self: *const Self) u16 {
|
||||
return self.read(u16, 0);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi;
|
|||
const Scheduler = @import("core/scheduler.zig").Scheduler;
|
||||
const FpsTracker = @import("util.zig").FpsTracker;
|
||||
|
||||
const span = @import("util.zig").span;
|
||||
|
||||
const gba_width = @import("core/ppu.zig").width;
|
||||
const gba_height = @import("core/ppu.zig").height;
|
||||
|
||||
|
@ -71,7 +73,7 @@ pub const Gui = struct {
|
|||
|
||||
return Self{
|
||||
.window = window,
|
||||
.title = std.mem.sliceTo(title, 0),
|
||||
.title = span(title),
|
||||
.ctx = ctx,
|
||||
.program_id = program_id,
|
||||
.audio = Audio.init(apu),
|
||||
|
|
51
src/util.zig
51
src/util.zig
|
@ -68,6 +68,57 @@ pub fn intToBytes(comptime T: type, value: anytype) [@sizeOf(T)]u8 {
|
|||
return result;
|
||||
}
|
||||
|
||||
/// The Title from the GBA Cartridge is an Uppercase ASCII string which is
|
||||
/// null-padded to 12 bytes
|
||||
///
|
||||
/// This function returns a slice of the ASCII string without the null terminator(s)
|
||||
/// (essentially, a proper Zig/Rust/Any modern language String)
|
||||
pub fn span(title: *const [12]u8) []const u8 {
|
||||
const end = std.mem.indexOfScalar(u8, title, '\x00');
|
||||
return title[0 .. end orelse title.len];
|
||||
}
|
||||
|
||||
test "span" {
|
||||
var example: *const [12]u8 = "POKEMON_EMER";
|
||||
try std.testing.expectEqualSlices(u8, "POKEMON_EMER", span(example));
|
||||
|
||||
example = "POKEMON_EME\x00";
|
||||
try std.testing.expectEqualSlices(u8, "POKEMON_EME", span(example));
|
||||
|
||||
example = "POKEMON_EM\x00\x00";
|
||||
try std.testing.expectEqualSlices(u8, "POKEMON_EM", span(example));
|
||||
|
||||
example = "POKEMON_E\x00\x00\x00";
|
||||
try std.testing.expectEqualSlices(u8, "POKEMON_E", span(example));
|
||||
|
||||
example = "POKEMON_\x00\x00\x00\x00";
|
||||
try std.testing.expectEqualSlices(u8, "POKEMON_", span(example));
|
||||
|
||||
example = "POKEMON\x00\x00\x00\x00\x00";
|
||||
try std.testing.expectEqualSlices(u8, "POKEMON", span(example));
|
||||
|
||||
example = "POKEMO\x00\x00\x00\x00\x00\x00";
|
||||
try std.testing.expectEqualSlices(u8, "POKEMO", span(example));
|
||||
|
||||
example = "POKEM\x00\x00\x00\x00\x00\x00\x00";
|
||||
try std.testing.expectEqualSlices(u8, "POKEM", span(example));
|
||||
|
||||
example = "POKE\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
try std.testing.expectEqualSlices(u8, "POKE", span(example));
|
||||
|
||||
example = "POK\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
try std.testing.expectEqualSlices(u8, "POK", span(example));
|
||||
|
||||
example = "PO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
try std.testing.expectEqualSlices(u8, "PO", span(example));
|
||||
|
||||
example = "P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
try std.testing.expectEqualSlices(u8, "P", span(example));
|
||||
|
||||
example = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
try std.testing.expectEqualSlices(u8, "", span(example));
|
||||
}
|
||||
|
||||
/// Creates a copy of a title with all Filesystem-invalid characters replaced
|
||||
///
|
||||
/// e.g. POKEPIN R/S to POKEPIN R_S
|
||||
|
|
Loading…
Reference in New Issue