King's Quest Title Screen

Inside King's Quest(1984)

A reverse engineering deep dive into Sierra On-Line's AGI engine — extracting vector graphics, sprite animations, and compiled game logic from the original floppy disk.

82 Backgrounds118 Sprites90 Logic Scripts16 Colors

> Backgrounds — Vector Art

Every background in King's Quest is stored as a series of vector drawing commands— not pixel bitmaps. Lines, fills, and brush patterns are executed by a state machine to produce 160×168 pixel images. This saved precious floppy disk space while enabling the rich, colorful scenes below.

Title Screen
Title ScreenPIC 83

The iconic King's Quest banner -- drawn entirely with vector line and fill commands

Castle Exterior
Castle ExteriorPIC 1

King Edward's castle with shields, drawbridge, and alligator-filled moat

Lake & Tree
Lake & TreePIC 5

A peaceful pond scene with a gnarled tree and cattails

Goat Pen
Goat PenPIC 10

The fenced goat pen area -- home to your future traveling companion

Forest
ForestPIC 30

Dense forest with pine and deciduous trees, typical of Daventry's wilderness

Forest Path
Forest PathPIC 40

A clearing with golden trees and winding dirt paths

Large Lake
Large LakePIC 20

A massive lake scene with surrounding pine trees and a small cloud

Cave Interior
Cave InteriorPIC 50

A dark cavern with stalactites -- the dragon's lair

> Sprites & Characters

Characters and objects are stored as VIEWresources — collections of Loops (directions/animations) containing Cels (individual frames). Each cel uses RLE compression with transparency, and loops can mirror each other to save space (left-facing = flipped right-facing).

Characters

Sir Graham
Sir GrahamVIEW 0

The hero -- a brave knight on a quest to save Daventry. 4 directional walk cycles.

The Wolf
The WolfVIEW 4

A vicious wolf that chases Graham through the forest. Red-eyed and fast.

The Witch
The WitchVIEW 3

A wicked witch on a broomstick who swoops down to grab you.

Fairy Godmother
Fairy GodmotherVIEW 6

Your magical protector -- grants a spell of invisibility.

The Dragon
The DragonVIEW 33

A fire-breathing green dragon guarding treasures in its cave.

The Condor
The CondorVIEW 8

A great eagle/condor with a majestic flight animation.

The Gnome
The GnomeVIEW 27

Rumplestiltskin! Guess his name correctly for a reward.

The Goat
The GoatVIEW 5

Your loyal (and sometimes sacrificial) companion.

Alligators
AlligatorsVIEW 50

Swimming in the castle moat -- don't fall in!

The Dwarf
The DwarfVIEW 111

A sneaky dwarf who tries to steal your treasures.

Condor + Graham
Condor + GrahamVIEW 89

The condor carrying Graham through the sky.

Moat Creatures
Moat CreaturesVIEW 107

Creatures lurking in the waters around the castle.

Items & Objects

Boulder
Boulder
Treasure Chest
Treasure Chest
Door/Gate
Door/Gate
Rock
Rock
Graham (Dead)
Graham (Dead)

> How the AGI Engine Works

Architecture Overview

The AGI (Adventure Game Interpreter) engine is a virtual machine designed by Sierra On-Line in 1984. It runs compiled bytecode scripts (LOGIC resources) that control everything: room transitions, NPC behavior, parser input, inventory, and scoring.

Game Loop (~20 Hz)

  1. Poll keyboard & joystick input
  2. Parse player text commands
  3. Execute all loaded LOGIC scripts
  4. Update object positions & animations
  5. Render visual screen with z-ordering
  6. Repeat

Resource Types

  • LOGIC — Compiled bytecode scripts
  • PIC — Vector background art
  • VIEW — RLE sprite animations
  • SOUND — PC speaker / Tandy audio
  • OBJECT — Inventory item definitions
  • WORDS.TOK — Parser vocabulary

PIC: Vector Drawing Engine

Backgrounds are stored as compact sequences of drawing commands — not bitmaps. A state machine reads opcodes 0xF0–0xFFand draws onto two parallel 160×168 buffers (visual + priority).

PIC Drawing Opcodes
0xF0 color   -- Set visual color & enable drawing
0xF1         -- Disable visual drawing
0xF2 color   -- Set priority color & enable
0xF3         -- Disable priority drawing
0xF4 x,y ... -- Y-corner step lines
0xF5 x,y ... -- X-corner step lines
0xF6 x,y ... -- Absolute line segments
0xF7 x,y ... -- Relative line (packed displacements)
0xF8 x,y ... -- Flood fill from seed points
0xF9 style   -- Set brush pen (size, shape, texture)
0xFA x,y ... -- Plot with current brush
0xFF         -- End of picture

The castle exterior (PIC 1) is only ~2KBof drawing commands but produces a detailed 320×168 image. That's how Sierra fit 82 backgrounds on a single 360K floppy disk.

VIEW: RLE Sprite Compression

Each animation frame (cel) is encoded with Run-Length Encoding. A single byte packs both color and run length:

VIEW Cel Data Format
Each byte: [color:4][count:4]
  Upper nibble = EGA color (0-15)
  Lower nibble = pixel count (1-15)
  0x00 = end of scanline

Example: 0x43 0xF2 0x00
  -> 3 pixels of color 4 (red)
  -> 2 pixels of color 15 (white)
  -> end of line, fill remaining with transparent

Loops can MIRROR other loops:
  Loop 0 (right-facing) -> data
  Loop 1 (left-facing)  -> mirrors loop 0, flipped

The EGA Palette — All 16 Colors

The entire game uses just 16 fixed colors from the EGA standard. Every pixel in every background and sprite is one of these:

0
Black
1
Blue
2
Green
3
Cyan
4
Red
5
Magenta
6
Brown
7
Lt Gray
8
Dk Gray
9
Lt Blue
10
Lt Green
11
Lt Cyan
12
Lt Red
13
Lt Magenta
14
Yellow
15
White

LOGIC: Bytecode Scripting

Game behavior is written in Sierra's custom scripting language, compiled to bytecode. The engine provides 183 action commands and 20 test conditions. Text strings are XOR-encrypted with the key "Avis Durgan" (cycling every 11 bytes).

LOGIC Bytecode Structure
+-----------------------------------+
| Bytes 0-1: Messages offset (LE)  |
+-----------------------------------+
| CODE SECTION                      |
|   0xFF [...tests...] 0xFF        |  <- if() block
|     jump_offset (2 bytes)         |
|   action_opcode arg1 arg2...      |  <- commands
|   0xFE jump_offset                |  <- else/goto
+-----------------------------------+
| MESSAGES SECTION                  |
|   num_messages (1 byte)           |
|   end_offset (2 bytes)            |
|   offset_table (2 * N bytes)     |
|   XOR-encrypted text strings      |
|   Key: "Avis Durgan" (cycling)    |
+-----------------------------------+

Virtual Machine State

256 Variables (8-bit)

Key Variables
v0  = currentRoom
v1  = previousRoom
v3  = currentScore
v6  = egoDirection
v7  = maxScore (158)
v17 = errorCode
v19 = keyPressed

256 Flags (boolean)

Key Flags
f0  = egoOnWater
f2  = playerControl
f4  = wordNotFound
f5  = newRoom (just entered)
f6  = restartGame
f9  = soundOn

> Disassembled Game Logic

Here's actual disassembled bytecode from the game, showing how rooms, NPCs, and player interactions work. The said() commands match player text input against the vocabulary.

LOGIC 1 -- Castle Exterior (Room Setup)
if (isset(f5(newRoom))) {
  load.pic(0);
  draw.pic(0);          // Draw castle background
  discard.pic(0);
  set.horizon(50);      // Sky/ground boundary

  // Place moat creatures
  load.view(107);
  animate.obj(11);
  animate.obj(12);
  set.view(11, 107);
  position(11, 121, 161);
  position(12, 73, 166);
  object.on.water(11);
  object.on.water(12);
  wander(11);           // Random movement
  wander(12);

  draw(0);              // Draw ego (Graham)
  show.pic();
  accept.input();
}

// Player types "look"
if (said(2, 137)) {
  print("You are standing outside a castle surrounded
         by an alligator filled moat.");
}

// Player types "swim" near moat
if (said(2, 151)) {
  print("The water in the moat is not conducive
         to swimming.");
}
LOGIC 21 -- The Witch Encounter
// Witch appears and chases Graham
load.view(3);           // Witch sprite
animate.obj(1);
set.view(1, 3);         // Assign witch to object 1

// Protection checks
if (isset(f75)) {
  print("Lucky for you that you are invisible,
         the witch can't find you.");
}

if (isset(f70)) {
  print("The witch can feel your protective spell
         and bids a hasty retreat.");
}

// Goat scares witch away!
if (isset(f68)) {
  print("There is a witch in the area, but since
         you have that menacing goat with you,
         she will keep her distance.");
}

// Witch catches you
print("The witch swoops down, grabs you by the neck,
       and carries you off to a fate worse than death.");
LOGIC 51 -- The Dragon's Lair
// Dragon encounter setup
load.view(33);          // Dragon sprite
load.view(61);          // Fire breath effect
animate.obj(1);
set.view(1, 33);

// Player throws dagger
if (said(174, 146) && has(9)) {
  print("Nervously, you throw the dagger at the dragon
         and strike it in the heart. Death for the
         dragon is instant.");
  // Dragon dies, reveal mirror treasure
}

// Player tries to fight without weapon
print("You need a weapon to fight a dragon!");

> Easter Eggs & Secrets

Death Messages

Sierra was famous for creative player deaths. Here are some from the game data:

“Misfortune strikes and you have fallen into the moat. Your struggles and cries have attracted hungry alligators. They do not want to let you go.”
“The witch swoops down, grabs you by the neck, and carries you off to a fate worse than death.”
“It seems the wolf caught up with you and is discussing dinner arrangements.”
“The moats slippery sides prevent you from getting out.”
“You hurl the dagger at the old goat... he tries to avoid the flying dagger, but your aim is true... as the dagger strikes, he slumps to the ground... dead.”
“Nervously, you throw the dagger at the dragon and strike it in the heart. Death for the dragon is instant.”

Funny Responses

Try typing weird things and the parser has something to say:

“What! Are you crazy?”
“It is very inconsiderate to eat in front of these poor, starving people. The pains of guilt set in on you.”
“The water in the moat is not conducive to swimming.”
“You can't carry a goat very far.”
“You do not want to carry around a dead goat.”
“Now is not a good time to try and eat the mushroom.”
“Seeing how much the goat likes that carrot, eating it may be hazardous to your health.”
“You open your mouth while swimming and quench your thirst.”
“It is not nice to tease the goat.”
“The wolf is in no mood for conversion.”
“The wicked witch is not interested in chitchat.”
“There is no way to kill a flying witch!”
“King Edward does not allow anyone to take his flowers.”

Developer Credits (Hidden in Data)

These messages were XOR-encrypted with “Avis Durgan” in the game binary. Decrypting them reveals:

“King's Quest was designed by Roberta Williams.”
“Thank you for playing King's Quest from Ken and Roberta Williams”
“We, at Sierra, wish to thank you for playing King's Quest. We are very sorry that you did not succeed and hope you will fare better next time.”
“We, at Sierra, wish to thank you for playing King's Quest. We are pleased to see that you are doing so well and hope that your score will keep improving.”
“We, at Sierra, wish to thank you for playing King's Quest. We are delighted by your score and hope you are enjoying the game.”

The “Avis Durgan” Mystery

All text strings in the game are XOR-encrypted with the key "Avis Durgan". Who is Avis Durgan? The encryption was meant to prevent casual hex-editor snooping of game text (spoilers!) rather than serious security. Our disassembler cracks it automatically by XOR'ing each byte with the corresponding position in the repeating 11-byte key.

> Technical Details

How This Was Built

This entire project was built by reverse engineering the original King's Quest floppy disk image downloaded from Archive.org. No existing extraction tools were used — all parsers and renderers were written from scratch in Python.

Extraction Pipeline

  1. Extract files from FAT12 floppy image using mtools
  2. Parse *DIR files (3 bytes/entry) to build resource index
  3. Read VOL files (verify 0x1234 signature, extract by offset)
  4. Decode each resource type with format-specific parsers

Disk Layout

360K Floppy Contents
VOL.0    48KB  --+
VOL.1   200KB    +-- Resources
VOL.2    91KB  --+
LOGDIR    315B --+
PICDIR    255B   |-- Directories
VIEWDIR   432B   |   (3 bytes/entry)
SNDDIR    144B --+
WORDS.TOK 3KB  -- Vocabulary
OBJECT    331B -- Items

Tools Written

  • agi_common.py — Resource loader
  • render_pic.py — PIC vector renderer (~400 LOC)
  • render_view.py — VIEW sprite renderer (~200 LOC)
  • disasm_logic.py — Bytecode disassembler (~350 LOC)