最近 IchigoJam がきっかけで C 言語に触れていました。
API(BIOS など)とライブラリが整っていれば、
BASIC に近いソースで記載できます。
それでマシン語で実行できてしまうのです。

その時新たに知ったツールがありました。z88dk です。

BASIC と C 言語

IchigoJam 版 風船

今や IchigoJam での代表作となっている「風船」🎈😍←風船大好き
元々はたった 6 行(コメント入れて 7 行)の BASIC プログラムです。

5 'フウセン
10 CLS:CLV:CLP
20 X=RND(31)
30 LOCATE X,23:PRINT CHR$(232)
40 WAIT 7
50 LOCATE X,23:PRINT CHR$(242);
60 GOTO 20

最近 IchigoJam BASIC にマシン語から操作しやすいよう API が追加されました。
それに合わせて開発者の福野さんが紹介していたのが、
C 言語を使える c4ij でした。

そこで「風船」を C 言語にしたんですね。こんな感じです。

#include <std15.h>


__attribute__ ((section(".main")))
int main(int param, int ram, int rom, int (*divfunc)()) {
    cls();

    for (;;) {
        int x = rnd(31);

        locate(x, 23);
        putc(232);
        putc(10);

        if( inkey() != -1 ) break;

        wait(7);

        locate(x, 23);
        putc(242);
    }

    return 0;
}

上にスクロールさせるため、改行コード 10 を出力させています。
マシン語では止める処理も考慮する必要があるので、
inkey() で押されたのを確認し、break でループから脱出しています。

あとは BASIC と見比べて理解できるでしょう。
API やライブラリ次第では、BASIC と変わらない記載ができて、
これでマシン語コードが生成されてしまうのです。
改めて C 言語の素晴らしさを今になって実感する事になります。

Z80 を使う様々な環境向けの開発ツール z88dk

それから間もなく知ったのが z88dk でした。

C 言語やアセンブラをクロスコンパイルできる開発環境は出てきていましたが、
z88dk は Z80 に対応するプラットフォームに対応し、
フラットフォーム向けの実行ファイルを出力してくれます。

しかも z88dk では ANSI C にほぼ対応してあり、テキストはもちろん、
モノクロではありますが、共通のグラフィック機能も備えていて、
一つのソースから複数のプラットファーム向けにビルドできます。

実際に作ってみました。

#include <graphics.h>

#include <math.h>

#include <stdio.h>


void main() {
  int x, y;
  clg();

  for (x=0; x<=getmaxx(); x+=2) {
    plot(x, getmaxy() / 2);
  }

  for (x=0; x<=getmaxx(); x++) {
    y = (int)((getmaxy() / 2) * (1.0 - sin(M_PI * 2.0 * x / getmaxx())));
    if (x==0){
      plot(x, y);
    } else {
      drawto(x, y);
    }
  }

  while (getk() < 1){}
}

サインカーブを表示します。これでソースのすべてですよ。シンプルですね。

まず MSX 向けにビルドします。この場合は +msx を付けます。
また、最近の Nighty Build では MSX と他のプラットフォームが共通化された影響で
-lmsxbios をオプションに追加する事が推奨されます。
更に float などの浮動小数点数型を使う場合は -lm を追加します。
-lndos はディスクアクセスや MSX-DOS ファンクションを使用していない場合、
使用している場合は外して下さい。
-subtype=disk を入れると sin.msx というファイルができ、
BASIC から BLOAD"SIN.MSX”,R で実行します。
この subtype を外すと sin.cas ができます。カセットイメージです。
BASIC から BLOAD"CAS:",R で実行できます。MSX1 でも実行できます。
-subtype=msxdos または -subtype=msxdos2 で、
MSX-DOS 向けの実行ファイル .COM も生成できます。
更に -subtype=rom にするとロムカートリッジのイメージにもできます。
z88dk はすぐに MSX で実行できる複数のファイル形式を直接出力してくれるのです。
-create-app は常に付けておくものだと思って下さい。

zcc +msx -lm -lndos -lmsxbios -create-app -subtype=disk -bnsin sin.c

MSX の公式エミュレータ MSXPLAYer を使って表示しました。

MSXPLAYer での表示

続いてシャープのポケコン PC-G850 シリーズ向けにビルドします。
この場合は +g800 を付け、-clib=g850 も加えます。
(本当は -clib=g850b 推奨なのですが、記載時不具合がありまして……)

zcc +g800 -lm -create-app -clib=g850 -o sin.ihx sin.c

sin.ihx ファイルが生成されます。これが Hex ファイルです。
これを PC-E200・G シリーズのエミュレータ g800 で表示させます。

g800 -machine=g850 sin.ihx 100

g800 での表示

C 言語のソースは全く一緒なのに、MSX・ポケコンで表示できました。
画面サイズに合わせて大きさも変化しています。
他の Z80 を使用する機種でも同じように動作できます。
これが z88dk の大きな特徴です。

z88dk ではじめて作ったもの

z88dk ではじめて作ったのは……なんと MSX2+ の自然画モード YJK の表示でした。

MSX2+ YJK

MSX2+ ではこんな画像表示ができるようになっていました。

100 DEFINT A-Z
110 C=15*8:'Y*8
120 SCREEN 12
130 COLOR 255,0,0
140 FOR J=-32 TO 31
150 X=128+J*256/64
160 LINE(X+2,0)-(X+2,211),C+(J AND 7)
170 LINE(X+3,0)-(X+3,211),C+(J AND 58)/8
180 NEXT J
190 FOR K=-32 TO 31
200 Y=106+K*212/64
210 FOR X=0 TO 255 STEP 4
220 LINE(X  ,Y)-(X  ,Y+3),C+(K AND 7)
230 LINE(X+1,Y)-(X+1,Y+3),C+(K AND 56)/8
240 NEXT X
250 NEXT K
260 GOTO 260

BASIC でも LINE を使ったりして少しでも高速にしようとしたのですが、
色数が多い分、変化も大きく、
ドット単位の線画が必要だったため、時間を要するんですね。
ならば C 言語で作ってみようと。

しかし、z88dk には VDP 関連は MSX1 向けのライブラリしかありません。
でもなければ作れば良いんですよね。
使用するファンクションコール(BIOS)を関数で記載すれば良いんです。
さすがにここはアセンブラレベル。とはいっても数行で実現できます。

int gttrig(char num)
{
#asm

    ld   a,l            ; num
    call 00d8h          ; GTTRIG
    ld   l,a
    ld   h,0
#endasm

}

引き渡される値は HL(複数ある場合はスタックに積まれるので SP+n)で取り出し、
必要なレジスタに入れ替えて CALL、返し値は HL に入れ直す。これだけです。
Z80 をある程度かじっているのであれば、ネットで調べながらでも作れます。

void main()
{
    int c=15;

    chgmdp(8);
    screen12();

    chgclr(255, 0, 0);

    c=c<<3;

    nstwrt(0);
    for (int y=0; y<212; y++) {
        for (int x=0; x<256; x+=4) {
            int j=x*64/256-32;
            int k=y*64/212-32;

            out(0x98, c|k&7);
            out(0x98, c|k>>3&7);
            out(0x98, c|j&7);
            out(0x98, c|j>>3&7);
        }
    }
    while (! gttrig(0));
}

メインルーチンの中身はたったこれだけ。左上からドットで線画しているのですが、
これでも BASIC とは比べ物にならない高速表示になりました。

マシン語実行なので、ファンクションコール(BIOS)を直接実行できる、
でも BASIC のようにソースが作りやすい。
マシン語と BASIC の良いとこ取りなのが C 言語だと改めて実感できたのでした。


MSX でも C 言語で「風船」を作りました。

MSX 版 風船 version 2

文字単位での表示は BASIC で作り、そこから C 言語で同じ動作をするものにして、
更に縦 192→212 ドット、MSX2 のハードウェアスクロール機能を使い、
スムーズなスクロールを実現して、「風船 version 2」としました。
この画面を見てたら、MSX-DOS の実行ファイルにすれば
スクリーンセーバで使ってもらえそうなので、実行ファイルも作りました。

MSX 実機に触れていた当時に
このような風船が飛んでいくプログラムを作りたかったのです。
でも作れなかったんですよね。
それからどれほどの年数が経過したでしょうか……ついに作る事ができました。


最新の Night Build を使っていたのですが、
ネット上でサンプルが出ていた msx/gfx.h 関連の動作に問題が発生しています。
MSX と共通しているプラットフォームをまとめているようで、
その影響で MSX のグラフィック・スプライト関連で
いくつか問題が発生しているようです。
examples/msx 関連でも公開時現在いくつかは動作に問題があります。
MSX1 向けで z88dk を使用しようと思った場合はご注意下さい。
いくつか issue を出しているので、そのうち解決できるかもしれません。\

自分は MSX1 向けでも上に記載している
ファンクションコール(BIOS)を直接実行する方法で作っています。

MSX 関連で z88dk 共通のモノクログラフィック・モノクロスプライトは
この記事を公開する数日前に正常動作できるようになったばかりだった事を確認しています。


追加 2019/07/07

サインカーブは GitHub Gist に公開しました。

https://gist.github.com/fu-sen/4e079229d43a0aa6c9d40734c35fb2ab

こちらでは確認できているプラットフォームでの zcc オプションと実行方法を
ソースのコメントに入れてあります。いくつか動作画像も入れました。
他のプラットフォームで確認できた zcc オプションと動作画像を
コメントに入れていただければ幸いです。