8086匯編語言初學者教程(第三部分)
變量
變量是一個內存地址。對於編程者來說,使用諸如名稱為“var1”這樣的
變量保存數據遠遠比使用5a73:235b這樣的地址容易的多。特別是當你
使用10個以上的變量的時侯。
編譯器支持這兩種變量 BYTE 和 WORD.(字節和字)
聲明變量的方法: name DB value 名稱 DB 值 name DW value 名稱 DW 值 DB - stays for Define Byte. DW - stays for Define Word. name -可以是任何字母與數字構成,但是必須由字母 開頭。可以通過不命名來聲明一個沒有名稱的的變量(這個變量 只有地址,沒有名稱) value - 可以是任何數值支持三種進制(十六進制,二進制和 十進制),你可以使用"?"符號表示初始值沒有確定。 |
你可能從第二章了解到, MOV 指令是將數值從源拷貝到目的。
讓我們再看一個 MOV 指令的例子
#MAKE_COM#
ORG 100h
MOV AL, var1
MOV BX, var2
RET ; stops the program.
VAR1 DB 7
var2 DW 1234h
|
將上面的代碼拷貝到emu8086源程序編輯器中,按下F5鍵編譯
並在模擬器中執行。你會看到如下畫面
從畫面可以看出,反編譯后的代碼同源程序很相似,不同的是變量
被具體的內存地址取代。當編譯器生成機器代碼它會自動將變量名稱
用該變量的便宜量代替。默認情況下,DS 寄存器存放段偏移
(當執行com文件的時侯,DS 寄存器的值同 CS 寄存器(代碼段)
的值一樣)。
內存第一列是偏移(offset),第二列是一個十六進制值
(hexadecimal value),第三列是十進制(decimal value),
最后一列是 ASCII 字符。
編譯器是非大小寫敏感的,所以 “VAR1” 同 “var1” 都是同一個變量。
VAR1變量的偏移是0108h,物理地址是0b56:0108
var2 變量的偏移是0109h,物理地址是 0b56:0109
這個變量是字,它占用2字節。這里假定低字節存放在低地址,
所以34h位於12h前面。
你可以看到,在RET指令后面還有一些指令,這樣是因為反編譯工具
無法判斷數據從什么地方開始。同樣,你可以寫出直接使用DB的程序.
#MAKE_COM#
ORG 100h
DB 0A0h
DB 08h
DB 01h
DB 8Bh
DB 1Eh
DB 09h
DB 01h
DB 0C3h
DB 7
DB 34h
DB 12h
|
將上面的代碼拷貝到emu8086原代碼編輯器,按下F5鍵編譯,並在
模擬器中運行,你可以看到同樣的反匯編結果,得到同樣的功能。
根據上面,你可以猜測,編譯器將源程序轉化為一些字節的集合,
這個集合被稱作機器代碼(machine code),處理器懂得他們,並且
執行它們。
ORG 100是一個編譯指令(它告訴編譯器如何處理源代碼)
當你使用變量的時侯,這條指令特別重要。它通知編譯器
可執行程序將被調入偏移量是100h(256字節)的位置,
有了它,編譯器就可以計算出所有變量的正確地址,然后
用這些地址(偏移量)來代替變量名稱。上面的這些指令
不會真正的編譯為任何機器代碼。
為何可執行程序總是被裝入偏移量100h?操作系統在CS寄存器
(代碼段)存儲着程序信息,比如命令行方式下的參數等等。
盡管上面只是一個COM文件的例子,EXE文件調入在偏移量0000
的位置,他使用特定的段保存變量。我們在下面會學習到關於
EXE文件的知識。
數組
數組可以看作是變量鏈。一個字符串是一個字節數組的例子,
其中每一個字符都當作一個ASCII碼的值(0....255)
下面是一些定義數組的例子
a DB 48h, 65h, 6Ch, 6Ch, 6Fh, 00h
b DB 'Hello', 0
b是一個數組,當編譯器發現引用了字符串值后,會自動將這些
字符轉化為對應的字節。下面圖表表示的就是聲明數組后在內存
中的分布:
你可以使用方括號做下標直接訪問到數組中的值,例如:
MOV AL, a[3]
同樣,你還可以使用任意一個內存索引寄存器BX, SI, DI, BP,
例如:
MOV SI, 3
MOV AL, a[SI]
如果你想聲明比較復雜的數組,你可以使用DUP指令 形式如下
number DUP ( value(s) )
number - 重復的數量(任意常數)
value - 將要復制的表達式
例如:
c DB 5 DUP(9)
就相當於如下定義:
c DB 9, 9, 9, 9, 9
另外一個例子:
d DB 5 DUP(1, 2)
等同於
d DB 1, 2, 1, 2, 1, 2, 1, 2, 1, 2
當然,如果需要存放超過255或者小於-128的數值,你還可以
使用DW來代替 DB。但是DW不能用於聲明字符串。
DUP命令展開后不能超過1020個字符(上一個例子中展開之后
是13個字符),如果需要聲明請將它們分成兩行(這樣,內存中
得到的仍然是一個大數組)。
取得變量地址
LEA指令(Load Effective Address 讀取有效地址)或者OFFSET
指令。OFFSET 和 LEA二者都能夠獲得變量的偏移量。
LEA在使用中更有效,這是因為它能返回索引變量的地址。取得
變量地址在很多情況下是非常有用的,例如你打算向一個過程
傳遞參數。
注意:
在編譯過程中使用如下聲明數據類型
BYTE PTR - 表示字節
WORD PTR - 表示字(2個字節)
例如:
BYTE PTR [BX] ;按字節訪問
or
WORD PTR [BX] ;按字訪問
Emu8086 容許使用如下更簡潔的前綴
b. - 等價於上面的 BYTE PTR
w. - 等價於上面的 WORD PTR
有時,編譯器可以自動計算出數據類型,但是如果一個參與運算
的數是立即數,這種方法就不可靠了。
第一個例子:
ORG 100h
MOV AL, VAR1 ; 將變量var1的數值放入al以便檢查
LEA BX, VAR1 ; 將var1的地址存入 BX.
MOV BYTE PTR [BX], 44h ; 修改變量var1的內容
MOV AL, VAR1 ; 將變量VAR1的數值放入AL以便檢查
RET
VAR1 DB 22h
END
|
下面是另外一個例子,用OFFSET指令代替LEA:
ORG 100h
MOV AL, VAR1 ; 將變量VAR1的值放入AL以便檢查.
MOV BX, OFFSET VAR1 ; 將變量VAR1的地址放入 BX.
MOV BYTE PTR [BX], 44h ; 修改變量VAR1內容
MOV AL, VAR1 ;將變量VAR1的值放入 AL以便檢查.
RET
VAR1 DB 22h
END
|
上面例子的功能相同。
這些語句:
LEA BX, VAR1
MOV BX, OFFSET VAR1
都將生成同樣的機器代碼: MOV BX, num
num 是16位變量偏移
請注意,只有這些寄存器可以放入方括號中(作為內存指針)
BX, SI, DI, BP(請參考本教程前述章節)
常量
常量同變量很相似,但是它一直存在。定義一個變量之后,它的值
不會改變。使用EQU定義常量:
name equ <任意表達式>
例如:
k EQU 5 MOV AX, k |
上面的例子等同於如下代碼:
MOV AX, 5 |
在程序執行過程中你可以選擇模擬器"View"菜單下的"Variables"
你可以點一個變量然后設置Elements屬性為數組大小來查看數組。
匯編語言對於數據類型並不嚴格,這樣以來所有的變量都可以被看
作是數組。
變量可以顯示為下列進制
- HEX - 十六進制 hexadecimal (基底 16).
- BIN - 二進制 (基底 2).
- OCT - 八進制 (基底 8).
- SIGNED - 有符號十進制 (基底 10).
- UNSIGNED - 無符號十進制 (基底 10).
- CHAR - ASCII 碼 (一共有256個符號,其中一些符號是不可見的).
程序運行的時侯,你可以通過雙擊它來編輯變量值,或者選中
之后點Edit按鈕。
十六進制數值以"h"結尾,二進制以"b" 結尾,八進制以"o" 結尾
十進制沒有結尾。字符串用這樣的方式表示:'hello world',0
(結尾以0表示)
數組按照如下輸入:
1, 2, 3, 4, 5
(數組可以是一組字節或者字,這取決於你想以字節還是字的
方式編輯)
表達式會自動計算,例如,輸入如下表達式
5 + 2
會自動計算為7。等等....