1 基礎知識
1.1 存儲單元
一個存儲單元存儲一個字節
1.2 地址總線
一個CPU有N根地址線,則可以說這個CPU的地址總線的寬度為N。這樣的CPU最多可以尋找2的N次方個內存單元。
地址總線的寬度決定了CPU的尋址能力。
1.3 數據總線
數據總線的寬度決定了CPU和外界的數據傳送速度。8根數據總線一次可以傳送一個8位二進制數據(1個字節)。
8086的數據總線寬度為16。
數據總線的寬度決定了CPU與其他器件進行數據傳送時的一次數據傳送量。
1.4 控制總線
控制總線的寬度決定了CPU對外部器件的控制能力。
1.5 內存地址空間

地址0~7FFFH的32KB空間為主隨機存儲器的地址空間;
地址8000H~9FFFH的8KB空間為顯存地址空間;
地址A000H~FFFFH的24KB空間為各個ROM的地址空間。
2 寄存器
2.1 通用寄存器
8086CPU的所有寄存器是16位的,可以存放兩個字節。AX,BX.CX.DX這4個寄存器通常用來存放一般性的數據,被稱為通用寄存器。
AX分為AH和AL;
BX分為BH和BL;
以此類推。

AX的低8位構成了AL寄存器,AX的高8位構成了AH寄存器。
2.2 字在寄存器中的存儲
字節:Byte,一個字節由8個bit組成,可以存在8位寄存器中。
字:word,一個字由兩個字節組成,這兩個字節分別稱為這個字的高位字節和低位字節。

2.3 8086CPU的物理地址

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

一個數據的二進制形式左移N位,相當於該數據乘以2的N次方;
段地址*16表示以二進制形式存放的段地址左移4位,十六進制形式存放的段地址左移1位。
2.4 段
CPU可以用不同的段地址和偏移地址形成同一個物理地址。
偏移地址16位,變化范圍為0~FFFFH,僅用偏移地址來尋址最多可尋64KB個內存單元。
比如給定段地址1000H,用偏移地址尋址,CPU的尋址范圍為:10000H~1FFFFH。
2.5 CS和IP
CS為代碼段寄存器,IP為指令指針寄存器。

8086CPU的工作過程可以簡要描述如下。
- 從CS:IP指向的內存單元讀取指令,讀取的指令進入指令緩沖器;
- IP=IP+所讀取指令的長度,從而指向下一條指令;
- 執行指令。轉到步驟1,重復這個過程
2.6 修改CS,IP的指令
JMP 段地址:偏移地址:用指令中給出的段地址修改CS,偏移地址修改IP。
JMP 某一合法寄存器:用寄存器的值修改IP。
2.7 代碼段
對於8086PC機,我們可以將長度為N(N<=64KB)的一組代碼,存在一組地址連續、起始地址為16的倍數的內存單元中。
3 寄存器(內存訪問)
3.1 字的傳送
在內存和寄存器之間傳送字型數據時,高地址單元和高8位寄存器、低地址單元和低8位寄存器相對應。
3.2 DS和[address]
DS是數據段寄存器,用來存放要訪問數據的段地址。
"[X]"表示一個內存單元,X表示內存單元的偏移地址
3.3 CPU提供的棧機制
push ax:將寄存器ax中的數據送入棧中。
pop ax:從棧頂取出數據送入ax。
任意時刻,SS:SP指向棧頂元素。

push ax的執行過程
- SP=SP-2,SS:SP指向當前棧頂前面的單元,以當前棧頂前面的單元為新的棧頂;
- 將ax中的內容送入SS:SP指向的內存單元處。SS:SP此時指向新棧頂。

SP=最底部的字單元的偏移地址+2,此時最底部的字單元的偏移地址SP是000EH,加2后SP是0010H
pop ax的執行過程
將SS:SP指向的內存單元處的數據送入ax中;
SP=SP+2,SS:SP指向當前棧頂下面的單元,以當前棧頂下面的單元為新的棧頂。

用棧來暫存以后需要恢復的寄存器的內容時,寄存器出棧的順序要和入棧的順序相反
4 第一個程序
4.1 源程序

1.偽指令
偽指令是由編譯器來執行的指令,沒有對應的機器指令。
(1)XXX segment XXX ends:定義一個段。
(2)end:匯編程序的結束標記。
(3)assume:它假設某一寄存器和程序中的某一個用segment...ends定義的段相關聯
2.匯編指令
匯編指令有對應的機器碼指令,可以被編譯為機器指令,最終為CPU所執行。

4.2 shell將可執行文件中的程序裝載進入內存並使它運行
PS:操作系統的外殼
任何通用的操作系統,都要提供一個稱為shell(外殼)的程序,用戶使用這個程序來操作計算機系統進行工作。
DOS中有一個程序command.com,這個程序在DOS中稱為命令解釋器,也就是DOS系統的shell。
- 在DOS中直接執行1.exe時,是正在運行的command,將1.exe中的程序加載如內存;
- command設置CPU的CS:IP指向程序的第一條指令(即程序的入口),從而使程序得以運行;
- 程序運行結束后,返回到command中,CPU繼續運行command。

5 [BX]和loop指令
5.1 [bx]
[bx]表示一個內存單元,它的偏移地址在bx中。
5.2 Loop指令
CPU執行loop指令的時候,要進行兩步操作
- (cx)=(cx)-1
- 判斷cx中的值,不為零則轉至標號處執行程序,如果為零則向下執行
5.3 段前綴
在訪問內存單元的指令中顯式地給出內存單元的段地址所在的段寄存器。比如:
mov ax,ds:[bx]
5.4 一段安全的空間
當我們需要直接向一段內存中寫入內容時,這段內存空間不應存放系統或其他程序的數據或代碼,否則寫入操作很可能引發錯誤;
DOS方式下,一般情況,0:200~0:2ff空間中沒有系統或其他程序的數據或代碼,這段內存空間是安全的;
6 包含多個段的程序
6.1 在代碼段中使用數據
dw:define word,用來開辟內存空間。

end指令指明了程序的入口在標號start處
6.2 在代碼段中使用棧
程序運行時,定義的數據存放在CS:0~CS:F單元中,共8個字單元。依次將這8個字單元的數據入棧,然后再依次出棧到這8個字單元中,從而實現數據的逆序存放。

把start標識符去掉的話,編譯器會認為CS:0是指令開始處,實際上CS:0~CS:F是數據,CS:10開始才是代碼段。
6.3 將數據、代碼、棧放入不同的段
將數據、棧和代碼都放到一個段里面,會有以下兩個問題:
- 把它們放到一個段中使程序顯得混亂;
- 在8086中,如果數據、棧和代碼需要的空間超過64KB,就不能放在一個段中。
如下程序實現和程序6.2同樣的功能,不同之處在於它將數據、棧和代碼放入了不同的段中。

7 更靈活的定位內存地址的方法
7.1 and和or指令
and指令:邏輯與指令,按位進行與運算。
例如:
mov al,01100011B
and al,00111011B
執行后:al=00100011B
or指令:邏輯或指令,按位進行或運算。
例如:
mov al,01100011B
or al,00111011B
al=01111011B
7.2 以字符形式給出的數據
assume cs:code,ds:data data segment db 'unIX' db 'forRK' data ends code segment start: mov al,'a' mov bl,'b' mov ax,4c00h int 21h code ends end start
db 'unIX'相當於"db 75H,6EH,49H,58H","u","n","I","X"的ASCII碼分別為75H,6EH,49H,58H;
7.3 大小寫轉換問題

將“BaSiC”轉化為小寫,將'iNfOrMaTiOn'轉化為大寫


7.4 用[bx+idata]的方式進行數組的處理
起始地址不需要像7.3顯示地聲明mov bx,5


以上匯編程序用C語言描述如下

7.5 SI和DI
si和di是8086CPU中和bx功能象進的寄存器, si和di不能夠分成兩個8位寄存器來使用。
下面的3組指令實現了相同的功能

下面的3組指令實現了相同的功能

7.6 不同的尋址方式的靈活應用
[idata]用一個常量來表示地址,可用於直接定位一個內存單元;
[bx]用一個變量來表示內存地址,可用於間接定位一個內存單元;
[bx+idata]用一個變量和常量表示地址,可在一個起始地址的基礎上用變量間接定位一個內存單元;
[bx+si]用兩個變量表示地址;
[bx+si+idata]用兩個變量和一個常量表示地址。
編程,將datasg段中每個單詞改為大寫字母。

程序如下:

8 數據處理的兩個基本問題
8.1 bx,si,di和bp
1.在8086CPU中,只有bx,si,di和bp這4個寄存器可以用在"[...]"中來進行內存單元的尋址。
以下的指令都是正確的:

2.在[...]中,這4個寄存器可以單個出現,或只能以4種組合出現:bx和si、bx和di、bp和si、bp和di。
以下的指令都是正確的:

8.2 尋址方式

8.3 指令要處理的數據有多長
在沒有寄存器名存在的情況下,用操作符X ptr指明內存單元的長度,X在匯編指令種可以為word或byte。
例如,下面的指令中,用word ptr指明了指令訪問的內存單元是一個字單元。
mov word ptr ds:[0],1 inc word ptr [bx] inc word ptr ds:[0] add word ptr [bx],2
下面的指令中,用byte ptr指明了指令訪問的內存單元是一個字節單元。
mov byte ptr ds:[0],1 inc byte ptr [bx] inc byte 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 FF ......
8.4 尋址方式的綜合應用

數據存放示意

匯編和C語言對應關系

