1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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()
|