Intel汇编语言程序设计学习-第三章 汇编语言基础-下
3.4 ?定義數(shù)據(jù)
3.4.1 ?內(nèi)部數(shù)據(jù)類型
? ? MASM定義了多種內(nèi)部數(shù)據(jù)類型,每種數(shù)據(jù)類型都描述了該模型的變量和表達(dá)式的取值集合。數(shù)據(jù)類型的基本特征是以數(shù)據(jù)位的數(shù)目量的大小:8,16,32,,48,64,80位。其他特征(如有符號(hào)、指針、浮點(diǎn)等)主要是為了方便程序員記憶變量中存儲(chǔ)的數(shù)據(jù)的類型。例如,聲明為DOWRD變量邏輯上存儲(chǔ)的是一個(gè)32位整數(shù)、一個(gè)32位的浮點(diǎn)數(shù)或一個(gè)32位的指針。MASM匯編器默認(rèn)情況下是大小寫不敏感的,因此偽指令如DWORD可寫成dword,Dword.dWord等大小寫混合的格式。
下表中,除了最后三種之外,其余的所有的數(shù)據(jù)類型都是整數(shù)數(shù)據(jù)類型。表中IEEE符號(hào)是指IEEE委員會(huì)發(fā)布的標(biāo)準(zhǔn)實(shí)數(shù)格式。
?
3.4.2 ?數(shù)據(jù)定義語句
? ? ? 在數(shù)據(jù)定義語句中使用BYTE(定義字節(jié))和SBYTE(定義有符號(hào)字節(jié))偽指令,可以為一個(gè)或多個(gè)有符號(hào)及無符號(hào)字節(jié)分配存儲(chǔ)空間,每個(gè)初始值必須是8位整數(shù)表達(dá)式或者字符常量。例如:
? ? value1 ?BYTE ??‘A’????;字符常量
? ? value2 ?BYTE ???0 ???;最小無符號(hào)字節(jié)常量
? ? value3 ?BYTE ??255 ??;最大無符號(hào)字節(jié)常量
? ? value4 ?SBYTE ?-128 ??;最小有符號(hào)字節(jié)常量
? ? value5 ?SBYTE ?+127 ?;最大有符號(hào)字節(jié)常量
? ? 使用問號(hào)代替初始值可以定義未初始化的變量,這表示將由可執(zhí)行指令在運(yùn)行時(shí)為變量動(dòng)態(tài)賦值:
value6 BYTE ?
? ? 可選的變量名是一個(gè)標(biāo)號(hào),標(biāo)記該變量相對(duì)其所在段開始的偏移。例如,假設(shè)value1位于數(shù)據(jù)段的偏移0000出并占用1個(gè)字節(jié)的存儲(chǔ)空間,那么value2將位于段內(nèi)偏移值0001的地方:
value1 BYTE 10h
value2 BYTE 20h
遺留的DB偽指令可以定義有符號(hào)或無符號(hào)的8位的變量:
val1 DB ?255 ??;無符號(hào)字節(jié)
val2 DB ?-128 ??;有符號(hào)字節(jié)
多個(gè)初始值
如果一條數(shù)據(jù)定義語句中有多個(gè)初始值,那么標(biāo)號(hào)(名字)僅僅代表第一個(gè)初始值的偏移。在下列中,假設(shè)list位于偏移0000處,那么值10將位于0000處值20位于0001處,一次類推。
list BYTE 10,20,30,40
下圖以字節(jié)序列的形式顯示了list的定義情況:
?
并非所有的數(shù)據(jù)定義都需要標(biāo)號(hào)(名字),如果想繼續(xù)以list開始的字節(jié)數(shù)組,就可以在隨后的行上接著定義其他數(shù)據(jù):
list BYTE 10,20,30,40
?????BYTE 50,60,70,80
?????BYTE 81,82,83,84
????在單條數(shù)據(jù)定義語句中,初始值可使用不同的基數(shù),字符和字符串課可以自由混用。在下面例子中,list1和list2的內(nèi)容是相同的。
list1 BYTE 10 ,32 ,41h ,00100010b
list2 BYTE 0Ah,20h,’A’?,22h
定義字符串
要想定義字符串,應(yīng)將一組字符用單引號(hào)或雙引號(hào)括起來。最常見的字符串是以空字符(也稱為NULL,0)結(jié)尾的字符串,C/C++,Java程序使用這種類型的字符串:
????greeting1 BYTE "Good afternoon",0
????greeting2 BYTE 'Good night',0
每個(gè)字節(jié)都占用一個(gè)字節(jié),對(duì)于前面提到過的數(shù)據(jù)定義中多個(gè)初始值必須以逗號(hào)分隔的規(guī)則,字符串是一個(gè)例外。如果沒有這種例外,就不得不這樣定義greeting1:
greeting1 BYTE ‘G’,’o’,’o’....etc.
這樣太冗長(zhǎng)乏味了!
字符串可以占用多行,而無需為每一行都提供一個(gè)標(biāo)號(hào),如下例所示:
?
????十六進(jìn)制字節(jié)0Dh和0Ah也稱為CR/LF(回車換行符)或航結(jié)束字符,在向標(biāo)準(zhǔn)輸出設(shè)備上寫的時(shí)候,回車換行將光標(biāo)移至下面一行左右開始處。
續(xù)行符(\)用來把兩行連接陳過一條程序語句,續(xù)行符只能放在每行的最后,下面的語句是等價(jià)的:
???greeting1 BYTE "Good afternoon",0
???greeting1 BYTE \
"Good afternoon",0
DUP操作符
DUP操作符使用一個(gè)常量表達(dá)式作為計(jì)數(shù)器為多個(gè)數(shù)據(jù)項(xiàng)分配存儲(chǔ)空間。在為字符串和數(shù)組分配空間的時(shí)候,DUP偽指令就十分有用。初始化和未初始化數(shù)據(jù)均可使用DPU偽代碼定義:
BYTE 20 DUP(0) ??????;20字節(jié),全部等于0
BYTE 20 DUP(?) ??????;20字節(jié),未初始化
BYTE 4 ?DUP("STACK") ;20字節(jié):"STACKSTACKSTACK"
3.4.4 ??定義WORD和SWORD數(shù)據(jù)
???和BYTE一樣,這里就簡(jiǎn)單寫幾個(gè)例子。
???word1 WORD 65535
???word2 SWORD -32768
???word2 WORD ?
???val1 DW 65535
???val2 DW -32768
???myList WORD 1,2,3,4,5
3.4.5 ?定義DWORD和SDWORD數(shù)據(jù)
和BYTE一樣,直接上例子
val1 DWORD ???13245678h
val2 SDWORD ??-2147483684
val3 DWORD ???20 DUP(?)
val1 DD ???????12345678h
val2 DD ???????-2147483648
myList DWORD ?1,2,3,4,5
3.4.6 ?定義QWORD數(shù)據(jù)
quad1 ?QWORD 1234567812345678h
quad1 ?DQ ????1234567812345678h
3.4.7 ?定義TBYTE數(shù)據(jù)
val1 TBYTE ?100000000000000123456789Ah
val1 DT ????100000000000000123456789Ah
3.4.8 ?定義實(shí)數(shù)
REAL4定義4字節(jié)的單精度實(shí)數(shù),REAL8定義8字節(jié)的雙精度實(shí)數(shù),REAL10定義10字節(jié)的擴(kuò)展精度實(shí)數(shù)。每個(gè)偽指令都要求一個(gè)或多個(gè)與其對(duì)應(yīng)的數(shù)據(jù)尺寸相匹配的實(shí)數(shù)常量初始值,例如:
rVal1 ???????REAL4 ??-2.1
rVal2 ???????REAL8 ??3.2E-260
rVal3 ???????REAL10 ?4.6E+4096
ShortArray ??REAL4 ??20 DUP(0.0)
下表列出了每種實(shí)數(shù)類型的最少有小數(shù)據(jù)位和最大有效數(shù)據(jù)位
?
遺留的DD,DQ和DT偽指令也可以用于定義實(shí)數(shù):
rVal1 ??DD ??-1.2
rVal2 ??DQ ??3.2E-260
rVal3 ??DT ??4.6E+4096
3.4.9 ?小尾順序
Intel處理器使用稱為小尾順序(little endian order)的方案存取內(nèi)存數(shù)據(jù),小尾的含義就是變量的最低有效字節(jié)存儲(chǔ)在地址值最小的地址單元中,其余字節(jié)在內(nèi)存中按順序連續(xù)存儲(chǔ)。
考慮一下雙字節(jié)12345678h在內(nèi)存中的存儲(chǔ)情況,如果將該雙字存儲(chǔ)在偏移0處,78h將存儲(chǔ)在第一個(gè)字節(jié)中,56h存儲(chǔ)在第二個(gè)字節(jié)中,其余存儲(chǔ)在第三和第四字節(jié)。如下圖:
?
大尾則相反:
3.4.10 ?為AddSub程序添加變量
現(xiàn)在寫一個(gè)AddSub2,在里面添加一些變量:
TITLE Add and Subtract ,Version 2 (AddSub2.asm)
;This program adds and subtracts 32-bit unsigned
;integers and stores the sum in a variable
INCLUDE Irvine32.inc
.data
val1 DWORD 10000h
val2 DWORD 40000h
val3 DWORD 20000h
finalVal DWORD ?
.code
main PROC
mov ?eax,val1 ???;start whith 10000h
add ?eax,val2 ???;add 40000h
sub ?eax,val3 ???;subtract 20000h
mov ?finalVal,eax;store the result (30000h)
call DumpRegs ???;display the registers
exit
main ENDP
END main
這個(gè)新的程序是如何工作的呢?首先,變量val1里的整數(shù)倍送到EAX寄存器:
mov eax,val1 ????;start with 10000h
接下來,變量val2存儲(chǔ)的整數(shù)值被加到EAX寄存器中:
add eax,val2 ????;add 40000h
再接下來,EAX寄存器內(nèi)的整數(shù)值減掉變量val3內(nèi)的整數(shù)值:
sub eax,val3 ????;subtract 20000h
最后,EAX寄存器內(nèi)的整數(shù)被復(fù)制到變量finalVal內(nèi):
Mov finalVal,eax ;store the result(30000h)
3.4.11 ?未初始化數(shù)據(jù)的聲明
“.DATA?”偽指令可用于聲明未初始化數(shù)據(jù),”.DATA?”在定義大塊的未初始化數(shù)據(jù)時(shí)非常有用,因?yàn)樗梢詼p少編譯后的程序的尺寸,下面的聲明是很有效率的:
.data
smallArray DWORD 10 DUP(0) ??;40字節(jié)
.data?
bigArray ??DWORD 5000 DUP(?) ;20000字節(jié),未初始化
相反,下面的代碼例子編譯后將生成大于20000字節(jié)的程序:
.data
smallArray DWORD 10 DUP(0) ??;40字節(jié)
bigArray ??DWORD 5000 DUP(?) ;20000字節(jié),未初始化
?
我自己做了個(gè)測(cè)試,發(fā)現(xiàn)果真有效果,除了上面的數(shù)據(jù)定義,其他配置選項(xiàng)或者是代碼完全一樣,下面是結(jié)果:
?
我的天!差距還真是大。
混合代碼和數(shù)據(jù):匯編器允許程序在代碼和數(shù)據(jù)之間來回切換。在定義僅在局部程序中使用的變量時(shí),這是非常方便的。下面的例子在兩端代碼中直接插入并創(chuàng)建一個(gè)名為temp的變量:
.code
??mov eax,ebx
.data
??temp DWORD ?
.code
??mov temp,eax
3.5 ?符號(hào)常量
符號(hào)常量(或符號(hào)定義)是通過將標(biāo)示符(或符號(hào))與整數(shù)表達(dá)式或文本聯(lián)系起來而創(chuàng)建的。與保留存儲(chǔ)空間的變量定義不同,符號(hào)常量并不占用任何實(shí)際的存儲(chǔ)空間。符號(hào)常量?jī)H在編譯期間匯編器掃描程序的時(shí)候使用,在運(yùn)行期間不能更改。下表總結(jié)了二者的區(qū)別:
?
??接下來講述如何使用等號(hào)偽指令(=)來創(chuàng)建代表整數(shù)表達(dá)式的符號(hào)常量,之后,還將講述如何使用EQU和TEXTEQU偽指令創(chuàng)建可代表任意文本的符號(hào)常量。
3.5.1 ?等號(hào)偽指令
????等號(hào)偽指令將符號(hào)名和整數(shù)表達(dá)式聯(lián)系起來。格式如下:
名字=表達(dá)式
通常,表達(dá)式(expression)是32位的整數(shù)值,匯編程序的時(shí)候,所有出現(xiàn)名字(name)的地方都由匯編器在預(yù)處理階段替換為對(duì)應(yīng)表達(dá)式的值。例如,當(dāng)編譯器遇到下列語句的時(shí)候:
COUNT ??= ??500
mov ????ax,COUNT
將生成并編譯下面的語句:
mov ax,500
為什么要使用符號(hào):跟C++的宏有點(diǎn)像(但不是,=號(hào)后面只能是表達(dá)式),改一個(gè)可以全改。
鍵值的定義:程序中經(jīng)常要為重要的鍵盤字符定義符號(hào),例如27是Esc鍵的ASCII碼值:
Esc_key = 27
這樣在同一程序中,如果語句中使用了這個(gè)符號(hào)而不是一個(gè)立即數(shù),那么語句的額含義就不言自明了。例如,應(yīng)該使用下面的語句:
mov ?al,Esc_key ?;好的風(fēng)格
而不是:
mov ?al,27 ?????;不好的風(fēng)格
??使用DUP操作符:3.4.3節(jié)講述了如何使用DUP操作符為數(shù)組和字符串分配存儲(chǔ)空間。好的編程風(fēng)格是使用符號(hào)常量作為DUP操作符的計(jì)數(shù)器,以簡(jiǎn)化程序的維護(hù)。在下例中,如果COUNT已經(jīng)預(yù)先定義,那么就可以用在下面的數(shù)據(jù)定義中:
array ?COUNT DUP(0)
重定義:同一程序中以”=”定義的符號(hào)可重定義。下面展示了在每次改變COUNT值時(shí)編譯器是如何對(duì)其進(jìn)行求值的:
COUNT = 5
mov al,COUNT ????;AL = 5
COUNT = 10
mov al,COUNT ????;Al = 10
COUNT = 100
mov al,COUNT ????;Al = 100
行號(hào)(如COUNT)值的改變與運(yùn)行時(shí)語句執(zhí)行的順序無關(guān),相反符號(hào)值是按照匯編器對(duì)源代碼的順序處理進(jìn)行改變的。
3.5.2 ?計(jì)算數(shù)組和字符串的大小
使用數(shù)組的時(shí)候,有時(shí)候需要知道數(shù)組的大小。下列使用一個(gè)名為listSize的常量聲明數(shù)組list的大小:
list ?BYTE ?10,20,30,40
ListSize = 43
在數(shù)組可能會(huì)改變大小的時(shí)候,手動(dòng)計(jì)算其大小并不是一個(gè)好主意。如果要為list添加幾個(gè)字節(jié),就需要同時(shí)修正ListSize。處理這種情況講好的辦法是讓編譯器自動(dòng)為我們計(jì)算ListSIze的值。MASM用($)減掉list的地址偏移值就得到了ListSize值(4):
list BYTE 10,20,30,40
ListSize = ($ - list)
ListSize必須緊跟在list之后。例如,下例中ListSize的值過大,這是因?yàn)?/span>ListSize包括了var2占用的存儲(chǔ)空間:(24)
??List ??BYTE ?10,20,30,40
??var2 ?BYTE ?20 DUP(?)
??ListSize = ($ - list)
與其手動(dòng)計(jì)算字符串的長(zhǎng)度,不如讓編譯器自動(dòng)做這種工作:(57)
myString BYTE "This is a long string, containing"
?????????BYTE "any number of characters"
myString_len = ($ - myString)?
字?jǐn)?shù)組和雙字?jǐn)?shù)組:如果數(shù)組的每個(gè)元素都是16位的字,以字節(jié)計(jì)算的數(shù)組長(zhǎng)度必須初一2才能得到數(shù)組元素的個(gè)數(shù)(4)
list WORD 100h ,200h ,300h ,400h
ListSize = ($ - list) / 2
與此類推,雙字?jǐn)?shù)組的呢個(gè)元素時(shí)4字節(jié)長(zhǎng)的,因此/4 (3)
list DWORD 100000h ,200000h,300000h
ListSize = ($ - list) / 4
3.5.3 ?EQU偽指令
??EQU偽指令將符號(hào)名同整數(shù)表達(dá)式或任意文本聯(lián)系起來,有一下三種格式:
??name EQU ?expression
??name EQU ?symbol
??name EQU ?<text>
??在第一種格式中,表達(dá)式(expression)必須是有效的整數(shù)表達(dá)式;在第二種格式中,符號(hào)(synbol)必須是已經(jīng)=或EQU定義的符號(hào)名;第三種格式中,尖括號(hào)內(nèi)可以是任意文本。當(dāng)匯編器在后面遇到已經(jīng)定義的 “名字”(name)時(shí),就用改名字代表的整數(shù)值或文本替代。當(dāng)定義任何非整數(shù)值的時(shí)候,EQU就可能非常有用了,例如實(shí)數(shù)常量就可以用EQU定義:
P1 EQU <3.1416>
例子:下列把一個(gè)符號(hào)同一個(gè)字符串聯(lián)系了起來,然后使用該符號(hào)創(chuàng)建了一個(gè)變量:
pressKey EQU <”Press any key to continue...”,0>
.
.
.data
pronpt BYTE pressKey
再來一個(gè)例子:
matrix1 EQU 10*10
matrix2 EQU <10*10>
.data
M1 WORD matrix1
M2 WORD matrix2
匯編器為M1和M2生成不同的數(shù)據(jù)定義,martix1中的整數(shù)表達(dá)式被計(jì)算并賦給M1,而matrix2內(nèi)的文本將直接復(fù)制到M2的數(shù)據(jù)定義中,實(shí)際的等效語句是:
M1 WORD 100
M2 WORD 10*10
不允許重定義:與”=”偽指令不同,用EQU定義的符號(hào)不能再同一源代碼文件中重定義,這個(gè)限制能夠防止已存在的符號(hào)被無意中賦了新值。
3.5.4 ?TEXTEQU偽指令
TEXTEQU偽指令與EQU非常相似,也可用來創(chuàng)建文本宏(text macro)。它有三種不同的使用格式:第一種格式將文本賦給符號(hào);第二種格式將已定義的文本宏內(nèi)容賦給符號(hào);第三種格式將整數(shù)表達(dá)式常量賦給符號(hào)。
name TEXTEQU <text>
name TEXTEQU textmacro
name TEXTEQU %constExpr
例如,prompt1變量使用了continueMsg文本宏:
continueMsg TEXTEQU <”Do you wish to continue (Y/N)?”>
.data
Prompt1 BYTE continueMsg
可用文本宏方便地創(chuàng)建其他的文本宏。在下例中,count被設(shè)置為包含宏rowSize的整數(shù)表達(dá)式的值,接下來符號(hào)move被定義為mov,setupAL則由move和count共同創(chuàng)建:
rowSize=5
count ??TEXTEQU %(rowSize*2)
move ???TEXTEQU <mov>
setupAL TEXTEQU <move al ,count>
因此下面的語句:
setupAl
將被匯編成
mov al,10
與EQU偽指令不同的是,TEXTEQU可以在程序中重新定義。
?
?
3.6 ?實(shí)地址模式程序設(shè)計(jì)(可選)
為MS-DOS設(shè)計(jì)的程序必須是云心更是低至模式下的16位應(yīng)用。實(shí)地址模式應(yīng)用程序使用16位的段并且遵循2.3.1節(jié)描述的分段尋址方案。如果使用的是IA-32處理器,則仍然可以使用32位通用寄存器存取數(shù)據(jù)。
3.6.1 ?基本的修改
將本章中的32位程序轉(zhuǎn)換成實(shí)地址模式程序,需要做一些修改:
INCLUDE偽指令要引用另外一個(gè)不同的庫文件:
INCLUDE Irvine16.inc
在啟動(dòng)過程(main)的開始插入兩條額外的指令,這兩條指令將DS寄存器初始化為數(shù)據(jù)段的其實(shí)地址,數(shù)據(jù)段的其實(shí)地址用MASM的預(yù)定義常量@data表示:
mov ax ,@data
mov ds,ax
如何匯編16位程序的步驟請(qǐng)參見www.asmirvine.com
數(shù)據(jù)標(biāo)號(hào)和代碼標(biāo)號(hào)的偏移(地址)是16位而不是32位。
不能把@data直接送DS和ES寄存器,因?yàn)?/span>MOV指令不允許直接向段寄存器傳送常量;
實(shí)地址AddSub程序:
INCLUDE Irvine16.inc ?;changed
.data
val1 DWORD 10000h
val2 DWORD 40000h
val3 DWORD 20000h
finalVal DWORD ?
.code
main PROC
????mov ax,@data ;new
mov ds,ax ???;new
?
mov eax,val1
add eax,val2
sub eax,val3
mov finalVal,eax
call DumpRegs
exit
main ENDP
END main
PS:這個(gè)我本機(jī)沒有編過,我的環(huán)境是vs2012+masm32 debug模式。不查了,用到的時(shí)候再說吧,實(shí)地址模式我?guī)缀跤貌簧稀?/span>
3.7 ?本章小結(jié)
?
?
總結(jié)
以上是生活随笔為你收集整理的Intel汇编语言程序设计学习-第三章 汇编语言基础-下的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Intel汇编语言程序设计学习-第三章
- 下一篇: Intel汇编语言程序设计学习-第四章