目次
0.最初のアセンブラ
アセンブラは、ほとんどすべての情報を言語で提供します。プログラマーは、CPUのレジスターやフラグも含めて、起こっていることすべてを見ることができます。ただし、この機能を備えている間、プログラマーはデータ表現の詳細と命令の形式に責任を負わなければなりません。プログラマーは、多くの詳細情報があるレベルで作業します。次に、その作業プロセスを理解するための例として、単純なアセンブリ言語プログラムを取り上げます。
次のプログラムは、2つの数値の加算を実行し、結果をレジスタに保存するコードです:(実行されていません)
.data ;此为数据区
sum DWORD 0 ;定义名为sum的变量
.code ;此为代码区
main PROC
mov eax,5 ;将数字5送入而eax寄存器
add eax,6 ;eax寄存器加6
mox sum,eax
INVOKE ExitProcess,0 ;结束程序
main ENDP
プログラマーが精通している可能性のあるタイプは、int、double、floatなど、それらよりも具体的ではありません。これらのキーワードはサイズを制限するだけで、変数に格納されている内容をチェックしません。プログラマーが完全に制御できることを忘れないでください。.codeおよび.dataディレクティブでマークされたコードおよびデータ領域は、セグメントと呼ばれます。つまり、プログラムにはコードセグメントとデータセグメントがあります。
1.言語定数
ここでは、Microsoftの構文表記が使用されています。角括弧内の要素はオプションです。中括弧内の要素は|記号で区切られ、要素の1つを選択する必要があります。斜体は明確に定義または説明されている要素を示します。
(1)整数定数
整数リテラル(整数定数とも呼ばれます)は、オプションのプレフィックス、1つ以上の数字、および基数を示すオプションの基数文字で構成されます。
[{+ |-}]桁[基数]
たとえば、26は有効な整数定数です。ベースがないため、10進数であると想定されます。16進数の26を表す場合は、26hと記述します。同様に、数値1101は、末尾に「b」を追加して1101b(2進数)にしない限り、10進値と見なすことができます。次の表に、可能な基本値を示します。
h | 16進数 | r | エンコードされた実数 |
q / o | オクタル | t | 10進数(スペア) |
d | 10進数 | Y | バイナリ(スペア) |
b | バイナリ |
文字で始まる16進数は、アセンブラが識別子として解釈しないように、接頭辞としてゼロを付ける必要があります。整数定数式(定数整数式)は、整数定数と算術演算子を含む一種の算術式です。各式の計算結果は整数である必要があり、32ビット(0からFFFFFFFFh)で格納できます。次の表に、算術演算子をリストし、それらの優先順位を高(1)から低(4)の順に示します。整数定数式は、アセンブリ時にのみ評価されることを理解することが重要です。ここでは、これらを単に整数式と呼びます。
オペレーター | 名前 | 優先度 |
---|---|---|
() | 括弧 | 1 |
+、- | 1ドルプラスマイナス | 2 |
*、/ | 掛け算と割り算 | 3 |
モッド | モジュロ | 3 |
+、- | 足し算、引き算 | 4 |
(2)実定数
実数リテラル(浮動小数点リテラルとも呼ばれます)は、10進数の実数とエンコードされた(16進数の)実数を表すために使用されます。実際の10進数には、オプションの符号、それに続く整数、小数点、小数部分を表すオプションの整数、およびオプションの指数が含まれます。
実数リテラル(浮動小数点リテラルとも呼ばれます)は、10進数の実数とエンコードされた(16進数の)実数を表すために使用されます。10進実数には、オプションの符号、それに続く整数、小数点、小数部分を表すオプションの整数、およびオプションの指数が含まれます。
[sign] integer。[integer] [exponent]
記号と指数の形式は次のとおりです。
記号{+、-}
指数E [{+、-}]整数
(3)文字定数
文字リテラルとは、一重引用符または二重引用符で囲まれた文字を指します。アセンブラは、文字のバイナリASCIIコード値をメモリに保存します。例えば:
「A」
「d」
文字列リテラルは、一重引用符または二重引用符で囲まれた一連の文字(スペースを含む)です。次に例を示します。
「ABC」
'バツ'
「おやすみなさい、グレイシー」
ネストされた引用符も使用できます。文字定数が整数形式で格納されるのと同様に、文字列定数は整数バイト値のシーケンスとしてメモリに格納されます。
2.予約語
予約語には特別な意味があり、正しい文脈でのみ使用できます。デフォルトでは、予約語では大文字と小文字は区別されません。たとえば、MOVはmovおよびMovと同じです。予約語にはさまざまな種類があります。
- MOV、ADD、MULなどの命令ニーモニック。
- 登録名。
- 疑似命令は、プログラムをアセンブルする方法をアセンブラに指示します。
- 属性は、変数とオペランドのサイズと使用法に関する情報を提供します。たとえば、BYTEとWORD。
- 定数式で使用される演算子。
- アセンブリ中に定数整数値を返す@dataなどの事前定義されたシンボル。
次の表は、一般的に使用される予約語のリストです。
$ | パリティ? | DWORD | STDCALL |
? | パスカル | 遠い | 剣 |
@B | QWORD | FAR16 | SYSCALL |
@F | REAL4 | FORTRAN | TBYTE |
ADDR | REAL8 | Fワード | VARARG |
ベーシック | REAL10 | 近く | 語 |
バイト | SBYTE | NEAR16 | ゼロ? |
C | SDORD | オーバーフロー? | |
運ぶ? | 符号? |
3.識別子
識別子(識別子)はプログラマーが選択した名前であり、変数、定数、サブルーチン、コードラベルを識別するために使用されます。
識別子の形成にはいくつかの規則があります。
- 1〜247文字を含めることができます。
- 大文字と小文字を区別しません。
- 最初の文字は文字(A --- Z、a --- z)、アンダースコア(_)、@、?、または$である必要があります。次の文字も数字にすることができます。
- 識別子は、アセンブラの予約語と同じにすることはできません。
一般に、記号@とアンダースコアはアセンブラーと高級言語コンパイラーの両方で使用されるため、最初の文字として使用することは避けてください。
4.疑似命令
ディレクティブは、ソースコードに埋め込まれ、アセンブラによって認識および実行されるコマンドです。ディレクティブは実行時に実行されませんが、変数、マクロ、およびサブルーチンを定義したり、メモリセグメントに名前を割り当てたり、アセンブラに関連する他の多くの日常的なタスクを実行したりできます。デフォルトでは、ディレクティブは大文字と小文字を区別しません。たとえば、.data、.DATA、および.Dataは同じです。
アセンブラディレクティブの重要な機能は、セグメントとも呼ばれるプログラムセクションを定義することです。プログラムのセグメントにはさまざまな機能があります。セクションは変数を定義するために使用でき、.DATA疑似命令によって識別されます。.CODE疑似命令によって識別されるプログラムセクションには実行可能命令が含まれます。.STACK疑似命令によって識別されるプログラムセクションは、ランタイムスタックとセットを定義します。そのサイズ、例:.stack 100h;
5.手順
命令は、プログラムがアセンブルおよびコンパイルされたときに実行可能になるステートメントです。アセンブラは、命令を機械語バイトに変換します。これらのバイトは、実行時にCPUによってロードおよび実行されます。命令には4つのコンポーネントがあります。
- ラベル(オプション)
- コマンドニーモニック(必須)
- オペランド(通常は必須)
- コメント(オプション)
さまざまなパーツの位置は次のように配置されています。
[ラベル:]ニーモニック[オペランド] [;コメント]
(1)ラベル
ラベルは識別子であり、命令とデータの位置マークです。ラベルは命令のフロントエンドにあり、命令のアドレスを示します。同様に、ラベルも変数の先頭にあり、変数のアドレスを示します。ラベルには、データラベルとコードラベルの2種類があります。データラベルは変数の場所を識別し、コード内の変数を参照するための便利な手段を提供します。アセンブラは、各ラベルに数値アドレスを割り当てます。ラベルの後に複数のデータ項目を定義できます。プログラムコード領域(命令が配置されているセクション)のラベルは、コロン(:)で終わる必要があります。コードラベルは、ジャンプおよびループ命令のターゲットとして使用されます。コードラベルは、命令と同じ行に配置することも、独自の行に配置することもできます。
(2)命令ニーモニック
命令ニーモニックは、命令をマークする短い単語です。英語では、ニーモニックは暗記を助ける方法です。同様に、mov、add、subなどのアセンブリ言語命令ニーモニックは、命令が実行する操作のタイプの手がかりを与えます。命令ニーモニックの例を次に示します。
ニモニック | 説明 | ニモニック | 説明 |
---|---|---|---|
MOV | 値を転送(配布)する | 私が持っています | 2つの数を掛ける |
追加 | 2つの値を追加します | JMP | 新しい場所にジャンプします |
サブ | ある値を別の値から引く | コール | サブルーチンを呼び出す |
(3)オペランド
オペランドは、命令の入力と出力の値です。アセンブリ言語命令のオペランドの数は0〜3の範囲です。各オペランドは、レジスタ、メモリオペランド、整数式、および入力ポートにすることができます。オペランドには固有の順序があります。命令に複数のオペランドがある場合、通常、最初のオペランドはデスティネーションオペランドと呼ばれ、2番目のオペランドはソースオペランドと呼ばれます。一般に、デスティネーションオペランドの内容は命令によって変更されます。
(4)注意事項
注釈は、プログラマーと読者がプログラミング情報を伝達するための重要な方法です。プログラムリストの最初には、通常、次の情報が含まれています。
- プログラムの目的の説明
- プログラムの作成者または修飾子のリスト
- プログラムの作成および変更の日付
- プログラム実装技術の説明
コメントを指定する方法は2つあります。
- 1行のコメントは、セミコロン(;)で始まります。アセンブラは、同じ行のセミコロンの後のすべての文字を無視します。
- ブロックコメントは、COMMENTディレクティブとユーザー定義シンボルで始まります。アセンブラは、同じユーザー定義シンボルが表示されるまで、後続のすべてのテキスト行を無視します。
(5)NOP(操作なし)命令
最も安全な(そして最も役に立たない)命令はNOP(操作なし)です。プログラム空間で1バイトを占有しますが、何もしません。コンパイラやアセンブラがコードを有効なアドレス境界に揃えるために使用することがあります。
6.アセンブラーとアセンブリプロセス
アセンブリ言語で記述されたソースプログラムは、ターゲットコンピュータで直接実行することはできず、翻訳またはアセンブリによって実行可能コードに変換する必要があります。実際、アセンブラはコンパイラと非常によく似ています。コンパイラは、C ++ または Java プログラムを実行可能コードに変換するために使用されるプログラムの一種 です。アセンブラは、オブジェクトファイルと呼ばれる機械語を含むファイルを生成します。このファイルは実行の準備ができていません。実行可能ファイルを生成するには、リンカーと呼ばれるプログラムに渡す必要があります。このファイルは、オペレーティングシステムのコマンドプロンプトで実行する準備ができています。
- ステップ1:プログラマーは、テキストエディターを使用して、ソースファイルと呼ばれるASCIIテキストファイルを作成します。
- ステップ2:アセンブラーはソースファイルを読み取り、プログラムの機械語翻訳であるターゲットファイルを生成します。または、リストファイルも生成します。エラーがある限り、プログラマーはステップ1に戻ってプログラムを変更する必要があります。
- ステップ3:リンカはオブジェクトファイルを読み取ってチェックし、プログラムにリンクライブラリ内のプロシージャへの呼び出しが含まれているかどうかを確認します。リンカは、要求されたプロシージャをリンクライブラリからコピーし、それらをターゲットファイルと組み合わせて、実行可能ファイルを生成します。
- ステップ4:オペレーティングシステムローダーが実行可能ファイルをメモリに読み込み、CPUをプログラムの開始アドレスに分岐させてから、プログラムの実行を開始します。
リストファイルには、プログラムソースファイルのコピーに加えて、行番号、各命令の数値アドレス、各命令のマシンコードバイト(16進数)、およびシンボルテーブルが含まれています。シンボルテーブルには、プログラム内のすべての識別子の名前、セグメント、および関連情報が含まれています。
7.詳細なデータ定義
アセンブラは、データサイズ(バイト、ワード、ダブルワードなど)に応じて、基本的な内部データ型(組み込みデータ型)のセットを認識します。これは、符号付き、整数、または実数のいずれであるかによって、その型を記述します。シーケンサーはSDWORDを使用して、この値が署名されていることをリーダーに通知しますが、アセンブラーには必須ではありません。アセンブラは、オペランドのサイズのみを評価します。したがって、たとえば、プログラマーは32ビット整数のみをDWORD、SDWORD、またはREAL4タイプとして指定できます。
次の表に、すべての内部データ型のリストを示します。一部のエントリのIEEE記号は、IEEE ComputerSocietyによって公開されている標準の実数形式を参照しています。
の種類 | 使用法 |
---|---|
バイト | 8ビットの符号なし整数、Bはバイトを表します |
SBYTE | 8ビットの符号付き整数、Sは符号付きを表します |
語 | 16ビットの符号なし整数 |
剣 | 16ビット符号付き整数 |
DWORD | 32ビットの符号なし整数、Dはdouble(ワード)を表します |
SDWORD | 32ビット符号付き整数、SDは符号付きdouble(ワード)を表します |
Fワード | 48ビット整数(プロテクトモードのファーポインタ) |
QWORD | 64ビット整数、Qは4(ワード)を表します |
TBYTE | 80ビット(10バイト)整数、Tは10バイトを表します |
REAL4 | 32ビット(4バイト)IEEEの短い実数 |
REAL8 | 64ビット(8バイト)IEEEの長い実数 |
REAL10 | 80 位(10 字节)IEEE 扩展实数 |
数据定义语句(data definition statement)在内存中为变量留岀存储空间,并赋予一个可选的名字。数据定义语句根据内部数据类型(上表)定义变量。数据定义语法如下所示:
[name] directive initializer [,initializer]...
- 名字:分配给变量的可选名字必须遵守标识符规范。
- 伪指令:数据定义语句中的伪指令可以是 BYTE、WORD、DWORD、SBTYE、SWORD 或其他在上表中列出的类型。此外,它还可以是传统数据定义伪指令,如下表所示。
伪指令 | 用法 | 伪指令 | 用法 |
---|---|---|---|
DB | 8位整数 | DQ | 64 位整数或实数 |
DW | 16 位整数 | DT | 定义 80 位(10 字节)整数 |
DD | 32 位整数或实数 |
MODEL 伪指令,它告诉汇编程序用的是哪一种存储模式。32 位程序总是使用平面(flat)存储模式,它与处理器的保护模式相关联。关键字 stdcall 在调用程序时告诉汇编器,怎样管理运行时堆栈。然后是 .STACK 伪指令,它告诉汇编器应该为程序运行时堆栈保留多少内存字节。ENDP 伪指令标记一个过程的结束。
(1)定义 BYTE 和 SBYTE 数据
BYTE(定义字节)和 SBYTE(定义有符号字节)为一个或多个无符号或有符号数值分配存储空间。每个初始值在存储时,都必须是 8 位的。问号(?)初始值使得变量未初始化,这意味着在运行时分配数值到该变量。如果同一个数据定义中使用了多个初始值,那么它的标号只指出第一个初始值的偏移量。并不是所有的数据定义都要用标号。比如,在 list 后面继续添加字节数组,就可以在下一行定义它们:在单个数据定义中,其初始值可以使用不同的基数。字符和字符串常量也可以自由组合。定义一个字符串,要用单引号或双引号将其括起来。最常见的字符串类型是用一个空字节(值为0)作为结束标记,称为以空字节结束的字符串,很多编程语言中都使用这种类型的字符串:每个字符占一个字节的存储空间。对于字节数值必须用逗号分隔的规则而言,字符串是一个例外。DUP 操作符使用一个整数表达式作为计数器,为多个数据项分配存储空间。在为字符串或数组分配存储空间时,这个操作符非常有用,它可以使用初始化或非初始化数据。
(2)定义 WORD 和 SWORD 数据
WORD(定义字)和 SWORD(定义有符号字)伪指令为一个或多个 16 位整数分配存储空间。也可以使用传统的 DW 伪指令。
(3)定义 DWORD 和 SDWORD 数据
DWORD(定义双字)和 SDWORD(定义有符号双字)伪指令为一个或多个 32 位整数分配存储空间,传统的 DD 伪指令也可以用来定义双字数据。DWORD 还可以用于声明一种变量,这种变量包含的是另一个变量的 32 位偏移量。
(4)定义 QWORD 数据
QWORD(定义四字)伪指令为 64 位(8 字节)数值分配存储空间,传统的 DQ 伪指令也可以用来定义四字数据。
(5)定义压缩 BCD(TBYTE)数据
Intel 把一个压缩的二进制编码的十进制(BCD, Binary Coded Decimal)整数存放在一个 10 字节的包中。每个字节(除了最高字节之外)包含两个十进制数字。在低 9 个存储字节中,每半个字节都存放了一个十进制数字。最高字节中,最高位表示该数的符号位。如果最高字节为 80h,该数就是负数;如果最高字节为 00h,该数就是正数。整数的范围是 -999 999 999 999 999 999 到 +999 999 999 999 999 999。MASM 使用 TBYTE 伪指令来定义压缩 BCD 变量。常数初始值必须是十六进制的,因为,汇编器不会自动将十进制初始值转换为 BCD 码。如果想要把一个实数编码为压缩 BCD 码,可以先用 FLD 指令将该实数加载到浮点寄存器堆栈,再用 FBSTP 指令将其转换为压缩 BCD 码,该指令会把数值舍入到最接近的整数。
(6)定义浮点类型
REAL4 定义 4 字节单精度浮点变量。REAL8 定义 8 字节双精度数值,REAL10 定义 10 字节扩展精度数值。每个伪指令都需要一个或多个实常数初始值。下表描述了标准实类型的最少有效数字个数和近似范围:
数据类型 | 有效数字 | 近似范围 |
---|---|---|
短实数 | 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 伪指令也可以定义实数。
(7)等号(=)伪指令
等号伪指令(equal-sign directive)把一个符号名称与一个整数表达式连接起来。通常,表达式是一个 32 位的整数值。当程序进行汇编时,在汇编器预处理阶段,所有出现的 name 都会被替换为 expression。假设下面的语句出现在一个源代码文件开始的位置。
最も重要な記号の1つは、現在位置カウンターと呼ばれ、$で示されます。「=」で定義されたシンボルは、同じプログラムで再定義できます。
8.配列と文字列の長さ
配列のサイズを明示的に宣言すると、特に配列要素が後で挿入または削除された場合に、プログラミングエラーが発生する可能性があります。配列のサイズを宣言するためのより良い方法は、アセンブラにこの値を計算させることです。$演算子(現在のアドレスカウンタ)は、現在のプログラムステートメントのオフセットを返します。配列でカウントされる要素の数にバイトが含まれていない場合、配列の合計サイズ(バイト単位)を単一の要素のサイズで割る必要があります。
9.一般的な疑似命令
(1)EQU疑似命令
EQUディレクティブは、シンボル名を整数式または任意のテキストに接続します。3つの形式があります。
名前EQU式
名EQUシンボル
名EQU <テキスト>
最初の形式では、式は有効な整数式である必要があります。2番目の形式では、symbolは既存のシンボル名であり、=またはEQUで定義されています。3番目の形式では、任意のテキストを<...>で表示できます。アセンブラは、プログラムの最後で名前を検出すると、シンボルを整数値またはテキストに置き換えます。EQUは、整数以外の値を定義するときに非常に役立ちます。
(2)TEXTEQU疑似命令
TEXTEQUディレクティブは、EQUと同様に、テキストマクロを作成します。これには3つの形式があります。1つ目は名前にテキストを割り当て、2つ目は既存のテキストマクロのコンテンツを割り当て、3つ目は整数定数式を割り当てます。
name TEXTEQU <text>
name TEXTEQU textmacro
name TEXTEQU%constExpr