星期一, 4月 13, 2009

園藝樂

上星期一因為心情不爽上班,ㄟ,我是說身體不舒服,莫名地得了星期一症候群,請了一天假一大早就跑到附近的內湖花市裡閒晃,第一次逛花市,即便我已經生活在內湖三年了。

老實說,這種別人在上班,我卻在外面蹓達又有錢領的感覺比大家都放假的感覺還開心。

很快買好東西後,就開始我的園藝樂,不知道有多少人聽過倒地鈴這植物,我在台北的山區晃了幾次沒看過,中南部很常見,所以趁著回南部掃墓順便摘了幾株,準備種在窗台邊希望能移植成功。



我覺得最好玩的地方在於它的種子,成熟的種子是黑色,中間會有一個白色的小愛心,很特別,就像小孩子收集星砂送給喜歡的人,有人也會收集種子,裝在小玻璃瓶裡當作禮物 ;)



因為瓶罐蟋蟀一書,特地買了幾個玻璃瓶回來建立自己的瓶中世界,植物就從老姐送的銅錢草移植過去,至於將蟋蟀養在瓶子裡就不必了 ;)







就像在水族箱種水草一樣,會有分前景、中景及後景,根據植物的高低來種植,使用箝子可以幫助更容易的將植物植入,以之前種水草的經驗,因為水族箱比較大的關係,這個難多了,比較難控制。



一個早上的成果,我連吃不完的地瓜都拿去種了 ;)

Complex C Declarations

因為 olv 丟了一個複雜的 C 語言宣告給我:
 void (*glXGetProcAddressARB(const GLubyte *procName))( void )
勾起學生時代的回憶,所以把這篇 1991 年出現在 comp.lang.c 的文章挖出來,這裡可以下載完整的內容。

The "right-left" rule is a completely regular rule for deciphering C declarations. It can also be useful in creating them.

First, symbols. Read

* -- as "pointer to" ----------- always on the left side --- 指標, 指向...
[ ] - as "array of" -------------- always on the right side - 陣列, 其陣列元素為...
( ) - as "function returning" - always on the right side - 函式, 回傳值為...

as you encounter them in the declaration.

利用 "right-left rule" 可以幫助我們正確地解讀 C 語言的宣告,不論是多麼複雜,即便是不合法的宣告。在解讀的過程中,若是遇到了上述表格裡的 * [ ] ( ) 符號,則將它們視為其後相對應的解釋。

STEP 1
------
Find the identifier. This is your starting point. Then say to yourself, "identifier is." You've started your declaration.

步驟一、先把識別子(變數名或函式名稱)找出來,這時候你可以說:xxx 是一個 ...,例如 int *p[ ]

識別子是 p,翻成「p 是一個...」

STEP 2
------
Look at the symbols on the right of the identifier. If, say, you find "( )" there, then you know that this is the declaration for a function. So you would then have "identifier is function returning". Or if you found a "[ ]" there, you would say "identifier is array of". Continue right until you run out of symbols *OR* hit a *right* parenthesis ")". (If you hit a left parenthesis, that's the beginning of a ( ) symbol, even if there
is stuff in between the parentheses. More on that below.)

步驟二、檢查識別子右邊出現的符號,如果有的話,例如看到 [ ] 我們可以說「xxx 是一個陣列,它的陣列元素是 ...」,若是看到 ( ) 則翻成「xxx 是一個函式,回傳值是 ...」,接著繼續地往識別子的右邊尋找其他符號,直到識別子右邊已經沒有符號或是遇到了 ")",跳到步驟三。

int *p[ ],往識別子 p 右邊遇到了符號 [ ],右邊也沒有其他符號,翻成「p 是一個陣列,其陣列元素是...」

另外,如果遇到的是 [N],也就是帶有陣列大小的陣列宣告,我們可以這樣翻「 xxx 是一個大小為 N 的陣列,它的陣列元素是...」,若是遇到的是 (yyy),我們可以翻成「xxx 是一個參數為 yyy 的函式,其回傳值為...」

STEP 3
------
Look at the symbols to the left of the identifier. If it is not one of our symbols above (say, something like "int"), just say it. Otherwise, translate it into English using that table above. Keep going left until you run out of symbols *OR* hit a *left* parenthesis "(".

