From 2418376c0ac6322f84f3d7f02cdf912b3a2d5c09 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Sun, 12 Feb 2017 23:01:56 +1030 Subject: [PATCH] Port disassembler to new API --- disasm.py | 89 ++++++++++++++++++++++++ libsynacor/bytecode.py | 12 ++++ tools/disasm.py | 154 ----------------------------------------- 3 files changed, 101 insertions(+), 154 deletions(-) create mode 100755 disasm.py delete mode 100755 tools/disasm.py diff --git a/disasm.py b/disasm.py new file mode 100755 index 0000000..13edaa1 --- /dev/null +++ b/disasm.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# synacor.py - An implementation of the Synacor Challenge +# Copyright © 2017 RunasSudo +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from libsynacor import * + +import sys + +with open(sys.argv[1], 'rb') as data: + SYN_MEM = memory_from_file(data) + +def escape_char(char): + return char.replace('\\', '\\\\').replace('\n', '\\n').replace('"', '\\"') + +MODE_OUT = False +MODE_DAT = False #False, 1 (data), 2 (text) + +SYN_PTR = 0 + +while SYN_PTR < len(SYN_MEM): + word = SYN_MEM[SYN_PTR] + + if MODE_OUT and word != 19: + print('"') + MODE_OUT = False + if MODE_DAT and 0 <= word <= 21: + if MODE_DAT == 1: + print() + if MODE_DAT == 2: + print('"') + MODE_DAT = False + + if word not in instructions_by_opcode: + # Data + if 32 <= word <= 126: + # looks like letters - unfortunately "\n" looks like MULT + if MODE_DAT == 2: + print(escape_char(chr(word)), end='') + else: + if MODE_DAT == 1: + print() + print('{:04x}: data "{}'.format(SYN_PTR, escape_char(chr(word))), end='') + MODE_DAT = 2 + if word == 0x0a: + print('"') + MODE_DAT = False # break on newlines + else: + if MODE_DAT == 1: + print(' {:04x}'.format(word), end='') + else: + if MODE_DAT == 2: + print('"') + print('{:04x}: data {:04x}'.format(SYN_PTR, word), end='') + MODE_DAT = 1 + SYN_PTR += 1 + else: + # Instruction + instruction, SYN_PTR = Instruction.next_instruction(SYN_MEM, SYN_PTR) + # Special cases + if isinstance(instruction, InstructionOut): + if isinstance(instruction.args[0], OpLiteral): + if MODE_OUT: + print(escape_char(chr(instruction.args[0].get(None))), end='') + else: + print('{:04x}: out "{}'.format(SYN_PTR, escape_char(chr(instruction.args[0].get(None)))), end='') + MODE_OUT = True + if instruction.args[0].get(None) == 0x0a: + print('"') + MODE_OUT = False # break on newlines + else: + if MODE_OUT: + print('"') + MODE_OUT = False + print('{:04x}: {}'.format(SYN_PTR, instruction.describe())) + else: + print('{:04x}: {}'.format(SYN_PTR, instruction.describe())) diff --git a/libsynacor/bytecode.py b/libsynacor/bytecode.py index 544d01e..d62497c 100644 --- a/libsynacor/bytecode.py +++ b/libsynacor/bytecode.py @@ -30,6 +30,9 @@ class OpLiteral(Operand): return self.value def set(self, cpu, value): raise Exception('Attempted to set literal value {} to {} at {}'.format(self.value, value, cpu.SYN_PTR)) + + def describe(self): + return '{:04x}'.format(self.value) class OpRegister(Operand): def __init__(self, register): @@ -38,6 +41,9 @@ class OpRegister(Operand): return cpu.SYN_REG[self.register] def set(self, cpu, value): cpu.SYN_REG[self.register] = value + + def describe(self): + return 'R{}'.format(self.register) instructions_by_opcode = {} instructions_by_name = {} @@ -58,6 +64,12 @@ class Instruction: def __init__(self): self.args = [] + def describe(self): + description = '{: <4}'.format(self.name) + for i in range(self.nargs): + description += ' {}'.format(self.args[i].describe()) + return description + @staticmethod def next_instruction(data, idx): opcode = Operand.read_op(data[idx]) diff --git a/tools/disasm.py b/tools/disasm.py deleted file mode 100755 index b02a89a..0000000 --- a/tools/disasm.py +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env python3 -# synacor.py - An implementation of the Synacor Challenge -# Copyright © 2017 RunasSudo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -import struct # for bytes<-->word handling -import sys # for args - -class OpLiteral: - def __init__(self, value): - self.value = value; - def get(self): - return '{:04x}'.format(self.value); - def set(self): - return '{:04x}'.format(self.value); - -class OpRegister: - def __init__(self, register): - self.register = register; - def get(self): - return 'R{}'.format(self.register); - def set(self): - return 'R{}'.format(self.register); - -def readWord(): - byteData = data.read(2) - if len(byteData) < 2: - return None - return struct.unpack('