2025年2月15日 星期六

砥礪前行

砥礪前行


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
16 February 2025


任何設計好的系統都必須經過長期考驗,執行考驗測試出結果卻是一種磨練,這種工作做起來並不比設計系統來得輕鬆。磨練系統需要範例,範例貴在具有創意,創意還得要有意義,範例才值得採用,有意義、有創意的範例就值得珍惜。自己能設想出來的範例是有限的,從網上尋求一些資源則是必要的,我在網上搜尋範例資源時,有過這些體會。

大家最熟悉的資源莫過於 Rosetta code 網頁: https://rosettacode.org/wiki/Category:Programming_Tasks
這個網頁的好處就在其資料公開的透明度是百分之百,提出來的範例題目毫無隱瞞,附貼出各種程式語言的程式解答也毫無隱瞞。不限制瀏覽、不限制下載、不限制貢獻、不限制引用,全球使用者均能受益,網頁受人尊重,貢獻者也多。

網上還有許多可用資源,這裡介紹另一知名之 Project Euler 網頁:https://projecteuler.net/archives
經過許多年的發展,這個網頁目前(20250216)的範例題目已經累積到將近有 1000 個了。網頁則要求使用者必須登記後才能參與活動,也才能看到題目的程式解答,使用起來有點不便。我大致瀏覽過其範例題目的內容,顯示出不少問題均涉及必須使用具有可程式化大數計算能力的系統才利於獲得答案。因此,能貢獻解答而貼出的範例程式就比較有限了,一般傳統的程式語言不具有大數計算的功能,還要能程式化,那就更不容易見到了。所以,參與活動者的貢獻也就因此而不如 Rosetta code 網頁來得多。當然,相較之下顯示出這個網頁內的範例資源題目比較艱深。

設計 ABC Forth 系統曾經留下的記錄,可以追溯到 1980 年代,剛開始時,主要目標放在為 Forth 系統設計出好用的中算符程式寫法。因為我個人的研究工作,常需要進行可程式化數學計算程式的設計,中算符的程式寫法省時好用,於是愛用 Forth 就愛屋及鳥的研發出 Forth 自我形成的中算符系統,中算符功能是設計出整個系統之構想的起源。

後來的發展則偏重於程式結構語法,BASIC 程式語言表達數學式子的方式方便好用,有利於數學計算方面之程式設計,於是 ABC Forth 的雛型應運而生,有了系統性的結構。由於它是架設於一般 Forth 系統上延伸出性能而成,它的特色就是 BASIC 式的程式可以跟傳統 Forth 程式混用,共享資源後執行出共榮結果。

可程式化的大數計算能力,是後來的發展,因為需要而加裝。加裝的過程中順便發現了設計出可程式化的複數計算能力、或比較罕用的可程式化常規分數計算能力、甚至於其他數系的可程式化計算能力都是類似的設計。原本的大數計算只考慮處理大整數問題,延伸性的研究,則設計出了能處理不限位數大浮點數問題的系統。

這個網頁過去公佈過的實際程式,都附有貼出時間。曾有好幾次在我發展出特殊性能而網貼成果時,網上都還見不到能執行出同樣性能的程式語言系統,網文貼出後,大家互相勉勵、互相砥礪,後來我就看到了別種程式語言性能上的進步。

互相觀摩別種程式語言設計出來的程式,能幫助自己設計 Forth 程式。公開 Forth 程式,當然也能幫助別人使用各自喜歡之程式語言設計程式。這裡刻意挑選了一個 Project Euler 網頁提出的範例『題目 8 』進行一次小小的貢獻。

為了砥礪前行,繼續發展 Forth ,這次,不用 BASIC 式的語法設計程式,純用 Forth 。

題目雖只採用一千位數來考驗程式設計,它卻是個形同『海底撈針』式的問題,舉例不用太誇張而使用海量位數,使用一千位數點到為止就夠了,用來研究問題才能恰到好處。

題目問你一千位數中連乘 4 位數者,那一個位置的數字最大。然後,再問你連乘 13 位數時又是那一個位置的數字最大?

題目從網頁擷圖展示如下,隨後附貼純用 Forth 寫成的程式及其執行後所得的答案。

所採用的系統為 ciForth 5.3.0 ,作業系統為 Ubuntu 20.04 。

鍾情於 Forth 的愛用者可以直接載入程式執行出結果。別種程式語言的愛用者,請勿嫌這個程式的冗長與複雜,它有助於了解求解問題的程式規劃步驟,指令名稱就是說明。

附記 : 由於三月份遠行返台一個月,貼文暫停兩次。四月份再見。

\ euler08.f

: cinumber ( addr -- n )
  0 0  rot dup 1+ c@ [char] . = >r
  $@ r@ if 1 /string then
  >number nip 0 = 
  if d>s r> if negate then 
  else r> drop 2drop 
  then ;

create chr 128 cells allot
create val 128 cells allot
create tmp 2 cells allot

s" 73167176531330624919225119674426574742355349194934" chr $!
s" 96983520312774506326239578318016984801869478851843" chr $+!
s" 85861560789112949495459501737958331952853208805511" chr $+!
s" 12540698747158523863050715693290963295227443043557" chr $+!
s" 66896648950445244523161731856403098711121722383113" chr $+!
s" 62229893423380308135336276614282806444486645238749" chr $+!
s" 30358907296290491560440772390713810515859307960866" chr $+!
s" 70172427121883998797908792274921901699720888093776" chr $+!
s" 65727333001053367881220235421809751254540594752243" chr $+!
s" 52584907711670556013604839586446706324415722155397" chr $+! 
s" 53697817977846174064955149290862569321978468622482" chr $+!
s" 83972241375657056057490261407972968652414535100474" chr $+!
s" 82166370484403199890008895243450658541227588666881" chr $+!
s" 16427171479924442928230863465674813919123162824586" chr $+!
s" 17866458359124566529476545682848912883142607690042" chr $+!
s" 24219022671055626321111109370544217506941658960408" chr $+!
s" 07198403850962455444362981230987879927244284909188" chr $+!
s" 84580156166097919133875499200524063689912560717606" chr $+!
s" 05886116467109405077541002256983155200055935729725" chr $+!
s" 71636269561882670428252483600823257530420752963450" chr $+!

: chr>val ( -- )
  val 128 cells erase
  chr $@ over + swap 
  do 
     tmp 2 cells erase 
     I c@ tmp $c+
     tmp cinumber val $c+
  loop ;

: mult13 ( add -- n )
  dup c@ swap 1+ dup 12 + swap
  do 
     I c@ * 
  loop ;

variable maxaddr
variable maxval

: 1000calculations ( -- )
  0 maxval !
  val $@ over dup maxaddr ! + swap
  do 
    I mult13 dup maxval @ >
    if   I maxaddr ! maxval !
    else drop
    then
  loop 
;

: check ( -- )
  maxaddr @  val $@ drop - 
  chr $@ drop + 13 type
; 

: main
  chr>val
  1000calculations
  cr ." greatest product 0f 13 adjacent digits are  " check 
  cr ." product value =  " maxval @ . ;

\s

Largest product in a series
Problem 8 
The four adjacent digits in the 1000-digit number 
that have the greatest product are 9 × 9 × 8 × 9 = 5832.

73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450

Find the thirteen adjacent digits in the 1000-digit number 
that have the greatest product. What is the value of this product?

Ans : 23514624000

greatest product 0f 13 adjacent digits are  5576689664895 <-- from 197 digits
product value =  23514624000  OK


2025年2月1日 星期六

專家系統

專家系統


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
2 February 2025


53年前(1972)出現過一個曾被公認是很成功的人工智慧軟體程式專家系統 MYCIN ,它被用來根據血液數據提供出診斷與治療建議。那時的電腦性能沒有那麼好,但可以容得下500條規則進行推論得出結果。這個應用典例,說明了專家系統由推理引擎及數據資料庫所形成,它是一種由專家整理出來的確定性人工智慧,由電腦執行的過程中,沒有模糊的(fuzzy)決策理念摻雜其間。它跟現今的熱門話題:運用類神經網路發展出來的人工智慧(AI)系統不同,差別就在設計系統時只依據專家總結出來的定論規劃程式。

運轉原子爐時,為了安全,必須講求運轉人員能迅速的確定爐心燃料棒的狀況,判斷狀況的依據則僅能根據各種儀錶的讀數。能運轉原子爐的工程師都是專家,都必須被訓練成能根據儀錶讀數解讀出燃料棒發生了什麼事情?而且判斷要快,才能立刻採取正確的處理措施,確保原子爐一定安全。

我曾運轉過一座 40MW(t) 的重水式原子爐許多年,爐心有大約 200 根以天然鈾製成的金屬燃料棒,由於它是專門供作研究用的巨型核輻射裝置,內部全面採用必須絕對安全的設計。例如:每單根燃料棒冷卻水的出口管路,都必須經過好幾套儀錶的監測,運轉時隨時提供重要數據,關鍵性的參數不正常時,還可能立即引發原子爐『急停』(trip),自動強制原子爐處於絕對安全的停爐狀態。

所用的監測儀錶有氣體分裂產物監測器(GFP)、冷卻水流量計(FT)、冷卻水溫度計(TE)、冷卻水壓力計(PIS).....等。由於壓力參數最敏感、響應最快速,一裝就三套,兩套接通到流量測量元件(FE)的上游,一套接通到下游,兩邊的壓差有 2 psi 的程度。冷卻水壓力出現問題時,壓力計的信號會直接驅動壓力控制器(PC),引發原子爐自動急停。

研究用原子爐的燃料棒比較容易出現異常狀況,因為爐心的運轉條件必須經常配合研究需要而調變。甚至於在全功率運轉狀況下,經常有添加的研究設施在爐心範圍內作動,也有只能短瞬間被照射的物質必須快速進出於爐心領域,而原子爐的動態性能研究本就是這種原子爐建造起來的目的。種種的研究需求都會令原子爐運轉功率變化,同時就令燃料棒的狀態跟著變化。

燃料棒出現問題時,出口冷卻水的監測參數會立刻顯現出來,根據參數判斷出問題是一門專家才能搞得很清楚的技術。判斷必須講求迅速,狀況又有許多種,把專家確定的知識設計成以電腦輔助運轉的專家系統就有價值。專家系統能快速的提供建議,告訴專家那些可能,例如:燃料棒內套管破了、外套管破了、管頂或管底的閥門漏水了、管內防止燃料棒斷掉的先斷式張力棒(tension member)斷掉了、管內冷卻水道變形或有雜物堵塞妨礙冷卻了.....等等現象,都需要能被快速的判斷出來,以便採取措施。這種專家技術討論起來有點複雜,我只把相關圖示貼在這裡供大家認識。請問:光憑上述資訊,能自己把儀錶參數與燃料棒狀況對應得上嗎?答案是不能,只能由學通過整個系統的專家來回答問題,這就是建立專家系統的好處。此外,我因較為資深,碰過例外情況,還能在例外狀況發生而專家系統上沒有列入程序的狀況下解釋問題,我做過這種貢獻,是比專家更專家的事情。
設計電腦的專家系統有許多例子可舉,我們可以舉一個很簡單之數學方面的專家程式為例,越簡單越好,主要目的只在說明專家系統的道理:

二元二次方程式的類型判別式,早就已經被整理得很完善了。從專門的數學手冊中,都可以找到這種規則分類表,使用者只須取出二元二次方程式的每一項係數,自行代入指定的幾個判別式,計算出結果,然後對照至規則分類表中的幾個條件,就能判別出方程式是屬於那一種幾何圖形?所根據的方法,在數學上稱為不變式論(Invarians)。

這樣的小型數學專家系統,於電腦的使用沒現在那麼方便前,至少還是要靠一點人工的計算,與人工設計的邏輯判斷來得到結果。但是,如果不能方便的設計程式,要解決這樣的問題,仍是不值得耗時、耗力去完成。 ABC Forth 系統改善了傳統 Forth 的這種情況,這一篇文章也係為了解決此類問題而寫。

在三位作者A. D. Aleksandrov(Editor ), A. N. Kolmogorov (Editor ), M. A. Lavrentiev(Editor ),所著之 Mathematics: Its Content, Methods, and Meaning (3 Volume Set).The MIT Press 於 March 15th 1969 出版的書中,Chapter 3 第十二節就刊載了整理得非常完善的規則分類表,我們就以這份表格來設計程式,設計出來的程式幾乎不用解說,任何人都可以很容易的明白與使用,程式中就得用到性能優越、用法平易近人的邏輯運算,才能表現得非常清楚。

由於我在設計 ciForth 的浮點運算系統時,把 『 E 或 e 』當作一個指令來處理出 10 的幾次方之表達方式,程式中不宜再用 E 或 e 來作為變數名稱。因此,這個範例程式中改採全用雙字元來宣告出變數,想用 E 或 e 時,改用 EE ,特此提示。


\ 二元二次方程式判別程式
\ 根據不變式論(Invarians)設計。
\ 作者:曾慶潭2011/4/16保有一切版權,知會作者,附加本宣告則允許引用。
\ Copyright 2011/4/16 Ching-Tang Tseng
\ Permission is granted by the author to use this software
\ for any application provided this copyright notice is preserved.
\ 2025/2/2重整內容並在Lina64之ABC Forth系統上執行,測試無誤。

10 Reals I1 I2 I3 K1 AA BB CC DD EE FF

: INPUT-COEFFICIENTS {{ AA = 9.0 E 0 }} {{ BB = 0.0 E 0 }} {{ CC = 1.6 E 1 }} {{ DD = 0.0 E 0 }} {{ EE = 0.0 E 0 }} {{ FF = 1.0 E 0 }} ; : DISCRIMINANTS BASIC 10 LET { BB = BB / 2.0 E 0 } 20 LET { DD = DD / 2.0 E 0 } 30 LET { EE = EE / 2.0 E 0 } 40 LET { I1 = AA + CC } 50 LET { I2 = AA * CC - BB * BB } 60 LET { I3 = AA * CC * FF + 2. E 0 * BB * DD * EE - AA * EE * EE - CC * DD * DD - FF * BB * BB } 70 LET { K1 = AA * FF - DD * DD + CC * FF - EE * EE } 80 END ; : DESCRIPTION BASIC 10 PRINT " 二元二次方程式:AX^2+BXY+CY^2+DX+EY+F=0 " 20 PRINT " 係數為: " 30 PRINT { " A = " ; AA ; " B = " ; BB ; " C = " ; CC } 40 PRINT { " D = " ; DD ; " E = " ; EE ; " F = " ; FF } 50 PRINT " 方程式為: " 60 END ; : MAIN BASIC 10 RUN INPUT-COEFFICIENTS 20 RUN DESCRIPTION 30 RUN DISCRIMINANTS 40 IF { ( I2 > f0.0E0 ) AND ( ( I1 * I3 ) < f0.0E0 ) } THEN 150 50 IF { ( I2 > f0.0E0 ) AND ( ( I1 * I3 ) > f0.0E0 ) } THEN 170 60 IF { ( I2 > f0.0E0 ) AND ( I3 = f0.0E0 ) } THEN 190 70 IF { ( I2 < f0.0E0 ) AND ( I3 <> f0.0E0 ) } THEN 210 80 IF { ( I2 < f0.0E0 ) AND ( I3 = f0.0E0 ) } THEN 230 90 IF { ( I2 = f0.0E0 ) AND ( I3 <> f0.0E0 ) } THEN 250 100 IF { ( I2 = f0.0E0 ) AND ( I3 = f0.0E0 ) AND ( K1 < f0.0E0 ) } THEN 270 110 IF { ( I2 = f0.0E0 ) AND ( I3 = f0.0E0 ) AND ( K1 > f0.0E0 ) } THEN 290 120 IF { ( I2 = f0.0E0 ) AND ( I3 = f0.0E0 ) AND ( K1 = f0.0E0 ) } THEN 310 130 PRINT " 已知條件不符合程式執行要求,未判別出結果。 " 140 GOTO 999 150 PRINT " 橢圓,典型方程式為:(X^2/a^2)+(X^2/b^2)=1 " 160 GOTO 999 170 PRINT " 虛橢圓,典型方程式為:(X^2/a^2)+(X^2/b^2)=-1 " 180 GOTO 999 190 PRINT " 點,典型方程式為:(X^2/a^2)+(X^2/b^2)=1 " 200 GOTO 999 210 PRINT " 雙曲線,典型方程式為:(X^2/a^2)-(X^2/b^2)=1 " 220 GOTO 999 230 PRINT " 一對相交的直線,典型方程式為:(X^2/a^2)-(Y^2/b^2)=0 " 240 GOTO 999 250 PRINT " 拋物線,典型方程式為:X^2=2pY,其中p=SQRT(-(I3/I1^3)) " 260 GOTO 999 270 PRINT " 一對平行線,典型方程式為:X^2=a^2 " 280 GOTO 999 290 PRINT " 一對虛平行線,典型方程式為:X^2=-a^2 " 300 GOTO 999 310 PRINT " 一對重合的直線,典型方程式為:X^2=0 " 999 END ; 載入程式後,執行MAIN指令,得到下列結果: MAIN 二元二次方程式:AX^2+BXY+CY^2+DX+EY+F=0 係數為: A = 9.000000000 B = 0.000000000E0 C = 16.00000000 D = 0.000000000E0 E = 0.000000000E0 F = 1.000000000 方程式為: 虛橢圓,典型方程式為:(X^2/a^2)+(X^2/b^2)=-1 ok


