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.
 

MrWhosHacking

Well-Known Member
Member
Joined
May 3, 2018
Messages
293
Trophies
0
Age
34
XP
505
Country
United States
I follow this tutorial for porting games/emulators to Nintendo Switch:



But I have some 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). That can't be right!? Somebody have any clue?


you mean a program that reads and write
 

DarkOrb

Well-Known Member
Member
Joined
Oct 11, 2013
Messages
290
Trophies
0
Age
31
XP
874
Country
Germany
you mean a program that reads and write

No, in layman's terms, a makefile is a file that tells the compiler, what header files to link with what source files (many source files need some header files to be able to use certain functions) and how to compile the program. Because every software has different source and header files, we have to rewrite each makefile, so it's refering to the source and header files of the new project.
 

salami68

Member
OP
Newcomer
Joined
Jun 1, 2018
Messages
13
Trophies
0
Age
27
XP
215
Country
United States
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?



The cannonball makefile looks really specialized to the cannonball project so it makes sense it would take a long time to adapt to something else. I like to start with the makefile from the switchbrew examples and then add what I need from there.
 

DarkOrb

Well-Known Member
Member
Joined
Oct 11, 2013
Messages
290
Trophies
0
Age
31
XP
874
Country
Germany
The cannonball makefile looks really specialized to the cannonball project so it makes sense it would take a long time to adapt to something else. I like to start with the makefile from the switchbrew examples and then add what I need from there.

I know. But that guy in the video mentioned, that he "cheated a bit" by using the Cannonball makefile for his Chocolate Doom project. So he did the same thing but still only need 45 minutes for everything.
 

Knucklesfan

Well-Known Member
Member
Joined
Sep 11, 2016
Messages
218
Trophies
0
Age
39
XP
726
Country
United States
Okay,so I'm running into a problem. I'm trying to port an open source Mario engine which only has one dependency (sdl2) and so far it built pretty well. It compiled a .elf file and then crashed. I decided to retry building, and this happened. https://pastebin.com/nfYPvD4s
What do these errors mean and what should I do? Thanks for the help.
 
Last edited by Knucklesfan,

greatfal

New Member
Newbie
Joined
Jul 16, 2018
Messages
1
Trophies
0
Age
34
XP
43
Country
Belgium
hi,

I'm stuck when i follow the video,
i'm recieving folowing error, when cloning the files from github

$ git clone
bash: git: command not found

any one could help me out
 

Deleted member 442567

Well-Known Member
Member
Joined
Feb 18, 2018
Messages
119
Trophies
0
Age
20
XP
451
Country
Germany
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.

hello thats a great tutorial anyone wanna help me porting the n64 emulator project 64 to the switch?
 

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
  • BigOnYa @ BigOnYa:
    He said he had 3 different doctors apt this week, so he prob there. Something about gerbal extraction, I don't know.
    +1
  • ZeroT21 @ ZeroT21:
    bored, guess i'll spread more democracy
  • LeoTCK @ LeoTCK:
    @K3Nv2 one more time you say such bs to @BakerMan and I'll smack you across the whole planet
  • K3Nv2 @ K3Nv2:
    Make sure you smack my booty daddy
    +1
  • LeoTCK @ LeoTCK:
    telling him that my partner is luke...does he look like someone with such big ne
    eds?
  • LeoTCK @ LeoTCK:
    do you really think I could stand living with someone like luke?
  • LeoTCK @ LeoTCK:
    I suppose luke has "special needs" but he's not my partner, did you just say that to piss me off again?
  • LeoTCK @ LeoTCK:
    besides I had bigger worries today
  • LeoTCK @ LeoTCK:
    but what do you know about that, you won't believe me anyways
  • K3Nv2 @ K3Nv2:
    @BigOnYa can answer that
  • BigOnYa @ BigOnYa:
    BigOnYa already left the chat
  • K3Nv2 @ K3Nv2:
    Biginya
  • BigOnYa @ BigOnYa:
    Auto correct got me, I'm on my tablet, i need to turn that shit off
  • K3Nv2 @ K3Nv2:
    With other tabs open you perv
  • BigOnYa @ BigOnYa:
    I'm actually in my shed, bout to cut 2-3 acres of grass, my back yard.
  • K3Nv2 @ K3Nv2:
    I use to have a guy for that thanks richard
  • BigOnYa @ BigOnYa:
    I use my tablet to stream to a bluetooth speaker when in shed. iHeartRadio, FlyNation
  • K3Nv2 @ K3Nv2:
    While the victims are being buried
  • K3Nv2 @ K3Nv2:
    Grave shovel
  • BigOnYa @ BigOnYa:
    Nuh those goto the edge of the property (maybe just on the other side of)
  • K3Nv2 @ K3Nv2:
    On the neighbors side
    +1
  • BigOnYa @ BigOnYa:
    Yup, by the weird smelly green bushy looking plants.
  • Xdqwerty @ Xdqwerty:
    Water park was quite fun
    Xdqwerty @ Xdqwerty: Water park was quite fun