Quadmachine is a virtual instruction set architecture, designed mainly for educational purposes. It has the following noteworthy features:
All of these, except for the preposterously large register file, are realistic for a 64-bit RISC design. The size of the register file is intended to trivialize register allocation in expository compilers.
This package is a software realization of the Quadmachine architecture. It is written in Java for portability; speed was not an implementation concern.
Here are some sample Quadmachine assembly programs:
The latest version is 2.4.0, released on 2007-02-10. Download the software at http://antti-juhani.kaijanaho.fi/software/dist/. Bleeding edge sources are available via darcs by the command darcs get http://antti-juhani.kaijanaho.fi/darcs/quadmachine/.
The class fi.jyu.mit.quadmachine.VirtualMachine is the façade to the implementation. It can be invoked as the main class, or it can be directly instantiated by another Java program.
This is the second design and implementation of Quadmachine, both by Antti-Juhani Kaijanaho in December 2006. The first design was done in August 2004 by Antti-Juhani Kaijanaho and implemented by Tuukka Hastrup. The main changes are the introduction of general-purpose registers, the increase of word and address size from 32 to 64 bits, and move from a memory-memory instruction set to a load-store instruction set.
There are two interfaces to the instruction set in the implementation: the assembler and the InstructionSet Java interface.
In assembly, register operands are denoted by $N, where N is an unsigned base-10 number between 0 and 65535, inclusive. All other numeric literals are also in base-10 (although, confusingly, disassembly uses base-16). Mnemonics are case-insensitive, labels are case-sensitive.
Registers $0 to $65533 (inclusive) are general purpose. The register $65534 can be used like a general purpose register, but it is customarily reserved for use as the stack pointer. The register $65535 is always zero and ignores any write attempts.
Unless otherwise indicated, the first operand is where the result is stored.
| Assembly instruction | InstructionSet method | Description |
|---|---|---|
| Load instructions | ||
| ld $N, $M | addIntegers | copies the content of $M to $N. Note that this is just another name for add $N, $M, $65535. |
| ld $N, imm | loadImmediate | copies the integer imm to $N. |
| ld $N, label | loadLabel | copies the address denoted by label to $N. |
| ld $N, [M] | loadDirect | copies the word at address M to $N. |
| ld $N, [label] | loadDirectLabel | copies the word at address denoted by label to $N. |
| ld $N, [$M+K] ld $N, [$M-K] |
loadIndirect | copies to $N the word at the address obtainable by adding (or subtracting) K to (from) the content of $M. |
| Store instructions | ||
| st $N, [M] | storeDirect | copies the content of $N to the word at address M. |
| st $N, [label] | storeDirectLabel | copies the content of $N to the word at address denoted by label. |
| st $N, [$M+K] st $N, [$M-K] |
storeIndirect | copies the contet of $N to the word at the address obtainable by adding (or subtracting) K to (from) the content of $M. |
| Integer arithmetic | ||
| add $N, $M, imm | addImmediate | Adds together integers. The third operand, imm, is an immediate integer. Note that it does not matter whether the integers are signed or unsigned; the operation is identical for both types. |
| add $N, $M, $K | addIntegers | Adds together integers. Note that it does not matter whether the integers are signed or unsigned; the operation is identical for both types. |
| sub $N, $M, $K | subtractIntegers | Subtracts the content of $K from the content of $M. Note that it does not matter whether the integers are signed or unsigned; the operation is identical for both types. |
| muls $N, $M, $K, $L | multiplyIntegers | Multiplies the 64-bit signed integers stored in $K and $L, and stores the resulting 128-bit signed integer in $N (the least significant 64 bits) and $M (the most significant 64 bits). |
| mulu $N, $M, $K, $L | multiplyUnsigned | Multiplies the 64-bit unsigned integers stored in $K and $L, and stores the resulting 128-bit unsigned integer in $N (the least significant 64 bits) and $M (the most significant 64 bits). |
| divs $N, $M, $J, $K, $L | divideIntegers | Divides the 128-bit signed integer stored in $J (most significant 64 bits) and $K (least significant 64 bits) by the 64-bit signed integer $L, and stores the results as follows: the quotient in $N and the remainder in $M. |
| divu $N, $M, $J, $K, $L | divideUnsigned | Divides the 128-bit unsigned integer stored in $J (most significant 64 bits) and $K (least significant 64 bits) by the 64-bit unsigned integer $L, and stores the results as follows: the quotient in $N and the remainder in $M. |
| Bitwise operations | ||
| shl $N, $M, $K | shiftLogically | Shifts $M to the left bitwise by $K bits. If $K is negative, the shift is to the right by the absolute value of $K. New bits are initialized to zero. |
| sha $N, $M, $K | shiftArithmetically | Shifts $M to the left bitwise by $K bits. If $K is negative, the shift is to the right by the absolute value of $K. New rightmost bits are initialized to zero; the leftmost bit is copied to any new leftmost bits, thereby performing sign extension. |
| and $N, $M, $K | binaryAnd | Performs the bitwise AND operation. |
| not $N, $M | binaryNot | Performs the bitwise NOT operation. |
| or $N, $M, $K | binaryInclusiveOr | Performs the bitwise (inclusive) OR operation. |
| xor $N, $M, $K | binaryExclusiveOr | Performs the bitwise XOR (exclusive or) operation. |
| Jumps and branches | ||
| jump label | jump | Jumps (unconditionally) to the address denoted by label. |
| jump [$N] | jumpIndirect | Jumps (unconditionally) to the address stored in the register $N. |
| jz $N, label | jumpIfZero | Jumps to the address denoted by label, if $N is zero. |
| jn $N, label | jumpIfNegative | Jumps to the address denoted by label, if $N is negative. |
| jp $N, label | jumpIfPositive | Jumps to the address denoted by label, if $N is zero. |
| jnz $N, label | jumpUnlessZero | Jumps to the address denoted by label, unless $N is zero. |
| jnn $N, label | jumpUnlessNegative | Jumps to the address denoted by label, unless $N is negative. |
| jnp $N, label | jumpUnlessPositive | Jumps to the address denoted by label, unless $N is zero. |
| System calls | ||
| sys read | invokeSystem(SYS_READ) |
System call to read from a file. Parameters
Return values
Failure conditions
|
| sys write | invokeSystem(SYS_WRITE) |
System call to write to a file. Parameters
Return values
Failure conditions
|
| sys exit | invokeSystem(SYS_EXIT) |
System call to terminate the program. Parameters
Return values The system call does not return. Failure conditions The system call does not fail. |
| Miscellaneous | ||
| slide imm | slideRegisters |
Slides the register window up (if positive) or down (if negative). More precisely: If imm is positive, the following happen:
If imm is negative, the following happen:
If imm is zero, this instruction merely makes sure that the segment in which the current address stored in $65534 lies is mapped in. |