8086CPU提供的如[bx+si+idata]的尋址方式為結構化數據的處理提供了方便。一般來說,我們可以用[bx+idata+si]的方式來訪問結構體中的數據。
用bx定位整個結構體,用idata定位結構體中的某一個數據項,用si定位數組頂中的每個元素。
8.5 div指令
被除數:
如果除數為8位,被除數為16位,默認存放在AX;
如果除數為16位,被除數則為32位,在DX和AX中存放,DX存放高16位,AX存放低16位;
結果:
如果除數為8位,AL存儲商,AH存儲余數;
如果除數為16位,AX存儲商,DX存儲余數;

編程,利用除法指令計算100001/100

編程,利用除法指令計算1001/100

8.6 dd和dup指令
dd:define double word
dup:用來進行數據的重復
db 重復的次數 dup (重復的字節型數據)
db 重復的次數 dup (重復的字型數據)
db 重復的次數 dup (重復的雙字型數據)
9 轉移指令的原理
9.1 操作符offset
操作符offset在匯編語言中是由編譯器處理的符號,它的功能是取得標號的偏移地址。
如下面的程序:

9.2 依據位移進行轉移的jmp指令
jmp short 標號:段內短轉移,它對IP的修改范圍為-128~127。
實際上,“jmp short 標號”的功能為:(IP)=(IP)+8位位移。
- 8位位移=標號處的地址-jmp指令后的第一個字節的地址;
- short指明此處的位移為8位位移;
- 8位位移的范圍為-128~127,用補碼表示;
- 8位位移由編譯程序在編譯時算出。
“jmp short 標號”指令所對應的機器碼中,並不包含轉移的目的地址,而包含的是轉移的位移。

jump near ptr 標號:段內近轉移
“jmp near ptr 標號”的功能為:(IP)=(IP)+16位位移。
- 16位位移=標號處的地址-jmp指令后的第一個字節的地址;
- near ptr 指明此處的位移為16位位移,進行的時段內近轉移;
- 16位位移的范圍為-32768~32767,用補碼表示;
- 16位位移由編譯程序在編譯時算出。
9.3 轉移的目的地址在指令中的jmp指令
jmp far ptr 標號:段間轉移,又稱為遠轉移。
far ptr指明了指令用標號的段地址和偏移地址修改CS和IP。
jmp far ptr s所對應的機器碼包含轉移的目的地址
9.4 轉移地址在內存中的jmp指令
1.jmp word ptr 內存單元地址(段內轉移)
功能:從內存單元地址處開始存放着一個字,是轉移的目的偏移地址。
2.jmp dword ptr 內存單元地址(段間轉移)
功能:從內存單元地址處開始存放着兩個字,高地址處的字是轉移的目的短地址,低地址處的字是轉移的目的偏移地址
9.5 jcxz指令
jcxz指令為有條件轉移指令,所有的有條件轉移指令都是短轉移,在對應的機器碼中包含轉移的位移,對IP的修改范圍是:-128~127。
指令格式:jcxz標號(如果(cx)=0,轉移到標號處執行)
操作:
當(cx)=0時,(IP)=(IP)+8位位移;
8位位移=標號處的地址-jcxz指令后的第一個字節的地址;
8位位移的范圍時-128~127,用補碼表示;
8位位移由編譯程序在編譯時算出。
當(cx)≠0時,程序向下執行。
9.6 loop指令
loop指令為循環指令,所有的循環指令都是短轉移,在對應的機器碼中包含轉移的位移,而不是目的地址。對IP的修改范圍都為:-128~127。
指令格式:loop標號((cx)=(cx)-1,如果(cx)≠0,轉移到標號處執行。)
操作:
(1) (cx)=(cx)-1
(2) 如果(cx)≠0,(IP)=(IP)+8位位移
8位位移=標號處的地址-loop指令后的第一個字節的地址;
8位位移的范圍為-128~127,用補碼表示;
8位位移由編譯程序在編譯時算出。
如果(cx)=0,什么也不做(程序向下執行)。
10 CALL和RET指令
10.1 ret和retf
ret指令用棧中的數據,修改IP的內容,從而實現近轉移
retf指令用棧中的數據,修改CS和IP的內容,從而實現遠轉移

用匯編語法解釋ret和retf指令,則
CPU執行ret指令時,相當於進行:
pop IP
CPU執行retf指令時,相當於進行:
pop IP
pop CS
10.2 依據位移進行轉移的call指令
call標號(將當前的IP壓棧后,轉到標號處執行指令)
CPU執行此種格式的call指令時,進行如下的操作:

16位位移=標號處的地址-call指令后的第一個字節的地址;
16位位移的范圍為-32768~32767,用補碼表示;
16位位移由編譯程序在編譯時算出。
CPU執行"call 標號"時,相當於進行:
push IP
jmp near ptr 標號
10.3 轉移的目的地址在指令中的call指令
"call far ptr 標號"實現的是段間轉移。
CPU執行此種格式的call指令時,進行如下操作。

CPU執行“call far ptr 標號”時,相當於進行:
push CS
push IP
jmp far ptr 標號
10.4 轉移地址在寄存器中的call指令
指令格式:call 16位reg
功能:
(SP)=(SP)-2
((SS)*16+(SP))=(IP)
(IP)=(16位reg)
CPU執行“call 16位reg”,相當於進行:
push IP
jmp 16位reg
10.5 轉移地址在內存中call指令
(1)call word ptr 內存單元地址
用匯編語法來解釋此種格式的call指令,則
CPU執行“call word ptr 內存單元地址”時,相當於進行:
push IP
jmp word ptr 內存單元地址
(2)call dword ptr 內存單元地址
用匯編語法來解釋此種格式的call指令,則:
CPU執行“call dword ptr 內存單元地址”時,相當於進行:
push CS
push IP
jmp dword ptr 內存單元地址
10.6 mul指令
mul是乘法指令
(1)兩個相乘的數:兩個相乘的數,要么都是8位,要么都是16位。如果是8位,一個默認放在AL中,另一個放在8位reg或內存字節單元中;如果
是16位,一個默認在AX中,另一個放在16位reg或內存字單元中。
(2)結果:如果是8位乘法,結果默認放在AX中;如果是16位乘法,結果高位默認在DX中存放,低位在AX中存放。
格式如下:
mul reg
mul 內存單元
例子1

例子2

11 標志寄存器
標志寄存器具有以下作用
- 用來存儲相關指令的某些執行結果;
- 用來為CPU執行相關指令提供行為依據;
- 用來控制CPU的相關工作方式;

11.1 ZF標志
零標志位
結果為0,zf=1;結果不為0,那么zf=0
11.2 PF標志
奇偶標志位
1的個數為偶數,pf=1;1的個數為奇數,那么pf=0;
11.3 SF標志
符號標志位
結果為負,sf=1;結果非負,sf=0;
11.4 CF標志
進位標志位
存在最高有效位向更高位進位/借位的情況,cf=1;否則cf=0
11.5 OF標志位
溢出標志位
進行有符號數運算的時候,結果超過了機器所能表示的范圍稱為溢出,溢出of=1,無溢出,of=0;
11.6 adc指令
指令格式:adc 操作對象1,操作對象2
功能:操作對象1 = 操作對象1 + 操作對象2 + CF
例如adc ax,bx 實現的功能是:(ax)=(ax)+(bx)+CF
11.7 sbb指令
指令格式:sbb 操作對象1,操作對象2
功能:操作對象1 = 操作對象1-操作對象2-CF
比如指令 sbb ax,bx 實現的功能是:(ax)=(ax)-(bx)-CF
11.8 cmp指令
cmp的功能相當於減法指令,只是不保存結果。cmp指令執行后,將對標志寄存器產生影響。
cmp 指令格式:cmp 操作對象1,操作對象2
例如,指令cmp ax,ax,做(ax)-(ax)的運算,結果為0,但並不在ax中保存,僅影響flag的相關各位。指令執行后:zf=1,pf=1,sf=0,cf=0,of=0
11.9 檢測比較結果的條件轉移指令
je 等於則轉移 zf=1
jne 不等於則轉移 zf=0
jb 低於則轉移 cf=1
jnb 不低於則轉移 cf=0
ja 高於則轉移 cf=0且zf=0
jna 不高於則轉移 cf=1或zf=1
11.10 DF標志和串傳送指令
DF標志
DF是方向標志位。
df=0 每次操作后si,di遞增;
df=1 每次操作后si,di遞減。
串傳送指令
格式:movb
功能:執行movsb指令相當於進行下面幾步操作。
((es)*16+(di))=((ds)*16+(si))
如果df=0則:(si)=(si)+1
(di)=(di)+1
如果df=1則:(si)=(si)-1
(di)=(di)-1
格式:movsw
movsw的功能是將ds:si指向的內存字單元中的字送入es:di中,然后根據標志寄存器df位的值,將si和di遞增2或遞減2。
rep movsb
用匯編語法來解釋
s:movsb
loop s
rep的作用是根據cx的值,重復執行后面的串傳送指令。
11.11 pushf和popf
pushf:將標志寄存器的值壓入棧
popf:從棧中彈出數據
12 內中斷
12.1 中斷向量表
中斷源:中斷信息的來源
中斷類型碼:由8位組成
中斷向量表:中斷向量的列表

