获得汇编语言编程技巧

gol*_*ean 9 assembly

我是一名DSP,嵌入式软件程序员,希望提高我的汇编语言编程技巧.在我职业生涯的7年里,我一直在使用C,Matlab编程,只需要一点汇编语言编码.(ARM组装,DSP处理器组装).

现在我希望通过大量的方法提高我的汇编语言编码技能(它可以是任何汇编语言,无关紧要),并将其提升到"专家级".我知道编程更多就是它的方法,但我在这里问的是:

  • 人们在汇编语言(任何)编码方面的经验,这是他们多年来用汇编语言编写的.

  • 学习新汇编语言时要牢记的准则

  • 汇编语言中高效,正确编码的具体提示和技巧

  • 如何有效地将给定的C代码转换为最佳汇编代码

  • 如何清楚地理解给定的汇编代码

  • 如何跟踪其中将包含操作数的寄存器,堆栈指针,程序计数器,如何更深入地理解底层架构以及它为程序员提供的资源等.

基本上我想从那些做过详尽和密集的汇编语言编程的人那里获得一些"真实生活"的技巧.

谢谢.

-广告

old*_*mer 20

我的答案一般是......写一个反汇编程序.你已经触及了ARM,也许你知道所有的ARM指令,也许不是,拇指怎么样?ARM是一个很好的学习这种方法,流行和固定的指令长度,所以你可以从头到尾线性拆卸.

我不是说编写一个抛光的sourceforge值得反汇编程序,可能一次写入5或10行汇编程序,max,也许是使用不同寄存器的相同指令,只需用if-then-else树或开关来解析二进制文件.

add r0,r0,#1
add r0,r1,#1
add r0,r2,#2

您的目标是检查操作码中的每个位,理解为什么您只能有8位立即数,理解为什么某些处理器只允许您为本地条件分支跳转127或128个字节.你不必写一个反汇编来做这件事,但对我而言,它可以将信息嵌入我的大脑.

为了创建测试反汇编程序的所有可能的操作码/指令,您最终将学习所使用的汇编程序的所有语法细微差别.芯片公司书中的汇编语言不一定是该处理器系列的每个汇编器使用的确切语法.mrc/mcr指令(ARM)就是一个很好的例子.特别是气体以其改变语法的可怕工作而闻名,这使得它比芯片公司的语法和工具更加痛苦.这取决于你想要做什么,如果你只是想编写几行或修改某些东西,你不需要知道每个角落情况或汇编程序功能,但如果你真的想学习指令集,那么我推荐这种方法.

我也是一名嵌入式软件工程师,主要使用C但每天拆解C(使用objdump,而不是我的工具)检查输出,确保此代码在此内存区域,并且代码在这里,链接器的东西.但有时我必须检查处理器/芯片的模拟,并需要遵循指令提取及其相关的I/O来跟踪代码的模拟.或者在ram或其他总线上使用逻辑分析仪调试电路板.我已经学会了许多不同的处理器,8,16,32,64位(以及寄存器长度不在该列表中的那些)cisc,risc,dsp和几个微引擎.为每一个人写了一个反汇编程序(除了pdp11和x86,我的前两个指令集),可能需要一个下午才能学习一个新的ISA,一旦你看到它们中的一些.没有,我需要一两天的时间才能从我每天使用的一天/几周/几个月切换到几个月/几年未使用的一个.我不会立刻想到所有语言.

拆卸可变长度指令(大多数处理器在那里),真的做得对,本身就是一种艺术形式,超出了我所说的,这就是为什么我一次只推荐一些指令,不要嵌入这些说明中的数据.理想情况下,如果您有一个工作/良好的反汇编程序方便使用此方法,那么您可以将您的输出与真正经过测试和调试的真实反汇编程序进行比较.

如果你真的很渴望,除了拆解之外,写一个模拟器是一个很好的练习,我再说写作而不是检查.许多核心都有模拟器,你可以只检查它们而不是自己编写,对我有用的东西可能不适合你.我只写了几个.这不是一个下午的项目,但您可以更深入地了解该处理器系列的工作原理.

无论什么学习环境最适合您,无论是反汇编,模拟器,单步通过基于gui的ISA模拟器,书籍,网页.学习一个或多个处理器的汇编程序肯定会使您的高级编程更好.即使你实际上从不编写汇编程序但只检查它.编写一些使用数组,指针和结构的C代码,没有结构,循环,展开循环,使用各种编译器选项编译每个代码,启用和不启用调试器填充,无需优化,直到最大/积极优化.(针对不同的处理器进行编译,并比较程序流程,指令数量等方面的差异.llvm非常适用于此).

除了使您的高级编码更好,您还可以了解编译器的优缺点和平均值.你应该避免使用什么gee whiz语法,即使它是某些标准的一部分,以及大多数编译器正确的语法.我强烈建议您尝试尽可能多的不同编译器.

我建议查看截然不同的家庭,没有/没有近亲繁殖的家庭,我提到了ARM /拇指(和拇指2),它们绝对是近亲,但很受欢迎,并且会支付账单,这样你就可以在业余时间学习其他人.返回6802或68hc11,8088和/或z80.旧pic pic12或pic16(pic32只是一个mips).mips,power pc,avr.我是msp430指令集的忠实粉丝,非常好学习,有pdp11感觉,编译器友好,可悲地针对利基市场.8051,仍然没有死,令人惊讶.较旧的,大多数都有各种形式的指令集模拟器(例如,mame有许多),因此您可以在程序执行时使用这些模拟器和打印存储器和寄存器来观察,学习和改进.然后将那些旧的与较现代的那些进行比较.看看为什么一些具有相同时钟速率的ISA突然超越其他功能,一些具有单个累加器,一个寄存器,可能是两个或四个,并且做任何有用的事情你必须不断加载和存储,为一个实际操作采取几个指令.通过简单地使用更多寄存器或通用寄存器而不是专用寄存器,更现代的东西在一个或两个或三个指令/时钟中进行实际操作.

高级主题是内存访问.Thumb(不是thumb2)不如ARM有效,有明显的开销,相同任务需要5-10%的指令,那么为什么ThumbB在GameBoy Advance上要快得多?答案,主要是16位内存总线,具有非零等待状态内存.GBA没有缓存,但在rom接口上有预取处理,rom时序是非线性的,第一次读取是N个时钟,后续顺序地址的读取是M个时钟(M小于N)(其中使rom执行速度比ram快.不知道这可以为您的嵌入式程序和其他平台的成功与失败做出贡献.远远超出了汇编语言的理解,但你无法在不能阅读和理解编译器的输出的情况下到达那里.

另一个高级主题是缓存.如果您可以访问具有缓存的内容并且可以将其关闭(例如来自gamepark的gp32或wiz,可以自行创建的旧iPod),等等.理想情况下,您可以单独控制指令和数据缓存.您可以感受到完全不同的优化,它不再是最少的跳转/分支和最少的内存访问的指令.现在,您必须处理缓存行的长度,其中指令落在该缓存​​行中.在程序开头添加一个,两个,三个,有时更多的nops(实际上,确实在start.S中添加一个nop)可以显着改善或破坏由相同(更高级别)源,编译器生成的程序的性能和优化设置.

你的问题具体:

- 人们在汇编语言(任何)编码方面的经验,这是他们多年来用汇编语言编写的.

往上看

- 学习新的汇编语言时要记住的指导

往上看.相信处理器比不同处理器更相似,它们加载和存储寄存器,无条件地和有条件地分支.同样少数条件分支是众所周知的并且被使用.首先查找公共指令,立即加载,从一个寄存器移动到另一个寄存器,基于寄存器的add,以及,或者xor.并非所有的处理器都有一个除法指令,大多数都没有,有些没有倍增,超出你的想象.大多数你不能一般地使用,如果操作数和乘法的结果都是相同大小的寄存器,那么操作数的许多组合将溢出结果.

- 汇编语言中高效,正确编码的特定提示和技巧

沿着道路中间行驶,不要深入了解这个汇编程序/编译器的特定技巧,或者语言的gee whiz功能.保持简单,我的一些20岁的C代码今天仍在许多编译器上编译.我经常在今天没有编译的世界中找到几年或更少的代码,必须不断维护以使用新的编译器执行相同的功能,仅仅是因为编译器或语言技巧.

- 如何有效地将给定的C代码转换为最佳汇编代码

从C或其他开始,编译和反汇编,可能是几个级别的优化,也许是几个不同的编译器.然后解决问题.这是一个有趣的任务,但实际上你陷入了那个gee whiz陷阱.通常,保存5或10或20中的1或2或7个指令不值得携带C的汇编程序并将您置于不可移植的情况下,或者在编译器可能会遇到的情况下下一个或两个版本,甚至超出你的能力,因为他们比你知道更多的指令以及如何使用它们.

我使用汇编程序的地方(除了自然启动)实际上是用于读写寄存器或存储器位置.我使用的每个编译器都在某个时间点无法获得正确的指令,用8位替换了32位存储,就是那种东西.我实际上浪费指令和时钟来实现汇编程序中的peek和poke例程,以确保编译器不会埋葬我.内存副本和类似的东西通常都很好(在C库中),但是你可以利用指令集.利用不属于您正在使用的语言的特定指令,位测试或位设置(编译器无法识别/优化).如果您有字节或半字交换指令,则进行字节交换.某些旋转或移位或签名扩展.

如果你能找到它,那么作为黑皮书Michael Abrash,汇编语言的禅宗的一部分,它就是免费的.测量执行时间并测试,测试,测试.无论你认为你有多好,秒表都会显示真正的赢家.硬件已经消除了他的一半教学,但思考过程,以及在那个细节水平上检查代码的深度(我有原版书籍BTW),后来的杂志文章进入超级定标器处理器并简单地重新安排一些指令使得它们可以被识别并传递给单独的执行单元,使得相同的指令执行速度快很多,这对于阅读和理解是有趣的.这里的大部分内容都被管道,更多的执行单元,并行处理,更快的时钟埋没在噪声中.实际上,这完全是可怕的编程语言的结果,它们的效率非常低,以至于硬件必须进行补偿.但是,当我们能够以比同行快几千到几万倍的速度执行相同的操作时,这对我们来说更加有趣.

尽管用这种活动拍摄自己的脚很容易(用汇编器提高C输出),但要小心谨慎.你被警告了.

- 如何清楚地理解给定的汇编代码

这是演习的重点.如果您正在编写自己的汇编程序并在中间行驶,那么有一些流行的指令,易于阅读,易于编写,您对此非常了解.您采用编译器生成的指令并尝试检查它们,这更难,反汇编程序与生成的代码一样多的帮助/问题.以旧的学校游戏roms手工编写汇编程序或机器代码,甚至更难.

- 如何跟踪其中将包含操作数的寄存器,堆栈指针,程序计数器,如何更深入地理解底层架构以及它为程序员提供的资源等.

这通常超出了汇编程序,您必须了解管道,预取,分支阴影,高速缓存,写缓冲区,内存总线,等待周期.

另一个答案取决于你在这里真正要求的是知道编译器调用约定,是存储在r0,r1,r2中的函数的操作数......如果是这样,在它们进入堆栈之前有多少是寄存器.这个编译器是否将所有内容放在堆栈中?标志是否也存储在堆栈中?存储的返回地址在哪里?这些CAN因不同的编译器而异,与过去的x86(Zortech/Watcom vs Microsoft/Borland)中的相同目标不同,或者与我们在现代看到的相同编译器的相同处理器(ABI和EABI)不同.现代你可能会发现某个界面是由某人(芯片公司本身?)设计和定义的,各种编译器会因各种原因,可移植性,营销,懒惰等而达到该标准.

我很早就学会了汇编语言而且经常让我的同事烦恼,我倾向于在我的C中重用泛型变量,好像我在编写汇编程序一样.因此,在程序中的哪个时间点跟踪哪些数据是什么变量对我来说是习惯性的.因人而异.在分析别人的汇编程序或编译器输出时,我会在我用来阅读它的文本编辑器中修改输出.在视图空间,功能块之间的空白行,在每条指令后对寄存器中的内容进行注释时,r0将索引号保存在表中,r1现在保存表中该项的字偏移量,r0现在保持表中该项的物理地址,r2现在从表中保存项目本身等.

祝你好运,玩得开心,对不起真的很长的答案.


Jim*_*hel 6

一个好的起点是Jeff Duntemann的书,汇编语言循序渐进.这本书是关于Linux下的x86编程.我记得,本书的前一版本涵盖了Windows下的编程.这是一本初学者的书,它从头开始:位,字节,二进制算术等.如果你愿意,你可以跳过那部分,但至少可以略过它,这可能是一个好主意.

我认为学习ASM编码的最佳方法是1)学习硬件的基础知识然后2)研究其他人的代码.我上面提到的这本书是值得的.您可能也对汇编语言程序设计艺术感兴趣.

在我的时间里,我已经完成了相当多的汇编语言编程,尽管在过去15年左右的时间并不多.正如一位评论者指出的那样,当我考虑到与高级语言相比增加的开发和维护时间时,很小的尺寸和性能增益很难证明.

也就是说,我不会阻止你在ASM中提高效率.更熟悉处理器在该级别的工作方式也只能提高您的HLL编程技能.