Table of Contents
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)!
);
- A list of variables to assign to register values at the end of the assembly block.
- A list of registers to assign the values of specified variables at the beginning of the assembly block.
- 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;
}
- 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. |