chore: use bitfield library

This commit is contained in:
Rekai Nyangadzayi Musuka 2022-01-02 13:01:11 -06:00
parent e144261e07
commit de9045fba3
3 changed files with 181 additions and 92 deletions

View File

@ -1,7 +1,11 @@
const std = @import("std"); const std = @import("std");
const util = @import("util.zig"); const util = @import("util.zig");
const bitfield = @import("util/bitfield.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;
const Bitfield = bitfield.Bitfield;
const Bit = bitfield.Bit;
const comptimeDataProcessing = @import("cpu/data_processing.zig").comptimeDataProcessing; const comptimeDataProcessing = @import("cpu/data_processing.zig").comptimeDataProcessing;
const comptimeSingleDataTransfer = @import("cpu/single_data_transfer.zig").comptimeSingleDataTransfer; const comptimeSingleDataTransfer = @import("cpu/single_data_transfer.zig").comptimeSingleDataTransfer;
@ -21,7 +25,7 @@ pub const Arm7tdmi = struct {
.r = [_]u32{0x00} ** 16, .r = [_]u32{0x00} ** 16,
.sch = scheduler, .sch = scheduler,
.bus = bus, .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 { fn checkCond(cpsr: *const CPSR, opcode: u32) bool {
// TODO: Should I implement an enum? // TODO: Should I implement an enum?
return switch (@truncate(u4, opcode >> 28)) { return switch (@truncate(u4, opcode >> 28)) {
0x0 => cpsr.z(), // EQ - Equal 0x0 => cpsr.z.read(), // EQ - Equal
0x1 => !cpsr.z(), // NEQ - Not equal 0x1 => !cpsr.z.read(), // NEQ - Not equal
0x2 => cpsr.c(), // CS - Unsigned higher or same 0x2 => cpsr.c.read(), // CS - Unsigned higher or same
0x3 => !cpsr.c(), // CC - Unsigned lower 0x3 => !cpsr.c.read(), // CC - Unsigned lower
0x4 => cpsr.n(), // MI - Negative 0x4 => cpsr.n.read(), // MI - Negative
0x5 => !cpsr.n(), // PL - Positive or zero 0x5 => !cpsr.n.read(), // PL - Positive or zero
0x6 => cpsr.v(), // VS - Overflow 0x6 => cpsr.v.read(), // VS - Overflow
0x7 => !cpsr.v(), // VC - No overflow 0x7 => !cpsr.v.read(), // VC - No overflow
0x8 => cpsr.c() and !cpsr.z(), // HI - unsigned higher 0x8 => cpsr.c.read() and !cpsr.z.read(), // HI - unsigned higher
0x9 => !cpsr.c() and cpsr.z(), // LS - unsigned lower or same 0x9 => !cpsr.c.read() and cpsr.z.read(), // LS - unsigned lower or same
0xA => cpsr.n() == cpsr.v(), // GE - Greater or equal 0xA => cpsr.n.read() == cpsr.v.read(), // GE - Greater or equal
0xB => cpsr.n() != cpsr.v(), // LT - Less than 0xB => cpsr.n.read() != cpsr.v.read(), // LT - Less than
0xC => !cpsr.z() and (cpsr.n() == cpsr.z()), // GT - Greater than 0xC => !cpsr.z.read() and (cpsr.n.read() == cpsr.z.read()), // GT - Greater than
0xD => cpsr.z() or (cpsr.n() != cpsr.v()), // LE - Less than or equal 0xD => cpsr.z.read() or (cpsr.n.read() != cpsr.v.read()), // LE - Less than or equal
0xE => true, // AL - Always 0xE => true, // AL - Always
0xF => std.debug.panic("0xF is a reserved condition field", .{}), 0xF => std.debug.panic("0xF is a reserved condition field", .{}),
}; };
@ -117,79 +121,16 @@ fn populate() [0x1000]InstrFn {
}; };
} }
const CPSR = struct { const CPSR = extern union {
inner: u32, mode: Bitfield(u32, 0, 5),
t: Bit(u32, 5),
pub fn n(self: *const @This()) bool { f: Bit(u32, 6),
return self.inner >> 31 & 1 == 1; i: Bit(u32, 7),
} v: Bit(u32, 28),
c: Bit(u32, 29),
pub fn setN(self: *@This(), set: bool) void { z: Bit(u32, 30),
self.setBit(31, set); n: Bit(u32, 31),
} val: u32,
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 Mode = enum(u5) { const Mode = enum(u5) {

View File

@ -40,10 +40,10 @@ pub fn comptimeDataProcessing(comptime I: bool, comptime S: bool, comptime instr
const didOverflow = @subWithOverflow(u32, op1_val, op2, &result); const didOverflow = @subWithOverflow(u32, op1_val, op2, &result);
cpu.cpsr.setV(v_ctx and (result >> 31 & 0x01 == 0x01)); cpu.cpsr.v.write(v_ctx and (result >> 31 & 0x01 == 0x01));
cpu.cpsr.setC(didOverflow); cpu.cpsr.c.write(didOverflow);
cpu.cpsr.setZ(result == 0x00); cpu.cpsr.z.write(result == 0x00);
cpu.cpsr.setN(result >> 31 & 0x01 == 0x01); cpu.cpsr.n.write(result >> 31 & 0x01 == 0x01);
}, },
else => std.debug.panic("TODO: implement data processing type {}", .{instrKind}), else => std.debug.panic("TODO: implement data processing type {}", .{instrKind}),
} }

148
src/util/bitfield.zig Normal file
View File

@ -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);
}