2018年12月21日 星期五

如何在Flash ROM上實現直譯語言4: MCU需求修改

原先的LbForth是在DOS-Box上執行,在RAM上執行且記憶體空間完全獨立。
有數個功能要修改:
1 . 記憶體從矩陣改成指標,這樣才能做MCU內的配置。包括生成字典的地方才可以控制。
2 . 記憶體空間管理,因為寫回的記憶體若是Flash區,要轉去Flash燒寫函式去執行。其他讀寫一樣。
3 . 屬性標記修改,因為NOR Flash清空時是0xFF,一般RAM清空是0,且Flash只能單向設定(1轉0),所以內容要反過來,判定程式也要反過來。

實作時前二項修改很容易。
再來是屬性,它有二個flag在用,各佔一個bit,剩下的bit則是數值,標示名字字串長度,用於尋找字。
一個flag是immediate,它是編譯模式下特權標記,標記設定後的字在編譯模式下是執行,而非媥譯,用來形成控制結構用的。
一個flag是hide,它是將此字標記為廢字,不執行也不會進查找。這個設計很奇怪的和Flash寫入程序很合。因為編譯時沒有做全文章分析,有可能會出現程式中有錯,但寫了一大堆的字進入Flash內,但它不完全,所以不可以執行及查找,故需要這個flag去標記。用在Flash上面可以先留下1(設定),在新字確定完成時再回來清除(設成0)。
又剛好STM32L系列及STM32F系列其Flash動作相反。故要修改成可以設定反相又可以運作的程式。

改完後放入MCU,不會動。因為Flash Erase是有規則的,可以Erase大塊,不能Erase單元,但Write可以寫入一個單元。變成又要加入Flash邊界檢知,在字典長到下一個sector時,發動Erase。
然後,還是不行。發現Flash不能只改一個bit,這個和個人認知有出入。測試一下動作,發現對Flash寫入單元可以做的只有二個動作: 寫入資料,寫入0(全清)。
這下頭大了,屬性欄位要改,若是要可以用,immediate及hide要分出來不能和字串長度放一起。
這下動到字典管理了。要符合,變成用資料的值來做為flag應用。變成Erase後的值是hide,若是沒改,新字就是廢字。若要是有用字就要寫入值(非清除),若是為immediate設定,則再加寫0(只能加寫成全清)。

改完後,還有一些Flash上小問題,再以程式解決。然後總算看到去執行字典生成的高階字編譯。又出問題了。
高階字的空值預設是0,但Nor-Flash不是,所以要去改高階指令。這下又要去理解Forth編譯的細節,硬著頭皮上了。
改完後,真的弄清楚了整個Forth語言系統。
總算在STM32F系列上執行了。不過事情沒有完,移去STM32L系列又掛掉。因為它的Flash除了是0寫成0xFF外,又不能亂寫有ECC在,二次寫入是不行的。看來只能改用在QSPI Flash上看可不可以。

如何在Flash ROM上實現直譯語言3: Forth系統分析

LBForth和一般Forth不同,因為它不用從CPU指令來建造,所以沒有基本指令再堆積成高階功能的問題。
因為由C建造,所以它只要專心應付語言實現問題。這使得系統簡單不少。

所以只要看幾個大的函式,弄清其功能,就可以知道如何動作。
但在解析前,有一個必須解決的問題,它的字典資料結構要先弄清楚。
因為這個是Forth運行最重要的結構。

字典的主要內容:
1.字名(函式名): 輸入的文字代表
2.內容(執行欄): op code或是指標,op code是基本執行功能。高階字以指標表代,遇到指標會跳去對應的記憶體內容再解析,直到執行op code。
3 .結構串接: 以link-list串起來
4. 屬性及標記,後面用到時再分析
實際排列:有固定長度的放在前面,所以link-list為第一個word,第二個是屬性,和名字共用32個byte(佔4個word),接下來是執行欄,長度不定。

字典在程式碼內是看不到的,因為它是在執行才生成,這個動作有利於Flash的寫入,也是可以利用未用的程式Flash的原因。個人覺得這個設計解決MCU上可以動態增加函式的方法。

再來是Forth的執行結構,它和CPU設計有相關,所以很像硬體的方式。
主要有二個堆疊,一個是資料堆,所以資料及參數全部放在此運算。
另外是一個返回堆,主要是呼叫返回堆疊及一些控制結構變數也放在內。
二個堆疊各有一個管理指標暫存器。
另外在執行上,有一個指令指標暫存器(IP)用來標記目前執行位址。

使用二個堆疊原因:
資料處理全部在資料堆疊上,故使用者要去做堆疊內容管理,這個在高階電腦語言是沒有的。因為此動作沒有執行效果,但可以限定RAM的使用只在資料堆疊上。這也是為何Forth使用RAM很少就可以運作。
MCU一向RAM就是不多,其他語言都有RAM需求問題,若是RAM大,MCU就不用寫得辛苦了。

實際上執行,有分成直譯模式及編譯模式。
平時在直譯模式下,輸入一個字串結束(空白字元出現),就去字典查找這個字,有找到,就依執行欄內容再進一步查找或是執行。
若是直譯失敗,再來是進行數字解析,因為這個字串有可能是數字字串,解析成功,就放入資料堆。
若再次直譯失敗,就沒法了,就執行錯誤處理(顯示輸入錯誤)。
有多字輸入下,會一個一個去執行,直到沒有新的輸入。
但有個直譯指令是不一樣的,它會進入編譯模式。
編譯模式下一樣有找字和轉換數字字串的動作,但再下去的動作不同,它不執行而是寫入Flash,生成新的字進入字典內。
在編譯模式下,一樣找字,找到字就將其執行欄位址寫入新字的內容,然後找下一個字。遇到數字時有點不一樣,會先寫入一個lit指令碼再寫入資料值。執行到lit指令碼會將下一欄的內容吐回資料堆疊。這樣就完成固定值的編譯。

了解Forth的運作後,才能做下一步的修改。

2018年12月13日 星期四

如何在Flash ROM上實現直譯語言2: 規劃及找材料

MCU上實現一個語言,在8位元時代就有,8051 Forth是我第一個看到的作品。
全部組合語言寫出來,以機器碼寫回。但有條件,RAM也是Code space,這個要在硬體上動手腳。
有興趣的人可以看一下CamelForth51,因為實用性不好,程式碼看了沒在用。

不過CamelForth的作者有另一個作品MSP430 Forth,它可以寫回Flash,無需佔用RAM做程式儲存。
可惜沒有用到大一點的MSP430,沒有機會用,也沒有機會看原始碼。但粗看和8051一樣用組合語言寫的。

二個Forth看完原始碼,皆打破我對Compile的觀念,原來字彙分析就可以集成語言。但它有一些基本模型在。
像是雙堆疊,有一個資料堆以及一個返迴堆。一般語言像C,只有一個堆疊,資料及返迴一起堆進Stack。
這個相當"硬體"的設計。
另外一個是直譯,沒有main函式。輸入字全部都可以執行。

再來是32位元時代,Forth仍用組合語言寫出來,這個完全沒有引起我的興趣。
因為這個時代,原廠都是給C函式庫,像USB如此複雜的裝置,要用別的語言再寫一次,那有時間?
要用,也用C寫的,但eLua有點大,它可以拆分將VM放入MCU內。用起來還是有點麻煩。

然後找到STM32Forth,看來可以了。且證明可以完全在Flash ROM上作業。不過它也有點問題。
加功能進去不是很容易,因為C的語法限制,不像組合語言一樣好寫,維護肯定不好用。先放著。

再來有一天找到LBForth,一看就是我要的。它有幾個特性:
  1. 它不使用機器碼,改用OP Code為基本單元。這個可以避免執行平台問題。
       2. 指令實現的C Source Code是集成的,不是分散的,這個易維護。但看來是動用到一些巨集技巧。

不過它在X86上執行,且為區域變數空間上。要在STM32上執行仍要改,順便修改時看看它是如何實作一個Forth。