Intel 80286のA20マスクにまつわる話

はじめに

HMAの説明で80286の不具合として挙げられていることが多いA20マスクがらみの話。しかし、これは仕様であって不具合ではないという解説をしてみたいと思います。

※本稿では16進数表現を「0000h」のように終端に「h」を付けて表現します。数値の先頭が0から9ではない場合には0を補います。ただし、参考文献からの引用は原文通りとします。

A20マスクとは

MS-DOS時代にメモリを拡張する方式の一つとして広く使われた規格にXMS(eXtended Memory Specification)があります。これにはXMB(Extended Memory Block)UMB(Upper Memory Block)HMA(High Memory Area)という3種類のメモリを管理するものです。

このうち、HMAはIntel 80286(以下、80286)のアドレス空間がIntel 8086/8088(以下、8086)の20ビットから24ビットへ拡張されたことによってもたらされた約64キロバイト(正確には65,520バイト)のメモリ空間を指します。これはアドレスで示すと100000hから10ffefhの部分にあたります。

8086は、プログラム上でのアドレス表現を「セグメント」と「オフセット」で行います。それぞれ16ビット幅です。メモリのアドレスはセグメント×16+オフセットで決まります。この表現でセグメント=0ffffhとし、オフセットを0010hから0ffffhの間とすると、意味するアドレスは100000hから10ffefhとなります。

しかし、8086ではアドレスが20ビットのため、アドレス線の21本目(20ビット目)は出力対象となりません。結果としてアドレスは丸め込まれて00000hから0ffefhになります。これに対して80286ではアドレスが24ビットあるため、そのまま100000hから10ffefhが有効なアドレスとなります。

この些細な差は当時のプログラムに対して互換性上の問題を生じさせるということで、IBMはIBM PC/ATに80286を採用するにあたってアドレスの20ビット目(アドレスを本数で数えた21本目)を常に0評価とし、互換性を保つ機構を追加しました。同様の機構は日本電気が開発したPC-9800シリーズなどにも存在します。これを通称「A20マスク」や「A20 gate」と言います。本稿では「A20マスク」と呼びます。

このA20マスクは、80286を初めて搭載したIBM PC/ATで最初から搭載されており、IBMがPC/ATを設計するにあたって80286にこの特性があることを念頭に置いていたことがわかります。

A20マスクが必要になったのは80286の不具合なのか?

後方互換のために、このA20マスクが必要となることについて、80286の不具合あるいはバグであるとする記載を行っているWebページが見受けられます。私はこの見方は誤りだと考えています。

Intelとしては、単純に8086においてアドレスのビットあふれを前提としたプログラムが作成されることを意図していなかっただけのようです。(補足1) このため、80286の設計においてこのことを念頭に置かない仕様で開発を進めたと推測しています。

この推測の根拠は1983年リリースのIntel iAPX 286 Programmer’s Reference ManualのAPPENDIX D iAPX 86/88 SOFTWARE COMPATIBILITY CONSIDERATIONS(iAPX 86/88用ソフトウェアとの互換性上の考慮事項)にこの事象の記載がないことと、80286が電源投入直後あるいはリセット後にリアルアドレスモード(一般にリアルモードと呼ばれる。以下、リアルモード)で、0fffff0hから実行を始める仕様となっていることによります。(補足2) つまり、リアルモードでありながら20ビットのアドレス範囲外の領域からプログラムの実行を開始する仕様となっているのです。このことから、Intelがリアルモードにおいても20ビットの範囲外のアドレスを指し示すことができることを仕様としていることが読み取れます。

この仕様の理解をさらに補強するAPPENDIX Dの7を引用します:

7. Place a Far JMP Instruction at FFFFOH.

After reset, CS:IP = FOOO:FFFO on the
iAPX 286. This change was made to
allow sufficient code space to enter
protected mode without reloading CS.
Placing a far JMP instruction at
FFFFOH will avoid this difference. Note
that the BOOTSTRAP option of LOC86
will automatically generate this jump
instruction.


(さかきけい意訳)
7. ffff0hへ置くfar JMP命令

iAPX 286におけるリセット後のCS:IPは0F000h:0FFF0hである。
この変更は、十分なコード格納領域を確保し、CSをリロードすることなくプロテクトモードに入ることを可能にするために行われた。(訳注1)
ffff0hへfar JMP命令を置くと、この違いは回避できる。(訳注2)
LOC86のBOOTSTRAPオプションがこのjump命令を自動的に生成することに注意すること。

下線強調は筆者による。

下線強調部分に記載のある変更により、上位アドレスにあるROMから下位のアドレスへ一度も移ることなくプロテクトモードに必要な初期化を終えて、直接プロテクトモードのプログラム(たとえばOSなど)を起動できるようになります。このことから、80286のリアルモードにおいて20ビット範囲外のアドレスへのアクセスはIntelが意図した通りの仕様であると結論付けることができます。

仕様だとしても仕様バグなのではないか、と思う向きもあるかもしれませんが、先ほどの資料のAPPENDIX Dに15項目の非互換の内容が列挙されているように、もともと「完全な」互換性を持たせるという設計仕様とはなっていないのです(ちなみに、後日発行された組み込み用80286の仕様書には仕様上の非互換としてこのアドレスの件も記載されています)。

手元にある(本稿執筆時点で)20年前の1993年に英語から日本語に翻訳されて出版された「80x86/80x87 ファミリー・テクニカルハンドブック / 著:Robert L.Hummel 訳:槌田浩一 / 技術評論社 ISBN4-87408-588-1 C3055 P5800E」では、CPUの非互換性と不具合を非常に細かく解説してるのですが、このアドレスの件は不具合分類ではなく、非互換性に分類しています。以下に同書の「第14章 非互換性とバグ – プロセッサの非互換性 – 8086と80286リアル・モード」より、本稿に関連する部分を241ページから引用します:

アドレス空間ラップアラウンド 8086では、1Mbを超えるアドレスは自動的にアドレス空間の先頭にラップアラウンドされます。たとえば8086では、FF00 : 8000hは0000 : 7000hと同じアドレスです。80286は、この動作をエミュレートしていませんが、外部ハードウェアを使って(PC互換機のように)強制的にエミュレートすることができます。

この記述より、少なくともこの本が出版された時点では不具合として扱われていなかったことがわかります。

また、この件が不具合だとするとIntelのDatasheet、Specification、あるいはSpecification Updateなどに必ず記載があるはずです。しかし、かなり力を入れて探したのですが見つかりませんでした。さらに、これが不具合だとすると当時のIntelがステッピング変更で修正しなかったことが不可解です。また、80386でも仕様(補足3)を継承したことも不可解だといえるでしょう。

まとめ

このように、

  • IBMは80286にこの非互換性があることを最初から知っていたことを考えると、Intelから仕様として情報を得ていたと推定される。
  • Intelはリアルモードで20ビットのアドレス範囲外をアクセスすることを意図した仕様を採用している。
  • Intelがこの件を不具合だとしている一次ソースが見当たらない。
  • 出版物においても不具合ではなく非互換性として扱われている。
  • 80286のステッピング変更で修正されていない。
  • 80386でも継承されている。
  • 私自身、当時8086系のアセンブリ言語でプログラムを書きまくっていたが、これを不具合だとする話を聞いたことがない。

といったことから、80286のA20マスクにまつわる不具合説は誤りであると考えています。

ところで、ここまでいろいろな状況証拠を重ねて提示してきましたが、実際にはもっと確実な証拠があるのです。それは初代IBM PCの発売は1981年8月12日で、80286の発表は1982年2月1日だということです。説明するまでもないかと思いますが、CPUはそう簡単に設計できるものではありません。IBM PCの発売から80286の発表まで約5カ月です。この間にMS-DOSの上で動作するアプリケーションが出そろい、その状況を分析して設計を終えて発表を行うなどあり得ないでしょう。

このことと紹介した資料の内容から、ほぼ間違いのない事実として80286はMS-DOSのようなOSとその上で動くアプリケーションソフトウェアの書かれ方を単純に考慮していなかったと推定できるのです。


  • さらに正確な表現をすると、Intelは80286の設計時において、セグメントの値はOSがメモリを確保したうえでその場所を指し示すものいうことを前提として考えていたようです。つまり、OSにメモリを要求すると、OSがメモリを確保してその位置を示すセグメントの値を返し、アプリケーションはそのセグメントの値をそのまま使うものだという前提を置いていたとみられます。したがって、アプリケーションが最適化のためにアドレスが折り返すことを前提として、管理対象外の位置を指すセグメントの値を設定するということなど、ユースケースとして考慮にいれなかったと考えられるのです。
  • ただし、CS:0F000hIP:0FFF0hとなっています。CSレジスターの実効値はこのレジスターの内容ではなく、ディスクリプターキャッシュレジスターの内容となるため、ここでは先ほどアドレスの計算方法として説明した、セグメント×16+オフセットとは異なるアドレスの計算結果となります。
  • CSをリロードするとディスクリプターキャッシュレジスターが上書きされて、上位メモリアドレスから下位メモリアドレスへ移動することになります。8086ではfar JMPまでの猶予が16バイトしかありませんが、それをこの変更で65536バイトに拡張して回避可能にすることを意図していると説明しています。
  • 8086のリセット後のCS:IP0ffffh:0000hだが80286のリセット後のCS:IP0f000:0fff0hとはリアルモードにおいて意味的に同じアドレスを指しているので、ここにfar JMP命令を書くと互換性を取ることができるということを説明しています。
  • ここでいう仕様とは、

    • リアルモードでも20ビットアドレス範囲外へアクセスできる。
    • CPUの起動あるいはリセット時にメモリアドレス最上位の64キロバイトが使用できる状態で最終アドレス-15の位置から実行を開始すること。

     
    を指しています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です