将 Intel 风格的汇编代码转换为 GCC 内联汇编。

3

如何将这段汇编代码编写为内联汇编?编译器:gcc(i586-elf-gcc)。GAS语法使我感到困惑。请告诉我如何编写可以在gcc中正常工作的内联汇编。

.set_video_mode:
    mov ah,00h
    mov al,13h
    int 10h   

.init_mouse:
    mov ax,0
    int 33h

我在汇编语言中有一个类似的程序,将它们单独编写成汇编例程以便从C程序中调用。我需要从C程序本身调用这些及其他一些中断。

另外,我需要根据调用的中断例程来在某些寄存器中放置一些值。请告诉我如何做到这一点。

我想要做的只是从C中调用中断例程。即使使用int86()也可以,但我没有该函数的源代码。 我想要int86()这样我就可以从C中调用中断。

我正在开发自己的小型操作系统,因此对于调用中断或进行任何直接硬件访问都没有限制。


除非您使用gcc编译ELF格式的内核(然后仍需要引导加载程序),否则这是不可行的,因为任何授予对BIOS中断直接访问权限的操作系统(例如MSDOS)都无法运行您的二进制文件。 - sehe
那么这基本上是一个语法问题吗? - Simon Richter
我看到另一个问题:您希望您的代码在实模式(16位)下执行,而gcc和gas将生成32位代码。 - Gunther Piez
2个回答

2

我没有测试过这个方法,但它应该能帮助你入门:

void set_video_mode (int x, int y) {
  register int ah asm ("ah") = x;
  register int al asm ("al") = y;

  asm volatile ("int $0x10"
                : /* no outputs */
                : /* no inputs */
                : /* clobbers */ "ah", "al");
}

我举了两个“clobber”的例子,但您需要设置正确的“clobber”列表,以便编译器知道您已覆盖了寄存器值(可能没有)。


再次查看代码后... 你可能需要将这两个整型变量声明为volatile,以防止编译器对它们进行优化。不确定。 - ams
@IsmaelLuceno 我不确定x86汇编应该是什么样子的(我主要用于ARM)。答案的重点在于C语言的“asm”语法。我已将其编辑成类似其他答案的样子。这样好些了吗? - ams
首先,asm 只适用于寄存器或静态变量,而 volatile 关键字必须放在 asm 之后。 - Ismael Luceno
其次,GCC对x86寄存器并不十分智能,它会做出错误的操作,因此您必须将%ah%al合并为%eax - Ismael Luceno
你仍然有一个非x86特定的问题,如何防止编译器优化未使用的“ah”和“al”变量? - Ismael Luceno
显示剩余2条评论

0
首先,请记住GCC尚不支持16位代码,因此您最终将以16位模式编译32位代码,这非常低效但可行(例如Linux和SeaBIOS使用它)。可以在每个文件的开头使用以下内容完成:
__asm__ (".code16gcc");

较新的GCC版本(自4.9以来)支持-m16标志,其具有相同的功能。
此外,除非您在内核运行init_mouse之前加载它,否则没有可用的鼠标驱动程序。
您似乎正在使用在几个x86 DOS中常见的API。 asm可以处理寄存器分配,因此代码可以缩减为:
void set_video_mode(int mode)
{
    mode &= 255;
    __asm__ __volatile__ (
        "int $0x10"
        : "+a" (mode) /* %eax = mode & 255 => %ah = 0, %al = mode */
    );
}

void init_mouse(void)
{
    /* XXX it is really important to check the IDT entry isn't 0 */
    int tmp = 0;
    __asm__ __volatile__ (
        "int $0x33"
        : "+a" (tmp) /* %eax = 0*/
        :: "ebx" /* %ebx is also clobbered by DOS mouse drivers */
    );
}
asm语句在GCC手册中有文档记录,但可能不够深入,并且缺乏x86示例。输出(冒号后面的部分)具有独特的晦涩语法,而其余部分则更容易理解(第二个冒号指定输入,第三个冒号指定被破坏的寄存器、标志和/或内存)。
输出必须以=前缀开头,表示您不关心它之前可能具有的值,或者以+开头,表示您也想将其用作输入。在这种情况下,我们使用它而不是输入,因为该值由中断修改,您不允许在被破坏的列表中指定输入寄存器(因为编译器禁止使用它们)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接