前回の記事でPC-98エミュ+FreeDOSの環境でMS-DOSアプリが動くことを確認した.
となると,あと思うことは1つ.
MS-DOSのアプリケーションを自作したい!
ということです.
今回はC言語でMS-DOSのアプリケーションを作ってみましょう.
環境
OS:Ubuntu 20.04
PC-98エミュ:xnp2kai rev.22 d3fe02a
FreeDOS(98):20200709更新のHDI形式
Cコンパイラのinstall
i8086版GCC
フリーのCコンパイラと言えばGCC.便利ですよね.
しかし,少し前までGCCは32bit || 64bitのアーキテクチャしかターゲットにしてきませんでした.
なので16bitのPC-98x1向けのバイナリは生成できなかったわけなんですよね.
それが最近,ついに16ビット版GCCが登場しました.しかもDOS環境特化.
詳しい話は以下の記事によくまとめられています!
gcc-ia16 を install
嬉しいことにaptでインストールできる.
https://launchpad.net/~tkchia/+archive/ubuntu/build-ia16/
$ sudo add-apt-repository ppa:tkchia/build-ia16 $ sudo apt-get update $ sudo apt-get install gcc-ia16-elf
Hello world!
以下のhello.c
をコンパイル→実行してみる.
#include <stdio.h> int main(void) { printf("Hello world!\n"); return 0; }
コンパイルする.
-march
オプションでターゲットマシンのCPUアーキテクチャを指定する.
$ ia16-elf-gcc -march=i286 -o hello.com hello.c
ちゃんとMS-DOSの実行ファイルになっているか確認しよう.
$ file hello.com hello.com: COM executable for DOS
hello.com
をPC-98エミュレータ上で実行してみる.
やったね!
気をつけるべきこと(メモリーモデル)
プログラムサイズがデカくなると以下のエラーが出るかもしれない.
Error: too large for a .com file.
これは,COM実行ファイルが64KB以下でないといけない仕様のため.
参考wikipedia: https://ja.wikipedia.org/wiki/COM%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB
これは-mcmodel
を調整することで解決できる.
コンパイルできない場合オプションの値をtiny
small
medium
と上げていこう.
tiny
で生成されるのがCOMファイル.small
medium
で生成されるのがEXEファイルなので,-o
オプションでの拡張子名には気をつけよう.
オプションの値の説明は以下の通りだ.
-mcmodel=medium Generate code for the medium code model, with one data segment and multiple text segments. -mcmodel=small Generate code for the small code model, with one data and one text segment. -mcmodel=tiny Generate code for the tiny code model (default), with one combined data and text segment.
なぜこの設定が必要なのかはx86 16ビットモード特有のメモリーモデルの話になるのだが...今回は割愛する.
以下のリンクを参照されたし.
https://www.wdic.org/w/TECH/%E3%83%A1%E3%83%A2%E3%83%AA%E3%83%BC%E3%83%A2%E3%83%87%E3%83%AB
アセンブル出力を見てみる
動きが怪しいなと思ったり,デバッグをしたいときはアセンブル出力を見てみよう.
$ ia16-elf-gcc -march=i286 -S hello.c $ cat hello.s
.arch i286,jumps .code16 .att_syntax prefix #NO_APP .section .rodata .LC0: .string "Hello world!" .text .global main .type main, @function main: pushw %bp movw %sp, %bp pushw $.LC0 pushw %ss popw %ds call puts addw $2, %sp movw $0, %ax movw %ax, %ax movw %ax, %ax leavew pushw %ss popw %ds ret .size main, .-main .ident "GCC: (GNU) 6.3.0"
うん,ちゃんとintel 80286向けのハロワアセンブラが生成されている.
ちなみにアセンブラの記法はAT&T記法.
Intel記法のアセンブラが見たい場合オプションに-masm=intel
を加えるといい.
だが,Intel記法の出力は実験的機能らしいので正確性は注意されたし.
よくわからんところ
ia16-elf-g++でC++コードもコンパイルできるようだが,なかなかうまくいかない.
具体的には,以下のコードのコンパイルにまだ1度も成功していない.
#include <iostream> int main(int argc, char *argv[]) { std::cout << "Hello World!" << std::endl; return 0; }
基本的に以下の記事と全く同じような状況にハマってしまっている.
https://flaterco.com/kb/gcc-i16.html
まだC++でのMS-DOSアプリの開発は無理なんだろうか...
おまけ(実機テスト)
PC-9801DX実機+MS-DOSで動かしてみたがしっかり動作した!
すごい!!
まとめ
コンパイラの使い方に困ったら.
$ ia16-elf-gcc --help $ ia16-elf-gcc --target-help $ man ia16-elf-gcc
あたりを見てみよう.
次は機会があれば,C言語でDOS(PC-98)のアプリケーションをいろいろ作ってみたいと思いますー
それでは!