2025年6月15日 星期日

文化傳承

文化傳承


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


Forth 是一種程式語言,比較好且適當的中文名稱是『符式』。簡明的解釋,就是其標準指令中使用了許多符號作為指令名稱。這樣用,有其原因,因為它是即時性控制語言,標準鍵盤上的符號用來當指令,更可以快速的執行出結果。

語言都會形成文化,Forth 亦然,它有它的文化。中文的 Forth 也有中文方面的文化,我用了它 40 幾年,比較熟悉它所形成的中文文化背景。用到後來,我發展出了數學計算系統,年紀大了還在用,於是產生了想要傳承它的使命感。跟中國文化傳承者的情懷一樣,傳承 Forth 中文方面的文化,是老來還能用它設計系統者應有的使命。

40 幾年來已有不少跟 Forth 有關的中文書籍或翻譯書本出版,台灣、中國大陸都有,但內容都偏重於最基礎的 Forth 使用教學。英文版本的書籍,有一些內容會涉及系統設計,但數量很少。用 Forth 設計出系統的公司,為了營利賺錢,不可能完全公開設計內容,網上能獲得源碼的檔案,都只有基礎之裸系統。因此,傳承屬於與 Forth 有關之系統性質的文化,是很可貴的工作。尤其是這種文化必須經過實際上使用過後的嚴格考驗,實做成功後,才能講得出它們的道理。可是,這種文化不會在基礎教學的書籍中出現,因為層次比較高,不容易介紹。

這一篇文章舉一個很簡單的例子說明:

應用 Forth 系統,不能自外於整個世界,也不能自外於另一個 Forth 系統。本網頁中刊出的題材,最可貴的地方,就是已經刊出過許多 Forth 與外界溝通的範例程式,讀者在別的場合很難見到,所以可貴。Forth 系統與外界溝通的方法都有侷限性,溝通方法會隨時間與背景環境的不同而不同,這種性質令系統設計者只能隨時間與環境而跟著調變,沒有規則可循。但是一種透過公稱方式的溝通慣例可以討論,那就是透過生成或接受『檔案』(file) 的方式來溝通。設計這種溝通技術,仍需要不少有關檔案格式的基本觀念,它超出了 Forth 的討論範疇,短文也不可能說得清楚。用法則需要先行建立起固定的使用協定(protocol)才能設計,也就是說,使用步驟絕非一步到位,有固定死的步驟才能完成設計。我再簡化舉例,只討論 Forth 系統輸出資料時需要進行什麼設計?

從檔案輸入資料進 Forth 系統的觀念比較直接,只要知道開啟了的檔案之起始位址,剩下的事情就是只須根據這個位址來進行資料處裡的程式設計。

從系統輸出資料到檔案,根據我多年的使用經驗,比較好的方法是必須找出所有 Forth 執行輸出時相關的最根本指令,然後令其執行內容改為被轉向到輸出至另外的緩衝區,待資料輸出工作結束後,才一次性的將緩衝區內的資料轉存進指定檔案。我歸納出最根本的通用指令,顯性的有三個 : TYPE, EMIT, CR 。 隱性的另有兩個 : LF, EOF。隱性的,只須適時適地的安放其碼就能完成工作。顯性的三個指令則需採用高階的明碼設計,內容並不複雜,只須記得 TYPE 是印出一大串字串,後二者則只印出單個字元,如此便可。而設計的關鍵,則在必須將這三個顯性指令規劃成為可改變執行內容的向量式(vectorized)指令,在資料被存進檔案後,系統才能據此而恢復成原來的標準輸出狀態。一套典型的相關設計如下:


 
0 value OriginalTYPE
0 value OriginalEMIT
0 value OriginalCR

: SaveTypeEmitCr
  ['] TYPE >body @ to OriginalTYPE
  ['] EMIT >body @ to OriginalEMIT
  ['] CR   >body @ to OriginalCR
;

: RecoverTypeEmitCr 
  OriginalTYPE is TYPE
  OriginalEMIT is EMIT
  OriginalCR   is CR
;

: TYPE>PAD ( addr len -- )
  PAD +PLACE ;
: EMIT>PAD ( c -- )
  PAD c+PLACE ;

: PAD>FILE ( -- )
  PAD COUNT Fadr Flen + SWAP DUP +TO Flen MOVE ;



每次傳承一個文化主題時,內容不需要多,要點是要有重點。

我也注重中國文化的傳承,每次提及中國文化時,只須提出一個簡單的要素,能將罕有的要素傳揚到全世界才是重點。

最近,我在環球時報新聞網頁看到這樣的照片,覺得珍貴,刻意將其轉貼此處,謝謝該新聞媒體的報導。

這是一幀取材自中國河南省鞏義市永熙陵旁之麥田裡的照片,永熙陵是北宋第二個皇帝宋太宗(西元976年~997年)死後葬身之地,今人已開墾成麥田,但保留了千年石雕之地上物, 照片顯示的場景頗為撼動人心。

我刻意介紹的這份中國文物,不是宏偉壯觀的建築,也不是華麗驚人的遺物,只是中國北方麥田裡的千年石雕,看到後卻令人發思古之幽情。我轉貼來跟全世界分享,也與大家分享我設計系統、解決問題時的一份小程式及其重要觀念。


2025年6月1日 星期日

ARM 使用環境之經驗記錄

ARM 使用環境之經驗記錄


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

通常,我在試用 Forth 系統之發展末期,大約都是以能夠發展出數學函數的繪圖功能才告一段落。這次改到 ARM 使用環境之發展亦然,在我實現舊有的題材產生出同樣結果後,發展可以告一段落了。

前文曾經強調過,我的 ARM 發展環境就是 Raspberry pi3 與 pi4 ,以下就簡稱 pi3, pi4 。發展的過程中有許多經驗應該記錄下來,以防日後再使用它們又出問題時會是重蹈覆徹。

這種單板電腦套件(kit)沒有裝設電源開關,由於發展過程中時常要面對當機問題,我不得不為使用裝置添加開關。紐西蘭的壁上插座都附有開關,要讓電腦能夠跑得起來時,需要至少兩個電源插孔,為此,我刻意另外購用每個插孔均帶有個別開關的延長線插座,兩套單板電腦就買了兩條。使用的螢幕必須具有 HDMI 接頭,我有兩個這種螢幕,都是廉價回收品。一個是單純的電腦專用螢幕,另一個是帶有 HDMI 接頭的小電視。長期使用後發現,不宜將兩條電源線只透過單一個開關來控制供電,因為我發現僅由單一個開關送電時,開關會跳出火花,這就表示開關的接點在每次送電時,表面都被電擊燒過一次,開關遲早都會被燒毀,不應該這樣使用電器。螢幕與單板電腦各自使用一個電源開關時,開關就不跳火花了,以後注意,必須這樣使用。

這兩套電腦都不是很正規的日用電腦, pi3 甚至於不太適合安裝視窗作業系統,滑鼠常常跟不上響應是其最大的問題。早在 2012 年就已存在的 pi3 ,已經十幾年了,性能雖不頂好,還是能做一些事情的。東西在我這裡放了許多年,現在跑起來也沒有什麼問題。隨著時日的演進,它所需要的靜態硬碟(SD),以性價比考量,目前採用 64GB 的 MicroSD 最為恰當。舊的 32GB 之 MicroSD 不用淘汰,因為這套系統要與外界進行資料傳送時,還沒有很適合的網上軟體可供採用,只宜使用 USB 來傳送,所以舊東西就還有用處,而且比較好用,因為容量不大,資料不多時,視窗作業系統連通 USB 就比較不耗時間,也才能夠及時反應出可存取的狀態。一個裝滿資料的外加硬碟,裡面可能有幾十萬甚至於幾百萬個檔案,pi3 為了掃描整個硬碟的資料夾,所耗時間將形同已經當機。這種現象可以從 pi3 後面的兩個指示燈看得出來,紅燈亮時僅表示已經供電,綠燈亮時表示系統正在執行程式。

長久以來,我在三套作業系統 Windows', MacOS, Linux 之間傳遞資料時,都已改採透過網路直接傳送與接收的方式進行,所用軟體叫做 croc ,它用起來很方便,無論多少 個 GB 的影音圖字碼都能傳送,基本作法就是透過 GitHub 做中間緩衝環境,只要你不在乎所傳資料會被盜取,那就儘管使用 croc 來傳檔案資料,USB 就都免用了。使用 croc 的細節不是我想討論的題材,怎麼安裝?怎麼用?用時有何細節必須注意?我就不在此處討論。但是,我已試過了, pi3 沒得安裝, pi4 可以安裝,使用時卻以安全問題為由而不能用,我暫時尚未試出可用結果。所以,在 ARM 使用環境內,我仍使用硬體 USB 傳送資料。

為電腦裝上作業系統,都已採用將影像檔(.img)直接展開至 MicroSD 的方式進行,在什麼網頁可以取得那一種作業系統?不是我該在這裡介紹的題材,我只能強調我為 pi3 裝的是 Ubuntu mate 20.04,而為 pi4 裝的是 Debian 。安裝過程很簡單、也很快。只有兩件事情需要對答回應,一是時區的選用,二是設定 WiFi 的網通密碼。中文輸入法是否加裝?在安裝系統初期可以不用考慮。而且,我在安裝 pi3 時發現,加裝中文輸入界面後,滑鼠又變得慢到不可理喻的程度了,而加裝這方面的軟體,裝後不能刪除,牽涉太廣,出問題時,我改採重裝系統的方式解決問題,耗時最少。

裝好了基礎作業系統,才能發展軟體,我花了不少時間為 ciForth 延伸出 abcForth 的功能,這樣我便能大肆測試整個系統的性能。本網頁在 2 January 2025 貼出之『天下為公』一文,附貼了一份很好的多重語言互助互榮,完成數學函數繪圖的成果。這裡再次借用這份程式,同時展示在三套不同之 Linux 環境完成同一工作的成果。在桌機上的 Ubuntu 20.04 是直接完成所有工作的。在 pi3 上基於 ARM 設計的 ciForth,不能再以一次操作完成所有工作,問題出在下列這一列指令中的 -c 之工作內容會出現例外訊息

$ ./l -c datagen.f > data.txt

ciForth 在將數據寫入 data.txt 資料檔案時,出現的多餘訊息全進入了檔案頂部,我暫時也不熟悉如何修正這個問題,只好改採以人工操作滑鼠的方式刪除訊息,剩下的程式就都能執行了。三個系統的成果圖與文字操作畫面的記錄同時展示於此,供作比對之用。程式內容請回頭看『天下為公』一文,展示的效果表示,我為 ARM 環境發展的 abcForth 已經完成。

我在 pi4 的 Debian 作業環境中想要對畫面進行硬拷貝擷圖時出現困難,因為系統不提供像 Ubuntu 那樣,可以直接壓下 print screen 鍵就獲得整個螢幕畫面的能力,於是加裝了 Kazam 軟體,透過它來實現要求。
想要系統能做許多事情,加裝軟體是免不了的要求,這方面需求,因人而異,但不是我該談的主題,要談,題目也很大。
要區別那一張圖代表是在那一個作業系統內完成的?請看螢幕桌面的背景畫面就能區別。




20250519 的小作文

研發電腦技術的精神總是出現在半夜兩三點

這一段時日,我一直在設法適應 ARM CPU 的發展環境,生活有點像回到五十年前剛接觸個人電腦的日子那樣,半夜三更老是還在搞電腦。

年輕時是怕家人打擾,研究無法持續,所以選在夜晚磨練技術。但那時年輕力壯,身體很好,少睡幾小時還受得了,可以造就出後來的技能。

現在不能再這樣糟蹋身體,也沒有必要,可以就在白天自由自在的搞電腦。想半夜搞電腦,只宜顛倒作業,改靠調整睡覺時間來面對。

昨晚,我改在半夜兩點起床,搞 Raspberry pi3 名片型單板電腦的軟體加裝。它是個麻雀雖小五臟俱全的小東西,前曾被英國政府採用為兒童電腦教育專用裝備,號稱生產價格只需五美金,市售價低於 35 元。目前的版本已經進展到 pi5 ,速度與容量已與慣用電腦相當,已成歐盟國家用來抗拒美國產品的利器。

因為白天家人要用網路,這種還靠 WiFi 傳輸訊息的弱小裝備,競爭不過普通電腦訊號比較強大的裝備。因此,我長期試用過後,發現它很容易當機,原因就是處理無線電信號的能力不如別人。昨晚我八點就早睡了,刻意在半夜兩點起床,開啟 pi3 ,獨佔 WiFi,進行加裝應用軟體的操作,一試之下,果真見到了效果。凡是容量上百個 MB 的應用軟體,都能加裝成功,顯示了我的判斷沒有錯。這就是搞電腦搞到半夜三更的效果,理由仍是避免被外部打擾,靜下心來耐心發展,就能成功。

這種使用 ARM cpu 設計出來的產品,都只使用免費的 Linux 公益作業系統運作,它的使用特色就是輕鬆裝好全裸系統,然後只靠每次只打一列字的方式加裝應用軟體。如下列:

$ sudo apt install [app name]

多年來的使用經驗告訴我,大約需要加裝 30 幾套應用軟體後,才能擁有一個令我滿意的使用環境。有幾個比較大型的應用軟體都需要佔用幾百個 MB 。白天裝過幾次沒有成功之後,我痛下決心,重溫年少時的心情,再度發揮夜研精神,一下功夫,就立刻得到了結果。

今晨裝好的軟體是像小畫家那樣的工具軟體 Kolourpaint ,佔用一百多個 MB ,還有好幾個都是,半夜裝成後,我才睡回鍋覺。

今晨因此而培育出、獲得了這種電腦之新的使用經驗。東西的性能不是不好,只是運轉能力較弱,耐心應對,都能發揮效果。

2025年5月16日 星期五

實踐

實踐


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


這一陣子,我在適應 ARM 系統的使用環境,經常會碰到一些問題,使用環境之適應性的建立,全憑身體力行的實踐,別無它法。這種事情已經不是時下青年喜歡做的事情,因為耗時又麻煩,要細讀許多資料,要實作許多設計,逐個度過難關後,才能有點成就。

以前,在發展系統時,沒有注意到摻雜了特例的用法,會給後來改到新環境繼續延用時產生麻煩。因此,重新在新環境發展系統時,我注意到重建的系統應該要注意永續可用的問題,最好不要強加個人偏好性的設計,以免自找麻煩。趁著這次再在 ARM 環境建立 abc Forth 運算系統的機會,把能夠永續使用的設計整理妥善,簡化發展程序,刪除所有個人偏好,不再添加於未來應用。刪除的部份可以另行建檔,需要時才載入系統。

有許多經驗是實踐後才知道的。今天,我在 Raspberry pi4 上完成了一套發展,改到 pi3 去試用,就出現了記憶體分段選址有問題之訊息,系統不能執行。但憑經驗,一看就知道,是因為我將系統容量宣告得太大了的關係,將設定改小就能解決問題。在 pi4 上有 4GB 的 RAM 可用,而 pi3 只有 1GB 的 RAM ,我在 pi4 上發展時,將系統宣告得很大,超出了 1GB 的範圍,就出問題了。由於我設計的 abc Forth 系統需要執行大數計算,它比較耗用記憶體,雖然長成的系統仍然是只有幾百 KB ,運算大數時,可能要耗用 Forth 系統從 HERE 開始算起以上的記憶體。因此,只宣告不到 1MB 的使用量絕對不夠。經過這次產生問題後的經驗告訴我, pi3 上只能用 1GB ,也就是 1000MB ,我若將 Forth 系統的記憶體使用量宣告成 1GB 的十分之一,也就是 100MB ,算是比較妥當。如此一來,我就能在操作上比較方便的 pi4 系統上發展出轉置到 pi3 也能執行的 abc Forth 系統。這件事已經測試完畢。

在家裡獨自發展系統,不易察覺這種單板電腦有什麼使用上的問題,家中若有人同時上網,就能體會出問題。昨天,我小女兒在我這裡使用網路開會,我才發現 pi4 啟動後連編寫程式都有困難,反應慢到無法接受的程度,只能關機停用,待我能夠獨享網路時再搞程式設計。單板電腦採用 WiFi 上網,響應速度當然沒有一般手提電腦的速度快,能分享的無線訊息傳遞必定大受影響。pi3 的運行速度為 1.2GHz ,pi4 為 1.8GHZ ,比起一般電腦為 2.5GHz 的速度,確實差了許多,用時不可不知。

ARM 是 Linux 的作業環境,我在產出基本系統後,必須取用原在 Window's 系統上發展出來的應用程式。程式中有些說明,包括中文,因係 big-5 的編碼,轉到 Linux 採用 utf-8 編碼時無法閱讀,要操作轉碼程式來更換編碼,才能看到中文的內容。每次打字總覺得麻煩,於是自己設計了一個小程式,想將檔案內容的碼轉換時,只須輸入檔案名稱就夠了,會用電腦的人,應該盡量自己設計程式簡化這種問題。這個程式可以在 Linux 作業系統中之 ciForth 系統使用,我用的是 Lina64 Forth。設計要點只是字串操作,然後設法運用以 Forth 轉交 BASH 執行出轉檔程式的技巧,就能完成工作。轉檔程式的名稱叫做 iconv ,它的詳細使用說明,不在此討論,這種 Forth 的使用技巧,則是當下程式發展者應該建立的運用觀念。程式能完成 big-5 與 utf-8 碼間的互換,只須輸入欲轉碼之檔案名稱便可。


 
\ utf8.f

create InputFile 32 cells allot
create OutputFile 32 cells allot

: InputSetup 
  InputFile 32 cells erase
  OutputFile 32 cells erase
  cr ." Input file name : " cr
  input$ InputFile $! 
;

: Utf8Setup
  S" iconv -f big-5 -t utf8 -o utf8-" OutputFile $!
  InputFile $@ OutputFile $+!
  BL OutputFile $c+
  InputFile $@ OutputFile $+!
;

: Big5Setup
  S" iconv -f utf8 -t big5 -o big5-" OutputFile $!
  InputFile $@ OutputFile $+!
  BL OutputFile $c+
  InputFile $@ OutputFile $+!
;
  
: utf8
  InputSetup 
  Utf8Setup
  OutputFile $@ system
;

: big5
  InputSetup 
  Big5Setup
  OutputFile $@ system
;


至聖先師


2025年5月2日 星期五

時基

時基


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


取得時基(time base)訊息是一個很古老的電腦技術,卻是一個恆有需要的功能。

使用電腦實測事件發生的時距,在許多場合都有實用價值。它的硬體根據,就是在 CPU 旁邊必須具有能自動運行的系統時脈計數器(system timer)。

我在 40 年前接觸單板電腦時就開始用過這種技術,那時的電腦,沒有現成的時脈計數器,時脈訊號只供 CPU 直接運行使用,沒有計數器來留存時脈的數量。

在 6502 CPU 盛行的時代,我們採用專屬的 6522 晶片解決問題,其中就有一個 32 位元的計時器,如果嫌其留存的計數量不足,還能串接兩個 6522 晶片來使用。我曾用它來測量原子爐停爐棒的跌降時間,精確度可以達到微秒,實際需求只須百分之一秒。有一篇討論 Forth 指令精確執行時間的文章在 FORTH 期刊第 2 期中刊出,標題為: FORTH 程式執行速度之定量評鑑,也用這套技術。我還曾為一家地磅公司提供電腦內的自動時鐘,也用這套功能,那個年代的個人電腦不附帶這種功能,需要自己設計程式。

衛星定位系統能夠提供精確的定位、速度、時間等訊息,其工作原理也離不開這套技術,系統的時基越精確,訊息就跟著精確。

今日的電腦,內附現成的時鐘是基本配備,使用者已經不需要另外加裝硬體就能取得標準時基。我在使用 Lina64 Forth 時,為系統添加了一個低階設計的指令 getticks ,以備不時之需。
getticks 的整個添加內容,與一個簡單的 main 程式,及 main 被執行出來的視窗畫面,展示如下 :


#  **************
#  * GETTICKS   *  ( -- n )
#  **************
#
        .balign    8,0x00
N_CLOCKFET:
        .quad      8
        .ASCII      "GETTICKS"
        .balign    8,0x00
CLOCKFET:
        .quad    CLOCKFET+HEADSIZE
        .quad    CLOCKFET+HEADSIZE
        .quad    0x0
        .quad    INVERT
        .quad    N_CLOCKFET
        .quad    0
        .quad    0

	rdtsc
	shl	%rdx, 32
	or	%rdx, %rax
	push	%rdx

	LODSQ                  # NEXT
        JMP     QWORD PTR[%RAX]   

\ display Time-Stamp Counter

: main
  !xy
     begin
           0 40 gotoxy 
           getticks 
           0 20 D.R
     key?
     until
  @xy 
;




透過幾千年的時基,認識北京、周公與召公。

時基尺度以千年計量時,考古證據顯示,北京建城已有三千多年的歷史。當時老是被孔子尊稱的周公又是何等人物?他很能代表中國很古的名人。

引用的照片取材自中國新華社供圖,展示北京房山縣琉璃河遺址出土的五件青銅禮器 : 尊、卣(音永)、爵(音決)、觶(音至)、鼎。

五個禮器上都各刻有七處的四字銘文「太保墉燕」實證了北京已有三千多年的建城歷史。這是重要的事情,所以說了七遍!

「太保」是西周成王時期的召公 奭(音是),「燕」指琉璃河遺址所在的燕地,「墉」意為「築城」。
「太保墉燕」,意為「太保召公來到燕地建設都城」。

北京在民國時期被平民老百姓稱為北平,文人墨客寫作時喜稱燕京,解放後被稱為北京。

召公是太保,周公是太師。比照現在,太保召公,他相當於國防部長,另有太師周公,他相當於行政院長。這兩人是輔佐周朝各個帝王,直至西周成王的重臣。

孔子說周公制禮作樂,我們都能朗朗上口,那麼,召公就應該是克商(朝)平亂了。後來,周公管陝東,召公管陝西。

後人恭維文官周公時,似乎也不該忘了武官召公,要尊敬軍人才有國本。


時基另有妙用

我在設計亂數產生函數時,特別喜歡取用硬體計時器的內容來當作產生亂數的起始種子數字, abcLina64 Forth 系統就這樣使用時基。

三月份,我從台灣回到紐西蘭後,便打算在 ARM 系統上再度發展出 ciForth 的等效版本。我有 Raspberry Pi3 及 pi4 的單板電腦,試用一段時間後發現,在 pi3 上安裝 Ubuntu 20.04 mate OS ,用起來性能還算可以,只是 1.2 GHz 的速度太慢,滑鼠在視窗作業環境響應太慢容易當機。 pi4 則安裝了 Debian OS ,1.8 GHz 的運行速度,讓滑鼠的響應勉強跟得上需要,編寫程式時比較方便。我已在兩套單板上試跑過 ciForth ,只在文字視窗中運行,沒有問題。我的目標是設計出與 Lina64 性能相當的 abc Forth 數學計算系統。很不幸,在設計與 getticks 功能相同的指令時遭遇到困難。

ARM 系統發展與應用的生態環境尚不健全,出現問題時,網上能獲得的資源相當有限,現行軟、硬體資料能夠提供的說明比較含糊,發展起來確實有點吃力。甚至於在已公佈的ciForth 源程式中,也明白的提示了有那些指令還暫時不能正常使用。想讓系統發揮作用,還得努力。

相關資料顯示,ARM CPU 從系統計數器(system counter)抓取時基的指令是 MRS ,但文件也提示,這個指令在使用者模式時不能執行, 必須在特權模式狀態下才能執行。

關於這方面的問題,我暫時抱持著觀望的態度,靜待有比較好的範例可以採用時再行引用。

把亂數產生程式中的起始種子數亂化一下的方法很簡單,可以將 getticks 取得之值直接就存進種子變數。
也可以把 getticks 隨時取得的數字當種子數來亂化一次,再回存於種子數。這樣做,就能憑硬體計時器低效部份之內容高速變化的不確定性,產生另一組亂數群。
採用硬體來亂化種子數的效果是非凡的做法,尤其在 64 位元的時代,運用起來幾乎不可能被任何使用者猜得到第一個亂數函數產生的新亂數會是多少。
亂化的方法可以如下列這麼一個簡單的程式而已:

: RANDOMIZE ( -- )
GETTICKS GETTICKS * RANDOM ( n1 -- n2 ) SEED ! ;

時基的運用還有許多例子可舉。所以,無論是誰在建造新的 cpu 時,都應該考慮到,硬體計時器,是一種在系統應用方面常常需要的設置。

2025年4月16日 星期三

日記幾篇

日記幾篇


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


最近,我有家事要忙,定期貼出文章的工作受到影響,只能力求規律。

平時,為與幾位好友保持聯繫,交流技術,我花在 Line 社群媒體上活動的時間比較多,幾乎每天都在 Line 上貼出好幾篇有關科技的小作文。

這是一篇與 Forth 技術相關的貼文。貼出時只用 W10 OS 中的 Win32Forth 系統展示成果。此處再附加使用 Ubuntu 20.04 中的 ciForth 及 gForth 展示同一成果。

對於一個很好的問題,給出一個很好的測試方法,同時驗證自己設計的系統性能無誤,作為永久的記錄,貼文的目的在強調這種精神。

我看到此一貼文,立刻用我自己設計的系統檢測,驗證測試的程式與執行結果如下:


 

這是在 Win32Forth 系統中,以幾分鐘內的響應速度,直接執行出來的結果

: test basic 
10 print { 6.0e0 * fpi * fpi * fpi * fpi * fpi } 
20 end 
;  ok
test 
       1836.12   ok

真的沒錯, 6 * (pi)^5 = 1836.12, 這就是質子與電子質量的比值。

這是在 ciForth 中直接執行的結果

ching@ctt:~$ ./l

AMDX86 ciforth 5.3.0 
: test basic
10 print { 6.0 e 0 * f(pi) * f(pi) * f(pi) * f(pi) * f(pi) }
20 end 
;  OK
test

1.8361181087 E 3   OK

這是在 gForth 中直接執行的結果

ching@ctt:~$ ./g
Gforth 0.7.3, Copyright (C) 1995-2008 Free Software Foundation, Inc.
Gforth comes with ABSOLUTELY NO WARRANTY; for details type `license'
Type `bye' to exit
: test basic  compiled
10 print { 6.0e0 * pi * pi * pi * pi * pi }  compiled
20 end  compiled
;  ok
test 
     1.83611810871169E3   ok
     
系統不同,答案沒錯,只是數字輸出表達的方式不同,但都得正確。




清明掃墓有感

清明節到了,請把書讀通了、把道理搞懂了才去掃墓。

不久前,我剛去掃墓兩次,第二次是為了刻意跟兄弟姊妹同去而去的,就在台北市公墓,交通便利,公車 508 路能到山腳下。

公墓現場好幾年前就已經規定不准燒紙錢了,我也欣然接受。今年更甚,規定燒香也不准了,我們照樣接受。

上山時要走一段路,背著祭品有點累,以生鮮水果為主,聊表心意,這點累算什麼? 誰不知道祭過的水果還是自己吃? 有什麼好計較的?

我是反對燒香、燒紙錢的,理由很簡單,汙染、浪費又危險,人世間已經沒有那麼多資源可供這樣濫用。
堅持祭祀要燒東西的看法絕對沒有必要,人人都應該配合新時代的要求,必須守規矩。有誰不懂誠心、誠意才是掃墓的重點?

我這次回台,在街上仍然看到還有許多店家在燒紙錢,這種行為只會讓我感到痛心與噁心,尤其是在公車站牌旁邊的店家,難道不知道旅客必定受到汙染影響嗎? 無論是誰這樣燒紙錢,我都會在心裡咒罵 : 閻王不要放過他們的祖先,必須以其子孫正在汙染世間與浪費資源之罪讓這種祖先在陰間受罪。

我心目中的中國社會,要普設國營食堂

我曾經是職業軍人,1970 年起,在中正理工學院讀書四年,全在學校的餐廳吃三餐,學生副食費每個月 680 元。畢業後,在正規野戰部隊幹過三年,吃的都是連隊飲食,飲食再不好,吃起來也都是香噴噴的,國家補給的副食費是軍官每個月才 240 元。

1977 年起,在中山科學院幹了十二年,生活上也都以吃食堂的飯為主。工作區的院區餐廳食堂,就是用購置原子爐 20 億美金的經費設立的,因為食堂必須供應原子爐值班工作人員的三餐。這些技術人員搭值班車前往食堂,可以比院區員工提前半小時開飯。我記得那時一餐飯的價格大約 20 來元,每本飯票 200 元,大約可使用一星期。

中科院的宿舍區在石管局的石園區,那裏有另外一個全院員工可以使用的食堂,我在婚前,都在國營食堂解決日常飲食問題,生活過得健康、愉快,從不為吃飯問題煩惱。

1989 年,退伍之後,就只能吃自己了,但我養成了習慣,在外奔波工作,半夜兩點才回家也不願在外面吃晚飯,一定要回家吃,保證可以免得肝病,非常想念國營的食堂。

我常去大陸,在江西、上海、北京時最喜歡去吃公營食堂的飯,北京航空航天大學的第二研究生招待所的食堂、上海自己開創的新格公司食堂,都是我解決日常飲食的好地方。

2003 年我刻意去桃園虎頭山腳下退除役官兵輔導委員會開辦的職訓中心接受重型機械的維修訓練,想要培養出在紐西蘭自營農場時的基礎技能,又有機會再吃公營食堂的飯,每個月的伙食費才 2200 元台幣, 7 天 24 小時供應三餐,我吃得不亦樂乎。

公營食堂的飲食都很健康,固定的開飯時間當然確保了規律的飲食。看看前述的費用,就能知道不存在營利不實的問題。對我而言,食物都是自選付費的,公平、合理、方便、自由.....等等,具有許多好處。

我們老了,家裡每天都得為準備三餐付出不少時間,能吃食堂或能去食堂打飯來吃,已成空想。食堂能讓人專心工作、不必憂慮飲食問題、節省開銷、確保健康。

中國人的社會應該普設國營食堂,看看中國大陸,這張照片就顯現出了一個理想的社會。原文以英文向全世界報導。



AI 潛藏了三個問題 :

第一是搞 AI 耗能源,值不值得耗還很難說,光是目前的有限 AI 就已經是家家大公司都提高能耗到 150% 以上,後續會更糟。

第二是我個人網頁中強調過的訊息應該天下為公、世界大同。有美國在,這個理想就難以突破,東、西方只能自己搞自己的,AI 的能力就更加有限。

第三是版權問題,我的個人網頁有幾百個創作程式,就算貼出,版權仍是我的,誰都可以引用,但引用時要說明出處,尊重版權。
這一點,AI根本不做,所以歐盟禁止 AI 的公開營運,因為美國大公司的 AI 偷得的訊息,主要就是來自歐盟各國的科學訊息為多。

我不太需要 AI 。

我女婿的公司營運受到 AI 很大的衝擊,幫別人建網上資料庫的公司,現在都加重了負荷。要增設存取資料的安全設計,有些東西實在很難加建,他們就直接受害,他們所增加的設計負荷卻是在妨礙 AI 的能力。

誰是誰非?

AI 不會殺死創作,但會殺死平庸,主因就是 AI 不會創作,只會偷竊資料,實在沒有智慧。

現行 AI 是離網就沒有 AI 。

必也正名乎 ! AI 應該改稱為 "網I"(NI) 。

2025年4月1日 星期二

學以致用

學以致用


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


三月份,我回台灣一個月,再回紐西蘭至今,已經做完許多事情,生活趨於穩定,可以繼續發展各種技術。

我的一生忙於學習,也忙於學以致用,學習是因為生活上有需要,也是因為有興趣而不斷學習。作學問要力求致知,更貴在致用。

我家迎來第三個新生外孫小嬰兒已經七個月了,俗話說七坐八爬,九個月大就要學走路。我回紐西蘭的第一件家務事,就是動手改善嬰兒學步車,它已被兩個嬰兒使用過,性能一般般。以前,這個嬰兒學步車使用時,發現前輪輪軸只用木螺絲固定,會經常出現嚴重問題。每用一段時日,木螺絲就被轉輪帶動到脫落了,老是需要成人使用螺絲起子將其重新固定,設計的很不理想。

我花了一個上午的時間進行轉輪改裝工作。先從車庫內收藏的零件中找出一整盒 NTN 公司生產的微小滾柱軸承,外徑 8mm 、內徑 4mm 、 寬度 8mm ,每個輪子使用兩個軸承,可以改善嬰兒學步車的車輪轉動性能。我就憑藉著游標卡尺量度零件、鑽頭與加工後的成孔,讓它們的尺寸都能適當配合。原用木螺絲安裝轉輪,不合理,我改用鋼釘,軸承便能在鋼釘上自由轉動。

安裝前輪時,我知道該是學以致用的時候了。中國人自古以來就懂得安裝車輪的技巧,我也學過,轉軸會有三種偏裝角度上的要求,轉輪被驅動時,車體才能夠自動的迎合出前進的方向。一是前束(toe-in)、二是外翻(tilted out)、三是後傾(negative caster)。思考出這些術語的用意也很簡單,前束的要求能使車輪轉動後兩輪不會向外發散,車身才能保持前行。空車時兩輪外翻,車身載重後,外翻就能抵銷車軸受重受壓後的向下變形。後傾的目的,則是在推車前行時讓車身的力量帶動輪子,而不是任由前有動力的輪子來帶動車身。這三種術語的中文,均與英文用字並非直接匹配,原因就是中國人自古以來就懂得這些車用轉輪安裝技術的意義。

我學以致用的裝好了嬰兒學步車的前輪,只有前束條件能夠比較明顯的從照片中看得出來。因為前輪只有單一個固定點,就不用考慮後傾問題。成果有照片為證,效果是我施力推出車後,車能自由直行至少五公尺以上絕不跑偏。車載過大約十公斤的小外孫,已經在我家室內地毯上推行了半個月,沒有出過問題。

上述英文術語可在下列車輛學專著內找到:
James E. Duffy, Modern Automotive Mechanics, South Holland , Illinois The Goodheart-Willcox company, Inc.1990, chapter 70 Wheel Alignment,(p.927~ p.929).

隨文附貼照片兩張
圖一是我借用本地超市購物車拍攝的,可以明顯的顯示推車前輪後傾與後輪前傾的設計。
圖二是嬰兒學步車的上視圖,可以明顯的顯示前輪前束的安裝方式。

圖一

圖二

另外,我從台灣帶回三套被棄用之扶輪社捐贈了 2000 套用來長期監測地震自動傳送訊息的 Raspberry pi3 單板電腦。有空時,我就架設起電腦,跑我熟悉的 Forth 系統,此單板電腦有四枚 64 位元的 ARM CPU ,工作頻率只有 1.4GHz ,所以執行軟體的速度不夠快。我試過了,大約安裝 Ubuntu Mate 20.04 版的作業系統,效果還算差強人意。使用時,千萬不要為了貪圖新版而同意提昇作業系統的版本到 22.04 版,否則上網後顯示的速度會氣死人,慢到無法接受。

長期以來,我有種感覺,現行 CPU 的發展,到達 64 位元後,算是位元上限了,好像沒有必要再去搞出 128 位元的 CPU 。 換句話說,受完考驗的 64 位元 CPU 應該都是有用的東西。 Raspberry pi3 有個好處,就是整套單板在使用時不必另外加裝冷卻裝置,不必加裝散熱片,也不必使用冷卻風扇,我用手指直接接觸晶片表面,溫度不高。據說,只有它使用的 micro SD 記憶體在溫度比較高時會出問題,我生活在紐西蘭,常年溫度不高,所以沒遇到過這方面的問題。

我新購買了兩片 64GB 的 micro SD ,用來改裝原為 32GB 的純文字視窗 Debian 作業系統。有視窗可用時,不必非用純文字不可,有視窗的環境,編寫程式還是比較方便的。我也試過觀賞影片的性能,影、音輸出的效果都還不錯。檔案傳輸可透過 USB 或在家中使用 SSH 的軟體功能,在家裡幾台電腦之間利用網路互傳檔案。已有幾套 Forth 系統可以在此單板電腦上使用,發展環境算是優良。

現代電腦作業系統的安裝方法,採用取得系統之 .img 檔案後,直接在主機上將 .img 檔案展開至指定的外加碟片的方法。將此碟片插入插槽,就能直接啟動系統,進行設定。設定完畢,單板電腦就能自主運行了。安裝程序簡單、方便、迅速,但由於單板電腦上沒有電源開關,我特地採購了附加開關的電源插座,讓這樣的系統在開啟螢幕與單板電腦時方便一些。為了自己今後易於參考,我在此處留下不常用之系統安裝記錄,算是記錄了學以致用的事實,我在貼文前進行過下列完整的操作。

我取得 Ubuntu Mate 20.04 OS 之 .img 檔案的網址為:
https://releases.ubuntu-mate.org/20.04/arm64/
我挑選的 .img 檔案為:
ubuntu-mate-20.04.1-desktop-arm64+raspi.img.xz
1.1 GiB 2020-Oct-29 14:57

展開操作記要:
在將 micro SD 插入 USB 端口前,先用 df 指令確定當時系統的狀態。插入 micro SD 到 USB 後,再用 df 指令確定新插入的位置在 /dev/sdc2 。
確定這項訊息後,便可以開始進行將 .img 檔案展開於 micro SD 的對答式視窗操作。

點擊一下檔案 ubuntu-mate-20.04.1-desktop-arm64+raspi.img.xz 。
會出現我的本機為 480GB 硬碟的視窗訊息,其中有一空白欄,會問你想要將 .img 檔案展開前往的位置在那裡?
經由上列 df 的操作,確定新插入本機 USB 之 micro SD 所在位置為 /dev/sdc2 ,因此選擇此一將要展開 .img 前往的位置為 /dev/sdc2 。
隨後,畫面顯示受影響的裝置為 sdc2 後,點選 "還原" ,系統便會自動展開整個 .img 檔案於 micro SD 上,最後,耗用記憶體的量約為 6GB 。

此後,我會在 Raspberry pi3 單板電腦上發展一些軟體,也要花些時間來熟悉他的輸出/輸入性能,但不會用它來上網瀏覽訊息。
所有以展開 .img 檔案安裝系統的方式,都跟上述系統安裝的方法相同。

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 of 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

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