「セグメント」とは何ですか,という質問はFAQではありません。とても難しい概念なので詳しい説明はしませんが,とりあえずイメージするならば,16ビットで表現可能なオフセットアドレス空間の単位にメモリを切り分けたものと考えてください。
┌──────┬──────┬──────┬──────┬──┬──┐ │セグメント-A│セグメント-B│セグメント-C│セグメント-D│… │… │ ├──────┼──────┼──────┼──────┼──┼──┤ │0000h〜FFFFh│0000h〜FFFFh│0000h〜FFFFh│0000h〜FFFFh│… │… │ └──────┴──────┴──────┴──────┴──┴──┘
実際は,こんな単純ではないようですが,セグメント-Aにいながら,セグメント-Cのオフセットアドレスにアクセスできないことは容易に想像できると思います。通常は,マクロでオフセットアドレスを指定すれば,スタックセグメント(cz)の特定の位置を指すというのが暗黙の了解となっています。
さて,VZはv1.60でコードセグメントとデータセグメントを分離するなど,メモリ関係の大きな改訂が施されました。これにより広大(いや,v1.5xに比べたらですよ。)なマクロバッファを確保できるようになり,各種のワークエリアにもセグメントの変更なしにアクセスできるようになっています。
変数名 | セグメント名称 | 内容 |
---|---|---|
vz | コードセグメント | VZ.COMのコード領域 |
cz,gz | スタックセグメント | マクロバッファや各種ワークの領域 |
dz | データセグメント | ポインタ演算で使用する領域(スタックセグメントを指している) |
tz | テキストセグメント | テキストバッファの領域 |
kz | テキストスタックセグメント | テキストスタックの領域 |
fz | ファイラーセグメント | ディレクトリプールや画面退避用ワークの領域 |
異なるセグメントにある情報で,何となく気になるものと言うとテキストスタックなのではないでしょうか。例えば,ブロックしてコピーした文字列を検索文字列入力窓で使いたいと思ったことがないという人は,ほぼ皆無なのではないかと思います。通常は編集テキストにしかペーストできません。
これを解決した習作が,村上和生師匠のsyura01.defです。検索文字列入力窓で起動すると,テキストスタックの最新文字列を出力してくれます。
=== syura01.def === by ζ゜; ;・検索にテキストスタック最新文字列を利用 ;・文字境界ブロックでのみ作動 ;・改行を含む場合「\n」に置換($0a 不対応) ;・最大は検索窓幅(末尾全角片割れの場合は-1) *M 80^KF"" (kp)??>z ;テキストスタック? dz[=kz,a=kp..-1,b=kp.-3,c=kp-3-a,dz], ;a;サイズ,b;タイプ,c;アタマ (b-2||cm!=55)?>z ;文字列境界 & 検索窓以外ダメ &> i-, (a>39)?a39, ;標準検索窓幅−カーソル :a dz[=kz,q=c.i,dz],p.i=q,(i++<a)?>a ;セグメントまたぎは通常変数で媒介 (a>38)?{i--, ;↑ワカンナカッタ〜 (p.i>$80&&p.i<$a0||p.i>$df&&p.i<$fd) ?p.i-,?i++,}p.i-, :b &SF(p,$0d)(r)?{r..0='n\',>b} ;改行対策 &?("%s",p). ;(^^)v :z &b(3). 0: p3+$,."$[40]" ;上限バッファ以内 *
v1.60において,if文を使用して次のように機種判別する方法が導入されたことにより,例えば複雑なフラグ判定もできるようになりました。
1 [ESC]C "" ?.
#if 98
(ks&17)
#if DOSV
(ks&5)
#endif
?{ mr-, mx+, #C mx-, }
通常はこの if〜else〜endif による判定で十分かと思われますが,シンボルが少ない,もっと細かい機種判別をしたい,という場合に使えそうなのがpc.9
(ハードウェアID)です。
ソースのディレクトリを開いて,vz.incの74行目から始まる「Hardware ID」を参照してください。(ビット表示はめざらが付しました)
;--- Hardware ID --- ID_PC98 equ 00h ; 0000 0000 ID_PC98Hi equ 01h ; 0000 0001 ID_J31 equ 10h ; 0001 0000 ID_IBM equ 20h ; 0010 0000 ID_EGA equ 21h ; 0010 0001 ID_AX equ 02h ; 0000 0010 ID_DOSV equ 04h ; 0000 0100 ID_PS55 equ 28h ; 0010 1000 IDN_PC98 equ 0F0h ; 1111 0000
if文で使えるシンボルよりも少し詳しいハードウェアの種類が見てとれます。機種によっては,いくつかの値のビット和になります。最後のIDN_PC98は,98か98でないかを判断するために使います。なお,pc.9
はvz.defが読み込まれて有効になります。
例えば,
機種 | pc.9の値 | 内容 | ビット表示 |
---|---|---|---|
HP200LX | 36(24h) | ID_IBM(20h) OR ID_DOSV(04h) | 0010 0100 |
ThinkPad A21m | 37(25h) | ID_EGA(21h) OR ID_DOSV(04h) | 0010 0101 |
IDN_PC98による判定は次のとおりです。
(pc.9&$F0)?&m("PC98ではありません") ?&m("PC98です")
この内容は,nifty:FGALDC/MES/15/4711(1997/02/07)の高橋洋光氏のコメントを参考にしたものです。
スイッチds(システム情報の表示)をオンにすると,ステータスラインにカーソル位置の文字コード,編集テキストのサイズ,テキスト内でのカーソル位置,テキストスタックの使用量といったシステム情報が表示されます。
これらの情報はVZのワークエリアに保存されているわけではなく,取得のタイミングに応じて書き換えられているものです。したがって,それぞれの情報を個別に変数等を介して取得することはできません。
しかし目の前にあるものを利用したいというのは人情というものですね。
ステータスラインのシステム情報は,内部ワークエリアにある出力文字列展開用バッファを使用して表示しています。出力文字列というと関数&m()
によるメッセージであり,メッセージはpq..133
(printf buffer pointer)の指すバッファで一度展開されて出力されるわけですが,このバッファはステータスラインのバッファでもあります。
ステータスラインとメッセージラインに表示されるものを一時的に展開する作業エリアですから,再表示によって書き換えられた直後であれば,ドラフトが残っている可能性が高いということです。pq..133
の指すアドレスはpu+bu
(ユーザーバッファの先頭からバッファのサイズ分先:つまりユーザーバッファの直後)で,84バイトの大きさを持っています。
試しに,ここの内容をカーソル位置に出力してみましょう。
1 ^\ "" ?.
(mr==$80)?.
ds[+, #] ds],
&?("%84s",pu+bu)
出力してみると以下のような文字列となるでしょう。下段はゲージ代わりです。(高橋版VZですので残メモリ量でなく時刻が表示されています。)
9964 89% ( 0) 17:57 01234567890123456789012345
ファイルサイズは先頭から8桁,カーソル位置は9バイト目から2桁。テキストスタックの使用量は14バイト目から5バイトです。
もしも編集テキストのサイズを取得したければ,先頭から空白をスキップしてpu+bu+7
の位置までの文字列を取得し,必要があればロング整数にすればよいということになります。具体的な方法は,「ファイル」節の「編集テキストのサイズを取得するには」項,「移動・ジャンプ」節「ファイルの中間点に移動するには」項を参照してください。
なお,製品版VZ(v1.6)ではカーソルがファイルの末尾にあっても99%と表示されますが,高橋版VZではきちんと100%と表示されます。このときのpu+bu
にある文字列は'00'となっていてファイルサイズ表示との境界であるスペースはそのままです。したがって,ファイル末尾にあってもファイルサイズの文字列を比較的簡単に切り出すことができます。
VZは16MBまで(v1.6以降,v1.5までは特別な制限はなかった)のファイルを編集できますが,複数のファイルを編集可能であること,テキストバッファの大きさは制限があることなど,メモリ量の問題でディスク上に作業ファイルを作成することがあります。これがテンポラリファイルです。
テンポラリファイルが壊れるなどのエラーが出た場合は,編集ファイルを保存することなく,即刻VZを終了すべきであることはよく知られています。
テンポラリファイルの参照パス名は変数pqの指す番地にあり,ここにある'VZTEMP.$$$'という文字列はコードセグメントVZにある固定文字列が複写されているものですので,通常は変更することができません。シングルタスクのDOS環境では,そもそも変更の必要はありませんでした。
しかし近年はWindows環境のDOS窓(コマンドプロンプト)で利用されることも多くなり,異なるプロセスで起動されていても同じテンポラリファイルを取り合うという危険が生じました。このため,プロセス毎にテンポラリファイル名を変更する必要性が出てきたわけです。
上村郁夫氏や見米快之氏は早くからこの問題に取り組み,解決策を提示していました。次のマクロは起動時にテンポラリファイル名を変更するm.gann師の!Startイベントです。
1 "!Start" (z)??{ ax$2c00, &i($21) cx[, &Sy(pq) &Sp(bx,"VZ%04x.$$$$$$",r]) ;ren tmpfile (i6){ r=bx.i--, dz[=vz, bx.i=r, dz], } }
なお,高橋版VZにおいては,Windows環境で起動された時は固有のテンポラリファイル名を設定する機能が付加されています。もっとも,近年のWindows環境ではかなり大きなメモリを積むのがふつうなので,よほどメモリが足らなくならない限りテンポラリファイルは作られないのではないかと思われます。
メモリアロケーションエラーの起こりやすい状況というのは,VZをスワップさせないでDOSコマンドを実行したのちのファイラーで&z()
関数によるマクロ組込を行った場合とされています。DOSコマンド実行は,DOSコマンドを記述したマクロにより意識外で行われる可能性もあるので,なかなか状況を把握するということはできません。
したがって,DOSコマンドを実行する場合は常に先頭を大文字にし,VZをスワップさせるようにするのがよいでしょう。
なお,m.gann師によってマクロローダー組込型のメモリアロケーションエラー予防マクロ「malloc.def」が開発されています。これをマクロローダーに仕込んでおき,&z()
関数を使用する前に必ずコールすることで,メモリアロケーションエラーを防止する効果があるそうです。なお,このマクロ番号は他のマクロでは使わないようにする必要があります。マクロローダー以外で使用しないことが暗黙の了解事項になっている88番とセットで覚えるのがよいでしょう。
77 "" ;Malloc (222) ;for &z() bug fg..33-, ;init headerp (mg.14<pn.5)?{ :q &q &b(3). } ;to kbdmac dz[=pn..15~-1, (wm&&'Z'==0.) dz], ?. ;check wm / mcb_id { (s)>?{ . >q >03 } (cm<0)?>q &> #[ (s-2)?{ &> >q } &03 #98 >> ;filer <-> cmdline 2: (zk)??. ax83, &i($e0) ax88, &i($e0) ;ezkey for silent (98) 3: &s cp[, #> lx[, #H (wa[||z>Sz)?#[ ?#95 ;init allocate memory (s)?#[ ?#E wa], (r])?#u cp], #? ; #95: for z|wa==0 &b(1)
高橋版VZではメモリアロケーションエラー対策が施されていますので,保険ということになると思います。
トグルスイッチのような動作をさせるには,マクロ内部に1バイトのバッファを用意して,呼び出されるたびに値を反転するようにします.
&> (r)?{ &m("ON") }?{ &m("OFF") }
;;中略
0: ($.8^=1)."$(1)" ; スイッチを押す(反転)
呼出毎にビット反転させた値を取得して分岐させます。この例は熊谷秀武氏の手法ですが,初回の動作を決定する数値を埋め込んでおけるのが特徴です。
初回の動作を[ON]に固定したいとき,または[ON]固定で構わないという場合は,次の上村郁夫氏の手法が使えます。
&> (r)?{ &m("ON") }?{ &m("OFF") } ;;中略 0: ($.7^=1). ;0: (($.6)!). ;別法:0か1なのでビット演算は不要
これはマクロの区切りのヌルまで利用してしまうという,VZマクロらしい省エネコードと言えるでしょう。
セグメント vz の オフセット $698 を参照する必要があります。
1 ^\ ""
&00 &m("%s",r*2+"非常駐")
0: ;常駐/非常駐を判定
; (pk/mes/03/05181 榊原知氏 1994/01/16)
; OUT -> r: 1=常駐
; 0=非常駐
dz[=vz,($698.0) dz],
なお常駐VZでも,直接コマンドラインから起動されたのか,それとも他のアプリケーションの子プロセスで起動されたのか,によって変化しますので注意が必要です。他のアプリケーションの子プロセスで起動されている場合は,常駐フラグは0になります。
見た目では,コマンドプロンプトの文字が黄色になっていれば常駐VZの管理下にあり,白い文字ならcommand.comの管理下にあることがわかります。
この式は &e()
にマクロ文字列を与えて実行させようとする場合の,
&> a=r, ;ローカルマクロ(メニュー)の戻り値をaにセットする
00: !01 ;メニューを呼び出す
(r) ;メニューの戻り値を評価して内部レジスタに格納する
…というサブルーチンをマクロ文字列を省略して記述したものです。&e()
への渡し方は,「p="!01(r)", &e(p)
」ということになります。
内部レジスタをセットする「(式)」は,実は開き括弧の「(」を省略することができます。更に,マクロの一番最後に限り閉じ括弧の「)」も省略できます。したがって,「p"!01(r)"
」は「p"!01r"
」とも書けることになります。
この項は,nifty:fgalpk/mes/14/03338(1994/01/21) 榊原知氏の発言を参考にしています