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

1

7

This question is inspired by a question asked by someone on another forum. In the following code what does the extended inline assembly constraint Rah and Ral mean. I haven't seen these before:

#include<stdint.h>

void tty_write_char(uint8_t inchar, uint8_t page_num, uint8_t fg_color)
{
    asm (
        "int $0x10"
        :
        : "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;
}

In particular are the use of Rah and Ral as constraints in this code:

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

The GCC Documentation doesn't have an l or h constraint for either simple constraints or x86/x86 machine constraints. R is any legacy register and a is the AX/EAX/RAX register.

What am I not understanding?

Dotation answered 1/7, 2020 at 22:30 Comment(0)
D
9

What you are looking at is code that is intended to be run in real mode on an x86 based PC with a BIOS. Int 0x10 is a BIOS service that has the ability to write to the console. In particular Int 0x10/AH=0x0e is to write a single character to the TTY (terminal).

That in itself doesn't explain what the constraints mean. To understand the constraints Rah and Ral you have to understand that this code isn't being compiled by a standard version of GCC/CLANG. It is being compiled by a GCC port called 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.

The documentation for ia16-gcc is hard to find online in HTML format but I have produced a copy for the recent GCC 6.3.0 versions of the documentation on GitHub. The documentation was produced by building ia16-gcc from source and using make to generate the HTML. If you review the machine constraints for Intel IA-16—config/ia16 you should now be able to see what is going on:

Ral The al register.

Rah The ah register.

This version of GCC doesn't understand the R constraint by itself anymore. The inline assembly you are looking at matches that of the parameters for 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

Other Information

The documentation does list all the constraints that are available for the IA16 target:

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).

There are many new constraints and some repurposed ones.

In particular the a constraint for the AX register doesn't work like other versions of GCC that target 32-bit and 64-bit code. The compiler is free to choose either AH or AL with the a constraint if the values being passed are 8 bit values. This means it is possible for the a constraint to appear twice in an extended inline assembly statement.

You could have compiled your code to a DOS EXE with this command:

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

This targets the 80186. You can generate 8086 compatible code by omitting the -march=i186 The generated code for main would look something like:

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

When run with the command line int10h.exe "Hello, world!" should print:

Hello, world!


Special Note: The IA16 port of GCC is very experimental and does have some code generation bugs especially when higher optimization levels are used. I wouldn't use it for mission critical applications at this point in time.

Dotation answered 1/7, 2020 at 22:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.