#!/usr/local/bin/python
# -----------------------------------------------------------------------------
# minime
#
# Author:  David Beazley (beazley@cs.uchicago.edu)
# Date  :  June 29, 2003
#
# This is a very tiny machine simulator.  The program reads in a file of
# hex "opcodes" and executes them.
#
# Each machine code is 16-bits in size and is encoded as a 4 digit
# hexadecimal number like '0xhhhh'.  The first digit (on the left),
# is the instruction code.  The remaining digits are arguments to
# the instruction.
#
# The following instructions are supported:
#
#  HALT:   0x0000              - Halts the machine
#  LOAD:   0x1S0D              - Loads memory into a register. S is the
#                                register with the memory address and
#                                D is the target register.
#  LOADI:  0x2Dnn              - Load immediate value into register D.
#                                'nn' is the 8-bit value
#  SETHI:  0x3Dnn              - Sets the upper 8 bits of register D.
#  STORE:  0x4S0D              - Stores register in memory.  S is register
#                                with memory address.  D is the register
#                                holding value to store.
#  ADD  :  0x5rst              - r+s --> t
#  SUB  :  0x6rst              - r-s --> t
#  AND  :  0x7rst              - r&s --> t
#  OR   :  0x8rst              - r|s --> t
#  NOT  :  0x9r0t              - ~r  --> t
#  LSHIFT: 0xar0t              - r<<1 --> t
#  RSHIFT: 0xbr0t              - r>>1 --> t
#  CMP   : 0xcrst              - Compare r and s and set condition code.
#                                t is type of comparison.
#                                0 (==), 1 (!=), 2 (<), 3 (<=), 4 (>), 5 (>=)
#  BCC  :  0xd0nn              - Branch on condition code. nn is offset.
#  JMP  :  0xennn              - Jump to instruction at location nnn
#  DISP :  0xfr0t              - Display register r on screen.  If t is
#                                0, displayed as hex.  If 1, displayed as ASCII
# -----------------------------------------------------------------------------

import sys

MEMORY_SIZE = 1024

try:
	filename = sys.argv[1]
        try:
		data = open(filename).readlines()
	except IOError:
		print "Couldn't open", filename
		sys.exit(1)
except:
	print "Usage:  minime filename"
	sys.exit(1)


# Get rid of leading white space

data = [x.strip() for x in data]

# Convert input data into op-codes
# Get rid of all lines that don't start with '0x'

data = [x for x in data if x[:2] == '0x']
ops = [eval(x[0:6]) for x in data]

# Start execution
print "Program loaded : %d instructions" % len(ops)

PC     = 0
REGS   = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
MEMORY = [0]*MEMORY_SIZE
CC     = 0

# Look for register settings
r = 2
while r < len(sys.argv):
	if sys.argv[r][:2] == '-r':
		rn = int(sys.argv[r][2])
		REGS[rn] = eval(sys.argv[r+1])
		r = r + 1
	r = r + 1

# Put the program in memory
for i in range(0,len(ops)):
	MEMORY[i] = ops[i]

# Execute the program:

while 1:
	try:
		op = MEMORY[PC]
	except:
		print "Memory fault. Bad address PC = %x" % PC
		print "Machine halted."
		sys.exit(1)
		
	code = (op & 0xf000) >> 12
	r1   = (op & 0x0f00) >> 8
	r2   = (op & 0x00f0) >> 4
	rd   = (op & 0x000f)

	# Register 0 always 0
	REGS[0] = 0
	
	# HALT
	if code == 0x00:
		print "\nMachine halted"
		print "Registers:"
		for i in range(0,8):
			print "R%d = 0x%x" % (i,REGS[i])
		sys.exit(0)

	# LOAD:  r1 = memory address, rd = target register
	elif code == 0x01:
		try:
			REGS[rd] = MEMORY[REGS[r1]]
		except:
			print "\nMemory fault. Bad address %x" % REGS[r1]
			print "Machine halted."
			sys.exit(1)

	# LOADI: r1 = target register
	elif code == 0x02:
		REGS[r1] = (op & 0xff)

	# SETHI: Set high bits of register
	elif code == 0x03:
		REGS[r1] = REGS[r1] + ((op & 0xff) << 8)

	# STORE: r1 = memory address, rd = target register
	elif code == 0x04:
		try:
			MEMORY[REGS[r1]] = REGS[rd]
		except:
			print "\nMemory fault. Bad address %x" % REGS[r1]
			print "Machine halted."
			sys.exit(1)

	# ADD: r1 + r2 --> rd
	elif code == 0x05:
		REGS[rd] = (REGS[r1] + REGS[r2]) & 0xffff

	# SUB: r1 - r2 --> rd
	elif code == 0x06:
		REGS[rd] = (REGS[r1] - REGS[r2]) & 0xffff

	# AND: r1 & r2 --> rd
	elif code == 0x07:
		REGS[rd] = REGS[r1] & REGS[r2]

	# OR: r1 | r2 --> rd
	elif code == 0x08:
		REGS[rd] = REGS[r1] | REGS[r2]

	# NOT:
	elif code == 0x09:
		REGS[rd] = ~REGS[r1]

	# LSHIFT:
	elif code == 0x0a:
		REGS[rd] = (REGS[r1] << 1) & 0xffff

	# RSHIFT:
	elif code == 0x0b:
		REGS[rd] = (REGS[r1] >> 1) & 0xffff
		
	# CMP
	elif code == 0x0c:
		CC = 0
		if rd == 0:
			if REGS[r1] == REGS[r2]:
				CC = 1
		elif rd == 1:
			if REGS[r1] != REGS[r2]:
				CC = 1
		elif rd == 2:
			if REGS[r1] < REGS[r2]:
				CC = 1
		elif rd == 3:
			if REGS[r1] <= REGS[r2]:
				CC = 1
		elif rd == 4:
			if REGS[r1] > REGS[r2]:
				CC = 1
		elif rd == 5:
			if REGS[r1] >= REGS[r2]:
				CC = 1
		else:
			CC = 0

	# BCC - Branch on condition code
	elif code == 0x0d:
		offset = op & 0x00ff
		if (offset > 128):
			offset = offset - 256
		if CC == 1:
			PC = PC + offset + 1
			continue

	# JMP:  Destination address in low 12 bits
	elif code == 0x0e:
		PC = op & 0x0fff
		continue

	# DISP: Display
	elif code == 0x0f:
		if rd == 0:
			print "0x%x" % REGS[r1]
		if rd == 1:
			sys.stdout.write(chr(REGS[r1] & 0xff))

	else:
		print "\nBad machine instruction at [%x] = %x\n" % (PC,op)
		print "Terminating."
		sys.exit(1)

	PC = PC + 1
