console-dev.de

Home of VsTortoise, VisualHAM, N3D and HEL Library

Archive for the ‘GBA’ Category

callstack trace on the nintendo ds

Posted by Peter Schraut under GBA, NDS, Programming

introduction

I was experimenting how I could retrieve a stack trace on the Nintendo DS, using the devkitARM compiler, recently. A callstack is a list of active subroutines of a computer program. This can be used to identify from which code-path a particular subroutine was called, which is enormously helpful when detecting logical errors at run-time.

I tend to flood my code with “ASSERT’s”. ASSERT is used to identify logic errors during program development by implementing the expression argument to evaluate to FALSE only when the program is operating incorrectly and then calling a function to report the error:

1
2
3
4
5
6
7
8
9
void Vector2Add(Vector2 *dest, const Vector2 *v0, const Vector2 *v1)
{
  ASSERT(dest != NULL);
  ASSERT(v0 != NULL);
  ASSERT(v1 != NULL);
 
  dest->x = v0->x + v1->x;
  dest->y = v0->y + v1->y;
}

When any of the incoming arguments point to NULL, ASSERT will call a subroutine to report the error. Typically the report includes the filename, line number and functionname. This information is helpful, but lacks of information from where the function was called.

I use Vector2Add all over the place, how should I know what part in my program code does not work, when I only know at some point a NULL argument is passed to Vector2Add?

Right, I need to know from where Vector2Add was called, I need a callstack trace!

my earlier instrumentation approach

Around 2005-2006 I implemented a callstack trace feature in HEL Library. HEL Library is a middleware solution to ease development of Game Boy Advance titles.

The callstack trace was displayed together with an assertion-report, that looked like:
HEL Library assertion report

HEL Library callstack trace

Back then I used function instrumentation (-finstrument-functions) to build a list of subroutine calls at run-time. It was basically a callback that was called for every function that is about to be entered (__cyg_profile_func_enter) and left (__cyg_profile_func_exit).

This was a huge overhead and slowed down program-execution dramatically, but the callstack trace was so precious, that the performance penalty didn’t matter for the debug library.

fail does not mean to stop

When I started the Nintendo DS version, one goal was to use an approach which does not have any impact on run-time performance! I have tried several libraries, unfortunalety I didn’t find anything that worked for me.

This includes libunwind, backtrace, several GCC builtin-keywords to get return addresses of different calldepths, just to name a few.

I gave all up. It was either a never ending story to integrate the library in to the project or headers / functions were missing in libraries that come with devkitARM.

hacking to success, then fail

Rather than giving up, I decided to accept it as challenge and come up with my own callstack trace code. It was a long and rocky journey, but at the end I had something that worked suprisingly well.

What I do is basically pretty simple, I interpret instructions of interest to simulate the beaviour of the Program Counter (PC) and Stack Pointer (SP). The Program Counter indicates where the program is in its intruction sequence. Stack Pointer indicates the current top of stack memory. The stack this is where local variables are located.

Armed with my own Program Counter and Stack Pointer variables, I can traverse program code and react when Program Counter is assigned a new value, which basically means to jump to a different address and continue to operate there.

Example:

1
2
3
4
5
6
7
8
9
10
void FirstFunc()
{
  SecondFunc();
}
 
void SecondFunc()
{
  // Program Counter is located here  <--
  ThirdFunc();
}

Let’s presume the Program Counter is located in SecondFunc, which was previously called by FirstFunc.

The thumb assembler version of this looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
FirstFunc:
  push {lr}       ; Push Link Register (LR) on stack
  sub sp, sp, #4  ; Update Stack Pointer by 4bytes because LR was pushed
  bl  SecondFunc  ; Call to SecondFunc()
  add sp, sp, #4  ; Update Stack Pointer to point to pushed LR again
  pop {pc}        ; Pop LR from stack and store in Program Counter, this will return to caller
 
SecondFunc:
  push {lr}       ; Push Link Register (LR) on stack
  sub sp, sp, #4  ; Update Stack Pointer by 4bytes because LR was pushed
  bl  ThirdFunc   ; Call to ThirdFunc()
  add sp, sp, #4  ; Update Stack Pointer to point to pushed LR again
  pop {pc}        ; Pop LR from stack and store in Program Counter, this will return to caller

I need to get my hands on the Link Register (LR) to return to SecondFunc’s caller (*1). Since LR was being pushed on the stack, I know where to look! What is left is to traverse the program code in reversed order and interpret the “sub” and “push” instructions.

Moving the Program Counter in reversed order, which actually would be a backtrace imo, didn’t work out for some reasons and I wasn’t able to solve those problems. It did, however, worked in some cases, unfortunalety too unreliable.

when in doubt, try it out

I stayed away from this problem for week and then had the idea why not trying to simulate the Program Counter the same way it would walk along when it continues program execution normally. It means rather then traversing program code backwards, I tried what happens, when I forward advance PC. Now I have to interpret “add” and “pop” instructions and this works suprisingly well!

From here on it took a few hours to come up with a version that is robust enough to work in all places that I tested with my code base, which consists of both, C and C++ source code.

Obviously it does not mean it always works in every sitiuation with all source code in the world. For example, optimization level -O0 generates so many unconditional branches and places return code all over the place, that the Stacktrace function fails. It supports thumb code only and has problens with hand written/optimized assembler routines (when LR is not push’ed and PC not pop’ed).

I added a couple of more instruction Stacktrace looks out for:

1
2
3
4
5
pop {...pc} ; adjust PC to the pop'ed value
add sp, nn  ; adjust SP by nn
sub sp, nn  ; adjust SP by nn
b   ofs     ; set PC to the addr PC+ofs
add sp, rn  ;  adjust SP by the value rn points to

The unconditional branch instruction “b” is from hell, because it could lead to an infinite loop in Stacktrace, such as while(1) {}. For this reason I added two special cases:

  • Limit how many instructions are allowed to touch
  • Branch only to the target address when it’s different from PC

When any of the conditions evaluates to true, Stacktrace aborts and outputs the callstack only to this position. It will, however, report that an error occured.

Support for the unconditional branch was important, because the compiler generates code like this sometimes:

1
2
3
4
5
6
7
8
9
10
11
12
ExampleFunc:
  push   {lr}
  ...
  tst    r0, #2
  bne    .L92 ; if r0 is not 2 then jump to L92
  ...
.L90:
  pop    {pc} ; returns to caller
 
.L92:
  bl     AnotherFunction
  b      .L90 ; jump to L90 (above)

When we return from AnotherFunction we must jump to L90 to pop the return address from the stack and return to the caller. This is the reason why I added support for unconditional branches. I haven’t noticed other branches that are of importance for Stacktrace yet.

At this point of time I was able to return to each function-caller and have the return addresses, e.g:

Stacktrace output with return addresses only

resolving function names by hand

In order to map the memory address to an actual function, we have to look up the address in the so called Map file. Map files are typically plain text files that indicate the relative offsets of functions for a given version of a compiled binary. This file is generated by the linker when you pass -Map to it, here is a part of it:

1
2
3
4
5
6
7
.text   0x02000310    0xdf28
.text   0x02000380    0x1ac main.o
        0x02000394      PrintStacktrace
        0x0200046c      FirstFunc
        0x0200040c      ThirdFunc
        0x020004c8      main
        0x02000450      SecondFunc

Line 1 specifies where the .text section (program code) starts and its size. Line 2 specifies the start address of the object file main.o followed by all functions it contains. While writing this article, I have learned static functions are not listed in the map file.

In order to find the function name of the memory address 0×02000458 (from screenshot above), we have to check in which range it is located. But it would be too simple when the .map file has everything sorted by addresses, so we have to do this instead.

Here is a sorted list by addresses:

