彙編筆記(持續更新中)

-风间琉璃-發表於2024-11-23

彙編筆記


暫存器register

​ 學習組合語言,首先必須瞭解兩個知識點:暫存器和記憶體模型。

​ 先來看暫存器。CPU 本身只負責運算,不負責儲存資料。資料一般都儲存在記憶體之中,CPU 要用的時候就去記憶體讀寫資料。但是,CPU 的運算速度遠高於記憶體的讀寫速度,為了避免被拖慢,CPU 都自帶一級快取和二級快取。基本上,CPU 快取可以看作是讀寫速度較快的記憶體。

​ 但是,CPU 快取還是不夠快,另外資料在快取裡面的地址是不固定的,CPU 每次讀寫都要定址也會拖慢速度。因此,除了快取之外,CPU 還自帶了暫存器(register),用來儲存最常用的資料。也就是說,那些最頻繁讀寫的資料(比如迴圈變數),都會放在暫存器裡面,CPU 優先讀寫暫存器,再由暫存器跟記憶體交換資料。


記憶體模型:Heap

  • 程式執行過程中,對於動態的記憶體佔用請求(比如新建物件,或者使用malloc命令),系統就會從預先分配好的那段記憶體之中,劃出一部分給使用者,具體規則是從起始地址開始劃分(實際上,起始地址會有一段靜態資料,這裡忽略)。舉例來說,使用者要求得到10個位元組記憶體,那麼從起始地址0x1000開始給他分配,一直分配到地址0x100A,如果再要求得到22個位元組,那麼就分配到0x1020

  • 這種因為使用者主動請求而劃分出來的記憶體區域,叫做 Heap(堆)。它由起始地址開始,從低位(地址)向高位(地址)增長\(Heap\) 的一個重要特點就是不會自動消失,必須手動釋放,或者由垃圾回收機制來回收

img


記憶體模型: Stack

  • 除了 Heap 以外,其他的記憶體佔用叫做 Stack(棧)。簡單說,Stack 是由於函式執行而臨時佔用的記憶體區域

  • 系統開始執行main函式時,會為它在記憶體裡面建立一個幀(frame),所有main的內部變數(比如ab)都儲存在這個幀裡面。main函式執行結束後,該幀就會被回收,釋放所有的內部變數,不再佔用空間。

  • 在函式內部在呼叫一個函式process()的時候,系統也會為該函式再新建一個幀,一般來說呼叫棧有多少層,就有多少幀。

  • 當函式執行結束之後,它的幀會被回收,系統回到函式中斷執行的地方繼續執行上一個幀的流程。透過這個機制,可實現函式的層層呼叫,且每一層函式都可以使用自己的本地變數

  • Stack是由記憶體區域的結束地址開始分配,從高位到地位分配

img


CPU指令

使用 g++ file.cpp -S out.s或者gcc file.cpp -S out.s,可用C/C++生成相應的彙編程式碼

#include<stdio.h>
void hello()
{
    int a=1,b=2;
    a=a+b;
    printf("hello world\n");
}
int main()
{
    int a=1,b=2;
    a=a+b;
    hello() ;

    return 0;
}
.LC0:
        .string "hello world"
hello():
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 1
        mov     DWORD PTR [rbp-8], 2
        mov     eax, DWORD PTR [rbp-8]
        add     DWORD PTR [rbp-4], eax
        mov     edi, OFFSET FLAT:.LC0
        call    puts
        nop
        leave
        ret
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 1
        mov     DWORD PTR [rbp-8], 2
        mov     eax, DWORD PTR [rbp-8]
        add     DWORD PTR [rbp-4], eax
        call    hello()
        mov     eax, 0
        leave
        ret

push 指令

	push	rbp

pushCPU指令,rbp是該指令的運算元.一個CPU指令可以有0到多個運算元


暫存器

IA-32體系結構中有10個32位和6個16位處理器暫存器。暫存器分三類:

  • 通用暫存器

    • 資料暫存器

    • 指標暫存器

    • 索引暫存器

  • 控制暫存器

  • 段暫存器


指令系統

80x86定址方式
  • 定址:尋找運算元的地址。

  • 定址方式:尋找運算元的方式

80x86指令格式
  • 指令助記符 +運算元1,+運算元2,+運算元3
  • 指令的運算元個數可以是0,1,2,3個
