Mixed arithmetic-with-logic instructions
Opcode | P/U | Category | Description |
HAM2 |
user | ALU: mixed | Hamming weight part 2 |
NOLIST |
user | ALU: mixed | NOP , but hidden in listings |
NOP |
user | ALU: mixed | no operation |
STUN |
user | ALU: mixed | stacked unary |
UN.A |
user | ALU: mixed | unary alpha |
UN.B |
user | ALU: mixed | unary beta |
UN.G |
user | ALU: mixed | unary gamma |
Mixed arithmetic-with-logic instructions are those where their operands and results represent neither all numeric values nor all non-numeric values. NOP
and NOLIST
are documented with this set, because they handle no data and therefore show no bias toward being solely for arithmetic or solely for logic.
HAM2
Hamming weight part 2
Syntax |
c = a ham2 b |
Register | Signedness |
All | ignored |
1 opcode only |
Flag | Set if and only if |
N |
never; flag is cleared |
Z |
all result bits are zero |
T |
flag does not change |
R |
flag does not change |
HAM2
evaluates the quartic polynomial:
c = b_0 * 2**0 + ( a_6 + b_1 ) * 2**1 + ( a_12 + a_7 + b_2 ) * 2**2 + ( a_13 + a_8 ) * 2**3 + a_14 * 2**4
where r_i
denotes the 2**i
bit of register r
. Each r_i
bit has either 0 or 1 as its value. The result of the polynomial evaluation is stored in c
. The maximum value this polynomial can produce is 49.
This crazy expression, when preceded by the HAM1
macro, counts the number of 1
bits in a register. This count is called the Hamming weight or population count. Because the architecture uses 36-bit words, the Hamming weight will be in the range 0 through 36 inclusive. Use would look like:
unsigned x ; input unsigned ones ; number of 1 bits in x unsigned t ; temporary t = ham1 x ones = t ham2 t
The POPC
macro, which expands to HAM1
and HAM2
, is more readable—although it disguises the fact two instructions execute instead of one:
unsigned x ; input unsigned ones ; number of 1 bits in x t = popc x
Unfortunately, none of the macros (including HAM1
and POPC
) are implemented, but POPC
would ultimately expand to STUN
and HAM2
, neither of which are macros. So the way population count is presently regression tested looks liike this:
unsigned x ; input unsigned ones ; number of 1 bits in x unsigned t ; temporary t = x stun.c 141414141414`o ones = t ham2 t
NOLIST
No list
Syntax |
nolist |
No registers used |
1 opcode only |
No flags changed |
This instruction sits out the current CPU cycle without writing to any register or changing any flags. NOLIST
produces the same control decoder signals as and behaves identically to NOP
.
NOP
differs from NOLIST
for documentation and testing purposes. Unused segments of code memory are filled with NOLIST
to tell disassemblers not to spew thousands of lines of irrelevant material. In contrast, NOP
is used in proximity to other instructions, and disassemblers should display any NOP
s encountered.
NOP
No operation
Syntax |
nop |
No registers used |
1 opcode only |
No flags changed |
This instruction sits out the current CPU cycle without writing to any register or changing any flags. See also NOLIST
.
The NOP
writeup on page 399 of the dissertation is very outdated and should be disregarded.
STUN
Stacked unary
Syntax |
c = a stun.a b |
c = a stun.b b |
... |
c = a stun.k b |
Register | Signedness |
Left | varies and not well specified |
Right | varies and not well specified |
Destination | varies and not well specified |
11 opcodes at present |
The STUN
opcodes have yet to be standardized and are unstable as of 14 June 2023. Do not use them in production software!
The STUN
opcodes represent “super-instructions” that employ the ALU’s alpha, beta, and gamma layers in combination to compute one of 64 unary functions. The dissertation describes 25 functions, leaving another 39 slots available for future use.
STUN
employs the left operand as the argument for the unary function, and the right operand as a 6-bit selector that specifies which of 64 functions is being applied. This selector must be replicated across all 6 subwords in order to reach all 18 RAMs of the ALU’s alpha, beta, and gamma layer, which is why the constant 141414141414`o appears in the above HAM2
code example.
STUN
can do some crazy tricks, such as:
- absolute value in 2 instructions
- limited-range absolute value in 1 instruction
- Hamming weight in 2 instructions
- linear feedback shift register in 1 instruction
- decrement bit-reversed word mod 236 in 1 instruction
The unary functions supplied by STUN
have specific, conflicting requirements with respect to some of the control decoder signals. This is why there are eleven opcodes, why this section is so incompletely written, why I’m not ready to even begin writing regression test cases, and why it’s not helpful to define any assembler macro semantics yet.
More information about STUN
is available in the dissertation at pages 137–138 and 161–170, as well as in instruct.c
and unary.c
in the firmware source.
UN.a
Simple unary, alpha layer
Syntax |
c = a un.a b |
Register | Signedness |
Left | ignored |
Right | ignored |
Dest. | ignored |
1 opcode only |
UN.a
splits left operand a
into 6 contiguous subwords of 6 bits each, and pairs them off with the corresponding subwords of the right operand b
. Each right operand selects one of 64 unary functions, each having 6 input and 6 output bits, to apply to its corresponding left operand. The results are stored in the corresponding subwords of c
. The operation is done in the alpha layer of the ALU only.
The UN.a
instruction is somewhat of a solution looking for a problem. You aren’t likely to use it in programs you write, nor would you typically find it in programs I write. A fuller description of UN.a
, along with a list of available functions, appears in the dissertation at pages 160–161.
The UN.a
, UN.b
, and UN.g
lookup tables are identical, so the UN.a
instruction will produce the same result as UN.g
despite employing different SRAMs to compute it.
UN.b
Simple unary, beta layer
Syntax |
c = a un.b b |
Register | Signedness |
Left | ignored |
Right | ignored |
Dest. | ignored |
1 opcode only |
UN.b
is analogous to UN.a
and UN.c
and uses the same lookup tables, except the bits from the left operand are transposed, and the bits to the destination are transposed. Drawings showing this transposition are available on pages 75 and 88 of the dissertation.
The following two code segments produce identical results for c
:
; segment 1 c = a un.b b ; computed in beta while electrically transposed ; segment 2 a' = 0 txor a ; transpose c' = a' un.a b ; computed in alpha using transposed value c = 0 txor c' ; un-transpose
The UN.b
instruction is somewhat of a solution looking for a problem. You aren’t likely to use it in programs you write, nor would you typically find it in programs I write. A fuller description of UN.b
, along with a list of available functions, appears in the dissertation at pages 160–161.
UN.g
Simple unary, gamma layer
Syntax |
c = a un.g b |
Register | Signedness |
Left | ignored |
Right | ignored |
Dest. | ignored |
1 opcode only |
UN.g
splits left operand a
into 6 contiguous subwords of 6 bits each, and pairs them off with the corresponding subwords of the right operand b
. Each right operand selects one of 64 unary functions, each having 6 input and 6 output bits, to apply to its corresponding left operand. The results are stored in the corresponding subwords of c
. The operation is done in the gamma layer of the ALU only.
The UN.g
instruction is somewhat of a solution looking for a problem. You aren’t likely to use it in programs you write, nor would you typically find it in programs I write. A fuller description of UN.g
, along with a list of available functions, appears in the dissertation at pages 160–161.
The UN.a
, UN.b
, and UN.g
lookup tables are identical, so the UN.a
instruction will produce the same result as UN.g
despite employing different SRAMs to compute it.