Direct Jump in C

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

Hi there,

Currently I have this (in a C program):

asm("jmp 0x1E000");

to jump to my bootloader,

but i want to #define the address, so how do I do that?:

#define FLASH__START_OF_BOOT_SOFTWARE	0x1E000
asm("jmp FLASH__START_OF_BOOT_SOFTWARE");

Thanks

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

I'd do it with:

#define FLASH_START 0x1E000

typedef void (*f_ptr_type) (void);

....

{
  f_ptr_type f_ptr = (f_ptr_type) FLASH_START;
  // Off to boot loader...
  f_ptr();

Sure that is going to generate a CALL not a JMP but the fact is that the bootloader is a standalone C program and one of the very first things it will be doing is resetting the stack pointer to RAMEND anyway so the fact that this "leaves" something on the stack is irrelevant. It's nice to keep things in the "C domain" rather than having to break out to Asm.

Cliff

EDIT: I just looked at some bootloader code I wrote previously (where I just needed to JMP 0) and found that I did it in a slightly naughty way:

my_spm_routine(
	void
){
	void (* fn_ptr) (void);
	uint8_t valid = 1;

	cli();
// test data	
	if (valid) {
// write the flash
	}

	if (valid) {
		// force a reboot into the new code
		fn_ptr = (void *) 0;
		fn_ptr();
	}
	sei();
}

I cast 0 to (void *) so it could be assigned to the function pointer without the complicated cast that would otherwise have been required - this in part is why it's nice to use typedef - so you just define the (complicated) type of the function pointer in one place then can use it over and over again.

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

There are many ways to skin the cat. As a dedicated C-hater, I despise the function-pointer-cast-to-void* game, as it is non-portable anyway and misses the point.

Here...

#define FLASH__START_OF_BOOT_SOFTWARE   0x1E000

#define xstr(s) str(s)
#define str(s) #s


int main(void) { 
  asm("jmp %0" :: "p" (FLASH__START_OF_BOOT_SOFTWARE));
  asm("jmp " xstr(FLASH__START_OF_BOOT_SOFTWARE) );
}

...are two ways how to achieve what you intend.

As a homework, explain how and why they work.

JW

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

Quote:

I despise the function-pointer-cast-to-void* game

Me too which is why I was advocating the typedef - simply showing that a "void *" is a "quick and dirty" solution.

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

clawson wrote:
Quote:

I despise the function-pointer-cast-to-void* game

Me too which is why I was advocating the typedef - simply showing that a "void *" is a "quick and dirty" solution.

That's still pretending it's C (read: portable), which it is not; although I know that is the common practice among the C afficionados.

That's why I prefer to be more explicit (expose the naked assembler) ;-)

JW

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

How is using Asm more "portable" than using C? In either case, when you are jumping to some absolute address surely that ties it to a specific architecture anyway?

I just think C looks "cleaner" when it's not festooned with unnecessary bursts of Asm. If you want a link to a bootloader in Asm put it in a separate .S file and do it as "proper" assembly code.

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

Quote:

I just think C looks "cleaner" when it's not festooned with unnecessary bursts of Asm.

So you goto an extern label that is linked to the correct spot, and stay in C?

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

clawson wrote:
How is using Asm more "portable" than using C?

I did not say that. Please re-read my posts above.

clawson wrote:
In either case, when you are jumping to some absolute address surely that ties it to a specific architecture anyway?

That's my point; the nonpartability should be expressed in the source.

clawson wrote:
I just think C looks "cleaner" when it's not festooned with unnecessary bursts of Asm.

That's also my point. The "cleaner" look of C function pointer gobbledygook (cleaned up further by the typedef) suggests that it is portable, and it is not. Writing it in asm clearly indicates it is non-portable. You can always hide its nastyness behind a macro, if you desire "clean looks".

clawson wrote:
If you want a link to a bootloader in Asm put it in a separate .S file and do it as "proper" assembly code.
This is not an option here, as the functionality required is not a function. You don't have a feature in C which maps directly and unambiguously to a far jump (you can concoct kludges, though).

Again, I know that this is the common practice among those who believe "everything can thus should be written in C"; just I never did care too much about the culinary opinion of democratic majority of flies... ;-)

JW

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

Quote:

This is not an option here,

Eh?

.global my_jump
my_jump:
  JMP somewhere
extern void my_jump(void);

some_fn() {
...
 my_jump();

as an alternative to:

some_fn() {
...
 asm("JMP somewhere");

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

clawson wrote:
Quote:

This is not an option here,

Eh?

.global my_jump
my_jump:
  JMP somewhere
extern void my_jump(void);

some_fn() {
...
 my_jump();

as an alternative to:

some_fn() {
...
 asm("JMP somewhere");


Yeah, a kludge, as I said above.

Except in trivial setups, it makes main to emit unnecessary caller-saved register saves before my_jump() call, CALL fills up stack, etc.

JW

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

Quote:

it makes main to emit unnecessary caller-saved register saves before my_jump() call, CALL fills up stack, etc.

What does that matter? - the preamble of the bootloader will be setting SP to RAMEND so any "junk" on the stack is lost anyway. Or did you mean it wasted opcode execution time to do such unnecessary PUSH's? If you are restarting into a bootloader is it likely that things are time critical at this point anyway?

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

And what if the purpose of jump is not to start a bootloader?

I am talking about style rather than a particular solution to a particular problem.

JW

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

Quote:

I am talking about style rather than a particular solution to a particular problem

But the OP said:
Quote:

to jump to my bootloader,

In fact, apart from a boot-app or app-boot jump, I cannot off hand think of another reason why a C program on an MCU would be jumping to a hard coded flash address?

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

Joeks wrote:
but i want to #define the address, so how do I do that?
Here is a variation of what you proposed that works:
// define destination addresses
#define FLASH__START_OF_BOOT_SOFTWARE   0x1E000

// define a macro to create a string from its parameter
#define STR(str)   #str

// note that the two strings here are concatenated
#define GOTO(addr) asm("jmp " STR(addr))

int main(void)
{
    GOTO(FLASH__START_OF_BOOT_SOFTWARE);
}

Here is a variation that uses a call. Note that this is suitable for calling service routines in the bootloader.

#define SET_VALUE(n, v)  __asm__ __volatile__(".global " #n "\n.set " #n ", " #v "\n"::)

void myEntry1(void);
void myEntry2(uint8_t val);

int main(void)
{
    // set the addresses of the bootloader entry points
    SET_VALUE(myEntry1, 0x1e000);
    SET_VALUE(myEntry2, 0x1e004);

    // invoke the bootloader routines
    myEntry1();
    myEntry2(0x55);
}

If you would prefer, you can set the addresses of the entry points in a .S file (rather than in the .c file) thusly:

.macro SET_VALUE _name, _value=0
    .global \_name
    .set \_name, \_value
.endm

SET_VALUE myEntry1, 0x1e000
SET_VALUE myEntry2, 0x1e004

edit: modified the addresses for consistency

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

Last Edited: Wed. Jun 30, 2010 - 05:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
That's my point; the nonpartability should be expressed in the source.
You are talking about a completely different kind of portability. As far as the C is concerned, it is perfectly portable to any compiler. What you are talking about is portability between models of AVRs. But by your argument, TCCR1B is "non-portable", so should we start accessing all registers with assembly to make that "non-portability" obvious?

Regards,
Steve A.

The Board helps those that help themselves.

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

Koshchi wrote:
Quote:
That's my point; the nonpartability should be expressed in the source.
You are talking about a completely different kind of portability. As far as the C is concerned, it is perfectly portable to any compiler.

C99 6.3.2.3 #5 thinks otherwise.

"Implementation defined" reads as nonportable between compilers; "might not be correctly aligned etc." reads as asking for troubles.

The rest of your post is inferred from what I never said.

JW

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

wek wrote:
There are many ways to skin the cat. As a dedicated C-hater, I despise the function-pointer-cast-to-void* game, as it is non-portable anyway and misses the point.

Here...

#define FLASH__START_OF_BOOT_SOFTWARE   0x1E000

#define xstr(s) str(s)
#define str(s) #s


int main(void) { 
  asm("jmp %0" :: "p" (FLASH__START_OF_BOOT_SOFTWARE));
  asm("jmp " xstr(FLASH__START_OF_BOOT_SOFTWARE) );
}

How about this?
#include 

#define xstr(s) str(s)
#define str(s) #s


int main(void) { 
  asm("jmp %0" :: "p" (APPTABLE_SECTION_START));
  asm("jmp " xstr(FLASH__START_OF_BOOT_SOFTWARE) );
}

Iluvatar is the better part of Valar.