Scripts

The Sword of Hrakel uses an interpreter, as did many of the text adventures of the time. A section of the game file contains the encoded scripts, starting at offest $1065:

001065 0D 2E 2A 01 08 03 09 06 04 00 01 04 00 0D 2E 2A ..*............*

001075 01 12 03 08 06 09 00 01 09 00 0D 29 2A 01 05 03 ...........)*...

001085 07 06 0E 00 01 0E 00 0D 29 3D 01 1D 03 0F 06 0B ........)=......

001095 00 01 0B 00 0D 2C 2A 01 08 04 04 06 05 00 01 05 .....,*.........

0010A5 00 0D 2C 2A 01 12 04 09 06 0A 00 01 0A 00 0D 2C ..,*...........,

0010B5 2A 01 1D 04 0B 06 0C 00 01 0C 00 0B 2C 2A 01 08 *...........,*..

0010C5 04 05 00 04 10 00 0B 02 2A 01 08 04 05 00 07 06 ........*.......

0010D5 00 0B 02 2A 01 12 04 0A 00 07 13 00 0B 02 2A 01 ...*..........*.

0010E5 1D 04 0C 00 07 1E 00 0B 02 2A 01 05 04 0E 00 07 .........*......

0010F5 04 00 07 02 2A 00 04 15 00 0B 0B 1E 01 03 00 01 ....*...........

001105 0D 04 02 00 0D 0B 2F 01 08 07 06 00 06 06 04 07 ....../.........

001115 00 0D 0B 25 01 0D 07 08 00 06 08 04 07 00 0D 0B ...%............

001125 44 05 1F 07 01 00 06 01 04 07 00 0F 0B 24 04 06 D............$..

001135 06 11 00 06 05 04 07 01 11 00 0D 0B 39 01 15 07 ............9...

001145 0F 00 06 0F 04 07 00 09 0B 0C 03 01 00 04 08 00 ................

001155 09 0B 14 03 0A 00 04 08 00 09 0B 32 01 0F 00 04 ...........2....

001165 02 00 09 2C 24 06 06 00 01 06 00 11 26 17 01 1B ...,$.......&...

001175 03 0B 06 10 00 01 10 0B 0B 04 0D 00 09 09 12 05 ................

001185 19 00 04 19 00 11 2D 10 03 05 05 19 00 0B 19 04 ......-.........

001195 06 08 05 06 0E 00 0B 2C 2A 01 12 04 0A 00 04 10 .......,*.......

0011A5 00 09 02 31 01 0E 00 07 0D 00 09 02 31 01 1D 00 ...1........1...

0011B5 07 1F 00 0B 02 3E 01 1D 04 0C 00 07 1E 00 11 30 .....>.........0

0011C5 14 03 0A 05 1B 00 08 1B 08 0A 06 09 04 0A 00 09 ................

