For each possible p-code operation, we give a brief description and provide a table that lists the inputs that must be present and their meaning. We also list the basic syntax for denoting the operation when describing semantics in a processor specification file.

Copy a sequence of contiguous bytes from anywhere to anywhere. Size of input0 and output must be the same.

This instruction loads data from a dynamic location into the output variable by dereferencing a pointer. The “pointer” comes in two pieces. One piece, input1, is a normal variable containing the offset of the object being pointed at. The other piece, input1, is a constant indicating the space into which the offset applies. The data in input1 is interpreted as an unsigned offset and should have the same size as the space referred to by the ID, i.e. a 4-byte address space requires a 4-byte offset. The space ID is not manually entered by a user but is automatically generated by the p-code compiler. The amount of data loaded by this instruction is determined by the size of the output variable. It is easy to confuse the address space of the output and input1 variables and the Address Space represented by the ID, which could all be different. Unlike many programming models, there are multiple spaces that a “pointer” can refer to, and so an extra ID is required.

It is possible for the addressable unit of an address
space to be bigger than a single byte. If
the **wordsize** attribute of the space
given by the ID is bigger than one, the offset into the space obtained
from input1 must be multiplied by this value in order to obtain the
correct byte offset into the space.

This instruction is the complement
of **LOAD**. The data in the variable
input2 is stored at a dynamic location by dereferencing a pointer. As
with **LOAD**, the “pointer” comes in two
pieces: a space ID part, and an offset variable. The size of input1
must match the address space specified by the ID, and the amount of
data stored is determined by the size of input2.

Its possible for the addressable unit of an address
space to be bigger than a single byte. If
the **wordsize** attribute of the space
given by the ID is bigger than one, the offset into the space obtained
from input1 must be multiplied by this value in order to obtain the
correct byte offset into the space.

This is an absolute jump instruction. The varnode parameter input0 encodes
the destination address (address space and offset) of the jump. The varnode is not
treated as a variable for this instruction and does not store the destination. Its address space and
offset *are* the destination. The size of input0 is irrelevant.

Confusion about the meaning of this instruction can result because of
the translation from machine instructions to p-code. The destination of the jump is
a *machine* address and refers to
the *machine* instruction at that address. When
attempting to determine which p-code instruction is executed next, the
rule is: execute the first p-code instruction resulting from the
translation of the machine instruction(s) at that address. The
resulting p-code instruction may not be attached directly to the
indicated address due to NOP instructions and delay slots.

If input0 is constant, i.e. its address space
is the **constant**
address space, then it encodes a *p-code relative branch*.
In this case, the offset of input0 is considered a relative offset into
the indexed list of p-code operations corresponding to the translation
of the current machine instruction. This allows branching within the
operations forming a single instruction. For example, if
the **BRANCH** occurs as the pcode
operation with index 5 for the instruction, it can branch to operation
with index 8 by specifying a constant destination “address” of
3. Negative constants can be used for backward branches.

This is a conditional branch instruction where the dynamic condition
for taking the branch is determined by the 1 byte variable input1. If
this variable is non-zero, the condition is
considered *true* and the branch is taken. As in
the **BRANCH** instruction the parameter
input0 is not treated as a variable but as an address and is
interpreted in the same way. Furthermore, a constant space address is
also interpreted as a relative address so that
a **CBRANCH** can do *p-code
relative branching*. See the discussion for the
**BRANCH** operation.

This is an indirect branching instruction. The address to branch to is
determined dynamically (at runtime) by examining the contents of the
variable input0. As this instruction is currently defined, the
variable input0 only contains the *offset* of the
destination, and the address space is taken from the address
associated with the branching instruction
itself. So *execution can only branch within the same address
space* via this instruction. The size of the variable input0
must match the size of offsets for the current address space. P-code
relative branching is not possible with **BRANCHIND**.

This instruction is semantically equivalent to
the **BRANCH** instruction.
**Beware:** This instruction does not
behave like a typical function call. In particular, there is no
internal stack in p-code for saving the return address. Use of this
instruction instead of **BRANCH** is
intended to provide a hint to algorithms that try to follow code flow.
It indicates that the original machine instruction, of which this
p-code instruction is only a part, is intended to be a function
call. The p-code instruction does not implement the full semantics of
the call itself; it only implements the final branch.

In the raw p-code translation process, this operation can only take
input0, but in follow-on analysis, it can take arbitrary additional inputs.
These represent (possibly partial) recovery of the parameters being
passed to the logical *call* represented by this
operation. These additional parameters have no effect on the original
semantics of the raw p-code but naturally hold the varnode values flowing
into the call.

This instruction is semantically equivalent to
the **BRANCHIND** instruction. It does
not perform a function call in the usual sense of the term. It merely
indicates that the original machine instruction is intended to be an
indirect call. See the discussion for
the **CALL** instruction.

As with the **CALL** instruction,
this operation may take additional inputs when not in raw form, representing
the parameters being passed to the logical call.

This instruction is semantically equivalent to
the **BRANCHIND** instruction. It does
not perform a return from subroutine in the usual sense of the
term. It merely indicates that the original machine instruction is
intended to be a return from subroutine. See the discussion for
the **CALL** instruction.

Similarly to **CALL** and **CALLIND**,
this operation may take an additional input when not in raw form. If input1 is
present it represents the value being *returned* by this operation.
This is used by analysis algorithms to hold the value logically flowing back to the parent
subroutine.

This is a concatenation operator that understands the endianess of the data. The size of input0 and input1 must add up to the size of output. The data from the inputs is concatenated in such a way that, if the inputs and output are considered integers, the first input makes up the most significant part of the output.

This is a truncation operator that understands the endianess of the
data. Input1 indicates the number of least significant bytes of input0
to be thrown away. Output is then filled with any remaining bytes of
input0 *up to the size of output*. If the size of
output is smaller than the size of input0 plus the constant input1,
then the additional most significant bytes of input0 will also be
truncated.

This is the integer equality operator. Output is
assigned *true*, if input0 equals input1. It works
for signed, unsigned, or any contiguous data where the match must be
down to the bit. Both inputs must be the same size, and the output
must have a size of 1.

This is the integer inequality operator. Output is
assigned *true*, if input0 does not equal
input1. It works for signed, unsigned, or any contiguous data where
the match must be down to the bit. Both inputs must be the same size,
and the output must have a size of 1.

This is an unsigned integer comparison operator. If the unsigned
integer input0 is strictly less than the unsigned integer input1,
output is set to *true*. Both inputs must be the
same size, and the output must have a size of 1.

This is a signed integer comparison operator. If the signed integer
input0 is strictly less than the signed integer input1, output is set
to *true*. Both inputs must be the same size, and
the output must have a size of 1.

This is an unsigned integer comparison operator. If the unsigned
integer input0 is less than or equal to the unsigned integer input1,
output is set to *true*. Both inputs must be the
same size, and the output must have a size of 1.

This is a signed integer comparison operator. If the signed integer
input0 is less than or equal to the signed integer input1, output is
set to *true*. Both inputs must be the same size,
and the output must have a size of 1.

Zero-extend the data in input0 and store the result in output. Copy all the data from input0 into the least significant positions of output. Fill out any remaining space in the most significant bytes of output with zero. The size of output must be strictly bigger than the size of input.

Sign-extend the data in input0 and store the result in output. Copy all the data from input0 into the least significant positions of output. Fill out any remaining space in the most significant bytes of output with either zero or all ones (0xff) depending on the most significant bit of input0. The size of output must be strictly bigger than the size of input0.

This is standard integer addition. It works for either unsigned or
signed interpretations of the integer encoding (twos complement). Size
of both inputs and output must be the same. The addition is of course
performed modulo this size. Overflow and carry conditions are
calculated by other
operations. See **INT_CARRY**
and **INT_SCARRY**.

This is standard integer subtraction. It works for either unsigned or
signed interpretations of the integer encoding (twos complement). Size
of both inputs and output must be the same. The subtraction is of
course performed modulo this size. Overflow and borrow conditions are
calculated by other
operations. See **INT_SBORROW**
and **INT_LESS**.

This operation checks for unsigned addition overflow or carry
conditions. If the result of adding input0 and input1 as unsigned
integers overflows the size of the varnodes, output is
assigned *true*. Both inputs must be the same size,
and output must be size 1.

This operation checks for signed addition overflow or carry
conditions. If the result of adding input0 and input1 as signed
integers overflows the size of the varnodes, output is
assigned *true*. Both inputs must be the same size,
and output must be size 1.

This operation checks for signed subtraction overflow or borrow
conditions. If the result of subtracting input1 from input0 as signed
integers overflows the size of the varnodes, output is
assigned *true*. Both inputs must be the same size,
and output must be size 1. Note that the equivalent unsigned
subtraction overflow condition
is **INT_LESS**.

