Skip to content

Commit 58d3e0d

Browse files
authored
Assembly macros update (#466)
* Change the default for the labels * Update macro.inc and include_asm.h each time splat is run * Option to customize generated macro.inc file * use endlabels in incbins * changelog and docs * black * update docs * Update tests * review changes and some fixes * path fixes * nmlabel -> nonmatching * Use configured labels instead of hardcoded ones * fix python 3.9 not having newline argument on write_text * Fix windows?
1 parent f8b09ec commit 58d3e0d

File tree

25 files changed

+1145
-63
lines changed

25 files changed

+1145
-63
lines changed

CHANGELOG.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,25 @@
22

33
### 0.35.0
44

5-
* Add `pair_segment` option to segments:
5+
* Add `pair_segment` option to segments.
66
* Allows pairing the sections of two different segments together, making cross-segment rodata migration possible.
77
* Now `create_config` for N64 games can create basic `symbol_addrs.txt` and `reloc_addrs.txt` files from the information inferred from its analysis.
8+
* splat will now start generating and regenerating files related to assembly macros, allowing less painful updates to newer splat versions.
9+
* This includes generating files like `include/include_asm.h`, `include/macro.inc`, `include/labels.inc` and `include/gte_macros.inc`.
10+
* This makes easier for splat to introduce new kinds of labels or update the current definitions with minimal pain for the end-user.
11+
* This behaviour can be slightly customized with `generated_macro_inc_content` or completely turned off with `generate_asm_macros_files`.
12+
* Both `splat` and `create_config` have this behaviour.
13+
* New yaml options:
14+
* `asm_nonmatching_label_macro`: Set the label to be used to signal an assembly symbol haven't been matched yet. Defaults to `nonmatching`.
15+
* `asm_data_end_label`: Set the label used at the end of a data symbol. Defaults to `enddlabel`.
16+
* `generated_macro_inc_content`: A string defining custom contents to be emitted at the generated `include/macro.inc` file.
17+
* `generate_asm_macros_files`: Controls if the assembly macros files should be regenerated by splat or not.
18+
* Change the default value for multiple assembly macro labels.
19+
* `asm_function_alt_macro`: Changed from `glabel` to `alabel`.
20+
* `asm_jtbl_label_macro`: Changed from `glabel` to `jlabel`.
21+
* `asm_data_macro`: Changed from `glabel` to `dlabel`.
22+
* `asm_end_label`: Changed from empty to `endlabel`.
23+
* `spimdisasm` 1.36.0 or above is now required.
824

925
### 0.34.3
1026

docs/Configuration.md

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,21 @@ String that is placed before the contents of newly-generated assembly (`.s`) fil
122122
generated_s_preamble: .set fp=64
123123
```
124124

125+
### generated_macro_inc_content
126+
127+
String that is placed after the contents of the splat-generated `macro.inc` file.
128+
129+
### generate_asm_macros_files
130+
131+
Tells splat to regenerate files containing assembly macros and C macros each time splat is run.
132+
133+
Specifically splat generates `include/include_asm.h`, `include/macro.inc`, `include/labels.inc` and `include/gte_macros.inc`, which contain the proper C and assembly macro definitions expected for building the generated assembly correctly. This allows splat to update the macro definitions with minimal user intervention and headaches.
134+
135+
Turning this off can be useful in case the user wants to control exactly the contents of those files, but it is not recommended, since the user definitions may get outdated. Before turning this option off consider using the [`generated_macro_inc_content`](#generated_macro_inc_content) option to customize the contents of the generated `macro.inc` file.
136+
137+
Some files may not be generated depending on the selected platform and compiler, because those setups don't require them. For example `include_asm.h` won't be generated if the compiler is set to `IDO` or `MWCCPS2`. `include/gte_macros.inc` is only generated on psx projects.
138+
139+
Defaults to `True`.
125140

126141
### o_as_suffix
127142

@@ -653,26 +668,48 @@ Determines the macro used to declare functions in asm files
653668

654669
### asm_function_alt_macro
655670

656-
Determines the macro used to declare symbols in the middle of functions in asm files (which may be alternative entries)
671+
Determines the macro used to declare symbols in the middle of functions in asm files (which may be alternative entries).
672+
673+
Defaults to `alabel`.
657674

658675
### asm_jtbl_label_macro
659676

660-
Determines the macro used to declare jumptable labels in asm files
677+
Determines the macro used to declare jumptable labels in asm files.
678+
679+
Defaults to `jlabel`.
661680

662681
### asm_data_macro
663682

664-
Determines the macro used to declare data symbols in asm files
683+
Determines the macro used to declare data symbols in asm files.
684+
685+
Defaults to `dlabel`.
665686

666687
### asm_end_label
667688

668-
Determines the macro used at the end of a function, such as endlabel or .end
689+
Determines the macro used at the end of a function, such as `endlabel` or `.end`.
690+
691+
Defaults to `endlabel`.
692+
693+
### asm_data_end_label
694+
695+
Determines the macro used at the end of a data symbol.
696+
697+
Defaults to `enddlabel`.
669698

670699
### asm_ehtable_label_macro
671700

672701
Determines the macro used to declare ehtable labels in asm files.
673702

674703
Defaults to `ehlabel`
675704

705+
### asm_nonmatching_label_macro
706+
707+
Determines the macro used to declare the given symbol is a non matching one.
708+
709+
Explicitly specifying that a symbol haven't been matched yet in the generated assembly is useful for other tools that consume the build artifacts of the project. This information can be used by those tools for stuff like progress reporting.
710+
711+
Defaults to `nonmatching`
712+
676713
### asm_emit_size_directive
677714

678715
Toggles the .size directive emitted by the disassembler

docs/General-Workflow.md

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ The macros to include text/rodata assembly are different for GCC vs IDO compiler
118118
**GCC**: `INCLUDE_ASM` & `INCLUDE_RODATA` (text/rodata respectively)
119119
**IDO**: `GLOBAL_ASM`
120120

121-
These macros must be defined in an included header, which splat currently does not produce.
121+
These macros must be defined in an included header, which splat generates and updates by default for GCC-based projects.
122122

123123
For a GCC example, see the [include.h](https://github.com/AngheloAlf/drmario64/blob/master/include/include_asm.h) from the Dr. Mario project.
124124

@@ -130,27 +130,75 @@ For MWCC, you will need [mwccgap](https://github.com/mkst/mwccgap) to include as
130130

131131
splat relies on some assembly macros for the asm generation. They usually live on the `include/macro.inc` file. Without these macros then an assembler would not be able to build our disassemblies.
132132

133+
By default splat will generate files with the required assembly macros.
134+
133135
Those macros usually look like this:
134136

135137
```mips
136-
.macro glabel label
137-
.global \label
138+
# A function symbol.
139+
.macro glabel label, visibility=global
140+
.\visibility \label
138141
.type \label, @function
139142
\label:
143+
.ent \label
140144
.endm
141145
142-
.macro dlabel label
143-
.global \label
146+
# The end of a function symbol.
147+
.macro endlabel label
148+
.size \label, . - \label
149+
.end \label
150+
.endm
151+
152+
# An alternative entry to a function.
153+
.macro alabel label, visibility=global
154+
.\visibility \label
155+
.type \label, @function
144156
\label:
157+
.aent \label
145158
.endm
146159
160+
# A label referenced by an error handler table.
161+
.macro ehlabel label, visibility=global
162+
.\visibility \label
163+
\label:
164+
.endm
165+
166+
167+
# A label referenced by a jumptable.
147168
.macro jlabel label
148169
.global \label
149170
\label:
150171
.endm
172+
173+
174+
# A data symbol.
175+
.macro dlabel label, visibility=global
176+
.\visibility \label
177+
.type \label, @object
178+
\label:
179+
.endm
180+
181+
# End of a data symbol.
182+
.macro enddlabel label
183+
.size \label, . - \label
184+
.endm
185+
186+
187+
# Label to signal the symbol haven't been matched yet.
188+
.macro nonmatching label, size=1
189+
.global \label\().NON_MATCHING
190+
.type \label\().NON_MATCHING, @object
191+
.size \label\().NON_MATCHING, \size
192+
\label\().NON_MATCHING:
193+
.endm
151194
```
152195

153-
Where `glabel` is used for functions, `dlabel` is used for data, rodata and bss variables and `jlabel` is used for branch labels used by jumptables.
196+
The most commonly used labels are:
197+
198+
- `glabel` and `endlabel` which are used to define function symbols.
199+
- `dlabel` and `enddlabel` which are used to defined data, rodata and bss symbols.
200+
- `jlabel` is used for defining branch labels used by jumptables.
201+
- `nonmatching` is used to define the symbol haven't been matched yet.
154202

155203
Asm differ tools can sometimes struggle to show diffs with `jlabel`s when combined with certain compilers. A workaround for this issue is to mark the `jlabel` as a function, like this:
156204

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ tqdm
44
intervaltree
55
colorama
66
# This value should be keep in sync with the version listed on disassembler/spimdisasm_disassembler.py and pyproject.toml
7-
spimdisasm>=1.33.0
7+
spimdisasm>=1.36.0
88
rabbitizer>=1.10.0
99
pygfxd
1010
n64img>=0.1.4

src/splat/disassembler/spimdisasm_disassembler.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
class SpimdisasmDisassembler(disassembler.Disassembler):
99
# This value should be kept in sync with the version listed on requirements.txt and pyproject.toml
10-
SPIMDISASM_MIN = (1, 33, 0)
10+
SPIMDISASM_MIN = (1, 36, 0)
1111

1212
def configure(self):
1313
# Configure spimdisasm
@@ -93,9 +93,15 @@ def configure(self):
9393
)
9494
spimdisasm.common.GlobalConfig.ASM_DATA_LABEL = options.opts.asm_data_macro
9595
spimdisasm.common.GlobalConfig.ASM_TEXT_END_LABEL = options.opts.asm_end_label
96+
spimdisasm.common.GlobalConfig.ASM_DATA_END_LABEL = (
97+
options.opts.asm_data_end_label
98+
)
9699
spimdisasm.common.GlobalConfig.ASM_EHTBL_LABEL = (
97100
options.opts.asm_ehtable_label_macro
98101
)
102+
spimdisasm.common.GlobalConfig.ASM_NM_LABEL = (
103+
options.opts.asm_nonmatching_label_macro
104+
)
99105

100106
if options.opts.asm_emit_size_directive is not None:
101107
spimdisasm.common.GlobalConfig.ASM_EMIT_SIZE_DIRECTIVE = (

src/splat/scripts/create_config.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from ..util.n64 import find_code_length, rominfo
88
from ..util.psx import psxexeinfo
9-
from ..util import log
9+
from ..util import log, file_presets, conf
1010

1111

1212
def main(file_path: Path):
@@ -70,10 +70,6 @@ def create_n64_config(rom_path: Path):
7070
use_legacy_include_asm: False
7171
mips_abi_float_regs: o32
7272
73-
asm_function_macro: glabel
74-
asm_jtbl_label_macro: jlabel
75-
asm_data_macro: dlabel
76-
7773
# section_order: [".text", ".data", ".rodata", ".bss"]
7874
# auto_link_sections: [".data", ".rodata", ".bss"]
7975
@@ -176,12 +172,17 @@ def create_n64_config(rom_path: Path):
176172
- [0x{rom.size:X}]
177173
"""
178174

179-
out_file = f"{cleaned_basename}.yaml"
180-
with open(out_file, "w", newline="\n") as f:
175+
out_file = Path(f"{cleaned_basename}.yaml")
176+
with out_file.open("w", newline="\n") as f:
181177
print(f"Writing config to {out_file}")
182178
f.write(header)
183179
f.write(segments)
184180

181+
# `file_presets` requires an initialized `opts`.
182+
# A simple way to do that is to simply load the yaml we just generated.
183+
conf.load([out_file])
184+
file_presets.write_all_files()
185+
185186
# Write reloc_addrs.txt file
186187
reloc_addrs = []
187188
if rom.entrypoint_info.bss_start_address is not None:
@@ -289,10 +290,6 @@ def create_psx_config(exe_path: Path, exe_bytes: bytes):
289290
o_as_suffix: True
290291
use_legacy_include_asm: False
291292
292-
asm_function_macro: glabel
293-
asm_jtbl_label_macro: jlabel
294-
asm_data_macro: dlabel
295-
296293
section_order: [".rodata", ".text", ".data", ".bss"]
297294
# auto_link_sections: [".data", ".rodata", ".bss"]
298295
@@ -329,7 +326,7 @@ def create_psx_config(exe_path: Path, exe_bytes: bytes):
329326
"""
330327
text_offset = exe.text_offset
331328
if text_offset != 0x800:
332-
segments += f"""\
329+
segments += """\
333330
- [0x800, rodata, 800]
334331
"""
335332
segments += f"""\
@@ -346,12 +343,17 @@ def create_psx_config(exe_path: Path, exe_bytes: bytes):
346343
- [0x{exe.size:X}]
347344
"""
348345

349-
out_file = f"{cleaned_basename}.yaml"
350-
with open(out_file, "w", newline="\n") as f:
346+
out_file = Path(f"{cleaned_basename}.yaml")
347+
with out_file.open("w", newline="\n") as f:
351348
print(f"Writing config to {out_file}")
352349
f.write(header)
353350
f.write(segments)
354351

352+
# `file_presets` requires an initialized `opts`.
353+
# A simple way to do that is to simply load the yaml we just generated.
354+
conf.load([out_file])
355+
file_presets.write_all_files()
356+
355357

356358
def add_arguments_to_parser(parser: argparse.ArgumentParser):
357359
parser.add_argument(

src/splat/scripts/split.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
from .. import __package_name__, __version__
1010
from ..disassembler import disassembler_instance
11-
from ..util import cache_handler, progress_bar, vram_classes, statistics
11+
from ..util import cache_handler, progress_bar, vram_classes, statistics, file_presets
1212

13-
from colorama import Fore, Style
13+
from colorama import Style
1414
from intervaltree import Interval, IntervalTree
1515
import sys
1616

@@ -571,6 +571,8 @@ def main(
571571
if options.opts.is_mode_active("code"):
572572
dump_symbols()
573573

574+
file_presets.write_all_files()
575+
574576

575577
def add_arguments_to_parser(parser: argparse.ArgumentParser):
576578
parser.add_argument(

src/splat/segtypes/common/textbin.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ def write_asm_contents(self, rom_bytes, f: TextIO):
9797

9898
if self.is_text() and options.opts.asm_end_label != "":
9999
f.write(f"{options.opts.asm_end_label} {sym.name}\n")
100+
elif self.is_data() and options.opts.asm_data_end_label != "":
101+
f.write(f"{options.opts.asm_data_end_label} {sym.name}\n")
100102

101103
if sym.given_name_end is not None:
102104
if (
@@ -106,6 +108,12 @@ def write_asm_contents(self, rom_bytes, f: TextIO):
106108
f.write(f"{asm_label} {sym.given_name_end}\n")
107109
if asm_label == ".globl":
108110
f.write(f"{sym.given_name_end}:\n")
111+
if self.is_text() and options.opts.asm_end_label != "":
112+
f.write(f"{options.opts.asm_end_label} {sym.given_name_end}\n")
113+
elif self.is_data() and options.opts.asm_data_end_label != "":
114+
f.write(
115+
f"{options.opts.asm_data_end_label} {sym.given_name_end}\n"
116+
)
109117

110118
def split(self, rom_bytes):
111119
if self.rom_end is None:

src/splat/segtypes/linker_entry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ def save_symbol_header(self):
522522
)
523523

524524
def save_dependencies_file(self, output_path: Path, target_elf_path: Path):
525-
output = f"{target_elf_path.as_posix()}:"
525+
output = f"{clean_up_path(target_elf_path).as_posix()}:"
526526

527527
for entry in self.dependencies_entries:
528528
if entry.object_path is None:

src/splat/util/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from . import cache_handler as cache_handler
22
from . import color as color
33
from . import compiler as compiler
4+
from . import file_presets as file_presets
45
from . import log as log
56
from . import n64 as n64
67
from . import options as options

0 commit comments

Comments
 (0)