我目前正在学习汇编x86,我为自己做了一个小任务。
C代码:
if (a == 4711) { a = a + 2 } else
{ a = a - 2 }
汇编代码(eax
为寄存器,cmp
为比较,jne
为不相等时跳转,jmp
为相等时跳转):
mov eax, a
cmp eax, 4711
jmp equal
equal: add eax, 2
jne unequal
unequal: sub eax, 2
我认为更有效的方法是:
mov eax, a
cmp eax, 4711
jne unequal
add eax, 2
unequal: sub eax, 2
编辑: mov eax, a
cmp eax, 4711
jne unequal
equal: add eax, 2
jmp continue
unequal: sub eax, 2
continue: ...
我翻译对了吗?
不
在第一种情况下,你的jne unequal
什么也不做,因为控制无论如何都会去那里。你需要在之后跳转到
在第二个例子中,如果比较为真,则同时加减2,不做任何操作。
您也不会将结果存储回原始值所在的位置,您只需将其保留在eax
中。
你的编辑是正确的,除了一件事。
mov eax, a
将"a"的地址移动到eax中,而不是内容/值
这个简短的代码片段是在Ubuntu 16.04 elf64上使用NASM完成的
section .text
global _start
_start
mov eax, a
cmp eax, 4711
jnz unequal
add eax, 2
jmp Done
unequal:
sub eax, 2
Done: mov [a], eax
section .rodata
a dd 180308
它反汇编为;
00400080 B89C004000 mov eax,0x40009c
00400085 3D67120000 cmp eax,0x1267
0040008A 7505 jnz 0x400091
0040008C 83C002 add eax,byte +0x2
0040008F EB03 jmp short 0x400094
00400091 83E802 sub eax,byte +0x2
00400094 8904259C004000 mov [0x40009c],eax
变量"a"在这里
0040009C 54C00200
注意@ 4000080 a的地址被移动到EAX中,但是@ Done (400091), EAX中的任何内容都被移动到该地址中。还要注意,值@"a"是以反向顺序存储的(小端序)。通常在代码中你会看到它是0x2c054
让我们回到你的第一个代码:
mov eax, a
cmp eax, 4711
jmp equal
equal: add eax, 2
jne unequal
unequal: sub eax, 2
让我们假设第一条指令用"a"加载eax
(实际上在TASM/MASM中是这样的,但更坚持明确和准确的[a]
,这更容易阅读源代码,并且在NASM中也可以工作)。
第二条指令是cmp
,它从eax中减去4711,把结果扔掉(不存储在任何地方),只有标志寄存器受到影响。如果"a"为4711,则减法的结果为零,因此ZF=1。否则ZF = 0。(有关受CMP影响的其他标志,请参阅一些文档)。
所以在第3行eax
仍然包含来自"a"的值,并且标志寄存器包含cmp eax,4711
的结果。然后做jmp
。这是无条件跳转,无论发生什么,所以你直接继续指令在"相等"的地址,这是add eax,2
。=>每次都要给"a"加2
add
本身也影响标志,所以对于"a" == -2 ZF=1,否则ZF=0!
然后是第一个条件跳转,根据当前标志寄存器的内容分支代码。jne
是"jump not equal"的缩写,这里的"equal"表示设置零标志(ZF=1)。
因此,当"a"为-2时,ZF在jne
之前为1("等于"),因此jne
不会跳转到"不相等"的地址,而是继续到下一条指令(实际上是在"不相等"的地址,所以jne
是没有意义的)。
对于与-2不同的"a", ZF将为0 ("is not equal"),因此jne
将在提供的标签上执行跳转,继续执行地址为"不相等"的指令。
所以你必须引导CPU远离你不想执行的指令。
xor eax,eax ; sets eax to 0, and ZF=1
jz label_1 ; ZF is 1, so jump is executed, CPU goes to "label_1"
inc eax ; this instruction is then skipped and not executed
label_1:
; eax being still 0, and ZF being still set ON
; whatever instruction is here, CPU will execute it after the "jz"
稍微修改的示例,以显示condition为false的情况
xor eax,eax ; sets eax to 0, and CF=0, ZF=1, ...
jc label_1 ; CF is 0, so "jump carry" is NOT executed
inc eax ; this instruction is executed after "jc"
label_1:
; here eax is 1
; CF is still 0 (not affected by INC)
; but ZF is 0 (affected by INC)
总结:你应该非常清楚哪些指令影响哪些标志,以及以何种方式影响。当不确定时,将CMP
+ Jcc
对保持在一起(以免意外影响cmp
的标志结果)。Jcc
表示任何"条件跳转"指令。当满足条件时,执行跳转到提供的标签。否则Jcc
指令将被忽略,并继续执行它后面的指令。
顺便说一句,我个人会写C代码:
if (a == 4711) { a = a + 2 } else
{ a = a - 2 }
:
cmp [a],DWORD 4711
mov eax,2
je a_is_4711
neg eax ; -2 for non 4711 value
a_is_4711:
add [a],eax