This chapter describes support for assembly language programming built into the CodeWarrior® compilers.
This chapter does not discuss the stand-alone assembler available for Win32/x86. For information on the stand-alone assembler, refer to the CodeWarrior Assembler Reference.
The sections in this chapter are:
Much of the general discussion in the first section applies to working with AMD® processors, with the exception of the assembly instructions used for illustration of the principles.
This section describes how to use the CodeWarrior compiler's built-in support for assembly language programming, including assembler syntax form and conventions.
This chapter does not document all the instructions available in Win32/x86 assembly language. For information on Win32/x86 assembly language instructions, refer to the Intel Architecture Software Developer's Manual, Volume 2: Instruction Set Reference Manual by Intel® Corporation.
The topics in this section are:
This section describes assembly syntax conventions. The assembly instructions you use are the standard x86 instruction mnemonics. For information on assembly support for AMD processor instructions refer to "Targeting AMD Processors."
This discussion of assembly syntax is broken up into the following sections:
To specify that a block of code in your file should be interpreted
as assembly language, use the asm keyword. The asm keyword is part of both the ANSI C and C++ standards.
NOTE To ensure that the C/C++ compiler recognizes the asm keyword, you must manipulate the ANSI Strict and ANSI Keywords Only options in the C/C++ Language settings panel. This panel and its options are fully described
in the IDE User Guide or the C Compilers Reference.
Sections marked asm will compile correctly when ANSI Strict is enabled, but enabling ANSI Keywords Only disables them when compiled as C, but not as C++. The alternate
form __asm, borrowed from Microsoft® Visual C++, is always supported.
You can write code to specify that an entire function is in assembly language. However, as of this writing, the compiler
does not directly support function-level assembly language using
the asm keyword in the declaration.
For now, function-level assembly code uses the syntax shown in
Listing 10.1, where the __declspec(naked) suppresses the normal compiler prologue and epilogue code.
__declspec(naked) void functionname(void)
{
asm {
// asm statements
}
}
Alternatively, assembly statement blocks within a function are also supported. In other words, you can write code that is both in function-level assembly language and statement-level assembly language.
An example of statement-level code syntax is shown in Listing 10.2.
long MyFunc (void)
{
asm {. . .} // statement blocks are supported
}
Statement-level assembler syntax has the form shown in Listing 10.3.
Statement-level assembler syntax:
asm { one or more instructions }
An asm statement can be used wherever a code statement is allowed.
NOTE Functions that contain an asm block are only partially optimized, as the function itself will
be optimized, but the optimizer will skip any asm blocks of code. See "Inline Assembly and Optimization" for more information.
Keep these tips in mind as you write assembly functions:
[LocalLabel:] (instruction | directive) [operands]
Each instruction must end with a new line or a semicolon (;).
Hex constants must be in C-style, not Pascal-style. For example, see Listing 10.5. Hex constant style:
mov eax, 0xABCDEF // OK mov eax, $ABCDEF // ERROR
0b prefix is an extension used by the CodeWarrior IDE for representing
binary numbers in constants. These expressions can be written
as shown in Listing 10.6. Hex and binary constants:
0aB42h == 0xab42 10100b == 0b10100
mov eax // OK MOV EAX // OK
The Win32/x86 inline assembler supports nesting asm blocks within a function. Inline assembly code for Win32/x86
uses the syntax shown in Listing 10.8. Nothing else can follow the statement on the same line except
a comment or another __asm statement. This form is useful for
defining macros that have inline assembly. An asm instruction or block may be used wherever a statement is allowed.
asm (single instruction )
asm { multiple instructions}
__asm single instruction
__asm statement __asm statement
NOTE Variable references by inline assembly instructions used to be forced into memory and could not be allocated to processor registers by the optimizer. This is no longer a limitation. If no instructions reference the address of a variable, it is still a candidate for being placed in a register.
There is one more feature that may prove useful in your code development.
You can also use Microsoft-style asm blocks, as in Listing 10.9.
void foo
{
asm { asm mov eax, 1 asm mov eax, 2 }
}
// or, format like the line below
__asm { __asm mov eax, eax __asm mov eax, eax }
You can also use the macro-expansion form shown in Listing 10.10.
Defining __asm Blocks as C Macros:
#define my_asm_block \
__asm { \
__asm mov eax, 1 \
__asm mov eax, 2 \
}
For more details on Microsoft-style assembly statement syntax, refer to the documentation that came with your Microsoft tools, or on the web at:
http://msdn.microsoft.com
The CodeWarrior IDE supports the ALIGN, DB, DW, DD, EMIT directives in assembly code, among others:
| align next instruction to n-byte boundry, if such boundry is within m bytes | |
The compiler supports a pair of directives called OPTIMIZE and NOOPTIMIZE. When used, these signal that a block of inline assembly code
should be treated as if it came from the compiler and allowed
to be both scheduled and peephole optimized. These directives
only affect the particular block of code in which they appear.
A label can be any identifier that you have not already declared
as a local variable. The name may start with an at-symbol (@), so foo, @foo, and @1 are legal names. All labels need to end in a colon. An example
is shown in Listing 10.11.
Labels for Win32/x86 assembly:
void foo(void)
{
asm {
x1: mov eax, 2 // OK, has colon
@x2: mov eax, 2 // OK, has both @ and colon
x3 mov eax, 2 // ERROR, Needs colon
}
}
NOTE The first statement in an assembly function cannot be a label
that starts with @.
You cannot begin comments with a pound sign (#), since the preprocessor uses the pound sign. However, you can
use C and C++ comments, and semicolons too. Listing 10.12 shows examples of valid comments.
Comments in Win32/x86 assembly:
mov eax, 8# ERRORmov eax, 8// OKmov eax, 8/* OK */mov eax, 8; OK
You can use all preprocessor features, such as comments and macros,
in the assembler. An instruction can end with a new asm keyword. This is allowed mainly for use in macros such as the
example in Listing 10.13, since the preprocessor will expand everything onto one line
of code.
#define ASM_MOVE \
__asm { \
__asm mov eax, eax \
__asm mov edx, edx \
}
Assembly instructions may refer to local and global variables
as operands. They can use the name of a structure, class, or union
as an immediate operand that evaluates to the size of the structure.
To specify the offset of a member, use the structure, union, or
class name qualified with the member name, separated by a dot
(.).
Local variables can now be assigned to MMX registers. Such local
variables can be used as operands to MMX and 3DNow! assembly instructions
within an asm block. The compiler first assigns the variables to virtual registers,
and then later they are assigned to real MMX registers as they
are available.
The example in Listing 10.14 shows a variable c that is assigned to an MMX register.
Local variables in MMX registers:
inline long long paddsb(long long a, long long b)
{
long long c;
asm {
movq c, a
paddsb c, b
}
return c;
}
Note that there are some restrictions on this feature. A long long variable will not be assigned to a virtual MMX register if any
of the following holds:
1. The address is taken with the & operator.
2. It is used in non-MMX contexts, as the variable c in the first line of Listing 10.15.
3. It is used in any C expression other than an operand to the
function return statement.
In addition, note that data objects that correspond to MMX types
should be declared as 64-bit integers (long long).
Yet another restriction is that local variables, like the variable
c shown here, can be used as operands to MMX instructions but
cannot be used in non-MMX contexts. For example, the code in the
first line of Listing 10.15 will not work, since the source operand of the mov instruction is not a context in which an MMX register can be
used. Note that the code in the last line of Listing 10.15 will work correctly, since an MMX register can serve as the source
operand of movd.
Restrictions on local variables in MMX Registers:
mov eax, c movd eax, c
The x86 compiler is able to inline C/C++-style functions containing
inline asm{} blocks. Previously, any functions that contained asm statements could not be inlined by the compiler. Using the inline keyword will now allow the desired operation in these cases.
The Metrowerks compiler for x86 can efficiently optimize inline
asm assembly code. Optimization and assembly code are no longer mutually-exclusive
options.
Note that functions must return values using the C return statement. You can't just place the return value in the proper
register as you normally would, since the compiler does not see
this as a return.
Intrinsics are specific extensions to the language that allow you to quickly insert blocks of assembly code from your high-level source code.
If the __builtin_ form of an intrinsic is shown, that means that the intrinsic
code will be generated even if the inlining of intrinsics is disabled
in the settings panels. See "x86 Processor" for more details.
The following types of intrinsics are supported:
These are memory allocator intrinsics. This family of functions allocate memory on the local stack. Therefore, the memory is automatically released when the function in which it is allocated exits.
| Intrinsic |
Description |
|---|---|
These are block memory operations. These all have C-standard equivalent high-level functions you can also use.
| Intrinsic |
Description |
|---|---|
These are block memory operations for wide characters.
| Intrinsic |
Description |
|---|---|
These intrinsics are used for strings. These all have C-standard equivalent functions you can also use.
| Intrinsic |
Description |
|---|---|
These intrinsics are used for wide strings.
| Intrinsic |
Description |
|---|---|
These instrinsics are for floating-point math operations. These
math functions all have equivalent high-level calls in the C Standard
library implementations. Refer to math.h for parameter syntax. The file is installed with the CodeWarrior
IDE and can easily be found using the Find feature of Windows®.
| Intrinsic |
Description |
|---|---|
You can find documentation on the World Wide Web for the intrinsics in Table 10.7, including the following page:
http://msdn.microsoft.com/library/devprods/vs6/visualc/vccore/_crt__outp.2c_._outpw.2c_._outpd.htm
These correspond with the built-in IN/OUT instructions on the
x86 processor in byte, word, and double word sizes. They are setup
so that if a compile time constant is used, they will turn into
the constant forms of the instructions, otherwise they will be
the forms using the EDX register.
NOTE These intrinsics will probably not work from code running in Windows NT®, since most port access is protected. These are most useful for engineers writing drivers that run inside an operating system where direct port access is allowed.
| Intrinsic |
|---|
Most of the same principles apply for x86 assembly syntax as for AMD syntax. However, the available assembly instructions for x86 processors differ from AMD processors.
For information on Advanced Micro Devices, Inc. (AMD) 3DNow! Technology, refer to the 3DNow! Technology Manual, published by AMD (publication # 21928). Also, see "Targeting AMD Processors" for specific information about getting the most from these processors.