1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
|
from gbz80disasm import opt_table, end_08_scripts_with, relative_jumps, relative_unconditional_jumps, call_commands
class RomStr(str):
""" Simple wrapper to prevent a giant rom from being shown on screen.
"""
def __repr__(self):
""" Simplifies this object so that the output doesn't overflow stdout.
"""
return "RomStr(too long)"
@classmethod
def load(cls, crystal=True, red=False):
""" Loads a ROM into a RomStr.
"""
if crystal and not red:
file_handler = open("../baserom.gbc", "r")
elif red and not crystal:
file_handler = open("../pokered-baserom.gbc", "r")
else:
raise Exception, "not sure which rom to load?"
bytes = file_handler.read()
file_handler.close()
return RomStr(bytes)
def length(self):
""" len(self)
"""
return len(self)
def len(self):
""" len(self)
"""
return self.length()
def interval(self, offset, length, strings=True, debug=True):
""" returns hex values for the rom starting at offset until
offset+length
"""
returnable = []
for byte in self[offset:offset+length]:
if strings:
returnable.append(hex(ord(byte)))
else:
returnable.append(ord(byte))
return returnable
def until(self, offset, byte, strings=True, debug=False):
""" Returns hex values from rom starting at offset until the given
byte.
"""
return self.interval(offset, self.find(chr(byte), offset) - offset, strings=strings)
def to_asm(self, address, end_address=None, size=None, max_size=0x4000, debug=None):
""" Disassembles ASM at some address. This will stop disassembling when
either the end_address or size is met. Also, there's a maximum size
that will be parsed, so that large patches of data aren't parsed as
code.
"""
start_address = address
if start_address == None:
raise Exception, "address must be given"
if debug == None:
if not hasattr(self, "debug"):
debug = False
else:
debug = self.debug
# this is probably a terrible idea.. why am i doing this?
if size != None and max_size < size:
raise Exception, "max_size must be greater than or equal to size"
elif end_address != None and (end_address - start_address) > max_size:
raise Exception, "end_address is out of bounds"
elif end_address != None and size != None:
if (end_address - start_address) >= size:
size = end_address - start_address
else:
end_address = start_address + size
elif end_address == None and size != None:
end_address = start_address + size
elif end_address != None and size == None:
size = end_address - start_address
return Asm(start_address=start_address, end_address=end_address, size=size, max_size=max_size, debug=debug, rom=self)
class Asm:
""" z80 disassembler
"""
def __init__(self, start_address=None, end_address=None, size=None, max_size=0x4000, debug=True, rom=None):
assert start_address != None, "start_address must be given"
if rom == None:
file_handler = open("../baserom.gbc", "r")
bytes = file_handler.read()
file_handler.close()
rom = RomStr(bytes)
if debug not in [None, True, False]:
raise Exception, "debug param is invalid"
if debug == None:
debug = False
# get end_address and size in sync with each other
if end_address == None and size != None:
end_address = start_address + size
elif end_address != None and size == None:
size = end_address - start_address
elif end_address != None and size != None:
size = max(end_address - start_address, size)
end_address = start_address + size
# check that the bounds make sense
if end_address != None:
if end_address <= start_address:
raise Exception, "end_address is out of bounds"
elif (end_address - start_address) > max_size:
raise Exception, "end_address goes beyond max_size"
# check more edge cases
if not start_address >= 0:
raise Exception, "start_address must be at least 0"
elif not end_address >= 0:
raise Exception, "end_address must be at least 0"
self.rom = rom
self.start_address = start_address
self.end_address = end_address
self.size = size
self.max_size = max_size
self.debug = debug
self.parse()
def parse(self):
""" Disassembles stuff and things.
"""
rom = self.rom
start_address = self.start_address
end_address = self.end_address
max_size = self.max_size
debug = self.debug
bank_id = start_address / 0x4000
# [{"command": 0x20, "bytes": [0x20, 0x40, 0x50],
# "asm": "jp $5040", "label": "Unknown5040"}]
asm_commands = []
offset = start_address
current_byte_number = 0
last_hl_address = None
last_a_address = None
used_3d97 = False
keep_reading = True
# for labeling future bytes (like for relative jumps)
byte_labels = {}
while offset <= end_address and keep_reading:
current_byte = ord(rom[offset])
is_data = False
maybe_byte = current_byte
# stuff...
raise NotImplementedError, "o_______o"
def __str__(self):
""" ASM pretty printer.
"""
raise NotImplementedError, "zzzzzz"
class AsmList(list):
""" Simple wrapper to prevent all asm lines from being shown on screen.
"""
def length(self):
""" len(self)
"""
return len(self)
def __repr__(self):
""" Simplifies this object so that the output doesn't overflow stdout.
"""
return "AsmList(too long)"
|