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

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

📁 本教程主要讲述计算机指令运行机制及汇编语言的相关知识
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<TD WIDTH="36%" VALIGN="TOP" HEIGHT=50>
寄存器/内存寄存器<br>
jjj/nnn</TD>
<TD WIDTH="30%" VALIGN="TOP" HEIGHT=50>
内存位移量<br>
disp</TD>
</TR>
<TR><TD WIDTH="15%" VALIGN="TOP">
2位</TD>
<TD WIDTH="20%" VALIGN="TOP">
3位</TD>
<TD WIDTH="36%" VALIGN="TOP">
3位</TD>
<TD WIDTH="30%" VALIGN="TOP">
8位/16|32位</TD>
</TR>
<TR><TD WIDTH="15%" VALIGN="TOP">
11</TD>
<TD WIDTH="20%" VALIGN="TOP">
寄存器</TD>
<TD WIDTH="36%" VALIGN="TOP">
寄存器</TD>
<TD WIDTH="30%" VALIGN="TOP">
无</TD>
</TR>
<TR><TD WIDTH="15%" VALIGN="TOP">
00</TD>
<TD WIDTH="20%" VALIGN="TOP">
寄存器</TD>
<TD WIDTH="36%" VALIGN="TOP">
内存寄存器</TD>
<TD WIDTH="30%" VALIGN="TOP">
无</TD>
</TR>
<TR><TD WIDTH="15%" VALIGN="TOP">
01</TD>
<TD WIDTH="20%" VALIGN="TOP">
寄存器</TD>
<TD WIDTH="36%" VALIGN="TOP">
内存寄存器</TD>
<TD WIDTH="30%" VALIGN="TOP">
8位位移量</TD>
</TR>
<TR><TD WIDTH="15%" VALIGN="TOP">
10</TD>
<TD WIDTH="20%" VALIGN="TOP">
寄存器</TD>
<TD WIDTH="36%" VALIGN="TOP">
内存寄存器</TD>
<TD WIDTH="30%" VALIGN="TOP">
16位模式:16位位移量<br>
32位模式:32位位移量</TD>
</TR>
<TR><TD WIDTH="15%" VALIGN="TOP">
备注</TD>
<TD WIDTH="85%" VALIGN="TOP" COLSPAN=3>
如果寄存器域被预设固定值则只取寄存器/内存寄存器域的值<br>
此时变化成为要么只有一个寄存器要么只有内存内存寻址的情况</TD>
</TR>
</TABLE><br>

(表2.2 寄存器字节分析)<br>
有三个操作数的指令又是如何的呢?实事上搞明白了上一种变化,三个操作数就很容易了,它只不过是在两个寄存器或内存地址操作数之后再加一个立即数,这种情况同样有一种特例,那就是有两个相同寄存器操作数,和一个立即数,那么在写汇编代码的时候就把这种情况认为是一个寄存器和一个立即数,而实际翻译成二进制代码时要翻译成两个相同的寄存器和一个立即数,它同样是属于三个操作数的情况,符号乘法(IMUL)指令就属于这一种特例。<br>
在具体的指令中还有一些例外,比如,一个看上去只有一个操作码,不含操作数的指令,而实际上它有隐含的寄存器作为它的操作数如计数寄存器,或者它的寄存器操作数被指令指定了,而不是可变的,如只使用累加寄存器。比如下例指令:1010 0001 0000 0000 0000 0000 0001 0010 0011 0100,用十六进制表示是0xA100001234,它的操作码是开始字节1010 0001,操作码的倒数第二位0指示操作数流向,最后一位1指示操作数大小,这串二进制码指令不含有寄存器,但实际上由它的操作码决定了它隐含有累加寄存器。这串指令的实际意义是将位于内存地址0x00001234处的32位数据传送到累加三二寄存器中。请注意这里的方向位,它与前面讲述的有所不同,通常情况,当方向位为0时,操作数流向中从左到右,即寄存器=&gt;内存地址,而方向位为1是从右到左,即寄存器&lt;=内存地址,但这是由于没有显示是给出寄存器,所以它的方向与之相反,也就是为0时它是寄存器&lt;=内存地址,为1时它是寄存器=&gt;内存地址。<br>
三、内存寻址 指令共存在三种操作数:寄存器操作数、内存操作数和立即数操作数。由前面分析我们知道,指令是通过一个叫做模数(MOD)的两位二进制位来指定寄存器操作数字节的最后三位代表的意义。因此,一条指令最多只可能有两个寄存器操作数(MOD=11);最多只可能有一个内存操作数(MOD=00/01/10),永远也不可能出现两个或者两个以上内存操作数的情况,而且也只可能有一个立即数操作数。有几个很特殊的指令拥有隐含的寄存器作为操作数,隐含寄存器通常是累加寄存器或者计数寄存器,隐含寄存器不由模数来决定而是由操作码来决定,因此,在极少的情况下有可能出现三个寄存器操作数的情况,如:监视指令,指令英文名称是MONITOR,它就拥有累加三二、计数三二和数据三二三个寄存器操作数。<br>
内存操作数相当复杂,这不仅是因为由模数决定是否拥有内存位移量的情况,更主要的是因为由三位二进制数来表示内存操作数时变化繁多。三位二进制数排序可以有八种变化,它们分别是000、001、010、011、100、101、110、111,这就代表八种不同的寻址方式。下面是在16位模式下这八个数分别代表的意义:<br>
<TABLE BORDER CELLSPACING=1 CELLPADDING=7 WIDTH=539>
<TR><TD WIDTH="21%" VALIGN="TOP">
内存二进制码</TD>
<TD WIDTH="47%" VALIGN="TOP">
寻址方式中文表示</TD>
<TD WIDTH="32%" VALIGN="TOP">
寻址方式英文表示</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
000</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
数据段:[基址一六  +源变址一六 ]</TD>
<TD WIDTH="32%" VALIGN="MIDDLE">
DS:[BX+SI]</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
001</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
数据段:[基址一六  +目的变址一六]</TD>
<TD WIDTH="32%" VALIGN="TOP">
DS:[BX+DI]</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
010</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
堆栈段:[基址指针一六+源变址一六 ]</TD>
<TD WIDTH="32%" VALIGN="TOP">
SS:[BP+SI]</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
011</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
堆栈段:[基址指针一六+目的变址一六]</TD>
<TD WIDTH="32%" VALIGN="TOP">
SS:[BP+DI]</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
100</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
数据段:[源变址一六  ]</TD>
<TD WIDTH="32%" VALIGN="TOP">
DS:[SI]</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
101</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
数据段:[目的变址一六]</TD>
<TD WIDTH="32%" VALIGN="TOP">
DS:[DI]</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
110</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
堆栈段:[基址指针一六]*</TD>
<TD WIDTH="32%" VALIGN="TOP">
SS:[BP]*</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
111</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
数据段:[基址一六   ]</TD>
<TD WIDTH="32%" VALIGN="TOP">
DS:[BX]</TD>
</TR>
</TABLE><br>

(表2.3 16位内存寻址方式)<br><br>
下面解释一下这几种情况:前四种情况即内存二进制码为000、001、010和011时,寻址方式叫做<B>基址加变址寻址</B>,如果内存寄存器中含有基址指针寄存器则访问的是堆栈段内的数据(下面的情况也如此),否则访问的是数据段内的数据,它的实际内存地址是相对于数据段寄存器或者是堆栈段寄存器表示的数据段首地址加上后面两个寄存器值。后四种情况即内存二进制码为100、101、110和111时,寻址方式称为<B>寄存器间接寻址</B>,为什么不叫做寄存器寻址,而是间接寻址呢?在相关的书籍中,把寄存器操作数和立即数操作数都纳入寻址概念的范围,因此把寄存器操作数叫做<B>寄存器寻址</B>,把立即数操作数叫做<B>立即寻址</B>,而内存寄存器寻址只好称为间接寻址了。我个人认为,把内存操作数与寄存器和立即数操作数分开,更容易理解,寄存器与立即数很简单,而内存操作数则变化复杂,掌握了指令操作数的模型很容易区分各种操作数的情况。后四种情况与前四种不同的就是只含有一个内存寄存器,实际内存地址少加了源变址或者目的变址寄存器的值。试想一下,上面所有列出的情况,内存寻址时都至少需要加上一个寄存器的值,如果我们直接给出内存相对于段首地址的位移量,而不需要加某个寄存器的值,难道就不能访问内存了吗?这当然是不符合现实情况的,细心的你一定发现了在表2.3的倒数第二行,即内存二进制码为110时寻址方式用了星号(*)注示,这种特殊的寻址方式就是用这一行的格式来表示的。当内存操作数不含寄存器只含有位移量时,把这种寻址方式称为<B>直接寻址</B>,此时,内存寄存器的三位二进制数被设置成基址指针寄存器,则模数被设置在00,即MOD=00、nnn=110。但是按照前面的分析,当模数MOD=00时表示的是内存寄存器没有位移量的情况,这就产生了冲突,于是就取消了内存寄存器为基址指针即nnn=110时没有位移量的表示情况,换句话说,如果你的意图是要表示当内存寄存器为基址指针且没有位移量的情况时,就必须表示成有8位位移量或者有16位位移量,且设置位移量为0的情况,即必须按下面的方法表示:MOD=01、disp=0x00或者MOD=10、disp=0x0000,来达到同样的目的。仅仅是内存寄存器就有这8种加1种特殊的寻址,共9种寻址的方式,如果再加上8位和16位的位移量,可想而知内存操作数是多么的复杂。有一点不要忘了,内存寄存器只表示一个内存地址,它真正的操作数是保存在内存地址指向的内存空间中的数据。如果有两个操作数,其中一个是寄存器操作数,而另一个是内存操作数,按照操作数大小匹配的原则,内存空间的大小与寄存器的大小保持一致,如果是8位寄存器操作数,那么内存操作数也是8位,是16位寄存器操作数,则内存操作数也是16位,但是如果只有一个内存操作数,或者有两个操作数,其中一个是内存操作数,而另一个是立即数操作数,那如何来确定内存操作数的大小呢?这就由必须由操作码来区分内存操作数的大小,这就是为什么有些操作码的最后一位用来指示是8位还是16位(在32位模式下是32位)内存操作数的大小。下面列出了16位寻址可能的组合:<br><br>
16位寻址方式时模数mm与内存寻址nnn可能的组合<br>
mm  nnn    寻址方式中文表示                             寻址方式英文表示<br>
00  000    数据段:[基址一六+源变址一六]                    DS:[BX+SI]<br>
00  001    数据段:[基址一六+目标变址一六]                  DS:[BX+DI]<br>
00  010    堆栈段:[基址指针一六+源变址一六]                SS:[BP+SI]<br>
00  011    堆栈段:[基址指针一六+目标变址一六]              SS:[BP+DI]<br>
00  100    数据段:[源变址一六]                             DS:[SI]<br>
00  101    数据段:[目标变址一六]                           DS:[DI]<br>
00  110    堆栈段:[基址指针一六]                           SS:[BP]<br>
00  111    数据段:[基址一六]                               DS:[BX]<br>
01  000    数据段:[基址一六+源变址一六+8位符号位移]        DS:[BX+SI+sign_disp8]<br>
01  001    数据段:[基址一六+目标变址一六+8位符号位移]      DS:[BX+DI+sign_disp8]<br>
01  010    堆栈段:[基址指针一六+源变址一六+8位符号位移]    SS:[BP+SI+sign_disp8]<br>
01  011    堆栈段:[基址指针一六+目标变址一六+8位符号位移]  SS:[BP+DI+sign_disp8]<br>
01  100    数据段:[源变址一六+8位符号位移]                 DS:[SI+sign_disp8]<br>
01  101    数据段:[目标变址一六+8位符号位移]               DS:[DI+sign_disp8]<br>
01  110    堆栈段:[基址指针一六+8位符号位移]               SS:[BP+sign_disp8]<br>
01  111    数据段:[基址一六+8位符号位移]                   DS:[BX+sign_disp8]<br>
10  000    数据段:[基址一六+源变址一六+16位符号位移]       DS:[BX+SI+disp16]<br>
10  001    数据段:[基址一六+目标变址一六+16位符号位移]     DS:[BX+DI+disp16]<br>
10  010    堆栈段:[基址指针一六+源变址一六+16位符号位移]   SS:[BP+SI+disp16]<br>
10  011    堆栈段:[基址指针一六+目标变址一六+16位符号位移] SS:[BP+DI+disp16]<br>
10  100    数据段:[源变址一六+16位符号位移]                DS:[SI+disp16]<br>
10  101    数据段:[目标变址一六+16位符号位移]              DS:[DI+disp16]<br>
10  110    堆栈段:[基址指针一六+16位符号位移]              SS:[BP+disp16]<br>
10  111    数据段:[基址一六+16位符号位移]                  DS:[BX+disp16]<br>
(表2.4 16位内存寻址方式组合)<br>

386或更高的处理器使用的是32位的寻址方式,它要比上述16位寻址方式还要复杂得多,原因是在32位寻址方式下引用了比例寻址。下表2.5列出了32位的寻址方式:<br>
<TABLE BORDER CELLSPACING=1 CELLPADDING=7 WIDTH=539>
<TR><TD WIDTH="21%" VALIGN="TOP">
内存二进制码</TD>
<TD WIDTH="47%" VALIGN="TOP">
寻址方式中文表示</TD>
<TD WIDTH="32%" VALIGN="TOP">
寻址方式英文表示</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
000</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
数据段:[累加三二]</TD>
<TD WIDTH="32%" VALIGN="MIDDLE">
DS:[EAX]</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
001</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
数据段:[计数三二]</TD>
<TD WIDTH="32%" VALIGN="MIDDLE">
DS:[ECX]</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
010</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
数据段:[数据三二]</TD>
<TD WIDTH="32%" VALIGN="MIDDLE">
DS:[EDX]</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
011</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
数据段:[基址三二]</TD>
<TD WIDTH="32%" VALIGN="MIDDLE">
DS:[EBX]</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
100</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
使用比例变址字节</TD>
<TD WIDTH="32%" VALIGN="MIDDLE">
使用比例变址字节</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
101</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
堆栈段:[基址指针三二]*</TD>
<TD WIDTH="32%" VALIGN="MIDDLE">
SS:[EBP]*</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
110</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
数据段:[源变址三二]</TD>
<TD WIDTH="32%" VALIGN="MIDDLE">
DS:[ESI]</TD>
</TR>
<TR><TD WIDTH="21%" VALIGN="MIDDLE">
111</TD>
<TD WIDTH="47%" VALIGN="MIDDLE">
数据段:[目的变址三二]</TD>
<TD WIDTH="32%" VALIGN="MIDDLE">
DS:[EDI]</TD>
</TR>
</TABLE><br>

(表2.5 32位内存寻址方式)<br>
32位寻址与16位寻址原理相同,都是利用指定的寄存器表示位移量来访问相对于段首地址的相应段内存地址数据。如果内存寄存器是基址指针寄存器则访问的是堆栈段数据,否则访问的是数据段的数据。请特别注意使用比例变址字节及打星号(*)的两项,打星号的是表示特殊寻址,与16位寻址相同,但16位的内存二进制码是nnn=110而32位是nnn=101,特殊寻址的特殊之处就在于取消了MOD=00没有位移量的内存表示方式而取代为<B>直接寻址</B>,将没有位移量的情况表示成8位位移量或者32位位移量为0的情况,即用MOD=01、disp=0x00或者MOD=10、disp=0x00000000来表示。使用比例变址字节是什么意思呢?意思就是当内存二进制码为100即nnn=100时,在当前内存寄存器字节之后再加一个字节来表示内存。我们把这个增加的字节称为比例变址字节,用比例变址字节来表示内存地址的方式我们称之为<B>比例变址寻址</B>。用比例变址字节来表示内存时可以用比例的方式来表示内存地址,具体的表示方法如下。一个字节是8位二进制数,取字节的头两位作为比例因子,两位二进制数可以表示四个数,相应地就有四个比例因子,两位比例因子用ss表示就是ss=00时因子为1、ss=01时因子为2、ss=10时因子为4、ss=11时因子为8。再取字节的后三位来表示变址域寄存器,也就是这三位表示的寄存器的值要乘以前面的因子,最后三位表示基址域寄存器,也就是最后要加这三位表示寄存器的值才能最终得到指定内存的地址。举一个简单的例子,比如比例变址字节的值为10010001,它的ss=10则比例因子为4,010是数据三二寄存器,001是计数三二寄存器,则最后内存地址就是[4*数据三二+计数三二],内存地址用中括号来表示,由于变址域寄存器和基址域寄存器都可以用不同的寄存器来表示,加上不同的因子、8位位移量以及32位位移量的

⌨️ 快捷键说明

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