Porting an emulator to Nintendo Swith (Tutorial/Walkthrough and Discussion)

Hey guys, A few weeks ago I ported a CHIP 8 emulator to the Nintendo switch and I was pretty surprised with how easy it turned out to be. I wanted to do a quick write up/guide so other people can hopefully get involved. I am hoping to find some people interested in porting other things to help me with future projects.
It would be great to get a few people going to work together on stuff and bounce ideas off of each other. Without further introduction, here begins my guide:

Step 1: Finding a suitable candidate for porting
The first thing we need to do is find something to port, right now the tools we have in libnx are somewhat limited, but we do have SDL2! I wanted to start off simple, so I decided to port a CHIP 8 emulator, possibly the easiest system to emulate. Because I knew I had access to SDL2, I googled "SDL2 chip 8 emulator".
I was able to find this project on github (scanlong/c8-master) which is licensed under the WTHPL license, which means "do whatever the hell you want". This is a perfect candidate for our purposes because it's only dependency is SDL2 and is written in standard c.

Step 2: Successful compilation
Now we have a source that we are ready to start working on. However, it will not compile under libnx currently, as it was written for pc. We need to completely rewrite the makefile.
A good starting point is a makefile from one of the switchbrew examples on github. However we will need to make some changes to get it to compile.

To link SDL2 to the source, we need to add lLSDL2 to the libs line in the makefile.

From:
LIBS := -lnx
To:
LIBS := -lSDL2 -lm -lnx



Notice we also added -lm library, which is for mathematical functions, which we also need.

Now we need to change some of the include statements in the header files to find SDL2 in the correct place.

From:
#include "SDL.h"
To:
#include <SDL2/SDL.h>
#include <SDL2/SDL_events.h>


We now have a compilable project! However it still does not work.

Step 3: Getting it to run
If we try to run our current build, we will notice that it exits immediately.
If we look at the entrypoint to the emulator, we will notice a few things:



int main(int argc, char* args[]) {
if (argc != 2) {
printf("usage: c8 game\n");
printf(" game: Chip8 bianry file to play\n");
return -1;
}
char* filename = args[1];

struct cpu cpu;
if (cpu_init(&cpu, filename)) {
fprintf(stderr, "Unable to initialize the emulator!\n");
return -1;
};

struct display display;
if (display_init(&display)) {
fprintf(stderr, "Unable to initialize the display!\n");
return -1;
}


The first thing we see is that the emu is looking for command line arguments, there's no way were passing any command line args on the switch, so we have to remove those checks. The arguments are for a path to a rom, and to make this quick and not jump into switch FS stuff, we will use a hardcoded rom for this proof of concept.

We can remove this entire section of code.



if (argc != 2) {
printf("usage: c8 game\n");
printf(" game: Chip8 bianry file to play\n");
return -1;
}
char* filename = args[1];





We wont use filename, but it is an argument for the creation of the cpu, so when we call the cpu create method, we can put whatever in there:


cpu_init(&cpu, "_");


Now, in the cpu.c file, we see this section of code related to pulling a rom from the filesystem:


FILE* game = fopen(filename, "rb");
if (!game) {
fprintf(stderr, "Unable to open file '%s'!\n", filename);
return -1;
}
fread(&cpu->memory[PC_START], 1, MEMORY_SIZE - PC_START, game);
fclose(game);



You can comment this all out or delete it, we will use a hardcoded rom for now, just to get something running.

To get the data from a chip8 rom, you can run xxd -i on it, which will output a C array with the relevent binary data.


unsigned char rom_bin[] = {
0x12, 0x25, 0x53, 0x50, 0x41, 0x43, 0x45...


We will store this on the heap, and then copy it into the cpu struct for the emulator.

memcpy(&cpu->memory[PC_START], &rom_bin, MEMORY_SIZE-PC_START);


We are getting close, but the program still immediately crashes.

After some debugging, we find the culprit in the display.c file:

display->renderer =
SDL_CreateRenderer(display->window, -1, SDL_RENDERER_ACCELERATED);
if (display_check_error("renderer", display->renderer)) return -1;


We have no accelerated rendering in libnx yet, so we must use software rendering.

display->renderer =
SDL_CreateRenderer(display->window, -1, SDL_RENDERER_SOFTWARE);
if (display_check_error("renderer", display->renderer)) return -1;


Now make again, and test. We now have a working emulator!

Step 4: Next steps
This is far from finished, and uses some bad coding practices to get things going, but I think its a great beginners proof of concept.
You can use what you've learned and more research to add controller support, loading roms from filesystem, and any other features!

Thanks for reading guys, this is my first tutorial so please let me know if I could have done anything better, or anything needs clarification. I would post some pictures, links, and files, but I don't think I have enough posts yet to do that.
 

salami68

Member
OP
Newcomer
Joined
Jun 1, 2018
Messages
13
Trophies
0
Age
27
XP
215
Country
United States
Thanks for the positive feedback guys, I realized I completely skipped instructions on how to set up the dev environment, but they are available if you search the switchbrew wiki.
 

jimmyj

Official founder of altariaism. Copyright jimmyj
Member
Joined
May 26, 2017
Messages
1,485
Trophies
1
Location
Hyrule
XP
1,632
Country
United Kingdom
Thanks for the positive feedback guys, I realized I completely skipped instructions on how to set up the dev environment, but they are available if you search the switchbrew wiki.
which makefile should I be using?

--------------------- MERGED ---------------------------

so just to be sure,I just need devkitpro?
 

salami68

Member
OP
Newcomer
Joined
Jun 1, 2018
Messages
13
Trophies
0
Age
27
XP
215
Country
United States
which makefile should I be using?

--------------------- MERGED ---------------------------

so just to be sure,I just need devkitpro?

Download one of the sample makfiles from the switchbrew examples repo, then make the changes that i mentioned.

Yes you need devkitpro and make sure to also install the SDL2 packages with pacman or msys on windows
 
  • Like
Reactions: jimmyj

MrWhosHacking

Well-Known Member
Member
Joined
May 3, 2018
Messages
293
Trophies
0
Age
34
XP
505
Country
United States
Hey guys, A few weeks ago I ported a CHIP 8 emulator to the Nintendo switch and I was pretty surprised with how easy it turned out to be. I wanted to do a quick write up/guide so other people can hopefully get involved. I am hoping to find some people interested in porting other things to help me with future projects.
It would be great to get a few people going to work together on stuff and bounce ideas off of each other. Without further introduction, here begins my guide:

Step 1: Finding a suitable candidate for porting
The first thing we need to do is find something to port, right now the tools we have in libnx are somewhat limited, but we do have SDL2! I wanted to start off simple, so I decided to port a CHIP 8 emulator, possibly the easiest system to emulate. Because I knew I had access to SDL2, I googled "SDL2 chip 8 emulator".
I was able to find this project on github (scanlong/c8-master) which is licensed under the WTHPL license, which means "do whatever the hell you want". This is a perfect candidate for our purposes because it's only dependency is SDL2 and is written in standard c.

Step 2: Successful compilation
Now we have a source that we are ready to start working on. However, it will not compile under libnx currently, as it was written for pc. We need to completely rewrite the makefile.
A good starting point is a makefile from one of the switchbrew examples on github. However we will need to make some changes to get it to compile.

To link SDL2 to the source, we need to add lLSDL2 to the libs line in the makefile.

From:
LIBS := -lnx
To:
LIBS := -lSDL2 -lm -lnx



Notice we also added -lm library, which is for mathematical functions, which we also need.

Now we need to change some of the include statements in the header files to find SDL2 in the correct place.

From:
#include "SDL.h"
To:
#include <SDL2/SDL.h>
#include <SDL2/SDL_events.h>


We now have a compilable project! However it still does not work.

Step 3: Getting it to run
If we try to run our current build, we will notice that it exits immediately.
If we look at the entrypoint to the emulator, we will notice a few things:



int main(int argc, char* args[]) {
if (argc != 2) {
printf("usage: c8 game\n");
printf(" game: Chip8 bianry file to play\n");
return -1;
}
char* filename = args[1];

struct cpu cpu;
if (cpu_init(&cpu, filename)) {
fprintf(stderr, "Unable to initialize the emulator!\n");
return -1;
};

struct display display;
if (display_init(&display)) {
fprintf(stderr, "Unable to initialize the display!\n");
return -1;
}


The first thing we see is that the emu is looking for command line arguments, there's no way were passing any command line args on the switch, so we have to remove those checks. The arguments are for a path to a rom, and to make this quick and not jump into switch FS stuff, we will use a hardcoded rom for this proof of concept.

We can remove this entire section of code.



if (argc != 2) {
printf("usage: c8 game\n");
printf(" game: Chip8 bianry file to play\n");
return -1;
}
char* filename = args[1];





We wont use filename, but it is an argument for the creation of the cpu, so when we call the cpu create method, we can put whatever in there:


cpu_init(&cpu, "_");


Now, in the cpu.c file, we see this section of code related to pulling a rom from the filesystem:


FILE* game = fopen(filename, "rb");
if (!game) {
fprintf(stderr, "Unable to open file '%s'!\n", filename);
return -1;
}
fread(&cpu->memory[PC_START], 1, MEMORY_SIZE - PC_START, game);
fclose(game);



You can comment this all out or delete it, we will use a hardcoded rom for now, just to get something running.

To get the data from a chip8 rom, you can run xxd -i on it, which will output a C array with the relevent binary data.


unsigned char rom_bin[] = {
0x12, 0x25, 0x53, 0x50, 0x41, 0x43, 0x45...


We will store this on the heap, and then copy it into the cpu struct for the emulator.

memcpy(&cpu->memory[PC_START], &rom_bin, MEMORY_SIZE-PC_START);


We are getting close, but the program still immediately crashes.

After some debugging, we find the culprit in the display.c file:

display->renderer =
SDL_CreateRenderer(display->window, -1, SDL_RENDERER_ACCELERATED);
if (display_check_error("renderer", display->renderer)) return -1;


We have no accelerated rendering in libnx yet, so we must use software rendering.

display->renderer =
SDL_CreateRenderer(display->window, -1, SDL_RENDERER_SOFTWARE);
if (display_check_error("renderer", display->renderer)) return -1;


Now make again, and test. We now have a working emulator!

Step 4: Next steps
This is far from finished, and uses some bad coding practices to get things going, but I think its a great beginners proof of concept.
You can use what you've learned and more research to add controller support, loading roms from filesystem, and any other features!

Thanks for reading guys, this is my first tutorial so please let me know if I could have done anything better, or anything needs clarification. I would post some pictures, links, and files, but I don't think I have enough posts yet to do that.
Is there a video how to
 

DarkOrb

Well-Known Member
Member
Joined
Oct 11, 2013
Messages
290
Trophies
0
Age
31
XP
874
Country
Germany
I follow this tutorial for porting games/emulators to Nintendo Switch:



But I have problems modifying an existing makefile (I'm using the makefile from the "CannonBall Engine") so it's working with my project. Somebody has any idea how to modify a makefile so it's refering to the source files of another project? "MVG" said that it took him 45 minutes to port chocolate doom, but it took me 6+ hours to modify the existing CannonBall makefile alone so it's refering to the source files (and headers) of the project I want to port (but it still doesn't work, I don't know what's wrong). That can't be right!? Somebody have any clue?
 
Last edited by DarkOrb,
  • Like
Reactions: MrWhosHacking

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
    SylverReZ @ SylverReZ: Had two cheeseburgers and a coke to fuel me up.