https://github.com/mistersaturn/abacus-c
Simple LMC Virtual Machine clone written in C
https://github.com/mistersaturn/abacus-c
assembler assembly-language-programming c calculator-application cpu-emulation educational-software littlemancomputer lmc low-level-programming virtual-machine
Last synced: about 2 months ago
JSON representation
Simple LMC Virtual Machine clone written in C
- Host: GitHub
- URL: https://github.com/mistersaturn/abacus-c
- Owner: mistersaturn
- License: gpl-2.0
- Created: 2025-04-08T14:56:00.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2025-04-11T08:56:54.000Z (about 2 months ago)
- Last Synced: 2025-04-11T09:56:24.573Z (about 2 months ago)
- Topics: assembler, assembly-language-programming, c, calculator-application, cpu-emulation, educational-software, littlemancomputer, lmc, low-level-programming, virtual-machine
- Language: C
- Homepage:
- Size: 5.86 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.org
- License: LICENSE
Awesome Lists containing this project
README
#+TITLE: Abacus-C Virtual Machine
#+DESCRIPTION: Virtual machine based on Little Man Computer written in C Language
#+AUTHOR: Dean Smith
#+DATE: Tue Apr 8 2025
#+PROPERTY:* Introduction
Abacus-C is a simple virtual machine to teach the fundamentals of how a basic computer works.
It was written in C language due to C's inherent low-level capabilities and also it serves the dual purpose of providing a good example of a program written in C to emulate a basic computer.It is loosely based upon the Little Man Computer concept.
** Example Program
Programs are entered using numeric instruction codes.
A good example of this is the instruction sequence used to add one number to another number:#+BEGIN_EXAMPLE
810
811
410
111
312
912
000
-1
#+END_EXAMPLE*** Step By Step
1. '8' is the opcode for INPUT. '10' is the memory register where the value of INPUT will be stored.
2. Likewise, in the second line, '8' prompts for INPUT again and stores the value in memory register '11'.
3. Then, '4' is called, which is the opcode to LOAD the value stored in memory register '10'.
4. After that, opcode 1 is called, which is the opcode to ADD the value that was just loaded with the value stored in memory register '11'.
5. The sum of that operation is then stored in memory register '12' using the opcode '3' to STORE the value.
6. Finally, the '9' opcode is called to OUTPUT the data stored in memory register '12'.To terminate the virtual machine, a '000' opcode is used to HALT the program and system, and the escape code '-1' exits.
** Opcodes / Operands
*** Opcodes
The opcode (operation code) is the part of the instruction that specifies the operation to be performed. In our program, opcodes are represented by the first two digits of a three-digit instruction. For example, in the instruction 101, the opcode is 1, which corresponds to the ADD operation. Here is a list of the opcodes:#+BEGIN_EXAMPLE
1 (ADD): Adds the value at the specified memory location to the accumulator.
2 (SUB): Subtracts the value at the specified memory location from the accumulator.
3 (STORE): Stores the current value of the accumulator into the specified memory location.
4 (LOAD): Loads a value from the specified memory location into the accumulator.
5 (JUMP): Sets the program counter to a specified location, effectively jumping to that instruction.
6 (JUMP IF ZERO): Jumps to a specified location if the accumulator is zero.
7 (JUMP IF POSITIVE): Jumps to a specified location if the accumulator is positive.
8 (INPUT): Prompts the user to input a number and stores it in the specified memory location.
9 (OUTPUT): Outputs the value stored in the specified memory location.
0 (HALT): Terminates the program.
#+END_EXAMPLEUnderstanding opcodes is crucial for grasping how low-level programming works, as they form the basis of machine-level instructions that the CPU executes.
*** Operands
The operand is the part of the instruction that specifies the data or memory location to be used in the operation. It is represented by the last two digits of the instruction. For instance, in the instruction 101, the operand is 01, which indicates the memory location from which to retrieve the value to be added to the accumulator.**** -- Note:
The usage of Opcodes and Operands is intentional in an attempt to facilitate a deeper understanding of how the handling of the input works. However, Abacus-C can be further extended by adding mnemonic codes and constant variable handling.* Code Example
** Header
*** Includes
#+BEGIN_SRC C
#include
#include
#+END_SRC*** Defines
#+BEGIN_SRC C
#define MEMORY_SIZE 100
#define INSTRUCTION_SIZE 4
#+END_SRC** Initialize Virtual Machine
*** Boot
Zero out the memory, the accumulator, and the program counter before starting the virtual machine here:#+BEGIN_SRC C
int memory[MEMORY_SIZE] = {0}; // Memory array initialized to zero
int accumulator = 0; // Accumulator for arithmetic operations
int program_counter = 0; // Tracks the current instruction
#+END_SRC*** Load
Function to load the program into memory:#+BEGIN_SRC C
void load_program() {
printf("\n\033[96;1;4m-- ABACUS C MACHINE --\n\033[0m");
printf("\nENTER THE PROGRAM \033[93m[END WITH -1] ->\n\n\033[0m");
int instruction;
for (int i = 0; i < MEMORY_SIZE; i++) {
if (scanf("%d", &instruction) != 1) { // Check for valid input
fprintf(stderr, "!!! -- INVALID INPUT. ENTER AN INTEGER.\n");
exit(EXIT_FAILURE);
}
if (instruction == -1) break; // End input on -1
if (i >= MEMORY_SIZE) {
fprintf(stderr, "!!! -- MEMORY OVERFLOW. MAXIMUM INSTRUCTIONS REACHED.\n");
exit(EXIT_FAILURE);
}
memory[i] = instruction; // Store instruction in memory
}
}
#+END_SRC*** Opcode / Instruction Handling
Function to execute a single instruction:#+BEGIN_SRC C
void execute_instruction(int instruction) {
int opcode = instruction / 100; // Extract opcode (first two digits)
int operand = instruction % 100; // Extract operand (last two digits)if (operand < 0 || operand >= MEMORY_SIZE) { // Validate operand
fprintf(stderr, "!!! -- INVALID OPERAND -> %d\n", operand);
exit(EXIT_FAILURE);
}switch (opcode) {
case 1: // ADD
accumulator += memory[operand]; // Add value from memory to accumulator
break;
case 2: // SUB
accumulator -= memory[operand]; // Subtract value from memory from accumulator
break;
case 3: // STORE
memory[operand] = accumulator; // Store accumulator value in memory
break;
case 4: // LOAD
accumulator = memory[operand]; // Load value from memory into accumulator
break;
case 5: // JUMP
program_counter = operand; // Set program counter to operand
return;
case 6: // JUMP IF ZERO
if (accumulator == 0) program_counter = operand; // Jump if accumulator is zero
return;
case 7: // JUMP IF POSITIVE
if (accumulator > 0) program_counter = operand; // Jump if accumulator is positive
return;
case 8: // INPUT
printf("ENTER A NUMBER -> ");
if (scanf("%d", &memory[operand]) != 1) { // Check for valid input
fprintf(stderr, "!!! -- INVALID INPUT. ENTER AN INTEGER.\n");
exit(EXIT_FAILURE);
}
break;
case 9: // OUTPUT
printf("\n\033[92mOUTPUT -> %d\n\n\033[0m", memory[operand]); // Print value from memory
break;
case 0: // HALT
exit(EXIT_SUCCESS); // Terminate the program gracefully
default:
fprintf(stderr, "!!! -- INVALID INSTRUCTION -> %d\n", instruction); // Error handling
exit(EXIT_FAILURE);
}
}
#+END_SRC* Run Abacus-C
Function to run the loaded program:#+BEGIN_SRC C
void run() {
while (1) {
if (program_counter < 0 || program_counter >= MEMORY_SIZE) { // Validate program counter
fprintf(stderr, "!!! -- PROGRAM COUNTER OUT OF BOUNDS -> %d\n", program_counter);
exit(EXIT_FAILURE);
}
int instruction = memory[program_counter]; // Fetch instruction from memory
execute_instruction(instruction); // Execute the fetched instruction
program_counter++; // Move to the next instruction
}
}
#+END_SRC* Main
#+BEGIN_SRC C
int main() {
load_program(); // Load instructions into memory
run(); // Start executing the loaded program
return EXIT_SUCCESS; // Exit the program gracefully
}
#+END_SRC