如何確定偏移地址的值
  • 與資料有關的十種方式,
    1. 立即定址
    2. 暫存器定址
    3. 直接定址
    4. 暫存器間接定址
    5. 暫存器相對定址
    6. 基址變址定址
    7. 相對基址變址定址
    8. 比例變址定址
    9. 基址比例變址定址
    10. 相對基址比例變址定址
  • 與轉移地址有關的4種定址方式
    1. 段內直接定址
    2. 段內間接定址
    3. 段間直接定址
    4. 段間間接定址

定址方式

立即定址方式

MOV AX, 5
MOV AX, 05H

把十六進位制數05H 移動到通用暫存器AX

暫存器定址方式(無EA)

  • 指令所要的運算元已儲存在暫存器中或者把目標數存入暫存器
MOV AX , BX
  • 指令中可以引用的暫存器及其符號名稱:

8位暫存器 AH、AL、BH、BL、CH、CL、DH、DL
8位暫存器 AX、BX、CX、DX、SI、DI、SP、BP和段暫存器等
8位暫存器 EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP。
  • DST 和SRC的字長一致

  • CS不能用MOV指令改變

直接定址方式

  • 指令所要的運算元存放在記憶體中,在指令中直接給出該運算元的有效地址

  • 實體地址=\((DS)\times16d+EA\)

    • MOV AX , [2000H]

    • MOV AX, VALUE

    • MOV AX , [VALUE]

  • 實體地址=\((ES)\times16d+EA\)

    • MOV AX , ES:[2000H]

    • MOV AX, ES:VALUE

    • MOV AX , ES:[VALUE]

    • 在通常情況下,運算元存放在資料段中,所以,其實體地址將有資料段暫存器DS和指令中的有效地址直接形成但如果使用段超越字首,那麼運算元可存放在其他段中。如:

      MOV ES:[1000H],	AX
      
    • 立即定址:1234H

    • 直接定址;[1234H]

暫存器間接定址方式→EA基址/變址

  • MOV AX,[BX]MOV AX,ES:[BX]

暫存器相對定址方式→EA=基址/變址+位移量

  • MOV AX,COUNT[SI]MOV AX,3000H[SI]

  • MOV AX,[COUNT+SI]MOV AX,[3000H+SI]

  • MOV AX,ES:COUNT[SI]MOV AX,ES:[COUNT+SI]

  • 運算元在儲存器中,其有效地址是一個基址暫存器(BXBP)或變址暫存器(SIDI的內容和指令中的8位/16位偏移量之和

基址變址定址方式→EA=基址+變址

  • 運算元在儲存器中,其有效地址是一個基址暫存器(BX、BP)和一個變址暫存器(SIDI)的內容之和

    • MOV AX,[BX][SI]MOV AX,[BX+SI]
    • MOV AX,ES:[BX][SI]MOV AX,ES:[BX+SI]

相對基址變址定址方式→EA=基址+變址+位移量

  • MOV AX,COUNT[BX][SI] MOV AX,-46H[BX][SI]
  • MOV AX,COUNT[BX+SI] MOV AX,0246H[BX+SI]
  • MOV AX,[COUNT+BX+SI]MOV AX,[-56H+BX+SI]
  • MOV AX,ES:COUNT[BX][SI]MOV AX,ES:[0246H+BX+SI]

相對比例變址定址→EA=變址×比例因子+位移量

  • MOV EBX,[EAX][EDX*8]MOV EBX,

  • [EAX+EDX\*8]MOV EBX,[ESP][EAX*2]

  • MOV EAX,TABLE[EBP][EDI*4]MOV EAX,[TABLE+EBP+EDI\*4]

​ 8位/16位/32位的位移量

注意1:比例因子只能與32位變址暫存器:EAX、EBX、ECX、EDX、EBP、ESI、

EDI聯用;且比例因子只能為1、2、4或8;

注意2: 8)、9)、10)只能是32位定址,沒有16位定址;

  • 基址比例變址定址→EA=基址+變址×比例因子

  • 相對基址比例變址定址→EA=基址+變址×比例因子+位移


彙編程式

  • 段結束:

    • xxx ends (xxx 即為段名)
  • 程式結束:

    • end
  • 程式返回:

    • MOV AX 4C00H
      INT 21 
      


彙編debug指令

DOSbox中輸入debug 即可進入debug模式

  • -d xxxx:xxxx 即可讀取相應的記憶體的資訊。 (dump)

  • -e xxxx:xxxx aa bb cc ddaa bb cc dd寫入對應的地址中。

  • -e 0000:0000 12.AB 34.CD 把記憶體對應的 12 修改成 AB, 34 修改成 CD

  • -a 輸入彙編指令

  • -t 執行 -a輸入的彙編指令。

.......回頭再補吧,debug 指令先放一下。


彙編基本指令

常用指令

  • add

  • add ax ,10 , 意思就是ax +=10

  • sub

    • sub ax ,10 意思就是 ax-=10
  • mul

    • 兩個相乘的數,要麼都是八位,要麼都是十六位.
      • 如果是8位,一個預設放在AL中 ,另一個放在8位reg 或記憶體位元組單元中。
      • 如果是16位,一個預設放在AX中,另一個放在16位reg 或記憶體位元組單元中。
  • div

  • shl

    • shl al ,1 左移一位。
  • shr

    • shr al ,1 右移一位。
  • rol

    • rol al ,1 迴圈左移。
  • ror

    • ror al ,1 迴圈右移
  • rcr

    • rcr al ,1 帶進位的迴圈右移。
  • rcl

    • rcl al ,1 帶進位的迴圈左移。
  • inc

    • inc ax 自增。
  • dec

    • dec ax 自減。
  • xchg

    • xchg ax, bx 交換。
  • mov bx , word ptr [0]

    • 位擴充。
  • OFFSET

    ​ 指定偏移地址指向的內容 [BX]。 (等價於DS:[BX])

    SEG 可以獲取段地址。

  • 中斷指令

    • int 0

      • 除法除以0 的中斷
    • 	MOV AX , 4C00H
      	INT 21H
      
      • 退出程式
  • jmp 跳轉指令

    • jmp 1000:0003 EA 1000: 0003 就是jmp的記憶體中顯示的格式,代表跳轉到 1000:0003
  • push ax :

    • 注意 若 ax : 1234H ,則它在記憶體裡面的儲存方式是: 34 12 , 從低到高的順序, 內部順序不變

    • 棧的儲存順序是從後往前依次儲存的.

  • pop ax :

    • 將棧頂元素彈出並賦值給ax

段地址和偏移地址

​ 段地址*16 +偏移地址 = 實體地址

0000:0100

​ :前面的是段地址 , : 後面的是偏移地址。

DS 暫存器的使用

			mov  AX, [60]

​ 相當於把 DS : 60 處的兩個位元組存入AX 中

  • MOV DS , BX 可以把暫存器裡的值賦給DS ,但不能把立即數賦值給DS。

MOV DS , 1000 (該操作是不被允許的)

  • 未宣告段地址的情況下預設段地址就是 DS。

宣告變數

	DATA SEGMENT 

		_DATA DB 100(?)

			;100 就是多少個? ,?代表待定

	DATA ENDS

暫存器的種類及作用

  • 8086 CPU 有14 個暫存器 , 他們的名稱為: AX , BX , CX , DX , SI , DI ,SP , BP , IP , CS , SS , DS , ES , PSW , 所有的的暫存器都是16位的,可以放兩個位元組。

  • AX: 累加器。

    muldiv 作用有關。

  • BX : 基地址暫存器,可以儲存地址並訪問

    • 地址表達方式 : 240B:1001
  • AX ,BX ,CX ,DX 都屬於通用暫存器,即可以把一個16位暫存器拆成兩個8位暫存器使用。

段暫存器 CS : IP
  • CS:IP 即為可執行彙編指令的儲存的位置
  • 組合語言中程式碼和資料是不加區分的,都存在於記憶體裡面.
棧暫存器 SS: SP:

​ 任意時刻 , SS:SP 指向棧頂元素

  • 壓棧 sp -=2

  • 彈出 sp+=2

  • 當棧已空的時候, 如果繼續彈出, 計算機會繼續進行 把sp指向的記憶體彈出並進行sp+=2的操作 .

  • 棧的容量

    • 空棧:在進行堆疊操作前,為空棧。此時SP應預置一個初值。該值為堆疊空間的大小

    • SP初值=堆疊空間的最大容量

      例:SP=0008H。則最大容量為8個位元組。

      SP指向當前的棧頂。

如何避免棧頂超界??

​ 只能透過人為注意棧頂是否超界的問題

相關文章