- Introduction
- Registers
- Programs
- Directives
- Rings
- Interrupts
- Paging
- Instruction Format
- Operations
- Math (R-Types)
- Add (
add
)- Subtract (
sub
)- Multiply (
mult
)- Multiply Unsigned (
multu
)- Shift Left Logical (
sll
)- Shift Right Logical (
srl
)- Shift Right Arithmetic (
sra
)- Modulo (
mod
)- Divide (
div
)- Divide Unsigned (
divu
)- Modulo Unsigned (
modu
)- Sign Extend 32 to 64 (
sext32
)- Sign Extend 16 to 64 (
sext16
)- Sign Extend 8 to 64 (
sext8
)- Logic (R-Types)
- Bitwise AND (
and
)- Bitwise NAND (
nand
)- Bitwise NOR (
nor
)- Bitwise NOT (
not
)- Bitwise OR (
or
)- Bitwise XNOR (
xnor
)- Bitwise XOR (
xor
)- Logical AND (
land
)- Logical NAND (
lnand
)- Logical NOR (
lnor
)- Logical NOT (
lnot
)- Logical OR (
lor
)- Logical XNOR (
lxnor
)- Logical XOR (
lxor
)- Math (I-Types)
- Add Immediate (
addi
)- Subtract Immediate (
subi
)- Multiply Immediate (
multi
)- Multiply Unsigned Immediate (
multui
)- Shift Left Logical Immediate (
slli
)- Shift Right Logical Immediate (
srli
)- Shift Right Arithmetic Immediate (
srai
)- Shift Left Logical Inverse Immediate (
sllii
)- Shift Right Logical Inverse Immediate (
srlii
)- Shift Right Arithmetic Inverse Immediate (
sraii
)- Modulo Immediate (
modi
)- Divide Immediate (
divi
)- Divide Unsigned Immediate (
divui
)- Divide Inverse Immediate (
divii
)- Divide Unsigned Inverse Immediate (
divuii
)- Modulo Unsigned Immediate (
modui
)- Logic (I-Types)
- Bitwise AND Immediate (
andi
)- Bitwise NAND Immediate (
nandi
)- Bitwise NOR Immediate (
nori
)- Bitwise OR Immediate (
ori
)- Bitwise XNOR Immediate (
xnori
)- Bitwise XOR Immediate (
xori
)- Comparisons (R-Types)
- Compare (
cmp
)- Set on Less Than (
sl
)- Set on Less Than or Equal (
sle
)- Set on Equal (
seq
)- Set on Greater Than or Equal (
sge
)- Set on Greater Than (
sg
)- Set on Less Than Unsigned (
slu
)- Set on Less Than or Equal Unsigned (
sleu
)- Set on Greater Than or Equal Unsigned (
sgeu
)- Set on Greater Than Unsigned (
sgu
)- Comparisons (I-Types)
- Compare Immediate (
cmpi
)- Set on Less Than Immediate (
sli
)- Set on Less Than or Equal Immediate (
slei
)- Set on Equal Immediate (
seqi
)- Set on Greater Than or Equal Immediate (
sgei
)- Set on Greater Than Immediate (
sgi
)- Set on Less Than Unsigned Immediate (
slui
)- Set on Less Than or Equal Unsigned Immediate (
sleui
)- Set on Greater Than or Equal Unsigned Immediate (
sgeui
)- Set on Greater Than Unsigned Immediate (
sgui
)- Jumps (J-Types)
- Jump (
j
)- Jump Conditional (
jc
)- Jumps (R-Types)
- Jump to Register (
jr
)- Jump to Register Conditional (
jrc
)- Jump to Register and Link (
jrl
)- Jump to Register and Link Conditional (
jrlc
)- Memory (R-Types)
- Copy (
c
)- Load (
l
)- Store (
s
)- Copy Byte (
cb
)- Load Byte (
lb
)- Store Byte (
sb
)- Copy Halfword (
ch
)- Load Halfword (
lh
)- Store Halfword (
sh
)- Copy Short (
cs
)- Load Short (
ls
)- Store Short (
ss
)- Stack Push (
spush
)- Stack Pop (
spop
)- Memset (
ms
)- Translate Address (
trans
)- Memory (I-Types)
- Load Immediate (
li
)- Store Immediate (
si
)- Load Indirect Immediate (
lni
)- Load Byte Immediate (
lbi
)- Store Byte Immediate (
sbi
)- Load Byte Indirect Immediate (
lbni
)- Set (
set
)- Load Upper Immediate (
lui
)- Sized Stack Push (
sspush
)- Sized Stack Pop (
sspop
)- Spill Store (
sps
)- Spill Load (
spl
)- Special Instructions
- External (
ext
)- Select (
sel
)- Interrupt (
int
)- Register Interrupt Table (
rit
)- Set Timer (
time
)- Set Timer Immediate (
timei
)- Save Timer (
svtime
)- Change Ring (
ring
)- Save Ring (
svring
)- Change Ring Immediate (
ringi
)- Disable Paging (
pgoff
)- Enable Paging (
pgon
)- Set Page Table (
setpt
)- Save Paging (
svpg
)- Query Memory (
qm
)- Disable Interrupts (
di
)- Enable Interrupts (
ei
)- Push Paging (
ppush
)- Pop Paging (
ppop
)- Pseudoinstructions
- Move (
mv
)- Return (
ret
)- Push (
push
)- Pop (
pop
)- Jump if Equal (
jeq
)- Externals
- Print Register (
printr
)- Halt (
halt
)- Evaluate String (
eval
)- Print Character (
prc
)- Print Decimal (
prd
)- Print Hexadecimal (
prx
)- Sleep (
sleep
)- Print Binary (
prb
)- Rest (
rest
)- I/O (
io
)
The VM emulates Why, a custom RISC instruction set that may or may not actually be theoretically implementable as real hardware. This instruction set has 64-bit word length, and memory addressability is also 64 bits.
There are 128 registers. Their purposes are pretty much stolen from MIPS:
Number | Name | Purpose |
---|---|---|
0 | $0 |
Always contains zero. |
1 | $g |
Global area pointer (right after end of program). |
2 | $sp |
Stack pointer. |
3 | $fp |
Frame pointer. |
4 | $rt |
Return address. |
5 | $lo |
Stores the lower half of a mult result. |
6 | $hi |
Stores the upper half of a mult result. |
7–22 | $r0 –$rf |
Contains return values. |
23–38 | $a0 –$af |
Contains arguments for subroutines. |
39–61 | $t0 –$t16 |
Temporary registers. |
62–84 | $s0 –$s16 |
Saved registers. |
85–100 | $k0 –$kf |
Kernel registers. |
101 | $st |
Status register. |
102–117 | $m0 –$mf |
Reserved for use by the assembler. |
118–121 | $f0 –$f3 |
Floating point return values. |
122–127 | $e0 –$e5 |
Contains data about exceptions. |
The $st
register stores flag bits. Currently, this includes the results of arithmetic instructions. In ascending order of significance, these are:
Z
(zero): Whether the last arithmetic result was zero (forcmp
/cmpi
, whether the compared values are equal)N
(negative): Whether the result of the last arithmetic result was negative (forcmp
/cmpi
, whether the left value was less than the right value)C
(carry): Whether the result of an addition was truncated. (Currently not implemented for subtraction.)O
(overflow): Whether the addition of two positive signed numbers had a negative result due to overflow.
These status numbers are used in conditional branches, but they can also be accessed by programs. Writing to the $st
register is possible, but strange behavior may occur as a result.
Programs are divided into five sections: metadata, code, data, symbol table and debug data. The metadata section contains information about the program. The code section consists of executable code. The data section consists of read/write data. The symbol table contains the names, locations and types of all visible symbols. The debug data section contains data that correlates assembly instructions to locations in source files for higher-level languages like C.
Note that the code section and data section are combined into the #text
section in assembly. Code and data sections can be switched between using the %code
and %data
directives.
The metadata section is a block of data at the beginning of the program that contains the beginning addresses of the other sections. The first value in this section represents the beginning address of the code section and is therefore equivalent to the size of the metadata section.
0x00
: Address of the beginning of the code section.0x08
: Address of the beginning of the data section.0x10
: Address of the beginning of the symbol table.0x18
: Address of the beginning of the debug data section.0x20
: Address of the beginning of the relocation data section.0x28
: Total size of the program.0x30
–0x38
: ORCID of the author (represented in ASCII without hyphens).0x40
–...
: Program name, version string and author name of the program (represented with null-terminated ASCII).- Example: given a program name
"Example"
, version string"4"
and author name"Kai Tamkun"
, this will be0x4578616d706c6500
0x34004b6169205461
0x6d6b756e00000000
.
- Example: given a program name
#meta author: "Kai Tamkun" orcid: "0000-0001-7405-6654" name: "Example" version: "4"
The code section consists of executable code. This is the only section of the code that the program counter is expected to point to. Code is represented using the syntax described by entries in the Operations section.
The data section contains read/write data. Data is added using directives.
The symbol table contains a list of debug symbols. Each debug symbol is assigned a numeric ID equal to part of the SHA256 hash of its name. (See /wasmc/src/wasm/Assembler.cpp for the precise transformation.) Each symbol is encoded in the table as a variable number of words. The upper half of the first is the numeric ID. The next 16 bits comprise the symbol type, while the lowest 16 bits comprise the length (in words) of the symbol's name. The second word represents the symbol's offset (its position relative to the start of the section containing it). The third word is the length of what the symbol points to (for functions, this will generally be eight times the number of instructions in the function). The remaining words encode the symbol's name. The length of the name in words is equal to the ceiling of 1/8 of the symbol name's length in characters. Any extra bytes in the last word are null.
The debug data section contains data mapping instructions to their positions in source files. It's stored as a list of entries whose order is important and must be maintained. The first byte of each entry determines the entry's type. All multibyte items in an entry are encoded as little-endian.
An entry with type 0
is invalid.
An entry with type 1
declares the filename of a source file and can be referenced by other entries. After the first byte in a type 1
entry, the next three bytes indicate the length of the filename. The filename then follows, plus padding until the total length of the entry is divisible by eight bytes.
An entry with type 2
declares a function name. It uses the same format as a type 1
entry, but instead of a filename, a function name is stored instead.
An entry with type 3
references a line on a source file defined by a type 1
entry. After the first byte in a type 3
entry, the next three bytes indicate the index of the referenced type 1
entry. The four bytes after that indicate the line number in the referenced file and the next three bytes indicate the column number. The next byte indicates how many contiguous instructions the entry applies to. The next four bytes indicate the index of the referenced type 2
entry. The final eight bytes indicate the address of an instruction.
The assembly syntax for type 3
entries defines a template. Multiple type 3
entries will be generated per template depending on how many instructions reference the type 3
entry. In the example below, only one entry is generated per template because each template occurs in a single continuous span. If a !2
instruction were added after the last !3
instruction, two type 3
entries would be generated for the template with index 2.
#debug 1 "src/printf.cpp" // 0 2 "_vsnprintf" // 1 3 0 541 10 1 // 2 (File 0, line 541, column 10, function 1) 3 0 551 12 1 // 3 (File 0, line 551, column 12, function 1) #code // ... $t6 -> [$ta] !2 [$t3] -> $t4 /b !3 1 -> $m0 !3 $m0 << 7 -> $m0 !3 $t4 x $m0 -> $t5 !3 $t5 - $m0 -> $t5 !3 $t5 & -1 -> $t5 !3 // ...
Relocation data allows the linker to combine multiple binaries. Some instructions have immediate values that contain not an absolute value but instead an address or an address plus a constant offset. Jumps to labels are the most common example.
The upper 61 bits of the first word of a relocation data entry represent the index of the symbol in the symbol table, while the next two bits are 0
if the value to relocate is 8 bytes wide, 2
if it's the lower 4 bytes of the symbol's address or 3
if it's the upper 4 bytes of the symbol's address. A value of 1
is invalid. The lowest bit of the first word is 1
if the value to be relocated is in the data section or 0
if it's in the code section. The second word is the signed offset to be applied to the symbol's location. The third and final word is the address of the value relative to the start of the code section.
Directives are instructions to the assembler. They control the assembly process.
Switches the current section to the code section. After using this, anything emitted using %8b
, %string
and other such directives will be placed in the code section. (Note that inserting 8-byte values directly in the code section is discouraged and that inserting strings is absolutely pointless.)
Switches the current section to the data section. After using this, anything emitted using %8b
, %string
and other such directives will be placed in the data section. (Note that putting instructions in the data section causes unspecified behavior.)
Tells the assembler the type of a symbol. The type can be either object
(for data) or function
(for code).
%type data_value object %type strprint function @data_value %8b 42 @strprint // ... : $rt
Tells the assembler the size (in bytes) of a symbol. The value supports expressions. Expressions are mathematical expressions whose operands are numeric constants, symbol names or .
, which represents the value of the location counter. An expression is valid if any of the following is true:
- None of its children contain any label references (that is, all leaves are numeric or
.
) - It's the difference of two labels
- It's the difference of
.
and a label - It's the difference of a label and a number
- It's the sum of a label and a number
These restrictions are in place because relocation data supports constant offsets only. All other information has to be known at compile time.
@some_8b %8b 42 %size some_8b 8 @function_one // ... : $rt @function_two // ... : $ rt %size function_one function_two-function_one %size function_two .-function_two
Emits a string (not terminated with a null byte).
@some_string %string "Hello, world!\n"
Emits a string (terminated with a null byte).
@some_string %stringz "Hello, world!\n"
Emits a value of the specified width (1 byte for %1b
, 2 bytes for %2b
and so on). The values can be expressions (see %size
).
%8b some_function+32 // 4 instructions past the start of some_function %8b some_label-10 %8b . %4b data_value // Valid if data_value is within the first 4 GiB %2b 65535 %1b 255
Inserts zero until the location counter reaches a given alignment (in bytes).
%align 65536 @p0 // ...
Adds a given number of bytes with a given value. The number of bytes is the first operand and the value is the second operand.
@ten_ones %fill 10 0x01
Why has support for four protection rings, just like x86. Ring 0 is called kernel mode and ring 3 is called user mode; rings 1 and 2 are currently unused.
Interrupts can be triggered by software or by the VM itself. Whenever an interrupt is triggered, $e0
is set to the program counter right before the interrupt was raised and $e1
is set to the current ring at the time the interrupt occurred. Interrupt handlers are expected to deal with properly resuming normal program execution after finishing up. The VM stores an address to a table containing pointers to interrupt handlers. The table is 32 words long.
The SYSTEM
interrupt is a software-triggered interrupt handled by the operating system. It can be called from any ring and causes a switch to kernel mode.
The TIMER
interrupt is a hardware-triggered interrupt caused when the hardware timer expires. Usable by ring zero only. This is to prevent unprivileged code from interfering with schedulers; operating systems can implement their own mechanisms to expose timer functionality to lower-privileged code. This interrupt causes a switch to kernel mode.
The PROTEC
interrupt is a hardware-triggered interrupt caused when a called instruction attempts to do something not possible within the current ring. This causes a switch to kernel mode.
The PFAULT
interrupt is raised if paging is enabled and an access to a non-present page is attempted. This causes a switch to kernel mode.
The INEXEC
interrupt is raised if program control flows to an address whose page is not marked as executable. This causes a switch to kernel mode.
The BWRITE
(bad write) interrupt is raised if paging is enabled and an instruction attempts to write to a nonwritable page.
The KEYBRD
interrupt is raised when a key is pressed. The value of the key will be stored in $e2
according to the format below. If this interrupt isn't set in the interrupt table, key presses will be ignored. This interrupt causes a switch to kernel mode.
Range | 63–35 (29) | 34 | 33 | 32 | 31–0 (32) |
---|---|---|---|---|---|
Purpose | Unused | Ctrl | Alt | Shift | Key value (UTF-8) |
Like much of this instruction set, the formatting for instructions is copied from MIPS with a few modifications (for example, instructions are 64 bits long in this instruction set, as opposed to 32 for MIPS64).
R-type instructions perform computations with multiple registers.
Range | 63–52 (12) | 51–45 (7) | 44–38 (7) | 37–31 (7) | 30–18 (13) | 17–14 (4) | 13-12 (2) | 11–0 (12) |
---|---|---|---|---|---|---|---|---|
Purpose | Opcode | rt | rs | rd | Unused | Conditions | Linker flags | Function |
I-type instructions perform computations with registers and an immediate value.
Range | 63–52 (12) | 51–48 (4) | 47–46 (2) | 45–39 (7) | 38–32 (7) | 31–0 (32) |
---|---|---|---|---|---|---|
Purpose | Opcode | Conditions | Linker flags | rs | rd | Immediate Value |
J-type instructions point the program counter to a given address under certain circumstances.
Range | 63–52 (12) | 51–45 (7) | 44 | 43–38 (6) | 37–34 (4) | 33–32 (2) | 31–0 (32) |
---|---|---|---|---|---|---|---|
Purpose | Opcode | rs | Link | Unused | Conditions | Linker flags | Address |
If the link bit is set, the current value of the program counter will be stored in $rt
, the return address register.
Before the relocation overhaul, linker flags were used to give the linker clues about relocation. There were four possible values. Back then, as now, linker flag values other than 0
are valid for I-type and J-type instructions only.
Now, the linker flags have three possible values. 0
indicates that the immediate value of the instruction isn't affected by relocation, whereas 1
indicates that the symbol needs to be relocated at link time. 2
is the same as 1
, but indicates that the current immediate value is invalid because the symbol is currently unknown. Executing an instruction whose linker flag is 2
will cause an error. Other values are invalid.
For operations that support conditions, the condition bits indicate what combination of ALU flags are required for the operation to occur.
0xxx
: Conditions disabled1000
: Positive (!N & !Z
)1001
: Negative1010
: Zero1011
: Nonzero
Why.js supports paging. It's disabled by default and must be enabled with the pgon
instruction. It uses a six-level system with a similar philosophy to x86_64's four-level system, but page sizes are 65,536 bytes large. The tables are named P0
through and P5
and each table contains 256 entries. To translate a virtual address to a physical one, P0
is inspected at the address set with setpt
. The most significant eight bits in the virtual address are used as an offset into P0
to find the address of P1
. If the P0
entry isn't present, a PFAULT
interrupt is raised. Otherwise, the P1
entry is inspected to find the P2
address, and so on, until the address for the P5
table is found and the entry specified by bits 23–16 in the virtual address is read. If the present bit isn't set, PFAULT
is raised as usual. Otherwise, the low 16 bits of the virtual address are added to the page table entry with the low 16 bits set to zero and this result is used as the physical address.
63–56 (8) | 55–48 (8) | 47–40 (8) | 39–32 (8) | 31–24 (8) | 23–16 (8) | 15–0 (16) |
---|---|---|---|---|---|---|
P0 offset |
P1 offset |
P2 offset |
P3 offset |
P4 offset |
P5 offset |
Offset in page |
63–11 (53) | 10–1 (10) | 0 |
---|---|---|
Address of next table | Unused | Present |
63–16 (48) | 15–6 (10) | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
Address of page start | Unused | Modified | Accessed | User Page | Executable | Writable | Present |
User Page: Whether the page can be accessed in Ring 3. If zero, only rings 2 and lower can access it.
$rs + $rt -> $rd
or$rd += $rt
000000000001
ttttttt
sssssss
ddddddd
0000000000000
......
000000000000
Adds the values in rs
and rt
and stores the result in rd
.
$rs - $rt -> $rd
or$rd -= $rt
000000000001
ttttttt
sssssss
ddddddd
0000000000000
......
000000000001
Subtracts the value in rt
from the value in rs
and stores the result in rd
.
$rs * $rt
000000000001
ttttttt
sssssss
0000000
0000000000000
......
000000000010
Multiplies the value in rs
by the value in rt
and stores the upper half in $hi
and the lower half in $lo
.
$rs * $rt /u
000000000001
ttttttt
sssssss
0000000
0000000000000
......
000000000101
Multiplies the value in rs
by the value in rt
(treating both as unsigned values) and stores the upper half in $hi
and the lower half in $lo
.
$rs << $rt -> $rd
or$rd <<= $rt
000000000001
ttttttt
sssssss
ddddddd
0000000000000
......
000000000110
Logically shifts the value in rs
to the left by a number of bits equal to the value in rt
and stores the result in rd
.
$rs >>> $rt -> $rd
or$rd >>>= $rt
000000000001
ttttttt
sssssss
ddddddd
0000000000000
......
000000000111
Logically shifts the value in rs
to the right by a number of bits equal to the value in rt
and stores the result in rd
.
$rs >> $rt -> $rd
or$rd >>= $rt
000000000001
ttttttt
sssssss
ddddddd
0000000000000
......
000000001000
Arithmetically shifts the value in rs
to the left by a number of bits equal to the value in rt
and stores the result in rd
.
$rs % $rt -> $rd
or$rs %= $rt
000000000001
ttttttt
sssssss
ddddddd
0000000000000
......
000000001001
Computes the signed rt
-modulo of rs
and stores the result in rd
.
$rs / $rt -> $rd
000000000001
ttttttt
sssssss
ddddddd
0000000000000
......
000000001010
Divides the value in rs
by the value in rt
and stores the result in rd
, discarding the remainder.
$rs / $rt -> $rd /u
000000000001
ttttttt
sssssss
ddddddd
0000000000000
......
000000001011
Divides the value in rs
by the value in rt
(treating both as unsigned values) and stores the result in rd
, discarding the remainder.
$rs % $rt -> $rd /u
or$rs %= $rt /u
000000000001
ttttttt
sssssss
ddddddd
0000000000000
......
000000001100
Computes the unsigned rt
-modulo of rs
and stores the result in rd
.
sext32 $rs -> $rd
000000000001
0000000
sssssss
ddddddd
0000000000000
......
000000001101
If bit 31 (zero-indexed) of rs
is 1, this instruction copies rs
into rd
and sets all upper 32 bits of rd
to 1.
Otherwise, it just copies rs
into rd
with the upper 32 bits set to zero.
sext16 $rs -> $rd
000000000001
0000000
sssssss
ddddddd
0000000000000
......
000000001110
If bit 15 (zero-indexed) of rs
is 1, this instruction copies rs
into rd
and sets all upper 48 bits of rd
to 1.
Otherwise, it just copies rs
into rd
with the upper 48 bits set to zero.
sext8 $rs -> $rd
000000000001
0000000
sssssss
ddddddd
0000000000000
......
000000001111
If bit 7 (zero-indexed) of rs
is 1, this instruction copies rs
into rd
and sets all upper 56 bits of rd
to 1.
Otherwise, it just copies rs
into rd
with the upper 56 bits set to zero.
$rs & $rt -> $rd
or$rd &= $rt
000000000010
ttttttt
sssssss
ddddddd
0000000000000
......
000000000000
Computes the bitwise AND of rs
and rt
and stores the result in rd
.
$rs ~& $rt -> $rd
or$rd ~&= $rt
000000000010
ttttttt
sssssss
ddddddd
0000000000000
......
000000000001
Computes the bitwise NAND of rs
and rt
and stores the result in rd
.
$rs ~| $rt -> $rd
or$rd ~|= $rt
000000000010
ttttttt
sssssss
ddddddd
0000000000000
......
000000000010
Computes the bitwise NOR of rs
and rt
and stores the result in rd
.
~$rs -> $rd
or~$rs.
000000000010
0000000
sssssss
ddddddd
0000000000000
......
000000000011
Computes the bitwise NOT of rs
and stores the result in rd
.
$rs | $rt -> $rd
or$rd |= $rt
000000000010
ttttttt
sssssss
ddddddd
0000000000000
......
000000000100
Computes the bitwise OR of rs
and rt
and stores the result in rd
.
$rs ~x $rt -> $rd
or$rd ~x= $rt
000000000010
ttttttt
sssssss
ddddddd
0000000000000
......
000000000101
Computes the bitwise XNOR of rs
and rt
and stores the result in rd
.
$rs x $rt -> $rd
or$rd x= $rt
000000000010
ttttttt
sssssss
ddddddd
0000000000000
......
000000000110
Computes the bitwise XOR of rs
and rt
and stores the result in rd
.
$rs && $rt -> $rd
or$rd &&= $rt
000000000010
ttttttt
sssssss
ddddddd
0000000000000
......
000000001000
Computes the logical AND of rs
and rt
and stores the result in rd
.
$rs !&& $rt -> $rd
or$rd !&&= $rt
000000000010
ttttttt
sssssss
ddddddd
0000000000000
......
000000001001
Computes the logical NAND of rs
and rt
and stores the result in rd
.
$rs !|| $rt -> $rd
or$rd !||= $rt
000000000010
ttttttt
sssssss
ddddddd
0000000000000
......
000000001010
Computes the logical NOR of rs
and rt
and stores the result in rd
.
!$rs -> $rd
or!$rs.
000000000010
0000000
sssssss
ddddddd
0000000000000
......
000000001011
Computes the logical NOT of rs
and stores the result in rd
.
$rs || $rt -> $rd
or$rd ||= $rt
000000000010
ttttttt
sssssss
ddddddd
0000000000000
......
000000001100
Computes the logical OR of rs
and rt
and stores the result in rd
.
$rs !xx $rt -> $rd
or$rd !xx= $rt
000000000010
ttttttt
sssssss
ddddddd
0000000000000
......
000000001101
Computes the logical XNOR of rs
and rt
and stores the result in rd
.
$rs xx $rt -> $rd
or$rd xx= $rt
000000000010
ttttttt
sssssss
ddddddd
0000000000000
......
000000001110
Computes the logical XOR of rs
and rt
and stores the result in rd
.
$rs + imm -> $rd
or$rd += imm
000000000011
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Adds the value in rs
and a constant and stores the result in rd
.
$rs - imm -> $rd
or$rd -= imm
000000000100
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Subtracts a constant from the value in rs
and stores the result in rd
.
$rs * imm
000000000101
......
sssssss
0000000
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Multiplies the value in rs
by a constant and stores the upper half in $hi
and the lower half in $lo
.
$rs * imm /u
000000011000
......
sssssss
0000000
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Multiplies the value in rs
by a constant (treating both as unsigned values) and stores the upper half in $hi
and the lower half in $lo
.
$rs << imm -> $rd
or$rd <<= imm
000000100010
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Logically shifts the value in rs
to the left by a number of bits equal to imm
and stores the result in rd
.
$rs >>> imm -> $rd
or$rd >>>= imm
000000100011
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Logically shifts the value in rs
to the right by a number of bits equal to imm
and stores the result in rd
.
$rs >> imm -> $rd
or$rd >>= imm
000000100100
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Arithmetically shifts the value in rs
to the right by a number of bits equal to imm
and stores the result in rd
.
imm << $rs -> $rd
000000111110
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Logically shifts imm
to the left by a number of bits equal to the value in rs
and stores the result in rd
.
imm >>> $rs -> $rd
000000111111
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Logically shifts imm
to the right by a number of bits equal to the value in rs
and stores the result in rd
.
imm >> $rs -> $rd
000001000000
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Arithmetically shifts imm
to the right by a number of bits equal to the value in rs
and stores the result in rd
.
$rs % imm -> $rd
or$rd %= imm
000000011110
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Computes the signed imm
-modulo of rs
and stores the result in rd
.
$rs / imm -> $rd
000000110100
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Divides the value in rs
by a constant and stores the result in rd
, discarding the remainder.
$rs / imm -> $rd /u
000000110101
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
imm / $rs -> $rd
000000110110
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Divides a constant by the value in rs
and stores the result in rd
, discarding the remainder.
$rs % imm -> $rd /u
or$rd %= imm /u
000001000011
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Computes the unsigned imm
-modulo of rs
and stores the result in rd
.
imm / $rs -> $rd /u
000000110111
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Divides a constant by the value in rs
(treating both as unsigned values) and stores the result in rd
, discarding the remainder.
$rs & imm -> $rd
or$rd &= imm
000000000110
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Computes the bitwise AND of rs
and a constant and stores the result in rd
.
$rs ~& imm -> $rd
or$rd ~&= imm
000000000111
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Computes the bitwise NAND of rs
and a constant and stores the result in rd
.
$rs ~| imm -> $rd
or$rd ~|= imm
000000001000
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Computes the bitwise NOR of rs
and a constant and stores the result in rd
.
$rs | imm -> $rd
or$rd |= imm
000000001001
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Computes the bitwise OR of rs
and a constant and stores the result in rd
.
$rs ~x imm -> $rd
or$rd ~x= imm
000000001010
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Computes the bitwise XNOR of rs
and a constant and stores the result in rd
.
$rs x imm -> $rd
or$rd x= imm
000000001011
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Computes the bitwise XOR of rs
and a constant and stores the result in rd
.
$rs ~ $rt
000000001110
ttttttt
sssssss
.......
0000000000000
......
000000000101
Compares the value in rs
to the value in rt
(treating both as signed values) and updates the status register.
$rs < $rt -> $rd
000000001110
ttttttt
sssssss
ddddddd
0000000000000
......
000000000000
If the value in rs
is less than the value in rt
, rd
is set to 1; otherwise, rd
is set to 0.
$rs <= $rt -> $rd
000000001110
ttttttt
sssssss
ddddddd
0000000000000
......
000000000001
If the value in rs
is less than or equal to the value in rt
, rd
is set to 1; otherwise, rd
is set to 0.
$rs == $rt -> $rd
000000001110
ttttttt
sssssss
ddddddd
0000000000000
......
000000000010
If the value in rs
is equal to the value in rt
, rd
is set to 1; otherwise, rd
is set to 0.
Set on Greater Than (sg
)
$rs < $rt -> $rd /u
000000001110
ttttttt
sssssss
ddddddd
0000000000000
......
000000000011
If the value in rs
is less than the value in rt
(treating both as unsigned values), rd
is set to 1; otherwise, rd
is set to 0.
$rs <= $rt -> $rd /u
000000001110
ttttttt
sssssss
ddddddd
0000000000000
......
000000000100
If the value in rs
is less than or equal to the value in rt
(treating both as unsigned values), rd
is set to 1; otherwise, rd
is set to 0.
$rs ~ imm
000000101011
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Compares the value in rs
to imm
and updates the status register.
$rs < imm -> $rd
000000011001
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
If the value in rs
is less than imm
, rd
is set to 1; otherwise, rd
is set to 0.
$rs <= imm -> $rd
000000011010
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
If the value in rs
is less than or equal to imm
, rd
is set to 1; otherwise, rd
is set to 0.
$rs == imm -> $rd
000000011011
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
If the value in rs
is equal to imm
, rd
is set to 1; otherwise, rd
is set to 0.
$rs > imm -> $rd
000000101001
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
If the value in rs
is greater than imm
, rd
is set to 1; otherwise, rd
is set to 0.
$rs >= imm -> $rd
000000101010
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
If the value in rs
is greater than or equal to imm
, rd
is set to 1; otherwise, rd
is set to 0.
$rs < imm -> $rd /u
000000011100
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
If the value in rs
is less than imm
(treating both as unsigned values), rd
is set to 1; otherwise, rd
is set to 0.
$rs <= imm -> $rd /u
000000011101
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
If the value in rs
is less than or equal to imm
(treating both as unsigned values), rd
is set to 1; otherwise, rd
is set to 0.
$rs >= imm -> $rd /u
000000111011
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
If the value in rs
is greater than or equal to imm
(treating both as unsigned values), rd
is set to 1; otherwise, rd
is set to 0.
$rs > imm -> $rd /u
000000111100
......
sssssss
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
If the value in rs
is greater than imm
(treating both as unsigned values), rd
is set to 1; otherwise, rd
is set to 0.
: label
or: imm
:: label
or:: imm
(linking variant)
000000001111
0000000
0000000
cccc
..
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Supports conditions:
+:
/+::
: jump if positive
-:
/-::
: jump if negative
0:
/0::
: jump if zero
*:
/*::
: jump if nonzero
Jumps to the address of a given label or directly to a given address.
: label if $rs
or: imm if $rs
:: label if $rs
or:: imm if $rs
(linking variant)
000000010000
sssssss
0000000
0000
..
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Jumps to the address of a given label or directly to a given address, provided the value in rs
is nonzero.
: $rd
000000010001
0000000
0000000
ddddddd
0000000000000
cccc
..
000000000000
Supports conditions
Jumps to the address stored in rd
.
: $rd if $rs
000000010001
0000000
sssssss
ddddddd
0000000000000
......
000000000001
Jumps to the address stored in rd
, provided the value in rs
is nonzero.
:: $rd
000000010001
0000000
0000000
ddddddd
0000000000000
cccc
..
000000000010
Supports conditions
Stores the address of the next instruction in $rt
and jumps to the address stored in rd
.
:: $rd if $rs
000000010001
0000000
sssssss
ddddddd
0000000000000
......
000000000011
Stores the address of the next instruction in $rt
and jumps to the address stored in rd
, provided the value in rs
is nonzero.
[$rs] -> [$rd]
000000010010
0000000
sssssss
ddddddd
0000000000000
......
000000000000
Copies the word beginning at the memory address pointed to by rs
into memory beginning at the address pointed to by rd
.
[$rs] -> $rd
000000010010
0000000
sssssss
ddddddd
0000000000000
......
000000000001
Loads the word beginning at the memory address pointed to by rs
into rd
.
$rs -> [$rd]
000000010010
0000000
sssssss
ddddddd
0000000000000
......
000000000010
Stores the value of rs
into memory beginning at the address pointed to by rd
.
[$rs] -> [$rd] /b
000000010010
0000000
sssssss
ddddddd
0000000000000
......
000000000011
Copies the byte stored at the memory address pointed to by rs
into the memory address pointed to by rd
.
[$rs] -> $rd /b
000000010010
0000000
sssssss
ddddddd
0000000000000
......
000000000100
Loads the byte stored at the memory address pointed to by rs
into rd
.
$rs -> [$rd] /b
000000010010
0000000
sssssss
ddddddd
0000000000000
......
000000000101
Stores the lowest 8 bits of rs
into the memory address pointed to by rd
.
[$rs] -> [$rd] /h
000000010010
0000000
sssssss
ddddddd
0000000000000
......
000000001000
Copies the halfword stored at the memory address pointed to by rs
into the memory address pointed to by rd
.
[$rs] -> $rd /h
000000010010
0000000
sssssss
ddddddd
0000000000000
......
000000001001
Loads the halfword stored at the memory address pointed to by rs
into rd
.
$rs -> [$rd] /h
000000010010
0000000
sssssss
ddddddd
0000000000000
......
000000001010
Stores the lowest 32 bits of rs
into the memory address pointed to by rd
.
[$rs] -> [$rd] /s
000000010010
0000000
sssssss
ddddddd
0000000000000
......
000000001100
Copies the short stored at the memory address pointed to by rs
into the memory address pointed to by rd
.
[$rs] -> $rd /s
000000010010
0000000
sssssss
ddddddd
0000000000000
......
000000001101
Loads the short stored at the memory address pointed to by rs
into rd
.
$rs -> [$rd] /s
000000010010
0000000
sssssss
ddddddd
0000000000000
......
000000001110
Stores the lowest 16 bits of rs
into the memory address pointed to by rd
.
[ $rs
000000010010
0000000
sssssss
0000000
0000000000000
......
000000000110
Copies the word at rs
into the stack and adjusts the stack pointer.
See also: push pseudoinstruction
] $rd
000000010010
0000000
ddddddd
0000000
0000000000000
......
000000000111
Adjusts the stack pointer and copies the word at the stack pointer into rd
.
See also: pop pseudoinstruction
memset $rs x $rt -> $rd
000000010010
ttttttt
ddddddd
sssssss
0000000000000
......
000000001011
Sets rs
bytes to rt
starting at address rd
. The value in rt
will be truncated to 8 bits.
translate $rs -> $rd
000001000100
0000000
ddddddd
sssssss
0000000000000
......
000000000000
Translates the virtual address stored in rs
and stores the resulting physical address in rd
.
If paging is disabled, this instruction simply copies rs
into rd
.
A page fault will occur if paging is enabled and the virtual address has no corresponding physical address.
[imm] -> $rd
000000010011
......
0000000
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Loads the word beginning at address imm
into rd
.
$rs -> [imm]
000000010100
......
sssssss
0000000
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Stores the value of rs
into memory beginning at address imm
.
[imm] -> $rd /b
000000100101
......
0000000
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Loads the byte at address imm
into rd
.
$rs -> [imm] /b
000000100110
......
sssssss
0000000
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Stores the lowest 8 bits of rs
into memory at address imm
.
[imm] -> [$rd]
000000100111
......
sssssss
0000000
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Copies the word stored in memory at address imm
into the memory beginning at the address pointed to by rd
.
[imm] -> [$rd] /b
000000101000
......
sssssss
0000000
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Copies the byte stored in memory at address imm
into the memory address pointed to by rd
.
imm -> $rd
000000010101
......
0000000
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Sets a register to the given immediate value.
lui: imm -> $rd
000000001101
......
0000000
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Loads an immediate value into the upper half of the word at rd
. The lower half is not affected.
[:imm $rs
000000111001
......
sssssss
0000000
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Writes rs
to the stack and decrements the stack pointer by imm
bytes.
]:imm $rd
000000111010
......
0000000
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Increments the stack pointer by imm
bytes and reads into rd
from the stack.
$rs -> [$fp - imm]
000000010110
......
sssssss
0000000
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Stores rs
at the address derived by subtracting imm
from the current value of $fp
.
[$fp - imm] -> $rd
000000010111
......
0000000
ddddddd
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Loads rd
from the address derived by subtracting imm
from the current value of $fp
.
(varies; see Externals)
000000011111
ttttttt
sssssss
ddddddd
0000000000000
......
xxxxxxxxxxxx
Performs a special instruction, typically for interaction with the world outside the VM.
[$rs = $rt] -> $rd
[$rs < $rt] -> $rd
[$rs > $rt] -> $rd
[$rs != $rt] -> $rd
000000111000
ttttttt
sssssss
ddddddd
0000000000000
cond
..
xxxxxxxxxxxx
Checks the status register and the condition bits. If the condition matches the status register, rd
is set to rs
; otherwise, it's set to rt
.
%int imm
000000100000
......
.......
.......
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Performs an interrupt. If no interrupt table has been registered, nothing interesting happens.
%rit imm
000000100001
......
.......
.......
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Registers the interrupt table. Takes a pointer to a table in the data section. Valid only in kernel mode; will cause the machine to halt if called in user mode.
%time $rs
000000110000
.......
sssssss
.......
0000000000000
......
000000000000
Sets the hardware timer to the number stored in rs
(in microseconds), canceling any previous timer. Requires kernel mode. Sub-millisecond precision may be unsupported or inaccurate. Once the timer expires, a timer interrupt occurs.
%time imm
000000110001
......
.......
.......
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Sets the hardware timer to the number stored in imm
(in microseconds), canceling any previous timer. Requires kernel mode. Sub-millisecond precision may be unsupported or inaccurate. Once the timer expires, a timer interrupt occurs.
%time -> $rd
000000110000
.......
sssssss
.......
0000000000000
......
000000000001
Saves the number of microseconds until the hardware timer expires in rd
. If no hardware timer is active, this instruction sets rd
to 0.
%ring $rs
000000110010
.......
sssssss
.......
0000000000000
......
000000000000
Sets the protection ring to the value stored in rs
. A protection interrupt will occur if the indicated ring is lower than the current ring to prevent privilege escalation.
%ring -> $rd
000000110010
.......
.......
ddddddd
0000000000000
......
000000000001
Stores the current protection ring in rd
.
%ring imm
000000110011
......
.......
.......
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Sets the protection ring to imm
. A protection interrupt will occur if the indicated ring is lower than the current ring to prevent privilege escalation.
%page off
000000111101
.......
.......
.......
0000000000000
......
000000000000
Disables virtual memory. Raises PROTEC
if used in a ring other than ring zero.
%page on
000000111101
.......
.......
.......
0000000000000
......
000000000001
Enables virtual memory. Raises PROTEC
if used in a ring other than ring zero.
%setpt $rs
: %setpt $rs $rt
000000111101
ttttttt
sssssss
.......
0000000000000
......
000000000010
Sets the address of P0
. Raises PROTEC
if used in a ring other than ring zero.
If rt
is specified (i.e., if it's any register other than $0
), it will be jumped to.
%page -> $rd
000000111101
.......
.......
ddddddd
0000000000000
......
000000000011
Sets rd
to 1 if paging is enabled or 0 if paging is disabled.
? mem -> $rd
000001000001
.......
.......
ddddddd
0000000000000
......
000000000000
Sets rd
to the size of the main memory in bytes.
%di
000001000010
.......
.......
.......
0000000000000
......
000000000000
Disables hardware interrupts. This currently includes TIMER
and KEYBRD
.
%ei
000001000010
.......
.......
.......
0000000000000
......
000000000001
Enables hardware interrupts. This currently includes TIMER
and KEYBRD
.
[ %page
[ %page
000000111101
.......
.......
.......
0000000000000
......
000000000100
Pushes the current paging state (whether paging is enabled, plus the physical address of P0) to a special stack that's not part of the accessible memory. If the implementation's stack size is limited and the stack is full, the bottom of the stack will be removed before the current paging state is pushed. Requires ring zero.
] %page
: ] %page $rs
000000111101
.......
sssssss
.......
0000000000000
......
000000000101
Pops a paging state (whether paging is enabled, plus the physical address of P0) from a special stack that's not part of the accessible memory. Does nothing if the stack is empty. If rs
is specified (i.e., if it's any register other than $0
), it will be jumped to, even if the paging stack was empty. Requires ring zero.
$rs -> $rd
Copies the value of rs
into rd
.
Translation:
$rs | $0 -> $rd
ret
Jumps to the return address.
Translation:
: $r
[ $x $y $z ...
Pushes the value of the specified register(s) to the stack. Acts as a shorthand for calling spush on multiple registers.
Translation for each register in order:
[ $rs
] $x $y $z
Pops the value(s) at the top of the stack and stores the value(s) in the specified register(s). Acts as a shorthand for calling spop on multiple registers.
Translation for each register in order:
] $rs
: $rd if $rs == $rt
If the value in rs
is equal to the value in rt
, jumps to the address stored in rd
. (Modifies m7
.)
Translation:
$rs == $rt -> $m7
: $rd if $m7
: label if $rs == $rt
If the value in rs
is equal to the value in rt
, jumps to label
. (Modifies m7
.)
$rs >= $rt -> $rd
If the value in rs
is greater than or equal to the value in rt
, rd
is set to 1; otherwise, rd
is set to 0.
Translation:
$rt <= $rs -> $rd
$rs > $rt -> $rd
If the value in rs
is greater than the value in rt
, rd
is set to 1; otherwise, rd
is set to 0.
Translation:
$rt < $rs -> $rd
$rs >= $rt -> $rd /u
If the value in rs
is greater than or equal to the value in rt
(treating both as unsigned values), rd
is set to 1; otherwise, rd
is set to 0.
Translation:
$rt <= $rs -> $rd
$rs > $rt -> $rd /u
If the value in rs
is greater than the value in rt
(treating both as unsigned values), rd
is set to 1; otherwise, rd
is set to 0.
Translation:
$rt < $rs -> $rd /u
Syntax: <print $rs>
Function value: 000000000001
Prints the value stored in rs
to the console.
Syntax: <halt>
Function value: 000000000010
Halts the VM.
Syntax: <eval $rs>
Function value: 000000000011
Evaluates the string beginning at the address stored in rs
. Unimplemented.
Syntax: <prc $rs>
Function value: 000000000100
Prints the character stored in rs
to the console.
Syntax: <prd $rs>
Function value: 000000000101
Prints the number stored in rs
to the console as a decimal number.
Syntax: <prx $rs>
Function value: 000000000110
Prints the number stored in rs
to the console as a hexadecimal number.
Syntax: <sleep $rs>
Function value: 000000000111
Sleeps for the number of microseconds stored in rs
.
Syntax: <prb $rs>
Function value: 000000001000
Prints the number stored in rs
to the console as a binary number.
Syntax: <rest>
Function value: 000000001001
Pauses execution until an interrupt occurs.
Syntax: <io operation_name>
Function value: 000000001010
Interacts with storage devices. See the I/O document for details.