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