⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 o语言汇编基础教程.htm

📁 本教程主要讲述计算机指令运行机制及汇编语言的相关知识
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<TD WIDTH="34%" VALIGN="MIDDLE">
寄存器说明</TD>
</TR>
<TR><TD WIDTH="12%" VALIGN="TOP" ROWSPAN=8>
多媒体寄存器</TD>
<TD WIDTH="22%" VALIGN="TOP">
媒体零</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM0</TD>
<TD WIDTH="16%" VALIGN="TOP">
000</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒体寄存器零</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
媒体一</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM1</TD>
<TD WIDTH="16%" VALIGN="TOP">
001</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒体寄存器一</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
媒体二</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM2</TD>
<TD WIDTH="16%" VALIGN="TOP">
010</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒体寄存器二</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
媒体三</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM3</TD>
<TD WIDTH="16%" VALIGN="TOP">
011</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒体寄存器三</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
媒体四</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM4</TD>
<TD WIDTH="16%" VALIGN="TOP">
100</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒体寄存器四</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
媒体五</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM5</TD>
<TD WIDTH="16%" VALIGN="TOP">
101</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒体寄存器五</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
媒体六</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM6</TD>
<TD WIDTH="16%" VALIGN="TOP">
110</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒体寄存器六</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
媒体七</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
MM7</TD>
<TD WIDTH="16%" VALIGN="TOP">
111</TD>
<TD WIDTH="34%" VALIGN="TOP">
媒体寄存器七</TD>
</TR>
<TR><TD WIDTH="12%" VALIGN="MIDDLE" ROWSPAN=8>
单指令流、多数据流寄存器</TD>
<TD WIDTH="22%" VALIGN="TOP">
单流零</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
XMM0</TD>
<TD WIDTH="16%" VALIGN="TOP">
000</TD>
<TD WIDTH="34%" VALIGN="TOP">
单指令流、多数据流寄存器零</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
单流一</TD>
<TD WIDTH="16%" VALIGN="TOP">
XMM1</TD>
<TD WIDTH="16%" VALIGN="TOP">
001</TD>
<TD WIDTH="34%" VALIGN="TOP">
单指令流、多数据流寄存器一</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
单流二</TD>
<TD WIDTH="16%" VALIGN="TOP">
XMM2</TD>
<TD WIDTH="16%" VALIGN="TOP">
010</TD>
<TD WIDTH="34%" VALIGN="TOP">
单指令流、多数据流寄存器二</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
单流三</TD>
<TD WIDTH="16%" VALIGN="TOP">
XMM3</TD>
<TD WIDTH="16%" VALIGN="TOP">
011</TD>
<TD WIDTH="34%" VALIGN="TOP">
单指令流、多数据流寄存器三</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
单流四</TD>
<TD WIDTH="16%" VALIGN="TOP">
XMM4</TD>
<TD WIDTH="16%" VALIGN="TOP">
100</TD>
<TD WIDTH="34%" VALIGN="TOP">
单指令流、多数据流寄存器四</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
单流五</TD>
<TD WIDTH="16%" VALIGN="TOP">
XMM5</TD>
<TD WIDTH="16%" VALIGN="TOP">
101</TD>
<TD WIDTH="34%" VALIGN="TOP">
单指令流、多数据流寄存器五</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
单流六</TD>
<TD WIDTH="16%" VALIGN="TOP">
XMM6</TD>
<TD WIDTH="16%" VALIGN="TOP">
110</TD>
<TD WIDTH="34%" VALIGN="TOP">
单指令流、多数据流寄存器六</TD>
</TR>
<TR><TD WIDTH="22%" VALIGN="TOP">
单流七</TD>
<TD WIDTH="16%" VALIGN="TOP">
XMM7</TD>
<TD WIDTH="16%" VALIGN="TOP">
111</TD>
<TD WIDTH="34%" VALIGN="TOP">
单指令流、多数据流寄存器七</TD>
</TR>
</TABLE><br><br>

