"""Строгий разбор Intel HEX для проверки образа, загруженного в MAME.""" from __future__ import annotations from pathlib import Path def read_ihx(path: Path) -> dict[int, int]: """Возвращает разреженный образ и отвергает битые/конфликтующие записи.""" memory: dict[int, int] = {} base = 0 eof = False for number, raw in enumerate(Path(path).read_text().splitlines(), 1): if not raw.startswith(':'): raise ValueError(f'Intel HEX, строка {number}: отсутствует двоеточие') try: record = bytes.fromhex(raw[1:]) except ValueError as error: raise ValueError(f'Intel HEX, строка {number}: неверные hex-цифры') from error if len(record) < 5 or len(record) != record[0] + 5: raise ValueError(f'Intel HEX, строка {number}: неверная длина') if sum(record) & 0xff: raise ValueError(f'Intel HEX, строка {number}: неверная контрольная сумма') length = record[0] address = int.from_bytes(record[1:3], 'big') kind = record[3] data = record[4:4 + length] if eof: raise ValueError(f'Intel HEX, строка {number}: данные после EOF') if kind == 0: for offset, value in enumerate(data): absolute = base + address + offset if absolute in memory and memory[absolute] != value: raise ValueError(f'Intel HEX: конфликт по адресу {absolute:#x}') memory[absolute] = value elif kind == 1: if length or address: raise ValueError(f'Intel HEX, строка {number}: неверная EOF-запись') eof = True elif kind == 2: if length != 2 or address: raise ValueError(f'Intel HEX, строка {number}: неверная segment-запись') base = int.from_bytes(data, 'big') << 4 elif kind == 4: if length != 2 or address: raise ValueError(f'Intel HEX, строка {number}: неверная linear-запись') base = int.from_bytes(data, 'big') << 16 elif kind not in (3, 5): raise ValueError(f'Intel HEX, строка {number}: неизвестный тип {kind}') if not eof: raise ValueError('Intel HEX: отсутствует EOF') return memory