hehe

王爽匯編全書知識點大綱


第一章 基礎知識

  • 機器語言

機器語言是機器指令的集合,電子計算機的機器指令是一系列二進制數字.計算機將之轉換為一系列高低電平脈沖信號來驅動硬件工作的.

  • 匯編語言的產生

由于機器語言指令都是由01組成,難以編寫,記憶和維護程序.所以匯編語言為了解決這一問題產生.匯編語言又稱為機器語言的助記符

  • 匯編語言的組成

匯編指令:有對應的機器碼.機器碼的助記符

偽指令:沒有對應的機器碼,由編譯器執行,計算機并不執行.

其他符號:如+,-,*,/等,由編譯器識別,沒有對應的機器碼

  • 存儲器

  • 相當于大腦,程序指令和數據在存儲器中都是以二進制數據形式存放,沒有什么區別.且內存是動態存儲數據的,程序或文件從磁盤加載到內存才能由cpu直接讀取.斷電后,內存中的數據就消失了.磁盤才是永久存儲數據的地方.

  • 存儲單元從0開始編號.能存儲一個8位二進制數的單元,稱為存儲單元.為一個byte.大小,單位B. 位的單位為bit.向上為kb,mb,GB,TB,PB

  • cpu對存儲器的讀寫

  • 必要的信息交互

(1). 存儲單元的地址(地址信息),地址總線(cpu要從存儲器中讀取數據,首先要要確認,某個存儲單元的地址.)

(2).器件的選擇,讀或寫的命令(控制信息)控制總線(另外計算器中不只有存儲器一種器件,其它器件,也有存儲器.要確定從什么器件讀取.)

(3).讀或寫的數據(數據信息),數據總線.

總線與信息傳送 : 電子計算機能處理,傳輸的信息都是電信號,電信號當然通過導線傳送,也就是總線(數根導線的集合).

讀取例子:Mov Ax,[3]

CPU通過地址線將地址信息3發出cpu通過控制總線發出讀命令,并選中存儲器芯片,并通知他,將要從中取數據,存儲器將3號單元中的數據8通過數據線送入cpu

  • 三大總線

  • 地址總線

地址總線:8根,表示能尋址0-1023的內存單元.能尋址2^8個內存單元,代表了cpu的尋址范圍

  • 數據總線

假如數據總線有8根,那么每次只能傳送1B數據.每次只能傳送一個8位數.

  • 控制總線

假如控制總線位有8根,那么有2^8種控制方式,控制總線的寬度決定了CPU對外部器件的控制能力.

  • 內存地址空間

  • 每種器件cpu都通過總線將其連接,cpu在操控它們的時候,都把它們當成是內存來對待.(正如linux把每個硬件都當成文件來對待),操作顯示器就是操作顯存,顯存就可以當文件一樣進行讀寫操作.cpu操作硬件實際上就是操作硬件的內存或端口

  • 0-7fffh的32kb空間為主隨機存儲器的地址空間.地址8000h-9fffh的8kb空間為顯存地址空間A000h~ffffh的24kb空間為各個rom的地址空間.(寫入顯存地址空間,就會顯示在顯示器上,寫入只讀rom地址空間,那么沒有任何效果,我們基于一個計算機硬件系統編程的時候,必須知道這個系統中的內存地址空間分配情況).

  • BIOS有一個只讀room,是硬件和操作系統的接口.

第二章 寄存器

  • cpu的基本結構

  • 運算器:運算器進行信息處理

  • 寄存器:寄存器進行信息存儲

  • 控制器:控制器控制各種器件進行工作

  • 內部總線:用來連接cpu內部各種元器件的導線集合

  • 寄存器:AX,BX,CX,DX, SI,DI,SP,BP,IP, CS,SS,DS,ES, PSW

  • 通用寄存器

為了和上一代cpu兼容可以拆分16位通用寄存器,拆分為兩個8位寄存器.從右向左,以0開始編號.

  • AX可拆分為AH和AL

  • BX可拆分為BH和BL

  • CX可拆分為CH和CL

  • DX可拆分為DH和DL

  • 字的存儲和數制表示

  • 字在寄存器中的存儲:高8位放在寄存器的高8位中,低8位放在寄存器的低8位中

  • 字在內存中的存儲:低位存儲在低地址中,高位存儲在高地址中.稱為小尾存儲.

  • 數制的表示:源代碼中默認數制是十進制,在數字后面添加h表示16進制,如果16進制最高位為字符,那么必須在前面添0, 如果數字后面加b后綴表示二進制數字. 特別注意,在debug中數制默認是16進制.

  • 幾條匯編指令

  • 匯編指令默認不區分大小寫

  • 假設ax和bx中的值為8226h,add ax,bx執行后,相加的結果為1044ch,但是ax存儲不下,最后ax中的值為044ch

  • 假設ax中的值為00c5h,add al,85h執行結果為158h,al存儲不下會設置標志位.千萬不要認為高位進位會存儲在ah中,這是錯誤的想法,最后ax中的結果為0058h.

  • 下面的指令是錯誤指令:

mov ax,bl //兩個寄存器尺寸不一致

mov bh,ax //兩個寄存器尺寸大小不一致

mov al 20000 //20000在al中不能存儲

add al 100H //100h超過al中存儲的最大范圍

  • 計算2^4:

Mov ax,2

ADD AX,AX

ADD AX,AX

ADD AX,AX

  • 8086物理地址

  • 內存空間是一個線性空間.每一個內存單元在這個空間中都有唯一的地址,我們將這個唯一的地址稱為物理地址.

  • 16位機的含義:

  • 運算器一次最多處理 16 位的數據

  • 寄存器最大寬度為 16 位

  • 寄存器和運算器之間通路為 16 位,即數據總線 16 條

  • 物理地址的計算:為了用16位地址尋址更多的內存,8086采取用連個16位寄存器合成一個20位地址,即

段寄存器中的地址*16+16位偏移地址

  • 一個段的起始地址必然是16的倍數

  • 因為偏移寄存器只有16位大小,所以給定一個段,它最大能在當前段尋址64kb

  • 尋址過程:段地址和偏移地址通過內部總線送入一個稱為地址加法器的部件,地址加法器通過內部總線將20位物理地址送入輸入輸出控制電路.輸入輸出控制電路將20位物理地址送上地址總線.20位物理地址總線傳送到存儲器.

  • 一個地址可以用不同的段形式表示.

  • CS和IP

  • 段寄存器:CS,DS,ES,SS

  • CS位代碼段寄存器,IP位代碼偏移地址寄存器,cpu把任何時刻cs和ip指向的地址處的數據當做指令執行.

  • 指令的讀取和執行:

  • 假設地址2000:0~2000:2中存儲著指令mov ax,123h

  • 初始狀態cs=2000h,ip=0,將從內存2000h:0中讀取指令執行

  • 將CS和IP的值送上地址加法器合成一個20位的物理地址

  • 地址加法器將物理地址送入輸入輸出電路

  • 輸入輸出電路將物理地址20000h送上地址總線

  • 從地址20000h中讀取數據通過數據總線送入cpu

  • 輸入輸出電路將指令mov ax,123h送入指令緩沖器

  • 讀取一條指令后ip的值自動增加,指向可以讀取的下一條指令

  • 執行控制器執行指令mov ax,123h

  • 然后重復上面的步驟

  • 8086cpu加電啟動或復位后,cs和ip被設置為cs=ffffh,ip=0000h,cpu從內存FFFF0h單元中讀取指令并執行.

  • 修改cs和ip的指令:

  • 通過修改cs和ip的指向,起到控制cpu執行流程和跳轉等功能.在debug中可以通過mov指令修改cs的值,但是不能用mov修改ip的值

  • 在cpu中程序員能夠用指令讀寫的部件只有寄存器

  • mov指令不能用于設置cs:ip的值.原因很簡單,因為沒有這個功能.但是debug中可以用mov cs,xxx

  • 想同時修改 CS,IP

jmp 1000:0 則 CS=1000H,IP=0000H //這條指令只在debug中有效

僅修改 IP 的內容

jmp 某一合法寄存器 //在源代碼中也有效

  • debug調試指令

  • 用 R(register) 命令查看或改變 cpu 寄存器內容

  • r ax/其它寄存器 功能:修改 ax 寄存器內容

  • 直接r, 查看各個寄存器的值.

  • 用 D(dump)命令查看內存中內容

  • d 1000:0 會顯示10000:0地址開始的128個內存單元的內容

  • D 段地址:開始偏移地址 結束偏移地址 可以查看指定內存單元的內容例子如下:

  • D 1000:0 f (1000:0 ~ 1000:f內存單元的數據) 再次輸入D會把后續的128個內存單元內容顯示出來.

  • 用 E (enter)命令改寫內存中的內容

  • 采用“e 起始段地址:偏移地址 數據 數據 數據..." e 1000:0 0 1 2 3 4 5 6 7 寫機器碼.這是一次性輸入

  • 用一個一個的輸入方式,改寫內存的內容 (按空格鍵陸續輸入,敲回車結束輸入.) 例如 e 1000:0,以向內存中寫入字符 e 1000:0 1 'a' 2 'b' 3 'c' 寫機器碼和字符.

注:e命令寫進去的機器碼數值16進制形式,所以數字不能超過兩位十六位數字,eg:111這是錯誤的(因為debug中默認是16進制數制).

  • 用 U(unassemble) 查看內存中機器碼含義,用 T 執行cs:ip指向的指令.

  • U 段地址:偏移地址

  • 查看指定地址間的匯編指令:U 段地址:偏移地址 結束地址

  • 用 A(assemble) 以匯編指令形式在內存中寫入機器指令. A 段地址:偏移地址

  • 用g(go) 命令跳到某指令,g 地址

eg:g 0012(表示從當前cs:ip指向的指令執行,一直到(ip) = 0012為止.) 用 t(trace) 命令單步執行 用 p (proceed)命令跳躍循環

  • q退出debug

  • dosbox中可以按alt+enter鍵實現全屏.

  • debug界面認識

  • 習題講解:下面的代碼幾次修改IP的值

mov ax,bx

sub ax,ax

jmp ax

答:一共4次,第一次:讀取完mov ax,bx后.第二次:讀取完sub ax,bx后.第三次:讀取完jmp ax后.第四次:執行完jmp ax后.

第三章 寄存器內存訪問

  • 字在內存中的存儲

  • 低位字節存放在低地址單元中,高位字節存放在高地址單元中.

  • 字單元:兩個字節聯合起來的16位.存儲單元.起始地址為N的字單元簡稱為N地址字單元

  • DS和[ADRESS]訪問內存單元

  • ds寄存器用來存放要訪問的內存單元的段地址

  • 8086硬件設計的問題,不能將立即數直接用mov指令傳送到段寄存器中

  • 在源代碼中使用[idata]時,[idata]會被當成idata.

調試的時候,[idata]才會被翻譯成內存單元.源代碼中需要使用[bx/si/di..]等

表示內存單元.也可以使用段前綴, 段寄存器 : [idata]

  • 字的傳送

  • 只要在mov指令中給出的寄存器是16位,就會對字進行操作.

  • 也可以用尺寸指針指明要操作的內存單元大小,byte ptr [],word ptr [],dword ptr[]

  • mov/add/sub指令詳解

  • mov指令有以下幾種格式:


