MachiKania ver 1.00 の紹介
2016年2月6日
ケンケンさんと共同で開発したBASIC搭載カラーゲームシステム、マチカニア(MachiKania)が、公開になりました。こちらでも、紹介させて頂きます。
マチカニアは、ビデオ端子のあるテレビと、PS/2キーボードをつなぐことで、それ単体でBASICプログラムを作成したり、色々なテレビゲームを楽しんだりすることの出来る、小型コンピューターシステムです。
私が製作した物は、6.5 cm x 9.5 cm x 4 cmほどの小さな筐体に収まっています。
使い方やソフトのダウンロードなどはケンケンさんが公開なさっているwebページに譲るとして、ここでは、私が担当したBASIC コンパイラー KM-1120 (KM-BASIC)の技術的なことに関して述べます。
概要
KM-BASICは、SD-カードに保存されているソースコードを元に、バイナリーをPIC32MXのRAM上に構築して実行する、コンパイラーです。FOR-NEXT, GOTO, IF-THENステートメントや、四則演算など、基本的な機能はすべてRAM上のバイナリーとして構築されて実行されるので、非常に高速です。PRINT文など、一部の機能の実行にはMachiKania上のライブラリーを用いて実行されるので若干速度が落ちますが、それでも通常のBASICインタープリターと比べて、ソースコードの認識は必要がない分、高速です。
現在のKM-BASICのバージョンでは、変数として32ビット整数型と、文字列型の二つが扱えます。また、32ビット整数型は、一次元配列として使用することも可能です。文字列型の使用は、かなりフレキシブルに行なうことが出来て、BASICをよくご存じの方は、さほど違和感なく使えると思います。一点だけ通常のBASICと異なるのは、文字列同士の一致判定に「=」演算子を使うことが出来なくて、STRNCMP()関数を使わないといけないことでしょうか。また、MID$()様の動作は、「A$()」の様に記述する事も、特徴です。この仕様は、APPLE I用の BASICを参考にしました。
レジスター使用規約
ここからは、コンパイラーの細かな仕様などについてメモ書きを残しておきます。色々ややこしい話なので、MachiKaniaを純粋に楽しみたい方には余り関係のない話であり、自分用のメモのような物です。
MIPS M4Kのレジスターの利用方法については、Cコンパイラーに於ける仕様規則に準拠していて、次のように使用しています。
・ $zero: zero (常に0)
・ $at 使用しない
・ $v0: 関数戻り値もしくは直前の演算結果
・ $v1: 演算用($v0=$v1*$v0の様に使う)
・ $a0-$a2: ライブラリー呼び出しの際の引数
・ $a3: ライブラリーの関数指定用
・ $t0-$t7: テンポラリーレジスターとして使用
・ $s0-$s5: 未使用。
・ $s6: 行番号格納。
・ $s7: call_library()関数へのポインター
・ $t8-$t9: テンポラリーレジスターとして使用
・ $k0-$k1: 使用しない
・ $gp: 使用しない(C コンパイラー側で使用)
・ $sp: スタックポインター
・ $fp($s8): 変数のアクセスに使用。
・ $ra: リターンアドレス格納
演算は、$v0と$v1の間で行ないます。演算子の優先順位を考慮した演算ルーチンのアルゴリズム等については、以前の記事で述べました。
バイナリーの構造
次に、バイナリーがどの様なコードになっているか、紹介します。次のBASICソースコードの例について述べます。
1C04-1C08が「I=1」に、1C0C-1C18が「100000」に、1C1C-1C24が省略されている「STEP 1」に相当します。1C50-1C58が「NEXT」です。「I」が100000に達したかどうかを判定するコードが、1C58と1C28-1C2Cに、「I」に1を足すコードが、1C30と1C3C-1C44にあります。こんな感じのコードを作成するのが、KM-BASICです。FOR-NEXTの処理部分のソースコードは、次の通りです。
なお、バイナリー中で2カ所、"ori s6,zero,0x1" "ori s6,zero,0x2"とあるのは、それぞれ、ソースコードの1行目、2行目のバイナリーコードの始まり部分であることを示します。BASICコードの実行中に問題があって停止した場合、どの行に相当する部分で停止したかが分かるようになっています。なお、$s6レジスターの最上位ビットは、文字列処理用の一時領域を破棄するタイミングを計るのにも、用いられています。
スタック
上記FOR-NEXTのバイナリーコードで、スタック(spレジスター)が用いられています。スタックを多用しているのがKM-BASICの特徴で、これはMZ-80K用のKM-BASICと共通の仕様です。この特徴から、FOR-NEXT文の途中でRETURNしたり、GOTO文で抜け出したりすることは出来ません。ただし、GOSUB文でサブルーチンを呼び出すことは出来ます。スタックは他に、演算処理部分でも用いられています。他方、Cコンパイラーではローカル変数の格納先としてスタックが用いられていますが、現在のバージョンのKM-BASICでは、ローカル変数という概念がありません。これについては、後で言及します。
ライブラリー
バイナリーコードを簡単に作成できない複雑な動作をする部分に関しては、MachiKania BASIC system上にCの関数として書かれたコードを呼び出すことで、対応しています。先に、レジスターの使用規則はCでのそれに準拠していると述べましたが、準拠しているのは、このようにBASICのコードからCの関数を呼び出して使うのが、目的です。なお、s7レジスターに、このための関数「call_library()」へのポインターを格納しており、これを用いています。どのライブラリー関数を用いるかの情報はa3レジスターに格納し、関数に渡す引数は、a0, a1, v0の3つのレジスターに格納することで行なわれます。
リンク
KM-BASICは、1パスコンパイラーで、リンク処理がコンパイルの後に行なわます。処理しているのは、GOTO, GOSUB, SOUNDの3つのステートメントです。GOTO文の場合、コンパイル時に書き込まれるのは、CPUのジャンプ命令ではなく、0x0810xxxx, 0x0811yyyyの2つのワードです。xxxxとyyyyの部分はジャンプ先を示しており、リンカーはこの2つのワードを認識すると、ジャンプ先アドレスを調べて(これを調べるのに、上で述べたs6レジスターに代入するコード "ori s6,zero,xxxx" を使います)、そのアドレスにジャンプするコードに書き換えます。GOSUB, SOUNDも、それぞれ0x0812xxxx + 0x0813yyyy, 0x0814xxxx + 0x0815yyyyと書かれているので、リンカーが同様に書き換えます。MIPSの様なRISC CPUのマシン語は、x86などと異なり、命令長が綺麗にそろっているのが特徴で、バイナリーに於けるこういった処理がやりやすくなっています。
今後の発展
次のメジャーアップデートでは、以下のような機能拡張を考えています。
・浮動小数点演算
現在のバージョンでは、数の計算は整数型のみですが、浮動小数点演算をサポートしたいと考えています。どの様な形で整数型と浮動小数点型を共存させるかは、現在考慮中です。
・サブルーチン内に於けるローカル変数
現在のバージョンでは、グローバルな変数が26個使えるだけですが、サブルーチン内でローカルな変数を使えるようにする予定です。サブルーチン内で、JavaScriptの様にVARステートメントでローカル変数が指定できるようにするつもりです。これにより、メインルーチンとサブルーチンで、処理を明確に分けることが出来るようになります。これの実装には、当然ながらスタックを用いますが、スタックは指定したローカル変数に対応するグローバル変数を待避しておくために使われ、RETURN時に、待避していた値が指定した変数に戻されます。
・GOSUB()関数で、引数の引き渡しをサポート。
GOSUB()関数に於いて、Cの関数のように引数を持たせることが出来るようにする予定です。これにより、上のローカル変数の実装と合わせて、大規模なプログラムを書くことがより容易になるはずです。
・その他
ケンケンさんと、色々話し合っています。もしかしたら、フルグラフィックとか、使えるようになるかも知れません。
リファレンス
マチカニアは、ビデオ端子のあるテレビと、PS/2キーボードをつなぐことで、それ単体でBASICプログラムを作成したり、色々なテレビゲームを楽しんだりすることの出来る、小型コンピューターシステムです。
私が製作した物は、6.5 cm x 9.5 cm x 4 cmほどの小さな筐体に収まっています。
使い方やソフトのダウンロードなどはケンケンさんが公開なさっているwebページに譲るとして、ここでは、私が担当したBASIC コンパイラー KM-1120 (KM-BASIC)の技術的なことに関して述べます。
概要
KM-BASICは、SD-カードに保存されているソースコードを元に、バイナリーをPIC32MXのRAM上に構築して実行する、コンパイラーです。FOR-NEXT, GOTO, IF-THENステートメントや、四則演算など、基本的な機能はすべてRAM上のバイナリーとして構築されて実行されるので、非常に高速です。PRINT文など、一部の機能の実行にはMachiKania上のライブラリーを用いて実行されるので若干速度が落ちますが、それでも通常のBASICインタープリターと比べて、ソースコードの認識は必要がない分、高速です。
現在のKM-BASICのバージョンでは、変数として32ビット整数型と、文字列型の二つが扱えます。また、32ビット整数型は、一次元配列として使用することも可能です。文字列型の使用は、かなりフレキシブルに行なうことが出来て、BASICをよくご存じの方は、さほど違和感なく使えると思います。一点だけ通常のBASICと異なるのは、文字列同士の一致判定に「=」演算子を使うことが出来なくて、STRNCMP()関数を使わないといけないことでしょうか。また、MID$()様の動作は、「A$()」の様に記述する事も、特徴です。この仕様は、APPLE I用の BASICを参考にしました。
レジスター使用規約
ここからは、コンパイラーの細かな仕様などについてメモ書きを残しておきます。色々ややこしい話なので、MachiKaniaを純粋に楽しみたい方には余り関係のない話であり、自分用のメモのような物です。
MIPS M4Kのレジスターの利用方法については、Cコンパイラーに於ける仕様規則に準拠していて、次のように使用しています。
・ $zero: zero (常に0)
・ $at 使用しない
・ $v0: 関数戻り値もしくは直前の演算結果
・ $v1: 演算用($v0=$v1*$v0の様に使う)
・ $a0-$a2: ライブラリー呼び出しの際の引数
・ $a3: ライブラリーの関数指定用
・ $t0-$t7: テンポラリーレジスターとして使用
・ $s0-$s5: 未使用。
・ $s6: 行番号格納。
・ $s7: call_library()関数へのポインター
・ $t8-$t9: テンポラリーレジスターとして使用
・ $k0-$k1: 使用しない
・ $gp: 使用しない(C コンパイラー側で使用)
・ $sp: スタックポインター
・ $fp($s8): 変数のアクセスに使用。
・ $ra: リターンアドレス格納
演算は、$v0と$v1の間で行ないます。演算子の優先順位を考慮した演算ルーチンのアルゴリズム等については、以前の記事で述べました。
バイナリーの構造
次に、バイナリーがどの様なコードになっているか、紹介します。次のBASICソースコードの例について述べます。
FOR I=1 to 100000 NEXTこのソースコードをコンパイルすると、次のようなコードが、RAM上に構築されます。
1C04-1C08が「I=1」に、1C0C-1C18が「100000」に、1C1C-1C24が省略されている「STEP 1」に相当します。1C50-1C58が「NEXT」です。「I」が100000に達したかどうかを判定するコードが、1C58と1C28-1C2Cに、「I」に1を足すコードが、1C30と1C3C-1C44にあります。こんな感じのコードを作成するのが、KM-BASICです。FOR-NEXTの処理部分のソースコードは、次の通りです。
char* for_statement(){ char* err; char b1; // Initialization of variable next_position(); b1=g_source[g_srcpos]; if (b1<'A' || 'Z'<b1) return ERR_SYNTAX; err=let_statement(); if (err) return err; // Check if "TO" exists next_position(); if (strncmp(g_source+g_srcpos,"TO ",3)) return ERR_SYNTAX; g_srcpos+=3; err=get_value(); if (err) return err; // Usage of stack: // 12(sp): "TO" value // 8(sp): "STEP" value // 4(sp): Address to return to in "NEXT" statement. // Store "TO" value in stack check_obj_space(2); g_object[g_objpos++]=0x27BDFFF4; // addiu sp,sp,-12 g_object[g_objpos++]=0xAFA2000C; // sw v0,12(sp) // Check if "STEP" exists next_position(); if (!strncmp(g_source+g_srcpos,"STEP ",5)) { // "STEP" exists. Get value g_srcpos+=5; err=get_value(); if (err) return err; } else { // "STEP" not exist. Use "1". check_obj_space(1); g_object[g_objpos++]=0x24020001; // addiu v0,zero,1 } // Store "STEP" value in stack and jump to start address // while store return address to $ra. check_obj_space(11); g_object[g_objpos++]=0x04110009; // bgezal zero,label1 g_object[g_objpos++]=0xAFA20008; // sw v0,8(sp) // After executing "NEXT" statement, process reaches following line. // Go to next step and check if variable reachs "TO" value. // Note that $v1 is set to 12($sp) in NEXT statement. // If yes, exit FOR-NEXT loop while restore stack pointer. // Note that $ra contain the address to go to. g_object[g_objpos++]=0x8FC20000|((b1-'A')*4); // lw v0,xx(s8) g_object[g_objpos++]=0x14430003; // bne v0,v1,label0 g_object[g_objpos++]=0x8FA30008; // lw v1,8(sp) g_object[g_objpos++]=0x03E00008; // jr ra g_object[g_objpos++]=0x27BD000C; // addiu sp,sp,12 // label0 g_object[g_objpos++]=0x00431021; // addu v0,v0,v1 g_object[g_objpos++]=0x10000002; // beq zero,zero,label2 g_object[g_objpos++]=0xAFC20000|((b1-'A')*4); // sw v0,xx(s8) // label1: g_object[g_objpos++]=0xAFBF0004; // sw ra,4(sp) // label2: return 0; } char* next_statement(){ // Return to address stored in 4($sp) // while set $v1 to 8($sp) (see for_statement) // and store return address to exit FOR-NEXT loop. check_obj_space(3); g_object[g_objpos++]=0x8FA20004; // lw v0,4(sp) g_object[g_objpos++]=0x0040F809; // jalr ra,v0 g_object[g_objpos++]=0x8FA3000C; // lw v1,12(sp) return 0; }
なお、バイナリー中で2カ所、"ori s6,zero,0x1" "ori s6,zero,0x2"とあるのは、それぞれ、ソースコードの1行目、2行目のバイナリーコードの始まり部分であることを示します。BASICコードの実行中に問題があって停止した場合、どの行に相当する部分で停止したかが分かるようになっています。なお、$s6レジスターの最上位ビットは、文字列処理用の一時領域を破棄するタイミングを計るのにも、用いられています。
スタック
上記FOR-NEXTのバイナリーコードで、スタック(spレジスター)が用いられています。スタックを多用しているのがKM-BASICの特徴で、これはMZ-80K用のKM-BASICと共通の仕様です。この特徴から、FOR-NEXT文の途中でRETURNしたり、GOTO文で抜け出したりすることは出来ません。ただし、GOSUB文でサブルーチンを呼び出すことは出来ます。スタックは他に、演算処理部分でも用いられています。他方、Cコンパイラーではローカル変数の格納先としてスタックが用いられていますが、現在のバージョンのKM-BASICでは、ローカル変数という概念がありません。これについては、後で言及します。
ライブラリー
バイナリーコードを簡単に作成できない複雑な動作をする部分に関しては、MachiKania BASIC system上にCの関数として書かれたコードを呼び出すことで、対応しています。先に、レジスターの使用規則はCでのそれに準拠していると述べましたが、準拠しているのは、このようにBASICのコードからCの関数を呼び出して使うのが、目的です。なお、s7レジスターに、このための関数「call_library()」へのポインターを格納しており、これを用いています。どのライブラリー関数を用いるかの情報はa3レジスターに格納し、関数に渡す引数は、a0, a1, v0の3つのレジスターに格納することで行なわれます。
リンク
KM-BASICは、1パスコンパイラーで、リンク処理がコンパイルの後に行なわます。処理しているのは、GOTO, GOSUB, SOUNDの3つのステートメントです。GOTO文の場合、コンパイル時に書き込まれるのは、CPUのジャンプ命令ではなく、0x0810xxxx, 0x0811yyyyの2つのワードです。xxxxとyyyyの部分はジャンプ先を示しており、リンカーはこの2つのワードを認識すると、ジャンプ先アドレスを調べて(これを調べるのに、上で述べたs6レジスターに代入するコード "ori s6,zero,xxxx" を使います)、そのアドレスにジャンプするコードに書き換えます。GOSUB, SOUNDも、それぞれ0x0812xxxx + 0x0813yyyy, 0x0814xxxx + 0x0815yyyyと書かれているので、リンカーが同様に書き換えます。MIPSの様なRISC CPUのマシン語は、x86などと異なり、命令長が綺麗にそろっているのが特徴で、バイナリーに於けるこういった処理がやりやすくなっています。
今後の発展
次のメジャーアップデートでは、以下のような機能拡張を考えています。
・浮動小数点演算
現在のバージョンでは、数の計算は整数型のみですが、浮動小数点演算をサポートしたいと考えています。どの様な形で整数型と浮動小数点型を共存させるかは、現在考慮中です。
・サブルーチン内に於けるローカル変数
現在のバージョンでは、グローバルな変数が26個使えるだけですが、サブルーチン内でローカルな変数を使えるようにする予定です。サブルーチン内で、JavaScriptの様にVARステートメントでローカル変数が指定できるようにするつもりです。これにより、メインルーチンとサブルーチンで、処理を明確に分けることが出来るようになります。これの実装には、当然ながらスタックを用いますが、スタックは指定したローカル変数に対応するグローバル変数を待避しておくために使われ、RETURN時に、待避していた値が指定した変数に戻されます。
・GOSUB()関数で、引数の引き渡しをサポート。
GOSUB()関数に於いて、Cの関数のように引数を持たせることが出来るようにする予定です。これにより、上のローカル変数の実装と合わせて、大規模なプログラムを書くことがより容易になるはずです。
・その他
ケンケンさんと、色々話し合っています。もしかしたら、フルグラフィックとか、使えるようになるかも知れません。
リファレンス
<BASIC言語の書式> BASICプログラムの記述は、行番号式、ラベル式、その混合、いずれの方法でも構 いません。以下、仕様について述べます。 <利用可能な変数型> 利用できる変数の型は、32ビット符号付整数(-2147483648 以上 +2147483647 以 下)と、文字列型の2種類です。文字列の末端部には0x00が付加されます。 A-Zの26個の整数型変数が利用可能です。文字列として扱う場合はA$のように記 述します。ただし、A(整数型)とA$(文字列型)を同時に使用することは出来ま せん。 整数型の定数は、10進法で記述します。16進法を使う場合、「$1200」のよう に、頭に「$」を付加するか、「0x1200」の様に表記して下さい。 文字列型の定数は、「"」で囲って記述してください。「"」を使用する場合は、 「CHR$($22)」のように記述することが出来ます。 <命令> 以下、x, y, z等は整数値を、x$, y$, z$は文字列を指します。xxx, yyy, zzz, www等は任意のステートメントを指します。[ ]は省略可能である事を示します。 命令同士を「:」で区切ることにより、一行で複数のコマンドを処理すること が出来ます。 BGCOLOR r,g,b 背景色指定。 CLEAR すべての文字列型変数と整数型配列を破棄し、整数値を0とする。また、 PCGの使用をやめ、表示キャラクターをリセットする。 CLS スクリーン消去。 COLOR x テキスト色指定。 CURSOR x,y カーソル位置指定。 DATA x[,y[,z[...]]] データー列を整数値で指定する。 DIM xxx [, yyy [, zzz [, ... ]]] 整数型の一次元配列を割り当てる。 xxx,yyy,zzzは、例えば「A(10)」のように記述する。この場合、A(0)から A(10)までの11個の整数型変数が確保される。 DRAWCOUNT DRAWCOUNT値を指定する。DRAWCOUNT値に付いては、DRAWCOUNT()関数を 参照。 END BASICプログラムを停止する。 EXEC x[,y[,z[...]]] 機械語を実行する。ただし、x,y,zは32ビット整数値。 FOR x=yyy TO zzz [ STEP www ] NEXT yyyで示された計算結果をxに代入し、xの値がzzzになるまで次のNEXT文 までのステートメントを、繰り返し実行する。繰り返しのたび、xの値は wwwずつ増加する(省略された場合は1ずつ)。「NEXT」の次に何も記述 しないことに注意。 GOSUB xxx 現在の実行位置を記憶し、xxx行目(もしくはラベル)に移動する。 GOTO xxx xxx行目(もしくはラベル)に移動する。 IF x THEN yyy [ ELSE zzz ] xが0以外のとき、yyyを、0のときzzzを実行 LABEL xxx GOTO/GOSUBのジャンプ先を登録する。xxxは、英数字6文字以内の文字列。 [LET] x=yyy yで示された計算結果を、xに代入する。「LET」は省略可。 [LET] x$=yyy yyyで示された文字列(もしくは連結結果;連結演算子は「+」)を、x$に 代入する。「LET」は省略可。 MUSIC x$ BGMを演奏する。詳細は、下記<MUSIC>の項を参照。 PALETTE n,r,g,b パレット指定。 PCG x,y,z ASCIIコードがxの文字の表示キャラクターを変更する。y,zは、キャラク ターデーター。詳細は、下記<PCG>の項を参照。 POKE x,y xで示される物理的アドレスに、yで示される値(1バイト値)を書き込む。 PRINT [ xまたはx$ [ ,または; [ yまたはy$ [ ... ]]]] ディスプレイに、整数値または文字列を表示する。「;」を使用した場 合、次の表示が続けて行われる。「,」を使用した場合、10文字ずつ に区切って表示される。どちらも使用しない場合、次の表示は行を変え て行われる。 REM xxx 何も実行しない RESTORE xxx DATA読み出し開始位置を指定。xxxは行番号もしくはラベル。 RETURN 最後に実行されたGOSUB文の次のステートメントに移動する。戻り値を指 定することがができる。この場合の戻り値はGOSUB()関数にて取得が可能。 SCROLL x,y 画面を横方向、もしくは縦方向(斜めも可)に動かす。動かす方向と大きさ は、x, yでそれぞれ、横方向の移動度、縦方向の移動度として指定する。 SOUND xxx 効果音を再生する。詳細は、下記<SOUND>の項を参照。xxxは行番号もしく はラベル。 USEPCG [x] PCGを使用、もしくは使用停止する。x=0で使用停止、x=1で使用、x=2で キャラクターをリセットして使用。xを省略した場合は、x=1と同じ。 WAIT x xで示された時間、プログラムの実行を停止する。xが60の場合、約1秒間 停止。 <関数> 以下、x, y, zは整数値を、x$, y$, z$は文字列を指します。[ ]は省略可能である事 を示します。 ABS(x) xの絶対値を返す。 ASC(x$) 文字列の最初の一文字の、アスキーコードを返す。 DRAWCOUNT() DRAWCOUNT値を得る。DRAWCOUNTは16ビット整数値で、1/60秒ごとに1ずつ 増える。 GOSUB(xxx) GOSUB命令と同じだが、戻り値(RETURNを参照)を得ることが出来る。xxxは、 ラベルもしくは行番号。 INKEY([x]) xを指定しない場合、現在押されているキーのASCII値を返す。押されていな い場合は、0。ASCII値でxを指定した場合、そのキーが押されているかどう かを返す。 KEYS([x]) キー入力を得る。xの値は以下の通り。xを指定しない場合は、x=63と同じ。 KEYUP: 1 KEYDOWN: 2 KEYLEFT: 4 KEYRIGHT: 8 KEYSTART: 16 KEYFIRE: 32 LEN(x$) 文字列の長さを返す。 MUSIC() BGMの演奏の残り数を返す。 NOT(x) x=0の場合に1を、そうでない場合に0を返す。 PEEK(x) xで示される物理アドレスから1バイト読み取り、返す。 READ() DATA文の後から、一つずつデーター(整数値)を読み出す。 RND() 0から32767までの擬似乱数を返す。 SGN(x) xの符号(-1, 0, または1)を返す。 STRNCMP(x$,y$,z) 2つの文字列のうちz文字分を比較し、結果を返す。同じ文字列の場合は0。 TVRAM([x]) ビデオRAMのx番目の内容を、バイト値で返す。xを省略した場合、ビデオ RAMの開始位置の物理アドレスを返す。 VAL(x$) 10進数もしくは16進数文字列としてのx$の値を、整数値で返す。 A$(x [,y])など xの値が0の場合、文字列全体を返す。 xの値が正の場合、xで示される位置より右側の文字列を返す。 xの値が負のとき、文字列の右側x文字を返す。 yが指定された場合、y文字分の文字列を返す。 CHR$(x) xをアスキーコードとする文字を返す。 DEC$(x) xの値を、10進数の文字列として返す。 HEX$(x [,y]) xの値を、16進数の文字列として返す。yが指定された場合、yバイト長の 文字列になる。 INPUT$() 文字列入力状態になり、入力が終了すると(Enterが押されると)文字列を返す。 <演算子> -x 符号を反転 x + y 整数加算 x - y 整数減算 x * y 整数乗算 x / y 整数除算 x % y 整数剰余 x = y 2つの整数値が等しい場合に1、そうでないときに0 x != y 2つの整数値が等しい場合に0、そうでないときに1 x < y xがyより小さい場合に1、そうでないときに0 x <= y xがyより小さいか等しい場合に1、そうでないときに0 x > y xがyより多きい場合に1、そうでないときに0 x >= y xがyより多きいか等しい場合に1、そうでないときに0 x AND y xとyの値のビットごとの AND(論理積でないことに注意) x OR y xとyの値のビットごとの OR x XOR y xとyの値のビットごとの XOR x$ + y$ 文字列の連結 なお、整数演算子の優先順位は、優先度が高いものから以下の順です。 + - (単項演算子) * / % + - (加算・減算) < <= > >= = != XOR AND OR <MUSIC> MUSIC命令では、BGM用のデーターを文字列で指定します。文字列の書式は、ABC notationに準拠しています。ただし、すべての記法が使えるわけではありません。 なお、キーや速度などのデフォルト設定値は以下の通りです。 Q: 1/4=90 L: 1/8 K: C BGM演奏時に一度に設定できる音の数は、31迄です。これを超えて音楽を再生したい 場合は、MUSIC()関数の戻り値を調べ、その値が十分小さくなってから、次のMUSIC命 令を実行するようにします。 添付のmusic.basに、使い方に関するサンプルがありますので、参考にして下さい。 <SOUND> SOUND命令では、DATA列のデーターを、行番号もしくはラベルで指定します。SOUND命 令による効果音再生中は、BGMは再生されません。また、前の効果音が終わる前に次 のSOUND命令を実行すると、前の効果音の再生は停止し、新しい効果音がすぐに再生 されます。 DATA列では、32ビット整数値として、交換音を表現します。この整数値の下位16 ビットは周波数の指定です。2048が440Hz(ラの音)に対応します。値が大きくなるほ ど、より低い音が出ます。上位16ビットは、音の長さです。1が、1/60秒に相当し ます。最後に、65535以下の値で、効果音の繰り返し回数を指定します。これらのデー ターの数は、32を超えないようにして下さい。 添付のsound.basに、使い方に関するサンプルがありますので、参考にして下さい。 <PCG> PCG(Programmable Character Generator)を用いると、ASCIIコードごとにフォント を指定して、疑似グラフィックスとして表示させることが出来ます。使用する場合 は、まず USEPCG とします。フォントの変更は、PCGステートメントを用いて、 PCG 0x80,0x80402010,0x08040201 の様に設定します。この例では、ASCIIコード0x80の文字のフォントを設定してい て、バックスラッシュの様な記号(左上から右下に向かう斜め線)が表示されるよう になります。PCGの利用を停止し、オリジナルのフォントに戻す場合は、 USEPCG 0 とします。再度PCGを使用したい場合は、 USEPCG として下さい。先に設定したフォントデーターが、復活します。なお、先に設定し たフォントデーターを破棄してPCGの使用を始めたい場合は、 USEPCG 2 として下さい。 <ヒント> FOR文では、TOステートメントの次に書かれた値に合致する場合(超えた時ではなく) に、ループから抜ける仕様です。また、FOR-NEXTループの途中で、GOTO文でループの 外に飛んだり、RETURN文を実行したりすると、予期せぬ結果(機器のリセット等)を 引き起こします。ただし、GOSUB文でサブルーチンを呼んだり、別のFOR-NEXTをネス トして使う事は可能です。 ON GOTO分やON GOSUB文はサポートしていません。ただし、例えば次のように記述す ることで、同様の動作をさせることは可能です。 GOSUB 10000+A .... 10000 PRINT "A=0" : RETURN 10001 PRINT "A=1" : RETURN 10002 PRINT "A=2" : RETURN 一行中で連続して文字列を扱うと、"String too complexed"というエラーがでて、 停止することがあります。この場合は、文字列を扱う命令を独立した行するか、文 字列関連の演算を幾つかのステップに分けて、それぞれ1行ずつの記述にして試し てみて下さい。 <バージョン履歴> ・KM-1120 2016年2月公開。 1.PCG機能を追加。 2.SCROLL命令を追加。 3.WAIT命令を追加。 4.「Ctrl+Break」キーによる実行停止に対応。 5.FOR無しでNEXTを実行した場合、GOSUB無しでRETURNを実行した場合にエラー を表示して停止するようにした。 ・Ver 1.1.0 (KM-1110) 2015年12月公開。 1.2015年11月21日に変更されたPIC32TVGSの仕様に対応。 2.PS/2キーボードに対応。 3.INKEY() INPUT$() VAL() DEC$() の4つの関数を追加。 4.TVRAM() ASC() PEEK()が、0x80-0xFFの値に関して負の数を返していた不具合 を修正。 5.DIMステートメントにより配列を定義した際、すべての要素がゼロになるよう にした。 6.単項演算子、「-」「+」を追加。 7.LABEL定義されていない飛び先にGOTOするとリセットされる不具合を修正。 8.同一のLABELを複数回使用している場合に、コンパイルエラーが出るように修 正。 9.引数を持たないPRINT文に対応。 ・Ver 1.0.5 (KM-1100) 最初の正規公開バージョン。