Great, that looks quite spiffy to me. Smile Thanks for sharing the fruits of my code.
*bump* I recently added the ability for SourceCoder to generate 8-bit, 2-bit, and 1-bit data from images. The following two routines work with the 2-bit versions. Two-bit images are particularly good for two-color or three-color images that also have a mask color.

Sample usages:

Code:
unsigned short int palette[4] = {COLOR_BLACK, COLOR_BLUEVIOLET, COLOR_RED, COLOR_WHITE};
CopySpriteMasked2bit(mysprite, 5, 70, 32, 32, palette);
CopySprite2bit(mysprite, 5, 70, 32, 32, palette);


For the Masked version, the COLOR_RED (aka, color #2 of 0,1,2,3) is the transparent color.


Code:
void CopySpriteMasked2bit(const unsigned char* data, int x, int y, int width, int height, short unsigned int* palette) {
   short unsigned int* VRAM = (short unsigned int*)0xA8000000;
   VRAM += (LCD_WIDTH_PX*y + x);
   int offset = 0;
   unsigned char buf;
   for(int j=y; j<y+height; j++) {
      int availbits = 0;
      for(int i=x; i<x+width;  i++) {
         if (!availbits) {
            buf = data[offset++];
            availbits = 8;
         }
         char this = ((buf&0xC0)>>6);
         if (this != 0x02)
            *VRAM = palette[(int)this];
         VRAM++;
         buf<<=2;
         availbits-=2;
      }
      VRAM += (LCD_WIDTH_PX-width);
   }
}



Code:
void CopySprite2bit(const unsigned char* data, int x, int y, int width, int height, short unsigned int* palette) {
   short unsigned int* VRAM = (short unsigned int*)0xA8000000;
   VRAM += (LCD_WIDTH_PX*y + x);
   int offset = 0;
   unsigned char buf;
   for(int j=y; j<y+height; j++) {
      int availbits = 0;
      for(int i=x; i<x+width;  i++) {
         if (!availbits) {
            buf = data[offset++];
            availbits = 8;
         }
         char this = ((buf&0xC0)>>6);
         *VRAM = palette[(int)this];
         VRAM++;
         buf<<=2;
         availbits-=2;
      }
      VRAM += (LCD_WIDTH_PX-width);
   }
}
*bump* Here's a pair of routines that I fixed in gCAS2. They're nothing amazing, but they work.


Code:
int _g_strcmp(char *a, char *b)
{
    const char* p1 = a - 1;
    const char* p2 = b - 1;

    while (*++p2 == *++p1)
        if (!*p2 || !*p1)
            break;

    if (*p2 > *p1)
        return (-1);
    else if (*p1 > *p2)
        return (1);

    return (0);
}
*Quadruple Bump Powerup Combo!*

For Graph3DP, I created an input routine for single-line, scrolling text or number entry. It does strings, ints, positive ints, and floats. I didn't make it do lowercase letters yet, since I didn't want them for my application; I'll probably update that page accordingly soon. You can read all about it here:

http://prizm.cemetech.net/index.php?title=Text_and_Number_Input
KermMartian wrote:
*Quadruple Bump Powerup Combo!*

For Graph3DP, I created an input routine for single-line, scrolling text or number entry. It does strings, ints, positive ints, and floats. I didn't make it do lowercase letters yet, since I didn't want them for my application; I'll probably update that page accordingly soon. You can read all about it here:

http://prizm.cemetech.net/index.php?title=Text_and_Number_Input


Wow, that's one of the finest additions to our useful routines set yet! Very nice work Kerm, this was probably one of our most-needed routines right now.
Thanks very much. Smile It took me quite a while to work up the motivation to write it, but once I started, it actually was surprisingly fast to complete it.
Frequency Changing Routines

Inputs: none
Outputs: none

Notes: PLEASE before handling MENU keypresses in your add-in, use PLL_16x to go back to normal operating speed, at the courtesy of other applications and the OS.

Thanks to Brijohn for the information that helped me make these headways. The routine below is based on his routines, except optimized and condensed to a single, easy-to-use routine, which can be simply plopped into your C file.

Feeding a value not defined below (AKA, an invalid value), will cause a crash.


Code:
#define PLL_24x 0b010111 // 87 MHz
#define PLL_18x 0b010001 // 65.25 MHz
#define PLL_16x 0b001111 // 58 MHz (NORMAL SPEED)
#define PLL_15x 0b001110 // 54.37 MHz
#define PLL_12x 0b001011 // 43.5 MHz
#define PLL_8x  0b000111 // 29 MHz
#define PLL_6x  0b000101 // 21.75 MHz
#define PLL_4x  0b000011 // 14.5 MHz
#define PLL_3x  0b000010 // 10.8 MHz
#define PLL_2x  0b000001 // 7.25 MHz
#define PLL_1x  0b000000 // 3.6 MHz

__attribute__ ((noinline))  void change_freq(int mult) {
   __asm__(
      "mov r4, r0\n\t"
      "and #0x3F, r0\n\t"
      "shll16 r0\n\t"
      "shll8 r0\n\t"
      "mov.l frqcr, r1\n\t" 
      "mov.l pll_mask, r3\n\t" 
      "mov.l @r1, r2\n\t" 
      "and r3, r2\n\t" 
      "or r0, r2\n\t"
      "mov.l r2, @r1\n\t"
      "mov.l frqcr_kick_bit, r0\n\t"
      "mov.l @r1, r2\n\t"
      "or r0, r2\n\t"
      "rts\n\t"
      "mov.l r2, @r1\n\t"
      ".align 4\n\t"
      "frqcr_kick_bit: .long 0x80000000\n\t"
      "pll_mask: .long 0xC0FFFFFF\n\t"
      "frqcr: .long 0xA4150000\n\t"
   );
}



Next thing on my list is finding out how to update the physical LCD memory from VRAM, since I think it can probably be doubly optimized to half speed. Once I have a full lead with working routines, they shall be here.


EDIT: I invite all people here to put their routines on the WikiPrizm page: http://prizm.cemetech.net/index.php?title=Useful_Routines It would be very helpful to everyone else here, to have all of these routines in an organized place as well.
Why do it in assembly when you can integrate more tightly?

Code:
void change_freq(uint8_t mult) {
    volatile uint32_t * const frqcr = 0xA4150000;

    uin32_t cr = *frqcr;
    cr &= ~(0x3F << 24);
    cr |= (mult & 0x3F) << 24;
    frqcr = cr;
    frqcr |= 0x80000000;
}

Usual disclaimer about how I didn't test this goes here, doubly so since I may have mistranslated some of it.
OPTIMIZATION TIEM!


Code:
#define PLL_24x 0b010111 // 87 MHz
#define PLL_18x 0b010001 // 65.25 MHz
#define PLL_16x 0b001111 // 58 MHz (NORMAL SPEED)
#define PLL_15x 0b001110 // 54.37 MHz
#define PLL_12x 0b001011 // 43.5 MHz
#define PLL_8x  0b000111 // 29 MHz
#define PLL_6x  0b000101 // 21.75 MHz
#define PLL_4x  0b000011 // 14.5 MHz
#define PLL_3x  0b000010 // 10.8 MHz
#define PLL_2x  0b000001 // 7.25 MHz
#define PLL_1x  0b000000 // 3.6 MHz

void change_freq(int mult) {
   __asm__(
      "mov #26, r0\n\t"
      "shld r0, r4\n\t" 
      "shlr2 r4\n\t"   
      "mov.l frqcr, r1\n\t" 
      "mov.l pll_mask, r3\n\t" 
      "mov.l @r1, r2\n\t" 
      "and r3, r2\n\t" 
      "or r4, r2\n\t" 
      "mov.l r2, @r1\n\t" 
      "mov #1, r0\n\t"
      "rotr r0\n\t"
      "or r0, r2\n\t"
      "rts\n\t"
      "mov.l r2, @r1\n\t"
      ".align 4\n\t"
      "pll_mask: .long 0xC0FFFFFF\n\t" 
      "frqcr: .long 0xA4150000\n\t"
   );
}
I prefer the C implementation, personally; is the version that GCC creates for that C code really that bad? Sad
KermMartian wrote:
I prefer the C implementation, personally; is the version that GCC creates for that C code really that bad? Sad


It's (almost) purely for the feeling of being 1337, methinks Wink (and in my case, the point of learning SH assembly) The C version should be fine; and of course, this routine in particular doesn't need to be optimized, since you won't be using it in a main game render loop.
I noticed that most routines still use a fix VRAM address (0xA8000000). This is bad, VERY BAD.

Within the past 7 years, Casio changed the VRAM address three times on models like the fx-9860G. This is okay, if you regularly update your add-ins, but most add-ins won't be updated regularly...

So better use syscall 0x01E6:
void *GetVRAMAddress(void);
Ah, I had no idea, thanks for that! Does that exist in the PrizmSDK? If so, would someone mind documenting that on WikiPrizm? I think that programs should only call that at the very beginning and save it in a global variable, however, to avoid lagging down screen-manipulation routines with a syscall.
KermMartian wrote:
Ah, I had no idea, thanks for that! Does that exist in the PrizmSDK? If so, would someone mind documenting that on WikiPrizm? I think that programs should only call that at the very beginning and save it in a global variable, however, to avoid lagging down screen-manipulation routines with a syscall.


I've already documented it a while ago, with the exact same idea of saving it to a variable and using it only once that you described Wink Way ahead of you, buddy.

http://prizm.cemetech.net/index.php?title=GetVRAMAddress
Then at some point I'll have to update the other Useful Routines to make mention of that global constant and the need to initialize it. On that note, we should try to pick a day to all work together to get the routines in this topic onto WikiPrizm in an organized fashion. The other thing I suppose I'll need to do is update Graph3DP, Tetrizm, and Obliterate to use this more proper method of setting the VRAM address, but I'm not sure whether the pressure of preempting a VRAM address change by Casio or the pressure of them actually changing it will be what's necessary to get me to actually make the updates. Wink
I'm doing Grayscale 16 grayscales sprites adaptation., but, the reseults are a bit funny :/

Anyway, here are the sources, and fixed, That works!

Code:
void CopySprite_16Gray(const unsigned char* data, int x, int y, int width, int height) {

   unsigned short* VRAM = (unsigned short*)VRAM_ADRESS;
   int i,j;
   unsigned short* ptr = VRAM + y*LCD_WIDTH_PX + x;
   for(j=0; j<height; j++) {
      for(i = 0; i < width/2; i++) {
         *ptr = makeGray( ( ( *(data) & 0xF0) *2 )>> 4 );
         ptr++;
         *ptr = makeGray((*(data++) & 0xF) *2 );
         ptr++;
      }
      ptr += LCD_WIDTH_PX-width;
   }   

}

void CopySprite_32Gray(const unsigned char* data, int x, int y, int width, int height) {

   unsigned short* VRAM = (unsigned short*)VRAM_ADRESS;
   int i,j;
   unsigned short* ptr = VRAM + y*LCD_WIDTH_PX + x;
   for(j=0; j<height; j++) {
      for(i = 0; i < width; i++) {
         *ptr = makeGray(*(data++));
         ptr++;
      }
      ptr += LCD_WIDTH_PX-width;
   }   

}


The 16 gray routine is made to work with SourceCoder.

aaaand the makegray, "stolen":

Code:
int makeGray(int shade) {
   return (((shade<<11)&0x0000F800) | ((shade << 6)&0x000007C0) | ((shade)&0x0000001F));
}

I miss a good grayscale function, or palette
Razz
Since people are still not successfully using the LCD controller primitive commands, here's a few simple routines that will make using fullscreen, normal screen size, and even arbitrary screen window sizes easy. Also includes self-reliant LCD updating command, efficient and will work no matter the screen mode you're in.


Code:
void fullscreen(void) {
   WriteLCDReg(LCD_WINDOW_LEFT, 0);
   WriteLCDReg(LCD_WINDOW_RIGHT, 395);
   WriteLCDReg(LCD_WINDOW_TOP, 0);
   WriteLCDReg(LCD_WINDOW_BOTTOM, 223);
}

// always use this before exiting with MENU from the add-in
void normalscreen(void) {
   WriteLCDReg(LCD_WINDOW_LEFT, 6);
   WriteLCDReg(LCD_WINDOW_RIGHT, 389);
   WriteLCDReg(LCD_WINDOW_TOP, 0);
   WriteLCDReg(LCD_WINDOW_BOTTOM, 215);
}

// set screen size to be of w*h dimension, centered on physical LCD
void set_ssize_centered(int w, int h) {
   if(w > 396 || h > 224 || w <= 0 || h <= 0) {
      return;
   }
   WriteLCDReg(LCD_WINDOW_LEFT, 198-(w/2));
   WriteLCDReg(LCD_WINDOW_RIGHT, 198+(w/2)-1);
   WriteLCDReg(LCD_WINDOW_TOP, 112-(h/2));
   WriteLCDReg(LCD_WINDOW_BOTTOM, 112+(h/2)-1);
}

// you need to pass a pointer to the buffer you use as Video RAM, since any sizes past normal
// sized screens can't fit their data into normal VRAM provided from the OS
void update(color_t*VRAM) {
   WriteLCDReg(LCD_GRAM_X, 0);
   WriteLCDReg(LCD_GRAM_Y, 0);
   int j = (ReadLCDReg(LCD_WINDOW_RIGHT) - ReadLCDReg(LCD_WINDOW_LEFT) + 1) *
         (ReadLCDReg(LCD_WINDOW_TOP) - ReadLCDReg(LCD_WINDOW_BOTTOM) + 1);
   SelectLCDReg(LCD_GRAM);
   for(int i = 0; i < j; i++) {
      LCDC = *((color_t*)VRAM+i);
   }
}


You'll notice the screen update command takes a pointer to Video RAM. In the case of fullscreen (or any size larger than normal screen size), VRAM provided by the OS is not sufficient in size. You'll have to create a new buffer like this:


Code:
color_t VidRAM[396*224];


Tari's updated Prizm.ld file allows this to compile, pick it up if you haven't already. I suggest you do this anyways even if you switch to a smaller screen size than normal -- because then, you free up the OS VRAM for whatever use you want of it.
I think that I improved a little the grayscale function :-


Code:
unsigned char makeGray(unsigned char index) {
   return index == 31 ? 0xFFFF : index * 0x0841;
}
I have finally gotten around to updating SourceCoder so that it outputs palettes for 8-bit, 2-bit, and 1-bit images; presumably I'll have to add 4-bit at some point, if people really, really need it. I have also updated the routine earlier in this thread to deal with this new format, including using the proper color_t data type. Enjoy!

http://prizm.cemetech.net/index.php?title=CopySpriteNBit
Good afternoon everyone. I have a question:

KermMartian wrote:
I have finally gotten around to updating SourceCoder so that it outputs palettes for 8-bit, 2-bit, and 1-bit images; presumably I'll have to add 4-bit at some point, if people really, really need it. I have also updated the routine earlier in this thread to deal with this new format, including using the proper color_t data type. Enjoy!

How to draw 8-bit sprites with an alpha channel?
  
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
» Goto page Previous  1, 2, 3 ... 7, 8, 9, 10, 11, 12  Next
» View previous topic :: View next topic  
Page 8 of 12
» All times are GMT - 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