Mixing C and Assembly

A Dr. Taylor Tutorial

Mixing C and Assembly Languages

Syntax Differences with GCC

Common Code in .s Files

Every .s file should contain the following GCC directives:

#define _SFR_ASM_COMPAT 1  /* Not sure when/if this is needed */
#define __SFR_OFFSET 0

C Compiler's Register Usage

Passing Parameters between C and Assembly

uint8_t function(uint8_t i, uint8_t j);

Example Code

Consider the following trivially simple program:

// driver.c
#include <avr/io.h>
  
extern void asmfunc_calledfrom_c(uint8_t val);
  
int main()
{
  DDRB = 0xff;
  asmfunc_calledfrom_c(3);
  
  return 0;
}
// raw.s
#define __SFR_OFFSET 0          // Use 0 for the I/O register offset
#include <avr/io.h>             // Defines I/O port aliases
  
.global asmfunc_calledfrom_c    ; Makes asmfunc_calledfrom_c visible in other source files
  
.section .text                  ; Defines a code section
  
asmfunc_calledfrom_c:           ; Start of asmfunc_calledfrom_c subroutine
    out  PORTB, r24             ; Send value passed to asmfunc_calledfrom_c to PORTB
    ret

When compiled and linked, the above two source files produce the following .lss file (only a portion of the file is included below):

00000092 <main>:
  92:   cf 93           push    r28
  94:   df 93           push    r29
  96:   cd b7           in      r28, 0x3d       ; 61
  98:   de b7           in      r29, 0x3e       ; 62
  9a:   e7 e3           ldi     r30, 0x37       ; 55
  9c:   f0 e0           ldi     r31, 0x00       ; 0
  9e:   8f ef           ldi     r24, 0xFF       ; 255
  a0:   80 83           st      Z, r24
  a2:   83 e0           ldi     r24, 0x03       ; 3
  a4:   0e 94 59 00     call    0xb2            ; 0xb2 <asmfunc_calledfrom_c>
  a8:   80 e0           ldi     r24, 0x00       ; 0
  aa:   90 e0           ldi     r25, 0x00       ; 0
  ac:   df 91           pop     r29
  ae:   cf 91           pop     r28
  b0:   08 95           ret

000000b2 <asmfunc_calledfrom_c>:
  b2:   88 bb           out     0x18, r24       ; 24
  b4:   08 95           ret

More Example Code

Consider the following slightly more complicated program:

// driver.c
#include <avr/io.h>
  
/**
  * Send value passed to function to PORTB
  *
  * Implemented in assembly
  *
  * @param val Value to be output to PORTB
  */
extern void send_to_portb_in_asm(uint8_t val);
  
/**
  * Divide value passed by 256 and return integer result.
  *
  * Implemented in assembly
  *
  * @param val Value to be divided by 256
  * @return integer result of val/256
  */
extern uint8_t divide_by_256_in_asm(uint16_t val);
  
/**
  * A completely useless function that accepts two arguments
  * and writes a value to PORTB based on the relative values
  * of the arguments passed.
  * If the first argument is larger, then the value of the
  * first argument is written to PORTB.  Otherwise, 0x00 is
  * written to PORTB.
  *
  * Implemented in assembly
  *
  * @param val Value to be acted upon
  * @param minus Value to be subtracted from val
  */
extern void strange_silliness_in_asm(uint8_t val, uint8_t minus);
  
/**
  * Main program that demonstrates calling assembly subroutines from
  * C functions.
  */
int main()
{
    DDRB = 0xff;
    send_to_portb_in_asm(3);
    PORTB = divide_by_256_in_asm(0x083f);
    strange_silliness_in_asm(32,16);
  
    return 0;
}
  
/**
  * Returns the absolute value of the arugment passed to the function.
  *
  * @param val A signed integer value
  * @return absolute value of the argument passed to the function
  */
uint8_t abs_in_c(int8_t val)
{
    if(val<0)
    {
        val *= -1;
    }
    return val;
}

and

// raw.s
#define __SFR_OFFSET 0          // Use 0 for the I/O register offset
#include <avr/io.h>             // Defines I/O port aliases
  
.global send_to_portb_in_asm    ; Makes send_to_portb_in_asm visible in other source files
.global divide_by_256_in_asm
.global strange_silliness_in_asm
  
.section .text                  ; Defines a code section
  
; Send value passed to subroutine to PORTB
send_to_portb_in_asm:           ; Start of asmfunc_calledfrom_c subroutine
    out  PORTB, r24             ; Send value passed to asmfunc_calledfrom_c to PORTB
    ret
  
; Return number passed to subroutine divided by 256
divide_by_256_in_asm:
    mov  r24, r25               ; Shift MSB into the LSB (divides by 256)
    ldi  r25, 0x00              ; Clear MSB
    ret
  
; Return number passed to subroutine divided by 256
strange_silliness_in_asm:
    sub  r24, r22               ; subtract minus (r22) from val (r24)
    mov  r22, r24               ; copy value in r24 into r22
    call abs_in_c               ; call c function that returns absolute value in r24
    sub  r24, r22               ; subtract val from abs(val)
    breq equal
    out  PORTB, 0x00
equal:
    out  PORTB, r22
    ret
00000092 <abs_in_c>:
  92:   87 fd           sbrc    r24, 7
  94:   81 95           neg     r24
  96:   90 e0           ldi     r25, 0x00       ; 0
  98:   08 95           ret

0000009a <main>:
  9a:   8f ef           ldi     r24, 0xFF       ; 255
  9c:   87 bb           out     0x17, r24       ; 23
  9e:   83 e0           ldi     r24, 0x03       ; 3
  a0:   0e 94 5e 00     call    0xbc            ; 0xbc <send_to_portb_in_asm>
  a4:   8f e3           ldi     r24, 0x3F       ; 63
  a6:   98 e0           ldi     r25, 0x08       ; 8
  a8:   0e 94 60 00     call    0xc0            ; 0xc0 <divide_by_256_in_asm>
  ac:   88 bb           out     0x18, r24       ; 24
  ae:   60 e1           ldi     r22, 0x10       ; 16
  b0:   80 e2           ldi     r24, 0x20       ; 32
  b2:   0e 94 63 00     call    0xc6            ; 0xc6 <strange_silliness_in_asm>
  b6:   80 e0           ldi     r24, 0x00       ; 0
  b8:   90 e0           ldi     r25, 0x00       ; 0
  ba:   08 95           ret

000000bc <send_to_portb_in_asm>:
  bc:   88 bb           out     0x18, r24       ; 24
  be:   08 95           ret

000000c0 <divide_by_256_in_asm>:
  c0:   89 2f           mov     r24, r25
  c2:   90 e0           ldi     r25, 0x00       ; 0
  c4:   08 95           ret

000000c6 <strange_silliness_in_asm>:
  c6:   86 1b           sub     r24, r22
  c8:   68 2f           mov     r22, r24
  ca:   0e 94 49 00     call    0x92            ; 0x92 <abs_in_c>
  ce:   86 1b           sub     r24, r22
  d0:   09 f0           breq    .+2             ; 0xd4 <equal>
  d2:   08 ba           out     0x18, r0        ; 24

000000d4 <equal>:
  d4:   68 bb           out     0x18, r22       ; 24
  d6:   08 95           ret

Passing Arguments with Stack

Earlier it was noted that the stack is used to store argument values if there isn't sufficient space in registers R25:8. The following example contains a function, stack_pass_example, which requires the stack to pass the last two arguments.

#include <avr/io.h>
  
/**
  * Example that uses the stack to pass arguments.
  *
  * Implemented in C
  *
  * @param val1 First long
  * @param val2 Second long
  * @param val3 Third long
  * @param val4 Fourth long
  * @param val5 Fifth long
  * @param val6 An eight bit integer
  * @return Nothing important
  */
long stack_pass_example(long val1, long val2, long val3, long val4, long val5, int8_t val6);
  
/**
  * Main program that demonstrates calling assembly subroutines from
  * C functions.
  */
int main()
{
    DDRB = stack_pass_example(1, 2, 3, 4, 5, 6);
  
    return 0;
}
  
/**
  * Example that uses the stack to pass arguments.
  *
  * @author t a y l o r@msoe.edu
  */
long stack_pass_example(long val1, long val2, long val3, long val4, long val5, int8_t val6)
{
    DDRB = val1;
    DDRB = val2;
    DDRB = val3;
    DDRB = val4;
    DDRB = val5;
    DDRB = val6;
    return val1;
}

Relevant portions of the .lss are found below:

00000092 <stack_pass_example>:
  92:   af 92           push    r10
  94:   bf 92           push    r11
  96:   cf 92           push    r12
  98:   df 92           push    r13
  9a:   ef 92           push    r14
  9c:   ff 92           push    r15
  9e:   0f 93           push    r16
  a0:   1f 93           push    r17
  a2:   cf 93           push    r28
  a4:   df 93           push    r29
  a6:   cd b7           in      r28, 0x3d       ; 61
  a8:   de b7           in      r29, 0x3e       ; 62
  aa:   67 bb           out     0x17, r22       ; 23
  ac:   27 bb           out     0x17, r18       ; 23
  ae:   e7 ba           out     0x17, r14       ; 23
  b0:   a7 ba           out     0x17, r10       ; 23
  b2:   2d 85           ldd     r18, Y+13       ; 0x0d
  b4:   27 bb           out     0x17, r18       ; 23
  b6:   29 89           ldd     r18, Y+17       ; 0x11
  b8:   27 bb           out     0x17, r18       ; 23
  ba:   df 91           pop     r29
  bc:   cf 91           pop     r28
  be:   1f 91           pop     r17
  c0:   0f 91           pop     r16
  c2:   ff 90           pop     r15
  c4:   ef 90           pop     r14
  c6:   df 90           pop     r13
  c8:   cf 90           pop     r12
  ca:   bf 90           pop     r11
  cc:   af 90           pop     r10
  ce:   08 95           ret

000000d0 <main>:
  d0:   af 92           push    r10
  d2:   bf 92           push    r11
  d4:   cf 92           push    r12
  d6:   df 92           push    r13
  d8:   ef 92           push    r14
  da:   ff 92           push    r15
  dc:   0f 93           push    r16
  de:   1f 93           push    r17
  e0:   86 e0           ldi     r24, 0x06       ; 6
  e2:   8f 93           push    r24
  e4:   85 e0           ldi     r24, 0x05       ; 5
  e6:   90 e0           ldi     r25, 0x00       ; 0
  e8:   a0 e0           ldi     r26, 0x00       ; 0
  ea:   b0 e0           ldi     r27, 0x00       ; 0
  ec:   bf 93           push    r27
  ee:   af 93           push    r26
  f0:   9f 93           push    r25
  f2:   8f 93           push    r24
  f4:   94 e0           ldi     r25, 0x04       ; 4
  f6:   a9 2e           mov     r10, r25
  f8:   b1 2c           mov     r11, r1
  fa:   c1 2c           mov     r12, r1
  fc:   d1 2c           mov     r13, r1
  fe:   83 e0           ldi     r24, 0x03       ; 3
 100:   e8 2e           mov     r14, r24
 102:   f1 2c           mov     r15, r1
 104:   01 2d           mov     r16, r1
 106:   11 2d           mov     r17, r1
 108:   22 e0           ldi     r18, 0x02       ; 2
 10a:   30 e0           ldi     r19, 0x00       ; 0
 10c:   40 e0           ldi     r20, 0x00       ; 0
 10e:   50 e0           ldi     r21, 0x00       ; 0
 110:   61 e0           ldi     r22, 0x01       ; 1
 112:   70 e0           ldi     r23, 0x00       ; 0
 114:   80 e0           ldi     r24, 0x00       ; 0
 116:   90 e0           ldi     r25, 0x00       ; 0
 118:   0e 94 49 00     call    0x92            ; 0x92 <stack_pass_example>
 11c:   67 bb           out     0x17, r22       ; 23
 11e:   0f 90           pop     r0
 120:   0f 90           pop     r0
 122:   0f 90           pop     r0
 124:   0f 90           pop     r0
 126:   0f 90           pop     r0
 128:   80 e0           ldi     r24, 0x00       ; 0
 12a:   90 e0           ldi     r25, 0x00       ; 0
 12c:   1f 91           pop     r17
 12e:   0f 91           pop     r16
 130:   ff 90           pop     r15
 132:   ef 90           pop     r14
 134:   df 90           pop     r13
 136:   cf 90           pop     r12
 138:   bf 90           pop     r11
 13a:   af 90           pop     r10
 13c:   08 95           ret

Tutorials for Other Courses

Additional Links

Site Mirror

This is a mirror to be used when http://myweb.msoe.edu/taylor/, (EDU instead of US) is not available.