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:
2023-08-01 15:13:18 -05:00
commit e32d6534c6
26 changed files with 7082 additions and 0 deletions

100
src/core/nds7/Bus.zig Normal file
View 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;
}

View 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
View 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;
}