feat: integrate cp15 and TCM code
This commit is contained in:
parent
71541c312c
commit
30cf951d2a
27
src/arm.zig
27
src/arm.zig
|
@ -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 },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
Loading…
Reference in New Issue