Calling a function using a pointer - a question about the assmbler from the compiler

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Calling a function using a pointer - a question about the assmbler from the compiler

3,865 Views
Stephen
Contributor III
Hi
I am using Code warrior for HC12 version 4.7, IDE version 5.9
The target is an MC9S12A256. The program used the BANKED memory model.

I was in the process of debugging an issue in our application, when I scanned through the assembler code and became curious about what exactly was happening at the assembler level.

I have a look up table of pointers to functions. It is stored in a constant storage segment of memory. Hence each entry requires 3 bytes, and is filled in by the compiler with the addresses of the routines that I wish to call. The format is:

typedef void (* __far tMain_Function)(void);

#pragma CONST_SEG __PPAGE_SEG BOOT_HEADER

const tMain_Function Run_main_program = (* main_program);
const tMain_Function Run_capture_tacho = (* Tacho_Edge_Seen);
const tMain_Function Run_timer2_isr = (* Opt_Sensor_Timer);

In turn, the function referenced is declared like this:
#pragma CODE_SEG DEFAULT_ROM
void __far main_program(void)
{
Setup_Ports();
/* do something here */
}

And is called like this:
Run_main_program();


When I disassmbled the code, it looked like:
001d c600 [1] LDAB #PAGE(Run_main_program)
001f 5b35 [2] STAB /*PPAGE*/53
0021 4bfb0000 [10] CALL [Run_main_program,PCR]

If the call is made from within an interrupt routine, the disassembl;ed code is even longer. E.g.
0000 9635 [3] LDAA 53
0002 36 [2] PSHA
139: Run_capture_tacho();
0003 c600 [1] LDAB #PAGE(Run_capture_tacho)
0005 5b35 [2] STAB /*PPAGE*/53
0007 4bfb0000 [10] CALL [Run_capture_tacho,PCR]
140: }
000b 32 [3] PULA
000c 5a35 [2] STAA 53
The contents of memory location 53 are being saved to stack.

My question is: why is accumulator B being stored to memory location 53 (hex 0x35)? According to the manual, this location is used by the Clocks and reset generator module, PLL divider register REFDV. I have stepped through the code one assembler instruction at the time. The STAB command has no effect because I have the PLL running, the flag PLLSEL is set and according to the manual you cannot change the REFDV register once this flag is set. I then reran this in the debugger and got it to miss the STAB step - everything still worked fine.

I am guessing that this instruction is required by another member of the HC12 family, and that somewhere there is an option or setting I have set/not set which removes it.
My concern is that somewhere this will cause a problem. Also it is in a section of code where I am trying to keep the overall code size down.

Many thanks for any help
Steve
Labels (1)
Tags (1)
0 Kudos
9 Replies

924 Views
CompilerGuru
NXP Employee
NXP Employee
Hi Stephen,

What options do you use? Do you have  -CpuHCS12?
Do you get any warnings when compiling?

Also where is Run_main_program allocated? Is it allocated in a PPAGE controlled area,
as it is indicated by the "#pragma CONST_SEG __PPAGE_SEG BOOT_HEADER" pragma
If not, then do not provide the __PAGE_SEG qualifier, that qualifier is what causes the compiler to write to 0x35 (which the compiler thinks its PPAGE).

I also saw quite a few __far qualifiers in your code, at least in the banked memory model, functions and function pointers are __far by default, so those are not necessary (unless the function is in a __NEAR qualified code section).
Instead of
#pragma CODE_SEG DEFAULT_ROM
You can use
#pragma CODE_SEG DEFAULT
Does the same thing, but does not cause the warning about the use of predefined section names.

Below your snippet a little bit edited so I don't get any warnings. The two "#pragma CODE_SEG DEFAULT" are actually not necessary as there is already the default section active at that location.
I did not see your code pattern, but I think this is because I don't know your actually used option.
The compiler uses 0x35 as the address of the PPAGE register in your code, that address can be specified with the option -CpPPAGE, do you have this option set?

Daniel

Code:
typedef void (* tMain_Function)(void);#pragma CODE_SEG DEFAULTextern void main_program(void);#pragma push#pragma CONST_SEG __PPAGE_SEG BOOT_HEADERconst tMain_Function Run_main_program;#pragma pop#pragma CODE_SEG DEFAULTvoid main_program(void){/* do something here */}void main() {  Run_main_program();}

 


0 Kudos

924 Views
Stephen
Contributor III
Hi Daniel
Thanks for your swift reply :smileyhappy:

The options I am using for the compiler are:
-Cf -CpPPAGE=0x35 -CPUHCS12 -Mb -OnB -TD4LD4LLD4 -WmsgNw100 -WmsgSd1106 -WmsgSd1825 -WmsgSd1860 -WmsgSd3401 -WmsgSd3801 -WmsgSd3804 -WmsgSd12006 -WmsgSd12056 -WmsgSi4200

I can see now that the PPAGE register is incorrectly assigned to 0x35 instead of 0x30 as it should be for the MC9S12A256, so that's my first error. I am not sure how I have caused this?

I added the __far qualifiers to a few function declarations to help me remember what was in banked memory - I know it had no further effect. I have some code in unbanked memory and they are tagged __near.

This leads to a final question - if the compiler generates a CALL[Run_main_program,PCR] assembler command, does it need to generate extra code to set the PPAGE register. I was under the impression that with the CALL and RTC statements, the bank page
number is saved and restored automatically during execution?

Many thanks
Steve
0 Kudos

924 Views
CompilerGuru
NXP Employee
NXP Employee
>I can see now that the PPAGE register is incorrectly assigned to 0x35 instead of 0x30 as it
>should be for the MC9S12A256, so that's my first error. I am not sure how I have caused this?
Check the options...
>-CpPPAGE=0x35

