The nes port is mostly done, I’ll tentatively call it v0.7, tentatively v0.8 would be if I ever manually fixed the graphics, v0.9 would be music and v1.0 would be adding the title screen, end flag, etc. The rest will be some of my rough thoughts on the process.



The core game engine was written in pure c, and the nes can be targeted using the same compiler, so at a glance, it’s all pure code and should be fairly easy, just a matter of separating out the graphics and recompiling, in practice it was not so easy. I got an early prototype running with no graphics …
The nes port is mostly done, I’ll tentatively call it v0.7, tentatively v0.8 would be if I ever manually fixed the graphics, v0.9 would be music and v1.0 would be adding the title screen, end flag, etc. The rest will be some of my rough thoughts on the process.



The core game engine was written in pure c, and the nes can be targeted using the same compiler, so at a glance, it’s all pure code and should be fairly easy, just a matter of separating out the graphics and recompiling, in practice it was not so easy. I got an early prototype running with no graphics fairly quickly, but getting it from, the game engine runs with no visuals to a working game was more of a struggle than you would think. There’s the obvious stuff like how the BG layer works is different, and a far more restricted palette, but my auto conversion code does a good enough job, and manual fixing could make it even better.
The handling of graphics went through a couple iterations, on the snes version, every object gets rendered as one 16x16 sprite, which is somewhat close to how the pico 8 version works, on the nes however, native sprites are only 8x8, so 4 sprites are needed per object which is fine, but on the nes, because of rooms with a lot of objects, it will both run out of hardware sprites, hit the scanline maximum of 8, and be excessively laggy in the snes implementation. Though initial tests did at least prove that it worked.
To counteract that, I started more deeply coupling an objects state change code into the rendering code, it’s still abstracted via a common interface so the main game code is shared between the nes and snes, but I fundamentally don’t think it’s possible to have a clean seperation of game code and rendering on these old consoles while keeping the framerate at 60, if the framerate was lowered to 30fps, it would be less of an issue, though going to a cpu less than half the speed of the snes one still hurt performance.
I introduced compression to try and reduce the the size of the compiled rom to fit in nrom by introducing compression on the level data, and it does greatly reduce the size of the level data for very little runtime cost in terms of number of frames because the level’s still decompress very quickly. The compression code does slow down the build python scripts runtime however, though level data wasn’t usually being edited so rerunning it wasn’t required for every change. Even with the compression though, I still ran into the upper limits of nrom due to code size.
Due to of the extensive use of fixed16 math for player movement, I couldn’t fit the game into nrom while keeping the way player movement the same, practically speaking it could probably theoretically fit in something like gnrom, but for simplicity I just picked unrom-512 so that rom size is a non issue. Practically speaking, the game fits in like 64kb, though the generated rom by llvm-mos is still 512kb. If anyone puts this on a cart, don’t worry about the empty banks, nothing checks for them/uses them.
Of course, this then introduced banking, in theory there’s multiple schemes to handle this but looking at llvm-mos’s nes sdk, the one that seemed to be the best supported was 16kb fixed with 16kb banks. In theory converting code to use banking should be fairly trivial, though because the code wasn’t written with banking in mind, it very much wasn’t and every byte moved out of the fixed bank was a hard fought battle, it was a bit of back and forth of move things out the fixed bank, add new feature, find more things to move, add new feature, etc.
Though eventually I got most things working to a satisfactory level, fundamentally at heart, I’m a systems programmer, so while the banking and CPU/PPU interactions and register programming, compression/decompression, and real time constraints on limited hardware are interesting, manually editing graphics is much less interesting to me, though if it ever get’s done, I’ll be sure to update the ROM here. Code is still on github.
In short order, the code needs to be cleaned up so that manual editing is even possible. Once that and some general refactoring and cleanup is completed, fixup the nes python script/build system so that editing levels/graphics and rebuilding afterwards is more streamlined. That way stuff like the level compression/decompression could more easily be reused across ports. Then consider future port(s), easy candidates are other llvm-mos targets like the PC Engine or Commander X16, the game should theoretically fit in 64kb so maybe a C64 port. Also potentially other 3rd/4th generation consoles with decent c compiler support. I don’t think it’s possible to bring it to 2nd generation consoles without major compromises, though I think this should stand as a testament that it’s possible to write games for old systems that run well, across multiple different systems, without resorting to pure asm all the way.
Lesson learned is that if you want to make a retro game engine that runs on multiple systems, account for banking in your core design from the beginning, because it’s easier to have the banking code do nothing in a flat memory model, than to try and shove a flat memory model into banking, particularly something are harsh as 16kb-16kb banking. Compression/banking will probably be core parts of your game engine and will need to be taken into account early on as such. I don’t think my current approach is the best, but you get better with practice and a pure game logic game rendering approach just isn’t viable on these old systems. At least not for a platformer running at 60fps on a sub 2MHZ cpu. I think other kinds of games where responsiveness is less critical like an rpg or a strategy game, this is more viable.