Atari Quill/Adventure Writer : File Format

This document aims to describe how the database for the Atari version of the Quill / Adventure Writer is laid out. Apart from 3 bytes in the header and 5 bytes in the main part of the database, none of which are referenced by the interpreter these explanations cover everything in the database.

Definitions

QuillA : The Quill for the ZX Spectrum, version A

QuillC : The Quill for the ZX Spectrum, version C

Condacts : Conditions & actions

General comments

  1. The Atari version sits somewhere between QuillA & QuillC in terms of functionality. Specifically, system messages are part of the database rather than being hard coded in the interpreter (QuillA) but it doesn't implement AUTO opcodes and object words (QuillC).
  2. The order of the blocks of data is not significant as far as the interpreter is concerned. Whether this is also true of the editor is unclear at this time.
  3. Text is (lightly) obfuscated by XORing with $FF.
  4. Colours in the header are copied to the shadow registers indicated on start up, so that display colours can be initialised per game.
  5. There is no text compression.
  6. The only text decoration provided is inverse video.
  7. Address tables are a sequence of word addresses, rather than the more efficient use of separate tables for low and high bytes of the addresses we often to see in 6502 programs.
  8. Condition and action opcodes shared some of the same byte values. Each list starts at zero, so opcode $00 is AT if it's a condition and INV if it's an action.

File format

The games are presented as Atari DOS segmented files, commonly refered to as .XEX files. Segments are stored in the files as a 6 byte header followed by the binary data. The Atari community will of course know this, but it's been included for the benefit of casual readers from elsewhere.

Offset Size Value Notes
0 2 $FFFF Constant $FFFF indicates start of segment
2 2 Start Start address of data block in memory
4 2 End End address of data block in memory
6 End-Start+1 Block of bytes Binary data loaded from disk into memory, starting from the address specified

The games contain 3 segments (based on a small sample, but I expect they are all the same).

Header format

The database starts at $1D00 with a 31 byte header, as follows:

Offset Address Size Notes
0 $1D00 1 Unused by the interpreter
1 $1D01 1 Text luminance (COLOR1, multiplied by 2)
2 $1D02 1 Background colour (COLOR2)
3 $1D03 1 Border colour (COLOR4)
4 $1D04 1 Number of objects that can be carried
5 $1D05 1 Object count
6 $1D06 1 Location count
7 $1D07 1 Message count
8 $1D08 1 System message count
9 $1D09 2 Event table address
11 $1D0B 2 Status table address
13 $1D0D 2 Object table address
15 $1D0F 2 Location table address
17 $1D11 2 Message table address
19 $1D13 2 System message table address
21 $1D15 2 Movement table address
23 $1D17 2 Vocabulary table address
25 $1D19 2 Object start location table address
27 $1D1B 2 End of database
29 $1D1D 2 Unused by the interpreter

Data blocks

Data starts at $1D1F (immediately after the header) with the condacts for the event table, then follows on as below. The order probably isn't important, at least not to the interpreter because of the addresses in the header and the indirection in the code. The order may be important to the editor, I have not checked. However, I suspect the order will always be the same.

Note that tables of pointers follow the data they reference in memory.

Where data is indirected using such a table, the address in the header points to this table, not the underlying data.

Block Type Notes
EventCondacts Condacts Opcodes and data for events.
EventTable Events Words and condact addresses for events. These are the responses to what the player inputs.
StatusCondacts Condacts Opcodes and data for statuses.
StatusTable Events Words (comments) and condact addresses for statuses. These are the actions which run each turn, regardless of player input.
ObjectsText Text Object descriptions.
ObjectsAddressTable Pointers Start addresses for each object description.
LocationsText Text Location descriptions.
LocationsAddressTable Pointers Start addresses for each location description.
MessagesText Text Messages (game specific).
MessagesAddressTable Pointers Start addresses for each message.
SystemMessagesText Text Messages (system).
SystemMessagesAddressTable Pointers Start addresses for each system message.
ConnectionsData Connections Words and location numbers which define how locations are connected.
ConnectionsAddressTable Pointers Start addresses for each location's connections.
Vocabulary Words List of words and associated numbers the game implements.
Unreferenced n/a Unreferenced by the interpreter. Seems to be a block of 5 bytes, all zeros.
ObjectLocationTable ObjectLocations Start locations for all objects.

See below for descriptions of each block type.

Block type : Pointers

Data items referenced by these pointers are numbered from 0 to N - 1, where N is the item count.

As noted above, addresses are stored as words (low,high) rather than being split across two tables. Therefore, the address for data item number I will be found at (start address from header + I * 2).

Block type : Text

This covers objects, locations, messages and system messages, which all share a common format.

Data starts at the address taken from the corresponding "Pointers" block.

Each string is stored as a zero terminated sequence of bytes, XORed with $FF. Note that the terminator bytes is also XORed, so is $FF in the database. Characters with the high bit set are inverse video in graphics 0, except $9B which is the newline character.

Block type : ObjectLocations

The address in the header points to a table of N + 1 bytes, where N is the number of objects. The last byte in the table is set to $FF and the interpreter depends on this to work as it doesn't check the object count in all cases.

Value Notes
$FC Not created (252)
$FD Worn (253)
$FE Carried (254)
$FF End of table (255)
<=$FB Initial location (0-251)

Block type : Words

The address in the header points to a table which consists of blocks of 5 bytes. Each block consists of 4 bytes for each word, XORed with $FF, followed by the word number. If the word number is $FF, that signifies the end of the table. The last word is "*". Words are space filled so they're all exactly 4 characters. Multiple words can have the same word value, making them aliases of each other.

Block type : Connections

Data starts at the address taken from the corresponding "Pointers" block.

There can be zero or more entries per location.

Each entry in the table points to a block of byte pairs, terminated with a $FF. The first byte in each pair is the word number (which, although not enforced in the interpreter, should relate to a direction word) and the second byte is the target location.

Block type : Events

The address in the header points to a table of 4 byte entries, as follows.

Offset Size Notes
0 1 Word number of first word
1 1 Word number of second word
2 2 Address of condact block

For the event table (or Vocabulary Action Table as the manual calls it) the word numbers are matched against what the player entered and the corresponding condact block executed. For the status table, the word numbers are ignored because the interpreter needs to run the whole table. The word numbers in this case are defined as comments in the manual.

The table is terminated with a single zero byte (word number 1 == 0).

Block type : Condacts

Each entry consists of zero more more condition opcodes and their arguments, followed by a $FF, then one or more action opcodes and their arguments. The entry is terminated with another $FF. In theory there could be zero action opcodes, but that's pointless and unlikely to exist in practice.

The interpreter knows how many arguments each opcode consumes and advances a pointer appropriately.

To make this clearer, here's a made up example. The player has entered KILL SMURF and these words matched an entry in the event table. Here is a sample condact.

Offset Byte Notes
0 $08 CARRIED (condition opcode)
1 $10 SWORD (let's say object $10 is a sword)
2 $00 AT (condition opcode)
3 $20 LAIR (let's say location $20 is the smurf's lair)
4 $04 PRESENT (condition opcode)
5 $11 SMURF (let's say object $11 is an unslain smurf)
6 $FF Mark end of condition block
7 $12 MESSAGE (action opcode)
8 $25 Let's say message $25 reads "You slay the foul creature with the sword."
9 $19 SWAP (action opcode)
10 $11 SMURF (the live smurf)
11 $12 CORPSE (let's say object $12 is the murdered smurf)
12 $01 DESC (action opcode), so the player gets to see the murdered smurf
13 $FF Mark end of action block and hence condact entry

Condition opcodes

Byte Opcode Arguments Notes
$00 AT 1 (location number) Player at location number
$01 NOTAT 1 (location number) Player not at location number
$02 ATGT 1 (location number) Player at location > number
$03 ATLT 1 (location number) Player at location < number
$04 PRESENT 1 (object number) Object is present
$05 ABSENT 1 (object number) Object is absent
$06 WORN 1 (object number) Object is worn
$07 NOTWORN 1 (object number) Object is not worn
$08 CARRIED 1 (object number) Object is carried
$09 NOTCARR 1 (object number) Object is not carried
$0A CHANCE 1 (percentage) Percentage > random number
$0B ZERO 1 (flag number) Flag == 0
$0C NOTZERO 1 (flag number) Flag != 0
$0D EQ 2 (flag number, constant) Flag == constant
$0E GT 2 (flag number, constant) Flag > constant
$0F LT 2 (flag number, constant) Flag < constant

Action opcodes

Byte Opcode Arguments Notes
$00 INV 0 List inventory
$01 DESC 0 Describe current location
$02 QUIT 0 Prompt player to quit game
$03 END 0 End game, prompt player to try again
$04 DONE 0 Stop processing events
$05 OK 0 Print OK message
$06 ANYKEY 0 Prompt player to press any key and wait for this to happen
$07 SAVE 0 Prompt user for file name and save game state
$08 LOAD 0 Prompt user for file name and load game state
$09 TURNS 0 Print number of turns taken
$0A SCORE 0 Print score (out of 100)
$0B CLS 0 Clear screen
$0C DROPALL 0 Drop all droppable items
$0D PAUSE 1 (ticks) Pause for number of ticks (1 tick ~ 1/50th of a second)
$0E SCREEN 1 (value) Set screen colour to value (COLOR2)
$0F TEXT 1 (value) Set screen text luminosity to value * 2 (COLOR1)
$10 BORDER 1 (value) Set screen border colour to value (COLOR4)
$11 GOTO 1 (location number) Move player to new location
$12 MESSAGE 1 (message number) Print message with specified number
$13 REMOVE 1 (object number) Remove object with specified number, if worn
$14 GET 1 (object number) Get object with specified number
$15 DROP 1 (object number) Drop object with specified number, if carried
$16 WEAR 1 (object number) Wear object with specified number if carried
$17 DESTROY 1 (object number) Change location of object with specified number to NOTCREATED, removing it from play
$18 CREATE 1 (object number) Change location of object with specified number to the current location, bringing it in to play
$19 SWAP 2 (object1, object2) Swap locations of the two specified objects
$1A PLACE 2 (object number, location number) Move specified object to specified location
$1B SET 1 (flag number) Flag = TRUE ($FF)
$1C CLEAR 1 (flag number) Flag = FALSE ($00)
$1D PLUS 2 (flag number, constant) Flag += constant, but with a maximum of 255
$1E MINUS 2 (flag number, constant) Flag -= constant, but with a minimum of 0
$1F LET 2 (flag number, constant) Flag = constant
$20 SOUND 4 (voice, pitch, distortion, volume) Set Atari sound registers