Compare commits
1 Commits
f59ad9bef5
...
0765682035
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | 0765682035 |
|
@ -1 +1 @@
|
||||||
Subproject commit 80e7409e21ebbf0bd182b34b6e0206cc26e5ca05
|
Subproject commit 602aeb7f1d33da71626aff54a25ef69fef7c613b
|
2
lib/zgui
2
lib/zgui
|
@ -1 +1 @@
|
||||||
Subproject commit ca27a472249a70dd1e94fb94f05f26dd931363fb
|
Subproject commit 2e4f8d1409ffbf957d5edaa080492e1b50ac059f
|
|
@ -1,6 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const Header = @import("cartridge.zig").Header;
|
const Header = @import("cartridge.zig").Header;
|
||||||
|
const SharedIo = @import("io.zig").Io;
|
||||||
const Scheduler = @import("Scheduler.zig");
|
const Scheduler = @import("Scheduler.zig");
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
@ -8,16 +9,13 @@ const Allocator = std.mem.Allocator;
|
||||||
/// Load a NDS Cartridge
|
/// Load a NDS Cartridge
|
||||||
///
|
///
|
||||||
/// intended to be used immediately after Emulator initialization
|
/// intended to be used immediately after Emulator initialization
|
||||||
pub fn load(allocator: Allocator, system: System, rom_path: []const u8) ![12]u8 {
|
pub fn load(allocator: Allocator, system: System, rom_file: std.fs.File) ![12]u8 {
|
||||||
const log = std.log.scoped(.load_rom);
|
const log = std.log.scoped(.load_rom);
|
||||||
|
|
||||||
const file = try std.fs.cwd().openFile(rom_path, .{});
|
const rom_buf = try rom_file.readToEndAlloc(allocator, try rom_file.getEndPos());
|
||||||
defer file.close();
|
defer allocator.free(rom_buf);
|
||||||
|
|
||||||
const buf = try file.readToEndAlloc(allocator, try file.getEndPos());
|
var stream = std.io.fixedBufferStream(rom_buf);
|
||||||
defer allocator.free(buf);
|
|
||||||
|
|
||||||
var stream = std.io.fixedBufferStream(buf);
|
|
||||||
const header = try stream.reader().readStruct(Header);
|
const header = try stream.reader().readStruct(Header);
|
||||||
|
|
||||||
log.info("Title: \"{s}\"", .{std.mem.sliceTo(&header.title, 0)});
|
log.info("Title: \"{s}\"", .{std.mem.sliceTo(&header.title, 0)});
|
||||||
|
@ -32,7 +30,7 @@ pub fn load(allocator: Allocator, system: System, rom_path: []const u8) ![12]u8
|
||||||
log.debug("ARM9 Size: 0x{X:0>8}", .{header.arm9_size});
|
log.debug("ARM9 Size: 0x{X:0>8}", .{header.arm9_size});
|
||||||
|
|
||||||
// Copy ARM9 Code into Main Memory
|
// Copy ARM9 Code into Main Memory
|
||||||
for (buf[header.arm9_rom_offset..][0..header.arm9_size], 0..) |value, i| {
|
for (rom_buf[header.arm9_rom_offset..][0..header.arm9_size], 0..) |value, i| {
|
||||||
const address = header.arm9_ram_address + @as(u32, @intCast(i));
|
const address = header.arm9_ram_address + @as(u32, @intCast(i));
|
||||||
system.bus9.dbgWrite(u8, address, value);
|
system.bus9.dbgWrite(u8, address, value);
|
||||||
}
|
}
|
||||||
|
@ -48,7 +46,7 @@ pub fn load(allocator: Allocator, system: System, rom_path: []const u8) ![12]u8
|
||||||
log.debug("ARM7 Size: 0x{X:0>8}", .{header.arm7_size});
|
log.debug("ARM7 Size: 0x{X:0>8}", .{header.arm7_size});
|
||||||
|
|
||||||
// Copy ARM7 Code into Main Memory
|
// Copy ARM7 Code into Main Memory
|
||||||
for (buf[header.arm7_rom_offset..][0..header.arm7_size], 0..) |value, i| {
|
for (rom_buf[header.arm7_rom_offset..][0..header.arm7_size], 0..) |value, i| {
|
||||||
const address = header.arm7_ram_address + @as(u32, @intCast(i));
|
const address = header.arm7_ram_address + @as(u32, @intCast(i));
|
||||||
system.bus7.dbgWrite(u8, address, value);
|
system.bus7.dbgWrite(u8, address, value);
|
||||||
}
|
}
|
||||||
|
@ -59,39 +57,6 @@ pub fn load(allocator: Allocator, system: System, rom_path: []const u8) ![12]u8
|
||||||
return header.title;
|
return header.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load NDS Firmware
|
|
||||||
pub fn loadFirm(allocator: Allocator, system: System, firm_path: []const u8) !void {
|
|
||||||
const log = std.log.scoped(.load_firm);
|
|
||||||
|
|
||||||
{ // NDS7 BIOS
|
|
||||||
const path = try std.mem.join(allocator, "/", &.{ firm_path, "bios7.bin" });
|
|
||||||
defer allocator.free(path);
|
|
||||||
|
|
||||||
log.debug("bios7 path: {s}", .{path});
|
|
||||||
|
|
||||||
const file = try std.fs.cwd().openFile(path, .{});
|
|
||||||
defer file.close();
|
|
||||||
|
|
||||||
const buf = try file.readToEndAlloc(allocator, try file.getEndPos());
|
|
||||||
defer allocator.free(buf);
|
|
||||||
|
|
||||||
@memcpy(system.bus7.bios[0..buf.len], buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // NDS9 BIOS
|
|
||||||
const path = try std.mem.join(allocator, "/", &.{ firm_path, "bios9.bin" });
|
|
||||||
defer allocator.free(path);
|
|
||||||
|
|
||||||
const file = try std.fs.cwd().openFile(path, .{});
|
|
||||||
defer file.close();
|
|
||||||
|
|
||||||
const buf = try file.readToEndAlloc(allocator, try file.getEndPos());
|
|
||||||
defer allocator.free(buf);
|
|
||||||
|
|
||||||
@memcpy(system.bus9.bios[0..buf.len], buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const bus_clock = 33513982; // 33.513982 Hz
|
const bus_clock = 33513982; // 33.513982 Hz
|
||||||
const dot_clock = 5585664; // 5.585664 Hz
|
const dot_clock = 5585664; // 5.585664 Hz
|
||||||
const arm7_clock = bus_clock;
|
const arm7_clock = bus_clock;
|
||||||
|
@ -125,14 +90,13 @@ pub fn runFrame(scheduler: *Scheduler, system: System) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Perf win to allocating on the stack instead?
|
// FIXME: Perf win to allocating on the stack instead?
|
||||||
pub const SharedCtx = struct {
|
pub const SharedContext = struct {
|
||||||
const MiB = 0x100000;
|
const MiB = 0x100000;
|
||||||
const KiB = 0x400;
|
const KiB = 0x400;
|
||||||
|
|
||||||
pub const Io = @import("io.zig").Io;
|
|
||||||
const Vram = @import("ppu.zig").Vram;
|
const Vram = @import("ppu.zig").Vram;
|
||||||
|
|
||||||
io: *Io,
|
io: *SharedIo,
|
||||||
main: *[4 * MiB]u8,
|
main: *[4 * MiB]u8,
|
||||||
wram: *Wram,
|
wram: *Wram,
|
||||||
vram: *Vram,
|
vram: *Vram,
|
||||||
|
@ -148,7 +112,7 @@ pub const SharedCtx = struct {
|
||||||
|
|
||||||
const ctx = .{
|
const ctx = .{
|
||||||
.io = blk: {
|
.io = blk: {
|
||||||
const io = try allocator.create(Io);
|
const io = try allocator.create(SharedIo);
|
||||||
io.* = .{};
|
io.* = .{};
|
||||||
|
|
||||||
break :blk io;
|
break :blk io;
|
||||||
|
@ -309,27 +273,3 @@ pub const System = struct {
|
||||||
self.bus9.deinit(allocator);
|
self.bus9.deinit(allocator);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: Using Wram.Device here is jank. System should probably carry an Enum + some Generic Type Fns
|
|
||||||
pub fn handleInterrupt(comptime dev: Wram.Device, cpu: if (dev == .nds9) *System.Arm946es else *System.Arm7tdmi) void {
|
|
||||||
const Bus = if (dev == .nds9) System.Bus9 else System.Bus7;
|
|
||||||
const bus_ptr: *Bus = @ptrCast(@alignCast(cpu.bus.ptr));
|
|
||||||
|
|
||||||
if (!bus_ptr.io.ime or cpu.cpsr.i.read()) return; // ensure irqs are enabled
|
|
||||||
if ((bus_ptr.io.ie.raw & bus_ptr.io.irq.raw) == 0) return; // ensure there is an irq to handle
|
|
||||||
|
|
||||||
// TODO: Handle HALT
|
|
||||||
// HALTCNG (NDS7) and CP15 (NDS9)
|
|
||||||
|
|
||||||
const ret_addr = cpu.r[15] - if (cpu.cpsr.t.read()) 0 else @as(u32, 4);
|
|
||||||
const spsr = cpu.cpsr;
|
|
||||||
|
|
||||||
cpu.changeMode(.Irq);
|
|
||||||
cpu.cpsr.t.unset();
|
|
||||||
cpu.cpsr.i.set();
|
|
||||||
|
|
||||||
cpu.r[14] = ret_addr;
|
|
||||||
cpu.spsr.raw = spsr.raw;
|
|
||||||
cpu.r[15] = if (dev == .nds9) 0xFFFF_0018 else 0x0000_0018;
|
|
||||||
cpu.pipe.reload(cpu);
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,8 +2,6 @@ const std = @import("std");
|
||||||
|
|
||||||
const Bitfield = @import("bitfield").Bitfield;
|
const Bitfield = @import("bitfield").Bitfield;
|
||||||
const Bit = @import("bitfield").Bit;
|
const Bit = @import("bitfield").Bit;
|
||||||
const System = @import("emu.zig").System;
|
|
||||||
const handleInterrupt = @import("emu.zig").handleInterrupt;
|
|
||||||
|
|
||||||
const log = std.log.scoped(.shared_io);
|
const log = std.log.scoped(.shared_io);
|
||||||
|
|
||||||
|
@ -13,9 +11,11 @@ const log = std.log.scoped(.shared_io);
|
||||||
|
|
||||||
pub const Io = struct {
|
pub const Io = struct {
|
||||||
/// Inter Process Communication FIFO
|
/// Inter Process Communication FIFO
|
||||||
ipc: Ipc = .{},
|
ipc_fifo: IpcFifo = .{},
|
||||||
|
|
||||||
wramcnt: WramCnt = .{ .raw = 0x00 },
|
wramcnt: WramCnt = .{ .raw = 0x00 },
|
||||||
|
|
||||||
|
// TODO: DS Cartridge I/O Ports
|
||||||
};
|
};
|
||||||
|
|
||||||
fn warn(comptime format: []const u8, args: anytype) u0 {
|
fn warn(comptime format: []const u8, args: anytype) u0 {
|
||||||
|
@ -23,20 +23,13 @@ fn warn(comptime format: []const u8, args: anytype) u0 {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inter-Process Communication
|
const IpcFifo = struct {
|
||||||
const Ipc = struct {
|
|
||||||
const Sync = IpcSync;
|
const Sync = IpcSync;
|
||||||
const Control = IpcFifoCnt;
|
const Control = IpcFifoCnt;
|
||||||
|
|
||||||
_nds7: Impl = .{},
|
_nds7: Impl = .{},
|
||||||
_nds9: Impl = .{},
|
_nds9: Impl = .{},
|
||||||
|
|
||||||
// we need access to the CPUs to handle IPC IRQs
|
|
||||||
arm7tdmi: ?*System.Arm7tdmi = null,
|
|
||||||
arm946es: ?*System.Arm946es = null,
|
|
||||||
|
|
||||||
// TODO: DS Cartridge I/O Ports
|
|
||||||
|
|
||||||
const Source = enum { nds7, nds9 };
|
const Source = enum { nds7, nds9 };
|
||||||
|
|
||||||
const Impl = struct {
|
const Impl = struct {
|
||||||
|
@ -54,11 +47,6 @@ const Ipc = struct {
|
||||||
last_read: ?u32 = null,
|
last_read: ?u32 = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn configure(self: *@This(), system: System) void {
|
|
||||||
self.arm7tdmi = system.arm7tdmi;
|
|
||||||
self.arm946es = system.arm946es;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// IPCSYNC
|
/// IPCSYNC
|
||||||
/// Read/Write
|
/// Read/Write
|
||||||
pub fn setIpcSync(self: *@This(), comptime src: Source, value: anytype) void {
|
pub fn setIpcSync(self: *@This(), comptime src: Source, value: anytype) void {
|
||||||
|
@ -67,13 +55,6 @@ const Ipc = struct {
|
||||||
self._nds7.sync.raw = masks.ipcFifoSync(self._nds7.sync.raw, value);
|
self._nds7.sync.raw = masks.ipcFifoSync(self._nds7.sync.raw, value);
|
||||||
self._nds9.sync.raw = masks.mask(self._nds9.sync.raw, (self._nds7.sync.raw >> 8) & 0xF, 0xF);
|
self._nds9.sync.raw = masks.mask(self._nds9.sync.raw, (self._nds7.sync.raw >> 8) & 0xF, 0xF);
|
||||||
|
|
||||||
if (value >> 13 & 1 == 1 and self._nds9.sync.recv_irq.read()) {
|
|
||||||
const bus: *System.Bus9 = @ptrCast(@alignCast(self.arm946es.?.bus.ptr));
|
|
||||||
|
|
||||||
bus.io.irq.ipcsync.set();
|
|
||||||
handleInterrupt(.nds9, self.arm946es.?);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value >> 3 & 1 == 1) {
|
if (value >> 3 & 1 == 1) {
|
||||||
self._nds7.fifo.reset();
|
self._nds7.fifo.reset();
|
||||||
|
|
||||||
|
@ -88,13 +69,6 @@ const Ipc = struct {
|
||||||
self._nds9.sync.raw = masks.ipcFifoSync(self._nds9.sync.raw, value);
|
self._nds9.sync.raw = masks.ipcFifoSync(self._nds9.sync.raw, value);
|
||||||
self._nds7.sync.raw = masks.mask(self._nds7.sync.raw, (self._nds9.sync.raw >> 8) & 0xF, 0xF);
|
self._nds7.sync.raw = masks.mask(self._nds7.sync.raw, (self._nds9.sync.raw >> 8) & 0xF, 0xF);
|
||||||
|
|
||||||
if (value >> 13 & 1 == 1 and self._nds7.sync.recv_irq.read()) {
|
|
||||||
const bus: *System.Bus7 = @ptrCast(@alignCast(self.arm7tdmi.?.bus.ptr));
|
|
||||||
|
|
||||||
bus.io.irq.ipcsync.set();
|
|
||||||
handleInterrupt(.nds7, self.arm7tdmi.?);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value >> 3 & 1 == 1) {
|
if (value >> 3 & 1 == 1) {
|
||||||
self._nds9.fifo.reset();
|
self._nds9.fifo.reset();
|
||||||
|
|
||||||
|
@ -125,13 +99,6 @@ const Ipc = struct {
|
||||||
if (!self._nds7.cnt.enable_fifos.read()) return;
|
if (!self._nds7.cnt.enable_fifos.read()) return;
|
||||||
try self._nds7.fifo.push(value);
|
try self._nds7.fifo.push(value);
|
||||||
|
|
||||||
if (self._nds9.cnt.recv_fifo_irq_enable.read()) {
|
|
||||||
const bus: *System.Bus9 = @ptrCast(@alignCast(self.arm946es.?.bus.ptr));
|
|
||||||
bus.io.irq.ipc_recv_not_empty.set();
|
|
||||||
|
|
||||||
handleInterrupt(.nds9, self.arm946es.?);
|
|
||||||
}
|
|
||||||
|
|
||||||
// update status bits
|
// update status bits
|
||||||
self._nds7.cnt.send_fifo_empty.write(self._nds7.fifo._len() == 0);
|
self._nds7.cnt.send_fifo_empty.write(self._nds7.fifo._len() == 0);
|
||||||
self._nds9.cnt.recv_fifo_empty.write(self._nds7.fifo._len() == 0);
|
self._nds9.cnt.recv_fifo_empty.write(self._nds7.fifo._len() == 0);
|
||||||
|
@ -143,13 +110,6 @@ const Ipc = struct {
|
||||||
if (!self._nds9.cnt.enable_fifos.read()) return;
|
if (!self._nds9.cnt.enable_fifos.read()) return;
|
||||||
try self._nds9.fifo.push(value);
|
try self._nds9.fifo.push(value);
|
||||||
|
|
||||||
if (self._nds7.cnt.recv_fifo_irq_enable.read()) {
|
|
||||||
const bus: *System.Bus7 = @ptrCast(@alignCast(self.arm7tdmi.?.bus.ptr));
|
|
||||||
bus.io.irq.ipc_recv_not_empty.set();
|
|
||||||
|
|
||||||
handleInterrupt(.nds7, self.arm7tdmi.?);
|
|
||||||
}
|
|
||||||
|
|
||||||
// update status bits
|
// update status bits
|
||||||
self._nds9.cnt.send_fifo_empty.write(self._nds9.fifo._len() == 0);
|
self._nds9.cnt.send_fifo_empty.write(self._nds9.fifo._len() == 0);
|
||||||
self._nds7.cnt.recv_fifo_empty.write(self._nds9.fifo._len() == 0);
|
self._nds7.cnt.recv_fifo_empty.write(self._nds9.fifo._len() == 0);
|
||||||
|
@ -173,13 +133,6 @@ const Ipc = struct {
|
||||||
break :blk val;
|
break :blk val;
|
||||||
} else blk: {
|
} else blk: {
|
||||||
self._nds7.cnt.fifo_error.set();
|
self._nds7.cnt.fifo_error.set();
|
||||||
if (self._nds9.cnt.send_fifo_irq_enable.read()) {
|
|
||||||
const bus: *System.Bus9 = @ptrCast(@alignCast(self.arm946es.?.bus.ptr));
|
|
||||||
bus.io.irq.ipc_send_empty.set();
|
|
||||||
|
|
||||||
handleInterrupt(.nds9, self.arm946es.?);
|
|
||||||
}
|
|
||||||
|
|
||||||
break :blk self._nds7.last_read orelse 0x0000_0000;
|
break :blk self._nds7.last_read orelse 0x0000_0000;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -201,14 +154,6 @@ const Ipc = struct {
|
||||||
break :blk val;
|
break :blk val;
|
||||||
} else blk: {
|
} else blk: {
|
||||||
self._nds9.cnt.fifo_error.set();
|
self._nds9.cnt.fifo_error.set();
|
||||||
|
|
||||||
if (self._nds7.cnt.send_fifo_irq_enable.read()) {
|
|
||||||
const bus: *System.Bus7 = @ptrCast(@alignCast(self.arm7tdmi.?.bus.ptr));
|
|
||||||
bus.io.irq.ipc_send_empty.set();
|
|
||||||
|
|
||||||
handleInterrupt(.nds7, self.arm7tdmi.?);
|
|
||||||
}
|
|
||||||
|
|
||||||
break :blk self._nds7.last_read orelse 0x0000_0000;
|
break :blk self._nds7.last_read orelse 0x0000_0000;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -304,13 +249,7 @@ pub const masks = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: bitfields depends on NDS9 / NDS7
|
pub const PostFlag = enum(u1) { in_progress = 0, completed };
|
||||||
pub const IntEnable = extern union {
|
|
||||||
ipcsync: Bit(u32, 16),
|
|
||||||
ipc_send_empty: Bit(u32, 17),
|
|
||||||
ipc_recv_not_empty: Bit(u32, 18),
|
|
||||||
raw: u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Fifo = struct {
|
const Fifo = struct {
|
||||||
const Index = u8;
|
const Index = u8;
|
||||||
|
|
|
@ -2,7 +2,8 @@ const std = @import("std");
|
||||||
const io = @import("io.zig");
|
const io = @import("io.zig");
|
||||||
|
|
||||||
const Scheduler = @import("../Scheduler.zig");
|
const Scheduler = @import("../Scheduler.zig");
|
||||||
const SharedCtx = @import("../emu.zig").SharedCtx;
|
const SharedIo = @import("../io.zig").Io;
|
||||||
|
const SharedContext = @import("../emu.zig").SharedContext;
|
||||||
const Wram = @import("../emu.zig").Wram;
|
const Wram = @import("../emu.zig").Wram;
|
||||||
const Vram = @import("../ppu.zig").Vram;
|
const Vram = @import("../ppu.zig").Vram;
|
||||||
const forceAlign = @import("../emu.zig").forceAlign;
|
const forceAlign = @import("../emu.zig").forceAlign;
|
||||||
|
@ -17,37 +18,28 @@ const log = std.log.scoped(.nds7_bus);
|
||||||
|
|
||||||
scheduler: *Scheduler,
|
scheduler: *Scheduler,
|
||||||
main: *[4 * MiB]u8,
|
main: *[4 * MiB]u8,
|
||||||
shr_wram: *Wram,
|
wram_shr: *Wram,
|
||||||
wram: *[64 * KiB]u8,
|
wram: *[64 * KiB]u8,
|
||||||
vram: *Vram,
|
vram: *Vram,
|
||||||
io: io.Io,
|
io: io.Io,
|
||||||
|
|
||||||
bios: *[16 * KiB]u8,
|
pub fn init(allocator: Allocator, scheduler: *Scheduler, shared_ctx: SharedContext) !@This() {
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, scheduler: *Scheduler, ctx: SharedCtx) !@This() {
|
|
||||||
const wram = try allocator.create([64 * KiB]u8);
|
const wram = try allocator.create([64 * KiB]u8);
|
||||||
@memset(wram, 0);
|
|
||||||
errdefer allocator.destroy(wram);
|
errdefer allocator.destroy(wram);
|
||||||
|
@memset(wram, 0);
|
||||||
const bios = try allocator.create([16 * KiB]u8);
|
|
||||||
@memset(bios, 0);
|
|
||||||
errdefer allocator.destroy(bios);
|
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.main = ctx.main,
|
.main = shared_ctx.main,
|
||||||
.shr_wram = ctx.wram,
|
.wram_shr = shared_ctx.wram,
|
||||||
.vram = ctx.vram,
|
.vram = shared_ctx.vram,
|
||||||
.wram = wram,
|
.wram = wram,
|
||||||
.scheduler = scheduler,
|
.scheduler = scheduler,
|
||||||
.io = io.Io.init(ctx.io),
|
.io = io.Io.init(shared_ctx.io),
|
||||||
|
|
||||||
.bios = bios,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *@This(), allocator: Allocator) void {
|
pub fn deinit(self: *@This(), allocator: Allocator) void {
|
||||||
allocator.destroy(self.wram);
|
allocator.destroy(self.wram);
|
||||||
allocator.destroy(self.bios);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(_: *@This()) void {}
|
pub fn reset(_: *@This()) void {}
|
||||||
|
@ -73,13 +65,12 @@ fn _read(self: *@This(), comptime T: type, comptime mode: Mode, address: u32) T
|
||||||
}
|
}
|
||||||
|
|
||||||
return switch (aligned_addr) {
|
return switch (aligned_addr) {
|
||||||
0x0000_0000...0x01FF_FFFF => readInt(T, self.bios[address & 0x3FFF ..][0..byte_count]),
|
|
||||||
0x0200_0000...0x02FF_FFFF => readInt(T, self.main[aligned_addr & 0x003F_FFFF ..][0..byte_count]),
|
0x0200_0000...0x02FF_FFFF => readInt(T, self.main[aligned_addr & 0x003F_FFFF ..][0..byte_count]),
|
||||||
0x0300_0000...0x037F_FFFF => switch (self.io.shr.wramcnt.mode.read()) {
|
0x0300_0000...0x037F_FFFF => switch (self.io.shared.wramcnt.mode.read()) {
|
||||||
0b00 => readInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count]),
|
0b00 => readInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count]),
|
||||||
else => self.shr_wram.read(T, .nds7, aligned_addr),
|
else => self.wram_shr.read(T, .nds7, aligned_addr),
|
||||||
},
|
},
|
||||||
0x0380_0000...0x03FF_FFFF => readInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count]),
|
0x0380_0000...0x0380_FFFF => readInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count]),
|
||||||
0x0400_0000...0x04FF_FFFF => io.read(self, T, aligned_addr),
|
0x0400_0000...0x04FF_FFFF => io.read(self, T, aligned_addr),
|
||||||
0x0600_0000...0x06FF_FFFF => self.vram.read(T, .nds7, aligned_addr),
|
0x0600_0000...0x06FF_FFFF => self.vram.read(T, .nds7, aligned_addr),
|
||||||
else => warn("unexpected read: 0x{x:0>8} -> {}", .{ aligned_addr, T }),
|
else => warn("unexpected read: 0x{x:0>8} -> {}", .{ aligned_addr, T }),
|
||||||
|
@ -107,11 +98,10 @@ fn _write(self: *@This(), comptime T: type, comptime mode: Mode, address: u32, v
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (aligned_addr) {
|
switch (aligned_addr) {
|
||||||
0x0000_0000...0x01FF_FFFF => log.err("tried to read from NDS7 BIOS: 0x{X:0>8}", .{aligned_addr}),
|
|
||||||
0x0200_0000...0x02FF_FFFF => writeInt(T, self.main[aligned_addr & 0x003F_FFFF ..][0..byte_count], value),
|
0x0200_0000...0x02FF_FFFF => writeInt(T, self.main[aligned_addr & 0x003F_FFFF ..][0..byte_count], value),
|
||||||
0x0300_0000...0x037F_FFFF => switch (self.io.shr.wramcnt.mode.read()) {
|
0x0300_0000...0x037F_FFFF => switch (self.io.shared.wramcnt.mode.read()) {
|
||||||
0b00 => writeInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count], value),
|
0b00 => writeInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count], value),
|
||||||
else => self.shr_wram.write(T, .nds7, aligned_addr, value),
|
else => self.wram_shr.write(T, .nds7, aligned_addr, value),
|
||||||
},
|
},
|
||||||
0x0380_0000...0x0380_FFFF => writeInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count], value),
|
0x0380_0000...0x0380_FFFF => writeInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count], value),
|
||||||
0x0400_0000...0x04FF_FFFF => io.write(self, T, aligned_addr, value),
|
0x0400_0000...0x04FF_FFFF => io.write(self, T, aligned_addr, value),
|
||||||
|
|
|
@ -4,41 +4,31 @@ const Bitfield = @import("bitfield").Bitfield;
|
||||||
const Bit = @import("bitfield").Bit;
|
const Bit = @import("bitfield").Bit;
|
||||||
|
|
||||||
const Bus = @import("Bus.zig");
|
const Bus = @import("Bus.zig");
|
||||||
const SharedCtx = @import("../emu.zig").SharedCtx;
|
const SharedIo = @import("../io.zig").Io;
|
||||||
|
const PostFlag = @import("../io.zig").PostFlag;
|
||||||
const masks = @import("../io.zig").masks;
|
const masks = @import("../io.zig").masks;
|
||||||
|
|
||||||
const IntEnable = @import("../io.zig").IntEnable;
|
|
||||||
const IntRequest = @import("../io.zig").IntEnable;
|
|
||||||
|
|
||||||
const log = std.log.scoped(.nds7_io);
|
const log = std.log.scoped(.nds7_io);
|
||||||
|
|
||||||
pub const Io = struct {
|
pub const Io = struct {
|
||||||
shr: *SharedCtx.Io,
|
shared: *SharedIo,
|
||||||
|
|
||||||
/// Interrupt Master Enable
|
// IME - Interrupt Master Enable
|
||||||
/// Read/Write
|
/// Read / Write
|
||||||
ime: bool = false,
|
ime: bool = false,
|
||||||
|
|
||||||
/// Interrupt Enable
|
/// IE - Interrupt Enable
|
||||||
/// Read/Write
|
/// Read / Write
|
||||||
///
|
ie: Interrupt = .{ .raw = 0x0000_0000 },
|
||||||
/// Caller must cast the `u32` to either `nds7.IntEnable` or `nds9.IntEnable`
|
|
||||||
ie: IntEnable = .{ .raw = 0x0000_0000 },
|
|
||||||
|
|
||||||
/// IF - Interrupt Request
|
/// IF - Interrupt Request
|
||||||
/// Read/Write
|
/// Read / Write
|
||||||
///
|
irq: Interrupt = .{ .raw = 0x0000_0000 },
|
||||||
/// Caller must cast the `u32` to either `nds7.IntRequest` or `nds9.IntRequest`
|
|
||||||
irq: IntRequest = .{ .raw = 0x0000_0000 },
|
|
||||||
|
|
||||||
/// Post Boot Flag
|
|
||||||
/// Read/Write
|
|
||||||
///
|
|
||||||
/// Caller must cast the `u8` to either `nds7.PostFlg` or `nds9.PostFlg`
|
|
||||||
postflg: PostFlag = .in_progress,
|
postflg: PostFlag = .in_progress,
|
||||||
|
|
||||||
pub fn init(io: *SharedCtx.Io) @This() {
|
pub fn init(io: *SharedIo) @This() {
|
||||||
return .{ .shr = io };
|
return .{ .shared = io };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -49,19 +39,17 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
|
||||||
0x0400_0210 => bus.io.ie.raw,
|
0x0400_0210 => bus.io.ie.raw,
|
||||||
0x0400_0214 => bus.io.irq.raw,
|
0x0400_0214 => bus.io.irq.raw,
|
||||||
|
|
||||||
0x0410_0000 => bus.io.shr.ipc.recv(.nds7),
|
0x0410_0000 => bus.io.shared.ipc_fifo.recv(.nds7),
|
||||||
else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
|
else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
|
||||||
},
|
},
|
||||||
u16 => switch (address) {
|
u16 => switch (address) {
|
||||||
0x0400_0180 => @truncate(bus.io.shr.ipc._nds7.sync.raw),
|
0x0400_0180 => @truncate(bus.io.shared.ipc_fifo._nds7.sync.raw),
|
||||||
0x0400_0184 => @truncate(bus.io.shr.ipc._nds7.cnt.raw),
|
0x0400_0184 => @truncate(bus.io.shared.ipc_fifo._nds7.cnt.raw),
|
||||||
else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
|
else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
|
||||||
},
|
},
|
||||||
u8 => switch (address) {
|
u8 => switch (address) {
|
||||||
0x0400_0240 => bus.vram.stat().raw,
|
0x0400_0240 => bus.vram.stat().raw,
|
||||||
0x0400_0241 => bus.io.shr.wramcnt.raw,
|
0x0400_0241 => bus.io.shared.wramcnt.raw,
|
||||||
|
|
||||||
0x0400_0300 => @intFromEnum(bus.io.postflg),
|
|
||||||
else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
|
else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
|
||||||
},
|
},
|
||||||
else => @compileError(T ++ " is an unsupported bus read type"),
|
else => @compileError(T ++ " is an unsupported bus read type"),
|
||||||
|
@ -73,18 +61,17 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
u32 => switch (address) {
|
u32 => switch (address) {
|
||||||
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
||||||
0x0400_0210 => bus.io.ie.raw = value,
|
0x0400_0210 => bus.io.ie.raw = value,
|
||||||
0x0400_0214 => bus.io.irq.raw &= ~value,
|
0x0400_0214 => bus.io.irq.raw = value,
|
||||||
|
|
||||||
0x0400_0188 => bus.io.shr.ipc.send(.nds7, value) catch |e| std.debug.panic("FIFO error: {}", .{e}),
|
0x0400_0188 => bus.io.shared.ipc_fifo.send(.nds7, value) catch |e| std.debug.panic("FIFO error: {}", .{e}),
|
||||||
else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
|
else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
|
||||||
},
|
},
|
||||||
u16 => switch (address) {
|
u16 => switch (address) {
|
||||||
0x0400_0180 => bus.io.shr.ipc.setIpcSync(.nds7, value),
|
0x0400_0180 => bus.io.shared.ipc_fifo.setIpcSync(.nds7, value),
|
||||||
0x0400_0184 => bus.io.shr.ipc.setIpcFifoCnt(.nds7, value),
|
0x0400_0184 => bus.io.shared.ipc_fifo.setIpcFifoCnt(.nds7, value),
|
||||||
else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
|
else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
|
||||||
},
|
},
|
||||||
u8 => switch (address) {
|
u8 => switch (address) {
|
||||||
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
|
||||||
else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
|
else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
|
||||||
},
|
},
|
||||||
else => @compileError(T ++ " is an unsupported bus write type"),
|
else => @compileError(T ++ " is an unsupported bus write type"),
|
||||||
|
@ -102,4 +89,6 @@ pub const Vramstat = extern union {
|
||||||
raw: u8,
|
raw: u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
const PostFlag = enum(u8) { in_progress = 0, completed };
|
const Interrupt = extern union {
|
||||||
|
raw: u32,
|
||||||
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@ const io = @import("io.zig");
|
||||||
|
|
||||||
const Ppu = @import("../ppu.zig").Ppu;
|
const Ppu = @import("../ppu.zig").Ppu;
|
||||||
const Scheduler = @import("../Scheduler.zig");
|
const Scheduler = @import("../Scheduler.zig");
|
||||||
const SharedCtx = @import("../emu.zig").SharedCtx;
|
const SharedContext = @import("../emu.zig").SharedContext;
|
||||||
const Wram = @import("../emu.zig").Wram;
|
const Wram = @import("../emu.zig").Wram;
|
||||||
const forceAlign = @import("../emu.zig").forceAlign;
|
const forceAlign = @import("../emu.zig").forceAlign;
|
||||||
|
|
||||||
|
@ -20,32 +20,23 @@ wram: *Wram,
|
||||||
io: io.Io,
|
io: io.Io,
|
||||||
ppu: Ppu,
|
ppu: Ppu,
|
||||||
|
|
||||||
bios: *[32 * KiB]u8,
|
|
||||||
|
|
||||||
scheduler: *Scheduler,
|
scheduler: *Scheduler,
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, scheduler: *Scheduler, ctx: SharedCtx) !@This() {
|
pub fn init(allocator: Allocator, scheduler: *Scheduler, shared_ctx: SharedContext) !@This() {
|
||||||
const dots_per_cycle = 3; // ARM946E-S runs twice as fast as the ARM7TDMI
|
const dots_per_cycle = 3; // ARM946E-S runs twice as fast as the ARM7TDMI
|
||||||
scheduler.push(.{ .nds9 = .draw }, 256 * dots_per_cycle);
|
scheduler.push(.{ .nds9 = .draw }, 256 * dots_per_cycle);
|
||||||
|
|
||||||
const bios = try allocator.create([32 * KiB]u8);
|
|
||||||
@memset(bios, 0);
|
|
||||||
errdefer allocator.destroy(bios);
|
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.main = ctx.main,
|
.main = shared_ctx.main,
|
||||||
.wram = ctx.wram,
|
.wram = shared_ctx.wram,
|
||||||
.ppu = try Ppu.init(allocator, ctx.vram),
|
.ppu = try Ppu.init(allocator, shared_ctx.vram),
|
||||||
.scheduler = scheduler,
|
.scheduler = scheduler,
|
||||||
.io = io.Io.init(ctx.io),
|
.io = io.Io.init(shared_ctx.io),
|
||||||
|
|
||||||
.bios = bios,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *@This(), allocator: Allocator) void {
|
pub fn deinit(self: *@This(), allocator: Allocator) void {
|
||||||
self.ppu.deinit(allocator);
|
self.ppu.deinit(allocator);
|
||||||
allocator.destroy(self.bios);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(_: *@This()) void {
|
pub fn reset(_: *@This()) void {
|
||||||
|
@ -77,7 +68,6 @@ fn _read(self: *@This(), comptime T: type, comptime mode: Mode, address: u32) T
|
||||||
0x0300_0000...0x03FF_FFFF => self.wram.read(T, .nds9, aligned_addr),
|
0x0300_0000...0x03FF_FFFF => self.wram.read(T, .nds9, aligned_addr),
|
||||||
0x0400_0000...0x04FF_FFFF => io.read(self, T, aligned_addr),
|
0x0400_0000...0x04FF_FFFF => io.read(self, T, aligned_addr),
|
||||||
0x0600_0000...0x06FF_FFFF => self.ppu.vram.read(T, .nds9, aligned_addr),
|
0x0600_0000...0x06FF_FFFF => self.ppu.vram.read(T, .nds9, aligned_addr),
|
||||||
0xFFFF_0000...0xFFFF_FFFF => readInt(T, self.bios[address & 0x0000_7FFF ..][0..byte_count]),
|
|
||||||
else => warn("unexpected read: 0x{x:0>8} -> {}", .{ aligned_addr, T }),
|
else => warn("unexpected read: 0x{x:0>8} -> {}", .{ aligned_addr, T }),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -107,7 +97,6 @@ fn _write(self: *@This(), comptime T: type, comptime mode: Mode, address: u32, v
|
||||||
0x0300_0000...0x03FF_FFFF => self.wram.write(T, .nds9, aligned_addr, value),
|
0x0300_0000...0x03FF_FFFF => self.wram.write(T, .nds9, aligned_addr, value),
|
||||||
0x0400_0000...0x04FF_FFFF => io.write(self, T, aligned_addr, value),
|
0x0400_0000...0x04FF_FFFF => io.write(self, T, aligned_addr, value),
|
||||||
0x0600_0000...0x06FF_FFFF => self.ppu.vram.write(T, .nds9, aligned_addr, value),
|
0x0600_0000...0x06FF_FFFF => self.ppu.vram.write(T, .nds9, aligned_addr, value),
|
||||||
0xFFFF_0000...0xFFFF_FFFF => log.err("tried to read from NDS9 BIOS: 0x{X:0>8}", .{aligned_addr}),
|
|
||||||
else => log.warn("unexpected write: 0x{X:}{} -> 0x{X:0>8}", .{ value, T, aligned_addr }),
|
else => log.warn("unexpected write: 0x{X:}{} -> 0x{X:0>8}", .{ value, T, aligned_addr }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,34 +4,32 @@ const Bitfield = @import("bitfield").Bitfield;
|
||||||
const Bit = @import("bitfield").Bit;
|
const Bit = @import("bitfield").Bit;
|
||||||
|
|
||||||
const Bus = @import("Bus.zig");
|
const Bus = @import("Bus.zig");
|
||||||
const SharedCtx = @import("../emu.zig").SharedCtx;
|
const SharedIo = @import("../io.zig").Io;
|
||||||
|
const PostFlag = @import("../io.zig").PostFlag;
|
||||||
const masks = @import("../io.zig").masks;
|
const masks = @import("../io.zig").masks;
|
||||||
|
|
||||||
const IntEnable = @import("../io.zig").IntEnable;
|
|
||||||
const IntRequest = @import("../io.zig").IntEnable;
|
|
||||||
|
|
||||||
const sext = @import("../../util.zig").sext;
|
const sext = @import("../../util.zig").sext;
|
||||||
|
|
||||||
const log = std.log.scoped(.nds9_io);
|
const log = std.log.scoped(.nds9_io);
|
||||||
|
|
||||||
pub const Io = struct {
|
pub const Io = struct {
|
||||||
shr: *SharedCtx.Io,
|
shared: *SharedIo,
|
||||||
|
|
||||||
/// Interrupt Master Enable
|
// IME - Interrupt Master Enable
|
||||||
/// Read/Write
|
/// Read / Write
|
||||||
ime: bool = false,
|
ime: bool = false,
|
||||||
|
|
||||||
/// Interrupt Enable
|
/// IE - Interrupt Enable
|
||||||
/// Read/Write
|
/// Read / Write
|
||||||
///
|
ie: Interrupt = .{ .raw = 0x0000_0000 },
|
||||||
/// Caller must cast the `u32` to either `nds7.IntEnable` or `nds9.IntEnable`
|
|
||||||
ie: IntEnable = .{ .raw = 0x0000_0000 },
|
|
||||||
|
|
||||||
/// IF - Interrupt Request
|
/// IF - Interrupt Request
|
||||||
/// Read/Write
|
/// Read / Write
|
||||||
///
|
irq: Interrupt = .{ .raw = 0x0000_0000 },
|
||||||
/// Caller must cast the `u32` to either `nds7.IntRequest` or `nds9.IntRequest`
|
|
||||||
irq: IntRequest = .{ .raw = 0x0000_0000 },
|
// TODO: move postflg
|
||||||
|
|
||||||
|
postflg: PostFlag = .in_progress,
|
||||||
|
|
||||||
/// POWCNT1 - Graphics Power Control
|
/// POWCNT1 - Graphics Power Control
|
||||||
/// Read / Write
|
/// Read / Write
|
||||||
|
@ -44,8 +42,8 @@ pub const Io = struct {
|
||||||
div: Divisor = .{},
|
div: Divisor = .{},
|
||||||
sqrt: SquareRootUnit = .{},
|
sqrt: SquareRootUnit = .{},
|
||||||
|
|
||||||
pub fn init(io: *SharedCtx.Io) @This() {
|
pub fn init(io: *SharedIo) @This() {
|
||||||
return .{ .shr = io };
|
return .{ .shared = io };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -62,15 +60,15 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
|
||||||
0x0400_02AC => @truncate(bus.io.div.remainder >> 32),
|
0x0400_02AC => @truncate(bus.io.div.remainder >> 32),
|
||||||
0x0400_02B4 => @truncate(bus.io.sqrt.result),
|
0x0400_02B4 => @truncate(bus.io.sqrt.result),
|
||||||
|
|
||||||
0x0410_0000 => bus.io.shr.ipc.recv(.nds9),
|
0x0410_0000 => bus.io.shared.ipc_fifo.recv(.nds9),
|
||||||
else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
|
else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
|
||||||
},
|
},
|
||||||
u16 => switch (address) {
|
u16 => switch (address) {
|
||||||
0x0400_0004 => bus.ppu.io.dispstat.raw,
|
0x0400_0004 => bus.ppu.io.dispstat.raw,
|
||||||
0x0400_0130 => bus.io.keyinput.load(.Monotonic),
|
0x0400_0130 => bus.io.keyinput.load(.Monotonic),
|
||||||
|
|
||||||
0x0400_0180 => @truncate(bus.io.shr.ipc._nds9.sync.raw),
|
0x0400_0180 => @truncate(bus.io.shared.ipc_fifo._nds9.sync.raw),
|
||||||
0x0400_0184 => @truncate(bus.io.shr.ipc._nds9.cnt.raw),
|
0x0400_0184 => @truncate(bus.io.shared.ipc_fifo._nds9.cnt.raw),
|
||||||
|
|
||||||
0x0400_0280 => @truncate(bus.io.div.cnt.raw),
|
0x0400_0280 => @truncate(bus.io.div.cnt.raw),
|
||||||
0x0400_02B0 => @truncate(bus.io.sqrt.cnt.raw),
|
0x0400_02B0 => @truncate(bus.io.sqrt.cnt.raw),
|
||||||
|
@ -88,9 +86,9 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
switch (T) {
|
switch (T) {
|
||||||
u32 => switch (address) {
|
u32 => switch (address) {
|
||||||
0x0400_0000 => bus.ppu.io.dispcnt_a.raw = value,
|
0x0400_0000 => bus.ppu.io.dispcnt_a.raw = value,
|
||||||
0x0400_0180 => bus.io.shr.ipc.setIpcSync(.nds9, value),
|
0x0400_0180 => bus.io.shared.ipc_fifo.setIpcSync(.nds9, value),
|
||||||
0x0400_0184 => bus.io.shr.ipc.setIpcFifoCnt(.nds9, value),
|
0x0400_0184 => bus.io.shared.ipc_fifo.setIpcFifoCnt(.nds9, value),
|
||||||
0x0400_0188 => bus.io.shr.ipc.send(.nds9, value) catch |e| std.debug.panic("IPC FIFO Error: {}", .{e}),
|
0x0400_0188 => bus.io.shared.ipc_fifo.send(.nds9, value) catch |e| std.debug.panic("IPC FIFO Error: {}", .{e}),
|
||||||
|
|
||||||
0x0400_0240 => {
|
0x0400_0240 => {
|
||||||
bus.ppu.vram.io.cnt_a.raw = @truncate(value >> 0); // 0x0400_0240
|
bus.ppu.vram.io.cnt_a.raw = @truncate(value >> 0); // 0x0400_0240
|
||||||
|
@ -101,7 +99,7 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
|
|
||||||
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
||||||
0x0400_0210 => bus.io.ie.raw = value,
|
0x0400_0210 => bus.io.ie.raw = value,
|
||||||
0x0400_0214 => bus.io.irq.raw &= ~value,
|
0x0400_0214 => bus.io.irq.raw = value,
|
||||||
|
|
||||||
0x0400_0290 => {
|
0x0400_0290 => {
|
||||||
bus.io.div.numerator = masks.mask(bus.io.div.numerator, value, 0xFFFF_FFFF);
|
bus.io.div.numerator = masks.mask(bus.io.div.numerator, value, 0xFFFF_FFFF);
|
||||||
|
@ -133,8 +131,8 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
|
else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
|
||||||
},
|
},
|
||||||
u16 => switch (address) {
|
u16 => switch (address) {
|
||||||
0x0400_0180 => bus.io.shr.ipc.setIpcSync(.nds9, value),
|
0x0400_0180 => bus.io.shared.ipc_fifo.setIpcSync(.nds9, value),
|
||||||
0x0400_0184 => bus.io.shr.ipc.setIpcFifoCnt(.nds9, value),
|
0x0400_0184 => bus.io.shared.ipc_fifo.setIpcFifoCnt(.nds9, value),
|
||||||
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
0x0400_0208 => bus.io.ime = value & 1 == 1,
|
||||||
|
|
||||||
0x0400_0280 => {
|
0x0400_0280 => {
|
||||||
|
@ -179,8 +177,8 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
bus.ppu.vram.update();
|
bus.ppu.vram.update();
|
||||||
},
|
},
|
||||||
0x0400_0247 => {
|
0x0400_0247 => {
|
||||||
bus.io.shr.wramcnt.raw = value;
|
bus.io.shared.wramcnt.raw = value;
|
||||||
bus.wram.update(bus.io.shr.wramcnt);
|
bus.wram.update(bus.io.shared.wramcnt);
|
||||||
},
|
},
|
||||||
0x0400_0248 => {
|
0x0400_0248 => {
|
||||||
bus.ppu.vram.io.cnt_h.raw = value;
|
bus.ppu.vram.io.cnt_h.raw = value;
|
||||||
|
@ -456,3 +454,7 @@ const AtomicKeyInput = struct {
|
||||||
_ = @atomicRmw(u16, &self.inner.raw, .And, value, ordering);
|
_ = @atomicRmw(u16, &self.inner.raw, .And, value, ordering);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Interrupt = extern union {
|
||||||
|
raw: u32,
|
||||||
|
};
|
||||||
|
|
103
src/core/ppu.zig
103
src/core/ppu.zig
|
@ -243,6 +243,7 @@ pub const Vram = struct {
|
||||||
return .{ .raw = (vram_d << 1) | vram_c };
|
return .{ .raw = (vram_d << 1) | vram_c };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Range = struct { min: u32, max: u32 };
|
||||||
const Kind = enum {
|
const Kind = enum {
|
||||||
a,
|
a,
|
||||||
b,
|
b,
|
||||||
|
@ -270,77 +271,113 @@ pub const Vram = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Rename
|
/// max inclusive
|
||||||
fn range(comptime kind: Kind, mst: u3, offset: u2) u32 {
|
fn range(comptime kind: Kind, mst: u3, offset: u2) Range {
|
||||||
const ofs: u32 = offset;
|
const ofs: u32 = offset;
|
||||||
// panic messages are from GBATEK
|
// panic messages are from GBATEK
|
||||||
|
|
||||||
return switch (kind) {
|
return switch (kind) {
|
||||||
.a => switch (mst) {
|
.a => switch (mst) {
|
||||||
0 => 0x0680_0000,
|
0 => .{ .min = 0x0680_0000, .max = 0x0682_0000 },
|
||||||
1 => 0x0600_0000 + (0x0002_0000 * ofs),
|
1 => blk: {
|
||||||
2 => 0x0640_0000 + (0x0002_0000 * (ofs & 0b01)),
|
const base = 0x0600_0000 + (0x0002_0000 * ofs);
|
||||||
|
break :blk .{ .min = base, .max = base + kind.size() };
|
||||||
|
},
|
||||||
|
2 => blk: {
|
||||||
|
const base = 0x0640_0000 + (0x0002_0000 * (ofs & 0b01));
|
||||||
|
break :blk .{ .min = base, .max = base + kind.size() };
|
||||||
|
},
|
||||||
3 => @panic("VRAMCNT_A: Slot OFS(0-3)"),
|
3 => @panic("VRAMCNT_A: Slot OFS(0-3)"),
|
||||||
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
||||||
},
|
},
|
||||||
.b => switch (mst) {
|
.b => switch (mst) {
|
||||||
0 => 0x0682_0000,
|
0 => .{ .min = 0x0682_0000, .max = 0x0684_0000 },
|
||||||
1 => 0x0600_0000 + (0x0002_0000 * ofs),
|
1 => blk: {
|
||||||
2 => 0x0640_0000 + (0x0002_0000 * (ofs & 0b01)),
|
const base = 0x0600_0000 + (0x0002_0000 * ofs);
|
||||||
|
break :blk .{ .min = base, .max = base + kind.size() };
|
||||||
|
},
|
||||||
|
2 => blk: {
|
||||||
|
const base = 0x0640_0000 + (0x0002_0000 * (ofs & 0b01));
|
||||||
|
break :blk .{ .min = base, .max = base + kind.size() };
|
||||||
|
},
|
||||||
3 => @panic("VRAMCNT_B: Slot OFS(0-3)"),
|
3 => @panic("VRAMCNT_B: Slot OFS(0-3)"),
|
||||||
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
||||||
},
|
},
|
||||||
.c => switch (mst) {
|
.c => switch (mst) {
|
||||||
0 => 0x0684_0000,
|
0 => .{ .min = 0x0684_0000, .max = 0x0686_0000 },
|
||||||
1 => 0x0600_0000 + (0x0002_0000 * ofs),
|
1 => blk: {
|
||||||
2 => 0x0600_0000 + (0x0002_0000 * (ofs & 0b01)),
|
const base = 0x0600_0000 + (0x0002_0000 * ofs);
|
||||||
|
break :blk .{ .min = base, .max = base + kind.size() };
|
||||||
|
},
|
||||||
|
2 => blk: {
|
||||||
|
const base = 0x0600_0000 + (0x0002_0000 * (ofs & 0b01));
|
||||||
|
break :blk .{ .min = base, .max = base + kind.size() };
|
||||||
|
},
|
||||||
3 => @panic("VRAMCNT_C: Slot OFS(0-3)"),
|
3 => @panic("VRAMCNT_C: Slot OFS(0-3)"),
|
||||||
4 => 0x0620_0000,
|
4 => .{ .min = 0x0620_0000, .max = 0x0620_0000 + kind.size() },
|
||||||
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
||||||
},
|
},
|
||||||
.d => switch (mst) {
|
.d => switch (mst) {
|
||||||
0 => 0x0686_0000,
|
0 => .{ .min = 0x0686_0000, .max = 0x0688_0000 },
|
||||||
1 => 0x0600_0000 + (0x0002_0000 * ofs),
|
1 => blk: {
|
||||||
2 => 0x0600_0000 + (0x0002_0000 * (ofs & 0b01)),
|
const base = 0x0600_0000 + (0x0002_0000 * ofs);
|
||||||
|
break :blk .{ .min = base, .max = base + kind.size() };
|
||||||
|
},
|
||||||
|
2 => blk: {
|
||||||
|
const base = 0x0600_0000 + (0x0002_0000 * (ofs & 0b01));
|
||||||
|
break :blk .{ .min = base, .max = base + kind.size() };
|
||||||
|
},
|
||||||
3 => @panic("VRAMCNT_D: Slot OFS(0-3)"),
|
3 => @panic("VRAMCNT_D: Slot OFS(0-3)"),
|
||||||
4 => 0x0660_0000,
|
4 => .{ .min = 0x0660_0000, .max = 0x0660_0000 + kind.size() },
|
||||||
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
||||||
},
|
},
|
||||||
.e => switch (mst) {
|
.e => switch (mst) {
|
||||||
0 => 0x0688_0000,
|
0 => .{ .min = 0x0688_0000, .max = 0x0689_0000 },
|
||||||
1 => 0x0600_0000,
|
1 => .{ .min = 0x0600_0000, .max = 0x0600_0000 + kind.size() },
|
||||||
2 => 0x0640_0000,
|
2 => .{ .min = 0x0640_0000, .max = 0x0640_0000 + kind.size() },
|
||||||
3 => @panic("VRAMCNT_E: Slots 0-3"),
|
3 => @panic("VRAMCNT_E: Slots 0-3"),
|
||||||
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
||||||
},
|
},
|
||||||
.f => switch (mst) {
|
.f => switch (mst) {
|
||||||
0 => 0x0689_0000,
|
0 => .{ .min = 0x0689_0000, .max = 0x0689_4000 },
|
||||||
1 => 0x0600_0000 + (0x0000_4000 * (ofs & 0b01)) + (0x0001_0000 * (ofs >> 1)),
|
1 => blk: {
|
||||||
2 => 0x0640_0000 + (0x0000_4000 * (ofs & 0b01)) + (0x0001_0000 * (ofs >> 1)),
|
const base = 0x0600_0000 + (0x0000_4000 * (ofs & 0b01)) + (0x0001_0000 * (ofs >> 1));
|
||||||
|
break :blk .{ .min = base, .max = base + kind.size() };
|
||||||
|
},
|
||||||
|
2 => blk: {
|
||||||
|
const base = 0x0640_0000 + (0x0000_4000 * (ofs & 0b01)) + (0x0001_0000 * (ofs >> 1));
|
||||||
|
break :blk .{ .min = base, .max = base + kind.size() };
|
||||||
|
},
|
||||||
3 => @panic("VRAMCNT_F: Slot (OFS.0*1)+(OFS.1*4)"),
|
3 => @panic("VRAMCNT_F: Slot (OFS.0*1)+(OFS.1*4)"),
|
||||||
4 => @panic("VRAMCNT_F: Slot 0-1 (OFS=0), Slot 2-3 (OFS=1)"),
|
4 => @panic("VRAMCNT_F: Slot 0-1 (OFS=0), Slot 2-3 (OFS=1)"),
|
||||||
5 => @panic("VRAMCNT_F: Slot 0"),
|
5 => @panic("VRAMCNT_F: Slot 0"),
|
||||||
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
||||||
},
|
},
|
||||||
.g => switch (mst) {
|
.g => switch (mst) {
|
||||||
0 => 0x0689_4000,
|
0 => .{ .min = 0x0689_4000, .max = 0x0689_8000 },
|
||||||
1 => 0x0600_0000 + (0x0000_4000 * (ofs & 0b01)) + (0x0001_0000 * (ofs >> 1)),
|
1 => blk: {
|
||||||
2 => 0x0640_0000 + (0x0000_4000 * (ofs & 0b01)) + (0x0001_0000 * (ofs >> 1)),
|
const base = 0x0600_0000 + (0x0000_4000 * (ofs & 0b01)) + (0x0001_0000 * (ofs >> 1));
|
||||||
|
break :blk .{ .min = base, .max = base + kind.size() };
|
||||||
|
},
|
||||||
|
2 => blk: {
|
||||||
|
const base = 0x0640_0000 + (0x0000_4000 * (ofs & 0b01)) + (0x0001_0000 * (ofs >> 1));
|
||||||
|
break :blk .{ .min = base, .max = base + kind.size() };
|
||||||
|
},
|
||||||
3 => @panic("VRAMCNT_G: Slot (OFS.0*1)+(OFS.1*4)"),
|
3 => @panic("VRAMCNT_G: Slot (OFS.0*1)+(OFS.1*4)"),
|
||||||
4 => @panic("VRAMCNT_G: Slot 0-1 (OFS=0), Slot 2-3 (OFS=1)"),
|
4 => @panic("VRAMCNT_G: Slot 0-1 (OFS=0), Slot 2-3 (OFS=1)"),
|
||||||
5 => @panic("VRAMCNT_G: Slot 0"),
|
5 => @panic("VRAMCNT_G: Slot 0"),
|
||||||
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
||||||
},
|
},
|
||||||
.h => switch (mst) {
|
.h => switch (mst) {
|
||||||
0 => 0x0689_8000,
|
0 => .{ .min = 0x0689_8000, .max = 0x068A_0000 },
|
||||||
1 => 0x0620_0000,
|
1 => .{ .min = 0x0620_0000, .max = 0x0620_0000 + kind.size() },
|
||||||
2 => @panic("VRAMCNT_H: Slot 0-3"),
|
2 => @panic("VRAMCNT_H: Slot 0-3"),
|
||||||
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
||||||
},
|
},
|
||||||
.i => switch (mst) {
|
.i => switch (mst) {
|
||||||
0 => 0x068A_0000,
|
0 => .{ .min = 0x068A_0000, .max = 0x068A_4000 },
|
||||||
1 => 0x0620_8000,
|
1 => .{ .min = 0x0620_8000, .max = 0x0620_8000 + kind.size() },
|
||||||
2 => 0x0660_0000,
|
2 => .{ .min = 0x0660_0000, .max = 0x0660_0000 + kind.size() },
|
||||||
3 => @panic("Slot 0"),
|
3 => @panic("Slot 0"),
|
||||||
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
else => std.debug.panic("Invalid MST for VRAMCNT_{s}", .{[_]u8{std.ascii.toUpper(@tagName(kind)[0])}}),
|
||||||
},
|
},
|
||||||
|
@ -393,7 +430,6 @@ pub const Vram = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We always update the entirety of VRAM when that argubably isn't necessary
|
|
||||||
pub fn update(self: *@This()) void {
|
pub fn update(self: *@This()) void {
|
||||||
const nds9_tbl = @constCast(self.nds9_table);
|
const nds9_tbl = @constCast(self.nds9_table);
|
||||||
const nds7_tbl = @constCast(self.nds7_table);
|
const nds7_tbl = @constCast(self.nds7_table);
|
||||||
|
@ -409,11 +445,10 @@ pub const Vram = struct {
|
||||||
else => cnt.offset.read(),
|
else => cnt.offset.read(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const min = range(kind, cnt.mst.read(), ofs);
|
const rnge = range(kind, cnt.mst.read(), ofs);
|
||||||
const max = min + kind.size();
|
|
||||||
const offset = addr & (kind.size() - 1);
|
const offset = addr & (kind.size() - 1);
|
||||||
|
|
||||||
if (min <= addr and addr < max) {
|
if (rnge.min <= addr and addr < rnge.max) {
|
||||||
if ((kind == .c or kind == .d) and cnt.mst.read() == 2) {
|
if ((kind == .c or kind == .d) and cnt.mst.read() == 2) {
|
||||||
// Allocate to ARM7
|
// Allocate to ARM7
|
||||||
nds7_ptr.* = self._buf[buf_offset(kind) + offset ..].ptr;
|
nds7_ptr.* = self._buf[buf_offset(kind) + offset ..].ptr;
|
||||||
|
|
20
src/main.zig
20
src/main.zig
|
@ -4,7 +4,7 @@ const clap = @import("zig-clap");
|
||||||
const emu = @import("core/emu.zig");
|
const emu = @import("core/emu.zig");
|
||||||
|
|
||||||
const Ui = @import("platform.zig").Ui;
|
const Ui = @import("platform.zig").Ui;
|
||||||
const SharedCtx = @import("core/emu.zig").SharedCtx;
|
const SharedContext = @import("core/emu.zig").SharedContext;
|
||||||
const System = @import("core/emu.zig").System;
|
const System = @import("core/emu.zig").System;
|
||||||
const Scheduler = @import("core/Scheduler.zig");
|
const Scheduler = @import("core/Scheduler.zig");
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ const ClapResult = clap.Result(clap.Help, &cli_params, clap.parsers.default);
|
||||||
|
|
||||||
const cli_params = clap.parseParamsComptime(
|
const cli_params = clap.parseParamsComptime(
|
||||||
\\-h, --help Display this help and exit.
|
\\-h, --help Display this help and exit.
|
||||||
\\-f, --firm <str> Path to NDS Firmware Directory
|
|
||||||
\\<str> Path to the NDS ROM
|
\\<str> Path to the NDS ROM
|
||||||
\\
|
\\
|
||||||
);
|
);
|
||||||
|
@ -32,11 +31,11 @@ pub fn main() !void {
|
||||||
const rom_path = try handlePositional(result);
|
const rom_path = try handlePositional(result);
|
||||||
log.debug("loading rom from: {s}", .{rom_path});
|
log.debug("loading rom from: {s}", .{rom_path});
|
||||||
|
|
||||||
const firm_path = result.args.firm;
|
const rom_file = try std.fs.cwd().openFile(rom_path, .{});
|
||||||
log.debug("loading firmware from from: {?s}", .{firm_path});
|
defer rom_file.close();
|
||||||
|
|
||||||
var ctx = try SharedCtx.init(allocator);
|
const shared_ctx = try SharedContext.init(allocator);
|
||||||
defer ctx.deinit(allocator);
|
defer shared_ctx.deinit(allocator);
|
||||||
|
|
||||||
var scheduler = try Scheduler.init(allocator);
|
var scheduler = try Scheduler.init(allocator);
|
||||||
defer scheduler.deinit();
|
defer scheduler.deinit();
|
||||||
|
@ -48,8 +47,8 @@ pub fn main() !void {
|
||||||
|
|
||||||
var cp15 = System.Cp15{};
|
var cp15 = System.Cp15{};
|
||||||
|
|
||||||
var bus7 = try System.Bus7.init(allocator, &scheduler, ctx);
|
var bus7 = try System.Bus7.init(allocator, &scheduler, shared_ctx);
|
||||||
var bus9 = try System.Bus9.init(allocator, &scheduler, ctx);
|
var bus9 = try System.Bus9.init(allocator, &scheduler, shared_ctx);
|
||||||
|
|
||||||
var arm7tdmi = System.Arm7tdmi.init(IScheduler.init(&scheduler), IBus.init(&bus7));
|
var arm7tdmi = System.Arm7tdmi.init(IScheduler.init(&scheduler), IBus.init(&bus7));
|
||||||
var arm946es = System.Arm946es.init(IScheduler.init(&scheduler), IBus.init(&bus9), ICoprocessor.init(&cp15));
|
var arm946es = System.Arm946es.init(IScheduler.init(&scheduler), IBus.init(&bus9), ICoprocessor.init(&cp15));
|
||||||
|
@ -57,10 +56,7 @@ pub fn main() !void {
|
||||||
break :blk .{ .arm7tdmi = &arm7tdmi, .arm946es = &arm946es, .bus7 = &bus7, .bus9 = &bus9, .cp15 = &cp15 };
|
break :blk .{ .arm7tdmi = &arm7tdmi, .arm946es = &arm946es, .bus7 = &bus7, .bus9 = &bus9, .cp15 = &cp15 };
|
||||||
};
|
};
|
||||||
defer system.deinit(allocator);
|
defer system.deinit(allocator);
|
||||||
|
const rom_title = try emu.load(allocator, system, rom_file);
|
||||||
ctx.io.ipc.configure(system); // Shared I/O needs access to both CPUs (e.g. IPCSYNC)
|
|
||||||
const rom_title = try emu.load(allocator, system, rom_path);
|
|
||||||
if (firm_path) |path| try emu.loadFirm(allocator, system, path);
|
|
||||||
|
|
||||||
var ui = try Ui.init(allocator);
|
var ui = try Ui.init(allocator);
|
||||||
defer ui.deinit(allocator);
|
defer ui.deinit(allocator);
|
||||||
|
|
Loading…
Reference in New Issue