diff --git a/AsciiPainter.py b/AsciiPainter.py index 84523d6..103d412 100644 --- a/AsciiPainter.py +++ b/AsciiPainter.py @@ -5,109 +5,109 @@ import subprocess NORMAL_COLOR = '\033[0m' REG_STATUS_TO_COLOR = { - "insert": '\033[32m', - "change": '\033[33m', - "remove": '\033[31m', - "none": '\033[0m', - "jump": '\033[36m' + "insert": '\033[32m', + "change": '\033[33m', + "remove": '\033[31m', + "none": '\033[0m', + "jump": '\033[36m' } class AsciiPainter: - def __init__ (self, registers=None, max_stack_size=8, stack_draw_mode="up"): - self.registers = registers if registers else REGISTERS - self.states = [] - self.max_stack_size = max_stack_size - self.stack_draw_mode = stack_draw_mode + def __init__ (self, registers=None, max_stack_size=8, stack_draw_mode="up"): + self.registers = registers if registers else REGISTERS + self.states = [] + self.max_stack_size = max_stack_size + self.stack_draw_mode = stack_draw_mode - def saveState (self, emu, line_nr=None): - # Init state - state = [] - self.states.append(state) - # Write opcode to state - if line_nr != None: - signature = emu.getLineSignature(line_nr) - for token in signature: - state.append({ 'state': 'none', 'val': token }) - # - for i in range(3-len(state)): state.append({'state':'none', 'val': ''}) - for i in range(len(state)-3): state.pop() + def saveState (self, emu, line_nr=None): + # Init state + state = [] + self.states.append(state) + # Write opcode to state + if line_nr != None: + signature = emu.getLineSignature(line_nr) + for token in signature: + state.append({ 'state': 'none', 'val': token }) + # + for i in range(3-len(state)): state.append({'state':'none', 'val': ''}) + for i in range(len(state)-3): state.pop() + state.append({'state':'none', 'val':''}) + # Write registers to state + for reg_name in self.registers: + if reg_name =="": state.append({'state':'none', 'val':''}) - # Write registers to state - for reg_name in self.registers: - if reg_name =="": - state.append({'state':'none', 'val':''}) - continue - reg_state = emu.regState(reg_name) - state.append({ 'state': reg_state, 'val': emu.getVal(reg_name) }) - # Write stack to state - stack_base_pointer = emu.getVal('%rbp') - base_sp = emu.getVal('%rsp') - assert(isinstance(emu.getVal('%rsp'), (int, long))) + continue + reg_state = emu.regState(reg_name) + state.append({ 'state': reg_state, 'val': emu.getVal(reg_name) }) + # Write stack to state + stack_base_pointer = emu.getVal('%rbp') + base_sp = emu.getVal('%rsp') + assert(isinstance(emu.getVal('%rsp'), (int, long))) + state.append({'state':'none', 'val': ''}) + if emu.regState('m'+str(base_sp-1)) == 'remove': + state.append({'state': emu.regState('m'+str(base_sp-1)), 'val': emu.stack[base_sp-1]}) + else: + state.append({'state': 'none', 'val': ''}) + for index in range(0, self.max_stack_size): + stack_i = base_sp + index + if stack_i < emu.max_stack_size: + state.append({ 'state': emu.regState("m"+str(stack_i)), 'val': emu.stack[stack_i] }) + if stack_base_pointer == index: + stack_base_pointer = emu.stack[base_pointer] + # TODO: Draw stack frame seperators + else: state.append({'state':'none', 'val': ''}) - if emu.regState('m'+str(base_sp-1)) == 'remove': - state.append({'state': emu.regState('m'+str(base_sp-1)), 'val': emu.stack[base_sp-1]}) - else: - state.append({'state': 'none', 'val': ''}) - for index in range(0, self.max_stack_size): - stack_i = base_sp + index - if stack_i < emu.max_stack_size: - state.append({ 'state': emu.regState("m"+str(stack_i)), 'val': emu.stack[stack_i] }) - if stack_base_pointer == index: - stack_base_pointer = emu.stack[base_pointer] - # TODO: Draw stack frame seperators - else: - state.append({'state':'none', 'val': ''}) - def getWidthOfColumns(self, number_states, number_states_pr_block, number_state_vars): - widths = [0 for i in range(0, number_states_pr_block)] - for base_state in range(0, number_states, number_states_pr_block): - for var_i in range(0, number_state_vars): - for state_i in range(0, min(number_states_pr_block, number_states - base_state)): - width = len(str(self.states[base_state+state_i][var_i]['val'])) - widths[state_i] = max(widths[state_i], width) - return widths + def getWidthOfColumns(self, number_states, number_states_pr_block, number_state_vars): + widths = [0 for i in range(0, number_states_pr_block)] + for base_state in range(0, number_states, number_states_pr_block): + for var_i in range(0, number_state_vars): + for state_i in range(0, min(number_states_pr_block, number_states - base_state)): + width = len(str(self.states[base_state+state_i][var_i]['val'])) + widths[state_i] = max(widths[state_i], width) + return widths - def getWidthOfNameColumn (self, number_state_vars): - widest = 0 + def getWidthOfNameColumn (self, number_state_vars): + widest = 0 + for var_i in range(0, number_state_vars): + widest = max(widest, len(self.nameOfVar(var_i-4))) + return widest + + def to_string (self, emu): + number_states = len(self.states) + number_state_vars = len(self.states[0]) + + term_width = int(subprocess.check_output(['tput', 'cols'])) + number_states_pr_block = term_width / (10+2) - 1 + separator = '-' * term_width + l = [] + + name_fmt = '{}{:'+str(self.getWidthOfNameColumn(number_state_vars))+'} ' + column_fmt = map(lambda width: '{}{:>'+str(width)+'} ', self.getWidthOfColumns(number_states, number_states_pr_block, number_state_vars)) + + for base_state in range(0, number_states, number_states_pr_block): + l.append(separator) for var_i in range(0, number_state_vars): - widest = max(widest, len(self.nameOfVar(var_i-4))) - return widest + l.append(name_fmt.format(NORMAL_COLOR, self.nameOfVar(var_i-4))) + for state_i in range(0, min(number_states_pr_block, number_states - base_state)): + var = self.states[base_state+state_i][var_i] + l.append(column_fmt[state_i].format(REG_STATUS_TO_COLOR[var['state']], var['val'], NORMAL_COLOR)) + l.append('\n') + l.append(separator) - def to_string (self, emu): - number_states = len(self.states) - number_state_vars = len(self.states[0]) + return ''.join(l) - term_width = int(subprocess.check_output(['tput', 'cols'])) - number_states_pr_block = term_width / (10+2) - 1 - separator = '-' * term_width - l = [] + def nameOfVar (self, i): + if i < 0: return '' + if i < len(self.registers): + return self.registers[i] + elif i != len(self.registers): + state_i = i-len(self.registers) - 2 + return 'TOS{}{}'.format('+' if state_i < 0 else '-', abs(state_i)) + else: + return '' - name_fmt = '{}{:'+str(self.getWidthOfNameColumn(number_state_vars))+'} ' - column_fmt = map(lambda width: '{}{:>'+str(width)+'} ', self.getWidthOfColumns(number_states, number_states_pr_block, number_state_vars)) - - for base_state in range(0, number_states, number_states_pr_block): - l.append(separator) - for var_i in range(0, number_state_vars): - l.append(name_fmt.format(NORMAL_COLOR, self.nameOfVar(var_i-4))) - for state_i in range(0, min(number_states_pr_block, number_states - base_state)): - var = self.states[base_state+state_i][var_i] - l.append(column_fmt[state_i].format(REG_STATUS_TO_COLOR[var['state']], var['val'], NORMAL_COLOR)) - l.append('\n') - l.append(separator) - - return ''.join(l) - - def nameOfVar (self, i): - if i < 0: return '' - if i < len(self.registers): - return self.registers[i] - elif i != len(self.registers): - state_i = i-len(self.registers) - 2 - return 'TOS{}{}'.format('+' if state_i < 0 else '-', abs(state_i)) - else: - return '' - - def __str__ (self): - return 'TikzPainter[]' + def __str__ (self): + return 'AsciiPainter[]' diff --git a/Emulator.py b/Emulator.py index 0f0b6c2..f1af3d2 100644 --- a/Emulator.py +++ b/Emulator.py @@ -9,153 +9,153 @@ REGISTERS=["%rax", "%rbx", "%rcx", "%rdx", "%rsp", "%rbp", "%rsi", "%rdi", class CodeParseException (BaseException): - def __init__ (self, line_nr, string): - self.line_nr = line_nr - self.string = string + def __init__ (self, line_nr, string): + self.line_nr = line_nr + self.string = string class InternalExecutionException (BaseException): - pass + pass class Emulator: - def __init__ (self, source_text, max_stack_size=1000): - self.source = source_text - self.registers = {} - for reg_name in REGISTERS: - self.registers[reg_name] = Junk.Junk() - self.stack = {} - for i in range(max_stack_size): - self.stack[i] = Junk.Junk() - self.code = [] - self.labels = {} - self.changes = {} - self.status = {'i':True} - self.last_sp = 0 - self.max_stack_size = 0 - index = 0 - for line in iter(source_text.splitlines()): - index = self.processSourceLine(line, index) + def __init__ (self, source_text, max_stack_size=1000): + self.source = source_text + self.registers = {} + for reg_name in REGISTERS: + self.registers[reg_name] = Junk.Junk() + self.stack = {} + for i in range(max_stack_size): + self.stack[i] = Junk.Junk() + self.code = [] + self.labels = {} + self.changes = {} + self.status = {'i':True} + self.last_sp = 0 + self.max_stack_size = 0 + index = 0 + for line in iter(source_text.splitlines()): + index = self.processSourceLine(line, index) - def setStack (self, *stack_list, **kwargs): - """ - Sets various stack elements, starting from 0 and going up. - Automatically sets rsp. This can be disabled by passing set_rsp=False. - """ - i = len(self.stack) - for element in stack_list: - i -= 1 - self.stack[i] = element - self.setRegs(rsp=i) - self.last_sp = i-1 + def setStack (self, *stack_list, **kwargs): + """ + Sets various stack elements, starting from 0 and going up. + Automatically sets rsp. This can be disabled by passing set_rsp=False. + """ + i = len(self.stack) + for element in stack_list: + i -= 1 + self.stack[i] = element + self.setRegs(rsp=i) + self.last_sp = i-1 - def setRegs (self, **reg_dict): - for reg_name, reg_val in reg_dict.iteritems(): - assert(reg_name[0] != '%') - self.registers["%"+reg_name] = reg_val + def setRegs (self, **reg_dict): + for reg_name, reg_val in reg_dict.iteritems(): + assert(reg_name[0] != '%') + self.registers["%"+reg_name] = reg_val - def getVal (self, val_text): - if val_text[0] == "$": - return int(val_text[1:]) - elif val_text[0] == "%": - return self.registers[val_text] - else: - raise ValueError('{} is not an usable value name'.format(val_text)) + def getVal (self, val_text): + if val_text[0] == "$": + return int(val_text[1:]) + elif val_text[0] == "%": + return self.registers[val_text] + else: + raise ValueError('{} is not an usable value name'.format(val_text)) - def compareVal (self, valT1, valT2): - val1 = self.getVal(valT2) - val2 = self.getVal(valT1) - if isinstance(val1,Junk.Junk) or isinstance(val2,Junk.Junk): - self.status["i"] = (True,) - return - self.status["g"] = val1>val2 - self.status["l"] = val1val2 + self.status["l"] = val10 and self.status["i"]: - raise Junk.JunkComparisonException(*self.status['i']) - and_, or_ = True, False - for cnd_name, cnd_val in conditions.iteritems(): - if self.status[cnd_name] == cnd_val: - or_ = True - else: - and_ = False - if or_ if cond_op=="or" else and_: - self.registers['%rip'] = self.labels[label] - return True - else: - return False + def jump (self, label, cond_op="or", **conditions): + if len(conditions)>0 and self.status["i"]: + raise Junk.JunkComparisonException(*self.status['i']) + and_, or_ = True, False + for cnd_name, cnd_val in conditions.iteritems(): + if self.status[cnd_name] == cnd_val: + or_ = True + else: + and_ = False + if or_ if cond_op=="or" else and_: + self.registers['%rip'] = self.labels[label] + return True + else: + return False - def iterate (self): - old_rip = self.registers['%rip'] - self.last_sp = self.registers['%rsp'] - if self.registers['%rip'] >= len(self.code) or isinstance(self.registers['%rip'], Junk.Junk): - return None - if not isinstance(self.registers['%rip'], (int, long)): - raise InternalExecutionException("Register %rip should be integer, but was "+str(self.registers['%rip'])) - instruct = self.code[self.registers['%rip']] - opcode = instruct[0] - self.changes = {} + def iterate (self): + old_rip = self.registers['%rip'] + self.last_sp = self.registers['%rsp'] + if self.registers['%rip'] >= len(self.code) or isinstance(self.registers['%rip'], Junk.Junk): + return None + if not isinstance(self.registers['%rip'], (int, long)): + raise InternalExecutionException("Register %rip should be integer, but was "+str(self.registers['%rip'])) + instruct = self.code[self.registers['%rip']] + opcode = instruct[0] + self.changes = {} - OPCODES[opcode](self, *instruct[1:]) + OPCODES[opcode](self, *instruct[1:]) - if self.registers['%rip'] == old_rip: - self.registers['%rip'] += 1 - else: - self.changedRegisters(('%rip',"jump")) + if self.registers['%rip'] == old_rip: + self.registers['%rip'] += 1 + else: + self.changedRegisters(('%rip',"jump")) - return old_rip + return old_rip - def __iter__(self): - return self + def __iter__(self): + return self - def next(self): - output = self.iterate() - if output is None: - raise StopIteration() - else: - return output + def next(self): + output = self.iterate() + if output is None: + raise StopIteration() + else: + return output - def getUsedRegisters (self): - return [reg_name for reg_name, reg_val in self.registers.iteritems() if not isinstance(reg_val, Junk.Junk)] + def getUsedRegisters (self): + return [reg_name for reg_name, reg_val in self.registers.iteritems() if not isinstance(reg_val, Junk.Junk)] diff --git a/Junk.py b/Junk.py index 4e097e5..9dce275 100644 --- a/Junk.py +++ b/Junk.py @@ -6,27 +6,27 @@ class JunkComparisonException (BaseException): class Junk: - def __init__ (self, represents = None): - assert(represents == None or isinstance(represents, basestring)) - self.repr = represents + def __init__ (self, represents = None): + assert(represents == None or isinstance(represents, basestring)) + self.repr = represents - def __str__ (self): - if self.repr: - return '['+self.repr+']' - return "[junk]" + def __str__ (self): + if self.repr: + return '['+self.repr+']' + return "[junk]" - def __repr__ (self): - return self.__str__() + def __repr__ (self): + return self.__str__() - def __add__ (self, other): - return self + def __add__ (self, other): + return self - def __radd__ (self, other): - return self + def __radd__ (self, other): + return self - def __sub__ (self, other): - return self + def __sub__ (self, other): + return self - def __rsub__ (self, other): - return self + def __rsub__ (self, other): + return self diff --git a/TikzPainter.py b/TikzPainter.py index e69743b..fd6e2e1 100644 --- a/TikzPainter.py +++ b/TikzPainter.py @@ -2,128 +2,128 @@ import re REG_STATUS_TO_COLOR = { - "insert": "green", - "change": "yellow", - "remove": "red", - "none": "black", - "jump": "orange" + "insert": "green", + "change": "yellow", + "remove": "red", + "none": "black", + "jump": "orange" } DEFAULT_OPTIONS = """register_node/.style={rectangle, draw=black!30, - fill=black!5, very thick, minimum size=0.5, - minimum width=20mm}, text_node/.style={}""" + fill=black!5, very thick, minimum size=0.5, + minimum width=20mm}, text_node/.style={}""" class TikzPainter: - def __init__ (self, registers=None, max_stack_size=8, stack_draw_mode="up", options=None): - self.registers = registers if registers else REGISTERS - self.text = [] - self.pos_x = 0 - self.max_stack_size = max_stack_size - self.stack_draw_mode = stack_draw_mode - self.options = options if options else DEFAULT_OPTIONS + def __init__ (self, registers=None, max_stack_size=8, stack_draw_mode="up", options=None): + self.registers = registers if registers else REGISTERS + self.text = [] + self.pos_x = 0 + self.max_stack_size = max_stack_size + self.stack_draw_mode = stack_draw_mode + self.options = options if options else DEFAULT_OPTIONS - def addText (self, str, *args): - text = re.sub(r"[^\\]%", "\\%", str.format(*args)) - self.text.append(text) + def addText (self, str, *args): + text = re.sub(r"[^\\]%", "\\%", str.format(*args)) + self.text.append(text) - def getRegColor (self, reg_state): - return REG_STATUS_TO_COLOR[reg_state] + def getRegColor (self, reg_state): + return REG_STATUS_TO_COLOR[reg_state] - def drawStackUpward (self, pos, emu, x): - base_pointer = emu.getVal('%rbp') + def drawStackUpward (self, pos, emu, x): + base_pointer = emu.getVal('%rbp') - pos -= 1.5 - base_sp = min(emu.last_sp, emu.getVal('%rsp')) - pos += 0.5 * max(0, emu.getVal('%rsp')-emu.last_sp) - assert(isinstance(emu.getVal('%rsp'), (int, long))) - for index in range(base_sp, 3+min(emu.max_stack_size, emu.getVal('%rsp')+self.max_stack_size)): - reg_state = emu.regState("m"+str(index)) - reg_dect = "register_node, fill="+self.getRegColor(reg_state)+"!10" - self.addText("\t\\node[{}]() at ({}, {}){{{}}};\n", - reg_dect, x, pos, emu.stack[index]) - if base_pointer == index: - self.addText("\t\\draw ({0},{2}) -- ({1},{2});\n",x-1.1,x+1.1,pos-0.25) - base_pointer = emu.stack[base_pointer] - pos -= 0.5 - return pos, x + pos -= 1.5 + base_sp = min(emu.last_sp, emu.getVal('%rsp')) + pos += 0.5 * max(0, emu.getVal('%rsp')-emu.last_sp) + assert(isinstance(emu.getVal('%rsp'), (int, long))) + for index in range(base_sp, 3+min(emu.max_stack_size, emu.getVal('%rsp')+self.max_stack_size)): + reg_state = emu.regState("m"+str(index)) + reg_dect = "register_node, fill="+self.getRegColor(reg_state)+"!10" + self.addText("\t\\node[{}]() at ({}, {}){{{}}};\n", + reg_dect, x, pos, emu.stack[index]) + if base_pointer == index: + self.addText("\t\\draw ({0},{2}) -- ({1},{2});\n",x-1.1,x+1.1,pos-0.25) + base_pointer = emu.stack[base_pointer] + pos -= 0.5 + return pos, x - def drawStackDownward (self, pos, emu, x): - pos -= 1 - for index in range(emu.getVal('%rsp')-8, max(emu.last_sp, emu.getVal('%rsp'))): - reg_state = emu.regState("m"+str(index)) - reg_dect = "register_node, fill="+self.getRegColor(reg_state)+"!10" - self.addText("\t\\node[{}]() at ({}, {}){{{}}};\n", - reg_dect, x, pos, emu.stack[index]) - pos -= 0.5 - return pos, x + def drawStackDownward (self, pos, emu, x): + pos -= 1 + for index in range(emu.getVal('%rsp')-8, max(emu.last_sp, emu.getVal('%rsp'))): + reg_state = emu.regState("m"+str(index)) + reg_dect = "register_node, fill="+self.getRegColor(reg_state)+"!10" + self.addText("\t\\node[{}]() at ({}, {}){{{}}};\n", + reg_dect, x, pos, emu.stack[index]) + pos -= 0.5 + return pos, x - def saveState (self, emu, line_nr=None): - x = (self.pos_x+1)*2.5-0.5 - self.pos_x += 1 + def saveState (self, emu, line_nr=None): + x = (self.pos_x+1)*2.5-0.5 + self.pos_x += 1 - # Draw register cells - pos = 0.5 - for reg_name in self.registers: - pos -= 0.5 - if reg_name =="": - continue - reg_state = emu.regState(reg_name) - reg_dect = "register_node, fill="+self.getRegColor(reg_state)+"!10" - self.addText("\t\\node[{}]() at ({}, {}){{{}}};\n", - reg_dect, x, pos, emu.getVal(reg_name)) + # Draw register cells + pos = 0.5 + for reg_name in self.registers: + pos -= 0.5 + if reg_name =="": + continue + reg_state = emu.regState(reg_name) + reg_dect = "register_node, fill="+self.getRegColor(reg_state)+"!10" + self.addText("\t\\node[{}]() at ({}, {}){{{}}};\n", + reg_dect, x, pos, emu.getVal(reg_name)) - # Draw stack - if self.stack_draw_mode == "up": - pos, x = self.drawStackUpward(pos, emu, x) - else: - pos, x = self.drawStackDownward(pos, emu, x) + # Draw stack + if self.stack_draw_mode == "up": + pos, x = self.drawStackUpward(pos, emu, x) + else: + pos, x = self.drawStackDownward(pos, emu, x) - # Draw line signature - if line_nr == None: - return - pos = 2 - signature = emu.getLineSignature(line_nr) - for token in signature: - self.addText("\t\\node[text_node]() at ({}, {}){{{}}};\n", - x-1.25, pos, token) - pos -= 0.5 + # Draw line signature + if line_nr == None: + return + pos = 2 + signature = emu.getLineSignature(line_nr) + for token in signature: + self.addText("\t\\node[text_node]() at ({}, {}){{{}}};\n", + x-1.25, pos, token) + pos -= 0.5 - def drawNames (self, emu): - x = 0 - pos = 0 - for reg_name in self.registers: - self.addText("\t\\node[text_node]() at ({}, {}){{{}}};\n", - x, pos, reg_name[1:]) - pos -= 0.5 + def drawNames (self, emu): + x = 0 + pos = 0 + for reg_name in self.registers: + self.addText("\t\\node[text_node]() at ({}, {}){{{}}};\n", + x, pos, reg_name[1:]) + pos -= 0.5 - # Draw stack - if self.stack_draw_mode == "up": - self.drawStackNamesUpward(pos, emu, x) - else: - self.drawStackNamesDownward(pos, emu, x) + # Draw stack + if self.stack_draw_mode == "up": + self.drawStackNamesUpward(pos, emu, x) + else: + self.drawStackNamesDownward(pos, emu, x) - def drawStackNamesUpward (self, pos, emu, x): - self.addText("\t\\node[text_node]() at ({}, {}){{TOS+1}};\n", - x, pos-0.5) - self.addText("\t\\node[text_node]() at ({}, {}){{TOS}};\n", - x, pos-1.0) - pos -= 1 - for index in range(1,self.max_stack_size): - pos -= 0.5 - self.addText("\t\\node[text_node]() at ({}, {}){{TOS-{}}};\n", - x, pos, index) + def drawStackNamesUpward (self, pos, emu, x): + self.addText("\t\\node[text_node]() at ({}, {}){{TOS+1}};\n", + x, pos-0.5) + self.addText("\t\\node[text_node]() at ({}, {}){{TOS}};\n", + x, pos-1.0) + pos -= 1 + for index in range(1,self.max_stack_size): + pos -= 0.5 + self.addText("\t\\node[text_node]() at ({}, {}){{TOS-{}}};\n", + x, pos, index) - def to_string (self, emu): - PRE = """ \\documentclass{standalone} - \\usepackage{tikz} - \\begin{document} - \\begin{tikzpicture}["""+self.options+"]" - POST = """\\end{tikzpicture} - \\end{document}""" - self.drawNames(emu) - return PRE + "".join(self.text) + POST + def to_string (self, emu): + PRE = """ \\documentclass{standalone} + \\usepackage{tikz} + \\begin{document} + \\begin{tikzpicture}["""+self.options+"]" + POST = """\\end{tikzpicture} + \\end{document}""" + self.drawNames(emu) + return PRE + "".join(self.text) + POST - def __str__ (self): - return 'TikzPainter[]' + def __str__ (self): + return 'TikzPainter[]' diff --git a/main.py b/main.py index 85c9ddb..f1e1446 100755 --- a/main.py +++ b/main.py @@ -29,75 +29,74 @@ PAINTER_ID_TO_CLASS = { } def parse_args (): - parser = argparse.ArgumentParser(description = ARG_DESC, epilog = LICENSE) - parser.add_argument('-i', '--input-file', default='-', help = 'File to use as input', dest = 'input-file') - parser.add_argument('-o', '--output-file', default='-', help = 'File to use as output', dest = 'output-file') - parser.add_argument('-p', '--painter', default='ascii', help = 'Drawer to use', dest = 'painter-name', choices=PAINTER_ID_TO_CLASS.keys()) - for register in REGISTERS: - parser.add_argument('--'+register[1:], nargs = 1, type = int, default = None, dest = register, help = 'the initial value of the register') - args = vars(parser.parse_args()) + parser = argparse.ArgumentParser(description = ARG_DESC, epilog = LICENSE) + parser.add_argument('-i', '--input-file', default='-', help = 'File to use as input', dest = 'input-file') + parser.add_argument('-o', '--output-file', default='-', help = 'File to use as output', dest = 'output-file') + parser.add_argument('-p', '--painter', default='ascii', help = 'Drawer to use', dest = 'painter-name', choices=PAINTER_ID_TO_CLASS.keys()) + for register in REGISTERS: + parser.add_argument('--'+register[1:], nargs = 1, type = int, default = None, dest = register, help = 'the initial value of the register') + args = vars(parser.parse_args()) - # Determine args # - registers_init = {} - for register in REGISTERS: + # Determine args # + registers_init = {} + for register in REGISTERS: registers_init[register[1:]] = Junk.Junk('old '+register[1:]) - # - registers_init['rip'] = 0 - registers_init['rsp'] = 0 - registers_init['rbp'] = Junk.Junk('old bp') - # - for register in REGISTERS: - if args[register]: - registers_init[register[1:]] = args[register][0] + # + registers_init['rip'] = 0 + registers_init['rsp'] = 0 + registers_init['rbp'] = Junk.Junk('old bp') + # + for register in REGISTERS: + if args[register]: + registers_init[register[1:]] = args[register][0] - # - program = "" - if args['input-file'] == '-': - program = sys.stdin.read() - else: - with open(args['input-file']) as file: - program = file.read() + # + program = "" + if args['input-file'] == '-': + program = sys.stdin.read() + else: + with open(args['input-file']) as file: + program = file.read() - # Output file: - output_file = None - if args['output-file'] == '-': - output_file = sys.stdout - else: - output_file = open(args['output-file'], 'w') + # Output file: + output_file = None + if args['output-file'] == '-': + output_file = sys.stdout + else: + output_file = open(args['output-file'], 'w') + # Determine painter class + painter_class = PAINTER_ID_TO_CLASS[args['painter-name']] - # Determine painter class - painter_class = PAINTER_ID_TO_CLASS[args['painter-name']] - - ## Return - return (program, registers_init, output_file, painter_class) + ## Return + return (program, registers_init, output_file, painter_class) def main (): - (program, registers_init, output_file, painter_class) = parse_args() + (program, registers_init, output_file, painter_class) = parse_args() - # Determine registers to display - registers_to_draw = ["%rip","%rbp","%rsp", ""] - for match in re.findall(r"%r[a-z0-9]{1,2}", program): - if match not in registers_to_draw: - registers_to_draw.append(match) + # Determine registers to display + registers_to_draw = ["%rip","%rbp","%rsp", ""] + for match in re.findall(r"%r[a-z0-9]{1,2}", program): + if match not in registers_to_draw: + registers_to_draw.append(match) - # Setup emulator and drawer - emu = Emulator(program) - emu.setRegs(**registers_init) - emu.setStack(Junk.Junk("junk..."), Junk.Junk("call eip")) + # Setup emulator and drawer + emu = Emulator(program) + emu.setRegs(**registers_init) + emu.setStack(Junk.Junk("junk..."), Junk.Junk("call eip")) - painter = painter_class(registers_to_draw) + painter = painter_class(registers_to_draw) - # Iteratively draw states. - painter.saveState(emu) - for line_nr in emu: - painter.saveState(emu, line_nr) + # Iteratively draw states. + painter.saveState(emu) + for line_nr in emu: + painter.saveState(emu, line_nr) - # Display result - output_file.write(painter.to_string(emu)) - output_file.close() + # Display result + output_file.write(painter.to_string(emu)) + output_file.close() if __name__ == "__main__": - main() + main() diff --git a/opcodes.py b/opcodes.py index aa77aab..c053a6c 100644 --- a/opcodes.py +++ b/opcodes.py @@ -2,10 +2,10 @@ OPCODES = {} def add_opcode (*opcode_names): - def wrapped (func): - for name in opcode_names: - OPCODES[name] = func - return wrapped + def wrapped (func): + for name in opcode_names: + OPCODES[name] = func + return wrapped ################################################################################ @@ -13,76 +13,76 @@ def add_opcode (*opcode_names): @add_opcode("push","pushq") def push(emu, value): - emu.pushToStack(emu.getVal(value)) + emu.pushToStack(emu.getVal(value)) @add_opcode("pop","popq") def pop(emu, target_reg): - emu.registers[target_reg] = emu.popFromStack() - emu.changedRegisters((target_reg,"change")) + emu.registers[target_reg] = emu.popFromStack() + emu.changedRegisters((target_reg,"change")) # Arithmetic @add_opcode("mov","movq") def mov(emu, from_reg, target_reg): - emu.registers[target_reg] = emu.getVal(from_reg) - emu.changedRegisters((target_reg,"change")) + emu.registers[target_reg] = emu.getVal(from_reg) + emu.changedRegisters((target_reg,"change")) @add_opcode("add","addq") def add(emu, add_val, target_reg): - emu.registers[target_reg] += emu.getVal(add_val) - emu.changedRegisters((target_reg,"change")) + emu.registers[target_reg] += emu.getVal(add_val) + emu.changedRegisters((target_reg,"change")) @add_opcode("sub","subq") def sub(emu, sub_val, target_reg): - emu.registers[target_reg] -= emu.getVal(sub_val) - emu.changedRegisters((target_reg,"change")) + emu.registers[target_reg] -= emu.getVal(sub_val) + emu.changedRegisters((target_reg,"change")) # Comparison and conditional jumping @add_opcode("cmp","cmpq") def comp(emu, val1, val2): - emu.compareVal(val1, val2) + emu.compareVal(val1, val2) @add_opcode("jg") def jg(emu, label): - emu.jump(label, g=True) + emu.jump(label, g=True) @add_opcode("jl") def jl(emu, label): - emu.jump(label, l=True) + emu.jump(label, l=True) @add_opcode("je") def je(emu, label): - emu.jump(label, e=True) + emu.jump(label, e=True) @add_opcode("jge") def jge(emu, label): - emu.jump(label, "or", g=True, e=True) + emu.jump(label, "or", g=True, e=True) @add_opcode("jle") def jle(emu, label): - emu.jump(label, "or", l=True, e=True) + emu.jump(label, "or", l=True, e=True) @add_opcode("jne") def jne(emu, label): - emu.jump(label, e=False) + emu.jump(label, e=False) # Unconditional Jumping @add_opcode("jmp") def jmp(emu, label): - emu.registers['%rip'] = emu.labels[label] + emu.registers['%rip'] = emu.labels[label] @add_opcode("call") def call(emu, label): - emu.pushToStack(emu.registers['%rip']+1) - emu.registers['%rip'] = emu.labels[label] + emu.pushToStack(emu.registers['%rip']+1) + emu.registers['%rip'] = emu.labels[label] @add_opcode("leave") def leave(emu): - emu.registers["%rsp"] = emu.registers["%rbp"] - emu.registers["%rbp"] = emu.popFromStack() + emu.registers["%rsp"] = emu.registers["%rbp"] + emu.registers["%rbp"] = emu.popFromStack() @add_opcode("ret") def ret(emu): - emu.registers['%rip'] = emu.popFromStack() + emu.registers['%rip'] = emu.popFromStack() diff --git a/tests/tests.py b/tests/tests.py index c73a8b2..21dc991 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -9,49 +9,49 @@ import infernal tests = [] def add_test(name, result, register, code): - tests.append((name, result, register, code)) + tests.append((name, result, register, code)) def printf (str, *args): - print(str.format(*args)) + print(str.format(*args)) def execute_tests(): - print("Executing tests!") - total_tests = 0 - failed_tests = 0 - error_tests = 0 - for name, result, register, code in tests: - total_tests += 1 - line_nr = None - try: - emu = infernal.Emulator(code) - emu.setStack("junk...", "calling eip") - emu.setRegs( rip = 0, rbp = 'old bp') - except infernal.CodeParseException as e: - error_tests += 1 - printf("Encountered error when parsing {}, at line {}: {}", - name, e.line_nr, e.str) - try: - for line_nr in emu: - pass - if isinstance(result, BaseException): - printf("Error should have happened in {}, but did not", name) - failed_tests += 1 - output = emu.getVal(register) - if output != result: - failed_tests += 1 - printf("Failed in {}. {} was {}, should be {}", - name, register, output, result) - except BaseException as e: - print(e) - if not isinstance(result, BaseException) or not isinstance(e, result): - error_tests += 1 - printf("Encountered error in {}, at operation {}: {}", - name, emu.getVal('%rip'), e) - traceback.print_exc() - printf("Tests done! {}/{}.", - total_tests - failed_tests - error_tests, total_tests) - printf("{} failed, and {} encountered a python error", - failed_tests, error_tests) + print("Executing tests!") + total_tests = 0 + failed_tests = 0 + error_tests = 0 + for name, result, register, code in tests: + total_tests += 1 + line_nr = None + try: + emu = infernal.Emulator(code) + emu.setStack("junk...", "calling eip") + emu.setRegs( rip = 0, rbp = 'old bp') + except infernal.CodeParseException as e: + error_tests += 1 + printf("Encountered error when parsing {}, at line {}: {}", + name, e.line_nr, e.str) + try: + for line_nr in emu: + pass + if isinstance(result, BaseException): + printf("Error should have happened in {}, but did not", name) + failed_tests += 1 + output = emu.getVal(register) + if output != result: + failed_tests += 1 + printf("Failed in {}. {} was {}, should be {}", + name, register, output, result) + except BaseException as e: + print(e) + if not isinstance(result, BaseException) or not isinstance(e, result): + error_tests += 1 + printf("Encountered error in {}, at operation {}: {}", + name, emu.getVal('%rip'), e) + traceback.print_exc() + printf("Tests done! {}/{}.", + total_tests - failed_tests - error_tests, total_tests) + printf("{} failed, and {} encountered a python error", + failed_tests, error_tests) ################################################################################ # Arithmetic Operations @@ -81,77 +81,77 @@ subq %rax, %rsi # Branching branch_tests = [ - ("jg",10,">",5,0), ("jg",5,">",5,1), ("jg",5,">",10,1), - ("jl",10,"<",5,1), ("jl",5,"<",5,1), ("jl",5,"<",10,0), - ("je",10,"==",5,1),("je",5,"==",5,0),("je",5,"==",10,1), - ("jge",10,">=",5,0),("jge",5,">=",5,0),("jge",5,">=",10,1), - ("jle",10,"<=",5,1),("jle",5,"<=",5,0),("jle",5,"<=",10,0), - ("jne",10,"!=",5,0),("jne",5,"!=",5,1),("jne",5,"!=",10,0), + ("jg",10,">",5,0), ("jg",5,">",5,1), ("jg",5,">",10,1), + ("jl",10,"<",5,1), ("jl",5,"<",5,1), ("jl",5,"<",10,0), + ("je",10,"==",5,1),("je",5,"==",5,0),("je",5,"==",10,1), + ("jge",10,">=",5,0),("jge",5,">=",5,0),("jge",5,">=",10,1), + ("jle",10,"<=",5,1),("jle",5,"<=",5,0),("jle",5,"<=",10,0), + ("jne",10,"!=",5,0),("jne",5,"!=",5,1),("jne",5,"!=",10,0), ] for jump_instruct, a, comp, b, result in branch_tests: - add_test("branch {a} {comp} {b}={result}".format(a=a,comp=comp,b=b,result=not result), result, "%rsi", """ - start: cmpq ${b}, ${a} - {jump_instruct} true - movq $1, %rsi - jmp return - true: movq $0, %rsi - return: ret - """.format(a=a,b=b,jump_instruct=jump_instruct)) + add_test("branch {a} {comp} {b}={result}".format(a=a,comp=comp,b=b,result=not result), result, "%rsi", """ + start: cmpq ${b}, ${a} + {jump_instruct} true + movq $1, %rsi + jmp return + true: movq $0, %rsi + return: ret + """.format(a=a,b=b,jump_instruct=jump_instruct)) ################################################################################ # Junk Comparisons add_test("invalid comparison 1", infernal.JunkComparisonException, "None", """ start: movq $100, %rsp # Set stack pointer to a random position. - popq %rsi # Move a Junk value into %rsi - cmpq $10, %rsi # Do a Junk comparison, which triggers the `i` comp - # virtual register. - jg start # Attempt to do a jump to the start, if (Junk-10)>0, - # which makes no sense, and thus throws an error. + popq %rsi # Move a Junk value into %rsi + cmpq $10, %rsi # Do a Junk comparison, which triggers the `i` comp + # virtual register. + jg start # Attempt to do a jump to the start, if (Junk-10)>0, + # which makes no sense, and thus throws an error. """) add_test("invalid addition 1", infernal.JunkComparisonException, "None", """ start: movq $100, %rsp # Set stack pointer to a random position. - popq %rsi # Move a Junk value into %rsi - addq %rsi, %rsp # Adds Junk to 101, which produces Junk. - cmpq $10, %rsp # Do a Junk comparison, which triggers the `i` comp - # virtual register. - jg start # Attempt to do a jump to the start, if (Junk-10)>0, - # which makes no sense, and thus throws an error. + popq %rsi # Move a Junk value into %rsi + addq %rsi, %rsp # Adds Junk to 101, which produces Junk. + cmpq $10, %rsp # Do a Junk comparison, which triggers the `i` comp + # virtual register. + jg start # Attempt to do a jump to the start, if (Junk-10)>0, + # which makes no sense, and thus throws an error. """) add_test("invalid addition 2", infernal.JunkComparisonException, "None", """ start: movq $100, %rsp # Set stack pointer to a random position. - popq %rsi # Move a Junk value into %rsi - addq %rsp, %rsi # Adds 101 to Junk, which produces Junk. - cmpq $10, %rsi # Do a Junk comparison, which triggers the `i` comp - # virtual register. - jg start # Attempt to do a jump to the start, if (Junk-10)>0, - # which makes no sense, and thus throws an error. + popq %rsi # Move a Junk value into %rsi + addq %rsp, %rsi # Adds 101 to Junk, which produces Junk. + cmpq $10, %rsi # Do a Junk comparison, which triggers the `i` comp + # virtual register. + jg start # Attempt to do a jump to the start, if (Junk-10)>0, + # which makes no sense, and thus throws an error. """) add_test("invalid subtraction 1", infernal.JunkComparisonException, "None", """ start: movq $100, %rsp # Set stack pointer to a random position. - popq %rsi # Move a Junk value into %rsi - subq %rsi, %rsp # Adds Junk to 101, which produces Junk. - cmpq $10, %rsp # Do a Junk comparison, which triggers the `i` comp - # virtual register. - jg start # Attempt to do a jump to the start, if (Junk-10)>0, - # which makes no sense, and thus throws an error. + popq %rsi # Move a Junk value into %rsi + subq %rsi, %rsp # Adds Junk to 101, which produces Junk. + cmpq $10, %rsp # Do a Junk comparison, which triggers the `i` comp + # virtual register. + jg start # Attempt to do a jump to the start, if (Junk-10)>0, + # which makes no sense, and thus throws an error. """) add_test("invalid subtraction 2", infernal.JunkComparisonException, "None", """ start: movq $100, %rsp # Set stack pointer to a random position. - popq %rsi # Move a Junk value into %rsi - subq %rsp, %rsi # Adds 101 to Junk, which produces Junk. - cmpq $10, %rsi # Do a Junk comparison, which triggers the `i` comp - # virtual register. - jg start # Attempt to do a jump to the start, if (Junk-10)>0, - # which makes no sense, and thus throws an error. + popq %rsi # Move a Junk value into %rsi + subq %rsp, %rsi # Adds 101 to Junk, which produces Junk. + cmpq $10, %rsi # Do a Junk comparison, which triggers the `i` comp + # virtual register. + jg start # Attempt to do a jump to the start, if (Junk-10)>0, + # which makes no sense, and thus throws an error. """) ################################################################################ if __name__ == "__main__": - execute_tests() + execute_tests()