扩展内联汇编中的约束 "Rah" 和 "Ral" 是什么意思?
What do the constraints "Rah" and "Ral" mean in extended inline assembly?
这个问题的灵感来自另一个论坛上某人提出的问题。在下面的代码中,扩展内联汇编约束 Rah
和 Ral
是什么意思。我以前没见过这些:
#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;
}
特别是在这段代码中使用 Rah
和 Ral
作为约束:
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 constraints。 R
是任何遗留寄存器,a
是 AX/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(终端)。
这本身并不能解释约束的含义。要了解约束 Rah
和 Ral
,您必须了解此代码不是由 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 那样工作。编译器可以自由选择 AH 或 AL 与 a
约束 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 端口是非常实验性的,确实存在一些代码生成错误,尤其是在使用更高优化级别时。目前我不会将它用于关键任务应用程序。
这个问题的灵感来自另一个论坛上某人提出的问题。在下面的代码中,扩展内联汇编约束 Rah
和 Ral
是什么意思。我以前没见过这些:
#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;
}
特别是在这段代码中使用 Rah
和 Ral
作为约束:
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 constraints。 R
是任何遗留寄存器,a
是 AX/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(终端)。
这本身并不能解释约束的含义。要了解约束 Rah
和 Ral
,您必须了解此代码不是由 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.
很难在网上找到 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 那样工作。编译器可以自由选择 AH 或 AL 与 a
约束 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 端口是非常实验性的,确实存在一些代码生成错误,尤其是在使用更高优化级别时。目前我不会将它用于关键任务应用程序。