os_kernel_lab/related_info/ostep/ostep12-threadlock/x86.py
2015-03-15 16:54:19 +08:00

1187 lines
44 KiB
Python
Executable File

#! /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, printstats, headercount):
#
# 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_EX = 5
self.REG_FX = 6
self.REG_SP = 7
self.REG_BP = 8
# 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.printstats = printstats
self.headercount = headercount
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_EX, self.REG_FX,
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['ex'] = self.REG_EX
self.regnames['fx'] = self.REG_FX
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],
print 'ex:', self.registers[self.REG_EX],
print 'fx:', self.registers[self.REG_FX],
return
def mdump(self, index):
print 'm[%d] ' % index, self.memory[index]
return
#
# MEMORY MOVES
#
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, scale):
tmp = value + self.registers[reg1] + (scale * self.registers[reg2])
self.memory[tmp] = src
return 0
def move_m_to_r(self, value, reg1, reg2, scale, dst):
tmp = value + self.registers[reg1] + (scale * self.registers[reg2])
self.registers[dst] = self.memory[tmp]
def move_r_to_m(self, src, value, reg1, reg2, scale):
tmp = value + self.registers[reg1] + (scale * 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
#
# LOAD EFFECTIVE ADDRESS (everything but the final change of memory value)
#
def lea_m_to_r(self, value, reg1, reg2, scale, dst):
tmp = value + self.registers[reg1] + (scale * self.registers[reg2])
self.registers[dst] = tmp
#
# ARITHMETIC INSTRUCTIONS
#
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 mul_i_to_r(self, src, dst):
self.registers[dst] *= src
return 0
def mul_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
def neg_r(self, src):
self.registers[src] = -self.registers[src]
#
# 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, scale):
self.registers[self.REG_SP] -= 4
tmp = value + self.registers[reg1] + (self.registers[reg2] * scale)
# 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.memory[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
def getregname(self, r):
t = r.strip()
if t == '':
return 'zero'
zassert(t[0] == '%', 'Expecting a proper register name, got [%s]' % r)
return r.split('%')[1].strip()
#
# 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] == '$':
# this is an IMMEDIATE VALUE
value = tmp.split('$')[1]
neg = 1
if value[0] == '-':
value = value[1:]
neg = -1
zassert(value.isdigit(), 'value [%s] must be a digit' % value)
return neg * int(value), 'TYPE_IMMEDIATE'
elif tmp[0] == '%':
# this is a REGISTER
register = tmp.split('%')[1]
return self.register_translate(register), 'TYPE_REGISTER'
elif tmp[0] == '.':
# this is a LABEL
targ = tmp
return targ, 'TYPE_LABEL'
elif tmp[0].isalpha() and not tmp[0].isdigit():
# this is a VARIABLE
zassert(tmp in self.vars, 'Variable %s is not declared' % tmp)
return '%d,%d,%d,1' % (self.vars[tmp], self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
elif tmp[0].isdigit() or tmp[0] == '-' or tmp[0] == '(':
# MOST GENERAL CASE: number(reg,reg) or number(reg) or number(reg,reg,number)
neg = 1
if tmp[0] == '-':
tmp = tmp[1:]
neg = -1
s = tmp.split('(')
if len(s) == 1:
# no parens -> we just assume that we have a constant value (an address), e.g., mov 10, %ax
value = neg * int(tmp)
return '%d,%d,%d,1' % (int(value), self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
elif len(s) == 2:
# here we just assume that we have something in parentheses
# e.g., mov 10(%ax) or mov 10(%ax,%bx) or mov 10(%ax,%bx,10) or mov (%ax,%bx,10) or ...
# if no leading number exists, first char should be a paren; in that case, value is just made to be 0
# otherwise we should handle either a number or a negative number
if tmp[0] != '(':
zassert(s[0].strip().isdigit() == True, 'First number should be a digit [%s]' % s[0])
value = neg * int(s[0])
else:
value = 0
t = s[1].split(')')[0].split('__BREAK__')
if len(t) == 1:
register = self.getregname(t[0])
return '%d,%d,%d,1' % (int(value), self.register_translate(register), self.register_translate('zero')), 'TYPE_MEMORY'
elif len(t) == 2:
register1 = self.getregname(t[0])
register2 = self.getregname(t[1])
return '%d,%d,%d,1' % (int(value), self.register_translate(register1), self.register_translate(register2)), 'TYPE_MEMORY'
elif len(t) == 3:
register1 = self.getregname(t[0])
register2 = self.getregname(t[1])
scale = int(t[2])
return '%d,%d,%d,%d' % (int(value), self.register_translate(register1), self.register_translate(register2), scale), 'TYPE_MEMORY'
else:
print 'mov: bad argument [%s]' % tmp
exit(1)
return
else:
print 'mov: bad argument [%s]' % tmp
exit(1)
return
zassert(True, 'mov: bad argument [%s]' % arg)
return
#
# helper function in parsing complex args to mov/lea instruction
#
def removecommas(self, cline, inargs):
inparen = False
outargs = ''
for i in range(len(inargs)):
if inargs[i] == '(':
zassert(inparen == False, 'cannot have nested parenthesis in argument [%s]' % cline)
inparen = True
if inargs[i] == ')':
zassert(inparen == True, 'cannot have right parenthesis without first having left one [%s]' % cline)
inparen = False
if inparen == True:
if inargs[i] == ',':
outargs += '__BREAK__'
else:
outargs += inargs[i]
else:
outargs += inargs[i]
zassert(inparen == False, 'did not close parentheses [%s]' % cline)
return outargs
#
# 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()
# 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 or len(tmp) == 3)
assert(tmp[0] not in self.vars)
self.vars[tmp[1]] = data
mul = 1
if len(tmp) == 3:
mul = int(tmp[2])
data += (4 * mul)
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()
# 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()
if self.verbose == True:
print 'opcode', opcode
# MAIN OPCODE LOOP
if opcode == 'mov':
# most painful one to parse (due to generic form)
# could be mov x(r1,r2,4), r3 or mov r1, (r2,r3) or ...
outargs = self.removecommas(cline, tmp[1])
rtmp = outargs.split(',')
zassert(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) == 4)
self.memory[pc] = 'self.move_m_to_r(%d, %d, %d, %d, %d)' % (int(tmp[0]), int(tmp[1]), int(tmp[2]), int(tmp[3]), dst)
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_MEMORY':
tmp = dst.split(',')
assert(len(tmp) == 4)
self.memory[pc] = 'self.move_r_to_m(%d, %d, %d, %d, %d)' % (src, int(tmp[0]), int(tmp[1]), int(tmp[2]), int(tmp[3]))
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) == 4)
self.memory[pc] = 'self.move_i_to_m(%d, %d, %d, %d, %d)' % (src, int(tmp[0]), int(tmp[1]), int(tmp[2]), int(tmp[3]))
else:
zassert(False, 'malformed mov instruction')
elif opcode == 'lea':
rtmp = tmp[1].split(',', 1)
zassert(len(tmp) == 2 and len(rtmp) == 2, 'lea: 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_MEMORY' and dtype == 'TYPE_REGISTER':
tmp = src.split(',')
assert(len(tmp) == 4)
self.memory[pc] = 'self.lea_m_to_r(%d, %d, %d, %d, %d)' % (int(tmp[0]), int(tmp[1]), int(tmp[2]), int(tmp[3]), dst)
else:
zassert(False, 'malformed lea instruction (should be memory address source to register destination')
elif opcode == 'neg':
zassert(len(tmp) == 2, 'neg: takes one argument')
arg = tmp[1].strip()
(dst, dtype) = self.getarg(arg)
zassert(dtype == 'TYPE_REGISTER', 'Can only neg a register')
self.memory[pc] = 'self.neg_r(%d)' % dst
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) == 4)
self.memory[pc] = 'self.push_m(%d,%d,%d,%d)' % (int(tmp[0]), int(tmp[1]), int(tmp[2]), int(tmp[3]))
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 == 'mul':
rtmp = tmp[1].split(',', 1)
zassert(len(tmp) == 2 and len(rtmp) == 2, 'mul: 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.mul_i_to_r(%d, %d)' % (int(src), dst)
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
self.memory[pc] = 'self.mul_r_to_r(%d, %d)' % (int(src), dst)
else:
zassert(False, 'malformed usage of add instruction')
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) == 4)
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) == 4)
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 self.printstats == True:
print 'icount',
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 self.printstats == True:
print '%6d' % self.icount,
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.
if procs.ismanual() == True:
intfreq = 1
interrupt = 1
intrand = False
interrupt = self.setint(intfreq, intrand)
self.icount = 0
# you always get one printing
print ''
self.print_headers(procs)
print ''
self.print_trace(True)
while True:
if self.headercount > 0 and self.icount % self.headercount == 0 and self.icount > 0:
print ''
self.print_headers(procs)
print ''
self.print_trace(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]
self.icount += 1
# halt instruction issued
if rc == -1:
procs.done()
# finish execution by returning from run()
if procs.numdone() == procs.getnum():
return self.icount
procs.next()
procs.restore()
self.print_trace(False)
for i in range(procs.getnum()):
print '----- Halt;Switch ----- ',
print ''
# do interrupt processing
# just counts down the interrupt counter to zero
# when it gets to 0, or when the 'yield' instruction is issued (rc=-2)
# a switch takes place
# key thing: if manual scheduling is done (procsched), interrupt
# must take place every instruction for this to work
interrupt -= 1
if interrupt == 0 or rc == -2:
interrupt = self.setint(intfreq, intrand)
curr = procs.getcurr()
procs.save()
procs.next()
procs.restore()
next = procs.getcurr()
if procs.ismanual() == False or (procs.ismanual() == True and curr != next):
self.print_trace(False)
for i in range(procs.getnum()):
print '------ Interrupt ------ ',
print ''
# END: while
return
#
# END: class cpu
#
#
# PROCESS LIST class
#
# Tracks all running processes in the program
# Also deals with manual scheduling as specified by user
#
class proclist:
def __init__(self):
self.plist = [] # list of process objects
self.active = 0 # tracks how many processes are active
self.manual = False
self.procsched = [] # list of which processes to run in what order (by ID)
self.curr = 0 # currently running process (index into procsched list)
def finalize(self, procsched):
if procsched == '':
for i in range(len(self.plist)):
self.procsched.append(i)
self.curr = 0
self.restore()
return
# in this case, user has passed in schedule
self.manual = True
for i in range(len(procsched)):
p = int(procsched[i])
if p >= self.getnum():
print 'bad schedule: cannot include a thread that does not exist (%d)' % p
exit(1)
self.procsched.append(p)
check = []
for p in self.procsched:
if p not in check:
check.append(p)
if len(check) != self.active:
print 'bad schedule: does not include ALL processes', self.procsched
exit(1)
self.curr = 0
self.restore()
return
def addproc(self, p):
self.active += 1
self.plist.append(p)
return
def ismanual(self):
return self.manual
def done(self):
p = self.procsched[self.curr]
self.plist[p].setdone()
self.active -= 1
return
def numdone(self):
return len(self.plist) - self.active
def getnum(self):
return len(self.plist)
def getcurr(self):
return self.plist[self.procsched[self.curr]]
def save(self):
self.plist[self.procsched[self.curr]].save()
return
def restore(self):
self.plist[self.procsched[self.curr]].restore()
return
def next(self):
while True:
self.curr += 1
if self.curr == len(self.procsched):
self.curr = 0
p = self.procsched[self.curr]
if self.plist[p].isdone() == False:
return
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
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('-P', '--procsched', default='', help='control exactly which thread runs when',
action='store', type='string', dest='procsched')
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('-H', '--headercount',default=-1, help='how often to print a row header', action='store', type='int', dest='headercount')
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 procsched', options.procsched
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')
procsched = options.procsched
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
hdrcount = options.headercount
#
# MAIN program
#
debug = False
debug = False
cpu = cpu(memsize, memtrace, regtrace, cctrace, options.solve, verbose, printstats, hdrcount)
# 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.addproc(process(cpu, pid, loadaddr, stack, arg))
stack -= 1000
pid += 1
# get first process ready to run
procs.finalize(procsched)
# 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()')