這樣的程式顯示出幾項特點:

(1) 並不是只有從頭到底都必須一直計算的程式,才叫數學計算程式。這個程式根本只進行很簡單的數學計算,最後能得到正確答案,主要還是依靠邏輯判斷的功能。

(2) 非結構化的程式性能,並不是不好的性能。這個程式簡直就是一個澈底非結構化的典型程式,但是!我在設計它時,就因為系統具有能寫非結構化程式的功能,如此設計出來的程式,簡直就是照規則表直接翻譯,一次完成。

大家現在就回頭讀一讀看 MAIN 指令的內容,您不可能會看不懂程式在做甚麼?嚴格的專家經驗,就需要這樣的程式功能來表達,可貴的經驗才不會被扭曲。
反之,您若使用現行任何一種號稱嚴格結構化的程式語言,設計這種程式,邏輯判斷三次,就足以將程式設計者搞混了,那怕您將程式依序一層一層的縮格,規劃得再漂亮,寫完程式後五分鐘,就無法正確的維護了。那麼,要結構化程式語言何用?不如就像 ABC Forth 系統一樣,能夠非結構化。

作者曾經是核子反應器廠(俗稱原子爐)的運轉專家,深知當年想以程式實現專家經驗時的關鍵問題所在,如果那時我就設計了具有邏輯功能的 ABC Forth 系統,想具體實現作者的專家經驗,並實際應用設計出來的程式,作為核子反應器廠運轉時的強力輔助工具,實現計劃就不會那麼困難了。

(3) 還有,最重要的一點,就是,如果系統不提供純粹中文式的邏輯運算功能,我就不覺得設計上述的程式,對中國人而言,會有任何意義了。因為邏輯條件講求嚴謹,輸出結果講求絕對正確,程式的日後維護講求易看、易懂、易修改,沒有中文功能,就不必白費力氣去設計日後難用、難維護的程式。

(4) 近代應用於解決數學問題的商售大型套裝軟體,由於電腦易於安裝廉價大量的記憶體,執行速度也不再是應用時主要的考量問題。系統的功能也開始提供非純粹計算式的解題功能。例如:執行求解數學式子的微積分問題時,根本不執行數值分析程式,改成設計對輸入數學式的文字或符號式剖析,再經歸納出來的數學分析規則,只進行簡單的判別式計算,就能輸出正確解答。這樣的程式功能,就得依靠本章中的『條件整合』式邏輯運算功能來處理問題,上述的程式提供了一點解決此類問題的思考依據。

(5) 電腦輸出決策絕對重要,如果最聰明的人類,能夠製作出最關鍵性的決策表格,截至目前為止,甚麼程式語言?才能提供人類最清楚的表達程式,令其與表格同步。甚麼樣的程式?放幾十年後,還能夠照樣清楚的反應出專家技術。這些問題,表示為電腦設計出簡單明確的邏輯運算功能非常重要。現行 ABC Forth 數學計算系統,具備這樣的特質。


2025年1月15日 星期三

ANSI標準FORTH指令

ANSI標準FORTH指令


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
16 January 2025

 

一、離開及協助了解系統的指令

ENVIRONMENT?		( strAddr u – x flag | flag | 0 )
以系統正式名稱之字串前引,執行此指令詢問系統,留下描述值及非零旗號。
UNUSED			    ( -- u )
指令字典區自由而尚未使用之記憶體數量。
WORDS				( -- )
列印出首先被指定在搜尋順序字彙表中分屬於各個字彙之下的所有指令。	
BYE				    ( -- )
離開FORTH系統。
TIME&DATE			( -- +n +n +n +n +n +n )
執行後獲得秒、分、時、日、月、年。

二、檢視記憶體內容及除錯用指令

DEPTH				( -- +n )
堆疊被使用了的單元之個數被推入堆疊。
LIST 				( u -- )
列印出第u個磁碟區塊的內容。
DUMP				( addr u -- )
傾印出addr位址起u個單位記憶體的內容。
?				    ( addr -- )
顯示儲存於addr此一單元記憶體內之整數值。
.S				    ( -- same )
以不影響原存內容方式列印出堆疊內容。
SEE defname			( -- )
反編譯出defname指令之程式內容。

三、改變編譯及執譯設定狀態之指令

BASE				( -- addr )
留下存放數基之單元記憶體的位址,用於基底轉換。
DECIMAL			    ( -- )
設定系統以十進制為基底。
FORGET defname		( -- )
將defname與所有其後所加之指令,從現行編譯字彙中刪除。
HEX				    ( -- )
設定系統以十六進制為基底。
MARKER name		    ( -- )		“標示”
定義name就是一個指令,執行此指令時,系統就將其後所加之指令全數刪除。
ALSO				( -- )		“還是”
複製第一個被搜尋的字彙於搜尋順序列表的頂部。
DEFINITIONS		    ( -- )		“定義”
將新定義之指令編納入第一個被搜尋的字彙。
FORTH				( -- )
系統主要字彙,執行後成為第一個被搜尋之字彙。
FORTH-WORDLIST	    ( -- selx )
留下系統主要字彙之識別碼。
GET-CURRENT		    ( -- selx )
留下接受新定義指令之現行字彙識別碼。
GET-ORDER			( -- selx… n )
以搜尋順序取得所有字彙之識別碼及全部字彙個數。
ONLY				( -- )
設定成只剩單一個ROOT根字彙的最小搜尋順序。
ORDER				( -- )
列印出字彙搜尋順序。
PREVIOUS			( -- )		“前一字彙”
取消現行字彙,並以下一個字彙作為新的第一個字彙。
SET-CURRENT		       ( selx -- )
根據字彙識別碼設定其為接受新定義指令之字彙。
SET-ORDER			( selx… n -- )
設定n個深度的字彙搜尋順序。
WORDLIST name		( -- )
定義name為新字彙的名稱。(傳統FORTH中稱為VOCABULARY)
name  				( -- selx )
執行name時,留下name字彙識別碼。
ASSEMBLER			( -- )
設定系統優先搜尋組合語言編譯程式字彙。
EDITOR				( -- )		“編輯”
設定系統優先搜尋編輯程式字彙。

四、源程式預處理、執譯、依訊息旗號編譯之指令

.( abc… )			( -- )
拓印出兩個小括弧間的文字。
INCLUDE-FILE		( … selx -- … )
執譯一個以檔案識別碼開啟的檔案程式。
INCLUDED		    ( … straddr u-- … )
開啟數個檔案程式並執譯之。
LOAD				( … u-- … )	“載入”
執譯整個u磁碟區塊,然後恢復系統原來接受輸入的設定狀態。
THRU				( …u1 u2 -- … )
使用u1至u2為區塊範圍,執行與LOAD指令相同的工作。
[IF] wordnumber		( flag -- )
[ELSE] wordnumber   ( -- )
[THEN]			    ( -- )
若旗號為真,執行[IF]…[ELSE]或[IF]…[THEN]之間的所有指令,旗號為假則執行[ELSE]或[THEN]以後所有的指令。

五、加入說明、註解用指令

\ abc…				( -- )		“背斜線”
此列反斜線以後文字均為註解。
( abc… )			( -- )
兩個小括弧間的內容為說明文字。

六、動態記憶體操作指令

ALLOCATE			( u – addr 0 | addr x, failure )	
配置從addr起的u個記憶體單元,作為控留空間,0旗號表成功。
FREE				( addr – 0 | x, failure )
解除從addr起曾被ALLOCATE指令控留配置過的記憶體區域,0旗號表成功。
RESIZE				( addr u – addr’ 0 | addr x, failure )	
重新配置自addr起u個記憶體用量的使用範圍,作為控留空間,0旗號表成功。

七、字串操作用指令

CONVERT			    ( ud strAddr – ud’ strAddr’ )
轉換strAddr+1開始的字串成相對應的堆疊數字,將此數值累加在雙整數上,轉換終止於第一個非數字的字元位置,也就是strAddr’。
此指令功能全面讓渡給>NUMBER指令了。
COUNT				( strAddr – strAddr’ u )
包封字串的計量長度留在堆疊上,原位址則加上此長度值。
ERASE				( addr u -- )
從addr位址起,將u個字元位址的記憶體內容清除為0。
FILL				( strAddr u char -- )
從strAddr起設定u個字元位址的記憶體內容為字元char之碼。
HOLD				( char -- )
添加char字元到數字字串中去。
MOVE				( addr addr’ u -- )
從addr起拷貝u個值到addr’起的記憶體中去。
>NUMBER			    ( ud strAddr u – ud’ strAddr’ u’ )
根據數基變數BASE所存放之基底值,將以strAddr u代表之數字字串的值加於ud上,得到ud’,留下以strAddr’ u’ 代表之不能將字轉換成值的剩餘字串。
<# prependWord    ( -- )		              "開始削剖出數字"
開始進行從ud轉換成數字字串之添加字元程序。
#>				    ( ud – strAddr )              "削剖結束"
棄除轉換不了的被除數,留下轉換成功的包封數字字串於strAddr。
#				    ( ud – ud’ )	               "削剖出一位數字"
從ud中轉換出下一位數字,並將其添加於數字字串。
#S				    ( ud – ud’為zero )             "削剖出剩下的數字"
從ud中轉換出所有剩下的位數,並將其全部添加於數字字串。
SIGN				( n -- )
如果n<0,則添加負號於數字字串。
BLANK				( strAddr u -- )
從strAddr起u個字元轉換成空格字元。
CMOVE				( strAddr strAddr’ u -- )
按由低位址到高位址的方式,拷貝u個單位由strAddr到strAddr’。
CMOVE>			    ( strAddr strAddr’ u -- )
與CMOVE性質類似,但拷貝方式為由高位址到低位址。
COMPARE			    ( strAddr u strAddr’ u’ – 0 | -1 | 1 )
比對由strAddr u與strAddr’ u’分別代表的兩組字串,完全一致時留下0。
SEARCH				( strAddr u strAddr’ u’ – addr u” flag )
從strAddr’ u’代表的字串,開始進行掃描式的搜尋,當獲得了與strAddr u代表的字串一致的結果時,留下為真的旗號,與以addr u” 所代表的剩餘尚未掃描字串。
/STRING			    ( strAddr u n – strAddr u’ )    "刪除字串尾部"
去除掉strAddr u所代表字串後面的n個字元。
-TRAILING			( strAddr u – strAddr u’ )	 "減掉尾部空格"
去除掉strAddr u所代表字串尾部所有的空格字元。
>FLOAT				( strAddr u – float flag | 0, failure )
將strAddr u所代表的字串轉換成浮點數,並留下代表成功與否的結果旗號。
REPRESENT			( float strAddr u – n flag flag’ )
將float浮點數轉換成以strAddr u所代表的u位數假數字串,留下方次指數n,表正或負數的旗號flag,與能否在表示範圍內的旗號flag’。

八、使用檔案或區塊緩衝區方式,執行磁碟輸出/輸入時的指令

BLOCK				( u – addr )
讀進第u個磁碟區塊的資料,移入一個指定的磁碟緩衝區,並留下相關位址。
BUFFER				( u – addr )
於磁碟緩衝區設定為第u個區塊,並留下相關位址,但不執行實際讀取的工作。
EMPTY-BUFFERS		( -- )
清空整個磁碟緩衝區,包括被更新過的區塊。
FLUSH				( -- )
儲存緩衝區內更新過的資料,然後清空整個緩衝區。
SAVE-BUFFERS		( -- )
儲存緩衝區內更新過的資料,但不執行清空的工作。
SCR				    ( -- addr )
將最後列印顯示的區塊相關位址留在堆疊上。
UPDATE				( -- )
現行使用中的區塊標示為更新過。
BIN				    ( x – x’ )
修正原檔案模式x成選定的二進制x’。
CLOSE-FILE			( selx – 0 | x, failure )
關閉檔案識別碼為selx的檔案,留下代表執行結果的旗號。
CREATE-FILE		    ( strAddr u x – selx 0 | x x, failure )
產生一個以strAddr u所代表字串為名稱的新檔案,並以x的模式開啟它,留下檔案識別碼selx及代表執行結果的旗號。
DELETE-FILE		    ( strAddr u – 0 | x, failure )
刪除以strAddr u所代表字串為名稱的檔案,並留下代表執行結果的旗號。
FILE-POSITION		( selx – ud 0 | x, failure )
留下檔案識別碼為selx檔案的現行操作位址ud,並留下代表執行結果的旗號。
FILE-SIZE			( selx – ud 0 | x, farilure )
留下檔案識別碼為selx檔案的容量大小ud,並留下代表執行結果的旗號。
FILE-STATUS		    ( strAddr u – x 0 | x, failure )
留下以strAddr u所代表字串為名稱的檔案狀態,0旗號代表檔案存在。
FLUSH-FILE			( selx – 0 | x, failure )
閃存檔案識別碼為selx的檔案,並留下代表執行結果的旗號。
OPEN-FILE			( strAddr u x – selx 0 | x x, failure | x’ | x” )
以x模式開啟以strAddr u所代表字串為名稱的檔案,執行結果成功時留下檔案識別碼及0,失敗時則留下代表為失敗、唯讀、或可讀寫檔案的旗號。
R/O				    ( -- x )
留下代表僅能讀出檔案模式的旗號。
R/W				    ( -- x )
留下代表可讀可寫檔案模式的旗號。
READ-FILE			( strAddr u selx – u’ 0 | u’ x, failure )
以檔案識別碼selx開啟檔案,讀進u個以下數量的字元於以strAddr為起始位址的區域,留下代表讀得字元的總數量u’,及代表成功與否的旗號。
READ-LINE			( strAddr u selx – u’ x 0 | u’ x x , failure | u’ 0, eof 0 )
與READ-FILE相似,但u’字元數量僅代表一列之量,而且遭遇到檔案結束碼時,三個執行結果的中間那個數值為0。
RENAME-FILE		    ( strAddr u straddr’ u’ – 0 | x, failure )
以strAddr’ u’所代表字串之新檔名取代以strAddr u所代表字串之舊檔名,並留下代表執行結果的旗號。
REPOSITION-FILE	    ( ud selx – 0 | x, failure )
重新設定selx檔案識別碼的檔案之操作位址為ud,並留下代表執行結果的旗號。
RESIZE-FILE			( ud selx – 0 | x, failure )
重新設定selx檔案識別碼的檔案之容量大小為ud,並留下代表執行結果的旗號。
W/O				    ( -- x )
留下代表僅能寫入檔案模式的旗號。
WRITE-FILE			( strAddr u selx – 0 | x, failure )
將strAddr位址起的u個字元寫入檔案識別碼為slex的檔案,並留下代表執行結果的旗號。
WRITE-LINE			( strAddr u selx – 0 | x, failure )
以strAddr位址起的u個字元,作為新的一列資料,加入檔案識別碼為slex的檔案內,並留下代表執行結果的旗號。

九、其他輸出/輸入操作指令

ACCEPT abc…		    ( strAddr +n -- +n(count-recd) )
讀進一列以內的+n個字元,儲存在以strAddr為起始位址的記憶體內。
CR				    ( -- )
換新列或印出等同於換新列的結果。
.				    ( n -- )		D.(double)		"拓印"
根據BASE內所儲存的數基之值,拓印出數值n的數字。
.R				    ( n n’ -- )		D.R(double)
在n’個字元寬度的欄位內,以右邊對齊的方式,拓印出n。
.” abc… “			( -- )
編納字串以及印出字串的指令入系統。
EMIT				( x -- )
印出相關於x的可印出字元。
EXPECT abc…		    ( strAddr +n -- )
讀進+n個字元,儲存在以strAddr為起始位址的記憶體內。
KEY (keypress)		( -- char )
讀進單一個鍵盤輸入的字元。
SPACE				( -- )
印出單一個空格字元。
SPACES				( n -- )
印出n個空格字元。
TYPE				( strAddr u -- )
印出以strAddr記憶體位址為起始的首u個字元。
U.				    ( u -- )		與『.』的意義類似
以無正負號數值的格式,印出數值u之數字。
U.R				    ( u n -- )		與『.R』的意義類似
在n個字元寬度的欄位內,以右邊對齊的方式,拓印出無號數u。
F.				    ( float -- )		例如:123400.
以固定點數字表示格式,印出浮點數float。
FE.				    ( float -- )		例如:123.4e3
以10的方次指數數字表示格式,印出浮點數float 。
FS.				    ( float -- )		例如:1.234e5
以科學領域常用的數字表示格式,印出浮點數float。
AT-XY				( u u’ -- )
設定EMIT指令執行時的目標區為第u直行,第u’橫列。
EKEY				( -- slex )
接受一次鍵盤發生過的訊息。
EKEY>CHAR		        ( slex – char flag | 0, failure )
將鍵盤發生過的訊息碼slex轉換成字元碼,轉換不成也得留下代表失敗的0碼。
?EKEY				( -- flag )
若鍵盤未發生任何訊息,留下0旗號。
?EMIT				( -- flag )
若顯示訊息裝置尚未備便,留下0旗號。
?KEY				( -- flag )
若鍵盤不曾有任何鍵被壓下,留下0旗號。
MS				    ( u -- )
讓系統耗住至少u毫秒。
PAGE				( -- )
設定EMIT指令執行時的目標區為新的一頁或新的一幕。

十、算數及邏輯運算指令

ABS				    ( n – u )		DABS(double)
u為n之絕對值。
AND				    ( x x’ – x” )		“而且”
x及x’之每一位元分別進行『且』(AND)之邏輯運算,得到x”的結果。
FM/MOD			    ( d n – n’ n” )
被除數d除以除數n,得到餘數n’ 及商數n”,但餘數必與除數同號。
INVERT				( x – x’ )
x’中的每一個位元均為x中對應位元的反轉狀態。
LSHIFT				( x u – x’ )
x中所有的位元一起向左位移u次,得到x’的結果。
M*				    ( n n’ – d )
n乘以n’,得雙整數d。
MAX				    ( n n’ – n” )		DMAX(double)
n與n’比較後,棄除較小的數字,結果為n”。
MIN				    ( n n’ – n” )		DMIN(double)
n與n’比較後,棄除較大的數字,結果為n”。
-				    ( nlu nlu’ – nlu” )	D-(double)
nlu減nlu’得到結果nlu”。
MOD				    ( n n’ – n” )
被除數n除以除數n’,不留商數只留下餘數n”。
*/MOD				( n n’ n” – n’” n”” )
n乘上n’除以n”,得餘數n’”及商數n””。
/MOD				( n n’ – n” n’” )
n除以n’,得餘數n”及商數n’”。
NEGATE				( n – n’ ) 		DNEGATE(double)
n’為n的負值,亦即n’ = 0 - n。
1+				    ( n – n’ )
n’為n加1。
1-                  ( n – n’ )
n’為n減1。
OR				    ( x x’ – x” )		“或者”
x及x’之每一位元分別進行『或』(OR)之邏輯運算,得到x”的結果。
+				    ( nlu nlu’ – nlu” )	D+(double)		M+(double)
nlu”為nlu加nlu’。
+!				    ( nlu addr -- )	D+!(double)
將記憶體位址addr內之值加上nlu。
RSHIFT				( x u – x’ )
x中所有的位元一起向右位移u次,得到x’的結果。
/				    ( n n’ – n” ) 		D/(double)
n”為n除以n’。
SM/REM				( d n – n’ n” )
被除數d除以除數n,得到餘數n’ 及商數n”,但餘數必與被除數同號。
*				    ( nlu n’lu’ – n”lu” )
n”為n乘上n’。
*/				    ( n n’ n” – n’” )	M*/(double)
n’”為n乘上n’除以n”。
2*				    ( x – x’ )		D2*(double)
x左移一個位元,最低效位元填入0,得到x’。
2/				    ( x – x’ )		D2/(double)
x右移一個位元,最高效位元內容不變,得到x’。
UM*				    ( u u’ – ud )
無號數u乘上無號數u’,得到無號雙整數ud。
UM/MOD			    ( ud u – u’ u” )
無號雙整數ud除以無號數u,得到無號餘數u’及無號商數u”。
XOR				    ( x x’ – x” )		 “一是一非”, “僅單個對”
x及x’之每一位元分別進行『互斥或』(XOR)之邏輯運算,得到x”的結果。
F*				    ( float float’ – float” )
浮點數相乘,float” = float * float’ 。
F/				    ( float float’ – float” )
浮點數相除,float” = float / float’ 。
F+				    ( float float’ – flaot” )
浮點數相加,float” = float + float’ 。
F-                  ( float float” – float” )
浮點數相減,float” = float - float’ 。
F+!				    ( float addr -- )
將記憶體位址addr內原存之浮點數值加上額外的float浮點數值。
FLOOR				( float – float’ )
按系統現行指定的有效位數,對float進行直接的捨位處理,得到float”。
FMAX				( float float’ – float” )
浮點數float與float’比較後,棄除較小的數字,結果為float”。
FMIN				( float float’ – float” )
浮點數float與float’比較後,棄除較大的數字,結果為float”。
FNEGATE			    ( float – float’ )
float’為float的負值,亦即float’ = 0 - flaot。
FROUND			    ( float – float’ )
按系統現行指定的有效位數,對float進行四捨五入式的捨位處理,得到float'。

十一、數字型態轉換操作指令

S>D				    ( n – d )
將整數轉換成雙整數,保留原有的正或負號。
D>S				    ( d – n )
將雙整數轉換成有號整數。
D>F				    ( d – float )
將有號雙整數轉換成浮點數。
F>D				    ( float – d )
將浮點數轉換成有號雙整數。

十二、宣告數字結構時使用的指令

CONSTANT	name	( x -- )		此後name執行時	( -- x )		“常數”
建立具有不可改變數值x之常數名稱name,執行常數名稱name時,將數值x放在堆疊上。
VALUE		name	( x -- )		此後name執行時	( -- x )		“變常數”
建立可以改變數值之變常數名稱name,變常數name之值為最後以TO指令存入之值,執行變常數名稱name時,將數值x放在堆疊上。
VARIABLE	name	( -- )		        此後name執行時	( -- addr )		“變數”
建立可以改變數值之變數名稱name,變數name之值以!或@指令來存或取,執行變數名稱name時,將儲存變數之位址addr放在堆疊上。
2CONSTANT	name	( x x’-- )		此後name執行時	( -- x x’ )
建立具有不可改變數值x x’之雙整數常數名稱name,執行雙整數常數名稱name時,將雙整數x x’放在堆疊上。
2VARIABLE	name	( -- )			此後name執行時	( -- addr )
建立可以改變數值之雙整數變數名稱name,此變數name之值以2!或2@指令來存或取,執行此變數名稱name時,將儲存此變數之位址addr放在堆疊上。
FCONSTANT	name	( float -- )		此後name執行時	( -- float )
建立具有不可改變數值float之浮點數常數名稱name,執行此常數名稱name時,將浮點數值float放在堆疊上。
FVARIABLE	name	( -- )			此後name執行時	( -- addr )
建立可以改變數值之浮點數變數名稱name,此變數name之值以F!或F@指令來存或取,執行此變數名稱name時,將儲存此變數之位址addr放在堆疊上。

十三、記憶體與堆疊間傳送數值時使用之指令

C@		            ( -addr – char )
從對齊(aligned)位址-addr飛取字元char。
C!		            ( char –addr -- )
將字元char存入對齊之位址-addr。
@ 		            ( -addr – x )		“飛取”
從對齊單元位址-addr飛取單整數x。
2@		            ( -addr – x x’ )
從對齊位址-addr飛取兩個單整數x及x’。
!		            ( x –addr -- )		“存入”
將單整數x存入對齊單元位址-addr。
2!		            ( x x’ –addr -- )
將兩個單整數x及x’存入對齊單元位址-addr。
TO defValuName		( x -- )
將單整數x存入以VALUE宣告而成的變常數。
F@		            ( -addr – float )
從對齊浮點位址-addr飛取浮點數float。
F!		            ( float –addr -- )
將浮點數float存入對齊浮點位址-addr。
TO defLocaName		( x -- )
將單整數x存入以LOCAL宣告而成的局部變數。

十四、比較操作用指令

=		        ( x x’ – flag )	D=
x等於x’時,留下真值旗號。
>		        ( n n’ – flag )
n大於n’時,留下真值旗號。
<		        ( n|u n’|u’ – flag )	D<
n小於n’時,留下真值旗號。
<		        ( n|u n’|u’ – flag )
n不等於n’時,留下真值旗號。
u<		    ( u u’ – flag )	    DU<
u小於u’時,留下真值旗號。
WITHIN	        ( n|u n’|u’ n”|u” – flag )
n>=n’<n”或u>=u’<u”時,留下真值旗號。
0=		        ( x – flag )		    D0=
x等於0時,留下真值旗號。
0>              ( n – flag )
n大於0時,留下真值旗號。
0<		    ( n – flag )		D0<
n小於0時,留下真值旗號。
0<		    ( x – flag )
x不等於0時,留下真值旗號。
F< 		    ( float float’ – flag )
Float小於float’時,留下真值旗號。
F0=		        ( float – flag )
float等於0時,留下真值旗號。
F0< 	        ( float – flag )
float小於0時,留下真值旗號。

十五、系統常數以及用來產生ASCII值所使用的指令

BL			    ( -- char )
將空格之美國資訊交換標準碼放到堆疊上。
CHAR abc		( -- char )
將後續用字的第一個字元的美國資訊交換標準碼放到堆疊上。
[CHAR] abc		( -- char ) (Runtime)
於編譯狀態使用此指令,系統會將後續用字的第一個字元的美國資訊交換標準碼編譯進系統。
以此指令設計而成的指令,執行(Runtime)到此處時,此碼將被放到堆疊上。
FALSE			( -- flag )
將代表為假的旗號值放到堆疊上。
TRUE			( -- flag )
將代表為真的旗號值放到堆疊上。

十六、形成有限迴路使用的指令(限於編譯狀態使用)

DO | ?DO word|number	( n|u n’|u’ -- )
標示為後續指令(word)或待處理數字(number)開始執行至少一次迴路的起始處。
如果n’大於或等於n,則?DO就完全不執行迴路。
I 			    ( -- n|u )
將上述迴路中新的n’值放到堆疊上。
J			    ( -- n|u)
與I指令的功能相同,但用來將更外一層迴路新的n’值放到堆疊上。 
LEAVE | UNLOOP	 word|number		( -- )
提前離開迴路,棄除而不再執行由此指令到迴路終止處的所有指令或數字。
LOOP | +LOOP    ( n” -- )
迴路終止處,當每次n’增量後仍小於n時,繼續回到迴路起始處,
+LOOP則使用n”而非1當增量。

十七、形成無限循環的指令(限於編譯狀態使用)

BEGIN word|number	( -- )
標示重覆執行後續指令或被處理數字的循環起始處。
AGAIN| 		        ( -- )
循環的終止處,但永遠重覆回到BEGIN標示的循環起始處。
UNTIL|			    ( flag -- )
循環的終止處,當flag旗號仍然為真時,重覆回到BEGIN標示的循環起始處。
WHILE word|number	( flag -- )
當flag旗號仍然為真時,執行由WHILE到REPEAT間的指令或處理出現的數字,然後重覆回到BEGIN標示的循環起始處,繼續形成循環。
REPEAT			    ( -- )
循環的終止處,當flag旗號為假時,棄除而不再執行或處理由WHILE 到REPEAT之間所有的指令或數字。

十八、其它編譯狀態使用的指令

ABORT 			    ( x…-- )
清空數據堆疊,執行潰停(QUIT)指令。
ABORT” abc…” 		( x…x’ -- )
將兩個雙引號之間的字串編進系統,當x’不為0時,執行ABORT並印出此字串。
C” abc…”		    ( -- strAddr )
將兩個雙引號之間的字串編進系統,並留下此字串之起始位址於堆疊上。
CASE word|number 	( -- ) 
標示CASE配合下列三個指令形成條件分支結構的起始處。
OF word|number		( x x’ -- )
如果x’等於x,則執行此條件判斷處後續OF到ENDOF之間的所有指令,然後跳到ENDCASE之後繼續執行後續指令。
如果x’不等於x,則跳到下一個ENDOF之後繼續執行後續指令。
ENDOF word|number 	( -- )
標示為前一個OF的結束處,以形成單一條件狀況成立時的分支執行內容。
ENDCASE		        ( x -- )
為整個CASE結構的終止處,棄除x。
: name |		    ( -- )
開始定義名稱為name的新指令。
:NAME word|number	( --xtoken )
開始定義一個沒有名稱的有體無頭指令,定義完成後,留下此指令的起始執行位址xtoken,可供給EXECUTE指令使用而被執行。
;			        ( -- )
完成name新指令之定義,其內容係前述所有word|number編譯而成的程式。
IMMEDIATE		    ( -- )
將剛完成定義之name指令,標示成編譯狀態會被立即執行。
EXIT			    ( -- )
讓程式在這個指令出現的位置直接跳出此指令的定義之外。
IF word|number		( flag -- ) 
如果旗號flag為真,則執行自IF以後直到ELSE再跳到THEN的程式。
ELSE word|number	( -- )
如果旗號flag為假,則執行ELSE以後直到THEN間的程式。
THEN			    ( -- )
如果旗號flag為假,又無ELSE,則不執行IF到THEN間的程式。
[ word|number		( -- )
改變系統原為編譯狀態成執譯狀態,直接執行後續指令。
QUIT			    ( -- )
潰停系統,清空回返堆疊,歸原系統的輸出/輸入成原始的顯示狀態,然後重覆不斷地執行讀進一列輸入,執譯它,顯示系統立即執行後的提示。
RECURSE		        ( -- )
在定義中叫用自己。
] word|number		( -- )
開始編譯後續所有非立即執行性的指令。
S” abc…”		    ( -- strAddr )
開始編譯後續以另一個雙引號『”』為界限的所有字串,執行此指令時,此字串的起始位址會被放在堆疊上。
CATCH			    ( …xtoken -- …0 )		( …xtoken -- …n(exception) )
執行一長串xtoken代碼指令後,留下是否會令系統出問題之例外情況相關參數0或n。
THROW			    ( 0 -- ) ( -1 -- ) ( -2 -- ) ( n – n )
為0時,表不會令系統出問題,棄除此0,並讓系統繼續正常工作。
為-1時,叫用ABORT。
為-2時,執行ABORT”。
為其他值時,跳出執行中的程式,前往CATCH最近剛剛要求之例外處理程式,並調整系統成接受新的輸入,且堆疊亦調整成相關設定。
CODE name 		    ( -- )
開始以name為指令名稱之低階指令定義。
code/word
此部份由code/words形成的程式隨系統而異,因此均不限定為標準指令。
;CODE codeword		( -- )
以低階碼編譯的指令,轉換成編譯完成一個定義程式。
(LOCAL)		        ( strAddr +n -- ) ( strAddr 0 -- )
非0之n可產生一個以strAddr表示之局部變數,此變數會將其值放在堆疊上。
局部變數的新值,以TO指令置入。
LOCALS | name… | name 	( x… -- ) ( -- x )
產生names個各有其名稱之局部變數,其起始值均由堆疊上各自相關的x決定,可用之正常局部變數為8個,新置入值以TO指令操作。

十九、堆疊操作指令

DROP		        ( x -- )
自堆疊拉出x。
2DROP		        ( x x’ -- )
自堆疊拉出x及x’。
DUP		            ( x – x x )
複製一個x並推入堆疊。
2DUP		        ( x x’ – x x’ x x’ )
複製一組x與x’,並將兩者推入堆疊。
?DUP		        ( x – x x ) ( 0 – 0 )
複製一個非0的x並推入堆疊,若x為0則保持堆疊狀況不變。
NIP			        ( x x’ – x’ )        "掐掉"
移除x。
OVER		        ( x x’ – x x’ x )
複製x並推入堆疊。
2OVER		        ( x x’ x” x’” – x x’ x” x’” x x’ )
複製一組x與x’,並將兩者推入堆疊。
PICK		        ( x … +n – x … x’ )
複製第n+2個深度(+n自身也當作一個深度單位)的輸入單元,並推入堆疊。
2>R			        ( x x’ -- )
將x及x’移入回返堆疊。
R>                  ( -- x )
將回返堆疊中的x移入數據堆疊。
2R>			        ( -- x x’ )
將回返堆疊中的x及x’移入數據堆疊。
R@			        ( -- x )
複製回返堆疊中執行位址之值到數據堆疊。
2R@		            ( -- x x’ )
複製回返堆疊中x及x’兩個值到數據堆疊。
ROLL		        ( x … +n – x … x’ )
旋置堆疊中第n+2個深度(+n自身也當作一個深度單位)的輸入單元至頂部。
ROT		            ( x x’ x” – x’ x” x )
旋置x到堆疊頂部。
SWAP		        ( x x’ – x’ x )
互換x及x’的位置。
2SWAP		        ( x x’ x” x’” – x” x’” x x’ )
互換xx’及x”x’”的位置。
TUCK		        ( x x’ – x’ x x’ )        "壓下"
複製堆疊頂部之值,並使其成為第三個單元之值。
2ROT		        ( x x’ x” x’” x”” x’””– x” x’” x”” x’”” x x’ )
將xx’移動到堆疊頂部。
FDEPTH		        ( -- +n )
浮點堆疊被使用了的單元之個數被推入堆疊。
FDROP		        ( float -- )
棄除浮點堆疊或一般數據堆疊上的浮點數float。
FDUP		        ( float – float float )
複製浮點堆疊或一般數據堆疊上的浮點數float。
FOVER		        ( float float’ – float float’ float )
複製浮點數float,並將其推入浮點堆疊或一般數據堆疊。
FROT		        ( float float’ float” – float’ float” float )
旋置浮點數float到浮點堆疊或一般數據堆疊的頂部。
FSWAP		        ( float float’ – float’ float )
互換浮點堆疊或一般數據堆疊上的float 與float’>。

二十、結構性編譯程式與執譯程式增用指令

ALIGN 		FALIGN(float)		( -- )
調整資料空間指標到實體記憶體之齊整位址。
ALIGNED 	FALIGNED(float)	( addr – addr’ )
確保原記憶體位址addr為齊整位址addr’,以儲存特定型態之資料。
ALLOT		        ( n -- )
資料儲存記憶空間之指標加上n。
>BODY		        ( xtoken – addr )
以指令之資料儲存位址addr,取代原為此指令之執行位址xtoken。
C,			        ( char -- )
為最近定義的指令,將字元char存入資料儲存記憶空間。
CELL+ 		FLOAT+(float)		( -addr -- -addr’ )
將以單元為計量標準的位址-addr增加一個單元量,成下一個位址-addr’。
CELLS		FLOATS(float)		( n – n’ )
將n個單元所需要的記憶體位址數量推放於堆疊上。
CHAR+		        ( -addr -- -addr’ )
將以字元為計量標準的位址-addr增加一個字元量,成下一個位址-addr’。
CHARS		        ( n – n’ )
將n個字元所需要的記憶體位址數量推放於堆疊上。
,			        ( n|u – )
為新近定義之指令,將n或u碼嵌入系統。
COMPILE,		    ( xtoken -- )
為新近定義之指令,編納一個已存在指令之相關起始執行位址xtoken。
[COMPILE] immWord	( -- )
為新近定義之指令,編納一個名稱為immWord的立即執行性指令,原要求編譯程式應立即執行之動作,將延後一個執行時層才執行此指令。
CREATE name		    ( -- )
name			    ( -- -addr )
創造一個名稱為name之資料結構,使其形同亦為一個指令之名稱。
資料結構name被叫用時,系統會將資料空間之起始指標位址addr推放於堆疊上。
: newDefiner			
Word|number1…
CREATE
Word|number2 …
DOES>			    ( -- -addr )
Word|number3 … ;
這個CREATE創造性指令,亦可以上列方式,使用於一種所謂的『造指令的指令』newDefiner之指令設計中。
系統每次再度使用如此設計出來的指令時,會先例行式的執行由Word|number1形成的全部指令,接著開始使用CREATE,為被造指令創造一個新的資料結構,
而且將同時執行一段,自CREATE起,至DOES>為止,其間所設計的全部指令Word|number2,完成按照新格式創造新指令的工作。 根據這種造指令的指令創造出來的新指令被執行時,系統首先會獲得新造指令資料空間的起始位址addr,接著,
通常再根據自DOES>至;之間所設計的全部指令程式Word|number3,處理以addr為依據的資料內容。 EVALUATE ( … strAddr u -- … ) 將以strAddr u所形成的一連串字串內容,視同為現行輸入字串流而執譯之。 EXECUTE ( … xtoken -- ) 執行以代碼xtoken代表起始執行位址的指令。 HERE ( -- addr ) 將資料空間內下一個可以自由使用的記憶體位址推入堆疊。 IMMEDIATE ( -- ) 設定新近定義的指令,令其一旦再被使用於其他指令的定義中時,會被立刻執行。 >IN ( -- -addr ) 將儲存文字輸入緩衝區游動指標內容的記憶體單元位址推入堆疊。 [‘] Word ( -- ) ( -- xtoken(Runtime) ) 編譯後續Word指令的起始執行位址進入系統。 以其設計而成的指令,執行至此指令時,被編入系統的起始執行位址會被推放於堆疊上。 LITERAL ( x -- ) ( -- x(Runtime) ) 編譯數字x進入現正定義中指令。 以其設計而成的指令,執行至此指令時,則被編入系統的數字會被推放於堆疊上。 PAD ( -- strAddr ) 將通常被當作字串處理空間區起始位址的strAddr放置在堆疊上。 PARSE abc…(delimiter) ( char – strAddr u ) 以字元char為界限,剖析輸入字串abc…。 執行後得到此字串的起始位址strAddr及字串長度u。 POSTPONE Word ( -- ) 編譯後續指令Word成被編譯指令,如果Word為立即執行性指令,則執行與[COMPILE]相同的工作。 QUERY ( -- ) 設定硬體終端機為系統現行輸入的裝置,將一列文字讀進記憶體的終端輸入緩衝區(TIB),並將系統在此區域所使用的游動指標>IN的內容歸原成零。 REFILL ( -- flag ) ( -- 0(failure) ) 若有必要,則由終端機讀入訊息(參考QUERY)後填入輸入緩衝區,並留下表真之旗號。
若現行輸入為形同待EVALUATE指令執行的一連串字串,則在堆疊上留下表假之旗號,且不做任何事情。 RESTORE-INPUT ( selx… n – flag ) 恢復代表第n個深度之參數slex作為輸入來源識別碼,並留下為真之旗號表執行成功。 SAVE-INPUT ( -- selx… n ) 推放所有代表現行輸入來源識別碼的參數slex…及其個數n到堆疊上,以便稍後供RESTORE-INPUT指令使用。 SOURCE ( -- strAddr u ) 推放輸入來源緩衝區的位址strAddr及其內之字元個數u於堆疊上。 SOURCE-ID ( -- 0 | -1 | x ) 推放代表輸入來源的識別碼於堆疊上,0表終端機,-1表待EVALUATE執行的字串,x表輸入檔案識別碼。 SPAN ( -- -addr ) 將儲存與EXPECT相關之字元計量數的單元位址addr推放於堆疊上。 STATE ( -- -addr ) 將儲存系統狀態值的記憶體單元位址推放於堆疊上。(狀態值係用來表示系統處於編譯狀態或執譯狀態) TIB ( -- strAddr ) 將文字輸入緩衝區(Text Input Buffer)的起始位址推放於堆疊上。 #TIB ( -- -addr ) 將儲存與文字輸入緩衝區相關之字元計量數的單元位址addr推放於堆疊上。 ‘ defName ( -- xtoken ) 搜尋名稱為defName指令在系統中的起始執行位址xtoken,將其推放於堆疊上。 WORD abc…(delimiter) ( char – strAddr ) 複製所有以char為終止字元標界的輸入字串流,到以strAddr作為起始位址的記憶體區域去。 FIND ( strAddr – xtoken 1(immed) ) ( strAddr – xtoken -1 ) ( strAddr – strAddr 0(failure) ) 根據現行字彙搜尋順序,以strAddr所代表包封字串之指令名稱為基準,找出系統中第一個與之匹配的指令。找到了,就留下這個指令的起始執行位址xtoken,
若為立即執行性指令,再留下旗號1,若為非立即執行性指令,則留下旗號-1。找不到,則仍然留下原strAddr,並留下旗號0於堆疊上。 SEARCH-WORDLIST ( strAddr u selx – xtoken 1 ) ( strAddr u selx – xtoken -1 ) ( strAddr u selx – 0(unmatched) ) 這個指令的功能與上述FIND類似,但使用額外的參數,其中,u是待搜尋字串的字長,selx表專門被指定來搜尋之字彙的識別碼。 SLITERAL ( strAddr u -- ) (Runtime) ( -- strAddr u ) 依據相關於字串之兩個參數:起始位址strAddr及字長u,將字串編納入現正定義中的指令。
以其設計而成的指令,執行到此指令時,則被編入字串的相關參數會被推放於堆疊上。 2LITERAL ( x x’ -- ) (Runtime) ( -- x x’ ) 將堆疊上的兩個數字x及x’編納入現正定義中的指令。以其設計而成之指令,執行到此指令時,則被編入的兩個數字會被推放於堆疊上。 FLITERAL ( float -- ) (Runtime) ( -- float ) 將堆疊上的浮點數float編納入現正定義中的指令。以其設計而成之指令,執行到此指令時,則被編入的浮點數會被推放於堆疊上。 BLK ( -- addr ) 將儲存現正使用中之磁碟區塊數的記憶體單元位址addr推放於堆疊上。 AHEAD ( -- orig) 推放主程式資料欄位址orig於流程控制堆疊上,供後續須要解決前向分支的指令使用。(系統可以設計成流程控制堆疊就使用一般堆疊) CS-PICK orig|dest… u ( -- orig|dest… dest ) 此指令用來操作流程控制堆疊,其功能與標準PICK指令類似。 CS-ROLL orig|dest… u ( -- orig|dest… dest ) 此指令用來操作流程控制堆疊,其功能與標準ROLL指令類似。

2025年1月1日 星期三

天下為公

天下為公


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
2 January 2025


大道之行也,天下為公。

偉大美好的生活方式在全世界運行時,天下是屬於大家的。這句話出自於孔子口述的言論。

孔子述而不作,他只教書論述言行,不寫書創作文章。學生記下之孔子的論述,不僅只被收集在『論語』一書之中。孔子之後大約三百年,西漢時期的戴德、戴聖叔侄兩人選編孔、孟、儒家思想的論述,編成『禮記』一書時,也記錄了孔子的言行教育。禮記中的一篇『禮運大同篇』就記載了孔子論述世界大同時的輪廓。這一篇文章的首段,後人視之為禮運大同篇的最精華所在,實際上文章還有後段,記錄了孔子對當時混亂局勢的批評。首段是我們高中國文課本中的一課,想深入了解全文的學生,可以根據每課課文後面『國學常識』欄目中的解說,自行研讀『禮記』一書中的全文。

想當年,高中生都得背誦這篇課文,達到此一要求倒也不難,因為我們學生時期的音樂課中就全面教唱禮運大同篇的歌曲,在人人會唱的情況下,考試若考這一課課文的默寫,那是難不倒學生的。不出聲,心中默唱、默寫,誰都能完整的答好這樣的考題。

天下為公、世界大同,在中國文化基礎教育中被列為教育宗旨,我們將其延伸到各個領域時,又何嘗不希望如此?做人處事該如此,我們走進全世界電腦軟體的發展環境時,也希望天下為公、世界大同。 AI 正面的發展,若沒有世界大同的觀念,就不可能實現自由取得天下為公的數據。

我們的    國父孫中山先生之禮運大同篇的墨寶,曾經在學校環境中處處可見。相關的歌曲,學生都會唱,可謂都無版權問題。向全世界廣傳這些有意義的東西,不失為身體力行正面教材的作法。我不擬用語體文解釋禮運大同篇,因為內容平易近人,墨寶字體清晰,網上容易取得。 這一份從 Youtube 下載的歌曲影片,畫面不加油添醋,只配樂,同步播出全文,最適合轉載。我不忘說明出處,感謝提供,影片下載的網址:
https://www.youtube.com/watch?v=dSssNn-bTL4


Forth 結合外部軟體功能的方法不是一成不變的,隨時代而變、隨使用環境而變、隨作業系統而變、隨外部軟體的執行方法而變、隨 Forth 系統設計者的規劃方式而變 ..... 等等等。所以,討論這個主題時很難以偏概全解釋得清楚。不過,就我個人的使用經驗而言,不管怎麼變,都有辦法可以實現從 Forth 系統的內部執行出外部軟體的功能。這就是一種天下為公的軟體使用概念, Forth 向來都不孤獨,能夠與所有的軟體功能共榮的精神,自古以來都有。我們發展 Forth 的同時,也希望 Forth 有世界大同的環境。網上的資源已成現行軟體發展時必須操作的對象,傳統的 Forth 系統不包括這種指令的設計,解決這項問題的方法,就是 Forth 系統必須抱持天下為公的觀念,結合外部能夠自動存取網上資料之軟體的功能,來實現理想。

為了避免天馬行空式的亂談一通,我就務實的只談現行 Linux 公益作業系統中所建之 Forth 系統的結合方法。我選定的作業系統是 Ubuntu 20.04,取其中文顯示功能比較完善的緣故。選定的 Forth 系統則為全以組合語言形式建成的 Lina64 ciForth 系統。就這麼單純的一個 Forth 系統,與外部軟體結合的方式,就可以分成好幾種了。例如:系統建立時,免不了要叫用作業系統的現成功能來實現文、數字的輸出與輸入,還有其他非常基礎的軟體叫用指令,但這都不是本文想要強調的重點。作業系統內的軟體,本就是屬於天下為公的資源,否則所有其他發展出來的軟體就無法在這個作業系統中運行。系統這樣的結合方式,可以視同為一種靜態連結的結合方式,亦即被 Forth 系統叫用與否,作業系統的基本功能軟體都在電腦開機之後立即被安裝在記憶體內,每一個後續被執行的軟體,都可以叫用作業系統中固有的功能。

另外有一種相對於靜態連結而被稱為可動態連結的軟體, 在 Forth 系統想叫用它時,必須讓作業系統先行載入已由可產生動態連結軟體之工具所產生的程式,然後才能以與叫用靜態連結程式同樣的方式來叫用其中的功能。這一部份,我已在一百個例題中的第 80 個範例中展示過執行出全套功能的方法。能令系統發出聲音的可動態連結功能程式叫作 kernel32.dll ,它是一種動態連結性質的程式。能被叫用來發聲的指令名稱叫作 Beep (大小寫嚴格劃分)。這種結合方式也不是本文想介紹的部份, .dll 的程式可以是天下為公的軟體,也可以不是,取決於 .dll 程式的設計者的意願,若作者願意公開,則該 .dll 的程式必須存在於作業系統中。

本文要介紹透過作業系統先行安裝出來的任何軟體,如何由 Forth 系統透過作業系統之外殼(shell)語言 BASH 來叫用的方法。關於 BASH 程式語言的執行原理,我在 20240710 貼出之 『設計浮點系統』 一文中,展示過它的執行迴路。簡而言之,跟 Forth 系統的執行原理相似,只是 BASH 程式語言不似 Forth 系統那樣能夠接受輸入數字。作業系統之所以能夠執行天下為公之軟體,實拜 BASH 執行迴路之賜。你在命令視窗(command console)內輸入任何指令時,就是 BASH 在處裡您的輸入。因此, Forth 想要結合外部軟體,執行出一些天下為公之軟體的功能來時,在 Forth 系統內叫用 BASH 就能實現。他的叫用指令也只有一個,ciForth 中叫做 system 。

長篇大論的講道理,不適合當今資訊媒體的傳輸教學,現行比較好的表達方式,就是給大家一個簡單的範例程式,讓有興趣的人能夠透過範例來實踐出自己的打算。

本網頁於 20190216 發佈的文章『測試函數』,文內直接刊載了各種函數的測試圖,並探討從測試過程中找出設計問題的技術,直到不再發現設計瑕疵後才貼出成果。這一套測試方法,就是典型的天下為公軟體的使用方法。現行軟體的熱門話題偏重於與影像處裡以及獲取網路資訊有關的技術,傳統的 Forth 系統並沒有這方面的基本指令可用,但透過世界大同、天下為公的軟體使用觀念,好的系統設計者精研過如何驅動外部軟體程式之技術,便發展出了現成指令。誰想抓取網上資料來用?誰想把相片背景直接清除? ..... 等等問題,現行 Forth 系統都有能力解決。下列介紹一個很精簡的繪圖成果,能令大家舉一反三的實現任何這方面的構想,請記住 : 世界大同、天下為公永遠是我們追求的理想。

我所借用的軟體是當今流行的 python 程式語言,借用的方法純靠執行 Lina64 Forth 系統中的 system 指令完成。
相關程式逐個列示如下,但執行過程中產生的一千筆數據組之 data.txt 檔案內容不列示於此處。
被請用的 oneset.py 繪圖程式,係網上下載之 python 公益範例程式,使用時,只略改抬頭文字顯示的內容成為:
ax1.set_title("f(x)=atan(x), -9.99<x<9.99")
便可。
在下列範例中,被計算的函數是 atan 函數,計算的範圍是從 -9.99E0 到 9.99E0 。
圖形顯示的成果,係以滑鼠操作,將視窗存成檔案而得。

\\\\\\\\\\\\\\\\\\\\\\\\

\ datagen.f (data generator)

' AllE is DotE

integer i
5 reals  a b d x y 

: datagen ( -- )
  basic
10 let { a =   -9.99 e 0 }              \ 定義域下限
20 let { b =    9.99 e 0 }              \ 定義域上限
30 let { d = ( b - a ) / 1000. e 0 }    
40 let { x = a }
50 for i = 0 to 999
60 let { y = atan ( x ) }               \ 指定函數
70 print { x , y }
80 let { x = x + d }
90 next i 
100 end 
;

datagen
bye

\\\\\\\\\\\\\\\\\\\\\\\\\

#!/usr/bin/python

import numpy as np
import matplotlib.pyplot as plt
from numpy import *

DataIn = loadtxt('data.txt')

x, y = loadtxt('data.txt', unpack=True)

fig = plt.figure()

ax1 = fig.add_subplot(111)

plt.grid()
plt.axhline(y=0,color="green",linewidth=2)
plt.axvline(x=0,color="green",linewidth=2)

ax1.set_title("f(x)=atan(x), -9.99<x<9.99")    
ax1.set_xlabel('x')
ax1.set_ylabel('f(x)')

ax1.plot(x,y, c='r', label='f(x)')

leg = ax1.legend()

plt.show()

\\\\\\\\\\\\\\\\\\\\\\\\\\

\ main

: gendata ( -- )
  s" ./f -c datagen.f > data.txt" system ;

: plotfig ( -- )
  s" python3 oneset.py" system ;	\ for Ubuntu 20.04

: main ( -- )
  gendata
  plotfig
;

main

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\