彙編 ---- 第三章 控制顯卡

第一節 顯卡和顯存

  • 通過彙編代碼在屏幕上顯示文字,需要用到兩個硬件:顯卡和顯示器
    • 顯卡:爲顯示器提供內容
    • 顯示器:將顯卡所提供的內容呈現在屏幕上
    • 集成顯卡:集成在主板上,和主板是一體的
    • 獨立顯卡:獨立生產與銷售的一個獨立的部件,但仍需插到主板上使用
    • 每個顯卡都有自己的存儲器,稱之爲顯示存儲器,簡稱“顯存”
  • 顯示器可以顯示的最小單位是像素
    • 顯示器顯示黑白色圖像是最簡單的,只需要控制每個像素是亮或是不亮即可,如何控制像素?
      • 利用顯存,把亮抽象爲1,把不亮抽象爲0,將顯存中的每個比特和顯示器中的每個像素一一對應起來
      • 顯卡會週期性的從顯存中提取比特,在顯示器上顯示
        • 週期性:顯卡不斷的讀取顯存中的內容,每讀取一次,屏幕便會刷新一次,讀取的速度越快,屏幕刷新的頻率也就越快
  • 1、圖形模式:黑白模式/真彩色模式
    - [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-kKJwl1uH-1577965421758)(E:\workspace\TyporaProjects\C筆記\彙編\images\第三章 控制顯卡\圖形模式.png)]
    • 如上圖所示,顯存中第一個字節的內容爲11110000,第二個字節中的內容爲11111111,其餘字節內容爲00000000,在顯示器上就會先顯示4個亮點,然後顯示4個黑點,再顯示8個亮點,最後全部顯示黑點,由於像素是僅僅連在一起的,故先看到一條白色的短線,間距4個像素的距離,再顯示一條黑色的長線。
    • 顯示器的分辨率越高,需要的顯存就越大
    • 黑白模式的圖像只要一個比特就可以表示一個像素
    • 真彩色模式的圖像使用24個比特(3個字節)來表示顏色,可以表示16777216種顏色
      • 若使用真彩色模式,顯存中的3個字節對應屏幕中的一個像素
  • 2、文本模式:BIOS會執行一個硬件的初始化,它會將顯存的顯示模式初始化爲80×25的文本模式
    • 80×25:在屏幕上顯示25行,每一行80個字符,若滿屏狀態下,共計2000個字符
      - [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NH4BSmwr-1577965421758)(E:\workspace\TyporaProjects\C筆記\彙編\images\第三章 控制顯卡\文本模式.png)]
    • 一個字符由大量像素組合而成,不同字符的像素分佈同樣不同,故顯示字符較麻煩
    • 爲解決字符顯示麻煩的問題,字符發生器由此誕生
      • 在顯示器中的哪個位置顯示哪個字符,只需要在顯存的對應位置存入字符的ASCII碼,然後顯卡會先將顯存中的內容讀取到字符發生器中,由字符發生器決定顯示器中顯示的字符
    • 在文本模式下,顯存中的內容和顯示器中的像素之間沒有直接關係
      • 顯存中存的是要顯示字符的ASCII碼
      • 字符發生器對ASCII碼進行解析之後交由顯示器顯示
  • 3、顯存的2個字節對應顯示器上的一個字符
    • 前一個字節用於存儲字符的ASCII碼,後一個字節用於存儲字符的屬性
    • 字符屬性這一字節的前四位決定了字符的背景色,後四位決定了字符的前景色
      - [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-zJLOKxLH-1577965421759)(E:\workspace\TyporaProjects\C筆記\彙編\images\第三章 控制顯卡\字符對應關係.png)]
      - [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-L0P98Sts-1577965421759)(E:\workspace\TyporaProjects\C筆記\彙編\images\第三章 控制顯卡\字符屬性取值表.png)]
      • 背景色:
        • K = 0 時不閃爍,K = 1 時閃爍
      • 前景色:
        • 前景色中的I值和RGB組合,會產生不同的顏色
    • 顯存的位置及地址
      • 顯存至於外圍板卡中,顯存的地址爲B8000-BFFFF
        - [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-jlXDx10s-1577965421759)(E:\workspace\TyporaProjects\C筆記\彙編\images\第三章 控制顯卡\顯存地址 .png)]

