# Scalar unit¶

## Introduction¶

The scalar unit is one of the four execution units of VP1. It is used for general-purpose arithmetic.

### Scalar registers¶

The scalar unit has 31 GPRs, `\$r0-\$r30`. They are 32 bits wide, and are usually used as 32-bit integers, but there are also SIMD instructions treating them as arrays of 4 bytes. In such cases, array notation is used to denote the individual bytes. Bits 0-7 are considered to be `\$rX`, bits 8-15 are `\$rX` and so on. `\$r31` is a special register hardwired to 0.

There are also 8 bits in each `\$c` register belonging to the scalar unit. Most scalar instructions can (if requested) set these bits according to the computation result. The bits are:

• bit 0: sign flag - set equal to bit 31 of the result
• bit 1: zero flag - set if the result is 0
• bit 2: b19 flag - set equal to bit 19 of the result
• bit 3: b20 difference flag - set if bit 20 of the result is different from bit 20 of the first source
• bit 4: b20 flag - set equal to bit 20 of the result
• bit 5: b21 flag - set equal to bit 21 of the result
• bit 6: alt b19 flag (G80 only) - set equal to bit 19 of the result
• bit 7: b18 flag (G80 only) - set equal to bit 18 of the result

The purpose of the last 6 bits is so far unknown.

### Scalar to vector data bus¶

In addition to performing computations of its own, the scalar unit is also used in tandem with the vector unit to perform complex instructions. Certain scalar opcodes expose data on so-called s2v path (scalar to vector data bus), and certain vector opcodes consume this data.

The data is ephemeral and only exists during the execution of a single bundle - the producing and consuming instructions must be located in the same bundle. If a consuming instruction is used without a producing instruction, it’ll read junk. If a producing instruction is used without a consuming instruction, the data is discarded.

The s2v data consists of:

• 4 signed 10-bits factors, used for multiplication
• `\$vc` selection and transformation, for use as mask input in vector unit, made of:
• valid flag: 1 if s2v data was emitted by proper s2v-emitting instruction (if false, vector unit will use an alternate source not involving s2v)
• 2-bit `\$vc` register index
• 1-bit zero flag or sign flag selection (selects which half of `\$vc` will be used)
• 3-bit transform mode: used to mangle the `\$vc` value before use as mask

The factors can alternatively be treated as two 16-bit masks by some instructions. In that case, mask 0 consists of bits 1-8 of factor 0, then bits 1-8 of factor 1 and mask 1 likewise consists of bits 1-8 of factors 2 and 3:

```s2v.mask = (s2v.factor >> 1 & 0xff) | (s2v.factor >> 1 & 0xff) << 8
s2v.mask = (s2v.factor >> 1 & 0xff) | (s2v.factor >> 1 & 0xff) << 8
```

The `\$vc` based mask is derived as follows:

```def xfrm(val, tab):
res = 0
for idx in range(16):
# bit x of result is set if bit tab[x] of input is set
if val & 1 << tab[idx]:
res |= 1 << idx
return res

val = \$vc[s2v.vcsel.idx]
# val2 is only used for transform mode 7
val2 = \$vc[s2v.vcsel.idx | 1]

if s2v.vcsel.flag == 'sf':
val = val & 0xffff
val2 = val2 & 0xffff
else: # 'zf'
val = val >> 16 & 0xffff
val2 = val2 >> 16 & 0xffff

if s2v.vcsel.xfrm == 0:
# passthrough
elif s2v.vcsel.xfrm == 1:
s2v.vcmask = xfrm(val, [2,  2,  2,  2,  6,  6,  6,  6, 10, 10, 10, 10, 14, 14, 14, 14])
elif s2v.vcsel.xfrm == 2:
s2v.vcmask = xfrm(val, [4,  5,  4,  5,  4,  5,  4,  5, 12, 13, 12, 13, 12, 13, 12, 13])
elif s2v.vcsel.xfrm == 3:
s2v.vcmask = xfrm(val, [0,  0,  2,  0,  4,  4,  6,  4,  8,  8, 10,  8, 12, 12, 14, 12])
elif s2v.vcsel.xfrm == 4:
s2v.vcmask = xfrm(val, [1,  1,  1,  3,  5,  5,  5,  7,  9,  9,  9, 11, 13, 13, 13, 15])
elif s2v.vcsel.xfrm == 5:
s2v.vcmask = xfrm(val, [0,  0,  2,  2,  4,  4,  6,  6,  8,  8, 10, 10, 12, 12, 14, 14])
elif s2v.vcsel.xfrm == 6:
s2v.vcmask = xfrm(val, [1,  1,  1,  1,  5,  5,  5,  5,  9,  9,  9,  9, 13, 13, 13, 13])
elif s2v.vcsel.xfrm == 7:
# mode 7 is special: it uses two \$vc inputs and takes every second bit
s2v.vcmask = xfrm(val | val2 << 16, [0,  2,  4,  6,  8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30])
```

## Instruction format¶

The instruction word fields used in scalar instructions are:

• bits 0-2: `CDST` - if < 4, index of the `\$c` register to set according to the instruction’s result. Otherwise, an indication that `\$c` is not to be written (nVidia appears to use 7 in such case).
• bits 0-7: `BIMMBAD` - an immediate field used only in bad opcodes
• bits 0-18: `IMM19` - a signed 19-bit immediate field used only by the mov instruction
• bits 0-15: `IMM16` - a 16-bit immediate field used only by the sethi instruction
• bits 1-9: `FACTOR1` - a 9-bit signed immediate used as vector factor
• bits 10-18: `FACTOR2` - a 9-bit signed immediate used as vector factor
• bit 1: `SIGN2` - determines if byte multiplication source 2 is signed
• 0: `u` - unsigned
• 1: `s` - signed
• bit 2: `SIGN1` - likewise for source 1
• bits 3-10: `BIMM`: an 8-bit immediate for bytewise operations, signed or unsigned depending on instruction.
• bits 3-13: `IMM`: signed 13-bit immediate.
• bits 3-6: `BITOP`: selects the bit operation to perform
• bits 3-7: `RFILE`: selects the other register file for mov to/from other register file
• bits 3-4: `COND` - if source mangling is used, the `\$c` register index to use for source mangling.
• bits 5-8: `SLCT` - if source mangling is used, the condition to use for source mangling.
• bit 8: `RND` - determines byte multiplication rounding behaviour
• 0: `rd` - round down
• 1: `rn` - round to nearest, ties rounding up
• btis 9-13: `SRC2` - the second source `\$r` register, often mangled via source mangling.
• bits 9-13 (low 5 bits) and bit 0 (high bit): `BIMMMUL` - a 6-bit immediate for bytewise multiplication, signed or unsigned depending on instruction.
• bits 14-18: `SRC1` - the first source `\$r` register.
• bits 19-23: `DST` - the destination `\$r` register.
• bits 19-20: `VCIDX` - the `\$vc` register index for s2v
• bit 21: `VCFLAG` - the `\$vc` flag selection for s2v:
• 0: `sf`
• 1: `zf`
• bits 22-23 (low part) and 0 (high part): `VCXFRM` - the `\$vc` transformation for s2v
• bits 24-31: `OP` - the opcode.

### Opcodes¶

The opcode range assigned to the scalar unit is `0x00-0x7f`. The opcodes are:

Todo

some unused opcodes clear \$c, some don’t

Some of the VP1 instructions look like they’re either buggy or just unintended artifacts of incomplete decoding hardware. These are known as bad opcodes and are characterised by using colliding bitfields. It’s probably a bad idea to use them, but they do seem to reliably perform as documented here.

### Source mangling¶

Some instructions perform source mangling: the source register(s) they use are not taken directly from a register index bitfield in the instruction. Instead, the register index from the instruction is… “adjusted” before use. There are several algorithms used for source mangling, most of them used only in a single instruction.

The most common one, known as `SRC2S`, takes the register index from `SRC2` field, a `\$c` register index from `COND`, and `\$c` bit index from `SLCT`. If `SLCT` is anything other than 4, the selected bit is extracted from `\$c` and XORed into the lowest bit of the register index to use. Otherwise (`SLCT` is 4), bits 4-5 of `\$c` are extracted, and added to bits 0-1 of the register index, discarding overflow out of bit 1:

```if SLCT == 4:
adjust = \$c[COND] >> 4 & 3
SRC2S = (SRC2 & ~3) | ((SRC2 + adjust) & 3)
else:
adjust = \$c[COND] >> SLCT & 1
```

