'goodfet.nrf carrier' works.
[goodfet] / client / goodfet.pic
index b99cfc1..282e88b 100755 (executable)
@@ -1,6 +1,12 @@
 #!/usr/bin/env python
 # PIC client (currently only supports dsPIC33F/PIC24H.)
 #
+# Note that a verify operation only considers addresses specified in
+# the given Intel hex file ($foo.hex).
+# 
+# sixfile expects a plain text file, one instruction per line.
+#
+#
 # Scott Livingston  <slivingston at caltech.edu>
 #
 # March, April 2010.
@@ -19,7 +25,8 @@ PICAPP = 0x34
 MONITORAPP = 0x00
 NOK = 0x7E
 dev_table = { 0x00EE : "dsPIC33FJ128GP708",
-              0x00EF : "dsPIC33FJ128GP710" }
+              0x00EF : "dsPIC33FJ128GP710",
+              0x080A : "PIC24HJ12GP201" }
 cfg_table = { 0xF80000 : "FBS",
               0xF80002 : "FSS",
               0xF80004 : "FGS",
@@ -34,6 +41,8 @@ cfg_table = { 0xF80000 : "FBS",
               0xF80016 : "FUID3",
               "width" : 7 } # For pretty printing.
 
+#cfg_bitmask_table = { 
+
 
 #############
 # Functions:
@@ -72,6 +81,13 @@ def readreg( reg_num ):
     client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
     return readVISI()
 
+def writereg( reg_num, word ):
+    """Write 16-bit word to a working register (i.e., W0, W1, ..., W15)."""
+    instr = 0x200000 + ((word&0xffff)<<4) + (reg_num&0xf)
+    client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
+                                       (instr >> 8) & 0xff,
+                                       (instr >> 16) & 0xff] )
+
 def readVISI():
     """Read VISI register; assumes ICSP session already started."""
     data = client.writecmd( PICAPP, 0x83, 0 )
@@ -88,6 +104,11 @@ def readNVMCON():
     runlist( rd_nvmcon_li )
     return readVISI()
 
+def resetPC():
+    """Reset program counter during an active ISCP session."""
+    runlist( [0x040200,   # GOTO 0x0200
+              0x000000] )
+
 def startICSP():
     #print "Starting dsPIC33F/PIC24H ICSP session..."
     data = client.writecmd( PICAPP, 0x84, 0 )
@@ -102,6 +123,86 @@ def dumpVISI():
     result = readVISI()
     print "VISI: 0x%04X" % result
 
