>在这里我表示了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
在某个假设函数(不是这个)中一样。 在临时函数中,再次在你计算某事的地方发表评论。
如果你正在阅读别人记录不佳的代码,你可以在一个块的顶部添加这样的注释,在查看它以查看是否有任何更改某个寄存器之后。 (当有函数调用时,这并不重要,你不知道它们破坏了哪些寄存器。 使用标准调用约定可以简化得多,因为您知道要假设哪些寄存器被破坏了。
正如杰斯特指出的那样,这是stos
,而不是movs
,所以它不读DS:SI
。 它仅将AX和AL存储到ES:DI
(英特尔文档)。 但是,这段代码看起来很糟糕:它设置了DS
,但在此之前没有设置ES
,就好像它期望 STOS 使用DS:DI
(它没有)。
也许它在实践中有效,因为SETMEM
实际上并没有破坏ES
,或者将其设置为此代码无论如何都需要的值。 但是从调用SETMEM
后的assume ES:NOTHING
来看,这段代码似乎SETMEM
会破坏ES
。
我假设这段代码来自您一直在查看的 DOS 1.0,所以大概ES
实际上仍然等于这个块顶部的 push/pop 的CS
,运气或其他原因。
在这种情况下,在调试器中单步执行可能有助于理解它。我认为 BOCHS 的内置调试器可以让您在任何地方设置断点,即使在操作系统的代码中也是如此,即使禁用中断,它们也可以工作。
无论如何,当事情变得复杂时,使用注释是有限制的。
这就是为什么在现实生活中我们将远距离/大规模优化/常量传播留给编译器(编译器在这方面非常出色),并且大多只担心热循环的 asm 微优化(编译器并不总是很好)。