Tuesday, September 13, 2011

Baby Steps in Nindendo DS “Homebrew” Hacking

(It is interesting to see how a company evolved from selling playing cards and tiny electronic gimmicks to a major market player in video games, when the video gaming industry actually flat-lined. Video Credit: http://blog.beforemario.com/)


While travelling to and from conferences or being in transit, I always missed some sort of entertainment device. I received a Kindle a while back but reading e-books all the time was not that rewarding. After doing some research, I settled on the Nintendo 3DS. The Nintendo DS and DSi its predecessors have a large base of 3rd party software apps, usually referred to as “homebrew”. Furthermore a fully customized GNU tool chain already exists for the device. Although there are no dedicated free development tools for the 3DS out there, it can run in compatibility mode with its predecessors. Hoping I may be able to program the 3D display and the camera I actually settled for the newer device. In addition this box also runs “Brainage 2”, several language trainers and, most importantly, “GTA Chinatown Wars” .... but this article is about hacking this thing.

Having taught operating systems in Waterloo, I deeply regret that I did discover this neat little toy and the growing developer community earlier. I probably might have adapted the term project for the students to be coded on this device instead of a boring Coldfire board. Unfortunately, I cannot change the past but only the future.



Nintendo DS Architecture
The Nintendo Architecture is actually a neat one. This toy is a little powerhouse. All DS models have two displays, one being a touch-screen, a WiFi adapter, a microphone and a synthesizer. Newer models come with an SD-card adapter, cameras and a 3D display. It has a dual-processor architecture, consisting of an ARM9 processor running at 66 MHz and an ARM7 processor running at 33 MHz. In general the ARM7 is usually used to take care of the peripherals, while the ARM9 runs the computationally intensive tasks, mainly graphics processing for the top-display. The initial Nintendo DS has about 4 MB of RAM and 656 KB of VRAM that is shared among the processors. Here you can find some neat pictures and explanation of the memory layout. If any of you remembers the layout of your ECE354 Coldfire project, trust me this is like Kindergarten compared to the NDS. I might port some of the OS project code to it for the heck of it, but figuring out a context-switch on two-processor system with shared memory architecture will take some time: P.



Development Environment
Using Linux the obvious choice was to install the modified ARM9 tool chain for the NDS, which can be found here. Follow their directions on installing it. As an emulator I chose Desmume.

Although you can use an emulator to run and test your code once you developed it, it is almost imperative to be able to run your code on the real device; and that is where free NDS development stops. Nintendo is and has historically been very obsessed with battling piracy. I think it goes without saying that since program this toy to the bare metal, you can easily pirate game roms as well. Therefore, Nindendo made substantial efforts to curb any sort of hacking with the device by implementing a sophisticated boot-loader for the roms. Although the firmware of the NDS is flashable, you probably do not want to risk turning your investment into a brick. A relatively cost-effective way to circumvent the boot loader is to buy a 3rd party adapter that spoofs the boot-loader and allows you to run your code in the device. In my case I ordered a R4i 3DS card that came with the “wood firmware” from OZ3DS. Although the shipper claimed to be in Australia, the package actually arrived last Saturday with Hong Kong Post.


The adapter came with an 8GB Micro-SD card that contained the firmware among other things. After piecing everything together and putting the card into my 3DS, the card was detected as “Sponge Bob Square Head”, this is the game that the engineers probably reverse engineered to circumvent the boot-loader. After starting it one looks at the main screen of the “wood” kernel. The kernel loads the file-system from the Micro-SD card. 
The “wood” firmware and your programs are saved as “NDS” files on the card. Whenever you select an NDS file the kernel will invoke the boot-loader and run the application. For those who have been around with computers for a while, this will feel an awful lot like MS-DOS or Amiga KickStart ... a single process operating system. Worse; when you exit the application you will be automatically thrown back into the 3DS GUI and not the “wood” kernel, which can be frustrating.



The First Tiny Steps
The first thing people program is usually an application that displays “Hello World” on the main screen of the box. Unfortunately, the NDS being a gaming device does not come with a nice firmware that allows you to invoke a BIOS/firmware routine to simply display an individual character on screen. Instead, characters would have to be rendered as sprites in the memory of the device and then copied to the screen. Worse initializing the two displays and processors may also not be that easy. To cut the explanation short, if you want to build something simple as hello world for starters, you need increase the level of abstraction and consider a 3rd party library that does this for you. Fortunately, such a library exists and comes with devkitPro. For our initial example, we will be borrowing the following functions: iprintf, consoleDemoInit and touchRead. “iprintf” is an integer-only version of printf that comes with the devkit.
  • Using iprintf instead of printf will potentially reduce the code size but otherwise makes little difference in our example.
  • consoleDemoInit performs several initializations, such as loading font sprites and initializing the bottom display for console output.
  • touchRead takes the last touch position and loads it into the given pointer.

Piecing everything together the finished looks like this
#include 
#include 
int main(void)
{
  consoleDemoInit();
  iprintf("Hello World!\n");
  while(1) {
    swiWaitForVBlank(); /* ?! */
    touchPosition touchXY;
    touchRead(&touchXY);
    iprintf("\x1b[1;0H");  /* ?! */
    iprintf("Touch x = %d   \n", touchXY.px);
    iprintf("Touch y = %d   \n", touchXY.py);
  }
  return 0;
}


If you read the code carefully, you will notice two potholes.
  1. What is that swiWaitForVBlank used for?
  2. What are those cryptic chars in the prinft?

Since this program continuously monitors the input from the touch-screen and displays the results on screen, we do not want to overrun the display routine. That is achieved by waiting for the VBLANK interrupt. The name stems from older systems that still had a CRT display. The interrupt was raised whenever the ray is turned off and the pointer returned to the upper left corner of the video screen. On the NDS the interrupt is raised whenever the display redraws.
The cryptic characters in the printf are an ANSI escape sequence that allows repositioning of the cursor. In fact our application will draw “Hello World” at the first line, then reposition the cursor at character position (1,0) and then print the coordinates. Because of the continuous while-loop the display would automatically scroll, if we do not reposition the cursor for each iteration. Running my first “homebrew” on the 3DS looks like this.



You can download the code from here (to come). The Makefile has only been slightly from the examples that came with devkitpro.



Summary
There is not much to summarize so far. The NDS is a really affordable toy that can, with some hacks, be easily programmed down to the bare hardware. There are numerous 3rd party emulators and developer tools for the NDS. In addition libNDS provides a very convenient high-level access to the hardware features of the device. The example shown here is very basic but shows how easy it is to program the box.
It actually took longer to take the pictures and write this article rather than coding the example. I will soon open some much more sophisticated open source projects.