Unix pages
Home -> UNIX software -> UnQuill -> QDB file format

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