【问题标题】:What is the nasm assembly code version of gcc inline assemblygcc内联汇编的nasm汇编代码版本是什么
【发布时间】:2021-02-15 20:20:45
【问题描述】:

我有这段代码可以启用光标:

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


static inline void outb(uint16_t port, uint8_t val)
{
    asm volatile ( "outb %0, %1" : : "a"(val), "d"(port) );
}


int main(void)
{
    outb(0x3D4, 0x0A);
    outb(0x3D5, (inb(0x3D5) & 0xC0) | 1);
    outb(0x3D4, 0x0B);
    outb(0x3D5, (inb(0x3D5) & 0xE0) | 2);
}

我不想使用内联汇编并在 nasm 汇编中编写它,但是当使用 outb 的汇编实现而不是内联汇编时,它不会正确启用光标。 nasm 函数的声明:

extern void outb(uint16_t port, uint8_t val);

以及功能:

outb:
    mov dx, di
    mov eax, esi
    out dx, al
    ret

有什么问题?

更新: 该函数现在看起来像这样,但它仍然不起作用:

outb:
    push ebp
    mov esp, ebp
        
    mov dx, word [ebp+2]
    mov al, byte [ebp+3]
    out dx, al
    
    mov esp, ebp
    pop ebp
    ret

【问题讨论】:

  • 你对 c++ 中的 nasm 函数的声明是什么?问题可能在于参数如何传递给您的 asm 函数。
  • 您应该将所有外部 asm 函数声明为 extern "C"。 c++ 中的参数传递可能会因 -O 优化参数或其他编译选项而异。这意味着 asm 函数的函数参数应该在堆栈上传递。
  • 你为什么不想使用内联汇编?! GCC 可以优化内联汇编器,但肯定不能使用您的外部 nasm 函数。
  • 偏移量ebp+2ebp+3 是错误的。也许您正在计算双字而不是字节?给自己画一张堆栈布局图,记住每个参数都是作为一个 4 字节的 dword 推送的。你应该以mov dx, word [ebp+8]mov al, byte [ebp+12] 结尾。当然,首先没有必要使用ebp 设置堆栈帧,因为您可以访问与esp 相关的参数,如Antti 的回答。
  • 第一个mov esp, ebp也是倒数,应该是mov ebp, esp

标签: c gcc x86 nasm inline-assembly


【解决方案1】:

您的代码对于64-bit SYSV ABI. 是正确的。参数在EDIESI 寄存器中传递:

outb:
        mov     eax, esi
        mov     edx, edi
        outb dx, al
        ret

但在 32 位 ABI 中,它们通过 on stack:

outb:
        mov     edx, DWORD PTR [esp+4]
        mov     eax, DWORD PTR [esp+8]
        outb dx, al
        ret

但这也显示了不使用内联汇编程序的问题 - 你的参数现在必须被压入堆栈并且它们也必须被弹出,而如果你使用 GCC内联汇编编译器只需要发出内联的 outb 指令,其中仅包含将正确值放入 aldx 的内容 - 您的代码将更加高效。

【讨论】:

  • 将其设为 out dx, al 用于 Intel 语法。
  • @NateEldredge 哎呀你是对的,我想知道为什么它会出现这样的错误:D
  • OP 为 AT&T 语法编写了他们的内联汇编,但 Godbolt 默认为 -masm=intel,这会破坏它。
【解决方案2】:

static inline void ... 等效的 NASM 是一个,而不是一个函数你实际上是 call。 C 编译器会将其内联到单个 out 指令,其中输入在寄存器中(或者作为立即数,这是 "N" 约束允许的,"d" 允许 DX 寄存器。)

实际上设置一个函数调用会比写一个out指令更麻烦。


该宏可能应该只是一个out 指令(没有mov 开销),可能带有%ifnidn 字符串处理和一个%if 数字条件来验证端口号是字符串dx 还是从 0..255 开始的数字,第二个操作数是 al。 (有关表格的限制,请参阅the manual。)

或者像普通人一样直接使用out;在 C 中包装它的原因是隐藏 inline-asm 噪音,但这不适用于纯 asm。使用宏更有可能使事情变得更加困难(如果您不将其限制为 out 指令,则通过踩其他寄存器)。

在 NASM 中,out 的操作数大小由您使用的寄存器隐含:您不能写 outb,只能写 out 0x80, alout 0x80, ax 或其他。 outb 是一个错误。

【讨论】:

    猜你喜欢
    • 2011-01-07
    • 1970-01-01
    • 2010-12-12
    • 2012-06-16
    • 2012-10-20
    • 2012-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多