feat: integrate cp15 and TCM code

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

View File

@@ -2,34 +2,133 @@ const std = @import("std");
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 {
_ = L;
_ = W;
_ = N;
_ = U;
_ = P;
pub fn dataTransfer(
comptime InstrFn: type,
comptime P: bool,
comptime U: bool,
comptime N: bool,
comptime W: bool,
comptime L: bool,
) InstrFn {
const Arm32 = @typeInfo(@typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?).Pointer.child;
return struct {
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;
}
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;
return struct {
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;
}