久久精品国产亚洲av水果派,久久久久亚洲AV无码麻豆,2020久久精品亚洲热综合一本http://www.shnenglu.com/oosky/一天一個腳印...... <br>每日一句: <script language="javascript" charset="utf-8" src="http://sl.iciba.com/spdshow.php"></script>zh-cnWed, 07 May 2025 03:18:21 GMTWed, 07 May 2025 03:18:21 GMT60調試5.0M sensor模組的筆記(轉載)http://www.shnenglu.com/oosky/archive/2010/09/15/126672.html任我行任我行Wed, 15 Sep 2010 08:58:00 GMThttp://www.shnenglu.com/oosky/archive/2010/09/15/126672.htmlhttp://www.shnenglu.com/oosky/comments/126672.htmlhttp://www.shnenglu.com/oosky/archive/2010/09/15/126672.html#Feedback0http://www.shnenglu.com/oosky/comments/commentRss/126672.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/126672.html詳細出處:http://www.52rd.com/Blog/Detail_RD.Blog_bmw7_24676.html#41074

   在25平臺上調試了一款帶ISP處理器的5.0M sensor 模組,在25上實現了5.0M sensor的預覽拍照功能。下面是調試過程中的一些筆記:

一.關于H-sync /V-Sync的知識:

1. 分辨率:比如說640x480,就會有640 個pixel &480 line,那么每個V-sync的信號時間內就會有480個H-sync,而一個H-sync會有640個pixel。但是,每個pixel會有2 byte,所以我們會量到PCLK 在一個H-sync內的數量會有1280個。

2. H-sync /V-Sync的極性polarity: polarity就是資料有效的準備,比方說V-sync上的H-sync有可能在V-sync的low,也有可能在high出現。

 

二. 所使用的ISP處理器簡介:XXX838是一款isp(圖像信號處理器)ic,核心是一款arm7 process,提供自動對焦,人臉識別等功能。BB通過i2c與其進行命令類的數據通信,而sensor數據則通過CCIR總線傳輸給BB.

 

三. 25平臺 camera處理流程學習

1.       void cam_event_ind_hdlr(ilm_struct *ilm_ptr)// This function is to handle camera event indication.

    在該函數中,通過camera_capture_mem_process(&capture_mem_param)命令從lcd層獲取capture數據,然后通過jpeg_encode_process(&jpg_encode)命令將這些數據軟編碼成jpeg格式的數據。

  

2.       void cam_capture_req_hdlr(ilm_struct *ilm_ptr)// This function is to handle camera capture request.

(1)       該函數首先執行exit_camera_preview_process();命令退出preview流程;

(2)       ./* copy preview data to MMI buffer */

memcpy(

                (kal_uint8*) cam_context_p->frame_buffer_p,

                (kal_uint8*) cam_context_p->int_frame_buffer_p,

                cam_context_p->frame_buffer_size);

(3). /* release preview related memory */

        cam_release_memory();

  

3.cam_context_p->capture_buffer_p :所需存儲的拍照數據指針

  cam_context_p->file_size :所要存儲的數據大小

4. 在cam_open_image_file函數中執行命令

cam_context_p->capture_buffer_p = (kal_uint32) med_alloc_ext_mem(buffer_size);

來分配內存。

Capture數據存儲指針:capture_isp_param.target_buffer_start_address = (kal_uint32) cam_context_p->capture_buffer_p;

camera_capture_jpeg_process(&capture_isp_param);

isp_capture_jpeg_data.target_buffer_start_address=isp_data->target_buffer_start_address;

sw_jpeg_encode_config_data.jpeg_file_start_address=isp_capture_jpeg_data.target_buffer_start_address;

 

 5. camera capture后的數據傳送流程:cam_context_p->intmem_start_address.

  (1).  capture_isp_param.intmem_start_address = cam_context_p->intmem_start_address =

        (kal_uint32) med_alloc_int_mem(cam_capture_mem[0]);//只有45k

    capture_isp_param.intmem_size = cam_context_p->intmem_size = (kal_uint32) cam_capture_mem[0];

  (2). file_size = camera_capture_jpeg_process(&capture_isp_param); //jpeg編碼后的文件大小

  (3).isp_capture_jpeg_data.intmem_start_address=isp_data->intmem_start_address;

       isp_capture_jpeg_data.intmem_size=isp_data->intmem_size;

  (4).    intmem_init((kal_uint32 *) isp_capture_jpeg_data.intmem_start_address,

                                   isp_capture_jpeg_data.intmem_size);

  (5).sw_jpeg_encode_config_data.intmem_start_address=isp_capture_jpeg_data.intmem_start_address; //將所獲取的capture原始數據地址指針賦給軟編碼的起始地址

 

 

四.調試關鍵點

1. 首先調通I2C通訊,必須確保BB與ISP的I2C通訊正常;

2.  重新定義#define MAX_CAM_FILE_BUFFER_LEN   (3150*1024)    /* 2700kb for 5.0M */

 

3.仿照camera_capture_jpeg_process函數,創建一個新函數,在該函數中對獲取的數據直接存儲,而不經過jpeg編碼流程(由于XXX838傳輸過來的已經是jpeg格式的數據)。

注意:

(1)       在該函數中,要設置:

ENABLE_CAMERA_OUTPUT_TO_MEM;//ISP輸出至Memory,

SET_CAMERA_CAPTURE_MODE

 /*** Capture,等待VSYNC中斷**/      

 (2).在capture完成后,要DISABLE_CMOS_SESNOR;//關閉sensor信號。

 (3).此時,獲取的capture的數據已經存儲在isp_data->target_buffer_start_address中;

然后讀取這些數據,通過0xff ,0xd8判斷文件頭,0xff ,0xd9判斷jpeg文件尾及其長度。

(4)最后,通過kal_int32 cam_close_image_file(kal_uint32 size)保存文件


 



任我行 2010-09-15 16:58 發表評論
]]>
關于調用約定(cdecl、fastcall、、thiscall) 的一點知識http://www.shnenglu.com/oosky/archive/2007/01/08/17422.html任我行任我行Mon, 08 Jan 2007 06:17:00 GMThttp://www.shnenglu.com/oosky/archive/2007/01/08/17422.htmlhttp://www.shnenglu.com/oosky/comments/17422.htmlhttp://www.shnenglu.com/oosky/archive/2007/01/08/17422.html#Feedback3http://www.shnenglu.com/oosky/comments/commentRss/17422.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/17422.html閱讀全文

任我行 2007-01-08 14:17 發表評論
]]>
PE文件格式詳解http://www.shnenglu.com/oosky/archive/2006/11/24/15614.html任我行任我行Fri, 24 Nov 2006 02:29:00 GMThttp://www.shnenglu.com/oosky/archive/2006/11/24/15614.htmlhttp://www.shnenglu.com/oosky/comments/15614.htmlhttp://www.shnenglu.com/oosky/archive/2006/11/24/15614.html#Feedback5http://www.shnenglu.com/oosky/comments/commentRss/15614.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/15614.html閱讀全文

任我行 2006-11-24 10:29 發表評論
]]>
消費者維權的5種途徑http://www.shnenglu.com/oosky/archive/2006/10/12/13609.html任我行任我行Thu, 12 Oct 2006 09:25:00 GMThttp://www.shnenglu.com/oosky/archive/2006/10/12/13609.htmlhttp://www.shnenglu.com/oosky/comments/13609.htmlhttp://www.shnenglu.com/oosky/archive/2006/10/12/13609.html#Feedback0http://www.shnenglu.com/oosky/comments/commentRss/13609.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/13609.html值10.1期間,到上海買了款Acer的本本,7天后問題出現。換機后麻煩更大。
詳細過程就不說了,具體看這個網址:http://benyouhui.it168.com/viewthread.php?tid=399111&extra=page%3D1

上網找了找相關的法律及法規。才明白還可以有多種途徑。看來這個消費者權益法還是必要讀讀看看。

消費者維權的5種途徑



一、協商和解

協商和解的定義

????消費者與經營者在發生爭議后,就與爭議有關的問題進行協商,在自愿、互諒的基礎上,通過直接對話擺事實、講道理,分清責任,達成和解協議,使糾紛得以解決的活動。消費者權益爭議的協商和解是一種快速、簡便的爭議解決方式,無論是對消費者還是對經營者,它都不失為一種理想的途徑。事實上,日常生活中大量的消費者權益爭議都是通過這種方式解決的。

消費者與經營者協商和解的法律依據

????《消費者權益保護法》第34條明確規定:"消費者和經營者發生消費者權益爭議的,可以通過下列途徑解決:(1)與經營者協商和解;(2)請求消費者協會調解;(3)向有關行政部門申訴;(4)根據與經營者達成的仲裁協議提請仲裁機構仲裁;(5)向人民法院提起訴訟。"此條第1項規定,即"與經營者協商和解",便是消費者與經營者協商和解的法律依據。

協商和解的步驟

????在實踐中,協商和解可以在其權益受到侵犯時,帶上有關證據,如購貨憑證或者服務單據以及受損失證據,找到經營者,向其負責人或者主管解決糾紛的部門說明情況,并提出自己的意見和要求。如果經營者覺得消費者的意見和要求合理,就會接受。如果經營者覺得消費者的要求過高,就會要求消費者降低其要求。經過一番"討價還價"后,達成一個雙方都愿意接受的協議時,爭議就解決了。

協商和解應堅持協作和平等原則:

????協作原則。要求消費者與經營者在融洽的氣氛中,在互相諒解的基礎上,本著實事求是、團結協作的精神,通過擺事實講道理,弄清事實,分清責任,自愿地達成協議,避免只從自己一方的利益出發,堅持已見,互不相讓。
????平等原則。消費者和經營者要在平等的前提下自行協商解決消費者權益爭議。決不允許任何一方憑借某種勢力,以強凌弱,以大壓小,享有特權,獲得不平等的利益。

在協商和解時,消費者應注意以下問題:

????針對經營者故意拖延或無理拒絕消費者協商和解建議的行為,消費者應立即采取措施,用其他途徑解決爭議問題。即可用投訴、申訴或仲裁、起訴手段解決糾紛。如果經營者的故意拖延和無理拒絕,致使消費者財產損失擴大的,經營者除了應當滿足消費者正常要求外,還應當就擴大的損失承擔賠償責任。

????針對經營者故意推卸責任,認為產品出現質量問題是生產廠家的事,要求消費者直接找廠家交涉的行為,按《消費者權益保護法》第35條規定:"消費者在購買、使用商品時,其合法權益受到損害的,可以向銷售者要求賠償。銷售者賠償后,屬于生產者的責任或者屬于向銷售者提供商品的其他銷售者的責任的,銷售者有權向生產者或者其他銷售者追償。消費者或者其他受害人因商品缺陷造成人身、財產損害的,可以向銷售者要求賠償,也可以向生產者要求賠償。屬于生產者責任的,銷售者賠償后,有權向生產者追償。屬于銷售者責任的,生產者賠償后,有權向銷售者追償。消費者在接受服務時,其合法權益受到損害的,可以向服務者要求賠償。"因此,當消費者遇到商品質量問題時,如經營者推卸責任,認為是生產廠家的問題,要求消費者直接找廠家交涉時,消費者應當有自我保護意識,不能挾在中間讓廠家和經營者當"皮球"踢。要以法律規定為依據,切實維護自己的合法權益。

????針對經營者以店堂通知、聲明、告示為由,拒不承擔責任的行為,按《消費者權益保護法》第24條規定:"經營者不得以格式合同、通知、聲明、店堂告示等方式作出對消費者不公平、不合理的規定,或者減輕、免除其損害消費者合法權益應當承擔的民事責任。格式合同、通知、聲明、店堂告示等含有前款所列內容的,其內容無效。"因此,當消費者因商品質量和服務問題與商家交涉、協商時,千萬不能為其店堂內服務規則或商品銷售告示所約束,這些服務規則與法無據,沒有法律效力,應視為無效規則。

二、投訴和調解

投訴的定義:

????消費者投訴,是指消費者為生活消費需要購買、使用商品或者接受服務,與經營者之間發生消費者權益爭議后,請求消費者權益保護組織調解,要求保護其合法權益的行為。

調解的含義:

????調解,即由第三方對爭議雙方當事人進行說服勸導、勾通調和,以促成爭議雙方達成解決糾紛的協議的活動。
《消費者權益保護法》規定,消費者爭議可以通過消費者協會調解解決。實際上,消費者糾紛的調解并非只能由消費者協會進行,任何第三人參與消費者糾紛的解決,促成爭議雙方達成協議的,都屬調解的范圍。并且只要不存在違法行為,則調解同樣受法律承認。

調解的原則:


????1、自愿原則。調解應建立在雙方自愿的基礎之上。調解不同于審判,當任何一方不同意調解時,應終止調解,而不得以任何理由加以強迫。

????2、合法原則。調解活動應在合法的原則上進行,既要有必要的靈活性,更要有高度的原則性,不能違反法律的規定來"和稀泥"。

投訴的形式:


????消費者投訴可以采取電話、信函、面談、互聯網形式進行。但無論采取哪種形式,都要講清楚以下內容:一是投訴人基本情況。即投訴人的姓名、性別、聯系地址、聯系電話、郵政編碼等。二是被投訴方的基本情況。即被投訴方名稱、地址、電話等。三是購買商品的時間、品牌、產地、規格、數量、價格等。四是受損害的具體情況、發現問題的時間及與經營者交涉的經過等。五是購物憑證、保修卡、約定書復印件等。

三、行政申訴

申訴的定義

????消費者和經營者發生權益爭議后,可以請求政府有關行政部門依行政程序解決爭議,與其他爭議解決途徑相比,申訴具有高效、快捷、力度強等特點。?
消費者向政府有關行政部門申訴的法律依據

???《消費者權益保護法》第34條的規定,消費者和經營者發生消費者權益爭議的,可以向有關行政部門申訴。

消費者如何進行申訴?

????消費者決定申訴時,應依照商品和服務的性質向具有相關職能的行政部門提出。消費者申訴一般應采用書面形式,一式兩份,并載明下列事項:(1)消費者的姓名、住址、電話號碼、郵政編碼;(2)被申訴人的名稱、地址、聯系電話、郵政編碼;(3)申訴的要求、理由及相關的事實根據;(4)申訴的日期。必要時,消費者可委托代理人進行申訴活動,但需向有關行政部門提交授權委托書。

????消費者向有關行政部門提出申訴后,如果與經營者協商和解,達成和解協議的,可以撤回申訴,請求有關行政部門根據和解協議作出調解書。如果與經營者達成仲裁協議,可以撤回申訴,向仲裁機構提請仲裁。如果想通過法律途徑解決,可以撤回申訴,向人民法院提起訴訟。

四、提請仲裁

仲裁的定義?

????雙方當事人在爭議發生前或者爭議發生后達成的協議,自愿將他們之間的爭議提交雙方所同意的仲裁機構居中調解,作出判斷或裁決的活動。

仲裁的優越性

仲裁具有當事人意思自愿、程序簡便、一裁終局、專家仲裁、費用較低、保守機密、相互感情影響小等特征。

當事人采取仲裁方式解決糾紛,應注意以下幾點:

(1)當事人采用仲裁方式解決糾紛,應當是雙方自愿,并達成仲裁協議;

(2)向哪個仲裁組織提請仲裁,由當事人協議選定;

(3)可以選擇或者委托仲裁組織指定仲裁員;

(4)可以自行和解,達成和解協議的,可以請求仲裁庭根據和解協議作出裁決書,也可以撤回仲裁申請。

仲裁的原則和制度

仲裁實行自愿、獨立、公正、一裁終局的原則和制度。

仲裁協議的定義

????雙方當事人自愿把他們之間的經濟爭議提交仲裁解決的書面約定。其表現形式包括合同中訂立的仲裁條款和以其他書面方式在糾紛發生后前或者發生后達成請求的仲裁協議。仲裁協議是獨立存在的,合同的變更、解除、終止或者無效,不影響仲裁協議的效力。

仲裁協議應具備的內容

(1)請求仲裁的意思表示;

(2)仲裁事項;

(3)選定的仲裁委員會。

當事人提請仲裁應當符合的條件

(1)有仲裁協議;

(2)有具體的仲裁請求和事實理由;

(3)屬于仲裁委員會的管理范圍。

仲裁案件受理費的承擔

????仲裁費用原則上由敗訴的當事人承擔,當事人部分勝訴,部分敗訴的,由仲裁庭根據當事人各方責任大小確定其各自應當承擔的仲裁費用的比例,當事人自行和解或者經仲裁庭調解結案的,當事人可以協商確定各自承擔的仲裁費用的比例。

五、提起訴訟

提起訴訟的定義

????消費者因其合法權益受到侵害后,可以向人民法院提起訴訟,請求人民法院依照法定程序進行審判。在我國,訴訟大致分為三種形式:(1)刑事訴訟;(2)民事訴訟;(3)行政訴訟。消費者因其合法權益受到侵害而提起的訴訟屬于民事訴訟范疇。

提起訟訴必須具備的法定條件

(1)原告必須是與本案有直接利害關系的公民、法人和其他組織;
(2)有明確的被告;
(3)有具體的訴訟請求和事實、理由;
(1)屬于人民法院受理民事訴訟的范圍和受訴人民法院管轄。

符合以上條件的起訴,人民法院才會予以受理。




任我行 2006-10-12 17:25 發表評論
]]>
嵌入式面試考題http://www.shnenglu.com/oosky/archive/2006/08/28/11763.html任我行任我行Mon, 28 Aug 2006 01:51:00 GMThttp://www.shnenglu.com/oosky/archive/2006/08/28/11763.htmlhttp://www.shnenglu.com/oosky/comments/11763.htmlhttp://www.shnenglu.com/oosky/archive/2006/08/28/11763.html#Feedback5http://www.shnenglu.com/oosky/comments/commentRss/11763.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/11763.html 作者不祥,
別看這題目有些比較簡單,仔細做來,可還是發現不少問題。?


預處理器(Preprocessor)


1. 用預處理指令#define 聲明一個常數,用以表明1年中有多少秒(忽略閏年問題)

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在這想看到幾件事情:
1). #define 語法的基本知識(例如:不能以分號結束,括號的使用,等等)
2). 懂得預處理器將為你計算常數表達式的值,因此,直接寫出你是如何計算一年中有多少秒而不是計算出實際的值,是更清晰而沒有代價的。
3). 意識到這個表達式將使一個16位機的整型數溢出-因此要用到長整型符號L,告訴編譯器這個常數是的長整型數。
4). 如果你在你的表達式中用到UL(表示無符號長整型),那么你有了一個好的起點。記住,第一印象很重要。


2. 寫一個“標準”宏MIN,這個宏輸入兩個參數并返回較小的一個。

#define MIN(A,B) ((A) <= (B) (A) : (B))
這個測試是為下面的目的而設的:
1). 標識#define在宏中應用的基本知識。這是很重要的,因為直到嵌入(inline)操作符變為標準C的一部分,宏是方便產生嵌入代碼的唯一方法,對于嵌入式系統來說,為了能達到要求的性能,嵌入代碼經常是必須的方法。
2). 三重條件操作符的知識。這個操作符存在C語言中的原因是它使得編譯器能產生比if-then-else更優化的代碼,了解這個用法是很重要的。
3). 懂得在宏中小心地把參數用括號括起來
4). 我也用這個問題開始討論宏的副作用,例如:當你寫下面的代碼時會發生什么事?
least = MIN(*p++, b);


3. 預處理器標識#error的目的是什么?

如果你不知道答案,請看參考文獻1。這問題對區分一個正常的伙計和一個書呆子是很有用的。只有書呆子才會讀C語言課本的附錄去找出象這種
問題的答案。當然如果你不是在找一個書呆子,那么應試者最好希望自己不要知道答案。


死循環(Infinite loops)


4. 嵌入式系統中經常要用到無限循環,你怎么樣用C編寫死循環呢?

這個問題用幾個解決方案。我首選的方案是:
while(1) { }
一些程序員更喜歡如下方案:
for(;;) { }
這個實現方式讓我為難,因為這個語法沒有確切表達到底怎么回事。如果一個應試者給出這個作為方案,我將用這個作為一個機會去探究他們這樣做的
基本原理。如果他們的基本答案是:“我被教著這樣做,但從沒有想到過為什么。”這會給我留下一個壞印象。
第三個方案是用 goto
Loop:
...
goto Loop;
應試者如給出上面的方案,這說明或者他是一個匯編語言程序員(這也許是好事)或者他是一個想進入新領域的BASIC/FORTRAN程序員。

數據聲明(Data declarations)

5. 用變量a給出下面的定義
a) 一個整型數(An integer)
b) 一個指向整型數的指針(A pointer to an integer)
c) 一個指向指針的的指針,它指向的指針是指向一個整型數(A pointer to a pointer to an integer)
d) 一個有10個整型數的數組(An array of 10 integers)
e) 一個有10個指針的數組,該指針是指向一個整型數的(An array of 10 pointers to integers)
f) 一個指向有10個整型數數組的指針(A pointer to an array of 10 integers)
g) 一個指向函數的指針,該函數有一個整型參數并返回一個整型數(A pointer to a function that takes an integer as an argument and returns an integer)
h) 一個有10個指針的數組,該指針指向一個函數,該函數有一個整型參數并返回一個整型數( An array of ten pointers to functions that take an integer argument and return an integer )

答案是:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer


人們經常聲稱這里有幾個問題是那種要翻一下書才能回答的問題,我同意這種說法。當我寫這篇文章時,為了確定語法的正確性,我的確查了一下書。
但是當我被面試的時候,我期望被問到這個問題(或者相近的問題)。因為在被面試的這段時間里,我確定我知道這個問題的答案。應試者如果不知道
所有的答案(或至少大部分答案),那么也就沒有為這次面試做準備,如果該面試者沒有為這次面試做準備,那么他又能為什么出準備呢?


Static

6. 關鍵字static的作用是什么?

這個簡單的問題很少有人能回答完全。在C語言中,關鍵字static有三個明顯的作用:
1). 在函數體,一個被聲明為靜態的變量在這一函數被調用過程中維持其值不變。
2). 在模塊內(但在函數體外),一個被聲明為靜態的變量可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問。它是一個本地的全局變量。
3). 在模塊內,一個被聲明為靜態的函數只可被這一模塊內的其它函數調用。那就是,這個函數被限制在聲明它的模塊的本地范圍內使用。
大多數應試者能正確回答第一部分,一部分能正確回答第二部分,同是很少的人能懂得第三部分。這是一個應試者的嚴重的缺點,因為他顯然不懂得本地化數據和代碼范圍的好處和重要性。


Const

7.關鍵字const是什么含意?
我只要一聽到被面試者說:“const意味著常數”,我就知道我正在和一個業余者打交道。去年Dan Saks已經在他的文章里完全概括了const的所有用法,因此ESP(譯者:Embedded Systems Programming)的每一位讀者應該非常熟悉const能做什么和不能做什么.如果你從沒有讀到那篇文章,只要能說出const意味著“只讀”就可以了。盡管這個答案不是完全的答案,但我接受它作為一個正確的答案。(如果你想知道更詳細的答案,仔細讀一下Saks的文章吧。)如果應試者能正確回答這個問題,我將問他一個附加的問題:下面的聲明都是什么意思?

const int a;
int const a;
const int *a;
int * const a;
int const * a const;

前兩個的作用是一樣,a是一個常整型數。第三個意味著a是一個指向常整型數的指針(也就是,整型數是不可修改的,但指針可以)。第四個意思a是一個指向整型數的常指針(也就是說,指針指向的整型數是可以修改的,但指針是不可修改的)。最后一個意味著a是一個指向常整型數的常指針(也就是說,指針指向的整型數是不可修改的,同時指針也是不可修改的)。如果應試者能正確回答這些問題,那么他就給我留下了一個好印象。順帶提一句,也許你可能會問,即使不用關鍵字const,也還是能很容易寫出功能正確的程序,那么我為什么還要如此看重關鍵字const呢?我也如下的幾下理由:
1). 關鍵字const的作用是為給讀你代碼的人傳達非常有用的信息,實際上,聲明一個參數為常量是為了告訴了用戶這個參數的應用目的。如果你曾花很多時間清理其它人留下的垃圾,你就會很快學會感謝這點多余的信息。(當然,懂得用const的程序員很少會留下的垃圾讓別人來清理的。)
2). 通過給優化器一些附加的信息,使用關鍵字const也許能產生更緊湊的代碼。
3). 合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數,防止其被無意的代碼修改。簡而言之,這樣可以減少bug的出現。

Volatile

8. 關鍵字volatile有什么含意 并給出三個不同的例子。

一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個例子:
1). 并行設備的硬件寄存器(如:狀態寄存器)
2). 一個中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)
3). 多線程應用中被幾個任務共享的變量
回答不出這個問題的人是不會被雇傭的。我認為這是區分C程序員和嵌入式系統程序員的最基本的問題。嵌入式系統程序員經常同硬件、中斷、RTOS等等打交道,所用這些都要求volatile變量。不懂得volatile內容將會帶來災難。
假設被面試者正確地回答了這是問題(嗯,懷疑這否會是這樣),我將稍微深究一下,看一下這家伙是不是直正懂得volatile完全的重要性。
1). 一個參數既可以是const還可以是volatile嗎?解釋為什么。
2). 一個指針可以是volatile 嗎?解釋為什么。
3). 下面的函數有什么錯誤:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1). 是的。一個例子是只讀的狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。
2). 是的。盡管這并不很常見。一個例子是當一個中服務子程序修該一個指向一個buffer的指針時。
3). 這段代碼的有個惡作劇。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由于*ptr指向一個volatile型參數,編譯器將產生類似下面的代碼:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}

位操作(Bit manipulation)

9. 嵌入式系統總是要用戶對變量或寄存器進行位操作。給定一個整型變量a,寫兩段代碼,第一個設置a的bit 3,第二個清除a 的bit 3。在以上兩個操作中,要保持其它位不變。

對這個問題有三種基本的反應
1). 不知道如何下手。該被面者從沒做過任何嵌入式系統的工作。
2). 用bit fields。Bit fields是被扔到C語言死角的東西,它保證你的代碼在不同編譯器之間是不可移植的,同時也保證了的你的代碼是不可重用的。我最近不幸看到Infineon為其較復雜的通信芯片寫的驅動程序,它用到了bit fields因此完全對我無用,因為我的編譯器用其它的方式來實現bit fields的。從道德講:永遠不要讓一個非嵌入式的家伙粘實際硬件的邊。
3). 用 #defines 和 bit masks 操作。這是一個有極高可移植性的方法,是應該被用到的方法。最佳的解決方案如下:
#define BIT3 (0x1<<3)
static int a;
void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}
一些人喜歡為設置和清除值而定義一個掩碼同時定義一些說明常數,這也是可以接受的。我希望看到幾個要點:說明常數、|=和&=~操作。

訪問固定的內存位置(Accessing fixed memory locations)

10. 嵌入式系統經常具有要求程序員去訪問某特定的內存位置的特點。在某工程中,要求設置一絕對地址為0x67a9的整型變量的值為0xaa66。編譯器是一個純粹的ANSI編譯器。寫代碼去完成這一任務。

這一問題測試你是否知道為了訪問一絕對地址把一個整型數強制轉換(typecast)為一指針是合法的。這一問題的實現方式隨著個人風格不同而不同。典型的類似代碼如下:
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;

一個較晦澀的方法是:
*(int * const)(0x67a9) = 0xaa55;

即使你的品味更接近第二種方案,但我建議你在面試時使用第一種方案。

中斷(Interrupts)

11. 中斷是嵌入式系統中重要的組成部分,這導致了很多編譯開發商提供一種擴展—讓標準C支持中斷。具代表事實是,產生了一個新的關鍵字__interrupt。下面的代碼就使用了__interrupt關鍵字去定義了一個中斷服務子程序(ISR),請評論一下這段代碼的。

__interrupt double compute_area (double radius)
{
??? double area = PI * radius * radius;
??? printf(" Area = %f", area);
??? return area;
}

這個函數有太多的錯誤了,以至讓人不知從何說起了:
1). ISR 不能返回一個值。如果你不懂這個,那么你不會被雇用的。
2). ISR 不能傳遞參數。如果你沒有看到這一點,你被雇用的機會等同第一項。
3). 在許多的處理器/編譯器中,浮點一般都是不可重入的。有些處理器/編譯器需要讓額處的寄存器入棧,有些處理器/編譯器就是不允許在ISR中做浮點運算。此外,ISR應該是短而有效率的,在ISR中做浮點運算是不明智的。
4). 與第三點一脈相承,printf()經常有重入和性能上的問題。如果你丟掉了第三和第四點,我不會太為難你的。不用說,如果你能得到后兩點,那么你的被雇用前景越來越光明了。

代碼例子(Code examples)
12 . 下面的代碼輸出是什么,為什么?

void foo(void)
{
??? unsigned int a = 6;
??? int b = -20;
??? (a+b > 6) puts("> 6") : puts("<= 6");
}

這個問題測試你是否懂得C語言中的整數自動轉換原則,我發現有些開發者懂得極少這些東西。不管如何,這無符號整型問題的答案是輸出是“>6”。原因是當表達式中存在有符號類型和無符號類型時所有的操作數都自動轉換為無符號類型。 因此-20變成了一個非常大的正整數,所以該表達式計算出的結果大于6。這一點對于應當頻繁用到無符號數據類型的嵌入式系統來說是豐常重要的。如果你答錯了這個問題,你也就到了得不到這份工作的邊緣。

13. 評價下面的代碼片斷:

unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1's complement of zero */

對于一個int型不是16位的處理器為說,上面的代碼是不正確的。應編寫如下:

unsigned int compzero = ~0;

這一問題真正能揭露出應試者是否懂得處理器字長的重要性。在我的經驗里,好的嵌入式程序員非常準確地明白硬件的細節和它的局限,然而PC機程序往往把硬件作為一個無法避免的煩惱。
到了這個階段,應試者或者完全垂頭喪氣了或者信心滿滿志在必得。如果顯然應試者不是很好,那么這個測試就在這里結束了。但如果顯然應試者做得不錯,那么我就扔出下面的追加問題,這些問題是比較難的,我想僅僅非常優秀的應試者能做得不錯。提出這些問題,我希望更多看到應試者應付問題的方法,而不是答案。不管如何,你就當是這個娛樂吧…



動態內存分配(Dynamic memory allocation)



14. 盡管不像非嵌入式計算機那么常見,嵌入式系統還是有從堆(heap)中動態分配內存的過程的。那么嵌入式系統中,動態分配內存可能發生的問題是什么?

這里,我期望應試者能提到內存碎片,碎片收集的問題,變量的持行時間等等。這個主題已經在ESP雜志中被廣泛地討論過了(主要是 P.J. Plauger, 他的解釋遠遠超過我這里能提到的任何解釋),所有回過頭看一下這些雜志吧!讓應試者進入一種虛假的安全感覺后,我拿出這么一個小節目:下面的代碼片段的輸出是什么,為什么?

char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");

這是一個有趣的問題。最近在我的一個同事不經意把0值傳給了函數malloc,得到了一個合法的指針之后,我才想到這個問題。這就是上面的代碼,該代碼的輸出是“Got a valid pointer”。我用這個來開始討論這樣的一問題,看看被面試者是否想到庫例程這樣做是正確。得到正確的答案固然重要,但解決問題的方法和你做決定的基本原理更重要些。

Typedef

15. Typedef 在C語言中頻繁用以聲明一個已經存在的數據類型的同義字。也可以用預處理器做類似的事。例如,思考一下下面的例子:
#define dPS struct s *
typedef struct s * tPS;

以上兩種情況的意圖都是要定義dPS 和 tPS 作為一個指向結構s指針。哪種方法更好呢?(如果有的話)為什么?
這是一個非常微妙的問題,任何人答對這個問題(正當的原因)是應當被恭喜的。答案是:typedef更好。思考下面的例子:
dPS p1,p2;
tPS p3,p4;

第一個擴展為
struct s * p1, p2;

上面的代碼定義p1為一個指向結構的指,p2為一個實際的結構,這也許不是你想要的。第二個例子正確地定義了p3 和p4 兩個指針。

晦澀的語法

16. C語言同意一些令人震驚的結構,下面的結構是合法的嗎,如果是它做些什么?
int a = 5, b = 7, c;
c = a+++b;

這個問題將做為這個測驗的一個愉快的結尾。不管你相不相信,上面的例子是完全合乎語法的。問題是編譯器如何處理它?水平不高的編譯作者實際上會爭論這個問題,根據最處理原則,編譯器應當能處理盡可能所有合法的用法。因此,上面的代碼被處理成:
c = a++ + b;
因此, 這段代碼持行后a = 6, b = 7, c = 12。
如果你知道答案,或猜出正確答案,做得好。如果你不知道答案,我也不把這個當作問題。我發現這個問題的最大好處是:這是一個關于代碼編寫風格,代碼的可讀性,代碼的可修改性的好的話題



任我行 2006-08-28 09:51 發表評論
]]>
KMP算法祥解http://www.shnenglu.com/oosky/archive/2006/07/06/9486.html任我行任我行Thu, 06 Jul 2006 06:02:00 GMThttp://www.shnenglu.com/oosky/archive/2006/07/06/9486.htmlhttp://www.shnenglu.com/oosky/comments/9486.htmlhttp://www.shnenglu.com/oosky/archive/2006/07/06/9486.html#Feedback38http://www.shnenglu.com/oosky/comments/commentRss/9486.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/9486.html閱讀全文

任我行 2006-07-06 14:02 發表評論
]]>
Boost安裝http://www.shnenglu.com/oosky/archive/2006/06/30/9243.html任我行任我行Fri, 30 Jun 2006 09:08:00 GMThttp://www.shnenglu.com/oosky/archive/2006/06/30/9243.htmlhttp://www.shnenglu.com/oosky/comments/9243.htmlhttp://www.shnenglu.com/oosky/archive/2006/06/30/9243.html#Feedback0http://www.shnenglu.com/oosky/comments/commentRss/9243.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/9243.html閱讀全文

任我行 2006-06-30 17:08 發表評論
]]>
難道C/C++是塊難啃的骨頭,這里落戶的兄弟太少了http://www.shnenglu.com/oosky/archive/2006/06/01/8022.html任我行任我行Thu, 01 Jun 2006 04:50:00 GMThttp://www.shnenglu.com/oosky/archive/2006/06/01/8022.htmlhttp://www.shnenglu.com/oosky/comments/8022.htmlhttp://www.shnenglu.com/oosky/archive/2006/06/01/8022.html#Feedback21http://www.shnenglu.com/oosky/comments/commentRss/8022.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/8022.html在Cppblog落戶的兄弟怎么還那么少呢?

看這首頁中的統計信息,少的可憐。1K用戶都不到,.NET區的一個零頭都不到。
  • 博客 - 822
  • 隨筆 - 2917
  • 文章 - 1217
  • 評論 - 2784
再來看看.NET區:
  • 博客 - 15618
  • 隨筆 - 108752
  • 文章 - 34156
  • 評論 - 208492
看來大伙得想想辦法能熱鬧點。
dudu也來想想辦法。


任我行 2006-06-01 12:50 發表評論
]]>
推薦兩款瀏覽器-FireFox&Maxthonhttp://www.shnenglu.com/oosky/archive/2006/04/30/6498.html任我行任我行Sun, 30 Apr 2006 11:05:00 GMThttp://www.shnenglu.com/oosky/archive/2006/04/30/6498.htmlhttp://www.shnenglu.com/oosky/comments/6498.htmlhttp://www.shnenglu.com/oosky/archive/2006/04/30/6498.html#Feedback3http://www.shnenglu.com/oosky/comments/commentRss/6498.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/6498.html閱讀全文

任我行 2006-04-30 19:05 發表評論
]]>
打造UltralEdit-32為C/C++編譯器http://www.shnenglu.com/oosky/archive/2006/04/20/5936.html任我行任我行Thu, 20 Apr 2006 06:24:00 GMThttp://www.shnenglu.com/oosky/archive/2006/04/20/5936.htmlhttp://www.shnenglu.com/oosky/comments/5936.htmlhttp://www.shnenglu.com/oosky/archive/2006/04/20/5936.html#Feedback16http://www.shnenglu.com/oosky/comments/commentRss/5936.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/5936.html閱讀全文

任我行 2006-04-20 14:24 發表評論
]]>
VC6 + ICC8.1+...VC6與VS2005媲美http://www.shnenglu.com/oosky/archive/2006/04/10/5255.html任我行任我行Mon, 10 Apr 2006 11:22:00 GMThttp://www.shnenglu.com/oosky/archive/2006/04/10/5255.htmlhttp://www.shnenglu.com/oosky/comments/5255.htmlhttp://www.shnenglu.com/oosky/archive/2006/04/10/5255.html#Feedback28http://www.shnenglu.com/oosky/comments/commentRss/5255.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/5255.html下面自己去體驗吧!
  閱讀全文

任我行 2006-04-10 19:22 發表評論
]]>
華為軟件編程規范和范例http://www.shnenglu.com/oosky/archive/2006/03/26/4625.html任我行任我行Sun, 26 Mar 2006 09:04:00 GMThttp://www.shnenglu.com/oosky/archive/2006/03/26/4625.htmlhttp://www.shnenglu.com/oosky/comments/4625.htmlhttp://www.shnenglu.com/oosky/archive/2006/03/26/4625.html#Feedback13http://www.shnenglu.com/oosky/comments/commentRss/4625.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/4625.html閱讀全文

任我行 2006-03-26 17:04 發表評論
]]>
匯編語言超濃縮教程http://www.shnenglu.com/oosky/archive/2006/02/20/3354.html任我行任我行Mon, 20 Feb 2006 08:17:00 GMThttp://www.shnenglu.com/oosky/archive/2006/02/20/3354.htmlhttp://www.shnenglu.com/oosky/comments/3354.htmlhttp://www.shnenglu.com/oosky/archive/2006/02/20/3354.html#Feedback4http://www.shnenglu.com/oosky/comments/commentRss/3354.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/3354.html    “ 哎喲,哥們兒,還搗鼓匯編呢?那東西沒用,兄弟用VB"釣"一個API就夠你忙活個十天半月的,還不一定搞出來。”此君之言倒也不虛,那吾等還有無必要研他一究呢?(廢話,當然有啦!要不然你寫這篇文章干嘛。)別急,別急,讓我把這個中原委慢慢道來:一、所有電腦語言寫出的程序運行時在內存中都以機器碼方式存儲,機器碼可以被比較準確的翻譯成匯編語言,這是因為匯編語言兼容性最好,故幾乎所有跟蹤、調試工具(包括WIN95/98下)都是以匯編示人的,如果閣下對CRACK頗感興趣……;二、匯編直接與硬件打交道,如果你想搞通程序在執行時在電腦中的來龍去脈,也就是搞清電腦每個組成部分究竟在干什么、究竟怎么干?一個真正的硬件發燒友,不懂這些可不行。三、如今玩DOS的多是“高手”,如能像吾一樣混入(我不是高手)“高手”內部,不僅可以從“高手”朋友那兒套些黑客級“機密”,還可以自詡“高手”盡情享受強烈的虛榮感--#$%& “醒醒!”

  對初學者而言,匯編的許多命令太復雜,往往學習很長時間也寫不出一個漂漂亮亮的程序,以致妨礙了我們學習匯編的興趣,不少人就此放棄。所以我個人看法學匯編,不一定要寫程序,寫程序確實不是匯編的強項,大家不妨玩玩DEBUG,有時CRACK出一個小軟件比完成一個程序更有成就感(就像學電腦先玩游戲一樣)。某些高深的指令事實上只對有經驗的匯編程序員有用,對我們而言,太過高深了。為了使學習匯編語言有個好的開始,你必須要先排除那些華麗復雜的命令,將注意力集中在最重要的幾個指令上(CMP LOOP MOV JNZ……)。但是想在啰里吧嗦的教科書中完成上述目標,談何容易,所以本人整理了這篇超濃縮(用WINZIP、WINRAR…依次壓迫,嘿嘿!)教程。大言不慚的說,看通本文,你完全可以“不經意”間在前輩或是后生賣弄一下DEBUG,很有成就感的,試試看!那么――這個接下來呢?―― Here we go!(閱讀時看不懂不要緊,下文必有分解)

  因為匯編是通過CPU和內存跟硬件對話的,所以我們不得不先了解一下CPU和內存:(關于數的進制問題在此不提)

  CPU是可以執行電腦所有算術╱邏輯運算與基本 I/O 控制功能的一塊芯片。一種匯編語言只能用于特定的CPU。也就是說,不同的CPU其匯編語言的指令語法亦不相同。個人電腦由1981年推出至今,其CPU發展過程為:8086→80286→80386→80486→PENTIUM →……,還有AMD、CYRIX等旁支。后面兼容前面CPU的功能,只不過多了些指令(如多能奔騰的MMX指令集)、增大了寄存器(如386的32位EAX)、增多了寄存器(如486的FS)。為確保匯編程序可以適用于各種機型,所以推薦使用8086匯編語言,其兼容性最佳。本文所提均為8086匯編語言。寄存器(Register)是CPU內部的元件,所以在寄存器之間的數據傳送非常快。用途:1.可將寄存器內的數據執行算術及邏輯運算。2.存于寄存器內的地址可用來指向內存的某個位置,即尋址。3.可以用來讀寫數據到電腦的周邊設備。8086 有8個8位數據寄存器,這些8位寄存器可分別組成16位寄存器:AH&AL=AX:累加寄存器,常用于運算;BH&BL=BX:基址寄存器,常用于地址索引;CH&CL=CX:計數寄存器,常用于計數;DH&DL=DX:數據寄存器,常用于數據傳遞。為了運用所有的內存空間,8086設定了四個段寄存器,專門用來保存段地址:CS(Code Segment):代碼段寄存器;DS(Data Segment):數據段寄存器;SS(Stack Segment):堆棧段寄存器;ES(Extra Segment):附加段寄存器。當一個程序要執行時,就要決定程序代碼、數據和堆棧各要用到內存的哪些位置,通過設定段寄存器 CS,DS,SS 來指向這些起始位置。通常是將DS固定,而根據需要修改CS。所以,程序可以在可尋址空間小于64K的情況下被寫成任意大小。 所以,程序和其數據組合起來的大小,限制在DS 所指的64K內,這就是COM文件不得大于64K的原因。8086以內存做為戰場,用寄存器做為軍事基地,以加速工作。除了前面所提的寄存器外,還有一些特殊功能的寄存器:IP(Intruction Pointer):指令指針寄存器,與CS配合使用,可跟蹤程序的執行過程;SP(Stack Pointer):堆棧指針,與SS配合使用,可指向目前的堆棧位置。BP(Base Pointer):基址指針寄存器,可用作SS的一個相對基址位置;SI(Source Index):源變址寄存器可用來存放相對于DS段之源變址指針;DI(Destination Index):目的變址寄存器,可用來存放相對于 ES 段之目的變址指針。還有一個標志寄存器FR(Flag Register),有九個有意義的標志,將在下文用到時詳細說明。

  內存是電腦運作中的關鍵部分,也是電腦在工作中儲存信息的地方。內存組織有許多可存放數值的儲存位置,叫“地址”。8086地址總線有20位,所以CPU擁有達1M的尋址空間,這也是DOS的有效控制范圍,而8086能做的運算僅限于處理16位數據,即只有0到64K,所以,必須用分段尋址才能控制整個內存地址。完整的20位地址可分成兩部份:1.段基址(Segment):16位二進制數后面加上四個二進制0,即一個16進制0,變成20位二進制數,可設定1M中任何一個64K段,通常記做16位二進制數;2.偏移量(Offset):直接使用16位二進制數,指向段基址中的任何一個地址。如:2222(段基址):3333(偏移量),其實際的20位地址值為:25553。除了上述營養要充分吸收外,你還要知道什么是DOS、BIOS功能調用,簡單的說,功能調用類似于WIN95 API,相當于子程序。匯編寫程序已經夠要命了,如果不用MS、IBM的子程序,這日子真是沒法過了(關于功能調用詳見《電腦愛好者》98年11期)。

  編寫匯編語言有兩種主要的方法:1.使用MASM或TASM等編譯器;2.使用除錯程序DEBUG.COM。DEBUG其實并不能算是一個編譯器,它的主要用途在于除錯,即修正匯編程序中的錯誤。不過,也可以用來寫短的匯編程序,尤其對初學者而言,DEBUG 更是最佳的入門工具。因為DEBUG操作容易:只要鍵入DEBUG回車,A回車即可進行匯編,過程簡單,而使用編譯器時,必須用到文本編輯器、編譯器本身、LINK以及EXE2BIN等程序,其中每一個程序都必須用到一系列相當復雜的命令才能工作,而且用編譯器處理源程序,必須加入許多與指令語句無關的指示性語句,以供編譯器識別,使用 DEBUG 可以避免一開始就碰到許多難以理解的程序行。DEBUG 除了能夠匯編程序之外,還可用來檢查和修改內存位置、載入儲存和執行程序、以及檢查和修改寄存器,換句話說,DEBUG是為了讓我們接觸硬件而設計的。(8086常用指令用法將在每個匯編程序中講解,限于篇幅,不可能將所有指令列出)。

  DEBUG的的A命令可以匯編出簡單的COM文件,所以DEBUG編寫的程序一定要由地址 100h(COM文件要求)開始才合法。FOLLOW ME,SETP BY SETP(步步回車):

  輸入 A100 ; 從DS:100開始匯編
  2.輸入 MOV DL,1 ; 將數值 01h 裝入 DL 寄存器
  3.輸入 MOV AH,2 ; 將數值 02h 裝入 DL 寄存器
  4.輸入 INT 21 ; 調用DOS 21號中斷2號功能,用來逐個顯示裝入DL的字符
  5.輸入 INT 20 ; 調用DOS 20號中斷,終止程序,將控制權交回給 DEBUG
  6.請按 Enter 鍵
  7.現在已將匯編語言程序放入內存中了,輸入 G(運行)
  8.出現結果:輸出一個符號。
  ㄖ ←輸出結果其實不是它,因WORD97無法顯示原結果,故找一贗品將就著。
  Program terminated normally

  我們可以用U命令將十六進制的機器碼反匯編(Unassemble)成匯編指令。你將發現每一行右邊的匯編指令就是被匯編成相應的機器碼,而8086實際上就是以機器碼來執行程序。
  1.輸入 U100,106
  1FED:0100 B201 MOV DL,01
  1FED:0102 B402 MOV AH,02
  1FED:0104 CD21 INT 21
  1FED:0106 CD20 INT 20
  DEBUG可以用R命令來查看、改變寄存器內容。CS:IP寄存器,保存了將執行指令地址。
  1.輸入R
  AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
  DS=1FED ES=1FED SS=1FED CS=1FED IP=0100 NV UP EI PL NZ NA PO NC
  1FED:0100 B201 MOV DL,01

  當程序由DS:100開始執行,那么終止程序時,DEBUG會自動將IP內容重新設定為100。當你要將此程序做成一個獨立的可執行文件,則可以用N命令對該程序命名。但一定要為COM文件,否則無法以DEBUG載入。
  輸入N SMILE.COM ;我們得告訴DEBUG程序長度:程序從100開始到106,故占用7
  ;字節。我們利用BX存放長度值高位部分,而以CX存放低位部分。
  2.輸入RBX ;查看 BX 寄存器的內容,本程序只有7個字節,故本步可省略
  3.輸入 RCX  ;查看 CX 寄存器的內容
  4.輸入 7  ;程序的字節數
  5.輸入 W ;用W命令將該程序寫入(Write)磁盤中

  修行至此,我們便可以真正接觸8086匯編指令了。 當我們寫匯編語言程序的時候,通常不會直接將機器碼放入內存中,而是打入一串助記符號(Mnemonic Symbols),這些符號比十六進制機器碼更容易記住,此之謂匯編指令。助記符號,告訴CPU應執行何種運算。 也就是說,助憶符號所構成的匯編語言是為人設計的,而機器語言是對PC設計的。

  現在,我們再來剖析一個可以將所有ASCII碼顯示出來的程序。
  1. 輸入 DEBUG
  2. 輸入 A100
  3.輸入 MOV CX,0100 ;裝入循環次數
  MOV DL,00 ;裝入第一個ASCII碼,隨后每次循環裝入新碼
  MOV AH,02
  INT 21
  INC DL ;INC:遞增指令,每次將數據寄存器 DL 內的數值加 1
  LOOP 0105 ;LOOP:循環指令,每執行一次LOOP,CX值減1,并跳
  ;到循環的起始地址105,直到CX為0,循環停止
  INT 20
  4.輸入 G即可顯示所有ASCII碼
 
  當我們想任意顯示字符串,如:UNDERSTAND?,則可以使用DOS21H號中斷9H號功能。輸入下行程序,存盤并執行看看:
  1.輸入 A100
   MOV DX,109 ;DS:DX = 字符串的起始地址
   MOV AH,9 ;DOS的09h功能調用
  INT 21 ;字符串輸出
  INT 20
  DB 'UNDERSTAND?$';定義字符串

  在匯編語言中,有兩種不同的指令:1.正規指令:如 MOV 等,是屬于CPU的指令,用來告訴CPU在程序執行時應做些什么,所以它會以運算碼(OP-code)的方式存入內存中;2.偽指令:如DB等,是屬于DEBUG等編譯器的指令,用來告訴編譯器在編譯時應做些什么。DB(Define Byte)指令用來告訴DEBUG 將單引號內的所有ASCII 碼放入內存中。使用 9H 功能的字符串必須以$結尾。用D命令可用來查看DB偽指令將那些內容放入內存。
  6.輸入 D100
  1975:0100 BA 09 01 B4 09 CD 21 CD-20 75 6E 64 65 72 73 74 ......!. underst
  1975:0110 61 6E 64 24 8B 46 F8 89-45 04 8B 46 34 00 64 19 and$.F..E..F4.d.
  1975:0120 89 45 02 33 C0 5E 5F C9-C3 00 C8 04 00 00 57 56 .E.3.^_.......WV
  1975:0130 6B F8 0E 81 C7 FE 53 8B-DF 8B C2 E8 32 FE 0B C0 k.....S.....2...
  1975:0140 74 05 33 C0 99 EB 17 8B-45 0C E8 D4 97 8B F0 89 t.3.....E.......
  1975:0150 56 FE 0B D0 74 EC 8B 45-08 03 C6 8B 56 FE 5E 5F V...t..E....V.^_
  1975:0160 C9 C3 C8 02 00 00 6B D8-0E 81 C3 FE 53 89 5E FE ......k.....S.^.
  1975:0170 8B C2 E8 FB FD 0B C0 75-09 8B 5E FE 8B 47 0C E8 .......u..^..G..

  現在,我們來剖析另一個程序:由鍵盤輸入任意字符串,然后顯示出來。db 20指示DEBUG保留20h個未用的內存空間供緩沖區使用。
  輸入A100
   MOV DX,0116 ;DS:DX = 緩沖區地址,由DB偽指令確定緩沖區地址
  MOV AH,0A ;0Ah 號功能調用
  INT 21 ;鍵盤輸入緩沖區
  MOV DL,0A ;由于功能Ah在每個字符串最后加一個歸位碼(0Dh由 Enter
  MOV AH,02 ;產生),使光標自動回到輸入行的最前端,為了使新輸出的
  INT 21 ;字符串不會蓋掉原來輸入的字符串,所以利用功能2h加一
  ;個換行碼(OAh),使得光標移到下一行的的最前端。
  MOV DX,0118 ;裝入字符串的起始位置
  MOV AH,09 ;9h功能遇到$符號才會停止輸出,故字符串最后必須加上
  INT 21 ;$,否則9h功能會繼續將內存中的無用數據胡亂顯示出來
  INT 20
  DB 20 ;定義緩沖區
  送你一句話:學匯編切忌心浮氣燥。

  客套話就不講了。工欲善其事,必先利其器。與其說DEBUG 是編譯器,倒不如說它是“直譯器”,DEBUG的A命令只可將一行匯編指令轉成機器語言,且立刻執行。真正編譯器(MASM)的運作是利用文本編輯器(EDIT等)將匯編指令建成一個獨立且附加名為.ASM的文本文件,稱源程序。它是MASM 程序的輸入部分。MASM將輸入的ASM文件,編譯成.OBJ文件,稱為目標程序。OBJ文件僅包含有關程序各部份要載入何處及如何與其他程序合并的信息,無法直接載入內存執行。鏈結程序LINK則可將OBJ文件轉換成可載入內存執行(EXEcute)的EXE文件。還可以用EXE2BIN,將符合條件的EXE文件轉成COM文件(COM 文件不但占用的內存最少,而且運行速度最快)。
  下面我們用MASM寫一個與用DEBUG寫的第一個程序功能一樣的程序。
  用EDIT編輯一個SMILE.ASM的源程序文件。
  源程序 DEBUG 程序
  prognam segment
  assume cs:prognam
  org 100h A100
  mov dl,1 mov dl,1
  mov ah,2 mov ah,2
  int 21h int 21
  int 20h int 20
  prognam ends
  end

  比較一下:1.因為MASM會將所有的數值假設為十進制,而DEBUG則只使用十六進制,所以在源程序中,我們必須在有關數字后加上代表進制的字母,如H代表十六進制,D代表十進制。若是以字母開頭的十六進制數字,還必須在字母前加個0,以表示它是數,如0AH。2.源程序增加五行敘述:prognam segment 與 prognam ends 是成對的,用來告訴 MASM 及LINK,此程序將放在一個稱為PROGNAM(PROGram NAMe)的程序段內,其中段名(PROGNAM)可以任取,但其位置必須固定。assume cs:prognam 必須在程序的開頭,用來告訴編譯器此程序所在段的位置放在CS寄存器中。end用來告訴MASM,程序到此結束, ORG 100H作用相當于DEBUG的A100,從偏移量100開始匯編。COM 文件的所有源程序都必須包含這五行,且必須依相同的次序及位置出現,這點東西記下就行,千篇一律。接著,我們用MASM編譯SMILE.ASM。
  輸入 MASM SMILE ←不用打入附加名.ASM。
  Microsoft (R) Macro Assembler Version 5.10
  Copyright (C) Microsoft Corp 1981, 1988. All rights reserved.
  Object filename [SMILE.OBJ]: ←是否改動輸出OBJ文件名,如不改就ENTER
  Source listing [NUL.LST]: ← 是否需要列表文件(LST),不需要就ENTER
  Cross-reference [NUL.CRF]: ←是否需要對照文件(CRF),不需要則ENTER
  50162 + 403867 Bytes symbol space free
  0 Warning Errors ←警告錯誤,表示編譯器對某些語句不理解,通常是輸入錯誤。
  0 Severe Errors ←嚴重錯誤,會造成程序無法執行,通常是語法結構錯誤。

  如果沒有一個錯誤存在,即可生成OBJ文件。OBJ中包含的是編譯后的二進制結果,它還無法被 DOS載入內存中加以執行,必須加以鏈結(Linking)。以LINK將OBJ文件(SMILE.OBJ)鏈結成 EXE 文件(SMILE.EXE)時,。
  1.輸入 LINK SMILE ←不用附加名OBJ
  Microsoft (R) Overlay Linker Version 3.64
  Copyright (C) Microsoft Corp 1981, 1988. All rights reserved.
  Run File [SMILE.EXE]: ← 是否改動輸出EXE文件名,如不改就ENTER
  List File [NUL.MAP]: ← 是否需要列表文件(MAP),不需要則ENTER
  Libraries [.LIB]: ←是否需要庫文件,要就鍵入文件名,不要則ENTER
  LINK : warning L4021: no stack segment← 由于COM文件不使用堆棧段,所以錯誤信息
  ←"no stack segment"并不影響程序正常執行

  至此已經生成EXE文件,我們還須使用EXE2BIN 將EXE文件(SMILE.EXE),轉換成COM文件(SMILE.COM)。輸入EXE2BIN SMILE產生 BIN 文件(SMILE.BIN)。其實 BIN 文件與 COM 文件是完全相同的,但由于DOS只認COM、EXE及BAT文件,所以BIN文件無法被正確執行,改名或直接輸入 EXE2BIN SMILE SMILE.COM即可。現在,磁盤上應該有 SMILE.COM 文件了,你只要在提示符號C:>下,直接輸入文件名稱 SMILE ,就可以執行這個程序了。

  你是否覺得用編譯器產生程序的方法,比 DEBUG 麻煩多了!以小程序而言,的確是如此,但對于較大的程序,你就會發現其優點了。我們再將ASCII程序以編譯器方式再做一次,看看有無差異。首先,用EDIT.COM建立 ASCII.ASM 文件。
  prognam segment ;定義段
  assume cs:prognam ;把上面定義段的段基址放入 CS
  mov cx,100h ; 裝入循環次數
  mov dl,0 ; 裝入第一個ASCII碼,隨后每次循環裝入新碼
  next: mov ah,2
   int 21h
   inc dl ;INC:遞增指令,每次將數據寄存器 DL 內的數值加 1
  loop next ; 循環指令,執行一次,CX減1,直到CX為0,循環停止
  int 20h
   prognam ends ;段終止
  end ;匯編終止
  在匯編語言的源程序中,每一個程序行都包含三項元素:
    start: mov dl,1 ;裝入第一個ASCII碼,隨后每次循環裝入新碼
    標識符 表達式 注解

  在原始文件中加上注解可使程序更易理解,便于以后參考。每行注解以“;”與程序行分離。編譯器對注解不予理會,注解的數據不會出現在OBJ、EXE或COM文件中。由于我們在寫源程序時,并不知道每一程序行的地址,所以必須以符號名稱來代表相對地址,稱為“標識符”。我們通常在適當行的適當位置上,鍵入標識符。標識符(label)最長可達31 個字節,因此我們在程序中,盡量以簡潔的文字做為標識符。現在,你可以將此ASCII.ASM 文件編譯成 ASCII.COM 了。1.MASM ASCII,2.LINK ASCII,3.EXE2BIN ASCII ASCII.COM。

  注意:當你以編譯器匯編你設計的程序時,常會發生打字錯誤、標識符名稱拼錯、十六進制數少了h、邏輯錯誤等。匯編老手常給新人的忠告是:最好料到自己所寫的程序一定會有些錯誤(別人告訴我的);如果第一次執行程序后,就得到期望的結果,你最好還是在檢查一遍,因為它可能是錯的。原則上,只要大體的邏輯架構正確,查找程序中錯誤的過程,與寫程序本身相比甚至更有意思。寫大程序時,最好能分成許多模塊,如此可使程序本身的目的較單純,易于撰寫與查錯,另外也可讓程序中不同部份之間的界限較清楚,節省編譯的時間。如果讀程序有讀不懂的地方最好用紙筆記下有關寄存器、內存等內容,在紙上慢慢比劃,就豁然開朗了。   下面我們將寫一個能從鍵盤取得一個十進制的數值,并將其轉換成十六進制數值而顯示于屏幕上的“大程序”。前言:要讓8086執行這樣的功能,我們必須先將此問題分解成一連串的步驟,稱為程序規劃。首先,以流程圖的方式,來確保整個程序在邏輯上沒有問題(不用說了吧!什么語言都要有此步驟)。這種模塊化的規劃方式,稱之為“由上而下的程序規劃”。而在真正寫程序時,卻是從最小的單位模塊(子程序)開始,當每個模塊都完成之后,再合并成大程序;這種大處著眼,小處著手的方式稱為“由下而上的程序設計”。

  我們的第一個模塊是BINIHEX,其主要用途是從8086的BX寄存器中取出二進制數,并以十六進制方式顯示在屏幕上。注意:子程序如不能獨立運行,實屬正常。
   binihex segment
   assume cs:binihex
  mov ch,4 ;記錄轉換后的十六進制位數(四位)
  rotate: mov cl,4 ;利用CL當計數器,記錄寄存器數位移動次數
  rol bx,cl ;循環寄存器BX的內容,以便依序處理4個十六進制數
  mov al,bl ;把bx低八位bl內數據轉移至al
  and al,0fh ;把無用位清零
  add al,30h ;把AL內數據加30H,并存入al
  cmp al,3ah ;與3ah比較
  jl printit ;小于3ah則轉移
  add al,7h ;把AL內數據加30H,并存入al
  printit:mov dl,al ;把ASCII碼裝入DL
  mov ah,2
   int 21h
   dec ch ;ch減一,減到零時,零標志置1
  jnz rotate ;JNZ:當零標志未置1,則跳到指定地址。即:不等,則轉移
  int 20h ;從子程序退回主程序
  binihex ends
   end

  利用循環左移指令ROL循環寄存器BX(BX內容將由第二個子程序提供)的內容,以便依序處理4個十六進制數:1. 利用CL當計數器,記錄寄存器移位的次數。2.將BX的第一個十六進制值移到最右邊。利用 AND (邏輯“與”運算:對應位都為1時,其結果為1,其余情況為零)把不要的部份清零,得到結果:先將BL值存入AL中,再利用AND以0Fh(00001111)將AL的左邊四位清零。由于0到9的ASCII碼為30h到39h,而A到F之ASCII碼為41h到46h,間斷了7h,所以得到結果:若AL之內容小于3Ah,則AL值只加30h,否則AL再加7h。ADD指令會將兩個表達式相加,其結果存于左邊表達式內。標志寄存器(Flag Register)是一個單獨的十六位寄存器,有9個標志位,某些匯編指令(大部份是涉及比較、算術或邏輯運算的指令)執行時,會將相關標志位置1或清0, 常碰到的標志位有零標志(ZF)、符號標志(SF)、溢出標志(OF)和進位標志(CF)。 標志位保存了某個指令執行后對它的影響,可用其他相關指令,查出標志的狀態,根據狀態產生動作。CMP指令很像減法,是將兩個表達式的值相減,但寄存器或內存的內容并未改變,只是相對的標志位發生改變而已:若 AL 值小于 3Ah,則正負號標志位會置0,反之則置1。 JL指令可解釋為:小于就轉移到指定位置,大于、等于則向下執行。CMP和JG 、JL等條件轉移指令一起使用,可以形成程序的分支結構,是寫匯編程序常用技巧。

  第二個模塊DECIBIN 用來接收鍵盤打入的十進制數,并將它轉換成二進制數放于BX 寄存器中,供模塊1 BINIHEX使用。
  decibin segment
  assume cs:decibin
  mov bx,0 ;BX清零
  newchar:mov ah,1 ;
  int 21h ;讀一個鍵盤輸入符號入al,并顯示
  sub al,30h ;al減去30H,結果存于al中,完成ASCII碼轉二進制碼
  jl exit ;小于零則轉移
  cmp al,9d
   jg exit ;左>右則轉移
  cbw ;8位al轉換成16位ax
  xchg ax,bx ;互換ax和bx內數據
  mov cx,10d ;十進制數10入cx
  mul cx ;表達式的值與ax內容相乘,并將結果存于ax
  xchg ax,bx
   add bx,ax
   jmp newchar ;無條件轉移
  exit: int 20 ;回主程序
  decibin ends
   end
  CBW 實際結果是:若AL中的值為正,則AH填入00h;反之,則AH填入FFh。XCHG常用于需要暫時保留某個寄存器中的內容時。
  當然,還得一個子程序(CRLF)使后顯示的十六進制數不會蓋掉先輸入的十進制數。
  crlf segment
  assume cs:crlf
  mov dl,0dh ;回車的ASCII碼0DH入DL
  mov ah,2
   int 21h
   mov dl,0ah ;換行的ASSII碼0AH入AH
  mov ah,2
   int 21h
   int 20 ;回主程序
  crlf ends
  end

  現在我們就可以將BINIHEX、DECIBIN及CRLF等模塊合并成一個大程序了。首先,我們要將這三個模塊子程序略加改動。然后,再寫一段程序來調用每一個子程序。
  crlf proc near;
  mov dl,0dh
  mov ah,2
  int 21h
  mov dl,0ah
  mov ah,2
  int 21h
  ret
  crlf endp

  類似SEGMENT與ENDS的偽指令,PROC與ENDP也是成對出現,用來識別并定義一個程序。其實,PROC 真正的作用只是告訴編譯器:所調用的程序是屬于近程(NEAR)或遠程(FAR)。 一般的程序是由 DEBUG 直接調用的,所以用 INT 20 返回,用 CALL 指令所調用的程序則改用返回指令RET,RET會把控制權轉移到棧頂所指的地址,而該地址是由調用此程序的 CALL指令所放入的。
  各模塊都搞定了,然后我們把子程序組合起來就大功告成
  decihex segment ;主程序
  assume cs:decihex
  org 100h
  mov cx,4 ;循環次數入cx;由于子程序要用到cx,故子程序要將cx入棧
  repeat: call decibin;調用十進制轉二進制子程序
  call crlf ;調用添加回、換行符子程序
  call binihex ;調用二進制轉十六進制并顯示子程序
  call crlf
  loop repeat ;循環4次,可連續運算4次
  mov ah,4ch ; 調用DOS21號中斷4c號功能,退出程序,作用跟INT 20H
  int 21H ; 一樣,但適用面更廣,INT20H退不出時,試一下它
  decibin proc near push cx ;將cx壓入堆棧,;
  ┇ exit: pop cx ;將cx還原; retdecibin endp binihex proc near push cx
  ┇ pop cx retbinihex endp crlf proc near
   push cx
  ┇ pop cx retcrlf endpdecihex ends end

  CALL指令用來調用子程序,并將控制權轉移到子程序地址,同時將CALL的下行一指令地址定為返回地址,并壓入堆棧中。CALL 可分為近程(NEAR)及遠程(FAR)兩種:1.NEAR:IP的內容被壓入堆棧中,用于程序與程序在同一段中。2.FAR:CS 、IP寄存器的內容依次壓入堆棧中,用于程序與程序在不同段中。PUSH、POP又是一對指令用于將寄存器內容壓入、彈出,用來保護寄存器數據,子程序調用中運用較多。堆棧指針有個“后進先出”原則,像PUSH AX,PUSH BX…POP BX,POP AX這樣才能作到保護數據絲毫不差。

  匯編語言超濃縮教程到這要告一段落了,希望能奠定你獨立設計的基礎。而更多更好的技巧則全依賴你平時的積累了。祝你成功!



任我行 2006-02-20 16:17 發表評論
]]>
關于在8086/88內存尋址方式http://www.shnenglu.com/oosky/archive/2006/02/20/3353.html任我行任我行Mon, 20 Feb 2006 08:15:00 GMThttp://www.shnenglu.com/oosky/archive/2006/02/20/3353.htmlhttp://www.shnenglu.com/oosky/comments/3353.htmlhttp://www.shnenglu.com/oosky/archive/2006/02/20/3353.html#Feedback0http://www.shnenglu.com/oosky/comments/commentRss/3353.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/3353.html 關于在8086/88內存尋址方式

?Writer:HSLY
?Excerpt:80x86匯編小站 ?Preface:
在學匯編時,很多初學者對PC的尋址方式和很不理解...... ?Content:

??? 在學匯編時,很多初學者對PC的尋址方式和很不理解,甚至是很難理解。的確,這方面的知識是很抽象的,需要比較強的空間想象能力。尤其是我們在輸入字符串時,那這些字符是如何進行排列的呢?對于,這個問題,我相信很多初學者也是很難想象是如何排列。但是,我可以這樣比喻:內存就是有很多棟“樓房”,“樓房”又是由“單元號”,“門戶號”組成,那“樓房”就相當于內存地址的段地址,“單元號”就相當于內存的的 偏移地址,“門戶號(家)”就相當于“變地址”,而每個單元有16個"門戶號(家)",又當我們找到"門戶號(家)"后,走進這個"門戶號(家)"就會見到里面會有"人",而我們所說的人就是寄存器所指的"內容"了,我畫個圖給你們看就會一目了然了。


用DEBUG的D命令得出這樣的效果:

 


|---------->0B1F就是"樓房"------>段地址
|
|???? |------>右邊的就是"單元號"--->偏移地址
|???? |
|???? |??????????? |-------->這部分就是"門戶號"----->變地址
|???? |????????? |<------------------------------------------>|
0B1F:0100 00 80 FF 02 75 05 C6 46-00 00 C3 E8 8C EB B4 3B
0B1F:0110 CD 21 72 39 8B FA 33 C0-8B C8 49 26 34 00 0E 0B
'
'
'
[省略]

看完這個圖之后,是不是就很明了呢?但是聰明的人就會有疑問,那我們怎么走進"門戶號(家)"呢?問得好,所以了為了可以走進"門戶號(家)",就出現了一個叫做"尋址方式"的概念!說白了,就是教你如何找到這個"門戶號(家)".呵呵!

好現在都明白了嗎?那你們就看看我是怎么理解PC的尋址方式(通俗易懂):
在這我就只介紹比較難理解的:

1:寄存器直接尋址:
你就想成:其實你已經站在你要找的"門戶號(家)"面前了,直接敲門進去就OK了!
例子: MOV AX,[2000H]
MOV AX,2000H -->2000H為存放操作數單元號的符號地址
上面兩者是不等效的

2:寄存器間接尋址方式:
你就想成:你已經站在你要找的"門戶號(家)"的"單元號",你要找到它,必須知道它在當前"單元號"幾樓.假如它在6樓,那你就上到6樓就OK了!!注意,最高只有16樓,因為什么呢?那就用DEBUG的D命令看看呀,慢慢數哦,呵呵!!
例子: MOV AX,[BX]

計算公式: 物理地址=16d*(DS)+(BX)
物理地址=16d*(DS)+(SI)
物理地址=16d*(DS)+(DI)
物理地址=16d*(SS)+(BP)

3:寄存器相對尋址方式:
你就想成:你要找的"門戶號(家)"其實就在你家的樓上或者樓下,你要找到它,就 必須知道它在你樓上幾樓,或者在樓下幾樓!就OK了!
例子: MOV AX,COUNT[SI]
MOV AX,[COUNT+SI]
其中 COUNT為位移量的符號地址


計算公式: 物理地址=16d*(DS)+(BX)+8位位移量
或+(SI) 或 16位位偏移量
或+(DI)


物理地址=16d*(SS)+(BP)+8位偏移量


4:基址變址尋址方式:
你就想成:你要找的"門戶號(家)"是跟住在同一棟樓的不同"單元號",你要找到它,就必須知道它是該棟的哪個"單元號",并且住在幾樓!那樣你就可以找到它了 !
例子: MOV AX,[BX][DI]
MOV AX,[BX+DI]

計算公式: 物理地址=16d*(DS)+(BX)+(SI)
或+(DI)
物理地址=16d*(SS)+(BP)+(SI)
或+(DI)


5:相對基址變址尋址方式:
你就想成:你就想成:你要找的"門戶號(家)"是跟住在同一棟樓的不同"單元號",它比你高幾層樓或者低幾層樓,然后用的你目前的樓數+/-就可以得出你要找的住在幾樓了!
例子: MOV,AX,MASK[BX][SI]
MOV,AX,MASK[BX+SI]
MOV,AX,[MASK+BX+SI]
以上三個例子是等效的!!

計算公式: 物理地址=16d*(DS)+(BX)+(SI)+8位位移量
或+(DI) 或 16位位偏移量
物理地址=16d*(SS)+(BP)+(SI)+8位位移量
或+(DI) 或 16位位偏移量
---------------------------------------------------------------------
呵呵,終于寫完了這篇教程,好累哦!! 是不是覺得我的思維很另類呀,要創新呀!
書上太理論了,我就創新一個,不知道你們看得懂嗎?
呵呵,反正你們不要
!@##)(#$*!@(@我就行了,我很努力寫了!!!

下面,我舉個程序例子,讓你們加深印象!!!

----------------------------------------------------------------------
編程步驟:
1: 建立緩沖區,為輸入字符串(最多能輸入9個)
2: 取緩沖區的首地址,以便后面進行"寄存器間接尋址方式"
3: 利用"寄存器間接尋址方式"取得實際輸入字符個數,以便確認循環次數
4: 利用"寄存器間接尋址方式"輸入字符串的最后一個字符
5: 利用LOOP指令和2號顯示功能來進行倒著顯示
----------------------------------------------------------------------

;程序功能:任意輸入幾個字符(最多能輸入9個),按回車則倒著輸出!

data segment
user_string db 10,0,10 dup(?)
data ends
code segment
assume cs:code,ds:data
start: mov ax,data
mov ds,ax
lea dx,user_string ;建立輸入字符串緩沖區
mov ah,0ah
int 21h
xor si,si
xor bx,bx
mov bx,dx
mov cx,[bx+si+1] ;看這個就是"寄存器間接尋址方式"
xor ch,ch ;其目的就是取實際輸入字符個數
mov di,cx
lop: mov ah,2
mov dx,[bx+di+1];看這又是"寄存器間接尋址方式"
int 21h ;其目的就是取輸入字符串的最后一個字符
dec di
loop lop ;依次循環倒著輸出字符
mov ah,4ch
int 21h
code ends
end start

-----------------------------------------------------------------------
完工了



任我行 2006-02-20 16:15 發表評論
]]>
常去的網址http://www.shnenglu.com/oosky/archive/2006/02/13/3232.html任我行任我行Mon, 13 Feb 2006 06:14:00 GMThttp://www.shnenglu.com/oosky/archive/2006/02/13/3232.htmlhttp://www.shnenglu.com/oosky/comments/3232.htmlhttp://www.shnenglu.com/oosky/archive/2006/02/13/3232.html#Feedback4http://www.shnenglu.com/oosky/comments/commentRss/3232.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/3232.htmlhttp://www.netyi.net/in.asp?id=oosky 書籍很多,但要你自己細細的尋找哦。
http://www.infoxa.com/ 這個網站不錯,推薦。很多經典的圖書都能找到。
http://www.itebook.net/ 
http://www.itepub.net/ 這個站應該很多人都知道的。

Linux免費下載:
http://www.linuxeden.com/forum/t132533.html Mandriva Linux 2006 的光盤鏡像
http://public.planetmirror.com/pub/ 
http://www.linuxeden.com/download/1214.html fedora core linux
http://fedora.redhat.com/download/mirrors.html#ASIA  fedora core linux官方網
http://ftp.osuosl.org/pub/ 看了就知道了




貼這么多,等發現好的再貼出來,大家也共享一下。


任我行 2006-02-13 14:14 發表評論
]]>
堆棧,堆棧,堆和棧的區別http://www.shnenglu.com/oosky/archive/2006/01/21/2958.html任我行任我行Sat, 21 Jan 2006 08:23:00 GMThttp://www.shnenglu.com/oosky/archive/2006/01/21/2958.htmlhttp://www.shnenglu.com/oosky/comments/2958.htmlhttp://www.shnenglu.com/oosky/archive/2006/01/21/2958.html#Feedback32http://www.shnenglu.com/oosky/comments/commentRss/2958.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/2958.html看完你就明白了。
  閱讀全文

任我行 2006-01-21 16:23 發表評論
]]>
#pragma 預處理指令詳解http://www.shnenglu.com/oosky/archive/2006/01/06/2464.html任我行任我行Fri, 06 Jan 2006 06:33:00 GMThttp://www.shnenglu.com/oosky/archive/2006/01/06/2464.htmlhttp://www.shnenglu.com/oosky/comments/2464.htmlhttp://www.shnenglu.com/oosky/archive/2006/01/06/2464.html#Feedback0http://www.shnenglu.com/oosky/comments/commentRss/2464.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/2464.html閱讀全文

任我行 2006-01-06 14:33 發表評論
]]>
驅動開程序發—安裝 http://www.shnenglu.com/oosky/archive/2006/01/03/2370.html任我行任我行Tue, 03 Jan 2006 02:11:00 GMThttp://www.shnenglu.com/oosky/archive/2006/01/03/2370.htmlhttp://www.shnenglu.com/oosky/comments/2370.htmlhttp://www.shnenglu.com/oosky/archive/2006/01/03/2370.html#Feedback4http://www.shnenglu.com/oosky/comments/commentRss/2370.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/2370.html作為一個完整的例子,你開發出來驅動還必須要能安裝。所以下面我講一下安裝。

如果前面的編譯過程沒有錯誤的話,現在我們應該已經得到了一個HelloWDM.sys文件,假設它是放在D:\HelloWDM\objfre\i386中。

安裝WDM驅動程序可以用兩種方法,一種是利用注冊表,還有一種是利用INF文件。我們一般是采用INF文件(這是微軟推薦的)。INF文件可以在 WINNT\INF 目錄中找到很多。為了順利安裝,我在這里先給出 HelloWDM 所需要的 HelloWDM.INF 文件:

;; The Win2K DDK documentation contains an excellent INF reference.

;--------- Version Section ---------------------------------------------------

[Version]
Signature="$CHICAGO$"
Provider=LC_Device
DriverVer=8/21/2002,3.0.0.3

; If device fits one of the standard classes, use the name and GUID here,
; otherwise create your own device class and GUID as this example shows.

Class=Unknown
ClassGUID={ff646f80-8def-11d2-9449-00105a075f6b}

;--------- SourceDiskNames and SourceDiskFiles Section -----------------------

; These sections identify source disks and files for installation. They are
; shown here as an example, but commented out.

[SourceDisksNames]
1 = "HelloWDM",Disk1,,

[SourceDisksFiles]
HelloWDM.sys = 1,objfre\i386,

;--------- ClassInstall/ClassInstall32 Section -------------------------------

; Not necessary if using a standard class

; 9X Style
[ClassInstall]
Addreg=Class_AddReg

; NT Style
[ClassInstall32]
Addreg=Class_AddReg

[Class_AddReg]
HKR,,,,%DeviceClassName%
HKR,,Icon,,"-5"

;--------- DestinationDirs Section -------------------------------------------

[DestinationDirs]
YouMark_Files_Driver = 10,System32\Drivers

;--------- Manufacturer and Models Sections ----------------------------------

[Manufacturer]
%MfgName%=Mfg0

[Mfg0]

; PCI hardware Ids use the form
; PCI\VEN_aaaa&DEV_bbbb&SUBSYS_cccccccc&REV_dd
;改成你自己的ID
%DeviceDesc%=YouMark_DDI, PCI\VEN_9999&DEV_9999

;---------- DDInstall Sections -----------------------------------------------
; --------- Windows 9X -----------------

; Experimentation has shown that DDInstall root names greater than 19 characters
; cause problems in Windows 98

[YouMark_DDI]
CopyFiles=YouMark_Files_Driver
AddReg=YouMark_9X_AddReg

[YouMark_9X_AddReg]
HKR,,DevLoader,,*ntkern
HKR,,NTMPDriver,,HelloWDM.sys
HKR, "Parameters", "BreakOnEntry", 0x00010001, 0

; --------- Windows NT -----------------

[YouMark_DDI.NT]
CopyFiles=YouMark_Files_Driver
AddReg=YouMark_NT_AddReg

[YouMark_DDI.NT.Services]
Addservice = HelloWDM, 0x00000002, YouMark_AddService

[YouMark_AddService]
DisplayName = %SvcDesc%
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
StartType = 3 ; SERVICE_DEMAND_START
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
ServiceBinary = %10%\System32\Drivers\HelloWDM.sys

[YouMark_NT_AddReg]
HKLM, "System\CurrentControlSet\Services\HelloWDM\Parameters",\
"BreakOnEntry", 0x00010001, 0


; --------- Files (common) -------------

[YouMark_Files_Driver]
HelloWDM.sys

;--------- Strings Section ---------------------------------------------------

[Strings]
ProviderName="Flying L Co.,Ltd."
MfgName="LC Soft"
DeviceDesc="Hello World WDM!"
DeviceClassName="LC_Device"
SvcDesc="???"



注意它可以同時在Win98或者Win2000中使用(系統會通過這個INF文件里面的字段名稱,自動選擇適合當前系統的安裝方法的)。關于INF文件的各個字段含義現在我也不知道,所以也沒有辦法說清楚,如果誰看到這篇文章,而又知道的話,不妨為我一份。

準備好這個 HelloWDM.INF 文件后,讓我們打開控制面板,雙擊“添加/刪除硬件”,選擇“添加/排除設備故障”->“添加新設備”->“否,我想從列表選擇硬件”->“其它設備”->“從磁盤安裝”,選擇 HelloWDM.INF 所在的路徑,然后安裝。

當安裝完成后,系統就會添加上你寫好的驅動程序了。(可以在“設備管理器”中查看到)。然后重啟電腦,這個驅動程序就投入使用啦。

關于安裝,我也只知道這么多,到底安裝驅動程序時,操作系統都作了些什么,我也不是很清楚,等我弄明白了我再貼上。



任我行 2006-01-03 10:11 發表評論
]]>
驅動程序開發—編譯正傳 http://www.shnenglu.com/oosky/archive/2006/01/03/2369.html任我行任我行Tue, 03 Jan 2006 02:10:00 GMThttp://www.shnenglu.com/oosky/archive/2006/01/03/2369.htmlhttp://www.shnenglu.com/oosky/comments/2369.htmlhttp://www.shnenglu.com/oosky/archive/2006/01/03/2369.html#Feedback0http://www.shnenglu.com/oosky/comments/commentRss/2369.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/2369.html我在前面也講過了一些關于編譯環境及工具的。在這里結合本例子我再說一下:

DDK分為98 DDK和2000 DDK兩種,它們工作起來是大同小異的,不過有些驅動程序只能在2000 DDK中使用。由于Win98注定是一種即將被淘汰的操作系統了,所以我學習的時候也沒有過多的關注,我用的是2000的DDK,所以以下的所有內容都是針對2000 DDK的。

·準備工作
1、確定你已經安裝了Visual C++
2、安裝2000 DDK
3、安裝2000 DDK成功后,在“開始”->“程序”里應該有“Development Kits”->“Windows 2000 DDK”的項目。
注意一定要先安裝好VC,然后才安裝DDK,這個順序決不能顛倒!!
4、保證DDKROOT環境變量設置為Windows 2000 DDK的基目錄,如果不是的話,請在控制面板“系統”屬性的“高級”標簽環境變量編輯器中設置好這個環境變量。


·編寫必需的文件
編譯WDM程序的時候,有兩個文件是必須要有的,它們是:
1、makefile
(這個是什么啊?你可能會問。)對于比較年輕的程序員來說,有可能沒有見過這個文件吧。其實在VC這些IDE出現之前,我們都必須使用makefile來確定項目中哪些文件需要重新編譯,現在的IDE都把這個工作自動做好了
我們要做的工作很簡單,就是提供這樣一個文件,它的內容是:

#
# DO NOT EDIT THIS FILE!!!  Edit .\sources. If you want to add a new source
# file to this component.  This file merely indirects to the real make file
# that is shared by all the driver components of the Windows NT DDK
#

!INCLUDE $(NTMAKEENV)\makefile.def


正如它所述,不要編輯這個文件。事實上每個WDM程序所需要的makefile的內容都是一樣的,也就是說,我們只需要簡單地copy一個makefile到新的項目中就可以了
2、Sources

TARGETNAME=HelloWDM //編譯出來的驅動程序的名稱
TARGETTYPE=DRIVER      //編譯的類型是驅動程序編譯
DRIVERTYPE=WDM           //驅動程序的類型是WDM驅動程序
TARGETPATH=OBJ             //生成的文件存放在OBJ目錄中

INCLUDES=$(BASEDIR)\inc;\   //這是需要引入的頭文件
         $(BASEDIR)\inc\ddk;\

TARGETLIBS=$(BASEDIR)\lib\*\free\usbd.lib\  //這是需要引入的庫文件

SOURCES=HelloWDM.cpp\    //這是源碼文件


這個文件指定了驅動程序目標名是HelloWDM.sys,是一個WDM驅動程序,生成的文件存放在OBJ目錄中。值得注意的是,“=”前后不能有空格,否則編譯的時候會出錯。


·開始編譯
娃哈哈,前面羅羅嗦嗦講了一大堆,現在終于到重點了。WDM程序的編譯過程比較特殊,它不是在VC里面按F7來編譯的(盡管你可以通過設置來達到這一目的),而是通過一個DDK實用工具build來完成。下面我們來講講具體步驟:
1、“Debug”版的生成
首先,我們假設你的源代碼放在D:\HelloWDM里面。請跟著以下步驟:

“開始”->“程序”->“Development Kits”->“Windows 2000 DDK”->“Checked Build Environment”

屏幕將顯示:(有“回車”的那行是需要讀者你親自打進去的)

New or updated MSVC detected.  Updating DDK environment….

Setting environment for using Microsoft Visual C++ tools.
Starting dirs creation…Completed.

D:\NTDDK>cd\HelloWDM    (回車)

D:\HelloWDM>build    (回車)


如果源代碼沒有錯誤的話,生成的HelloWDM.sys將存放在objchk\i386目錄中。

2、“Release”版的生成
請跟著以下步驟:

“開始”->“程序”->“Development Kits”->“Windows 2000 DDK”->“Free Build Environment”

隨后的步驟跟“Debug”版相同,不同的是生成的HelloWDM.sys將存放在objfre\i386目錄中。

任我行 2006-01-03 10:10 發表評論
]]>
驅動程序開發—編譯前傳http://www.shnenglu.com/oosky/archive/2006/01/03/2368.html任我行任我行Tue, 03 Jan 2006 02:08:00 GMThttp://www.shnenglu.com/oosky/archive/2006/01/03/2368.htmlhttp://www.shnenglu.com/oosky/comments/2368.htmlhttp://www.shnenglu.com/oosky/archive/2006/01/03/2368.html#Feedback0http://www.shnenglu.com/oosky/comments/commentRss/2368.htmlhttp://www.shnenglu.com/oosky/services/trackbacks/2368.html好啦,辛辛苦苦終于寫完了程序,讓我們編譯運行吧!按下Ctrl+F5(嘿嘿,讓我們先假設你習慣用VC來寫程序),我等啊等……疑?怎么毫無動靜的?再看看Output窗口,哇!有幾百個錯誤啊!!不禁頭大——這是怎么回事呢?

原來,WDM程序編譯出來的并不是我們常見的.exe,而是.sys文件,在未經設置編譯環境之前,是不能直接用VC來編譯的(這就是為什么會有幾百個錯誤了)。這種類型的文件你可以在WINNT\System32\Drivers里面找到很多。其實驅動程序也是一種PE文件,它同樣由DOS MZ header開頭,也有完整的DOS stub和PE header,同樣擁有Import table和Export table——……那跟普通的PE文件有什么不一樣呢?那么就讓我們先來做個小剖析,加深對.sys文件的認識吧


首先祭出Delphi里附帶的tdump.exe程序。讓我們鍵入:
C:\WINNT\System32\Drivers>tdump ccport.sys -em -ee
參數-em是列出Import table,-ee是列出Export table。回車之后,屏幕列出一大堆東西:

C:\WINNT\SYSTEM32\DRIVERS>tdump ccport.sys -em -ee
Turbo Dump  Version 5.0.16.12 Copyright ? 1988, 2000 Inprise Corporation
                    Display of File CCPORT.SYS

IMPORT:     NTOSKRNL.EXE={hint:011Fh}.’memcpy’
IMPORT:     NTOSKRNL.EXE={hint:003Dh}.’IoDeleteDevice’
IMPORT:     NTOSKRNL.EXE={hint:0030h}.’IoAttachDeviceToDeviceStack’
IMPORT:     NTOSKRNL.EXE={hint:008Eh}.’KeSetEvent’
IMPORT:     NTOSKRNL.EXE={hint:0068h}.’IofCallDriver’
IMPORT:     NTOSKRNL.EXE={hint:0095h}.’KeWaitForSingleObject’
IMPORT:     NTOSKRNL.EXE={hint:0074h}.’KeInitializeEvent’
IMPORT:     NTOSKRNL.EXE={hint:003Fh}.’IoDetachDevice’
IMPORT:     NTOSKRNL.EXE={hint:00D3h}.’RtlFreeUnicodeString’
IMPORT:     NTOSKRNL.EXE={hint:0077h}.’KeInitializeSpinLock’
IMPORT:     NTOSKRNL.EXE={hint:0129h}.’strcpy’
IMPORT:     NTOSKRNL.EXE={hint:0121h}.’memset’
IMPORT:     NTOSKRNL.EXE={hint:003Ch}.’IoCreateUnprotectedSymbolicLink’
IMPORT:     NTOSKRNL.EXE={hint:0038h}.’IoCreateDevice’
IMPORT:     NTOSKRNL.EXE={hint:00C2h}.’RtlAnsiStringToUnicodeString’
IMPORT:     NTOSKRNL.EXE={hint:0069h}.’IofCompleteRequest’
IMPORT:     NTOSKRNL.EXE={hint:0124h}.’sprintf’
IMPORT:     NTOSKRNL.EXE={hint:003Eh}.’IoDeleteSymbolicLink’
IMPORT:     NTOSKRNL.EXE={hint:0042h}.’IoFreeIrp’
IMPORT:     NTOSKRNL.EXE={hint:004Dh}.’IoInitializeIrp’
IMPORT:     NTOSKRNL.EXE={hint:002Dh}.’IoAllocateIrp’
IMPORT:     NTOSKRNL.EXE={hint:0027h}.’InterlockedExchange’
IMPORT:     NTOSKRNL.EXE={hint:0025h}.’InterlockedCompareExchange’
IMPORT:     NTOSKRNL.EXE={hint:0035h}.’IoCancelIrp’
IMPORT:     NTOSKRNL.EXE={hint:012Ah}.’strlen’
IMPORT:     NTOSKRNL.EXE={hint:0126h}.’strcat’
IMPORT:     NTOSKRNL.EXE={hint:0114h}.’atoi’
IMPORT:     NTOSKRNL.EXE={hint:0128h}.’strcmp’
IMPORT:     NTOSKRNL.EXE={hint:0034h}.’IoBuildSynchronousFsdRequest’
IMPORT:     NTOSKRNL.EXE={hint:00D5h}.’RtlInitAnsiString’
IMPORT:          HAL.DLL={hint:0006h}.’KfAcquireSpinLock’
IMPORT:          HAL.DLL={hint:0009h}.’KfReleaseSpinLock’

EXPORT ord:0001=’Vcomm_DriverControl’



看到了嗎?它主要調用了NTOSKRNL.EXE和HAL.DLL文件(實際上你會發現,幾乎所有的WDM驅動程序都會調用NTOSKRNL.EXE文件,從它的名字你可以看出為什么了吧?),并且輸出了一個函數“Vcomm_DriverControl”。這表明,其實.sys跟.exe文件一樣,都是一種PE文件來的。不同的是,.sys文件Import的通常是NTOSKRNL.EXE,而.exe文件Import的通常是KERNEL32.DLL和USER32.DLL。

知道了這些有什么用呢?實際上,由于.sys通常不調用KERNEL32.DLL和USER32.DLL,所以你是不能在設備驅動程序里面調用任何C、C++和Win32函數的,而且也不能用C++關鍵字new和delete等(可以用malloc和free來代替),而必須使用大量的內核函數。另外,你應該也能看到她調用了像IoDeleteDevice、IoAttachDeviceToDeviceStack等等函數,這些你以前可能沒有見過的函數都是些內核函數。為了讀者的方便,下面我列出一些常見的驅動程序可用的內核函數:

Ex…        執行支持
Hal…        硬件抽象層(僅NT/Windows 2000)
Io…        I/O管理器(包括即插即用函數)
Ke…        內核
Ks…        內核流IRP管理函數
Mm…        內存管理器
Ob…        對象管理器
Po…        電源管理
Ps…        進程結構
Rtl…        運行時庫
Se…        安全引用監視
Zw…        其他函數


最后讓我們再來看看,寫設備驅動程序時必須注意的一些問題:

1、內核宏
如果查看DDK頭文件,會發現有幾個內核函數是以宏的方式實現的。這種宏中有幾個宏的定義是相當糟糕的。例如,我們看到RemoveHeadList的定義如下:

#define RemoveHeadList(ListHead)
        (ListHead)->Flink;
        {RemoveEntryList((ListHead)->Flink)}


如果以以下方式調用RemoveHeadList,則將編譯錯誤的代碼:

if(SomethingInList)
        Entry = RemoveHeadList(list);


使這個調用安全的唯一方法是使用花括號:

if(SomethingInList)
    {
        Entry = RemoveHeadList(list);
    }


所以我們切勿為了貪圖一時的方便,而使用不太規范的寫法,最好是在所有的if、for和while等語句中使用花括號。

2、驅動程序函數名稱
跟C/C++的main()函數一樣,設備驅動程序也有一個必須存在,而且只能以DriverEntry()為名稱的入口函數。然而,除此之外,我們可以使用任何名字來給其他函數命名——只要你自己記得就行了,當然,最好符合某些特定的規范啦,例如匈牙利命名法……

3、安裝時的問題
·在Windows98中驅動程序可執行文件必須是8.3文件名。(別問我為什么,我也不知道,我只能建議你去問比爾該死)
·如果INF文件中含有非法節的詳細資料,Windows將不使用這個INF文件。

本節羅羅嗦嗦講了一大堆,跟實際的編程卻并沒有太大的關系,前傳嘛!就是這樣的啦!



任我行 2006-01-03 10:08 發表評論
]]>
久久成人永久免费播放| 18岁日韩内射颜射午夜久久成人| 日韩乱码人妻无码中文字幕久久 | 国产叼嘿久久精品久久| 26uuu久久五月天| 亚洲伊人久久综合中文成人网| 一极黄色视频久久网站| 久久超碰97人人做人人爱| 国产精自产拍久久久久久蜜| 久久久国产视频| 97久久精品人人澡人人爽| 久久精品一区二区三区AV| 热久久国产精品| 少妇内射兰兰久久| 青草久久久国产线免观| 狠狠狠色丁香婷婷综合久久五月 | 天天爽天天狠久久久综合麻豆| 欧美激情精品久久久久| 亚洲av成人无码久久精品| 久久亚洲国产成人影院网站 | 中文字幕久久精品无码| 久久se这里只有精品| 人人狠狠综合久久亚洲88| 久久精品九九亚洲精品| 亚洲人成精品久久久久 | 久久精品一本到99热免费| 久久99精品国产99久久6| 97精品伊人久久大香线蕉app| 亚洲AV无码一区东京热久久| 一级a性色生活片久久无| 久久精品国产只有精品66| 国产99久久久国产精品~~牛| 久久成人国产精品| 999久久久无码国产精品| 久久综合88熟人妻| 2021精品国产综合久久| 72种姿势欧美久久久久大黄蕉| 欧美牲交A欧牲交aⅴ久久| 国产精品免费福利久久| 国产成人精品久久免费动漫 | 久久精品人成免费|