﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-任我行</title><link>http://www.cppblog.com/oosky/</link><description>一天一个脚印......
&lt;br&gt;每日一句: &lt;script language="javascript" charset="utf-8" src="http://sl.iciba.com/spdshow.php"&gt;&lt;/script&gt;</description><language>zh-cn</language><lastBuildDate>Fri, 03 Apr 2026 19:21:09 GMT</lastBuildDate><pubDate>Fri, 03 Apr 2026 19:21:09 GMT</pubDate><ttl>60</ttl><item><title>调试5.0M sensor模组的笔记(转载)</title><link>http://www.cppblog.com/oosky/archive/2010/09/15/126672.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Wed, 15 Sep 2010 08:58:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2010/09/15/126672.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/126672.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2010/09/15/126672.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/126672.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/126672.html</trackback:ping><description><![CDATA[<p>详细出处：<a href="http://www.52rd.com/Blog/Detail_RD.Blog_bmw7_24676.html#41074">http://www.52rd.com/Blog/Detail_RD.Blog_bmw7_24676.html#41074</a><br><br>&nbsp;&nbsp; 在25平台上调试了一款带ISP处理器的5.0M sensor 模组，在25上实现了5.0M sensor的预览拍照功能。下面是调试过程中的一些笔记：</p>
<p>一.关于H-sync /V-Sync的知识：</p>
<p>1. 分辨率：比如说640x480,就會有640 个pixel &amp;480 line，那么每个V-sync的信号时间内就会有480个H-sync，而一个H-sync会有640个pixel。但是,每个pixel会有2 byte，所以我们会量到PCLK 在一个H-sync內的数量会有1280个。</p>
<p>2. H-sync /V-Sync的极性polarity： polarity就是资料有效的准备，比方说V-sync上的H-sync有可能在V-sync的low,也有可能在high出现。</p>
<p>&nbsp;</p>
<p>二. 所使用的ISP处理器简介：XXX838是一款isp（图像信号处理器）ic，核心是一款arm7 process，提供自动对焦，人脸识别等功能。BB通过i2c与其进行命令类的数据通信，而sensor数据则通过CCIR总线传输给BB.</p>
<p>&nbsp;</p>
<p>三. 25平台 camera处理流程学习</p>
<p>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void cam_event_ind_hdlr(ilm_struct *ilm_ptr)// This function is to handle camera event indication.</p>
<p>&nbsp;&nbsp;&nbsp; 在该函数中，通过camera_capture_mem_process(&amp;capture_mem_param)命令从lcd层获取capture数据，然后通过jpeg_encode_process(&amp;jpg_encode)命令将这些数据软编码成jpeg格式的数据。</p>
<p>&nbsp;&nbsp;</p>
<p>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void cam_capture_req_hdlr(ilm_struct *ilm_ptr)// This function is to handle camera capture request.</p>
<p>（1）&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 该函数首先执行exit_camera_preview_process();命令退出preview流程；</p>
<p>（2）&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ./* copy preview data to MMI buffer */</p>
<p>memcpy(</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (kal_uint8*) cam_context_p-&gt;frame_buffer_p,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (kal_uint8*) cam_context_p-&gt;int_frame_buffer_p,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cam_context_p-&gt;frame_buffer_size);</p>
<p>（3）. /* release preview related memory */</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cam_release_memory();</p>
<p>&nbsp;&nbsp;</p>
<p>3.cam_context_p-&gt;capture_buffer_p ：所需存储的拍照数据指针</p>
<p>&nbsp; cam_context_p-&gt;file_size ：所要存储的数据大小</p>
<p>4. 在cam_open_image_file函数中执行命令</p>
<p>cam_context_p-&gt;capture_buffer_p = (kal_uint32) med_alloc_ext_mem(buffer_size);</p>
<p>来分配内存。</p>
<p>Capture数据存储指针：capture_isp_param.target_buffer_start_address = (kal_uint32) cam_context_p-&gt;capture_buffer_p;</p>
<p>camera_capture_jpeg_process(&amp;capture_isp_param);</p>
<p>isp_capture_jpeg_data.target_buffer_start_address=isp_data-&gt;target_buffer_start_address;</p>
<p>sw_jpeg_encode_config_data.jpeg_file_start_address=isp_capture_jpeg_data.target_buffer_start_address;</p>
<p>&nbsp;</p>
<p>&nbsp;5. camera capture后的数据传送流程：cam_context_p-&gt;intmem_start_address.</p>
<p>&nbsp; （1）.&nbsp; capture_isp_param.intmem_start_address = cam_context_p-&gt;intmem_start_address =</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (kal_uint32) med_alloc_int_mem(cam_capture_mem[0]);//只有45k</p>
<p>&nbsp;&nbsp;&nbsp; capture_isp_param.intmem_size = cam_context_p-&gt;intmem_size = (kal_uint32) cam_capture_mem[0];</p>
<p>&nbsp; （2）. file_size = camera_capture_jpeg_process(&amp;capture_isp_param); //jpeg编码后的文件大小</p>
<p>&nbsp; （3）.isp_capture_jpeg_data.intmem_start_address=isp_data-&gt;intmem_start_address;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isp_capture_jpeg_data.intmem_size=isp_data-&gt;intmem_size;</p>
<p>&nbsp; （4）.&nbsp;&nbsp;&nbsp; intmem_init((kal_uint32 *) isp_capture_jpeg_data.intmem_start_address,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isp_capture_jpeg_data.intmem_size);</p>
<p>&nbsp; （5）.sw_jpeg_encode_config_data.intmem_start_address=isp_capture_jpeg_data.intmem_start_address; //将所获取的capture原始数据地址指针赋给软编码的起始地址</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>四.调试关键点</p>
<p>1. 首先调通I2C通讯，必须确保BB与ISP的I2C通讯正常；</p>
<p>2.&nbsp; 重新定义#define MAX_CAM_FILE_BUFFER_LEN&nbsp;&nbsp; (3150*1024)&nbsp;&nbsp;&nbsp; /* 2700kb for 5.0M */</p>
<p>&nbsp;</p>
<p>3.仿照camera_capture_jpeg_process函数，创建一个新函数，在该函数中对获取的数据直接存储，而不经过jpeg编码流程（由于XXX838传输过来的已经是jpeg格式的数据）。</p>
<p>注意：</p>
<p>（1）&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在该函数中，要设置：</p>
<p>ENABLE_CAMERA_OUTPUT_TO_MEM;//ISP输出至Memory，</p>
<p>SET_CAMERA_CAPTURE_MODE</p>
<p>&nbsp;/*** Capture,等待VSYNC中断**/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br></p>
<p>&nbsp;（2）.在capture完成后，要DISABLE_CMOS_SESNOR;//关闭sensor信号。</p>
<p>&nbsp;（3）.此时，获取的capture的数据已经存储在isp_data-&gt;target_buffer_start_address中;</p>
<p>然后读取这些数据，通过0xff ，0xd8判断文件头，0xff ，0xd9判断jpeg文件尾及其长度。</p>
<p>（4）最后，通过kal_int32 cam_close_image_file(kal_uint32 size)保存文件</p>
<p><br>&nbsp;</p>
<img src ="http://www.cppblog.com/oosky/aggbug/126672.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2010-09-15 16:58 <a href="http://www.cppblog.com/oosky/archive/2010/09/15/126672.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于调用约定(cdecl、fastcall、、thiscall) 的一点知识</title><link>http://www.cppblog.com/oosky/archive/2007/01/08/17422.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Mon, 08 Jan 2007 06:17:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2007/01/08/17422.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/17422.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2007/01/08/17422.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/17422.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/17422.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 函数调用时，调用者依次把参数压栈，然后调用函数，函数被调用以后，在堆栈中取得数据，并进行计算。函数计算结束以后，或者调用者、或者函数本身修改堆栈，使堆栈恢复原装。在参数传递中，有两个很重要的问题必须得到明确说明： &nbsp;&nbsp;<a href='http://www.cppblog.com/oosky/archive/2007/01/08/17422.html'>阅读全文</a><img src ="http://www.cppblog.com/oosky/aggbug/17422.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2007-01-08 14:17 <a href="http://www.cppblog.com/oosky/archive/2007/01/08/17422.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>PE文件格式详解</title><link>http://www.cppblog.com/oosky/archive/2006/11/24/15614.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Fri, 24 Nov 2006 02:29:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/11/24/15614.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/15614.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/11/24/15614.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/15614.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/15614.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 既然在Windows下跑，也得了解一下怎么跑起来的&nbsp;&nbsp;<a href='http://www.cppblog.com/oosky/archive/2006/11/24/15614.html'>阅读全文</a><img src ="http://www.cppblog.com/oosky/aggbug/15614.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-11-24 10:29 <a href="http://www.cppblog.com/oosky/archive/2006/11/24/15614.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>消费者维权的5种途径</title><link>http://www.cppblog.com/oosky/archive/2006/10/12/13609.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Thu, 12 Oct 2006 09:25:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/10/12/13609.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/13609.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/10/12/13609.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/13609.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/13609.html</trackback:ping><description><![CDATA[
		<p>值10.1期间，到上海买了款Acer的本本，7天后问题出现。换机后麻烦更大。<br />详细过程就不说了，具体看这个网址：<a href="http://benyouhui.it168.com/viewthread.php?tid=399111&amp;extra=page%3D1">http://benyouhui.it168.com/viewthread.php?tid=399111&amp;extra=page%3D1</a><br /><br />上网找了找相关的法律及法规。才明白还可以有多种途径。看来这个消费者权益法还是必要读读看看。<br /></p>
		<div align="center">
				<font color="blue">
						<font size="3">消费者维权的5种途径</font>
				</font>
		</div>
		<p>
				<br />
				<br />
				<font color="green">
						<b>一、协商和解</b>
				</font>
				<br />
				<br />
				<b>协商和解的定义</b>
				<br />
				<br />    消费者与经营者在发生争议后，就与争议有关的问题进行协商，在自愿、互谅的基础上，通过直接对话摆事实、讲道理，分清责任，达成和解协议，使纠纷得以解决的活动。消费者权益争议的协商和解是一种快速、简便的争议解决方式，无论是对消费者还是对经营者，它都不失为一种理想的途径。事实上，日常生活中大量的消费者权益争议都是通过这种方式解决的。<br /><br /><b>消费者与经营者协商和解的法律依据</b><br /><br />    《消费者权益保护法》第34条明确规定："消费者和经营者发生消费者权益争议的，可以通过下列途径解决：（1）与经营者协商和解；（2）请求消费者协会调解；（3）向有关行政部门申诉；（4）根据与经营者达成的仲裁协议提请仲裁机构仲裁；（5）向人民法院提起诉讼。"此条第1项规定，即"与经营者协商和解"，便是消费者与经营者协商和解的法律依据。<br /><br /><b>协商和解的步骤</b><br /><br />    在实践中，协商和解可以在其权益受到侵犯时，带上有关证据，如购货凭证或者服务单据以及受损失证据，找到经营者，向其负责人或者主管解决纠纷的部门说明情况，并提出自己的意见和要求。如果经营者觉得消费者的意见和要求合理，就会接受。如果经营者觉得消费者的要求过高，就会要求消费者降低其要求。经过一番"讨价还价"后，达成一个双方都愿意接受的协议时，争议就解决了。<br /><br /><b>协商和解应坚持协作和平等原则：</b><br /><br />    协作原则。要求消费者与经营者在融洽的气氛中，在互相谅解的基础上，本着实事求是、团结协作的精神，通过摆事实讲道理，弄清事实，分清责任，自愿地达成协议，避免只从自己一方的利益出发，坚持已见，互不相让。<br />    平等原则。消费者和经营者要在平等的前提下自行协商解决消费者权益争议。决不允许任何一方凭借某种势力，以强凌弱，以大压小，享有特权，获得不平等的利益。<br /><br /><b>在协商和解时，消费者应注意以下问题：</b><br /><br />    针对经营者故意拖延或无理拒绝消费者协商和解建议的行为，消费者应立即采取措施，用其他途径解决争议问题。即可用投诉、申诉或仲裁、起诉手段解决纠纷。如果经营者的故意拖延和无理拒绝，致使消费者财产损失扩大的，经营者除了应当满足消费者正常要求外，还应当就扩大的损失承担赔偿责任。<br /><br />    针对经营者故意推卸责任，认为产品出现质量问题是生产厂家的事，要求消费者直接找厂家交涉的行为，按《消费者权益保护法》第35条规定："消费者在购买、使用商品时，其合法权益受到损害的，可以向销售者要求赔偿。销售者赔偿后，属于生产者的责任或者属于向销售者提供商品的其他销售者的责任的，销售者有权向生产者或者其他销售者追偿。消费者或者其他受害人因商品缺陷造成人身、财产损害的，可以向销售者要求赔偿，也可以向生产者要求赔偿。属于生产者责任的，销售者赔偿后，有权向生产者追偿。属于销售者责任的，生产者赔偿后，有权向销售者追偿。消费者在接受服务时，其合法权益受到损害的，可以向服务者要求赔偿。"因此，当消费者遇到商品质量问题时，如经营者推卸责任，认为是生产厂家的问题，要求消费者直接找厂家交涉时，消费者应当有自我保护意识，不能挟在中间让厂家和经营者当"皮球"踢。要以法律规定为依据，切实维护自己的合法权益。<br /><br />    针对经营者以店堂通知、声明、告示为由，拒不承担责任的行为，按《消费者权益保护法》第24条规定："经营者不得以格式合同、通知、声明、店堂告示等方式作出对消费者不公平、不合理的规定，或者减轻、免除其损害消费者合法权益应当承担的民事责任。格式合同、通知、声明、店堂告示等含有前款所列内容的，其内容无效。"因此，当消费者因商品质量和服务问题与商家交涉、协商时，千万不能为其店堂内服务规则或商品销售告示所约束，这些服务规则与法无据，没有法律效力，应视为无效规则。<br /><br /><font color="green"><b>二、投诉和调解</b></font><br /><br /><b>投诉的定义：</b><br /><br />    消费者投诉，是指消费者为生活消费需要购买、使用商品或者接受服务，与经营者之间发生消费者权益争议后，请求消费者权益保护组织调解，要求保护其合法权益的行为。<br /><br /><b>调解的含义：</b><br /><br />    调解，即由第三方对争议双方当事人进行说服劝导、勾通调和，以促成争议双方达成解决纠纷的协议的活动。<br />《消费者权益保护法》规定，消费者争议可以通过消费者协会调解解决。实际上，消费者纠纷的调解并非只能由消费者协会进行，任何第三人参与消费者纠纷的解决，促成争议双方达成协议的，都属调解的范围。并且只要不存在违法行为，则调解同样受法律承认。<br /><b><br />调解的原则：</b><br /><br />    1、自愿原则。调解应建立在双方自愿的基础之上。调解不同于审判，当任何一方不同意调解时，应终止调解，而不得以任何理由加以强迫。<br /><br />    2、合法原则。调解活动应在合法的原则上进行，既要有必要的灵活性，更要有高度的原则性，不能违反法律的规定来"和稀泥"。<br /><b><br />投诉的形式：</b><br /><br />    消费者投诉可以采取电话、信函、面谈、互联网形式进行。但无论采取哪种形式，都要讲清楚以下内容：一是投诉人基本情况。即投诉人的姓名、性别、联系地址、联系电话、邮政编码等。二是被投诉方的基本情况。即被投诉方名称、地址、电话等。三是购买商品的时间、品牌、产地、规格、数量、价格等。四是受损害的具体情况、发现问题的时间及与经营者交涉的经过等。五是购物凭证、保修卡、约定书复印件等。<br /><br /><font color="green"><b>三、行政申诉</b></font><br /><br /><b>申诉的定义</b><br /><br />    消费者和经营者发生权益争议后，可以请求政府有关行政部门依行政程序解决争议，与其他争议解决途径相比，申诉具有高效、快捷、力度强等特点。 <br />消费者向政府有关行政部门申诉的法律依据<br /><br />   《消费者权益保护法》第34条的规定，消费者和经营者发生消费者权益争议的，可以向有关行政部门申诉。<br /><br /><b>消费者如何进行申诉？</b><br /><br />    消费者决定申诉时，应依照商品和服务的性质向具有相关职能的行政部门提出。消费者申诉一般应采用书面形式，一式两份，并载明下列事项：（1）消费者的姓名、住址、电话号码、邮政编码；（2）被申诉人的名称、地址、联系电话、邮政编码；（3）申诉的要求、理由及相关的事实根据；（4）申诉的日期。必要时，消费者可委托代理人进行申诉活动，但需向有关行政部门提交授权委托书。<br /><br />    消费者向有关行政部门提出申诉后，如果与经营者协商和解，达成和解协议的，可以撤回申诉，请求有关行政部门根据和解协议作出调解书。如果与经营者达成仲裁协议，可以撤回申诉，向仲裁机构提请仲裁。如果想通过法律途径解决，可以撤回申诉，向人民法院提起诉讼。<br /><br /><font color="green"><b>四、提请仲裁</b></font><br /><br /><b>仲裁的定义 </b><br /><br />    双方当事人在争议发生前或者争议发生后达成的协议，自愿将他们之间的争议提交双方所同意的仲裁机构居中调解，作出判断或裁决的活动。<br /><br /><b>仲裁的优越性</b><br /><br />仲裁具有当事人意思自愿、程序简便、一裁终局、专家仲裁、费用较低、保守机密、相互感情影响小等特征。<br /><br /><b>当事人采取仲裁方式解决纠纷，应注意以下几点：</b><br /><br />（1）当事人采用仲裁方式解决纠纷，应当是双方自愿，并达成仲裁协议；<br /><br />（2）向哪个仲裁组织提请仲裁，由当事人协议选定；<br /><br />（3）可以选择或者委托仲裁组织指定仲裁员；<br /><br />（4）可以自行和解，达成和解协议的，可以请求仲裁庭根据和解协议作出裁决书，也可以撤回仲裁申请。<br /><br /><b>仲裁的原则和制度</b><br /><br />仲裁实行自愿、独立、公正、一裁终局的原则和制度。<br /><br /><b>仲裁协议的定义</b><br /><br />    双方当事人自愿把他们之间的经济争议提交仲裁解决的书面约定。其表现形式包括合同中订立的仲裁条款和以其他书面方式在纠纷发生后前或者发生后达成请求的仲裁协议。仲裁协议是独立存在的，合同的变更、解除、终止或者无效，不影响仲裁协议的效力。<br /><br /><b>仲裁协议应具备的内容</b><br /><br />（1）请求仲裁的意思表示；<br /><br />（2）仲裁事项；<br /><br />（3）选定的仲裁委员会。<br /><br /><b>当事人提请仲裁应当符合的条件</b><br /><br />（1）有仲裁协议；<br /><br />（2）有具体的仲裁请求和事实理由；<br /><br />（3）属于仲裁委员会的管理范围。<br /><br /><b>仲裁案件受理费的承担</b><br /><br />    仲裁费用原则上由败诉的当事人承担，当事人部分胜诉，部分败诉的，由仲裁庭根据当事人各方责任大小确定其各自应当承担的仲裁费用的比例，当事人自行和解或者经仲裁庭调解结案的，当事人可以协商确定各自承担的仲裁费用的比例。<br /><br /><font color="green"><b>五、提起诉讼</b></font><br /><br />提起诉讼的定义<br /><br />    消费者因其合法权益受到侵害后，可以向人民法院提起诉讼，请求人民法院依照法定程序进行审判。在我国，诉讼大致分为三种形式：（1）刑事诉讼；（2）民事诉讼；（3）行政诉讼。消费者因其合法权益受到侵害而提起的诉讼属于民事诉讼范畴。<br /><br />提起讼诉必须具备的法定条件<br /><br />（1）原告必须是与本案有直接利害关系的公民、法人和其他组织；<br />（2）有明确的被告；<br />（3）有具体的诉讼请求和事实、理由；<br />（1）属于人民法院受理民事诉讼的范围和受诉人民法院管辖。<br /><br />符合以上条件的起诉，人民法院才会予以受理。<br /><br /><br /></p>
