In memory of Ben “bushing” Byer, who passed away on Monday, February 8th, 2016.

Difference between revisions of "Developer tips"

From WiiBrew
Jump to navigation Jump to search
 
(51 intermediate revisions by 30 users not shown)
Line 1: Line 1:
=Code Snippets=
+
==General Programming Tips==
==Video Auto-Detect Routine==
+
*Keep your code commented throughout; it helps others help you. How much you comment it is a matter of debate; in general, people tend to comment bits of code where it isn't obvious what it actually does or why, or variables that aren't obvious what they are used for (or are not used until quite a bit later on).
Please include an autodetect routine (VIDEO_GetCurrentTVMode()) to detect HDTV/EDTV and set the appropriate video mode.
+
*Any unused code should be deleted out of the program, unless it is a program for teaching people.
 +
*If someone does the same thing in a more efficient way (i.e. faster and/or in less code), accept it and learn from it.
 +
*It is a good idea to release your app open source so others can learn from your code.
 +
*To keep things tidy, if you store files on SD, store them in a folder named the same as your application's name.
  
The current video autodetect routine doesn't work with PAL60 (480i @ 60 Hz in PAL Wii) using the offical Nintendo RGB cable for Wii.
+
== Code Snippets ==
 +
=== Video Auto-Detect Routine ===
 +
Here is the video detect code from the DevKitPro Wii example.
 +
Keep in mind, however, the libogc included in DevkitPPC since r15 does this for you already with one function call.
 +
<source lang="c">
 +
#include <gccore.h>
 +
static GXRModeObj *rmode = NULL;
 +
// ...
 +
rmode = VIDEO_GetPreferredMode(NULL);
  
Here is the video detect code from the DevKitPro Wii example.
+
if( CONF_GetAspectRatio() )
 +
{
 +
rmode->viWidth = 678;
 +
rmode->viXOrigin = (VI_MAX_WIDTH_PAL - 678)/2;
 +
}
 +
VIDEO_Configure(rmode);
 +
</source>
  
    rmode = VIDEO_GetPreferredMode(NULL);
+
Please see also: [[Display Issues]]
    VIDEO_Configure(rmode);
 
  
[http://forum.wiibrew.org/viewtopic.php?t=230 Forum thread]
+
=== Exit to Loader ===
 +
It's a good idea to add some way to return to the loader, otherwise you have to reboot your Wii to exit.
 +
<source lang="c">
 +
// Just call the exit() function to go back to the loader
 +
// Returning from main also works, since that calls exit for you
 +
    #include <stdlib.h>
 +
    // ...
 +
    exit(0);
 +
</source>
  
==Exit to Loader==
+
=== How to use the Wiimote ===
It's a good idea to add some way to return to the loader, otherwise you have to reboot you Wii to exit.
+
A separate article is available: [[How to use the Wiimote]].
  
    // Just call the exit() function to go back to the loader
+
=== Reboot Wii ===
    // Returning from main also works, since that calls exit for you
+
Use:
    // ....
+
<source lang="c">
    exit(0);
+
#include <gccore.h>
Note: The floating around __crtmain code is faulty in the aspect that it does not call exit as the standard requires. Fix it your self. In addition, it also does not call the constructors for global objects in c++. But that's what you get when using quickly hacked together one liners.
+
// ...
 +
SYS_ResetSystem(SYS_RESTART,0,0);
 +
</source>
  
if all else fails
+
Or use SYS_RETURNTOMENU for a "soft" return to the system menu, SYS_POWEROFF to shut down the wii (automatically to the appropriate Idle or Standby mode, depending on the WC24 setting), or SYS_POWEROFF_STANDBY or _IDLE to specify the mode and override the system setting.
  
    //cleanup system for exit
+
=== How to use the callback function for Power and Reset ===
    SYS_ResetSystem(SYS_SHUTDOWN,0,0);
+
This is just a way of doing this stuff, feel free to change the code.
    //load boot loader address to function pointer and call it all in one step
+
<source lang="c">
    ((void(*)(void))0x80001800)();
+
s8 HWButton = -1;
  
you can put this all in one line since it is essentially a non-library dependent exit as from above.
+
/**
not good practice on a general purpose machine, but this is an embedded system.
+
* Callback for the reset button on the Wii.
 +
*/
 +
void WiiResetPressed()
 +
{
 +
HWButton = SYS_RETURNTOMENU;
 +
}
  
==How to use the Wiimote==
+
/**
A separate article is available: [[How to use the Wiimote]].
+
* Callback for the power button on the Wii.
 +
*/
 +
void WiiPowerPressed()
 +
{
 +
HWButton = SYS_POWEROFF_STANDBY;
 +
}
  
==Reboot Wii==
+
/**
Here's the source code to reboot the Wii.
+
* Callback for the power button on the Wiimote.
<source lang="c">
+
* @param[in] chan The Wiimote that pressed the button
void Reboot()
+
*/
 +
void WiimotePowerPressed(s32 chan)
 
{
 
{
// Thanks to hell_hibou
+
HWButton = SYS_POWEROFF_STANDBY;
int fd = IOS_Open("/dev/stm/immediate", 0);
 
IOS_Ioctl(fd, 0x2001, NULL, 0, NULL, 0);
 
IOS_Close(fd);
 
 
}
 
}
</source>
 
  
=Debugging Tip=
+
/**
When faced with a crash in your Homebrew, often you'll see a code dump with an address and some machine code. Here's my trick to track that back to a line of C++ code.
+
* Entry point.
 +
* @param[in] argc The number of arguments invoked with the program
 +
* @param[in] argv The array containing the arguments
 +
* @return 0 on clean exit, an error code otherwise
 +
  */
 +
int main(int argc, char **argv)
 +
{
 +
// Initialization
 +
 
 +
SYS_SetResetCallback(WiiResetPressed);
 +
SYS_SetPowerCallback(WiiPowerPressed);
 +
WPAD_SetPowerButtonCallback(WiimotePowerPressed);
  
For example if your homebrew game crashes it might show something like this:
+
while(1)
 +
{
 +
// Do Stuff Here
 +
if(HWButton != -1)
 +
break;
 +
}
  
    CODE DUMP:
+
if(HWButton != -1)
   
+
{
    800084ac:  809F0020 2F840000 ...
+
SYS_ResetSystem(HWButton, 0, 0);
    800084bc:  ...
+
}
    800084cc:  ...
 
  
The 800084ac is the memory address in hex of where the crash occured. 809F0020 is the machine code for the offending instruction.
+
return 0;
 +
}
 +
</source>
 +
Possible values for HWButton are:
 +
<source lang="c">
 +
#define SYS_RESTART 0 /*!< Reboot the gamecube, force, if necessary, to boot the IPL menu. Cold reset is issued */
 +
#define SYS_HOTRESET 1 /*!< Restart the application. Kind of softreset */
 +
#define SYS_SHUTDOWN 2 /*!< Shutdown the thread system, card management system etc. Leave current thread running and return to caller */
 +
#define SYS_RETURNTOMENU 3 /*!< Directly load the Wii Channels menu, without actually cold-resetting the system */
 +
#define SYS_POWEROFF 4 /*!< Powers off the Wii, automatically choosing Standby or Idle mode depending on the user's configuration */
 +
#define SYS_POWEROFF_STANDBY 5 /*!< Powers off the Wii to standby (red LED, WC24 off) mode. */
 +
#define SYS_POWEROFF_IDLE 6 /*!< Powers off the Wii to idle (yellow LED, WC24 on) mode. */
 +
</source>
  
*Step 1:
+
== Note on Projection Matrices ==
In your makefile change the CXXFLAGS line to the following:
+
There are a lot of tutorials and projects out there that are using standard 'Mtx' variables for projection matrices. Although this works in many cases (because the variables declared right after the matrix don't mind being corrupted once or twice), this is not correct, and is a crash waiting to happen. These matrices should be declared as 'Mtx44' objects instead. Note the prototypes for the projection matrix functions (see "ogc/gu.h"):
  CXXFLAGS = -save-temps -Xassembler -aln=$@.lst $(CFLAGS)
+
<source lang="c">
The "-save-temps" will save the assembly language file, which can be interesting.
+
void guFrustum(Mtx44 mt,f32 t,f32 b,f32 l,f32 r,f32 n,f32 f);
The "-Xassembler -aln=$@.lst" creates a list file which contains the assembly and the machine code.
+
void guPerspective(Mtx44 mt,f32 fovy,f32 aspect,f32 n,f32 f);
Now recompile your entire project.
+
void guOrtho(Mtx44 mt,f32 t,f32 b,f32 l,f32 r,f32 n,f32 f);
Note, this just affects C++ code.
+
</source>
 +
One way to catch such errors is to enable more warnings in gcc like:
 +
<source lang="bash">gcc -Wall -pedantic -std=c99</source>
  
*Step 2:
+
''For example the current ''''devkitPro\examples\wii\graphics\gx\triangle.c'''' uses 'Mtx' but it should be using 'Mtx44' - I fell into this trap, please be careful & take note of the above.''
Look at the map file that was built. The mapfile is on by default in the Wii template makefile. Typically it's in the build subdirectory and called something.map.  Look in that mapfile for the nearest memory address that doesn't go over the one found in the CODE DUMP.  Here is an example:
+
=== Timing ===
    0x80008464                ShooterView::Render(BibGraphicsDevice&)
+
Use:
This tells me that the crash was 72 bytes into the ShooterView::Render() function. Now to find the line number in Render()
+
<source lang="c">
 +
#include <ogc/lwp_watchdog.h.h>
  
*Step 3:
+
int main () {
Look at the list file for the relevant function.  Here's an example:
+
double deltaTime;
 +
settime((uint64_t)0); //So we don't have to start with a huge number.
 +
uint64_t deltaTimeStart = gettime();
 +
while (1) {
 +
deltaTime = (double)(gettime() - deltaTimeStart) / (double)(TB_TIMER_CLOCK * 1000); // division is to convert from ticks to seconds
 +
deltaTimeStart = gettime();
 +
//Do something
 +
}
 +
}
 +
</source>
  
    473              .globl _ZN11ShooterView6RenderER17BibGraphicsDevice
+
Do not use clock() from time.h or any other library, clock() will return 4294967295 because the number of ticks is much larger than 32 bits. The function body for gettime() can be found in timesupp.c.
    474              .type _ZN11ShooterView6RenderER17BibGraphicsDevice, @function
 
    475              _ZN11ShooterView6RenderER17BibGraphicsDevice:
 
    476              .LFB1465:
 
    477              .loc 1 158 0
 
    478              .LVL20:
 
    479 02d0 9421FF00 stwu 1,-256(1)
 
  
The function names are mangled because this is C++ code.  See http://en.wikipedia.org/wiki/Name_mangling#Name_mangling_in_C.2B.2B
+
== GX Tips ==
The address of the first instruction of Render() is at 02d0.  This is also line 158 in the file (".loc 1 158 0").  To find the error location, just look at 0x2d0 + 72 = 0x318.  See below:
+
=== When your code hangs drawing ===
 +
If your GX application hangs as soon as you begin drawing with GX_Begin(), here are a few possible causes:
 +
* incorrect vertex formats - the vertex format specified with GX_SetVtxAttrFmt() must match what your app sends between GX_Begin() and GX_End()
 +
* incorrect vertex count - the vertex count specified by GX_Begin() must match the vertex count your app sends between GX_Begin() and GX_End()
 +
*incorrect vertex data order - vertex data is order dependent - the order GX expects vertex data is: position, normal, color, bi-normals, and texture coordinates
 +
* surpassing the maximum number of vertex per between GX_Begin() and GX_End() block which is 65,536 (not counting normals/colours)
  
                                .loc 1 168 0
+
=== Examples ===
    528 0314 809F0020 lwz 4,32(31)
+
If you are looking for GX examples. Devkitpro comes with a few GX examples which should help you to get started. Some knowledge about OpenGL is useful, but remember that GX is not the same as OpenGL.
    529 0318 2F840000 cmpwi 7,4,0
+
=== Texture Compression ===
 +
Do use texture compression on all textures where you can live with the lost picture quality. It reduces the memory usage and speeds up the loading and rendering.
 +
[[devkitPro]] comes with [[gxtexconv]] which supports DXT1 compression.
  
This shows machine address 0x318 has the proper machine code and the nearest .loc statement says the problem is at line 168 of the ShooterView.cpp.
+
To use it specify colfmt=14 in your script or on the command line. You get textures 1/4 the size of uncompressed RGB565 (colfmt=4) textures.
For more info on the assember output see the manual here:  http://sourceware.org/binutils/docs-2.18/as/index.html
 
  
=General Programming Tips=
+
[[Category:Development]]
*Keep your code commented throughout; it helps others help you.
 
*Any unused code should be deleted out of the program, unless it is a program for teaching people.
 
*If someone does the same thing in a more efficient way (i.e. faster and/or in less code), accept it and learn from it.
 

Latest revision as of 17:00, 26 November 2022

General Programming Tips

  • Keep your code commented throughout; it helps others help you. How much you comment it is a matter of debate; in general, people tend to comment bits of code where it isn't obvious what it actually does or why, or variables that aren't obvious what they are used for (or are not used until quite a bit later on).
  • Any unused code should be deleted out of the program, unless it is a program for teaching people.
  • If someone does the same thing in a more efficient way (i.e. faster and/or in less code), accept it and learn from it.
  • It is a good idea to release your app open source so others can learn from your code.
  • To keep things tidy, if you store files on SD, store them in a folder named the same as your application's name.

Code Snippets

Video Auto-Detect Routine

Here is the video detect code from the DevKitPro Wii example. Keep in mind, however, the libogc included in DevkitPPC since r15 does this for you already with one function call.

#include <gccore.h>
static GXRModeObj *rmode = NULL;
// ...
rmode = VIDEO_GetPreferredMode(NULL);

if( CONF_GetAspectRatio() )
{
	rmode->viWidth = 678;
	rmode->viXOrigin = (VI_MAX_WIDTH_PAL - 678)/2;
}
VIDEO_Configure(rmode);

Please see also: Display Issues

Exit to Loader

It's a good idea to add some way to return to the loader, otherwise you have to reboot your Wii to exit.

// Just call the exit() function to go back to the loader
// Returning from main also works, since that calls exit for you
    #include <stdlib.h>
    // ...
    exit(0);

How to use the Wiimote

A separate article is available: How to use the Wiimote.

Reboot Wii

Use:

#include <gccore.h>
// ...
SYS_ResetSystem(SYS_RESTART,0,0);

Or use SYS_RETURNTOMENU for a "soft" return to the system menu, SYS_POWEROFF to shut down the wii (automatically to the appropriate Idle or Standby mode, depending on the WC24 setting), or SYS_POWEROFF_STANDBY or _IDLE to specify the mode and override the system setting.

How to use the callback function for Power and Reset

This is just a way of doing this stuff, feel free to change the code.

s8 HWButton = -1;

/**
 * Callback for the reset button on the Wii.
 */
void WiiResetPressed()
{
	HWButton = SYS_RETURNTOMENU;
}

/**
 * Callback for the power button on the Wii.
 */
void WiiPowerPressed()
{
	HWButton = SYS_POWEROFF_STANDBY;
}

/**
 * Callback for the power button on the Wiimote.
 * @param[in] chan The Wiimote that pressed the button
 */
void WiimotePowerPressed(s32 chan)
{
	HWButton = SYS_POWEROFF_STANDBY;
}

/**
 * Entry point.
 * @param[in] argc The number of arguments invoked with the program
 * @param[in] argv The array containing the arguments
 * @return 0 on clean exit, an error code otherwise
 */
int main(int argc, char **argv)
{
	// Initialization

	SYS_SetResetCallback(WiiResetPressed);
	SYS_SetPowerCallback(WiiPowerPressed);
	WPAD_SetPowerButtonCallback(WiimotePowerPressed);

	while(1)
	{
		// Do Stuff Here
		if(HWButton != -1)
			break;
	}

	if(HWButton != -1)
	{
		SYS_ResetSystem(HWButton, 0, 0);
	}

	return 0;
}

Possible values for HWButton are:

#define SYS_RESTART 0 /*!< Reboot the gamecube, force, if necessary, to boot the IPL menu. Cold reset is issued */
#define SYS_HOTRESET 1 /*!< Restart the application. Kind of softreset */
#define SYS_SHUTDOWN 2 /*!< Shutdown the thread system, card management system etc. Leave current thread running and return to caller */
#define SYS_RETURNTOMENU 3 /*!< Directly load the Wii Channels menu, without actually cold-resetting the system */
#define SYS_POWEROFF 4 /*!< Powers off the Wii, automatically choosing Standby or Idle mode depending on the user's configuration */
#define SYS_POWEROFF_STANDBY 5 /*!< Powers off the Wii to standby (red LED, WC24 off) mode. */
#define SYS_POWEROFF_IDLE 6 /*!< Powers off the Wii to idle (yellow LED, WC24 on) mode. */

Note on Projection Matrices

There are a lot of tutorials and projects out there that are using standard 'Mtx' variables for projection matrices. Although this works in many cases (because the variables declared right after the matrix don't mind being corrupted once or twice), this is not correct, and is a crash waiting to happen. These matrices should be declared as 'Mtx44' objects instead. Note the prototypes for the projection matrix functions (see "ogc/gu.h"):

void guFrustum(Mtx44 mt,f32 t,f32 b,f32 l,f32 r,f32 n,f32 f);
void guPerspective(Mtx44 mt,f32 fovy,f32 aspect,f32 n,f32 f);
void guOrtho(Mtx44 mt,f32 t,f32 b,f32 l,f32 r,f32 n,f32 f);

One way to catch such errors is to enable more warnings in gcc like:

gcc -Wall -pedantic -std=c99

For example the current 'devkitPro\examples\wii\graphics\gx\triangle.c' uses 'Mtx' but it should be using 'Mtx44' - I fell into this trap, please be careful & take note of the above.

Timing

Use:

#include <ogc/lwp_watchdog.h.h>

int main () {
	double deltaTime;
	settime((uint64_t)0); //So we don't have to start with a huge number.
	uint64_t deltaTimeStart = gettime();
	while (1) {
		deltaTime = (double)(gettime() - deltaTimeStart) / (double)(TB_TIMER_CLOCK * 1000); // division is to convert from ticks to seconds
		deltaTimeStart = gettime();
		//Do something
	}
}

Do not use clock() from time.h or any other library, clock() will return 4294967295 because the number of ticks is much larger than 32 bits. The function body for gettime() can be found in timesupp.c.

GX Tips

When your code hangs drawing

If your GX application hangs as soon as you begin drawing with GX_Begin(), here are a few possible causes:

  • incorrect vertex formats - the vertex format specified with GX_SetVtxAttrFmt() must match what your app sends between GX_Begin() and GX_End()
  • incorrect vertex count - the vertex count specified by GX_Begin() must match the vertex count your app sends between GX_Begin() and GX_End()
  • incorrect vertex data order - vertex data is order dependent - the order GX expects vertex data is: position, normal, color, bi-normals, and texture coordinates
  • surpassing the maximum number of vertex per between GX_Begin() and GX_End() block which is 65,536 (not counting normals/colours)

Examples

If you are looking for GX examples. Devkitpro comes with a few GX examples which should help you to get started. Some knowledge about OpenGL is useful, but remember that GX is not the same as OpenGL.

Texture Compression

Do use texture compression on all textures where you can live with the lost picture quality. It reduces the memory usage and speeds up the loading and rendering. devkitPro comes with gxtexconv which supports DXT1 compression.

To use it specify colfmt=14 in your script or on the command line. You get textures 1/4 the size of uncompressed RGB565 (colfmt=4) textures.