pointer = &&label

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

    Hello,

 

    This is my first post regarding C programming, so bear with me.

 

    I read that GCC has an extension so that one could find out a label's address. This could be used with a GOTO function in order to jump to variable locations in code. I did install WinAVR and it complains that a 'stray &' is found when I use "pointer = &&label".

 

    My question is, does WinAVR implementation supports this feature ? I checked IAR and Arduino, none of them does. And if it does not, there is a way to get around this ? (I mean still using GOTO with variable landing points). Thanks.

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

angelu wrote:
    This is my first post regarding C programming

It is a very strange thing to be asking as your first question!!

 

surprise

 

Almost certainly, there will be better ways to achieve whatever it is that you're trying to do.

 

Please try explaining the actual goal - then people can suggest appropriate ways to reach that goal in 'C' (or point out why 'C' is not an appropriate tool for the job).

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

    I want to implement a simple cooperative task/thread switching scheme.

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

angelu wrote:
I want to implement a simple cooperative task/thread switching scheme.

 

Perhaps it might be useful to have a label here-and-there.  However I'd guess that most cooperative task-switching examples don't use labels, but rather the task is a function.  When it yields, it calls the switcher and the switcher grabs the return address off the stack.  To go back, the return address is put back on the stack and RET...

 

This simple approach doesn't even fuss with the stack at all:

https://www.avrfreaks.net/forum/t...

 

... but at least the Part 1 doesn't have the concept of "yield", just "done".

 

Neither does this one: https://www.avrfreaks.net/forum/p...

 

Nor does:

https://www.google.com/url?sa=t&...

 

http://www.electro-tech-online.c...

 

FreeRTOS is probably the "gold standard" to use as a reference:

http://www.freertos.org/RTOS.html

That does indeed do context saving and stack manipulation in co-operative AFAIK.

 

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Tue. Dec 15, 2015 - 09:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
However I'd guess that most cooperative task-switching examples don't use labels, but rather the task is a function.

+1

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

angelu wrote:
I did install WinAVR and it complains that a 'stray &' is found when I use "pointer = &&label".

There is another current thread where someone is starting out with "WinAVR".  And the responses are almost totally:  "Why are you starting out using an obsolete toolchain?"

 

That said, show the context of your stray.  Ideally a complete test program.  The docs at https://gcc.gnu.org/onlinedocs/g... seem to indicate it is allowed...

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

"Why are you starting out using an obsolete toolchain?"

 

I just installed Studio 7, so is this toolchain obsolete ? And regardless, what I have to do in order to be able to use that label stuff on (x)mega processors ?

 

Thank you.

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

angelu wrote:
I just installed Studio 7, so is this toolchain obsolete ?

But you said WinAVR in the OP.

 

angelu wrote:
And regardless, what I have to do in order to be able to use that label stuff on (x)mega processors ?
theusch wrote:
...show the context of your stray. Ideally a complete test program.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

There are two issues here:

How can OP accomplish the stated goal?

Why did OP's first attempt not compile?

 

That OP's first attempt was a poor design choice does not

imply that the ensuing question should not be answered.

I expect the answer is that OP's IDE, whatever it is,

uses an old version of avr-gcc .

"Demons after money.
Whatever happened to the still beating heart of a virgin?
No one has any standards anymore." -- Giles

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

OK, first, my apologize, it did not compile because I had in my code $ instead &. Now that it compiles, it still does not work properly. It just jumps to arbitrary locations in the code.

 

Here is my example code. There are two tasks in it, t1 and t2. Each task has various points were it yields control to the other task:

#include <avr/io.h>

void * label_address1;
void * label_address2;
uint8_t a;
uint8_t b;

int main(void)
{
	label_address1 = &&t1A;
	label_address2 = &&t2A;

	goto *label_address1;
	
	// task1 start
	t1A:
	    a++; // some code
		label_address1 = &&t1B;
		goto *label_address2;

	t1B:
	    a = a+b;
		label_address1 = &&t1C;
		goto *label_address2;
	t1C:
	    a = a+b;
	    a = a+b;
		label_address1 = &&t1A;
		goto *label_address2;



	// task2 start
	t2A:
		b = a-b;
		label_address2 = &&t2B;
		goto *label_address1;
	t2B:
	    b = a-b;
		label_address2 = &&t2C;
		goto *label_address1;
	t2C:
	    b = a-b;
		label_address2 = &&t2A;
		goto *label_address1;
}

George.

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

Actually I have not seen more ridicolous way of programming in C. What is the goal of using such a weird language constructions?

