The Structure of Podd's Word List
The list of words that Podd can recognise are listed from $1900 onwards. Each word is a $0D-terminated string, with the next two bytes indicating the memory location of the animation subroutine to be executed when the word is guessed correctly.
For example, at location $1900:
we have the word "glide", a carriage return, and the information that the gliding subroutine is at $3713. (multiple words can share the same animation). Words are stored in lower case, as user input is converted to lower case when it is validated.
When the user guesses a word, the subroutine at $1E7A (S_CHECK_WORD) starts comparing the guess with $1900, incrementing one byte at a time. If it gets as far as a $0D without a mis-match, it returns the address of the animation subroutine. The check for $0D is only carried out after the fourth characters match, so the minimum word length is four characters. (The maximum word length permitted is 10 characters.) If a mis-match happens before the end of a word, the subroutine skips ahead to the end of the word, skips past the two bytes of the animation address, and tries again. If the first character of the next word is $23 ( a '#'), this indicates that the end of he list has been reached, i.e. Podd doesn't know that word.
Looking at the memory above $1900, the '#' marker is found at $1CB9. The nine strings that follow are duplicates of "expand" to "pop", and aren't searched by S_CHECK_WORD. They're probably left over in memory from development. From $1D01 onward, we seem to have a partial disassembly of the game code itself in memory! In particular, the contents from $1D42:
Code: Select all
& CMP noch:BCC cont009
0 PLA:JMP loop100
N LDY chcnt%:STA(v7d1%),Y
looks very similar to my disassembly of $1E37 onwards:
Code: Select all
(ADDR_L is at location $0070. The first instruction at S_INPUT_WORD has value $0A, so represents the number of characters permitted ('noch').
That's enough information for me to change the words associated with Podd's actions. But as well as the list of words Podd recognises, I will want to translate the user interface.
The User Interface
The main menu program loop is at $2CE2, and prompts the user to choose one of two games: guess a word/action ($25BA), or chain 2-5 actions together ($2E4C). Each has their own subroutine for inputting words, using the S_READKB subroutine at $1850.
Podd has two sets of subroutines for printing strings of text. The simpler ones is at $1FC4 (S_PRINT_STR), which simply prints a string through OSWRCH until it finds a $0D character. It's used for executing VDU commands.
The text displayed onscreen is double-height, and a pair of subroutines at $23C4 and $23D4 (S_2L_PRINT_STR and S_2L_PRINT_CH) are responsible for this. S_2L_PRINT_CH takes a character from the A register, looks up the character's definition with OSWORD 0A, then expands the character vertically by repeating each line of pixels. The resulting pair of characters are printed on the current line and the one below. S_2L_PRINT_STR simply feeds a $0D-terminated string to S_2L_PRINT_CH instead of OSWRCH.
The original version gives this prompt (in game 1):
followed by this response for an incorrect guess:
(Yes, I pulled suitable random screenshots from the internet, rather than make my own. So shoot me...)
Here we hit upon a problem, as the Welsh for "Podd can" requires more space on the screen: " Mae Pod yn gallu" needs an extra eight characters. And we need up to ten characters for the guessed word. In the original that required 19 characters, but the Welsh equivalent won't fit on a single line.
My father distinctly recalls the Welsh version repeating the word back at you if it don't recognise it, which isn't part of the original's functionality at all. I think that this was part of the way the translators got around the above problem. As there is space for two lines of text, if they arranged the "Podd can" prompt as :
Then instead of adding a second line of text when the word is wrong, they could overwrite the first, to give:
that is, "Podd can't ___" or "Podd isn't able to ___".
Tweaking the VDU codes that set the text cursor's location before each string is printed could be done in-situ, but the strings to be printed won't be the same length. That means they probably won't fit into the space reserved for them in between subroutines. There is, however, free space at the end of the list of words from $1900: as far as I can make out everything up until $1DB0 is reserved for the word list. As we've seen, the end of that consists of garbage memory from the unassembled/disassembled code. Plus, my Welsh word list will be shorter than the English one, as the language has fewer synonyms. (English has taken loanwords from so many different sources, e.g. "big" and "large" would both equate to "mawr".) That ought to give me plenty of memory space in which to place my modifications.
So that's most of the theory and research done. I'll need to do a detailed decoding of the VDU commands I want to change, of course. But I've gotten to the point where I can start modifying the codebase with the expectation that the program will still run.