(表1.1 寄存器中文-英文命名对照表)<br>
注:英文名称有星号“*”的表示作为保留域,实际并没有使用,二进制码有星号“*”表示无需二进制数表示<br><br>
汇编指令解析<br><br>
为什么研究汇编指令<br>
计算机思考的方式就是执行程序,这些程序都是由指令所构成的,计算机的指令是一串二进制码数。精简指令系统的指令长度等长,指令格式种类少,寻址方式种类少,大多数是简单指令且都能在一个时钟周期内完成,易于设计超标量与流水线,寄存器数量多,大量操作在寄存器之间进行,因此执行指令速度快。而复杂指令系统长度不等,指令格式繁多,寻址方式变化多样,使指令的编写与执行都错综复杂,这增加了处理器的硬件设计与处理指令的时间,复杂指令系统的形成是有它的历史背景的,现在除了个人电脑市场还在用复杂指令集外,服务器及更大的系统早已不用了,它仍然存在的理由是为了兼容基于这种指令集平台上的大量软件,这也是我们研究复杂指令的理由,毕竟我们最终关心的还是丰富多彩的应用软件。<br>
我们所说的编写软件就是要写处理器能够识别的二进制码指令有序集合,让处理器按照我们设计的方式来处理数据,最原始的计算机语言就是直接写二进制码,而现在已经有许多种语言可以方便地完成这样的工作,人们熟知的语言有如C、PASCAL、BASIC、C++、VC、VB、DEPHI等等,它们最终无一例外地都要转化成二进制指令,处理器才能够识别并执行。现如今还有微软推出的.NET框架下的一整套语言与人们熟知的JAVA语言是另一类型的计算机语言,它与上述语言不同的是,它的代码并不被直接编译成二进制可执行代码,这是由它们的工作机制决定的,这一类型的语言被编译后产生中间代码并不直接与处理器打交道,而是通过虚拟机执行完成最终任务,因此它的代码只要被编译成虚拟机能够识别就可以了,由于虚拟机处理代码要额外消耗时间,因此执行效能相对较低,而且它们工作在虚拟机上,不能直接完成对底层的硬件操作。上述的语言人们把它们叫做高级语言,不需要用复杂的汇编指令来编写程序,它们的语法容易为人们所理解和接受,学起来比较容易,但对中国人来说,这些语言都是以英文为媒介的,即便语法比较人性化,而英文本身对我们就是一道障碍,这也是为什么我们国家的软件产业发展缓慢的重要原因,O语言的目标是彻底改变中国没有计算机语言的历史,最终使用符合我们习惯的语言。上述非工作在虚拟机上的高级语言被相应的编译程序处理后,都被转化成了汇编语言,最终由汇编程序来翻译成二进制可执行代码,直接与处理器打交道的汇编语言是一切计算机高级语言的基础,因此,要改变我们没有计算机语言的历史,就必须从最底层的汇编语言做起。<br>
汇编指令的本源二进制机器指令<br>
由于汇编语言只是为了人们便于编写二进制机器指令而写的语言,它最终还是由机器指令来决定的,因此在学习汇编之前,首先了解二进制机器指令是有必要的。我们首先来看一串二进制数:1011 1000 0001 0010 0011 0100 这串二进制数用十六进制表示是0xB81234,大部分人看到这串数一定会一头雾水,然而计算机处理器却可以识别出这一串数字的意义,我们把上面的二进制数分成几个部分来讲解。从左到右取开始四位是:1011它是这一指令的主要部分,它实际上相当于指令的名称,是用来区分不同指令的标识,它也决定了指令的功能。对应的英文汇编指令名称是“MOV”,而O语言中是“传送”指令。接下来的四位是:1000,它又分为1和000两部分,第一位1决定后面三位的意义,后面的三位表示寄存器,如果第一位是1则指示后面的寄存器是字寄存器,即16位寄存器,如果是0则指示是字节寄存器,即8位寄存器,所以四位组合起来指定的就是一个16位寄存器,通过查表可得到000表示16位寄存器的是AX寄存器,也就是O语言里的累加一六寄存器。剩下的16位二进制码做为最后一部分:0001 0010 0011 0100,它就是数字0x1234。显然这条指令就是一条数据传送指令,它的意义是把0x1234这个数传送到累加一六这个寄存器中,不管原先累加一六寄存器中是什么数,执行这条指令后,它都会变成是0x1234。我们把这一条指令的前四位的指令标识叫做<U>操作码</U>,而后面的寄存器包括数0x1234都叫做<U>操作数</U>,纵观指令集,所有的指令都是由操作码及操作数组成的,只不过有些指令不需要操作数,而只有操作码。有一点需要说明的是,上面的例子在16位指令模式下是成立的,如果是32位指令模式则稍有不同,指示寄存器的位如果是1则指示的是双字寄存器,即32位寄存器,如果是0则依然指示为字节寄存器,当指示为32位寄存器时后面的操作数也必须是32位即0x00001234,这时的指令二进制码就应该为:1011 1000 0000 0000 0000 0000 0001 0010 0011 0100,它的意义是:把0x00001234这个数传送到累加三二寄存器中。16位模式指令适用于286之前的处理器,32位指令模式适用于386之后的处理器,386之后的处理器既可以工作在16位模式下,也可以工作在32位模式下,通常我们把工作在16位模式下称为实模式,而工作在32位模式称为保护模式,它们的寻址方式有很大不同,如果要想在386以上的处理器中处于32位模式下使用16位的指令则必须在指令前加上0x66前缀。关于<U>寻址方式</U>和<U>指令前缀</U>后面章节会详细阐述。<br>
一、操作码 由前面例子我们大致知道了什么是操作码,所谓操作码就是唯一代表着指令的意义的一段二进制码,操作码通常占一个字节或者是两个字节,如果你看到不同指令的前一个或前两个字节完全相同也不用奇怪,它们是操作码,但它们的意义还决定于后面的操作数字节,特别是第一个寄存器位。还有一种特殊的操作码,它与寄存器操作数合起来共用一个字节或者两个字节,且寄存器的二进制代码都为这操作码字节的最后三位,上节中所举的数据传送的例子就属于这种特例的操作码。我们使用汇编程序来编写程序时指令助记符通常表示的就是操作码,但实事上二进制操作码与汇编指令助记符并非一一对应,一条汇编指令的名称如:传送指令,它可以对应多个操作码,只有当指令名称与指令操作数合在一起,才与操作码和操作数有一一对应的关系。也就是说同一个指令名称,指令操作数类型不同,对应的二进制操作码也可能不同。<br>
在操作码字节中并非如上介绍的那么简单,只标识着指令的功能,它与操作数是息息相关的,在操作码字节中通常有方向位、符号位、操作数大小修饰位,这些位都用一个二进制位来表示,方向位与符号位都在操作码的倒数第二位,在同一时刻只表示一种意义,要么表示操作数的方向,要么表示操作数的符号扩展,而操作数修饰位通常在操作码的最后一位。同样我们也举例说明,请看下面的这串二进制数:1000 1001 1001 1000 0000 0000 0000 0000 0001 0010 0011 0100,它用十六进制表示是0x899800001234,假设当前处理器运行在32位模式下,如果你能够直接看懂这一长串的二进制数,那么祝贺你已经拥有世界上运行最快的人脑,无需再阅读后面的教程了。如果不行请看我下面的分析。开始的字节1000 1001表示的是操作码,这个字节的倒数第二位就用来表示操作数的流向,为0时表示从左流向右=&gt;,为1时表示从右流向左&lt;=,字节的最后一位指示操作数的大小,为0时表示字节操作数,即8位操作数,为1时表示双字操作数,即32位操作数。它的操作码同样对应的也是传送指令,英文汇编为MOV指令,这就映证了一个汇编助记符对应多个操作码的情况。接下来的字节是1001 1000,它又可以分为三个部分,开始的两位10代表的是模数(MOD),后三位011是寄存器操作数,再后三位000在这里代表的内存操作数,本指令剩下的32位数即0x00001234则代表的是内存操作数的位移量。这些意义是如何区分的呢?主要是由开始的两位模数来决定,如果模数不是10而是11,那么它指示的是两个操作数都是32位寄存器,如果模数为00则它指示的是模数之后的三位代表32位寄存器,而接下来的三位代表的是内存寄存器,且没有位移量,如果模数为01则它指示的意义与00相似,但有8位的位移量。通过查寄存器对照表可知,011代表32位寄存器是EBX也就是O汇编语言里描述的基址三二寄存器,而000代表的32位寄存器是EAX即累加三二寄存器。因此,这条冗长的指令实际代表的意义就是,将基址三二寄存器内的数据传送到指定的内存位置,这个内存地址由累加三二寄存器的值再加上0x00001234位移量得到。下表可以直观地得到这条指令的意义:<br>
<TABLE BORDER CELLSPACING=1 CELLPADDING=7 WIDTH=539>
<TR><TD WIDTH="39%" VALIGN="TOP" COLSPAN=3 HEIGHT=28>
操作码(8位)</TD>
<TD WIDTH="61%" VALIGN="TOP" COLSPAN=4 HEIGHT=28>
操作数(40位)</TD>
</TR>
<TR><TD WIDTH="14%" VALIGN="MIDDLE">
指令<br>
标识码</TD>
<TD WIDTH="11%" VALIGN="MIDDLE">
操作数<br>
流向</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
操作数<br>
大小</TD>
<TD WIDTH="20%" VALIGN="MIDDLE">
模数</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
寄存器</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
内存<br>
寄存器</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
位移量</TD>
</TR>
<TR><TD WIDTH="14%" VALIGN="MIDDLE">
1000 10</TD>
<TD WIDTH="11%" VALIGN="MIDDLE">
0</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
1</TD>
<TD WIDTH="20%" VALIGN="MIDDLE">
10</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
011</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
000</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
0x00001234</TD>
</TR>
<TR><TD WIDTH="14%" VALIGN="MIDDLE">
汇编助记符为:传送</TD>
<TD WIDTH="11%" VALIGN="MIDDLE">
从左<br>
到右</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
32位<br>
操作数</TD>
<TD WIDTH="20%" VALIGN="MIDDLE">
内存操作数有32位位移量</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
基址三二</TD>
<TD WIDTH="13%" VALIGN="MIDDLE">
累加三二</TD>
<TD WIDTH="16%" VALIGN="MIDDLE">
32位<br>
位移量</TD>
</TR>
</TABLE><br>

