Pobtastic / Location Graphics Decoding

Created Sun, 08 Feb 2026 12:00:00 +0000 Modified Wed, 11 Mar 2026 16:21:04 +0000

Introduction

When you move between locations in The Hobbit, the game sometimes draws a small picture for that place, most famously the “tunnel-like hall”:

The Tunnel-Like Hall From The Hobbit.
The First Location In The Hobbit, The Tunnel-Like Hall.

And when I say “it draws” - it really draws! These pictures aren’t stored as raw pixel data; instead they’re stored as a stream of drawing instructions which the game slowly interprets at run time, drawing it and filling it in before your very eyes!

For my The Hobbit disassembly I wanted that drawing data to appear in the HTML as readable instructions instead of a long run of anonymous DEFB bytes.

Bytecode Format

Each of these location graphics starts with two bytes: border colour and paper/ ink (the usual Spectrum attribute layout).

And after that comes a sequence of data:

Opcode Bytes Meaning
$00 1 Stop. End of this graphic.
$08 X Y 3 Move the “pen” to (X, Y).
$80$FF 2 Draw line; opcode and next byte.
$40$7F X Y 3 Fill at (X, Y) with colour in opcode.
$20$3F H L … 3+ Paint background.

So now we have: move to a position, draw lines (with a direction and “pixels, step every N pixels”), fill a point with a colour, paint attribute cells (with direction and step counts), and stop.

The draw-line instruction is the trickiest! The bottom three bits of the opcode tell us the direction-UP, RIGHT, DOWN, LEFT, or one of the four diagonals. Then a single data byte packs in both how many pixels to draw and how often to step sideways, so you can get thin lines or chunky ones. I didn’t want readers to have to decode “n” and “m” in their heads, so in the disassembly I spell it out: “Draw a line DOWN-RIGHT 08 pixels, step every 01 pixel(s).” which is much clearer.

Paint background works in the same way, with the “steps” idea: each step is a direction (up, right, down, or left) and a count. We’re targeting a position in the attribute buffer, the address comes from HL. The game stores it in big-endian here, which feels a bit odd when you’re used to Z80’s usual little-endian ways.

Block Boundaries

The graphic blocks are just laid out one after another in memory. If the parser isn’t careful it can run past the end of one block and start decoding into the next block’s header-and then sna2skool gets cross about overlapping directives.

I got around that by passing an end address into the parser. For each block we know where the next one starts (or where memory ends, for the last block). Before we gobble bytes for a multi-byte instruction we check we’re not stepping over that line. If we would, we emit a short “(incomplete)” line and stop there, so the next block’s header and bytes stay untouched.

Why is this even a thing? It’s because some graphics use the drawing code from the next graphic, and this facilitates how the game “shares” the code. Take a look at: Goblins Dungeon and Dark Dungeon

$E02C $E049
The Goblins Dungeon From The Hobbit.
Goblins Dungeon
The Dark Dungeon From The Hobbit.
Dark Dungeon

Generator And Control File

A little Python script, hobbit_gfx2skool.py, walks through the bytecode and spits out the matching instruction lines. It loads the snapshot (building it with tap2sna if it’s not there yet), loops over the known graphics block addresses, and for each block calls a parser that keeps an eye on the next block’s start. Everything goes into sources/hobbit/graphics.ctl, which then is merged into the main hobbit.ctl so when you run sna2skool, the location graphics show up as nice grouped, commented bytes instead of one long DEFB blob.

The same instruction set lives in Python in hobbit.py for the #DRAWING macro: when the HTML build hits a location graphic it runs that bytecode and turns the result into a PNG, so the disassembly can show the actual picture right next to the bytes.

I got the idea for doing this from Richard Carlsson’s GitHub rep, which is a very helpful resource for decoding/ displaying this graphic data.

Example

Here’s a short snippet of how the output looks in the skool file:

; Graphics at $CC43: Tunnel Like Hall
b $CC43 Graphics: Tunnel Like Hall
@ $CC43 label=Graphics_TunnelLikeHall
N $CC43 #DRAWING(#PC,scale=$02)(tunnel-like-hall.png)
N $CC43 Location #LOCATION$01: "#LOCATIONNAME$01".
  $CC43,$02 Border: #INK(#PEEK(#PC)). Colours: #COLOUR(#PEEK(#PC+$01)).
  $CC45,$03 Move to X: #N(#PEEK(#PC+$01))/ Y: #N(#PEEK(#PC+$02)).
  $CC48,$02 Draw a line UP-RIGHT 08 pixels, step every 01 pixel(s).
  $CC4A,$02 Draw a line UP-RIGHT 08 pixels, step every 01 pixel(s).
  ...
  $CC95,$01 Stop.

So you get a clear list of moves, draws, fills, and paint steps-and the #DRAWING macro drops the rendered graphic into the HTML. No more squinting at that stretch of bytes and wondering what it’s for!

The Hobbit (adventure-games disassembly)