A89: "Transfer" of values between C and ASM


[Prev][Next][Index][Thread]

A89: "Transfer" of values between C and ASM




This will be a bit long ...

 > how can l "transfer" register values between my C programming and the library 
 > funcions?  For example, how can l get the output of the userlib::idleloop() 

You can't. From C's perspective there are no registers. The register
keyword means something different and according to the ANSI standard
the compiler can (and does) ignore it at will.
[Sidenote: with gcc's extensions you actually *can* access registers
and more from C but until you become fluent in the trickier parts of
C I strongly suggest not to try it]

The parameter passing mechanism is compiler dependent, but in case of
a default GCC m68k elf/coff setup it is the following:

Parameters passed to a function are placed onto the stack, in reverse
order. This means that if you call foo(a, b, c) then first c then b
and lastly a will be pushed, then jsr foo is executed. The parameters
on the stack are removed by the caller, that is, foo() does *not*
remove them from the stack. The usual C type expansions occur.
(By the way, gcc guarantees that foo() will not modify d2-d7, a2-a7).
If the parameter is a structure, then the structure is copied onto the 
stack.

Return values are returned on a type dependent way:

- Data types that fit within 32 bits are returned in d0
- Data types that fit in 64 bits are returned in d0:d1
- Pointers are returned in a0

Returning structures is a little bit different.
If you have something like this:

struct baz {

	int a, b, c;
	double x, y;
} in, out;

out = foo( in )

the compiler will push a copy of 'in' onto the stack, as expected. 
However, it can't push a copy of an outgoing parameter, so instead of
doing that it will push an implicit pointer to the stack. That is, the 
call out = foo( in ) will be transformed to a foo( &out, in ) form by
the compiler. On the same token, of course, within foo it will do
other tricks to make this transparent to you (effectively re-writing
your code).
If you do not put the returned struct into a location but use it as an 
intermediate in an expression, like bar( foo( in ).x ) then gcc will
create a temporary struct on the stack, pass its address to foo, use
the value from it and remove the structure from the stack;
something akin to this:

bar( foo( in ).x ) turns into

  struct baz *tmp;
  
  tmp = allocate_stack( sizeof baz );
  foo( tmp, out );
  bar( tmp->x );
  release_stack( sizeof baz );
  
where allocate_stack() and release_stack() are internal functions to
the compiler and they do nothing but move the stackpointer back and
forth.

 > whoa!  that's pretty cool.  What would happen if a function returned two 
 > values...would l do
 >  index1,index2 = function();
 > or something else?

In C a function does *not* return two values. By the language
definition a function can return one or none values. The value can be
an aggregate (struct or union) which is returned by the way described
abouve, but it is *one* value even if it has multiple fields. 
[Sidenote: again, the first sidenote applies.]
If you want to return more values, you have to pass pointers of the 
returned variables to the function. That is, instead of:

int val1, val2;
val1,val2 = func();

you do:

int val1, val2;
func( &val1, &val2);
  
 > Also, what would an example of getting a pointer returned be? (instead of a 
 > value, like in d0).  Thanks!

/*
* This function gets an integer array and aninteger value. It returns a
* pointer to the member of the array which holds the value or NULL
* if the value is not in the array.
*/

int *very_dumb_search( int *array, int value, int array_size )
{
int i;

   for ( i=0 ; i < array_size ; i++ )
   
      if ( array[ i ] == value ) 

        return( array + i );
	  
   return( (int *) 0 );
}

 > > list_header *List_Find(list_pointer *pointer, USHORT ID);
 > 
 > what does the * do?
 > 
 > >  You can now do this:
 > >  list *mylist;
 > 
 > is this how to define a list?  and again what does the * do?
 > 
 > >  list_header *alistitem;
 > >  alistitem = List_Find(&mylist, 42);
 > 
 > what is the & for?

I think it is time to go and fetch Kernighan and Ritchie, "The C
Programming Language", second edition at your favourite bookstore.
No offense, but you'd do yourself a favour.

The '*' is the dereference operator and the '&' is the addressof
operator. The '*' also means a pointer in declarations.
Pointer means a *typed* address of something.

   <some_type> *x 
   
declares 'x' as a pointer to a 'some_type' object, that 
is, you declare that x will hold the address of something of the
'some_type' kind.

The '&' operator gets the address of an object:

If you have 

   <some_type> y;

and write

   x = &y;
   
then x will be loaded with the address of y. In assembly terms, it 
will turn into this:

   move.l #y,x
   
The '*' operator is the dereference operator, meaning that you want to 
use the object of which the address is given and not the address
itself. Example:

int an_integer, *a_pointer;

a_pointer = &an_integer;
an_integer = 3;     // an_integer is now 3
an_integer++;       // an_integer is now 4
(*a_pointer)--;     // an_integer is now 3 again
a_pointer--;        // a_pointer is now pointing to sizeof(int) bytes 
                    // before the address of an_integer
(*(++a_pointer))++  // First, the poiner is moved back to point to 
                    // an_integer, then an_integer is incremented

In assembly:

; int an_integer, *a_pointer;

an_integer ds.l 1
a_pointer  ds.l 1

; a_pointer = &an_integer;

   move.l #an_integer,a_pointer
   
; an_integer = 3;  

   move.l #3,an_integer
   
; an_integer++;    

   addq.l   #1,an_integer
   
; (*a_pointer)--;

   move.l  a_pointer,a0
   subq.l  #1,(a0)
   
; a_pointer--;

   subq.l  #4,a_pointer   ; 4 is sizeof int
   
; (*(++a_pointer))++  

   move.l  a_pointer,a0   ; get the pointer
   addq.l  #4,a0          ; increment it by the size of the object pointed
   move.l  a0,a_pointer   ; Store the result and leave it in a0 too
   addq.l  #1,(a0)        ; Increment the pointed object
   
 > alright sorry l think l got it now.  (This may be wrong) but if you do this:

Almost:
 
 > int pointer;
 
If it is a pointer, it must be declared as such. Here you declared an
integer which you call pointer but it is still an integer and not a
pointer.

 > int a;
 > a = 20;
 > pointer = a;

Here is what you have done:

a = 20;      Set the value of 'a' to 20
pointer = a; Set the value of 'pointer' to the value of 'a' (that is,
             pointer is now 20).
			  
 > *pointer = 5
 
Here you get an error message for 'pointer' is actually an integer and 
you can't apply the '*' operator on it.

 > then a will now equal 5 and pointer will "contain" a?

The idea is OK, but the actual way to do it would be:

int *pointer; // The '*' tells the compiler that it is a pointer
int a;
a=20; 
pointer = &a; // The '&' tells the compiler that you want the address of a
*pointer=5;   // Now it does indeed set a to 5.

There are endless further uses for pointers, pointers to pointers and
pointers to functions returning pointers to pointers to ...
On top of that, arrays and pointers have a very intimate relationship, 
for a[n] in c transforms to *(a+n). In fact you could write
n[a], if you really wanted (try, it will work and makes your code
completely uncomprehensible). In turn, in (a+n) 'a' will turn into
&(a[0]). Now this can be further messed up with the fact that almost
always an arrays and pointers to them are interchangable, with the 
notable exception of extern arrays - this little exception can cause 
all sorts of late night debugging :-) 
Throw in the . and -> operators for just to entagle pointers,
structures and unions, then you get the picture.

This simple example is a call to a function which has no parameters
and returns no value and of which the the entry address is stored at 
an address stored at 4 bytes after the start address of the short 
array called APPLIC:

	(**((void(***)(void))((int)APPLIC+sizeof(long))))();

This simply turns to:

        move.l APPLIC+4,%a0
        move.l (%a0),%a0
        jsr (%a0)

This example is not very hairy and is actually from the boot code of a 
real device with a somewhat twisted memory arrangement.

Believe me, the K&R book is *very* good. It is short but very clearly
written and explains these things on a super-understandable way, with
lots of examples. All in all, they invented the language ...
If you want to program in C and use the language properly, you should
have it on your shelf. Do *not* buy the first edition (it is pre-ANSI), 
go for the second one, with a big red ANSI C stamp on the cover.
In addition, try everything out, if you get an error message or a
warning, don't let it go until you understand why did the compiler
issue it. Also, it is a very good thing to see what did the compiler
transform a particular construct to (use the -S switch with gcc).

Regards,

Zoltan



References: