在前面的幾個章節中,我們的程式都是只有一個代碼段,本章我們開始學習如何編寫包含多個段的程式。 1、在代碼段中使用數據 首先考慮這樣一個問題,計算以下8個數據的和,結果存放在ax寄存器中: 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H 在前面的課程中, ...
在前面的幾個章節中,我們的程式都是只有一個代碼段,本章我們開始學習如何編寫包含多個段的程式。 1、在代碼段中使用數據 首先考慮這樣一個問題,計算以下8個數據的和,結果存放在ax寄存器中: 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H 在前面的課程中,我們都是累加某些記憶體單元中的數據,並不關心數據本身,可現在我們要累加就是已經給定了數值的數據。 代碼如下:
1 assume cs:codesg 2 codesg segment 3 dw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H 4 mov bx,0 5 mov ax,0 6 mov cx,8 7 s: add ax,cs:[bx] 8 add bx,2 9 loop s 10 mov ax,4c00h 11 int 21h 12 codesg ends 13 endView Code
在該程式第一行中的"dw"的含義是定義字型數據,即define word。在這裡,我們使用dw定義了8個字型數據(數據之間用逗號分隔),它們所占的記憶體空間大小為16個位元組。 將代碼進行編譯,連接後,使用debug進行調試,我們可以看到,在地址0B4FH:0000這個地址單元中,存放了我們定義的字型數據: 在0B4FH:0010開始的地址單元中,存放了我們的指令: 但是我們查看0B4F:0000這個地址單元時,它卻並沒有存放我們的指令(按理說應該是會存放我們的指令的,但是我們的指令卻是存放在0B4FH:0010開始的地址單元中): 出現這樣的原因是,cpu讀取的是指令所對應的機器碼,從0B4F:0000開始的地質單元中,存放的是由數據0123h、0456h等我們預先定義好的字型數據轉換成機器碼所對應的指令,這樣造成了cpu的誤讀。要解決這個問題,我們就應該給程式指定一個入口,而不是直接從定義字型數據的指令開始,所以程式應該改為如下所示:
1 assume cs:codesg 2 codesg segment 3 dw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H 4 start:mov bx,0 5 mov ax,0 6 mov cx,8 7 8 s: add ax,cs:[bx] 9 add bx,2 10 loop s 11 12 mov ax,4c00h 13 int 21h 14 codesg ends 15 end startView Code
我們重新編譯連接這個程式,然後使用debug調試後,使用r命令查看寄存器的情況,可以發現,cs:ip預設就指向了程式的第一條指令,這是因為我們在程式中定義了一個程式的入口,但是程式是根據最後的end來查找程式的入口的,這一點要特別提醒一下。debug調試如圖所示: 2、在代碼段中使用棧 首先我們還是考慮一個問題,利用棧將程式定義的數據逆序存放,這個程式的代碼應該如何來寫? 當我們看到逆序存放這樣的字眼的時候,就應該首先想到棧,應為要實現逆序,就必定會用到棧“LIFO”這個特性(後進先出)。所以程式的代碼如下所示:
1 assume cs:codesg 2 codesg segment 3 dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h 4 dw 0,0,0,0,0,0,0,0 ;這裡用dw定義8個字型數據,在程式載入後,將取得8個字的記憶體空間,存放這8個數據,我們在後面的程式中 ;就將這段空間當做棧來使用。 5 6 start:mov ax,cs 7 mov ss,ax 8 mov sp,32 ;這裡設置棧頂ss:sp指向cs:32 9 mov bx,0 10 mov cx,8 11 s: push cs:[bx] 12 add bx,2 13 loop s ;這裡是將代碼段0~16單元中的8個字型數據依次入棧 14 15 mov bx,0 16 mov cx,8 17 s0: pop cs:[bx] 18 add bx,2 19 loop s0 ;這裡依次出棧8個字型數據到代碼段0~16單元中 20 21 mov ax,4c00h 22 int 21h 23 24 codesg ends 25 end startView Code
在編譯連接這段程式後,我們使用debug來查看程式的運行情況: 我們是用t和d命令,來看看執行指令後,記憶體單元中的存儲情況: 此時使用d命令來查看記憶體的情況:
3、將數據、代碼、棧放入不同的段中 在前面的程式中,我們將數據、代碼、棧放在了同一個段中,這樣使程式顯得很混亂。假如程式中的數據、代碼、棧不大的話,這樣做沒問題,但是程式變大後,程式將變得很難看。在8086CPU中,如果數據、代碼、棧的空間需求大於了64kb的話,這三者就不能存放在同一個段中,需要分開來存放。那麼應該如何定義多個段呢? 我們使用定義代碼段一樣的方法來定義多個段,在這些段裡面定義需要的數據,或通過定義數據來取得棧空間。現在我們還是考慮同樣的一個問題,將棧中的數據逆序存儲到一個寄存器中,代碼入下所示:
1 assume cs:code,ds:data,ss:stack 2 3 data segment 4 dw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H 5 data ends 6 7 stack segment 8 dw 0,0,0,0,0,0,0,0 9 stack ends 10 11 code segment 12 start:mov ax,stack 13 mov ss,ax 14 mov sp,16 ;設置ss指向stack,設置ss:sp指向stack:16,cpu執行完這些指令後,就將stack段當做一個棧空間來使用。 15 mov ax,data 16 mov ds,ax ;想要ds:bx訪問數據段,ds就要指向ax 17 mov bx,0 18 mov cx,8 19 s: push [bx] 20 add bx,2 21 loop s 22 23 mov bx,0 24 mov cx,8 25 s0: pop [bx] 26 add bx,2 27 loop s0 28 code ends 29 end startView Code 在上面的代碼中,我們定義了數據段data,棧段stack,代碼段code,分別用ds,ss,cs指向這些段。這樣做,看上去整個程式非常清晰明瞭。 CPU如何處理我們定義的段中的內容,是當做指令來執行,當做數據訪問,還是當做棧空間來使用,完全是靠程式中具體的彙編指令和彙編指令對CS:IP、SS:SP、DS等寄存器的設置來決定的。