content: #! /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:
  