少ないリソースを酷使する

低レイヤーとレトロPC(PC98,MSX)が好きな情報学生

MS-DOS(PC-98)のアプリケーションをC言語で作る

前回の記事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環境特化.
詳しい話は以下の記事によくまとめられています!

ということでCコンパイラgcc-ia16を使いましょう!

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.comPC-98エミュレータ上で実行してみる.
f:id:T-takeda:20201027224008p:plain

やったね!

気をつけるべきこと(メモリーモデル)

プログラムサイズがデカくなると以下のエラーが出るかもしれない.

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で動かしてみたがしっかり動作した!
すごい!!
f:id:T-takeda:20201027230600j:plain f:id:T-takeda:20201027230608j:plain

まとめ

コンパイラの使い方に困ったら.

$ ia16-elf-gcc --help
$ ia16-elf-gcc --target-help
$ man ia16-elf-gcc

あたりを見てみよう.
次は機会があれば,C言語DOS(PC-98)のアプリケーションをいろいろ作ってみたいと思いますー
それでは!