中文版-Plan9彙編器手冊-A Manual for the Plan 9 assembler

A Manual for the Plan 9 assembler

英文原鏈接:A Quick Guide to Go’s Assembler - The Go Programming Language

彙編器手冊:A Manual for the Plan 9 assembler

編譯器手冊:Plan 9 C Compilers

每個MIPS,SPARC,Intel 386,AMD64,Power PC和ARM都有一個彙編器。 68020彙編器2a(不再分發)是最古老的原型,並且在許多方面都是原型。 彙編程序實際上只是單個程序的變體:它們共享許多屬性,例如指令操作數的從左到右分配順序,以及諸如MOVE之類的宏指令合成,以隱藏機器的負載和存儲結構的特殊性。 爲了保持具體,本手冊的第一部分專門針對68020。最後是對其他彙編程序之間差異的描述。

羅伯·派克(Rob Pike)撰寫的文件“如何使用Plan 9 C編譯器”是本手冊的前提。

寄存器

彙編器中的所有預定義符號均爲大寫。數據寄存器爲R0至R7;地址寄存器是A0到A7;浮點寄存器爲F0至F7。

C編譯器使用A6中的指針指向數據,從而可以更短地使用短地址。 A6的值是常數,必須在C程序初始化期間將其設置爲外部定義符號a6base的地址。

在彙編器中定義了以下硬件寄存器;對於68020手冊,其含義應該顯而易見:CAAR,CACR,CCR,DFC,ISP,MSP,SFC,SR,USP和VBR。

彙編器還定義了一些操縱堆棧的僞寄存器:FP,SP和TOS。 FP是幀指針,因此0(FP)是第一個參數,4(FP)是第二個參數,依此類推。 SP是本地堆棧指針,其中保存了自動變量(SP僅是68020上的僞寄存器); 0(SP)是第一個自動值,以此類推。最後,TOS是堆棧頂部的寄存器,用於將參數推送到過程,保存臨時值等。

彙編器和加載器會跟蹤這些僞寄存器,因此無論A7指向硬件堆棧上的內容如何,上述語句都是正確的。名稱A7指的是硬件堆棧指針,但要小心混合使用A7和上述與堆棧相關的僞寄存器,這會造成麻煩。還要注意,加載程序會觀察PEA指令來更改SP,因此會在所有返回之前插入相應的彈出窗口。彙編器接受要附加到FP和SP用法的類似標籤的名稱,例如p + 0(FP),以幫助說明p是例程的第一個參數。該名稱位於符號表中,但對程序結果沒有任何意義。

引用數據

所有外部引用都必須相對於某些僞寄存器(PC(虛擬程序計數器)或SB(“靜態基數”寄存器))進行。 PC計算指令,而不是數據字節。 例如,要跳轉到第二條指令,即跳過一條指令,可以寫一條

  BRA 2(PC)

標籤也被允許,如

   BRA return

    NOP

return:

    RTS

使用標籤時,沒有(PC)註釋。

僞寄存器SB是指程序的地址空間的開頭。因此,對全局數據和過程的引用被寫爲SB的偏移量,如

  MOVL    $array(SB), TOS

將全局數組的地址壓入堆棧,或者

  MOVL    array+4(SB), TOS

推送數組的第二個(4字節)元素。注意偏移量的使用;尋址模式的完整列表如下。同樣,子例程調用必須使用SB:

 BSR exit(SB)

文件靜態變量具有語法

local<>+4(SB))

<>將在加載時由唯一的整數填充。

程序啓動時必須執行

 MOVL  $a6base(SB), A6

在訪問任何全局數據之前。 (在諸如MIPS和SPARC之類的不能在單個指令中加載寄存器的機器上,常量是通過靜態基址寄存器加載的。加載程序會識別初始化靜態基址寄存器的代碼,並對其進行特殊處理。但是,您必須小心, 當未設置靜態基址寄存器時(例如在中斷例程的早期),請勿在此類計算機上加載較大的常量。)

表達方式

表達主要是人們所期望的。 如果需要偏移量或常量,則允許使用帶有一元運算符的主表達式。 括號中允許使用通用C常數表達式。

源文件的處理過程與C編譯器完全相同,因此#define和#include可以正常工作。

尋址方式

所有彙編程序都共享簡單的尋址模式。 出於完整性考慮,此處列出所有68020尋址模式的表格,因爲該機器設置最豐富。 在該表中,o是一個偏移量,如果可以忽略零,則爲d,而d是偏移量,它是介於-128和127之間的常數。 列出的許多模式具有相同的名稱。 仔細檢查格式將顯示正在應用的默認值。 例如,不提供地址寄存器的索引模式就像使用零值寄存器一樣工作。 對於“偏移”,請閱讀“位移”。 對於“ .s”,請讀取.L或.W之一,後跟* 1,* 2,* 4或* 8,以指示數據的大小和縮放比例。

img

放下數據

將數據放入指令流(例如中斷向量)很容易:僞指令LONG和WORD(但不是BYTE)放置適當大小的單個參數的值,就好像它是一條指令:

LONG    $12345

將長12345(以10爲底)放置在指令流中。 (在大多數計算機上,唯一的此類運算符是WORD,它佔用32位的數量。386具有全部三個:LONG,WORD和BYTE。AMD64將QUAD添加到64位值的QUAD中。960僅具有一個LONG。)

在數據部分中放置信息會更加痛苦。僞指令DATA提供了兩個參數來完成這項工作:放置條目的地址(包括其大小)和放置在那裏的值。例如,要定義一個包含字符abc和終止null的字符數組的數組:

  DATA    array+0(SB)/1,$’a’

  DATA    array+1(SB)/1,$’b’

  DATA    array+2(SB)/1,$’c’

  GLOBL   array(SB),$4

要麼

  DATA    array+0(SB)/4,$"abc\z"

  GLOBL   array(SB),$4

/ 1定義要定義的字節數,GLOBL使符號成爲全局符號,而$ 4表示符號佔用多少字節。未初始化的數據將自動歸零。字符\ z等效於C \ 0。 DATA語句中的字符串最多可包含八個字節。分段構建更大的字符串。 DYNT和INIT這兩個僞指令允許(過時的)Alef編譯器在加載階段構建動態類型信息。 DYNT僞指令具有兩種形式:

  DYNT    ,ALEF_SI_5+0(SB)

  DYNT    ALEF_AS+0(SB),ALEF_SI_5+0(SB)

在第一種形式中,DYNT將符號定義爲一個小的唯一整數常量,由加載程序選擇,該常量是字長的大約倍數。

在第二種形式中,DYNT以相同的方式定義第二個符號,將第一個符號指定的數組中最近定義的文本符號的地址放置在第二個符號的值定義的索引處,然後調整大小相應的數組。

INIT僞指令採用與DATA語句相同的參數。它的符號用作數組的基礎,數據項以最新DYNT僞指令指定的偏移量安裝在數組中。數組的大小會相應調整。 DYNT和INIT僞指令未在68020上實現。

定義程序

入口點由僞操作TEXT定義,僞操作TEXT以過程的名稱(包括無處不在的(SB))和要在堆棧上預分配的自動存儲的字節數爲參數,當該變量通常爲零時,編寫彙編語言程序。在具有鏈接寄存器的計算機上,例如MIPS和SPARC,特殊值-4指示加載程序不生成PC保存和恢復指令,即使該功能不是葉子也是如此。這是一個返回其兩個參數之和的完整過程:

文字總和(SB),$ 0

TEXT    sum(SB),$0

    MOVL    arg1+0(FP),R0

    ADDL    arg2+4(FP),R0

    RTS

TEXT僞操作的可選中間參數是加載程序選項的位字段。當爲程序的其餘部分啓用了性能分析時,將1位置1會暫停性能分析。例如,

文字總和(SB),1,$ 0

TEXT    sum(SB),1,$0

    MOVL    arg1+0(FP),R0

    ADDL    arg2+4(FP),R0

    RTS

不會被剖析;上面的第一個版本是。狀態特殊的子例程(例如系統調用例程)不應進行概要分析。

將2位置1可以在程序中定義同一TEXT符號的多個定義。加載程序只會在圖像中放置一個這樣的功能。它僅由Alef編譯器發出。

即使是地址,從C調用的子例程也應將其結果放置在R0中。浮點值在F0中返回。將結構返回給C程序的函數將其存儲結果的位置的地址作爲其第一個參數。 R0在此類程序的調用協議中未使用。子例程負責保存其自己的寄存器,因此可以隨意使用任何寄存器而無需保存它們(“調用者保存”)。 A6和A7是上述例外。

有疑問時

如果您感到困惑,請嘗試對2c使用-S選項並編譯示例程序。標準輸出是對彙編器的有效輸入。

使用說明

彙編器的指令集與機器的指令集不同。選擇它以匹配編譯器生成的內容,並根據操作系統的特定需求對其進行稍微擴展。例如,2a不能區分各種形式的MOVE指令:快速移動,移動地址等。相反,上下文可以完成這項工作。例如,

MOVL $ 1,R1

MOVL A0,R2

MOVW SR,R3

生成正式的MOVEQ,MOVEA和MOVESR指令。許多指令沒有指定其全部功能所必需的語法。值得注意的示例是位域指令,乘法和除法指令等。有關生成的指令名的完整集合(以2a表示法,不是摩托羅拉的名稱),請參見文件/sys/src/cmd/2c/2.out.h。儘管有其名稱,該文件仍包含由編譯器生成的中間文件中出現的指令的枚舉,這些指令與彙編語言的各行完全對應。

放下指示

加載器修改由彙編器和編譯器生成的代碼。它摺疊分支,複製短代碼序列以消除分支,並丟棄無法訪問的代碼。假定每個功能的第一條指令都是可以到達的。您可能會在編譯器輸出中看到的僞指令NOP意味着根本沒有指令,而不是什麼都不做的指令。加載程序會丟棄所有NOP。

要生成真正的NOP指令或彙編程序未知的任何其他指令,請使用WORD僞指令。加載程序未安排有關RISC的此類指令,因此必須手動填充其延遲時間。

MIPS

寄存器僅按編號尋址:R0至R31。 R29是堆棧指針; R30用作靜態基址指針,類似於68020上的A6。它的值是全局符號setR30(SB)的地址。包含子程序返回值的寄存器爲R1。調用函數時,第一個參數的空間保留爲0(FP),但在C(不是Alef)中,該值改爲在R1中傳遞。

裝載機將R28用作臨時設備。系統使用R26和R27作爲中斷時間臨時對象。因此,這些寄存器都不應該在用戶代碼中使用。

彙編程序不知道控制寄存器。而是將它們編號爲寄存器M0,M1等。使用此技巧可訪問狀態:

#定義狀態12

MOVW M(STATUS),R1

浮點寄存器稱爲F0至F31。按照慣例,必須將F24初始化爲值0.0,將F26初始化爲0.5,將F28初始化爲1.0,將F30初始化爲2.0。這是由操作系統完成的。

這些說明及其語法與製造商的手冊不同。沒有路易和親屬。而是有MOVW(移動字),MOVH(移動半字)和MOVB(移動字節)僞指令。如果操作數是無符號的,則指令爲MOVHU和MOVBU。操作數的順序是按照數據流順序從左到右,就像68020一樣,但是不像MIPS文檔中那樣。這意味着Bcond指令相對於這本書是相反的。例如,va BGTZ生成MIPS bltz指令。

彙編程序適用於R2000,R3000以及大多數R4000和R6000體系結構。它瞭解64位指令MOVV,MOVVL,ADDV,ADDVU,SUBV,SUBVU,MULV,MULVU,DIVV,DIVVU,SLLV,SRLV和SRAV。彙編器沒有任何緩存,加載鏈接或存儲條件指令。

加載程序將某些彙編程序指令擴展爲多個指令。例如,加載器可以將32位常量的負載轉換成lui,然後是ori。

彙編程序指令的佈局應像沒有負載,分支或浮點比較延遲槽一樣;裝載機將重新安排時間表,以確保正確性並提高性能。唯一的例外是,使用控制寄存器的指令的正確調度因機器的型號而異(並且通常沒有文檔記錄),因此您應手動調度此類指令以保證正確的行爲。加載程序生成

或非R0,R0,R0

需要真正的無操作指令時。手動調度代碼時,請嚴格使用此指令;加載程序會識別它,並分別在代碼之前和之後安排代碼。同樣,WORD僞操作的安排與無操作的一樣。

NOSCHED僞操作禁用指令調度(默認情況下啓用調度); SCHED重新啓用它。對於未計劃的指令,分支摺疊,代碼複製和無效代碼消除被禁用。

SPARC

一旦瞭解了MIPS的Plan 9模型,就很熟悉SPARC。寄存器僅具有數字名稱:R0至R31。忘記註冊窗口:Plan 9根本不使用它們。機器有32個全局寄存器,每個週期。 R1 [sic]是堆棧指針。 R2是靜態基址寄存器,其值是setSB(SB)的地址。 R7是返回寄存器,也是保存C(不是Alef)函數的第一個參數的寄存器,同樣保留了0(FP)的空間。 R14是裝載程序臨時文件。

浮點寄存器與MIPS完全相同。

控制寄存器以諸如FSR之類的名稱已知。訪問這些寄存器的指令是MOVW指令,例如

MOVW Y,R8

用於SPARC指令

rdy%r8

移動指令類似於MIPS上的指令:僞操作,將其轉換爲適當的sethi指令,加法運算等順序。指令從左至右讀取。因爲參數已翻轉到SUBCC,所以條件代碼不會像在MIPS上那樣被反轉。

ASI內容的語法例如是將單詞從ASI 2中移出:

MOVW(R7,2),R8

雙索引的語法是

MOVW(R7 + R8),R9

SPARC的指令調度類似於MIPS的調度。官方的禁止操作說明是:

ORN R0,R0,R0

i960

寄存器編號爲R0至R31。堆棧指針是R29;返回寄存器是R4;靜態基數爲R28;它被初始化爲setSB(SB)的地址。 R3必須爲零;這應該在執行的早期手動完成

SUBO R3,R3

R27是裝載程序臨時文件。

不支持浮點。

不支持並且不能使用Intel調用約定。請改用BAL。說明主要與本書中的內容相同。主要變化是LOAD和STORE都稱爲MOV。 MOV的擴展字符與手冊中的相同:O表示序數,W表示帶符號等。

i386

彙編器採用32位保護模式。寄存器名稱爲SP,AX,BX,CX,DX,BP,DI和SI。堆棧指針(不是僞寄存器)是SP,返回寄存器是AX。沒有物理幀指針,但是就MIPS而言,FP是用作幀指針的僞寄存器。

操作碼名稱與英特爾手冊中列出的名稱基本相同,並在其後附加L,W或B以標識32位,16位和8位操作。加載,存儲和條件是例外。所有在通用寄存器,特殊寄存器(例如CR0,CR3,GDTR,IDTR,SS,CS,DS,ES,FS和GS)或存儲器中加載和存儲的操作碼都寫爲

MOVx src,dst

其中x是L,W或B。因此要獲得AL,請使用MOVB指令。如果需要訪問AH,則必須在MOVB中明確提及它:

MOVB AH,BX

非法舉動的例子很多,例如,

MOVB BP,DI

加載程序實際上實現爲僞操作。

所有條件指令(J,SET)中的條件名稱均遵循68020的約定,而不遵循Intel彙編程序的約定:JOS,JOC,JCS,JCC,JEQ,JNE,JLS,JHI,JMI,JPL,JPS,JPC ,JLT,JGE,JLE和JGT,而不是JO,JNO,JB,JNB,JZ,JNZ,JBE,JNBE,JS,JNS,JP,JNP,JL,JNL,JLE和JNLE。

尋址模式具有AX,(AX),(AX)(BX * 4),10(AX)和10(AX)(BX * 4)之類的語法。 AX的偏移量可以替換爲FP或SB到訪問名稱的偏移量,例如extern + 5(SB)(AX * 2)。

其他說明:非相對JMP和CALL在語法上添加了*。只有LOOP,LOOPEQ和LOOPNE是合法的循環指令。僅REP和REPN是公認的中繼器。這些不是前綴,而是字符串前面的獨立操作碼,例如

CLD; REP; MOVSL

不支持MOD / RM字段中的細分替換前綴。

AMD64

除非給出MODE僞操作,否則彙編器將採用64位模式:

模式$ 32

更改爲32位模式。效果主要是診斷給定模式下的非法指令,但加載程序還將採用32位操作數和地址,以及32位PC值進行調用和返回。彙編程序的約定與上面的386相似。該體系結構提供了額外的定點寄存器R8至R15。所有寄存器均爲64位,但指令訪問低位8、16和32位,如處理器手冊中所述。例如,MOVL to AX將一個值放在低32位中,並將高32位清除爲零。文字操作數限制爲有符號的32位值,在64位操作中將其符號擴展爲64位。 MOVQ是例外,它允許使用64位文字。計劃9 C中的外部寄存器是從R15向下分配的。

有許多新的指令,包括MMX和XMM媒體指令以及條件移動指令。 MMX寄存器爲M0至M7,XMM寄存器爲X0至X15。與386指令名稱一樣,所有新的64位整數指令以及MMX和XMM指令統一使用L表示“長字”(32位),而Q表示“四字”(64位)。有些指令將O(“八位字”)用於128位值,而處理器手冊則使用O或DQ。彙編程序還始終將PL用於XMM指令中的“ packed long”,而不是Q,DQ或PI。即使寄存器爲64位,也可以使用MOVL或MOVQ在控制寄存器之間來回移動值。彙編程序通常會接受手冊的名稱,以簡化現有代碼的轉換(但請記住,操作數的順序是源先是目標,然後纔是目標)。

C的long long類型爲64位,但是按值(而不是按引用)傳遞和返回。更值得注意的是,C指針值爲64位,因此long long和unsigned long long是僅有足夠寬的整數類型可以容納指針值。 C編譯器和庫使用XMM浮點指令,而不是舊的387浮點指令,儘管後者是由彙編器和加載器實現的。與386不同,第一個整數或指針參數在寄存器中傳遞,該寄存器是整數或指針的BP(可以在彙編代碼中由化名RARG引用)。 AX像以前一樣保留子程序的返回值。儘管當前第一個浮點參數未在寄存器中傳遞,但浮點結果將在X0中返回。所有長度小於8個字節的參數都在堆棧上保留了8個字節的插槽,以保持對齊並簡化可變長度參數列表訪問,包括在傳入寄存器時傳遞的第一個參數,即使未初始化字節4至7。

Power PC

Power PC遵循MIPS和SPARC設定的Plan 9模型,而不遵循複雜的ABI。支持60x和8xx PowerPC架構的32位指令。不支持較早的POWER指令。寄存器爲R0至R31。 R0初始化爲零;這是由C啓動代碼完成的,並由編譯器和加載器承擔。 R1是堆棧指針。 R2是靜態基址寄存器,其值是setSB(SB)的地址。 R3是返回寄存器,也是保存C函數的第一個參數的寄存器,與MIPS一樣,其空間保留爲0(FP)。 R31是裝載程序臨時文件。計劃9 C中的外部寄存器是從R30向下分配的。

浮點寄存器稱爲F0至F31。按照慣例,幾個寄存器被初始化爲特定的值。這是由操作系統完成的。 F27必須初始化爲值0x4330000080000000(用於浮點到整數轉換),F28初始化爲值0.0,F29初始化爲0.5,F30初始化爲1.0,F31初始化爲2.0。

與在MIPS和SPARC上一樣,彙編器接受任意文字作爲MOVW,ADD和其他存在“立即”變體的操作數,並且加載程序根據需要生成addi,addis,oris等序列。寄存器間接尋址模式使用與SPARC相同的語法,包括允許時的雙索引。

指令名稱通常來自摩托羅拉的指令名稱,需要進行一些改動:將條件代碼設置的“。”替換爲CC,當字母“ o”表示“ OE = 1”時,將其替換爲V。因此添加,addo。和subfzeo。成爲ADD,ADDVCC和SUBFZEVCC。除了三操作數條件分支指令BC之外,彙編器還爲以下常見情況提供僞指令:BEQ,BNE,BGT,BGE,BLT,BLE,BVC和BVS。無條件轉移指令是BR。間接分支使用(CTR)或(LR)作爲目標。

加載或存儲操作通常以MOV變體替換:MOVW(移動字),MOVH(移動帶符號擴展的半字)和MOVB(移動帶符號擴展的字節,僞指令),並帶有無符號變體MOVHZ和MOVBZ ,以及字節反轉的MOVWBR和MOVHBR。 “加載或存儲更新”版本是MOVWU,MOVHU和MOVBZU。加載或存儲多個是MOVMW。例外是字符串指令,分別是LSW和STSW,以及保留指令lwarx和stwcx。,它們分別是LWAR和STWCCC,它們都具有按常規數據流順序排列的操作數。浮點加載或存儲指令爲FMOVD,FMOVDU,FMOVS和FMOVSU。該寄存器用於註冊移動指令fmr和fmr。分別寫爲FMOVD和FMOVDCC。

彙編器知道常用的專用寄存器:CR,CTR,DEC,LR,MSR和XER。其餘的通常取決於體系結構,被稱爲SPR(n)。 60x系列的段寄存器類似地是SEG(n),但是n也可以是寄存器名稱,如SEG(R3)中一樣。在體系結構允許的情況下,專用寄存器和通用寄存器之間的移動被編寫爲MOVW,從而取代了mfcr,mtcr,mfmsr,mtmsr,mtspr,mfspr,mftb等。

條件寄存器CR的字段稱爲CR(0)至CR(7)。它們由產生mcrf或mtcrf的MOVFL(移動字段)僞指令使用。例如:

MOVFL CR(3),CR(0)

MOVFL R3,CR(1)

MOVFL R3,7美元,CR

例如,在條件分支指令中也接受它們

BEQ CR(7),標籤

使用MOVFL以類似的方式訪問FPSCR的字段:

MOVFL FPSCR,F0

MOVFL F0,FPSCR

MOVFL F0,$ 7,FPSCR

MOVFL $ 0,FPSCR(3)

酌情生成mffs,mtfsf或mtfsfi。

ARM

彙編器可通過R14和PC來訪問R0。堆棧指針爲R13,鏈接寄存器爲R14,靜態基址寄存器爲R12。 R0是返回寄存器,也是保存子例程第一個參數的寄存器。計劃9 C中的外部寄存器是從R10向下分配的。加載程序將R11用作臨時寄存器。彙編器支持CPSR和SPSR寄存器。它還知道協處理器寄存器C0至C15。浮動寄存器爲F0至F7,FPSR和FPCR。

與其他架構一樣,加載和存儲也稱爲MOV,例如MOVW用於加載字或存儲字,而MOVM用於加載或存儲多個字,具體取決於操作數。

指令的後綴支持尋址模式:.IA(之後遞增)、. IB(之前遞增)、. DA(之後遞減)和.DB(之前遞減)。這些只能與MOV指令一起使用。多重移動指令MOVM使用括號來定義寄存器範圍,例如[R0-R12]。特殊的MOVM尋址模式位W,U和P以相同的方式寫入,例如MOVM.DB.W。後綴.S允許MOVM指令在另一種處理器模式下訪問用戶R13和R14。二進制運算符<<(邏輯左移),>>(邏輯右移),->(算術右移)和@>(右旋轉)支持尋址模式下的移位和旋轉。例如R7 >> R2或R2 @> 2。彙編器不支持通過移位表達式進行索引;只有名稱可以被雙重索引。

任何指令之後都可以帶有使該指令成爲條件的後綴:例如.EQ,.NE等,如ARM手冊中所述,例如,帶有同義詞.HS(對於.CS)和.LO(對於.CC)。 ADD.NE。在ARM允許下,算術和邏輯指令可以帶有.S後綴來設置條件代碼。

MCR和MRC協處理器指令的語法在很大程度上與手冊中的內容相同,並進行了常規調整。彙編器僅直接支持編譯器使用的ARM浮點協處理器操作:CMP,ADD,SUB,MUL和DIV,所有後綴都選擇F或D後綴以選擇單精度或雙精度。浮點加載或存儲成爲MOVF和MOVD。轉換指令也由移動指定:MOVWD,MOVWF,MOVDW,MOVWD,MOVFD和MOVDF。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章