Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# splat Release Notes

### 0.36.0

* `incbin`s segments now provide a default symbol if the user hasn't defined a symbol at that given address.
* This name is automatically escaped to be a valid symbol name.
* The name of this symbol is based on the segment's name, with a `__` prefix and a suffix depending on the type of incbin used.
* This naming scheme may change at any time without further notice.
* `incbin`s segments now emit a nonmatching marker.

### 0.35.2

* Miscellaneous updates to generated macro labels.
Expand Down
2 changes: 2 additions & 0 deletions docs/Segments.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,8 @@ Generating assembly files enables better customization of these binaries, like a

If a known symbol (via a symbol_addrs file) matches the vram of a incbin segment then it will be emitted accordingly at the top. If the symbol contains a [`name_end`](Adding-Symbols.md#name_end) property then it will be emitted after the `.incbin` (useful for Nintendo64's RSP ucodes).

If no symbol is provided then splat will emit a symbol based on the segment's name with a `__` prefix and a suffix depending on the type of incbin used. This autogenerated naming scheme may change at any time without any previous notice.

Curretly there are 3 types of incbins, `textbin`, `databin` and `rodatabin`, which are intended for binary blobs of `.text`, `.data` and `.rodata` sections.

If a `textbin` section has a corresponding `databin` and/or `rodatabin` section with the same name then those will be included in the same generated assembly file.
Expand Down
85 changes: 55 additions & 30 deletions src/splat/segtypes/common/textbin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pathlib import Path
import re
from typing import Optional, TextIO

from ...util import log, options
Expand Down Expand Up @@ -75,45 +76,66 @@ def write_asm_contents(self, rom_bytes, f: TextIO):

f.write(f"{self.get_section_asm_line()}\n\n")

sym_name = None
sym_name_end = None
sym_size_matches = None

# Check if there's a symbol at this address
sym = None
vram = self.rom_to_ram(self.rom_start)
if vram is not None:
sym = self.get_symbol(vram, in_segment=True)

if sym is not None:
f.write(f"{asm_label} {sym.name}\n")
if asm_label == ".globl":
if self.is_text():
f.write(f".ent {sym.name}\n")
f.write(f"{sym.name}:\n")
sym.defined = True
if sym is not None:
sym.defined = True
sym_name = sym.name
sym_name_end = sym.given_name_end
if (
sym.given_size is None
or sym.given_size == self.rom_end - self.rom_start
):
sym_size_matches = self.rom_end - self.rom_start

if sym_name is None:
# Normalize stuff like slashes and such.
n = regex_sym_name_normalizer.sub("_", self.name)
if self.is_text():
suffix = "textbin"
elif self.is_data():
suffix = "databin"
elif self.is_rodata():
suffix = "rodatabin"
else:
suffix = "incbin"
sym_name = f"__{n}_{suffix}"

if options.opts.asm_nonmatching_label_macro != "":
siz = f", 0x{sym_size_matches:X}" if sym_size_matches is not None else ""
f.write(f"{options.opts.asm_nonmatching_label_macro} {sym_name}{siz}\n\n")

f.write(f"{asm_label} {sym_name}\n")
if asm_label == ".globl":
if self.is_text():
f.write(f".ent {sym_name}\n")
f.write(f"{sym_name}:\n")

f.write(f'.incbin "{binpath.as_posix()}"\n')

if sym is not None:
if options.opts.asm_emit_size_directive:
f.write(f".size {sym.name}, . - {sym.name}\n")
if options.opts.asm_emit_size_directive:
f.write(f".size {sym_name}, . - {sym_name}\n")

if self.is_text() and options.opts.asm_end_label != "":
f.write(f"{options.opts.asm_end_label} {sym.name}\n")
elif self.is_data() and options.opts.asm_data_end_label != "":
f.write(f"{options.opts.asm_data_end_label} {sym.name}\n")
if self.is_text() and options.opts.asm_end_label != "":
f.write(f"{options.opts.asm_end_label} {sym_name}\n")
elif options.opts.asm_data_end_label != "":
f.write(f"{options.opts.asm_data_end_label} {sym_name}\n")

if sym.given_name_end is not None:
if (
sym.given_size is None
or sym.given_size == self.rom_end - self.rom_start
):
f.write(f"{asm_label} {sym.given_name_end}\n")
if asm_label == ".globl":
f.write(f"{sym.given_name_end}:\n")
if self.is_text() and options.opts.asm_end_label != "":
f.write(f"{options.opts.asm_end_label} {sym.given_name_end}\n")
elif self.is_data() and options.opts.asm_data_end_label != "":
f.write(
f"{options.opts.asm_data_end_label} {sym.given_name_end}\n"
)
if sym_name_end is not None and sym_size_matches is not None:
f.write(f"{asm_label} {sym_name_end}\n")
if asm_label == ".globl":
f.write(f"{sym_name_end}:\n")

if self.is_text() and options.opts.asm_end_label != "":
f.write(f"{options.opts.asm_end_label} {sym_name_end}\n")
elif options.opts.asm_data_end_label != "":
f.write(f"{options.opts.asm_data_end_label} {sym_name_end}\n")

def split(self, rom_bytes):
if self.rom_end is None:
Expand Down Expand Up @@ -159,3 +181,6 @@ def should_split(self) -> bool:
return (
self.extract and self.should_scan()
) # only split if the segment was scanned first


regex_sym_name_normalizer = re.compile(r"[^0-9a-zA-Z_]")
Loading