feat: integrate cp15 and TCM code

This commit is contained in:
Rekai Nyangadzayi Musuka 2023-09-15 14:18:40 -05:00
parent 71541c312c
commit 30cf951d2a
3 changed files with 125 additions and 29 deletions

View File

@ -221,17 +221,14 @@ pub fn Arm32(comptime isa: Architecture) type {
const readInt = std.mem.readIntSliceLittle; const readInt = std.mem.readIntSliceLittle;
if (is_v5te) { if (is_v5te) {
const itcm_base: u32 = self.itcm.base_address; const dtcm_base = self.dtcm.base_address;
const itcm_size = self.itcm.buf.len; const dtcm_size = self.dtcm.virt.size;
const dtcm_base: u32 = self.dtcm.base_address;
const dtcm_size = self.dtcm.buf.len;
// FIXME: verify correctness + can this be faster? if (address < 0x0000_0000 + self.itcm.virt.size)
if (itcm_base < address and address < itcm_base + itcm_size) return readInt(T, self.itcm.buf[address & self.itcm.virt.mask ..][0..@sizeOf(T)]);
return readInt(T, self.itcm.buf[address..][0..@sizeOf(T)]);
if (dtcm_base < address and address < dtcm_base + dtcm_size) if (dtcm_base < address and address < dtcm_base + dtcm_size)
return readInt(T, self.dtcm.buf[address..][0..@sizeOf(T)]); return readInt(T, self.dtcm.buf[address & self.dtcm.virt.mask ..][0..@sizeOf(T)]);
} }
return self.bus.read(T, address); return self.bus.read(T, address);
@ -241,17 +238,14 @@ pub fn Arm32(comptime isa: Architecture) type {
const writeInt = std.mem.writeIntSliceLittle; const writeInt = std.mem.writeIntSliceLittle;
if (is_v5te) { if (is_v5te) {
const itcm_base: u32 = self.itcm.base_address; const dtcm_base = self.dtcm.base_address;
const itcm_size = self.itcm.buf.len; const dtcm_size = self.dtcm.virt.size;
const dtcm_base: u32 = self.dtcm.base_address;
const dtcm_size = self.dtcm.buf.len;
// FIXME: verify correctness + can this be faster? if (address < 0x0000_0000 + self.itcm.virt.size)
if (itcm_base < address and address < itcm_base + itcm_size) return writeInt(T, self.itcm.buf[address & self.itcm.virt.mask ..][0..@sizeOf(T)], value);
return writeInt(T, self.itcm.buf[address..][0..@sizeOf(T)], value);
if (dtcm_base < address and address < dtcm_base + dtcm_size) if (dtcm_base < address and address < dtcm_base + dtcm_size)
return writeInt(T, self.dtcm.buf[address..][0..@sizeOf(T)], value); return writeInt(T, self.dtcm.buf[address & self.dtcm.virt.mask ..][0..@sizeOf(T)], value);
} }
return self.bus.write(T, address, value); return self.bus.write(T, address, value);
@ -439,6 +433,7 @@ fn Tcm(comptime count: usize, comptime default_addr: u32) type {
return struct { return struct {
buf: [count * KiB]u8 = [_]u8{0x00} ** (count * KiB), buf: [count * KiB]u8 = [_]u8{0x00} ** (count * KiB),
base_address: u32 = default_addr, base_address: u32 = default_addr,
virt: struct { size: u32, mask: u32 } = .{ .size = count * KiB, .mask = (count * KiB) - 1 },
}; };
} }

View File

@ -2,34 +2,133 @@ const std = @import("std");
const log = std.log.scoped(.coprocessor_handler); const log = std.log.scoped(.coprocessor_handler);
pub fn dataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: bool, comptime N: bool, comptime W: bool, comptime L: bool) InstrFn { pub fn dataTransfer(
_ = L; comptime InstrFn: type,
_ = W; comptime P: bool,
_ = N; comptime U: bool,
_ = U; comptime N: bool,
_ = P; comptime W: bool,
comptime L: bool,
) InstrFn {
const Arm32 = @typeInfo(@typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?).Pointer.child; const Arm32 = @typeInfo(@typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?).Pointer.child;
return struct { return struct {
fn inner(cpu: *Arm32, opcode: u32) void { fn inner(cpu: *Arm32, opcode: u32) void {
_ = cpu; if (!P and !W and !U) return copExt(cpu, opcode); // Coprocessor Extension Space
log.err("TODO: handle 0x{X:0>8} which is a coprocessor data transfer instr", .{opcode}); const rn = opcode >> 16 & 0xF;
const crd = opcode >> 12 & 0xF;
const cp_num = opcode >> 8 & 0xF;
const offset = (opcode & 0xFF) << 2;
// TODO: Make sure this is comptime
const addr_mode: u2 = comptime @as(u2, @intFromBool(P)) << 1 | @intFromBool(W);
const start_address: u32 = switch (addr_mode) {
0b00 => blk: {
// Unindexed Addressing
std.debug.assert(U == true);
break :blk cpu.r[rn];
},
0b01 => blk: {
// Immediate Post-Indexed Addressing
const addr = cpu.r[rn];
cpu.r[rn] = if (U) cpu.r[rn] + offset else cpu.r[rn] - offset;
break :blk addr;
},
0b10 => if (U) cpu.r[rn] + offset else cpu.r[rn] - offset, // Immediate Offset Addressing
0b11 => blk: {
// Immediate Pre-Indexed Addressing
cpu.r[rn] = if (U) cpu.r[rn] + offset else cpu.r[rn] - offset;
break :blk cpu.r[rn];
},
};
// TODO: Increment address + 4 (and perform op) until coprocessor says stop
if (L) {
log.debug("TODO: ldc{s} p{}, c{}, 0x{X:0>8}", .{ [_]u8{if (N) 'l' else ' '}, cp_num, crd, start_address });
} else {
log.debug("TODO: stc{s} p{}, c{}, 0x{X:0>8}", .{ [_]u8{if (N) 'l' else ' '}, cp_num, crd, start_address });
}
}
fn copExt(cpu: *Arm32, opcode: u32) void {
_ = cpu;
const cp_num = opcode >> 8 & 0xF;
const rd = opcode >> 12 & 0xF;
const rn = opcode >> 16 & 0xF;
const crm = opcode & 0xF;
const cp_opcode = opcode >> 4 & 0xF; // FIXME: We could get this value at comptime
std.debug.assert(rd != 15); // UNPREDICTABLE
std.debug.assert(rn != 15); // UNPREDICTABLE
if (L) {
// MRRC
log.debug("TODO: mrrc p{}, {}, r{}, r{}, c{}", .{ cp_num, cp_opcode, rd, rn, crm });
} else {
// MCRR
log.debug("TODO: mcrr p{}, {}, r{}, r{}, c{}", .{ cp_num, cp_opcode, rd, rn, crm });
}
} }
}.inner; }.inner;
} }
pub fn registerTransfer(comptime InstrFn: type, comptime opcode1: u3, comptime L: bool, comptime opcode2: u3) InstrFn { pub fn registerTransfer(comptime InstrFn: type, comptime opcode1: u3, comptime L: bool, comptime opcode2: u3) InstrFn {
_ = opcode2;
_ = L;
_ = opcode1;
const Arm32 = @typeInfo(@typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?).Pointer.child; const Arm32 = @typeInfo(@typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?).Pointer.child;
return struct { return struct {
fn inner(cpu: *Arm32, opcode: u32) void { fn inner(cpu: *Arm32, opcode: u32) void {
_ = cpu; const crn: u4 = @intCast(opcode >> 16 & 0xF);
const rd = opcode >> 12 & 0xF;
const cp_num = opcode >> 8 & 0xF;
const crm: u4 = @intCast(opcode & 0xF);
log.err("TODO: handle 0x{X:0>8} which is a coprocessor register transfer instr", .{opcode}); std.debug.assert(cp_num == 0xF); // There's no other coprocessor on NDS9;
if (L) {
// MRC
const value = cpu.cp15.read(opcode1, crn, crm, opcode2);
if (rd != 0xF) {
cpu.r[rd] = value;
return;
}
// TODO: I can probably do this with a mask and the like
cpu.cpsr.n.write(value >> 31 & 1 == 1);
cpu.cpsr.z.write(value >> 30 & 1 == 1);
cpu.cpsr.c.write(value >> 29 & 1 == 1);
cpu.cpsr.v.write(value >> 28 & 1 == 1);
} else {
// MCR
std.debug.assert(rd != 0xF); // UNPREDICTABLE
cpu.cp15.write(opcode1, crn, crm, opcode2, cpu.r[rd]);
{
// OK so the idea is that I don't want to pass the coprocessor a reference to the CPU,
// so there's some side effects that we need to deal with. Right now I think I'll just process
// all side affects on every MCR write and hope this isn't too awful
// TODO: there has to be a better way.....
// ICTM / DTCM Stuff
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
cpu.dtcm.base_address = dtcm_size_base & 0xFFFF_F000;
cpu.dtcm.virt.size = @as(u32, 0x200) << @truncate(std.math.clamp(dtcm_size_base >> 1 & 0x1F, 3, 23));
cpu.dtcm.virt.mask = std.math.clamp(cpu.dtcm.virt.size, 0, @as(u32, @intCast(cpu.dtcm.buf.len))) - 1;
cpu.itcm.virt.size = @as(u32, 0x200) << @truncate(std.math.clamp(itcm_size_base >> 1 & 0x1F, 3, 23));
cpu.itcm.virt.mask = std.math.clamp(cpu.itcm.virt.size, 0, @as(u32, @intCast(cpu.itcm.buf.len))) - 1;
}
}
} }
}.inner; }.inner;
} }

View File

@ -38,6 +38,8 @@ pub const arm = struct {
return comptime comptime_blk: { return comptime comptime_blk: {
@setEvalBranchQuota(0xE000); @setEvalBranchQuota(0xE000);
var table = [_]InstrFn{und} ** 0x1000; var table = [_]InstrFn{und} ** 0x1000;
// op : 27 26 25 24 23 22 21 20 07 06 05 04
// idx: 11 10 09 08 07 06 05 04 03 02 01 00
for (&table, 0..) |*handler, i| { for (&table, 0..) |*handler, i| {
handler.* = switch (@as(u2, i >> 10)) { handler.* = switch (@as(u2, i >> 10)) {