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

📄 preparing-asm_4.htm

📁 编写自己的操作系统
💻 HTM
📖 第 1 页 / 共 5 页
字号:
movl    $5, 
%eax            # 
true, return&nbsp;5&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
jmp&nbsp;&nbsp;&nbsp;&nbsp; 
.L2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .p2align 
2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<BR>.L3:&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; 
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; movl&nbsp;&nbsp;&nbsp; (%eax), 
%eax&nbsp;&nbsp; 
<BR>.L2:&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; 
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; popl&nbsp;&nbsp;&nbsp; 
%ebp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret</FONT></P>
<P><FONT 
face=System>由于内联汇编语句__asm__("":::"memory")向GCC声明,在此内联汇编语句出现的位置内存内容可能了改变,所以GCC在编译时就不能像刚才那样处理。这次,GCC老老实实的将if语句生成了汇编代码。</FONT></P>
<P><FONT 
face=System>可能有人会质疑:为什么要使用__asm__("":::"memory")向GCC声明内存发生了变化?明明“Instruction 
List”是空的,没有任何对内存的操作,这样做只会增加GCC生成汇编代码的数量。</FONT></P>
<P><FONT 
face=System>确实,那条内联汇编语句没有对内存作任何操作,事实上它确实什么都没有做。但影响内存内容的不仅仅是你当前正在运行的程序。比如,如果你现在正在操作的内存是一块内存映射,映射的内容是外围I/O设备寄存器。那么操作这块内存的就不仅仅是当前的程序,I/O设备也会去操作这块内存。既然两者都会去操作同一块内存,那么任何一方在任何时候都不能对这块内存的内容想当然。所以当你使用高级语言C/C++写这类程序的时候,你必须让编译器也能够明白这一点,毕竟高级语言最终要被编译为汇编代码。</FONT></P>
<P><FONT 
face=System>你可能已经注意到了,这次输出的汇编结果中,有两个符号:#APP和#NO_APP,GCC将内联汇编语句中"Instruction 
List"所列出的指令放在#APP和#NO_APP之间,由于__asm__("":::"memory")中“Instruction 
List”为空,所以#APP和#NO_APP中间也没有任何内容。但我们以后的例子会更加清楚的表现这一点。</FONT></P>
<P><FONT 
face=System>关于为什么内联汇编__asm__("":::"memory")是一条声明内存改变的语句,我们后面会详细讨论。</FONT></P>
<P><FONT face=System>刚才我们花了大量的内容来讨论"Instruction 
List"为空是的情况,但在实际的编程中,"Instruction List"绝大多数情况下都不是空的。它可以有1条或任意多条汇编指令。</FONT></P>
<P><FONT face=System>当在"Instruction 
List"中有多条指令的时候,你可以在一对引号中列出全部指令,也可以将一条或几条指令放在一对引号中,所有指令放在多对引号中。如果是前者,你可以将每一条指令放在一行,如果要将多条指令放在一行,则必须用分号(;)或换行符(\n,大多数情况下\n后还要跟一个\t,其中\n是为了换行,\t是为了空出一个tab宽度的空格)将它们分开。比如:</FONT></P>
<P><FONT face=System>&nbsp;__asm__("movl %eax, %ebx&nbsp;&nbsp;&nbsp; 
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp; 
sti&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
popl 
%edi&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
subl %ecx, %ebx"); </FONT></P>
<P><FONT face=System>&nbsp;__asm__("movl %eax, %ebx; 
sti&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; popl %edi; subl %ecx, 
%ebx");</FONT></P>
<P><FONT face=System>&nbsp;__asm__("movl %eax, %ebx; sti\n\t popl 
%edi<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; subl %ecx, %ebx");</FONT></P>
<P><FONT 
face=System>都是合法的写法。如果你将指令放在多对引号中,则除了最后一对引号之外,前面的所有引号里的最后一条指令之后都要有一个分号(;)或(\n)或(\n\t)。比如:</FONT></P>
<P>&nbsp;<FONT face=System>__asm__("movl %eax, %ebx&nbsp;&nbsp;&nbsp; 
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp; 
sti\n"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"popl 
%edi;"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"subl %ecx, %ebx"); </FONT></P>
<P><FONT face=System>&nbsp;__asm__("movl %eax, %ebx; 
sti\n\t"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;"popl %edi; subl %ecx, %ebx");</FONT></P>
<P><FONT face=System>&nbsp;__asm__("movl %eax, %ebx; sti\n\t popl 
%edi\n"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;"subl %ecx, %ebx");</FONT></P>
<P><FONT face=System>__asm__("movl %eax, %ebx; sti\n\t popl %edi;" "subl %ecx, 
%ebx");<BR></FONT><BR>都是合法的。</P>
<P><FONT face=System>上述原则可以归结为:</FONT></P>
<UL>
  <LI><FONT face=System>任意两个指令间要么被分号(;)分开,要么被放在两行;</FONT> 
  <LI><FONT face=System>放在两行的方法既可以从通过\n的方法来实现,也可以真正的放在两行;</FONT> 
  <LI><FONT 
face=System>可以使用1对或多对引号,每1对引号里可以放任一多条指令,所有的指令都要被放到引号中。</FONT></LI></UL>
<P><FONT face=System>在基本内联汇编中,“Instruction 
List”的书写的格式和你直接在汇编文件中写非内联汇编没有什么不同,你可以在其中定义Label,定义对齐(.align<EM> n 
</EM>),定义段(.section <EM>name </EM>)。例如:</FONT></P>
<P><FONT face=System>&nbsp;__asm__(".align 
2\n\t"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp; "movl %eax, %ebx\n\t"&nbsp; 
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp; "test %ebx, %ecx\n\t"&nbsp; 
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "jne 
error\n\t"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
"sti\n\t"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp; "error: popl %edi\n\t" 
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "subl %ecx, %ebx");</FONT></P>
<P><FONT 
face=System>上面例子的格式是Linux内联代码常用的格式,非常整齐。也建议大家都使用这种格式来写内联汇编代码。</FONT><BR></P>
<P><STRONG><FONT face=System>3、__volatile__</FONT></STRONG></P>
<P><FONT face=System>__volatile__是GCC关键字volatile的宏定义:</FONT></P>
<P><FONT face=System>#define __volatile__ volatile</FONT></P>
<P><FONT 
face=System>__volatile__或volatile是可选的,你可以用它也可以不用它。如果你用了它,则是向GCC声明“不要动我所写的Instruction 
List,我需要原封不动的保留每一条指令”,否则当你使用了优化选项(-O)进行编译时,GCC将会根据自己的判断决定是否将这个内联汇编表达式中的指令优化掉。</FONT></P>
<P><FONT 
face=System>那么GCC判断的原则是什么?我不知道(如果有哪位朋友清楚的话,请告诉我)。我试验了一下,发现一条内联汇编语句如果是基本内联汇编的话(即只有“Instruction 
List”,没有Input/Output/Clobber的内联汇编,我们后面将会讨论这一点),无论你是否使用__volatile__来修饰,GCC 
2.96在优化编译时,都会原封不动的保留内联汇编中的“Instruction 
List”。但或许我的试验的例子并不充分,所以这一点并不能够得到保证。</FONT></P>
<P><FONT 
face=System>为了保险起见,如果你不想让GCC的优化影响你的内联汇编代码,你最好在前面都加上__volatile__,而不要依赖于编译器的原则,因为即使你非常了解当前编译器的优化原则,你也无法保证这种原则将来不会发生变化。而__volatile__的含义却是恒定的。</FONT></P>
<P><FONT 
face="Times New Roman TUR"><STRONG>2、带有C/C++表达式的内联汇编</STRONG></P></FONT>
<P><FONT face=System>GCC允许你通过C/C++表达式指定内联汇编中"Instrcuction 
List"中指令的输入和输出,你甚至可以不关心到底使用哪个寄存器被使用,完全靠GCC来安排和指定。这一点可以让程序员避免去考虑有限的寄存器的使用,也可以提高目标代码的效率。</FONT></P>
<P><FONT face=System>我们先来看几个例子:</FONT></P>
<P><FONT face=System>__asm__ (" " : : : "memory" );&nbsp; // 前面提到的</FONT></P>
<P><FONT face=System>__asm__ ("mov %%eax, %%ebx" : "=b"(rv) : "a"(foo) : "eax", 
"ebx");</FONT></P>
<P><FONT face=System>__asm__ __volatile__("lidt %0": "=m" 
(idt_descr));</FONT></P>
<P><FONT face=System>__asm__("subl 
%2,%0\n\t"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;"sbbl 
%3,%1"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;: "=a" (endlow), "=d" 
(endhigh)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
: "g" (startlow), "g" (starthigh), "0" (endlow), "1" (endhigh));</FONT></P>
<P><FONT 
face=System>怎么样,有点印象了吧,是不是也有点晕?没关系,下面讨论完之后你就不会再晕了。(当然,也有可能更晕^_^)。</FONT><FONT 
face=System>讨论开始——</FONT></P>
<P><FONT face=System>带有C/C++表达式的内联汇编格式为:</FONT></P>
<P><FONT face=System>__asm__ __volatile__("Instruction List" : Output&nbsp;: 
Input : Clobber/Modify);</FONT></P>
<P><FONT 
face=System>从中我们可以看出它和基本内联汇编的不同之处在于:它多了3个部分(Input,Output,Clobber/Modify)。</FONT><FONT 
face=System>在括号中的4个部分通过冒号(:)分开。</FONT></P>
<P><FONT face=System>这4个部分都不是必须的,任何一个部分都可以为空,其规则为:</FONT></P><FONT face=System>
<UL>
  <LI>如果Clobber/Modify为空,则其前面的冒号(:)必须省略。比如__asm__("mov %%eax, %%ebx" : "=b"(foo) 
  : "a"(inp) : )就是非法的写法;而__asm__("mov %%eax, %%ebx" : "=b"(foo) : "a"(inp) 
  )则是正确的。 
  <LI>如果Instruction List为空,则Input,Output,Clobber/Modify可以不为空,也可以为空。比如__asm__ ( " 
  " : : : "memory" );和__asm__(" " : : );都是合法的写法。</FONT> 
  <LI><FONT 
  face=System>如果Output,Input,Clobber/Modify都为空,Output,Input之前的冒号(:)既可以省略,也可以不省略。如果都省略,则此汇编退化为一个基本内联汇编,否则,仍然是一个带有C/C++表达式的内联汇编,此时"Instruction 
  List"中的寄存器写法要遵守相关规定,比如寄存器前必须使用两个百分号(%%),而不是像基本汇编格式一样在寄存器前只使用一个百分号(%)。比如__asm__( 
  " mov %%eax, %%ebx" : :&nbsp;);__asm__( " mov %%eax, %%ebx" :&nbsp;)和__asm__( 
  " mov %eax, %ebx" )都是正确的写法,而__asm__( " mov %eax, %ebx" : :&nbsp;);__asm__( " 
  mov %eax, %ebx" :&nbsp;)和__asm__( " mov %%eax, %%ebx" )都是错误的写法。</FONT> 
  <LI><FONT 
  face=System>如果Input,Clobber/Modify为空,但Output不为空,Input前的冒号(:)既可以省略,也可以不省略。比如__asm__( 
  " mov %%eax, %%ebx" : "=b"(foo) :&nbsp;);__asm__( " mov %%eax, %%ebx" : 
  "=b"(foo)&nbsp;)都是正确的。</FONT> 
  <LI><FONT 
  face=System>如果后面的部分不为空,而前面的部分为空,则前面的冒号(:)都必须保留,否则无法说明不为空的部分究竟是第几部分。比如,&nbsp;Clobber/Modify,Output为空,而Input不为空,则Clobber/Modify前的冒号必须省略(前面的规则),而Output前的冒号必须为保留。如果Clobber/Modify不为空,而Input和Output都为空,则Input和Output前的冒号都必须保留。比如__asm__( 
  " mov %%eax, %%ebx" : : "a"(foo)&nbsp;)和__asm__( " mov %%eax, %%ebx" : 

⌨️ 快捷键说明

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