## Instructions¶

Loads a 19-bit signed immediate to the selected register. If you need to load a const that doesn’t fit into 19 signed bits, use this instruction along with sethi.

Instructions:
Instruction Operands Opcode
`mov` `\$r[DST] IMM19` `0x65`
Operation:
```\$r[DST] = IMM19
```

### Set high bits: sethi¶

Loads a 16-bit immediate to high bits of the selected register. Low 16 bits are unaffected.

Instructions:
Instruction Operands Opcode
`sethi` `\$r[DST] IMM16` `0x75`
Operation:
```\$r[DST] = (\$r[DST] & 0xffff) | IMM16 << 16
```

### Move to/from other register file: mov¶

Does what it says on the tin. There is `\$c` output capability, but it always outputs 0. The other register file is selected by `RFILE` field, and the possibilities are:

• 0: `\$v` word 0 (ie. bytes 0-3)
• 1: `\$v` word 1 (bytes 4-7)
• 2: `\$v` word 2 (bytes 8-11)
• 3: `\$v` word 3 (bytes 12-15)
• 4: ??? (NV41:G80 only)
• 5: ??? (NV41:G80 only)
• 6: ??? (NV41:G80 only)
• 7: ??? (NV41:G80 only)
• 8: `\$sr`
• 9: `\$mi`
• 10: `\$uc`
• 11: `\$l` (indices over 3 are ignored on writes, wrapped modulo 4 on reads)
• 12: `\$a`
• 13: `\$c` - read only (indices over 3 read as 0)
• 18: curiously enough, aliases 2, for writes only
• 20: `\$m[0-31]`
• 21: `\$m[32-63]`
• 22: `\$d` (indices over 7 are wrapped modulo 8) (G80 only)
• 23: `\$f` (indices over 1 are wrapped modulo 2)
• 24: `\$x` (indices over 15 are wrapped modulo 16) (G80 only)

Todo

figure out the pre-G80 register files

Attempts to read or write unknown register file are ignored. In case of reads, the destination register is left unmodified.

Instructions:
Instruction Operands Opcode
`mov` `[\$c[CDST]] \$<RFILE>[DST] \$r[SRC1]` `0x6a`
`mov` `[\$c[CDST]] \$r[DST] \$<RFILE>[SRC1]` `0x6b`
Operation:
```if opcode == 0x6a:
\$<RFILE>[DST] = \$r[SRC1]
else:
\$r[DST] = \$<RFILE>[SRC1]

if CDST < 4:
\$c[CDST].scalar = 0
```

### Arithmetic operations: mul, min, max, abs, neg, add, sub, shr, sar¶

`mul` performs a 16x16 multiplication with 32 bit result. `shr` and `sar` do a bitwise shift right by given amount, with negative amounts interpreted as left shift (and the shift amount limitted to `-0x1f..0x1f`). The other operations do what it says on the tin. `abs`, `min`, `max`, `mul`, `sar` treat the inputs as signed, `shr` as unsigned, for others it doesn’t matter.

The first source comes from a register selected by `SRC1`, and the second comes from either a register selected by mangled field `SRC2S` or a 13-bit signed immediate `IMM`. In case of `abs` and `neg`, the second source is unused, and the immediate versions are redundant (and in fact one set of opcodes is used for mov to/from other register file instead).

Most of these operations have duplicate opcodes. The canonical one is the lowest one.

All of these operations set the full set of scalar condition codes.

Instructions:
Instruction Operands Opcode
`mul` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x41, 0x51`
`min` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x48, 0x58`
`max` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x49, 0x59`
`abs` `[\$c[CDST]] \$r[DST] \$r[SRC1]` `0x4a, 0x5a, 0x7a`
`neg` `[\$c[CDST]] \$r[DST] \$r[SRC1]` `0x4b, 0x5b, 0x7b`
`add` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x4c, 0x5c`
`sub` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x4d, 0x5d`
`sar` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x4e`
`shr` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x5e`
`mul` `[\$c[CDST]] \$r[DST] \$r[SRC1] IMM` `0x61, 0x71`
`min` `[\$c[CDST]] \$r[DST] \$r[SRC1] IMM` `0x68, 0x78`
`max` `[\$c[CDST]] \$r[DST] \$r[SRC1] IMM` `0x69, 0x79`
`add` `[\$c[CDST]] \$r[DST] \$r[SRC1] IMM` `0x6c, 0x7c`
`sub` `[\$c[CDST]] \$r[DST] \$r[SRC1] IMM` `0x6d, 0x7d`
`sar` `[\$c[CDST]] \$r[DST] \$r[SRC1] IMM` `0x6e`
`shr` `[\$c[CDST]] \$r[DST] \$r[SRC1] IMM` `0x7e`
Operation:
```s1 = sext(\$r[SRC1], 31)
if opcode & 0x20:
s2 = sext(IMM, 12)
else:
s2 = sext(\$r[SRC2], 31)

