Skip to content

Commit

Permalink
Add register API for raw bit patterns
Browse files Browse the repository at this point in the history
This is functionally identical to the existing unsigned API, but better indicates intent for values that are known to be bitmasks. It would also align with any future Zig support for raw bit types: see ziglang/zig#7512 and ziglang/zig#8388.
  • Loading branch information
alinebee committed Jan 5, 2022
1 parent 0139ca4 commit dc2b179
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 43 deletions.
21 changes: 10 additions & 11 deletions src/instructions/register_and.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ pub const Instance = struct {
/// The ID of the register to apply the mask to.
destination: RegisterID.Enum,

/// The mask to apply to the value in the register.
value: Register.Mask,
/// The bitmask to apply to the value in the register.
value: Register.BitPattern,

pub fn execute(self: Instance, machine: *Machine.Instance) void {
// Masking must always be done against the unsigned representation of the register value.
const original_value = machine.registers.unsigned(self.destination);
const original_value = machine.registers.bitPattern(self.destination);
const masked_value = original_value & self.value;
machine.registers.setUnsigned(self.destination, masked_value);
machine.registers.setBitPattern(self.destination, masked_value);
}
};

Expand All @@ -26,7 +25,7 @@ pub const Instance = struct {
pub fn parse(_: Opcode.Raw, program: *Program.Instance) Error!Instance {
return Instance{
.destination = RegisterID.parse(try program.read(RegisterID.Raw)),
.value = try program.read(Register.Mask),
.value = try program.read(Register.BitPattern),
};
}

Expand Down Expand Up @@ -55,9 +54,9 @@ test "parse parses valid bytecode and consumes 3 bytes" {

test "execute masks destination register" {
// zig fmt: off
const original_value: Register.Unsigned = 0b1010_0101_1010_0101;
const mask: Register.Mask = 0b1100_0011_1111_0000;
const expected_value: Register.Unsigned = 0b1000_0001_1010_0000;
const original_value: Register.BitPattern = 0b1010_0101_1010_0101;
const mask: Register.BitPattern = 0b1100_0011_1111_0000;
const expected_value: Register.BitPattern = 0b1000_0001_1010_0000;
// zig fmt: on

const instruction = Instance{
Expand All @@ -68,9 +67,9 @@ test "execute masks destination register" {
var machine = Machine.testInstance(null);
defer machine.deinit();

machine.registers.setUnsigned(instruction.destination, original_value);
machine.registers.setBitPattern(instruction.destination, original_value);

instruction.execute(&machine);

try testing.expectEqual(expected_value, machine.registers.unsigned(instruction.destination));
try testing.expectEqual(expected_value, machine.registers.bitPattern(instruction.destination));
}
21 changes: 10 additions & 11 deletions src/instructions/register_or.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ pub const Instance = struct {
/// The ID of the register to apply the mask to.
destination: RegisterID.Enum,

/// The mask to apply to the value in the register.
value: Register.Mask,
/// The bitmask to apply to the value in the register.
value: Register.BitPattern,

pub fn execute(self: Instance, machine: *Machine.Instance) void {
// Masking must always be done against the unsigned representation of the register value.
const original_value = machine.registers.unsigned(self.destination);
const original_value = machine.registers.bitPattern(self.destination);
const masked_value = original_value | self.value;
machine.registers.setUnsigned(self.destination, masked_value);
machine.registers.setBitPattern(self.destination, masked_value);
}
};

Expand All @@ -26,7 +25,7 @@ pub const Instance = struct {
pub fn parse(_: Opcode.Raw, program: *Program.Instance) Error!Instance {
return Instance{
.destination = RegisterID.parse(try program.read(RegisterID.Raw)),
.value = try program.read(Register.Mask),
.value = try program.read(Register.BitPattern),
};
}

Expand Down Expand Up @@ -55,9 +54,9 @@ test "parse parses valid bytecode and consumes 3 bytes" {

test "execute masks destination register" {
// zig fmt: off
const original_value: Register.Unsigned = 0b1010_0101_1010_0101;
const mask: Register.Mask = 0b1100_0011_1111_0000;
const expected_value: Register.Unsigned = 0b1110_0111_1111_0101;
const original_value: Register.BitPattern = 0b1010_0101_1010_0101;
const mask: Register.BitPattern = 0b1100_0011_1111_0000;
const expected_value: Register.BitPattern = 0b1110_0111_1111_0101;
// zig fmt: on

const instruction = Instance{
Expand All @@ -68,9 +67,9 @@ test "execute masks destination register" {
var machine = Machine.testInstance(null);
defer machine.deinit();

machine.registers.setUnsigned(instruction.destination, original_value);
machine.registers.setBitPattern(instruction.destination, original_value);

instruction.execute(&machine);

try testing.expectEqual(expected_value, machine.registers.unsigned(instruction.destination));
try testing.expectEqual(expected_value, machine.registers.bitPattern(instruction.destination));
}
15 changes: 8 additions & 7 deletions src/instructions/register_shift_left.zig
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ pub const Instance = struct {

pub fn execute(self: Instance, machine: *Machine.Instance) void {
// Zig is currently happy to << and >> signed values without respecting their sign bit,
// but that doesn't seem safe and may go away in future. To be sure, treat the value as unsigned.
const original_value = machine.registers.unsigned(self.destination);
// but that doesn't seem safe and may go away in future.
// To be sure, treat the value as a raw bit pattern.
const original_value = machine.registers.bitPattern(self.destination);
const shifted_value = original_value << self.shift;
machine.registers.setUnsigned(self.destination, shifted_value);
machine.registers.setBitPattern(self.destination, shifted_value);
}
};

Expand Down Expand Up @@ -83,9 +84,9 @@ test "parse returns error.ShiftTooLarge and consumes 4 bytes on invalid shift di

test "execute shifts destination register" {
// zig fmt: off
const original_value: Register.Unsigned = 0b0000_1111_1111_0000;
const original_value: Register.BitPattern = 0b0000_1111_1111_0000;
const shift: Register.Shift = 3;
const expected_value: Register.Unsigned = 0b0111_1111_1000_0000;
const expected_value: Register.BitPattern = 0b0111_1111_1000_0000;
// zig fmt: on

const instruction = Instance{
Expand All @@ -96,9 +97,9 @@ test "execute shifts destination register" {
var machine = Machine.testInstance(null);
defer machine.deinit();

machine.registers.setUnsigned(instruction.destination, original_value);
machine.registers.setBitPattern(instruction.destination, original_value);

instruction.execute(&machine);

try testing.expectEqual(expected_value, machine.registers.unsigned(instruction.destination));
try testing.expectEqual(expected_value, machine.registers.bitPattern(instruction.destination));
}
15 changes: 8 additions & 7 deletions src/instructions/register_shift_right.zig
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ pub const Instance = struct {

pub fn execute(self: Instance, machine: *Machine.Instance) void {
// Zig is currently happy to << and >> signed values without respecting their sign bit,
// but that doesn't seem safe and may go away in future. To be sure, treat the value as unsigned.
const original_value = machine.registers.unsigned(self.destination);
// but that doesn't seem safe and may go away in future.
// To be sure, treat the value as a raw bit pattern.
const original_value = machine.registers.bitPattern(self.destination);
const shifted_value = original_value >> self.shift;
machine.registers.setUnsigned(self.destination, shifted_value);
machine.registers.setBitPattern(self.destination, shifted_value);
}
};

Expand Down Expand Up @@ -83,9 +84,9 @@ test "parse returns error.ShiftTooLarge and consumes 4 bytes on invalid shift di

test "execute shifts destination register" {
// zig fmt: off
const original_value: Register.Unsigned = 0b0000_1111_1111_0000;
const original_value: Register.BitPattern = 0b0000_1111_1111_0000;
const shift: Register.Shift = 9;
const expected_value: Register.Unsigned = 0b0000_0000_0000_0111;
const expected_value: Register.BitPattern = 0b0000_0000_0000_0111;
// zig fmt: on

const instruction = Instance{
Expand All @@ -96,9 +97,9 @@ test "execute shifts destination register" {
var machine = Machine.testInstance(null);
defer machine.deinit();

machine.registers.setUnsigned(instruction.destination, original_value);
machine.registers.setBitPattern(instruction.destination, original_value);

instruction.execute(&machine);

try testing.expectEqual(expected_value, machine.registers.unsigned(instruction.destination));
try testing.expectEqual(expected_value, machine.registers.bitPattern(instruction.destination));
}
22 changes: 19 additions & 3 deletions src/machine/registers.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,20 @@ const register_count = static_limits.register_count;
pub const Instance = struct {
const UnsignedValues = [register_count]Register.Unsigned;
const SignedValues = [register_count]Register.Signed;
const BitPatternValues = [register_count]Register.BitPattern;

values: UnsignedValues = .{0} ** register_count,

const Self = @This();

/// Get the value of the specified register as an unsigned value.
pub fn unsigned(self: Self, id: RegisterID.Enum) Register.Unsigned {
return self.values[@enumToInt(id)];
return @bitCast(Register.Unsigned, self.values[@enumToInt(id)]);
}

/// Set the value of the specified register to the specified unsigned value.
pub fn setUnsigned(self: *Self, id: RegisterID.Enum, value: Register.Unsigned) void {
self.values[@enumToInt(id)] = value;
self.values[@enumToInt(id)] = @bitCast(Register.Unsigned, value);
}

/// Get the value of the specified register as a signed value.
Expand All @@ -34,15 +35,30 @@ pub const Instance = struct {
self.values[@enumToInt(id)] = @bitCast(Register.Unsigned, value);
}

/// Get the value of the specified register as a signed value.
pub fn bitPattern(self: Self, id: RegisterID.Enum) Register.BitPattern {
return @bitCast(Register.BitPattern, self.values[@enumToInt(id)]);
}

/// Set the value of the specified register to the specified value.
pub fn setBitPattern(self: *Self, id: RegisterID.Enum, value: Register.BitPattern) void {
self.values[@enumToInt(id)] = @bitCast(Register.Unsigned, value);
}

/// A mutable view of the contents of all registers as unsigned values.
pub fn unsignedSlice(self: *Self) *UnsignedValues {
return &self.values;
return @ptrCast(*UnsignedValues, &self.values);
}

/// A mutable view of the contents of all registers as signed values.
pub fn signedSlice(self: *Self) *SignedValues {
return @ptrCast(*SignedValues, &self.values);
}

/// A mutable view of the contents of all registers as raw bit patterns.
pub fn bitPatternSlice(self: *Self) *BitPatternValues {
return @ptrCast(*BitPatternValues, &self.values);
}
};

// -- Tests --
Expand Down
4 changes: 2 additions & 2 deletions src/machine/user_input.zig
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub const Instance = struct {
}

/// The value to insert into RegisterID.movement_inputs.
pub fn movementInputsRegisterValue(self: Self) Register.Mask {
pub fn movementInputsRegisterValue(self: Self) Register.BitPattern {
// zig fmt: off
var mask: u4 = 0b0000;
if (self.right) mask |= 0b0001;
Expand All @@ -64,7 +64,7 @@ pub const Instance = struct {
}

/// The value to insert into RegisterID.all_inputs.
pub fn allInputsRegisterValue(self: Self) Register.Mask {
pub fn allInputsRegisterValue(self: Self) Register.BitPattern {
var mask = self.movementInputsRegisterValue();
if (self.action) mask |= 0b1000_0000; // 0x80
return mask;
Expand Down
16 changes: 14 additions & 2 deletions src/values/register.zig
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
//! Register values can interpreted as signed or unsigned 16-bit integers.
const introspection = @import("../utils/introspection.zig");

/// A 16-bit register value interpreted as a signed integer.
/// Intended for signed arithmetic.
pub const Signed = i16;
/// A 16-bit register value interpreted as an unsigned integer.
/// Intended for unsigned arithmetic.
pub const Unsigned = u16;
pub const Mask = Unsigned;
pub const Shift = introspection.ShiftType(Unsigned);
/// A 16-bit register value interpreted as a pattern of 16 raw bits.
/// Intended for bitmasking and shifting.
pub const BitPattern = Unsigned;

/// The integer type used for bit-shift operations.
/// Example:
/// --------
/// const shift: Shift = 6;
/// const shifted_value = @as(BitPattern, 0b0000_0000_0000_1000) << 6
pub const Shift = introspection.ShiftType(BitPattern);

0 comments on commit dc2b179

Please sign in to comment.