<img src ="http://www.cppblog.com/oosky/aggbug/13609.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-10-12 17:25 <a href="http://www.cppblog.com/oosky/archive/2006/10/12/13609.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>嵌入式面试考题</title><link>http://www.cppblog.com/oosky/archive/2006/08/28/11763.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Mon, 28 Aug 2006 01:51:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/08/28/11763.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/11763.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/08/28/11763.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/11763.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/11763.html</trackback:ping><description><![CDATA[
		<p>
				<font size="2">
						<span class="123503400-16032006">作者不祥，<br />别看这题目有些比较简单，仔细做来，可还是发现不少问题。 <br /><br /></span>
						<br />预处理器（Preprocessor）<br /><br /><br /></font>
				<font size="2">
						<font color="#ff0000">1. 用预处理指令#define 声明一个常数，用以表明1年中有多少秒（忽略闰年问题）<br /></font>
						<br />#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL <br />我在这想看到几件事情： <br />1). #define 语法的基本知识（例如：不能以分号结束，括号的使用，等等） <br />2). 懂得预处理器将为你计算常数表达式的值，因此，直接写出你是如何计算一年中有多少秒而不是计算出实际的值，是更清晰而没有代价的。 <br />3). 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。 <br />4). 如果你在你的表达式中用到UL（表示无符号长整型），那么你有了一个好的起点。记住，第一印象很重要。<br /><br /><br /><font color="#ff0000">2. 写一个“标准”宏MIN，这个宏输入两个参数并返回较小的一个。</font><br /></font>
		</p>
		<p>
				<font size="2">#define MIN(A,B) ((A) &lt;= (B) (A) : (B)) <br />这个测试是为下面的目的而设的： <br />1). 标识#define在宏中应用的基本知识。这是很重要的，因为直到嵌入(inline)操作符变为标准C的一部分，宏是方便产生嵌入代码的唯一方法，对于嵌入式系统来说，为了能达到要求的性能，嵌入代码经常是必须的方法。 <br />2). 三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码，了解这个用法是很重要的。 <br />3). 懂得在宏中小心地把参数用括号括起来 <br />4). 我也用这个问题开始讨论宏的副作用，例如：当你写下面的代码时会发生什么事？ <br />least = MIN(*p++, b);<br /><br /><br /><font color="#ff0000">3. 预处理器标识#error的目的是什么？</font><br /><br />如果你不知道答案，请看参考文献1。这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录去找出象这种 <br />问题的答案。当然如果你不是在找一个书呆子，那么应试者最好希望自己不要知道答案。<br /><br /><br />死循环（Infinite loops）<br /><br /><br /><font color="#ff0000">4. 嵌入式系统中经常要用到无限循环，你怎么样用C编写死循环呢？</font><br /><br />这个问题用几个解决方案。我首选的方案是： <br />while(1) { } <br />一些程序员更喜欢如下方案： <br />for(;;) { } <br />这个实现方式让我为难，因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案，我将用这个作为一个机会去探究他们这样做的 <br />基本原理。如果他们的基本答案是：“我被教着这样做，但从没有想到过为什么。”这会给我留下一个坏印象。 <br />第三个方案是用 goto <br />Loop: <br />... <br />goto Loop; <br />应试者如给出上面的方案，这说明或者他是一个汇编语言程序员（这也许是好事）或者他是一个想进入新领域的BASIC/FORTRAN程序员。<br /><br />数据声明（Data declarations） <br /><br /><font color="#ff0000">5. 用变量a给出下面的定义</font><br />a) 一个整型数（An integer） <br />b) 一个指向整型数的指针（A pointer to an integer） <br />c) 一个指向指针的的指针，它指向的指针是指向一个整型数（A pointer to a pointer to an integer） <br />d) 一个有10个整型数的数组（An array of 10 integers） <br />e) 一个有10个指针的数组，该指针是指向一个整型数的（An array of 10 pointers to integers） <br />f) 一个指向有10个整型数数组的指针（A pointer to an array of 10 integers） <br />g) 一个指向函数的指针，该函数有一个整型参数并返回一个整型数（A pointer to a function that takes an integer as an argument and returns an integer） <br />h) 一个有10个指针的数组，该指针指向一个函数，该函数有一个整型参数并返回一个整型数（ An array of ten pointers to functions that take an integer argument and return an integer ）<br /><br />答案是： <br />a) int a; // An integer <br />b) int *a; // A pointer to an integer <br />c) int **a; // A pointer to a pointer to an integer <br />d) int a[10]; // An array of 10 integers <br />e) int *a[10]; // An array of 10 pointers to integers <br />f) int (*a)[10]; // A pointer to an array of 10 integers <br />g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer <br />h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer <br /><br /><br />人们经常声称这里有几个问题是那种要翻一下书才能回答的问题，我同意这种说法。当我写这篇文章时，为了确定语法的正确性，我的确查了一下书。 <br />但是当我被面试的时候，我期望被问到这个问题（或者相近的问题）。因为在被面试的这段时间里，我确定我知道这个问题的答案。应试者如果不知道 <br />所有的答案（或至少大部分答案），那么也就没有为这次面试做准备，如果该面试者没有为这次面试做准备，那么他又能为什么出准备呢？<br /><br /><br />Static<br /><br /><font color="#ff0000">6. 关键字static的作用是什么？</font><br /><br />这个简单的问题很少有人能回答完全。在C语言中，关键字static有三个明显的作用： <br />1). 在函数体，一个被声明为静态的变量在这一函数被调用过程中维持其值不变。 <br />2). 在模块内（但在函数体外），一个被声明为静态的变量可以被模块内所用函数访问，但不能被模块外其它函数访问。它是一个本地的全局变量。 <br />3). 在模块内，一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是，这个函数被限制在声明它的模块的本地范围内使用。 <br />大多数应试者能正确回答第一部分，一部分能正确回答第二部分，同是很少的人能懂得第三部分。这是一个应试者的严重的缺点，因为他显然不懂得本地化数据和代码范围的好处和重要性。<br /><br /><br />Const <br /><br /><font color="#ff0000">7．关键字const是什么含意？</font><br />我只要一听到被面试者说：“const意味着常数”，我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法，因此ESP(译者：Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章，只要能说出const意味着“只读”就可以了。尽管这个答案不是完全的答案，但我接受它作为一个正确的答案。（如果你想知道更详细的答案，仔细读一下Saks的文章吧。）如果应试者能正确回答这个问题，我将问他一个附加的问题：下面的声明都是什么意思？<br /><br />const int a; <br />int const a; <br />const int *a; <br />int * const a; <br />int const * a const;<br /><br />前两个的作用是一样，a是一个常整型数。第三个意味着a是一个指向常整型数的指针（也就是，整型数是不可修改的，但指针可以）。第四个意思a是一个指向整型数的常指针（也就是说，指针指向的整型数是可以修改的，但指针是不可修改的）。最后一个意味着a是一个指向常整型数的常指针（也就是说，指针指向的整型数是不可修改的，同时指针也是不可修改的）。如果应试者能正确回答这些问题，那么他就给我留下了一个好印象。顺带提一句，也许你可能会问，即使不用关键字const，也还是能很容易写出功能正确的程序，那么我为什么还要如此看重关键字const呢？我也如下的几下理由： <br />1). 关键字const的作用是为给读你代码的人传达非常有用的信息，实际上，声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾，你就会很快学会感谢这点多余的信息。（当然，懂得用const的程序员很少会留下的垃圾让别人来清理的。） <br />2). 通过给优化器一些附加的信息，使用关键字const也许能产生更紧凑的代码。 <br />3). 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数，防止其被无意的代码修改。简而言之，这样可以减少bug的出现。<br /><br />Volatile <br /><br /><font color="#ff0000">8. 关键字volatile有什么含意 并给出三个不同的例子。</font><br /><br />一个定义为volatile的变量是说这变量可能会被意想不到地改变，这样，编译器就不会去假设这个变量的值了。精确地说就是，优化器在用到这个变量时必须每次都小心地重新读取这个变量的值，而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子： <br />1). 并行设备的硬件寄存器（如：状态寄存器） <br />2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) <br />3). 多线程应用中被几个任务共享的变量 <br />回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道，所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。 <br />假设被面试者正确地回答了这是问题（嗯，怀疑这否会是这样），我将稍微深究一下，看一下这家伙是不是直正懂得volatile完全的重要性。 <br />1). 一个参数既可以是const还可以是volatile吗？解释为什么。 <br />2). 一个指针可以是volatile 吗？解释为什么。 <br />3). 下面的函数有什么错误： <br />int square(volatile int *ptr) <br />{ <br />return *ptr * *ptr; <br />} <br />下面是答案： <br />1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。 <br />2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。 <br />3). 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方，但是，由于*ptr指向一个volatile型参数，编译器将产生类似下面的代码： <br />int square(volatile int *ptr) <br />{ <br />int a,b; <br />a = *ptr; <br />b = *ptr; <br />return a * b; <br />} <br />由于*ptr的值可能被意想不到地该变，因此a和b可能是不同的。结果，这段代码可能返不是你所期望的平方值！正确的代码如下： <br />long square(volatile int *ptr) <br />{ <br />int a; <br />a = *ptr; <br />return a * a; <br />}<br /><br />位操作（Bit manipulation）<br /><br /><font color="#ff0000">9. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a，写两段代码，第一个设置a的bit 3，第二个清除a 的bit 3。在以上两个操作中，要保持其它位不变。</font><br /><br />对这个问题有三种基本的反应 <br />1). 不知道如何下手。该被面者从没做过任何嵌入式系统的工作。 <br />2). 用bit fields。Bit fields是被扔到C语言死角的东西，它保证你的代码在不同编译器之间是不可移植的，同时也保证了的你的代码是不可重用的。我最近不幸看到Infineon为其较复杂的通信芯片写的驱动程序，它用到了bit fields因此完全对我无用，因为我的编译器用其它的方式来实现bit fields的。从道德讲：永远不要让一个非嵌入式的家伙粘实际硬件的边。 <br />3). 用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法，是应该被用到的方法。最佳的解决方案如下： <br />#define BIT3 (0x1&lt;&lt;3) <br />static int a; <br />void set_bit3(void) <br />{ <br />a |= BIT3; <br />} <br />void clear_bit3(void) <br />{ <br />a &amp;= ~BIT3; <br />} <br />一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数，这也是可以接受的。我希望看到几个要点：说明常数、|=和&amp;=~操作。<br /><br />访问固定的内存位置（Accessing fixed memory locations） <br /><br /><font color="#ff0000">10. 嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中，要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。<br /></font><br />这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换（typecast）为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下： <br />int *ptr; <br />ptr = (int *)0x67a9; <br />*ptr = 0xaa55;<br /><br />一个较晦涩的方法是： <br />*(int * const)(0x67a9) = 0xaa55;<br /><br />即使你的品味更接近第二种方案，但我建议你在面试时使用第一种方案。<br /><br />中断（Interrupts） <br /><br /><font color="#ff0000">11. 中断是嵌入式系统中重要的组成部分，这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是，产生了一个新的关键字__interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR)，请评论一下这段代码的。</font><br /><br />__interrupt double compute_area (double radius) <br />{ <br />    double area = PI * radius * radius; <br />    printf(" Area = %f", area); <br />    return area; <br />}<br /><br />这个函数有太多的错误了，以至让人不知从何说起了： <br />1). ISR 不能返回一个值。如果你不懂这个，那么你不会被雇用的。 <br />2). ISR 不能传递参数。如果你没有看到这一点，你被雇用的机会等同第一项。 <br />3). 在许多的处理器/编译器中，浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈，有些处理器/编译器就是不允许在ISR中做浮点运算。此外，ISR应该是短而有效率的，在ISR中做浮点运算是不明智的。 <br />4). 与第三点一脉相承，printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点，我不会太为难你的。不用说，如果你能得到后两点，那么你的被雇用前景越来越光明了。<br /><br />代码例子（Code examples）<br /><font color="#ff0000">12 . 下面的代码输出是什么，为什么？</font><br /><br />void foo(void) <br />{ <br />    unsigned int a = 6; <br />    int b = -20; <br />    (a+b &gt; 6) puts("&gt; 6") : puts("&lt;= 6"); <br />}<br /><br />这个问题测试你是否懂得C语言中的整数自动转换原则，我发现有些开发者懂得极少这些东西。不管如何，这无符号整型问题的答案是输出是“&gt;6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。 因此-20变成了一个非常大的正整数，所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题，你也就到了得不到这份工作的边缘。<br /><br /><font color="#ff0000">13. 评价下面的代码片断：</font><br /><br />unsigned int zero = 0; <br />unsigned int compzero = 0xFFFF; <br />/*1's complement of zero */<br /><br />对于一个int型不是16位的处理器为说，上面的代码是不正确的。应编写如下：<br /><br />unsigned int compzero = ~0;<br /><br />这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里，好的嵌入式程序员非常准确地明白硬件的细节和它的局限，然而PC机程序往往把硬件作为一个无法避免的烦恼。 <br />到了这个阶段，应试者或者完全垂头丧气了或者信心满满志在必得。如果显然应试者不是很好，那么这个测试就在这里结束了。但如果显然应试者做得不错，那么我就扔出下面的追加问题，这些问题是比较难的，我想仅仅非常优秀的应试者能做得不错。提出这些问题，我希望更多看到应试者应付问题的方法，而不是答案。不管如何，你就当是这个娱乐吧…<br /><br /><br /><br />动态内存分配（Dynamic memory allocation）<br /><br /><br /><br /><font color="#ff0000">14. 尽管不像非嵌入式计算机那么常见，嵌入式系统还是有从堆（heap）中动态分配内存的过程的。那么嵌入式系统中，动态分配内存可能发生的问题是什么？</font><br /><br />这里，我期望应试者能提到内存碎片，碎片收集的问题，变量的持行时间等等。这个主题已经在ESP杂志中被广泛地讨论过了（主要是 P.J. Plauger, 他的解释远远超过我这里能提到的任何解释），所有回过头看一下这些杂志吧！让应试者进入一种虚假的安全感觉后，我拿出这么一个小节目：下面的代码片段的输出是什么，为什么？<br /><br />char *ptr; <br />if ((ptr = (char *)malloc(0)) == NULL) <br />puts("Got a null pointer"); <br />else <br />puts("Got a valid pointer"); <br /><br />这是一个有趣的问题。最近在我的一个同事不经意把0值传给了函数malloc，得到了一个合法的指针之后，我才想到这个问题。这就是上面的代码，该代码的输出是“Got a valid pointer”。我用这个来开始讨论这样的一问题，看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要，但解决问题的方法和你做决定的基本原理更重要些。<br /><br />Typedef <br /><br /><font color="#ff0000">15. Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如，思考一下下面的例子：</font><br />#define dPS struct s * <br />typedef struct s * tPS; <br /><br />以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。哪种方法更好呢？（如果有的话）为什么？ <br />这是一个非常微妙的问题，任何人答对这个问题（正当的原因）是应当被恭喜的。答案是：typedef更好。思考下面的例子： <br />dPS p1,p2; <br />tPS p3,p4;<br /><br />第一个扩展为 <br />struct s * p1, p2;<br /><br />上面的代码定义p1为一个指向结构的指，p2为一个实际的结构，这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。<br /><br />晦涩的语法<br /><br /><font color="#ff0000">16. C语言同意一些令人震惊的结构,下面的结构是合法的吗，如果是它做些什么？ <br /></font>int a = 5, b = 7, c; <br />c = a+++b;<br /><br />这个问题将做为这个测验的一个愉快的结尾。不管你相不相信，上面的例子是完全合乎语法的。问题是编译器如何处理它？水平不高的编译作者实际上会争论这个问题，根据最处理原则，编译器应当能处理尽可能所有合法的用法。因此，上面的代码被处理成： <br />c = a++ + b; <br />因此, 这段代码持行后a = 6, b = 7, c = 12。 <br />如果你知道答案，或猜出正确答案，做得好。如果你不知道答案，我也不把这个当作问题。我发现这个问题的最大好处是:这是一个关于代码编写风格，代码的可读性，代码的可修改性的好的话题</font>
		</p>
<img src ="http://www.cppblog.com/oosky/aggbug/11763.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-08-28 09:51 <a href="http://www.cppblog.com/oosky/archive/2006/08/28/11763.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>KMP算法祥解</title><link>http://www.cppblog.com/oosky/archive/2006/07/06/9486.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Thu, 06 Jul 2006 06:02:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/07/06/9486.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/9486.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/07/06/9486.html#Feedback</comments><slash:comments>38</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/9486.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/9486.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: KMP算法详解&nbsp;&nbsp;<a href='http://www.cppblog.com/oosky/archive/2006/07/06/9486.html'>阅读全文</a><img src ="http://www.cppblog.com/oosky/aggbug/9486.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-07-06 14:02 <a href="http://www.cppblog.com/oosky/archive/2006/07/06/9486.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Boost安装</title><link>http://www.cppblog.com/oosky/archive/2006/06/30/9243.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Fri, 30 Jun 2006 09:08:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/06/30/9243.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/9243.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/06/30/9243.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/9243.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/9243.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Boost安装&nbsp;&nbsp;<a href='http://www.cppblog.com/oosky/archive/2006/06/30/9243.html'>阅读全文</a><img src ="http://www.cppblog.com/oosky/aggbug/9243.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-06-30 17:08 <a href="http://www.cppblog.com/oosky/archive/2006/06/30/9243.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>难道C/C++是块难啃的骨头，这里落户的兄弟太少了</title><link>http://www.cppblog.com/oosky/archive/2006/06/01/8022.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Thu, 01 Jun 2006 04:50:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/06/01/8022.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/8022.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/06/01/8022.html#Feedback</comments><slash:comments>21</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/8022.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/8022.html</trackback:ping><description><![CDATA[
		<br />在Cppblog落户的兄弟怎么还那么少呢？<br /><br />看这首页中的统计信息，少的可怜。1K用户都不到，.NET区的一个零头都不到。<br /><ul><li>
		博客 - 822
		
	</li><li>
		随笔 - 2917
		
	</li><li>
		文章 - 1217
		
	</li><li>
		评论 - 2784
		
	</li></ul>再来看看.NET区：<br /><ul><li>
		博客 - 15618
		
	</li><li>
		随笔 - 108752
		
	</li><li>
		文章 - 34156
		
	</li><li>
		评论 - 208492
		
	</li></ul>看来大伙得想想办法能热闹点。<br />dudu也来想想办法。<br /><img src ="http://www.cppblog.com/oosky/aggbug/8022.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-06-01 12:50 <a href="http://www.cppblog.com/oosky/archive/2006/06/01/8022.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>推荐两款浏览器-FireFox&amp;Maxthon</title><link>http://www.cppblog.com/oosky/archive/2006/04/30/6498.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Sun, 30 Apr 2006 11:05:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/04/30/6498.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/6498.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/04/30/6498.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/6498.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/6498.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 两款浏览器-FireFox&Maxthon&nbsp;&nbsp;<a href='http://www.cppblog.com/oosky/archive/2006/04/30/6498.html'>阅读全文</a><img src ="http://www.cppblog.com/oosky/aggbug/6498.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-04-30 19:05 <a href="http://www.cppblog.com/oosky/archive/2006/04/30/6498.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>打造UltralEdit-32为C/C++编译器</title><link>http://www.cppblog.com/oosky/archive/2006/04/20/5936.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Thu, 20 Apr 2006 06:24:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/04/20/5936.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/5936.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/04/20/5936.html#Feedback</comments><slash:comments>16</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/5936.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/5936.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: UltralEdit-32不仅是一个强大的编辑器，而且还可以编译C/C++，Java，Python程序，一个方便的编译器。&nbsp;&nbsp;<a href='http://www.cppblog.com/oosky/archive/2006/04/20/5936.html'>阅读全文</a><img src ="http://www.cppblog.com/oosky/aggbug/5936.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-04-20 14:24 <a href="http://www.cppblog.com/oosky/archive/2006/04/20/5936.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC6 + ICC8.1+...VC6与VS2005媲美</title><link>http://www.cppblog.com/oosky/archive/2006/04/10/5255.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Mon, 10 Apr 2006 11:22:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/04/10/5255.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/5255.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/04/10/5255.html#Feedback</comments><slash:comments>28</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/5255.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/5255.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 因为VC6自带的编译器对C++支持不好，好像还不支持C98标准吧，反正是很久以前的标准了，Intel自家的编译器应该是比较优秀的。听说VC的编译器就是MS和Intel一起搞的，现在Intel想独立门户了吧。<br>下面自己去体验吧！<br>&nbsp;&nbsp;<a href='http://www.cppblog.com/oosky/archive/2006/04/10/5255.html'>阅读全文</a><img src ="http://www.cppblog.com/oosky/aggbug/5255.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-04-10 19:22 <a href="http://www.cppblog.com/oosky/archive/2006/04/10/5255.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>华为软件编程规范和范例</title><link>http://www.cppblog.com/oosky/archive/2006/03/26/4625.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Sun, 26 Mar 2006 09:04:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/03/26/4625.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/4625.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/03/26/4625.html#Feedback</comments><slash:comments>13</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/4625.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/4625.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: [ 										华为软件编程规范和范例 ]								 														 																																				〔一〕=====[排版] ]=======. 																																														...&nbsp;&nbsp;<a href='http://www.cppblog.com/oosky/archive/2006/03/26/4625.html'>阅读全文</a><img src ="http://www.cppblog.com/oosky/aggbug/4625.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-03-26 17:04 <a href="http://www.cppblog.com/oosky/archive/2006/03/26/4625.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言超浓缩教程</title><link>http://www.cppblog.com/oosky/archive/2006/02/20/3354.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Mon, 20 Feb 2006 08:17:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/02/20/3354.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/3354.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/02/20/3354.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/3354.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/3354.html</trackback:ping><description><![CDATA[<P><FONT size=2>&nbsp;&nbsp;&nbsp; “ 哎哟，哥们儿，还捣鼓汇编呢？那东西没用，兄弟用VB"钓"一个API就够你忙活个十天半月的，还不一定搞出来。”此君之言倒也不虚，那吾等还有无必要研他一究呢？（废话，当然有啦！要不然你写这篇文章干嘛。）别急，别急，让我把这个中原委慢慢道来：一、所有电脑语言写出的程序运行时在内存中都以机器码方式存储，机器码可以被比较准确的翻译成汇编语言，这是因为汇编语言兼容性最好，故几乎所有跟踪、调试工具（包括WIN95/98下）都是以汇编示人的，如果阁下对CRACK颇感兴趣……；二、汇编直接与硬件打交道，如果你想搞通程序在执行时在电脑中的来龙去脉，也就是搞清电脑每个组成部分究竟在干什么、究竟怎么干？一个真正的硬件发烧友，不懂这些可不行。三、如今玩DOS的多是“高手”，如能像吾一样混入（我不是高手）“高手”内部，不仅可以从“高手”朋友那儿套些黑客级“机密”，还可以自诩“高手”尽情享受强烈的虚荣感--#$%&amp; “醒醒!” </FONT></P>
<P><FONT size=2>　　对初学者而言，汇编的许多命令太复杂，往往学习很长时间也写不出一个漂漂亮亮的程序，以致妨碍了我们学习汇编的兴趣，不少人就此放弃。所以我个人看法学汇编，不一定要写程序，写程序确实不是汇编的强项，大家不妨玩玩DEBUG，有时CRACK出一个小软件比完成一个程序更有成就感（就像学电脑先玩游戏一样）。某些高深的指令事实上只对有经验的汇编程序员有用，对我们而言，太过高深了。为了使学习汇编语言有个好的开始，你必须要先排除那些华丽复杂的命令，将注意力集中在最重要的几个指令上（CMP LOOP MOV JNZ……）。但是想在啰里吧嗦的教科书中完成上述目标，谈何容易，所以本人整理了这篇超浓缩（用WINZIP、WINRAR…依次压迫，嘿嘿！）教程。大言不惭的说，看通本文，你完全可以“不经意”间在前辈或是后生卖弄一下DEBUG，很有成就感的，试试看！那么――这个接下来呢？―― Here we go！（阅读时看不懂不要紧，下文必有分解）</FONT></P>
<P><FONT size=2>　　因为汇编是通过CPU和内存跟硬件对话的，所以我们不得不先了解一下CPU和内存：（关于数的进制问题在此不提）</FONT></P>
<P><FONT size=2>　　ＣＰＵ是可以执行电脑所有算术╱逻辑运算与基本 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位寄存器：ＡＨ&amp;ＡＬ＝ＡＸ：累加寄存器，常用于运算；ＢＨ&amp;ＢＬ＝ＢＸ：基址寄存器，常用于地址索引；ＣＨ&amp;ＣＬ＝ＣＸ：计数寄存器，常用于计数；ＤＨ&amp;ＤＬ＝ＤＸ：数据寄存器，常用于数据传递。为了运用所有的内存空间，8086设定了四个段寄存器，专门用来保存段地址：ＣＳ（Code Segment）：代码段寄存器；ＤＳ（Data Segment）：数据段寄存器；ＳＳ（Stack Segment）：堆栈段寄存器；ＥＳ（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）,有九个有意义的标志，将在下文用到时详细说明。</FONT></P>
<P><FONT size=2>　　内存是电脑运作中的关键部分，也是电脑在工作中储存信息的地方。内存组织有许多可存放数值的储存位置，叫“地址”。8086地址总线有20位，所以CPU拥有达1M的寻址空间，这也是DOS的有效控制范围，而8086能做的运算仅限于处理16位数据，即只有0到64K，所以，必须用分段寻址才能控制整个内存地址。完整的20位地址可分成两部份：1.段基址(Segment)：16位二进制数后面加上四个二进制０，即一个16进制０，变成20位二进制数，可设定1M中任何一个64K段，通常记做16位二进制数；2.偏移量(Offset)：直接使用16位二进制数，指向段基址中的任何一个地址。如：2222（段基址）:3333（偏移量），其实际的20位地址值为：25553。除了上述营养要充分吸收外，你还要知道什么是DOS、BIOS功能调用，简单的说，功能调用类似于WIN95 API，相当于子程序。汇编写程序已经够要命了，如果不用MS、IBM的子程序，这日子真是没法过了（关于功能调用详见《电脑爱好者》98年11期）。</FONT></P>
<P><FONT size=2>　　编写汇编语言有两种主要的方法：1.使用MASM或TASM等编译器；2.使用除错程序DEBUG.COM。DEBUG其实并不能算是一个编译器，它的主要用途在于除错，即修正汇编程序中的错误。不过，也可以用来写短的汇编程序，尤其对初学者而言，DEBUG 更是最佳的入门工具。因为DEBUG操作容易：只要键入DEBUG回车，A回车即可进行汇编，过程简单，而使用编译器时，必须用到文本编辑器、编译器本身、LINK以及EXE2BIN等程序，其中每一个程序都必须用到一系列相当复杂的命令才能工作，而且用编译器处理源程序，必须加入许多与指令语句无关的指示性语句，以供编译器识别，使用 DEBUG 可以避免一开始就碰到许多难以理解的程序行。DEBUG 除了能够汇编程序之外，还可用来检查和修改内存位置、载入储存和执行程序、以及检查和修改寄存器，换句话说，DEBUG是为了让我们接触硬件而设计的。（8086常用指令用法将在每个汇编程序中讲解，限于篇幅，不可能将所有指令列出）。</FONT></P>
<P><FONT size=2>　　DEBUG的的A命令可以汇编出简单的COM文件，所以DEBUG编写的程序一定要由地址 100h（COM文件要求）开始才合法。FOLLOW ME，SETP BY SETP（步步回车）：</FONT></P>
<P><FONT size=2>　　输入 A100 ； 从DS：100开始汇编<BR>　　2.输入 MOV DL,1 ； 将数值 01h 装入 DL 寄存器<BR>　　3.输入 MOV AH,2 ； 将数值 02h 装入 DL 寄存器<BR>　　4.输入 INT 21 ； 调用DOS 21号中断2号功能，用来逐个显示装入DL的字符<BR>　　5.输入 INT 20 ； 调用DOS 20号中断，终止程序，将控制权交回给 DEBUG<BR>　　6.请按 Enter 键<BR>　　7.现在已将汇编语言程序放入内存中了，输入 G(运行)<BR>　　8.出现结果：输出一个符号。<BR>　　ㄖ ←输出结果其实不是它，因WORD97无法显示原结果，故找一赝品将就着。<BR>　　Program terminated normally</FONT></P>
<P><FONT size=2>　　我们可以用Ｕ命令将十六进制的机器码反汇编（Unassemble）成汇编指令。你将发现每一行右边的汇编指令就是被汇编成相应的机器码，而8086实际上就是以机器码来执行程序。<BR>　　1.输入 U100,106<BR>　　1FED:0100 B201 MOV DL,01<BR>　　1FED:0102 B402 MOV AH,02<BR>　　1FED:0104 CD21 INT 21<BR>　　1FED:0106 CD20 INT 20<BR>　　DEBUG可以用Ｒ命令来查看、改变寄存器内容。CS：IP寄存器，保存了将执行指令地址。<BR>　　1.输入R<BR>　　AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000<BR>　　DS=1FED ES=1FED SS=1FED CS=1FED IP=0100 NV UP EI PL NZ NA PO NC<BR>　　1FED:0100 B201 MOV DL,01</FONT></P>
<P><FONT size=2>　　当程序由DS：100开始执行，那么终止程序时，DEBUG会自动将IP内容重新设定为100。当你要将此程序做成一个独立的可执行文件，则可以用Ｎ命令对该程序命名。但一定要为COM文件，否则无法以DEBUG载入。<BR>　　输入N SMILE.COM ；我们得告诉DEBUG程序长度：程序从100开始到106，故占用7<BR>　　；字节。我们利用BX存放长度值高位部分，而以CX存放低位部分。<BR>　　2.输入RBX ；查看 BX 寄存器的内容，本程序只有7个字节，故本步可省略<BR>　　3.输入 RCX　 ；查看 CX 寄存器的内容<BR>　　4.输入 7　 ；程序的字节数<BR>　　5.输入 W ；用Ｗ命令将该程序写入（Write）磁盘中</FONT></P>
<P><FONT size=2>　　修行至此，我们便可以真正接触8086汇编指令了。 当我们写汇编语言程序的时候，通常不会直接将机器码放入内存中，而是打入一串助记符号（Mnemonic Symbols），这些符号比十六进制机器码更容易记住，此之谓汇编指令。助记符号，告诉CPU应执行何种运算。 也就是说，助忆符号所构成的汇编语言是为人设计的，而机器语言是对PC设计的。 </FONT></P>
<P><FONT size=2>　　现在，我们再来剖析一个可以将所有ASCII码显示出来的程序。<BR>　　1. 输入 DEBUG<BR>　　2. 输入 A100<BR>　　3．输入 MOV CX,0100 ；装入循环次数<BR>　　MOV DL,00 ；装入第一个ASCII码，随后每次循环装入新码<BR>　　MOV AH,02<BR>　　INT 21<BR>　　INC DL ；INC：递增指令，每次将数据寄存器 DL 内的数值加 1<BR>　　LOOP 0105 ；LOOP：循环指令，每执行一次LOOP，CX值减1，并跳<BR>　　；到循环的起始地址105，直到CX为0，循环停止<BR>　　INT 20<BR>　　4.输入 G即可显示所有ASCII码<BR>　<BR>　　当我们想任意显示字符串，如：UNDERSTAND？，则可以使用DOS21H号中断9H号功能。输入下行程序，存盘并执行看看：<BR>　　1.输入 A100<BR>　　 MOV DX,109 ；DS:DX ＝ 字符串的起始地址<BR>　　 MOV AH,9 ；DOS的09h功能调用<BR>　　INT 21 ；字符串输出<BR>　　INT 20<BR>　　DB 'UNDERSTAND？$'；定义字符串 </FONT></P>
<P><FONT size=2>　　在汇编语言中，有两种不同的指令：1.正规指令：如 MOV 等，是属于CPU的指令，用来告诉CPU在程序执行时应做些什么，所以它会以运算码（OP-code）的方式存入内存中；2.伪指令：如DB等，是属于DEBUG等编译器的指令，用来告诉编译器在编译时应做些什么。DB（Define Byte）指令用来告诉DEBUG 将单引号内的所有ASCII 码放入内存中。使用 9H 功能的字符串必须以$结尾。用Ｄ命令可用来查看DB伪指令将那些内容放入内存。<BR>　　6.输入 D100<BR>　　1975:0100 BA 09 01 B4 09 CD 21 CD-20 75 6E 64 65 72 73 74 ......!. underst<BR>　　1975:0110 61 6E 64 24 8B 46 F8 89-45 04 8B 46 34 00 64 19 and$.F..E..F4.d.<BR>　　1975:0120 89 45 02 33 C0 5E 5F C9-C3 00 C8 04 00 00 57 56 .E.3.^_.......WV<BR>　　1975:0130 6B F8 0E 81 C7 FE 53 8B-DF 8B C2 E8 32 FE 0B C0 k.....S.....2...<BR>　　1975:0140 74 05 33 C0 99 EB 17 8B-45 0C E8 D4 97 8B F0 89 t.3.....E.......<BR>　　1975:0150 56 FE 0B D0 74 EC 8B 45-08 03 C6 8B 56 FE 5E 5F V...t..E....V.^_<BR>　　1975:0160 C9 C3 C8 02 00 00 6B D8-0E 81 C3 FE 53 89 5E FE ......k.....S.^.<BR>　　1975:0170 8B C2 E8 FB FD 0B C0 75-09 8B 5E FE 8B 47 0C E8 .......u..^..G..</FONT></P>
<P><FONT size=2>　　现在，我们来剖析另一个程序：由键盘输入任意字符串，然后显示出来。db 20指示DEBUG保留20h个未用的内存空间供缓冲区使用。<BR>　　输入A100<BR>　　 MOV DX,0116 ；DS:DX ＝ 缓冲区地址，由DB伪指令确定缓冲区地址<BR>　　MOV AH,0A ；0Ah 号功能调用<BR>　　INT 21 ；键盘输入缓冲区<BR>　　MOV DL,0A ；由于功能Ah在每个字符串最后加一个归位码（0Dh由 Enter<BR>　　MOV AH,02 ；产生），使光标自动回到输入行的最前端，为了使新输出的<BR>　　INT 21 ；字符串不会盖掉原来输入的字符串，所以利用功能2h加一<BR>　　；个换行码(OAh)，使得光标移到下一行的的最前端。<BR>　　MOV DX,0118 ；装入字符串的起始位置<BR>　　MOV AH,09 ；9h功能遇到$符号才会停止输出，故字符串最后必须加上<BR>　　INT 21 ；$，否则9h功能会继续将内存中的无用数据胡乱显示出来<BR>　　INT 20<BR>　　DB 20 ；定义缓冲区 <BR>　　送你一句话：学汇编切忌心浮气燥。</FONT></P>
<P><FONT size=2>　　客套话就不讲了。工欲善其事，必先利其器。与其说DEBUG 是编译器，倒不如说它是“直译器”，DEBUG的A命令只可将一行汇编指令转成机器语言，且立刻执行。真正编译器（MASM）的运作是利用文本编辑器（EDIT等）将汇编指令建成一个独立且附加名为.ASM的文本文件，称源程序。它是MASM 程序的输入部分。MASM将输入的ASM文件，编译成.OBJ文件，称为目标程序。OBJ文件仅包含有关程序各部份要载入何处及如何与其他程序合并的信息，无法直接载入内存执行。链结程序LINK则可将OBJ文件转换成可载入内存执行（EXEcute）的EXE文件。还可以用EXE2BIN，将符合条件的EXE文件转成COM文件（COM 文件不但占用的内存最少，而且运行速度最快）。<BR>　　下面我们用MASM写一个与用DEBUG写的第一个程序功能一样的程序。<BR>　　用EDIT编辑一个SMILE.ASM的源程序文件。<BR>　　源程序 DEBUG 程序<BR>　　prognam segment<BR>　　assume cs:prognam<BR>　　org 100h A100<BR>　　mov dl,1 mov dl,1<BR>　　mov ah,2 mov ah,2<BR>　　int 21h int 21<BR>　　int 20h int 20<BR>　　prognam ends<BR>　　end</FONT></P>
<P><FONT size=2>　　比较一下：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。<BR>　　输入 MASM SMILE ←不用打入附加名.ASM。<BR>　　Microsoft (R) Macro Assembler Version 5.10<BR>　　Copyright (C) Microsoft Corp 1981, 1988. All rights reserved.<BR>　　Object filename [SMILE.OBJ]: ←是否改动输出OBJ文件名，如不改就ENTER<BR>　　Source listing [NUL.LST]: ← 是否需要列表文件（LST），不需要就ENTER<BR>　　Cross-reference [NUL.CRF]: ←是否需要对照文件（CRF），不需要则ENTER<BR>　　50162 + 403867 Bytes symbol space free<BR>　　0 Warning Errors ←警告错误，表示编译器对某些语句不理解，通常是输入错误。<BR>　　0 Severe Errors ←严重错误，会造成程序无法执行，通常是语法结构错误。</FONT></P>
<P><FONT size=2>　　如果没有一个错误存在，即可生成OBJ文件。OBJ中包含的是编译后的二进制结果，它还无法被 DOS载入内存中加以执行，必须加以链结（Linking）。以LINK将OBJ文件（SMILE.OBJ）链结成 EXE 文件（SMILE.EXE）时，。<BR>　　1.输入 LINK SMILE ←不用附加名OBJ<BR>　　Microsoft (R) Overlay Linker Version 3.64<BR>　　Copyright (C) Microsoft Corp 1981, 1988. All rights reserved.<BR>　　Run File [SMILE.EXE]: ← 是否改动输出EXE文件名，如不改就ENTER<BR>　　List File [NUL.MAP]: ← 是否需要列表文件（MAP），不需要则ENTER<BR>　　Libraries [.LIB]: ←是否需要库文件，要就键入文件名，不要则ENTER<BR>　　LINK : warning L4021: no stack segment← 由于COM文件不使用堆栈段，所以错误信息<BR>　　←"no stack segment"并不影响程序正常执行</FONT></P>
<P><FONT size=2>　　至此已经生成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：&gt;下，直接输入文件名称 SMILE ，就可以执行这个程序了。</FONT></P>
<P><FONT size=2>　　你是否觉得用编译器产生程序的方法，比 DEBUG 麻烦多了！以小程序而言，的确是如此，但对于较大的程序，你就会发现其优点了。我们再将ASCII程序以编译器方式再做一次，看看有无差异。首先，用EDIT.COM建立 ASCII.ASM 文件。<BR>　　prognam segment ;定义段<BR>　　assume cs:prognam ;把上面定义段的段基址放入 CS<BR>　　mov cx,100h ; 装入循环次数<BR>　　mov dl,0 ; 装入第一个ASCII码，随后每次循环装入新码<BR>　　next: mov ah,2<BR>　　 int 21h<BR>　　 inc dl ;INC：递增指令，每次将数据寄存器 DL 内的数值加 1<BR>　　loop next ; 循环指令，执行一次，CX减1，直到CX为0，循环停止<BR>　　int 20h<BR>　　 prognam ends ;段终止<BR>　　end ;汇编终止<BR>　　在汇编语言的源程序中，每一个程序行都包含三项元素：<BR>　　　 start: mov dl,1 ；装入第一个ASCII码，随后每次循环装入新码<BR>　　　 标识符 表达式 注解</FONT></P>
<P><FONT size=2>　　在原始文件中加上注解可使程序更易理解，便于以后参考。每行注解以“；”与程序行分离。编译器对注解不予理会，注解的数据不会出现在OBJ、EXE或COM文件中。由于我们在写源程序时，并不知道每一程序行的地址，所以必须以符号名称来代表相对地址，称为“标识符”。我们通常在适当行的适当位置上，键入标识符。标识符（label）最长可达31 个字节，因此我们在程序中，尽量以简洁的文字做为标识符。现在，你可以将此ASCII.ASM 文件编译成 ASCII.COM 了。1.MASM ASCII，2.LINK ASCII，3.EXE2BIN ASCII ASCII.COM。</FONT></P>
<P><FONT size=2>　　注意：当你以编译器汇编你设计的程序时，常会发生打字错误、标识符名称拼错、十六进制数少了ｈ、逻辑错误等。汇编老手常给新人的忠告是：最好料到自己所写的程序一定会有些错误（别人告诉我的）；如果第一次执行程序后，就得到期望的结果，你最好还是在检查一遍，因为它可能是错的。原则上，只要大体的逻辑架构正确，查找程序中错误的过程，与写程序本身相比甚至更有意思。写大程序时，最好能分成许多模块，如此可使程序本身的目的较单纯，易于撰写与查错，另外也可让程序中不同部份之间的界限较清楚，节省编译的时间。如果读程序有读不懂的地方最好用纸笔记下有关寄存器、内存等内容，在纸上慢慢比划，就豁然开朗了。 　　下面我们将写一个能从键盘取得一个十进制的数值，并将其转换成十六进制数值而显示于屏幕上的“大程序”。前言：要让8086执行这样的功能，我们必须先将此问题分解成一连串的步骤，称为程序规划。首先，以流程图的方式，来确保整个程序在逻辑上没有问题（不用说了吧！什么语言都要有此步骤）。这种模块化的规划方式，称之为“由上而下的程序规划”。而在真正写程序时，却是从最小的单位模块（子程序）开始，当每个模块都完成之后，再合并成大程序；这种大处著眼，小处著手的方式称为“由下而上的程序设计”。</FONT></P>
<P><FONT size=2>　　我们的第一个模块是BINIHEX，其主要用途是从8086的BX寄存器中取出二进制数，并以十六进制方式显示在屏幕上。注意：子程序如不能独立运行，实属正常。<BR>　　 binihex segment<BR>　　 assume cs:binihex<BR>　　mov ch,4 ;记录转换后的十六进制位数（四位）<BR>　　rotate: mov cl,4 ;利用CL当计数器，记录寄存器数位移动次数<BR>　　rol bx,cl ;循环寄存器BX的内容，以便依序处理4个十六进制数<BR>　　mov al,bl ;把bx低八位bl内数据转移至al<BR>　　and al,0fh ;把无用位清零<BR>　　add al,30h ;把AL内数据加30H，并存入al<BR>　　cmp al,3ah ;与3ah比较<BR>　　jl printit ;小于3ah则转移<BR>　　add al,7h ;把AL内数据加30H，并存入al<BR>　　printit:mov dl,al ;把ASCII码装入DL<BR>　　mov ah,2<BR>　　 int 21h<BR>　　 dec ch ;ch减一，减到零时，零标志置1<BR>　　jnz rotate ;JNZ：当零标志未置1，则跳到指定地址。即：不等，则转移<BR>　　int 20h ;从子程序退回主程序<BR>　　binihex ends<BR>　　 end</FONT></P>
<P><FONT size=2>　　利用循环左移指令ROL循环寄存器BX(BX内容将由第二个子程序提供)的内容，以便依序处理4个十六进制数:1. 利用CL当计数器，记录寄存器移位的次数。2.将BX的第一个十六进制值移到最右边。利用 AND （逻辑“与”运算：对应位都为１时，其结果为１，其余情况为零）把不要的部份清零，得到结果：先将BL值存入AL中，再利用AND以0Fh（00001111）将AL的左边四位清零。由于０到９的ASCII码为30h到39h，而Ａ到Ｆ之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等条件转移指令一起使用，可以形成程序的分支结构，是写汇编程序常用技巧。</FONT></P>
<P><FONT size=2>　　第二个模块DECIBIN 用来接收键盘打入的十进制数，并将它转换成二进制数放于BX 寄存器中，供模块1 BINIHEX使用。<BR>　　decibin segment<BR>　　assume cs:decibin<BR>　　mov bx,0 ;BX清零<BR>　　newchar:mov ah,1 ;<BR>　　int 21h ;读一个键盘输入符号入al，并显示<BR>　　sub al,30h ;al减去30H，结果存于al中，完成ASCII码转二进制码<BR>　　jl exit ;小于零则转移<BR>　　cmp al,9d<BR>　　 jg exit ;左&gt;右则转移<BR>　　cbw ;8位al转换成16位ax<BR>　　xchg ax,bx ;互换ax和bx内数据<BR>　　mov cx,10d ;十进制数10入cx<BR>　　mul cx ;表达式的值与ax内容相乘，并将结果存于ax<BR>　　xchg ax,bx<BR>　　 add bx,ax<BR>　　 jmp newchar ;无条件转移<BR>　　exit: int 20 ;回主程序<BR>　　decibin ends<BR>　　 end<BR>　　CBW 实际结果是:若AL中的值为正，则AH填入00h；反之，则AH填入FFh。XCHG常用于需要暂时保留某个寄存器中的内容时。<BR>　　当然，还得一个子程序（CRLF）使后显示的十六进制数不会盖掉先输入的十进制数。<BR>　　crlf segment<BR>　　assume cs:crlf<BR>　　mov dl,0dh ;回车的ASCII码0DH入DL<BR>　　mov ah,2<BR>　　 int 21h<BR>　　 mov dl,0ah ;换行的ASSII码0AH入AH<BR>　　mov ah,2<BR>　　 int 21h<BR>　　 int 20 ;回主程序<BR>　　crlf ends<BR>　　end</FONT></P>
<P><FONT size=2>　　现在我们就可以将BINIHEX、DECIBIN及CRLF等模块合并成一个大程序了。首先，我们要将这三个模块子程序略加改动。然后，再写一段程序来调用每一个子程序。<BR>　　crlf proc near；<BR>　　mov dl,0dh<BR>　　mov ah,2<BR>　　int 21h<BR>　　mov dl,0ah<BR>　　mov ah,2<BR>　　int 21h<BR>　　ret<BR>　　crlf endp</FONT></P>
<P><FONT size=2>　　类似SEGMENT与ENDS的伪指令，PROC与ENDP也是成对出现，用来识别并定义一个程序。其实，PROC 真正的作用只是告诉编译器：所调用的程序是属于近程（NEAR）或远程（FAR）。 一般的程序是由 DEBUG 直接调用的，所以用 INT 20 返回，用 CALL 指令所调用的程序则改用返回指令RET,RET会把控制权转移到栈顶所指的地址，而该地址是由调用此程序的 CALL指令所放入的。<BR>　　各模块都搞定了，然后我们把子程序组合起来就大功告成<BR>　　decihex segment ;主程序<BR>　　assume cs:decihex<BR>　　org 100h<BR>　　mov cx,4 ;循环次数入cx；由于子程序要用到cx，故子程序要将cx入栈<BR>　　repeat: call decibin;调用十进制转二进制子程序<BR>　　call crlf ;调用添加回、换行符子程序<BR>　　call binihex ;调用二进制转十六进制并显示子程序<BR>　　call crlf<BR>　　loop repeat ;循环4次，可连续运算4次<BR>　　mov ah,4ch ; 调用DOS21号中断4c号功能，退出程序，作用跟INT 20H<BR>　　int 21H ; 一样，但适用面更广，INT20H退不出时，试一下它<BR>　　decibin proc near push cx ;将cx压入堆栈，;<BR>　　┇ exit: pop cx ;将cx还原; retdecibin endp binihex proc near push cx<BR>　　┇ pop cx retbinihex endp crlf proc near<BR>　　 push cx<BR>　　┇ pop cx retcrlf endpdecihex ends end</FONT></P>
<P><FONT size=2>　　CALL指令用来调用子程序，并将控制权转移到子程序地址，同时将CALL的下行一指令地址定为返回地址，并压入堆栈中。CALL 可分为近程（NEAR）及远程（FAR）两种：1.NEAR：IP的内容被压入堆栈中，用于程序与程序在同一段中。2.FAR：CS 、IP寄存器的内容依次压入堆栈中,用于程序与程序在不同段中。PUSH、POP又是一对指令用于将寄存器内容压入、弹出，用来保护寄存器数据，子程序调用中运用较多。堆栈指针有个“后进先出”原则，像PUSH AX，PUSH BX…POP BX，POP AX这样才能作到保护数据丝毫不差。</FONT></P>
<P><FONT size=2>　　汇编语言超浓缩教程到这要告一段落了，希望能奠定你独立设计的基础。而更多更好的技巧则全依赖你平时的积累了。祝你成功！</FONT></P><img src ="http://www.cppblog.com/oosky/aggbug/3354.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-02-20 16:17 <a href="http://www.cppblog.com/oosky/archive/2006/02/20/3354.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于在8086/88内存寻址方式</title><link>http://www.cppblog.com/oosky/archive/2006/02/20/3353.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Mon, 20 Feb 2006 08:15:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/02/20/3353.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/3353.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/02/20/3353.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/3353.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/3353.html</trackback:ping><description><![CDATA[
		<table class="tableBorder1" style="TABLE-LAYOUT: fixed; WORD-WRAP: break-word" cellspacing="1" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td align="left">
										<strong>
												<span class="FontSizeBig">关于在8086/88内存寻址方式</span>
												<br />
												<br />
										</strong> <font size="2"><span class="FontSizeNormal">Writer:HSLY</span><br /></font> <span class="FontSizeNormal"><font size="2">Excerpt:80x86汇编小站</font></span></td>
						</tr>
						<tr bgcolor="#d1d9e2">
								<td height="1">
								</td>
						</tr>
						<tr>
								<td align="left"> <font size="2"><span class="FontSizeNormal">Preface：</span><br /></font><span class="FontPrefaceSize"><font color="#da7e34">在学汇编时，很多初学者对PC的寻址方式和很不理解......</font></span></td>
						</tr>
						<tr bgcolor="#d1d9e2">
								<td height="1">
								</td>
						</tr>
						<tr>
								<td align="left"> <font size="2"><span class="FontSizeNormal">Content：</span><br /></font><p><font size="2">    在学汇编时，很多初学者对PC的寻址方式和很不理解，甚至是很难理解。的确，这方面的知识是很抽象的，需要比较强的空间想象能力。尤其是我们在输入字符串时，那这些字符是如何进行排列的呢？对于，这个问题，我相信很多初学者也是很难想象是如何排列。但是，我可以这样比喻：内存就是有很多栋“楼房”，“楼房”又是由“单元号”，“门户号”组成，那“楼房”就相当于内存地址的段地址，“单元号”就相当于内存的的 偏移地址，“门户号(家)”就相当于“变地址”，而每个单元有16个"门户号(家)",又当我们找到"门户号(家)"后,走进这个"门户号(家)"就会见到里面会有"人",而我们所说的人就是寄存器所指的"内容"了,我画个图给你们看就会一目了然了。</font></p><p><br /><font size="2">用DEBUG的D命令得出这样的效果：</font></p><p><font size="2">　</font></p><p><br /><font size="2">|----------&gt;0B1F就是"楼房"------&gt;段地址 <br />|<br />|     |------&gt;右边的就是"单元号"---&gt;偏移地址<br />|     |<br />|     |            |--------&gt;这部分就是"门户号"-----&gt;变地址<br />|     |          |&lt;------------------------------------------&gt;|<br />0B1F:0100 00 80 FF 02 75 05 C6 46-00 00 C3 E8 8C EB B4 3B<br />0B1F:0110 CD 21 72 39 8B FA 33 C0-8B C8 49 26 34 00 0E 0B<br />'<br />'<br />'<br />[省略] </font></p><p><font size="2">看完这个图之后,是不是就很明了呢?但是聪明的人就会有疑问,那我们怎么走进"门户号(家)"呢?问得好,所以了为了可以走进"门户号(家)",就出现了一个叫做"寻址方式"的概念!说白了,就是教你如何找到这个"门户号(家)".呵呵!</font></p><p><font size="2">好现在都明白了吗?那你们就看看我是怎么理解PC的寻址方式(通俗易懂):<br />在这我就只介绍比较难理解的: </font></p><p><font size="2">1:寄存器直接寻址: <br />你就想成:其实你已经站在你要找的"门户号(家)"面前了,直接敲门进去就OK了! <br />例子: MOV AX,[2000H] <br />MOV AX,2000H --&gt;2000H为存放操作数单元号的符号地址<br />上面两者是不等效的</font></p><p><font size="2">2:寄存器间接寻址方式:<br />你就想成:你已经站在你要找的"门户号(家)"的"单元号",你要找到它,必须知道它在当前"单元号"几楼.假如它在6楼,那你就上到6楼就OK了!!注意,最高只有16楼,因为什么呢?那就用DEBUG的D命令看看呀,慢慢数哦,呵呵!!<br />例子: MOV AX,[BX]</font></p><p><font size="2">计算公式: 物理地址=16d*(DS)+(BX)<br />物理地址=16d*(DS)+(SI)<br />物理地址=16d*(DS)+(DI)<br />物理地址=16d*(SS)+(BP)</font></p><p><font size="2">3:寄存器相对寻址方式:<br />你就想成:你要找的"门户号(家)"其实就在你家的楼上或者楼下,你要找到它,就 必须知道它在你楼上几楼,或者在楼下几楼!就OK了!<br />例子: MOV AX,COUNT[SI]<br />MOV AX,[COUNT+SI]<br />其中 COUNT为位移量的符号地址</font></p><p><br /><font size="2">计算公式: 物理地址=16d*(DS)+(BX)+8位位移量<br />或+(SI) 或 16位位偏移量<br />或+(DI)</font></p><p><br /><font size="2">物理地址=16d*(SS)+(BP)+8位偏移量</font></p><p><br /><font size="2">4:基址变址寻址方式:<br />你就想成:你要找的"门户号(家)"是跟住在同一栋楼的不同"单元号",你要找到它,就必须知道它是该栋的哪个"单元号",并且住在几楼!那样你就可以找到它了 !<br />例子: MOV AX,[BX][DI]<br />MOV AX,[BX+DI]</font></p><p><font size="2">计算公式: 物理地址=16d*(DS)+(BX)+(SI)<br />或+(DI)<br />物理地址=16d*(SS)+(BP)+(SI)<br />或+(DI)</font></p><p><br /><font size="2">5:相对基址变址寻址方式:<br />你就想成:你就想成:你要找的"门户号(家)"是跟住在同一栋楼的不同"单元号",它比你高几层楼或者低几层楼,然后用的你目前的楼数+/-就可以得出你要找的住在几楼了!<br />例子: MOV,AX,MASK[BX][SI]<br />MOV,AX,MASK[BX+SI]<br />MOV,AX,[MASK+BX+SI]<br />以上三个例子是等效的!!</font></p><p><font size="2">计算公式: 物理地址=16d*(DS)+(BX)+(SI)+8位位移量<br />或+(DI) 或 16位位偏移量<br />物理地址=16d*(SS)+(BP)+(SI)+8位位移量<br />或+(DI) 或 16位位偏移量<br />---------------------------------------------------------------------<br />呵呵,终于写完了这篇教程,好累哦!! 是不是觉得我的思维很另类呀,要创新呀!<br />书上太理论了,我就创新一个,不知道你们看得懂吗?<br />呵呵,反正你们不要</font><a href="mailto:!@###$" target="_blank"><font color="#000000" size="2">!@##)(#$</font></a><font size="2">*!@(@我就行了,我很努力写了!!!</font></p><p><font size="2">下面,我举个程序例子,让你们加深印象!!!</font></p><p><font size="2">----------------------------------------------------------------------<br />编程步骤:<br />1: 建立缓冲区,为输入字符串(最多能输入9个)<br />2: 取缓冲区的首地址,以便后面进行"寄存器间接寻址方式"<br />3: 利用"寄存器间接寻址方式"取得实际输入字符个数,以便确认循环次数<br />4: 利用"寄存器间接寻址方式"输入字符串的最后一个字符<br />5: 利用LOOP指令和2号显示功能来进行倒着显示<br />----------------------------------------------------------------------</font></p><p><font size="2">;程序功能：任意输入几个字符(最多能输入9个)，按回车则倒着输出！</font></p><p><font size="2">data segment<br />user_string db 10,0,10 dup(?)<br />data ends<br />code segment<br />assume cs:code,ds:data<br />start: mov ax,data<br />mov ds,ax <br />lea dx,user_string ;建立输入字符串缓冲区<br />mov ah,0ah<br />int 21h<br />xor si,si<br />xor bx,bx<br />mov bx,dx <br />mov cx,[bx+si+1] ;看这个就是"寄存器间接寻址方式"<br />xor ch,ch ;其目的就是取实际输入字符个数<br />mov di,cx<br />lop: mov ah,2<br />mov dx,[bx+di+1];看这又是"寄存器间接寻址方式"<br />int 21h ;其目的就是取输入字符串的最后一个字符<br />dec di<br />loop lop ;依次循环倒着输出字符<br />mov ah,4ch<br />int 21h<br />code ends<br />end start</font></p><p><font size="2">-----------------------------------------------------------------------<br />完工了</font></p></td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cppblog.com/oosky/aggbug/3353.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-02-20 16:15 <a href="http://www.cppblog.com/oosky/archive/2006/02/20/3353.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>常去的网址</title><link>http://www.cppblog.com/oosky/archive/2006/02/13/3232.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Mon, 13 Feb 2006 06:14:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/02/13/3232.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/3232.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/02/13/3232.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/3232.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/3232.html</trackback:ping><description><![CDATA[专业电子书记籍下载:<BR><A href="http://www.netyi.net/in.asp?id=oosky">http://www.netyi.net/in.asp?id=oosky</A>&nbsp;书籍很多，但要你自己细细的寻找哦。<BR><A href="http://www.infoxa.com/">http://www.infoxa.com/</A>&nbsp;这个网站不错，推荐。很多经典的图书都能找到。<BR><A href="http://www.itebook.net/">http://www.itebook.net/</A>&nbsp;<BR><A href="http://www.itepub.net/">http://www.itepub.net/</A>&nbsp;这个站应该很多人都知道的。<BR><BR>Linux免费下载：<BR><A href="http://www.linuxeden.com/forum/t132533.html">http://www.linuxeden.com/forum/t132533.html</A>&nbsp;<FONT size=2>Mandriva Linux 2006 的光盘镜像<BR></FONT><A href="http://public.planetmirror.com/pub/">http://public.planetmirror.com/pub/</A>&nbsp;<BR><A href="http://www.linuxeden.com/download/1214.html">http://www.linuxeden.com/download/1214.html</A>&nbsp;fedora core linux<BR><A href="http://fedora.redhat.com/download/mirrors.html#ASIA">http://fedora.redhat.com/download/mirrors.html#ASIA</A>&nbsp; fedora core linux官方网<BR><A href="http://ftp.osuosl.org/pub/">http://ftp.osuosl.org/pub/</A>&nbsp;看了就知道了<BR><BR><BR><BR><BR>贴这么多，等发现好的再贴出来，大家也共享一下。<BR><img src ="http://www.cppblog.com/oosky/aggbug/3232.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-02-13 14:14 <a href="http://www.cppblog.com/oosky/archive/2006/02/13/3232.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>堆栈，堆栈，堆和栈的区别</title><link>http://www.cppblog.com/oosky/archive/2006/01/21/2958.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Sat, 21 Jan 2006 08:23:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/01/21/2958.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/2958.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/01/21/2958.html#Feedback</comments><slash:comments>32</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/2958.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/2958.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 堆栈，堆栈？什么是堆，什么是栈？<br>看完你就明白了。<br>&nbsp;&nbsp;<a href='http://www.cppblog.com/oosky/archive/2006/01/21/2958.html'>阅读全文</a><img src ="http://www.cppblog.com/oosky/aggbug/2958.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-01-21 16:23 <a href="http://www.cppblog.com/oosky/archive/2006/01/21/2958.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>#pragma 预处理指令详解</title><link>http://www.cppblog.com/oosky/archive/2006/01/06/2464.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Fri, 06 Jan 2006 06:33:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/01/06/2464.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/2464.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/01/06/2464.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/2464.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/2464.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 这份文档说的比较详细，对#pragma 预处理指令不了解的，非常有帮助&nbsp;&nbsp;<a href='http://www.cppblog.com/oosky/archive/2006/01/06/2464.html'>阅读全文</a><img src ="http://www.cppblog.com/oosky/aggbug/2464.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-01-06 14:33 <a href="http://www.cppblog.com/oosky/archive/2006/01/06/2464.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>驱动开程序发—安装 </title><link>http://www.cppblog.com/oosky/archive/2006/01/03/2370.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Tue, 03 Jan 2006 02:11:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/01/03/2370.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/2370.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/01/03/2370.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/2370.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/2370.html</trackback:ping><description><![CDATA[<P>作为一个完整的例子，你开发出来驱动还必须要能安装。所以下面我讲一下安装。</P>