1
2
3
4
5
6
.text   0x02000380    0x1ac main.o
        0x02000394      PrintStacktrace
        0x0200040c      ThirdFunc
        0x02000450      SecondFunc
        0x0200046c      FirstFunc
        0x020004c8      main

The memory address 0×02000458 is between:

1
2
        0x02000450      SecondFunc
        0x0200046c      FirstFunc

So 0×02000458 belongs to SecondFunc. While this approach seems to work, it is complicated and really only works when you have the exact .map file available that was used to build the project. You can’t use a .map file of a different version of your code, this is very unlikely to work.

It would be so much better when the actual function name could be displayed instead!

what is your function name, dear memory address

Fortunalety, the compiler has an option to insert function names in plain-text infront of each function. This can be activated by adding -mpoke-function-name to the CFLAGS variable in your makefile.

But it not only inserts the name, it also inserts a bit-pattern to recognize that there is a function name as well as a relative offset to the name from this pattern.

What Stacktrace function does, it takes a memory address and then decreases this address until it detects the bit-pattern of the function name and we have it!

Stacktrace without functio names and return addresses

Well, there is of course a special case again. When -mpoke-function-name has not been specified, the function name bit-pattern is missing, thus Stacktrace would try to find it until it reaches the start of the text section. While this makes little sense, I added a limit how many 32bit words the “GetFunctionnameTag” function is allowed to touch before it returns “did not find”.

how to integrate in your own project

The stacktrace.zip archive contains the entire source code, you only need those two files:

  • stacktrace.h
  • stacktrace.c

Copy them to your source code directory. Then open the makefile file which should be located in your project directory also. You have to adjust the following variables:

1
2
ARCH   := -mthumb -mthumb-interwork
CFLAGS := -O2 -mpoke-function-name

Now do a rebuild, rather than a regular build. You can do this by performing a “make clean” first, then a “make”. Don’t forget to include stacktrace.h in that file where you want to use Stacktrace.

Optimization level -O2 worked best with Stacktrace so far. When you use more aggressive optimization levels the compiler also starts to inline code heavily, which can be quite confusing when the Stacktrace output does not reflect what you see in your high level code.

stacktrace interface and usage

The usage of Stacktrace function is fairly simple.

All it expects is an array of StacktraceEntry elements and the count of it. Here is the interface from stacktrace.h:

1
2
3
4
5
6
7
typedef struct
{
  unsigned int addr;  //!< Return address
  const char *name;   //!< Name of function
} StacktraceEntry;
 
unsigned int Stacktrace(StacktraceEntry* dest, unsigned int count);

Use it like this:

1
2
3
4
5
6
7
8
9
10
11
12
void OutputStacktrace()
{
  unsigned int n;
  unsigned int count;
  StacktraceEntry entries[32]; // allocate memory for 32 stacktrace entries
 
  // get only the 10 deepest stacktrace entries
  count = Stacktrace(entries, 10);
 
  for (n=0; n<count; ++n)
    printf("0x%08x: %s", entries[n].addr, entries[n].name);
}

improving stacktrace

If you want to improve Stacktrace, I recommend to read through the comments in stacktrace.c and enable STACKTRACE_VERBOSE, by setting it to 1.

This will, as far as OutputDebugString (also in stacktrace.c) uses a debug message type your Nintendo DS software emulator understands, output what instructions Stacktrace comes across and interprets.

I’ve added an opcode structure for all instructions Stacktrace interprets as well as print-routines to dump the contents.

I have used no$gba during development, which was of great help because of its “debugger“. Usually I yell about only see the assembler code in the shareware version of no$gba, but for this project it was exactly what I needed :)

conclusion

The Stacktrace function…

  • works suprisingly well for being such a hack
  • does not need debugging information/symbols
  • it runs directly on the target device, not needed to be connected to GDB or a similar debugger
  • does not come with any run-time performance penalty
  • works best with optimization level -O2
  • is most informative when adding -mpoke-function-name to CFLAGS in your makefile
  • comes with full documented source code
  • uses very little Nintendo DS specifics, except the text section start and length (portable to other ARM devices?)
  • works with thumb code only
  • does not detect when switching from thumb to arm mode
  • depends on LR / PC being push’ed and pop’ed, hand-written assembler routine can cause trouble
  • is tested in/with my code base only

