将C程序转换为汇编代码。

时间:2021-11-03 03:14:45

How exactly do I convert this C program into assembly code? I am having a hard time understanding this process or how to even start it. I am new to this. Any help would be appreciated!

如何将这个C程序转换成汇编代码?我很难理解这个过程,甚至不知道如何开始。我是新手。如有任何帮助,我们将不胜感激!

while(a!=b){
     if(a > b){
        a = a - b;
       }
        else{
          b = b - a;
    }
  }
   return a;
   }

Side Note: Assume two positive integers a and b are already given in register R0 and R1.
Can you leave comments explaining how you did it?

边注:假设寄存器R0和R1中已经给出了两个正整数a和b。你能留下评论解释你是怎么做的吗?

5 个解决方案

#1


9  

If you are using gcc, you can get the assembly as gcc -S -o a.s a.c if your source code is a.c. If you are using Visual Studio, you can get it when you debug by selecting the "disassembly" window. Here is the output of Visual studio (I named the subrountine/function called "common" that's why "common" appears):

如果您正在使用gcc,您可以获得gcc -S -o a的程序集。年代。c如果你的源代码是a.c。如果你在使用Visual Studio,你可以通过选择“disassembly”窗口进行调试获得它。这是Visual studio的输出(我将子例程/函数命名为“common”,这就是为什么会出现“common”):

    while(a!=b){
    003613DE  mov         eax,dword ptr [a]  
    003613E1  cmp         eax,dword ptr [b]  
    003613E4  je          common+44h (0361404h)  
         if(a > b){
    003613E6  mov         eax,dword ptr [a]  
    003613E9  cmp         eax,dword ptr [b]  
    003613EC  jle         common+39h (03613F9h)  
            a = a - b;
    003613EE  mov         eax,dword ptr [a]  
    003613F1  sub         eax,dword ptr [b]  
    003613F4  mov         dword ptr [a],eax  
         }
         else{
    003613F7  jmp         common+42h (0361402h)  
             b = b - a;
    003613F9  mov         eax,dword ptr [b]  
    003613FC  sub         eax,dword ptr [a]  
    003613FF  mov         dword ptr [b],eax  
        }
      }
    00361402  jmp         common+1Eh (03613DEh)  
       return a;
    00361404  mov         eax,dword ptr [a]  
    }

Here variable a is saved in memory initially and so is b (dword ptr [b]).

这里变量a最初被保存在内存中,b也是如此(dword ptr [b])。

#2


9  

The professor that taught me system programming used what he called 'atomic-C' as a stepping stone between C and assembly. The rules for atomic-C are (to the best of my recollection):

教我系统编程的教授用他所谓的“原子-C”作为C和汇编之间的垫脚石。关于原子- c的规则是(我最好的回忆):

  1. only simple expressions allowed, i.e. a = b + c; is allowed a = b + c + d; is not allowed because there are two operators there.
  2. 只允许简单的表达式,即a = b + c;允许a = b + c + d;不允许,因为那里有两个操作符。
  3. only simple boolean expressions are allowed in an if statement, i.e. if (a < b) is allowed but if (( a < b) && (c < d)) is not allowed.
  4. 在if语句中只允许使用简单的布尔表达式,即不允许((a < b),但如果((a < b) && & (c < d))是不允许的。
  5. only if statements, no else blocks.
  6. 只有语句,否则不会阻塞。
  7. no for / while or do-while is allowed, only goto's and label's
  8. 不可以在此停留或停留,只允许在此停留

So, the above program would translate into;

因此,上面的程序将转化为;

 label1:
     if (a == b) 
         goto label2;

     if (a < b)
         goto label4;

     a = a - b;
     goto label3;

 label4:
     b = b - a;

 label3:
     goto label1; 

 label2:
     return a;

I hope I got that correct...it has been almost twenty years since I last had to write atomic-C. Now assuming the above is correct, lets start converting some of the atomic-C statements into MIPS (assuming that is what you are using) assembly. From the link provided by Elliott Frisch, we can almost immediately translate the subtraction steps:

我希望我没弄错……我上次不得不写原子- c已经快二十年了。现在假设上面的内容是正确的,让我们开始将一些atomic-C语句转换为MIPS(假设您正在使用的是MIPS)程序集。从Elliott Frisch提供的链接,我们几乎可以立即翻译减法步骤:

a = a - b     becomes R0 = R0 - R1 which is: SUBU R0, R0, R1
b = b - a     becomes R1 = R1 - R0 which is: SUBU R1, R1, R0

I used unsigned subtraction due to both a and b being positive integers.

由于a和b都是正整数,我使用了无符号减法。

The comparisons can be done thusly:

比较可以这样做:

if(a == b) goto label2 becomes if(R0 == R1) goto label2 which is: beq R0, R1, L2?

The problem here is that the third parameter of the beq op-code is the displacement that the PC moves. We will not know that value till we are done doing the hand assembly here.

这里的问题是,beq操作码的第三个参数是PC移动的位移。在我们完成手工组装之前,我们不会知道这个值。

The inequality is more work. If we leave of the pseudo code instructions, we first need to use the set on less than op-code which put a one in destination register if the first register is less than the second. Once we have done that, we can use the branch on equal as described above.

不平等意味着更多的工作。如果我们离开pseudo代码指令,我们首先需要使用小于op-code的设置,如果第一个寄存器小于第二个寄存器,则在目标寄存器中放置一个。一旦我们做到了这一点,我们就可以像上面描述的那样,使用相等的分支。

if(a < b)              becomes    slt R2, R0, R1  
    goto label4                   beq R2, 1, L4?        

Jumps are simple, they are just j and then the label to jump to. So,

跳转很简单,它们只是j,然后跳转到标签。所以,

goto label1 becomes j label1

Last thing we have to handle is the return. The return is done by moving the value we want to a special register V0 and then jumping to the next instruction after the call to this function. The issue is MIPS doesn't have a register to register move command (or if it does I've forgotten it) so we move from a register to RAM and then back again. Finally, we use the special register R31 which holds the return address.

我们必须处理的最后一件事是退货。返回是通过将我们想要的值移动到一个特殊的寄存器V0,然后在调用这个函数之后跳转到下一条指令来完成的。问题是MIPS没有寄存器来注册move命令(或者如果有的话,我忘记了),所以我们从一个寄存器移动到RAM,然后再返回。最后,我们使用专用寄存器R31来保存返回地址。

return a     becomes   var = a      which is SW R0, var
                       ret = var    which is LW var, V0
                       jump RA      which is JR R31

With this information, the program becomes. And we can also adjust the jumps that we didn't know before:

有了这些信息,程序就变成了。我们还可以调整我们以前不知道的跳跃:

           L1:
 0x0100        BEQ R0, R1, 8
 0x0104        SLT R2, R0, R1                 ; temp = (a < b)  temp = 1 if true, 0 otherwise
 0x0108        LUI R3, 0x01                   ; load immediate 1 into register R3
 0x010C        BEQ R2, 1, 2                   ; goto label4         
 0x0110        SUBU R0, R0, R1                ; a = a - b
 0x0114        J L3                           ; goto label3
           L4:
 0x0118        SUBU R1, R1, R0                ; b = b - a;
           L3:
 0x011C        J L1                           ; goto lable1
           L2:
 0x0120        SW R0, ret                     ; move return value from register to a RAM location
 0x0123        LW ret, V0                     ; move return value from RAM to the return register.
 0x0124        JR R31                         ; return to caller

It has been almost twenty years since I've had to do stuff like this (now a days, if I need assembly I just do what others have suggested and let the compiler do all the heavy lifting). I am sure that I've made a few errors along the way, and would be happy for any corrects or suggestions. I only went into this long-winded discussion because I interpreted the OP question as doing a hand translation -- something someone might do as they were learning assembly.

我已经有将近二十年没做过这样的事情了(现在,如果我需要汇编,我只做别人建议的事情,让编译器来完成所有繁重的工作)。我相信我在这个过程中犯了一些错误,如果有任何更正或建议,我将很高兴。我之所以讨论这个冗长的问题,是因为我把OP问题理解为做手工翻译——有些人在学习汇编时可能会这么做。

cheers.

欢呼。

#3


1  

I've translated that code to 16-bit NASM assembly:

我将代码翻译为16位的NASM程序集:

loop:
    cmp ax, bx
    je .end;        if A is not equal to B, then continue executing. Else, exit the loop
    jg greater_than;    if A is greater than B...

    sub ax, bx;     ... THEN subtract B from A...

    jmp loop;       ... and loop back to the beginning!

.greater_than:
    sub bx, ax;     ... ELSE, subtract A from B...

    jmp loop;       ... and loop back to the beginning!

.end:
    push ax;        return A

I used ax in place of r0 and bx in place of r1

我用ax代替r0,用bx代替r1

#4


0  

ORG 000H                   // origin
MOV DPTR,#LUT              // moves starting address of LUT to DPTR
MOV P1,#00000000B          // sets P1 as output port
MOV P0,#00000000B          // sets P0 as output port
MAIN: MOV R6,#230D         // loads register R6 with 230D
      SETB P3.5            // sets P3.5 as input port
      MOV TMOD,#01100001B  // Sets Timer1 as Mode2 counter & Timer0 as Mode1 timer
      MOV TL1,#00000000B   // loads TL1 with initial value
      MOV TH1,#00000000B   // loads TH1 with initial value
      SETB TR1             // starts timer(counter) 1
BACK: MOV TH0,#00000000B   // loads initial value to TH0
      MOV TL0,#00000000B   // loads initial value to TL0
      SETB TR0             // starts timer 0
HERE: JNB TF0,HERE         // checks for Timer 0 roll over
      CLR TR0              // stops Timer0
      CLR TF0              // clears Timer Flag 0
      DJNZ R6,BACK
      CLR TR1              // stops Timer(counter)1
      CLR TF0              // clears Timer Flag 0
      CLR TF1              // clears Timer Flag 1
      ACALL DLOOP          // Calls subroutine DLOOP for displaying the count
      SJMP MAIN            // jumps back to the main loop
DLOOP: MOV R5,#252D
BACK1: MOV A,TL1           // loads the current count to the accumulator
       MOV B,#4D           // loads register B with 4D
       MUL AB              // Multiplies the TL1 count with 4
       MOV B,#100D         // loads register B with 100D
       DIV AB              // isolates first digit of the count
       SETB P1.0           // display driver transistor Q1 ON
       ACALL DISPLAY       // converts 1st digit to 7seg pattern
       MOV P0,A            // puts the pattern to port 0
       ACALL DELAY
       ACALL DELAY
       MOV A,B
       MOV B,#10D
       DIV AB              // isolates the second digit of the count
       CLR P1.0            // display driver transistor Q1 OFF
       SETB P1.1           // display driver transistor Q2 ON
       ACALL DISPLAY       // converts the 2nd digit to 7seg pattern
       MOV P0,A
       ACALL DELAY
       ACALL DELAY
       MOV A,B             // moves the last digit of the count to accumulator
       CLR P1.1            // display driver transistor Q2 OFF
       SETB P1.2           // display driver transistor Q3 ON
       ACALL DISPLAY       // converts 3rd digit to 7seg pattern
       MOV P0,A            // puts the pattern to port 0
       ACALL DELAY         // calls 1ms delay
       ACALL DELAY
       CLR P1.2
       DJNZ R5,BACK1       // repeats the subroutine DLOOP 100 times
       MOV P0,#11111111B
       RET

DELAY: MOV R7,#250D        // 1ms delay
 DEL1: DJNZ R7,DEL1
       RET

DISPLAY: MOVC A,@A+DPTR    // gets 7seg digit drive pattern for current value in A
         CPL A
         RET
LUT: DB 3FH                // LUT starts here
     DB 06H
     DB 5BH
     DB 4FH
     DB 66H
     DB 6DH
     DB 7DH
     DB 07H
     DB 7FH
     DB 6FH
END

#5


0  

https://ctoassembly.com

https://ctoassembly.com

Try executing your code here. Just copy it inside the main function, define a and b variables before your while loop and you are good to go.

尝试在这里执行代码。只需在主函数中复制它,在while循环之前定义a和b变量,就可以了。

You can see how the code is compiled to assembly with a fair amount of explanation, and then you can execute the assembly code inside a hypothetical CPU.

您可以看到如何将代码编译为汇编,并提供大量的解释,然后您可以在假设的CPU中执行汇编代码。

#1


9  

If you are using gcc, you can get the assembly as gcc -S -o a.s a.c if your source code is a.c. If you are using Visual Studio, you can get it when you debug by selecting the "disassembly" window. Here is the output of Visual studio (I named the subrountine/function called "common" that's why "common" appears):

如果您正在使用gcc,您可以获得gcc -S -o a的程序集。年代。c如果你的源代码是a.c。如果你在使用Visual Studio,你可以通过选择“disassembly”窗口进行调试获得它。这是Visual studio的输出(我将子例程/函数命名为“common”,这就是为什么会出现“common”):

    while(a!=b){
    003613DE  mov         eax,dword ptr [a]  
    003613E1  cmp         eax,dword ptr [b]  
    003613E4  je          common+44h (0361404h)  
         if(a > b){
    003613E6  mov         eax,dword ptr [a]  
    003613E9  cmp         eax,dword ptr [b]  
    003613EC  jle         common+39h (03613F9h)  
            a = a - b;
    003613EE  mov         eax,dword ptr [a]  
    003613F1  sub         eax,dword ptr [b]  
    003613F4  mov         dword ptr [a],eax  
         }
         else{
    003613F7  jmp         common+42h (0361402h)  
             b = b - a;
    003613F9  mov         eax,dword ptr [b]  
    003613FC  sub         eax,dword ptr [a]  
    003613FF  mov         dword ptr [b],eax  
        }
      }
    00361402  jmp         common+1Eh (03613DEh)  
       return a;
    00361404  mov         eax,dword ptr [a]  
    }

Here variable a is saved in memory initially and so is b (dword ptr [b]).

这里变量a最初被保存在内存中,b也是如此(dword ptr [b])。

#2


9  

The professor that taught me system programming used what he called 'atomic-C' as a stepping stone between C and assembly. The rules for atomic-C are (to the best of my recollection):

教我系统编程的教授用他所谓的“原子-C”作为C和汇编之间的垫脚石。关于原子- c的规则是(我最好的回忆):

  1. only simple expressions allowed, i.e. a = b + c; is allowed a = b + c + d; is not allowed because there are two operators there.
  2. 只允许简单的表达式,即a = b + c;允许a = b + c + d;不允许,因为那里有两个操作符。
  3. only simple boolean expressions are allowed in an if statement, i.e. if (a < b) is allowed but if (( a < b) && (c < d)) is not allowed.
  4. 在if语句中只允许使用简单的布尔表达式,即不允许((a < b),但如果((a < b) && & (c < d))是不允许的。
  5. only if statements, no else blocks.
  6. 只有语句,否则不会阻塞。
  7. no for / while or do-while is allowed, only goto's and label's
  8. 不可以在此停留或停留,只允许在此停留

So, the above program would translate into;

因此,上面的程序将转化为;

 label1:
     if (a == b) 
         goto label2;

     if (a < b)
         goto label4;

     a = a - b;
     goto label3;

 label4:
     b = b - a;

 label3:
     goto label1; 

 label2:
     return a;

I hope I got that correct...it has been almost twenty years since I last had to write atomic-C. Now assuming the above is correct, lets start converting some of the atomic-C statements into MIPS (assuming that is what you are using) assembly. From the link provided by Elliott Frisch, we can almost immediately translate the subtraction steps:

我希望我没弄错……我上次不得不写原子- c已经快二十年了。现在假设上面的内容是正确的,让我们开始将一些atomic-C语句转换为MIPS(假设您正在使用的是MIPS)程序集。从Elliott Frisch提供的链接,我们几乎可以立即翻译减法步骤:

a = a - b     becomes R0 = R0 - R1 which is: SUBU R0, R0, R1
b = b - a     becomes R1 = R1 - R0 which is: SUBU R1, R1, R0

I used unsigned subtraction due to both a and b being positive integers.

由于a和b都是正整数,我使用了无符号减法。

The comparisons can be done thusly:

比较可以这样做:

if(a == b) goto label2 becomes if(R0 == R1) goto label2 which is: beq R0, R1, L2?

The problem here is that the third parameter of the beq op-code is the displacement that the PC moves. We will not know that value till we are done doing the hand assembly here.

这里的问题是,beq操作码的第三个参数是PC移动的位移。在我们完成手工组装之前,我们不会知道这个值。

The inequality is more work. If we leave of the pseudo code instructions, we first need to use the set on less than op-code which put a one in destination register if the first register is less than the second. Once we have done that, we can use the branch on equal as described above.

不平等意味着更多的工作。如果我们离开pseudo代码指令,我们首先需要使用小于op-code的设置,如果第一个寄存器小于第二个寄存器,则在目标寄存器中放置一个。一旦我们做到了这一点,我们就可以像上面描述的那样,使用相等的分支。

if(a < b)              becomes    slt R2, R0, R1  
    goto label4                   beq R2, 1, L4?        

Jumps are simple, they are just j and then the label to jump to. So,

跳转很简单,它们只是j,然后跳转到标签。所以,

goto label1 becomes j label1

Last thing we have to handle is the return. The return is done by moving the value we want to a special register V0 and then jumping to the next instruction after the call to this function. The issue is MIPS doesn't have a register to register move command (or if it does I've forgotten it) so we move from a register to RAM and then back again. Finally, we use the special register R31 which holds the return address.

我们必须处理的最后一件事是退货。返回是通过将我们想要的值移动到一个特殊的寄存器V0,然后在调用这个函数之后跳转到下一条指令来完成的。问题是MIPS没有寄存器来注册move命令(或者如果有的话,我忘记了),所以我们从一个寄存器移动到RAM,然后再返回。最后,我们使用专用寄存器R31来保存返回地址。

return a     becomes   var = a      which is SW R0, var
                       ret = var    which is LW var, V0
                       jump RA      which is JR R31

With this information, the program becomes. And we can also adjust the jumps that we didn't know before:

有了这些信息,程序就变成了。我们还可以调整我们以前不知道的跳跃:

           L1:
 0x0100        BEQ R0, R1, 8
 0x0104        SLT R2, R0, R1                 ; temp = (a < b)  temp = 1 if true, 0 otherwise
 0x0108        LUI R3, 0x01                   ; load immediate 1 into register R3
 0x010C        BEQ R2, 1, 2                   ; goto label4         
 0x0110        SUBU R0, R0, R1                ; a = a - b
 0x0114        J L3                           ; goto label3
           L4:
 0x0118        SUBU R1, R1, R0                ; b = b - a;
           L3:
 0x011C        J L1                           ; goto lable1
           L2:
 0x0120        SW R0, ret                     ; move return value from register to a RAM location
 0x0123        LW ret, V0                     ; move return value from RAM to the return register.
 0x0124        JR R31                         ; return to caller

It has been almost twenty years since I've had to do stuff like this (now a days, if I need assembly I just do what others have suggested and let the compiler do all the heavy lifting). I am sure that I've made a few errors along the way, and would be happy for any corrects or suggestions. I only went into this long-winded discussion because I interpreted the OP question as doing a hand translation -- something someone might do as they were learning assembly.

我已经有将近二十年没做过这样的事情了(现在,如果我需要汇编,我只做别人建议的事情,让编译器来完成所有繁重的工作)。我相信我在这个过程中犯了一些错误,如果有任何更正或建议,我将很高兴。我之所以讨论这个冗长的问题,是因为我把OP问题理解为做手工翻译——有些人在学习汇编时可能会这么做。

cheers.

欢呼。

#3


1  

I've translated that code to 16-bit NASM assembly:

我将代码翻译为16位的NASM程序集:

loop:
    cmp ax, bx
    je .end;        if A is not equal to B, then continue executing. Else, exit the loop
    jg greater_than;    if A is greater than B...

    sub ax, bx;     ... THEN subtract B from A...

    jmp loop;       ... and loop back to the beginning!

