The drop timer reads one record eighteen times
Things fall from the sky once DROP_TIMER has run out (
SKY_DROP), and
RESET_DROP_TIMER sets it at the start of every room and after every
drop. By its shape it counts the quest items still to do -- a loop over the
eighteen quest records at
QUEST_RECORDS, adding one for each graphic of 112 to 115
-- and sets four turns for each, plus eight: 8 to 24 turns. But the loop
loads DE with the record length and never adds it to HL. It tests the first
record eighteen times, so the timer is 80 turns while the first quest item
(the one in room 122) is still to do, and 8 once it is done, whatever the
other three are.
Watched in the emulator, reading DROP_TIMER at the first turn in room 30:
80 with the quest items as a new game has them; 8 with only the first staged
as done; 80 with the other three done and not the first; 8 with all four.
Meant: 24, 20, 12 and 8.
Nothing done is 56 per cent
PERCENTAGE adds up the game-over score -- half the rooms seen, at
most 54, 4 for each quest item done and 6 for each collectable placed -- and
counts it into BCD one at a time with ADD A,1 / DAA in a DJNZ loop. With a
sum of 0 the loop runs 256 times, not none, and 256 in BCD with the hundreds
dropped is 56. The sum is 0 whenever a game ends before a second room has
been seen, since one room halves to nothing. So a player who loses every
life in the room he started in is told he has done more than half the quest.
The picture was drawn by the game's own code in SkoolKit's simulator: a first
game started from the menu, the lives taken away at its first turn and the
player killed; the game's own death, game over and percentage followed, and
printed 56. Watched in the emulator too: the same in the start
room gave 56, and with one more room seen, 01.
Steering by direction was left half done
Knight Lore let a joystick steer by direction -- push the way you want him to
face -- and Pentagram keeps the code:
HANDLE_LEFT_RIGHT has a directional branch, and
CHK_PICKUP_DROP takes the pick-up control from bit 5 of INPUT instead of bit 4 in that
mode, which bit 3 of CONTROL selects. No menu choice sets bit 3, so it is
reachable only by a poke, and there it is broken three ways.
READ_CONTROLS never
sets bit 5, so nothing can be picked up or put down. The directional code
takes bit 4 as down, but every joystick reads down as jump (bit 3) and bit 4
is the pick-up keys, so the stick can never turn him to face down and left,
and the pick-up keys turn him instead. And
FLASH_MENU would flash the start line
of the menu, where Knight Lore had a line for this mode (read, not seen).
Watched in the emulator, with the cursor joystick chosen and a bucket staged
beside him in room 30: the bottom-row key picked it up with CONTROL 4; with
CONTROL 12 (bit 3 set) the bucket stayed where it was and he turned from
facing 0 to facing 3; the stick's down (6) left him facing 0.
Too much in room 87 crashes the game
LIST_DRAWN writes the number of every record to be drawn into
DRAW_LIST, and an $FF after the last. The list has 48
bytes -- Knight Lore's size, for its forty records -- but Pentagram has 54,
and nothing checks: a list of more than 47 records runs over the first
bytes of
SORT_AND_DRAW, which follows it. On the first turn in a room
everything is listed, because everything has to be drawn. Counted from the
room data the way
BUILD_ROOM reads it, the fullest rooms fill, of their 48
records: 87 (43), 80 (42), 2 (40), 59 (40), 93 (39). Room 87 lists 45 with the player's legs and
body, 5 records to spare -- and the quest things he carries fill
records too when they are put down there. Three of them are enough.
With 48 listed, the $FF lands on the XOR A that begins SORT_AND_DRAW
and stays there. $FF is RST $38: every turn from then on starts drawing with a
call to the ROM's interrupt routine, which ends with EI, so interrupts come on
in a game written to run without them. The next frame interrupt lands while
the sprite drawer (
DRAW_OBJECT) is reading a sprite with the stack pointer
pointed into it, and the return address pushed there overwrites the sprite;
the game goes wrong from there. With more listed, record numbers overwrite the code as well.
Watched in the emulator, with collectables' quest records staged into room
87 (as if carried there and put down) and the room entered by the
game's own restart: with two, 47 were listed and 300 turns went by
normally; with three, 48 were listed, SORT_AND_DRAW began with $FF,
the ROM's interrupt routine ran over and over during the first turn --
called at first from $B58B, then from all over the drawing code with the
stack inside sprite data. In that run the game never finished its first turn
in the room; in another, stopped differently, it went four turns with its
variables overwritten -- the turn counter and the room number read as
nonsense -- and hung. With five, 50 were listed and it ran six turns before
it hung.
The well counts one bolt's puff and not the other's
BOLT_TOUCHING asks whether either of his two bolts is touching the
well, with HIT_TEST, which ignores a puff -- graphics 64 to 71, what a
bolt turns into when it hits something. The second bolt goes in at the top,
but the first goes in at HIT_TEST_Z, past the test for a puff. So a bolt fired
from the first record counts for the turn it touches the well and then for
each of the eight turns its puff hangs there; one from the second record
counts once.
WELL wants 32.
Watched in the emulator, logging the well's count at the instruction that
adds to it, in room 71 with a shot every five turns: 32 counts in 35 turns,
from 7 shots, all but a few of them made by the first record's bolt or its
puff. With the first record sent through the puff test like the second
(below), the same shooting took 32 shots and 160 turns, one count a shot.
Which of the two was meant is not known; the difference is only that they are
not the same.
POKE 53182,22
A stray OUT pages a 128K's memory
READ_KEYS does OUT ($FD),A before IN A,($FE), with the half-rows to
read in A. OUT (n),A puts A on the top half of the address bus, so the port
is A * 256 + $FD. A 48K ignores it. A 128K takes any port with bits 15 and 1
clear as its paging port, $7FFD: every half-row value with bit 7 clear is a
paging write. The menu tune's "any key" test (
PLAY_TUNE_ONCE) reads with A = 0,
which pages the 128's editor ROM in and does no harm, since the game never
calls the ROM. But in play the keyboard reads SPACE's half-row as $7F, and
the pause test (
PAUSE) reads $7E every turn, whatever the controls: that
puts RAM bank 7 or 6 at $C000, where most of the code and the screen buffer
are, and locks the paging. The game cannot survive its first turn.
Watched in the emulator's 128K, from the loaded game put into a 128K snapshot
with the 48 BASIC ROM and bank 0 paged in, as the 128's Tape Loader leaves
it: the menu worked, with ROM 0 paged in by the tune's key test; at the first
turn of play READ_KEYS was called with $7F from the keyboard reader, the
paging port became $7F, locked, and the machine ended in the ROM, reset.
So on a 128K the game must be run in 48 mode. Knight Lore's
keyboard routine has the same OUT,
and its callers pass $7E too; it was not tried on a 128K. The poke that
removes the OUT is with the pokes.
The last quest item's link is bent
When the fourth quest item is done,
ALL_FOUR_DONE walks the quest
records setting bit 4 of each pentagram piece's flags, and with each piece it
also sets bit 0 of +$11 of IX -- the quest item that called it. +$10 and +$11
are that object's link to its own quest record, so the link now points 256
bytes further on. Nothing follows a quest item's link -- only a carried
thing's link is used, by
TAKE_OR_LEAVE, and a quest item cannot be carried -- and
the record is matched by graphic when he leaves the room (
QUEST_OUT_OF_ROOM), so it
does no harm. What the SET was meant to do is not known.
Watched in the emulator, with three quest items staged as done and the
bucket staged as carried into room 122 and put down: it rose, flew to the
stone and was used up in 58 turns; the stone became graphic 116, QUEST_DONE
went to 4, the lives from 4 to 5, PENTAGRAM_ON was set, and the stone's link
went from $D432 to $D532.
Lives are added without BCD
LIVES is printed as two BCD digits (
DRAW_LIVES) and a quest item adds one with a
plain INC (
QUEST_ITEM);
RESTART_PLAYER takes one with a plain DEC. Past 9 the INC would
make $0A, not $10. It never happens: a game starts with five, the first
life takes one, and there are only four quest items, so a player who never
dies ends with eight. Read from the code; the one extra life was watched (see
the bug above).