frequently asked questions (faq)

Q: Why do I see so weird function names, like _ZN13DEMOPARTQUEUE9CutToPartEj and the like?

A: Because in C++, the function names go through name mangling. Name mangling is a technique used to solve various problems caused by the need to resolve unique names for programming entities.

download

Dear visiter, 100% OFF on console-dev.de, take your chance and download stacktrace.zip now! ;)

what I didn’t tell you yet

*1) LR does not always contains the address from where it was being called, but it holds the address where to return when the function completes. When using code optimization levels above -O2 the compiler e.g. generates code that does not return to functions where it was the last instruction involved anyway.

Single language vs. multi-language

Posted by Peter Schraut under GBA, NDS, Programming

Introduction

During my daily forum visits, I came across a thread about how to create a game in more than one language.

This reminded me on the first multi-language game I was working on. We ran into several issues back then and I want to share my experience with you, so you don’t make the same mistakes.

When you create a multi-language game, several things change that would be static otherwise. Here is a list a few things to consider:

  • You can’t hard-code every text, because you’re usually not the person who translates text.
  • You can’t assume texts have same lengths in different languages.
  • Prepare your GUI to word-/line-break and squeze text.
  • Different languages might imply different character ranges (char, wchar, utf-8, choose which one to support before you start)
  • Different languages might imply different font definitions
  • Different languages might imply different graphics: title logo, textures with baked-in text, etc
  • Different languages might imply different sounds: voice output
  • Being able to change language in the options menu, in case it is required.
  • Being able to change language at runtime during development.
  • Testing time increases with more languages
  • You might need to communicate with people who don’t speak your language (bug reports etc)
  • Several persons involved in development have more work to do: programmer, translator, quality assurance, (artist, sound artist maybe)

Localization from a programmers perspective

If text is available in more than one language, it must not be hard-coded in the game code.

Functionality to retrieve texts, depending on the current language setting is essential. It can be something simple like a function that expects an identifier that represents the text in question and returns its text or a database query.

We decided to edit texts in Microsoft Excel, because it’s standard in many offices and we don’t wanted to force translators to use some unknown custom in-house tool that he/she might not understand and we would have to support.

The spreadsheet structure looked like:

1
2
3
4
5
ID               EN                  DE            DESC
---------------+-------------------+-------------+---------------
helloworld     | HELLO WORLD       | HALLO WELT  | Displayed in the welcome screen
goodday        | GOOD DAY          | GUTEN TAG   | Displayed when the player confirms the welcome screen
...

ID represents the text identifier that is used to query the translated text.

EN and DE are the english and german translations.

DESC is an optional field that describes the purpose of this text. It can be quite challenging to find a good translation when you don’t know the context of the text, that’s what DESC should solve.

Localization, 1st iteration

Our first language system iteration worked like this:

  • Create texts in Microsoft Excel and save as XML
  • Custom tool to convert XML to our text format
  • Custom tool outputs .cpp file with texts as array
  • Custom tool outputs .h file with identifier #define’s that index into the text arrays

In order to get the translated text, we had a function that expects one of the generated identifiers and returned the corresponding text. The text system looked like this:

language.h file:

#ifndef __language_h__
#define __language_h__
 
#define TID_helloworld  0
#define TID_goodday     1
#define TID__MAX        2
 
#define LANG_EN  0
#define LANG_DE  1
 
const char* GetText(unsigned int textId);
#endif // __language_h__

language.cpp file:

#include "language.h"
 
// initial language is english
int CurrentLanguage=LANG_EN;
 
// german texts
const char* const TEXT_DE[]=
{
  "HALLO WELT",
  "GUTEN TAG"
};
 
// english texts
const char* const TEXT_EN[]=
{
  "HELLO WORLD",
  "GOOD DAY"
};
 
// get text of the specified text identifer
const char* GetText(unsigned int textId)
{
  assert(textId < TID__MAX); // invalid text id
 
  switch(CurrentLanguage)
  {
    case LANG_DE:
      return TEXT_DE[textId];
 
    default:
      break;
  }
 
  return TEXT_EN[textId];
}

Once we worked with it for a while, we realised updating the language file is a time killer.

Everytime we modified text and exported, the header file changed, thus all source files that include language.h were recompiled due to header dependencies. This was a huge problem for us, nobody wanted to wait several minutes for a recompile, only because someone fixed a typo in the translation or added a new text.

Localization, 2nd iteration

What we learned from the 1st approach is no matter if we change, add or even remove text, it must not have a significant influence on compile times.

Rather than using generated identifiers, we used crc32’s of string literals to identify texts. This completely removed the header dependency / recompile problem! Our text system now worked like:

  • Create texts in Microsoft Excel and save as XML
  • Custom tool to convert XML to our text format, verify that all text identifiers generate unique checksums
  • Custom tool outputs .cpp file with texts as array and checksum lookup table

In order to get a tranlated text, GetText() now expects a string literal as id, generates a crc32 of the incoming id, performs a binary search on the checksum table and then uses the lookup position to index into the language array.

This even allowed us to return the text id when the translated text was not found, so the tester could add the text id of the translated text that is missing to the bug report. But it also allowed us to switch text at runtime to display the id rather than text (”uh what text id is display here” belongs to the past).

The new text system was a bit slower than the 1st iteration, but it had no significant influence on the overall runtime performance.

We worried more about being able to have typos in text id’s, as those are strings and located in game code now, but this was never really a problem I think.

We added text id’s to the Excel file first, then always copy/paste text id’s from Excel to game code. However, we can’t be 100% certain that all possible missing texts were hunted down by the QA team. We still needed a verification system that does this automatically and 100% reliable, but more on this later.

Due to the additional checksum lookup table and the string literal id’s in the code itself, the game also requires more memory.

Most development systems feature some kind of debug memory (eg no$gba has an option to emulate 8MB rather than 4MB main memory), this is at least during development not a problem. More on this later, again.

language.h looked like this:

#pragma once
 
enum Languages
{
  LANG_EN,
  LANG_DE,
};
 
const char* GetText(const char* textId);

We changed from #defines to enum’s too, because those are much more debug friendly.

language.cpp looked like this:

#include "language.h"
 
// initial language is english
Languages CurrentLanguage=LANG_EN;
 
// crc32 checksums / hashes of text id's
// in sorted ascending order
const unsigned int TEXT_IDs[]=
{
  12345, // text id: helloworld
  23456, // text id: goodday
};
 
// german texts
const char* const TEXT_DE[]=
{
  "HALLO WELT",
  "GUTEN TAG"
};
 
// english texts
const char* const TEXT_EN[]=
{
  "HELLO WORLD",
  "GOOD DAY"
};
 
// performs a binary search on the the TEXT_IDs array
// and returns the index where the hash is located, or
// -1 when it could not be found.
int FindTextIndex(unsigned int hash)
{
  int left  = 0;
  int right = (sizeof(TEXT_IDs) / sizeof(TEXT_IDs[0])) - 1;
 
  while (left <= right)
  {
    int index = (left + right) / 2;
    if (hash == TEXT_IDd[index])
      return index; // hash found, leave!
 
    if (hash > TEXT_IDd[index])
      left = index + 1;
    else
      right = index - 1;
  }
 
  return -1; // hash not found
}
 
// get text of the specified text identifer,
// returns the textId if text could not be found
const char* GetText(const char* textId)
{
  // generate checksum / hash of incoming text id
  unsigned int hash = CalcCRC32(textId);
 
  // search for the checksum / hash in our TEXT_IDs array
  int index= FindTextIndex(hash);
  if(index == -1)
  {
    // text not found, return the id instead!
    return textId;
  }
 
  switch(CurrentLanguage)
  {
    case LANG_DE:
      return TEXT_DE[index];
 
    default:
      break;
  }
 
  return TEXT_EN[index];
}

Localization, 3rd iteration

The 2nd iteration is not bad, but as the project came along, new requirements did pop up.

We not only needed to display texts in different languages, different title logos should be displayed. We just hacked to load different resources, depending on the language setting, in game code.

But we should have known before, this ain’t fulfils the artists vision. So before we clump the whole game code, we decided it should be handled automatically without any action from a programmer and this was easier than we thought.

We already used some sort of file archive, where all game content is stored, to load files from. Think of it as a zip archive. In order to load language dependend resources, all we had to do is to support more than one file archive and priorize it. When the game requests a file, file archives are searched by priority.

We added an additional file archive with german content and high-priorized it. When the game requests “title.bmp”, the german archive was searched first. If the resource could not be found, the next archive was consulted. This allowed to add language dependend resources without any programmer work!

Localization, 4th iteration

Having all languages in main memory is quite a waste, at least on systems that don’t feature hundrets of mega-bytes. In my experience, non-text-heavy games contain about 700 texts, where edutainment games can contain thousands.

If every text would be 50 chars and 700 texts are available, it’s 50*700 = 35000 chars, which in ASCII is about 34kb for one language! 5 languages sum up to 34kb*5 = 170kb.

This is more than 4% of the Nintendo DS main memory, only for text! Not really an option to spend that much precious memory for text if you could use those wasted 135kb and spend them on a larger level, more sounds or more textures instead.

On memory limited systems it makes sense to have one language in memory only, namely the current language. However, this comes with several things to consider:

  • Memory consumption is different for different languages.
  • GetText() returns different pointers for different languages.

Different memory consumption is a huge problem. It’s irresponsible if some levels don’t load when german language is activated, because german texts consume 2kb more memory than the english ones. It makes it also impossible to replace entire language files on-the-fly, change language setting in options for example, without any delete/new mechanism involved.

Furthermore it’s also quirky when GetText() returns a different pointer for every language, because GUI widgets can no longer store pointers to texts, because they would point to whatever memory if the language setting changes.

The secret is always try to keep memory consumption as static as possible! Our custom text tool compared texts of every language and padded shorter texts of different languages with zero-bytes to consume as much space as the longest text, for example:

You won.000000000
Du hast gewonnen.
 
Hello World
Hallo Welt0

Where “0″ represents the padding 0×00 byte.

This makes sure that:

  • every language file has the exact same size.
  • offsets to texts inside the language file are always the same.

This approach makes it possible to allocate memory once for the language file and then being able to work with that buffer, because the size never changes for different language files of the same category. You can load other language files to this buffer and the text system still works.

When text is located in a language file, we also no longer have the const char* overhead from our text arrays, just make sure to null-terminate every text!

Localization, 5th iteration

The additional memory footprint introduced with the 2nd iteration bothered us and we wanted it better spending on textures than text and this is very simple again.

We supported a hybrid system of the 1st and 2nd iteration. The 2nd iteration was perfect for development purposes, as it does not require much recompile, but comes with higher memory footprint.

The 1st iteration on the other hand is horrible during development, because of the recompile times, but does not require any additional data (crc table) and is lighting-fast.

Instead of using string literals for the text identifier directly, we wrapped them in a TXT macro. The debug build stringified the incoming parameter, where as the release build concatenated it to create an identifier:

#if _DEBUG
  #define TXT(id) #id
#else
  #define TXT(id) TID_##id
#endif

It was used like this:

const char* text = GetText(TXT(helloworld));

The debug build replaced it with:

const char* text = GetText("helloworld");

and the release build with:

const char* text = GetText(TID_helloworld);

where TID_helloworld is the generated #define identifier of our custom text tool, as shown in 1st iteration.

We used the 2nd approach for debug builds and the 1st approach for release builds. When you switch between debug and release builds, you need to do a recompile anyway, so using the 1st does not hurt.

