This page documents the known esoteric tricks of Battle of Olympus.
We concentrate here on tricks that are mainly usable in a TAS.
Climbing on non-existing stairs
Stub. TODO: Expand this section.
Press up+down while descending/ascending stairs, and depending on situation,
you might start climbing non-existing stairs.
When you reach the bottom or the top of the screen, depending on situation,
you may make the screen scroll immediately to the left or the right, even
repeatedly, by attempting to walk. This may enable immense shortcuts in the game.
Super fast horizontal motion.
How velocity works
Note that climbing has different mechanics. This explanation only
applies to walking and jumping.
"Velocity" here refers to a variable, found in the memory address $0053,
which tells how much will be added to the hero's X position at every frame.
Negative values mean leftwards motion, and positive values mean rightwards motion.
The high 4 bits refer to fullpixel motion, low 4 bits refer to subpixel motion. 2^4 is 16, thus divide the velocity by 16 to get the motion speed in pixels per frame.
Examples:
- Velocity of +16 means moving to the right at 1.0 pix/frame
- Velocity of −24 means moving to the left at 1.5 pix/frame (24 ∕ 16 = 1.5)
When the hero enters the view from somewhere, his velocity starts from
either +16 or −16, depending on whether he moves to the right, or to
the left, respectively.
When the player holds down the Left button:
If velocity ≥ 0, velocity decreases by 2. (Meaning that the hero accelerates towards the left)
If velocity = −24, nothing happens.
If velocity < 0 and ≠ −24, velocity decreases by 1.
When the player holds down the Right button:
If velocity ≤ 0, velocity increases by 2.
If velocity = 24, nothing happens.
If velocity > 0 and ≠ 24, velocity increases by 1.
When the player holds down both Left and right:
If velocity = 15, the velocity is set to 16.
If velocity = 16, nothing happens.
If velocity ≠ 15 or 16, velocity increases by 2.
When the player does not hold either Left or Right, or is holding Down:
If the player is jumping, the velocity is preserved. Otherwise:
The velocity increases or decreases by 1 towards zero.
However, when the player is fighting:
The Left and Right keys do not affect at all, except to change the hero's facing.
If the player is jumping, the velocity is preserved. Otherwise, it is set to 0 immediately.
The velocity is a 8-bit signed integer variable, meaning
that increasing +127 by 1 yields −128, and decreasing −128 by 1 yields +127.
The fastest way to move to the right
Hold Left+Right, until the velocity is 16. Then hold only Right for 1 frame
(to get the velocity into 17), and then hold Left+Right again, until the
velocity is 127. Do not hold Right after that, or the velocity will wrap
into −128 (Right) or −127 (Left+Right).
If you do not press any key, the velocity will start decreasing towards zero.
To prevent the velocity from decreasing, jump. During the jump, the velocity is preserved. If you cannot jump, let the velocity decrease to +126, then press Right once to restore it into +127, and keep alternating it between +126 and +127.
The fastest way to move to the left
If you hold Left, the maximum velocity you can get is −24. To achieve further speed, you must use the wrapping trick. Increase your velocity towards the right until it reaches +126 or +127, then let it wrap by letting it increase by 1 (Right) or 2 (Left+Right). At that point, the hero will suddenly zoom to the left.
To prevent the velocity from falling (technically, increasing) from −128 to −127, use the same technique as with when going to the right: jump, or alternate.
Note that the velocity works the same way even when the hero is facing an obstacle,
so you can achieve the zipping speed to the left even by walking towards a wall on
the right side.
If you are standing on the ground, fight, and the velocity will drop to 0 immediately. You can then jump immediately if you want to continue moving slowly.
If you are jumping, you can only slow down by changing the velocity into either direction with Left / Right / Left+right.
Bypassing the red bush in the Peloponnesos forest without burning it
Super speed
Gain super speed and jump over the bush.
Damage
Jump towards the bush, and turn to the right, before the collision.
When the hero gets damage, he will be pushed to the left if you did it right.
Bypassing the barrier in Crete
Damage
Normally, the barrier in Crete will instantly kill Orpheus unless he
possesses the second fragment of love. But, when Orpheus is damaged by
one of the flying one-eyed monsters in the room, he briefly becomes
invincible. The invincibility time is long enough for Orpheus to slip
through the barrier. This trick allows Orpheus to obtain the third
fragment of love out of sequence, but, unfortunately, the path to
Tartarus will not open unless he leaves Crete with all three fragments
of love.
Memory addresses
Address | Name |
---|
0053 | Velocity |
0063 | X-axis position |
0052 | X-axis subpixel |
0067 | Y-axis position |
006D | Olives counter |
006C | Bay leaves counter |
Item drop calculations for bay leaves and olives are based off the games global RNG. When a player enters a new screen, there are 2 counters, 006C is the counter for bay leaves, 006D is the counter for olives, these get reset to '75' upon entering a new screen. Before a drop is calculated, the game takes the sum of these two counters, subtracts 50 and if the result is greater than 50, this table is used:
Offset | Item | % |
---|
0 | - | 25% |
1 | Bay leaf | 25% |
2 | - | 25% |
3 | Olive | 25% |
If the result of subtracting 50 is less than or equal to 50, this table is used:
Offset | Item | % |
---|
0 | - | 12.5% |
1 | Bay leaf | 12.5% |
2 | - | 12.5% |
3 | Olive | 12.5% |
4 | - | 12.5% |
5 | - | 12.5% |
6 | - | 12.5% |
7 | - | 12.5% |
After the game determines the drop table to use, it rolls the
RNG
to select the specific drop.
The RNG value is then AND'd against 3 (if the first table is used), or 7 (if the 2nd table was used).
The result determines the drop (e.g., if the result was 3, an olive drops; if 1, a bay leaf; etc.).
However, there is one more check to perform before an item finally drops.
The game once again checks the respective counters (olive/bay leaf) to see if the counter has reached zero.
If it has, no item drops.
This means that there is a limit of 75 bay leaves and 75 olives per screen.
And both of them dropping can affect the % of drops that can occur within the screen.
RNG
Address | Instruction |
---|
$D7DD:18 | CLC
|
$D7DE:A5 DA | LDA $00DA
|
$D7E0:29 48 | AND #$48
|
$D7E2:F0 06 | BEQ $D7EA
|
$D7E4:C9 48 | CMP #$48
|
$D7E6:18 | CLC
|
$D7E7:F0 01 | BEQ $D7EA
|
$D7E9:38 | SEC
|
$D7EA:26 DC | ROL $00DC
|
$D7EC:26 DD | ROL $00DD
|
$D7EE:26 DA | ROL $00DA
|
$D7F0:26 DB | ROL $00DB
|
$D7F2:A5 DA | LDA $00DA
|
The memory locations $00DA, $00DB, $00DC, and $00DD hold the state of the shift register.
When the RNG function is called,
two bits from $00DA (corresponding to the mask 0x48) are tested.
If the bits are equal, a 0 bit is shifted into the state;
if they are unequal, a 1 bit is shifted in.
This is done by either clearing (CLC
) or setting (SEC
)
the carry flag before starting the shift.
The new bit is shifted into $00DC, which then shifts into $00DD,
then $00DA, and finally $00DB.
(Note the non-consecutive order of memory addresses.)
Finally, the function returns the value of $00DA.
(Not $00DB, as you might expect.)
$00DB $00DA $00DD $00DC
xxxxxxxx <- xxxxxxxx <- xxxxxxxx <- xxxxxxxx <-.
| | |
'--+------------------------------'
In C, the RNG function would look like this
(with one 32-bit state
variable taking the place of
four 8-bit state variables in the original):
static uint32_t state = 0x00aa00aa; // Initialized at $C02E in the ROM.
uint8_t rng(void)
{
// If both tap bits are 0 or both are 1, shift in a 0 bit.
// Otherwise, shift in a 1 bit.
uint32_t carry;
if ((state & 0x00480000) == 0x00000000 || (state & 0x00480000) == 0x00480000)
carry = 0;
else
carry = 1;
state = (state << 1) | carry;
// Return the second-to-last byte in the state, not the last byte.
return (uint8_t) (state >> 16);
}
An odd feature is that the return value is the second-to-last byte
in the register ($00DA), not the last byte ($00DB).
Even though there are 32 bits of state variables,
only 24 bits are really used.
The only effect $00DB could possibly have
is affecting the CPU's carry flag;
otherwise the function would return the same sequence of values
if that part of the state were removed.
Despite having 32 (or 24) bits of state,
the choice of taps leads to a period of only 32,767.