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.

Beginners > Quick question on mode 4

#58083 - Lazy1 - Thu Oct 20, 2005 6:22 pm

I'm having problems with mode 4, I have read that you need to write two pixels at a time but I couldn't find an example where an image is read into the framebuffer this way.

I'm going through the GBA tutorials on www.drunkencoders.com and I'm on day 2 where it deals with mode 4 and pcx images.
Rather than copy the tutorial files I prefer to go along and write my own code so I decided to replace the pcx images with a half-life sprite.
( Don't worry about incompatible licenses, I sat down with a hex editor and figured out the file format myself ).

So I'm at the point where I have loaded the sprite's palette into BGPaletteMem ( I have verified this to be correct using visualboy advance's palette viewer ) and have to write the actual image data into the framebuffer.
All my attempts so far haven't worked, though I did see something it was usually the image spaced out by black pixels.

There is also another wierd problem which I'm not sure if it was the way I was displaying the image but the top half of the image is cut off and displayed at the bottom aswell as some garbage at the very right.

Could someone provide an example of taking two pixels from an image and placing them on the screen in mode 4?
Thanks in advance.

[Added]
I found a few really dumb mistakes and now I can actually see the sprite!
There is still a problem though as you can see here http://img422.imageshack.us/img422/6417/day2mb8vf.png

Here is my ( so far ) hacked up code for day 2.
Code:

#include "gba.h"
#include "hl_sprite.h"

#define MODE3_POS( x, y ) ( x + ( y * 240 ) )
#define MODE4_POS( x, y ) ( x + ( y * 120 ) )
#define MODE5_POS( x, y ) ( x + ( y * 160 ) )

int __gba_multiboot = 0;

extern u8 _binary_blueflare1_spr_start[ ];
//extern u8 _binary_bluejet1_spr_start[ ];

void FatalError( void ) {
   unsigned int iX = 0;
   unsigned int iY = 0;
   
   SetMode( MODE_3 | BG2_ENABLE );
   
   for ( iX = 0; iX < 240; iX++ ) {
      for ( iY = 0; iY < 160; iY++ ) {
         VideoBuffer[ MODE3_POS( iX, iY ) ] = RGB16( 31, 0, 0 );
      }
   }

   while ( 1 ) {
   }
}

// Loads a Half-Life sprite into the frontbuffer
int LoadHLSprite( u8* pSpritememory, int iFrame ) {
   hl_sprheader_t* pSprite = ( hl_sprheader_t* ) pSpritememory;
   hl_sprframe_t* pFrame = NULL;
   u8* pDataptr = pSpritememory;
   u32 iX = 0;
   u32 iY = 0;
   u32 i = 0;
   u16 p = 0;
   
   // Make sure this is has a valid sprite header ( ASCII "IDSP" ) and the version is 2 ( half-life format )
   // If not, return failure now.
   if ( *( ( int* ) pSprite->sprIdentify ) != HL_SPR_IDENTITY || pSprite->iVersion != 2 )
      return 0;
   
   // Move past the header
   pDataptr+= sizeof( hl_sprheader_t );
   
   // Copy in the sprite's palette
   for ( i = 0; i < 256; i++, pDataptr+= 3 )
      BGPaletteMem[ i ] = RGB16( pDataptr[ 0 ] >> 3, pDataptr[ 1 ] >> 3, pDataptr[ 2 ] >> 3 );
      
   // Get a pointer to the first sprite frame after the palette and
   // point our data pointer past it
   pFrame = ( hl_sprheader_t* ) pDataptr;
   pDataptr+= sizeof( hl_sprheader_t );
   
   // BROKEN
   // Display sprite
   for ( iX = 0; iX < pFrame->iWidth >> 1; iX++ ) {
      for ( iY = 0; iY < pFrame->iHeight; iY++ ) {
         p = ( ( *pDataptr++ ) << 8 ) + *pDataptr++;
         FrontBuffer[ MODE4_POS( iX, iY ) ] = p;
      }
   }
   
   return 1;
}

// Mode 4 16 bit
//
// 11111111 2222222
// Leftmost Rightmost

int main( void ) {
   SetMode( MODE_4 | BG2_ENABLE );
   
   if ( ! LoadHLSprite( _binary_blueflare1_spr_start, 0 ) )
      FatalError( );

   while ( 1 ) {
   }
   
   return 0;
}

#58117 - Lazy1 - Thu Oct 20, 2005 11:57 pm

Anyone have an idea what is going on here?
I'm running out of thoughts as to why this won't display properly.

#58133 - Nacho - Fri Oct 21, 2005 4:19 am

Hi! I think the following sentence is the problem:

Code:
p = ( ( *pDataptr++ ) << 8 ) + *pDataptr++;


Remember that the post-increment operator is applied AFTER the entire statement is executed, so you?re basically accessing the same data twice. Hope that helps!!

--Nacho

#58134 - gauauu - Fri Oct 21, 2005 4:29 am

Or technically, even scarier, the C standard says that trying to use the postincrement operator on the same value in the same expression is undefined -- meaning ANYTHING can happen.

Although most likely it won't do anything disastrous, you can't really be sure WHAT will happen when you do it. (gcc might have a standard way of handling it, I don't know) ..... Thus, it's best avoided.

See the C faq, sections 3.2 and 11.33

#58139 - Cearn - Fri Oct 21, 2005 8:14 am

What they said.

And are you sure that the sprite data is column-based? Pretty much ever bitmap format I know is row-based. And have you tried getting the algorithm to work in a PC environment, where you have all the proper debugging tools?

#58181 - Lazy1 - Fri Oct 21, 2005 3:06 pm

Thanks, that helped a little bit ( damn stupid mistake with incrementing the pointer ) and I switched it to draw in rows instead.
It looks better now except the sprite displays as it should from top to bottom except the first few pixels appear on the other side.

It's possible I have to dig around and figure out the file format a little more since I only worked on getting the main header, palette and frame header.

Here is what I know ( besides actual struct definitions )
Code:

// Half-life sprite file structure...
// ---------------------
// Header 42 bytes
// Palette 768 bytes
// Frame header 20 bytes
// Image data ( frame header->width * frame header->height )
// ...
// If you have more than one frame...
// Frame header 20 bytes
// Image data ( frame header->width * frame header->height )
// ...
// ... ect...

#58188 - Cearn - Fri Oct 21, 2005 3:52 pm

Quote:
Code:
... sizeof(hl_sprheader_t) ...

Can we see the struct definitions please? hint: data alignment

#58191 - Lazy1 - Fri Oct 21, 2005 3:56 pm

Code:

#ifndef _HL_SPRITE_H_
#define _HL_SPRITE_H_

// Should be at the start of the file, identifies this as a sprite
#define HL_SPR_IDENTITY ( ( 'P' << 24 ) + ( 'S' << 16 ) + ( 'D' << 8 ) + ( 'I' ) )

// Half-life sprite file structure...
// ---------------------
// Header 42 bytes
// Palette 768 bytes
// Frame header 20 bytes
// Image data ( frame header->width * frame header->height )
// ...
// If you have more than one frame...
// Frame header 20 bytes
// Image data ( frame header->width * frame header->height )
// ...
// ... ect...

// Half-life sprite rendering modes, these determine how sprites are
// rendered by the engine.
enum hl_sprite_rendermodes_e {
   SPR_MODE_NORMAL = 0, // Normal rendering
   SPR_MODE_ADDITIVE,   // Black ( RGB 0, 0, 0 ) is transparent
   SPR_MODE_INDEXALPHA, // Last color in palette is the sprite's in-game color
   SPR_MODE_ALPHATEST   // Last color in palette is transparent
};

// Half-life sprite types, these determine how the sprite rotates
// when viewed by a player in-game.
enum hl_sprite_type_e {
   SPR_TYPE_ROTATE_Y_ONLY = 0,   // Sprite does not rotate on the X axis
   SPR_TYPE_ONE_WAY,             // Sprite faces one way and does not rotate ( NOTE: Not 100% sure on this )
   SPR_TYPE_ROTATE_XY,           // Sprite rotates to face the player on both the X and Y axis
   SPR_TYPE_ONE_WAY2,            // Odd, same as SPR_FACE_ONE_WAY except in a different direction
   SPR_TYPE_ROTATE_XY2           // Hmm, this seemingly does the same thing as SPR_FACE_ROTATE_XY
};

// Half-life sprite header.
// Still under research.
//#pragma pack( 2 )
typedef struct hl_sprheader_s {
   char sprIdentify[ 4 ];  // 4 Character non null terminated file id. Should contain "IDSP"
   int iVersion;           // Sprite version number. Should be 2.
   int iSpritetype;        // Type of sprite
   int iRendermode;        // Sprite render mode. See hl_sprite_rendermodes_e.
   int iUnknown4;          // Offset? Actually no, it's something else...
   unsigned int iWidth;    // Width of sprite
   unsigned int iHeight;   // Height of sprite
   int iFramecount;        // Number of frames in sprite
   int iUnknown5;
   int iUnknown6;
   unsigned short iUnknown7;
} __attribute__( ( packed ) ) hl_sprheader_t;

// This structure seems to appear at the start of a new frame within the sprite.
// NOTES:
// This is a complete structure!
// Only alter when new type/use information is found.
typedef struct hl_sprframe_s {
   int iUnknown1;          // Always seems to be 0
   int iUnknown2;          // Always seems to be C0 FF FF FF
   int iUnknown3;          // Always seems to be 1
   unsigned int iWidth;    // Width of frame
   unsigned int iHeight;   // Height of frame
} __attribute( ( packed ) ) hl_sprframe_t;

#endif


Thats as far as I researched it.

#58301 - Cearn - Sat Oct 22, 2005 1:17 pm

Ok, so you're already using the packing attribute. It still looks like you have an offset error somewhere, though. Could you check with a simple sprite,something with well-defined edges; a simple box maybe? That should show offset anomalies. Also, each pixel seems to be doubled somehow, or is that just something from the double *ptr++ stuff.

#58328 - Lazy1 - Sat Oct 22, 2005 6:45 pm

I removed the problem code where I set the pixels and used a different method so I don't think that is the problem.
I did make a small sprite which was a 16x16 green box and it did come out broken when displayed in the gba program.

I also did some tests as to how the image data was stored in the sprite itself, it is written like this...

[16x16 image]
Line 1: 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
Line 2: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02

Modified code section
Code:

   // BROKEN
   // Display sprite
   for ( iY = 0; iY < pFrame->iHeight; iY++ ) {
      for ( iX = 0; iX < ( pFrame->iWidth ) >> 1; iX++ ) {
         FrontBuffer[ MODE4_POS( iX, iY ) ] = ( ( *pDataptr ) << 8 ) + *( pDataptr + 1 );
         pDataptr+= 2;
      }
   }


Or is it the way I embed the sprite into the .gba file?
Code:

arm-elf-objcopy -I binary -O elf32-littlearm -B arm --rename-section .data=.ewram square.spr square.od

#58336 - tepples - Sat Oct 22, 2005 7:24 pm

You have to set the image converter to tiled mode, not mode 4.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#58343 - Lazy1 - Sat Oct 22, 2005 8:38 pm

I'm not using an image converter, or atleast not one that converts it directly into a format the gba can use.
Unless I didn't understand what you said...

#58345 - tepples - Sat Oct 22, 2005 8:44 pm

If you're loading pcx images directly in your GBA program, then you'll need to convert them to tiled format inside your GBA program. Read the CowBite spec and GBATEK (use Google).
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#58347 - Lazy1 - Sat Oct 22, 2005 8:57 pm

Actually, I'm not using PCX images...
A while ago I got bored and figured out the game Half-Life 's sprite file format.
The ( mostly ) figured out format sat around doing nothing until a few days ago when I decided to put it to work while learning GBA programming.

The format is probably similar though, you have the main header, palette, sprite frame header and the rest is bytes which index into the palette.
It's because of that I chose to use it since mode 4 was the same way.

#58350 - Cearn - Sat Oct 22, 2005 9:33 pm

http://www.planethalflife.com/mach3/sprites/source.html
http://www.gamers.org/dEngine/quake/Qutil/spritegn.h

GIYF

#58353 - Lazy1 - Sat Oct 22, 2005 9:56 pm

Thats not what I meant, I don't need to know any more about the sprite format itself - and if I use that information I'll have to deal with some kind of license crap.

I'm just trying to put the information I gathered to use, is there a problem with the drawing code itself?
If there isn't then it looks like I'll go back and work on the file format some more and use something else for day2.

#58356 - Cearn - Sat Oct 22, 2005 10:14 pm

Lazy1 wrote:
Thats not what I meant, I don't need to know any more about the sprite format itself - and if I use that information I'll have to deal with some kind of license crap.

OK, fair enough. But isn't Quake code opensource nowadays anyway? It does seem that your frame struct may have the wrong size, though.

Lazy1 wrote:

I'm just trying to put the information I gathered to use, is there a problem with the drawing code itself?

Now that you mention it, yeah, there is something wrong. GBA is little endian, so that the byte-order (and hence the pixel order) is 0x0201, not 0x0102, so you probably need to reverse that. In fact, if the pixel data is always on even addresses, you can cast to u16* instead of u8*, or use DMA for each line.

Also, in what way is it broken now?