From: Ross Ridge <ross@zooid.guild.org>
Subject: Re: djgpp 2.2.2 Q's: mouse/svga/assembler/bios
Organization: ZOOiD BBS
Date: Tue, 8 Sep 1992 09:01:32 GMT

benrg@ocf.berkeley.edu (Ben Rudiak-Gould) writes:
>o  Register names like %bl and %cx are not used; instead, the word size
>   is indicated by a letter (`b', `w', or `l') appended to the instruction
>   mnemonic.  (This is not done for operations like shrd, where there is
>   no possibility of confusion.)

This isn't true, registers names like %bl and %cx are used.  The letter
suffixes are used instead of the BYTE PTR, WORD PTR, etc... constructs
of MASM.  Suffixes are always required, just not when there is an ambiguity.

>For example, the following function will multiply two fixed-point
>integers (each with 16 bits of integer and 16 bits of fraction), and
>return their product as a third:
>
>int fixed_mul(int a, int b)
>{
>	asm("movl 8(%ebp),%eax");	/* get operands from stack into %eax */
>	asm("movl 12(%ebp),%edx");	/* and %edx (%ebp is set up for us) */
>	asm("mull %edx");		/* do the multiplication */
>	asm("shrd $16,%edx,%eax");	/* shift off lower 16 bits from */
>					/* 64-bit result */
>
>}		/* result is in %eax, which is where djgpp wants it */
>
>Note that I deduced the assembly notation from looking at examples, and
>the inline notation by trial and error.  If anybody knows of any official
>documentation for this (in the full gcc package?) let me know.

The many uses of the "asm" keyword are all described in the in the
GCC documentation.  You'll also need the to look at the machine
description files for your CPU because it's very machine dependent.

I only have GCC 1.37.1 to work with, so things might be different
with GCC 2.2.2, but the way you should define the fixed_mul function
is like this:

int 
fixed_mul(int a, int b) {
	int ret;

	asm("mull %2;shrd $16,%%edx,%0"
/* %0 is replaced with the first operand, %2 is replaced with
   the third operand.  A semicolon (;) seperated assembly
   statments */
	    : "=a" (ret)
/* the constraints on the first operand, an output operand.  The equals
   sign (=) means that this operand is written to, the letter 'a' means that
   this operand must be the EAX register.  In brackets is an lvalue that
   will be loaded with result */
	    : "0" (a),
/* the constraints on the second operand, an input operand.  The digit
   '0' means that this operand must be the same the first operand.
   In backets is the value of this operand. */
	      "rm" (b)
/* the constraints on the third operand, an input operand.  The letter
   'r' means that it can be a register, the letter 'm' means that it
   can also be a memory reference. */
	    : "dx");
/* this means that the EDX register is clobberd by this statement */

	return ret;
}

This generates the following code, one instruction shorter with my
version of GCC:

	pushl %ebp
	movl %esp,%ebp
	movl 8(%ebp),%eax
	mull 12(%ebp)
	shrd $16,%edx,%eax
	leave
	ret

or in Intel syntax:

	push	ebp
	mov	ebp,esp
	mov	eax,[ebp+0x8]
	mul	[ebp+0xc]
	shrd	eax,edx,0x10
	leave
	ret

By using operands and constraints we're able to tell GCC about
all the side effects of the asm statement, and have it load everything
so we don't have to make any assumption on calling conventions.
We can easily put it into a macro so it can be used inline:

#define fixed_mul(a, b) ({ int __result;			\
	__asm__("mull %2;shrd $16,%%edx,%0":			\
	"=a" (_t): "0" ((int)(a)), "rm" ((int)(b)): "dx"); 	\
	__result })

							Ross Ridge

-- 
Ross Ridge - The Great HTMU					     l/	 //
								    [OO][oo]
ross@zooid.guild.org						    /()\/()/
uunet.ca!zooid!ross						     db	 //
Newsgroups: comp.os.msdos.programmer