系統主要由 Phil Burk 及 Larry Polansky, David Rosenboom 開發. 特別感謝下列的貢獻者: Darren Gibbs, Herb Maeder, Gary Arakaki, Mike Haas.
by Phil Burk with Larry Polansky, David Rosenboom. Special thanks to contributors Darren Gibbs, Herb Maeder, Gary Arakaki, Mike Haas.
PForth 原始碼是可以自由地使用的. 作者也提供定製 pForth 系統的服務: 移植到新平台, 或是在有契約的型式下發展 pForth 的應用程式. 如果有需要, 可以 從 philburk@softsynth.com 連絡 Phil Burk.
PForth source code is freely available. The author is available for customization of pForth, porting to new platforms, or developing pForth applications on a contractual basis. If interested, contact Phil Burk at philburk@softsynth.com
回到(Back to) pForth 首頁(pForth Home Page)
The pForth software code is dedicated to the public domain, and any third party may reproduce, distribute and modify the pForth software code or any derivative works thereof without any compensation or license. The pForth software code is provided on an "as is" basis without any warranty of any kind, including, without limitation, the implied warranties of merchantability and fitness for a particular purpose and their equivalents under the laws of any jurisdiction.
pforth 相關文件的中譯文採用 BSD 風格的授權, 任何人都可以重製、散布和修改, 但必須保留文件的出處及相關作者和譯者的姓名. 使用時不需付出任何的報酬, 也可 以只引用部分的內容, 但是不可以宣稱這些內容完全是自己創作的.
PForth is an ANSI style Forth designed to be portable across many platforms. The 'P' in pForth stands for "Portable". PForth is based on a Forth kernel written in ANSI standard 'C'.
Forth is a stack based language invented by astronomer Charles Moore for controlling telescopes. Forth is an interactive language. You can enter commands at the keyboard and have them be immediately executed, similar to BASIC or LISP. Forth has a dictionary of words that can be executed or used to construct new words that are then added to the dictionary. Forth words operate on a data stack that contains numbers and addresses.
要了解更多有關於 Forth 的資訊, 請閱讀 Forth 教學.
To learn more about Forth, see the Forth Tutorial.
PForth began as a JSR threaded 68000 Forth called HForth that was used to support HMSL, the Hierarchical Music Specification Language. HMSL was a music experimentation language developed by Phil Burk, Larry Polansky and David Rosenboom while working at the Mills College Center for Contemporary Music. Phil moved from Mills to the 3DO Company where he ported the Forth kernel to 'C'. It was used at 3DO as a tool for verifying ASIC design and for bringing up new hardware platforms. At 3DO, the Forth had to run on many systems including SUN, SGI, Macintosh, PC, Amiga, the 3DO ARM based Opera system, and the 3DO PowerPC based M2 system. PForth is now being developed for use at CagEnt, a spinoff of 3DO.
PForth has been designed with portability as the primary design goal. As a result, pForth avoids any fancy UNIX calls. pForth also avoids using any clever and original ways of constructing the Forth dictionary. It just compiles its kernel from ANSI compatible 'C' code then loads ANS compatible Forth code to build the dictionary. Very boring but very likely to work on almost any platform.
pForth 可以儲存的這一個詞典檔案幾乎是與平台無關的. 它們可以在一個處理 器上編譯, 並且在另一個處理器上執行. 只要兩個機器上的 endian 是相同的就不 會有問題. 換句話說, 在 PC 上建造的詞典只適用於 PC. 在大部分的其他機型的 電腦上建造的詞典幾乎適用於大部分的其他機型的電腦.
The dictionary files that can be saved from pForth are almost host independant. They can be compiled on one processor, and then run on another processor. as long as the endian-ness is the same. In other words, dictionaries built on a PC will only work on a PC. Dictionaries built on almost any other computer will work on almost any other computer.
pForth 可以運用於只實作非常少量系統服務的硬體系統. 有可能編譯 pforth 系統來用於只支援收發一個單一字元的系統. 如果沒有 malloc() 和 free() 函式, 系統也有用標準 C 程式碼寫的相等函式. 如果沒有檔案的 I/O 功能, 詞典也可以 用 C 的靜態陣列原始碼的方式來儲存在機器裡. 用 C 寫的詞典格式可以稍後編譯 成特製的 pForth 核心來避免必須從磁碟上讀取詞典的問題.
PForth can be used to bring up minimal hardware systems that have very few system services implemented. It is possible to compile pForth for systems that only support routines to send and receive a single character. If malloc() and free() are not available, equivalent functions are available in standard 'C' code. If file I/O is not available, the dictionary can be saved as a static data array in 'C' source format on a host system. The dictionary in 'C' source form is then compiled with a custom pForth kernel to avoid having to read the dictionary from disk.
The process of building pForth involves several steps. This process is typically handled automatically by the Makefile or IDE Project.
A Makefile has been provided that should work on most UNIX platforms.
cd to top directory of pForth
Enter: make all
A precompiled PPC binary for pForth is provided. A Code Warrior Project has been provided that will rebuild pForth for PPC if desired. Alternatively you could use MPW to make pForth as an MPW Tool. Make sure that you provide at least 1 Meg of heap space. If you build for 68K, make sure you use 32 bit integers, and select the appropriate libraries. To rebuild pForth for PPC:
Open pForthCW
Make target "pForthApp"
Run pForthApp
Enter "-i" as Argumant in starting dialog to initialize dictionary.
To compile system.fth, enter "loadsys".
Quit pForth using File menu.
From now on, just double click pForthApp icon to run pForth.
A precompiled binary for pForth is provided. To rebuild under Windows NT or Win95 using Microsoft Visual C++:
Double click on the pForth.dsw icon in "pForth\pcbuild".
Select the "MakeDic" configuration.
Select "Rebuild All" from the Build menu.This will build the pForth.exe file.
Run the app with CTRL-F5 which will build the pforth.dic file.
Select the "Release" configuration.
Run the app with CTRL-F5 which will drop you into Forth.
From now on, to run pForth, just double click on the pforth.exe file.
ansilocs.fth = 支援 ANSI (LOCAL) 詞.
c_struct.fth = 似 'C' 的資料結構格式.
case.fth = CASE OF ENDOF ENDCASE
catch.fth = CATCH 和 THROW
condcomp.fth = [IF] [ELSE] [THEN] 條件式編譯系統
filefind.fth = FILE?
floats.fth = 浮點運算支援
forget.fth = FORGET [FORGET] IF.FORGOTTEN
loadp4th.fth = 載入基本的詞典
locals.fth = { } 風格的區域變數, 使用了 (LOCAL)
math.fth = 許多數學運算詞
member.fth = 更多的似 'C' 資料結構支援
misc1.fth = 各式各樣的詞
misc2.fth = 各式各樣的詞
numberio.fth = 格式化的數字輸出入
private.fth = 隱藏低階的詞
quit.fth = 高階的 QUIT EVALUATE INTERPRET
smart_if.fth = 允許條件式子在冒號定義之外
see.fth = Forth "反組譯器". 例如: SEE SPACES
strings.fth = 字串支援
system.fth = Bootstrap 載入 pForth 詞典
trace.fth = 除錯用的單步追蹤系統
ansilocs.fth = support for ANSI (LOCAL) word
c_struct.fth = 'C' like data structures
case.fth = CASE OF ENDOF ENDCASE
catch.fth = CATCH and THROW
condcomp.fth = [IF] [ELSE] [THEN] conditional compiler
filefind.fth = FILE?
floats.fth = floating point support
forget.fth = FORGET [FORGET] IF.FORGOTTEN
loadp4th.fth = loads basic dictionary
locals.fth = { } style locals using (LOCAL)
math.fth = misc math words
member.fth = additional 'C' like data structure support
misc1.fth = miscellaneous words
misc2.fth = miscellaneous words
numberio.fth = formatted numeric input/output
private.fth = hide low level words
quit.fth = QUIT EVALUATE INTERPRET in high level
smart_if.fth = allows conditionals outside colon definition
see.fth = Forth "disassembler". Eg. SEE SPACES
strings.fth = string support
system.fth = bootstraps pForth dictionary
trace.fth = single step trace for debugging
csrc/pfcompil.c = pForth compiler support
csrc/pfcustom.c = example of 'C' functions callable from pForth
csrc/pfinnrfp.h = float extensions to interpreter
csrc/pforth.h = include this in app that embeds pForth
csrc/pf_cglue.c = glue for pForth calling 'C'
csrc/pf_clib.c = replacement routines for 'C' stdlib
csrc/pf_core.c = primary words called from 'C' app that embeds pForth
csrc/pf_float.h = defines PF_FLOAT, and the floating point math functions
such as fp_sin
csrc/pf_inner.c = inner interpreter
csrc/pf_guts.h = primary include file, define structures
csrc/pf_io.c = input/output
csrc/pf_main.c = basic application for standalone pForth
csrc/pf_mem.c = optional malloc() implementation
csrc/pf_save.c = save and load dictionaries
csrc/pf_text.c = string tools, error message text
csrc/pf_words.c = miscellaneous pForth words implemented
PForth can be run from a shell or by double clicking on its icon, depending on the system you are using. The execution options for pForth are described assuming that you are running it from a shell.
使用(Usage):
pforth [-i] [-d詞典檔名] [原始碼檔名]
pforth [-i] [-dDictionaryFilename] [SourceFilename]
pforth -dgame.dic
pForth 會取得傳給它的 Forth 原始檔檔名之後, 自動地編譯執行. 這在將 Forth 作為組譯或是自動化的硬體測試時十分有用. 記住原始碼需要能夠在整個檔案內 編譯及執行完成. (譯註: 這是指使用者需要傳給 pForth 最主要的原始檔, 而這一 個檔案除了系統提供的函式之外, 其他需要的函式庫都要在其中 INCLUDE 進來, 由於 Forth 並不提供像是 C 一樣編譯出 object 檔案的功能, 所以每次提供給 Forth 的檔案均需完整.)
To verify that PForth is working, enter:
3 4 + .它現在應該會顯示 "7 ok". 現在輸入:
It should print "7 ok". Now enter:
You should see a long list of all the words in the pForth dictionary. Don't worry. You won't need to learn all of these. More tests are described in the README.txt file.
This Forth is intended to be ANS compatible. I will not claim that it is compatible until more people bang on it. If you find areas where it deviates from the standard, please let me know.
支援的詞集包括(Word sets supported include):
Here are the areas that I know are not compatible:
環境變數的查詢未實作.
The ENVIRONMENT queries are not implemented.
不支援的詞集包括:
Word sets NOT supported include:
These features are not part of the ANS standard for Forth. They have been added to assist developers.
Use INCLUDE to compile source code from a file:
INCLUDE filename你可以巢狀地呼叫 INCLUDE. INCLUDE 只是簡單地將 Forth 系統原本的鍵盤輸入方式 重新導向至檔案. 所以使用者可以在原始程式碼中放入任何合法的 Forth 程式碼.
You can nest calls to INCLUDE. INCLUDE simply redirects Forth to takes its input from the file instead of the keyboard so you can place any legal Forth code in the source code file.
Use SAVE-FORTH save your precompiled code to a file. To save the current dictionary to a file called "custom.dic", enter:
c" custom.dic" SAVE-FORTH使用者可以離開 pForth, 並且改用使用者自設的詞典. 只需輸入下列的指令:
You can then leave pForth and use your custom dictionary by enterring:
pforth -dcustom.dic在使用圖像的系統, 使用者可能希望將他自己的詞典存成 "pforth.dic", 以便 下次進入系統時, 這一個檔案將會自動被載入.
On icon based systems, you may wish to name your custom dictionary "pforth.dic" so that it will be loaded automatically.
小心不要在詞典中存入絕對的位置, 因為當使用者重新載入 pForth 時, 系統的位置將會改變. 使用 A! 來將位置用一種可以重新定位的方式來存進變 數中, 並且使用 A@ 來取回使用者所需的位置.
Be careful that you do not leave absolute addresses stored in the dictionary because they will not work when you reload pForth at a different address. Use A! to store an address in a variable in a relocatable form and A@ to get it back if you need to.
VARIABLE DATA-PTR CREATE DATA 100 ALLOT DATA DATA-PTR ! \ 不佳! 儲存絕對位置 DATA DATA-PTR A! \ 良好! 儲存相對位置 DATA-PTR A@ \ 取得相對位置
VARIABLE DATA-PTR CREATE DATA 100 ALLOT DATA DATA-PTR ! \ storing absolute address! BAD DATA DATA-PTR A! \ storing relocatable address! GOOD DATA-PTR A@ \ fetch relocatable address
When you are testing a file full of code, you will probably recompile many times. You will probably want to FORGET the old code before loading the new code. You could put a line at the beginning of your file like this:
FORGET XXXX-MINE : XXXX-MINE ;這將會讓系統自動地 "FORGET" 使用者每次載入的程式碼. 不幸地, 使用者必須先 定義 XXXX_MINE 之後, 才能載入這一個檔案. 我們製造一個詞來自動地為使用者 定義這一個必須存在的字. 它叫做 ANEW, 能夠在幾乎每一個 Forth 原始碼中找到. 作者們使用一個字首 TASK- 並接著檔案名稱來達成一致性. 這一個 TASK-name 的 單字對於配合 INCLUDE? 詞使用時也很方便. 下面有一個例子:
This would automatically FORGET for you every time you load. Unfortunately, you must define XXXX-MINE before you can ever load this file. We have a word that will automatically define a word for you the first time, then FORGET and redefine it each time after that. It is called ANEW and can be found at the beginning of most Forth source files. We use a prefix of TASK- followed by the filename just to be consistent. This TASK-name word is handy when working with INCLUDE? as well. Here is an example:
\ 檔案的開始(Start of file) INCLUDE? TASK-MYTHING.FTH MYTHING.FTH ANEW TASK-THISFILE.FTH \ 檔案的其他部分(the rest of the file follows...)注意這一個 INCLUDE? 在呼叫 ANEW 之前, 所以我們將不會在每次重新編譯時 FORGET MYTHING.FTH.
Notice that the INCLUDE? comes before the call to ANEW so that we don't FORGET MYTHING.FTH every time we recompile.
FORGET 允許使用者清除已經編譯過的程式碼. 這是一個在程式語言中不平常的 特色. 這在 Forth 中是一種十分便利的特性, 但是卻可能導致問題. 大部分的問題 是某些位置仍指向 FORGET 已經清除的程式碼. 這有可能發生在使用者設定了一個 deferred 系統詞指到他所創造的詞, 接著用 FORGET 將那一個詞清除. 這一個系統 詞指向的程式碼不存在了之後, 如果有其他的程式碼使用到這一個詞, 就可能會造 成系統毀壞. (請參考後面的 DEFER 討論). 另一個問題是如果你的程式碼配置了 記憶體、開啟了檔案、或是開了視窗, 程式碼一被忘記之後, 就無法釋放或是關上 這些事物了. 使用者也可能遇到另一種問題就是將這些程式碼的位置加進跳躍表或 是資料表格中.
FORGET allows you to get rid of code that you have already compiled. This is an unusual feature in a programming language. It is very convenient in Forth but can cause problems. Most problems with FORGET involve leaving addresses that point to the forgotten code that are not themselves forgotten. This can occur if you set a deferred system word to your word then FORGET your word. The system word which is below your word in the dictionary is pointing up to code that no longer exists. It will probably crash if called. (See discussion of DEFER below.) Another problem is if your code allocates memory, opens files, or opens windows. If your code is forgotten you may have no way to free or close these thing. You could also have a problems if you add addresses from your code to a table that is below your code. This might be a jump table or data table.
由於這是很常見的問題, 所以作者們提供了一套工具來處理它. 如果你有一些 程式碼如果使用 FORGET 指令會有潛在性的問題, 就可以寫一個清除用的詞來消除 這一個問題. 這一個詞可以 UNdefer 詞, 釋放記憶體等等. 然後告訴系統如果需 要 FORGET 程式碼時就呼叫這一個詞. 下面是它的用法:
Since this is a common problem we have provided a tool for handling it. If you have some code that you know could potentially cause a problem if forgotten, then write a cleanup word that will eliminate the problem. This word could UNdefer words, free memory, etc. Then tell the system to call this word if the code is forgotten. Here is how:
: MY.CLEANUP ( -- , do whatever ) MY-MEM @ FREE DROP 0 MY-MEM ! ; IF.FORGOTTEN MY.CLEANUPIF.FORGOTTEN 創造了一個鍵結串列, 其中包含了所有設定過的 CFA, 並且在使用 FORGET 指令時將會被檢查. 每一個結束於 FORGET 執行後的 HERE 位置(這是 Forth 指向詞典最上層的指標) 之前的節點都會被執行. (譯註: 應該是從連結的最後開始 執行, 直到最後的位置落在要 FORGET 的指令之前的位置為止, 然後再執行 FORGET 的動作.)
IF.FORGOTTEN creates a linked list node containing your CFA that is checked by FORGET. Any nodes that end up above HERE (the Forth pointer to the top of the dictionary) after FORGET is done are executed.
Sometimes, you may need to extend the way that FORGET works. FORGET is not deferred, however, because that could cause some real problems. Instead, you can define a new version of [FORGET] which is searched for and executed by FORGET. You MUST call [FORGET] from your program or FORGET will not actually FORGET. Here is an example.
: [FORGET] ( -- , my version ) ." Change things around!" CR [FORGET] ( must be called ) ." Now put them back!" CR ; : FOO ." Hello!" ; FORGET FOO ( Will print "Change things around!", etc.)這種方式比重新定義 FORGET 指令更值得推薦的原因是像是 ANEW 的指令中會呼叫 到 FORGET 指令, 而採用這種做法將可以真正使用到使用者自訂的 FORGET 動作.
This is recommended over redefining FORGET because words like ANEW that call FORGET will now pick up your changes.
In pForth, you can use IF THEN DO LOOP and other conditionals outside of colon definitions. PForth will switch temporarily into the compile state, then automatically execute the conditional code. (Thank you Mitch Bradley) For example, just enter this at the keyboard.
10 0 DO I . LOOP
If you cannot remember the exact name of a word, you can use WORDS.LIKE to search the dictionary for all words that contain a substring. For an example, enter:
WORDS.LIKE FOR WORDS.LIKE EMIT
You can use FILE? to find out what file a word was compiled from. If a word was defined in multiple files then it will list each file. The execution token of each definition of the word is listed on the same line.
FILE? IF FILE? AUTO.INIT
You can use SEE to "disassemble" a word in the pForth dictionary. SEE will attempt to print out Forth source in a form that is similar to the source code. SEE will give you some idea of how the word was defined but is not perfect. Certain compiler words, like BEGIN and LITERAL, are difficult to disassemble and may not print properly. For an example, enter:
SEE SPACES SEE WORDS
It is often useful to proceed step by step through your code when debugging.
PForth provides a simple single step trace facility for this purpose.
Here is an example of using TRACE to debug a simple program. Enter
the following program:
: SQUARE ( n -- n**2 ) DUP * ; : TSQ ( n -- , test square ) ." Square of " DUP . ." is " SQUARE . CR ;雖然這一個程式是正確的, 讓我們先假裝它有問題, 並且對它進行除錯. 輸入:
Even though this program should work, let's pretend it doesn't and try to debug it. Enter:
You should see:
7 trace tsq << TSQ +0 <10:1> 7 || (.") Square of " >> ok這個 "TSQ +0" 是表示你正在執行從 TSQ 開始的第 "+0" 個位置的程式碼. <10:1> 是指我們正在使用十進位, 並且在堆疊上有一個元素, 並且它的數值是 7. (.") 是 準備要被執行的詞. (.") 是使用 ." 指令時會被編譯的詞. 現在進行單步追蹤, 輸入:
The "TSQ +0" means that you are about to execute code at an offset of "+0" from the beginning of TSQ. The <10:1> means that we are in base 10, and that there is 1 item on the stack, which is shown to be "7". The (.") is the word that is about to be executed. (.") is the word that is compiled when use use .". Now to single step, enter:
s你將會看到:
You should see:
Square of << TSQ +16 <10:1> 7 || DUP >> ok"Square os" 已經被 (.") 指令印出來了. 我們可以用 sm 指令來進行多步的追蹤. 輸入:
The "Square os" was printed by (."). We can step multiple times using the "sm" command. Enter:
3 sm你可以看到:
You should see:
<< TSQ +20 <10:2> 7 7 || . >> 7 << TSQ +24 <10:1> 7 || (.") is " >> is << TSQ +32 <10:1> 7 || SQUARE >> ok在 ">>" 後面的 "7" 是由 "." 詞所印出來的. 如果我們再輸入 "s", 就會直接一步 執行完 SQUARE 詞. 如果我們需要追蹤進 SQUARE 詞, 我們可以輸入:
The "7" after the ">>" was printed by the . word. If we entered "s", we would step over the SQUARE word. If we want to dive down into SQUARE, we can enter:
sd你將會看到:
You should see:
<< SQUARE +0 <10:1> 7 || DUP >> ok要在 SQUARE 中單步執行, 輸入:
To step once in SQUARE, enter:
s你應該可以看見:
You should see:
<< SQUARE +4 <10:2> 7 7 || * >> ok要直接結束目前的詞, 輸入:
To go to the end of the current word, enter:
g你應該可以看見:
You should see:
<< SQUARE +8 <10:1> 49 || EXIT >> << TSQ +36 <10:1> 49 || . >> okEXIT 是在每一個編譯過的 Forth 詞的結束位置. 要得到有關 TRACE 的更多的資訊, 可以輸入 TRACE.HELP:
EXIT is compiled at the end of every Forth word. For more information on TRACE, enter TRACE.HELP:
TRACE ( i*x <name> -- , 設定來追蹤 Forth 詞 ) S ( -- , 單步追蹤一次 ) SM ( many -- , 單步追蹤許多次 ) SD ( -- , 深入追蹤 ) G ( -- , 執行直到詞結束 ) GD ( n -- , 從目前的等級追蹤進入 N 層, 在目前的等級的結束位置停止 )
TRACE ( i*x <name> -- , setup trace for Forth word ) S ( -- , step over ) SM ( many -- , step over many times ) SD ( -- , step down ) G ( -- , go to end of word ) GD ( n -- , go down N levels from current level, stop at end of this level )
PForth supports conditional compilation words similar to 'C''s #if, #else, and #endif.
TRUE constant USE_FRENCH USE_FRENCH [IF] : WELCOME ." Bienvenue!" cr ; [ELSE] : WELCOME ." Welcome!" cr ; [THEN]這裡是如何在冒號定義中利用 [ 和 ] 來有條件的地編譯.
Here is how to conditionally compile within a colon definition by using [ and ].
: DOIT ( -- ) START.REACTOR IF [ USE_FRENCH [IF] ] ." Zut alors!" [ [ELSE] ] ." Uh oh!" [THEN] THEN cr ;
In a complicated Forth word it is sometimes hard to keep track of where things are on the stack. If you find you are doing a lot of stack operations like DUP SWAP ROT PICK etc. then you may want to use local variables. They can greatly simplify your code. You can declare local variables for a word using a syntax similar to the stack diagram. These variables will only be accessible within that word. Thus they are "local" as opposed to "global" like regular variables. Local variables are self-fetching. They automatically put their values on the stack when you give their name. You don't need to @ the contents. Local variables do not take up space in the dictionary. They reside on the return stack where space is made for them as needed. Words written with them can be reentrant and recursive.
假設要寫一個計算兩個平方數差值的詞, 下面有兩種方式可以寫出相同作用的詞.
Consider a word that calculates the difference of two squares, Here are two ways of writing the same word.
: DIFF.SQUARES ( A B -- A*A-B*B )
DUP *
SWAP DUP *
SWAP -
;
( or )
: DIFF.SQUARES { A B -- A*A-B*B }
A A *
B B * -
;
3 2 DIFF.SQUARES ( would return 5 )
在第二個定義中, DIFF.SQUARES 用大括弧來告訴編譯器開始定義區域變數.
目前定義兩個區域變數, A 及 B. 它們的名字可以跟一般的 Forth 詞一樣長.
"--" 符號代表了區域變數列表的結束. 當執行這一個詞時, 這些數值將會自動地
從堆疊中取出, 然後放進區域變數中. 當區域變數被執行時, 它將會直接把它的
數值放進堆疊, 而不是放進它的位置. 這就叫做是自我讀取. 由於沒有使用到位置
, 你可能會懷疑要如何將數值存入. 有一個特別的運算符號可以做這種儲存的動作,
它看起來像是 "->", 並且發音成 "to".
In the second definition of DIFF.SQUARES the curly bracket '{' told the compiler to start declaring local variables. Two locals were defined, A and B. The names could be as long as regular Forth words if desired. The "--" marked the end of the local variable list. When the word is executed, the values will automatically be pulled from the stack and placed in the local variables. When a local variable is executed it places its value on the stack instead of its address. This is called self-fetching. Since there is no address, you may wonder how you can store into a local variable. There is a special operator for local variables that does a store. It looks like -> and is pronounced "to".
區域變數不需要全部由堆疊傳入. 你可以自己在 "|" 符號後宣告它們. 它們 在被創造出來時會被設成 0. 下面有一個運用 "->" 和 "|" 詞的範例.
Local variables need not be passed on the stack. You can declare a local variable by placing it after a "vertical bar" ( | )character. These are automatically set to zero when created. Here is a simple example that uses -> and | in a word:
: SHOW2*
{ loc1 | unvar -- , 一個是規律的, 一個是未初始化的 }
LOC1 2* -> UNVAR
( 將 UNVER 設成 2*LOC1 )
UNVAR . ( 顯示 UNVAR )
;
3 SHOW2* ( 只傳一個參數, 顯示 6 )
: SHOW2*
{ loc1 | unvar -- , 1 regular, 1 uninitialized }
LOC1 2* -> UNVAR
( set unver to 2*LOC1 )
UNVAR . ( print UNVAR )
;
3 SHOW2* ( pass only 1 parameter, prints 6 )
由於區域變數通常是用來作為一個計數器或是累加器, 我們有一個特別的運算符號來
在一個區域變數中增加數值. 它是 "+->", 發音成 "plus to". 下面兩行的功能是相等
的, 但是第二行會較快而且較小. Since local variable often used as counters or accumulators, we have a special operator for adding to a local variable It is +-> which is pronounced "plus to". These next two lines are functionally equivalent but the second line is faster and smaller:
ACCUM 10 + -> ACCUM 10 +-> ACCUM如果你將一個區域變數命令成跟詞典中的 Forth 詞的名字相同. 例如: INDEX 或是 COUNT, 你將會得到一個警告訊息. 這一個區域變數仍然能工作, 但是其他人可能為 因此而遇到困擾, 所以我們為此向你提出警告. 其他可能出現的錯誤包括: 少了一個 "}" 符號, 少了 "--", 或是使用了過多的區域變數.
If you name a local variable the same as a Forth word in the dictionary, eg. INDEX or COUNT, you will be given a warning message. The local variable will still work but one could easily get confused so we warn you about this. Other errors that can occur include, missing a closing '}', missing '--', or having too many local variables.
You can define 'C' like data structures in pForth using :STRUCT. For example:
:STRUCT SONG LONG SONG_NUMNOTES \ 定義名為 SONG_NUMNOTES 的 32 位元結構成員 SHORT SONG_SECONDS \ 定義 16 位元的結構成員 BYTE SONG_QUALITY \ 定義 8 位元的結構成員 LONG SONG_NUMBYTES \ 自動在 SHORT 和 BYTE 後 align RPTR SONG_DATA \ 重定位到資料的指標 ;STRUCT
SONG HAPPY \ 定義一個 song 資料結構叫做 happy
400 HAPPY S! SONG_NUMNOTES \ 設定 SONG_NUMNOTES 為 400 17 HAPPY S! SONG_SECONDS \ S! 對於各種大小的結構成員都適用
CREATE SONG-DATA 23 , 17 , 19 , 27 , SONG-DATA HAPPY S! SONG_DATA \ 用重訂位的型式來儲存指標
HAPPY DST SONG \ 用 SONG 資料格式來顯示 HAPPY 的內容
HAPPY S@ SONG_NUMNOTES . \ 取得 SONG_NUMNOTES 並印出
:STRUCT SONG LONG SONG_NUMNOTES \ define 32 bit structure member named SONG_NUMNOTES SHORT SONG_SECONDS \ define 16 bit structure member BYTE SONG_QUALITY \ define 8 bit member LONG SONG_NUMBYTES \ auto aligns after SHORT or BYTE RPTR SONG_DATA \ relocatable pointer to data ;STRUCT
SONG HAPPY \ define a song structure called happy
400 HAPPY S! SONG_NUMNOTES \ set number of notes to 400 17 HAPPY S! SONG_SECONDS \ S! works with all size members
CREATE SONG-DATA 23 , 17 , 19 , 27 , SONG-DATA HAPPY S! SONG_DATA \ store pointer in relocatable form
HAPPY DST SONG \ dump HAPPY as a SONG structure
HAPPY S@ SONG_NUMNOTES . \ fetch numnotes and print閱讀 "c_struct.fth" 來取得更多的資訊.
See the file "c_struct.fth" for more information.
Using DEFER for vectored words. In Forth and other languages you can save the address of a function in a variable. You can later fetch from that variable and execute the function it points to.This is called vectored execution. PForth provides a tool that simplifies this process. You can define a word using DEFER. This word will contain the execution token of another Forth function. When you execute the deferred word, it will execute the function it points to. By changing the contents of this deferred word, you can change what it will do. There are several words that support this process.
Simple way to see the name of what's in a deferred word:
WHAT'S EMIT >NAME ID.
should print name of current word that's in EMIT.
Here is an example that uses a deferred word.
DEFER PRINTIT ' . IS PRINTIT ( 讓 PRINTIT 產生作用(make PRINTIT use). ) 8 3 + PRINTIT : COUNTUP ( -- , 呼叫 deferred 詞 - call deferred word ) ." Hit RETURN to stop!" CR 0 ( 第一個值 - first value ) BEGIN 1+ DUP PRINTIT CR ?TERMINAL UNTIL ; COUNTUP ( 使用簡單版 - uses simple . ) : FANCY.PRINT ( N -- , 印出十進位和十六進位 - print in DECIMAL and HEX) DUP ." DECIMAL = " . ." , HEX = " .HEX ; ' FANCY.PRINT IS PRINTIT ( 改變 PRINTIT - change printit ) WHAT'S PRINTIT >NAME ID. ( 顯示如何使用 WHAT'S - shows use of WHAT'S ) 8 3 + PRINTIT COUNTUP ( 注意現在是使用 FANCY.PRINT - notice that it now uses FANCY.PRINT )許多在系統中的詞使用 DEFER 來定義, 因此我們可以改變他們如何工作的方式, 而不需重新編譯系統. 這裡是這些詞中的部分列表.
Many words in the system have been defined using DEFER which means that we can change how they work without recompiling the entire system. Here is a partial list of those words
ABORT EMIT NUMBER?
Deferred words are very handy to use, however, you must be careful with them. One problem that can occur is if you initialize a deferred system more than once. In the below example, suppose we called STUTTER twice. The first time we would save the original EMIT vector in OLD-EMIT and put in a new one. The second time we called it we would take our new function from EMIT and save it in OLD-EMIT overwriting what we had saved previously. Thus we would lose the original vector for EMIT . You can avoid this if you check to see whether you have already done the defer. Here's an example of this technique.
DEFER OLD-EMIT ' QUIT IS OLD-EMIT ( 設成已知值 ) : EEMMIITT ( char --- , 我們有趣的 EMIT ) DUP OLD-EMIT OLD-EMIT ; : STUTTER ( --- ) WHAT'S OLD-EMIT 'C QUIT = ( 仍然一樣? ) IF ( 這是第一次 ) WHAT'S EMIT ( 取得 EMIT 目前的值 ) IS OLD-EMIT ( 將值存入 OLD-EMIT ) 'C EEMMIITT IS EMIT ELSE ." Attempt to STUTTER twice!" CR THEN ; : STOP-IT! ( --- ) WHAT'S OLD-EMIT ' QUIT = IF ." STUTTER not installed!" CR ELSE WHAT'S OLD-EMIT IS EMIT 'C QUIT IS OLD-EMIT ( 重設至顯示結束 ) THEN ;
DEFER OLD-EMIT ' QUIT IS OLD-EMIT ( set to known value ) : EEMMIITT ( char --- , our fun EMIT ) DUP OLD-EMIT OLD-EMIT ; : STUTTER ( --- ) WHAT'S OLD-EMIT 'C QUIT = ( still the same? ) IF ( this must be the first time ) WHAT'S EMIT ( get the current value of EMIT ) IS OLD-EMIT ( save this value in OLD-EMIT ) 'C EEMMIITT IS EMIT ELSE ." Attempt to STUTTER twice!" CR THEN ; : STOP-IT! ( --- ) WHAT'S OLD-EMIT ' QUIT = IF ." STUTTER not installed!" CR ELSE WHAT'S OLD-EMIT IS EMIT 'C QUIT IS OLD-EMIT ( reset to show termination ) THEN ;在上面的例子中, 我們可以任意呼叫 STUTTER 或是 STOP-IT!, 並且仍然保持系統 的安全.
In the above example, we could call STUTTER or STOP-IT! as many times as we want and still be safe.
假設你 FORGET 了 EMIT 目前呼叫的詞. 在你編讀你的詞時, 將會覆蓋到 EMIT 呼叫 的程式碼而悲慘地 crash. 你必須在 FORGET 程式碼之前重置呼叫你的程式碼的 deferred 的詞, 最簡單的方法是用 IF.FORGOTTEN 函式來指定一個清除詞來被從 FORGET 指令中呼中. 在上面使用 EMIT 的例子中, 我們可以寫:
Suppose you forget your word that EMIT now calls. As you compile new code you will overwrite the code that EMIT calls and it will crash miserably. You must reset any deferred words that call your code before you FORGET your code. The easiest way to do this is to use the word IF.FORGOTTEN to specify a cleanup word to be called if you ever FORGET the code in question. In the above example using EMIT , we could have said:
IF.FORGOTTEN STOP-IT!
PForth supports the FLOAT word set and much of the FLOATEXT word set as a compile time option. You can select single or double precision as the default by changing the typedef of PF_FLOAT.
FS. FE. FG. F. 1.234000e+12 1.234000e+12 1.234e+12 1234000000000. 1.234000e+11 123.4000e+09 1.234e+11 123400000000. 1.234000e+10 12.34000e+09 1.234e+10 12340000000. 1.234000e+09 1.234000e+09 1.234e+09 1234000000. 1.234000e+08 123.4000e+06 1.234e+08 123400000. 1.234000e+07 12.34000e+06 1.234e+07 12340000. 1.234000e+06 1.234000e+06 1234000. 1234000. 1.234000e+05 123.4000e+03 123400. 123400.0 1.234000e+04 12.34000e+03 12340. 12340.00 1.234000e+03 1.234000e+03 1234. 1234.000 1.234000e+02 123.4000e+00 123.4 123.4000 1.234000e+01 12.34000e+00 12.34 12.34000 1.234000e+00 1.234000e+00 1.234 1.234000 1.234000e-01 123.4000e-03 0.1234 0.1234000 1.234000e-02 12.34000e-03 0.01234 0.0123400 1.234000e-03 1.234000e-03 0.001234 0.0012340 1.234000e-04 123.4000e-06 0.0001234 0.0001234 1.234000e-05 12.34000e-06 1.234e-05 0.0000123 1.234000e-06 1.234000e-06 1.234e-06 0.0000012 1.234000e-07 123.4000e-09 1.234e-07 0.0000001 1.234000e-08 12.34000e-09 1.234e-08 0.0000000 1.234000e-09 1.234000e-09 1.234e-09 0.0000000 1.234000e-10 123.4000e-12 1.234e-10 0.0000000 1.234000e-11 12.34000e-12 1.234e-11 0.0000000 1.234568e+12 1.234568e+12 1.234568e+12 1234567890000. 1.234568e+11 123.4568e+09 1.234568e+11 123456789000. 1.234568e+10 12.34568e+09 1.234568e+10 12345678900. 1.234568e+09 1.234568e+09 1.234568e+09 1234567890. 1.234568e+08 123.4568e+06 1.234568e+08 123456789. 1.234568e+07 12.34568e+06 1.234568e+07 12345679. 1.234568e+06 1.234568e+06 1234568. 1234568. 1.234568e+05 123.4568e+03 123456.8 123456.8 1.234568e+04 12.34568e+03 12345.68 12345.68 1.234568e+03 1.234568e+03 1234.568 1234.568 1.234568e+02 123.4568e+00 123.4568 123.4568 1.234568e+01 12.34568e+00 12.34568 12.34568 1.234568e+00 1.234568e+00 1.234568 1.234568 1.234568e-01 123.4568e-03 0.1234568 0.1234568 1.234568e-02 12.34568e-03 0.01234568 0.0123456 1.234568e-03 1.234568e-03 0.001234568 0.0012345 1.234568e-04 123.4568e-06 0.0001234568 0.0001234 1.234568e-05 12.34568e-06 1.234568e-05 0.0000123 1.234568e-06 1.234568e-06 1.234568e-06 0.0000012 1.234568e-07 123.4568e-09 1.234568e-07 0.0000001 1.234568e-08 12.34568e-09 1.234568e-08 0.0000000 1.234568e-09 1.234568e-09 1.234568e-09 0.0000000 1.234568e-10 123.4568e-12 1.234568e-10 0.0000000 1.234568e-11 12.34568e-12 1.234568e-11 0.0000000
The pForth kernel is written in 'C' for portability. The inner interpreter is implemented in the function ExecuteToken() which is in pf_inner.c.
void pfExecuteToken( ExecToken XT );傳給這個函式的執行單元是跟 EXECUTE 所使用的相同. 它處理接著的執行序列, 並且使用一個很大的 switch() case 敘述來解譯基本單元. 為了要利用暫存器變數 的優點, 並且減少呼叫時的負擔, 這是一個很巨大的函式. 如果順利的話, 你的編譯器 將會把 switch() 敘述最佳化成一個跳躍表格, 因此能執行得更快.
It is passed an execution token the same as EXECUTE would accept. It handles threading of secondaries and also has a large switch() case statement to interpret primitives. It is in one huge routine to take advantage of register variables, and to reduce calling overhead. Hopefully, your compiler will optimise the switch() statement into a jump table so it will run fast.
This Forth supports multiple dictionaries. Each dictionary consists of a header segment and a seperate code segment. The header segment contains link fields and names. The code segment contains tokens and data. The headers, as well as some entire dictionaries such as the compiler support words, can be discarded when creating a stand-alone app.
[未實作] 詞典可以分割, 因此編譯時期的詞可以放在主詞典之上. 因而它們可以 用相同的關聯位置, 而在 turnkey 時被清除.
[NOT IMPLEMENTED] Dictionaries can be split so that the compile time words can be placed above the main dictionary. Thus they can use the same relative addressing but be discarded when turnkeying.
執行的符號可以是一個執行單元的索引 ( n < NUM_PRIMITIVES), 或是在次程式節區的一個相對位置. ( n >= NUM_PRIMITIVES)
Execution tokens are either an index of a primitive ( n < NUM_PRIMITIVES), or the offset of a secondary in the code segment. ( n >= NUM_PRIMITIVES)
詞典的 NAME 檔頭部分包含了每一個在詞典中有名字的詞. 它包含下列的欄位:
The NAME HEADER portion of the dictionary contains a structure for each named word in the dictionary. It contains the following fields:
4 位元組 - 連結欄: 前一個 NAME 檔頭的相對位置. 4 位元組 - 程式碼指標: 對應到的程式碼的相對位置. n 位元組 - 名稱欄: 名稱是有長度計數的字串, 並且 align 成 4 位元組.
bytes 4 Link Field relative address of previous name header 4 Code Pointer relative address of corresponding code n Name Field name as counted string Headers are quad byte aligned.這個詞典中 CODE 的部分由下列的結構組成:
The CODE portion of the dictionary consists of the following structures:
No Forth code. 'C' code in "pf_inner.c".
4*n 個位元組 - 參數欄: 執行符號 4 個位元組 - ID_NEXT = 0: 結束次單元
4*n Parameter Field execution tokens 4 ID_NEXT = 0 terminates secondary
4 ID_CREATE_P 符號 4 Token 選用的 DOES> 程式碼, OR ID_NEXT = 0 4 ID_NEXT = 0 n Body = 任意的資料
4 ID_CREATE_P token 4 Token for optional DOES> code, OR ID_NEXT = 0 4 ID_NEXT = 0 n Body = arbitrary data
4 ID_DEFER_P 與 ID_NOOP 相同的功能, 用來識別 deferred 詞 4 Execution Token 要執行的詞 4 ID_NEXT = 0
4 ID_DEFER_P same action as ID_NOOP, identifies deferred words 4 Execution Token of word to execute. 4 ID_NEXT = 0
4 ID_CALL_C 4 Pack C 呼叫的資訊位元
4 ID_CALL_C 4 Pack C Call Info Bits
0-15 = 函式索引位元 16-23 = 函式表格索引位元(未用) 24-30 = 數字參數位元 31 = 1 如果函式傳回值
0-15 = Function Index Bits 16-23 = FunctionTable Index (Unused) Bits 24-30 = NumParams Bit 31 = 1 if function returns value
4 ID_NEXT = 0
There are several versions of PForth that can be built. By default, the full kernel will be built. For custom builds, define the following options in the Makefile before compiling the 'C' code:
PF_NO_INIT
不要編譯用來初始化詞典的程式碼. 如果你已經有預先編好的詞典時, 這可以
節省空間.
To build on UNIX, do nothing, system will default to "pf_unix.h".
要在麥金塔上建造時:
To build on Macintosh:
-DPF_USER_INC1='"pf_mac.h"'要在 PC 上建造時:
To build on PCs:
-DPF_USER_INC2='"pf_win32.h"'要建造一個只執行 turnkey 或是 cloned 執行檔的系統時:
To build a system that only runs turnkey or cloned binaries:
-DPF_NO_INIT -DPF_NO_SHELL
You may want to create a version of pForth that can be run on a small system that does not support file I/O. This is useful when bringing up new computer systems. On UNIX systems, you can use the supplied gmake target. Simply enter:
gmake pfemb對於其他的系統, 這邊有如何造出內嵌式 pForth 的步驟.
For other systems, here are the steps to create an embedded pForth.
Determine whether your target system has a different endian-ness than your
host system. If the address of a long word is the address of the
most significant byte, then it is "big endian". Examples of big endian
processors are Sparc, Motorola 680x0 and PowerPC60x. If the address
of a long word is the address of the lest significant byte, then it is
"Little Endian". Examples of little endian processors are Intel 8088 and
derivatives such as the Intel Pentium.
If your target system has a different endian-ness than your host system,
then you must compile a version of pForth for your host that matches the
target. Rebuild pForth with either PF_BIG_ENDIAN_DIC or PF_LITTLE_ENDIAN_DIC
defined. You will need to rebuild pforth.dic as well as the executable
Forth. If you do not specify one of these variables, then the dictionary
will match the native endian-ness of the processor (and run faster as a
result).
Execute pForth. Notice the message regarding the endian-ness of the dictionary.
Compile your custom Forth words on the host development system.
Compile the pForth utulity "utils/savedicd.fth".
Enter in pForth: SDAD
SDAD will generate a file called "pfdicdat.h" that contains your dictionary in source code form.
Rewrite the character primitives sdTerminalOut(), sdTerminalIn() and sdTerminalFlush() defined in pf_io.h to use your new computers communications port.
Write a "user_chario.h" file based on the API defined in "pf_io.h".
Compile a new version of pForth for your target machine with the following options:
-DPF_NO_INIT -DPF_NO_MALLOC -DPF_NO_FILEIO \ -DPF_USER_CHARIO="user_chario.h" \ -DPF_NO_CLIB -DPF_STATIC_DIC
The file "pfdicdat.h" will be compiled into this executable and your dictionary will thus be included in the pForth executable as a static array.
Burn a ROM with your new pForth and run it on your target machine.
If you compiled a version of pForth with different endian-ness than your host system, do not use it for daily operation because it will be much slower than a native version.
You can call the pForth interpreter as an embedded tool in a 'C' application. For an example of this, see the file pf_main.c. This application does nothing but load the dictionary and call the pForth interpreter.
你可以從 pForth 中呼叫 C 程式碼, 只需在指令分發表中加進你自己的 C 函式. 並且在 Forth 程式中呼叫這些函式. 研究 "pfcustom.c" 這一個檔案來 了解更進一步的資料.
You can call 'C' from pForth by adding your own custom 'C' functions to a dispatch table, and then adding Forth words to the dictionary that call those functions. See the file "pfcustom.c" for more information.
Once you have compiled pForth, you can test it using the small verification suite we provide. The first test you should run was written by John Hayes at John Hopkins University. Enter:
pforth include tester.fth include coretest.fth bye輸出的結果將會自己解釋它的用途. 作者也增加了一些測試來顯示成功和失敗的數量. 輸入:
The output will be self explanatory. There are also a number of tests that I have added that print the number of successes and failures. Enter:
pforth t_corex.fth pforth t_locals.fth pforth t_strings.fth pforth t_floats.ft注意 t_corex.fth 會顯示一個非預期的錯誤, 因為 SAVE-INPUT 並未被完全實作. (FIXME).
Note that t_corex.fth reveals an expected error because SAVE-INPUT is not
fully implemented. (FIXME)
PForth 原始碼是可以自由地使用的. 作者也提供定製 pForth 系統的服務: 移植到新平台, 或是在有契約的型式下發展 pForth 的應用程式. 如果有需要, 可以 從 philburk@softsynth.com 連絡 Phil Burk.
PForth source code is freely available. The author is available
for customization of pForth, porting to new platforms, or developing pForth
applications on a contractual basis. If interested, contact
Phil Burk at philburk@softsynth.com
回到(Back to) pForth 首頁(pForth Home Page)