User Tools

Site Tools


wswan:guide:c_inline_assembly

Writing C inline assembly

The gcc-ia16 compiler supports inline assembly written with the AT&T syntax using the __asm keyword.

Most standalone source code in the Wonderful toolchain uses the Intel syntax instead.

For example, to enable CPU interrupts, we could write the following function:

void enable_cpu_interrupts(void) {
    __asm ("sti");
}

However, this wouldn't be ideal. The optimization passes of the compiler may freely re-order instruction blocks, including the interrupt enabler we just wrote; this could lead the CPU interrupts to be enabled at a different time in the function's execution than we expect it! To prevent this, we can add the volatile keyword, like so:

void enable_cpu_interrupts(void) {
    __asm volatile ("sti");
}

Passing values between C and assembly

A more advanced form of using inline assembly is specifying constraints. The __asm keyword supports three types of constraints:

__asm ("code",
        : outputs // (1)!
        : inputs // (2)!
        : clobbers // (3)!
    );

  1. A list of variables to assign to register values at the end of the assembly block.
  2. A list of registers to assign the values of specified variables at the beginning of the assembly block.
  3. A list of registers which may be arbitrarily modified (or clobbered) by the assembly block.

Each constraint consists of a type and expression - for example, “a” (value) connects the constraint a - the register AX - to the variable cvalue. If we wish for a constraint to be an assignment, we should prefix it with the character =.

For example, we could model an I/O port output call: one which has inputs - the port and the value to write - but no outputs:

void outportw(uint8_t port, uint16_t value) {
    __asm volatile (
        "outw %0, %1"
        :
        : "a" (value), "Nd" ((uint16_t) port)
    );
}

Every constraint maps to a %`-prefixed value, in order - so %0 is mapped to value, and %1 is mapped to port.

Another example could involve querying the current value of the code segment:

uint16_t get_cs_value(void) {
    uint16_t result;
    __asm (
        "mov %%cs, %0" // (1)!
        : "=r" (result)
    );
    return result;
}

  1. Note that as percentages are used for constraint mapping, specifying an actual code percentage requires writing % twice.

Finally, we could look at a more complex example: performing a FreyaOS interrupt call.

uint16_t sys_get_version(void) {
        uint16_t result;
        __asm volatile (
                "int $0x17"
                : "=a" (result)
                : "Rah" ((uint8_t) 0x12)
                : "cc", "memory"
        );
        return result;
}

This ASM block will set AH to the constant 0x12, then call the interrupt, then set result to the value of AX. As this is an interrupt call, we may not be certain what it will modify - so we assume it modifies both flags and memory.

Some IA-16 constraints

Type Description
X Any operand.
m Memory address.
g Any non-segment (general-purpose) register, memory address or immediate integer.
r Any register.
a The register AX; for an 8-bit operand, the register AL or AH as chosen by the compiler.
b The register BX.
c The register CX.
d The register DX.
S The register SI.
D The register DI.
Ral The register AL.
Rah The register AH.
Rcl The register CL.
Rbp The register BP.
Rds The register DS.
e The register ES.
q Any 8-bit register - AL, AH, BL, BH, CL, CH, DL, DH.
T Any 16-bit register, including segment registers.
A The 32-bit register pair DX:AX.
j The 32-bit register pair BX:DX.
l Lower 8-bit registers - AL, BL, CL, DL.
u Upper 8-bit registers - AH, BH, CH, DH.
k Any 32-bit register pair, where the lower word is one of AX, BX, CX, DX.
x The register SI or DI.
w The register BX or BP.
B The register SI, DI, BX or BP.
Q A free segment register - DS or ES.

Some constant constraints

Type Description
i Any numeric constant.
Z The constant `0`.
P1 The constant `1`.
M1 The constant `-1`.
Um The constant `-256`.
Lbm The constant `255`.
Lor Constants 128 through 254.
Lom Constants 1 through 254.
Lar Constants -255 through 129.
Lam Constants -255 through -2.
N Constants 0 through 255.

Clobbers

Type Description
cc Processor flags.
memory Memory.
wswan/guide/c_inline_assembly.txt · Last modified: 2024/02/17 10:51 by asie