The following bugs were found while working on this.
# Armor/Weapon Cap Logic Swapped
When equipping the Battle Suit, the code checks to make sure the weapons slots aren't overflowed. Roughly:
```c
void Player_PickUpBattleSuit() {
...
i = NumberOfWeapons;
if (NumberOfWeapons >= 4) {
i = 3;
}
ArmorInventory[i] = ARMOR_BATTLE_SUIT;
NumberOfArmors = i + 1;
}
```
And similarly, when picking up the Dragon Slayer:
```c
void Player_PickUpDragonSlayer() {
...
i = NumberOfArmors;
if (NumberOfArmors >= 4) {
i = 3;
}
WeaponInventory[i] = WEAPON_DRAGON_SLAYER;
NumberOfWeapons = i + 1;
}
```
This has the effect of setting the wrong index for the Dragon Slayer or Battle Suit, based on which armor/weapons you have when you equip either.
This doesn't seem to truly impact the game, as there's code that checks if you have all three and then sets your inventory. But it's possible that this result in an empty item slot or prevent acquiring other armors/weapons, overwriting them when the Dragon Slayer/Battle Suit are picked up.
# Bad "Can Climb" Ladder Check
When checking if a ladder can be climbed from side to side, the code does this:
```asm
LDA #0x3
JSR Player_CheckIfPassable
```
That _should_ be `LDX`. This function checks the direction to move in, stored in `X`. But this caller is passing in `A`, meaning the function will operate on whatever value was last in `X` instead.
Probably not exploitable, but worth checking further.
# Wrong Text Sound in Textboxes
Faxanadu players are very familiar with the almost typewriter-like sound when writing text in a textbox. But this turns out to be a bug due to an off-by-one and reading part of an `LDA` instruction.
[SoundEffect_Message_OnTick](https://chipx86.com/faxanadu/PRG5.html#SoundEffect_Message_OnTick), uses a [SOUNDEFFECT_MESSAGE_NOISE_LO](https://chipx86.com/faxanadu/PRG5.html#SOUNDEFFECT_MESSAGE_NOISE_LO) table to define the two sounds used when typing. It looks like:
```asm
.byte $10
.byte $01
```
That second byte isn't used, though. Instead, the following instruction is referenced:
```
a9 14 LDA #$14
```
Specifically, the `a9`. This produces a louder, sharper sound, instead of the soft `01` sound in the table.
The reason this happens is that this code stores an offset into the table, which was likely originally 0 or 1, but the value used for the offset also controls timing.
An alternating 0/1 value would result in playing a sound every tick, but they appear to have changed this to play every other tick instead. This meant this instruction:
```asm
AND #$01
```
Changed to:
```asm
AND #$02
```
This improved the timing, but meant we now overflowed the table, pulling in the `a9` from the `LDA`, producing the sound we hear in the final game.
Here's an example of how it would have sounded before:
https://chipx86.com/faxanadu/videos/typing-bug-fixed.mp4
# Bad Memory Reads During Guru Password Display
When talking to a Guru, they'll usually give you a password ("Mantra") to help you continue your game next time. It will generate a password and display it character-by-character, wrapping as needed.
However, while it does this, it's also reading incorrect data from the wrong bank when determining what to do with each character. It works in the shipped game, but can easily cause problems in a mod. The main issue is that the password won't wrap to the next line, but instead will wrap back on the first line, overwriting part of the password.
Here's what's going on. First, a primer on textboxes:
1. When showing a message in a textbox, the code first loads the ID, starting address (as individual high and low bytes), message length, and processed number of characters into memory.
2. As that message is displayed, the textbox code pays attention to the character being written to determine what to do with it. Special characters may indicate a newline, an "insert player rank here," a "pause message," an "end of message," or others.
3. Whenever the character position is in the final column, it specially checks the _next_ character (using the message address and processed character length) to check if it should prepare to insert a newline or just end/pause.
This happens when you first talk to the Guru. They'll say "You need peace of mind. I'll meditate with you.", which triggers that behavior. When finished, the loaded message state ends up on the _next_ message in bank 13.
Then, the [[Interaction Scripts|IScript]] handling the Guru triggers an action that loads and writes the password. This bypasses much of the textbox code, directly triggering the writing of each character. It needs to do this because the password isn't stored as a message string.
As it writes, the textbox code will still perform the next-character check from point #3 above. But that next character doesn't come from the password. It comes from the previously-loaded (and now processed) message. At this point, the address it reads from is set to the address immediately following the Guru's message.
However, it's no longer reading from the correct bank. Messages come from bank 13, but this IScript is running from bank 12, and it never switches to bank 13 during this process.
This means that it's reading from the message address but in the wrong bank. And if that character happens to be a special control character, like 0xFF (End Of Message), it'll try to terminate writing, and won't generate a newline. The IScript code doesn't know this, so it just tries writing again, and the position value ends up wrapping back to 0 without moving to the next line, corrupting the displayed password.
In the shipped game, this address just so happens to fall somewhere in the list of characters that can be shown in the password, but if the address were later in the bank, it'd easily fall in a run of 0xFF characters.
This could have been resolved in the code by setting the loaded message state to a safe address for bank 12 prior to the textbox write loop.
# First Level Loading Bug
Whenever a level is loaded, there's a lookup to grab the bank where the level data lives and to then grab screen data. Both should be keyed off the index of the region of the game.
However, when loading the very first screen in the game, a mistake resulted in using the resulting _bank_ as the index into the screens, rather than the _area index_. This means if you try to move the first level into another bank, you get an out-of-bounds lookup and corruption.
# Sprite Loading
* `TILES_PLAYER_ARMOR_STUDDED_MAIL_START` (PRG8:8056) does not include a tile used when climbing (`SPRITE_TILES_PLAYER_ARMOR_LEATHER_46_WSHIELD_34__STUDDED_052` — PRG8:8194). This is provided via the Leather Armor tile loading, but should be in this list.