This is the twos complement or arithmetic negation operation. Treating input0 as a signed integer, the result is the same integer value but with the opposite sign. This is equivalent to doing a bitwise negation of input0 and then adding one. Both input0 and output must be the same size.

This is the bitwise negation operation. Output is the result of taking every bit of input0 and flipping it. Both input0 and output must be the same size.

This operation performs a logical Exclusive-Or on the bits of input0 and input1. Both inputs and output must be the same size.

This operation performs a Logical-And on the bits of input0 and input1. Both inputs and output must be the same size.

This operation performs a Logical-Or on the bits of input0 and input1. Both inputs and output must be the same size.

This operation performs a left shift on input0. The value given by input1, interpreted as an unsigned integer, indicates the number of bits to shift. The vacated (least significant) bits are filled with zero. If input1 is zero, no shift is performed and input0 is copied into output. If input1 is larger than the number of bits in output, the result is zero. Both input0 and output must be the same size. Input1 can be any size.

This operation performs an unsigned (logical) right shift on input0. The value given by input1, interpreted as an unsigned integer, indicates the number of bits to shift. The vacated (most significant) bits are filled with zero. If input1 is zero, no shift is performed and input0 is copied into output. If input1 is larger than the number of bits in output, the result is zero. Both input0 and output must be the same size. Input1 can be any size.

This operation performs a signed (arithmetic) right shift on input0. The value given by input1, interpreted as an unsigned integer, indicates the number of bits to shift. The vacated bits are filled with the original value of the most significant (sign) bit of input0. If input1 is zero, no shift is performed and input0 is copied into output. If input1 is larger than the number of bits in output, the result is zero or all 1-bits (-1), depending on the original sign of input0. Both input0 and output must be the same size. Input1 can be any size.

This is an integer multiplication operation. The result of multiplying input0 and input1, viewed as integers, is stored in output. Both inputs and output must be the same size. The multiplication is performed modulo the size, and the result is true for either a signed or unsigned interpretation of the inputs and output. To get extended precision results, the inputs must first by zero-extended or sign-extended to the desired size.

This is an unsigned integer division operation. Divide input0 by
input1, truncating the result to the nearest integer, and store the
result in output. Both inputs and output must be the same size. There
is no handling of division by zero. To simulate a processor’s handling
of a division-by-zero trap, other operations must be used before
the **INT_DIV**.

This is an unsigned integer remainder operation. The remainder of
performing the unsigned integer division of input0 and input1 is put
in output. Both inputs and output must be the same size. If q =
input0/input1, using the **INT_DIV**
operation defined above, then output satisfies the equation q*input1 +
output = input0, using the **INT_MULT**
and **INT_ADD** operations.

This is a signed integer division operation. The resulting integer is
the one closest to the rational value input0/input1 but which is still
smaller in absolute value. Both inputs and output must be the same
size. There is no handling of division by zero. To simulate a
processor’s handling of a division-by-zero trap, other operations must
be used before the **INT_SDIV**.

This is a signed integer remainder operation. The remainder of
performing the signed integer division of input0 and input1 is put in
output. Both inputs and output must be the same size. If q = input0 s/
input1, using the **INT_SDIV** operation
defined above, then output satisfies the equation q*input1 + output =
input0, using the **INT_MULT**
and **INT_ADD** operations.

This is a logical negate operator, where we assume input0 and output
are boolean values. It puts the logical complement of input0, treated
as a single bit, into output. Both input0 and output are size
1. Boolean values are implemented with a full byte, but are still
considered to only support a value of *true*
or *false*.

This is an Exclusive-Or operator, where we assume the inputs and
output are boolean values. It puts the exclusive-or of input0 and
input1, treated as single bits, into output. Both inputs and output
are size 1. Boolean values are implemented with a full byte, but are
still considered to only support a value of *true*
or *false*.

This is a Logical-And operator, where we assume the inputs and output
are boolean values. It puts the logical-and of input0 and input1,
treated as single bits, into output. Both inputs and output are size
1. Boolean values are implemented with a full byte, but are still
considered to only support a value of *true*
or *false*.

This is a Logical-Or operator, where we assume the inputs and output
are boolean values. It puts the logical-or of input0 and input1,
treated as single bits, into output. Both inputs and output are size
1. Boolean values are implemented with a full byte, but are still
considered to only support a value of *true*
or *false*.

