InvalidateICache widened its search by MAX_BLOCK_INSTRUCTIONS, but
block_map_ is keyed by byte addresses - a block can span four times that
many bytes. A block longer than 0x4000 bytes starting just below the end
of the invalidated range could therefore be missed and left behind after
the code under it changed. Multiply by 4.
DestroyBlock's "invalid original address" safety check returned after
setting b->invalid but before UnlinkBlock() and the checkedEntry
poisoning, so other blocks kept jumping directly into a block the cache
had already written off. The check is there to guard the memory access
that restores the original opcode, so only guard that.
Also: codeSize is a byte count assigned from a full pointer difference,
so a block over 64KB truncated it (affecting GetAddressFromBlockPtr and
the disassembly view); widen it to u32. The RAMTOP range check missed a
block sitting exactly on the user-memory midpoint, since the RAMBOTTOM
check next to it is strict. And GetBlockDebugInfo disassembled one
instruction past the end of the block.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>