dasmos 4.2.1__tar.gz → 4.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dasmos-4.2.1/src/dasmos.egg-info → dasmos-4.3.0}/PKG-INFO +2 -2
- {dasmos-4.2.1 → dasmos-4.3.0}/README.md +1 -1
- {dasmos-4.2.1 → dasmos-4.3.0}/pyproject.toml +1 -1
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/__init__.py +1 -1
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/asm_renderer.py +277 -94
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/labels.py +42 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/move.py +12 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/program.py +39 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/disassembler.py +235 -60
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/acorn_mos/hooks.py +7 -8
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/renderers/beebasm/renderer.py +63 -11
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/renderers/json/renderer.py +59 -16
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/renderers/tass64/renderer.py +48 -1
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/hooks.py +14 -12
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ir.py +7 -1
- {dasmos-4.2.1 → dasmos-4.3.0/src/dasmos.egg-info}/PKG-INFO +2 -2
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos.egg-info/SOURCES.txt +5 -0
- dasmos-4.3.0/tests/test_binary_addressed_api.py +134 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_golden_json.py +5 -3
- dasmos-4.3.0/tests/test_move_owned_labels.py +166 -0
- dasmos-4.3.0/tests/test_multi_image.py +399 -0
- dasmos-4.3.0/tests/test_offset_bases.py +113 -0
- dasmos-4.3.0/tests/test_overlapping_moves.py +151 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/LICENSE +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/setup.cfg +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/_text.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/cli.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/annotations.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/classification.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/config.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/cpu_state.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/data_type.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/disassembly.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/expr.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/expr_parse.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/file_header.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/format_hint.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/markdown_asm.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/memory.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/core/regions.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/cpu.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/environment.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/exceptions.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/expr.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/cpus/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/cpus/cpu6502/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/cpus/cpu6502/cpu.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/cpus/cpu65c02/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/cpus/cpu65c02/cpu.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/_acorn_bbc_common.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/acorn_fdc_1770/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/acorn_fdc_1770/environment.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/acorn_fdc_8271/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/acorn_fdc_8271/environment.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/acorn_master_hardware/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/acorn_master_hardware/environment.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/acorn_model_b_hardware/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/acorn_model_b_hardware/environment.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/acorn_mos/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/acorn_mos/enums.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/acorn_mos/environment.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/acorn_sideways_rom/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/acorn_sideways_rom/environment.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/bbc_basic_6502/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/bbc_basic_6502/environment.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/environments/bbc_basic_6502/floats.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/renderers/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/renderers/beebasm/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/renderers/json/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/ext/renderers/tass64/__init__.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/extension.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/output.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/py.typed +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos/renderer.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos.egg-info/dependency_links.txt +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos.egg-info/entry_points.txt +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos.egg-info/requires.txt +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/src/dasmos.egg-info/top_level.txt +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_absolute_operands.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_adfs_roundtrip.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_anfs_418_roundtrip.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_anfs_421_roundtrip.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_beebasm.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_cli.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_cpu.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_cpu6502.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_cpu65c02.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_disassembler.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_driver_roundtrip.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_econet_bridge_roundtrip.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_environment.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_expr.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_expr_operators.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_expr_parse.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_expression_symbols.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_file_header.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_forward_operand_size.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_golden_asm.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_include_binary.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_index_regions.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_json_renderer.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_macros.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_mnemonic_hash.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_nfs_334_roundtrip.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_nfs_roundtrip.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_output.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_program.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_py8dis2dasmos.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_reference_kinds.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_renderer.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_smoke.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_tass64.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_trace.py +0 -0
- {dasmos-4.2.1 → dasmos-4.3.0}/tests/test_tube_client_roundtrip.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dasmos
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.0
|
|
4
4
|
Summary: Extensible tracing disassembler for classic CPUs, with CPU, renderer and environment extension points.
|
|
5
5
|
Author-email: Robert Smallshire <rob@sixty-north.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -32,7 +32,7 @@ Dynamic: license-file
|
|
|
32
32
|
<img src="https://raw.githubusercontent.com/acornaeology/dasmos/master/docs/_static/dasmos-banner.svg" alt="Dasmos — an extensible tracing disassembler for classic CPUs">
|
|
33
33
|
</p>
|
|
34
34
|
|
|
35
|
-
An extensible tracing disassembler for classic CPUs, version `4.
|
|
35
|
+
An extensible tracing disassembler for classic CPUs, version `4.3.0`.
|
|
36
36
|
|
|
37
37
|
<p align="center">
|
|
38
38
|
<a href="https://pypi.org/project/dasmos/"><img src="https://img.shields.io/pypi/v/dasmos.svg" alt="PyPI"></a>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<img src="https://raw.githubusercontent.com/acornaeology/dasmos/master/docs/_static/dasmos-banner.svg" alt="Dasmos — an extensible tracing disassembler for classic CPUs">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
An extensible tracing disassembler for classic CPUs, version `4.
|
|
5
|
+
An extensible tracing disassembler for classic CPUs, version `4.3.0`.
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<a href="https://pypi.org/project/dasmos/"><img src="https://img.shields.io/pypi/v/dasmos.svg" alt="PyPI"></a>
|
|
@@ -4,7 +4,7 @@ The top-level package re-exports the public API. Consumers should import
|
|
|
4
4
|
from :mod:`dasmos` rather than reaching into sub-modules.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
__version__ = "4.
|
|
7
|
+
__version__ = "4.3.0"
|
|
8
8
|
|
|
9
9
|
from dasmos.core.annotations import Align, Annotation, AnnotationStore, Banner, Comment
|
|
10
10
|
from dasmos.core.format_hint import FormatHint
|
|
@@ -19,6 +19,7 @@ subclass's syntax methods.
|
|
|
19
19
|
|
|
20
20
|
from __future__ import annotations
|
|
21
21
|
|
|
22
|
+
import re
|
|
22
23
|
import warnings
|
|
23
24
|
from typing import TYPE_CHECKING
|
|
24
25
|
|
|
@@ -54,7 +55,7 @@ from dasmos.core.expr import (
|
|
|
54
55
|
from dasmos.core.format_hint import FormatHint
|
|
55
56
|
from dasmos.core.markdown_asm import markdown_to_asm_text, strip_address_uri_links
|
|
56
57
|
from dasmos.core.memory import BinaryAddr
|
|
57
|
-
from dasmos.core.move import Move
|
|
58
|
+
from dasmos.core.move import BASE_MOVE_ID, Move
|
|
58
59
|
from dasmos.cpu import Opcode, OperandKind
|
|
59
60
|
from dasmos.exceptions import MacroRenderError
|
|
60
61
|
from dasmos.output import TextOutput
|
|
@@ -220,7 +221,9 @@ class AssemblerRenderer(TextRenderer):
|
|
|
220
221
|
"""
|
|
221
222
|
return None
|
|
222
223
|
|
|
223
|
-
def _save_directive(
|
|
224
|
+
def _save_directive(
|
|
225
|
+
self, load_start, load_end, program=None, name=None,
|
|
226
|
+
) -> str | None:
|
|
224
227
|
"""A directive writing the loaded range to a binary file, or
|
|
225
228
|
``None`` when the assembler saves via a command-line flag
|
|
226
229
|
instead. Default: ``None`` (no in-source save). Beebasm
|
|
@@ -229,10 +232,76 @@ class AssemblerRenderer(TextRenderer):
|
|
|
229
232
|
|
|
230
233
|
``program`` is the IR's :class:`~dasmos.core.program.ProgramInfo`
|
|
231
234
|
(or ``None``) — load-and-run metadata (exec/reload addresses) a
|
|
232
|
-
backend may fold into its save directive.
|
|
235
|
+
backend may fold into its save directive. ``name`` is the
|
|
236
|
+
single image's declared name (``d.load(name=...)``), or
|
|
237
|
+
``None``; an explicit :attr:`output_filename` takes precedence.
|
|
233
238
|
"""
|
|
234
239
|
return None
|
|
235
240
|
|
|
241
|
+
def _image_save_directive(self, image, start_ref: str, end_ref: str) -> str | None:
|
|
242
|
+
"""A directive saving one of several loaded images as its own
|
|
243
|
+
file (#49), or ``None`` when the assembler has no in-source save.
|
|
244
|
+
|
|
245
|
+
``image`` is the :class:`~dasmos.core.program.LoadedImage`;
|
|
246
|
+
``start_ref`` / ``end_ref`` are the expressions for its range —
|
|
247
|
+
its boundary labels, or hex literals when those are suppressed.
|
|
248
|
+
Default ``None``; beebasm overrides with its ``save``.
|
|
249
|
+
"""
|
|
250
|
+
return None
|
|
251
|
+
|
|
252
|
+
def _segment_boundary_labels(
|
|
253
|
+
self, segments,
|
|
254
|
+
) -> list[tuple[str | None, str | None]]:
|
|
255
|
+
"""``(start_label, end_label)`` for each segment of the main walk.
|
|
256
|
+
|
|
257
|
+
A single segment uses the familiar ``{prefix}start`` /
|
|
258
|
+
``{prefix}end``. Several (one per image) get
|
|
259
|
+
``{prefix}{name}_start`` / ``{prefix}{name}_end``, the image
|
|
260
|
+
name made into an identifier (``Dunjun4`` -> ``dunjun4``, ``Game
|
|
261
|
+
B`` -> ``game_b``) and kept unique. Suppressed boundary labels
|
|
262
|
+
(an empty prefix) give ``(None, None)``.
|
|
263
|
+
"""
|
|
264
|
+
if len(segments) == 1:
|
|
265
|
+
return [(self.boundary_start_label, self.boundary_end_label)]
|
|
266
|
+
if not self.emit_boundary_labels:
|
|
267
|
+
return [(None, None)] * len(segments)
|
|
268
|
+
prefix = self.boundary_label_prefix
|
|
269
|
+
return [
|
|
270
|
+
(f"{prefix}{ident}_start", f"{prefix}{ident}_end")
|
|
271
|
+
for ident in self._image_identifiers(
|
|
272
|
+
[image for _start, _end, image in segments],
|
|
273
|
+
)
|
|
274
|
+
]
|
|
275
|
+
|
|
276
|
+
@staticmethod
|
|
277
|
+
def _image_identifiers(images) -> list[str]:
|
|
278
|
+
"""An identifier for each image, from its name — lower-cased,
|
|
279
|
+
other characters folded to ``_`` (``Game B`` -> ``game_b``) —
|
|
280
|
+
made unique by a numeric suffix. Used for per-image boundary
|
|
281
|
+
labels and section names."""
|
|
282
|
+
identifiers: list[str] = []
|
|
283
|
+
for image in images:
|
|
284
|
+
stem = re.sub(r"[^0-9a-z_]+", "_", image.name.lower()).strip("_") or "image"
|
|
285
|
+
candidate, n = stem, 2
|
|
286
|
+
while candidate in identifiers:
|
|
287
|
+
candidate, n = f"{stem}_{n}", n + 1
|
|
288
|
+
identifiers.append(candidate)
|
|
289
|
+
return identifiers
|
|
290
|
+
|
|
291
|
+
def image_section_declarations(self, section_ids: list[str]) -> list[str]:
|
|
292
|
+
"""Lines declaring one output section per loaded image, emitted
|
|
293
|
+
near the top when several images are loaded (#49). Default: none
|
|
294
|
+
— a backend that saves in-source (beebasm) needs no sections."""
|
|
295
|
+
return []
|
|
296
|
+
|
|
297
|
+
def image_section_start(self, section_id: str) -> list[str]:
|
|
298
|
+
"""Lines opening (or reopening) an image's section. Default: none."""
|
|
299
|
+
return []
|
|
300
|
+
|
|
301
|
+
def image_section_end(self, section_id: str) -> list[str]:
|
|
302
|
+
"""Lines closing an image's section. Default: none."""
|
|
303
|
+
return []
|
|
304
|
+
|
|
236
305
|
def included_binary_directive(self, path: str) -> list[str]:
|
|
237
306
|
"""Lines emitting an external binary include for ``path`` (an
|
|
238
307
|
:class:`~dasmos.core.classification.IncludedBinary` region).
|
|
@@ -272,6 +341,10 @@ class AssemblerRenderer(TextRenderer):
|
|
|
272
341
|
# uses ``b2r`` (most-recent move) and so misses inline anchors
|
|
273
342
|
# from a non-most-recent move's body emission.
|
|
274
343
|
self._inline_emitted_runtime_addrs: set[int] = set()
|
|
344
|
+
# Every label name emitted inline so far (as ``.name`` or as an
|
|
345
|
+
# inline ``name = base+offset``), so each is defined exactly
|
|
346
|
+
# once however many overlays reach its address.
|
|
347
|
+
self._inline_emitted_names: set[str] = set()
|
|
275
348
|
# Macros invoked *natively* (as a value function or a statement)
|
|
276
349
|
# during this render — only these get a definition emitted; a
|
|
277
350
|
# macro used only via inline-expansion needs none.
|
|
@@ -291,6 +364,10 @@ class AssemblerRenderer(TextRenderer):
|
|
|
291
364
|
# addresses of the labels it names (see :meth:`_render_opcode`);
|
|
292
365
|
# ``None`` otherwise.
|
|
293
366
|
self._operand_label_refs: set[int] | None = None
|
|
367
|
+
# The images saved as separate files by this render — set when
|
|
368
|
+
# more than one is loaded (#49), so :meth:`build_command` can
|
|
369
|
+
# describe one output per image. Empty for a single image.
|
|
370
|
+
self._saved_images: list = []
|
|
294
371
|
|
|
295
372
|
def render(self, ir: "IntermediateRepresentation") -> TextOutput:
|
|
296
373
|
"""Walk the IR's classifications and emit a beebasm source
|
|
@@ -362,21 +439,53 @@ class AssemblerRenderer(TextRenderer):
|
|
|
362
439
|
for move in [moves_by_src[src]]
|
|
363
440
|
}
|
|
364
441
|
|
|
442
|
+
# The main walk runs one segment per saved image. A single image
|
|
443
|
+
# (the usual case) is one segment spanning the whole load range.
|
|
444
|
+
# Several separately-loaded images (#49) are each their own
|
|
445
|
+
# segment — own ``org``, boundary labels and save — so the gaps
|
|
446
|
+
# between them are never assembled or saved; a backend that
|
|
447
|
+
# saves per section (64tass) also gets each image's content,
|
|
448
|
+
# moved blocks included, wrapped in that image's section.
|
|
449
|
+
images = sorted(ir.images, key=lambda image: image.start)
|
|
450
|
+
if len(images) > 1:
|
|
451
|
+
segments = [(image.start, image.end, image) for image in images]
|
|
452
|
+
section_ids = self._image_identifiers(images)
|
|
453
|
+
self._saved_images = images
|
|
454
|
+
if self.output_filename is not None:
|
|
455
|
+
warnings.warn(
|
|
456
|
+
f"output_filename {self.output_filename!r} is ignored: "
|
|
457
|
+
f"{len(images)} images are loaded and each is saved "
|
|
458
|
+
f"under its own name (see d.load(name=...))",
|
|
459
|
+
UserWarning,
|
|
460
|
+
stacklevel=2,
|
|
461
|
+
)
|
|
462
|
+
lines.extend(self.image_section_declarations(section_ids))
|
|
463
|
+
else:
|
|
464
|
+
segments = [(int(load_start), int(load_end), None)]
|
|
465
|
+
section_ids = [None]
|
|
466
|
+
boundary_labels = self._segment_boundary_labels(segments)
|
|
467
|
+
|
|
468
|
+
def section_of(binary_addr: int):
|
|
469
|
+
"""The section id of the image holding ``binary_addr``."""
|
|
470
|
+
for (start, end, _image), section_id in zip(segments, section_ids):
|
|
471
|
+
if start <= binary_addr < end:
|
|
472
|
+
return section_id
|
|
473
|
+
return None
|
|
474
|
+
|
|
365
475
|
# PHASE 1: emit move regions first, in source-binary-address
|
|
366
476
|
# order. Each move's emission is self-contained: org-to-src,
|
|
367
477
|
# source label, move-enter, body, move-exit.
|
|
368
478
|
for src_addr in sorted(moves_by_src):
|
|
369
479
|
move = moves_by_src[src_addr]
|
|
480
|
+
section_id = section_of(int(src_addr))
|
|
481
|
+
if section_id is not None:
|
|
482
|
+
lines.extend(self.image_section_start(section_id))
|
|
370
483
|
lines.extend(
|
|
371
484
|
self._emit_move_section(ir, move, moves_by_src, move_ids)
|
|
372
485
|
)
|
|
486
|
+
if section_id is not None:
|
|
487
|
+
lines.extend(self.image_section_end(section_id))
|
|
373
488
|
|
|
374
|
-
# PHASE 2: main code. ``org`` to the load start (resetting
|
|
375
|
-
# PC after the moves' restore-to-source positions left it
|
|
376
|
-
# somewhere arbitrary) plus the boundary start marker.
|
|
377
|
-
lines.extend(self.code_start(load_start, load_end, first=True))
|
|
378
|
-
if self.boundary_start_label is not None:
|
|
379
|
-
lines.append(self.inline_label(self.boundary_start_label))
|
|
380
489
|
|
|
381
490
|
# Skip ranges: any binary address covered by ANY move's
|
|
382
491
|
# source range was emitted in phase 1.
|
|
@@ -384,39 +493,67 @@ class AssemblerRenderer(TextRenderer):
|
|
|
384
493
|
(src, src + mv.length) for src, mv in moves_by_src.items()
|
|
385
494
|
]
|
|
386
495
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
496
|
+
for index, (seg_start, seg_end, _image) in enumerate(segments):
|
|
497
|
+
start_label, end_label = boundary_labels[index]
|
|
498
|
+
if section_ids[index] is not None:
|
|
499
|
+
lines.extend(self.image_section_start(section_ids[index]))
|
|
500
|
+
# ``org`` to the segment start (resetting PC after the moves'
|
|
501
|
+
# restore-to-source positions left it somewhere arbitrary)
|
|
502
|
+
# plus the boundary start marker.
|
|
503
|
+
lines.extend(self.code_start(seg_start, seg_end, first=index == 0))
|
|
504
|
+
if start_label is not None:
|
|
505
|
+
lines.append(self.inline_label(start_label))
|
|
506
|
+
|
|
507
|
+
# Track PC through the walk so we can emit ``org &<addr>``
|
|
508
|
+
# whenever we resume after a skipped (move-source) range —
|
|
509
|
+
# the main walk needs to resume at the byte just after the
|
|
510
|
+
# last move source.
|
|
511
|
+
expected_pc = seg_start
|
|
512
|
+
for binary_addr, classification in ir.classifications.iter_classified_starts():
|
|
513
|
+
ba = int(binary_addr)
|
|
514
|
+
if not (seg_start <= ba < seg_end):
|
|
515
|
+
continue
|
|
516
|
+
if any(s <= ba < e for s, e in move_src_ranges):
|
|
517
|
+
continue
|
|
518
|
+
if ba != expected_pc:
|
|
519
|
+
lines.extend(self.set_origin(ba))
|
|
520
|
+
expected_pc = ba
|
|
521
|
+
lines.extend(self._emit_classification_at(
|
|
522
|
+
ir, ba, classification,
|
|
523
|
+
active_move=None, active_move_id=None,
|
|
524
|
+
))
|
|
525
|
+
expected_pc = ba + classification.length()
|
|
526
|
+
|
|
527
|
+
# Position PC at the segment end before emitting the boundary
|
|
528
|
+
# end marker so the label takes that value (otherwise the
|
|
529
|
+
# marker lands wherever the last classification's last byte
|
|
530
|
+
# ended, which can be earlier when a move source extends to
|
|
531
|
+
# the end).
|
|
532
|
+
if end_label is not None:
|
|
533
|
+
if expected_pc != seg_end:
|
|
534
|
+
lines.extend(self.set_origin(seg_end))
|
|
535
|
+
lines.append(self.inline_label(end_label))
|
|
536
|
+
if section_ids[index] is not None:
|
|
537
|
+
lines.extend(self.image_section_end(section_ids[index]))
|
|
538
|
+
|
|
539
|
+
if len(segments) > 1:
|
|
540
|
+
saves = [
|
|
541
|
+
self._image_save_directive(
|
|
542
|
+
image,
|
|
543
|
+
boundary_labels[index][0] or self.hex4(seg_start),
|
|
544
|
+
boundary_labels[index][1] or self.hex4(seg_end),
|
|
545
|
+
)
|
|
546
|
+
for index, (seg_start, seg_end, image) in enumerate(segments)
|
|
547
|
+
]
|
|
548
|
+
else:
|
|
549
|
+
# A single image's declared name names its save, unless the
|
|
550
|
+
# renderer was given an explicit output filename.
|
|
551
|
+
name = images[0].name if images and images[0].name_declared else None
|
|
552
|
+
saves = [self._save_directive(load_start, load_end, ir.program, name)]
|
|
553
|
+
saves = [save for save in saves if save is not None]
|
|
554
|
+
if saves:
|
|
418
555
|
lines.append("")
|
|
419
|
-
lines.
|
|
556
|
+
lines.extend(saves)
|
|
420
557
|
lines.extend(self.disassembly_end())
|
|
421
558
|
|
|
422
559
|
# End-of-file label-reference frequency table.
|
|
@@ -626,7 +763,10 @@ class AssemblerRenderer(TextRenderer):
|
|
|
626
763
|
end = src_addr + length
|
|
627
764
|
move_id = move_ids.get(id(move))
|
|
628
765
|
src_label = self._first_explicit_name(ir, src_addr)
|
|
629
|
-
|
|
766
|
+
# The anchor for the trailer's copyblock / clear: a name this
|
|
767
|
+
# move's own body defines (or one belonging to no overlay), never
|
|
768
|
+
# another overlay's name from a later section.
|
|
769
|
+
dest_label = self._first_explicit_name(ir, dest_addr, move.move_id)
|
|
630
770
|
|
|
631
771
|
# Position PC at the move's source binary address. Every
|
|
632
772
|
# move emits this so the moves can appear in any order in
|
|
@@ -635,6 +775,7 @@ class AssemblerRenderer(TextRenderer):
|
|
|
635
775
|
if src_label is not None:
|
|
636
776
|
lines.append(self.inline_label(src_label))
|
|
637
777
|
self._inline_emitted_runtime_addrs.add(src_addr)
|
|
778
|
+
self._inline_emitted_names.add(src_label)
|
|
638
779
|
|
|
639
780
|
# Switch PC into the relocated destination range via the
|
|
640
781
|
# backend's pseudo-PC hook (beebasm ``org``+``copyblock`` idiom;
|
|
@@ -699,31 +840,33 @@ class AssemblerRenderer(TextRenderer):
|
|
|
699
840
|
for ann in ir.annotations.get_for_align(binary_addr, Align.BEFORE_LABEL):
|
|
700
841
|
lines.extend(self._render_annotation(ann))
|
|
701
842
|
|
|
702
|
-
# Inline labels at this runtime address.
|
|
703
|
-
#
|
|
704
|
-
#
|
|
705
|
-
#
|
|
706
|
-
#
|
|
707
|
-
#
|
|
708
|
-
#
|
|
709
|
-
#
|
|
710
|
-
# / ``clear`` directives in the
|
|
711
|
-
#
|
|
843
|
+
# Inline labels at this runtime address. A name is emitted once:
|
|
844
|
+
# moves that overlay the same run range (ADFS's three NMI
|
|
845
|
+
# handlers at &0D0A) each walk the shared addresses, and beebasm
|
|
846
|
+
# rejects a second ``.<name>`` (D-023). Each overlay emits the
|
|
847
|
+
# names visible from it — its own (declared with ``move=``, or
|
|
848
|
+
# made for its references) plus any belonging to no overlay — so
|
|
849
|
+
# a name lands in the section of the move it describes (#53),
|
|
850
|
+
# and a name belonging to no overlay lands in the first section
|
|
851
|
+
# to reach it. The ``copyblock`` / ``clear`` directives in the
|
|
852
|
+
# per-move trailers resolve names wherever they are defined.
|
|
712
853
|
label = ir.labels.get_label(runtime_addr)
|
|
713
|
-
if
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
if
|
|
719
|
-
|
|
854
|
+
overlay = active_move.move_id if active_move is not None else BASE_MOVE_ID
|
|
855
|
+
if label is not None:
|
|
856
|
+
visible = ir.labels.names_in_overlay(runtime_addr, overlay)
|
|
857
|
+
names = [n for n in visible if n not in self._inline_emitted_names]
|
|
858
|
+
first_visit = runtime_addr not in self._inline_emitted_runtime_addrs
|
|
859
|
+
if first_visit and (names or not label.explicit_name_texts()):
|
|
860
|
+
xref = self._format_inline_xref_summary(label, runtime_addr)
|
|
861
|
+
if xref is not None:
|
|
862
|
+
lines.append(xref)
|
|
720
863
|
# Insertion order, not alphabetical — preserves the label
|
|
721
864
|
# author's intended sequence at addresses with multiple
|
|
722
865
|
# names. The first-registered name is the structural one
|
|
723
866
|
# and should lead.
|
|
724
|
-
names = label.explicit_names_in_insertion_order()
|
|
725
867
|
for name in names:
|
|
726
868
|
lines.append(self.inline_label(name))
|
|
869
|
+
self._inline_emitted_names.add(name)
|
|
727
870
|
if names:
|
|
728
871
|
self._inline_emitted_runtime_addrs.add(runtime_addr)
|
|
729
872
|
# Mid-instruction labels INSIDE this classification's span
|
|
@@ -735,9 +878,8 @@ class AssemblerRenderer(TextRenderer):
|
|
|
735
878
|
# against the inline anchor's PC, which IS at the base's
|
|
736
879
|
# runtime. (Example: ``tube_cmd_lo = tube_dispatch_cmd+1``
|
|
737
880
|
# inside move 1 of NFS-3.65.)
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
base_name = base_names[0]
|
|
881
|
+
if visible:
|
|
882
|
+
base_name = visible[0]
|
|
741
883
|
for off in range(1, classification.length()):
|
|
742
884
|
inner_binary = binary_addr + off
|
|
743
885
|
if active_move is not None:
|
|
@@ -750,6 +892,19 @@ class AssemblerRenderer(TextRenderer):
|
|
|
750
892
|
inner_label = ir.labels.get_label(inner_runtime)
|
|
751
893
|
if inner_label is None:
|
|
752
894
|
continue
|
|
895
|
+
inner_names = [
|
|
896
|
+
name for name in ir.labels.names_in_overlay(
|
|
897
|
+
inner_runtime, overlay,
|
|
898
|
+
)
|
|
899
|
+
if name not in self._inline_emitted_names
|
|
900
|
+
]
|
|
901
|
+
# A nameless inner label (references only) still gets
|
|
902
|
+
# its xref summary and annotations, once. Names that
|
|
903
|
+
# are already defined, or that belong to another
|
|
904
|
+
# overlay of this run range, are left alone.
|
|
905
|
+
nameless = not inner_label.explicit_name_texts()
|
|
906
|
+
if not inner_names and not (nameless and first_visit):
|
|
907
|
+
continue
|
|
753
908
|
# Mid-instruction labels live inside an opcode's
|
|
754
909
|
# operand bytes — the addr the human cares about
|
|
755
910
|
# is the BINARY one (where the byte sits in the
|
|
@@ -781,8 +936,9 @@ class AssemblerRenderer(TextRenderer):
|
|
|
781
936
|
inner_binary, Align.BEFORE_LINE,
|
|
782
937
|
):
|
|
783
938
|
lines.extend(self._render_annotation(ann))
|
|
784
|
-
for inner_name in
|
|
939
|
+
for inner_name in inner_names:
|
|
785
940
|
lines.append(f"{inner_name} = {base_name}+{off}")
|
|
941
|
+
self._inline_emitted_names.add(inner_name)
|
|
786
942
|
self._inline_emitted_runtime_addrs.add(inner_runtime)
|
|
787
943
|
|
|
788
944
|
for ann in ir.annotations.get_for_align(binary_addr, Align.AFTER_LABEL):
|
|
@@ -971,29 +1127,35 @@ class AssemblerRenderer(TextRenderer):
|
|
|
971
1127
|
# emission yet (e.g. user added them after rendering
|
|
972
1128
|
# started — currently impossible but the codepath is
|
|
973
1129
|
# defensive).
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
)
|
|
980
|
-
if inline_anchor:
|
|
981
|
-
continue
|
|
982
|
-
# Mid-instruction labels with a named base are emitted
|
|
983
|
-
# inline under the base as ``name = base+offset`` — skip
|
|
984
|
-
# the equate table to avoid double-definition.
|
|
985
|
-
if (
|
|
986
|
-
in_range
|
|
987
|
-
and self._label_has_inline_offset_base(ir, runtime_addr)
|
|
988
|
-
):
|
|
989
|
-
continue
|
|
1130
|
+
#
|
|
1131
|
+
# The test is per name: where several moves overlay the
|
|
1132
|
+
# address, each overlay anchors only its own names (#53), so
|
|
1133
|
+
# one name can be anchored while another at the same address
|
|
1134
|
+
# still needs its equate here.
|
|
990
1135
|
explicit_names = sorted({
|
|
991
1136
|
name.text
|
|
992
1137
|
for name_list in label.explicit_names.values()
|
|
993
1138
|
for name in name_list
|
|
994
|
-
})
|
|
1139
|
+
} - self._inline_emitted_names)
|
|
995
1140
|
if not explicit_names:
|
|
996
1141
|
continue
|
|
1142
|
+
if runtime_addr not in self._inline_emitted_runtime_addrs:
|
|
1143
|
+
inline_anchor = (
|
|
1144
|
+
in_range
|
|
1145
|
+
and self._label_address_is_classification_start(
|
|
1146
|
+
ir, runtime_addr,
|
|
1147
|
+
)
|
|
1148
|
+
)
|
|
1149
|
+
if inline_anchor:
|
|
1150
|
+
continue
|
|
1151
|
+
# Mid-instruction labels with a named base are emitted
|
|
1152
|
+
# inline under the base as ``name = base+offset`` — skip
|
|
1153
|
+
# the equate table to avoid double-definition.
|
|
1154
|
+
if (
|
|
1155
|
+
in_range
|
|
1156
|
+
and self._label_has_inline_offset_base(ir, runtime_addr)
|
|
1157
|
+
):
|
|
1158
|
+
continue
|
|
997
1159
|
if (
|
|
998
1160
|
not in_range
|
|
999
1161
|
and not label.required
|
|
@@ -1085,16 +1247,16 @@ class AssemblerRenderer(TextRenderer):
|
|
|
1085
1247
|
return out
|
|
1086
1248
|
|
|
1087
1249
|
@staticmethod
|
|
1088
|
-
def _first_explicit_name(
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
``
|
|
1250
|
+
def _first_explicit_name(
|
|
1251
|
+
ir, runtime_addr: int, move_id: int = BASE_MOVE_ID,
|
|
1252
|
+
) -> str | None:
|
|
1253
|
+
"""First-registered explicit name at this runtime address as seen
|
|
1254
|
+
from the overlay laid out by ``move_id``, or ``None``. Used as
|
|
1255
|
+
the symbolic anchor for relocation directives — beebasm's
|
|
1256
|
+
``copyblock`` / ``clear`` / ``org`` need named anchors to
|
|
1257
|
+
reference.
|
|
1093
1258
|
"""
|
|
1094
|
-
|
|
1095
|
-
if label is None:
|
|
1096
|
-
return None
|
|
1097
|
-
names = label.explicit_names_in_insertion_order()
|
|
1259
|
+
names = ir.labels.names_in_overlay(runtime_addr, move_id)
|
|
1098
1260
|
return names[0] if names else None
|
|
1099
1261
|
|
|
1100
1262
|
def _build_stats_block(self, ir) -> list[str]:
|
|
@@ -2011,11 +2173,17 @@ class AssemblerRenderer(TextRenderer):
|
|
|
2011
2173
|
|
|
2012
2174
|
if kind is OperandKind.ADDRESS_8:
|
|
2013
2175
|
v = ir.memory.get_u8(operand_addr)
|
|
2014
|
-
return self._addr_text(
|
|
2176
|
+
return self._addr_text(
|
|
2177
|
+
ir, v, width=8, using_binary_addr=using_addr,
|
|
2178
|
+
active_move=active_move,
|
|
2179
|
+
)
|
|
2015
2180
|
|
|
2016
2181
|
if kind in (OperandKind.ADDRESS_16, OperandKind.ADDRESS_16_INDIRECT):
|
|
2017
2182
|
v = ir.memory.get_u16_le(operand_addr)
|
|
2018
|
-
return self._addr_text(
|
|
2183
|
+
return self._addr_text(
|
|
2184
|
+
ir, v, width=16, using_binary_addr=using_addr,
|
|
2185
|
+
active_move=active_move,
|
|
2186
|
+
)
|
|
2019
2187
|
|
|
2020
2188
|
if kind is OperandKind.RELATIVE_OFFSET:
|
|
2021
2189
|
offset = ir.memory.get_u8(operand_addr)
|
|
@@ -2041,6 +2209,7 @@ class AssemblerRenderer(TextRenderer):
|
|
|
2041
2209
|
target = base_runtime + opcode.length() + offset
|
|
2042
2210
|
return self._addr_text(
|
|
2043
2211
|
ir, target, width=16, using_binary_addr=using_addr,
|
|
2212
|
+
active_move=active_move,
|
|
2044
2213
|
)
|
|
2045
2214
|
|
|
2046
2215
|
# Defensive — the mode-name dispatch in _render_operand
|
|
@@ -2054,6 +2223,7 @@ class AssemblerRenderer(TextRenderer):
|
|
|
2054
2223
|
*,
|
|
2055
2224
|
width: int,
|
|
2056
2225
|
using_binary_addr: int | None = None,
|
|
2226
|
+
active_move=None,
|
|
2057
2227
|
) -> str:
|
|
2058
2228
|
"""Render an address operand: a label name if one is
|
|
2059
2229
|
registered, otherwise the appropriate hex literal.
|
|
@@ -2086,7 +2256,18 @@ class AssemblerRenderer(TextRenderer):
|
|
|
2086
2256
|
# they flow through this path naturally. Insertion order
|
|
2087
2257
|
# picks the structural / first-registered name when an
|
|
2088
2258
|
# address has multiple aliases.
|
|
2089
|
-
|
|
2259
|
+
# Inside a move that overlays a shared run range, prefer the
|
|
2260
|
+
# names that overlay sees (its own, then any belonging to no
|
|
2261
|
+
# overlay) over another overlay's (#53); every name at the
|
|
2262
|
+
# address has the same value, so this is about readability.
|
|
2263
|
+
overlay = (
|
|
2264
|
+
active_move.move_id if active_move is not None else BASE_MOVE_ID
|
|
2265
|
+
)
|
|
2266
|
+
visible = ir.labels.names_in_overlay(addr, overlay)
|
|
2267
|
+
names = visible + [
|
|
2268
|
+
n for n in label.explicit_names_in_insertion_order()
|
|
2269
|
+
if n not in visible
|
|
2270
|
+
]
|
|
2090
2271
|
if names:
|
|
2091
2272
|
if not self._label_address_is_in_range(ir, addr):
|
|
2092
2273
|
self._used_external_labels.add(addr)
|
|
@@ -2658,7 +2839,9 @@ class AssemblerRenderer(TextRenderer):
|
|
|
2658
2839
|
def _render_ref_node(self, ir, node: Ref, active_move) -> str:
|
|
2659
2840
|
"""Resolve a :class:`Ref` to its label's best name, or a hex
|
|
2660
2841
|
literal when the address has no name."""
|
|
2661
|
-
return self._addr_text(
|
|
2842
|
+
return self._addr_text(
|
|
2843
|
+
ir, int(node.runtime_addr), width=16, active_move=active_move,
|
|
2844
|
+
)
|
|
2662
2845
|
|
|
2663
2846
|
#: Unary operators rendered as a *function-like* form that brackets
|
|
2664
2847
|
#: its own operand (so operand precedence is irrelevant and no
|
|
@@ -216,6 +216,15 @@ class Label:
|
|
|
216
216
|
for name in name_list
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
+
def names_for_move(self, move_id: int) -> list[str]:
|
|
220
|
+
"""The explicit names recorded for ``move_id``, de-duplicated,
|
|
221
|
+
in registration order."""
|
|
222
|
+
out: list[str] = []
|
|
223
|
+
for name in self.explicit_names.get(move_id, []):
|
|
224
|
+
if name.text not in out:
|
|
225
|
+
out.append(name.text)
|
|
226
|
+
return out
|
|
227
|
+
|
|
219
228
|
def explicit_names_in_insertion_order(self) -> list[str]:
|
|
220
229
|
"""The explicit-name strings, de-duplicated but in registration
|
|
221
230
|
order. Use this when rendering the visible label sequence at
|
|
@@ -314,6 +323,39 @@ class LabelManager:
|
|
|
314
323
|
def get_label(self, runtime_addr) -> Label | None:
|
|
315
324
|
return self._labels.get(RuntimeAddr(runtime_addr))
|
|
316
325
|
|
|
326
|
+
def names_in_overlay(self, runtime_addr, move_id: int) -> list[str]:
|
|
327
|
+
"""The explicit names at ``runtime_addr`` as seen from the
|
|
328
|
+
overlay laid out by ``move_id`` (``BASE_MOVE_ID`` for unmoved
|
|
329
|
+
code), in registration order.
|
|
330
|
+
|
|
331
|
+
Several moves can share a run range — alternative overlays of
|
|
332
|
+
the same RAM. A name recorded for one of the moves covering
|
|
333
|
+
``runtime_addr`` names that move's layout, so the other
|
|
334
|
+
overlays don't see it (#53); a name recorded for no covering
|
|
335
|
+
move is visible from all of them. Where at most one move covers
|
|
336
|
+
the address there is nothing to tell apart and every name is
|
|
337
|
+
returned.
|
|
338
|
+
"""
|
|
339
|
+
label = self.get_label(runtime_addr)
|
|
340
|
+
if label is None:
|
|
341
|
+
return []
|
|
342
|
+
covering = self._move_manager.move_ids_for_runtime_addr(runtime_addr)
|
|
343
|
+
if len(covering) <= 1:
|
|
344
|
+
return label.explicit_names_in_insertion_order()
|
|
345
|
+
out: list[str] = []
|
|
346
|
+
for bucket, name_list in label.explicit_names.items():
|
|
347
|
+
if bucket != move_id and bucket in covering:
|
|
348
|
+
continue
|
|
349
|
+
for name in name_list:
|
|
350
|
+
if name.text not in out:
|
|
351
|
+
out.append(name.text)
|
|
352
|
+
return out
|
|
353
|
+
covering = self._move_manager.move_ids_for_runtime_addr(runtime_addr)
|
|
354
|
+
for bucket, name_list in label.explicit_names.items():
|
|
355
|
+
if bucket in covering and any(n.text == name for n in name_list):
|
|
356
|
+
return bucket
|
|
357
|
+
return None
|
|
358
|
+
|
|
317
359
|
def addr_for_name(self, name: str) -> RuntimeAddr | None:
|
|
318
360
|
"""Find the runtime address of a label by one of its names.
|
|
319
361
|
|