assembly - 我们如何在编写大型程序时跟踪不同寄存器的值
问题描述
这里我代表了一个 msdos 代码的快照,在这种类型的代码块中,我们经常会遇到非常失望的情况,我们可以清楚地看到在第 26 行的行号处,我们将 DS:SI 的值存储到 ES:DI 中,但是我们可以清楚地看到寄存器 SI 或其内容不在第 26 行附近。我在理解汇编语言编码时经常遇到这种情况。
01 PUSH CS
02 POP DS
03 PUSH CS
04 POP ES
05 ASSUME DS:DOSGROUP,ES:DOSGROUP
06 MOV AX,OFFSET DOSGROUP:INITBLOCK
07 ADD AX,0Fh ; round to a paragraph
08 MOV CL,4
09 SHR AX,CL
10 MOV DI,DS
11 ADD DI,AX
12 INC DI
13 MOV [CurrentPDB],DI
14 PUSH BP
15 PUSH DX ; Save COMMAND address
16 MOV AX,[ENDMEM]
17 MOV DX,DI
18 invoke SETMEM ; Basic Header
19 ASSUME DS:NOTHING,ES:NOTHING
20 PUSH CS
21 POP DS
23 ASSUME DS:DOSGROUP
24 MOV DI,PDB_JFN_Table
25 XOR AX,AX
26 STOSW ;<--------------------here
27 STOSB
我们可以清楚地看到 SI 的值在第 26 行附近没有。这类问题的补救措施是什么。我们是对程序编码时流动的所有寄存器的值进行硬拷贝,还是回到代码中找到 SI(或任何特定寄存器)的值,然后在我们的编码中调整它的值。
解决方案
要回答标题问题:是的,使用注释来描述哪个逻辑“变量”将位于代码块的哪个寄存器中。并记录每个功能的输入/输出/破坏。就像;;; input: ds:si pointer to a 0-terminated string
在一些假设的函数中一样(不是这个)。在临时函数中,再次在您正在计算的地方发表评论。
如果您正在阅读其他人的文档记录不佳的代码,您可以在查看它以查看是否有任何更改某个寄存器之后,在块的顶部添加此类注释。(当存在您不知道它们破坏了哪些寄存器的函数调用时,这很重要。使用标准调用约定会使这变得容易得多,因为您知道要假定哪些寄存器被破坏了。)
正如 Jester 指出的那样,这是stos
,不是movs
,所以它不读DS:SI
。它仅将 AX 和 AL 存储到ES:DI
(英特尔文档)。然而,这段代码看起来有问题:它设置DS
但不是ES
在此之前,就好像它期望使用 STOS DS:DI
(它没有)。
也许它在实践中有效,因为实际上SETMEM
并没有clobber ,或者将其设置为这段代码想要的值。但是从调用到之后,看起来这段代码预计会被破坏。ES
assume ES:NOTHING
SETMEM
SETMEM
ES
我假设此代码来自您一直在查看的 DOS 1.0,因此可能ES
实际上仍然等于CS
来自此块顶部的推送/弹出,幸运或其他原因。
在这种情况下,在调试器中单步执行可能有助于理解它。 我认为 BOCHS 的内置调试器可以让您在任何地方设置断点,即使在操作系统的代码中,它们甚至可以在禁用中断的情况下工作。
无论如何,当事情变得复杂时,使用注释是有限制的。
这就是为什么在现实生活中我们将长距离/大规模优化/常量传播留给编译器(它们在这方面非常出色),并且大多只担心热循环的 asm 微优化(编译器并不总是很好) .
推荐阅读
- apache-spark - PySpark:使用行的主键作为 rand 的种子
- r - 如何在 Kable 中居中刷新右列文本?
- c# - C# 如果包含/等于
- botframework - Ms Bot V4(LUIS) 尝试启用通道 Cortana 时出现内部服务器错误
- mongodb - 更新依赖于新更新的文档键的值
- python - Matplotlib 动画 - 空轴
- graphql - 是否可以创建 GraphQL 子查询来创建数组作为 markdownRemark 的元素?
- python - 二维图像的地形高度预测
- sql - 如何创建检查以确保列中只有一个条目可以具有基于 SQL 中不同列的 id 的特定值?
- javascript - 自动将 Google 表格打开到最后一行第一列的脚本语言是什么?