mov 寄存器,數據

mov 寄存器,寄存器

mov 寄存器,內存單元

mov 內存單元,寄存器

mov 段寄存器,寄存器

mov 寄存器,段寄存器 //這個是自己實驗添加的

mov 段寄存器,內存單元//這個是自己實驗添加的

mov 內存單元,段寄存器//這個是自己實驗添加的

注意事項:

  1. 從這幾種格式可以看出,操作數不能同時為段寄存器
  1. 也不能同時為內存操作數.
  1. 不能將立即數直接送入段寄存器.
  1. 立即數只能放在右邊,如果此時左邊操作數為內存單元,需要指明內存單元尺寸
  1. 操作數不能出現ip寄存器
  • add指令有以下幾種格式:

add 寄存器,數據

add 寄存器,寄存器

add 寄存器,內存單元

add 內存單元,寄存器

add byte/word/... ptr 內存單元,數據

注意事項:

  1. add指令中不能出現段寄存器操作數
  1. add中操作數不能同時為內存單元
  1. 第一個操作數必須是寄存器或者內存單元
  1. add指令中也不能出現ip寄存器
  • sub指令,格式和使用方式同add指令

  • 數據段

  • 我們可以將一組已知起始地址的內存單元定義為數據段,該段<= 64kb

  • 然后將ds設置為起始地址,然后該地址開始的段就可以當做數據段使用

  • 同理可以設置ss:sp的指向,來構造棧段

  • 代碼段是通過,end 標號 指明起始地址(標號就是代碼段開始處)

  • 后進先出數據結構,最初棧指針指向高地址,越壓棧,sp寄存器指針越往低地址移動.

  • 通過ss:sp指定和初始化棧.

  • push和pop指令出棧和入棧

  • push 寄存器/內存單元/段寄存器 : sp=sp-2 mov (sp),(寄存器/內存單元/段寄存器)

  • pop 寄存器/內存單元/段寄存器: mov (寄存器/內存單元/段寄存器),(sp) sp=sp+2

  • 注意事項:

操作數不能是ip寄存器,棧每次操作一個字

  • 棧可以超界,且沒有任何保護機制

  • 棧環繞問題

如果將10000H~1FFFFH這段空間當做棧段,初始狀態是空的,此時ss=1000h,sp=?

答:sp=0,因為此時會發生棧環繞問題,FFFF的底端無法尋址,所以就會循環跑到另一頭0上面去.,此時壓棧會發生,sp=0-2=0FFFEH

  • 編程例子:

編程:將10000h~1000Fh這段空間當做棧,初始狀態是空的,設置ax=001ah,bx=001bh,將ax,bx入棧,然后將ax,bx清零,從棧中恢復ax,bx的值.

解答:


mov ax,1000h

mov ss,ax

mov sp,10h

mov ax,001ah

mov bx,001bh

push ax

push bx

sub ax,ax

sub bx,bx

pop bx

pop ax

利用棧交換ax,bx內容


push ax

push bx

pop ax

pop bx

....

  • 實驗2

  • 可以在調試命令中需要段地址的地方,使用段寄存器.例子如下:

  • d 1000:0 會將段地址1000送入ds中.

  • e ds:0 0 1 2 3 4

  • u cs:0 ;以匯編指令形式,顯示當前代碼

  • a ds:0

  • ss:sp的設置指令應該放在一起緊挨著,單步中斷并不會中斷,這兩條 指令會連續執行,因為中斷也設計壓棧操作,那么假設如果在sp還未設置時,發生了中斷,那么中斷的壓棧操作將會壓入不正確的棧頂,緊接著又設置了新的sp值,那么程序就不會正確運行.

第4章 第一個程序

  • 源程序到可執行程序

  • 源程序文本文件-->編譯鏈接--->生成可執行程序

  • 可執行程序包含從源程序中翻譯過來的機器碼和數據,還有相關的描述信息(比如,程序有多大,要占用多少內存空間等)

  • 操作系統根據可執行文件的描述信息,將可執行文件中的機器碼和數據加載入內存,并進行相關的初始化,并進行相關的初始化(比如設置CS:IP指向第一條指令),然后cpu執行程序.

  • 如果不用end指明程序入口,那么編譯成程序時,cpu會自動將cs,ip指向改程序數據開頭.如果指定了標號,那么cpu會將cs和ip指向標號地址處.

  • 源程序


assume cs:code

code segment

mov ax,0123h

mov bx,0456h

add ax,bx

add ax,ax

mov ax, 4c00h

int 21h

code ends

end

  • xxx segment ....xxx ends偽指令是定義一個段.標號就是偏移地址,不需要用offset獲取

  • end偽指令 : 表示結束一個匯編源程序的編譯,end 后面可以接一個標號來指明可執行代碼入口.

  • assume偽指令 : 它假設程序的某個段寄存器與某個段標號相關聯.通過這種關聯,在需要的情況下,編譯程序可以將段寄存器和某一個具體的段想聯系.

  • 程序返回 : mov ax,4c00h int 21h程序返回.告訴運行本程序的程序,運行結束后的結果狀態.ah=4c,傳遞中斷子程序功能號,表示調用21中斷的4c號子程序. Al=0表示返回值為0.如果不正確返回