(表2.1 二进制指令分析)<br>
如果指令工作在16位模式下则稍有不同,位移量为16位即0x1234且操作数为16位,指令的意义表示为:将基址一六内的数据传送到累加一六内的值加上0x1234指定的内存所在的位置。在使用汇编语言编写程序时可以指定编写的是16位模式指令还是32位模式指令,不同的位模式指令实用于不同的操作系统,如今常用到的都是32位的模式指令,只有在特殊的情况下才有必要编译成16位模式指令。<br>
二、操作数 前面讲述操作码时有讲到操作数,但仅做了简单的介绍。计算机指令按其功能可简单分为:常用指令、浮点指令、多媒体指令和单指令流多数据流(SSE)指令。汇编指令因其繁多且对应的二进制代码有多种变化而变得异常复杂,而且操作数又存在多种情形,使大多数人谈到汇编就望而生畏。实际上它们是有规律可循的,就指令与操作数的关系而言,指令无非是无操作数、单操作数、双操作数和三操作数这几种情况。最为简单的指令就是无操作数指令,这种指令只含有操作码,比如暂停指令,英文汇编指令助记符是HLT,它只有操作码0xF4,比这稍微复杂点的情况就是有一个操作数的情形。操作数既可以是寄存器、内存地址也可以是立即数。前面的第一个例子是操作数为寄存器和立即数的情况,而第二个例子是寄存器和内存操作数的情况。处理器是如何来区分这些不同的操作数的呢?这主要是由不同的操作码来决定,因此,我们说同一个汇编指令名称助记符会对应多个操作码原因就在于此,它们的功能是相似的,使用相同的助记符便于记忆和使用,汇编编译程序在翻译汇编指令时,可以根据相同的指令名称与不同的操作数组合,翻译成与之匹配的操作码和操作数,使指令二进制码与汇编语句一一对应,而反汇编程序恰恰相反,根据正确的指令二进制码唯一地翻译成一条对应的汇编语句。通常情况下,当指令操作数含有立即数时与不含立即数时的操作码不同;指令操作数都为寄存器时,通用寄存器、段寄存器、控制寄存器、调试寄存器和任务寄存器的指令操作码不同;多媒体指令、浮点指令和单指令流多数据流指令(SSE)都有各自的指令系统。当操作数为寄存器或内存时变化最为复杂,主要是因为在这种情况下,由一个叫做模数(MOD)的两位二进制数来决定寄存器、内存以及内存位移量的多种情况。<br>
当操作数是寄存器或是内存地址时,主要是用一个字节来区分,这个字节的前两位就是模数(MOD),后三位是寄存器的二进制代码,最后三位的值由开始两位的模数来决定具体的类型。如果模数为11那么最后三位就用来表示寄存器。内存地址是用寄存器的值来表示的,有的内存地址带有位移量,于是,当模数为00时字节的最后三位就表示寄存器内的数据是内存地址,并且没有位移量,当模数为01时,字节最后三位表示寄存器内的数据是内存地址,并且在这一字节之后有8位数的位移量,当模数为10时字节后面有16位或32位,是16位还是32位由汇编模式来决定。16位模式常用于实模式,可使用16位寄存器,如AX、BX等,32位常用于保护模式,可使用32位寄存器,如EAX、EBX等。然而,只用一个字节的最后三位来表示内存地址,不可能用来表示众多的寄存器组合,于是模数为00时(没有位移量),就有在16位汇编模式,当nnn=000(nnn表示是用来表示内存的三位二进制数)时,内存地址为DS:[BX+SI],在32位汇编模式时nnn=000,内存地址为DS:[EAX],其它的值请参照后面的对应表。于是,我们知道了,计算机指令是通过模数、寄存器和寄/内三个域来决定操作数的,其实除此之外,还有一种特殊的情况,就是操作数并没有都用到这三个域,而是把第二个域的值设为一个预设值,也就是说,第二个用来表示寄存器的域设成了固定值,而不是可变的寄存器,那么这就出现了只有一个寄存器或是内存地址操作数的变型,其实它是由前一种类型变化得来的。<br>
<TABLE BORDER CELLSPACING=1 CELLPADDING=7 WIDTH=540>
<TR><TD WIDTH="15%" VALIGN="TOP" HEIGHT=50>
模数<br>
MOD</TD>
<TD WIDTH="20%" VALIGN="TOP" HEIGHT=50>
寄存器<br>
jjj</TD>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -