Haxor VM
Haxor consists of compiler hcc, linker hld and virtual machine hvm. hcc translates asm-like code into tokens, hld links them into bytecode, while hvm runs it.
Man, why have you written that?
Writing own implementation of VM gives a lot of knowledge about computer's architecture. You hit into issues not known during day by day activity in high level languages you use. So... just to broaden horizons and for fun ;)
Usage
Compilation:
hcc program.hax
Linking:
hld -o program.hax.e program.hax.u
hld -s 4096 -o program.hax.e program.hax.u # custom stack size
Run:
hvm program.hax.e
License
Haxor is licensed under BSD 3-clause license. You can read it here.
Architecture
General information
- Little Endian
- Size of WORD is 64 bit
- All registers are 64 bit
- All numbers are signed
- Memory works in flat model
- Only integer arithmetic is supported
- All memory cells are writable
OpCodes
OpCode is 64 bit integer. 0-7 bits are used to designate command, 8-63 are used to specify flags. Command can take 0, 1 or 2 operands. All operands are 64 bit addresses in memory. Const values are pushed into .data section by compiler and pointed by automatically generated labels.
[opcode (command + flags)] [operand1] [operand2]
vCPU
vCPU registers are mapped into lowest memory addresses between 0 and 1024 bytes.
- ip - instruction pointer
- sp - stack pointer
- bp - base pointer
- ar - arithmetic register I
- dr - arithmetic register II
- fr - flags register
- sc - syscall register
- op - currently processed opcode
- r01-10 - general usage registres
Flags register
Flags register is 64 bit.
- bit 0 - ZERO (
A-B=0, so numbers are equal) - bit 1 - SIGN (result is negative)
- rest is reserved for future use
Memory map
Language
Haxor uses primitive asm-like syntax. Each command goes into separate line.
You can add comments in code, but they also need to be separate lines, beginning
from # or rem.
For two-argument commands destination goes into first argument while source into second one.
In some commands you can dereference value by enclosing it in brackets (e.g. [sp]).
Program starts from main label.
command A, B
Instructions
Arithmetic
add
Sums A and B, result goes to A. A or/and B can be dereferenced. OpCode: 0x01.
add A, B
sub
Subtracts B from A, result goes to A. A or/and B can be dereferenced. OpCode: 0x02.
sub A, B
div
Divides ar register by A. Result goes to register. Remainder goes to dr register.
A can be dereferenced.
OpCode: 0x03.
div A
mul
Multiplies ar register by A. Result goes to register.
A can be dereferenced.
OpCode: 0x04.
mul A
inc
Increments A by 1. A can be dereferenced. OpCode: 0x05.
inc A
dec
Decrements A by 1. A can be dereferenced. OpCode: 0x06.
dec A
cmp
Compares A with B by subtracting B from A and setting flags register bits. A or/and B can be dereferenced. OpCode: 0x07.
cmp A, B
Logical
and
Performs bitwise AND operation. A or/and B can be dereferenced. OpCode: 0x40.
and A, B
neg
Reverses the sign of number A. A can be dereferenced. OpCode: 0x41.
neg A
not
Performs bitwise NOT operation. A can be dereferenced. OpCode: 0x42.
not A
or
Performs bitwise OR operation. A or/and B can be dereferenced. OpCode: 0x43.
or A, B
xor
Performs bitwise XOR operation. A or/and B can be dereferenced. OpCode: 0x44.
xor A, B
Transfer
mov
Moves data from B to A. A or/and B can be dereferenced. OpCode: 0x60.
mov A, B
push
Places A on top of stack. OpCode: 0x61.
push A
pop
Removes element from top of the stack and into A. OpCode: 0x62.
pop A
Jumps
call
Places ip register on the stack and jumps to A. A can be dereferenced. OpCode: 0x20.
call A
ret
Pops ip from the stack, and jumps to it. OpCode: 0x2c.
ret
iret
Comes back from interrupt. OpCode: 0x2d.
iret
jmp
Performs unconditional jump to A. A can be dereferenced. OpCode: 0x21.
jmp A
je
Jumps to A if in cmp A is equal to B. A can be dereferenced. OpCode: 0x22.
je A
jg
Jumps to A if in cmp A is greater than B. A can be dereferenced. OpCode: 0x23.
jg A
jge
Jumps to A if in cmp A is greater or equal to B. A can be dereferenced. OpCode: 0x24.
jge A
jl
Jumps to A if in cmp A is less than B. A can be dereferenced. OpCode: 0x25.
jl A
jle
Jumps to A if in cmp A is less or equal to B. A can be dereferenced. OpCode: 0x26.
jle A
jne
Jumps to A if in cmp A is not equal to B. A can be dereferenced. OpCode: 0x27.
jne A
jng
Jumps to A if in cmp A is not greater than B. A can be dereferenced. OpCode: 0x28.
jng A
jnge
Jumps to A if in cmp A is not greater or equal to B. A can be dereferenced. OpCode: 0x29.
jnge A
jnl
Jumps to A if in cmp A is not less than B. A can be dereferenced. OpCode: 0x2a.
jnl A
jnle
Jumps to A if in cmp A is not less or equal to B. A can be dereferenced. OpCode: 0x2b.
jnle A
Various
lea
Pushes address of B into A. OpCode: 0x80.
lea A, B
nop
Does nothing. OpCode: 0x81.
nop
int
Generate software interrupt with ID specified by A. A can be dereferenced. OpCode: 0x85.
int A
syscall
Asks Haxor VM to do "system" call. OpCode: 0x86.
syscall
System calls
Using syscall command you can run some system calls provided by Haxor VM. System call number is passed via sc register, arguments go via stack in reversed order.
exit (01h)
Terminates VM process with specified exit code. Takes 1 argument:
- exit code
push 100
mov sc, 01h
syscall
printf (02h)
Prints formatted text into file specified by descriptor. Takes 2 or more arguments:
- file descriptor (1 for standard output, 2 for standard error)
- format string
- data depending on format string...
lea r01, format_text
push r01
push 1
mov sc, 02h
scanf (03h)
Converts data from file specified by descriptor. Remember that memory is not automatically allocated by this function. You need to prepare space before calling this function. Use length limits to avoid buffer overflow (e.g. %100s to take up to 100 characters from string). In case of string your buffer must have 1 element more for closing '0'. Takes 2 or more arguments:
- file descriptor (0 for standard input)
- format string
- addresses in memory to put data into them...
section .data
dw scanfmt, "%100s", 0
section .bss
label name
resw 101
lea r01, name
push r01
lea r01, scanfmt
push r01
push 0
mov sc, 03h
syscall
random (04h)
Generates random integer from specified range. Arguments:
- minimum (inclusive)
- maximum (inclusive)
Generated number is pushed onto stack.
mov sc, 04h
push 100
push 1
syscall