彙編入門(長文多圖,流量慎入!!!)

8086彙編ios


本筆記是筆者觀看小甲魚老師(魚C論壇)《零基礎入門學習彙編語言》系列視頻的筆記,在此感謝他和像他同樣共享資源、幫助他人的筒子們==本文比較長,因爲筆者我的能力有限,錯漏在所不免,歡迎讀者們批評指正。程序員

圖文無關

本文首發於個人CSDN博客,同時也發佈於個人簡書算法

1、基礎知識

引言

  • 基本瞭解硬件系統的結構;
  • 利用硬件系統的編程結構和指令集,有效靈活地控制系統進行工做。

1.1 機器語言

  • 機器語言是機器指令的集合。電子計算機的機器指令是一系列二進制數字。計算機將之轉換爲一系列高低電平脈衝信號來驅動硬件工做的。

1.2 彙編語言的產生

  • 因爲機器語言指令都是由01組成,難以編寫,記憶和維護程序.因此彙編語言爲了解決這一問題產生。彙編語言的主體是彙編指令,彙編指令是機器指令的助記符。
  • 寄存器: CPU中存儲數據的器件,一個CPU中有多個寄存器。

1.3 彙編語言的組成

  • 一、彙編指令(機器碼的助記符,有對應的機器碼);
  • 二、僞指令(由編譯器執行)和其餘符號(由編譯器識別)。

1.4 存儲器

  • CPU工做須要指令和數據,指令和數據存儲在存儲器中。

1.5 指令和數據

  • 在內存或者磁盤中存儲的都是爲二進制信息,指令和數據由咱們設定(走的總線)

1.6 存儲單元

  • 存儲器被劃分爲若干個存儲單元,每一個存儲單元從0開始順序編號。
  • B、KB、MB、GB、TB等單位。

1.7 CPU對存儲器的讀寫

  • CPU要對數據進行讀寫,必須和外部器件進行如下三類信息的交互:
    • 一、存儲單元的地址(地址信息);
    • 二、器件的選擇、讀或寫命令(控制信息);
    • 三、讀或寫的數據(數據信息) 。
  • 總線是鏈接CPU和其餘芯片的導線,邏輯上分爲地址總線數據總線控制總線編程

    邏輯上總線的分類

  • CPU從內存單元中讀寫數據的過程:
    • 一、CPU經過地址線將地址信息發出;
    • 二、CPU經過控制線發出內存讀命令,選中存儲器芯片,並通知它將要從中讀或寫數據;
    • 三、存儲器將相應的地址單元中的數據經過數據線送入CPU或CPU經過數據線將數據送入相應的內存單元。

1.8 地址總線

  • CPU是經過地址總線指定存儲單元,地址總線傳送的能力決定了CPU對存儲單元的尋址能力。(通常32位CPU,尋址能力爲2^32=4G)

1.9 數據總線

  • CPU經過數據總線來與內存等器件進行數據傳送,數據總線的寬度決定了CPU和外界的數據傳送速度。

1.10 控制總線

  • 控制總線是一些不一樣控制的集合,CPU經過控制總線對外部器件的控制。控制總線的寬度決定了CPU對外部器件的控制能力。

小結

  • 一、彙編指令時機器指令的助記符,與機器指令一一對應。
  • 二、每一種CPU都有本身的彙編指令集。
  • 三、CPU能夠直接使用的信息在存儲器中存放。
  • 四、在存儲器中指令和數據都是二進制信息。
  • 五、存儲單元從0開始順序編號。
  • 六、一個存儲單元能夠存儲8個bit。
  • 七、B、KB、MB、GB等單位之間的轉換。
  • 八、CPU管腳和總線相連。總線的寬度表示CPU不一樣方面的性能:
    • 地址總線的寬度決定了CPU的尋址能力;
    • 數據總線的寬度決定了CPU與其餘器件進行一次數據傳送的量;
    • 控制總線寬度決定了CPU對系統中其餘器件的控制。

檢測點 1.1

檢測點1.1

1.11 內存地址空間(概述)

  • CPU可尋的內存單元構成這個CPU的內存地址空間。例如一個CPU的地址總線寬度爲10,那麼能夠尋址的1024個內存單元構成了這個CPU的內存空間。

1.12 主板

  • 主板主板,主要的電路板 😆

1.13 接口卡

  • CPU經過接口卡間接控制外部設備。

1.14 各種存儲器

  • 隨機存儲器RAM(主板上的RAM、拓展插槽上的RAM和接口卡上的RAM)和只讀存儲器器ROM(裝有BIOS的ROM)。
    PC集中各種存儲器的邏輯鏈接

1.15 內存地址空間

  • 各種存儲器在物理上是獨立的,可是:
    • 一、都和CPU的總線相連;
    • 二、 CPU對他們進行讀或寫的時候都經過控制線發出的內存讀寫命令。
    將各種存儲器看做一個邏輯存儲器
  • 不一樣的計算機系統的內存地址空間分配狀況是不一樣的。

2、寄存器(CPU的工做原理)

引言

  • CPU由運算器、控制器、寄存器 等器件組成,靠內部總線相連。
  • 內部總線實現CPU內部各器件之間的聯繫;外部總線實現CPU和主板上其餘器件的聯繫。
  • 在CPU中:
    • 運算器進行信息處理;
    • 寄存器進行信息存儲;
    • 控制器控制各類器件進行工做;
    • 內部總線鏈接各類器件在它們之間進行數據的傳送。

2.1 通用寄存器

  • 8086有14個寄存器:
    • AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、CS、ES、PSW
  • AX、BX、CX、DX一般用來存放通常性數據,被稱爲通用寄存器。
  • 16位寄存器所能存儲的數據最大值爲2^16^-1 。
  • 爲保證兼容性,8086 CPU的通用寄存器能夠分爲兩個獨立的8位寄存器使用。例: AX可分爲AH和AL。

2.2 字在寄存器中的存儲

  • 8086 CPU全部的寄存器是16位,能夠存放2個字節(一個字)。windows

  • 一字節由8 bit 組成,能夠存在8位寄存器中。api

  • 字(word)是兩字節,16位。數組

    一個字由兩個字節組成

2.3 幾條彙編指令

  • 彙編指令對大小寫不敏感安全

    彙編指令舉例框架

彙編指令 控制CPU完成的操做 用高級語言的語法描述
mov ax,18 將8送入AX AX=18
mov ah,78 將78送入AH AH=78
add ax,8 將寄存器AX中的數值加上8結果存入AX中 AX=AX+8
mov ax,bx 將寄存器BX中的數據送入寄存器AX AX=BX
add ax,bx 將AX,BX中的內容相加結果存入AX中 AX=AX+BX

檢測點 2.1

檢測點2.1

2.4 物理地址

  • 全部的內存單元構成一個一維的線性存儲空間。
  • CPU訪問內存單元時要給出內存單元的惟一地址就是物理地址。

2.5 16位結構的CPU

  • 一、運算器一次最多能夠處理16位數據。
  • 二、 寄存器的最大寬度爲16位。
  • 三、寄存器和運算器之間的通路是16位。

2.6 8086 CPU給出物理地址的方法

  • 8086有20位的地址總線,能夠傳送20位地址,尋址能力爲1M;但8086內部爲16位結構,只能傳送16位的地址。
  • 8086CPU採用一種在內部用兩個16位地址合成的方法來造成一個20位的物理地址。

8086CPU相關部件的邏輯結構

  • 8086CPU讀寫內存的步驟:
    • 一、CPU中的相關部件提供段子和偏移地址這兩個16位的地址;
    • 二、段地址和偏移地址經過內部總線送入到一個稱爲地址加法器的部件;
    • 三、地址加法器將兩個16位地址合併成一個20位的地址;
    • 四、地址加法器經過內部總線將20位物理地址送送入輸入輸出地址;
    • 五、輸入輸出控制電路將20位物理地址送上地址總線;
    • 六、20位物理地址被地址總線傳送到存儲器。
  • 地址加法器工做原理:物理地址=段地址*16+偏移地址

地址加法器的過程

  • 段地址*16就是數據左移4位(二進制)
移位位數 二進制 十六進制 十進制
0 10B 2H 2
1 100B 4H 4
2 1000B 8H 8
3 10000B 10H 16
4 100000B 20H 32
  • 一個數據的二進制形式左移N位,至關於該數據乘以2的N次方。一個數據X進制形式左移N位,至關乘以NX。

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

  • CPU能夠經過不一樣的段地址和偏移地址造成一個相同的物理地址。

CPU能夠經過不一樣的段地址和偏移地址造成相同的物理地址

段地址*16是移位編輯器

2.8 段的概念

  • 人爲定義的,將若干地址連續的內存單元看做一個段。用段地址*16定位段的起始地址(基址),用偏移地址定位段中的內存單元。
    段的概念

  • 一個段的起始地址是16的倍數。偏移地址爲16位,尋址能力爲64K,因此段的最大長度也是64K。

檢測點 2.2

檢測點2.2

2.9 段寄存器

  • 8086 CPU有4個段寄存器:CS(代碼段)、DS(數據段)、SS(堆棧段)、ES(附加段),這4個段提供給8086CPU內存單元的段地址。

2.10 CS和IP

  • CS(代碼段寄存器)IP(指令指針寄存器) 是8086CPU中最關鍵的寄存器,它們指示了CPU當前要讀取指令的地址。在任意時刻CPU將CS:IP指向的內容看成指令執行。
  • 8086CPU工做過程的簡要概述:
    • 一、從CS:IP指向內存單元讀取指令,讀取的指令進入指令緩衝器;

      8086PC機剛開始啓動時,CPU從內存FFFF0h單元中讀取指令執行,FFFF0h單元中的指令時8086PC機開機後執行的第一條指令。

    • 二、 IP=IP+所讀取指令的長度,從而正確的指向下一條指令;
    • 三、執行指令。轉到步驟1,周而復始。

2.11 修改CS、IP的指令

  • mov指令(傳送指令) 能夠改變8086CPU大部分寄存器的值,但不能用於設置CS、IP的值。
  • jmp指令(轉移指令) 能夠用來同時修改CS和IP的值,格式爲
jmp 段地址:偏移地址;同時修改CS和IP
jmp 某一合法寄存器;則是僅修改IP

2.12 代碼段

  • 對於8086PC機,在編程時能夠將長度爲N(N小於等於64KB)的一組代碼存在一組地址連續、起始地址爲16的倍數的內存單元中,這段內存是用來存放代碼的,從而定義了一個代碼段。
  • 利用CS:IP來指向內存單元從而讓CPU執行其中的內容。

檢測點 2.3

檢測點2.3

使用Debug

windows xp系統自帶debug,請使用xp以上系統的讀者執行自行下載debug.exe和dosbox,使用方法筆者再也不贅述,在dosbox中可使用debug。
debug in dosbox

  • 可使用匯編金手指查閱指令。
  • R命令查看、改變CPU寄存器的內容;
  • D命令查看內存中的內容;
  • E命令改寫內存中的內容;
  • U命令將內存中的機器指令翻譯成彙編指令;
  • T命令執行一條機器指令;
  • G命令跳轉到偏移地址;
  • P命令結束循環或者是int 21H時是退出程序;
  • A命令是以彙編指令的格式在內存中寫入一條機器指令。

3、寄存器(內存訪問)

3.1 內存中字的存儲

  • 字是兩個字節,要用兩個地址連續的內存來存放,字的低位字節存在低地址中,高位字節存放在高地址單元中。

3.2 DS和[address]

  • DS一般存放要訪問的數據的段地址。
  • 8086 CPU因爲硬件的設計不支持將數據直接送入段寄存器的操做。

    數據 -> 通用寄存器 -> 段寄存器

  • [ ]裏邊的數據表明偏移地址值
  • mov指令:
    • 將數據直接送入寄存器;
    • 將一個寄存器或內存單元中的內容送入另外一個寄存器;
  • mov指令格式:
mov 寄存器名,內存單元

3.3 字型的傳送

  • 高地址單元和高8位寄存器,低地址單元和低8位寄存器相對應。

3.4 mov、add、sub指令

  • 有兩個操做對象,jmp只有一個操做對象。
  • 使用匯編金手指查閱指令
  • mov指令的幾種形式
mov 寄存器,數據;mov ax,8
mov 寄存器,寄存器;mov ax,bx
mov 寄存器,內存單元;mov ax,[0]
mov 內存單元,寄存器;mov [0],ax
mov 段寄存器,寄存器;mov ds,ax
mov 寄存器,段寄存器;mov ax,ds
     ……
  • add指令的幾種形式
add 通用寄存器,數據
add 通用寄存器,通用寄存器
add 通用寄存器,內存單元
add 內存單元,寄存器
  • sub指令的幾種形式
sub 通用寄存器,數據
sub 通用寄存器,通用寄存器
sub 通用寄存器,內存單元
sub 內存單元,通用寄存器

3.5 數據段

  • 對於8086PC機,在編程時能夠將長度爲N(N小於等於64KB)的一組代碼存在一組地址連續、起始地址爲16的倍數的內存單元中,這段內存是用來存放數據的,從而定義了一個數據段。
  • 能夠經過在DS中存放數據段的段地址,用相關的指令訪問數據段中的具體單元來訪問數據段中的數據。

檢測點 3.1

檢測點3.1

3.6 棧

  • 具備特殊的訪問方式的存儲空間,也是內存空間的一部分,數據先進後出。
  • 有兩個基本操做:
    • 入棧:將一個新的元素放到棧頂;
    • 出棧:從棧頂取出一個元素。
  • 棧頂元素最後入棧最早出棧。

3.7 8086 CPU提供的棧機制

  • 現今的CPU都有棧的設計,基於8086CPU編程能夠將一段內存看成棧來使用。
  • 8086CPU的入棧(PUSH)POP(出棧),以字爲單位。
    • push ax 將寄存器ax中的數據送入棧
    • pop ax 從棧頂取出數據送入ax
  • 段寄存器SS存放棧頂的段地址,寄存器SP存放棧頂的偏移地址。任意時刻SS:SP指向棧頂元素。push時SP先自減法後寫內存,pop先讀內存sp後自加。
  • pop以後數據仍是存在內存中,push時覆蓋。

    CS和IP存放當前指令的段地址和偏移地址。

3.8 棧頂越界的問題

  • 棧是空的,則SP指向棧底+1的內存。
  • 8086 CPU只紀錄棧頂,棧空間由本身控制。棧頂越界問題致使溢出漏洞。
  • 8086CPU只考慮當前的狀況:
    • 當前棧頂在何處;
    • 當前要執行的指令時哪一條。

3.9 push、pop指令

  • 能夠直接對段寄存器使用。
;push和pop格式
push 寄存器
pop 寄存器
push 段寄存器
pop 段寄存器
push 內存單元
pop 內存單元
  • 通用寄存器命名是x結尾的,段寄存器是以s結尾。
  • CPU在執行指令時,數據的段地址是從DS中得到,代碼是在CS中得到,棧地址是從SS得到。

3.10 棧段

  • 對於8086PC機,在編程時能夠將長度爲N(N小於等於64KB)的一組代碼存在一組地址連續、起始地址爲16的倍數的內存單元中,這段內存是看成棧來用,從而定義了一個棧段。
  • 寄存器清零可用sub ax,ax或者直接賦值0,常見的也有使用xor
  • 當棧空間定義爲最大時,棧爲空時SP=0。

檢測點 3.2

檢測點3.2


4、第一個程序

引言

編寫完成的彙編語言程序,用編譯器編譯成可執行文件並在操做系統中運行。

4.1 一個源程序從寫出到執行的過程

  • 編寫
    • 用編輯器(Sublime Text、Nodepad++、UltraEdit)編寫,文件後綴爲.asm。
  • 編譯連接
    • 使用MASM.EXE編譯生產obj(目標文件)。masm也請讀者自行搜索下載。
    • LINKE.EXE對目標文件進行鏈接生產可在操做系統中直接運行的可執行文件。

      可執行文件包含程序(機器碼)、數據(源程序中定義的數據)和相關的描述信息。

  • 執行
    • 操做系統中依照可執行文件中的描述信息將可執行文件中的機器碼和數據加載入內存並進行相關的初始化,而後CPU執行。

4.2 源程序

  • 彙編指令:有對應的機器碼的指令,編譯爲機器碼被CPU執行
  • 僞指令:沒有對應的機器碼,不被CPU所執行,由編譯器執行來進行相關的編譯工做。
    • segment和ends是用來定義一個段的,是成對使用的僞指令,再寫可被編譯器編譯的彙編程序是必需要用的。
    assume cs:codesg ;假設代碼段的名稱爲codesg
    codesg segment ;定義一個codesg段
    mov ax,0123H
    mov bx,0456H
    add ax,bx
    add ax,ax
    mov ax,4c00h
    int 21h
    codesg ends ;codesg段結束
    end ;是個僞指令,程序的結束標記

assume用來加上某一段寄存器和程序中的某一用segment……ends定義的段相關聯。經過assume說明這種關聯,在須要的狀況下編譯程序能夠將段寄存器和某一個具體的段相聯繫。

  • 一個彙編程序是由多個段組成。一個有意義的彙編程序中至少要用一個段來存放代碼。
  • 程序與源程序
    程序與源程序

  • 標號:指代地址
  • 程序的結構

  • 小練習:

;編程運算2^3
assume cs:abc ;段與寄存器關聯

abc segment ;定義一個段,名稱爲abc
mov ax,2;寫入彙編指令
add ax,ax
add ax,ax
 
abd ends      
end ;程序結束處
  • 程序的返回:一個程序結束後將CPU的控制權交還給使它得以運行的程序的過程。應該在程序的末尾添加返回的程序段。

    codesg:放在segment前面,做爲一個段的名稱,這個段的名稱最終將被編譯、鏈接程序,稱爲一個段的段地址 。

mov ax,4c00H
int 21H ;第21號中斷
;這兩條指令說實現的功能就是程序返回。
  • 語法錯誤和邏輯錯誤

4.3 編輯源程序

  • 使用編輯器編輯,擴展名爲.asm
assume cs:ABC
    ABC segment
        mov ax,2
        add ax,ax
        add ax,ax
        mov ax,4c00H
        int 21h
    ABC ends
    end

4.4 編譯

  • masn和 1.asm在同一目錄中,dos下使用masm 1.asm命令便可生產1.obj文件。
    源程序文件和masm文件在同一目錄下

    編譯源程序

4.5 鏈接

  • link 1.obj,生成exe文件,摁enter忽略編譯程序提示輸入的信息。

    鏈接程序

    鏈接生成exe

  • 當源程序很大時,能夠將它分紅多個源程序文件編譯,每一個源程序編譯成目標文件後再用鏈接程序將他們鏈接到一塊兒,生成一個可執行文件。或者程序中調用了某個庫文件中的子程序,須要將這個庫文件和該目標文件鏈接到一塊兒,生成一個可執行文件。或者一個源程序編譯後獲得存有機器碼的目標文件,目標文件中的有些內容還不能直接生成可執行文件,鏈接程序將此內容處理爲最終的可執行文件信息。

4.6 簡化編譯和鏈接

  • 使用ml命令,ml 1.asm

4.7 exe的執行

  • 爲兼容16位的程序,使用dosbox運行。

4.8 可執行文件中的程序轉入內存並運行的原理

  • 在dos中可執行文件中的程序p1若要運行嗎必須有一個正在運行的程序p2將p1從可執行文件中加載如內存,將CPU的控制權交給它,p1才能得以運行;當p1運行完畢後,應該將CPU的控制權交還給使它de'yi 運行的程序p2。
  • 彙編程序從寫出到執行的過程:編程 -> 編譯 -> 鏈接 -> 加載 -> 內存中的程序 -> 運行
  • 在dos系統中.exe文件中的加載過程

    exe文件中程序的加載過程

    psp的內容

4.9 程序執行過程的跟蹤

  • 使用debug(xp以上的系統在dosbox中使用)來跟蹤一個程序的運行過程。

使用debug來跟蹤程序運行


5、[BX]和loop指令

引言

  • 約定符號()來表示一個寄存器或者一個內存單元中的內容。例如(ax)=0010H表示ax中的內容爲0010H;(21000H)=0010H,表示2000:1000處的內容爲0010H。
  • 約定符號idata表示常量。

5.1 [BX]

  • inc指令是自增1的意思
  • 和[0]有些相似,[0]表示內存單元,它的偏移地址是0。[bx]也是表示一個內存單元,它的內存偏移地址在bx中。
mov bx,0
mov ax,[bx]
mov al,[bx]
  • 用如下兩種信息描述一個內存單元:
    • 一、內存單元的地址;
    • 二、內訓單元的長度(類型)。

      咱們用[0]表示一個內訓單元時,0表示單元的偏移地址,段地址默認在DS中,單元的長度(類型)能夠由具體指令中的其餘的操做對象(好比說寄存器)指出。

mov ax,[0];0對應的字單元,主要單位要看操做對象(寄存器)
mov al,[0];字節

5.2 loop指令

  • 指令的格式是loop 標號。CUP執行loop指令時要進兩步操做:
    • CX中存放循環的次數,執行時CX中的內容自減1。至關於C的do while
    • 判斷CX中的值,不爲0則轉至標號處執行程序,爲0則向下執行。
  • 一般loop指令來實現循壞功能CX中存放循環的次數。
assume cs:code
       code segment
       mov ax,2
       add ax,ax
          
       mov ax,4c00H
       int 21H
code ends
end
;計算2^3
assume cs:code
code segment
     mov ax,2
     add ax,ax
     add,ax,ax
          
     mov ax,4c00H
     int 21h
code ends
end
;計算2^12
assume cs:code
code segment
start:  mov ax,2
        mov cx,11
      p:add,ax,ax       
        loop p;p是標號
              
       mov ax,4c00H;masm默認數字是十進制
       int 21H
code ends
end start
;編程計算123*236,結果放在ax中
assume cs:code
code segment
start:mov ax,0
      mov cx,236
   an:add ax,123
     loop an
      mov ax,4c00H
      int 21H
code ends
end start
assume cs:code
code segment
start:mov ax,0
      mov cx,123
  pa:add ax,236
      loop pa
       
      mov ax,4c00H
      int 21H
 
code ends
end start

5.3 在Debug中跟蹤用loop指令實現的循環程序

  • 注意:在彙編源程序中數據不能以字母開頭,有字母的在前面加0處理。
  • t命令單步執行、G命令和P命令。
  • 使用匯編金手指查閱指令。

5.4 Debug和彙編編譯器Masm對指令的不一樣處理

  • Degug中mov ax,[0],表示將ds:0處的數據存入al中。ah=0,由於一個內存單元是8位的,ax是16位的,同位存儲。而編譯器[0]會被看成0處理
  • 將內存2000:0、2000:一、2000:二、2000:3單元中的數據(字節)送入阿al、bl、cl、dl中。
    • debug中:

    debug 1

    debug 3
    • 在MASM中:

    1masmtest

    masmtest2
    • 要在編譯器中實現用偏移地址[]中的內容傳送先bx來代替,mov 偏移地址,bx 再 mov al,[bx]。如要直接使用[ ]則要加上段地址ds:[偏移地址]
  • 在MASM中:
mov al,[0] ;將al賦值0
mov al,ds[0] ;將al賦值段地址爲ds,偏移地址爲0的內存單元中的內容
mov al,[bx] ;默認段地址爲ds,將al賦值偏移地址爲bx
mov al,ds:[bx] ;將al賦值段地址爲ds,偏移地址爲bx

5.5 loop和[BX]的聯合應用

  • 能夠用循環來解決處理地址連續的內存單元中的數據的問題,用變量來給出內存單元的地址。

5.6 段前綴

  • 出如今訪問內存單元的指令中用顯式地指明內存單元的段地址的ds、cs、ss、es稱爲段前綴。沒有顯式地給出內存單元的段地址則默認在ds中。

5.7 一段安全的空間

在8086模式中,隨意向一段內存空間寫入數據是危險的,由於這段空間中可能存放着重要的系統數據或代碼。

assume cs:code
code segment
    mov ax,0
    mov ds,ax
    mov ds:[26H],ax
    mov ax,4c00H
    int 21H
code ends
end
  • 但筆者在練習的時候出現dosbox下debug卡死

    dangeroustest
  • dos下0:200H~0:2FFH的256個字節的空間是安全的,dos和其餘合法程序通常都不會使用這段空間。內存0000:0000~0000:03FF大小爲1kb的空間是系統存放中斷處理程序入口地址的中斷向量表。通常狀況下0:200H~0:2FFH的256個字節的空間所對應的中斷向量表都是空的,操做系統和其餘應用程序都不佔用。
    dos安全空間

5.8 段前綴的使用

  • 將內存ffff:0~ffff:b段單元中的數據拷貝到0:200 ~ 0:20b單元中
assume cs:code
code segment
          mov bx,0 ;(bx)=0,偏移地址從0開始
          mov cx,12 ;(cx)=12,循環12次
      s:  mov ax,offffh
          mov ds,ax ;(ds)=0ffffh
          mov dl,[bx] ;(ds)=((ds)*16+(bx)),將ffff:bx中的數據送入dl
          mov ax,0020h
          mov ds,ax ;(ds)=0020h
          mov [bx],dl ;((ds)*16+(bx))=dl,將數據送入0020:bx
          inc bx ;(bx)=(bx)+1
          loop s
      
          mov ax,4c00h
          int 21h
code ends
end
  • 兩個內存單元相差64KB則再也不同一個段裏,須要設置ds的值兩次,效率不高。
  • 使用 es(附加段)
;優化後的代碼,優化了兩次設置ds
assume cs:code
code segment
           mov ax,offffh
           mov ds,ax ;(ds)=0ffffh
           mov ax,0020h
           mov es,ax ;(es)=0020H
           mov bx,0 ;(bx)=0,此時ds:bx指向ffff:0,es:bx指向0020:0
           mov cx,12 ;(cx)=12,循環12次
       s:  mov dl,[bx] ;(ds)=((ds)*16+(bx)),將ffff:bx中的數據送入dl
           mov es:[bx],dl ;((es)*16+(bx))=dl,將數據送入0020:bx
           inc bx ;(bx)=(bx)+1
           loop s
       
           mov ax,4c00h
           int 21h
code ends
end

6、包含多個段的程序

6.1在代碼段中使用數據

  • 編程計算0123H、0456H,0abxH、0defH、0fesH、0cbaH、0987H這8個數據的和,結果存放在ax中:
assume cs:codesg
codesg segment
        dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
        ;dw,define word,定義字型數據,db定義字節型數據
        ;因爲數據在代碼段中,因此段地址是CS
        ;dw定義的數據在最開始的地方,因此偏移地址是0開始
    start:mov bx,0 ;第一條指令
        mov ax,0
        mov cx,8
    s:      add ax,cs:[bx]
        add bx,2
        loop s
        mov ax,4c00H
        int 21H
codesg ends
end start ;入口找end

定義字型數據

  • end的做用除了通知編譯器結束以外還有告訴編譯器程序的入口在什麼地方。
  • 可執行文件中的程序執行過程

6.2 在代碼段中使用棧

  • 利用棧編程將定義的數據逆序(聯想棧的特性)存放:dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
assume cs:codesg
codesg segment
        dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H;地址0~15
        dw 0,0,0,0,0,0,0,0;定義8個字型空數據,後面看成棧來使用,地址是16~31
    
    start:  
            mov ax,cs
            mov ss,ax
            mov sp,32;設置棧底ss:sp指向cs:32,十進制的32
            mov bx,0
            mov cx,8
           s:push cs:[bx]
            add bx,2
            loop s; 以上代碼段0~15個單元中的8個字型數據一次入棧
    
            mov bx,0
            mov cx,8
          s0:pop cs:[bx]
            add bx,2
            loop s0;依次出棧8個執行數據到代碼段0~15單元中
    
            mov ax,4c00h
            int 21h
codesg ends
end start;指明程序入口在start處
  • 若是對此程序的棧有疑惑,跳轉到 3.6 棧和3.10 棧段

6.3 將數據、代碼、棧放入不一樣的段

  • 在8086CPU中數據、棧和代碼存儲空間不能大於64KB。能夠用像定義代碼段同樣的方法來定義多個段並在其中定義須要的數據,或者經過定義數據來取得棧空間。
assume cs:codesg,ds:data,ss:stack;在源程序中爲三個段進行有意義的名稱
data segment
        dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
data ends
    
stack segment
        dw 0,0,0,0,0,0,0,0;定義8個字型空數據,後面看成棧來使用
stack ends
    
code segment
    start:      
            mov ax,stack
            mov ss,ax
            mov sp,16;設置棧底ss:sp指向stack:16,
            mov ax,data
            mov ds,ax;ds指向data段
            mov bx,0;ds:bx指向data段中的第一個單元
           s:push cs:[bx]
            add bx,2
            loop s; 以上代碼段0~16個單元中的8個字型數據一次入棧
    
            mov bx,0
            mov cx,8
          s0:pop cs:[bx]
            add bx,2
            loop s0;依次出棧8個執行數據到代碼段0~16單元中
    
            mov ax,4c00h
            int 21h
codesg ends
end start;指明程序入口在start處
  • 程序中指令決定了斷中的內容是做爲數據處理仍是做爲指令執行仍是做爲棧空間使用。

檢測點 6.1

檢測點6.1

實驗五

assume cs:codesg,ds:data,ss:stack
data segment
        dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
data ends
    
stack segment
        dw 0,0,0,0,0,0,0,0
stack ends
    
codesg segment
    start:  mov ax,stack
            mov ss,ax
            mov sp,16
            mov ax,data
            mov ds,ax
            push ds:[0]
            push ds:[2]
            pop ds:[2]
            pop ds:[0]
    
            mov ax,4c00h
            int 21h
codesg ends
end start

7、更靈活的定位內存地址的方法

7.1 and和or指令

  • and指令:邏輯與指令,按位進行與運算。

    and兩個同時爲真的結果才爲真。

mov al,01100011B
and al,00111011B
;執行後 al=00100011B
  • 可用and指令將操做對象的相應位設爲0,其餘位不變
and al,10111111B;將al第六位設爲0
and al,01111111B;將al第七位設爲0
and al,11111110B;將al第0位設爲0
  • or指令:邏輯或指令,按位進行或運算。
  • or兩個同時爲假的結果才爲假

mov al,01100011B
and al,00111011B
;執行後 al=01111011B
  • 可用or指令將操做對象的相應位設爲1,其餘位不變
and al,01000000B;將al第六位設爲1
and al,10000000B;將al第七位設爲1
and al,00000001B;將al第0位設爲1

7.2 關於ASCII碼

ASCII碼錶

  • 將字符的ascii碼寫入顯存屏幕就顯示出相關的字符。

7.3 以字符形式給出數據

  • 用‘’的方式指明數據是以字符的形式給出的。例如'A'
assume cs:code,ds:data

data segment
        db 'unIx'
        db 'foRK'
data ends
    
    code segment
    start:  mov al,'a'
            mov bx,'b'
    
            mov ax,4c00h
            int 21h
code ends
end start

73 ascii

以字符形式給出數據

7.4 大小寫轉換的問題

  • 大寫字母比小寫字母ASCII大32(20H)。
大寫 二進制 小寫 二進制
A 01000001 a 01100001
B 01000010 b 01100010
C 01000011 c 01100011
D 01000100 d 01100100
  • 從第0位開始計算,大寫字母ASCII碼第五位爲0,小寫字母ASCII碼第五位爲1。
;大小寫轉換
assume cs:codesg,ds:datasg
datasg segment
db'BaSiC'
db'iNfOfMaTiOn'
datasg ends
    
codesg segment
    start:  mov ax,datasg
            mov ds,ax;設置ds執行datasg段
            mov bx,0;設置(bx)=0,ds:bx指向'BaSiC'的第一個字母
            mov cx,5;設置循環次數,由於BaSiC有5個字母
              s:mov al,[bx];將ASCII碼從ds:bx所指向的單元中取出
            and al,11011111B;口岸al中ASCII碼的第5個位置變爲0,變爲大寫字母
            mov [bx],al;轉變後將ASCII碼寫回單元
            inc bx;(bx)加1,ds:bx指向下一個字母
            loop x
            mov bx,5;設置(bx)=5,ds:bx指向'iNfOfMaTiOn'的第一個字母
            mov cx,11
             s0:mov al,[bx]
            or al,00100000B
            mov [bx],al
            inc bx
            loop s0
    
            mov ax,4c00H
            int 21H
codesg ends
end start

7.5 [bx+idata]

  • [bx+idata]表示的是一個內存單元,它的偏移地址爲bx+idata
;[bx+idata]能夠寫成如下格式
mov ax,[200+bx]
mov ax,200[bx]
mov ax,[bx].200
;使用debug查看內存
mov ax,2000H
mov ds:ax
mov bx,1000H
mov ax,[bx]
mov cx,[bx+1]
add cx,[bx+2]

7.6 用[bx+idata]的方式進行數組的處理

  • 用[bx+idata]的方式進行數組處理
;改進大小寫轉換程序
assume cs:codesg,ds:datasg
datasg segment
db'BaSiC'
db'iNfOfMaTiOn'
datasg ends
    
codesg segment
    start:  mov ax,datasg
            mov ds,ax;設置ds執行datasg段
            mov bx,0;設置(bx)=0,ds:bx指向'BaSiC'的第一個字母
            mov cx,5;設置循環次數,由於BaSiC有5個字母
          s:mov al,[bx+0];將ASCII碼從ds:bx所指向的單元中取出
            and al,11011111B;口岸al中ASCII碼的第5個位置變爲0,變爲大寫字母
            mov [bx],al;轉變後將ASCII碼寫回單元
            mov [bx+5];定位第二個字符串的字符
            or al,00100000B
            mov [bx+5],al
            inc bx
            loop s
    
            mov ax,4c00H
            int 21H
codesg ends
end start
  • C語言的形式
include<stdio.h>

    char a[5]="BaSiC";
    char b[11]="iNfOfMaTiOn";
    main()
    {
        int i;
        i=0;
        do
        {
            a[i]=a[i]&0xDF;
            b[i]=b[i]|0x20;
            i++;
        }while(i<5);
    }

7.7 SI和DI

  • SI和DI在8086CPU中和bx功能相近,充當BX的擴充,可是不能分紅兩個8位寄存器來使用。[SI]段地址默認也是在DS中。
  • 下面的指令實現了相同的功能
mov bx,0
mov ax,[bx]
    
mov si,0
mov ax,[si]
    
mov di,0
mov ax,[di]
;-------------
;下面的三組指令也實現了另外一個組相同的功能
;-------------
mov bx,0
mov ax,[bx+123]
    
mov si,0
mov ax,[si+123]
    
mov di,0
mov ax,[di+123]
  • 通常ds:si指向要複製的原始空間,ds:di指向複製的目的空間。
;用DI和SI實現複製到它後面的數據區中
assume cs:codesg,ds:datasg
datasg segment
db'welcome to asm!'
db'................'
datasg ends
    
codesg segment
    start  :mov ax,datasg
            mov ds,ax
            mov si,0
            mov di,16
            mov cx,8
          s:mov ax,[si]
            mov [di],ax
            add si,2
            add di,2
            loop s
    
            mov ax,4c00h
            int 21H
;------
;用數組的思惟[bx(si或di)+idata]的方式優化程序
;------
assume cs:codesg,ds:datasg
datasg segment
db'welcome to asm!'
db'................'
datasg ends
    
codesg segment
    start  :mov ax,datasg
            mov ds,ax
            mov si,0
            mov cx,8
          s:mov ax,[si];第一個字符串的的第一個元素
            mov [si+16],ax;目標字符串的第二個元素
            add si,2
            loop s
    
            mov ax,4c00h
            int 21H
codesg ends
end start

7.8 [bx+si]和[bx+di]

mov ax,2000h
    mov ds,ax
    mov bx,1000h
    mov si,0
    mov ax,[bx+si]
    inc si
    mov cx,[bx+si]
    inc si
    mov di,si
    mov ax,[bx+di]

7.9 [bx+si+idata]和[bx+di+idata]

  • 常數後要加.例如[bx+si].idata或者[bx].idata[si]
mov ax,2000h
    mov ds,ax
    mov bx,1000h
    mov si,0
    mov ax,[bx+2+si]
    inc si
    mov cx,[bx+si+2]
    inc si
    mov di,si
    mov ax,[bx+di+2]

7.10 不一樣的尋址方式的靈活應用

  • 編程將數據段中每個單詞的頭一個字母改成大寫字母。

數據段中的數據存儲結構_2

assume cs:codesg,ds:datasg
datasg segment
    db'1. file          ';長度恰好都是16個字節
    db'2. edit          '
    db'3. search        '
    db'4. view          '
    db'5. options       '
    db'6. help          '
datasg ends
    
codesg segment
      start:
            mov ax,datasg
            mov ds,ax
            mov bx,0
            mov cx,6
          s:    
            mov al,[bx+3]
            and al,11011111B
            mov [bx+3],al
            add bx,16
            loop s
            
            mov ax,4c00h
            int 21h
codesg ends
end start
  • 編程將數據段中每一個單詞改成大寫字母

數據段中的數據存儲結構2

;有bug,問題在於cx的使用,進行二重循環,只用一個循環計數器,形成在進行內層的時候覆蓋了外層循環的循環計數值。
assume cs:codesg,ds:datasg
datasg segment
    db 'ibm             '
    db 'dec             '
    db 'dos             '
    db 'vax             '
datasg ends
    
codesg segment
      start:mov ax,datasg
            mov ds,ax
            mov bx,0;用bx來定位行
            mov cx,4
         s0:mov si,0;用si來定位列
            mov cx,3
          s:mov al,[bx+si]
            and al,11011111B
            mov [bx+si],al
            inc si
            loop s
            add bx,16
            loop s0
            
            mov ax,4c00h
            int 21h
codesg ends
end start
  • 程序沒有返回到cmd

712bug

712bug1

712bug2

loop s;三次循環後cx等於0了
add bx,16
loop s0;先是cx=cx-1再判斷時候等於0,此時cx=FFFF不爲0再循環,變成死循環了
  • 由於loop是和cx一塊兒使用的,不能多用個寄存器來解決loop循環次數的問題。解決的方法是在每次開始內層循環時用dx將外層循環cx的值保存起來,在執行外層循環的loop指令前再回復外層循環的cx的數值。
  • 改進後程序
assume cs:codesg,ds:datasg
    datasg segment
    db 'ibm             '
    db 'dec             '
    db 'dos             '
    db 'vax             '
datasg ends
    
codesg segment
      start:
            mov ax,datasg
            mov ds,ax
            mov bx,0;用bx來定會行
            mov cx,4
         s0:
            mov dx,cx;用dx寄存器來臨時存放外層cx的值
            mov si,0;用si來定位列
            mov cx,3
          s:
            mov al,[bx+si]
            and al,11011111B
            mov [bx+si],al
            inc si
            loop s
            add bx,16
            mov cx,dx;在進行外層循環的時候回覆cx的值
            loop s0
            mov ax,4c00h
            int 21h
codesg ends
end start
  • 在上面的程序中,8086 CPU si、cx、ax、bx這些寄存器常常要使用到;cs、ip、ds也不能用,由於cs:ip時刻指向當前指令,ds指向datasg段;那麼可用的寄存器就只用dx、di、es、ss、sp、bp等寄存器了。內存能夠解決常常性的數據暫存問題。爲了使程序結構清晰便於閱讀,應該使用棧
  • 再次被改進的程序
assume cs:codesg,ds:datasg
    datasg segment
    db 'ibm             '
    db 'dec             '
    db 'dos             '
    db 'vax             '
    dw 0;定義一個字用來保存cx的值
datasg ends
    
codesg segment
      start:mov ax,datasg
            mov ds,ax
            mov bx,0;用bx來定位行
            mov cx,4
         s0:mov ds:[40h],cx;datasg:40h單元存放外層cx的值
            mov si,0;用si來定位列
            mov cx,3
          s:mov al,[bx+si]
            and al,11011111B
            mov [bx+si],al
            inc si
            loop s
            add bx,16
            mov cx,ds:[40h];在進行外層循環的時候回覆cx的值
            loop s0
            mov ax,4c00h
            int 21h
codesg ends
end start
  • 再次使用棧改進程序
assume cs:codesg,ds:datasg,ss:stacksg
datasg segment
    db 'ibm             '
    db 'dec             '
    db 'dos             '
    db 'vax             '
datasg ends
    
stacksg segment
    dw 0,0,0,0,0,0,0,0;定義一個段,用做棧段,容量爲16個字節
stacksg ends
    
codesg segment
      start:mov ax,stacksg
            mov ss,ax
            mov sp,16
            mov ax,datasg
            mov ds,ax
            mov bx,0;用bx來定位行
            mov cx,4
         s0:push cx;datasg:40h單元存放外層cx的值
            mov si,0;用si來定位列
            mov cx,3
          s:mov al,[bx+si]
            and al,11011111B
            mov [bx+si],al
            inc si
            loop s
            add bx,16
            pop cx;在進行外層循環的時候回覆cx的值
            loop s0
            mov ax,4c00h
            int 21h
codesg ends
end start
  • 編程將數據段中的每一個單詞的前四個字母改成大寫字母

數據段中的數據存儲結構3

assume cs:codesg,ds:datasg,ss:stacksg
    
stacksg segment
stacksg ends
    
datasg segment
    db '1. display      '
    db '2. brows        '
    db '3. replace      '
    db '4. modify       '
datasg ends
codesg segment
      start:mov ax,stacksg
            mov ss,ax
            mov sp,16
            mov ax,datasg
            mov ds,ax
            mov bx,0
            mov cx,4
         s0:push cx
            mov si,0
            mov cx,4
          s:mov al,[bx+si+3]
            and al,11011111B
            mov [bx+si+3],al
            inc si
            loop s
            add bx,16
            pop cx
            loop s0
            
            mov ax,4c00h
            int 21h
codesg ends
end start

8、數據處理的兩個基本問題

引言

  • 本章是總結性的內容,數據處理的兩個基本問題是
    • 處理的數據在哪?
    • 要處理的數據有多長?
  • 自定義得描述符:
    • reg寄存器
      • ax、bx、cx、dx、ah、al、bh、bl、ch、cl、dh、dl、sp、bp、si、di;
    • sreg段寄存器
      • ds、ss、cs、es。

8.1 bx、si、di、bp

  • 在8086 CPU中只有bx、si、di、bp這四個寄存器用在[ ]中進行內存單元尋址。在[]中,組合只能以這四種形式:bx和si、bx和di、bp和si、bp和di
;如下指令是錯誤的
    mov ax,[ax]
    mov ax,[cx]
    mov ax,[dx]
    mov ax,[ds]
    mov ax,[bx+bp]
    mov ax,[si+di]
  • 正確的指令
mov ax,[bx]
    mov ax,[si]
    mov ax,[di]
    mov ax,[bp]
    mov ax,[bx+si]
    mov ax,[bx+di]
    mov ax,[bp+si]
    mov ax,[bp+di]
    mov ax,[bx+si+idata]
    mov ax,[bx+di+idata]
    mov ax,[bp+si+idata]
    mov ax,[bp+di+idata]
  • [bp]的段地址默認在ss中。

8.2 機器指令處理的數據所在的位置

  • 絕大部分機器指令時進行數據處理的,大體能夠分爲3類:讀、寫、運算。指令在處理前能夠在三個地方:CPU內部、內存、端口。
機器碼 彙編指令 指令執行前數據的位置
89C3 mov bx,[0] 內存,ds:0單元
89C3 mov bx,ax CPU內部,ax寄存器
BB0100 mov bx,1 CPU內部,指令緩衝器

8.3 彙編語言中數據位置的表達

  • 彙編語言中用三個概念來表達數據的位置。
    • 一、當即數(idata)
    • 二、寄存器
    • 三、段地址(SA)和偏移地址(EA)

8.4 尋址方式總小結

尋址方式總結_1

8.5 指令要處理的數據有多長

  • 8086 CPU能夠處理byte和word兩種數據尺寸。
  • 經過寄存器指明要處理的數據尺寸;push指令只進行字操做,若沒有寄存器名存在的狀況下,用操做符word ptr或者byte ptr指明內存單元的長度。 例如
mov word ptr ds:[0],1
    inc word ptr [bx]
    inc word ptr ds:[0]
    add byte ptr [bx],2
;假設內存2000:1000 FF FF FF FF FF FF ……
    ;若是用如下指令
    mov ax,2000H
    mov ds,ax
    mov byte ptr [1000H],1
    ;那麼內存中的內容變爲
    ;2000:1000 01 FF FF FF FF FF ……
    若是是用如下指令
    mov ax,2000H
    mov ds,ax
    mov word ptr [1000H],1
    ;那麼內存中的內容變爲
    ;2000:1000 01 00 FF FF FF ……

8.6 尋址方式的綜合應用

86題目_1

  • 初步彙編代碼
mov ax,seg
    mov ds,ax
    mov bx,60h;肯定記錄物理地址:ds:bx
    mov word ptr [bx+0ch],38;寄存器相對尋址     排名字段改成38
    add word ptr [bx+0eh],70;收入字段增長70
    
    mov si,0;用si來定位產品字符串中的字符
    mov byte ptr [bx+10h+si],'V';相對基址變址尋址
    inc si
    mov byte ptr [bx+10h+si],'A'
    inc si
    mov byte ptr [bx+10h+si],'X'
  • c語言描述
struct company  /*定義一個公司記錄的結構體*/
    {
        char cn[3]; /*公司名稱*/
        char hn[9]; /*總裁姓名*/
        int pm;     /*排名*/
        int sr;     /*收入*/
        char cp[3]; /*著名產品*/
    };
    
    struct compant dec={"DEC","Ken Olsen",137,40,"PDF"};
    /*定義一個公司記錄的變量,內存中將存有一條公司的記錄*/
    mian()
    {
        int i;
        dec.pm=38;
        dec.sr=dec.sr+70;
        i=0;
        dec.cp[i]='V';
        i++;
        dec.cp[i]='A';
        i++;
        dec.cp[i]='X';
        return 0;
    }
  • 按照c語言的風格用匯編寫
mov ax,seg
    mov ds,ax
    mov bx,60h;記錄首地址送入bx
    
    mov word ptr [bx].och,38;排名字段改成38
    add word ptr [bx].0eh,70;收入字段增長70
    
    ;產品名字段改成字符串'VAX'
    mov si,0
    mov byte ptr [bx].10h[si],'V'
    inc si
    mov byte ptr [bx].10h[si],'A'
    inc si
    mov byte ptr [bx].10h[si],'X'
  • 多種尋址方式爲結構化數據的處理提供了方便。
  • 通常用[bx+idata+si]的方式來訪問結構體,用idata定位結構體中的某一數據項,用si定位數組項中的每一個元素。 例如:[bx].idata、[bx].idata[si]。

8.7 div指令

  • div(divide)是除法指令,可用乘法模擬,格式爲:
div reg(寄存器)
div 內存單元。
  • 除數:8位或16位,在寄存器或內存單元中;被除數:默認放在AX或DX和AX中。
div byte ptr ds:[0]
        div byte ptr [bx+si+idata]
        ;al放商,ah放餘數
        
        div word ptr es:[0]
        div word ptr [bx+si+idata]
        ;ax放商,dx放餘數
除數 被除數
8位 16爲(AX)
16位 32位(DX高16位+AX低16位)
  • 8位或16位看的是除數。
運算 8位 16位
AL AX
餘數 AH DX
  • 利用除法指令計算10001/100編程
;被除數1001可用ax寄存器存放,除數100可用8位寄存器存放,要進行8位除法。
    mov ax,1001
    mov bl,100
    div bl
    ;執行後al的值等於0AH(10),ah的值等於1(餘數爲1)。
  • 利用除法指令計算100001/100編程
;被除數100001大於2^16=65535(FFFF),不能用ax來存放,要用dx和ax兩個寄存器聯合存放。除數小於255,可用一個8位寄存器存放,可是被除數是32位的,除數應爲16位,因此要用一個16位寄存器來存放除數。
    ;100001的十六進制爲186A1H,100001的高16位(1)存放在dx,低16位(86AH)存放在ax中。
    mov dx,1
    mov ax,86A1H
    mov bx,100
    div bx
    ;執行後ax內容等於03E8H(即1000),dx的值等於1(餘數)。

8.8 僞指令dd

  • db定義字節型數據,dw定於字型數據,dd 定於 dword(double word雙字型數據)
data segment
        db 1;第一個數據爲01h,在data:0處,佔1個字節
        dw 1;第二個數據爲0001h,在data:1處,佔1個字
        dd 1;第三個數據爲00000001h,在data:3處,佔2個字
data ends
  • 利用除法指令計算 dd 100001H 除以 dw 100,商放在 dw 0中
data segment
    dd 100001H;低16位存儲在ax中,高16位存儲在dx中
    dw 100
    dw 0
data ends

    mov ax,data
    mov ds,ax
    mov ax,ds:[0];低16位存儲在ax中
    mov dx,ds:[2];高16位存儲在dx中
    div word ptr ds:[4]
    mov ds:[6],ax

8.9 僞指令dup

  • 和db、dw、dd等數據定義僞指令配合使用,用來進行數據的重複。格式 db或者dw或者dd 重複的次數 dup (重複的數據)
  • 例如:
db 3 dup(0)
;定義了3個字節,它們的值都是0,等同於db 0,0,0。
db 3 dup(0,1,2)
;定義了9個直接,它們是0、一、二、0、一、二、0、一、2,至關於db 0、一、二、0、一、二、0、一、2
db 3 dup('abc','ABC')
;定義了18個直接,它們是'abcABCabcABCabcABC'

實驗七 尋址方式在結構化數據訪問中的應用

實驗7
實驗7數據

  • ds已經和data段聯繫了,數據段不夠用時用擴展段ES
;初始化階段
    mov ax,data
    mov ds,ax
    mov ax,table;data已經被佔用
    mov es,ax
    
    mov bx,0
    mov si,0
    mov di,0
    mov cx,21
    
    ;存放年份,每個bx就是一個字節
    mov al,[bx]
    mov es:[di],al
    mov al,[bx+1]
    mov es:[di+1],al
    mov al,[bx+2]
    mov es:[di+2],al
    mov al,[bx+3]
    mov es:[di+3],al
    
    ;存放公司的總收入
    mov ax,[bx+54H];第一個年收入是dd數據類型,段地址爲54H
    mov dx,[bx+54H]
    mov es:[di+5H],ax
    mov es:[di+7H],dx
    
    ;存放公司的人數
    mov ax,[si+0A8H];第一我的數的數據段地址爲0A8H
    mov es:[di+0A8H],ax
    
    ;計算人均收入並存放
    mov ax,[bx+54H]
    mov dx,[bx+56H];這兩句詩初始化被除數
    div word ptr,ds:[si+0A8H];除以人數
    mov es:[di+0dH],ax;將商放入指定位置
    
    ;爲下一次循環時存放數據作準備
    add bx,4;bx肯定年份和收入
    add si,2;si肯定人數
    add di,16;di肯定的是每行的列數
  • 完整的程序
assume cs:codesg,ds:data,es:table
    
data segment
        db '1975','1976' '1977' ……
        dd 16,22,382 ……
        dw 3,7,9 ……
        ;數據在題目中
data ends
    
table segment
        db 21 dup('year summ ne ?? ')
table ends
    
      start:mov ax,data
            mov ds,ax
            mov ax,table
            mov es,ax
    
            mov bx,0
            mov si,0
            mov di,0
            mov cx,21
    
          s:mov al,[bx]
            mov es:[di],al
            mov al,[bx+1]
            mov es:[di+1],al
            mov al,[bx+2]
            mov es:[di+2],al
            mov al,[bx+3]
            mov es:[di+3],al
    
            mov ax,[bx+54H]
            mov dx,[bx+56H]
            mov es:[di+5H],ax
            mov es:[di+7H],dx
    
            mov ax,[si+0A8H]
            mov es:[di+0AH],ax
    
            mov ax,[bx+54H]
            div word ptr ds:[si+0A8H]
            mov es:[di+0dH],ax
    
            add bx,4
            add si,2
        loop s
mov ax,4c00h
int 21h
codesg ends
end start

9、轉移指令的原理

引言

  • 能夠修改IP,或者同時修改CS和IP的指令統稱爲轉移指令。 簡單的來講能夠控制CPU執行內存中某處代碼的指令就是轉移指令。
  • 8086
  • CPU的轉移行爲有只修改的段內轉移(如jmp ax) 和同時修改該CS和IP的段間轉移(如jmp 1000:0)。其中段內轉移分爲短轉移(IP的修改範圍爲-128~127)和近轉移 (IP的修改範圍爲-32768~32767)。
  • 8086 CPU的轉移指令分爲如下幾類:
    • 無條件轉移指令(如:jmp)
    • 條件轉移指令
    • 循環指令(如:loop)
    • 過程
    • 中斷

9.1 操做符offset

  • offset是僞指令,由編譯器處理,它的功能是取得標號的偏移地址。
assume cs:codesg
    codesg segment
    
      start:mov ax,offset start;至關於 mov ax,偏移地址0,段地址是從0開始
          s:mov ax,offset s;至關於 mov ax,3,標記的是代碼段中的第二條指令,第一條指令長度爲3個字節,則s的偏移地址爲3
    
    codesg ends
    end start

9.2 jmp指令

  • jmp爲無條件轉移,能夠只修改IP,也能夠同時修改CS和IP。
  • jmp須要兩種信息
    • 一、轉移的目的地址;
    • 二、轉移的距離(段間轉移、段內轉移、段內近轉移)。

9.3 依據位移進行轉移的jmp指令

  • 段內短轉移,jmp short 標號 ,對IP的修改範圍是-128~127,一個字節的空間,即向前轉移最多128字節,向後最多127字節。short 代表指令進行的是短轉移,標號指明瞭指令要轉移的目的地,轉移指令結束後CS:IP指向標號處的指令
assume cs:codesg
    codesg segment
    
      start:mov ax,0
          jmp short s
          add ax,1
          s:inc ax
    
    codesg ends
    end start
  • 通常彙編指令中的當即數(idata)會出如今對應的機器指令中。而jmp指令的機器指令並不包含目的地址,包含的是相對於當前IP的轉移位移,CPU並不須要目的地址就能夠實現對IP的修改。
    92debug_1

  • CPU執行指令的過程 在 2.10 CS和IP
  • jmp short s 指令的讀取和執行過程:
    • 一、CS:IP指向jmp short s 的機器碼;
    • 二、讀取指令碼進入指令緩衝器
    • 三、 改變IP,(IP)=(IP)+所讀取指令的長度,IP指向下一個指令;
    • 四、CPU執行指令緩衝器中的指令;
    • 五、執行後CS:IP繼續指向下一個指令
  • jmp short 標號的功能爲(IP)=(IP)+8位位移。
    • 一、8位爲=標號處的地址-jmp指令後的第一個字節的地址;
    • 二、short 指明此處的位移爲8位;
    • 三、8位位移的範圍爲-128~127,用補碼錶示。
    • 四、8位位移由編譯程序在編譯時算出的。
  • jmp near ptr 標號 指令實現段內近轉移,功能爲(IP)=(IP)+16位位移。
    • 一、16位爲=標號處的地址-jmp指令後的第一個字節的地址;
    • 二、nearptr 指明此處的位移爲16位;
    • 三、16位位移的範圍爲-32769~32767,用補碼錶示。
    • 四、16位位移由編譯程序在編譯時算出的。

轉移位移的計算方法

9.4 轉移的目的地址在指令中的jmp指令

  • jmp far ptr 段間轉移,又稱爲遠轉移
  • jmp far ptr 標號的功能:
    • (CS)=標號所在段的段地址;
    • (IP)=標號所在段總的偏移地址;
    • far ptr 指明瞭指令用標號的段地址和偏移地址修改CS和IP。
    assume cs:codesg
          codesg segment
            start:mov ax,0
                mov bx,0
                jmp far ptr s
                db 256 dup(0)
              s:add ax,1
                  inc ax
          codesg ends
          end start
  • 機器碼中包含了轉移的目的地址。

94debug

附註3 彙編編譯器(masm.exe)對jmp的相關處理

這裏寫圖片描述

9.5 轉移地址在寄存器中的jmp指令

  • jmp 16位寄存器,功能是16位寄存器賦值給IP,實現段內的近(短)轉移。
  • 參考 2.11 修改CS、IP的指令

9.6 轉移地址在內存中的jmp指令

  • 轉移地址在內存中的jmp指令有兩種格式:
    • 一、jmp word ptr內存單元地址(16位只能實現段內轉移)。 功能是從內存單元地址處開始存放一個字(轉移的目的偏移地址),內存單元地址可用尋址方式的格式給出。
    mov ax,0123H
        mov ds:[0],ax
        jmp word ptr ds:[0]
        ;至關於 jmp ax,執行後(IP)=0123h
    
        mov ax,0123H
        mov [bx],ax
        jmp word ptr [bx]
        ;執行後(IP)=0123h
    • 二、jmp dword ptr 內存單元地址(段間轉移)。 功能:從內存單元地址處開始存放兩個字型數據,高地址是轉移的目的段地址,低地址處是轉移的目的偏移地址。(CS)=(內存單元地址+2),(IP)=(內存單元地址),內存單元地址可用尋址方式的任一格式給出。
    mov ax,0123H
        mov ds:[0],ax
        mov word ptr ds:[2],0
        jmp dword ptr ds:[0]
    
        mov ax,0123H
        mov [dx],ax
        mov word ptr [bx+2],0
        jmp dword ptr [bx]
    
        ;執行後 (CS)=0,(IP)=0123H CS:IP指向0000:0123

檢測點 9.1

檢測點 9.1

9.7 jcxz指令

  • 指令格式爲jcxz 標號,若是cx的值爲0,則轉移到標號處執行,不爲0則向下執行。
    • 當cx的值爲0時,(IP)=(IP)+8位位移,8位位移=標號處的地址-jcxz指令後的第一個字節的地址。
    • 8位位移的範圍是-128~127,用補碼錶。
    • 8位位移由編譯器在編譯時算出。
  • jcxz指令是有條件轉移指令,全部的條件轉移指令都是短指令,在對應的機器碼中包含轉移的位移而不包含目的地址,對IP的修改範圍都爲-128-127。

檢測點 9.2

檢測點9.2_1

9.8 loop指令

  • loop指令爲循環指令,全部的循環指令都是短轉移,在對應的機器碼中包含轉移的位移而不包含目的地址。操做i:
    • cx先自減1;
    • 當cx的值不爲0時,(IP)=(IP)+8位位移,8位位移=標號處的地址-loop指令後的第一個字節的地址。
    • 8位位移的範圍是-128~127,用補碼錶。
    • 8位位移由編譯器在編譯時算出。

檢測點 9.3

檢測點 9.3

9.9 根據位移進行轉移的意義

jmp short 標號
    jmp near ptr 標號
    jcxz 標號
    loop 標號
  • 它們對IP的修改時根據轉移目的地址和轉移起始地址自檢的位移來進行的。在它們對應的機器碼中不包含轉移的目的地址,而包含的是目的地址的位移距離。方便了程序段在內存中的浮動分配,沒有固定目的地址的限制,更靈活。

9.10 編譯器對轉移位移超界的檢測

  • 根據位移進行轉移的指令,它們的轉移範圍受到了轉移位移的限制,若是在源程序中出現了轉移範圍超界的問題,在編譯時編譯器會報錯。
assume cs:code
    
    code segment
    start:  jmp short s
          db 128 dup(0)
          s:mov ax,0FFFFH
    code ends
end start

910err

910err2

實驗8

  • 實驗八能夠正常退出
    test8
assume cs:codesg
codesg segment
            mov ax,4c00h
            int 21h
    
      start:mov ax,0
          s:nop
            nop;nop佔用兩個字節,不執行任何操做
    
            mov di,offset s
            mov si,offset s2
            mov ax,cs:[si];jmp short s1的機器碼給了ax
            mov cs:[di],ax;覆蓋到指令 s:nop nop那
    
         s0:jmp short s;s那已經被jmp short s1機器碼覆蓋
         s1:mov ax,0
            int 21h
            mov ax,0
         s2:jmp short s1;jmp -8h,向上跳到s1,s1又向上跳-10字節
            nop
codesg ends
end start

test8debug

實驗9

實驗9

assume cs:code,ds:data,ss:stack
    
data segment
            db'welcome to masm!';定義要顯示的字符串(共16字節)
            db 02H,24H,71H;定義字符的屬性
data ends
    
stack segment
            dw 8 dup(0)
stack ends
    
code segment
    
    start:
            mov ax,data
            mov ds,ax
            mov ax,stack
            mov ss,ax
            mov sp,10H
    
            xor bx,bx;bx清零,用來索引顏色
            mov ax,0b872H;算出屏幕第12行中間的顯存的段起始位置放入ax中
    
            mov cx,3;s3循環控制行數,要顯示三個字符串外循環爲3次
        s3: push cx;三個進棧操做爲外循環s3保存相關寄存器的值
            push ax;以防止它們的值在內循環中被破壞
            push bx
    
            mov es,ax;此時es爲屏幕第12行中間的顯存的段起始位置
    
            mov si,0;si用來索引代碼列的字符
            mov di,0;di用來定位目標列
    
            mov cx,10H
            ;s1循環控制存放的字符,一個字符串中含有10H個字節內循環爲10H次
        s1: mov al,ds:[si]
            mov es:[di],al
            inc si
            add id,2
            loop s1;吃循環實現偶地址中存放字符
    
            mov di,1;設置di的值爲1,爲在顯存奇數地址中存放字符的顏色屬性作準備
            pop bx
            mov al.ds:[bx+10H];取消顏色屬性
            inc bx
    
            mov cx,10H;第二個內循環也爲10H
        s2: mov es:[di],al
            add di 2
            loop s2;此循環實現奇數地址存放字符的顏色屬性
    
            ;如下4句爲下一趟外循環作準備
            pop ax
            add ax,0AH;將顯存的段地址起始地址設置爲當前行的下一行
                      ;[在段地址中甲0aH,至關於在偏移地址中加了0a0h(=160d)]
            pop cx
            loop s3
    
            mov ax,4C00H
            int 21H
            
code ends
end start
  • welcome to masm

welcome to masm

10、CALL和RET指令

引言

  • 回想程序之間的加載返回過程。
  • call和ret指令都是轉移指令,它們都修改IP或者同時修改CS和IP,常常被共用來實現程序的設計。
  • 這一章講解call和ret指令的原理。

10.1 ret和retf指令

  • ret指令用棧中的數據來修改IP的內容,從而實現近轉移。
  • CPU執行ret指令時:
    • 一、(IP)=((SS)*16+(SP)),指向棧頂
    • 二、(SP)=(SP)+2
  • retf指令用棧中的數據,修改CS和IP的內容,從而實現遠轉移。
  • CPU執行retf指令時,進行下面兩步操做:
    • 一、(IP)=((SS)*16+(SP))
    • 二、(SP)=(SP)+2
    • 三、(CS)=((SS)*16+(SP))
    • 四、(SP)=(SP)+2
  • 用匯編的語法來解釋ret和retf指令:
    • CPU執行ret指令至關於進行 POP IP
    • CPU執行retf指令至關於進行 POP IP和POP CS
assume cs:codesg
          
 stack segment
              db 16 dup(0)
stack ends
          
          codesg segment
              mov ax,4c00h
              int 21h
          
          start:
              mov ax,stack
              mov ss,ax
              mov sp,16
              mov ax,0
              
              push ax
              
              mov bx,0
              ret
codesg ends
          
end start

1012debug_1

assume cs:codesg
    
stack segment
        db 16 dup(0)
stack ends
    
codesg segment
        mov ax,4c00h
        int 21h
    
    start:
        mov ax,stack
        mov ss,ax
        mov sp,16
        mov ax,0
        push cs
        push ax
    
        
        mov bx,0
        retf
codesg ends
    
end start

檢測點 10.1

檢測點10.1

10.2 call指令

  • call指令常常跟ret指令配合使用,CPU執行call指令時:
    • 一、將當前的IP或者CS和IP壓入棧;
    • 二、轉移(jmp)。
  • call指令除了不能實現短轉移以外,call指令實現轉移的方法和jmp指令的原理相同。call指令實現段間的轉移(遠轉移)或近轉移。

10.3 依據位移進行轉移的call指令

  • call標號(將當前的IP壓入棧後轉到目標處執行指令),執行時進行如下操做:
    • 一、(SP)=(SP)-2
      ((SS)*16+(SP))=(IP)
    • 二、(IP)=(IP)+16位位移;
    • 三、16位位移=標號處的地址減去call指令後的第一個字節的地址。16位位移的範圍是-32768~32767,用補碼錶示。16位位移由編譯器編譯時算出。
  • 用匯編語法解釋call指令:
push IP
jmp near 標號

檢測點 10.2

檢測點10.2

10.4 轉移的目的地址在指令中的call指令

  • call far ptr 標號 實現的是段間轉移,執行時:
    • 一、CS先自減2;
    • 二、CS的值等於SS的值乘以16加上SP的值,SP自減2,IP的值等於SS的值*16加上SP的值;
    • 三、CS的值等於標號所在的段地址,IP的值等於標號所在的偏移地址.
  • 用匯編語法解釋call指令:
push CS
push IP
jmp far ptr 標號

檢測點 10.3

檢測點 10.3

10.5 轉移地址在寄存器中的call指令

  • 指令格式是:call 16位寄存器,功能是:
    • 一、SP的值先自減2;
    • 二、IP的值SS的值乘以16再加上SP的值;
    • 三、 IP的值等於16位寄存器的內容。
  • 用匯編語法解釋此種call指令,CPU執行call 16位reg時,至關於:
push IP
          jmp 16位寄存器

檢測點 10.4

檢測點 10.4

10.6 轉移地址在內存中的call指令

  • 轉移地址在內存中的call指令有兩種格式
call word ptr 內存單元地址;段內跳轉
       call dword ptr 內存單元地址;段間跳轉
  • 用匯編語法解釋call word ptr 內存單元地址
push IP
       jmp word ptr 內存單元地址
  • 例子:
mov sp,10h
       mov ax,0123H
       mov ds:[0],ax
       call word ptr ds:[0]
       ;執行後IP的值等於0123H,SP的值等於0EH
  • 用匯編語法解釋call dword ptr 內存單元地址
push CS
       push IP
       jmp word ptr 內存單元地址
  • 例子:
mov sp,10h
       mov ax,0123H
       mov ds:[0],ax
       mov word ptr ds:[0],0
       call dword ptr ds:[0]
       ;執行後IP的值等於0123H,SP的值等於0CH,CS的值等於0

檢測點 10.5

檢測點10.5

10.7 call和ret的配合使用

  • 下面的程序返回前,bx中的值是多少?
assume cs:code
code segment
      start:
            mov ax,1
            mov cx,3
            call s
            mov bx,ax
            mov ax,4c00h
            int 21h
          s:
            add ax,ax
            loop s
            ret
code ends
end start
  • 具備必定功能的程序段稱爲子程序,用call轉去執行,在子程序後面使用ret實現返回。
  • 具備子程序的源程序的框架以下

107call

10.8 mull指令

  • mull指令時乘法指令,相乘的兩個數要麼都是8位的,要麼都是16位的
    • 8位:在AL中和8位寄存器中或內存字節單元中;
    • 16位:在AX中和16位寄存器或內存字單元中。
    • 結果
      • 8位的存放在AX中;
      • 16位:DX(高位)和AX(低位)中。
mull reg
              
mull 內存單元
mull byte ptr ds:[0]

mull word ptr [bx+si+idata]
;(ax)=(ax)*((ds)*16+(bx)+(si)+idata)
;(dx)=(ax)*((ds)*16+(bx)+(si)+idata)
;計算100*10,兩個數都小於255,能夠作8位乘法
mov ax,100
mov bx,10
mull bl
;結果(ax)=1000(03E8H)
              
;計算100*1000,1000都大於255,要作16位乘法
mov ax,100;高位自動補零
mov bx,10000
mull bx
;結果(ax)=4240H,(dx)=000FH,F4240H=1000000

10.9 模塊化程序設計

  • cal和ret指令共同支持彙編語言編程中的模塊化設計。

10.10 參數和結果傳遞的問題

  • 用寄存器來存儲參數和結果是最經常使用的方法。對於存放參數的寄存器和存放結果的寄存器,調用者和子程序的讀寫操做偏偏相反:
    • 調用者將參數送入參數寄存器,從結果寄存器中取到返回值;
    • 子程序 從參數寄存器中取到參數,將返回值送入結果寄存器。
  • 編程:根據提供的N來計算N^3
cube:mov ax,bx
               mul bx
               mul bx
               ret
  • 編程:計算data段中第一組數據的3次方,結果保存在後面一組dword單元中
assume cs:code
          
data segment
          dw 1,2,3,4,5,6,7,8
          dd 8 dup (0)
data ends
          
code segment
          start:
              mov ax,data
              mov ds,ax
              mov si,0;ds:si指向第一組word單元
              mov di,16;ds:di指向第二組dword單元
              
              mov cx,8
          s:  mov bx,[si]
              call cube
              mov [di],ax
              mov [di+2],dx
              add si,2;ds:di指向下一個word單元
              add di,4;ds:di指向下一個dword單元
              loop s
              
              mov ax,4c00h
              int 21h
                  
             cube:mov ax,bx
              mul bx
              mul bx
              ret
code ends
end start

10.11 批量數據的傳遞

  • 將批量數據放在內存中,而後將他們呢所在內存空間的首地址放在寄存器中,傳遞給須要的子程序,批量數據的返回結果也是採用一樣的方法。除此以外還能夠用棧來傳遞參數。
assume cs:code
    
data segment
    db'conversation'
data ends
    
    start:
        mov ax,data
        mov ds,ax
        mov si,0;ds:si指向字符串(批量數據)所在空間的首地址
    
        mov cx,12;cx存放字符串的長度
        call capital
    
        mov ax,4c00h
        int 21h
    
    capital:
        add byte ptr [si],11011111B
        inc si
        loop capital
        ret
code ends

10.12 寄存器衝突的問題

  • 編程:將一個全是字母,以0結尾的字符串轉化爲大寫
capital:
        mov cl,[si];低8位
        mov ch,0;高8位設置爲0
        jcxz ok;若是(cx)=0則結束,若是不是0則處理
        and byte ptr [si],11011111B
        inc si
        jmp short capital
    ok:
        ret
  • 編程將data段中的字符串所有轉化爲大寫
assume cs:code
    data segment
    db'word',0
    db'unix',0
    db'wind',0
    db'good',0
    data ends
;此程序有bug,cx有問題
    assume cs:code
    
    data segment
    db'word',0
    db'unix',0
    db'wind',0
    db'good',0
    data ends
    
    code segment
    start:
            mov ax,data
            mov ds,ax
            mov bx,0
    
            mov cx,4
        s:
            mov si,bx
            call capital
            add bx,5
            loop s
    
            mov ax,4c00h
            int 21h
    
    capital:
            mov cl,[si]
            mov ch,0
            jcxz ok
            and byte ptr [si],11011111b
            inc si
            jmp short capital
        ok:
            ret
    
    code ends
    end start

實驗十

test10_1_1
test10_1_2
test10_1_3

  • 實驗10.1 顯示字符串
assume cs:code
    
data segment
    db 'welcome to masm!',0
data ends
    
code segment
    start:
            mov dh,8;行號
            mov dl,3;列號
            mov cl,2;顏色屬性
            mov ax,data
            mov ds,ax
            mov si,0
            call show_str
    
            mov ax,4c00h
            int 21h
        show_str:;子程序
            push cx
            push si
    
            mov al,0A0h;每行有80*2=160個字節=0a0h
            dec dh;行號在顯存中下標從0開始,因此減1
            mul dh;至關於從第(n-1)*0a0h個byte單元開始
    
            mov bx,ax;定位好的位置偏移地址存放在bx裏(行)
    
            mov al,2;每一個字符佔2個字節
            mul dl;定位列,結果ax存放的是定位好的列的位置
            sub ax,2;列號在顯存中下標從0開始,又由於是偶字節存放字符,因此減2
    
            add bx,ax;此時bx中存放的是行與列的偏移地址
    
            mov ax,0B800h;顯存開始的地方
            mov es,ax;es中存放的是顯存的第0頁的起始地段地址
    
            mov di,0;di指向顯存的偏移地址,肯定指向下一個要處理的字符的位置
            mov al,cl;cl存放顏色參數,下邊cl要用來臨時存放要處理的字符
            mov ch,0;下邊cx存放的是每次準備處理的字符
        s:
            mov cl,ds:[si];指向'welcome to masm ',0
    
            jcxz ok;cl爲0時跳轉
            mov es:[bx+di],cl;偶地址存放字符
            mov es:[bx+di+1],al;奇地址存放字符的顏色屬性
            inc si
            add di,2;指向了下個字符
            jmp short s ;無條件跳轉,jcxz是離開的關鍵跳
    
        ok:
            pop si
            pop cx
            ret;定義結束
code ends
end start

test101

  • 實驗10.2
assume cs:code,ss:stack
    
    stack segment
    dw 8 dup(0)
    stack ends
    
    code segment
    
    start:
            mov ax,stack
            mov ss,ax
            mov sp,10h
            mov ax,4240h
            mov dx,0fh
            mov xx,0ah
    
            call divdw
    
            mov ax,4c00h
            int 21h
    
        divdw:
            push ax;低16位先保存
            mov ax,dx;ax這時是高16位了
            mov dx,0;爲了避免影響餘數位和高位數
            div cx
            mov bx,ax
            pop ax
            div cx
            mov cx,dx
            mov dx,dx
            ret
    code ends
    end start
  • 實驗10.3
assume cs:code,ds:data
    
data segment
    db 10 dup(0)
data ends
    
code segment
    start:
            mov ax,12666
            mov bx,data;指向字符串的首地址
            mov ds,bx
            mov si,0
    
            call dtoc;實現將word型整數轉化爲字符串並存儲
    
            mov dh,8;打印初始化
            mov dl,3
            mov cl,0cah
    
            call show_str;開始打印字符串
    
            mov ax,4c00h
            int 21h
        dtoc:
            push dx
            push cx
            push ax
            push si
    
            mov bx,0;bx在子程序中用來存放位數,用棧來臨時存放修改後的字符
        s1:
            mov cx,10d;d表示十進制,cx準備被除,用取餘法來取出數字
            mov dx,0
    
            div cx;除以十
            mov cx,ax;獲得的商複製給cx,要利用jcxz
            jcxz s2;當商爲0則跳到s2
            add dx,30h;餘數加上30h獲得相應的ascii碼
            push dx
            inc bx
    
            jmp short s1
        s2:
            add ax,30h;當商爲0的時候,餘數爲個位
            push dx
    
            inc bx;再進行一次棧操做(補充當商爲零而餘數不爲零時的狀況)
            mov cx,bx;總共有bx位進棧,因此循環次數爲bx
            mov si,0
        s3:
            pop ax;s3實現將棧中的數據依次出棧放到指定的內存中
            mov [si],al
            inc si
            loop s3
    
        okay:
            pop bx
            pop si
            pop ax
            pop dx
    
            ret
        show_str:;子程序
            push bx
            push cx
            push si
    
            mov al,0A0h;每行有80*2=160個字節=0a0h
            dec dh;行號在顯存中下標從0開始,因此減1
            mul dh;至關於從第(n-1)*0a0h個byte單元開始
    
            mov bx,ax;定位好的位置偏移地址存放在bx裏(行)
            mov al,2;每一個字符佔2個字節
            mul dl;定位列,結果ax存放的是定位好的列的位置
    
            sub ax,2;列號在顯存中下標從0開始,又由於是偶字節存放字符,因此減2
            add bx,ax;此時bx中存放的是行與列的偏移地址
    
            mov ax,0B800h;顯存開始的地方
            mov es,ax;es中存放的是顯存的第0頁的起始地段地址
    
            mov di,0;di指向顯存的偏移地址,肯定指向下一個要處理的字符的位置
            mov al,cl;cl存放顏色參數,下邊cl要用來臨時存放要處理的字符
            mov ch,0;下邊cx存放的是每次準備處理的字符
        S:
            mov cl,ds:[si]
    
            jcxz ok
    
            mov es:[bx+di],cl
            mov es:[bx+di+i],al
    
            inc si
            add di,2
    
            jmp short s
    
        ok:
            pop si
            pop cx
            pop bx
            ret
code ends
end start

11、標誌寄存器

引言

  • CPU內部的寄存器中有一種特殊的寄存器:
    • 一、用來存儲相關指令的某些執行結果;
    • 二、用來爲CPU執行相關指令提供行爲依據;
    • 三、用來控制CPU的相關工做方式。
  • 8086 CPU的標誌寄存器只有16位,其中存儲的信息一般被稱爲程序狀態字(PSW)。
  • 本章中的標誌寄存器(如下簡稱爲flag)。某些指令將影響標誌寄存器中的多個標誌位,這些被影響的標記位比較全面地記錄ill指令的執行結果,爲相關的處理提供了所需的依據。
  • flag寄存器是按位起做用的,每一位都有專門的含義,記錄特定的信息,與其餘寄存器不同。
  • 8086 CPU的flag寄存器的結構:

flag

  • flag的一、三、五、十二、1三、1四、15位在8086 CPU中沒有使用,而0、二、四、六、七、八、九、十、11位都具備特殊的含義。

11.1 ZF(zero flag)標誌

  • flag的第6位是ZF,零標誌位,它記錄相關指令執行後,結果爲0,ZF=1(記錄下是0這樣的確定信息),結果不爲0,ZF=0(表示結果非0)。
mov ax,1
    sub ax,1
    
    mov ax,1
    and ax,0
    ;指令執行後,結果爲0,則ZF=1
    
    mov ax,2
    sub ax,1
    
    mov ax,1
    or ax,0
    ;指令執行後,結果爲1,則ZF=0
  • 在8086CPU中,add、sub、mul、div、inc、or、and等它們大多都是運算(邏輯運算或是算術運算)指令,是影響標誌寄存器的,而mov、push、pop等傳送指令對標誌寄存器通常沒有影響,由於不會產生結果。

11.2 PF標誌

  • flag的第2位是PF,奇偶標誌位,記錄指令執行後結果全部的二進制位中1的個數。爲偶數,PF=1,爲奇數PF=0
mov al,1
    add al,10
    ;執行結果爲00001011B,有3個1,則PF=0

    mov al,1
    or al,10
    ;執行後結果爲00000011B,有2個1,則PF=1

11.3 SF(sign flag)標誌

  • flag的第7位是SF符號標誌位,記錄指令執行後結果爲負則SF=1,結果爲正,SF=0。弱國咱們將數據看成無符號數來運算,SF的值沒有意義,雖然相關的指令影響了它的值。
  • 有符號數與補碼
    • 計算機默認把負數用補碼記錄。
    • 00000001B,能夠看做無符號數1,也能夠看做符號數+1;
    • 10000001B,能夠看做無符號數129,也能夠看做有符號數-127。
  • 補碼
mov al,10000001B
add al,1
;執行指令後al的值是10000010B,無符號數130,有符號數-126

檢測點 11.1

檢測點11.1

11.4 CF(carry flag)標誌

  • flag的第0位是CF,進位標誌位。通常狀況下,在進行無符號運算的時候,它記錄了運算結果的最高有效位向更高位的進位值或從更高位的借位值。對於位數爲N的無符號數,其對應的二進制信息的最高位爲N-1位的最高有效位,假想存在第N位。
    更高位

  • 兩個8位的數據運算可能產生進位或者借位,因爲這個進位值在8位數中沒法保存,8086CPU就用flag的CF位來記錄這個進位值。
mov al.98h
    add al,al;執行後(al)=30h,cf=1,cf記錄了從最高有效位向更高位的進位值
    add al,al;執行後(al)=60h,cf=0,cf記錄了從更高有效位向更高位的進位值
    
    mov al,97h
    sub al,98h;執行後(al)=ffh,cf=1,cf記錄了向更高位的借位值
    sub al,al;執行後(al)=0,cf=0,cf記錄了向更高位的借位值

11.5 OF(overflow flag)標誌

  • 若是運算結果超出了機器所能表達的範圍(對於8位有符號數,機器所能表達的範圍是-128~127)將產生溢出,對有符號數而言。
assume cs:code
    
    code segment
    
    start:
            mov al,01100010b
            add al,01100011b
    
            mov ax,4c00h
            int 21h
    code ends
end start

1150f_1

assume cs:code
    
    code segment
    
    start:
            mov al,10001000b
            add al,11110000b
    
            mov ax,4c00h
            int 21h
    code ends
    end start

115of2

assume cs:code

    code segment
    start:
            mov al,98h
            add al,al
            add al,al
    
            mov ax,4c00h
            int 21h
    code ends
end start

CFdebug

assume cs:code

    code segment
    start:
            mov al,97h
            sub al,98h
            add al,al
    
            mov ax,4c00h
            int 21h
    code ends
end start

114cf2

  • CF是對無符號數運算有意義的標誌位,而OF是對有符號數運算有意義的標誌位; CPU用CF位來記錄無符號數運算是否產生了進位,用OF位來記錄有符號數是否產生了溢出。用SF位來記錄結果的符號
mov al,98d
    add al,99d
    ;對於無符號數運算,98+99沒有進位,CF=0
    ;對於有符號數運算,98+99發生溢出,OF=1

檢測點 11.2

檢測點11.2

11.6 adc指令

  • adc是帶有進位加法指令,利用了CF位上記錄的進位值。格式:adc操做對象1,操做對象2,功能:操做對象1=操做對象1+操做對象2+CF。
mov ax,2
    mov bx,1
    sub bx,ax
    adx ax,1
    ;執行後 (ax)=4,至關於計算(ax)+1+CF=2+1+1+4
    
    mov ax,1
    add ax,ax
    adc ax,3
    ;執行後(ax)=5,至關於執行(ax)+3+CF=2+3+0=5
    
    mov al,98H
    add al,al
    adx al,3
    ;執行後 (ax)=34H,至關於執行(ax)+3+CF=30H+3+1=34H
  • 由adc指令前面的指令決定在執行adc指令的時候加上的CF的值的含義,關鍵在於所加上的CF的值是被什麼指令設置的。若是CF的值是被sub指令設置的,那麼它的含義就是借位值;若是是被add指令設置的,那麼它的含義就是進位值。加法運算先是低位相加,再高位相加加上低位相加產生的進位值。
  • 編程:計算1EF000H+201000H,結果存放在AX(高16位)和BX(低16位)中。
mov ax,001EH
    mov bx,0F000H
    add bx,1000H
    adc ax,0020H
  • 編程:1EF0001000H+2010001EF0H,結果存放在AX(高16位)、BX(次16位)中和cx(低16位)。
mov ax,001EH
    mov bx,0F000H
    mov cx,1000H
    add cx,1EF0H
    add bx,1000H
    adc ax,0020H
  • 編程:對兩個128位數據進行相加
assume cs:code,ds:data
    data segment
            db 16 dup(88H)
            db 16 dup(11H)
    data ends
    
    code segment
    start:
            mov ax,data
            mov ds,ax
            mov si,0
            mov di,16
    
            mov cx,8
            call add128
    
            mov ax,4C00H
            int 21H
    add128:
            push ax
            push cx
            push si
            push di
    
            sub ax,ax;將CF設置爲0
        s:
            mov ax,[si]
            adc ax,[di]
            mov [si],ax
            inc si;不能用add si,2代替
            inc si;由於會影響cf位
            inc di;而loop和inc不會影響
            inc di
    
            loop s
    
             pop di
             pop si
             pop cx
             pop ax
             ret
    code ends
end start

11.7 sbb指令

  • sbb是帶借位減法指令,利用了CF位上記錄的借位值。格式:sbb 操做對象1,操做對象2,功能是:操做對象1=操做對象1-操做對象2-CF。
  • 利用sbb指令咱們能夠對任意大的數據進行減法運算。sbb和adc是基於一樣的思想設計的兩條指令,在應用思路上sbb和adc相似。
  • 編程:計算003E1000H-00202000H,結果放在ax、bx中
mov bx,1000H
    mov ax,003EH
    sbb bx,2000H
    sbb ax,0020H

11.8 cmp指令

  • cmp是比較指令,功能上至關於減法指令,只是不保存結果。格式:cmp 操做對象1,操做對象2.功能:計算操做對象1-操做對象2但不保存結果,僅僅是根據計算結果對標誌寄存器進行設置。
  • cmp指令運算執行後經過作減法將對標誌寄存器產生影響,其餘相關指令經過識別這些被影響的標誌寄存器位來得知比較結果。
cmp ax,ax
    ;執行後結果爲0,ZF=1,PF=1,SF=0,CF=0,OF=0

    mov ax,8
    mov bx,3
    cmp ax,bx
    ;執行後ax、bx的值不變,ZF=0,PF=1,SF=0,CF=0,OF=0
cmp ax,bx

118cmp_1

118cmp2

  • CPU在執行cmp指令時也包含了對無符號數運算和進行有符號數運算,因此利用cmp指令能夠對無符號數進行比較也能夠對有符號數進行比較。
  • 單純地考察SF的值不可能知道結果的正負。由於SF記錄的只是能夠在計算機中存放的相應位數的結果的正負(例如:add ah, al執行後,SF記錄的是ah中的8位二進制信息所表示的數據的正負)。若是沒有溢出發生的話,實際結果的正負和邏輯上真正結果的正負就一致了。。例如:22H(34)-0A0H(-96)=130=82H(是-126的補碼),SF=1。
    • 一、若是SF=1或SF=0,OF=0,邏輯上真正結果的正負=實際結果的正負。
    • 二、若是SF=1或SF=0,OF=1,邏輯上真正結果的負正=實際結果的正負。

11.9 檢測比較結果的條件轉移指令

  • 與cmp相配使用,根據cmp指令的比較結果(cmp指令執行後相關標誌位的值)進行工做的指令。
  • cmp指令能夠同時進行兩種比較,無符號數比較和有符號數比較,因此根據cmp指令的比較結果進行轉移的指令也分爲兩種:
    • 根據無符號數的比較結果進行轉移的條件轉移指令,它們檢測ZF、CF的值;
    • 根據有符號數的比較結果進行轉移的條件轉移指令,它們檢測SF、OF、ZF的值。
  • 它們所檢測的標誌位都是cmp指令進行無符號數比較時候記錄比較結果的標誌位。
指令 含義 檢測的相關標誌位
je 等於則轉移 ZF=1
jne 不等於則轉移 ZF=0
jb 低於則轉移 CF=1
jnb 不低於則轉移 CF=0
ja 高於則轉移 CF=0 and ZF=0
jna 不高於則轉移 CF=1 or ZF=1


j e ne b nb a na
jump equal not equal below not below above not above
  • 編程:若是ah的值等於bh則ah的值等於ah的值加ah的值,不然ah的值等於ah的值加上bh的值。
cmp ah,bh
        je s;ZF=1則跳轉
        add ah,bh
        jmp short ok
    s:
        add ah,bh
    ok:ret
  • je檢測的是ZF的位置,無論je前面是什麼指令,只要CPU執行je指令時,ZF=1那麼就發生轉移。
mov ax,0
        mov ax,0
        je s
        inc ax
    s:
        inc ax
    ;執行後ax的值等於1,add ax,0使得ZF=1,因此je指令將進行轉移。
  • 課堂練習

  • 編程:統計data段中數值爲8的字節的個數,用ax保存統計結果。
;方案一
assume cs:code
    
    data segment
            db 8,11,8,1,8,5,63,38
    data ends
    
    code segment
    start:
            mov ax,data
            mov ds,ax
            mov bx,0;ds:bx指向第一個字節
            mov ax,0;初始化累加器
            mov cx,0
    
        s:
            cmp byte ptr [bx],8;和8進行比較
            jne next;若是不相等轉到next,繼續循環
            inc ax;若是相等就計數值加1
    
        next:
            inc bx
            loop s;執行後:(ax)=3
    
            mov ax,4c00h
            int 21h
    code ends
end segment


;方案二
assume cs:code
    
    data segment
            db 8,11,8,1,8,5,63,38
    data ends
    
    code segment
    start:
            mov ax,data
            mov ds,ax
            mov bx,0;ds:bx指向第一個字節
            mov ax,0;初始化累加器
            mov cx,0
    
        s:
            cmp byte ptr [bx],8;和8進行比較
            je ok;若是不相等轉到ok,繼續循環
            jmp short next;若是不想等就轉到next,繼續循環
    
        ok:
            inc ax;若是相等就計數值加1
    
        next:
            inc bx
            loop s;執行後:(ax)=3
    
            mov ax,4c00h
            int 21h
    code ends
end segment
  • 編程:統計data段中數值大於8的字節的個數,用ax保存統計結果。
assume cs:code
    
    data segment
            db 8,11,8,1,8,5,63,38
    data ends
    
    code segment
    start:
            mov ax,data
            mov ds,ax
            mov bx,0;ds:bx指向第一個字節
            mov ax,0;初始化累加器
            mov cx,0
    
        s:
            cmp byte ptr [bx],8;和8進行比較
            jna next;若是大於8轉到next,繼續循環
            inc ax;若是大於就計數值加1
    
        next:
            inc bx
            loop s;執行後:(ax)=3
    
            mov ax,4c00h
            int 21h
    code ends
end segment

檢測點 11.3

檢測點11.3

11.10 DF(direction flag)標誌和串傳送指令

  • flag的第10位是DF,方向標誌位,在串處理指令中,控制每次操做後si(通常指向原始偏移地址)、di(通常指向目標偏移地址)的增減。
    • DF=0:每次操做後si、di遞增;
    • DF=1,每次操做後so、di遞減。
  • movsb(mov string byte)串傳送指令,以字節爲單位傳送。將ds:si指向的內存單元中的字節送入es:di中,而後根據標誌寄存器DF位的值將si和di遞增1或遞減1。movsw,以字爲單位傳送。將ds:si指向的內存單元中的字送入es:di中,而後根據標誌寄存器DF位的值將si和di遞增2或遞減2。
  • movsb和movsw進行的是串傳送操做中的一個步驟,通常和rep配合使用,格式:rep movsb,rep的做用是根據cx 的值,重複執行後面的串傳送指令。因爲每次執行一次movsb指令si和di都會遞增或遞減指向後一個單元或前個單元,則rep movsb就能夠循環實現(cx)個字符的傳送。
    • 一、傳送的原始位置;
    • 二、傳送的目的位置;
    • 三、傳送的長度;
    • 四、傳送的方向。
    • movsb功能:((es)16+(di))=((ds)16+(si)),若是DF=0,則(si)=(si)+1,(di)=(di)+1;若是DF=1,則(si)=(si)-1,(di)=(di)-1。
  • 因爲flag的DF位決定着串傳送指令執行後,si和di改變的方向,8086CPU提供兩條指令對DF位進行設置:
    • cld指令:將標誌寄存器的DF位設置爲0;
    • std指令:將標誌寄存器的DF位設置爲1。

11.11 pushf和popf

  • pushf的功能 是件標誌寄存器的值壓棧,popf是從棧中彈出數據m,送入標誌寄存器中。pushf和popf爲直接訪問標誌寄存器提供了一種方法。
;下面的程序執行後ax的值是多少?
    mov ax,0
    push ax
    popf
    mov ax,0fff0h
    add ax,0010h
    pushf
    pop ax 
    and al,11000101b
    and ah 00001000b
  • 編程:用串傳送指令將data段總的第一個字符串複製到它後面的空間中。
assume cs:code
    
    data segment
            db'welcome to masm!'
            db 16 dup(0)
    data ends
    
    code segment
    start:
            mov ax,data
            mov ds,ax
            mov si,0;指向data:0
            mov es,ax
            mov di,16;指向data:16
            mov cx,16;rep循環16次
    
            cld;設置DF=0,正向傳送
            rep movsb
    
            mov ax,4c00h
            int 21h
    code ends
end start

1110

  • 用串傳送指令將F00H段中的最後16個字符複製到data段中
assume cs:code
    
    data segment
            db 16 dup(0)
    data ends
    
    code segment
    start:
            mov ax,0f00h
            mov ds,ax
            mov si,0ffffh;指向f0000:ffff
            mov ax,data
            mov es,ax
            mov di,16;指向data:15
            mov cx,16;rep循環16次
    
            std;設置DF=1,逆向傳送
            rep movsb
    
            mov ax,4c00h
            int 21h
    code ends
end start

檢測點 11.4

檢測點11.4

11.12 標誌寄存器在Debug中的表示

debugflag

標誌 值爲1的標記 值爲0的標記
OF OV NV
SF NG PL
ZF ZR NZ
PF PE PO
CF CY NC
DF DN UP

12、內中斷

引言

  • 中斷時CPU處理外部突發事件的一個重要技術。它能使CPU在運行過程當中對外部事件發出的中斷請求幾時進行處理,處理完成後又當即返回斷電,基礎進行CPU原來的工做。引發中斷的緣由或是說發出中斷請求的來源叫作中斷源。根據中斷源的不一樣,能夠把中斷分爲硬件中斷和軟件中斷兩大類,而硬件中斷又能夠分爲外部中斷和內部中斷兩類。
  • 外部中斷通常是指由計算器外部設備發出的中斷請求。如:鍵盤中斷、打印機中斷、定時器中斷等。外部中斷時能夠屏蔽的中斷,業績是說利用中斷控制器能夠屏蔽這些外部設備的中斷請求。
  • 內部中斷是指因硬件出錯(如忽然掉電)或運算出錯(如除數爲0、單步中斷)所引發的中斷。內部中斷是不可屏蔽的。
  • 軟件中斷其實並非真正的中斷,它們只是可被調用執行的通常程序以及DOS的系統功能調用(int 21)等都是軟件中斷。
  • 中斷的優先權:
    • 一、除法出錯、溢出中斷、軟件中斷;
    • 二、不可屏蔽中斷;
    • 三、可屏蔽中斷;
    • 四、單步中斷。
  • 中斷信息中包含有標識中斷源的類型碼。根據CPU的設計,中斷源類型碼的做用就是用來定位中斷處理程序。

12.1 內中斷的產生

  • 8086CPU內部有如下狀況發生時將產生相應的中斷信息:
    • 一、除法錯誤;
    • 二、單步執行;
    • 三、執行into指令;
    • 四、執行int指令。
  • 8086CPU中的中斷類型碼以下:
    • 一、除法錯誤:0
    • 二、單步執行:1
    • 三、執行into指令:4
    • 四、執行int指令,該指令格式爲int n,n爲當即數是提供給CPU的中斷類型碼。

12.2 中斷處理程序

  • CPU在收到中斷信息後當即去執行該中斷信息的處理程序。

12.3 中斷向量表

  • 中斷向量列表就是中斷向量(中斷處理程序的入口地址)的列表,其在內存中保存,存放着256箇中斷源說對應的中斷處理程序的入口。8086PC機中斷向量表放在內存地址0處。從內存0000:0000到0000:03FF的1024(一個物理地址是由段地址和偏移地址構成,要用4個字節來存放)個單元中存放着中斷向量表。
  • CPU用8位的中斷類型碼經過中斷向量表找到相應的中斷處理程序的入口地址。中斷向量表中存放的就是各個類型的處理程序的地址,8位的類型碼是個索引。

12.4 中斷過程

  • 用中斷碼在中斷向量表中找到中斷處理程序的入口地址,用它來設置CS和IP,使CPU執行中斷程序。用中斷類型碼找到中斷向量並用它設置CS和IP,這個工做室由CPU的硬件自動完成的,這個工做的過程被稱爲中斷過程。
  • 8086CPU的中斷過程:
    • 一、從中斷信息中取得中斷類型碼;
    • 二、標誌寄存器的值入棧,以保護標誌位;
    • 三、設置標誌寄存器的第8位TF和第9位IF的值爲0;
    • 四、CS的內容入棧,IP的內容入棧;
    • 五、從內存地址爲中斷類型碼* 4和中斷類型碼 *4+2的兩個字單元中讀取中斷處理程序的入口地址設置IP和CS。
    • 在最後一步完成後,CPU開始執行由程序員編寫的中斷處理程序。

12.5 中斷處理程序和iret指令

  • 常規的步驟
    • 一、保存用到的寄存器;
    • 二、處理中斷;
    • 三、 恢復用到的寄存器;
    • 四、 用iret指令返回。
  • iret指令的功能爲相應的按順序恢復以前保存起來的IP、CS地址和標誌位寄存器。用匯編語法描述爲:
pop IP
pop CS
popf

12.6 除法錯誤中斷的處理

  • 當CPU執行dvi等處罰指令的時候,若是發生了除法溢出錯誤,將產生中斷類型碼爲0的中斷信息,CPU將檢測到這個信息而後引起中斷過程,轉去執行0號中斷所對應的中斷處理程序。
assume cs:codesg
    
    codesg segment
    start:
            mov ax,1000h
            mov bh,1    
            div bh
    codesg ends
    
end start

126
debug 126

12.7 編程處理0號中斷

  • 改變0號中斷處理程序的功能,在屏幕中間顯示字而後再返回操做系統。
    • 當發生除法溢出時產生0號中斷信息,引起中斷過程。
      • 此時CPU將進行如下工做(中斷過程)
      • 當中斷0發生時,CPU將轉去執行中斷處理程序。
      • 先進行相關處理,而後向顯示緩衝區送字符串,最後返回。
    • 改變後的中斷處理程序應該放在內存中,由於除法溢出隨時可能發生,CPU隨時均可能將CS:IP指向改變後的中斷處理程序的入口執行程序。
    • 把程序存入內存,修改向量表(即將內存地址登記在中斷向量表的對應表項中),中斷時調用這個內存。

      除法溢出對應的中斷類型碼爲0,它的中斷處理程序的入口地址應該從0* 4+2地址單元開始存放,段地址存放在0* 4+2字單元中,偏移地址存放在0*4字單元中。也就是改變後的中斷處理程序的段地址0存放在0000:0002字單元中,偏移地址200H存放在0000:0000字單元中。若是要顯示的字符串在程序的data段中,那麼程序執行完成後返回,它所佔用的內存空間被系統釋放,在其中存放的信息也可能被別的信息覆蓋。

    assume cs:code
    
          code segment
          start:
                  mov ax,cs
                  mov ds,ax
                  mov si,offset do0;設置ds:di指向源地址
                  mov ax,0
                  mov es,ax
                  mov di,200h;設置es:si指向目的地址
                  mov cx,offset do0end - offset do0;設置cx爲傳輸長度,編譯器能夠識別加減乘除運算符
                  cld;設置傳輸方向爲正
                  rep movsb
    
                  mov ax,0;設置中斷向量表
                  mov es,ax
                  mov word ptr es:[0*4],200h
                  mov word ptr es:[0*4+2],0
    
                  mov ax,4c00h
                  int 21h
              do0:
                  jmp short do0start
                  db"welcome to masm!";在代碼段中存儲數據
              do0start:
                  mov ax,cs
                  mov ds,ax
                  mov si,202h;jmp short do0start這條指令棧兩個字節
                  ;顯示字符串,設置es:di指向字符串
                  mov ax,0b800h;顯存空間,直接顯示在顯示器上
                  mov es,ax
                  mov di,12*160+36*2;這隻es:di指向顯存空間的中間位置
                  mov cx,16;設置cx爲字符串(welcome to masm!)長度
              s:
                  mov al,[si]
                  mov es:[di],al
                  inc si
                  add di,1
                  mov al,02h
                  mov es:[di],al
                  add di,1
                  loop s
    
                  mov ax,4c00h
                  int 21h
              do0end:
                  nop
    
          code ends
    end start
    do0

do02

12.8 單步中斷

  • CPU執行完一條指令以後,若是檢測到標誌寄存器的TF位爲1,則產生單步中斷引起中斷過程。單步中斷的中斷類型碼爲1,它所引起的中斷過程以下:
    • 一、取得中斷類型碼;
    • 二、標誌寄存器入棧,TF、IF設置爲0;
    • 三、CS、IP入棧;
    • 四、指向指定類型碼的中斷向量表。

12.9 響應中斷的特殊狀況

  • 在有些狀況下CPU在執行完當前指令後,即使是發生了中斷也不會響應。

    在執行完向ss寄存器傳送數據的指令後,即使檢測到了中斷信號CPU也不會響應。由於ss:sp指向棧頂,對他們的設置應該連續完成。若是在執行完設置ss指令後mCPU響應中斷引起中斷過程,要在棧中壓入標誌寄存器、CS和IP的值。而ss改變,sp並未改變則ss:sp指向不是正確的棧頂將引起錯誤。

  • 咱們要將棧頂設置爲1000:0,不該該隔開

應該 不該該
mov ax,1000h mov ax,1000h
mov ss,ax mov ss,ax
mov sp,0 mov ax,0
mov ax,0 mov sp,0

十3、int 指令

引言

  • 在第12章中瞭解中斷過程和除法錯誤中斷和單步中斷的處理,這章瞭解int指令。

13.1 int 指令

  • int格式:int n,n爲中斷類型碼,它的功能是引起中斷過程。CPU執行int n之力量可以,至關引起一個n號的中斷過程,能夠在程序中使用int指令調用任何一箇中斷的中斷處理程序。執行過程以下:
    • 中斷過程從,此處去執行n號中斷的中斷處理程序。
    assume cs:code
    
        code segment
    
        start:
                mov ax,0b800h
                mov es,ax
                mov byte ptr es:[12*160+40*2],'!'
    
                int 0;執行int 0指令,引起中斷過程,執行0號中斷處理程序
        code ends
     end start
    131dosbox
  • int指令的最終功能和call相似,都是調用一段程序。通常狀況下系統將一些具備必定功能的子程序以中斷處理程序的方式提供給應用程序調用,也能夠本身編寫一些中斷處理程序供別人使用。

13.2 編寫供應用程序調用的中斷例程

  • 中斷處理程序簡稱爲中斷例程。
  • 實例1:編寫、安裝中斷7ch的中斷例程實現求一word型數據的平方。
    • 一、編程實現求平方功能的程序;
    • 二、安裝程序在0:200處;
    • 三、設置中斷向量表將程序的入口地址保存在7ch表項中,使其成爲中斷7ch的中斷例程。
    ;計算
    ssume cs:code
            code segment
            start:
                    mov ax,3456
                    int 7ch
                    add ax,ax
                    adc ax,dx
                    mov ax,4c00h
                    int 21h
            code ends
            end start
    
    
    
            ;安裝程序
            assume cs:code
            code segment
            start:
                    mov ax,cs
                    mov ds,ax
                    mov si offset sqr;設置ds:si指向源地址
                    mov ax,0
                    mov es,ax
                    mov di,200h;設置es:di指向目的地址
                    mov cx,offset sqrend- offset sqr;設置cx爲傳輸長度
                    cld;設置傳輸方向爲正
                    rep movsb
    
                    mov ax,0
                    mov es,ax
                    mov word ptr es:[7ch*4],200h
                    mov word ptr ws:[7ch*4+2],0
                    mov ax,4c00h
                    int 21h
                sqr:
                    mul ax
                    iret
                sqrend:
                    nop
            code ends
    end start
  • CPU執行int 7ch指令進入中斷例程以前,標誌寄存器、當前的CS和IP都被壓入棧中,在執行完中斷例程後,用iret指令恢復int 7ch執行前的標誌寄存器和CS和IP的值,從而接着執行應用程序。
  • int指令和iret指令配合使用與call指令和ret指令配合使用具備類似的思路。
  • 實例2:編寫、安裝中斷7ch的中斷例程,實現將一個全是字母,以0爲結尾的字符串轉化爲大寫。
assume cs:code
             data segment
                     db'conversation',0
             data ends
             
             code segment
             start:
                     mov ax,data
                     mov ds,ax
                     mov si,0
                     int 7ch
                     mov ax,4c00h
                     int 21h
             code ends 
             end start
             
             
             
             
             
             
             assume cs:code
             code segment
             
             start:
                     mov ax,cs
                     mov ds,ax
                     mov si,offset capital
                     mov ax,0
                     mov es,ax
                     mov di 200h
                     mov cx,offset capitalend - offset capital
                     cld
                     rep movsb
             
                     mov ax,0
                     mov es,ax
                     mov word ptr es:[7ch*4],200h
                     mov word ptr es:[7ch*4+2],0
             
                     mov ax,4c00h
                     int 21h
             
                 capital:
                     push cx
                     push si
                 change:
                     mov cl,[si]
                     mov ch,0
                     jcxz ok
                     and byte ptr [si],11011111b
                     inc si
                     jmp short change
                 ok:
                     pop si
                     pop cx
                     iret
             
                 capitalend:
                     nop
             code ends
end start
  • 要注意用到的寄存器衝突。

13.3 對int、iret和棧的深刻理解

  • 中斷處理程序和iret指令
  • 編程:用7ch中斷例程完成loop指令的功能,在屏幕中間顯示80個"!".

    loop指令須要循環次數和到標號的位移。爲了模擬loop指令7ch中斷例程應具有下面dec cx和若是cx的值不等於0則轉移到標號s處。

    • int 7ch引起中斷過程後,進入7ch中斷例程在中斷過程當中當前的標誌寄存器、CS和IP都要壓棧。此時壓入的CS和IP中的內容分別是調用程序的段地址(能夠認爲是標號s的段地址)和int 7ch後一條指令的偏移地址(即標號se的偏移地址)。使用iret指令用棧中的內容設置CS、IP,從而實現轉移到標號s處。
    assume cs:code
    
        code segment
        start:
                mov ax,0b800h;顯存地址
                mov es,ax
                mov di,160*12
                mov bx,offset s- offset se;設置從標號s的轉移位移
                mov cx,80
            s:
                mov byte ptr es:[di],'!'
                add di,2
                int 7ch;若是cx的值不等於0則轉移到標號s處
            se:
                nop
    
                mov ax,4c00h
                int 21h
    code ends
    end start
    
    
    
    
        ;7ch中斷例程
            lp:
                push bp
                mov bp,sp;
                dec cx
                jcxz lpret
                add [bp+2],bx
            lpret:
                pop bp
                iret

13.4 BIOD和DOS所提供的中斷例程

  • bios中主要包含如下幾部份內容:
    • 一、硬件操做系統的檢測和初始化程序;
    • 二、外部中斷和內部中斷的中斷例程;
    • 三、用於對硬件設備進行I\O操做的中斷例程;
    • 四、其餘和硬件系統相關的中斷例程。
  • bios和dos在所提供的中斷例程中包含了許多子程序,能夠用int指令直接調用。和硬件設備相關的dos中斷例程中通常都調用了bios的中斷例程

13.5 bios和dos中斷例程的安裝過程

  • 一、開機後8086CPU一加電初始化CS和IP,自動執行FFFF:0處指令,轉去執行bios中的硬件系統檢測和初始化程序。
  • 二、初始化程序將創建bios所支持的中斷向量,即將biso提供的中斷例程的入口地址登記在中斷向量表中。
  • 三、硬件系統檢測和初始化完成後,調用int 19h進行操做系統的引導。今後將計算機交由操做系統控制。
  • 四、dos啓動後除完成其餘工做外,還將它所提供的中斷例程裝入內存並創建相應的中斷向量。

13.6 bios中斷例程應用

  • bios和dos提供的中斷例程douyongah來傳遞內部子程序的編號。
  • int 10h中斷例程是bios提供的中斷例程,其中包含了多個和屏幕輸出相關的子程序。int 10h中斷例程的設置光標位置功能:
mov ah,2;表示調用10h號中斷例程的2號子程序,功能爲設置光標位置
    mov bh,0;頁號
    mov dh,5;行號
    mov dl 12;列號
    int 10h;
;功能爲在光標位置顯示字符功能
mov ah,9;置光標,調用9號子程序
mov al,'a';字符
mov bl,7;顏色屬性,和在顯存中的屬性字節的格式相同
mov bh,0;第0頁
mov cx,3;字符重複個數
int 10h
​```
- 編程:在屏幕的第5行12列顯示3個紅底高亮閃爍綠色的'a'
​```asm
    assume cs:code
    
    code segment
            mov ah,2;設置光標
            mov bh,0;第0頁
            mov dh,5;dh中放行號
            mov dl,12;dl中放列號
            int 10
    
            mov ah,9;設置光標
            mov al,'a';字符
            mov bl,11001010b;顏色屬性
            mov bh,0;第0頁
            mov cx,3;字符重複個數
            int 10h
    
            mov ax,4c00h
            int 21h
    code ends
    end

136

13.7 dos中斷例程應用

  • int 21h 中斷例程
mov ah,4ch;程序返回
          mov al,0;返回值0是正常返回
          ;合起來寫就是 mov ax,4c00h
          int 21h
  • int 2h中斷例程還具備在光標位置顯示字符串的功能、

    ds:dx;要顯示的字符串須要用 $ 做爲結束符
          mov ah,9;功能號9,表示在光標位置顯示字符串
          int 21h

    十4、端口

引言

  • CPU能夠直接讀寫3個地方的數據:
    • 一、CPU內部的寄存器;
    • 二、內存單元;
    • 三、 端口。

14.1 端口的讀寫

  • mov、push、pop等死內存讀寫指令。in和out是端口讀指令寫指令時in是從端口讀取數據,out是往端口寫入數據。in和out指令只能用ax或al來存放從端口中讀入的數據或要發送到端口中的數據,訪問8位短空時用al,訪問16位端口時用ax。
    • 訪問內存:
      • mov ax,ds:[8+0];假設(ds)=0
      • 執行時,與總線相關的操做:
        • 一、CPU經過地址線信息8發出;
        • 二、CPU經過控制線發出內存讀命令,選中存儲器芯片並通知它將要從中讀取數據;
        • 三、 存儲器將8號單元中的數據經過數據線送入CPU。
    • 訪問端口:
      • in al,60h;從60h號端口讀入一個字節。
      • 執行時與總線相關的操做:
        • 一、CPU經過地址線將地址信息60h發出;
        • 二、CPU經過控制線發出端口讀命令,選中端口所在的芯片,並通知它,將要從中讀取數據;
        • 三、端口所在的芯片將60h端口中的數據經過數據線送入CPU。
        ;對0~255之內的端口進行讀寫
        in al,20h;從20h端口讀入一個字節
        out 20h,al;往20h端口寫入一個字節
        ;對256~65535的端口進行讀寫時,端口放在dx中
        mov dx,3f8h;將端口號3f8h送入dx
        in al,dx;從3f8端口讀入一個字節
        out 3f8h,al;往3f8h端口寫入一個字節

14.2 CMOS RAM芯片

  • CMOA RAM特徵:
    • 一、包含一個實時鐘和一個有128個存儲單元的RAM存儲器。(早期的計算機位64個字節)。
    • 二、該芯片靠電池供電。所以關機後其內部的實時鐘仍可正常工做,RAM中的信息不會丟失。
    • 三、128個字節的RAM,內部實時鐘佔用0~0dh單元來保存時間信息默契與大部分單元用於保存系統配置信息,供系統啓動時bios程序讀取。
    • bios也提供了相關的程序使用戶在開機時配置CMOS RAM中的系統信息。

    • 四、該芯片內部有兩個端口,端口地址爲70h和71
      h。CPU經過這兩個端口讀寫CMOS RAM。
    • 五、70h爲地址端口,存放要訪問的CMOS RAM單元的地址;71h爲數據端口,存放從選定的CMOS RAM單元中讀取的數據,或要寫入到其中的數據。
  • CPU對CMOS RAM的讀寫分兩步進行,以讀2號單元爲例:
    • 一、將2送入端口70h;
    • 二、從71h讀出2號單元的內容。

14.3 shl和shr指令

  • shl爲邏輯左移指令功能爲:
    • 一、將一個寄存器或內存單元中的數據向左移位;
    • 二、將最後移出的一位寫入CF中;
    • 三、最低位用0補充。
mov al,01001000b
          shl al,1;將al中的數據左移一位
          ;執行後al的值是10010000b,CF=0
  • 若是移動位數大於1時,必須將移動位數放在cl中。
mov al,01010001b
          mov cl,3
          shl al,cl
          ;執行後al的值爲10001000b,cf=0
  • 二進制邏輯左移一位,至關於執行x=x*2(2是進制位)
mov al,00000001b 執行後al的值等於00000001b=1
shl al,1 執行後al的值等於00000010b=2
shl al,1 執行後al的值等於00000100b=4
shl al,1 執行後al的值等於00001000b=8
mov cl,3
shl al,cl 執行後al的值等於01000000b=64
  • shr爲邏輯左移指令功能爲:
    • 一、將一個寄存器或內存單元中的數據向右移位;
    • 二、將最後移出的一位寫入CF中;
    • 三、最高位用0補充。
  • 二進制邏輯右移一位,至關於執行x=x/2(2是進制位)

14.4 CMOS RAM中存儲的時間信息

  • 在CMOS RAM中以每一個信息一字節存放着當前的時間信息:年09h,月08h,日07h,時04h,分02h,秒00h。這些數據以BCD碼的方式存放,BCD碼以4位爲一位。

  • 數值26BCD碼錶示爲0010 0110,用兩個BCD碼錶示兩位十進制,高4位表示十位,低4位表示各位。
  • 編程:在屏幕中間顯示當前的月份。
assume cs:code
    
    code segment
    start:
            ;向地址端口70h寫入要訪問的單元地址,讀取CMOS RAM的信息
            mov al,8
            out 70h,al
            in al,71h;從數據端口中取得指定單元中的數據
            mov ah,al;al中爲從CMOS RAM的8號端口讀出數據
            mov cl,4
            shr ah,cl;ah中爲月份的十位數碼值
            and al,00001111b;ah中爲月份的個位數值碼
            add ah,30h;BCD碼值+30h(字符'0')=十進制對應的ASCII碼
            add al,30h
            ;用BCD碼錶示的月份以十進制的形式顯示到屏幕上。
            mov bx,0b800h;顯存
            mov es,bx
            mov byte ptr es:[160*12+40*2],ah;顯示月份的十位數碼
            mov byte ptr es:[160*12+40*2+2],al;顯示月份的個位數碼
    
            mov ax,4c00h
            int 21h
    
    code ends
    end start

144_1

十5、外中斷

15.1接口芯片和端口

  • CPU經過端口和外設進行聯繫。在PC系統的接口卡和主板上,裝有各類接口芯片。這些外設接口芯片的內部有若干寄存器,CCPU將這些寄存器看成端口來訪問。外設的輸入不直接送入內存和CPU,而是送入相關的接口芯片的端口中;CPU向外設的輸出也不是直接送入到外設而是先送入端口再由相關的芯片送到外設。

15.2外中斷信息

  • 外中斷源有兩類:
    • 一、可屏蔽中斷;
      • 可屏蔽中斷時CPU能夠不響應的外中斷。CPU是否響應可屏蔽中斷要看標誌寄存器的IF位的設置。
    • 二、不可屏蔽中斷
  • 當CPU檢測到可屏蔽中斷信息時:
    • 若是IF=1,則CPU在執行完當前指令後響應中斷引起中斷過程。
    • 若是IF=0,着不響應可屏蔽中斷。
    • 內中斷過程
  • 可屏蔽中斷所引起的中斷過程,除在第一步的實現上有所不一樣外,基本上和內中斷的中斷過程相同。由於可屏蔽中斷信息來自於CPU外部,中斷類型碼是經過數據總線送入CPU的;而內中斷的中斷類型碼是在CPU內部產生的。在中斷過程當中將IF置0的緣由是在進入中斷處理程序後禁止其餘的可屏蔽中斷。

  • 8086CPU提供的設置IF的指令以下:
    • sti,設置IF=1;
    • cli,設置if=0.
  • 不可屏蔽中斷是CPU必須響應的外中斷。當CPU檢測到不可屏蔽中斷信息時,則在執行完當前指令後當即響應引起中斷過程。對於8086CPU不可屏蔽的中斷類型碼固定爲2。因此中斷過程當中不須要取中斷類型碼。幾乎全部外設引起的外中斷都是可屏蔽中斷。
  • 不可屏蔽中斷過程:
    • 一、標誌寄存器入棧,IF=0,TF=0’
    • 二、CS和IP入棧;
    • 三、(IP)=(8),(CS)=(0AH)

15.3PC機及鍵盤的處理過程

  • 鍵盤輸入的處理過程:
    • 一、鍵盤輸入產生掃描碼;
    • 二、掃描碼送入60h端口;
    • 三、引起9號中斷;
    • 四、執行int 9中斷例程。
    • 前三步由硬件系統自動完成,第四步用戶能夠修改int 9中斷程序。

  • 按下一個鍵產生的掃描碼稱爲通碼,鬆開一個鍵產生的掃描碼稱爲斷碼。掃描碼被送入主板上的相關接口芯片端口地址爲60h的寄存器中。
  • 掃描碼長度爲一個字節,通碼的第7位爲0,斷碼的第7位爲1。即斷碼=通碼+80h。

鍵盤上部分鍵的掃描碼

  • bios提供了int 9中斷例程,用來進行基本鍵盤輸入處理,主要的工做以下:
    • 一、讀出60h端口中的掃描碼;
    • 二、若是是字符鍵的掃描碼就將它和它所對應的字符碼(ASCII碼)送入內存中的bios鍵盤緩衝區;
      • 鍵盤的輸入到達60h端口時相關的芯片就會向CPU發出中斷類型碼爲9的可屏蔽中斷信息。
      • CPU檢測到該中斷信息後,若是IF=1,則相應中斷,引起中斷過程,轉去執行int 9中斷例程。
      • 若是是控制鍵(如ctrl)和切換鍵(如capslock)的掃描碼,則將其轉變爲狀態字節(用爲進制位記錄控制鍵和切換鍵狀態的字節)寫入內存中存儲狀態字節的單元
    • 三、鍵盤系統進行相關的控制。如向相關芯片發出應答信息。
  • bios鍵盤緩衝區是系統啓動後mbios用於存放int 9中斷例程所接收的鍵盤輸入的內存區。該內存能夠存儲15個鍵盤輸入,在bios鍵盤緩衝區中一個鍵盤輸入用一個字單元存放,高位字節存放掃描碼,低位字節存放字符碼。0040:17單元存儲鍵盤狀態字節該字節記錄了控制鍵和切換鍵的狀態
    • 0:置1表表示按下右shift鍵
    • 1:置1表表示按下左shift鍵
    • 2:置1表表示按下ctrl
    • 3:置1表表示按下alt
    • 4:置1表表示按下scroll指示燈亮
    • 5:置1表表示按下numlock,小鍵盤輸入的是數字
    • 6:置1表表示按下capslock,輸入大寫字母
    • 7:置1表表示按下insert。處於刪除狀態

15.4編寫int 9中斷

  • 鍵盤輸入的處理過程
  • 編程:在屏幕中間依次顯示讓人看清的a~z,按下esc鍵後改變顯示的顏色。
;顯示字符
code segment
    start:
            mov ax,0b800h
            mov es,ax
            mov ah,'a'
        s:
            mov es:[160*12+40*2],ah
            inc ax
            cmp ah,'z'
            jna s
            mov ax,4c00h
            int 21h
    code ends
end start


;延遲顯示字符

assume cs:code
    stack segment
            db 128 dup(0)
    stack ends
    
    code segment
    start:
            mov ax,stack
            mov ss,ax
            mov sp,128
            mov ax,0b800h
            mov es,ax
            mov ah,'a'
        s:
            mov es:[160*12+40*2],ah
            call delay
            inc ah
            cmp ah,'z'
            jna s
    
            mov ax,4c00h
            int 21h
    
        delay:
            push ax
            push dx
            mov dx,10h;循環100次,延遲的時間和CPU的計算能力成反比
            mov ax,0
        s1:
            sub ax,1
            sbb dx,0
            cmp ax,0
            jne s1
            cmp dx,0
            jne s1
            pop dx
            pop ax
            ret
    code ends
end start


;實現IF=0,TF=0步驟
    pushf
    pop ax
    and ah,11111100b
    push ax
    popf
  • int指令在執行時CPU進行的工做
  • 完整程序
assume cs:code
    
    stack segment
            db 128 dup(0)
    stack ends
    
    data segment
            dw 0,0
    data ends
    
    code segment
    start:
            mov ax,stack
            mov ss,ax
            mov sp,128
            mov ax,data
            mov ds,ax
            mov ax,0
            mov es,ax
    
            push es:[9*4]
            pop ds:[0]
            push es:[9*4+2]
            pop ds:[2];將原來的int9中斷例程的入口地址保存
    
            mov word ptr es:[9*4+2],offset int9
            mov es:[9*4+2],cs;在中斷向量表中設置新的int 9中斷例程的入口地址
    
            mov ax,0b800h
            mov es,ax
            mov ah,'a'
    
        s:
            mov es:[160*12+40*2],ah
            call delay
            inc ah
            cmp ah,'z'
            jna s
            mov ax,0
            mov es,ax
    
            push ds:[0]
            pop es:[9*4]
            push ds:[2]
            pop es:[9*4+2];將中斷向量表中int9中斷例程的入口恢復爲原來的地址
    
            mov ax,4c00h
            int 21h
    
        delay:
            push ax
            push dx
            mov dx,10h;循環100次,延遲的時間和CPU的計算能力成反比
            mov ax,0
        s1:
            sub ax,1
            sbb dx,0
            cmp ax,0
            jne s1
            cmp dx,0
            jne s1
            pop dx
            pop ax
            ret
    
            ;新的int 9中斷例程
    
        int9:
            push ax
            push bx
            push es
    
            in al,60h
    
            pushf
            pushf
            pop bx
            and bh,11111100b
            push bx
            popf
            call dword ptr ds:[0];對int指令進行模擬,調用原來的int9中斷例程
    
            cmp al,1;esc鍵盤掃描碼
            jne int9ret
    
            mov ax,0b800h
            mov es,ax
            inc byte ptr es:[160*12+40*2+1];改變顏色
    
        int9ret:
            pop es
            pop bx
            pop ax
            iret
    
    code ends
end start

15.5安裝新的int 9中斷例程

  • 小甲魚版(筆者未成功運行)
assume cs:code
    
    stack segment
            db 128 dup(0)
    stack ends
    
    data segment
            dw 0,0
    data ends
    
    code segment
    start:
            mov ax,stack
            mov ss,ax
            mov sp,128
            mov ax,data
            mov ds,ax
            mov ax,0
            mov es,ax
    
            push es:[9*4]
            pop ds:[0]
            push es:[9*4+2]
            pop ds:[2];將原來的int9中斷例程的入口地址保存
    
            mov word ptr es:[9*4+2],offset int9
            mov es:[9*4+2],cs;在中斷向量表中設置新的int 9中斷例程的入口地址
    
            mov ax,0b800h
            mov es,ax
            mov ah,'a'
    
        s:
            mov es:[160*12+40*2],ah
            call delay
            inc ah
            cmp ah,'z'
            jna s
            mov ax,0
            mov es,ax
    
            push ds:[0]
            pop es:[9*4]
            push ds:[2]
            pop es:[9*4+2];將中斷向量表中int9中斷例程的入口恢復爲原來的地址
    
            mov ax,4c00h
            int 21h
    
        delay:
            push ax
            push dx
            mov dx,10000h;循環100次,延遲的時間和CPU的計算能力成反比
            mov ax,0
        s1:
            sub ax,1
            sbb dx,0
            cmp ax,0
            jne s1
            cmp dx,0
            jne s1
            pop dx
            pop ax
            ret
    
            ;新的int 9中斷例程
    
        int9:
            push ax
            push bx
            push es
    
            in al,60h
    
            pushf
            pushf
            pop bx
            and bh,11111100b
            push bx
            popf
            call dword ptr ds:[0];對int指令進行模擬,調用原來的int9中斷例程
    
            cmp al,1;esc鍵盤掃描碼
            jne int9ret
    
            mov ax,0b800h
            mov es,ax
            inc byte ptr es:[160*12+40*2+1];改變顏色
    
        int9ret:
            pop es
            pop bx
            pop ax
            iret
    
    code ends
end start
  • 王爽原版(筆者未成功運行)
assume cs:code
    
    stack segment
            db 128 dup(0)
    stack ends
    
    code segment
    start:
            mov ax,stack
            mov ss,ax
            mov sp,128
    
            push cs
            pop ds
    
            mov ax,0
            mov es,ax
    
            mov si,offset int9;設置ds:si指向源地址
            mov di,204h;設置es:di指向目的地址
            mov cx,offset int9end - offset int9;設置cx爲傳輸長度
            cld;設置傳輸方向
            rep movsb
    
            push es:[9*4]
            pop es:[200h]
            push es:[9*4+2]
            pop es:[202h]
    
            cli
            mov word ptr es:[9*4],204h
            mov word ptr es:[9*4+2],0
            sti
    
            mov ax,4c00h
            int 21h
    
        int9:
            push ax
            push bx
            push cx
            push es
    
            in al,60h
    
            pushf
            call dword ptr cs:[200h];當此中斷例程執行時(CS)=0
    
            cmp al,3bh;f1的掃描碼
            jne int9ret
    
            mov ax,0b800h
            mov es,ax
            mov bx,1
            mov cx,2000
    
        s:
            inc byte ptr es:[bx]
            add bx,2
            loop s
    
        int9ret:
            pop es
            pop cx
            pop bx
            pop ax
            iret
    
        int9end:
            nop
    
    code ends
end start

第16章 直接定址表

16.1 描述單元長度的標號

assume cs:code
cod segment
    a:db 1,2,3,4,5,6,7,8
    b:dw 0

start:
        mov si,offset a
        mov bx,offset b
        mov cx,8
    s:
        mov al,cs:[si]
        mov ah,0
        add cs:[bx],ax
        inc si

    loop s
        mov ax,4c00h
        int 21h
code ends
end start
;代碼中的 s、start等都是標號,表示了內存的地址

在code段中使用的標號a,b後面沒有:,所以他們能夠同時描述內存地址和單元長度的標號

assume cs:code
cod segment
    a db 1,2,3,4,5,6,7,8 ;描述了地址code:0,和從這個地址開始之後的內存單元都是直接單元
    b dw 0 ;則b是code[8]
    
start:
        mov si,0
        mov cx,8
    s:
        mov al,a[si] ;至關於mov al,cs:0[si]
        mov ah,0
        add b,ax
        inc si

    loop s
        mov ax,4c00h
        int 21h
code ends
end start

檢測點 16.1

檢測點16.1

16.2 在其餘段中使用數據標號

  • 注意:在後面加有:的地址標號只能在代碼段中使用,不能在其餘段中使用。
assume cs:code,ds:data
  cod segment
    a:db 1,2,3,4,5,6,7,8
    b:dw 0
  data ends

  start:
        mov ax,data
        mov ds,ax
        mov si,0
    s:
        mov al,a[si]
        mov ah,0
        add b,ax
        inc si

    loop s
        mov ax,4c00h
        int 21h
  code ends
  end start
  • 若是如今代碼段中直接用數據標號訪問數據,則須要用僞指令assume將標號所在的段和一個段寄存器聯繫起來。 咱們能夠將標號看成數據來定義,此時編譯器將標號所表示的地址看成數據的值。
data segment
    a db 1,2,3,4,5,6,7,8
    b dw 0
    c dw a,b
    ;至關於 c dw offset a,offset b
data ends

data segment
    a db 1,2,3,4,5,6,7,8
    b dw 0
    c dd a,b
    ;至關於 c dw offset a,seg a,offset b,seg b
    ;seg操做符,功能是取得某一標號的段地址
data ends

16.3 直接定址表

  • 利用表,在兩個數據集合之間創建一種映射關係,使咱們能夠利用查表的方法根據給出的數據獲得其在另外一集合中對應數據
    • 目的:
      1. 爲了算法的清晰和簡潔
      2. 爲了加快運算速度
      3. 爲了使程序易於擴充
  • 小練習,編寫子程序,以十六進制的形式在屏幕中間顯示給定的byte型數據。小技巧,利用映射關係,0-9數值+30h=對應字符的ascii值,10-15和A到F之間的銀色關係是:數值+37h=對應字符的ascii的值
assume cs:code
  code:segment
        mov al,0eh

        call showbyte

        mov ax,4c00h
        int 21
        ;子程序,用al傳送要顯示的數據

  showbyte:
        jmp short show

        table db '1023456789ABCDEF';字符表

  show:
        push bx
        push es

        mov ah,al
        shr ah,1
        shr ah,1
        shr ah,1
        shr ah,1;右移4位,ah中獲得高4位的值
        and al,00001111b;al中爲低4位

        mov bl,ah
        mov bh,0
        mov ah,table[bx];用高4位的值做爲相對於table的便宜,取得對應的字符

        mov bx,0b800h
        mov es,bx
        mov es:[160*12+40*2],ah

        mov bl,al
        mov bh,0
        mov al,table[bx];用低4位的值做爲相對於table的偏移,取得對應的字符

        mov es:[160*12+40*2+2],al

        pop es
        pop bx
        ret

  code ends
  end start

16.4 程序入口地址的直接定址表

  • 小練習
  1. 清屏:將顯存中當前屏幕中的支付設爲空格;
  2. 設置前景色:設置顯存中當前屏幕中處於奇地址的屬性字節的第0、一、2位;
  3. 設置背景色:設置顯存中當前屏幕中處於奇地址的屬性字節的第四、五、6位;
  4. 向上滾動一行:依次將第n+行的內容複製到第n行處,最後一行爲空。
;================================入口函數1=====================================
;入口函數說明;
;用ah傳遞功能號,0是清屏,1是設置前景色,2是設置背景色,3是向上滾動一行

setscreen:
        jmp short set
        table dw sub1,sub2,sub3,sub4

set:
        push bx
        cmp ah,3;判斷傳遞的功能號是否大於3
        ja sret
        mov bl,ah
        mov bh,0
        add bx,bx;根據ah中的功能號計算對應子程序的地址在table表中的偏移

        call word ptr table[bx];調用對應的子程序

sret;
        pop bx
        iret

;================================入口函數2=====================================
;入口函數說明;
;用ah傳遞功能號,0是清屏,1是設置前景色,2是設置背景色,3是向上滾動一行

setscreen:
        cmp ah,0
        je do1
        cmp ah,1
        je do2
        cmp ah,2
        je do3
        cmp ah,3
        je do4

        jmp short sret

do1:
        call sub1
        jmp short sret

do2:
        call sub2
        jmp short sret

do3:
        call sub3
        jmp short sret

do4:
        call sub4
        jmp short sret

;子功能==========================================================================

;清屏
sub1:
        push bx
        push cx
        push es
        mov bx,0b800h
        mov es,bx
        mov bx,0
        mov cx,2000

sub1s:
        mov byte ptr es:[bx],''
        add bx,2
    loop sub1s

        pop es
        pop cx
        pop bx
        ret

;設置前景色
sub2:
        push bx
        push cx
        push es
        mov bx,0b800h
        mov es,bx
        mov bx,1
        mov cx,2000

sub2s:
        mov byte ptr es:[bx],11111000b
        or es:[bx],al
        add bx,2
    loop sub2s

        pop es
        pop cx
        pop bx
        ret

;設置背景色
sub3:
        push bx
        push cx
        push es
        mov cl,4
        shl al,cl
        mov bx,0b800h
        mov es,bx
        mov bx,1
        mov cx,2000

sub3s:
        mov byte ptr es:[bx],10001111b
        or es:[bx],al
        add bx,2
    loop sub3s

        pop es
        pop cx
        pop bx
        ret

;向上滾動一行
sub4:
        push cx
        push si
        push di
        push es
        push ds

        mov si,0b800h
        mov es,si
        mov ds,si
        mov si,160;ds:si指向第n+行
        mov di,0;es:di指向第n行
        cld
        mov cx,24;共複製24行

sub4s:
        push cx
        mov cx,160
        rep movsb;複製
        pop cx
    loop sub4s

        mov cx,80
        mov si,0

sub4s1:
        mov byte ptr es:[160*24+si],'';最後一行清空
        add si,2
    loop sub4s1

        pop ds
        pop es
        pop di
        pop si
        pop cx
        ret;結束

第十七章 使用BIOS進行鍵盤輸入和磁盤讀寫

略。。。。。。

筆者看不下去了。。。。有興趣的讀者能夠繼續找相關的資料看。。。

END

相關文章
相關標籤/搜索