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!
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?
6 Answers
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):
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]).
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):
- 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
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:
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, R0I used unsigned subtraction due to both a and b being positive integers.
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.
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.
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,
goto label1 becomes j label1Last 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.
return a becomes var = a which is SW R0, var ret = var which is LW var, V0 jump RA which is JR R31With 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 callerIt 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.
cheers.
4I've translated that code to 16-bit NASM assembly:
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 AI used ax in place of r0 and bx in place of r1
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 3 Although this is compiler's task but if you want to make your hands dirty then look at godbolt
This is great compiler explorer tool let you convert your C/C++ code into the assembly line by line.
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.
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.
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.
1