[net.unix-wizards] Weird code

justice@isucs1.UUCP (10/15/85)

Ok you wizards out there, why does this program do what it does?
Here are a couple of hints:
	1) It only works on Vaxes
	2) The same idea is used to initialize the UNIBUS adapters
--------------Here is a script showing the details------------------------
Script started on Mon Oct 14 14:08:05 1985
% cat reg.c
main()
{
        register int x, y;
        int i;
        int func();

        fix((int *)func);
        x = 1; y = 5;
        for (i = 1; i <= 10; i++) {
                func(1,5);
                printf("x = %d, y = %d\n",x,y);
        }
}

func(x,y)
int x,y;
{
        register int a,b;

        a += a; b *= 5;
        x--; y++;
}

fix(f)
int *f;
{
        *f &= ~0xc00;
}
% cc reg.c -N -o reg
% reg
x = 2, y = 25
x = 4, y = 125
x = 8, y = 625
x = 16, y = 3125
x = 32, y = 15625
x = 64, y = 78125
x = 128, y = 390625
x = 256, y = 1953125
x = 512, y = 9765625
x = 1024, y = 48828125
% exit
% 
script done on Mon Oct 14 14:08:29 1985
--------------------------------------------------------------------------
Good luck!
Brian Justice, Iowa State University
UUCP: {okstate||umn-cs||csu-cs}!isucs1!justice	 CSNET: justice@iowa-state

laman@ncr-sd.UUCP (Mike Laman) (10/17/85)

In article <468@isucs1.UUCP> justice@isucs1.UUCP writes:
>
>Ok you wizards out there, why does this program do what it does?
>Here are a couple of hints:
>	1) It only works on Vaxes
	:
>main()
	:
>	int func();
>
>	fix((int *) func);
	:
>fix(f)
>int *f;
>{
>        *f &= ~0xc00;
>}
	:

Someone else will probably answer this before me -- we always seem to lag
behind on the news.  I apologize if someone else has answered it "already".

The routine "fix" is modifying a mask that tells the "calls" (and friends)
instruction which registers (if any) to save ON A VAX 11/780 (and friends).
The mask is kept in the "first word" of the function which is where "func"
points.  "Fix" is changing the mask so registers 11 and 10 will NOT be
saved/restored across function calls.  On the VAX the UNIX C compiler allocates
register 11 for the first register variable, then decreasing number in order of
allocation of registers.  This means the first two register variables
(if the compiler see fit to use them as registers -- that's another
story) will get overwritten in the calling routine ("main") every time it
calls the routine that was "fixed", namely "func".  In other words "func"
is modifying "main"'s register variables.

And I thought all that assembly I played with a year ago would be just
a useless effort in futility :-).

		Mike Laman, NCR Rancho Bernardo
		UUCP: {ucbvax,philabs,sdcsla}!sdcsvax!ncr-sd!laman

ghr@wucs.UUCP (George Robbert) (10/19/85)

>Ok you wizards out there, why does this program do what it does?
>Here are a couple of hints:
>	1) It only works on Vaxes
>	2) The same idea is used to initialize the UNIBUS adapters
>main()
>{
>        register int x, y;
>        int i;
>        int func();
>
>        fix((int *)func);
>        x = 1; y = 5;
>        for (i = 1; i <= 10; i++) {
>                func(1,5);
>                printf("x = %d, y = %d\n",x,y);
>        }
>}
>func(x,y)
>int x,y;
>{
>        register int a,b;
>
>        a += a; b *= 5;
>        x--; y++;
>}
>
>fix(f)
>int *f;
>{
>        *f &= ~0xc00;
>}
>% cc reg.c -N -o reg
>% reg
>x = 2, y = 25
>x = 4, y = 125
>x = 8, y = 625
>x = 16, y = 3125
>x = 32, y = 15625
>x = 64, y = 78125
>x = 128, y = 390625
>x = 256, y = 1953125
>x = 512, y = 9765625
>x = 1024, y = 48828125
>Brian Justice, Iowa State University
>UUCP: {okstate||umn-cs||csu-cs}!isucs1!justice	 CSNET: justice@iowa

The reason that you get the strange behavior of x and y is due
to fix's clearing 2 bits of the register save mask for func().
All functions on the VAX begin with a word (16 bit) that is the register 
save mask.  This word indicates which registers should be pushed on
the stack at function call time and restored on return.  Bit n set
corresponds to saving register n.  Therefore, *f &= ~0xc00; clears the 
mask for r10 and r11.  These just happen to be the two registers that
x and y are stored in.  They also happen to be the ones that a and b
are stored in.  Thus, since they are not saved and restored when
func(1,5) is called, the changes to a and b are actually changes to 
x and y.  This is why the code does what it does on the VAX.  I don't 
know why they do this to initialize unibus adapters. (except maybe
to minimize function call overhead by reducing the registers saved &
restored at each call.)

George Robbert,  Washington University
ghr@wucs.UUCP		...!ihnp4!wucs!ghr

art@ACC.ARPA (10/24/85)

>>Ok you wizards out there, why does this program do what it does?
>>Here are a couple of hints:
>>	1) It only works on Vaxes
>>	2) The same idea is used to initialize the UNIBUS adapters
>>main()
>>{
>>        register int x, y;
>>        int i;
>>        int func();
>>
>>        fix((int *)func);
>>        x = 1; y = 5;
>>        for (i = 1; i <= 10; i++) {
>>                func(1,5);
>>                printf("x = %d, y = %d\n",x,y);
>>        }
>>}
>> ......

> ....
>This is why the code does what it does on the VAX.  I don't 
>know why they do this to initialize unibus adapters. (except maybe
>to minimize function call overhead by reducing the registers saved &
>restored at each call.)

This is done by autoconfigure when calling the probe() routines in
device drivers.  The probe routine is supposed to make the device
cause an interrupt.  An interrupt catcher figures out what vector
and priority level the interrupt used and places these in r10 and
r11.  When the interrupt catcher exits, the probe routine sees the
local variables cvec and br (which "just happen" to be in r10 and r11)
magically change.  The purpose of the entry mask "fix" is to prevent
the values in r10 and r11 from being destroyed when probe returns
to autoconfigure.  Autoconfigure has also defined cvec and br to
be local variables in r10 and r11 so these end up reflecting what
probe got.

I've always felt that this is a real kludge in 4.2 that is very
machine and even compiler dependent.  I wonder why globals were
not defined for this purpose.  Oh well, this is only one of my
irritations with 4.2's driver environment.

				"Art Berggreen"<Art@ACC.ARPA>

------

narten@purdue.ARPA (Thomas Narten) (10/24/85)

>I've always felt that this is a real kludge in 4.2 that is very
>machine and even compiler dependent.  I wonder why globals were
>not defined for this purpose.  Oh well, this is only one of my
>irritations with 4.2's driver environment.

Blatant machine and compiler dependencies are not limited to driver or
even kernel code! The following line appears in the middle of ld.c with
no comments anywhere explaining what is going on.

		asm("movc3 r8,(r11),(r7)");

At the very least, an ``ifdef vax'' would be appropriate. How would you
like to port this kind of undocumented stuff to a new machine?

Thomas Narten
----------