gcc内联汇编示例
通过中断获取鼠标信息并打印
首先作者是菜鸟,有不妥当的地方,可以讨论指教。
另外,x64的windows下,内联汇编受到很多限制,所以最后的实战在linux系统下完成。
前面的语法讲解,为了方便,也因为作者只会8086,所以用的8086语法。
计算机领域的技术日新月异,与时俱进太重要啦。进入正题吧。
在某些特殊情况下,通过内联汇编,可以提高程序效率,且更加灵活。下面以通过int 33h获取鼠标信息并打印为例。
一、基础语法
加上asm标识,后面直接括号内添加指令即可。其中可选[ volatile ],为不优化编译直接用这个指令
asm [ volatile ] ( AssemblerInstructions )
二、进阶语法
基础语法过于死板,当遇到输入输出时就无能为力,这个时候就可以用完整语法。
asm [volatile] ( AssemblerTemplate
: OutputOperands
: InputOperands
: Clobbers)
进阶语法包括4个模块,每个模块用:隔开。其中,第一个模块AssemblerTemplate,是必须要有的,相当于汇编指令。后面三个可选,如果没有内容,也需要用:隔开。例子如下。调用33号中断使用03号功能。为了简便,在下面的例子中我使用8086汇编的语法,也不考虑变量的长度!!!
asm ("mov ax,03h\n "
"int 33h "
:
:
: )
需要注意的是指令需要用双引号包围且换行需要转义。在实际编程中,通常使用’'\n" 后紧随一个制表符"(\t)“”,使得输出的汇编文件排版更加清晰。
小难点
第一个模块AssemblerTemplate中是没办法使用c语言的变量的,那么如何完成对C程序变量的访问
很明显,需要下面的输入输出模块去内联啦。
输出模块内联
我们要把ax的值给到一个c变量。会面临一个考验,那就是ax的位数是有限,比如32位,但我们long long int是64位,类似这些问题,肯定需要解决。但这个问题我们后面说,先把输出的格式搞定先。
语法很简单,把变量名用"()"扩起来就是。前面有一个可选项,如果你用默认的变量序号 “%n"来的话,不需要,当然绑定一个编号也是可以的。多个输出数用”,"号隔开即可。
[asmSymbolicName] constraint (cvariablename)
把cx和dx的值分别给到第0个和第1个输出参数。对了constraint是一个字符串,用来对后面的C变量进行限定,细节在后面会单独介绍。目前可以忽略。
int mouthX,mouthY;
asm ("mov ax,03h\n "
"int 33h\n "
"mov %0,cx\n "
"mov %1,dx\n "
: (mouthX), (mouthY)
:
: )
与输出类似,那么就该输入了
输入模块
和输出模块差不多,比如我们要向什么地方输入直接写上即可,其中我们会看到 ”a“这是一个限定符,表示输入到哪里(用到了那个寄存器)。后面会讲到
int mouthX,mouthY;
int funNmb=3;
asm ("int 33h\n "
"mov %0,cx\n "
"mov %1,dx\n "
: (mouthX), (mouthY)
:"a" (funNmb)
: )
最后一个模块
破坏列表Clobbers
Clobbers部分的作用是让程序员告诉GCC汇编指令可能会修改哪些寄存器,这样GCC就会认为在asm语句执行完毕后,这些内容是无效的,进而在编译时会插入相应的保护现场和恢复现场的代码。所以把它称为”破坏列表“。
除了在”破坏列表“中指定被使用的寄存器外,其实还可以在输入输出时加上限定修饰符,这样被修饰的就不用再在”破坏列表“中出现。
限定修饰符constraint
asm汇编语句中操作数(输入输出)可使用的contraints(约束),约束告诉GCC操作数是否应该在寄存器中,以及在哪类寄存器中;是否在内存中,以及在哪类内存中;是否是常量,以及它可能的取值;此外约束还可以匹配输入操作和输出操作数。
限定修饰符的使用比较麻烦,它包括限定符修饰符&约束修饰符。
最简单的限定符是由一组字母组成的字符串,其中每个字符描述了允许的一种类型操作数,常用的限定符如下: