more serial improvements
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import unittest
|
||||
from typing import Any
|
||||
|
||||
from h8536.serial_semantics import analyze_serial_semantics
|
||||
|
||||
@@ -60,6 +61,92 @@ def only_semantics(testcase: unittest.TestCase, payload: dict) -> dict:
|
||||
return analysis["protocol_semantics"][0]
|
||||
|
||||
|
||||
def semantic_items(value: Any) -> list[Any]:
|
||||
if isinstance(value, list):
|
||||
return value
|
||||
if isinstance(value, dict):
|
||||
return list(value.values())
|
||||
return []
|
||||
|
||||
|
||||
def command_item(items: list[Any], command: int) -> Any:
|
||||
for item in items:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
command_value = item.get("command_value", item.get("command"))
|
||||
if command_value == command:
|
||||
return item
|
||||
if isinstance(command_value, str) and command_value.lower() == f"0x{command:02x}":
|
||||
return item
|
||||
return None
|
||||
|
||||
|
||||
def semantic_text(value: Any) -> str:
|
||||
if isinstance(value, dict):
|
||||
return " ".join(
|
||||
f"{key} {semantic_text(item)}"
|
||||
for key, item in value.items()
|
||||
).lower()
|
||||
if isinstance(value, list):
|
||||
return " ".join(semantic_text(item) for item in value).lower()
|
||||
return str(value).lower()
|
||||
|
||||
|
||||
def planned_semantics_payload() -> dict:
|
||||
return base_payload(
|
||||
[
|
||||
instruction(0xBF00, "MOV:G.B", "@H'F860, R0", [0xF860]),
|
||||
instruction(0xBF04, "AND.B", "#H'07, R0"),
|
||||
instruction(0xBF08, "CMP:E.B", "#H'00, R0"),
|
||||
instruction(0xBF0C, "BEQ", "loc_C000", targets=[0xC000]),
|
||||
instruction(0xBF10, "CMP:E.B", "#H'01, R0"),
|
||||
instruction(0xBF14, "BEQ", "loc_C100", targets=[0xC100]),
|
||||
instruction(0xBF18, "CMP:E.B", "#H'06, R0"),
|
||||
instruction(0xBF1C, "BEQ", "loc_C600", targets=[0xC600]),
|
||||
instruction(0xBF20, "CMP:E.B", "#H'07, R0"),
|
||||
instruction(0xBF24, "BEQ", "loc_C700", targets=[0xC700]),
|
||||
instruction(0xC000, "MOV:G.B", "@H'F861, R1", [0xF861]),
|
||||
instruction(0xC004, "MOV:G.B", "@H'F862, R2", [0xF862]),
|
||||
instruction(0xC008, "BSR", "loc_622B", targets=[0x622B]),
|
||||
instruction(0xC00C, "MOV:G.W", "@H'F863, R3", [0xF863]),
|
||||
instruction(0xC010, "MOV:G.W", "R3, @H'F900", [0xF900]),
|
||||
instruction(0xC014, "MOV:G.W", "R3, @H'F920", [0xF920]),
|
||||
instruction(0xC018, "MOV:G.B", "#H'01, @H'FAA2", [0xFAA2]),
|
||||
instruction(0xC01C, "MOV:G.B", "#H'04, @H'F850", [0xF850]),
|
||||
instruction(0xC020, "MOV:G.B", "@H'F861, R4", [0xF861]),
|
||||
instruction(0xC024, "MOV:G.B", "R4, @H'F851", [0xF851]),
|
||||
instruction(0xC028, "MOV:G.B", "@H'F862, R5", [0xF862]),
|
||||
instruction(0xC02C, "MOV:G.B", "R5, @H'F852", [0xF852]),
|
||||
instruction(0xC030, "BSR", "loc_BA26", targets=[0xBA26]),
|
||||
instruction(0xC100, "MOV:G.B", "@H'F861, R1", [0xF861]),
|
||||
instruction(0xC104, "MOV:G.B", "@H'F862, R2", [0xF862]),
|
||||
instruction(0xC108, "BSR", "loc_622B", targets=[0x622B]),
|
||||
instruction(0xC10C, "MOV:G.W", "@H'F900, R3", [0xF900]),
|
||||
instruction(0xC110, "MOV:G.B", "#H'04, @H'F850", [0xF850]),
|
||||
instruction(0xC114, "MOV:G.B", "@H'F861, R4", [0xF861]),
|
||||
instruction(0xC118, "MOV:G.B", "R4, @H'F851", [0xF851]),
|
||||
instruction(0xC11C, "MOV:G.B", "@H'F862, R5", [0xF862]),
|
||||
instruction(0xC120, "MOV:G.B", "R5, @H'F852", [0xF852]),
|
||||
instruction(0xC124, "MOV:G.W", "R3, @H'F853", [0xF853]),
|
||||
instruction(0xC128, "MOV:G.B", "@H'F9B5, R6", [0xF9B5]),
|
||||
instruction(0xC12C, "BSR", "loc_BA26", targets=[0xBA26]),
|
||||
instruction(0xC600, "MOV:G.B", "@H'F861, R1", [0xF861]),
|
||||
instruction(0xC604, "MOV:G.B", "@H'F862, R2", [0xF862]),
|
||||
instruction(0xC608, "BSR", "loc_622B", targets=[0x622B]),
|
||||
instruction(0xC60C, "MOV:G.W", "@H'F863, R3", [0xF863]),
|
||||
instruction(0xC610, "MOV:G.W", "R3, @H'F940", [0xF940]),
|
||||
instruction(0xC614, "MOV:G.B", "#H'01, @H'F980", [0xF980]),
|
||||
instruction(0xC618, "MOV:G.B", "#H'01, @H'F9C0", [0xF9C0]),
|
||||
instruction(0xC700, "MOV:G.B", "#H'07, @H'F850", [0xF850]),
|
||||
instruction(0xC704, "MOV:G.B", "@H'F861, R1", [0xF861]),
|
||||
instruction(0xC708, "MOV:G.B", "R1, @H'F851", [0xF851]),
|
||||
instruction(0xC70C, "BSR", "loc_BA26", targets=[0xBA26]),
|
||||
instruction(0xC800, "CMP:E.B", "@H'F865, R7", [0xF865]),
|
||||
instruction(0xC804, "BNE", "loc_C700", targets=[0xC700]),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class SerialSemanticsTest(unittest.TestCase):
|
||||
def test_detects_low_three_bit_command_dispatch(self):
|
||||
payload = base_payload(
|
||||
@@ -139,6 +226,76 @@ class SerialSemanticsTest(unittest.TestCase):
|
||||
[0xF850, 0xF851, 0xF852, 0xF853, 0xF854],
|
||||
)
|
||||
|
||||
def test_planned_command_effects_include_core_command_behaviors(self):
|
||||
semantics = only_semantics(self, planned_semantics_payload())
|
||||
|
||||
self.assertIn("command_effects", semantics)
|
||||
effects = semantic_items(semantics["command_effects"])
|
||||
|
||||
command_0 = command_item(effects, 0x00)
|
||||
self.assertIsNotNone(command_0)
|
||||
command_0_text = semantic_text(command_0)
|
||||
self.assertIn("write", command_0_text)
|
||||
self.assertIn("primary_value_table", command_0_text)
|
||||
self.assertIn("current_value_table", command_0_text)
|
||||
|
||||
command_1 = command_item(effects, 0x01)
|
||||
self.assertIsNotNone(command_1)
|
||||
command_1_text = semantic_text(command_1)
|
||||
self.assertIn("read", command_1_text)
|
||||
self.assertIn("response", command_1_text)
|
||||
|
||||
command_6 = command_item(effects, 0x06)
|
||||
self.assertIsNotNone(command_6)
|
||||
command_6_text = semantic_text(command_6)
|
||||
self.assertIn("write", command_6_text)
|
||||
self.assertIn("secondary_value_table", command_6_text)
|
||||
|
||||
def test_planned_response_schema_tracks_immediates_and_rx_copies(self):
|
||||
semantics = only_semantics(self, planned_semantics_payload())
|
||||
|
||||
self.assertIn("response_schema", semantics)
|
||||
schema_text = semantic_text(semantics["response_schema"])
|
||||
|
||||
self.assertIn("tx", schema_text)
|
||||
self.assertIn("byte0", schema_text)
|
||||
self.assertIn("0x04", schema_text)
|
||||
self.assertIn("byte1", schema_text)
|
||||
self.assertIn("rx[1]", schema_text)
|
||||
self.assertIn("byte2", schema_text)
|
||||
self.assertIn("rx[2]", schema_text)
|
||||
|
||||
def test_planned_table_map_candidates_name_known_tables(self):
|
||||
semantics = only_semantics(self, planned_semantics_payload())
|
||||
|
||||
self.assertIn("table_map_candidates", semantics)
|
||||
table_text = semantic_text(semantics["table_map_candidates"])
|
||||
|
||||
self.assertIn("primary_value_table", table_text)
|
||||
self.assertIn("current_value_table", table_text)
|
||||
self.assertIn("secondary_value_table", table_text)
|
||||
self.assertIn("flag_table", table_text)
|
||||
|
||||
def test_planned_state_variable_candidates_include_known_addresses(self):
|
||||
semantics = only_semantics(self, planned_semantics_payload())
|
||||
|
||||
self.assertIn("state_variable_candidates", semantics)
|
||||
state_text = semantic_text(semantics["state_variable_candidates"])
|
||||
|
||||
self.assertIn("faa2", state_text)
|
||||
self.assertIn("f9b5", state_text)
|
||||
self.assertIn("f9c0", state_text)
|
||||
|
||||
def test_planned_retry_error_model_identifies_retransmit_and_checksum_error(self):
|
||||
semantics = only_semantics(self, planned_semantics_payload())
|
||||
|
||||
self.assertIn("retry_error_model", semantics)
|
||||
retry_text = semantic_text(semantics["retry_error_model"])
|
||||
|
||||
self.assertIn("retransmit", retry_text)
|
||||
self.assertIn("0x07", retry_text)
|
||||
self.assertIn("checksum_error_response", retry_text)
|
||||
|
||||
def test_missing_serial_reconstruction_candidates_emit_no_protocol_semantics(self):
|
||||
payload = {
|
||||
"serial_reconstruction": {"candidates": []},
|
||||
|
||||
Reference in New Issue
Block a user