summary history branches tags files
commit:6c3ddab6627857f1caa6821d0048fcb63ae04607
author:mrmekon
committer:mrmekon
date:Thu Nov 8 15:57:55 2012 -0500
parents:f2b792d07cd008bf3951bc52045b1faa3f72f536
Checked in project in current form.  Much functionality is already implemented (connect, load, step, continue, breakpoints).  Much remains.
diff --git a/README.md b/README.md
line changes: +6/-0
index c6d17bb..b122c35
--- a/README.md
+++ b/README.md
@@ -3,6 +3,12 @@ picdb
 
 Command-line debugger for Microchip PIC processors
 
+Debugger written in jython.  Interacts with the Java API provided by Microchip in the MPLAB X installation.
+
+For now this is an early demonstration, working only with the PICkit3 and PIC32MX150F128B processor.
+
+Verify path to JARs is correct in picdb.sh, and run picdb.sh.
+
 
 Reverse engineering notes
 =====

diff --git a/mdb/__init__.py b/mdb/__init__.py
line changes: +0/-0
index 0000000..e69de29
--- /dev/null
+++ b/mdb/__init__.py

diff --git a/mdb/picdebugger.py b/mdb/picdebugger.py
line changes: +178/-0
index 0000000..1434405
--- /dev/null
+++ b/mdb/picdebugger.py
@@ -0,0 +1,178 @@
+import sys
+import time
+import java.lang.String
+import java.lang.System as System
+import com.microchip.mplab.util.observers
+import com.microchip.mplab.mdbcore.debugger.Debugger
+from com.microchip.mplab.comm import MPLABCommProvider
+from com.microchip.mplab.mdbcore.assemblies.assemblyfactory import MCAssemblyFactory
+from com.microchip.mplab.mdbcore.debugger import Debugger
+from com.microchip.mplab.mdbcore.debugger import DebugException
+from com.microchip.mplab.mdbcore.debugger import ToolEvent
+from com.microchip.mplab.mdbcore.loader import Loader
+from com.microchip.mplab.mdbcore.translator.interfaces import ITranslator
+from com.microchip.mplab.mdbcore.translator.exceptions import TranslatorException
+from com.microchip.mplab.mdbcore.disasm import DisAsm
+from com.microchip.mplab.mdbcore.memory.memorytypes import ProgramMemory
+
+from com.microchip.mplab.mdbcore.ControlPointMediator.ControlPoint import BreakType
+from com.microchip.mplab.mdbcore.ControlPointMediator import ControlPointMediator
+
+System.setProperty("crownking.stream.verbosity", "quiet")
+
+class picdebugger(com.microchip.mplab.util.observers.Observer):
+    def __init__(self):
+        self.mdb = None
+        self._breakpoints = []
+        self.isHalted = True
+
+    def Update(self, obj):
+        if obj.GetEvent() == ToolEvent.EVENTS.HALT:
+            self.isHalted = True
+        elif obj.GetEvent() == ToolEvent.EVENTS.RUN:
+            self.isHalted = False
+
+    def waitForHalt(self):
+        while not self.isHalted:
+            pass
+
+    def getPC(self):
+        return self.mdb.GetPC()
+        
+    def selectDevice(self, devstr):
+        # Register PIC target device
+        self.factory = MCAssemblyFactory()
+        self.assembly = self.factory.Create(devstr)
+        self.provider = MPLABCommProvider()
+
+    def enumerateDevices(self):
+        # Enumerate USB debuggers
+        try:
+            self.devices = self.provider.GetCurrentToolList(None, "USB","04D8", None)
+            if not self.devices:
+                print "No USB debugger found."
+        except DebugException:
+            print "Failed to enumerate USB devices."
+            return False
+        return True
+
+    def run(self):
+        self.mdb.Run()
+
+    def setBreakpoint(self, addr):
+        self.cpm = self.assembly.getLookup().lookup(ControlPointMediator)
+        wcps = self.cpm.getWritableControlPointStore()
+        if wcps.getNumberAvailableProgramControlPoints() > 0:
+            bp = wcps.getNewControlPoint()
+            bp.setBreakType(BreakType.PROGRAM)
+            bp.setBreakAddress(addr)
+            bp.setEnabled(True)
+            (file,line) = self.addressToSourceLine(addr)
+            bp.setFileNameAndLine(file, line)
+            self._breakpoints.append(bp)
+            self.cpm.commitAndReleaseWritableControlPointStore(wcps)
+            print "New breakpoint at 0x%X (%s:%d)" % (bp.getBreakAddress(), file, line)
+            return True
+        return False
+
+    def breakpointIndexForAddress(self, addr):
+        result = -1
+        for i,bp in enumerate(self._breakpoints):
+            if bp.getBreakAddress() == addr:
+                result = i
+                break
+        return result
+
+    def listBreakpoints(self):
+        print "All breakpoints:"
+        for i,bp in enumerate(self._breakpoints):
+            print "%d: 0x%X (%s:%d) %c" % (i, bp.getBreakAddress(),
+                bp.getFileName(), bp.getFileLine(),
+                '*' if bp.getEnabled() else ' ')
+
+    def selectDebugger(self):
+        # Select PICkit3 debugger
+        self.factory.ChangeTool(self.assembly,
+                                "PICkit3PlatformTool",
+                                "com.microchip.mplab.mdbcore.PICKit3Tool.PICkit3DbgToolManager",
+                                "debuggerProgrammer",
+                                self.devices[0])
+        self.factory.SetToolProperties(self.assembly,None)
+
+    def connect(self):
+        # Connect to debugger
+        self.assembly.SetHeader("");
+        self.mdb = self.assembly.getLookup().lookup(Debugger)
+
+        print "Connecting to PICkit3..."
+        try:
+            self.mdb.Attach(self, None)
+            self.mdb.Connect(Debugger.CONNECTION_TYPE.DEBUGGER)
+        except DebugException:
+            print "Failed to connect to debugger."
+            return False
+        return True
+
+    def load(self, file):
+        # Load ELF file onto target
+        self.loader = self.assembly.getLookup().lookup(Loader)
+        print "Loading ELF file..."
+        try:
+            self.loader.Load(file)
+            self.mdb.Program(Debugger.PROGRAM_OPERATION.AUTO_SELECT)
+            self.translator = self.assembly.getLookup().lookup(ITranslator)
+            self.disassembler = self.assembly.getLookup().lookup(DisAsm)
+            self.mem = self.assembly.getLookup().lookup(ProgramMemory).GetVirtualMemory()
+        except DebugException:
+            print "Failed to load ELF onto target."
+            return False
+        return True
+
+    def testSourceLookup(self):
+        sourcefile = "/path/to/MainDemo.c"
+        line = 248
+        info = self.translator.sourceLineToAddress(sourcefile, line)
+        print "%s:%d ==> 0x%X" % (sourcefile.split("/")[-1], line, info.lStartAddr)
+
+    def reset(self):
+        # Reset to main
+        print "Resetting target..."
+        self.mdb.Reset(True)
+        pc = self.mdb.GetPC()
+        print "PC: 0x%X" % pc
+
+    def disconnect(self):
+        if self.mdb:
+            self.mdb.Disconnect()
+
+    def addressToSourceLine(self, addr):
+        try:
+            info = self.translator.addressToSourceLine(addr)
+            return (info.file.split("/")[-1], info.lLine)
+        except TranslatorException:
+            return ("unknown",0)
+
+    def step(self):
+        try:
+            self.mdb.StepOver()
+        except DebugException:
+            print "Lost communication with debugger or target!"
+            return
+        pc = self.mdb.GetPC()
+        print "PC: 0x%X" % pc,
+        try:
+            info = self.translator.addressToSourceLine(pc)
+            print " (%s:%d)" % (info.file.split("/")[-1], info.lLine),
+            lines = self.translator.sourceLinesFromAddress(pc, True)
+            for sl in lines.result:
+                ins = self.disassembler.Disassemble(
+                    self.mem.ReadWord(sl.Address()),
+                    self.mem.ReadWord(sl.Address() + sl.AddressIncrement()),
+                    sl.Address() | (1 if sl.AddressIncrement() == 2 else 0),
+                    DisAsm.OPTIONS.FULL_SYMBOLS,
+                    None)
+                print " (%s)" % ins.instruction,
+        except TranslatorException:
+            print " Unknown line.",
+        print
+

