automap.fasttravel¶
Where each title keeps the bytes a fast travel writes.
automap/actions.py carries the behaviour; this carries the numbers. They are
separated because the numbers are the part that is per title, and every one of
them was a Pool of Radiance constant until
#19 (Can Curse be fast-travelled at all, or is the mechanism Pool of
Radiance's alone?) read the other two titles’ overlays.
Nothing here is Pool of Radiance’s address with an offset applied. Each
field’s comment says which instruction it was read out of, and
tests/test_newecl.py re-derives the lot off the player’s own disks by
tools/newecl.py’s procedure – find the script VM by its self-modifying
dispatch, take entry $20 of the tables it builds, read the routine. A
constant written down is a claim nothing checks.
A title with no row is offered no fast travel at all. addresses_for
answers None, FastTravel.legality refuses, and automap.actions.area_rows
hands back nothing – because the failure of a wrong address here is not an
error message, it is a JMP into somebody else’s code and a byte written into
whatever the running title keeps at another title’s number. Three of the six
C64 Gold Box titles have been read; the Krynn pair and Gateway have not.
## What differs, and what does not
Pool of Radiance |
Curse |
Silver Blades |
|
|---|---|---|---|
|
|
|
|
tail, where a trip enters |
|
|
|
cache slot |
|
|
|
disk byte |
|
|
|
came-from |
|
|
|
scratch wipe |
`$4A00`+32 |
`$4C00`+32 |
`$4C00`+32 |
also zeroed |
– |
– |
`$4BFB` |
indoors flag |
|
|
|
wall slot pinned |
`$49E7`+3 |
`$4BE7`+3 |
`$4BE7`+3 |
resident |
|
none |
none |
travel-grid square |
|
none |
none |
live triple |
|
|
|
The live triple does not relocate in any of them, which is the whole reason a square can be written before the jump and flushed by the handler itself.
Module Attributes
x, y, facing. |
|
`LDX #$1F . |
|
How many wall pieces |
|
Pool of Radiance. |
|
Curse of the Azure Bonds. |
|
Secret of the Silver Blades. |
|
Every title whose overlays have been read, by |
Functions
|
This title's fast-travel addresses, or None if nobody has read it. |
|
Can a fast travel be made in this title at all? |
Classes
One title's fast-travel addresses, each read out of that title's own overlays. |
- automap.fasttravel.ADDRESSES: Mapping[str, automap.fasttravel.FastTravelAddresses] = mappingproxy({'pool-of-radiance': FastTravelAddresses(key='pool-of-radiance', title='Pool of Radiance', handler=8209, tail=8244, slot=28187, disk=28178, came_from=18930, scratch=18944, indoors=18918, key_wait=(4290, 4332), key_fetch=(11854, 11883), wall_slot_pinned=18919, wall_slot_pinned_len=3, walls_slot=28188, travel_square=18883, zeroed=(), live_square=49227, scratch_len=32), 'curse-of-the-azure-bonds': FastTravelAddresses(key='curse-of-the-azure-bonds', title='Curse of the Azure Bonds', handler=8634, tail=8669, slot=32539, disk=32530, came_from=19442, scratch=19456, indoors=19430, key_wait=(4125, 4182), key_fetch=(12247, 12280), wall_slot_pinned=19431, wall_slot_pinned_len=3, walls_slot=None, travel_square=None, zeroed=(), live_square=49227, scratch_len=32), 'secret-of-the-silver-blades': FastTravelAddresses(key='secret-of-the-silver-blades', title='Secret of the Silver Blades', handler=8422, tail=8460, slot=32539, disk=32530, came_from=19442, scratch=19456, indoors=19430, key_wait=(4176, 4233), key_fetch=(16641, 16674), wall_slot_pinned=19431, wall_slot_pinned_len=3, walls_slot=None, travel_square=None, zeroed=(19451,), live_square=49227, scratch_len=32)})¶
Every title whose overlays have been read, by
Game.key. Champions of Krynn, Death Knights of Krynn and Gateway to the Savage Frontier are absent rather than empty: nobody has looked, and an entry with Pool of Radiance’s numbers in it would fast-travel a party by writing into whatever those titles keep at them.
- automap.fasttravel.CURSE_OF_THE_AZURE_BONDS = FastTravelAddresses(key='curse-of-the-azure-bonds', title='Curse of the Azure Bonds', handler=8634, tail=8669, slot=32539, disk=32530, came_from=19442, scratch=19456, indoors=19430, key_wait=(4125, 4182), key_fetch=(12247, 12280), wall_slot_pinned=19431, wall_slot_pinned_len=3, walls_slot=None, travel_square=None, zeroed=(), live_square=49227, scratch_len=32)¶
Curse of the Azure Bonds.
DUNGEON $21BA, instruction for instruction Pool of Radiance’s handler with three relocations: the cache slot by the loader’s page (+$1100) and the two save-relative writes bysave_load_address(+$0200). CONFIRMED from the bytecode, and CONFIRMED again in the running machine – four driven warps, each with an exact-byte map match at$0400and the arriving script’s own text on screen (#19).No `walls_slot` and no `travel_square`. Curse’s disks carry no
WALLSfile, onlyWALLDEF01-WALLDEF12and their `WALLSET`s, so slot 9 of the cache holds something nobody has read; and it has no square-engine overland.
- class automap.fasttravel.FastTravelAddresses[source]¶
Bases:
objectOne title’s fast-travel addresses, each read out of that title’s own overlays.
Five fields are
NEWECL’s own writes and its entry point; the rest are what a trip has to set up around them, and the three that can be None are the ones a later title turned out not to have.- __init__(key, title, handler, tail, slot, disk, came_from, scratch, indoors, key_wait, key_fetch, wall_slot_pinned=None, wall_slot_pinned_len=3, walls_slot=None, travel_square=None, zeroed=(), live_square=49227, scratch_len=32)¶
- disk: int¶
Which side of the game disks the arriving area lives on. The loader prompts when that side is not in the drive.
- handler: int¶
The
NEWECLhandler’s head. Nothing jumps here – a trip has no script stream for the operand fetch – and it is carried so the tail can be shown to belong to it.
- key: str¶
goldbox.games.Game.key, so a row cannot be matched to the wrong title by a display string.
- key_fetch: tuple[int, int]¶
The key fetcher that loop calls, in
LIBRARY,[start, end). Safe for the same reason: it is called from the loop.
- key_wait: tuple[int, int]¶
The key-wait loop
DUNGEONidles in,[start, end). The one place it is safe to take the program counter from.
- slot: int¶
The
ECLslot of the loaded-files cache. Bit 7 means “reload me”, and the low seven bits are the area id.
- tail: int¶
the address the handler’s own
CMP #$FF / BEQbranches to, past the operand fetch.- Type:
Where a trip enters instead
- title: str¶
goldbox.games.Game.title, which is howautomap.actions.area_rowsandgoldbox/areas.pyspell a title. Kept here so the lookup takes either.
- travel_square: int | None = None¶
The travel grid’s own square, window-local x then y. Pool of Radiance only: no other C64 title in the family has a square-engine overland (
goldbox.games.Game.travel_grid).
- wall_slot_pinned: int | None = None¶
One flag per wall piece. Non-zero means “keep whatever screen codes are already there”, so a piece can hold the previous area’s wall art (
#179). Zeroed by a trip because the departing scripts that clear it are what a trip skips.
- automap.fasttravel.LIVE_SQUARE = 49227¶
x, y, facing. Unrelocated in all three titles read, which is not an assumption –
NEWECL’s own tail call isLDA <indoors> / BEQ / LDX #$02 / LDA $C04B,X / STA <save position>,X, and the$C04Bin it is the same three bytes in Pool of Radiance’sDUNGEON $1A3C, Curse’s$1BE7and Silver Blades’$1AF9. CONFIRMED from the instructions, and measured in a running Curse (#19).- Type:
Where the engine keeps the party’s square while the game runs
- automap.fasttravel.POOL_OF_RADIANCE = FastTravelAddresses(key='pool-of-radiance', title='Pool of Radiance', handler=8209, tail=8244, slot=28187, disk=28178, came_from=18930, scratch=18944, indoors=18918, key_wait=(4290, 4332), key_fetch=(11854, 11883), wall_slot_pinned=18919, wall_slot_pinned_len=3, walls_slot=28188, travel_square=18883, zeroed=(), live_square=49227, scratch_len=32)¶
Pool of Radiance.
DUNGEON $2011, and the five addresses in it are the onesdocs/118-debug-mode.md§3 has measured since P15 – this row is a restatement of whatautomap/actions.pyalready shipped, not a new reading, andtests/test_fasttravel_addresses.pychecks the two against each other.KEY_WAITandKEY_FETCHwere measured from 400 program-counter samples of an idle party and then reproduced from the bytes.
- automap.fasttravel.SCRATCH_LEN = 32¶
LDX #$1F ... DEX / BPL, the same count in all three.- Type:
How many bytes
NEWECLwipes atscratch
- automap.fasttravel.SECRET_OF_THE_SILVER_BLADES = FastTravelAddresses(key='secret-of-the-silver-blades', title='Secret of the Silver Blades', handler=8422, tail=8460, slot=32539, disk=32530, came_from=19442, scratch=19456, indoors=19430, key_wait=(4176, 4233), key_fetch=(16641, 16674), wall_slot_pinned=19431, wall_slot_pinned_len=3, walls_slot=None, travel_square=None, zeroed=(19451,), live_square=49227, scratch_len=32)¶
Secret of the Silver Blades.
DUNGEON $20E6, and six writes rather than five:LDX #$1F / LDA #$00 / STA $4BFB / STA $4C00,X / DEX / BPL $2106. The back edge is theSTA $4C00,X, so$4BFBis written once and the wipe 32 times.$4BFBis the flag that suppresses the party’s coordinates on the status line –DUNGEON $0A0EisLDA $4BFB / BNEover the block that loads the square for printing (docs/138-multiple-games.md§8). Zeroing it is what the handler does, so a trip does it too.PROBABLE rather than CONFIRMED: every address is read off Silver Blades’ own overlays and the handler is the same routine, and no Silver Blades party has been fast-travelled through this code.
tools/ssbwarp.pyis the driver that would settle it.
- automap.fasttravel.WALL_SLOT_PINNED_LEN = 3¶
How many wall pieces
wall_slot_pinnedcovers.DUNGEONunpacks three –$ED50,$F05Cand$F368in Pool of Radiance.
- automap.fasttravel.addresses_for(game=None)[source]¶
This title’s fast-travel addresses, or None if nobody has read it.
Takes whatever the caller is holding: a
goldbox.games.Game, aGame.key, aGame.title– which is howgoldbox/areas.pyspells a title – or None, which means Pool of Radiance the way it does everywhere else in the program.None is a refusal and never a default. A caller that falls back to Pool of Radiance’s row for a title that has no row writes Pool of Radiance’s numbers into another game, which is the corruption
#14fixed for the area list and the same one address by address.- Return type: