def fix_addresses(self, statements, this_index): """ Once all of the statements have been translated, all of the addresses must be 'fixed'. In particular, branch operations need to know how many statements they need to skip ahead or behind, and the address at the target statement. This function calculates what the target of a branch, jump or subroutine call needs to go to, and inserts it in the code package for the assembled instruction. :param statements: the full set of statements that make up the proram :param this_index: the index that this instruction occurs at """ if self.operand.is_type(OperandType.RELATIVE): base_value = 0x101 if self.instruction.is_short_branch else 0x10001 branch_index = self.code_pkg.additional.int size_hint = 2 if self.instruction.is_short_branch else 4 length = 0 if branch_index < this_index: length = 1 for statement in statements[branch_index:this_index + 1]: length += statement.code_pkg.size self.code_pkg.additional = NumericValue(base_value - length, size_hint=size_hint) else: for statement in statements[this_index + 1:branch_index]: length += statement.code_pkg.size self.code_pkg.additional = NumericValue(length, size_hint=size_hint) return if self.operand.value.is_type(ValueType.ADDRESS): self.code_pkg.additional = statements[ self.operand.value.int].code_pkg.address
def test_save_file_works_correct(self): cassette_file = CassetteFile(NumericValue(0x1234), NumericValue(0x5678)) raw_bytes = [0x02] expected = [ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x3C, 0x00, 0x0F, 0x74, 0x65, 0x73, 0x74, 0x66, 0x69, 0x6C, 0x65, 0x02, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0x85, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x3C, 0x01, 0x01, 0x02, 0x04, 0x55, 0x55, 0x3C, 0xFF, 0x00, 0xFF, 0x55 ] cassette_file.host_file = MockFile() cassette_file.save_file("testfile", raw_bytes) self.assertEqual(expected, [value for value in cassette_file.host_file.value])
def test_save_file_works_correct(self): cassette_file = CassetteFile() raw_bytes = [0x02] expected = [ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x3C, 0x00, 0x0F, 0x74, 0x65, 0x73, 0x74, 0x66, 0x69, 0x6C, 0x65, 0x02, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0x85, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x3C, 0x01, 0x01, 0x02, 0x04, 0x55, 0x55, 0x3C, 0xFF, 0x00, 0xFF, 0x55 ] cassette_file.host_file = MockFile() coco_file = CoCoFile( name="testfile", load_addr=NumericValue(0x1234), exec_addr=NumericValue(0x5678), data=raw_bytes ) cassette_file.save_to_host_file(coco_file) self.assertEqual(expected, [value for value in cassette_file.host_file.value])
def test_numeric_from_character_literal_word_character_is_correct(self): for char_val in range(65, 91): result = NumericValue("'{}".format(chr(char_val))) self.assertEqual(char_val, result.int) for char_val in range(97, 123): result = NumericValue("'{}".format(chr(char_val))) self.assertEqual(char_val, result.int)
def test_get_binary_array_all_correct(self): statement1 = Statement(" JMP $FFFF") statement1.code_pkg.op_code = NumericValue("$DEAD") statement1.code_pkg.post_byte = NumericValue("$BEEF") statement1.code_pkg.additional = NumericValue("$CAFE") program = Program() program.statements = [statement1] self.assertEqual([0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE], program.get_binary_array())
def test_append_header_correct(self): name = "testfile" expected = [0x55, 0x3C, 0x00, 0x0F, 0x74, 0x65, 0x73, 0x74, 0x66, 0x69, 0x6C, 0x65, 0x02, 0xFF, 0x00, 0x12, 0x34, 0x56, 0x78, 0x84, 0x55] buffer = [] cassette_file = CassetteFile(NumericValue(0x1234), NumericValue(0x5678)) cassette_file.append_header(buffer, name, CassetteFileType.OBJECT_FILE, CassetteDataType.ASCII) self.assertEqual(expected, buffer)
def fix_pcr_relative_addresses(self, statements, this_index): """ Once all of the statements have been translated, then we need to check whether program counter relative indexed statements can be 8-bit, or if they must be 16-bit. This operation changes the size of a code package depending on how many steps away a statement is. To calculate the relative offset, we use a cheat - since we don't yet know how big any of the PCR statements are, we count the size of each instruction between the PCR statement using the code package's max_size. This tells us potentially how large the statements will be. If they are bigger than an 8-bit value, then we can default to a 16-bit offset. There may be edge cases where this heuristic may fail, most notably when there is a PCR statement that has not yet been resolved to be 8-bit, and would change the max_size calculation to be a valid 8-bit offset value instead. :param statements: the full set of statements that make up the program :param this_index: the index that this instruction occurs at """ if not self.code_pkg.additional_needs_resolution or not self.code_pkg.post_byte_choices: return if self.operand.left.is_address_expression(): relative_index = self.operand.left.extract_address_index_from_expression( ) else: relative_index = self.code_pkg.additional.int if relative_index > this_index: total_size = 0 raw_post_byte = self.code_pkg.post_byte.int for x in range(this_index, relative_index): total_size += statements[x].code_pkg.max_size if total_size <= 255: self.code_pkg.size += 1 raw_post_byte |= self.code_pkg.post_byte_choices[0] else: self.code_pkg.size += 2 raw_post_byte |= self.code_pkg.post_byte_choices[1] self.code_pkg.post_byte = NumericValue(raw_post_byte) else: total_size = 0 raw_post_byte = self.code_pkg.post_byte.int for x in range(relative_index, this_index): total_size += statements[x].code_pkg.max_size if total_size <= 255: self.code_pkg.size += 1 raw_post_byte |= self.code_pkg.post_byte_choices[0] else: self.code_pkg.size += 2 raw_post_byte |= self.code_pkg.post_byte_choices[1] self.code_pkg.post_byte = NumericValue(raw_post_byte)
def resolve_symbols(self, symbol_table): """ Given a symbol table, searches the operands for any symbols, and resolves them with values from the symbol table. Returns a (possibly) new Operand class type as a result of symbol resolution. :param symbol_table: the symbol table to search :return: self, or a new Operand class type with a resolved value """ if self.is_type(OperandType.EXPRESSION): return self.resolve_expression(symbol_table) if not self.value.is_type(ValueType.SYMBOL): return self symbol = self.get_symbol(self.value.ascii(), symbol_table) if symbol.is_type(ValueType.ADDRESS): self.value = AddressValue(symbol.int) if self.type == OperandType.UNKNOWN: return ExtendedOperand(self.operand_string, self.instruction, value=self.value) return self if symbol.is_type(ValueType.NUMERIC): self.value = copy(symbol) if self.value.hex_len() == 2: return DirectOperand(self.operand_string, self.instruction, value=self.value) return ExtendedOperand(self.operand_string, self.instruction, value=self.value)
def translate(self): code_pkg = CodePackage() code_pkg.op_code = NumericValue(self.instruction.mode.rel) if self.value.is_type(ValueType.ADDRESS): code_pkg.additional = self.value code_pkg.size = self.instruction.mode.rel_sz return code_pkg
def test_numeric_raises_exception_on_invalid_strings(self): with self.assertRaises(ValueTypeError) as context: NumericValue("this is not a valid string") self.assertEqual( "[this is not a valid string] is not valid integer, character literal, or hex value", str(context.exception) )
def translate(self): return CodePackage( op_code=NumericValue(self.instruction.mode.rel), additional=self.value if self.value.is_address() else NoneValue(), size=self.instruction.mode.rel_sz, max_size=self.instruction.mode.rel_sz, )
def translate(self): if not self.instruction.mode.ext: raise ValueError( "Instruction [{}] does not support extended addressing".format( self.instruction.mnemonic)) return CodePackage(op_code=NumericValue(self.instruction.mode.ext), additional=self.value, size=self.instruction.mode.ext_sz)
def translate(self): if not self.instruction.mode.inh: raise OperandTypeError("Instruction [{}] requires an operand".format(self.instruction.mnemonic)) return CodePackage( op_code=NumericValue(self.instruction.mode.inh), size=self.instruction.mode.inh_sz, max_size=self.instruction.mode.inh_sz, )
def translate(self): if not self.instruction.mode.dir: raise OperandTypeError( "Instruction [{}] does not support direct addressing".format(self.instruction.mnemonic) ) return CodePackage( op_code=NumericValue(self.instruction.mode.dir), additional=self.value, size=self.instruction.mode.dir_sz, max_size=self.instruction.mode.dir_sz, )
def translate(self): if self.instruction.mnemonic == "FCB": return CodePackage(additional=NumericValue(self.value.int, size_hint=2), size=1, max_size=1) if self.instruction.mnemonic == "FDB": return CodePackage(additional=NumericValue(self.value.int, size_hint=4), size=2, max_size=2) if self.instruction.mnemonic == "RMB": return CodePackage( additional=NumericValue(0, size_hint=self.value.int*2), size=self.value.int, max_size=self.value.int, ) if self.instruction.mnemonic == "ORG": return CodePackage(address=self.value) if self.instruction.mnemonic == "FCC": return CodePackage(additional=self.value, size=self.value.byte_len(), max_size=self.value.byte_len()) return CodePackage()
def set_address(self, address): """ This function sets the address where this statement should be located in memory. If the address is not already set, it will set the address and return the address that was set. If the address was already set (for example, in an ORG operation), it will return that address instead. :param address: the address to set for the statement :return: the address that was set or returned """ if not self.code_pkg.address.is_type(ValueType.NONE): return self.code_pkg.address.int self.code_pkg.address = NumericValue(address) return self.code_pkg.address.int
def resolve_expression(self, symbol_table): """ Attempts to resolve the expression contained in the operand using the symbol table supplied. Returns a (possibly) new Operand class type as a result of symbol resolution. :param symbol_table: the symbol table to use for resolution :return: self, or a new Operand class type with a resolved value """ if self.left.is_type(ValueType.SYMBOL): self.left = self.get_symbol(self.left.ascii(), symbol_table) if self.right.is_type(ValueType.SYMBOL): self.right = self.get_symbol(self.right.ascii(), symbol_table) if self.right.is_type(ValueType.NUMERIC) and self.left.is_type( ValueType.NUMERIC): left = self.left.int right = self.right.int if self.operation == "+": self.value = NumericValue("{}".format(left + right)) if self.operation == "-": self.value = NumericValue("{}".format(left - right)) if self.operation == "*": self.value = NumericValue("{}".format(int(left * right))) if self.operation == "/": self.value = NumericValue("{}".format(int(left / right))) if self.value.hex_len() == 2: return DirectOperand(self.operand_string, self.instruction, value=self.value) return ExtendedOperand(self.operand_string, self.instruction, value=self.value) raise ValueError("[{}] unresolved expression".format( self.operand_string))
def translate(self): if not self.instruction.mode.ind: raise ValueError( "Instruction [{}] does not support indexed addressing".format( self.instruction.mnemonic)) raw_post_byte = 0x00 size = self.instruction.mode.ind_sz additional = NoneValue() # Determine register (if any) if "X" in self.right: raw_post_byte |= 0x00 if "Y" in self.right: raw_post_byte |= 0x20 if "U" in self.right: raw_post_byte |= 0x40 if "S" in self.right: raw_post_byte |= 0x60 if self.left == "": raw_post_byte |= 0x80 if "-" in self.right or "+" in self.right: if "+" in self.right: raw_post_byte |= 0x00 if "++" in self.right: raw_post_byte |= 0x01 if "-" in self.right: raw_post_byte |= 0x02 if "--" in self.right: raw_post_byte |= 0x03 else: raw_post_byte |= 0x04 elif self.left == "A" or self.left == "B" or self.left == "D": raw_post_byte |= 0x80 if self.left == "A": raw_post_byte |= 0x06 if self.left == "B": raw_post_byte |= 0x05 if self.left == "D": raw_post_byte |= 0x0B else: if "+" in self.right or "-" in self.right: raise ValueError("[{}] invalid indexed expression".format( self.operand_string)) if type(self.left) == str: self.left = NumericValue(self.left) if self.left.is_type(ValueType.ADDRESS): raise ValueError( "[{}] cannot translate address in left hand side".format( self.operand_string)) numeric = self.left if numeric.byte_len() == 2: size += 2 if "PC" in self.right: raw_post_byte |= 0x8D additional = numeric else: raw_post_byte |= 0x89 additional = numeric elif numeric.byte_len() == 1: if "PC" in self.right: raw_post_byte |= 0x8C additional = numeric size += 1 else: if numeric.int <= 0x1F: raw_post_byte |= numeric.int else: raw_post_byte |= 0x88 additional = numeric size += 1 return CodePackage(op_code=NumericValue(self.instruction.mode.ind), post_byte=NumericValue(raw_post_byte), additional=additional, size=size)
class IndexedOperand(Operand): def __init__(self, operand_string, instruction): super().__init__(instruction) self.type = OperandType.INDEXED self.operand_string = operand_string self.right = "" self.left = "" if "," not in operand_string or instruction.is_string_define: raise ValueError( "[{}] is not an indexed value".format(operand_string)) if len(operand_string.split(",")) != 2: raise ValueError( "[{}] incorrect number of commas in indexed value".format( operand_string)) self.left, self.right = operand_string.split(",") def resolve_symbols(self, symbol_table): if self.left != "": if self.left != "A" and self.left != "B" and self.left != "D": self.left = Value.create_from_str(self.left, self.instruction) if self.left.is_type(ValueType.SYMBOL): self.left = self.get_symbol(self.left.ascii(), symbol_table) return self def translate(self): if not self.instruction.mode.ind: raise ValueError( "Instruction [{}] does not support indexed addressing".format( self.instruction.mnemonic)) raw_post_byte = 0x00 size = self.instruction.mode.ind_sz additional = NoneValue() # Determine register (if any) if "X" in self.right: raw_post_byte |= 0x00 if "Y" in self.right: raw_post_byte |= 0x20 if "U" in self.right: raw_post_byte |= 0x40 if "S" in self.right: raw_post_byte |= 0x60 if self.left == "": raw_post_byte |= 0x80 if "-" in self.right or "+" in self.right: if "+" in self.right: raw_post_byte |= 0x00 if "++" in self.right: raw_post_byte |= 0x01 if "-" in self.right: raw_post_byte |= 0x02 if "--" in self.right: raw_post_byte |= 0x03 else: raw_post_byte |= 0x04 elif self.left == "A" or self.left == "B" or self.left == "D": raw_post_byte |= 0x80 if self.left == "A": raw_post_byte |= 0x06 if self.left == "B": raw_post_byte |= 0x05 if self.left == "D": raw_post_byte |= 0x0B else: if "+" in self.right or "-" in self.right: raise ValueError("[{}] invalid indexed expression".format( self.operand_string)) if type(self.left) == str: self.left = NumericValue(self.left) if self.left.is_type(ValueType.ADDRESS): raise ValueError( "[{}] cannot translate address in left hand side".format( self.operand_string)) numeric = self.left if numeric.byte_len() == 2: size += 2 if "PC" in self.right: raw_post_byte |= 0x8D additional = numeric else: raw_post_byte |= 0x89 additional = numeric elif numeric.byte_len() == 1: if "PC" in self.right: raw_post_byte |= 0x8C additional = numeric size += 1 else: if numeric.int <= 0x1F: raw_post_byte |= numeric.int else: raw_post_byte |= 0x88 additional = numeric size += 1 return CodePackage(op_code=NumericValue(self.instruction.mode.ind), post_byte=NumericValue(raw_post_byte), additional=additional, size=size)
class Operand(ABC): def __init__(self, instruction, value=None): self.type = OperandType.UNKNOWN self.operand_string = "" self.instruction = instruction self.requires_resolution = False self.value = value if value else NoneValue(None) self.left = NoneValue(None) self.right = NoneValue(None) self.operation = "" @classmethod def create_from_str(cls, operand_string, instruction): try: return PseudoOperand(operand_string, instruction) except ValueError: pass try: return SpecialOperand(operand_string, instruction) except ValueError: pass try: return RelativeOperand(operand_string, instruction) except ValueError: pass try: return InherentOperand(operand_string, instruction) except ValueError: pass try: return ImmediateOperand(operand_string, instruction) except ValueError: pass try: return DirectOperand(operand_string, instruction) except ValueError: pass try: return ExtendedOperand(operand_string, instruction) except ValueError: pass try: return ExtendedIndexedOperand(operand_string, instruction) except ValueError: pass try: return IndexedOperand(operand_string, instruction) except ValueError: pass try: return ExpressionOperand(operand_string, instruction) except ValueError: pass try: return UnknownOperand(operand_string, instruction) except ValueError: pass raise ValueError("[{}] unknown operand type".format(operand_string)) def is_type(self, operand_type): """ Returns True if the type of this operand class is the type being compared. :param operand_type: the OperandType to check :return: True if the OperandType values match, False otherwise """ return self.type == operand_type def resolve_symbols(self, symbol_table): """ Given a symbol table, searches the operands for any symbols, and resolves them with values from the symbol table. Returns a (possibly) new Operand class type as a result of symbol resolution. :param symbol_table: the symbol table to search :return: self, or a new Operand class type with a resolved value """ if self.is_type(OperandType.EXPRESSION): return self.resolve_expression(symbol_table) if not self.value.is_type(ValueType.SYMBOL): return self symbol = self.get_symbol(self.value.ascii(), symbol_table) if symbol.is_type(ValueType.ADDRESS): self.value = AddressValue(symbol.int) if self.type == OperandType.UNKNOWN: return ExtendedOperand(self.operand_string, self.instruction, value=self.value) return self if symbol.is_type(ValueType.NUMERIC): self.value = copy(symbol) if self.value.hex_len() == 2: return DirectOperand(self.operand_string, self.instruction, value=self.value) return ExtendedOperand(self.operand_string, self.instruction, value=self.value) def resolve_expression(self, symbol_table): """ Attempts to resolve the expression contained in the operand using the symbol table supplied. Returns a (possibly) new Operand class type as a result of symbol resolution. :param symbol_table: the symbol table to use for resolution :return: self, or a new Operand class type with a resolved value """ if self.left.is_type(ValueType.SYMBOL): self.left = self.get_symbol(self.left.ascii(), symbol_table) if self.right.is_type(ValueType.SYMBOL): self.right = self.get_symbol(self.right.ascii(), symbol_table) if self.right.is_type(ValueType.NUMERIC) and self.left.is_type( ValueType.NUMERIC): left = self.left.int right = self.right.int if self.operation == "+": self.value = NumericValue("{}".format(left + right)) if self.operation == "-": self.value = NumericValue("{}".format(left - right)) if self.operation == "*": self.value = NumericValue("{}".format(int(left * right))) if self.operation == "/": self.value = NumericValue("{}".format(int(left / right))) if self.value.hex_len() == 2: return DirectOperand(self.operand_string, self.instruction, value=self.value) return ExtendedOperand(self.operand_string, self.instruction, value=self.value) raise ValueError("[{}] unresolved expression".format( self.operand_string)) @staticmethod def get_symbol(symbol_label, symbol_table): if symbol_label not in symbol_table: raise ValueError("[{}] not in symbol table".format(symbol_label)) return symbol_table[symbol_label] @abstractmethod def translate(self): """
def translate(self): if not self.instruction.mode.ind: raise ValueError( "Instruction [{}] does not support indexed addressing".format( self.instruction.mnemonic)) size = self.instruction.mode.ind_sz if not type(self.value) == str and self.value.is_type( ValueType.ADDRESS): size += 2 return CodePackage(op_code=NumericValue(self.instruction.mode.ind), post_byte=NumericValue(0x9F), additional=self.value, size=size) if not type(self.value) == str and self.value.is_type( ValueType.NUMERIC): size += 2 return CodePackage(op_code=NumericValue(self.instruction.mode.ind), post_byte=NumericValue(0x9F), additional=self.value, size=size) raw_post_byte = 0x80 additional = NoneValue() if "X" in self.right: raw_post_byte |= 0x00 if "Y" in self.right: raw_post_byte |= 0x20 if "U" in self.right: raw_post_byte |= 0x40 if "S" in self.right: raw_post_byte |= 0x60 if self.left == "": if "-" in self.right or "+" in self.right: if self.right == "X+" or self.right == "Y+" or self.right == "U+" or self.right == "S+": raise ValueError( "[{}] not allowed as an extended indirect value". format(self.right)) if self.right == "-X" or self.right == "-Y" or self.right == "-U" or self.right == "-S": raise ValueError( "[{}] not allowed as an extended indirect value". format(self.right)) if "++" in self.right: raw_post_byte |= 0x11 if "--" in self.right: raw_post_byte |= 0x13 else: raw_post_byte |= 0x14 elif self.left == "A" or self.left == "B" or self.left == "D": if self.left == "A": raw_post_byte |= 0x16 if self.left == "B": raw_post_byte |= 0x15 if self.left == "D": raw_post_byte |= 0x1B else: if "+" in self.right or "-" in self.right: raise ValueError("[{}] invalid indexed expression".format( self.operand_string)) if type(self.left) == str: self.left = NumericValue(self.left) if self.left.is_type(ValueType.ADDRESS): raise ValueError( "[{}] cannot translate address in left hand side".format( self.operand_string)) numeric = self.left if numeric.byte_len() == 2: size += 2 if "PC" in self.right: raw_post_byte |= 0x9D additional = numeric else: raw_post_byte |= 0x99 additional = numeric elif numeric.byte_len() == 1: size += 1 if "PC" in self.right: raw_post_byte |= 0x9C additional = numeric else: raw_post_byte |= 0x98 additional = numeric return CodePackage(op_code=NumericValue(self.instruction.mode.ind), post_byte=NumericValue(raw_post_byte), additional=additional, size=size)
class ExtendedIndexedOperand(Operand): def __init__(self, operand_string, instruction, value=None): super().__init__(instruction) self.type = OperandType.EXTENDED_INDIRECT self.operand_string = operand_string self.left = None self.right = None if value is not None: self.value = value return match = EXTENDED_INDIRECT_REGEX.match(self.operand_string) if not match: raise ValueError( "[{}] is not an extended indexed value".format(operand_string)) parsed_value = match.group("value") if "," not in parsed_value: self.value = Value.create_from_str(parsed_value, self.instruction) elif len(parsed_value.split(",")) == 2: self.left, self.right = parsed_value.split(",") else: raise ValueError( "[{}] incorrect number of commas in extended indexed value". format(operand_string)) def resolve_symbols(self, symbol_table): if not self.value.is_type(ValueType.NONE) and self.value.is_type( ValueType.SYMBOL): self.value = self.get_symbol(self.value.ascii(), symbol_table) return self if self.left and self.left != "": if self.left != "A" and self.left != "B" and self.left != "D": self.left = Value.create_from_str(self.left, self.instruction) if self.left.is_type(ValueType.SYMBOL): self.left = self.get_symbol(self.left.ascii(), symbol_table) return self def translate(self): if not self.instruction.mode.ind: raise ValueError( "Instruction [{}] does not support indexed addressing".format( self.instruction.mnemonic)) size = self.instruction.mode.ind_sz if not type(self.value) == str and self.value.is_type( ValueType.ADDRESS): size += 2 return CodePackage(op_code=NumericValue(self.instruction.mode.ind), post_byte=NumericValue(0x9F), additional=self.value, size=size) if not type(self.value) == str and self.value.is_type( ValueType.NUMERIC): size += 2 return CodePackage(op_code=NumericValue(self.instruction.mode.ind), post_byte=NumericValue(0x9F), additional=self.value, size=size) raw_post_byte = 0x80 additional = NoneValue() if "X" in self.right: raw_post_byte |= 0x00 if "Y" in self.right: raw_post_byte |= 0x20 if "U" in self.right: raw_post_byte |= 0x40 if "S" in self.right: raw_post_byte |= 0x60 if self.left == "": if "-" in self.right or "+" in self.right: if self.right == "X+" or self.right == "Y+" or self.right == "U+" or self.right == "S+": raise ValueError( "[{}] not allowed as an extended indirect value". format(self.right)) if self.right == "-X" or self.right == "-Y" or self.right == "-U" or self.right == "-S": raise ValueError( "[{}] not allowed as an extended indirect value". format(self.right)) if "++" in self.right: raw_post_byte |= 0x11 if "--" in self.right: raw_post_byte |= 0x13 else: raw_post_byte |= 0x14 elif self.left == "A" or self.left == "B" or self.left == "D": if self.left == "A": raw_post_byte |= 0x16 if self.left == "B": raw_post_byte |= 0x15 if self.left == "D": raw_post_byte |= 0x1B else: if "+" in self.right or "-" in self.right: raise ValueError("[{}] invalid indexed expression".format( self.operand_string)) if type(self.left) == str: self.left = NumericValue(self.left) if self.left.is_type(ValueType.ADDRESS): raise ValueError( "[{}] cannot translate address in left hand side".format( self.operand_string)) numeric = self.left if numeric.byte_len() == 2: size += 2 if "PC" in self.right: raw_post_byte |= 0x9D additional = numeric else: raw_post_byte |= 0x99 additional = numeric elif numeric.byte_len() == 1: size += 1 if "PC" in self.right: raw_post_byte |= 0x9C additional = numeric else: raw_post_byte |= 0x98 additional = numeric return CodePackage(op_code=NumericValue(self.instruction.mode.ind), post_byte=NumericValue(raw_post_byte), additional=additional, size=size)
def test_symbol_byte_len_correct_when_resolved(self): result = SymbolValue('symbol') result.resolved = True result.value = NumericValue("$AB") self.assertEqual(1, result.byte_len())
def test_expression_symbol_left_resolves_correctly(self): symbol_table = {"VAR": NumericValue("$FE", mode=ExplicitAddressingMode.DIRECT)} result = ExpressionValue("VAR+1") result = result.resolve(symbol_table) self.assertEqual(result.hex(), "FF")
def translate(self): code_pkg = CodePackage() code_pkg.op_code = NumericValue(self.instruction.mode.imm) code_pkg.size = self.instruction.mode.imm_sz code_pkg.post_byte = 0x00 if self.instruction.mnemonic == "PSHS" or self.instruction.mnemonic == "PULS": if not self.operand_string: raise ValueError("one or more registers must be specified") registers = self.operand_string.split(",") for register in registers: if register not in REGISTERS: raise ValueError("[{}] unknown register".format(register)) code_pkg.post_byte |= 0x06 if register == "D" else 0x00 code_pkg.post_byte |= 0x01 if register == "CC" else 0x00 code_pkg.post_byte |= 0x02 if register == "A" else 0x00 code_pkg.post_byte |= 0x04 if register == "B" else 0x00 code_pkg.post_byte |= 0x08 if register == "DP" else 0x00 code_pkg.post_byte |= 0x10 if register == "X" else 0x00 code_pkg.post_byte |= 0x20 if register == "Y" else 0x00 code_pkg.post_byte |= 0x40 if register == "U" else 0x00 code_pkg.post_byte |= 0x80 if register == "PC" else 0x00 if self.instruction.mnemonic == "EXG" or self.instruction.mnemonic == "TFR": registers = self.operand_string.split(",") if len(registers) != 2: raise ValueError("[{}] requires exactly 2 registers".format( self.instruction.mnemonic)) if registers[0] not in REGISTERS: raise ValueError("[{}] unknown register".format(registers[0])) if registers[1] not in REGISTERS: raise ValueError("[{}] unknown register".format(registers[1])) code_pkg.post_byte |= 0x00 if registers[0] == "D" else 0x00 code_pkg.post_byte |= 0x00 if registers[1] == "D" else 0x00 code_pkg.post_byte |= 0x10 if registers[0] == "X" else 0x00 code_pkg.post_byte |= 0x01 if registers[1] == "X" else 0x00 code_pkg.post_byte |= 0x20 if registers[0] == "Y" else 0x00 code_pkg.post_byte |= 0x02 if registers[1] == "Y" else 0x00 code_pkg.post_byte |= 0x30 if registers[0] == "U" else 0x00 code_pkg.post_byte |= 0x03 if registers[1] == "U" else 0x00 code_pkg.post_byte |= 0x40 if registers[0] == "S" else 0x00 code_pkg.post_byte |= 0x04 if registers[1] == "S" else 0x00 code_pkg.post_byte |= 0x50 if registers[0] == "PC" else 0x00 code_pkg.post_byte |= 0x05 if registers[1] == "PC" else 0x00 code_pkg.post_byte |= 0x80 if registers[0] == "A" else 0x00 code_pkg.post_byte |= 0x08 if registers[1] == "A" else 0x00 code_pkg.post_byte |= 0x90 if registers[0] == "B" else 0x00 code_pkg.post_byte |= 0x09 if registers[1] == "B" else 0x00 code_pkg.post_byte |= 0xA0 if registers[0] == "CC" else 0x00 code_pkg.post_byte |= 0x0A if registers[1] == "CC" else 0x00 code_pkg.post_byte |= 0xB0 if registers[0] == "DP" else 0x00 code_pkg.post_byte |= 0x0B if registers[1] == "DP" else 0x00 if code_pkg.post_byte not in \ [ 0x01, 0x10, 0x02, 0x20, 0x03, 0x30, 0x04, 0x40, 0x05, 0x50, 0x12, 0x21, 0x13, 0x31, 0x14, 0x41, 0x15, 0x51, 0x23, 0x32, 0x24, 0x42, 0x25, 0x52, 0x34, 0x43, 0x35, 0x53, 0x45, 0x54, 0x89, 0x98, 0x8A, 0xA8, 0x8B, 0xB8, 0x9A, 0xA9, 0x9B, 0xB9, 0xAB, 0xBA, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x88, 0x99, 0xAA, 0xBB ]: raise ValueError("[{}] of [{}] to [{}] not allowed".format( self.instruction.mnemonic, registers[0], registers[1])) code_pkg.post_byte = NumericValue(code_pkg.post_byte) return code_pkg
def test_value_high_value_zero_when_length_less_than_two(self): result = NumericValue("$0123", size_hint=1) self.assertEqual(result.high_byte(), 0x00)
def test_value_low_value_correct(self): result = NumericValue("$1223") self.assertEqual(result.low_byte(), 0x23)
def test_value_high_value_correct_when_length_is_two(self): result = NumericValue("$0123") self.assertEqual(result.high_byte(), 0x01)
def test_value_low_value_zero_when_length_zero(self): result = NumericValue("$0123", size_hint=0) self.assertEqual(result.low_byte(), 0x00)
def test_value_low_value_correct_when_length_less_or_equal_to_two(self): result = NumericValue("$23") self.assertEqual(result.low_byte(), 0x23)