Additional steps I am taking to make my next E-Reader game more robust
The Series One release of my E-Reader games was a pretty good success. But it did have a pretty bad problem, two of the games are a bit buggy. I decided to learn from this blunder and take steps to ensure my next E-Reader game is less buggy. Here's what I'm doing.

Be more cautious with a reverse engineered platform

One of my games lets you give treats to a dog. But if you give her too many, the game locks up. I was finally able to track this down to ERAPI's SpriteFree
function.
SpriteFree
does exactly what it sounds, it takes a sprite you earlier allocated and dellocates it, cleaning up all of its resources. But sometimes ... it doesn't? The thing is, I'm not entirely sure. I wrote a small test program that does this
for (let i = 0; i < 200; ++i) { const handle = ERAPI_SpriteCreate(spriteInfo); ERAPI_SetSpritePos(handle, i , 40); // display the sprite for 10 frames halt(10); ERAPI_SpriteFree(handle);}
The real app is in z80 assembly, but it's pretty much just the same as this for loop. This works fine and does exactly what you would expect. After creating and freeing 200 sprites, the system is still rocking along without any issue.
I even extended the app and had it run the for loop twice. So it creates 400 sprites no problem. Considering the E-Reader can only manage 56 sprites at once, this really seems like SpriteFree
is working correctly.
But ... if I just do this, the game will lock up.
for (let i = 0; i < 200; ++i) { const handle = ERAPI_SpriteCreate(spriteInfo); ERAPI_SetSpritePos(handle, i , 40); // display the sprite for 10 frames halt(10); // call halt one more time... halt(1); ERAPI_SpriteFree(handle);}
The only difference here is I am halting an extra time. halt
is the way to tell the E-Reader to render a frame to the screen.
This version of the app locks up after creating about 18 sprites. I can easily get the app to lock up by doing all kinds of things that on the surface don't seem related to SpriteFree
at all.
Why? I have no idea. But this tells me we don't fully understand SpriteFree
, and that makes it dangerous to use. And I paid the price with Franny Answers when people started reporting the game locking up. Doh...
So lesson number one learned. This is an obscure system that had to be reverse engineered to figure out how it works. That's awesome that people did that and enabled us to make E-Reader games, but reverse engineering is inherently less thorough and more error prone than a documented system intended for others to use.
No more SpriteFree
So for my next game, I stopped using SpriteFree
altogether. This was challenging, and required me to restructure the game quite a bit. But it was worth it, as now the game is much more robust and I actually like how much simpler it is now.
On top of this I now treat all ERAPI functions with a little bit of skepticism. Is that really all it does? Have we figured out what all of the parameters are? Is it safe to use in all scenarios? A little caution here can go a long way.
Avoiding complex games, at least for now ...
Another mistake I made was simply making the game Exo Attack. I was too ambitious on this one. Why's that? Exo Attack is a pretty complex game by E-Reader standards.

It's an action game where the boss, player, bullets, explosions, coins, all kinds of stuff are constantly moving and changing. This means the game has a ton of state in it, and it uses ERAPI quite a bit. The bugs in this game are game breaking and bad. One bug is just due to me making a simple coding mistake I never caught. Doh. And the other bugs? I honestly have no idea. They are hard to reproduce, and when they do reproduce, it's difficult to pinpoint what is causing them. That can be the dangers of a system that has so much mutating state in it.
I just think this game was perhaps a bit much for a first release. I should have saved the more complex endeavours for when I have more chops under my E-Reader belt.
Adding integration tests
For obscure systems like this, often the only practical way to test your game is to play it and look for bugs. But E-Reader games are written in z80 assembly. The z80 was a widly successful cpu used in many devices, so there is a lot of z80 related tooling out there. I was able to take some of these already existing tools, and adapt them for the E-Reader, due to it essentially being a z80 cpu at its core.
This improvement I'm really excited about! My upcoming game now has an integration test system and a suite of ever growing tests for it.
I took my E-Reader emulator and made a "headless" version of it. So it no longer shows the screen, plays sound effects or accepts input from a controller. Now the game runs just like before, but only in my computer's memory.
Using this emulator, I write a test that does something in the game, then examines the resulting game memory to make sure everything is as expected.
Here is one of those tests.
it('should set the clock_empty flag when the time runs out' , async function () { const runner = await createRunner();
runner .runUntil( 'scan in a card, choose first puzzle' , [ERAPI_KEY_A, ERAPI_KEY_A], (result) => { return result .getByte('_b_cur_size_tile') === 8; } );
const emptyClockResult = runner .runUntil('clock runs out' , (result) => { return result .getByte('clock_empty') === 1; });
expect(emptyClockResult.getWord('clock_seconds_counter')).toBe(0);});
My real runner and test code are a bit more complex than this, but the above is the overall gist.
The runner is the z80 emulator with my game loaded into it. runUntil
tells the emulator to run the game until a certain condition is met. Then it stops running it, and allows the test to examine the game's memory.
The array with ERAPI_KEY_A
in it are the inputs. So the first runUntil
presses the A button twice to get a puzzle loaded and running.
The second runUntil
has no inputs, it just sits there and allows the puzzle timer to run out. When it runs out, it verifies that the clock_empty
flag was set, and also verifies that clock_seconds_counter
is zero, which it needs to be so that the clock on the screen that the player sees shows 00:00
.
These tests are so helpful! I've already found several bugs thanks to them. And I can run them whenever I want. If a test fails, the runner outputs a log that lets me see exactly what the game was doing. This is helpful in figuring out bugs in the game itself, but also helps ensure the tests are doing the right thing too. Here is a little snippet of a log
pc: cursor_init erapi call : SpriteCreateerapi call : SpriteAutoAnimatepc: flash_init pc: hints_init erapi call : SpriteCreateerapi call : SetSpritePospc: hints_on_stop erapi call : SpriteHidepc: game_init pc: board_init erapi call : CreateRegionpc: numbers_init erapi call : CreateRegionerapi call : CreateRegionpc: clock_init erapi call : SpriteCreateerapi call : SetSpritePoserapi call : SpriteCreateerapi call : SpriteAutoMovepc: clock_on_stop
Tests make it easier to make changes
A common need for E-Reader games is to try and cut down how big they are, as only so much data can fit onto those paper cards. So with Pixel Pup, when I inevitably look for space savings, these tests can help ensure the changes I make to save bytes didn't break the game. I wish I had these tests when I reworked the game to not use SpriteFree
as I talked about above.
Tests aren't a silver bullet
Tests are super helpful and I'm a big advocate for them. But it's important to keep in mind they are just one layer of defense. The tests can still miss things, or even worse a test can be written incorrectly and give you false hope!
So I will still involve beta testers, do lots of manual testing, write defensive code, all that good stuff.
More beta testers
And speaking of beta testers, I plan to have more of them this time. For my first games I had friends and family play the games and test them out. That was helpful for sure, but I think if I had even more beta testers I may have found those bugs that ended up in the final games.
Conclusion
So yeah, lots of lessons learned from my first release. I'm confident Pixel Pup will be a more robust and less buggy game than Exo Attack was.
If you got this far and want to read more of my blatherings on the E-Reader, here they are. And if you own an E-Reader and want to try out some new games for them, maybe buy a Series One pack?. Just, uh, don't mind the bugs :)