Search code examples
assemblydelayavr

Assembler loop does not execute properly


I've created a simple assembler subroutine (asmFunction) that is supposed to toggle the output of PORTD every 10 ms.

Here is the assembler source code :

#define __SFR_OFFSET 0
#include <avr/io.h>

.section .text      ; Defines a code section


;initialize
initialize:
    ldi r24,0xFF    ; set 11111111 value to the r24 register
    out DDRD,r24    ; put r24 register value into DDRD register
    ldi r24,0x00    ; set the r24 register to 00000000 value
    out PORTD,r24   ; put the r24 register value to PORTD
    ret             ; return from a branch

;wait 100uS
subdelay:
    ldi r23,99      ; set register r23 to 99
    loop:
        dec r23     ; 1 cycle, decrement r23
        cpi r23,0   ; 1 cycle, compare r23 and 0
        ;nop to get exactly 16 clock cycles per loop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        brne loop   ; 2 cycles, if r23 not eqal to 0, continue the loop
    ;wait a bit more (11 cycles to get exactly 100uS of delay
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    ret             ; return from branch

;delay x/10 ms (R22)
delay:
    ldi r23,100     ; set delay to 100x100uS
    loop1:
        dec r23     ; decrement r23
        cpi r23,0   ; compare r23 to 0
        call subdelay; call subdelay routine
        brne loop1  ; if r23 is not equal to 0, continue the loop
ret                 ; otherwise return from the delay branch

.global asmFunction ; makes asm function visible in other source files

asmFunction:        ; Start of asmfunc_calledfrom_c subroutine
    call initialize ; call initialize branch
    while:          ; main loop
        ;turn output high
        ldi r24,0xFF
        out PORTD,r24

        ;delay 10mS
        call delay

        ;turn output low
        ldi r24,0x00
        out PORTD,r24

        ;delay 10 ms
        call delay

        rjmp while  ; jump to while (for loop to continue existing)

main.c :

#include "avr/io.h"

void asmFunction();

int main()
{
    asmFunction();
    return 0;
}

And here is disassembled hex file :

Atmega328p_test.elf:     file format elf32-avr

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .data         00000000  00800100  000000f4  00000168  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  1 .text         000000f4  00000000  00000000  00000074  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .comment      00000011  00000000  00000000  00000168  2**0
                  CONTENTS, READONLY
  3 .note.gnu.avr.deviceinfo 00000040  00000000  00000000  0000017c  2**2
                  CONTENTS, READONLY

Disassembly of section .text:

00000000 <__vectors>:
   0:   0c 94 34 00     jmp 0x68    ; 0x68 <__ctors_end>
   4:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
   8:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
   c:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  10:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  14:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  18:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  1c:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  20:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  24:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  28:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  2c:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  30:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  34:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  38:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  3c:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  40:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  44:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  48:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  4c:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  50:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  54:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  58:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  5c:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  60:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>
  64:   0c 94 3e 00     jmp 0x7c    ; 0x7c <__bad_interrupt>

00000068 <__ctors_end>:
  68:   11 24           eor r1, r1
  6a:   1f be           out 0x3f, r1    ; 63
  6c:   cf ef           ldi r28, 0xFF   ; 255
  6e:   d8 e0           ldi r29, 0x08   ; 8
  70:   de bf           out 0x3e, r29   ; 62
  72:   cd bf           out 0x3d, r28   ; 61
  74:   0e 94 73 00     call    0xe6    ; 0xe6 <main>
  78:   0c 94 78 00     jmp 0xf0    ; 0xf0 <_exit>

0000007c <__bad_interrupt>:
  7c:   0c 94 00 00     jmp 0   ; 0x0 <__vectors>

00000080 <initialize>:
  80:   8f ef           ldi r24, 0xFF   ; 255
  82:   8a b9           out 0x0a, r24   ; 10
  84:   80 e0           ldi r24, 0x00   ; 0
  86:   8b b9           out 0x0b, r24   ; 11
  88:   08 95           ret

0000008a <subdelay>:
  8a:   73 e6           ldi r23, 0x63   ; 99

0000008c <loop>:
  8c:   7a 95           dec r23
  8e:   70 30           cpi r23, 0x00   ; 0
    ...
  a8:   89 f7           brne    .-30        ; 0x8c <loop>
    ...
  be:   00 00           nop
  c0:   08 95           ret

000000c2 <delay>:
  c2:   74 e6           ldi r23, 0x64   ; 100

000000c4 <loop1>:
  c4:   7a 95           dec r23
  c6:   70 30           cpi r23, 0x00   ; 0
  c8:   0e 94 45 00     call    0x8a    ; 0x8a <subdelay>
  cc:   d9 f7           brne    .-10        ; 0xc4 <loop1>
  ce:   08 95           ret

000000d0 <asmFunction>:
  d0:   0e 94 40 00     call    0x80    ; 0x80 <initialize>

000000d4 <while>:
  d4:   8f ef           ldi r24, 0xFF   ; 255
  d6:   8b b9           out 0x0b, r24   ; 11
  d8:   0e 94 61 00     call    0xc2    ; 0xc2 <delay>
  dc:   80 e0           ldi r24, 0x00   ; 0
  de:   8b b9           out 0x0b, r24   ; 11
  e0:   0e 94 61 00     call    0xc2    ; 0xc2 <delay>
  e4:   f7 cf           rjmp    .-18        ; 0xd4 <while>

000000e6 <main>:
  e6:   0e 94 68 00     call    0xd0    ; 0xd0 <asmFunction>
  ea:   80 e0           ldi r24, 0x00   ; 0
  ec:   90 e0           ldi r25, 0x00   ; 0
  ee:   08 95           ret

000000f0 <_exit>:
  f0:   f8 94           cli

000000f2 <__stop_program>:
  f2:   ff cf           rjmp    .-2         ; 0xf2 <__stop_program>

The problem is that when I'm running this code on my atmega328p microcontroler, it produces the signal with the period of 200 uS. It seems that the subdelay subroutine executes normally and produces 100uS of delay but for some reason loop1 inside delay subroutine does not work which cause the subdelay subroutine to be executed only ones.

The question is : what am I doing wrong?


Solution

  • You use r23 inside both delay and subdelay. You need to save it’s value before calling subdelay and restore it afterwards, either with a push and a pop, or storing it in a specific memory location.