Two templates no room uses
Of the 29 block types in block_type_tbl, number 1 -- a fire that stands still -- and number 17 -- spikes raised four levels -- are named by no room in the castle; the rooms use the moving fires and the ordinary spikes instead. Both are drawn on the templates page.
A wrong charm is destroyed, not returned
Anything dropped into the wizard's cauldron is taken out of the game. When a charm lands in it, add_obj_to_cauldron counts it towards the fourteen only if it is the kind the cauldron is asking for; either way its place in special_objs_tbl is cleared and the object disappears. There are four of each kind, so a mistake costs one of them, not the game.
The percentage without a divide
The score at the end is rooms visited plus twice the charms delivered, out of 128 rooms and 14 charms: 156 points. calc_and_display_percent turns that into a percentage without dividing: it adds 100/156 as a 16-bit fraction once per point and counts the carries out of the top in BCD. A last small addition makes a perfect game come out at 100 rather than 99.
The wanted list is never reset
The order in which the cauldron asks for the charms is a list of fourteen at objects_required, two of each kind. At the start of every game shuffle_objects_required rotates it in place by four to seven places, from the seed -- and nothing ever puts it back. So the first game after loading can ask in only four orders, and each game after that rotates on from where the last one left it.
Time on the menu picks the start
The seed at $5BA0 goes up by one on every pass of the menu's loop (menu_loop), and its bottom two bits choose both the start room -- one of four, in start_locations -- and how far the wanted list is rotated. How long a player waits on the menu before pressing 0 decides where the game begins.
The balls chase the werewolf and flee the man
The bouncing balls (types $B6 and $B7) move by rewriting their own code: each frame upd_182_183 writes JR C or JR NC into two of its jumps, depending on whether the player's legs are Sabreman's or Sabrewulf's. So the same balls hop away from the knight and towards the werewolf.
Sprites are turned round in place
A sprite is stored once. When an object wants it facing the other way or upside down, vflip_sprite_data rewrites the sprite's own bytes -- rows swapped end for end, or each row's bytes reversed through the bit-reversal table at reverse_bits_tbl -- and toggles a bit in its header to remember which way round the data now lies. So a snapshot catches each sprite as it was last drawn: spr_014 is stored upside down in this one, because the last thing drawn with it was the fourth corner of the menu's border, which print_border draws flipped.
A day is 392 frames
The sun or moon crosses the panel's window a pixel every eight frames, and changes over after 49 steps: 392 frames of daylight, then 392 of night, so the forty days are 31360 of the game's frames. How long that is in minutes depends on the rooms: a frame waits out whatever time its drawing left over, and a busy room's frames take longer.
Forty objects, thirty-two bytes each
The object table is exactly $5C08 to $6108, and $6108 is the first byte of the font. The loop at ret_from_tbl_jp detects the end of the table by comparing IX with the address of the font rather than with a count, so the two are the same number by construction.
Every room is one of three sizes
A room record does not carry its dimensions; it carries an index into the three-entry table at room_size_tbl. The castle is large but it is built from three box sizes.
Facing west is facing east, reversed
Among the tables the game builds at start-up (build_lookup_tbls) is the bit-reversal of every byte value, at reverse_bits_tbl. Mirroring a sprite horizontally is then a table lookup per byte, which is how the game gets a knight walking west out of the artwork for one walking east without storing it twice.