シンボルがあるか否かを、データのサイズ(バイト、ワード、ダブルワード、等)に応じて、基本的なアセンブラ内部データ型(組み込みデータ型)のセットを識別する、整数または実数は、そのタイプを記述する。これらのタイプは、オーバーラップのかなりの程度、例えば、DWORD型(32ビット符号なし整数)を有しており、(32ビットの符号付き整数)交換タイプをSDWORDことができます。
いくつかは、プログラマは、この値が署名されている、しかし、アセンブラのために必須ではありませんが、プログラムを読むために人々に伝えるSDWORD、言うかもしれません。アセンブラは、オペランドのサイズを見積もります。したがって、例えば、プログラマは、32ビット整数DWORD、SDWORD又はREAL4タイプを指定することができます。
次の表は、内部データ型のすべてのリストを与える、いくつかのIEEEシンボルテーブルエントリは、IEEEコンピュータ学会本当の形式で発行され、標準を指します。
タイプ | 使い方 |
---|---|
バイト | 8ビット符号なし整数、バイトに対するB |
SBYTE | 8ビットの符号付き整数のために、Sを締結 |
ワード | 16ビット符号なし整数 |
剣 | 16ビット符号付き整数 |
DWORD | 32ビット符号なし整数、ダブル(ワード)のためにD |
SDWORD | 32ビット符号付き整数、符号付きSDビス(単語)を表します |
Fワード | 48の整数(farポインタプロテクトモード) |
QWORD | 64ビット整数、Qが表すテトラキス(ワード) |
テラバイト | 80(10バイト)の整数、T 10バイトの代表 |
REAL4 | 32ビット(4バイト)IEEEショート実 |
REAL8 | IEEE実の64ビット(8バイト)の長さ |
REAL10 | 80(10バイト)は、IEEEのリアルタイム拡張しました |
データ定義言語
データ定義言語(データ定義文)変数チュー・ストレージ・スペースのためにメモリ内に残っている、とオプションの名前を与えられました。データ定義ステートメントは、変数(テーブル)の内部データ型を定義します。
次のようにデータ定義の構文は次のとおりです。
[名前]ディレクティブ初期化子[、初期化子] ...
次は、データ定義文の例です。
DWORD 12345を数えます
どこで:
- 名前:仕様に準拠しなければならない変数識別子に割り当てられた名前(オプション)。
- 指令:データ定義文ディレクティブはBYTE、WORD、DWORD、SBTYE、SWORDまたは上記の表に記載されている他のタイプかもしれません。また、以下の表に定義された従来のデータ指示子であってもよいです。
指令 | 使い方 | 指令 | 使い方 |
---|---|---|---|
DB | 8ビット整数 | DQ | 64ビット整数または実数 |
DW | 16ビット整数 | DT | 80(10バイト)の整数の定義 |
DD | 32ビット整数または実数 |
データ定義は、値が0の場合でも、少なくとも初期値を持っています。その他の初期値は、もしあれば、カンマで区切って指定します。このようなバイトまたはワードなどの整数データ型、初期値(初期の)整数定数または変数タイプ、のための整数表現に一致します。
プログラマは、変数(ランダム化された値)を初期化したくない場合は、シンボルは、初期値として?を使用することができます。バイナリデータにアセンブラによってかかわらず、そのフォーマットのすべての初期値、、。初期値0011 0010B、32H、および50dが同じバイナリ値を持っています。
AddTwoプログラムに変数を追加します。
フロント「整数の加算や減算」セクションでは、AddTwoプログラムを記述し、それは今AddTwoSumとして知られている新しいバージョンを作成しています。このバージョンでは、変数sumを紹介し、それがプログラムの完全なリストに表示されます。
; .386 .MODELフラットAddTowSum.asm、4096のExitProcessプロト、dwExitCode .STACK STDCALL:DWORD .DATA和DWORD 0 .CODEメインPROCのMOVのEAX、5アドEAX、6 MOV和、EAX INVOKEのExitProcess、0主ENDP端主に
図13は、各行が実行され、行にブレークポイントを設定し、デバッガプログラムをステップ実行することができます。ライン15の実装後、その値を確認するために、変数sumにカーソルを合わせ。(デバッグセッション中)のWindowsでの[デバッグ]メニューで、[ウォッチ]を選択し、の4つの利用可能なオプション(ウォッチ1、ウォッチ2、Watch3またはウォッチ4)のいずれかを選択しますか、ウォッチウィンドウを開き、開いているプロセスは以下のとおりです。次に、マウスでの表示は、ウォッチウィンドウにドラッグして、変数sumをハイライトします。次の図は、大きな矢印は、実行後に線15で示された例では、合計の電流値を示しています。
BYTEデータの定義とSBYTE
バイト(バイトが定義されている)とSByte記憶スペース割り当てられた1つまたは複数の符号なしまたは署名された番号の(定義された符号付きバイト)。各初期値を記憶する場合、それは8ビットでなければなりません。例えば:
VALUE1 BYTE 'A'、文字定数値2のBYTE 0;符号なしバイト最小値3のBYTE 255;最大符号なしバイトvalue4のSBYTE -128;最小の符号付きバイトVALUE5のSBYTE 127;最大符号付きバイト
疑問符は、実行時にその変数に値を代入を意味し、初期化されていない変数の初期値になります(?):
value6のBYTE?
あるいは名前は、セグメントの先頭からオフセット変数に変数識別子を含む、ラベルです。例えば、データセグメント値1であれば0000のオフセット、および0001のオフセットで、1バイトのメモリに、自動的に値2です。
VALUE1 BYTE 10hの
値2バイト20H
DBディレクティブは、符号付きまたは符号なしの8ビット変数を定義することができます。
VAL1 DB 255;符号なしバイト
val2のDB -128;符号付きバイト
1)マルチ初期値
如果同一个数据定义中使用了多个初始值,那么它的标号只指出第一个初始值的偏移量。在下面的例子中,假设 list 的偏移量为 0000。那么,数值 10 的偏移量就为 0000, 20 的偏移量为 0001,30 的偏移量为 0002,40 的偏移量为 0003:
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
2) 定义字符串
定义一个字符串,要用单引号或双引号将其括起来。最常见的字符串类型是用一个空字节(值为0)作为结束标记,称为以空字节结束的字符串,很多编程语言中都使用这种类型的字符串:
greeting1 BYTE "Good afternoon",0 greeting2 BYTE 'Good night',0
每个字符占一个字节的存储空间。对于字节数值必须用逗号分隔的规则而言,字符串是一个例外。如果没有这种例外,greeting1 就会被定义为:
greeting1 BYTE 'G', 'o', 'o', 'd'….etc.
这就显得很冗长。一个字符串可以分为多行,并且不用为每一行都添加标号:
greeting1 BYTE "Welcome to the Encryption Demo program " BYTE "created by Kip Irvine.",0dh, 0ah BYTE "If you wish to modify this program, please " BYTE "send me a copy.",0dh,0ah,0
十六进制代码 0Dh 和 0Ah 也被称为 CR/LF (回车换行符)或行结束字符。在编写标准输出时,它们将光标移动到当前行的下一行的左侧。
行连续字符()把两个源代码行连接成一条语句,它必须是一行的最后一个字符。下面的语句是等价的:
greeting1 BYTE "Welcome to the Encryption Demo program "
和
greeting1
BYTE "Welcome to the Encryption Demo program "
3) DUP 操作符
DUP 操作符使用一个整数表达式作为计数器,为多个数据项分配存储空间。在为字符串或数组分配存储空间时,这个操作符非常有用,它可以使用初始化或非初始化数据:
BYTE 20 DUP ( 0 ) ;20 个字节,值都为 0 BYTE 20 DUP ( ? ) ;20 个字节,非初始化 BYTE 4 DUP ( "STACK" ) ; 20 个字节:
定义 WORD 和 SWORD 数据
WORD(定义字)和 SWORD(定义有符号字)伪指令为一个或多个 16 位整数分配存储空间:
word1 WORD 65535 ;最大无符号数 word2 SWORD -32768 ;最小有符号数 word3 WORD ? ;未初始化,无符号
也可以使用传统的 DW 伪指令:
val1 DW 65535 ;无符号 val2 DW -32768 ;有符号
16 位字数组通过列举元素或使用 DUP 操作符来创建字数组。下面的数组包含了一组数值:
myList WORD 1,2,3,4,5
下图是一个数组在内存中的示意图,假设 myList 起始位置偏移量为0000。由于每个数值占两个字节,因此其地址递增量为 2。
DUP 操作符提供了一种方便的方法来声明数组:
array WORD 5 DUP (?) ; 5 个数值,未初始化
定义 DWORD 和 SDWORD 数据
DWORD(定义双字)和 SDWORD(定义有符号双字)伪指令为一个或多个 32 位整数分配存储空间:
val1 DWORD 12345678h ;无符号 val2 SDWORD -2147483648 ;有符号 val3 DWORD 20 DUP (?) ;无符号数组
传统的 DD 伪指令也可以用来定义双字数据:
val1 DD 12345678h ;无符号 val2 DD -2147483648 ;有符号
DWORD 还可以用于声明一种变量,这种变量包含的是另一个变量的 32 位偏移量。如下所示,pVal 包含的就是 val3 的偏移量:
pVal DWORD val3
32 位双字数组
现在定义一个双字数组,并显式初始化它的每 一个值:
myList DWORD 1,2,3,4,5
下图给岀了这个数组在内存中的示意图,假设 myList 起始位置偏移量为 0000,偏移量增量为 4。
定义 QWORD 数据
QWORD(定义四字)伪指令为 64 位(8 字节)数值分配存储空间:
quad1 QWORD 1234567812345678h
传统的 DQ 伪指令也可以用来定义四字数据:
quad1 DQ 1234567812345678h
定义压缩 BCD(TBYTE)数据
Intel 把一个压缩的二进制编码的十进制(BCD, Binary Coded Decimal)整数存放在一个 10 字节的包中。每个字节(除了最高字节之外)包含两个十进制数字。在低 9 个存储字节中,每半个字节都存放了一个十进制数字。最高字节中,最高位表示该数的符号位。如果最高字节为 80h,该数就是负数;如果最高字节为 00h,该数就是正数。整数的范围是 -999 999 999 999 999 999 到 +999 999 999 999 999 999。
示例下表列出了正、负十进制数 1234 的十六进制存储字节,排列顺序从最低有效字节到最高有效字节:
十进制数值 | 存储字节 |
---|---|
+1234 | 34 12 00 00 00 00 00 00 00 00 |
-1234 | 34 12 00 00 00 00 00 00 00 80 |
MASM 使用 TBYTE 伪指令来定义压缩 BCD 变量。常数初始值必须是十六进制的,因为,汇编器不会自动将十进制初始值转换为 BCD 码。下面的两个例子展示了十进制 数 -1234 有效和无效的表达方式:
intVal TBYTE 800000000000001234h ;有效 intVal TBYTE -1234 ;无效
第二个例子无效的原因是 MASM 将常数编码为二进制整数,而不是压缩 BCD 整数。
如果想要把一个实数编码为压缩 BCD 码,可以先用 FLD 指令将该实数加载到浮点寄存器堆栈,再用 FBSTP 指令将其转换为压缩 BCD 码,该指令会把数值舍入到最接近的整数:
.data posVal REAL8 1.5 bcdVal TBYTE ? .code fid posVal ;加载到浮点堆栈 fbstp bcdVal ;向上舍入到 2,压缩 BCD 码值
如果 posVal 等于 1.5,结果 BCD 值就是 2。
定义浮点类型
REAL4 定义 4 字节单精度浮点变量。REAL8 定义 8 字节双精度数值,REAL10 定义 10 字节扩展精度数值。每个伪指令都需要一个或多个实常数初始值:
rVal1 REAL4 -1.2 rVal2 REAL8 3.2E-260 rVal3 REAL10 4.6E+4096 ShortArray REAL4 20 DUP(0.0)
下表描述了标准实类型的最少有效数字个数和近似范围:
数据类型 | 有效数字 | 近似范围 |
---|---|---|
短实数 | 6 | 1.18x 10-38 to 3.40 x 1038 |
长实数 | 15 | 2.23 x 10-308 to 1.79 x 10308 |
扩展精度实数 | 19 | 3.37 x 10-4932 to 1.18 x 104932 |
DD、DQ 和 DT 伪指令也可以定义实数:
rVal1 DD -1.2 ;短实数 rVal2 DQ 3.2E-260 ;长实数 rVal3 DT 4.6E+4096 ;扩展精度实数
MASM 汇编器包含了诸如 wal4 和 real8 的数据类型,这些类型表明数值是实数。更准确地说,这些数值是浮点数,其精度和范围都是有限的。从数学的角度来看,实数的精度和大小是无限的。
变量加法程序
到目前为止,本节的示例程序实现了存储在寄存器中的整数加法。现在已经对如何定义数据有了一些了解,那么可以对同样的程序进行修改,使之实现三个整数变量相加,并将和数存放到第四个变量中。
;AddTowSum.asm .386 .model flat,stdcall .stack 4096 ExitProcess PROTO, dwExitCode:DWORD .data firstval DWORD 20002000h secondval DWORD 11111111h thirdval DWORD 22222222h sum DWORD 0 .code main PROC mov eax,firstval add eax,secondval add eax,thirdval mov sum,eax INVOKE ExitProcess,0 main ENDP END main
注意,已经用非零数值对三个变量进行了初始化(9〜11 行)。16〜18 行进行变量相加。x86 指令集不允许将一个变量直接与另一个变量相加,但是允许一个变量与一个寄存器相加。这就是为什么 16〜17 行用 EAX 作累加器的原因:
mov eax,firstval
add eax,secondval
第 17 行之后,EAX 中包含了 firstval 和 secondval 之和。接着,第 18 行把 thirdval 加到 EAX 中的和数上:
add eax,thirdval
最后,在第 19 行,和数被复制到名称为 sum 的变量中:
mov sum,eax
作为练习,鼓励大家在调试会话中运行本程序,并在每条指令执行后检查每个寄存器。最终和数应为十六进制的 53335333。
在调试会话过程中,如果想要变量显示为十六进制,则按下述步骤操作:鼠标在变量或寄存器上悬停 1 秒,直到一个灰色矩形框出现在鼠标下。右键点击该矩形框,在弹出菜单中选择 Hexadecimal Display。
小端顺序
x86 处理器在内存中按小端(little-endian)顺序(低到高)存放和检索数据。最低有效字节存放在分配给该数据的第一个内存地址中,剩余字节存放在随后的连续内存位置中。考虑一个双字 12345678h。如果将其存放在偏移量为 0000 的位置,则 78h 存放在第一个字节,56h 存放在第二个字节,余下的字节存放地址偏移量为 0002 和 0003,如下图所示。
其他有些计算机系统采用的是大端顺序(高到低)。 下图展示了 12345678h 从偏移量 0000 开始的大端顺序存放。
声明未初始化数据
.DATA ? 伪指令声明未初始化数据。当定义大量未初始化数据时,.DATA ? 伪指令减少了编译程序的大小。例如,下述代码是有效声明:
.data smallArray DWORD 10 DUP (0) ;40 个字节 .data? bigArray DWORD 5000 DUP ( ? ) ;20 000 个字节,未初始化
而另一方面,下述代码生成的编译程序将会多岀 20 000 个字节:
.data smallArray DWORD 10 DUP ( 0 ) ; 40 个字节 bigArray DWORD 5000 DUP ( ? ) ; 20 000 个字节
代码与数据混合汇编器允许在程序中进行代码和数据的来回切换。比如,想要声明一个变量,使其只能在程序的局部区域中使用。下述示例在两个代码语句之间插入了一个名为 temp 的变量:
.code mov eax,ebx .data temp DWORD ? .code mov temp,eax
尽管 temp 声明的出现打断了可执行指令流,MASM 还是会把 temp 放在数据段中,并与保持编译的代码段分隔开。然而同时,混用 .code 和 .data 伪指令会使得程序变得难以阅读。
下一篇:等号伪指令
强力推荐阅读文章