diff options
Diffstat (limited to 'libpsn00b/psxapi/generate_stubs.py')
| -rw-r--r-- | libpsn00b/psxapi/generate_stubs.py | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/libpsn00b/psxapi/generate_stubs.py b/libpsn00b/psxapi/generate_stubs.py new file mode 100644 index 0000000..9db60de --- /dev/null +++ b/libpsn00b/psxapi/generate_stubs.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +# PSn00bSDK BIOS stub generator +# (C) 2022 spicyjpeg - MPL licensed + +import os, sys, json +from argparse import ArgumentParser, FileType + +## Listing section generation + +SECTION_TEMPLATE = """## {title} ({count}) + +{body}""" +FUNCTION_TEMPLATE = """.section .text.{name} +.global {name} +.type {name}, @function +{name}: + li $t2, 0x{address:02x} + jr $t2 + li $t1, 0x{id:02x} + +""" +SYSCALL_TEMPLATE = """.section .text.{name} +.global {name} +.type {name}, @function +{name}: + li $a0, 0x{id:02x} + syscall 0 + jr $ra + nop + +""" + +STUB_TYPES = { + "a": ( 0xa0, FUNCTION_TEMPLATE, "A0 table functions" ), + "b": ( 0xb0, FUNCTION_TEMPLATE, "B0 table functions" ), + "c": ( 0xc0, FUNCTION_TEMPLATE, "C0 table functions" ), + "syscall": ( 0x00, SYSCALL_TEMPLATE, "Syscalls" ) +} + +def generate_section(_type, entries): + address, template, title = STUB_TYPES[_type] + body = "" + + for entry in entries: + if entry.get("comment", None): + body += f"# {entry['comment'].strip()}\n" + + body += template.format( + name = entry["name"].strip(), + address = address, + id = entry["id"] + ) + + return SECTION_TEMPLATE.format( + title = title, + count = len(entries), + body = body + ) + +## Main + +HEADER_TEMPLATE = """# PSn00bSDK BIOS API stubs +# (C) 2022 spicyjpeg - MPL licensed + +# This file has been generated automatically. Each function is placed in its +# own section to allow the linker to strip unused functions. + +.set noreorder + +""" + +def get_args(): + parser = ArgumentParser( + description = "Generates MIPS assembly listings of BIOS API stubs." + ) + parser.add_argument( + "json_list", + type = FileType("rt"), + help = "path to JSON list of stubs to generate" + ) + parser.add_argument( + "-o", "--output", + type = str, + default = os.curdir, + help = "where to save generated files (current directory by default)", + metavar = "directory" + ) + + return parser.parse_args() + +def main(): + args = get_args() + + with args.json_list as _file: + all_entries = json.load(_file) + + # { file: { type: [ entry, ... ], ... }, ... } + files = {} + + # Sort all functions by the file they are going to be put in, and by the + # type within the file. + for entry in all_entries: + _type = entry["type"].strip().lower() + _file = entry["file"].strip() + + if type(entry["id"]) is str: + entry["id"] = int(entry["id"], 0) + + if _file not in files: + files[_file] = { _type: [] for _type in STUB_TYPES.keys() } + + files[_file][_type].append(entry) + + for path, types in files.items(): + full_path = os.path.normpath(os.path.join(args.output, path)) + listing = HEADER_TEMPLATE + + for _type, entries in types.items(): + if not entries: + continue + + entries.sort(key = lambda entry: entry["id"]) + listing += generate_section(_type, entries) + + with open(full_path, "wt", newline = "\n") as _file: + _file.write(listing) + +if __name__ == "__main__": + main() |