diff --git a/picdb.py b/picdb.py
line changes: +140/-0
index 0000000..cf702d4
--- /dev/null
+++ b/picdb.py
@@ -0,0 +1,140 @@
+import sys
+import time
+import string
+import signal
+from select import select
+
+from mdb.picdebugger import picdebugger
+
+class CommandHandler:
+    def __init__(self, quitCB):
+        self.quitCB = quitCB
+        self.dbg = picdebugger()
+        self._commandMap = {
+            "connect": {'fn': self.cmdConnect, 'help': "Conects to a PIC target."},
+            "load": {'fn': self.cmdLoad, 'help': "Load ELF file onto target."},
+            "step": {'fn': self.cmdStep, 'help': "Step over next source line."},
+            "quit": {'fn': self.cmdQuit, 'help': "Quits this program."},
+            "help": {'fn': self.cmdHelp, 'help': "Displays this help."},
+            "break": {'fn': self.cmdBreak},
+            "continue": {'fn': self.cmdContinue},
+            "print": {'fn': self.cmdPrint},
+            "breakpoints": {'fn': self.cmdBreakpoints},
+        }
+        
+    def cmdConnect(self, args):
+        '''
+Connects to a PIC target.
+Usage: connect <PIC device>
+ex: connect PIC32MX150F128B
+'''
+        splitargs = args.split(None)
+        if len(splitargs) < 1:
+            print "Not enough arguments"
+        self.dbg.selectDevice(args)
+        self.dbg.enumerateDevices()
+        self.dbg.selectDebugger()
+        self.dbg.connect()
+
+    def cmdPrint(self, args):
+        if args.lower() == "pc":
+            print "PC: 0x%X" % self.dbg.getPC()
+            
+    def cmdLoad(self, args):
+        self.dbg.load(args)
+        self.dbg.reset()
+
+    def cmdBreak(self, args):
+        addr = int(args,0)
+        self.dbg.setBreakpoint(addr)
+
+    def cmdBreakpoints(self, args):
+        self.dbg.listBreakpoints()
+        
+    def cmdContinue(self, args):
+        self.dbg.run()
+        self.dbg.waitForHalt()
+
+        # It doesn't know where it is immediately after stopping.
+        # But it also LIES.
+        # Ask, wait, ask again.  It'll figure it out.
+        # Hopefully.
+        pc = self.dbg.getPC()
+        time.sleep(1.0)
+        pc = self.dbg.getPC()
+        bp = self.dbg.breakpointIndexForAddress(pc)
+        (file,line) = self.dbg.addressToSourceLine(pc)
+        print "%sStopped at 0x%X (%s:%d)" % ("" if bp < 0 else "Breakpoint %d: " % bp,
+                                             pc,file,line)
+
+    def cmdStep(self, args):
+        self.dbg.step()
+        
+    def cmdQuit(self, args):
+        self.quitCB()
+        
+    def cmdHelp(self, args):
+        if len(args) == 0:
+            print "Type 'help <topic>' for help."
+            print
+            for x,info in self._commandMap.iteritems():
+                line = x.ljust(20)
+                if info.has_key('help'):
+                    line += info['help']
+                print line[0:80]
+            print
+        else:
+            try:
+                fn = self._commandMap[args]['fn']
+                print fn.__doc__
+            except KeyError:
+                print "Nothing found for topic: %s" % args
+
+
+class CommandInterpreter:
+    def __init__(self):
+        self.running = False
+        self._handler = CommandHandler(self.stopInputLoop)
+
+    def stopInputLoop(self):
+        self.running = False
+        signal.alarm(1)
+
+    def _displayPrompt(self):
+        sys.stdout.write("PICdb> ")
+        sys.stdout.flush()
+        
+    def _readUserInput(self):
+        try:
+            user_input = raw_input()
+            return user_input.strip()
+        except EOFError:
+            self.running = False
+            return ""
+
+    def _stringStartsWithCmd(self, str, cmd):
+        matches = False
+        n = len(cmd)
+        m = len(str)
+        if str[0:n].lower() == cmd.lower():
+            if m == n or (m > n and str[n] not in string.ascii_letters):
+                matches = True
+        return matches
+    
+    def runInputLoop(self):
+        signal.signal(signal.SIGINT, lambda x,y: self.stopInputLoop())
+        self.running = True
+        while self.running:
+            self._displayPrompt()
+            user_input = self._readUserInput()
+            if user_input == "":
+                continue
+            for cmd,info in self._handler._commandMap.iteritems():
+                if self._stringStartsWithCmd(user_input, cmd):
+                    info['fn'](user_input[len(cmd):].strip())
+        print
+        self._handler.dbg.disconnect()
+
+interp = CommandInterpreter()
+interp.runInputLoop()
+

diff --git a/picdb.sh b/picdb.sh
line changes: +5/-0
index 0000000..4c5fe2a
--- /dev/null
+++ b/picdb.sh
@@ -0,0 +1,5 @@
+MPLAB_JAR_PATH=/Applications/microchip/mplabx/mplab_ipe.app/Contents/Resources/Java/lib
+JAVAARGS=
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+CLASSPATH=$(echo "$MPLAB_JAR_PATH/"*.jar | tr ' ' ':') jython $JAVAARGS "$DIR"/picdb.py
+