扩展内联汇编中的约束 "Rah" 和 "Ral" 是什么意思?

What do the constraints "Rah" and "Ral" mean in extended inline assembly?

这个问题的灵感来自另一个论坛上某人提出的问题。在下面的代码中,扩展内联汇编约束 RahRal 是什么意思。我以前没见过这些:

#include<stdint.h>

void tty_write_char(uint8_t inchar, uint8_t page_num, uint8_t fg_color)
{
    asm (
        "int [=12=]x10"
        :
        : "b" ((uint16_t)page_num<<8 | fg_color),
          "Rah"((uint8_t)0x0e), "Ral"(inchar));
}

void tty_write_string(const char *string, uint8_t page_num, uint8_t fg_color)
{
    while (*string)
        tty_write_char(*string++, page_num, fg_color);
}

/* Use the BIOS to print the first command line argument to the console */
int main(int argc, char *argv[])
{
    if (argc > 1)
        tty_write_string(argv[1], 0, 0);

    return 0;
}

特别是在这段代码中使用 RahRal 作为约束:

asm (
    "int [=13=]x10"
    :
    : "b" ((uint16_t)page_num<<8 | fg_color),
      "Rah"((uint8_t)0x0e), "Ral"(inchar));

GCC Documentation doesn't have an l or h constraint for either simple constraints or x86/x86 machine constraintsR 是任何遗留寄存器,aAX/EAX/RAX 寄存器。

我哪里不明白?

您正在查看的代码旨在 运行 在具有 BIOS 的基于 x86 的 PC 上以实模式运行。 Int 0x10 is a BIOS service that has the ability to write to the console. In particular Int 0x10/AH=0x0e是写单个字符到TTY(终端)。

这本身并不能解释约束的含义。要了解约束 RahRal,您必须了解此代码不是由 GCC/CLANG 的标准版本编译的。它由名为 ia16-gcc. It is a special port that targets 8086/80186 and 80286 and compatible processors. It doesn't generate 386 instructions or use 32-bit registers in code generation. This experimental version of GCC is to target 16-bit environments like DOS (FreeDOS, MSDOS), and ELKS.

的 GCC 端口编译

很难在网上找到 HTML 格式的 ia16-gcc 文档,但我为最近的 GCC 6.3.0 versions of the documentation on GitHub 制作了一份副本。该文档是通过从源代码构建 ia16-gcc 并使用 make 生成 HTML 生成的。如果您查看 Intel IA-16—config/ia16 的机器限制,您现在应该能够看到发生了什么:

Ral The al register.

Rah The ah register.

此版本的 GCC 本身不再理解 R 约束。您正在查看的内联程序集与 Int 0x10/Ah=0xe:

的参数匹配
VIDEO - TELETYPE OUTPUT
AH = 0Eh
AL = character to write
BH = page number
BL = foreground color (graphics modes only)

Return:
Nothing

Desc: Display a character on the screen, advancing the cursor
      and scrolling the screen as necessary

其他信息

文档确实列出了可用于 IA16 目标的所有约束:

Intel IA-16—config/ia16/constraints.md
a
The ax register. Note that for a byte operand, 
this constraint means that the operand can go into either al or ah.
     
b
The bx register.

c
The cx register.

d
The dx register.

S
The si register.

D
The di register.

Ral
The al register.

Rah
The ah register.

Rcl
The cl register.

Rbp
The bp register.

Rds
The ds register.

q
Any 8-bit register.

T
Any general or segment register.

A
The dx:ax register pair.

j
The bx:dx register pair.

l
The lower half of pairs of 8-bit registers.

u
The upper half of pairs of 8-bit registers.

k
Any 32-bit register group with access to the two lower bytes.

x
The si and di registers.

w
The bx and bp registers.

B
The bx, si, di and bp registers.

e
The es register.

Q
Any available segment register—either ds or es (unless one or both have been fixed).

Z
The constant 0.

P1
The constant 1.

M1
The constant -1.

Um
The constant -256.

Lbm
The constant 255.

Lor
Constants 128 … 254.

Lom
Constants 1 … 254.

Lar
Constants -255 … -129.

Lam
Constants -255 … -2.

Uo
Constants 0xXX00 except -256.

Ua
Constants 0xXXFF.

Ish
A constant usable as a shift count.

Iaa
A constant multiplier for the aad instruction.

Ipu
A constant usable with the push instruction.

Imu
A constant usable with the imul instruction except 257.

I11
The constant 257.

N
Unsigned 8-bit integer constant (for in and out instructions).

有许多新约束和一些重新调整用途的约束。

特别是 AX 寄存器的 a 约束不像针对 32 位和 64 位代码的其他版本的 GCC 那样工作。编译器可以自由选择 AHALa 约束 if传递的值是 8 位值。这意味着 a 约束可以在扩展的内联汇编语句中出现两次。

您可以使用以下命令将代码编译为 DOS EXE:

ia16-elf-gcc -mcmodel=small -mregparmcall -march=i186 \
             -Wall -Wextra -std=gnu99 -O3 int10h.c -o int10h.exe

这针对 80186。您可以通过省略 -march=i186 生成 8086 兼容代码 main 生成的代码类似于:

00000000 <main>:
   0:   83 f8 01                cmp    ax,0x1
   3:   7e 1d                   jle    22 <tty_write_string+0xa>
   5:   56                      push   si
   6:   89 d3                   mov    bx,dx
   8:   8b 77 02                mov    si,WORD PTR [bx+0x2]
   b:   8a 04                   mov    al,BYTE PTR [si]
   d:   20 c0                   and    al,al
   f:   74 0d                   je     1e <tty_write_string+0x6>
  11:   31 db                   xor    bx,bx
  13:   b4 0e                   mov    ah,0xe
  15:   46                      inc    si
  16:   cd 10                   int    0x10
  18:   8a 04                   mov    al,BYTE PTR [si]
  1a:   20 c0                   and    al,al
  1c:   75 f7                   jne    15 <main+0x15>
  1e:   31 c0                   xor    ax,ax
  20:   5e                      pop    si
  21:   c3                      ret
  22:   31 c0                   xor    ax,ax
  24:   c3                      ret

当 运行 使用命令行 int10h.exe "Hello, world!" 时应该打印:

Hello, world!


特别说明:GCC 的 IA16 端口是非常实验性的,确实存在一些代码生成错误,尤其是在使用更高优化级别时。目前我不会将它用于关键任务应用程序。