(フラグテクニックも使って、74byteまで縮んだ「オールマシン語かわくだりゲーム」スピードをPOKEで改造するまで動画)
計算結果がゼロかどうかを判定するゼロフラグを使って分岐したりループを作ったりしてきましたが、フラグはそれだけではありません。
Z 結果が0かどうかのゼロフログ
C 桁溢れが分かるキャリーフラグ(引き算は2の補数の足し算になる)
N 結果がマイナスかどうかのネガティブフラグ
V 符号付き計算が破綻したかのオーバーフローフラグ
IchigoJam BASICなどのプログラミング言語では、等号や不等号とIF文を使って判定していますが、実際はこれら4つのフラグが使われています。 試しに、パラーメーターが15以上かどうか判定してみましょう。(CC = キャリーフラグが立っていない時分岐)
R1=0 R0-15 IF CC GOTO 2 R1=1 R0=R1 RET
asm15でアセンブルしたものをIchigoJamでメモリに書き込み、動かしてみましょう。
POKE#700,0,33,15,40,0,219,1,33,8,70,112,71 ?USR(#700,15) 1 ?USR(#700,14) 0
うまく判定できてますね!(-15は、2の補数表現との足し算となるため、15より大きいと桁あふれでキャリーフラグが立ち、CCでキャリフラグが立っていない時はスキップ。)
こちらがフラグ判定に使用できる条件の一覧です。
- フラグ判定
EQ Z=1, ゼロフラグセット(一致) Equals =0
NE Z=0, ゼロフラグクリア(不一致) Not Equals =!0
CS C=1, キャリーフラグセット =HS
CC C=0, キャリーフラグクリア =LO
MI N=1, ネガティブフラグセット
PL N=0, ネガティブフラグクリア
VS V=1, オーバーフローセット
VC V=0, オーバーフローリセット
AL 常に分岐
- 符号なし
HS C=1, 以上, Higher or Same =CS
HI C=1 and Z=0, より大きい, HIgher
LS C=0 or Z=1, 以下, Lower or Same
LO C=0, より小さい, Lower =CC
- 符号付き
GE N=V, 以上
LT N!=V, より小さい
GT Z=0 and N=V, より大きい
LE Z=1 or N!=V, 以下
32bitのレジスタ、符号付きとして使うか、符号なしとして使うかはあなた次第!
いろいろ試してみましょう。
また、キャリーフラグを使った計算命令を使うと大きな桁を使った計算ができます。
IchigoJamの配列を4つ使った8byte、64bitの足し算引き算をしてみましょう。
足し算 [8-11] = [0-3] + [4-7] (下位バイトを先とするリトルエンディアンとしています)
20 CLV 30 LET[0],-1,-1,0,0 40 LET[4],1,0,0,0 60 FOR I=8 TO 11:?[I];" ";:NEXT:?
足し算するマシン語を作ります。
PUSH {R4,PC} R0+=R1 R1=[R0+0]L R2=[R0+1]L R3=[R0+2]L R4=[R0+3]L R1=R1+R3 R2+=R4+C [R0+4]L=R1 [R0+5]L=R2 POP {R4,LR}
R2+=R4+Cで、直前の足し算によるキャリーフラグ分も合わせて足します。
R1+=R3 ではフラグが変化しないので、R0〜R7までのみ使える、R1=R1+R3を使いましょう。
(参考、Cortex-M0 Armマシン語表 asm15)
asm15でアセンブルしたマシン語と呼び出しコードを加えて実行してみます。
10 POKE#700,240,181,8,68,1,104,66,104,131,104,196,104,201,24,98,65,1,97,66,97,240,189 50 U=USR(#700,#800) RUN 0 0 1 0
無事、桁上りの計算ができてます!
キャリーフラグ付きの引き算 Rd-=Rm+!C を使って引き算も実装してみましょう。
(引き算は、桁あふれが発生していないときにキャリーフラグがセットされるので、!Cを使います)
@fujitanozomuさんによる、フラグを使ったスゴ技を紹介します。 オールマシン語のかわくだりゲームのキー入力判定、素直につくるとこういう感じで6命令12byte
R0-28 IF !0 GOTO 2 R4-=1 R0-29 IF !0 GOTO 2 R4+=1
左キーが28、右キーが29と連続していることとフラグを使うと、下記のように4命令8byteで実装可能です
R0-=28 R0-=1 IF HI GOTO 2 R4+=R0+C
28減算し、判定したいものを0か1にした後に、更に1減算しつつ符号なしで1より大きければスキップ。 R0が0、つまり左キーの時だったときは、R0が-1になっていて、右キーの時はキャリーフラグが立っているので+1されます。 見事!
IchigoJamのAPIアクセス、#C0をキャッシュし、差分メモリアクセスを使う技を使うと、かわくだりゲームが37命令74byteで実現できました。
PUSH {R4-R7,LR} R7=#C0 R6=[R7+(#C4-#C0)/2]W 'putc R5=[R7+(#CE-#C0)/2]W 'locate R3=[R7+(#CC-#C0)/2]W 'cls GOSUB R3 R4=15 'X=15 @LOOP R0=R4 'locate X,5 R1=5 GOSUB R5 R0=#EC 'putc neko GOSUB R6 R0=32 'rnd(32) R3=[R7+(#C0-#C0)/2]W GOSUB R3 R1=23 'locate R0,23 GOSUB R5 R0=#2A 'putc *, enter GOSUB R6 R0=10 GOSUB R6 R0=3 'wait 3 R3=[R7+(#D6-#C0)/2]W GOSUB R3 R3=[R7+(#CA-#C0)/2]W 'inkey() GOSUB R3 R0-=28 'X=X-(R0=28)+(R0=29) R0-=1 IF HI GOTO 2 ADC R4,R0 R0=R4 'scr(X,5) R1=5 R3=[R7+(#D0-#C0)/2]W GOSUB R3 R0-0 ' IF R0=0 GOTO @LOOP IF 0 GOTO @LOOP POP {R4-R7,PC}
asm15でアセンブルした実行可能にしたものがこちら。(IchigoJam webではAPI未対応のため動きません)
10 POKE#700,240,181,192,39,190,136,253,137,187,137,152,71,15,36,32,70,5,33,168,71,236,32,176,71,32,32,59,136,152,71,23,33,168,71,42,32,176,71,10,32,176,71,3,32,251,138,152,71,123,137,152,71,28,56 20 POKE#736,1,56,0,216,68,65,32,70,5,33,59,138,152,71,0,40,226,208,240,189 30 U=USR(#700)
c4ij/zen4ij/rust4ij/Ruby on Jam でベンチマークしているスコア付きかわくだりゲームも、なんと82byte!
PUSH {R4-R7,LR} R6=#C0 'cls R3=[R6+(#CC-#C0)/2]W GOSUB R3 R4=15 ' X R5=0 ' S 'putc R7=[R6+(#C4-#C0)/2]W 'locate R3=[R6+(#CE-#C0)/2]W R12=R3 @LOOP 'locate X,5 R0=R4 R1=5 GOSUB R12 'putc neko R0=#EC GOSUB R7 'rnd(32) R0=32 R3=[R6+(#C0-#C0)/2]W GOSUB R3 'locate R0,23 R1=23 GOSUB R12 'putc *, enter R0=#2A GOSUB R7 R0=10 GOSUB R7 'wait 3 R0=3 R3=[R6+(#D6-#C0)/2]W GOSUB R3 'inkey() R3=[R6+(#CA-#C0)/2]W GOSUB R3 R0-=28 R0-=1 IF HI GOTO 2 ADC R4,R0 'scr(X,5) R0=R4 R1=5 R3=[R6+(#D0-#C0)/2]W GOSUB R3 R5+=1 R0-0 IF 0 GOTO @LOOP R0=R5-1 POP {R4-R7,PC}
日本のスパコン富岳のコア、A64FXも同じArmのコンピューター(fujitsu/A64FX on GitHub)。
世界最速になったヒミツを探るためにも必要な、マシン語の基本、楽しく習得しておきましょう!