[PicForth] A smarter disassembly utility

David McNab david at rebirthing.co.nz
Thu Oct 28 05:00:11 CEST 2004


Hi,

I've written a picforth utility called 'fdasm' (python script attached).

fdasm is a wrapper for the gputils 'gpdasm' disassembler, which parses a 
hex file disassembly and cross-references it against a 
picforth-generated .map file.

It then outputs a disassembly annotated with known symbols, much more 
intelligible than raw disassembly.

--------
Example

1. forth source

   false set-wdte false set-boden false set-lvp
   pic16f87x

   : f0 ;
   : f1 ;
   : f2 ;

   main : main
       f0
       begin
           f1 f2
       again
   ;

2. raw gpdasm disassembly (yuck)

   000000:  018a  clrf     0xa
   000001:  2808  goto     0x8
   000002:  0000  nop
   000003:  0000  nop
   000004:  0000  nop
   000005:  0008  return
   000006:  0008  return
   000007:  0008  return
   000008:  3032  movlw    0x32
   000009:  0084  movwf    0x4
   00000a:  2005  call     0x5
   00000b:  2006  call     0x6
   00000c:  2007  call     0x7
   00000d:  280b  goto     0xb
   00000e:  0000  nop
   00000f:  0000  nop
   002007:  3f22  addlw    0x22
   002008:  3fff  addlw    0xff

3. fdasm output (easier than constantly flicking between
    raw disassembly and map)

     000001:  2808  goto   (init-picforth)
     000002:  0000  nop
     000003:  0000  nop
     000004:  0000  nop

   f0:
     000005:  0008  return

   f1:
     000006:  0008  return

   f2:
     000007:  0008  return

   (init-picforth):
     000008:  3032  movlw  0x32
     000009:  0084  movwf  0x4

   main:
     00000a:  2005  call   f0
     00000b:  2006  call   f1
     00000c:  2007  call   f2
     00000d:  280b  goto   0xb
     00000e:  0000  nop
     00000f:  0000  nop
     002007:  3f22  addlw  0x22
     002008:  3fff  addlw  0xff


-- 
Cheers
David
-------------- next part --------------
#! /usr/bin/env python

"""
fdasm

Smart-ish disassembler for picforth programs.

Wraps gpdasm (from gputils).
Analyses the .map file.

Outputs a more meaningful disassembly by annotating
code addresses with known symbols, and converting
arguments of call/goto with symbols

If you're running on windows, please note that you'll have to rename
this file as fdasm.py, and ensure you have python 2.3 or later installed.

Written Oct 2004 by David McNab david at freenet dot org dot nz
Released into the public domain
"""

# set your processor type here
defaultProcessor = "pic16f873"

# --------------------------------------------------
# shouldn't need to worry about anything below

import sys, os, commands, re, getopt

reSpaces = re.compile("\\s+")

progname = sys.argv[0]

# mnemonics operating on registers
regops = ['addwf', 'andwf', 'clrf', 'comf', 'decf', 'decfsz', 'incf',
          'incfsz', 'iorwf', 'movf', 'movwf', 'rlf', 'rrf',
          'subwf', 'swapf', 'xorwf', 'bcf', 'bsf', 'btfsc',
          'btfss']

def usage():
    print "Usage: %s [-p processor] [-l] filename" % progname
    print
    print "A disassembly-smartener for picForth programs which"
    print "runs gpdasm on a picforth-compiled .hex file,"
    print "cross-references with the picforth-generated .map file,"
    print "and outputs an annotated disassembly"
    print
    print "Options:"
    print "  -h, --help                show this help"
    print "  -p, --processor=procname  select processor (default 'pic16f873')"
    print "  -l, --list-processors     show available processors"
    print
    print "Any suffix on the 'filename' argument is ignored. This utility"
    print "attempts to open 'filename.hex' and 'filename.map'"
    print
    sys.exit(1)

def main():

    # handle options
    try:
        opts, args = getopt.getopt(
            sys.argv[1:],
            "?hp:l",
            ["help", "processor=", "list-processors"])
    except getopt.GetoptError:
        # print help information and exit:
        usage()
        sys.exit(2)

    processor = defaultProcessor

    for o, a in opts:
        if o in ("-h", "-?", "--help"):
            usage()
        elif o in ('-p', '--processor'):
            processor = a
        elif o in ('-l', '--list-processors'):
            os.system("gpdasm -l")
            sys.exit(0)

    gpdasmCmd = "gpdasm -p %s %%s.hex" % processor

    # barf if not exactly 1 arg
    if len(args) != 1:
        usage()
    basename = os.path.splitext(args[0])[0]
    
    # check that .map and .hex files are available
    if not os.path.isfile(basename+".map"):
        print "Can't find %s.map" % basename
        usage()
    if not os.path.isfile(basename+".hex"):
        print "Can't find %s.hex" % basename
        usage()
    
    # get lines of disassembly
    disRaw = commands.getoutput(gpdasmCmd % basename).strip()
    disLines = [line.strip() for line in disRaw.split("\n")]

    # sanity check
    if len(disLines) < 2:
        print "Sorry, but we can't get a disassembly"
        print "Please check that:"
        print " 1. You have gputils installed on your system"
        print " 2. You have 'gpdasm' on your PATH"
        usage()

    disItems = []
    for line in disLines:
        flds = reSpaces.split(line) # break up disassembly into fields
        addr = int(flds[0][:-1], 16) # convert to numerical addr
        opcode = flds[1]             # take opcode as string, no need to process
        mnemonic = flds[2]
        if len(flds) > 3:
            arg1 = flds[3]
            if len(flds) == 5:
                arg1 = arg1[:-1] # remove comma
                arg2 = flds[4]
            else:
                arg2 = ''
        else:
            arg1 = ''
        # can add now
        disItems.append({'addr': addr, 'opcode' : opcode, 'mnemonic' : mnemonic,
                         'arg1': arg1, 'arg2': arg2})
    
    # build map as dictionary numerical addr->sym
    mapRaw = file(basename + ".map").read().strip()
    mapLines = [line.strip() for line in mapRaw.split("\n")]
    addr2sym = {}
    for line in mapLines:
        addr, crap, sym = reSpaces.split(line)
        addr = int(addr, 16)
        addr2sym[addr] = sym

    # can now dump out
    outLines = []
    for item in disItems:
        addr = item['addr']
        opcode = item['opcode']
        mnemonic = item['mnemonic']
        arg1 = item['arg1']
        arg2 = item['arg2']

        # pick up known addresses
        if addr2sym.has_key(addr):
            outLines.append('')
            outLines.append("%s:" % addr2sym[addr])

        # pick up goto/gosub to known address
        if mnemonic in ['goto', 'call']:
            target = int(arg1, 16)
            if addr2sym.has_key(target):
                # going to known address
                arg1 = addr2sym[target]
        
        # now can output line
        line = "  %06x:  %s  %-6s %s" % (addr, opcode, mnemonic, arg1)
        if arg2:
            line += ", %s" % arg2
        outLines.append(line)

    # join 'em up
    outRaw = "\n".join(outLines)
    
    print outRaw

if __name__ == '__main__':
    main()



More information about the PicForth mailing list