步驟三、檢查識別子左邊的符號,如果出現的是 * [ ] ( ) 就照表翻譯,若出現的並不屬於上述的符號,那麼看到甚麼就說甚麼,不需要額外的翻譯,例如看到 int 就直接翻成 int,繼續地往識別子的左邊尋找,直到識別子左邊已經沒有其他符號或是遇到了 "(",接著回到步驟二(若是識別子左右兩邊已經沒有其他符號)

int *p[ ],往識別子左邊先找到了 *,所以翻成「p 是一個陣列,其陣列元素是指標,指向...」,繼續往左邊看,遇到了 int,由於識別子左右兩邊都沒有符號了,所以完整的解讀是「p 是一個陣列,其陣列元素是指標,指向 int」

「p 是一個陣列,其陣列元素是指向 int 的指標」

Now repeat steps 2 and 3 until you've formed your declaration.

重複地套用步驟二、三,直到完成整個宣告的解讀。

我相信剛接觸 C 語言的人很難分辨上述的 int *p[ ] 與 int (*p)[10] 的差別,試著套用 ”right-left-rule” 解讀:

1. int (*p)[10],識別子是 p,翻成「p 是一個」

2. 往p 的右邊看到的符號是 ”)”,根據步驟二的說明,我們跳到步驟三檢查識別子 p 的左邊

3. 往 p 的左邊看到的符號是 ”*”,翻成「p 是一個指標,指向...」

4. 繼續往 p 的左邊檢查,遇到的符號是 ”(”,根據步驟三的說明,我們回到步驟二檢查識別子 p 右邊的其他符號

5. 繼續往 p 右邊我們遇到了 [10],翻成「p 是一個指標,指向大小為10的陣列,其陣列元素是...」

6. 繼續往 p 右邊檢查,已經沒有其他符號了,根據步驟二的說明,接著繼續講查識別子 p 左邊的符號

7. 接著往 p 左邊我們遇到了 int,因為不屬於 * [ ] ( ) 中的一個,所以翻成「p 是一個指標,指向大小為10的陣列,其陣列元素是 int」

8. 識別子左右兩邊已經沒有符號,結束

知道了 p 的宣告,在寫程式的時候,就可以避免 assign 錯誤型態的變數給它的錯誤
int main(void) {
int array[10];
int (*p)[10] = &array;
array[2] = 111;
(*p)[2] = 222;
printf("%d\n", array[2]);
return 0;
}

回頭看 void (*glXGetProcAddressARB(const GLubyte *procName))( void ),套用 ”right-left-rule”:

1. 先把識別子找出來, glXGetProcAddressARB 是識別子,翻成「glXGetProcAddressARB 是一個」

2. 往識別子右邊看到的符號是 (const GLubyte *procName),翻成「glXGetProcAddressARB 是一個參數為 const GLubyte *procName 的函式,其回傳值為...」

3. 再往右邊看下去,我們遇到了 ”)”,根據步驟二的說明,我們跳到步驟三

4. 往識別子的左邊看到的符號是 ”*”,翻成「glXGetProcAddressARB 是一個參數為 const GLubyte *procName 的函式,其回傳值為指標,指向...」

5. 繼續往左邊看,我們遇到了符號 ”(“,根據步驟三,我們跳回步驟二

6. 回到步驟二,我們繼續往識別子右邊找其他的符號,這時出現的是 “( void )”,所以翻成「glXGetProcAddressARB 是一個參數為 const GLubyte *procName 的函式,其回傳值為指標,指向一個參數為 void的函式,其回傳值為...」

7. 再往右邊找,已經沒有任何符號,跳到步驟三

8. 往左邊找到了 void,直接翻,「glXGetProcAddressARB 是一個參數為 const GLubyte *procName 的函式,其回傳值為指標,指向一個參數為 void的函式,其回傳值為 void」

9. 識別子左右兩邊已經沒有符號了,完成
「glXGetProcAddressARB 是一個參數為 const GLubyte *procName 的函式,其回傳值為指標,指向一個參數為 void的函式,其回傳值為 void」再稍微修飾一下,改成「glXGetProcAddressARB 是一個參數為 const GLubyte *procName 的函式,其回傳值為函式指標,指向一個參數為 void的函式,其回傳值為 void」

void helloworld(void) { return; }

void (*glXGetProcAddressARB(char *procName))( void ) {
return helloworld;
}

int main(void) {
glXGetProcAddressARB("ARB");
return 0;
}

星期二, 3月 31, 2009

石來運轉

石來運轉取其諧音「時來運轉」,因為風水的緣故這類的裝飾品還蠻常見的,今天的故事就發生在我家裡的它。

直徑和深度都比一般的臉盆來的大一些,盆子本身是用整顆石頭下去挖空的,加上那顆不停轉動的石球及球下的底座(石頭做的),在沒有裝水時,普通人已經是很難抱得動,更何況是在盆子裝滿水的情況下,要移動它其難度不難想像。



它的位置就擺放在家裡的一角,安放在桌子上,桌子約莫 100 公分高



某天老爹晚上打電話找我閒聊,告訴我家裡發生一件詭異的事情,吃完晚餐,他們從外面散步回家後,發現地上忽然出現一大攤水,才看到整個石盆裡的水倒的乾乾淨淨的,乾淨到放個十元硬幣進去,剩下的水都沒有辦法淹過硬幣。

我們開始聊可能的原因:

「石盆會不會有破洞?」「本來也這樣想阿,可是重新裝水進去後,也沒有看到漏的地方阿」(台)
「會不會是地震還是家裡的狗撞到桌子?」「水也不會漏的這麼乾淨」(台)
「小偷?」「嘿阿,但是家裡都檢查過了,也沒有東西不見阿」(台)

想不到甚麼合理的解釋,這個話題就這樣結束了

接著,再和我老媽接上電話,聊到這件事,才知道原本我老爹在石盆裡還有放一顆一顆的佛珠(大顆的那種木製佛珠),因為放在水裏面已經有一段時間,表面都發霉了,所以我爸在出門前就先把所有的佛珠從水裡撿起來丟到垃圾桶裡了...

最後再打電話給我爸,聊到佛珠的事情,問他後續的動作

「就再從垃圾桶撿起來,一顆一顆的洗乾淨再放回去」(笑)

現在,它還是待在原來的位置上,石頭繼續不停的轉阿轉,裏面的水也再沒有少過 ;)

Prex + GTA02 (Cont.)

有兩個 Device Driver 對移植 Prex 是很重要的,分別是 Clock(Timer) 及 Serial(Uart)。

Kernel 負責的工作項目之一就是排程,而 Timer 對於排程則是一個很重要的依據,提到了 Timer 就不得不去面對中斷 (Interrupt) 的處理,Prex 用了一個很簡單的機制及資料結構使其支援 Nested Interrupt 同時利用軟體實作 Interrupt Priority.

而沒有 Serial Driver 就無法和 Prex 有最基本的互動,在 Booting 後,透過 Serial 我們可以在Prompt 下執行命令,這樣才好玩不是嗎。下載完整的 Booting logImage,所有的 Patch 檔和文件會在隨後附上。




另外,對於較 Low Level 的程式的 Trace + Debug,我向來只有使用 JTAG 搭配 Objdump,利用這個方法,我們可以把 Bootloader 及 Kernel 之類的程式很輕鬆也很完整的 Trace (可設中斷點),從 Device Power On 之後系統的每個動作到進入 Kernel 後的一些行為,都會清清楚楚的呈現在面前 ;)

星期日, 3月 22, 2009

Sweet Rain

人生不在於生命的長短,而是在於是否已經活出自己的價值,完成此行目的,而沒有遺憾。

「不過小美才十歲阿」
「目的已經達到,跟壽命長短無關」

如果真能像電影 - 死神的精準度裡一般,死神們會降落凡間化身為人,融入屬於我們自己的故事裡,藉此去判斷一個人從出生到現在的人生內容,再決定執行死亡與否。



就像故事裡的藤木一惠,個性悲觀,周遭所有愛她的人都離她而去,投擲銅板出現的永遠是否定的那一面,不止一次有過死亡的念頭,似乎從來就不曾真正活過,卻在故事的最後被音樂製作人發掘,受人肯定,她的人生是從那一刻才開始,也因此判定的結果是...

不過,電影終歸是電影,事實上,每分每秒在你張口呼吸的當下,遺憾,就在世界各地不斷地發生,沒有人可以給我們第二次的機會,你唯一可以作的就是去行,把握自己的每一刻,不要讓自己在最後一刻後悔抱憾,意外之所以稱之為意外,因為你無法知道它何時發生。我情願承受作卻失敗的痛苦,也不願活在後悔當初為何不的深淵中。

「我已經毫無遺憾,隨時都能死去」

在電影後段出現的一些台詞,所描述的也是我所追求的一種關係,很多場合應該用的到 ;)

星期三, 3月 18, 2009

Prex + GTA02

Prex - An Open Source, Royalty-free, Real-time Operating System.

關於 Prex 的介紹,網頁已經有詳細的說明,最吸引我的地方是 - "Written in ANSI C based" "microkernel architecture" "POSIX emulation layer"

週末利用一些時間嘗試將它移植到 GTA02(S3C2442) 上,Prex 自附的 bootldr 已經可以將 kernel 載入並執行,雖說如此,現在的進度仍是落後,還有許多的 device driver 尚待完成,可以在 Download 裡找到相關的資料檔案,包含 kernel image 及 patch,剩下的會再放上,希望能有越來越多的人一起學習,彼此分享 ;)

星期三, 3月 04, 2009

MARVELS

某天從老家台南坐車回台北,一如往常選擇對住內湖的我最方便的統聯客運,終點站是台北松山,大約 5.5 個小時的車程。

下午 5:40 的車從台南北門路出發,大約晚上八點多開始,我就隱隱約約聽到有人(女生)嘻鬧的聲音,當時因為正在看電影,會在兩個頻道間作切換,所以當下以為聲音是從電視傳來的,也就不以為意了。

松山站是終點站,大部分的乘客會在前面幾站就陸續下車,往往到了松山站時,車上的乘客只剩小貓兩三隻了。

當車子彎進松隆路或永吉路時,我會開始整理手邊的東西,準備下車,當我把座椅的喇叭關掉後,這次,我很清楚的聽到一個女生的嘻笑聲,想像兩個國中或高中女生坐在一起玩的很瘋時發出的聲音(類似互相搔癢但不是哈哈大笑的那種)

因為太清楚了,所以我的感覺是「拜託!公共場所耶,還敢玩這麼瘋喔...」我想偷瞄一下對方是長甚麼樣子,從雙人坐中間的縫隙偷偷往後看過去,後面除了中間坐著一個看似剛睡醒的女生,接著就是坐在最後一排的一個男生,從他們發呆的表情看來,不太像是前一秒玩的很瘋的樣子。

其實在聽到時,耳朵很清楚地告訴我聲音從前面傳來,但同時,大腦也很清楚地告訴我,我位子是第二排,而我很確定第一排已經沒有人了,這就是為甚麼聲音從前面來,但我卻回頭看。

直到下車前,我還在看是不是司機旁的座位坐了一個女生,不過當然是沒有。

星期四, 2月 19, 2009

ldm stm

在使用 stm / ldm 實作 Stack 時,發現先前追程式碼時,太過輕率而忽略掉一些細節,假設大家都了解 stmia / stmib / stmda / stmdb 及 ldmdb / ldmda / ldmib / ldmia 指令的語法與實際意義。
r0=0x32001000
r1=0x09
r2=0x08
r3=0x07

stmib r0!, {r1-r3}
ldmda r0!, {r1-r3}
在執行完 ldmda 後,我認為暫存器 (r1,r2,r3) 的值分別是 (7,8,9),也就是會 Reverse,這樣既符合 Stack 的精神(FILO)也符合該指令的定義,但實際的結果是 (r1,r2,r3) = (9,8,7)



問題出在哪裡呢,直到這裡的一句話,一切的疑問都解開了。

「Remember that registers are always stored lowest at lowest address」翻成白話文的意思為「編號較小的暫存器會存到較低的記憶體位址」或「較低的記憶體位址內容會載入到編號較小的暫存器」

所以 (r1,r2,r3) = (9,8,7) 這樣的結果是沒有問題的,看 ldmda 指令,r3 相較於 r1,r2 是編號大的暫存器,因此會將較高的記憶體位址的內容寫入到 r3 裡。



再看一個例子,按照前面的原則思考,注意記憶體位址及暫存器編號的關係。

星期二, 1月 27, 2009

華采衣兮若英

「李力想,幸好自己沒有去塞北,而是在長安城陪著若英」

「匈奴」一書裡的男女情沒有撒狗血的情節,就只是很淡,很真實,卻很觸動人心,是整個故事的起點,李力初遇若英時的青澀,現實的衝擊,抉擇時內心的交戰

「若英,我要走了,去北方投軍。」
「你怎麼突然想要去投軍?」
「我喜歡你。」

成長!李力的生活雖然說不上是渾噩,卻也是有一天過一天,許多問題是李力未曾思考過,他也從未真正地替自己下過決定,直到遇上若英,直到蘇總管的點醒, 才意識到「他一出身即是劣民的身份」的事實,現實總是殘酷的。這是命,沒得選擇;這是事實,無可避免。

邊塞投軍,封侯拜將是李力唯一想到的路,為了他娘,為了自己,更是為了若英,此為大破大立,在那一刻,看似不相干的問答,卻是表達了李力心中一切所想:

- 奴僕 從軍 良民 封侯拜將 若英 -

「投軍兩年,他仍然什麼也沒有,又要如何面對若英呢」
「你回來為什麼不找我?」
「我們都是奴,我們都沒有選擇,李力,你甚麼時候會回來?」
「很快,我很快就會回來」
「趙破奴還沒成名,他那個新婚妻子已是長安城人人掛在嘴上的美女啦!」

無奈與期待。

星期五, 1月 23, 2009

非戰之罪

「匈奴」一書作者是張國立先生,我在一個偶然的機會裡從 PTT 看到許多鄉民的淚推而買下。書裡許多段落讓人鼻酸眼眶紅,情緒被書中的人物帶著走,其中的因素,更有些是因為 - 我了解,因為無奈所以才跟著難過;內容除了描述戰爭外還有感情,其實就是述說一個人的心境及面對自己時的所產生的矛盾與掙扎。



「善戰者求戰,他的舞台在沙場,但功名利祿之成就卻牽涉到許多非戰之罪及非一已所能控制的機緣」

在台灣,許多的工程師是絕對的技術本位,將所有的心思氣力放在工程技術的追逐上,接著一廂情願的以為只要將技術的東西搞好,案子作出來,總有變成千里馬的那天,等待遇到伯樂的賞識,跟著就一路飛黃騰達上去。只是,早晚總會有那麼一天,這樣一廂情願的想法會讓你很鬱悶,因為非但沒有你想的順利,也許還會發現那些你認為能力不如你的人卻早已經在你之上,更比你得到很多的關愛。憤恨之後,也許你還是不解,但 是事實就是如此,除了技術,也許你在其他方面並不完善,連帶讓你的主管忽略到你原有的優點,可惜了。

懂得做人,打點好關係,知道如何與人溝通,提升自己的形象,形象即是假象,虛情假意是必要之惡,也是必要之善,別鑽牛角尖在虛假兩字,在不失大方向,不偏離大原則下,如果能讓別人開心,別人也讓你開心,何樂不為呢。在專業能力之外,還必須將管理的觀念帶入。「管、理」就是「關心、調整」,所以:

「管理自己的情緒」「管理和其他同事及主管的關係」「管理和其他人之間的溝通管道是否順暢」「管理別人眼中看到的你」,有不完善的地方則立刻調整,八面玲瓏是也。

「河間王府已沒落,隔街的公孫侯府卻是道賀的川流不息」「公孫家交往的朝廷高官愈來愈多」

「廢話,李廣封不了侯不是因為他的命不好,是他不會搞關係,連我一個匈奴人都知道沒人在朝廷上撐腰,會打仗有個鬼用」

「要是李廣能有提莫呼的嚮導隊在,說不定趙食其會因李廣而一戰封侯」

「光會打仗是沒有用的,你曾拜當今宰相為師嗎?你到大將軍府送過禮嗎?你認識幾個皇帝身邊的侍中?」「多謝先生一番指導,李陵心內的結才豁然而解,不過人生自有定數,我李家歷代都志在沙場,和朝廷官員交際卻始終做不來,這是先生說的命哪!」

「李力覺得將領間的不合會影響戰力,可是他依然沒有勸阻李陵。李廣一家都太自負了,這是李家個個為英雄的本錢吧!」

自負是當英雄的本錢,但是單有自負是無法成就大業的,是運命使然嗎,李廣祖孫倆都因沒有打好關係,無法得到支援,終至失敗。飛將軍李廣以自殺了結一生,其孫李陵兵敗詐降於匈奴,武帝卻誅殺九族,自此李氏一族沒落。

知道是一回事,能不能作到又是另當別論,每個人的個性不同,很多事勉強不來,只能說儘量為之。話說回來,有另一種人,能力不足,不去思考如何提升能力,卻將大部分的時間花在逢迎巴結,別當這種人,名聲會不好。

漢武帝是個混帳傢伙,不過成就豐功大業的有哪個人不是混帳,只不過,永遠都是勝者王,敗者寇,歷史是由最後存活的人製造,這就是我們讀的歷史。