feat(v5te): let cp15 affect more tcm behaviours

cp15 can now enable/disable ITCM/DTCM. cp15 can also now enable/disable load mode.
TODO: load mode doesn't effect SWP/SWPB for some reason?
This commit is contained in:
Rekai Nyangadzayi Musuka 2023-09-29 23:32:26 -05:00
parent 1bd96304ba
commit 8d2a3f1b67
2 changed files with 81 additions and 20 deletions

View File

@ -220,34 +220,18 @@ pub fn Arm32(comptime isa: Architecture) type {
// CPU needs it's own read/write fns due to ICTM and DCTM present in v5te
// I considered implementing Bus.cpu_read and Bus.cpu_write but ended up considering that a bit too leaky
pub fn read(self: *Self, comptime T: type, address: u32) T {
const readInt = std.mem.readIntSliceLittle;
if (is_v5te) {
const dtcm_base = self.dtcm.base_address;
const dtcm_size = self.dtcm.virt.size;
if (address < 0x0000_0000 + self.itcm.virt.size)
return readInt(T, self.itcm.buf[address & self.itcm.virt.mask ..][0..@sizeOf(T)]);
if (dtcm_base <= address and address < dtcm_base + dtcm_size)
return readInt(T, self.dtcm.buf[address & self.dtcm.virt.mask ..][0..@sizeOf(T)]);
if (self.itcm.read(T, address)) |val| return val;
if (self.dtcm.read(T, address)) |val| return val;
}
return self.bus.read(T, address);
}
pub fn write(self: *Self, comptime T: type, address: u32, value: T) void {
const writeInt = std.mem.writeIntSliceLittle;
if (is_v5te) {
const dtcm_base = self.dtcm.base_address;
const dtcm_size = self.dtcm.virt.size;
if (address < 0x0000_0000 + self.itcm.virt.size)
return writeInt(T, self.itcm.buf[address & self.itcm.virt.mask ..][0..@sizeOf(T)], value);
if (dtcm_base <= address and address < dtcm_base + dtcm_size)
return writeInt(T, self.dtcm.buf[address & self.dtcm.virt.mask ..][0..@sizeOf(T)], value);
if (self.itcm.write(T, address, value)) return;
if (self.dtcm.write(T, address, value)) return;
}
return self.bus.write(T, address, value);
@ -441,6 +425,56 @@ fn Tcm(comptime count: usize, comptime default_addr: u32) type {
buf: [count * KiB]u8 = [_]u8{0x00} ** (count * KiB),
base_address: u32 = default_addr,
virt: struct { size: u32, mask: u32 } = .{ .size = count * KiB, .mask = (count * KiB) - 1 },
enabled: bool = true,
load_mode: bool = false,
/// Read from TCM
///
/// Returns `T` on success, which is the value we have read from TCM.
/// Returns `null` on failure for one of the following reasons:
/// - TCM is disabled
/// - TCM is in load mode (TODO: What about SWP and SWPB)
/// - TCM Address is not mapped to TCM
///
/// The caller doesn't particularly care about "why" though.
pub fn read(self: *const @This(), comptime T: type, address: u32) ?T {
const readInt = std.mem.readIntSliceLittle;
if (!self.enabled) return null;
if (self.load_mode) return null;
const start_addr = self.base_address;
const end_addr = self.base_address + self.virt.size;
if (start_addr <= address and address < end_addr) {
return readInt(T, self.buf[address & self.virt.mask ..][0..@sizeOf(T)]);
}
return null;
}
/// Write to TCM
///
/// Returns `true` on success. Will return `false` for one of the following reasons:
/// - TCM is disabled
/// - Address is not mapped to TCM
///
/// The caller doesn't particularly care about "why" though.
pub fn write(self: *@This(), comptime T: type, address: u32, value: T) bool {
const writeInt = std.mem.writeIntSliceLittle;
if (!self.enabled) return false;
const start_addr = self.base_address;
const end_addr = self.base_address + self.virt.size;
if (start_addr <= address and address < end_addr) {
writeInt(T, self.buf[address & self.virt.mask ..][0..@sizeOf(T)], value);
return true;
}
return false;
}
};
}

View File

@ -1,4 +1,5 @@
const std = @import("std");
const Bit = @import("bitfield").Bit;
const log = std.log.scoped(.coprocessor_handler);
@ -118,6 +119,13 @@ pub fn registerTransfer(comptime InstrFn: type, comptime opcode1: u3, comptime L
// TODO: there has to be a better way.....
// ICTM / DTCM Stuff
const ctrl: cp15.Control = @bitCast(cpu.cp15.read(0, 1, 0, 0));
cpu.dtcm.enabled = ctrl.dtcm_enable.read();
cpu.dtcm.load_mode = ctrl.dtcm_load_mode.read();
cpu.itcm.enabled = ctrl.itcm_enable.read();
cpu.itcm.load_mode = ctrl.itcm_load_mode.read();
const dtcm_size_base = cpu.cp15.read(0, 9, 1, 0); // mrc 0, c9, c1, 0
const itcm_size_base = cpu.cp15.read(0, 9, 1, 1); // mrc 0, c9, c1, 1
@ -146,3 +154,22 @@ pub fn dataProcessing(comptime InstrFn: type, comptime opcode1: u4, comptime opc
}
}.inner;
}
const cp15 = struct {
// Only the bits that are R/W on the NDS (for now)
const Control = extern union {
pu_enable: Bit(u32, 0),
unified_cache: Bit(u32, 2),
endian: Bit(u32, 7),
instruction_cache: Bit(u32, 12),
exception_vectors: Bit(u32, 13),
cache_replacement: Bit(u32, 14),
pre_armv5_mode: Bit(u32, 15),
dtcm_enable: Bit(u32, 16),
dtcm_load_mode: Bit(u32, 17),
itcm_enable: Bit(u32, 18),
itcm_load_mode: Bit(u32, 19),
raw: u32,
};
};