0011D5 28 32 01 0F 00 07 0C 00 0D 26 21 05 1B 03 06 00 (2.......&!.....

0011E5 08 1B 04 0B 00 13 26 23 01 13 03 06 06 0F 00 01 ......&#........

0011F5 0F 08 1D 08 1E 04 0B 00 0D 26 1B 05 11 03 06 00 .........&......

001205 0B 11 04 0C 00 0D 26 1F 05 17 03 06 00 0B 17 04 ......&.........

001215 0E 00 0C 26 22 05 1C 03 02 00 04 1C 05 00 0B 2C ...&"..........,

001225 2B 01 06 04 11 00 01 03 00 0D 02 2B 01 06 04 03 +..........+....

001235 00 07 05 01 01 00 09 02 33 01 0F 00 07 1A 00 0D ........3.......

001245 02 1E 01 13 04 07 04 08 00 07 14 00 0B 28 07 01 .............(..

001255 03 04 0D 00 07 04 00 0B 27 0F 01 04 05 04 00 07 ........'.......

001265 01 00 0B 2C 2A 01 1D 04 0C 00 04 10 00 0B 15 1A ...,*...........

001275 01 23 03 10 00 07 21 00 0F 37 23 01 13 03 12 00 .#....!..7#.....

001285 08 12 01 08 04 11 00 11 36 35 01 13 06 07 03 06 ........65......

001295 00 01 07 0B 1D 04 12 00 0B 38 1B 03 12 00 08 12 .........8......

0012A5 04 13 00 0B 15 1A 01 21 03 10 00 07 22 00 0B 0B .......!...."...

0012B5 0E 01 22 03 03 00 07 23 00 0D 42 43 03 15 03 21 .."....#..BC...!

0012C5 00 0B 15 01 02 00 09 42 43 03 16 00 04 1A 00 0B .......BC.......

0012D5 0A 43 03 16 00 02 02 06 16 00 0C 09 43 05 16 00 .C..........C...

0012E5 01 02 03 16 FF 00 0B 02 06 01 1B 06 10 00 04 01 ................

0012F5 00 09 02 04 05 17 00 04 18 00 0D 02 08 01 04 03 ................

001305 01 00 07 03 04 0F 00 0A 02 08 01 04 00 04 03 05 ................

001315 00 0B 02 07 01 05 00 02 01 07 06 00 08 41 FF 00 .............A..

001325 04 05 05 00 10 09 1D 01 06 05 14 00 03 14 FF 04 ................

001335 04 01 11 00 0B 0A 0F 01 04 00 04 1B 06 04 00 09 ................

001345 42 16 03 21 00 04 1E 00 07 2C 2A 00 04 16 00 07 B..!.....,*.....

001355 2D 10 00 04 20 00 07 36 32 00 04 20 00 11 3B 16 -... ..62.. ..;.

001365 01 1B 03 0B 06 10 00 01 10 0B 0B 04 0D 00 09 09 ................

001375 1F 01 1A 00 04 21 00 09 09 20 01 07 00 04 21 00 .....!... ....!.

There are two ways to decode such script data: One way is to disassemble the 6502 machine code of the game and work out what the interpreter is doing. The other way is to attempt to recognise patterns within the script data that might help us unravel the structure. I'm going to attempt to do it the second way.

So what do we know so far?

We know how many commands there are, how many locations, how many items and how many messages. This will help us because if we see a byte value that is higher than the number of locations (for example), then it probably doesn't represent a location number. The same applies for commands, items and messages.

The other thing we've seen over and over again for locations, items, and messages, is a repeating structure of a length byte at the start and a $00 at the end. Might we see the same in the script data?

It turns out that yes we do, and that's our first big break. On reflection it seems a little strange that most strings in the game are both length-prefixed and zero-terminated. Normally a string of this vintage would be one or the other. We're certainly not complaining though since it makes our task easier. Let's arrange the bytes into a row for each script, with the length byte at the start and the $00 byte at the end:

0D 2E 2A 01 08 03 09 06 04 00 01 04 00

0D 2E 2A 01 12 03 08 06 09 00 01 09 00

0D 29 2A 01 05 03 07 06 0E 00 01 0E 00

0D 29 3D 01 1D 03 0F 06 0B 00 01 0B 00

0D 2C 2A 01 08 04 04 06 05 00 01 05 00

0D 2C 2A 01 12 04 09 06 0A 00 01 0A 00

0D 2C 2A 01 1D 04 0B 06 0C 00 01 0C 00

0B 2C 2A 01 08 04 05 00 04 10 00

0B 02 2A 01 08 04 05 00 07 06 00

0B 02 2A 01 12 04 0A 00 07 13 00

0B 02 2A 01 1D 04 0C 00 07 1E 00

0B 02 2A 01 05 04 0E 00 07 04 00

07 02 2A 00 04 15 00

0B 0B 1E 01 03 00 01 0D 04 02 00

0D 0B 2F 01 08 07 06 00 06 06 04 07 00

0D 0B 25 01 0D 07 08 00 06 08 04 07 00

0D 0B 44 05 1F 07 01 00 06 01 04 07 00

0F 0B 24 04 06 06 11 00 06 05 04 07 01 11 00

0D 0B 39 01 15 07 0F 00 06 0F 04 07 00

09 0B 0C 03 01 00 04 08 00

09 0B 14 03 0A 00 04 08 00

09 0B 32 01 0F 00 04 02 00

09 2C 24 06 06 00 01 06 00

11 26 17 01 1B 03 0B 06 10 00 01 10 0B 0B 04 0D 00

09 09 12 05 19 00 04 19 00

11 2D 10 03 05 05 19 00 0B 19 04 06 08 05 06 0E 00

0B 2C 2A 01 12 04 0A 00 04 10 00

09 02 31 01 0E 00 07 0D 00

09 02 31 01 1D 00 07 1F 00

0B 02 3E 01 1D 04 0C 00 07 1E 00

11 30 14 03 0A 05 1B 00 08 1B 08 0A 06 09 04 0A 00

09 28 32 01 0F 00 07 0C 00

0D 26 21 05 1B 03 06 00 08 1B 04 0B 00

13 26 23 01 13 03 06 06 0F 00 01 0F 08 1D 08 1E 04 0B 00

0D 26 1B 05 11 03 06 00 0B 11 04 0C 00

0D 26 1F 05 17 03 06 00 0B 17 04 0E 00

0C 26 22 05 1C 03 02 00 04 1C 05 00

0B 2C 2B 01 06 04 11 00 01 03 00

0D 02 2B 01 06 04 03 00 07 05 01 01 00

09 02 33 01 0F 00 07 1A 00

0D 02 1E 01 13 04 07 04 08 00 07 14 00

0B 28 07 01 03 04 0D 00 07 04 00

0B 27 0F 01 04 05 04 00 07 01 00

0B 2C 2A 01 1D 04 0C 00 04 10 00

0B 15 1A 01 23 03 10 00 07 21 00

0F 37 23 01 13 03 12 00 08 12 01 08 04 11 00

11 36 35 01 13 06 07 03 06 00 01 07 0B 1D 04 12 00

0B 38 1B 03 12 00 08 12 04 13 00

0B 15 1A 01 21 03 10 00 07 22 00

0B 0B 0E 01 22 03 03 00 07 23 00

0D 42 43 03 15 03 21 00 0B 15 01 02 00

09 42 43 03 16 00 04 1A 00

0B 0A 43 03 16 00 02 02 06 16 00

0C 09 43 05 16 00 01 02 03 16 FF 00

0B 02 06 01 1B 06 10 00 04 01 00

09 02 04 05 17 00 04 18 00

0D 02 08 01 04 03 01 00 07 03 04 0F 00

0A 02 08 01 04 00 04 03 05 00

0B 02 07 01 05 00 02 01 07 06 00

08 41 FF 00 04 05 05 00

10 09 1D 01 06 05 14 00 03 14 FF 04 04 01 11 00

0B 0A 0F 01 04 00 04 1B 06 04 00

09 42 16 03 21 00 04 1E 00

07 2C 2A 00 04 16 00

07 2D 10 00 04 20 00

07 36 32 00 04 20 00

11 3B 16 01 1B 03 0B 06 10 00 01 10 0B 0B 04 0D 00

09 09 1F 01 1A 00 04 21 00

09 09 20 01 07 00 04 21 00

What is immediately obvious now is the $00 byte in the middle of every script row above. It appears to separate two parts of the script. The other observation is that $44 is the highest number that appears in columns two and three, which happens to be exactly equal to the highest number in our commands list. We don't see numbers that high in any of the other columns (apart from the value $FF, which must surely have some as yet unknown to us special meaning). It is quite likely then that the first two columns after the length byte are the two-word commands, and it also makes sense from a design point of view that the typed in commands would be at the start of the script.

A script in a game like this will usually be of the form:

if (said some words) and [optionally] (some test conditions are true) {

then do some actions

}

This is the most likely explanation for the $00 byte in the middle of the script. It would be separating the test conditions from the actions to perform, should all the test conditions be true.

Since we suspect that columns two and three are the two words that the player has typed in, those words can reveal a great deal about what the test conditions and actions might be if we were to now go through every line and decode these words. For example, if the player types "look tree" and they are in the forest location where the oak is, then the game tells us "I can see some handholds now". So if the two words for one of the scripts above happens to decode to "look tree" then we know that the test condition must be checking that the location number is $0F and that the action must be printing message number $02.

And sure enough, here it is:

09 Length of the script

0B Command number (verb): $0B = "look"

32 Command number (noun): $32 = "tree"

01 This must be the test condition code to check the location number

0F $0F = forest location with the oak

00 Separates test conditions from actions

04 This must be the action code to print a message by number.

02 $02 = "I can see some handholds now."

00 End of the script

Let's now list the scripts again but this time remove the length prefix, the mid zero byte and the end zero byte, and then decode the words in each case. Seeing it in this form is going to help us greatly:

"unlock door" 01 08 03 09 06 04 01 04

"unlock door" 01 12 03 08 06 09 01 09

"smash door" 01 05 03 07 06 0E 01 0E

"smash lock" 01 1D 03 0F 06 0B 01 0B

"open door" 01 08 04 04 06 05 01 05

"open door" 01 12 04 09 06 0A 01 0A

"open door" 01 1D 04 0B 06 0C 01 0C

"open door" 01 08 04 05 04 10

"go door" 01 08 04 05 07 06

"go door" 01 12 04 0A 07 13

"go door" 01 1D 04 0C 07 1E

"go door" 01 05 04 0E 07 04

"go door" 04 15

"look hole" 01 03 01 0D 04 02

"look forest" 01 08 07 06 06 06 04 07

"look stove" 01 0D 07 08 06 08 04 07

"look nest" 05 1F 07 01 06 01 04 07

"look chest" 04 06 06 11 06 05 04 07 01 11

"look furnace" 01 15 07 0F 06 0F 04 07

"look ring" 03 01 04 08

"look scroll" 03 0A 04 08

"look tree" 01 0F 04 02

"open chest" 06 06 01 06

"kill plants" 01 1B 03 0B 06 10 01 10 0B 0B 04 0D

"get crowbar" 05 19 04 19

"rub gem" 03 05 05 19 0B 19 04 06 08 05 06 0E

"open door" 01 12 04 0A 04 10

"go hut" 01 0E 07 0D

"go hut" 01 1D 07 1F

"go shed" 01 1D 04 0C 07 1E

"give scroll" 03 0A 05 1B 08 1B 08 0A 06 09 04 0A

"climb tree" 01 0F 07 0C

"kill guru" 05 1B 03 06 08 1B 04 0B

"kill man" 01 13 03 06 06 0F 01 0F 08 1D 08 1E 04 0B

"kill duck" 05 11 03 06 0B 11 04 0C

"kill wolf" 05 17 03 06 0B 17 04 0E

"kill mage" 05 1C 03 02 04 1C 05

"open trapdoor" 01 06 04 11 01 03

"go trapdoor" 01 06 04 03 07 05 01 01

"go path" 01 0F 07 1A

"go hole" 01 13 04 07 04 08 07 14

"climb up" 01 03 04 0D 07 04

"cross shelf" 01 04 05 04 07 01

"open door" 01 1D 04 0C 04 10

"sail raft" 01 23 03 10 07 21

"feed man" 01 13 03 12 08 12 01 08 04 11

"cut chains" 01 13 06 07 03 06 01 07 0B 1D 04 12

"eat duck" 03 12 08 12 04 13

"sail raft" 01 21 03 10 07 22

"look compass" 01 22 03 03 07 23

"light lamp" 03 15 03 21 0B 15 01 02

"light lamp" 03 16 04 1A

"drop lamp" 03 16 02 02 06 16

"get lamp" 05 16 01 02 03 16 FF

"go west" 01 1B 06 10 04 01

"go south" 05 17 04 18

"go down" 01 04 03 01 07 03 04 0F

"go down" 01 04 04 03 05

"go up" 01 05 02 01 07 06

(swear) 04 05 05

"get rug" 01 06 05 14 03 14 FF 04 04 01 11

"put shelf" 01 04 04 1B 06 04

"light match" 03 21 04 1E

"open door" 04 16

"rub gem" 04 20

"cut tree" 04 20

"pour matches" 01 1B 03 0B 06 10 01 10 0B 0B 04 0D

"get wolf" 01 1A 04 21

"get gargoyle" 01 07 04 21

The words on the left are the player's input, the numbers in the middle the test conditions, and the numbers on the right the actions to take if all of the test conditions are true. We can see from the above that some scripts are simple and some are more complex. Looking at the oak tree was a simple script since it tested one condition (i.e. is the player in the forest location with the oak tree) and performed one action (i.e. print "I can see some handholds now."). Some of the scripts shown above are tested up to three different things, and some are performing up to four different actions. The most complex script appears to be the "kill man" script, with three test conditions and four actions.

Even with just the words decoded, it is almost taking on the form of a walk through. Just imagine... if we simply rearrange these commands into the right order, with a few direction commands added in, and a few death cases taken out, we've basically got the walk through that we're looking for. Unfortunately we don't yet know what order these commands should be entered in and we don't yet know which commands lead to a dead end or death.

What we do know from decoding the "look tree" script is that the $01 test code tests that the player is in a given location. Many of the scripts shown above make use of the $01 test code, but there are other test codes that also appear. It is probably safe to assume that each test code has a single argument. This seems to be the case since the test condition part of every script is a multiple of two bytes in length, ranging from 0 to 6 bytes, which would mean 0 to 3 test conditions. With this in mind, glancing an eye backwards and forwards over the test conditions of the scripts would seem to indicate we have test codes $01, $03, $04, $05, $06 and $07.

We don't appear to have any $02 test codes, which is something that highlights the difference between decoding these scripts by reversing the logic from the data versus disassembling the 6502 interpreter code. It would appear that the interpreter supports a $02 test code but that the Sword of Hrakel game does not make use of it. The most likely reason for this is that the interpreter was written for no particularly game in mind but rather to support multiple games. Indeed Sword of Hrakel is the second in Romik's series of text adventures, so it is possible that the first game, The Golden Apples of Zeus, used a version of same interpreter and therefore may have used the $02 test code.

We notice a similar situation with the action codes. I can see $01, $02, $03, $04, $05, $06, $07, $08 and $0B. There is no $09 or $0A.

How do we work out what this test and action codes mean? Perhaps the best way from here is to decode some examples that we're already familiar with from playing the game. Unlocking and then opening the town hall doors is one example. The town hall doors are in the village square. This is location number $12. We aleady know that test code $01 tests the location number. So the "unlock door" script that tests that the location number is $12 is the one we're looking for:

"unlock door" 01 12 03 08 06 09 01 09

It no only tests the player's location but it also tests two other conditions. What could these be? Well think about what the player needs in order to unlock the door. Yes, the bronze key. This is item number $08. One of the test codes in the script does have $08 as an argument and therefore it is almost certain that test code $03 tests that the player is carrying an item. That leaves us with trying to work out the meaning of test code $06. What else needs to be true in order to allow the player to unlock the door? Well the door obviously needs to be locked. In this script, the $06 $09 test condition must be checking that the door is locked. This most like means that $06 is used for testing the state of a boolean flag, where the argument is the flag number. The logic here is probably a double negative in that it is probably interpreted as "is_not_set(flag 9)?", which, given the purpose of flag 9 would translate to "the door is not yet unlocked" rather than "the door is locked". So that leaves us with the action code. There is only one in this case and it is action code $01. The argument is $09, which matches the argument used by test code $06. This obviously isn't a coincidence. When the door is unlocked, the game needs to store that state so that it can remember that the player has already done this. So it needs to set the "town hall doors unlocked flag" to true. I think we can safely assume then that action code $01 sets a game state flag to true.

"open door" 01 12 04 09 06 0A 01 0A

After unlocking the door, it needs to be opened. The script above is an "open door" script that starts by testing that the player is in location $12, which is the village square again. In order to open the door, it needs to be unlocked but not open. So we will be expecting it to be checking flag $09, which is the "town hall doors are unlocked" flag. We do in fact see $09 as an argument to test code $04, which leads us to determine that test code $04 is for testing that a flag is set, i.e. "is_set(flag 9)?" in this case. The third test code is $06, which we already know to be testing that a flag is not yet set to true. Logically flag $0A must be used for determining whether the town hall doors have been opened yet. We now see the same $01 action code we saw in the previous example, which we already know to be setting a game state flag to true. This time it is setting flag $0A, which we've already determined to be the "town hall doors are open" flag.

I don't intend to explain in this level of detail how I decoded each and every script, but it was simply by using the same type of deduction and process used in the three examples we've already worked through above. By doing this for each script, I was able to determine the following meanings for each of the test codes and action codes:

Test codes

$01 = Is player in location <locationNumber> ?

$03 = Is player carrying item <itemNumber> ?

$04 = Is flag <flagNumber> set?

$05 = Is item <itemNumber> in current location ?

$06 = Is flag <flagNumber> not set?

$07 = Is item <itemNumber> not in current location?

Action codes

$01 = Set flag <flagNumber>

$02 = Clear flag <flagNumber>

$03 = Add item <itemNumber> to player's inventory

$04 = Print message <messageNumber>

$05 = Die

$06 = Drop item <itemNumber>

$07 = Go to location <locationNumber>

$08 = Destroy item <itemNumber>

$0B = Toggle item <itemNumber>

The last action code $0B is probably not obvious. What it does is change the state of an item into its other state. There are some items, such as the lamp and the bottle of weed-o-kill, that have two states. The $0B action toggles between these states.

Now that we know these codes, we can list out the fully decoded scripts:

If said "unlock door" and the player is in the forest location with the small door and the player is carrying the silver key and the small door is locked then unlock the small door.

If said "unlock door" and the player is in the village square and the player is carrying the bronze key and the town hall doors are locked then unlock the town hall doors.

If said "smash door" and the player is on the long staircase and the player is carrying the dull iron bar and the door is not smashed then smash the door.

If said "smash lock" and the player is at the crossroads and the player is carrying the small sturdy hammer and the lock is not smashed then smash the lock.

If said "open door" and the player is in the forest location with the small door and the door is unlocked and the door is not open then open the door.

If said "open door" and the player is in the village square and the town hall doors are unlocked and the town hall doors are not open then open the town hall doors.

If said "open door" and the player is at the crossroads and the lock is smashed and the door is not open then open the door.

If said "open door" and the player is in the forest location with the small door and the small door is open then say "The door is open".

etc.

etc.