Keil debug command SAVE 命令保存文件的解析
简介
使用 Keil debug 很方便,把内存中的一段区域 dump 出来也很方便,例如使用命令 SAVE filepath startAddr, endAddr, typeCode 。但是要查看 dump 出来的内容却很不方便。因为 dump 出来的格式是 INTEL hex386 格式的,这个格式是给机器读的而不是给人读的。
例如下面是一个完成的 INTEL HEX386 格式的文件,你能看懂是什么意思吗?
:10001300AC12AD13AE10AF1112002F8E0E8F0F2244
:10000300E50B250DF509E50A350CF5081200132259
:03000000020023D8
:0C002300787FE4F6D8FD7581130200031D
:10002F00EFF88DF0A4FFEDC5F0CEA42EFEEC88F016
:04003F00A42EFE22CB
:00000001FF
把 hex386 格式的文件翻译成人能看到的格式是很有必要的,我参考 keil 官方资料 编写了一个 Python 脚本,输入的文件必须是 SAVE filepath startAddr, endAddr, 0x04 产生出来的文件,其中的 typeCode=0x04 表示目标地址时 32bit 对齐的。
脚本所做的事情就是读取输入文件(例如 abc.hex)处理之后把寄存器的地址-值对写到输出文件(和输入文件名相同,后缀名为 txt)。
Python 脚本
脚本内容如下:
# -*- coding: utf-8 -*-
"""
Created on Thu Jul 18 09:46:05 2019
@author: LinTeX9527
@version: v0.1-20190718
@desc: convert HEX386 file to txt file.
"""
import os.path
class RegAddrCls:
    """
    an entity represents register addr-value pair.
    """
    def __init__(self, addr, value):
        #super(RegAddrCls, self).__init()
        # default value
        self.__addr = 0
        self.__value = 0
        self.__valid = False
        self.regAddr = addr
        self.regValue = value
    @property
    def regAddr(self):
        return self.__addr
    @regAddr.setter
    def regAddr(self, addr):
        if isinstance(addr, int):
            self.__addr = addr
        else:
            raise TypeError("@addr must be a number")
    @property
    def regValue(self):
        return self.__value
    @regValue.setter
    def regValue(self, value):
        if isinstance(value, int):
            self.__value = value
            self.isValid = True
        else:
             raise TypeError("@value must be a number")
    @property
    def isValid(self):
        return self.__valid
    @isValid.setter
    def isValid(self, valid):
        if isinstance(valid, bool):
            self.__valid = valid
        else:
            raise TypeError("@valid must be True or False")
def dumpRegArray(regArray):
    if isinstance(regArray, list):
        index = 0
        while index < len(regArray):
            item = regArray[index]
            if isinstance(item, RegAddrCls):
                if item.isValid:
                    print("0x{_addr:08x} - 0x{_value:08x}".format(_addr=item.regAddr, _value=item.regValue))
            index += 1
    else:
        print("@regArray is not list type.")
# tt for HEX data types.
TT_00_DATA_RECORD = 0x00
TT_01_EOF = 0x01
TT_02_EX_SEG_ADDR_RECORD = 0x02
TT_04_EX_LINEAR_ADDR_RECORD = 0x04
TT_05_START_LINEAR_ADDR_RECORD = 0x05
gBaseRegAddr = 0
def processLine(lineData):
    global gBaseRegAddr
    # return this array when data contains valid register addr-value pairs.
    regArray = []
    # number of data bytes in the record
    ll = 0
    # starting address
    aaaa = 0
    # HEX record type.
    tt = 00
    # data bytes
    dd = []
    # @cc is the checksum read from the file;
    # @checksum is checksum we calibrate the record
    cc = 0
    checkSum = 0
    #print(lineData)
    #print(lineData[2:6])
    ll = int(lineData[0:2], base=16)
    #print("ll = 0x{_ll:x}".format(_ll=ll))
    aaaa = int(lineData[2:6], base=16)
    aaaa_a = int(lineData[2:4], base=16)
    aaaa_b = int(lineData[4:6], base=16)
    #print("aaaa = 0x{_aaaa:x}".format(_aaaa=aaaa))
    tt = int(lineData[6:8], base=16)
    #print("tt = 0x{_tt:x}".format(_tt=tt))
    index = 0
    while (index < ll*2):
        item = int(lineData[8+index:(8+index+2)], base=16)
        dd.append(item)
        index += 2
    #print("dataBytes: {_dd}".format(_dd=dd))
    cc = int(lineData[8+index:(8+index+2)], base=16)
    #print("cc = {_cc:x}".format(_cc=cc))
    #                          ll + aaaa + tt ++ all_data
    checkSum = 0x01 + 0xFF - ( (ll + (aaaa_a + aaaa_b) + tt + sum(dd)) & 0xFF )
    # checkSum should only be 0~255.
    checkSum = checkSum % 256
    #print("checkSum = {_csum:X}".format(_csum=checkSum))
    if cc != checkSum:
        print("CC error for this line data:\ncc = {_cc}\ncheckSum = {_chsum}\n{_dd}".format(_cc=cc, _chsum=checkSum, _dd=lineData))
        return None
    ###########################  check tt  ####################################
    if tt == TT_01_EOF:
        #print("EOF, no data available.")
        return None
    elif tt == TT_00_DATA_RECORD:
        #print("TT_00_DATA_RECORD")
        index = 0
        # register is 4 bytes aligned.
        regNum = int(ll / 4) * 4
        while index < regNum:
            #print("index = {_index}".format(_index = index))
            # calculate register address.
            itemAddr = gBaseRegAddr + aaaa + index
            # get register value.
            itemValue = dd[index] + (dd[index+1] << 8)+ (dd[index+2] << 16) + (dd[index+3] << 24)
            #regObj = RegAddrCls()
            #regObj.regAddr = itemAddr
            #regObj.regValue = itemValue
            regArray.append(RegAddrCls(itemAddr, itemValue))
            index += 4
        #dumpRegArray(regArray)
        return regArray
    elif tt == TT_02_EX_SEG_ADDR_RECORD:
        #print("TT_02_EX_SEG_ADDR_RECORD")
        segAddr = (dd[0] << 12) + (dd[1] << 4)
        print("segAddr = 0x{_seg:08x}".format(_seg=segAddr))
        gBaseRegAddr += segAddr
        print("gBaseRegAddr = 0x{_g:x}".format(_g=gBaseRegAddr))
        return None
    elif tt == TT_04_EX_LINEAR_ADDR_RECORD:
        #print("TT_04_EX_LINEAR_ADDR_RECORD")
        if ll != 2:
            print("it must contain 2 data bytes for upper 16 bits address.")
            return None
        # update global register base address.
        upAddr = (dd[0] << 8) + dd[1];
        gBaseRegAddr = (upAddr << 16) + aaaa;
        #print("gBaseRegAddr = 0x{_g:x}".format(_g=gBaseRegAddr))
    elif tt == TT_05_START_LINEAR_ADDR_RECORD:
        print("TT_05_START_LINEAR_ADDR_RECORD")
        if ll != 4:
            print("it must contain 4 data bytes for start address of the application.")
            return None
        # TODO: do nothing, we just ignore this type.
        return None
    else:
        #print("TT--Unkown")
        return None
def fileHex2Txt(filepath):
    """
    @filepath input file must be HEX format, which is generated from Keil debug
              command: "SAVE filepath start_addr, end_addr, 0x4"
    """
    if not isinstance(filepath, str):
        print("@filepath must be subclass of str.")
        return
    regSet = []
    with open(filepath, 'rt') as fileObj:
        while True:
            line = fileObj.readline()
            if len(line):
                # remove ':'
                line = line.replace(':', '').strip()
                tempSet = processLine(line)
                if tempSet != None:
                    regSet += tempSet
            else:
                break
    inputAbsPath = os.path.abspath(filepath)
    outputFilepath = inputAbsPath.split('.')[0] + '.txt'
    print(" input filepath = {_in}".format(_in=inputAbsPath))
    print("output filepath = {_out}".format(_out=outputFilepath))
    #dumpRegArray(regSet)
    with open(outputFilepath, 'wt') as fileObj:
        fileObj.write("  address ---  value\n")
        index = 0
        while index < len(regSet):
            addr = regSet[index].regAddr
            val = regSet[index].regValue
            fileObj.write("0x{_addr:08x} - 0x{_val:08x}\n".format(_addr=addr, _val=val))
            index += 1    
def convertHex386ToTxt(filepath):
    # check file suffix
    suffix = os.path.basename(filepath).split('.')[1]
    if suffix != 'hex':
        print("file must have 'hex' suffix")
        return
    # check if file exists.
    hex_file_path = os.path.abspath(filepath)
    if os.path.exists(hex_file_path):
        fileHex2Txt(hex_file_path)
    else:
        print("file does NOT exist: {_f}".format(_f=hex_file_path))
def main():
    print("------  main()  start  ------")
    convertHex386ToTxt("demo.hex")
    print("------  main()   over  ------")
if __name__ == "__main__":
    main()
参考资料
声明
欢迎转载,请注明出处和作者,同时保留声明。
作者:LinTeX9527
出处:https://www.cnblogs.com/LinTeX9527/p/11233421.html
本博客的文章如无特殊说明,均为原创,转载请注明出处。如未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利
Keil debug command SAVE 命令保存文件的解析的更多相关文章
- Keil综合(03)_map文件全解析[转]
		推荐分享一个大神的人工智能教程.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到人工智能的队伍中来!http://www.captainbed.net/strongerhuang 我的网站:ht ... 
- 在linux下通过hexdump生成一个十六进制的文本保存文件,解析此文件转变成正常源代码文件。
		举例说明: 此十六进制保存的文件为此源代码hexdump生成的: #include<stdio.h> #include<string.h> #include<stdlib ... 
- 一个简单的wed服务器SHTTPD(1)————命令行和文件配置解析
		开始学习<LInux网络编程>中的综合案例,虽然代码书上有,还是自己打一下加深理解和印象. 主要有两个函数,完成命令行的解析,另一个实现配置文件的解析,注释还是比较丰富的哦. //star ... 
- Vim保存文件命令 ":wq" 与 ":x" 的区别
		CSDN转载 [1] Vim是Unix/Linux系统最常用的编辑器之一,在保存文件时,我通常选择":wq",因为最开始学习vim的时候,就只记住了几个常用的命令:也没有细究命令的 ... 
- [No0000192]Vim打开和保存文件-Vim使用技巧(7)
		使用Vim打开和保存文件是最常用的操作,介绍使用edit命令通过文件路径来打开文件,使用write命令保存文件,当文件路径不存在或用户权限不匹配时,使用write命令调用外部shell程序完成操作. ... 
- linux:终端常用命令  +  vi命令修改文件及保存   方法
		首先介绍一下Ubuntu下各个目录的一般作用: / 这就是根目录,一台电脑有且只有一个根目录,所有的文件都是从这里开始的.举个例子:当你在终端里输入“/home”,你其实是在告诉电脑,先从/(根目录 ... 
- vi命令修改文件及保存的使用方法
		简单点:vi文件名,按"I"进入insert模式,可以正常文本编辑,编辑好之后按“esc”退出到“命令模式”,再按“shift+:”进入“底行模式”, 按“:wq”保存退出! 还一 ... 
- linux下vi命令修改文件及保存的使用方法
		进入vi的命令 vi filename :打开或新建文件,并将光标置于第一行首 vi n filename :打开文件,并将光标置于第n行首 vi filename :打开文件,并将光标置于一行首 v ... 
- Docker commit 命令保存的镜像文件太大的问题
		基础镜像: centos7.5 进入容器后, 先后安装了 jdk1.8, maven3.6.0, git, rocketmq4.3.2 安装完成后使用 docker commit 命令保存为镜像 结果 ... 
随机推荐
- Qt的模态对话框和非模态对话框 经常使用setAttribute (Qt::WA_DeleteOnClose)
			模态对话框就是指在子对话框弹出时,焦点被强行集中于该子对话框,子对话框不关闭,用户将无法操作其他的窗口.非模态相反,用户仍然可以操作其他的窗口,包括该子对话框的父对话框. 如果从线程角度来讲,模态对话 ... 
- 关于客户端javascript的理解及事件浅析
			1,核心JavaScript和客服端JavaScript都有一个单线程执行模型.脚本和事件处理程序在同一时间只能执行一个,没有并发性.这样保持了js编程的简单性. 2,document的定义:一些呈现 ... 
- java集合框架collection(2)ArrayList和LinkedList
			ArrayList是基于动态数组实现的list,而LinkedList是基于链表实现的list.所以,ArrayList拥有着数组的特性,LinkedList拥有着链表的特性. 优缺点 ArrayLi ... 
- Android native进程间通信实例-binder篇之——简单的单工通信
			网上找了很多binder相关文章,大部分都是在跟踪binder实现源代码,然后再把框架代码贴出来,看着实在费力. 这篇文章从实际出发,直接用一个案例下手,后续想了解binder相关原理的话,可以参考& ... 
- DNS之主服务器正向区域部署流程
			正向区域:将域名解析为IP 搭建步骤 1)定义区域 2)编写区域解析库文件 3)添加记录 环境介绍 [root@dns ~]# cat /etc/centos-releaseCentOS releas ... 
- Netty源码分析--Channel注册(中)(六)
			接上一篇,我们继续看 不知道大家第一次看这段代码的时候有没有一脸懵逼,反正我是一脸懵,为什么这个if else 最终都是调用的register0方法,都是一样的. 其实这里就是为什么Netty是线程安 ... 
- 手动安装gitlab-runner
			手动安装gitlab-runner 在终端使用命令curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runn ... 
- 基于STM32之UART串口通信协议(一)详解
			一.前言 1.简介 写的这篇博客,是为了简单讲解一下UART通信协议,以及UART能够实现的一些功能,还有有关使用STM32CubeMX来配置芯片的一些操作,在后面我会以我使用的STM32F429开发 ... 
- Failed to start Docker Application Container Engine.
			[root@dockertest ~]# systemctl status docker.service● docker.service - Docker Application Container ... 
- RT-thread线程创建:动态线程与静态线程
			本文介绍了如何创建一个动态线程和一个静态线程 RT-thread版本:RT-thread system 3.1.0 开发环境:MDK5 为了编程方便,创建了sample1.c文件,然后添加到工程中 话 ... 
