dimの考察

dimは配列変数を設定・提供するdefine節専用命令である。
配列変数は他の数値変数・文字変数と同様に番号で表され、?の接頭辞で区別され、[]で囲まれた添え字(0と正の整数)を必要とする。格納できるのは数値変数と同じく整数のみである。
表示に関してはほぼ数値変数と同様に扱える。

宣言の仕方

 *define
dim ?100[10]
game
 *start
end

これが最も基本的な形式。しかしもっと使いやすくしてみる。

 *define
numalias dim_max,10
numalias dim1,100
numalias dim2,101
dim ?dim1[dim_max]
dim ?dim2[dim_max]
game
 *start
end

numaliasを先に宣言して使用することで、マジックナンバー的な効果が得られる。
同じ長さの配列が複数欲しい時など重宝する。
また、添え字は多次元にできる。

 *define
dim ?100[10][10] ; 11*11の配列変数を用意する。
game
 *start
end

あまりやりすぎるとすぐにメモリーが足りなくなるので注意する。ユーザーのローカル環境に過大な期待を抱かないこと。

添え字

 *define
dim ?100[10]
; 配列変数100番を設定。この配列変数は、0〜10までの添え字を取る。
game
 *start
for %0=0 to 11
%0番の中身:?100[%0]
; %0が11になった時、添え字の最大値以上にアクセスしようとしてエラーが発生する。
next
end

または。

 *define
dim ?100[10]
; 配列変数100番を設定。この配列変数は、0〜10までの添え字を取る。
game
 *start
for %0=10 to -1 step -1
%0番の中身:?100[%0]
; %0が-1になった時、負の添え字にアクセスしようとしてエラーが発生する。
next
end

数値の格納方法

配列変数に数値を設定する時は、movを使う。

 *define
dim ?100[10]
game
 *start
0番の中身:?100[0]
mov ?100[0],10
0番の中身:?100[0]
\
end

一度に複数の配列変数を設定する時は、movlを使うと便利。

 *define
dim ?100[10]
dim ?101[10][10]
game
 *start
movl ?100,0,1,2,3,4,5,6,7,8,9,10
movl ?101[0],0,1,2,3,4,5,6,7,8,9,10
end

使える場所

添え字を必要とする以外は数値変数と同じように使える。例外はあるので注意したい。
格納されている数値を画面に表示したい時は、そのまま書けばいい。

 *define
dim ?100[10]
game
 *start
; 配列変数の中身を順番に表示する。(ただし、何も設定してないので0になるが)
for %0=0 to 10
%0番の中身:?100[%0]
next
\
end

何かの命令の引数に使うこともできる。

 *define
dim ?100[10]
game
 *start
movl ?100,10,10,10,10,10,10,10,10,10,10,10
strsp ?100[0],"文字列",?100[1],?100[2],?100[3],?100[4],?100[5],?100[6],?100[7],?100[8],?100[9],?100[10]
print 1
@
end

ただし、結果を受け取るための数値変数のかわりにはできない。

 *define
dim ?100[1]
game
 *start
movl ?100,0,10
rnd2 %0,?100[0],?100[1]
; ランダムの最小値や最大値を指定するのに配列変数を使っても問題ないが……
乱数:%0
rnd2 ?100[0],1,10
; 配列変数に直接乱数を格納しようとする、エラーが出る。
乱数:?100[0]@
end

なお、これはdefsubで自作した関数でも同様で、getparamでi%numやs%strなどした個所に配列変数をもってくるとエラーになる。その引数部分は数値変数か文字列変数しか受け付けないのだ。
この制限はかなりきついもので、inc,dec,add,sub,mul,divのような基本的な四則演算命令すらもdimは使えないことになる。

 *define
dim ?100[10]
game
 *start
inc ?100[0] ; ここがエラーになる!
end

配列変数の計算をしたければ、一旦補助の数値変数にコピーして計算して戻すか、movを使わなければならない。

 *define
dim ?100[10]
game
 *start
; incのかわり
mov ?100[0],?100[0]+1 ; これならOK
end

globalonとの関係

配列変数はグローバルにはならない。以下のサンプルを何度も起動してみて欲しい。

 *define
globalon
dim ?301[10]
game
 *start
inc %300
起動:%300回目
配列変数の中身(変更前):?301[0]
rnd2 %0,1,100
mov ?301[0],%0
配列変数の中身(変更後):?301[0]
@
end

起動回数は保存されてカウントアップしていくが、配列変数の中身は毎回初期化されてしまうのがわかるだろう。
従って、配列変数の中身を保持しようとすると、独自にそれなりの処理を作らなければならない。
最近追加されたcsv関連の命令を使ってもいいが、csvだとユーザーに変更される恐れがある。
そこで考えたのがグローバル文字列を使う方法だ。
配列変数の中身全体をグローバルの文字列変数に格納し、適宜文字列変数から配列変数に展開するのだ。
セーブ直前で文字列変数に格納。ロード直後に配列変数に展開。これなら大丈夫だと思う。

 *define
globalon
dim ?300[10]

; 訳語が変だったらごめん
defsub squeeze_array ; 指定された配列変数を文字変数に圧縮する。
defsub spread_array ; 指定された文字変数を配列変数に展開する。
; numaliasの設定数値は適宜変更してください。
numalias array_num,100
numalias array_length,101
numalias array_loop,102
numalias array_temp,103
numalias array_separater,104
numalias array_cursor,105
stralias array_default_separater,"," ; デフォルトの区切り文字

game

 *squeeze_array
; 第一引数は圧縮したい配列変数の番号。同じ番号の文字列変数に圧縮する。
; 第二引数は圧縮したい配列変数の添え字の最大値。
; この関数は一次元配列のみを対象とする。
getparam %array_num,%array_length
mov $%array_num,"" ; 初期化
for %array_loop=0 to %array_length
add $%array_num,array_default_separater
itoa $array_temp,?%array_num[%array_loop]
add $%array_num,$array_temp
next
add $%array_num,array_default_separater
return

 *spread_array
; 第一引数は展開したい文字列変数の番号。同じ番号の配列変数に展開する。
; この関数は一次元配列のみを対象とする。
getparam %array_num
mov %array_length,0 ; 配列変数の添え字
mid $array_separater,$%array_num,0,1 ; 区切り文字を取得
mov %array_cursor,1 ; 文字列変数のどこを見るか設定
mov $array_cursor,"" ; その文字列変数の切片を保管する変数。
len %array_temp,$%array_num ; 文字列の長さを取得しておく
 *spread_array_loop
; ループ開始
mid $array_temp,$%array_num,%array_cursor,1 ; 一文字取得
if $array_temp=$array_separater goto *spread_array_1
add $array_cursor,$array_temp
goto *spread_array_loop_end
 *spread_array_1
atoi %array_loop,$array_cursor
mov ?%array_num[%array_length],%array_loop
mov $array_cursor,""
inc %array_length
 *spread_array_loop_end
inc %array_cursor
if %array_cursor<%array_temp goto *spread_array_loop
return

 *start

spread_array 300 ; 文字列変数300番を、配列変数300番に展開

notif ?300[0]=0 goto *main

; 最初の起動であれば、配列変数の中身をランダムに設定。
for %0=0 to 10
rnd2 %1,1,100
mov ?300[%0],%1
next

 *main

; 配列変数の中身を表示する。
for %0=0 to 10
%0番の中身:?300[%0]
next

; 配列変数一つ一つに1を足す。
for %0=0 to 10
mov ?300[%0],?300[%0]+1
next
squeeze_array 300,10 ; 配列変数300番(長さ10)を文字列変数300番に保存
@
end