Port disassembler to new API
This commit is contained in:
parent
86bcc094fa
commit
2418376c0a
89
disasm.py
Executable file
89
disasm.py
Executable file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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()))
|
@ -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])
|
||||
|
154
tools/disasm.py
154
tools/disasm.py
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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('<H', byteData)[0]
|
||||
|
||||
def readOp():
|
||||
word = readWord()
|
||||
if 0 <= word <= 32767:
|
||||
return OpLiteral(word)
|
||||
if 32768 <= word <= 32775:
|
||||
return OpRegister(word - 32768)
|
||||
raise Exception('Invalid word {} at {}'.format(word, SYN_PTR))
|
||||
|
||||
def escapeChar(char):
|
||||
return char.replace('\\', '\\\\').replace('\n', '\\n').replace('"', '\\"')
|
||||
|
||||
MODE_OUT = False
|
||||
MODE_DAT = False #False, 1 (data), 2 (text)
|
||||
|
||||
with open(sys.argv[1], 'rb') as data:
|
||||
while True:
|
||||
pos = int(data.tell() / 2)
|
||||
|
||||
word = readWord()
|
||||
if word == None:
|
||||
break
|
||||
|
||||
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 == 21: #NOP
|
||||
print('{:04x} nop'.format(pos))
|
||||
elif word == 0: #HALT
|
||||
print('{:04x} halt'.format(pos))
|
||||
elif word == 1: #SET
|
||||
print('{:04x} set {} {}'.format(pos, readOp().set(), readOp().get()))
|
||||
elif word == 2: #PUSH
|
||||
print('{:04x} push {}'.format(pos, readOp().get()))
|
||||
elif word == 3: #POP
|
||||
print('{:04x} pop {}'.format(pos, readOp().set()))
|
||||
elif word == 4: #EQ
|
||||
print('{:04x} eq {} {} {}'.format(pos, readOp().set(), readOp().get(), readOp().get()))
|
||||
elif word == 5: #GT
|
||||
print('{:04x} gt {} {} {}'.format(pos, readOp().set(), readOp().get(), readOp().get()))
|
||||
elif word == 6: #JMP
|
||||
print('{:04x} jmp {}'.format(pos, readOp().get()))
|
||||
elif word == 7: #JT (jump if not zero)
|
||||
print('{:04x} jt {} {}'.format(pos, readOp().get(), readOp().get()))
|
||||
elif word == 8: #JF (jump if zero)
|
||||
print('{:04x} jf {} {}'.format(pos, readOp().get(), readOp().get()))
|
||||
elif word == 9: #ADD
|
||||
print('{:04x} add {} {} {}'.format(pos, readOp().set(), readOp().get(), readOp().get()))
|
||||
elif word == 10: #MULT
|
||||
print('{:04x} mult {} {} {}'.format(pos, readOp().set(), readOp().get(), readOp().get()))
|
||||
elif word == 11: #MOD
|
||||
print('{:04x} mod {} {} {}'.format(pos, readOp().set(), readOp().get(), readOp().get()))
|
||||
elif word == 12: #AND
|
||||
print('{:04x} and {} {} {}'.format(pos, readOp().set(), readOp().get(), readOp().get()))
|
||||
elif word == 13: #OR
|
||||
print('{:04x} or {} {} {}'.format(pos, readOp().set(), readOp().get(), readOp().get()))
|
||||
elif word == 14: #NOT
|
||||
print('{:04x} not {} {}'.format(pos, readOp().set(), readOp().get()))
|
||||
elif word == 15: #RMEM
|
||||
print('{:04x} rmem {} {}'.format(pos, readOp().set(), readOp().get()))
|
||||
elif word == 16: #WMEM
|
||||
print('{:04x} wmem {} {}'.format(pos, readOp().set(), readOp().get()))
|
||||
elif word == 17: #CALL
|
||||
print('{:04x} call {}'.format(pos, readOp().get()))
|
||||
elif word == 18: #RET
|
||||
print('{:04x} ret'.format(pos))
|
||||
elif word == 19: #OUT
|
||||
op = readOp()
|
||||
|
||||
if isinstance(op, OpLiteral):
|
||||
if MODE_OUT:
|
||||
print(escapeChar(chr(op.value)), end='')
|
||||
else:
|
||||
print('{:04x} out "{}'.format(pos, escapeChar(chr(op.value))), end='')
|
||||
MODE_OUT = True
|
||||
if op.value == 0x0a:
|
||||
print('"')
|
||||
MODE_OUT = False # break on newlines
|
||||
else:
|
||||
if MODE_OUT:
|
||||
print('"')
|
||||
MODE_OUT = False
|
||||
print('{:04x} out {}'.format(pos, op.get(), end=''))
|
||||
elif word == 20: #IN
|
||||
print('{:04x} in {}'.format(pos, readOp().set()))
|
||||
else:
|
||||
if 32 <= word <= 126:
|
||||
# looks like letters - unfortunately "\n" looks like MULT
|
||||
if MODE_DAT == 2:
|
||||
print(escapeChar(chr(word)), end='')
|
||||
else:
|
||||
if MODE_DAT == 1:
|
||||
print()
|
||||
print('{:04x} data "{}'.format(pos, escapeChar(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(pos, word), end='')
|
||||
MODE_DAT = 1
|
Reference in New Issue
Block a user