程序就會運行不正常.

  • 鏈接

  • 當源程序很大的時候,我們可以將源程序分成多個程序,然后編譯成多個obj文件.然后連接在一起,

  • 程序中調用了某個庫文件中子程序,需要將這個庫文件和程序生成的目標文件連接到一起,生成一個可執行文件.

  • 一個源程序編譯后,得到了存有機器碼的目標文件,目標文件中的有些內容還不能直接生成可執行文件.連接程序將這些內容處理為最終可執行信息.所在,在只有一個源程序文件,而又不需要調用某個庫中的子程序的情況下,也必須用連接程序對目標文件進行處理,生成可執行文件.

  • dos程序的加載流程

  • dos中直接運行exe文件時,首先加載入內存,設置cs:ip的入口(end指定的標號處).

  • 找到一段起始地址為SA:0000(即起始地址的偏移地址為0)的容量足夠的空閑內存區

  • 前256個字節中創建一個段前綴psp的數據區,dos利用psp來和被加載程序進行通信.ds首先初始化時,是指向psp數據區的.

  • 程序放在SA+10H:0地址處,CS:IP指向這里

  • 實驗3

  • 雜項知識:將程序加載入內存執行,cx存儲的是程序的長度

第五章 [bx]和loop

  • [bx]

  • 要完整描述一個內存單元需要兩種信息:@內存單元地址@內存單元長度

  • 表示段地址在ds中,bx中存放的是該段的偏移地址.

  • Loop指令

  • (cx)=(cx) - 1

  • 判斷cx中的值是否為零,非零繼續循環,零則退出循環.

  • 循環例子程序:計算2^12次方


assume cs:code

code segment

mov ax,2

mov cx,11

s:

add ax,ax

loop s

mov ax, 4c00h

int 21h

code ends

end

例子2:計算123*236


assume cs:code

code segment

mov ax,0

mov cx,123

s:

add ax,236

loop s

mov ax, 4c00h

int 21h

code ends

end

  • inc/dec 寄存器或

  • inc/dec (byte /word/..) ptr [內存單元]

  • 自增或自減指令

  • 雜項

  • 在匯編源程序中,數據不能以字母開頭需要在前面添加一個0.

  • 在 dos 下,一般情況 0:200-0:2ff 空間中沒有系統或其他程序的數據或代碼

  • 遇到loop指令時,用p,可以跳過循環.遇到int 21是也可以用p命令結束程序

  • g 地址偏移 ,直接跳轉到該地址處執行

  • loop和bx 的聯合應用

  • 計算ffff:0~ffff:b每個內存單元中的數據相加的和,結果存儲在dx中


assume cs:code

code segment

mov ax,0ffffh

mov ds,ax

mov bx,0

mov dx,0

mov cx,12

mov ah,0

s:

mov al,[bx]

add dx,ax

inc bx

loop s

mov ax, 4c00h

int 21h

code ends

  • 段前綴

  • 可以在[idata]或[bx]前面加上cs,ds,es,ss等段前綴來指明段地址

  • cs/ds/es/ss : []

  • 段前綴的使用

  • 編程:將內存ffff:0ffff:b單元的數據復制到0:200020b中


assume cs:code

code segment

mov ax,0ffffh

mov ds,ax

mov ax,0020h

mov es,ax

mov bx,0

mov cx,6

s:

mov ax,[bx]

mov es:[bx],ax

add bx,2

loop s

mov ax, 4c00h

int 21h

code ends

end

  • 實驗4 bx和loop的使用

  • 編程,向內存0:200~0:23f依次傳送數據0-63(3fh)


assume cs:code

code segment

mov ax,0020h

mov ds,ax

mov bx,0

mov cx,64

s:

mov [bx],bl

inc bx

loop s

mov ax, 4c00h

int 21h

code ends

end

第六章 包含多個段的程序

  • 在代碼中使用數據

  • 程序獲得所需空間的方法有兩種

  • 是在程序加載的時候為程序分配

  • 是在程序運行過程中向系統申請.我們只討論前者.因為后者屬于動態分配內存.這兩種方式都是由操作系統分配.

  • 還有就是在dos時代,可以直接將數據送入內存.當數據有很多時,這種方法存儲明顯不現實

  • db定義字節, dw定義字數據,dd定義雙字,dq8字節,dt十字節

  • 程序框架


assume cs:code

code segment

start:

程序代碼

mov ax, 4c00h

int 21h

code ends

end start

end代表程序源代碼結束,并指明可執行代碼入口地址.

如果指明入口地址,那么從程序的首地址開始執行,不管起始地址處是否是可執行代碼.

  • CPU根據什么設置CS:IP指向程序的第一條要執行的指令?是由可執行文件中的描述信息指明的.描述信息則主要是編譯,連接程序對源程序中相關偽指令進行處理所得到的信息.

  • 即使我們用了類似Assume cs:code,ds:data這樣的偽指令,在執行的時候,Cpu也不會把他們的段寄存器指向相應的內容,cpu在加載程序運行把CS:IP指向我們Start入口處,我們在這里調整ss,ds等段寄存器的指向.

  • 在代碼中使用棧

  • 利用棧將程序中的數據逆序存放


assume cs:code

code segment

dw 0123h, 0456h, 0789h, 0abch, 0defh, 0fedh, 0cbah, 0987h

dw 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0

start:

mov ax,cs

mov ss,ax

mov sp,30h

mov bx,0

mov cx,8

s:

push cs:[bx]

add bx,2

loop s

mov bx,0

mov cx,8

s1:

pop cs:[bx]

add bx,2

loop s1

mov ax, 4c00h

int 21h

code ends

end  start

  • 棧使用技巧:在內存之間傳遞數據,連個變量之間的傳遞,可以先用棧做中轉,即先壓棧變量1

然后彈出來給變量2

  • 將數據,代碼,棧放入不同的段

  • 實驗5

  • 非常重要:如果段中的數據占N個字節,則程序加載后,該段實際占有的空間為 ((N+15)/16)_16 。

解析:

N分為被16整除和不被16整除。

當N被16整除時: 占有的空間為(N/16)_16

當N不被16整除時: 占有的空間為(N/16+1)_16,N/16得出的是可以整除的部分,還有一個余數,余數肯定小于16,加上一個16。

程序加載后分配空間是以16個字節為單位的,也就是說如果不足16個字節的也分配16個字節。

兩種情況總結成一個通用的公式:((N+15)/16)_16

  • 每個段(例如 數據段,代碼段)必須是 16字節的整數倍(系統規定)所以小于16個字節的會自動的變成16個字節

  • 程序開始時ds指向psp比cs大10h,cx中存放著程序的長度

  • 編寫代碼,將a段和b段的數據相加,保存在c段中.


assume cs:code

a segment

db 1, 2, 3, 4, 5, 6, 7, 8

a ends

b segment

db 1, 2, 3, 4, 5, 6, 7, 8

b ends

c segment

db 0, 0, 0, 0, 0, 0, 0, 0

c ends

code segment

start:  mov ax, a

mov ds, ax

mov ax, b

mov es,ax

mov ax, c

mov ss,ax

mov ax,0

mov bx,0

mov cx,8

s: mov al,ds:[bx]

add al,es:[bx]

mov ss:[bx],al

inc bx

loop s

mov ax, 4c00h

int 21h

code ends

end start

第7章 更靈活的定位內存的方法

  • and和or 指令

  • and指令,按位進行與運算,or按位進行或運算.

  • 第一個操作數必須寄存器或內存單元(內存單元必須指明尺寸)

  • 操作數不能是段寄存器,或ip寄存器

  • 兩個操作數的尺寸必須一致

  • 操作數不能同時是兩個內存單元.

  • 關于ascii碼

  • 大寫字母在6590(41h5Ah)(0100000101011010)開始,小寫字母97122(61h-7Ah)(01100001~01111010)開始.

  • 大寫字母第6位為0,小寫字母第6位為1(索引從1開始算)

  • 重點是了解了字符的ascii碼的規律后,可以利用and和or指令進行大小寫轉換

  • 數字的ascii碼4857(30h-49h)(00110000b00111001b).

  • 數字字符,跟二進制數字的區別是數字字符第5位和第6位都為1而,數字值第7位和第8位都為0

  • 有助于理解ascii碼壓縮和非壓縮指令

  • 編碼方案:在顯示器上呈現的文字等都是文本字符串,在計算機內部是以二進制形式存儲,通過解碼,顯示器在屏幕上畫出文本,其中一種編碼方案是ascii碼.

  • 文本處理過程例子:我們按下鍵盤a鍵,這個按鍵的信息被送入計算機,計算機用ascii嗎的規則對其進行編碼,將其轉化為61h存儲在內存的指定空間中,文本編輯軟件從內存中取出61h,將其送到顯卡上的顯存中;工作在文本模式下的顯卡,用ascii碼的規則解釋顯存中的內容.61h被當作字符"a".顯卡驅動顯示器,將字符"a"的圖像畫在屏幕上.

  • db 'unIX' <=> db 75h,6eh, 49h,58h.

mov al, 'a' <=> mov al,97

  • 大小寫轉換問題例子代碼

  • 寫一段程序將 'BaSic'字符串轉化為大寫,將'iNfOrMaTiOn'轉換為小寫


assume cs:code, ds:data

data segment

db 'BaSic'

db 'iNfOrMaTiOn'

data ends

code segment

start:

mov ax, data

mov ds, ax

mov bx,0

mov cx,5

s0:

mov al,[bx]

and al,11011111b

mov [bx],al

inc bx

loop s0

mov cx,11

mov bx,5

s1:

mov al,[bx]

or al,00100000b

mov [bx], al

inc bx

loop s1

mov ax, 4c00h

int 21h

code ends

end  start

  • 更靈活的尋址

  • [bx+idata] <=> idata[bx],[bx].idata

  • 可以用于數組尋址,例子如下,將數據段中第一個字符串轉換為大寫,第二個轉換為小寫


assume cs:code, ds:data

data segment

db 'BaSic'

db 'MinIX'

data ends

code segment

start:

mov ax, data

mov ds, ax

mov bx,0

mov cx,5

s0:

mov al,[bx]

and al,11011111b

mov [bx],al

mov al,[bx+5]

or al,00100000b

mov [bx+5],al

inc bx

loop s0

mov ax, 4c00h

int 21h

code ends

end  start

  • 技巧:因為第一個字符串和第二個字符串一樣長,我們就可以用bx+idata尋址,且可以用一個循環就可以達到轉換目的,因為長度一樣長.

  • C語言:a[i]<=>idata[i]

  • [bx+si],[bx+di]

  • [bx+si+idata],[bx+di+idata]默認段地址在ds中.

  • [bp+si+idata],[bp+di+idata] 如果bp沒有給出段前綴,默認段地址在ss中.

  • 注意事項 : bp和bx不能同時出現,di和si也不能同時出現.且di和si不能拆分成8位寄存器.

  • 尋址綜合應用

  • 將data段中的每個單詞的首字母大寫.每個字符串占16個字節.


assume cs:code, ds:data

data segment

db '1\. file        '

db '2\. edit        '

db '3\. search      '

db '4\. view        '

db '5\. options      '

db '6\. help        '

data ends

code segment

start:

mov ax, data

mov ds, ax

mov bx,0

mov cx, 6

s0:

mov al,[bx+3]

and al,11011111b

mov [bx+3],al

add bx,16

loop s0

mov ax, 4c00h

int 21h

code ends

end  start

  • 將data段中的每個單詞改為大寫字母,由于每個字符串一樣長,我們可以用嵌套循環

assume cs:codesg, ds:datasg

datasg segment

db 'ibm            '

db 'dec            '

db 'dos            '

db 'vax            '

datasg segment

codesg segment

start:mov ax,datasg

mov ds,ax

mov bx,0

mov cx,4

mov ax,0

s0: mov dx,cx

mov si,0

mov cx,3

s1:mov al,[bx+si]

and al,11011111b

mov [bx+si], al

inc si

loop s1

add bx,16

mov cx,dx

loop s0

codesg ends

end start

這里注意保存外層循環的cx值.可以用其它寄存器保存,也可以保存在內存單元中,也可以保存在棧中

  • 將data段中的前四個字母改為大寫.

assume cs:codesg, ds:datasg

datasg segment

db '1\. display      '

db '2\. brows        '

db '3\. replace      '

db '4\. modify      '

datasg segment

codesg segment

start:

mov ax, datasg

mov ds,ax

mov bx,0

mov cx,4

s1:

push cx

mov cx,4

mov si,0

s2:

mov al,[bx+si+3]

and al,11011111b

mov [bx+si+3],al

inc si

loop s2

add bx, 16

pop cx

loop s1

mov ax, 4c00h

int 21h

codesg ends

end start

注意:si需要在循環中每次重置為0

  • 實驗6

  • 實踐本單元所有程序

第八章 數據處理的兩個基本問題

  • 兩個基本問題

  • 處理的數據在什么地方

  • cpu內部(寄存器,緩存)

  • 端口

  • 內存

  • 數據的尺寸有多長

  • 寄存器的大小指明尺寸

  • 用byte/word/dword ptr [尋址寄存器或段前綴]來指明.

  • 數據的位置表達

  • 立即數:直接包含在指令中的立即數在執行前是在cpu內部的指令緩沖器中.

  • 寄存器

  • 內存單元:bx系列尋址組合默認段地址在ds中,bp系列組合默認地址在ss中

  • 尋址的綜合應用

  • 關于DEC公司的一條記錄(1982年)如下:

公司名稱:DEC

總裁姓名:Ken Olsen

排名:137

收入: 40(40億美元)

著名產品:PDP(小型機)


mov ax,seg

mov ds,ax

mov bx,60h

mov word ptr [bx+0ch],38

mov word ptr [bx+0eh],70

mov byte ptr [bx+10h],'V'

inc bx

mov byte ptr [bx+10h],'A'

inc bx

mov byte ptr [bx+10h],'X'

  • 幾條指令

  • div指令

  • 格式:

div reg

div 內存單元 //必須指明內存單元尺寸

  • 指令說明

  • 除數:有8位和16位兩種在reg或內存單元中.

  • 被除數:放ax或ax和dx中,如果除數為8位,則被除數為16位,默認在ax中存放,如果除數為16位,則被除數則為32位.在dx和ax中存放,dx存放高16位,ax存放低16位

  • 結果:如果除數為8位,al存放商,AH存放余數,如果除數為16位,則AX存放除法的商,dx存放除法操作的余數..

  • 例子:利用除法計算1000001/100

因為100001大于65536,化成16進制為186A1


mov dx,1

mov ax,86A1h

mov bx,100

div bx

  • 例子:編程,利用除法指令計算1001/100

mov ax,1001

mov bl,100

div bl

  • 用div計算data段中的第一個數據除以第二個數據后的結果,商存在第三個數據的存儲單元中

assume cs:code, ds:data

data segment

dd 100001

dw 100

dw 0

data ends

code segment

start:

mov ax, data

mov ds,ax

mov bx,0

mov ax,[bx]

mov dx,[bx+2]

div word ptr [bx+4]

mov [bx+6],ax

mov ax, 4c00h

int 21h

code ends

end  start

  • dd偽指令:定義雙字型數據. 還有db/dw/dq(八字節)/dt(十字節)

  • dup偽指令:db/dw/dd 重復次數 dup (需要重復的數據列表)

eg:db 3 dup (0,1,2) 一共占用3*3=9個字節

  • 實驗7

  • 代碼如下:


assume cs:code,ds:data,es:table

data segment

db '1975','1976','1977','1978','1979','1980','1981','1982','1983'

db '1984','1985','1986','1987','1988','1989','1990','1991','1992'

db '1993','1994','1995'

dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514

dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000

dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226

dw 11452,14430,15257,17800

data ends

table segment

db 21 dup ('year summ ne ?? ')

table ends

code segment

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

s1:

mov ax,[bx]

mov es:[di],ax

mov ax,[bx+2]

mov es:[di+2],ax

mov byte ptr [di+4],' '

mov ax,84[bx]

mov es:[di+5],ax

mov ax,84[bx+2]

mov es:[di+7],ax

mov byte ptr [di+9],' '

mov ax,168[si]

mov es:[di+10],ax

mov byte ptr [di+12],' '

mov dx,86[bx]

mov ax,84[bx]

div word ptr 168[si]

mov es:[di+13],ax

mov es:[di+15],' '

add bx,4

add si,2

add di,16

loop s1

mov ax, 4c00h

int 21h

code ends

end start

第九章 轉移指令的原理

  • offset操作符

  • 操作符offset在匯編語言中是由編譯器處理的符號.功能是獲取標號的偏移地址

  • 段標號名稱就代表地址,所以不需要offset 操作符.

  • Nop指令占用一個字節

  • 轉移指令分類

  • 可以修改ip或同時修改CS或IP的指令統稱為轉移指令:

(1).只修改ip:段內轉移

  • 8位數值范圍的修改IP,稱為短轉移

  • 修改范圍為16位的稱為近轉移

(2).同時修改Cs和IP時,稱為段間轉移.

  • 8086CPU的轉移指令分為以下幾類:

(1)無條件轉移指令(如:jmp)

(2)條件轉移指令

(3)循環指令

(4)過程

(5)中斷

  • 無條件跳轉jmp指令

  • 跳轉到標號:

  • 短轉移:jmp sort 標號 (機器碼中包含的是8位位移)

  • 近轉移:jmp near ptr 標號(機器碼中包含16位位移)

位移計算:要跳轉的標號的偏移地址 減掉 跳轉指令的下一條指令的偏移地址,位移在編譯時由匯編器算出.

  • 遠轉移:jmp far ptr 標號,cs=標號段地址,ip=標號偏移地址,該指令機器碼中不是用的位移表示.而是包含了轉移地址4個字節.機器碼的高位字單元是段地址,低位時偏移地址

  • 轉移地址在寄存器:jmp 16位寄存器

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

  • 段內轉移:jmp word ptr 內存單元地址

  • 段間轉移:jmp dword ptr 內存單元地址(高字單元是段地址,低地址字單元是偏移地址)

  • 條件轉移指令jcxz

  • 所有條件轉移都是短轉移.,在對應的機器碼中包含轉移的位移.

  • 格式:jcxz 標號,當cx=0時就跳轉到標號處.

  • 例子程序:補全程序,利用jcxz指令,實現在內存2000h段中,查找第一個值為0的字節,找到后,將它的偏移地址存儲在dx中


assume cs:code

code segment

start:

mov ax,2000h

mov ds,ax

mov bx,0

s:

mov cx, 0

mov cl,[bx]

jcxz ok

inc bx

jmp short s

ok:

mov dx, bx

mov ax, 4c00h

int 21h

code ends

end  start

  • loop指令

  • 所有的循環指令都是短轉移.對應的機器碼包含的是位移.

  • 例子程序:例子程序:補全程序,利用loop指令,實現在內存2000h段中,查找第一個值為0的字節,找到后,將它的偏移地址存儲在dx中


assume cs:code

code segment

start:  mov ax,2000h

mov ds,ax

mov bx,0

s:mov cl,[bx]

mov ch,0

inc cx

inc bx

loop s

ok:dec bx

mov dx,bx

mov ax,4c00h

int 21h

code ends

end start

inc cx是防止當cx為0時,loop指令會首先cx=cx-1會變成0ffffh,就會循環ffff次.

  • 實驗

  • 實驗8:


assume cs:codesg

codesg segment

mov ax,4c00h

int 21h

start: mov ax,0            ax=0

s: nop                占一字節,機器碼90

nop                占一字節,機器碼90

mov di,offset s    (di)=s偏移地址

mov si,offset s2    (si)=s2偏移地址

mov ax,cs:[si]      (ax)=jmp short s1指令對應的機器碼EBF6

mov cs:[di],ax      jmp short s1覆蓋s處指令2條nop指令

s0: jmp short s        執行到這里,不會再繼續向下執行,直接跳回mov ax,4c00h了

s1: mov ax,0

int 21h

mov ax,0

s2: jmp short s1

nop

codesg ends

end start

因為jmp short s1<=>EB 位移.位移等于S1標號偏移地址減去nop指令的偏移地址大約為-10.當跳轉到s處執行jmp short s1,實際上是執行EB -10.會向前跳轉10個字節,也就是說會執行程序返回的代碼.

  • 實驗9:根據材料編程:在屏幕中間分別顯示綠色,綠底紅色,白色藍底的字符串’welcome to masm!’

  • 顯示字符相關知識: B8000h-bffffh共32kb的空間,為80*25彩色字符模式的顯示緩存區.一個字符占兩個字節,后面的字節是該字符的一些顏色屬性信息.所以一屏幕內容占緩沖區4000b約等于4kb


assume cs:code, ds:data

data segment

db 'welcome to masm!'

data ends

code segment

start:

mov ax, data

mov ds, ax

mov bx,0

mov ax,0b800h

mov es, ax

mov di,11*160+72

mov cx,16

s:

mov al,[bx]

mov es:[di], al

mov byte ptr es:[di+1],00000010b

mov es:160[di],al

mov byte ptr es:161[di],00100100b

mov es:320[di],al

mov byte ptr es:321[di],01110001b

add di,2

inc bx

loop s

mov ax, 4c00h

int 21h

code ends

end  start

第十章 call和ret指令

  • ret和retf指令

  • ret<=>pop ip<=>(ip)=((ss)*16+(sp)),(sp)=(sp)+2

  • retf<=>pop ip,pop cs <=>(ip)=((ss)_16+(sp)),(sp)=(sp)+2

(cs)=((ss)_16+(sp)),(sp)=(sp)+2

  • call指令

  • call指令執行的步驟

  • 將當前call指令后的下一條指令的ip,或cs和ip壓入棧中

  • 跳轉到目標地址執行

(sp)=(sp) - 2

((ss)*16+(sp)) = (ip)

(ip)=(ip)+16位位移

  • call指令不能實現短轉移,此外轉移的方法同jmp指令一樣.

  • call 標號:16位位移=標號處的地址-call指令后的第一個字節的地址, 位移范圍為-32768~32767

有等同于 push ip,jmp near ptr 標號

  • call far ptr 標號:實現的是段間轉移.

push cs,push ip, jmp far ptr 標號

  • 轉移地址在寄存器中的call指令:call 16位reg

push ip, jmp reg

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

  • call word ptr 內存單元地址

push ip, jmp word ptr 內存單元地址

  • call dword ptr 內存單元地址

