#!/usr/bin/env python """Compiles C into assembly for the practicum processor (PP2) All rights reserved, you may not redistribute or use this program without prior permission from Peter Wu or Xander Houtman. Use of this program is entirely your own risk. In no circumstances can the authors of this program be held responsible for any damage including, but not limited to, financial damage or data loss. Modification of this program is not allowed without prior permission. The generated output (assembly and messages) are not subject to this license. """ import re __author__ = "Peter Wu" __copyright__ = "Copyright 2011, Peter Wu" __credits__ = ["Peter Wu"] __license__ = "Proprietary" __version__ = "1.0" __maintainer__ = "Peter Wu" __email__ = "uwretep@gmail.com" class Registers(object): """Register related functions Some functions were supposed to optimize things (alloc and free), but it's not implemented """ def __init__(self): # which registers are in use? self.registers = {} for reg in range(0, 8): self.registers[str(reg)] = False # the "Base Pointer", local variables are relative to this address. The # value of this register points to a location in the stack self.BP = "R5" def alloc(self, register=None): """Retrieves a register which is marked unused, marks it as in use and return it""" # if a register was explicitly requested if register: if not self.is_register(register): raise RuntimeError("'{}' is not a register".format(register)) register = register[1] if self.registers[register]: raise RuntimeError("Register 'R{}' is already in use".format(register)) else: for register in range(0, 5): register = str(register) # find a free register if not self.registers[register]: break else: raise RuntimeError("No free registers are available") self.registers[register] = True return "R" + register def free(self, register): """Marks a register as unused Keyword arguments: register -- a register in the format Rn where 0 <= n < 8 """ # remove leading R register = register[1:] if register in self.registers: if self.registers[register]: self.registers[register] = False; else: raise RuntimeError("free() of unused register") else: raise RuntimeError("free() of invalid register") def next_free(self, use_register=None): """Mark use_register as in use, find the next free register and then free both. """ if use_register: self.alloc(use_register) next_avail = self.alloc() self.free(next_avail) if use_register: self.free(use_register) return next_avail def get_register(self, text): """Retrieve the first register from a binary instruction""" text = text.strip() # ignore commented lines if text.startswith(";"): return None # skip labels if any text = text.upper().split(":", 1)[-1] # exclude the first operand, e.g. LOAD text = text.lstrip(" ").split(" ", 1)[-1] # retieve the first element before the next space, e.g. R0 text = text.lstrip(" ").split(" ", 1)[0] # is it a register? if self.is_register(text): return text # out of luck return None def is_register(self, text): """Returns True if text is a register, false otherwise""" return len(text) == 2 and text[0] == "R" and text[1] in self.registers def get_instruction(self, text): """Retrieve the instruction from text skipping labels and comments""" text = text.strip() # ignore commented lines if text.startswith(";"): return None # skip labels if any text = text.upper().split(":", 1)[-1] # the first element is assumed to be an instruction text = text.lstrip(" ").split(" ", 1)[0] # instructions have a length between 2 (OR) and 4 (LOAD) if len(text) >= 2 and len(text) <= 4: return text return None def find_register(self, instructions_list, fatal=False): """Finds the last modified register in a list of instructions""" for line in reversed(instructions_list): reg = self.get_register(line) if reg: return reg else: instruction = self.get_instruction(line) # convention: non-void functions store their result value in R0 if instruction == "BRS": return "R0" if fatal: raise RuntimeError("No register found in instructions list") return None def is_register_changed(self, line, register): """Returns True if the register is possibly modified in the line Keyword arguments: line -- The instruction line to be analyzed register -- The register to be looked for in the line """ line = line.split(";", 1)[0].strip().upper() register = register.upper() if not self.is_register(register): raise RuntimeError("Invalid register argument") # split into at most 3 elements (instruction, register, operand) matches = re.split("\s+", line, 2) if len(matches) == 2: instruction, reg = matches if (instruction == "PULL" and self.is_register(reg) and reg == register): return True # Assume function calls to be malicious if instruction == "BRS": return True elif len(matches) == 3: instruction, reg, operand = matches # remove whitespace from the operand. LF and CR do not occur operand = operand.translate(None, "\t ") if (operand.startswith("[--" + register) or operand.endswith(register + "++]")): return True if instruction == "STOR" and operand == register: return True # MULL and DVMD modify two registers if (instruction in ("MULL", "DVMD") and self.is_register(reg) and int(reg[1]) + 1 == int(register[1])): return True if instruction not in ("CMP", "CHCK") and reg == register: return True return False