Skip to content

Instantly share code, notes, and snippets.

@nileshtrivedi
Created October 5, 2011 16:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nileshtrivedi/1264930 to your computer and use it in GitHub Desktop.
Save nileshtrivedi/1264930 to your computer and use it in GitHub Desktop.
A simple virtual machine to experiment with minimal instruction sets
# My attempt to implement a simple virtual machine with a minimal instruction set
# 8-bit computer, fixed-size memory, single cpu, 0-registers, based on von-neumann architecture
# No support for interrupts or I/O although we cheat and provide a "print" instruction :)
# The goal is to find a minimal, but turing-complete, instruction set
# For simplicity, every instruction has two operands, which are located adjacent to it in the memory
# This code is just to experiment with various instruction sets and not to be taken seriously
# Next fun step would be to develop a language/compiler for this virtual machine
class Memory
def initialize(size, program)
puts "initialising memory of #{size} bytes..."
raise "cannot load a program larger than #{size} bytes" if program.size > size
@size = size
@bytes = program # TODO: we should check here that program is an array of 8-bit integers only
end
def read(address)
raise "attempt to read beyond address #{(@size -1)}: #{address}" if address >= @size
@bytes[address]
end
def write(address, value)
raise "attempt to write beyond address #{(@size -1)}: (#{address},#{value})" if address >= @size
raise "attempt to write a value bigger than 255: (#{address},#{value}" if value > 255
raise "attempt to write a value lesser than 0: (#{address},#{value}" if value < 0
@bytes[address] = value
end
end
class Computer
def initialize(mem_size, program)
# program is loaded into memory upon booting
@memory = Memory.new(mem_size, program)
puts "booting the os..."
execute(0) # start executing instructions from the first byte in the memory
end
def execute(address)
i = @memory.read(address)
# pre-fetch the operands even though one or both may be unnecessary. Scope for optimization here.
o1 = @memory.read(address + 1)
o2 = @memory.read(address + 2)
case i # this is where we implement our instruction set
when 0:
# conditional print - print value at address o1 (in decimal) if value at address o2 is non-zero
flag = @memory.read(o2)
if flag != 0
v = @memory.read(o1)
puts v.to_s
end
when 1:
# write - value at address o2 is written to address o1: i.e. copy.
v = @memory.read(o2)
@memory.write(o1, v)
when 2:
# add - value at address o2 is added into value at address o1
v = @memory.read(o2)
orig = @memory.read(o1)
@memory.write(o1, orig + v)
when 3:
# subtract - value at address o2 is subtracted from value at address o1
v = @memory.read(o2)
orig = @memory.read(o1)
@memory.write(o1, orig - v)
when 4:
# multiply - value at address o2 is multiplied into value at address o1
v = @memory.read(o2)
orig = @memory.read(o1)
@memory.write(o1, orig * v)
when 5:
# divide - value at address o1 is divided by value at address o2
v = @memory.read(o2)
orig = @memory.read(o1)
@memory.write(o1, orig / v)
when 6:
# conditional jump - start executing from instruction at address o1 if value at address o2 is non-zero
v = @memory.read(o2)
execute(o1) if v != 0
when nil:
raise "no instruction found at address: #{address}. Halting."
end
execute(address + 3) # move to the next instruction
end
end
# Sample programs to do some trivial tasks
# multiply 13 with 10 and print the result
Computer.new(64,[4,6,7,0,6,0,13,10])
# infinite loop using jump which prints 111. Will halt with StackOverflow error.
Computer.new(64,[0,6,1,6,0,1,111])
# TODO: program to print squares of 1 to 10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment