[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