Write Arduino conditional branches in AVR ASM

Go To Last Post
22 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using my Arduino IDE and an Uno to learn the basics of AVR ASM.  So far have have some simple stuff down including conditional branching and so on.

 

But compound conditional branching I am not sure how to implement in assembler. All the conditional checks I see  for opcodes check 2 variables (registers, immediate, etc) at a time.  So to convert the Arduino snippet here into BRNE, CP and so on would involved nested conditional calls?

 

if (     (a < b && c > d)   ||  j >= 45 )

   {

   do something

   }

else

   {

   other thing

   }

 

 

This is not actual code, just an example.  I am trying to get my head around how to do it.

 

 

Thank you all in advance for any advice / examples / etc. 

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, first you compute the argument, then branch depending on whether the result is true or not. 

 

Actually, for me, it helps very little to propose a C "structure" then ask "how to do it in assembler" because, very often, the optimum way to do it often does not look very much like the way the problem is posed in the higher level language.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
if ((a < b && c > d)   ||  j >= 45 )   {  
       do something
   }  else {
     other thing
   }

This would PROBABLY come out something like:

test: cmp a,b      ; compare a and b
      brge testor   ; a >= b; try or clause.
      cmp c,d      ; compare c and d
      brg  ifcd     ;  a < b and c < d; go to the "if" clause
      ;; fall through to || test

testor: cpi j, 45    ; compare j with 45
      brge ifcd     ;   goto "if" clause.
      ;; fall through into else code

elscd:
     ;;  statement was false; execute the else code...
      ;; other thing
     rjmp testend

ifcd:
    ;; our test worked out true
     ;; do something

testend: ;; end of the whole if/else statement.

Assuming all of the variables are in registers and are 8bits long.  Note that this is theoretically NOT the way C does it, since theoretically C computes a single boolean value based on the expression in the if clause, rather than the string of conditional jumps.  OTOH, this might be what you get by the time the optimizing is done.

You DO know how to compile something with C and LOOK at the code it produces, don't you?

 

very often, the optimum way to do it often does not look very much like the way the problem is posed in the higher level language.

 Indeed, and ... exactly!

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One of the benefits with ASM is that you can evaluate in the order you want and that way speed the code up.

in your example it could be that j very often is above 45, so check it and go on. Perhaps it's  rare it above 45 then do it the other way around.   

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:
This would PROBABLY come out something like:
No need to guess the "probably" here:

C:\SysGCC\avr\bin>type avr.c
#include <avr/io.h>

uint8_t a,b,c,d,j;

int main(void) {
        if (     (a < b && c > d)   ||  j >= 45 )
    {
                PORTB = 0xAA;
    }

    else
    {
                PORTB = 0x55;
    }
}

C:\SysGCC\avr\bin>avr-gcc -mmcu=atmega16 -g -Os avr.c -o avr.elf

C:\SysGCC\avr\bin>avr-objdump -S avr.elf

avr.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0:   0c 94 2a 00     jmp     0x54    ; 0x54 <__ctors_end>
   4:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
   8:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
   c:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  10:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  14:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  18:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  1c:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  20:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  24:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  28:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  2c:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  30:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  34:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  38:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  3c:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  40:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  44:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  48:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  4c:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>
  50:   0c 94 3c 00     jmp     0x78    ; 0x78 <__bad_interrupt>

00000054 <__ctors_end>:
  54:   11 24           eor     r1, r1
  56:   1f be           out     0x3f, r1        ; 63
  58:   cf e5           ldi     r28, 0x5F       ; 95
  5a:   d4 e0           ldi     r29, 0x04       ; 4
  5c:   de bf           out     0x3e, r29       ; 62
  5e:   cd bf           out     0x3d, r28       ; 61

00000060 <__do_clear_bss>:
  60:   20 e0           ldi     r18, 0x00       ; 0
  62:   a0 e6           ldi     r26, 0x60       ; 96
  64:   b0 e0           ldi     r27, 0x00       ; 0
  66:   01 c0           rjmp    .+2             ; 0x6a <.do_clear_bss_start>

00000068 <.do_clear_bss_loop>:
  68:   1d 92           st      X+, r1

0000006a <.do_clear_bss_start>:
  6a:   a5 36           cpi     r26, 0x65       ; 101
  6c:   b2 07           cpc     r27, r18
  6e:   e1 f7           brne    .-8             ; 0x68 <.do_clear_bss_loop>
  70:   0e 94 3e 00     call    0x7c    ; 0x7c <main>
  74:   0c 94 55 00     jmp     0xaa    ; 0xaa <_exit>

00000078 <__bad_interrupt>:
  78:   0c 94 00 00     jmp     0       ; 0x0 <__vectors>

0000007c <main>:
#include <avr/io.h>

uint8_t a,b,c,d,j;

int main(void) {
        if (     (a < b && c > d)   ||  j >= 45 )
  7c:   90 91 64 00     lds     r25, 0x0064
  80:   80 91 60 00     lds     r24, 0x0060
  84:   98 17           cp      r25, r24
  86:   30 f4           brcc    .+12            ; 0x94 <main+0x18>
  88:   90 91 62 00     lds     r25, 0x0062
  8c:   80 91 63 00     lds     r24, 0x0063
  90:   89 17           cp      r24, r25
  92:   20 f0           brcs    .+8             ; 0x9c <main+0x20>
  94:   80 91 61 00     lds     r24, 0x0061
  98:   8d 32           cpi     r24, 0x2D       ; 45
  9a:   10 f0           brcs    .+4             ; 0xa0 <main+0x24>
    {
                PORTB = 0xAA;
  9c:   8a ea           ldi     r24, 0xAA       ; 170
  9e:   01 c0           rjmp    .+2             ; 0xa2 <main+0x26>
    }

    else
    {
                PORTB = 0x55;
  a0:   85 e5           ldi     r24, 0x55       ; 85
  a2:   88 bb           out     0x18, r24       ; 24
    }
}
  a4:   80 e0           ldi     r24, 0x00       ; 0
  a6:   90 e0           ldi     r25, 0x00       ; 0
  a8:   08 95           ret

000000aa <_exit>:
  aa:   f8 94           cli

000000ac <__stop_program>:
  ac:   ff cf           rjmp    .-2             ; 0xac <__stop_program>

Another way to explore this is to look at the source that the C compiler actually generated (rather than a disassembly of the object after the build):

C:\SysGCC\avr\bin>avr-gcc -mmcu=atmega16 -Os avr.c -o avr.elf -save-temps

C:\SysGCC\avr\bin>type avr.s
        .file   "avr.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
        .section        .text.startup,"ax",@progbits
.global main
        .type   main, @function
main:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
        lds r25,a
        lds r24,b
        cp r25,r24
        brsh .L2
        lds r25,c
        lds r24,d
        cp r24,r25
        brlo .L3
.L2:
        lds r24,j
        cpi r24,lo8(45)
        brlo .L4
.L3:
        ldi r24,lo8(-86)
        rjmp .L6
.L4:
        ldi r24,lo8(85)
.L6:
        out 0x18,r24
        ldi r24,0
        ldi r25,0
        ret
        .size   main, .-main
        .comm   j,1,1
        .comm   d,1,1
        .comm   c,1,1
        .comm   b,1,1
        .comm   a,1,1
        .ident  "GCC: (GNU) 5.3.0"
.global __do_clear_bss

and a way to enhance that is:

 

https://spaces.microchip.com/gf/...

 

(written by a genius apparently!) This yields:

C:\SysGCC\avr\bin>avr-gcc -mmcu=atmega16 -gdwarf-2 -Os avr.c -o avr.elf -save-temps

C:\SysGCC\avr\bin>avr-source.exe avr.s
file 1 = avr.c
file 2 = c:\\sysgcc\\avr\\avr\\include\\stdint.h

