www.delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/1996/07/24/18:00:29

Xref: news2.mv.net comp.os.msdos.djgpp:6369
From: korpela AT albert DOT ssl DOT berkeley DOT edu (Eric J. Korpela)
Newsgroups: comp.os.msdos.djgpp
Subject: Re: Referencing local variables with inline assembly.
Date: 24 Jul 1996 18:31:01 GMT
Organization: Cal Berkeley-- Space Sciences Lab
Lines: 121
Message-ID: <4t5q55$5al@agate.berkeley.edu>
References: <Dv208u DOT Izs AT ftp DOT jags DOT co DOT uk>
NNTP-Posting-Host: albert.ssl.berkeley.edu
To: djgpp AT delorie DOT com
DJ-Gateway: from newsgroup comp.os.msdos.djgpp

In article <Dv208u DOT Izs AT ftp DOT jags DOT co DOT uk>,
Luke Steele  <luke AT jaglogic DOT demon DOT co DOT uk> wrote:
>Hi,
>	How do you reference local variables with inline assembly?  I _have_
>read the GCC documentation on the subject, but I cannot work out what
>to do.  A few lines of code demonstating how to do this would be
>great.
>	Secondly:  Which segment registers are used by the compiler, and so
>which ones can I use without worrying about their values becoming
>corrupted?

The answer to both your questions is the Extended Assembly format.
A good tutorial is at "http://www.pegasuz.com/binky/attasm.htm".
The basic format of extended assembly is ....

 __asm__ ( "assembly code" : output list : input list : clobbered registers );

To use the input and output lists properly you need to use appropriate
macros (%0, %1, %2, ...) which correspond to the first, second, third ...
parameters in the output and input lists.  

You also need to understand the operand codes.  They are

"r" any general purpose register (eax,ebx,ecx,edx,esi,edi)
"q" any of (eax,ebx,ecx,edx)
"a","b","c","d","S","D"  (eax,ebx,ecx,edx,esi,edi) respectively
"A"  a 64 bit register pair (i.e. edx:eax)
"f"  a floating point register 
"t"  first fp register (top of floating point stack)
"u"  second fp register 
"m"  a memory operand
"i"  an immediate operand
"0" "1" "2" ...  (use the same operand as %0

an "=" sign must be used in output operands.

So here's an example done several ways...

-------------------------------------------------------------------------
inline int multiply_by_5(int x)
{
  int x_times_5;
  __asm__ ("leal (%1,%1,2),%0",
           : "=a" (x_times_5) /* use the eax register and store the result in */
                              /* the variable x_times_5                       */
           : "b" (x) /* use the ebx register and load it with the variable x  */
           )  /* no clobbered registers other than above */
  return(x_times_5);
}
-------------------------------------------------------------------------

if x and x_times_5 were global variables the above would compile as...

movl _x,%eax
leal (%eax,%eax,2),%ebx
movl %ebx,_x_times_5

if the eax and ebx registers need to be saved gcc will do it automatically.

Here it is another way .............. 
-------------------------------------------------------------------------
inline int multiply_by_5(int x)
{
  int x_times_5;
  __asm__ ("leal (%1,%1,2),%0",
           : "=r" (x_times_5) /* use any register and store the result in     */
                              /* the variable x_times_5                       */
           : "0" (x) /* use the same register and load it with the variable x */
           )  /* no clobbered registers other than above */
  return(x_times_5);
}
-------------------------------------------------------------------------
This would probably compile as (again if _x and _x_times_5 were global)

movl _x,%edx
leal (%edx,%edx,2),%edx
movl %edx,_x_times_5

Again, whatever register is used will be saved if necessary.
One more time.....
-------------------------------------------------------------------------
inline int multiply_by_5(int x)
{
  int x_times_5;
  __asm__ ("leal (%1,%1,2),%0
            xor %%ebx,%%ebx", /* clobber a register for no reason             */
           : "=a" (x_times_5) /* use eax register and store the result in     */
                              /* the variable x_times_5                       */
           : "0" (x) /* use the same register and load it with the variable x */
           : "ebx" ); /* we clobbered the ebx register for no reason          */
  return(x_times_5); 
}
--------------------------------------------------------------------------

In this case because we clobbered the ebx register we had to tell the compiler
about it by adding it to the clobbered register list.  If it's in the clobbered
list gcc will save and restore it's value if necessary.  (It actually rarely
is).

There are a couple other things you may need to add to you clobbered list.
One is "memory".  If you explicitly write to a global variable you should
add "memory" to the clobbered list.  Otherwise the compiler could keep its
value cached in a register during your assembly code.  It's also possible
that you could run into trouble if the compiler expects the condition codes
to be the same when your assebly is complete.  If so you need to add "cc" to
your clobbered list.

As far as segment registers go, there are no codes for them so you'll
need to add them to your clobbered list if you change them.  If you change
a segment register make sure you're loading it with a valid descriptor,
otherwise you get SIGSEGVs up the yin-yang. (It's protected mode after all.)
Only play with segments if it's absolutely necessary.

Hope this helped.

Eric
-- 
Eric Korpela                        |  An object at rest can never be
korpela AT ssl DOT berkeley DOT edu            |  stopped.
<a href="http://www.cs.indiana.edu/finger/mofo.ssl.berkeley.edu/korpela/w">
Click here for more info.</a>

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019