Quill QDB file format (v0.2)
.QDB is the format used for games produced by the Quill adventure system under CP/M. It's the only distribution format of Quill games I've seen where the database is in a separate file from the interpreter.
This document has been produced solely by studying the Amstrad PCW port of the Very Big Cave Adventure, so I may well have missed aspects of the .QDB file format not covered by this particular implementation.
The .QDB file is assumed to load in memory at 0x0F00. It begins with a header:
0F00 DB 0x00 ;Unknown -- zero in both CAVE1.QDB and CAVE2.QDB 0F01 DB 0x01 ;Database version, 1 0F02 DB debugmode ;Nonzero in debug mode (interpreter prints a ;banner at startup, and issues warnings if an ;out-of-range object / location / flag is ;accessed). 0F03 DB objcount ;Number of objects 0F04 DB loccount ;Number of locations 0F05 DB msgcount ;Number of messages 0F06 DB syscount ;Number of system messages 0F07 DW response ;Address of response table 0F09 DW process ;Address of process table 0F0B DW objects ;Address of objects table 0F0D DW locations ;Address of locations table 0F0F DW messages ;Address of messages table 0F11 DW sysmsg ;Address of system messages table 0F13 DW connections ;Address of connections table 0F15 DW vocab ;Address of vocabulary table 0F17 DW ob_locs ;Address of object initial locations table 0F19 DW ob_words ;Address of object words table 0F1B DW end ;Address of database end
The format of the individual tables is then:
Process / Response
The Process table is executed before user input, the Response after. The Response table has four bytes per entry:
DB word_id ;Verb to match, 0xFF is a wildcard DB word_id ;Noun to match, 0xFF is a wildcard DW address ;Address of bytecode
The Process table has only two bytes per entry: the address of the bytecode. All entries are executed with no checking of verb / noun.
The end of the table is marked by an all-zeroes entry.
As in PAW, but in a change from earlier versions of the Quill, there is no distinction at the bytecode level between conditions and actions; they can be interspersed as the game author pleases. They are:
- 0x00 loc: AT loc
- Execution continues if the player is at the specified location.
- 0x01 loc: NOTAT loc
- Execution continues if the player is not at the specified location.
- 0x02 loc: ATGT loc
- Execution continues if the player's location number is higher than the number specified.
- 0x03 loc: ATLT loc
- Execution continues if the player's location number is lower than the number specified.
- 0x04 obj: PRESENT obj
- Execution continues if the specified object is in the same location as the player, carried, or worn.
- 0x05 obj: ABSENT obj
- Execution continues if the specified object is not in the same location as the player, carried, or worn.
- 0x06 obj: WORN obj
- Execution continues if the player is wearing the specified object.
- 0x07 obj: NOTWORN obj
- Execution continues if the player is not wearing the specified object.
- 0x08 obj: CARRIED obj
- Execution continues if the player is carrying the specified object.
- 0x09 obj: NOTCARR obj
- Execution continues if the player is not carrying the specified object.
- 0x0A number: CHANCE number
- Execution continues if a pseudo-random number is less than number.
- 0x0B flag: ZERO flag
- Execution continues if the specified flag is zero.
- 0x0C flag: NOTZERO flag
- Execution continues if the specified flag is not zero.
- 0x0D flag const: EQ flag const
- Execution continues if the specified flag has the correct value.
- 0x0E flag const: GT flag const
- Execution continues if the specified flag has a value greater than the amount indicated.
- 0x0F flag const: LT flag const
- Execution continues if the specified flag has a value less than the amount indicated.
- 0x10 word_id: WORD3 word_id
- Execution continues if a third vocabulary word is matched in the command after the verb and noun, and it is word.
- 0x11 word_id: WORD4 word_id
- Execution continues if a fourth vocabulary word is matched in the command after verb, noun, and WORD3, and it is word.
- 0x12: INVEN
- Print the player's inventory.
- 0x13: DESC
- Stop bytecode processing, describe the player's current location and prompt for a new input.
- 0x14: QUIT
- Ask the player if they want to quit. Execution continues if they say yes.
- 0x15: END
- Stop bytecode processing, display the game over message, and ask if the player wants another go. If they do, restart the game; otherwise, return to CP/M.
- 0x16: DONE
- Stop bytecode processing and prompt for a new input.
- 0x17: OK
- Display system message 15 ("OK") and behave as DONE.
- 0x18: ANYKEY
- Display system message 16 ("Press any key to continue") and wait for a keypress.
- 0x19: SAVE
- Prompt for a filename to save the game state to, and save it.
- 0x1A: LOAD
- Prompt for a filename to load the game state from, and load it.
- 0x1B: TURNS
- Display the number of turns the player has taken.
- 0x1C: SCORE
- Display the player's score.
- 0x1D: CLS
- Clear the screen.
- 0x1E: DROPALL
- Move everything the player is carrying to the current location.
- 0x1F: AUTOG
- Try to match the current noun with an object number. If successful, execute GET on that object.
- 0x20: AUTOD
- Try to match the current noun with an object number. If successful, execute DROP on that object.
- 0x21: AUTOW
- Try to match the current noun with an object number. If successful, execute WEAR on that object.
- 0x22: AUTOR
- Try to match the current noun with an object number. If successful, execute REMOVE on that object.
- 0x23 ticks: PAUSE ticks
- Delay for roughly ticks/50 seconds.
- 0x24: BELL
- Sound the beeper by writing character 7 (BEL) to the screen.
- 0x25 loc: GOTO loc
- Move the player to the specified location.
- 0x26 msg: MESSAGE msg
- Display the specified message.
- 0x27 obj: REMOVE obj
- If the specified object is worn, move it to the player's inventory. If this isn't possible, display an error saying why.
- 0x28 obj: GET obj
- If the specified object is in the current location, move it to the player's inventory. If this isn't possible, display an error saying why.
- 0x29 obj: DROP obj
- If the specified object is in the player's inventory, move it to the current location. If this isn't possible, display an error saying why.
- 0x2A obj: WEAR obj
- If the specified object is in the player's inventory, the player wears it. If this isn't possible, display an error saying why.
- 0x2B obj: DESTROY obj
- Destroy the specified object. If it was in the player's inventory, the number of objects carried decreases by 1.
- 0x2C obj: CREATE obj
- Move the specified object to the current location. If it was in the player's inventory, the number of objects carried decreases by 1 (fixing a bug in earlier Quill and Quill-like engines.)
- 0x2D obj1 obj2: SWAP obj1 obj2
- Exchange the locations of the two objects.
- 0x2E obj loc: PLACE obj loc
- Move the specified object to the specified location.
- 0x2F flag: SET flag
- Set the specified flag to 255.
- 0x30 flag: CLEAR flag
- Set the specified flag to 0.
- 0x31 flag amount: PLUS flag amount
- Add amount to the specified flag. If the total exceeds 255 it will be capped at 255.
- 0x32 flag amount: MINUS flag amount
- Subtract amount from the specified flag. If the total is less than zero it will be set to zero.
- 0x33 flag amount: LET flag amount
- Set the value of the specified flag to amount.
- 0x34: NEWLINE
- Print a carriage return / line feed.
- 0x35 flag: PRINT flag
- Print the value of a flag as a decimal number.
- 0x36 msg: SYSMESS msg
- Display the specified system message.
- 0x37 obj loc: ISAT obj loc
- Execution continues if the specified object is at the specified location.
- 0x38 obj flag: COPYOF obj flag
- Set flag to the location of the specified object.
- 0x39 obj1 obj2: COPYOO obj1 obj2
- Move object obj2 to the location of obj1.
- 0x3A flag obj: COPYFO flag obj
- Move object obj to the location specified in flag.
- 0x3B flag1 flag2: COPYFF flag1 flag2
- Set flag2 to the value of flag1.
- 0x3C: ISDESC
- Execution continues if the location description has been displayed this turn.
- 0x3D nn: EXTERN nn
- Call external code, passing the supplied byte to it as a parameter. This requires the external code to be added to the interpreter; by default it is a no-op.
- 0xFF
- End of bytecode sequence.
Text tables
The object, location, message and system message tables are all stored in the same way. The corresponding word in the header points to a table of words giving the addresses of the object/location/message strings.
The strings are stored with each byte complemented (XORed with 0xFF) as a protection against casual snooping. End of string is indicated by 0xF5, which complemented is 0x0D (carriage return). The only other control code I have seen used is 0x0A (newline).
Regarding character set: since CP/M has no standard character set beyond ASCII, I would expect QDB files only to contain ASCII and not to make assumptions about characters 0x80 and up.
Connections
As with location texts, the word in the header points to a table of words with one entry per location. Each word points to a list of connections:
DB word_id ;Direction DB location ;Where it leads to
The list is terminated by a word_id of 0xFF.
Vocabulary
The vocabulary table contains five bytes per word. The first four are the word (complemented ASCII) and the fifth is its word_id. Words 1-12 are used for movement verbs.
The end of the table is marked by an entry containing five zero bytes.
Object initial locations
One byte per object giving its start location. Values of 0xFC or greater mean:
0xFC: Object destroyed 0xFD: Object worn 0xFE: Object carried
The table must be followed by a 0xFF byte.
Object words
One byte per object giving the word_id for that object — used by the AUTOG / AUTOD / AUTOW / AUTOR operations. For example, in the Very Big Cave Adventure, object 4 ("A shiny brass lamp") has word 20 (LAMP) as its name. Objects with no name use a word_id of 0xFF.
Format of saved game state
The game state is saved as a 512-byte file with the extension .QGP. The first 256 bytes hold flags:
DB game flags ;Flags 0-29: 30 bytes DB score ;Flag 30: Score DW turns ;Flags 31-32: Number of turns DB verb ;Flag 33: Current verb DB noun ;Flag 34: Current noun DB word3 ;Flag 35: Third recognised word DB word4 ;Flag 36: Fourth recognised word DB ability ;Flag 37: Maximum number of objects player ; can carry DB location ;Flag 38: Player's current location ; ; Flags 39-255 appear not to be used and contain zeroes. ;
The second 256 bytes hold the locations of the 256 objects, with the following special meanings:
0xFC: Object destroyed 0xFD: Object worn 0xFE: Object carried
Interpreter header
The interpreter (RUN.COM) begins with a jump block allowing system support to be customised:
0100 JP start ;Entry point 0103 JP cls ;Clear screen 0106 JP delay ;Delay for approx 1/50 second. 0109 JP diagnostic ;Called before each input if the game is in ;debug mode 0112 JP restart ;Start / restart the loaded game. 0115 RET ;Vector for external routines called by EXTERN NOP ;Will be entered with A=parameter, IX-> flags NOP
The area from 0x118 to 0x142 contains system-dependent subroutines called from this jump block.
John Elliott 2023-09-15