![]() |
How the game is put together |
| Offset | Every record |
|---|---|
| +$00 | sprite, which also selects the handler |
| +$01 | room |
| +$02 | a flag |
| +$03, +$04 | x and y — the point its feet stand on, since sprites are drawn upwards |
| +$05 | drawing mode: top three bits pick the drawing routine, low two how the sprite combines with the screen, bit 6 a doorway's facing |
| Table | Record | What is in it | Dispatched |
|---|---|---|---|
| $EA90-$EAA7 | 8 bytes | the player, its weapon, the sound slot | once per frame, by FRAME_TICK |
| $EAA8-$EE5F | 8 bytes | objects, collectables, sounds | only if in the player's room |
| $EE60-$EEDF | 16 bytes | the monsters | every pass, wherever they are |
| $EEE0 up | 16 bytes, a half per room | doors and furniture | only through the player's room's list, from ROOM_LIST_PASS |
LD SP,$5E00 resets the stack on every pass, so no handler has to leave it as it found it — which is what lets a routine like LOSE_FOOD_SIXTEEN abandon its caller and jump straight to the death routine when the life force runs out. And the EI beside it is where the DI from the entry point is finally lifted; everything before this runs with interrupts off.
The dispatch itself is a jump, not a call. The loop puts the address to come back to in HL, and DISPATCH_ACTOR pushes it before jumping to the handler, so the handler's own RET lands back in the loop.
LD HL,ACTOR_HANDLERS LD C,(IX+$00) ; the sprite byte doubles as the type SLA C / RL B ; two bytes per entry ADD HL,BC LD A,(HL) / INC HL / LD H,(HL) / LD L,A JP $5CB0 ; the JP (HL) the tape poked into placeThat is why so many of the per-creature routines have nothing at all referencing them: they are reachable only through a table which, to a disassembler, is data.
| Sprite codes | Handler | What it is |
|---|---|---|
| $01-$10 | UPDATE_KNIGHT | the knight |
| $11-$20 | UPDATE_WIZARD | the wizard |
| $21-$30 | UPDATE_SERF | the serf |
| $4C-$4D | MOVE_ACTOR | the pumpkin |
| $5C-$5D | MOVE_ACTOR | the spider — the same routine, a different creature |
| $66, $67 | MATERIALISING, DYING | the player rising and sinking |
LD A,L / XOR $08 / LD L,A ; IX now points at the other halfNo pointer and no lookup. Each half carries the destination room and the position you arrive at going that way, so stepping through a door means reading the half you did not touch — which is the first thing ENTER_ROOM does. The open-or-shut state is bit 3 of the drawing mode, and OPEN_DOOR and SHUT_DOOR always set or clear it on both halves at once, so the two sides can never disagree. A locked door is not a separate kind of thing: it is an ordinary door whose handler shuts it again unless the player is carrying a key of the matching colour — and once the player has walked through it, the handler turns both halves into a plain door for good. The key is not used up.
LD A,($EA90) ; the player's sprite SUB $21 ; the serf's first code -- or $11, or 1 CP $10 ; sixteen codes per character JR NC,shutFail it and the door is still drawn; it simply does not open. The same three lines with a different constant give three different doors, which is the whole implementation.
DEC DE instead of INC DE, and every byte passes through REVERSE_BITS, which rolls its eight bits out of one register and into another in the opposite order. Turning a picture over costs one call before the loop starts, to move the data pointer to the last row. Turning it through a right angle (DRAW_TURNED_RIGHT and its three relatives) builds each screen byte a bit at a time from one bit column of eight successive rows.
Creatures do not use this: each heading of a character or creature is its own picture, and +$05 is simply its colour. It is the same economy as the room outlines, where three shapes share an edge list and differ only in their vertices.
| Where | What is written | Why |
|---|---|---|
| MARK_ROOM_VISITED | the operand of SET b,(HL) | the bit number is a room number, not known until it runs |
| DRAW_OUTLINE | the displacement in LD r,(IX+d) | the vertex index comes out of the room's edge list |
| BLIT_SPRITE | NOP, OR (HL), XOR (HL) or AND (HL) | how a sprite meets the screen, decided once instead of per byte |
| SETUP_SPRITE_DRAW | the displacement of a JR | picks how far down an unrolled chain of shifts to enter, which positions a sprite to the pixel |