A door in Atic Atac is one sixteen-byte record whose two eight-byte halves stand in the two rooms it joins. Its type says what kind of door it is -- plain, locked, timed, a trapdoor, a secret passage for one character, the way out -- and picks the handler that the room's list dispatches it to on every pass. This page reads the kinds from the game, then follows the doors that change: the timed doors, chosen at random when a game starts and opening and shutting by themselves; the locked doors and their keys; the characters' doors; and the trapdoors. Everything was run through the game's own code in SkoolKit's simulator, from the first game after loading.

The record

OffsetIn a door's half
+$00type: which kind of door (and, less $A1, which graphic draws it)
+$01the room this half stands in
+$02where the player arrives coming through the other way: the low nibble twice is added to this half's x, the high nibble twice taken from its y
+$03, +$04x and y of the door's bottom-left corner
+$05bits 7-5 the drawing mode, which is also the wall (0 north, 4 south, 3 east, 7 west); bit 3 shut; bit 2 solid; bits 1-0 how it meets the screen
+$06, +$07its walk box (see movement)

The two halves are eight bytes apart, so DOOR_OTHER_SIDE finds the far side by flipping bit 3 of the address, and OPEN_DOOR and SHUT_DOOR always set or clear bit 3 of +$05 on both, so the two sides never disagree. Furniture uses the same record, though nothing passes between its halves (149 halves of the template have types that are not doors).

The kinds

A room record of type t is dispatched from entry $A2 + t of ACTOR_HANDLERS (DISPATCH_FROM_LIST starts its lookup at $802A). The door types, their handlers read from that table, and how many record halves of each there are in the template and in the first game once START_GAME has run:

TypeWhatHandlerHalves in the templateIn the first game
$01cave door, always openDOOR8644
$02door, always openDOOR16470
$03the big door frame at a staircase's top; a wider doorwayBIG_DOOR66
$08red doorDOOR_LOCKED_A1818
$09green doorDOOR_LOCKED_A2222
$0Acyan doorDOOR_LOCKED_A2020
$0Byellow doorDOOR_LOCKED_A2222
$0Cred cave doorDOOR_LOCKED_B66
$0Dgreen cave doorDOOR_LOCKED_B66
$0Ecyan cave doorDOOR_LOCKED_B44
$10grandfather clock: a door for the knight onlyDOOR_KNIGHT1212
$17bookcase: for the wizard onlyDOOR_WIZARD1010
$19trapdoor, openTRAPDOOR1111
$1Abarrel: for the serf onlyDOOR_SERF1010
$20timed door, shutTIMED_DOOR_SHUT094
$22timed cave door, shutTIMED_DOOR_SHUT042
$24the A.C.G. doorACG_DOOR22

Types $18 and $20-$23 are not in the template: the game makes them as it runs.

Walking through

DOOR asks PLAYER_AT_DOOR whether the player is in the door's box -- 17 pixels each way, halved across the door -- and, if he is, calls ENTER_ROOM: to the other half; its room becomes the player's; its +$02 gives where he appears; SET_ARRIVAL_HEADING gives him a heading of 32 away from the arrival door's wall, from ARRIVAL_HEADINGS:

Arrival door's wallHeading (x, y)He walks
north (mode 0)0, 32down
east (mode 3)-32, 0left
south (mode 4)0, -32up
west (mode 7)32, 0right

and the low nibble of his +$02 is set to 15. Then the room is drawn (see drawing). While the nibble counts down, once a frame, the controls are ignored and he walks in on his own, and no door can fire, so he cannot bounce straight back out; a run on the movement page shows the fifteen frames. Creatures in the old room stay there; a weapon in flight vanishes, since it is no longer in the player's room.

Timed doors

Once, at the start of a game, CHOOSE_TIMED_DOORS walks every door record. Where both halves are a plain door ($02) or a plain cave door ($01), it reads a byte from a pointer made of TICKS' low byte and FRAMES' low four bits with bit 4 set -- an address in the ROM, $1000-$1FFF, stepped on a byte per record -- and if the byte is below $70, makes both halves a shut timed door ($20) or timed cave door ($22). The ROM is the random numbers. In the first game after loading TICKS is 0 and FRAMES' low byte $6F when it runs, so it reads from $1F00 on, and it made 47 of the 78 plain doors and 21 of the 42 plain cave doors timed (68 doors in all, counted pair by pair from the records before and after). The same doors, every first game: FRAMES is frozen on the title screen. Room $00 has 2 of them.

A timed door's handler (TIMED_DOOR_SHUT or TIMED_DOOR_OPEN) does nothing on odd passes; on even passes it counts down one shared counter, DOOR_WAIT. The door that finds it at zero calls TOGGLE_TIMED_DOOR: the counter back to 94, the picture XORed off, bit 0 of the type flipped on both halves ($20 and $21, $22 and $23), the door opened or shut to match, the new picture drawn, and a rasp. Only the doors in the player's room are dispatched, so only they count down. Followed in room $00, the knight standing still for 700 passes:

PassWallBecameTICKS
64southtimed door, shut191
158westtimed door, shut285
254southtimed door, open381
348westtimed door, open475
444southtimed door, shut571
538westtimed door, shut665
634southtimed door, open761

The toggles came 94 – 96 passes apart: with 2 timed doors in the room both count the one counter down on every even pass, so it empties twice as fast as it would for one, and they take turns. The south door open and shut:

Room $00's south door, open and shut.

Room $00's south door, open and shut.

An open timed door will not shut while the player is in a doorway: at zero, TIMED_DOOR_OPEN looks at IN_DOORWAY and just acts as a door. Staged: the knight put in room $00's south doorway, at y $A8, below the rectangle. The counter reached zero and stayed there for 20 passes with the door still open (IN_DOORWAY 1); put back in the middle of the room, the door shut 1 pass later.

Locked doors and the keys

Types $08-$0B are locked doors and $0C-$0F locked cave doors, their colour the low two bits of the type through DOOR_COLOURS. All four colours are one graphic with four colour tables; drawn by the game, the doors then the cave doors:

Types $08-$0F, drawn by the game's drawers.

Types $08-$0F, drawn by the game's drawers.

Low bitsTypesColourDoor halvesCave door halves
0$08, $0C$42red186
1$09, $0D$44green226
2$0A, $0E$45cyan204
3$0B, $0F$46yellow220

On every pass DOOR_NEEDS_KEY asks FIND_CARRIED for sprite $81 (a key) in the door's colour among the three carried things. Not carried: SHUT_DOOR, and the walk test ignores its doorway. Carried: OPEN_DOOR, and PLAYER_AT_DOOR as for any door; going through, DOOR_LOCKED_A sets both halves to $02 (DOOR_LOCKED_B to $01) -- a plain door, open to anyone, for the rest of the game -- and the key is kept. Room $00's cyan door, with its key given and taken away (staged in the inventory):

The door
At the start, no keyshut
The cyan key written into slot 1, two passes lateropen, both halves: the same
The slot emptied again, two passes latershut

The movement page shows the knight going through with the key: the door becomes type $02 on both halves. Where the keys are is chosen by PLACE_KEYS at the start of a game, before the template is copied, from three tables of eight rooms; TICKS is always 0 at that moment, so the green and red keys use the same index and move together. The yellow key never moves. The first game's rooms in bold:

KeyTableIndexCandidatesFirst game
greenRANDOM_ROOMS_ONEFRAMES AND 7$05, $06, $07, $6D, $25, $24, $23, $22room $22
red (and the mummy)RANDOM_ROOMS_TWO(FRAMES + TICKS) AND 7$17, $13, $09, $0D, $89, $87, $80, $85room $85
cyanRANDOM_ROOMS_THREE(FRAMES' high byte + TICKS' high byte) AND 7$53, $8F, $41, $94, $33, $91, $39, $4Croom $91
yellow(the template)never movedroom $66

Doors that know who you are

DOOR_KNIGHT, DOOR_WIZARD and DOOR_SERF -- a grandfather clock, a bookcase and a barrel -- take the character's first sprite code from the player's and ask whether what is left is under 16. The right character gets OPEN_DOOR and an ordinary doorway; anyone else gets SHUT_DOOR and a picture. Each character was staged in room $09 below its clock (at $30, $27, in the north wall, leading to room $17) and walked up into it:

CharacterWhat happened
knightthrough, into room $17, after 24 frames
wizardstopped at y $31 under it, the clock shut (bit 3 of its +$05 set)
serfstopped at y $31 under it, the clock shut (bit 3 of its +$05 set)

Trapdoors

All 11 trapdoors start open, type $19. The open trapdoor's handler (TRAPDOOR) jumps to TRAPDOOR_FALL unless the byte RUNNING_SUM is zero, when it closes instead (the tail of TRAPDOOR_CLOSED, which flips the type to $18). A closed one opens again whenever TICKS' low byte is zero, every 256 passes. RUNNING_SUM is a running sum of FRAMES and TICKS that MAIN_LOOP keeps at the end of every pass. The trapdoor in room $03, watched for 60 seconds with the knight standing well away from it: it closed 8 times and was open 48% of the time, each time opening on the pass after TICKS' low byte came round to zero:

WhenIt becameTICKS
5.6 sclosed$0165
9.5 sopen$0201
10.1 sclosed$021B
15.7 sopen$0301
24.8 sclosed$0471
28.3 sopen$0501
31.3 sclosed$057A
34.6 sopen$0601
36.3 sclosed$0646
40.8 sopen$0701
43.0 sclosed$0756
47.2 sopen$0801
49.2 sclosed$0856
53.4 sopen$0901
57.9 sclosed$09BA
59.7 sopen$0A01

TRAPDOOR_FALL asks PLAYER_AT_DOOR with a box of 24 by 12. On it, the play area is cleared and drawn as entry $96 of the room table -- twelve nested rectangles -- and for 128 frames a falling tone plays while the middle four attribute cells are white one frame in eight and black otherwise, spread outwards by the spiral fill; then ENTER_ROOM, through the trapdoor's other half, which is the landing place in the room below. Staged: the knight put in room $03 above the trapdoor and walked down onto it (12 frames). The fall took 161 frames -- 128 steps, each waiting for FRAMES to move, and some of the tone's beeps outlast a frame -- and he landed in room $65 at ($40, $6E) (the trapdoor's other half says room $65).

The rectangles are drawn in the fall's own colour, black on black, so they are seen only where the attributes are white: each step the spiral fill copies every cell's colour from the one inside it, so the white pulses from the middle travel outwards a ring of cells a step, lighting whatever part of a rectangle lies in a white ring -- here the bottom and right-hand sides, where the lines and the cells line up. The pixels on their own, then the play area as shown at the start of the fall, after 8, 64 and 120 steps, and in the room below; then eight steps in a row, 40 to 47:

The fall's pixels, ink only.

The fall's pixels, ink only.

The fall.

The fall.

Steps 40-47.

Steps 40-47.

What is confirmed and what is inferred

Inferred, or not tried: