diff --git a/src/arm.zig b/src/arm.zig index 89ea754..cd08562 100644 --- a/src/arm.zig +++ b/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; + } }; } diff --git a/src/arm/cpu/arm/coprocessor.zig b/src/arm/cpu/arm/coprocessor.zig index 6dea4f7..52d5a74 100644 --- a/src/arm/cpu/arm/coprocessor.zig +++ b/src/arm/cpu/arm/coprocessor.zig @@ -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, + }; +};