Yan's profilecomputing lifePhotosBlogListsMore Tools Help

Blog


    [coding]DirectShow的配置问题

    [coding]DirectShow的配置问题
    注:本文主要内容改编自互联网。感谢这些内容的原创者、Linxie以及Tao。
    我的妈搞死我了。总结一下造福后来人。
    首先,现在版本的DirectX并不包括DirectShow,如果光辛辛苦苦下了一个200MB+的DirectX SDK,然后郁闷地google为什么用不了DirectShow,请再去下一个4MB的DirectShow SDK先。
    要使用DirectShow的大部分功能,需要编译它的一个sample(诡异啊诡异),位置在\Samples\C++\DirectShow\
    这里就有一个叫baseclasses的工程,为安全起见,请先备份此工程。
    1. 双击baseclasses.sln打开,按提示转换工程,编译。
    提示错误:
    1>D:\Microsoft Visual Studio 8\VC\PlatformSDK\include\winnt.h(222) : error C2146: syntax error : missing ';' before identifier 'PVOID64'
    1>D:\Microsoft Visual Studio 8\VC\PlatformSDK\include\winnt.h(222) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
    1>D:\Microsoft Visual Studio 8\VC\PlatformSDK\include\winnt.h(5940) : error C2146: syntax error : missing ';' before identifier 'Buffer'
    1>D:\Microsoft Visual Studio 8\VC\PlatformSDK\include\winnt.h(5940) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
    1>D:\Microsoft Visual Studio 8\VC\PlatformSDK\include\winnt.h(5940) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
    1>d:\DX90SDK\Samples\C++\DirectShow\BaseClasses\ctlutil.h(278) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
    第一类问题的解决:错误发生在:operator=(LONG);函数定义中,这是因为在VC6中,如果没有显示的指定返回值类型,编译器将其视为默认整形;但是vs2005不支持默认整形,解决这个问题不能修改每个没有显示指示返回值类型的函数地方,可以用wd4430来解决;具体的在工程选项中的c+ +/Command Line中添加/wd4430即可。
    2. 在Tools->Options->Projects and solutions->vc++ directories->show directories for里选择include files
    然后增加
    DX90SDK\Include
    DX90SDK\Samples\C++\DirectShow\BaseClasses
    DX90SDK\Samples\C++\Common
    可能会问题依旧,调整一下include files的顺序,把它们都放在最后。
    然后在project->BaseClasses properties->configuration->C/C++ -> General->Additional Include Directories里面的内容(.,..\..\..\..\include)删掉,重新编译 ,PVOID64的错误消失,原因如下:
    POINTER_64 是一个宏,在64位编译下起作用,它包含在SDK目录下的BASETSD.H中(Microsoft Visual Studio 8\VC\PlatformSDK\Include\basetsd.h(23):#define POINTER_64 __ptr64),但DXSDK自己也带了一个basetsd.h,里面没有定义POINTER_64,从而导致出错,只需要改变include files的优先级即可。当然,也可以改写winnt.h中的代码,在下面这两行:
    typedef  void  *PVOID;
    typedef  void  *POINTER_64  PVOID64;
    之前增加一行:
    #define  POINTER_64  __ptr64
    3. 到目前为止,还剩下:
    BaseClasses\ctlutil.h(278) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
    这个错误,解决方法如下:
    打开project->BaseClasses properties->configuration->C/C++ ->Command Line,增加/wd4430选项。
    4. 接着编译,提示error C2065: 'Count' : undeclared identifier等等错误,这个是微软的历史遗留问题了,C++标准语法的问题,因为在之前在for循环内定义的变量可以在for之外的地方使用,即 在第一个for里for(int i,...),以后的for再使用i不必再声明,解决方法也很简单,打开project->BaseClasses properties->configuration- >C/C++->Language->Force Comformance in For Loop Scrope设置为No即可。当然,也可以手动改代码,增加一个声明就是了。
    经过上面几个步骤,应该是可以顺利编译了,对于其它版本的工程,照样设置就可以了,总共编译出下面4个文件备用:
    BaseClasses\Debug\strmbasd.lib
    BaseClasses\Debug_Unicode\strmbasd.lib
    BaseClasses\Release\STRMBASE.lib
    BaseClasses\Release_Unicode\STRMBASE.lib
    在lib里面加入
    commctrl.lib
    coredll.lib
    strmiids.lib
    strmbase.lib
    uuid.lib
    ole32.lib
    commdlg.lib
    你或许需要在Tools->Options->Projects and solutions->vc++ directories->show directories for->library files里把它们加进去。
    赶快编译DX90SDK\Samples\C++\DirectShow\Players\PlayDMO工程试试看吧。

    [coding]JustCengIt

    [coding]JustCengIt
    实用小软件,恶搞无极限!:-P

    JustCengIt

    [coding]gTerm 0.2

    gTerm 0.2也搞定了,支持自动发帖和快速连接POP3,字体也调了一下。现在支持退格和方向键了。
    image 
      image
    image
    image 
    image

    [coding]gTerm 0.1终于搞定

    以后可以用自己的terminal啦。工作基本正常。可以连80端口取HTTP服务,可以连110POP3,可以连23Telnet,其他没试。Telnet还不支持NVT,只是简单的跳过了它的信息。

    HTTP:
    HTTP

    POP3:
    pop3

    Telnet:
    ”支持“NVT前:
    bbs
    ”支持“NVT后:
    未命名

    诶~编程真是个好东西~ 

    [coding]编程的境界

    1

    写了十几年的程序蓦然发现转了一圈又回来了……写的什么狗屁东西……

    不过这也意味着我的gTerm有望啦啊哈哈哈哈哈~

    [coding][zz]大话“20 世纪10大算法”

    大话“20 世纪10大算法”
    (本文为转载,原文作者柯化成,发表于《程序员》2006年4月号)
    本世纪初, 美国物理学会(American Institute of Physics)和IEEE计算机社团 (IEEE Computer Society)的一本联合刊物《科学与工程中的计算》发表了由田纳西大学的Jack Dongarra和橡树岭国家实验室的Francis Sullivan 联名撰写的“世纪十大算法”一文,该文“试图整理出在20世纪对科学和工程领域的发展产生最大影响力的十大算法”。作者苦于“任何选择都将是充满争议的,因为实在是没有最好的算法”,他们只好用编年顺序依次列出了这十项算法领域人类智慧的巅峰之作——给出了一份没有排名的算法排行榜。有趣的是,该期杂志还专门邀请了这些算法相关领域的“大拿”为这十大算法撰写十篇综述文章,实在是蔚为壮观。本文的目的,便是要带领读者走马观花,一同回顾当年这一算法界的盛举。
    1946 蒙特卡洛方法
    在广场上画一个边长一米的正方形,在正方形内部随意用粉笔画一个不规则的形状,呃,能帮我算算这个不规则图形的面积么?蒙特卡洛(Monte Carlo)方法便是解决这个问题的巧妙方法:随机向该正方形内扔N(N 是一个很大的自然数)个黄豆,随后数数有多少个黄豆在这个不规则几何形状内部,比如说有M个:那么,这个奇怪形状的面积便近似于M/N,N越大,算出来的值便越精确。别小看这个数黄豆的笨办法,大到国家的民意测验,小到中子的移动轨迹,从金融市场的风险分析,到军事演习的沙盘推演,蒙特卡洛方法无处不在背后发挥着它的神奇威力。
    蒙特卡洛方法由美国拉斯阿莫斯国家实验室的三位科学家John von Neumann(看清楚了,这位可是冯诺伊曼同志!),Stan Ulam 和 Nick Metropolis共同发明。就其本质而言,蒙特卡洛方法是用类似于物理实验的近似方法求解问题,它的魔力在于,对于那些规模极大的问题,求解难度随着问题的维数(自变量个数)的增加呈指数级别增长,出现所谓的“维数的灾难”(Course of Dimensionality)。对此,传统方法无能为力,而蒙特卡洛方法却可以独辟蹊径,基于随机仿真的过程给出近似的结果。
    最后八卦一下,Monte Carlo这个名字是怎么来的?它是摩纳哥的一座以博彩业闻名的城市,赌博其实是门概率的高深学问,不是么?
    1947 单纯形法
    单纯形法是由大名鼎鼎的“预测未来”的兰德公司的Grorge Dantzig发明的,它成为线性规划学科的重要基石。所谓线性规划,简单的说,就是给定一组线性(所有变量都是一次幂)约束条件(例如a1*x1+ b1*x2+c1*x3>0),求一个给定的目标函数的极值。这么说似乎也太太太抽象了,但在现实中能派上用场的例子可不罕见——比如对于一个公司而言,其能够投入生产的人力物力有限(“线性约束条件”),而公司的目标是利润最大化(“目标函数取最大值”),看,线性规划并不抽象吧!线性规划作为运筹学(operation research)的一部分,成为管理科学领域的一种重要工具。而Dantzig提出的单纯形法便是求解类似线性规划问题的一个极其有效的方法,说来惭愧,本科二年级的时候笔者也学过一学期的运筹学,现在脑子里能想起的居然只剩下单纯形法了——不过这不也正说明了该方法的简单和直观么?
    顺便说句题外话,写过《万历十五年》的黄仁宇曾说中国的传统是“不能从数目字上管理”,我们习惯于“拍脑袋”,而不是基于严格的数据做决定,也许改变这一传统的方法之一就是全民动员学习线性规划喔。
    1950 Krylov子空间迭代法
    1951 矩阵计算的分解方法

    50年代初的这两个算法都是关于线性代数中的矩阵计算的,看到数学就头大的读者恐怕看到算法的名字已经开始皱眉毛了。Krylov子空间叠代法是用来求解形如Ax=b 的方程,A是一个n*n 的矩阵,当n充分大时,直接计算变得非常困难,而Krylov方法则巧妙地将其变为Kxi+1=Kxi+b-Axi的迭代形式来求解。这里的K(来源于作者俄国人Nikolai Krylov姓氏的首字母)是一个构造出来的接近于A的矩阵,而迭代形式的算法的妙处在于,它将复杂问题化简为阶段性的易于计算的子步骤。
    1951年由橡树岭国家实验室的AlstonHouseholder提出的矩阵计算的分解方法,则证明了任何矩阵都可以分解为三角、对角、正交和其他特殊形式的矩阵,该算法的意义使得开发灵活的矩阵计算软件包成为可能。
     1957 优化的Fortran编译器
    说实话,在这份学术气息无比浓郁的榜单里突然冒出一个编译器(Compiler)如此工程化的东东实在让人有“关公战秦琼”的感觉。不过换个角度想想,Fortran这一门几乎为科学计算度身定制的编程语言对于科学家(尤其是数学家,物理学家)们实在是太重要了,简直是他们形影不离的一把瑞士军刀,这也难怪他们纷纷抢着要把票投给了它。要知道,Fortran是第一种能将数学公式转化为计算机程序的高级语言,它的诞生使得科学家们真正开始利用计算机作为计算工具为他们的研究服务,这是计算机应用技术的一个里程碑级别的贡献。
    话说回来,当年这帮开发Fortran的家伙真是天才——只用23500行汇编指令就完成了一个Fortran编译器,而且其效率之高令人叹为观止:当年在IBM 主持这一项目的负责人JohnBackus在数十年后,回首这段往事的时候也感慨,说它生成代码的效率“出乎了所有开发者的想象”。看来作为程序员,自己写的程序跑起来“出乎自己的想象”,有时候还真不一定是件坏事!
    1959-61 计算矩阵特征值的QR算法
    呼,又是一个和线性代数有关的算法,学过线性代数的应该还记得“矩阵的特征值”吧?计算特征值是矩阵计算的最核心内容之一,传统的求解方案涉及到高次方程求根,当问题规模大的时候十分困难。QR算法把矩阵分解成一个正交矩阵(什么是正交矩阵?!还是赶紧去翻书吧!)与一个上三角矩阵的积,和前面提到的 Krylov 方法类似,这又是一个迭代算法,它把复杂的高次方程求根问题化简为阶段性的易于计算的子步骤,使得用计算机求解大规模矩阵特征值成为可能。这个算法的作者是来自英国伦敦的J.G.F. Francis。
    1962 快速排序算法
    不少读者恐怕和我一样,看到“快速排序算法”(Quick Sort)这个条目时,心里的感觉是——“这可总算找到组织了”。相比于其他一些对程序员而言高深莫测的数学物理公式,快速排序算法真是我们朝夕相处的好伙伴——老板让你写个排序算法,如果你写出来的不是快速排序,你都不好意思跟同事打招呼。其实根本不用自己动手实现, 不论是ANSI C,C++ STL,还是Java SDK,天下几乎所有的SDK里都能找到它的某种实现版本。
    快速排序算法最早由Tony Hoare爵士设计,它的基本思想是将待排序列分为两半,左边的一半总是“小的”,右边的一半总是“大的”,这一过程不断递归持续下去,直到整个序列有序。说起这位Tony Hoare爵士,快速排序算法其实只是他不经意间的小小发现而已,他对于计算机贡献主要包括形式化方法理论,以及ALGOL60 编程语言的发明等,他也因这些成就获得1980 年图灵奖。
    快速排序的平均时间复杂度仅仅为O(Nlog(N)),相比于普通选择排序和冒泡排序等而言,实在是历史性的创举。
     1965 快速傅立叶变换
    如果要评选对我们的日常生活影响最大的算法,快速傅立叶变换算法应该是当仁不让的总冠军——每天当拿起话筒,打开手机,听mp3,看DVD,用DC拍照 ——毫不夸张的说,哪里有数字信号处理,哪里就有快速傅立叶变换。快速傅立叶算法是离散傅立叶算法(这可是数字信号处理的基石)的一种快速算法,它有 IBM 华生研究院的James Cooley和普林斯顿大学的John Tukey共同提出,其时间复杂度仅为O(Nlog(N));比时间效率更为重要的是,快速傅立叶算法非常容易用硬件实现,因此它在电子技术领域得到极其广泛的应用。
    1977 整数关系探测算法
    整数关系探测是个古老的问题,其历史甚至可以追溯到欧几里德的时代。具体的说:
    给定—组实数X1,X2,...,Xn,是否存在不全为零的整数a1,a2,...an,使得:a 1 x 1 +a 2 x 2 + . . . + a n x n = 0 这一年BrighamYoung大学的Helaman Ferguson 和Rodney Forcade解决了这一问题。至于这个算法的意义嘛,呃,该算法应用于“简化量子场论中的Feynman图的计算”——太深奥的学问拉!
     1987 快速多极算法
    日历翻到了1987 年,这一年的算法似乎更加玄奥了,耶鲁大学的Leslie Greengard和Vladimir Rokhlin提出的快速多极算法用来计算“经由引力或静电力相互作用的N 个粒子运动的精确计算——例如银河系中的星体,或者蛋白质中的原子间的相互作用”,天哪,不是我不明白,这世界真是变得快!
    所谓浪花淘尽英雄,这些算法的发明者许多已经驾鹤西去。二十一世纪的头五年也已经在不知不觉中从我们指尖滑过,不知下一次十大算法评选的盛事何时再有,也许我们那时已经垂垂老去,也许我们早已不在人世,只是心中唯一的希望——里面该有个中国人的名字吧!
    参考文献

    本文基于下列材料编写:
    [1] Dongarra and Sulliv an, Top Ten Algorithmsof the Century, Computing in Science andEngineering, January /February 2000.
    [2] Barry Cipra,The Best of the 20th Century :Editors Name Top 10 Algorithms SIAM News,Volume 33, Number 4, May 2000, page 1.

    [Coding]GPopup

    [Coding]GPopup
    写了一个根据IP地址进行消息和文件发送/接收的小程序。用了MFC、多线程编程、Socket编程,总结经验教训如下:
    根据自己的实践经验和网络上的搜索结果,MFC静态版本的CSocket似乎有Bug,运行报Assertion failed。换成动态版本问题解决。但这样有利有弊,一方面动态版本有利于程序的维护和升级,一方面程序分发的难度加大,必须要加入一个MFC动态链接库才可以。
    传文件和传消息的差异在于两个方面,第一是大小不同。消息一般是1024字节以下,但文件则上不封顶,因此就涉及到CSocket::Receive()的用法。需要注意,这一函数并不是你让它Receive多少就Receive多少,而是用返回值来告诉调用函数它收到了多少字节。处理的时候应当重视这一点。
    传文件和传消息的第二个不同就是模式不同。传消息是不需要征得接收方允许的,因此实现起来很方便。但是传文件需要发送方先发送请求,同时还要发送一些基本信息,比如文件名、大小等等,然后等待接收方同意。接收方收到请求和文件的基本信息后询问用户,再给发送方发送反馈。如果同意才开始正式的文件传输。不管代码实现是困难还是简单,算法是事先要搞清楚的。不过值得庆幸的是这一次我没有成为代码民工。看来前一段时间的反思是有效果的。
    开发文档的维护相当重要。从前一段开始很多程序就已经进入多线程的阶段了。虽然面向对象程序设计大大减少了开发者要维护的内容,但要处理的东西还是很繁杂的。类之间的关系,线程之间的配合,时间一长就容易忘掉。所以一份详尽完整的开发文档的维护是相当必要的。它不仅可以理清开发的过程,而且可以使后续开发可以在短时间内运作起来。
    架构!最重要的东西往往留在最后压轴。一个程序,或者说一个工程,最重要的东西就是架构。一方面因为架构从根本上决定/体现了程序的效率、算法,一方面因为一旦开发进入了编码阶段,架构的更改就非常困难,骑虎难下也许是最生动的比喻。所以对于架构一定要慎重慎重再慎重,思考思考再思考,理清楚各个部件的关系,谁包含谁,谁和谁通信。三思而后行。
    一件事往往看上去简单,但真正做起来的时候却不得不处理很多细节方面的问题。还是那句老话,纸上得来终觉浅,绝知此事要躬行啊!

    此外,stephen同学从跨平台兼容性的角度提出了很好的建议。可是我所知道的跨平台的方法就是在每个平台下都写一份代码,然而目前主流的操作系统Windows和Linux在创建线程/进程、界面构造等方面差异实在太大,除了傻傻的把这个程序再写一遍,我个人实在不晓得怎么搞 :-( 然后stephen同学说到了标准的问题,只要在写代码的时候支持POSIX标准就好了~另外,他提到了Linux和Windows在设计理念上的不同。觉得很好,引用如下:(已征得作者允许)
    ======================quote begin=========================
    我觉得跨平台不是这么理解的。
    正真讲到跨平台,应该是同一份代码能够直接或者只需很少的改动就能在多个平台上运行。
    比如Java程序就是跨平台的,同一份代码,完全不用修改就能在Windows、Linux、Mac等系统上运行,甚至能直接在手机上运行。但是,注意,我说的是Java程序,而不是Java虚拟机。Java虚拟机不是跨平台的,每个平台上的代码都不一样,但用Java开发的程序却是跨平台的。再说我刚才提到的Qt、GTK等,就开发工具而言并不是跨平台的,但开发出来的程序却是跨平台的,用Qt或GTK开发的程序,在界面部分的代码可以完全不用修改,就能直接在Win下运行。当然,涉及到其他一些东西,比如网络通信等,两个系统的接口不一样的,可能需要改一些代码,但这部份不属于Qt或GTK,当然也有解决方法,那就是Qt或GTK也考虑到了这一点,它们像MFC一样,对多线程、网络等都有自己的封装,直接用它们封装好的类就行了,就完全没有兼容性问题了。如果我用Qt写程序,我肯定会尽量不用fork这类函数,毕竟它们太底层了,而Qt已经封装的很高层了,掺和在一起写,程序就会很别扭。我向来坚持,要么写纯C,要么写纯C++,至少在一个源文件中是这样,不然程序很丑,就像现在班里很多人写算法实验,用的是C语言,但用cpp的后缀,而程序中唯一用到的C++的特性就是随地声明变量。这样的程序真的很难看。Qt有多线程的类,我学习一下~
    说实话,涉及到网络的东西,Linux/Unix有着先天性的长处。毕竟Win的设计初衷就是桌面系统,而Linux/Unix从最早的版本就设计成网络模式。所以现在服务器市场Linux/Unix占的份额还是比Win多。我们现在用电脑都是Single User(意思就是自己的电脑自己用,很少使用公共电脑),但其实*nix从最初就设计成终端模式,不推荐用户直接物理接触试用机器,而是像那个freeshell一样,大家共用一台机器,每个人都有自己的终端,互不干扰。所以*nix的在网络方面相当成熟。至于桌面系统,其实我个人觉得Linux本身并不想争桌面系统的地位,只是一些发行版在那里热闹而已。Linux因为其设计的初衷(多终端系统),它更多的是用于科研计算,就像集群机一样,提供很强的机器与机器/人与机器的交互行,合作完成高性能的计算。*nix下的X只是为了提高某些工作的效率,因为图形界面很多时候能比字符界面提供更多的信息。但很多事情还是命令行更高效。(如果用习惯的话)
    如果撇开界面,就核心部分而言,其实跨平台也不难,只要尽可能试用兼容POSIX标准的东西就行了。随便哪个系统,其进程管理/通信等各方面的接口肯定都是差不多的,可能只是函数名不大一样,头文件位子不一样,只要简单的写几个define就能解决问题的。
    ======================quote end==========================

    [coding]补码的设计思路

    [coding]补码的设计思路
    目的:通过设计一种新的编码来让减法转换成加法来做。
    如1011 - 0010能够利用加法器的电路来完成而不用专门设计减法器的电路。当然,运算数和结果全部用这种新的编码来表示。
    稍微思考一下可以发现,由于计算机的字长是有限的,我们有可能利用溢出这种现象来完成任务。比如1011 - 0010 = 1001,但是我们可以让1011 + 某个数,让它们的和等于11001,这样最高位被迫舍弃,得到的结果恰为1001。我们记减数为-A,计算得到的“某个数”为B。若A和B之间有某种确定的映射关系,则我们可以认为找到了一种新的编码。由于这种编码是针对于优化减法的,因此我们可以认为正数的编码为它本身,而负数的编码需要通过新找到(如果存在)的映射关系来计算。这样不论加法或是减法都可以用B0 + B来表示,B0,B以及结果都用这个新的编码来表示。
    下面从1011 - 0010这个特例出发,讨论是否存在这样的映射关系。
    由我们利用溢出进行计算的原理可知1011 + B = 11001 = 10000 + 1011 - A = 1111 + 1011 + 1 - A
    两边的1011消去,有B = 1111 - A + 1
    由于结果和被减数1011无关,所以不论被减数是什么,以上结论都成立,所以B与A有确定的映射关系。
    如果进一步从这个特力拓展,假设计算机的字长为n位,则很容易得到结论:
    B = 111...1(n个1) - A + 1
    从形象的角度来说,B就是A按位取反后加1得到。注意这里A指的是那个负数的绝对值。
    这样我们就得到了一种新的编码,由于负数-A在编码过程中需要计算111...1(n个1) - A,这是一个求补的操作,因此它的一个看起来比较适当的名称可以是:“补码”。正数的补码就是它本身,负数的补码根据机器字长按位取反再加1即可得到。但是由于一位被符号位占据,所以正数的合法范围是00...0 ~ 011...1,而负数是100...0 ~ 111...1。在补码下,加法和减法可以按照同一种逻辑进行计算。

    P.S.探讨既知知识的发明历程是一件很有意思的事情~假装他们还没有被发现,照着一般的思路来思考~发现~发明~~~
    P.S.2.从特例出发,探讨一般情况是一种很有启发性的思路~

    [coding]请喊我代码民工头,谢谢

    [coding]请喊我代码民工头,谢谢
    接到HY短信,问在C语言中怎么用printf把一个double型变量小数点后12位打出来。稍微试了一下%12f不行,然后代码民工本性开始发作了……觉得似乎可以通过写代码解决这个问题,于是从2030开始写代码,写写写,写到2330。搞定。
    然后HY无比迷惑发个短信过来:C语言库函数不能实现吗……
    一想是啊,废话,C语言有那么傻么,且不说这是一个基本功能,你都把打印6位小数给实现了,怎么会不提供打印12位小数。一翻书,我靠,类似%.12f的语句是可以实现改变有效数字位数的。
    代码民工了吧,写代码完全不想它的意义,完全不晓得它在整个程序架构中的地位,就知道写!写!写!真是……搞得跟一民办电脑培训学校C语言速成班毕业的一样。还本科生,还计算机系,买块豆腐撞死算了!
    最后小安慰一下,这次好歹知道关注一下算法,没有超级SB自己去写高精度乘法和加法来转二进制,用了滑动窗口转二进制的方法,也知道IEEE 754浮点数标准。想想看一般代码民工还写不出来这样底层的东西。所以……
    请喊我代码民工头,谢谢。

    P.S.以后除了注意算法以外,在写代码之前更要注意它的意义和整个程序中的地位。也许写代码之前先写用户文档是个不错的主意。

    [coding]template的头文件问题

    [coding]template的头文件问题
    写一个模板类的时候,照往常一样将声明和定义分开存放。然后在main前#include相应的头文件。可是连接的时候就是过不了,非要把.cpp文件也#include了才可以。
    比较诡异的一点是,在用Visual Assist X的Create implementation功能时,平常都是在.cpp文件中自动创建定义,可是在这个模板类里,它把定义自动放在了头文件里。
    后来通过上课听讲和查询资料才知道,模板类的定义和一般类是不一样的,在模板函数“定义”的时候并不立刻生成代码,分配内存,而是在调用的时候根据相应的类型参数分配代码段内存。从这个角度出发,模板函数的定义也应归为头文件中。
    上课听讲还是很重要滴~同时感谢Stephen同学的热心帮助~~~

    [coding]源码面前 了无秘密

    [coding]源码面前 了无秘密
    前两天每天熬夜两三点牛逼哄哄把2D-World和WumpusWorld的平台写出来了。方格世界周游世界的Agent写的无比复杂,当时还一览众山小的大放厥词,说只要是简单的算法都是错的。
    妈的人太狂就是要遭雷劈的啊。
    Stephen的算法写出来了,核心代码27行。这还是他良好的风格下无数空行和单独占行的大括号造就的。无语了都。
    他的平台功能更强,更人性化,支持多Agent,算法试了几张地图没发现什么错。连平台带Agent才16K,想想我23K的庞然大物,汗颜啊。
    总结了一下原因,一个是他的算法的确比我原先的算法好,和道福的算法是一样的,这个问题在前面的文章里已经分析过;另一个原因就是他编码的精巧和简洁。
    当然,我没有说编码短就是好,但精巧简洁和短是两码事。
    最重要的一点是,数学帮助他简化了很多很多的编码。
    在我的代码里,转弯是一个复杂的switch:
    //Rotate Right
    switch (LastDirection.to_ulong())
    {
    case UP: ActionResult = RIGHT; break;
    case DOWN: ActionResult = LEFT; break;
    case LEFT: ActionResult = UP; break;
    case RIGHT: ActionResult = DOWN; break;
    }
    但Stephen的就要简单许多:
    void turnRight(){dir = (dir+2) % 8;}
    都是用bitset放数据,数据结构都是一样的,算法我为什么就想不到呢?想了一下,觉得可以从以下几个方面来解释。
    首先是对代码要有优化意识。最直观的算法往往是很笨的。每次想到一种解决方法,尤其是看起来比较复杂的解决方法后,都要想一想,这个算法可以优化吗?不要想出来一个SB算法立马就跟代码民工似的吭哧吭哧写的一表多开心。
    然后是对经常出现的代码尤其要有优化意识。这个是优化的重点,在写完了以后还是要看一下的。
    最后要有利用数学进行抽象,简化编码的意识。数学的力量在于它的抽象性和概括性。既然我的数学不怎么熟练,不能做到一开始就想出那么艺术性的算法,就需要在提出算法以后有意识地想一想,这个复杂的算法可以用数学来抽象吗?常此以往,设计算法能力应当会有质的提高。
    提出要有这些意识是一回事,平时注意养成习惯又是一回事了~我会努力的~sigh~算法,我还差得远呢~一天到晚装B,被雷劈了才晓得自己是SB~
    Stephen编码简洁的第二个原因是类写的不像我那么啰嗦。看看我写的代码里很多都是废代码,那些类接口写的,明知道永远用不到还写一身劲,以后要改。
    把道福和Stephen放一起总结一下。总的来说就是三思而后行,还有看本质,用数学的习惯要在平时培养。嘿,其实做人不也这样?
    感谢上帝,感谢Stephen,在我狂的时候劈了我一下。被劈的外焦里嫩的-_-b|||,以后一定夹着尾巴做人,专心做一SB小言言~
    P.S. 其实Stephen的算法和道福一致,只是实现的更“相对化”,绝对坐标体现的更弱。这一点导致了编码的简洁。
    附:我根据Stephen思路修改的代码(原来花了我一晚上时间,写了6K啊……)
    /*
    program: World2DAgent.cpp
    function: the implementing file of the agent travelling around the virtual 2D-world.
    author: grapeot
    date: 20080909
    last modified date: 20080910
    */
    #ifndef _INC_WORLD2DAGENT_CPP
    #define _INC_WORLD2DAGENT_CPP
    #include "World2DAgent.h"
    //travel around the virtual world counter-clockwise
    bitset<8> World2DAgent::Action(bitset<8> SensorState)
    {
     bool isRightWall = !SensorState[(Direction + _R - _U) % 8];
     bool isLeftWall = !SensorState[(Direction + _L - _U) % 8];
     bool isFrontWall = !SensorState[Direction];
     bool isFrontRightWall = !SensorState[(Direction + _RU - _U) % 8];
     bool isFrontLeftWall = !SensorState[(Direction + _LU - _U) % 8];
     bool isBacktRightWall = !SensorState[(Direction + _RD - _U) % 8];
     bool isBackLeftWall = !SensorState[(Direction + _LD - _U) % 8];
      if (isRightWall)
     {
      if (!isFrontWall) ; //go forward
      else if (!isLeftWall) Direction = (Direction - 2) % 8; //turn left
      else Direction = (Direction - 4) % 8; //turn back
     }
     else
     {
      if (isBacktRightWall) Direction = (Direction + 2) % 8; //turn right
      else if (isBackLeftWall) Direction = (Direction - 4) % 8; //turn back
      else if (isFrontLeftWall) Direction = (Direction - 2) % 8; //turn left
      else if (isFrontRightWall) ; //go forward
      else if (!isFrontWall) ; //go forward
      else if (!isLeftWall) Direction = (Direction - 2) % 8; //turn left
      else Direction = (Direction - 4) % 8; //turn back
     }
     return bitset<8>(0).set(Direction);
    }
    #endif

    [Coding]再剧透 来个demo

    原来做成gif的,Live Space显示不了,鄙视一下

    123456789101112131415 

    [coding]学到一个新词 剧透!慎入!WumpusWorldSimulationPlatformDemo 标题要长长长长长

    Starting to read the world from input file: ExampleWorld.txt
    m reads successful, m = 5
    n reads successful, n = 4
    World content reads successful.
    Content:
    WP...
    .....
    GP...
    ..P..
    Step 0
    ---------------------------------
    WP...
    .....
    GP...
    X.P..
    ---------------------------------
    Agent location: (0, 3)Agent direction: Right
    AgentSensorState:
    AgentLastAction:
    TakenGold: 0    Shoot: 1
    Score: 0
    Step 1
    ---------------------------------
    WP...
    .....
    GP...
    X.P..
    ---------------------------------
    Agent location: (0, 3)Agent direction: Up
    AgentSensorState:
    AgentLastAction: TurnLeft
    TakenGold: 0    Shoot: 1
    Score: -1
    Step 2
    ---------------------------------
    WP...
    .....
    XP...
    ..P..
    ---------------------------------
    Agent location: (0, 2)Agent direction: Up
    AgentSensorState: Breeze Glitter
    AgentLastAction: Go
    TakenGold: 0    Shoot: 1
    Score: -2
    Step 3
    ---------------------------------
    WP...
    .....
    XP...
    ..P..
    ---------------------------------
    Agent location: (0, 2)Agent direction: Up
    AgentSensorState: Breeze
    AgentLastAction: Grab
    TakenGold: 1    Shoot: 1
    Score: -3
    Step 4
    ---------------------------------
    .P...
    .....
    XP...
    ..P..
    ---------------------------------
    Agent location: (0, 2)Agent direction: Up
    AgentSensorState: Breeze Scream
    AgentLastAction: Shoot
    TakenGold: 1    Shoot: 0
    Score: -4
    The agent sends a CLIMB instruction. But it is not in the start, instruction ign
    ored.
    Step 5
    ---------------------------------
    .P...
    .....
    XP...
    ..P..
    ---------------------------------
    Agent location: (0, 2)Agent direction: Up
    AgentSensorState: Breeze
    AgentLastAction: Climb
    TakenGold: 1    Shoot: 0
    Score: -5
    Step 6
    ---------------------------------
    .P...
    .....
    XP...
    ..P..
    ---------------------------------
    Agent location: (0, 2)Agent direction: Right
    AgentSensorState: Breeze
    AgentLastAction: TurnRight
    TakenGold: 1    Shoot: 0
    Score: -6
    Step 7
    ---------------------------------
    .P...
    .....
    XP...
    ..P..
    ---------------------------------
    Agent location: (0, 2)Agent direction: Down
    AgentSensorState: Breeze
    AgentLastAction: TurnRight
    TakenGold: 1    Shoot: 0
    Score: -7
    Step 8
    ---------------------------------
    .P...
    .....
    .P...
    X.P..
    ---------------------------------
    Agent location: (0, 3)Agent direction: Down
    AgentSensorState:
    AgentLastAction: Go
    TakenGold: 1    Shoot: 0
    Score: -8
    Step 9
    ---------------------------------
    ...P.
    ...P.
    .G.P.
    XP..W
    ---------------------------------
    Agent location: (0, 3)Agent direction: Right
    AgentSensorState: Breeze
    AgentLastAction: Climb
    TakenGold: 0    Shoot: 1
    Score: 991
    The agent sends a STOP instruction.
    请按任意键继续. . .

    [coding][神!][原创]手把手教你用机器码编程

    [coding][神!][原创]手把手教你用机器码编程
    本文的目的在于让各位了解最基本的机器码知识。
    例程仅有30字节,尽情揭露各位的本质~咔咔~
    方法1:
    1.开始->运行,输入debug,回车
    2.复制如下代码并粘贴到debug中去:
    e100 BA 11 01 B4 09 B9 0A 00 CD 21 E2 FC B4 01 CD 21
    e110 C3 49 20 61 6D 20 61 20 53 42 2E 0D 0A 24
    具体的方法是单击debug窗口左上角的命令提示符图标,选择编辑->粘贴,回车即可。
    3.输入g (运行的意思,括号里面的东西不要傻乎乎打进去啊),回车,看结果吧~
    4.看完结果后按q回车可退出debug。
    方法2:
    1.打开UltraEdit32,新建一个文件,输入30个A(或者随便什么字符,仅为下一步做准备)。
    2.按Ctrl + H切换到HEX编辑模式,定位到第一字节,依次输入下列代码:
    BA 11 01 B4 09 B9 0A 00 CD 21 E2 FC B4 01 CD 21
    C3 49 20 61 6D 20 61 20 53 42 2E 0D 0A 24
    3.保存为.com文件,双击运行看看!
    代码分析:
    方法1里,e100,e101是debug的内部命令,暂且不管,其他部分是指令。
    为了方便分析,首先将地址和代码对应一下:
    00h: BA 11 01
    03h: B4 09
    05h: B9 0A 00
    08h CD 21
    0Ah: E2 FC
    0Ch: B4 01
    0Eh: CD 21
    10h: C3
    11h: 49 20 61 6D 20 61 20 53 42 2E 0D 0A 24
    从11h开始实际是I am a SB, CR, LF(回车换行), $的ASCII码,注意偏移地址为11h。这是这个程序的数据段。下面分析指令段。
    回忆一下计算机文化基础的内容,CPU的指令由操作符和操作数组成,前面已经将每条指令都划分出来了。第一个字节是操作符,后面的是操作数。
    再普及一个知识,.com文件读入内存时前100h字节是保留的,因此在内存寻址时需要把偏移量加上100h成为绝对地址。知道了这些就可以分析这个程序了。
    频繁出现的一个指令是CD 21,CD的意思是调用DOS中断,那这个21(注意是16进制)是不是很熟悉啊~~它就是操作数~CD 21就是调用21号DOS中断的意思~如果不晓得什么是DOS中断的话,只要知道B4 09, BA指令和CD 21结合可以显示一个字符串,B4 01和CD 21结合可以读入键盘就可以了~
    前2条指令,B4 09让电脑准备打印字符串,(在CD 21时正式开打),BA 11 01将字符串的地址11 01读入寄存器,由于x86架构采用高地址存高位的存储方式,所以这里实际上把I am a SB的绝对首地址0111h读入了寄存器,经过了这两步之后,只要调用CD 21,就可以打印字符串了~
    05h: B9 0A 00和0Ah: E2 FC构成了一个循环。B9指令将循环次数0A 00(即10次)读入循环寄存器,E2是循环跳转指令,将循环寄存器内的值减一,如果寄存器内的数不是0则跳转到相应偏移量的地方去执行,否则继续下一条指令。这里偏移量是FC,即-4,所以向前跳4个字节,即08h的地方执行。
    若10次打印结束,调用B4 01, CD 21,暂时中断程序,等待从键盘读入一个字符,这里实际上起得是一个Press any key to continue的作用。
    C3是退出程序的指令。
    所以整个程序的作用就是,打印一个字符串10次,然后中断程序,在用户按下任意键后退出。
    事实上也很容易发现,机器码和汇编有很简单的一一对应的关系(至少这个程序里是这样),所以很容易反汇编出它的汇编源程序(执行完方法1后,直接键入u回车即可反汇编):
    147A:0100 BA1101        MOV     DX,0111
    147A:0103 B409          MOV     AH,09
    147A:0105 B90A00        MOV     CX,000A
    147A:0108 CD21          INT     21
    147A:010A E2FC          LOOP    0108
    147A:010C B401          MOV     AH,01
    147A:010E CD21          INT     21
    147A:0110 C3            RET
    ;此处开始为数据段,但反汇编器没我们聪明看不出来,依旧照指令的方式反汇编
    147A:0111 49            DEC     CX
    147A:0112 20616D        AND     [BX+DI+6D],AH
    147A:0115 206120        AND     [BX+DI+20],AH
    147A:0118 53            PUSH    BX
    147A:0119 42            INC     DX
    147A:011A 2E            CS:
    147A:011B 0D0A24        OR      AX,240A
    147A:011E 0000          ADD     [BX+SI],AL
    人工修饰一下得到:
    ORG  100h    ;伪指令,com文件必须
    MOV DX, OFFSET msg
    MOV AH, 9
    MOV CX, 10
    next:
    INT 21h
    LOOP next
    MOV AH, 1
    INT 21h
    RET
    msg         DB 'I am a SB.', 0dh, 0ah, '$'
    result
    你也可以试试改动机器码~写出自己用原汁原味的机器码写出的HelloWorld!
    P.S.如果没有UltraEdit也可以用C写一个小程序如下:
    #include<stdio.h>
    int main(void)
    {
     char data[30] = {0xBA, 0x11, 0x01, 0xB4, 0x09, 0xB9, 0x0A, 0x00, 0xCD, 0x21, 0xE2, 0xFC, 0xB4, 0x01, 0xCD, 0x21, 0xC3, 0x49, 0x20, 0x61, 0x6D, 0x20, 0x61, 0x20, 0x53, 0x42, 0x2E, 0x0D, 0x0A, 0x24};
     FILE *F = NULL;
     F = fopen("temp.com", "wb");
     fwrite(data, 1, 30, F);
     fclose(F);
     return 0;
    }
    值得玩味的是,fopen的模式改成w就不行,wb就可以。经过比较两个模式下生成的文件发现,在wb模式下,文件与data数组的内容完全相同,但是在w模式下,data中所有单独出现的0A或者0D都被篡改成了0A 0D(回车换行),这样就可以理解为什么w被定义为适用于文本文件的输出格式了。所有单独的回车符或者换行符都被当作回车换行符输出来。这样对Windows环境下的文本文件而言的确不容易出乱子。

    [coding]汇编风格

    [coding]汇编风格
    TEST, CMP等等要和条件跳转语句紧挨着,否则中间的其他语句可能会破坏PSW。
    选择寄存器很重要,要了解在何种情况下这个寄存器的值会改变。比如调用21H中断会改变AL寄存器的值(即使AH != 1的情况下有时也如此)。所以就在调用中断前确认参数是好习惯。
    AX寄存器不能用来间接寻址, DX不可用来段间寻址?
    段定义中的名字代表的是地址。
    用存储单元传递参数思路比较清晰,但不适用于递归。
    用堆栈传递参数时不要忘了加上段前缀SS:
     
    P.S.汇编这么麻烦,不如自己写个编译器算了
    P.S.2. 所谓细心不出错就是这样积累来的。
    P.S.3.历时5天的汇编学习至此告一段落。

    [coding][实用]阶乘

    [coding][实用]阶乘
    这样简单的代码贴上来的确比较丢人,但是有不少实用的地方,比如子程序的调用,堆栈传参数,递归的写法,2进制向BCD码的转换(不要用除法,效率太低下)等等。
    汇编不是用来做数学运算的,做接口才是王道。
    ;program: factor.asm
    ;function: calculate factor using recursion
    ;author: grapeot
    ;date: 20080811
    name 'factor'
    .model small
    .stack 100h
    .data                                
        MsgEnter DB 0dh, 0ah, '$'
        Msg1 DB 'Please input a number(0~8):$'
        Msg2 DB 'Factor of the input:$'     
        Msg3 DB 'Error! Input out of range!', 0dh, 0ah, '$'
        Word2BCD_DecimalBase: DW 1, 10, 100, 1000, 10000
    .code
    main PROC far                 
        .startup
        ;input
        begin:
        LEA DX, Msg1
        MOV AH, 9
        INT 21h
        MOV AH, 1
        INT 21h
        MOV BL, AL  ;store the input
        AND BL, 0fh ;adjust for ascii code
        LEA DX, MsgEnter
        MOV AH, 9
        INT 21h     ;enter
        ;check the input 
        CMP BL, 8
        JA err
        JMP nerr
        err:
        CALL error
        JMP begin
        nerr:
        ;output header
        LEA DX, Msg2
        MOV AH, 9
        INT 21h
        ;call the subproc
        MOV BH, 0
        PUSH BX ;to from a word and then deliver the parameter
        CALL fact
        CALL Word2BCD   
           
        .exit 0
        RET
    main ENDP 
    ;fact proc near
    ;calculate the factor of the input using recursion
    ;the parameters and result are delivered via stack
    fact PROC near
        ;preserve the original registers
        PUSH AX
        PUSH BX
        PUSH DX
        ;calculate
        MOV BX, SP
        MOV AX, SS: BX[8]
        CMP AX, 0
        JNE next
        ;MOV BX, SP
        MOV SS: BX[8], 1
        JMP FactReturn
        next:
        ;next recursion
        MOV DX, AX
        DEC DX
        PUSH DX
        CALL fact
        POP DX
        MUL DX
        ;MOV BX, SP
        MOV SS: BX[8], AX
       
        ;restore the original registers and push the result
        FactReturn:
        POP DX
        POP BX
        POP AX
        RET
    fact ENDP
    ;error proc near
    ;prompt an error message
    ;no parameters are passed
    error PROC near     
        PUSH DX
        PUSH AX
        LEA DX, Msg3
        MOV AH, 9
        INT 21h
        POP AX
        POP DX
        RET
    error ENDP 
             
    ;Word2BCD proc near
    ;convert a word of binary code into BCD code then print it on the screen
    ;the parameters are delivered via stack
    Word2BCD PROC near                    
        ;preserve original contents in registers
        ;and get the parameter
        PUSH AX
        PUSH BX    
        PUSH CX
        PUSH DX
        MOV BX, SP
        MOV AX, SS: BX[10]
       
        ;deal with the first 4 figures
        MOV CX, 4
        Word2BCD_Next:
            MOV BX, CX
            ADD BX, CX
            MOV BX, DS: [Word2BCD_DecimalBase + BX]
            MOV DX, 0
            ;use subtraction to do division for 10000 is too large for a single byte
            Word2BCD_Compare:
            CMP AX, BX
            JAE Word2BCD_Subtract
            JMP Word2BCD_Continue
            Word2BCD_Subtract:
            INC DX
            SUB AX, BX
            JMP Word2BCD_Compare
            Word2BCD_Continue:
            ;print
            CMP DL, 0
            JE Word2BCD_Loop
            MOV BX, AX  ;store the remain in BX for AX will be used for calling interpretation
            OR DL, 30h
            MOV AH, 2
            INT 21h
            MOV AX, BX  ;restore AX
        Word2BCD_Loop:
        LOOP Word2BCD_Next
       
        ;deal with the digit
        MOV DL, BL
        OR DL, 30h
        MOV AH, 2
        INT 21h
                        
        POP DX
        POP CX
        POP BX
        POP AX
        RET 1            
    Word2BCD ENDP
    END
    result

    [coding]second assemble program

    [coding]second assemble program
    第一个理所当然是HelloWorld,第二个是个查找表,查找0~9的平方,用二进制和十进制打印出来。
    SourceCode:
    ;Program: search.asm
    ;Function: do a search operation then print the result on the screen in binary and decimal
    ;Author: grapeot
    ;Date: 20080811
    name 'Search'
    .model small
    .stack 100H
    .data    
        table DB 0, 1, 4, 9, 16, 25, 36, 49, 64, 81
        msg1 DB 'Please input one number (0~9):$'   
        msg2 DB 'Square of input:', 0dh, 0ah, '$'  
        result DB 2 dup (0)
               DB 'd$'
        MsgEnter DB 0dh, 0ah, '$'
    .code
    .startup
    PSearch PROC far
    input:
        LEA DX, msg1
        MOV AH, 9
        INT 21h
        MOV AH, 1
        INT 21h
        AND AL, 0fh
        MOV AH, 0   ;adjust for ascii code
       
    search:
        LEA BX, table
        ADD BX, AX
        MOV BL, BYTE PTR [BX]
                                               
        LEA DX, MsgEnter
        MOV AH, 9
        INT 21h         
        LEA DX, msg2
        MOV AH,9
        INT 21h
        MOV CX, 8   ;8 bits
    printb:
        TEST BL, 10000000b  
        JZ mov0
    mov1:               
        MOV DL, '1'
        JMP callint
    mov0:         
        MOV DL, '0'
    callint:
        MOV AH, 2
        INT 21h
        ROL BL, 1   ;use ROtateLeft instead of SHiftLeft to preserve the result after print
        LOOP printb
           
        MOV DL, 'b'
        MOV AH,2
        INT 21h
        LEA DX, MsgEnter
        MOV AH, 9
        INT 21h
                                     
        MOV AX, 0
    convertd:
        ;convert to decimal          
        CMP BL, 0ah
        JAE continue 
        ; end
        MOV AL, BL
        OR AL, 30h
        OR AH, 30h   ;adjust for ascii code
        MOV [result], AH        ;tens
        MOV [result + 1], AL    ;digit
        JMP printd;
    continue:
        INC AH   
        SUB BL, 0ah
        JMP convertd
    printd:
        LEA DX, result
        MOV AH, 9
        INT 21h
       
    PSearch ENDP
    .exit 0
    END
    运行效果:
    screen

    [coding]好花哨的HelloWorld...

    zz from the first example of emu8086
     
    name "hi-world"
    ; this example prints out  "hello world!"
    ; by writing directly to video memory.
    ; in vga memory: first byte is ascii character, byte that follows is character attribute.
    ; if you change the second byte, you can change the color of
    ; the character even after it is printed.
    ; character attribute is 8 bit value,
    ; high 4 bits set background color and low 4 bits set foreground color.
    ; hex    bin        color
    ;
    ; 0      0000      black
    ; 1      0001      blue
    ; 2      0010      green
    ; 3      0011      cyan
    ; 4      0100      red
    ; 5      0101      magenta
    ; 6      0110      brown
    ; 7      0111      light gray
    ; 8      1000      dark gray
    ; 9      1001      light blue
    ; a      1010      light green
    ; b      1011      light cyan
    ; c      1100      light red
    ; d      1101      light magenta
    ; e      1110      yellow
    ; f      1111      white
     

    ORG 100h
    ; set video mode   
    MOV AX, 3     ; text mode 80x25, 16 colors, 8 pages (ah=0, al=3)
    INT 10h       ; do it!
    ; cancel blinking and enable all 16 colors:
    MOV AX, 1003h
    MOV BX, 0
    INT 10h

    ; set segment register:
    MOV     AX, 0b800h
    MOV     DS, AX
    ; print "hello world"
    ; first byte is ascii code, second byte is color code.
    MOV [02h], 'H'
    MOV [04h], 'e'
    MOV [06h], 'l'
    MOV [08h], 'l'
    MOV [0ah], 'o'
    MOV [0ch], ','
    MOV [0eh], 'W'
     
    MOV [10h], 'o'
    MOV [12h], 'r'
    MOV [14h], 'l'
    MOV [16h], 'd'
    MOV [18h], '!'
     

    ; color all characters:
    MOV CX, 12  ; number of characters.
    MOV DI, 03h ; start from byte after 'h'
    c:  MOV [DI], 11101100b   ; light red(1100) on yellow(1110)
        ADD DI, 2 ; skip over next ascii code in vga memory.
        LOOP c
    ; wait for any key press:
    MOV AH, 0
    INT 16h
    RET
     
    运行结果:
    result

    [coding]天哪天哪天哪

    [coding]天哪天哪天哪
    EditPlus就是神啊,AoGo神啊~
    以前只知道EditPlus+gcc组简单的C/C++ IDE,原来结合Windows批处理/脚本还是可以这样玩的啊~
    给MASM For EditPlus做下广告吧~
    Windows也可以组IDE的啊,不逊于VI + gcc + gdb哦~而且EditPlus不比那个号称巨无敌,但学习至少要3个月的VI差哦~只是大家不知道怎么用而已~比如选定一个矩形区域(在未Word Wrap的模式下Alt + 鼠标拖动),比如一次删除一个词(Ctrl + Backspace),都很方便,而且快捷键比VI的诡异容易适应很多。而且他的可扩展性也很无敌~哼哼~
    把MASM for EditPlus的Readme贴上来~
    =====================================================
       MASM For EditPlus v1.0 Serial 0002自述文档
      ===========================================================
        !!!如果您是第一次安装MASM for EditPlus 1.0,我强烈建议您认真的看完这个ReadMe文件!!!
    EditPlus注册信息:
    =================================
      用户:XXOXOXOXOX
      注册码:XXOOXOXOX
    关于前一版MASM for EditPlus v1.0 Serial 0001的说明
    ==================================================
      经过长时间的修改与测试,MASM for EditPlus终于可以成为一个成熟的MASM配置包了,1.0的MASM for EditPlus即将完成之际,又得知MASM已经升级到v8.0,基于这个原因,我把MASM5、MASMv8.0的LIB/INCLUDE/BIN还有VC资源编辑专版一起打包到MASM for EditPlus中,方便大家(更多是为初学者)设置MASM开发环境,也就是说,我这样做的目的是只要您安装MASM for EditPlus就可以使用MASM进行Win32ASM编程,我决定把现在这个MASM for EditPlus正式定为1.0 Serial 0001,以便于日后大家对照版本号升级.
      MASMv8.0的升级变化非常大,在制作新的语法文件中,发现MASMv8.0相对于7.0升级方面做了非常大的更新,API函数新增6608个(包括UNIcode函数),新增247个结构类型,新增516个常数,包括以前不支持的一些控件风格现在都添加了,尤其在API函数方面,更新可说是巨大,添加了一些新的API模块,如MSI/SQL接口函数,其中有近1/3都是以前的函数的UNIcode函数,还优化了LIB,虽然编译器与链接器没有更新,但是MASM可以说是越来越完善,MASMv8.0更是为在Windows后述版本中开发程序打下良好的基础,我相信,只要没有停止更新,在Windows开发平台上,MASM将与通行世界的ANSI C一样是一个永恒的开发工具。
      因为所以,这一次MASM for EditPlus的变化也非常大,批处理几乎全部重写,由原来的8个BAT集成在四个BAT中,更加小心地控制环境变量,尽量避免因为环境变量过多而发生"Out of environment space"的错误,相信控制之下此错误是不会再出现了(如果您不幸碰到这个错误而无法进行工作,请和我联系或到论坛提出),同时,MASM For EditPlus v1.0 Serial 0001集成了C/C++(VC6.0)的开发环境设置,能对C/C++进行很全面的支持,添加了新的C/C++模板,编辑C/C++文件现在能和MASM一样支持控制台与DLL的编译,支持语法检查,支持使用SoftICE进行源码级调试。

    MASM for EditPlus v1.0 Serial 0002更新
    ====================================================
      批处理在编译时会自动识别扩展名.asm/.c/.cpp进行不同的编译,MASM for EditPlus v1.0 Serial 0002最大的更新就是使用了一个程序进行编译时控制,EXE/DLL/LIB/VXD/MDI/DLG等等程序都只需要按Ctrl+1就可以编译,唯一的要求就是需要您在源程序的最前面添加一行来表示,只要在最前面三行就行,因为编译程序是在找到双引号后再取后面三个字符的,所以只需要关键字对就可以了,如在第一行直接提供;"EXE",当然C/C++则是//"EXE",相关的编译关键字有DOS/CON/DLG/EXE/MDI/DLL/VXD/LIB,这样做还有一个好外就是方便手工进行编译的朋友,你只需在启动时打通MASM的BIN目录,以后在DOS下只需要输入 mlink 文件路径 就可以一步编译完成.当然前提是程序中要提供编译说明.
      出于可能发生错误,我没有提供当没有提供编译模式时的默认编译方式,如果您觉得有必要,可以这样做:打开BIN目录下的BuildASM.bat,找到"REM 默认方式跳转"一行,下面那行则是当没有提供编译关键字时的跳转,我默认是返回,如果您正在学习Win32汇编,可以选择跳转到ISEXE,如果正在学DOS汇编,则可以选择跳到ISDOS,这个就依你自己来看了。另外,BuildC++.bat也可以这样修改一下。
      为了方便您定制MASM for EditPlus,编译程序的源代码也在BIN目录下,名字是compiler.c,如果您熟悉C/C++语言,您可以修改它来定制编译程序.

    以下所有的说明都是Serial 0001 与Serial 0002相同的部分
    =====================================================
      关于C/C++编译还有一个重要的信息就是,链接时我添加了基本的库文件,如果您在VC的源代码文件中用到了其它没有加入的库中的函数,则需要自己添加才行,具体请打开BIN目录下的BuildC++.bat,找到引用库的地方(就是kernel32.lib user32.lib gdi32.lib..这些列表),在其中添加其它没有引用的库文件名就行了.注意二个地方都要添加.
      使用中快捷键有:(第一次编译请先保存)
      -----------------------------------------------------
      CTRL+1 编译(.asm/.c/.cpp)
      CTRL+2 运行已经编译的程序
      CTRL+3 使用SoftICE源码调试
      CTRL+4 是进行资源编辑
      工具位置与名字都可自定,另外还可以根据需要再增加新工具,如使用Debug调试DOS程序
    关于相关模板的说明
    ===============================
      为了方便,我已经为您写了一系列的模板,对应表如下:
      -----------------------------------------------------
      ASM-DOS DOS下汇编模板
      ASM-CON 控制台程序模板
      ASM-DLG 以对话框为主的程序模板
      ASM-GUI 标准Windows32位GUI程序模板
      ASM-MDI 标准Windows32位GUI多文档程序
      ASM-DLL 动态库程序模板
      ASM-VxD 虚拟设备驱动程序模板
      ASM-LIB 函数库模板,把有用的函数编译为LIB可以方便地重利用
      C++-CON 同上
      C++-DLG 同上
      C++-GUI 同上
      C++-DLL 同上
      C++-LIB 同上
      --------------------------
      其中DLG/GUI/MDI/DLL等模板在编译时,如果同目录下不存在同名的RC/RES文件,则批处理会把对应的RES文件复制到当前源文件目录下面,DLL在检查RC/RES的同时,还会检查同目录下是否存在DEF文件,如果不存在则自动创建一个.
      建议汇编初学者先从DOS汇编学起,再转向Windows GUI程序设计,而一些API函数的用法建议在C/C++中学习.
    关于使用SoftICE进行源码级调试程序
    ===============================
      如果想使用SoftICE进行源码级调试程序,BIN目录下必须有MSPDB60.DLL这个文件,而这个文件在MASMv7.0的安装包中是没有带的,在VC6.0中倒是有一个,不过请放心,MASM For EditPlus安装时已经把这个文件安装到了BIN下.
      您在编写程序时可以直接按CTRL+3或点工具栏上的3号工具进行带符号编译然后启动SoftICE进行源码调试,当然前提您得先安装好SoftICE并让它在系统启动时加载,1.0的MASM for EditPlus开始支持C/C++程序的源码级调试,如果您正确安装了SoftICE并已经加载,CTRL+3后会自动编译并激活SoftICE停在当前程序的入口处,调试时您可以在代码中设置断点,可都是源码级调试哟!
      众所周知,用SoftICE进行源代码调试时不支持中文显示。
      另外,请不要升级EditPlus,因为它高版本在文件扩展名上有些出入,升级后会因为这个原因而无法编译.

    关于资源文件的使用
    ================================
      您只需把资源脚本(rc)或资源模板(res)保存为与asm文件同名的文件即可,编译时编译包会自动识别,如果一个asm文件目录同时存在rc与res,默认为优先使用rc,这是因为很多的源代码都是使用rc,当然您可以修改.bat文件来改掉默认值,只需打开Bin目录下的BuildRES.bat,找到Set MAINRC=RC改成Set MAINRC=RES就行了.
      建议使用VC资源编辑专版进行编辑并保存为RES.因为VC资源编辑版保存为RC后会因为一些设置而导致无法编译成OBJ.
      如果您不会写批处理,请不要修改.bat文件,如果想重新设置参数,可以重新安装一遍,在安装过程中把所有的路径都设置好,另如果由于其它任何原因导致无法正常使用,也请重新安装,这样能恢复到最初状态。
      使用中有任何问题,请给我发Mail或到我的论坛给发贴,我会尽力帮您解决.
    MASM for EditPlus相对的优点:
    ============================
      1.支持关键字(汇编指令/API函数/API常数/API结构)语法高亮显示.
      2.支持API函数/API常数/API结构自动完成(在输入后按空格会自动按正确单词改变大小写)
      3.只需保存一次,以后保存、编译、链接、运行一步完成.
      4.编译不同模式的代码时能自动的创建相关的文件
    MASM for EditPlus已知的缺点:
    ============================
      1.新文件第一次编译必须手工保存
      2.asm文件路径中不能包含空格.
      3.不支持API函数/API常数/API结构原型显示(可以使用CGB的ApiPopList).
      4.没有使用Make,每次编译都是全部更新.
    估计下个版本的变化:
    ============================
      修改EditPlus,能让它有显示API函数原型的功能(有相当的难度,因为要反汇编EditPlus并插入自己的代码,如果实用性不高或本人难以适度此难度将放弃).
      上述功能完成后,我想MASM for EditPlus基本上就不会再更新了,做为一个编辑器,这些功能已经能够很方便的进行编程,当然最主要的原因是EditPlus不是我写的程序,如果真的要做一个IDE,唯一的办法就是自己的,我也正在努力的写,希望能早日写出一个好的IDE。

      本配置文件所有批处理与语法文件都是由AoGo自己编写配置的,花费了相当大的精力在其中,转载请不要去掉本文件。
      ========================================
      衷心希望MASM For EditPlus能给您一些帮助.
      ========================================
       AoGo
       sixnabs@163.com
       站点域名:
       http://aogo.yeah.net

    [coding]VerilogHDL 比 VHDL优越的地方

    [coding]VerilogHDL 比 VHDL优越的地方
    1. 符号{ }的使用
    2. 位域选择
    3. casex, casez的支持
    4. 模块参数的设定
    5. 编码更简洁,和C语言语法相近(这个是不是优越的地方就很难说了)
    6.二维数组的应用更加方便
    P.S.两天搞定VerilogHDL,感觉蛮好~进一步只能在实践中练习了~不过GRE荒废了……
    P.S.2.海明码原来是(7,4)线性分组码啊