Every byte accounted for
The game is 30208 bytes and about two thirds of it is not code. This page is what that two thirds turned out to be. Nothing here was guessed from a hex dump: every table was found by following the code that reads it, and every extent was checked by seeing whether it tiled its region without leaving a byte over.
The castle, written out in full
5488 bytes at INITIAL_STATE are simply the whole runtime area -- $EA90 upwards -- as it looks before the game starts, copied into place with one LDIR. It holds three empty records for the player, the weapon and the sound slot; 115 objects, among them sixteen mushrooms and ten each of eight kinds of food; five monsters, one each of the mummy, Dracula, the devil, Frankenstein's monster and the humpback; and 274 sixteen-byte records, 205 doors and 69 pairs of furniture. The doors are sixteen bytes each, not eight -- one record holding both sides. The room lists prove it: of the 274, exactly 272 are named by two different rooms, which is what a door joining two rooms looks like from the data. That is also why DOOR_OTHER_SIDE flips bit 3 of an address rather than adding anything: it is moving between the two halves of one record. Two routines edit this template before it is copied, PLACE_ACG_KEY and PLACE_KEYS, choosing rooms out of small tables using the frame counter. So the key pieces, three of the keys and the mummy are somewhere different every game (except the first after loading, which the frozen frame counter always sets out alike), and the randomness is applied to the picture of the castle rather than to the castle itself.
The sprites
Every graphic's address is in SPRITE_TABLE, 239 entries of two bytes: 161 creatures and objects, 39 pieces of furniture, and the furniture's 39 colour tables. A creature or object carries no length: the first byte is the row count and every one is two bytes -- sixteen pixels -- wide. That was not read off a hex dump either. Each code was drawn on a machine of its own with the reads into the graphics region logged, and every creature and object read exactly one plus twice the row count, contiguously, from the address the table holds. The fourteen graphics below GFX_0D are the wider pieces the rooms are built from and carry a width as well, and those fourteen tile their region exactly, ending on the first byte of ROOM_TABLE. An earlier version of this page said a few hundred bytes here were artwork nothing points at. There is none: see "Code and data that nothing reaches" below.
The rooms
A room is a shape number and a colour. The shape gives how far the player may walk from the centre each way, a table of corners, and a list of which corners to join up. There are thirteen of them. Count the entries in ROOM_TABLE carefully: there are 151, not 149. The last is not a room at all but the trapdoor fall -- shape $0C, twelve nested rectangles, drawn with the walk limits closing in so that it reads as dropping down a shaft. Stopping at 149 makes that shape look like unused artwork, which it very much is not. Two shapes really are unused. $05 to $08 are one staircase in four orientations, sharing a single edge list and differing only in their corners; the game uses $05 for five rooms and $08 for one, and leaves $06 and $07 -- both complete, both drawable -- untouched. What is in a room is a separate list, and those lists follow their index with no gaps at all, filling everything up to $7C18, the byte before the game's own entry point.
The two character sets
There are two. The text font at TEXT_FONT has 59 characters, $20 to $5A, so the game can print spaces, digits, punctuation and capitals and nothing else. The code never names that address: it loads TEXT_FONT less $100, the same block less $20 characters, so a character's own code indexes it, and FONT_DIGITS, one bias further on, so that a digit's value indexes its own glyph. A second set of 94 characters draws the status panel, with a 192-byte map of which piece goes in each of its 8 columns by 24 rows: the parchment scroll down the side of the screen is a little character set and an index, not a bitmap. Running the routine and photographing the screen is what established that -- these tiles were written up here as a title picture until then.
CONGRATULATIONT
The message shown when the player escapes reads CONGRATULATION and then $D4, which is "T" with the end marker set. Drawing the screen confirms it: the game says CONGRATULATIONT. The sequence that plays it -- a note climbing while the screen flashes in and out from the middle -- is never reached by a machine playing the game at random, which is why it sat in the disassembly as data until it was picked apart by hand.