Table of Contents

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