aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/psxapi/generate_stubs.py
blob: 9db60de7c6e996b5d1f64803efbccb962a776fff (plain) (blame)
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()