Added Junk value, for which all operations are
defined to produce junk. Any comparison involving junk triggers the i status register. Any conditional jump triggered when the i register is true, triggers a python level exception.
This commit is contained in:
parent
6d7de5ae75
commit
8b146efe68
41
Emulator.py
41
Emulator.py
|
@ -1,24 +1,33 @@
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
import Junk
|
||||||
from opcodes import OPCODES
|
from opcodes import OPCODES
|
||||||
|
|
||||||
REGISTERS=["%rax", "%rbx", "%rcx", "%rdx", "%rsp", "%rbp", "%rsi", "%rdi",
|
REGISTERS=["%rax", "%rbx", "%rcx", "%rdx", "%rsp", "%rbp", "%rsi", "%rdi",
|
||||||
"%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"]
|
"%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"]
|
||||||
|
JUNK_VALUE = Junk.Junk()
|
||||||
|
|
||||||
|
class CodeParseException (BaseException):
|
||||||
|
|
||||||
|
def __init__ (self, line_nr, string):
|
||||||
|
self.line_nr = line_nr
|
||||||
|
self.string = string
|
||||||
|
|
||||||
class Emulator:
|
class Emulator:
|
||||||
|
|
||||||
def __init__ (self, source_text, max_stack_size=1000):
|
def __init__ (self, source_text, max_stack_size=1000):
|
||||||
|
self.source = source_text
|
||||||
self.registers = {}
|
self.registers = {}
|
||||||
for reg_name in REGISTERS:
|
for reg_name in REGISTERS:
|
||||||
self.registers[reg_name] = "Junk"
|
self.registers[reg_name] = JUNK_VALUE
|
||||||
self.stack = {}
|
self.stack = {}
|
||||||
for i in range(max_stack_size):
|
for i in range(max_stack_size):
|
||||||
self.stack[i] = 0
|
self.stack[i] = JUNK_VALUE
|
||||||
self.code = []
|
self.code = []
|
||||||
self.labels = {}
|
self.labels = {}
|
||||||
self.changes = {}
|
self.changes = {}
|
||||||
self.status = {}
|
self.status = {'i':True}
|
||||||
self.last_sp = 0
|
self.last_sp = 0
|
||||||
self.max_stack_size = 0
|
self.max_stack_size = 0
|
||||||
index = 0
|
index = 0
|
||||||
|
@ -48,14 +57,18 @@ class Emulator:
|
||||||
elif val_text[0] == "%":
|
elif val_text[0] == "%":
|
||||||
return self.registers[val_text]
|
return self.registers[val_text]
|
||||||
else:
|
else:
|
||||||
raise "NOT A VALUE! UGHGUIHGUE!"
|
raise ValueError('{} is not an usable value name'.format(val_text))
|
||||||
|
|
||||||
def compareVal (self, valT1, valT2):
|
def compareVal (self, valT1, valT2):
|
||||||
val1 = self.getVal(valT2)
|
val1 = self.getVal(valT2)
|
||||||
val2 = self.getVal(valT1)
|
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["g"] = val1>val2
|
||||||
self.status["l"] = val1<val2
|
self.status["l"] = val1<val2
|
||||||
self.status["e"] = val1==val2
|
self.status["e"] = val1==val2
|
||||||
|
self.status["i"] = False
|
||||||
|
|
||||||
def changedRegisters (self, *args):
|
def changedRegisters (self, *args):
|
||||||
for reg, val in args:
|
for reg, val in args:
|
||||||
|
@ -63,7 +76,7 @@ class Emulator:
|
||||||
|
|
||||||
def processSourceLine(self, line_text, index):
|
def processSourceLine(self, line_text, index):
|
||||||
tokens = re.findall(r'[^\s,]+', line_text)
|
tokens = re.findall(r'[^\s,]+', line_text)
|
||||||
if len(tokens) <= 0:
|
if len(tokens) <= 0 or tokens[0][0] == '#':
|
||||||
return index
|
return index
|
||||||
if tokens[0][-1] == ':':
|
if tokens[0][-1] == ':':
|
||||||
self.labels[tokens[0][:-1]] = index
|
self.labels[tokens[0][:-1]] = index
|
||||||
|
@ -96,6 +109,21 @@ class Emulator:
|
||||||
self.registers['%rsp'] += 1
|
self.registers['%rsp'] += 1
|
||||||
return temp
|
return temp
|
||||||
|
|
||||||
|
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):
|
def iterate (self):
|
||||||
old_rip = self.registers['%rip']
|
old_rip = self.registers['%rip']
|
||||||
self.last_sp = self.registers['%rsp']
|
self.last_sp = self.registers['%rsp']
|
||||||
|
@ -123,3 +151,6 @@ class Emulator:
|
||||||
raise StopIteration()
|
raise StopIteration()
|
||||||
else:
|
else:
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
def getUsedRegisters (self):
|
||||||
|
return [reg_name for reg_name, reg_val in self.registers.iteritems() if reg_val!=JUNK_VALUE]
|
||||||
|
|
27
Junk.py
Normal file
27
Junk.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
|
||||||
|
class JunkComparisonException (BaseException):
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Junk:
|
||||||
|
|
||||||
|
def __init__ (self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __str__ (self):
|
||||||
|
return "[Junk]"
|
||||||
|
|
||||||
|
def __repr__ (self):
|
||||||
|
return "[Junk]"
|
||||||
|
|
||||||
|
def __add__ (self, other):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __radd__ (self, other):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __sub__ (self, other):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __rsub__ (self, other):
|
||||||
|
return self
|
|
@ -2,8 +2,9 @@
|
||||||
import sys
|
import sys
|
||||||
import getopt
|
import getopt
|
||||||
|
|
||||||
from Emulator import Emulator
|
from Emulator import Emulator, CodeParseException
|
||||||
from TikzPainter import TikzPainter
|
from TikzPainter import TikzPainter
|
||||||
|
from Junk import Junk, JunkComparisonException
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
pass
|
pass
|
||||||
|
|
18
opcodes.py
18
opcodes.py
|
@ -45,33 +45,27 @@ def comp(emu, val1, val2):
|
||||||
|
|
||||||
@add_opcode("jg")
|
@add_opcode("jg")
|
||||||
def jg(emu, label):
|
def jg(emu, label):
|
||||||
if emu.status["g"]:
|
emu.jump(label, g=True)
|
||||||
emu.registers['%rip'] = emu.labels[label]
|
|
||||||
|
|
||||||
@add_opcode("jl")
|
@add_opcode("jl")
|
||||||
def jl(emu, label):
|
def jl(emu, label):
|
||||||
if emu.status["l"]:
|
emu.jump(label, l=True)
|
||||||
emu.registers['%rip'] = emu.labels[label]
|
|
||||||
|
|
||||||
@add_opcode("je")
|
@add_opcode("je")
|
||||||
def je(emu, label):
|
def je(emu, label):
|
||||||
if emu.status["e"]:
|
emu.jump(label, e=True)
|
||||||
emu.registers['%rip'] = emu.labels[label]
|
|
||||||
|
|
||||||
@add_opcode("jge")
|
@add_opcode("jge")
|
||||||
def jge(emu, label):
|
def jge(emu, label):
|
||||||
if emu.status["g"] or emu.status["e"]:
|
emu.jump(label, "or", g=True, e=True)
|
||||||
emu.registers['%rip'] = emu.labels[label]
|
|
||||||
|
|
||||||
@add_opcode("jle")
|
@add_opcode("jle")
|
||||||
def jle(emu, label):
|
def jle(emu, label):
|
||||||
if emu.status["l"] or emu.status["e"]:
|
emu.jump(label, "or", l=True, e=True)
|
||||||
emu.registers['%rip'] = emu.labels[label]
|
|
||||||
|
|
||||||
@add_opcode("jne")
|
@add_opcode("jne")
|
||||||
def jne(emu, label):
|
def jne(emu, label):
|
||||||
if not emu.status["e"]:
|
emu.jump(label, e=False)
|
||||||
emu.registers['%rip'] = emu.labels[label]
|
|
||||||
|
|
||||||
# Unconditional Jumping
|
# Unconditional Jumping
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import os,sys,inspect
|
import os, sys, inspect
|
||||||
|
import traceback
|
||||||
sys.path.insert(1, os.path.join(sys.path[0], '..'))
|
sys.path.insert(1, os.path.join(sys.path[0], '..'))
|
||||||
|
|
||||||
import infernal
|
import infernal
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
@ -9,16 +11,6 @@ tests = []
|
||||||
def add_test(name, result, register, code):
|
def add_test(name, result, register, code):
|
||||||
tests.append((name, result, register, code))
|
tests.append((name, result, register, code))
|
||||||
|
|
||||||
def execute_test (register, code):
|
|
||||||
emu = infernal.Emulator(code)
|
|
||||||
emu.setStack("junk...", "calling eip")
|
|
||||||
emu.setRegs( rip = 0, rbp = 'old bp')
|
|
||||||
|
|
||||||
for line_nr in emu:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return emu.getVal(register)
|
|
||||||
|
|
||||||
def printf (str, *args):
|
def printf (str, *args):
|
||||||
print(str.format(*args))
|
print(str.format(*args))
|
||||||
|
|
||||||
|
@ -29,15 +21,32 @@ def execute_tests():
|
||||||
error_tests = 0
|
error_tests = 0
|
||||||
for name, result, register, code in tests:
|
for name, result, register, code in tests:
|
||||||
total_tests += 1
|
total_tests += 1
|
||||||
|
line_nr = None
|
||||||
try:
|
try:
|
||||||
output = execute_test(register, code)
|
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:
|
if output != result:
|
||||||
failed_tests += 1
|
failed_tests += 1
|
||||||
printf("Failed in {}. {} was {}, should be {}",
|
printf("Failed in {}. {} was {}, should be {}",
|
||||||
name, register, result, output)
|
name, register, result, output)
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
|
if not isinstance(e, result):
|
||||||
error_tests += 1
|
error_tests += 1
|
||||||
printf("Encountered error in {}: {}",name,e)
|
printf("Encountered error in {}, at operation {}: {}",
|
||||||
|
name, emu.getVal('%rip'), e)
|
||||||
|
traceback.print_exc()
|
||||||
printf("Tests done! {}/{}.",
|
printf("Tests done! {}/{}.",
|
||||||
total_tests - failed_tests - error_tests, total_tests)
|
total_tests - failed_tests - error_tests, total_tests)
|
||||||
printf("{} failed, and {} encountered a python error",
|
printf("{} failed, and {} encountered a python error",
|
||||||
|
@ -60,6 +69,56 @@ movq $20, %rax
|
||||||
addq %rax, %rsi
|
addq %rax, %rsi
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
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.
|
||||||
|
""")
|
||||||
|
|
||||||
|
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.
|
||||||
|
""")
|
||||||
|
|
||||||
|
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.
|
||||||
|
""")
|
||||||
|
|
||||||
|
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.
|
||||||
|
""")
|
||||||
|
|
||||||
|
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.
|
||||||
|
""")
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user