Note that the compiler does not set PPAGE to jump to a certain address, it set it to access the constant function pointer. So where is the function pointer allocated? My guess: non paged. And then the __PPAGE_SEG qualifier as specified in your snippet is unnecessary. Together with the probably incorrect usage of -CpPPAGE (see below) this caused the code you got to be generated.
Are you using __far data pointers?
Only specifying -CpPPAGE=0x30 with no -CpPPAGE=RUNTIME tells the compiler to access __far data,
without using a runtime routine. That only works if all the code using __far data pointers is allocated non banked.
So using -CpPPAGE=0x30 -CpPPAGE=RUNTIME (or just -CpPPAGE=RUNTIME, 0x30 is the default PPAGE address with -CpuHCS12...)  causes a runtime routine access to __PPAGE_SEG data.
If you are not using __far data (that's completely independent from __far function pointer usage!) then you do not need to use -CpPPAGE and you should not use the __PPAGE_SEG segment qualifier.

Daniel

BTW, there are a bit many warnings disabled. By also using segment pragma's before declarations, C4200 should not occur.


0 Kudos

924 Views
Lundin
Senior Contributor IV
As a sidenote, on older versions of CW (3.1 and older) the compiler didn't give a darn about that compiler switch unless you included a file called DATAPAGE.C somewhere from the Codewarrior\lib\. Then you'd have to make sure the file contained a PPAGE address of 0x30.

I hope this was fixed in version 4.x of CW...

Anyway, you are using the banked memory model(switch -Mb) so you shouldn't have to worry about this. The compiler will use call and rtc instructions rather than jsr and rts. The only time you need to think of this is when you are writing inline assembler.

And you don't have to write non-standard C code with "far".

Message Edited by Lundin on 2008-09-18 04:34 PM
0 Kudos

924 Views
CompilerGuru
NXP Employee
NXP Employee
>I hope this was fixed in version 4.x of CW...

In those old versions, the library contained one version of datapage.c, and if that one did not fit the target processor, you had to add the datapage.c to the project  to build it. The main problem with this was that this just failed at run time, and that's bad.
Now datapage.c is no longer contained in the library, so you still have to include it into the project (it is added in every wizard created project), but if you fail to add it for whatever reason, it wont link if __far data support is used. So no incorrect automatic datapage.c anymore.
Also datapage.c does now use the PPAGE address as specified with -CpPPAGE, and if -CpPPAGE is not specified, it uses the right address by depending on the -cpu option. So there is little need to change datapage.c unless you have some very custom setup.

>Anyway, you are using the banked memory model(switch -Mb) so you shouldn't have to worry about this.
>The compiler will use call and rtc instructions rather than jsr and rts. The only time you need to
> think of this is when you are writing inline assembler.

PPAGE is used mainly/only for __far data, and the code snippets provided did explicitely ask for __PPAGE_SEG access to constants (which happened to be function pointers, but that does not really matter).
I doubt a bit that the use of __PPAGE_SEG in the original snippet was intended, if not then resolving the issue is as simple as deleting the __PPAGE_SEG qualifier.
Otherwise the -CpPPAGE option should be fixed. (well it should be fixed either way, I guess :smileyhappy:.

Daniel
0 Kudos

924 Views
Lundin
Senior Contributor IV
I suppose I might be wrong, but as long as you have set the memory model to banked, won't all function pointers be "far pointers" by default? I'm pretty sure they are.

In my programs for S12 I am using plenty of function pointers and there are never any problems with banked memory. I never use the far keyword, which is a good thing.

Also, my interrupt vector table is written in C as an array of function pointers, and I know that I had to explicitly declare those as "near" to get it working.
0 Kudos

924 Views
CompilerGuru
NXP Employee
NXP Employee
In the banked memory model function pointers are __far, meaning they are 3 bytes in size and the function is called with a CALL.
However that is independent on how they are allocated, or in other words what kind of code the compiler has to generate in order to load the address of the function.
Placing a function pointer into a __PPAGE_SEG section therefore tells the compiler to use PPAGE twice in order to call the function pointer. First set PPAGE to the page of the function pointer address (e.v. inside of a runtime, depending on -cpPPAGE) and read the function pointer, then use CALL, which implicitely sets PPAGE to the page of the called function during the function call.

There is no need to use anything special (__far, #pragma's,...) in order to use __far function pointers in the S12 banked memory model. There are some things to adapt in order to use __far data, and this has been partially done in the initial snippets.

Daniel
0 Kudos

924 Views
Stephen
Contributor III
Hi Daniel
Many thanks for your help - it is all beginning to fall into place.
It has also clarified why I needed to include the __PPAGE_SEG pragma for the function lookup table.

In a similar approach to the one Lundin describes, I also have a table of function pointers written in C, which reference the functions run by the interrupts. This was done to work with a bootloader system we developed. This allows us to download a new firmware via a serial port onto our product. Everything can be overwritten except the little bootloader itself.

At power up, we start running the bootloader. The bootloader then calculates the checksum for the main application and compares it with the checksum stored in a standard location in memory. If it is correct, it the looks up the main program location in the look-up table. If there is an error in the checksum (most often caused because a previous reprogramming attempt has been interrupted), the bootloader keeps running in an error mode until a new firmware is downloaded.

Anyway, that's the background - it means that our product can be reprogrammed in the field via a serial port.

Best regards
Steve
0 Kudos

924 Views
CompilerGuru
NXP Employee
NXP Employee
Hi Steve,

Placing the function pointer table at a certain location is done with the pragma,
so I see (and thought before) that you need that.
However if you need

> #pragma CONST_SEG __PPAGE_SEG BOOT_HEADER

or just

> #pragma CONST_SEG BOOT_HEADER

(say the same pragma without __PPAGE_SEG) depends on where that segment is placed and how your code accesses those constants. As the code worked with the wrong -Cppage option, it still looks to be as if the __PAGE_SEG segement qualifier may not be necessary. Either way, good you have it working :smileyhappy:.

Daniel
0 Kudos