checksmix 0.4.3

An assembler, emulator and source-level debugger for Knuth's MMIX.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
# MMIX Instruction Quick Reference

MMIX is a 64-bit big-endian RISC machine (Knuth, 1999) with 256 general-purpose registers (`$0`–`$255`), a separate special-register file, byte-addressed memory, and fixed 32-bit instructions. Immediates in assembly may be decimal, hexadecimal (`#`-prefixed, or `0x`/`0X`-prefixed — also a checksmix extension), or character literals — one quote, one character, one quote, the character possibly a quote itself, so `'''` is the apostrophe; a character literal's value is that character's Unicode scalar value: `'é'` is `#E9`, `'算'` is `#7B97`. Every operand is an MMIXAL expression (see "Expressions" below). A leading `0` is an ordinary decimal digit, as in MMIXAL — `SET $1,010` loads 10, and there is no octal spelling. A string literal has no escape mechanism either: its content is exactly what it spells, one unit per character, each its character's constant (a data directive's own per-character rule for a string is in "Assembler directives" below).

## Memory access

MMIX has no unaligned access. A wyde, tetra, or octa access at address `A`
resolves to `w·⌊A/w⌋` for its width `w` (2, 4, or 8) — the low `log2(w)` bits
of `A` are ignored. `LDO $X,$Y,$Z` with an address ending in 3 loads the
octabyte at the aligned base below it, not eight bytes straddling two
octabytes. A misaligned address is rounded, never rejected: there is no trap
or diagnostic. Byte access is unaffected — a byte is its own alignment. An
instruction fetch is a tetra access too: `GO` or `PUSHGO` to an address off a
tetra boundary runs the instruction at the aligned base, but the PC keeps the
address's own two low bits, and `GETA`, `rJ` after `PUSHJ`/`PUSHGO`, and `rW`
at an interrupt carry them, as the Instruction Reference's GO page states
(<https://mmix.cs.hm.edu/doc/instructions/go.html>).

Every load, store, `GO` and cache instruction that auto-selects its register
or immediate opcode also takes a two-operand form: `LDO $X,$Y` fills Z with
`0`, a register `$Y` read as an offset of zero. A pure `$Y` is instead an
address: the assembler resolves it against the largest `GREG`-allocated base
register, among those declared earlier in the source with a nonzero initial
value, whose value is no more than 255 below it, and emits the three-operand
form with that base in Y and the remaining offset in Z. No such base is an
error: `no GREG before this instruction holds a base address 0 to 255 bytes
below {addr:#x}`. The form always assembles one tetra. `LDA` takes this path
too, as `LDA $X,base,offset`; its two-operand form is described below the
instruction table.

## Minimal assembly skeleton

```
        LOC     #100        % set load address to 0x100
        GREG    @           % allocate a base register (optional)
Main    SETL    $0,42       % your code here
        TRAP    0,Halt,0    % halt, exit code in $255
```

A program starts with `$255` holding its entry address — `Main`'s, or the
first instruction's when there is no `Main` (MMIXAL reference). rG starts at
255 minus the number of `GREG`s the program declares — every register but
`$255` is local with none — and rL at 0, `$0` and `$1` zero (MMIX passes argc
and argv there; checksmix's command line passes no arguments). rK, rT, rTT
and rV start `#FFFFFFFFFFFFFFFF`, `#8000000500000000`, `#8000000600000000`
and `#369C200400000000` (TAOCP Vol. 1 Fascicle 1, p. 90). A byte two
statements assemble to the same address loads as their XOR, not the second
overwriting the first.

## Line structure

A statement holds a LABEL field, an OP field and an EXPR field, each
separated from the next by a blank; every field but OP is optional (Knuth,
*The Art of Computer Programming*, Volume 1, Fascicle 1, §1.3.2′ "The MMIX
Assembly Language", p. 34). A label with nothing but blanks and a comment
after it is a statement on its own; anything else there means the OP field
held a word the assembler doesn't recognize, reported as an unknown
operation with the statement printed in full.

`;` separates statements — `SETL $1,1; ADD $1,$1,1` assembles both — and
needs no blank on either side. A statement after a `;` is read exactly like
one at the start of a line, label field included.

`%` is the only comment character. It runs to the end of the line and wins
over a later `;`: in `SETL $1,1 % note; ADD` the `; ADD` sits inside the
comment, so no second statement begins.

A line whose first character is not a letter, a digit, `:` or `_` is a
comment in its entirety — `;`, `*`, `#`, `/` and `-` all open one this way.
An indented line has no label field: its first word is always the OP field,
whatever it spells. A statement after `;` keeps its label field regardless
of indentation. Mnemonics and directives match in upper case only; a
lower-case or mixed-case spelling is an ordinary symbol.

Text past EXPR is a **remark** — Knuth's own word, from his listings'
Remarks column, for the commentary his prose permits there. Two rules
govern it: EXPR is greedy, taking the longest operand field the grammar
reads; and whatever it leaves behind is a remark unless it is mistakable
for part of the statement. Text disqualifies itself from being a remark by:

- abutting the statement, with no blank marking where the statement ended;
- opening with one of `, + - * / ~ & | ^ < > $`, any of which could extend
  an expression or an operand list — `/` is in this set, so `SET $1,2 / 3`
  is an error though `SET $1,2/3` (no blank) still divides to `0` inside
  the expression itself;
- opening with a digit, almost always a dropped operand separator rather
  than commentary, so `HALT 2 apples` is an error, not a warning.

`ADD $1,$2,$3 sum of the parts` assembles, the trailing words a permitted
remark; `SETL $1,2 + 3` is a syntax error, `+ 3` read as continuing the
expression rather than dropped in silence. Text with no statement ahead of
it passes the same test: ignored when it passes (`    # note` assembles),
an unknown operation when it fails (`SETL $1,1;9foo` reports
`unknown operation: 9foo`).

Whitespace around an operand list's commas stays legal — `TRAP 0, Time, 2`
parses — a checksmix extension over MMIXAL, which ends the operand field at
the first blank.

## Assembler directives

| Directive | Syntax | Effect |
| --- | --- | --- |
| `LOC` | `LOC expr` | Set the assembly location counter to *expr*; a label on the same line names the location *before* the move |
| `GREG` | `[label] GREG expr` / `[label] GREG` | Allocate a global register initialized to *expr*, or to `0` with the operand omitted; optional label becomes a register alias; a nonzero value is a base address for the two-operand memory form; at most 223 per program, `$254` down to `$32` |
| `IS` | `Name IS expr` | Define a numeric or register alias constant |
| `PREFIX` | `PREFIX str` | Qualify subsequent unqualified names as `str<name>`; names beginning with `:` opt out |
| `BYTE` | `BYTE expr,...` | Emit one byte per operand |
| `WYDE` | `WYDE expr,...` | Emit one 16-bit wyde per operand |
| `TETRA` | `TETRA expr,...` | Emit one 32-bit tetra per operand |
| `OCTA` | `OCTA expr,...` | Emit one 64-bit octa per operand |
| `LOCAL` | `LOCAL expr` | Declare register *expr* local; checked against the global threshold at the close of assembly |
| `BSPEC` | `BSPEC expr` | Open special mode |
| `ESPEC` | `ESPEC` | Close special mode |
| `INCLUDE` | `INCLUDE file` | Assemble the named file as if inserted here, resolved relative to the including file; recursive, cycles are an error |

A string operand assembles one unit per character. The directive aligns
once, before the first unit; a list does not realign between items. A bare
`""`, the whole item, assembles as one zero unit and warns; the same empty
string beside an operator or inside parentheses is an error.

A string also stands inside a data-list item's own expression, abbreviating
its characters as comma-separated character constants: an operator before
the string applies to its first character and one after it to its last, so
`BYTE 1+"ace"+2,0` is `BYTE 1+'a','c','e'+2,0` — four bytes, `b`, `c`, `g`,
`0`. A string alone as an item keeps the rule above. This expansion reaches
data-list items only; a string is not a valid instruction operand.

The assembler aligns before it places an item: it rounds the location counter
up to the item's natural width — 4 for an instruction, 2, 4 or 8 for `WYDE`,
`TETRA` and `OCTA` — and the label on that line takes the rounded address. The
skipped bytes are a gap rather than emitted padding, and load as zero. `BYTE`
is never aligned. Alignment follows the item's kind, not the count of bytes it
emits, so `BYTE "abcd"` is four bytes wide and still lands wherever the counter
stands.

`LOC` sets the counter exactly; it does not align, and the next instruction or
wide datum rounds up from wherever `LOC` left it. Rounding happens when an item
is assembled, never when a label is defined, so a label alone on its own line
keeps the unrounded counter. A bare label followed by `OCTA` can therefore name
an address up to seven bytes below the octabyte, and since MMIX has no
unaligned access a load through that label rounds back down past the datum. A
label on a `LOC` line itself takes the location the counter held *before* the
move: `X LOC @+500` names `X` as the first of the 500 bytes `LOC` skips, and
assembly continues at `X+500`.

The counter ends at `#FFFFFFFFFFFFFFFF`: an item may fill the address space's
last bytes exactly, but any statement that then needs an address — an
instruction, a data item, a label bound to the counter, or `@` — is an error
until a `LOC` moves the counter to a valid one again.

`BSPEC expr` opens special mode; `ESPEC` closes it. Inside, only `IS`,
`PREFIX`, `GREG`, `LOCAL` and the four data directives are legal — an
instruction or any other directive between them is an error, and `BSPEC`
does not nest. `IS`, `PREFIX`, `GREG` and `LOCAL` keep their full effect
there; the location counter does not move, and a `BYTE`/`WYDE`/`TETRA`/`OCTA`
list inside emits nothing to the object file at all — no MMIX loader would
have loaded it anyway. A label after `ESPEC` has exactly the address it would
have if the whole block were deleted.

### Expressions

Every operand — a register, an immediate, `LOC`'s target, a data item — is an
MMIXAL expression: constants, symbols, `@`, unary operators, and two
left-associative precedence levels of binary operators. A decimal or
hexadecimal constant always has a value, however many digits it spells: one
of 2⁶⁴ or more reduces mod 2⁶⁴, so `OCTA #112233445566778899` assembles
`#2233445566778899` and `OCTA 18446744073709551621` assembles `5`. Every
field an operand fills has a range; see
[Operand ranges](#operand-ranges).

| Level | Operators |
| --- | --- |
| Strong (binds tighter) | `*` `/` `//` `%` `<<` `>>` `&` |
| Weak | `+` `-` `\|` `^` |

`a-b-c` is `(a-b)-c`; `2+3*4` is `14`, since `*` binds tighter than `+`.
Parentheses are the only grouping — MMIXAL has no brackets — and nest freely:
`(2+3)*4` is `20`. Unary operators are `+` (identity), `-` (negate, mod
2⁶⁴), `~` (complement), `$` (cast a pure value to a register number) and `&`
(a symbol's serial number, always rejected — checksmix's object file carries
no symbol table to index).

A bare expression — one with no enclosing parentheses — holds no whitespace:
it is one unbroken run of characters ending at the first space, tab, comma,
`;`, comment character or newline. What follows a bare expression is subject
to the remark rules in "Line structure" above. Write a negative
literal closed up:
`SETI $1,-5`, never `SETI $1,- 5`. A parenthesized group is the one place an
expression may hold whitespace, a checksmix extension over MMIXAL's own
closed-up syntax and a pure superset of it: `SET $1,(2 + 3)` assembles.

`%` is the remainder operator wherever an expression is being parsed, and a
comment character everywhere else — the same rule MMIXAL itself uses.
`5%3` is `2`, the whole thing one unbroken expression; `5 % 3` is `5`, the
space ending the expression before `% 3` opens a comment; `(5 % 3)` is `2`,
whitespace being ordinary inside a group; `(2 + 3) % sum` is `5`, the group
having already closed before `%` opens the comment (so `sum` need not even be
defined).

Arithmetic is on unsigned octabytes: `+` `-` `*` wrap mod 2⁶⁴; `x/y` is
⌊x/y⌋ and illegal at `y=0`; `x//y` is ⌊2⁶⁴·x/y⌋ and illegal at `x≥y`; `x%y` is
the remainder of that same division; `x<<y` is `(x·2ʸ) mod 2⁶⁴` and `x>>y` is
⌊x/2ʸ⌋, both `0` for `y≥64`; `&` `|` `^` are bitwise.

A symbol's value is pure — a label, an `IS` constant, a predefined constant —
or a register — `IS $n`, a `GREG` label. Unary `$` casts a pure value to a
register. Register arithmetic: register+pure, pure+register and
register−pure give a register; register−register gives a pure value; any
other binary operator with a register operand is an error, as is every unary
operator but `+`. With `x IS $1` and `y IS $10`, `x+3` and `3+x` are `$4`,
and `y-x` is the pure value `9`. A register value may run past 255 inside an
expression, but the final value a register site consumes must fit `0..255`,
same as a bare `$256` today.

A program may redefine a predefined symbol with a label, `IS` or `GREG`
before any use of it, and its definition then holds at every reference; a
symbol may be defined again only with the same value.

`@` is the current location: for an instruction, its tetra-aligned address;
for a data directive, the aligned address of the directive's first unit — the
same address for every item in its list, since the whole list is evaluated
before any of it assembles.

A forward reference resolves like any label: in most operands it may name a
symbol defined anywhere in the program, and operators apply to it exactly as
to a resolved value — `JMP Later+4` and `OCTA Later-8` both assemble. Applying
an operator to a forward reference at all is a **checksmix extension**: MMIXAL
assembles in one pass and forbids it outright, but every program it accepts
still assembles identically here. `LOC`, `IS` and `GREG` are the exception:
they resolve only a symbol already defined above, an assembler restriction
this widens to cover expressions rather than lifts.

### Local symbols

A decimal digit followed by `H` defines a local label; the same digit
followed by `B` or `F` references it backward or forward in an operand. `H`
is legal only in the label field, `B`/`F` only in an operand, and all three
are upper case only. Ten counters run independently, one per digit.

`dB` is the address of the last `dH` of that digit at or before the
referencing statement, or `0` when none has appeared yet — never an error.
`dF` is the address of the first `dH` of that digit after the referencing
statement, and an error when none follows. Resolution follows source order,
not address, so a `LOC` that moves the counter backward does not change what
a later `dB`/`dF` sees. `dH` is redefinable: a second `2H` is not a
redefinition error, which is what makes `9H IS 9B+1` a running counter.

```
2H      JMP     2F      % forward: to the second 2H below
        JMP     TestFail
2H      JMP     2B      % backward: to the first 2H above
```

### INCLUDE

`INCLUDE file` is a **checksmix extension**, not part of
MMIXAL. It is a preprocessor stage, not a grammar rule: the
named file is inserted as its own translation unit(s), so errors inside it
report *its own* filename and line numbers rather than the includer's. The path
resolves relative to the including file's own directory (like C's
`#include "..."`), each level resolving against its own directory in turn.
Inclusion is recursive; a cycle (a file re-entering itself while already on
the current include chain) is a hard error naming the chain, and an unreadable
file is a hard error naming the file.

```
INCLUDE lib.mms      % pulls lib.mms in as if inserted here
```

Two known limitations:

- **Own line, no label.** `INCLUDE` must occupy its own line; a line whose
  first token is not `INCLUDE` is left untouched, so a label cannot be
  attached to an `INCLUDE` line. `;` cannot separate an `INCLUDE` from
  anything else either: the operand runs to the end of the line, so a `;`
  lands inside it and fails as an unreadable file naming the whole text.
- **`source_text` first-match-on-filename.** Splitting a host file at an
  `INCLUDE` produces multiple units that share the same filename. The
  debug-info API `source_text(file, line)` resolves a unit by the first match
  on filename, so a caller asking for a line in a later host segment (after
  the `INCLUDE`) may get an earlier segment's text instead. This is a
  pre-existing `source_text` limitation surfaced by `INCLUDE`, not something
  `INCLUDE` itself introduces or fixes.

### Global symbols and PREFIX

The active prefix starts at `:`, the root namespace; `PREFIX :` returns to
it. `x` and `:x` name the same symbol there, and `MMixAssembler::labels`/
`symbols` key a root name without its colon. A label or operand that begins
with `:` opts out of the active `PREFIX`; unqualified names are prefixed by
the active `PREFIX` string instead.

A symbol may carry interior colons: an operand may name a qualified symbol
directly, `Foo:Bar`, and the active `PREFIX` still applies to it unless it
begins with `:`. A symbol never ends with a colon, so the legacy `Label:`
spelling still defines `Label` — but a blank must follow the colon, since
`Label:SET` reads as one qualified name rather than a label and a
mnemonic.

```
        PREFIX  P_
Foo     TRAP    0,Halt,0    % stored as "P_Foo"
:Bar    TRAP    0,Halt,0    % stored as "Bar" (root, PREFIX not applied)
        PREFIX  Lib:
Sub     TRAP    0,Halt,0    % stored as "Lib:Sub"
        PREFIX  :
        SET     $1,Lib:Sub  % a qualified reference names a symbol directly
```

### Multi-source assembly

`checksmix` and `mmixasm` accept multiple `.mms` inputs in one invocation. All files share one symbol space and one byte stream, assembled as if concatenated in command-line order.

```
checksmix run   main.mms lib.mms
checksmix check main.mms lib.mms
checksmix build -o prog.mmo main.mms lib.mms
mmixasm         main.mms lib.mms -o prog.mmo
```

### The `.mmo` object format

`build` writes the MMIXAL reference's object format: a preamble with a
zero timestamp (so the same source always builds the same bytes), one
location record and its data tetras per contiguous run of assembled bytes, a
`debug` string table, and a postamble carrying every `GREG`-initialized
register through `$255`'s entry point. `run` on a `.mmo` reads exactly that
shape back and rejects anything else — a stray record, a foreign-shaped one,
or an unrecognized preamble version — rather than risk loading it wrong. A
`.mmo` built by checksmix 0.3.12 or earlier must be rebuilt.

## Floating-point arithmetic

All floating-point instructions use IEEE 754 double precision. Positive
infinity is the predefined constant `Inf`, rather than a bit pattern the
reader must spell out. Results honor the **rounding mode** in bits 17–16 of special register `rA` (register 21). `rA` is 18 bits wide, so the mode field sits at its top: `PUT rA,$X` above `#3FFFF` is an illegal-instruction interrupt, and since this VM has no interrupt vector, it halts with a diagnostic and exits 1 — every halt but the `TRAP 0,Halt,0` trap does. `PUTI` cannot reach the field — its operand is `Z` alone, eight bits — so selecting a mode needs the register form of `PUT`:

| rA bits 17–16 | Mode | Meaning |
| --- | --- | --- |
| `0` | `ROUND_NEAR` | Round to nearest, ties to even (default) |
| `1` | `ROUND_OFF` | Round toward zero (truncate) |
| `2` | `ROUND_UP` | Round toward +∞ |
| `3` | `ROUND_DOWN` | Round toward −∞ |

Instructions that honor rounding mode: `FADD`, `FSUB`, `FMUL`, `FDIV`, `FSQRT`, `FINT`, `FIX`, `FIXU`, `FLOT`, `FLOTI`, `FLOTU`, `FLOTUI`, `SFLOT`, `SFLOTI`, `SFLOTU`, `SFLOTUI`, `STSF`, `STSFI`.

`FIX`, `FIXU`, `FSQRT`, `FINT`, and the `FLOT`/`FLOTI`/`FLOTU`/`FLOTUI`/`SFLOT`/`SFLOTI`/`SFLOTU`/`SFLOTUI` family additionally take a `Y` operand that overrides the mode for that one instruction, numbered independently of rA's own field:

| `Y` | Symbol | rA-equivalent mode |
| --- | --- | --- |
| `0` | `ROUND_CURRENT` | none — uses rA's current mode |
| `1` | `ROUND_OFF` | `1` |
| `2` | `ROUND_UP` | `2` |
| `3` | `ROUND_DOWN` | `3` |
| `4` | `ROUND_NEAR` | `0` |

`Y` is omitted (the two-operand form) or `0` to defer to `rA`; `Y > 4` raises an illegal-instruction interrupt, and since this VM has no interrupt vector, it halts with a diagnostic instead. `STSF`/`STSFI` take no `Y` operand and always use `rA`'s mode.

**Known gap:** none for the twelve mnemonics above — `Y` overrides `rA`'s mode per-instruction; `Y = 0` or the implicit two-operand form falls back to `rA`.

**NaN results.** `FADD`, `FSUB`, `FMUL`, `FDIV`, `FREM`, `FSQRT`, and `FINT`
build every NaN result themselves rather than take whatever the host CPU's
arithmetic produces, so a result is identical on every target. A signaling
NaN operand raises I; every NaN operand is quieted (its fraction's top bit
set) before it can appear in a result. For a binary operation the result is
`$Z` if `$Z` is a NaN, otherwise `$Y`; `FSUB` negates `$Z` only when `$Z` is
not a NaN, since it computes `$Y + (−$Z)`. `FIX` and `FIXU` instead copy an
infinite or NaN operand through unchanged, raising I alone.

Each invalid operation (a NaN operand aside) yields `NaN(1/2)`
(`#7FF8000000000000`, or with the sign bit set) and raises I: `FADD`'s
∞ + (−∞) is signed as `$Z`'s; `FSUB`'s ∞ − ∞ as the negated `$Z`'s; `FMUL`'s
`0 × ∞` and `FDIV`'s `0/0` and `∞/∞` by the operands' sign product; `FREM`
with an infinite `$Y` or a zero `$Z` as `$Y`'s; `FSQRT` of a negative
operand (∞ included) always negative.

**Signed zero.** An exactly-zero `FADD`/`FSUB` result is `+0` in every
rounding mode but ROUND_DOWN, except `(−0) + (−0) = −0`. In ROUND_DOWN it is
`−0`, except `(+0) + (+0) = +0`.

### rA event flags

An arithmetic exception whose enable bit (below) is clear ORs its event flag
into `rA`; event flags are never cleared automatically. One whose enable bit
is set trips to its handler instead, and its event flag stays clear — see
"User trips". The bit values are the predefined symbols `D_BIT` … `X_BIT`,
rather than spellings a program must supply itself.

| Flag | rA bit | Kind | Raised when |
| --- | --- | --- | --- |
| X | `0x01` | floating | Result is inexact (rounded) |
| Z | `0x02` | floating | A finite nonzero dividend divided by zero (`FDIV`); alone, never with O or X |
| U | `0x04` | floating | Underflow |
| O | `0x08` | floating | Overflow: the value, rounded in rA's current mode with the exponent unbounded, exceeds the largest finite number — the largest short float for `STSF`/`STSFI`, the largest double for `FADD`/`FSUB`/`FMUL`/`FDIV`; always with X |
| I | `0x10` | floating | A signaling NaN operand; a quiet one does not raise it. Also an invalid operation (0/0, ∞−∞, etc.), or `FIX`/`FIXU` of an infinite or NaN operand |
| W | `0x20` | floating | `FIX`'s rounded result falls below `−2^63` or above `2^63 − 1`; `FIXU` never raises it |
| V | `0x40` | integer | Integer overflow — `ADD`, `SUB`, `MUL`, `NEG`, `DIV` of `#8000000000000000` by −1, `SL`, and the signed stores `STB`/`STW`/`STT` |
| D | `0x80` | integer | Divide check — signed division by zero |

On overflow the delivered result depends on rA's mode: ROUND_NEAR gives
±∞; ROUND_OFF gives ±the format's largest finite value; ROUND_UP gives +∞
for a positive result and −the largest finite value for a negative one;
ROUND_DOWN mirrors it, +the largest finite value and −∞. O follows the
value rounded in rA's mode with the exponent unbounded: rounding to
exactly the largest finite value is not itself overflow — ROUND_OFF of the
largest double plus half its ulp rounds, exponent unbounded, back down to
exactly that value and raises X alone, not O — but rounding, exponent
unbounded, past that value does raise O, even where the ordinary
(exponent-bounded) round-to-nearest result was itself still finite.

There is no denormalized-operand event: a subnormal operand raises nothing.
A rounded result below the normal range raises `U` only when the exact
result is not itself an exact subnormal, or when `U`'s enable bit is set (an
enabled `U` trips on a subnormal or zero result, exact or not, except an
`FADD`/`FSUB` with a zero operand or a zero sum); an underflow with the bit
clear always raises `U` and `X` together. `FREM` and
`FSQRT` raise `U` in no case — the IEEE remainder is exact by definition,
and the square root of a nonzero finite operand is neither zero nor
subnormal. `DIVU` raises no divide check, because `u($Z) ≤ u(rD)` — which
includes a zero divisor — is part of its definition rather than an error.
`±∞` divided by `±0` raises nothing: the result is an exact infinity.

Read/clear `rA` with `GET $X,rA` / `PUT rA,$X`.

### User trips

`rA`'s low byte holds the eight event flags above; the next byte holds their
enable bits, one per event flag, in the same `D V W I O U Z X` order. `PUT
rA,$X` writes both bytes at once (bounded to 18 bits total, the rounding mode
occupying the top two — see above); enabling an exception is `PUT
rA,$X` with the matching bit set two places left of its event flag. `PUTI`'s
operand is a single byte, too narrow to reach the enable byte — set a
register and use the register form of `PUT`.

`TRIP X,Y,Z` always trips, unconditionally, to the handler at `#00`. An
arithmetic exception trips only when its enable bit is set; instead of the
event flag, control transfers to a fixed handler address, the predefined
symbols `D_Handler` … `X_Handler` (`#10` `#20` `#30` `#40` `#50` `#60` `#70`
`#80`) for `D V W I O U Z X` respectively. An
instruction that raises several exceptions at once trips to the leftmost
enabled one, in `D V W I O U Z X` order; every other raised exception whose
enable bit is clear still sets its event flag, but one that is enabled and
not leftmost is dropped entirely — no trip, no event flag.

A trip — explicit or arithmetic — sets `rB ← $255`, `$255 ← rJ`, `rW` to the
address of the instruction after the one that trips, and `rX` to `#80000000`
in the high tetra with that instruction's own opcode/X/Y/Z in the low tetra
(always negative, since the top bit is set). `rY` and `rZ` take the operand
values the instruction used: a register operand's contents, or the literal
field for an immediate or non-register operand — `ADDI $3,$2,5` gives
`rZ = 5`, and `FIX`/`FLOT`'s rounding-mode `Y` and `NEG`'s immediate `Y` are
non-register fields too. A store trip (`STB`/`STW`/`STT`/`STSF`, either form)
sets `rY` to the computed address and `rZ` to the aligned octabyte memory
holds after the store: the stored bytes in place, the rest unchanged. An
arithmetic trip's operands are captured before the instruction's own
destination write, so `ADD $5,$5,$3` overflowing still shows the handler the
pre-`ADD` `$5` in `rY`.

`RESUME 0` returns from a handler: if `rX` is negative — always true right
after a trip — execution continues at `rW`. Otherwise `rX`'s top byte is a
ropcode; `0` reinserts the instruction in `rX`'s low tetra as if it stood at
`rW − 4`, then continues at `rW` — unless that instruction is itself
`RESUME`, which halts the same way ropcodes `1`–`3` do rather than
dispatching itself again. Ropcodes `1`–`3` (operand substitution,
forced-trap emulation, page-table insertion) and `RESUME` with a nonzero `Z`
(the kernel's `RESUME 1`) have nothing in this VM to act on and halt with a
diagnostic instead, PC unmoved.

A trip to a vector nothing ever loaded would read zero, which decodes as a
silent `TRAP 0,0,0` — instead it halts with a diagnostic naming the trip and
the vector, and a nonzero exit code, after every register above is set so a
debugger can see why.

### Epsilon instructions (FCMPE / FUNE / FEQLE)

`FCMPE`, `FUNE`, and `FEQLE` are the "with epsilon" variants of `FCMP`, `FUN`, and `FEQL`. Each compared value `u` has an ε-neighborhood `Nε(u)`, scaled by its own binade: for a normal `u` the radius is `2^(e−1022)·ε`, where `e` is `u`'s raw IEEE-754 biased exponent field; for a denormal it is the fixed `2^−1021·ε`; `Nε(0) = {0}`; `Nε(+∞)` is `{+∞}` when `ε < 1`, every value except `−∞` when `1 ≤ ε < 2`, and every value when `ε ≥ 2` (mirrored for `−∞`). `FCMPE` reports `$Y ≺ $Z` (`-1`), `$Y ∼ $Z` (`0`, meaning `$Y ∈ Nε($Z)` or `$Z ∈ Nε($Y)`), or `$Y ≻ $Z` (`+1`). `FEQLE` reports the stronger `$Y ≈ $Z` (`1`), which requires both memberships to hold, and `0` otherwise.

`FCMPE` and `FEQLE` force their result to `0` and raise `I` when `$Y`, `$Z`, or `rE` is NaN, or `rE` is negative — never on an ordinary inequality. `FUNE` reports `1` on exactly that same exceptional condition and `0` otherwise; it says nothing about proximity, and raises no flag either way.

## TRAP interface

`TRAP 0, Code, Handle` invokes a system call identified by the predefined
symbol *Code*, numbered per the MMIXAL reference. `Handle` (`Z`) names an
open handle, 0–255. A call with one further argument takes it in `$255`
directly; a call with two takes an address in `$255`, the first argument as
the octa there and the second as the octa at `$255+8`. The result replaces
`$255`; a negative result means failure.

| Code | Value | Extra arguments | Result in `$255` |
| --- | --- | --- | --- |
| `Halt` | 0 | `$255` = exit code | — (halts) |
| `Fopen` | 1 | name address, mode | 0, or −1 |
| `Fclose` | 2 | — | 0, or −1 |
| `Fread` | 3 | buffer, size | 0 if all `size` bytes read; `n − size` if end of file after `n`; `−1 − size` on error |
| `Fgets` | 4 | buffer, size | characters stored, or −1 |
| `Fgetws` | 5 | buffer, size | wydes stored, or −1 |
| `Fwrite` | 6 | buffer, size | 0, or `n − size` after writing `n` |
| `Fputs` | 7 | `$255` = string address | bytes written, or −1 |
| `Fputws` | 8 | `$255` = string address | wydes written, or −1 |
| `Fseek` | 9 | `$255` = offset | 0, or −1 |
| `Ftell` | 10 | — | position, or −1 |

`size` is a full octabyte for both `Fread` and `Fwrite`, taken literally: 0
moves nothing, and 2^40 asks for 2^40 bytes. Neither call bounds a request;
each moves it in chunks of at most 1,048,576 bytes, so host memory does not
grow with `size`. `Fwrite` returns `n − size` only when a write stops short
after `n` bytes. Memory and disk are the operator's: an `Fread` from a
device that never ends, such as `/dev/zero`, reads until `size` bytes
arrive, and an `Fwrite` of a huge `size` writes until it finishes or a
write fails. `--max-steps` interrupts neither, since it counts
instructions and the whole `TRAP` is one.

`Fopen`'s mode is one of `TextRead` (0), `TextWrite` (1), `BinaryRead` (2),
`BinaryWrite` (3), `BinaryReadWrite` (4). A handle carries four capability
bits: read, write, seek, and read-write. Text modes grant read or write
alone; binary read/write modes add seek; `BinaryReadWrite` grants all four
and switches — a read clears the write capability and a write clears the
read capability, until `Fseek` restores both. The three write modes
truncate an existing file. A call on a handle lacking the needed capability
fails with the table's failure value and touches no file. `Fopen` lets the
program choose the handle; opening one already open closes it first, and a
failed open leaves the handle closed.

`Fopen`'s name is the bytes at its address up to the first zero byte,
passed to the host unchanged and whole; the filesystem's own path limit
rejects a long one. A name that is not valid UTF-8 fails with −1 and
touches no file.

`Fgets` reads until `size − 1` characters or a newline, then a zero byte,
returning the count stored (a partial last line at end of file included), or
−1 when `size` is 0 or nothing was read. `Fgetws`/`Fputws` move wyde
characters, two bytes each in memory order, raw to and from the file:
`Fgetws` rounds its buffer address down to even and stops at the wyde
`#000A`, `size − 1` wydes, or end of file; `Fputws` writes up to, not
including, the first zero wyde. `Fputs` writes up to, not including, the
first zero byte, with no byte value translated. `Fputs` and `Fputws` write
the whole string in chunks, however long, and return the bytes or wydes
written, or −1 on a write error, even one after earlier chunks landed.
`Fseek`'s offset, `≥ 0`, positions that many bytes from the start;
`< 0` positions `−offset − 1` bytes before the end, so `−1` is the end
itself.

Handles 0, 1 and 2 (`StdIn`, `StdOut`, `StdErr`, the predefined symbols'
values) are open at start with `TextRead`, `TextWrite`, `TextWrite`.
**Departure from the reference:** `Fopen`/`Fclose` on any of them return −1
and leave the stream as it was, rather than letting the program rebind them
— checksmix routes handles 1 and 2 through the host's own write and handle 0
through its own read, neither of which has a file underneath to rebind.
`checksmix run` reads the shell's own stdin; `mmixdb --stdin FILE` reads
FILE, replayed from the start on every run, and without `--stdin` a read
fails; an embedder's own `Host` decides for itself, and fails by default.

**Departure from the reference:** checksmix rejects an `Fopen` name that is
not valid UTF-8; the reference accepts any name the host allows.

### Extensions

Three codes are checksmix's own, numbered `#80`–`#82` so an old binary's
codes 11–13 halt as an unsimulated `TRAP` rather than running the wrong call:

| Code | Value | Behavior |
| --- | --- | --- |
| `Fputc` | `#80` | Write one byte (`$255`'s low byte) to `Handle`; shares `Fputs`'s capability check and read-write switching. Returns 0, or −1. |
| `Time` | `#81` | `Handle` (`Z`) selects the unit: 0 seconds, 1 milliseconds, 2 microseconds since the Unix epoch. Returns the time in `$255`. |
| `Debug` | `#82` | Backs the `debug "text"` directive, below. |

A `TRAP` outside both tables above — any `X ≠ 0`, or `X = 0` with a `Code`
neither one lists — sets `rBB ← $255`, `$255 ← rJ`, `rWW` to the address of
the instruction after the `TRAP`, `rXX` to the `TRAP` instruction word with
its high bit set, and `rYY`/`rZZ` to `$Y`/`$Z`, exactly as the reference's
TRAP page gives them. It then halts with a diagnostic, PC on the `TRAP`,
exit code 1 — this VM has no kernel at `rT` to receive control.

### `debug "text"`

`debug "text"` is a checksmix extension, not part of MMIXAL. It assembles to
one `TRAP 0,Debug,K` at its own address — labelled with the directive's own
label, if it has one — where `K` is the directive's index, 0-based, in
program order across every translation unit assembled together. `K` is one
byte, so a 257th `debug` directive in one program is an assembly error
naming its file and line.

The directive's text is taken exactly as written between the quotes, as its
own UTF-8 bytes, and lives in a table outside guest memory: nothing is
written to guest memory and no label is generated. Running the TRAP writes
the string and a newline (`#0A`) to handle 1, changing no register —
`$255` included. A `K` past the table's end prints nothing and reports a
diagnostic instead.

## Register stack

Every register from `rL` through `rG-1` is marginal and reads zero. `PUT rG,z`
accepts a value only for `32 ≤ z ≤ 255` with `z ≥ rL`; any other value is an
illegal-instruction interrupt, and since this VM has no interrupt vector, it
halts with a diagnostic. A legal `PUT rG` zeroes every register between the
old and new `rG` — global to local/marginal when raising, local/marginal to
global when lowering.

`SAVE $X, 0` requires `X` global (`X ≥ rG`); a local `X` halts with a
diagnostic. It pushes a context onto the register stack at the current `rO`,
growing upward, in this order: the `rL` local registers `$0..$(rL-1)`, a
marker octa holding `rL`, the global registers `$rG..$255`, the twelve
special registers `rB rD rE rH rJ rM rR rP rW rX rY rZ`, and one packed octa
holding `rG` in its top byte and `rA` in its low bits. `$X` receives the
packed octa's address; `rO` and `rS` both become the address of the byte
after it, and `rL` becomes 0. `rJ` is saved as data among the specials, never
overwritten — `SAVE` opens no call frame.

`UNSAVE 0, $Z`, or its one-operand spelling `UNSAVE $Z`, restores a context
whose topmost (packed) octa `$Z` addresses, validating it whole before
changing anything: a packed `rG` outside
`32..255`, a packed `rA` above the widest legal value, or a saved local
count greater than the packed `rG` all halt with a diagnostic and the
machine unchanged. Otherwise every saved register restores, `rL` becomes the
saved local count, and `rO = rS` land at the address of the first restored
local — where `rO` stood before the matching `SAVE`.

A nonzero must-be-zero field — `SAVE`'s `Y` and `Z`, `UNSAVE`'s `X` and `Y` —
is an illegal-instruction interrupt, which halts. `GET`, `PUT` and `RESUME`
carry the same rule: `GET`'s and `PUT`'s `Y`, and `RESUME`'s `X` and `Y`, must
also be zero.

Writing a marginal register `$X` raises `rL` to `X+1` and zeroes `$rL`
through `$X`. For an instruction whose `X` field is a general-register
destination, this rise happens before the instruction runs, so `GET $X,rL`
stores the raised `rL`, not the value it held when the instruction began.
`PUT rL,z` (and `PUTI`) only ever lowers `rL`, to `min(z, rL)`, and zeroes
every register the drop excludes from the local range.

The register stack lives in memory at `rO`: `S[k] = M8[rO+8k]`. `PUSHJ $X,
addr` (and `PUSHJB`, `PUSHGO`, `PUSHGOI`) push the caller's local registers
there and slide the window down. For `X < rG`: `S[0..X] = $0..$X`, with the
marginal slot `S[X]` holding `X` itself — the hole `POP` reads back; `rO`
and `rS` both advance to `rO + 8(X+1)`. The caller's `$(X+1)..$(rL-1)`
become the callee's `$0..$(rL-X-2)`; `rL` becomes `saturating_sub(rL,
X+1)`. For `X ≥ rG`: all of `$0..$(rL-1)` push the same way, followed by
the marker `rL`, and the callee starts with `rL = 0` — the hole for the
later `POP` is `rL`, not `X`.

`POP X, YZ` returns from a `PUSHJ $x` frame whose callee has `rL = L`. It
reads the hole from memory: `x = M8[rO-8] mod 256`. If `X > L`, `X` becomes
`L+1` and the hole gets zero. The caller's `$0..$(x-1)` restore from
`M8[rO-8(x+1)..]`; `$x` gets the callee's `$(X-1)` (the *last* output lands
in the hole), or zero when `X = 0` or the clamp fired; `$(x+1)..$(x+X-1)`
get the callee's `$0..$(X-2)`. `rO` and `rS` retract to `rO - 8(x+1)` —
exactly where the matching `PUSHJ` found them. `rL` becomes `min(x+X, rG)`;
every register from the new `rL` through `rG-1` reads zero. `POP` branches
to `rJ + 4·YZ` and leaves `rJ` unchanged; a subroutine that calls another
saves `rJ` (`GET $k,rJ`) and restores it (`PUT rJ,$k`) before its own
`POP`.

`rO` and `rS` always agree between instructions: every `PUSHJ` and `POP`
stores eagerly and moves both together, where MMIXware may leave `rS`
behind a ring of unspilled registers.

Measured on MMIXware:

| Program | Register | MMIXware |
|---|---|---|
| Caller sets `$1..$5` = 111..555; `PUSHJ $0`; callee sets `$0`=999, `$1`=777; `POP 1,0` | `$0` | 999 |
| | `$1..$5` | 0 |
| | `rL` | 1 |
| Caller sets `$0..$6` = 10..70; `PUSHJ $3`; callee sets `$0,$1,$2` = 801,802,803; `POP 2,0` | `$0..$2` | 10, 20, 30 |
| | `$3` (hole) | 802 |
| | `$4` | 801 |
| | `$5, $6` | 0, 0 |
| | `rL` | 5 |

## Operand counts

MMIXAL's general rule: three operands fill X, Y and Z; two fill X and YZ;
one fills XYZ; an empty operand field is the single operand `0`. Most
mnemonics take a fixed count. These take the range the reference allows,
each field a pure byte or a register unless noted:

| Form | Fields |
| --- | --- |
| `TRAP x,y,z` / `TRIP` / `SWYM` | X, Y, Z |
| `TRAP x,yz` / `TRIP` / `SWYM` | X=x, Y=yz>>8, Z=yz&255 |
| `TRAP xyz` / `TRIP` / `SWYM` | X=xyz>>16, Y=(xyz>>8)&255, Z=xyz&255 |
| bare `TRAP` / `TRIP` / `SWYM` | every field 0 |
| `POP p,yz` | X=p, YZ=yz |
| `POP xyz` | XYZ=xyz |
| bare `POP` | every field 0 |
| bare `RESUME` / `SYNC` | XYZ=0; the one-operand form is unchanged |
| `UNSAVE $Z` | the one-operand spelling of `UNSAVE 0,$Z` |
| bare `UNSAVE` / bare `SAVE` | error |
| `NEG $X,z` / `NEGU $X,z` | `NEG $X,0,z`: Y omitted is 0 |
| `label GREG` with no operand | a global register holding 0 |
| `PUSHJ`, `PUSHJB`, `PUSHGO` X | a pure byte or a register, same bytes; `GO`'s X stays a register |
| `PRELD`, `PREGO`, `PREST`, `SYNCD`, `SYNCID`, `STCO` X | a pure byte or a register, same bytes |

The longest matching form wins, so `TRAP 0,1,2` fills every field rather
than leaving `,2` behind. A partial list — `TRAP 0,`, `POP 1,` — is still an
error.

## Operand ranges

Every value that fills an instruction field must fit that field's range or
assembly fails at the operand:

| Field | Range | Sites |
| --- | --- | --- |
| Special register | `0..31` | `GET`'s `Z`; `PUT`'s and `PUTI`'s `X` |
| Byte | `0..255` | `Z` of every explicit `*I` three-operand spelling, `STCO`'s `X` included; `Y` of `NEG`/`NEGU` and of the float rounding-mode forms; `PUTI`'s `Z`; `SAVE`'s `Z`; `UNSAVE`'s `X`; `POP p,yz`'s `X` |
| Wyde | `0..65535` | the sixteen wyde immediates `SETL`, `SETH`, `SETMH`, `SETML`, `INCL`, `INCH`, `INCMH`, `INCML`, `ORH`, `ORMH`, `ORML`, `ORL`, `ANDNH`, `ANDNMH`, `ANDNML`, `ANDNL`; `yz` of `TRAP`/`TRIP`/`SWYM` and `POP` |
| Three bytes | `0..16777215` | `xyz` of `TRAP`/`TRIP`/`SWYM` and `POP`; `RESUME`; `SYNC` |

A negative value is its 64-bit two's complement and fails every field
narrower than 64 bits: `ADDI $1,$2,-1` is an error, as is `SET $1,-1`
(naming `SETI` and `NEG`). `RESUME` and `SYNC` take the MMIXAL definition's
24-bit `XYZ`, all three bytes reaching the encoding; the machine halts on a
`SYNC` code above 7 regardless.

This departs from the MMIXAL reference, which warns on an out-of-range
instruction field and keeps its low bits rather than rejecting it; a
truncated field would otherwise assemble a different instruction.

A data directive's value follows the reference instead: `BYTE`, `WYDE` and
`TETRA` warn and keep the value's low byte, wyde or tetra when it overflows
that width; `OCTA` never overflows. A string's characters are data values
like any other. A bare `""` — the whole item — assembles as one zero unit
of the directive's width and warns; the same empty string beside an
operator or inside parentheses stays an error.

## Instruction table

| Mnemonic | Operands | Description |
| --- | --- | --- |
| `SET` | `SET $X, $Y` / `SET $X, imm` | MMIXAL alias — emits `ORI $X, $Y, 0` for a register, `SETL $X, imm` for a wyde-wide immediate; an immediate outside `0..#FFFF` is an error (see [Operand ranges](#operand-ranges)) |
| `SETI` | `SETI $X, imm` | checksmix extension — sets a full 64-bit constant in four tetras, clearing the register |
| `SETL` | `SETL $X, YZ` | Set low wyde; the other 48 bits become zero |
| `SETH` | `SETH $X, YZ` | Set high wyde; the other 48 bits become zero |
| `SETMH` | `SETMH $X, YZ` | Set medium-high wyde; the other 48 bits become zero |
| `SETML` | `SETML $X, YZ` | Set medium-low wyde; the other 48 bits become zero |
| `INCH` | `INCH $X, YZ` | Add into the high wyde; the other 48 bits are preserved |
| `INCMH` | `INCMH $X, YZ` | Add into the medium-high wyde; a carry propagates into the high wyde |
| `INCML` | `INCML $X, YZ` | Add into the medium-low wyde; a carry propagates into the higher wydes |
| `INCL` | `INCL $X, YZ` | Add into the low wyde, unsigned wrapping; a carry propagates into the higher wydes |
| `ORH` | `ORH $X, YZ` | Set bits in the high wyde; the other 48 bits are preserved |
| `ORMH` | `ORMH $X, YZ` | Set bits in the medium-high wyde; the other 48 bits are preserved |
| `ORML` | `ORML $X, YZ` | Set bits in the medium-low wyde; the other 48 bits are preserved |
| `ORL` | `ORL $X, YZ` | Set bits in the low wyde; the other 48 bits are preserved |
| `ANDNH` | `ANDNH $X, YZ` | Clear bits in the high wyde; the other 48 bits are preserved |
| `ANDNMH` | `ANDNMH $X, YZ` | Clear bits in the medium-high wyde; the other 48 bits are preserved |
| `ANDNML` | `ANDNML $X, YZ` | Clear bits in the medium-low wyde; the other 48 bits are preserved |
| `ANDNL` | `ANDNL $X, YZ` | Clear bits in the low wyde; the other 48 bits are preserved |
| `LDB` | `LDB $X, $Y, $Z` / `LDB $X, $Y` | Load byte signed (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `LDBI` | `LDB $X, $Y, Z` | Load byte signed (immediate) |
| `LDBU` | `LDBU $X, $Y, $Z` / `LDBU $X, $Y` | Load byte unsigned (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `LDBUI` | `LDBU $X, $Y, Z` | Load byte unsigned (immediate) |
| `LDW` | `LDW $X, $Y, $Z` / `LDW $X, $Y` | Load wyde signed (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `LDWI` | `LDW $X, $Y, Z` | Load wyde signed (immediate) |
| `LDWU` | `LDWU $X, $Y, $Z` / `LDWU $X, $Y` | Load wyde unsigned (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `LDWUI` | `LDWU $X, $Y, Z` | Load wyde unsigned (immediate) |
| `LDT` | `LDT $X, $Y, $Z` / `LDT $X, $Y` | Load tetra signed (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `LDTI` | `LDT $X, $Y, Z` | Load tetra signed (immediate) |
| `LDTU` | `LDTU $X, $Y, $Z` / `LDTU $X, $Y` | Load tetra unsigned (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `LDTUI` | `LDTU $X, $Y, Z` | Load tetra unsigned (immediate) |
| `LDO` | `LDO $X, $Y, $Z` / `LDO $X, $Y` | Load octa (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `LDOI` | `LDO $X, $Y, Z` | Load octa (immediate) |
| `LDOU` | `LDOU $X, $Y, $Z` / `LDOU $X, $Y` | Load octa unsigned (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `LDOUI` | `LDOU $X, $Y, Z` | Load octa unsigned (immediate) |
| `LDUNC` | `LDUNC $X, $Y, $Z` / `LDUNC $X, $Y` | Load octa uncached (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `LDUNCI` | `LDUNC $X, $Y, Z` | Load octa uncached (immediate) |
| `LDHT` | `LDHT $X, $Y, $Z` / `LDHT $X, $Y` | Load high tetra (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `LDHTI` | `LDHT $X, $Y, Z` | Load high tetra (immediate) |
| `LDSF` | `LDSF $X, $Y, $Z` / `LDSF $X, $Y` | Load short float (widen f32 → f64) (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `LDSFI` | `LDSF $X, $Y, Z` | Load short float (immediate) |
| `LDVTS` | `LDVTS $X, $Y, $Z` / `LDVTS $X, $Y` | Load virtual translation status (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `LDVTSI` | `LDVTS $X, $Y, Z` | Load virtual translation status (immediate) |
| `CSWAP` | `CSWAP $X, $Y, $Z` / `CSWAP $X, $Y` | Compare and swap: if `M8[$Y+$Z] = rP`, store `$X` there and set `$X ← 1`; otherwise `rP ← M8[$Y+$Z]` and `$X ← 0` (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `CSWAPI` | `CSWAP $X, $Y, Z` | Compare and swap (immediate): if `M8[$Y+Z] = rP`, store `$X` there and set `$X ← 1`; otherwise `rP ← M8[$Y+Z]` and `$X ← 0` |
| `LDA` | `LDA $X, $Y, $Z` / `LDA $X, addr` | Load address of `$Y + $Z` — the `ADDU $X, $Y, $Z` alias; two-operand form described below the table |
| `LDAI` | `LDA $X, $Y, Z` / `LDAI $X, addr` | Load address of `$Y + Z` — the `ADDU $X, $Y, Z` alias; two-operand form described below the table |
| `STB` | `STB $X, $Y, $Z` / `STB $X, $Y` | Store byte signed (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `STBI` | `STB $X, $Y, Z` | Store byte signed (immediate) |
| `STBU` | `STBU $X, $Y, $Z` / `STBU $X, $Y` | Store byte unsigned (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `STBUI` | `STBU $X, $Y, Z` | Store byte unsigned (immediate) |
| `STW` | `STW $X, $Y, $Z` / `STW $X, $Y` | Store wyde signed (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `STWI` | `STW $X, $Y, Z` | Store wyde signed (immediate) |
| `STWU` | `STWU $X, $Y, $Z` / `STWU $X, $Y` | Store wyde unsigned (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `STWUI` | `STWU $X, $Y, Z` | Store wyde unsigned (immediate) |
| `STT` | `STT $X, $Y, $Z` / `STT $X, $Y` | Store tetra signed (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `STTI` | `STT $X, $Y, Z` | Store tetra signed (immediate) |
| `STTU` | `STTU $X, $Y, $Z` / `STTU $X, $Y` | Store tetra unsigned (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `STTUI` | `STTU $X, $Y, Z` | Store tetra unsigned (immediate) |
| `STO` | `STO $X, $Y, $Z` / `STO $X, $Y` | Store octa (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `STOI` | `STO $X, $Y, Z` | Store octa (immediate) |
| `STOU` | `STOU $X, $Y, $Z` / `STOU $X, $Y` | Store octa unsigned (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `STOUI` | `STOU $X, $Y, Z` | Store octa unsigned (immediate) |
| `STUNC` | `STUNC $X, $Y, $Z` / `STUNC $X, $Y` | Store octa uncached (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `STUNCI` | `STUNC $X, $Y, Z` | Store octa uncached (immediate) |
| `STCO` | `STCO X, $Y, $Z` / `STCO X, $Y` | Store constant octabyte, or to the base address `$Y` alone resolves to; `X` is a byte or a register holding one |
| `STCOI` | `STCO X, $Y, Z` | Store constant octabyte, immediate address; `X` is a byte or a register holding one |
| `STHT` | `STHT $X, $Y, $Z` / `STHT $X, $Y` | Store high tetra (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `STHTI` | `STHT $X, $Y, Z` | Store high tetra (immediate) |
| `STSF` | `STSF $X, $Y, $Z` / `STSF $X, $Y` | Store short float (narrow f64 → f32, honors rA rounding) (the two-operand form's `$Y` is a register, an offset of zero, or a base-relative address) |
| `STSFI` | `STSF $X, $Y, Z` | Store short float (immediate) |
| `ADD` | `ADD $X, $Y, $Z` | Add signed (sets overflow) |
| `ADDI` | `ADD $X, $Y, Z` | Add signed immediate |
| `ADDU` | `ADDU $X, $Y, $Z` | Add unsigned (wrapping, same as LDA) |
| `ADDUI` | `ADDU $X, $Y, Z` | Add unsigned immediate |
| `ADDU2` | `2ADDU $X, $Y, $Z` | `$X = 2*$Y + $Z` unsigned |
| `ADDU2I` | `2ADDU $X, $Y, Z` | `$X = 2*$Y + Z` unsigned |
| `ADDU4` | `4ADDU $X, $Y, $Z` | `$X = 4*$Y + $Z` unsigned |
| `ADDU4I` | `4ADDU $X, $Y, Z` | `$X = 4*$Y + Z` unsigned |
| `ADDU8` | `8ADDU $X, $Y, $Z` | `$X = 8*$Y + $Z` unsigned |
| `ADDU8I` | `8ADDU $X, $Y, Z` | `$X = 8*$Y + Z` unsigned |
| `ADDU16` | `16ADDU $X, $Y, $Z` | `$X = 16*$Y + $Z` unsigned |
| `ADDU16I` | `16ADDU $X, $Y, Z` | `$X = 16*$Y + Z` unsigned |
| `SUB` | `SUB $X, $Y, $Z` | Subtract signed (sets overflow) |
| `SUBI` | `SUB $X, $Y, Z` | Subtract signed immediate |
| `SUBU` | `SUBU $X, $Y, $Z` | Subtract unsigned (wrapping) |
| `SUBUI` | `SUBU $X, $Y, Z` | Subtract unsigned immediate |
| `NEG` | `NEG $X, Y, $Z` / `NEG $X, $Z` | `$X = Y − $Z` signed (Y is literal; omitted Y is 0) |
| `NEGI` | `NEG $X, Y, Z` | `$X = Y − Z` signed |
| `NEGU` | `NEGU $X, Y, $Z` / `NEGU $X, $Z` | `$X = Y − $Z` unsigned (omitted Y is 0) |
| `NEGUI` | `NEGU $X, Y, Z` | `$X = Y − Z` unsigned |
| `MUL` | `MUL $X, $Y, $Z` | Multiply signed |
| `MULI` | `MUL $X, $Y, Z` | Multiply signed immediate |
| `MULU` | `MULU $X, $Y, $Z` | Multiply unsigned (high half in rH) |
| `MULUI` | `MULU $X, $Y, Z` | Multiply unsigned immediate |
| `DIV` | `DIV $X, $Y, $Z` | Divide signed (remainder in rR) |
| `DIVI` | `DIV $X, $Y, Z` | Divide signed immediate |
| `DIVU` | `DIVU $X, $Y, $Z` | Divide unsigned |
| `DIVUI` | `DIVU $X, $Y, Z` | Divide unsigned immediate |
| `FCMP` | `FCMP $X, $Y, $Z` | Floating compare: `$X` = −1/0/+1; unordered operands give 0 and raise I |
| `FUN` | `FUN $X, $Y, $Z` | Floating unordered: `$X` = 1 if NaN |
| `FEQL` | `FEQL $X, $Y, $Z` | Floating equal: `$X` = 1 if equal |
| `FCMPE` | `FCMPE $X, $Y, $Z` | Floating compare with epsilon (rE) |
| `FUNE` | `FUNE $X, $Y, $Z` | Floating unordered with epsilon (rE) |
| `FEQLE` | `FEQLE $X, $Y, $Z` | Floating equivalent with epsilon (rE) |
| `FADD` | `FADD $X, $Y, $Z` | Floating add (honors rA rounding) |
| `FSUB` | `FSUB $X, $Y, $Z` | Floating subtract (honors rA rounding) |
| `FMUL` | `FMUL $X, $Y, $Z` | Floating multiply (honors rA rounding) |
| `FDIV` | `FDIV $X, $Y, $Z` | Floating divide (honors rA rounding) |
| `FREM` | `FREM $X, $Y, $Z` | Floating remainder (IEEE 754 round-half-to-even); a zero remainder takes the dividend's sign |
| `FSQRT` | `FSQRT $X, $Z` / `FSQRT $X, Y, $Z` | Floating square root (honors rA rounding; Y = mode override) |
| `FINT` | `FINT $X, $Z` / `FINT $X, Y, $Z` | Round float to integer (honors rA rounding; Y = mode override) |
| `FIX` | `FIX $X, $Z` / `FIX $X, Y, $Z` | Convert float → signed integer (honors rA rounding; Y = mode override) |
| `FIXU` | `FIXU $X, $Z` / `FIXU $X, Y, $Z` | Convert float → unsigned integer, reduced mod 2^64 (honors rA rounding; Y = mode override) |
| `FLOT` | `FLOT $X, $Z` / `FLOT $X, Y, $Z` | Convert signed integer → float (honors rA rounding; Y = mode override) |
| `FLOTI` | `FLOT $X, Z` / `FLOT $X, Y, Z` | Convert signed integer → float immediate (honors rA rounding; Y = mode override) |
| `FLOTU` | `FLOTU $X, $Z` / `FLOTU $X, Y, $Z` | Convert unsigned integer → float (honors rA rounding; Y = mode override) |
| `FLOTUI` | `FLOTU $X, Z` / `FLOTU $X, Y, Z` | Convert unsigned integer → float immediate (honors rA rounding; Y = mode override) |
| `SFLOT` | `SFLOT $X, $Z` / `SFLOT $X, Y, $Z` | Convert signed integer → short float (honors rA rounding; Y = mode override) |
| `SFLOTI` | `SFLOT $X, Z` / `SFLOT $X, Y, Z` | Convert signed integer → short float immediate (honors rA rounding; Y = mode override) |
| `SFLOTU` | `SFLOTU $X, $Z` / `SFLOTU $X, Y, $Z` | Convert unsigned integer → short float (honors rA rounding; Y = mode override) |
| `SFLOTUI` | `SFLOTU $X, Z` / `SFLOTU $X, Y, Z` | Convert unsigned integer → short float immediate (honors rA rounding; Y = mode override) |
| `CMP` | `CMP $X, $Y, $Z` | Compare signed: `$X` = −1/0/+1 |
| `CMPI` | `CMP $X, $Y, Z` | Compare signed immediate |
| `CMPU` | `CMPU $X, $Y, $Z` | Compare unsigned: `$X` = −1/0/+1 |
| `CMPUI` | `CMPU $X, $Y, Z` | Compare unsigned immediate |
| `AND` | `AND $X, $Y, $Z` | Bitwise AND |
| `ANDI` | `AND $X, $Y, Z` | Bitwise AND immediate |
| `OR` | `OR $X, $Y, $Z` | Bitwise OR |
| `ORI` | `OR $X, $Y, Z` | Bitwise OR immediate |
| `XOR` | `XOR $X, $Y, $Z` | Bitwise XOR |
| `XORI` | `XOR $X, $Y, Z` | Bitwise XOR immediate |
| `ANDN` | `ANDN $X, $Y, $Z` | Bitwise AND-NOT (`$Y & ~$Z`) |
| `ANDNI` | `ANDN $X, $Y, Z` | Bitwise AND-NOT immediate |
| `ORN` | `ORN $X, $Y, $Z` | Bitwise OR-NOT (`$Y | ~$Z`) |
| `ORNI` | `ORN $X, $Y, Z` | Bitwise OR-NOT immediate |
| `NAND` | `NAND $X, $Y, $Z` | Bitwise NAND |
| `NANDI` | `NAND $X, $Y, Z` | Bitwise NAND immediate |
| `NOR` | `NOR $X, $Y, $Z` | Bitwise NOR |
| `NORI` | `NOR $X, $Y, Z` | Bitwise NOR immediate |
| `NXOR` | `NXOR $X, $Y, $Z` | Bitwise XNOR |
| `NXORI` | `NXOR $X, $Y, Z` | Bitwise XNOR immediate |
| `MUX` | `MUX $X, $Y, $Z` | Bitwise multiplex using rM mask |
| `MUXI` | `MUX $X, $Y, Z` | Bitwise multiplex immediate |
| `BDIF` | `BDIF $X, $Y, $Z` | Byte difference (saturating, each byte) |
| `BDIFI` | `BDIF $X, $Y, Z` | Byte difference immediate; Z is the octabyte `#00…0Z`, so only the low byte subtracts |
| `WDIF` | `WDIF $X, $Y, $Z` | Wyde difference (saturating) |
| `WDIFI` | `WDIF $X, $Y, Z` | Wyde difference immediate; Z is the octabyte `#00…0Z`, so only the low wyde subtracts |
| `TDIF` | `TDIF $X, $Y, $Z` | Tetra difference (saturating) |
| `TDIFI` | `TDIF $X, $Y, Z` | Tetra difference immediate; Z is the octabyte `#00…0Z`, so only the low tetra subtracts |
| `ODIF` | `ODIF $X, $Y, $Z` | Octa difference (saturating) |
| `ODIFI` | `ODIF $X, $Y, Z` | Octa difference immediate |
| `SADD` | `SADD $X, $Y, $Z` | Sideways add (population count of `$Y & ~$Z`) |
| `SADDI` | `SADD $X, $Y, Z` | Sideways add immediate |
| `MOR` | `MOR $X, $Y, $Z` | Matrix OR (boolean 8×8 matrix multiply) |
| `MORI` | `MOR $X, $Y, Z` | Matrix OR immediate |
| `MXOR` | `MXOR $X, $Y, $Z` | Matrix XOR |
| `MXORI` | `MXOR $X, $Y, Z` | Matrix XOR immediate |
| `SL` | `SL $X, $Y, $Z` | Shift left (signed, sets overflow) |
| `SLI` | `SL $X, $Y, Z` | Shift left immediate |
| `SLU` | `SLU $X, $Y, $Z` | Shift left unsigned |
| `SLUI` | `SLU $X, $Y, Z` | Shift left unsigned immediate |
| `SR` | `SR $X, $Y, $Z` | Shift right signed (arithmetic) |
| `SRI` | `SR $X, $Y, Z` | Shift right signed immediate |
| `SRU` | `SRU $X, $Y, $Z` | Shift right unsigned (logical) |
| `SRUI` | `SRU $X, $Y, Z` | Shift right unsigned immediate |
| `JMP` | `JMP addr` | Unconditional jump (24-bit relative offset) |
| `JMPB` | `JMPB addr` | Unconditional jump, backward target required |
| `BN` | `BN $X, addr` | Branch if `$X < 0` |
| `BNB` | `BNB $X, addr` | Branch if `$X < 0` (backward hint) |
| `BZ` | `BZ $X, addr` | Branch if `$X == 0` |
| `BZB` | `BZB $X, addr` | Branch if `$X == 0` (backward hint) |
| `BP` | `BP $X, addr` | Branch if `$X > 0` |
| `BPB` | `BPB $X, addr` | Branch if `$X > 0` (backward hint) |
| `BOD` | `BOD $X, addr` | Branch if `$X` is odd |
| `BODB` | `BODB $X, addr` | Branch if `$X` is odd (backward hint) |
| `BNN` | `BNN $X, addr` | Branch if `$X >= 0` |
| `BNNB` | `BNNB $X, addr` | Branch if `$X >= 0` (backward hint) |
| `BNZ` | `BNZ $X, addr` | Branch if `$X != 0` |
| `BNZB` | `BNZB $X, addr` | Branch if `$X != 0` (backward hint) |
| `BNP` | `BNP $X, addr` | Branch if `$X <= 0` |
| `BNPB` | `BNPB $X, addr` | Branch if `$X <= 0` (backward hint) |
| `BEV` | `BEV $X, addr` | Branch if `$X` is even |
| `BEVB` | `BEVB $X, addr` | Branch if `$X` is even (backward hint) |
| `PBN` | `PBN $X, Y, Z` | Probable branch if negative |
| `PBNB` | `PBNB $X, Y, Z` | Probable branch if negative (backward) |
| `PBZ` | `PBZ $X, Y, Z` | Probable branch if zero |
| `PBZB` | `PBZB $X, Y, Z` | Probable branch if zero (backward) |
| `PBP` | `PBP $X, Y, Z` | Probable branch if positive |
| `PBPB` | `PBPB $X, Y, Z` | Probable branch if positive (backward) |
| `PBOD` | `PBOD $X, Y, Z` | Probable branch if odd |
| `PBODB` | `PBODB $X, Y, Z` | Probable branch if odd (backward) |
| `PBNN` | `PBNN $X, Y, Z` | Probable branch if non-negative |
| `PBNNB` | `PBNNB $X, Y, Z` | Probable branch if non-negative (backward) |
| `PBNZ` | `PBNZ $X, Y, Z` | Probable branch if non-zero |
| `PBNZB` | `PBNZB $X, Y, Z` | Probable branch if non-zero (backward) |
| `PBNP` | `PBNP $X, Y, Z` | Probable branch if non-positive |
| `PBNPB` | `PBNPB $X, Y, Z` | Probable branch if non-positive (backward) |
| `PBEV` | `PBEV $X, Y, Z` | Probable branch if even |
| `PBEVB` | `PBEVB $X, Y, Z` | Probable branch if even (backward) |
| `CSN` | `CSN $X, $Y, $Z` | Conditional set if `$Y < 0` |
| `CSNI` | `CSNI $X, $Y, Z` | Conditional set if `$Y < 0` (immediate) |
| `CSZ` | `CSZ $X, $Y, $Z` | Conditional set if `$Y == 0` |
| `CSZI` | `CSZI $X, $Y, Z` | Conditional set if `$Y == 0` (immediate) |
| `CSP` | `CSP $X, $Y, $Z` | Conditional set if `$Y > 0` |
| `CSPI` | `CSPI $X, $Y, Z` | Conditional set if `$Y > 0` (immediate) |
| `CSOD` | `CSOD $X, $Y, $Z` | Conditional set if `$Y` is odd |
| `CSODI` | `CSODI $X, $Y, Z` | Conditional set if `$Y` is odd (immediate) |
| `CSNN` | `CSNN $X, $Y, $Z` | Conditional set if `$Y >= 0` |
| `CSNNI` | `CSNNI $X, $Y, Z` | Conditional set if `$Y >= 0` (immediate) |
| `CSNZ` | `CSNZ $X, $Y, $Z` | Conditional set if `$Y != 0` |
| `CSNZI` | `CSNZI $X, $Y, Z` | Conditional set if `$Y != 0` (immediate) |
| `CSNP` | `CSNP $X, $Y, $Z` | Conditional set if `$Y <= 0` |
| `CSNPI` | `CSNPI $X, $Y, Z` | Conditional set if `$Y <= 0` (immediate) |
| `CSEV` | `CSEV $X, $Y, $Z` | Conditional set if `$Y` is even |
| `CSEVI` | `CSEVI $X, $Y, Z` | Conditional set if `$Y` is even (immediate) |
| `ZSN` | `ZSN $X, $Y, $Z` | Zero or set `$Z` into `$X` if `$Y < 0` |
| `ZSNI` | `ZSNI $X, $Y, Z` | Zero or set immediate if `$Y < 0` |
| `ZSZ` | `ZSZ $X, $Y, $Z` | Zero or set if `$Y == 0` |
| `ZSZI` | `ZSZI $X, $Y, Z` | Zero or set immediate if `$Y == 0` |
| `ZSP` | `ZSP $X, $Y, $Z` | Zero or set if `$Y > 0` |
| `ZSPI` | `ZSPI $X, $Y, Z` | Zero or set immediate if `$Y > 0` |
| `ZSOD` | `ZSOD $X, $Y, $Z` | Zero or set if `$Y` is odd |
| `ZSODI` | `ZSODI $X, $Y, Z` | Zero or set immediate if `$Y` is odd |
| `ZSNN` | `ZSNN $X, $Y, $Z` | Zero or set if `$Y >= 0` |
| `ZSNNI` | `ZSNNI $X, $Y, Z` | Zero or set immediate if `$Y >= 0` |
| `ZSNZ` | `ZSNZ $X, $Y, $Z` | Zero or set if `$Y != 0` |
| `ZSNZI` | `ZSNZI $X, $Y, Z` | Zero or set immediate if `$Y != 0` |
| `ZSNP` | `ZSNP $X, $Y, $Z` | Zero or set if `$Y <= 0` |
| `ZSNPI` | `ZSNPI $X, $Y, Z` | Zero or set immediate if `$Y <= 0` |
| `ZSEV` | `ZSEV $X, $Y, $Z` | Zero or set if `$Y` is even |
| `ZSEVI` | `ZSEVI $X, $Y, Z` | Zero or set immediate if `$Y` is even |
| `PUSHJ` | `PUSHJ X, addr` | Push registers and jump; return address in `rJ`; `X` is a byte or a register holding one |
| `PUSHJB` | `PUSHJB X, addr` | Push registers and jump (backward hint); `X` is a byte or a register holding one |
| `PUSHGO` | `PUSHGO X, $Y, $Z` / `PUSHGO X, $Y` | Push registers and jump to `$Y + $Z`, or to the base address `$Y` alone resolves to; `X` is a byte or a register holding one |
| `PUSHGOI` | `PUSHGO X, $Y, Z` | Push registers and jump to `$Y + Z`; `X` is a byte or a register holding one |
| `POP` | `POP X, YZ` / `POP xyz` / `POP` | Pop registers and return; the hole gets the last of the X returned values, the rest land above it in order; `XYZ=xyz` for the one-operand form; bare `POP` is `POP 0,0` |
| `GO` | `GO $X, $Y, $Z` / `GO $X, $Y` | Jump to `$Y + $Z`, or to the base address `$Y` alone resolves to; save next PC in `$X` |
| `GOI` | `GO $X, $Y, Z` | Jump to `$Y + Z`; save next PC in `$X` |
| `GETA` | `GETA $X, addr` | Get relative address into `$X` |
| `GETAB` | `GETAB $X, addr` | Get relative address (backward hint) |
| `GET` | `GET $X, Z` | Read special register Z into `$X`; `Z ≥ 32` or `Y != 0` halts |
| `PUT` | `PUT X, $Z` | Write `$Z` into special register X; `X ≥ 32` or `Y != 0` halts; `rC rN rO rS rI rT rTT rK rQ rU rV` (8–18) are read-only in user mode; `rG` must be 32–255 and at least `rL`; `rA` at most `#3FFFF` |
| `PUTI` | `PUT X, Z` | Write immediate Z into special register X; same rejections as `PUT` |
| `SAVE` | `SAVE $X, 0` | Push a context onto the register stack; `$X` (global) receives its address; a nonzero `Y` or `Z` halts; bare `SAVE` is an error |
| `UNSAVE` | `UNSAVE 0, $Z` / `UNSAVE $Z` | Restore the context `$Z` addresses from the register stack; a nonzero `X` or `Y` halts; bare `UNSAVE` is an error |
| `RESUME` | `RESUME XYZ` / `RESUME` | Resume after interrupt or trip; a nonzero `X` or `Y` halts; bare `RESUME` is `RESUME 0` |
| `TRAP` | `TRAP X, Y, Z` / `TRAP X, YZ` / `TRAP XYZ` / `TRAP` | System call (see TRAP interface above); `X`, `Y` and `Z` (or `X`) are each a pure byte or a register; bare `TRAP` is `TRAP 0,0,0` |
| `HALT` | `HALT` | checksmix extension — encodes as `TRAP 0,Halt,0` |
| `TRIP` | `TRIP X, Y, Z` / `TRIP X, YZ` / `TRIP XYZ` / `TRIP` | Forced trip (software interrupt); `X`, `Y` and `Z` (or `X`) are each a pure byte or a register; bare `TRIP` is `TRIP 0,0,0` |
| `SYNC` | `SYNC XYZ` / `SYNC` | Synchronize memory/pipeline; `XYZ` 0–3 is a no-op, 4–7 a privileged-operation interrupt, above 7 an illegal-instruction interrupt; bare `SYNC` is `SYNC 0` |
| `SWYM` | `SWYM` / `SWYM X` / `SWYM X, YZ` / `SWYM X, Y, Z` | Sympathize with your machinery (no-op); operands optional, default to zero; `X`, `Y` and `Z` are each a pure byte or a register |
| `PRELD` | `PRELD X, $Y, $Z` / `PRELD X, $Y` | Prefetch data into cache, or the range the base address `$Y` alone resolves to; `X` is a byte or a register holding one |
| `PRELDI` | `PRELD X, $Y, Z` | Prefetch data (immediate); `X` is a byte or a register holding one |
| `PREGO` | `PREGO X, $Y, $Z` / `PREGO X, $Y` | Prefetch for execution, or for the base address `$Y` alone resolves to; `X` is a byte or a register holding one |
| `PREGOI` | `PREGO X, $Y, Z` | Prefetch for execution (immediate); `X` is a byte or a register holding one |
| `PREST` | `PREST X, $Y, $Z` / `PREST X, $Y` | Prestore data, or the range the base address `$Y` alone resolves to; `X` is a byte or a register holding one |
| `PRESTI` | `PREST X, $Y, Z` | Prestore data (immediate); `X` is a byte or a register holding one |
| `SYNCD` | `SYNCD X, $Y, $Z` / `SYNCD X, $Y` | Synchronize data cache, or the range the base address `$Y` alone resolves to; `X` is a byte or a register holding one |
| `SYNCDI` | `SYNCD X, $Y, Z` | Synchronize data cache (immediate); `X` is a byte or a register holding one |
| `SYNCID` | `SYNCID X, $Y, $Z` / `SYNCID X, $Y` | Synchronize instruction and data cache, or the range the base address `$Y` alone resolves to; `X` is a byte or a register holding one |
| `SYNCIDI` | `SYNCID X, $Y, Z` | Synchronize instruction and data cache (immediate); `X` is a byte or a register holding one |

`X` in `PRELD`, `PREGO`, `PREST`, `SYNCD`, `SYNCID` and `STCO` takes either
spelling: a pure byte or a register holding one, both assembling the same
tetra. In MMIX, `X` is an immediate byte count: `PRELD X,$Y,$Z` covers the
`X+1` bytes `M[$Y+$Z]` through `M[$Y+$Z+X]`. `PRELD 7,$1,$2` and
`PRELD $7,$1,$2` both emit `#9A070102`.

`LDA`/`LDAI $X, addr` are the two-operand memory form: `addr` resolves
against a preceding `GREG` base exactly as `LDO $X,addr` and the other
memory operations do, one tetra always. No base within 255 bytes below
`addr` is the same error the memory forms give. `LDA $X,$Y` is `LDA
$X,$Y,0`, a register `$Y` read as an offset of zero. `SETI` loads any
address in four tetras when no base is in scope.

`GETA`'s `addr` must be 4-byte aligned relative to the current instruction. A
forward target reaches 0 to 262140 bytes ahead (an unsigned count of 0 to
65535 tetras); a backward target is accepted too and is encoded as `GETAB`
automatically, reaching up to 262144 bytes behind (an unsigned count of 1 to
65536 tetras). The two directions don't mirror: a forward delta of zero
tetras takes the field's first value, so `GETAB`'s all-backward field has one
more tetra of reach than `GETA`'s forward-only side. `GETAB` written
directly enforces the same backward-only range and rejects a forward target
outright. Targets that are out of range, misaligned, or (for `GETAB`) not
behind the current instruction are a hard assembly-time error naming the
byte figure; use `SETI`, or `LDA` against a `GREG` base, for addresses that
don't fit either field.