You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
278 lines
11 KiB
278 lines
11 KiB
#!/usr/bin/env python |
|
# $Id: memory.py,v 1.4.2.1 2009/05/19 09:07:21 rlim Exp $ |
|
import sys |
|
import elf |
|
|
|
DEBUG = 0 |
|
|
|
class FileFormatError(IOError): |
|
"""file is not in the expected format""" |
|
|
|
|
|
class Segment: |
|
"""store a string with memory contents along with its startaddress""" |
|
def __init__(self, startaddress = 0, data=None): |
|
if data is None: |
|
self.data = '' |
|
else: |
|
self.data = data |
|
self.startaddress = startaddress |
|
|
|
def __getitem__(self, index): |
|
return self.data[index] |
|
|
|
def __len__(self): |
|
return len(self.data) |
|
|
|
def __repr__(self): |
|
return "Segment(startaddress=0x%04x, data=%r)" % (self.startaddress, self.data) |
|
|
|
class Memory: |
|
"""represent memory contents. with functions to load files""" |
|
def __init__(self, filename=None): |
|
self.segments = [] |
|
if filename: |
|
self.filename = filename |
|
self.loadFile(filename) |
|
|
|
def append(self, seg): |
|
self.segments.append(seg) |
|
|
|
def __getitem__(self, index): |
|
return self.segments[index] |
|
|
|
def __len__(self): |
|
return len(self.segments) |
|
|
|
def __repr__(self): |
|
return "Memory:\n%s" % ('\n'.join([repr(seg) for seg in self.segments]),) |
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
|
|
def loadIHex(self, file): |
|
"""load data from a (opened) file in Intel-HEX format""" |
|
segmentdata = [] |
|
currentAddr = 0 |
|
startAddr = 0 |
|
extendAddr = 0 |
|
lines = file.readlines() |
|
for l in lines: |
|
if not l.strip(): continue #skip empty lines |
|
if l[0] != ':': raise FileFormatError("line not valid intel hex data: '%s...'" % l[0:10]) |
|
l = l.strip() #fix CR-LF issues... |
|
length = int(l[1:3],16) |
|
address = int(l[3:7],16) + extendAddr |
|
type = int(l[7:9],16) |
|
check = int(l[-2:],16) |
|
if type == 0x00: |
|
if currentAddr != address: |
|
if segmentdata: |
|
self.segments.append( Segment(startAddr, ''.join(segmentdata)) ) |
|
startAddr = currentAddr = address |
|
segmentdata = [] |
|
for i in range(length): |
|
segmentdata.append( chr(int(l[9+2*i:11+2*i],16)) ) |
|
currentAddr = length + currentAddr |
|
elif type == 0x02: |
|
extendAddr = int(l[9:13],16) << 4 |
|
elif type in (0x01, 0x03, 0x04, 0x05): |
|
pass |
|
else: |
|
sys.stderr.write("Ignored unknown field (type 0x%02x) in ihex file.\n" % type) |
|
if segmentdata: |
|
self.segments.append( Segment(startAddr, ''.join(segmentdata)) ) |
|
|
|
def loadTIText(self, file): |
|
"""load data from a (opened) file in TI-Text format""" |
|
startAddr = 0 |
|
segmentdata = [] |
|
#Convert data for MSP430, TXT-File is parsed line by line |
|
for line in file: #Read one line |
|
if not line: break #EOF |
|
l = line.strip() |
|
if l[0] == 'q': break |
|
elif l[0] == '@': #if @ => new address => send frame and set new addr. |
|
#create a new segment |
|
if segmentdata: |
|
self.segments.append( Segment(startAddr, ''.join(segmentdata)) ) |
|
startAddr = int(l[1:],16) |
|
segmentdata = [] |
|
else: |
|
for i in l.split(): |
|
try: |
|
segmentdata.append(chr(int(i,16))) |
|
except ValueError, e: |
|
raise FileFormatError('File is no valid TI-Text (%s)' % e) |
|
if segmentdata: |
|
self.segments.append( Segment(startAddr, ''.join(segmentdata)) ) |
|
|
|
def loadELF(self, file): |
|
"""load data from a (opened) file in ELF object format. |
|
File must be seekable""" |
|
obj = elf.ELFObject() |
|
obj.fromFile(file) |
|
if obj.e_type != elf.ELFObject.ET_EXEC: |
|
raise Exception("No executable") |
|
for section in obj.getSections(): |
|
if DEBUG: |
|
sys.stderr.write("ELF section %s at 0x%04x %d bytes\n" % (section.name, section.lma, len(section.data))) |
|
if len(section.data): |
|
self.segments.append( Segment(section.lma, section.data) ) |
|
|
|
def loadFile(self, filename, fileobj=None): |
|
"""fill memory with the contents of a file. file type is determined from extension""" |
|
close = 0 |
|
if fileobj is None: |
|
fileobj = open(filename, "rb") |
|
close = 1 |
|
try: |
|
#first check extension |
|
try: |
|
if filename[-4:].lower() == '.txt': |
|
self.loadTIText(fileobj) |
|
return |
|
elif filename[-4:].lower() in ('.a43', '.hex'): |
|
self.loadIHex(fileobj) |
|
return |
|
except FileFormatError: |
|
pass #do contents based detection below |
|
#then do a contents based detection |
|
try: |
|
self.loadELF(fileobj) |
|
except elf.ELFException: |
|
fileobj.seek(0) |
|
try: |
|
self.loadIHex(fileobj) |
|
except FileFormatError: |
|
fileobj.seek(0) |
|
try: |
|
self.loadTIText(fileobj) |
|
except FileFormatError: |
|
raise FileFormatError('file could not be loaded (not ELF, Intel-Hex, or TI-Text)') |
|
finally: |
|
if close: |
|
fileobj.close() |
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
def saveIHex(self, filelike): |
|
"""write a string containing intel hex to given file object""" |
|
noeof=0 |
|
for seg in self.segments: |
|
address = seg.startaddress |
|
data = seg.data |
|
start = 0 |
|
while start<len(data): |
|
end = start + 16 |
|
if end > len(data): end = len(data) |
|
filelike.write(self._ihexline(address, data[start:end])) |
|
start += 16 |
|
address += 16 |
|
filelike.write(self._ihexline(0, [], end=1)) #append no data but an end line |
|
|
|
def _ihexline(self, address, buffer, end=0): |
|
"""internal use: generate a line with intel hex encoded data""" |
|
out = [] |
|
if end: |
|
type = 1 |
|
else: |
|
type = 0 |
|
out.append( ':%02X%04X%02X' % (len(buffer),address&0xffff,type) ) |
|
sum = len(buffer) + ((address>>8)&255) + (address&255) + (type&255) |
|
for b in [ord(x) for x in buffer]: |
|
out.append('%02X' % (b&255) ) |
|
sum += b&255 |
|
out.append('%02X\r\n' %( (-sum)&255)) |
|
return ''.join(out) |
|
|
|
def saveTIText(self, filelike): |
|
"""output TI-Text to given file object""" |
|
for segment in self.segments: |
|
filelike.write("@%04x\n" % segment.startaddress) |
|
for i in range(0, len(segment.data), 16): |
|
filelike.write("%s\n" % " ".join(["%02x" % ord(x) for x in segment.data[i:i+16]])) |
|
filelike.write("q\n") |
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
|
|
def getMemrange(self, fromadr, toadr): |
|
"""get a range of bytes from the memory. unavailable values are filled with 0xff.""" |
|
res = '' |
|
toadr = toadr + 1 #python indexes are excluding end, so include it |
|
while fromadr < toadr: |
|
for seg in self.segments: |
|
segend = seg.startaddress + len(seg.data) |
|
if seg.startaddress <= fromadr and fromadr < segend: |
|
if toadr > segend: #not all data in segment |
|
catchlength = segend - fromadr |
|
else: |
|
catchlength = toadr - fromadr |
|
res = res + seg.data[fromadr-seg.startaddress : fromadr-seg.startaddress+catchlength] |
|
fromadr = fromadr + catchlength #adjust start |
|
if len(res) >= toadr-fromadr: |
|
break #return res |
|
else: #undefined memory is filled with 0xff |
|
res = res + chr(255) |
|
fromadr = fromadr + 1 #adjust start |
|
return res |
|
|
|
def getMem(self, address, size): |
|
"""get a range of bytes from the memory. a ValueError is raised if |
|
unavailable addresses are tried to read""" |
|
data = [] |
|
for seg in self.segments: |
|
#~ print "0x%04x " * 2 % (seg.startaddress, seg.startaddress + len(seg.data)) |
|
if seg.startaddress <= address and seg.startaddress + len(seg.data) >= address: |
|
#segment contains data in the address range |
|
offset = address - seg.startaddress |
|
length = min(len(seg.data)-offset, size) |
|
data.append(seg.data[offset:offset+length]) |
|
address += length |
|
value = ''.join(data) |
|
if len(value) != size: |
|
raise ValueError("could not collect the requested data") |
|
return value |
|
|
|
def setMem(self, address, contents): |
|
"""write a range of bytes to the memory. a segment covering the address |
|
range to be written has to be existent. a ValueError is raised if not |
|
all data could be written (attention: a part of the data may have been |
|
written!)""" |
|
#~ print "%04x: %r" % (address, contents) |
|
for seg in self.segments: |
|
#~ print "0x%04x " * 3 % (address, seg.startaddress, seg.startaddress + len(seg.data)) |
|
if seg.startaddress <= address and seg.startaddress + len(seg.data) >= address: |
|
#segment contains data in the address range |
|
offset = address - seg.startaddress |
|
length = min(len(seg.data)-offset, len(contents)) |
|
seg.data = seg.data[:offset] + contents[:length] + seg.data[offset+length:] |
|
contents = contents[length:] #cut away what is used |
|
if not contents: return #stop if done |
|
address += length |
|
raise ValueError("could not write all data") |
|
|
|
if __name__ == "__main__": |
|
from optparse import OptionParser |
|
parser = OptionParser() |
|
parser.add_option("-i", "--input", dest="input", |
|
help="read file") |
|
parser.add_option("-o", "--output", |
|
dest="output", help="output file") |
|
parser.add_option("-f", "--format", |
|
dest="format", help="output format [titxt,ihex]", |
|
choices=["titxt", "ihex"], |
|
default="titxt") |
|
|
|
(options, args) = parser.parse_args() |
|
|
|
if not options.output or not options.input: |
|
parser.error("input and output are required") |
|
|
|
mem = Memory() |
|
mem.loadFile(options.input) |
|
fp = open(options.output, "w") |
|
if options.format == "titxt": |
|
print "convert to TI Hex" |
|
mem.saveTIText(fp) |
|
elif options.format == "ihex": |
|
print "convert to Intel Hex" |
|
mem.saveIHex(fp) |
|
fp.close()
|
|
|