中斷類型碼作為中斷向量表的表項號,定位相應的表項,從而得到中斷處理程序的入口地址。
內存0000:0000到0000:03FF的1024個單元中存放着中斷向量表。
對於8086CPU,一個表項占兩個字,高地址存放段地址,低地址存放偏移地址。
12.2 中斷過程
簡潔地描述中斷過程
- 取得中斷類型碼N
- pushf
- TF=0,IF=0
- push CS
- push IP
- (IP)=(N*4),(CS)=(N*4+2)
在最后一步完成后,CPU開始執行由程序員編寫的中斷處理程序。
12.3 中斷處理程序和iret指令
中斷處理程序的常規步驟
- 保存用到的寄存器;
- 處理中斷;
- 恢復用到的寄存器;
- 用iret指令返回;
iret指令的功能用匯編語法描述為:
pop IP
pop CS
popf
12.4 編程處理0號中斷
assume cs:code
code segment
start:設置es:di指向目的地址
設置ds:si指向源地址
設置cx為傳輸長度
設置傳輸方向為正
rep movsb
設置中斷向量表
mov ax,4c00h
int 21h
do0:顯示字符串"overflow"
mov ax,4c00h
int 21h
code ends
end start
12.5 設置中斷向量
mov ax,0
mov es,ax
mov word ptr es:[0*4],200h
mov word ptr es:[0*4+2],0
12.6 單步中斷
CPU在執行完一條指令之后,如果檢測到標志寄存器的TF位為1,則產生單步中斷,引發中斷過程。單步中斷的中斷類型碼為1,它引發的中斷過程如下。
- 取得中斷類型碼1
- 標志寄存器入棧,TF、IF設置為0;
- CS、IP入棧;
- (IP)=(1*4),(CS)=(1*4+2);
在進入中斷處理程序之前,設置TF=0。從而避免CPU在執行中斷處理程序的時候發生中斷。
12.7 響應中斷的特殊情況
在執行完ss寄存器傳送數據的指令后,即便是發生中斷,CPU也不會響應。
這樣做的主要原因是,ss:sp聯合指向棧頂,而對它們的設置應該連續完成。所以debug的時候mov sp,0不會單步中斷。
13 int指令
13.1 int指令
int指令的格式:int n,n為中斷類型碼,它的功能是引發中斷過程。
CPU執行int n指令,相當於引發一個n號中斷的中斷過程,執行過程如下。
- 取得中斷類型碼n
- 標志寄存器入棧,TF、IF設置為0;
- CS、IP入棧;
- (IP)=(1*4),(CS)=(1*4+2);
mov ah,9
int 21h
調用21h例程9號子程序
13.2 編寫供應用程序調用的中斷例程
功能:將一個全是字母,以0結尾的字符串,轉化為大寫。
參數:ds:si指向字符串的首地址。
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 BIOS和DOS中斷例程的安裝過程
- 開機后,CPU一加電,初始化(CS)=0FFFFH,(IP)=0,自動從FFFF:0單元開始執行程序。FFFF:0處有一條轉跳指令,CPU執行該指令,轉去執行BIOS中的硬件系統檢測和初始化程序。
- 初始化程序將簡歷BIOS所支持的中斷向量,即將BIOS提供的中斷例程的入口地址登記在中斷向量表中。注意,對於BIOS所提供的中斷例程,只需將入口地址登記在中斷向量表中即可,因為它們是固化到ROM中的程序,一直在內存中存在。
- 硬件系統檢測和初始化完成后,調用int 19h 進行操作系統的引導。從此將計算機交由操作系統控制。
- DOS啟動后,除完成其他工作外,還將它所提供的中斷例程裝入內存,並建立相應的中斷向量。
14 端口
在PC機系統中,和CPU通過總線相連的芯片除各種存儲器外,還有以下3種芯片。
- 各種接口卡(網卡,顯卡)上的接口芯片,它們控制接口卡進行工作;
- 主板上的接口芯片,CPU通過它們對部分外設進行訪問;
- 其他芯片,用來存儲相關的系統信息,或進行相關的輸入輸出處理。
14.1 端口的讀寫
端口的讀寫指令只有兩條:in和out,分別用於從端口讀取數據和往端口寫入數據。
CPU執行內存訪問指令和端口訪問指令的比較
(1).訪問內存
mov ax,ds:[8] ;假設執行前(ds)=0
- CPU通過地址線將地址信息8發出;
- CPU通過控制線發出內存讀命令,選中存儲芯片,並通知它,將要從中讀取數據;
- 存儲器將8號單元中的數據通過數據線送入CPU。
(2).訪問端口
in al,60h
- CPU通過地址線將地址信息60h發出;
- CPU通過控制線發出端口讀命令,選中端口所在的芯片,並通知它,將要從中讀取數據;
- 存儲器將60h號端口中的數據通過數據線送入CPU。
注意,在in和out指令中,只能使用ax或al來存放端口中讀入的數據或要發送到端口中的數據。

