def assemble_indexed_operand(self, operand: Indexed, opcode_key, statement, opcode_bytes): if operand.base in RR: rr = RR[operand.base] if operand.offset in ACCUMULATOR_OFFSET_POST_BYTE: # Accumulator offset post_byte = ACCUMULATOR_OFFSET_POST_BYTE[operand.offset] post_byte |= rr << 5 return bytes((post_byte, )) elif isinstance(operand.offset, Integral): if operand.offset == 0: post_byte = 0b10000100 post_byte |= rr << 5 return bytes((post_byte, )) elif -16 <= operand.offset <= +15: # 5-bit offset post_byte = twos_complement(operand.offset, 5) post_byte |= rr << 5 return bytes((post_byte, )) elif -128 <= operand.offset <= +127: # 8-bit offset post_byte = 0b10001000 post_byte |= rr << 5 offset_byte = twos_complement(operand.offset, 8) return bytes((post_byte, offset_byte)) elif -32768 <= operand.offset <= +32767: # 16-bit offset post_byte = 0b10001001 post_byte |= rr << 5 offset_bytes = twos_complement(operand.offset, 16) return bytes( (post_byte, hi(offset_bytes), lo(offset_bytes))) else: raise ValueError( f"Cannot use indexed addressing offset {operand.offset} with base {operand.base}" ) elif isinstance(operand.base, AutoIncrementedRegister): if operand.base.register not in RR: raise ValueError( f"Cannot use auto pre-/post- increment or decrement with register {operand.base.register}" ) rr = RR[operand.base.register] post_byte = INDEX_CREMENT_POST_BYTE[operand.base.delta] post_byte |= rr << 5 return bytes((post_byte, )) else: raise ValueError( f"Cannot use {operand.base} as a base register for indexed addressing modes" )
def test_smallest_in_range_does_not_raise_value_error(num_bits): smallest = -(2**(num_bits - 1)) v = twos_complement(smallest, num_bits) c = int( ''.join(str(int(not int(x))) for x in bin(abs(smallest) - 1)[2:]).rjust(num_bits, '1'), 2) assert v == c
def test_twos_complement_negative(d): num_bits = d.draw(integers(min_value=1, max_value=32)) value = d.draw(integers(min_value=-2**(num_bits - 1), max_value=-1)) c = int( ''.join(str(int(not int(x))) for x in bin(abs(value) - 1)[2:]).rjust(num_bits, '1'), 2) assert twos_complement(value, num_bits) == c
def test_index_with_eight_bit_offset(index_register, offset): asm = AsmDsl() asm(LDA, {offset: index_register}, "8-BIT OFFSET FROM INDEX REGISTER") code = assemble(statements(asm)) assert code[0] == bytes( (0xA6, 0b10001000 | INDEX_REGISTER_CODES[index_register], twos_complement(offset, 8)))
def test_index_with_sixteen_bit_offset(offset, index_register): asm = AsmDsl() asm(LDA, {offset: index_register}, "16-BIT OFFSET FROM INDEX REGISTER") code = assemble(statements(asm)) assert code[0] == (bytes( (0xA6, 0b10001001 | INDEX_REGISTER_CODES[index_register])) + twos_complement(offset, 16).to_bytes( length=2, byteorder='big', signed=False))
def test_index_with_five_bit_offset(index_register, offset): assume(offset != 0) asm = AsmDsl() asm(LDA, {offset: index_register}, "5-BIT OFFSET FROM INDEX REGISTER") code = assemble(statements(asm)) assert code[0] == bytes( (0xA6, INDEX_REGISTER_CODES[index_register] | twos_complement(offset, 5)))
def _assemble_relative_operand(self, operand, operand_bytes_length, opcode_bytes): if isinstance(operand.address, Label): if operand.name in self._label_addresses: target_address = self._label_addresses[operand.name] # TODO: Consider threading opcode_bytes through as an argument offset = target_address - self.pos - len( opcode_bytes) - operand_bytes_length unsigned_offset = twos_complement(offset, operand_bytes_length * 8) result = self.value_to_bytes(unsigned_offset, operand_bytes_length) self._unresolved_labels.discard(operand) else: self._more_passes_required = True result = bytes(operand_bytes_length) self._unresolved_labels.add(operand) self._unreferenced_labels.discard(operand) else: # TODO: What if the operand is a number? raise NotImplementedError return result
def _(operand, opcode_key, asm, statement): # If we know the address of the label, use it if operand.name in asm._label_addresses: target_address = asm._label_addresses[operand.name] if opcode_key in branch_operand_widths: operand_bytes_length = branch_operand_widths[opcode_key] offset = target_address - asm.pos - len( asm._opcode_bytes) - operand_bytes_length unsigned_offset = twos_complement(offset, operand_bytes_length * 8) if opcode_key == REL8: return (unsigned_offset, ) elif opcode_key == REL16: return (hi(unsigned_offset), lo(unsigned_offset)) else: assert opcode_key is IMM return (hi(target_address), lo(target_address)) else: asm._more_passes_required = True if opcode_key in branch_operand_widths: return (0, ) * branch_operand_widths[opcode_key] else: assert opcode_key is IMM return (0, 0)
def test_twos_complement_non_positive_num_bits_raises_value_error(num_bits): with raises(ValueError): twos_complement(0, num_bits)
def test_largest_out_of_range_raises_value_error(num_bits): largest = -(2**(num_bits - 1)) - 1 with raises(ValueError): twos_complement(largest, num_bits)
def test_smallest_out_of_range_does_raises_value_error(num_bits): smallest = (2**(num_bits - 1)) with raises(ValueError): twos_complement(smallest, num_bits)
def test_largest_in_range_does_not_raise_value_error(num_bits): largest = (2**(num_bits - 1)) - 1 v = twos_complement(largest, num_bits) assert v == largest
def test_twos_complement_negative_too_narrow(d): value = d.draw(integers(max_value=-2)) num_bits = d.draw(integers(min_value=1, max_value=value.bit_length() - 1)) with raises(ValueError): twos_complement(value, num_bits)
def test_twos_complement_minus_one_too_narrow(): with raises(ValueError): twos_complement(-1, 0)
def test_twos_complement_positive_too_narrow(d): value = d.draw(integers(min_value=0)) num_bits = d.draw(integers(min_value=0, max_value=value.bit_length())) assume(num_bits >= 1) with raises(ValueError): twos_complement(value, num_bits)
def test_twos_complement_positive(d): num_bits = d.draw(integers(min_value=1, max_value=32)) value = d.draw(integers(min_value=0, max_value=(2**(num_bits - 1)) - 1)) assert twos_complement(value, num_bits) == value