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:
parent
1bd96304ba
commit
8d2a3f1b67
74
src/arm.zig
74
src/arm.zig
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue