1. 程式人生 > 實用技巧 >彙編基礎

彙編基礎

第一章,組合語言產生

1,機器語言與組合語言一一對應

2,彙編指令:Mov AX,BX 將暫存器BX,移入AX

3,暫存器:CUP中的儲存器,注意不是CUP的快取,

4,彙編--》編譯器---》機器碼01

5,組合語言組成

  • 彙編指令,機器碼的助記符
  • 偽指令,編譯器執行
  • 其他符號,編譯器識別

6,一串機器碼,可以使指令,也可以是資料,就看是cd,ds那個指向他

7,地址資訊,讀或寫,資料資訊

​ cup與所有記憶體之間:地址匯流排,資料匯流排,控制匯流排,每條線對應不同資訊,指令與資料分開

8,匯流排

  • 地址匯流排:產品cpu通過它指定儲存單元,地址匯流排有多少不同資訊,就是CPU的定址範圍,64位CPU就是64個地址匯流排
  • 資料匯流排:寬度決定傳輸速度,例如一次傳8bit,16bit
  • 控制匯流排:寬度表示控制能力

9,例

地址:0-7FFFH,為RAM

地址:8000H-9FFFH,為視訊記憶體地址:當資料寫入此處,就會顯示

地址:A000H-FFFFH為各個ROM

10,8086CPU如下,8086,8088,龍芯,酷睿等都是類似分配

第二章,暫存器(CPU工作原理)

通用暫存器

1,CPU:運算器,控制器,暫存器,內部匯流排相連線

2,8086cpu暫存器:共14個,有8個通用暫存器

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

  • 所有暫存器16bit,16位的CPU,8086上一代8bit

  • AX,BX,CX,DX通用暫存器,可以分為兩個獨立的8位暫存器使用 AX-->AH,AL

暫存器中資料大多是n*8bit

彙編指令

實體地址

16位結構CPU

1,一次16bit資料

2,暫存器最大寬度16bit

3,暫存器運算器之間通路16bit

8086CPU給出實體地址

1,20位地址匯流排,定址1m

2,內部16位結構,定址64kb

方法:

3,實體地址=段地址*16+偏移地址 因為是2進位制

1,記憶體並沒有分段,分段是因為cup,內部不足,但因此使用分段管理記憶體

2,偏移地址16bit,因此每段64KB

3,CUP可以不痛段地址與偏移構成相同的實體地址

段暫存器

8086:CS,DS,SS,ES

cs:指令段暫存器,ip指令偏移暫存器 ----》段,偏移

修改CS,IP

1,只能操作暫存器控制cpu,因此需要控制cs,ip

2,mov 傳送指令,可修改ax,但不能cs,ip

3,jmp 2ae3:3 跳轉的意思 此時cs 為2ae3,ip為3

程式碼段

將n<64kb的資料 ---》程式碼段

將cs,ip指向該程式碼段,會認為是指令,才會執行,執行後ip = ip + 指令長度

第三章,暫存器

記憶體中的儲存

1,例:數字2000 (4E20H)

任何兩個連續單元,N,N+1,可以看做兩個記憶體單元,也可以看做地址為N的字單元中的高位位元組單元,低位位元組單元

DS與Address

DS:存放的資料的段地址,

1,執行指令時,自動取DS中資料為記憶體單元的段地址

2,mov al,[0] 將ds:0 記憶體單元中的資料移入al

3,但不能直接把值移入ds ,mov ds 1000H:錯誤的 8086cpu不支援放入段暫存器,硬體問題。只能資料 --》通用暫存器--》段暫存器

字的傳送

mov,add,sub

1,mov

  • mov 暫存器,資料
  • mov 暫存器,暫存器
  • mov 暫存器,記憶體
  • mov 記憶體,暫存器
  • mov 段暫存器,暫存器
  • mov 暫存器,段暫存器 ,也可以

資料段

push ax:將ax放入棧記憶體中

​ sp =sp-2

​ ss:sp 指向新地址

pop ax:從棧記憶體取資料到ax

1,cup如何知道某段記憶體是棧記憶體?

ss,棧頂段暫存器,sp棧頂偏移 ss:sp指向棧頂,棧為空時指向棧頂下一位,

出棧時只是修改索引,資料還未覆蓋,由此可見,硬碟類似

2,cpu如何知道哪個是棧頂,棧底?從大地址向小地址寫資料

3,語言中的函式,呼叫時就是申請一塊棧記憶體,執行完,棧記憶體元素全部出棧,因此區域性變數失效,棧記憶體由編譯器管理,堆記憶體有程式設計師管理

棧頂越界

1,cpu關心棧只關心棧頂指標在何處,當前要執行的指令是哪個

2,c,c++常會溢位

POP,PUSH

1,棧空間,一段固定讀取格式的記憶體

棧段

1,可以將小於64kb的記憶體當做棧

2,認為10010H,1001FH 當做16位元組棧,但cup只關心棧頂,不會關心棧段的大小

第四章,第一個程式

彙編---》可執行

1,流程

  • 編寫,

  • 編譯程式(masm.exe),產生目標檔案

  • 連線程式(link.exe),生成可執行檔案

2,可執行程式組成

  • 程式,資料(程式中定義的資料)
  • 描述資訊,程式多大,佔空間多大等

3,執行過程

  • 按照可執行檔案描述資訊,將程式,資料載入到記憶體,設定cs,ip等

4,彙編指令,偽指令

  • segment .... ends:定義一個段,程式段,資料段等,一個程式有多個段

  • end:便已結束標記

  • assume:假設,將段指定為資料段,程式段等,偽指令指向程式開始等等

    • assume cs:codesg
      codesg segment
      start: mov ax 0123H
      codesg ends
      end
      

源程式

1,一個標號只帶一個地址

2,codesg:給段的命名,最後會變為地址

3,程式返回,

  • 返回使它執行的那個程式
    • mov ax,4c00H
    • int 21H
    • 這兩個規定的語句實現中斷

編輯源程式

assume cs:abc
abc segment
	mov ax,2
	add ax,ax
	add ax,ax
	mov ax,4c00H
	int 21H
abc ends
end

masm 編譯,link 連結 生成exe檔案

“;”可以簡化masm,link

ml:兩部合起來

# 有入口的檔案
assume cs:abc
abc segment
start:mov ax,2
	add ax,ax
	add ax,ax
	mov ax,4c00H
	int 21H
abc ends
end start

1,exe檔案載入過程

程式載入後,記憶體地址為ds:0

其中前256是dos與程式通訊的,256向後是程式

第五章,bx與loop

注意:在debug中[0]表示段地址的偏移地址,在masm中表示數值0

bx

mov bx ,0

mov ax,[bx] ,此時[bx]表示以bx內容位偏移地址

1,將bx中的資料作為偏移地址

inc bx # 自加

loop

1,loop 標號

  • cx = cx-1
  • 判斷cx中的值,不為零轉到標號執行處,若果為零則向下執行
  • cx中存放的一般是迴圈次數

2,2^1000

assume cs:code
code segment
mov ax,2
mov cx,11
s:add as,ax
loop s # s就是標號   判斷cx,是否跳到標號

3,越位賦值會可能出錯 mov ax,[bx]

mov al,[bx]

mov ah,0

4,組合語言中數字以字母開頭,例如大於9fffh的,Afffh,需要在前邊加0

5,使用deubg追蹤迴圈

g 命令地址:相當於 debug中的段點

p :在迴圈時使用,會直接跳到迴圈結束,即自動完成迴圈過程

注意

在masm中

mov al,[0] 將0移入al

mov al,ds:[0] ,偏移為0的地址中內容放入al

mov al,[bx],將bx為偏移地址的地址中的內容放入al

mov al,ds:[bx],同上

loop,bx聯合應用

記憶體數值相加時的問題

例:ffff:0---ffff:b 12個位元組相加,

問題:放入16位ax,會不會越界,8位如何放入16位暫存器

解決方法:使用中介,將8位資料放入16位中介暫存器ax,再相加

assume cs:code
code segment
	mov,ax,0ffffh
	mov,ds,ax # 設定段地址
	
	mov dx,0 # 初始化累加暫存器
	mov al,ds:[0]  # 賦值給中間暫存器
	mov ah,0 # 設定高位為0,此時ax為0
	add dx,ax # 累加
	...
	mov ax,4c00h
	int 21h

\(sum =\sum\limits_{X=0}^{0bh}(ffffh*10h+X)\)

# 優化:使用迴圈,偏移地址應該遞增
asume cs:code
code segment
	mov ax,0ffffh
	mov ds,ax
	mov bx,0
	
	mov dx,0
	
	mov cx,12
	
s:	mov al,[bx]
	mov ah,0
	add dx,ax
	int bx
	loop s
	mov ax,4c00h
	int 21h
code ends
end

段字首

ds就是段地址,也稱為段字首,ds是預設的段字首,其他還有cs,es等

一段安全的空間

在PC中,0:200到0:2ff是安全的空間

段字首的使用

在不同的段中操作時,一個ds需要多次更改,可能需要多個段暫存器,例如用es替代。效果更好

第六章,包含多個段的程式

程式碼段中使用資料

dw 0123h,0456h 定義字元型資料

db 45h,78h 定義位元組型資料

當上面定義在cs中時,資料段地址就是程式碼段的段地址

dw在第一行定義,資料地址偏移為0,2,4,6....

assume cs:code
code segment 
	dw 0123h,0456h,0789h
	start:mov bx,0
	mov ax,0
	mov cx,0
	s:add ax,cs:[bx]
	add bx 2
	loop s
	mov ax 4c00h
	int 21h
code ends
end start
# cup讀指令的時候會從start開始,若不新增start,應為dw的存在,存放的是資料,無法轉化為機器指令,程式無法執行
# end的作用:通知編譯器程式結束,告訴編譯器程式入口在哪裡,當不指定入口時,會按照上到下執行,因此若現定義資料,會把資料當做指令執行

程式碼段中使用棧

問題:將上面程式中的資料逆序存放

assume cs:code
code:segment
	dw 0123h,0456h,0789h
	dw 0,0,0 # 定義資料當做棧空間
	
	start :mov ax,cs
	mov ss,ax   # 指定棧段
	mov sp,32	# 指定棧偏移,棧指標,操作時指標自動變
	mov bx,0
	mov cx,3	# 迴圈次數
	
	s:push cs:[bx] # 入棧資料
	add bx,2
	loop s
	
	mov bx,0
	mov cx,3
	
	s0:pop cs:[bx]
	add bx,2
	loop s0
	
	mov 4c00h
	int 21h
code ends
end start

將資料,程式碼,棧放入不同的段

上面的逆序排放

assume cs:code,ds:data,ss:stack
data segment
	dw 0123h,0456h,0789h
data ends
stack segment
	dw 0,0,0
stack ends
code segment
start:mov ax,stack  # cs,ss,da 系統載入程式時會有系統指定載入的位置,cs 確定後,ds應該就是cs-2,ss=cs-1
	  mov ss,ax
	  mov sp,6  # 指定棧指標
	  mov ax,data 
	  mov ds,ax # 資料段地址
	  
	  mov bx,0
	  mov cx,3
	  
	  s:pop [bx]
	  add bx,2
	  loop s
	  
	  mov bx,0
	  mov cs,8
	  s0:pull [bx]
	  add bx,2
	  loop s0
	  
	  mov ax,4c00h
	  int 21h
code ends
end start

第七站,更靈活的定位記憶體地址的方法

and和or指令

  • and
    • 兩個都是1為1,其他是0 ,add al 10100010B
    • 可以把暫存器某一位設定為0:and al,10111111B
  • or
    • 有一個1就為1
    • 使得暫存器某一位為1:or al,00000100B

關於ASCII

鍵盤按下a鍵:鍵盤產生61h,放入記憶體空間,編輯器軟體從指定記憶體讀取,送到視訊記憶體中

以字元形式給出的資料

'....'指明資料是以字元形式給出,編譯器會轉化為ASCII

assume ds:data
data segment
db 'unix'  # db 75H,6EH,49H,58H
db 'fork' # ...
data ends
code segment
start:mov al,'a' # mov al,61h
	mov b1,'b'
	mov ax,4c00h
	int 21h
code ends
end start

大小寫轉化問題

A:65,a:97

大寫:第五位為0

小寫:第五位為1

and 小寫,11011111b

or 大寫,00100000b

[bx+idata]

mov ax,[bx+200]:將地址內資料放入ax,這個地址是bx中的值+200

mov ax,200[bx] mov ax,[bx].200

用[bx+idata]方式進行陣列的處理

assume cs:code,ds:data
data segment
	db 'BaSiC'
	db 'MinIX'
data ends

code segment
start:mov as,data
	mov ds,ax
	mov bx,0
	
	mov cx,5
	s:mov al,[bx]
	and al,11011111b
	mov [bx],al
	
	mov al,[5+bx]   # 5[bx]
	or al,00100000b
	mov [5+bx],al
	
	inc bx
	loop s

code ends
end start

SI和DI

與bx類似的功能,但不能分為2個8位的使用,

bx不夠用的問題

# 將資料複製到後邊地址
# ds:si 指向原資料
# ds:di 指向目的地址
assume cs:code,ds:data
data segment
	db 'welcome to masm!'
	db '...............'
data ends

code segment
start:mov ax,data
	mov ds,ax
	mov si,0
	mov di,16
	mov cs,8
	s:mov ax,[si]
	mov [di],ax
	add si,2
	add di,2
	loop s
	
	mov ax,4c00h
	int 21h
code ends
end start

[bx+si]和[bx+di]

兩個功能類似

[bx+si] 表示一個記憶體單元,偏移地址為bx值+si值

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

表示記憶體單元,偏移地址,

不同定址方式的靈活應用

  • ds:[idata]
  • [bx]
  • [bx+idata]
  • [bx+si]
  • [bx+si+idata]

例:將資料段單詞,開頭變為大寫

assume cs:code,ds:data
data segment
db '1.file    '
db '2.editable'
db '3.help    '
data ends
code segment
start:	mov ax,data
		mov ds,ax
		mov bx,0
		
		mov cx,3
	s:mov al,[bx+2] # 把第二個字母放入al
 	add al,11011111b  # 修改字母,變為大寫
	mov [bx+3],al 	# 放回
	add bx,10
	loop s
	mov ax,4c00h
	int 21h
code ends
end start

對於多層迴圈,需要其它暫存器記下外層cx的值,內層迴圈結束後恢復,不然會遇到死迴圈

當暫存器不夠時,需要使用記憶體了,如使用棧,每層cx入棧 push cx,結束時出棧pop cx

資料處理的基本問題

引言

  • 處理的資料在哪裡

  • 處理的資料有多少,多長

暫存器:ax,bx,cx,dx ah,al,bh,bj,ch,cl,dh,dl

sp(棧指標),bp, si(原資料地址),di(目的資料地址)

段暫存器:ds(資料段),ss(棧段),cs(程式碼段),es(擴充套件段)

bx,si,di,bp

  • 8086可以用[...]表達記憶體地址的暫存器:bx bp si di,[ax]是錯誤的
  • 這幾種不能任意組合
    • bx,si
    • bx,di
    • bp,si
    • bp,di
    • 其他的組合是錯誤的,例如:mov ax,[bp+si]
  • 在[...]使用bp,時段地址是ss,即bp就像是sp

機器指令處理的資料所在位置

指令執行前,資料所在位置:CUP,記憶體,埠

彙編中資料位置的表達

  • 立即數:直接包含在機器指令中的資料,存在於指令緩衝區:mov ax,1 :b80100

    • B8表示移動立即數到ax
    • 指令暫存器:
      • 就是cpu讀取一條指令
      • 經過資料匯流排條指令存出入指令緩衝器
      • 讀取該指令要操作的資料,放入地址加法器
      • 資料放入目的地址
  • 暫存器 mov ax,bx:89D8

  • 段地址和偏移地址

    • mov ax,ds:[bp] 強制改變段地址

定址方式

指令要處理的資料有多長

8086可操作byte,word,在機器指令中需要指明進行操作的是字元還是位元組

  • 通過暫存器指名要處理的資料
    • mov ax,bx
  • X ptr指明記憶體長度
    • mov word ptr ds:[0],1
    • inc word ptr [bx]
    • inc word ptr ds:[0]
    • add word ptr [bx],2
    • mov byte ptr ds:[0],1
    • 等等,
  • 其他
    • 棧指令,操作時,預設都是按照字元操作

定址方式的總和應用

  • 字串在記憶體中就是陣列
  • 一般用[bx+idata+si]訪問結構體
    • bx表示結構體
    • idata表示某一個數據項
    • si表示資料項中每個元素
    • student.name[i]

divide指令

  • 除數:8位,16位,暫存器,記憶體都可以
  • 被除數:預設ax,dx與ax
    • 除數8位,被除數16位(dx)
    • 除數16位,被除數應該是32位(dx+ax)
  • 指令格式
    • div 暫存器
    • div 記憶體單元
      • div byte ptr ds:[0]
        • al = ax/0地址的商
        • ah = ax/0地址餘數
      • div word ptr es:[0]
        • ax = [(dx)*10000H+(ax)]/((ds)*16+0)的商
        • dx = [(dx)*10000H+(ax)]/((ds)*16+0)的餘數
      • div byte ptr [bx+si+8]
        • al = (ax)/((ds)*16+bx+si+8)的商
        • ah = (ax)/((ds)*16+bx+si+8)的餘數
      • div word ptr [bx+si+8]
        • ax = [dx*10000h+ax]/(ds*16+bx+si+8)的商
        • dx = [dx*10000h+ax]/(ds*16+bx+si+8)的餘數
      • 最後結果覆蓋原來被除數的位置

偽指令dd

  • 定義dword,即double word

  • data segment
    db 1 # 01H
    dw 1 # 0001H
    dd 1 # 00000001H
    data ends
    

dup

  • 同dw,dd,db一樣有編譯器識別處理的符號,用來資料重複

  • db 3 dup(0) # 定義了3個位元組0
    db 0,0,0
    db 3 dup(0,1,2)
    db 0,1,2,0,1,2,0,1,2
    db 2 dup('abc,'ABC)
    db 'abcABCabcABC'
    
    stack segment
    dw 0,0,0,0,0,0...
    stack ends
    stack segment
    db 200 dup(0)
    stack ends
    

第九章,轉移指令的原理

8086CPU轉移指令分類

  • 無條件轉移指令 例jmp
  • 條件轉移指令
  • 迴圈指令 例loop
  • 過程
  • 中斷

操作符offset

由編譯器處理的符號,取得標號的偏移地址嗎,就標號所在地址

assume cs:code
code segment
	start:mov ax.offset start  # 相當於mov ax,0
	s:mov ax,offset s # 相當於mov ax,3
code ends
end start

jmp指令

  • cs,ip
    • 可以只修改ip
    • 也可以修改cs
  • 轉移要求
    • 目的地址
    • 轉移的距離 (-128----》127)段內跳轉
      • jmp start 標號

依據位移進行jmp指令

movax,0123 		# B8 23 01
mov as,ds:[0123]# A1 23 01
push ds:[0123]	# FF 36 23 01

jmp指令對應的機器碼中沒有目的地址,是相對於jmp所在指令的偏移

  • jmp near ptr 標號 實現段內近轉移:
    • IP=IP+16位位移
    • near ptr 指明此處為16位位移,進行的是段內近轉移
    • 16位位移 = 標號處地址減去jmp指令後第一個位元組地址

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

  • 偏移地址是實際的地址
  • 段間轉移,遠轉移
  • jmp fat ptr 標號:修改了cs:ip ---->EAXXXXYYYY

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

jmp 16位暫存器

jmp ax

轉移地址在記憶體中的jmp指令

  • jmp word ptr 記憶體
  • jmp dword ptr 記憶體,段之間轉移,32位 高16位cs,低16位ip

jcxz指令

  • 有條件轉移
  • (-128--127)
  • 機器碼包含位移,而不是目的地址
  • jcxz 標號,
    • cx=0 跳轉,否則不跳轉,向下執行
    • ip = ip+8位移
      • 負數用補碼錶示
      • 有編譯時編譯器算出

loop指令

  • 位移而不是目的地址
  • -128--127
  • 與cx配合
  • 都是編譯器指令

根據位移進行轉移的意義

使用位移可使程式在記憶體中任意位置呼叫

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

編譯時編譯器會報錯

注:

視訊記憶體地址:B8000H-BFFFFH dos系統的視訊記憶體地址

第十章,Call和Ret指令

都是修改cs,ip

ret與retf

  • ret使用棧中的資料,修改ip實現近轉移
  • cpu執行ret時
    • ip=ss*16+sp
    • sp=sp+2
  • cpu執行retf時
    • ip=ss*16+sp
    • sp=sp+2
    • cs=ss*16+sp
    • sp=sp+2
  • ret執行後 ip=0,cs:ip指向程式碼段第一條指令
  • retf執行後,cs:ip指向程式碼段第一條指令

call

  • 與ret配合使用
    • 將ip,或cs,ip入棧
    • 轉移(jmp)
  • 不能短轉移

位移轉移的call

  • 將當前標號入棧後,轉到標號處執行

    • sp =sp -2
      • ss*16+sp=ip
    • ip = ip+16位位移
  • 相當於進行push ip,jmp near ptr 標號

直接地址轉移的call

call far ptr 標號
sp = sp -2
ss*16+sp=cs
sp = sp -2
ss*16+sp=ip

cs=標號所在段地址
ip=標號所在偏移地址

相當於push cs,push ip,jmp fat ptr 標號

暫存器轉移的call

sp =sp -2

ss *16+sp =ip

ip=16位暫存器

相當於push ip,jmp 16位暫存器

轉移地址在記憶體的call

  • call word ptr 記憶體
    • push ip
    • jmp word ptr 記憶體單元
  • call dword ptr 記憶體
    • push cs
    • push ip
    • jmp dword ptr 記憶體單元

call與ret的配合使用

可以使用call與ret配合寫子程式,就像是高階語言中的方法,使用call跳轉,ret返回

mul

  • 乘法指令
    • 相乘的2位數位數需要相同
    • 8位:AL中和8位暫存器或記憶體單元
    • 16位:AX中和16位暫存器或記憶體單元
  • 結果
    • 2個8位:放在ax
    • 2個16位:高位DX,低位AX
  • mul reg,mul 記憶體單元
    • mul byte ptr ds:[0] ==(ax)=(al)*(ds*16+0)
    • mul word ptr [bx+si+8]
      • (ax)=(ax)*((ds)*16+(bx)+(si)+8) 低8位
      • (dx)=(ax)*((ds)*16+(bx)+(si)+8) 高8位
  • 例100*10
    • mov al,100
    • mov b1,10
    • mul bl
    • ax = 1000(03E8H)
  • 100*10000
    • mov ax,100
    • mov bx,10000
    • mul bx
    • ax=4240H
    • dx=000FH

模組化程式設計

如何儲存子程式引數,與返回值

  • 子程式計算N的3次方

    • N儲存位置

      • 暫存器
    • 結果儲存位置

      • dx,ax
    • bx = N ;型參
      dx:ax=N^3
      
      cube:mov ax,bx
      mul bx
      mul bx
      ret
      

引數和結果傳遞問題

用暫存器儲存引數與結果最常用的方法

批量資料傳遞

傳遞的資料多的時候怎麼辦?使用記憶體,或者棧,高階語言就是棧

assume cs:code
data segment
	db 'conversation'	
data ends
code segment
start:mov ax,data
	mov ds,ax
	mov si,0	;ds:si指向字串所在空間首地址
	
	mov cx,12	;cx存放字串長度
	call capital
	
	mov ax,4c00h
	int 21h
capital:and byte ptr [si]:11011111b
	inc si
	loop capital
	ret
code ends
end start

暫存器衝突

上面的程式,當不知道字串長度時,如何做

db 'conversation','0'

capital:mov cl,[si]
	mov ch,0
	jcxz ok	;若果cx=0,結束,若不是,接著處理
	and byte ptr [si],11011111b
	inc si
	jmp short capital
ok:ret
	

第十一章,標誌暫存器

引言

計算機中的資料可以看做是有符號數,也可以是無符號數

00000001B # 可看做無符號數1,或有符號數+1
10000001B # 可看做無符號數129,或有符號數-127

flag標誌暫存器與其它不同,其他是用來存放資料的,flag是按位起作用

1,3,5,12,13,14,15在8086中沒有使用

ZF,零標誌位

  • 上一條指令結果為0,此時標誌位ZF=1
    • mov ax,1
    • sub ax,1
    • 此時ax=0
  • 否則ZF=0
    • mov ax,2
    • sub ax,1
    • ax=1
  • add,sub,mul,inc 等等都是運算指令,mov push等都是傳送指令

PF,奇偶標誌位

  • 指令執行後,結果中所有二進位制中1的個數
    • 偶數,PF=1
    • 奇數,PF=0

SF,符號標誌位

  • 指令執行後結果的正負情況

  • SF=1,結果為負

  • SF=0,結果為正

  • mov al,10000001B
    add al,1
    ;結果 al=100000010B
    ;有符號位 -12 6
    ;無符號位 130
    ;所以說該指令包含結果有2個
    
    • SF標誌,就是CPU對有符號數運算結果的一種記錄,它記錄資料正負,Cpu會影響SF標誌位,但Cpu並不識別資料有無符號
  • 但我們把資料當做無符號數,SF則無實際作用

    • mov al,10000001B
      add al,1
      ;結果為10000010B,SF=1
      ;如果指令是進行有符號運算,那麼結果為負數,否則不用處理
      ;注意沒有-0的說法,只有+0
      

CF,進位標誌位

儲存上一條指令執行後是否有資料進位,或借位(減 法)

  • 有進位 CF=CY=1
  • 無進位 CF=NC=0

減法

  • 97H-98H 在計算機中就是
  • 197H-98H = FF,CY=1

OF,溢位標誌位

超出了機器的範圍

  • 溢位是針對有符號位,正+正邊負
  • 進位是相對於無符號位,例如8位邊9位
  • 溢位時OF=OV=1

對於cpu的計算結果,若做有符號位觀察,OF,SF,若看作無符號位觀察CF

總之,cpu計算時,不管有無符號,adc指令:結合符號位獲取正確值

mov ax,98d ; 0110 0010
add ax,99d ; 0110 0011

;1100 0101  C5 SF=1,OF=1,CF=0
;當做無符號位相加,C5
;當有符號位,首位為1,SF=1表示是負數,是-59的補碼,

adc指令

帶進位加法指令,利用CF位記錄值

adc ax,bx ax = ax+bx+CF

add,配合adc可以解決這些問題

;1EF000h+201000h 高16位ax,低16位bx
mov ax,001EH
mov bx,0f000H
add bx,1000H ; 0000H cf=1
adc ax,0020H ; 

adc執行後也會可能產生進位

;1E F000 1000h+ 20 1000 1EF0h 高位16ax,次高位16bx,cx低16位
;思路 低16位相加,然後次16位adc,高16位adc
mov ax,001EH
mov bx,0f000H
mov cx,1000H

add cx,1ef0h
adc bx,1000h
adc ax,0020h

更大的資料可以放在棧或記憶體中

sbb指令

帶位減法指令

sbb ax,bx = > ax= ax-bx-cf

與加法類似

cmp指令

相當於減法指令,但不儲存結果,僅僅對標誌位進行設定

cmp ax,ax ===> 此時ax不變,僅影響flag ---》ZF=1,PF=1,SF=0,CF=0,OF=0

有符號位比較

ZF

需要考慮OF,SF

  • OF=0,SF=1 AH<BH
  • OF=1,SF=1 AH>BH
  • SF=0,OF=1 AH<BH
  • OF=0,SF=0 AH>BH

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

與cmp配合

cmp ah,bh
je s
add ah,bh
jmp short ok
s:add ah,ah
ok:ret

DF標誌和串傳送指令

  • flag第10位,方向標誌位

  • 在串指令中,控制每次操作後si,di增減

  • DF=0,每次操作後遞增

  • DF=1遞減

  • movsb:以位元組為單位傳遞,將ds:si指向的記憶體中的資料送入es:di

    • es*16+di=ds*16+si
    • 若DF=0,SI=SI+1,DI=DI+1
    • 若DF=1;SI=SI-1,DI=DI-1
  • movsw 以字為單位傳送,即16位,si,di每次加減2

  • movsb,movsw與rep配合

    • rep movsb ,根據cx的值重複n次
  • cld:設定DF=0,std設定DF=1

  • assume cd:code
    data segment
    db 'welcome to masm!'
    db 16 dup(0)
    data ends
    code segment
    start:mov cx,16
    	cld;
    	mov ax data
    	mov ds,ax
    	mov si,0
    	mov di,16
    	mov es,ax
    	rep movsb
    	mov ax,4c00h
    	int 21h
    code ends
    end start
    
    

pushf和popf

pushf:將標誌暫存器的值壓入棧中

popf:彈出到標誌暫存器,出棧

第十二章,內中斷

內中斷的產生

外部中斷:外部裝置

內部中斷:內部錯誤

軟體中斷,int 21h等

CPU中斷優先權:

  • 除法錯誤,溢位中斷,軟體中斷
  • 不可遮蔽中斷
  • 可遮蔽中斷
  • 單步中斷

中斷處理程式

記憶體中有中斷處理程式

中斷向量表

  • 形式:序號 中斷程式地址

  • 地址用8bit,所以一共256箇中斷程式

  • int 序號 就會呼叫中斷處理程式

  • 對於8086CUP,中斷程式地址存放位置0000:0000---》0000:03FF,段地址16bit,偏移地址16bit,32*256

  • 8086中0:200-0:300安全的地址,因為並不是256個程式都要用

中斷過程

找到中斷程式地址,CPU設定cs,ip該過程為中斷過程

中斷過程:

  • 取得中斷型別碼
  • 標誌暫存器入棧
  • 設定標誌暫存器的第八位TF,第九位IF為0
  • CS,IP入棧
  • 讀入cs,ip

中斷處理程式

  • 儲存用到的暫存器
  • 處理中斷
  • 恢復暫存器
  • 用iret指令返回

iret:pop ip;pop cs;popf

iret和硬體自動完成中斷過程配合使用

除法錯誤中斷處理

程式設計處理0號中斷

發生除法溢位時,即結果比暫存器範圍大,Cpu將轉換為處理中斷程式

中斷向量表中該錯誤終端地址為0號地址

可以自定義中斷程式,修改中斷向量表,此時會執行自己的中斷程式

assume cs:code
code segment
start:
	mov ax,cs
	mov ds,ax
	mov si,offset do0;設定自定義終端程式位置
	mov ax,0
	mov es,ax
	mov di,200h	;設定es:di位置
	
	mov cx offset do0end- offset do0
	cld rep movsb ;中斷程式寫入記憶體
	;設定中斷向量表
	mov ax,4c00h
	int 21h
do0:
	xxxxx
	mov ax,4c00h
	int 21h
do0end:nop ;佔一個位元組,無作用
code ends
end start

單步中斷

CPU在執行一條指令後檢測到TF=1,就會引發中斷過程

單步中斷的型別碼為1,debug就是改變了1號中斷的程式,改為debug的程式

響應中斷的特殊情況

有時候遇到中斷訊號CPU也不會響應,

例如:

  • SS:SP的設定過程,ss,sp需要同時改變,因此不能被中斷
    • mov設定ss:sp時也要連續

第十三章,int指令

int指令

與call類似,int呼叫中斷程式

對int,iret與棧的深入理解

BIOS與dos中斷例程的安裝

  • 開機後CS:0FFFFH,IP=0, 該位置有一條跳轉指令,執行後專區bios的硬體檢測與初始化程式
  • 初始化程式,將BIOS所支援的中斷向量,即Bois提供的中斷例程入口記錄在中斷向量表
  • 完成後 int 19h進行作業系統的引導,將計算機交給作業系統
    • dos病毒就是改變 int 19h的中斷

BIOS中斷歷程應用

  • int 10h中斷的設定游標位置功能

    • 10h中包含多個子程式,通過ah,設定子程式的序號
    mov ah,2 ;2號子程式
    mov bh,0 ;0頁
    mov dh,5 ;5行
    mov dl,12 ;12列
    int 10h
    
  • int 21h

    • 程式返回功能
      • mov ah 4ch # 序號,程式返回的功能
      • mov al,0 # 返回值
      • int 21h
    • 游標顯示字元功能

第十四章,埠

埠的讀寫

  • 訪問記憶體

    • mov ax,ds:[8]
    • cpu 通過地址線將地址8資訊發出
    • cpu控制線發出記憶體讀命令,選中儲存器新編,通知他讀資料
    • 儲存器將8單元資料通過資料線送入cpu
  • 當問埠

    • 使用in,out
    • in al,60h
    • 地址匯流排60h發出
    • 控制線發出埠度命令,埠晶片,通知讀資料
    • 埠晶片通過資料線傳入cpu
    • 8位埠al,16位ax
      • in al,20h 讀取20h埠一個位元組
      • mov dx,3f8h
      • in al,dx 讀取改埠一個位元組

CMOS RAM 晶片

  • 包含一個實時鐘和128儲存單元的RAM
  • 該晶片靠電池供電,不是電源
  • 128單元中,內部時鐘佔用0-0dh,其他儲存系統配置資訊
  • 晶片內部有2埠70h,71h,cup通過他們進行讀寫
    • 70h地址埠,要訪問的RAM單元地址,
    • 71h是資料埠,要讀取或寫入的資料
    • 例:讀取RAM 2號單元
      • 2送入 70h
      • 從71 讀取2單元內容

shl與shr指令

  • 邏輯移位指令,左移與右移

  • 將暫存器或記憶體單元中資料左移,右移

  • shl

    • 資料左移

    • 最後移出的寫入CF

    • 最低位0補充

    • 若移位大於1,移動位數放在cl,不能直接用數字

    • mov  al,01001000b
      shl al,1
      ;al = 10010000b
      
  • shr 相反的操作

CMOS RAM中儲存時間資訊

  • 秒00H,分02h,時04h,日07h,月08h,年,09h

  • BCD格式碼存放

    • 四位01表示1-9
    • 0000-0,0001-1,0010-2 ..... 1001-9
    • 例如26:0010 0110

第十五章,外中斷

介面晶片和埠

外設的出入不是直接送入記憶體,而是相關介面晶片的埠中

外中斷資訊

  • 可遮蔽
  • 不可遮蔽
  • 需要看IF位,IF=1,指令執行後中斷,否則不中斷
  • sti設定TF=1,cli if=0
  • 不可遮蔽型別碼固定是2
    • 標誌位入棧,IF=0,TF=0,表示此程式不能被中斷
    • cs,ip入棧
    • ....
  • 外設幾乎都是和遮蔽中斷

PC鍵盤的處理過程

  • 按下一個鍵
  • 鍵盤晶片產生掃描碼
  • 送入主機板介面晶片暫存器,暫存器埠60h
  • 鬆開時也會送入
  • 到達60h時,傳送int 9 中斷
  • Bios int9中斷例程,
    • 讀取60h埠掃描碼
    • 是字元,將掃描碼與ascii送入bios鍵盤緩衝區(因為有些操作在作業系統之前),緩衝區中 一個按鍵16位 8位掃描碼,8位ASCII
    • 是控制鍵,轉換為狀態位元組,寫入記憶體中儲存狀態的位元組單元
  • cpu檢測到,若IF=1,引發中斷去執行int9

編寫int 9 中斷例程

安裝新的int 9終端例程

第十六章,直接定址表

描述了單元長度的標號

assume cs:code
code segment
a:db 1,2,3,4,5,6,
b:dw 0
start :mov si,offset a;將a的偏移地址給si
code ends
end start
;若標號後邊沒有“:”他們可以同時描述記憶體地址與單元長度

在其他段中使用資料標號

有“:"的地址標號只能在程式碼段使用

若要使用資料標號訪問資料,需要assume 將暫存器與段對應

例如上面的資料,訪問12345678中的1 需要設定ds,si

若使用a,則mov ax,[0]即可不用管ds,因為assume ds:data

  • 儲存地址

    • data segment
      a db 1,2,3,4,5,6,7,8
      b dw 0
      c dw offset a,seg a,offset b,seg b ;
      c dd a,b ;32bit儲存地址
      data ends
      

直接定址表

程式入口地址的直接定址表

第十七章,BIOS鍵盤的輸入磁碟讀寫

int 9 中斷對鍵盤輸入的處理

鍵盤緩區15個字單元,儲存掃描碼,ascii

int 16 讀取鍵盤緩衝區

mov ah,0
int 16h 
;從鍵盤緩衝區讀取一個,並刪除它
; ah=掃描碼,al=Ascii

int 16h的0號功能

  • 檢測緩衝區是否有資料
  • 沒有繼續上一步
  • 讀取緩衝區第一個字
  • 送入ax
  • 緩衝區已讀取那個字刪除

int 9向緩衝區寫資料,int16h讀資料,程式設計接受使用者輸入時,就是int16h

字串的輸入

  • 要求
    • 能夠輸入
    • 輸入時顯示
    • 可以刪除
  • 使用棧

int 13h

柱面號,磁軌號,扇區號

讀取:

ah = int 13h ;功能號,2表示讀取,3表示寫入
al=讀取扇區數
ch=磁軌號
cl=扇區號
dh=磁頭號
dl=驅動號 0:軟碟機A,1:軟碟機B   80h:硬碟C,81h:硬碟D
ex:bx 向此區域讀入資料

ah=0,al = 讀入的扇區數
ah=出錯程式碼

寫入同上

  • 直接寫入可能覆蓋資料,危險