|
157 | 157 | __copyright__ = """ |
158 | 158 | objutils - Object file library for Python. |
159 | 159 |
|
160 | | - (C) 2010-2025 by Christoph Schueler <github.com/Christoph2, |
| 160 | + (C) 2010-2026 by Christoph Schueler <github.com/Christoph2, |
161 | 161 | cpu12.gems@googlemail.com> |
162 | 162 |
|
163 | 163 | All Rights Reserved |
|
218 | 218 | "int32": "i", |
219 | 219 | "uint64": "Q", |
220 | 220 | "int64": "q", |
| 221 | + "float16": "e", |
221 | 222 | "float32": "f", |
222 | 223 | "float64": "d", |
223 | 224 | } |
|
231 | 232 | "int32": 4, |
232 | 233 | "uint64": 8, |
233 | 234 | "int64": 8, |
| 235 | + "float16": 2, |
234 | 236 | "float32": 4, |
235 | 237 | "float64": 8, |
236 | 238 | } |
|
241 | 243 | "be": ">", |
242 | 244 | } |
243 | 245 |
|
| 246 | +ASAM_BYTEORDER_ALIASES = { |
| 247 | + "LITTLE_ENDIAN": "MSB_LAST", |
| 248 | + "BIG_ENDIAN": "MSB_FIRST", |
| 249 | +} |
| 250 | + |
| 251 | +ASAM_STRING_ENCODINGS = { |
| 252 | + "ASCII": "ascii", |
| 253 | + "UTF8": "utf-8", |
| 254 | + "UTF16": "utf-16", |
| 255 | + "UTF32": "utf-32", |
| 256 | +} |
| 257 | + |
| 258 | +ASAM_NUMERIC_DTYPES = { |
| 259 | + "UBYTE": "uint8", |
| 260 | + "SBYTE": "int8", |
| 261 | + "UWORD": "uint16", |
| 262 | + "SWORD": "int16", |
| 263 | + "ULONG": "uint32", |
| 264 | + "SLONG": "int32", |
| 265 | + "A_UINT64": "uint64", |
| 266 | + "A_INT64": "int64", |
| 267 | + "FLOAT16_IEEE": "float16", |
| 268 | + "FLOAT32_IEEE": "float32", |
| 269 | + "FLOAT64_IEEE": "float64", |
| 270 | +} |
| 271 | + |
| 272 | +ASAM_ENDIAN_FOR_BYTEORDER = { |
| 273 | + "MSB_FIRST": "be", |
| 274 | + "MSB_LAST": "le", |
| 275 | + "MSB_FIRST_MSW_LAST": "be", |
| 276 | + "MSB_LAST_MSW_FIRST": "le", |
| 277 | +} |
| 278 | + |
244 | 279 | TypeInformation = namedtuple("TypeInformation", "type byte_order size") |
245 | 280 |
|
246 | 281 | DTYPE = re.compile( |
247 | 282 | r""" |
248 | 283 | (?:(?P<uns>u)?int(?P<len>8 | 16 | 32 | 64)(?P<sep>[-/_:])(?P<end> be | le)) |
249 | 284 | | (?P<byte>byte) |
250 | | - | (?P<flt>float)(?P<flen>32 | 64)""", |
| 285 | + | (?P<flt>float)(?P<flen>16 | 32 | 64)""", |
251 | 286 | re.IGNORECASE | re.VERBOSE, |
252 | 287 | ) |
253 | 288 |
|
@@ -512,6 +547,50 @@ def _getformat(self, dtype: str, length: int = 1) -> str: |
512 | 547 | else: |
513 | 548 | return f"{BYTEORDER.get(bo)}{FORMATS.get(fmt)}" |
514 | 549 |
|
| 550 | + def _resolve_asam_byteorder(self, byte_order: str) -> str: |
| 551 | + normalized = byte_order.strip().upper() |
| 552 | + normalized = ASAM_BYTEORDER_ALIASES.get(normalized, normalized) |
| 553 | + if normalized not in ASAM_ENDIAN_FOR_BYTEORDER: |
| 554 | + raise ValueError(f"Unsupported ASAM byte order {byte_order!r}") |
| 555 | + return normalized |
| 556 | + |
| 557 | + def _permute_asam_bytes_for_read(self, data: bytes, byte_order: str) -> bytes: |
| 558 | + size = len(data) |
| 559 | + if size <= 1: |
| 560 | + return data |
| 561 | + if byte_order == "MSB_FIRST_MSW_LAST": |
| 562 | + if size % 2 != 0: |
| 563 | + raise ValueError("MSB_FIRST_MSW_LAST requires even-sized numeric types") |
| 564 | + return b"".join(data[idx + 1 : idx + 2] + data[idx : idx + 1] for idx in range(0, size, 2)) |
| 565 | + if byte_order == "MSB_LAST_MSW_FIRST": |
| 566 | + if size % 2 != 0: |
| 567 | + raise ValueError("MSB_LAST_MSW_FIRST requires even-sized numeric types") |
| 568 | + return b"".join(data[idx + 1 : idx + 2] + data[idx : idx + 1] for idx in range(0, size, 2)) |
| 569 | + return data |
| 570 | + |
| 571 | + def _permute_asam_bytes_for_write(self, data: bytes, byte_order: str) -> bytes: |
| 572 | + size = len(data) |
| 573 | + if size <= 1: |
| 574 | + return data |
| 575 | + if byte_order == "MSB_FIRST_MSW_LAST": |
| 576 | + if size % 2 != 0: |
| 577 | + raise ValueError("MSB_FIRST_MSW_LAST requires even-sized numeric types") |
| 578 | + return b"".join(data[idx + 1 : idx + 2] + data[idx : idx + 1] for idx in range(0, size, 2)) |
| 579 | + if byte_order == "MSB_LAST_MSW_FIRST": |
| 580 | + if size % 2 != 0: |
| 581 | + raise ValueError("MSB_LAST_MSW_FIRST requires even-sized numeric types") |
| 582 | + return b"".join(data[idx + 1 : idx + 2] + data[idx : idx + 1] for idx in range(0, size, 2)) |
| 583 | + return data |
| 584 | + |
| 585 | + def _asam_numeric_dtype_to_internal(self, asam_dtype: str, asam_byte_order: str) -> str: |
| 586 | + normalized_dtype = asam_dtype.strip().upper() |
| 587 | + internal_dtype = ASAM_NUMERIC_DTYPES.get(normalized_dtype) |
| 588 | + if internal_dtype is None: |
| 589 | + raise TypeError(f"Unsupported ASAM datatype {asam_dtype!r}") |
| 590 | + if internal_dtype in ("uint8", "int8"): |
| 591 | + return internal_dtype |
| 592 | + return f"{internal_dtype}_{ASAM_ENDIAN_FOR_BYTEORDER[asam_byte_order]}" |
| 593 | + |
515 | 594 | def read(self, addr: int, length: int, **kws) -> bytes: |
516 | 595 | """Read raw bytes from section at specified address. |
517 | 596 |
|
@@ -675,6 +754,72 @@ def read_numeric_array(self, addr: int, length: int, dtype: str, **kws) -> Union |
675 | 754 | data = self.data[offset : offset + data_size] |
676 | 755 | return struct.unpack(fmt, data) |
677 | 756 |
|
| 757 | + def read_asam_numeric(self, addr: int, dtype: str, byte_order: str = "MSB_LAST", **kws) -> Union[int, float]: |
| 758 | + asam_byte_order = self._resolve_asam_byteorder(byte_order) |
| 759 | + internal_dtype = self._asam_numeric_dtype_to_internal(dtype, asam_byte_order) |
| 760 | + value = self.read_numeric(addr, internal_dtype, **kws) |
| 761 | + if TYPE_SIZES.get(internal_dtype.split("_")[0], 0) <= 1: |
| 762 | + return value |
| 763 | + |
| 764 | + fmt = self._getformat(internal_dtype) |
| 765 | + packed = struct.pack(fmt, value) |
| 766 | + permuted = self._permute_asam_bytes_for_read(packed, asam_byte_order) |
| 767 | + return struct.unpack(fmt, permuted)[0] |
| 768 | + |
| 769 | + def write_asam_numeric( |
| 770 | + self, |
| 771 | + addr: int, |
| 772 | + value: Union[int, float], |
| 773 | + dtype: str, |
| 774 | + byte_order: str = "MSB_LAST", |
| 775 | + **kws, |
| 776 | + ) -> None: |
| 777 | + asam_byte_order = self._resolve_asam_byteorder(byte_order) |
| 778 | + internal_dtype = self._asam_numeric_dtype_to_internal(dtype, asam_byte_order) |
| 779 | + if TYPE_SIZES.get(internal_dtype.split("_")[0], 0) <= 1: |
| 780 | + self.write_numeric(addr, value, internal_dtype, **kws) |
| 781 | + return |
| 782 | + |
| 783 | + fmt = self._getformat(internal_dtype) |
| 784 | + packed = struct.pack(fmt, value) |
| 785 | + permuted = self._permute_asam_bytes_for_write(packed, asam_byte_order) |
| 786 | + self.write(addr, permuted) |
| 787 | + |
| 788 | + def read_asam_string(self, addr: int, dtype: str, length: int = -1, **kws) -> str: |
| 789 | + encoding = ASAM_STRING_ENCODINGS.get(dtype.strip().upper()) |
| 790 | + if encoding is None: |
| 791 | + raise TypeError(f"Unsupported ASAM string datatype {dtype!r}") |
| 792 | + if length == -1 and encoding in ("utf-8", "utf-16", "utf-32"): |
| 793 | + offset = addr - self.start_address |
| 794 | + if offset < 0: |
| 795 | + raise InvalidAddressError(f"read_asam_string(0x{addr:08x}) access out of bounds.") |
| 796 | + tail = self.data[offset:] |
| 797 | + terminator = "\x00".encode(encoding=encoding) |
| 798 | + pos = tail.find(terminator) |
| 799 | + if pos != -1: |
| 800 | + return tail[:pos].decode(encoding=encoding) |
| 801 | + raise TypeError("Unterminated String!!!") |
| 802 | + return self.read_string(addr, encoding=encoding, length=length, **kws) |
| 803 | + |
| 804 | + def write_asam_string(self, addr: int, value: str, dtype: str, **kws) -> None: |
| 805 | + encoding = ASAM_STRING_ENCODINGS.get(dtype.strip().upper()) |
| 806 | + if encoding is None: |
| 807 | + raise TypeError(f"Unsupported ASAM string datatype {dtype!r}") |
| 808 | + if encoding == "ascii": |
| 809 | + self.write_string(addr, value, encoding=encoding, **kws) |
| 810 | + return |
| 811 | + |
| 812 | + offset = addr - self.start_address |
| 813 | + if offset < 0: |
| 814 | + raise InvalidAddressError(f"write_asam_string(0x{addr:08x}) access out of bounds.") |
| 815 | + encoded = value.encode(encoding=encoding) |
| 816 | + terminator = "\x00".encode(encoding=encoding) |
| 817 | + total_length = len(encoded) + len(terminator) |
| 818 | + if offset + total_length > self.length: |
| 819 | + raise InvalidAddressError(f"write_asam_string(0x{addr:08x}) access out of bounds.") |
| 820 | + self.data[offset : offset + len(encoded)] = encoded |
| 821 | + self.data[offset + len(encoded) : offset + total_length] = terminator |
| 822 | + |
678 | 823 | def write_numeric_array(self, addr: int, data: Union[list[int], list[float]], dtype: str, **kws) -> None: |
679 | 824 | if not hasattr(data, "__iter__"): |
680 | 825 | raise TypeError("data must be iterable") |
|
0 commit comments