MASM, Visual C++におけるメモリーモデルについて


Microsoft Macro Assembler（以降、MASMと略）でアプリケーションを開発する際のメモ書きです。
この記事では、MASM, Visual C++等のMicrosoftの開発環境（16bit, 32bit）におけるメモリーモデルの指定方法を解説します。
※64bitのコードにおいては、メモリモデルは無く、以下の３つのディレクティブは非対応です。

1. [メモリーモデルとは](#メモリーモデルとは)
2. [メモリモデルの指定(`.model`)](#model)
3. [スタートアップコード(`.startup`)](#startup)
4. [終了コード(`.exit`)](#exit)

# メモリーモデルとは

Intel 8086のCPUシリーズ（及び286以降のリアルモード）における、セグメントを用いたメモリアクセスを行うアーキテクチャに対応するためのモデル。
これらのアーキテクチャでは、アドレス空間は20bit（1 MBytes）となるが、アドレスを示すレジスタや、各命令コードが持つアドレス幅は16bit（64 kByte）という制約がある。

これらのIntel 8086、及び286以降のリアルモード(16 bit)のプログラムコードでは、セグメントレジスタ（`CS`, `DS`, `ES`, `SS`, 386以降より`FS`, `GS`）に格納される16bitのセグメントアドレス、並びに16bitのオフセットアドレスによって、20bit幅のアドレスが決まる（そのため、１つのセグメントは、64 kByteの制約がある）。

※20bitのアドレス空間は、セグメントアドレス(16bit) × 16 + オフセットアドレス(16bit)となる。


## メモリーモデルの種類

※64bit（x86-64）の実行ファイルには、メモリモデルは無い。

### TINY

実行コードと、データが同じセグメントに存在するメモリーモデル。
したがって、両方合わせて、64 kByte以内となる。
MS-DOS, Windows以外のアプリケーション（例えば、ワンダースワンのゲーム）を開発する場合等は、このメモリーモデルを使う。

- 実行ファイルの形式：バイナリ形式（MS-DOSの実行ファイル`.COM`を含む）
- セグメントグループ：`DGROUP` ＝ `_TEXT`, `_DATA`, `CONST`, `_BSS`(, `STACK`)

**＜MS-DOS の `.COM`形式について＞**
`.COM`形式のファイルは、0x0100番地から始まるプレーンなバイナリであり、プログラム開始時は以下の様に初期化される。

- セグメントレジスタ：`CS`=`DS`=`ES`=`SS`
- スタックポインタ：`SP`=0000h （※十分な空きメモリがある場合）
- 命令ポインタ：`IP`=0100h

従い、以下のような注意点がある。

- コード(`_TEXT`セグメント)の先頭には、`ORG 0100h`、もしくは[`.startup`ディレクティブ](https://qiita.com/sha_w/items/8c703fefa76741cfebaa#startup)が必要となる。  
- `.COM`形式の実行時、COMMAND.COMは空きメモリ領域を全て割り当て、この時64 kByte以上割り当てることが出来た場合、`SP`レジスタを0に設定する。そのため、多くの場合、ファンクションコール（`INT 21h, AH=4Ah`）により割り当てられたメモリサイズを変更する必要があり、それに伴いスタックポインタ(`SP`レジスタ)も設定し直す必要がある。

例：

```main.asm
.186
.model	tiny,　stdcall
.dosseg
.stack	1000h

;プロトタイプ宣言
Memory_Small_Com    proto   near,   sizeCOM:WORD

.code
        .startup                        ;スタートアップコードの生成

        MOV     SP, offset DGROUP:STACK ; SP を.stackディレクティブで設定したスタック領域の末尾に設定。
        INVOKE  Memory_Small_Com, SP    ;メモリの最小化
        CALL    _main                   ;mainルーチンの呼出

        .exit   0                       ;終了コードの生成
```

```comsmall.asm
.186
.model	tiny,stdcall
.code

;メモリーの最小化
Memory_Small_Com          proc    near    uses ax bx es,
        sizeCOM:WORD

        PUSH    CS              ;
        POP     ES              ;ES ← CS
        MOV     BX, sizeCOM
        SHR     BX, 4
        INC     BX              ;BX ← (sizeCOM >> 4) + 1
        MOV     AH, 04AH        ;
        INT     21H             ;割り当てられたメモリの最小化
        .if     (carry?)
        ;       to do: Error 処理をここに書く
        .endif

        RET
Memory_Small_Com        endp

        end

```

※`.COM`は、元々CP/M-80で使われていた拡張子であり、コード・データのサイズは、64kByte-256Byte（オフセットアドレス0x0100～0xFFFFの範囲）となる。

### SMALL

実行コードに1つのセグメント（`_TEXT`）、データに1つのセグメント(`DGROUP`)を使用するメモリーモデル。
実行コード、データ共に64 kByte（合計128 kByte）までの実行ファイルを作れる。
一般的なMS-DOS用の16bitアプリケーションは、このモデルを用いて開発する。

- 実行ファイルの形式：`.EXE` (16 bit)
- セグメントグループ：`DGROUP` ＝ `_DATA`, `CONST`, `_BSS`(, `STACK`)

### COMPACT

実行コードに1つのセグメント（`_TEXT`）、データには複数のセグメントを使用することができるメモリーモデル。

- 実行ファイルの形式：`.EXE` (16 bit)
- セグメントグループ：`DGROUP` ＝ `_DATA`, `CONST`, `_BSS`(, `STACK`)

### MEDIUM

実行コードに複数のセグメント、データに1つのセグメント(`DGROUP`)を使用することができるメモリーモデル。

- 実行ファイルの形式：`.EXE` (16 bit)
- セグメントグループ：`DGROUP` ＝ `_DATA`, `CONST`, `_BSS`(, `STACK`)

### LARGE

実行コード、データ共に複数のセグメント使用することができるメモリーモデル。
配列変数等のサイズは64 kByteに制限される。

- 実行ファイルの形式：`.EXE` (16 bit)
- セグメントグループ：`DGROUP` ＝ `_DATA`, `CONST`, `_BSS`(, `STACK`)

### HUGE

実行コード、データ共に複数のセグメント使用することができるメモリーモデル。
配列変数等のサイズは64 kByteに制限されない。

- 実行ファイルの形式：`.EXE` (16 bit)
- セグメントグループ：`DGROUP` ＝ `_DATA`, `CONST`, `_BSS`(, `STACK`)

### FLAT

プロテクトモード(32bit)用のメモリモデル。
実行コード、データ共に同一のセグメントを使用する。
Windows用の32bitアプリケーションを開発する場合は、このメモリモデルを使う。

- 実行ファイルの形式：`.EXE` (32 bit)
- セグメントグループ：`FLAT` ＝ `_TEXT`, `_DATA`, `CONST`, `_BSS`, `STACK`

## Intel 8086、及びリアルモード(16bitコード)におけるポインタについて

### near ポインタ

オフセットアドレス（16bit）のみで構成されるポインタ。

### far ポインタ

セグメントアドレス(16bit)と、オフセットアドレス(16bit)で構成されるポインタ。

# .model

## 書式

```Assembly
.model memorymodel (,langtype) (,stackoption)
```
## 説明

当該アセンブリ言語ファイルをアセンブルする時のモデルを設定する。
通常、CPU指定（例： `.586` ）の次に記述する。

## パラメータ

### memorymodel

メモリーモデルを指定する。
メモリーモデルについては、上述の[メモリーモデルの種類](#メモリーモデルの種類)を参照

- 32bit版のWindows用アプリケーションの実行ファイル（*.exe）のメモリモデルは`FLAT`であり、Visual C++等でコンパイルされた*.objファイルとリンクする場合は、`FLAT`を指定する。
- 64bit版のWindows用アプリケーションの実行ファイル（*.exe）にはメモリモデルは無く、`.model`ディレクティブは不要となる。

### langtype

デフォルトの関数の言語タイプを設定する。
関数呼出規約についての詳細は、別途、[MASM, Visual C++における関数呼出規約](https://qiita.com/sha_w/items/15c598c2bf28814f7929)を参照。


| langtype | Visual C++ における関数呼出規約 |
|----------|--------------------|
| C        | __cdecl            |
| SYSCALL  | __syscall (廃止)   |
| STDCALL  | __stdcall          |
| BASIC    |                    |
| FORTRAN  | __fortran (廃止)   |
| PASCAL   | __pascal (廃止)    |
※`BASIC`, `FORTRAN`, `PASCAL`は、全く同じ関数呼出規約です。
※16bit版Windows（Windows 3.x以前）のWINAPIの関数呼び出し規約は`PASCAL`です。
※32bit版Windows（Windows NT及びWindows 95以降）のWINAPIの関数呼び出し規約は`STDCALL`です。

### stackoption

スタック領域の場所を示す。

| stack option | スタックの場所 |
|--------------|---------------|
| NEARSTACK    | SS = DS       |
| FARSTACK     | SS ≠ DS       |

※i8086、及びリアルモード用のメモリモデルにおいて、stackoptionにて`FARSTACK`を指定した場合、セグメントグループ`DGROUP`からセグメント`STACK`は外される。
尚、`FLAT`モデルでは、stackoptionは不要。


---
# .startup

## 書式

```Assembly
.startup
```
## 説明

MS-DOS用の、スタートアップのコードを生成する。
modelの指定内容によって、生成コードが異なる。
32bit, 64bitのスタートアップコードの生成は非対応。

memorymodel=`TINY`の場合。

```Assembly
        org     0100h
@Startup:
```

memorymodel≠`TINY`, `FLAT`以外で、且つstackoption=`NEARSTACK`（SS=DS）の場合
SS=DS(DGROUP)として、SPレジスタを調整するコードを生成する。

```Assembly
@Startup:
        mov     ax, DGROUP
        mov     ds, ax
        mov     bx, ss
        sub     bx, ax
        shl     bx, 1           ; If .186 or higher, this is
        shl     bx, 1           ;   shortened to shl bx, 4
        shl     bx, 1
        shl     bx, 1
        cli                     ; Not necessary in .286 or higher
        mov     ss, ax
        add     sp, bx
        sti                     ; Not necessary in .286 or higher

;       statements

        END	@Startup
```

memorymodel≠`TINY`, `FLAT`以外で、且つstackoption=`FARSTACK`（SS≠DS）の場合

```Assembly
@Startup:
        mov     dx, DGROUP
        mov     ds, dx

;       statements

        END     @Startup
```

---
# .exit

## 書式

```Assembly
.exit	expression
```

## 説明

MS-DOS用の、シェル（command.com等）に戻るコードを生成する。
32bit, 64bitのスタートアップコードの生成は非対応。

```Assembly
        mov     ah, 04Ch
        int     21h
```

## パラメータ

### expression

シェールに返すコードを0～255で指定する。
主に、エラーコードとして用いられ、指定した場合は以下のコードを生成する。

```Assembly
        mov     ax, 04C00h + expression
        int     21h
```