feat: initial commit
some basic ROMs already boot (ARM946E-S only), working on Armwrestler and Rockwrestler next so I can ensure CPU compatability for but ARMv5TE and ARMv4T
This commit is contained in:
100
src/core/nds7/Bus.zig
Normal file
100
src/core/nds7/Bus.zig
Normal file
@@ -0,0 +1,100 @@
|
||||
const std = @import("std");
|
||||
|
||||
const io = @import("io.zig");
|
||||
|
||||
const Scheduler = @import("Scheduler.zig");
|
||||
const SharedIo = @import("../io.zig").Io;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Mode = enum { normal, debug };
|
||||
const MiB = 0x100000;
|
||||
const KiB = 0x400;
|
||||
|
||||
const log = std.log.scoped(.nds7_bus);
|
||||
|
||||
scheduler: *Scheduler,
|
||||
main: *[4 * MiB]u8,
|
||||
wram: *[64 * KiB]u8,
|
||||
io: io.Io,
|
||||
|
||||
pub fn init(allocator: Allocator, scheduler: *Scheduler, shared_io: *SharedIo) !@This() {
|
||||
const main_mem = try allocator.create([4 * MiB]u8);
|
||||
errdefer allocator.destroy(main_mem);
|
||||
@memset(main_mem, 0);
|
||||
|
||||
const wram = try allocator.create([64 * KiB]u8);
|
||||
errdefer allocator.destroy(wram);
|
||||
@memset(wram, 0);
|
||||
|
||||
return .{
|
||||
.main = main_mem,
|
||||
.wram = wram,
|
||||
.scheduler = scheduler,
|
||||
.io = io.Io.init(shared_io),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This(), allocator: Allocator) void {
|
||||
allocator.destroy(self.main);
|
||||
allocator.destroy(self.wram);
|
||||
}
|
||||
|
||||
pub fn reset(_: *@This()) void {}
|
||||
|
||||
pub fn read(self: *@This(), comptime T: type, address: u32) T {
|
||||
return self._read(T, .normal, address);
|
||||
}
|
||||
|
||||
pub fn dbgRead(self: *@This(), comptime T: type, address: u32) T {
|
||||
return self._read(T, .debug, address);
|
||||
}
|
||||
|
||||
fn _read(self: *@This(), comptime T: type, comptime mode: Mode, address: u32) T {
|
||||
const byte_count = @divExact(@typeInfo(T).Int.bits, 8);
|
||||
const readInt = std.mem.readIntLittle;
|
||||
|
||||
switch (mode) {
|
||||
// .debug => log.debug("read {} from 0x{X:0>8}", .{ T, address }),
|
||||
.debug => {},
|
||||
else => self.scheduler.tick += 1,
|
||||
}
|
||||
|
||||
return switch (address) {
|
||||
0x0200_0000...0x02FF_FFFF => readInt(T, self.main[address & 0x003F_FFFF ..][0..byte_count]),
|
||||
0x0380_0000...0x0380_FFFF => readInt(T, self.wram[address & 0x0000_FFFF ..][0..byte_count]),
|
||||
0x0400_0000...0x04FF_FFFF => io.read(self, T, address),
|
||||
else => warn("unexpected read: 0x{x:0>8} -> {}", .{ address, T }),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn write(self: *@This(), comptime T: type, address: u32, value: T) void {
|
||||
return self._write(T, .normal, address, value);
|
||||
}
|
||||
|
||||
pub fn dbgWrite(self: *@This(), comptime T: type, address: u32, value: T) void {
|
||||
return self._write(T, .debug, address, value);
|
||||
}
|
||||
|
||||
fn _write(self: *@This(), comptime T: type, comptime mode: Mode, address: u32, value: T) void {
|
||||
const byte_count = @divExact(@typeInfo(T).Int.bits, 8);
|
||||
const writeInt = std.mem.writeIntLittle;
|
||||
|
||||
switch (mode) {
|
||||
// .debug => log.debug("wrote 0x{X:}{} to 0x{X:0>8}", .{ value, T, address }),
|
||||
.debug => {},
|
||||
else => self.scheduler.tick += 1,
|
||||
}
|
||||
|
||||
switch (address) {
|
||||
0x0200_0000...0x02FF_FFFF => writeInt(T, self.main[address & 0x003F_FFFF ..][0..byte_count], value),
|
||||
0x0380_0000...0x0380_FFFF => writeInt(T, self.wram[address & 0x0000_FFFF ..][0..byte_count], value),
|
||||
0x0400_0000...0x04FF_FFFF => io.write(self, T, address, value),
|
||||
else => log.warn("unexpected write: 0x{X:}{} -> 0x{X:0>8}", .{ value, T, address }),
|
||||
}
|
||||
}
|
||||
|
||||
fn warn(comptime format: []const u8, args: anytype) u0 {
|
||||
log.warn(format, args);
|
||||
return 0;
|
||||
}
|
||||
59
src/core/nds7/Scheduler.zig
Normal file
59
src/core/nds7/Scheduler.zig
Normal file
@@ -0,0 +1,59 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Bus = @import("Bus.zig");
|
||||
|
||||
const PriorityQueue = std.PriorityQueue(Event, void, Event.lessThan);
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
tick: u64 = 0,
|
||||
queue: PriorityQueue,
|
||||
|
||||
pub fn init(allocator: Allocator) !@This() {
|
||||
var queue = PriorityQueue.init(allocator, {});
|
||||
try queue.add(.{ .tick = std.math.maxInt(u64), .kind = .heat_death });
|
||||
|
||||
return .{ .queue = queue };
|
||||
}
|
||||
|
||||
pub fn push(self: *@This(), kind: Event.Kind, offset: u64) void {
|
||||
self.queue.add(.{ .kind = kind, .tick = self.tick + offset }) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn deinit(self: @This()) void {
|
||||
self.queue.deinit();
|
||||
}
|
||||
|
||||
pub fn now(self: @This()) u64 {
|
||||
return self.tick;
|
||||
}
|
||||
|
||||
pub fn next(self: @This()) u64 {
|
||||
@setRuntimeSafety(false);
|
||||
return self.queue.items[0].tick;
|
||||
}
|
||||
|
||||
pub fn reset(self: *@This()) void {
|
||||
self.tick = 0;
|
||||
}
|
||||
|
||||
pub fn handle(self: *@This(), bus: *Bus) void {
|
||||
_ = bus;
|
||||
const event = self.queue.remove();
|
||||
const late = self.tick - event.tick;
|
||||
_ = late;
|
||||
|
||||
switch (event.kind) {
|
||||
.heat_death => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub const Event = struct {
|
||||
tick: u64,
|
||||
kind: Kind,
|
||||
|
||||
pub const Kind = enum { heat_death };
|
||||
|
||||
fn lessThan(_: void, left: @This(), right: @This()) std.math.Order {
|
||||
return std.math.order(left.tick, right.tick);
|
||||
}
|
||||
};
|
||||
71
src/core/nds7/io.zig
Normal file
71
src/core/nds7/io.zig
Normal file
@@ -0,0 +1,71 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Bitfield = @import("bitfield").Bitfield;
|
||||
const Bit = @import("bitfield").Bit;
|
||||
|
||||
const Bus = @import("Bus.zig");
|
||||
const SharedIo = @import("../io.zig").Io;
|
||||
const writeToAddressOffset = @import("../io.zig").writeToAddressOffset;
|
||||
const valueAtAddressOffset = @import("../io.zig").valueAtAddressOffset;
|
||||
|
||||
const log = std.log.scoped(.nds7_io);
|
||||
|
||||
pub const Io = struct {
|
||||
shared: *SharedIo,
|
||||
|
||||
pub fn init(io: *SharedIo) @This() {
|
||||
return .{ .shared = io };
|
||||
}
|
||||
};
|
||||
|
||||
pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
|
||||
return switch (T) {
|
||||
// zig fmt: off
|
||||
u32 =>
|
||||
@as(T, read(bus, u8, address + 3)) << 24
|
||||
| @as(T, read(bus, u8, address + 2)) << 16
|
||||
| @as(T, read(bus, u8, address + 1)) << 8
|
||||
| read(bus, u8, address + 0) << 0,
|
||||
// zig fmt: on
|
||||
u16 => @as(T, read(bus, u8, address + 1)) << 8 | read(bus, u8, address),
|
||||
u8 => switch (address) {
|
||||
0x0400_0180...0x0400_0183 => valueAtAddressOffset(u32, address, bus.io.shared.ipc_sync.raw),
|
||||
0x0400_0184...0x0400_0187 => valueAtAddressOffset(u32, address, bus.io.shared.ipc_fifo_cnt.raw),
|
||||
|
||||
0x0400_0208...0x0400_020B => valueAtAddressOffset(u32, address, @intFromBool(bus.io.shared.ime)),
|
||||
|
||||
else => warn("unexpected read: 0x{X:0>8}", .{address}),
|
||||
},
|
||||
else => @compileError(T ++ " is an unsupported bus read type"),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||
switch (T) {
|
||||
u32 => {
|
||||
write(bus, u8, address + 3, @as(u8, @truncate(value >> 24)));
|
||||
write(bus, u8, address + 2, @as(u8, @truncate(value >> 16)));
|
||||
write(bus, u8, address + 1, @as(u8, @truncate(value >> 8)));
|
||||
write(bus, u8, address + 0, @as(u8, @truncate(value >> 0)));
|
||||
},
|
||||
u16 => {
|
||||
write(bus, u8, address + 1, @as(u8, @truncate(value >> 8)));
|
||||
write(bus, u8, address + 0, @as(u8, @truncate(value >> 0)));
|
||||
},
|
||||
u8 => switch (address) {
|
||||
0x0400_0180...0x0400_0183 => writeToAddressOffset(&bus.io.shared.ipc_sync.raw, address, value),
|
||||
0x0400_0184...0x0400_0187 => writeToAddressOffset(&bus.io.shared.ipc_fifo_cnt.raw, address, value),
|
||||
|
||||
0x0400_0208 => bus.io.shared.ime = value & 1 == 1,
|
||||
0x0400_0209...0x0400_020B => {}, // unused bytes from IME
|
||||
|
||||
else => log.warn("unexpected write: 0x{X:}u8 -> 0x{X:0>8}", .{ value, address }),
|
||||
},
|
||||
else => @compileError(T ++ " is an unsupported bus write type"),
|
||||
}
|
||||
}
|
||||
|
||||
fn warn(comptime format: []const u8, args: anytype) u0 {
|
||||
log.warn(format, args);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user