語言類的學習最好的辦法就是放到在功能代碼中理解,文字分析特殊情況。
體驗軟件編程下硬件工作(二)
◆在DOS的DEBUG下,可以通過指令跟蹤和修改各個寄存器以及內存中的數據,具體的指令如下:
R查看和改變寄存器內容
D查看內存中內容
E改寫內存中的內容
U將內存中的機器指令翻譯成彙編指令
T執行一條機器指令
A以彙編指令的格式在內存中寫入一條機器指令
這些指令在DEBUG中多用就會熟悉。
◆第一個彙編程序:
- assume cs:codesg
- codesg segment
- start: mov ax,0123h
- mov bx,0456h
- add ax,bx
- add ax,ax
- mov ax,4c00h
- int21h
- codesg ends
- end
⑵XXX segment
......
XXX ends----成對使用的僞指令,定義一個段,分別表示段開始和段結束。
⑶end----僞指令,彙編語言的結束標誌。
以上這三條是通過僞指令實現的彙編結構,它們只對編譯器有作用。
mov指令:傳送指令,將第二個數據送到第一個位置處。
add指令:加法指令,將第一個位置和第二個位置的數據相加,並存到第一個位置處。
mov ax,4c00h
int 21h:程序返回的指令。我們的程序最先以湖邊指令的形式存在源程序中,經編譯、連接後編程機器碼,存儲在可執行的文件中,那麼怎麼得到運行呢?
一個程序A在可執行的文件中,則必須有一個正在運行的程序B,將A從可執行文件中加載在內存後,將CPU的控制權交給A,A才能得到運行。A開始運行後,B暫停運行。而當A運行完畢後,應該將CPU的控制權交還給使它得以運行的程序B,B繼續運行。
用我們的系統解釋就是,我們的這個可執行程序被commond運行並加載到內存中,系統將CPU的控制權交給我們的程序,等我們的程序執行完後,我們要將CPU的控制權還給commond,所以以上的兩條指令就是將控制權返回的指令。
下面進行源程序的編譯和連接:
想要進行編譯連接工作,我們需要一個編譯器,目前彙編語言的編譯器有很多,推薦兩種,masm和masm32
一個是DOS下的編譯器,一個是可視化的編譯器(使用方法網上很多,安裝好了別忘了設置環境變量,否則會報錯)
下圖是編譯連接的過程過程源文件轉換成的機器碼:
整個過程可以通過debug *.exe來跟蹤。
在DOS系統中EXE文件中的程序加載過程:
◆第二段代碼
- assume cs:code,ds:data,ss:stack
- data segment
- dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
- data ends
- stack segment
- dw 0,0,0,0,0,0,0,0
- stack ends
- code segment
- start:mov ax,stack
- mov ss,ax
- mov sp,16
- mov ax,data
- mov ds,ax
- mov bx,0
- mov cx,8
- s:push [bx]
- add bx,2
- loop s
- mov bx,0
- mov cx,8
- s0:pop [bx]
- add bx,2
- loop s0
- mov ax,4c00h
- int 21h
- code ends
- end start
我們逐行來分析代碼:
第1行:可能有人會問,是不是通過assume cs:code,ds:data,ss:stack就可以讓CPU的cs指向code段,ds指向data段,ss指向stack段了?如果你這樣想的話就大錯特錯了,assume只是一種僞指令,它只能作用於編譯器,並不能作用於CPU。這樣做的意義不需要知道,只要知道在程序前一定要將各個段和各個段寄存器對應assume就好。
第2~4行:數據段,在這裏儲存數據。
第5~7行:棧段,這裏定義的多少個0是爲了說明定義多大的棧空間。
第8~26行:代碼段,實現功能代碼。
mov ax,stack----stack代表的是棧段的段地址,通過
mov ax,stack
mov ss,ax
mov sp,16 ---- 將棧指針指向棧底。
mov ax,data
mov ds,ax
mov bx,0 ---- 定位數據段的位置。
mov cx,8 ---- cx中存放的是loop(循環)的次數
s:push [bx] ---- s:是標記,在使用跳轉命令時這種標記十分有用。
push是將其後數據推向棧頂的意思,與其相反的是pop,將棧頂的數據彈出
loop s ---- 條件跳轉,只要cx中不爲0就跳轉。
◆第三個程序(靈活的尋址方式)
定義一個數據段:
datasg segment
db 'BaSic'
db 'iNfOrMaTiOn'
datasg ends
要將datasg中的第一個字符串轉化爲大寫,第二個字符串轉化爲小寫。
程序如下:
- datasg segment
- db 'BaSiC'
- db 'iNfOrMaTiOn'
- datasg ends
- codesg segment
- start: mov ax,datasg
- mov ds,ax
- mov bx,0
- mov cx,5
- s:mov al,[bx]
- and al,11011111b
- mov[bx],al
- inc bx
- loop s
- mov bx,5
- 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
or al,00100000b ---- 這句是將大寫轉換成小寫
inc bx ----加1的意思
在高級語言中,遇到'BaSiC'這種字符串我們經常會將它存儲在數組中,以便進行相關的操作,那麼作爲一種底層的語言,讓我們看看“數組”是怎麼實現的:
將datasg segment
db 'BaSic'
db 'iNfOrMaTiOn'
datasg ends中的第一個字符傳轉化爲大寫字母,第二個字符串轉換成小寫字母
實現程序如下:
- assume cs:codesg,ds:datasg
- datasg segment
- db 'BaSiC'
- db 'iNfOrMaTiOn'
- datasg ends
- codesg segment
- start: mov ax,datasg
- mov ds,ax
- mov bx,0
- mov cx,5
- s:mov al,[bx]
- and al,11011111b
- mov[bx],al
- mov al,<span style="color:#ff0000;">[5+bx] </span>;可以寫成5[bx]
- or al,00100000b
- mov [bx],al
- mov <span style="color:#ff0000;">[5+bx]</span>,al
- inc bx
- loop s
- mov ax,4c00h
- int 21h
- codesg ends
- end start
在8086CPU中還有兩種寄存器,它們的作用和bx很相似,它們就是si和di(它們不能拆成兩個8字節寄存器來用)
我們可以利用它們使用[bx+si]、[bx+di]、[bx+si+idata]和[bx+di+idata]的結構,實現高級語言的多重循環。
代碼如下:
- assume cs:codesg,ds:datasg,ss:stacksg
- datasg segment
- db 'ibm '
- db 'ibm '
- db 'ibm '
- db 'ibm '
- datasg ends
- stacksg segment
- dw 0,0,0,0,0,0,0,0
- stacksg ends
- codesg segment
- start:mov ax,stacksg
- mov ss,ax
- mov sp,16
- mov ax,datasg
- mov bx,0
- mov cx,4
- s0:push cx
- mov si,0
- mov cx,3
- s:mov al,[bx+si]
- and al,11011111b
- mov [bx+si],al
- inc si
- loop s
- add bx,16
- pop cx
- loop s0
- mov ax,4c00h
- inc 21h
- codesg ends
- end start
在上述的代碼中用到了push cx,在彙編編程中可以將需要暫時存儲的數據存放到棧中,這和高級語言中函數中的局部變量是一個道理。
◆指令要處理的數據長度
⑴通過寄存器來指明要處理的數據的長度。
例如:
mov ax,1----字長度
mov al,1----字節長度
⑵在沒有寄存器名存在的情況下,用操作符X ptr指明內存單元的長度,X在彙編指令中可以是word或者byte
例如:
mov word ptr ds:[0],1 -----訪問的內存單元是一個字單元
mov byte ptr ds:[0],1 -----訪問的內存單元是一個字節單元
◆div指令(除法指令)
⑴除數:有8位和16位兩種,在一個寄存器或內存單元中
⑵被除數:默認的放在AX或DX和AX中,如果除數是8位,除數則是16位,默認在AX中存放;如果除數爲16位,則被除數位32位,在DX和AX中存放,DX存放高16位,AX存放底16位
⑶結果:如果除數爲8位,則AL存儲觸發操作的商,AL存儲除法操作的餘數;如果除數是16位,則AX存儲除法操作的商,DX中存儲除法操作的餘數。
◆幾個僞指令:
⑴db定義字節型數據,dw定義字型數據,dd定義雙字型數據
⑵dup表示重複的數據
例如:db 3 dup(0)表示 db 0,0,0