Skip to content

Instantly share code, notes, and snippets.

@Frontrider
Forked from unnikked/CPU.java
Created October 30, 2018 17:30
Show Gist options
  • Save Frontrider/a48b651b86bb278a4191c8016bc2a001 to your computer and use it in GitHub Desktop.
Save Frontrider/a48b651b86bb278a4191c8016bc2a001 to your computer and use it in GitHub Desktop.
Tiny RISC Virtual Machine - Read README first!

#A RISC Virtual Machine written in Java

I wrote this tiny virtual machine for fun in a couple of hour. It's use is very straightforward for who is familiar with a simple assembly code (like RASP machines).

It's RAM is an array of String, so it should be very easy to dump the memory and explore it. I choose Strings because it was easier to code and the RAM looks more human friendly.

I have not designed it to be the strongest one on this planed, I just wanted to build it for fun and to let someone else understand how logically a computer works.

##Commands

###I/O commands

  • READ read a numerical value (double) from the input and move it to the accumulator
  • PRINT print the content of the accumulator
  • ECHO print a given string (for user interaction)

###Memory I/O

  • LOAD load the content of the ram address into the accumulator
  • LOAD# load the numerical value into the accumulator
  • LOAD@ dereferencing operator
  • STORE store the content of the accumulator to the given address ram

###Arithmetic operators

  • ADD adds to the current value of the accumulator the content of address ram specified
  • ADD# adds directly to the accumulator the numerical value given
  • SUB subtracts to the current value of the accumulator the content of address ram specified
  • SUB#subtracts to the accumulator the numerical value given
  • MUL multiplies the accumulator with the content of the given address ram
  • DIV performs a division between the accumulator (numerator) and the content of the given address ram (denominator)
  • MOD performs modulo between the accumulator (numerator) and the content of the given address ram (denominator)
  • POW performs the power of the accumulator (base) and the content of the given address ram (exponent)

###Bitwise operators

  • SHL bitwise left shift (n as parameter) of the accumulator
  • SHR bitwise right shift (n) of the accumulator
  • AND performs bitwise and between the accumulator and the content of the given address ram
  • OR performs bitwise or between the accumulator and the content of the given address ram
  • NOT performs bitwise not of the accumulator
  • XOR performs bitwise xor between the accumulator and the content of the given address ram

###Flow Control

  • JUMP unconditional jump
  • JZ jump if zero
  • JNZ jump if not zero
  • JGZ jump if greater than zero
  • JGEZ jump if greater of equals to zero
  • JLZ jump if less than zero
  • JLZE jump if less or equals to zero
  • HALT halts the machine (required at least at the end of every program)

Assembly

Unfortunately you must specify the address of the ram manually, I have not implemented a variable system at the moment.

Example

Here it is an example of a program that calculates the factorial of the given number:

ECHO "Factorial of"
READ
STORE 100
LOAD# 1 
STORE 101
LOAD 100 
JZ 14
LOAD 101
MUL 100
STORE 101
LOAD 100
SUB# 1
STORE 100
JUMP 5
LOAD 101
PRINT
HALT

I bet you to do better and more interesting programs :)

output

> java Loader factorial.asm 
Factorial of
5
120.0

sample ram dump

0.	ECHO "Factorial of"
1.	READ
2.	STORE 100
3.	LOAD# 1 
4.	STORE 101
5.	LOAD 100 
6.	JZ 14
7.	LOAD 101
8.	MUL 100
9.	STORE 101
10.	LOAD 100
11.	SUB# 1
12.	STORE 100
13.	JUMP 5
14.	LOAD 101
15.	PRINT
16.	HALT
100.	0.0
101.	120.0
import java.util.HashMap;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CPU {
public String[] ram;
private HashMap<String, Instruction> rom;
private double accumulator;
private int instructionPointer;
public CPU () {
rom = new HashMap<String, Instruction>();
rom.put("READ", new Read());
rom.put("LOAD", new Load());
rom.put("LOAD#", new LoadDirect());
rom.put("LOAD@", new LoadIndirect());
rom.put("STORE", new Store());
rom.put("ADD", new Add());
rom.put("ADD#", new AddDirect());
rom.put("SUB", new Sub());
rom.put("SUB#", new SubDirect());
rom.put("MUL", new Mul());
rom.put("DIV", new Div());
rom.put("MOD", new Mod());
rom.put("POW", new Pow());
rom.put("SHL", new ShiftLeft());
rom.put("SHR", new ShiftRight());
rom.put("AND", new And());
rom.put("OR", new Or());
rom.put("NOT", new Not());
rom.put("XOR", new Xor());
rom.put("JZ", new JumpIfZero());
rom.put("JNZ", new JumpIfNotZero());
rom.put("JUMP", new Jump());
rom.put("JGZ", new JumpIfGreaterThanZero());
rom.put("JGEZ", new JumpIfGreaterOrEqualsThanZero());
rom.put("JLZ", new JumpIfLessThanZero());
rom.put("JLEZ", new JumpIfLessOrEqualsThanZero());
rom.put("PRINT", new Print());
rom.put("ECHO", new Echo());
rom.put("HALT", new Halt());
}
public void start() {
while(instructionPointer >= 0) {
fetch(ram[instructionPointer]);
}
}
public void load(String[] ram) {
this.ram = ram;
}
public void dump() {
for (int i = 0; i < ram.length; i++)
if (ram[i] != null)
System.out.println(String.format("%d.\t%s", i, ram[i]));
}
public void fetch(String instruction) {
try {
Pattern pattern = Pattern.compile("([^\\s]+)");
Matcher match = pattern.matcher(instruction);
String executor = nextToken(match).toUpperCase();
if (rom.containsKey(executor)) {
Instruction cmd = rom.get(executor);
cmd.execute(match);
} else {
throw new RuntimeException("Unknow Instruction");
}
}
catch (Exception e) {
System.out.println("Address: " + instructionPointer + "\tInterrupt: ");
e.printStackTrace();
fetch("HALT");
}
}
private String nextToken(Matcher match) {
if (!match.find()) throw new RuntimeException("Command malformed");
return match.group();
}
private class Load implements Instruction {
@Override
public void execute(Matcher matcher) {
accumulator = Double.parseDouble(ram[(int)Double.parseDouble(nextToken(matcher))]);
instructionPointer++;
}
}
private class LoadDirect implements Instruction {
@Override
public void execute(Matcher matcher) {
accumulator = Double.parseDouble(nextToken(matcher));
instructionPointer++;
}
}
private class LoadIndirect implements Instruction {
public void execute(Matcher matcher) {
accumulator = Double.parseDouble(ram[(int)Double.parseDouble(ram[(int)Double.parseDouble(nextToken(matcher))])]);
instructionPointer++;
}
}
private class Store implements Instruction {
@Override
public void execute(Matcher matcher) {
ram[(int)Double.parseDouble(nextToken(matcher))] = Double.toString(accumulator);
instructionPointer++;
}
}
private class Add implements Instruction {
@Override
public void execute(Matcher matcher) {
accumulator += Double.parseDouble(ram[(int)Double.parseDouble(nextToken(matcher))]);
instructionPointer++;
}
}
private class AddDirect implements Instruction {
@Override
public void execute(Matcher matcher) {
accumulator += Double.parseDouble(nextToken(matcher));
instructionPointer++;
}
}
private class JumpIfZero implements Instruction {
@Override
public void execute(Matcher matcher) {
if(accumulator == 0) {
instructionPointer = (int)Double.parseDouble(nextToken(matcher));
return;
}
instructionPointer++;
}
}
private class JumpIfNotZero implements Instruction {
@Override
public void execute(Matcher matcher) {
if(accumulator != 0) {
instructionPointer = (int)Double.parseDouble(nextToken(matcher));
return;
}
instructionPointer++;
}
}
private class Jump implements Instruction {
@Override
public void execute(Matcher matcher) {
instructionPointer = (int)Double.parseDouble(nextToken(matcher));
}
}
private class Print implements Instruction {
@Override
public void execute(Matcher matcher) {
System.out.println(accumulator);
instructionPointer++;
}
}
private class Halt implements Instruction {
@Override
public void execute(Matcher matcher) {
instructionPointer = -1;
}
}
private class JumpIfGreaterThanZero implements Instruction {
@Override
public void execute(Matcher matcher) {
if(accumulator > 0) {
instructionPointer = (int)Double.parseDouble(nextToken(matcher));
return;
}
instructionPointer++;
}
}
private class JumpIfGreaterOrEqualsThanZero implements Instruction {
@Override
public void execute(Matcher matcher) {
if(accumulator >= 0) {
instructionPointer = (int)Double.parseDouble(nextToken(matcher));
return;
}
instructionPointer++;
}
}
private class JumpIfLessThanZero implements Instruction {
@Override
public void execute(Matcher matcher) {
if(accumulator < 0) {
instructionPointer = (int)Double.parseDouble(nextToken(matcher));
return;
}
instructionPointer++;
}
}
private class JumpIfLessOrEqualsThanZero implements Instruction {
@Override
public void execute(Matcher matcher) {
if(accumulator <= 0) {
instructionPointer = (int)Double.parseDouble(nextToken(matcher));
return;
}
instructionPointer++;
}
}
private class Sub implements Instruction {
@Override
public void execute(Matcher matcher) {
accumulator -= Double.parseDouble(ram[(int)Double.parseDouble(nextToken(matcher))]);
instructionPointer++;
}
}
private class SubDirect implements Instruction {
@Override
public void execute(Matcher matcher) {
accumulator -= Double.parseDouble(nextToken(matcher));
instructionPointer++;
}
}
private class Mul implements Instruction {
@Override
public void execute(Matcher matcher) {
accumulator *= Double.parseDouble(ram[(int)Double.parseDouble(nextToken(matcher))]);
instructionPointer++;
}
}
private class Div implements Instruction {
@Override
public void execute(Matcher matcher) {
double d = Double.parseDouble(ram[(int)Double.parseDouble(nextToken(matcher))]);
if(d == 0) throw new RuntimeException("Division by Zero");
accumulator /= d;
instructionPointer++;
}
}
private class Mod implements Instruction {
@Override
public void execute(Matcher matcher) {
accumulator %= Double.parseDouble(ram[(int)Double.parseDouble(nextToken(matcher))]);
instructionPointer++;
}
}
/* base = accumulator; exponent = param */
private class Pow implements Instruction {
@Override
public void execute(Matcher matcher) {
accumulator = Math.pow(accumulator, Double.parseDouble(ram[(int)Double.parseDouble(nextToken(matcher))]));
instructionPointer++;
}
}
private class ShiftLeft implements Instruction {
@Override
public void execute(Matcher matcher) {
accumulator = (int) accumulator << Integer.parseInt(ram[(int)Double.parseDouble(nextToken(matcher))]);
instructionPointer++;
}
}
private class ShiftRight implements Instruction {
@Override
public void execute(Matcher matcher) {
accumulator = (int) accumulator >> Integer.parseInt(ram[(int)Double.parseDouble(nextToken(matcher))]);
instructionPointer++;
}
}
private class And implements Instruction {
@Override
public void execute(Matcher matcher) {
accumulator = (int) accumulator & Integer.parseInt(ram[(int)Double.parseDouble(nextToken(matcher))]);
instructionPointer++;
}
}
private class Or implements Instruction {
@Override
public void execute(Matcher matcher) {
accumulator = (int) accumulator | Integer.parseInt(ram[(int)Double.parseDouble(nextToken(matcher))]);
instructionPointer++;
}
}
private class Not implements Instruction {
@Override
public void execute(Matcher matcher) {
accumulator = ~ (int) accumulator;
instructionPointer++;
}
}
private class Xor implements Instruction {
@Override
public void execute(Matcher matcher) {
accumulator = (int) accumulator ^ Integer.parseInt(ram[(int)Double.parseDouble(nextToken(matcher))]);
instructionPointer++;
}
}
private class Read implements Instruction {
@Override
public void execute(Matcher matcher) {
Scanner sc = new Scanner(System.in);
accumulator = sc.nextDouble();
instructionPointer++;
}
}
private class Echo implements Instruction {
@Override
public void execute(Matcher matcher) {
String message = "";
while (matcher.find()) {
message += matcher.group();
message += " ";
}
if(message.charAt(0) == '"' && message.charAt(message.length()-2) == '"') {
System.out.println(message.substring(1, message.length()-2));
instructionPointer++;
return;
}
throw new IllegalArgumentException("Instruction Malformed");
}
}
}
import java.util.regex.Matcher;
public interface Instruction {
public void execute(Matcher matcher);
}
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class Loader {
public String[] load(String fileName) {
FileInputStream fis = null;
BufferedReader reader = null;
int count = 0;
String[] ram = new String[1024];
try {
fis = new FileInputStream(fileName);
reader = new BufferedReader(new InputStreamReader(fis));
String line = reader.readLine();
while(line != null){
ram[count++] = line;
line = reader.readLine();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
reader.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return ram;
}
public static void main(String[] args) {
if(args[0] == null) {
System.out.println("USAGE: java Loader programfile");
}
Loader cpuExecutor = new Loader();
CPU cpu = new CPU();
cpu.load(cpuExecutor.load(args[0]));
cpu.start();
//cpu.dump();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment