Compare commits
6 Commits
3a51707280
...
166bc6fc6d
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | 166bc6fc6d | |
Rekai Nyangadzayi Musuka | bf4207ba8c | |
Rekai Nyangadzayi Musuka | 78080b4682 | |
Rekai Nyangadzayi Musuka | 9159270e87 | |
Rekai Nyangadzayi Musuka | 428eff1468 | |
Rekai Nyangadzayi Musuka | 5ec8d4b0a5 |
|
@ -6,6 +6,9 @@
|
||||||
**/*.log
|
**/*.log
|
||||||
**/*.bin
|
**/*.bin
|
||||||
|
|
||||||
# Build on WIndows
|
# Build on Windows
|
||||||
/.build_config
|
/.build_config
|
||||||
/lib/SDL2
|
/lib/SDL2
|
||||||
|
|
||||||
|
# Any Custom Scripts for Debugging purposes
|
||||||
|
*.sh
|
|
@ -7,8 +7,11 @@ buf: []u8,
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
|
||||||
pub fn init(alloc: Allocator) !Self {
|
pub fn init(alloc: Allocator) !Self {
|
||||||
|
const buf = try alloc.alloc(u8, 0x8000);
|
||||||
|
std.mem.set(u8, buf, 0);
|
||||||
|
|
||||||
return Self{
|
return Self{
|
||||||
.buf = try alloc.alloc(u8, 0x8000),
|
.buf = buf,
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,11 @@ buf: []u8,
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
|
||||||
pub fn init(alloc: Allocator) !Self {
|
pub fn init(alloc: Allocator) !Self {
|
||||||
|
const buf = try alloc.alloc(u8, 0x40000);
|
||||||
|
std.mem.set(u8, buf, 0);
|
||||||
|
|
||||||
return Self{
|
return Self{
|
||||||
.buf = try alloc.alloc(u8, 0x40000),
|
.buf = buf,
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ pub const Io = struct {
|
||||||
0x0400_0000 => @as(u32, self.dispcnt.raw),
|
0x0400_0000 => @as(u32, self.dispcnt.raw),
|
||||||
0x0400_0004 => @as(u32, self.dispstat.raw),
|
0x0400_0004 => @as(u32, self.dispstat.raw),
|
||||||
0x0400_0006 => @as(u32, self.vcount.raw),
|
0x0400_0006 => @as(u32, self.vcount.raw),
|
||||||
0x0400_0130 => @as(u32, self.keyinput.raw),
|
|
||||||
0x0400_0200 => @as(u32, self.ie.raw),
|
0x0400_0200 => @as(u32, self.ie.raw),
|
||||||
0x0400_0208 => @boolToInt(self.ime),
|
0x0400_0208 => @boolToInt(self.ime),
|
||||||
else => std.debug.panic("[I/O:32] tried to read from {X:}", .{addr}),
|
else => std.debug.panic("[I/O:32] tried to read from {X:}", .{addr}),
|
||||||
|
|
|
@ -28,26 +28,28 @@ pub fn format78(comptime op: u2, comptime T: bool) InstrFn {
|
||||||
const address = cpu.r[rb] + cpu.r[ro];
|
const address = cpu.r[rb] + cpu.r[ro];
|
||||||
|
|
||||||
if (T) {
|
if (T) {
|
||||||
|
// Format 8
|
||||||
switch (op) {
|
switch (op) {
|
||||||
0b00 => {
|
0b00 => {
|
||||||
// STRH
|
// STRH
|
||||||
bus.write16(address & 0xFFFF_FFFE, @truncate(u16, cpu.r[rd]));
|
bus.write16(address & 0xFFFF_FFFE, @truncate(u16, cpu.r[rd]));
|
||||||
},
|
},
|
||||||
0b01 => {
|
0b01 => {
|
||||||
|
// LDSB
|
||||||
|
cpu.r[rd] = u32SignExtend(8, @as(u32, bus.read8(address)));
|
||||||
|
},
|
||||||
|
0b10 => {
|
||||||
// LDRH
|
// LDRH
|
||||||
const value = bus.read16(address & 0xFFFF_FFFE);
|
const value = bus.read16(address & 0xFFFF_FFFE);
|
||||||
cpu.r[rd] = std.math.rotr(u32, @as(u32, value), 8 * (address & 1));
|
cpu.r[rd] = std.math.rotr(u32, @as(u32, value), 8 * (address & 1));
|
||||||
},
|
},
|
||||||
0b10 => {
|
|
||||||
// LDSB
|
|
||||||
cpu.r[rd] = u32SignExtend(8, @as(u32, bus.read8(address)));
|
|
||||||
},
|
|
||||||
0b11 => {
|
0b11 => {
|
||||||
// LDSH
|
// LDSH
|
||||||
cpu.r[rd] = u32SignExtend(16, @as(u32, bus.read16(address & 0xFFFF_FFFE)));
|
cpu.r[rd] = u32SignExtend(16, @as(u32, bus.read16(address & 0xFFFF_FFFE)));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Format 7
|
||||||
switch (op) {
|
switch (op) {
|
||||||
0b00 => {
|
0b00 => {
|
||||||
// STR
|
// STR
|
||||||
|
|
|
@ -15,7 +15,7 @@ const File = std.fs.File;
|
||||||
const window_scale = 3;
|
const window_scale = 3;
|
||||||
const gba_width = @import("ppu.zig").width;
|
const gba_width = @import("ppu.zig").width;
|
||||||
const gba_height = @import("ppu.zig").height;
|
const gba_height = @import("ppu.zig").height;
|
||||||
const buf_pitch = @import("ppu.zig").buf_pitch;
|
const framebuf_pitch = @import("ppu.zig").framebuf_pitch;
|
||||||
|
|
||||||
pub const enable_logging: bool = false;
|
pub const enable_logging: bool = false;
|
||||||
const is_binary: bool = false;
|
const is_binary: bool = false;
|
||||||
|
@ -164,8 +164,8 @@ pub fn main() anyerror!void {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Is it OK just to copy the Emulator's Frame Buffer to SDL?
|
// FIXME: Is it OK just to copy the Emulator's Frame Buffer to SDL?
|
||||||
const buf_ptr = bus.ppu.frame_buf.ptr;
|
const buf_ptr = bus.ppu.framebuf.ptr;
|
||||||
_ = SDL.SDL_UpdateTexture(texture, null, buf_ptr, buf_pitch);
|
_ = SDL.SDL_UpdateTexture(texture, null, buf_ptr, framebuf_pitch);
|
||||||
_ = SDL.SDL_RenderCopy(renderer, texture, null, null);
|
_ = SDL.SDL_RenderCopy(renderer, texture, null, null);
|
||||||
SDL.SDL_RenderPresent(renderer);
|
SDL.SDL_RenderPresent(renderer);
|
||||||
|
|
||||||
|
|
34
src/ppu.zig
34
src/ppu.zig
|
@ -7,8 +7,7 @@ const Scheduler = @import("scheduler.zig").Scheduler;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
pub const width = 240;
|
pub const width = 240;
|
||||||
pub const height = 160;
|
pub const height = 160;
|
||||||
pub const buf_pitch = width * @sizeOf(u16);
|
pub const framebuf_pitch = width * @sizeOf(u16);
|
||||||
const buf_len = buf_pitch * height;
|
|
||||||
|
|
||||||
pub const Ppu = struct {
|
pub const Ppu = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
@ -16,24 +15,27 @@ pub const Ppu = struct {
|
||||||
vram: Vram,
|
vram: Vram,
|
||||||
palette: Palette,
|
palette: Palette,
|
||||||
sched: *Scheduler,
|
sched: *Scheduler,
|
||||||
frame_buf: []u8,
|
framebuf: []u8,
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
|
||||||
pub fn init(alloc: Allocator, sched: *Scheduler) !Self {
|
pub fn init(alloc: Allocator, sched: *Scheduler) !Self {
|
||||||
// Queue first Hblank
|
// Queue first Hblank
|
||||||
sched.push(.Draw, sched.tick + (240 * 4));
|
sched.push(.Draw, sched.tick + (240 * 4));
|
||||||
|
|
||||||
|
const framebuf = try alloc.alloc(u8, framebuf_pitch * height);
|
||||||
|
std.mem.set(u8, framebuf, 0);
|
||||||
|
|
||||||
return Self{
|
return Self{
|
||||||
.vram = try Vram.init(alloc),
|
.vram = try Vram.init(alloc),
|
||||||
.palette = try Palette.init(alloc),
|
.palette = try Palette.init(alloc),
|
||||||
.sched = sched,
|
.sched = sched,
|
||||||
.frame_buf = try alloc.alloc(u8, buf_len),
|
.framebuf = framebuf,
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: Self) void {
|
pub fn deinit(self: Self) void {
|
||||||
self.alloc.free(self.frame_buf);
|
self.alloc.free(self.framebuf);
|
||||||
self.vram.deinit();
|
self.vram.deinit();
|
||||||
self.palette.deinit();
|
self.palette.deinit();
|
||||||
}
|
}
|
||||||
|
@ -44,10 +46,10 @@ pub const Ppu = struct {
|
||||||
|
|
||||||
switch (bg_mode) {
|
switch (bg_mode) {
|
||||||
0x3 => {
|
0x3 => {
|
||||||
const start = buf_pitch * @as(usize, scanline);
|
const start = framebuf_pitch * @as(usize, scanline);
|
||||||
const end = start + buf_pitch;
|
const end = start + framebuf_pitch;
|
||||||
|
|
||||||
std.mem.copy(u8, self.frame_buf[start..end], self.vram.buf[start..end]);
|
std.mem.copy(u8, self.framebuf[start..end], self.vram.buf[start..end]);
|
||||||
},
|
},
|
||||||
0x4 => {
|
0x4 => {
|
||||||
const select = io.dispcnt.frame_select.read();
|
const select = io.dispcnt.frame_select.read();
|
||||||
|
@ -62,8 +64,8 @@ pub const Ppu = struct {
|
||||||
const id = byte * 2;
|
const id = byte * 2;
|
||||||
const j = i * @sizeOf(u16);
|
const j = i * @sizeOf(u16);
|
||||||
|
|
||||||
self.frame_buf[buf_start + j + 1] = self.palette.buf[id + 1];
|
self.framebuf[buf_start + j + 1] = self.palette.buf[id + 1];
|
||||||
self.frame_buf[buf_start + j] = self.palette.buf[id];
|
self.framebuf[buf_start + j] = self.palette.buf[id];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
else => {}, // std.debug.panic("[PPU] TODO: Implement BG Mode {}", .{bg_mode}),
|
else => {}, // std.debug.panic("[PPU] TODO: Implement BG Mode {}", .{bg_mode}),
|
||||||
|
@ -78,8 +80,11 @@ const Palette = struct {
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
|
||||||
fn init(alloc: Allocator) !Self {
|
fn init(alloc: Allocator) !Self {
|
||||||
|
const buf = try alloc.alloc(u8, 0x400);
|
||||||
|
std.mem.set(u8, buf, 0);
|
||||||
|
|
||||||
return Self{
|
return Self{
|
||||||
.buf = try alloc.alloc(u8, 0x400),
|
.buf = buf,
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -118,13 +123,8 @@ const Vram = struct {
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
|
||||||
fn init(alloc: Allocator) !Self {
|
fn init(alloc: Allocator) !Self {
|
||||||
// In Modes 3 and 4, parts of the VRAM are copied to the
|
|
||||||
// frame buffer, therefore we want to zero-initialize Vram
|
|
||||||
//
|
|
||||||
// some programs like Armwrestler assume that VRAM is zeroed-out.
|
|
||||||
const black = std.mem.zeroes([0x18000]u8);
|
|
||||||
const buf = try alloc.alloc(u8, 0x18000);
|
const buf = try alloc.alloc(u8, 0x18000);
|
||||||
std.mem.copy(u8, buf, &black);
|
std.mem.set(u8, buf, 0);
|
||||||
|
|
||||||
return Self{
|
return Self{
|
||||||
.buf = buf,
|
.buf = buf,
|
||||||
|
|
10
src/util.zig
10
src/util.zig
|
@ -1,6 +1,10 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub fn signExtend(comptime T: type, comptime bits: usize, value: anytype) T {
|
pub fn u32SignExtend(comptime bits: usize, value: u32) u32 {
|
||||||
|
return @bitCast(u32, signExtend(i32, bits, @bitCast(i32, value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signExtend(comptime T: type, comptime bits: usize, value: anytype) T {
|
||||||
const ValT = comptime @TypeOf(value);
|
const ValT = comptime @TypeOf(value);
|
||||||
comptime std.debug.assert(isInteger(ValT));
|
comptime std.debug.assert(isInteger(ValT));
|
||||||
comptime std.debug.assert(isSigned(ValT));
|
comptime std.debug.assert(isSigned(ValT));
|
||||||
|
@ -16,10 +20,6 @@ pub fn signExtend(comptime T: type, comptime bits: usize, value: anytype) T {
|
||||||
return ((value & ((1 << bits) - 1)) << bit_diff) >> bit_diff;
|
return ((value & ((1 << bits) - 1)) << bit_diff) >> bit_diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn u32SignExtend(comptime bits: usize, value: u32) u32 {
|
|
||||||
return @bitCast(u32, signExtend(i32, bits, @bitCast(i32, value)));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn isInteger(comptime T: type) bool {
|
fn isInteger(comptime T: type) bool {
|
||||||
return @typeInfo(T) == .Int;
|
return @typeInfo(T) == .Int;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue