組合語言 === ###### tags: `大學必修-筆記` CH1 基本觀念 === ## 架構 ![](https://i.imgur.com/GBXvEUc.png) * Level 4 -> C++、Java... * Level 3 -> 組語 * Level 2 -> ISA * Level 1 -> 邏輯層 CH2 x86 程序架構 === * IA-32 -> 32 bits Intel Architecture (32位頻寬Intel架構) ## General-Purpose Registers (GPA) 通用暫存器 | 暫存器名稱 | 中文名稱 | 用途 | | -------- | -------- | -------- | |AX|累加器暫存器|用在算術運算| |BX|基址暫存器|作為一個指向資料的指標| |CX|計數器暫存器|用於移位/迴圈指令和迴圈| |DX|資料暫存器|用在算術運算和I/O操作| |SP|堆疊指標暫存器|用於指向堆疊的**頂部**| |BP|棧基址指標暫存器|用於指向堆疊的**底部**| |SI|源變址暫存器|在流操作中用作源的一個指標| |DI|標的索引暫存器|用作在流操作中指向標的的指標| | 暫存器 | | 累加器 | | 基址 | | 計數器 | | 資料 | |:--------:|:----:|:-----:|:----:|:-----:|:----:|:-----:|:----:|:-----:| | 32-bit | | EAX | | EBX | | ECX | | EDX | | 16-bit | | AX | | BX | | CX | | DX | | 8-bit | AH | AL | BH | BL | CH | CL | DH | DL | | 暫存器 | 堆疊指標 | 棧基址指標 | 源變址 | 標的索引 | |--------|:----:|:-----:|:----:|:-----:|:----:|:----:| | 32-bit |ESP | EBP | ESI | EDI | | 16-bit | SP | BP | SI | DI | * AH 與 AL 實際上是獨立不互相影響的,但對於 AX 來說 AH 與 AL 又是其一部份 ## Segment 區段暫存器 | 暫存器名稱 | 中文名稱 | |-------|----------| | CS | 程式區段暫存器 | | DS | 資料區段暫存器 | | SS | 堆疊區段暫存器 | | ES | 額外區段暫存器 | | FS | 額外區段暫存器 | | GS | 額外區段暫存器 | ## EFLAGS 暫存器 | FLAG名稱 | 中文名稱 | |----------------|---------| | CF (Carry) | 進位旗標 | | OF (Overflow) | 溢位旗標 | | ZF (Zero) | 零值旗標 | | AF (Auxiliary) | 輔助進位旗標 | | PF (Parity) | 奇偶旗標 | | DF (Direction) | 方向旗標 | | IF (Interrupt) | 中斷旗標 | | TF (Trap) | 單步旗標 | ## Instruction Pointer Register * 簡稱 IP * 用來指向下一個 CPU 準備執行的指令所在的 memory address * 當指令執行後,IP 就會自動指向下一個準備執行的指令所在的 memory address。 CH3 基本組語程式架構 === ## Basic Element * 數字結尾 * h $\rightarrow$ 16進位 * d $\rightarrow$ 10進位 * b $\rightarrow$ 2進位 * 指令格式 * label * mnemonic * operand * comment $\rightarrow$ 用 「;」 * ==label:== ==mnemonic== ==[operands]== ==[;comment]== * 指令範本 ```javascript= TITLE Program Template (Template.asm) ; Program Description: ; Author: ; Creation Date: ; Revisions: ; Date: Modified by: INCLUDE Irvine32.inc .data ; (這裡插入變數) .code main PROC ; (這裡插入可執行的 instructions) exit main ENDP ; (這裡插入額外的程序) END main ``` ## Data Types | Unsigned Data Types | 功用 | Signed Data Types | 功用 | |---------------------|-------------------------|-------------------|-----------------------| | BYTE | 8-bit unsigned integer | SBYTE | 8-bit signed integer | | WORD | 16-bit unsigned integer | SWORD | 16-bit signed integer | | DWORD | 32-bit unsigned integer | SDWORD | 32-bit signed integer | | QWORD | 64-bit integer | | | | TBYTE | 80-bit integer | | | | Data Types| 功用 | | -------- | -------- | | REAL4 | 32-bit (4 byte) IEEE短實數 | |REAL8|64-bit (8 byte) IEEE長實數| |REAL10|80-bit (10 byte) IEEE延伸實數| ## 定義 * 定義變數 ``` 名稱 型態 值 value BYTE 10 -> 定義一個BYTE 名字value 值為10 value BYTE ? -> 定義一個BYTE 名字value 值末定 ``` * 定義矩陣 ``` list BYTE 10, 20, 30, 40 -> 定義一個BYTE 大小有四格的矩陣 list BYTE ?, 32, 41h, 00100010b ``` `利用 DUP()` ``` list BYTE 20 DUP(0) -> 定義一個大小為 20 byte(格),內容皆為 0 的矩陣 ``` * 定義字串 ``` string BYTE "Hallo World!", 0 -> 記得要加上 0,表示結束 string BYTE "Hallo World!", 0dh, 0ah, 0 -> 表示換行 ``` ## directive * Directive 則是人自己手動加入的,而不屬於 CPU 的指令,作用有兩種: * 指示 assembler 要作某些事情 * 告知 assembler 某些訊息 * 而這個部分,並不會被 assembler 轉為 machine code,一般常見的 directive 用法大概有幾種: * 定義常數(constant) * 定義儲存資料的記憶體位置 * 將多段記憶體空間組合為 segment * 視情況 include 外部的 source code * include 其他檔案 ## EQU & Symbolic Constants ### $ (current location counter) * 它會一直往後指,不斷更新目前的位置 ```javascript= list BYTE 10, 20, 30, 40 ListSize = ($ - list) ; 得到 list 矩陣的 index 個數 list WORD 1000h, 2000h, 3000h, 4000h ListSize = ($ - list) / 2 ; 得到 list 矩陣的 index 個數 list DWORD 1, 2, 3, 4 ListSize = ($ - list) / 4 ; 得到 list 矩陣的 index 個數 ``` ### EQU * 用來定義有名稱的常數 * 定義好之後,值不可再度變更 * 類似 #define ```javascript= PI EQU <3.1416> move EAX, PI ``` ### TEXTEQU * 可以用來定義定串,變成一個變數名稱 * 字串可以是文字、計算式、指令 ```javascript= continueMsg TEXTEQU <"Do you wish to continue (Y/N)?"> // 定義為文字 count TEXTEQU %(rowSize * 2) // 定義為計算式 setupAL TEXTEQU <mov al, count> // 定義為指令 ``` ### = (equal-sign directive) * 用來定義有名稱的常數 * 定義好之後可以再度變更 ```javascript= COUNT = 500 mov al,COUNT ``` ## 矩陣 * 存到記憶體裡的順序 * ==用小頭端 (little-endian)== * 多位元組數值的最低位位元組首先寫入 * ==高位元去大的位置、低位元去小的位置== * ex:list DWORD 12345678h ![](https://i.imgur.com/1SBuNNT.png) * 矩陣的存取 * 加上 [] 表示要取值 * 每移動的距離以 byte 為單位 ```javascript= ;定義兩個陣列,每個元素的大小分別為 byte 與 word byteArray db 5, 4, 3, 2, 1 wordArray dw 5, 4, 3, 2, 1 ;存取 byte 陣列 mov al, [byteArray] ;AL = byteArray[0] mov al, [byteArray + 1] ;AL = byteArray[1] mov [byteArray + 3], al ;byteArray[3] = AL ;存取 word 陣列 mov ax, [wordArray] ;AX = wordArray[0] mov ax, [wordArray + 2] ;AX = wordArray[1] 注意! 這邊 + 2 bytes 即是 + 1 word mov [wordArray + 6], ax ;wordArray[3] = AX mov ax, [wordArray + 1] ;這是錯誤的用法,因為元素大小為 word(2 bytes) ;存取 dword 陣列 mov ax, [dwordArray] ;AX = dwordArray[0] mov ax, [dwordArray + 4] ;AX = dwordArray[1] 注意! 這邊 + 4 bytes 即是 + 1 dword ``` CH4 Data Transfers, Addressing, and Arithmetic === ## Instruction operands 指令運算元 * **register** * 指向儲存於 CPU register 中的值。 * **memory** * 指向儲存於 memory 中的資料,而 memory address 有可能是直接寫於指令中,或是由 register 的值所組成。 * **immediate** * 列於指令中的固定值,儲存於指令本身(在 code segment 中),而非在 data segment 中。 * 也就是常數,ex:45 * **implied** * 此種 operand 不會明確的顯示。例如:將某個 register 中的值加 1。 ## Instruction 指令 ### MOV 搬移資料 ``` MOV 目地, 來源 ``` * 目地及來源,都不能是 immediate (變數名稱) * 目地及來源,不能一起是 memory (變數名稱) * 目地及來源,都必須相同大小 (表示 AX 中的值不能 mov 到 BL 中) #### MOVZX 搬移資料(小到大) * Zero Extension * 當從 AL 搬到 AX 時 (小搬大) * AH 的地方會填滿 0 ![](https://i.imgur.com/Pt6krQd.png) #### MOVSX 搬移資料(小到大) * Sign Extension * 當從 AL 搬到 AX 時 (小搬大) * AH 的地方會根據你目前的 sign 值(MSB),來把它填滿 ![](https://i.imgur.com/dAf5xLj.png) ### XCHG 交換資料 ``` XCHG 要互換的資料, 要互換的資料 ``` * 目地及來源,都不能是 immediate (變數名稱) * 目地及來源,不能一起是 memory (變數名稱) ### INC 加一 & DEC 減一 ``` INC 變數名稱 DEC 變數名稱 ``` ```javascript= inc myWord ; 1001h dec myWord ; 1000h inc myDword ; 10000001h mov ax, 00FFh inc ax ; AX = 0100h mov ax, 00FFh inc al ; AX = 0000h ``` ```javascript= myByte BYTE 0FFh, 0 mov al, myByte ; AL = FFh mov ah, [myByte+1] ; AH = 00h dec ah ; AH = FFh inc al ; AL = 00h dec ax ; AX = FEFF ``` ### ADD 加 & SUB 減 ``` ADD 目標變數, [數字, 變數] SUB 目標變數, [數字, 變數] ``` ```javascript= .data var1 DWORD 10000h var2 DWORD 20000h .code mov eax, var1 ; 00010000h add eax, var2 ; 00030000h add ax, 0FFFFh ; 0003FFFFh add eax, 1 ; 00040000h sub ax, 1 ; 0004FFFFh ``` ### NEG 正負號交換 ``` NEG [變數名稱, 暫存器] ``` * neg 也就是把那個數字的位元==變成二補數== * 因為 0 的二補數還是 0 (00000000),所以 ==neg 0 是 0== * 因為 -128 的二補數還是 -128 (10000000),所以 ==neg -128 是 -128== ```javascript= .data valB BYTE -1 valW WORD +32767 .code mov al,valB ; AL = -1 neg al ; AL = +1 neg valW ; valW = -32767 ``` ## Flag * 當 Flag 值為 1 時 -> set * 當 Flag 值為 0 時 -> clear ### Zero Flag (ZF) 看結果是否為 0 * 如果 ZF = 1 -> 值為 0 * 如果 ZF = 0 -> 值不為 0 ```javascript= mov cx, 1 sub cx, 1 ; CX = 0, ZF = 1 mov ax, 0FFFFh inc ax ; AX = 0, ZF = 1 inc ax ; AX = 1, ZF = 0 ``` ### Sign Flag (SF) 看是正是負 * 如果 SF = 0 -> 值為正的 * 如果 SF = 1 -> 值為負的 * SF 的判斷其實就是看最高的位元數字 (MSB) ```javascript= mov al, 0 sub al, 1 ; AL = 11111111b, SF = 1 add al, 2 ; AL = 00000001b, SF = 0 ``` ### Carry Flag (CF) 看 unsigned value 是否超出邊界 * 看無號數 unsigned value * 當==相加 $\geq 2^n$== 時 or 最高位有==進位==時 * 當相減==由小減大==時 or 最高位有==借位==時 * 如果一個運算的結果超過 [0, 255] ```javascript= mov al, 0FFh add al, 1 ; CF = 1, AL = 00 mov al, 0 sub al, 1 ; CF = 1, AL = FF ``` * ==inc 與 dec指令不會影響進位旗標== ### Overflow Flag (OF) 看 signed value 是否超出邊界 * 看有號數 signed value * 當兩個==正數==相加變成==負數==時 * 當兩個==負數==相加變成==正數==時 * 如果一個運算的結果超過 [-128, 127] ```javascript= mov al, -128 neg al ; CF = 1 OF = 1 mov ax, 8000h add ax, 2 ; CF = 0 OF = 0 mov ax, 0 sub ax, 2 ; CF = 1 OF = 0 mov al, -5 sub al, +125 ; OF = 1 ``` ## Data-Related Operators and Directives ### OFFSET ![](https://i.imgur.com/NMZA6Pf.png) ``` mov esi, OFFSET 要看起使位置的變數 ``` * 傳回變數從所在區段開始的==偏移==距離 * 也就是傳回變數的起使位置 ```javascript= .data bVal BYTE ? wVal WORD ? dVal DWORD ? dVal2 DWORD ? .code ; assume bVal's offset : 00404000 mov esi, OFFSET bVal ; ESI = 00404000 mov esi, OFFSET wVal ; ESI = 00404001 因為 BYTE 1 個 byte mov esi, OFFSET dVal ; ESI = 00404003 因為 WORD 2 個 byte mov esi, OFFSET dVal2 ; ESI = 00404007 因為 DWORD 4 個 byte ``` ### PTR * 存取的變數大小與當初宣告的變數不同 ``` 型態 PTR 變數名 ``` * 由大的型態轉成小的型態 * 轉成多大的型態,就從最小位元開始算幾個byte ```javascript= .data myDouble DWORD 12345678h mov al, BYTE PTR myDouble ; AL = 78h mov al, BYTE PTR [myDouble+1] ; AL = 56h mov al, BYTE PTR [myDouble+2] ; AL = 34h mov ax, WORD PTR myDouble ; AX = 5678h mov ax, WORD PTR [myDouble+2] ; AX = 1234h ``` * 由小的型態轉成大的型態 * CPU 會自動把 byte 顛倒過來 ```javascript= .data myBytes BYTE 12h, 34h, 56h, 78h .code mov ax, WORD PTR [myBytes] ; AX = 3412h mov ax, WORD PTR [myBytes+2] ; AX = 7856h mov eax, DWORD PTR myBytes ; EAX = 78563412h ``` ### TYPE * 可以回傳以 byte 為單位的大小值 ```javascript= .data var1 BYTE ? var2 WORD ? var3 DWORD ? var4 QWORD ? .code mov eax,TYPE var1 ; 1 mov eax,TYPE var2 ; 2 mov eax,TYPE var3 ; 4 mov eax,TYPE var4 ; 8 ``` ### LENGTHOF * 計算陣列中的元件數 ```javascript= byte1 BYTE 10, 20, 30 ; 3 array1 WORD 30 DUP(?), 0, 0 ; 32 array2 WORD 5 DUP(3 DUP(?)) ; 15 digitStr DWORD "12345678", 0 ; 9 mov ecx, LENGTHOF array1 ; 32 ``` ### SIZEOF * 傳回的值等於 LENGTHOF 乘上 TYPE 的值 ```javascript= byte1 BYTE 10,20,30 ; 3 = 3 * 1 array1 WORD 30 DUP(?),0,0 ; 64 = 32 * 2 array2 WORD 5 DUP(3 DUP(?)) ; 30 = 15 * 2 digitStr DWORD "12345678",0 ; 36 = 9 * 4 ``` * 如果目標矩陣為二元矩陣時 * LENGTHOF 及 SIZEOF 只會計算第一列定義的部分 ```javascript= .data array WORD 10, 20 WORD 30, 40 WORD 50, 60 .code mov eax, LENGTHOF array ; 2 -> 只會看 WORD 10,20 mov ebx, SIZEOF array ; 4 -> 只會看 WORD 10,20 ``` ### LABEL * 賦予下一行宣告的資料另一個名字和另一種型態 * LABEL 那一行並不會在記憶體獲取空間 * 取代 PTR 的效果 ```javascript= .data dwList LABEL DWORD wordList LABEL WORD intList BYTE 00h, 10h, 00h, 20h .code mov eax,dwList ; 20001000h mov cx,wordList ; 1000h mov dl,intList ; 00h ``` ## Indirect Addressing 間接定址 * 間接定址 -> 暫存器當作指標 * 利用指標的方式去存取矩陣中的值 * 跟 C 的指標很像 ### Indirect Operands 間接運算元 * 利用 esi 當作指標,去存取矩陣的元素 * esi 指標加 1 * 矩陣大小(byte) 後為往下一個格子存取 ```javascript= .data val1 BYTE 10h, 20h, 30h .code mov esi,OFFSET val1 mov al,[esi] ; dereference ESI (AL = 10h) inc esi mov al,[esi] ; AL = 20h inc esi mov al,[esi] ; AL = 30h ``` * 如果 OFFSET 的目標的一個變數而不是矩陣的話 ```javascript= .data myCount WORD 0 .code mov esi,OFFSET myCount inc esi ; 錯誤 inc WORD PTR [esi] ; 要給一個確定的大小才是對的 ``` ### Indexed Operands 索引運算元 * 另一種存取矩陣的方法 ``` 常數[暫存器] [ 常數 + 暫存器 ] ``` ```javascript= .data arrayW WORD 1000h, 2000h, 3000h .code mov esi, 0 mov ax, [arrayW + esi] ; AX = 1000h mov ax, arrayW[esi] ; 另一種寫法 add esi, 2 add ax, [arrayW + esi] ``` ### Pointers * 直接定義一個 pointer varible 指標變數 * 去存取其它變數的位置 * 指標大小必須為 DWORD ```javascript= .data arrayW WORD 1000h, 2000h, 3000h ptrW DWORD arrayW ; 定義一個指標變數 ptrW DWORD OFFSET arrayW ; 另一種寫法 .code mov esi, ptrW ; 把指標變數丟給 esi 暫存器 mov ax, [esi] ; AX = 1000h ``` ## JMP and LOOP Instructions 迴圈 ### JMP 架構 ``` JMP 目的標籤 ``` ```javascript= top: ... jmp top ; 跳到 top 標籤 ``` ### LOOP 架構 * 使用 ecx 暫存器,存入要重複的次數 * ==ecx 會先減 1 再進入迴圈== * JMP 就會重覆執行特定區段的指令 * 迴圈的目的必須在現在位置-128到+127之間 ```javascript= mov ax, 0 mov ecx, 5 L1: inc ax loop L1 ; 迴圈中止後 ax = 5, ecx = 0 ``` * 如果 ecx 為 0 時 * 因為 ecx 會先減 1 再進入迴圈 ```javascript= mov ecx,0 X2: inc ax loop X2 ; 迴圈中止後 ax = 4,294,967,296, ecx = 0 ``` ### Nested Loop ```javascript= .data count DWORD ? .code mov ecx,100 ; 設定 L1 外迴圈的次數 L1: mov count,ecx ; 把 L1 的次數存起來 mov ecx,20 ; 設定 L2 外迴圈的次數 L2: ... loop L2 ; 重複 L2 迴圈 mov ecx, count ; 把 L1 的迴圈次數存回來 loop L1 ; 重複 L1 迴圈 ``` ### Ex:把矩陣數字加總 ```javascript= intarray WORD 100h, 200h, 300h, 400h mov edi, OFFSET intarray ;「 指向 intarray 位址 」 mov ecx, LENGTHOF intarray ; 「迴圈次數」 mov ax, 0 ; 歸零 L1: add ax, [edi] ; 加上一整數,加上[]表示取值 add edi, TYPE intarray ; 指向下一個整數 loop L1 ; 重複直到 ecx = 0 ``` ### Ex:字串複製 ```javascript= source BYTE "This is the source string", 0 target BYTE SIZEOF source DUP(0) mov esi, 0 ; 索引暫存器 mov ecx, SIZEOF source ; 迴圈次數 L1: mov al, source[esi] ; 從來源取得字元 mov target[esi], al ; 儲存在目標 inc esi ; 移到下一個字元 loop L1 ; 重複整個字串 ``` CH5 Procedures 程序 === ## 使用 Library * 先 INCLUDE Irvine32 * 作者寫的 Library * 用 call 去叫 Function ```javascript= INCLUDE Irvine32.inc .code mov eax,1234h call WriteHex ; print 出 hex call Crlf ; 結束一行 ``` ### 一些 function * call Clrscr 清空螢幕 * call DumpRegs 把暫存器和 flag 的值 print 出來 * call Crlf 換行 * call WriteBin 印成二進位 * call WriteDec 印成十進位 * call WriteHex 印成十六進位 * call WriteString 印字串 ## Stack Operations 堆疊操作 * 堆疊 -> 後進先出 (LIFO) * 使用 ESP (stack pointer) 來指向堆疊 ![](https://i.imgur.com/i1kpv0J.png) ### PUSH ![](https://i.imgur.com/ARnm2sT.png) 1. 先將堆疊指標的值==減四== (在保護模式下) 2. 再將資料拷貝到堆疊指標所指的位置 ### POP 1. 複製堆疊中由 esp 所指向的內容到一個16-bit或 32-bit 的目的運算元 2. 並存在變數或暫存器裡 3. 再將 ESP 加 N 4. 如果指到 16-bit $\rightarrow$ ==esp+2== 5. 如果指到 32-bit $\rightarrow$ ==esp+4== ### 其它堆疊指令 * PUSHFD * 將 32-bit旗標暫存器PUSH到堆疊 * POPFD * 由堆疊中將旗標資料POP到暫存器 * PUSHAD * 將一般用途的32-bit暫存器以 eax ecx edx ebx esp ebp esi edi 順序 push 到堆疊 * POPAD * 以相反順序POP出來 * PUSHA * 將一般用途的16-bit途暫存器以 ax cx dx bx sp bp si di 順序 push 到堆疊 * POPA * 以相反順序POP出來 ### 指令操作 * ex:用 Stack 來實作 Nested Loop ```javascript= mov ecx, 100 ; 設定外部迴圈次數 L1: ; 外部迴圈開始 push ecx ; 把目前外部迴圈的次數存起來 mov ecx, 20 ; 設定內部迴圈次數 L2: ; 內部迴圈開始 ... loop L2 ; 回 L2 pop ecx ; 把外部迴圈的次數重新存回 ecx loop L1 ; 回 L1 ``` ## Defining & Using Procedures 定義及使用程序 * 大問題可以分成一個個小區塊 * 這樣比較好管理 * 類似 function 的功能 ### 創造一個程序 ```javascript= 程序名稱 PROC ...... ret 程序名稱 ENDP ``` ### 程序例子 call & ret ```javascript= main PROC // 以下為各指令的 OFFSET (位置) call MySub // 00000020 mov eax, ebx // 00000025 -> mov 的 offset main ENDP MySub PROC mov eax, edx // 00000040 -> MySub 程序第一行的 offset ret MySub ENDP ``` * call 呼叫程序 * ret (return) 回傳 * call 會 push 00000025 到 stack 上 * 然後把 00000040 存進 EIP(下一個執行指標) 裡 * 也就是把 call 下一行的 offset 先存進 stack 裡 * 這樣到時候 call 跑完後才會知道要從 main 的哪裡繼續執行 * ret 會從 stack pop 出 00000025 到 EIP 裡面 * 把剛剛存的 offset 重新存回 EIP * 回到 main 繼續執行 ![](https://i.imgur.com/7L4ij7K.png) ![](https://i.imgur.com/07OxzVF.png) ### 傳遞參數 * 一般都會使用暫存器作為存參數的地方 * 不會有 global local 的問題 ```javascript= theSum DWORD ? mov eax, 10000h ; 參數 1 mov ebx, 20000h ; 參數 2 mov ecx, 30000h ; 參數 3 call SumOf ; eax = ( eax + ebx + ecx) mov theSum, eax ; 存到 theSum 裡 ``` ### USES 運算子 ``` 程序名稱 PROC USES [暫存器名稱, 暫存器名稱, ...] ``` * ==列出程序中使用到的暫存器,就會被系統保留起來== * 在程序開始之初產生push指令 * 將暫存器值儲存到堆疊中 * 程序結束時產生pop指令回復 ```javascript= ArraySum PROC USES esi ecx mov eax, 0 L1: add eax, [esi] add esi, 4 loop L1 ret ArraySum ENDP ``` * 編碼會被組譯器產生 ```javascript= ArraySum PROC push esi // 由組譯器產生 push ecx // 由組譯器產生 mov eax, 0 L1: add eax, [esi] add esi, 4 loop L1 pop ecx // 由組譯器產生 pop esi // 由組譯器產生 ret ArraySum ENDP ``` CH6 Conditional Processing 條件處理 === ## 布林值運算 ### AND * 兩運算元中每對相對應位元間的 and 布林運算,**結果存放目的運算元** * 總是清除 OF 及 CF,根據目的運算元的值修改 SF、ZF、PF ``` AND 目的地, 來源 ``` ``` 00111011 00001111 (and ------------- 00001011 ``` * 功能:將字元轉換成大寫字母 ```javascript= 01100001 = 61h ('a') 11011111 = DFh (and ; 將字元與 11011111 作 and 運算轉成大寫 ----------------------------------- 01000001 = 41h ('A') ``` ### OR * 兩運算元中每對相對應位元間的 or 布林運算,**結果存放目的運算元** * 總是清除 OF 及 CF,根據目的運算元的值修改 SF、ZF、PF ``` OR 目的地, 來源 ``` ``` 00111011 00001111 (or ------------- 00111111 ``` * 功能:將0~9整數位元組轉換成 ASCII ```javascript= 00000101 = 05h 00110000 = 30h (or ; 將字元與 00110000 作 or 運算轉成ascii ----------------------------------- 00110101 = 35h ('5') ``` ### XOR * 兩運算元中每對相對應位元間的 互斥 布林運算,**結果存放目的運算元** * 總是清除 OF 及 CF,根據目的運算元的值修改 SF、ZF、PF ``` XOR 目的地, 來源 ``` ``` 00111011 00001111 (xor ------------- 00110100 ``` ### NOT * 把 0 變 1,1 變 0 * 不影響任何旗標 ``` NOT 目的地, 來源 ``` ``` 00111011 (not ------------- 11000100 ``` ### TEST ``` TEST 變數名稱, 變數名稱 ``` * 其實就是兩變數做 AND * 但是結果==不會存進目的變數裡面== * 不過結果會觸發 ZF(zero flag) * 常被用於==發現運算元個別的各個位元是否為 1== --- * EX:看看第 0、1 位元是否為 1 ```javascript= mov al, 00001101b ; 輸入值 test al, 00000001b ; 測試位元 0,結果為 00000001b,ZF = 0 ------------------------------ mov al, 00001101b ; 輸入值 test al, 00000010b ; 測試位元 1,結果為 00000000b,ZF = 1 ``` ### CMP ``` CMP 目的地, 來源 ``` * 兩變數作比較,看誰大誰小 * 其實就是把兩個變數==相減==,目的變數減來源變數 * 結果不會存進目的變數裡面 --- * 與 flag 的互動 * 無號數 * 如果目的 > 來源 $\rightarrow$ ZF $=$ 0, OF $=$ 0 * 如果目的 = 來源 $\rightarrow$ ZF $=$ 1, OF $=$ 0 * 如果目的 < 來源 $\rightarrow$ ZF $=$ 0, OF $=$ 1 * 有號數 * 如果目的 > 來源 $\rightarrow$ SF $=$ OF * 如果目的 = 來源 $\rightarrow$ ZF $=$ 1 * 如果目的 < 來源 $\rightarrow$ SF $\neq$ OF ## 清除或設定個別 CPU flag * 設定或清除 zero flag ```javascript= and al, 0 ; zero flag = 1,任何運算元和 0 作 and 運算 or al, 1 ; zero flag = 0,任何運算元和 1 作 or 運算 ``` * 設定或清除 sign flag ```javascript= or al, 80h ; sign flag = 1,任何運算元和 100000000 作 or 運算 and al, 7Fh ; sign flag = 0,任何運算元和 01111111 作 and 運算 ``` * 設定或清除 carry flag ```javascript= stc ; carry flag = 1 clc ; carry flag = 0 ``` * 設定或清除 overflow flag * 設定溢位旗標 * 將兩個正位元組值相加產生的和為負 * 清除溢位旗標 * 將一運元和 0 作 or 運算 ```javascript= mov al, 7Fh ; al = +127 inc al ; al = 80h(-128) , overflow flag = 1 or eax, 0 ; overflow flag = 0 ``` ## Conditional Jmps 條件跳越 * 條件跳越指令的類型 * 特殊的 flag 值 * 兩運算元是否相等 * Unsigned 數字間的比較 * Sign 數字間的比較 * 一些幫助記憶的代碼 * JA 無號數的大於、JB 無號數的小於 * JG 有號數的大於、JL 有號數的小於 * E -> equal 等於 * N -> not 反向 * 特別的 jump * JB、JC -> 如果 Carry flag 是 1 會跳到這個 label * JE、JZ -> 如果 Zero flag 是 1 會跳到這個 label * JS -> 如果 Sign flag 是 1 會跳到這個 label * JNE、JNZ -> 如果 Zero flag 是 0 會跳到這個 label * JECXZ -> 如果 ECX 是 0 會跳到這個 label ### 特殊的 flag 值 | 助憶符 | 說明 | 旗標 | |-----|----------|--------| | JZ | 若為零則跳 | ZF = 1 | | JNZ | 若不為零則跳 | ZF = 0 | | JC | 若進位則跳 | CF = 1 | | JNC | 若不進位則跳 | CF = 0 | | JO | 若溢位則跳 | OF = 1 | | JNO | 若不溢位則跳 | OF = 0 | | JS | 若負號則跳 | SF = 1 | | JNS | 若非負號則跳 | SF = 0 | | JP | 同位(偶)則跳 | PF = 1 | | JNP | 非同位(偶)則跳 | PF = 0 | ### 兩運算元是否相等 | 助憶符 | 說明 | |-------|--------------| | JE | 相等則跳 | | JNE | 不相等則跳 | | JCXZ | 若 CX = 0 則跳 | | JECXZ | 若 ECX = 0 則跳 | ### Unsigned 數字間的比較 | 助憶符 | 說明 | |------|-----------------| | JA | 較大則跳 | | JNBE | 不是較小或相等則跳 (=JA) | | JAE | 較大或相等則跳 | | JNB | 不是較小則跳 (=JAE) | | JB | 較小則跳 | | JNAE | 不是較大或相等則跳 (=JB) | | JBE | 較小或相等則跳 | | JNA | 不是較大則跳 (=JBE) | ### Sign 數字間的比較 | 助憶符 | 說明 | |------|-----------------| | JG | 較大則跳 | | JNLE | 不是較小或相等則跳 (=JG) | | JGE | 較大或相等則跳 | | JNL | 不是較小則跳 (=JGE) | | JL | 較小則跳 | | JNGE | 不是較大或相等則跳 (=JL) | | JLE | 較小或相等則跳 | | JNG | 不是較大則跳 (=JLE) | ### 一些例子 * 任務:如果 **unsigned** EAX 大於 EBX,就跳到標籤 ```javascript= cmp eax, ebx ja Larger ``` * 任務:如果 **signed** EAX 大於 EBX,就跳到標籤 ```javascript= cmp eax, ebx jg Greater ``` * 任務:如果 **unsigned** EAX 小於等於 Val1,就跳到標籤 ```javascript= cmp EAX, Val1 jbe L1 ``` * 任務:如果**signed** EAX 小於等於 Val1,就跳到標籤 ```javascript= cmp EAX, Val1 jle L1 ``` * 任務:比較 EAX EBX,把比較大的那個存進 Large 變數裡 ```javascript= mov Large, bx cmp ax, bx jna Next mov Large, ax Next: ``` * 任務:如果 ESI 指向的 WORD 記憶體大小等於 0,就跳到標籤 ```javascript= cmp WORD PTR [esi], 0 je L1 ``` * 任務:如果 ESI 指向的 DWORD 記憶體大小是偶數,就跳到標籤 ```javascript= test DWORD PTR [edi], 1 // 與 1 作 and jz L2 ``` * 任務:如果 AL 裡的第0, 1, 3位元都是 1 的話,就跳到標籤 ```javascript= and al,00001011b ; 清空不須要的 bit cmp al,00001011b ; 檢查剩下的 bit je L1 ``` ## 位元測試指令 ### BT (Bit Test) ``` bt bitBase, n 目標暫存器, 第n個位元 ``` * 從一個暫存器中,把第 n 個 bit 複制到 Carry flag 去 * EX:如果 AX 中第 9 個 bit 是 1 就跳到 L1 去 ```javascript= bt ax, 9 ; CF = bit 9 jc L1 ; jump if Carry ``` ## 條件迴圈指令 ### LOOPZ & LOOPE * LOOPZ 與 LOOPE 相同 * LOOPZ (loop if zero) * 允許迴圈繼續的條件 * ==Zero flag 值為 1== * ecx > 0 * 運行邏輯 1. ecx = ecx - 1 2. 若 ecx > 0 且 ZF = 1,則跳至目的 3. 否則不進行跳越而繼續下一個指令 * 常用在檢查矩陣中的值是否等於一個給定的值 ### LOOPNZ & LOOPNE * LOOPNZ & LOOPNE 相同 * LOOPNZ (loop if not zero) * 允許迴圈繼續的條件 * ==Zero flag 值為 0== * ecx > 0 * 運行邏輯 1. ecx = ecx - 1 2. 若 ecx > 0 且 ZF = 0,則跳至目的 3. 否則不進行跳越而繼續下一個指令 * 常用在檢查矩陣中的值是否不等於一個給定的值 ### EX:在矩陣中找第一個正數 ```javascript= .data array SWORD -3, -6, -1, -10, 10, 30, 40, 4 sentinel SWORD 0 .code mov esi, OFFSET array mov ecx, LENGTHOF array next: test WORD PTR [esi], 8000h ; test sign bit pushfd ; 將flag放入堆疊 add esi, TYPE array popfd ; 將flag從堆疊中拿出 loopnz next ; 迴圈繼續 jnz quit ; none found sub esi, TYPE array ; ESI 指向該值 quit: ``` ## 條件結構 ( Conditional Structures ) ### If 結構 ```javascript= if( op1 == op2 ) X = 1; else X = 2; -------------------------------- mov eax, op1 cmp eax, op2 jne L1 mov X, 1 jmp L2 L1: mov X, 2 L2: ``` ```javascript= if( ebx <= ecx ) { eax = 5; edx = 6; } -------------------------------- cmp ebx, ecx ja next mov eax, 5 mov edx, 6 next: ``` ### if 中有 and ```javascript= if (al > bl) && (bl > cl) X = 1; -------------------------------- // 用正面的邏輯去實作 // code 比較長 cmp al, bl ; 第一次比較 ja L1 jmp next L1: cmp bl, cl ; 第二次比較 ja L2 jmp next L2: ; 都是對的時 mov X, 1 ; 把 X 設成 1 next: -------------------------------- // 用反面的邏輯去實作 // code 比較短 cmp al, bl ; 第一次比較 jbe next ; 離開 if false cmp bl, cl ; 第二次比較 jbe next ; 離開 if false mov X, 1 ; 都是對的時 next: ``` ### if 中有 OR ```javascript= if (al > bl) || (bl > cl) X = 1; -------------------------------- cmp al, bl ; 看條件一是不是對的 ja L1 ; 對的,跳到 L1 cmp bl, cl ; 錯的,看條件二是不是對的 jbe next ; 錯的,跳過 L1 的標籤 L1: mov X, 1 ; set X to 1 next: ``` ### while 結構 ```javascript= while( eax < ebx) eax = eax + 1; -------------------------------- top: cmp eax, ebx ; 看看條件是不是對的 jae next ; 如果錯的話,離開迴圈 inc eax jmp top ; 重複迴圈 next: ``` CH7 Integer Arithmetic 整數算數 === ### logical shift 邏輯位移 ![](https://i.imgur.com/lAO0FK1.png) * 在移動位元後 * 在新建立的位元位置,==填入 0== ### Arithmetic Shifts 算術位移 ![](https://i.imgur.com/mHrKL2s.png) * 在移動位元後 * 在新建立的位元位置,填入==該數值原本的正負號位元== ### SHL (shift left) * 對目標運算元執行==向左====邏輯移位==,並且將最低位元填入 0。 ```javascript= shl reg, imm8 shl mem, imm8 shl reg, CL shl mem, CL ``` * SHL 可以執行 ==2 的次方==數的高速乘法 ```javascript= EX: 5 * 2 ^ 2 = 20 mov dl, 5 shl dl, 2 ; dl = 20 ``` ### SHR (shift right) * 對目標運算元執行==向右====邏輯移位==,並且將最高位元填入 0。 * SHR 可以執行 ==2 的次方==數的高速除法 ```javascript= EX: 20 / 2 ^ 2 mov dl, 5 shr dl, 2 ; dl = 5 ``` ### SAL 和 SAR ![](https://i.imgur.com/YC4DAky.png) * SAL 和 SHL 功能一樣 * SAR 可以對其目標運算元,執行==向右====算數位移==的運算 * 算術位移保持數字的符號 ```javascript= mov dl, -80 sar dl, 1 ; DL = -40 sar dl, 2 ; DL = -10 ``` ### ROL 和 ROR * ROL 將每個位元向左移位 * 最高位元會複製到 ==CF== 以及 ==最低位元位置== * 沒有位元遺失 ```javascript= mov al, 11110000b rol al, 1 ; AL = 11100001b mov dl, 3Fh rol dl, 4 ; DL = F3h ``` ![](https://i.imgur.com/MQsSp5H.png) * ROR 將每個位元向右移位 * 最低位元會複製到 ==CF== 以及 ==最高位元位置== * 沒有位元遺失 ```javascript= mov al, 11110000b ror al, 1 ; AL = 01111000b mov dl, 3Fh ror dl, 4 ; DL = F3h ``` ![](https://i.imgur.com/0Qe8iI1.png) ### RCL 和 RCR * RCL 將每個位元向左移 * CF 會複製到最小有效位元 * 最大位元複製到 CF 中 ```javascript= clc ; CF = 0 mov bl, 88h ; CF,BL = 0 10001000b rcl bl, 1 ; CF,BL = 1 00010000b rcl bl, 1 ; CF,BL = 0 00100001b ``` ![](https://i.imgur.com/hD6QBvN.png) * RCR 將每個位元向右移 * CF 會複製到最大有效位元 * 最小位元複製到 CF 中 ```javascript= stc ; CF = 1 mov ah, 10h ; CF,AH = 00010000 1 rcr ah, 1 ; CF,AH = 10001000 0 ``` ![](https://i.imgur.com/ybHX0GI.png) ### SHLD 和 SHRD * SHLD 將運算元向左移位指定的位元數 * 移位過程中所產生的未定位元,填入來源運算元==最大==有效位元的位元值 * 來源運算元的值並不會受影響 ```javascript= SHLD destination, source, count ``` * EX: * 將 wval 左移 4 位元 * 將 AX 的最高四個位元,插入 wval 的最低 四個位元 ```javascript= .data wval WORD 9BA6h .code mov ax, 0AC36h shld wval, ax, 4 ``` ![](https://i.imgur.com/Xs9J4Og.png) * SHRD 將運算元向右移位指定的位元數 * 移位過程中所產生的未定位元,填入來源運算元==最小==有效位元的位元值 * 來源運算元的值並不會受影響 ```javascript= SHRD destination, source, count ``` * EX: * 將 wval 右移 4 位元 * 將 AX 的最小四個位元,插入 wval 的最高四個位元 ```javascript= mov ax, 234Bh mov dx, 7654h shrd ax, dx, 4 ``` ![](https://i.imgur.com/IbVuQOY.png) ### 一些例子 #### 算算數 * AX 乘以 26 ```javascript= mov ax, 2 ; test value mov dx, ax shl dx, 4 ; AX * 16 push dx ; save for later mov dx, ax shl dx, 3 ; AX * 8 shl ax, 1 ; AX * 2 add ax, dx ; AX * 10 pop dx ; recall AX * 16 add ax, dx ; AX * 26 ``` #### 取日期 ![](https://i.imgur.com/0N9VHs8.png) * 把月份取出來 ```javascript= mov ax, dx ; make a copy of DX shr ax, 5 ; shift right 5 bits and al, 00001111b ; clear bits 4-7 mov month, al ; save in month variable ``` CH8 Advanced Procedures 程序 === ### 函式呼叫與 stack * 使用 stack 來存取 * return 的位址 * 傳遞的參數 * 函式內的區域變數 * 什麼是 EBP * EBP -> base pointer * EBP 中存著在 stack 中的 return 位址 * EBP 的指標位址並不會改變 * 為什麼要 EBP * 因為 ESP 的值會隨著 stack 的增加而變動 * 指到記憶體的位址會有所不同 * 這樣去存取變數很麻煩 * 所以設計一個 EBP 使它等於一開始的 ESP ![](https://i.imgur.com/fIJaU3v.png) ![](https://i.imgur.com/hNAVOW2.png) ![](https://i.imgur.com/7BaM1JG.png) ![](https://i.imgur.com/HhYMUXD.png) * stack 注意事項 * PUSH、POP 兩個指令 * ESP (stack pointer) EBP register 的搭配 * ==PUSH 會將 ESP 中的位址 - 4== * 如何實作一個 stack 1. 呼叫一個函式 2. 把參數 push 到 stack 裡 3. 把 return 的位址 push 到 stack 裡 4. 在函式裡把 EBP push 到 stack裡 5. 把 EBP 等於 ESP 6. 如果有需要使用到區域變數,要先將 ESP 往下移出一些空間出來 ```javascript= push ebp ; 將 EBP 的內容先存到 stack 中 mov ebp, esp ; EBP = ESP ; 函式的實作內容置於此處 pop ebp ; 還原 EBP 的值 ret ``` ```javascript= .data sum DWORD ? .code push 6 ; 第二個參數 push 5 ; 第一個參數 call AddTwo ; EAX = sum mov sum, eax ; save the sum AddTwo PROC push ebp ; stack 起手式 mov ebp, esp mov eax, [ebp + 12] ; 存取第二個參數 (6) 使用位址來存取參數 add eax, [ebp + 8] ; 存取第一個參數 (5) 使用位址來存取參數 pop ebp ret 8 ; stack 起手式結束 AddTwo ENDP ; EAX contains the sum ``` ![](https://i.imgur.com/0gKBl2g.png) ### 區域變數 #### 如何定義一個區域變數 * 使用 LOCAL 自訂變數名稱 * 使用 stack 中的 EBP 直接指向記憶體 * ==EBP 減掉所有區域變數的大小== ```javascript= push ebp mov ebp, esp sub esp, LOCAL_BYTES ; 在此處根據需求保留給 local 變數的空間 ; subprogram 的實作內容置於此處 mov esp, ebp pop ebp ret ``` #### LOCAL * 定義一個區域變數 * 直接接在 PROC 後面 * 需要給定大小 ```javascript= MySub PROC LOCAL var1:BYTE, var2:WORD, var3:SDWORD ``` #### 使用 EBP ![](https://i.imgur.com/HhYMUXD.png) * 第一個==參數== -> EBP + 8 * 第二個==參數== -> EBP + 12 * 第一個==區域變數== -> EBP - 4 * 第二個==區域變數== -> EBP - 8 . . . * 函式的例子 ```cpp= ; (C 語言程式碼) ; void calc_sum(int n, int *sump) { ; int i, sum = 0; ; ; for(i = 1 ; i <= n ; i++) ; sum += i; ; ; *sump = sum; ; } ``` ```javascript= calc_sum: push ebp mov ebp, esp sub esp, 4 ;保留儲存 local 變數的 memory address 的空間 mov dword [ebp - 4], 0 ; sum = 0; mov ebx, 1 ; 假設 EBX = i for_loop: cmp ebx, [ebp + 8] ; i <= n jnle end_for ; jump if not(less & equal) add [ebp - 4], ebx ; sum += i inc ebx jmp for_loop end_for: mov ebx, [ebp + 12] ; ebx = sump mov eax, [ebp - 4] ; eax = sum mov [ebx], eax ; *sump = sum mov esp, ebp pop ebp ret ``` CH9 Strings and Arrays === * 處理 array 的指令 * 使用 ESI、EDI 做為索引之用 * DF 設定好後,ESI EDI 會自動加減 * Direction flag (DF) 來判斷索引為遞增或遞減 * CLD:將 DF 設定為 0,為==遞增== * STD:將 DF 設定為 1,為==遞減== * Repeat Prefix 可以用來重複陣列操作指令,方向由 DF 控制 * 用法 : REP [指令] * ==重複條件 : ECX > 0== * 比較用延伸用法(CMP) : REPZ、REPNZ ### LOASx 與 STOSx #### LOASx (Load) * 把 ==ESI== 指向的值存入 AL/AX/EAX * EX:把一個陣列內的值印出來 ```javascript= .data array BYTE 1, 2, 3, 4, 5, 6, 7, 8, 9 .code mov esi, OFFSET array mov ecx, LENGTHOF array cld L1: lodsb ; 從 AL 中讀取值 or al, 30h ; convert to ASCII call WriteChar ; display it loop L1 ``` #### STOSx (Store) * 把在 AL/AX/EAX 中存入的值,放進 ==EDI== 所指向的位置 * EX:把一個陣列放滿 0FFh ```javascript= .data Count = 100 string1 BYTE Count DUP(?) .code mov al, 0FFh ; value to be stored mov edi, OFFSET string1 ; 用 EDI 指向存入目標 mov ecx, Count ; character count cld ; 把 DL 設成往上加 rep stosb ; fill with contents of AL ``` ### MOVSx * 將記憶體中 ESI 指向的位置的值複製到 EDI 指向的位置 * EX:把一個陣列的值複製到另陣列中 ```javascript= .data source DWORD 0FFFFFFFFh target DWORD ? .code mov esi, OFFSET source mov edi, OFFSET target movsd ``` ### CMPSx * 將記憶體中比較 ESI 位置與 EDI 位置的值 * EX:如果 source > target,跳至 label L1,不然跳至 label L2 ```javascript= .data source DWORD 1234h target DWORD 5678h .code mov esi, OFFSET source mov edi, OFFSET target cmpsd ; 比較 ESI EDI ja L1 ; jump if source > target jmp L2 ; jump if source <= target ``` ### SCASx * 將記憶體中比較 AL/AX/EAX 中==存入的值==與 ==EDI 位置的值== * EX:在一個陣列中找 "F" 這個字元 ```javascript= .data alpha BYTE "ABCDEFGH", 0 .code mov edi, OFFSET alpha mov al, 'F' ; 把 'F' 放入 AL 中代表我們要找它 mov ecx, LENGTHOF alpha cld repne scasb ; 重複直到不一樣 jnz quit dec edi ; EDI points to 'F' ``` ### 二維陣列 * 之前有提到過二維陣列可以用下面這個方法定義 ```javascript= table BYTE 10h, 20h, 30h, 40h, 50h BYTE 60h, 70h, 80h, 90h, 0A0h BYTE 0B0h, 0C0h, 0D0h, 0E0h, 0F0h NumCols = 5 ``` * 現在也可以用這個方法定義 ```javascript= table BYTE 10h, 20h, 30h, 40h, 50h, 60h, 70h, 80h, 90h, 0A0h, 0B0h, 0C0h, 0D0h, 0E0h, 0F0h NumCols = 5 ``` * 只不過要用下面的方法來取值 * $data(n, m) = n*M + m$ * $n$ = 列 * $m$ = 行 * $M$ = 原定義的陣列中行數為多少 * EX:在上面 5x3 的陣列中取 table[1][2] ```javascript= RowNumber = 1 ColumnNumber = 2 mov ebx, NumCols * RowNumber ; NumCols = 5 mov esi, ColumnNumber mov al, table[ebx + esi] ``` ![](https://i.imgur.com/n4r9e6q.png) CH10 Structure and Macro === ### Structure 結構 * 給予邏輯相關變數的模板或模式。 * 巢狀結構 * SIZE 與 LENGTH 皆為其所有變數的總和 * 宣告成結構陣列要特別小心,每次換到下一個陣列值的地址差距 * 定義方式 ```javascript= name STRUCT STRUCT 裡面放的各式變數 name ENDS ``` * 宣告、使用方式 ```javascript= .data name StructName < 變數1, 變數2.....> .code MOV 對應暫存器, name.變數1 ``` * EX: ```javascript= ; 定義 STRUCT Employee STRUCT IdNum BYTE "000000000" LastName BYTE 30 DUP(0) Years WORD 0 SalaryHistory DWORD 0,0,0,0 Employee ENDS ---------------------------------- ; 取 STRUCT 中的值 mov dx, worker.Years mov esi, OFFSET worker mov ax, (Employee PTR [esi]).Years mov ax, [esi].Years ; 無效,指令不明確 ``` ### Union 聯集 * 與結構相似,但是內部所有變數共享一個記憶體位置 * SIZE 與 LENGTH 由變數中最大者決定 * 宣告方式和使用方式與Struture類似 ```javascript= name UNION UNION 裡面放的各式變數 name ENDS ``` ### MACRO 巨集指令 * MACRO 類似於指令函數,定義完後可以重複使用,組譯時會將所有MACRO全部取代成定義好的指令集 * 定義方式 ```javascript= name MACRO [ 參數1, 參數2...] //參數可選 MACRO 裡面放的指令集 name ENDM ``` ## Conditional-Assembly Directive 條件式組譯指引 * 組譯時就會運行這些指引,可以完成自我報錯的功能 ### IFB * 若參數為空(呼叫時沒給),進入IFB' * EXITM 後面可以加 <-1>(ture) 或 <0>(false) ```javascript= IFB <參數> //若參數為空 = true EXITM // 退出巨集 ENDIF ``` ### IF, ELSE, ENDIF ```javascript= IF boolean-expression statements [ELSE //可選 statements] ENDIF ``` * Boolean Expression * LT、GT、EQ、NE、LE、GE ### IFIDN, IFIDNI * 比較兩個給予的參數名稱,若相同 = true * IFIDN 有大小寫分別、IFIDNI 沒有 ```javascript= IFIDNI <參數1>, <參數二> //此處參數可為任何內容、主要是比名稱 statements ENDIF ``` ## Special Operators 特殊運算子 * Substitution (&) 用於代替參數成被MACRO定義的對象 ```javascript= ShowRegister MACRO regName //宣告MACRO,將regName改成參數 .data tempStr BYTE " &regName=",0 //本宣告內容會變成 " EDX=" .code ShowRegister EDX //呼叫巨集,使此參數改名為EDX ``` * Expansion (%) 用於將參數從常數改成文本提供MACRO輸入 * mGotoXY %( 5*10), %( 3+4) → mGotoXY 50, 7 * Literal-Text (<>) 用於將多個文本參數組合成單一文本 * Literal-Character (!) 用於將上述特殊運算子改成正常文本 ## Repeat Directive 迴圈指引 ### WHILE ```javascript= WHILE constExpression //類似於boolean-expression statements ENDM //迴圈直到constExpression = false ``` ### REPEAT ```javascript= REPEAT constExpression //參數為重複的次數 statements ENDM //迴圈重複的次數 ``` ### FOR * 迴圈會重複將引數代入參數後執行,然後重複至所有引數都被代入過 ```javascript= FOR 參數,< 引數1, 引數2, 引數3,...> statements ENDM ``` ### FORC * 類似於FOR,只不過帶入的改為字串中的每一個字元 ```javascript= FOR 參數,<字串> statements ENDM ``` Reference === * 小信豬的原始部落 * http://godleon.blogspot.com/2008/01/machine-language-cpu-machine-language.html * 組語維基教科書 * https://zh.m.wikibooks.org/wiki/X86%E7%B5%84%E5%90%88%E8%AA%9E%E8%A8%80 * Xuite 筆記 * https://blog.xuite.net/asd.wang/alog/269336-%5BMasm%5D+Assembly+%E7%AD%86%E8%A8%98+-+Ch3+%E7%B5%84%E5%90%88%E8%AA%9E%E8%A8%80%E5%9F%BA%E7%A4%8E