124 lines
4.6 KiB
Python
124 lines
4.6 KiB
Python
import unittest
|
|
|
|
from h8536.model import Instruction
|
|
from h8536.symbols import discover_symbols, instruction_accesses, symbol_for_address
|
|
|
|
|
|
def ins(address, mnemonic, operands="", references=None):
|
|
return Instruction(
|
|
address,
|
|
b"\x00",
|
|
mnemonic,
|
|
operands,
|
|
references=list(references or []),
|
|
)
|
|
|
|
|
|
class SymbolDiscoveryTest(unittest.TestCase):
|
|
def test_discovers_ram_symbol_counts_direction_and_widths(self):
|
|
instructions = {
|
|
0x1000: ins(0x1000, "MOV:G.B", "#H'12, @H'F680", [0xF680]),
|
|
0x1004: ins(0x1004, "CMP:G.B", "#H'01, @H'F680", [0xF680]),
|
|
0x1008: ins(0x1008, "ADD:Q.W", "#1, @H'F680", [0xF680]),
|
|
}
|
|
|
|
analysis = discover_symbols(instructions)
|
|
symbols = analysis["symbols"]
|
|
|
|
self.assertEqual(len(symbols), 1)
|
|
symbol = symbols[0]
|
|
self.assertEqual(symbol["address"], 0xF680)
|
|
self.assertEqual(symbol["name"], "ram_F680")
|
|
self.assertEqual(symbol["region"], "on_chip_ram")
|
|
self.assertEqual(symbol["kind"], "ram")
|
|
self.assertEqual(symbol["access_count"], 3)
|
|
self.assertEqual(symbol["read_count"], 2)
|
|
self.assertEqual(symbol["write_count"], 2)
|
|
self.assertEqual(symbol["unknown_count"], 0)
|
|
self.assertEqual(symbol["width_hints"], ["byte", "word"])
|
|
self.assertEqual(symbol["width"], "mixed")
|
|
self.assertEqual(symbol["first_access"], 0x1000)
|
|
self.assertEqual(symbol["last_access"], 0x1008)
|
|
self.assertEqual(symbol_for_address(analysis, 0xF680), "ram_F680")
|
|
|
|
def test_names_program_or_external_memory_and_excludes_registers_by_default(self):
|
|
instructions = [
|
|
ins(0x2000, "MOV:G.W", "@H'1234, R1", [0x1234]),
|
|
ins(0x2004, "MOV:G.B", "#H'80, @RAMCR", [0xFF11]),
|
|
]
|
|
|
|
analysis = discover_symbols(instructions)
|
|
|
|
self.assertEqual([symbol["name"] for symbol in analysis["symbols"]], ["mem_1234"])
|
|
symbol = analysis["symbols"][0]
|
|
self.assertEqual(symbol["region"], "program_or_external")
|
|
self.assertEqual(symbol["kind"], "memory")
|
|
self.assertEqual(symbol["read_count"], 1)
|
|
self.assertIsNone(symbol_for_address(analysis, 0xFF11))
|
|
|
|
def test_can_include_io_register_symbols_when_requested(self):
|
|
instructions = [
|
|
ins(0x2004, "MOV:G.B", "#H'80, @RAMCR", [0xFF11]),
|
|
]
|
|
|
|
analysis = discover_symbols(instructions, include_registers=True)
|
|
|
|
self.assertEqual(len(analysis["symbols"]), 1)
|
|
symbol = analysis["symbols"][0]
|
|
self.assertEqual(symbol["address"], 0xFF11)
|
|
self.assertEqual(symbol["name"], "RAMCR")
|
|
self.assertEqual(symbol["region"], "register_field")
|
|
self.assertEqual(symbol["kind"], "register")
|
|
self.assertEqual(symbol["write_count"], 1)
|
|
|
|
def test_bit_and_clear_operations_use_conservative_directions(self):
|
|
instructions = [
|
|
ins(0x3000, "BSET.B", "#4, @H'F690", [0xF690]),
|
|
ins(0x3002, "BCLR.B", "#4, @H'F690", [0xF690]),
|
|
ins(0x3004, "TST.B", "@H'F690", [0xF690]),
|
|
ins(0x3006, "CLR.B", "@H'F690", [0xF690]),
|
|
]
|
|
|
|
analysis = discover_symbols(instructions)
|
|
symbol = analysis["symbols"][0]
|
|
|
|
self.assertEqual(symbol["read_count"], 3)
|
|
self.assertEqual(symbol["write_count"], 3)
|
|
self.assertEqual(
|
|
[access["direction"] for access in symbol["accesses"]],
|
|
["read_write", "read_write", "read", "write"],
|
|
)
|
|
|
|
def test_optional_pointer_table_candidates_add_xrefs_without_io_pollution(self):
|
|
instructions = [
|
|
ins(0x4000, "MOV:G.B", "@H'F680, R0", [0xF680]),
|
|
]
|
|
data_candidates = {
|
|
"pointer_tables": [
|
|
{
|
|
"address": 0x0200,
|
|
"targets": [0xF680, 0x1234, 0xFF11],
|
|
},
|
|
],
|
|
}
|
|
|
|
analysis = discover_symbols(instructions, data_candidates=data_candidates)
|
|
by_name = {symbol["name"]: symbol for symbol in analysis["symbols"]}
|
|
|
|
self.assertEqual(by_name["ram_F680"]["xref_count"], 1)
|
|
self.assertEqual(by_name["mem_1234"]["access_count"], 0)
|
|
self.assertEqual(by_name["mem_1234"]["xref_count"], 1)
|
|
self.assertNotIn("RAMCR", by_name)
|
|
|
|
def test_instruction_accesses_handles_comma_inside_displacement_operand(self):
|
|
access = instruction_accesses(
|
|
ins(0x5000, "MOV:G.B", "@(H'0010,R1), @H'F682", [0xF682]),
|
|
)
|
|
|
|
self.assertEqual(access[0]["direction"], "write")
|
|
self.assertEqual(access[0]["operand"], "@H'F682")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|