これらは私が理解していない2つの質問です:
One-Pass Assembler は将来のシンボルの問題をどのように解決しますか?
この点で、2 パス アセンブラは 1 パス アセンブラとどう違うのですか?
最初のパスまたは 2 番目のパスで解決しますか? 2 番目のパスで実行する場合、1 パス アセンブラと実際にどこが違うのでしょうか。2 番目のパスで実行される場合、最初のパスで実行されないのはなぜですか?
これらは私が理解していない2つの質問です:
One-Pass Assembler は将来のシンボルの問題をどのように解決しますか?
この点で、2 パス アセンブラは 1 パス アセンブラとどう違うのですか?
最初のパスまたは 2 番目のパスで解決しますか? 2 番目のパスで実行する場合、1 パス アセンブラと実際にどこが違うのでしょうか。2 番目のパスで実行される場合、最初のパスで実行されないのはなぜですか?
このPDFを読んでください。シングルパスとマルチパスのアセンブラがどのように機能するかについて、順を追って説明します。また、両者の長所と短所、および両者の違いについても説明します。
これはロード アンド ゴータイプのアセンブラの一種で、通常はすぐに実行できるようにオブジェクト コードをメモリ内に直接生成します。ソースコードを一度だけ解析すれば完了です。ブルーム...
前方参照!つまり、ワンパス アセンブラがソース コードをたどっている間に、未定義のデータ シンボルと未定義のラベル (ジャンプ アドレス) の形で未知のものに遭遇します。あなたのアセンブラーは、これらの見知らぬ人に、彼らが誰であるかを尋ねますか? 見知らぬ人は「後で教えてあげるよ!」と言います (前方参照) あなたのアセンブラーは怒って、これらの見知らぬ人を完全に排除するようにあなたに言います。しかし、これらの見知らぬ人はあなたの友達であり、完全に排除することはできません。そこで、アセンブラーと妥協の契約を結びます。変数を使用する前に、すべての変数を定義することを約束します。アセンブラは、未定義のデータ シンボルのサイズがわからないため、未定義のデータ シンボル用の一時ストレージを予約することさえできないため、これについて妥協することはできませんでした。データはさまざまなサイズにすることができます
そのようなものなら
PAVAN EQU SOMETHING
; Your code here
mov register, PAVAN
; SOMETHING DB(or DW or DD) 80 ; varying size data, not known before
アセンブラは、未定義のジャンプ ラベルについて妥協することに同意します。ジャンプ ラベルはアドレスに過ぎず、アドレス サイズは事前に知ることができるため、アセンブラは未定義シンボル用に一定のスペースを予約できます。
このような場合
jump AHEAD
AHEAD add reg,#imm
アセンブラは として変換jump AHEAD
され0x45 **0x00 0x00**
ます。0x45
のオペコードで、アドレスjump
用に予約された 4 バイトAHEAD
途中で、アセンブラが未定義のラベルに遭遇した場合、シンボルが未定義のシンボルの値を配置する必要があるアドレスとともに、それをシンボルテーブルに配置します。すべての未定義のラベルに対して同じことを行い、これらの未定義のシンボルの定義を確認すると、それらの値をテーブル (それによってそのラベルを定義済みにする) と以前に一時ストレージを予約したメモリ位置の両方に追加します。
解析の最後に、まだ未定義の状態にある貧しい魂がさらにある場合、アセンブラはファウルを叫び、エラーを出します:( 未定義のラベルがない場合は、そのままにしてください!
説明したように、ワンパス アセンブラはデータ シンボルの前方参照を解決できません。使用する前に、すべてのデータ シンボルを定義する必要があります。2 パス アセンブラは、1 つのパスを専用にしてすべての (データ/ラベル) 前方参照を排他的に解決し、次のパスで手間をかけずにオブジェクト コードを生成することで、このジレンマを解決します。
データ シンボルが別のデータ シンボルに依存し、この別のデータ シンボルがさらに別のデータ シンボルに依存する場合、アセンブラはこれを再帰的に解決しました。それをこの記事で説明しようとすると、記事が大きくなりすぎます。詳細については、このpptをお読みください
はい。再定義などを検出できます。
PS : ここで 100% 正しいとは限りません。より良い投稿にするための提案をお待ちしております。
ワンパスアセンブラは、コードを生成し、未定義のシンボルについては、埋められるスロットを残し、テーブルまたは他のデータ構造にそれを記憶します。次に、シンボルが定義されている場所で、テーブルの情報を使用して、適切な場所にその値を入力します。
従来、2パスアセンブラを使用する理由は、ターゲットプログラムがメモリに収まらず、ソースはそのままにしておくためです。巨大なソースプログラムがパンチテープリーダーから1行ずつ読み取られ、ラベルのテーブルが内部メモリに保持されます。(私は実際に、8080を搭載したIntelの最初の開発システムであるISISでそれを行いました。)2回目はソーステープを最初から再度読み取りますが、すべてのラベルの値は、各行として既知です。が読み取られると、ターゲットプログラムがテープにパンチアウトされます。メモリが不足している16ビットIntel8086システムでは、これは、紙テープの代わりにハードディスクまたはフロッピーを使用して、64Kバイトをはるかに超える可能性のある詳細に文書化されたソースファイルを作成するための便利な手法でした。
現在、2つのパスを実行する必要はありませんが、このアーキテクチャはまだ使用されています。I / Oを犠牲にして、少し単純です。
アセンブラについて考える 1 つの方法は、アセンブラが、順次増加するメモリ位置に割り当てられた一連の式の値を計算することを想像することです。式は慣習的に、シンボルの値、シンボルに対して行われる算術演算、定数、および「現在位置カウンター」などの特殊変数 (「$」などの変な名前で書かれることが多い)、または真に特殊な式で構成される場合があります。構文は機械語命令の構文です。
式は、いくつかの連続したメモリ位置を満たす値を生成する場合があることに注意してください。マシン命令はこれを行う傾向がありますが、文字列リテラル、多倍精度数、初期化された構造体などの式があると便利です。これは簿記の詳細にのみ影響しますが、アセンブラーが抽象的に行うことは変わりません。
各式の最終的な値を計算するために、アセンブラーは関連するシンボルの値を認識している必要があります。シンボル値を検出する方法はわずかです。まず、シンボル値が式の結果として定義される場合があります。第 2 に、シンボル値に現在のロケーション カウンターの値が割り当てられる場合があります。通常、アセンブラは、シンボルが「ラベル」位置に書かれているときにこれを行います。このような検出時に、アセンブラはシンボル名とその値をシンボル テーブルに記録し、式の評価に使用します。
アセンブラーが直面する重要な問題は、まだシンボルの定義をすべて確認していないにもかかわらず、式の値を生成することです。シンボルが特定の行で定義されていない場合、アセンブラが最終的に処理する後の行で定義されると想定されています。
2 パス アセンブラは、"最初の" パスと "2 番目の" パスと呼ばれる 2 つのパスで、式に遭遇したときに各式の値を計算しようとします。最初のパスで、式に未定義のシンボル (前方参照と見なされる) がある場合、アセンブラは単純にダミー値 (多くの場合ゼロ) を代入します。いずれの場合も、式の値を計算します。マシン命令またはデータ定数が処理されている場合、結果は無視されますが、サイズは位置カウンターを進めて、ラベル値の割り当てを有効にするために使用されます。ラベルが検出された場合、その値は現在のロケーション カウンターに設定されます。シンボル割り当て "A EQU " が検出された場合、シンボル値は式の結果に設定されます。式に未定義のシンボルが含まれている場合、アセンブラーはエラーを出します。Origin ステートメントが見つかった場合、"$ EQU " を記述したものとして扱われます。最初のパスの最後に、すべてのラベルに値が割り当てられています。値を持たないシンボルは、シンボル テーブルで「未定義」としてマークされます。2 番目のパスでは、最初の式の評価が繰り返されますが、シンボルは (再) 定義されません。すべてのシンボルが (定義されることが期待される) 定義されているため、式の値は正しく、出力ストリームに出力されます。
ワンパス アセンブラは、式に遭遇すると、各式の値を計算しようとします。式に定義済みのシンボルのみが含まれている場合、アセンブラーはそれを評価して最終的な値を生成し、その情報を出力ストリームに書き込むことができます。(ここでの別の回答は、一部のワンパスアセンブラーが回答をメモリに書き込むことを示唆しています。それは単なる特殊なケースです)。式に未定義のシンボルが含まれている場合、アセンブラは、後でシンボルが定義されるか、アセンブリの最後に再処理されるように、ペア(場所、式)を格納します。ロケーション カウンターを設定する式など、一部の式では未定義のシンボルを使用できません。その場合、アセンブラは文句を言います。
したがって、トリッキーな部分は、未解決の式を保存し、いつ再評価するかを決定することです。式を保存する 1 つの方法は、単純にテキストを保持することです。もう 1 つは、式の (逆) ポーランド語表記に相当するものを作成することです。式をいつ再評価する必要があるかを判断するために、式に含まれる未定義のシンボルと関連付けることができます。次に、シンボルが定義されると、対応する未解決の式が再評価され、完了した式が発行され、未解決の式が再処理のために再び残されます。あるいは、アセンブラは、入力の終わりに到達するまですべての式を単純に保存することもできます。この時点で、すべてのシンボルを定義する必要があるため、各式の最終値を決定できるはずです。
前世紀に、私は 8k バイトのコンピューターで動作するワンパス アセンブラーを構築しました。これは、式のポーランド語表現を使用していました。シンボルが定義されると、ポーランド式が評価され、計算可能な部分式が計算され、結果のポーランド語が最終値または未定義シンボルの演算子のみを含むより小さなポーランド式に単純化されました。未定義の値のシンボル テーブル エントリには、未定義のシンボルに対応するすべてのポーランド式スロットのリンク リストがありました。シンボル定義が検出されると、リンクされたリストのすべての要素が更新され、ポーランド式が再評価されました。これにより、ポーランド式のサイズが可能な限り小さく保たれ、すべてのシンボルが定義された瞬間に削除されます。このアセンブラは、小さなマシンで数十万行のプログラムを問題なく処理しました。このような小さなマシンでワンパス アセンブラを実行する理由は、ソース コードが紙テープ (覚えている人はテレタイプ) から来ており、その紙テープを 1 回でも読むのは非常に苦痛で時間がかかるためです。2 回目は良い考えではなかったので、2 パス アセンブラは適切な選択ではありませんでした。
私の同僚の 1 人が、かなり後に興味深い 2 パス アセンブラを作成しました。テキストを 2 回処理するのではなく、最初のパスでテキストをトークン化し (メモリに格納)、シンボル値を収集しました。トークン化されたテキストを処理した 2 つを渡します。これは、2 パスの非常に高速なアセンブラーでした。彼にはもっと多くのメモリがありました。
将来のシンボルの問題は、シンボルが定義される前に使用されることを意味します。
アセンブリ言語プログラムでは、プログラマーはいつでもシンボルを定義できるため、シンボルが定義される前に使用されている場合、「前方参照」と呼ばれる可能性があります
そのため、機械語 (バイナリ) に変換した時点で、アセンブラは Opcode-Reg-X2-B2 を取得しますが、シンボル テーブルの D2(変位) エントリを取得していないため、機械語 (バイナリ) に変換することはできません。 ) そしてそれは「前方参照問題」と呼ばれます。
これを解決するには 2 Pass Assembler が定義されています。
**最初のパス (最初のスキャン) で、シンボル (ラベル) のエントリをシンボル テーブルに作成します。**そして、2回目のパス(2回目のスキャン)でアセンブラが機械語(バイナリ)に変換します。とてもシンプルです:)