A89: This sprite code in C


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

A89: This sprite code in C




 > sprite0(x,y)
 >     int x;
 >     int y;
 > {
 > asm("
 >     move.w 8(%sp),%d0
 >     move.w 10(%sp),%d1
 >     lea sprite(PC),%a0  this command works if l remove the (PC), but l need 
 > to put (PC) after sprite and the compiler doesn't like it.

A few problems here:

- It should be sprite(%pc), gcc requires the % to prefix register
  names and it is case sensitive.
 
- sprite(%pc) will *not* change the pc. It is merely a relative
  addressing form. Instead of storing the absolute address of sprite
  in the instruction, it will store the difference between the PC and
  sprite. It does *not* change the PC. Therefore, your program will
  blow up as soon as it executes the lea: it will fetch the next insn
  from sprite. Boom. 
  Note that it is not gcc related, executing data structures usually
  bombs the code regardless of the language: switching between C, 
  assembler or typing hex numbers directly into memory has no effect 
  on the result ...
  
 > sprite: dc.w    8
 >     dc.w    1
 >     dc.b    %00000000   having this line returns "bad expression."  l guess 
 > it's maybe because % is used to signal a register, but how do l do this 
 > command now?

Under gcc/gas you can not use % for binary and $ for hex constants.
You don't have binary constants, you have decimal, octal and
hexadecimal constants which you write exactly the same way as if they 
were C constants. That is, ten would be 10 or 012 or 0xa.

 > mask:   dc.b    %00000000   same here.
 > 
 >     ");
 > put_sprite();
 > }

While in this particular case the C wrapper over an assembly function
would work this way, assuming that put_sprite does not destroy any
registers but d0, d1, a0, a1. However, this solution is anything but 
efficient. A better way might be this:
(You put this into a header file or, if you only have one source file, 
at the beginning of that)

static inline void sprite( int x, int y, void *data )
{
register int   xx asm ( "d0" ) = x;
register int   yy asm ( "d1" ) = y;
register void *pp asm ( "a0" ) = data;

   asm volatile ( "
   
      jsr put_sprite
  
    " :                                /* No output parameters        */
      : "d" (xx), "d" (yy), "a" (pp)   /* xx, yy and pp are inputs    */
      : "r1", "r2" ...                 /* List of clobbered registers */
   );
}

where "r1", "r2", ... are the list of registers (without the %) that 
put_sprite changes, if any. If put_sprite preserves all registers then
you just leave it empty, that is, you omit the : and everything after
it until the ); bit. Let's say put_sprite destroys d2 (I don't
know, it's for the example), so it is actually

 : "d2" /* List of ... */

Now with this one if you write this:

foo()
{
   /* Here would be some code */
   
   sprite( 10, 20, &mysprite );
	
   /* here comes yet some other code */
}
 
this is what compiles from it (with -O2), with my comments of what
is what:

foo:
        link.w %a6,#0
        move.l %d2,-(%sp) /* d2 is saved for put_sprite destroys it */

    /* Here is where the some code would be */
    
        moveq.l #10,%d0
        moveq.l #20,%d1
        lea mysprite,%a0
        
    /* All that push, call, pop etc. business is removed and 
       substituted with parameters being loaded where they should
       be: 10 to d0, 20 to d1 and the address of mysprite to a0.
	   It is quite a significant saving in speed and code size ! */
	   
#APP
      jsr put_sprite
#NO_APP
    
	/* here's where the yet some other code would be */
        
		move.l -4(%a6),%d2 /* d2 is restored */ 
        unlk %a6
        rts

Now the only question remaining is how do you store your sprites in a C
fashion. Well, easily:

#define SPRITE( name, width, height ) \
  const struct { \
   unsigned short w, h; \
   char shape[(((width)+7)/8)*(height)]; \
   char mask[(((width)+7)/8)*(height)]; \
  } name = { (width),(height),
  
Then you can write this:

SPRITE( sprite0, 8, 1 )
   { 0x3c },
   { 0x7e }
};

This will compile to:

sprite0:
        .word 8
        .word 1
        .byte 60
        .byte 126

Note that .word is the same as dc.w and .byte is dc.b.
Say you want a 16x4 sprite, you would write:

SPRITE( bigsprite, 16, 4 )

  { 0xff, 0xff,
    0x80, 0x01,
    0x80, 0x01,
    0xff, 0xff },
	
  { 0xff, 0xff,
    0xff, 0xff,
	0xff, 0xff,
	0xff, 0xff }
};

which will generate:

bigsprite:
        .word 16
        .word 4
        .byte -1
        .byte -1
        .byte -128
        .byte 1
        .byte -128
        .byte 1
        .byte -1
        .byte -1
        .byte -1
        .byte -1
        .byte -1
        .byte -1
        .byte -1
        .byte -1
        .byte -1
        .byte -1
	
That would do, wouldn't it ?

Regards,

Zoltan



References: