マクロ名がわかっているときは,モジュール検索関数 &Zs()
を使います。
1 ^\ "" ?.
p="abcdef",
&Zs(p) ;返り値rはモジュールの先頭番地
&m("r:%xh rr:%xh",r,rr) ;マクロ実行ポインタrrもセットされる
注意すべき点は,この関数はマクロ名を英字の大小文字同一視で検索します。これは妥当な仕様ではありますが,もしもマルチバイト文字のマクロファイル名であったりすると,文字コードの上位バイトが同じで下位バイトが41h〜5Ah(ABCDEFGHIJKLMNOPQRSTUVWXYZ)の文字と61h〜7Ah(abcdefghijklmnopqrstuvwxyz)の文字が同一視されます。具体的には,「オカ」「オニ」「ナカ」「ナニ」はすべて同一視されます。「VWX」と「678」も同じものとして扱われます。これらはすべてが同一視される極端な例ですが,文字列のうちの1文字だけ異なっても同一視されるわけですから,マクロ名に漢字などのマルチバイト文字を使わない方が安全でしょう。
同一視される危険のある対の文字の例です。上段・下段で対になっています。
0123456789 PQRSTUVWXY アィイゥウェエォオカガキギクグケゲコゴサザシジスズセ ヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホ 陰隠韻吋右宇烏羽迂雨卯鵜窺丑碓臼渦嘘唄欝蔚鰻姥厩浦瓜 餌叡営嬰影映曳栄永泳洩瑛盈穎頴英衛詠鋭液疫益駅悦謁越
なお,モジュールに含まれるグローバルマクロの先頭を求めるには,&Zs()
のもうひとつの引数で指定します。
&Zs("abcdef",3) ;引数は0から開始(この例は4番目のマクロ)
マクロバッファの中で何番目か。それがわかるというのがどういう状況下かと言うと,モジュールメニュー(マクロメニュー)です。メニューの中で特定のアイテムを選択すると,返り値として上位アイテムから順に0,1,2…が得られます。これにより何番目のモジュールかがわかるというわけです。ただしモジュール検索関数&Zs()
に順位を引数として渡せるわけではないので,モジュールヘッダをはじめとする構造の知識と,少々面倒な手続きが必要です。
先頭のモジュールヘッダのアドレスはmhという変数に格納されています。mhは,通常は最初に読み込まれる基本設定ファイルとしてのvz.defのモジュール先頭を指しています。さて,個々のモジュールの先頭番地を直接取得することはできません。それではどうするかと言うと,モジュールヘッダの中に次のモジュール先頭へのリンクポインタが記録されているので,そのリンクをひとつずつたどって目的のモジュールに到達するのです。
モジュールヘッダの構造は次のようになっています。
+0 | B | モジュールヘッダ番号 | 0FFh(最後のメニュー#31に相当) |
+1 | W | モジュールヘッダのサイズ | |
+3 | B | モジュールフラグ | +1:Sleep,+2:Removable,+4:External,+80h:keyboard macro |
+4 | W | 次のモジュールヘッダへのリンクポインタ | |
+6 | W | モジュールIDの長さ | |
+8 | n(1〜8) | モジュールID(8文字以内のファイル名) | |
+n+9 | m(不定) | モジュールのタイトル文字列 |
モジュールの先頭をpとしたとき,p..2
に格納されている値が次のモジュールの先頭アドレスです。p..2..2
がその次の,p..2..2..2
がそのまた次のモジュールの先頭というふうにリンクしていきます。したがって,例えばn番目のモジュールの先頭番地を求めるには,次のようなコードを使います。確認のためモジュールIDも表示しておきます。
1 ^\ "" ?.
&g("何番目のモジュール?")
(r<1)?.
n=r-1,
p=mh,
(n)?{ (n){ p=p..2,} }
&m("%d番目のモジュールの先頭番地:%04xh (ID:%s)",n+1,p,p+8)
実際には数えたりするのは面倒なので,モジュールメニューで選択するのが簡単でしょう。モジュールメニューを利用すると次のようになります。
1 ^\ "" ?.
!97 (r<0)?.
p=mh, (r)?{ (r){ p=p..2, } }
&m("%d番目のモジュールの先頭番地:%04xh (ID:%s)",r+1,p,p+8)
モジュール削除関数&Zd()
は,実行中のモジュールよりも先に読み込まれているモジュールを削除しません。例えば次のようなマクロをロードして,モジュールメニューで選択したマクロ以降のマクロをすべて削除しようとしても,最後に読み込んだこのマクロが実行中になるので期待する動作をしてくれません。
1 ^\ "" ?.
!97 (r<0)?.
&Zd(r)
関数&Zd()
の仕様を越えてゆくためには,裏技に頼る以外にありません。裏技に頼るということは,m.gann師のワザに頼るということと同義かもしれませんね。
m.gann師の編み出したワザというのは,削除したいモジュールヘッダの先頭を破壊しておいて,ダミーのモジュールをマクロバッファ以外の場所に作成し,その削除を行うことでモジュールのリンクを再設定させ,結果的に破壊したヘッダ以下のモジュールを削除するというものです。(先頭の破壊といっても,レッテルを剥がすだけです。先頭にはモジュールであることを示すFFh(メニュー番号31に相当)があり,これをヌルに変更します。)
nifty:fgaldc/mes/15/04186(1997/01/17) 「モジュールを削除して遊ぼう」より
=del_mdl= モジュールの強制削除(削除不可モジュールを含む) *m 9 ^Q\ "" ; mh.0-, ;[;] いっさいの全マクロを削除 ; mh..2.0-, ;[;] 第2モジュール以下を削除 ; mg..2..3.0-, ;[;] このモジュール以下を削除 mg..2..3..2.0-, ;[;] このモジュール下方を削除 p=mh, n+, ;マクロバッファ先頭、カウンタ :R (r=p..2)?{ p=r, n++, >R } ;最後のモジュールをサーチ p..2=pu, pu.3=2, pu..2-, ;※puにダミーモジュールを作る &Zd(n) ;その削除(マクロリンク等の初期化) (3){ &b(5) &w(9) } ;[;] 自らを削除してもベルは鳴る *
この技術を応用して,モジュールメニュー選択版を作成すると次のようになります。最初に起動したモジュールメニューで選択したマクロ以降を,実行中のマクロを含めて削除します。
1 ^\ "" ?.
!97 (r<0)?. m=r+1,
p=mh, n+,
:R (r=p..2)?{ p=r, (n++==m)?p.0-, >R }
p..2=pu, pu.3=2, pu..2-,
&Zd(n)
え? 実行中マクロより前に読み込まれているマクロ【だけ】を選択削除したい?
こそこそ。((((((;^_^)
マクロバッファの初期化というのは,外部マクロの一括削除ということです。製品版添付のmac16.docに掲載されている「マクロの一括削除」は次のとおりですが,不具合が指摘されています。
* M 80 ^\ "マクロの一括削除" p=mh, c-, :A (p)?{ &Zd(p+8) (r)?? c++, p=(p+4)..0, >A} &m("%d 個のモジュールを削除しました",c)
メインとなるループの部分は次のように修正しなければならないそうです。
:A (p)?{ &Zd(p+8) (r)?p=(p+4)..0, ?c++, >A}
モジュール削除関数&Zd(n)
は,引数にモジュール番号あるいはモジュールIDを与えることができます。モジュールヘッダの8バイト先にあるのはモジュールIDで,マクロファイル名のファイル名部分です。
なお,モジュール削除関数&Zd(n)
は前項で述べたように,実行中のマクロ以前に読み込まれているモジュールは削除しません。一括削除マクロはvz.defなどの基本設定マクロに入っているなど,マクロバッファの先頭付近になければ意味がありません。
他に上村郁夫氏のマクロローダOUTLAWに付属のwhipでは,次のようにマクロバッファを初期化しています。
50 ^KO "whip" ; マクロバッファに鞭をいれる (128){ &Zd(mg..4) } ; 外部マクロ一括削除 r=34&jx, &^ jx|=r, ; オプション初期化/vw, zk は保護 &d &m("Bm:%u bytes free",bm) (ks&1)? cm=1,
これは理屈を抜きにして,榊原知氏の「自爆」ルーチンを使ってください。
q=mg..2..3+8, mg..2..3-, ; zibaku &Sc(pu,"&q&Zd(q).") &e(pu) ; by Tomo
この部品をマクロの最後に置くことで,マクロ実行後に自動削除することができます。