Windows Forth +

Windowsウィンドウメッセージングスナップインの設計



Fort言語は、ほとんどの場合、Windows上でもプログラムするのに適していません。 結局のところ、グラフィックスはなく、鈍い黒いテキストコンソールだけがあります。

この神話を克服してみましょう。



まず、Windowsでのプログラミングは非常に簡単で、WinAPI命令を開くだけです。



次に、Windows自体がすべてのグラフィックを制御します。必要な機能を呼び出して、メッセージを正しく処理するだけです。



ウィンドウを作成する前に、クラスを作成する必要があります。 WNDCLASS構造には、WNDPROC lpfnWndProcフィールドが含まれています。このフィールドには、このクラスのウィンドウからのメッセージを処理するためのプロシージャへのリンクが含まれています。



この手順のWindows要件は簡単です。



1)メッセージがプロシージャによって処理されない場合、DefWindowProc関数を呼び出す必要があります

2)rdi rsi rbxレジスタの内容を保存します



アセンブラーを挿入してみましょう。 Fortで記述されたプロシージャを呼び出すマッチングスタブが必要です。 逆に、メッセージが処理されていないという上位レベルのプロシージャから信号が来る場合は、DefWindowProcを呼び出します。



winproc
HEADER winproc HERE CELL+ , push_rcx push_rdx push_r8 push_r9 push_rbx push_rsi push_rdi mov_rax,# hwnd , mov_[rax],rcx mov_rax,# wmsg , mov_[rax],rdx mov_rax,# wparam , mov_[rax],r8 mov_rax,# lparam , mov_[rax],r9 mov_rax,# ' inWinProc , mov_r11,# ' Push @ , call_r11 mov_r11,# ' EXECUTE @ , call_r11 mov_r11,# ' Pop @ , call_r11 test_rax,rax jne forward> pop_rdi pop_rsi pop_rbx pop_r9 pop_r8 pop_rdx pop_rcx ret >forward pop_rdi pop_rsi pop_rbx pop_r9 pop_r8 pop_rdx pop_rcx push_rcx push_rdx push_r8 push_r9 push_rbx push_rsi push_rdi mov_r11,# ' DefWindowProcA CELL+ @ , sub_rsp,b# 0x 20 B, call_r11 add_rsp,b# 0x 20 B, pop_rdi pop_rsi pop_rbx pop_r9 pop_r8 pop_rdx pop_rcx ret
      
      





この作品のロジックは、不必要なコメントなしで明確です。



1)パラメーターを変数に保存する

2)高レベルプロシージャを呼び出す

3)受信した値がゼロでない場合、DefWindowProcを呼び出します



それでは、高レベルの部分に対処しましょう



フォートという言葉自体は手順です。



 WORD: Messages do_something ;WORD
      
      





今、私たちは何をすべきか、そして私たちは知るでしょう。



アセンブラーの挿入では、wmsg変数の使用がわかります。 パラメータuMsg-Windowsのメッセージ番号を取ります。 wmsgの内容を必要なメッセージの番号と比較する必要があり、その番号である場合はメッセージを処理します。 DefWindowProcが呼び出されないようにするには、ゼロを返します。



サンプル
 WORD: Messages wmsg @ hex, 201 (( WM_LBUTTONDOWN ) = If 1 Else do_lbuttondown 0 Then ;WORD
      
      





存在する権利があります。 ただし、これは1つまたは2つのメッセージを処理する必要がある場合に受け入れられます。 しかし、それは不便で、ugく、メンテナンスが不十分であり、タスクを解決しません。 結局、ネストされたIf Thenコンストラクトを作成する必要があり、これはリストのホラーホラーです。



glyい
 WORD: Messages wmsg @ hex, 201 (( WM_LBUTTONDOWN ) = If wmsg @ hex, 202 (( WM_LBUTTONUP ) = If 1 Else do_lbuttonup 0 Then Else do_lbuttondown 0 Then ;WORD
      
      





メッセージは2つだけですが、メッセージが正しく記述されていることを確認するために緊張する必要があります。

幸いなことに、Case ... Of ... EndOf ... EndCaseコンストラクトは非常に簡単に実装され、コードを大幅に装飾します。



書き直します:
 WORD: Messages Case wmsg @ hex, 201 (( WM_LBUTTONDOWN ) = Of do_lbuttondown 0 EndOf wmsg @ hex, 202 (( WM_LBUTTONUP ) = Of do_lbuttonup 0 EndOf EndCase ;WORD
      
      





読みやすく、その場合はハンドラーを追加します。 しかし、あなたはまだより良いことができます。



まず、wmsg @と=はここで常に繰り返されます。



第二に、定数の16進数の数値を挿入することは、なんとなく見た目が悪い。 さらに、この値の意味についてコメントを書く必要があります。



WM_LBUTTONDOWN、WM_LBUTTONUPなどをしましょう 定数になります。



wmsg @と=は1つの単語に結合されます。
 WORD: (?wm) wmsg @ = ;WORD WORD: Messages Case WM_LBUTTONDOWN (?wm) Of do_lbuttondown 0 EndOf WM_LBUTTONUP (?wm) Of do_lbuttonup 0 EndOf EndCase ;WORD
      
      





はるかに美しく、理解しやすくなりました。 それでも、リストには余分な単語が多すぎます。



書けたら
 Messages{{ WM_LBUTTONDOWN{{ do_lbuttondown }} WM_LBUTTONUP{{ do_lbuttonup }} }}Messages
      
      





この問題を解決します。

最も簡単なのは、単語}}を実装することです。 これは、EndOfという単語とほぼ同じです。単語0を追加するだけです。



そして...
 WORD: }} 0 EndOf ;WORD
      
      





しかし、ありません。 EndOfという語は即時実行です。 コンパイルされる代わりに、実行されます。 単語のコンパイル中に実行されました}}。 そして、メッセージ処理モジュールのコンパイル中に実行する必要があります。



EndOfの実装を見てください
 WORD: EndOf COMPILE BRANCH HERE >R COMPILE 0 THEN R> ;WORD
      
      





私たちはHis下コピパストを使って書きます...しかし、最初に、単語}}がすぐに実行されなければならないことを考慮します。



だから
 IMMEDIATES CURRENT ! WORD: EndOf COMPILE 0 COMPILE BRANCH HERE >R COMPILE 0 THEN R> ;WORD FORTH32 CURRENT !
      
      





0 EndOfの代わりに単語}}を挿入し、それが機能することを確認します。



言葉で対処しましょう}}メッセージ



する必要があります:



1)ゼロ以外のコンパイル

2)EndCaseを実行する

3)同様にコンパイルを終了します; WORD



この単語は即時実行であることに注意してください。



書くのはとても簡単です:
 IMMEDIATES CURRENT ! WORD: }}Messages COMPILE 1 (EndOf) ;Word quit ;WORD ;WORD FORTH32 CURRENT !
      
      





では、冒頭の言葉を作成しましょう。 メッセージから始めましょう{{



どうすればいいですか?



4)コンパイルを実行する

3)ケースのコンパイル

2)プロシージャの開始アドレスをwinproc挿入で使用可能にする

1)メッセージ処理手順を開始するアドレスをマークします



自動コンパイルは、単語immediateatorによって開始されます。 ソーステキストに従って、コンパイルされた単語のパラメータフィールドに入力します。 パラメータフィールドの前にはコードフィールドがあり、高レベルの定義の場合はアドレスインタープリタへのリンクを含める必要があります。 #解釈定数を提供します。 Caseという語は0の同義語です。JustCaseは即時実行用であり、0は通常のコンパイルされた語です。



書きます
 WORD: Messages{{ HERE ['] inWinProc CELL+ ! 0 interpret# , immediator ;WORD
      
      





アセンブラーの挿入でinWinProc呼び出しを満たします。 これは、いわゆるベクトルワードです。 これはほとんど通常の定数ですが、値をスタックに置く代わりに実行します。



今楽しい部分



単語WM_LBUTTONDOWN {{およびWM_LBUTTONUP {{
 IMMEDIATES CURRENT ! WORD: WM_LBUTTONDOWN{{ COMPILE WM_LBUTTONDOWN COMPILE (?wm) COMPILE ?OF HERE COMPILE 0 ;WORD WORD: WM_LBUTTONUP{{ COMPILE WM_LBUTTONUP COMPILE (?wm) COMPILE ?OF HERE COMPILE 0 ;WORD FORTH32 CURRENT !
      
      





定数のみを修正して、各メッセージにこのコードをコピーすることが本当に必要ですか? よく見てみましょう。 各定義のコードは同じであり、名前と使用される定数のみが異なります。 この定数は、後続のコードのパラメーターです。 概略的にx do_something_with_xのようになります。



幸いなことに、フォートには定義語の概念があります。 そのような場合のために設計されています。



書きます
 WORD: WM: CREATE , DOES> @ COMPILE (?wm) COMPILE ?OF HERE COMPILE 0 ;WORD
      
      





使い方
 IMMEDIATES CURRENT ! WM_LBUTTONDOWN WM: WM_LBUTTONDOWN{{ WM_LBUTTONUP WM: WM_LBUTTONUP{{ FORTH32 CURRENT !
      
      





えーと...同じテキストを左右に繰り返すのはなぜですか? さらに3回。 (定数は以前に決定しました)。 たぶん、定数を定義するのではなく、すぐに単語を定義する必要がありますか?



このように
 0d 513 WM: WM_LBUTTONDOWN{{ 0d 514 WM: WM_LBUTTONUP{{
      
      





そして...動作しません。 よく見てみましょう。 まず、これらの単語はすべて即時実行する必要があります。 つまり、DOES>の後にコードをメッセージ{{の本文にコンパイルする必要があります。



この部分:COMPILE(?Wm)COMPILE?OF HERE COMPILE 0はすべてを正しく行います。 しかし、DOES>の直後に、WM_Lという単語の作成中にコンパイルされた値を取得します...そして、Messages {{。



メッセージ{{。の本文に既にあるリテラルとしてこの値をコンパイルする必要があります。



正しいコード
 WORD: WM: CREATE , DOES> @ LIT, COMPILE (?wm) COMPILE ?OF HERE COMPILE 0 ;WORD
      
      





まとめると。 別のファイルで共通のヘッダー部分を選択すると便利です。



winuser.f
 WORD: Messages{{ HERE ['] inWinProc CELL+ ! 0 interpret# , immediator ;WORD WORD: (?wm) wmsg @ = ;WORD WORD: WM: CREATE , DOES> @ LIT, COMPILE (?wm) COMPILE ?OF HERE COMPILE 0 ;WORD IMMEDIATES CURRENT ! FORTH32 CONTEXT ! WORD: }}Messages COMPILE 1 (EndCase) ;Word quit ;WORD WORD: }} COMPILE 0 COMPILE BRANCH HERE >R COMPILE 0 THEN R> ;WORD 0d 513 WM: WM_LBUTTONDOWN{{ 0d 514 WM: WM_LBUTTONUP{{ 0d 512 WM: WM_MOUSEMOVE{{ 0d 15 WM: WM_PAINT{{ 0d 16 WM: WM_CLOSE{{ FORTH32 CURRENT !
      
      





ファイル
test.f
 INCLUDE: winuser.f WORD: do_on_lbuttondown do on left button down ;WORD WORD: do_on_lbuttondup do on left button up ;WORD do someting else Messages{{ WM_LBUTTONDOWN{{ do_on_lbuttondown }} WM_LBUTTONUP{{ do_on_lbuttondup }} }}Messages EXIT
      
      





あとがき



Fort言語を使用しているにもかかわらず、スタックを一度も記憶したことはなく、スタックで操作の単語が1つもありませんでした。 そして、最悪の管理構造を隠しませんでした。 彼女は見えますが、見えません。 コードは手続き型よりも記述的です。 さらに、コードはコメントを必要とせず、コメントとして読み取られます。 Fortで記述されたFortシステムは、それ自体への参照です。 別のニュアンス。 プログラムを開発するには、あらゆるレベルのツールを使用できます。 さらに低い組み込みアセンブラから、欠落しているオペコードとニーモニックを組み込みアセンブラに追加し、コンパクトで表現力豊かなコードを作成できる高レベルの汎用ツールを作成できます。



All Articles