|
|
- #! /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()')
-
-
-
-
|