em1keyの定義記述法メモ&変数と命令の絡み方メモ。

(未来:em1keyの定義記述法メモ&変数と命令の絡み方メモ、第2回。)
(原典: http://flatlib.main.jp/dench/vec/more/em1keyscript.txt )

 oyayubiwmのスクリプトを読んでいて「ハマッていた→なんとなく理解できた」ところについて、メモしておくことにしました。
 ここに記述していない命令などについては、ほかの言語で学習した知識をそのまま使えば何とかなりそうな気がします……。


 em1keyが「汎用言語エンジン」ではなく「キー配列入れ替えエンジン」として成り立っている一番の要因は、「キーコードに依存したイベントドリブン関数群」「キーボードにより入力されたキーコードにより呼び出されるイベントドリブン関数群」にあるようです。
 ……ということは、スクリプトと解説から理解しようとするよりも、「em1keyがどういう概念で設計され、どういう挙動で動いているのか」を想像してみるほうが、より早期に挙動を理解できるのかもしれません。


 ……とはいえ、このあたりの概念や挙動がわかったからといって、自由に定義を書ける……というわけではないようですが^^;。
 以下、マニュアルの説明を「変数の順番」に並べ替えつつ、(無理なく続けられる 年収10倍アップ勉強法の受け売りそのままに)自分の言葉で書き直してみました。 
 とりあえず、以下のメモで扱っているのは「処理」「出力」だけです。
 まだ「入力」「分岐」については記述していませんし、「入力」のキーDownとキーUpを分離する方法などについては理解できていません。

「TABLE_FUNC」「TABLE_DEFAULT」「TABLE_EXT」「TABLE_ALLMODE」の関係について。

 基本的にem1keyのスクリプトは、「TABLE_DEFAULT」「TABLE_EXT」で始まるイベントドリブン関数(キー入力によって呼び出される)と、「TABLE_FUNC」で始まる任意関数の組み合わせで定義されています……たぶん。

#------------------------------------------------------------------------------
# 左手
#------------------------------------------------------------------------------

func _SELTABLE 'Q'
	ALLOCVSTATE	0
	SETVAR0	_kana_MARU	# 。
	SETVAR1	_kana_XA	# ぁ
	SETVAR2	_kana_NOP
	SETVAR3	_kana_NOP
	CALL	_subLEFT
endfunc

(from oyayubiwm1.10 - scriptcommand.txt )

 「_SELTABLE」なんてfunctionはない……のだけれど、上のほうにこんな行がある。

define	_SELTABLE	TABLE_EXT

(from oyayubiwm1.10 - scriptcommand.txt )

 要するに、「_SELTABLE」は「TABLE_EXT」の事を指してるよ〜という話。
 em1keyでは、キーを押す&離すたびに「TABLE_DEFAULT」か「TABLE_EXT」のどちらか片方だけが呼び出される。この2種類のイベントドリブン関数群が主役なのだけれど、どちらを呼び出すのかは「SETTABLE」で決められる……と。

func TABLE_FUNC	0
	# 初期状態で使用するキーテーブルの選択
	#   1=TABLE_DEFAULT
	#   2=TABLE_EXT
	# 1 にすると 親指シフト入力 が off になります。
	SETTABLE	2
endfunc

(from oyayubiwm1.10 - scriptcommand.txt )

 たとえば、「TABLE_DEFAULT」には【ひらがな入力モード】用のデータを定義して、「TABLE_EXT」には【英数入力モード】用のデータを定義する……という使い方をすることができる。
 そして、「TABLE_DEFAULT」と「TABLE_EXT」の両方に全く同じ定義を書くのは面倒だよ!という場合は、「TABLE_ALLMODE」を使って「TABLE_DEFAULT」と「TABLE_EXT」の両方に同じ定義を行うことができる。


 「英数キーを押したら【SETTABLE 2】にする」&「ひらがなキーを押したら【SETTABLE 1】にする」ようにしてやれば、「英数←→かな」切替のために内部変数を使ったりすることなく、簡単に2つのモードを切替できる。
 ……って、これはかなり便利そうな予感。ただし、一つだけ注意点があって……冒頭にあるdefine定義では

# Function Table
define	TABLE_FUNC		1
define	TABLE_DEFAULT	2
define	TABLE_EXT		4
define	TABLE_KANA		4
define	TABLE_ALLMODE	6

(from oyayubiwm1.10 - scriptcommand.txt )

こう書かれていたりする。
 SETTABLE の引数として使えるマジックナンバーの意味と、あらかじめ「TABLE_DEFAULT」と「TABLE_EXT」というdefine済みの定数が持つ数字では対応関係がずれている。
 したがって、【SETTABLE 2】と書くつもりで【SETTABLE TABLE_EXT】と書くと、実際には【SETTABLE 4】と解釈されてしまい無意味になる。
 また、【SETTABLE 1】と書くつもりで【SETTABLE TABLE_DEFAULT】と書くと、実際には【SETTABLE 2】と解釈されてしまい誤動作する……と。
 ここのテーブルでは「TABLE_EXT」と「TABLE_KANA」が同じ値をとっているから、もしかすると

  • 「TABLE_DEFAULT」には英数配列を定義する。
  • 「TABLE_EXT」にはカナ配列を定義する。

……とするほうがよいのかもしれない。


 とすると、パソコンで一般的なMS-IMEの挙動にあわせて、「Shiftキーを押しながら文字キーを押すと、英字シフト側を出してから【SETTABLE 2】にする(簡易英数モードへ移る)」&「Shiftキーのみを単独でDown→Upすると、【SETTABLE 1】にする(カナモードに戻る)」という仕掛けを、em1keyで再現することができる。
 繭姫/姫踊子草が実現している「MS-IME互換の英数かな簡易切替」が実現できる……となると、「飛鳥カナ配列」では「英数/かな」キーの制御を端折っても「和英交ぜがき入力」ができるわけで。


 「TABLE_FUNC」はキー入力によるイベントドリブンには対応しない関数で、こちらは「TABLE_DEFAULT」か「TABLE_EXT」から呼び出されない限りは参照されない……と。
 繰り返しになるけど、「TABLE_ALLMODE」というのは「TABLE_DEFAULT」と「TABLE_EXT」の両方に「同じ事を定義する」というだけのことなので、「TABLE_ALLMODE」という関数があるわけではない(と考えないとややこしい)。


 続いて、特殊な「TABLE_FUNC」について。
 前述どおり、本来「TABLE_FUNC」は呼び出さなければ使われない……のだけれど、(マニュアルにあるとおり)明示的に呼び出しを行わなくとも、以下の関数は以下のタイミングで勝手に実行される

  • 「TABLE_FUNC 0x00」──em1keyのスクリプトを読み込んだ直後に、無条件で実行される。
  • 「TABLE_FUNC 0x01」──キー入力用のイベントが発生する

 もちろん、これらの関数は「普通のTABLE_FUNC」なので、明示的に呼び出しても使えるはず……だけれど、よほど関数に余裕がない場合を除けば「この関数は普通の関数から呼び出したりはしない」ほうが良さそう。

どこでも使える(Globalな)変数名と、それに絡む命令。

(2007年5月7日1:59:51追記、記述順序などを修正)
(2007年5月9日10:16:30追記、CALL関数の説明&LOAD関数の目次が飛んでいたので復帰、VARをVERと誤記していた部分数カ所を訂正)

  • カレントレジスタ(current resistor) ── 32ビット。提供元を指定しない演算&書き込みには、全てここの値が使われる。また、格納先を指定しない演算&呼び出しの結果は、全てここに収容される。この「カレントレジスタ」の概念がわかっていないと話が始まらないので要注意。
  • VAR0〜31 ── 8bit容量の変数32個分。内部的には32bitで扱われているが、8bitを越える部分にはアクセスできない。
    • SETVAR0〜3 ── 値を直接代入できる……けど、VAR0 〜VAR3のみ。たくさんのレジスタを使う場合は、SETVAR0〜3を使わずに、「LOAD 0x値」→「STOREVAR 0〜31」の2段階でアクセスするほうが解りやすい。
    • CALLVAR 0〜31 ── TABLE_FUNC 関数群のうち、VAR0〜31に格納された値の関数を呼び出す。
      • VAR16に格納された値と同じ番号の TABLE_FUNC 関数を呼び出す場合は「CALLVAR 16」とする。仮にVAR16に「0x20」が格納されていれば、呼び出される関数は「TABLE_FUNC 0x20」である。
      • マジックナンバーやDefine定数で TABLE_FUNC 関数を呼び出す場合は、CALLVAR命令ではなくCALL命令を使う。
    • CALLVARDEF 0〜31 ── TABLE_DEFAULT 関数群のうち、VAR0〜31に格納された値の関数を呼び出す。
      • VAR16に格納された値と同じ番号の TABLE_DEFAULT 関数を呼び出す場合は「CALLVARDEF 16」とする。仮にVAR16に「0x20」が格納されていれば、呼び出される関数は「TABLE_DEFAULT 0x20」である。
      • マジックナンバーやDefine定数で TABLE_DEFAULT 関数を呼び出すことはできない。
    • CALLVAREXT 0〜31 ── TABLE_EXT 関数群のうち、VAR0〜31に格納された値の関数を呼び出す。
      • VAR16に格納された値と同じ番号の TABLE_EXT 関数を呼び出す場合は「CALLVAREXT 16」とする。仮にVAR16に「0x20」が格納されていれば、呼び出される関数は「TABLE_EXT 0x20」である。
      • マジックナンバーやDefine定数で TABLE_EXT 関数を呼び出すことはできない。
    • SENDVAR 0〜31
      • VAR0〜31の中身を、キーコードとみなして送信する。
      • VAR16に格納された値をキーコードとみなして送信する場合は、「SENDVAR 16」とする。仮にVAR16に「0x20」が格納されていれば、この命令は「SENDKEY 0x20」と同じ役割を果たす。
    • DOWNVAR 0〜31
      • VAR0〜31の中身を、キーDownコードとみなして送信する。
      • VAR16に格納された値をキーDownコードとみなして送信する場合は、「DOWNVAR 16」とする。仮にVAR16に「0x20」が格納されていれば、この命令は「RAWDOWN 0x20」と同じ役割を果たす。
    • UPVAR 0〜31
      • VAR0〜31の中身を、キーUpコードとみなして送信する。
      • VAR16に格納された値をキーUpコードとみなして送信する場合は、「UPVAR 16」とする。仮にVAR16に「0x20」が格納されていれば、この命令は「RAWUP 0x20」と同じ役割を果たす。
    • LOADVAR 0〜31【?】 ── VAR0〜31の値を、カレントレジスタに読み込む。
    • STOREVAR 0〜31【?】 ── VAR0〜31に対して、カレントレジスタの値を書き込む。
    • LOADIVAR 0〜31【?】 ── VAR0〜31に書き込まれている値を見て、その値が指し示すVAR0〜31の値を、カレントレジスタに読み込む。
      • 【?】VAR16に格納された値を使ってVAR0〜31のどれを使うかを指定する場合には、「LOADIVAR 16」とする。仮にVAR16に「0x02」が格納されていれば、この命令は「LOADVAR 2」と同じ役割を果たす。
    • STOREIVAR 0〜31【?】 ── VAR0〜31に書き込まれている値を見て、その値が指し示すVAR0〜31に対して、カレントレジスタの値を書き込む。
      • 【?】VAR16に格納された値を使ってVAR0〜31のどれを使うかを指定する場合には、「STOREIVAR 16」とする。仮にVAR16に「0x02」が格納されていれば、この命令は「STOREVAR 2」と同じ役割を果たす。
    • CMPVAR_EQ 0〜31 ── カレントレジスタに対してVAR0〜31の値が等しければ、カレントレジスタは0以外に置き換え。等しくなければ、カレントレジスタの値は0に置き換え。
    • CMPVAR_LT 0〜31 ── カレントレジスタに対してVAR0〜31が大きければ、カレントレジスタは0以外に置き換え。等しくなければ、カレントレジスタの値は0に置き換え。
    • CMPVAR_GT 0〜31 ── カレントレジスタに対してVAR0〜31が小さければ、カレントレジスタは0以外に置き換え。等しくなければ、カレントレジスタの値は0に置き換え。
    • ADDVAR 0〜31 ── カレントレジスタに対してVAR0〜31を足し算し、VAR0〜31を計算結果で置き換える。
    • SUBVAR 0〜31 ── カレントレジスタに対してVAR0〜31を引き算し、VAR0〜31を計算結果で置き換える。
    • ANDVAR 0〜31 ── カレントレジスタに対してVAR0〜31をAND演算し、VAR0〜31を計算結果で置き換える。
    • ORVAR 0〜31 ── カレントレジスタに対してVAR0〜31をOR演算し、VAR0〜31を計算結果で置き換える。
    • XORVAR 0〜31 ── カレントレジスタに対してVAR0〜31をXOR演算し、VAR0〜31を計算結果で置き換える。
    • MULVAR 0〜31 ── カレントレジスタに対してVAR0〜31を掛け算し、VAR0〜31を計算結果で置き換える。
    • DIVVAR 0〜31 ── カレントレジスタに対してVAR0〜31を割り算し、VAR0〜31を計算結果で置き換える。
    • MODVAR 0〜31 ── カレントレジスタに対してVAR0〜31を余り算し、VAR0〜31を計算結果で置き換える。
    • SLVAR 0〜31 ── カレントレジスタに対してVAR0〜31を左シフト演算し、VAR0〜31を計算結果で置き換える。
    • SRVAR 0〜31 ── カレントレジスタに対してVAR0〜31を右シフト演算し、VAR0〜31を計算結果で置き換える。
  • VAR32 = ○○GSTATE 0〜31 ── 32bit容量のグローバル変数VAR32を「32個のフラグ」とみなして使っている。
    • いずれも、○○GSTATE のあとに半角空白を置いてからGSTATEの番号(0〜31)を指定する……のだけれど、マジックナンバーを使うと「何を表す数字なのかがわかりにくく」メンテナンスしづらいので、実際には事前にDefineで定数を決めておいて、ここではその定数の名前を使う。
    • フラグに「1」を格納する命令は【SET】GSTATE。
      • GSTATE16(VAR32-16)に「1」を格納する場合は「SETGSTATE 16」とする。
    • フラグに「0」を格納する命令は【RESET】GSTATE。
      • GSTATE16(VAR32-16)に「0」を格納する場合は「RESETGSTATE 16」とする。
    • フラグ読み出しは【TEST】GSTATE、データはカレントレジスタへ。
      • GSTATE16(VAR32-16)の中身をカレントレジスタにコピーする場合は「TESTGSTATE 16」とする。
  • VAR33 ── SaveTime (SAVETIME 命令で保存したキーイベント発生時の時間) 【オリジナルのまま】
    • SAVETIME 0を使って書き換える。
  • VAR34 ── (SETDIFFTIME 命令で設定した時間値、msec 単位)【オリジナルのまま】
    • SETDIFFTIME <差分時間>
  • VAR35 ── Modifier (SETMODIFIER 命令で設定したパラメータ)【オリジナルのまま】
  • VAR36 ── DataStackPointer (PUSHVAR/POPVAR 命令で書き換わる)【オリジナルのまま】
  • VAR37 ── KeyCode (呼び出したとき押されていたキーコード)【オリジナルのまま】
  • VAR38 ── EventTime (キーイベントが発生した時間)【オリジナルのまま】
  • VAR39〜47 ── 使用禁止(将来のための予約領域)【オリジナルのまま】
  • VAR48〜79 ── PushVAR/PopVARのデータスタック用。【オリジナルのまま】
    • PUSHVAR 0〜127 ── カレントレジスタのデータを、データスタック(先入れ後出し先入れ先出し方式のデータ領域)に「コピー」する。このときカレントレジスタの中身は変わらない【?】。
      • OSのシェルコマンドにあるようなPushd/Popd(アレは先出し後入れ)ではないので、誤解しないよう注意。
      • (2007年5月21日0:21:09追記)oyayubiwm(v1.42)/line801-815を見る限り、PUSHVARとPOPVARの関係は「OSのシェルコマンドにあるそれと同じ」ようです。
    • POPVAR 0〜127 ── データスタック(先入れ後出し先入れ先出し方式のデータ領域)に突っ込んであるデータを、カレントレジスタに「移動」する。このときカレントレジスタは、一番「最後に」「最初に」入れていたデータが消える。
      • OSのシェルコマンドにあるようなPushd/Popd(アレは先出し後入れ先入れ後出し)ではないので、誤解しないよう注意。
      • (2007年5月21日0:21:09追記)oyayubiwm(v1.42)/line801-815を見る限り、PUSHVARとPOPVARの関係は「OSのシェルコマンドにあるそれと同じ」ようです。

今後の目標。

  • 新JISかな配列(JIS X 6004)の実現。
  • 親指シフトの「飛鳥カナ配列」の実現。

 ……ただし、予定は未定。