8086アセンブリプログラミングでは、データをセグメントレジスタにロードするには、まず汎用レジスタにデータをロードしてから、この汎用レジスタからセグメントレジスタにデータを移動する必要があります。
直接ロードできないのはなぜですか?許可されない特別な理由はありますか?
違いは何ですか mov ax,5000H
およびmov ax,[5000H]
? [5000h]
メモリ位置5000hのコンテンツを意味しますか?
アセンブリ言語(任意のアセンブリ)の構文は、機械語を書くための人間が読める形式にすぎないことを忘れないでください。マシンコードで実行できるルールは、アセンブラ構文が簡単にサポートできるものではなく、プロセッサの電子機器がどのように設計されたかによって異なります。
だから、あなたがmov DS, [5000h]
を書くことができるように見え、概念的にはそれを行うことができないはずの理由がないように見えるからといって、それは本当に「プロセッサは、メモリ位置のコンテンツからセグメントレジスタをロードできますか?」
8086アセンブリの場合、理由は単純に、エンジニアがメモリI/Oデータラインからセグメントレジスタに書き込むラインに信号を送ることができる電気パスを作成しなかったためだと思います。
どうして?私にはいくつかの理論がありますが、信頼できる知識はありません。
最も可能性の高い理由は、単純に設計を単純化することの1つです。それを行うには、追加の配線とゲートが必要です。また、チップ内の不動産に値しないほど珍しい操作(これは70年代)です。これは驚くべきことではありません。 8086はすでに使用されており、通常のレジスタのいずれかをALU(算術論理演算ユニット)に接続して、レジスタをアキュムレータとして使用することができます。安くはなかったと思います。当時のほとんどのプロセッサは、その目的で使用するために1つのレジスタ(theアキュムレータ)のみを許可していました。
セグメントレジスタをメモリ読み取りから書き込むことを許可すると、回路で正しく理解するのが困難ないくつかの奇妙なエッジケースが発生した可能性もあります。結局のところ、書き込まれるセグメントレジスタは、ソースオペランドをアドレス指定するために使用される可能性があります。
括弧に関する限り、あなたは正しいです。メモリ位置5000hに数値4321hが含まれているとしましょう。 mov ax, 5000h
は値5000hをaxに入れ、mov ax, [5000h]
は4321hをメモリからaxにロードします。基本的に、角かっこはCの*
ポインター逆参照演算子のように機能します。
アセンブリがマシンコードで実行できることの理想的な抽象化であるという事実を強調するために、2つのバリエーションは、異なるパラメータを持つ同じ命令ではなく、完全に異なるオペコードであることに注意してください。最初のオペコードにMOV
、2番目のオペコードにMVD
(MoVeダイレクトアドレス指定メモリ)を使用することもできますが、プログラマーにとってブラケット構文の方が覚えやすいと判断したに違いありません。
x86マシンコードには、move-to-Sreg用のオペコードが1つだけあります。そのオペコードは8E /r
mov Sreg, r/m16
、およびはレジスタまたはメモリソースを許可します(ただし即時ではありません)。
他の回答の一部の主張とは異なり、アドレスmov ds, [5000h]
の2バイトが有用なセグメントを保持していると仮定すると、5000h
は正常に実行されます現在のモードの値。(数値として直接使用されるリアルモードと、Sreg値がLDT/GDTにインデックスを付けるセレクターである保護されたモード)。
x86は、命令の即時形式(マシンコードの一部としてエンコードされた定数を使用)とレジスタ/メモリソースバージョンでは常に異なるオペコードを使用します。例えばadd eax, 123
は、add eax, ecx
とは異なるオペコードにアセンブルします。ただし、add eax, [esi]
はadd r, r/m32
と同じオペコードadd eax, ecx
オペコードであり、ModR/Mバイトが異なるだけです。
nasm sreg.asm -l/dev/stdout
からのNASMリスト、16ビットモードでフラットバイナリをアセンブルし、リストを作成します。
バイトをopcode modrm extra
に分割するために手作業で編集しました。これらはすべて1バイトのオペコードです(ModRMバイトの/ rフィールドにスペースを借りる余分なオペコードビットはありません)。したがって、最初のバイトを見て、それが何であるかを確認し、2つの命令が同じオペコードを共有する場合に注意してください。
address machine code source ; comments
1 00000000 BE 0050 mov si, 5000h ; mov si, imm16
2 00000003 A1 0050 mov ax, [5000h] ; special encoding for AX, no modrm
3 00000006 8B 36 0050 mov si, [5000h] ; mov r16, r/m16 disp16
4 0000000A 89 C6 mov si, ax ; mov r/m16, r16
5
6 0000000C 8E 1E 0050 mov ds, [5000h] ; mov Sreg, r/m16
7 00000010 8E D8 mov ds, ax ; mov Sreg, r/m16
8
9 mov ds, 5000h
9 ****************** error: invalid combination of opcode and operands
mov Sreg, imm16
エンコーディングをサポートするには、別のオペコードが必要になります。これは、8086がデコードするために余分なトランジスタを必要とし、将来の拡張のための余地を少なくして、より多くのオペコードコーディングスペースを消費します。これらのどれが8086 ISAの設計者によってより重要であると考えられたかはわかりません。
8086には、絶対アドレスからアキュムレータをロードするときに1バイトを節約する特別なmov AL/AX, moffs
オペコードがあることに注意してください。しかし、Sregのmov
- immediateのオペコードを節約できなかったのでしょうか。この設計上の決定は理にかなっています。どのくらいの頻度でセグメントレジスタをリロードする必要がありますか?非常にまれであり、実際の大規模なプログラムでは、定数がないことがよくあります(私は思います)。しかし、静的データを使用するコードでは、ループ内の固定アドレスにアキュムレータをロードまたは格納している可能性があります。 (8086のコードフェッチは非常に弱いため、ほとんどの場合、コードサイズ=速度)。
また、mov Sreg, r/m16
は、1つの追加の命令(mov ax, 4321h
など)だけで、アセンブル時定数に使用できることにも注意してください。しかし、mov Sreg, imm16
しかない場合、ランタイム変数セグメント値には自己変更コードが必要でした。 (したがって、明らかにr/m16
ソースバージョンを除外することはありません。)私のポイントは、1つしかない場合は、間違いなくレジスタ/メモリソースバージョンになるということです。
セグメントレジスタは、(ハードウェアレベルで)汎用レジスタと同じではありません。もちろん、Mike Wがコメントで述べたように、即時値をセグメントレジスタに直接移動できない正確な理由は、Intel開発者だけが知っています。でも、こういうのはデザインがシンプルだからだと思います。セグメントレジスタ操作は非常にまれであるため、この選択はプロセッサのパフォーマンスに影響を与えないことに注意してください。したがって、1つ多い命令、1つ少ない命令は、まったく重要ではありません。
X86アセンブラ構文のすべての妥当な実装では、mov reg, something
は、即値something
をレジスタreg
に移動します。例えば:
NamedConst = 1234h
SomeLabel:
mov edx, 1234h ; moves the number 1234h to the register edx
mov eax, SomeLabel ; moves the value (address) of SomeLabel to eax
mov ecx, NamedConst ; moves the value (1234h in this case) to ecx
角かっこで囲まれた数値は、このアドレスを持つメモリの内容がレジスタに移動されることを意味します。
SomeLabel dd 1234h, 5678h, 9abch
mov eax, [SomeLabel+4] ; moves 5678h to eax
mov ebx, dword [100h] ; moves double Word memory content from the
; address 100h in the data segment (DS) to ebx.
その理由を昔に読んだことを思い出します。目の前にその書類はありませんので、手を振ってご容赦ください。
メモリの場所または定数からセグメントレジスタを読み込むには、メモリサイクルが必要です。メモリの配置が混乱している場合、16ビット値の読み取りには2メモリサイクルかかる可能性があります。サイクル間では、セグメントレジスタの値が無効です。ここで、スタックセグメントレジスタをいじっていて、割り込みが発生したと想像してください。これが手押し車です。ライドをお楽しみください!