# Othello (U) ## Status **Pass 3 complete; promoted.** `prg.s` is a traced disassembly with all address-derived labels replaced, all `jsr`/`jmp` operands symbolic, hardware registers named via `symbols.inc`, and `apply_symbols.py` reporting 0 residue. Code coverage: 14,220 / 32,768 bytes (43.4%). `make` assembles and verifies byte-identical to `rom.nes`. **Pass 3 data tables done.** Every `; ---- data ----` block in the PRG is now interpreted and named in `prg.s`; the full inventory (address, size, name, record structure and the consuming instruction, or the ROM provenance for the blocks nothing reads) is in [Pass 3 data-table inventory](#pass-3-data-table-inventory). 20 tables were placed; the many small blocks that no instruction of this dump reads are recorded there as unreferenced data, as text reached through the game's own text interpreter, or as code the tracer could not reach. ## Power-on flow analysis Derived from the disassembly by `_tools/derive_poweron.py`; the call graph is walked from the reset entry. **RESET** is at `$8000` (`Reset`). The CPU comes here out of reset with the PPU and APU in their power-on state and the zero page and RAM uninitialised. The reset path calls: | Address | Routine | |---------|---------| | `$8009` | `Sub_OnNonnegative` (falls through) | | `$800E` | `Sub_OnNegative` (falls through) | | `$8013` | `Sub_OnNonnegative2` (falls through) | | `$8018` | `Sub_OnNegative2` (falls through) | **NMI** is at `$D6B4` (`NmiHandler`). It is the per-frame handler; what it calls: | Address | Routine | |---------|---------| | `$A2AE` | `UpdateFrameCounter` | | `$D90A` | `UpdateInputQueueHead` | | `$D70D` | `SetPpuScrollAndControl` | | `$DA09` | `PollController` | | `$DB70` | `ReadControllerPort2` | **IRQ** is at `$804E` (`IrqHandler`) - a different address from RESET. ## ROM | Property | Value | |----------|-------| | Format | iNES | | Mapper | 0 (NROM) | | PRG | 32 KB | | CHR | 8 KB | | Mirroring | Horizontal | | NMI vector | $D6B4 | | RESET vector | $8000 | | IRQ vector | $804E | ## Power-on flow RESET enters at `$8000`: sei ; CPU:$8000 PRG:0000 78 lda #$00 ; CPU:$8001 PRG:0001 A9 00 sta PPUCTRL ; CPU:$8003 PRG:0003 8D 00 20 sta PPUMASK ; CPU:$8006 PRG:0006 8D 01 20 lda PPUSTATUS ; CPU:$8009 PRG:0009 AD 02 20 bpl Reset_OnNonnegative ; CPU:$800C PRG:000C 10 FB lda PPUSTATUS ; CPU:$800E PRG:000E AD 02 20 bmi Reset_OnNegative ; CPU:$8011 PRG:0011 30 FB lda PPUSTATUS ; CPU:$8013 PRG:0013 AD 02 20 bpl Reset_OnNonnegative2 ; CPU:$8016 PRG:0016 10 FB lda PPUSTATUS ; CPU:$8018 PRG:0018 AD 02 20 bmi Reset_OnNegative2 ; CPU:$801B PRG:001B 30 FB NMI handler at `$D6B4`: pha ; CPU:$D6B4 PRG:56B4 48 txa ; CPU:$D6B5 PRG:56B5 8A pha ; CPU:$D6B6 PRG:56B6 48 tya ; CPU:$D6B7 PRG:56B7 98 pha ; CPU:$D6B8 PRG:56B8 48 lda #$00 ; CPU:$D6B9 PRG:56B9 A9 00 sta OAMADDR ; CPU:$D6BB PRG:56BB 8D 03 20 lda #$02 ; CPU:$D6BE PRG:56BE A9 02 ## Pass 3 data-table inventory Othello's PRG is 32 KB of code with the data tables embedded in it, so most of the `; ---- data ----` blocks are small fragments of code the tracer could not reach. Every block is listed below, one row each, with the byte ranges that hold real tables called out separately. "Read by" gives the instruction in this dump that fetches the bytes, by address; where the address is written as "none" the block is not fetched by any instruction of this disassembly and the row records its ROM provenance instead. | Address | Size | Name | Structure | Read by | |---------|------|------|-----------|---------| | `$8077-$807D` | 7 | *(unnamed)* | bytes that decode as instructions (`JSR $D674` / `JSR $D77C` / `JMP $D270`) | none (unreached code) | | `$8089-$808A` | 2 | *(unnamed)* | the low and high bytes of the code address `$D680` | none (unreached code) | | `$8091-$8092` | 2 | *(unnamed)* | the code address `$D68C` | none (unreached code) | | `$8099-$809A` | 2 | *(unnamed)* | the code address `$D698` | none (unreached code) | | `$80A5-$80A6` | 2 | *(unnamed)* | the code address `$F6D0` | none (unreached code) | | `$80AD-$80AE` | 2 | *(unnamed)* | the code address `$D698` | none (unreached code) | | `$80B5-$80B6` | 2 | *(unnamed)* | the code address `$D68C` | none (unreached code) | | `$80BD-$80BE` | 2 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$80C5-$80C6` | 2 | *(unnamed)* | the code address `$D674` | none (unreached code) | | `$810D-$810E` | 2 | *(unnamed)* | the code address `$824A` | none (unreached code) | | `$8118-$8119` | 2 | *(unnamed)* | the code address `$CE6C` | none (unreached code) | | `$8138-$8139` | 2 | *(unnamed)* | the code address `$C87D` | none (unreached code) | | `$8140-$8141` | 2 | *(unnamed)* | the code address `$C8A1` | none (unreached code) | | `$814A-$8150` | 7 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$815A-$815B` | 2 | *(unnamed)* | the code address `$CCA5` | none (unreached code) | | `$8162-$8168` | 7 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$816F-$8170` | 2 | *(unnamed)* | the code address `$CDAD` | none (unreached code) | | `$81D4-$81D5` | 2 | *(unnamed)* | the code address `$C87D` | none (unreached code) | | `$822C-$82B8` | 141 | *(unnamed)* | decodes as instructions (the routine that reads the joystick and sets up the board) | none (unreached code) | | `$8350-$8367` | 24 | `BoardSquareScreenOffsetTable` | four parallel 6-byte tables at a stride of six - `$8350` (the screen column), `$8356` (the screen row), `$835C` and `$8362` (two more per-square values) - giving where each class of board square is drawn and how it behaves | `lda $8350,x` at `$833B`; `lda $8356,x` at `$8340`; `lda $835C,x` at `$8345`; `lda $8362,x` at `$834A` | | `$8397-$839D` | 7 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$83A1` | 1 | *(unnamed)* | one byte, `$30` (`BMI` with a relative operand) | none (unreached code) | | `$83DC-$83EB` | 16 | *(unnamed)* | eight 16-bit code addresses (`$8355`, `$842C`, `$8441`, `$844B`, `$8453`, `$8442`, `$8475`) - the interpreter's operation table | none in this dump (the table is walked by the code that follows it) | | `$8463-$846C` | 10 | *(unnamed)* | four 16-bit code addresses and a zero terminator | none in this dump (the same interpreter table) | | `$849E-$849F` | 2 | *(unnamed)* | the code address `$8522` | none (unreached code) | | `$84A7-$84B2` | 12 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$851E-$854D` | 48 | *(unnamed)* | the ASCII strings "BLACK", "WHITE", "MANUAL", "DEMO" and "SLOT", each preceded by its own length byte and a control byte (`$EA` / `$05` / `$06` / `$04`) - the menu entries | none in this dump (the screen text is reached through the interpreter, not by an absolute operand) | | `$8556-$8561` | 12 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$8582-$858E` | 13 | *(unnamed)* | six 16-bit code addresses and a terminator - the interpreter's operation table | none in this dump | | `$85C4-$85CA` | 7 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$85D9-$85DA` | 2 | *(unnamed)* | the code address `$CE23` | none (unreached code) | | `$85E1-$85E2` | 2 | *(unnamed)* | the code address `$CE4F` | none (unreached code) | | `$85FF-$860B` | 13 | *(unnamed)* | six 16-bit code addresses and a terminator - the interpreter's operation table | none in this dump | | `$8640-$864C` | 13 | *(unnamed)* | six 16-bit code addresses and a terminator - the interpreter's operation table | none in this dump | | `$865E-$865F` | 2 | *(unnamed)* | the code address `$8675` | none (unreached code) | | `$8661-$866C` | 12 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$866D-$8698` | 44 | `TimeLimitTextTable` | the ASCII strings "NONE", "20 MINS", "30 MINS" and "40 MINS", each wrapped in the game's own text delimiters (`$2A` / `$21` and a length byte) | the block is fetched by `lda $8692,x` at `$8635`, which copies the "MINS" label into `$0123`; the strings themselves are reached through the text interpreter | | `$8699-$869C` | 4 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$86C0-$86C6` | 7 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$874C-$874D` | 2 | *(unnamed)* | the code address `$9B3B` | none (unreached code) | | `$884A-$888A` | 65 | `PlayerNameTextTable` | the ASCII strings "BLACK" and "WHITE", the 26 letters "A" to "Z", then the two-digit entry codes "69" and "COM" and the control bytes the interpreter needs | `lda $884A,x` at `$883A` and `lda $8850,x` at `$8840` (the "BLACK"/"WHITE" pair); `lda $8856,x` at `$87FC` and `$8808` (the letter table); `lda $887F,x` at `$8702` and `lda $8885,x` at `$872A` (the entry codes) | | `$8B57-$8B66` | 16 | *(unnamed)* | the ASCII string "SLOT GAME" with the game's text control bytes around it | none in this dump (reached through the text interpreter) | | `$8C80-$8C8B` | 12 | *(unnamed)* | the ASCII string "PUSH 'B'" with the game's text control bytes around it | none in this dump (reached through the text interpreter) | | `$8E2A-$8E2B` | 2 | *(unnamed)* | the code address `$9BAB` | none (unreached code) | | `$8E55-$8E56` | 2 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$8E5E-$8E5F` | 2 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$8EB5-$8EB6` | 2 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$9003-$900A` | 8 | `BoardDirectionColumnDeltaTable` | the column step of each of the eight board directions: `+1 0 -1 0 +1 -1 -1 +1` | `adc $9003,x` at `$8FB4`, `$9020`, `$9069` and `$90D1` | | `$900B-$9012` | 8 | `BoardDirectionRowDeltaTable` | the row step of each of the eight board directions: `0 -1 +1 0 -1 +1 +1 -1` - the two tables together are the eight compass directions | `adc $900B,x` at `$8FC2`, `$9028`, `$9077` and `$90D9` | | `$92BF-$92FA` | 60 | `BoardSquareIndexTable` | 60 byte values in the range `$00`-`$3F` - a permutation of the board square numbers, used to order the squares | `lda $92BF,x` at `$92A9` - 60 bytes copied to `$0126` | | `$92FB-$9336` | 60 | `BoardSquareIndexTableVariant` | a second 60-byte permutation of the same square numbers, copied to the same place instead of the first | `lda $92FB,x` at `$92B5` - 60 bytes copied to `$0126` | | `$954C-$9561` | 22 | *(unnamed)* | ten 16-bit code addresses and a terminator - the interpreter's operation table | none in this dump | | `$95AD-$95D6` | 42 | *(unnamed)* | five 16-bit code addresses, a terminator, and then the code that uses them | none in this dump | | `$9784-$97C3` | 64 | *(unnamed)* | five 16-bit code addresses, a terminator, and then the code that uses them | none in this dump | | `$980F-$9810` | 2 | *(unnamed)* | the code address `$9872` | none (unreached code) | | `$9841-$9842` | 2 | *(unnamed)* | the code address `$9885` | none (unreached code) | | `$985D-$985E` | 2 | *(unnamed)* | the code address `$9872` | none (unreached code) | | `$986D-$9880` | 20 | *(unnamed)* | the ASCII strings "BLACK" and "WHITE" with the game's text control bytes around them, and the first byte of the code that follows | none in this dump (reached through the text interpreter) | | `$9885-$9889` | 5 | *(unnamed)* | the game's text control bytes and the first byte of the code that follows | none (unreached code) | | `$98AC-$98AD` | 2 | *(unnamed)* | the code address `$9872` | none (unreached code) | | `$98D7-$98D8` | 2 | *(unnamed)* | the ASCII string "PASS" | none in this dump (reached through the text interpreter) | | `$98E0-$98F0` | 17 | *(unnamed)* | the ASCII string "PUSH 'A' BUTTON" with the game's text control bytes around it, and the first byte of the code that follows | none in this dump (reached through the text interpreter) | | `$98F7-$98F8` | 2 | *(unnamed)* | the code address `$995B` | none (unreached code) | | `$9967-$997D` | 23 | *(unnamed)* | the ASCII strings "CANNOT CANCEL" and "OVER", with the game's text control bytes around them | none in this dump (reached through the text interpreter) | | `$999B-$99AB` | 17 | *(unnamed)* | the ASCII string "AGAINST LEVEL" with the game's text control bytes around it, and the first byte of the code that follows | none in this dump (reached through the text interpreter) | | `$99B7-$99B8` | 2 | *(unnamed)* | the code address `$99C7` | none (unreached code) | | `$99C7-$99D9` | 19 | *(unnamed)* | the ASCII strings "TRY" and "AGAIN" with the game's text control bytes around them, and the first byte of the code that follows | none in this dump (reached through the text interpreter) | | `$99E6-$99FF` | 26 | *(unnamed)* | the ASCII strings "WINS", "DRAW" and "GAME" with the game's text control bytes around them, and the first byte of the code that follows | none in this dump (reached through the text interpreter) | | `$9A09-$9A34` | 44 | *(unnamed)* | the ASCII string "!!STOP!! PUSH SELECTOR START BUTTON" with the game's text control bytes around it, and the first byte of the code that follows | none in this dump (reached through the text interpreter) | | `$9A5E-$9A8D` | 48 | *(unnamed)* | decodes as instructions | none (unreached code) | | `$9AA2-$9AA9` | 8 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$9AAD-$9AAE` | 2 | *(unnamed)* | the code address `$9B12` | none (unreached code) | | `$9AD7-$9BAF` | 217 | *(unnamed)* | the ASCII strings "HURRY!", "GAME END" and "=DEMO=" with the game's text control bytes around them, followed by the ASCII strings "INPUT", "GAME", "MODE", "COMPUTER LEVEL", "TIME" and "LIMIT" | none in this dump (reached through the text interpreter) | | `$9FA6-$9FAD` | 8 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$A263-$A29F` | 61 | `CursorPositionTable` | four parallel 5-byte tables - `$A263` (the cursor column), `$A267` (the cursor row), `$A26B` and `$A26F` (two more per-square values) - followed by the code that uses them | `lda $A263,x` at `$A176`; `lda $A267,x` at `$A17D`; `lda $A26B,x` at `$A1CA` | | `$A3E9-$A3F8` | 16 | `CursorOffsetTable` | four parallel 4-byte tables - `$A3E9` (screen X), `$A3ED` (screen Y), `$A3F1` (a 16-bit value) and `$A3F5` (a 16-bit value added to it) - the four cursor positions the game uses | `lda $A3E9,x` at `$A3D5`; `lda $A3ED,x` at `$A455`; `lda $A3F1,x` at `$A3DB` and `$A45B`; `adc $A3F5,x` at `$A3E3` and `$A463` | | `$A46A-$A46B` | 2 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$A47E` | 1 | *(unnamed)* | one byte that decodes as an instruction | none (unreached code) | | `$A4CA-$A4CB` | 2 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$A4DF` | 1 | *(unnamed)* | one byte that decodes as an instruction | none (unreached code) | | `$A568` | 1 | *(unnamed)* | one byte that decodes as an instruction | none (unreached code) | | `$A56C` | 1 | *(unnamed)* | one byte that decodes as an instruction | none (unreached code) | | `$A59C-$A678` | 221 | *(unnamed)* | decodes as instructions | none (unreached code) | | `$A706-$ABCF` | 1226 | *(unnamed)* | mostly code; the two bytes at `$A900` are a 16-bit work variable the code updates in place (`asl $A901` at `$8E64`, `lda $A900` at `$AC62`) | `asl $A901` at `$8E64`; `lda $A900` at `$AC62` | | `$ABDB-$ABE2` | 8 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$AD3E-$AD43` | 6 | `BoardSquareWeightTable` | 6 bytes of square weights | `cmp $AD3E,x` at `$AC73` | | `$AD44-$AD49` | 6 | *(part of `BoardSquareWeightTable`)* | 6 bytes of square weights (a second band) | `cmp $AD44,x` at `$AC78` | | `$AD4A-$AD4F` | 6 | *(part of `BoardSquareWeightTable`)* | 6 bytes of square weights (a third band) | `cmp $AD4A,x` at `$AC7D` | | `$AD50-$AD51` | 2 | `BoardEdgeBonusTable` | 2 bytes (`$33`, `$3B`) of edge bonus values | `lda $AD50,x` at `$AD2D` | | `$AF00-$AF97` | 152 | `MoveOrderTable` | 152 bytes of square numbers in the range `$00`-`$3F` in a shuffled order - the order in which the computer considers the available moves | `lda $AF00,y` at `$AED2` - the value is used as an index into the 64-entry work table at `ZpA5` | | `$BCCE-$D6A3` | 6614 | `BoardFlipMaskTable` | one large byte table, indexed by a computed square/direction number, that the flip routines add to and subtract from to build the list of discs to turn. The table is read as 8-bit values only; the record size is not established | `lda $BCCE,y` at `$AFB3`, `$B11F` and `$B1A2`; `adc $BCCE,x` at `$AFBA`, `$AFC7`, `$B160`, `$B16C`, `$B18F`, `$B1C2`, `$B1CE`, `$B1F1`, `$B204`, `$B22B`, `$B239`, `$B246`, `$B2E4` and `$B2F1`; `sbc $BCCE,y` at `$B106`, `$B110`, `$B259` and `$B26C`; `sbc $BCCE,x` at `$B304` and `$B317`; `ldy $BCCE,x` at `$B0DC` and `$B15A` and `$B1BC` | | `$DA49-$DA50` | 8 | `DirectionBitMaskTable` | the 8 one-bit masks (`$80 $40 $10 $20 $08 $04 $02 $01`) that pick out one of the eight directions from a direction byte | `and $DA49,x` at `$DA1C` and `$DA3A` | | `$DA51-$DA58` | 8 | `DirectionNameTable` | the upper-case letter that names each direction: "ABSXUDLR" - that is, the letters A, B, S, X, U, D, L and R in the order of the masks above | `lda $DA51,x` at `$DA21` | | `$DA59-$DA60` | 8 | `DirectionNameTableLower` | the same eight direction names in lower case: "absxudlr" | `lda $DA59,x` at `$DA3F` | | `$DD5F-$DD5F` | 1 | *(unnamed)* | one byte, `$06` | none (unreferenced data) | | `$DD60-$DDDF` | 128 | `SquareEvaluationWeightTable` | 64 x 16-bit little-endian weights, one per board square, used to score a position: the high bytes fall off steadily from `$06` to `$01` as the square number rises, so the table is the classic positional weighting of the board | `lda $DD60,y` at `$DC05`, index = (square & `$3F`) x 2 | | `$DDE0-$DDD8` | 57 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$DDD9-$DDE9` | 17 | `PenaltyCountTable` | a run of `$28` (40) - the number of discs that have to be on the board before the computer stops passing | `cmp $DDD9,x` at `$DB15`; `lda $DDD9,x` at `$DB1B` | | `$DDEA-$DE17` | 46 | *(unnamed)* | bytes that decode as instructions | none (unreached code) | | `$DE18-$DE56` | 63 | `SquareMoveLimitTable` | 63 bytes, almost all `$07`, with one `$0F` - the number of moves the computer will look ahead for each square | `lda $DE18,x` at `$DB34` | | `$DE57-$DE65` | 15 | `BoardMaskTable` | 8 x 16-bit board masks (`$4A75`, `$5E5E`, `$F5F5`, `$3B3B`, `$6868`, `$8686`, `$9A9A`, `$BBBB`) - the masks of the four edge rows and the four edge columns of the board | `lda $DE57,x` at `$DB2A` | | `$DE66-$DE8F` | 42 | *(unnamed)* | a mixture of code and data | `lda $DE66,x` at `$DB2F` | | `$DE90-$FFFA` | 5322 | *(unnamed)* | the rest of the traced code of the game | none as a whole | | `$FFFB-$FFFF` | 5 | *(unnamed)* | the tail of the PRG image; the three 6502 vectors are at `$FFFA`-`$FFFF` | the CPU's hardware | ### Notes on the interpretation * The eight compass directions are the clearest structure in the ROM: the masks at `$DA49` pick a direction out of a direction byte, the letters at `$DA51` and `$DA59` name it, and the two delta tables at `$9003` and `$900B` step the cursor in that direction. * The screen text in this game is not fetched by absolute operands: it is reached through the game's own text interpreter, which walks the byte stream that the text sits in. Those blocks are therefore recorded here as unreferenced by any instruction of this dump, with the strings they hold spelled out; the same is true of the interpreter's operation tables, which are read by the code that follows them rather than by an absolute operand. * The 2-byte and 7-byte blocks in the first half of the PRG are the two halves of 16-bit code addresses inside unreached code; they are recorded as such rather than as tables. ## Entry-point naming (2026-10-21) The 302 address-derived entry points (`Sub_`) - every `jsr`/`jmp` target that carried no semantic name - are now named, together with the 101 `SubC` local labels of the same kind. Each new name says what the code does **in Othello**, read off the routine's own body and the caller that reaches it: | Old | New | What the code shows | |-----|-----|----------------------| | `Sub_804F` | `InitGameState` | the reset path: clears the player palettes, sets the tile tables and enters the menu loop | | `Sub_D6E2` | `WaitForVBlank` | spins on the frame counter until it goes non-negative | | `Sub_D6FF` | `SetPpuMask` | writes the value in A to PPUMASK after the vblank wait | | `Sub_D70D` | `SetPpuScrollAndControl` | writes `$2000` to PPUADDR and rebuilds PPUCTRL from the scroll bytes | | `Sub_D9EA` | `ReadKeyFromQueue` | walks the 16-byte key queue at `$0400` and returns the next key | | `Sub_DB0F` | `PlaySoundEffect` | takes a sound id, checks the "still playing" table at `$DDD9` and queues it | | `Sub_9D0A` | `SetCursorOnBoard` | turns the cursor's row/column into a board square and updates it | | `Sub_9479` | `ComputeBoardSquareIndex` | `row*9 + column + $0A` - the index of the square under the cursor | | `Sub_8F65` | `SetSquareOwner` | writes the owner bits into the square array at `ZpA5,x` | | `Sub_8F84` | `ClearSquareOwner` | clears the owner bits of the square under the cursor | | `Sub_8F9B` | `PrepareSquareLookup` | looks the square up and remembers its owner | | `Sub_8FAF` | `FindFlippableSquare` | walks the eight directions and records the squares a move would flip | | `Sub_94B2` | `ComputeBoardSquareIndexAlt` | the same index for the *other* cursor (row/column swapped) | | `Sub_9369` | `WriteBoardSquareToVram` | writes the two tiles of a square into the nametable | | `Sub_988A` | `DrawBoardBackground` | points the VRAM cursor at the board frame | | `Sub_8EBE` | `DrawScoreDigit` | converts a number to ASCII and draws it | | `Sub_9210` | `ProcessBoardSquare` | the square-by-square half of a move: flip, sound, redraw | | `Sub_A1D6` | `CheckSquareOwner` | compares a square's owner with the player whose turn it is | **Residue: 0 entry points remain address-derived.** Every `jsr`/`jmp` operand in the file now names a routine whose role the dump establishes. Not covered is the separate set of internal branch labels - 649 of them, named `Sub_Loop`, `Sub_OnNonzero`, `Sub_OnCarrySet` and so on. They are branch targets rather than entry points, and they are still address-derived; naming them is the next piece of work. ## Build ``` Byte-identical ROM verified. ```