One program, two machines

Lore of the Ember targets the STe, a machine that can slide its own image around effortlessly. That comfort has carried the engine from the start.

Except that the real installed base isn't all STe. Plenty of people, myself included, have the original machine, the STf, which doesn't have that hardware help. And an Atari ST game that refuses to boot on an STf is, for part of the audience, a game that doesn't exist.

I had two options: two separate versions, or a single program that recognises the machine at startup and picks its display mode by itself. I took the second. On STe, the original hardware scroll, untouched. On STf, a scroll I write for the occasion. The rest of the game, the spreading corruption, the controls, the enemies, the boss, doesn't even know which machine it's running on.

This post is about that second mode. And above all about the weeks when I thought the problem was the scroll, when the truth lay elsewhere.

The STf cannot scroll

On the STf, scrolling the image means doing everything by hand. Shifting the scenery by a few pixels means copying the entire play area on every frame, and that is slow. If you naively shift the pixels on every frame, you drop below ten frames per second. Unplayable.

I had documented that ceiling from the start, by watching real games shipped on the STf run. The practical consequence is that the STf displays half as often as the STe. So that the game doesn't run in slow motion on the STf, I made all the logic independent of the display rate: the speed of the world stays the same whether the screen refreshes fast or slowly. Without that, the STf would play in slow motion.

That left making horizontal scrolling affordable. The answer: a cache.

Prepare rather than recompute

The idea is the same old trick as for the sprites: compute almost nothing live. Rather than reworking the scenery on every frame, I prepare the shifted versions of the visible columns ahead of time, and the display only has to copy the right one. A copy costs far less than a recalculation. When the camera moves forward, only the columns coming into view are prepared, the rest are already ready.

It costs a little memory, and memory was already tight. So I housed that cache in a space that lies dormant on the STf: on the STe, the HUD scenery is prepared in several copies to follow the hardware scroll, but on the STf that scroll doesn't exist, so the space stays free. It fitted exactly.

On paper it was done. On screen it rippled.

The waves, first culprit

The scenery rippled when the hero moved, especially at the top of the play area. A subtle wave, a few pixels, but definitely there, and unbearable once you have seen it.

I spent an unreasonable amount of time blaming my cache. I disabled everything one piece at a time, a daft and slow method, one test per hypothesis. Every time, the waves stayed. The real culprit was elsewhere: a display trick I had carried over from the STe that simply doesn't work on the STf. It disturbed the image right in the middle of it being drawn, and that looked exactly like waves.

The lesson was clear: what works in hardware on the STe breaks the image on the STf. So I removed it on the STf. And the scroll no longer waved for that reason.

Except the waves were still there.

The waves, second culprit

Once the first lead was ruled out, I went back over all my tests. The scenery in Lore of the Ember is alive: water animates, corruption spreads and changes the ground constantly. But my cache, which prepares columns ahead of time, froze that scenery at the moment it prepared it. While the world kept moving, the cache was showing an out-of-date version. When scrolling, you could see that lag in time as a wave.

The fix leans on something the engine already knows how to do: spot the scenery cells that change and redraw them. All it took was to also refresh the cache for those same cells, right afterwards. The cache stays in step with the world.

One last trap remained. When the plague spreads, it changes a lot of cells at once. Refreshing all of it in the same frame is a hitch. So I spread that work over several frames and slow the pulse of the corruption slightly on the STf so it keeps up. The cache takes an extra second to update after a big change, which is invisible in play, and the smoothness never dips.

The quarry scenery scrolling on both machines: the STe hardware scroll on one side, the STf software scroll on the other, at the same smoothness.

The freezes that weren't where I thought

The game was smooth and stable. Then two one-second freezes appeared: one when going right for the first time, one when shooting at the plague. Black screen, nothing.

I first suspected my cache, then the dialogue. Wrong on both counts. Investigating properly, I saw that the screen stayed frozen in the middle of a fade to black. And fades are the transitions between rooms.

The real culprit was a bad initial assumption: several sequences of the engine (fades, transitions) had been written assuming that displaying an image cost nothing, which is true on the STe but false on the STf. On the STf, every small step of the fade redid the entire display work, and a three-second fade was the result. Since the level is designed to run rooms together seamlessly, crossing a border gave the impression of staying in the same place, with a long stretch of black in the middle.

The fix is simple: during a fade the scene is frozen, so there is no reason to redisplay everything at each step. Three seconds became a fraction of a second, and both freezes vanished at once.

Taking stock

What is delivered: a single program that boots on the STe as well as the STf. On the STe, the original hardware scroll, untouched and fast. On the STf, a smooth hand-written scroll, without waves and without freezes, with game logic that runs at the same speed on both machines.

What honestly remains to be done on the STf: the dialogue. It relied on the display trick I had to remove, and its position conflicts with the STf HUD. For now, on the STf, the narrative murmurs are skipped rather than freezing the game. A dialogue rendering path specific to the STf is the next job. On the STe nothing has moved, the dialogue is there.

The lesson of these weeks fits in one sentence: on the STf, everything the STe does for free costs time, and several parts of the engine had been written assuming the screen updated itself. It was never the scroll that cost me the most evenings. It was everything that assumed display was free.

The build for this milestone is downloadable here (a single STe + STf binary). Run it in Hatari in STf mode to see the software scroll, or in STe mode for the hardware scroll: it's the same file. Arrows to move, fire to clean the plague. For the music, take the archive from the homepage instead, you need the .SND next to the binary. For the current version of the game, see the homepage.