【问题标题】:Convert C program into assembly code将 C 程序转换为汇编代码
【发布时间】:2014-09-29 02:10:22
【问题描述】:

如何将这个 C 程序转换为汇编代码?我很难理解这个过程或什至如何开始它。我是新来的。任何帮助将不胜感激!

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

旁注:假设寄存器 R0 和 R1 中已经给出了两个正整数 a 和 b。
您能否让 cmets 解释您是如何做到的?

【问题讨论】:

  • 使用 C 编译器选项生成汇编代码。
  • gcc -S foo.c.
  • @Oregon Trail,您为什么认为许多专业人士缺乏 MIPS 组装经验?如果不出意外的话,许多人已经参加过这样的课程或阅读过将 MIPS 用作学习汇编语言和操作系统概念的模型系统的书籍。
  • @OregonTrail MIPS 使用 $ 或 $t/s/a/v/k... 作为寄存器名称,而不是 Rx

标签: c assembly


【解决方案1】:

教我系统编程的教授使用他所谓的“原子-C”作为 C 和汇编之间的垫脚石。 atomic-C 的规则是(据我所知):

  1. 只允许使用简单的表达式,即允许使用a = b + c; 不允许使用a = b + c + d;,因为那里有两个运算符。
  2. if 语句中只允许使用简单的布尔表达式,即允许使用if (a < b),但不允许使用if (( a < b) && (c < d))。
  3. 只有 if 语句,没有 else 块。
  4. 不允许 for / while 或 do-while,只允许 goto 和 label

因此,上述程序将转换为;

 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;

我希望我没看错……自从我上次编写 atomic-C 以来已经快二十年了。现在假设以上是正确的,让我们开始将一些 atomic-C 语句转换为 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

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

可以这样进行比较:

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

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

不平等是更多的工作。如果我们离开伪代码指令,我们首先需要使用set on less than 操作码,如果第一个寄存器小于第二个,则将一个放在目标寄存器中。完成此操作后,我们可以如上所述使用branch on equal。

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

跳转很简单,它们只是 j 和要跳转到的标签。所以,

goto label1 becomes j label1

我们必须处理的最后一件事是退货。返回是通过移动我们想要的值来完成的 一个特殊的寄存器V0,然后在调用这个函数后跳转到下一条指令。问题是 MIPS 没有寄存器来注册移动命令(或者如果有的话,我已经忘记了),所以我们从寄存器移动到 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

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

           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

自从我不得不做这样的事情以来已经快二十年了(现在,如果我需要汇编,我只是按照其他人的建议去做,让编译器完成所有繁重的工作)。我确信我在此过程中犯了一些错误,如果有任何更正或建议,我会很高兴。我之所以参加这个冗长的讨论,是因为我将 OP 问题解释为进行手动翻译——有人在学习汇编时可能会这样做。

干杯。

【讨论】:

  • 谢谢!我正在尝试理解编译器,而破译 gcc 或其他编译器生成的汇编代码真是太痛苦了!顺便说一句,名字重要吗?我知道我说的是加载到寄存器中,但是在汇编代码中,说 BEQ A,B,8 有效吗?
  • 我假设您的意思是寄存器的名称。如果是这样,那么是的,它们确实很重要。你的汇编程序应该在BEQ A, B, 8 上抛出一个错误,除非你已经将 A 和 B 定义为某种东西。
  • 所以让我说得对,汇编指令的所有操作数名称都来自预定义的集合(代表寄存器)?即,将任意名称映射到寄存器不是汇编程序的工作,即这是在编译时完成的?
  • 据我所知,设置了操作码助记符的名称(即 mov、cmp 等)以及寄存器(eax、esi、edi 等)。您的汇编器可能允许定义宏(例如,请参阅 HLA,高级汇编),这可能会使汇编更方便使用。
【解决方案2】:

如果您使用的是gcc,如果您的源代码为a.c,则可以将程序集设为gcc -S -o a.s a.c。如果您使用的是 Visual Studio,则可以在调试时通过选择“反汇编”窗口来获取它。这是 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]  
    }

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

【讨论】:

    【解决方案3】:

    我已将该代码翻译成 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
    

    我用ax 代替r0 和bx 代替r1

    【讨论】:

      【解决方案4】:
      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
      

      【讨论】:

      • 将汇编转换为 c 编程
      • 需要适当的格式并添加一些说明来解释
      • 这个问题是在寻找一个解释,而不仅仅是为了工作代码。您的回答没有为提问者提供任何见解,可能会被删除。请edit解释导致观察到的症状的原因。
      【解决方案5】:

      虽然这是编译器的任务,但如果你想弄脏你的手,那么看看godbolt

      这是一款出色的编译器浏览器工具,可让您将 C/C++ 代码逐行转换为汇编代码。

      如果您是初学者并想了解“C 程序如何转换为程序集?”然后我在上面写了一篇详细的帖子here。

      【讨论】:

        【解决方案6】:

        http://ctoassembly.com

        尝试在此处执行您的代码。只需将其复制到 main 函数中,在 while 循环之前定义 a 和 b 变量即可。

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

        【讨论】:

        • 听起来是一个非常酷的网站,但它似乎不起作用(至少对我而言)。它消失了吗?
        猜你喜欢
        • 2014-09-01
        • 2020-11-04
        • 2011-05-04
        • 2013-01-22
        • 2013-04-15
        • 1970-01-01
        • 2021-08-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多