THROW BALL was 63 starts and 63 blocked on v0.4.3 and SWITCH 15 of them, and the
reason is neither macro's own script. The screen reads FIGHT PKMN over ITEM RUN
and the game's index does not: wCurrentMenuItem is the row inside the column the
cursor is in, and selection adds two for the right column, so the order is
FIGHT, ITEM, PKMN, RUN. battle_entry had PKMN 1 and ITEM 2 -- the row-major
reading of the picture -- so every macro that meant to open the bag opened the
party list and every macro that meant to open the party list opened the bag.
Surveyed on the cartridge from the rung-9 checkpoint: a THROW BALL aiming at 2
walked the cursor to wTopMenuItemX 15 / wCurrentMenuItem 0, pressed A, and the
party list opened -- wTopMenuItemY 1, wTopMenuItemX 0, wListMenuID $02 -- with
the game writing wCurrentMenuItem 2 on the frame after the press.
The fake had the same mistake in its own geometry, so it could not have caught
it: its two-by-two moved row-major. It is column-major now, like the cartridge.
THROW BALL on the cartridge was 63 starts and 63 `blocked`, mean sixty-nine
frames: the cursor to ITEM, the A that confirms it, the twenty settle frames,
and then a refusal on the very next frame. Red keeps one cursor for every menu
in the game, the bag takes longer than twenty frames to draw, and `listing` was
still answering for the battle *menu* -- four entries, max 3. A ball whose bag
index was above that read as off the end of the list and the step gave up at
once; one inside it was walked for by pressing UP and LEFT at the battle menu,
which is the blind pressing section 4 forbids.
So a Listing now says which list it is (ListKind) and a cursor step says which
list its target indexes into. A step whose list is not up waits exactly as it
waits for a list that reports no cursor at all, and takes its press order and
its budget from that list on the first frame it accepts input rather than from
whatever was up when the script was built. A confirming press that has already
begun still finishes: the press is what answers the list, so the cartridge is
already drawing the next one while it is issued.
The crossings are named: FIGHT and ITEM and PKMN and RUN over the battle menu,
a move slot over the move list, a bag index over the bag, a party slot over the
party list. A shop's own screens are one list and stay as they were.
The fake gained the one thing it could not express -- a list that takes frames
to draw -- and two of its fixtures had FIGHT and a potion opening nothing at
all, which is how they passed while the cartridge could not.
The v0.4.3 residual: BACK was 263 of 797 macro starts and every one of them was
over an open move list. A move list whose battler the seam cannot place binds no
MOVE n at all -- move_slot_bound needs wBattleMon* -- so its pad was BACK alone,
and the only thing that button does is close the list MOVE 1 on the menu
underneath had just opened. That is section 12.10's pair with MOVE 1 standing
where NEXT used to.
So BACK is dealt on the move list only while battle.own reads, and MOVE 1 keeps
the backstop it has over the top-level menu: with nothing readable the pad is
MOVE 1 alone and its script confirms wherever the cursor stands, which is the
press that ends a turn. With the moves readable the pad is unchanged.
Rung 10, thirty brain minutes inside the Pewter museum's upper floor (map 0x35):
macro starts MENU 82, BACK 82, GO FRONTIER 8, the event log alternating
`MENU start/done, BACK start/done`. MENU pressed START, the start menu opened,
and that scene's pad is CLOSE / CONFIRM / BACK -- so BACK pressed B and closed it
again. Two buttons that undo each other with nothing else changing, which is
section 12.10's rule one scene wider than a battle, and section 12.2's trap by
definition: a precondition satisfied wherever the fly stands and a macro that
completes without moving.
Nothing in the macro vocabulary uses the start menu for anything, so MENU comes
off scene_set's overworld row entirely rather than being narrowed. It stays a
type, a population, a tag and a script, so the roles, the channel order and
--print-compatibility are untouched, and the fly's raw START still opens the
start menu in macros mode.
MENU was also what made an empty overworld pad impossible. That guarantee moves
to the way out: `ways` gains one last resort for a *room*, the one GO ROUTE has
had outdoors since 13.1, so a map that offers nothing else at all still offers
its door or its staircase -- ignoring the blocked window, because a target the
ledger is resting is still the only place to go. The museum's staircase is a
passage and not an exit, which is why the old tier 3 could not answer for it.
A map with no way out at all is now a genuinely empty pad. No map in Red is
that, it is asserted as the named residual, and game.padEmptyMs reports it.
Unit: no battle pad holds both NEXT and BACK, over every sub-state with and
without a potion and a ball, and NEXT is on none with a cursor accepting input
(the assertion the top-level menu failed before this branch). A battle frame
with a cursor accepting input is the fly's turn, against real WRAM, for all
four menus plus the two frames that correctly are not one. The bag deals
ITEM / THROW BALL / BACK, and BACK alone when the bag is empty.
ROM-gated, from the live rung-9 forest checkpoint: no pad with both buttons, no
NEXT while a menu accepts input, the longest NEXT/BACK alternation under four,
and every battle entered also left, each on a bounded number of macros. The
battle rules ask the scene the pad was dealt for rather than wIsInBattle, so
the $ff frame a lost battle passes through -- which reads Unknown, whose pad is
NEXT and BACK by contract -- is not accused of a battle rule.
The test fails on v0.4.2 from the same checkpoint with "NEXT was on the pad
while a battle menu was accepting input", NEXT dealt on battle/main, and a run
that never leaves map 51.
Live on rung 9 thirty-five minutes after v0.4.2: NEXT 1264 macro starts, BACK
1241, the log alternating NEXT start/done, BACK start/done every hold, on map
51. NEXT on the top-level battle menu is an A press on the cursor, and the
cursor sits on FIGHT, so it opened the move list; BACK on the move list closed
it again. Each button was legitimate where it stood, and the pair undid itself
with nothing else changing, so the turn never resolved.
NEXT is now on no pad with a cursor accepting input. It keeps the between-turns
row alone and the forced switch, which has no BACK beside it to undo it. MOVE 1
is the top-level menu's backstop instead: the question over that menu is 12.8's
"is there a move list to open", FIGHT always opens, and the script there is
confirm FIGHT and stop, which reads no move at all.
The bag is the fly's own turn, because a cursor accepting input is one, and its
pad is the bag's own three answers: ITEM, THROW BALL, BACK. Section 12.10.
The buffer is ds 1300 and every real map plus its three-block border fits in it.
A header that says otherwise is one read mid-load, and decoding it would read
past the buffer into somebody else s WRAM, so it is a refusal rather than a
clamp.
A warp writes wCurMap before the map header and the block data: on Oak s lab
doormat wCurMap already reads PALLET_TOWN while the header still reads the lab s
ten-by-twelve. The decode agrees with the screen on such a frame -- both are the
old map -- so only the id is wrong, and a grid filed under it would stay wrong
for as long as it was cached. One byte answers it: does the cached grid still
agree with the screen about the tile the fly is standing on.
Measured on the cartridge, not derived: a map tile is 2x2 screen tiles and
CheckTilePassable matches one id, and the screen agrees with the lower-left tile
of the quadrant. Viridian Forest s (4, 32) reads 3, the second row of its
block, where the first row holds bash4. On open ground most quadrants hold one id
four times over, so the upper-left guess reads correctly on a town and falls
apart in a forest -- which is why the decode is cross-checked against the screen
before it is trusted.
route and frontier ask for the grid once per plan and fall back to the window
when there is none. With it: one plan crosses a whole map, GO WARP / GO OUT /
GO ROUTE route to their warp or connection tile rather than to the nearest tile
of an edge that reads Unknown, a tile-pair wall is planned around instead of
being learned by walking into it, and the frontier is the nearest unstood
walkable tile anywhere on the map instead of the nearest one on screen.
The walkable predicate answered for the ten-by-nine screen window and Unknown
everywhere else. MapGrid is the same rule over every tile of the loaded map:
block ids out of wOverworldMap, a blocks-to-tiles read of the tileset header s
blockset through the new bank-aware ROM read, the tileset s collision list as
before, and the TilePairCollisionsLand values as directed walls both ways.
The reader checks itself before it answers: the decode is compared against the
window predicate over the player s own neighbourhood, and a frame where the
window can answer for none of it -- a battle, a text box, a frame mid-warp -- is
refused, because wOverworldMap shares its bytes with the picture buffer. Every
refusal is named (GridRefusal) and leaves the window predicate in charge.
Cached per map id and size, so a map is decoded once on arrival rather than once
per question, and owned beside the session ledgers: never checkpointed.
MemoryReader::read8 reads the CPU bus, where banks 1 and up are whichever bank
the cartridge last switched to, so a table in another bank could only be read by
writing the mapper register -- and the joypad is the only write this workspace
makes into a running game. read_rom takes a bank and a CPU address and reads the
cartridge image the process already holds instead. Defaulted to None, so every
reader without a cartridge behind it narrows rather than guesses.
Two traps from the rung-9 forest checkpoint, both section 12.2's rule: a
macro that completes without moving because its precondition is already
satisfied where the fly stands.
BACK was dealt by the between-turns row, which section 13.1 gave NEXT and
BACK for the sake of the battle bag -- the bag reads as nobody's turn, so
it lands there -- and which is also every frame of battle text. With
nothing open there is nothing to back out of, so the press completes in
fifteen frames on the tile it started on: 163 of 959 macro starts in
twenty brain minutes from the checkpoint, plus twelve more on a move list
whose cursor the seam cannot place. The row is now the sub-state's: BACK
with the bag open, NEXT alone otherwise. The move list and the party list
keep it, and the top-level menu never had it.
THROW BALL gains a fourth fact: the species on the other side is not one
the party already holds. The party is the caught set, because it is the
cartridge's own lifetime record in the same internal numbering the enemy
is read in; wPokedexOwned is by Pokedex number and the table that converts
one to the other is in a ROM bank this crate cannot read. An enemy the
seam cannot place leaves the button where it was.
RUN is unchanged. Its blocked outcomes are the script and not the
cartridge: a cursor macro started as the menu closes waits out CURSOR_WAIT
pressing nothing (mean 184 frames over ten starts), which is audit row 19.
The decoder, the reward catalog, the adapter version and the compatibility
string are untouched: --print-compatibility is byte-identical at 648 bytes.
resolve_wram.py is a second reading of the disassembly beside gen_symbols.py:
it walks ram/wram.asm with a cursor that is only ever live while anchored on an
address symbols.rs already pins, re-derives 40 of the 63 it carries with no
disagreement, and emits an address only when a pinned one after it agrees too.
wOverworldMap, wCurMapTileset, wTilesetBank and wTilesetBlocksPtr come out of
that pass; no address is hand-written and nothing else in the table moves.