<P>如果前面的编译过程没有错误的话，现在我们应该已经得到了一个HelloWDM.sys文件，假设它是放在D:\HelloWDM\objfre\i386中。<BR><BR>安装WDM驱动程序可以用两种方法，一种是利用注册表，还有一种是利用INF文件。我们一般是采用INF文件（这是微软推荐的）。INF文件可以在 WINNT\INF 目录中找到很多。为了顺利安装，我在这里先给出 HelloWDM 所需要的 HelloWDM.INF 文件：<BR><BR>
<TABLE cellSpacing=0 cellPadding=0 bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD>;; The Win2K DDK documentation contains an excellent INF reference.<BR><BR>;--------- Version Section ---------------------------------------------------<BR><BR>[Version]<BR>Signature="$CHICAGO$"<BR>Provider=LC_Device<BR>DriverVer=8/21/2002,3.0.0.3<BR><BR>; If device fits one of the standard classes, use the name and GUID here,<BR>; otherwise create your own device class and GUID as this example shows.<BR><BR>Class=Unknown<BR>ClassGUID={ff646f80-8def-11d2-9449-00105a075f6b}<BR><BR>;--------- SourceDiskNames and SourceDiskFiles Section -----------------------<BR><BR>; These sections identify source disks and files for installation. They are<BR>; shown here as an example, but commented out.<BR><BR>[SourceDisksNames]<BR>1 = "HelloWDM",Disk1,,<BR><BR>[SourceDisksFiles]<BR>HelloWDM.sys = 1,objfre\i386,<BR><BR>;--------- ClassInstall/ClassInstall32 Section -------------------------------<BR><BR>; Not necessary if using a standard class<BR><BR>; 9X Style<BR>[ClassInstall]<BR>Addreg=Class_AddReg<BR><BR>; NT Style<BR>[ClassInstall32]<BR>Addreg=Class_AddReg<BR><BR>[Class_AddReg]<BR>HKR,,,,%DeviceClassName%<BR>HKR,,Icon,,"-5"<BR><BR>;--------- DestinationDirs Section -------------------------------------------<BR><BR>[DestinationDirs]<BR>YouMark_Files_Driver = 10,System32\Drivers<BR><BR>;--------- Manufacturer and Models Sections ----------------------------------<BR><BR>[Manufacturer]<BR>%MfgName%=Mfg0<BR><BR>[Mfg0]<BR><BR>; PCI hardware Ids use the form<BR>; PCI\VEN_aaaa&amp;DEV_bbbb&amp;SUBSYS_cccccccc&amp;REV_dd<BR>;改成你自己的ID<BR>%DeviceDesc%=YouMark_DDI, PCI\VEN_9999&amp;DEV_9999<BR><BR>;---------- DDInstall Sections -----------------------------------------------<BR>; --------- Windows 9X -----------------<BR><BR>; Experimentation has shown that DDInstall root names greater than 19 characters<BR>; cause problems in Windows 98<BR><BR>[YouMark_DDI]<BR>CopyFiles=YouMark_Files_Driver<BR>AddReg=YouMark_9X_AddReg<BR><BR>[YouMark_9X_AddReg]<BR>HKR,,DevLoader,,*ntkern<BR>HKR,,NTMPDriver,,HelloWDM.sys<BR>HKR, "Parameters", "BreakOnEntry", 0x00010001, 0<BR><BR>; --------- Windows NT -----------------<BR><BR>[YouMark_DDI.NT]<BR>CopyFiles=YouMark_Files_Driver<BR>AddReg=YouMark_NT_AddReg<BR><BR>[YouMark_DDI.NT.Services]<BR>Addservice = HelloWDM, 0x00000002, YouMark_AddService<BR><BR>[YouMark_AddService]<BR>DisplayName = %SvcDesc%<BR>ServiceType = 1 ; SERVICE_KERNEL_DRIVER<BR>StartType = 3 ; SERVICE_DEMAND_START<BR>ErrorControl = 1 ; SERVICE_ERROR_NORMAL<BR>ServiceBinary = %10%\System32\Drivers\HelloWDM.sys<BR><BR>[YouMark_NT_AddReg]<BR>HKLM, "System\CurrentControlSet\Services\HelloWDM\Parameters",\<BR>"BreakOnEntry", 0x00010001, 0<BR><BR><BR>; --------- Files (common) -------------<BR><BR>[YouMark_Files_Driver]<BR>HelloWDM.sys<BR><BR>;--------- Strings Section ---------------------------------------------------<BR><BR>[Strings]<BR>ProviderName="Flying L Co.,Ltd."<BR>MfgName="LC Soft"<BR>DeviceDesc="Hello World WDM!"<BR>DeviceClassName="LC_Device"<BR>SvcDesc="???"<BR></TD></TR></TBODY></TABLE></P>
<P><BR><BR>注意它可以同时在Win98或者Win2000中使用（系统会通过这个INF文件里面的字段名称，自动选择适合当前系统的安装方法的）。关于INF文件的各个字段含义现在我也不知道，所以也没有办法说清楚，如果谁看到这篇文章，而又知道的话，不妨为我一份。<BR><BR>准备好这个 HelloWDM.INF 文件后，让我们打开控制面板，双击“添加/删除硬件”，选择“添加/排除设备故障”-&gt;“添加新设备”-&gt;“否，我想从列表选择硬件”-&gt;“其它设备”-&gt;“从磁盘安装”，选择 HelloWDM.INF 所在的路径，然后安装。<BR><BR>当安装完成后，系统就会添加上你写好的驱动程序了。（可以在“设备管理器”中查看到）。然后重启电脑，这个驱动程序就投入使用啦。</P>
<P>关于安装，我也只知道这么多，到底安装驱动程序时，操作系统都作了些什么，我也不是很清楚，等我弄明白了我再贴上。<BR></P><img src ="http://www.cppblog.com/oosky/aggbug/2370.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-01-03 10:11 <a href="http://www.cppblog.com/oosky/archive/2006/01/03/2370.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>驱动程序开发—编译正传 </title><link>http://www.cppblog.com/oosky/archive/2006/01/03/2369.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Tue, 03 Jan 2006 02:10:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/01/03/2369.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/2369.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/01/03/2369.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/2369.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/2369.html</trackback:ping><description><![CDATA[<P>我在前面也讲过了一些关于编译环境及工具的。在这里结合本例子我再说一下：</P>
<P>DDK分为98 DDK和2000 DDK两种，它们工作起来是大同小异的，不过有些驱动程序只能在2000 DDK中使用。由于Win98注定是一种即将被淘汰的操作系统了，所以我学习的时候也没有过多的关注，我用的是2000的DDK，所以以下的所有内容都是针对2000 DDK的。<BR><BR><BIG><B>·准备工作</B></BIG><BR>1、确定你已经安装了Visual C++<BR>2、安装2000 DDK<BR>3、安装2000 DDK成功后，在“<B>开始</B>”-&gt;“<B>程序</B>”里应该有“<B>Development Kits</B>”-&gt;“<B>Windows 2000 DDK</B>”的项目。<BR>（<FONT color=red>注意一定要先安装好VC，然后才安装DDK，这个顺序决不能颠倒！！</FONT>）<BR>4、保证DDKROOT环境变量设置为Windows 2000 DDK的基目录，如果不是的话，请在控制面板“<B>系统</B>”属性的“<B>高级</B>”标签环境变量编辑器中设置好这个环境变量。<BR><BR><BR><BIG><B>·编写必需的文件</B></BIG><BR>编译WDM程序的时候，有两个文件是必须要有的，它们是：<BR>1、<FONT color=green><B>makefile</B></FONT><BR>（这个是什么啊？你可能会问。）对于比较年轻的程序员来说，有可能没有见过这个文件吧。其实在VC这些IDE出现之前，我们都必须使用makefile来确定项目中哪些文件需要重新编译，现在的IDE都把这个工作自动做好了<BR>我们要做的工作很简单，就是提供这样一个文件，它的内容是：<BR><BR></P>
<TABLE cellSpacing=0 cellPadding=0 bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD>#<BR># DO NOT EDIT THIS FILE!!!&nbsp;&nbsp;Edit .\sources. If you want to add a new source<BR># file to this component.&nbsp;&nbsp;This file merely indirects to the real make file<BR># that is shared by all the driver components of the Windows NT DDK<BR>#<BR><BR>!INCLUDE $(NTMAKEENV)\makefile.def<BR></TD></TR></TBODY></TABLE><BR><BR>正如它所述，不要编辑这个文件。事实上每个WDM程序所需要的makefile的内容都是一样的，也就是说，我们只需要简单地copy一个makefile到新的项目中就可以了<BR>2、<FONT color=green><B>Sources</B></FONT><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD>TARGETNAME=HelloWDM //编译出来的驱动程序的名称<BR>TARGETTYPE=DRIVER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //编译的类型是驱动程序编译<BR>DRIVERTYPE=WDM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //驱动程序的类型是WDM驱动程序<BR>TARGETPATH=OBJ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //生成的文件存放在OBJ目录中<BR><BR>INCLUDES=$(BASEDIR)\inc;\&nbsp;&nbsp; //这是需要引入的头文件<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(BASEDIR)\inc\ddk;\<BR><BR>TARGETLIBS=$(BASEDIR)\lib\*\free\usbd.lib\&nbsp; //这是需要引入的库文件<BR><BR>SOURCES=HelloWDM.cpp\&nbsp;&nbsp;&nbsp; //这是源码文件<BR></TD></TR></TBODY></TABLE><BR><BR>这个文件指定了驱动程序目标名是HelloWDM.sys，是一个WDM驱动程序，生成的文件存放在OBJ目录中。值得注意的是，“=”前后不能有空格，否则编译的时候会出错。<BR><BR><BR><BIG><B>·开始编译</B></BIG><BR>娃哈哈，前面罗罗嗦嗦讲了一大堆，现在终于到重点了。WDM程序的编译过程比较特殊，它不是在VC里面按F7来编译的（尽管你可以通过设置来达到这一目的），而是通过一个DDK实用工具build来完成。下面我们来讲讲具体步骤：<BR>1、“Debug”版的生成<BR>首先，我们假设你的源代码放在D:\HelloWDM里面。请跟着以下步骤：<BR><BR>“开始”-&gt;“程序”-&gt;“Development Kits”-&gt;“Windows 2000 DDK”-&gt;“Checked Build Environment”<BR><BR>屏幕将显示：（有“回车”的那行是需要读者你亲自打进去的）<BR><BR>
<TABLE cellSpacing=0 cellPadding=0 bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD>New or updated MSVC detected.&nbsp;&nbsp;Updating DDK environment….<BR><BR>Setting environment for using Microsoft Visual C++ tools.<BR>Starting dirs creation…Completed.<BR><BR>D:\NTDDK&gt;cd\HelloWDM&nbsp;&nbsp;&nbsp;&nbsp;（回车）<BR><BR>D:\HelloWDM&gt;build&nbsp;&nbsp;&nbsp;&nbsp;（回车）<BR></TD></TR></TBODY></TABLE><BR><BR>如果源代码没有错误的话，生成的HelloWDM.sys将存放在objchk\i386目录中。<BR><BR>2、“Release”版的生成<BR>请跟着以下步骤：<BR><BR>“开始”-&gt;“程序”-&gt;“Development Kits”-&gt;“Windows 2000 DDK”-&gt;“Free Build Environment”<BR><BR>随后的步骤跟“Debug”版相同，不同的是生成的HelloWDM.sys将存放在objfre\i386目录中。 <img src ="http://www.cppblog.com/oosky/aggbug/2369.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-01-03 10:10 <a href="http://www.cppblog.com/oosky/archive/2006/01/03/2369.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>驱动程序开发—编译前传</title><link>http://www.cppblog.com/oosky/archive/2006/01/03/2368.html</link><dc:creator>任我行</dc:creator><author>任我行</author><pubDate>Tue, 03 Jan 2006 02:08:00 GMT</pubDate><guid>http://www.cppblog.com/oosky/archive/2006/01/03/2368.html</guid><wfw:comment>http://www.cppblog.com/oosky/comments/2368.html</wfw:comment><comments>http://www.cppblog.com/oosky/archive/2006/01/03/2368.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/oosky/comments/commentRss/2368.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/oosky/services/trackbacks/2368.html</trackback:ping><description><![CDATA[<P>好啦，辛辛苦苦终于写完了程序，让我们编译运行吧！按下Ctrl+F5（嘿嘿，让我们先假设你习惯用VC来写程序），我等啊等……疑？怎么毫无动静的？再看看Output窗口，哇！有几百个错误啊！！不禁头大——这是怎么回事呢？<BR><BR>原来，WDM程序编译出来的并不是我们常见的.exe，而是.sys文件，在未经设置编译环境之前，是不能直接用VC来编译的（这就是为什么会有几百个错误了）。这种类型的文件你可以在WINNT\System32\Drivers里面找到很多。其实驱动程序也是一种PE文件，它同样由DOS MZ header开头，也有完整的DOS stub和PE header，同样拥有Import table和Export table——……那跟普通的PE文件有什么不一样呢？那么就让我们先来做个小剖析，加深对.sys文件的认识吧</P>
<P><BR>首先祭出Delphi里附带的tdump.exe程序。让我们键入：<BR>C:\WINNT\System32\Drivers&gt;tdump ccport.sys -em -ee<BR>参数-em是列出Import table，-ee是列出Export table。回车之后，屏幕列出一大堆东西：<BR><BR></P>
<P>
<TABLE cellSpacing=0 cellPadding=0 bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD>C:\WINNT\SYSTEM32\DRIVERS&gt;tdump ccport.sys -em -ee<BR>Turbo Dump&nbsp;&nbsp;Version 5.0.16.12 Copyright ? 1988, 2000 Inprise Corporation<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Display of File CCPORT.SYS<BR><BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:011Fh}.’memcpy’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:003Dh}.’IoDeleteDevice’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0030h}.’IoAttachDeviceToDeviceStack’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:008Eh}.’KeSetEvent’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0068h}.’IofCallDriver’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0095h}.’KeWaitForSingleObject’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0074h}.’KeInitializeEvent’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:003Fh}.’IoDetachDevice’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:00D3h}.’RtlFreeUnicodeString’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0077h}.’KeInitializeSpinLock’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0129h}.’strcpy’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0121h}.’memset’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:003Ch}.’IoCreateUnprotectedSymbolicLink’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0038h}.’IoCreateDevice’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:00C2h}.’RtlAnsiStringToUnicodeString’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0069h}.’IofCompleteRequest’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0124h}.’sprintf’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:003Eh}.’IoDeleteSymbolicLink’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0042h}.’IoFreeIrp’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:004Dh}.’IoInitializeIrp’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:002Dh}.’IoAllocateIrp’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0027h}.’InterlockedExchange’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0025h}.’InterlockedCompareExchange’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0035h}.’IoCancelIrp’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:012Ah}.’strlen’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0126h}.’strcat’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0114h}.’atoi’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0128h}.’strcmp’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:0034h}.’IoBuildSynchronousFsdRequest’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp; NTOSKRNL.EXE={hint:00D5h}.’RtlInitAnsiString’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HAL.DLL={hint:0006h}.’KfAcquireSpinLock’<BR>IMPORT:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HAL.DLL={hint:0009h}.’KfReleaseSpinLock’<BR><BR>EXPORT ord:0001=’Vcomm_DriverControl’<BR></TD></TR></TBODY></TABLE></P>
<P><BR><BR>看到了吗？它主要调用了NTOSKRNL.EXE和HAL.DLL文件（实际上你会发现，几乎所有的WDM驱动程序都会调用NTOSKRNL.EXE文件，从它的名字你可以看出为什么了吧？），并且输出了一个函数“Vcomm_DriverControl”。这表明，其实.sys跟.exe文件一样，都是一种PE文件来的。不同的是，.sys文件Import的通常是NTOSKRNL.EXE，而.exe文件Import的通常是KERNEL32.DLL和USER32.DLL。<BR><BR>知道了这些有什么用呢？实际上，由于.sys通常不调用KERNEL32.DLL和USER32.DLL，所以你是不能在设备驱动程序里面调用任何C、C++和Win32函数的，而且也不能用C++关键字new和delete等（可以用malloc和free来代替），而必须使用大量的内核函数。另外，你应该也能看到她调用了像IoDeleteDevice、IoAttachDeviceToDeviceStack等等函数，这些你以前可能没有见过的函数都是些内核函数。为了读者的方便，下面我列出一些常见的驱动程序可用的内核函数：<BR><BR></P>
<P>
<TABLE cellSpacing=0 cellPadding=0 bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD>Ex…&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;执行支持<BR>Hal…&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;硬件抽象层（仅NT/Windows 2000）<BR>Io…&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;I/O管理器（包括即插即用函数）<BR>Ke…&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;内核<BR>Ks…&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;内核流IRP管理函数<BR>Mm…&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;内存管理器<BR>Ob…&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对象管理器<BR>Po…&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;电源管理<BR>Ps…&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;进程结构<BR>Rtl…&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;运行时库<BR>Se…&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;安全引用监视<BR>Zw…&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;其他函数<BR></TD></TR></TBODY></TABLE></P>
<P><BR>最后让我们再来看看，写设备驱动程序时必须注意的一些问题：<BR><BR>1、<B>内核宏</B><BR>如果查看DDK头文件，会发现有几个内核函数是以宏的方式实现的。这种宏中有几个宏的定义是相当糟糕的。例如，我们看到RemoveHeadList的定义如下：<BR></P>
<P>
<TABLE cellSpacing=0 cellPadding=0 bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD>#define RemoveHeadList(ListHead)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(ListHead)-&gt;Flink;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{RemoveEntryList((ListHead)-&gt;Flink)}<BR></TD></TR></TBODY></TABLE></P>
<P><BR>如果以以下方式调用RemoveHeadList，则将编译错误的代码：<BR></P>
<P>
<TABLE cellSpacing=0 cellPadding=0 bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD>if(SomethingInList)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Entry = RemoveHeadList(list);<BR></TD></TR></TBODY></TABLE></P>
<P><BR>使这个调用安全的唯一方法是使用花括号：<BR></P>
<P>
<TABLE cellSpacing=0 cellPadding=0 bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD>if(SomethingInList)<BR>&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Entry = RemoveHeadList(list);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR></TD></TR></TBODY></TABLE></P>
<P><BR>所以我们切勿为了贪图一时的方便，而使用不太规范的写法，最好是在所有的if、for和while等语句中使用花括号。<BR><BR>2、<B>驱动程序函数名称</B><BR>跟C/C++的main()函数一样，设备驱动程序也有一个必须存在，而且只能以DriverEntry()为名称的入口函数。然而，除此之外，我们可以使用任何名字来给其他函数命名——只要你自己记得就行了，当然，最好符合某些特定的规范啦，例如匈牙利命名法……<BR><BR>3、<B>安装时的问题</B><BR>·在Windows98中驱动程序可执行文件必须是8.3文件名。（别问我为什么，我也不知道，我只能建议你去问比尔该死）<BR>·如果INF文件中含有非法节的详细资料，Windows将不使用这个INF文件。</P>
<P><STRONG>本节罗罗嗦嗦讲了一大堆，跟实际的编程却并没有太大的关系，前传嘛！就是这样的啦！</STRONG></P><img src ="http://www.cppblog.com/oosky/aggbug/2368.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/oosky/" target="_blank">任我行</a> 2006-01-03 10:08 <a href="http://www.cppblog.com/oosky/archive/2006/01/03/2368.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>