C语言 将C程序转换为汇编代码
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26091622/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Convert C program into assembly code
提问by Sam
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?
旁注:假设两个正整数 a 和 b 已经在寄存器 R0 和 R1 中给出。
你能留下评论解释你是如何做到的吗?
回答by CS Pei
If you are using gcc, you can get the assembly as gcc -S -o a.s a.cif 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.s a.c源代码一样获取程序集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]
}
Here variable ais saved in memory initially and so is b(dword ptr [b]).
这里变量a最初保存在内存中,b( dword ptr [b])也是如此。
回答by thurizas
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):
教我系统编程的教授使用他所谓的“atomic-C”作为 C 和汇编之间的垫脚石。atomic-C 的规则是(据我所知):
- only simple expressions allowed, i.e.
a = b + c;is alloweda = b + c + d;is not allowed because there are two operators there. - only simple boolean expressions are allowed in an if statement, i.e.
if (a < b)is allowed butif (( a < b) && (c < d))is not allowed. - only if statements, no else blocks.
- no for / while or do-while is allowed, only goto's and label's
- 只有简单的表达式允许的,即
a = b + c;允许a = b + c + d;不允许,因为有两家运营商那里。 - if 语句中只允许使用简单的布尔表达式,即
if (a < b)允许但if (( a < b) && (c < d))不允许。 - 只有 if 语句,没有 else 块。
- 不允许 for / while 或 do-while,只有 goto 和 label 的
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:
我希望我说对了……距离我上次不得不写 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
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 thanop-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 equalas described above.
不平等是更多的工作。如果我们离开伪代码指令,我们首先需要使用set on less than操作码,如果第一个寄存器小于第二个,则在目标寄存器中放置一个。一旦我们这样做了,我们就可以使用branch on equal上面描述的。
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 没有寄存器来注册移动命令(或者如果有,我已经忘记了),所以我们从寄存器移动到 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.
干杯。
回答by SirPython
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 axin place of r0and bxin place of r1
我用ax代替r0和bx代替r1
回答by denish
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
回答by darijan
Try executing your code here. Just copy it inside the main function, define aand bvariables before your whileloop and you are good to go.
尝试在此处执行您的代码。只需将它复制到主函数中,在循环之前定义a和b变量,while就可以了。
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 中执行汇编代码。
回答by Vishal Chovatiya
Although this is compiler's task but if you want to make your hands dirty then look at godbolt
虽然这是编译器的任务,但如果你想让你的手变脏,那么看看Godbolt
This is great compiler explorer tool let you convert your C/C++ code into the assembly line by line.
这是一款出色的编译器资源管理器工具,可让您将 C/C++ 代码逐行转换为汇编代码。
If you are a beginner and wants to know "How C program converts into the assembly?" then I have written a detailed post on it here.
如果您是初学者并且想知道“C 程序如何转换为程序集?” 然后我在这里写了一篇详细的文章。

