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

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