Task switching is something much more complex than just jumping to place in the code.  (priorities, messages, heap, deadlocks, timer & resource sharing and much, much more

 

Take a look on freeRTOS or quite a new one DISORTOS which is C++ optimized one.

 

Your example is too trivial all "tasks" do exactly the same - in real task switching you need to guarantee some time of execution to all of them. In your approach task is executed until it gives back the controll. it will never work, as the programmer cant know how often the control will be given back to a particular task, and how long execution of the task will take.

Last Edited: Wed. Dec 16, 2015 - 02:04 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To potential contributors: I do have a clear and exact question and issue to solve. I am asking if someone could help me to implement this goto scheme. Why like this, why not the other way is out of topic. When I see that someone replied, I would like to read something useful. Otherwise, just leave this thread unanswered.

 

Thank you.

 

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

Unfortunately, the code appears strange.

I'd suggest you run the code in the simulator to observe what you have coded. The label is an address, just like a function is an address, so why the address of the address?

That said, i'd suggest you look at setjump()

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

Why wouldn't you simply write that as:

#include <avr/io.h>

uint8_t a;
uint8_t b;

void t1A(void) {
    a++;
}

void t1B(void) {
    a = a + b;
}

void t1C(void) {
    a = a + b;
    a = a + b;
}

void t2A(void) {
    b = a - b;
}

void t2B(void) {
    b = a - b;
}

void t2C(void) {
    b = a - b;
}

int main(void) {
    while(1) {
        t1A();
        t2A();
        t1B();
        t2B();
        t1C();
        t2C();
    }
}

or a possibly more elegant way:

#include <avr/io.h>

uint8_t a;
uint8_t b;

typedef enum {
    A,
    B,
    C
} state_t;

void task_A(void) {
    static state_t state = A;
    switch(state) {
        case A:
            a++;
            state = B;
            break;
        case B:
            a = a + b;
            state = C;
            break;
        case C:
            a = a + b;
            a = a + b;
            state = A;
            break;
    }
}


void task_B(void) {
    static state_t state = A;
    switch(state) {
        case A:
            b = a - b;
            state = B;
            break;
        case B:
            b = a - b;
            state = C;
            break;
        case C:
            b = a - b;
            state = A;
            break;
    }
}


int main(void) {
    while(1) {
        task_A();
        task_B();
    }
}

What you appear to be attempting at present is to apply an Asm programmers mindset to a C solution. While you might ultimately achieve that it's a very rocky road and any high-level language that involves any use of goto is a maintenance nightmare. Indeed I had to really sit and think about your label_address1/2 there to try and work out what your intended flow of execution was. I hope you read my examples he and it is instantly obvious exactly what the flow of execution is to any programmer who comes to this code. I particularly like the second option and is probably how I would implement such a co-operating task system where each task function "does the next bit" each time it is called.

 

(you will be relieved to hear that "under the hood" the C compiler will indeed by RJMPing (or rather BRNE'ing) all over the place to achieve what I wrote in task_A() and task_B() so there are "goto"s - it's just you cannot see them at the high level ;-)

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

clawson wrote:
What you appear to be attempting at present is to apply an Asm programmers mindset to a C solution.

Can you moderate yourself out here, Cliff? ;-) You know Angelu didn't want to get that answer...

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

I'll vote with Johan, pretty much.  No, I don't think I'd use that mechanism for RTOS-like facilities.  But I might well use something like that in, say, a peripheral driver that had options--probably keeps it skinnier than function pointers?

 

So can we help to solve the "doesn't work" issue?  Indeed, a simulator run should tell why

angelu wrote:
It just jumps to arbitrary locations in the code.

 

Perhaps OP can simplify even further, and cut down to say one pointer and two cases and move back-and-forth from section to section. 

 

Or -- do this first:  make a and b volatile.  As things are shown, won't the optimizer tie itself into knots for a while, and then decide that the code does nothing but Jump Around ? https://www.youtube.com/watch?v=...

 

 

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

It works with no problems - the problem is that yoursimple code is to trivial and is actualy removed by the compiler, if you have any opimisation enabled. Disable the optimisation and your example will work.

 

0000004A  PUSH R28		Push register on stack 
0000004B  PUSH R29		Push register on stack 
0000004C  IN R28,0x3D		In from I/O location 
0000004D  IN R29,0x3E		In from I/O location 
	label_address1 = &&t1A;
0000004E  LDI R24,0x5F		Load immediate 
0000004F  LDI R25,0x00		Load immediate 
00000050  STS 0x0104,R25		Store direct to data space 
00000052  STS 0x0103,R24		Store direct to data space 
	label_address2 = &&t2A;
00000054  LDI R24,0x9C		Load immediate 
00000055  LDI R25,0x00		Load immediate 
00000056  STS 0x0102,R25		Store direct to data space 
00000058  STS 0x0101,R24		Store direct to data space 
	goto *label_address1;
0000005A  LDS R24,0x0103		Load direct from data space 
0000005C  LDS R25,0x0104		Load direct from data space 
0000005E  RJMP PC+0x0010		Relative jump 
	a++; // some code
0000005F  LDS R24,0x0105		Load direct from data space 
--- E:\avr\tests\test\test\Debug/.././main.c -----------------------------------
00000061  SUBI R24,0xFF		Subtract immediate 
00000062  STS 0x0105,R24		Store direct to data space 
	label_address1 = &&t1B;
00000064  LDI R24,0x71		Load immediate 
00000065  LDI R25,0x00		Load immediate 
00000066  STS 0x0104,R25		Store direct to data space 
00000068  STS 0x0103,R24		Store direct to data space 
	goto *label_address2;
0000006A  LDS R24,0x0101		Load direct from data space 
0000006C  LDS R25,0x0102		Load direct from data space 
0000006E  PUSH R24		Push register on stack 
0000006F  PUSH R25		Push register on stack 
00000070  RET 		Subroutine return 
	a = a+b;
00000071  LDS R25,0x0105		Load direct from data space 
00000073  LDS R24,0x0100		Load direct from data space 
00000075  ADD R24,R25		Add without carry 
00000076  STS 0x0105,R24		Store direct to data space 
	label_address1 = &&t1C;
00000078  LDI R24,0x83		Load immediate 
00000079  LDI R25,0x00		Load immediate 
0000007A  STS 0x0104,R25		Store direct to data space 
0000007C  STS 0x0103,R24		Store direct to data space 
	goto *label_address2;
0000007E  LDS R24,0x0101		Load direct from data space 
00000080  LDS R25,0x0102		Load direct from data space 
00000082  RJMP PC-0x0014		Relative jump 
	a = a+b;
00000083  LDS R25,0x0105		Load direct from data space 
00000085  LDS R24,0x0100		Load direct from data space 
00000087  ADD R24,R25		Add without carry 
00000088  STS 0x0105,R24		Store direct to data space 
	a = a+b;
0000008A  LDS R25,0x0105		Load direct from data space 
0000008C  LDS R24,0x0100		Load direct from data space 
0000008E  ADD R24,R25		Add without carry 
0000008F  STS 0x0105,R24		Store direct to data space 
--- E:\avr\tests\test\test\Debug/.././main.c -----------------------------------
	label_address1 = &&t1A;
00000091  LDI R24,0x5F		Load immediate 
00000092  LDI R25,0x00		Load immediate 
00000093  STS 0x0104,R25		Store direct to data space 
00000095  STS 0x0103,R24		Store direct to data space 
	goto *label_address2;
00000097  LDS R24,0x0101		Load direct from data space 
00000099  LDS R25,0x0102		Load direct from data space 
0000009B  RJMP PC-0x002D		Relative jump 
	b = a-b;
0000009C  LDS R25,0x0105		Load direct from data space 
0000009E  LDS R24,0x0100		Load direct from data space 
000000A0  MOV R18,R25		Copy register 
000000A1  SUB R18,R24		Subtract without carry 
000000A2  MOV R24,R18		Copy register 
000000A3  STS 0x0100,R24		Store direct to data space 
	label_address2 = &&t2B;
000000A5  LDI R24,0xB0		Load immediate 
000000A6  LDI R25,0x00		Load immediate 
000000A7  STS 0x0102,R25		Store direct to data space 
000000A9  STS 0x0101,R24		Store direct to data space 
	goto *label_address1;
000000AB  LDS R24,0x0103		Load direct from data space 
000000AD  LDS R25,0x0104		Load direct from data space 
000000AF  RJMP PC-0x0041		Relative jump 
	b = a-b;
000000B0  LDS R25,0x0105		Load direct from data space 
000000B2  LDS R24,0x0100		Load direct from data space 
000000B4  MOV R18,R25		Copy register 
000000B5  SUB R18,R24		Subtract without carry 
000000B6  MOV R24,R18		Copy register 
000000B7  STS 0x0100,R24		Store direct to data space 
	label_address2 = &&t2C;
000000B9  LDI R24,0xC4		Load immediate 
000000BA  LDI R25,0x00		Load immediate 
000000BB  STS 0x0102,R25		Store direct to data space 
000000BD  STS 0x0101,R24		Store direct to data space 
--- E:\avr\tests\test\test\Debug/.././main.c -----------------------------------
	goto *label_address1;
000000BF  LDS R24,0x0103		Load direct from data space 
000000C1  LDS R25,0x0104		Load direct from data space 
000000C3  RJMP PC-0x0055		Relative jump 
	b = a-b;
000000C4  LDS R25,0x0105		Load direct from data space 
000000C6  LDS R24,0x0100		Load direct from data space 
000000C8  MOV R18,R25		Copy register 
000000C9  SUB R18,R24		Subtract without carry 
000000CA  MOV R24,R18		Copy register 
000000CB  STS 0x0100,R24		Store direct to data space 
	label_address2 = &&t2A;
000000CD  LDI R24,0x9C		Load immediate 
000000CE  LDI R25,0x00		Load immediate 
000000CF  STS 0x0102,R25		Store direct to data space 
000000D1  STS 0x0101,R24		Store direct to data space 
	goto *label_address1;
000000D3  LDS R24,0x0103		Load direct from data space 
000000D5  LDS R25,0x0104		Load direct from data space 
000000D7  RJMP PC-0x0069		Relative jump 
--- No source file -------------------------------------------------------------
000000D8  CLI 		Global Interrupt Disable 
000000D9  RJMP PC-0x0000		Relative jump 
000000DA  SBIW R24,0x01		Subtract immediate from word 
000000DB  BRNE PC-0x01		Branch if not equal 
000000DC  RJMP PC+0x0001		Relative jump 
000000DD  NOP 		No operation 
000000DE  LDI R22,0x00		Load immediate 
000000DF  LDI R24,0x0C		Load immediate 
000000E0  CALL 0x00000083		Call subroutine 
000000E2  LDI R24,0x3F		Load immediate 
000000E3  LDI R25,0x9C		Load immediate 
000000E4  SBIW R24,0x01		Subtract immediate from word 

 

 

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

PS Just use as up to date toolchain as possible
 

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

What you appear to be attempting at present is to apply an Asm programmers mindset to a C solution.

    Yes, something like that.

    To be a more clear with my example, I expand a bit the first case:

	t1A:
	    if PINA.0 = 0
	    {
	    // do something if pin0 in PORTA = 0 and then next task time land to t1B
		label_address1 = &&t1B;
	    }
	    else
	    {
	    a++; // if pin still 1, increment a and next task time still land at t1A (just because I did not update the "landing" address pointer)
	    }
		goto *label_address2;

	t1B:

While task t1 is stuck in one place waiting for an external event or a flag to be set, other tasks can advance at their own pace.

 

Clawson, your first example won't work because the flow is fixed. The second example with switches is exactly what I am looking for. I just think if I mix a bit of asm and C, will get things move faster. Nevertheless, thank you.

 

Kartman, I have it working in asm:

t1A:
    // do something
    
    // define where to land next task time.
    LDI ZL, LOW(t1B)
    STS label_adress1, ZL
    LDI ZL, HIGH(t1B)
    STS label_adress1 + 1, ZL
    
    // land to
    LDS ZL, label_address2
    LDS ZH, label_address2 + 1
    IJMP

t1B:

It switches tasks in around 14 clocks.

 

Theusch: I will take a look at the listing file on the weekend.

 

George.

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

Clawson, your first example won't work because the flow is fixed.

The flow is fixed in your code too! My examples will follow the same path of execution as yours as far as the 'a's and 'b's are concerned.

 

Do you mean that ultimately you want to break the ABCABCABC flow and do ACBBAABBACBAA or something?

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

Yes Clawson.

void task_A(void) {
    static state_t state = A;
    switch(state) {
        case A:
            a++;
            state = B;
            break;

Depending what happen while code is running at task A.case A, you may or may not run "state = B" line. If you run it, then next time will land at case B, if the line is not executed, you will stilll land at case A. So the flow depends on what happens while code is running. So you could get a loop almost for free.

 

Virtually one task becomes a virtual main loop. So I get more main loops. Notice that "while 1" is no longer there.

 

One major limitation is that it does not handle registers, nor stack. The switch must be done while in a "clean" state.

Last Edited: Wed. Dec 16, 2015 - 03:36 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But actually I don't see any reason of using GOTOs & label pointers, other than programing in assembler using c mnemonics & variables.

angelu wrote:
Depending what happen while code is running at task A.case A, you may or may not run "state = B" line. If you run it, then next time will land at case B, if the line is not executed, you will stilll land at case A. So the flow depends on what happens while code is running.

 

But it does every single program more complex than switching leds.

such a switching may have (but done different way) sense in a multitasking environment, where are many tasks concurrently running at the same time. Otherwise it is just a programming trick, extremely difficult to debug and any change in program logic causes almost complete program reconstruction.

 

Have you seen anyone using it? probably not.

Last Edited: Wed. Dec 16, 2015 - 04:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

franekz wrote:
Otherwise it is just a programming trick,...

 

Again, please separate the issues of getting a documented GCC C extension construct working and the end use of it.  So what if it is a contrived example? 

 

But indeed:

angelu wrote:
Theusch: I will take a look at the listing file on the weekend.
??? While I guess that is related to the optimizer removing all code, why can't George just make a and b volatile and re-test?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

One major limitation is that it does not handle registers, nor stack. The switch must be done while in a "clean" state.

Well what you are looking at their is the difference between a co-operative and a pre-emptive OS.

 

In the former all the tasks agree to spit their work into "bite sized chunks" (some people often put a 5ms .. 10ms limit on that and say anything that takes longer must be broken into pieces). The code effectively "yields" to the operating system by returning from the task function after doing one chunk. But if a task gets greedy or more often the implementor makes a mistake and doesn't realise that a thing he thinks will finish in 3ms actually takes 30,000ms then it gets "stuck" in a task and everything else is starved of the CPU. If a task thinks it may take a while for something to complete it can effectively yield() which just means "return and let the main loop pass control to the next task on the list".

 

In the latter, a pre-emptive OS, you have a ticker interrupt (normally) and each time it "ticks" it "parks" the task that is running (by storing away all its registers and its stack state in a thing called a Task Control Block (TCB)) and then it moves down its list to find the TCB of the next task to run and uses all the register and stack data from there to load the CPU so that when it does the RETI from the ticker handler it RETs back into running a different task.

 

Writing your own co-op OS is a pretty simple task(sic) as my very simplistic state machine example above shows (though see Kartman's tutorial article for a better example) but writing a pre-emptive, ticker based OS is much more fraught and usually involves devling into Asm and the internals of the compiler operation. For that reason most people just visit: http://www.freertos.org/ and use that on their AVR. If they want to know how it works under the hood they read: http://www.freertos.org/implemen... and follow the "Next:" links at the bottom of each page. Rather handily the description of how it works is actually for the avr-gcc version out of all the CPUs/compilers that FreeRTOS is available for!

 

(FreeRTOS can also be run in a co-op way too).

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

clawson wrote:
...all the tasks agree to spit their work into "bite sized chunks" ...

Like a wine taster and a mouthful?

Image result for wine taster

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Kartman wrote:
The label is an address, just like a function is an address, so why the address of the address? That said, i'd suggest you look at setjump()
Because that is gcc's required syntax for the address corresponding to a label.

 

OP's design choice is harder to understand.

"Demons after money.
Whatever happened to the still beating heart of a virgin?
No one has any standards anymore." -- Giles

Last Edited: Wed. Dec 16, 2015 - 07:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
Again, please separate the issues of getting a documented GCC C extension construct working and the end use of it.  

It is such a bizarre use case of such an obscure construct that this is the kind of place where bugs are most likely to hide - simply because so few people will have tried it.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Thu. Dec 17, 2015 - 06:31 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, that was something i didn't know. Not sure if i'd ever use it though.

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

Kartman wrote:
Well, that was something i didn't know. Not sure if i'd ever use it though.

There have been threads on the GCC "computed goto" extension off and on for about 10 years here.

 

The straightforward use is to force a JMP, as when leaving a bootloader:

http://legacy.avrfreaks.net/inde...

http://legacy.avrfreaks.net/inde...

 

The religious arguments are presented here:

http://legacy.avrfreaks.net/inde...

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

    I tested three versions on a mega48 at 8MHz. It toggles a pin and I measured the output frequency

 

Switch based:

#include <avr/io.h>

typedef enum {A, B, C} state_t;

void task_A(void) {
	static state_t stateA = A;
	switch(stateA) {
		case A:
			PINB = (1 << 0);
			stateA = B;
			break;
		case B:
			PINB = (1 << 0);
			stateA = A;
			break;
	}
}

void task_B(void) {
	static state_t stateB = A;
	switch(stateB) {
		case A:
			PINB = (1 << 1);
			stateB = B;
			break;
		case B:
			PINB = (1 << 1);
			stateB = C;
			break;
		case C:
			PINB = (1 << 1);
			stateB = A;
			break;
	}
}

int main(void) {
	DDRB = (1 << 1 | 1 << 0);
	
	while(1) {
		task_A();
		task_B();
	}
}

It outputs 51kHz.

 

GOTO label based:

#include <avr/io.h>

void * label_taskA;
void * label_taskB;

int main(void)
{
	DDRB = 3;
	
	label_taskA = &&taskA_A;
	label_taskB = &&taskB_A;
	
	goto *label_taskA;
	
	//taskA
	taskA_A:
		PINB = (1 << 0);
		label_taskA = &&taskA_B;
		goto *label_taskB;
	taskA_B:
		PINB = (1 << 0);
		label_taskA = &&taskA_A;
		goto *label_taskB;

	//taskB
	taskB_A:
		PINB = (1 << 1);
		label_taskB = &&taskB_B;
		goto *label_taskA;
	taskB_B:
		PINB = (1 << 1);
		label_taskB = &&taskB_C;
		goto *label_taskA;
	taskB_C:
		PINB = (1 << 1);
		label_taskB = &&taskB_A;
		goto *label_taskA;

}

It outputs 77.16 kHz.

 

ASM version:

#include "m48def.inc"

.DSEG
taskA: .BYTE 2
taskB: .BYTE 2


.CSEG
LDI r16, 3
OUT DDRB, r16

LDI r16, LOW(taskA_A)
STS taskA, r16
LDI r16, HIGH(taskA_A)
STS taskA + 1, r16

LDI r16, LOW(taskB_A)
STS taskB, r16
LDI r16, HIGH(taskB_A)
STS taskB + 1, r16

LDS ZL, taskA
LDS ZH, taskA + 1
IJMP

// taskA
taskA_A:
	LDI r16, 1
	OUT PINB, r16

	LDI r16, LOW(taskA_B)
	STS taskA, r16
	LDI r16, HIGH(taskA_B)
	STS taskA + 1, r16

	LDS ZL, taskB
	LDS ZH, taskB + 1
	IJMP
taskA_B:
	LDI r16, 1
	OUT PINB, r16

	LDI r16, LOW(taskA_A)
	STS taskA, r16
	LDI r16, HIGH(taskA_A)
	STS taskA + 1, r16

	LDS ZL, taskB
	LDS ZH, taskB + 1
	IJMP

// taskB
taskB_A:
	LDI r16, 2
	OUT PINB, r16

	LDI r16, LOW(taskB_B)
	STS taskB, r16
	LDI r16, HIGH(taskB_B)
	STS taskB + 1, r16

	LDS ZL, taskA
	LDS ZH, taskA + 1
	IJMP
taskB_B:
	LDI r16, 2
	OUT PINB, r16

	LDI r16, LOW(taskB_C)
	STS taskB, r16
	LDI r16, HIGH(taskB_C)
	STS taskB + 1, r16

	LDS ZL, taskA
	LDS ZH, taskA + 1
	IJMP
taskB_C:
	LDI r16, 2
	OUT PINB, r16

	LDI r16, LOW(taskB_A)
	STS taskB, r16
	LDI r16, HIGH(taskB_A)
	STS taskB + 1, r16

	LDS ZL, taskA
	LDS ZH, taskA + 1
	IJMP
.EXIT

It outputs 140.7 kHz. A good part of speed improvement is because the compiler handles the pin toggle quite poorly.

 

George.

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

angelu wrote:
A good part of speed improvement is because the compiler handles the pin toggle quite poorly.

I risk saying this: If the actual pin toggling, rather than the state code surrounding it, is slow in your first program then it is

  • either because you've set a low (or no) level of optimization,
  • and/or because you've actually not coded to toggle one pin - not telling the compiler that a simple sbi/cbi can be used, but rather a sequence of instructions ending with an out instruction to write an 8-bit value to the complete port,
  • and/or that this mistake also makes the code for task B possibly thrashing the bit handled by task A and vice versa. Depending on something close to slim chance, this will go unnoticed or affect the wave-forms substantially (I'm suspecting something close to a 50/50 on/off square wave for the unaffected case, to something like 25/75 for the affected case).

 

Study the .lss for the first case. Then change the bit manipulations to just involve one pin/bit, and look at that .lss. Any difference?

 

Next, try making your task functions static inline. Any difference in frequency generated?

 

Also, since this is an ATmega48 then you do not need to keep track of the pin state yourself. The port is capable of toggling a pin from one state to another "on it's own". Just set the corresponding bit in the PINx register while the DDRx bit is set to make the pin an output.

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]

Last Edited: Thu. Dec 24, 2015 - 11:36 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

angelu wrote:
A good part of speed improvement is because the compiler handles the pin toggle quite poorly.

There has been the debate about whether translating "PORTx |= (1<<n);" into SBI PORTX, n is really the same as the writer stated a RMW operation on a volatile location.

 

Given that laxity in dogma, one can trick GCC into doing the SBI  with PINB |= (1<<n);

 

If it isn't the SBI that  is the problem, then I'm with the others:  Let's see the generated code, and/or tell the optimization level (and version and target AVR model so that we can reproduce.

 

To make a fair comparison of the above approaches, you need to force task_A() and task_B() inline.  Otherwise it will be just an exercise in specmanship.

 

With all the high-count op codes in the ASM version, I'm surprised one of the C versions didn't come out on top.

 

 

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

theusch wrote:
Given that laxity in dogma, one can trick GCC into doing the SBI  with PINB |= (1<<n);
I dislike code the would fail if the compiler believed me.

For the purpose, I use PINB=(1<<n);

Absent register pressure, it is at least as fast (sometimes faster) and requires at most one extra word.

 

Edit: not on xmegas

"Demons after money.
Whatever happened to the still beating heart of a virgin?
No one has any standards anymore." -- Giles

Last Edited: Thu. Dec 24, 2015 - 04:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Couldn't keep myself from digging into this...

 

I did some tests, running Angelus code, and some variants of it in the Studio simulator, keeping a good eye on the cycle counter. I had a breakpoint on task A, at the case where it's state is A. I counted the cycles it took to go from there until I returned there again. In some of the test cases below the timings weren't the same, but rather a repeating sequence of three different timings. This is due to the fact that the code for task B does take the same amount of clocks for the three different cases. I have given the repeating sequence for the cases where it occurred. The variation is small and has no substantial influence on the outcome.

 

Here are the results:

Angelu1
    a)	As given by angelu, with optimization Os	73.3
        (72, 75, 73...)

    b)	With task functions inlined			46.6
        (45, 48, 47)

Angelu2
    a)	As given by Angelu, with optimization Os	50
    Loses out to Angelu1b.

Angelu3
    a)	As given by Angelu				56
    Loses out to Angelu1b.

Re the frequencies observed by Angelu, the differenced between the function approach (Angelu1a) and the goto approach (Angelu2) fist with the above. The difference between those two and the assembler approach Angelu reports does not fit with my experiments. What optimization level where you using Angelo?

 

[EDIT: This section, beginning here,  might be rubbish on my part. Need to analyze more. See theusch's remark later, and read this with caution...]

Aside: In the assembler alternative a good hand-optimization is done: Each task start with simply toggling the PIN register bit, regardless of what state the task is in. The C variants does not do this (and since the PIN register is volatile, the compiler can not optimize this by folding the code). This is what I'm talking about:

 

void task_A(void) {
	static state_t stateA = A;
	switch(stateA) {
		case A:
			PINB |= (1 << 0);
			stateA = B;
			break;
		case B:
			PINB |= (1 << 0);
			stateA = A;
			break;
	}
}

could be rewritten (to make it correspond to the assembler alternative)

void task_A(void) {
	static state_t stateA = A;
	PINB |= (1 << 0);
	switch(stateA) {
		case A:
			stateA = B;
			break;
		case B:
			stateA = A;
			break;
	}
}

Does it make any difference for the run time? Oh yes!

Angelu1
    c)	Pin toggling factored out of cases	35.3
        (35, 35, 36, 36, 34, 37...)

I'm keeping this separate because there can be debate as to how much this approach resembles e.g. the assembler code by Angelu - making it possibly an unfair comparison. Be that as it may, the result is interesting.

[EDIT Possible rubbish section ends here]

 

So...

  1. I don't trust Angelus observations on generated frequencies. I'd like to know exactly how he came to them. I am especially interested in knowing which version of the tool chain he has been using, and what settings he did his builds with (e.g optimization)
    Notie that I have not once mentioned frequencies for my experiments. I have relied solely on the CPU cycle counter since it is not dependent in any way of CPU clock frequency and because what we are doing is a relative comparison between three different approaches.
  2. I am of the opinion that the C function approach is the best: a) It shows the best timings. b) It it the least obfuscated.

 

As for

angelu wrote:
A good part of speed improvement is because the compiler handles the pin toggle quite poorly.

that's just rubbish. In both C cases above the toggling proper is done by a LDI, OUT sequence. Here's a snippet from the .lss file for Angelu1a:

		case A:
			PINB = (1 << 0);
  64:	81 e0       	ldi	r24, 0x01	; 1
  66:	83 b9       	out	0x03, r24	; 3

Two instructions, one clock each = two clocks. Can't get better than that. The pin toggling has no part in the explanation of the difference in timings. This code is generated under Os optimization. If you had optimizations switched off then that is an entirely different explanation.

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]

Last Edited: Thu. Dec 24, 2015 - 06:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohanEkdahl wrote:
In the assembler alternative a good hand-optimization is done: Each task start with simply toggling the PIN register bit, regardless of what state the task is in.

I don't agree.  The ASM version seems to be true to the C version.

 

But yes, when inlined to make it correspond to the goto/ijmp versions the switch() should be OK.  Now, depending on the particular number of cases the results may be a bit different.

 

Also, AFAIK GCC doesn't have the "8-bit enum" and "8-bit case" that e.g. CodeVision has.  Again, depending on the number of cases and the values (e.g. contiguous or not) that co0uld cause a variation.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

theusch wrote:
JohanEkdahl wrote:
In the assembler alternative a good hand-optimization is done: Each task start with simply toggling the PIN register bit, regardless of what state the task is in.
I don't agree. The ASM version seems to be true to the C version.

Reinspecting the assembler code I see that. A misread on my part. Sorry.

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]

Last Edited: Thu. Dec 24, 2015 - 06:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

    Ok, thank you all for your time.

 

    The thing with the PIN handling I added just to convince the optimizer not to take out the switch code (not sure it was the best way to do).

 

    I did take a look at the listing file for all optimization level and I will describe them here. While I do comment a bit on the pin handling, it is not the main topic. For some optimization levels, the compiler uses CPU registers to keep some label addresses or port values for the PIN handling, so I did increase the taskA sections to make it run out of CPU registers in order to force it to use RAM.

 

    -O0:

	//taskA
	taskA_A:
		PINB = (1 << 0);
  8a:	83 e2       	ldi	r24, 0x23	; 35
  8c:	90 e0       	ldi	r25, 0x00	; 0
  8e:	21 e0       	ldi	r18, 0x01	; 1
  90:	fc 01       	movw	r30, r24
  92:	20 83       	st	Z, r18
		label_taskA = &&taskA_B;
  94:	87 e5       	ldi	r24, 0x57	; 87
  96:	90 e0       	ldi	r25, 0x00	; 0
  98:	90 93 01 01 	sts	0x0101, r25
  9c:	80 93 00 01 	sts	0x0100, r24
		goto *label_taskB;
  a0:	80 91 02 01 	lds	r24, 0x0102
  a4:	90 91 03 01 	lds	r25, 0x0103
  a8:	8f 93       	push	r24
  aa:	9f 93       	push	r25
  ac:	08 95       	ret
	taskA_B:
		PINB = (1 << 0);
  ae:	83 e2       	ldi	r24, 0x23	; 35
  b0:	90 e0       	ldi	r25, 0x00	; 0
  b2:	21 e0       	ldi	r18, 0x01	; 1
  b4:	fc 01       	movw	r30, r24
  b6:	20 83       	st	Z, r18
		label_taskA = &&taskA_C;
  b8:	87 e6       	ldi	r24, 0x67	; 103
  ba:	90 e0       	ldi	r25, 0x00	; 0
  bc:	90 93 01 01 	sts	0x0101, r25
  c0:	80 93 00 01 	sts	0x0100, r24
		goto *label_taskB;
  c4:	80 91 02 01 	lds	r24, 0x0102
  c8:	90 91 03 01 	lds	r25, 0x0103
  cc:	ed cf       	rjmp	.-38     	; 0xa8 <main+0x52>

    As you can see, it loads the register address in r25:r24, the value to write to r18, copy r25:r24 to r31:r30 and indirect writes the port. What a bloated way to do it..

 

    Back to the switch. For the tastA_A, it looks like the assembly version with the exception that instead IJMP it uses the sequence PUSH PUSH RET. Next tasks jump to this PUSH PUSH RET section.

 

    -O1:

	//taskA
	taskA_A:
		PINB = (1 << 0);
  da:	33 b8       	out	0x03, r3	; 3
		label_taskA = &&taskA_B;
  dc:	e9 e7       	ldi	r30, 0x79	; 121
  de:	f0 e0       	ldi	r31, 0x00	; 0
  e0:	f0 93 01 01 	sts	0x0101, r31
  e4:	e0 93 00 01 	sts	0x0100, r30
		goto *label_taskB;
  e8:	e0 91 02 01 	lds	r30, 0x0102
  ec:	f0 91 03 01 	lds	r31, 0x0103
  f0:	09 94       	ijmp
	taskA_B:
		PINB = (1 << 0);
  f2:	33 b8       	out	0x03, r3	; 3
		label_taskA = &&taskA_C;
  f4:	e5 e8       	ldi	r30, 0x85	; 133
  f6:	f0 e0       	ldi	r31, 0x00	; 0
  f8:	f0 93 01 01 	sts	0x0101, r31
  fc:	e0 93 00 01 	sts	0x0100, r30
		goto *label_taskB;
 100:	e0 91 02 01 	lds	r30, 0x0102
 104:	f0 91 03 01 	lds	r31, 0x0103
 108:	f3 cf       	rjmp	.-26     	; 0xf0 <main+0x9a>

    PIN handling got smarter. The switch fro taskA_A is exactly like the assembly version. For the next task it uses a RJMP instruction to to the the only one IJMP existent.

 

    -O2 and -O3:

	taskA_K:
		PINB = (1 << 0);
 1b4:	33 b8       	out	0x03, r3	; 3
		label_taskA = &&taskA_L;
 1b6:	50 92 01 01 	sts	0x0101, r5
 1ba:	40 92 00 01 	sts	0x0100, r4
		goto *label_taskB;
 1be:	e0 91 02 01 	lds	r30, 0x0102
 1c2:	f0 91 03 01 	lds	r31, 0x0103
 1c6:	09 94       	ijmp
	taskA_L:
		PINB = (1 << 0);
 1c8:	33 b8       	out	0x03, r3	; 3
		label_taskA = &&taskA_M;
 1ca:	e0 ef       	ldi	r30, 0xF0	; 240
 1cc:	f0 e0       	ldi	r31, 0x00	; 0
 1ce:	f0 93 01 01 	sts	0x0101, r31
 1d2:	e0 93 00 01 	sts	0x0100, r30
		goto *label_taskB;
 1d6:	e0 91 02 01 	lds	r30, 0x0102
 1da:	f0 91 03 01 	lds	r31, 0x0103
 1de:	09 94       	ijmp

    Now the switch is consistent with the ASM version and it uses IJMP for all task switches. I don't see a better way to do this kind of task switch.

 

    -Os:

  cc:	09 94       	ijmp

...............................................
...............................................
	
		PINB = (1 << 0);
 1be:	33 b8       	out	0x03, r3	; 3
		label_taskA = &&taskA_A;
 1c0:	50 92 01 01 	sts	0x0101, r5
 1c4:	40 92 00 01 	sts	0x0100, r4
		goto *label_taskB;
 1c8:	e0 91 02 01 	lds	r30, 0x0102
 1cc:	f0 91 03 01 	lds	r31, 0x0103
 1d0:	7d cf       	rjmp	.-262    	; 0xcc <main+0x76>

	//taskB
	taskB_A:
		PINB = (1 << 1);
 1d2:	23 b8       	out	0x03, r2	; 3
		label_taskB = &&taskB_B;
 1d4:	e5 ef       	ldi	r30, 0xF5	; 245
 1d6:	f0 e0       	ldi	r31, 0x00	; 0
 1d8:	f0 93 03 01 	sts	0x0103, r31
 1dc:	e0 93 02 01 	sts	0x0102, r30
		goto *label_taskA;
 1e0:	e0 91 00 01 	lds	r30, 0x0100
 1e4:	f0 91 01 01 	lds	r31, 0x0101
 1e8:	71 cf       	rjmp	.-286    	; 0xcc <main+0x76>

    As you can see, there is again only one IJMP instruction. All switches are jumping to that instruction. Is this more program memory saving ? I don't see how.

 

    Conclusion is that the GOTO is working, the label address thing is working also.

 

    George.

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

angelu wrote:
What a bloated way to do it..

Complaining about bloated code while applying optimization level -O0 is stupid. That optimization level is never to be used by the ordinary mortals.

 

angelu wrote:
Conclusion is that the GOTO is working, the label address thing is working also.

Never argued with that.

 

You made an argument about which worked best, in the sense "executing fastest". It is the results you put forth there that I question.

 

I ask again: What optimization level did you use when measuring frequencies?

 

And when you have measured the frequencies again with e.g. -Os, what frequencies do you get if you make the function approach use static inline functions?

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

What optimization level did you use when measuring frequencies?

    No optimization . With optimization the simulator is jumping first through the code to load some SFR registers with label addresses and then it starts the actual code run. I was confused by this so i took out the optimization.

 

    How a static inline function would look like ? I am going to test again the frequencies.

 

    George.

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

angelu wrote:

What optimization level did you use when measuring frequencies?

    No optimization . With optimization the simulator is jumping first through the code to load some SFR registers with label addresses and then it starts the actual code run. I was confused by this so i took out the optimization.

And with that you claim meaningful comparison of code generated by the compiler and code hand-written in assembler?!?

 

All your numbers above on measured frequencies become completely meaningless.

 

You might as well compare two cars claiming one is much faster, but the speed of the other was measured driving in reverse..

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

    Johan, in addition to your strong critic tone, could you please give me a hint about how a static inline function in this case would look like ? I am going to redo the frequency test, so both cars would speed ahead.

 

    Thank you,

    George.

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

angelu wrote:
in addition to your strong critic tone

Of-course I'm critical. You  present performance figures which seems to imply that the C implementations, especially the one employing functions, is much worse than your hand-written assembler. You have arrived at those figure only by disabling a crucial part of the compiler - the optimizer. If you stick your neck out this way, then be prepared to be criticized.

 

I have attached the AS7 workspace with the three projects I used to get to the numbers I presented in my post #34 above. The three projects are

 

Angelu1: Using functions, static inlined as they stand right now.

 

Angelu2: Using the &&/goto approach that started this thread.

 

Angelu3: Angelus assembler code presented above.

 

Angelu2 and Angelu3 has been "unloaded" from the workspace. This is so that I do not accidentally build and run the wrong project - I have learned that for investigations/comparisons like this I make less errors if I only have one project loaded in the workspace. If you want to activate Angelu2 or Angelu3, simply right-click the project node in Solution explorer and select Reload project.

 

Making a function static inline is easy. Start the function header with static inline and you're done. See e.g. the source in my Angelu1 project.

Attachment(s): 

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]

Last Edited: Sun. Jan 3, 2016 - 06:33 PM