はじめて読む486　第5章 セグメント

## 5. セグメント

## 5.1 セグメント

- セグメント方式
    - 8086版
        - セグメントの大きさは64Kバイトまで
        - 他のセグメントへのアクセスが出来てしまう
    - 486版
        - 上記の問題を解消
- リニアアドレス
    - マシン語命令では物理アドレスでメモリを指定せず、セグメントアドレスとオフセットアドレスの組を使う
    - セグメントアドレスからセグメントベースを作成する
        - セグメント先頭のリニアアドレスをセグメントベースという
        - 作成方法は486のリアルモードとプロテクトモードで異なる
    - さらに、リニアアドレス＝セグメントベース+オフセットアドレスを作成する
- セグメントレジスタ
    - セグメントを指定するレジスタでこれとオフセットアドレスを参照してメモリアクセスする
    - CSレジスタ
        - メモリからマシン語命令を読みこむセグメントを指し示す
        - オフセットアドレスはIP（EIP）アドレスの内容
            - 命令読み込み後は次の命令を指すよう加算される
    - DSレジスタ
        - 命令の実行によるメモリアクセスのセグメントを指し示す（原則）
    - SSレジスタ
        - スタック領域として使うセグメントを指し示す
            - push, pop, call, ret命令
            - BPレジスタでアドレスをしてする命令
    - ES/FS/GSレジスタ
        - 特定の用途が決まっていない予備のセグメントレジスタ
            - 但しSTOS, MOVSなどではESを使うなど例外あり
        - セグメントオーバーライドプリフィックスでDSから変更する
            - es: [0080h]
    
    ## 5.2 リアルモードのセグメント
    
    - セグメント指定方法
        - セグメントアドレスを4~19ビット目に当てはめてセグメントベースと対応させる
    - 問題点
        - 下位4ビット、20ビットより上は常に0
            - 1Mバイトの壁
        - オフセットアドレスも16ビットまで
            - 64Kバイトの壁
        - 論理的なセグメントの大きさを超えてアクセスできてしまう
        - セグメントレジスタに任意の値をセットできてしまう
    
    ## 5.3 プロテクトモードのセグメント
    
    - セレクタ値
        - リアルモードのセグメントアドレスに該当するもの
    - （グローバル）ディスクリプタテーブル
        - セレクタ値順にセグメントディスクリプタを格納
        - メモリ上に配置
        - セグメントディスクリプタ
            - セレクタ値毎にセグメントベース、リミット値、属性をまとめたもの
                - セグメントベース
                    - セグメント先頭のリニアアドレス
                    - 286では24ビット：16Mバイト
                    - 486では32ビット：4Gバイト
                - リミット値
                    - セグメントの大きさ-1
                    - 286では16ビット：64Kバイト
                    - 486では32ビット：1Mバイト
                        - Gビットセットで4K倍：4Gバイト
                - 属性
                    - セグメントの種類を設定
    - セグメント指定方法
        - セグメントディスクリプタでセグメントベースと対応させる
    - セグメントの割り当て
        1. アプリケーションのメモリ要求
        2. メモリ確保
        3. ディスクリプタテーブルの空いているセグメントディスクリプタの確保
        4. セグメントディスクリプタ設定
        5. セレクタ値をプログラムに返す
        6. セグメントレジスタにセレクタ値をセット
- GDTR
    - ディスクリプタテーブルのリニアアドレスをロードしておく（CPUからディスクリプタテーブルを参照するため）
    - LGDT命令でロード
- セグメントディスクリプタキャッシュ
    - セグメントセレクタにセレクタ値をロードすると自動的にセグメントディスクリプタがキャッシュされる
    - キャッシュがあることで、リニアアドレスへの変換やオフセット値のチェックを高速に行える

## 5.4 プロテクトモードのセグメント方式の働きを確認するプログラム

省略

## 5.5 32ビットセグメント

- 32ビットの数値を扱う（オペランドサイズ）
    - 1つのマシン語命令で扱う数値の大きさをオペランドサイズという
    - オペランドサイズが32ビットの命令を扱えるようになることで、プログラム作成や実行効率が大きく向上する
- 32ビットのアドレス空間（アドレスサイズ）
    - アドレス空間の大きさをアドレスサイズという

|  | オペランドサイズ16ビット | オペランドサイズ32ビット |
| --- | --- | --- |
| アドレスサイズ16ビット | mov ax, [bx] | mov eax, [bx] |
| アドレスサイズ32ビット | mov ax, [ebx] | mov eax, [ebx] |
- アドレスバスA20の制御
    - 486を搭載したMS-DOSマシンでは、1Mバイトを超えるアドレスのメモリをアクセスできないようにガードが掛けられている
    - 8086では、アドレスバスが20ビットしかないため、オーバーフローするとアドレス空間を一周して予期せぬアドレスにアクセスしてしまう
        - ただこれを利用して、割り込みベクタテーブルや低位アドレスの作業領域をアクセスするようなプログラムも存在した
    - （8086の仕様に合わせて）286以降のCPUを搭載したMS-DOSマシンには、アドレスバスの21本目のA20ビットからどんな値を出力されてもメモリには常に0を送るような回路が組まれている
        - そのため上記のような目的で使いたい場合にはこのガードを外して全アドレスを有効にする必要がある
- オペランドサイズプリフィックス
    - オペランドサイズが32ビットの命令の先頭についたマシン語命令（書籍では”66H”）をオペランドサイズプリフィックスという
    - これにより次のオペランドサイズを32ビットに変えることができる
- アドレスサイズプリフィックス
    - アドレスサイズが32ビットの命令の先頭についたマシン語命令（書籍では”67H”）をアドレスサイズプリフィックスという
    - これにより次のアドレスサイズを32ビットに変えることができる
- 32ビットセグメント
    - リアルモードでは、オペランドサイズやアドレスサイズのデフォルト値は必ず16ビットだが、プロテクトモードではデフォルト値を16ビットか32ビットを選ぶことができる
    - サイズプレフィックスをつけることで、デフォルト値ではないビット数を意味する命令に変更できる
- Dビット
    - コードセグメントが16ビットか32ビットかはセグメントディスクリプタ中のDビットで決まる
    - Dビットは属性部分の第6ビットで、0なら16ビットセグメントとなり、1なら32ビットセグメントとなる
- use16とuse32
    - アセンブリ言語では、SEGMENT擬似命令で指定するセグメントの属性によって、16ビットセグメントであるか32ビットセグメントであるかを指定することができる
    
    ```nasm
    segment byte public use16 'CODE'
    ```
    
- 486のパワーを生かす32ビットセグメント
    - 486のパワーを生かすには、MS-DOSではなく32ビットセグメントを活用できる新しいオペレーティングシステムに移行する必要がある