mirror of
https://github.com/elliotnunn/tbxi-patches.git
synced 2024-06-09 01:29:30 +00:00
Allow multiple labels per line
Python keywords can be used as labels (sorry eval), and multiple labels are allowed per line
This commit is contained in:
parent
7bcf2895de
commit
6bdba0df45
87
ppcasm.py
87
ppcasm.py
|
@ -32,47 +32,56 @@ import struct
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
|
|
||||||
def assemble(asm):
|
def assemble(asm, return_labels=False):
|
||||||
"""Assemble a string into a bytes object
|
"""Assemble a string into a bytes object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
labels = resolve_labels(asm)
|
asm = asm.rstrip('\n').split('\n')
|
||||||
|
|
||||||
clean_asm = _strip(asm)
|
# First pass: discern labels from instructions
|
||||||
|
line_list = []
|
||||||
|
offset = 0
|
||||||
|
for lineno, orig_line in enumerate(asm, 1):
|
||||||
|
line = orig_line # mutate line a bit
|
||||||
|
line = line.lower() # normalize case
|
||||||
|
line = line.partition('#')[0] # strip comments
|
||||||
|
|
||||||
|
line_labels, line = re.match(r'^((?:\s*\w+:)*)(.*)', line).groups()
|
||||||
|
|
||||||
|
line_labels = re.findall(r'\w+', line_labels)
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
line_list.append((lineno, offset, orig_line, line_labels, line))
|
||||||
|
|
||||||
|
if line: offset += 4
|
||||||
|
|
||||||
|
# Second pass: resolve labels (each instruction is 4 bytes, easy)
|
||||||
|
all_labels = {}
|
||||||
|
for lineno, offset, orig_line, line_labels, line in line_list:
|
||||||
|
for label in line_labels:
|
||||||
|
if label in all_labels:
|
||||||
|
raise SyntaxError('redefined label %s\n %d: %s' % (label, lineno, orig_line))
|
||||||
|
all_labels[label] = offset
|
||||||
|
|
||||||
|
# Third pass: assemble
|
||||||
binary = bytearray()
|
binary = bytearray()
|
||||||
offset = 0
|
offset = 0
|
||||||
for lineno, line in enumerate(clean_asm):
|
for lineno, offset, orig_line, line_labels, line in line_list:
|
||||||
if line == '':
|
if line:
|
||||||
pass # skip blanks
|
cur_labels = {lab: lab_offset - offset for (lab, lab_offset) in all_labels.items()}
|
||||||
|
|
||||||
elif line.endswith(':'): # check labels
|
|
||||||
if not re.match(r'^\w+:$', line):
|
|
||||||
raise SyntaxError('invalid label\n %d: %s' % (lineno + 1, clean_asm[lineno].lstrip()))
|
|
||||||
|
|
||||||
else: # compile instructions!
|
|
||||||
cur_labels = {lab: lab_offset - offset for (lab, lab_offset) in labels.items()}
|
|
||||||
try:
|
try:
|
||||||
binary.extend(struct.pack('>L', instruction(line, cur_labels)))
|
binary.extend(struct.pack('>L', instruction(line, cur_labels)))
|
||||||
except SyntaxError as e:
|
except SyntaxError as e:
|
||||||
e.msg += '\n %d: %s' % (lineno + 1, clean_asm[lineno].lstrip())
|
e.msg += '\n %d: %s' % (lineno, orig_line.lstrip())
|
||||||
raise e
|
raise e
|
||||||
offset += 4
|
|
||||||
|
|
||||||
return bytes(binary)
|
binary = bytes(binary)
|
||||||
|
|
||||||
|
if return_labels:
|
||||||
def resolve_labels(asm):
|
return binary, all_labels
|
||||||
asm = _strip(asm)
|
else:
|
||||||
|
return binary
|
||||||
labels = {}
|
|
||||||
offset = 0
|
|
||||||
for l in asm:
|
|
||||||
if l.endswith(':'): # label
|
|
||||||
labels[l[:-1]] = offset
|
|
||||||
elif l: # instruction
|
|
||||||
offset += 4
|
|
||||||
|
|
||||||
return labels
|
|
||||||
|
|
||||||
|
|
||||||
def instruction(line, variables):
|
def instruction(line, variables):
|
||||||
|
@ -131,7 +140,14 @@ def instruction(line, variables):
|
||||||
return int(x.lstrip('cr'))
|
return int(x.lstrip('cr'))
|
||||||
|
|
||||||
def eval_expression(x):
|
def eval_expression(x):
|
||||||
return eval(x, {"__builtins__": variables}, {})
|
def label_replacer(label):
|
||||||
|
label = label.group(0)
|
||||||
|
if label not in variables:
|
||||||
|
raise SyntaxError('undefined label: %s' % label)
|
||||||
|
return hex(variables[label])
|
||||||
|
|
||||||
|
replaced = re.sub(r'\b[^\W\d]\w*', label_replacer, x)
|
||||||
|
return eval(replaced, {"__builtins__": {}}, {})
|
||||||
|
|
||||||
# Create a validation regex and an evaluation function for each arg
|
# Create a validation regex and an evaluation function for each arg
|
||||||
validation_list = []
|
validation_list = []
|
||||||
|
@ -173,19 +189,6 @@ def _b(value, startbit, endbit=None):
|
||||||
return (value & mask) << shift
|
return (value & mask) << shift
|
||||||
|
|
||||||
|
|
||||||
def _strip(asm):
|
|
||||||
# returns a list of lines, all being instruction, label: or empty
|
|
||||||
|
|
||||||
asm = asm.rstrip('\n').split('\n')
|
|
||||||
|
|
||||||
# Strip comments and whitespace, normalize case
|
|
||||||
asm = [l.partition('#')[0] for l in asm]
|
|
||||||
asm = [' '.join(l.strip().split()) for l in asm]
|
|
||||||
asm = [l.lower() for l in asm]
|
|
||||||
|
|
||||||
return asm
|
|
||||||
|
|
||||||
|
|
||||||
def command_line():
|
def command_line():
|
||||||
parser = argparse.ArgumentParser(description='''
|
parser = argparse.ArgumentParser(description='''
|
||||||
Assemble PowerPC assembly code into a raw binary. Supports instructions, labels: and #comments.
|
Assemble PowerPC assembly code into a raw binary. Supports instructions, labels: and #comments.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user