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