[英]Convert C program into assembly code
我究竟如何將這個 C 程序轉換成匯編代碼? 我很難理解這個過程或者如何開始它。 我是新來的。 任何幫助,將不勝感激!
while(a!=b){
if(a > b){
a = a - b;
}
else{
b = b - a;
}
}
return a;
}
旁注:假設兩個正整數 a 和 b 已經在寄存器 R0 和 R1 中給出。
你能留下評論解釋你是如何做到的嗎?
如果您使用的是gcc
,並且您的源代碼是ac
,則可以將程序集作為gcc -S -o as ac
ac
。 如果您使用的是 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]
)也是如此。
教我系統編程的教授使用他所謂的“atomic-C”作為 C 和匯編之間的墊腳石。 atomic-C 的規則是(據我所知):
a = b + c;
允許a = b + c + d;
不允許,因為那里有兩個運營商。if (a < b)
但不允許if (( a < b) && (c < d))
。所以,上面的程序將轉化為;
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
操作碼的set on less than
如果第一個寄存器小於第二個,則在目標寄存器中放置一個。 一旦我們這樣做了,我們就可以像上面描述的那樣branch on equal
使用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 問題解釋為手工翻譯——有人在學習匯編時可能會這樣做。
干杯。
我已將該代碼轉換為 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
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
嘗試在此處執行您的代碼。 只需將它復制到主函數中,在while
循環之前定義a
和b
變量,就可以了。
您可以通過大量的解釋了解如何將代碼編譯為匯編代碼,然后您可以在假設的 CPU 中執行匯編代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.