14.2 CMIOS RAM芯片
CMOS RAM芯片,一般簡稱為CMOS。此芯片特征如下。
- 包含一個實時鍾和一個有128個存儲單元的RAM存儲器。
- 該芯片靠電池供電,所以,關機后其內部的實時鍾仍可正常工作,RAM中的信息不丟失。
- 128個字節的RAM中,內部時鍾占用 0~0dh 單元來保存時間信息,其余大部分單元用於保存系統配置信息,供系統啟動時BIOS程序讀取。
- 該芯片內部有兩個端口,端口地址為 70h 和 71h 。CPU 通過這兩個端口來讀寫CMOS RAM。
- 70h 為地址端口存放要訪問的 CMOS RAM 單元的地址; 71h 為數據端口,存放從選定的 CMOS RAM 單元中讀取的數據,或要寫入到其中的數據。
比如,讀CMOS RAM的2號單元
- 將2送入端口70h;
- 從端口71h讀取2號單元的內容。
14.3 shl和shr指令
shl是邏輯左移指令,它的功能為:
- 將一個寄存器或內存單元中的數據向左移位;
- 將最后移出的一位寫入CF中;
- 最低位用0補充。
指令:
mov al,01001000b
shl al,1
執行后(al)=10010000b,CF=0。
shr是邏輯左移指令,它的功能為:
- 將一個寄存器或內存單元中的數據向右移位;
- 將最后移出的一位寫入CF中;
- 最高位用0補充。
指令:
mov al,01010001b
mov cl,3
shl al,cl
執行后(al)=10001000b,因為最后移出的一位是0,所以CF=0。
14.4 CMOS RAM中存儲的時間信息
年、月、日、時、分、秒存放單元依次為
0H、2H、4H、7H、8H、9H
這些數據以BCD碼的方式存放。
在屏幕中間顯示當前的月份
程序如下
;編程:在屏幕中間顯示當前的月份 assume cs:code code segment start: mov al,8 out 70h,al in al,71h mov ah,al mov cl,4 shr ah,cl and al,00001111b add ah,30h add al,30h 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
15 外中斷
15.1 接口芯片與端口
PC系統的接口卡和主板上,裝有各種接口芯片。這些外設接口芯片的內部有若干寄存器,CPU將這些寄存器當作端口來訪問。
外設的輸入送入相關的接口芯片的端口中;CPU向外設的輸出先送入端口中,再由相關的芯片送到外設。
CPU向外設輸出控制命令,這些命令先送到相關芯片的端口中,然后再由相關的芯片根據命令對外設實施控制。
可見,CPU通過端口和外部設備進行聯系。
15.2 外中斷信息
1.可屏蔽中斷
CPU可以不響應的中斷。當CPU檢測到可屏蔽中斷信息時,如果IF=1,則CPU在執行完當前指令后響應中斷,引發中斷過程;
如果IF=0,則不響應可屏蔽中斷,禁止其他的可屏蔽中斷。
sti:設置IF=1
cli:設置IF=0
2.不可屏蔽中斷
不可屏蔽中斷是CPU必須響應的中斷。當CPU檢測到不可屏蔽中斷信息時,則在執行完當前指令后,立即響應,引發中斷過程。
不可屏蔽中斷的中斷類型碼固定為2,所以中斷過程中,不需要獲取中斷類型碼。
15.3 PC機鍵盤的處理過程
1.鍵盤輸入
按下一個鍵,該芯片產生一個掃描碼,叫做通碼。掃描碼被送入主板上的相關接口芯片的寄存器中,該寄存器的端口地址為60h。
松開按下的鍵,也產生一個掃描碼,叫做斷碼。松開按鍵時產生的掃描碼也被送入60h端口中。
掃描碼長度為一個字節,通碼的第7位為0,斷碼的第7位為1,即:
斷碼=通碼 + 80h
例如,g鍵的通碼為22h,斷碼為a2h。

2.引發9號中斷
鍵盤的輸入到達60h端口時,相關的芯片就會向CPU發出中斷類型碼為9的可屏蔽中斷信息。CPU檢測到該中斷信息后,如果IF=1,則響應中斷,
引發中斷過程,轉去執行int9中斷例程。
3.執行int9中斷例程
(1)讀取60h端口的掃描碼;
(2)如果是字符鍵的掃描碼,將該掃描碼的和它所對應的ASCII碼送入內存中的BIOS鍵盤緩沖區;如果是控制鍵(Ctrl)和切換鍵(Capslock)的掃描碼,
則將其轉變為狀態字節(用二進制位記錄控制鍵和切換鍵狀態的字節)寫入內存中存儲狀態字節的單元;
(3)對鍵盤系統進行相關的控制。
BIOS鍵盤緩沖區中,一個鍵盤輸入用一個字單元存放,高位字節存放掃描碼,低位字節存放字符碼
0040:17單元存儲鍵盤狀態字節,該字節記錄了控制鍵和切換鍵的狀態。鍵盤狀態字節各位記錄的信息如下。

15.4 指令系統總結
8086CPU 提供以下幾大類指令:
1、數據傳送指令
比如:mov、push、pop、pushf、popf、xchg等都是數據傳送指令,這些指令實現寄存器和內存、寄存器和寄存器之間的單個數據傳送。
2、算術運算指令
比如:add、sub、adc、sbb、inc、dec、cmp、imul、idiv、aaa等都是算術運算指令,這些指令實現寄存器和內存中的數據的算數運算。
它們的執行結果影響標志寄存器的:sf、zf、of、cf、pf、af位。
3、邏輯指令
比如:and、or、not、xor、test、shl、shr、sal、sar、rol、ror、rcl、rcr 等都是邏輯指令。
除了not指令外,它們的執行結果都影響標志寄存器的相關標志位。
4、轉移指令
可以修改IP ,或同時修改CS 和IP 的指令統稱為轉移指令。轉移指令分為以下幾類:
(1)無條件轉移指令,比如:jmp;
(2)條件轉移指令,比如:jcxz、je、jb、ja、jnb、jna等;
(3)循環指令,比如:loop;
(4)過程,比如:call、ret、retf;
(5)中斷,比如int、iret。
5、處理機控制指令
這些指令對標志寄存器或其他處理機狀態進行設置,比如:cld、std、cli、sti、nop、clc、cmc、stc、hlt、wait、esc、lock等都是處理機控制指令。
6、串處理指令
這些指令對內存中的批量數據進行處理
比如:movsb、movsw、cmps、scas、lods、stos等。
若要使用這些指令方便地進行批量數據的處理,則需要和rep、repe、repne等前綴指令配合使用。
16 直接定址表
16.1 數據標號
數據標號標記了存儲數據的單元的地址和長度。
對於程序中的a db 1,2,3,4,5,6,7,8:
指令:mov al,a[si]
相當於:mov al,cs:0[si]
指令:mov al,a[3]
相當於:mov al,cs:0[3]
指令:mov al,a[bx+si+3]
相當於:mov al,cs:0[bx+si+3]
16.2 在其他段中使用數據標號
在其他段中,我們可以使用數據標號來描述存儲數據的單元的地址和長度。
在后面加有":"的地址標號,只能在代碼段中使用,不能在其他段中使用。
assume cs:code,ds:data
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
data ends
code segment
start:
mov ax,data
mov ds,ax
mov si,0
mov cx,8
s:
mov al,a[si]
mov ah,0
add b,ax
inc si
loop s
mov ax,4c00h
int 21h
code ends
end start
16.3 直接定址表
建立一張具有映射關系的表,節省計算時間。
編寫程序,以十六進制的形式在屏幕中間顯示給定的字節型數據。
在數值0~15和字符"0"~"F"建立映射關系。
assume cs:code code segment start: mov al,0eh call showbyte mov ax,4c00h int 21h ;子程序: ;用al傳送要顯示的數據 showbyte: jmp short show table db '0123456789ABCDEF' ;字符表 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)用ah 寄存器傳遞功能號:
0 表示清屏,
1表示設置前景色,
2 表示設置背景色,
3 表示向上滾動一行;
(2)對於2、3號功能,用al傳送顏色值,
(al)∈{ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 }
下面,我們討論一下各種功能如何實現 :
(1)清屏
將顯存中當前屏幕中的字符設為空格符;
(2)設置前景色
設置顯存中當前屏幕中處於奇地址的屬性字節的第0、1、2位;
(3)設置背景色
設置顯存中當前屏幕中處於奇地址的屬性字節的第4、5、6位;
(4)向上滾動一行
依次將第 n+1行的內容復制到第n行處:最后一行為空。
;編程:實現一個子程序setscreen,為顯示輸出提供如下功能: ;(1) 清屏。 ;(2) 設置前景色。 ;(3) 設置背景色。 ;(4) 向上滾動一行。 ; ;入口參數說明: ;(1) 用 ah 寄存器傳遞功能號:0 表示清屏,1表示設置前景色,2 表示設置背景色,3 表示向上滾動一行; ;(2) 對於2、3號功能,用 al 傳送顏色值,(al) ∈{0,1,2,3,4,5,6,7} 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 ;功能子程序1:清屏 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 ;sub1 ends ;功能子程序2:設置前景色 sub2: push bx push cx push es mov bx,0b800h mov es,bx mov bx,1 mov cx,2000 sub2s: and byte ptr es:[bx],11111000b or es:[bx],al add bx,2 loop sub2s pop es pop cx pop bx ret ;sub2 ends ;功能子程序3:設置背景色 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: and byte ptr es:[bx],10001111b or es:[bx],al add bx,2 loop sub2s pop es pop cx pop bx ret ; sub3 ends ;功能子程序4:向上滾動一行 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+1行 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 ;sub4 ends
17 使用BIOS進行鍵盤輸入和磁盤讀寫
17.1 int9中斷例程對鍵盤輸入的處理
我們通過下面幾個鍵A,B,C,D,E,shift_A,A的輸入過程,簡要地看一下int9中斷例程對鍵盤輸入的處理方法。


17.2 使用int16h中斷例程讀取鍵盤緩沖區
int16h中斷例程是從鍵盤緩沖區讀取一個鍵盤輸入,並且將其從緩沖區中刪除,該功能的編號為0。
編程,接收用戶的鍵盤輸入,輸入"r",將屏幕上的字符設置為紅色;輸入"g",將屏幕上的字符設置為綠色;輸入"b",將屏幕上的字符設置為藍色。
;編程: ;接收用戶的鍵盤輸入,輸入“r”,將屏幕上的字符設置為紅色:輸入“g”, ;將屏幕上的字符設置為綠色;輸入“b ”,將屏幕上的字符設置為藍色。 ;A、B、C處的程序指令比較有技巧,請讀者自行分析 assume cs:code code segment start: mov ah,0 int 16h ;int 16h 0號功能實現從鍵盤緩沖區讀取一個鍵盤輸入 mov ah,1 ;A 001 cmp al,'r' je red cmp al,'g' je green cmp al,'b' je blue jmp short sret red: shl ah,1 ;B 100 green: shl ah,1 ;C 010 blue: mov bx,0b800h mov es,bx mov bx,1 mov cx,2000 s: and byte ptr es:[bx],11111000b or es:[bx],ah add bx,2 loop s sret: mov ax,4c00h int 21h code ends end start
17.3 字符串的輸入
簡單地確定程序的處理過程如下。
- 調用int16h讀取鍵盤輸入;
- 如果是字符,進入字符棧,顯示字符棧中的所有字符;繼續執行1;
- 如果是退格鍵,從字符棧中彈出一個字符,顯示字符棧中的所有字符;繼續執行1;
- 如果是Enter鍵,向字符棧中壓入0,返回。
完整的接收字符串輸入的子程序如下所示。
;最基本的字符串輸入程序,需要具備下面的功能: ;(1) 在輸入的同時需要顯示這個字符串; ;(2)一般在輸入回車符后,字符串輸入結束; ;(3)能夠刪除已經輸入的字符。 ;編寫一個接收字符串的輸入子程序,實現上面三個基本功能。 ;因為在輸入的過程中需要顯示,子程序的參數如下: ; (dh)、(dl)=字符串在屏幕上顯示的行、列位置; ; ds:si 指向字符串的存儲空間,字符串以O 為結尾符。 assume cs:code code segment start: call getstr return: mov ax,4c00h int 21h ;完整的接收字符串輸入的子程序 getstr: push ax getstrs: mov ah,0 int 16h cmp al,20h jb nochar ;判斷的是ASCII碼小於0,說明不是字符 mov ah,0; call charstack ;字符入棧 mov ah,2 call charstack ;顯示棧中的字符 jmp getstrs nochar: cmp ah,0eh ;退格鍵的掃描碼 je backspace cmp ah,1ch ;回車鍵的掃描碼 je enter jmp getstrs backspace: ;退格 mov ah,1 call charstack ;字符出棧 mov ah,2 call charstack ;顯示棧中的字符 jmp getstrs enter: ;回車 mov al,0 mov ah,0 call charstack ;0入棧 mov ah,2 call charstack ;顯示棧中的字符 pop ax ret ;getstr ends ;功能子程序實現 charstack: jmp short charstart table dw charpush,charpop,charshow top dw 0 ;棧頂 charstart: push bx push dx push di push es cmp ah,2 ja sret mov bl,ah mov bh,0 add bx,bx jmp word ptr table[bx] charpush: mov bx,top mov [si][bx],al inc top jmp sret charpop: cmp top,0 je sret dec top mov bx,top mov al,[si][bx] jmp sret charshow: mov bx,0b800h mov es,bx mov al,160 mov ah,0 mul dh mov di,ax add dl,dl mov dh,0 add di,dx mov bx,0 charshows: cmp bx,top jne noempty mov byte ptr es:[di],' ' jmp sret noempty: mov al,[si][bx] mov es:[di],al mov byte ptr es:[di+2],' ' inc bx add di,2 jmp charshows sret: pop es pop di pop dx pop bx ret code ends end start
17.4 應用int 13h中斷例程對磁盤進行讀寫
3.5英寸軟盤分為上下兩面,每面有80個磁道,每個磁道分為18個扇區,每個扇區的大小為512個字節。
則:2面*80磁道*18扇區*512字節=1440KB=1.44MB
BIOS提供的訪問磁盤的中斷例程為int 13h
assume cs:code code segment start: mov ax,0b800h mov es,ax mov bx,0 ;es:bx 指向將寫入磁盤的數據的內存區 mov al,8 ;寫入的扇區數 mov ch,0 ;磁道號,從0開始 mov cl,1 ;扇區號 從1開始 mov dl,0 ;驅動器號0:軟驅A, 1:軟驅B,硬盤從80h開始, 80h:硬盤C,81h:硬盤D mov dh,0 ;磁頭號,(對於軟盤即面號,因為一個面用一個磁頭來讀寫) mov ah,3 ;傳遞 int 13h 寫入數據的功能號 int 13h ;返回參數 ;操作成功:(ah) = 0,(al) = 寫入的扇區數 ;操作失敗:(ah) = 出錯代碼 return: mov ax,4c00h int 21h code ends end start
資料

電子書:https://pan.baidu.com/s/1oF3qs08fWCG8mZoPNQD_kg 提取碼:3vwj
小甲魚視頻:https://pan.baidu.com/s/1cmX-U-YVk6Hv83xzZTzSKg 提取碼:got0
小甲魚視頻ppt和書中的源碼:https://pan.baidu.com/s/1Qo63M0diytfTTjMEq884AA 提取碼:pqs9
課后習題答案:https://blog.csdn.net/andrewgithub/article/details/78432046
侵刪。