+def dump_pm( start_addr, stop_addr, pretty=False ):
+    """Dump routine, now encapsulated in a function.
+Returns an instance of IntelHex corresponding to result.
+
+Note that we start and stop an ICSP session here! This means an
+existing session will be broken."""
+    readpm_cmd_li = [0x200000, # MOV #addr<23:16>, W0
+                     0x880190, # MOV W0, TBLPAG
+                     0x200006, # MOV #addr<15:0>, W6
+                     0xEB0380, # CLR W7
+                     0x000000, # NOP
+                     0xBA1B96, # TBLRDL [W6], [W7++]
+                     0x000000, # NOP
+                     0x000000, # NOP
+                     0xBADBB6, # TBLRDH.B [W6++], [W7++]
+                     0x000000, # NOP
+                     0x000000, # NOP
+                     0xBADBD6, # TBLRDH.B [++W6], [W7++]
+                     0x000000, # NOP
+                     0x000000, # NOP
+                     0xBA1BB6, # TBLRDL [W6++], [W7++]
+                     0x000000, # NOP
+                     0x000000, # NOP
+                     0xBA1B96, # TBLRDL [W6], [W7++]
+                     0x000000, # NOP
+                     0x000000, # NOP
+                     0xBADBB6, # TBLRDH.B [W6++], [W7++]
+                     0x000000, # NOP
+                     0x000000, # NOP
+                     0xBADBD6, # TBLRDH.B [++W6], [W7++]
+                     0x000000, # NOP
+                     0x000000, # NOP
+                     0xBA0BB6, # TBLRDL [W6++], [W7]
+                     0x000000, # NOP
+                     0x000000] # NOP
+
+    dumphex = IntelHex()
+
+    startICSP()
+
+    print "Reading program memory 0x%06X:0x%06X ..." % ( start_addr, stop_addr )
+    # Prep device
+    client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
+    client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
+    client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) #NOP (pump clock)
+
+    last_highb = -1 # Only set TBLPAG when needed.
+    packed_instr_list = [0,0,0,0,0,0] # For packing 4 (24-bit) instructions in 6 (16-bit) words
+    for addr in range( start_addr&0xfffff8, stop_addr+8, 8 ):
+        if (addr>>16)&0xff != last_highb: 
+            last_highb = (addr>>16)&0xff;
+            specify_addr_cmd = [readpm_cmd_li[0] + ((addr & 0xff0000)>>12),
+                                readpm_cmd_li[1],
+                                readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
+        else:
+            specify_addr_cmd = [readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
+        runlist( specify_addr_cmd + readpm_cmd_li[3:] )
+
+        for reg_num in range(6): # Read W0:5, to get packed instructions
+            packed_instr_list[reg_num] = readreg( reg_num )
+        instr_list = words2instr( packed_instr_list )
+
+        for offset in range(4): # Print result
+            if addr+offset*2 < start_addr:
+                continue
+            if addr+offset*2 > stop_addr:
+                break
+            dumphex.puts( ((addr+offset*2)&0xffffff)*2,
+                          chr(instr_list[offset]&0xff)
+                          +chr((instr_list[offset]>>8)&0xff)
+                          +chr((instr_list[offset]>>16)&0xff)
+                          +chr(0) )
+            if pretty:
+                print "0x%06X    0x%06X" % (addr+offset*2,instr_list[offset])
+
+    stopICSP()
+
+    return dumphex
+
+    
 def instr2words( instr_list ):
     """Convert a list of 4 24-bit instructions to a list of 6 words
 (16-bit width).
@@ -140,22 +241,22 @@ Returns [-1] on failure."""
 #############
 
 if len(sys.argv) == 1:
-    print "Usage: %s verb [objects]" % sys.argv[0]
-    print "Warning: only supports dsPIC33F/PIC24H (maybe)...\n"
+    print "Usage: %s verb [objects]\n" % sys.argv[0]
     print "%s devid" % sys.argv[0]
     print "%s read 0x$addr" % sys.argv[0]
     print "%s dump $foo.hex [0x$start 0x$stop] [pretty]" % sys.argv[0]
     print "%s config" % sys.argv[0]
     print "%s program $foo.hex" % sys.argv[0]
-    print "%s verify $foo.hex (NOTE: only addresses in $foo.hex are verified.)" % sys.argv[0]
-    print "%s write $0xADDRESS $0xVALUE" % sys.argv[0]
-    print "%s write_config $REG_NAME [$0x0000]" % sys.argv[0]
+    print "%s verify $foo.hex" % sys.argv[0]
+    print "%s write 0x$address 0x$value" % sys.argv[0]
+    print "%s write_config 0x$reg_address (or $reg_name) [$0x0000]" % sys.argv[0]
     print "%s erase [0x$page]" % sys.argv[0] # bulk or page erase
     print "%s six [instruction]" % sys.argv[0]
-    print "%s sixfile [$foo.txt] (NOTE: plaintext, one instruction per line.)" % sys.argv[0]
+    print "%s sixfile [$foo.txt]" % sys.argv[0]
     print "%s regout" % sys.argv[0]
     print """
 Note: use - for stdout.
+Warning: only supports dsPIC33F/PIC24H (maybe)...
 """
     sys.exit()
 
@@ -192,11 +293,8 @@ if sys.argv[1] == "devid": #0x81
     #print "\n(Note that -1 indicates failure to read a value.)"
 
 elif sys.argv[1] == "config": # Dump configuration registers
-    startICSP()
-
     prep_cmd_li = [0x040200, # GOTO 0x0200
-                   0x040200, # GOTO 0x0200
-                   0x000000, # NOP
+                   0x000000, #
                    0x200F80, # MOV #0x00F8, W0
                    0x880190, # MOV W0, TBLPAG
                    0xEB0300, # CLR W6
@@ -205,6 +303,7 @@ elif sys.argv[1] == "config": # Dump configuration registers
     rd_cmd_li = [0xBA0BB6, # TBLRDL [W6++], [W7]
                  0x000000, # NOP
                  0x000000] # NOP
+    startICSP()
     print "Dumping configuration registers..."
     for instr in prep_cmd_li:
         data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
@@ -218,11 +317,11 @@ elif sys.argv[1] == "config": # Dump configuration registers
         result = readVISI()
         addr = 0xF80000+k*2
         if cfg_table.has_key( addr ):
-            print "0x%06X (%s): 0x%04X" % ( addr,
+            print "0x%06X (%s): 0x%02X" % ( addr,
                                             cfg_table[addr].ljust(cfg_table["width"]),
                                             result )
         else:
-            print "0x%06X: 0x%04X" % ( addr, result )
+            print "0x%06X: 0x%02X" % ( addr, result )
 
     stopICSP()
 
@@ -236,87 +335,117 @@ elif sys.argv[1] == "program":
     except IOError:
         print "Error while attempting to read from %s" % sys.argv[2]
         exit(-1)
+    ph_addrs = proghex.addresses()
+
+    # Load starting program memory address
+    wr_pm64_li_setaddr = [0x040200, # GOTO 0x0200
+                          0x000000, #
+                          0x24001A, # MOV $0x4001, W10
+                          0x883B0A, # MOV W10, NVMCON
+                          0x200000, # + ((addr>>12)&0xff0),  # MOV #addr<23:16>, W0
+                          0x880190, # MOV W0, TBLPAG
+                          0x200007] # + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
+
+    # Load 4 instructions into write latches
+    wr_pm64_li_wrlat = [0xEB0300, # CLR W6
+                        0x000000, # NOP
+                        0xBB0BB6, # TBLWTL [W6++], [W7]
+                        0x000000, # NOP
+                        0x000000, # NOP
+                        0xBBDBB6, # TBLWTH.B [W6++], [W7++]
+                        0x000000, # NOP
+                        0x000000, # NOP
+                        0xBBEBB6, # TBLWTH.B [W6++], [++W7]
+                        0x000000, # NOP
+                        0x000000, # NOP
+                        0xBB1BB6, # TBLWTL [W6++], [W7++]
+                        0x000000, # NOP
+                        0x000000, # NOP
+                        0xBB0BB6, # TBLWTL [W6++], [W7]
+                        0x000000, # NOP
+                        0x000000, # NOP
+                        0xBBDBB6, # TBLWTH.B [W6++], [W7++]
+                        0x000000, # NOP
+                        0x000000, # NOP
+                        0xBBEBB6, # TBLWTH.B [W6++], [++W7]
+                        0x000000, # NOP
+                        0x000000, # NOP
+                        0xBB1BB6, # TBLWTL [W6++], [W7++]
+                        0x000000, # NOP
+                        0x000000] # NOP
 
-    wr_pm_li = [0x040200, # GOTO 0x0200
-                0x040200, # GOTO 0x0200
-                0x000000, # NOP
-
-                0x24003A, # MOV $0x4003, W10
-                0x883B0A, # MOV W10, NVMCON
-                
-                0x200000,# + ((addr>>12)&0xff0),  # MOV #addr<23:16>, W0
-                0x880190, # MOV W0, TBLPAG
-                0x200007,# + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
-
-                # Here we load the instruction into registers W0:W1
-                0x200000,# + ((new_words[0]&0xffff)<<4),
-                0x200001,# + ((new_words[1]&0xffff)<<4),
-
-                0xEB0300, # CLR W6
-                0x000000, # NOP
-                0xBB0BB6, # TBLWTL [W6++], [W7]
-                0x000000, # NOP
-                0x000000, # NOP
-                0xBBDBB6, # TBLWTH.B [W6++], [W7++]
-                0x000000, # NOP
-                0x000000, # NOP
-
-                0xA8E761, # BSET NVMCON, #WR
-                0x000000, # NOP
-                0x000000, # NOP
-                0x000000, # NOP
-                0x000000] # NOP
-
+    wr_pm64_li_wr = [0xA8E761, # BSET NVMCON, #WR
+                     0x000000, # NOP
+                     0x000000, # NOP
+                     0x000000, # NOP
+                     0x000000] # NOP
+    
     startICSP()
-
-    for addr in proghex.addresses():
-        # Ignore non-aligned steps
-        if addr % 4 != 0:
-            continue
-
-        # Configuration registers must be treated separately
-        if (addr>>1) >= 0xf80000 and (addr>>1) <= 0xf80017:
-            wr_cfg_li = [0x040200, # GOTO 0x0200
-                         0x040200, # GOTO 0x0200
-                         0x000000, # NOP
-                         0x200007 + ((addr<<3)&0xffff0), # MOV #addr<15:0>, W7
-                         0x24000A, # MOV #0x4000, W10
-                         0x883B0A, # MOV W10, NVMCON
-                         0x200F80, # MOV #0xF8, W0
-                         0x880190, # MOV W0, TBLPAG
-                         0x200000 + ((proghex[addr]&0x00ff)<<4), # MOV #new_val<7:0>, W0
-                         0xBB0B80, # TBLWTL W0, [W7]
-                         0x000000, # NOP
-                         0x000000, # NOP
-                         0xA8E761, # BSET NVMCON, #WR
-                         0x000000, # NOP
-                         0x000000, # NOP
-                         0x000000, # NOP
-                         0x000000] # NOP
-            runlist( wr_cfg_li )
+    
+    for last_code_addr in reversed(ph_addrs):
+        if (last_code_addr>>1) < 0xf80000:
+            last_row_addr = (last_code_addr>>1) & 0xffff80
+            break
+    for addr in range((ph_addrs[0]>>1)&0xffff80,last_code_addr+2,128):
+        # Prevent crossing addresses where upper byte
+        # (i.e., address<23:16>) changes.
+        #if addr+126 > 0xffff:
+        #    stop_addr = addr | 0xffff
+        #else:
+        #    stop_addr = addr+126
+
+        runlist( wr_pm64_li_setaddr[:4]
+                 + [0x200000 + ((addr>>12)&0xff0),
+                    wr_pm64_li_setaddr[5],
+                    0x200007 + ((addr&0xffff)<<4)] )
+                 
+        instr_list = [0,0,0,0]
+        for mid_addr in range(addr,addr+126,8):
+            for offset in range(4):
+                if (mid_addr+offset*2)<<1 not in ph_addrs:
+                    instr_list[offset] = 0xffffff
+                else:
+                    instr_list[offset] = proghex[(mid_addr+offset*2)<<1]
+                    instr_list[offset] |= proghex[((mid_addr+offset*2)<<1)+1] << 8
+                    instr_list[offset] |= proghex[((mid_addr+offset*2)<<1)+2] << 16
+            packed_instr_list = instr2words( instr_list )
+            runlist( [0x200000 + ((packed_instr_list[0]&0xffff)<<4),
+                      0x200001 + ((packed_instr_list[1]&0xffff)<<4),
+                      0x200002 + ((packed_instr_list[2]&0xffff)<<4),
+                      0x200003 + ((packed_instr_list[3]&0xffff)<<4),
+                      0x200004 + ((packed_instr_list[4]&0xffff)<<4),
+                      0x200005 + ((packed_instr_list[5]&0xffff)<<4)]
+                     + wr_pm64_li_wrlat )
+        
+        runlist( wr_pm64_li_wr )
+        status = readNVMCON()
+        while status & 0x8000:
+            client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
             status = readNVMCON()
-            while status & 0x8000:
-                client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
-                status = readNVMCON()
-            continue
 
-        # Build instruction from 3 separate bytes
-        instr = proghex[addr]
-        instr |= proghex[addr+1] << 8
-        instr |= proghex[addr+2] << 16
-
-        # Change HEX file address to actual program memory address
-        addr_pm = addr >> 1
-
-        # Program this instruction word
-        addr_highb_cmd  = 0x200000 + ((addr_pm>>12)&0xff0)
-        addr_loww_cmd   = 0x200007 + ((addr_pm&0xffff)<<4)
-        instr_loww_cmd  = 0x200000 + ((instr&0xffff)<<4)
-        instr_highb_cmd = 0x200001 + ((instr>>12)&0xff0)
-        runlist( wr_pm_li[:5] + [addr_highb_cmd] + [wr_pm_li[6]]
-                 + [addr_loww_cmd,
-                    instr_loww_cmd, instr_highb_cmd]
-                 + wr_pm_li[10:] )
+    # Configuration registers must be treated separately
+    for addr in ph_addrs[ph_addrs.index(last_code_addr):]:
+        if addr%4 != 0:
+            continue
+        if (addr>>1) > 0xf80017:
+            break
+        wr_cfg_li = [0x040200, # GOTO 0x0200
+                     0x000000, #
+                     0x200007 + ((addr<<3)&0xffff0), # MOV #addr<15:0>, W7
+                     0x24000A, # MOV #0x4000, W10
+                     0x883B0A, # MOV W10, NVMCON
+                     0x200F80, # MOV #0xF8, W0
+                     0x880190, # MOV W0, TBLPAG
+                     0x200000 + ((proghex[addr]&0x00ff)<<4), # MOV #new_val<7:0>, W0
+                     0xBB0B80, # TBLWTL W0, [W7]
+                     0x000000, # NOP
+                     0x000000, # NOP
+                     0xA8E761, # BSET NVMCON, #WR
+                     0x000000, # NOP
+                     0x000000, # NOP
+                     0x000000, # NOP
+                     0x000000] # NOP
+        runlist( wr_cfg_li )
         status = readNVMCON()
         while status & 0x8000:
             client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
@@ -336,62 +465,35 @@ elif sys.argv[1] == "verify":
         print "Error while attempting to read from %s" % sys.argv[2]
         exit(-1)
 
-    readpm_cmd_li = [0x040200, # GOTO 0x0200
-                     0x040200, # GOTO 0x0200
-                     0x000000, # NOP
-                     0x200000, # MOV #addr_pm<23:16>, W0
-                     0x880190, # MOV W0, TBLPAG
-                     0x200006, # MOV #addr_pm<15:0>, W6
-                     0xEB0380, # CLR W7
-                     0xEB0080, # CLR W1
-                     0x000000, # NOP
-                     0xBA1B96, # TBLRDL [W6], [W7++]
-                     0x000000, # NOP
-                     0x000000, # NOP
-                     0xBACB96, # TBLRDH.B [W6], [W7]
-                     0x000000, # NOP
-                     0x000000] # NOP
-
     print "WARNING: verifying config registers not yet supported."
 
-    startICSP()
-
-    # Step through addresses in HEX file and compare to those in target PIC
     for addr in proghex.addresses():
-        # Ignore non-aligned steps
-        if addr % 4 != 0:
+        if addr % 4 != 0 or (addr>>1) < 0x200:
             continue
+        #elif first_addr == None:
+        #    first_addr = addr>>1
+        if (addr>>1) >= 0xf80000:
+            break
+        last_addr = addr>>1
+    dumphex = dump_pm( (proghex.addresses()[0]>>1),
+                       last_addr )
 
-        # Configuration registers must be treated separately
-        if (addr>>1) >= 0xf80000 and (addr>>1) <= 0xf80017:
-            # Need to read datasheets to see which bits are masked.
-            # Then add dictionary to handle this.
-            # (Straightforward but tedious.)
-            continue
-        else: 
-            # Build instruction from 3 separate bytes
-            instr = proghex[addr]
-            instr |= proghex[addr+1] << 8
-            instr |= proghex[addr+2] << 16
-
-        # Change HEX file address to actual program memory address
-        addr_pm = addr >> 1
-        
-        runlist( readpm_cmd_li[:3] + [readpm_cmd_li[3]+((addr_pm&0xff0000)>>12)]
-                 + [readpm_cmd_li[4]] + [readpm_cmd_li[5]+((addr_pm&0xffff)<<4)]
-                 + readpm_cmd_li[6:] )
-        loww = readreg(0)  # Read W0, which has lower 16 bits
-        highb = readreg(1) # Read W1, which has high 8 bits
-        result = (highb<<16) | loww
-        
+    # Step through addresses in HEX file and compare to those in target PIC
+    for addr in proghex.addresses():
+        if addr>>1 >= 0xF80000:
+            break # verifying config registers not yet supported
         # Compare; fail if mismatch
-        if instr != result:
-            print "Fail at address 0x%06X: found 0x%06X, expected 0x%06X." % (addr_pm, result, instr )
-            stopICSP()
+        if proghex[addr] != dumphex[addr]:
+            addr &= addr&0xfffffffc
+            found_instr = dumphex[addr]
+            found_instr |= dumphex[addr+1]<<8
+            found_instr |= dumphex[addr+2]<<16
+            exp_instr = proghex[addr]
+            exp_instr |= proghex[addr+1]<<8
+            exp_instr |= proghex[addr+2]<<16
+            print "Fail at address 0x%06X: found 0x%06X, expected 0x%06X." % ( addr>>1, found_instr, exp_instr )
             exit(-1)
 
-    stopICSP()
-
     print "PASSED" # Be verbose.
 
 
@@ -409,8 +511,7 @@ elif sys.argv[1] == "write":
     #new_words = instr2words( new_instr_li )
         
     wr_pm_li = [0x040200, # GOTO 0x0200
-                0x040200, # GOTO 0x0200
-                0x000000, # NOP
+                0x000000, #
 
                 0x24003A, # MOV $0x4003, W10
                 0x883B0A, # MOV W10, NVMCON
@@ -480,14 +581,16 @@ elif sys.argv[1] == "write_config":
         new_val = 0xFF # Assume new value
     else:
         new_val = int(float.fromhex(sys.argv[3]))
-    if sys.argv[2] not in cfg_table.values():
-        print "Given register, %s, not recognized." % sys.argv[2]
-        exit(1)
-    reg_addr = cfg_table.keys()[cfg_table.values().index(sys.argv[2])]
+    try:
+        reg_addr = int(float.fromhex(sys.argv[2]))
+    except ValueError:
+        if sys.argv[2] not in cfg_table.values():
+            print "Given register, %s, not recognized." % sys.argv[2]
+            exit(1)
+        reg_addr = cfg_table.keys()[cfg_table.values().index(sys.argv[2])]
     
     wr_cfg_li = [0x040200, # GOTO 0x0200
-                 0x040200, # GOTO 0x0200
-                 0x000000, # NOP
+                 0x000000, #
                  0x200007 + ((reg_addr&0xffff)<<4), # MOV #addr<15:0>, W7
                  0x24000A, # MOV #0x4000, W10
                  0x883B0A, # MOV W10, NVMCON
@@ -520,8 +623,7 @@ elif sys.argv[1] == "read": # Read an address of program memory
     addr = int(float.fromhex(sys.argv[2]))
 
     readpm_cmd_li = [0x040200,                         # GOTO 0x0200
-                     0x040200,                         # GOTO 0x0200
-                     0x000000,                         # NOP
+                     0x000000,                         #
                      0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
                      0x880190,                         # MOV W0, TBLPAG
                      0x200006+((addr & 0xffff)<<4),    # MOV #addr<15:0>, W6
@@ -562,52 +664,18 @@ elif sys.argv[1] == "dump": # Read section of program memory
         # Assume all of program memory (including unimplemented sections,
         # which will be read as zeroes.
         start_addr = 0x0
-        stop_addr = 0xFFFFFE #
+        stop_addr = 0xFFFFFE
 
-    readpm_cmd_li = [0x200000,                         # MOV #addr<23:16>, W0
-                     0x880190,                         # MOV W0, TBLPAG
-                     0x200006,                         # MOV #addr<15:0>, W6
-                     0xEB0380,                         # CLR W7
-                     0xEB0080,                         # CLR W1
-                     0x000000,                         # NOP
-                     0xBA1B96,                         # TBLRDL [W6], [W7++]
-                     0x000000,                         # NOP
-                     0x000000,                         # NOP
-                     0xBACB96,                         # TBLRDH.B [W6], [W7]
-                     0x000000,                         # NOP
-                     0x000000]                         # NOP
-
-    dumphex = IntelHex()
-
-    startICSP()
-
-    print "Reading program memory 0x%06X:0x%06X ..." % ( start_addr, stop_addr )
-    # Prep device
-    client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
-    client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
-    client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) #NOP (pump clock)
-    for addr in range( start_addr, stop_addr+2, 2 ):
-        specify_addr_cmd = [readpm_cmd_li[0] + ((addr & 0xff0000)>>12),
-                            readpm_cmd_li[1],
-                            readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
-        runlist( specify_addr_cmd + readpm_cmd_li[3:] )
-        loww = readreg(0)  # Read W0, which has lower 16 bits
-        highb = readreg(1) # Read W1, which has high 8 bits
-        result = (highb<<16) | loww
-        dumphex.puts( (addr&0xffffff)*2,
-                      chr(result&0xff)+chr((result>>8)&0xff)+chr((result>>16)&0xff)+chr(0) )
-        if print_term:
-            print "0x%06X    0x%06X" % (addr,result)
-    
+    dumphex = dump_pm( start_addr, stop_addr, print_term )
+        
     if not print_term:
         if fname == "-":
             dumphex.tofile( sys.stdout, format="hex" )
         else:
             dumphex.tofile( fname, format="hex" )
 
-    stopICSP()
 
-elif sys.argv[1] == "erase": # Bulk erase (all program memory)
+elif sys.argv[1] == "erase": # Bulk (all program memory) or page erase
 
     if len(sys.argv) == 3:
         addr = int(float.fromhex(sys.argv[2]))
@@ -616,8 +684,7 @@ elif sys.argv[1] == "erase": # Bulk erase (all program memory)
 
     if addr is None: # Bulk erase (all of program memory)
         erase_cmd_li = [0x040200, # GOTO 0x0200
-                        0x040200, # GOTO 0x0200
-                        0x000000, # NOP
+                        0x000000, #
                         0x2404FA, # MOV $0x404F, W10
                         0x883B0A, # MOV W10, NVMCON
                         0xA8E761, # BSET NVMCON, #WR
@@ -626,14 +693,14 @@ elif sys.argv[1] == "erase": # Bulk erase (all program memory)
                         0x000000, # NOP
                         0x000000] # NOP
     else: # Page erase
+        print "Erasing page of 0x%06X" % addr
         erase_cmd_li = [0x040200, # GOTO 0x0200
-                        0x040200, # GOTO 0x0200
-                        0x000000, # NOP
+                        0x000000, #
                         0x24042A, # MOV $0x4042, W10
                         0x883B0A, # MOV W10, NVMCON
                         0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
                         0x880190, # MOV W0, TBLPAG
-                        0x200001+((addr&0xffff)<<4), # MOV 0x0, W1
+                        0x200001+((addr&0xffff)<<4), # MOV addr<15:0>, W1
                         0x000000, # NOP
                         0xBB0881, # TBLTWL W1, [W1]
                         0x000000, # NOP