access logger

家庭コンピュータ環境の模索 >WP34sという電卓 >E24マクロの作り方
最終更新: 2013-03-02


E24マクロの作り方

電子部品の定数は、一般的にE24系列という数値に丸められたものしか作られていない。たとえば、123.456kΩの抵抗が欲しくても、120kΩと130kΩしかなく、その間の部品は存在しない。設計に便利なようにE24系列に丸めるマクロ E24 をつくって見た。それを題材に、WP34sのマクロについて解説する。Windows で動く電卓アプリ iMemo にも同様の関数が存在する。

本稿ではマクロ、ライブラリ、コマンド、関数はなどの用語が同じ意味で使われている。本質的には同じなのだが、文脈や話の流れで違う単語が使われることもある。ごめんなさい。

WP34s ではいろいろな場面でアセンブラが使われる。emacs における elisp のように。

trunk/xrom/ 以下のアセンブリコードは C で書かれた組み込み関数と同様に扱われる。さらに、通常のアセンブラでは使えない特殊命令も使える。組み込み関数でアセンブラを使うことは、ROM容量の削減に役立っている。

library/ 以下の関数はユーザが打ち込んだマクロと同じように動作するが、Flash に焼き込まれるので、電池を外しても、ファームアップデートをしても変わらない。

もちろん、ユーザが打ち込んだマクロも同じように使える。それは、通常、バッテリーバックアップされたRAM領域に格納される。SAVE コマンドを使うことによって、Flash 領域(FM)に保存することもできる。

実機で入力する。

まずは実機に入力してみる。作成したいのはE24 だが、テーブルが長いので、以下ではE6 で説明する。

それぞれ、E6.wp34sE24.wp34sからダウンロードできる。

リンク先にあるものは、改良によってバージョンアップしてあるので、以下の解説と整合しない。解説のもとになっているバージョンはこちら。

/*
Input: X reg
Output: X reg <- E6(Input)
E6:  1.0 1.5 2.2 3.3 4.7 6.8
E24: 1.0 1.1 1.2 1.3 1.5 1.6  1.8 2.0 2.2 2.4 2.7 3.0
     3.3 3.6 3.9 4.3 4.7 5.1  5.6 6.2 6.8 7.5 8.2 9.1
 */
// TODO
// ローカル変数を使ってスタックの消費を減らす。
// 継続して計算ができるように、スタックのゴミをなくす。
// テーブル、間接アドレッシング化。
// E6〜E24 の統合、E96の追加
	LBL'E6'
	SSIZE8
	EXPT
	RCL L
	MANT
// X = MANT(orig X), Y=EXPT(orig X)
        ENTER[^]
	ENTER[^]
	# 010	// begin
	# 015
	XEQ 00
	RTN	// end
	# 015	// begin
	# 022
	XEQ 00
	RTN	// end
	# 022	// begin
	# 033
	XEQ 00
	RTN	// end
	# 033	// begin
	# 047
	XEQ 00
	RTN	// end
	# 047	// begin
	# 068
	XEQ 00
	RTN	// end
	# 068	// begin
	# 100
	XEQ 00
	RTN	// end
	# 100	// begin
	# 150
	XEQ 00
	RTN	// end
	LBL 00	// CHOISE
	LocR 001
	x[<->] Y
	STO .00
	+
	# 020
	/
	x>0?
	SKIP 008
	DROP
	RCL Y
	1
	-
	10[^x]
	RCL .00
	[times]
	RTN
	DROP
	ENTER[^]
	RTN+1
	END	// Program Separator
アセンブラキー画面説明
[h][P/R]
[h][GTO][.][.][▼]
[h][P/R] は、プログラムモードに入る、または、出るコマンド。
[h][GTO][.][.]は、最終行に移るコマンド。そこから、さらに一行下がる[▼]ことによって、最新行に移る。
表示は、今入力しようとしているのが000行目であることを意味している。指数表示部に437と表示されているが、これは残りメモリが437行であることを示している。
/*
Input: X reg
Output: X reg <- E6(Input)
E6:  1.0 1.5 2.2 3.3 4.7 6.8
E24: 1.0 1.1 1.2 1.3 1.5 1.6  1.8 2.0 2.2 2.4 2.7 3.0
     3.3 3.6 3.9 4.3 4.7 5.1  5.6 6.2 6.8 7.5 8.2 9.1
 */
// TODO
// ローカル変数を使ってスタックの消費を減らす。
// 継続して計算ができるように、スタックのゴミをなくす。
// テーブル、間接アドレッシング化。
// 小数定数の整数化
// E6?E24 の統合、E96の追加
コメント
C スタイル(/* */)、C++スタイル(// \n)のコメントが使える。 実機に入力する時は不要。
LBL'E6'
[f][LBL] まで押すと次のような画面になる。この次は、ラベル名を入れるのだが、数字ラベルと文字ラベルがある。
よく見ると、右上のSTOセグメントが消灯しているが、これは行を入力中のことを示している。
[ENTER] [ENTER] を押すと、「'」が自動入力され、キーボードは alpha モードになっている。
[E][f][6][ENTER] alpha モードで、2から9の数字を入力するときは[f]シフト、ギリシャ文字を入力するときは[g]シフトを使う。0,1の数字は alpha モードでも直接入力できる。ラベルは3文字まで使える。3文字入力した場合は、自動的に「'」が閉じられ、行入力が完了するが、2文字で終了する場合は[ENTER]を押す。 行入力が完了すると、ステップカウンタが1増え、次の行の入力待ち状態となる。
SSIZE8
[h][MODE][S][▼]...(SSIZE8)[XEQ] スタックサイズを8にする。
EXPT
[h][X.FCN][E][▼]...(EXPT)[XEQ] EXPT は指数部を得る関数。
RCL L
[RCL][L] RCL L はlast x と同じ。
MANT
[h][X.FCN][M][ENTER] MANT は仮数部を得るコマンド。
この時点で、step 005 となって、4行目までプログラムが入っていて次は5行目であることを示している。また、スタックは X レジスタが仮数部、Yレジスタが指数部が入っている。
ENTER[^]
ENTER[^]
[ENTER][ENTER] X レジスタを複製する。
# 010
[h][CONST][▲]...(#)[ENTER][0][1][0] 本来であれば
1
0
ENTER[^]
のように3行で入力しなければならない。しかし0から225までの整数定数は # ???で1行で入力できるので、うまく使えば行が節約できる
# 015
XEQ 00
[XEQ][0][0] 下にある、LBL 00 のサブルーチンを実行する。
XEQ で実行した場合は RTN で戻ってくるサブルーチン呼び出し、GTOは戻ってこないジャンプになる。数字ラベルは00から99までの2桁。2桁入力すると自動的に入力完了になる。
RTN
[g][RTN] LBL00 サブルーチンから、RTN 命令で、ここに戻ってくる。
さらに、LBL'E6'サブルーチンから戻る。
# 015
LBL00 サブルーチンから、RTN+1命令でリターンした場合は、ここに戻ってくる。
次の境界値を積み始めている。
# 022
XEQ 00
次の境界値を積み終わったら、LBL00 サブルーチンを呼ぶ。
RTN	// end
# 022
# 033
XEQ 00
RTN	// end
# 033
# 047
XEQ 00
RTN	// end
# 047
# 068
XEQ 00
RTN	// end
# 068
# 100
XEQ 00
RTN	// end
以下、境界値を変えながら、繰り返す。
入力時の編集コマンドについて。
[←]は行削除。 [▲][▼]は行移動。
# 100
# 150
XEQ 00
RTN	// end
終了段階では次のように step035 になっているはずである。
# 100 と # 150のペアは、(68+100)/2=84 より大きかったときに、(100+150)/2=125よりは必ず小さいために引っかかる「番兵」である。
LBL 00	// CHOISE
[f][LBL][0][0]数字ラベルは2ケタなので、[0][0]と押した時点で行完了する。
数字2桁のラベルはローカルラベルを表す。
この LBL 00 サブルーチンは、X=上側閾値、Y=下側閾値、Z=仮数部、T=仮数部、A=指数部、で上側と下側の平均値を10で割ったものが仮数部より小さければ下側閾値X10^(指数部-1)をXレジスタに返す。そうでなければ RTN+1する。
LocR 001
[h][P.FCN][L][▼]...(LocR)[XEQ][0][0][1]ローカル変数(.00〜)を使う個数を宣言する。
プログラム機能に関するものは、[P.FCN]の中に入っている。
x[<->] Y
STO .00
[h][x<>][Y]
[STO][.][0][0]
境界値の下の方の引数を、ローカル変数 .00 に保存する。
+
# 020
/
境界値(X レジスタと Y レジスタに入っている)を平均して10で割る。
-
境界値の平均値と仮数部を引き算する。
x>0?
[h][TEST][X][▼]...(x>?)[XEQ][0] 比較関数は[TEST]に入っている。
結果が 0 より大きいか?(平均値より仮数部が大きいか?)
SKIP 008
もし、平均値より仮数部が大きいなら、8ステップ飛ばす。つまり、下の方のDROPに飛ぶ。そうでないなら、次の行を実行。
DROP
この行には、平均値より仮数部が小さいときに来る。DROP命令でXレジスタに入っている「差」を捨てる。
RCL Y
Y レジスタには、EXPTで求めた指数部が入っている。
1
-
位を揃えるために 1 引く。
10[^x]
[10x]指数部を計算する。
RCL .00
境界値の下側の値を取り出す。
[times]
[X]指数部と仮数部を掛ける。アセンブラでは[times] というニモニックで表される。
RTN
丸めた値を返す。これは、XEQ 00 で呼ばれた次の行に帰る。
DROP
ENTER[^]
STEP 008 で飛んだら、ここに来る。つまり、平均より仮数部が大きかった時。
スタックを、X=仮数(平均と引き算するよう)、Y=仮数(次のステップにとっておく)、Z=指数となるように整える。
RTN+1
XEQ 00 で呼ばれた次の次の行に帰る。帰った先では、次の境界値が積まれる。
END	// Program Separator
END はプログラム終了の区切り。

実行してみよう。

[h][P/R]でプログラムモードを抜ける。

[1][1][1][1][1] 11111 を E6 で丸めてみよう。
[h][CAT][▼]...(LBL'E6')[XEQ] 10kΩに丸まる。表示形式は ENG 6 になっている。
[5][0] 50Ωが欲しいのだけれど…
[h][CAT][▼]...(LBL'E6')[XEQ] そんな中途半端な値はない。
私の環境では、ドットマトリクス部に Y レジスタが表示されるようになっている。

ステップ実行してみよう。

実行モードにして、[▲][▼]を押して、001:LBL'E6'が表示されている状態にする。
[1][1][1][1][1][▼] [▼]を押すごとにステップ実行される。QtGui版では右側にレジスタが表示されるのでデバッグに最適。

エミュレータを使う。

お気づきかと思うが、上で「実機を使う」となっているにも関わらず、すべてエミュレータ上での実行であった。QtGui を使うときの注意点がひとつ。Edit→Preference→Kyeboard→Use H-shift click というのがある。これは、キーの下半分の緑色のところを押したら、h シフト状態で押したのと同じに扱う、という事。便利な反面、操作ミスにもつながる。

いちいちキー入力をしなくても、エディタでファイルを編集して、コンパイルし、それをエミュレータのライブラリディレクトリにコピーして、エミュレータでデバッグ実行しても良い。つまり、次のようなスクリプトを実行する。

#!/bin/sh
cd library
DEBUG=0
TOOLS=../trunk/tools
ASM=$TOOLS/wp34s_asm.pl
DIR3=../trunk/QtGui/memory

$LIB -pp matrix.wp34s matrixedit.wp34s vectors.wp34s digamma.wp34s coordinates.wp34s -olib $DAT || exit
$LIB TVM.wp34s -ilib $DAT -olib $DAT || exit
$LIB -pp TRIGON.wp34s PF.wp34s -ilib $DAT -olib $DAT || exit
$LIB E24.wp34s -ilib $DAT -olib $DAT || exit

$LIB -cat -ilib $DAT >library.cat

cp $DAT $DIR3

保存する。

電源を切っても、RAM に保存されているプログラムは保存される。しかし、電池を交換すると忘れてしまう。SAVE コマンドを使えば、RAM のデータを Flash メモリ(FM)に保存できます。

library/ に置く。

デバッグができたら、いっそのこと library/ に置いて、ROM に焼き込んでしまおう。makelib.sh などというスクリプトがあって、library/ 以下のファイルのアッセンブル、Cからコンパイルされたバイナリとの結合などの作業を行なっている。標準の SVN では makelib.cmd という、バッチファイルなのだが、Linux 環境で build するための sh スクリプトファイルを作成してあるのだ。

#!/bin/sh
cd library
#DEBUG=3
DEBUG=0
TOOLS=../trunk/tools
ASM=$TOOLS/wp34s_asm.pl
LIB="$TOOLS/wp34s_lib.pl -d $DEBUG"
PP=$TOOLS/wp34s_pp.pl

DIR1=../trunk/windows/wp34sgui
DIR2=../trunk/realbuild
DIR3=../trunk/QtGui/memory

DAT=wp34s-lib.dat

$LIB -pp matrix.wp34s matrixedit.wp34s vectors.wp34s digamma.wp34s coordinates.wp34s -olib $DAT || exit
$LIB TVM.wp34s -ilib $DAT -olib $DAT || exit
$LIB -pp TRIGON.wp34s PF.wp34s -ilib $DAT -olib $DAT || exit
$LIB E24.wp34s -ilib $DAT -olib $DAT || exit

$LIB -cat -ilib $DAT >library.cat

cp $DAT $DIR1
cp $DAT $DIR2
cp $DAT $DIR3

cd ../trunk/realbuild
cat calc_xtal.bin $DAT > calc_xtal_full_my.bin

xrom/ に置く。

xrom/ に置かれた関数は、組み込み関数と同等に扱われる。さらに、xrom/ でしか使えない特殊な命令も使える。あんまり自分で触っていないのでよくわからない。でも、trunk/xrom/who.wp34s に自分の名前を表示させたりして自己満足に浸っているのだ。

[] [簡易マニュアル] [How to Develop] [コレクション]



近藤靖浩
Last modified: Sat Mar 10 08:19:20 JST 2012