嵌入C代码中的386汇编语言程序段.pdf
《嵌入C代码中的386汇编语言程序段.pdf》由会员分享,可在线阅读,更多相关《嵌入C代码中的386汇编语言程序段.pdf(6页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、欢迎您阅读并下载本文档,本文档来源于互联网,如有侵权请联系删除!我们将竭诚为您提供优质的文档!嵌入 C 代码中的 386 汇编语言程序段 当需要在 C 语言的程序中嵌入一段汇编语言程序段时,可以使用 gcc 提供的“asm”语句功能。例如,下面这么一行代码:#define _SLOW_DOWN_IO _asm_ _volatile_(“outb%al,$0 x80”)这里,暂时忽略在 asm 和 volatile 前后的两个“_”字符,这也是 gcc 对 C 语言的一种扩充,后面我们还要讲到。先来看括号里面加上了引号的汇编指令。这是一条 8 位输出指令,如前所述在操作符上加了后缀“b”以表示这
2、是 8 位的,而 0 x80 因为是常数,即所谓“直接操作数”,所以要加上前缀“$”,而寄存器名 al 也加了前缀“%”。知道了前面所讲AT&T 格式与 Intel 格式的不同,这就是一条很普通的汇编指令,很容易理解。在同一个 asm 语句中也可以插入多行汇编程序。就在同一个文件中,在不同的条件下,_SLOW_DOWN_IO 又有不同的定义:#define _SLOW_DOWN_IO _asm_ _volatile_(“jmp 1f nl:tjmp 1f n1:”)这就不怎么直观了。这里,一共插入了三行汇编语句,“n”就是换行符,而“t”则表示 TAB 符。所以 gcc 将之翻译成下面的格式而
3、交给 gas 去汇编:jmp 1f 1:jmp 1f 1:这里转移指令的目标 1f 表示往前(f 表示 forward)找到第一个标号为 1 的那一行。相应地,如果是 1b 就表示往后找。这也是从早期 Unix 汇编代码中继承下来的,读过 Unix 第6 版的读者大概都还能记得。所以,这一小段汇编代码的用意就在于使 CPU 空做两条跳转指令和消耗掉一些时间。既然是要消耗掉一些时间,而不是要节省一些时间,那么为什么要用汇编语句来实现,而不是在 C 里面来实现呢?原因在于想要对此有比较确切的控制。如果用 C 语言来消耗一些时间的话,你常常不能确切地知道经过编译以后,特别是经过优化的话,最后产生的汇
4、编代码究竟怎样。如果读者觉得这毕竟还是容易理解的话,那么下面这一段(取自 include/asm/atomic.h)就困难多了:static _inline_ void atomic_add(int i,atomic_t*v)_asm_ _volatile_(LOCK addl%1,%0:=m(v-counter):ir(i),m(v-counter);一般而言,往 C 代码中插入汇编语言的代码片段要比“纯粹”的汇编语言代码复杂得多,因为这里有个怎样分配使用寄存器,怎样与 C 语言代码中的变量结合的问题。为了这个目的,必须对所用的汇编语言作更多的扩充,增加对汇编工具的指导作用。其结果是其语法实
5、际上变成了既不同于汇编语言,也不同于 C 语言的某种中间语言。欢迎您阅读并下载本文档,本文档来源于互联网,如有侵权请联系删除!我们将竭诚为您提供优质的文档!下面,先介绍一下插入 C 代码中的汇编成分的一般格式,并加以解释。以后,在我们走过各种情景时碰到具体的代码时还会加以提示。插入 C 代码中的一个汇编语言代码片段可以分成四部分,以“:”号加以分隔,其一般形式为:指令部:输出部:输入部:损坏部 注意不要把这些“:”号跟程序标号中所用的(如前面的 1:)混淆。第一部分就是汇编语句本身,其格式与在汇编语言程序中使用的基本相同,但也有区别,不同之处下面会讲到。这一部分可以称为“指令部”,是必须有的,
6、而其他各部分则可视具体情况而省略,所以在最简单的情况下就与常规的汇编语句基本相同,如前面的两个例子那样。当将汇编语言的代码片段嵌入到 C 代码中时,操作数与 C 代码中的变量如何结合显然是个问题。在本节开头的两个例子中,汇编指令都没有产生与 C 程序中的变量结合的问题,所以比较简单。当汇编指令中的操作数需要与 C 程序中的某些变量结合时,情况就复杂多了。这是因为:程序员在编写嵌入的汇编代码时,按照程序逻辑的要求很清楚应该选用什么指令,但是却无法确切地知道 gcc 在嵌入点的前后会把哪一个寄存器分配用于哪一个变量,以及哪一个或哪几个寄存器是空闲着的。而且,光是被动地知道 gcc 对寄存器的分配情
7、况也还是不够,还得有个手段把使用寄存器的要求告知 gcc,反过来影响它对寄存器的分配。当然,如果 gcc 的功能非常强,那么通过分析嵌入的汇编代码也应该能够归纳出这些要求,再通过优化,最后也能达到目的。但是,即使那样,所引入的不确定性也还是个问题,更何况要做到这样还不容易。针对这个问题,gcc 采取了一种折中的办法:程序员只提供具体的指令而对寄存器的使用则一般只提供一个“样板”和一些约束条件,而把到底如何与变量结合的问题留给 gcc 和 gas 去处理。在指令部中,数字加上前缀%,如%0、%1 等等,表示需要使用寄存器的样板操作数。可以使用的此类操作数的总数取决于具体 CPU 中通用寄存器的数
8、量。这样,指令部中用到了几个不同的这种操作数,就说明有几个变量需要与寄存器结合,由 gcc 和 gas 在编译和汇编时根据后面的约束条件自行变通处理。由于这些样板操作数也使用“%”前缀,在涉及到具体的寄存器时就要在寄存器名前面加上两个“%”符,以免混淆。那么,怎样表达对变量结合的约束条件呢?这就是其余几个部分的作用。紧接在指令部后面的是“输出部”,用以规定对输出变量,即目标操作数如何结合的约束条件。每个这样的条件称为一个“约束”(constraint)。必要时输出部中可以有多个约束,互相以逗号分隔。每个输出约束以“=”号开头,然后是一个字母表示对操作数类型的说明,然后是关于变量结合的约束。例如
9、,在上面的例子中,输出部为:=m(v-counter)这里只有一个约束条件,“=m”表示相应的目标操作数(指令部中的%0)是一个内存单元 v-counter。凡是与输出部中说明的操作数相结合的寄存器或操作数本身,在执行嵌入的汇编代码以后均不保留执行之前的内容,这就给gcc提供了调度使用这些寄存器的依据。输出部后面是“输入部”。输入约束的格式与输出约束条件相似,但不带“=”号。在前面的例子中的输入部有两个约束。第一个为”ir”(i),表示指令中的%1 可以是一个寄存欢迎您阅读并下载本文档,本文档来源于互联网,如有侵权请联系删除!我们将竭诚为您提供优质的文档!器中的“直接操作数”(i 表示 imm
10、ediate),并且该操作数来自于 C 代码中的变量名(这里是调用参数)i。第二个约束为m(v-counter),意义与输出约束中相同。如果一个输入约束要求使用寄存器,则在预处理时 gcc 会为之分配一个寄存器,并自动插入必要的指令将操作数即变量的值装入该寄存器。在输入部中说明的操作数结合的寄存器或操作数本身,在执行嵌入的汇编代码后也不保留执行之前的内容。例如,这里的%1 要求使用寄存器,所以 gcc 会为其分配一个寄存器,并自动插入一条 movl 指令把参数 i 的数值装入该寄存器,可是这个寄存器原来的内容就不复存在了。如果这个寄存器本来就是空闲的,那倒无所谓,可是如果所有的寄存器都在使用,
11、而只好暂时借用一个,那就得保证在使用以后恢复其原有的内容。此时,gcc 会自动在开头处插入一条 pushl 指令,将该寄存器原来的内容保存在堆栈中,而在结束以后插入一条 popl 指令,恢复寄存器的内容。在有些操作中,除用于输入操作数和输出操作数的寄存器以外,还要将若干个寄存器用于计算或操作的中间结果。这样,这些寄存器原有的内容就损坏了,所以要在损坏部对操作的副作用加以说明,让 gcc 采取相应的措施。不过,有时候就直接把这些说明放在输出部了,那也并无不可。操作数的编号从输出部的第一个约束(序号为 0)开始,顺序数下来,每个约束计数一次,在指令部中引用这些操作数或分配用于这些操作数的寄存器时,
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 嵌入 代码 中的 386 汇编语言 程序
限制150内