星期一, 6月 23, 2008

nop? 為什麼程式裡有nop?

今天心情不太好,就多寫一些。

常寫Firmware(韌體)的人應該很常看到這個指令。不是以組合語言的指令nop,就是以c函數_nop_()的方式存在。nop望文生義,是no operation的縮寫。可是電腦的存在就是要來計算的,什麼時候要用到nop呢?

第一個情況是用來做短暫的delay,尤於這種delay在不同時脈上很難計算,所以通常適用於短期的等候上,這種情形不在今天的討論內。

我想要提的是,尤於firmware經常要存取硬體暫存器(register),其實通常在先人或前輩的慘痛教訓之後,你會看到如下的程式。

int rega _at_ 0x800; // 這是fixed address hardware register
rega = 0x55;
_nop_();
rega = 0x80;


這程式是要對某個register下一連串的指令,但是為什麼會中間加一段nop?
先人慘痛教訓千萬不能隨意修改,看不懂的code決對不要去動,你如果覺得這段code毫無義意就輕易拿掉你就會發覺原本會跑的程式不會動了。甚至你拿起LA仔細量起timing都不會發覺為什麼有需要在兩行指令內加上delay,硬體的速度並沒有那麼慢,那這是為什麼呢?


rega = 0x55;
rega = 0x80;


大部份的原因是因為compiler是optimize過的,它認為rega一個記憶體變數,你先寫0x55, 下一行指令又直接設成0x80,夠聰明的編譯器就自動省略第一行指令。變成

rega = 0x80;


那當然不會運作,於是工程師就加上nop指令,讓不是那麼聰明的編譯器不要將這行指令優化省略掉。就是你看到的結果。

故事到這裡還沒有結束

對於解決這種問題加上nop並不是正解,那怎麼辦呢?你會碰到的問題聰明的工程師早就有人跟你遇到同樣的問題。C/C++ 為語言設計了volatile modifier。這是修飾data storage的保留字,宣告變數為volatile就是宣稱此變數為揮發性的,也就是它的值是有可能隨外在環境改變,相對一般變數,你只要一存入,不去改它它會一直保持住,這種變數是non-volatile,編譯器可以任意優化不用怕出錯。而針對這種可能會隨著時間變動的register,你可以宣告成volatile,強制編譯器每次看到此變數一定要去做動作,這樣才不會出錯。

所以我們的程式最終的結果變成如此

volatile int rega _at_ 0x800; // 這是fixed address hardware register
rega = 0x55;
rega = 0x80;


volatile modifier不只用在硬體變數,只要有變數在你的thread內執行期間內有可能被改變(或者是被另一個thread),都適用加上這個volatile關鍵字來確保你程式的正確執行。

星期四, 5月 29, 2008

C++ programming language constant type

可以宣告一個data type為constant


const int bufSize = 512;


但是不能宣告一個沒有初始值的const data type,這很合理,因為宣告後就不能再改了。


const int bufSize; //compiler error here


那也不能宣告一個pointer指到const data type, 否則就可以經由pointer 修改constant data type.


const int minSalary; // 什麼都漲,只有薪水是固定的
int * pSalary = &minSalary; // error here.
*pSalary+=100000; // 如果可以的話多好


但是可以宣告一個const pointer type指向data, pointer的值本身可變動,也就是可以指向任何data value,但是你無法經由這個pointer去改變指向的data value, 因為pointer本身是constant type的。


const int * pConstInt; // pointer is constant
pConstInt = minSalary; // OK, can point to a constant value
int maxSalary; // this is not a constant value
pConstInt = &maxSalary; // OK too, can be point to a non-constant value.
*pConstInt += 5000; // 哈哈,不要妄想。你不能從這個pointer去改值。


利用這個特性,const pointer data type所指向的東西無法經由pointer修改。這在C++這種基本上所有的function call都是call by value的方式很好用。有時想要傳一整個structure, 但是又不想函式更改structure裡的內容,這時就可以將pointer 加以const 宣告。


func(const largestruct * pVeryLargeStructure) {
... do something you want here...
... 10000 lines of code below,
... but the sturcture pointed by VeryLargeStructure will not be changed.
}

你可以宣告一個const pointer,pointer本身值不能改變,但是指向的data type可以被改變,我沒有想到什麼時候會用到這樣的特性來解決問題。


int * const ptrNotChanged = &minSalary;
ptrNotChanged = &maxSalary; // error here.


其實寫程式寫了這幾年,雖然使用const這樣的keyword看起來微不足道,但是確可以減少debug的時間,一但養成良好的習慣,在你一開始定義的函數的同時就知道不可能改變傳進去的參數時,就加上const modifier,不但可以防止自己出錯的機會,對於將來自己或是別人來維護你的程式時,更是有很大的幫助。

星期五, 5月 16, 2008

好用的網路備份方式 - Mozy (a EMC company service)

參照我的備份方法 ,這裡的備份主要還是備份到外接式硬碟機。再佐以燒錄成光碟的方式,可是用久了(我一年才備份一次光碟),我光碟還是跑出好多張不曉得怎麼處理的光碟。況且以這樣的方式其實沒有異地備份。萬一出亂子的時候(備份就是要預防出了亂子的時候)還真是麻煩。

EMC是一間非常有名的儲存備份公司,他推出了網路備份的服務,你只要申請一個帳號,它就先給你2GB的網路儲存空間,下載安裝好它的備份程式後,它就默默的在背後幫你備份。不只這樣,這間公司的備份軟體使用介面極佳,很多細節都有考慮到給一般的人使用。


舉例來說,備份不是使用檔案夾的關念來選擇,而是檔案類型。
mozy_backupset

或是平常資料已經整理的很好了,你覺得類型不好用,想用自行檔案的方式來選擇。
mozy_filesystem

至於什麼時候備份的選項也是很聰明,可以自動選擇在電腦不忙的時候自動備份。
mozy_schedule

一旦裝好之後,只要電腦有寛頻,基本就可以不用理會,它會自動備份,完全不用操心。至於復原的方法也很方便從手冊上讀來也是很方便,基本上這是一家備份軟體做的非常好的解決方案,加上有EMC加持,我覺得沒有什麼理由不用,更加上如果你只備份2GB,它還不用錢。

如果你覺得不錯,想要申請,也覺得我這篇介紹不錯,申請帳候麻煩點一下我的連結https://mozy.com/?code=3SDB5A或是在建立帳號時,填入referral(介紹人)3SDB5 這個代號。被介紹人以及介紹人都可以增加250MB的儲存空間。

星期二, 4月 22, 2008

Borland C++ 3.1 link with c startup module and default link library

最近重操舊業,寫起早期的DOS程式,有很多技巧都已經忘光了,所以也吃了不少苦頭。找到原因後,趕緊記錄下來,希望下次再遇到時就不用再摸索一次了。

在IDE的環境,只要將CPP or C 的Source Code加入Prj內,Borland C++會自動將你處理後續的動作。如果要能更有彈性一點,就要自己做了。尤於我要與組合語言撰寫的library連結,所以內建的project management不怎麼適用,所以只好自行撰寫makefile。以command line bcc 先行以 -c 只編譯不 link,最後再一起與assembly object code連結。但是就算只有單純的一個空的main()函式,單純只compiler, link 時仍然需要link C startup module 與run time library。但是怎麼知道startup module 與 run time library 是那一個就有點麻煩了。早期的lanuguage help system 都寫的不太多,總是要猜一下,這樣一猜又猜掉我一天的時間.....

DOS program 有一件事很重要,要注意memory model, 這在32 bit programming 是連聽都沒聽過的事。所以compiler時,就要先指定memory model。以BC++來人說,即是


接下來要Link指明連結link large starup code and run time library, 分別是c0l.obj 與 cl.lib。


其中Link 時切記注意有順序關係的,我因為偷懶,一開始沒有將c0l.obj放在Link裡的第一項,Link雖然沒有錯誤訊息,但是執行是會發生異常現像,為此又搞了一段的時間來解決此問題。

如果不是很確定makefile要怎麼寫,borland++的bin folder內有提供prj2mak.exe 提供將project 檔轉換成makefile。雖然是borland C++ 內附的make utility 在使用的,不過大同小異,參考一下總是有幫助的。

星期二, 3月 18, 2008

北橫摔車,第一次做農夫的經驗

犛田,可是一項特別的技藝,一般的車禍可不算是犛田。

2/28假日早上,不知道為什麼睡不著太早起床,在吃完早餐、看完報紙後老婆小孩都還在床上睡覺。想了一想太久沒有去跑車,跟老婆報備一下,就決定往北橫出發去跑跑山路。尤於是剛結束差不多一個月的雨季,加上天氣冷,摩托車真的很久都沒有騎了,因為天氣冷,於是在盤算要穿什麼衣服,就在這裡加加減減,結果就忘了穿戴護膝,一直到了上了快速道路才想到護膝沒有穿,懶得再掉頭回去穿護具,因為就又造成後來的膝蓋擦傷。

一路上跟著車流跑,沿路碰了幾輛直接超過我的重機,一直到過了角板山之後,進入狹小的山路範圍內,此時就沒有什麼車了。我就這樣一個彎道又一個彎道繼續騎,複習及回味一下重機高扭力的出彎平順感。就這樣一個接一個彎道,我也不斷的進檔退檔,然後就在一個看起來不怎樣的彎道,降檔入彎傾斜,準備順勢出彎,一加油,後輪就突然失去抓地力,只感覺到為什麼我的視野一直往水平方向傾斜,再也拉不直,就突然驚覺,S**T.........我摔車了。

因為車速實在不快,我估計應該只有30出頭,剛摔完沒多久對向這時也來了一輛計程車,乘客很熱心就下來幫我牽車到旁邊放好。因為速度實在是不快,車子從倒地後大約只滑了4公尺左右,所以也沒去撞到什麼東西,純粹就是單純的犛田,快速的檢視了一下自身有沒有受傷,發覺左膝的褲子內外兩層全破了,上半身完全沒異狀,回家後隔了兩天才發現衣服左手外側也有輕微傷痕。手有手套、頭部有安全帽。全身除了忘了戴上護膝受了點小傷外,一切都沒事。再檢查一下車子,左側的防倒球被削掉一塊,引擎外殼有點擦傷,左側的方向燈又鈄了一點,這下子車子前後左右的方向燈幾乎全被被我摔過了,後方因為有裝防摔馬鞍袋,所以也沒什麼外傷了。


摔車之後決定不再往前,直接回家,一直回到家才跟老婆跟小朋友說我摔車。這時昭毅光幃就都很有興趣的問我會不會痛之類的。我把破皮的地方給他們看,跟他們講不怎麼痛,但是補了一句,面子比較痛。昭毅一開始還聽不懂我在說什麼,我就舉例給他聽,你在一群人前面,本來可以很帥的過去,結果不小心跌跤,這時生理上不怎麼樣,心理可是很糗呢。

Photo_022808_003
摔車的地方;小彎道加上速度慢,實在料想不到會在這裡摔。

被削掉的防倒球
被削掉的防倒球

捨身護主的馬鞍袋
捨身護主的馬鞍袋

引擎蓋的擦傷
引擎蓋的擦傷