我是否将非常短的C代码正确地翻译成汇编程序?

时间:2016-09-12 19:01:52

标签: c assembly x86

我目前正在学习汇编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: ...

我是否正确翻译了它?

3 个答案:

答案 0 :(得分:1)

不。

在第一种情况下,你的jne unequal什么都不做,因为无论如何控制都会去那里。您需要在之后跳转到

在你的第二种情况下,如果比较为真,你加减2,什么也不做。

您也不会将结果存储回原始值的位置,只需将其保留在eax

答案 1 :(得分:1)

除了一件事,你的编辑是正确的。

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”以相反的顺序存储(little endian。通常在代码中,您会将其视为0x2c054

答案 2 :(得分:0)

让我们回到您的第一个代码:

         mov eax, a
         cmp eax, 4711
         jmp equal
equal:   add eax, 2
         jne unequal
unequal: sub eax, 2

让我们假装第一条指令加载eax&#34; a&#34; (它实际上是在TASM / MASM中,而是坚持明确和准确[a],它更容易阅读源并在NASM中也有效。)

第二条指令是cmp,它从eax中减去4711,将结果抛出(不存储在任何地方),只有标志寄存器受到影响。如果&#34; a&#34;是4711,然后减法的结果为零,那么ZF = 1。否则ZF = 0。 (对于受CMP影响的其他标志,请参阅一些文档。)

因此,在第3行,eax仍包含来自&#34; a&#34;的值,而标志寄存器包含cmp eax,4711的结果。你做jmp。这是无条件的跳跃,无论发生什么,所以你直接继续指导等于&#34;地址,add eax,2。 =&GT;你将2添加到&#34; a&#34;在每种情况下。

add本身也会影响旗帜,因此对于&#34; a&#34; == -2 ZF = 1,否则ZF = 0!

然后是第一个条件跳转,根据当前标志寄存器内容对代码进行分支。 jne的缩写为&#34;跳跃不等于&#34;和&#34;等于&#34;在此上下文中表示设置零标志(ZF = 1)。

所以当&#34; a&#34;是-2,ZF在jne之前是1(&#34;等于&#34;),因此jne不会跳到&#34;不等于&#34;地址,但将继续到下一条指令(实际上是#34;不相等&#34;无论如何地址,所以jne毫无意义。)

对于&#34; a&#34;与-2不同,ZF将为0(&#34;不等于&#34;),因此jne将在提供的标签上执行跳转,继续执行地址&#34;不等于&#34;

因此,您必须远离不想执行的指令。

    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"

略微修改的示例,以显示条件为假的情况

    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代表任何&#34;条件跳转&#34;指令。满足条件时,将执行跳转到提供的标签。否则,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