Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
* Allows configuring the style used by the generated `INCLUDE_ASM` macro. It currently allows two possible values:
* `default`: Uses the default definition for the macro. This is the default.
* `maspsx_hack`: Changes the definition of the generated `INCLUDE_ASM` to be compatible with the one expected by `maspsx` when using the [reordering workaround hack](https://github.com/mkst/maspsx?tab=readme-ov-file#include_asm-reordering-workaround-hack). This value is only relevant for psx projects.
* `incbin` 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.
* `incbin` segments now emit a nonmatching marker.
* `spimdisasm` 1.36.1 or above is now required.

### 0.35.2
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.

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