Hey!
So after I posted my last video about hacking the new Nintendo Game & Watch, the people
on twitter, in the youtube comments, and even my friends made one thing very clear:
The game & watch has to run Doom.
And so motivated by this, I got started - and wow, did I underestimate the effort. But,now
I'm excited to show you: The game & watch running doom!
It's not yet performing great, a lot of textures had to be removed, sound had to disabled,
the screen resolution is slightly reduced, and there's
still a lot to do before you can enjoy the full game, but it's running and it's playable!
Awesome!
And so in the next few minutes I want to give you a brief overview over what it took, and
what the challenges were, and what you can expect in terms of homebrew
on the game & watch in the future!
Now in the last video I already mentioned that the next step that needs to happen is
to dump the full firmware of the main processor - and we managed to do just that!
On Monday afternoon I teamed up with Konrad Beckmann - another hardware hacker from sweden,
who does really interesting hardware hacking & electronics stuff, and you should
go & follow him on twitter!
We bounced a couple of ideas back & forth, and eventually found that the original Game
& Watch firmware loads a bit of ARM code from
the external flash - in the same way, and with the same encryption, as the NES ROM in
the last video. After some more reverse engineering we were
able to load and execute a tiny piece of code that allowed us to dump the original firmware.
I'll go into more detail about this in a future video, and we'll soon be releasing scripts
so you can dump & backup your own game-and-watch, and also get
started with building custom homebrew for the device.
Next it was time to get the hardware running with our own code:
This is not as easy as it might sound: While the datasheets for the STM32 and also for
the flash are readily available, we had to reverse engineer all the pin connections
on the device, and use a logic analyzer to sniff and then reproduce the SPI commands
used to initialize the display. We document a lot
of this in the github repository linked in the description, so if you are interested
you should check it out!
Eventually we had the full hardware working: Display, buttons,
sound, and even the flash-chip were supported - which Konrad immediately put to good use
by using the game & watch to rick roll people on twitter.
Now that we had a basic, working project I could start porting doom over:
I started by looking around for other ports of doom for similar processors, and I found
stm32doom by floppes: A port of chocolate-doom to the STM32F4.
While it may sound like it would be easy to port this over to the stm32h7 used on the
game and watch, it unfortunately wasnt: STM32Doom is build for a board with over 8
megabytes of RAM, and uses a USB-stick as datastorage for the DOOM files.
On the game and watch, we only have 128kb of integrated flash, 1 megabyte of slower
external flash,and only 1.3 megabytes of RAM!
So obviously, there was quite a lot of work to be done.
Now DOOM uses a fileformat called WAD - where's all my data - as the package fileformat for
all game resources such as sprites, textures, levels and so on.
The original DOOM1 WAD is 4 megabytes in size, and given that we only have roughly 1 megabyte
of storage, in which we also need to fit the code, the
size of it had to be massively reduced. After some research, I found miniwad: a minimalist
doom iwad, that replaces all the textures with flat colors, all the enemy
models with simple outlines and so on - and so it wasn't pretty,
but wow was it tiny! After compression with a tool called wadptr it's barely a quarter
of a megabyte - perfect!
I started the port by copying all the chocolate-doom source files into my basic hardware-bringup
project, and just fixed whatever compilation issues
came up.
In the end I had to implement a couple of things:
A custom file loader that would load the WAD directly from a large buffer instead of using
filesystem functions such as WAD Custom framebuffer support
Game and watch button support and so on.
I also had to remove a couple of features, such as sound support and the infamous wipe
effect, just to reduce the memory usage. And eventually it was compiling and
flashing! But it was far away from actually running:
It would crash pretty much immediately on start because of memory issues, so I had to
look at the RAM usage:
We have a couple of ranges that are a must-have: We have the LCD framebuffer, which by itself
already takes up 154 kilobytes, we have 160 kilobytes of initialized game
state, 320kb of uninitialized data, and a large area for dynamic allocations.
And exactly these dynamic allocations were a large issue: The game has its own allocator,
but also uses malloc and calloc. The common use of allocating memory and freeing
it immediately quickly fragmented the memory, making it impossible
for the game to allocate new, larger memory areas.
I fixed that by replacing a lot of the dynamic buffers with statically
allocated buffers - which also had the advantage of making it easier to move data around the
different RAM regions. It also had the advantage of giving better error messages
on memory consumption at compile time!
And eventually, after a lot more testing, fixing, flashing, compiling, coding, waiting,
I got it: Doom running on the Game and watch.. at a
terribly bad performance!
It took multiple minutes to load, then multiple seconds per frame! Luckily, Konrad came to
the rescue!
He figured out how to increase the CPU & flash performance drastically, and after merging
those changes into my codebase doom suddenly became playable!
And here we are, DOOM running on the game & watch, without any hardware modifications.
And you can find the full source-code in the description!
Now this is just the beginning of homebrew on the Game & Watch, and we are currently
working on building a basic project for others to start building homebrew for
the device. If you are interested in following that journey along, please subscribe!
As always: Thank you for watching, and see you on this channel again soon!