A minimal custom Instruction Set Architecture (ISA) emulator written in Go. This project is designed for educational purposes and architecture research, providing a simple yet complete CPU emulator with registers, memory, instruction decoding, and step-by-step execution.
- 16 General-Purpose Registers: 32-bit integer registers (R0-R15)
- 64KB Memory: Addressable byte-level memory
- Complete Instruction Set: Arithmetic, memory, and control flow operations
- Step-by-Step Execution: Execute instructions one at a time or run complete programs
- Debug Mode: Trace instruction execution with human-readable output
- Educational: Clean, well-documented code suitable for learning CPU architecture
The ISA uses fixed 4-byte instructions with the following format:
[OpCode (1 byte)] [Arg1 (1 byte)] [Arg2 (1 byte)] [Arg3 (1 byte)]
| Opcode | Instruction | Description | Example |
|---|---|---|---|
| 0x01 | ADD | R1 = R2 + R3 | ADD R0, R1, R2 |
| 0x02 | SUB | R1 = R2 - R3 | SUB R0, R1, R2 |
| 0x03 | MUL | R1 = R2 * R3 | MUL R0, R1, R2 |
| 0x04 | DIV | R1 = R2 / R3 | DIV R0, R1, R2 |
| Opcode | Instruction | Description | Example |
|---|---|---|---|
| 0x10 | LOAD | Load from memory | LOAD R0, 0x1000 |
| 0x11 | STORE | Store to memory | STORE R0, 0x1000 |
| 0x12 | LOADI | Load immediate value | LOADI R0, 42 |
| Opcode | Instruction | Description | Example |
|---|---|---|---|
| 0x20 | JMP | Unconditional jump | JMP 0x0010 |
| 0x21 | JZ | Jump if zero | JZ R0, 0x0010 |
| 0xFF | HALT | Stop execution | HALT |
# Clone the repository
git clone https://github.com/BaseMax/go-micro-isa.git
cd go-micro-isa
# Build the emulator
go build
# Run the example program
./go-micro-isaThe included example calculates the factorial of 5 (5! = 120):
go run .Output:
=== Go Micro ISA Emulator ===
Example Program: Calculate 5!
Execution trace:
--------------------------------------------------
PC: 0x0000 | LOADI R0, 5
PC: 0x0004 | LOADI R1, 1
PC: 0x0008 | LOADI R2, 1
PC: 0x000C | MUL R1, R1, R0
PC: 0x0010 | SUB R0, R0, R2
PC: 0x0014 | JZ R0, 0x001C
PC: 0x000C | MUL R1, R1, R0
...
--------------------------------------------------
Execution completed successfully!
Register Dump:
R00: 0 R01: 120 R02: 1 R03: 0
...
Result stored at memory[0x1000]: 120
Expected: 120 (5! = 5 × 4 × 3 × 2 × 1)
package main
func main() {
// Create a new CPU
cpu := NewCPU()
cpu.Debug = true // Enable instruction tracing
// Write a program (example: add two numbers)
program := []byte{
byte(OpLOADI), 0, 0x00, 0x0A, // LOADI R0, 10
byte(OpLOADI), 1, 0x00, 0x14, // LOADI R1, 20
byte(OpADD), 2, 0, 1, // ADD R2, R0, R1
byte(OpHALT), 0, 0, 0, // HALT
}
// Load and run the program
cpu.LoadProgram(program)
cpu.Run()
// Check results
cpu.DumpRegisters()
fmt.Printf("Result: %d\n", cpu.Registers[2]) // Should be 30
}cpu := NewCPU()
cpu.LoadProgram(program)
// Execute one instruction at a time
for !cpu.Halted {
err := cpu.Step()
if err != nil {
fmt.Printf("Error: %v\n", err)
break
}
cpu.DumpRegisters()
}Run the comprehensive test suite:
# Run all tests
go test
# Run tests with verbose output
go test -v
# Run tests with coverage
go test -coverCalculate n! using a loop:
LOADI R0, 5 ; Number to calculate factorial of
LOADI R1, 1 ; Result accumulator
LOADI R2, 1 ; Decrement value
loop:
MUL R1, R1, R0 ; result *= n
SUB R0, R0, R2 ; n--
JZ R0, end ; if n == 0, exit loop
JMP loop ; repeat
end:
HALT
LOADI R0, 10 ; Load 10 into R0
LOADI R1, 20 ; Load 20 into R1
ADD R2, R0, R1 ; R2 = R0 + R1 = 30
HALT
type CPU struct {
Registers [16]int32 // 16 general-purpose registers
PC uint16 // Program counter
Memory [65536]byte // 64KB of memory
Halted bool // CPU halt flag
Debug bool // Debug mode flag
}NewCPU(): Create a new CPU instanceLoadProgram(program []byte): Load a program into memoryStep(): Execute a single instructionRun(): Execute until HALT or errorReset(): Reset CPU to initial stateDumpRegisters(): Print register state
- Register Size: 32-bit signed integers
- Memory Size: 64KB (65536 bytes)
- Instruction Size: 4 bytes (fixed length)
- Endianness: Little-endian for memory operations
- Program Counter: 16-bit, byte-addressable
This emulator is ideal for:
- Learning CPU Architecture: Understand how instructions are fetched, decoded, and executed
- Compiler Development: Target for simple compiler backends
- Algorithm Visualization: See how algorithms execute at the instruction level
- Computer Architecture Courses: Teaching fundamental concepts
- ISA Design Research: Experiment with custom instruction sets
MIT License - see LICENSE file for details
Contributions are welcome! Feel free to submit issues or pull requests.
Max Base