Compare commits
2 Commits
77ce083a04
...
e69bbde0e8
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | e69bbde0e8 | |
Rekai Nyangadzayi Musuka | eba2de39fb |
|
@ -92,14 +92,17 @@ 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 SharedContext = struct {
|
pub const SharedContext = struct {
|
||||||
const MiB = 0x100000;
|
const MiB = 0x100000;
|
||||||
|
const KiB = 0x400;
|
||||||
|
|
||||||
io: *SharedIo,
|
io: *SharedIo,
|
||||||
main: *[4 * MiB]u8,
|
main: *[4 * MiB]u8,
|
||||||
|
wram: *[32 * KiB]u8,
|
||||||
|
|
||||||
pub fn init(allocator: Allocator) !@This() {
|
pub fn init(allocator: Allocator) !@This() {
|
||||||
const ctx = .{
|
const ctx = .{
|
||||||
.io = try allocator.create(SharedIo),
|
.io = try allocator.create(SharedIo),
|
||||||
.main = try allocator.create([4 * MiB]u8),
|
.main = try allocator.create([4 * MiB]u8),
|
||||||
|
.wram = try allocator.create([32 * KiB]u8),
|
||||||
};
|
};
|
||||||
ctx.io.* = .{};
|
ctx.io.* = .{};
|
||||||
|
|
||||||
|
@ -109,6 +112,7 @@ pub const SharedContext = struct {
|
||||||
pub fn deinit(self: @This(), allocator: Allocator) void {
|
pub fn deinit(self: @This(), allocator: Allocator) void {
|
||||||
allocator.destroy(self.io);
|
allocator.destroy(self.io);
|
||||||
allocator.destroy(self.main);
|
allocator.destroy(self.main);
|
||||||
|
allocator.destroy(self.wram);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
214
src/core/io.zig
214
src/core/io.zig
|
@ -5,6 +5,10 @@ const Bit = @import("bitfield").Bit;
|
||||||
|
|
||||||
const log = std.log.scoped(.shared_io);
|
const log = std.log.scoped(.shared_io);
|
||||||
|
|
||||||
|
// FIXME: This whole thing is bad bad bad bad bad
|
||||||
|
// I think only the IPC stuff needs to be here, since they talk to each other.
|
||||||
|
// every other "shared I/O register" is just duplicated on both CPUs. So they shouldn't be here
|
||||||
|
|
||||||
pub const Io = struct {
|
pub const Io = struct {
|
||||||
/// Interrupt Master Enable
|
/// Interrupt Master Enable
|
||||||
/// Read/Write
|
/// Read/Write
|
||||||
|
@ -39,120 +43,130 @@ fn warn(comptime format: []const u8, args: anytype) u0 {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Please Rename
|
|
||||||
// TODO: Figure out a way to apply masks while calling valueAtAddressOffset
|
|
||||||
// TODO: These aren't optimized well. Can we improve that?
|
|
||||||
pub inline fn valueAtAddressOffset(comptime T: type, address: u32, value: T) u8 {
|
|
||||||
const L2I = std.math.Log2Int(T);
|
|
||||||
|
|
||||||
return @truncate(switch (T) {
|
|
||||||
u16 => value >> @as(L2I, @truncate((address & 1) << 3)),
|
|
||||||
u32 => value >> @as(L2I, @truncate((address & 3) << 3)),
|
|
||||||
else => @compileError("unsupported for " ++ @typeName(T) ++ "values"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn WriteOption(comptime T: type) type {
|
|
||||||
return struct { mask: ?T = null };
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: also please rename
|
|
||||||
// TODO: Figure out a way to apply masks while calling writeToAddressOffset
|
|
||||||
// TODO: These aren't optimized well. Can we improve that?
|
|
||||||
pub inline fn writeToAddressOffset(
|
|
||||||
register: anytype,
|
|
||||||
address: u32,
|
|
||||||
value: anytype,
|
|
||||||
// mask: WriteOption(@typeInfo(@TypeOf(register)).Pointer.child),
|
|
||||||
) void {
|
|
||||||
const Ptr = @TypeOf(register);
|
|
||||||
const ChildT = @typeInfo(Ptr).Pointer.child;
|
|
||||||
const ValueT = @TypeOf(value);
|
|
||||||
|
|
||||||
const left = register.*;
|
|
||||||
|
|
||||||
register.* = switch (ChildT) {
|
|
||||||
u32 => switch (ValueT) {
|
|
||||||
u16 => blk: {
|
|
||||||
// TODO: This probably gets deleted
|
|
||||||
const offset: u1 = @truncate(address >> 1);
|
|
||||||
|
|
||||||
break :blk switch (offset) {
|
|
||||||
0b0 => (left & 0xFFFF_0000) | value,
|
|
||||||
0b1 => (left & 0x0000_FFFF) | @as(u32, value) << 16,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
u8 => blk: {
|
|
||||||
// TODO: Remove branching
|
|
||||||
const offset: u2 = @truncate(address);
|
|
||||||
|
|
||||||
break :blk switch (offset) {
|
|
||||||
0b00 => (left & 0xFFFF_FF00) | value,
|
|
||||||
0b01 => (left & 0xFFFF_00FF) | @as(u32, value) << 8,
|
|
||||||
0b10 => (left & 0xFF00_FFFF) | @as(u32, value) << 16,
|
|
||||||
0b11 => (left & 0x00FF_FFFF) | @as(u32, value) << 24,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
else => @compileError("for " ++ @typeName(Ptr) ++ ", T must be u16 or u8"),
|
|
||||||
},
|
|
||||||
u16 => blk: {
|
|
||||||
if (ValueT != u8) @compileError("for " ++ @typeName(Ptr) ++ ", T must be u8");
|
|
||||||
|
|
||||||
const shamt = @as(u4, @truncate(address & 1)) << 3;
|
|
||||||
const mask: u16 = 0xFF00 >> shamt;
|
|
||||||
const value_shifted = @as(u16, value) << shamt;
|
|
||||||
|
|
||||||
break :blk (left & mask) | value_shifted;
|
|
||||||
},
|
|
||||||
else => @compileError("unsupported for " ++ @typeName(Ptr) ++ " values"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const IpcFifo = struct {
|
const IpcFifo = struct {
|
||||||
const Sync = IpcSync;
|
const Sync = IpcSync;
|
||||||
const Control = IpcFifoCnt;
|
const Control = IpcFifoCnt;
|
||||||
|
|
||||||
|
_nds7: Impl = .{},
|
||||||
|
_nds9: Impl = .{},
|
||||||
|
|
||||||
|
const Source = enum { nds7, nds9 };
|
||||||
|
|
||||||
|
const Impl = struct {
|
||||||
/// IPC Synchronize
|
/// IPC Synchronize
|
||||||
/// Read/Write
|
/// Read/Write
|
||||||
sync: Sync = .{ .raw = 0x0000_0000 },
|
sync: Sync = .{ .raw = 0x0000_0000 },
|
||||||
|
|
||||||
/// IPC Fifo Control
|
/// IPC Fifo Control
|
||||||
/// Read/Write
|
/// Read/Write
|
||||||
cnt: Control = .{ .raw = 0x0000_0000 },
|
cnt: Control = .{ .raw = 0x0000_0101 },
|
||||||
|
|
||||||
fifo: [2]Fifo = .{ Fifo{}, Fifo{} },
|
fifo: Fifo = Fifo{},
|
||||||
|
|
||||||
const Source = enum { arm7, arm9 };
|
/// Latch containing thel last read value from a FIFO
|
||||||
|
last_read: ?u32 = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// IPCSYNC
|
||||||
|
/// Read/Write
|
||||||
|
pub fn setIpcSync(self: *@This(), comptime src: Source, value: anytype) void {
|
||||||
|
switch (src) {
|
||||||
|
.nds7 => {
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
.nds9 => {
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// IPCFIFOCNT
|
||||||
|
/// Read/Write
|
||||||
|
pub fn setIpcFifoCnt(self: *@This(), comptime src: Source, value: anytype) void {
|
||||||
|
switch (src) {
|
||||||
|
.nds7 => self._nds7.cnt.raw = masks.ipcFifoCnt(self._nds7.cnt.raw, value),
|
||||||
|
.nds9 => self._nds9.cnt.raw = masks.ipcFifoCnt(self._nds9.cnt.raw, value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// IPC Send FIFO
|
/// IPC Send FIFO
|
||||||
/// Write-Only
|
/// Write-Only
|
||||||
pub fn send(self: *@This(), comptime src: Source, value: u32) !void {
|
pub fn send(self: *@This(), comptime src: Source, value: u32) !void {
|
||||||
const idx = switch (src) {
|
switch (src) {
|
||||||
.arm7 => 0,
|
.nds7 => {
|
||||||
.arm9 => 1,
|
if (!self._nds7.cnt.enable_fifos.read()) return;
|
||||||
};
|
try self._nds7.fifo.push(value);
|
||||||
|
|
||||||
if (!self.cnt.enable_fifos.read()) return;
|
// update status bits
|
||||||
|
self._nds7.cnt.send_fifo_empty.write(self._nds7.fifo._len() == 0);
|
||||||
|
self._nds9.cnt.recv_fifo_empty.write(self._nds7.fifo._len() == 0);
|
||||||
|
|
||||||
try self.fifo[idx].push(value);
|
self._nds7.cnt.send_fifo_full.write(self._nds7.fifo._len() == 0x10);
|
||||||
|
self._nds9.cnt.recv_fifo_full.write(self._nds7.fifo._len() == 0x10);
|
||||||
|
},
|
||||||
|
.nds9 => {
|
||||||
|
if (!self._nds9.cnt.enable_fifos.read()) return;
|
||||||
|
try self._nds9.fifo.push(value);
|
||||||
|
|
||||||
|
// update status bits
|
||||||
|
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._nds9.cnt.send_fifo_full.write(self._nds9.fifo._len() == 0x10);
|
||||||
|
self._nds7.cnt.recv_fifo_full.write(self._nds9.fifo._len() == 0x10);
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// IPC Receive FIFO
|
/// IPC Receive FIFO
|
||||||
/// Read-Only
|
/// Read-Only
|
||||||
pub fn recv(self: *@This(), comptime src: Source) u32 {
|
pub fn recv(self: *@This(), comptime src: Source) u32 {
|
||||||
const idx = switch (src) {
|
switch (src) {
|
||||||
.arm7 => 1, // switched around on purpose
|
.nds7 => {
|
||||||
.arm9 => 0,
|
const enabled = self._nds7.cnt.enable_fifos.read();
|
||||||
|
const val_opt = if (enabled) self._nds9.fifo.pop() else self._nds9.fifo.peek();
|
||||||
|
|
||||||
|
const value = if (val_opt) |val| blk: {
|
||||||
|
self._nds9.last_read = val;
|
||||||
|
break :blk val;
|
||||||
|
} else blk: {
|
||||||
|
self._nds7.cnt.fifo_error.set();
|
||||||
|
break :blk self._nds7.last_read orelse 0x0000_0000;
|
||||||
};
|
};
|
||||||
|
|
||||||
const enabled = self.cnt.enable_fifos.read();
|
// update status bits
|
||||||
const val_opt = if (enabled) self.fifo[idx].pop() else self.fifo[idx].peek();
|
self._nds7.cnt.recv_fifo_empty.write(self._nds9.fifo._len() == 0);
|
||||||
|
self._nds9.cnt.send_fifo_empty.write(self._nds9.fifo._len() == 0);
|
||||||
|
|
||||||
return val_opt orelse blk: {
|
self._nds7.cnt.recv_fifo_full.write(self._nds9.fifo._len() == 0x10);
|
||||||
self.cnt.send_fifo_empty.set();
|
self._nds9.cnt.send_fifo_full.write(self._nds9.fifo._len() == 0x10);
|
||||||
|
|
||||||
break :blk 0x0000_0000;
|
return value;
|
||||||
|
},
|
||||||
|
.nds9 => {
|
||||||
|
const enabled = self._nds9.cnt.enable_fifos.read();
|
||||||
|
const val_opt = if (enabled) self._nds7.fifo.pop() else self._nds7.fifo.peek();
|
||||||
|
|
||||||
|
const value = if (val_opt) |val| blk: {
|
||||||
|
self._nds7.last_read = val;
|
||||||
|
break :blk val;
|
||||||
|
} else blk: {
|
||||||
|
self._nds9.cnt.fifo_error.set();
|
||||||
|
break :blk self._nds7.last_read orelse 0x0000_0000;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// update status bits
|
||||||
|
self._nds9.cnt.recv_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_full.write(self._nds7.fifo._len() == 0x10);
|
||||||
|
self._nds7.cnt.send_fifo_full.write(self._nds7.fifo._len() == 0x10);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -208,32 +222,26 @@ pub const masks = struct {
|
||||||
const Bus9 = @import("nds9/Bus.zig");
|
const Bus9 = @import("nds9/Bus.zig");
|
||||||
const Bus7 = @import("nds7/Bus.zig");
|
const Bus7 = @import("nds7/Bus.zig");
|
||||||
|
|
||||||
pub inline fn ipcFifoSync(bus: anytype, value: anytype) @TypeOf(value) {
|
inline fn ipcFifoSync(sync: u32, value: anytype) u32 {
|
||||||
comptime verifyBusType(@TypeOf(bus));
|
const _mask: u32 = 0x6F00;
|
||||||
const T = @TypeOf(value);
|
return (@as(u32, value) & _mask) | (sync & ~_mask);
|
||||||
const _mask: T = 0xF;
|
|
||||||
|
|
||||||
return value & ~_mask | @as(T, @intCast(bus.io.shared.ipc_fifo.sync.raw & _mask));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn ipcFifoCnt(bus: anytype, value: anytype) @TypeOf(value) {
|
inline fn ipcFifoCnt(cnt: u32, value: anytype) u32 {
|
||||||
comptime verifyBusType(@TypeOf(bus));
|
const _mask: u32 = 0xC40C;
|
||||||
const T = @TypeOf(value);
|
const err_mask: u32 = 0x4000; // bit 14
|
||||||
const _mask: T = 0x0303;
|
|
||||||
|
|
||||||
return value & ~_mask | @as(T, @intCast(bus.io.shared.ipc_fifo.cnt.raw & _mask));
|
const err_bit = (cnt & err_mask) & ~(value & err_mask);
|
||||||
|
if (value & 0b1000 != 0) log.err("TODO: handle IPCFIFOCNT.3", .{});
|
||||||
|
|
||||||
|
const without_err = (@as(u32, value) & _mask) | (cnt & ~_mask);
|
||||||
|
return (without_err & ~err_mask) | err_bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// General Mask helper
|
/// General Mask helper
|
||||||
pub inline fn mask(original: anytype, value: @TypeOf(original), _mask: @TypeOf(original)) @TypeOf(original) {
|
pub inline fn mask(original: anytype, value: @TypeOf(original), _mask: @TypeOf(original)) @TypeOf(original) {
|
||||||
return (value & _mask) | (original & ~_mask);
|
return (value & _mask) | (original & ~_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verifyBusType(comptime BusT: type) void {
|
|
||||||
std.debug.assert(@typeInfo(BusT) == .Pointer);
|
|
||||||
std.debug.assert(@typeInfo(BusT).Pointer.size == .One);
|
|
||||||
std.debug.assert(@typeInfo(BusT).Pointer.child == Bus9 or @typeInfo(BusT).Pointer.child == Bus7);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const nds7 = struct {
|
pub const nds7 = struct {
|
||||||
|
|
|
@ -24,12 +24,12 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
|
||||||
0x0400_0210 => bus.io.shared.ie,
|
0x0400_0210 => bus.io.shared.ie,
|
||||||
0x0400_0214 => bus.io.shared.irq,
|
0x0400_0214 => bus.io.shared.irq,
|
||||||
|
|
||||||
0x0410_0000 => bus.io.shared.ipc_fifo.recv(.arm7),
|
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.shared.ipc_fifo.sync.raw),
|
0x0400_0180 => @truncate(bus.io.shared.ipc_fifo._nds7.sync.raw),
|
||||||
0x0400_0184 => @truncate(bus.io.shared.ipc_fifo.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) {
|
||||||
|
@ -46,12 +46,12 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
0x0400_0210 => bus.io.shared.ie = value,
|
0x0400_0210 => bus.io.shared.ie = value,
|
||||||
0x0400_0214 => bus.io.shared.irq = value,
|
0x0400_0214 => bus.io.shared.irq = value,
|
||||||
|
|
||||||
0x0400_0188 => bus.io.shared.ipc_fifo.send(.arm7, 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.shared.ipc_fifo.sync.raw = masks.ipcFifoSync(bus, value),
|
0x0400_0180 => bus.io.shared.ipc_fifo.setIpcSync(.nds7, value),
|
||||||
0x0400_0184 => bus.io.shared.ipc_fifo.cnt.raw = masks.ipcFifoCnt(bus, 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) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ const KiB = 0x400;
|
||||||
const log = std.log.scoped(.nds9_bus);
|
const log = std.log.scoped(.nds9_bus);
|
||||||
|
|
||||||
main: *[4 * MiB]u8,
|
main: *[4 * MiB]u8,
|
||||||
|
wram: *[32 * KiB]u8,
|
||||||
vram1: *[512 * KiB]u8, // TODO: Rename
|
vram1: *[512 * KiB]u8, // TODO: Rename
|
||||||
io: io.Io,
|
io: io.Io,
|
||||||
ppu: Ppu,
|
ppu: Ppu,
|
||||||
|
@ -31,6 +32,7 @@ pub fn init(allocator: Allocator, scheduler: *Scheduler, shared_ctx: SharedConte
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.main = shared_ctx.main,
|
.main = shared_ctx.main,
|
||||||
|
.wram = shared_ctx.wram,
|
||||||
.vram1 = vram1_mem,
|
.vram1 = vram1_mem,
|
||||||
.ppu = try Ppu.init(allocator),
|
.ppu = try Ppu.init(allocator),
|
||||||
.scheduler = scheduler,
|
.scheduler = scheduler,
|
||||||
|
@ -71,6 +73,7 @@ fn _read(self: *@This(), comptime T: type, comptime mode: Mode, address: u32) T
|
||||||
|
|
||||||
return switch (aligned_addr) {
|
return switch (aligned_addr) {
|
||||||
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]),
|
||||||
|
// TODO: Impl Shared WRAM
|
||||||
0x0400_0000...0x04FF_FFFF => io.read(self, T, aligned_addr),
|
0x0400_0000...0x04FF_FFFF => io.read(self, T, aligned_addr),
|
||||||
0x0600_0000...0x06FF_FFFF => readInt(T, self.vram1[aligned_addr & 0x0007_FFFF ..][0..byte_count]),
|
0x0600_0000...0x06FF_FFFF => readInt(T, self.vram1[aligned_addr & 0x0007_FFFF ..][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 }),
|
||||||
|
@ -109,3 +112,70 @@ fn warn(comptime format: []const u8, args: anytype) u0 {
|
||||||
log.warn(format, args);
|
log.warn(format, args);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Before I implement Bus-wide Fastmem, Let's play with some more limited (read: less useful)
|
||||||
|
// fastmem implementations
|
||||||
|
const Wram = struct {
|
||||||
|
const page_size = 1 * KiB; // perhaps too big?
|
||||||
|
const addr_space_size = 0x0100_0000;
|
||||||
|
const table_len = addr_space_size / page_size;
|
||||||
|
const IntFittingRange = std.math.IntFittingRange;
|
||||||
|
|
||||||
|
_ptr: *[32 * KiB]u8,
|
||||||
|
|
||||||
|
read_table: *const [table_len]?*const anyopaque,
|
||||||
|
write_table: *const [table_len]?*anyopaque,
|
||||||
|
|
||||||
|
pub fn init(allocator: Allocator, ptr: *[32 * KiB]u8) @This() {
|
||||||
|
const tables = try allocator.create(?*anyopaque, 2 * table_len);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.read_table = tables[0..table_len],
|
||||||
|
.write_table = tables[table_len .. 2 * table_len],
|
||||||
|
._ptr = ptr,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(_: *@This()) void {
|
||||||
|
@panic("TODO: reload WRAM FASTMEM");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(self: @This(), comptime T: type, address: u32) T {
|
||||||
|
const bits = @typeInfo(IntFittingRange(0, page_size - 1)).Int.bits;
|
||||||
|
const page = address >> bits;
|
||||||
|
const offset = address & (page_size - 1);
|
||||||
|
|
||||||
|
std.debug.assert(page < table_len);
|
||||||
|
|
||||||
|
if (self.read_table[page]) |some_ptr| {
|
||||||
|
const ptr: [*]const T = @ptrCast(@alignCast(some_ptr));
|
||||||
|
|
||||||
|
return ptr[forceAlign(T, offset) / @sizeOf(T)];
|
||||||
|
}
|
||||||
|
|
||||||
|
log.err("read(T: {}, addr: 0x{X:0>8}) was in un-mapped WRAM space", .{ T, address });
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(self: *@This(), comptime T: type, address: u32, value: T) void {
|
||||||
|
const bits = @typeInfo(IntFittingRange(0, page_size - 1)).Int.bits;
|
||||||
|
const page = address >> bits;
|
||||||
|
const offset = address & (page_size - 1);
|
||||||
|
|
||||||
|
std.debug.assert(page < table_len);
|
||||||
|
|
||||||
|
if (self.write_table[page]) |some_ptr| {
|
||||||
|
const ptr: [*]T = @ptrCast(@alignCast(some_ptr));
|
||||||
|
ptr[forceAlign(T, offset) / @sizeOf(T)] = value;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.warn("write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8}) was in un-mapped WRAM space", .{ T, address, value });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: @This(), allocator: Allocator) void {
|
||||||
|
const og_ptr: [*]?*anyopaque = @ptrCast(self.read_table);
|
||||||
|
allocator.free(og_ptr[0 .. 2 * table_len]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -43,15 +43,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.shared.ipc_fifo.recv(.arm9),
|
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.shared.ipc_fifo.sync.raw),
|
0x0400_0180 => @truncate(bus.io.shared.ipc_fifo._nds9.sync.raw),
|
||||||
0x0400_0184 => @truncate(bus.io.shared.ipc_fifo.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),
|
||||||
|
@ -69,9 +69,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.shared.ipc_fifo.sync.raw = masks.ipcFifoSync(bus, value),
|
0x0400_0180 => bus.io.shared.ipc_fifo.setIpcSync(.nds9, value),
|
||||||
0x0400_0184 => bus.io.shared.ipc_fifo.cnt.raw = masks.ipcFifoCnt(bus, value),
|
0x0400_0184 => bus.io.shared.ipc_fifo.setIpcFifoCnt(.nds9, value),
|
||||||
0x0400_0188 => bus.io.shared.ipc_fifo.send(.arm9, 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.io.vramcnt_a.raw = @truncate(value >> 0); // 0x0400_0240
|
bus.ppu.io.vramcnt_a.raw = @truncate(value >> 0); // 0x0400_0240
|
||||||
|
@ -114,8 +114,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.shared.ipc_fifo.sync.raw = masks.ipcFifoSync(bus, value),
|
0x0400_0180 => bus.io.shared.ipc_fifo.setIpcSync(.nds9, value),
|
||||||
0x0400_0184 => bus.io.shared.ipc_fifo.cnt.raw = masks.ipcFifoCnt(bus, value),
|
0x0400_0184 => bus.io.shared.ipc_fifo.setIpcFifoCnt(.nds9, value),
|
||||||
0x0400_0208 => bus.io.shared.ime = value & 1 == 1,
|
0x0400_0208 => bus.io.shared.ime = value & 1 == 1,
|
||||||
|
|
||||||
0x0400_0280 => {
|
0x0400_0280 => {
|
||||||
|
|
Loading…
Reference in New Issue