diff --git a/src/cpu.zig b/src/cpu.zig index 97cb45c..452d602 100644 --- a/src/cpu.zig +++ b/src/cpu.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const util = @import("util.zig"); const Bus = @import("bus.zig").Bus; const Scheduler = @import("scheduler.zig").Scheduler; @@ -16,12 +17,11 @@ pub const ARM7TDMI = struct { cpsr: CPSR, pub fn new(scheduler: *Scheduler, bus: *Bus) @This() { - const cpsr: u32 = 0x0000_00DF; return .{ .r = [_]u32{0x00} ** 16, .sch = scheduler, .bus = bus, - .cpsr = @bitCast(CPSR, cpsr), + .cpsr = .{ .inner = 0x0000_00DF }, }; } @@ -47,7 +47,7 @@ pub const ARM7TDMI = struct { }; fn armIdx(opcode: u32) u12 { - return @truncate(u12, opcode >> 20 & 0xFF) << 4 | @truncate(u12, opcode >> 8 & 0xF); + return @truncate(u12, opcode >> 20 & 0xFF) << 4 | @truncate(u12, opcode >> 4 & 0xF); } fn populate() [0x1000]InstrFn { @@ -65,29 +65,18 @@ fn populate() [0x1000]InstrFn { lut[i] = comptimeDataProcessing(I, S, instrKind); } - if (i >> 9 & 0x7 == 0b000 and i >> 6 & 0x01 == 0x00 and i & 0xF == 0x0) { + if (i >> 9 & 0x7 == 0b000 and i >> 3 & 0x01 == 0x01 and i & 0x01 == 0x01) { // Halfword and Signed Data Transfer with register offset const P = i >> 8 & 0x01 == 0x01; const U = i >> 7 & 0x01 == 0x01; - const I = true; + const I = i >> 6 & 0x01 == 0x01; const W = i >> 5 & 0x01 == 0x01; const L = i >> 4 & 0x01 == 0x01; lut[i] = comptimeHalfSignedDataTransfer(P, U, I, W, L); } - 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) { + if (i >> 10 & 0x3 == 0b01) { const I = i >> 9 & 0x01 == 0x01; const P = i >> 8 & 0x01 == 0x01; const U = i >> 7 & 0x01 == 0x01; @@ -108,22 +97,85 @@ fn populate() [0x1000]InstrFn { }; } -const CPSR = packed struct { - n: bool, // Negative / Less Than - z: bool, // Zero - c: bool, // Carry / Borrow / Extend - v: bool, // Overflow - _: u20, - i: bool, // IRQ Disable - f: bool, // FIQ Diable - t: bool, // State - m: Mode, // Mode +const CPSR = struct { + inner: u32, + + pub fn n(self: *const @This()) bool { + return self.inner >> 31 & 0x01 == 0x01; + } + + pub fn set_n(self: *@This(), set: bool) void { + self.set_bit(31, set); + } + + 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) { User = 0b10000, - Fiq = 0b10001, - Irq = 0b10010, + FIQ = 0b10001, + IRQ = 0b10010, Supervisor = 0b10011, Abort = 0b10111, Undefined = 0b11011, @@ -142,8 +194,7 @@ fn comptimeBranch(comptime L: bool) InstrFn { cpu.r[14] = cpu.r[15] - 4; } - const offset = @bitCast(i32, (opcode << 2) << 8) >> 8; - cpu.r[15] = cpu.fakePC() + @bitCast(u32, offset); + cpu.r[15] = cpu.fakePC() + util.u32SignExtend(24, opcode << 2); } }.branch; } diff --git a/src/cpu/data_processing.zig b/src/cpu/data_processing.zig index 0052edb..07dccdf 100644 --- a/src/cpu/data_processing.zig +++ b/src/cpu/data_processing.zig @@ -20,15 +20,32 @@ pub fn comptimeDataProcessing(comptime I: bool, comptime S: bool, comptime instr switch (instrKind) { 0x4 => { + // ADD cpu.r[rd] = cpu.r[op1] + op2; if (S) std.debug.panic("TODO: implement ADD condition codes", .{}); }, 0xD => { + // MOV cpu.r[rd] = op2; 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}), } } diff --git a/src/cpu/half_signed_data_transfer.zig b/src/cpu/half_signed_data_transfer.zig index d567bb4..271b0b5 100644 --- a/src/cpu/half_signed_data_transfer.zig +++ b/src/cpu/half_signed_data_transfer.zig @@ -40,12 +40,12 @@ pub fn comptimeHalfSignedDataTransfer(comptime P: bool, comptime U: bool, compti 0b10 => { // LDRSB const byte = bus.readByte(address); - cpu.r[rd] = util.u32_sign_extend(@as(u32, byte), 8); + cpu.r[rd] = util.u32SignExtend(8, @as(u32, byte)); }, 0b11 => { // LDRSH const halfword = bus.readHalfWord(address); - cpu.r[rd] = util.u32_sign_extend(@as(u32, halfword), 16); + cpu.r[rd] = util.u32SignExtend(16, @as(u32, halfword)); }, } } else { diff --git a/src/util.zig b/src/util.zig index 88a19c1..a34eed6 100644 --- a/src/util.zig +++ b/src/util.zig @@ -1,6 +1,30 @@ const std = @import("std"); +const assert = std.debug.assert; -pub fn u32_sign_extend(value: u32, bitSize: anytype) u32 { - const amount: u5 = 32 - bitSize; - return @bitCast(u32, @bitCast(i32, value << amount) >> amount); +pub fn signExtend(comptime T: type, comptime bits: usize, value: anytype) T { + const ValT = comptime @TypeOf(value); + comptime assert(isInteger(ValT)); + 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; }