彙編基礎
第一章,組合語言產生
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)的餘數
- 最後結果覆蓋原來被除數的位置
- div byte ptr ds:[0]
偽指令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位位移
- sp =sp -2
-
相當於進行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=出錯程式碼
寫入同上
- 直接寫入可能覆蓋資料,危險