Maker Pro
Maker Pro

Question about function call in a microprocessor?

Hello all the experts here,
I would like to know how a typical porcessor executes a function call
or subroutine call in a assembly code.

I know it uses stack for doing it and I do understand the mechanism
about the stack here. I would want to go one step further and ask you
how it exaclty saves the internal variable, returned variable and call
in functions and all those things. If you see I am asking something
like a compiler design, where in it maps the high-level language into
architecture specific assembly code. I would really appreciate if you
could do with an example.

I propose something like this,

main {

int k = foo (int a, int b, int c);
foo uses lets says 5 local varaibles.

where in foo calls another function
int l = foo1 (int d, int e);
foo1 uses 2 local variables.

Please shed some thought on this.

Thanks in advance
Hariharan K Srinivasan.
 
D

DaveC

Jan 1, 1970
0
[email protected] wrote in @o13g2000cwo.googlegroups.com:
Hello all the experts here,
I would like to know how a typical porcessor executes a function call
or subroutine call in a assembly code.

I know it uses stack for doing it and I do understand the mechanism
about the stack here. I would want to go one step further and ask you
how it exaclty saves the internal variable, returned variable and call
in functions and all those things. If you see I am asking something
like a compiler design, where in it maps the high-level language into
architecture specific assembly code. I would really appreciate if you
could do with an example.

I propose something like this,

main {

int k = foo (int a, int b, int c);
foo uses lets says 5 local varaibles.

where in foo calls another function
int l = foo1 (int d, int e);
foo1 uses 2 local variables.

Please shed some thought on this.

Thanks in advance
Hariharan K Srinivasan.

Well, to give Microchip's PIC micro-controller as an example. It only has
one working register (W). I have not used any other chips, but I know
Intel chips in your home PC have many more.

I'd use the W register for the return value. But Id need to tell my
assembler in advance that I wanted to use a,b and c. Because I have not
OS that can dynamically allocate memory for me.

So for int k = foo (int a, int b, int c);

I would globally define a, b, c
Set some values in a,b,c
Call the foo function

Foo:
Movlw a,w
Addwf b,w
Addwf c,w
Return

And end up with the result in the W register.

The stack is used by the processor to remember program jumps, because the
location of Foo in program memory is way off in some distant place. But I
never worry about this.

The only time the Stack is of concern for me is in implementing recursive
functions, because the stack is only so deep and could overflow.

It's good that you know C but you will quickly answer any questions you
have about the low level stuff, if you looked at a datasheet of any
micro-controller or processor.

Here is a simple one
http://ww1.microchip.com/downloads/en/DeviceDoc/35007b.pdf


DaveC
 
S

Spehro Pefhany

Jan 1, 1970
0
Hello all the experts here,
I would like to know how a typical porcessor executes a function call
or subroutine call in a assembly code.

I know it uses stack for doing it and I do understand the mechanism
about the stack here.

This is not necessarily true. Using the stack is theoretically what
you'd like to do because it makes the function calls re-entrant.
However the crippled architecture of small micros (and possibly issues
with caching with larger ones) make it sometimes necessary to just use
part of memory of some kind for the required storage.
I would want to go one step further and ask you
how it exaclty saves the internal variable, returned variable and call
in functions and all those things. If you see I am asking something
like a compiler design, where in it maps the high-level language into
architecture specific assembly code. I would really appreciate if you
could do with an example.

Yes, it's pretty much that.
I propose something like this,

main {

int k = foo (int a, int b, int c);
foo uses lets says 5 local varaibles.

where in foo calls another function
int l = foo1 (int d, int e);
foo1 uses 2 local variables.

Okay, it's a little more complex than that because there will be
registers that have to be saved in addition to the automatic variables
in foo/foo1. At least the accumulator(s), but maybe some floating
point registers etc. The compiler will hopefully figure out the
minimum that is required, rather than just saving everything every
time (safer from compiler bugs, but potentially very inefficient).

One way is just to push the accumulator(s), index registers etc. on
the stack, and assign automatic variables within a stack frame for the
function. There might be more than one stack, BTW. Some small
processors have a hardware stack for function calls. That creates
re-entrant code to the extent you don't run out of stack space.

But in brain-dead small micros with Harvard architecture, often
non-re-entrant functions are used- some general purpose (on-chip or
off) RAM is allocated for the storage of automatic variables etc. .
This is not as elegant.

A von Neuman 8-bit micro such the HC908 might have a compiler that
would do something like (generated by Metrowerks CodeWarrior C/C++
cross compiler):

;int foo1(int d, int e) {
; int v,w;
; v = d; w = e;
; return d + e;
;}


0000 87 PSHA
0001 89 PSHX
0002 95 TSX
0003 e601 LDA 1,X
0005 eb05 ADD 5,X
0007 87 PSHA
0008 e604 LDA 4,X
000a f9 ADC ,X
000b 97 TAX
000c 86 PULA
000d a702 AIS #2
000f 81 RTS

;int foo(int a, int b, int c) {
; int q,r,s,t,u;
; q=r=s=a; t=b; u=c;
; return a + foo1(b, c);
; }



0000 87 PSHA
0001 89 PSHX
0002 a7fe AIS #-2
0004 9ee608 LDA 8,SP
0007 87 PSHA
0008 9ee608 LDA 8,SP
000b 87 PSHA
000c 9ee60c LDA 12,SP
000f 9ee703 STA 3,SP
0012 9ee606 LDA 6,SP
0015 ad00 BSR foo1
0017 a702 AIS #2
0019 9ee702 STA 2,SP
001c 9ee609 LDA 9,SP
001f 87 PSHA
0020 9ee602 LDA 2,SP
0023 9eeb03 ADD 3,SP
0026 87 PSHA
0027 9f TXA
0028 95 TSX
0029 e901 ADC 1,X
002b 97 TAX
002c 86 PULA
002d 8a PULH
002e a704 AIS #4
0030 81 RTS


;void main(void) {
; int x;
; x = foo(1,2,3);
; for(;;) {
; __RESET_WATCHDOG();
; }


0000 a7fe AIS #-2
0002 ae01 LDX #1
0004 8c CLRH
0005 89 PSHX
0006 8b PSHH
0007 5c INCX
0008 89 PSHX
0009 8b PSHH
000a a603 LDA #3
000c 5f CLRX
000d ad00 BSR foo
000f a704 AIS #4
0011 9ee702 STA 2,SP
0014 9eef01 STX 1,SP



Now, here's a similar dissembly listing generated by PIC18
cross-compiler for the PIC18 core-- Quite a difference! This one uses
fixed memory locations rather than Push/pull from the stack.


2:
3: int foo1(int d, int e) {
00001E 0D013 BRA 0x46
4: int v,w;
5: v = d; w = e;
000020 0C0F8 MOVFF 0xf8, 0xfc
000022 0F0FC NOP
000024 0C0F9 MOVFF 0xf9, 0xfd
000026 0F0FD NOP
000028 0C0FA MOVFF 0xfa, 0xfe
00002A 0F0FE NOP
00002C 0C0FB MOVFF 0xfb, 0xff
00002E 0F0FF NOP
6: return d + e;
000030 0C0F8 MOVFF 0xf8, 0
000032 0F000 NOP
000034 0C0F9 MOVFF 0xf9, 0x1
000036 0F001 NOP
000038 00100 MOVLB 0
00003A 051FA MOVF 0xfa, W, BANKED
00003C 02600 ADDWF 0, F, ACCESS
00003E 051FB MOVF 0xfb, W, BANKED
000040 02201 ADDWFC 0x1, F, ACCESS
000042 0D000 BRA 0x44
7: }
000044 00012 RETURN 0
8:
9:
10: int foo(int a, int b, int c) {
00004A 0D023 BRA 0x92
11: int q,r,s,t,u;
12: q=r=s=a; t=b; u=c;
00004C 0C0E8 MOVFF 0xe8, 0xf2
00004E 0F0F2 NOP
000050 0C0E9 MOVFF 0xe9, 0xf3
000052 0F0F3 NOP
000054 0C0F2 MOVFF 0xf2, 0xf0
000056 0F0F0 NOP
000058 0C0F3 MOVFF 0xf3, 0xf1
00005A 0F0F1 NOP
00005C 0C0F0 MOVFF 0xf0, 0xee
00005E 0F0EE NOP
000060 0C0F1 MOVFF 0xf1, 0xef
000062 0F0EF NOP
000064 0C0EA MOVFF 0xea, 0xf4
000066 0F0F4 NOP
000068 0C0EB MOVFF 0xeb, 0xf5
00006A 0F0F5 NOP
00006C 0C0EC MOVFF 0xec, 0xf6
00006E 0F0F6 NOP
000070 0C0ED MOVFF 0xed, 0xf7
000072 0F0F7 NOP
13: return a + foo1(b, c);
000074 0C0EA MOVFF 0xea, 0xf8
000076 0F0F8 NOP
000078 0C0EB MOVFF 0xeb, 0xf9
00007A 0F0F9 NOP
00007C 0C0EC MOVFF 0xec, 0xfa
00007E 0F0FA NOP
000080 0C0ED MOVFF 0xed, 0xfb
000082 0F0FB NOP
000084 0DFCC RCALL 0x1e
000086 051E8 MOVF 0xe8, W, BANKED
000088 02600 ADDWF 0, F, ACCESS
00008A 051E9 MOVF 0xe9, W, BANKED
00008C 02201 ADDWFC 0x1, F, ACCESS
00008E 0D000 BRA 0x90
14: }
000090 00012 RETURN 0
15:
16:
17: void main(void) {
000094 0D012 BRA 0xba
18: int x;
19:
20: x = foo(1,2,3);
000096 00E01 MOVLW 0x1
000098 00100 MOVLB 0
00009A 06FE8 MOVWF 0xe8, BANKED
00009C 06BE9 CLRF 0xe9, BANKED
00009E 00E02 MOVLW 0x2
0000A0 06FEA MOVWF 0xea, BANKED
0000A2 06BEB CLRF 0xeb, BANKED
0000A4 00E03 MOVLW 0x3
0000A6 06FEC MOVWF 0xec, BANKED
0000A8 06BED CLRF 0xed, BANKED
0000AA 0DFCF RCALL 0x4a
0000AC 0C000 MOVFF 0, 0xe6
0000AE 0F0E6 NOP
0000B0 0C001 MOVFF 0x1, 0xe7
0000B2 0F0E7 NOP
21: for(;;);
0000B4 0D7FF BRA 0xb4
22:
23:
24: }
0000B6 0EF0C GOTO 0x18




Please shed some thought on this.

Thanks in advance
Hariharan K Srinivasan.


Best regards,
Spehro Pefhany
 
K

Ken Smith

Jan 1, 1970
0
Hello all the experts here,
I would like to know how a typical porcessor executes a function call
or subroutine call in a assembly code.

I know it uses stack for doing it and I do understand the mechanism
about the stack here. I would want to go one step further and ask you
how it exaclty saves the internal variable, returned variable and call
in functions and all those things. If you see I am asking something
like a compiler design, where in it maps the high-level language into
architecture specific assembly code. I would really appreciate if you
could do with an example.

I propose something like this,

main {

int k = foo (int a, int b, int c);
foo uses lets says 5 local varaibles.

where in foo calls another function
int l = foo1 (int d, int e);
foo1 uses 2 local variables.

In the 8051 it usually depends a great deal on whether the function
involved must be able to be re-entered. If not, the linker allocates the
variables in memory such that routines that can't be running at the same
time reuse locations. Effectively the variable spaces get merged. This
is part fo the reason that a good C compiler can make 8051 code that is
only 3 times slower than assembly.

Returned values are usually put into ACC or (B,ACC) if they are 16 bit.
 
G

Guy Macon

Jan 1, 1970
0
...in a assembly code...
[...]

I propose something like this,

main {

[snip]

Decide whether you want to write assembly language or C.

If you are programming in C, your compiler does all of this for you.

If you are programming in assembly, you will know how to do this
as part of knowing how to program in assembly language.
 
G

Guy Macon

Jan 1, 1970
0
Spehro said:
Guy said:
...in a assembly code...
[...]

I propose something like this,

main {

[snip]

Decide whether you want to write assembly language or C.

If you are programming in C, your compiler does all of this for you.

If you are programming in assembly, you will know how to do this
as part of knowing how to program in assembly language.

If you're programming a small micro in C, you'd better understand
both.

Good point. I was only thinking of the PC case.
 
S

Spehro Pefhany

Jan 1, 1970
0
...in a assembly code...
[...]

I propose something like this,

main {

[snip]

Decide whether you want to write assembly language or C.

If you are programming in C, your compiler does all of this for you.

If you are programming in assembly, you will know how to do this
as part of knowing how to program in assembly language.

If you're programming a small micro in C, you'd better understand
both.


Best regards,
Spehro Pefhany
 
A

Andrew Holme

Jan 1, 1970
0
Hello all the experts here,
I would like to know how a typical porcessor executes a function call
or subroutine call in a assembly code.

I know it uses stack for doing it and I do understand the mechanism
about the stack here. I would want to go one step further and ask you
how it exaclty saves the internal variable, returned variable and call
in functions and all those things. If you see I am asking something
like a compiler design, where in it maps the high-level language into
architecture specific assembly code. I would really appreciate if you
could do with an example.

80x86 compilers store not only the subroutine return address, but also
function parameters and local variables on the stack. Together, they
comprise the stack frame. Often, a special register called the base pointer
keeps track of where they are. The parameters and variables are accessed
using base pointer relative addressing. Here's a (16-bit) stack frame:

BP+6 => Param1
BP+4 => Param2
BP+2 => Return adddress
BP+0 => Previous value of BP saved here
BP-2 => Local1
BP-4 => Local2

You can read about this in books and on the web, or you can just inspect the
code your compiler produces.

If your processor doesn't have base pointer support, then the compiler has
to do a lot more work!
 
R

Roger Lascelles

Jan 1, 1970
0
Hello all the experts here,
I would like to know how a typical porcessor executes a function call
or subroutine call in a assembly code.

I know it uses stack for doing it and I do understand the mechanism
about the stack here. I would want to go one step further and ask you
how it exaclty saves the internal variable, returned variable and call
in functions and all those things. If you see I am asking something
like a compiler design, where in it maps the high-level language into
architecture specific assembly code. I would really appreciate if you
could do with an example.

I propose something like this,

main {

int k = foo (int a, int b, int c);
foo uses lets says 5 local varaibles.

where in foo calls another function
int l = foo1 (int d, int e);
foo1 uses 2 local variables.

Please shed some thought on this.

Thanks in advance
Hariharan K Srinivasan.

This is the sort of question you can best answer by investigation. Google
will
help you more than the busy people on newsgroups can. You need to get a C
compiler and look at the output listing. I recommend Pelles C from :

http://www.smorgasbordet.com/pellesc/

If you target the 80x86 family, you get a beautiful clear assembly you can
work through. Unlike many small and RISC processors, the 80x86 family
compilers tend to give consistent output for high level languages no matter
how many levels deep are your function calls.

Pelles C gives you a nice editor to write and compile from. To see the
output
listing run the PODUMP utility with the /DISASM switch on the .OBJ files .

Here is what I get :


15: void foo1( int d, int e )
16: {

_foo1:
[00000000] 55 push ebp
[00000001] 89E5 mov ebp,esp

@1:
[00000003] 5D pop ebp
[00000004] C3 ret



19: void foo( int a, int b, int c )
20: {

_foo:
[00000010] 55 push ebp
[00000011] 89E5 mov ebp,esp
[00000013] 83EC10 sub esp,+10
[00000016] 53 push ebx
21: int v1, v2, v3, v4, v5;
22: v1 = 0;
[00000017] 31DB xor ebx,ebx
23:
24: foo1( v1, a );
[00000019] 8B4508 mov eax,dword ptr [ebp+8]
[0000001C] 50 push eax
[0000001D] 53 push ebx
[0000001E] E800000000 call _foo1
[00000023] 83C408 add esp,+8

@2:
[00000026] 5B pop ebx
[00000027] 89EC mov esp,ebp
[00000029] 5D pop ebp
[0000002A] C3 ret



The function call to foo1() pushes on the stack the parameters from in eax
and ebx.
The ebp is an indexing register used to access the stack variables belonging
to
a function.

sub esp,+10 makes room for local variables while add esp+8 releases that
stack
space at the end of the function.

If you start with simple function calls, you can work out what is going on.

Roger
 
K

keith

Jan 1, 1970
0
Hello all the experts here,
I would like to know how a typical porcessor executes a function call
or subroutine call in a assembly code.

I know it uses stack for doing it and I do understand the mechanism
about the stack here. I would want to go one step further and ask you
how it exaclty saves the internal variable, returned variable and call
in functions and all those things. If you see I am asking something
like a compiler design, where in it maps the high-level language into
architecture specific assembly code. I would really appreciate if you
could do with an example.

Thre is no one answer to this. Different processors and compilers do it
differently. Note that many processors don't have a "stack", per se.
 
Top