解开汇编语言意粉代码

bit*_*per 17 embedded assembly coding-style 8051

我继承了用8051汇编语言编写的10K行程序,需要进行一些更改.不幸的是,它是用最好的意大利面条代码传统写成的.程序 - 作为单个文件编写 - 是CALL和LJMP语句的迷宫(总共约1200个),子程序具有多个入口和/或出口点,如果它们可以被识别为子程序的话.所有变量都是全局的.有评论; 有些是正确的.没有现有的测试,也没有重构预算.

关于应用程序的一些背景知识:代码控制当前在国际上部署的自动售货应用程序中的通信中心.它同时处理两个串行流(在单独的通信处理器的帮助下),并且可以与最多四个不同的物理设备通信,每个物理设备来自不同的供应商.其中一个设备的制造商最近做了一个改变("是的,我们做了一个改变,但软件完全相同!")这导致一些系统配置不再起作用,并且不想改变它(无论它是什么他们没有改变).

该计划最初由另一家公司编写,转让给我的客户,然后由另一位顾问在九年前进行了修改.原始公司和顾问都不是资源.

基于对其中一条串行总线上的流量的分析,我想出了一个看似有效的黑客攻击,但它很难看并且没有解决根本原因.如果我对该计划有了更好的理解,我相信我可以解决实际问题.在代码被冻结之前,我还有大约一周的时间来支持月末发货日期.

原始问题:我需要很好地理解程序,以便在不破坏的情况下进行更改.有没有人开发过处理这种乱七八糟的技术?

我在这里看到一些很棒的建议,但受到时间的限制.但是,我将来可能有另一个机会去寻求一些更复杂的行动方案.

non*_*one 17

首先,我会尝试与那些最初开发代码或至少在我之前维护代码的人取得联系,希望获得足够的信息以便对代码进行基本的理解,以便您可以开始添加有用的注释它.

也许你甚至可以让某人为代码描述最重要的API(包括它们的签名,返回值和目的).如果函数修改了全局状态,那么这也应该是明确的.同样,开始区分函数和过程,以及输入/输出寄存器.

您应该向雇主明确说明这些信息是必需的,如果他们不相信您,让他们在您描述您应该做的事情以及您必须做什么的同时在这段代码前与您坐下来它(逆向工程).拥有具有计算和编程背景的雇主在这种情况下实际上会有所帮助!

如果您的雇主没有这样的技术背景,请他带另一位程序员/同事向他解释您的步骤,这样做实际上会向他表明您对此非常认真,因为这是一个真正的问题 - 而不仅仅是从你的角度来看(确保有同事了解这个'项目').

如果可行且可行的话,我也会非常明确地说,签约(或至少是联系)以前的开发人员/维护人员(如果他们不再为贵公司工作,那就是)帮助记录这些代码将是一个前期 - 在短时间内实际改进代码并确保将来可以更轻松地维护代码的必要条件.

强调整个情况是由于先前软件开发过程中的缺点,并且这些步骤将有助于改进代码库.因此,当前形式的代码库是一个日益严重的问题,现在无论如何处理这个问题都是对未来的投资.

这本身对于帮助他们评估和了解你的情况也很重要:做你现在应该做的事情远非微不足道,他们应该知道它 - 如果只是为了直接设定他们的期望(例如关于截止日期和复杂性任务).

另外,我个人会开始为那些我理解得很好的部分添加单元测试,以便我可以慢慢地开始重构/重写一些代码.

换句话说,良好的文档和源代码注释是一回事,但拥有一个全面的测试套件是另一个重要的事情,没有任何人可以在没有任何测试关键功能的既定方法的情况下修改不熟悉的代码库.

鉴于代码是10K,我还会考虑将子例程分解为单独的文件,以使组件更易于识别,最好使用访问包装而不是全局变量以及直观的文件名.

此外,我会研究通过降低复杂性来进一步提高源代码的可读性的步骤,具有多个入口点的子例程(甚至可能是不同的参数签名?)看起来像是不必要地混淆代码的可靠方法.

同样,巨大的子程序也可以重构为较小的子程序,以帮助提高可读性.

因此,我要做的第一件事就是确定那些使得代码库变得非常复杂然后重新编写这些部分的东西,例如将具有多个入口点的巨大子例程拆分为不同的东西.相反调用的子例程.如果由于性能原因或调用开销而无法完成此操作,请改用宏.

另外,如果它是一个可行的选项,我会考虑使用更高级别的语言逐步重写部分代码,或者使用C的子集,或者至少通过过度使用汇编宏来帮助标准化代码基础,但也有助于本地化潜在的错误.

如果C中的增量重写是一个可行的选项,一种可能的入门方法是将所有明显的函数转换为C函数,这些函数的主体在开始时从汇编文件中复制/粘贴,因此最终得到C具有大量内联汇编的函数.

就个人而言,我也会尝试在模拟器/模拟器中运行代码以轻松地逐步完成代码,并希望开始了解最重要的构建块(同时检查寄存器和堆栈的使用情况),一个带有内置调试器的8051模拟器应该是如果你真的必须自己做这件事,你可以使用它.

这也可以帮助您提出初始化序列和主循环结构以及调用图.

也许,你甚至可以找到一个很好的开源80851模拟器,可以很容易地修改,也可以自动提供一个完整的调用图,只是做一个快速搜索,我发现gsim51,但显然有其他几个选项,各种专有的选项.

如果我是你的情况,我甚至会考虑外包修改我的工具,以简化与此源代码工作的努力,即许多SourceForge上的项目接受捐赠,也许你可以跟你的雇主为赞助这样的修改.

如果不经济,也许你提供相应的补丁?

如果您已经在使用专有产品,您甚至可以与该软件的制造商交谈并详细说明您的要求,并询问他们是否愿意以这种方式改进此产品,或者他们是否至少可以允许接口允许客户进行此类自定义(某种形式的内部API或甚至简单的胶水脚本).

如果他们没有回应,请表明您的雇主一直在考虑使用不同的产品一段时间,并且您是唯一坚持使用该特定产品的人...... ;-)

如果软件需要某些I/O硬件和外设,您甚至可能希望编写相应的硬件仿真循环以在仿真器中运行该软件.

最后,我知道一个事实,我个人更喜欢定制其他软件的过程,以帮助我理解这样的意大利面条代码怪物,而不是手动单步执行代码并自己玩模拟器,无论我加多少加仑咖啡得到.

从开源8051模拟器中获取可用的调用图不应该比周末(最多)花费更长的时间,因为它主要意味着查找CALL操作码并记录其地址(位置和目标),以便将所有内容转储到文件供以后检查.

访问模拟器的内部实际上也是一种很好的方法来进一步检查代码,例如为了找到重复的操作码模式(比如20-50 +),这可能会被考虑到独立的函数/过程中,这实际上可能是有助于进一步减少代码库的大小和复杂性.

下一步可能是检查堆栈和寄存器使用情况.并确定所用函数参数的类型/大小,以及它们的值范围 - 以便您可以设想相应的单元测试.

使用dot/graphviz等工具可视化初始化序列的结构和主循环本身,与手动完成所有这些操作相比,将是一种纯粹的乐趣.

此外,您实际上最终会得到有用的数据和文档,从长远来看,它们可以作为更好的文档的基础.


Ian*_*anW 7

我担心这种问题没有灵丹妙药.我发现唯一的解决方案是打印出ASM文件然后安静地去模拟在你脑海中逐行运行程序(同时在记事本上写入寄存器和内存位置的内容).过了一段时间,你发现这不会像你期望的那样长.准备好花很多时间做这个并喝加仑咖啡.过了一会儿,你将了解它在做什么,你可以考虑改变.

8051是否有任何未使用的IO端口?如果确实如此,并且在调用某些例程时无法解决问题,则添加代码以将这些备用端口发送到高或低.然后当程序运行时用示波器观察这些端口.

祝好运


joh*_*ash 6

我知道这听起来很疯狂......但我失业了(我选择了错误的时间告诉大多数伙伴下地狱)并且有空闲时间.我愿意看看它.我曾经为苹果[和原版PC]编写程序集.如果我可以在模拟器上玩你的代码几个小时,我可以给你一个想法,如果我有机会为你记录它(没有运行我的计划外假期).由于我对8051一无所知这对像我这样的人来说可能是不可能的,但模拟器看起来很有希望.我不想要任何钱来做这件事.它足以让我们接触8051嵌入式开发.我告诉过你这听起来很疯狂.