Uxn is an [[MIT License]] licensed virtual [[ISA|instruction set]] developed by [[Neauoire]]. - [Website](https://wiki.xxiivv.com/site/uxn.html) - [Specification](https://wiki.xxiivv.com/site/uxntal_reference.html) - [Documentation](https://100r.co/site/uxn.html) - [Source](https://sr.ht/~rabbits/uxn/) (reference implementation) > The Uxn/Varvara ecosystem is a personal computing stack based on a small virtual machine that lies at the heart of our software, and that allows us to run the same application on a variety of systems. \- Official Docs Variously spelled "uxn" and "Uxn" in the documentation. # Notability Uxn is an instruction set designed for personal use and is implemented in a software virtual machine. - [[Tal|Tal]] is its associated high-level assembly language - [[Varvara]] is an operating system built on top of it # Instruction Set Uxn is an 8-bit instruction set with 16-bit characteristics. Around half of the instructions are dedicated to 16-bit operations, but the opcodes themselves all fit into 8-bits and most of the system limitations are 8-bit. ## Complexity > uxn is a little forth machine with 32 instructions. \- Compudanzas[^5] Should be trivial to implement on almost anything - and has been. ## Registers Uxn is a pure stack-machine, so there's no traditional registers, but there are a few hidden ones that you would need to implement in an emulator or as hardware. This is the same thing that [[GreenArrays]] and other [[Forth]] processors do. | Register | | | -------- | -------------------- | | `pc` | Program Counter | | `sp` | Stack Pointer | | `rp` | Return Stack Pointer | Of these, only the program counter has any real ability to be directly altered. ## Opcodes >[!NOTE] > I suspect that the documentation does not always account for the natural increments of the program counter ($\mathrm{pc}$), so there may be off-by-one confusion in the addition, but I've tried to faithfully reproduce it until I can verify it myself. Overview of the 32 core opcodes in 8 groups of 4 instructions.[^4] | Op | Stack I | Op | Stack II | | ----- | ----------------------------- | ----- | --------------------------------- | | `BRK` | See [[#BRK]] | `SWP` | $\set{a, b} \mapsto \set{b, a}$ | | `INC` | $\set{a} \mapsto \set{a + 1}$ | `ROT` | $\set{a,b,c} \mapsto \set{b,c,a}$ | | `POP` | $\set{a} \mapsto \varnothing$ | `DUP` | $\set{a} \mapsto \set{a,a}$ | | `NIP` | $\set{a, b} \mapsto \set{b}$ | `OVR` | $\set{a,b} \mapsto \set{a,b,a}$ | | Op | Logic | Op | Arith | Op | Bit | | ----- | ---------------------------------- | ----- | ------------------------------------ | ----- | --------------------------------------------------------------------------------------- | | `EQU` | $\set{a,b} \mapsto \set{a=b}$ | `ADD` | $\set{a,b} \mapsto \set{a+b}$ | `AND` | $\set{a,b} \mapsto \set{a\ \&\ b}$ | | `NEQ` | $\set{a,b} \mapsto \set{a \neq b}$ | `SUB` | $\set{a,b} \mapsto \set{a-b}$ | `ORA` | $\set{a,b} \mapsto \set{a \parallel b}$ | | `GTH` | $\set{a,b} \mapsto \set{a > b}$ | `MUL` | $\set{a,b} \mapsto \set{a \times b}$ | `EOR` | $\set{a,b} \mapsto \set{a \oplus b}$ | | `LTH` | $\set{a,b} \mapsto \set{a < b}$ | `DIV` | $\set{a,b} \mapsto \set{a \div b}$ | `SFT` | $\set{a,b} \mapsto \set{c}lt;br>$x = a \gg b_{(1\dots 4)}lt;br>$c = a \ll b_{(5\dots 8)}$ | | Op | Stash | Op | Mem I | Op | Mem II | | ----- | ----------------------------------------------------------------------------------------------------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------ | | `JMP` | $\set{a} \mapsto \varnothinglt;br>$\mathrm{pc} = \mathrm{pc}+a$ | `LDZ` | $\begin{bmatrix}b\end{bmatrix}_alt;br>$\set{a} \mapsto \set{b}$ | `LDA` | $\begin{bmatrix}c\end{bmatrix}_{ab}lt;br>$\set{a,b} \mapsto \set{c}$ | | `JCN` | $\set{a, b} \mapsto \varnothinglt;br>$\mathsf{if}\ b \neq 0\ \mathsf{then}\ \mathrm{pc}=\mathrm{pc}+a$ | `STZ` | $\set{a,b} \mapsto \varnothinglt;br>$\begin{bmatrix}\varnothing\end{bmatrix}_b \mapsto \begin{bmatrix}a\end{bmatrix}_b$ | `STA` | $\set{a,b,c} \mapsto \varnothinglt;br>$\begin{bmatrix}\varnothing\end{bmatrix}_{bc} \mapsto \begin{bmatrix}a\end{bmatrix}_{bc}$ | | `JSR` | $\set{a} \mapsto \varnothinglt;br>$\mathsf{ret}=(\mathrm{pc})lt;br>$\mathrm{pc} = \mathrm{pc}+a$ | `LDR` | $\begin{bmatrix}b\end{bmatrix}_{\mathrm{pc}+a}lt;br>$\set{a} \mapsto \set{b}$ | `DEI` | $\mathsf{dev}_a = blt;br>$\set{a} \mapsto \set{b}$ | | `STH` | $\set{a} \mapsto \varnothinglt;br>$\mathsf{ret}=(a)$ | `STR` | $\set{a,b} \mapsto \varnothinglt;br>$\begin{bmatrix}\varnothing\end{bmatrix}_{\mathrm{pc}+b} \mapsto \begin{bmatrix}a\end{bmatrix}_{\mathrm{pc}+b}$ | `DEO` | $\set{a, b} \mapsto \varnothinglt;br>$\mathsf{dev}_b = a$ | ### Immediate Opcodes They look more complex, but they are really just variants of the core operators using `pc` as the starting number. | Op | | Info | | ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | | `LIT` | $\begin{bmatrix}a\end{bmatrix}_{\mathrm{pc}+1}lt;br>$\varnothing \mapsto \set{a}lt;br>$\mathrm{pc} = \mathrm{pc}+1$ | $\mathrm{pc}$ equivalent of `LDA` | | `JCI` | $\begin{bmatrix}b,c\end{bmatrix}_{\mathrm{pc}+1\dots2}lt;br>$\set{a} \mapsto \varnothinglt;br>$\mathrm{pc} = \mathsf{if}\ a \neq 0\ \mathsf{then}\ bc\ \mathsf{else}\ \mathrm{pc}+2$ | $\mathrm{pc}$ equivalent of `JCN` with 2 bytes | | `JMI` | $\begin{bmatrix}b,c\end{bmatrix}_{\mathrm{pc}+1\dots2}lt;br>$\varnothing \mapsto \varnothinglt;br>$\mathrm{pc} = \mathrm{pc}+bc$ | $\mathrm{pc}$ equivalent of `JMP` with 2 bytes | | `JSI` | $\begin{bmatrix}b,c\end{bmatrix}_{\mathrm{pc}+1\dots2}lt;br>$\varnothing \mapsto \varnothinglt;br>$\mathsf{ret}=(\mathrm{pc}+2)lt;br>$\mathrm{pc} = \mathrm{pc}+bc$ | $\mathrm{pc}$ equivalent of `STH` with 2 bytes | ### Addressing Modes | Mode | Suffix | Effect | | | ------- | ------ | ---------------------- | --------------------------------------------------------------------------- | | Default | *none* | Bytewise operation | Operands are single bytes (8 bits) | | Short | `2` | Short int operation | Operands are two bytes (16 bits) | | Keep | `k` | Keep Mode | Operands are not removed from the stack<br>Result pushed on top of operands | | Return | `r` | Return stack operation | Operands are taken from the Return stack<br>Result put on the Return stack | ### All Opcodes [[Uxn]] only has 32 "real" opcodes, the 244 other instructions are [[#Immediate Opcodes|variations]] and combinations of the above [[Addressing Mode]]s.[^4] | | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0a | 0b | 0c | 0d | 0e | 0f | | --- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | | 00 | [BRK](#brk) | [INC](#inc) | [POP](#pop) | [NIP](#nip) | [SWP](#swp) | [ROT](#rot) | [DUP](#dup) | [OVR](#ovr) | [EQU](#equ) | [NEQ](#neq) | [GTH](#gth) | [LTH](#lth) | [JMP](#jmp) | [JCN](#jcn) | [JSR](#jsr) | [STH](#sth) | | 10 | [LDZ](#ldz) | [STZ](#stz) | [LDR](#ldr) | [STR](#str) | [LDA](#lda) | [STA](#sta) | [DEI](#dei) | [DEO](#deo) | [ADD](#add) | [SUB](#sub) | [MUL](#mul) | [DIV](#div) | [AND](#and) | [ORA](#ora) | [EOR](#eor) | [SFT](#sft) | | 20 | [JCI](#jci) | INC2 | POP2 | NIP2 | SWP2 | ROT2 | DUP2 | OVR2 | EQU2 | NEQ2 | GTH2 | LTH2 | JMP2 | JCN2 | JSR2 | STH2 | | 30 | LDZ2 | STZ2 | LDR2 | STR2 | LDA2 | STA2 | DEI2 | DEO2 | ADD2 | SUB2 | MUL2 | DIV2 | AND2 | ORA2 | EOR2 | SFT2 | | 40 | [JMI](#jmi) | INCr | POPr | NIPr | SWPr | ROTr | DUPr | OVRr | EQUr | NEQr | GTHr | LTHr | JMPr | JCNr | JSRr | STHr | | 50 | LDZr | STZr | LDRr | STRr | LDAr | STAr | DEIr | DEOr | ADDr | SUBr | MULr | DIVr | ANDr | ORAr | EORr | SFTr | | 60 | [JSI](#jsi) | INC2r | POP2r | NIP2r | SWP2r | ROT2r | DUP2r | OVR2r | EQU2r | NEQ2r | GTH2r | LTH2r | JMP2r | JCN2r | JSR2r | STH2r | | 70 | LDZ2r | STZ2r | LDR2r | STR2r | LDA2r | STA2r | DEI2r | DEO2r | ADD2r | SUB2r | MUL2r | DIV2r | AND2r | ORA2r | EOR2r | SFT2r | | 80 | [LIT](#lit) | INCk | POPk | NIPk | SWPk | ROTk | DUPk | OVRk | EQUk | NEQk | GTHk | LTHk | JMPk | JCNk | JSRk | STHk | | 90 | LDZk | STZk | LDRk | STRk | LDAk | STAk | DEIk | DEOk | ADDk | SUBk | MULk | DIVk | ANDk | ORAk | EORk | SFTk | | a0 | LIT2 | INC2k | POP2k | NIP2k | SWP2k | ROT2k | DUP2k | OVR2k | EQU2k | NEQ2k | GTH2k | LTH2k | JMP2k | JCN2k | JSR2k | STH2k | | b0 | LDZ2k | STZ2k | LDR2k | STR2k | LDA2k | STA2k | DEI2k | DEO2k | ADD2k | SUB2k | MUL2k | DIV2k | AND2k | ORA2k | EOR2k | SFT2k | | c0 | LITr | INCkr | POPkr | NIPkr | SWPkr | ROTkr | DUPkr | OVRkr | EQUkr | NEQkr | GTHkr | LTHkr | JMPkr | JCNkr | JSRkr | STHkr | | d0 | LDZkr | STZkr | LDRkr | STRkr | LDAkr | STAkr | DEIkr | DEOkr | ADDkr | SUBkr | MULkr | DIVkr | ANDkr | ORAkr | EORkr | SFTkr | | e0 | LIT2r | INC2kr | POP2kr | NIP2kr | SWP2kr | ROT2kr | DUP2kr | OVR2kr | EQU2kr | NEQ2kr | GTH2kr | LTH2kr | JMP2kr | JCN2kr | JSR2kr | STH2kr | | f0 | LDZ2kr | STZ2kr | LDR2kr | STR2kr | LDA2kr | STA2kr | DEI2kr | DEO2kr | ADD2kr | SUB2kr | MUL2kr | DIV2kr | AND2kr | ORA2kr | EOR2kr | SFT2kr | ## Instruction Listing ### BRK Uxn uses a [[Multitasking|Cooperative Multitasking]] strategy, so in order to turn over control to another task it needs to do so explicitly. `BRK` is that operation. It ends processing of the current instruction list "vector". The documentation isn't very clear where control is handed off too, but presumably it jumps back to `0x100`. It is the equivalent of `yield` in some higher-level languages. ### SFT Overall the design of the instruction is extremely elegant and consistent. The shift instruction is perhaps the only real oddball. It combines the shift-left and shift-right operations into a single instruction. That in itself isn't too strange, but it uses nibbles (4-bit) to do *both* operations *in series* rather than a sign bit or twos complement encoding to do just one or the other. I get very [[RISC]]-y vibes from this, like it has some clever uses that aren't immediately apparent. The documentation doesn't describe the behavior in detail, so it's unclear if there is an advantage to this approach or just a fun quirk. In some architectures a shift copies the last bit, in others its a rotate rather than a simple shift, in some it is 1 in some cases and 0 in others. Presumably here the new bit is always zero. Since there doesn't seem to be a carry or overflow register, the bit that falls off is likely not retained anywhere either. # Implementations Can be hosted on pretty much anything that [[ANSI]] [[C|C]] can be built on or that [[Javascript]] can run in. - [[Linux]] - [[MacOS]] - [[Windows]] - [[Plan 9]] - [[Haiku]][^1] - [[Web]][^3] - [[Essence]] (third party) There are *dozens* of complete and partial emulators for both modern and classic computers and game consoles.[^2] # Resources - [[Tal]], an assembly language for [[Uxn]] - https://github.com/hundredrabbits/awesome-uxn - https://compudanzas.net/uxn_tutorial.html - https://permacomputing.net/Uxn/ # References [^1]: https://discuss.haiku-os.org/t/uxn-ecosystem-on-haiku/12209 [^2]: https://github.com/hundredrabbits/awesome-uxn#emulators [^3]: https://git.sr.ht/~rabbits/uxn5 [^4]: https://wiki.xxiivv.com/site/uxntal_opcodes.html [^5]: https://compudanzas.net/uxn_tutorial.html (the source's contents have changed since I first referenced it)