汇编语言编程提示和技巧

Jus*_*tin 8 x86 assembly nasm

我正在编写自己的"玩具"操作系统,目前我主要是在汇编(NASM)中进行操作 - 部分是因为我希望它能帮助我理解x86的反汇编,也因为我找到了它也很有趣!

这是我第一次使用汇编编程的经验 - 我的选择速度比我想象的要快,但是学习任何明显不同的语言我发现我的代码结构相当混乱,因为我试图找出哪些模式和约定我应该使用.

特别是目前我正在努力:

跟踪寄存器

目前一切都处于16位模式,因此我只有6个通用寄存器可供使用,其中可用于访问存储器的寄存器更少.我继续践踏我自己的寄存器,这反过来意味着我经常交换寄存器来避免这种情况 - 因此我很难跟踪哪些寄存器包含什么值,即使是自由评论.这是正常的吗?有什么我可以做的,以帮助使事情更容易跟踪?

例如,我开始使用被破坏的寄存器列表来评论我的所有函数:

; ================
; c_lba_chs
; Converts logical block addressing to Cylinder / Head / Selector
;  ax (input, clobbered) - LBA
;  ch (output) - Track number (cylinder)
;  cl (output) - Sector number
;  dh (output) - Head number
; ================
Run Code Online (Sandbox Code Playgroud)

跟踪堆栈

在一些情况下,当我用完寄存器时,我已经开始使用堆栈了,但这让事情变得更糟 - 任何比push call pop保存寄存器的简单序列更复杂的事情都会导致我完全失去轨道,使得它甚至变得棘手告诉我堆栈中是否有正确数量的项目(特别是涉及错误处理时 - 见下文),更不用说它们处于什么顺序.我知道必须有更好的方法来使用堆栈,我只是可以看不出它是什么.

处理错误

我一直在使用进位标志和零标志(取决于函数)来向调用者指示错误,例如:

myfn:
    ; Do things
    jz .error
    ; Do more things
    ret

    .error:
        stc
        ret
Run Code Online (Sandbox Code Playgroud)

这是指示错误的正常方式吗?

还有什么其他的提示或技巧,我可以用来更好地组装我的装配?

最后是否有良好的资源/精心编写的汇编示例?我遇到了汇编语言编程的艺术,但它似乎非常关注语言的细节,而不太重视代码的结构.(还有一些代码示例使用了段,我认为我应该避免使用段).

我正在使用零段(平面内存模型)来做所有这些,以保持简单,并在我开始使用C时使事情变得更容易.

old*_*mer 6

别担心,你几乎走在正确的轨道上.作为装配,您可以做您想做的事情,这样您就可以自由决定如何管理您的寄存器和数据.我建议为自己开发一些标准,使用C标准可能不是一个坏主意.我还建议为这样的第一个项目使用不同的汇编语言(例如在qemu上运行的ARM),x86在指令集中有点可怕.但这是一个单独的主题......

汇编程序通常允许您声明变量(如果您愿意),内存使用名称:

bob: .word 0x1234
Run Code Online (Sandbox Code Playgroud)

然后从汇编程序(在这里使用ARM asm)

ldr r0,bob
add r0,#1
str r0,bob
Run Code Online (Sandbox Code Playgroud)

寄存器暂时使用,实际数据保存在内存中.这样的模型可以帮助跟踪事物,因为真实数据保存在内存中,用户创建的变量名称就像高级语言一样.x86使得这更容易,因为您可以对内存执行操作,而不必通过寄存器来处理所有内容.同样,您可以使用堆栈框架管理本地变量,从堆栈中减去一些数字以覆盖该函数的堆栈帧,并在该函数内知道/记住变量joe是堆栈指针+4,ted是堆栈指针+8,等等.可能在代码中使用注释来记住这些内容的位置.记住在返回之前将堆栈指针/帧恢复到其入口点.这种方法有点困难,因为您没有使用变量名称而是数字偏移.但是提供局部变量和递归和/或一些全局内存节省.

用你的眼睛和手(键盘和鼠标)做一个人的工作你可能想要将数据保存在寄存器中的时间不能超过文本编辑器屏幕上的数据,一眼就能看到变量转到然后寄存器一目了然地返回内存中的变量.程序/编译器当然可以跟踪系统中的内存,远远超过人类.这就是为什么编译器平均产生比人类更好的汇编程序(特定情况下人类可以随时调整或修复问题).

错误处理,你需要小心使用标志,由于某种原因它不适合我.它可能很好,中断保留标志,你的代码都必须保留或设置标志等.嗯,标志的问题是你必须在函数返回后立即检查/使用该返回值,之前你有一个修改标志的指令.如果您使用寄存器,则在需要采样或使用该返回值之前,您可以选择不修改该返回寄存器以获取更多指令.

我认为这里的底线是,查看编译器用于该指令集的C调用约定规则,以及其他指令集,您将看到强烈的相似性并且有充分的理由.它们易于管理.由于寄存器太少,您可以看到为什么调用约定有时会直接进入所有参数的堆栈,有时也会返回返回值.我被告知Amiga bios为每个bios函数都有一个自定义调用约定,这使得一个紧凑而快速执行的系统,但是乳清试图使用编译器在C中重新创建bios并使用汇编程序包装器附加到函数很困难.我确信没有关于每个功能的良好文档,它是无法管理的.在路上你可能会决定你想要这个便携式,并且可能希望你选择了一个常用的呼叫约定.你仍然想要注释你的代码,说参数1是这个,参数2是那个,等等.另一方面,如果你现在或过去编程过x86汇编程序调用DOS和BIOS调用,你会很满意将每个函数放在一个引用中,并将数据放在每个函数的适当寄存器中.因为有很好的参考资料,所以每个功能都可以自定义.