I had the idea just now to perhaps create a nice library full of graphical/IO routines, useful defines/renames, etc. that would make game programming on the PRIZM even easier. I think it would be best as a community effort, since many others have far more knowledge than me about the prizm and it's inner workings, so me doing it alone won't get it far. For now, it would probably start by just pitching in any *easy to use* routines you have that you think should be included. I have a few small routines I have that may be useful (drawing rectangles, circles, polygons, etc.) that I'll add in later today, and I know Kerm has a million routines floating around that hopefully he'll add and perhaps polish to make even easier to work with for beginners Smile

What do you guys think of this idea?

EDIT: extra incentive of compiling this into a beginners' library: likely a lot of extra games for the Prizm will result out of this, and perhaps contest 8 will see more entries in the Prizm section Smile
I've got quite a few routines that I've been moving from file to file. Most of them are pretty self-explanatory, and some may only make sense if Kerm releases his floating point library. All the angles are in degrees (as that's what the FP library uses). Some are out-right taken straight from Wikipedia, but I did my best to cite where I got everything from. I can take parts of it out if anyone wants me to.

Also, I'll be the first to say that some of these are terrible, and that I don't know how to structure a *.h and *.c set of files, so... Yeah.

library.h:
Code:
#define LCD_WIDTH_PX 384
#define LCD_HEIGHT_PX 216
#define LCD_MIDDLE_X (LCD_WIDTH_PX / 2)
#define LCD_MIDDLE_Y (LCD_HEIGHT_PX / 2)
#define Flip Bdisp_PutDisp_DD
#define memcpy(dest, src, size) int abcd; for(abcd=0;abcd<size;abcd++) dest[abcd] = src[abcd];
#define abs(x) ((x) < 0 ? -(x) : (x))
#define swap(x, y) { x = x + y; y = x - y; x = x - y; }
#define square(x) ((x) * (x))
//
void CopySprite(char* data, int x, int y, int width, int height);
void XORSprite(char* data, int x, int y, int width, int height);
void Line(int x0, int y0, int x1, int y1, color_t color);
void Plot(int x, int y, color_t color);
color_t Get(int x, int y);
void Arc(int startangle, int endangle, int dist, int width, color_t color);
void Bucket(int x, int y, color_t targetcolor, color_t replacecolor);
void ClearScreen();
void FillScreen(color_t color);
void FilledCircle(int centerx, int centery, int radius, color_t color);
void FilledArc(int centerx, int centery, int radius, int startangle, int endangle, color_t color);
void FilledCheckLine(int x0, int y0, int x1, int y1, fp slopeleft, fp sloperight, color_t color);
int PRGM_GetKey();
int tandeg(int angle);
unsigned int rand(void);
unsigned int srandom(int seed);
int GetX(int centerx, int angle, int dist);
int GetY(int centery, int angle, int dist);
void FilledCircleAngle(int centerx, int centery, int angle, int dist, int radius, color_t color);

library.c:

Code:
// KermMartian
void XORSprite(char* data, int x, int y, int width, int height) {
  int j, i;
  char* VRAM = (char*)0xA8000000;
  VRAM += 2*(LCD_WIDTH_PX*y + x);
  for(j=y; j<y+height; j++) {
    for(i=x; i<x+width;  i++) {
      *(VRAM++) ^= *(data++);
      *(VRAM++) ^= *(data++);
    }
    VRAM += 2*(LCD_WIDTH_PX-width);
  }
}

// KermMartian
void CopySprite(char* data, int x, int y, int width, int height) {
  int j;
  char* VRAM = (char*)0xA8000000;
  VRAM += 2*(LCD_WIDTH_PX*y + x);
  for(j=y; j<y+height; j++) {
    memcpy(VRAM,data,2*width);
    VRAM += 2*LCD_WIDTH_PX;
    data += 2*width;
  }
}
// Catherine
void Line(int x0, int y0, int x1, int y1, color_t color) {
  Plot(x0, y0, color);
  Plot(x1, y1, color);
  int steep = abs(y1 - y0) > abs(x1 - x0);
  if (steep) {
    swap(x0, y0);
    swap(x1, y1);
  }
  if (x0 > x1) {
    swap(x0, x1);
    swap(y0, y1);
  }
  int deltax = x1 - x0;
  int deltay = abs(y1 - y0);
  int error = deltax / 2;
  int ystep;
  int y = y0;
  int x;
  if (y0 < y1)
    ystep = 1;
  else
    ystep = -1;
  for(x=x0;x<x1;x++) {
    if (steep)
      Plot(y, x, color);
    else
      Plot(x, y, color);
    error -= deltay;
    if (error < 0) {
      y += ystep;
      error += deltax;
    }
  }
}
// Catherine
void Plot(int x, int y, color_t color) {
  short* VRAM = (short*)0xA8000000;
  VRAM[LCD_WIDTH_PX*y + x] = color;
}
// Catherine
color_t Get(int x, int y) {
  short* VRAM = (short*)0xA8000000;
  return VRAM[LCD_WIDTH_PX*y + x];
}
// Catherine
void Arc(int startangle, int endangle, int dist, int width, color_t color) {
  while (startangle > endangle)
    endangle += 360;
  int angle;
  int cos;
  int sin;
  int halfwidth = fp_convert(0, 14, width / 2);
  width = fp_convert(0, 14, width);
  dist = fp_convert(0, 14, dist);
  int lastcos = cosdeg(startangle % 360);
  int lastsin = sindeg(startangle % 360);
  Line(LCD_WIDTH_PX / 2 + fp_convert(14, 0, fp_mul(dist, lastcos, 14)),
       LCD_HEIGHT_PX / 2 + fp_convert(14, 0, fp_mul(dist, lastsin, 14)),
       LCD_WIDTH_PX / 2 + fp_convert(14, 0, fp_mul(dist + width, lastcos, 14)),
       LCD_HEIGHT_PX / 2 + fp_convert(14, 0, fp_mul(dist + width, lastsin, 14)),
       color);
  for(angle=startangle; angle<endangle; angle++) {
    cos = cosdeg((angle + 360) % 360);
    sin = sindeg((angle + 360) % 360);
    Line(LCD_WIDTH_PX / 2 + fp_convert(14, 0, fp_mul(dist, cos, 14)),
    LCD_HEIGHT_PX / 2 + fp_convert(14, 0, fp_mul(dist, sin, 14)),
    LCD_WIDTH_PX / 2 + fp_convert(14, 0, fp_mul(dist, lastcos, 14)),
    LCD_HEIGHT_PX / 2 + fp_convert(14, 0, fp_mul(dist, lastsin, 14)),
    color);
    Line(LCD_WIDTH_PX / 2 + fp_convert(14, 0, fp_mul(dist + width, cos, 14)),
    LCD_HEIGHT_PX / 2 + fp_convert(14, 0, fp_mul(dist + width, sin, 14)),
    LCD_WIDTH_PX / 2 + fp_convert(14, 0, fp_mul(dist + width, lastcos, 14)),
    LCD_HEIGHT_PX / 2 + fp_convert(14, 0, fp_mul(dist + width, lastsin, 14)),
    color);
    lastcos = cos;
    lastsin = sin;
  }
  Line(LCD_WIDTH_PX / 2 + fp_convert(14, 0, fp_mul(dist, lastcos, 14)),
       LCD_HEIGHT_PX / 2 + fp_convert(14, 0, fp_mul(dist, lastsin, 14)),
       LCD_WIDTH_PX / 2 + fp_convert(14, 0, fp_mul(dist + width, lastcos, 14)),
       LCD_HEIGHT_PX / 2 + fp_convert(14, 0, fp_mul(dist + width, lastsin, 14)),
       color);
  if (startangle != endangle) {
    lastcos = LCD_WIDTH_PX / 2 + fp_convert(14, 0,
                   fp_mul(dist + halfwidth,
                     cosdeg(((startangle + endangle) / 2) % 360),
                     14));
    lastsin = LCD_HEIGHT_PX / 2 + fp_convert(14, 0,
                    fp_mul(dist + halfwidth,
                      sindeg(((startangle + endangle) / 2) % 360),
                      14));
    Bucket(lastcos, lastsin, Get(lastcos, lastsin), color);
  }
}
// Catherine
void Bucket(int x, int y, color_t targetcolor, color_t replacecolor) {
  if (x < 0 || x >= LCD_WIDTH_PX || y < 0 || y >= LCD_HEIGHT_PX)
    return;
  if (Get(x, y) == targetcolor) {
    Plot(x, y, replacecolor);
    Bucket(x - 1, y, targetcolor, replacecolor);
    Bucket(x, y - 1, targetcolor, replacecolor);
    Bucket(x + 1, y, targetcolor, replacecolor);
    Bucket(x, y + 1, targetcolor, replacecolor);
  }
}
// Catherine
void ClearScreen() {
  FillScreen(COLOR_WHITE);
}
// Catherine
void FillScreen(color_t color) {
  int j;
  short* VRAM = (short*)0xA8000000;
  for(j=0;j<LCD_WIDTH_PX * LCD_HEIGHT_PX;j++)
    VRAM[j] = color;
}
// Wikipedia/Bresenham
void FilledCircle(int centerx, int centery, int radius, color_t color) {
  int f = 1 - radius;
  int ddF_x = 1;
  int ddF_y = -2 * radius;
  int x = 0;
  int y = radius;
 
  Line(centerx, centery + radius, centerx, centery - radius, color);
  Line(centerx + radius, centery, centerx - radius, centery, color);
 
  while(x < y)
    {
      // ddF_x == 2 * x + 1;
      // ddF_y == -2 * y;
      // f == x*x + y*y - radius*radius + 2*x - y + 1;
      if(f >= 0)
   {
     y--;
     ddF_y += 2;
     f += ddF_y;
   }
      x++;
      ddF_x += 2;
      f += ddF_x;   
      Line(centerx + x, centery + y, centerx - x, centery + y, color);
      Line(centerx + x, centery - y, centerx - x, centery - y, color);
      Line(centerx + y, centery + x, centerx - y, centery + x, color);
      Line(centerx + y, centery - x, centerx - y, centery - x, color);
    }
}
// Wikipedia/Bresenham
void FilledArc(int centerx, int centery, int radius, int startangle, int endangle, color_t color) {
  int f = 1 - radius;
  int ddF_x = 1;
  int ddF_y = -2 * radius;
  int x = 0;
  int y = radius;
 
  int slopeleft = fp_div(fp_mul(fp_convert(0, 14, radius),
            sindeg(startangle),
            14),
          fp_mul(fp_convert(0, 14, radius),
            cosdeg(startangle),
            14),
          14);
  int sloperight = fp_div(fp_mul(fp_convert(0, 14, radius),
             sindeg(endangle),
             14),
           fp_mul(fp_convert(0, 14, radius),
             cosdeg(endangle),
             14),
           14);

  FilledCheckLine(centerx, centery + radius, centerx, centery - radius, slopeleft, sloperight, color);
  FilledCheckLine(centerx + radius, centery, centerx - radius, centery, slopeleft, sloperight,  color);
 
  while(x < y)
    {
      // ddF_x == 2 * x + 1;
      // ddF_y == -2 * y;
      // f == x*x + y*y - radius*radius + 2*x - y + 1;
      if(f >= 0)
   {
     y--;
     ddF_y += 2;
     f += ddF_y;
   }
      x++;
      ddF_x += 2;
      f += ddF_x;
      FilledCheckLine(centerx + x, centery + y, centerx - x, centery + y, slopeleft, sloperight, color);
      FilledCheckLine(centerx + x, centery - y, centerx - x, centery - y, slopeleft, sloperight, color);
      FilledCheckLine(centerx + y, centery + x, centerx - y, centery + x, slopeleft, sloperight, color);
      FilledCheckLine(centerx + y, centery - x, centerx - y, centery - x, slopeleft, sloperight, color);
    }
}
// Wikipedia
void FilledCheckLine(int x0, int y0, int x1, int y1, fp slopeleft, fp sloperight, color_t color) {
  int slope = fp_div(fp_convert(0, 14, y0),
           fp_convert(0, 14, x0),
           14);
  int slope2 = fp_div(fp_convert(0, 14, y1),
            fp_convert(0, 14, x1),
            14);
  if (slope >= slopeleft && slope <= sloperight && slope2 >= slopeleft && slope2 <= sloperight)
    Line(x0, y0, x1, y1, color);
}
// MiniSDK
int PRGM_GetKey() {
  unsigned char buffer[12];
  PRGM_GetKey_OS( buffer );
  return ( buffer[1] & 0x0F ) * 10 + ( ( buffer[2]  & 0xF0 )  >> 4 );
}
// Catherine
int tandeg(int angle) {
  return sindeg(angle) / cosdeg(angle);
}
// KermMartian
static unsigned int lastrandom=0x12345678;
unsigned int rand(void) {
  return srandom(0);
}
// KermMartian
unsigned int srandom(int seed){
  if (seed) lastrandom=seed;
  lastrandom = ( 0x41C64E6D*lastrandom ) + 0x3039;
  return ( lastrandom >> 16 );
}
// Catherine
int GetX(int centerx, int angle, int dist) {
  return centerx + fp_convert(14, 0, fp_mul(fp_convert(0, 14, dist),
                   cosdeg(angle),
                   14));
}
// Catherine
int GetY(int centery, int angle, int dist) {
  return centery + fp_convert(14, 0, fp_mul(fp_convert(0, 14, dist),
                   sindeg(angle),
                   14));
}
// Catherine
void FilledCircleAngle(int centerx, int centery, int angle, int dist, int radius, color_t color) {
  FilledCircle(GetX(centerx, angle, dist),
          GetY(centery, angle, dist),
          radius,
          color);
}
If you want help with building the .a file and other such things I can help you through it once I update it to use the SDK's new Makefile format. Or you can just look up how devkitpro libraries are setup as that is basically what I am copying. Wink
Correct me if I am wrong, but are those all non-buffered functions?
Well, Catherine asked if I could throw together some quick Prizm File I/O routines that would make working with files easier, so here they are:


Code:
typedef file int

file open_file(char*name, int mode) {
    char*n = malloc(256*sizeof(unsigned short));
    char*n2 = malloc(strlen(name)+7*sizeof(char));
    *(sizeof(char)*7+strlen(name)+n+1) = 0x00;
    memcpy(n, "\\\\fls0\\", 7*sizeof(char));
    memcpy(sizeof(char)*7+n, name, strlen(name));
    Bfile_StrToName_ncpy((const unsigned short*)n2, n, strlen(n));
    free(n);
    name = n2;
    free(n2);
    return Bfile_OpenFile_OS(n2, mode);
}

#define close_file Bfile_CloseFile_OS(handle)

#define _OPENMODE_READ 0x01
#define _OPENMODE_READ_SHARE 0x80
#define _OPENMODE_WRITE 0x02
#define _OPENMODE_READWRITE 0x03
#define _OPENMODE_READWRITE_SHARE 0x83

file make_file(char*name, int size) {
    char*n = malloc(256*sizeof(unsigned short));
    char*n2 = malloc(strlen(name)+7*sizeof(char));
    *(sizeof(char)*7+strlen(name)+n+1) = 0x00;
    memcpy(n, "\\\\fls0\\", 7*sizeof(char));
    memcpy(sizeof(char)*7+n, name, strlen(name));
    Bfile_StrToName_ncpy((const unsigned short*)n2, n, strlen(n));
    free(n);
    name = n2;
    free(n2);
    Bfile_CreateEntry_OS((const unsigned short*)n2, 0x1, size);
    return Bfile_OpenFile_OS(n2, 0x3);
}

void*read_file(file handle, int size, int offset) {
    static char*buf = malloc(size);
    Bfile_ReadFile_OS(handle, buf, size, offset);
    return buf;
}

void write_file(file handle, int size, int offset, void*data) {
    char*buffer = malloc(size+offset);
    memcpy(buffer, read_file(handle, offset, 0), offset);
    memcpy(buffer+offset, data, size);
    Bfile_WriteFile_OS(handle, buffer, size+offset);
    free(buffer);
}

int file_exists(char*name) {
    char*n = malloc(256*sizeof(unsigned short));
    char*n2 = malloc(strlen(name)+7*sizeof(char));
    *(sizeof(char)*7+strlen(name)+n+1) = 0x00;
    memcpy(n, "\\\\fls0\\", 7*sizeof(char));
    memcpy(sizeof(char)*7+n, name, strlen(name));
    Bfile_StrToName_ncpy((const unsigned short*)n2, n, strlen(n));
    free(n);
    name = n2;
    free(n2);
    int exists = Bfile_OpenFile_OS(n2, mode);
    close_file(exists);
    return (exists>=0);
}



They're quick, and may not all work (can't test them at the moment), but should help with others using files for saving game hgighscores/save files, etc. and abstract them from the really ugly prizm file I/O.
Just the problems I noticed in a cursory glance..
_player1537 wrote:
library.h:
Code:
#define memcpy(dest, src, size) int abcd; for(abcd=0;abcd<size;abcd++) dest[abcd] = src[abcd];
Bad bad bad. You should always wrap compound statements in a block when making macros out of them. Consider the following with your macro definition:

Code:
if (foo == 1)
    memcpy(bar, baz, sizeof(foo));

// Expands to
if (foo == 1)
    int abcd;
for (abcd=0; ...)

I was going to provide a fixed version of that, but there are so many ways that macro can go wrong it's not even funny (particularly with typing of the arguments). A 'canonical' Prizm memcpy/memmove/etc function should move words at a time whenever possible (4x speedup). I might give that a stab tomorrow or later tonight..

_player1537 wrote:

Code:
// KermMartian
void XORSprite(char* data, int x, int y, int width, int height) {
  int j, i;
  char* VRAM = (char*)0xA8000000;
[snip]
}

No reason not to access by halfwords here. Similar case for CopySprite() (no reason to declare VRAM as a char *).
Thanks for posting that, Ashbad! I'll have to try to write some tests using them tomorrow (Might go to bed soon-ish).

Tari, bah, I knew there were a few places that I had forgotten to do that. I actually think I only use that in one place, so I will most likely just remove it completely. Someone in IRC said that there was a sprite.h (when was that made?) that had XORSprite and CopySprite, but I hadn't taken that part down yet :/ Part of the reason I'm for a main library like this is so we don't have to look through those long threads to find routines (that may or may not have already been optimized). I'm also sure that if I upgraded my PrizmSDK, I'd have most of the functions I put up there *shrug*
Here's a (completely untested and probably nonfunctional for whatever reason) optimized memcpy (poke poke libfxcg/Jonimus):

Code:
void *memcpy(void *dest, const void *src, size_t n) {
    void *out = dest;

    while ((dest % 4 || src % 4) && n > 0) {
        *dest++ = *src++;
        n--;
    }
    while (n >= 4) {
        __asm__("mov.l @%0+, r3\n\t"
                "mov.l r3, @%1\n\t"
                : "=r"(src)
                : "0"(src), "r"(dest)
                : "memory", "r3");
        dest += 4;
        n -= 4;
    }
    while (n > 0) {
        *dest++ = *src++;
        n--;
    }
    return out;
}

A bit of a lazy solution for when one operand is 32-bit aligned but the other isn't. It could be improved further by doing halfword loads when possible (but 32-bit loads aren't). Ah well.
Well, you're a lot more knowledgeable than me about C, so I feel weird calling you out, but the one thing I do see that may break it is that you try to increment void* pointers without a (what I think was meant) int cast. Unless this is okay in this one instance of usage?
There is a memcpy syscall so I'm not sure why we'd need another implementation of it unless the syscall is just that slow.
Ashbad wrote:
Well, you're a lot more knowledgeable than me about C, so I feel weird calling you out, but the one thing I do see that may break it is that you try to increment void* pointers without a (what I think was meant) int cast. Unless this is okay in this one instance of usage?
Casting as anything other than void here would cause unwanted behavior. If you add 1 to an int*, the pointer moves four bytes (ie, sizeof(int)) forward in memory. If you add 1 to a void*, the pointer moves one byte forward in memory. You could do char*, but even that is bad if the platform happens to have multiple-byte chars. void* is the way to go.
TheStorm wrote:
There is a memcpy syscall so I'm not sure why we'd need another implementation of it unless the syscall is just that slow.
Fair enough. I assumed that there wasn't since somebody had a fail-implementation. Razz
You can add to this lib a real keydown function Razz
Sounds good, guys Smile I'll start putting the static library together in devkitpro formatting this weekend. As for naming conventions, I'm thinking using things like CGL_openfile, CGL_DrawSpriteMasked, etc., kind of like how SDL names its functions (like SDL_BlitSurface, SDL_KillThread, etc.) where 'CGL' stands for 'Cemetech Game Library'. Objections?
So, I'm working on compiling together all of the routines, and formatting them to be uniform in name, etc. Right now I'm not following any sort of DevKitPro formatting, but I can change that later.

Also, for just an idea, after the Prizm version of this is far done, what about making an SDL-based version for the PC as well? That way, if you just use the CGL library and nothing else, you could link against the two versions of the .a library so that you could make the game for the Prizm and the Computer at the same time! Of course, far out idea, but maybe motivation to get this thing rolling. Smile
Did this ever get released? It sounds like a great idea. Smile
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement