Reversing Robico’s Island of Xaan

Overview

This is a basic description of a reverse engineer of Robico’s first text adventure: Island of Xaan.

Although it is one of their weakest, it shows a number of trademark features of Robico’s adventures: lots of detail descriptions, lots of humour and some strange logic. The parser, being just verb and noun, is quite weak compared to Robico’s later adventures.

The adventure was only available on the BBC Micro and Acorn Electron computers. The version I’m reverse engineering is the BBC Micro one, taken from the Stairway to Hell archive and is designed to run in &1900 DFS, using MODE 7.

This write up uses BBC Micro conventions (e.g. using & to signify a hexadecimal number) and assumes that some knowledge is known about the BBC micro.

All notes and scrap code is available from my github.

The disk

The disk contains six files, three of which are loaders:

I’ll get these out of the way, as they’re the simplest and have nothing really interesting from a reverse engineering perspective:

  • $.!BOOT – is loaded when the disc is run with shift-break and just runs $.LOAD
  • $.LOAD – is the general Stairway to Hell loader and just runs $.LOADER
  • $.LOADER – prints the introduction, then loads and runs $.XAAN1. It also shows the address for Robico software, which is an unimposing semi-detached house in Wales.

The other files are code for the adventure which are loaded in the following order, and locations:

  • $.XAAN1 which is loaded from &880 to &D00 with an exec address of &888
  • $.XAAN which is loaded from &1200 to &7BFC with an exec address of &225D
  • $.XAAN2 which is loaded from &2000 to &2400 with an exec address of &2000

XAAN1

XAAN1 contains the intial loader, a selection of library routines, such as the message printer, and several command routines, for short commands, probably placed here because memory was short.
Once loaded it performs the basic system calls, before handing over control to the XAAN file:

  1. OSBYTE 225        (disable function keys)
  2. OSBYTE 200 3      (disable escape and clear memory on break)
  3. OSBYTE 139 1      (*OPT 1 equivalent – report nothing during file ops)
  4. OSCLI “R. XAAN”   (Load XAAN and jump to its executable address)

Other obvious functions include (these are the names I’ve given the functions):

  • printmsg – to decompress and print a message. This uses OSWRCH to print to the screen.
  • findmsg – finds the message passed in X and passes it to printmsg
  • getinput – retrieves a input from the command line, using OSWORD 0. This also splits the input into a verb (stored at &41f) and a noun (stored at &43e)
  • searchlist – looks for the passed word in the passed list and returns the index in X. This is used to convert the verb and noun to a number.
  • handlecmd – looks up the entered verb and the looks for the appropriate address from a list of pointers.
  • the routines for the commands WAIT, SIT and QUIT

XAAN2

XAAN2 looks like it was originally the copy protection file – it contains a selection of calls to OSWORD to read sectors directly from the disk. This is unused on the Stairway to Hell disk.

XAAN

XAAN contains nearly everything, the code, the game data and everything else, apart from a few routines in XAAN1.

XAAN has multiple chunks of data with roughly the following uses:

From To Addr Purpose
0000 10be 1200 Code
10bf 30ff 22bf Empty data
3100 3248 4300 Introduction text
3249 32ff 4449 Unknown
3300 5c56 4500 Messages
5c57 5c5b 6e57 Unknown
5c5c 6e5c Room exits
62f6 74f6 Object locations and flags
6354 7554 Room messages
6400 6437 7600 Verb action pointer (low)
6438 646f 7638 Verb action point (high)
6473 65a8 7673 List of verbs
65a9 66e1 77a9 List of nouns
66e4 69fb 78e4 List of tokens

As far as we’re concerned, the data is all listed towards the end of free memory (&7c00 in Mode 7). The bit that surprises me is the amount of spare memory – bearing in mind that the BBC Micro only had 32KB of memory, of which large swathes are unusable due to disk and screen memory, there’s still around 8 KB of free memory whilst XAAN is running!

I’ll go into detail of the different sections later, but for now, the code block about performs all the stuff to run the game and apply the verbs. Unfortunately Robico decided against using a form of bytecode (like other text adventure creators), so all the logic is done in raw 6502.

The machine code does do a lot of JMPing to JMP instructions suggesting to me that it was probably assembled in blocks to reduce memory usage whilst coding it.

Objects are handled in a slightly strange way – every noun is counted as an object, with a location byte.

Anyway, to the data formats…

Strings

All strings are compressed with routine referred to by the creator as MIDGE. THis is relatively easy to decompress and there have been public domain examples found in the past.

This uses a dictionary of common patterns with some handy special characters.

Decompressing it is a matter of reading the next byte and then expanding according to the following rules. The best way to show this is to show my notes for the first location in the game Rick Hanson (which uses MIDGE too):

45 = "you"
FC = "'"
ED = "r" = 0xed - 0xdb (size of database)
59 = "e "
48 = "in "
3a = "the "
00 = "entrance "
70 = "ha"
93 = "ll"
FB = " "
49 = "of "
5D = "a "
EE = "s"
81 = "ma"
93 = "ll"
F9 = ","
DF = "d"
73 = "es"
66 = "er"
6D = "te"
5C = "d "
43 = "rail"
4B = "way"
FB = " "

Internally, this is all handled by printmsg in the XAAN file.
To save space, messages in the code are constructed from several different messages, e.g. from the THROW command:

.canthrow   lda invsize
            bne stuffinv      ; if we have stuff in inv 
            ldx #&10          ; message 16 "You are not"
            jsr findmsg
            ldx #&1a          ; message 26 "holding"
            jsr findmsg
            ldx #&18          ; "anything"
            jmp prtmsg

This is used to compress the intro message (at offset &3100), the bank of messages (at offset &3300) and the room descriptions (more later).

Rooms

Rooms are stored in two chunks:

  • exits (at offset &5c5c)
  • descriptions (at offset &6354)

Room numbers start at 52; this is because they are treated as nouns (see later).
For each room there is are a sequence of bytes. The bottom nybble of the first byte signifies the direction (up to 10, including IN and OUT); the top nybble indicates whether the exit is blocked (&40) or whether there are no exits (&80).

If there is an exit, the flag byte is followed by the destination.

Descriptions are stored as a list of tokens, like the messages.

Objects

Object and nouns (and rooms) all share a common number, which follows the ranges of:

  • 2 – 47 nouns
  • 52 – 223 rooms

For each noun/room there is a location byte (at offset &62f6) which has the location of the object. 0 is the inventory and 1 is being worn. This does mean that potentially items can be carried by other items!
Verbs/NounsThese are a simple list of strings separated by an “@” character. The number offset is used as an internal reference for the verb/noun number.

Running verb specific actions

There is a table of action entry points, at offset &6400. This is divided into two sections – low byte and high byte; this is to make it easy to do a vector jump:

            lda verbptrsl,x      ; lookup table to control each verb
            sta verbv
            lda verbptrsh,x
            sta verbvh
            jsr setnouns
            jmp (verbv)

Out of curiosity

I found one example of self modifying code in the handlecmd function, where it sets the colour of the output text. It does this by modifying this statement in printmsg:

 colourinst  = &0922
; &10921
.printmsg
{          
            lda #&86          ; this instruction can be altered
            jmp oswrch        ; VDU 134 (Colour = cyan)

We see this location modified in both xaan1.asm (in handlecmd) and in xaan.asm, for example:

.l20ca      lda #&86
            sta l0922
            rts