JavaScriptでVM作って「30日でできる!OS自作入門」をやってみた (2日目)

カーネル/VM Advent Calendar の40日目です。ごめんなさい1日遅れ。他の方のも面白いですよ!左のリンクからどうぞ。

私、JavaScriptVM(仮装機械)を色々作ってきましたが、いつも、言語処理系のVMばかりで、VMwareのような、マシン自体のVMはやったことがなかったので、トライしてみました。

選んだテーマは、川合秀実さんの5年前の名著、「30日でできる! OS自作入門」。これ、凄くいい本ですね!読んでみてびっくりしました。かなりお勧めです。英訳は出ていないと思いますが、英訳を出すに値する本だと思います。でも、川合さんのOSASKは終了(中断?)しちゃっているみたいですね。

30日でできる! OS自作入門

30日でできる! OS自作入門

どうやって、VMを作るか、何時間も悩みました。OS自作入門では、NASKという川合さん自作のアセンブラC言語で書かれています。C言語で書かれたのも一度NASKのアセンブラに変換されて、最終的にバイト列に変換されるので、NASKをターゲットにすることにしました。というわけで、やることは、CPUとBIOSの作成です。

インタープリタ方式にするか、コンパイラ方式にするかも悩んだのですが、とりあえず、コンパイラ方式にしました。NASK は LIST ファイルという、左側にバイト列があって、右側にニーモニックが出る、扱いやすい出力を作ってくれます。これを、Groovyで x86 -> JavaScriptコンパイラを作り、JavaScript に変換することにしました。本当はコンパイラJavaScript で書きたかったのですが、ごめんなさい、手抜きです。

やってみて思ったのですが、x86 のバイト列は、命令列とデータ列が混在になっていて、コンパイルがやっかいです。NASK のアセンブラなら、データ列の部分は DB などのデータ列を表現するニーモニックを使っているので、データ列を避けてコンパイルすることができます。今回はそれに逃げています。本当にコンパイルしようと思ったら、JavaScriptJITコンパイラを作り、x86 -> JavaScriptソースコードに変換して、それを eval() で読み込むロジックが必要そうです。データとプログラムが混在するバイト列の中で、実行しようとした番地はプログラムの始まりなので、そこからJITコンパイルしていけば良さそうです。ただし、プログラムの終わりがわからないので、適当なところで中断して、そこから先も実行しようとしたら、再度コンパイルすると言う処理が必要そうです。また、プログラムの自己書き換えとかがあると、やっかいかも。

OS自作入門は、1日目はビルド方法などなので、2日目から挑戦しました。2日目は、print "\n\nhello, world\n" を実行するだけのOSです。ちなみに、3日目から32ビットモードになり、VRAMの操作が必要で、ハードルがぐんと上がります。いつか機会があったら、3日目も挑戦したいです。

あと、もう一つ愚痴。x86 って命令数がなんか多いですね!Intel® 64 and IA-32 Architectures Software Developer Manuals | Intel® Software に命令セットのマニュアルがあり、なんか、わかりにくく、x86 -> JavaScript の変換はだいぶ想像で作ってしまいました。(追記:Resource & Design Center for Development with Intel に日本語マニュアルがあるんですね)

と言うわけで、作ったファイルやデモの一覧。

  • helloos.nas - これが、元のアセンブラです。川合さんのコメントが大量に入っていてわかりやすいです。一文字ずつ読み込んで、INT 0x10 を使ってビデオBIOSを呼び出します。末尾に、RESB 1469432 という命令が入っていて、これは、サイズを1.4MBのフロッピーのサイズに揃えるために入っているのですが、これがあると次のLISTファイルの生成でエラーになるのでコメントアウトしました。
  • helloos.lst - 付属の asm.bat を書き換え、nask.exe helloos.nas helloos.img helloos.lst で生成できます。左側に番地やバイト列がつきます。
  • Compiler.groovy - これが、lst -> JavaScriptコンパイラです。前半の方で lst を読み込んで、後半の方で JavaScript を生成しています。170行程度ですし、たぶん、簡単に読めると思います。実行すると、helloos.js を生成します。
  • videoBios.js - ビデオBIOSの部分です。id が stdOut のいうDIVタグに innerHTML で追加しています。
  • helloos.js - これが、コンパイル結果です。素直にコンパイルしています。末尾に、全てのバイト列を含めていて、これを、一文字ずつ print する時に読み込んでいます。

以上により作られるデモがこれです〜!ちゃんと表示された!

helloos.html