Compare commits
No commits in common. "f2cc0721c7b646f2edbeac61cb94c5f008d1821b" and "c660ca8922da3db3d5589ca4d86bb89486a6fcd9" have entirely different histories.
f2cc0721c7
...
c660ca8922
113
src/cpu.zig
113
src/cpu.zig
|
@ -1,5 +1,4 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const util = @import("util.zig");
|
|
||||||
const Bus = @import("bus.zig").Bus;
|
const Bus = @import("bus.zig").Bus;
|
||||||
const Scheduler = @import("scheduler.zig").Scheduler;
|
const Scheduler = @import("scheduler.zig").Scheduler;
|
||||||
|
|
||||||
|
@ -17,11 +16,12 @@ pub const ARM7TDMI = struct {
|
||||||
cpsr: CPSR,
|
cpsr: CPSR,
|
||||||
|
|
||||||
pub fn new(scheduler: *Scheduler, bus: *Bus) @This() {
|
pub fn new(scheduler: *Scheduler, bus: *Bus) @This() {
|
||||||
|
const cpsr: u32 = 0x0000_00DF;
|
||||||
return .{
|
return .{
|
||||||
.r = [_]u32{0x00} ** 16,
|
.r = [_]u32{0x00} ** 16,
|
||||||
.sch = scheduler,
|
.sch = scheduler,
|
||||||
.bus = bus,
|
.bus = bus,
|
||||||
.cpsr = .{ .inner = 0x0000_00DF },
|
.cpsr = @bitCast(CPSR, cpsr),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ pub const ARM7TDMI = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
fn armIdx(opcode: u32) u12 {
|
fn armIdx(opcode: u32) u12 {
|
||||||
return @truncate(u12, opcode >> 20 & 0xFF) << 4 | @truncate(u12, opcode >> 4 & 0xF);
|
return @truncate(u12, opcode >> 20 & 0xFF) << 4 | @truncate(u12, opcode >> 8 & 0xF);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn populate() [0x1000]InstrFn {
|
fn populate() [0x1000]InstrFn {
|
||||||
|
@ -65,18 +65,29 @@ fn populate() [0x1000]InstrFn {
|
||||||
lut[i] = comptimeDataProcessing(I, S, instrKind);
|
lut[i] = comptimeDataProcessing(I, S, instrKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i >> 9 & 0x7 == 0b000 and i >> 3 & 0x01 == 0x01 and i & 0x01 == 0x01) {
|
if (i >> 9 & 0x7 == 0b000 and i >> 6 & 0x01 == 0x00 and i & 0xF == 0x0) {
|
||||||
// Halfword and Signed Data Transfer with register offset
|
// Halfword and Signed Data Transfer with register offset
|
||||||
const P = i >> 8 & 0x01 == 0x01;
|
const P = i >> 8 & 0x01 == 0x01;
|
||||||
const U = i >> 7 & 0x01 == 0x01;
|
const U = i >> 7 & 0x01 == 0x01;
|
||||||
const I = i >> 6 & 0x01 == 0x01;
|
const I = true;
|
||||||
const W = i >> 5 & 0x01 == 0x01;
|
const W = i >> 5 & 0x01 == 0x01;
|
||||||
const L = i >> 4 & 0x01 == 0x01;
|
const L = i >> 4 & 0x01 == 0x01;
|
||||||
|
|
||||||
lut[i] = comptimeHalfSignedDataTransfer(P, U, I, W, L);
|
lut[i] = comptimeHalfSignedDataTransfer(P, U, I, W, L);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i >> 10 & 0x3 == 0b01) {
|
if (i >> 9 & 0x7 == 0b000 and i >> 6 & 0x01 == 0x01) {
|
||||||
|
// Halfword and Signed Data Tranfer with immediate offset
|
||||||
|
const P = i >> 8 & 0x01 == 0x01;
|
||||||
|
const U = i >> 7 & 0x01 == 0x01;
|
||||||
|
const I = false;
|
||||||
|
const W = i >> 5 & 0x01 == 0x01;
|
||||||
|
const L = i >> 4 & 0x01 == 0x01;
|
||||||
|
|
||||||
|
lut[i] = comptimeHalfSignedDataTransfer(P, U, I, W, L);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >> 10 & 0x3 == 0b01 and i & 0x01 == 0x00) {
|
||||||
const I = i >> 9 & 0x01 == 0x01;
|
const I = i >> 9 & 0x01 == 0x01;
|
||||||
const P = i >> 8 & 0x01 == 0x01;
|
const P = i >> 8 & 0x01 == 0x01;
|
||||||
const U = i >> 7 & 0x01 == 0x01;
|
const U = i >> 7 & 0x01 == 0x01;
|
||||||
|
@ -97,85 +108,22 @@ fn populate() [0x1000]InstrFn {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const CPSR = struct {
|
const CPSR = packed struct {
|
||||||
inner: u32,
|
n: bool, // Negative / Less Than
|
||||||
|
z: bool, // Zero
|
||||||
pub fn n(self: *const @This()) bool {
|
c: bool, // Carry / Borrow / Extend
|
||||||
return self.inner >> 31 & 0x01 == 0x01;
|
v: bool, // Overflow
|
||||||
}
|
_: u20,
|
||||||
|
i: bool, // IRQ Disable
|
||||||
pub fn set_n(self: *@This(), set: bool) void {
|
f: bool, // FIQ Diable
|
||||||
self.set_bit(31, set);
|
t: bool, // State
|
||||||
}
|
m: Mode, // Mode
|
||||||
|
|
||||||
pub fn z(self: *const @This()) bool {
|
|
||||||
return self.inner >> 30 & 0x01 == 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_z(self: *@This(), set: bool) void {
|
|
||||||
self.set_bit(30, set);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn c(self: *const @This()) bool {
|
|
||||||
return self.inner >> 29 & 0x01 == 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_c(self: *@This(), set: bool) void {
|
|
||||||
self.set_bit(29, set);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn v(self: *const @This()) bool {
|
|
||||||
return self.inner >> 28 & 0x01 == 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_v(self: *@This(), set: bool) void {
|
|
||||||
self.set_bit(28, set);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn i(self: *const @This()) bool {
|
|
||||||
return self.inner >> 7 & 0x01 == 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_i(self: *@This(), set: bool) void {
|
|
||||||
self.set_bit(7, set);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn f(self: *const @This()) bool {
|
|
||||||
return self.inner >> 6 & 0x01 == 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_f(self: *@This(), set: bool) void {
|
|
||||||
self.set_bit(6, set);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn t(self: *const @This()) bool {
|
|
||||||
return self.inner >> 5 & 0x01 == 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_t(self: *@This(), set: bool) void {
|
|
||||||
self.set_bit(5, set);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mode(self: *const @This()) Mode {
|
|
||||||
return self.inner & 0x1F;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_mode(_: *@This(), _: Mode) void {
|
|
||||||
std.debug.panic("TODO: Implement set_mode for CPSR", .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_bit(self: *@This(), comptime bit: usize, set: bool) void {
|
|
||||||
const set_val = @as(u32, @boolToInt(set)) << bit;
|
|
||||||
const mask = ~(@as(u32, 1) << bit);
|
|
||||||
|
|
||||||
self.inner = (self.inner & mask) | set_val;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Mode = enum(u5) {
|
const Mode = enum(u5) {
|
||||||
User = 0b10000,
|
User = 0b10000,
|
||||||
FIQ = 0b10001,
|
Fiq = 0b10001,
|
||||||
IRQ = 0b10010,
|
Irq = 0b10010,
|
||||||
Supervisor = 0b10011,
|
Supervisor = 0b10011,
|
||||||
Abort = 0b10111,
|
Abort = 0b10111,
|
||||||
Undefined = 0b11011,
|
Undefined = 0b11011,
|
||||||
|
@ -194,7 +142,8 @@ fn comptimeBranch(comptime L: bool) InstrFn {
|
||||||
cpu.r[14] = cpu.r[15] - 4;
|
cpu.r[14] = cpu.r[15] - 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu.r[15] = cpu.fakePC() + util.u32SignExtend(24, opcode << 2);
|
const offset = @bitCast(i32, (opcode << 2) << 8) >> 8;
|
||||||
|
cpu.r[15] = cpu.fakePC() + @bitCast(u32, offset);
|
||||||
}
|
}
|
||||||
}.branch;
|
}.branch;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,32 +20,15 @@ pub fn comptimeDataProcessing(comptime I: bool, comptime S: bool, comptime instr
|
||||||
|
|
||||||
switch (instrKind) {
|
switch (instrKind) {
|
||||||
0x4 => {
|
0x4 => {
|
||||||
// ADD
|
|
||||||
cpu.r[rd] = cpu.r[op1] + op2;
|
cpu.r[rd] = cpu.r[op1] + op2;
|
||||||
|
|
||||||
if (S) std.debug.panic("TODO: implement ADD condition codes", .{});
|
if (S) std.debug.panic("TODO: implement ADD condition codes", .{});
|
||||||
},
|
},
|
||||||
0xD => {
|
0xD => {
|
||||||
// MOV
|
|
||||||
cpu.r[rd] = op2;
|
cpu.r[rd] = op2;
|
||||||
|
|
||||||
if (S) std.debug.panic("TODO: implement MOV condition codes", .{});
|
if (S) std.debug.panic("TODO: implement MOV condition codes", .{});
|
||||||
},
|
},
|
||||||
0xA => {
|
|
||||||
// CMP
|
|
||||||
var result: u32 = undefined;
|
|
||||||
|
|
||||||
const op1_val = cpu.r[op1];
|
|
||||||
const v_ctx = (op1_val >> 31 == 0x01) or (op2 >> 31 == 0x01);
|
|
||||||
|
|
||||||
const didOverflow = @subWithOverflow(u32, op1_val, op2, &result);
|
|
||||||
|
|
||||||
cpu.cpsr.set_v(v_ctx and (result >> 31 & 0x01 == 0x01));
|
|
||||||
cpu.cpsr.set_c(didOverflow);
|
|
||||||
cpu.cpsr.set_z(result == 0x00);
|
|
||||||
cpu.cpsr.set_n(result >> 31 & 0x01 == 0x01);
|
|
||||||
|
|
||||||
},
|
|
||||||
else => std.debug.panic("TODO: implement data processing type {}", .{instrKind}),
|
else => std.debug.panic("TODO: implement data processing type {}", .{instrKind}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,12 +40,12 @@ pub fn comptimeHalfSignedDataTransfer(comptime P: bool, comptime U: bool, compti
|
||||||
0b10 => {
|
0b10 => {
|
||||||
// LDRSB
|
// LDRSB
|
||||||
const byte = bus.readByte(address);
|
const byte = bus.readByte(address);
|
||||||
cpu.r[rd] = util.u32SignExtend(8, @as(u32, byte));
|
cpu.r[rd] = util.u32_sign_extend(@as(u32, byte), 8);
|
||||||
},
|
},
|
||||||
0b11 => {
|
0b11 => {
|
||||||
// LDRSH
|
// LDRSH
|
||||||
const halfword = bus.readHalfWord(address);
|
const halfword = bus.readHalfWord(address);
|
||||||
cpu.r[rd] = util.u32SignExtend(16, @as(u32, halfword));
|
cpu.r[rd] = util.u32_sign_extend(@as(u32, halfword), 16);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
30
src/util.zig
30
src/util.zig
|
@ -1,30 +1,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const assert = std.debug.assert;
|
|
||||||
|
|
||||||
pub fn signExtend(comptime T: type, comptime bits: usize, value: anytype) T {
|
pub fn u32_sign_extend(value: u32, bitSize: anytype) u32 {
|
||||||
const ValT = comptime @TypeOf(value);
|
const amount: u5 = 32 - bitSize;
|
||||||
comptime assert(isInteger(ValT));
|
return @bitCast(u32, @bitCast(i32, value << amount) >> amount);
|
||||||
comptime assert(isSigned(ValT));
|
|
||||||
|
|
||||||
const value_bits = @typeInfo(ValT).Int.bits;
|
|
||||||
comptime assert(value_bits >= bits);
|
|
||||||
|
|
||||||
const bit_diff = value_bits - bits;
|
|
||||||
|
|
||||||
// (1 << bits) -1 is a mask that will take values like 0x100 and make them 0xFF
|
|
||||||
// value & mask so that only the relevant bits are sign extended
|
|
||||||
// therefore, value & ((1 << bits) - 1) is the isolation of the relevant bits
|
|
||||||
return ((value & ((1 << bits) - 1)) << bit_diff) >> bit_diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn u32SignExtend(comptime bits: usize, value: u32) u32 {
|
|
||||||
return @bitCast(u32, signExtend(i32, bits, @bitCast(i32, value)));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn isInteger(comptime T: type) bool {
|
|
||||||
return @typeInfo(T) == .Int;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn isSigned(comptime T: type) bool {
|
|
||||||
return @typeInfo(T).Int.signedness == .signed;
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue