Mercurial > lcfOS
changeset 104:ed230e947dc6
Added hexviewer
author | windel |
---|---|
date | Sun, 30 Dec 2012 22:31:55 +0100 |
parents | 28a35161ef23 |
children | 6a303f835c6d |
files | python/hexfile.py python/hexviewer.py python/ppci/compilers/kscompiler.py python/ppci/core/__init__.py python/ppci/core/bitreader.py python/ppci/core/context.py python/ppci/core/function.py python/ppci/core/instruction.py python/ppci/core/llvmtype.py python/ppci/core/module.py python/ppci/core/value.py python/ppci/frontends/ks/irgenerator.py python/ppci/frontends/ks/nodes.py python/zcc.py |
diffstat | 14 files changed, 315 insertions(+), 183 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/hexfile.py Sun Dec 30 22:31:55 2012 +0100 @@ -0,0 +1,97 @@ +import os + +class HexFileException(Exception): + pass + +def parseHexLine(line): + """ Parses a hexfile line into three parts """ + line = line[1:] # Remove ':' + nums = bytes.fromhex(line) + bytecount = nums[0] + if len(nums) != bytecount + 5: + raise HexFileException('byte count field incorrect') + crc = sum(nums) + if (crc & 0xFF) != 0: + raise HexFileException('crc incorrect') + address = nums[1] * 256 + nums[2] + typ = nums[3] + data = nums[4:-1] + return (address, typ, data) + +class HexFile: + """ Represents an intel hexfile """ + def __init__(self, filename=None): + self.regions = [] + self.startAddress = 0 + self.filename = None + if filename: + self.load(filename) + + def load(self, filename): + with open(filename, 'r') as f: + endOfFile = False + offset = 0 + startAddress = 0 + curAddress = 0 + curData = bytearray() + for line in f: + line = line.strip() # Strip spaces and newlines + if not line: continue # Skip empty lines + if line[0] != ':': continue # Skip lines that do not start with a ':' + if endOfFile: raise HexFileException('hexfile line after end of file record') + address, typ, data = parseHexLine(line) + if typ == 0x0: # Data record + address += offset # Fix address with offset + # Append data + if address == curAddress: + curData += data + curAddress += len(data) + else: + if curData: + self.regions.append(HexFileRegion(startAddress, bytes(curData))) + startAddress = address + curAddress = address + len(data) + curData = bytearray(data) + elif typ == 0x4: # Extended linear address record + offset = ((data[0] << 8) + data[1]) << 16 + elif typ == 0x1: # End of file record + if len(data) != 0: + raise HexFileException('end of file record must contain no data') + endOfFile = True + elif typ == 0x5: # Start address record (where IP goes after loading) + self.startAddress = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3] + else: + raise HexFileException('record type {0} not implemented'.format(typ)) + print(hex(address), typ, data) + # After all lines: + if curData: + self.regions.append(HexFileRegion(startAddress, bytes(curData))) + # Store the filename: + self.filename = filename + def __repr__(self): + i = [] + i.append(super().__repr__()) + i.append('Start address {0}'.format(hex(self.startAddress))) + i.append('Filename: {0}'.format(self.filename)) + for r in self.regions: + i.append(str(r)) + return os.linesep.join(i) + def save(self, filename): + with open(filename, 'w') as f: + for line in f: + pass + +class HexFileRegion: + def __init__(self, address, data = bytes()): + self.address = address + self.data = data + def __repr__(self): + return 'Region at 0x{0:X} of {1} bytes'.format(self.address, len(self.data)) + +if __name__ == '__main__': + h = HexFile() + print(h) + """ Test hexfile implementation with some hexfile """ + h1 = HexFile('audio.hex') + print(h1) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/hexviewer.py Sun Dec 30 22:31:55 2012 +0100 @@ -0,0 +1,78 @@ +#!/usr/bin/python + +from PyQt4.QtCore import * +from PyQt4.QtGui import * +import sys +import hexfile + +class BinViewer(QWidget): + """ + The binviewer consists of a hex view, a ascii view + and perhaps others.. + """ + def __init__(self): + super().__init__() + self.setFont(QFont('Courier')) + self.fontHeight = self.fontMetrics().height() + self.fontWidth = self.fontMetrics().width('x') + self.hexfile = hexfile.HexFile() + def paintEvent(self, event): + painter = QPainter(self) + br = QBrush(Qt.lightGray) + painter.setBrush(br) + h = self.height() + addressWidth = self.fontWidth * 8 + painter.drawRect(2, 2, addressWidth, h) + br2 = QBrush(Qt.yellow) + painter.setBrush(br2) + w = self.width() + byteWidth = self.fontWidth * 16 * 3 + painter.drawRect(addressWidth + 4, 2, byteWidth, h) + asciiWidth = self.fontWidth * 16 + br2 = QBrush(Qt.gray) + painter.setBrush(br2) + painter.drawRect(addressWidth + byteWidth + 4, 2, asciiWidth, h) + for r in range(1, 10): + addr = '{0:08X}'.format(r << 16) + painter.drawText(2, 2 + self.fontHeight * r, addr) + bts = bytes(range(16)) + x = addressWidth + 4 + for b in bts: + b = '{0:02X}'.format(b) + painter.drawText(x, 2 + self.fontHeight * r, b) + x += 3 * self.fontWidth + x = addressWidth + byteWidth + 4 + for b in bts: + b = '{0}'.format(chr(b)) + painter.drawText(x, 2 + self.fontHeight * r, b) + x += 1 * self.fontWidth + def setHexFile(self, hf): + self.hexFile = hf + self.update() + +class BinViewMain(QMainWindow): + def __init__(self): + super().__init__() + self.bv = BinViewer() + self.setCentralWidget(self.bv) + self.bv.setHexFile(hexfile.HexFile('audio.hex')) + mb = self.menuBar() + fileMenu = mb.addMenu("File") + + def addMenuEntry(name, menu, callback, shortcut=None): + a = QAction(name, self) + menu.addAction(a) + a.triggered.connect(callback) + if shortcut: a.setShortcut(shortcut) + addMenuEntry("Open", fileMenu, self.openFile, QKeySequence(QKeySequence.Open)) + def openFile(self): + filename = QFileDialog.getOpenFileName(self, "Open hex file...", "*.hex", "Intel hexfiles (*.hex)") + if filename: + h = hexfile.HexFile(filename) + +if __name__ == '__main__': + app = QApplication(sys.argv) + bv = BinViewMain() + bv.show() + app.exec_() +
--- a/python/ppci/compilers/kscompiler.py Wed Dec 26 10:53:33 2012 +0100 +++ b/python/ppci/compilers/kscompiler.py Sun Dec 30 22:31:55 2012 +0100 @@ -5,17 +5,16 @@ #from .codegenerator import CodeGenerator #from .nodes import ExportedSymbol from ..core.errors import CompilerException -from ..core import version +from ..core.bitreader import BitcodeWriter +from ..core import version, Context #from .. import version class KsCompiler: def __repr__(self): return 'LCFOS compiler {0}'.format(version) - def generateSignature(self, src): - return hashlib.md5(bytes(src,encoding='ascii')).hexdigest() - def compilesource(self, src): + context = Context() """ Front end that handles the stages: """ self.errorlist = [] # Pass 1: parsing and type checking @@ -28,22 +27,10 @@ if len(p.errorlist) > 0: self.errorlist = p.errorlist return + ir = KsIrGenerator().generateIr(context, ast) # pass 2: Optimization # TODO: implement optimization # Pass 3: code generation - ir = KsIrGenerator().generateIr(ast) - # Attach a signature: - ast.signature = self.generateSignature(src) - # Generate exported symbols: return ast - def compileProject(self, project): - mods = [] - for fname in project.files: - print('Compiling {0}...'.format(fname)) - source = project.loadProjectFile(fname) - mod = self.compilesource(source) - mods.append(mod) - return mods -
--- a/python/ppci/core/__init__.py Wed Dec 26 10:53:33 2012 +0100 +++ b/python/ppci/core/__init__.py Sun Dec 30 22:31:55 2012 +0100 @@ -1,10 +1,11 @@ from .instruction import * -from .function import * -from .value import * -from .bitreader import BitReader +from .function import Function +from .value import Value +from .bitreader import BitReader, BitcodeWriter from .errors import CompilerException from .module import Module - +from .llvmtype import FunctionType +from .context import Context version='0.0.1'
--- a/python/ppci/core/bitreader.py Wed Dec 26 10:53:33 2012 +0100 +++ b/python/ppci/core/bitreader.py Sun Dec 30 22:31:55 2012 +0100 @@ -26,3 +26,49 @@ return int(b[0]) return 2 +class BitstreamWriter: + def __init__(self, f): + self.f = f + self.u32 = 0 + self.curpos = 0 + def Emit1(self, val): + self.Emit(val, 1) + def Emit(self, val, numbits): + """ Emits value using numbits bits """ + if numbits == 1: + if val != 0: + self.u32 |= (0x1 << self.curpos) + self.curpos += 1 + if self.curpos == 32: + self.writeWord() + elif numbits > 1: + for i in range(numbits): + if val & (1 << i) != 0: + self.Emit1(1) + else: + self.Emit1(0) + def writeWord(self): + bts = struct.pack('<I', self.u32) + self.f.write(bts) + self.u32 = 0 + self.curpos = 0 + def flush(self): + if self.curpos != 0: + self.writeWord() + +class BitcodeWriter: + def __init__(self): + pass + def WriteModule(self, module): + pass + def WriteModuleToFile(self, module, f): + s = BitstreamWriter(f) + s.Emit(ord('B'), 8) + s.Emit(ord('C'), 8) + s.Emit(0x0, 4) + s.Emit(0xC, 4) + s.Emit(0xE, 4) + s.Emit(0xD, 4) + self.WriteModule(module) + s.flush() +
--- a/python/ppci/core/context.py Wed Dec 26 10:53:33 2012 +0100 +++ b/python/ppci/core/context.py Sun Dec 30 22:31:55 2012 +0100 @@ -1,4 +1,12 @@ +from .llvmtype import IntegerType, llvmType, typeID, FunctionType class Context: - pass + """ Global context """ + def __init__(self): + self.Int8Type = IntegerType(self, 8) + self.Int16Type = IntegerType(self, 16) + self.Int32Type = IntegerType(self, 32) + self.Int64Type = IntegerType(self, 64) + self.VoidType = llvmType(self, typeID.Void) + self.DoubleType = llvmType(self, typeID.Double)
--- a/python/ppci/core/function.py Wed Dec 26 10:53:33 2012 +0100 +++ b/python/ppci/core/function.py Sun Dec 30 22:31:55 2012 +0100 @@ -1,6 +1,7 @@ class Function: - def __init__(self): - pass + def __init__(self, functiontype, name, module): + self.functiontype = functiontype + self.name = name + self.module = module -
--- a/python/ppci/core/instruction.py Wed Dec 26 10:53:33 2012 +0100 +++ b/python/ppci/core/instruction.py Sun Dec 30 22:31:55 2012 +0100 @@ -2,16 +2,18 @@ from .value import Value class Instruction(Value): - """ - Base class for all instructions. - """ + """ Base class for all instructions. """ pass class CallInstruction(Instruction): pass class BinaryOperator(Instruction): - pass + def __init__(self, operation, value1, value2): + # Check types of the two operands: + self.value1 = value1 + self.value2 = value2 + self.operation = operation class LoadInstruction(Instruction): def __init__(self, ptr, name, insertBefore):
--- a/python/ppci/core/llvmtype.py Wed Dec 26 10:53:33 2012 +0100 +++ b/python/ppci/core/llvmtype.py Sun Dec 30 22:31:55 2012 +0100 @@ -2,12 +2,20 @@ def Enum(**enums): return type('Enum', (), enums) +typeID = Enum(Void=0, Double=3, Integer=10, Function=11, Struct=12, Array=13, Pointer=14, Vector=15) + class llvmType: - typeID = Enum(Void=0, Double=3, Integer=10, \ - Function=11, Struct=12, \ - Array=13, Pointer=14\ - Vector=15) def __init__(self, context, tid): self.context = context +class IntegerType(llvmType): + def __init__(self, context, bits): + super().__init__(context, typeID.Integer) + self.bits = bits +class FunctionType(llvmType): + def __init__(self, resultType, parameterTypes): + super().__init__(resultType.context, typeID.Function) + self.resultType = resultType + self.parameterTypes = parameterTypes +
--- a/python/ppci/core/module.py Wed Dec 26 10:53:33 2012 +0100 +++ b/python/ppci/core/module.py Sun Dec 30 22:31:55 2012 +0100 @@ -1,5 +1,7 @@ -class Module: +from .value import Value + +class Module(Value): def __init__(self): print('new module!')
--- a/python/ppci/core/value.py Wed Dec 26 10:53:33 2012 +0100 +++ b/python/ppci/core/value.py Sun Dec 30 22:31:55 2012 +0100 @@ -5,6 +5,8 @@ self.name = None def getContext(self): return self.valueType.context + def dump(self): + print(self) def setName(self, name): if not self.name and not name:
--- a/python/ppci/frontends/ks/irgenerator.py Wed Dec 26 10:53:33 2012 +0100 +++ b/python/ppci/frontends/ks/irgenerator.py Sun Dec 30 22:31:55 2012 +0100 @@ -6,7 +6,6 @@ from ...core.errors import Error from ... import core from .builtin import real, integer, boolean, char -#from .assembler import * class KsIrGenerator: def __init__(self): @@ -26,19 +25,17 @@ if node.op == 'mod': assert(node.typ.isType(integer)) - self.addCode(mov('rax', node.a.reg)) elif node.op == 'div': assert(node.typ.isType(integer)) - self.addCode(mov('rax', node.a.reg)) elif node.op == '*': if node.typ.isType(integer): - self.addCode(imulreg64(node.a.reg, node.b.reg)) + pass elif node.op == '+': if node.typ.isType(integer): - self.addCode(addreg64(node.a.reg, node.b.reg)) + pass elif node.op == '-': if node.typ.isType(integer): - self.addCode(subreg64(node.a.reg, node.b.reg)) + pass else: Error('Unknown Binop {0}'.format(node.op)) @@ -78,14 +75,8 @@ self.genexprcode(node.obj) node.reg = node.obj.reg else: - self.getreg(node) + pass # Get a register to store the integer value - if node.obj.isLocal: - # relative to rbp: - self.addCode( mov(node.reg, ['rbp', node.obj.offset]) ) - else: - self.addCode(mov(node.reg, ['RIP', 0x0])) - self.fixCode(self.rip-4, imm32(node.obj.offset - self.rip)) else: Error('Cannot load variable type {0}'.format(node.typ)) @@ -101,32 +92,10 @@ Error('Relop not implemented for {0}'.format(node.a.typ)) elif type(node) is Constant: - if node.typ.isType(integer): - self.getreg(node) + print('TODO: constant') elif type(node) is ProcedureCall: - if type(node.proc.obj) is BuiltinProcedure: - # Handle builtin procedures different, these not always call - # a function, but generate code. - bi = node.proc.obj - if bi.name == 'chr': - arg = node.args[0] - self.genexprcode(arg) - # Store character in full width register: - # TODO: store in char only register - node.reg = arg.reg - else: - Error('Unknown builtin function {0}'.format(bi.name)) - else: - # Use generic procedure call first - self.gencode(node) - # Retrieve result: - if node.typ.isType(integer): - # Store result! - self.getreg(node) - self.addCode( mov(node.reg, 'rax') ) - else: - Error('Return type not supported {0}'.format(node.typ)) + Error('TODO: proc call') else: Error('Cannot generate expression code for: {0}'.format(node)) @@ -134,44 +103,19 @@ """ Code generation function for AST nodes """ if isinstance(node, Module): # TODO: recurse! - return core.Module() + + self.mod = core.Module() + # Create a function called init for this module: + ftype = core.FunctionType(self.context.VoidType, []) + func = core.Function(ftype, "init", self.mod) + bb = self.gencode(node.initcode) + self.mod.dump() + return self.mod elif type(node) is Procedure: # calculate offsets for local variables and parameters # Variable location relative to 'rbp' register variables = node.symtable.getAllLocal(Variable) - offset = 0 - paramoffset = 16 - for var in variables: - var.isLocal = True - if not var.isParameter: - offset += var.typ.size - # Offset is negative of rbp in stack frame - var.offset = -offset - node.framesize = offset - # Calculate offsets of parameters relative to rbp register - for par in reversed(node.typ.parameters): - pvar = node.symtable.getLocal(Variable, par.name) - pvar.offset = paramoffset - paramoffset += pvar.typ.size - - # code generation - node.entrypoint = self.rip - self.addCode(push('rbp')) - self.addCode(mov('rbp', 'rsp')) # Setup the base pointer - self.addCode(subreg64('rsp', node.framesize)) # reserve space for locals - self.gencode(node.block) - if node.retexpr: - if node.retexpr.typ.isType(integer): - self.genexprcode(node.retexpr) - self.addCode( mov('rax', node.retexpr.reg) ) - self.freereg(node.retexpr) - else: - Error('Cannot return this kind yet {0}'.format(node.retexpr.typ)) - self.addCode( addreg64('rsp', node.framesize) ) - self.addCode( pop('rbp') ) - self.addCode( ret() ) - assert(len(self.usedregs) == 0) elif isinstance(node, StatementSequence): for s in node.statements: @@ -179,7 +123,6 @@ elif type(node) is ProcedureCall: # Prepare parameters on the stack: - stacksize = 0 assert(len(node.args) == len(node.proc.typ.parameters)) for arg, param in zip(node.args, node.proc.typ.parameters): @@ -191,98 +134,33 @@ else: Error('Parameter kind other than value') - # Calculate address using designator - if type(node.proc.obj) is Procedure: - self.addCode( call(0x0) ) - self.fixCode( self.rip - 4, imm32(node.proc.obj.entrypoint - self.rip)) - elif type(node.proc.obj) is ImportedSymbol: - # Load the entry point of the import table - self.getreg(node.proc.obj) - # Load the address of the procedure: - self.addCode( mov(node.proc.obj.reg, ['RIP', 0x0]) ) - self.fixCode( self.rip - 4, imm32(node.proc.obj.offset - self.rip) ) - # Call to the address in register: - self.addCode( call(node.proc.obj.reg) ) - # Free register that holds the address of the object - self.freereg( node.proc.obj ) - elif type(node.proc.obj) is BuiltinProcedure: - if node.proc.obj.name == 'chr': - print('int to char') - else: - Error('Unknown builtin function {0}'.format(node.proc.obj.name)) - else: - Error('Cannot call designator of type {0}'.format(node.proc.obj)) - - # Restore stack (pop all arguments of): - self.addCode(addreg64('rsp', stacksize)) - elif type(node) is Assignment: if node.lval.typ.isType(integer): # TODO if node.rval is Constant of some datatype, move it to mem directly self.genexprcode(node.rval) # Calculate the value that has to be stored. - self.storeRegInDesignator(node.rval.reg, node.lval) - self.freereg(node.rval) + print("TODO") else: Error('Assignments of other types not implemented') # TODO if left and right are designators, do some sort of memcpy. elif type(node) is IfStatement: self.genexprcode(node.condition) - self.addCode( cmpreg64(node.condition.reg, 1) ) - self.freereg(node.condition) + print("TODO IF") if node.falsestatement: # If with else clause - self.addCode( nearjump(0x0, condition='NE') ) # if Not Equal jump to false - rip1 = self.rip - fixloc1 = self.rip - 4 - self.gencode(node.truestatement) - self.addCode( nearjump( 0x0 ) ) # jump over false code - fixloc2 = self.rip - 4 - self.fixCode(fixloc1, imm32(self.rip - rip1)) - rip2 = self.rip - self.gencode(node.falsestatement) - self.fixCode(fixloc2, imm32(self.rip - rip2)) + pass else: # If without else clause - self.addCode( nearjump(0x0, condition='NE') ) # if Not Equal jump to false - rip1 = self.rip - fixloc1 = self.rip - 4 - self.gencode(node.truestatement) - self.fixCode(fixloc1, imm32(self.rip - rip1)) # Fixup near jump over true code. + pass elif isinstance(node, WhileStatement): - rip1 = self.rip # Store the start of the while loop self.genexprcode(node.condition) - self.addCode( cmpreg64(node.condition.reg, 1) ) # Test condition for true-ness - self.freereg(node.condition) - self.addCode( nearjump(0x0, condition='NE') ) # If Not Equal jump over while code AND jump back (fix later) - fixloc1 = self.rip - 4 - rip2 = self.rip self.gencode(node.dostatements) - self.addCode( nearjump(0x0) ) # JMP to condition, fix exact jump position below - fixloc2 = self.rip - 4 - rip3 = self.rip # end of while loop - self.fixCode(fixloc2, imm32(rip1 - rip3)) # Fixup jump to start of while loop - self.fixCode(fixloc1, imm32(rip3 - rip2)) # Fixup jump out of while loop - elif type(node) is ForStatement: # Initial load of iterator variable: self.genexprcode(node.begin) self.genexprcode(node.end) - # TODO: link reg with variable so that a register is used instead of a variable - iterreg = node.begin.reg # Get the register used for the loop - #self.addCode(cmpreg64(iterreg, node.endvalue)) - rip1 = self.rip self.gencode(node.statements) - #self.loadDesignatorInReg(node. - #self.addCode( addreg64(node.variable, node.increment) ) - self.addCode(nearjump(0x0)) - fixloc1 = self.rip - 4 - rip2 = self.rip - self.fixCode(fixloc1, imm32(rip1 - rip2)) - - self.freereg(node.begin) # Release register used in loop - self.freereg(node.end) Error('No implementation of FOR statement') elif type(node) is AsmCode: @@ -325,11 +203,6 @@ if type(selector) is Index: # Deref an array index self.genexprcode(selector.index) - self.getreg(selector) - self.addCode( mov(selector.reg, selector.typ.elementType.size) ) - self.addCode( imulreg64(selector.reg, selector.index.reg ) ) - self.freereg(selector.index) - self.addCode(addreg64(node.reg, selector.reg)) self.freereg(selector) elif type(selector) is Field: print('Field') @@ -338,11 +211,12 @@ Error('Unknown selector') else: Error('Can only gencode for designator with selectors') - else: print('not generating code for {0}'.format(node)) - def generateIr(self, ast): + def generateIr(self, context, ast): """ ir generation front end """ + # Create a new context for this code. + self.context = context return self.gencode(ast)
--- a/python/ppci/frontends/ks/nodes.py Wed Dec 26 10:53:33 2012 +0100 +++ b/python/ppci/frontends/ks/nodes.py Sun Dec 30 22:31:55 2012 +0100 @@ -31,14 +31,14 @@ def __init__(self, fieldname): self.fieldname = fieldname def __repr__(self): - return 'FLD {0}'.format(self.fieldname) + return 'FIELD {0}'.format(self.fieldname) class Index(Node): def __init__(self, index, typ): self.index = index self.typ = typ def __repr__(self): - return 'IDX {0}'.format(self.index) + return 'INDEX {0}'.format(self.index) class Deref(Node): pass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/zcc.py Sun Dec 30 22:31:55 2012 +0100 @@ -0,0 +1,26 @@ +#!/usr/bin/python + +import sys, os, argparse +from ppci.compilers import KsCompiler +from ppci.core import BitcodeWriter + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='K# to bitcode compiler') + parser.add_argument('source', type=str, help='the source file to build') + args = parser.parse_args() + + print(args) + try: + with open(args.source, 'r') as f: + src = f.read() + except IOError: + print('Failed to load {0}'.format(args.project)) + sys.exit(3) + c = KsCompiler() + module = c.compilesource(src) + + with open('sjaak.bc', 'wb') as f: + BitcodeWriter().WriteModuleToFile(module, f) + + +