以下では仕様について書いていきます。雑記・考察ログを見たい人は「オペコードなどについて考察する」をお読みください。専門用語の一覧は用語集に掲載されています。インタプリタで実装されている追加仕様などについてはインタプリタの追加設定一覧(暫定)を参照ください。Twitterで出た質問はTogetterにまとめてあります(part1, part2, part3)。ソースコードの例としては非再帰フィボナッチ再帰フィボナッチquicksort開平があります。

-1. はじめに

0. 呼び出し規約

1. ニーモニック一覧

細かい仕様が確定しておらずインタプリタでまだ実行できない命令についてはフォントサイズを小さくしている。

1-1. 移動命令

 命令  語源  x86  意味
 krz または kRz  kRantairzarth  mov  複製(第1オペランドの値を第2オペランドに代入。'c'i指定があるときは第2オペランドと第1オペランドの役割が逆転する。)
 malkrz または malkRz  mal kRantairzarth  cmovcc  フラグが立っているときのみkrzを行う
 fen  fav es niv e'i  nop
 何もしない。krz f0 f0と等価。オペランドを取らない。
 inj    irzarst ileceonj    二重移動 (第1オペランドの値を第2オペランドに代入しつつ第2オペランドの古い値を第3オペランドに代入。'c'i指定があるときは第3オペランドと第1オペランドの役割が逆転する。)

2018年8月27日追記:なお、厳密には、injの挙動を

  1. 第二オペランドの値を一時保管する
  2. 第一オペランドの値を第二オペランドに入れる
  3. 一時保管した値を第三オペランドに入れる

という操作をこの順に行う行為と同等であるべし、と定義する。

早見表 (以下a, b, cはそれぞれ異なる場所を指すとする):
inj a b c : cには旧bが、bには旧aが入る。aは変更されない。
inj a b a : aとbの値が交換される。
inj a a c : cには旧aが入る。aは変更されない。
inj a b b : どの値も変更されない。
inj a a a : どの値も変更されない。

2018年8月27日追記:なお、injのさらなる規定として、第二オペランドにレジスタ、第三オペランドにメモリを取る場合で、第三オペランドが第二オペランドで登場するレジスタを使っていた場合の動作を未定義(何が起きても構わない)とする。

1-2. 算術命令

基本的にオペランドを2つ取り、第二オペランド(書き込み可能である必要がある)に対して第一オペランドの値を用いた算術演算を行う。例えば、nta 4 f5であれば、「レジスタf5の値を読み取り、そこから4を引いた値をレジスタf5に書き込む」という意味である。'c'i擬似命令がある場合は第一オペランドと第二オペランドの役割が入れ替わる。

nacは0をdalすることと等価であり、オペランドを1つのみ取る。

latはオペランドを3つ取り、以下の動作をする。

2018年8月27日追記:なお、latのさらなる規定として('i'cの時)、第三オペランドにレジスタ、第二オペランドにメモリを取る場合で、第二オペランドが第三オペランドで登場するレジスタを使っていた場合の動作を未定義(何が起きても構わない)とする。

 命令  語源  x86  意味
 ata  atakeses  add  加算
 nta  ny atakeses  sub  減算
 lat  latvaves  (mul)  乗算(符号なし)
 latsna  latvaves (snakxazasyk)  (mul)  乗算(符号付き)
 (kak)  (kakites)  (div)  除算
 ada  adales  and  ビット積
 ekc  ekcan  or  ビット和
 nac  nacises  not  ビット反転。0をdalすることと等価。
 dal  daliuales  xnor  ビットxnor
 dto  dusnij tesnokonj  shr  論理右シフト
 dro または dRo  dusnij Restutonj  shl  左シフト
 dtosna  dusnij tesnokonj (snakxazasyk)  sar  算術右シフト

2018年8月27日追記:シフト命令でシフトするビット数は0~63ビットまでは合法である。64ビット以上は未定義とする。

@sosoBOTpi 2003lkのdto, dro, dtosnaで32ビット以上シフトしたときって0とかになるんでしょうか。シフト量の値をmod 32して扱ったり、32以上のシフトを未定義にしたりする処理系が多いみたいですし、実際論理回路で組むのが面倒なので

— 炭酸ソーダ (@na2co3_ftw) 2017年12月15日

実装依存にしてしまっていい気もしますが、ぴったり32ビットのシフトって意外と使う気がしないでもないので、どうしたもんかなぁ、とは思っています

— .sozysozbot.@hsjoihs@jekto.vatimeliju (@sosoBOTpi) 2017年12月15日

なるほど。32には対応して、33以上は未定義にするのであれば回路もそんなに複雑にはならないですね。(x & 32のビットだけ読んで判断できるので)

— 炭酸ソーダ (@na2co3_ftw) 2017年12月15日

となると、「32~63はゼロ、64以上は未定義」とできますね。現世と違って最初は寡占でしたし、最初に採用された仕様がずっと引き継がれても違和感もそんなにありません

— .sozysozbot.@hsjoihs@jekto.vatimeliju (@sosoBOTpi) 2017年12月15日

ですねえ(現世での実装が面倒)。あとdtosnaで元の数がマイナスだった場合は-1になりますかね。

— 炭酸ソーダ (@na2co3_ftw) 2017年12月15日

ですね。(ちなみにHaskellで書いているのでビットシフトは合理的に動きます、Cの遺産を引きずっていないので)

— .sozysozbot.@hsjoihs@jekto.vatimeliju (@sosoBOTpi) 2017年12月15日

1-3. 比較命令

fiの後に値を二つ取り、その二つの大小に応じてフラグを設定する命令。'c'i擬似命令や'i'c擬似命令の影響を受けない。

 命令  語源  意味
 fi ~ xtlo  fi * es * xut loler  以下ならフラグを立てる(符号付き比較)
 fi ~ xylo  fi * es * xy loler  未満ならフラグを立てる(符号付き比較)
 fi ~ clo  fi * es * ce loler  同等ならフラグを立てる
 fi ~ xolo  fi * es * xo loler  以上ならフラグを立てる(符号付き比較)
 fi ~ llo  fi * es * le loler  超過ならフラグを立てる(符号付き比較)
 fi ~ niv  fi * es * niv  等しくないならフラグを立てる
 fi ~ xtlonys  fi * es * xut loler (ny snakxaz)  以下ならフラグを立てる(符号無し比較)
 fi ~ xylonys  fi * es * xy loler (ny snakxaz)  未満ならフラグを立てる(符号無し比較)
 fi ~ xolonys  fi * es * xo loler (ny snakxaz)  以上ならフラグを立てる(符号無し比較)
 fi ~ llonys  fi * es * le loler (ny snakxaz)  超過ならフラグを立てる(符号無し比較)

1-4. 8bit/16bit移動命令

 命令  意味  精密な定義
 krz8i または kRz8i  -'iが8bitの複製(8bitロード)第1オペランドの指す32bitの値のうち、上位8bitを取って符号拡張して32bitにし、それを第2オペランドに代入。'c'i指定があるときは第2オペランドと第1オペランドの役割が逆転する。
 krz8c または kRz8c  -'cが8bitの複製(8bitストア)第1オペランドの値の下位8bitを、第2オペランドの指す32bitの内上位8bitに代入。'c'i指定があるときは第2オペランドと第1オペランドの役割が逆転する。
 krz16i または kRz16i  -'iが16bitの複製(16bitロード)第1オペランドの指す32bitの値のうち、上位16bitを取って符号拡張して32bitにし、それを第2オペランドに代入。'c'i指定があるときは第2オペランドと第1オペランドの役割が逆転する。
 krz16c または kRz16c  -'cが16bitの複製(16bitストア)第1オペランドの値の下位16bitを、第2オペランドの指す32bitの内上位16bitに代入。'c'i指定があるときは第2オペランドと第1オペランドの役割が逆転する。

2. レジスタ一覧

レジスタはリパライン語でfirjalである。

レジスタ名役割
f0関数内でいじって良いレジスタ。単純型戻り値はf0で返す。
f1
f2
f3
f4未設定
f5主にスタックを扱うためのレジスタ。
f6未設定
xxxeumon xelal「次に見るところ」の略。次に実行する命令のアドレスが格納されている。CPUはプログラムカウンタを持っていて、ひたすら「次の命令を指すところにxxを移動させる→針の指す命令を実行する→xxの指す位置に針を動かす」を繰り返すので、xxにkrzしてやるとジャンプできる。

3. その他の構文

構文語源役割
'c'i第一オペランドが-'c格、第二オペランドが-'i格として解釈される。現世の表現で言うと、GNU as的 mov src, destの語順でなくMASM的 mov dest, srcの語順になる。ただし、比較演算子はひっくり返らない。
'i'c第一オペランドが-'i格、第二オペランドが-'c格として解釈される(これがデフォルトの語順である)ように設定する。現世の表現で言うと、MASM的 mov dest, srcの語順でなくGNU as的 mov src, destの語順になる。ただし、比較演算子はひっくり返らない。
レジスタ名@レジスタに入っている番地のメモリを表す。
レジスタ名 + 定数@レジスタに入っている番地に定数を足した番地のメモリを表す。
レジスタ名 + レジスタ名@2つのレジスタに入っている数値を足した番地のメモリを表す。
l' ラベル名(後ろから修飾)lex直前の命令に対してラベル名を定義する
nll ラベル名(前から修飾)
ny la lex直後の命令に対してラベル名を定義する
kue ラベル名
kinunsaresラベル名を他のファイルからも見えるようにする
xok ラベル名
xokison他のファイルで定義されたラベル名を見えるようにする

4. 便利なイディオム

スタックにiskaをプッシュする

	'c'i
	nta f5 4	; f5を持ち上げて
	krz f5@ iska	; 積む

スタックをポップしてiskaに代入

	'c'i
	krz iska f5@	; スタックの値を代入
	ata f5 4	; f5を下げる

なお、スタックの伸長方向はアドレスの減少する向きである。

returnする

	'c'i
	krz xx f5@	; リターンアドレスに飛ぶ
	        	; スタックからリターンアドレスを削除するのは呼び出し側の仕事

関数funcを呼び出す

	'c'i
	nta f5 4	; スタックを準備
	inj f5@ xx func	; リターンアドレスを積んで、同一命令で関数のアドレスにジャンプ
	ata f5 4	; スタックからリターンアドレスを削除する

aとbを交換

	'c'i
	inj a b a

フラグをレジスタに取り出す(xxレジスタ以外)

	'c'i
	krz f0 0
	fi * * *
	malkrz f0 1

f0を8bitの値と見て32bitに符号拡張する

	'i'c
	dro 24 f0
	krz8i f0 f0

左シフトして右シフトすれば符号拡張も0拡張もできるが、この方法だと即値を1つ削減できる。

f0を16bitの値と見て32bitに符号拡張する

	'i'c
	dro 16 f0
	krz16i f0 f0

16bitと16bitを連結して32bitに (下位:f0, 上位:f1, 結果:f0)

	'i'c
	krz16c f1 f0

f0が0でなかったら無限ループに突入

'c'i 
nll jiesesn   fi f0 0 niv   malkRz xx jiesesn