第二節 爲訪問顯存做準備

  • mov ax, 0xb800
    ;將b800傳送到累加寄存器寄存器ax中
    mov es, ax
    ;將累加寄存器ax中的值(b800)傳送到附加段寄存器es中
    
  • 上述第二行代碼爲什麼不直接寫成mov es, 0xb800

    • 因爲intel處理器不允許直接將一個數傳送到段寄存器中
    • 只允許:move 段寄存器, 通用寄存器/內存單元
    • 所以上述代碼需要用通用寄存器(累加寄存器ax)來做中轉,再將值傳送到附加段寄存器ES中
  • b800傳送到附加段寄存器ES中的目的是什麼?

    • 附加段寄存器ES用途:存儲數據段的段地址,顯存就相當於一個數據段
    • 顯存的段地址爲b800,顯示文本需要訪問顯存
    • 上述兩行代碼執行完以後,顯存數據段的段地址(b800)存儲到附加段寄存器es中,以便後續用於訪問顯存
  • 第二行代碼處也可以用數據段寄存器DS,但數據段寄存器DS後續有其他用途,故此處用附加段寄存器ES

  • 上述兩行代碼的目的:爲後面訪問顯存做準備

第三節 向顯存寫入數據

  • mov byte [es:0x00], 'L'(對應顯存的第一個存儲單元,存儲字符LASCII碼)
    • 可以使用字符L的字面值,但是需要用''引起來,編譯器在編譯時會自動將''內的字符轉換爲ASCII碼
    • 編譯器會將字符自動編譯爲二進制形式進行存儲
    • 代碼作用:將"字符L的ASCII碼"存儲到顯存的第一個存儲單元中
  • 地址 [es:0x00]
    • 地址必須使用方括號[]括起來
    • 若地址中沒有聲明附加段寄存器ES,處理器會默認段地址在數據段寄存器DS中
    • 地址中第一個值爲附加段寄存器ES,附加段寄存器ES中存儲的是顯存的段地址(上一節中的b800)
    • 地址中第二個值爲偏移地址00
    • 段地址b800 + 偏移地址00指向顯存的第一個存儲單元
  • byte/word
    • 8086處理器爲16位操作系統,因此進行單次數據操作的寬度可以是8位,也可以是16位
    • 在編譯器無法進行判斷是8位指令還是16位指令時,需用byte/word進行修飾
    • 明確的告訴編譯器這條指令的數據寬度是1個字節/字
      • 補充:
        • 1字節(byte) = 8比特(bit)
        • 字(word)的大小取決於具體系統的總線寬度,如果是32位的系統,則一個字是4個字節,如果是64位的系統,則一個字是8個字節
    • 編譯器會根據byte/word進行相應的編譯
  • 不需要byte/word修飾的例子:
    • mov [00], AL
      • 源操作數是8位通用寄存器AL,AL寄存器的大小爲1個字節(1byte = 8bit),故按字節操作
    • mov AX, [00]
      • 目的操作數爲16位通用寄存器AX,AX寄存器的大小爲1個字(1word = 2 byte = 16bit),故按字操作
  • mov byte [es:0x01], 0x07(對應顯存的第二個存儲單元,存儲字符L屬性)
    • 將"字符L的屬性"存儲到顯存的第二個存儲單元中
    • 屬性值07:黑底白字、無閃爍、無加亮
mov byte [es:0x00], 'L'
;將"字符L的ASCII碼"存儲到顯存的第一個存儲單元中
;move byte [es:0x01], 0100 1100b / 76 /0x4C  
;(L ASCII碼的二進制形式/十進制形式/十六進制形式)
;如果地址指令前沒有byte,那地址指令[es:0x00]`未指明是多少位,因此編譯器無法判斷第一條指令是8位還是16位
;不需要修飾:mov [00], AL / mov AX, [00]
mov byte [es:0x01], 0x07
;將"字符L的屬性"存儲到顯存的第二個存儲單元中
;屬性值07:黑底白字、無閃爍、無加亮

mov byte [es:0x02], 'a'
mov byte [es:0x03], 0x07

mov byte [es:0x04], 'b'
mov byte [es:0x05], 0x07

mov byte [es:0x06], 'e'
mov byte [es:0x07], 0x07

mov byte [es:0x08], 'l'
mov byte [es:0x09], 0x07

mov byte [es:0x0a], ' '
mov byte [es:0x0b], 0x07

mov byte [es:0x0c], "o"
mov byte [es:0x0d], 0x07

mov byte [es:0x0e], 'f'
mov byte [es:0x0f], 0x07

mov byte [es:0x10], 'f'
mov byte [es:0x11], 0x07

mov byte [es:0x12], 's'
mov byte [es:0x13], 0x07

mov byte [es:0x14], 'e'
mov byte [es:0x15], 0x07

mov byte [es:0x16], 't'
mov byte [es:0x17], 0x07

mov byte [es:0x18], ':'
mov byte [es:0x19], 0x07

第四節 MOV指令

  • 功能:用於數據的傳送
  • 格式:Mov 目的操作數, 源操作數
    • Mov AX, 0x22
  • 注意:
    • 1、"目的操作數"必須是一個容器 ----> 寄存器或者內存單元,而源操作數則沒有限制,可以是寄存器、內存單元或者數字
    • 2、“傳送"的本質是"複製”,只改變目的操作數中的值,並不會影響源操作數中的值
    • 3、目的操作數和源操作數的數據寬度必須是一致的
    • 4、目的操作數和源操作數不能同時爲內存單元
      • 原因:內存內部構造導致(RAM存儲器的存儲單元都是互相獨立的,不能將一個內存單元中的內容直接傳送到另一個存儲單元中)
    • 5、intel處理器不允許數字直接傳送到段寄存器中,需要經過寄存器或內存單元進行中轉
      • Mov 段寄存器, 通用寄存器/內存單元
  • 目的操作數爲寄存器的情況
    • Mov AX, BX
      • 源操作數爲寄存器
    • Mov AX, [0x33]
      • 源操作數爲內存單元
    • Mov AX, 0x66
      • 源操作數爲數字/立即數
        • 稱作立即數的原因:Mov AX, [0x33]指令中,源操作數爲內存單元,需要先到內存地址爲0x33的位置處取出數據,然後將數據傳送到通用寄存器AX中;而Mov AX, 0x66指令中,源操作數爲數字,可以直接將數字傳送到通用寄存器AX中
  • 目的操作數爲內存單元的情況
    • Mov [0x04], BX
      • 源操作數爲寄存器
    • Mov [0x04], [0x33]
      • 錯誤!!,源操作數和目的操作數同爲內存單元
    • Mov [0x04], 0x66
      • 源操作數爲數字/立即數
  • 其他情況:
    • Mov AX, BL
      • 錯誤!!,通用寄存器AX爲16位,而BL爲8位,造成源操作數和目的操作數的數據寬度不一致
    • Mov CS, 0x22
      • 錯誤!!,目的操作數代碼段寄存器CS爲段寄存器,而源操作數爲數字,intel處理器不允許數字直接傳送到段寄存器中

第五節 標號和彙編地址

  • 1、彙編地址

    • 將彙編源程序進行編譯後會生成兩個文件,bin文件(.bin)和列表文件(.lst)

      • bin文件:機器指令,提交給處理器執行的文件

      • 列表文件:存儲關於源程序的詳細信息

        • 編譯器會將源程序中的每一條指令進行編號
        • 編號從00000000開始,第一條指令長度爲3個字節,故第二條指令編號爲00000003,第二條指令長度爲2個字節,故第三條指令編號爲00000005,第三條指令長度爲6個字節,故第四條指令編號爲0000000B,第四條指令長度爲6個字節,故第五條指令編號爲00000011,第五條指令長度爲6個字節,故第六條指令編號爲00000017,······· ,以此類推。
        行號 編號(彙編地址) 機器指令 彙編代碼
        1 00000000 B800B8 mov ax, 0xb800
        2 00000003 8EC0 mov es, ax
        3 00000005 26C60600004C mov byte [es:0x00], ‘L’
        4 0000000B 26C606010007 move byte [es:0x01], 0x07
        5 00000011 26C606020061 mov byte [es:0x02], ‘a’
        6 00000017 26C606030007 mov byte [es:0x03], 0x07
        ··· ··· ··· ··· ··· ··· ··· ···
        131 00000100 0000000000 number db 0, 0, 0, 0, 0
    • 將整個程序當作一個段,讀取到內存中,指令的編號等於在段內的偏移地址

    • 將前三行指令加載到內存中,如下圖所示:

    - [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OUrnLNcr-1577965421759)(E:\workspace\TyporaProjects\C筆記\彙編\images\第三章 控制顯卡\編號和彙編地址.png)]

    • 第一條指令中B8爲段地址開始的地址,在段內對應的偏移地址爲0
      • 第一條指令的編號爲0,偏移地址爲0
    • 第二條指令中8E在段內的偏移地址對應爲3
      • 第二條指令的編號爲3,偏移地址爲3
    • 第三條指令中26在段內偏移地址對應爲5
      • 第三條指令的編號爲5,偏移地址爲5
    • 編號是由編譯器通過編譯得到,編號的值等於指令在段內的偏移地址,所以也稱編號爲彙編地址
  • 2、標號

    • 由編譯器自動生成
    • 關係:標號 = 彙編地址
    • 第131行彙編代碼,程序的開頭爲number,number即爲標號
      • 作用:相當於指令彙編地址的符號表示,number代表0100
      • 若在程序的其他位置使用了number,編譯器在編譯過程中會自動將number替換爲0100參與運算
    • 在程序中需要使用某一條指令的彙編地址的時候,在該指令前添加標號,在需要使用匯編地址的地方使用標號代替具體的彙編地址
    • 編譯器允許在每一條指令前添加標號,標號的書寫規範較爲寬鬆,一般以字母開頭就不會出現問題。

第六節 需求分析

  • 將第131行代碼的彙編地址的十進制顯示在屏幕上
    • 第一步:通過標號:number,拿到彙編地址(二進制)
    • 第二步:分解各個數位
      • number = 0x0100 = 1 0000 0000B
      • 1、 1 0000 0000 除以 1010 商 = 1 1001 餘數 = 110 (6:個位)
      • 2、 0001 1001 除以 1010 商 = 10 餘數 = 101 (5:十位)
      • 3、 10 除以 1010 商 = 0 餘數 = 10 (2:百位)
      • 4、因爲商(被除數) = 0,所以運算到此結束
    • 第三步:推算出標號十進制形式的每個數值的ASCII碼
      • 將第二步得到的每個數位的二進制的值加 11 0000得到十進制數所對應的ASCII碼,將得到的ASCII碼寫入顯存
        • 數字0的ASCII碼是 11 0000 = 0 + 11 0000
        • 數字1的ASCII碼是 11 0001 = 1 + 11 0000
        • 數字2的ASCII碼是 11 0010 = 10 + 11 0000
        • 數字3的ASCII碼是 11 0011 = 11 + 11 0000
        • 數字4的ASCII碼是 11 0100 = 100 + 11 0000
        • ··· ··· ···

第七節 DB指令和僞指令

  • 跳轉指令

    • infi jmp near infi
      ;無限循環
      
  • db指令(僞指令)

    • db: declare byte 聲明並初始化數據

    • 僞指令:不能調動處理器執行指令,只能調動編譯器進行數據的聲明和初始化

      • 作用:編譯器在規定的位置(0100)預留出5個字節的空間並按照指令要求進行賦值(佔位)
    • number db 0, 0, 0, 0, 0
      ;標號:number = 彙編地址 = 0x0100
      ;書寫規範:以字母開頭
      ;db:聲明並初始化數據
      ;聲明數據的本質就是在內存中佔用一塊空間
      ;初始化數據的本質就是給這個空間賦予一個值
      ;在內存中佔用了5個字節的空間,這5個字節的值都是0
      
  • Mov指令:會被編譯器編譯爲對應的機器指令

    • 作用:控制處理器,將數據傳送到指定位置
  • DB指令:會被編譯器編譯爲對應的數據

    • Bbyte的意思,表示聲明的每個數值佔用1個字節的寬度
    • 作用:控制編譯器,聲明並初始化數據
  • DW指令、DD指令、DQ指令

    • Wword的意思,表示聲明的每個數值佔用2個字節的寬度
    • Ddouble word的意思,表示聲明的每個數值佔用4個字節的寬度
    • Qquad word的意思,表示聲明的每個數值佔用8個字節的寬度

第八節 DIV指令

  • DIV指令:除法
  • div: division
    • 16位數 ÷ 8位數
      • 除數:由8位通用寄存器或者內存單元來提供
      • intel處理器規定,被除數必須存儲在16位寄存器AX中:
        • 不能用內存單元(立即數)直接參與運算,必須進行轉化,存儲到AX寄存器中再進行運算
      • 16位通用寄存器AX:被除數
        - [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-L5sp2B5B-1577965421761)(E:\workspace\TyporaProjects\C筆記\彙編\images\第三章 控制顯卡\AX.png)]
      • 商被存儲到寄存器AL(AX寄存器中的低八位)中,餘數被存儲到寄存器AH(AX寄存器中的高八位)中
        • 計算完成後,商和餘數會覆蓋掉被除數
      • 結構:div 除數
        • div 後只跟除數的原因:被除數必須存儲在AX寄存器中
        • div CL(CX寄存器中的低八位)
    • 32位數 ÷ 16位數
      • 被除數爲32位數,處理器和寄存器都是16位數,故32位數需要用到兩個16位寄存器來存儲
      • 除數:由16位通用寄存器或者內存單元來提供
      • 被除數的高16位被存儲在DX寄存器中,低16位被存儲在AX寄存器中(intel處理器的規定)
      • DX:被除數的高16位 AX:被除數的低16位
        - [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-sj1686Np-1577965421761)(E:\workspace\TyporaProjects\C筆記\彙編\images\第三章 控制顯卡\DX.png)]
        • 商被存儲到寄存器AX(低十六位)中,餘數被存儲到寄存器DX(高十六位)中
      • 結構:div 除數
        • div CX (CX爲十六位通用寄存器)

第九節 運算前的準備

  • mov ax, number
    ;將“標號”number存儲到AX寄存器中,作爲下面除法運算的被除數的低16位
    ;編譯器在編譯的時候會將number替換爲其所對應的彙編地址
    mov dx, 0
    ;將0存儲到DX寄存器中,作爲下面除法運算的被除數的高16位
    ;由於低十六位number的值只有9位,故用不到被除數的高十六位,所以高十六位設置爲0
    mov bx, 10
    ;將10,也就是二進制的1010存儲到BX寄存器中,作爲下面除法運算的除數
    ;以上三條指令爲第2個步驟的除法運算做好了準備工作
    

第十節 定位內存地址

  • mov cx, cs
    ;將代碼段寄存器CS中的值,也就是0x000,傳送到CX寄存器中
    mov ds, cx
    ;將CX寄存器中的值,也就是0x000傳送到數據段寄存器DS中
    ;DS寄存器中存儲的是本程序在內存中的段地址
    
  • 寫好彙編程序

  • 對其進行編譯,拿到機器指令

  • 將機器指令,也就是bin文件寫入硬盤的主引導扇區

  • 啓動計算機

  • BIOS將主引導扇區(程序)加載到內存地址爲7C00的位置處

  • BIOS通過一條跳轉指令jump 0x0000, 0x7C00

    • 將代碼段寄存器CS的值設置爲0x0000, 指令指針寄存器IP的值設置爲0x7C00
      • 段的起始位置(代碼段的段地址):代碼段寄存器CS左移四位 --> 00000
      • 指令指針寄存器IP的值爲7C00程序從段內偏移地址爲7C00的位置處開始加載
    • 使處理器跳轉到7C00的位置處執行程序
    • 使用DB指令所預留的內存空間的段地址爲00000,第一個字節的偏移地址爲7C00 + 0100,第二個字節的偏移地址爲07C00 + 0100 + 1,第三個字節的偏移地址爲07C00 + 0100 + 2, ··· ···
  • 注意:

    • 下圖的程序不是從段開始的位置00000處加載的,而是從段內偏移地址爲7C00的位置處開始加載的
    • 程序是作爲一個段被加載到內存中的,並沒有對程序進行分段,故程序的數據段和代碼段的地址是相同的
      - [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QSEScrnz-1577965421761)(E:\workspace\TyporaProjects\C筆記\彙編\images\第三章 控制顯卡\運算前的準備.png)]

第十一節 分解各個數位

  • div bx
    ;對應第一次除法運算
    ;商存儲在AX寄存器(低十六位)中,作爲下次運算的被除數
    ;餘數,也就是標號的個位上的值,存儲在DX寄存器(高十六位)中
    mov [0x7c00 + number + 0x00], dl
    ;將第一次除法運算所得到的標號的個位上的值
    ;存入了使用db聲明的第一個存儲單元中(標號個位上的值對應第一個存儲單元)
    ;十六位的DX寄存器被分爲兩個八位的通用寄存器DH和DL,而此時的餘數只有三位,只佔用了DL寄存器中的低三位,故此處使用dl寄存器存儲餘數即可
    ;mov操作數數據寬度要一致,db所聲明的第一個存儲單元的大小爲8位,故用8位dl指令,若用dx則會將操作數傳入第一個和第二個存儲單元中,會佔據兩個存儲單元的容量
    xor dx, dx
    ;異或指令:對兩個數進行異或運算,並將結果存儲到目的操作數中
    ;異或運算的特點:相同爲0,不同爲1
    ;這條指令的作用:將DX寄存器中的值清零
    ;使用指令操作的原因:AX爲低十六位寄存器,DX爲高十六位寄存器,在本節運行第一次除法運算的時候,餘數被存儲到DX寄存器中,故DX寄存器中含值,在進行第二次除法運算之前需對DX寄存器中的值清零,爲第二次除法運算做準備
    div bx
    ;對應第二次除法運算
    mov [0x7c00 + number + 0x01], dl
    ;將第二次除法運算所得到的標號的十位上的值
    ;存入了使用db所聲明的第二個存儲單元中
    xor dx, dx
    ;爲第三次除法運算做準備
    div bx
    ;對應第三次除法運算
    mov [0x7c00 + number + 0x02], dl
    ;將第三次除法運算所得到的標號的百位上的值
    ;存入了使用db所聲明的第三個存儲單元中
    

第十二節 顯示各個數位

mov al, [0x7c00 + number + 0x02]
;將標號百位上的數值傳送給AL寄存器
add al, 0x30
;獲得標號百位上數值的ASCII碼
;0x30的二進制爲110000
mov [es: 0x1a], al
;將標號百位上數值的ASCII碼存入顯存
;前一個字節:存儲字符的ASCII碼
mov byte [es: 0x1b], 0x04
;將0x04存入顯存中偏移地址爲0x1b的存儲單元
;顯存中的兩個字節對應屏幕上的一個字符
;後一個字節,存儲字符屬性04:黑底、紅字、無閃爍、無加亮

mov al, [0x7c00 + number + 0x01]
;將標號十位上的數值傳送給al寄存器
add al, 0x30
;獲得標號十位上數值的ASCII碼
mov [es: 0x1c], al
;將標號十位上數值的ASCII碼存入顯存
mov byte [es: 0x1d], 0x04

mov al, [0x7c00 + number + 0x00]
;將標號個位上的數值傳送給AL寄存器
add al, 0x30
;獲得標號個位上數值的ASCII碼
mov [es: 0x1e], al
;將標號個位上數值的ASCII碼存入顯存
mov byte [es: 0x1f], 0x04

mov byte [es: 0x20], 'D'
;前一個字節:將字符D的ASCII碼存入顯存
mov byte [es: 0x21], 0x07
;後一個字節:存儲字符屬性值07:黑底白字、無閃爍、無加亮

第十三節 無限循環

infi jmp near infi
;infi:標號,等於該條指令的彙編地址00FD
;無限循環
;對應的機器指令爲E9FDFF,其中E9爲操作碼,FDFF爲操作數
  • jmp 0x005c, 0x003d

    • 直接給出段地址0x005c和偏移地址0x003d
    • 指令執行完畢,CS寄存器的值變爲0x005cIP寄存器的值變爲0x003d
    • 執行到下一個週期,處理器會將CS寄存器中的值左移四位,形成一個段地址,加上IP寄存器中的偏移地址,形成邏輯地址
    • 處理器到邏輯地址中取指令並加以執行
  • jmp near infi

    • jmp:跳轉指令,用於改變CS寄存器IP寄存器中的值

      • CS寄存器IP寄存器決定了處理器將要執行哪條任務
    • jmp作用:使處理器轉移到指定位置執行

    • near:修飾符,表示指令的操作數是16位的

    • 000000FD E9FDFF
      
    • 操作數 = 標號的彙編地址 - 當前指令的彙編地址 - 當前指令的長度

      • 標號的彙編地址 = 當前指令的彙編地址
      • 當前指令E9FDFF的長度爲3
      • jmp near infi的操作數爲-3
        • -3在計算機中如何存儲?
          • 整數在計算機中的存儲規則:無符號表示法、符號加絕對值表示法、補碼錶示法
          • 1.將3轉化爲二進制11
          • 2.near操作數是16位,故需在前補0,0000000000000011
          • 3.判斷正負,-3爲負數,故取其補碼1111111111111101,轉換爲16進制數爲FFFD,操作數爲0xFFFD,由於intel處理器爲小端存儲,故會將操作數進行一個反轉,操作數最終結果爲FDFF
    • 這條指令不會改變代碼段寄存器CS的值,只改變指令指針寄存器IP的值

    • 處理器在執行指令時的做法:

      • 指令指針寄存器IP + 該指令的操作數 + 該指令的長度 = xxx ,再將xxx存儲到IP寄存器中
    • 該指令執行完畢,CS寄存器和IP寄存器的值都不會改變,故處理器會再次跳轉到該指令繼續執行該指令,由此使處理器陷入無限循環

    • 該結構jmp指令的運行效果:讓處理器跳轉到指定的標號位置處執行

第十四節 主引導扇區的有效標誌

times 249 db 0
;僞指令,讓編譯器重複生成db 0  ----  249次
db 0x55, 0xaa
;硬盤主引導扇區的有效標誌
  • 添加最後兩行代碼的意義:
    • 硬盤要求主引導扇區最後兩個字節的數據必須是0x550xaa
    • 一個扇區有512個字節,必須保證0x550xaa這兩個字節在第511和第512個字節處,若沒有times 249 db 0指令,則位置不符合扇區要求

第十五節 編譯並運行程序

  • 編寫完成源程序demo.asm
  • 打開NASM-IDE軟件 --> 文件 --> 打開源程序 --> 選擇demo.asm打開 --> 文件 --> 編譯本文檔(編譯後會得到兩個文件:bin文件<存儲機器指令>和lst文件<存儲程序詳細信息>) --> 將bin文件寫入硬盤主引導扇區 --> 啓動虛擬機中創建的虛擬硬盤
    • bin文件寫入硬盤主引導扇區:
      • 打開Vhd Writer軟件 --> 選擇虛擬硬盤文件 --> 選擇虛擬硬盤.vhd 並雙擊 --> 下一步 --> 選擇數據文件(bin文件) --> 下一步 --> 寫入文件 --> 完成
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章