2021-12-29 21:09:00 +00:00
|
|
|
const std = @import("std");
|
2022-01-02 19:01:11 +00:00
|
|
|
|
2022-01-07 23:49:58 +00:00
|
|
|
const Bus = @import("Bus.zig");
|
2022-01-08 00:00:42 +00:00
|
|
|
const Bit = @import("bitfield").Bit;
|
|
|
|
const Bitfield = @import("bitfield").Bitfield;
|
2021-12-29 21:09:00 +00:00
|
|
|
const Scheduler = @import("scheduler.zig").Scheduler;
|
2022-09-19 19:07:19 +00:00
|
|
|
const Logger = @import("../util.zig").Logger;
|
2022-01-05 18:58:11 +00:00
|
|
|
|
2022-01-25 22:18:52 +00:00
|
|
|
const File = std.fs.File;
|
2022-11-16 14:21:40 +00:00
|
|
|
const log = std.log.scoped(.Arm7Tdmi);
|
2022-01-25 22:18:52 +00:00
|
|
|
|
2022-07-27 16:23:29 +00:00
|
|
|
// ARM Instructions
|
|
|
|
pub const arm = struct {
|
2022-08-21 17:28:02 +00:00
|
|
|
pub const InstrFn = *const fn (*Arm7tdmi, *Bus, u32) void;
|
2022-07-27 16:23:29 +00:00
|
|
|
const lut: [0x1000]InstrFn = populate();
|
|
|
|
|
|
|
|
const processing = @import("cpu/arm/data_processing.zig").dataProcessing;
|
|
|
|
const psrTransfer = @import("cpu/arm/psr_transfer.zig").psrTransfer;
|
|
|
|
const transfer = @import("cpu/arm/single_data_transfer.zig").singleDataTransfer;
|
|
|
|
const halfSignedTransfer = @import("cpu/arm/half_signed_data_transfer.zig").halfAndSignedDataTransfer;
|
|
|
|
const blockTransfer = @import("cpu/arm/block_data_transfer.zig").blockDataTransfer;
|
|
|
|
const branch = @import("cpu/arm/branch.zig").branch;
|
|
|
|
const branchExchange = @import("cpu/arm/branch.zig").branchAndExchange;
|
|
|
|
const swi = @import("cpu/arm/software_interrupt.zig").armSoftwareInterrupt;
|
|
|
|
const swap = @import("cpu/arm/single_data_swap.zig").singleDataSwap;
|
|
|
|
|
|
|
|
const multiply = @import("cpu/arm/multiply.zig").multiply;
|
|
|
|
const multiplyLong = @import("cpu/arm/multiply.zig").multiplyLong;
|
|
|
|
|
2022-09-07 02:58:24 +00:00
|
|
|
/// Determine index into ARM InstrFn LUT
|
|
|
|
fn idx(opcode: u32) u12 {
|
|
|
|
return @truncate(u12, opcode >> 20 & 0xFF) << 4 | @truncate(u12, opcode >> 4 & 0xF);
|
|
|
|
}
|
|
|
|
|
2022-07-27 16:23:29 +00:00
|
|
|
// Undefined ARM Instruction handler
|
|
|
|
fn und(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void {
|
2022-09-07 02:58:24 +00:00
|
|
|
const id = idx(opcode);
|
2022-07-27 16:23:29 +00:00
|
|
|
cpu.panic("[CPU/Decode] ID: 0x{X:0>3} 0x{X:0>8} is an illegal opcode", .{ id, opcode });
|
|
|
|
}
|
|
|
|
|
|
|
|
fn populate() [0x1000]InstrFn {
|
|
|
|
return comptime {
|
|
|
|
@setEvalBranchQuota(0xE000);
|
|
|
|
var ret = [_]InstrFn{und} ** 0x1000;
|
|
|
|
|
|
|
|
var i: usize = 0;
|
|
|
|
while (i < ret.len) : (i += 1) {
|
|
|
|
ret[i] = switch (@as(u2, i >> 10)) {
|
|
|
|
0b00 => if (i == 0x121) blk: {
|
|
|
|
break :blk branchExchange;
|
|
|
|
} else if (i & 0xFCF == 0x009) blk: {
|
|
|
|
const A = i >> 5 & 1 == 1;
|
|
|
|
const S = i >> 4 & 1 == 1;
|
|
|
|
break :blk multiply(A, S);
|
|
|
|
} else if (i & 0xFBF == 0x109) blk: {
|
|
|
|
const B = i >> 6 & 1 == 1;
|
|
|
|
break :blk swap(B);
|
|
|
|
} else if (i & 0xF8F == 0x089) blk: {
|
|
|
|
const U = i >> 6 & 1 == 1;
|
|
|
|
const A = i >> 5 & 1 == 1;
|
|
|
|
const S = i >> 4 & 1 == 1;
|
|
|
|
break :blk multiplyLong(U, A, S);
|
|
|
|
} else if (i & 0xE49 == 0x009 or i & 0xE49 == 0x049) blk: {
|
|
|
|
const P = i >> 8 & 1 == 1;
|
|
|
|
const U = i >> 7 & 1 == 1;
|
|
|
|
const I = i >> 6 & 1 == 1;
|
|
|
|
const W = i >> 5 & 1 == 1;
|
|
|
|
const L = i >> 4 & 1 == 1;
|
|
|
|
break :blk halfSignedTransfer(P, U, I, W, L);
|
|
|
|
} else if (i & 0xD90 == 0x100) blk: {
|
|
|
|
const I = i >> 9 & 1 == 1;
|
|
|
|
const R = i >> 6 & 1 == 1;
|
|
|
|
const kind = i >> 4 & 0x3;
|
|
|
|
break :blk psrTransfer(I, R, kind);
|
|
|
|
} else blk: {
|
|
|
|
const I = i >> 9 & 1 == 1;
|
|
|
|
const S = i >> 4 & 1 == 1;
|
|
|
|
const instrKind = i >> 5 & 0xF;
|
|
|
|
break :blk processing(I, S, instrKind);
|
|
|
|
},
|
|
|
|
0b01 => if (i >> 9 & 1 == 1 and i & 1 == 1) und else blk: {
|
|
|
|
const I = i >> 9 & 1 == 1;
|
|
|
|
const P = i >> 8 & 1 == 1;
|
|
|
|
const U = i >> 7 & 1 == 1;
|
|
|
|
const B = i >> 6 & 1 == 1;
|
|
|
|
const W = i >> 5 & 1 == 1;
|
|
|
|
const L = i >> 4 & 1 == 1;
|
|
|
|
break :blk transfer(I, P, U, B, W, L);
|
|
|
|
},
|
|
|
|
else => switch (@as(u2, i >> 9 & 0x3)) {
|
|
|
|
// MSB is guaranteed to be 1
|
|
|
|
0b00 => blk: {
|
|
|
|
const P = i >> 8 & 1 == 1;
|
|
|
|
const U = i >> 7 & 1 == 1;
|
|
|
|
const S = i >> 6 & 1 == 1;
|
|
|
|
const W = i >> 5 & 1 == 1;
|
|
|
|
const L = i >> 4 & 1 == 1;
|
|
|
|
break :blk blockTransfer(P, U, S, W, L);
|
|
|
|
},
|
|
|
|
0b01 => blk: {
|
|
|
|
const L = i >> 8 & 1 == 1;
|
|
|
|
break :blk branch(L);
|
|
|
|
},
|
|
|
|
0b10 => und, // COP Data Transfer
|
|
|
|
0b11 => if (i >> 8 & 1 == 1) swi() else und, // COP Data Operation + Register Transfer
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
2022-02-04 08:00:48 +00:00
|
|
|
|
2022-07-27 16:23:29 +00:00
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// THUMB Instructions
|
2022-07-27 16:10:58 +00:00
|
|
|
pub const thumb = struct {
|
2022-08-21 17:28:02 +00:00
|
|
|
pub const InstrFn = *const fn (*Arm7tdmi, *Bus, u16) void;
|
2022-07-27 16:10:58 +00:00
|
|
|
const lut: [0x400]InstrFn = populate();
|
|
|
|
|
|
|
|
const processing = @import("cpu/thumb/data_processing.zig");
|
|
|
|
const alu = @import("cpu/thumb/alu.zig").fmt4;
|
|
|
|
const transfer = @import("cpu/thumb/data_transfer.zig");
|
|
|
|
const block_transfer = @import("cpu/thumb/block_data_transfer.zig");
|
|
|
|
const swi = @import("cpu/thumb/software_interrupt.zig").fmt17;
|
|
|
|
const branch = @import("cpu/thumb/branch.zig");
|
|
|
|
|
2022-09-19 19:07:19 +00:00
|
|
|
/// Determine index into THUMB InstrFn LUT
|
2022-09-07 02:58:24 +00:00
|
|
|
fn idx(opcode: u16) u10 {
|
|
|
|
return @truncate(u10, opcode >> 6);
|
|
|
|
}
|
|
|
|
|
2022-07-27 16:10:58 +00:00
|
|
|
/// Undefined THUMB Instruction Handler
|
|
|
|
fn und(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
|
2022-09-07 02:58:24 +00:00
|
|
|
const id = idx(opcode);
|
2022-07-27 16:10:58 +00:00
|
|
|
cpu.panic("[CPU/Decode] ID: 0b{b:0>10} 0x{X:0>2} is an illegal opcode", .{ id, opcode });
|
|
|
|
}
|
2022-02-04 08:00:48 +00:00
|
|
|
|
2022-07-27 16:10:58 +00:00
|
|
|
fn populate() [0x400]InstrFn {
|
|
|
|
return comptime {
|
|
|
|
@setEvalBranchQuota(5025); // This is exact
|
|
|
|
var ret = [_]InstrFn{und} ** 0x400;
|
|
|
|
|
|
|
|
var i: usize = 0;
|
|
|
|
while (i < ret.len) : (i += 1) {
|
|
|
|
ret[i] = switch (@as(u3, i >> 7 & 0x7)) {
|
|
|
|
0b000 => if (i >> 5 & 0x3 == 0b11) blk: {
|
|
|
|
const I = i >> 4 & 1 == 1;
|
|
|
|
const is_sub = i >> 3 & 1 == 1;
|
|
|
|
const rn = i & 0x7;
|
|
|
|
break :blk processing.fmt2(I, is_sub, rn);
|
|
|
|
} else blk: {
|
|
|
|
const op = i >> 5 & 0x3;
|
|
|
|
const offset = i & 0x1F;
|
|
|
|
break :blk processing.fmt1(op, offset);
|
|
|
|
},
|
|
|
|
0b001 => blk: {
|
|
|
|
const op = i >> 5 & 0x3;
|
|
|
|
const rd = i >> 2 & 0x7;
|
|
|
|
break :blk processing.fmt3(op, rd);
|
|
|
|
},
|
|
|
|
0b010 => switch (@as(u2, i >> 5 & 0x3)) {
|
|
|
|
0b00 => if (i >> 4 & 1 == 1) blk: {
|
|
|
|
const op = i >> 2 & 0x3;
|
|
|
|
const h1 = i >> 1 & 1;
|
|
|
|
const h2 = i & 1;
|
|
|
|
break :blk processing.fmt5(op, h1, h2);
|
|
|
|
} else blk: {
|
|
|
|
const op = i & 0xF;
|
|
|
|
break :blk alu(op);
|
|
|
|
},
|
|
|
|
0b01 => blk: {
|
|
|
|
const rd = i >> 2 & 0x7;
|
|
|
|
break :blk transfer.fmt6(rd);
|
|
|
|
},
|
|
|
|
else => blk: {
|
|
|
|
const op = i >> 4 & 0x3;
|
|
|
|
const T = i >> 3 & 1 == 1;
|
|
|
|
break :blk transfer.fmt78(op, T);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
0b011 => blk: {
|
|
|
|
const B = i >> 6 & 1 == 1;
|
|
|
|
const L = i >> 5 & 1 == 1;
|
|
|
|
const offset = i & 0x1F;
|
|
|
|
break :blk transfer.fmt9(B, L, offset);
|
|
|
|
},
|
|
|
|
else => switch (@as(u3, i >> 6 & 0x7)) {
|
|
|
|
// MSB is guaranteed to be 1
|
|
|
|
0b000 => blk: {
|
|
|
|
const L = i >> 5 & 1 == 1;
|
|
|
|
const offset = i & 0x1F;
|
|
|
|
break :blk transfer.fmt10(L, offset);
|
|
|
|
},
|
|
|
|
0b001 => blk: {
|
|
|
|
const L = i >> 5 & 1 == 1;
|
|
|
|
const rd = i >> 2 & 0x7;
|
|
|
|
break :blk transfer.fmt11(L, rd);
|
|
|
|
},
|
|
|
|
0b010 => blk: {
|
|
|
|
const isSP = i >> 5 & 1 == 1;
|
|
|
|
const rd = i >> 2 & 0x7;
|
|
|
|
break :blk processing.fmt12(isSP, rd);
|
|
|
|
},
|
|
|
|
0b011 => if (i >> 4 & 1 == 1) blk: {
|
|
|
|
const L = i >> 5 & 1 == 1;
|
|
|
|
const R = i >> 2 & 1 == 1;
|
|
|
|
break :blk block_transfer.fmt14(L, R);
|
|
|
|
} else blk: {
|
|
|
|
const S = i >> 1 & 1 == 1;
|
|
|
|
break :blk processing.fmt13(S);
|
|
|
|
},
|
|
|
|
0b100 => blk: {
|
|
|
|
const L = i >> 5 & 1 == 1;
|
|
|
|
const rb = i >> 2 & 0x7;
|
|
|
|
|
|
|
|
break :blk block_transfer.fmt15(L, rb);
|
|
|
|
},
|
|
|
|
0b101 => if (i >> 2 & 0xF == 0b1111) blk: {
|
|
|
|
break :blk thumb.swi();
|
|
|
|
} else blk: {
|
|
|
|
const cond = i >> 2 & 0xF;
|
|
|
|
break :blk branch.fmt16(cond);
|
|
|
|
},
|
|
|
|
0b110 => branch.fmt18(),
|
|
|
|
0b111 => blk: {
|
|
|
|
const is_low = i >> 5 & 1 == 1;
|
|
|
|
break :blk branch.fmt19(is_low);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
2022-02-04 08:00:48 +00:00
|
|
|
|
2022-07-27 16:10:58 +00:00
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
2022-01-14 11:25:44 +00:00
|
|
|
|
2022-01-02 03:08:36 +00:00
|
|
|
pub const Arm7tdmi = struct {
|
2022-01-10 03:34:33 +00:00
|
|
|
const Self = @This();
|
|
|
|
|
2021-12-29 21:09:00 +00:00
|
|
|
r: [16]u32,
|
2022-09-28 19:37:40 +00:00
|
|
|
pipe: Pipeline,
|
2022-01-05 22:34:42 +00:00
|
|
|
sched: *Scheduler,
|
2022-07-27 16:44:24 +00:00
|
|
|
bus: *Bus,
|
2022-01-12 08:48:57 +00:00
|
|
|
cpsr: PSR,
|
2022-01-19 11:29:49 +00:00
|
|
|
spsr: PSR,
|
|
|
|
|
2022-11-16 14:21:40 +00:00
|
|
|
bank: Bank,
|
2022-01-19 11:29:49 +00:00
|
|
|
|
2022-11-16 14:21:40 +00:00
|
|
|
logger: ?Logger,
|
2022-01-19 11:29:49 +00:00
|
|
|
|
2022-11-16 14:21:40 +00:00
|
|
|
/// Bank of Registers from other CPU Modes
|
|
|
|
const Bank = struct {
|
|
|
|
/// Storage for r13_<mode>, r14_<mode>
|
|
|
|
/// e.g. [r13, r14, r13_svc, r14_svc]
|
|
|
|
r: [2 * 6]u32,
|
2021-12-29 21:09:00 +00:00
|
|
|
|
2022-11-16 14:21:40 +00:00
|
|
|
/// Storage for R8_fiq -> R12_fiq and their normal counterparts
|
|
|
|
/// e.g [r[0 + 8], fiq_r[0 + 8], r[1 + 8], fiq_r[1 + 8]...]
|
|
|
|
fiq: [2 * 5]u32,
|
|
|
|
|
|
|
|
spsr: [5]PSR,
|
|
|
|
|
|
|
|
const Kind = enum(u1) {
|
|
|
|
R13 = 0,
|
|
|
|
R14,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn create() Bank {
|
|
|
|
return .{
|
|
|
|
.r = [_]u32{0x00} ** 12,
|
|
|
|
.fiq = [_]u32{0x00} ** 10,
|
|
|
|
.spsr = [_]PSR{.{ .raw = 0x0000_0000 }} ** 5,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
inline fn regIdx(mode: Mode, kind: Kind) usize {
|
|
|
|
const idx: usize = switch (mode) {
|
|
|
|
.User, .System => 0,
|
|
|
|
.Supervisor => 1,
|
|
|
|
.Abort => 2,
|
|
|
|
.Undefined => 3,
|
|
|
|
.Irq => 4,
|
|
|
|
.Fiq => 5,
|
|
|
|
};
|
|
|
|
|
|
|
|
return (idx * 2) + if (kind == .R14) @as(usize, 1) else 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline fn spsrIdx(mode: Mode) usize {
|
|
|
|
return switch (mode) {
|
|
|
|
.Supervisor => 0,
|
|
|
|
.Abort => 1,
|
|
|
|
.Undefined => 2,
|
|
|
|
.Irq => 3,
|
|
|
|
.Fiq => 4,
|
|
|
|
else => std.debug.panic("[CPU/Mode] {} does not have a SPSR Register", .{mode}),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
inline fn fiqIdx(i: usize, mode: Mode) usize {
|
|
|
|
return (i * 2) + if (mode == .Fiq) @as(usize, 1) else 0;
|
|
|
|
}
|
|
|
|
};
|
2022-01-25 22:18:52 +00:00
|
|
|
|
2022-08-29 05:32:41 +00:00
|
|
|
pub fn init(sched: *Scheduler, bus: *Bus, log_file: ?std.fs.File) Self {
|
2022-04-21 10:56:17 +00:00
|
|
|
return Self{
|
2021-12-29 21:09:00 +00:00
|
|
|
.r = [_]u32{0x00} ** 16,
|
2022-09-28 19:37:40 +00:00
|
|
|
.pipe = Pipeline.init(),
|
2022-01-05 22:34:42 +00:00
|
|
|
.sched = sched,
|
2022-07-27 16:44:24 +00:00
|
|
|
.bus = bus,
|
2022-02-06 22:41:16 +00:00
|
|
|
.cpsr = .{ .raw = 0x0000_001F },
|
2022-01-19 11:29:49 +00:00
|
|
|
.spsr = .{ .raw = 0x0000_0000 },
|
2022-11-16 14:21:40 +00:00
|
|
|
.bank = Bank.create(),
|
2022-08-29 05:32:41 +00:00
|
|
|
.logger = if (log_file) |file| Logger.init(file) else null,
|
2022-01-19 11:29:49 +00:00
|
|
|
};
|
2022-04-14 02:21:25 +00:00
|
|
|
}
|
|
|
|
|
2022-01-28 21:11:29 +00:00
|
|
|
pub inline fn hasSPSR(self: *const Self) bool {
|
2022-02-04 20:54:57 +00:00
|
|
|
const mode = getModeChecked(self, self.cpsr.mode.read());
|
2022-02-01 22:38:09 +00:00
|
|
|
return switch (mode) {
|
2022-01-19 11:29:49 +00:00
|
|
|
.System, .User => false,
|
|
|
|
else => true,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-01-28 21:11:29 +00:00
|
|
|
pub inline fn isPrivileged(self: *const Self) bool {
|
2022-02-04 20:54:57 +00:00
|
|
|
const mode = getModeChecked(self, self.cpsr.mode.read());
|
2022-02-01 22:38:09 +00:00
|
|
|
return switch (mode) {
|
2022-01-19 11:29:49 +00:00
|
|
|
.User => false,
|
|
|
|
else => true,
|
2021-12-29 21:09:00 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-06-16 03:49:37 +00:00
|
|
|
pub inline fn isHalted(self: *const Self) bool {
|
|
|
|
return self.bus.io.haltcnt == .Halt;
|
|
|
|
}
|
|
|
|
|
2022-01-19 11:29:49 +00:00
|
|
|
pub fn setCpsr(self: *Self, value: u32) void {
|
2022-01-25 14:33:16 +00:00
|
|
|
if (value & 0x1F != self.cpsr.raw & 0x1F) self.changeModeFromIdx(@truncate(u5, value & 0x1F));
|
2022-01-19 11:29:49 +00:00
|
|
|
self.cpsr.raw = value;
|
|
|
|
}
|
|
|
|
|
2022-01-25 14:33:16 +00:00
|
|
|
fn changeModeFromIdx(self: *Self, next: u5) void {
|
2022-02-04 20:54:57 +00:00
|
|
|
self.changeMode(getModeChecked(self, next));
|
2022-01-25 14:33:16 +00:00
|
|
|
}
|
|
|
|
|
2022-02-02 19:15:41 +00:00
|
|
|
pub fn setUserModeRegister(self: *Self, idx: usize, value: u32) void {
|
2022-02-04 20:54:57 +00:00
|
|
|
const current = getModeChecked(self, self.cpsr.mode.read());
|
2022-02-02 19:15:41 +00:00
|
|
|
|
2022-02-12 08:33:32 +00:00
|
|
|
switch (idx) {
|
|
|
|
8...12 => {
|
|
|
|
if (current == .Fiq) {
|
2022-11-16 14:21:40 +00:00
|
|
|
self.bank.fiq[Bank.fiqIdx(idx - 8, .User)] = value;
|
2022-02-12 08:33:32 +00:00
|
|
|
} else self.r[idx] = value;
|
|
|
|
},
|
|
|
|
13, 14 => switch (current) {
|
2022-02-02 19:15:41 +00:00
|
|
|
.User, .System => self.r[idx] = value,
|
|
|
|
else => {
|
2022-11-16 14:21:40 +00:00
|
|
|
const kind = std.meta.intToEnum(Bank.Kind, idx - 13) catch unreachable;
|
|
|
|
self.bank.r[Bank.regIdx(.User, kind)] = value;
|
2022-02-02 19:15:41 +00:00
|
|
|
},
|
2022-02-12 08:33:32 +00:00
|
|
|
},
|
|
|
|
else => self.r[idx] = value, // R0 -> R7 and R15
|
|
|
|
}
|
2022-02-02 19:15:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn getUserModeRegister(self: *Self, idx: usize) u32 {
|
2022-02-04 20:54:57 +00:00
|
|
|
const current = getModeChecked(self, self.cpsr.mode.read());
|
2022-02-02 19:15:41 +00:00
|
|
|
|
2022-02-12 08:33:32 +00:00
|
|
|
return switch (idx) {
|
2022-11-16 14:21:40 +00:00
|
|
|
8...12 => if (current == .Fiq) self.bank.fiq[Bank.fiqIdx(idx - 8, .User)] else self.r[idx],
|
2022-02-12 08:33:32 +00:00
|
|
|
13, 14 => switch (current) {
|
|
|
|
.User, .System => self.r[idx],
|
|
|
|
else => blk: {
|
2022-11-16 14:21:40 +00:00
|
|
|
const kind = std.meta.intToEnum(Bank.Kind, idx - 13) catch unreachable;
|
|
|
|
break :blk self.bank.r[Bank.regIdx(.User, kind)];
|
2022-02-02 19:15:41 +00:00
|
|
|
},
|
2022-02-12 08:33:32 +00:00
|
|
|
},
|
|
|
|
else => self.r[idx], // R0 -> R7 and R15
|
|
|
|
};
|
2022-02-02 19:15:41 +00:00
|
|
|
}
|
|
|
|
|
2022-01-25 14:33:16 +00:00
|
|
|
pub fn changeMode(self: *Self, next: Mode) void {
|
2022-02-04 20:54:57 +00:00
|
|
|
const now = getModeChecked(self, self.cpsr.mode.read());
|
2022-01-19 11:29:49 +00:00
|
|
|
|
|
|
|
// Bank R8 -> r12
|
2022-02-12 08:33:32 +00:00
|
|
|
var i: usize = 0;
|
|
|
|
while (i < 5) : (i += 1) {
|
2022-11-16 14:21:40 +00:00
|
|
|
self.bank.fiq[Bank.fiqIdx(i, now)] = self.r[8 + i];
|
2022-01-19 11:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Bank r13, r14, SPSR
|
|
|
|
switch (now) {
|
|
|
|
.User, .System => {
|
2022-11-16 14:21:40 +00:00
|
|
|
self.bank.r[Bank.regIdx(now, .R13)] = self.r[13];
|
|
|
|
self.bank.r[Bank.regIdx(now, .R14)] = self.r[14];
|
2022-01-19 11:29:49 +00:00
|
|
|
},
|
|
|
|
else => {
|
2022-11-16 14:21:40 +00:00
|
|
|
self.bank.r[Bank.regIdx(now, .R13)] = self.r[13];
|
|
|
|
self.bank.r[Bank.regIdx(now, .R14)] = self.r[14];
|
|
|
|
self.bank.spsr[Bank.spsrIdx(now)] = self.spsr;
|
2022-01-19 11:29:49 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Grab R8 -> R12
|
2022-02-12 08:33:32 +00:00
|
|
|
i = 0;
|
|
|
|
while (i < 5) : (i += 1) {
|
2022-11-16 14:21:40 +00:00
|
|
|
self.r[8 + i] = self.bank.fiq[Bank.fiqIdx(i, next)];
|
2022-01-19 11:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Grab r13, r14, SPSR
|
|
|
|
switch (next) {
|
|
|
|
.User, .System => {
|
2022-11-16 14:21:40 +00:00
|
|
|
self.r[13] = self.bank.r[Bank.regIdx(next, .R13)];
|
|
|
|
self.r[14] = self.bank.r[Bank.regIdx(next, .R14)];
|
2022-01-19 11:29:49 +00:00
|
|
|
},
|
|
|
|
else => {
|
2022-11-16 14:21:40 +00:00
|
|
|
self.r[13] = self.bank.r[Bank.regIdx(next, .R13)];
|
|
|
|
self.r[14] = self.bank.r[Bank.regIdx(next, .R14)];
|
|
|
|
self.spsr = self.bank.spsr[Bank.spsrIdx(next)];
|
2022-01-19 11:29:49 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-01-25 14:33:16 +00:00
|
|
|
self.cpsr.mode.write(@enumToInt(next));
|
2022-01-19 11:29:49 +00:00
|
|
|
}
|
|
|
|
|
2022-10-09 17:11:18 +00:00
|
|
|
/// Advances state so that the BIOS is skipped
|
|
|
|
///
|
|
|
|
/// Note: This accesses the CPU's bus ptr so it only may be called
|
|
|
|
/// once the Bus has been properly initialized
|
|
|
|
///
|
|
|
|
/// TODO: Make above notice impossible to do in code
|
2022-01-25 15:15:17 +00:00
|
|
|
pub fn fastBoot(self: *Self) void {
|
2022-02-12 08:33:32 +00:00
|
|
|
self.r = std.mem.zeroes([16]u32);
|
|
|
|
|
2022-08-18 22:51:46 +00:00
|
|
|
// self.r[0] = 0x08000000;
|
|
|
|
// self.r[1] = 0x000000EA;
|
2022-01-02 20:58:39 +00:00
|
|
|
self.r[13] = 0x0300_7F00;
|
|
|
|
self.r[15] = 0x0800_0000;
|
|
|
|
|
2022-11-16 14:21:40 +00:00
|
|
|
self.bank.r[Bank.regIdx(.Irq, .R13)] = 0x0300_7FA0;
|
|
|
|
self.bank.r[Bank.regIdx(.Supervisor, .R13)] = 0x0300_7FE0;
|
2022-01-02 20:58:39 +00:00
|
|
|
|
2022-08-18 22:51:46 +00:00
|
|
|
// self.cpsr.raw = 0x6000001F;
|
|
|
|
self.cpsr.raw = 0x0000_001F;
|
2022-10-09 17:11:18 +00:00
|
|
|
|
|
|
|
self.bus.bios.addr_latch = 0x0000_00DC + 8;
|
2022-01-02 20:58:39 +00:00
|
|
|
}
|
|
|
|
|
2022-04-09 22:15:12 +00:00
|
|
|
pub fn step(self: *Self) void {
|
2022-09-28 20:25:21 +00:00
|
|
|
defer {
|
|
|
|
if (!self.pipe.flushed) self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4);
|
|
|
|
self.pipe.flushed = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self.cpsr.t.read()) {
|
|
|
|
const opcode = @truncate(u16, self.pipe.step(self, u16) orelse return);
|
2022-10-12 21:40:38 +00:00
|
|
|
if (self.logger) |*trace| trace.mgbaLog(self, opcode);
|
2022-01-17 11:18:44 +00:00
|
|
|
|
2022-09-07 02:58:24 +00:00
|
|
|
thumb.lut[thumb.idx(opcode)](self, self.bus, opcode);
|
2022-09-28 20:25:21 +00:00
|
|
|
} else {
|
|
|
|
const opcode = self.pipe.step(self, u32) orelse return;
|
2022-10-12 21:40:38 +00:00
|
|
|
if (self.logger) |*trace| trace.mgbaLog(self, opcode);
|
2022-01-14 09:23:16 +00:00
|
|
|
|
|
|
|
if (checkCond(self.cpsr, @truncate(u4, opcode >> 28))) {
|
2022-09-07 02:58:24 +00:00
|
|
|
arm.lut[arm.idx(opcode)](self, self.bus, opcode);
|
2022-01-14 09:23:16 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-29 21:09:00 +00:00
|
|
|
}
|
|
|
|
|
2022-06-16 04:46:37 +00:00
|
|
|
pub fn stepDmaTransfer(self: *Self) bool {
|
2022-10-31 06:22:08 +00:00
|
|
|
comptime var i: usize = 0;
|
|
|
|
inline while (i < 4) : (i += 1) {
|
|
|
|
if (self.bus.dma[i].in_progress) {
|
|
|
|
self.bus.dma[i].step(self);
|
|
|
|
return true;
|
|
|
|
}
|
2022-06-16 04:46:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2022-05-06 01:04:59 +00:00
|
|
|
}
|
|
|
|
|
2022-02-20 23:40:22 +00:00
|
|
|
pub fn handleInterrupt(self: *Self) void {
|
|
|
|
const should_handle = self.bus.io.ie.raw & self.bus.io.irq.raw;
|
|
|
|
|
2022-07-13 08:10:58 +00:00
|
|
|
// Return if IME is disabled, CPSR I is set or there is nothing to handle
|
|
|
|
if (!self.bus.io.ime or self.cpsr.i.read() or should_handle == 0) return;
|
|
|
|
|
2022-09-28 20:25:21 +00:00
|
|
|
// If Pipeline isn't full, we have a bug
|
|
|
|
std.debug.assert(self.pipe.isFull());
|
2022-07-13 08:10:58 +00:00
|
|
|
|
|
|
|
// log.debug("Handling Interrupt!", .{});
|
|
|
|
self.bus.io.haltcnt = .Execute;
|
|
|
|
|
2022-09-28 19:11:25 +00:00
|
|
|
// FIXME: This seems weird, but retAddr.gba suggests I need to make these changes
|
|
|
|
const ret_addr = self.r[15] - if (self.cpsr.t.read()) 0 else @as(u32, 4);
|
2022-07-13 08:10:58 +00:00
|
|
|
const new_spsr = self.cpsr.raw;
|
|
|
|
|
|
|
|
self.changeMode(.Irq);
|
|
|
|
self.cpsr.t.write(false);
|
|
|
|
self.cpsr.i.write(true);
|
|
|
|
|
|
|
|
self.r[14] = ret_addr;
|
|
|
|
self.spsr.raw = new_spsr;
|
|
|
|
self.r[15] = 0x0000_0018;
|
2022-09-28 19:11:25 +00:00
|
|
|
self.pipe.reload(self);
|
2022-02-20 23:40:22 +00:00
|
|
|
}
|
|
|
|
|
2022-09-28 20:25:21 +00:00
|
|
|
inline fn fetch(self: *Self, comptime T: type, address: u32) T {
|
2022-04-21 13:15:52 +00:00
|
|
|
comptime std.debug.assert(T == u32 or T == u16); // Opcode may be 32-bit (ARM) or 16-bit (THUMB)
|
2022-01-14 09:23:16 +00:00
|
|
|
|
2022-09-28 20:25:21 +00:00
|
|
|
// Bus.read will advance the scheduler. There are different timings for CPU fetches,
|
|
|
|
// so we want to undo what Bus.read will apply. We can do this by caching the current tick
|
|
|
|
// This is very dumb.
|
|
|
|
//
|
|
|
|
// FIXME: Please rework this
|
2022-06-17 01:35:42 +00:00
|
|
|
const tick_cache = self.sched.tick;
|
2022-09-28 20:25:21 +00:00
|
|
|
defer self.sched.tick = tick_cache + Bus.fetch_timings[@boolToInt(T == u32)][@truncate(u4, address >> 24)];
|
2022-06-17 01:35:42 +00:00
|
|
|
|
2022-09-28 20:25:21 +00:00
|
|
|
return self.bus.read(T, address);
|
2021-12-29 21:09:00 +00:00
|
|
|
}
|
2022-01-04 07:11:53 +00:00
|
|
|
|
2022-02-01 22:38:09 +00:00
|
|
|
pub fn panic(self: *const Self, comptime format: []const u8, args: anytype) noreturn {
|
|
|
|
var i: usize = 0;
|
|
|
|
while (i < 16) : (i += 4) {
|
|
|
|
const i_1 = i + 1;
|
|
|
|
const i_2 = i + 2;
|
|
|
|
const i_3 = i + 3;
|
|
|
|
std.debug.print("R{}: 0x{X:0>8}\tR{}: 0x{X:0>8}\tR{}: 0x{X:0>8}\tR{}: 0x{X:0>8}\n", .{ i, self.r[i], i_1, self.r[i_1], i_2, self.r[i_2], i_3, self.r[i_3] });
|
|
|
|
}
|
|
|
|
std.debug.print("cpsr: 0x{X:0>8} ", .{self.cpsr.raw});
|
2022-11-16 14:21:40 +00:00
|
|
|
self.cpsr.toString();
|
2022-02-01 22:38:09 +00:00
|
|
|
|
|
|
|
std.debug.print("spsr: 0x{X:0>8} ", .{self.spsr.raw});
|
2022-11-16 14:21:40 +00:00
|
|
|
self.spsr.toString();
|
2022-02-01 22:38:09 +00:00
|
|
|
|
2022-08-27 02:45:35 +00:00
|
|
|
std.debug.print("pipeline: {??X:0>8}\n", .{self.pipe.stage});
|
|
|
|
|
2022-02-05 17:46:24 +00:00
|
|
|
if (self.cpsr.t.read()) {
|
2022-08-07 10:11:29 +00:00
|
|
|
const opcode = self.bus.dbgRead(u16, self.r[15] - 4);
|
2022-09-07 02:58:24 +00:00
|
|
|
const id = thumb.idx(opcode);
|
2022-02-05 17:46:24 +00:00
|
|
|
std.debug.print("opcode: ID: 0x{b:0>10} 0x{X:0>4}\n", .{ id, opcode });
|
|
|
|
} else {
|
2022-08-07 10:11:29 +00:00
|
|
|
const opcode = self.bus.dbgRead(u32, self.r[15] - 4);
|
2022-09-07 02:58:24 +00:00
|
|
|
const id = arm.idx(opcode);
|
2022-02-05 17:46:24 +00:00
|
|
|
std.debug.print("opcode: ID: 0x{X:0>3} 0x{X:0>8}\n", .{ id, opcode });
|
|
|
|
}
|
|
|
|
|
2022-02-01 22:38:09 +00:00
|
|
|
std.debug.print("tick: {}\n\n", .{self.sched.tick});
|
|
|
|
|
|
|
|
std.debug.panic(format, args);
|
|
|
|
}
|
2021-12-29 21:09:00 +00:00
|
|
|
};
|
|
|
|
|
2022-11-01 11:29:42 +00:00
|
|
|
const condition_lut = [_]u16{
|
|
|
|
0xF0F0, // EQ - Equal
|
|
|
|
0x0F0F, // NE - Not Equal
|
|
|
|
0xCCCC, // CS - Unsigned higher or same
|
|
|
|
0x3333, // CC - Unsigned lower
|
|
|
|
0xFF00, // MI - Negative
|
|
|
|
0x00FF, // PL - Positive or Zero
|
|
|
|
0xAAAA, // VS - Overflow
|
|
|
|
0x5555, // VC - No Overflow
|
|
|
|
0x0C0C, // HI - unsigned hierh
|
|
|
|
0xF3F3, // LS - unsigned lower or same
|
|
|
|
0xAA55, // GE - greater or equal
|
|
|
|
0x55AA, // LT - less than
|
|
|
|
0x0A05, // GT - greater than
|
|
|
|
0xF5FA, // LE - less than or equal
|
|
|
|
0xFFFF, // AL - always
|
|
|
|
0x0000, // NV - never
|
|
|
|
};
|
|
|
|
|
|
|
|
pub inline fn checkCond(cpsr: PSR, cond: u4) bool {
|
|
|
|
const flags = @truncate(u4, cpsr.raw >> 28);
|
|
|
|
|
|
|
|
return condition_lut[cond] & (@as(u16, 1) << flags) != 0;
|
2022-01-02 03:56:58 +00:00
|
|
|
}
|
|
|
|
|
2022-09-28 19:37:40 +00:00
|
|
|
const Pipeline = struct {
|
2022-06-29 09:33:17 +00:00
|
|
|
const Self = @This();
|
|
|
|
stage: [2]?u32,
|
|
|
|
flushed: bool,
|
|
|
|
|
|
|
|
fn init() Self {
|
|
|
|
return .{
|
|
|
|
.stage = [_]?u32{null} ** 2,
|
|
|
|
.flushed = false,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-07-13 08:10:58 +00:00
|
|
|
pub fn isFull(self: *const Self) bool {
|
|
|
|
return self.stage[0] != null and self.stage[1] != null;
|
|
|
|
}
|
|
|
|
|
2022-06-29 09:33:17 +00:00
|
|
|
pub fn step(self: *Self, cpu: *Arm7tdmi, comptime T: type) ?u32 {
|
|
|
|
comptime std.debug.assert(T == u32 or T == u16);
|
|
|
|
|
2022-11-11 07:56:19 +00:00
|
|
|
const opcode = self.stage[0];
|
2022-06-29 09:33:17 +00:00
|
|
|
self.stage[0] = self.stage[1];
|
2022-09-28 20:25:21 +00:00
|
|
|
self.stage[1] = cpu.fetch(T, cpu.r[15]);
|
2022-06-29 09:33:17 +00:00
|
|
|
|
|
|
|
return opcode;
|
|
|
|
}
|
|
|
|
|
2022-09-28 19:11:25 +00:00
|
|
|
pub fn reload(self: *Self, cpu: *Arm7tdmi) void {
|
|
|
|
if (cpu.cpsr.t.read()) {
|
2022-09-28 20:25:21 +00:00
|
|
|
self.stage[0] = cpu.fetch(u16, cpu.r[15]);
|
|
|
|
self.stage[1] = cpu.fetch(u16, cpu.r[15] + 2);
|
2022-09-28 19:11:25 +00:00
|
|
|
cpu.r[15] += 4;
|
|
|
|
} else {
|
2022-09-28 20:25:21 +00:00
|
|
|
self.stage[0] = cpu.fetch(u32, cpu.r[15]);
|
|
|
|
self.stage[1] = cpu.fetch(u32, cpu.r[15] + 4);
|
2022-09-28 19:11:25 +00:00
|
|
|
cpu.r[15] += 8;
|
|
|
|
}
|
2022-08-27 04:59:59 +00:00
|
|
|
|
2022-08-18 22:51:46 +00:00
|
|
|
self.flushed = true;
|
2022-06-29 09:33:17 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-01-12 08:48:57 +00:00
|
|
|
pub const PSR = extern union {
|
2022-01-02 19:01:11 +00:00
|
|
|
mode: Bitfield(u32, 0, 5),
|
|
|
|
t: Bit(u32, 5),
|
|
|
|
f: Bit(u32, 6),
|
|
|
|
i: Bit(u32, 7),
|
|
|
|
v: Bit(u32, 28),
|
|
|
|
c: Bit(u32, 29),
|
|
|
|
z: Bit(u32, 30),
|
|
|
|
n: Bit(u32, 31),
|
2022-01-04 04:25:11 +00:00
|
|
|
raw: u32,
|
2022-11-16 14:21:40 +00:00
|
|
|
|
|
|
|
fn toString(self: PSR) void {
|
|
|
|
std.debug.print("[", .{});
|
|
|
|
|
|
|
|
if (self.n.read()) std.debug.print("N", .{}) else std.debug.print("-", .{});
|
|
|
|
if (self.z.read()) std.debug.print("Z", .{}) else std.debug.print("-", .{});
|
|
|
|
if (self.c.read()) std.debug.print("C", .{}) else std.debug.print("-", .{});
|
|
|
|
if (self.v.read()) std.debug.print("V", .{}) else std.debug.print("-", .{});
|
|
|
|
if (self.i.read()) std.debug.print("I", .{}) else std.debug.print("-", .{});
|
|
|
|
if (self.f.read()) std.debug.print("F", .{}) else std.debug.print("-", .{});
|
|
|
|
if (self.t.read()) std.debug.print("T", .{}) else std.debug.print("-", .{});
|
|
|
|
std.debug.print("|", .{});
|
|
|
|
if (getMode(self.mode.read())) |m| std.debug.print("{s}", .{m.toString()}) else std.debug.print("---", .{});
|
|
|
|
|
|
|
|
std.debug.print("]\n", .{});
|
|
|
|
}
|
2021-12-29 21:09:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const Mode = enum(u5) {
|
|
|
|
User = 0b10000,
|
2022-01-25 15:14:15 +00:00
|
|
|
Fiq = 0b10001,
|
|
|
|
Irq = 0b10010,
|
2021-12-29 21:09:00 +00:00
|
|
|
Supervisor = 0b10011,
|
|
|
|
Abort = 0b10111,
|
|
|
|
Undefined = 0b11011,
|
|
|
|
System = 0b11111,
|
|
|
|
|
2022-11-16 14:21:40 +00:00
|
|
|
fn toString(self: Mode) []const u8 {
|
|
|
|
return switch (self) {
|
|
|
|
.User => "usr",
|
|
|
|
.Fiq => "fiq",
|
|
|
|
.Irq => "irq",
|
|
|
|
.Supervisor => "svc",
|
|
|
|
.Abort => "abt",
|
|
|
|
.Undefined => "und",
|
|
|
|
.System => "sys",
|
|
|
|
};
|
|
|
|
}
|
2022-02-12 08:33:32 +00:00
|
|
|
};
|
|
|
|
|
2022-02-04 20:54:57 +00:00
|
|
|
fn getMode(bits: u5) ?Mode {
|
2022-02-01 22:38:09 +00:00
|
|
|
return std.meta.intToEnum(Mode, bits) catch null;
|
2022-01-19 11:29:49 +00:00
|
|
|
}
|
|
|
|
|
2022-02-04 20:54:57 +00:00
|
|
|
fn getModeChecked(cpu: *const Arm7tdmi, bits: u5) Mode {
|
2022-02-17 05:23:35 +00:00
|
|
|
return getMode(bits) orelse cpu.panic("[CPU/CPSR] 0b{b:0>5} is an invalid CPU mode", .{bits});
|
2022-02-04 20:54:57 +00:00
|
|
|
}
|