From 7016fcdb79026945e3855085f29dce50794eb7f9 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Fri, 21 Oct 2022 05:11:45 -0300 Subject: [PATCH] chore: use bitfield library --- src/cpu.zig | 117 +++++++--------------------- src/cpu/data_processing.zig | 8 +- src/util/bitfield.zig | 148 ++++++++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+), 92 deletions(-) create mode 100644 src/util/bitfield.zig diff --git a/src/cpu.zig b/src/cpu.zig index af3487d..e6844d7 100644 --- a/src/cpu.zig +++ b/src/cpu.zig @@ -1,7 +1,11 @@ const std = @import("std"); const util = @import("util.zig"); +const bitfield = @import("util/bitfield.zig"); + const Bus = @import("bus.zig").Bus; const Scheduler = @import("scheduler.zig").Scheduler; +const Bitfield = bitfield.Bitfield; +const Bit = bitfield.Bit; const comptimeDataProcessing = @import("cpu/data_processing.zig").comptimeDataProcessing; const comptimeSingleDataTransfer = @import("cpu/single_data_transfer.zig").comptimeSingleDataTransfer; @@ -21,7 +25,7 @@ pub const Arm7tdmi = struct { .r = [_]u32{0x00} ** 16, .sch = scheduler, .bus = bus, - .cpsr = .{ .inner = 0x0000_00DF }, + .cpsr = .{ .val = 0x0000_00DF }, }; } @@ -52,20 +56,20 @@ fn armIdx(opcode: u32) u12 { fn checkCond(cpsr: *const CPSR, opcode: u32) bool { // TODO: Should I implement an enum? return switch (@truncate(u4, opcode >> 28)) { - 0x0 => cpsr.z(), // EQ - Equal - 0x1 => !cpsr.z(), // NEQ - Not equal - 0x2 => cpsr.c(), // CS - Unsigned higher or same - 0x3 => !cpsr.c(), // CC - Unsigned lower - 0x4 => cpsr.n(), // MI - Negative - 0x5 => !cpsr.n(), // PL - Positive or zero - 0x6 => cpsr.v(), // VS - Overflow - 0x7 => !cpsr.v(), // VC - No overflow - 0x8 => cpsr.c() and !cpsr.z(), // HI - unsigned higher - 0x9 => !cpsr.c() and cpsr.z(), // LS - unsigned lower or same - 0xA => cpsr.n() == cpsr.v(), // GE - Greater or equal - 0xB => cpsr.n() != cpsr.v(), // LT - Less than - 0xC => !cpsr.z() and (cpsr.n() == cpsr.z()), // GT - Greater than - 0xD => cpsr.z() or (cpsr.n() != cpsr.v()), // LE - Less than or equal + 0x0 => cpsr.z.read(), // EQ - Equal + 0x1 => !cpsr.z.read(), // NEQ - Not equal + 0x2 => cpsr.c.read(), // CS - Unsigned higher or same + 0x3 => !cpsr.c.read(), // CC - Unsigned lower + 0x4 => cpsr.n.read(), // MI - Negative + 0x5 => !cpsr.n.read(), // PL - Positive or zero + 0x6 => cpsr.v.read(), // VS - Overflow + 0x7 => !cpsr.v.read(), // VC - No overflow + 0x8 => cpsr.c.read() and !cpsr.z.read(), // HI - unsigned higher + 0x9 => !cpsr.c.read() and cpsr.z.read(), // LS - unsigned lower or same + 0xA => cpsr.n.read() == cpsr.v.read(), // GE - Greater or equal + 0xB => cpsr.n.read() != cpsr.v.read(), // LT - Less than + 0xC => !cpsr.z.read() and (cpsr.n.read() == cpsr.z.read()), // GT - Greater than + 0xD => cpsr.z.read() or (cpsr.n.read() != cpsr.v.read()), // LE - Less than or equal 0xE => true, // AL - Always 0xF => std.debug.panic("0xF is a reserved condition field", .{}), }; @@ -117,79 +121,16 @@ fn populate() [0x1000]InstrFn { }; } -const CPSR = struct { - inner: u32, - - pub fn n(self: *const @This()) bool { - return self.inner >> 31 & 1 == 1; - } - - pub fn setN(self: *@This(), set: bool) void { - self.setBit(31, set); - } - - pub fn z(self: *const @This()) bool { - return self.inner >> 30 & 1 == 1; - } - - pub fn setZ(self: *@This(), set: bool) void { - self.setBit(30, set); - } - - pub fn c(self: *const @This()) bool { - return self.inner >> 29 & 1 == 1; - } - - pub fn setC(self: *@This(), set: bool) void { - self.setBit(29, set); - } - - pub fn v(self: *const @This()) bool { - return self.inner >> 28 & 1 == 1; - } - - pub fn setV(self: *@This(), set: bool) void { - self.setBit(28, set); - } - - pub fn i(self: *const @This()) bool { - return self.inner >> 7 & 1 == 1; - } - - pub fn setI(self: *@This(), set: bool) void { - self.setBit(7, set); - } - - pub fn f(self: *const @This()) bool { - return self.inner >> 6 & 1 == 1; - } - - pub fn setF(self: *@This(), set: bool) void { - self.setBit(6, set); - } - - pub fn t(self: *const @This()) bool { - return self.inner >> 5 & 1 == 1; - } - - pub fn setT(self: *@This(), set: bool) void { - self.setBit(5, set); - } - - pub fn mode(self: *const @This()) Mode { - return self.inner & 0x1F; - } - - pub fn setMode(_: *@This(), _: Mode) void { - std.debug.panic("TODO: Implement set_mode for CPSR", .{}); - } - - fn setBit(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 CPSR = extern union { + 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), + val: u32, }; const Mode = enum(u5) { diff --git a/src/cpu/data_processing.zig b/src/cpu/data_processing.zig index 04291dc..f406029 100644 --- a/src/cpu/data_processing.zig +++ b/src/cpu/data_processing.zig @@ -40,10 +40,10 @@ pub fn comptimeDataProcessing(comptime I: bool, comptime S: bool, comptime instr const didOverflow = @subWithOverflow(u32, op1_val, op2, &result); - cpu.cpsr.setV(v_ctx and (result >> 31 & 0x01 == 0x01)); - cpu.cpsr.setC(didOverflow); - cpu.cpsr.setZ(result == 0x00); - cpu.cpsr.setN(result >> 31 & 0x01 == 0x01); + cpu.cpsr.v.write(v_ctx and (result >> 31 & 0x01 == 0x01)); + cpu.cpsr.c.write(didOverflow); + cpu.cpsr.z.write(result == 0x00); + cpu.cpsr.n.write(result >> 31 & 0x01 == 0x01); }, else => std.debug.panic("TODO: implement data processing type {}", .{instrKind}), } diff --git a/src/util/bitfield.zig b/src/util/bitfield.zig new file mode 100644 index 0000000..7253dc3 --- /dev/null +++ b/src/util/bitfield.zig @@ -0,0 +1,148 @@ +// source + credit: https://github.com/FlorenceOS/Florence/blob/master/lib/util/bitfields.zig -- + +const std = @import("std"); + +fn PtrCastPreserveCV(comptime T: type, comptime PtrToT: type, comptime NewT: type) type { + return switch (PtrToT) { + *T => *NewT, + *const T => *const NewT, + *volatile T => *volatile NewT, + *const volatile T => *const volatile NewT, + + else => @compileError("wtf you doing"), + }; +} + +fn BitType(comptime FieldType: type, comptime ValueType: type, comptime shamt: usize) type { + const self_bit: FieldType = (1 << shamt); + + return struct { + bits: Bitfield(FieldType, shamt, 1), + + pub fn set(self: anytype) void { + self.bits.field().* |= self_bit; + } + + pub fn unset(self: anytype) void { + self.bits.field().* &= ~self_bit; + } + + pub fn read(self: anytype) ValueType { + return @bitCast(ValueType, @truncate(u1, self.bits.field().* >> shamt)); + } + + // Since these are mostly used with MMIO, I want to avoid + // reading the memory just to write it again, also races + pub fn write(self: anytype, val: ValueType) void { + if (@bitCast(bool, val)) { + self.set(); + } else { + self.unset(); + } + } + }; +} + +// Original Bit Constructor +// pub fn Bit(comptime FieldType: type, comptime shamt: usize) type { +// return BitType(FieldType, u1, shamt); +// } + +pub fn Bit(comptime FieldType: type, comptime shamt: usize) type { + return BitType(FieldType, bool, shamt); +} + +fn Boolean(comptime FieldType: type, comptime shamt: usize) type { + return BitType(FieldType, bool, shamt); +} + +pub fn Bitfield(comptime FieldType: type, comptime shamt: usize, comptime num_bits: usize) type { + if (shamt + num_bits > @bitSizeOf(FieldType)) { + @compileError("bitfield doesn't fit"); + } + + const self_mask: FieldType = ((1 << num_bits) - 1) << shamt; + + const ValueType = std.meta.Int(.unsigned, num_bits); + + return struct { + dummy: FieldType, + + fn field(self: anytype) PtrCastPreserveCV(@This(), @TypeOf(self), FieldType) { + return @ptrCast(PtrCastPreserveCV(@This(), @TypeOf(self), FieldType), self); + } + + pub fn write(self: anytype, val: ValueType) void { + self.field().* &= ~self_mask; + self.field().* |= @intCast(FieldType, val) << shamt; + } + + pub fn read(self: anytype) ValueType { + const val: FieldType = self.field().*; + return @intCast(ValueType, (val & self_mask) >> shamt); + } + }; +} + +test "bit" { + const S = extern union { + low: Bit(u32, 0), + high: Bit(u32, 1), + val: u32, + }; + + std.testing.expect(@sizeOf(S) == 4); + std.testing.expect(@bitSizeOf(S) == 32); + + var s: S = .{ .val = 1 }; + + std.testing.expect(s.low.read() == 1); + std.testing.expect(s.high.read() == 0); + + s.low.write(0); + s.high.write(1); + + std.testing.expect(s.val == 2); +} + +test "boolean" { + const S = extern union { + low: Boolean(u32, 0), + high: Boolean(u32, 1), + val: u32, + }; + + std.testing.expect(@sizeOf(S) == 4); + std.testing.expect(@bitSizeOf(S) == 32); + + var s: S = .{ .val = 2 }; + + std.testing.expect(s.low.read() == false); + std.testing.expect(s.high.read() == true); + + s.low.write(true); + s.high.write(false); + + std.testing.expect(s.val == 1); +} + +test "bitfield" { + const S = extern union { + low: Bitfield(u32, 0, 16), + high: Bitfield(u32, 16, 16), + val: u32, + }; + + std.testing.expect(@sizeOf(S) == 4); + std.testing.expect(@bitSizeOf(S) == 32); + + var s: S = .{ .val = 0x13376969 }; + + std.testing.expect(s.low.read() == 0x6969); + std.testing.expect(s.high.read() == 0x1337); + + s.low.write(0x1337); + s.high.write(0x6969); + + std.testing.expect(s.val == 0x69691337); +}