
NOTE: Ecco 2 US Version MK-1553-00 is used in code examples below.
In the previous section we have reviewed some key aspects of the dynamic difficulty system used in Tides of Time. Things are about to get much more technical as we dive even deeper to see exactly what's going on under the hood!
Everything that is required to track the player's progress, the game keeps at the memory location of 0xA7BC. It's two bytes that they are using the individual nibbles of as follows:
0x0000 ^^^^ │││└--- Current difficulty (1: easy, 0: hard) ││└---- Force Easy Mode (0: disabled, 8: enabled) │└----- Deaths counter when easy/hard; Points when Normal └------ Force Hard Mode (0: disabled, 8: enabled)
Starting the game on the selection screen preloads this memory location with 0x1001. Selecting the Easy mode sets it to 0x0081, after which it stays that way and does not change throughout the rest of the game. Similarly, forcing Hard mode will fix it at 0x8000 -- note how Easy Mode flag EF mentioned above has been set to 0 and at the same time the Force Hard FH has been set instead.

In both cases above the third (from the right) nibble is then used to keep track of player's deaths as mentioned above. For example:
You can see now why the maximum number of player deaths the game can track is 15 at most since only 4 bits are reserved for that. This seems an odd choice, given how only the most significant single bit (0x80) is used as Hard Mode flag, so in theory they could have carried that data over and have the counter go as high as 127 or at least 31 if we limit ourselves to only one extra bit. For whatever reason that was not used and game does explicitly check for overflows to make sure the counter maxes out at 15. This data is saved along the rest inside passwords.
Here the memory location that’s used to track player’s death is now reserved to storing the difficulty points instead.

As game begins, it is preloaded with 0x0C01: easy mode flag is set and we are allocated 12 (0xC0) points. From now on the game will keep track of those and use them as follows:
You can see how unlike deaths counter they have an entire extra bit here, so the former going only up to 15 looks like a deliberately chosen value as with that it could at least go up to 31 as well if they so desired.
There are two main functions that are responsible for calculating the difficulty points. The one that decrements them every time you win is located at ROM offset 0x865D8. The function actually does a lot more than just that but if we focus on what's important, it is going to look like this when translated from Motorola 68000 assembly:
uint16_t winNormalStage(uint16_t points) {
uint16_t modifier = points >> 2;
return points - modifier;
}Note how thanks to it being integer division, the modifier will become zero as soon as points reach 3, which explains why they stop there. As you can see, not only the code for this is very short, but also very elegant!
Its sibling responsible for penalizing the player and living at ROM offset 0x868CC. Similarly, we are only showing the relevant code here:
uint16_t loseNormalStage(uint16_t points) {
uint16_t modifier = points;
if(modifier < 20) modifier++;
if(modifier >= 4) {
if((modifier < 10) modifier = 10;
}
points |= modifier;
return points;
}You can see how by carefully controlling the modifier and ORing it with the current points the programmer makes sure that the result cannot go beyond 31 (0x1F).
It is, however, worth noting how this also introduces some unpredictable behaviour for "in between" points the player may end up having. Consider a following situation:
Tides of time was a very unique game for its time on Mega Drive and one of the many aspects that made it such was its dynamic difficulty. I hope that this short article has helped demystify the features of this system and overall made you better appreciate the elegant code that Novotrade have come up with for this!
We welcome our readers to discuss this article on our Message Board!