Internals: Nga Virtual Machine

Overview

At the heart of Retro is a simple MISC (minimal instruction set computer) processor for a dual stack architecture.

This is a very simple and straightforward system. There are 30 instructions. The memory is a linear array of signed 32 bit values. And there are two stacks: one for data and one for return addresses.

Instruction Table

                                     |     Stacks      | | Opcode | Muri | Full Name          | Data  | Address | | ------ | ---- | ------------------ | ----- | ------- | |  0     | ..   | nop                |   -   |   -     | |  1     | li   | lit                |   -n  |   -     | |  2     | du   | dup                |  n-nn |   -     | |  3     | dr   | drop               |  n-   |   -     | |  4     | sw   | swap               | xy-yx |   -     | |  5     | pu   | push               |  n-   |   -n    | |  6     | po   | pop                |   -n  |  n-     | |  7     | ju   | jump               |  a-   |   -     | |  8     | ca   | call               |  a-   |   -A    | |  9     | cc   | conditional call   | af-   |   -A    | | 10     | re   | return             |   -   |  A-     | | 11     | eq   | equality           | xy-f  |   -     | | 12     | ne   | inequality         | xy-f  |   -     | | 13     | lt   | less than          | xy-f  |   -     | | 14     | gt   | greater than       | xy-f  |   -     | | 15     | fe   | fetch              |  a-n  |   -     | | 16     | st   | store              | na-   |   -     | | 17     | ad   | addition           | xy-n  |   -     | | 18     | su   | subtraction        | xy-n  |   -     | | 19     | mu   | multiplication     | xy-n  |   -     | | 20     | di   | divide & remainder | xy-rq |   -     | | 21     | an   | bitwise and        | xy-n  |   -     | | 22     | or   | bitwise or         | xy-n  |   -     | | 23     | xo   | bitwise xor        | xy-n  |   -     | | 24     | sh   | shift              | xy-n  |   -     | | 25     | zr   | zero return        |  n-?  |   -     | | 26     | ha   | halt               |   -   |   -     | | 27     | ie   | i/o enumerate      |   -n  |   -     | | 28     | iq   | i/o query          |  n-xy |   -     | | 29     | ii   | i/o invoke         | ...n- |   -     |

Encoding

Up to four instructions can be packed into each memory cell.

As an example,

Opcode 4 Opcode 3 Opcode 2 Opcode 1 00000000:00000000:00000000:00000000

If we have a bundle of duliswst, it would look like:

st       sw       li       du 00010000:00000100:00000001:00000010

Each li should have a value in the following cell(s). These values will be pushed to the stack. E.g., lili.... and 1, 2:

00000000:00000000:00000001:00000001 00000000 00000000 00000000 00000001 (1) 00000000 00000000 00000000 00000010 (2)

Shifts

sh performs a bitwise arithmetic shift operation.

This takes two values:

xy

And returns a single one:

z

If y is positive, this shifts x right by y bits. If negative, it shifts left.

Queries: Memory, Stacks

The fe instruction allows queries of some data related to the Nga VM state. These are returned by reading from negative addresses:

| Address | Returns                | | ------- | ---------------------- | | -1      | Data stack depth       | | -2      | Address stack depth    | | -3      | Maximum Image Size     | | -4      | Minimum Integer Value  | | -5      | Maximum Integer Value  |

I/O Devices

Nga provides three instructions for interacting with I/O devices. These are:

ie   i/o enumerate    returns the number of attached devices iq   i/o query        returns information about a device ii   i/o interact     invokes an interaction with a device

As an example, with an implementation providing an output source, a block storage system, and keyboard:

ie    will return `3` since there are three i/o devices 0 iq  will return 0 0, since the first device is a screen       (device class 0, version of 0) 1 iq  will return 1 3, since the second device is a block       storage (class 3), with a version of 1 2 iq  will return 0 1, since the third device is a keyboard       (class 1), with a version of 0

In this case, some interactions can be defined:

: c:put i liiire.. d 0

: c:get i liiire.. d 2

Setup the stack, push the device handle to the stack, and then use ii to invoke the interaction.

A Retro system requires one I/O device (a generic output for a single character). This must be the first device, and must have a device class of 0.

All other devices are optional and can be specified in any order.

The currently supported and reserved device identifiers are:

| ID   | Device Type      | Notes                      | | ---- | ---------------- | -------------------------- | | 0000 | Generic Output   | Always present as device 0 | | 0001 | Keyboard         |                            | | 0002 | Floating Point   |                            | | 0003 | Block Storage    | Raw, 1024 cell blocks      | | 0004 | Filesystem       | Unix-style Files           | | 0005 | Network: Gopher  | Make gopher requests       | | 0006 | Network: HTTP    | Make HTTP requests         | | 0007 | Network: Sockets |                            | | 0008 | Syscalls: Unix   |                            | | 0009 | Scripting Hooks  |                            | | 0010 | Random Number    |                            |

This list may be revised in the future. The only guaranteed stable indentifier is 0000 for generic output.