博客 / 詳情

返回

王爽《彙編語言(第4版)》讀書筆記(第9-10章)

寫這個系列文章的主要目的是記錄書中重要的知識點,並和大家分享一些個人理解與實踐。由於筆記中的知識點比較零散,而書中系統的介紹了一個 x86-16 處理器在實模式下的工作原理以及如何使用匯編語言與其進行“溝通”,所以推薦想要系統學習的朋友們去學習這本書。當我們掌握了實模式的工作原理之後,就可以進一步研究後來出現的其他運行模式(如保護模式)。除此之外,熟悉彙編語言有助於我們掌握上層語言(如 C)的執行原理,因為它們都要對彙編(機器碼)進行抽象,而彙編程序就是基於 CPU 的執行機理寫出來的。

第九章

轉移行為分類

  • 段內轉移(只修改 IP, 根據轉移的範圍不同又分為:短轉移近轉移
  • 段間轉移(同時修改 CSIP

jmp 指令的兩種實現差異

在一般的彙編指令中,立即數不論表示一個數據還是內存單元的偏移地址,都會在對應的機器指令中出現
  1. 基於轉移的距離——指令中不包含目的地址,而包含的是位移量(向前或向後移動的距離)

    • 優點,可浮動裝配,即位移量不變的情況下,這段程序可在任何地方執行
  2. 基於要轉移的目的地址

以上兩個維度的彙總

基於轉移的距離(間接轉移) 基於要轉移的目的地址(直接轉移)
段內轉移 jmp short Label
jmp Labeljmp near ptr Label
jmp 某一合法16位寄存器——將寄存器中的內容寫入 IP
jmp word ptr 地址——轉移到目的偏移地址
段間轉移 jmp far ptr Label(注:它也能夠實現段內轉移的效果)
jmp dword ptr 地址h_word=段地址,l_word=目的偏移地址)
  • jmp short Label: 翻譯成的機器碼為 EB??H , ?? 代表 8 位的位移量(這種方式是短轉移

    本質為:IP = IP + 8位的位移量 【重點(容易被忽略):CPU 在執行該指令時,是使用這個位移量來計算 IP 的】
    - short 指明此處的位移為 8 位位移,範圍是 -128~127, 用【補碼】表示
    - 8位的位移量 = 標號處的地址 - jmp指令後的第一個字節的地址,編譯器在【編譯時】算出
    • 為什麼是 jmp 指令後的第一個字節的地址aj?為什麼這麼設計呢?因為取址之後(執行之前)IP寄存器中的地址會被設置為該地址aj,這樣在執行轉移指令時,可以直接參與目的偏移地址的計算!

      • 編譯時:disp=as-aj, 執行指令時:IP(as) = IP(aj) + disp(位移量) 即直接完成該指令的功能
  • jmp near ptr Label近轉移,本質與短轉移相同,區別僅僅是它支持16位的位移量
  • jmp far ptr Label遠轉移

    CS 設置為 標號所在段的【段地址】
    IP 設置為 標號在該段中的【偏移地址】

masm 對 jmp 內部轉移的編譯實現

【條件轉移】和【循環指令】是對【短轉移】的功能擴展

  • jcxz 指令的偽代碼:

       ......
    
       if ((cx) == 0) {
         jmp short s
       }
    
    s: ......
  • loop 指令的偽代碼

    s: ......
    
       (cx)--;
       if ((cx) != 0) {
         jmp short s
       }
    
       ......

實驗八:分析一個奇怪的程序

注意:是將 jmp short s1 的機器碼 EBF6 拷貝至 076C:0008~076C:0009 兩個存儲單元中,F6-10 的補碼,表示位移量

其實補碼 F6 表示什麼有符號數,可以不用圖片中的方法計算,而使用以下方法計算更為簡單:

  • 因為補碼 1111 1111 表示十進制有符號數 -1, 這個 -1 的計算方式是:

    $$ -1 = -1*2^7 + 1*2^6 + 1*2^5 + 1*2^4 + 1*2^3 + 1*2^2 + 1*2^1 + 1*2^0 $$

    $$ = -128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 $$

  • 那麼補碼 1111 0110(0xF6) 就等於:

    $$ -1*2^7 + 1*2^6 + 1*2^5 + 1*2^4 + 0*2^3 + 1*2^2 + 1*2^1 + 0*2^0 $$

    $$ (-1*2^7+1*2^6+1*2^5+1*2^4+1*2^3+1*2^2+1*2^1+1*2^0) - 1*2^3 - 1*2^0 = -1 - 8 - 1 = -10 $$

實驗九:80x25 彩色字符模式緩衝區輸出程序

  • 顯示緩衝區:B8000H~BFFFFH(共32KB, 8頁,每頁4KB)
  • 80x25——每頁由 25 行組成,每行由 80 個字符(160byte)組成
  • 注:dosbox 中執行完可執行文件,輸出結果會被向上頂一行(彈出輸入提示符導致),所以以下程序的輸出就顯示在 11,12,13 行了
; 在屏幕中間分別顯示黑底綠色、綠底紅色、白底藍色的字符串'welcome to masm!'
; 題目要求屏幕中間顯示?那就 12,13,14 行顯示
assume cs:codesg, ds:datasg, ss:stacksg

datasg segment
    db 'welcome to masm!'
    db 02H, 24H, 71H ; 三種屬性:黑底綠色、綠底紅色、白底藍色
datasg ends

stacksg segment
    dd 0,0,0,0
stacksg ends

codesg segment
  start: mov ax, datasg
         mov ds, ax

         mov ax, stacksg
         mov ss, ax
         mov sp, 10H

         mov ax, 0B800H ; masm 彙編器要求立即數不能以字母開頭
         mov es, ax

         mov bx, 0
         mov bp, 16

         mov cx, 3       ; 共寫入3行
    s0:  mov si, 0
         mov di, 1824    ; di=1760+64, 1760是12行第一字節的offset, 每行中間位置的offset=64
         add di, bx      ; 計算每一行開始寫入的偏移地址
         mov ah, ds:[bp] ; 該行要顯示的屬性值

         push cx         ; 保存
         mov cx, 16
     s:  mov al, [si]
         mov es:[di], al ; 顯存區域:字符低字節表示 ASCII
         inc di
         mov es:[di], ah ; 字符高字節對應顏色屬性
         inc si
         inc di
         loop s

         add bx, 160 ; 0-160-320 (控制寫入下一行)
         inc bp      ; 16-17-18(控制獲取下一行的屬性值)
         pop cx      ; 恢復
         loop s0

         mov ax, 4c00H
         int 21H
codesg ends

end start

第十章

模塊化程序設計

  1. callret 指令配合使用(近轉移

    • call 等價於:push IP + jmp ...
    • ret等價於:pop IP
  2. call和retf指令配合使用(遠轉移

    • call等價於:push CS + push IP + jmp ...
    • retf等價於:pop IP + pop CS(注意這個順序)

call指令

相當於將call指令之後的那一個指令IPCS+IP壓棧之後,進行jmp轉移。
支持近轉移遠轉移,不支持短轉移

  • call Label

    • push IP
    • jmp near ptr Label
  • call far ptr Label

    • push CS
    • push IP
    • jmp far ptr Label
  • call 16位寄存器

    • push IP
    • jmp 16位寄存器
  • call word ptr 內存地址

    • push IP
    • jmp word ptr 內存地址
  • call dword ptr 內存地址

    • push CS
    • push IP
    • jmp dword ptr 內存地址

總結——【目前已經介紹的】轉移指令對應的機器碼

call 轉移指令 機器碼 jmp 轉移指令 機器碼
call L E8 ????(16位位移量) jmp near ptr L E9 ????
call far ptr L 9A ???? ????<br/>標號在段中的偏移地址 標號所在段地址 jmp far ptr L EA ???? ????
call ax、call bx FFD0、FFD3 jmp ax、jmp bx FFE0、FFE3
call word ptr addr FF160000 (todo) jmp word ptr addr FF260000 (todo)
call dword ptr addr FF1E0000 (todo) jmp dword ptr addr FF2E0000 (todo)

8086 中乘法的計算規則

彙編指令:mul 乘數(在內存或某個寄存器中),【被乘數和乘數】要麼都是 8 位,要麼都是 16 位

8位乘數 16位乘數
被乘數 8位——在 al 16位——在 ax
結果 ax dx 存高16位、ax 存低 16 位

實驗十:編寫子程序

(1)指定位置打印字符串

exp10_1.asm

(2)解決除法溢出的問題

exp10_2.asm

(3)二進制轉十進制,打印

exp10_3.asm

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.