Opcodeについて

Last-modified: 2023-01-03 (火) 23:43:53
 

概要

Opcodeは中間言語です。
C言語やBASICなどの高級言語と機械語の中間の言語です。
高級言語のような読みやすさがあり、ソースコードを復元しやすいのが特徴です。

Opcodeの構成要素

変数 … 0@ , $300 等、変化する値
定数 … 10 , 3.14 等、変化しない値
関数 … 02F6: 0@ = sine 1@ 等、データを渡すと何らかの仕事をし、値を返す要素
配列 … &0(0@,1i) 等、同じ性質の変数を並べた物
制御文 … if , jump 等、プログラムの流れを制御する要素
演算子 … + , - 等、変数などに対して働きかける要素
コメント … //~ 等、ソースプログラムの中に書き込める注釈
それでは簡単に説明をしていきます。

コードの例

00A5: 0@ = create_car #PONY at 1.0 2.0 3.0
  • 00A5:
    コード番号 と言えばいいのでしょうか?
    コードにはこのような4桁の16進数が付いています。これは変更できません。
    命令の種類を表す重要な部分です。「付く物」と覚えておいてください。
    一部Sanny Builderで省略することが出来る物もあります。
    比較などの場合、先頭の0を8に変えると否定の意味になります。
    番号が分からないという方、Sanny Builderにちゃんとあります。
    ツールバー Tools → IDE tools → Opcode Search (Ctrl+Alt+2)
  • = create_car #PONY at 1.0 2.0 3.0
    「= create_car , at」の部分は飾りです。全て省いて「00A5: 0@ #PONY 1.0 2.0 3.0」でも機能しますが、これではわかりにくいので付けられています。
    #PONY の部分で生成する車を指定しています。
    後ろの 1.0 2.0 3.0 は座標で、大抵の場合はX,Y,Zの順に書かれています。
  • 0@
    変数 0@ に右式を代入。
    この場合、座標 [1.0, 2.0, 3.0] に出した#PONYが代入されます。

つまり、このコードは
「座標X1.0, Y2.0, Z3.0に#PONYを出せ」という命令になります。

この命令のあとに

020B: explode_car 0@ // versionA

と命令してやることで0@、つまり上で代入したPonyが爆発するわけです。

定数・変数

文字通り、「変化する数」です。
演算したり 関数の引数、分岐のための比較など とても重要な働きをします。
Opcodeの変数には4種類あり、整数、小数、短文字列、長文字列を指定できます。

定数ローカル変数グローバル変数メモリ変数
整数100@$100&100
小数3.14
短文字列'ABC'0@ss$100s&100
長文字列"This is a pen."0@vv$100v&100

定数

整数-2147483648~2147483647
少数-3.402823466E+38~3.402823466E+38
短文字列7文字まで
長文字列15文字まで

ローカル変数

0@~31@まで利用可。その他にタイマー変数32@と33@が存在し、これらには1フレームの時間*1が加算されていく。
ミッションスレッドでは1023@まで使用可能。
どちらも同スレッド内のみで利用可能。

グローバル変数

$0~$65535まで利用可。
配列を使えば$-2147483648 - $2147549182まで参照可能。
main.scmを含む全スレッドで共通。そのため、main.scmと重複するとエラーが出る可能性がある。
main.scmで使用する主なグローバル変数

整数(int)・小数(float)

Opcodeでは整数と少数は別物です。
計算する場合は、整数または小数同士に揃えなければ計算できません。
またその都合上、変数同士の代入・計算・比較は必ずOpcodeを表記しなければいけません。
(片方が定数であればOpcodeを省略して表記することもできる)

文字列

短文字列と長文字列があります。
短文字列は7文字まで、長文字列は15文字まで使用可能です。
ただし、短文字列変数は変数を2つ分、長文字列変数では変数を4つ分使用する点に注意。
例えば0@sは0@~1@を、0@vは0@~3@を使用するため、気をつけて使わないと変数を上書きしてしまったり、逆に文字列を壊してしまいます。

関数 (ここでは演算に関する関数を記述します。)

使用可能な関数
無い物は自分で関数を作成する必要がある。

sine
02F6, 02F8など。
古いバージョンのSBのOpcodeSearchではcosineとなっているが、間違い。
cosine
02F7, 02F9など。
こちらも古いバージョンのSBのOpcodeSearchではsineとなっているが、間違い。
平方根
01FBなど。
整数・小数の絶対値
0094など。
単位変換(メートル→フィート)
0425 と 042D の2つ存在する。前者がfloat用、後者がint用。
「meters to_feet」「metric to_imperial」と何故か微妙に表記揺れしているが意味は同じ。
乱数生成
0208, 0209など。

配列

全ての変数は配列として扱うことが出来ます。
配列について詳しいことはこちらへ→配列について

書式

配列名(インデックス変数,サイズ 型)
&0(0@,1i)
型
i …4バイト整数
f …4バイト小数
s …8バイト短文字列
v …16バイト長文字列
 

Sanny Builderは配列宣言もできるようです。
書式

var
配列名: array サイズ of 型
end

制御文

プログラムの流れを変える要素。構文も交えて説明します。

ラベル・ジャンプ・サブルーチン

ラベル

:label

スクリプトの特定場所に名前を付けます。
これ自体は何もしませんが、ジャンプ命令やサブルーチン命令でその場所へと飛んでこれるようになります。

ジャンプ

jump @label

指定したラベルへと移動し、そこから処理を続けます。

サブルーチン

gosub @label

ジャンプ命令と同じですが、移動する前に現在の場所を記憶しておきます。
飛んだ先の処理でreturn命令を使う事によって記憶していた元の場所へと戻り、そこから処理を再開できます。

条件分岐

if
(条件)

指定した条件が真であるかどうかによって処理を分岐させられます。
条件には比較演算やチェックコードが入ります。

書式1
if <N>
条件1
条件2
:
条件8
jf @ラベル名 (偽処理)
真処理
<N>には or または and を指定でき、指定した場合は一つのifに最大8つまで条件を指定できるようになります。
or: 指定した条件のうち、どれか一つでも真ならば真処理を行う。
and: 指定した条件の全てが真の時にのみ真処理を行う。一つでも偽なら偽処理に飛ぶ。
jf @ラベル名: 偽ならば<ラベル名>へジャンプする。
書式2 (if-then-else-end)
この書式はSanny Builder3独自のものであるため、コンパイルした物をデコンパイルしたとき同じ形に戻りません。
if <N>
条件
then
条件が真の場合の処理
else
条件が偽の場合の処理
end
elseと偽の場合の処理は省略できます。

:START
 :
if and
0039:   0@ == 1
0449:   actor $PLAYER_ACTOR in_a_car
jf @START
03C0: 10@ = actor $PLAYER_ACTOR car
04BA: set_car 10@ speed_to 100.0
 :

「もし、変数0@の中身が整数の1で かつ $PLAYER_ACTORが乗り物に乗っていたら、変数10@に$PLAYER_ACTORが乗っている乗り物を代入し、その乗り物のスピードを100m/sにする。
変数0@が1ではなかったり、$PLAYER_ACTORが乗り物に乗っていなければラベルSTARTへジャンプする。」
というプログラムになります。

and/orの注意点

and や or を指定したifではそれ以外の判定ができません。
つまり「条件1と2のどちらか、及び条件3を満たした場合」みたいなことができません。
どうしてもやりたい場合は、以下のようにまずどちらか一方の判定をし、その後でもう片方を判定するように書く必要があります。

if or
条件1
条件2
then
    if
    条件3
    then
        真処理
    end
end

ループ処理

for-end

一定の回数、同じ処理を繰り返します。
この書式はSanny Builder3独自のものであるため、コンパイルした物をデコンパイルしたとき同じ形に戻りません。
書式

for 変数 = 初期値 to(downto) 終値 step 増加数
処理
(break)
end

変数に初期値を代入し、増加数分を変数に加算しつづけ、終値に達した場合に処理を抜けます。
stepは省略可能で、省略した場合は1になります。
toでは増加数が加算され、downtoでは増加数分減算されます。

 

for 0@ = 0 to 4
020C: create_explosion_with_radius 0 at 0.0 0.0 0.0
0001: wait 0
end

1ループ目 0@に0を代入。座標 0.0 0.0 0.0に爆発作成 0@は0なので戻る。
2ループ目 0@に1を加算。座標 0.0 0.0 0.0に爆発作成 0@は1なので戻る。
3ループ目 0@に1を加算。座標 0.0 0.0 0.0に爆発作成 0@は2なので戻る。
4ループ目 0@に1を加算。座標 0.0 0.0 0.0に爆発作成 0@は3なので戻る。
5ループ目 0@に1を加算。座標 0.0 0.0 0.0に爆発作成 0@は4なので戻らずループを抜ける。
結果として、座標 0.0 0.0 0.0に5回爆発作成する。

while-end

条件がの間、同じ処理を繰り返します。
この書式はSanny Builder3独自のものであるため、コンパイルした物をデコンパイルしたとき同じ形に戻りません。
''書式

while 条件
処理
end

条件として true を指定する事もできます。
この場合無限ループとなり、中で jump や break を使用して自分で脱出しない限りずっとループし続けることになります。

 

while 8248: not model #AK47 available
0001: wait 0
end

「8248: not model #AK47 available」
これは#AK47 が読み込めていると真を返す「0248: model #AK47 available」の否定形で、つまり #AK47 が読み込めていない時に真を返します。
つまり、この例文は「モデル#AK47が読み込まれるまでwait 0をし続ける」という意味になります。

repeat-until

条件がの間、同じ処理を繰り返します。
条件の真偽がwhileと逆なので注意。
この書式はSanny Builder3独自のものであるため、コンパイルした物をデコンパイルしたとき同じ形に戻りません。
書式

repeat
処理
until 条件

見ての通り、中の処理を行った後に条件判定を行うため、必ず一度は処理がなされるというのがwhileとの大きな違いです。

 

repeat
0001: wait 0 ms
until 0248: model #AK47 available

上記のwhileの例文と同じく「モデル#AK47が読み込まれるまでwait 0をし続ける」という意味。
whileとは条件が逆になる(真になった時にループを抜ける)点に注意。

演算子

計算をするために必要な記号
整数または小数同士でなければ計算不可能。
プログラム上では、「=」は等しいという意味ではありません。
殆どのプログラム言語では左に変数 右に変数または定数を置き、左式に右式を代入という意味になります。
0006: 0@ = 1 ならば、0@に1を代入という意味です。
また、定数を小数で扱いたい場合は「1」と表記するのではなく「1.0」と表記して下さい。

演算演算子比較演算子
代入=等しいa == b
加算+=大なりa > b
減算-=小なりb > a
乗算*=以上a >= b
除算/=以下b >= a

上の表を見て「アレッ?」と思った方もいるでしょう。
実はOpcodeの比較命令には「等しい(==)」「大なり(>)」「以上(>=)」の3つしか存在しません。
そのため、それ以外の比較をしたい場合は左式と右式を入れ替えたり、Opcodeの先頭を 8 に変えて真偽を反転させたり等する必要があります。

コメント

何がしたいか、その処理に何の意味があるかといった注釈を入れたい場合に使います。
コンパイル時には無視されるため、デコンパイルした後のソースコードにはコメントは残りません。

書式1

//注釈
//「//」を記述した後ろ一行はすべて注釈として扱われます。
//基本的にはこちらを使います。

書式2

{
注釈
複数行を一度にコメント化したい場合はこちらを使うと楽です。
}

書式3

/*
注釈
書式2と同じですが、コメント内に } を含めたい場合はこちらを使うと良いでしょう。
*/

*1 1000÷フレームレート