FoenixToolbox/client-64tass/genbindings.py

286 lines
6.8 KiB
Python
Raw Normal View History

2024-09-14 12:08:21 -04:00
#
# Create the 64tass parameters structures for the Toolbox functions
#
import re
import sys
class FunctionParameter:
def __init__(self):
self._name = "UNKNOWN"
self._position = 0
self._type = ""
self._description = ""
def set_name(self, name):
if name.endswith("[]"):
name = name[0:-2]
self._name = name
def name(self):
return self._name
def set_position(self, position):
self._position = position
def position(self):
return self._position
def set_type(self, type):
self._type = type
def type(self):
return self._type
def set_description(self, description):
self._description = description
def description(self):
return self._description
def print(self):
"""Print a simple version of a parameter"""
print("\tName: {0}, Type: {1}, Comment: {2}".format(self._name, self._type, self._description))
def size(self):
"""Return the size of the parameter in bytes."""
if self.type() == "char" or self.type() == "unsigned char" or self.type() == "uint8_t":
return 1
elif self.type() == "short" or self.type() == "unsigned short" or self.type() == "uint16_t":
return 2
else:
return 4
def emit_asm(self, output):
"""Emit the assembly reference for the parameter."""
size = self.size()
if size == 1:
output.write("{0:<16}.byte ? ; {1}\n".format(self.name(), self.description()))
elif size == 2:
output.write("{0:<16}.word ? ; {1}\n".format(self.name(), self.description()))
else:
output.write("{0:<16}.dword ? ; {1}\n".format(self.name(), self.description()))
def emit_accumulator(self, output):
size = self.size()
if size == 1:
output.write("; {0} goes in A[7..0]\n".format(self.name()))
elif size == 2:
output.write("; {0} goes in A[15..0]\n".format(self.name()))
else:
output.write("; {0} goes in X[15..0]:A[15..0]\n".format(self.name()))
class Function:
def __init__(self):
self._name = "UNKNOWN"
self._brief = ""
self._description = ""
self._type = "void"
self._parameters = []
self._address = 0
self._prototype = ""
def set_name(self, name):
self._name = name
def name(self):
return self._name
def set_prototype(self, prototype):
self._prototype = prototype
def prototype(self):
return self._prototype
def set_brief(self, brief):
self._brief = brief
def brief(self):
return self._brief
def set_description(self, description):
self._description = description
def description(self):
return self._description
def set_type(self, type):
self._type = type
def type(self):
return self._type
def set_address(self, address):
self._address = address
def address(self):
return self._address
def add_parameter(self, param_name, param_type):
"""Add a parameter to the function."""
param = FunctionParameter()
param.set_name(param_name)
param.set_type(param_type)
param.set_position(len(self._parameters))
self._parameters.append(param)
def parameters(self):
return self._parameters
def stack_size(self):
"""Return the number of bytes needed on the stack for the parameters"""
size = 0
if len(self.parameters()) > 1:
for parameter in self.parameters()[1:]:
size = size + parameter.size()
return size
def add_param_comment(self, param_name, param_comment):
"""Add a comment to a parameter"""
for param in self._parameters:
if param._name == param_name:
param.set_description(param_comment)
break
def print(self):
"""Print out a simple description of the function"""
print("Name: {0}, Type: {1}".format(self._name, self._type))
print(self._description)
for parameter in self._parameters:
parameter.print()
print()
def emit_asm(self, output):
"""Emit the assembly reference for the function."""
output.write("\n;\n; {0}\n".format(self.prototype()))
if len(self.parameters()) > 0:
first = self.parameters()[0]
output.write("; \n")
first.emit_accumulator(output)
output.write(";\n")
output.write("; {0} bytes needed for the stack parameters\n;\n".format(self.stack_size()))
output.write("{0} = ${1:06x}\n\n".format(self.name(), self.address()))
if len(self.parameters()) > 1:
m = re.match("sys_(\w+)", self.name())
if m:
short_name = m.group(1)
else:
short_name = self.name()
stack_parameters = self.parameters()[1:]
2024-10-13 16:14:05 -04:00
output.write("{0:<16} .namespace\n".format(short_name))
2024-09-14 12:08:21 -04:00
output.write(" .virtual 1,s\n")
for parameter in stack_parameters:
parameter.emit_asm(output)
output.write(" .endv\n")
2024-09-19 20:54:59 -04:00
output.write(" .endn\n")
2024-09-14 12:08:21 -04:00
comments = []
functions = []
def process_comment(line):
"""Deal with a comment line while we're in the middle of a block comment."""
index = line.index("*")
if index > -1:
comment_line = line[index+1:].strip()
else:
comment_line = line.strip()
if comment_line != "":
comments.append(comment_line)
def process_definition(type, name, parameters, comments, prototype):
"""Deal with a function prototype."""
func = Function()
func.set_name(name)
func.set_type(type)
func.set_prototype(prototype)
is_in_func_comments = True
for param in func_parameters:
m1 = re.match("^\s*(.*)\s(\S+)\s*$", param)
if m1:
param_type = m1.group(1).strip()
param_name = m1.group(2).strip()
func.add_parameter(param_name, param_type)
for comment in comments:
m2 = re.match("@param\s(\w+)\s+(.*)$", comment)
if m2:
param_name = m2.group(1).strip()
param_comment = m2.group(2).strip()
func.add_param_comment(param_name, param_comment)
else:
func._description = func._description + comment
functions.append(func)
#
# Read in the C header file with the extern prototypes and parse all the function names,
# return values, and parameters
#
with open(sys.argv[1], "r") as input:
is_in_comment = False
for line in input.readlines():
line = line.strip()
if line.startswith("/**"):
is_in_comments = True
comments = []
elif line.endswith("*/"):
is_in_comments = False
if is_in_comments and line.startswith("*"):
process_comment(line)
else:
m = re.match("extern\s+SYSTEMCALL\s+(\w+)\s+(\w+)\((.*)\)", line)
if m:
func_type = m.group(1)
func_name = m.group(2)
func_parameters = str.split(m.group(3), ",")
process_definition(func_type, func_name, func_parameters, comments, line)
#
# Read in the Caylpsi Toolbox jumptable assembly file and extract the addresses
#
with open(sys.argv[2], "r") as addresses:
for line in addresses.readlines():
m = re.match("^(\w+):\s+.equlab\s+0x([0-9a-fA-F]+)", line)
if m:
func_name = m.group(1).strip()
func_address = int(m.group(2), 16)
2024-10-13 16:14:05 -04:00
print("Name: {0}, Address: {1}".format(func_name, func_address))
2024-09-14 12:08:21 -04:00
for func in functions:
if func.name() == func_name:
func.set_address(func_address)
break
#
# Create the bindings file with the relevant information
#
with open("bindings.s", "w") as bindings:
for func in functions:
func.emit_asm(bindings)