Rob*_*t B 8 x86 gcc g++ inline-assembly osdev
据我所知,gcc内联汇编中使用的约束告诉gcc输入和输出变量必须(或必须),以便生成有效的汇编.正如精细手册所说,"对操作数放置的限制".
这是一个教程中特定的工作示例.
static inline uint8_t inb(uint16_t port)
{
uint8_t ret;
asm volatile ( "inb %1, %0"
: "=a"(ret)
: "Nd"(port) );
return ret;
}
Run Code Online (Sandbox Code Playgroud)
inb是AT&T语法 - 代表IN从I/O端口接收一个字节的i386 指令.
以下是本指令的规格,取自i386手册.需要注意的是端口号,从去0x0000到0xFFFF.
IN AL,imm8 // Input byte from immediate port into AL
IN AX,imm8 // Input word from immediate port into AX
IN EAX,imm8 // Input dword from immediate port into EAX
IN AL,DX // Input byte from port DX into AL
IN AX,DX // Input word from port DX into AX
IN EAX,DX // Input dword from port DX into EAX
Run Code Online (Sandbox Code Playgroud)
给出类似uint8_t x = inb(0x80);汇编输出的声明,正确地说inb $0x80,%al.它使用IN AL,imm8了指令的形式.
现在,假设我只关心IN AL,imm8表单,接收uint8_t来自端口0x00和0xFF包含端口的端口.这和工作示例之间的唯一区别是,port现在是uint8_t模板参数(使其有效地成为常量),现在是约束"N".
template<uint8_t port>
static inline uint8_t inb()
{
uint8_t ret;
asm volatile ( "inb %1, %0"
: "=a"(ret)
: "N"(port) );
return ret;
}
Run Code Online (Sandbox Code Playgroud)
失败!
我认为"N"约束意味着"你必须为这条指令设置一个恒定的无符号8位整数",但很明显它并不是因为它是一个"不可能的约束".uint8_t模板参数不是一个常量无符号8位整数吗?
如果我用"Nd"替换"N",我会得到一个不同的错误:
./test.h: Assembler messages:
./test.h:23: Error: operand type mismatch for `in'
Run Code Online (Sandbox Code Playgroud)
在这种情况下,汇编器输出inb %dl, %al显然是无效的.
为什么会这样只有工作"Nd"和uint16_t不"N"和uint8_t?
编辑:
这是我在godbolt.org上试过的精简版:
#include <cstdint>
template<uint8_t N>
class Port {
public:
uint8_t in() const {
uint8_t data;
asm volatile("inb %[port], %%al"
:
: [port] "N" (N)
: // clobbers
);
return data;
}
};
void func() {
Port<0x7F>().in();
}
Run Code Online (Sandbox Code Playgroud)
有趣的是,这种方法很好,除非您将N更改为0x80和0xFF之间的任何值.在clang上,这会生成"128超出约束N的范围"错误.这会在gcc中生成更一般的错误.
根据约束的记录方式,您的代码应该按预期工作。
一年多后,这似乎仍然是一个错误。编译器似乎正在N从无符号值转换为有符号值,并尝试将其传递到内联汇编约束中。当传递到约束的值无法表示为 8 位有符号值时,这当然会失败。输入约束"N"假设允许无符号 8 位值,并且应接受 0 到 255 (0xff) 之间的任何值:
氮
无符号 8 位整数常量(用于 in 和 out 指令)。
有一个与 GCC 的 bugzilla类似的错误报告,标题为“常量约束检查符号扩展了无符号常量输入操作数”。
在相关线程之一中,建议您可以通过将 (&) 0xff 与常量进行与运算(即:N & 0xff)来解决此问题。我还发现静态转换N为比更宽的无符号类型uint8_t也有效:
#include <cstdint>
template<uint8_t N>
class Port {
public:
uint8_t in() const {
uint8_t data;
asm volatile("inb %[port], %0"
: "=a"(data)
: [port] "N" (static_cast<uint16_t>(N))
: // clobbers
);
return data;
}
};
void func() {
Port<0x7f>().in();
Port<0x80>().in();
// Port<0x100>().in(); // Fails as expected since it doesn't fit in a uint8_t
}
Run Code Online (Sandbox Code Playgroud)
要测试这一点,您可以在godbolt上使用它。
| 归档时间: |
|
| 查看次数: |
349 次 |
| 最近记录: |