彙編學習--體驗軟件編程下硬件工作(二)

語言類的學習最好的辦法就是放到在功能代碼中理解,文字分析特殊情況。

體驗軟件編程下硬件工作(二)

◆在DOS的DEBUG下,可以通過指令跟蹤和修改各個寄存器以及內存中的數據,具體的指令如下:

R查看和改變寄存器內容

D查看內存中內容

E改寫內存中的內容

U將內存中的機器指令翻譯成彙編指令

T執行一條機器指令

A以彙編指令的格式在內存中寫入一條機器指令

這些指令在DEBUG中多用就會熟悉。

◆第一個彙編程序:

[plain] view plaincopy
  1. assume cs:codesg  
  2. codesg segment  
  3. start: mov ax,0123h      
  4.        mov bx,0456h  
  5.        add ax,bx  
  6.        add ax,ax  
  7.          
  8.        mov ax,4c00h  
  9.        int21h  
  10. codesg ends  
  11. end  
⑴assume----僞指令,意爲假設將段寄存器和某一段連接起來。

⑵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文件中的程序加載過程:


◆第二段代碼

[plain] view plaincopy
  1. assume  cs:code,ds:data,ss:stack  
  2. data segment  
  3.     dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h  
  4. data ends  
  5. stack segment  
  6.     dw 0,0,0,0,0,0,0,0  
  7. stack ends  
  8. code segment  
  9.      start:mov ax,stack  
  10.              mov ss,ax  
  11.              mov sp,16  
  12.              mov ax,data  
  13.              mov ds,ax  
  14.              mov bx,0  
  15.              mov cx,8  
  16.            s:push [bx]  
  17.               add bx,2  
  18.               loop s  
  19.               mov bx,0  
  20.               mov cx,8  
  21.           s0:pop [bx]  
  22.                add bx,2  
  23.                loop s0  
  24.                mov ax,4c00h  
  25.                int 21h  
  26. code ends  
  27. end start  
之前的一段代碼只有一個代碼段,而這個代碼採用了3個不同的段,用以區分存儲數據,棧和代碼,這樣的利用方便連續的讀取數據和訪問棧

我們逐行來分析代碼:

第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中的第一個字符串轉化爲大寫,第二個字符串轉化爲小寫。

程序如下:

[plain] view plaincopy
  1. datasg  segment  
  2.      db 'BaSiC'  
  3.      db 'iNfOrMaTiOn'  
  4. datasg ends  
  5.   
  6. codesg segment   
  7.       start: mov ax,datasg  
  8.                mov ds,ax  
  9.                mov  bx,0  
  10.                mov cx,5  
  11.             s:mov al,[bx]  
  12.                and al,11011111b  
  13.                mov[bx],al  
  14.                inc bx  
  15.                loop s  
  16.                mov bx,5  
  17.                mov cx,11  
  18.            s0:mov al,[bx]  
  19.                 or al,00100000b  
  20.                 mov [bx],al  
  21.                 inc bx  
  22.                 loop s0  
  23.                
  24.                 mov ax,4c00h  
  25.                 int 21h  
  26. codesg ends  
  27. end start  
           and al,11011111b   ----這句是將小寫轉換成大寫(不明白可以對比一下大寫和小寫字母的ascii碼)
           or  al,00100000b    ---- 這句是將大寫轉換成小寫

           inc  bx                   ----加1的意思

在高級語言中,遇到'BaSiC'這種字符串我們經常會將它存儲在數組中,以便進行相關的操作,那麼作爲一種底層的語言,讓我們看看“數組”是怎麼實現的:

datasg segment

       db  'BaSic'

       db  'iNfOrMaTiOn'

datasg ends中的第一個字符傳轉化爲大寫字母,第二個字符串轉換成小寫字母

實現程序如下:

[plain] view plaincopy
  1. assume cs:codesg,ds:datasg  
  2. datasg  segment  
  3.      db 'BaSiC'  
  4.      db 'iNfOrMaTiOn'  
  5. datasg ends  
  6.   
  7. codesg segment   
  8.       start: mov ax,datasg  
  9.                mov ds,ax  
  10.                mov  bx,0  
  11.                mov cx,5  
  12.             s:mov al,[bx]  
  13.                and al,11011111b  
  14.                mov[bx],al  
  15.                mov al,<span style="color:#ff0000;">[5+bx]       </span>;可以寫成5[bx]  
  16.                 or al,00100000b  
  17.                 mov [bx],al  
  18.                 mov <span style="color:#ff0000;">[5+bx]</span>,al        
  19.                 inc bx  
  20.                 loop s  
  21.   
  22.                 mov ax,4c00h  
  23.                 int 21h  
  24. codesg ends  
  25. end start     
這裏採用了一種[bx+idata]的結構,可以通過這種結構來尋找到每個字符串的開端,非常的方便。

在8086CPU中還有兩種寄存器,它們的作用和bx很相似,它們就是si和di(它們不能拆成兩個8字節寄存器來用)

我們可以利用它們使用[bx+si]、[bx+di]、[bx+si+idata]和[bx+di+idata]的結構,實現高級語言的多重循環。

代碼如下:

[plain] view plaincopy
  1. assume cs:codesg,ds:datasg,ss:stacksg  
  2. datasg segment  
  3.      db 'ibm              '  
  4.      db 'ibm              '  
  5.      db 'ibm              '  
  6.      db 'ibm              '  
  7. datasg ends  
  8. stacksg segment  
  9.     dw 0,0,0,0,0,0,0,0  
  10. stacksg ends  
  11.    
  12. codesg segment  
  13.      start:mov ax,stacksg  
  14.              mov ss,ax  
  15.              mov sp,16  
  16.              mov ax,datasg  
  17.              mov bx,0  
  18.              mov cx,4  
  19.          s0:push cx  
  20.              mov  si,0  
  21.              mov cx,3  
  22.            s:mov al,[bx+si]  
  23.               and al,11011111b  
  24.               mov [bx+si],al  
  25.               inc si  
  26.               loop s  
  27.               add  bx,16  
  28.               pop cx  
  29.               loop s0  
  30.                 
  31.               mov ax,4c00h  
  32.               inc 21h  
  33. codesg ends  
  34. 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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章