一週幹掉彙編語言 #Day1 #基礎知識 #寄存器 #CPU #內存


基礎不會,啥都白費;基礎不牢,地動山搖。
彙編在基礎中的地位舉足輕重,學習彙編,可以幫助我們從CPU角度出發,理解程序,寫出更好的高級語言 程序。可以幫助我們理解程序的運行機制,知道原理,解決一些隱蔽的BUG。
學習步驟: 看視頻,看書,做筆記,理解爲主。習題獨立完成,全對,才能進入下一章的學習。
視頻: 小甲魚
教材: 《彙編語言(王爽)》(三版)
日期: 2020-06-25

進度:19/77

一、基礎知識:5/77

1. 兩個語言

機器語言: 一串二進制數。轉變爲電信號就可以控制計算機活動。


彙編語言: 彙編指令 + 僞指令 + 其它符號
 - 彙編指令: 機器語言助記符,和機器語言一一對應

注意: 機器型號不一樣,所對應的彙編指令集也不一樣

 - 僞指令/其它符號: 由編譯器識別/執行,沒有對應的機器語言


2. 存儲單元&三類信息

存儲單元: 一個存儲單元,可以存儲1B的數據


三類信息: CPU進行數據讀寫,需要進行三類信息的交互
 1. 地址信息: 數據所在地址
 2. 數據信息: 數據
 3. 控制信息: 讀 or 寫


讀寫流程: 得到地址,發出控制,找到數據


總線: 地址線 + 數據線 + 控制線
 1. 地址總線: 寬度表示CPU尋址能力(CPU位數)。假設寬度爲N,那麼最多可以尋找(A21)N({A^1_2})^N個內存單元(一個內存單元1B )

注意: 一個64位需要滿足三條件(不滿足的話,就是假64位):64位CPU + 64位操作系統 + 64位軟件

 2. 數據總線: 寬度表示CPU一次能讀寫的數據大小。一根線一次傳1b,8根線一次傳1B。
 3. 控制總線: 寬度表示CPU最多能控制的外部器材的數量。

檢測點 1.1

  1. 2132^{13}。尋址能力爲8KB,即232102^3 * 2^{10}
  2. 2102^{10}, 0, 21012^{10} -1
  3. 2132^{13}, 2102^{10}
  4. 2302^{30}, 2202^{20}, 2102^{10}
  5. 262^6, 202^0, 242^4, 222^2216/210=262^{16}/2^{10} = 2^6, 以此類推。
  6. 1, 1, 2, 2, 4。除以8即可。
  7. 512, 256。210/212^{10}/2^1210/222^{10}/2^2
  8. 二進制數

3. 存儲器

存儲器: 隨機存儲器RAM(隨機存儲器,接口卡上的),只讀存儲器ROM(刷了BIOS的)
 - 隨機存儲器: 斷電丟失數據,如主存(內存)
 - BIOS: 標準輸入輸出系統。用來檢測硬件,加載操作系統的。

補充: 往顯卡RAM中寫入數據,就會顯示在顯示器中。


  • 抽象連接情況: (物理層面)
    在這裏插入圖片描述

  • 抽象連接情況: (邏輯層面)
    上述各部件在物理上是獨立的,但是在邏輯上,都是與主線相連,可以把各部分存儲器看做一個整體。在CPU看來,這些存儲器是一個整體,從上往下線性地存儲

注意: 運行程序的主體是CPU,所以,我們必須站在CPU的角度思考問題。

在這裏插入圖片描述

補充: 每個物理存儲器,佔用一個地址段(區間)
注意: 不同的計算機內存地址分配是不同的,《彙編語言(王爽)》(三版)使用的是Intel8086


8080PC內存地址空間分配:
在這裏插入圖片描述

二、 寄存器(CPU工作原理):10/77

1. 總論

CPU:運算器、控制器、寄存器 等組成,依靠內部總線 相連(連接主板上外部存儲器的是外部總線

擴展: 可以看看《程序是怎樣跑起來的》這本入門書


8086寄存器: 8086的寄存器都是16位(可存放兩字節)。有以下14個寄存器:AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW

2. 通用寄存器

AX、BX、CX、DX 稱爲通用寄存器

在這裏插入圖片描述

注意: 8086上一代CPU的寄存器都是8位的,爲了向下兼容,8086CUP上的AX、BX、CX、DX都可以拆成兩個可獨立使用的8位寄存器來用。
比如,AX 分爲AHAL (其中H 是高, L 是低的意思),如果要向下兼容,就使用ALAH 全部填0。

3. 字與寄存器

字: 一個字 = 2B,一個字的高位字節存放在寄存器高八位,低位字節存放在低八位

補充: 閱讀二進制不方便,所以我們一般寫作16進制。4個二進制數就是一個十六進制。爲了區分不同進制,二進制後加B,十六進制加H,八進制加O。

4. 彙編指令(部分)

注意: 彙編指令不區分大小寫

彙編指令 CPU操作 對應高級語言
mov ax, 7 把7存入ax ax = 7
add ax, bx 把ax + bx的結果存入ax ax += bx

練習題2.1 :

在這裏插入圖片描述
練習題2.2:

在這裏插入圖片描述

注意: 進行運算時,位數須一致。比如8位到8位,16位到16位。

檢測點 2.1

  1. F4A3H
    31A3H。F4是高位,被替換爲31。
    3123H
    6246H
    826CH
    6246H
    826CH
    164CH
    04D8H。104D8H,最高位溢出
    0482H
    6C82H
    D882H
    D888H
    D810H。低位爲110,作爲一個獨立的存儲器使用,1不能交給高位,直接溢出捨棄。
    6246H
  2. mov ax, 2
    add ax, ax
    add ax, ax
    add ax, ax

5. 物理地址

物理地址: CPU認爲,所有存儲器是一個整體,每個存儲單元都有其唯一地址,這個唯一地址 被稱爲物理地址


16位結構CPU: 具有以下特徵

  1. 運算器一次最多可以處理16位數據
  2. 通用寄存器最大寬度爲16位
  3. 寄存器和運算器之間的通路爲16位

一個疑問? 8086有20位地址總線,尋址能力爲1M。可是它是16位系統,只能傳送16位地址,尋址能力只有64k。那麼怎麼能達到1M的尋址能力呢?

  • 答: 8086的CPU在內部用兩個16位的地址合成的方法來合成一個20位的地址。
    在這裏插入圖片描述
    具體的步驟見下節。

6. 段地址&偏移地址 【重點】

=16+物理地址 = 段地址*16 + 偏移地址

  • 例子: 段地址1230H,偏移地址00C8H。物理地址 = 1230H*16 + 00C8H = 12300H + 00C8H = 123C8H
  • 解釋: 段地址*16,對於二進制而言,就是往左移4位。4+16正好20位。

  • 段地址: 內存並不是被分爲一段一段的,內存就是一條。段地址是源於8086的無奈之舉,與真正的分段無關係,真正的分段是由程序員爲了使用方便而在心裏上的一種劃分,可根據需要,把地址連續,起始地址爲16的倍數的一組內存單元定義爲一個段

偏移地址爲16位,16位的尋址能力爲64K,所以一個段的最大長度爲64K。


CPU可以通過不同的段地址和偏移地址到達同一個物理地址,這就是條條大路通羅馬在這裏插入圖片描述


補充: 數據A13C6H在8086中可以表述爲A000:13C6單元中;也可以表述爲在A000段中的13C6單元中。

檢測點 2.2

  1. 0010H, 1000FH。SA16+EA=0001H16+EA=0010H+(0000HSA*16 + EA = 0001H*16 +EA = 0010H + (0000H ~ FFFFH)FFFFH)
  2. 1001H, 2000H,。SA=SA = (20000HSE20000H - SE)/$16
    最小:SE = FFFFH時,算出10001H/16, 向上取整地1001H。
    最大:SE = 0000H時。算出2000H。

7. 段寄存器

8086CPU有4個段寄存器:

CS DS SS ES
英文 code statement data … stack… extra…
解釋 代碼段(寄存器) 數據… 堆棧… 附加段

CSIP是8086CPU中最關鍵的寄存器,它們指示了CPU當前要讀取指令的地址。

  • CS: 代碼段寄存器
  • IP: 指令指針寄存器

注意: 此IP非彼IP

在這裏插入圖片描述
步驟:

  1. CS和IP經過地址加法器,變爲20000H
  2. 通過20位地址總線,找到20000H,這個指令是3位,所以一下讀取3位
  3. 組成B80123的指令(B8是mov ax的意思。23和01是數據,因爲下面的01是高位,上面的23是低位,所以是0123H。
  4. 通過數據總線傳遞給指令緩衝器,然執行控制器執行
  5. 執行完畢後, 把0123的值賦給AX
  6. 由於一次索引了三個地址,所以IP加3

附加: 8086按下電源鍵的一刻,CS被設置爲FFFFH,IP被設置爲0000H。所以,FFFF0H單元中的指令是8086開機後執行的第一條指令。

8. 修改CS/IP的指令

  • 在CPU中,程序員能夠用指令控制的只有寄存器,程序員可以通過改變寄存器內的內容實現對CPU的控制
  • CPU從何處執行指令是由CS、IP的值決定的,程序員可以通過改變CS、IP的值來控制CPU來執行目標指令
  • 那麼如何修改CS、IP的指令

  • mov指令可以改變8086中大部分寄存器的值,被稱爲傳送指令 。(但,它不能用來修改CS、IP的值。

轉移指令: 可以改變CS、IP的值
用法: jmp 段地址:偏移地址


只修改IP的值: jmp 某一合法寄存器

例子: jmp ax
相當於: mov IP, ax
功能: 把ax的值賦值給IP


對於8086來說,可以根據需要將一組內存定義爲一個段(地址連續、起始地址爲16的倍數、長度爲N(N <= 64k))。這段內存是用來存放代碼的,從而定義了一個代碼段。

注意: CPU並不認代碼段,只認CS、IP。所以要讓CS:IP指向代碼段第一行的首地址。
任意時刻,CPU將CS、IP指向的內存地址當做指令執行

檢測點 2.3

補充: sub ax, bx
含義: ax -= bx

四次:

  1. 第一個語句
  2. 第二個語句
  3. 第三個語句
  4. 跳轉到ax

最後IP = ax = 0000H

9. 實驗

9.1 實驗1

下個DOSBox,設定一個目錄,把這三個文件扔進去在這裏插入圖片描述

  • 命令:發送

- r 查看/修改 寄存器

在這裏插入圖片描述

- a 以彙編格式輸入命
- d 查看內存中的內容
- e 改寫內存中的內容
- u 將機器指令轉變爲彙編指令
- d 段地址:偏移地址 查看從物理地址開始的內存
- u 查看彙編代碼
- t 執行命令CS/IP


在這裏插入圖片描述
在這裏插入圖片描述
把CS\IP改了,開始執行


按t一步步執行
在這裏插入圖片描述

9.2 實驗2

在這裏插入圖片描述

9.3 實驗3

注意: 內存區域的值是ASCII值

在這裏插入圖片描述

ASCII碼 16進制
48 0 30
50 2 32
47 / 2f

在這裏插入圖片描述
會發現!不讓改!

9.4 實驗4

在這裏插入圖片描述
這個是顯存的地址,所以顯示了圖像

三、寄存器(內存訪問):19/77

1. 內存中字的存儲

字單元: 任意兩個連續的內存單元可以被稱爲一個字單元
在這裏插入圖片描述

注意: 0號是低地址單元。兩個16進制數是一個字(2字節)

8086CPU中有一個DS寄存器,通常用來存放要訪問的數據的段地址。


mov可以將一個內存單元中的數據送入一個寄存器:

  • mov寄存器名, [偏移地址]
    mov ax, [0]
    我們執行指令時,8086CPU會自動取DS中數據爲內存單元的段地址

注意: 8086cpu有一個缺陷(硬件問題),不能直接把值賦給DS,需要靠一個通用寄存器轉手
mov ax, 1000H
mov DS, ax


mov 通用寄存器, 段寄存器是可行的
在這裏插入圖片描述


不能直接對段寄存器進行操作:在這裏插入圖片描述


CS指向的段就是代碼段,DS指向的段就是數據段

注意: mov ax, 1000[0]不會報錯,但不會進行賦值。這個可能和機器型號有關,我用的是DOSBox。

檢測點 3.1

  1. 2662H
    E626H
    E626H
    2662H
    D6E6H
    FD48H
    2C14H
    0000H
    00B6H
    0000H
    0026H。記錄: 我之前一直以爲一行是10個內存單元,看到這題,突然想起偏移地址是16進制,一行應該是16個內存單元。
    000CH
指令 ax bx CS IP DS
初始 0 0 2000H 0 1000H
mov ax, 6622H 6622H 3
jmp 0ff0:0100 0ff0 0100
變形 1000 0
mov ax, 2000H 2000H 3
mov ds, ax 2000H 5 2000H
mov ax, [0008] C389H 8
mov ax, [0002] EA66H B

數據和程序沒區別,都是一堆二進制數。在CS段中就是指令,在DS段中就是數據。

2. 棧 【重點】

2.1 概念&用法

棧: stack,一種具有特殊的訪問方式的存儲空間,LIFO先進後出。

用處: 用處非常廣泛,介紹一種最開始發明棧的原因。一個程序,如果只能從上至下進行,那麼功能就太差了。爲了複用性,需要函數 ,有一個問題就是,怎麼回去?怎麼回到調用處?於是發明了棧,把調用處的地址入棧,執行完函數後,再調用return(彙編語言指令都是三個字的,不叫這名,這是《程序是怎樣跑起來的》上面的說法),把棧中的地址彈給CS/IP。

注意: 把一段內存,當做什麼來使用,是程序員的看法。CPU只認數據。


push axax中的數據入棧
pop ax把棧中的數據彈入ax

注意: 8086CPU入棧/出棧是以字爲單位的(ax是16進制,當然是字,而不是字節)
補充: push CS/pop CS,push和pop可以對段寄存器 進行直接操作
補充: push [1]/pop [2],push和pop可以對內存進行直接操作


SS: 存放棧頂的棧地址
SP: 存放棧頂的偏移地址
SS:SP: 指向棧頂的元素


內存空間上面是低地址,下面是高地址。數據一個個存放進去,棧頂就向上移動,所以入棧又稱爲壓棧 。那麼棧頂向下移動,SS:SP又時刻指向棧頂,是怎麼做到的呢?靠得是SP進行變化。
push ax: SP -= 2。減號表示向上移動;pop指令正好相反。

補充: 當棧是空的時候,不存在棧頂。SP指向SS:0的下面一個內存單元(更大的內存單元)
注意: pop後,原指向的內存中的數據不會消失,只是SP向下移動了。當再次push時,會對原存在的數據進行覆蓋。(格式化不是真正的刪除,只有覆蓋重寫纔是真的刪除,這就是數據恢復的原理)

2.2 棧頂越界

棧頂越界: 當棧滿時仍push,當棧空時仍pop,那麼操作就會脫離我們規劃的棧空間。這就叫棧頂越界 (危險)

注意: java等語言有溢出檢測,而像C/C++之類的就沒有

解決方法: 弄兩個寄存器,一個記錄棧下限,一個記錄棧上限。然而,這種CPU目前並不存在。我們只能自己小心不越界,合理預估所需棧的大小(在C中,程序員可以操作Heap堆,這是由一堆零散空間通過邏輯連接的。要注意使用後,及時釋放內存)

2.3 一些問題的解答

問題3.7: 注意,sp是可以直接賦值的,初始賦值爲下一個內存單元,即000fH的下一個0010H
問題3.8: 注意,清零的步驟,我使用的是mov ax, 0這樣的機器碼是3個字節。書上的sub ax, ax的機器碼是2個字節(現在資源沒有那麼匱乏,不需要糾結這麼一小點空間)。系統很喜歡用xor ax, ax異或,如果ax等於ax就將ax清零。
問題3.10: 重要的事情說N遍,CPU只認二進制數據!


補充: mov指令涉及一個操作。push/pop涉及兩個操作,push先改變SP的值,再放入值;pop先取出值,再改變SP
注意: 因爲入棧是改變SP,所以棧的最大變化範圍爲0~ffff。當棧滿,會由於SP溢出,從而又回到棧底部開始循環。


問題3.11: SP = 0000。當有程序入棧時,SP -= 2,會得到fffe。也就是利用了數據溢出 的原理


編譯: 高級語言 -> 機器語言
反編譯: 機器語言 -> 高級語言
彙編: 彙編語言 -> 機器語言
反彙編: 機器語言 -> 彙編語言

檢測點 3.2

  1. mov ax, 2000H
    mov SS, ax
    mov SP, 0010H
  2. mov ax, 1000H
    mov SS, ax
    mov SP, 0000H

3. 實驗2

- d 段地址:偏移地址可以查看內存單元的數據。
那麼必須有一個寄存器來記錄這個段地址,這個寄存器是DS(DS的值在操作前後,不會改變)。
D更多用法:

  • - d DS:0000H 查看當前數據段內存
  • - d CS:0000H 查看當前代碼段內存
  • - d SS:0000H 查看當前棧段內存

  • 用e命令,批量修改內存:
    在這裏插入圖片描述
  • 用u命令,查看彙編形式的代碼段:在這裏插入圖片描述
  • 用a命令,以彙編的形式寫入棧: 在這裏插入圖片描述
  • Debug程序在執行修改寄存器SS時,下一條指令也會緊跟着執行 (一般情況下,t只能執行一條命令,這是個特例,涉及中斷機制 ,以後再學)

實驗任務1: 在這裏插入圖片描述
實驗任務2:: SP = 10H,-2就變成e,再-2就變成c。(王爽老師說理解這裏是很有悟性,我倒覺得沒有理解就該回去複習了,顯而易見到我不覺得這三個問題了)

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