^ go up - main page

vurce machine description

This page describes the vurce virtual machine and its standard I/O devices.

memory and registers

vurce is a stack-based system. The machine uses the following memory spaces, which are all completely separate from each other and use separate address spaces:

The implementation must ensure that device memory is initialized to all zeroes. The behavior of uninitialized memory for all other memory spaces is undefined.

The machine uses the following three registers:

All registers have an initial value of zero.

vurce is a little-endian system. That is, 16-bit values are stored with their least significant byte in the lowest memory address, and their most significant byte in the highest memory address. For example, the hexadecimal integer 0x12ab would actually be stored in memory as the bytes 0xab followed by 0x12.

All opcodes exclusively treat values as unsigned integers. However, an I/O device may occasionally wish to encode a signed integer value; in this case, two's compliment encoding is used.

Both stacks always grow upwards. When a value is pushed to a stack, that value is written to the address pointed to by the stack pointer, then the stack pointer is incremented. When a value is popped, the stack pointer is decremented, and the value pointed to by the stack pointer is read. Stack underflows and overflows do not trigger errors: the stack pointer simply wraps around.

The majority of opcodes operate on the main stack (which will often be referred to as just "the stack" in this document). The main stack exclusively holds 16-bit values. Single byte values can still be read and written between the stack and main memory; they will just be converted to/from 16-bit values on the stack. The main stack can hold a maximum of 128 values.

The call stack is used for subroutine calls, and exclusively stores 16-bit addresses to main memory. vurce supports a maximum of 128 nested subroutine calls.

Device memory provides a memory-mapped interface to I/O devices. Addresses to device memory are known as "ports". I/O devices can perform tasks in response to ports being read from or written to. Often, two consecutive ports are used to hold a single 16-bit value.

vectors

A vector is a location in a program that is executed when an event occurs. The reset vector is located at address 0x0000, and is executed when the program starts. Other vectors may be executed by I/O devices. Vectors are the primary means by which I/O events are handled, as opposed to interrupts or polling.

instructions

All instructions consist of solely a single byte opcode.

Boolean values are represented as 0x0000 for false, 0xffff for true. This is so that boolean NOT is equivalent to bitwise NOT.

When an instruction jumps to an address, the next instruction to be executed should be the byte at that address, not the byte after. An easy way to accomplish this is to increment pc after the opcode is read, but before it is executed.

Name Opcode Synopsis Description
ret 0x00 -- Returns from the current subroutine (pops from the call stack, then sets pc to that value). If not currently in a subroutine (ie. csp is 0), stops execution of the current vector.
push 0x01 -- N Reads the 16-bit value stored in the 2 bytes immediately following the opcode, and pushes it to the stack. Increments pc by 2, so as to skip over the immediate value. This is the only opcode which takes a postfix argument.
dup 0x02 x -- x x Duplicates the top stack value.
swap 0x03 x y -- y x Swaps the top two stack values.
over 0x04 x y -- x y x Takes the second value from the top and duplicates it to the top.
rot 0x05 x y z -- y z x Takes the third value from the top and moves it to the top.
drop 0x06 x -- Pops a value from the stack and discards it.
setb 0x07 x addr -- Writes the lower byte of x into addr.
getb 0x08 addr -- x Reads a byte from addr.
set 0x09 x addr -- Writes the 16-bit value x into addr.
get 0x0a addr -- x Reads a 16-bit value from addr.
add 0x0b x y -- x+y Push x plus y.
sub 0x0c x y -- x-y Push x minus y.
mul 0x0d x y -- x*y Push x multiplied by y.
div 0x0e x y -- x/y Push x divided by y. If y is 0, pushes 0.
mod 0x0f x y -- x%y Push x modulo y. If y is 0, pushes 0.
and 0x10 x y -- x&y Performs a bitwise AND operation on x and y.
or 0x11 x y -- x|y Performs a bitwise OR operation on x and y.
xor 0x12 x y -- x^y Performs a bitwise XOR operation on x and y.
not 0x13 x -- ~x Performs a bitwise NOT operation on x.
Note that this inverts all 16 bits of x, so there may be unexpected results if you store boolean variables as single bytes.
eq 0x14 x y -- x==y Pushes 0xffff if x is equal to y, 0x0000 otherwise.
neq 0x15 x y -- x!=y Pushes 0xffff if x is not equal to y, 0x0000 otherwise.
gt 0x16 x y -- x<y Pushes 0xffff if x is greater than y, 0x0000 otherwise.
lt 0x17 x y -- x>y Pushes 0xffff if x is less than y, 0x0000 otherwise.
jmp 0x18 addr -- Jumps to addr (sets pc to addr).
jc 0x19 cond addr -- Jumps to addr if cond is a non-zero value.
call 0x1a addr -- Calls the subroutine at addr (pushes pc to the call stack, then sets pc to addr).
outb 0x1b x port -- Writes the lower byte of x into port.
inb 0x1c port -- x Reads a byte from port.
out 0x1d x port -- Writes the 16-bit value x into port.
in 0x1e port -- x Reads a 16-bit value from port.

standard devices

In theory, one can simply implement the "vurce CPU" and map the I/O ports to whatever functions suit the implementation's desires. However, vurce has a standard set of I/O devices, described below.

