gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Coding > Using hardware sin and cos tables

#7158 - Lupin - Tue Jun 10, 2003 8:17 pm

How could I access the hardware sin and cos tables? I read that they are stored in bios

#7159 - DekuTree64 - Tue Jun 10, 2003 8:43 pm

Use swi 0xe (BG affine set, data is described in the CowBite spec (http://www.cs.rit.edu/~tjh8300/CowBite/CowBiteSpec.htm)
Just set x, y, tx and ty to 0, sx and sy to 256 (that's 1 in fixed point), and theta to the angle you want, which is also 8.8 fixed point. And there's 256 degrees in a circle, not 360. So like, for 90 degrees, set theta to 64 << 8.

Then, as you may or may not know, the way to set up your rotation regs is
pa = cos * x scale
pb = sin * y scale
pc = -sin * x scale
pd = cos * y scale

so if your x/y scale is set to 1, you can take pb for sin, or pa/pd for cos.
But sin(angle) = cos(angle + 1/2 pi) (1/2 pi is 1/4 a revolution in radians, or 90 degrees), so you can just make one table for cos and for sin use table[angle + 64 & 255], because 64 is 1/4 a revolution, assuming your table is 0-255 (you could make your step size for BgAffineSet's theta 128 (which is 1/2 fp) and have a table of 0-511, to double the accuracy if needed, so then you'd use table[angle + 128 & 511] for sin).
Thanks to MrMr[iCE] for using this technique in his Tweakmode demo, and releasing the source to it, because that's where I learned it

#7180 - Lupin - Wed Jun 11, 2003 5:33 pm

erm.... yes.... ?!?!

I will give it up I think :)

#7182 - DekuTree64 - Wed Jun 11, 2003 5:59 pm

Really, it's not that hard. Here's how to do it in code:

Code:


#define cos(x) (trigTable[(x) & 255])
#define sin(x) (trigTable[((x) + 64) & 255])

typedef struct tBGAffineSource {
     s32 x;     //Original data's center X coordinate (8bit fractional portion)
     s32 y;     //Original data's center Y coordinate (8bit fractional portion)
     s16 tX;    //Display's center X coordinate
     s16 tY;    //Display's center Y coordinate
     s16 sX;    //Scaling ratio in X direction (8bit fractional portion)
     s16 sY;    //Scaling ratio in Y direction (8bit fractional portion)
     u16 theta; //Angle of rotation (8bit fractional portion) Effective Range 0-FFFF
} BGAffineSource;

typdef struct tBGAffineDest {
     s16 pa;  //Difference in X coordinate along same line
     s16 pb;  //Difference in X coordinate along next line
     s16 pc;  //Difference in Y coordinate along same line
     s16 pd;  //Difference in Y coordinate along next line
     s32 x;   //Start X coordinate
     s32 y;   //Start Y coordinate
} BGAffineDest;

extern void BgAffineSet(void*, void*, u32); //src, dest, count (more on this later)

BGAffineSource bgSrc[256];
BGAffineDest bgDest[256];
u32 i;
s16 trigTable[256];

for(i = 0; i < 256; i++)
{
   bgSrc.x = 0;
   bgSrc.y = 0;
   bgSrc.tX = 0;
   bgSrc.tY = 0;
   bgSrc.sX = 256;
   bgSrc.sY = 256;
   bgSrc.theta = i << 8;
}

BgAffineSet(bgSrc, bgDest, 256);

for(i = 0; i < 256; i++)
{
   trigTable[i] = bgDest[i].pa;
}


Then in a .s file, put
Code:

.global BgAffineSet
.thumb
.align 2
.thumb_func
BgAffineSet:
swi 0xe
bx lr

And compile it exactly like you would a C file. GCC figures out it's in ASM by the extension.

And you're done.
The args of the C prototype, extern void BgAffineSet(void*, void*, u32); are set up just right for this SWI. r0 is a pointer to the source data structs(s), r1 is the dest data, and r2 is the number of structs you want to calculate. I haven't done any speed tests, but I assume it's faster to have it calculate a whole array of 256 at once than to loop through adding 256 (1 fixed-point) to theta and calling BgAffineSet with the count set to 1.
But anyway, function arguments are pased in the registers r0-r3 (and then pushed onto the stack if you have more than 4, but we only have 3), so you can just put them in the order the SWI needs, and then when you call the function they're already set up. Much easier than doing it with inline ASM.
Sorry if my previous post was too confusing^^

#7183 - Lupin - Wed Jun 11, 2003 6:39 pm

thx, now even I understand it ^^

but Why are there only 256 entries for the trig-table, I thought there should be 360 (each for every degree)?

#7185 - DekuTree64 - Wed Jun 11, 2003 7:01 pm

It just makes it work better with a base-2 number system. You could change your theta step size to (256 << 8) / 360 instead of just (1 << 8) to get 360 degrees, but then you'd have to use % 360 instead of & 255 when looking up values in your table. So basically 90 degrees becomes 64, 45 is 32, 180 is 128, and so on. Just a different proportion.

#7187 - Lupin - Wed Jun 11, 2003 7:37 pm

You help an n00b like me very much - thanks!

#7188 - Lupin - Wed Jun 11, 2003 8:07 pm

I implemented the code, but as soon as BgAffineSet(); gets called the rom hangs up :(

I'm pretty sure that I'm linking the .s file into my code, but somehow it doesn't work.

Here is what I did:


Code:

/*
 MATHLUTS.H
*/

#ifndef MATHLUTS_H
#define MATHLUTS_H

typedef struct tBGAffineSource {
     s32 x;     //Original data's center X coordinate (8bit fractional portion)
     s32 y;     //Original data's center Y coordinate (8bit fractional portion)
     s16 tX;    //Display's center X coordinate
     s16 tY;    //Display's center Y coordinate
     s16 sX;    //Scaling ratio in X direction (8bit fractional portion)
     s16 sY;    //Scaling ratio in Y direction (8bit fractional portion)
     u16 theta; //Angle of rotation (8bit fractional portion) Effective Range 0-FFFF
} BGAffineSource;

typedef struct tBGAffineDest {
     s16 pa;  //Difference in X coordinate along same line
     s16 pb;  //Difference in X coordinate along next line
     s16 pc;  //Difference in Y coordinate along same line
     s16 pd;  //Difference in Y coordinate along next line
     s32 x;   //Start X coordinate
     s32 y;   //Start Y coordinate
} BGAffineDest;

extern void BgAffineSet(void*, void*, u32);

#define cos(x) (trigTable[(x) & 255])
#define sin(x) (trigTable[((x) + 64) & 255])

s16 trigTable[256];

void InitTrigTable(void) {
  BGAffineSource bgSrc[256];
  BGAffineDest bgDest[256];
  u32 i;

  for(i = 0; i < 256; i++) {
   bgSrc[i].x = 0;
   bgSrc[i].y = 0;
   bgSrc[i].tX = 0;
   bgSrc[i].tY = 0;
   bgSrc[i].sX = 256;
   bgSrc[i].sY = 256;
   bgSrc[i].theta = i << 8;
  }

  BgAffineSet(bgSrc, bgDest, 256);

  for(i = 0; i < 256; i++) {
   trigTable[i] = bgDest[i].pa;
  }
}


#endif
//EOF


this header file gets included into my main c++ file, I call the InitTrigTable();-Function right after my Main();-function gets called. These are my compiler options (I'm using an makefile, created by an wizard):

CFLAGS = -I $(INCDIR2) -I $(INCDIR) -I $(SRCDIR) -mthumb-interwork -c -Wall -fverbose-asm
SFLAGS = -I $(INCDIR2) -I $(INCDIR) -mthumb-interwork
LDFLAGS = -L $(LIBDIR) -L $(LIBDIR2) -T LinkScript


I'm so sorry that I need your help once again, just because of my stupidity :(

#7190 - DekuTree64 - Wed Jun 11, 2003 9:25 pm

Hmm, well I typed that code out pretty fast, so there could be something wrong with it, but I went and looked at the code on my programming computer, and it does look just about the same, so it seems like it would work. Those structs are pretty big though, so with 256 of each, it could be running out of IWRAM. In mine, I used EWRAM for the arrays, so maybe try that. If you don't have anything else in EWRAM, just use
Code:

BGAffineSource *bgSrc = (BGAffineSource *)0x2000000;
BGAffineDest *bgDest = (BGAffineDest *)(0x2000000 + (sizeof(BgAffineSource) << 8));

And leave the rest the same.
Then if that doesn't work, try changing the ASM function to
Code:

.global BgAffineSet
.arm
.align 4
BgAffineSet:
swi 0xe0000
bx lr

Which does the same thing, but in ARM, which I think is what your main function is compiled with, since it doesn't specify, and I'm pretty sure it's the default, and it may not be interworking correctly (it should though).

Then if all else fails, do them one at a time, which would be someting like
Code:

void InitTrigTable(void) {
  BGAffineSource bgSrc;
  BGAffineDest bgDest;
  u32 i;

  for(i = 0; i < 256; i++) {
   bgSrc.x = 0;
   bgSrc.y = 0;
   bgSrc.tX = 0;
   bgSrc.tY = 0;
   bgSrc.sX = 256;
   bgSrc.sY = 256;
   bgSrc.theta = i << 8;
   BgAffineSet(&bgSrc, &bgDest, 1); //use & because they're no longer arrays, so you need to get the address
   trigTable[i] = bgDest.pa;
  }
}


And if that STILL doesn't work, post again and I'll bang my head on the wall a few times and see if I realize anything wrong with it^_^

#7337 - Lupin - Sun Jun 15, 2003 1:32 pm

thx! :)