コンパイル用バッチファイル

まずC言語プログラミングの前に、Windows上でのプログラムのコンパイルと6502-COREへのロード用のファイルの作成を手軽に行えるように、1つだけのソースファイルのプログラムをコンパイルするためのバッチファイルを作成しましょう。

バッチファイルを「コンパイル用バッチファイル:cc65exec.bat」に紹介します。

PATH=C:\XENESIS\6502\CC65\bin;C:\XENESIS\SREC
cl65 -t none --cpu 65C02 -O %1.c -o %1.bin -m %1.map xcore.lib
srec_cat %1.bin -binary -offset 0x1000 -o %1.SR
copy %1.SR + STARTVEC.SREC %1.SREC
DEL %1.SR %1.bin

コンパイル用バッチファイル:cc65exec.bat

バッチファイルの各行は以下の様な処理を行います。

  • 1行目:コンパイラやツールの実行ファイルのパスを設定する
  • 2行目:バッチファイルの引数で指定したプログラムをコンパイルし、ライブラリとリンクしてバイナリファイルを作る
  • 3行目:バイナリファイルを6502-COREにロードするためのSレコード形式に変換する
  • 4行目:Sレコード形式に、プログラムの実行開始アドレスを自動設定するためのデータを追加する
  • 5行目:いらないファイルを削除する

バッチファイルの1行目は、ご自身がCC65やSレコードツールをインストールしたパスを反映させてください。

4行目のSTARTVEC.SRECは、プログラムを6502-CORE にロードした際に、プログラムの実行開始アドレスを Universal Monitorに自動設定し、Gコマンドで開始アドレスの指定を省略できるようにするものです。

STARTVEC.SRECデモパッケージに含まれていますが、作成法を\ref{c-unimon-go}節に示します。

以下にバッチファイルcc65exec.batの使用法を示します。

例えば、evalpr.cというファイルをコンパイルする場合には、拡張子のcを取って引数に指定して以下の様にバッチファイルを実行します。

% cc65exec evalpr

バッチファイルを利用したコンパイル

最初のCプログラム

お約束のHello Worldは端折らせていただいて、繰り返し処理により0から9までを出力するプログラムevalpr.cを図に示します。

#include <xcore.h>
void main()
{
    int i ;
    for (i = 0; i < 10; i++) {
        cprintf("i = %d\n", i) ; // printfの画面出力版
    }
    wexit() ; // プログラムの終了:モニタのウォームスタート
}

evalpr.c

C言語のプログラムでは、多くのプログラムでstdio.hがインクルードされていますが、6502-CORE にはstdio.hが取り扱う標準入出力やファイルIOの機能がないので、stdio.hがサポートする関数群を使うことができず、したがってstdio.hをインクルードしません。

画面に対する入出力機能がxcore.hに宣言されているのでstdio.hの代わりにxcore.hをインクルードしてください。

evalpr.c には以下の様なライブラリ関数が使用されています。

  • cprintf()は、画面出力専用のprintf()です。
  • wexit()は、プログラムを終了しUniversal Monitorをウォームスタートさせます。

プログラムを終了させ、Universal Monitorをコールドスタートさせるcexit()関数もあります。

コールドスタートすると、起動後の経過時間も含め、Universal Monitorの全ての設定が初期化されます。

evalpr.c は先に作成したコンパイル用のバッチファイルを使用して、図\ref{fig:c-cc65exec-do2}の様にcc65exec evalprと入力して簡単にコンパイルできます。

% cc65exec evalpr

バッチファイルによるevalpr.cのコンパイル

コンパイルによりSレコードファイルevalpr.SRECが生成されます。

図\ref{fig:ex-cdemo-1}に示すように、6502-COREにevalpr.SRECをロードし、Gコマンドで実行することができます。

evalpr.cの実行結果

コンパイラで生成されたコードのサイズも気になりますよね。

コンパイル処理では、ファイルの拡張子をmapとしたファイルが生成されており、その中の[Segment list]を見ると、生成されたコードサイズを確認することができます。

図\ref{fig:c-demo-test1-map}には、evalpr.cのコンパイルコードのアドレスマッピングやコードサイズが示されています。

STARTUP, ONCE, CODEのSizeの合計が、自分自身で書いたプログラムだけでなくくっついてきたライブラリも含めたプログラムのサイズです。

また、RODATA,DATA,BSSのSizeがデータのサイズです。

Segment list:
-------------
Name                   Start     End    Size  Align
----------------------------------------------------
ZEROPAGE              000080  000099  00001A  00001
STARTUP               001000  001016  000017  00001
ONCE                  001017  001022  00000C  00001
CODE                  001023  001725  000703  00001
RODATA                001726  0017D4  0000AF  00001
DATA                  0017D5  00180B  000037  00001
BSS                   00180C  001837  00002C  00001

生成コードのメモリマップ:evalpr.map

全部ひっくるめると2Kバイト程度ですね。こんな短いプログラムでTinyBasicが書けるサイズを必要とするの?と思いますよね。

mapファイルの中身をよく確認するとわかりますが、実際には、cprintf()関係の処理が1KB程度を占めています。

逆に言えば、cprintf()を使用しなければ、あっという間に1KB程度コードサイズは減少します。

その他にも、C言語の処理を行う各種のサブルーチンが多くのサイズを占めているため、思いのほかプログラムのサイズが大きくなっています。

Cのプログラムはプログラムが小さくても、様々なライブラリ関数を使用すると初期的なコードサイズが急激に増大しますが、その後はプログラムの行数が増えても、使用されるライブラリ関数が増えなければコードサイズの増加は緩やかです。

ライブラリ関数の追加使用

もう少し多くの入出力用の基本的なライブラリ関数を使用するevalprsc.cを図\ref{fig:c-demo-evalprsc}に示します。

#include <xcore.h>
void main()
{
    int i, j, n, m ;
    clrscr() ; // 画面の消去とカーソルを画面左上に移動
    cputs("Input Int Num: ") ; // 文字列を画面に出力
    cscanf("%d", &n) ; // scanfのキーボード入力版
    for (i = 0; i < n; i++) {
        cprintf("i = %d ", i) ;
        m = i % 70 ;
        for (j = 0; j < m; j++)
            cputc('*') ;
        cputc('\n') ; // 文字を画面に出力
    }
    wexit() ;
}

evalprsc.c

evalpr.c.c のプログラムのライブラリに加え、evalprsc.cでは以下の様なライブラリ関数が使用されています。

  • clrscr()は画面を消去し、カーソルを画面の左上に移動させます。
  • cputs()は、文字列をコンソールに出力します。
  • cscanf()は、コンソール入力専用のscanf()です。
  • cputc()は、文字をコンソールに出力します。

evalprsc.cもevalpr.c同様にバッチファイルでコンパイルしてevalprsc.SRECを生成し、6502-CORE にロードして実行できます。

図\ref{fig:ex-cdemo-2}に示すように、6502-COREにevalprsc.SRECをロードし、Gコマンドで実行することができます。

evalprsc.cの実行結果

evalprsc.cのmapファイルのSegment listを図\ref{fig:c-demo-evalprsc-map}に示します。

Segment list:
-------------
Name                   Start     End    Size  Align
----------------------------------------------------
ZEROPAGE              000080  000099  00001A  00001
STARTUP               001000  001016  000017  00001
ONCE                  001017  001022  00000C  00001
CODE                  001023  002219  0011F7  00001
RODATA                00221A  0022FA  0000E1  00001
DATA                  0022FB  002333  000039  00001
BSS                   002334  0023A6  000073  00001

生成コードのメモリマップ:evalprsc.c

今度は、全部ひっくるめると5Kバイト程度とかなり大きくなっています。

evalpr.cの時と同様にmapファイルを確認すると、cscanf()関係だけで2Kバイト以上のコードサイズを占めているようです。

オリジナルのUNIXでも同様ですが、printf系、scanf系のライブラリ関数は大メモリぐらいですね。