#emulation #8-bit #vm #rust

bin+lib e8bit_emulator

8-bit emulator written in Rust

18 releases

Uses new Rust 2024

new 0.2.7 May 14, 2025
0.2.6 May 13, 2025
0.1.9 May 9, 2025

#16 in Emulators

Download history 2444/week @ 2025-05-07

2,444 downloads per month

Custom license

37KB
604 lines

e8Bit Emulator

Crates.io Project GitHub mi66mc

This project is a simple 8-bit virtual machine (VM) emulator written in Rust. It simulates a basic CPU with registers, memory, and a set of instructions for performing arithmetic, memory operations, and control flow.

Features

  • Registers: Five general-purpose 8-bit registers (A, B, C, D, E).
  • Memory: 256 bytes of memory.
  • Instruction Set:
    • Arithmetic: ADD, SUB, MUL, DIV, MULH
    • Data Movement: MOV, STORE
    • Memory Access: Supports [0], [A], [B], etc.
    • Control Flow: JMP, JZ, JNZ, LOOP
    • Input/Output: INPUT, PRINT, PRINTCH
    • Program Termination: HALT
    • Screen Operations: DRAW, CLS, RENDER
    • Comparison: CMP
  • Zero Flag: Indicates whether the result of the last operation is zero, often used for conditional branching or logical evaluations. Comparisons evaluate to false (non-zero) or true (zero), enabling conditional logic.
  • Custom Parsing: Accepts comments (//) and instruction separation via ; or by lines.
  • Character Literals: Supports character literals in instructions, e.g., MOV A 'p'. Characters are internally treated as their ASCII numeric values and must fit within 8 bits (0–255), just like any other number.
  • Debug Mode: Optional debug mode for detailed output during execution.
  • IDLE Mode: Allows direct input of instructions for testing and debugging.
  • Screen Rendering: Supports drawing characters on an 80 by 25 virtual screen and rendering it to the console.

Example Program

The files example.e8, example2.e8, example3.e8, example4.e8, example5.e8, and example6.e8 contain examples of programs that demonstrate the use of registers, arithmetic operations, memory storage, and loops.

// FACTORIAL SCRIPT

// registers
MOV A 1;           // A = 1 (result)
MOV B 5;           // B = 5 (number to get factorial)

// loop starts here (index 2)
MUL A B;           // A *= B
SUB B 1;           // B -= 1
LOOP 2 B;           // if B != 0 go to index 2 (MUL A B)

// END
PRINT A;           // shows result
HALT;
// WELCOME SCRIPT

MOV C 0;

INPUT A;

STORE A [C];
ADD C 1;
INPUT A;
JNZ 2;          // If nothing in the input continue, else go back

// Welcome message

MOV D 'H';       // H
PRINTCH D -N;
MOV D 'e';      // e
PRINTCH D -N;
MOV D 'l';      // l
PRINTCH D -N;
PRINTCH D -N;
MOV D 111;      // o
PRINTCH D -N;
MOV D ',';       // ,
PRINTCH D -N;
MOV D 32;       //  
PRINTCH D -N;

// Hello, 

MOV D 0;
MOV A [D];
PRINTCH A -N;   // No line break
ADD D 1;
MOV B C;
SUB B D;
JNZ 20;

// Hello, (name)

HALT
// "16" bits factorial

MOV A 0        // A = 0 (high byte)
MOV B 1        // B = 1 (low byte)
MOV C 6        // C = 6 (or 2, 5, etc.)
MOV D B        // D = B
MUL D C        // D = B * C (low 8 bits)
STORE D [0]    // Store low byte
MULH D B C     // D = B * C (high 8 bits)
STORE D [1]    // Store high byte
MOV D A        // D = A
MUL D C        // D = A * C (low 8 bits)
ADD D [1]      // D = (A * C) + high byte of B * C
MOV A D        // A = new high byte
MOV B [0]      // B = new low byte
SUB C 1        // C--
LOOP 3 C       // Loop to instruction 3 if C != 0


// if 6 (just examples to know how to calculate)
PRINT A        // 2
PRINT B        // 208
// 256 * 2 = 512
// 512 + 208 = 720
// 6! = 720


HALT
// DRAWING A "BOX"

CLS;               // Clear the screen
MOV A 5;           // X = 5
MOV B 5;           // Y = 5
MOV C '#';         // Char = '#'
DRAW A B C;        // Draw '#' at (5, 5)

MOV A 10;          // X = 10
DRAW A B C;        // Draw '#' at (10, 5)

MOV B 10;          // Y = 10
DRAW A B C;        // Draw '#' at (10, 10)

MOV A 5;           // X = 5
DRAW A B C;        // Draw '#' at (5, 10)

RENDER;            // Render the screen
HALT;

How to Run

  1. Install Rust: Ensure you have Rust installed. You can download it from rust-lang.org.

  2. Compile the Program:

    cargo build --release
    
  3. Run the Emulator:

    cargo run example.e8 -d
    

    Replace example.e8 with the path to your program file.

    Or if no file is specified, it will run IDLE mode, where you can enter directly the instructions.

    -d flag is optional and enables debug mode, which provides additional output for debugging purposes.

How to Write Programs

Programs for the emulator are written in a custom assembly-like language. Each instruction is written on a new line or semicolon and can include comments starting with //. Refer to the example program above for syntax.

Instruction Set

Instruction Description
MOV A 10 Move value 10 into register A
MOV A 'p' Move character literal 'p' into register A (treated as its ASCII value)
MOV A [0] Move value from memory address 0 into A
MOV A [B] Move value from memory at index stored in B
ADD A B A = A + B
SUB A 1 A = A - 1
MUL A 2 A = A * 2
DIV A 2 A = A / 2
MULH A B C A = high byte of (B * C)
STORE A [0] Store A into memory[0]
STORE A [B] Store A into memory at index in B
INPUT A Read input (u8 or char) into register A
JMP 10 Jump to instruction index 10
JZ 5 Jump to index 5 if last result was 0
JNZ 8 Jump if last result was not zero
LOOP 3 C Jump to index 3 while C != 0
PRINT A Print value of A with newline
PRINT A -N Print value of A without newline
PRINTCH A Print character represented by value in A
PRINTCH A -N Print character without newline
DRAW X Y C Draw character C at screen position (X, Y)
CLS Clear the screen
RENDER Render the screen to the console screen is 80 by 25
SLP 1000 Pause execution for 1 second
HALT Stops program execution
CMP A 10 Compare register A with value 10. Sets the zero flag if equal.

Args Types

Type Example Description
Register A, B, C, D, E One of the five registers
Immediate Value 42, 'p' A literal number or character between 0–255
Memory Address [0] Direct access to memory index 0
Memory via Reg [A] Access memory using value in register A

Note: Square brackets ([]) are used to specify memory addresses. For example:

  • MOV A [0] loads the value from memory address 0 into register A.
  • STORE A [0] stores the value of register A into memory address 0.

Args Types Supported

Instruction Arg 1 Type Arg 2 Type Arg 3 Type
MOV Register Immediate Value, Register, or Memory Address ([0], [A], etc.) -
ADD Register Immediate Value, Register, or Memory Address -
SUB Register Immediate Value, Register, or Memory Address -
MUL Register Immediate Value, Register, or Memory Address -
DIV Register Immediate Value, Register, or Memory Address -
MULH Register Register Register
STORE Register Memory Address ([0], [B], etc.) -
JMP Immediate Value - -
JZ Immediate Value - -
JNZ Immediate Value - -
LOOP Immediate Value (Instruction Index) Register -
PRINT Register Optional: -N to suppress newline -
PRINTCH Register Optional: -N to suppress newline -
INPUT Register - -
DRAW Immediate Value, Register, or Memory Address Immediate Value, Register, or Memory Address Immediate Value, Register, or Memory Address
CLS - - -
RENDER - - -
SLP Milliseconds - -
HALT - - -
CMP Register Immediate Value, Register, or Memory Address -

Future Improvements

  • Add support for more instructions.
  • Implement debugging tools.
  • Enhance error handling for invalid programs.

License

This project is open-source and available under the MIT License.

No runtime deps