Add built-in string support in dis/assembler
This commit is contained in:
parent
f7ccdb06fc
commit
4322fe57e7
54
disasm.py
54
disasm.py
@ -30,7 +30,7 @@ with open(args.file, 'rb') as data:
|
|||||||
SYN_MEM = memory_from_file(data)
|
SYN_MEM = memory_from_file(data)
|
||||||
disassemble_end = len(SYN_MEM)
|
disassemble_end = len(SYN_MEM)
|
||||||
|
|
||||||
labels, comments_before, comments_inline, replacements = {}, {}, {}, {}
|
labels, comments_before, comments_inline, replacements, strings = {}, {}, {}, {}, []
|
||||||
|
|
||||||
# Do smart things if requested
|
# Do smart things if requested
|
||||||
if args.smart:
|
if args.smart:
|
||||||
@ -99,11 +99,18 @@ if args.hints:
|
|||||||
code = line[line.index(' ', line.index(' ') + 1) + 1:].strip()
|
code = line[line.index(' ', line.index(' ') + 1) + 1:].strip()
|
||||||
instruction = assemble_line(None, code)[0][0]
|
instruction = assemble_line(None, code)[0][0]
|
||||||
replacements[loc] = instruction
|
replacements[loc] = instruction
|
||||||
|
elif line.startswith('del '):
|
||||||
|
loc = int(line.split()[1], 16)
|
||||||
|
replacements[loc] = None
|
||||||
|
elif line.startswith('str '):
|
||||||
|
loc = int(line.split()[1], 16)
|
||||||
|
strings.append(loc)
|
||||||
else:
|
else:
|
||||||
raise Exception('Invalid line in hint file: {}'.format(line))
|
raise Exception('Invalid line in hint file: {}'.format(line))
|
||||||
|
|
||||||
MODE_OUT = False
|
MODE_OUT = False
|
||||||
MODE_DAT = False #False, 1 (data), 2 (text)
|
MODE_DAT = False #False, 1 (data), 2 (text), 3 (unknown string), 4 (data string), 5 (text string)
|
||||||
|
str_ctr = None
|
||||||
|
|
||||||
SYN_PTR = 0
|
SYN_PTR = 0
|
||||||
|
|
||||||
@ -124,7 +131,7 @@ def set_mode_dat(mode):
|
|||||||
pass
|
pass
|
||||||
elif mode == False:
|
elif mode == False:
|
||||||
# Switching off
|
# Switching off
|
||||||
if MODE_DAT == 2:
|
if MODE_DAT == 2 or MODE_DAT == 5:
|
||||||
print('"', end='')
|
print('"', end='')
|
||||||
print()
|
print()
|
||||||
elif MODE_DAT == 1:
|
elif MODE_DAT == 1:
|
||||||
@ -135,11 +142,20 @@ def set_mode_dat(mode):
|
|||||||
# Switching from text to data
|
# Switching from text to data
|
||||||
print('"')
|
print('"')
|
||||||
print('{:04x}: data'.format(SYN_PTR), end='')
|
print('{:04x}: data'.format(SYN_PTR), end='')
|
||||||
else:
|
elif mode == 4:
|
||||||
|
# Detected data string
|
||||||
|
pass
|
||||||
|
elif mode == 5:
|
||||||
|
# Detected text string
|
||||||
|
print(' "', end='')
|
||||||
|
elif mode == 1 or mode == 2:
|
||||||
# Switching to a new mode
|
# Switching to a new mode
|
||||||
print('{:04x}: data'.format(SYN_PTR), end='')
|
print('{:04x}: data'.format(SYN_PTR), end='')
|
||||||
if mode == 2:
|
if mode == 2:
|
||||||
print('"', end='')
|
print('"', end='')
|
||||||
|
elif mode == 3:
|
||||||
|
# Switching to a new string mode
|
||||||
|
print('{:04x}: str'.format(SYN_PTR), end='')
|
||||||
MODE_DAT = mode
|
MODE_DAT = mode
|
||||||
def clear_modes():
|
def clear_modes():
|
||||||
set_mode_out(False)
|
set_mode_out(False)
|
||||||
@ -164,13 +180,41 @@ while SYN_PTR < len(SYN_MEM):
|
|||||||
# Handle replacements
|
# Handle replacements
|
||||||
if SYN_PTR in replacements:
|
if SYN_PTR in replacements:
|
||||||
instruction = replacements[SYN_PTR]
|
instruction = replacements[SYN_PTR]
|
||||||
|
if instruction is not None:
|
||||||
print('{:04x}: {}{}'.format(SYN_PTR, instruction.describe(), comment_inline))
|
print('{:04x}: {}{}'.format(SYN_PTR, instruction.describe(), comment_inline))
|
||||||
SYN_PTR += len(instruction.assemble(None))
|
SYN_PTR += len(instruction.assemble(None))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
word = SYN_MEM[SYN_PTR]
|
word = SYN_MEM[SYN_PTR]
|
||||||
|
|
||||||
if SYN_PTR >= disassemble_end or word not in instructions_by_opcode:
|
if SYN_PTR in strings:
|
||||||
|
# String length
|
||||||
|
set_mode_dat(3)
|
||||||
|
str_ctr = word
|
||||||
|
SYN_PTR += 1
|
||||||
|
elif MODE_DAT == 3:
|
||||||
|
# Detect string type
|
||||||
|
if 32 <= word <= 126:
|
||||||
|
set_mode_dat(5)
|
||||||
|
print(escape_char(chr(word)), end='')
|
||||||
|
else:
|
||||||
|
set_mode_dat(4)
|
||||||
|
print(' {:04x}'.format(word), end='')
|
||||||
|
SYN_PTR += 1
|
||||||
|
str_ctr -= 1
|
||||||
|
if str_ctr <= 0:
|
||||||
|
set_mode_dat(False)
|
||||||
|
elif MODE_DAT == 4 or MODE_DAT == 5:
|
||||||
|
# String
|
||||||
|
if MODE_DAT == 4:
|
||||||
|
print(' {:04x}'.format(word), end='')
|
||||||
|
else:
|
||||||
|
print(escape_char(chr(word)), end='')
|
||||||
|
SYN_PTR += 1
|
||||||
|
str_ctr -= 1
|
||||||
|
if str_ctr <= 0:
|
||||||
|
set_mode_dat(False)
|
||||||
|
elif SYN_PTR >= disassemble_end or word not in instructions_by_opcode:
|
||||||
# Data
|
# Data
|
||||||
if 32 <= word <= 126:
|
if 32 <= word <= 126:
|
||||||
# Looks like letters - unfortunately "\n" looks like MULT
|
# Looks like letters - unfortunately "\n" looks like MULT
|
||||||
|
@ -73,6 +73,12 @@ ren label_059d self_test_mod_fail
|
|||||||
ren label_034d self_test_rwmem
|
ren label_034d self_test_rwmem
|
||||||
lbl 034b self_test_rwmem_data
|
lbl 034b self_test_rwmem_data
|
||||||
cmb 034b Test rmem/wmem
|
cmb 034b Test rmem/wmem
|
||||||
|
rep 034d rmem R0 $self_test_rwmem_data
|
||||||
|
rep 0357 add R2 $self_test_rwmem_data 0001
|
||||||
|
rep 0365 set R0 $self_test_rwmem_data
|
||||||
|
cmb 0377 Check the data has decrypted successfully
|
||||||
|
rep 0377 rmem R0 $encrypted_data
|
||||||
|
rep 0381 add R2 $encrypted_data 0001
|
||||||
ren label_04d7 self_test_rmem_fail
|
ren label_04d7 self_test_rmem_fail
|
||||||
ren label_04ee self_test_wmem_fail
|
ren label_04ee self_test_wmem_fail
|
||||||
ren sub_06bb decrypt_data
|
ren sub_06bb decrypt_data
|
||||||
@ -80,14 +86,20 @@ cmb 0375 Sneaky!
|
|||||||
cmb 0375 call $decrypt_data
|
cmb 0375 call $decrypt_data
|
||||||
rep 0375 nop
|
rep 0375 nop
|
||||||
rep 0376 nop
|
rep 0376 nop
|
||||||
|
cmb 038f Replace the "t" in "test string" with "T"
|
||||||
lbl 17b4 encrypted_data
|
lbl 17b4 encrypted_data
|
||||||
ren label_03ad self_test_wmem_cmd_fail
|
ren label_03ad self_test_wmem_cmd_fail
|
||||||
|
cmb 03a3 Try replacing the next instructions
|
||||||
|
rep 03a3 wmem $self_test_dynamic 0015
|
||||||
|
rep 03a6 wmem $($self_test_dynamic+1) 0007
|
||||||
rep 03ab out $self_test_complete
|
rep 03ab out $self_test_complete
|
||||||
cmi 03a9 Becomes noop; jt 0013 $self_test_complete
|
cmi 03a9 Becomes noop; jt 0013 $self_test_complete
|
||||||
|
lbl 03a9 self_test_dynamic
|
||||||
lbl 03d2 self_test_complete
|
lbl 03d2 self_test_complete
|
||||||
lbl 17c0 str_self_test_result
|
lbl 17c0 str_self_test_result
|
||||||
cmi 03d6 The "F"
|
cmi 03d6 The "F"
|
||||||
lbl 17e4 str_complete
|
lbl 17e4 str_complete
|
||||||
|
str 17e4
|
||||||
ren label_03e8 self_test_loop_copy_complete
|
ren label_03e8 self_test_loop_copy_complete
|
||||||
cmb 03e8 Copy the "complete" string over the "FAILED!!" substring of $str_self_test_result
|
cmb 03e8 Copy the "complete" string over the "FAILED!!" substring of $str_self_test_result
|
||||||
ren label_03ff self_test_concat_all_pass
|
ren label_03ff self_test_concat_all_pass
|
||||||
|
@ -103,7 +103,7 @@ def assemble_instruction(source, tokens):
|
|||||||
instruction.args.append(OpLiteral(ord(char)))
|
instruction.args.append(OpLiteral(ord(char)))
|
||||||
instructions.append(instruction)
|
instructions.append(instruction)
|
||||||
return instructions, []
|
return instructions, []
|
||||||
elif isinstance(instruction, InstructionData):
|
elif isinstance(instruction, InstructionData) or isinstance(instruction, InstructionString):
|
||||||
if tokens[1].startswith('"'):
|
if tokens[1].startswith('"'):
|
||||||
chars = unescape_char(tokens[1][1:-1])
|
chars = unescape_char(tokens[1][1:-1])
|
||||||
instruction.args = [ord(char) for char in chars]
|
instruction.args = [ord(char) for char in chars]
|
||||||
@ -120,6 +120,10 @@ def assemble_instruction(source, tokens):
|
|||||||
# Register
|
# Register
|
||||||
arg = OpRegister(int(argstr[1:]))
|
arg = OpRegister(int(argstr[1:]))
|
||||||
elif argstr.startswith('$'):
|
elif argstr.startswith('$'):
|
||||||
|
if argstr.startswith('$('):
|
||||||
|
# Expression
|
||||||
|
arg = OpExpression(argstr[2:-1])
|
||||||
|
else:
|
||||||
# Label
|
# Label
|
||||||
arg = OpLabel(argstr[1:])
|
arg = OpLabel(argstr[1:])
|
||||||
else:
|
else:
|
||||||
|
@ -64,6 +64,28 @@ class OpLabel(Operand):
|
|||||||
raise Exception('Unknown label {}'.format(self.label))
|
raise Exception('Unknown label {}'.format(self.label))
|
||||||
return OpLiteral(labels[self.label]).assemble(labels)
|
return OpLiteral(labels[self.label]).assemble(labels)
|
||||||
|
|
||||||
|
# Used only in assembling process
|
||||||
|
class OpExpression(Operand):
|
||||||
|
def __init__(self, expr):
|
||||||
|
self.expr = expr
|
||||||
|
|
||||||
|
def describe(self):
|
||||||
|
return '$({})'.format(self.expr)
|
||||||
|
def assemble(self, labels):
|
||||||
|
if labels is None:
|
||||||
|
# First pass
|
||||||
|
return 0xffff
|
||||||
|
|
||||||
|
# Replace labels
|
||||||
|
expr = self.expr
|
||||||
|
for label in labels:
|
||||||
|
expr = expr.replace('$' + label, str(labels[label]))
|
||||||
|
|
||||||
|
# Warning: Not safe for untrusted code!
|
||||||
|
value = eval(expr)
|
||||||
|
|
||||||
|
return OpLiteral(value).assemble(labels)
|
||||||
|
|
||||||
instructions_by_opcode = {}
|
instructions_by_opcode = {}
|
||||||
instructions_by_name = {}
|
instructions_by_name = {}
|
||||||
|
|
||||||
@ -246,3 +268,8 @@ class InstructionData(Instruction):
|
|||||||
def assemble(self, labels):
|
def assemble(self, labels):
|
||||||
return self.args
|
return self.args
|
||||||
instructions_by_name['data'] = InstructionData
|
instructions_by_name['data'] = InstructionData
|
||||||
|
|
||||||
|
class InstructionString(Instruction):
|
||||||
|
def assemble(self, labels):
|
||||||
|
return [len(self.args)] + self.args
|
||||||
|
instructions_by_name['str'] = InstructionString
|
||||||
|
Reference in New Issue
Block a user