版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第4章ARM編程與調(diào)試4.1ARM指令系統(tǒng)4.2ARM匯編語言設(shè)計(jì)4.3ARMC語言設(shè)計(jì)4.4ADS開發(fā)平臺(tái)4.5SDT開發(fā)平臺(tái)4.6基于JTAG的調(diào)試系統(tǒng)4.7仿真器調(diào)試系統(tǒng)4.8本章小結(jié)
ARM微處理器是基于精簡(jiǎn)指令集計(jì)算機(jī)(RISC)原理設(shè)計(jì)的。ARM體系提供兩種指令集:32位的ARM指令集和16位的Thumb指令集。ARM指令集執(zhí)行效率高,但是代碼密度低。4.1ARM指令系統(tǒng)Thumb指令集是ARM指令集的功能子集,它具有較高的代碼密度,同時(shí)保持了ARM大多數(shù)性能上的優(yōu)勢(shì)。ARM程序和Thumb程序可以相互調(diào)用,且兩種狀態(tài)之間的切換開銷幾乎為零。4.1.1ARM指令介紹
ARM指令包括數(shù)據(jù)處理指令、數(shù)據(jù)傳送指令、控制流指令、分支、陷入系統(tǒng)代碼。
ARM指令字長(zhǎng)為固定的32位,基本格式如下:
<opcode>{<cond>}{S}<Rd>,<Rn>,{<operand2>}
其中<>號(hào)內(nèi)的項(xiàng)是必需的,{}號(hào)內(nèi)的項(xiàng)是可選的。各項(xiàng)的含義如下:
opcode:指令助記符,如AND表示邏輯與指令;
cond:指令執(zhí)行條件;
S:指令的操作是否影響CPRS寄存器的值;
Rd:目標(biāo)寄存器;
Rn:包含第一個(gè)操作數(shù)的寄存器;
operand2:第二個(gè)操作數(shù)。
指令中的第二個(gè)操作數(shù)“operand2”有很多表示方法,靈活地使用這些表示方法能夠提高代碼效率。它包括以下形式。表4.1ARM指令編碼格式
1.常數(shù)表達(dá)式
常數(shù)表達(dá)式
#immed_8r必須對(duì)應(yīng)8位位圖,即是由一個(gè)8位的常數(shù)通過循環(huán)右移偶數(shù)位得到。所以不是每一個(gè)32位的常數(shù)都是合法的,只有通過以上的移位方法得到的常數(shù)才是合法的。
合法的常量如下:
0xff,0x3fc,0xff00,0x104,200。
不合法的常量如下:
0x102,0x1010,511,0xff1,0xffff。常量表達(dá)式應(yīng)用示例如下:
MOVR0,#0X104 ;令R0的數(shù)值為0X104
ANDR1,R2,#0xff ;R2和0xff與,并把結(jié)果保存在R1中
2.寄存器方式
在寄存器方式Rm下,指令的操作數(shù)“operand2”為寄存器的數(shù)值。
寄存器方式應(yīng)用示例如下:
MOVR1,R3 ;將R3的數(shù)值放到R1中
SUBR1,R2,R3 ;R1的數(shù)值等于R2的數(shù)值減去R3的數(shù)值
3.寄存器移位方式
在寄存器移位方式下,指令的操作數(shù)“operand2”為寄存器移位后所得的結(jié)果。
移位方式如下:
LSL#m ;邏輯左移m位(1≤m≤31)
LSR#m ;邏輯右移m位(1≤m≤31)
ASL#m ;算術(shù)左移m位(1≤m≤32)
ASR#m ;算術(shù)右移m位(1≤m≤32)
ROR#m ;循環(huán)右移m位(1≤m≤31)
RRX ;右移一位,并用CPSR中的C條件標(biāo)志位填補(bǔ)空出的位寄存器移位方式應(yīng)用示例如下:
ADDR1,R1,R1,LSL#4 ;R1=R1+R1*24=R1*17
SUBR1,R1,R2,LSRR3 ;R1=R1-R2/2R3
ARM指令中的第28~32位是給cond用的,它可以組成16種條件碼。當(dāng)ARM微處理器工作在ARM狀態(tài)時(shí),所有的指令都是根據(jù)CPSR中的條件標(biāo)志位狀態(tài)與指令的條件碼是否符合來決定是否執(zhí)行。當(dāng)執(zhí)行的條件滿足時(shí)就執(zhí)行該命令,否則指令被忽略,轉(zhuǎn)向下一條指令。例如,在跳轉(zhuǎn)指令B后面加上后綴EQ(即BEQ),則表示“相等則跳轉(zhuǎn)”,即當(dāng)CPSR中的Z標(biāo)志置位時(shí)發(fā)生跳轉(zhuǎn)。16種條件碼的含義及助記符如表4.2所示,其中只有15種可以使用,第16種(1111)為系統(tǒng)保留。
表4.216種條件碼的含義及助記符續(xù)表4.1.2ARM指令尋址方式
尋址方式是根據(jù)指令中給出的地址碼字段來實(shí)現(xiàn)尋找真實(shí)操作數(shù)地址的方式。ARM指令的尋址方式有以下幾種。
1.立即尋址
立即尋址又叫做立即數(shù)尋址,操作碼字段后面的地址部分就是操作數(shù),即數(shù)據(jù)就包含在指令當(dāng)中,那么取出指令也就取到了操作數(shù)。
立即尋址方式示例如下:
MOVR0,#0x104 ;R0←0x104
ADDR0,R1,#200 ;R0←R1+200
2.寄存器尋址
指令中的地址碼字段指定了寄存器的編號(hào),將寄存器中的數(shù)值作為操作數(shù)。執(zhí)行指令時(shí)直接取出寄存器中的數(shù)值進(jìn)行操作,這種尋址方式的效率比較高。
寄存器尋址方式示例如下:
SUBR0,R1,R2 ;R0←R1-R2
MOVR0,R1 ;R0←R1
3.寄存器間接尋址
寄存器間接尋址指令中的地址碼字段給出的是一個(gè)通用寄存器的編號(hào),它是以該寄存器中的值作為操作數(shù)的地址,而操作數(shù)本身存放在存儲(chǔ)單元中。
寄存器間接尋址方式示例如下:
ADDR1,R2,[R3] ;將R3中的數(shù)值作為地址,取出此地址中的數(shù)值后與R2相加,
;結(jié)果保存在R1中
LDRR1,[R2] ;將R2中的數(shù)值作為地址,取出此地址中的數(shù)值并保存在R1中
4.寄存器偏移尋址
寄存器偏移尋址是ARM指令集特有的尋址方式,當(dāng)?shù)?個(gè)操作數(shù)是寄存器偏移方式時(shí),先將其進(jìn)行移位操作后再與第一個(gè)操作數(shù)結(jié)合。
寄存器偏移尋址方式示例如下:
MOVR0,R1,LSL#1 ;R1中的值左移一位,結(jié)果放入R0中,即R0=R1*2
ANDR0,R0,R1,LSRR2 ;R1中的值右移R2位,和R0作邏輯與操作后,結(jié)果放
;入R0中可采用的移位方式有以下幾種:
LSL:邏輯左移,寄存器中字的低端空出的位補(bǔ)0。
圖4.1循環(huán)移位操作圖示
LSR:邏輯右移,寄存器中字的高端空出的位補(bǔ)0。
ASR:算術(shù)右移,在移位過程中保持字的符號(hào)位不變,即如果原操作數(shù)為正數(shù),則在字的高端空出的位補(bǔ)0,否則補(bǔ)1。
ROR:循環(huán)右移,即由字的低端移出的位來填補(bǔ)字的高端空出位。
RRX:帶擴(kuò)展的循環(huán)右移,操作數(shù)右移一位,高端空出的位由原CPSR中的C條件標(biāo)志位填補(bǔ)。
各移位操作如圖4.1所示。
5.基址尋址
基址尋址是將基址寄存器的內(nèi)容與指令中所給出的地址偏移量相加,形成操作數(shù)的有效地址?;穼ぶ芬话阌糜谠L問基址附近的存儲(chǔ)單元。
基址尋址方式示例如下:
LDRR0,[R1,#0X0C] ;將R1中的數(shù)值加上#0X0C形成操作數(shù)的有效地址,取出
;此地址中的數(shù)值保存在R0中
LDRR0,[R1#-2] ;將R1中的數(shù)值減去2形成操作數(shù)的有效地址,取出此地址
;中的數(shù)值保存在R0中,然后R1的內(nèi)容自動(dòng)減2個(gè)字節(jié)
6.多寄存器尋址
多寄存器尋址可以一次傳送多個(gè)寄存器的值,在一條指令中最多允許傳送16個(gè)通用寄存器的值。
多寄存器尋址方式示例如下:
LDMIAR0!,{R1-R4,R7} ;將R0單元中的數(shù)據(jù)讀到R1、R2、R3、R4和R7中,
;R0自動(dòng)加1
STMIAR0!,{R1-R4,R7} ;將R1、R2、R3、R4和R7中的值保存在R0指向的地址
;單元,R0自動(dòng)加1
在使用多寄存器尋址方式的時(shí)候,寄存器子集的順序按由小到大的順序排列,連續(xù)的寄存器用連字符“-”連接,否則用“,”分隔。
7.堆棧尋址
堆棧是一個(gè)按照特定順序進(jìn)行存取的存儲(chǔ)區(qū),操作順序?yàn)椤跋冗M(jìn)后出”,它使用一個(gè)被稱做堆棧指針的專用寄存器指示當(dāng)前的操作位置,堆棧指針總是指向棧頂。當(dāng)堆棧指針指向最后壓入堆棧的有效數(shù)據(jù)時(shí),稱為滿堆棧;當(dāng)堆棧指針指向下一個(gè)將要放入數(shù)據(jù)的空位置時(shí),稱為空堆棧。
另外,根據(jù)堆棧的生成方式,堆棧又可以分為遞增堆棧和遞減堆棧。當(dāng)堆棧由低地址向高地址生成時(shí),稱為遞增堆棧;當(dāng)堆棧由高地址向低地址生成時(shí),稱為遞減堆棧。因此根據(jù)以上的劃分方式就有4種不同類型的組合。
滿遞增堆棧:堆棧指針指向最后壓入的數(shù)據(jù),且由低地址向高地址生成。如指令LDMFA、STMFA等。
空遞增堆棧:堆棧指針指向下一個(gè)將要放入數(shù)據(jù)的空位置,且由低地址向高地址生成。如指令LDMEA、STMEA等。
滿遞減堆棧:堆棧指針指向最后壓入的數(shù)據(jù),且由高地址向低地址生成。如指令LDMFD、STMFD等。
空遞減堆棧:堆棧指針指向下一個(gè)將要放入數(shù)據(jù)的空位置,且由高地址向低地址生成。如指令LDMED、STMED等。
堆棧尋址方式示例如下:
STMFDSP!,{R0-R5,LR} ;將R0~R5、LR入棧,滿遞減堆棧
LDMFDSP!,{R0-R5,LR} ;數(shù)據(jù)出棧,放入R0~R5、LR寄存器中,滿遞減堆棧
8.塊復(fù)制尋址
塊復(fù)制尋址也是一種多寄存器傳送指令,它用于將一塊數(shù)據(jù)從存儲(chǔ)器的某一位置復(fù)制到另一位置。
塊復(fù)制尋址方式示例如下:
STMDAR0!,{R1-R5} ;將R1~R5的數(shù)據(jù)保存到存儲(chǔ)器中,存儲(chǔ)器指針在保存第一
;個(gè)值之后增加,增長(zhǎng)方向?yàn)橄蛳略鲩L(zhǎng)
STMDBR0!,{R1-R5} ;將R1~R5的數(shù)據(jù)保存到存儲(chǔ)器中,存儲(chǔ)器指針在保存第一
;個(gè)值之前增加,增長(zhǎng)方向?yàn)橄蛳略鲩L(zhǎng)
STMIAR0!,{R1-R5} ;將R1~R5的數(shù)據(jù)保存到存儲(chǔ)器中,存儲(chǔ)器指針在保存第一
;個(gè)值之后增加,增長(zhǎng)方向?yàn)橄蛏显鲩L(zhǎng)
STMDBR0!,{R1-R5} ;將R1~R5的數(shù)據(jù)保存到存儲(chǔ)器中,存儲(chǔ)器指針在保存第一
;個(gè)值之前增加,增長(zhǎng)方向?yàn)橄蛏显鲩L(zhǎng)
9.相對(duì)尋址
相對(duì)尋址方式與基址尋址類似,它以程序寄存器PC提供的當(dāng)前值為基地址,指令中的地址碼作為偏移量,兩者相加后得到的地址作為操作數(shù)的有效地址。
相對(duì)尋址方式示例如下:
BLSUBR1 ;調(diào)用到SUBR1子程序
BEQLOOP ;條件跳轉(zhuǎn)到LOOP標(biāo)號(hào)處
…
LOOPMOVR1,#3
…
SUBR1
…
跳轉(zhuǎn)指令BL用的是相對(duì)尋址方式。4.1.3ARM指令集介紹
ARM指令集可以分為6大類,即跳轉(zhuǎn)指令、數(shù)據(jù)處理指令、程序狀態(tài)寄存器(PSR)處理指令、加載/存儲(chǔ)指令、協(xié)處理器指令和異常產(chǎn)生指令。為了更清晰地描述這些指令,有些大類的指令進(jìn)一步分為幾個(gè)小類指令來分別介紹。
1.跳轉(zhuǎn)指令
跳轉(zhuǎn)指令用于實(shí)現(xiàn)程序流程的跳轉(zhuǎn),在ARM程序中有兩種方法可以實(shí)現(xiàn)程序的跳轉(zhuǎn):一是使用跳轉(zhuǎn)指令;另一種是直接向PC寄存器中寫入目標(biāo)地址值。通過后一種方式可以實(shí)現(xiàn)在4GB空間中的任意跳轉(zhuǎn)。如果在跳轉(zhuǎn)之前結(jié)合使用“MOVLR,PC”等指令,可以保存返回地址值,從而實(shí)現(xiàn)了在4GB連續(xù)的線性地址空間中進(jìn)行子程序的調(diào)用。
ARM跳轉(zhuǎn)指令可以實(shí)現(xiàn)從當(dāng)前指令向前或向后32MB的地址空間內(nèi)的跳轉(zhuǎn),它包括以下4類指令:
(1)
B指令——跳轉(zhuǎn)指令。
格式:B{cond}<地址>
B指令是最簡(jiǎn)單的跳轉(zhuǎn)指令。當(dāng)遇到B指令時(shí),ARM處理器就立即跳轉(zhuǎn)到給定的地址處,從那里開始執(zhí)行。需要注意的是,指令中的地址是相對(duì)當(dāng)前PC值的一個(gè)偏移量,而不是目標(biāo)地址。目標(biāo)地址應(yīng)是先將指令中的24位帶符號(hào)的補(bǔ)碼立即數(shù)擴(kuò)展為32位(擴(kuò)展其符號(hào)位),再將此32位數(shù)左移兩位得到的值與PC寄存器的值相加所得的結(jié)果。
(2)
BL指令——帶返回的跳轉(zhuǎn)指令。
格式:BL{cond}<地址>
BL跳轉(zhuǎn)指令常用于實(shí)現(xiàn)子程序的調(diào)用。跳轉(zhuǎn)之前,在寄存器R14中保存PC的當(dāng)前內(nèi)容,返回時(shí)通過將R14的內(nèi)容重新裝載到PC中來實(shí)現(xiàn)。
(3)
BLX指令——帶返回和切換的跳轉(zhuǎn)指令。
格式:BLX<地址>
BLX{cond}Rm
第一種格式的BLX指令實(shí)現(xiàn)從ARM指令集跳轉(zhuǎn)到指令中的目標(biāo)地址,并將處理器的工作狀態(tài)由ARM狀態(tài)切換到Thumb狀態(tài),同時(shí)將PC的當(dāng)前內(nèi)容保存到寄存器R14中。
第二種格式的BLX指令的目標(biāo)地址存放在指令中的寄存器Rm中,目標(biāo)地址指令既可以是ARM指令,也可以是Thumb指令,具體是由CPSR中的T位決定的。
因此當(dāng)子程序?yàn)門humb指令集而調(diào)用者為ARM指令集時(shí),可以通過BLX指令實(shí)現(xiàn)子程序的調(diào)用和處理器工作狀態(tài)的切換。同時(shí),子程序的返回可以通過將寄存器R14中的值復(fù)制到PC中來完成。
(4)
BX指令——帶狀態(tài)切換的跳轉(zhuǎn)指令。
格式:BX{cond}Rm
BX指令跳轉(zhuǎn)到指令中所指定的目標(biāo)地址,目標(biāo)地址處的指令既可以是ARM指令,也可以是Thumb指令。
2.數(shù)據(jù)處理指令
數(shù)據(jù)處理指令大致分為3類:數(shù)據(jù)傳送指令、算術(shù)邏輯運(yùn)算指令和比較指令。數(shù)據(jù)處理指令只能對(duì)寄存器的內(nèi)容進(jìn)行操作,而不能對(duì)內(nèi)存中的數(shù)據(jù)進(jìn)行操作。所有的ARM數(shù)據(jù)處理指令都可以選擇S作為后綴,以影響狀態(tài)標(biāo)志。比較指令不需要采用后綴,它會(huì)直接影響狀態(tài)標(biāo)志位。
1)數(shù)據(jù)傳送指令
數(shù)據(jù)傳送指令用于在寄存器和存儲(chǔ)器之間進(jìn)行數(shù)據(jù)的雙向傳輸。
(1)
MOV——數(shù)據(jù)傳送指令。
格式:MOV{cond}{S}Rd,operand2
MOV指令將操作數(shù)operand2傳送到目標(biāo)寄存器Rd中,其中operand2可以是寄存器、被移位的寄存器以及立即數(shù)。該指令用于移位運(yùn)算等操作。
(2)
MVN——數(shù)據(jù)求反傳送指令。
格式:MVN{cond}{S}Rd,operand2
MVN指令將操作數(shù)operand2按位取反后傳送到目標(biāo)寄存器Rd中,operand2同上。因?yàn)槠渚哂腥》垂δ埽钥梢匝b載范圍更廣的立即數(shù)。
2)算術(shù)邏輯運(yùn)算指令
算術(shù)邏輯運(yùn)算指令用于完成常用的算術(shù)與邏輯運(yùn)算,不僅將運(yùn)算結(jié)果存入目標(biāo)寄存器中,同時(shí)更新CPSR中相應(yīng)的條件標(biāo)志位。
(1)
ADD——加法指令。
格式:ADD{cond}{S}Rd,Rn,operand2
ADD指令將操作數(shù)operand2與Rn的值相加,結(jié)果保存在Rd寄存器中,operand2同上,Rn是寄存器。ADD指令可用于無符號(hào)和有符號(hào)數(shù)的加法運(yùn)算。
(2)
SUB——減法指令。
格式:SUB{cond}{S}Rd,Rn,operand2
SUB指令將操作數(shù)Rn減去operand2,結(jié)果保存在Rd中,operand2同上,Rn是寄存器。SUB指令可用于無符號(hào)和有符號(hào)數(shù)的減法運(yùn)算。
(3)
RSB——逆向減法指令。
格式:RSB{cond}{S}Rd,Rn,operand2
RSB指令將操作數(shù)operand2減去Rn,結(jié)果保存在Rd中,operand2同上,Rn是寄存器。該指令同樣可用于無符號(hào)和有符號(hào)數(shù)的減法運(yùn)算。
(4)
ADC——帶進(jìn)位加法指令。
格式:ADC{cond}{S}Rd,Rn,operand2
ADC指令將operand2與Rn中的值相加,再加上CPSR中的C條件標(biāo)志位的值,把結(jié)果保存在Rd中。由于它使用了進(jìn)位標(biāo)志位,因此可以進(jìn)行比32位數(shù)大的加法運(yùn)算。
(5)
SBC——帶進(jìn)位減法指令。
格式:SBC{cond}{S}Rd,Rn,operand2
SBC指令將寄存器Rn中的值減去operand2,再減去CPSR中的C條件標(biāo)志位的非(即若C標(biāo)志位為0則減1,反之減0),結(jié)果保存在Rd中。由于使用了進(jìn)位標(biāo)志位來借位,因此可以進(jìn)行大于32位的減法操作。
(6)
RSC——帶進(jìn)位逆向減法指令。
格式:RSC{cond}{S}Rd,Rn,operand2
運(yùn)算方式同SBC,但是是用operand2減去Rn。
(7)
AND——邏輯與操作指令。
格式:AND{cond}{S}Rd,Rn,operand2
AND指令將兩個(gè)操作數(shù)進(jìn)行按位邏輯與操作,結(jié)果保存到Rd中。該指令常用于屏蔽Rn的某些位。
(8)
ORR——邏輯或指令。
格式:ORR{cond}{S}Rd,Rn,operand2
ORR指令將兩個(gè)操作數(shù)進(jìn)行邏輯或操作,結(jié)果保存到Rd中。該指令常用于設(shè)置Rn的某些位。
(9)
EOR——邏輯異或指令。
格式:EOR{cond}{S}Rd,Rn,operand2
EOR指令將兩個(gè)操作數(shù)進(jìn)行邏輯異或操作,結(jié)果保存到Rd中。該指令常用于對(duì)Rn的某些位取反。
(10)
BIC——位清除指令。
格式:BIC{cond}{S}Rd,Rn,operand2
BIC指令將Rn的值與operand2的反碼按位作邏輯與操作,結(jié)果保存到Rd中。operand2為32位的掩碼,如果在掩碼中設(shè)置了某一位則清除該位,未設(shè)置的掩碼位保持不變。
3)比較指令
比較指令不保存運(yùn)算結(jié)果,只更新CPSR中的標(biāo)志位。
(1)
CMP——比較指令。
格式:CMP{cond}Rn,operand2
CMP指令將Rn中的值減去operand2的值,根據(jù)運(yùn)算結(jié)果設(shè)置CPSR中的條件標(biāo)志位,以便后面的指令進(jìn)行條件執(zhí)行。它與SUBS指令的區(qū)別在于,CMP指令不保存運(yùn)算結(jié)果。
(2)
CMN——基于相反數(shù)的比較指令。
格式:CMN{cond}Rn,operand2
CMN指令將Rn中的值加上operand2的值,根據(jù)運(yùn)算結(jié)果設(shè)置CPSR中的條件標(biāo)志位,以便后面的指令進(jìn)行條件執(zhí)行。它與ADDS指令的區(qū)別在于,CMN指令不保存運(yùn)算結(jié)果。
(3)
TST——位測(cè)試指令。
格式:TST{cond}Rn,operand2
TST指令對(duì)兩個(gè)操作數(shù)進(jìn)行按位邏輯與操作,根據(jù)運(yùn)算結(jié)果設(shè)置CPSR中的條件標(biāo)志位。該指令一般用來測(cè)試是否設(shè)置了特定的位。Rn為要測(cè)試的數(shù)據(jù)字,而operand2是一個(gè)位掩碼。經(jīng)測(cè)試,如果匹配則Z置位,否則Z復(fù)位。
(4)
TEQ——相等測(cè)試指令。
格式:TEQ{cond}Rn,operand2
類似于TST指令,但TEQ指令是對(duì)兩個(gè)操作數(shù)進(jìn)行按位邏輯異或操作。與EORS指令的區(qū)別同樣在于它不保存運(yùn)算結(jié)果。在用TEQ進(jìn)行相等測(cè)試時(shí),它不會(huì)影響進(jìn)位標(biāo)志(不像CMP),TEQ指令常與EQNE條件碼配合使用,若兩個(gè)數(shù)據(jù)相等則EQ有效,反之NE有效。
另外,ARM指令集中還包括兩類乘法指令:一類是32位的乘法指令,即操作結(jié)果為32位;另一類是64位的乘法指令,即操作結(jié)果為64位。具體包括以下6條指令:
(1)
MUL——32位乘法指令。
格式:MUL{cond}{S}Rd,Rm,Rs
MUL提供了32位整數(shù)乘法,將Rm和Rs中的值相乘,結(jié)果保存在Rd中。Rm和Rs為32位的有符號(hào)數(shù)或無符號(hào)數(shù)。
(2)
MLA——32位帶加法的乘法指令。
格式:MLA{cond}{S}Rd,Rm,Rs,Rn
同MUL,但將乘積后得到的結(jié)果再加上Rn。
(3)
UMULL——64位無符號(hào)數(shù)乘法指令。
格式:UMULL{cond}{S}RdLo,RdHi,Rm,Rs
將Rm和Rs中的值作無符號(hào)數(shù)的乘法,乘積結(jié)果的低32位放在RdLo中,高32位放在RdHi中。Rm和Rs都為32位的有符號(hào)數(shù)或無符號(hào)數(shù)。
(4)
UMLAL——64位帶加法的無符號(hào)數(shù)乘法指令。
格式:UMLAL{cond}{S}RdLo,RdHi,Rm,Rs
將Rm和Rs中的值作無符號(hào)數(shù)的乘法,得到的64位乘積結(jié)果與RdLo和RdHi相加。低32位保存在RdLo中,高32位保存在RdHi中。
(5)
SMULL——64位有符號(hào)數(shù)乘法指令。
格式:SMULL{cond}{S}RdLo,RdHi,Rm,Rs
與UMULL類似,不過SMULL是作64位有符號(hào)數(shù)的相乘操作。
(6)
SMLAL——64位帶加法的有符號(hào)數(shù)乘法指令。
格式:SMLAL{cond}{S}RdLo,RdHi,Rm,Rs
與UMLAL類似,不過SMLAL是作64位有符號(hào)數(shù)的相乘操作。
3.程序狀態(tài)寄存器處理指令
程序狀態(tài)寄存器處理指令用于在狀態(tài)寄存器和通用寄存器之間傳送數(shù)據(jù),包括以下兩條指令。
(1)
MRS——讀狀態(tài)寄存器指令。
格式:MRS{cond}Rd,psr
其中psr為CPSR或SPSR,Rd為目標(biāo)寄存器,不允許為R15。MRS指令把程序狀態(tài)寄存器的內(nèi)容傳送到通用寄存器中,該指令主要用于兩種情況:一是當(dāng)需要改變狀態(tài)寄存器的值時(shí),就用MRS指令把數(shù)據(jù)從狀態(tài)寄存器送入通用寄存器,修改之后再寫回狀態(tài)寄存器。另一種情況是當(dāng)在處理異常或進(jìn)程切換,需要保存狀態(tài)寄存器的值時(shí),可用該指令把狀態(tài)寄存器的值讀出,然后進(jìn)行保存。
(2)
MSR——寫狀態(tài)寄存器指令。
格式:MSR{cond}psr_fields,#immed_8r
MSR{cond}psr_fields,Rm
psr為CPSR或SPSR。
fields:指定傳送的區(qū)域,狀態(tài)寄存器的32位可以分為4個(gè)8位的域:bit[7:0]為控制域,用c表示;bit[15:8]為擴(kuò)展域,用x表示;bit[23:16]為狀態(tài)域,用s表示;bit[31:24]為標(biāo)志域,用f表示。
#immed_8r:將要傳送到狀態(tài)寄存器的立即數(shù)。
Rm:源寄存器,存放將要傳送到狀態(tài)寄存器指定域的數(shù)據(jù)。
MSR:用于將操作數(shù)的內(nèi)容傳送到程序狀態(tài)寄存器中的指定域中。通常用于恢復(fù)或改變狀態(tài)寄存器的內(nèi)容。如:當(dāng)退出中斷異常處理時(shí),如果事先保存了狀態(tài)寄存器的內(nèi)容,則可以通過MSR指令將保存的值恢復(fù)到狀態(tài)寄存器中。其中,MRS與MSR通常配合使用實(shí)現(xiàn)CPSR或SPSR寄存器的“讀—修改—寫”操作,來進(jìn)行處理器模式切換。需要注意的是,程序不能通過直接修改CPSR中T控制位將程序狀態(tài)從ARM狀態(tài)切換到Thumb狀態(tài),而必須通過執(zhí)行BX等指令來實(shí)現(xiàn)。
4.加載/存儲(chǔ)指令
加載指令用于從內(nèi)存中讀取數(shù)據(jù)放入寄存器中;存儲(chǔ)指令用于將寄存器的值保存到內(nèi)存。加載/存儲(chǔ)指令可以分為單寄存器操作指令和多寄存器操作指令。單寄存器加載/存儲(chǔ)指令可分為字和無符號(hào)字節(jié)加載/存儲(chǔ)指令與半字和有符號(hào)字節(jié)加載/存儲(chǔ)指令。前者包括以下4種指令:
(1)
LDR(T)——字?jǐn)?shù)據(jù)讀取指令(用戶模式的字?jǐn)?shù)據(jù)讀取指令)。
格式:LDR(T){cond}Rd,<地址>
LDR指令通常用于兩種情況:一種情況是從指定地址讀取32位的字到目標(biāo)寄存器Rd中;另一種情況是當(dāng)PC作為指令中的目標(biāo)寄存器時(shí),指令能實(shí)現(xiàn)程序跳轉(zhuǎn)的功能。T為可選后綴,如果指令有T,就為用戶模式下的字?jǐn)?shù)據(jù)讀取指令,也就是即使處理器是在特權(quán)模式下,存儲(chǔ)系統(tǒng)也將訪問看成是在處理器的用戶模式下進(jìn)行的。
(2)
LDRB(T)——字節(jié)數(shù)據(jù)讀取指令(用戶模式的字節(jié)數(shù)據(jù)讀取指令)。
格式:LDRB(T){cond}Rd,<地址>
LDRB指令用于從指定地址中讀取8位的字節(jié)數(shù)據(jù)到目標(biāo)寄存器Rd中,并將寄存器的高24位清零。同樣,T也是可選后綴,有T就為用戶模式下的字節(jié)數(shù)據(jù)讀取指令。
(3)
STR(T)——字?jǐn)?shù)據(jù)寫入指令(用戶模式字?jǐn)?shù)據(jù)寫入指令)。
格式:STR(T){cond}Rd,<地址>
STR指令把存儲(chǔ)在寄存器Rd中的字?jǐn)?shù)據(jù)寫入到指令中指定地址的存儲(chǔ)單元中。同樣,T也是可選后綴,作用同前。
(4)
STRB(T)——字節(jié)數(shù)據(jù)寫入指令(用戶模式字節(jié)數(shù)據(jù)寫入指令)。
格式:STRB(T){cond}Rd,<地址>
STRB指令用于把寄存器Rd中低8位的字節(jié)數(shù)據(jù)寫入到指定地址的存儲(chǔ)單元。T的作用同前。半字和有符號(hào)字節(jié)加載/存儲(chǔ)指令包括以下4種:
(1)
LDRH——半字?jǐn)?shù)據(jù)讀取指令。
格式:LDRH{cond}Rd,<地址>
LDRH指令用于從指定地址中讀取16位的半字?jǐn)?shù)據(jù)到目標(biāo)寄存器Rd中,并將寄存器的高16位清零。
(2)
LDRSB——有符號(hào)的字節(jié)數(shù)據(jù)讀取指令。
格式:LDRSB{cond}Rd,<地址>
LDRSB指令用于從指定地址中讀取8位的字節(jié)數(shù)據(jù)到目標(biāo)寄存器Rd中,并將寄存器的高24位設(shè)置為該字節(jié)數(shù)據(jù)的符號(hào)位的值(即將該8位字節(jié)數(shù)據(jù)進(jìn)行符號(hào)位擴(kuò)展,生成32位字?jǐn)?shù)據(jù))。
(3)
LDRSH——有符號(hào)的半字?jǐn)?shù)據(jù)讀取指令。
格式:LDRSH{cond}Rd,<地址>
LDRSH指令用于從指定地址中讀取16位的半字節(jié)數(shù)據(jù)到目標(biāo)寄存器Rd中,并將寄存器的高16位設(shè)置為該字節(jié)數(shù)據(jù)的符號(hào)位的值。
(4)
STRH——半字?jǐn)?shù)據(jù)寫入指令。
格式:STRH{cond}Rd,<地址>
STRH指令用于將寄存器Rd中的低16位的半字?jǐn)?shù)據(jù)寫入指令中指定地址的存儲(chǔ)單元。
多寄存器加載/存儲(chǔ)指令又稱為批量加載/存儲(chǔ)指令,它實(shí)現(xiàn)在一組寄存器和一塊連續(xù)的內(nèi)存單元傳輸數(shù)據(jù),主要用于現(xiàn)場(chǎng)保護(hù)﹑數(shù)據(jù)復(fù)制﹑參數(shù)傳送等。指令形式為L(zhǎng)DM/STM,LDM為加載多個(gè)寄存器,STM為存儲(chǔ)多個(gè)寄存器。允許一條指令傳送16個(gè)寄存器的任何子集或全部寄存器。
LDM/STM指令的格式如下:
LDM{cond}<模式>Rn{!},registers{^}
STM{cond}<模式>Rn{!},registers{^}
模式:控制地址的增長(zhǎng)方式,一共包括8種模式,如下所示(前4種用于數(shù)據(jù)塊的傳輸,后4種用于堆棧操作):
●
IA每次傳送后地址加4
●
IB每次傳送前地址加4
●
DA每次傳送后地址減4
●
DB每次傳送前地址減4
●
FD滿遞增堆棧
●
ED空遞增堆棧
●
FA滿遞減堆棧
●
EA空遞減堆棧
“!”:表示在操作結(jié)束后,將最后的地址寫回Rn中。
registers:寄存器,可以包含多個(gè)寄存器,使用“,”分開,且由小到大排列,如{R1,R2,R4-R7}。
“^”:若使用該后綴,則在進(jìn)行數(shù)據(jù)傳送且寄存器不包括PC時(shí),加載/存儲(chǔ)的寄存器是在用戶模式下的,而不是在當(dāng)前模式下。若在LDM指令的寄存器中包含PC的情況下使用,那么除了正常的多寄存器傳送外,
還將SPSR復(fù)制到CPSR中,這樣可用于異常處理返回。需要注意的是該后綴不允許在用戶模式和系統(tǒng)模式下使用。
在進(jìn)行數(shù)據(jù)復(fù)制時(shí),先設(shè)置好源數(shù)據(jù)指針和目標(biāo)指針,然后使用塊復(fù)制指令LDMIA/STMIA、LDMIB/STMIB、LDMDA/STMDA、LDMDB/STMDB進(jìn)行讀取和存儲(chǔ)。
在進(jìn)行堆棧操作時(shí),先設(shè)置好堆棧指針(SP),然后使用堆棧尋址指令STMFD/LDMFD、STMED/LDMED、STMFA/LDMFA、STMEA/LDMEA實(shí)現(xiàn)堆棧操作。另外,加載/存儲(chǔ)指令還包括數(shù)據(jù)交換指令SWP,格式如下:
SWP{cond}{B}Rd,Rm,[Rn]
其中B為可選后綴,若有B則交換字節(jié),否則交換32位字。SWP指令用于將一個(gè)內(nèi)存字單元(該單元的地址存放在寄存器Rn中)的內(nèi)容讀取到寄存器Rd中,并將寄存器Rm中的內(nèi)容寫入到該內(nèi)存字單元。若Rd和Rm相同,則指令表示交換該寄存器和內(nèi)存單元的內(nèi)容。如指令“SWPR1,R2,[R3]”表示將R3指向的存儲(chǔ)單元內(nèi)容讀取到R1中,并將R2中的內(nèi)容寫入R3指向的存儲(chǔ)單元。
SWPB指令只從內(nèi)存單元取出一個(gè)字節(jié)到Rd中,將Rd的高24位設(shè)置為0,同時(shí)將Rm中的低8位數(shù)值寫入到該內(nèi)存單元。
5.協(xié)處理器指令
ARM微處理器支持16個(gè)協(xié)處理器,用于各種協(xié)處理操作。在程序的執(zhí)行過程中,每個(gè)協(xié)處理器只執(zhí)行針對(duì)自身的協(xié)處理指令,而忽略其他處理器的指令。當(dāng)一個(gè)協(xié)處理器不能執(zhí)行屬于自身的協(xié)處理指令時(shí),會(huì)產(chǎn)生未定義指令異常中斷。
協(xié)處理器指令主要用于:ARM處理器初始化、ARM協(xié)處理器的數(shù)據(jù)處理操作;ARM處理器的寄存器和ARM協(xié)處理器的寄存器之間的數(shù)據(jù)傳送;ARM協(xié)處理器的寄存器和存儲(chǔ)器之間的數(shù)據(jù)傳送。ARM協(xié)處理指令包括以下5條指令:
(1)
CDP——協(xié)處理器數(shù)據(jù)操作指令。
格式:CDP{cond}coproc,opcode1,CRd,CRn,CRm,{opcode2}
cond:條件指令執(zhí)行條件碼,當(dāng)cond忽略或?yàn)?b1111時(shí),指令無條件執(zhí)行。
coproc:協(xié)處理器編碼,標(biāo)準(zhǔn)名為pn,n的范圍為0~15。
opcode1:協(xié)處理器的操作碼。
CRd:作為目標(biāo)寄存器的協(xié)處理器寄存器。
CRn:存放第一個(gè)操作數(shù)的協(xié)處理器寄存器。
CRm:存放第二個(gè)操作數(shù)的協(xié)處理器寄存器。
opcode2:可選的協(xié)處理器的操作碼。
如指令:
CDPp2,4,C3,C4,C1,7;完成對(duì)協(xié)處理器p2的初始化其中操作碼1為4,操作碼2為7,目標(biāo)寄存器為C3,原操作數(shù)寄存器為C4、C1。該類指令操作是由協(xié)處理器完成的,不涉及ARM存儲(chǔ)器和內(nèi)存單元。
(2)
LDC——協(xié)處理器數(shù)據(jù)讀取指令。
格式:LDC{cond}{L}coproc,CRd,<地址>
其中L為可選后綴,指明是長(zhǎng)整數(shù)傳送,如雙精度數(shù)據(jù)的傳送。
LDC指令用于將指令中地址所指向的存儲(chǔ)器中的字?jǐn)?shù)據(jù)讀取到協(xié)處理器寄存器中。
如指令:
LDCp2,C3,[R4#5];將內(nèi)存單元(R4+5)所對(duì)應(yīng)的數(shù)值傳送到協(xié)處理器p2的C3寄存器中
(3)
STC——協(xié)處理器數(shù)據(jù)寫入指令。
格式:STC{cond}{L}coproc,CRd,<地址>
STC指令用于將協(xié)處理器寄存器中的數(shù)據(jù)寫入到指令中指定地址所指向的存儲(chǔ)器中。
如指令:
STCp2,C3,[R4#5];將協(xié)處理器p2的C3寄存器中的內(nèi)容傳送到內(nèi)存單元(R4+5)
(4)
MCR——ARM寄存器到協(xié)處理寄存器的數(shù)據(jù)傳輸指令。
格式:MCR{cond}coproc,opcode1,CRd,CRn,CRm,{opcode2}
MCR指令用于將ARM處理寄存器中的數(shù)據(jù)傳送到協(xié)處理器寄存器中。
(5)
MRC——協(xié)處理寄存器到ARM寄存器的數(shù)據(jù)傳輸指令。
格式:MRC{cond}coproc,opcode1,CRd,CRn,CRm,{opcode2}
MRC指令用于將協(xié)處理器寄存器中的數(shù)據(jù)傳送到ARM處理寄存器中。
6.異常產(chǎn)生指令
異常產(chǎn)生指令包括兩條:SWI和BKPT。
(1)
SWI——軟中斷指令。
格式:SWI{cond}immed_24
SWI指令用于產(chǎn)生軟中斷,從而實(shí)現(xiàn)從用戶模式切換到管理模式,并將CPSR保存到管理模式下的SPSR中,然后程序跳轉(zhuǎn)到SWI異常入口。
另外,用戶請(qǐng)求的服務(wù)類型由指令中的24位立即數(shù)指定,參數(shù)通過寄存器傳遞;若24位立即數(shù)被忽略,則用戶請(qǐng)求的服務(wù)類型由寄存器R0的數(shù)值決定。
(2)
BKPT——斷點(diǎn)中斷指令。
格式:BKPTimmed_16
BKPT用于產(chǎn)生軟件斷點(diǎn)中斷,供軟件調(diào)試程序使用,用來保存額外的斷點(diǎn)信息。4.1.4Thumb指令集
Thumb指令集是ARM指令集的功能子集,指令長(zhǎng)度為16位,它沒有改變ARM體系底層的程序設(shè)計(jì)模型,只是在該模型上增加了一些限制條件。與32位等價(jià)代碼相比,Thumb指令集在保留了32位代碼優(yōu)勢(shì)的同時(shí),大大節(jié)省了系統(tǒng)的存儲(chǔ)空間。
在編寫Thumb指令時(shí),要先使用偽指令CODE16聲明;而在編寫ARM指令時(shí),使用偽指令CODE32聲明。在ARM指令下,可以通過BX指令跳轉(zhuǎn)到Thumb指令,以切換處理器的工作狀態(tài)。Thumb指令沒有提供訪問CPSR/SPSR寄存器的指令,處理器根據(jù)CPSR中的T位來決定指令類型:當(dāng)T位為0時(shí),指令為ARM指令;當(dāng)T位為1時(shí),指令為Thumb指令。
根據(jù)Thumb指令集的功能可以將其分為兩大類:存儲(chǔ)器訪問指令和數(shù)據(jù)處理指令。由于從功能上來講,Thumb指令是ARM指令的子集,在了解了ARM指令的基礎(chǔ)上很容易理解Thumb指令,所以這里只對(duì)其作一個(gè)簡(jiǎn)單的介紹。
1.存儲(chǔ)器訪問指令
Thumb指令集的存儲(chǔ)器訪問指令包括3類:
(1)單寄存器加載/存儲(chǔ)指令LDR/STR,它可以將R0~R7的任何子集進(jìn)行加載/存儲(chǔ)。同ARM中的LDR/STR指令一樣,也可以通過加后綴B、H、SB、SH實(shí)現(xiàn)對(duì)無符號(hào)字節(jié)、無符號(hào)半字、有符號(hào)字節(jié)和有符號(hào)半字的加載/存儲(chǔ)操作。
(2)批量加載/存儲(chǔ)指令LDMIA/STMIA。加上后綴IA,即在每次傳送完數(shù)據(jù)后,地址加4。
(3)堆棧處理指令PUSH、POP。PUSH實(shí)現(xiàn)低寄存器和可選寄存器LR的入棧操作;POP實(shí)現(xiàn)低寄存器和可選寄存器PC的出棧操作;堆棧地址由SP寄存器設(shè)置。
格式:PUSH{register,[LR]}
POP{register,[PC]}
2.數(shù)據(jù)處理指令
1)數(shù)據(jù)傳送指令
(1)
MOV——數(shù)據(jù)傳送指令。
格式:MOVRd,Rm
(2)
MVN——數(shù)據(jù)求反傳送指令。
格式:MVNRd,Rm;先將Rm取反,再傳送到Rd中
(3)
NEG——數(shù)據(jù)取負(fù)傳送指令。
格式:NEGRd,Rm;先將Rm乘
-1,再傳送到Rd中
2)算術(shù)邏輯指令
算術(shù)邏輯指令包括:加法運(yùn)算指令A(yù)DD、減法運(yùn)算指令SUB、帶進(jìn)位加法指令A(yù)DC、帶進(jìn)位減法指令SBC、乘法指令MUL、邏輯與指令A(yù)ND、邏輯或指令ORR、邏輯異或指令EOR、位清除指令BIC、算術(shù)右移指令A(yù)SR、邏輯左移指令LSL、邏輯右移指令LSR和循環(huán)右移指令ROR。格式與ARM指令中的算術(shù)邏輯指令相同,這里就不再贅述。
3)比較指令
比較指令包括:比較指令CMP、負(fù)數(shù)比較指令CMN和位測(cè)試指令TST。
Thumb指令集也包括跳轉(zhuǎn)指令B、帶返回的跳轉(zhuǎn)指令BL、帶切換的跳轉(zhuǎn)指令BX以及軟中斷指令SWI。
3.?ARM指令集與Thumb指令集的區(qū)別
Thumb指令集大多數(shù)是常用的32位ARM指令的子集,壓縮成16位寬。它沒有協(xié)處理器指令、信號(hào)量指令以及訪問CPSR或SPSR的指令,也沒有乘加指令以及64位乘法指令等,且第二操作數(shù)受到限制。除了條件指令B具有條件執(zhí)行功能外,其他指令均是無條件執(zhí)行。Thumb指令一般是不能預(yù)測(cè)的,而ARM指令是可以預(yù)測(cè)的。Thumb指令用的是雙地址形式(目標(biāo)寄存器和原寄存器是同一個(gè)寄存器),而ARM指令用的是3地址形式。對(duì)Thumb指令來說,訪問寄存器R0~R7是透明的,訪問寄存器R8~R15則是受到限制的(只有MOV和ADD指令能直接訪問寄存器R8~R15),而在ARM狀態(tài)下,對(duì)R0~R15的訪問都是透明的。
4.2.1ARM匯編語言格式簡(jiǎn)介
1.?ARM匯編語言的語句格式
ARM匯編語言的語句格式如下:
{符號(hào)}{指令或偽指令};{注釋}
4.2ARM匯編語言設(shè)計(jì)符號(hào)必須位于一行的代碼開頭,并且不能包含空格。指令或偽指令的助記符必須全部采用大寫或小寫字母,不能大小寫混用。如果一條語句很長(zhǎng),可以將其分為若干行,上下行之間通過上一行末尾的“/”符號(hào)來連接。
2.?ARM匯編語言中的符號(hào)
在ARM匯編語言中使用符號(hào)來代替地址、常量和變量,以增加程序的可讀性。符號(hào)的命名規(guī)則如下:
符號(hào)由大小寫字母、數(shù)字、下劃線組成。
符號(hào)有大小寫之分。
符號(hào)在其作用范圍內(nèi)是唯一的,即在其作用范圍內(nèi)不可以有同名的符號(hào)。
自定義的符號(hào)名不能與系統(tǒng)保留字相同,不能與指令或偽指令同名。另外,符號(hào)代表地址時(shí)又稱為標(biāo)號(hào)。當(dāng)標(biāo)號(hào)以數(shù)字開頭時(shí),其作用范圍就是當(dāng)前段,稱為局部標(biāo)號(hào)。只有局部標(biāo)號(hào)以數(shù)字開頭,其他符號(hào)都不能以數(shù)字開頭。符號(hào)包括變量、常量、地址標(biāo)號(hào)、局部標(biāo)號(hào)等。
1)變量
ARM匯編語言有數(shù)字變量、邏輯變量和字符串變量3種。變量的值在程序的運(yùn)行過程中可以改變,但是類型不能改變。
(1)數(shù)字變量:用于在程序運(yùn)行過程中保存數(shù)字值,但數(shù)字值的大小不能超出數(shù)字變量所能表示的范圍。
(2)邏輯變量:用于在程序運(yùn)行過程中保存邏輯值,邏輯值只有true和false。
(3)字符串變量:用于在程序中保存一個(gè)字符串,字符串的長(zhǎng)度不能超過字符串變量所能表示的范圍。
2)常量
常量是指在程序運(yùn)行過程中不能被改變的量,相對(duì)于變量而言,常量也包括三種類型:數(shù)字常量、邏輯常量和字符串常量。它們都是固定的,不能被程序改變。數(shù)字常量一般是32位的整數(shù),當(dāng)為無符號(hào)數(shù)時(shí)其取值范圍為0~231-1;
當(dāng)為有符號(hào)數(shù)時(shí),其取值范圍為-231~231-1。
3)地址標(biāo)號(hào)
地址標(biāo)號(hào)是表示程序中指令或者數(shù)據(jù)的地址,根據(jù)地址標(biāo)號(hào)的生成方法可以分為絕對(duì)地址標(biāo)號(hào)、基于PC的標(biāo)號(hào)和基于寄存器的標(biāo)號(hào)3種。
(1)絕對(duì)地址標(biāo)號(hào):一個(gè)32位的數(shù)字量,尋址范圍為0~231-1。
(2)基于PC的標(biāo)號(hào):用于表示跳轉(zhuǎn)指令的目標(biāo)地址,或代碼中所嵌入數(shù)據(jù)。PC標(biāo)號(hào)通常位于目標(biāo)指令或數(shù)據(jù)定義偽指令之前。
(3)基于寄存器的標(biāo)號(hào):常用于訪問數(shù)據(jù)段中的數(shù)據(jù),在匯編中用MAP、FIELD和EQU偽指令來定義。
4)局部標(biāo)號(hào)
局部標(biāo)號(hào)主要在局部范圍內(nèi)使用,它由兩部分組成:前面是一個(gè)0~99的數(shù)字,后面跟一個(gè)表示該局部變量作用范圍的符號(hào)。
3.?ARM匯編語言中的表達(dá)式和運(yùn)算符
在ARM匯編語言中也經(jīng)常使用表達(dá)式,表達(dá)式一般由數(shù)值、符號(hào)、括號(hào)、運(yùn)算符組成。其運(yùn)算次序的優(yōu)先級(jí)如下:
括號(hào)運(yùn)算符的優(yōu)先級(jí)最高。
相鄰的單目運(yùn)算符的執(zhí)行順序?yàn)橛捎抑磷螅覇文窟\(yùn)算符的優(yōu)先級(jí)高于其他運(yùn)算符。
優(yōu)先級(jí)相同的雙目運(yùn)算符的執(zhí)行順序?yàn)橛捎抑磷蟆?/p>
常用的表達(dá)式有數(shù)字表達(dá)式、邏輯表達(dá)式和字符串表達(dá)式,與它們相關(guān)的運(yùn)算符及其表達(dá)的含義如表4.3所示。
表4.3常用的表達(dá)式和運(yùn)算符4.2.2ARM匯編語言的程序設(shè)計(jì)
ARM匯編語言以程序段為單位組織代碼,段是相對(duì)獨(dú)立的、不可分割的并且具有獨(dú)立名稱的指令或者數(shù)字序列。段可以分為代碼段和數(shù)據(jù)段,代碼段存放執(zhí)行代碼,數(shù)據(jù)段存放執(zhí)行代碼時(shí)需要用到的數(shù)據(jù)。一個(gè)ARM匯編程序至少應(yīng)該有一個(gè)代碼段,大的程序可以分割成幾個(gè)代碼段和數(shù)據(jù)段,在編譯連接時(shí)最終形成一個(gè)可執(zhí)行的映像文件,該文件包括以下幾個(gè)部分:
一個(gè)或多個(gè)代碼段,代碼段的屬性為只讀。
零個(gè)或多個(gè)包含初始值的數(shù)據(jù)段,數(shù)據(jù)段的屬性為可讀寫。
零個(gè)或多個(gè)不包含初始值的數(shù)據(jù)段,數(shù)據(jù)段的屬性為可讀寫。
鏈接器根據(jù)一定的規(guī)則將各個(gè)段安排在存儲(chǔ)器中相應(yīng)的位置,因此源程序中段之間的相鄰關(guān)系與執(zhí)行的映像文件中段之間的相鄰關(guān)系一般不相同。通過下面一個(gè)簡(jiǎn)單的例子說明ARM匯編語言源程序的基本結(jié)構(gòu)代碼如下:
AREAExample1,CODE,READONLY
ENTRY
START
MOVR0,#0x104
MOVR1,#200
SUBR0,R0,R1
…
END在上面的例子中,AREA偽指令表明了一個(gè)段的開始,并定義該段的名稱為Example,屬性為代碼段,且只讀。ENTRY偽指令標(biāo)識(shí)程序的入口點(diǎn),ARM程序至少要包含一個(gè)ENTRY。START為地址標(biāo)號(hào)。接下來是指令序列,該指令實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的減法運(yùn)算。程序的末尾為END偽指令,告訴編譯器源文件結(jié)束,每個(gè)匯編程序都必須有一條END偽指令。
在匯編語言中通常會(huì)對(duì)子程序進(jìn)行調(diào)用。子程序的調(diào)用一般是通過BL指令完成的,格式如下:
BL子程序名在執(zhí)行該指令時(shí),首先將子程序的返回地址存放在LR寄存器中,同時(shí)將程序計(jì)數(shù)器PC指向子程序的入口點(diǎn);在子程序執(zhí)行完畢需要返回時(shí),將LR中的返回地址傳送到PC中即可。在調(diào)用子程序的同時(shí),可以通過寄存器R0~R3來完成參數(shù)的傳遞和從子程序返回運(yùn)算結(jié)果。匯編語言的子程序調(diào)用示例如下:
AREAExample1,CODE,READONLY
ENTRY
START
MOVR0,#0x104
MOVR1,#200
SUBR0,R0,R1
BLram_loop
…
ram_loop;子程序
…
MOVPC,LR
…
END
本章主要介紹ARM處理器下C語言編程的優(yōu)化,C語言與匯編語言的混合編程以及ARMC/C++
編譯器的基本知識(shí)。4.3ARMC語言設(shè)計(jì)4.3.1C語言編程技術(shù)
ARM和ThumbC編譯器采用的都是標(biāo)準(zhǔn)ANSIC編譯器,它能夠產(chǎn)生精簡(jiǎn)而有效的機(jī)器指令,但是若能在寫C代碼的時(shí)候注意一些編程技巧,則能夠?qū)Τ绦蜻M(jìn)行進(jìn)一步的優(yōu)化,從而有效地降低C語言編譯后代碼的長(zhǎng)度和執(zhí)行時(shí)間,提高代碼的執(zhí)行效率。下面將介紹幾種ARM處理器下C語言編程效率優(yōu)化的方法。
1.變量的定義
由于ARM采用32位4字節(jié)來存儲(chǔ)數(shù)據(jù),因此在定義變量的時(shí)候一定要注意空間的分布。最好是把所有相同類型的變量放在一起定義,這樣會(huì)避免造成存儲(chǔ)空間的浪費(fèi)。另外,對(duì)于局部變量,盡量使用32位的變量類型。因?yàn)榫幾g器在把局部變量分配給內(nèi)部寄存器時(shí),每個(gè)變量占用一個(gè)32位寄存器,因此使用char、short型變量不但沒有提高程序的執(zhí)行效率,反而會(huì)需要更多指令。
2.除法和求模的優(yōu)化
ARM在硬件上不支持除法指令,編譯器是通過調(diào)用C庫(kù)函數(shù)來實(shí)現(xiàn)除法運(yùn)算的。但直接利用C庫(kù)函數(shù)進(jìn)行除法運(yùn)算,會(huì)消耗較多的軟件運(yùn)行時(shí)間,執(zhí)行速度比較慢,因此應(yīng)盡量避免使用除法和求模運(yùn)算。
(1)在某些程序設(shè)計(jì)中,可以把除法改寫為乘法。如:(x/y)
>
z,在已知y是正數(shù)而且y×z是整數(shù)的情況下,就可以寫為x
>
(z
×
y)。
(2)盡可能使用2的次方作為除數(shù),編譯器使用移位操作完成除法。在程序設(shè)計(jì)中,使用無符號(hào)型的除法要快于符號(hào)型的除法。
(3)對(duì)于求模運(yùn)算也可以采用其他的語句來進(jìn)行替換,例如代碼:
Count=(Count+increment)%size;
可以用以下代碼替換:
Count+=increment;
if(size<=Count)
{
Count-=size;
}
(4)對(duì)于一些特殊的除法和求余運(yùn)算,采用查找表的方法也可以獲得很好的運(yùn)行效果。
3.循環(huán)體的優(yōu)化
(1)如果循環(huán)體內(nèi)存在邏輯判斷,并且循環(huán)次數(shù)很大,最好將邏輯判斷移到循環(huán)體的外面。在多重循環(huán)中應(yīng)將最長(zhǎng)的循環(huán)放在最內(nèi)層,這樣可以減少PC指針從內(nèi)循環(huán)跳到外循環(huán)的次數(shù)。
(2)程序中的循環(huán)結(jié)束條件應(yīng)是“遞減到0”的循環(huán),結(jié)束條件盡量簡(jiǎn)單,以減少指令和寄存器的使用。
(3)如果循環(huán)體至少執(zhí)行一次,則優(yōu)先選用do-while。如果一個(gè)循環(huán)體只循環(huán)幾次,可以將其展開。因?yàn)楫?dāng)循環(huán)展開后,不需要循環(huán)計(jì)數(shù)器和相關(guān)的跳轉(zhuǎn)語句,雖然代碼的長(zhǎng)度有所增加,但是執(zhí)行的效率更高。
4.條件執(zhí)行的優(yōu)化
(1)在ARMC語言程序設(shè)計(jì)中,有符號(hào)變量應(yīng)盡量采用x<0、x>=0、x==0和x!=0的關(guān)系運(yùn)算;對(duì)于無符號(hào)變量應(yīng)采用x==0、x!=0(或者x>0)的關(guān)系運(yùn)算符。編譯器都可以對(duì)條件執(zhí)行進(jìn)行優(yōu)化。
(2)
ARM中的條件執(zhí)行是通過對(duì)運(yùn)算結(jié)果標(biāo)志位進(jìn)行判斷實(shí)現(xiàn)的。在ARMC語言程序設(shè)計(jì)中,條件判斷應(yīng)當(dāng)盡量采用“與0比較”的形式,因?yàn)樵贏RMC語言程序中,如果運(yùn)算結(jié)果是與0作比較,編譯器會(huì)移去比較指令,通過一條帶標(biāo)志位指令實(shí)現(xiàn)運(yùn)算和判斷。
(3)對(duì)于程序設(shè)計(jì)中的條件語句,應(yīng)盡量簡(jiǎn)化if和else判斷條件。與傳統(tǒng)的C語言程序設(shè)計(jì)有所不同,在ARMC語言程序設(shè)計(jì)中,關(guān)系表述中類似的條件應(yīng)該集中在一起,使編譯器能夠?qū)ε袛鄺l件進(jìn)行優(yōu)化。4.3.2C語言與匯編語言混合編程
在嵌入式系統(tǒng)開發(fā)中,目前使用的主要編程語言是C語言和匯編語言。C語言結(jié)構(gòu)好,表達(dá)能力強(qiáng),有大量的支持庫(kù),功能豐富,使用靈活,開發(fā)效率高;但是在一些實(shí)時(shí)控制的場(chǎng)合,匯編語言有著不可替代的作用。匯編語言是一種面向機(jī)器的語言,具有運(yùn)行速度快,占用存儲(chǔ)空間小,可直接對(duì)硬件進(jìn)行控制的特點(diǎn),因此在實(shí)際開發(fā)和軟件編制過程中,我們常常將C和匯編語言進(jìn)行混合編程,充分利用兩種語言的優(yōu)勢(shì),使開發(fā)和編程工作達(dá)到事半功倍的效果。
1.在C語言中內(nèi)嵌匯編
在ARMC語言中內(nèi)嵌匯編使用的標(biāo)記是_asm或asm關(guān)鍵字,用法如下:
_asm
{
instruction[;instruction]
…
[instruction]
}
asm("instruction[;instruction]");需要注意的問題有:
(1)內(nèi)嵌的匯編語句可以用“;”結(jié)束,也可以用換行符結(jié)束,一行中可以有多個(gè)匯編語句,相互間用分號(hào)分隔,不能跨行書寫;內(nèi)嵌匯編語句的分號(hào)不是注釋的開始,要對(duì)語句進(jìn)行注釋時(shí),應(yīng)使用C語言的注釋。
(2)一般不要直接指定物理寄存器,而應(yīng)讓編譯器進(jìn)行分配;在使用物理寄存器時(shí),不要使用過于復(fù)雜的C語言表達(dá)式,以避免物理寄存器沖突。
(3)
R12和R13可能被編譯器用來存放中間編譯結(jié)果,R0~R3、R12和R14可能在計(jì)算表達(dá)式值時(shí)用于子程序調(diào)用,因此要避免直接使用這些物理寄存器。
2.在匯編程序中訪問C程序變量
匯編程序可以通過地址間接地訪問C程序中聲明的全局變量。具體訪問方法是:首先,使用IMPORT偽指令聲明該全局變量;用LDR指令讀取該全局變量的內(nèi)存地址,然后根據(jù)該數(shù)據(jù)的類型使用相應(yīng)的LDR指令讀取該全局變量的值,再使用相應(yīng)的STR指令修改后將值賦予該全局變量。程序如下:
AREAasmfile,CODE,READONLY
EXPORTasmAdd
IMPORTglobV1 ;聲明變量
asmAdd
LDRR0,=globV1 ;將內(nèi)存地址讀入到R0中
LDRR1,[R0] ;將數(shù)據(jù)讀入到R1中
MOVR2,#2
MULR3,R1,R2
STRR3,[R0] ;修改后將值賦予變量
MOVPC,LR
END
3.匯編語言與C語言之間的相互調(diào)用
匯編語言與C語言在進(jìn)行相互調(diào)用的時(shí)候要遵守相應(yīng)的ATPCS規(guī)則,以保證程序調(diào)用時(shí)參數(shù)的正確傳送。
1)在C程序中調(diào)用匯編程序
在C程序中調(diào)用匯編程序,一是要在C程序中聲明函數(shù)原型,并加extern關(guān)鍵字;二是要在匯編程序中使用EXPORT偽指令導(dǎo)出函數(shù)名,并用該函數(shù)名作為匯編代碼段的標(biāo)識(shí),以使該代碼段可以被其他程序調(diào)用,最后用MOVPC,LR返回。
2)在匯編程序中調(diào)用C程序
在匯編程序中調(diào)用C程序,
需要在匯編中使用偽指令I(lǐng)MPORT聲明將要調(diào)用的C程序,然后將C程序的代碼放在一個(gè)獨(dú)立的C文件中進(jìn)行編譯,剩下的工作由連接器來處理。4.3.3ARMC/C++?編譯器
ARMC/C++
編譯器可以被使用在UNIX和Windows/MS-DOS環(huán)境下。ARMC++
編譯器可以編譯成多種格式的C/C++
源代碼,其中包括ANSIC、EC++
和C++。表4.4列出的是ARM中常見的C/C++
編譯器。
表4.4ARM中常見的C/C++
編譯器在介紹這些編譯器之前,必須要明白什么是匯編/編譯過程。所謂匯編/編譯過程就是將用ANSIC或匯編語言編寫的程序編譯成16位的Thumb或32位的ARM指令代碼。匯編和編譯工作是由集成到ADS中的開發(fā)工具完成的,用戶也可以通過ADS提供的命令行界面直接調(diào)用它們。了解這些開發(fā)工具,將有助于開發(fā)出高質(zhì)量的產(chǎn)品。
1.?ARMC編譯器armcc
armcc編譯器用于將遵循ANSIC編寫的程序編譯成32位的ARM指令代碼,它通過了PlumHallCValidationSuite為ANSIC設(shè)計(jì)的一致性測(cè)試。
armcc編譯器最基本的用法為
armcc[options]file1file2…filen
其中,
options:編譯器所需要的選項(xiàng),它控制了編譯的過程和生成文件的屬性。
file1,file2…filen:需要處理的源文件名字。
表4.5常用的options選項(xiàng)對(duì)于詳細(xì)的armcc的選項(xiàng)和用法,可以在ADS命令控制臺(tái)環(huán)境下,使用armcc–help命令來查看。
ARMC/C++編譯器通過文件后綴名來區(qū)分文件的類型。ARMC/C++編譯器支持和產(chǎn)生以下幾種格式的文件。
filename.c:ARMC編譯器將*.C格式的文件作為源文件;ARMC++編譯器將*.C、*.CPP、*.CP、*.C++、*.CC格式的文件都作為源文件。
filename.h:頭文件。
filename.o:編譯器輸出的ELF格式的目標(biāo)文件。
filename.s:ARM或者Thumb格式的匯編代碼文件。
filename.lst:錯(cuò)誤及警告信息的列表文件。
ARM編譯器支持的各種語法pragmas及其含義如表4.6所示。
表4.6ARM編譯器支持的各種語法及其含義
2.?ThumbC編譯器tcc
tcc編譯器將ANSIC源代碼編譯成16位的Thumb指令代碼,其用法類似于armcc。該編譯器也通過了PlumHallCValidationSuite為ANSIC設(shè)計(jì)的一致性測(cè)試。
3.匯編器armasm
armasm是ARM和Thumb的匯編器,它對(duì)用ARM匯編語言和Thumb匯編語言編寫的源代碼進(jìn)行匯編。
4.?ARMC++編譯器armcpp
armcpp編譯器用來將ISOC++
或EC++
編譯成32位的ARM指令代碼。
5.?ThumbC++編譯器tcpp
tcpp編譯器用來將ISOC++或EC++編譯成16位的Thumb指令代碼。
對(duì)于初學(xué)者來說,在ARM入門過程中,可以只對(duì)這些命令工具做一些了解,不必花太多的精力來研究每一條指令的選項(xiàng)或者用法。ADS中提供的GUI界面可以滿足大多數(shù)用戶配置匯編/編譯過程的要求。
本節(jié)主要介紹ARM開發(fā)軟件ADS(ARMDeveloperSuite)。通過學(xué)習(xí)如何在CodeWarriorIDE集成開發(fā)環(huán)境下編寫、編譯一個(gè)示例工程,使讀者掌握在ADS軟件平臺(tái)下開發(fā)用戶應(yīng)用程序的方法。本節(jié)還描述了使用AXD調(diào)試工程的方法,使讀者對(duì)于調(diào)試工程有個(gè)初步的理解,為進(jìn)一步使用和掌握調(diào)試工具起到拋磚引玉的作用。4.4ADS開發(fā)平臺(tái)4.4.1ADS開發(fā)平臺(tái)的特點(diǎn)
ARMADS的全稱為ARMDeveloperSuite,是ARM公司推出的新一代ARM集成開發(fā)工具。現(xiàn)在ADS的最新版本是1.2,它取代了早期的ADS1.1和ADS1.0。ADS可以安裝在WindowsNT/2000/98/95/XP等操作系統(tǒng)中使用。
ADS由命令行開發(fā)工具、ARM實(shí)時(shí)庫(kù)、GUI開發(fā)環(huán)境(CodeWarrior和AXD)、實(shí)用程序和支持軟件組成。有了這些部件,用戶就可以為ARM系列的RISC處理器編寫和調(diào)試自己開發(fā)的應(yīng)用程序了。4.4.2CodeWarrior軟件的使用方法
CodeWarriorforARM是一套集編輯、編譯、連接于一體的集成開發(fā)工具,它充分發(fā)揮了ARMRISC的優(yōu)勢(shì),使產(chǎn)品開發(fā)人員能夠很好地應(yīng)用尖端的片上系統(tǒng)技術(shù)。該工具是專為基于ARMRISC的處理器而設(shè)計(jì)的,它可加速并簡(jiǎn)化嵌入式開發(fā)過程中的每一個(gè)環(huán)節(jié),使得開發(fā)人員只需通過一個(gè)集成軟件開發(fā)環(huán)境就能研制出ARM產(chǎn)品,在整個(gè)開發(fā)周期中,開發(fā)人員無需離開CodeWarrior開發(fā)環(huán)境,因此節(jié)省了在操作工具上所花的時(shí)間,使得開發(fā)人員有更多的精力投入到代碼編寫上來。
CodeWarriorIDE為用戶提供了以下功能:
(1)源代碼編輯器,集成在CodeWarriorIDE的瀏覽器中,能夠根據(jù)語法格式,使用不同的顏色顯示代碼。
(2)源代碼瀏覽器,它保存了在源碼中定義的所有符號(hào),能夠使用戶在源碼中快速方便的跳轉(zhuǎn)。
(3)查找和替換功能,用戶可以在多個(gè)文件中,利用字符串通配符,進(jìn)行字符串的搜索和替換。
(4)文件比較功能,方便用戶比較路徑中的不同文本文件的內(nèi)容。
1.建立一個(gè)工程
工程將多個(gè)源碼文件組織在一起,并決定最終生成文件的格式、存放路徑等。
建立工程的步驟如下:
(1)首先在CodeWarrior下新建一個(gè)工程。方法有兩種,可以在工具欄中單擊New按鈕,也可以選擇File→New命令,彈出的對(duì)話框如圖4.2所示。圖4.2在CodeWarrior下新建一個(gè)工程my_first該對(duì)話框?yàn)橛脩籼峁┝?種可選的工程類型:
ARMExcutableImage:用于由ARM指令的代碼生成一個(gè)ELF格式的可執(zhí)行的映像文件。
ARMObjectLibrary:用于由ARM指令的代碼生成一個(gè)armar格式的目標(biāo)文件庫(kù)。
EmptyProject:用于創(chuàng)建一個(gè)不包含任何庫(kù)或者源文件的工程。
MakefileImporterWizard:用于將VisualC的nmake或者GNUmake文件轉(zhuǎn)入到CodeWarriorIDE工程文件。
ThumbARMExcutableImage:用于由ARM指令和Thumb指令的混和代碼生成一個(gè)可執(zhí)行的ELF格式的映像文件。
ThumbExcutableimage:用于由Thumb指令創(chuàng)建一個(gè)可執(zhí)行的ELF格式的映像文件。
ThumbObjectLibrary:用于由Thumb指令的代碼生成一個(gè)armar格式的目標(biāo)文件庫(kù)。我們?cè)谶@里選擇ARMExecutableImage,在“Projectname:”中輸入工程文件名,本例以ADS1.2中自帶的example中的sorts為例,輸入“my_first”,點(diǎn)擊“Location:”文本框的“Set”按鈕,瀏覽選擇想要保存該工程的路徑(本例為“D:\work”),將這些設(shè)置好之后,點(diǎn)擊“確定”,即可創(chuàng)建一個(gè)新的名為“my_first”的工程。
這個(gè)時(shí)候會(huì)出現(xiàn)“my_first.mcp”窗口,如圖4.3所示,同時(shí)會(huì)在D:\work目錄下創(chuàng)建一個(gè)工程目錄my_first,而my_first.mcp會(huì)出現(xiàn)在D:\work\myfirst目錄中,如圖4.4所示。
圖4.3新建的my_first.mcp文檔窗口的工作區(qū)
圖4.4新建工程的保存目錄在新建的my_first.mcp文檔窗口中,大致可以分為四個(gè)區(qū)域,如圖4.3所示。
在出現(xiàn)的my_first.mcp的窗口中,可以看到默認(rèn)的目標(biāo)調(diào)試環(huán)境是DebugRel,右側(cè)的下拉列表框中還有另外兩個(gè)可用的目標(biāo)調(diào)試環(huán)境,分別為Release和Debug。
3個(gè)目標(biāo)調(diào)試系統(tǒng)的含義分別為:
DebugRel:在生成
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 商業(yè)生態(tài)下小微企業(yè)營(yíng)銷策略研究
- 辦公環(huán)境中創(chuàng)新思維的激發(fā)與運(yùn)用
- 2024至2030年亞克力涂料項(xiàng)目投資價(jià)值分析報(bào)告
- 2024至2030年不銹鋼立式浸燙池項(xiàng)目投資價(jià)值分析報(bào)告
- 實(shí)驗(yàn)室建設(shè)的環(huán)保理念與綠色施工管理
- 2024年飛行模擬系統(tǒng)項(xiàng)目可行性研究報(bào)告
- 游戲化美術(shù)課程設(shè)計(jì)
- 2024年版擔(dān)保公司貸款協(xié)議協(xié)議范本版B版
- 2024年高端機(jī)床設(shè)備進(jìn)口與合作合同
- 二零二五年度房地產(chǎn)開發(fā)法務(wù)與合同管理準(zhǔn)則283篇
- 機(jī)動(dòng)車駕駛員考試《科目一》試題與參考答案(2024年)
- 2024年四年級(jí)英語上冊(cè) Module 8 Unit 2 Sam is going to ride horse說課稿 外研版(三起)
- 重慶南開中學(xué)2025屆生物高二上期末聯(lián)考試題含解析
- 高中地理人教版(2019)必修第一冊(cè) 全冊(cè)教案
- X市科協(xié)領(lǐng)導(dǎo)班子2021年工作總結(jié)
- 2024年新人教版七年級(jí)上冊(cè)地理課件 第二章 地圖 第二節(jié) 地形圖的判讀
- 2024至2030年中國(guó)汽摩配行業(yè)發(fā)展?fàn)顩r及競(jìng)爭(zhēng)格局分析報(bào)告
- 濰柴天然氣發(fā)動(dòng)機(jī)結(jié)構(gòu)及工作原理
- 國(guó)家開放大學(xué)《理工英語2》形考任務(wù)1-8參考答案
- 建筑公司證書津貼支付管理辦法
- 2024年電大勞動(dòng)與社會(huì)保障法期末考試題庫(kù)及答案
評(píng)論
0/150
提交評(píng)論