def pushUal(self, lineNumber: int, label: Optional[Label], operator: str, destination: int, regOperands: Tuple[int, ...], littOperand: Optional[Litteral] = None) -> None: """ Ajoute une commande de calcul UAL dans l'assembleur. :param lineNumber: numéro de la ligne d'origine :type lineNumber: int :param label: label de l'instruction :type label: Optional[Label] :param destination: registre destination :type destination: int :param operator: opérateur :type operator: string :param regOperands: opérandes de type registre :type regOperands: tuple[int] :param littOperand: opérande de type littéral :type littOperand: Optional[Litteral] """ for ope in regOperands: assert isinstance(ope, int) if destination != 0 and not self.__engine.ualOutputIsFree(): raise CompilationError( f"Calcul {operator} stocké dans le registre {destination}.", {"lineNumber": lineNumber}) if self.__engine.ualOutputIsFree(): regOperands = (destination, ) + regOperands if isinstance(littOperand, Litteral): opcode = self.__engine.getLitteralOpcode(operator) asmCommand = self.__engine.getLitteralAsmCommand(operator) if opcode != "" and asmCommand != "": maxSize = self.__engine.getLitteralMaxSizeIn(operator) if not littOperand.isBetween(0, maxSize): raise CompilationError( f"Litteral trop grand pour {operator}", {"lineNumber": lineNumber}) self.__lines.append( AsmLine(lineNumber, label, opcode, asmCommand, regOperands, littOperand)) return raise CompilationError( f"Pas de commande pour {operator} avec litteral dans le modèle de processeur", {"lineNumber": lineNumber}) opcode = self.__engine.getOpcode(operator) asmCommand = self.__engine.getAsmCommand(operator) if asmCommand == "" or opcode == "": raise AttributesError( f"Pas de commande pour {operator} dans le modèle de processeur.", {"lineNumber": lineNumber}) self.__lines.append( AsmLine(lineNumber, label, opcode, asmCommand, regOperands, None))
def pushStore(self, lineNumber: int, label: Optional[Label], source: int, destination: Variable) -> None: """ Ajoute une commande STORE dans l'assembleur. Réserve l'espace mémoire pour la variable. :param lineNumber: numéro de la ligne d'origine :type lineNumber: int :param label: label de l'instruction :type label: Optional[Label] :param source: registre source :type source: int :param destination: variable destination :type destination: Variable """ opcode = self.__engine.getOpcode("store") asmCommand = self.__engine.getAsmCommand("store") if asmCommand == "" or opcode == "": raise AttributesError( "Pas de commande pour store dans le modèle de processeur.", {"lineNumber": lineNumber}) memoryItem = self.__pushMemory(destination) asmLine = AsmLine(lineNumber, label, opcode, asmCommand, (source, ), memoryItem) self.__lines.append(asmLine)
def pushJump(self, lineNumber: int, label: Optional[Label], cible: Label, operator: Optional[str] = None) -> None: """ Ajoute une commande JUMP, saut conditionnel ou non, dans l'assembleur. :param lineNumber: numéro de la ligne d'origine :type lineNumber: int :param label: label de l'instruction :type label: Optional[Label] :param cible: étiquette cible :type cible: Label :param operator: comparaison parmi <, <=, >=, >, ==, !=. None pour Jump inconditionnel :type operator: str / None """ if operator == None: operator = "goto" assert operator in ("<", "<=", ">", ">=", "==", "!=", "goto") opcode = self.__engine.getOpcode(operator) asmCommand = self.__engine.getAsmCommand(operator) if asmCommand == "" or opcode == "": raise AttributesError( f"Pas de commande pour {operator} dans le modèle de processeur.", {"lineNumber": lineNumber}) self.__lines.append( AsmLine(lineNumber, label, opcode, asmCommand, (), cible))
def pushCmp(self, lineNumber: int, label: Optional[Label], operand1: int, operand2: int) -> None: """ Ajoute une commande CMP, comparaison, dans l'assembleur. :param lineNumber: numéro de la ligne d'origine :type lineNumber: int :param label: label de l'instruction :type label: Optional[Label] :param operand1: registre premier opérande :type operand1: int :param operand2: registre second opérande :type operand2: int .. note:: Une telle commande doit précéder l'utilisation d'un saut conditionnel. """ assert isinstance(operand1, int) assert isinstance(operand2, int) opcode = self.__engine.getOpcode("cmp") asmCommand = self.__engine.getAsmCommand("cmp") if asmCommand == "" or opcode == "": raise AttributesError( "Pas de commande pour cmp dans le modèle de processeur.", {"lineNumber": lineNumber}) self.__lines.append( AsmLine(lineNumber, label, opcode, asmCommand, (operand1, operand2), None))
def pushMoveLitteral(self, lineNumber: int, label: Optional[Label], source: Litteral, destination: int) -> None: """ Ajoute une commande MOVE avec littéral dans l'assembleur. :param lineNumber: numéro de la ligne d'origine :type lineNumber: int :param label: label de l'instruction :type label: Optional[Label] :param destination: registre destination :type destination: int :param source: littéral source :type source: Litteral """ assert isinstance(source, Litteral) opcode = self.__engine.getLitteralOpcode("move") asmCommand = self.__engine.getLitteralAsmCommand("move") if opcode != "" and asmCommand != "": maxSize = self.__engine.getLitteralMaxSizeIn("move") if source.isBetween(0, maxSize): self.__lines.append( AsmLine(lineNumber, label, opcode, asmCommand, (destination, ), source)) return self.pushLoad(lineNumber, label, source, destination)
def pushLoad(self, lineNumber: int, label: Optional[Label], source: Union[Variable, Litteral], destination: int) -> None: """ Ajoute une commande LOAD dans l'assembleur. Réserve l'espace mémoire pour la source. :param lineNumber: numéro de la ligne d'origine :type lineNumber: int :param label: label de l'instruction :type label: Optional[Label] :param destination: registre destination :type destination: int :param source: variable ou littéral source :type source: Litteral / Variable """ opcode = self.__engine.getOpcode("load") asmCommand = self.__engine.getAsmCommand("load") if asmCommand == "" or opcode == "": raise AttributesError( "Pas de commande pour load dans le modèle de processeur.", {"lineNumber": lineNumber}) if not self.__engine.valueFitsInMemory(source.value, False): raise CompilationError( f"Valeur {source.value} trop grande pour le modèle de processeur.", {"lineNumber": lineNumber}) memoryItem = self.__pushMemory(source) asmLine = AsmLine(lineNumber, label, opcode, asmCommand, (destination, ), memoryItem) self.__lines.append(asmLine)
def pushHalt(self, label: Optional[Label]) -> None: """Ajoute une commande HALT, fin de programme, à l'assembleur. :param label: label de l'instruction :type label: Optional[Label] """ opcode = self.__engine.getOpcode("halt") asmCommand = self.__engine.getAsmCommand("halt") if asmCommand == "" or opcode == "": raise AttributesError( "Pas de commande pour halt dans le modèle de processeur.") self.__lines.append(AsmLine(-1, label, opcode, asmCommand, (), None))
def pushPrint(self, lineNumber: int, source: int) -> None: """ Ajoute une commande PRINT, affichage à l'écran, dans l'assembleur. :param lineNumber: numéro de la ligne d'origine :type lineNumber: int :param source: registre dont on doit afficher le contenu :type source: int """ assert isinstance(source, int) opcode = self.__engine.getOpcode("print") asmCommand = self.__engine.getAsmCommand("print") if asmCommand == "" or opcode == "": raise AttributesError( "Pas de commande pour print dans le modèle de processeur.", {"lineNumber": lineNumber}) self.__lines.append( AsmLine(lineNumber, None, opcode, asmCommand, (source, ), None))
def pushInput(self, lineNumber: int, label: Optional[Label], destination: Variable) -> None: """ Ajoute une commande INPUT, lecture entrée, dans l'assembleur. :param lineNumber: numéro de la ligne d'origine :type lineNumber: int :param label: label de l'instruction :type label: Optional[Label] :param destination: variable cible :type destination: Variable """ assert isinstance(destination, Variable) opcode = self.__engine.getOpcode("input") asmCommand = self.__engine.getAsmCommand("input") if asmCommand == "" or opcode == "": raise AttributesError( "Pas de commande pour input dans le modèle de processeur.", {"lineNumber": lineNumber}) self.__lines.append( AsmLine(lineNumber, label, opcode, asmCommand, (), destination))
def pushMove(self, lineNumber: int, label: Optional[Label], source: int, destination: int) -> None: """ Ajoute une commande MOVE dans l'assembleur :param lineNumber: numéro de la ligne d'origine :type lineNumber: int :param label: label de l'instruction :type label: Optional[Label] :param destination: registre destination :type destination: int :param source: registre source :type source: int """ opcode = self.__engine.getOpcode("move") asmCommand = self.__engine.getAsmCommand("move") if asmCommand == "" or opcode == "": raise AttributesError( "Pas de commande pour move dans le modèle de processeur.", {"lineNumber": lineNumber}) moveOperands = (destination, source) self.__lines.append( AsmLine(lineNumber, label, opcode, asmCommand, moveOperands, None))
def __formatBinaryLine(self, asmItem: AsmLine) -> str: """Calcul le code binaire correspondant à un ligne assembleur :return: code binaire :rtype: str :raises:CompilationError si item à coder ne convient pas """ elements = asmItem.getElementsToCode() if len(elements) == 0: return "" regSize = self.__engine.regBits wordSize = self.__engine.dataBits binaryCode = "" for elem in elements: if isinstance(elem, int): # codage d'un registre binaryElem = self.__formatBinaryElement(elem, regSize) if binaryElem is None: raise CompilationError( f"{asmItem} -> Codage de r{elem} impossible !", {"lineNumber": asmItem.lineNumber}) else: binaryCode += binaryElem continue if isinstance(elem, Litteral): # litteral litteralSize = asmItem.getLastOperandSize(wordSize, regSize) binaryElem = self.__formatBinaryElement( elem.value, litteralSize) if binaryElem is None: raise CompilationError( f"{asmItem} -> Codage de {Litteral.value} impossible !", {"lineNumber": asmItem.lineNumber}) else: binaryCode += binaryElem continue if isinstance(elem, Variable): # variable adresseVariableSize = asmItem.getLastOperandSize( wordSize, regSize) adresseToCode = self.getMemAbsPos(elem) if isinstance(adresseToCode, int): binaryElem = self.__formatBinaryElement( adresseToCode, adresseVariableSize) if binaryElem is None: raise CompilationError( f"{asmItem} -> Codage de l'adresse {elem}[{adresseToCode}] impossible !", {"lineNumber": asmItem.lineNumber}) else: binaryCode += binaryElem else: raise CompilationError( f"{asmItem} -> Variable {elem.name} introuvable !", {"lineNumber": asmItem.lineNumber}) continue if isinstance(elem, Label): # label adresseLabelSize = asmItem.getLastOperandSize( wordSize, regSize) adresseToCode = self.getLineLabel(elem) if isinstance(adresseToCode, int): binaryElem = self.__formatBinaryElement( adresseToCode, adresseLabelSize) if binaryElem is None: raise CompilationError( f"{asmItem} -> Codage du saut {elem}[{adresseToCode}] impossible !", {"lineNumber": asmItem.lineNumber}) else: binaryCode += binaryElem else: raise CompilationError( f"{asmItem} -> Label {elem} introuvable !", {"lineNumber": asmItem.lineNumber}) continue binaryCode += elem if len(binaryCode) > wordSize: raise CompilationError( f"{asmItem} -> {binaryCode} : code binaire trop long !", {"lineNumber": asmItem.lineNumber}) unusedBits = wordSize - len(binaryCode) return binaryCode + "0" * unusedBits