我决定将最近读的Intel汇编的东西写下来,慢慢更新。汇编这个东西嘞,你让我拿来写个大程序,真是组织不了,没那个架构能力。但是看看工作过程,对理解其它东西可能有点帮助吧。知道比不知道好。找空就开始写。
我以《Intel汇编语言程序设计》(第五版)为顺序,写下我读书后,还能剩下的东西。很多东西书上没有写,我觉得它省略的,我自己给加上了,为得是给我自己看,如果能帮助各位理解,当然更好。内容讲intel汇编(windows平台使用),至于另一种汇编AT&T,则是linux下采用的。大家学习那一种汇编都是可以的,因为处理器都是一个架构的,机器码都一样,所以有很多工具可以互相转的,在linux下调试的时候,用命令简单设置一下成intel汇编就可以了,都一样的。
一 基本概念
1 汇编语言与高级语言的区别
对我个人来讲,是因为一条汇编指令对应一条机器语言指令,有助于我对操作系统和应用程序的理解。而高级语言如C++等,一条语句对应多条机器码的。
2 虚拟机的概念
这里和我们讲的云计算里的虚拟机是不一样的,应该说和java虚拟机的概念比较像。这里讲的虚拟机是一个更抽象的概念,主要是从虚拟机的角度考虑计算机系统的分层设计。目前大家使用的高级语言的程序设计,一般都是在第五层,如C++,这些高级语言编译成汇编语言,汇编语言就在第四层。每条汇编就是一条指令,可以理解为CPU厂商给操作系统厂商提供的接口。所以AMD和Intel都给微软提供了相同的x86指令,但是CPU内部还是不一样,那里不一样?就是微指令集不一样,这个是他们保密的东西,目前来看,Intel的效果要好一些。微指令操作的是数字逻辑了,这个算是硬件。
3 计算机中数据的存储
在计算机系统中,所有存储的信息,最终都变成了数值。不管是程序,还是电影,最终都会转化成数值存储到计算机上。所以我们写程序时,存储了一个字符串,别以为计算机真的把abcd都存储了,其实都是转化成数值存储的。而计算机中的整数一律用补码来表示和存储。所以说,所有的整数,最终在计算机里,都成了补码。还有浮点数,数据地址,指令等,不是补码哈。补码对人类来讲,其实很不好理解,一眼看不出来是多大,也不方便转成10进制的数来理解。那么,计算机为什么还是要使用补码?因为使用补码,可以将符号位和数值位统一处理,加法和减法也可以统一处理,不需要额外的硬件电路,更有利于CPU的设计。好吧,都是为了硬件设计考虑才这么干的,硬件工程师为了自己方便,就这么干了(其实是为了CPU方便)。那么到底有没有一种更友好的其它什么码,更有利于我们人类的理解?好吧,有一个,叫原码。原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值。例如,我们用8位二进制表示一个数,+11的原码为00001011,-11的原码就是10001011。原码不能直接拿符号位参加运算,可能会出错。例如数学上,1+(-1)=0,而在二进制中原码 00000001 + 10000001 = 10000010,换算成十进制为-2。显然出错了。所以原码的符号位不能直接参与运算,必须和其他位分开,这就增加了硬件的开销和复杂性。好吧,原码对CPU来讲很烂,补码很好。那么补码是怎么表示数值的嘞?是这样干的:正整数的补码与原码相同,而负整数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 然后再加1。然尔有一个问题,在前面的说明中,我们都使用原码来转到补码的。在一个字节8bit的空间,用原码,我们可以表示的整数范围是-127~+127,你看,只有255个值,而根据信息论,我们8bit的数字,最多可以表示256个值,那么少的那一个,跑那里去了?0,对,就是这个数,在原码是,会出现 10000000 和 00000000 都是0的问题,一个是负0,一个是正0。负0有个毛意义啊,在补码中,就当成-128吧,这样,补码就可以用8bit空间,表示出256个值,圆满了。
刚才讲了,所有的东西进了计算机,都成了补码,那么我的名字,我的电子邮件,是怎么转化成补码的嘞?其实为了表示字符,计算机需要使用字符编码(Character Encoding)。字符编码,通俗的说,按照何种规则将字符存储在计算机中,如'a'用什么表示,称为"编码";反之,将存储在计算机中的二进制数解析显示出来,称为"解码",如同密码学中的加密和解密。到了这里,好像很简单了,有了字符编码,不就什么都解决了嘛。but!没这么简单。计算机是善良的美国人民发明的,为了计算炮弹弹道才来的研发动力,搞出了计算机。美国人民用英语,平时写写文件,26个英语字母再加几个标点符号,就几乎可以搞定所有的一切了。所以先驱们就用7位(bits)表示一个字符,共128字符, 够用了吧,绰绰有余啊,这样,就诞生了ASCII字符编码,用在MS-Dos系统里,大家开心又满足。所以啊,同志们哪,写程序就要像先驱一样,多留点空间,能分配1M的,就不要用1K,为了扩展嘛。到这里,就出现了字符集,字符集(Charset)就是一个系统支持的所有抽象字符的集合,作用就是将字符映射为数值,而数值就能通过补码存储到计算机里了。ASCII字符集就是那128字符,其它的表示不了,不认识,它就懂128字符。所以计算机到了天朝,就玩不转了,不能显示汉字。那么我们现在清楚了什么是字符集,什么是字符编码。除了ASCII,后来还有了个ANSI编码,也是美国人民搞的,这个有了巨大进步,终于可以表示256个字符了!大家欢欣鼓舞,弹冠相庆。可惜还是表示不了汉字,天朝人民整天翘首以盼,啥时候能打五笔啊?天朝专家这时候应运而生,凑到一起吃吃喝喝,顺便无聊的时候搞出一个规定:汉字占两个字节,英文字母一个字节,一个小于127的字符的意义与ASCII相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到 0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。那么这个规定,这个编码规则就是GB2312。原来经常在windows下搞MFC的小伙子们,都对这个GB2312应该很有感受,如果没有感受,那么你一定是个不会写程序的计算机硕士。天朝人民有了GB2312,终于满足了大家都要打五笔的迫切愿望,但是这个时候,来自斯洛伐克等世界知名大国不干了,纷纷表示对五笔无爱,要制定自己的编码规则,而且还真这么干了。后来因特网出现了,世界人民都喜欢上了聊天,中国人民满怀热情的问候过去,沙俄群众看到的都是乱码,黑乎乎的一串,夹杂着奇怪的符号,纷纷骂傻X。这个时候为了互联互通,Unicode字符集出现了,只要是你能想出来的样子,它都能给你包进去,什么样子鬼划符的符号,都给你收录了,甭管你是那个部落的,只要有文字,都加加加。慢着,Unicode是听说过,但是,UTF-32/ UTF-16/ UTF-8是什么东东嘞?没听说过?好吧,你程序写多了。UTF-32/ UTF-16/ UTF-8其实是Unicode字符集的三种字符编码方案。对,就是这么傲娇!ASCII字符集只有一种编码,叫ASCII字符编码,占用7bit。ANSI字符集也只有一个编码,叫ANSI字符编码,占用8bit。天朝的GB2312字符集还是只有一个编码,叫GB2312字符编码,占用2个字节,16bit。到了Unicode了,就不这么玩了,它有UTF-32, UTF-16,UTF-8 三种字符编码。具体如何编的,大家搜吧,我写累了。反正windows用UTF-16存储Unicode,网络上常用UTF-8,UTF-32我只是听说过,没碰到过。今天累了,改天再写,今天写的啰嗦了。有些内容就不写了,没有什么可以深究的地方,如布尔代数,ASCII字符串等。