This is a floating-point equality operator. Output is
assigned *true*, if input0 and input1 are
considered equal as floating-point values. Both inputs must be the
same size, and output is size 1. If either input
is **NaN**, output is set
to *false*.

This is a floating-point inequality operator. Output is
assigned *true*, if input0 and input1 are not
considered equal as floating-point values. Both inputs must be the
same size, and output is size 1. If either input
is **NaN**, output is set
to *false*.

This is a comparison operator, where both inputs are considered
floating-point values. Output is assigned *true*,
if input0 is less than input1. Both inputs must be the same size, and
output is size 1. If either input
is **NaN**, output is set
to *false*.

This is a comparison operator, where both inputs are considered
floating-point values. Output is assigned *true*,
if input0 is less than or equal to input1. Both inputs must be the
same size, and output is size 1. If either input
is **NaN**, output is set
to *false*.

This is a floating-point addition operator. The result of adding
input0 and input1 as floating-point values is stored in output. Both
inputs and output must be the same size. If either input
is **NaN**, output is set
to **NaN**. If any overflow condition
occurs, output is set to **NaN**.

This is a floating-point subtraction operator. The result of
subtracting input1 from input0 as floating-point values is stored in
output. Both inputs and output must be the same size. If either input
is **NaN**, output is set
to **NaN**. If any overflow condition
occurs, output is set to **NaN**.

This is a floating-point multiplication operator. The result of
multiplying input0 to input1 as floating-point values is stored in
output. Both inputs and output must be the same size. If either input
is **NaN**, output is set
to **NaN**. If any overflow condition
occurs, output is set to **NaN**.

This is a floating-point division operator. The result of dividing
input1 into input0 as floating-point values is stored in output. Both
inputs and output must be the same size. If either input
is **NaN**, output is set
to **NaN**. If any overflow condition
occurs, output is set to **NaN**.

This is a floating-point negation operator. The floating-point value
in input0 is stored in output with the opposite sign. Both input and
output must be the same size. If input
is **NaN**, output is set
to **NaN**.

This is a floating-point absolute-value operator. The absolute value
of input0 is stored in output. Both input0 and output must be the same
size. If input0 is **NaN**, output is set
to **NaN**.

This is a floating-point square-root operator. The square root of
input0 is stored in output. Both input0 and output must be the same
size. If input0 is **NaN**, output is set
to **NaN**.

This operation rounds input0, as a signed floating-point value, towards *positive infinity*.
For instance, the value 1.2 rounds to 2.0; -1.2 rounds to -1.0.
The integral value obtained by rounding input0 up is stored in output, as a floating-point
value. Both input0 and output must be the same size. If input0
is **NaN**, output is set
to **NaN**.

This operation rounds input0, as a floating-point value, towards *negative infinity*.
For instance, the value 1.2 rounds to 1.0 and -1.2 rounds to -2.0.
The integral value obtained by rounding input0 down is stored in output, as a floating-point
value. **FLOAT_FLOOR** does *not* produce
a twos complement integer output (See the **TRUNC** operator).
Both input0 and output must be the same size. If input0
is **NaN**, output is set
to **NaN**.

This is a floating-point rounding operator. The integral value closest to the
floating-point value in input0 is stored in output, as a floating-point value.
For example, 1.2 rounds to 1.0 and 1.9 rounds to 2.0.
**FLOAT_ROUND** does *not*
produce a twos complement integer output (See the **TRUNC** operator).
Both input0 and output must be the same size. If
input0 is **NaN**, output is set
to **NaN**.

This operator returns *true* in output if input0 is
interpreted as **NaN**. Output must be
size 1, and input0 can be any size.

This is an integer to floating-point conversion operator. Input0 viewed as a signed integer is converted to floating-point format and stored in output. Input0 and output do not need to be the same size. The conversion to floating-point may involve a loss of precision.

This is a floating-point precision conversion operator. The
floating-point value in input0 is converted to a floating-point value
of a different size and stored in output. If output is smaller than
input0, then the operation will lose precision. Input0 and output
should be different sizes. If input0
is **NaN**, then output is set
to **NaN**.

This is a floating-point to integer conversion operator. The
floating-point value in input0 is converted to a signed integer and
stored in output using the default twos complement encoding.
The fractional part of input0 is dropped in the conversion by rounding *towards zero*.
Input0 and output can be different sizes.