b99cfc189279406f5b21c774a473a1d67810c8c5
[goodfet] / client / goodfet.pic
1 #!/usr/bin/env python
2 # PIC client (currently only supports dsPIC33F/PIC24H.)
3 #
4 # Scott Livingston  <slivingston at caltech.edu>
5 #
6 # March, April 2010.
7
8
9 import sys
10 import time
11
12 from GoodFET import GoodFET
13 from intelhex import IntelHex
14
15 #############
16 # Some constants (not really immutable...)
17 #############
18 PICAPP = 0x34
19 MONITORAPP = 0x00
20 NOK = 0x7E
21 dev_table = { 0x00EE : "dsPIC33FJ128GP708",
22               0x00EF : "dsPIC33FJ128GP710" }
23 cfg_table = { 0xF80000 : "FBS",
24               0xF80002 : "FSS",
25               0xF80004 : "FGS",
26               0xF80006 : "FOSCSEL",
27               0xF80008 : "FOSC",
28               0xF8000A : "FWDT",
29               0xF8000C : "FPOR",
30               0xF8000E : "FICD",
31               0xF80010 : "FUID0",
32               0xF80012 : "FUID1",
33               0xF80014 : "FUID2",
34               0xF80016 : "FUID3",
35               "width" : 7 } # For pretty printing.
36
37
38 #############
39 # Functions:
40 #############
41
42 def runlist( cmd_li ):
43     """Load (and execute) a given list of instructions.
44 Assumes ICSP session already started."""
45     cmd_byte_li = build_instr_stream( cmd_li )
46     data = client.writecmd( PICAPP, 0x86, len(cmd_byte_li), cmd_byte_li )
47     if client.app == PICAPP and client.verb == 0x86 and client.count != len(cmd_byte_li):
48         print "Error: incomplete execution of sixlist.\nOnly %d instructions run." % (client.count/3)
49         stopICSP()
50         exit(-1)
51     elif client.app == 0xff and client.verb == 0xff: # GoodFET debugstr
52         print "Error (in runlist): failed transaction; aborting."
53         stopICSP()
54         exit(-1)
55
56 def build_instr_stream( cmd_li ):
57     """Given a list of instruction words, returns a list of bytes of
58 the same, in little endian ordering."""
59     cmd_byte_li = []
60     for instr in cmd_li:
61         cmd_byte_li += [instr & 0xff,
62                         (instr >> 8) & 0xff,
63                         (instr >> 16) & 0xff]
64     return cmd_byte_li
65
66 def readreg( reg_num ):
67     """Read contents of a working register (i.e. W0, W1, ..., W15)."""
68     instr = 0x883C20+(reg_num&0xf)
69     client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
70                                        (instr >> 8) & 0xff,
71                                        (instr >> 16) & 0xff] )
72     client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
73     return readVISI()
74
75 def readVISI():
76     """Read VISI register; assumes ICSP session already started."""
77     data = client.writecmd( PICAPP, 0x83, 0 )
78     result = ord(data[0])
79     result |= ord(data[1]) << 8
80     return result
81
82 def readNVMCON():
83     """Read NVMCON register; assumes ICSP session already started."""
84     rd_nvmcon_li = [0x803B00, # MOV NVMCON, W0
85                     0x883C20, # MOV W0, VISI
86                     0x000000, # NOP
87                     0x000000] # NOP
88     runlist( rd_nvmcon_li )
89     return readVISI()
90
91 def startICSP():
92     #print "Starting dsPIC33F/PIC24H ICSP session..."
93     data = client.writecmd( PICAPP, 0x84, 0 )
94
95 def stopICSP():
96     #print "Stopping dsPIC33F/PIC24H ICSP session..."
97     data = client.writecmd( PICAPP, 0x85, 0 )
98
99 def dumpVISI():
100     """Read and print VISI register to stdout; assumes ICSP session already started."""
101     print "Reading VISI register..."
102     result = readVISI()
103     print "VISI: 0x%04X" % result
104
105 def instr2words( instr_list ):
106     """Convert a list of 4 24-bit instructions to a list of 6 words
107 (16-bit width).
108
109 Returns [-1] on failure."""
110     if len(instr_list) < 4: # Catch mistakes
111         print "Error in instr2words: operand has less than 4 elements."
112         return [-1]
113     word_list = [0,0,0,0,0,0]
114     for k in [0,1]:
115         word_list[k*3] = instr_list[k*2] & 0xffff
116         word_list[k*3+1] = (instr_list[k*2]>>16)&0xff
117         word_list[k*3+1] |= (instr_list[k*2+1]>>8)&0xff00
118         word_list[k*3+2] = instr_list[k*2+1] & 0xffff
119     return word_list
120
121 def words2instr( word_list ):
122     """4 24-bit instructions from a packing in 6 words (16 bit width).
123 This is the inverse of function instr2words.
124
125 Returns [-1] on failure."""
126     if len(word_list) < 6: # Catch mistakes
127         print "Error in words2instr: operand has less than 6 elements."
128         return [-1]
129     instr_list = [0,0,0,0]
130     for k in [0,1]:
131         instr_list[k*2] = word_list[k*3]
132         instr_list[k*2] |= (word_list[k*3+1]&0xff)<<16
133         instr_list[k*2+1] = word_list[k*3+2]
134         instr_list[k*2+1] |= (word_list[k*3+1]&0xff00)<<8
135     return instr_list
136
137
138 #############
139 # Main entry: 
140 #############
141
142 if len(sys.argv) == 1:
143     print "Usage: %s verb [objects]" % sys.argv[0]
144     print "Warning: only supports dsPIC33F/PIC24H (maybe)...\n"
145     print "%s devid" % sys.argv[0]
146     print "%s read 0x$addr" % sys.argv[0]
147     print "%s dump $foo.hex [0x$start 0x$stop] [pretty]" % sys.argv[0]
148     print "%s config" % sys.argv[0]
149     print "%s program $foo.hex" % sys.argv[0]
150     print "%s verify $foo.hex (NOTE: only addresses in $foo.hex are verified.)" % sys.argv[0]
151     print "%s write $0xADDRESS $0xVALUE" % sys.argv[0]
152     print "%s write_config $REG_NAME [$0x0000]" % sys.argv[0]
153     print "%s erase [0x$page]" % sys.argv[0] # bulk or page erase
154     print "%s six [instruction]" % sys.argv[0]
155     print "%s sixfile [$foo.txt] (NOTE: plaintext, one instruction per line.)" % sys.argv[0]
156     print "%s regout" % sys.argv[0]
157     print """
158 Note: use - for stdout.
159 """
160     sys.exit()
161
162 # Initialize and open connection to GoodFET
163 client = GoodFET()
164 client.verbose = False # Dump activity to terminal
165 client.serInit( timeout=10 ) # UART comm timeout (in seconds)
166
167
168 # Handle each possible PIC verb in turn
169
170 if sys.argv[1] == "devid": #0x81
171     print "Requesting Application ID, DEVID and hardware revision..."
172     data = client.writecmd( PICAPP, 0x81, 0 )
173     
174     if len(data) > 0:
175         appid = ord(data[0])
176     else:
177         appid = -1
178     if len(data) > 2:
179         devid = ord(data[1]) + (ord(data[2]) << 8)
180     else:
181         devid = -1
182     if len(data) > 4:
183         hwrev = ord(data[3]) + (ord(data[4]) << 8)
184     else:
185         hwrev = -1
186     print "Application ID:   0x%02X" % appid
187     if dev_table.has_key( devid ):
188         print "DEVID:          0x%04X  (%s)" % ( devid, dev_table[devid] )
189     else:
190         print "DEVID:          0x%04X  (unknown)" %  devid
191     print "revision:       0x%04X"% hwrev
192     #print "\n(Note that -1 indicates failure to read a value.)"
193
194 elif sys.argv[1] == "config": # Dump configuration registers
195     startICSP()
196
197     prep_cmd_li = [0x040200, # GOTO 0x0200
198                    0x040200, # GOTO 0x0200
199                    0x000000, # NOP
200                    0x200F80, # MOV #0x00F8, W0
201                    0x880190, # MOV W0, TBLPAG
202                    0xEB0300, # CLR W6
203                    0x207847, # MOV #VISI, W7
204                    0x000000] # NOP
205     rd_cmd_li = [0xBA0BB6, # TBLRDL [W6++], [W7]
206                  0x000000, # NOP
207                  0x000000] # NOP
208     print "Dumping configuration registers..."
209     for instr in prep_cmd_li:
210         data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
211                                                   (instr >> 8) & 0xff,
212                                                   (instr >> 16) & 0xff] )
213     for k in range(12): # twelve configuration registers total
214         for instr in rd_cmd_li:
215             data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
216                                                   (instr >> 8) & 0xff,
217                                                   (instr >> 16) & 0xff] )
218         result = readVISI()
219         addr = 0xF80000+k*2
220         if cfg_table.has_key( addr ):
221             print "0x%06X (%s): 0x%04X" % ( addr,
222                                             cfg_table[addr].ljust(cfg_table["width"]),
223                                             result )
224         else:
225             print "0x%06X: 0x%04X" % ( addr, result )
226
227     stopICSP()
228
229 elif sys.argv[1] == "program":
230     if len(sys.argv) != 3:
231         print "Error: an Intel HEX file to load must be given."
232         exit(1)
233
234     try:
235         proghex = IntelHex( sys.argv[2] )
236     except IOError:
237         print "Error while attempting to read from %s" % sys.argv[2]
238         exit(-1)
239
240     wr_pm_li = [0x040200, # GOTO 0x0200
241                 0x040200, # GOTO 0x0200
242                 0x000000, # NOP
243
244                 0x24003A, # MOV $0x4003, W10
245                 0x883B0A, # MOV W10, NVMCON
246                 
247                 0x200000,# + ((addr>>12)&0xff0),  # MOV #addr<23:16>, W0
248                 0x880190, # MOV W0, TBLPAG
249                 0x200007,# + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
250
251                 # Here we load the instruction into registers W0:W1
252                 0x200000,# + ((new_words[0]&0xffff)<<4),
253                 0x200001,# + ((new_words[1]&0xffff)<<4),
254
255                 0xEB0300, # CLR W6
256                 0x000000, # NOP
257                 0xBB0BB6, # TBLWTL [W6++], [W7]
258                 0x000000, # NOP
259                 0x000000, # NOP
260                 0xBBDBB6, # TBLWTH.B [W6++], [W7++]
261                 0x000000, # NOP
262                 0x000000, # NOP
263
264                 0xA8E761, # BSET NVMCON, #WR
265                 0x000000, # NOP
266                 0x000000, # NOP
267                 0x000000, # NOP
268                 0x000000] # NOP
269
270     startICSP()
271
272     for addr in proghex.addresses():
273         # Ignore non-aligned steps
274         if addr % 4 != 0:
275             continue
276
277         # Configuration registers must be treated separately
278         if (addr>>1) >= 0xf80000 and (addr>>1) <= 0xf80017:
279             wr_cfg_li = [0x040200, # GOTO 0x0200
280                          0x040200, # GOTO 0x0200
281                          0x000000, # NOP
282                          0x200007 + ((addr<<3)&0xffff0), # MOV #addr<15:0>, W7
283                          0x24000A, # MOV #0x4000, W10
284                          0x883B0A, # MOV W10, NVMCON
285                          0x200F80, # MOV #0xF8, W0
286                          0x880190, # MOV W0, TBLPAG
287                          0x200000 + ((proghex[addr]&0x00ff)<<4), # MOV #new_val<7:0>, W0
288                          0xBB0B80, # TBLWTL W0, [W7]
289                          0x000000, # NOP
290                          0x000000, # NOP
291                          0xA8E761, # BSET NVMCON, #WR
292                          0x000000, # NOP
293                          0x000000, # NOP
294                          0x000000, # NOP
295                          0x000000] # NOP
296             runlist( wr_cfg_li )
297             status = readNVMCON()
298             while status & 0x8000:
299                 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
300                 status = readNVMCON()
301             continue
302
303         # Build instruction from 3 separate bytes
304         instr = proghex[addr]
305         instr |= proghex[addr+1] << 8
306         instr |= proghex[addr+2] << 16
307
308         # Change HEX file address to actual program memory address
309         addr_pm = addr >> 1
310
311         # Program this instruction word
312         addr_highb_cmd  = 0x200000 + ((addr_pm>>12)&0xff0)
313         addr_loww_cmd   = 0x200007 + ((addr_pm&0xffff)<<4)
314         instr_loww_cmd  = 0x200000 + ((instr&0xffff)<<4)
315         instr_highb_cmd = 0x200001 + ((instr>>12)&0xff0)
316         runlist( wr_pm_li[:5] + [addr_highb_cmd] + [wr_pm_li[6]]
317                  + [addr_loww_cmd,
318                     instr_loww_cmd, instr_highb_cmd]
319                  + wr_pm_li[10:] )
320         status = readNVMCON()
321         while status & 0x8000:
322             client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
323             status = readNVMCON()
324
325     stopICSP()
326
327
328 elif sys.argv[1] == "verify":
329     if len(sys.argv) != 3:
330         print "Error: an Intel HEX file with which to compare must be given."
331         exit(1)
332
333     try:
334         proghex = IntelHex( sys.argv[2] )
335     except IOError:
336         print "Error while attempting to read from %s" % sys.argv[2]
337         exit(-1)
338
339     readpm_cmd_li = [0x040200, # GOTO 0x0200
340                      0x040200, # GOTO 0x0200
341                      0x000000, # NOP
342                      0x200000, # MOV #addr_pm<23:16>, W0
343                      0x880190, # MOV W0, TBLPAG
344                      0x200006, # MOV #addr_pm<15:0>, W6
345                      0xEB0380, # CLR W7
346                      0xEB0080, # CLR W1
347                      0x000000, # NOP
348                      0xBA1B96, # TBLRDL [W6], [W7++]
349                      0x000000, # NOP
350                      0x000000, # NOP
351                      0xBACB96, # TBLRDH.B [W6], [W7]
352                      0x000000, # NOP
353                      0x000000] # NOP
354
355     print "WARNING: verifying config registers not yet supported."
356
357     startICSP()
358
359     # Step through addresses in HEX file and compare to those in target PIC
360     for addr in proghex.addresses():
361         # Ignore non-aligned steps
362         if addr % 4 != 0:
363             continue
364
365         # Configuration registers must be treated separately
366         if (addr>>1) >= 0xf80000 and (addr>>1) <= 0xf80017:
367             # Need to read datasheets to see which bits are masked.
368             # Then add dictionary to handle this.
369             # (Straightforward but tedious.)
370             continue
371         else: 
372             # Build instruction from 3 separate bytes
373             instr = proghex[addr]
374             instr |= proghex[addr+1] << 8
375             instr |= proghex[addr+2] << 16
376
377         # Change HEX file address to actual program memory address
378         addr_pm = addr >> 1
379         
380         runlist( readpm_cmd_li[:3] + [readpm_cmd_li[3]+((addr_pm&0xff0000)>>12)]
381                  + [readpm_cmd_li[4]] + [readpm_cmd_li[5]+((addr_pm&0xffff)<<4)]
382                  + readpm_cmd_li[6:] )
383         loww = readreg(0)  # Read W0, which has lower 16 bits
384         highb = readreg(1) # Read W1, which has high 8 bits
385         result = (highb<<16) | loww
386         
387         # Compare; fail if mismatch
388         if instr != result:
389             print "Fail at address 0x%06X: found 0x%06X, expected 0x%06X." % (addr_pm, result, instr )
390             stopICSP()
391             exit(-1)
392
393     stopICSP()
394
395     print "PASSED" # Be verbose.
396
397
398 elif sys.argv[1] == "write":
399     if len(sys.argv) != 4:
400         print "Error: an address (in program memory) and\n value (or instruction) to write must be given."
401         exit(1)
402     
403     addr = int(float.fromhex(sys.argv[2]))
404     new_instr = int(float.fromhex(sys.argv[3]))
405     #new_instr_li = []
406     #for k in range(4):
407     #    new_instr_li.append( int(float.fromhex(sys.argv[k+3])) )
408
409     #new_words = instr2words( new_instr_li )
410         
411     wr_pm_li = [0x040200, # GOTO 0x0200
412                 0x040200, # GOTO 0x0200
413                 0x000000, # NOP
414
415                 0x24003A, # MOV $0x4003, W10
416                 0x883B0A, # MOV W10, NVMCON
417                 0x200000 + ((addr>>12)&0xff0),  # MOV #addr<23:16>, W0
418                 0x880190, # MOV W0, TBLPAG
419                 0x200007 + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
420
421                 # Here we load the instruction into registers W0:W1
422                 0x200000 + ((new_instr&0xffff)<<4),
423                 0x200001 + ((new_instr>>12)&0xff0),
424                 #0x200000 + ((new_words[0]&0xffff)<<4),
425                 #0x200001 + ((new_words[1]&0xffff)<<4),
426                 #0x200002 + ((new_words[2]&0xffff)<<4), 
427                 #0x200003 + ((new_words[3]&0xffff)<<4),
428                 #0x200004 + ((new_words[4]&0xffff)<<4),
429                 #0x200005 + ((new_words[5]&0xffff)<<4),
430
431                 0xEB0300, # CLR W6
432                 0x000000, # NOP
433                 0xBB0BB6, # TBLWTL [W6++], [W7]
434                 0x000000, # NOP
435                 0x000000, # NOP
436                 0xBB8B96, # TBLWTH [W6], [W7]
437                 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
438                 0x000000, # NOP
439                 0x000000, # NOP
440                 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
441                 #0x000000, # NOP
442                 #0x000000, # NOP
443                 #0xBB1BB6, # TBLWTL [W6++], [W7++]
444                 #0x000000, # NOP
445                 #0x000000, # NOP
446                 #0xBB0BB6, # TBLWTL [W6++], [W7]
447                 #0x000000, # NOP
448                 #0x000000, # NOP
449                 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
450                 #0x000000, # NOP
451                 #0x000000, # NOP
452                 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
453                 #0x000000, # NOP
454                 #0x000000, # NOP
455                 #0xBB1BB6, # TBLWTL [W6++], [W7++]
456                 #0x000000, # NOP
457                 #0x000000, # NOP
458                 
459                 0xA8E761, # BSET NVMCON, #WR
460                 0x000000, # NOP
461                 0x000000, # NOP
462                 0x000000, # NOP
463                 0x000000] # NOP
464     
465     startICSP()
466
467     runlist( wr_pm_li )
468     status = readNVMCON()
469     while status & 0x8000:
470         client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
471         status = readNVMCON()
472
473     stopICSP()
474
475 elif sys.argv[1] == "write_config":
476     if len(sys.argv) < 3:
477         print "Error: please specify the target config register."
478         exit(1)
479     elif len(sys.argv) == 3:
480         new_val = 0xFF # Assume new value
481     else:
482         new_val = int(float.fromhex(sys.argv[3]))
483     if sys.argv[2] not in cfg_table.values():
484         print "Given register, %s, not recognized." % sys.argv[2]
485         exit(1)
486     reg_addr = cfg_table.keys()[cfg_table.values().index(sys.argv[2])]
487     
488     wr_cfg_li = [0x040200, # GOTO 0x0200
489                  0x040200, # GOTO 0x0200
490                  0x000000, # NOP
491                  0x200007 + ((reg_addr&0xffff)<<4), # MOV #addr<15:0>, W7
492                  0x24000A, # MOV #0x4000, W10
493                  0x883B0A, # MOV W10, NVMCON
494                  0x200F80, # MOV #0xF8, W0
495                  0x880190, # MOV W0, TBLPAG
496                  0x200000 + ((new_val&0xffff)<<4), # MOV #<new_val>, W0
497                  0xBB0B80, # TBLWTL W0, [W7]
498                  0x000000, # NOP
499                  0x000000, # NOP
500                  0xA8E761, # BSET NVMCON, #WR
501                  0x000000, # NOP
502                  0x000000, # NOP
503                  0x000000, # NOP
504                  0x000000] # NOP
505
506     startICSP()
507
508     runlist( wr_cfg_li )
509     status = readNVMCON()
510     while status & 0x8000:
511         client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
512         status = readNVMCON()
513
514     stopICSP()
515         
516
517 elif sys.argv[1] == "read": # Read an address of program memory
518     startICSP()
519
520     addr = int(float.fromhex(sys.argv[2]))
521
522     readpm_cmd_li = [0x040200,                         # GOTO 0x0200
523                      0x040200,                         # GOTO 0x0200
524                      0x000000,                         # NOP
525                      0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
526                      0x880190,                         # MOV W0, TBLPAG
527                      0x200006+((addr & 0xffff)<<4),    # MOV #addr<15:0>, W6
528                      0xEB0380,                         # CLR W7
529                      0xEB0080,                         # CLR W1
530                      0x000000,                         # NOP
531                      0xBA1B96,                         # TBLRDL [W6], [W7++]
532                      0x000000,                         # NOP
533                      0x000000,                         # NOP
534                      0xBACB96,                         # TBLRDH.B [W6], [W7]
535                      0x000000,                         # NOP
536                      0x000000]                         # NOP
537                      
538     runlist( readpm_cmd_li )
539     loww = readreg(0)  # Read W0, which has lower 16 bits
540     highb = readreg(1) # Read W1, which has high 8 bits
541     result = (highb<<16) | loww
542     
543     print "0x%06X: 0x%06X" % ( addr, result )
544
545     stopICSP()
546
547 elif sys.argv[1] == "dump": # Read section of program memory
548     if len(sys.argv) < 3:
549         print "Error: please specify file in which to dump program memory."
550         exit(1)
551     fname = sys.argv[2]
552
553     if len(sys.argv) == 6:
554         print_term = True
555     else:
556         print_term = False
557         
558     if len(sys.argv) > 4:
559         start_addr = int(float.fromhex(sys.argv[3]))
560         stop_addr  = int(float.fromhex(sys.argv[4]))
561     else:
562         # Assume all of program memory (including unimplemented sections,
563         # which will be read as zeroes.
564         start_addr = 0x0
565         stop_addr = 0xFFFFFE #
566
567     readpm_cmd_li = [0x200000,                         # MOV #addr<23:16>, W0
568                      0x880190,                         # MOV W0, TBLPAG
569                      0x200006,                         # MOV #addr<15:0>, W6
570                      0xEB0380,                         # CLR W7
571                      0xEB0080,                         # CLR W1
572                      0x000000,                         # NOP
573                      0xBA1B96,                         # TBLRDL [W6], [W7++]
574                      0x000000,                         # NOP
575                      0x000000,                         # NOP
576                      0xBACB96,                         # TBLRDH.B [W6], [W7]
577                      0x000000,                         # NOP
578                      0x000000]                         # NOP
579
580     dumphex = IntelHex()
581
582     startICSP()
583
584     print "Reading program memory 0x%06X:0x%06X ..." % ( start_addr, stop_addr )
585     # Prep device
586     client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
587     client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
588     client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) #NOP (pump clock)
589     for addr in range( start_addr, stop_addr+2, 2 ):
590         specify_addr_cmd = [readpm_cmd_li[0] + ((addr & 0xff0000)>>12),
591                             readpm_cmd_li[1],
592                             readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
593         runlist( specify_addr_cmd + readpm_cmd_li[3:] )
594         loww = readreg(0)  # Read W0, which has lower 16 bits
595         highb = readreg(1) # Read W1, which has high 8 bits
596         result = (highb<<16) | loww
597         dumphex.puts( (addr&0xffffff)*2,
598                       chr(result&0xff)+chr((result>>8)&0xff)+chr((result>>16)&0xff)+chr(0) )
599         if print_term:
600             print "0x%06X    0x%06X" % (addr,result)
601     
602     if not print_term:
603         if fname == "-":
604             dumphex.tofile( sys.stdout, format="hex" )
605         else:
606             dumphex.tofile( fname, format="hex" )
607
608     stopICSP()
609
610 elif sys.argv[1] == "erase": # Bulk erase (all program memory)
611
612     if len(sys.argv) == 3:
613         addr = int(float.fromhex(sys.argv[2]))
614     else:
615         addr = None
616
617     if addr is None: # Bulk erase (all of program memory)
618         erase_cmd_li = [0x040200, # GOTO 0x0200
619                         0x040200, # GOTO 0x0200
620                         0x000000, # NOP
621                         0x2404FA, # MOV $0x404F, W10
622                         0x883B0A, # MOV W10, NVMCON
623                         0xA8E761, # BSET NVMCON, #WR
624                         0x000000, # NOP
625                         0x000000, # NOP
626                         0x000000, # NOP
627                         0x000000] # NOP
628     else: # Page erase
629         erase_cmd_li = [0x040200, # GOTO 0x0200
630                         0x040200, # GOTO 0x0200
631                         0x000000, # NOP
632                         0x24042A, # MOV $0x4042, W10
633                         0x883B0A, # MOV W10, NVMCON
634                         0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
635                         0x880190, # MOV W0, TBLPAG
636                         0x200001+((addr&0xffff)<<4), # MOV 0x0, W1
637                         0x000000, # NOP
638                         0xBB0881, # TBLTWL W1, [W1]
639                         0x000000, # NOP
640                         0x000000, # NOP
641                         0xA8E761, # BSET NVMCON, #WR
642                         0x000000, # NOP
643                         0x000000, # NOP
644                         0x000000, # NOP
645                         0x000000] # NOP
646         # Note that page alignment (effectively, bitmask of 0xffff80
647         # applied to given program memory address) seems to be
648         # enforced by hardware.
649
650     startICSP()
651
652     runlist( erase_cmd_li )
653     status = readNVMCON()
654     while status & 0x8000:
655         client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
656         status = readNVMCON()
657
658     stopICSP()
659
660 elif sys.argv[1] == "six": #0x82
661     startICSP()
662     
663     if len(sys.argv) < 3:
664         instr = 0x000000 # Assume nop
665     else:
666         instr = int(float.fromhex(sys.argv[2]))
667     print "Loading 0x%06X for execution..." % instr
668     data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
669                                        (instr >> 8) & 0xff,
670                                        (instr >> 16) & 0xff] )
671     print "Executing (by loading nop; pumping clock)..."
672     data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
673
674     dumpVISI()
675
676     stopICSP()
677
678 elif sys.argv[1] == "sixfile": #0x82 (repeated for each instruction in file)
679     startICSP()
680
681     if len(sys.argv) < 3:
682         print "Warning: no file given; trying cmd.txt..."
683         fname = "cmd.txt"
684     else:
685         fname = sys.argv[2]
686
687     try:
688         instrfile = open( fname, "r" )
689     except:
690         print "Failed to open file %s for reading." % fname
691         exit(-1)
692
693     try:
694         print "Opened file %s. Consecutively reading commmands..." % fname
695         for line in instrfile:
696             words = line.split()
697             if len(words) > 0:
698                 try:
699                     instr = int(float.fromhex(words[0]))
700                 except ValueError:
701                     print "Warning: invalid instruction found at offset %d." % instrfile.tell()
702                     continue
703                 print "Loading 0x%06X for execution..." % instr
704                 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
705                                                    (instr >> 8) & 0xff,
706                                                    (instr >> 16) & 0xff] )
707             #else:
708             #    print "Warning: empty line found at offset %d." % instrfile.tell()
709     except:
710         print "Error: exception caught while reading file %s." % fname
711         instrfile.close()
712         stopICSP()
713         exit(-1)
714
715     instrfile.close()
716     
717     print "Loading nop, pumping clock (to finish execution)..."
718     data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
719     
720     dumpVISI()
721
722     stopICSP()
723
724 elif sys.argv[1] == "regout": #0x83
725     startICSP()
726
727     print "Reading VISI register..."
728     data = client.writecmd( PICAPP, 0x83, 0 )
729     result = ord(data[0])
730     result |= ord(data[1]) << 8
731     print "VISI: 0x%04X" % result
732
733     stopICSP()
734
735 else:
736     print "Unrecognized verb: %s" % sys.argv[1]
737     sys.exit(-1)
738