C:\SysGCC\avr\bin>type avr.source.s
        .file   "avr.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
        .text
.Ltext0:

        .section        .text.startup,"ax",@progbits
.global main
        .type   main, @function
main:
//==> int main(void) {
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
//==>   if (     (a < b && c > d)   ||  j >= 45 )
        lds r25,a
        lds r24,b
        cp r25,r24
        brsh .L2
//==>   if (     (a < b && c > d)   ||  j >= 45 )
        lds r25,c
        lds r24,d
        cp r24,r25
        brlo .L3
.L2:
//==>   if (     (a < b && c > d)   ||  j >= 45 )
        lds r24,j
        cpi r24,lo8(45)
        brlo .L4
.L3:
//==>           PORTB = 0xAA;
        ldi r24,lo8(-86)
        rjmp .L6
.L4:
//==>           PORTB = 0x55;
        ldi r24,lo8(85)
.L6:
        out 0x18,r24
//==> }
        ldi r24,0
        ldi r25,0
        ret
        .size   main, .-main
        .comm   j,1,1
        .comm   d,1,1
        .comm   c,1,1
        .comm   b,1,1
        .comm   a,1,1
        .text
.Letext0:

which gives you both the "compiler view" and intersperses it with C source code. Just one potential enhancement to take that one step further is the use of -fverbose-asm so:

C:\SysGCC\avr\bin>avr-gcc -mmcu=atmega16 -gdwarf-2 -Os avr.c -o avr.elf -save-temps -fverbose-asm

C:\SysGCC\avr\bin>avr-source.exe avr.s
file 1 = avr.c
file 2 = c:\\sysgcc\\avr\\avr\\include\\stdint.h

C:\SysGCC\avr\bin>type avr.source.s
        .file   "avr.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
 ;  GNU C11 (GCC) version 5.3.0 (avr)
 ;      compiled by GNU C version 4.7.3, GMP version 5.1.3, MPFR version 3.1.2, MPC version 1.0.2
 ;  GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
 ;  options passed:  -fpreprocessed avr.i -mn-flash=1 -mno-skip-bug
 ;  -mmcu=avr5 -gdwarf-2 -Os -fverbose-asm
 ;  options enabled:  -faggressive-loop-optimizations -falign-functions
 ;  -falign-jumps -falign-labels -falign-loops -fauto-inc-dec
 ;  -fbranch-count-reg -fcaller-saves -fchkp-check-incomplete-type
 ;  -fchkp-check-read -fchkp-check-write -fchkp-instrument-calls
 ;  -fchkp-narrow-bounds -fchkp-optimize -fchkp-store-bounds
 ;  -fchkp-use-static-bounds -fchkp-use-static-const-bounds
 ;  -fchkp-use-wrappers -fcombine-stack-adjustments -fcommon -fcompare-elim
 ;  -fcprop-registers -fcrossjumping -fcse-follow-jumps -fdefer-pop
 ;  -fdevirtualize -fdevirtualize-speculatively -fdwarf2-cfi-asm
 ;  -fearly-inlining -feliminate-unused-debug-types
 ;  -fexpensive-optimizations -fforward-propagate -ffunction-cse -fgcse
 ;  -fgcse-lm -fgnu-runtime -fgnu-unique -fguess-branch-probability
 ;  -fhoist-adjacent-loads -fident -fif-conversion -fif-conversion2
 ;  -findirect-inlining -finline -finline-atomics -finline-functions
 ;  -finline-functions-called-once -finline-small-functions -fipa-cp
 ;  -fipa-cp-alignment -fipa-icf -fipa-icf-functions -fipa-icf-variables
 ;  -fipa-profile -fipa-pure-const -fipa-ra -fipa-reference -fipa-sra
 ;  -fira-hoist-pressure -fira-share-save-slots -fira-share-spill-slots
 ;  -fisolate-erroneous-paths-dereference -fivopts -fkeep-static-consts
 ;  -fleading-underscore -flifetime-dse -flra-remat -flto-odr-type-merging
 ;  -fmath-errno -fmerge-constants -fmerge-debug-strings
 ;  -fmove-loop-invariants -fomit-frame-pointer -foptimize-sibling-calls
 ;  -fpartial-inlining -fpeephole -fpeephole2 -fprefetch-loop-arrays
 ;  -freg-struct-return -freorder-blocks -freorder-functions
 ;  -frerun-cse-after-loop -fsched-critical-path-heuristic
 ;  -fsched-dep-count-heuristic -fsched-group-heuristic -fsched-interblock
 ;  -fsched-last-insn-heuristic -fsched-rank-heuristic -fsched-spec
 ;  -fsched-spec-insn-heuristic -fsched-stalled-insns-dep -fschedule-fusion
 ;  -fsemantic-interposition -fshow-column -fshrink-wrap -fsigned-zeros
 ;  -fsplit-ivs-in-unroller -fsplit-wide-types -fssa-phiopt -fstdarg-opt
 ;  -fstrict-aliasing -fstrict-overflow -fstrict-volatile-bitfields
 ;  -fsync-libcalls -fthread-jumps -ftoplevel-reorder -ftrapping-math
 ;  -ftree-bit-ccp -ftree-builtin-call-dce -ftree-ccp -ftree-ch
 ;  -ftree-coalesce-vars -ftree-copy-prop -ftree-copyrename -ftree-dce
 ;  -ftree-dominator-opts -ftree-dse -ftree-forwprop -ftree-fre
 ;  -ftree-loop-if-convert -ftree-loop-im -ftree-loop-ivcanon
 ;  -ftree-loop-optimize -ftree-parallelize-loops= -ftree-phiprop
 ;  -ftree-pre -ftree-pta -ftree-reassoc -ftree-scev-cprop -ftree-sink
 ;  -ftree-slsr -ftree-sra -ftree-switch-conversion -ftree-tail-merge
 ;  -ftree-ter -ftree-vrp -funit-at-a-time -fvar-tracking
 ;  -fvar-tracking-assignments -fverbose-asm -fzero-initialized-in-bss

        .text
.Ltext0:

        .section        .text.startup,"ax",@progbits
.global main
        .type   main, @function
main:
//==> int main(void) {
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
//==>   if (     (a < b && c > d)   ||  j >= 45 )
        lds r25,a        ;  a, a
        lds r24,b        ;  b, b
        cp r25,r24       ;  a, b
        brsh .L2         ; ,
//==>   if (     (a < b && c > d)   ||  j >= 45 )
        lds r25,c        ;  c, c
        lds r24,d        ;  d, d
        cp r24,r25       ;  d, c
        brlo .L3         ; ,
.L2:
//==>   if (     (a < b && c > d)   ||  j >= 45 )
        lds r24,j        ;  j, j
        cpi r24,lo8(45)  ;  j,
        brlo .L4         ; ,
.L3:
//==>           PORTB = 0xAA;
        ldi r24,lo8(-86)         ;  tmp54,
        rjmp .L6         ;
.L4:
//==>           PORTB = 0x55;
        ldi r24,lo8(85)  ;  tmp56,
.L6:
        out 0x18,r24     ; , tmp56
//==> }
        ldi r24,0        ;
        ldi r25,0        ;
        ret
        .size   main, .-main
        .comm   j,1,1
        .comm   d,1,1
        .comm   c,1,1
        .comm   b,1,1
        .comm   a,1,1
        .text
.Letext0:

Anyway the point is that the C compiler is pretty good at writing AVR Asm. In fact it is generally so good that it's not usually worth bothering to do it yourself.

 

I guess that if you did all the above and found:

.global main
        .type   main, @function
main:
//==> int main(void) {
        push r28
        push r29
        in r28,__SP_L__
        in r29,__SP_H__
/* prologue: function */
/* frame size = 0 */
/* stack size = 2 */
.L__stack_usage = 2
//==>   if (     (a < b && c > d)   ||  j >= 45 )
        lds r25,a
        lds r24,b
        cp r25,r24
        brsh .L2
//==>   if (     (a < b && c > d)   ||  j >= 45 )
        lds r25,c
        lds r24,d
        cp r24,r25
        brlo .L3
.L2:
//==>   if (     (a < b && c > d)   ||  j >= 45 )
        lds r24,j
        cpi r24,lo8(45)
        brlo .L4
.L3:
//==>           PORTB = 0xAA;
        ldi r24,lo8(56)
        ldi r25,0
//==>           PORTB = 0xAA;
        ldi r18,lo8(-86)
        movw r30,r24
        st Z,r18
        rjmp .L5
.L4:
//==>           PORTB = 0x55;
        ldi r24,lo8(56)
        ldi r25,0
//==>           PORTB = 0x55;
        ldi r18,lo8(85)
        movw r30,r24
        st Z,r18
.L5:
        ldi r24,0
        ldi r25,0
/* epilogue start */
//==> }
        pop r29
        pop r28
        ret
        .size   main, .-main
.Letext0:

then you might start to think "actually, I can come up with something tighter than this C compiler".

 

(but in this case I simply "knobbled" the C compiler - I told it not to turn the optimiser on!)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AVR_Beginner wrote:
I am using my Arduino IDE and an Uno to learn the basics of AVR ASM. 

I'm sure it's been said elsewhere - but why choose the Arduino IDE to do this? This is certainly not what it's designed for.

 

 

Anyhow...

I am trying to get my head around how to do it.

 

As clawson has shown, looking at the way the 'C' compiler does it is always a good starting point!

 

The thing to remember about asm is that each instruction does only one very simple operation:

  • one addition of two arguments
  • test one condition
  • move 1 byte
  • etc ...

 

You need to study the Instruction Set document to see what instructions are available, and what they do.

To be an asm programmer, you will need to be very familiar with that document!

 

So, when starting with something like

if (     (a < b && c > d)   ||  j >= 45 )
{
   do something
}
else
{
   other thing
} 

 

Your first step is to break down those complex expressions into a series of simple expressions.

You could do that in 'C', if you find it helps, or as a flowchart - whatever works for you.

 

 

EDIT

 

Of course, people have been programming AVRs in assembler for decades - so there is no shortage of books & tutorials on the subject

 

eg, http://www.avrfreaks.net/forum/books-atmel-avr-assembler

 

http://www.lmgtfy.com?q=AVR+assmebler+book

Last Edited: Sat. Dec 2, 2017 - 12:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you everyone!  I mean that with all honesty.

 

Lost of good examples for me to digest! Once my kid gets back from his play date I am going to barricade my self in the basement and digest it.  Thank you again!

 

 

I am using the Arduino IDE and set up because it it is stable and already running and I am familiar with it.  I tried to get avr-gcc , avr-dude and AS7 to work and spent a day trying to update drivers and intrepret error messages.  So for the sake of just learning the basics: using the existing Arduino IDE that I already have seems to be the way to go.  With the noted exception of the help I recieve here from all of you I am learning alone out of books, internet, etc.  Thus I am grateful for all the help!!

 

Thank you again!  I have a lot to read through today :-)

 

 

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

But why not AS7? If you use that you even get to choose between two different assemblers - the one you are currently using and the one that all the books and internet articles are written about.
.
The main reason to use your current one (GNU as) is usually for interworking C and Asm together.

Last Edited: Sat. Dec 2, 2017 - 02:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
Your first step is to break down those complex expressions into a series of simple expressions.

You could do that in 'C',

 

One thing to note: in 'C', the use of goto is strongly deprecated.

 

But, in assembler, the use of goto is going to (sic) be essential!

 

eg, taking something like

if( this && that )
{
    whatever;
}

is equivalent to

if( this )
{
    if( that )
    {
        whatever;
    }
}

which helps with your break-down from 'C' towards assembler.

 

But something like

if( this || that )
{
    whatever;
}

would have to become something like

if( this ) goto do_whatever;
if( !that ) goto whatevers_next:

do_whatever:
    whatever;
    
whatevers_next:
   etc
   :
   :

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would suggest that thinking in C and then trying to convert that to assembly is the wrong way to go a about it.

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

True that. It's unlikely you will out think a C compiler. Really good Asm code comes from using intricate CPU knowledge that cannot be encoded into an automated code generator like a compiler.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Brian Fairchild wrote:
I would suggest that thinking in C and then trying to convert that to assembly is the wrong way to go a about it.

I think I agree.

 

But the OP started by showing some C-like "pseudo-code" - so my point is that (s)he's going to have to break that down into simple steps.

 

Flowcharts may be more appropriate.

 

A good supply of pencils & paper will be necessary - with a good eraser & sharpener.

 

clawson wrote:
Really good Asm code comes from using intricate CPU knowledge

Absolutely.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

looking at the way the 'C' compiler does it is always a good starting point!

thinking in C and then trying to convert that to assembly is the wrong way to go a about it.

 Those two comments, both of which have been made by multiple people, sort-of contradict each other.  That sort of doublethink is  not uncommon for assembly language programmers :-)

 

You can get much shorter, but slightly obscured, assembler code out of C by compiling just the module you're interested in and using objdump on the .o file:

 

BillW-MacOSX-2<4997> cat ifs.c
extern void something(), otherthing();

void test(char a, char b, char c, char d, char j) {
    if (     (a < b && c > d)   ||  j >= 45 )
    {
    something();
    } else {
    otherthing();
    }
}
BillW-MacOSX-2<4998> avr-gcc -Og -g -mmcu=atmega8 -c ifs.c
BillW-MacOSX-2<4999> avr-objdump -S ifs.o

ifs.o:     file format elf32-avr

Disassembly of section .text:

00000000 <test>:
extern void something(), otherthing();

void test(char a, char b, char c, char d, char j) {
   0:    0f 93           push    r16
    if (     (a < b && c > d)   ||  j >= 45 )
   2:    86 17           cp    r24, r22
   4:    04 f4           brge    .+0          ; 0x6 <test+0x6>
   6:    24 17           cp    r18, r20
   8:    04 f0           brlt    .+0          ; 0xa <test+0xa>
   a:    0d 32           cpi    r16, 0x2D    ; 45
   c:    04 f0           brlt    .+0          ; 0xe <test+0xe>
    {
    something();
   e:    00 d0           rcall    .+0          ; 0x10 <test+0x10>
  10:    00 c0           rjmp    .+0          ; 0x12 <test+0x12>
    } else {
    otherthing();
  12:    00 d0           rcall    .+0          ; 0x14 <test+0x14>
    }
}
  14:    0f 91           pop    r16
  16:    08 95           ret

Note that you can't quite see which way the jumps are going, but the logic is pretty clear, and you don't have to look at all the boilerplate added to make a full-fledged C executable.  (There may be some magic compile/link option to "partially resolve" the jump addresses, but it would probably take some searching to find.)

 

 

in assembler, the use of goto is going to (sic) be essential!

Well, you can implement assembler macros that implement less goto-like structures (sort of like a compiler, only simpler.)

https://github.com/WestfW/struct...

   cpi r16, 'a'
   _if e
     call sub_for_a
   _elseif <cpi r16, 'b'>, e
     call sub_for_b
   _elseif <cpi r16, 'c'>, e
     call sub_for_c
   _else
     call badcmd
   _endif

 

