#!/bin/env python
"""
This is a hypothetical virtual machine. It has two registers and can perform addition, conditionals, and stack operations.
example_code.txt:
#########################################
# Iterative Fibonacci Number Generator #
#########################################
#
30055 # 00 JMP 55
00001 # 01 DATA fib-1
00000 # 02 DATA fib-2
00020 # 03 DATA 20 (Process until #20)
00000 # 04 DATA counter
00001 # 05 DATA 1
01001 # 06 LDA 01
50000 # 07 PRI
01004 # 08 LDA 04 (load counter)
20005 # 09 ADD 05 (add const 1)
02004 # 10 STA 04 (save counter)
03000 # 11 XAB
01003 # 12 LDA 03
32023 # 13 JIE 23 (Jump to halt if done)
01001 # 14 LDA 01 // a = b + c
20002 # 15 ADD 02
50000 # 16 PRI
03000 # 17 XAB // Store A for later
01001 # 18 LDA 01 // c = b
02002 # 19 STA 02
03000 # 20 XAB
02001 # 21 STA 01 // b = a
30008 # 22 JMP 08
99000 # 23 HLT
00072 # 24 DATA 'H'
00101 # 25 DATA 'e'
00108 # 26 DATA 'l'
00108 # 27 DATA 'l'
00111 # 28 DATA 'o'
00032 # 29 DATA ' '
00087 # 30 DATA 'W'
00111 # 31 DATA 'o'
00114 # 32 DATA 'r'
00108 # 33 DATA 'l'
00100 # 34 DATA 'd'
00033 # 35 DATA '!'
00010 # 36 DATA '\n'
00014 # 37 DATA 14
00023 # 38 DATA offset
00000 # 39 DATA placemarker
01004 # 40 LDA 04 (load counter)
20005 # 41 ADD 05 (add const 1)
02004 # 42 STA 04 (save counter)
03000 # 43 XAB
01037 # 44 LDA 37 (12)
32052 # 45 JIE 52 (Jump to func_end if done)
01004 # 46 LDA 04 (counter)
20038 # 47 ADD 38 (add offset)
02039 # 48 STA 39 (save place)
05039 # 49 LDP 39 (load place address)
51000 # 50 PRA
30040 # 51 JMP 40
04000 # 52 CLA
02004 # 53 STA 04
30057 # 54 JMP 57
42040 # 55 SAV
30040 # 56 JMP 40
43000 # 57 LOD
30006 # 58 JMP 06
"""
import sys
import getopt
# Mnemonics
NOP = 00 # No op, useful for allowing the machine to skip DATA entries
LDA = 01 # LOAD address into A
STA = 02 # STORE A into address
XAB = 03 # SWAP A & B
CLA = 04 # CLEAR A
LDP = 05 # LOAD the value that Adrress points to
ADD = 20 # ADD address to A
SUB = 21 # SUBTRACT address from A
JMP = 30 # JUMP to address
JLE = 31 # JUMP to address if less-than or equal
JIE = 32 # JUMP to address if equal
JLT = 33 # JUMP to address if less than
PSH = 40 # PUSH A onto stack
POP = 41 # POP off stack into A
SAV = 42 # SAVE A & B (for function jumps)
LOD = 43 # LOAD A & B (for function returns)
PRI = 50 # PRINT contents of A as int
PRA = 51 # PRINT contents of A as char
HLT = 99 # HALT computer
debug = False
class BadCodeException(Exception):
def __init__(self, mar, mdr, ir, op, addr):
self.value = [mar,mdr,ir,op,addr]
def __str__(self):
return repr(self.value)
def usage():
print "Usage of the hypothetical computer"
print
print "-h, --help\tPrint this message"
print "-f, --file=\tRead file for code input"
print "-d, --debug\tShow debug information to stderr"
print "\t\t This is best to display using 2> debug.txt"
def read_file(file=sys.stdin):
code = []
for line in file.readlines():
line = line.split('#')[0].strip()
if len(line) > 0:
code.append(int(line))
return code
def parse_code(memory):
global debug
ir, a, b, mdr = 0, 0, 0, 0
pc, mar, op = 0, 0, 0
stp = 512
if debug:
sys.stderr.write("PC\tOP\tAddr\tA\tB\tSTP\tStack\n")
while True:
mar = pc
mdr = memory[mar]
ir = mdr
op = ir / 1000
addr = ir % 1000
if debug:
sys.stderr.write("%s\t%s\t%s\t%s\t%s\t%s\t%s\n" % (pc, op, addr, a, b, stp, memory[stp]))
pc += 1
if op == LDA:
mar = addr
mdr = memory[mar]
a = mdr
elif op == STA:
mar = addr
mdr = a
memory[mar] = mdr
elif op == XAB:
temp = a
a = b
b = temp
elif op == CLA:
a = 0
elif op == LDP:
mar = addr
mdr = memory[mar]
a = memory[mdr]
elif op == ADD:
mar = addr
mdr = memory[mar]
a = a + mdr
elif op == SUB:
mar = addr
mdr = memory[mar]
a = a - mdr
elif op == JMP:
pc = addr
elif op == PSH:
stp += 1
memory[stp] = a
elif op == POP:
a = memory[stp]
stp -= 1
elif op == SAV:
memory[stp+1] = a
memory[stp+2] = b
stp += 2
elif op == LOD:
a = memory[stp-2]
b = memory[stp-1]
stp -= 2
elif op == JLE:
if a <= b: pc = addr
elif op == JIE:
if a == b: pc = addr
elif op == JLT:
if a < b: pc = addr
elif op == PRI:
print a
elif op == PRA:
sys.stdout.write(chr(a))
elif op == HLT:
sys.exit(0)
elif op == NOP:
continue
else:
raise BadCodeException(mar,mdr,ir,op,addr)
def main(argv):
global debug
memory = [0 for i in xrange(0,1024)]
file_read = False
try:
opts, args = getopt.getopt(argv[1:], "hf:d", ["help", "file=", "debug"])
except getopt.GetoptError, err:
sys.stderr.write(str(err))
usage()
sys.exit(2)
for opt, arg in opts:
if opt in ('-f', '--file'):
code = read_file(open(arg, 'r'))
file_read = True
if opt in ('-h', '--help'):
usage()
sys.exit(0)
if opt in ('-d', '--debug'):
debug = True
if not file_read:
code = read_file()
memory = code + memory
parse_code(memory[0:1024])
if __name__ == '__main__': main(sys.argv)