A complete 64-bit virtual machine implementation featuring a custom CPU architecture, assembler, compiler, and development environment built in Java with JavaFX.
The Triton-64 VM is a comprehensive virtual machine system that includes:
- Custom 64-bit CPU Architecture: Complete instruction set with 32 registers
- TriC Programming Language + Compiler: High-level language with optional typing that compiles to Triton-64 assembly
- Assembler: Converts assembly code to machine code with macro expansion
- Memory Management: Sophisticated memory mapping with ROM, RAM, MMIO, and framebuffer regions
- Visual Debugging Tools: Real-time CPU and memory viewers
- ROM System: Bootable ROM with integrated assembly code
- Architecture: 64-bit RISC-style processor
- Registers: 32 general-purpose 64-bit registers
- Instruction Set: 32-bit fixed-width instructions
- Memory Model: Unified 64-bit address space
- Execution Model: Sequential execution with conditional jumps
| Alias | Numeric | Purpose |
|---|---|---|
ra |
r0 |
Return address |
sp |
r1 |
Stack pointer |
fp |
r2 |
Frame pointer |
gp |
r3 |
Global pointer |
hp |
r4 |
Heap pointer |
s0-s9 |
r5-r14 |
Saved registers |
a0-a6 |
r15-r21 |
Argument registers |
t0-t8 |
r22-r30 |
Temporary registers |
t9 |
r31 |
Reserved for assembler use |
0x0000_0000_0000_0000 ┌─────────────────┐
│ ROM │ 128 KB
0x0000_0000_0002_0000 ├─────────────────┤
│ RAM │ 512 MB
0x0000_0000_2002_0000 ├─────────────────┤
│ MMIO │ 2 MB
0x0000_0000_2022_0000 ├─────────────────┤
│ Framebuffer │ 16 MB
0x0000_0000_2122_0000 └─────────────────┘
NOP- No operationHLT- Halt CPU execution
MOV rdest, rsrc- Copy register valueLDI rdest, imm10- Load 10-bit signed immediate (-512 to +511)LD rdest, rsrc- Load from memory addressST rdest, rsrc- Store to memory address
ADD rdest, rsrc1, rsrc2- AdditionSUB rdest, rsrc1, rsrc2- SubtractionMUL rdest, rsrc1, rsrc2- MultiplicationDIV rdest, rsrc1, rsrc2- DivisionNEG rdest, rsrc- Arithmetic negation
AND rdest, rsrc1, rsrc2- Bitwise ANDOR rdest, rsrc1, rsrc2- Bitwise ORXOR rdest, rsrc1, rsrc2- Bitwise XORNOT rdest, rsrc- Bitwise NOTSHL rdest, rsrc1, rsrc2- Logical shift leftSHR rdest, rsrc1, rsrc2- Logical shift rightSAR rdest, rsrc1, rsrc2- Arithmetic shift right
JMP rdest- Unconditional jump to register addressJZ rdest, rsrc- Jump if zeroJNZ rdest, rsrc- Jump if not zeroJPP rdest, rsrc- Jump if positiveJPN rdest, rsrc- Jump if negativeJAL rdest, rsrc- Jump and link (store return address)
The assembler provides powerful macro expansions for common operations:
loop: ; Define jump target
JMP loop ; Jump to label (expands to address loading)
JZ loop, r0 ; Conditional jump to labelLDI r0, 0x123456789ABCDEF ; 64-bit immediate (multi-instruction expansion)
LDIU r0, 0x123456789ABCDEF ; Unsafe fast load (clobbers temps)PUSH r0, r1, r2 ; Push multiple registers to stack
POP r0, r1, r2 ; Pop multiple registers from stackADD r0, r1, 42 ; Add immediate (expands to temp + register operation)
SUB r0, r1, 100 ; Subtract immediateTriC is a high-level programming language designed specifically for the Triton-64 Virtual Machine. It provides a more accessible way to write programs for the VM compared to writing raw assembly code. TriC features a C-like syntax with optional type annotations that primarily affect memory access sizes rather than enforcing type safety.
TriC features an optional type system where types mainly guide memory access operations. Unlike strongly-typed languages, TriC allows implicit conversions between all types and does not enforce type safety.
| Type | Size (bytes) | Description |
|---|---|---|
byte |
1 | 8-bit value |
int |
4 | 32-bit value |
long |
8 | 64-bit value (default type for untyped variables) |
- Pointer Types:
byte*,int*,long*(pointers to specific types) - Array Types:
int[],byte[], etc. (alternative for pointers) - Function Types: Defined by return type and parameter types
- Optional Typing: Variables can be declared with or without types (default is
long) - Implicit Conversions: All types can be implicitly converted to any other type
- Pointer Arithmetic: Direct memory access with pointer operations
- Array Syntax: Arrays are just a different syntax for pointer arithmetic
- Variables: Declare and use variables with automatic stack management
- Functions: Define functions with parameters and return values
- Control Flow: Use
if-elsestatements andwhileloops - Expressions: Write complex expressions with arithmetic and logical operators
- Unlimited Arguments: Function calls can have any number of arguments (first 7 in registers, rest on stack)
- Dereferencing: Access memory locations using the
@operator - Inline Assembly: Embed raw assembly code within TriC programs
var x = 10 ; Untyped (defaults to long)
var y: int = 20 ; Typed as int
var z: byte = 30 ; Typed as byte
var sum = x + y ; Implicit conversion between types
var raw = malloc(16) ; Raw pointer (address stored as long)
int@(raw + 1) = 0xAAAA ; Store int at offset 1
var value = int@(raw + 1); Load int from offset 1
var p: byte* = malloc(4); Typed pointer
@p = 0xFF ; Store byte
p[1] = 0xAA ; Store next byte (equivalent to byte@(p + 1))
var b = p[1] ; Load byte
var arr: int[] = malloc(32) ; Array of ints (same as int*)
@arr = [1, 2, 3, 4] ; Store array literal
arr[2] = 0xDE ; Store 3rd element (equivalent to int@(arr + 8))
var y = @arr ; Load first element (equivalent to int@arr)
arr[4] = y ; Store 5th element
global g: byte = 67 ; Global variable
func main() {
var b = 1
var i: int = 42
var l = -1
var r = b + g ; Implicit conversion from byte to long
r = add1(r)
return r
}
func add1(a) {
var r = a + 1 ; All operations work with implicit conversions
return r
}
func add(a: int, b: int) {
return a + b ; Implicit conversion to return type (long by default)
}
if (x > y) {
z = x - y
} else {
z = y - x
}
var i = 0
while (i < 10) {
i = i + 1
}
TriC's type system has several important characteristics to understand:
-
Implicit Conversions: All types can be implicitly converted to any other type
var b: byte = 10 var i = b ; b is implicitly converted to long var b2: byte = i ; i is implicitly converted to byte (potential data loss) -
Array Types Are Pointers: Array types are purely syntactic sugar
var arr: int[] = malloc(32) ; This is equivalent to: var arr: int* = malloc(32) -
No Bounds Checking: Array indexing has no bounds checking
var arr: byte[] = malloc(4) arr[100] = 0xAA ; This will write to memory beyond the allocated region -
No Pointer Safety: Any pointer can be dereferenced as any type
var p: int* = malloc(8) p[0] = 42 byte@p = 0xAA ; This is allowed, even though p was declared as int* -
Type Annotations Affect Memory Access: Types determine the size of memory operations
var p = malloc(8) int@p = 0x12345678 ; 4-byte store long@p = 0x12345678 ; 8-byte store (overwrites more memory) -
Untyped Variables Default to Long: Variables without type annotations are treated as
longvar x = 10 ; x is a long (64-bit)
The Tri-C compiler translates TriC source code into Triton-64 assembly code. It consists of several components:
- Lexer: Breaks the source code into tokens
- Parser: Analyzes the tokens to build an Abstract Syntax Tree (AST)
- Code Generator: Traverses the AST to generate assembly code
Key aspects of the compiler:
- Type Handling: Uses type annotations to determine memory access sizes, but doesn't enforce type safety
- Implicit Conversions: Generates code for all implicit type conversions
- Register Management: Efficient allocation of temporary registers
- Stack Management: Handles stack frames for functions and local variables
- Function Calling: Supports unlimited function arguments (first 7 in registers, rest on stack)
The compiler is designed to be flexible and permissive, allowing the programmer low-level control at the expense of safety.
TriC provides memory management through:
- Heap Allocation:
malloc(size)returns a pointer to newly allocated memory - Stack Allocation: Local variables are automatically allocated on the stack
- Type-aware Memory Access: Type annotations determine the size of memory operations
The compiler handles stack frame management, including:
- Variable storage on the stack
- Parameter passing for function calls
- Return value handling
TriC programs are compiled to assembly code, which can then be assembled and run on the Triton-64 VM. The compiler ensures that the generated code adheres to the VM's architecture.
When writing TriC programs, developers should be aware of:
- Untyped variables default to
long(64-bit) - Type annotations affect memory access sizes but don't enforce safety
- Arrays are just pointers with different syntax
- Function calls can have unlimited arguments
- All type conversions happen implicitly
- No array bounds checking
- No pointer safety
While developing TriC programs, you can leverage the VM's debugging tools:
- CPU Viewer: Monitor register states and program counter
- Memory Viewer: Inspect memory contents
- Type Information: View how types affect memory operations
The debugging tools help identify issues that might arise from implicit conversions or unsafe memory operations.
Below is a complete TriC program that demonstrates the language's features:
; Triton-C Feature Showcase
; Demonstrates variables, functions, control flow, and memory operations
global counter = 0
global framebuffer = 0x20220000
; Main program
func main() {
; Variable declarations with and without types
var x = 5
var y: int = 10
var z = 0
; If-else demonstration
if (x < y) {
z = y - x
} else {
z = x - y
}
; While loop demonstration
var i = 0
while (i < 5) {
i = i + 1
}
; Function calls
var fact = factorial(5) ; 120
; Memory operations
var raw = malloc(16) ; raw pointer
int@(raw + 1) = 0xAAAA ; store int at offset 1
var value = int@(raw + 1); load int from offset 1
var p: byte* = malloc(4); typed pointer
@p = 0xFF ; store byte
p[1] = 0xAA ; store next byte
var b = p[1] ; load byte
@p = "abc" ; store string literal (byte array)
var arr: int[] = malloc(32) ; array of ints
@arr = [1, 2, 3, 4] ; store array literal
arr[2] = 0xDE ; store 3rd element
var first = @arr ; load first element
arr[4] = first ; store 5th element
; Framebuffer manipulation
var fb: byte* = framebuffer
var j = 0
while (j < 100) {
fb[j] = counter ; store byte in framebuffer
counter = counter + 1
j = j + 1
}
; Return the total of all operations
return z + fact + first
}
; Function with return value
func factorial(n) {
if (n <= 1) {
return 1
} else {
return n * factorial(n - 1)
}
}
This program showcases variable typing (or lack thereof), pointer operations, array syntax, global variables, and unlimited function arguments.
The assembler performs sophisticated two-pass assembly:
- First Pass: Symbol resolution and size calculation
- Pseudo-instruction Expansion: Converts macros to native instructions
- Second Pass: Machine code generation
Key features:
- Symbol Table: Global label resolution
- Macro System: Powerful pseudo-instruction expansion
The Tri-C compiler translates TriC source code into Triton-64 assembly. It follows a compilation pipeline:
- Linking: (
Linker)**: Combines multiple assembly files into a single executable - Lexical Analysis (
Lexer): Converts source code into tokens - Parsing (
Parser): Constructs an Abstract Syntax Tree (AST) - Code Generation (
CodeGenerator): Walks the AST to emit assembly code
Key features of the compiler:
- Optional Type Handling: Uses type annotations for memory access sizing
- Implicit Conversions: Handles all type conversions automatically
- Register Management: Efficient allocation of temporary registers
- Stack Management: Handles stack frames for functions
- Unlimited Arguments: Supports any number of function arguments
- Memory Operations: Generates appropriate load/store instructions based on types
Sophisticated memory management with:
- Region Protection: ROM write protection
- Address Validation: Bounds checking for all accesses
- Endianness Handling: Little-endian byte ordering
- MMIO Support: Memory-mapped I/O simulation
The CPU core provides:
- Instruction Execution: Full ISA implementation
- Register Management: 32 64-bit general-purpose registers
- Control Flow: Program counter management with jumps
The system includes JavaFX-based debugging tools:
- Real-time register display
- Program counter tracking
- Execution status monitoring
- Memory region visualization
- Hexadecimal dump display
- Address-based navigation
- Real-time memory updates
- Visualizes the framebuffer as text characters
- Visualizes the framebuffer as pixels
The ROM system allows bootable code development:
- Write assembly code in
rom.asm - Run
RomDumperto generateROMData.java - Boot ROM code executes automatically on VM startup
main:
LDI r0, 42 ; Load immediate value
LDI r1, 58 ; Load another value
ADD r2, r0, r1 ; Add them together
HLT ; Halt executionmain:
LDI r0, 0x123456789ABCDEF ; 64-bit immediate load
PUSH r0, r1, r2 ; Save registers
; ... some computation ...
POP r0, r1, r2 ; Restore registers
JMP return_label ; Jump to label
return_label:
HLTmain:
LDI sp, 0x20000000 ; Initialize stack pointer
; Push values onto stack
LDI r0, 100
PUSH r0
; Call function
JAL function_addr, ra
; Pop result
POP r1
HLT
function_addr:
; Function implementation
; Return address is in ra
JMP ra- Intelligent Macro Expansion: Pseudo-instructions expand to optimal native instruction sequences
- Temporary Register Management: Automatic use of temps for complex operations
- Symbol Resolution: Forward and backward label references
- Optional Typing: Types are hints for memory access sizes
- Implicit Conversions: All types can be implicitly converted
- Array-Pointer Equivalence: Array types are syntactic sugar for pointers
- Unlimited Function Arguments: First 7 arguments in registers, rest on stack
- No Safety Checks: No array bounds checking or pointer safety
- Real-time Visualization: Live updates of CPU and memory state
- Memory Inspection: Detailed memory region analysis
Instructions use a 32-bit fixed-width format:
31 27 26 22 21 17 16 12 11 7 6 0
├──────┼────────┼────────┼────────┼────────┼────────┤
│ IMM │ SRC2 │ SRC │ DEST │ OPCODE │
│(10b) │ (5b) │ (5b) │ (5b) │ (7b) │
└──────┴────────┴────────┴────────┴────────┴────────┘
- OPCODE: 7-bit instruction identifier
- DEST: 5-bit destination register
- SRC: 5-bit source register 1
- SRC2: 5-bit source register 2
- IMM: 10-bit signed immediate value
The VM supports type-specific memory operations:
- Byte Access:
LB,SBfor 8-bit operations - Int Access:
LI,SIfor 32-bit operations - Long Access:
LD,STfor 64-bit operations
These instructions ensure proper memory alignment and access sizes based on the type annotations in TriC code.
This project demonstrates advanced virtual machine architecture concepts including:
- Custom instruction set design
- Multi-pass assembler implementation
- High-level language compilation with optional typing
- Memory management systems
- Visual debugging interfaces
- ROM-based system initialization
The Triton-64 VM serves as both an educational tool and a practical platform for systems programming experimentation, providing low-level control with the convenience of a high-level language syntax. The optional type system offers flexibility for both high-level abstractions and low-level memory manipulation.