if op == 'mul':
res = sext(s1, 15) * sext(s2, 15)
elif op == 'min':
res = min(s1, s2)
elif op == 'max':
res = max(s1, s2)
elif op == 'abs':
res = abs(s1)
elif op == 'neg':
res = -s1
res = s1 + s2
elif op == 'sub':
res = s1 - s2
elif op == 'shr' or op == 'sar':
# shr/sar are unsigned/signed versions of the same insn
if op == 'shr':
s1 &= 0xffffffff
# shift amount is 6-bit signed number
shift = sext(s2, 5)
# and -0x20 is invalid
if shift == -0x20:
shift = 0
# negative shifts mean a left shift
if shift < 0:
res = s1 << -shift
else:
# sign of s1 matters here
res = s1 >> shift

\$r[DST] = res
# build \$c result
cres = 0
if res & 1 << 31:
cres |= 1
if res == 0:
cres |= 2
if res & 1 << 19:
cres |= 4
if (res ^ s1) & 1 << 20:
cres |= 8
if res & 1 << 20:
cres |= 0x10
if res & 1 << 21:
cres |= 0x20
if variant == 'G80':
if res & 1 << 19:
cres |= 0x40
if res & 1 << 18:
cres |= 0x80
if CDST < 4:
\$c[CDST].scalar = cres
```

### Bit operations: bitop¶

Performs an arbitrary two-input bit operation on two registers, selected by `SRC1` and `SRC2`. `\$c` output works, but only with a subset of flags.

Instructions:
Instruction Operands Opcode
`bitop` `BITOP [\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2]` `0x42`
Operation:
```s1 = \$r[SRC1]
s2 = \$r[SRC2]

res = bitop(BITOP, s2, s1) & 0xffffffff

\$r[DST] = res
# build \$c result
cres = 0
# bit 0 not set
if res == 0:
cres |= 2
if res & 1 << 19:
cres |= 4
# bit 3 not set
if res & 1 << 20:
cres |= 0x10
if res & 1 << 21:
cres |= 0x20
if variant == 'G80':
if res & 1 << 19:
cres |= 0x40
if res & 1 << 18:
cres |= 0x80
if CDST < 4:
\$c[CDST].scalar = cres
```

### Bit operations with immediate: and, or, xor¶

Performs a given bitwise operation on a register and 13-bit immediate. Like for bitop, `\$c` output only works partially.

Instructions:
Instruction Operands Opcode
`and` `[\$c[CDST]] \$r[DST] \$r[SRC1] IMM` `0x62`
`xor` `[\$c[CDST]] \$r[DST] \$r[SRC1] IMM` `0x63`
`or` `[\$c[CDST]] \$r[DST] \$r[SRC1] IMM` `0x64`
Operation:
```s1 = \$r[SRC1]

if op == 'and':
res = s1 & IMM
elif op == 'xor':
res = s1 ^ IMM
elif op == 'or':
res = s1 | IMM

\$r[DST] = res
# build \$c result
cres = 0
# bit 0 not set
if res == 0:
cres |= 2
if res & 1 << 19:
cres |= 4
# bit 3 not set
if res & 1 << 20:
cres |= 0x10
if res & 1 << 21:
cres |= 0x20
if variant == 'G80':
if res & 1 << 19:
cres |= 0x40
if res & 1 << 18:
cres |= 0x80
if CDST < 4:
\$c[CDST].scalar = cres
```

### Simple bytewise operations: bmin, bmax, babs, bneg, badd, bsub¶

Those perform the corresponding operation (minumum, maximum, absolute value, negation, addition, substraction) in SIMD manner on 8-bit signed or unsigned numbers from one or two sources. Source 1 is always a register selected by `SRC1` bitfield. Source 2, if it is used (ie. instruction is not `babs` nor `bneg`), is either a register selected by `SRC2S` mangled bitfield, or immediate taken from `BIMM` bitfield.

Each of these instructions comes in signed and unsigned variants and both perform result clipping. Note that abs is rather uninteresting in its unsigned variant (it’s just the identity function), and so is neg (result is always 0 or clipped to 0.

These instruction have a `\$c` output, but it’s always set to all-0 if used.

Also note that `babs` and `bneg` have two redundant opcodes each: the bit that normally selects immediate or register second source doesn’t apply to them.

Instructions:
Instruction Operands Opcode
`bmin s` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x08`
`bmax s` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x09`
`babs s` `[\$c[CDST]] \$r[DST] \$r[SRC1]` `0x0a`
`bneg s` `[\$c[CDST]] \$r[DST] \$r[SRC1]` `0x0b`
`badd s` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x0c`
`bsub s` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x0d`
`bmin u` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x18`
`bmax u` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x19`
`babs u` `[\$c[CDST]] \$r[DST] \$r[SRC1]` `0x1a`
`bneg u` `[\$c[CDST]] \$r[DST] \$r[SRC1]` `0x1b`
`badd u` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x1c`
`bsub u` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x1d`
`bmin s` `[\$c[CDST]] \$r[DST] \$r[SRC1] BIMM` `0x28`
`bmax s` `[\$c[CDST]] \$r[DST] \$r[SRC1] BIMM` `0x29`
`babs s` `[\$c[CDST]] \$r[DST] \$r[SRC1]` `0x2a`
`bneg s` `[\$c[CDST]] \$r[DST] \$r[SRC1]` `0x2b`
`badd s` `[\$c[CDST]] \$r[DST] \$r[SRC1] BIMM` `0x2c`
`bsub s` `[\$c[CDST]] \$r[DST] \$r[SRC1] BIMM` `0x2d`
`bmin u` `[\$c[CDST]] \$r[DST] \$r[SRC1] BIMM` `0x38`
`bmax u` `[\$c[CDST]] \$r[DST] \$r[SRC1] BIMM` `0x39`
`babs u` `[\$c[CDST]] \$r[DST] \$r[SRC1]` `0x3a`
`bneg u` `[\$c[CDST]] \$r[DST] \$r[SRC1]` `0x3b`
`badd u` `[\$c[CDST]] \$r[DST] \$r[SRC1] BIMM` `0x3c`
`bsub u` `[\$c[CDST]] \$r[DST] \$r[SRC1] BIMM` `0x3d`
Operation:
```for idx in range(4):
s1 = \$r[SRC1][idx]
if opcode & 0x20:
s2 = BIMM
else:
s2 = \$r[SRC2S][idx]

if opcode & 0x10:
# unsigned
s1 &= 0xff
s2 &= 0xff
else:
# signed
s1 = sext(s1, 7)
s2 = sext(s2, 7)

if op == 'bmin':
res = min(s1, s2)
elif op == 'bmax':
res = max(s1, s2)
elif op == 'babs':
res = abs(s1)
elif op == 'bneg':
res = -s1
res = s1 + s2
elif op == 'bsub':
res = s1 - s2

if opcode & 0x10:
# unsigned: clip to 0..0xff
if res < 0:
res = 0
if res > 0xff:
res = 0xff
else:
# signed: clip to -0x80..0x7f
if res < -0x80:
res = -0x80
if res > 0x7f:
res = 0x7f

\$r[DST][idx] = res

if CDST < 4:
\$c[CDST].scalar = 0
```

### Bytewise bit operations: band, bor, bxor¶

Performs a given bitwise operation on a register and an 8-bit immediate replicated 4 times. Or, intepreted differently, performs such operation on every byte of a register idependently. `\$c` output is present, but always outputs 0.

Instructions:
Instruction Operands Opcode
`and` `[\$c[CDST]] \$r[DST] \$r[SRC1] BIMM` `0x25`
`or` `[\$c[CDST]] \$r[DST] \$r[SRC1] BIMM` `0x26`
`xor` `[\$c[CDST]] \$r[DST] \$r[SRC1] BIMM` `0x27`
Operation:
```for idx in range(4):
if op == 'and':
\$r[DST][idx] = \$r[SRC1][idx] & BIMM
elif op == 'or':
\$r[DST][idx] = \$r[SRC1][idx] | BIMM
elif op == 'xor':
\$r[DST][idx] = \$r[SRC1][idx] ^ BIMM

if CDST < 4:
\$c[CDST].scalar = 0
```

### Bytewise bit shift operations: bshr, bsar¶

Performs a bytewise SIMD right shift. Like the usual shift instruction, the shift amount is considered signed and negative amounts result in left shift. In this case, the shift amount is a 4-bit signed number. Operands are as in usual bytewise operations.

Instructions:
Instruction Operands Opcode
`bsar` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x0e`
`bshr` `[\$c[CDST]] \$r[DST] \$r[SRC1] \$r[SRC2S]` `0x1e`
`bsar` `[\$c[CDST]] \$r[DST] \$r[SRC1] BIMM` `0x2e`
`bshr` `[\$c[CDST]] \$r[DST] \$r[SRC1] BIMM` `0x3e`
Operation:
```for idx in range(4):
s1 = \$r[SRC1][idx]
if opcode & 0x20:
s2 = BIMM
else:
s2 = \$r[SRC2S][idx]

if opcode & 0x10:
# unsigned
s1 &= 0xff
else:
# signed
s1 = sext(s1, 7)

shift = sext(s2, 3)

if shift < 0:
res = s1 << -shift
else:
res = s1 >> shift

\$r[DST][idx] = res

if CDST < 4:
\$c[CDST].scalar = 0
```

### Bytewise multiplication: bmul¶

These instructions perform bytewise fractional multiplication: the inputs and outputs are considered to be fixed-point numbers with 8 fractional bits (unsigned version) or 7 fractional bits (signed version). The signedness of both inputs and the output can be controlled independently (the signedness of the output is controlled by the opcode, and of the inputs by instruction word flags `SIGN1` and `SIGN2`). The results are clipped to the output range. There are two rounding modes: round down and round to nearest with ties rounded up.

The first source is always a register selected by `SRC1` bitfield. The second source can be a register selected by `SRC2` bitfield, or 6-bit immediate in `BIMMMUL` bitfield padded with two zero bits on the right.

Note that besides proper `0xX1` opcodes, there are also `0xX2` bad opcodes. In case of register-register ops, these opcodes are just aliases of the sane ones, but for immediate opcodes, a colliding bitfield is used.

The instructions have no `\$c` output capability.

Instructions:
Instruction Operands Opcode
`bmul s` `RND \$r[DST] SIGN1 \$r[SRC1] SIGN2 \$r[SRC2]` `0x01, 0x02`
`bmul u` `RND \$r[DST] SIGN1 \$r[SRC1] SIGN2 \$r[SRC2]` `0x11, 0x12`
`bmul s` `RND \$r[DST] SIGN1 \$r[SRC1] SIGN2 BIMMMUL` `0x21`
`bmul u` `RND \$r[DST] SIGN1 \$r[SRC1] SIGN2 BIMMMUL` `0x31`
`bmul s` `RND \$r[DST] SIGN1 \$r[SRC1] SIGN2 BIMMBAD` `0x22` (bad opcode)
`bmul u` `RND \$r[DST] SIGN1 \$r[SRC1] SIGN2 BIMMBAD` `0x32` (bad opcode)
Operation:
```for idx in range(4):
s1 = \$r[SRC1][idx]
if opcode & 0x20:
if opcode & 2:
else:
s2 = BIMMMUL << 2
else:
s2 = \$r[SRC2S][idx]

# convert inputs to 8 fractional bits - unsigned inputs are already ok
if SIGN1:
ss1 = sext(ss1, 7) << 1
if SIGN2:
ss2 = sext(ss2, 7) << 1

# multiply - the result has 16 fractional bits
res = ss1 * ss2

if opcode & 0x10:
# unsigned result
# first, if round to nearest is selected, apply rounding correction
if RND == 'rn':
res += 0x80
# convert to 8 fractional bits
res >>= 8
# clip
if res < 0:
res = 0
if res > 0xff:
res = 0xff
else:
# signed result
if RND == 'rn':
res += 0x100
# convert to 7 fractional bits
res >>= 9
# clip
if res < -0x80:
res = -0x80
if res > 0x7f:
res = 0x7f

\$r[DST][idx] = res
```

### Send immediate to vector unit: vec¶

This instruction takes two 9-bit immediate operands and sends them as factors to the vector unit. The first immediate is used as factors 0 and 1, and the second is used as factors 2 and 3. `\$vc` selection is sent as well.

Instructions:
Instruction Operands Opcode
`vec` `FACTOR1 FACTOR2 \$vc[VCIDX] VCFLAG VCXFRM` `0x24`
Operation:
```s2v.factor = s2v.factor = FACTOR1
s2v.factor = s2v.factor = FACTOR2
s2v.vcsel.idx = VCIDX
s2v.vcsel.flag = VCFLAG
s2v.vcsel.xfrm = VCXFRM
```

### Send mask to vector unit and shift: vecms¶

This instruction shifts a register right by 4 bits and uses the bits shifted out as s2v mask 0 after expansion (each bit is replicated 4 times). The s2v factors are derived from that mask and are not very useful. The right shift is sign-filling. `\$vc` selection is sent as well.

Instructions:
Instruction Operands Opcode
`vecms` `\$r[SRC1] \$vc[VCIDX] VCFLAG VCXFRM` `0x45`
Operation:
```val = sext(\$r[SRC1], 31)
\$r[SRC1] = val >> 4
# the factors are made so that the mask derived from them will contain
# each bit from the short mask repeated 4 times
f0 = 0
f1 = 0
if val & 1:
f0 |= 0x1e
if val & 2:
f0 |= 0x1e0
if val & 4:
f1 |= 0x1e
if val & 8:
f1 |= 0x1e0
s2v.factor = f0
s2v.factor = f1
s2v.factor = s2v.factor = 0
s2v.vcsel.idx = VCIDX
s2v.vcsel.flag = VCFLAG
s2v.vcsel.xfrm = VCXFRM
```

### Send bytes to vector unit: bvec¶

Treats a register as 4-byte vector, sends the bytes as s2v factors (treating them as signed with 7 fractional bits). `\$vc` selection is sent as well. If the s2v output is used as masks, this effectively takes mask 0 from source bits 0-15 and mask 1 from source bits 16-31.

Instructions:
Instruction Operands Opcode
`bvec` `\$r[SRC1] \$vc[VCIDX] VCFLAG VCXFRM` `0x0f`
Operation:
```for idx in range(4):
s2v.factor[idx] = sext(\$r[SRC1][idx], 7) << 1
s2v.vcsel.idx = VCIDX
s2v.vcsel.flag = VCFLAG
s2v.vcsel.xfrm = VCXFRM
```

Figure out this one yourself. It sends s2v factors based on SIMD multiply & add, uses weird source mangling, and even weirder source 1 bitfields.

Instructions:
Instruction Operands Opcode
`bvecmad` `\$r[SRC1] \$r[SRC2]q \$vc[VCIDX] VCFLAG VCXFRM` `0x04`
`bvecmadsel` `\$r[SRC1] \$r[SRC2]q \$vc[VCIDX] VCFLAG VCXFRM` `0x05`
Operation:
```if SLCT== 4:
adjust = \$c[COND] >> 4 & 3
else:
adjust = \$c[COND] >> SLCT & 1

# SRC1 selects the pre-factor, which will be multiplied by source 3
prefactor = \$r[SRC1] >> 11 & 0xff
prefactor = \$r[SRC1] >> 11 & 0x7f

s2b = \$r[SRC2 | 2 | adjust]

for idx in range(4):
# this time source is mangled by OR, not XOR - don't ask me

midx = idx
midx = idx & 2
if SLCT == 2 and \$c[COND] & 0x80:
midx |= 1

# baseline (res will have 16 fractional bits, sources have 8)
res = s2a[midx] << 8
# throw in the multiplication result
res += prefactor * s2b[idx]
# and rounding correction (for round to nearest, ties up)
res += 0x40
# and round to 9 fractional bits
s2v.factor[idx] = res >> 7

s2v.vcsel.idx = VCIDX
s2v.vcsel.flag = VCFLAG
s2v.vcsel.xfrm = VCXFRM
```