Directives

A compiler directive is a non executable statement which tells the compiler how to compile. That is, it won’t generate any code that can be run on the target device. For example, some microcontrollers have certain hardware features that others don’t have. A compiler directive can be used to tell the compiler to add or remove source code, based on that particular devices ability to support that hardware.

Unlike normal program code, you cannot use the line continuation character when using directives. You should therefore ensure that each directive is on a line of its own. Don’t have directives and source code on the same line.

Directives can be nested in the same way as source code statements. For example,

#ifdef MyValue
   #if MyValue = 10
      Const CodeConst = 10
      #else
      Const CodeConst = 0
   #endif
#endif

#const

 #const identifier = expression

Creates a constant identifier. A constant identifier is global and its value cannot be changed once set.

#variable

 #variable identifier = expression

Creates a variable identifier. A variable identifier is global, but unlike a constant directive, its value and type can be changed. For example,

#variable MyValue = 10
#variable MyValue = "Hello"

#define

 #define identifier [ = expression]

Creates a define identifier. A define identifier is global, which can be taken out of scope using the #undefine directive. An optional expression can be assigned to a define directive. This can be used like a constant in any expression.

#undefine

 #undefine identifier

Undefines a previously declared define directive.

#ifdef…#else…#endif

 #ifdef identifier
   {code}
 [#else 
   {code}]
 #endif

Include source statements if a define directive has been declared. If the expression evaluates to false (that is, the identifier has not been declared), the code statements following #else are included. The #else directive is optional.

#ifndef…#else…#endif

 #ifndef identifier
   {code}
 [#else
   {code}]
 #endif

Include source statements if a define directive has not been declared. If the expression evaluates to false (that is, the identifier has been declared), the code statements following #else are included. The #else directive is optional.

#if…#elseif…#else…#endif

 #if expression
   {code}
 [#elseif expression
   {code}]
 [#else
   {code}]
 #endif

Include source statements if an expression evaluates to true. If the expression evaluates to false, each #elseif is then evaluated. If neither #if or #elseif evaluate to true, code statements following #else are included. Both #elseif and #else directives are optional.

#error

 #error "Error string"

Generate a preprocessor error and halt compilation.

#warning

 #warning "Error string"

Generate a preprocessor warning. Unlike #error, compilation will continue.

#option

 #option identifier [ = expression]

Creates an option identifier. An option identifier is global and identical to the #define directive in every way, except for two main differences. Firstly, an option identifier can be seen and used in the declaration block of a user program. For example,

#option BUFFER_SIZE = 64      
Const MyBufferSize = BUFFER_SIZE

In the above example, the program constant MyBufferSize becomes equal to 64, as this is the value assigned to the option identifier BUFFER_SIZE. It is important to note that a user program identifier will always take precedence over an option directive. For example,

#option BUFFER_SIZE = 64      
Const BUFFER_SIZE = 16
Const MyBufferSize = BUFFER_SIZE

In this example, MyBufferSize becomes equal to 16. The other difference between option and define directives is that re-declaring an option twice, using the same identifier will not cause an error. For example,

#option BUFFER_SIZE = 64 
#option BUFFER_SIZE = 16 

In this example, BUFFER_SIZE will become equal to 64, which is the first option found by the preprocessor. The second declaration of BUFFER_SIZE is ignored. This capability may appear a little redundant at first, but it's an extremely useful technique to enable users to configure a module from their main program code.

IsOption

 IsOption(Identifier)

Used in conjunction with an #If…#else…#endif statement IsOption will return true if an #option for the referenced identifier has been defined. This is primarily of use for validating the parameters which have been defined in the #option. Typically, if the validation fails an error can be raised to the preprocessor and the error message reported.

#if IsOption(USART_LS) And Not (USART_LS in (True, False))
   #error USART_LS, "Invalid option. LS must be TRUE or FALSE."
#endif
#option USART_LS = False

This will check to see if the option USART_LS has been previously defined. If it has, it will check the values that have been assigned to the option are true or false. If any another value has been assigned to USART_LS it will generate an error. The last line will then set a default value for the USART_LS option.

Preprocessor Expressions

The preprocessor allows you to use a rich set of operators within expressions. They include

Relational<, <=, <>, >=, >, =
Logicaland, or, xor, not
Bitwise<<, >>, and, or, xor, not
Math+, -, /, *, mod

In addition to the standard operators show above, the preprocessor also allows the use of the in operator. For example,

#if _clock = 3 Or _clock = 4 Or _clock = 8 Or _clock = 10
   #define LowSpeed
#endif

can be written more simply as

#if _clock in (3,4,8,10)
   #define LowSpeed
#endif

You can also use a range of values with the in operator. For example,

#if Value in (1 to 10, 20, 100 to 255)
   #define LegalRange
#else
   #error "Value out of range"
#endif

Because the in operator evaluates to true or false, you can apply other boolean operators. For example,

#if Not (Value in (1 to 10))
   #error "Out of range"
#endif

In the above example, an error is generated if value is not in the range 1 to 10.