移植 AT&T inline-asm inb / outb 包装器以与 gcc -masm=intel 一起使用

2024-01-24

我目前正在开发 x86 操作系统。我尝试实现 inb 函数here https://wiki.osdev.org/Inline_Assembly/Examples它给了我Error: Operand type mismatch for `in'.

这也可能与outb or io_wait.

我正在使用英特尔语法(-masm=intel),我不知道该怎么办。

Code:

#include <stdint.h>
#include "ioaccess.h"

uint8_t inb(uint16_t port)
{
    uint8_t ret;
    asm volatile ( "inb %1, %0"
                   : "=a"(ret)
                   : "Nd"(port) );
    return ret;
}

对于 AT&T 语法,这确实有效。


对于 outb ,在反转操作数后我遇到了不同的问题:

void io_wait(void)
{
    asm volatile ( "outb $0x80, %0" : : "a"(0) );
}

Error: operand size mismatch for `out'


If you need to use -masm=intel you will need to insure that your inline assembly is in Intel syntax. Intel syntax is dst, src (AT&T syntax is reverse). This somewhat related answer https://stackoverflow.com/a/54961862/3857942 has some useful information on some differences between NASM's Intel variant1 (not GAS's variant) and AT&T syntax:

有关如何将 NASM Intel 语法转换为 GAS 的 AT&T 语法的信息,请参阅此堆栈溢出答案 https://stackoverflow.com/a/54961862/3857942,并且其中提供了很多有用的信息IBM 文章 https://www.ibm.com/developerworks/library/l-gas-nasm/index.html.

[snip]

一般来说,最大的区别是:

  • 对于 AT&T 语法,源位于左侧,目标位于右侧,而 Intel 则相反。
  • 使用 AT&T 语法,寄存器名称前面带有一个%
  • 对于 AT&T 语法,立即值前面带有一个$
  • 内存操作数可能是最大的区别。 NASM用途[段:disp+base+index*scale]而不是 GAS 的语法段:disp(基数,索引,比例).

您代码中的问题是源操作数和目标操作数必须与您使用的原始 AT&T 语法相反。这段代码:

asm volatile ( "inb %1, %0"
               : "=a"(ret)
               : "Nd"(port) );

需要是:

asm volatile ( "inb %0, %1"
               : "=a"(ret)
               : "Nd"(port) );

关于您的更新:问题是在英特尔语法中,立即值没有前面加上$。这一行有问题:

asm volatile ( "outb $0x80, %0" : : "a"(0) );

它应该是:

asm volatile ( "outb 0x80, %0" : : "a"(0) );

如果你有一个合适的outb函数你可以这样做:

#include <stdint.h>
#include "ioaccess.h"

uint8_t inb(uint16_t port)
{
    uint8_t ret;
    asm volatile ( "inb %0, %1"
                   : "=a"(ret)
                   : "Nd"(port) );
    return ret;
}

void outb(uint16_t port, uint8_t byte)
{
    asm volatile ( "outb %1, %0"
                   :
                   : "a"(byte),
                     "Nd"(port) );
}

void io_wait(void)
{
    outb (0x80, 0);
}

稍微复杂一点的版本,支持 AT&T 和 Inteldialects https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html:

asm 模板中的多种汇编语言在 x86 等目标上, GCC 支持多种汇编语言。 -masm 选项控制 GCC 使用哪种方言作为内联汇编器的默认方言。这 -masm 选项的目标特定文档包含以下列表 支持的方言,以及默认方言(如果选项为) 未指定。理解这些信息可能很重要,因为 使用一种方言编译时可以正常工作的汇编代码 如果使用另一个编译可能会失败。请参阅 x86 选项。

如果您的代码需要支持多种汇编语言(例如 例如,如果您正在编写需要支持的公共标头 各种编译选项),使用这种形式的构造:

{ 方言0 |方言1 |方言2...}

在 x86 和 x86-64 目标上有两种方言。 Dialect0 是 AT&T 语法,Dialect1 是 Intel 语法。这些功能可以这样重新设计:

#include <stdint.h>
#include "ioaccess.h"

uint8_t inb(uint16_t port)
{
    uint8_t ret;
    asm volatile ( "inb {%[port], %[retreg] | %[retreg], %[port]}"
                   : [retreg]"=a"(ret)
                   : [port]"Nd"(port) );
    return ret;
}

void outb(uint16_t port, uint8_t byte)
{
    asm volatile ( "outb {%[byte], %[port] | %[port], %[byte]}"
                   :
                   : [byte]"a"(byte),
                     [port]"Nd"(port) );
}

void io_wait(void)
{
    outb (0x80, 0);
}

我还给了约束符号名称而不是使用%0 and %1使内联汇编更易于阅读和维护。从 GCC 文档来看,每个约束的形式如下:

[ [asmSymbolicName] ] 约束 (cvariablename)

Where:

asm符号名

指定操作数的符号名称。通过将名称括在方括号中来引用汇编器模板中的名称(即“%[Value]”)。名称的范围是包含定义的 asm 语句。任何有效的 C 变量名称都是可接受的,包括周围代码中已定义的名称。同一 asm 语句中的两个操作数不能使用相同的符号名称。

当不使用 asmSymbolicName 时,请使用汇编器模板中操作数列表中操作数的(从零开始)位置。例如,如果有三个输出操作数,则在模板中使用“%0”表示第一个,“%1”表示第二个,“%2”表示第三个。

This version should work2 whether you compile with -masm=intel or -masm=att options


脚注

  • 1Although NASM Intel dialect and GAS's (GNU Assembler) Intel syntax are similar there are some differences. One is that NASM Intel syntax uses [segment:disp+base+index*scale] where a segment can be specified inside the [] and GAS's Intel syntax requires the segment outside with segment:[disp+base+index*scale].
  • 2Although the code will work, you should place all these basic functions in the ioaccess.h file directly and eliminate them from the .c file that contains them. Because you placed these basic functions in a separate .c file (external linkage) the compiler can't optimize them as well as it could. You can modify the functions to be of type static inline and place them in the header directly. The compiler will then have the ability to optimize the code by removing function calling overhead and reduce the need for extra loads and stores. You will want to compile with optimizations higher than -O0. Consider -O2 or -O3.
  • Special Notes Regarding OS Development:
    1. 有许多玩具操作系统(示例、教程和even代码上操作系统开发维基 https://wiki.osdev.org/Main_Page)不适用于优化。很多失败都是因为坏/差的内联汇编 https://gcc.gnu.org/wiki/DontUseInlineAsm或使用未定义的行为。内联汇编应该作为最后的手段。如果您的内核没有在优化的情况下运行,那么它可能不是编译器中的错误(有可能,只是不太可能)。
    2. 请注意 @PeterCordes 答案中有关可能触发 DMA 读取的端口访问的建议。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

移植 AT&T inline-asm inb / outb 包装器以与 gcc -masm=intel 一起使用 的相关文章

随机推荐