[スクリプト言語しか書けないあなたへ]FreeDOSとdebugコマンドで8086アセンブラ入門

この記事は [慶應義塾大学SFC村井&徳田研 Advent Calendar 2015](http://www.adventar.org/calendars/1053) の16日目の記事です。

-----

※この記事は現代の進歩した技術しか知らない、低レイヤの技術には触れたことがないという人を対象にしています。

# はじめに

せっかくの[このような機会](http://www.adventar.org/calendars/1053)なので最初に少しだけ、私の思うこと、この記事の目指すところを書きたいと思います。

そんなものに用はないという人は→[こちら](#usk-main-contents)

タイトルをQiita的なものにするか迷ったのですが、あえてこのようにしてみました。このタイトルがまさに私の思うところです。私感ですが、最近、Ruby,Pythonをはじめとしたスクリプト言語しか書けない人が増えているように思います。また、表面的な理解だけで流行りの技術やワードに飛びつく人が多いように思います。

確かに現在は、便利な言語やフレームワークが次々と開発され、あらゆることが簡単にできるようになりました。また、それにより数々のITベンチャー企業が誕生してきています。ここで今一度考えたいのは、このような技術の進歩は、数々の偉大なる巨人たちによる技術の進歩の賜物であるということです。

技術の進歩により、あらゆることが便利になったのは間違いなく良いことであると思います。もちろん、それらの便利なものは積極的に使っていくべきです。なにも無駄にコストをかける必要はありません。しかし、だからといってそれらの技術をブラックボックスのままにして、中身については自分の領域ではないと知らん顔をするのはいかがなものでしょうか。最近、このように技術を表面的にしか学ぼうとしない人が多いように思います。そのような人が、例えばC言語を学ぶなどすると決まって「Pythonなら1行で書ける」、「Rubyなら...」などと言います。しかし勘違いしないでください、C言語だからx行で書けるんです。

お金を稼ぐことだけが目的のベンチャー企業であれば、別に技術に大して深い理解は必要ないかもしれませんし、それでいいと思います。しかし、研究という言葉を使って技術を学ぶ学生がこれでいいのでしょうか？少なくとも私はそうであってはならないと思うし、そのような現状にある種危機感を覚えます。別にすべてを理解しなくてはならないとまでは言いません。現代のすべての技術を根底から理解するには人間の寿命は短すぎます。しかし、知っておくことは重要なのではないでしょうか。

そこで、今回は低レイヤ技術であるアセンブラ、それも古い時代のアセンブラを紹介します。この機会に普段あまり低レイヤ技術に触れない人、触れようとしない人が、その面白さや、過去の巨人たちの功績を感じるきっかけとなれば幸いです。


<span style="color: #999; font-size: 9px">以上はあくまで私の個人的な理念です。かくいう私もこの便利な世界しか知りませんし、何を知っているのかと言われると何も知らないかも知れません。しかしこのような時代だからこそ、技術の進歩を省み、それを知るという姿勢が重要であると私は考えています。個人的なポエムにお付き合いいただきありがとうございました。ここからは本題に移りたいと思います。</span>

<h1 id="usk-main-contents">概要</h1>

今回はIntel8086 CPU上で動作するフリーのOSであるFreeDOSと標準搭載されているDEBUGコマンドを用いて8086アセンブラに触れてみたいと思います。
VirtualBoxなどを用いてFreeDOSをVMにインストールし、実際に8086アセンブラを書いて画面に`Hello, world!`を表示するところまでを行います。

#### 動作環境

環境やバージョンにより動作が変わってくる可能性があるため、今回私が試した環境を記載しておきます。

* OS X 10.9.5
* VirtualBox 5.0.10
* FreeDOS 1.1

## Intel8086とは

[Intel 8086 - Wikipedia](https://ja.wikipedia.org/wiki/Intel_8086)

Intel8086は、1978年に発表された16bit CPUです。x86系最初のCPUで、1985年に発表されたIntel80386で32ビットに、その後2004年のNocona、Pentium4などと64ビットに拡張され現代に至っています。
このCPU上で動作するOSとして有名なものとして、MS-DOSなどが挙げられます。
また、現代の32bit/64bit CPUと比べると命令が少なく、一つ一つがシンプルであると言えます。

![Intel8086](https://upload.wikimedia.org/wikipedia/commons/thumb/e/e1/KL_Intel_D8086.jpg/250px-KL_Intel_D8086.jpg)

## FreeDOSとは

[FreeDOS | The FreeDOS Project](http://www.freedos.org)
[FreeDOS - Wikipedia](https://ja.wikipedia.org/wiki/FreeDOS)


FreeDOSとは、オープンソースによるMS-DOSを代替するために作られたOSです。今回これを用いる理由として、フリーであるということとMS-DOS同様、メモリ保護機能がないということが挙げられます。これにより、メモリを直接操作することができるので<del>いろいろ破壊できます</del>よりアーキテクチャの理解につながることが期待できます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/70183/ce752a61-6c54-1482-3aed-e2ccdefbe511.png" alt="freedos_logo.png" width="300px">

# 環境構築

## 仮想化ソフトウェアのインストール

まず、FreeDOSをインストールするVMを建てるために仮想化ソフトウェアをインストールします。お好きなもので構いません。特にない場合は、フリーで利用することのできるVirtualBoxをインストールしてください。[Oracle VM VirtualBox](https://www.virtualbox.org)

インストールの仕方が分からないなんてことはないと思いますが、念のため詳しく解説されていた方のページへのリンクを張っておきます。[MacにVirtualBoxをインストールしてみた - VirtualBoxの使い方](http://pc-karuma.net/mac-virtualbox-install/)

それでもわからない人は →[こちら](https://www.google.co.jp)

## FreeDOSのインストール

次に今回使用するFreeDOSをインストールします。まず、[ダウンロードリンク](http://www.freedos.org/download/)よりOSのイメージファイルをダウンロードしてください。
「OSのイメージファイルのダウンロードか......時間かかりそうだから休憩するか」というあなた、安心してください。FreeDOSは8MBですべてそろいます。（LiveFloppyイメージであれば1.44MBしかありません。数秒で終わります。でも今回はインストールディスクを選択してください。）

けど、できれば今回はFreeDOS 1.1を使用するので、これに合わせていただけると幸いです。（<del>地味にダウンロードに時間かかった</del>）

ダウンロードが完了したら、VMを建ててインストールします。
今回はVirtualBoxを使用します。（バージョン 5.0.10）
まず、以下の画像の雰囲気でVMを作成してください。

![freedos_on_virtualbox.png](https://qiita-image-store.s3.amazonaws.com/0/70183/be3d2e4f-9f3b-0b16-d6ae-98902f34d40a.png "freedos_on_virtualbox.png")

画像のストレージ>コントローラー: Floppy>フロッピーデバイスの欄は今は気にしないでください。（ホストとのファイルのやり取りをするために仮想フロッピーディスクを使用します。方法は後ほど触れます。）
VMを作成したら、設定>ストレージ>コントローラーIDEを選択し、光学ドライブを追加ボタンを押して、先ほどダウンロードしたisoファイルを選択してください。選択したら、OKを押して設定を保存してください。

ここまで完了したら、次はVMを起動し、FreeDOSのインストールを行います。
少し行程が複雑なのですが、ここでは簡単な説明にとどめるので、詳しくは以下のサイトを参考にしてください。
[【レビュー】世界のOSたち - コンピューターを支える一時代を築いた「MS-DOS」 (2) 現在のMS-DOS環境「FreeDOS」.1 | マイナビニュース](http://news.mynavi.jp/articles/2012/04/20/msdos/001.html)

起動したら以下の画面が表示されます。

![install.png](https://qiita-image-store.s3.amazonaws.com/0/70183/cc73bfb8-fa1e-8f4e-1d9f-6b335b335ebc.png "install01.png")

表示されたら、まず上から2番目、`Create drive C:`を選択してHDDの初期化を行ってください。基本的には初期設定のまま進めていけば問題ありません。
環境したら再起動します。このとき、光学ディスクの起動順序がハードディスクよりも先になっていることを確認してください。もしハードディスクの方が先にさっている場合は、VMを終了して、設定>システム>起動順序より起動順序を変更してください。

再び起動したら、今度は1番上の`Install to harddisk`を選択してください。
その後のインストール手順は上記のサイトを参考にしてください。
インストールが完了したら、VMを終了して設定からインストールディスクを取り出してください。（インストールディスクはもう必要ありません。）

インストール完了後再起動すると以下のような画面が表示されてFreeDOSが立ち上がります。

![running.png](https://qiita-image-store.s3.amazonaws.com/0/70183/c82d4cc3-cb8f-3e71-c1ba-3e5278438977.png "running.png")

この画面になったら`1, 2, 3, 4`いずれかを押すことで起動します。
それぞれの違いは各種ドライバ、マネージャを読み込むかどうかの違いなのですが、今回は勉強に使うだけなので、最小構成の4番でも構いません。

ここで注意なのですが、 **VirtualBoxを使用している方は1番を選択すると途中でフリーズします**。
少なくとも私の環境では起動しませんでした。VirtualBoxの仮想化の問題ということですが、詳しくはわかりません。1番を選択して起動しない場合は、`2~4`番のいずれかを選択して起動してください。

# FreeDOSを触ってみる

さて、ここまででFreeDOSを起動することができたので少し触ってみましょう。

みなさん、いつも黒い画面でなにをしていますか？私は特にやることがないと`ls -la`と`Ctrl + l`を交互に入力していたりします。（余談ですが、lsにエイリアスを貼っていないのは個人的な趣味です。`ls -la`の指の動きが好きです。）
それではやってみしょう。

![ls.png](https://qiita-image-store.s3.amazonaws.com/0/70183/90ebae38-3d8e-5308-a45b-a1e4b28ab18e.png "ls.png")

まあ当たり前ですね。FreeDOSはUnixの血を引くOSではないので、Unixコマンドは使えません。普段Windowsを使っている人からしたら当たり前かもしれません。各種コマンドは[MS-DOSコマンド一覧](http://www.geocities.co.jp/SiliconValley-PaloAlto/2099/ms-dos.html)などを参考にしてください。
基本的に以下のものくらい覚えておけばいいかなと思います。

|Unix|FreeDOS|
|:-:|:-:|
|cd|chdir(cd)|
|pwd|cd(引数なし)|
|ls|dir|
|mkdir|md(mkdir)|
|touch x|type nul > x|
|rm|del|
|rmdir|rd(rmdir)|
|cp|copy|
|mv|move|
|clear|cls|

また、コマンドのオプションは`-x`の形式ではなく`/x`です。
さらに、ファイル名やディレクトリ名の表示が大文字になってますが、FreeDOSのコマンドは原則、大文字小文字を区別しないので、小文字でも大文字でも同じように扱われます。

もう一つ触れておきたいことがあります。

![editor.png](https://qiita-image-store.s3.amazonaws.com/0/70183/87c2ca2a-d368-a9e1-49e3-85cd6203def6.png "editor.png")

ここは平和な世界です。争いはありません。（まあ頑張れば入れられるみたいですが......）
テキストを編集ためには`edit`というコマンドを使用します。

![edit.png](https://qiita-image-store.s3.amazonaws.com/0/70183/2912f33e-c813-1cfe-f60a-442588559c1c.png "edit.png")

青いですね。
保存、終了などの操作は、ショートカットキーもしくは画面上の方のメニューから行います。メニューは`alt+赤文字`で開くことができます。（例えば、Fileメニューを開く場合は`alt+f`）

それでは、この`edit`コマンドを用いて少し設定ファイルを書き換えてみましょう。
起動時に読み込まれる設定ファイルおよび実行されるファイルはそれぞれ

- `FDCONFIG.SYS`
- `AUTOEXEC.BAT`

です。これらのファイルは`c:\`直下にあります。
どちらも`REM`から始まる行はコメントです。
まずは`FDCONFIG.SYS`を開いてみましょう。

```FDCONFIG.SYS
!COUNTRY=001,858,C:\FDOS\BIN\COUNTRY.SYS 
!SET DOSDIR=C:\FDOS
!LASTDRIVE=Z
!BUFFERS=20     
!FILES=40
!MENUCOLOR=7,0
MENUDEFAULT=1,5
MENU 1 - Load FreeDOS with JEMMEX, no EMS (most UMBs), max RAM free
MENU 2 - Load FreeDOS with EMM386 (Expanded Memory) and SHARE loaded
MENU 3 - Load FreeDOS including XMGR XMS-memory driver
MENU 4 - Load FreeDOS without drivers 
123?DOS=HIGH
12?DOS=UMB
12?DOSDATA=UMB
1?DEVICE=C:\FDOS\BIN\JEMMEX.EXE NOEMS X=TEST I=TEST NOVME NOINVLPG
2?DEVICE=C:\FDOS\BIN\HIMEMX.EXE 
2?DEVICE=C:\FDOS\BIN\JEMM386.EXE X=TEST I=TEST I=B000-B7FF NOVME NOINVLPG
3?DEVICE=C:\FDOS\BIN\XMGR.SYS 
3?SHELL=C:\FDOS\bin\4dos.com C:\FDOS\bin /E:1024 /P:C:\AUTOEXEC.BAT  
4?SHELL=C:\FDOS\BIN\COMMAND.COM C:\FDOS\BIN /E:1024 /P=C:\AUTOEXEC.BAT 
12?SHELLHIGH=C:\FDOS\BIN\COMMAND.COM C:\FDOS\BIN /E:1024 /P=C:\AUTOEXEC.BAT 
```

何となく雰囲気は分かると思います。
ここでは、7行目の`MENUDEFAULT=1,5`を書き換えてみましょう。この行は起動時に選択されるデフォルトのメニュー番号が指定されているのですが、FreeDOSのインストールの章に書いたように、VirtualBoxを使用している場合、1番を選択してしまうとフリーズしてしまうため、デフォルトでは2番になるように次のように変更しましょう。

```FDCONFIG.SYS
MENUDEFAULT=2,5
```

次に`AUTOEXEC.BAT`を開いてみます。

```AUTOEXEC.BAT
@echo off 
SET LANG=JA 
SET MTCPCFG=C:\FDOS\MTCP.CFG 
SET WATTCP.CFG=C:\FDOS 
SET PATH=%dosdir%\BIN
SET NLSPATH=%dosdir%\NLS 
SET HELPPATH=%dosdir%\HELP
SET TEMP=%dosdir%\TEMP
SET TMP=%TEMP%
SET BLASTER=A220 I5 D1 H5 P330
SET DIRCMD=/P /OGN /4
SET COPYCMD=/-Y
if "%config%"=="4" goto end
SHSUCDX /QQ /D3
LH SHSUCDHD /QQ /F:FDBOOTCD.ISO
LH FDAPM APMDOS
IF "%config%"=="2" LH SHARE
LH DOSLFN 
REM NLSFUNC C:\FDOS\BIN\COUNTRY.SYS
REM DISPLAY CON=(EGA),858,2)
REM MODE CON CP PREP=((858) C:\FDOS\CPI\EGA.CPX)
REM KEYB US,858,C:\FDOS\bin\keyboard.sys
REM CHCP 858
REM LH PCNTPK INT=0x60
REM DHCP
MOUSE 
DEVLOAD /H /Q %dosdir%\BIN\UIDE.SYS /H /D:FDCD0001 /S5
SHSUCDX /QQ /~ /D:?SHSU-CDR,D /D:?SHSU-CDH,D /D:?FDCD0001,D /D:?FDCD0002,D /D:?FDCD0003,D
MEM /C /N
IF NOT "%config%"=="4" SHSUCDX /D
GOTO END
:END
SET AUTOFILE=%0
SET CFGFILE=C:\FDCONFIG.SYS
LH KEYB JP,,keyboard.sys  
alias reboot=fdapm warmboot 
alias reset=fdisk /reboot
alias halt=fdapm poweroff 
alias shutdown=fdapm poweroff
alias cfg=edit %cfgfile%
alias auto=edit %0
echo Done processing startup files %cfgfile% and %0
echo Type HELP to get support on commands and navigation
echo. 
echo Welcome to the FreeDOS 1.1 operating system (http://www.freedos.org)
```

起動時のメッセージやエイリアスの設定がされているのが分かるかと思います。
Unix系OSになれていて`ls`とか打っちゃう、って人はここでエイリアスを張ってしまってもいいかもしれません。
今回はプロンプトの書き換えを行ってみたいと思います。
ファイル末尾に以下のように追記してみてください。

```AUTOEXEC.BAT
SET PROMPT=[$P]$_$G
```

追記後再起動するとプロンプトが変更されたことが分かるかと思います。（個人的におすすめの2行プロンプトです。）
その他の特殊文字は以下のリンクなどを参考にしてください。

[Prompt - DOS コマンド一覧 - Programming Field](http://pf-j.sakura.ne.jp/program/dos/doscmd/prompt.htm)


さて、ここでは`edit`を用いて設定ファイルの書き換えを行ってみましたが、実を言うと、実際に私がアセンブラでプログラムを作成した際は、`edit`だといろいろと辛かったので、Bochsに同梱されていた`bximage`コマンドで仮想フロッピーディスクを作成し、これをホスト、ゲスト両方にマウントすることでファイルのやり取りを行えるようにしていました。<span style="color: #999;">つまり、コードはホスト側で書いていました。</span>
そこで、次に仮想フロッピーディスクの使用方法を紹介します。

## 仮想フロッピーディスクの使用

この章はホストとゲストでファイルのやり取りを行うための設定なので、その必要がないという人はスキップして構いません。

みなさん、フロッピーディスク使ってますか？
ひょっとすると今の小学生はフロッピーディスクなど知らないかもしれないですね。

しかし、今回はフロッピーディスク、使います。まずは、仮想フロッピーディスクを作成しましょう。
いろいろな方法があるかと思いますが、私は`Bochs`をインストールした際についてきた`bximage`コマンドで作成しました。作成の仕方が分からない、という人はそうにかしてこの`bximage`コマンドをインストールしてください。
インストールしたら以下の要領で仮想フロッピーディスクを作成します。

```commandline
% bximage
========================================================================
                                bximage
  Disk Image Creation / Conversion / Resize and Commit Tool for Bochs
         $Id: bximage.cc 12690 2015-03-20 18:01:52Z vruppert $
========================================================================

1. Create new floppy or hard disk image
2. Convert hard disk image to other format (mode)
3. Resize hard disk image
4. Commit 'undoable' redolog to base image
5. Disk image info

0. Quit

Please choose one [0] 1

Create image

Do you want to create a floppy disk image or a hard disk image?
Please type hd or fd. [hd] fd

Choose the size of floppy disk image to create, in megabytes.
Please type 160k, 180k, 320k, 360k, 720k, 1.2M, 1.44M, 1.68M, 1.72M, or 2.88M.
 [1.44M] 

What should be the name of the image?
[a.img] fd.img

Creating floppy image 'fd' with 2880 sectors

The following line should appear in your bochsrc:
  floppya: image="fd", status=inserted
```

以上で、カレントディレクトリに`fd.img`というファイルが作成されたかと思います。
次に、仮想化ソフトウェアのFreeDOSの設定を開き、ストレージ>コントローラー: Floppyから先ほどのフロッピーディスクを選択して追加してください。
完了したら、設定画面右の属性の欄を確認して`フロッピードライブ(D): フロッピーデバイス 0`となっていることを確認してOKで完了します。

フロッピーディスクの追加が終わったらFreeDOSを起動してください。
フロッピーディスクは`a:`にマウントされているかと思います。
フロッピーディスクを使用できるようにするために、まずはフォーマットを行います。
以下の要領でフォーマットを行ってください。

![format.png](https://qiita-image-store.s3.amazonaws.com/0/70183/1833ecf2-ca77-d816-6970-6392572cafb3.png "format.png")

これによりフォーマットが完了しました。フォーマットしたフロッピーディスクには`a:`と入力することで移動することができます。またファイルのコピーも`copy xxx a:\xxx`などのようにすることで行えます。

あとは、ホスト側でこのフロッピーディスクをマウントするだけです。（OS Xならダブルクリックするだけ）
これでホストとゲストでファイルのやり取りを行うことができるようになりました。

# 8086アセンブラ入門

それではいよいよここからアセンブラの基礎を説明していきます。
とはいっても、すべて説明していたらそれだけで記事1つになっていまうので、ここではごくごく一部のみの説明にとどめます。この機会に興味を持ってくださった方はぜひいろいろと調べてみてください。

## アセンブリ言語とは

そもそもコンピュータが理解できるのは0と1の二値だけです。すなわち、C言語などで書いたプログラムをコンピュータそのまま実行することはできません。そこで、C言語の場合はコンパイラを使って機械語に翻訳（コンパイル）します。それによって、コンピュータが実行することのできる機械語が生成されるわけです。
この機械語というのは当然0と1の羅列になります。しかし、人間が0と1の羅列を理解することはとても困難です。そこでアセンブリ言語が登場します。このアセンブリ言語はC言語などの高級言語とは違い、機械語と1対1で対応しています。すなわち、あるアセンブリ言語で書かれた命令は必ず一つの対応する機械語が存在する訳です。
ともあれ例をみてみましょう。

![assembly01.png](https://qiita-image-store.s3.amazonaws.com/0/70183/b283afa2-a25a-c8b1-58a7-eb27b9e1c65d.png "assembly01.png")

この図の左から3列目の`B81000`などの文字列が機械語です。（16進数で表示されています。）そして、右側に表示されているのがそれに対応するアセンブリ言語です。

## ニーモニックとは

では、具体的なアセンブリ言語をみてみましょう。今回は上記の図の1行目の命令に注目してみます。

```assembly
MOV AX,0010
```

これは、16進数の10を`AX`レジスタに転送するという意味です。

このようなアセンブリ言語の命令をニーモニックと言います。
また、ニーモニックは2つの部分に分けることができます。上記の例のうち、`MOV`の部分をオペコード、`AX,0010`の部分をオペランドといい、またオペランドの前半`AX`のことを特に第1オペランド、後半`0010`のことを第2オペランドなどと言います。
このように、ニーモニックは1つのオペコードと0~2個のオペランドで構成されます。`AX`の意味は後ほど説明しますが、簡単に説明すると`AX`というレジスタを指定しているということです。

## 主なニーモニック

次に主なニーモニックを紹介します。
この他にもたくさんの命令がありますが、今回はそのうちのごく一部のみを説明します。

### 移動命令

メモリ、レジスタ、即値などを指定し、値を移動(複写)する命令です

```移動命令
MOV [Destination], [Source]
```

### 加算命令、減算命令

`ADD`、`SUB`命令はそれぞれレジスタやメモリの値を加算、減算します。
また、`INC`、`DEC`命令は指定したレジスタの値をインクリメント、デクリメントします。

```加算命令
ADD [Destination], [Source]
INC [Destination]
```

```減算命令
SUB [Destination], [Source]
DEC [Destination]
```

### 比較命令

レジスタと値を指定し、比較した結果に応じて、フラグレジスタの値を更新する命令です。
ジャンプ命令の前などに使用します。

```比較命令
CMP [Register], [Value]
```

### ジャンプ命令

指定されたアドレスに応じてIPを変更することにより、プログラムの処理の流れを変更する命令です。

```ジャンプ命令
JMP [Address]
```

また以下のようにいくつかの条件付きジャンプ命令も存在します。

|命令|条件|
|:-:|:-:|
|JA|より上の場合（zeroフラグとcarryフラグがセットされていない）|
|JB|より下の場合（carryフラグがセットされている）|
|JZ|zeroフラグがセットされている場合|
|JNZ|seroフラグがセットされていない場合|

## レジスタ

レジスタとは、CPU内部に存在する記憶回路でメモリなどと比べて遥かに高速にアクセスすることができます。アセンブラを書くときには、値をこれらのレジスタに転送し、演算するという操作が主になります。

以下が8086 CPUのレジスタです。

![register.png](https://qiita-image-store.s3.amazonaws.com/0/70183/f3fa0514-f40e-85af-4b37-4a97d9d4d172.png "register.png")

今回は簡単な解説のみにとどめます。

#### AX,BX, CX, DX

汎用レジスタ
演算の際の各種データの格納に使用します。

#### SP, BP

ポインタレジスタ
スタックポインタのアドレスを格納します。

#### SI, DI

インデックスレジスタ
メモリアドレスを格納し、転送命令などのベースアドレスとして使用します。

#### DS, ES, SS, CS
セグメントレジスタ
セグメントの管理に使用します。

#### IP

インストラクションポインタ
（次に）実行する命令のアドレスを示します．

#### その他

フラグレジスタ
CPUの状態を示すもので、各種命令により変更されます。また、ジャンプ命令はこのフラグレジスタの状態によって処理を分岐します。

# DEBUGコマンド

実際にアセンブラ書いていきたいところですが、そのまえにもう一つ`DEBUG`コマンドの使い方を解説します。
このコマンドは名前の通り実行ファイルのデバッグを行うものですが、メモリに直接アセンブラを記述し実行することもできます。FreeDOSには標準でこのコマンドが含まれているので、起動してみましょう。コンソールに`debug`と入力してエンターを押すと起動できます。

![debug.png](https://qiita-image-store.s3.amazonaws.com/0/70183/182781c7-23a5-4bff-f171-f6ba031c7273.png "debug.png")

このように入力待機状態になっていれば起動できています。
まずは以下に使用できるコマンドの一部を表にまとめます。

|コマンド名|使用方法|処理|使用例|
|:--|:--|:--|:--|
|register|R|すべてのレジスタの値を表示|R|
|register|R [regidter] [value]|レジスタに値を書き込む|R AX 1234|
|dump|D [start address] [end address]|メモリの値を表示する|D 100 120|
|enter|E [address] [list]|値をアドレスで指定したメモリに書き込む|E B8 ff 00|
|enter|E [address]|指定したアドレスからメモリに値を書き込む|E 100|
|assemble|A [address]|アセンブリ言語をマシン語に変換してアドレスで指定したメモリへ書きこむ|A 100|
|unassemble|U [start address] [endaddress]|指定したアドレスに格納されている機械語を逆アセンブルして表示する|U 100 106|
|go|G[=address] [breakpoint]|指定したアドレスのプログラムを実行する|G=100 108|
|proceed|P[=address]|プログラムをステップごとに実行する|P=100|
|help|?|使用方法の表示|?|
|quit|Q|デバッグコマンドを終了する|Q|

さてこれでアセンブリ言語を書き始める用意が整いました。
まずは`DEBUG`コマンドの確認がてら簡単にプログラムを書いてみましょう。

![program01.png](https://qiita-image-store.s3.amazonaws.com/0/70183/7a6215d0-18eb-d89f-7cae-3a3aa70cd1b6.png "program01.png")

上の図ではまずメモリアドレス100番からプログラムを書き始めています。命令は順番に

1. AXレジスタに10を転送
2. DXレジスタに20を転送
3. AXレジスタとDXレジスタの値を加算
4. AXレジスタに5を加算

となっています。その後、メモリアドレス100番からプログラムをステップごとに実行し、レジスタの値が変更されていくのが分かるかと思います。

それでは次に、少しレベルを挙げて10回のループで1から10までの合計を求めてみましょう。

![program02.png](https://qiita-image-store.s3.amazonaws.com/0/70183/ad916011-0161-6ad2-fc92-8372974d2b1c.png "program02.png")

こんな具合ですね。最終的にAXレジスタの値が16進数で`37`、10進数の55になっていることが確認できるかと思います。

# IBM PCのアーキテクチャ

今回のゴールであるHello, world!を表示するためには、文字の構成と表示する方法を知る必要があります。
そこで次に、文字を表示するために必要なMS-DOSやFreeDOSが動作するIBM PC互換機のアーキテクチャを解説します。

## VRAMの構成

画面に表示されている文字の情報も当然メモリのどこかに格納されています。この画面の情報が格納されている領域をVRAM(Video RAM)と言います。そしてこのVRAMはメモリアドレス`B8000`番以降に割り当てられています。

次に文字の構成を知る必要があります。
IMB PC（標準のキャラクタモード）の場合、1文字は偶数アドレス1バイトの文字コードと奇数アドレス1バイトの文字属性の2バイトで構成されています。また、文字属性は前半4ビットの背景色と後半4ビットの文字色で構成されています。
さらにもう一つ、重要なこととして画面に表示されている文字は横80文字、縦25文字で固定です。つまり、画面には1度に`80文字*25行*2バイト`の4000バイト分が表示されているということです。以上を簡単に図にまとめると以下のようになります。

![vram.png](https://qiita-image-store.s3.amazonaws.com/0/70183/b94efb47-e3bb-0562-d873-75c665ba36a1.png "vram.png")

## 文字コードと文字属性

まず文字コードについて、IBM PCの文字コードはasciiコードとは異なります。
以下に[ＯＡＤＧテクニカル・リファレンス](http://n.shop.vector.co.jp/service/servlet/Catalogue.Detail.Top?ITEM_NO=SR236180)より引用したIBM PCの文字コード表を記載します。

![char_code.png](https://qiita-image-store.s3.amazonaws.com/0/70183/20068c2d-db40-2112-2bba-aed53d79947b.png "char_code.png")

次に文字属性について、実際に表示させて確認した図を以下に記載します。

![char.png](https://qiita-image-store.s3.amazonaws.com/0/70183/b8b02d51-7e4b-e452-6321-8a125634c135.png "char.png")

上記の`0000~1111`までの4ビットで背景色、文字色をそれぞれ表現することができます。

ここまでの確認のため、`debug`コマンドを用いて画面左上に文字を表示してみます。

![display.png](https://qiita-image-store.s3.amazonaws.com/0/70183/27d62b28-31b8-3ad3-038e-bfd591c36b16.png "display.png")

画面左上に白背景緑色で顔が表示されているのが確認できるかと思います。
ここで1つ解説していなかったことがあります。
上記の図でメモリアドレスを`B800:0`と指定している点です。これは16ビットCPUが24ビットの実メモリアドレスを指定するために左側のセグメントアドレスと右側のオフセットアドレス2つのアドレスを使用しているためです。特にセグメントアドレスの指定がない場合は`DS`レジスタがセグメントアドレスとなるのですが、ここでは明示的にVRAMの先頭アドレスを示しています。

# Hello, world!

それではいよいよ`Hello, world!`を表示してみましょう！
まずは`debug`コマンドを起動して`enter`でメモリに`Hello, world!`を格納しておきましょう。

```
> debug
- e 9000:0 48 fc 65 fc 6c fc 6c fc 6f fc 2c fc 20 fc 77 fc 6f fc 72 fc 6c fc 64 fc 21 fc
```

今回は見た目が派手になるように白背景に赤文字にしました。

次にプログラムを書いていきます。以下の要領で書いていきます。

![hw01.png](https://qiita-image-store.s3.amazonaws.com/0/70183/f0f1225b-8956-9ac5-9ad4-f2d6f4d926fc.png "hw01.png")

ちょっと無駄がありますが、ここまでやった内容だけで理解できるはずです。
基本的には先ほど格納したメモリアドレスから値を取り出して、VRAMに転送するのを13回繰り返しているだけです。
`29BC:010d mov di,7c0`はVRAMの左上に表示すると終了とともに画面が送られて見えなくなってしまうためにいい感じに真ん中ら辺に表示するようにしています。

それではこれを実行してみましょう。

![w02.png](https://qiita-image-store.s3.amazonaws.com/0/70183/289c289b-995d-ee52-fe3f-b197e859de80.png "w02.png")

完璧ですね。以上で画面に`Hello, world!`を表示することができました。

そうですね、C言語だったら1行でかけますね！

# おわりに

最後までやってみてくださった方が果たして一人でもいるのかは疑問ですが、いかがだったでしょうか。
実際はアセンブラといってもマクロアセンブラを使用してアセンブリ言語から機械語に変換して実行するので、ここまで面倒ではありません。
しかし、あえてこのように何にも頼らずすべて手打ちで命令を記述していくことのよって、以下に普段の環境が優れている、また便利であるかということが実感できたのではないかと思います。

これを機に低レイヤの技術も勉強してみようと思った人がいれば幸いですし、そうでなくても普段使っている技術が数々の巨人たちの功績の上に成り立っているということを感じることができれば、少しはこの記事の意味もあったのかなと思います。