(On the third hand, when you're writing in assembly language, it can be a bad idea to start using macro packages that are not likely to be familiar to other programmers using the same chip.)

 

Last Edited: Sun. Dec 3, 2017 - 02:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sort-of contradict each other.

Then I don't think you get it!

 

When you are new to an controller (or totally new to this ASM), then it's good to see a way that works, simple things like compare 16 bit, read from flash, set pointers up and all those things,  could be a good starting point.

But in general the C structure is very rare optimal for the hole project and that is why you can gain a lot more than the normal 5-20% buy optimizing the C output with a better structure (for the job on hand) in ASM.

 

And most people only think of ASM for raw speed, but sometimes it's better because of other things like smaller code, less RAM use, faster to program for jobs close to HW.

Last Edited: Sun. Dec 3, 2017 - 06:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:
sometimes it's better because of other things like [..] faster to program

Here comes that war again.. ;-)

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry I can see that I wrote could mean that, so I have reworded my sentence.

 

No this was not to make a war just give some info to OP. (that he wouldn't get from C people)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:

One of the benefits with ASM is that you can evaluate in the order you want and that way speed the code up.

in your example it could be that j very often is above 45, so check it and go on. Perhaps it's  rare it above 45 then do it the other way around.   

You can do the same in C:

if (j >= 45 || (a < b && c > d))

The first part (j >= 45) will be evaluated first and the second part will NOT be evaluated if the first part is true.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But tomorrow (next release, or you change variables use) the compiler might do the evaluation in an other order! (you have not guaranty for the order) 

Last Edited: Mon. Dec 4, 2017 - 06:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But tomorrow (next release, or you change variables use) the compiler might do the evaluation in an other order! (you have not guaranty for the order) 

Uh, no.  That behaviour is part of the standard.  The operators &&, || and ?: are all 'short-circuit' operators.  Operands are evaluated left-to-right (subject to operator precedence, of course), and as soon as an operand evaluates to a value which makes the remaining operand irrelevant, operand evaluation stops.

 

In @christop's example:

if (j >= 45 || (a < b && c > d))

... if the first operand j >= 45 evaluates as true, then the || operator's other operand is irrelevant to the result, and is therefore not evaluated.

 

 

 

 

The && operator is similar:

 

 

And the ternary conditional operator ?:

 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Mon. Dec 4, 2017 - 07:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes... and I am still learning to read what it all says.  When I see my own code in it from avr-objdump it is obvious, but I am learning what all the other sections and what they do and how... so but steady going   for now.

 

 

westfw wrote:

if ((a < b && c > d)   ||  j >= 45 )   {
       do something
   }  else {
     other thing
   }

This would PROBABLY come out something like:

test: cmp a,b      ; compare a and b
      brge testor   ; a >= b; try or clause.
      cmp c,d      ; compare c and d
      brg  ifcd     ;  a < b and c < d; go to the "if" clause
      ;; fall through to || test

testor: cpi j, 45    ; compare j with 45
      brge ifcd     ;   goto "if" clause.
      ;; fall through into else code

elscd:
     ;;  statement was false; execute the else code...
      ;; other thing
     rjmp testend

ifcd:
    ;; our test worked out true
     ;; do something

testend: ;; end of the whole if/else statement.

Assuming all of the variables are in registers and are 8bits long.  Note that this is theoretically NOT the way C does it, since theoretically C computes a single boolean value based on the expression in the if clause, rather than the string of conditional jumps.  OTOH, this might be what you get by the time the optimizing is done.

You DO know how to compile something with C and LOOK at the code it produces, don't you?

 

very often, the optimum way to do it often does not look very much like the way the problem is posed in the higher level language.

 Indeed, and ... exactly!

 

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfa:

 

 

The branching at the end of your post is very similar to what i wound up doing after all. So.. I feel good knowing i am very close to the "right" way.  I have tested it with all sorts of values for the 4 params and seems to work.

 

Thank you westfa and thank you everyone else :-)

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

That is exactly where I am and how I am learning :-)

 

 

sparrow2 wrote:
When you are new to an controller (or totally new to this ASM), then it's good to see a way that works, simple things like compare 16 bit, read from flash, set pointers up and all those things,  could be a good starting point.

I am a new AVR programmer. I am learning alone out of books, the Internet, etc. Please excuse me if I ask simple questions. Thanks.