.greater_than:
    sub bx, ax;     ... ELSE, subtract A from B...

    jmp loop;       ... and loop back to the beginning!

.end:
    push ax;        return A

I used ax in place of r0 and bx in place of r1

我用ax代替r0,用bx代替r1

#4


0  

ORG 000H                   // origin
MOV DPTR,#LUT              // moves starting address of LUT to DPTR
MOV P1,#00000000B          // sets P1 as output port
MOV P0,#00000000B          // sets P0 as output port
MAIN: MOV R6,#230D         // loads register R6 with 230D
      SETB P3.5            // sets P3.5 as input port
      MOV TMOD,#01100001B  // Sets Timer1 as Mode2 counter & Timer0 as Mode1 timer
      MOV TL1,#00000000B   // loads TL1 with initial value
      MOV TH1,#00000000B   // loads TH1 with initial value
      SETB TR1             // starts timer(counter) 1
BACK: MOV TH0,#00000000B   // loads initial value to TH0
      MOV TL0,#00000000B   // loads initial value to TL0
      SETB TR0             // starts timer 0
HERE: JNB TF0,HERE         // checks for Timer 0 roll over
      CLR TR0              // stops Timer0
      CLR TF0              // clears Timer Flag 0
      DJNZ R6,BACK
      CLR TR1              // stops Timer(counter)1
      CLR TF0              // clears Timer Flag 0
      CLR TF1              // clears Timer Flag 1
      ACALL DLOOP          // Calls subroutine DLOOP for displaying the count
      SJMP MAIN            // jumps back to the main loop
DLOOP: MOV R5,#252D
BACK1: MOV A,TL1           // loads the current count to the accumulator
       MOV B,#4D           // loads register B with 4D
       MUL AB              // Multiplies the TL1 count with 4
       MOV B,#100D         // loads register B with 100D
       DIV AB              // isolates first digit of the count
       SETB P1.0           // display driver transistor Q1 ON
       ACALL DISPLAY       // converts 1st digit to 7seg pattern
       MOV P0,A            // puts the pattern to port 0
       ACALL DELAY
       ACALL DELAY
       MOV A,B
       MOV B,#10D
       DIV AB              // isolates the second digit of the count
       CLR P1.0            // display driver transistor Q1 OFF
       SETB P1.1           // display driver transistor Q2 ON
       ACALL DISPLAY       // converts the 2nd digit to 7seg pattern
       MOV P0,A
       ACALL DELAY
       ACALL DELAY
       MOV A,B             // moves the last digit of the count to accumulator
       CLR P1.1            // display driver transistor Q2 OFF
       SETB P1.2           // display driver transistor Q3 ON
       ACALL DISPLAY       // converts 3rd digit to 7seg pattern
       MOV P0,A            // puts the pattern to port 0
       ACALL DELAY         // calls 1ms delay
       ACALL DELAY
       CLR P1.2
       DJNZ R5,BACK1       // repeats the subroutine DLOOP 100 times
       MOV P0,#11111111B
       RET

DELAY: MOV R7,#250D        // 1ms delay
 DEL1: DJNZ R7,DEL1
       RET

DISPLAY: MOVC A,@A+DPTR    // gets 7seg digit drive pattern for current value in A
         CPL A
         RET
LUT: DB 3FH                // LUT starts here
     DB 06H
     DB 5BH
     DB 4FH
     DB 66H
     DB 6DH
     DB 7DH
     DB 07H
     DB 7FH
     DB 6FH
END

#5


0  

https://ctoassembly.com

https://ctoassembly.com

Try executing your code here. Just copy it inside the main function, define a and b variables before your while loop and you are good to go.

尝试在这里执行代码。只需在主函数中复制它,在while循环之前定义a和b变量,就可以了。

You can see how the code is compiled to assembly with a fair amount of explanation, and then you can execute the assembly code inside a hypothetical CPU.

您可以看到如何将代码编译为汇编,并提供大量的解释,然后您可以在假设的CPU中执行汇编代码。