The higher nybble (4 bits) of a port specify which device the port belongs to, while the lower nybble specifies the port within that device. For example, port 0x42 refers to port 0x2 of device 0x4. Thusly, a maximum of 16 devices are supported, each holding 16 ports.

Some I/O ports may hold an address to a vector to be executed when an event occurs. In this case, the vector must not be executed if its value is 0x0000 (ie. uninitialized).

NOTE: vurce's standard devices are a work in progress. Some devices have yet to be specified, and the existing ones are subject to change.

system device (0x00-0x0f)

The system device provides access to some generic system functionality, including interfacing with standard terminal input/output.

Port Name Description
0 stdout When written, write the given character to stdout.
1 stdin When read, read a character from stdin. Reads from stdin may be blocking.
2-3 printd When written, prints a value to the console as an unsigned decimal integer.
Mostly intended for debugging and convenience.

screen device (0x10-0x1f)

The screen device provides an interface to a graphics display with a resolution of 240x180 pixels and 216 possible colors. Graphics drawing is accomplished primarily by copying rectangles of pixel data from main memory onto the framebuffer.

The pallete of 216 colors is the same as the "web-safe" color pallete: each red, green, and blue value can be from 0 to 5. Each pixel is represented by a single byte, encoded by the formula: r*36 + g*6 + b. Eventually I'll make a reference image for how colors are encoded, but for now, there's this very hard to read image from a different VM which uses the same scheme.

Coordinates follow the common convention in which x=0,y=0 is the top left of the screen, with positive X going rightwards and positive Y going downwards.

Port Name Description
0-1 vector Stores a vector which is executed before drawing each frame, at a rate of 60hz.
2 x Stores the X position of the rectangle to copy to.
3 y Stores the Y position of the rectangle to copy to.
4 width Stores the width of the rectangle to copy to.
5 height Stores the height of the rectangle to copy to.
6 color Stores the color to be used by a drawing operation.
7-8 source Stores the address of pixel data to be copied to the specified rectangle.
9 cmd When written, executes a drawing operation (see below).

The possible drawing operations are:

Command Description
0x00 Fills the rectangle specified by x y width height with the color color.
0x01 Copies pixels from source into the rectangle specified by x y width height.
The implementation must ensure that pixels which go outside the edges of the screen are not drawn in any way, and that sprites do not appear distorted when going past the edges.
If the pixel value 0xff is encountered, no pixel at all is drawn to the screen, allowing sprites to have masked backgrounds.
0x02 Copies a bitmap from source with the color color into the rectangle specified by x y width height.
This is the same as the previous command, except that values are read one bit at a time (from most to least significant) instead of one byte at a time. If a bit is 1, color is written to that pixel, otherwise nothing is written at all (ie. the background is "transparent").

audio device (0x20-0x2f)

The audio device will eventually allow programs to play audio in some way. However, it does not yet exist.

keyboard device (0x30-0x3f)

The keyboard device provides an interface to a keyboard. The keyboard device only recognizes a reduced set of often-used keys. The implementation must ignore any key presses/releases involving an unsupported key.

Port Name Description
0-1 vector Stores a vector which is executed when a key is pressed/released.
2 key Stores the keycode that was just pressed or released. Bit 7 (the leftmost, most significant bit) will be 1 if the key was released, or 0 if the key was pressed.

Each key is assigned a 7-bit code (0x00-0x7f). Any key which types a printable ASCII character (codes 0x20-0x7e) is assigned the same code as the ASCII character it types (without shift or caps lock active). Other keys are assigned the following codes:

Code Key
0x01 Up arrow
0x02 Down arrow
0x03 Left arrow
0x04 Right arrow
0x05 Shift (either key)
0x06 Caps lock
0x07 Control (either key)
0x08 Backspace
0x09 Tab
0x0d Return/Enter
0x10 Alt (either key)
0x1b Escape

mouse device (0x40-0x4f)

The mouse device provides an interface to a mouse. The mouse device only recognizes the left, right, and middle mouse buttons. The implementation must ignore any presses/releases involving unsupported mouse buttons.

Port Name Description
0-1 vector Stores a vector which is executed when the mouse is moved, a mouse button is pressed, or the mouse's scroll wheel is moved.
2 x Stores the X position of the mouse.
3 y Stores the Y position of the mouse.
4 buttons Stores a bitmap encoding which mouse buttons are being pressed. A bit is 1 if that button is pressed, 0 if not.
Bit 0 (the least significant bit) is the left button, bit 1 is the right button, bit 2 is the middle button.
5 scrollx Stores the amount that was just scrolled horizontally, as a signed integer. Positive is to the right, negative is to the left.
This port is reset to 0 after the mouse vector has been executed.
6 scrolly Stores the amount that was just scrolled vertically, as a signed integer. Positive is upwards, negative is downwards.
This port is reset to 0 after the mouse vector has been executed.

disk device (0x50-0x5f)

The disk device may eventually provide an interface to some sort of sandboxed long-term storage. However, it does not yet exist.

time device (0x60-0x6f)

The time device allows programs to read the current local date and time.

Port Name Description
0-1 year When read, returns the current year.
2 month When read, returns the current month from 1 to 12.
3 day When read, returns the current day of the month from 1 to 31.
4 hour When read, returns the current hour from 0 to 23.
5 minute When read, returns the current minute from 0 to 59.
6 second When read, returns the current second from 0 to 60.
7 weekday When read, returns the current day of the week from 0 to 6, where 0 is Sunday.