#! /usr/bin/env python
|
|
|
|
import sys
|
|
import time
|
|
import random
|
|
from optparse import OptionParser
|
|
|
|
#
|
|
# HELPER
|
|
#
|
|
def dospace(howmuch):
|
|
for i in range(howmuch):
|
|
print '%24s' % ' ',
|
|
|
|
# useful instead of assert
|
|
def zassert(cond, str):
|
|
if cond == False:
|
|
print 'ABORT::', str
|
|
exit(1)
|
|
return
|
|
|
|
class cpu:
|
|
#
|
|
# INIT: how much memory?
|
|
#
|
|
def __init__(self, memory, memtrace, regtrace, cctrace, compute, verbose):
|
|
#
|
|
# CONSTANTS
|
|
#
|
|
|
|
# conditions
|
|
self.COND_GT = 0
|
|
self.COND_GTE = 1
|
|
self.COND_LT = 2
|
|
self.COND_LTE = 3
|
|
self.COND_EQ = 4
|
|
self.COND_NEQ = 5
|
|
|
|
# registers in system
|
|
self.REG_ZERO = 0
|
|
self.REG_AX = 1
|
|
self.REG_BX = 2
|
|
self.REG_CX = 3
|
|
self.REG_DX = 4
|
|
self.REG_SP = 5
|
|
self.REG_BP = 6
|
|
|
|
# system memory: in KB
|
|
self.max_memory = memory * 1024
|
|
|
|
# which memory addrs and registers to trace?
|
|
self.memtrace = memtrace
|
|
self.regtrace = regtrace
|
|
self.cctrace = cctrace
|
|
self.compute = compute
|
|
self.verbose = verbose
|
|
|
|
self.PC = 0
|
|
self.registers = {}
|
|
self.conditions = {}
|
|
self.labels = {}
|
|
self.vars = {}
|
|
self.memory = {}
|
|
self.pmemory = {} # for printable version of what's in memory (instructions)
|
|
|
|
self.condlist = [self.COND_GTE, self.COND_GT, self.COND_LTE, self.COND_LT, self.COND_NEQ, self.COND_EQ]
|
|
self.regnums = [self.REG_ZERO, self.REG_AX, self.REG_BX, self.REG_CX, self.REG_DX, self.REG_SP, self.REG_BP]
|
|
|
|
self.regnames = {}
|
|
self.regnames['zero'] = self.REG_ZERO # hidden zero-valued register
|
|
self.regnames['ax'] = self.REG_AX
|
|
self.regnames['bx'] = self.REG_BX
|
|
self.regnames['cx'] = self.REG_CX
|
|
self.regnames['dx'] = self.REG_DX
|
|
self.regnames['sp'] = self.REG_SP
|
|
self.regnames['bp'] = self.REG_BP
|
|
|
|
tmplist = []
|
|
for r in self.regtrace:
|
|
zassert(r in self.regnames, 'Register %s cannot be traced because it does not exist' % r)
|
|
tmplist.append(self.regnames[r])
|
|
self.regtrace = tmplist
|
|
|
|
self.init_memory()
|
|
self.init_registers()
|
|
self.init_condition_codes()
|
|
|
|
#
|
|
# BEFORE MACHINE RUNS
|
|
#
|
|
def init_condition_codes(self):
|
|
for c in self.condlist:
|
|
self.conditions[c] = False
|
|
|
|
def init_memory(self):
|
|
for i in range(self.max_memory):
|
|
self.memory[i] = 0
|
|
|
|
def init_registers(self):
|
|
for i in self.regnums:
|
|
self.registers[i] = 0
|
|
|
|
def dump_memory(self):
|
|
print 'MEMORY DUMP'
|
|
for i in range(self.max_memory):
|
|
if i not in self.pmemory and i in self.memory and self.memory[i] != 0:
|
|
print ' m[%d]' % i, self.memory[i]
|
|
|
|
#
|
|
# INFORMING ABOUT THE HARDWARE
|
|
#
|
|
def get_regnum(self, name):
|
|
assert(name in self.regnames)
|
|
return self.regnames[name]
|
|
|
|
def get_regname(self, num):
|
|
assert(num in self.regnums)
|
|
for rname in self.regnames:
|
|
if self.regnames[rname] == num:
|
|
return rname
|
|
return ''
|
|
|
|
def get_regnums(self):
|
|
return self.regnums
|
|
|
|
def get_condlist(self):
|
|
return self.condlist
|
|
|
|
def get_reg(self, reg):
|
|
assert(reg in self.regnums)
|
|
return self.registers[reg]
|
|
|
|
def get_cond(self, cond):
|
|
assert(cond in self.condlist)
|
|
return self.conditions[cond]
|
|
|
|
def get_pc(self):
|
|
return self.PC
|
|
|
|
def set_reg(self, reg, value):
|
|
assert(reg in self.regnums)
|
|
self.registers[reg] = value
|
|
|
|
def set_cond(self, cond, value):
|
|
assert(cond in self.condlist)
|
|
self.conditions[cond] = value
|
|
|
|
def set_pc(self, pc):
|
|
self.PC = pc
|
|
|
|
#
|
|
# INSTRUCTIONS
|
|
#
|
|
def halt(self):
|
|
return -1
|
|
|
|
def iyield(self):
|
|
return -2
|
|
|
|
def nop(self):
|
|
return 0
|
|
|
|
def rdump(self):
|
|
print 'REGISTERS::',
|
|
print 'ax:', self.registers[self.REG_AX],
|
|
print 'bx:', self.registers[self.REG_BX],
|
|
print 'cx:', self.registers[self.REG_CX],
|
|
print 'dx:', self.registers[self.REG_DX],
|
|
|
|
def mdump(self, index):
|
|
print 'm[%d] ' % index, self.memory[index]
|
|
|
|
def move_i_to_r(self, src, dst):
|
|
self.registers[dst] = src
|
|
return 0
|
|
|
|
# memory: value, register, register
|
|
def move_i_to_m(self, src, value, reg1, reg2):
|
|
tmp = value + self.registers[reg1] + self.registers[reg2]
|
|
self.memory[tmp] = src
|
|
return 0
|
|
|
|
def move_m_to_r(self, value, reg1, reg2, dst):
|
|
tmp = value + self.registers[reg1] + self.registers[reg2]
|
|
# print 'doing mov', 'val:', value, 'r1:', self.get_regname(reg1), self.registers[reg1], 'r2:', self.get_regname(reg2), self.registers[reg2], 'dst', self.get_regname(dst), 'tmp', tmp, 'reg[dst]', self.registers[dst], 'mem', self.memory[tmp]
|
|
self.registers[dst] = self.memory[tmp]
|
|
|
|
def move_r_to_m(self, src, value, reg1, reg2):
|
|
tmp = value + self.registers[reg1] + self.registers[reg2]
|
|
self.memory[tmp] = self.registers[src]
|
|
return 0
|
|
|
|
def move_r_to_r(self, src, dst):
|
|
self.registers[dst] = self.registers[src]
|
|
return 0
|
|
|
|
def add_i_to_r(self, src, dst):
|
|
self.registers[dst] += src
|
|
return 0
|
|
|
|
def add_r_to_r(self, src, dst):
|
|
self.registers[dst] += self.registers[src]
|
|
return 0
|
|
|
|
def sub_i_to_r(self, src, dst):
|
|
self.registers[dst] -= src
|
|
return 0
|
|
|
|
def sub_r_to_r(self, src, dst):
|
|
self.registers[dst] -= self.registers[src]
|
|
return 0
|
|
|
|
|
|
#
|
|
# SUPPORT FOR LOCKS
|
|
#
|
|
def atomic_exchange(self, src, value, reg1, reg2):
|
|
tmp = value + self.registers[reg1] + self.registers[reg2]
|
|
old = self.memory[tmp]
|
|
self.memory[tmp] = self.registers[src]
|
|
self.registers[src] = old
|
|
return 0
|
|
|
|
def fetchadd(self, src, value, reg1, reg2):
|
|
tmp = value + self.registers[reg1] + self.registers[reg2]
|
|
old = self.memory[tmp]
|
|
self.memory[tmp] = self.memory[tmp] + self.registers[src]
|
|
self.registers[src] = old
|
|
|
|
#
|
|
# TEST for conditions
|
|
#
|
|
def test_all(self, src, dst):
|
|
self.init_condition_codes()
|
|
if dst > src:
|
|
self.conditions[self.COND_GT] = True
|
|
if dst >= src:
|
|
self.conditions[self.COND_GTE] = True
|
|
if dst < src:
|
|
self.conditions[self.COND_LT] = True
|
|
if dst <= src:
|
|
self.conditions[self.COND_LTE] = True
|
|
if dst == src:
|
|
self.conditions[self.COND_EQ] = True
|
|
if dst != src:
|
|
self.conditions[self.COND_NEQ] = True
|
|
return 0
|
|
|
|
def test_i_r(self, src, dst):
|
|
self.init_condition_codes()
|
|
return self.test_all(src, self.registers[dst])
|
|
|
|
def test_r_i(self, src, dst):
|
|
self.init_condition_codes()
|
|
return self.test_all(self.registers[src], dst)
|
|
|
|
def test_r_r(self, src, dst):
|
|
self.init_condition_codes()
|
|
return self.test_all(self.registers[src], self.registers[dst])
|
|
|
|
#
|
|
# JUMPS
|
|
#
|
|
def jump(self, targ):
|
|
self.PC = targ
|
|
return 0
|
|
|
|
def jump_notequal(self, targ):
|
|
if self.conditions[self.COND_NEQ] == True:
|
|
self.PC = targ
|
|
return 0
|
|
|
|
def jump_equal(self, targ):
|
|
if self.conditions[self.COND_EQ] == True:
|
|
self.PC = targ
|
|
return 0
|
|
|
|
def jump_lessthan(self, targ):
|
|
if self.conditions[self.COND_LT] == True:
|
|
self.PC = targ
|
|
return 0
|
|
|
|
def jump_lessthanorequal(self, targ):
|
|
if self.conditions[self.COND_LTE] == True:
|
|
self.PC = targ
|
|
return 0
|
|
|
|
def jump_greaterthan(self, targ):
|
|
if self.conditions[self.COND_GT] == True:
|
|
self.PC = targ
|
|
return 0
|
|
|
|
def jump_greaterthanorequal(self, targ):
|
|
if self.conditions[self.COND_GTE] == True:
|
|
self.PC = targ
|
|
return 0
|
|
|
|
#
|
|
# CALL and RETURN
|
|
#
|
|
def call(self, targ):
|
|
self.registers[self.REG_SP] -= 4
|
|
self.memory[self.registers[self.REG_SP]] = self.PC
|
|
self.PC = targ
|
|
|
|
def ret(self):
|
|
self.PC = self.memory[self.registers[self.REG_SP]]
|
|
self.registers[self.REG_SP] += 4
|
|
|
|
#
|
|
# STACK and related
|
|
#
|
|
def push_r(self, reg):
|
|
self.registers[self.REG_SP] -= 4
|
|
self.memory[self.registers[self.REG_SP]] = self.registers[reg]
|
|
return 0
|
|
|
|
def push_m(self, value, reg1, reg2):
|
|
# print 'push_m', value, reg1, reg2
|
|
self.registers[self.REG_SP] -= 4
|
|
tmp = value + self.registers[reg1] + self.registers[reg2]
|
|
# push address onto stack, not memory value itself
|
|
self.memory[self.registers[self.REG_SP]] = tmp
|
|
return 0
|
|
|
|
def pop(self):
|
|
self.registers[self.REG_SP] += 4
|
|
|
|
def pop_r(self, dst):
|
|
self.registers[dst] = self.registers[self.REG_SP]
|
|
self.registers[self.REG_SP] += 4
|
|
|
|
#
|
|
# HELPER func for getarg
|
|
#
|
|
def register_translate(self, r):
|
|
if r in self.regnames:
|
|
return self.regnames[r]
|
|
zassert(False, 'Register %s is not a valid register' % r)
|
|
return
|
|
|
|
#
|
|
# HELPER in parsing mov (quite primitive) and other ops
|
|
# returns: (value, type)
|
|
# where type is (TYPE_REGISTER, TYPE_IMMEDIATE, TYPE_MEMORY)
|
|
#
|
|
# FORMATS
|
|
# %ax - register
|
|
# $10 - immediate
|
|
# 10 - direct memory
|
|
# 10(%ax) - memory + reg indirect
|
|
# 10(%ax,%bx) - memory + 2 reg indirect
|
|
# 10(%ax,%bx,4) - XXX (not handled)
|
|
#
|
|
def getarg(self, arg):
|
|
tmp1 = arg.replace(',', '')
|
|
tmp = tmp1.replace(' \t', '')
|
|
|
|
if tmp[0] == '$':
|
|
zassert(len(tmp) == 2, 'correct form is $number (not %s)' % tmp)
|
|
value = tmp.split('$')[1]
|
|
zassert(value.isdigit(), 'value [%s] must be a digit' % value)
|
|
return int(value), 'TYPE_IMMEDIATE'
|
|
elif tmp[0] == '%':
|
|
register = tmp.split('%')[1]
|
|
return self.register_translate(register), 'TYPE_REGISTER'
|
|
elif tmp[0] == '(':
|
|
register = tmp.split('(')[1].split(')')[0].split('%')[1]
|
|
return '%d,%d,%d' % (0, self.register_translate(register), self.register_translate('zero')), 'TYPE_MEMORY'
|
|
elif tmp[0] == '.':
|
|
targ = tmp
|
|
return targ, 'TYPE_LABEL'
|
|
elif tmp[0].isalpha() and not tmp[0].isdigit():
|
|
zassert(tmp in self.vars, 'Variable %s is not declared' % tmp)
|
|
# print '%d,%d,%d' % (self.vars[tmp], self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
|
|
return '%d,%d,%d' % (self.vars[tmp], self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
|
|
elif tmp[0].isdigit() or tmp[0] == '-':
|
|
# MOST GENERAL CASE: number(reg,reg) or number(reg)
|
|
# we ignore the common x86 number(reg,reg,constant) for now
|
|
neg = 1
|
|
if tmp[0] == '-':
|
|
tmp = tmp[1:]
|
|
neg = -1
|
|
s = tmp.split('(')
|
|
if len(s) == 1:
|
|
value = neg * int(tmp)
|
|
# print '%d,%d,%d' % (int(value), self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
|
|
return '%d,%d,%d' % (int(value), self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
|
|
elif len(s) == 2:
|
|
value = neg * int(s[0])
|
|
t = s[1].split(')')[0].split(',')
|
|
if len(t) == 1:
|
|
register = t[0].split('%')[1]
|
|
# print '%d,%d,%d' % (int(value), self.register_translate(register), self.register_translate('zero')), 'TYPE_MEMORY'
|
|
return '%d,%d,%d' % (int(value), self.register_translate(register), self.register_translate('zero')), 'TYPE_MEMORY'
|
|
elif len(t) == 2:
|
|
register1 = t[0].split('%')[1]
|
|
register2 = t[1].split('%')[1]
|
|
# print '%d,%d,%d' % (int(value), self.register_translate(register1), self.register_translate(register2)), 'TYPE_MEMORY'
|
|
return '%d,%d,%d' % (int(value), self.register_translate(register1), self.register_translate(register2)), 'TYPE_MEMORY'
|
|
else:
|
|
print 'mov: bad argument [%s]' % tmp
|
|
exit(1)
|
|
return
|
|
zassert(True, 'mov: bad argument [%s]' % arg)
|
|
return
|
|
|
|
#
|
|
# LOAD a program into memory
|
|
# make it ready to execute
|
|
#
|
|
def load(self, infile, loadaddr):
|
|
pc = int(loadaddr)
|
|
fd = open(infile)
|
|
|
|
bpc = loadaddr
|
|
data = 100
|
|
|
|
for line in fd:
|
|
cline = line.rstrip()
|
|
# print 'PASS 1', cline
|
|
|
|
# remove everything after the comment marker
|
|
ctmp = cline.split('#')
|
|
assert(len(ctmp) == 1 or len(ctmp) == 2)
|
|
if len(ctmp) == 2:
|
|
cline = ctmp[0]
|
|
|
|
# remove empty lines, and split line by spaces
|
|
tmp = cline.split()
|
|
if len(tmp) == 0:
|
|
continue
|
|
|
|
# only pay attention to labels and variables
|
|
if tmp[0] == '.var':
|
|
assert(len(tmp) == 2)
|
|
assert(tmp[0] not in self.vars)
|
|
self.vars[tmp[1]] = data
|
|
data += 4
|
|
zassert(data < bpc, 'Load address overrun by static data')
|
|
if self.verbose: print 'ASSIGN VAR', tmp[0], "-->", tmp[1], self.vars[tmp[1]]
|
|
elif tmp[0][0] == '.':
|
|
assert(len(tmp) == 1)
|
|
self.labels[tmp[0]] = int(pc)
|
|
if self.verbose: print 'ASSIGN LABEL', tmp[0], "-->", pc
|
|
else:
|
|
pc += 1
|
|
fd.close()
|
|
|
|
if self.verbose: print ''
|
|
|
|
# second pass: do everything else
|
|
pc = int(loadaddr)
|
|
fd = open(infile)
|
|
for line in fd:
|
|
cline = line.rstrip()
|
|
# print 'PASS 2', cline
|
|
|
|
# remove everything after the comment marker
|
|
ctmp = cline.split('#')
|
|
assert(len(ctmp) == 1 or len(ctmp) == 2)
|
|
if len(ctmp) == 2:
|
|
cline = ctmp[0]
|
|
|
|
# remove empty lines, and split line by spaces
|
|
tmp = cline.split()
|
|
if len(tmp) == 0:
|
|
continue
|
|
|
|
# skip labels: all else must be instructions
|
|
if cline[0] != '.':
|
|
tmp = cline.split(None, 1)
|
|
opcode = tmp[0]
|
|
self.pmemory[pc] = cline.strip()
|
|
|
|
# MAIN OPCODE LOOP
|
|
if opcode == 'mov':
|
|
rtmp = tmp[1].split(',', 1)
|
|
zassert(len(tmp) == 2 and len(rtmp) == 2, 'mov: needs two args, separated by commas [%s]' % cline)
|
|
arg1 = rtmp[0].strip()
|
|
arg2 = rtmp[1].strip()
|
|
(src, stype) = self.getarg(arg1)
|
|
(dst, dtype) = self.getarg(arg2)
|
|
# print 'MOV', src, stype, dst, dtype
|
|
if stype == 'TYPE_MEMORY' and dtype == 'TYPE_MEMORY':
|
|
print 'bad mov: two memory arguments'
|
|
exit(1)
|
|
elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_IMMEDIATE':
|
|
print 'bad mov: two immediate arguments'
|
|
exit(1)
|
|
elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
|
|
self.memory[pc] = 'self.move_i_to_r(%d, %d)' % (int(src), dst)
|
|
elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
|
|
self.memory[pc] = 'self.move_i_to_r(%d, %d)' % (int(src), dst)
|
|
elif stype == 'TYPE_MEMORY' and dtype == 'TYPE_REGISTER':
|
|
tmp = src.split(',')
|
|
assert(len(tmp) == 3)
|
|
self.memory[pc] = 'self.move_m_to_r(%d, %d, %d, %d)' % (int(tmp[0]), int(tmp[1]), int(tmp[2]), dst)
|
|
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_MEMORY':
|
|
tmp = dst.split(',')
|
|
assert(len(tmp) == 3)
|
|
self.memory[pc] = 'self.move_r_to_m(%d, %d, %d, %d)' % (src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
|
|
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
|
|
self.memory[pc] = 'self.move_r_to_r(%d, %d)' % (src, dst)
|
|
elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_MEMORY':
|
|
tmp = dst.split(',')
|
|
assert(len(tmp) == 3)
|
|
self.memory[pc] = 'self.move_i_to_m(%d, %d, %d, %d)' % (src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
|
|
else:
|
|
zassert(False, 'malformed mov instruction')
|
|
elif opcode == 'pop':
|
|
if len(tmp) == 1:
|
|
self.memory[pc] = 'self.pop()'
|
|
elif len(tmp) == 2:
|
|
arg = tmp[1].strip()
|
|
(dst, dtype) = self.getarg(arg)
|
|
zassert(dtype == 'TYPE_REGISTER', 'Can only pop into a register')
|
|
self.memory[pc] = 'self.pop_r(%d)' % dst
|
|
else:
|
|
zassert(False, 'pop instruction must take zero/one args')
|
|
elif opcode == 'push':
|
|
(src, stype) = self.getarg(tmp[1].strip())
|
|
if stype == 'TYPE_REGISTER':
|
|
self.memory[pc] = 'self.push_r(%d)' % (int(src))
|
|
elif stype == 'TYPE_MEMORY':
|
|
tmp = src.split(',')
|
|
assert(len(tmp) == 3)
|
|
self.memory[pc] = 'self.push_m(%d,%d,%d)' % (int(tmp[0]), int(tmp[1]), int(tmp[2]))
|
|
else:
|
|
zassert(False, 'Cannot push anything but registers')
|
|
elif opcode == 'call':
|
|
(targ, ttype) = self.getarg(tmp[1].strip())
|
|
if ttype == 'TYPE_LABEL':
|
|
self.memory[pc] = 'self.call(%d)' % (int(self.labels[targ]))
|
|
else:
|
|
zassert(False, 'Cannot call anything but a label')
|
|
elif opcode == 'ret':
|
|
assert(len(tmp) == 1)
|
|
self.memory[pc] = 'self.ret()'
|
|
elif opcode == 'add':
|
|
rtmp = tmp[1].split(',', 1)
|
|
zassert(len(tmp) == 2 and len(rtmp) == 2, 'add: needs two args, separated by commas [%s]' % cline)
|
|
arg1 = rtmp[0].strip()
|
|
arg2 = rtmp[1].strip()
|
|
(src, stype) = self.getarg(arg1)
|
|
(dst, dtype) = self.getarg(arg2)
|
|
if stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
|
|
self.memory[pc] = 'self.add_i_to_r(%d, %d)' % (int(src), dst)
|
|
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
|
|
self.memory[pc] = 'self.add_r_to_r(%d, %d)' % (int(src), dst)
|
|
else:
|
|
zassert(False, 'malformed usage of add instruction')
|
|
elif opcode == 'sub':
|
|
rtmp = tmp[1].split(',', 1)
|
|
zassert(len(tmp) == 2 and len(rtmp) == 2, 'sub: needs two args, separated by commas [%s]' % cline)
|
|
arg1 = rtmp[0].strip()
|
|
arg2 = rtmp[1].strip()
|
|
(src, stype) = self.getarg(arg1)
|
|
(dst, dtype) = self.getarg(arg2)
|
|
if stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
|
|
self.memory[pc] = 'self.sub_i_to_r(%d, %d)' % (int(src), dst)
|
|
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
|
|
self.memory[pc] = 'self.sub_r_to_r(%d, %d)' % (int(src), dst)
|
|
else:
|
|
zassert(False, 'malformed usage of sub instruction')
|
|
elif opcode == 'fetchadd':
|
|
rtmp = tmp[1].split(',', 1)
|
|
zassert(len(tmp) == 2 and len(rtmp) == 2, 'fetchadd: needs two args, separated by commas [%s]' % cline)
|
|
arg1 = rtmp[0].strip()
|
|
arg2 = rtmp[1].strip()
|
|
(src, stype) = self.getarg(arg1)
|
|
(dst, dtype) = self.getarg(arg2)
|
|
tmp = dst.split(',')
|
|
assert(len(tmp) == 3)
|
|
if stype == 'TYPE_REGISTER' and dtype == 'TYPE_MEMORY':
|
|
self.memory[pc] = 'self.fetchadd(%d, %d, %d, %d)' % (src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
|
|
else:
|
|
zassert(False, 'poorly specified fetch and add')
|
|
elif opcode == 'xchg':
|
|
rtmp = tmp[1].split(',', 1)
|
|
zassert(len(tmp) == 2 and len(rtmp) == 2, 'xchg: needs two args, separated by commas [%s]' % cline)
|
|
arg1 = rtmp[0].strip()
|
|
arg2 = rtmp[1].strip()
|
|
(src, stype) = self.getarg(arg1)
|
|
(dst, dtype) = self.getarg(arg2)
|
|
tmp = dst.split(',')
|
|
assert(len(tmp) == 3)
|
|
if stype == 'TYPE_REGISTER' and dtype == 'TYPE_MEMORY':
|
|
self.memory[pc] = 'self.atomic_exchange(%d, %d, %d, %d)' % (src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
|
|
else:
|
|
zassert(False, 'poorly specified atomic exchange')
|
|
elif opcode == 'test':
|
|
rtmp = tmp[1].split(',', 1)
|
|
zassert(len(tmp) == 2 and len(rtmp) == 2, 'test: needs two args, separated by commas [%s]' % cline)
|
|
arg1 = rtmp[0].strip()
|
|
arg2 = rtmp[1].strip()
|
|
(src, stype) = self.getarg(arg1)
|
|
(dst, dtype) = self.getarg(arg2)
|
|
if stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
|
|
self.memory[pc] = 'self.test_i_r(%d, %d)' % (int(src), dst)
|
|
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
|
|
self.memory[pc] = 'self.test_r_r(%d, %d)' % (int(src), dst)
|
|
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_IMMEDIATE':
|
|
self.memory[pc] = 'self.test_r_i(%d, %d)' % (int(src), dst)
|
|
else:
|
|
zassert(False, 'malformed usage of test instruction')
|
|
elif opcode == 'j':
|
|
(targ, ttype) = self.getarg(tmp[1].strip())
|
|
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
|
|
self.memory[pc] = 'self.jump(%d)' % int(self.labels[targ])
|
|
elif opcode == 'jne':
|
|
(targ, ttype) = self.getarg(tmp[1].strip())
|
|
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
|
|
self.memory[pc] = 'self.jump_notequal(%d)' % int(self.labels[targ])
|
|
elif opcode == 'je':
|
|
(targ, ttype) = self.getarg(tmp[1].strip())
|
|
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
|
|
self.memory[pc] = 'self.jump_equal(%d)' % self.labels[targ]
|
|
elif opcode == 'jlt':
|
|
(targ, ttype) = self.getarg(tmp[1].strip())
|
|
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
|
|
self.memory[pc] = 'self.jump_lessthan(%d)' % int(self.labels[targ])
|
|
elif opcode == 'jlte':
|
|
(targ, ttype) = self.getarg(tmp[1].strip())
|
|
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
|
|
self.memory[pc] = 'self.jump_lessthanorequal(%s)' % self.labels[targ]
|
|
elif opcode == 'jgt':
|
|
(targ, ttype) = self.getarg(tmp[1].strip())
|
|
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
|
|
self.memory[pc] = 'self.jump_greaterthan(%d)' % int(self.labels[targ])
|
|
elif opcode == 'jgte':
|
|
(targ, ttype) = self.getarg(tmp[1].strip())
|
|
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
|
|
self.memory[pc] = 'self.jump_greaterthanorequal(%s)' % self.labels[targ]
|
|
elif opcode == 'nop':
|
|
self.memory[pc] = 'self.nop()'
|
|
elif opcode == 'halt':
|
|
self.memory[pc] = 'self.halt()'
|
|
elif opcode == 'yield':
|
|
self.memory[pc] = 'self.iyield()'
|
|
elif opcode == 'rdump':
|
|
self.memory[pc] = 'self.rdump()'
|
|
elif opcode == 'mdump':
|
|
self.memory[pc] = 'self.mdump(%s)' % tmp[1]
|
|
else:
|
|
print 'illegal opcode: ', opcode
|
|
exit(1)
|
|
|
|
if self.verbose: print 'pc:%d LOADING %20s --> %s' % (pc, self.pmemory[pc], self.memory[pc])
|
|
|
|
# INCREMENT PC for loader
|
|
pc += 1
|
|
# END: loop over file
|
|
fd.close()
|
|
if self.verbose: print ''
|
|
return
|
|
# END: load
|
|
|
|
def print_headers(self, procs):
|
|
# print some headers
|
|
if len(self.memtrace) > 0:
|
|
for m in self.memtrace:
|
|
if m[0].isdigit():
|
|
print '%5d' % int(m),
|
|
else:
|
|
zassert(m in self.vars, 'Traced variable %s not declared' % m)
|
|
print '%5s' % m,
|
|
print ' ',
|
|
if len(self.regtrace) > 0:
|
|
for r in self.regtrace:
|
|
print '%5s' % self.get_regname(r),
|
|
print ' ',
|
|
if cctrace == True:
|
|
print '>= > <= < != ==',
|
|
|
|
# and per thread
|
|
for i in range(procs.getnum()):
|
|
print ' Thread %d ' % i,
|
|
print ''
|
|
return
|
|
|
|
def print_trace(self, newline):
|
|
if len(self.memtrace) > 0:
|
|
for m in self.memtrace:
|
|
if self.compute:
|
|
if m[0].isdigit():
|
|
print '%5d' % self.memory[int(m)],
|
|
else:
|
|
zassert(m in self.vars, 'Traced variable %s not declared' % m)
|
|
print '%5d' % self.memory[self.vars[m]],
|
|
else:
|
|
print '%5s' % '?',
|
|
print ' ',
|
|
if len(self.regtrace) > 0:
|
|
for r in self.regtrace:
|
|
if self.compute:
|
|
print '%5d' % self.registers[r],
|
|
else:
|
|
print '%5s' % '?',
|
|
print ' ',
|
|
if cctrace == True:
|
|
for c in self.condlist:
|
|
if self.compute:
|
|
if self.conditions[c]:
|
|
print '1 ',
|
|
else:
|
|
print '0 ',
|
|
else:
|
|
print '? ',
|
|
if (len(self.memtrace) > 0 or len(self.regtrace) > 0 or cctrace == True) and newline == True:
|
|
print ''
|
|
return
|
|
|
|
def setint(self, intfreq, intrand):
|
|
if intrand == False:
|
|
return intfreq
|
|
return int(random.random() * intfreq) + 1
|
|
|
|
def run(self, procs, intfreq, intrand):
|
|
# hw init: cc's, interrupt frequency, etc.
|
|
interrupt = self.setint(intfreq, intrand)
|
|
icount = 0
|
|
|
|
self.print_headers(procs)
|
|
self.print_trace(True)
|
|
|
|
while True:
|
|
# need thread ID of current process
|
|
tid = procs.getcurr().gettid()
|
|
|
|
# FETCH
|
|
prevPC = self.PC
|
|
instruction = self.memory[self.PC]
|
|
self.PC += 1
|
|
|
|
# DECODE and EXECUTE
|
|
# key: self.PC may be changed during eval; thus MUST be incremented BEFORE eval
|
|
rc = eval(instruction)
|
|
|
|
# tracing details: ALWAYS AFTER EXECUTION OF INSTRUCTION
|
|
self.print_trace(False)
|
|
|
|
# output: thread-proportional spacing followed by PC and instruction
|
|
dospace(tid)
|
|
print prevPC, self.pmemory[prevPC]
|
|
icount += 1
|
|
|
|
# halt instruction issued
|
|
if rc == -1:
|
|
procs.done()
|
|
if procs.numdone() == procs.getnum():
|
|
return icount
|
|
procs.next()
|
|
procs.restore()
|
|
|
|
self.print_trace(False)
|
|
for i in range(procs.getnum()):
|
|
print '----- Halt;Switch ----- ',
|
|
print ''
|
|
|
|
# do interrupt processing
|
|
interrupt -= 1
|
|
if interrupt == 0 or rc == -2:
|
|
interrupt = self.setint(intfreq, intrand)
|
|
procs.save()
|
|
procs.next()
|
|
procs.restore()
|
|
|
|
self.print_trace(False)
|
|
for i in range(procs.getnum()):
|
|
print '------ Interrupt ------ ',
|
|
print ''
|
|
# END: while
|
|
return
|
|
|
|
#
|
|
# END: class cpu
|
|
#
|
|
|
|
|
|
#
|
|
# PROCESS LIST class
|
|
#
|
|
class proclist:
|
|
def __init__(self):
|
|
self.plist = []
|
|
self.curr = 0
|
|
self.active = 0
|
|
|
|
def done(self):
|
|
self.plist[self.curr].setdone()
|
|
self.active -= 1
|
|
|
|
def numdone(self):
|
|
return len(self.plist) - self.active
|
|
|
|
def getnum(self):
|
|
return len(self.plist)
|
|
|
|
def add(self, p):
|
|
self.active += 1
|
|
self.plist.append(p)
|
|
|
|
def getcurr(self):
|
|
return self.plist[self.curr]
|
|
|
|
def save(self):
|
|
self.plist[self.curr].save()
|
|
|
|
def restore(self):
|
|
self.plist[self.curr].restore()
|
|
|
|
def next(self):
|
|
for i in range(self.curr+1, len(self.plist)):
|
|
if self.plist[i].isdone() == False:
|
|
self.curr = i
|
|
return
|
|
for i in range(0, self.curr+1):
|
|
if self.plist[i].isdone() == False:
|
|
self.curr = i
|
|
return
|
|
|
|
#
|
|
# PROCESS class
|
|
#
|
|
class process:
|
|
def __init__(self, cpu, tid, pc, stackbottom, reginit):
|
|
self.cpu = cpu # object reference
|
|
self.tid = tid
|
|
self.pc = pc
|
|
self.regs = {}
|
|
self.cc = {}
|
|
self.done = False
|
|
self.stack = stackbottom
|
|
|
|
# init regs: all 0 or specially set to something
|
|
for r in self.cpu.get_regnums():
|
|
self.regs[r] = 0
|
|
if reginit != '':
|
|
# form: ax=1,bx=2 (for some subset of registers)
|
|
for r in reginit.split(':'):
|
|
tmp = r.split('=')
|
|
assert(len(tmp) == 2)
|
|
self.regs[self.cpu.get_regnum(tmp[0])] = int(tmp[1])
|
|
|
|
# init CCs
|
|
for c in self.cpu.get_condlist():
|
|
self.cc[c] = False
|
|
|
|
# stack
|
|
self.regs[self.cpu.get_regnum('sp')] = stackbottom
|
|
# print 'REG', self.cpu.get_regnum('sp'), self.regs[self.cpu.get_regnum('sp')]
|
|
|
|
return
|
|
|
|
def gettid(self):
|
|
return self.tid
|
|
|
|
def save(self):
|
|
self.pc = self.cpu.get_pc()
|
|
for c in self.cpu.get_condlist():
|
|
self.cc[c] = self.cpu.get_cond(c)
|
|
for r in self.cpu.get_regnums():
|
|
self.regs[r] = self.cpu.get_reg(r)
|
|
|
|
def restore(self):
|
|
self.cpu.set_pc(self.pc)
|
|
for c in self.cpu.get_condlist():
|
|
self.cpu.set_cond(c, self.cc[c])
|
|
for r in self.cpu.get_regnums():
|
|
self.cpu.set_reg(r, self.regs[r])
|
|
|
|
def setdone(self):
|
|
self.done = True
|
|
|
|
def isdone(self):
|
|
return self.done == True
|
|
|
|
#
|
|
# main program
|
|
#
|
|
parser = OptionParser()
|
|
parser.add_option('-s', '--seed', default=0, help='the random seed', action='store', type='int', dest='seed')
|
|
parser.add_option('-t', '--threads', default=2, help='number of threads', action='store', type='int', dest='numthreads')
|
|
parser.add_option('-p', '--program', default='', help='source program (in .s)', action='store', type='string', dest='progfile')
|
|
parser.add_option('-i', '--interrupt', default=50, help='interrupt frequency', action='store', type='int', dest='intfreq')
|
|
parser.add_option('-r', '--randints', default=False, help='if interrupts are random', action='store_true', dest='intrand')
|
|
parser.add_option('-a', '--argv', default='',
|
|
help='comma-separated per-thread args (e.g., ax=1,ax=2 sets thread 0 ax reg to 1 and thread 1 ax reg to 2); specify multiple regs per thread via colon-separated list (e.g., ax=1:bx=2,cx=3 sets thread 0 ax and bx and just cx for thread 1)',
|
|
action='store', type='string', dest='argv')
|
|
parser.add_option('-L', '--loadaddr', default=1000, help='address where to load code', action='store', type='int', dest='loadaddr')
|
|
parser.add_option('-m', '--memsize', default=128, help='size of address space (KB)', action='store', type='int', dest='memsize')
|
|
parser.add_option('-M', '--memtrace', default='', help='comma-separated list of addrs to trace (e.g., 20000,20001)', action='store',
|
|
type='string', dest='memtrace')
|
|
parser.add_option('-R', '--regtrace', default='', help='comma-separated list of regs to trace (e.g., ax,bx,cx,dx)', action='store',
|
|
type='string', dest='regtrace')
|
|
parser.add_option('-C', '--cctrace', default=False, help='should we trace condition codes', action='store_true', dest='cctrace')
|
|
parser.add_option('-S', '--printstats',default=False, help='print some extra stats', action='store_true', dest='printstats')
|
|
parser.add_option('-v', '--verbose', default=False, help='print some extra info', action='store_true', dest='verbose')
|
|
parser.add_option('-c', '--compute', default=False, help='compute answers for me', action='store_true', dest='solve')
|
|
(options, args) = parser.parse_args()
|
|
|
|
print 'ARG seed', options.seed
|
|
print 'ARG numthreads', options.numthreads
|
|
print 'ARG program', options.progfile
|
|
print 'ARG interrupt frequency', options.intfreq
|
|
print 'ARG interrupt randomness',options.intrand
|
|
print 'ARG argv', options.argv
|
|
print 'ARG load address', options.loadaddr
|
|
print 'ARG memsize', options.memsize
|
|
print 'ARG memtrace', options.memtrace
|
|
print 'ARG regtrace', options.regtrace
|
|
print 'ARG cctrace', options.cctrace
|
|
print 'ARG printstats', options.printstats
|
|
print 'ARG verbose', options.verbose
|
|
print ''
|
|
|
|
seed = int(options.seed)
|
|
numthreads = int(options.numthreads)
|
|
intfreq = int(options.intfreq)
|
|
zassert(intfreq > 0, 'Interrupt frequency must be greater than 0')
|
|
intrand = int(options.intrand)
|
|
progfile = options.progfile
|
|
zassert(progfile != '', 'Program file must be specified')
|
|
argv = options.argv.split(',')
|
|
zassert(len(argv) == numthreads or len(argv) == 1, 'argv: must be one per-thread or just one set of values for all threads')
|
|
|
|
loadaddr = options.loadaddr
|
|
memsize = options.memsize
|
|
|
|
memtrace = []
|
|
if options.memtrace != '':
|
|
for m in options.memtrace.split(','):
|
|
memtrace.append(m)
|
|
|
|
regtrace = []
|
|
if options.regtrace != '':
|
|
for r in options.regtrace.split(','):
|
|
regtrace.append(r)
|
|
|
|
cctrace = options.cctrace
|
|
|
|
printstats = options.printstats
|
|
verbose = options.verbose
|
|
|
|
#
|
|
# MAIN program
|
|
#
|
|
debug = False
|
|
debug = False
|
|
|
|
cpu = cpu(memsize, memtrace, regtrace, cctrace, options.solve, verbose)
|
|
|
|
# load a program
|
|
cpu.load(progfile, loadaddr)
|
|
|
|
# process list
|
|
procs = proclist()
|
|
pid = 0
|
|
stack = memsize * 1000
|
|
for t in range(numthreads):
|
|
if len(argv) > 1:
|
|
arg = argv[pid]
|
|
else:
|
|
arg = argv[0]
|
|
procs.add(process(cpu, pid, loadaddr, stack, arg))
|
|
stack -= 1000
|
|
pid += 1
|
|
|
|
# get first one ready!
|
|
procs.restore()
|
|
|
|
# run it
|
|
t1 = time.clock()
|
|
ic = cpu.run(procs, intfreq, intrand)
|
|
t2 = time.clock()
|
|
|
|
if printstats:
|
|
print ''
|
|
print 'STATS:: Instructions %d' % ic
|
|
print 'STATS:: Emulation Rate %.2f kinst/sec' % (float(ic) / float(t2 - t1) / 1000.0)
|
|
|
|
# use this for profiling
|
|
# import cProfile
|
|
# cProfile.run('run()')
|
|
|
|
|
|
|
|
|