-
Notifications
You must be signed in to change notification settings - Fork 0
/
monitor.py
231 lines (205 loc) · 6.79 KB
/
monitor.py
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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
from operator import itemgetter
import os
import textwrap
from cpu import *
from disassemble import Disassembler
from memory import Memory
class Monitor(metaclass=CpuMeta):
def __init__(self, level, *, seed=None):
self._level = level
self._bps = {}
self._seed = seed
self.reset()
def _getr(self, reg):
return self._cpu._r[reg]
def _setr(self, reg, val):
self._cpu._r[reg] = val & 0xffff
def reset(self):
self._mem = Memory()
self._mem.load(self._level)
self._cpu = Cpu(self._mem, seed=self._seed)
self._d = Disassembler(self._mem.labels, self._mem.strings)
self._unlocked = False
self._cbrk = None
self._tracefile = None
self._tracing = False
self.E()
def brk(self, target=False, *, silent=False):
if target is None:
self._bps = {}
return
elif not target:
points = sorted(list(self._bps.values()))
for (addr, label) in points:
print("%04x %s" % (addr, '' if label is None else label))
return
elif isinstance(target, str):
addr = self._d.find_label(target)
if target is None:
print("ERROR: unknown label")
return
elif isinstance(target, int) and target < 0x10000:
addr = target
target = None
else:
print("cannot set breakpoint %s" % target)
bps = self._bps
if addr in bps:
del bps[addr]
if not silent:
print("clear breakpoint %04x" % addr)
else:
bps[addr] = (addr, target)
if not silent:
print("set breakpoint %04x" % addr)
def trace(self, tracefile=None):
if tracefile is None:
if self._tracefile is None:
print("please specify a trace file")
return
if self._tracing:
self._tracing = False
print("trace off")
else:
self._tracing = True
print("trace on")
else:
if self._tracefile is None:
self._tracing = True
print("trace on")
self._tracefile = tracefile
def _getdis(self, inst):
mnem, ops = self._d.disassemble(inst)
return '%04x: %-6s %s' % (inst.startpc, mnem, ', '.join(ops))
def _gettrace(self, inst):
return self._getdis(inst)
def _step(self):
try:
inst = self._cpu.next_inst()
self._cpu.exec_inst(inst)
if self._tracing:
print(self._gettrace(inst), file=self._tracefile)
return True
except ExecError as e:
print(str(e))
except DoorUnlocked:
self._unlocked = True
print("DOOR UNLOCKED!")
return False
def C(self, b=None):
if b is not None:
if b != self._cbrk:
self.brk(b, silent=True)
self._cbrk = b
if self._tracing:
print(file=self._tracefile)
print("CONTINUE", file=self._tracefile)
print(file=self._tracefile)
while True:
if not self._step():
break
if self._cpu.pc in self._bps:
self.E()
break
if self._cbrk is not None:
self.brk(self._cbrk, silent=True)
self._cbrk = None
def D(self, line1, line2=None):
if line2 is None:
line2 = line1
line1 &= 0xfff0
line2 &= 0xfff0
for line in range(line1, line2+0x10, 0x10):
data = self._mem[line:line+0x10]
asc = map(lambda x: chr(x) if x >= 32 and x < 127 else '.', data)
asc = ''.join(asc)
dump = ''.join(map(lambda x: "%02x" % x, data))
dump = ' '.join(textwrap.wrap(dump, 4))
print("%04x: %s %s" % (line, dump, asc))
def E(self):
c = self._cpu
print()
print(('pc %04x sp %04x sr %04x cg %04x ' +
'r04 %04x r05 %04x r06 %04x r07 %04x') %
(c.pc, c.sp, c.sr, c.cg, c.r4, c.r5, c.r6, c.r7))
print(('r08 %04x r09 %04x r10 %04x r11 %04x ' +
'r12 %04x r13 %04x r14 %04x r15 %04x') %
(c.r8, c.r9, c.r10, c.r11, c.r12, c.r13, c.r14, c.r15))
print('-' * 78)
inst = c.next_inst()
print(self._getdis(inst))
def I(self, ibytes=None):
try:
c = self._cpu
if ibytes is None:
ibytes = b''
if isinstance(ibytes, str):
if ibytes.startswith('0x'):
ibytes = bytes.fromhex(ibytes[2:])
else:
ibytes = bytes(ibytes, 'ascii')
if not isinstance(ibytes, bytes):
print("ERROR: input must be string or bytes")
return
c.send_input(ibytes)
except ExecError as e:
print(str(e))
def N(self):
c = self._cpu
inst = c.next_inst()
oldcbrk = self._cbrk
oldbps = self._bps
self.C(inst.pc)
self._cbrk = oldcbrk
self._bps = oldbps
def S(self):
if self._step():
self.E()
class MonitorX(Monitor):
def reset(self):
super().reset()
self._cpu = CpuX(self._cpu._mem, seed=self._seed)
def _gettrace(self, inst):
mnem, ops = self._d.disassemble(inst)
d1 = '%04x: %-6s %s' % (inst.startpc, mnem, ', '.join(ops))
d2 = []
if inst.format != Format.JUMP:
try:
for lv in inst.live[:-1]:
if len(lv) == 1:
lvstr = "%04x" % lv[0]
else:
lvstr = "[%04x]:%04x" % (lv[0], lv[1])
d2.append(lvstr)
if inst.live[-1] is not None:
d2.append("%04x" % inst.live[-1])
except AttributeError:
print(inst.format)
return "%-35s--%s" % (d1, ' ' + ' '.join(d2))
def test(soldir):
f = open(os.path.join(soldir, 'solutions.txt'))
ok = True
fails = []
for line in f:
level, sol = line.split()
print("testing", level)
m = Monitor(os.path.join(soldir, level))
try:
m.C()
for inp in sol.split(','):
m.I(bytes.fromhex(inp))
m.C()
if not m._unlocked:
ok = False
fails.append(level)
print(level, "failed")
except:
fails.append(level)
print(level, " failed")
ok = False
if not ok:
print("Failures:")
for fail in fails:
print(fail)
f.close()
return ok