And at this point I can also resolve the “more on it later..” note from the 2nd iteration paragraph.

Because we used the 1st approach in release builds, we could catch all invalid text identifiers at compile time and had no memory overhead anymore, yay! Supporting both systems is also not really problematic in my opinion, since they’re pretty similar and not complicated anyway.

Conclusion

Creating a multi-language game comes with a couple of new tasks, don’t underestimate it! :-)

Super Mario: The Last GBA Quest

Posted by Peter Schraut under GBA

I found another Game Boy Advance homebrew game that was built with HEL Library 2.5. It’s “Super Mario: The Last GBA Quest” by YodaJr and omg. They entered NEO Compo 2008 with the game by the way.

You can download the ROM image right here.

Chocobo World Deluxe

Posted by Peter Schraut under GBA

I just came across another Game Boy Advance homebrew game that was built with HEL Library 2.5. The name is Chocobo World Deluxe and was developed by Davgav. He entered NEO Compo 2008 with the game.

You can download it, full source code included, either at Davgav’s Homepage or use the local mirror.

Playable demo of El Tío sin Chorra available

Posted by Peter Schraut under GBA

Chano M. writes:

This is an incomplete demo of the battle system. The buddies development is mostly done, but you only can do simple attacks and there’s no animation for them . Please try the demo a few times, because the baddies, the backgrounds and the intros change with each reset. The battle system is similar to the system used in Grandia games, but with a bunch of changes (for example, there’s no magic points).

Head over to playeradvance.org for more information about the game, including videos as well as the Game Boy Advance ROM image.

Demo of Chano’s RPG engine

Posted by Peter Schraut under GBA

Chano M., who some of you should know from the ngine.de forum, has released a video showing the battle-system of the RPG-engine. The engine is written with HEL Library, hence for the Game Boy Advance.

pdroms.de has organized another coding competition, running in the time from January 24th to March 2nd, 2008.

The goal is to create any kind of childproof game, using a maximum of four colors at once per scene for at least one of these systems:

Atari 2600, Coleco Vision, Gameboy, Gameboy Color, Gameboy Advance, Game Gear, Genesis/Megadrive, GP2x, Intellivision, Master System, Neo Geo Pocket, Neo Geo Pocket Color, Nintendo Entertainment System (NES), Super Nintendo Entertainment System (SNES), Nintendo DS, PC Engine, Playstation Portable (PSP), Wonderswan, Wonderswan Color

Full details at the PDRC #3.99 – Official Rules

Pikilipita Advance released

Posted by Peter Schraut under GBA

Under development for 3 years, this project is now completed! Pikilipita Advance is a software to mix visuals, somewhere between an old school game and a regular live visual mix software.

Pikilipita Advance is a collection of 30 stages ready to be enjoyed. The cartridge is compatible with Game Boy Advance systems, Nintendo DS and Game Cube+Game boy player (for live events), its production is limited to 80 copies. A ROM is also available: full version and demo version.

The official website is available at http://www.pikilipita.com/vj/

News borrowed from pdroms.de. If you wonder why I post it here too: Pikilipita Advance is made with HEL 2 Library.

Conker Advance v0.2

Posted by Peter Schraut under GBA

Being a HEL Library game hunter, sooner or later I find them all! My latest accomplishment is a platform game called Conker Advance, under development by crazy lapinou.

In the current version is just the player character that can move around in a scrolling world-map, but I think it has good potential to become a neat platformer!

You can download the latest Conker Advance releases from his website at http://conker-advance.fr.nf

HEL Library version 2.5 released

Posted by Peter Schraut under GBA, Programming

This version fixes some pretty nasty bugs in the Map-System, such as tile corruption and random crashes. Beside the fixes, I also included the latest Katie version.

Download it right here and don’t forget to refer to the online documentation for the changelog.

This site uses a Hackadelic PlugIn, Hackadelic SEO Table Of Contents 1.6.0.