A Quick Introduction to Virgil - Raw Types

This is part of a basic introduction to programming in Virgil and gives an overview of the language, its syntax, and its structures. This page focuses on the raw types and operators, including literals, typing rules, operators, and usage.

The Need for Bits

Low-level code often has to manipulate data that is encoded in specific patterns of bits. For example, often a hardware register for controlling a device will have several subfields, where some bits select the mode, another bit enables the device, etc. Languages such as C and Java force programmers to express such bit level operations with masks and shifts on integer quantities, often mixed with hexadecimal constants. Such code is tedious to write and obscure to read.

local x: 32 // x is of type 32
local y: 17 // y is of type 17

Virgil instead defines a family of types that correspond to bit-level quantities. The types 1, 2, 3, to 64 define fixed-width bit quantities called raw types. Raw types are values like integers and booleans; they are not passed by reference. The assignment and promotion rules for raw types are simple:

  • a smaller raw type can be assigned to (or used in the place of) a larger raw type.
  • upper bits are always zero-filled when converting.
  • assigning a larger raw type to a smaller raw type requires an explicit conversion (using the :: conversion operator) which discards the upper bits.

Literals

0xfc3    // hex,   12 bits
0b10110  // binary, 5 bits
0752     // octal,  9 bits
997      // int

Virgil supports writing hexadecimal, octal, and binary literals as expressions in programs. Hexadecimal constants must begin with 0x, binary constants must begin with 0b, and octal constants begin with a leading zero. The length of the literal determines the number of bits, and thus the resulting raw type. (Note that decimal constants are always interpreted as integers, never as raw literals).

Operators

The raw types support a family of familiar bit-level operators:

  • & - the bitwise and operation on two operands
  • | - the bitwise or operation on two operands
  • ^ - the bitwise exclusive-or operation on two operands
  • ~ - the one's complement operation on a single operand
  • << - the shift left within a window operation
  • >> - the shift right within a window operation
  • # - the concatenate operation on two operands

0xffc[3]    // access bit 3
expr[n]     // access bit n
v[2] = 0b1  // update bit 2
The raw types also support accessing of individual bits using the array subscript operator [] with an index expression that must be of type int. This operator, when used as a suffix to an expression of a raw type, retrieves the specified bit as a raw value of type 1 (i.e. 1 bit). Similarly, this operator can be used to update an individual bit of a variable with raw type by using it as the target of an assignment. Bit number 0 is always the least-significant bit. Out of bounds accesses do not generate exceptions: a read of a bit number outside of the valid range for the type returns the value 0b0, and a write out of the valid range is ignored.

Conversions

local a: 32 = -377                // OK
local b: int = 0xffff             // ERROR
local c: 8 = 'c'                  // OK
local d: char = 0xfc              // ERROR
local e: int = 0xffff :: int      // OK
local g: char = 0xfc :: char      // OK
local h: boolean = 0b1 :: boolean // OK

Virgil supports implicit and explicit conversions of primitive types to and from raw values:

  • values of type int can be implicitly converted to raw type 32 (the resulting bit pattern is the standard two's complement encoding of the integer).
  • values of type char can be implicitly converted to raw type 8 (the resulting bit pattern is the standard ASCII encoding of the character).
  • raw values of type 32 (or less) can be explicitly converted to type int.
  • raw values of type 8 (or less) can be explicitly converted to type char.
  • raw values of type 1 can be explicitly converted to type boolean.

For more detail on each of these operations see the operator reference.

Go back to the tutorial.