diff --git a/tools/sadfe.py b/tools/sadfe.py new file mode 100755 index 0000000..c6a3a40 --- /dev/null +++ b/tools/sadfe.py @@ -0,0 +1,361 @@ +#!/usr/bin/env python2.7 -B + +import cmd +import socket +import sys +import select +import threading +import struct + + +class SimpleAmigaDebuggerConnection(object): + def __init__(self, server): + self.server = server + + def recv(self, size=None): + recv = lambda: self.server.recv(8192) + + if not size: + return recv() + + buf = "" + + while len(buf) < size: + data = recv() + if not len(data): + break + buf += data + + if len(buf) != size: + raise ValueError(buf) + + return buf + + def send(self, data): + self.server.send(data) + + def send_cmd(self, cmd, payload=None): + self.send(struct.pack('>BB', 0xAF, cmd)) + + if payload is not None: + return self.send(payload) + + def close(self): + self.server.close() + + def expect(self, data): + recv_data = self.recv(len(data)) + assert recv_data == data + + def expect_ack(self, cmd): + self.expect(struct.pack('>BB', 0x00, cmd)) + + def expect_done(self, cmd, datalen=0): + self.expect(struct.pack('>BB', 0x1F, cmd)) + + if datalen: + data = self.recv(datalen) + else: + data = None + + self.expect("SAD?") + + return data + + def nop(self): + self.send_cmd(0) + + def write_byte(self, ptr, val): + self.send_cmd(1, struct.pack('>IB', ptr, val)) + self.expect_ack(1) + self.expect_done(1) + + def write_word(self, ptr, val): + self.send_cmd(2, struct.pack('>IH', ptr, val)) + self.expect_ack(2) + self.expect_done(2) + + def write_long(self, ptr, val): + self.send_cmd(3, struct.pack('>II', ptr, val)) + self.expect_ack(3) + self.expect_done(3) + + def read_byte(self, ptr): + self.send_cmd(4, struct.pack('>I', ptr)) + self.expect_ack(4) + data = self.expect_done(4, 1) + return struct.unpack('>B', data)[0] + + def read_word(self, ptr): + self.send_cmd(5, struct.pack('>I', ptr)) + self.expect_ack(5) + data = self.expect_done(5, 2) + return struct.unpack('>H', data)[0] + + def read_long(self, ptr): + self.send_cmd(6, struct.pack('>I', ptr)) + self.expect_ack(6) + data = self.expect_done(6, 4) + return struct.unpack('>I', data)[0] + + def call_address(self, ptr): + self.send_cmd(7, struct.pack('>I', ptr)) + self.expect_ack(7) + self.expect_done(7) + + def return_to_system(self): + self.send_cmd(8, struct.pack('>I', 0)) + self.expect_ack(8) + + def get_context_frame(self): + self.send_cmd(9) + self.expect_ack(9) + data = self.expect_done(9, 4) + return struct.unpack('>I', data)[0] + + def allocate_memory(self, size, memtype): + self.send_cmd(10, struct.pack('>II', size, memtype)) + self.expect_ack(10) + data = self.expect_done(10, 4) + return struct.unpack('>I', data)[0] + + def free_memory(self, ptr): + self.send_cmd(11, struct.pack('>I', ptr)) + self.expect_ack(11) + self.expect_done(11) + + def turn_on_single(self): + self.send_cmd(12) + self.expect_ack(12) + data = self.expect_done(12, 4) + return struct.unpack('>I', data)[0] + + def turn_off_single(self, ptr): + self.send_cmd(13, struct.pack('>I', ptr)) + self.expect_ack(13) + self.expect_done(13) + + def write_array(self, ptr, data): + self.send_cmd(14, struct.pack('>IIs', ptr, len(data), data)) + self.expect_ack(14) + self.expect_done(14) + + def read_array(self, ptr, size): + self.send_cmd(15, struct.pack('>II', ptr, size)) + self.expect_ack(15) + return self.expect_done(15, size) + + def reset(self): + self.send_cmd(16) + self.expect_ack(16) + + +class SimpleAmigaDebuggerFrontEnd(cmd.Cmd): + def __init__(self, sad): + cmd.Cmd.__init__(self) + self.prompt = '>>> ' + self.sad = sad + + def parse_args(self, args, *types): + args = args.split(' ') + + if len(args) != len(types): + print 'Wrong number of arguments!' + return None + + values = [] + + for v, t in zip(args, types): + if t == 'ptr': + v = int(v, 16) + assert (v >= 0 or v < 2 ** 32) + elif t == 'byte': + v = int(v, 10) + assert (v >= 0 or v < 2 ** 8) + elif t == 'word': + v = int(v, 10) + assert (v >= 0 or v < 2 ** 16) + elif t == 'long': + v = int(v, 10) + assert (v >= 0 or v < 2 ** 32) + elif t == 'data': + v = v.decode('hex') + else: + raise ValueError + + values.append(v) + + return values + + def do_quit(self, args): + return True + + def do_wb(self, args): + """ write byte """ + ptr, byte = self.parse_args(args, 'ptr', 'byte') + self.sad.write_byte(ptr, byte) + + def do_ww(self, args): + """ write word """ + ptr, word = self.parse_args(args, 'ptr', 'word') + self.sad.write_word(ptr, word) + + def do_wl(self, args): + """ write long """ + ptr, long_ = self.parse_args(args, 'ptr', 'long') + self.sad.write_long(ptr, long_) + + def do_rb(self, args): + """ read byte """ + ptr = self.parse_args(args, 'ptr')[0] + byte = self.sad.read_byte(ptr) + print "%.2x" % byte + + def do_rw(self, args): + """ read word """ + ptr = self.parse_args(args, 'ptr')[0] + word = self.sad.read_word(ptr) + print "%.4x" % word + + def do_rl(self, args): + """ read long """ + ptr = self.parse_args(args, 'ptr')[0] + long_ = self.sad.read_long(ptr) + print "%.8x" % long_ + + def do_call(self, args): + """ call address """ + ptr = self.parse_args(args, 'ptr')[0] + self.sad.call_address(ptr) + + def do_return(self, _): + """ return to system """ + self.sad.return_to_system() + return True + + def do_frame(self, _): + """ get context frame """ + ptr = self.sad.get_context_frame() + data = self.sad.read_array(ptr, 21 * 4 + 2) + data = struct.unpack('>IIIIIIIIIIIIIIIIIIIIHI', data) + desc = ['VBR', 'AttnFlags', 'ExecBase', 'USP', 'D0', 'D1', 'D2', 'D3', + 'D4', 'D5', 'D6', 'D7', 'A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', + 'Prompt', 'SR', 'PC'] + + for k, v in zip(desc, data): + print k.ljust(max(len(d) for d in desc)), "%.8x" % v + + def do_alloc(self, args): + """ allocate memory """ + size = self.parse_args(args, 'long')[0] + ptr = self.sad.allocate_memory(size, 0) + print "%.8x" % ptr + + def do_free(self, args): + """ free memory """ + ptr = self.parse_args(args, 'ptr')[0] + self.sad.free_memory(ptr) + + def do_trace(self, _): + """ turn on single """ + ptr = self.sad.turn_on_single() + print "%.8x" % ptr + + def do_notrace(self, args): + """ turn off single """ + ptr = self.parse_args(args, 'ptr')[0] + self.sad.turn_off_single(ptr) + + def do_wr(self, args): + """ write array """ + ptr, data = self.parse_args(args, 'ptr', 'data') + self.sad.write_array(ptr, data) + + def do_rr(self, args): + """ read array """ + ptr, size = self.parse_args(args, 'ptr', 'long') + data = self.sad.read_array(ptr, size) + print data.encode('hex') + + def do_reset(self, _): + """ reset """ + self.sad.reset() + + +class SimpleAmigaDebuggerThread(threading.Thread): + def __init__(self, server): + threading.Thread.__init__(self) + self.sad = SimpleAmigaDebuggerConnection(server) + + def run(self): + sadfe = SimpleAmigaDebuggerFrontEnd(self.sad) + sadfe.cmdloop() + self.sad.close() + + +class SimpleAmigaDebuggerClient(object): + def __init__(self): + self.socket = None + self.sad_mode = False + + def sad_keep_alive(self): + ready = select.select([self.socket, self.client], [], [], 0.5)[0] + + if not ready: + self.socket.send(struct.pack('>BB', 0xAF, 0)) + + if self.socket in ready: + data = self.socket.recv(8192) + # print "=> client", data.encode('hex') + self.client.send(data) + + if self.client in ready: + data = self.client.recv(8192) + + if not len(data): + self.client.close() + self.sad_mode = False + return + + # print "=> server", data.encode('hex') + self.socket.send(data) + + def run(self): + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.connect(("localhost", 1234)) + + try: + recvbuf = "" + + while True: + if self.sad_mode: + self.sad_keep_alive() + continue + + data = self.socket.recv(8192) + recvbuf += data + + if recvbuf.endswith("SAD?"): + print "" + + client, server = socket.socketpair() + + recvbuf = "" + self.sad_mode = True + self.client = client + + SimpleAmigaDebuggerThread(server).start() + + sys.stdout.write(data) + sys.stdout.flush() + + recvbuf = recvbuf[-4:] + except KeyboardInterrupt: + pass + + self.socket.close() + + +if __name__ == '__main__': + client = SimpleAmigaDebuggerClient() + client.run()