The Hobbit has no sprites and no frame loop: what moves on its screen is a picture being drawn, the story being typed out and scrolled, and the odd change of border. The pictures being drawn are on the Locations page (each picture's own entry in the listing shows its GIF), and the Patches page has the original and the fast-drawing patch side by side. This page has the rest, and one animation the screen never shows: the other characters walking about the map while the player waits.

Every animation here is a game played in SkoolKit's simulator when these pages were built, from the snapshot the tape leaves (the same start as scripts/hobbit_drive.py): commands typed a key at a time, held a tenth of a second with a gap of 0.06s, as the build's own playthrough types them, so the game echoes them; each key wait answered as soon as the game reaches it; and the screen memory and the border read at every frame interrupt, 69888 T-states apart. Each frame of a GIF lasts as long as the game took, at 3.5MHz and without the memory contention of a real Spectrum, which would be a little slower; where the game waits for a key for as long as it takes, the GIF holds the picture for a second or two and the table under it says so. Identical pictures in a row are one frame. The tables say what the game was doing, from where the program counter was at each frame interrupt.

The opening

The title screen, then Bag End drawn and described

The title is the loading screen, still on the screen from the tape: the game draws nothing of it. START keeps a copy of the objects, rooms and variables for new games, and NEW_GAME makes the border black and waits at $6C6D for a key -- N held there turns the pictures off for the game. Nothing on the title moves: no attribute has FLASH set and the only code running is the key test. Then CLEAR_SCREEN, the wavy DIVIDER across row 18, "> LOOK" printed in the input window, and the first turn is that LOOK (DO_LOOK, DESCRIBE_ROOM, DESCRIBE_LOCATION): the room's description goes into the story window first, then the picture is drawn -- 6.7s of it here -- then the game waits for a key, and only then does the rest of the description come, scrolling the story up over the picture. The whole opening, from the key to the first prompt, took 8.4s.

FromForWhat the game was doing
0.00s0.00sthe title screen, waiting for a key (START) -- shown for 3.0s here; the game waits for as long as it takes
0.00s0.22sthe turn: the parser, the characters, the story composed and printed
0.22s6.75sdrawing the picture (DRAW_LOCATION_PICTURE)
6.97s0.02sthe picture finished, waiting for a key (WAIT_FOR_ANY_KEY) -- shown for 1.5s here; the game waits for as long as it takes
6.99s1.42sthe turn: the parser, the characters, the story composed and printed
8.41sthe next prompt: the end

The story being told

The story of arriving in the trolls' clearing, told over its picture

The turn that walks into the trolls' clearing, from its picture finished and waiting for a key to the next prompt. Everything the story says goes through PRINT_CHAR to STORY_CHAR, a character at a time in the game's own six-pixel font (NARROW_CHAR), always on row 17; at the end of each line SCROLL_STORY moves rows 0-17 up by one, picture and all, which is how the story climbs over the picture. The input window below the divider is the ROM's font (INPUT_CHAR) and does not move. $6D13 (MAIN_LOOP) lets a turn's first nine lines out at once (NO_PAUSE_LINES, $B716); after that each line waits about half a second, or until a key, before it scrolls.

Measured in the same game played again and stopped at every character printed past PRINT_GATE: 297 characters and 11 line ends in the story window this turn, one every 4543 T-states (1.3ms) while a line is going out -- up to 15 in one fiftieth of a second, so on the screen a line appears in two or three frames rather than a letter at a time. 6 lines waited, to the nearest frame, 0.60s, 0.58s, 0.58s, 0.58s, 0.58s, 0.60s (the loop at $86DF: 32768 polls of the keyboard at 62 T-states, 0.58s). Below, 3 of those lines at a fifth of the speed, a frame for every character, 14.4s where the game took 2.88s:

Lines of the story a letter at a time, slowed

FromForWhat the game was doing
0.00s0.02sthe picture finished, waiting for a key (WAIT_FOR_ANY_KEY) -- shown for 1.5s here; the game waits for as long as it takes
0.02s0.76sthe turn: the parser, the characters, the story composed and printed
0.78s0.60sthe pause at the end of a story line (STORY_CHAR)
1.38s0.12sthe turn: the parser, the characters, the story composed and printed
1.50s0.58sthe pause at the end of a story line (STORY_CHAR)
2.08s0.20sthe turn: the parser, the characters, the story composed and printed
2.28s0.58sthe pause at the end of a story line (STORY_CHAR)
2.86s0.12sthe turn: the parser, the characters, the story composed and printed
2.98s0.58sthe pause at the end of a story line (STORY_CHAR)
3.55s0.14sthe turn: the parser, the characters, the story composed and printed
3.69s0.58sthe pause at the end of a story line (STORY_CHAR)
4.27s0.12sthe turn: the parser, the characters, the story composed and printed
4.39s0.60sthe pause at the end of a story line (STORY_CHAR)
4.99s0.30sthe turn: the parser, the characters, the story composed and printed
5.29sthe next prompt: the end

The trolls' clearing: night into day

The trolls' clearing at night The trolls' clearing by day

The same picture, LOC5_TROLLS_CLEARING_PIC, drawn on the way in and after the dawn, each as it stood finished, waiting for a key, with the border it set. The picture is not redrawn at dawn and has no second version: its first two bytes, the border and the attribute CLEAR_CANVAS fills the canvas with before the stream runs, are 0 and $00 on the tape (black border, black paper, black ink) and TROLLS_TURN_TO_STONE writes 5 and $28 over them (cyan border, cyan paper, black ink); the rest of the stream is the same. Its lines are drawn in black, and PLOT_PIXEL flips a cell's ink to the opposite colour when it would be the same as the paper, so on the black canvas every line and fill comes out white, and on cyan black. NEW_GAME writes the night's zeros back at $6C2B, so every game starts in the dark. The bytes were read before and after the dawn turn in the game below.

Day dawns while the player waits in Rivendell; back in the clearing

One game: OPEN DOOR, EAST, EAST into the clearing (its story is the animation above), SOUTHEAST at once to Rivendell, then WAIT. The trolls' script counts the turns itself (TROLLS_EAT each turn, a pause when the player is not there), and the dawn came at the end of the fourth turn after the one that entered: the GIF starts with that WAIT being typed. The dawn's words are printed wherever the player is (the routine turns printing on first); both trolls were dead after it (flag bit 3 of their records). Then WEST, back into the clearing, and its picture drawn by day.

FromForWhat the game was doing
0.00s0.70styping WAIT and ENTER (READ_LINE)
0.70s1.11sthe turn: the parser, the characters, the story composed and printed
1.81s0.71styping WEST and ENTER (READ_LINE)
2.52s0.36sthe turn: the parser, the characters, the story composed and printed
2.88s3.12sdrawing the picture (DRAW_LOCATION_PICTURE)
5.99s0.02sthe picture finished, waiting for a key (WAIT_FOR_ANY_KEY) -- shown for 1.5s here; the game waits for as long as it takes
6.01s0.64sthe turn: the parser, the characters, the story composed and printed
6.65s0.58sthe pause at the end of a story line (STORY_CHAR)
7.23s0.51sthe turn: the parser, the characters, the story composed and printed
7.73sthe next prompt: the end

A fight: attacking Elrond

ATTACK ELROND, five turns

Nothing staged: a new game, OPEN DOOR, EAST, EAST, SOUTHEAST -- into Rivendell, which has no picture, so the story fills the screen -- and ATTACK ELROND typed 5 times, bare-handed. Elrond is on the player's side and the elves'; attacking him takes him off the player's (SAME_SIDE), and his script's reaction to ATTACK WITH is to hit back. Each blow is DO_ATTACK: the attacker's strength jostled by -10 to +10 (JOSTLE) against the target's defence jostled the same way; no stronger is wasted, more than 16 stronger kills, and anything between wounds, wearing the target's strength and defence down -- erratically, because the halving is a rotate (see Bugs). The same game played again with the same keys, stopped at $91C1 where the blow and guard are both in hand, gave:

TurnBlow byAtBlowGuardResultAfter the turn: youElrond
before64/6464/64
1youelrond6865a wound, margin 3 (odd)
elrondyou7272wasted64/6464/64
2youelrond6568wasted
elrondyou6667wasted64/6464/64
3youelrond6866a wound, margin 2
elrondyou6365wasted64/6462/64
4youelrond6464wasted
elrondyou6971wasted64/6462/64
5youelrond7268a wound, margin 4
elrondyou6068wasted64/6459/62

Strength/defence are bytes 5 and 6 of each record, read after each turn. Every change in them is what DO_ATTACK's arithmetic gives for the margins above (worked out from the instructions at $91D2-$91F8 and compared): a margin m is doubled, rotated right twice and taken off with CPL and ADD, written back only if that does not go below zero -- so an even m takes m/2 + 1 off the strength and, when m is a multiple of four, m/4 + 1 off the defence, while an odd m rotates its low bit into bit 7, over 128, which a strength or defence of 64 cannot pay, and does nothing at all. Neither side kills easily: 64 against 64, each give or take ten, needs the blow at the top of its range and the guard near the bottom of its to beat it by 17. Elrond struck back 5 times and 5 of them were wasted. The "day dawns" in the middle of the fight is the trolls' dawn (above): the walk to Rivendell went through their clearing.

The characters roaming

Where every character is, turn by turn

A new game left alone at its first prompt in Bag End for 50 turns -- the first LOOK, which the game types itself, is already a turn, so the others have moved once by then. Nobody types anything: after about 23 seconds with no key GET_KEY types WAIT itself and presses ENTER, so every turn here is the game's own WAIT (checked in INPUT_LINE after each), 24.3s of game time apiece, nearly all of it the patience running out. After each turn every character's place is read from byte 16 of its record (OBJECT_INDEX). The map is the Map page's layout, grey boxes the dark places; a line in a character's colour is the way it went that turn; a hollow disc is a character whose CHARACTERS slot is still empty -- not yet in the story, so it does nothing -- and a cross a dead one. The GIF shows 0.7s a turn.

What moves them is each character's own script (CHARACTERS_ACT; the Characters page has every script). In this run 10 of the 18 moved and 8 never did. Thorin's script follows the player, who only waited; the characters not yet in the story do nothing at all.

CharacterIn the storyTurns it moved inPlaces it was inAt the first promptWhere it ended
Yyouthe player01tunnel like hall (1)tunnel like hall (1)
Drred golden dragonnot yet01lower halls (41)lower halls (41)
g1nasty goblinyes333dark stuffy passage (15)large dry cave (14)
Ggandalfyes119tunnel like hall (1)goblins dungeon (13) (from turn 14)
Ththorinyes01tunnel like hall (1)tunnel like hall (1)
WEwood elfyes3310levelled elvish clearing (28)forest (3)
Elelrondyes01rivendell (9)rivendell (9)
Bubutlernot yet01elvenkings cellar (32)elvenkings cellar (32)
Wavicious wargyes359beorns house (22)dark dungeon (31) (from turn 36)
Gogollumyes504dark stuffy passage (55)dark stuffy passage (54)
g2hideous goblinyes503dark stuffy passage (59)dark stuffy passage (57)
Babardnot yet01lake town (35)lake town (35)
T1hideous trollyes01trolls clearing (5)trolls clearing (5)
T2vicious trollyes01trolls clearing (5)trolls clearing (5)
g3horrible goblinyes5014dark stuffy passage (63)dark stuffy passage (64)
g4mean goblinyes504dark stuffy passage (60)dark stuffy passage (60)
g5vicious goblinyes503dark stuffy passage (65)dark stuffy passage (63)
g6disgusting goblinyes5015dark winding passage (18)dark stuffy passage (59)

Between them, found by playing the same game twice more, stopped at every capture that was done for real (DO_CAPTURE past its test, at $A41D) and at every blow (DO_ATTACK at $91C1): 2 captures and 1 blow, none of them seen by the player. A capture puts the captive straight into the elves' dungeon or the goblins', with everything it carries, without its walking there.

The whole run, a column a turn; a bold number is a move that turn, and a place's name is in its tooltip.

01234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
Y you111111111111111111111111111111111111111111111111111
Dr red golden dragon414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141
g1 nasty goblin151415155215151415155215151414151552151514151552151514151552151514151552151514151552151514151552151514
G gandalf114599106810106810111413131313131313131313131313131313131313131313131313131313131313131313131313
Th thorin111111111111111111111111111111111111111111111111111
WE wood elf2825666666666666256625666666256666666666662566666666662524462422244623345845345845834534583
El elrond999999999999999999999999999999999999999999999999999
Bu butler323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232
Wa vicious warg221222244624462345345834534584584534584534534583234533313131313131313131313131313131
Go gollum556154175561541755615417556154175561541755615417556154175561541755615417556154175561541755615417556154
g2 hideous goblin595857585958575859585758595857585958575859585758595857585958575859585758595857585958575859585758595857
Ba bard353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535
T1 hideous troll555555555555555555555555555555555555555555555555555
T2 vicious troll555555555555555555555555555555555555555555555555555
g3 horrible goblin635859605660596056606154551755175560566056605958576557586261605660566059581558626154551755175554555464
g4 mean goblin606160566059605660566056605660566056605660566056605660616059605660566056605660566056605660596059605660
g5 vicious goblin656463646564636465646364656463646564636465646364656463646564636465646364656463646564636465646364656463
g6 disgusting goblin186364655716186364546454645464545517556056605958626154645464636454645455616059605960596059605960566059

Eaten, and starting again

WAIT in the trolls' clearing: eaten, and a new game

A new game, OPEN DOOR, EAST, EAST into the clearing, and WAIT there. On the next turn the troll's script finds the player where it is (TROLLS_EAT) and eats him: PLAYER_DIES prints the end and the score (SHOW_SCORE) and waits for a key. That key goes back into NEW_GAME, which puts back the objects, the rooms and the variables from the copies START made, and -- the key still being down when it reaches the title's key wait at $6C6D -- starts at once: the screen cleared, Bag End drawn again, the first LOOK. The title picture is not shown again; it is gone from the screen. And the troll that ate him, hideous troll, dies of it: DO_EAT adds ten to the eater's strength and kills it if that reaches 128, and a troll's is 160.

FromForWhat the game was doing
0.00s0.70styping WAIT and ENTER (READ_LINE)
0.70s1.24sthe turn: the parser, the characters, the story composed and printed
1.94s0.02swaiting for a key to start a new game (PLAYER_DIES) -- shown for 2.5s here; the game waits for as long as it takes
1.96s0.22sthe turn: the parser, the characters, the story composed and printed
2.18s6.75sdrawing the picture (DRAW_LOCATION_PICTURE)
8.93s0.02sthe picture finished, waiting for a key (WAIT_FOR_ANY_KEY) -- shown for 1.5s here; the game waits for as long as it takes
8.95s1.36sthe turn: the parser, the characters, the story composed and printed
10.30sthe next prompt: the end

Everything else that moves

Nobody at the keyboard: the game types WAIT itself

Nobody at the keyboard. The first turn of the run above: the prompt, 23.4s of nothing (shown here for 3s), then the game typing WAIT into the input window itself and the turn that follows. The time is $B714 (PATIENCE), 3000 scans of the keyboard at about 7.8ms each, set by READ_LINE for every line; see How it works.

FromForWhat the game was doing
0.00s23.40sat the prompt (READ_LINE) -- shown for 3.0s here
23.40s1.01sthe turn: the parser, the characters, the story composed and printed
24.41sthe next prompt: the end

ATTACK ELROND typed into the input window

Typing, and the cursor. The input window while ATTACK ELROND is typed, at the fight's first prompt (2.1s, at the typing speed above). READ_LINE echoes each key through INPUT_CHAR in the ROM's font, capitals always, and INPUT_CHAR draws the cursor after it: a +, which the game prints as an ordinary character and never flashes. ENTER blanks it, and the window scrolls up a line (SCROLL_INPUT) when a line fills or ends.

PAUSE: a green border until a key

PAUSE. DO_PAUSE makes the border green and waits for a key (NEW_KEYPRESS), then for the key to be let go, and makes it white again. Typed at the first prompt here; the key came 1.0s after the border went green. The game writes the border in six places, found by searching for OUT ($FE): black for the title (NEW_GAME), white in CLEAR_SCREEN, the picture's own colour in CLEAR_CANVAS, white again in WAIT_FOR_ANY_KEY once the key after a picture comes (the night clearing's black border turns white there, in the story animation above), and these two.

What does not move. SAVE and LOAD call the ROM's own tape routines, whose border stripes are the ROM's, not the game's. PRINT sends each story line to a ZX Printer from LINE_TO_PRINTER with nothing on the screen to show for it (and the simulator has no printer). No frame filmed for this page has an attribute with FLASH set, and no picture starts with one (the second byte of each of the 22 streams in PICTURE_TABLE).