ぐだぐだ低レベルプログラミング(179)x86、16/32/64bit、整数レジスタの発展?

Joseph Halfmoon

長らく練習してきたArmを終え(まだ他所でやるケド)、今回からx86に入ります。Armの命令多過ぎなどと文句を垂れてきましたが、x86の命令「もっと」多過ぎ、死ぬまでに舐め終わらない気がします。そのうえ屋上屋を重ねる拡張により「古い16ビット命令」は「64ビット機では練習しずらく」なってます。どうするの?

※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら

※実機動作確認には以下を使用しております。

    •  Raspberry Pi 4 model B、Cortex-A72コア(ARMv8-A)
    •  Raspberry Pi OS (64bit) bullseye
    •  QEMU 5.2.0
    •  FreeDOS 1.3
x86の命令の練習をArmの上でやるのよ

パソコンのCPUとして広く使われているx86(インテルおよびAMDなど製)は今を去ること約50年前のインテル8086を源流とするシリーズです。当初は16ビットCPUとして開発されるも、インテル80386にて32ビットCPUに昇格、その後、AMD社が64ビット化を行い、御本家インテルもAMDに合わせることになって目出度く今日の64ビットのx86_64に繋がっております。

ただし、ここに深い谷があります。現在主流の64ビットOS上では、64ビットのプログラムだけでなく、32ビットのプログラムを走らせることは問題ありません。勿論オペランドとしては8ビットだろうが16ビットだろうが扱えます。しかし、64ビットOS上で16ビットの古い命令セット(8086などの)を走らせようとすると困り果てます。x86のハードウエアの奥底には脈々と受け継がれているのですが、こと64ビット化した実行状態から呼び出すのがとっても辛いです。

一方、x86のアセンブリ言語命令セットをおさらいして行こうとすると、やはり

    1. 16ビット
    2. 32ビット
    3. 64ビット

という順番でやっていきたいです。そこで

    • 32ビット、64ビットのx86命令セットはx86のパソコン上で直接練習
    • 16ビットのx86命令セットはCPUエミュレータ上で練習

ということにいたしました。16ビットの命令セットは、ソフトウエア上で疑似的に実行してみる、ということなので実際に走らせるマシンはx86だろうが何だろうがかまいませぬ。そこで、前回までArmの命令セットの練習に使っていたラズパイ4機に定番のエミュレータ QEMUを載せて使うことにいたしました。そして開発環境として 8086用のOSの代名詞、MS-DOS「上位互換」のFreeDOSを使うことといたしました。なお、QEMU上でFreeDOSを走らせるところ、およびFreeDOS環境でのx86開発ツール類の整備は以下回以降の別シリーズでやってます。

ソフトな忘却力(45) FreeDOS、その伝統はMS-DOS超え、進化も続くOS

x86の発展?を整数レジスタに見る

x86CPUは16ビットから始まって64ビット化したと書きましたが、実を言えばその前があるのです。8ビットCPU、8080です。インテル8080は、今となっては「インベーダーゲーム」などに使われたことで有名なCPUです。初期のマイクロプロセッサ業界においては最大のヒット商品でした。しかしその後、命令コンパチのZilog Z80が登場したりして流れが「ソッチ」に行きかけてました。そこでインテルとしては16ビットの8086に8ビットの8080の顧客層の「受け皿」になる性格を持たせたのでした。その結果がx86の整数レジスタセットには色濃く反映されてます。x86IntRegisters

一番左が、現在の64ビット化したx86_64の整数命令セットです。レジスタ左端がMSB、右がLSBの64ビット幅です。その中でも白抜き部分はAMD社が「拡張」した部分。一方、色が付けてあるのがインテルが決めた部分です。そのうち赤色の部分は80386以降の32ビット化での拡張で継ぎ足されたところ。当初の8086では、青色部分と緑色部分のみでした。

真ん中のところに、見やすく青色と緑色の整数レジスタ8本を取りだしてあります。8086(8088)および80186、80286までは整数レジスタはこの部分だけです。そこでちょっと妙なのは青く色をつけた部分です。青色のレジスタは、16ビットのレジスタを上の8ビットと下の8ビットに分けて別々にアクセスすることもできるようになっていました。一方、緑色のレジスタ4本は16ビット幅のレジスタをそのまま16ビット幅で扱うようになってました(この時点では8ビット幅での取り扱いはできませぬ。)

青色のレジスタの命名をよくみると、A、B、C、Dの順ではなく、AX、CX、DX、BXの順になってます。なお16ビットレジスタAXの上側8ビットがAH、下側8ビットがALというネーミングです。

これを理解するにはさらに右(古い)の8080のレジスタを眺めねばなりませぬ。8080の場合、8ビットのA、B、C、D、E、H、Lという7本のレジスタがありました(半端な7本の理由は後で)、そして「レジスタペア」としてBC、DE、HLが組み合わさって16ビットのレジスタとして使えることになっていたのです。そして8080の場合、各レジスタの使い途にはかなりクセがありました。

    • A、アキュムレータとして各種演算のオペランドの一方と結果を保持
    • BC、カウンタなどにつかう
    • DE、メモリを指すのに使うがHLの補助?
    • HL、メモリを指すのに主に使う

とくにHLにアドレスを置いておくと、第8の仮想的なレジスタ M というものを経由して、さも8ビットレジスタ間の操作のように処理ができるという仕組みになっていました。それで現物レジスタは7本なのね。

上記のような「クセ強」な8080の命令を使ったプログラム(アセンブラ)を、オブジェクトコード互換性は無いものの、アセンブリ言語レベルで「機械的に」移植できるようにしたのが8086だったのです。対応関係は以下のごとし。

    • Aー>AL
    • BCー>CX(B=CH、C=CL)
    • DEー>DX(D=DH、E=DL)
    • HLー>BX(H=BH、L=BL)

次回から8086の16ビットの命令セットを練習していきますが、「なんでこんなヘンテコなことになっているの?」と思ったら、その裏には「8080ではこうなっていたから」という理由があることが多いです。

ぐだぐだ低レベルプログラミング(178)ARM64(AArach64)オプショナル命令確認 へ戻る

ぐだぐだ低レベルプログラミング(180)x86(16bit)、INC/DECにCISCを見? へ進む