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: