X-BASICメモ
初版 2024/05/27
改訂 2024/06/11
X68000でプログラムを開発する手段はいくつかあるが、ここではそのうち最も簡単に開発できる「X-BASIC」を使うための情報を記載する。
マニュアルに記載されている基本的な内容を把握していることを前提としているため、詳細についてはマニュアルを参照すること。
X-BASICの利点
-
標準で付いてくる。
-
豊富なハードウェア資源を一通り使える関数が用意されている。
-
インタプリタ形式のため、手軽にプログラムの実行確認ができる。
-
エディタでプログラムを書ける。
-
別売りのCコンパイラに同梱されているBASICコンパイラ(BASTOC)を使うことで、コマンドシェルから直接実行可能なファイルを作成できる。
-
ユーザー独自で外部関数を作成し、BASICに組み込むことが出来る。
X-BASICの制約
- 従来のMicrosoft系BASICとは大きく構文などが違うので、慣れが必要。
C言語をやっていたら入りやすいかも。
- BASIC環境では行番号がついており、プログラムを作成しにくい。
これについては、行番号なしの形式でプログラムをロード・セーブできる命令が用意されており、この形式で保存したプログラムはテキストエディタで編集可能となる(後述「テキストエディタでプログラムを書く場合の制約事項」参照)。 また、
renum
命令はgoto
、gosub
で指定する行番号までは変更されず、ラベルを使うこともできないため、行番号指定のジャンプは極力避けた方が無難。 - インタプリタであるため、実行速度が遅い。
特にMSX等と同じく、割り込みで制御を行う命令がないため、BGとスプライトの制御、処理ロジックを入れると目に見えて遅くなる。16ビット機とは言え、凝ったものが作れないのは残念。
- 変数の定義が省略できない。
これは、後にC言語への変換を可能とするための制約と思われる。
- 直接メモリにアクセスする手段(従来のPOKE、PEEK、CALLなどの命令)がなく、マシン語を使用できない。
これに対する解消方法としては、処理をユーザー外部関数として別途作成して使うという方法があるが、BASICのシステムとして取り込まれるため、注意が必要である。
また、ユーザー定義関数はアセンブラで作成するには難易度が高く、Cで作成するにしてもCコンパイラが別売りのため、ハードルが高い。 - 定数、構造体が使えない。
定数は変数名で便宜的な定義ができるが、構造体が使えないのは惜しいところ。
- 関数定義で、引数の省略や配列が指定できない。
引数の省略が指定できないのはまだ関数の設計で回避できるが、せめて配列は指定できるようにして欲しかった。(Cではポインタ渡しで可能)
- 配列の要素数を調べる命令がない。
C言語で言う
sizeof
的なものがない。そのため、終端データを捜査して要素数を調べるしかない。
X-BASIC起動時の画面表示文字数を変更する
デフォルトでは横64文字(画面モード512x512)で起動するようになっており、filesの結果などが見にくい。
以下のようにすることで、横96文字(画面モード768x512)で起動できる。
basic -w96
また、BASIC.CNF
でWIDTHの行を以下の内容に更新することで、常に上記の状態で起動できる。
...
WIDTH = 96
...
テキストエディタでプログラムを書く場合の制約事項
前述のとおり、BASICプログラムもテキストエディタで書くことが可能であるが、以下の制約がある点に注意する。
-
文字コードはShift_JIS、改行コードはCRLFで作成する。
-
「行番号なし」の形式で書くため、行の先頭を数字にすると行番号とみなされ、basicからload@できなくなる。例えば、以下のように書くと「行番号があるファイルです」のエラーがでる。
dim char c(10) c ={ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 }
この場合、以下のように行の先頭を「,」などにし、数字以外になるように修正する。
dim char c(10) c ={10, 20, 30, 40, 50 ,60, 70, 80, 90, 100 }
行番号を付けて書いても良いが、エディタを使う意味がなくなる。
-
1行255文字以内とすること。
-
プログラムの途中で空行を入れることはできない。
-
ソースの最後は&H0Aでないとならない。最終行は空行とすること。
変数の定義について
-
使用する変数は必ず定義が必要。
- 変数には、グローバル変数とローカル変数の2種類がある。
- グローバル変数:関数の外で定義された変数で、プログラム全体からアクセスが可能。
- ローカル変数:関数の中で定義された変数で、関数の中でのみアクセス可能。
- 変数はローカル変数→グローバル変数の順に参照され、同名の変数が定義された場合はローカル変数が操作対象になる。そのため、グローバル変数とローカル変数は同名でもエラーにならないが、グローバル変数を明示的に指定する手段がないため、この場合はローカル変数しか操作できなくなる。
例)
100 int x = 10
110 print update(x)
120 print x /* ここで表示されるのはグローバル変数の値(元の値から変わらない)
130 end
140 func update(x;int) /* このxはローカル変数として扱われる
150 x = 20
160 print x /* ここで表示されるのはローカル変数の値
170 endfunc
- 配列変数はマニュアルにもあるが、グローバル変数として定義する。
実際は定義もできるが、関数の引数・戻り値に配列の型を指定できないことと、関数の処理が終了すると定義した配列が破棄されるため、ほとんど意味がない。
char型
- 0~255(&H00~&HFF)までの整数を扱う。マイナス値は利用不可。
- 1バイトデータを扱う際に便利。
int型
- -2,147,483,648~2,147,483,647(&H00000000~&HFFFFFFFF、&HFFFFFFFFが-1)までの値を扱う。
- 小数桁は切り捨てられる。
- 型の指定を省略した場合は、暗黙的にint型として扱われる。
float型
- 実数(int型の範囲を超える数値、14桁以内の数値と”E”を使った指数表示の組み合わせ、ピリオドを含む値、末尾に”#”を付けた数値)を扱う。
- 式の記述に注意。記述方法により、以下の結果になる。
例)
PRINT 1/3 ←int型として処理される
0
Ok
PRINT 1/3.0 ←float型として処理される
0.33333333333333
Ok
PRINT 1/3# ←float型として処理される
0.33333333333333
Ok
str型
- 定義時に型を指定するため、他のBASICと違い、変数名に”$”を付ける必要はない(付けても良い)。
- str型の変数の末尾は’&H00’が設定される。
- 桁数を指定せずに宣言すると、32文字までしか格納できないが、代入時にはエラーにならず、余剰分は無視される。
例)
str str1 = "SHARP PERSONAL WORKSTATION X68000"
Ok
print str1
SHARP PERSONAL WORKSTATION X6800
Ok
- 32文字以上の文字列を定義する際は、変数名の後に
[ ]
で括り、格納する桁数を指定する。最大で255まで指定可能。
例)
str str2[255] = "SHARP PERSONAL WORKSTATION X68000"
Ok
print str2
SHARP PERSONAL WORKSTATION X68000
Ok
- 非公式(マニュアル未記載)であるが、文字列変数は指定桁の値を直接操作可能。
ただし、char型の配列を文字列として扱うことはできない。
例)
str str3 = "ABCDE"
Ok
print str3[4]
69
Ok
print chr$(str3[4])
E
Ok
str3[4] = 68
Ok
print str3
ABCDD
Ok
- なお、X-BASICで動作させるプログラムでは、関数の戻り値としてstr型を指定可能だが、BAStoCを使用する場合はエラーとなるため、なるべくグローバル変数として定義する。
型のキャストについて
- 代入時に暗黙的にキャストされるが、値により以下の制限がある。
移送元 | 移送先 | 結果 |
---|---|---|
char | int | OK |
float | OK | |
int | char | 条件付きOK ※0~255、-1~-128ならOK、以外はNG |
float | OK | |
float | char | 条件付きOK ※0~255、-1~-128ならOK、以外はNG |
int | 条件付きOK ※小数部切捨て、-2,147,483,648~2,147,483,647を超えるとNG |
- なお、int型へのキャストは
int()
関数が用意されているため、こちらを利用することでも可能。
配列変数の定義について
- 配列変数は
dim
文で要素数を指定することで定義できる。
要素番号は0から始まるため、dim
文で指定した値+1のサイズが確保されるため、初期値を設定する際は留意する。
例)
100 dim int ary(5) /* 要素0~5の配列を定義
110 int i
120 ary = {1, 2, 3, 4, 5, 6}
130 for i = 0 to 5
140 print ary(i);
150 next
- X-BASICでは多次元配列を定義できる。
値の初期値も設定できるが、直列で記載することになるため、要素番号との対応に注意する。
例)
100 dim ary2(1, 2) /* (0~1,0~2)の2次元配列を定義
110 int x, y
120 ary2 = {1, 2, 3, /* (0,0)~(0,2)
130 4, 5, 6} /* (1,0)~(1,2)
140 for y = 0 to 1
150 for x = 0 to 2
150 print ary2(y, x);
170 next
180 print
190 next
run
1 2 3
4 5 6
Ok
- 但し、多次元配列を1次元配列と同様の方法での参照はできない。
例)
print ary2(4)
添え字の値が異常です
print ary2(4)
^
Ok
print ary2(1)
配列の添字が足りません
print ary2(1)
^
Ok
関数の定義について
- 関数は以下のように定義する。
func [<戻り値の型>] <関数名>(<引数の変数名1>[;<型>], <引数の変数名2>[:<型>], ...) <型> <ローカル変数名> ... return(<戻り値>) endfunc
- 引数は値渡しのみ。ただし、グローバル変数は関数内でも操作できる。
- 引数・戻り値に配列変数は指定不可。
- 省略可能な引数の指定は不可。
- 引数の変数の型、戻り値の型を省略した場合は、int型として処理される。
付属のスプライトエディタ(DEFSPTOOL.BAS)について
Human68kシステムディスクの\etc
ディレクトリには、BASICで書かれたスプライトエディタであるdefsptool.bas
が収録されている。
起動は、コマンドラインから
A>basic \etc\defsptool.bas
とするか、basicから
run "\etc\defsptool.bas"
とする。
-
このエディタでは、8x8ドットで512パターン、16x16ドットで128パターンの定義が可能だが、セーブしたデータを加工することでプログラム上ではこれ以上の定義も可能。
- 16x16ドット単位での編集となるが、8x8ドット単位でのパターン番号の対応は、以下の通りとなる。(n=16x16ドット時のパターン番号×4)
+-----+-----+ | | | | n | n+2 | | | | +-----+-----+ | | | | n+1 | n+3 | | | | +-----+-----+
- BGパターンとして指定できるのは、0~255までの256パターンのため、上半分を使用すると良い。
実際は使用するBG面の数により変動する。この場合は自前でパターン番号を指定した定義を行う必要がある。
- 付属のスプライトエディタにはロード機能がないが、起動時にスプライト・パレットを初期化しないため、データが定義された状態で起動すると、その時点のVRAMのPCGエリアの内容が表示され、ロードと同じ扱いになる。
BGデータエリアの部分は表示されない。
- セーブ時は、行番号付きのbasicプログラムの形式になるため、load@でマージができない。save@しても配列データが行番号とみなされてしまうため、ロードができなくなる。
この対処として、配列データの先頭に「,」を付ける修正を行う必要がある。
または、sp_def関数の代わりに配列の内容をファイルに出力する関数を作成し差し替え、プログラム側ではファイルから読込み定義するようにする方法もある。 - なお、全て16.x16ドットの定義(
sp_pat
のパターンサイズ=1)で出力される。256x256ドットの画面モードではBG用として8x8ドットのパターンが必要となるが、bg_put
する際のパターン番号を考慮してパターンデータを作成することで、そのまま使用可能である。
FM音源の使用について
- X68000はFM音源(YM2151)が内蔵されており、X-BASICからも利用するための命令が用意されている。
- 使用するためには、事前にOS(Command.x)に音源ドライバ(OPMDRV2/3, Z-MUSICなど)が登録されていることと、BASIC.CNFにドライバに対応する外部関数が設定されていることが前提となる。
- 例えば、
OPMDRV3.X
での基本的な利用手順は以下となる。m_init
でドライバ初期化m_alloc
でメモリ確保(全体のバッファサイズはOPMDRV組み込み時に指定、デフォルトは64KB)m_trk
でトラックデータ設定(≠チャンネルとは別)m_assign
でチャンネル(1~8)にトラックデータを関連付けm_play
で指定トラック or 全トラック演奏
例)
100 m_init() 110 m_alloc(1, 1000) 120 m_alloc(2, 1000) 130 m_alloc(3, 1000) 140 m_trk(1, "t160@1o4v13l4cdefgab<c") 150 m_trk(2, "t160@21o5v13l4c<bagfedc") 160 m_trk(3, "t160@10o2v13l8ccc<c>ccc<c>") 170 m_assign(1, 1) 180 m_assign(2, 2) 190 m_assign(3, 3) 200 m_play()
- 注意事項
m_alloc
は実行時にトラック全体の初期化が行われるようで、m_trk
実行後にm_alloc
を実行すると、それ以前のトラックデータが消去される。
例)
100 m_init() 110 m_alloc(1, 1000) 120 m_trk(1, "t160@1o4v13l4cdefgab<c") 130 m_alloc(2, 1000) 140 m_trk(2, "t160@21o5v13l4c<bagfedc") 150 m_alloc(3, 1000) 160 m_trk(3, "t160@10o2v13l8ccc<c>ccc<c>") 170 m_assign(1, 1) 180 m_assign(2, 2) 190 m_assign(3, 3) 200 m_play() /* トラック3しか演奏されない
- m_assign でプログラム実行中にトラックの指定を変更できるが、m_stopしてからの方が良い。
その他あれこれ
コンパイルしたプログラムの画面初期化などをやめる
- BAStoCで生成されたCプログラムからコンパイルすると、X-BASIC起動時と同様に画面初期化が自動で行われるが、以下修正を行いコンパイルすることで、画面初期化をせずに実行するプログラムが作成できる。
b_init();
を削除する。b_exit(0);
をexit(0);
に変更する。