Porting NESticle for Fun and Profit: Part 0
Written by Paco Pascal, on 24 September 2022.
Tags:
#nes
#emulation
Introducing Porting NESticle for Fun & Profit
I've decided to take on the task of porting the leaked NESticle source to modern platforms. The end product isn't intended to be practical. If one's looking for an NES emulator, there are plenty of modern options available. This is purely for fun (and maybe profit); it is indeed, a labor of love.
At the moment, the plan is to post an article for each significant point of the project's development. I am also live streaming the development on Twitch. However, I don't actively engage with the audience much on Twitch and unlike these posts, the flow isn't as digestible for viewers. You are however, welcome to watch me struggle in real-time.
Also, do not expect the posts to remain in sync with the streams. The articles will lag far behind.
With respect to end goals, I am at the moment bias to preserving as much of the original NESticle content and methodology as possible; at least until the first working build/release. I imagine there are many improvements to be made. The code-base is a mixture of C++ and 32 bit x86 assembly. One obvious point of improvement would be removing the low level assembly code and make the code-base more portable. I'd love to see NESticle running on my Raspberry Pi. However, for reasons of strategy and historical preservation, I'm keeping the lack of portability and other inherit faults by avoiding implementing initial improvements.
I have already ported a chunk of the assembly code. For now, you should expect the initial releases to be 32bit x86 Linux executables.
Assessing the Situation
Tackling this project means moving through many unknowns. It's been a long time since NESticle's source code was updated. The C++ standards have changed; the compilers have changed; the operating systems have changed. We need to methodologically break down the problem and keep the unknown variables down to a minimum at each step. Therefore, the first thing to do is simply get each source file to compile. After we accomplished that, we can begin building off of the potentially working code.
But, before we can start compiling, we need the source code which can be downloaded at The Archive. After extracting the zip file, we see this mess of files and directories:
BACK/ COMMON.H CUNT.ZIP DSOUND.LIB FUCK.ZIP GUIROOT.H INPUTW.CPP LOCAL.H MOUSE.CPP NES.CSM NES.EXE NES.MBT NESSOUND.H NESVIDEO.LIB PTRN.ZIP README SCROLLFK.ZIP SOUND.H STDDLG.CPP TYPES.H VOL.H BUILD.CPP CONFIG.CPP CYCLE.ZIP EXAM GUICOLOR.H GUIVOL.H INPUTW.H MAIN.CPP MOUSE.H NES.~DE NES.H NES.MRT NESSRC.ZIP NESVIDEO.OLD R2IMGASM.ASM README~ SFUCK.ZIP SOUND.ZIP SYNC.ZIP USER.LIB VULNS.H BYTEOR~1.H CONFIG.H DD.H FILE.CPP GUI.H INCLUDES.H INSTALL MESSAGE.CPP NAMESERV.H NESDLG.CPP NESH~1.OLD NES.OBR NESTICLE.ICO PATTERN.ZIP R2IMGASM.OBJ RESOURCE.H SHITFUCK.ZIP SPRITE.ASM TILE.ASM UUTIMER.H WIN95.LIB CLIP.H CPU.CPP DDRAW.LIB FILE.H GUI.LIB INPUT.CPP KANJI.H MESSAGE.H NES95.MK NESDOS.MK NES.ICO NES.OLD NESVIDEO.CPP PPU.H R2IMG.CPP ROM.CPP SLIST.CPP SPRITEBG.ASM TILE.OBJ VERSION.H COMMAND.CPP CPU.OLD DISASM.CPP FONT.CPP GUIMENU.H INPUT.H KEYB.H MMC.CPP NES95.WPJ NESDOS.WPJ NES.IDE NESPAL.H NESVIDEO.FUK PROF.CPP R2IMG.H ROM.H SLIST.H SPRITEBG.OBJ TILE.OLD VOLBATCH.H COMMAND.H CRASH.ZIP DLGPOS.H FONT.H GUIRECT.H INPUT.LIB LOADPARM.H MMC.H NES.CPP NES.DSW NES.LIB NESSOUND.CPP NESVIDEO.H PROF.H R2.LIB SCRIPT1.RC SNAPSHOT.CPP SPRITE.OBJ TIMING.H VOL.CPP
Unfortunately, it's not clear what's going to be important. The README is not going to be helpful, because it's a completely unrelated document titled "NetBIOS Security Kit".
To make matters worse, all those zip files are different revisions of the code-base. Thus, we're going to have to decide which revision to port. At the moment, I suspect the newest revision is the best place to start.
The files in the top directory are from April 4th, 1997. Running the following in my fish shell,
for z in *.ZIP unzip -l $z | awk -v z=$z '$2 ~ "199[0-9].*" {print $2, z}' end | sort -r | uniq
we get the following (truncated) output:
1997-03-31 SOUND.ZIP 1997-03-30 SOUND.ZIP 1997-03-29 SOUND.ZIP 1997-03-28 SOUND.ZIP 1997-03-27 SOUND.ZIP 1997-03-27 CYCLE.ZIP 1997-03-27 CRASH.ZIP 1997-03-26 SOUND.ZIP 1997-03-26 CYCLE.ZIP # Omitted output 1995-02-06 SOUND.ZIP 1995-02-06 NESSRC.ZIP 1995-01-25 SOUND.ZIP 1995-01-25 NESSRC.ZIP 1994-01-16 SOUND.ZIP 1994-01-16 NESSRC.ZIP
Each line corresponds to a file within one of the zip archives. The left column is the modification or creation date and the right column is the zip archive the file belongs to. The lines are sorted by the date in descending order.
From this output, we can see that SOUND.ZIP
is the latest revision
from among the zip files. The files in the top directory are newer but
after poking around, it looks like the top directory is
incomplete. There's missing files such as 6502/6502.CPP
which exists
in SOUND.ZIP
. I'm betting starting with SOUND.ZIP
is the safer
bet. As we progress, we can always cross compare the different
revisions with our current working directory.
Preparing the code-base
Now that we picked which revision of the code-base we're porting, we need to prep the source files for working with on Linux. This code was originally written and built on DOS and Windows 95 with the Watcom C/C++ tool chain; all of which are ancient.
For starters, filenames on the the FAT file system (which is what
DOS uses) convert all filenames to uppercase. We're going to need
our filenames lowercase. If we don't convert them, the C++
preprocessor won't be able to include any of the header files. Our
modern file systems are (usually) case-sensitive. If one of our source
files is trying to include NES.H
by doing #include "nes.h"
, the
compiler will throw an error because it can't find the
file. Therefore, let's wack down our majuscules to minuscules.
# First change directory names to lowercase. for f in (find . -t d) mv $f (echo $f | tr [:upper:] [:lower:]) end # Then change filenames to lowercase. for f in (find . -t f) mv $f (echo $f | tr [:upper:] [:lower:]) end
Next, we need to change how newlines are defined. Anyone who has
copied a file from Windows over to a Unix-like operating system has
encountered this problem. The standard way of putting a newline in a
Windows text file is with "\r\n"
while on Linux (and Unix) it's
"\n"
. If we take a peek at the first line of main.cpp
, we can see
the extra "\r"
byte for the newline.
$ head -n1 main.cpp | xxd -g 1 00000000: 2f 2f 36 35 30 32 20 6e 65 73 20 65 6d 75 6c 61 //6502 nes emula 00000010: 74 6f 72 20 66 72 6f 6e 74 20 65 6e 64 0d 0a tor front end..
The two bytes 0d
and 0a
correspond to "\r"
and "\n"
respectively. To fix this, we just need to use a tool called
dos2unix
.
for f in (find . -type f) dos2unix $f end
If we double check the first line of main.cpp
, we'll confirm the
"\r"
is gone. I'll let you do it yourself.
Finishing Up
Now that we have the code-base prepped, we can start digging around trying to find a direction of attack. But first, let's commit this into a git repository as our initial starting point.
git init git add -A git commit -m init
From this point, we'll begin the actual porting of the code.