Application Note 172
Using the Extended C166 Inline Assembler

Revision 2 - 26 Feb. 2003

This Application Note tells you how to use the extended inline assembler that is integrated in the Keil C166 Compiler. With the extended inline assembler you may insert assembler instructions into C function code while retaining full debug and optimization capabilities. The extended inline assembler fully supports all C16x, XC16x, ST10 and Super10 instruction set extensions.

Information in this file, the accompany manuals, and software is
Copyright © Keil Software, Inc and Keil Elektronik GmbH.
All rights reserved.


Revision History:

Rev 1: 25. Oct. 2002: Initial Version
Rev 2: 25. Feb. 2003: Added Inline-Assembly within Preprocessor Macros


Contents

  1. Overview
  2. Using the Extended Inline Assembly
  3. Operands and Operators
  4. Register Usage in Assembler Blocks
  5. Technical Support
  6. Contact Details

Overview

The Keil C166 Compiler supports (in version 4.35 or higher) two types of inline assembly:

Inline Assembly with SRC directive: is compatible with previous versions of the C166 Compiler and generates an assembler source file. The assembler source file needs to be assembled with A166. Example:

C166 Measure.c SRC
A166 Measure.SRC

The assembler source file might be used as template for own assembler programs. You may therefore modify the generated SRC file and use it as assembler source file in your µVision2 project.

Inline Assembly without SRC directive: the C166 Compiler assembles the inline assembly statements directly into the object code. The integrated assembler supports all instruction set variants (C166, C167, ST10, and XC16x) including MAC instructions.

The benefits of the integrated inline assembler are:

Supported Processor Instruction Sets

The set of instructions depends on the C166 compiler invocation directives as shown in the table below:

Directive Description
--- 80C166 instruction set
MOD167 C167 instruction set
EXTMAC C167 instructions including ST10 MAC extensions
MODV2 XC16x / Super10 instructions including MAC extensions

Using the Extended Inline Assembler

Extended inline assembler blocks are started with __asm statetmens. The syntax is:

__asm { instruction ; comment }
__asm {               ; open inline-assembly block
       instruction    ; comment
       ...
      }               // close inline-assembly block
Rules for __asm statement blocks:

Example:

int AddUp (int n, int near *pTab)  {
  __asm  {            ; open inline-assembly block
    mov   r2,pTab     ; R2:=start of table
    mov   r3,n
    cmp   r3,#0
    jmp   cc_sle,stop ;
    shl   r3,#1       ; n * 2
    add   r3,r2       ; R3 := (n*2)+pTab, end of table + 2
    mov   r4,#0x00    ; clear result

lM: add   r4,[r2+]    ; add up next value
    cmp   r2,r3       ; end of table ?
    jmp   cc_nz,lM    ; loop if not eot
    ret               ; need result in R4
  }
stop:
  __asm { nop    ; single line assembly }
  __asm { nop    ; another nop          }
  return (0);
}

Using Inline-Assembly within Preprocessor Macros

C macros offer a convenient way to insert assembly code into your source code, but they demand extra care because a macro expands into a single logical line. To create trouble-free macros, follow these rules:

Example:

#define ABLOCK __asm  {  /* comment */     \
                  __asm   mov R5,#0x10     \
                  __asm   l1: mov rl4,#'}' \
                  __asm   mov R6,R5        \
                  __asm   jnbs R6.2,l1     \
              }

The last four __asm keywords seem superfluous, they are needed, however, because the macro expands into a single line.

The preprocessor's handling of '#' within macro definitions starting with __asm is relaxed since # is used for immediate values in assembly language. In pure C, something like #0x10 causes an error in the preprocessing phase due to violating the C preprocessor rules.

#define ABLOCK __asm  {  /* comment */      \
                  __asm       mov R5,#0x10  \
                  __asm   l1: mov rl4,#'}'  \
                  __asm       mov R6,R5     \
                  __asm       bset R6.3     \
                  __asm       jnbs R6.2,l1  \
              }
int  i;

void main (void)  {
  i = 1;
  ABLOCK        // expand inline assembly macro
  i = 2;
}

Operands and Operators

The syntax of the A166 Macro Assembler apply also to __asm blocks. Expression can be absolute or relocatable, as it is the case with the A166 Macro Assembler.

Operands

Constants can be written in Assembler or C format, i.e. 0E123H, 0xE123, 256.

Assembler Symbols can refer to C identifiers such as automatic, global or external variable names, parameters, labels, enumerations, functions, and registerbank names. For you can refer to a registerbank as shown in the following example:

void FuncRB (void) using __MyBank  {
  ...
}

   mov   r3,#__MyBank
   scxt  r3,#__MyBank

The Keil C166 Compiler optimizes the C code and assigns automatic variables and parameters to registers (if possible). When an variable is assigned to CPU registers, you may use it in almost all CPU instructions as direct operand. However, if the variable is located on the user stack or in fixed memory locations, only a few instructions can access this variable. Therefore the legal operand combinations depend on the location of such variables (CPU register, user stack, or fixed memory location). Illegal operand combinations are flagged as error by the C166 Compiler. The instruction combination determined by the C166 Compiler is shown in the listing file. Therefore you should generate a C166 Compiler listing and refer to this listing file in case of errors.

The following two examples show legal and illegal instruction set combinations:

Example 1: Valid access to a local variable

long lFunc (void)  {
  long  l1 = 0;       // l1 is assigned to R6/R7
  __asm  {
     mov  r2,#0x5678
     mov  r1,#0x1234
     add  word0 l1,r1
     add  word2 l1,r2
  }
  return (l1);
}

The instruction ADD WORD0 l1,r1 is converted to ADD R7,R1 which is a valid CPU instruction. The code generated for the above example is shown below:

0000 E006          MOV       R6,#00H
0002 E007          MOV       R7,#00H
;---- Variable 'l1' assigned to Register 'R6/R7' ----
0004 E6F27856      mov  r2,#0x5678
0008 E6F13412      mov  r1,#0x1234
000C 0071          add  word0 l1,r1
000E 0062          add  word2 l1,r2

Example 2: Invalid access to a local variable

This example is almost identical to Example 1 except that the C166 Compiler does not assign the variable to a register due to the lack of an initial assignment.

long lFunc (void)  {
  long  l1;          // l1 is not assigned to registers
                     // because no initial value is present
  __asm  {
    mov  r2,#0x5678
    mov  r1,#0x1234
    add  word0 l1,r1  ; flagged as error
    add  word2 l1,r2
  }
  return (l1);
}

In the previous example, word0 l1 translates to [R0] so the resulting instruction is ADD [R0],R1. This operand combination is not a valid 166 instruction. The instruction is marked as erroneous and details can be reviewed in the listing file:

*** add  word0 l1,r1
*** ______________^
*** add  [Rn+#const],Rn
*** ERROR C195 IN LINE 20 OF InlAsm.C: inline-asm: Invalid instruction operand(s)

Operators

The following operators are supported by the extended inline assembler:

Operator Description
% modulo operator
$ current PC offset (example: JMP cc_sgt,$-2)
. bit position operator
, expression separator
# immediate operand operator
: label operator (example: myLab:)
+  -  * /  arithmetic operators: addition, subtraction, multiplication, division
(  ) expression precedence grouping
[  ] used for indirect register operators (example: [R0+#0x10])
&  |  ^  ! binary operators: and, or, xor, not
==  !=
<=  <  >=  >
relational operators: equal, not equal,
less or equal, less than, greater or equal, greater than
<<  >> shift operators:  shift left, shift right
PAG page number operator (example: MOV R5,#PAG iTable)
POF page offset operator (example: MOV R4,#POF iTable)
SEG segment number operator (example: MOV R5,#SEG iTable)
SOF segment offset operator (example: MOV R4,#SOF iTable)
SHORT short type override (example: JMP SHORT myLabel)
FAR  NEAR far or near type override (example: CALL FAR Func)
BYTE  WORD type override (example: MOV WORD [R1+],#0x12)
sizeof C like sizeof operator (example:  MOV R3,#sizeof (pTable) * 2 )
HIGH  LOW high or low byte of word operand (example: MOV RL4,#HIGH (SOF iTable) )
BYTE0 BYTE1 BYTE2 BYTE3 byte of word or long operand (example: MOV RL4,#BYTE2 iTable) )
WORD0 WORD2 low or high word of long operand (example: MOV R2,WORD0 xhuge_ptr

The access to a high or low word of parameters or local symbols of the data types long, unsigned long, far *, huge *, or xhuge * the WORD0 and WORD2 operators must be used. C166 stores far *, huge *, or xhuge * pointers and long values with low word first, then high word. This word order is also true for long values that are assigned to CPU registers and implies that R(n) is low word and R(n+1) is high word.

The example below shows how to access long values in __asm blocks:

int XpAddUp (int n, int xhuge *pTab)  {
  __asm  {
    mov   r2,word0 pTab   ; get SOF part of xhuge ptr
    mov   r3,word2 pTab   ; get SEG part of xhuge ptr
    cmp   n,#0            ; count zero ?
    jmp   cc_sle,stop     ; stop if so.
    mov   r4,#0x0000      ; clear result

lM: exts  r3,#1
    add   r4,[r2]         ; add data
    add   r2,#sizeof (n)  ; xptrL + 2
    addc  r3,#0x00        ; xptrH + 1 + Cy
    sub   n,#1
    jmp   cc_nz,lM        ; loop if not eot
    ret                   ; return result in R4
  }
stop:
  return (0);
}

This example generates the following code:

             ; FUNCTION XpAddUp (BEGIN  RMASK = @0x4010)
;---- Variable 'pTab' assigned to Register 'R9/R10' ----
;---- Variable 'n' assigned to Register 'R8' ----

004E F029          mov   r2,word0 pTab   ; get SOF part of xhuge ptr
0050 F03A          mov   r3,word2 pTab   ; get SEG part of xhuge ptr
0052 4880          cmp   n,#0            ; count zero ?
0054 BD08          jmp   cc_sle,stop     ; stop if so.
0056 E004          mov   r4,#0x0000      ; clear result
0058         lM:
0058 DC03          lM: exts  r3,#1
005A 084A          add   r4,[r2]         ; add data
005C 0822          add   r2,#sizeof (n)  ; xptrL + 2
005E 1830          addc  r3,#0x00        ; xptrH + 1 + Cy
0060 2881          sub   n,#1
0062 3DFA          jmp   cc_nz,lM        ; loop if not eot
0064 CB00          ret                   ; need result in R4
0066         stop:
0066 E004          MOV   R4,#00H
0068 CB00          RET
             ; FUNCTION XpAddUp (END    RMASK = @0x4010)

Register Usage in Assembler Blocks

When using __asm blocks the user should be aware of the following:

Technical Support

At Keil Software, we are dedicated to providing you with the best development tools and technical support. That's why we offer numerous ways you can get the technical support you need to complete your embedded projects.

Many of the features of our Technical Support Knowledgebase and Web Site are the results of your suggestions. If you have any ideas that will improve them, please give us your feedback!

Contact Details

If you experience any problems or have any questions about this Application Note, contact one of our distributors or offices for assistance.

In the USA...

Keil Software, Inc.
1501 10th Street, Suite 110
Plano, TX  75074
USA

800-348-8051 - Sales
972-312-1107 - Support
972-312-1159 - Fax

sales.us@keil.com
- Sales E-Mail
support.us@keil.com
- Support E-mail 
  

In Europe...

Keil Elektronik GmbH
Bretonischer Ring 15
D-85630 Grasbrunn
Germany

+49 89 456040-0 - Sales
+49 89 456040-24 - Support
+49 89 468162 - Fax

sales.intl@keil.com
- Sales E-Mail
support.intl@keil.com
- Support E-Mail
  


Copyright © Keil Software, Inc. and Keil Elektronik GmbH.
All rights reserved.
Visit our web site at www.keil.com.