push cs, push ip,jmp dword ptr 內存單元地址

  • call和ret的配合使用

  • 子程序框架:


assume cs:code

code segment

main:

call sub1

:

:

call sub2

:

:

mov ax, 4c00h

int 21h

sub1:

:

:

sub2:

:

code ends

所以c語言中的函數名其實就是一個標識子程序的地址

  • mul指令

  • 要求:

  • 兩個相乘的數,要么都是8位,要么都是16位,如果是8位,那么一個乘數默認放在AL中,另一個放在8位reg或內存單元中,如果是16位,一個默認在ax中,另一個放在16位reg或內存單元中

  • 結果:如果是8位乘法,結果默認放在AX中,如果是16位乘法,結果高位默認放在dx中,低位放在ax中.

  • 格式:mul reg/內存單元(必須指明內存單元尺寸)

  • 例子:計算100*10,因為兩個乘數都是8位,所以用8位乘法


mov al,100

mov bl,10

mul bl

計算 100 * 10000,必須用16位乘法


mov ax,100

mov bx,10000

mul bx

  • 模塊化程序設計

  • 參數和結果傳遞問題:

  • 用寄存器傳遞參數和用寄存器存儲函數結果值

  • 例子:計算n的立方,用bx傳遞參數dx和ax返回結果值


cube:

mov ax,bx

mul bx

mul bx

ret

  • 參數的批量傳遞:可以將參數放在一組內存單元中,然后用si等間接寄存器指向它.返回值也可以這樣做.

  • 例子:將一個si指向的全是字母的字符串轉換為大寫


capital:

and byte ptr [si], 11011111b

inc si

loop capital

ret

  • 用棧來傳遞參數

  • 寄存器沖突問題:

  • 例子程序:將一個全是字母,以0結尾的字符串,轉化為大寫si指向該字符串


capital:mov cl, [si]

mov ch,0

jcxz ok

and byte ptr [si],11011111b

inc si

jmp short capital

ok : ret

如果調用該子程序中的代碼中使用了cx寄存器,將產生錯誤.解決辦法是在該子程序開頭壓棧保存需要使用的寄存器,在返回前彈出該寄存器的值.改正如下


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

ret

  • 所以子程序框架如下:

子程序標號:

子程序中使用的寄存器入棧保存

子程序指令標號:

....

子程序返回標號:

子程序中保存的寄存器出棧恢復

子程序返回(ret,retf)

  • 實驗10 編寫子程序

  • 顯示字符串子程序:show_str

  • 功能:在指定的位置,用指定的顏色,顯示一個用0結束的字符串

  • 參數:(dh)=行號(取值范圍024),(dl)=列號(取值范圍是079).(cl)=顏色,ds:si指向字符串的首地址

  • 返回值:無


show_str:

push ax

push bx

push cx

push dx

push es

push si

push di

begn:

mov ax,0b800h

mov es,ax

mov al,160

mul dh

mov bx,ax

mov al,2

mul dl

add bx,ax

mov di,bx

mov al,cl

mov ch,0

s:

mov cl,[si]

jcxz retn

mov es:[di],cl

mov es:[di+1],al

inc si

add di,2

jmp short s

retn:

pop di

pop si

pop es

pop dx

pop cx

pop bx

pop ax

ret

  • 解決觸發溢出問題子程序:

  • 名稱:divdw

  • 功能:進行不會產生溢出的除法運算,被除數位dword型,除數位word型.結果為dword型

  • 參數:(ax)=dword型數據的低16位,(dx)=dword型數據的高16位,(cx)=除數

  • 返回:(dx)=結果的高16位,(ax)=結果的低16位,(cx)=余數


divdw:

push ax

push bx

push cx

push dx

begn:

mov bx,ax

mov ax,dx

mov dx,0

div cx

push ax

mov ax,bx

div cx

mov cx,dx

pop dx

retn:

pop dx

pop cx

pop bx

pop ax

ret

  • 將數值以十進制形式顯示:

  • 名稱:dtoc

  • 功能:將word型數據轉變為表示十進制數的字符串,字符串以0結尾

  • 參數:(ax)=word型數據,ds:si指向字符串首地址

  • 返回值:無


dtoc:

push ax

push bx

push cx

push dx

push ds

push si

push 0

begn:

mov dx,0

mov bx,10

div bx

mov cx,ax

jcxz sw1

add dx,30h

push dx

jmp short begn

sw1:add dx,30h

push dx

sw2:

pop cx

jcxz retn

mov [si],cl

inc si

jmp short sw2

retn:

mov [si+1],0

pop si

pop ds

pop dx

pop cx

pop bx

pop ax

ret

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 8086匯編 本筆記是筆者觀看小甲魚老師(魚C論壇)《零基礎入門學習匯編語言》系列視頻的筆記,在此感謝他和像他一樣...
    Gibbs基閱讀 37,371評論 8 114
  • 計算機通過執行指令序列來使機器得以工作,所以對于每一系列的計算機都有指定的一組指令集供計算機使用,這組指令...
    未來科技工作室閱讀 8,128評論 1 10
  • 匯編總結 匯編的發展史 機械語言 由0和1組成的機器指令(如:0101 0001 1101 0110) 匯編語言(...
    iChuck閱讀 1,336評論 1 8
  • ![Uploading 圖片_484415.png . . .] 校驗和格式化工具:JSONLint 用jQuer...
    Hathaway_桉閱讀 347評論 0 0
  • 你來 我欣喜 并 赴湯蹈火 你走 我戀之不得 惟 再無所求 仿佛一場花開 在夢中 夢醒時 卻 冷痛 無以復加
    未可_Win閱讀 211評論 3 9