{
 "FB": [
  {
   "name": "driveCmdStrPtr",
   "meaning": "Low byte of the pointer to the current DOS command string (set from driveCmdPtrTable by c128SendDriveCommand, used by LDA ($FB),Y). Boot-sector only; the game itself never touches $FB/$FC (XREF zero-page list ends at $F8).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "boot_c128boot_T1S0_0B00",
   "unit": "boot/c128BootSector"
  }
 ],
 "FC": [
  {
   "name": "driveCmdStrPtrHi",
   "meaning": "High byte of driveCmdStrPtr (always $0B here).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "boot_c128boot_T1S0_0B00",
   "unit": "boot/c128BootSector"
  }
 ],
 "FF00": [
  {
   "name": "mmuConfigReg",
   "meaning": "C128 MMU configuration register (only exists on a C128). c128BootEntry writes 0 = bank 15 (BASIC/KERNAL ROM + I/O visible) right before calling GO64; defensive, since BOOT_CALL already runs in bank 15.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_c128boot_T1S0_0B00",
   "unit": "boot/c128BootSector"
  }
 ],
 "FF4D": [
  {
   "name": "kernalGo64",
   "meaning": "C128 KERNAL jump-table entry GO64: switches the MMU to C64 mode and performs a C64 reset. Final destination of the boot sector; execution resumes at $8009 through the CBM80 cold-start vector.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_c128boot_T1S0_0B00",
   "unit": "boot/c128BootSector"
  }
 ],
 "8000": [
  {
   "name": "cartHeader",
   "meaning": "Destination of the 256-byte copy: the C64 pseudo-cartridge image (cold/warm vectors $8009, signature CBM80). Same address is named cartHeader in the boot/c64cart_8000 survey.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_c128boot_T1S0_0B00",
   "unit": "boot/c128BootSector"
  }
 ],
 "0282": [
  {
   "name": "kernalMemStrHi",
   "meaning": "KERNAL MEMSTR high byte (start of BASIC/user memory); the cartridge stores $08 so low memory starts at $0800 exactly as on a normal C64 reset.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_c64cart_8000_8000",
   "unit": "boot/c64CartridgeStub8000"
  }
 ],
 "0288": [
  {
   "name": "kernalHiBase",
   "meaning": "KERNAL HIBASE (page of the text screen); the cartridge stores $04 so CINT uses the default screen at $0400.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_c64cart_8000_8000",
   "unit": "boot/c64CartridgeStub8000"
  }
 ],
 "0B45": [
  {
   "name": "c128OpenCommandChannel",
   "meaning": "Routine left in RAM by the C128 boot sector (boot/c128boot_T1S0.s): SETNAM length 0, SETLFS 15,8,15, OPEN. Called from the cartridge at $8043; its SETLFS settings are also what the later KERNAL_LOAD at $8065 uses.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_c64cart_8000_8000",
   "unit": "boot/c64CartridgeStub8000"
  }
 ],
 "0B55": [
  {
   "name": "c128SendDriveCommand",
   "meaning": "Routine left in RAM by the C128 boot sector: A = index into the pointer table at $0B75 (0=\"U0>M0\", 1=\"U0>H0\", 2=\"I\"), sends the zero-terminated string on lfn 15 via CHKOUT/CIOUT/CLRCHN. The cartridge calls it with A=2 to initialise the drive.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_c64cart_8000_8000",
   "unit": "boot/c64CartridgeStub8000"
  }
 ],
 "9D": [
  {
   "name": "kernalMsgFlag",
   "meaning": "KERNAL MSGFLG: bit 7 enables control messages (SEARCHING FOR / LOADING), bit 6 enables error messages. eaLoadGame stores 0 so the LOAD is silent. Only written here; not a game variable.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "boot_ea_02A8",
   "unit": "boot/eaBootFile"
  }
 ],
 "0300": [
  {
   "name": "basicErrorVector",
   "meaning": "BASIC IERROR vector. In this file it is part of the 6-vector hijack table (all = $02B8); at run time the game reloads $0200-$03FF from track 18 s15-16 and $0300 then holds startup code called from $0A87 (that is what the XREF label sub_0300 refers to).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_ea_02A8",
   "unit": "boot/eaBootFile"
  },
  {
   "name": "fastLoaderEntry",
   "meaning": "Buffer 0 / first byte of the fast loader. Temporarily patched with $60 (RTS) for the self-locating trick, then overwritten by the sector-18 read and finally jumped to.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  },
  {
   "name": "hillSiteCount",
   "meaning": "number of hill summit sites (both halves); 0 = no hills",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "filmDirectory",
   "meaning": "$0300-$04DF: 30 film names of 16 bytes (3 pages of 10), loaded from / written to track 18 sectors 2-3",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0302": [
  {
   "name": "basicMainVector",
   "meaning": "BASIC IMAIN vector (JMP ($0302) at $A480 after READY.). This is the vector that actually transfers control to eaLoadGame after LOAD\"EA\",8,1 finishes in direct mode.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_ea_02A8",
   "unit": "boot/eaBootFile"
  },
  {
   "name": "unusedBlobTile0302",
   "meaning": "written with $41 by placeForestClusters, never read (dead store)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "E0": [
  {
   "name": "ldTrack",
   "meaning": "loader: track of the sector being transferred",
   "scope": "zp",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  },
  {
   "name": "ldTrack",
   "meaning": "loader: current track",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  }
 ],
 "E1": [
  {
   "name": "ldSector",
   "meaning": "loader: sector being transferred, incremented after each good sector",
   "scope": "zp",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  },
  {
   "name": "ldSector",
   "meaning": "loader: current sector",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  }
 ],
 "E2": [
  {
   "name": "ldDestPtr",
   "meaning": "loader: destination pointer (lo) for the 256-byte receive loop, copied from bootDestLo",
   "scope": "zp",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  }
 ],
 "E3": [
  {
   "name": "ldDestPtrHi",
   "meaning": "loader: destination pointer (hi), copied from bootDestHi",
   "scope": "zp",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  }
 ],
 "E4": [
  {
   "name": "ldCia2Bits",
   "meaning": "loader: CIA2_PRA bits 0-1 (VIC bank, = %01 here) rewritten on every strobe during the transfer; also EORed out of the assembled byte",
   "scope": "zp",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  }
 ],
 "E7": [
  {
   "name": "ldCount",
   "meaning": "loader: sectors left to transfer in bootLoadSectors",
   "scope": "zp",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  },
  {
   "name": "ldCount",
   "meaning": "loader: sectors left",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  }
 ],
 "BA": [
  {
   "name": "kernalCurrentDevice",
   "meaning": "KERNAL FA (current device number), = 8 after the LOAD of 'load'; used by bootSendDosCommand for LISTEN",
   "scope": "zp",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  },
  {
   "name": "magnifyEdgeFlags",
   "meaning": "Flags passed to the $C000 double-size renderer sub_C24B: bit 0 skip top half row, bit 1 skip bottom half row, bit 2 skip left half column, bit 3 skip right half column (for odd sizes)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "magnifyEdgeMask",
   "meaning": "Quadrant suppression mask for plotGlyphDoubleSize: bit 0 = skip top cells, bit 1 = skip bottom cells, bit 2 = skip left cells, bit 3 = skip right cells (set only by magnifyWindow2x)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "01FE": [
  {
   "name": "bootFakeReturnHi",
   "meaning": "High byte of the crafted return address: bootShowTitlePicture stores $04 here; with $60 pushed at $01FD by the patched PHA, the RTS of bootMain jumps to $0461",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  }
 ],
 "09C3": [
  {
   "name": "randomSeed",
   "meaning": "SID voice 3 noise sample stored by bootMain just before its exit; the checksum code at $0461 saves it, zeroes it while summing and restores it; the game uses it as random seed (inferred from overview)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  },
  {
   "name": "randomSeed",
   "meaning": "SID noise sample stored by the boot loader; it is physically the immediate operand of the LDA #$xx at $09C2 in the map-identification protection, and verifyProgramChecksum zeroes/restores it so the checksum is seed independent",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "C003": [
  {
   "name": "bootLoaderStatus",
   "meaning": "last non-zero drive status byte of a failed sector read; never cleared by the loader",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  }
 ],
 "C087": [
  {
   "name": "bootSendShiftReg",
   "meaning": "byte being shifted out to the drive (LSR, LSB first) by bootExchangeByte",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  }
 ],
 "C088": [
  {
   "name": "bootRecvShiftReg",
   "meaning": "byte shifted in from the drive (ROR, LSB first) by bootExchangeByte; returned in A",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  }
 ],
 "C0FE": [
  {
   "name": "bootRasterThreshold",
   "meaning": "SBC operand inside bootReceiveSectorData, patched to (VIC_CTRL1 and 7) + $2F = $32 for the title screen; defines which raster lines are treated as bad-line neighbours",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  }
 ],
 "C141": [
  {
   "name": "bootDestLo",
   "meaning": "destination low byte for the next sector (LDA operand patched by bootSetDest)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  }
 ],
 "C142": [
  {
   "name": "bootDestHi",
   "meaning": "destination high byte for the next sector; incremented after every good sector",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  }
 ],
 "C144": [
  {
   "name": "bootSavedSprites",
   "meaning": "VIC_SPR_ENA saved during a sector transfer (sprites are switched off to keep the timing)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  }
 ],
 "C198": [
  {
   "name": "bootCurrentTrackOperand",
   "meaning": "LDY operand at $C197, self-incremented from $16 to $1B while loading tracks 22-27 to $0800-$73FF",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  }
 ],
 "C371": [
  {
   "name": "bootSavedCpuPort",
   "meaning": "CPU_PORT value saved by bootBlockExecute and restored by bootSendDosCommand",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  }
 ],
 "FFFA": [
  {
   "name": "nmiVector",
   "meaning": "NMI vector in RAM (word); set to rtiVector ($C229) by bootMain",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  },
  {
   "name": "nmiVector",
   "meaning": "RAM NMI vector; installCommNmiVector points it at $E685 while the link is up.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "FFFE": [
  {
   "name": "irqVector",
   "meaning": "IRQ/BRK vector in RAM (word); set to rtiVector ($C229) by bootMain, later replaced by the game",
   "scope": "abs",
   "confidence": "high",
   "chunk": "boot_loader_C000_C000",
   "unit": "boot/fastLoaderC000"
  }
 ],
 "00": [
  {
   "name": "jobCodeBuf0",
   "meaning": "1541 DOS job code for buffer 0 ($0300); set to $80 (read) by queueFastLoaderReads, polled until bit 7 clears, result $01 = OK.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  },
  {
   "name": "jobCodeBuffer0",
   "meaning": "1541 disk-controller job code for buffer 0; $B0 issued at startup (and $B0 before read/write), the code waits for bit 7 to clear (job done). Standard DOS job slot.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  }
 ],
 "01": [
  {
   "name": "jobCodeBuf1",
   "meaning": "DOS job code for buffer 1 ($0400); $80 = read track 1 sector 19.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  }
 ],
 "02": [
  {
   "name": "jobCodeBuf2",
   "meaning": "DOS job code for buffer 2 ($0500); $80 = read track 1 sector 20.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  }
 ],
 "03": [
  {
   "name": "jobCodeBuf3",
   "meaning": "DOS job code for buffer 3 ($0600), where the bootstrap now runs; cleared to $00 so the job controller never touches that buffer.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  }
 ],
 "04": [
  {
   "name": "jobCodeBuf4",
   "meaning": "DOS job code for buffer 4 ($0700, BAM buffer); cleared to $00.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  }
 ],
 "05": [
  {
   "name": "jobCodeBuf5",
   "meaning": "Unused sixth job slot (the 1541 has no buffer 5); cleared to $00 together with the others.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  }
 ],
 "06": [
  {
   "name": "jobTrackBuf0",
   "meaning": "Track for buffer 0 job = 1.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  },
  {
   "name": "currentTrack",
   "meaning": "Track of the sector being read/written (buffer-0 track). Initialised to $12 (18); compared to $12 by the cipher to skip encryption on track 18.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  }
 ],
 "07": [
  {
   "name": "jobSectorBuf0",
   "meaning": "Sector for buffer 0 job = 18 ($12): first page of the fast loader -> $0300.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  },
  {
   "name": "currentSector",
   "meaning": "Sector of the sector being read/written (buffer-0 sector); used as an operand of the cipher key.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  }
 ],
 "08": [
  {
   "name": "jobTrackBuf1",
   "meaning": "Track for buffer 1 job = 1.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  }
 ],
 "09": [
  {
   "name": "jobSectorBuf1",
   "meaning": "Sector for buffer 1 job = 19 ($13) -> $0400.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  }
 ],
 "0A": [
  {
   "name": "jobTrackBuf2",
   "meaning": "Track for buffer 2 job = 1.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  }
 ],
 "0B": [
  {
   "name": "jobSectorBuf2",
   "meaning": "Sector for buffer 2 job = 20 ($14) -> $0500.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  }
 ],
 "14": [
  {
   "name": "bootstrapSrcPtrLo",
   "meaning": "Low byte of the pointer to the page the bootstrap was originally executing in; always $00. In stock DOS $14/$15 hold the disk id of the nonexistent drive 1, so they are safe scratch.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  },
  {
   "name": "serialHandshakeTemp",
   "meaning": "Scratch used by exchangeByteWithHost to hold the last VIA1 serial read for the bit handshake compare.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  }
 ],
 "15": [
  {
   "name": "bootstrapSrcPtrHi",
   "meaning": "High byte of the source page, recovered from the stack after the JSR $0300 / RTS trick (page of the buffer DOS assigned to channel 2).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  }
 ],
 "0100": [
  {
   "name": "cpuStackPage",
   "meaning": "6502 stack; $0100,X with X = SP after the RTS holds the high byte of the return address pushed by JSR $0300, i.e. the page the bootstrap is running in.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "drive_bootstrap_T1S17_0600",
   "unit": "drive/driveBootstrap"
  }
 ],
 "12": [
  {
   "name": "diskIdByte0",
   "meaning": "DOS disk/header id byte 0, used to build the expected header GCR image and in the id-consistency check (zp_12 EOR zp_13 EOR $15).",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  }
 ],
 "13": [
  {
   "name": "diskIdByte1",
   "meaning": "DOS disk/header id byte 1, paired with zp_12 in the header image and id check.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  }
 ],
 "16": [
  {
   "name": "headerScratch16",
   "meaning": "Header-build scratch in findSectorHeader (id byte / GCR image assembly).",
   "scope": "zp",
   "confidence": "low",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  }
 ],
 "1A": [
  {
   "name": "headerChecksum",
   "meaning": "XOR checksum byte of the header fields, computed in findSectorHeader before building the GCR header image.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  },
  {
   "name": "mathTemp2",
   "meaning": "general scratch; multiply partial product low byte",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "textRightCol",
   "meaning": "right-hand column of the current window (drawWindowInnerCorners, sub_2C7F); scratch elsewhere",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameRightColumn",
   "meaning": "In drawFrameBox: column of the right frame edge; in buildGroupFormationSprite: unit loop index",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte1A",
   "meaning": "scratch in resolveAttack: damage-table row offset (0/5/10 by attacker type), later the attacker's facing",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte1A",
   "meaning": "scratch: boomer ordinal in selectBoomerTarget, side index loop in recycleDeadUnits",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte1A",
   "meaning": "scratch: unit loop index in rebuildGroupTables; stack pointer into $0464 while building the processing order",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupSumDyLo",
   "meaning": "in this chunk: low byte of the summed dy (averageGroupMoveVector) or the member count (computeGroupBounds)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rlePageCounter",
   "meaning": "unpackRleTo0200: pages left to fill (2); general scratch elsewhere",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte1A",
   "meaning": "general scratch: low byte of the summed row delta in averageGroupMoveVector, member count in computeGroupBounds/isPointWithinGroupRange",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "blobBackDirectionBits",
   "meaning": "direction bits (1 N, 2 W, 4 E, 8 S) preset on a newly pushed blob cell so it does not grow back toward the origin",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "tempByte1A",
   "meaning": "scratch: seed shift byte 2 in seedToMapName",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "nibbleSwapTemp",
   "meaning": "Scratch: high nibble collector for the colour nibble swap in drawDroneFuelDigits/blinkDroneCountBox, bit counter in buildRadarNoiseMasks",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "loopCounter1A",
   "meaning": "drawStatsTab: 50-slot unit loop counter; drawComcenSchematic: character rows left (10).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "mathWorkHi",
   "meaning": "High byte of the 24-bit value; also the byte counter inside expandGlyphNibbles",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "zpThreatBalanceLo",
   "meaning": "16-bit signed accumulator: enemy weight minus own weight inside the candidate box (evaluateCellSafety); also a 16-bit sum in averageGroupPosition",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiScoreLo",
   "meaning": "low byte of the 16-bit threat total in isGroupDestinationTooRisky; block score in scoreTargetBlock; cell distance in assessThreatAroundAiComcen",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "arcCentreDirection",
   "meaning": "Centre direction (y-1) of the arc currently being summed by findQuietestDirectionArc; also the 2x2 cluster score inside sub_E9A6",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "tempFlag1A",
   "meaning": "$FF when the plan index is 0, tested with BIT so that plan 0 gets no staggered unit activation.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "checksumSpareSeed",
   "meaning": "verifyProgramChecksum stores SID_OSC3 ($D41B) AND $7F here and never reads it back - a dead store (probably a second random seed in an earlier version)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "24": [
  {
   "name": "headerGcrImage",
   "meaning": "8-byte expected GCR header image built by ROM_MAKE_HEADER_GCR and compared byte-for-byte against the raw header read from disk.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  },
  {
   "name": "distPointAY",
   "meaning": "Y of point A for sub_3870.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scanRow",
   "meaning": "current map row of the scan square",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "zpTargetRow",
   "meaning": "maximum destination column while scanning a group; the chosen target row afterwards",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "cellRowA",
   "meaning": "row argument of distanceSquared/cellDistance; group bounding box minimum row in sub_E531",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "cellRow",
   "meaning": "Map row argument of distanceSquared and of the queued $90 strike command",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "targetRow",
   "meaning": "Row of the point being ordered / cell row argument of distanceSquared.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "cellRowA",
   "meaning": "row of the first point handed to cellDistance/distanceSquared.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "30": [
  {
   "name": "bufferPtr",
   "meaning": "Pointer (lo) to the active disk buffer; the data field is read/written through (zp_30). Points into $0600.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  },
  {
   "name": "magnifyRow",
   "meaning": "Source row counter of magnifyWindow2x",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "magnifyRow",
   "meaning": "Written with 5 by playDroneLostAnimation but only read inside magnifyWindow2x, which sets it itself: dead store here",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "31": [
  {
   "name": "bufferPage",
   "meaning": "High byte / page of the active buffer, set to $06 so the buffer is at $0600 for both read and write.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  },
  {
   "name": "viewportCellCol",
   "meaning": "Column counter 0-6 while drawing the viewport (also the index into the map row).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "magnifyColumn",
   "meaning": "Source column counter of magnifyWindow2x",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "32": [
  {
   "name": "headerPtr",
   "meaning": "Pointer to the current buffer's header (track/sector) bytes, dereferenced in findSectorHeader.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  },
  {
   "name": "tileColour",
   "meaning": "screen RAM colour byte for the tile being rendered (terrain colour, then unit colour)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tileColour",
   "meaning": "colour-RAM byte for the overview character being built (terrain colour from D_07EA)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "radarGlyphColour",
   "meaning": "Colour nibble/byte for the glyph being drawn by drawRadarGlyph (terrain, own or enemy unit colour chosen by getRadarCellGlyph)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "3A": [
  {
   "name": "dataChecksum",
   "meaning": "Data-block checksum: compared against the ROM-computed checksum after a read (error $05), and stored as the checksum to write on a write.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  },
  {
   "name": "textWindowBottom",
   "meaning": "text window bottom row / height ($16 for the menu panel)",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowBottom",
   "meaning": "text window last row + 1",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowBottom",
   "meaning": "Text engine: row limit (exclusive) used by window clears.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowBottom",
   "meaning": "text window bottom limit (21)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowBottom",
   "meaning": "text window bottom row limit (top+10 for the repair menu)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowBottom",
   "meaning": "Bottom row limit (exclusive) of the text window; computed here as textWindowTop + 10 = $13, so the panel occupies rows 9..18.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "windowBottomRow",
   "meaning": "Text window bottom row (last row cleared by clearToWindowBottom)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "3D": [
  {
   "name": "driveIndex",
   "meaning": "Drive/side selector; doubled to index the id-byte table (zp_12,x / zp_13,x) in findSectorHeader.",
   "scope": "zp",
   "confidence": "low",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  },
  {
   "name": "textAttribute",
   "meaning": "current text colour/attribute byte (e.g. $6F, $61, $21); saved and replaced by zp_42 in $C133 when a message is active",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textColour",
   "meaning": "colour byte used for printed text (foreground/background nibbles of the bitmap colour cell)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textColor",
   "meaning": "Text engine: colour written for printed characters.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "multicolourTextColour",
   "meaning": "value written to colour RAM for each plotted character when the multicolour bit of the $D016 shadow is set",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textColour",
   "meaning": "Colour byte; sub_2AC8 writes it to colour RAM in multicolour mode, magnifyWindow2x loads it with the source cell's colour, clearCurrentScreenWindows zeroes it",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textColour",
   "meaning": "text colour byte for the $C000 print routines; set to 4 together with zp_42 for the 'RE-POSITIONED' message",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textColour",
   "meaning": "text colour byte; also the EOR mask used to highlight a menu row (5 for the unit menu, 4 for group menus, uiLevelTextColourTable otherwise)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textColour",
   "meaning": "colour byte for printed text and for the menu highlight EOR ($61 print, $60 highlight, $66 clear in the repair menu)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textColour",
   "meaning": "Colour byte for printed text; set by setMenuTextColour ($61, or $21 when the comcen is stunned) before the repair messages are drawn. Its low nibble also determines the schematic background ($16 normally) that eraseSystemMarker assumes.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textColour",
   "meaning": "colour byte used by the $C000 text engine; set to 4 (together with zp_42) for the RE-POSITIONED message, left as the caller had it for TOO FAR!",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textColour",
   "meaning": "Colour byte used by the $C000 text engine for printed characters and as the EOR mask for the menu highlight: $61 while the header is printed, $60 while the menu runs, $66 for the final window clear.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textAttribute",
   "meaning": "current text colour byte (9 in the map-name screen, 0 before the setup phase); also written next to the cursor block in enterMapName",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "textColour",
   "meaning": "colour byte for printed text / menu highlight EOR",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "textColour",
   "meaning": "text colour byte (8 for all panels drawn by this code)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "textColour",
   "meaning": "colour byte for printed text ($CF MISSILES, $CD FIRE, $0B DRONE ALERT)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "textColour",
   "meaning": "Text colour byte ($61 on the STATS tab).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "textColour",
   "meaning": "Colour byte used while drawing text into the bitmap: $CF for the DRONE caption, $CD for LAUNCH and FIRE, $C7 for STRIKE. sub_C137 conditionally swaps it for zp_42.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "textColour",
   "meaning": "Colour byte written for each character cell - into colour RAM ($D800) in multicolour mode, into screen RAM ($8C00) in hires mode",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "50": [
  {
   "name": "loopStatus",
   "meaning": "Cleared to 0 at the top of every command-loop iteration; DOS status/counter slot.",
   "scope": "zp",
   "confidence": "low",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  }
 ],
 "8F": [
  {
   "name": "recvShiftReg",
   "meaning": "Receive shift register in exchangeByteWithHost; bits from the host are rotated in here and the completed byte is returned in A.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  },
  {
   "name": "lineDeltaRow",
   "meaning": "|end row - start row|",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "lineDeltaY",
   "meaning": "|target row - current row|",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  }
 ],
 "90": [
  {
   "name": "sendShiftReg",
   "meaning": "Send shift register in exchangeByteWithHost; the byte to transmit is loaded here and shifted out LSB-first.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "drive_fastloader_T1S18_20_0300",
   "unit": "drive/driveFastLoader"
  },
  {
   "name": "lineDeltaCol",
   "meaning": "|end col - start col|",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "lineDeltaX",
   "meaning": "|target column - current column|",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  }
 ],
 "8C": [
  {
   "name": "lineErrRow",
   "meaning": "line stepper error accumulator for the row axis (starts at 0 or major/2)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "lineErrY",
   "meaning": "Bresenham error accumulator (row axis), sub_FBB8/sub_FBFA",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "randomBitsLeft",
   "meaning": "applyContourRuleAtCell: uses left in the random bit buffer zp_8D before a new random byte is drawn",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "8D": [
  {
   "name": "lineErrCol",
   "meaning": "line stepper error accumulator for the column axis",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "lineErrX",
   "meaning": "Bresenham error accumulator (column axis)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "randomBitBuffer",
   "meaning": "random byte shifted right one bit per irregular-pattern decision",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "8E": [
  {
   "name": "lineMajorLen",
   "meaning": "length of the line along its major axis (max(|dRow|,|dCol|)); also set directly by overlay A at $73F5",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "lineLength",
   "meaning": "Bresenham major-axis length",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "contourInteriorClass",
   "meaning": "terrain class of the interior tile being contoured (6 for $65, 4 for $61); neighbours of class >= this count as inside",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "91": [
  {
   "name": "lineStepsLeft",
   "meaning": "remaining steps of the current line (0 = finished); returned in A by initLineStepper/stepLine",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "lineStepsLeft",
   "meaning": "remaining Bresenham steps",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineStepsLeft",
   "meaning": "Remaining steps of the Bresenham line started by sub_FBB8 (max(|dx|,|dy|)).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineStepsLeft",
   "meaning": "Remaining line steps; drawLineInPathSprite loops until 0",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineStepsLeft",
   "meaning": "steps left on the current line (0 = destination reached with the last step)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "contourRingClass",
   "meaning": "contourInteriorClass - 1 = class of the contour tiles that get erased and redrawn",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "lineStepsLeft",
   "meaning": "Steps left in the high-memory line stepper; 0 ends the shot line.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "92": [
  {
   "name": "lineCurRow",
   "meaning": "current map row of the line walker (start row on entry); pairs with $94. Main code loads it from the view origin $A6+2 or the unit row array",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "cursorRow",
   "meaning": "current map row of the view centre / line start (= zp_A6 + 2)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineCurrentY",
   "meaning": "Current Y of the Bresenham line (sub_FBB8/sub_FBFA).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineCurRow",
   "meaning": "Current row of the line stepper = sprite row plotted by plotPathSpritePixel",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineCurRow",
   "meaning": "line walker current row (start row on entry); also the cursor row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineCurRow",
   "meaning": "line walker current row = the unit's path row during a step",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "forestSeedRow",
   "meaning": "row of the current forest seed (jittered from zp_94)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "lineCurRow",
   "meaning": "Line start/current row (attacker row in drawShotLineInViewport, 10 = sprite centre in drawDroneHeadingIndicator).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "lineCurrentRow",
   "meaning": "line-stepper current row (initLineStepper); the trainer preloads it with the group centre row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "lineStartRow",
   "meaning": "line-walker start row (shared with initLineStepper/stepLine)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "lineStartRow",
   "meaning": "Line-stepper start row (initLineStepper $FBB8 convention: $92/$93 = start row/column, $94/$95 = end row/column)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "lineCurRow",
   "meaning": "line-stepper start row = the drone's row (clamped to 0 when the drone is off the top of the map).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "93": [
  {
   "name": "lineCurCol",
   "meaning": "current map column of the line walker (start column on entry); pairs with $95. Overlay B uses it as the horizontal pixel source ($83BF -> X of plotFatPixel)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "cursorCol",
   "meaning": "current map column of the view centre / line start (= zp_A5 + 3)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineCurrentX",
   "meaning": "Current X of the Bresenham line.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineCurCol",
   "meaning": "Current column of the line stepper = sprite column plotted by plotPathSpritePixel",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineCurCol",
   "meaning": "line walker current column",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineCurCol",
   "meaning": "line walker current column = the unit's path column during a step",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "forestSeedCol",
   "meaning": "column of the current forest seed (jittered from zp_95)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "lineCurCol",
   "meaning": "Line start/current column (attacker column; 12 = sprite centre for the heading indicator).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "lineCurrentCol",
   "meaning": "line-stepper current column; preloaded with the group centre column",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "lineStartCol",
   "meaning": "line-walker start column",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "lineStartColumn",
   "meaning": "Line-stepper start column",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "lineCurCol",
   "meaning": "line-stepper start column = the drone's column (clamped to 0 when negative).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "94": [
  {
   "name": "lineEndRow",
   "meaning": "target row of the line (loaded from unitRow $F6A4,x at $1D9C)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "lineTargetRow",
   "meaning": "Bresenham target row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineTargetY",
   "meaning": "Target Y of the Bresenham line.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineEndRow",
   "meaning": "line walker target row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineEndRow",
   "meaning": "line target row (unit destination row)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "forestClusterRow",
   "meaning": "row of the current forest cluster centre (4..35)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "lineEndRow",
   "meaning": "Line end row (target row / heading line end).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "lineTargetRow",
   "meaning": "line-stepper target row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "lineEndRow",
   "meaning": "line-walker end row; also the chosen target row written by scoreTargetBlock",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "lineEndRow",
   "meaning": "Line-stepper end row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "lineEndRow",
   "meaning": "line-stepper end row = the row of the chosen 2x2 aim block, written by scoreTargetBlock and shifted here when the start had to be clamped.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "95": [
  {
   "name": "lineEndCol",
   "meaning": "target column of the line (loaded from unitCol $F640,x at $1D97)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "lineTargetCol",
   "meaning": "Bresenham target column",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineTargetX",
   "meaning": "Target X of the Bresenham line.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineEndCol",
   "meaning": "line walker target column",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineEndCol",
   "meaning": "line target column (unit destination column)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "forestClusterCol",
   "meaning": "column of the current forest cluster centre (4..35)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "lineEndCol",
   "meaning": "Line end column (target column / heading line end).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "lineTargetCol",
   "meaning": "line-stepper target column",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "lineEndCol",
   "meaning": "line-walker end column; also the chosen target column written by scoreTargetBlock",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "lineEndColumn",
   "meaning": "Line-stepper end column",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "lineEndCol",
   "meaning": "line-stepper end column = the column of the chosen aim block.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "96": [
  {
   "name": "lineStepRow",
   "meaning": "row increment per major step, $01 or $FF",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "lineStepY",
   "meaning": "row step direction (+1/-1)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineStepRow",
   "meaning": "row step of the line (+1/-1); sign gives north/south in initLineFromUnit",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "97": [
  {
   "name": "lineStepCol",
   "meaning": "column increment per major step, $01 or $FF",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "lineStepX",
   "meaning": "column step direction (+1/-1)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineStepCol",
   "meaning": "column step of the line (+1/-1); sign gives east/west",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "A9": [
  {
   "name": "isDiagonalStep",
   "meaning": "set by stepLine: 1 when the last step moved both row and column, 0 for an orthogonal step or a finished line; $45DF charges 8 extra movement points for a diagonal step",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "isDiagonalStep",
   "meaning": "1 when the last stepLine moved diagonally: costs 8 extra movement points",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  }
 ],
 "B4": [
  {
   "name": "bitmapPtr",
   "meaning": "pointer (lo) to a byte in the $A000 multicolour bitmap; set by plotBitmapPixelRow and by sub_2AA3 (bitmap cell address from the $9300 tables)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "bitmapDestPtr",
   "meaning": "Bitmap address of the current character cell (zp_B4/zp_B5), from sub_2A9F.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "bitmapCellPtr",
   "meaning": "bitmap address of the current character cell ($A000 based) (lo)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "bitmapPtr",
   "meaning": "Pointer (lo) to the destination 8x8 cell in the $A000 bitmap (row table $9300/$9319 + column table $9332/$935A)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "bitmapPtr",
   "meaning": "pointer (lo) to the character cell in the $A000 bitmap",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "bitmapCellPtr",
   "meaning": "Bitmap address of the current radar cell (lo); advanced by 8 per glyph in drawRadarGlyph",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "bitmapPtr",
   "meaning": "pointer (lo) to the 8x8 bitmap block of the cell being drawn ($A000 bitmap, from the row/column tables)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "bitmapPtr",
   "meaning": "Bitmap destination pointer (lo) of drawComcenSchematic, from calcCellPointersXY.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "bitmapPointer",
   "meaning": "Bitmap address of the current character cell ($A000 + row*320 + col*8), set by calcCellPointersXY",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "B5": [
  {
   "name": "bitmapPtrHi",
   "meaning": "pointer (hi) to a byte in the $A000 multicolour bitmap",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "bitmapDestPtrHi",
   "meaning": "High byte of bitmapDestPtr.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "bitmapCellPtrHi",
   "meaning": "bitmap address of the current character cell (hi)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "bitmapPtrHi",
   "meaning": "Pointer (hi) of bitmapPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "bitmapPtrHi",
   "meaning": "pointer (hi) of bitmapPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "bitmapCellPtrHi",
   "meaning": "High byte of bitmapCellPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "bitmapPtrHi",
   "meaning": "pointer (hi) to the bitmap block",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "bitmapPtrHi",
   "meaning": "Bitmap destination pointer (hi).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "18": [
  {
   "name": "mathTemp0",
   "meaning": "general scratch; in the high code: multiply product low / divide divisor / jitter bit counter",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "tempByte18",
   "meaning": "scratch (seen-nonspace flag in buildOwnerNameString, map index in manualLookupCheck, string lo in $C100)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte18",
   "meaning": "scratch; carrier wait counter in waitForCarrier, moved flag / temp in keepGroupBoxInsideMap",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scratchRow",
   "meaning": "General scratch; for sub_2A9F/sub_26C1 it is the character row (1..10 in the viewport).",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textRow",
   "meaning": "character row (0-24) for the text/glyph plotters (calcCellPointers); also general scratch",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cellRow",
   "meaning": "Screen row (0-24) of the cell addressed by sub_2A9F and the character drawer; frame and block drawers use it as their row cursor",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte18",
   "meaning": "scratch: list loop index in scanVisibleEnemies, energy temp in resolveAttack",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte18",
   "meaning": "scratch: unit index passed to initUnitRecord/placeUnitOnMap, list index in selectBoomerTarget, damage cap in the attack routine",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte18",
   "meaning": "scratch: loop index (unit), stun count, remaining-points temp, speed class",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupSumDxLo",
   "meaning": "in this chunk: low byte of the summed dx in averageGroupMoveVector, min x in computeGroupBounds, average dx in followGroupMovement (general scratch elsewhere)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte18",
   "meaning": "scratch: film header checksum accumulator (computeFilmHeaderChecksum), character row in drawOverviewMap",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte18",
   "meaning": "scratch; member counter in countGroupMembers",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte18",
   "meaning": "general scratch: low byte of the summed column delta in averageGroupMoveVector (then the average dx), min column of the box in computeGroupBounds",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "blobCellsToPlace",
   "meaning": "in this chunk: number of cells growTerrainBlob still has to paint (also scratch in pickHillSummitSites and applyContourRuleAtCell)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "tempByte18",
   "meaning": "scratch: typed character / alphabet index in enterMapName, seed shift byte 0 in seedToMapName, swap counter in shuffleInitialUnitOrder, negotiated side returned by the link code ($7971)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "tempByte18",
   "meaning": "scratch: player loop index in applyGameTypeDefaults, unit index for placeAllUnitsOnMap, sector counter in the film save/load loops, name length copy, type-name counter",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "productLo",
   "meaning": "low byte of the multiply8x8 product (unused here; A already holds it)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "scratch18",
   "meaning": "scratch: unit index in redrawEnemyUnitsOnRadar, glyph row for drawTallGlyph",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "unitCountScratch",
   "meaning": "drawStatsTab: number of living units of the current type on the side being counted.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "mathWorkLo",
   "meaning": "Low byte of the 24-bit dividend in convertToDecimalDigits and of the table base in printTableEntry",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "zpTemp18",
   "meaning": "general scratch; in the group code it holds the average row / a saved column, in offsetPointInDirection the resulting column",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiScratch18",
   "meaning": "candidate column from computeCellInDirection; low byte of the column sum in computeGroupCentroid; loop index in assessThreatAroundAiComcen; mirror flag in buildDirectionFan",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiScratch18",
   "meaning": "General scratch; in rankThreatDirections it holds the index of the quietest direction, in chooseAiDroneTarget the objective-weight accumulator, in runAiMissileTurn a missile-conservation term",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "tempByte18",
   "meaning": "General scratch: unit down-counter in rebuildAiGroupTables, row base in the destination randomiser, sign/half values in buildAiHeadingSearchOrder.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "zpScratch18",
   "meaning": "scratch: the target unit's column while the four candidate blocks are scanned, and the 3/4 detonate threshold in the stunned-target test.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "19": [
  {
   "name": "mathTemp1",
   "meaning": "general scratch; in the high code: multiplier then product high / dividend low then quotient / jitter count",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "tempByte19",
   "meaning": "scratch; 'view moved' flag in keepGroupBoxInsideMap",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scratchCol",
   "meaning": "General scratch; for sub_2A9F/sub_26C1 it is the character column (25..37 in the viewport).",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textCol",
   "meaning": "character column (0-39) for the text/glyph plotters; also general scratch",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cellColumn",
   "meaning": "Screen column (0-39) of the cell addressed by sub_2A9F; column cursor of the frame/block drawers",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte19",
   "meaning": "scratch: locked target index in selectBoomerTarget, unit type of the dead unit in recycleDeadUnits, next-cell value in the movement code",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte19",
   "meaning": "scratch: destination cell byte returned by tryStepTowardTarget (bit 7 = a unit, low 7 bits = its index), terrain class, flags",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupSumDxHi",
   "meaning": "in this chunk: high byte of the summed dx, min y of the group bounds, average dy in followGroupMovement",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte19",
   "meaning": "scratch: page counter in writeFilmSetupSnapshot, character column in drawOverviewMap",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte19",
   "meaning": "scratch; group key being counted in countGroupMembers",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte19",
   "meaning": "general scratch: high byte of the summed column delta (then the average dy in followGroupMovement), min row of the group box",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte19",
   "meaning": "scratch: seed shift byte 1 in seedToMapName",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "tempByte19",
   "meaning": "scratch: disk track of the film slot during save/load",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "scratch19",
   "meaning": "scratch: loop counter (50) in redrawEnemyUnitsOnRadar, glyph column for drawTallGlyph",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "mathWorkMid",
   "meaning": "Middle byte of the 24-bit value / high byte of the table base",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "zpTemp19",
   "meaning": "general scratch; resulting row of offsetPointInDirection, base direction for buildDirectionPreference",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiScratch19",
   "meaning": "candidate row from computeCellInDirection; heading for buildDirectionFan; unit type / weapon range in buildUnitThreatTables and assessThreatAroundAiComcen",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiScratch19",
   "meaning": "Carry accumulator of the direction blur ($EC9C-$ECC2), then the best (smallest) 3-direction arc sum; also the per-objective weight in chooseAiDroneTarget",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "zpScratch19",
   "meaning": "scratch: the target unit's row during the block scan, then the desired heading 0-7 that steerDroneAndEmitMove turns toward.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  },
  {
   "name": "savedRandomSeed",
   "meaning": "verifyProgramChecksum: saved copy of randomSeed ($09C3) while the sum is computed",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "57": [
  {
   "name": "gameRngState0",
   "meaning": "game random generator state byte 0 (seeded $49 at $030B); also read directly by $77D6 and $8316",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "rngState",
   "meaning": "24-bit LFSR random state (zp_57-zp_59) stepped by sub_FD5D; seeded from the joystick direction when zero",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rngState0",
   "meaning": "random number generator state (sub_FD5D), 3 bytes $57-$59",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rngState0",
   "meaning": "pseudo-random generator state (sub_FD5D), byte 0",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rngState",
   "meaning": "Low byte of the 24-bit random-number state; re-seeded from the joystick direction by stepRandomSeed whenever all three bytes are zero.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rngState",
   "meaning": "game RNG byte 0 (nextGameRandom $FD5D); source of random map seeds (F5/F7)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "rngState0",
   "meaning": "game RNG state byte 0; copied (with low 5 bits set) into mapSeed by runMapTypeMenu",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "gameRandomLow",
   "meaning": "Low byte of the lock-step game RNG state $57-$59 (nextGameRandom $FD5D); BIT zp_57 at $ECF3 breaks ties between equally quiet directions",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "gameRngState0",
   "meaning": "byte 0 of the 24-bit game RNG state stepped by nextGameRandom ($FD5D); seeded with $49 at $030B",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ],
 "58": [
  {
   "name": "gameRngState1",
   "meaning": "game random generator state byte 1 (seeded $19)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "rngStateMid",
   "meaning": "random state byte 2",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rngState1",
   "meaning": "pseudo-random generator state, byte 1",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rngStateMid",
   "meaning": "Middle byte of the 24-bit random-number state.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rngStateMid",
   "meaning": "game RNG byte 1, copied to $5B for a random map",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "rngState1",
   "meaning": "game RNG state byte 1 -> mapSeed byte 1",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "gameRngState1",
   "meaning": "byte 1 of the game RNG state; seeded with $19 at $030F",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ],
 "59": [
  {
   "name": "gameRngState2",
   "meaning": "game random generator state byte 2 (seeded $02)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "rngStateHi",
   "meaning": "random state byte 3",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rngState2",
   "meaning": "pseudo-random generator state, byte 2",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rngStateHi",
   "meaning": "High byte of the 24-bit random-number state.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rngStateHi",
   "meaning": "game RNG byte 2, copied to $5C for a random map",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "rngState2",
   "meaning": "game RNG state byte 2 -> mapSeed byte 2",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "gameRngState2",
   "meaning": "byte 2 of the game RNG state; seeded with $02 at $0313",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ],
 "5A": [
  {
   "name": "scenarioSeed0",
   "meaning": "scenario/map random generator state byte 0; the 24-bit $5A-$5C value is the MAP ID seed (5 base-32 characters). Also reused as the 3-byte answer buffer of the disabled manual copy-protection check ($09FC)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "manualAnswer0",
   "meaning": "first byte of the player's answer to the manual look-up check (typed via sub_7717)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scenarioSeed0",
   "meaning": "scenario RNG state byte 0; the sound script command $80-$FF adds it to the voice frequency as pseudo-random jitter",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scenarioSeed0",
   "meaning": "scenario RNG state byte 0; loaded from mapSeed $0B95 by sub_EC2B at the start of generation",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "scenarioSeed0",
   "meaning": "scenario RNG state byte 0 / low byte of the 24-bit map seed returned by enterMapName (low 5 bits = last name letter; $1F = symmetric map)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "5B": [
  {
   "name": "scenarioSeed1",
   "meaning": "scenario random generator state byte 1; bit 7 supplies the sign in addRandomJitter",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "manualAnswer1",
   "meaning": "second answer byte",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scenarioSeed1",
   "meaning": "scenario RNG state / map seed byte 1",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "5C": [
  {
   "name": "scenarioSeed2",
   "meaning": "scenario random generator state byte 2",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "manualAnswer2",
   "meaning": "third answer byte",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scenarioSeed2",
   "meaning": "scenario RNG state / map seed byte 2 (only 4 bits of the first letter survive packing)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "A5": [
  {
   "name": "viewOriginCol",
   "meaning": "map column of the left edge of the scrolling battlefield view ($A5+3 is passed as the column to the map address routine sub_25FA at $260D)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "viewLeftCol",
   "meaning": "map column of the left edge of the 7x5 battlefield view",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewportOriginX",
   "meaning": "Map column of the viewport's top-left cell (may be negative near the left edge). The cursor cell is viewportOriginX + 3.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewMapX",
   "meaning": "map column of the cell being rendered / left edge of the map viewport (may be negative or >= 40 when the viewport overhangs the map)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewOriginCol",
   "meaning": "Map column of the view's left edge; +3 is the cursor/centre column used by positionCursorSprite",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewOriginCol",
   "meaning": "map column of the left edge of the battlefield view; cursor cell = +3",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewOriginCol",
   "meaning": "map column of the left edge of the battlefield view",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewLeftCol",
   "meaning": "map column of the left edge of the battlefield view; cursor column = zp_A5 + 3",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewOriginCol",
   "meaning": "map column of the left edge of the battlefield viewport (temporarily set to the cell being drawn)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewOriginCol",
   "meaning": "map column of the left edge of the 7x5 battlefield view; the cursor cell is viewOriginCol+3",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "blobCurCol",
   "meaning": "column of the blob cell being expanded (reuses the view-origin column variable); input to sub_25C7",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "viewOriginCol",
   "meaning": "map column of the view's left edge (45 = off map in resetSelectionAndView)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "viewOriginCol",
   "meaning": "map column of the viewport's left edge; sub_8364 parks it at 45 (off the map) to blank the viewport",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "viewOriginCol",
   "meaning": "Map column of the view origin; on the drone screen = drone column - 6 (radar top-left), on the missile screen = comcen column - 10. drawDroneRadarWindow walks it per cell",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "viewOriginCol",
   "meaning": "map column of the left edge of the current view; on screen 1 = comcenCol - 10 (radar grid origin)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "viewOriginCol",
   "meaning": "Map column of the left edge of the battlefield/drone view (may be negative); the drone screen sets it to comcen column - 6.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "A6": [
  {
   "name": "viewOriginRow",
   "meaning": "map row of the top edge of the scrolling battlefield view ($A6+2 is the row index into the $BFB0/$BFD8 row table)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "viewTopRow",
   "meaning": "map row of the top edge of the battlefield view (biased +20 when zp_B1 is nonzero)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewportOriginY",
   "meaning": "Map row of the viewport's top-left cell; cursor row = viewportOriginY + 2. Restricted to rows 20-39 (own half) while zp_B1 != 0 (sub_1CC1).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewMapY",
   "meaning": "map row of the cell being rendered / top edge of the map viewport",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewOriginRow",
   "meaning": "Map row of the view's top edge; +2 is the cursor/centre row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewOriginRow",
   "meaning": "map row of the top edge of the battlefield view; cursor cell = +2",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewOriginRow",
   "meaning": "map row of the top edge of the battlefield view",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewTopRow",
   "meaning": "map row of the top edge of the battlefield view; cursor row = zp_A6 + 2",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewOriginRow",
   "meaning": "map row of the top edge of the viewport",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewOriginRow",
   "meaning": "map row of the top edge of the battlefield view; the cursor cell is viewOriginRow+2",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "blobCurRow",
   "meaning": "row of the blob cell being expanded",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "viewOriginRow",
   "meaning": "map row of the view's top edge",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "viewOriginRow",
   "meaning": "map row of the viewport's top edge; 45 = viewport blanked",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "viewOriginRow",
   "meaning": "Map row of the view origin; drone row - 5 on the drone screen, comcen row - 8 on the missile screen",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "viewOriginRow",
   "meaning": "map row of the top edge of the current view; on screen 1 = comcenRow - 8",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "viewOriginRow",
   "meaning": "Map row of the top edge of the view (may be negative); drone screen sets comcen row - 5.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "90F4": [
  {
   "name": "lineMovedRow",
   "meaning": "row delta (-1/0/+1) applied by the last stepLine call; read at $1D89 and $E821",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "lineLastStepY",
   "meaning": "row step (-1/0/+1) taken by the last sub_FBFA call",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineStepY",
   "meaning": "Y increment (-1/0/+1) of the last Bresenham step (sub_FBFA).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  }
 ],
 "90F5": [
  {
   "name": "lineMovedCol",
   "meaning": "column delta (-1/0/+1) applied by the last stepLine call; read at $1D8C and $E819",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "lineLastStepX",
   "meaning": "column step taken by the last sub_FBFA call",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lineStepX",
   "meaning": "X increment (-1/0/+1) of the last Bresenham step.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  }
 ],
 "FC4D": [
  {
   "name": "plotColorIndex",
   "meaning": "colour index 0-3 of the pixel being plotted (saved A of plotFatPixel)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  }
 ],
 "FC4E": [
  {
   "name": "plotColorBits",
   "meaning": "colour pattern already masked to the target pixel pair",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  }
 ],
 "FC4F": [
  {
   "name": "plotKeepMask",
   "meaning": "inverted pixel-pair mask (bits of the bitmap byte to keep)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  }
 ],
 "F640": [
  {
   "name": "unitCol",
   "meaning": "per-unit map column array (100 entries, $F640-$F6A3); compared with $93 at $25A2",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "unitXTable",
   "meaning": "map column of each unit (100 entries)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitX",
   "meaning": "Unit record array (100 entries, one per unit index 0-99; indices < 50 belong to one side): map column.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitX",
   "meaning": "unit record array (100 entries, one per unit): map column",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitCol",
   "meaning": "map column of each unit (100 entries; units 0-49 side 0, 50-99 side 1, comcen = 49/99)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitCol",
   "meaning": "per-unit map column (100 entries; all per-unit arrays are 100 bytes apart)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitCol",
   "meaning": "per-unit map column (100 entries)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitX",
   "meaning": "unit array (100 entries): map column",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitCol",
   "meaning": "per-unit map column (100 entries)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitCol",
   "meaning": "Per-unit map column (100 entries); source of the column parameter of the halt command.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitCol",
   "meaning": "map column of each of the 100 units (parallel arrays, stride $64 from $F640)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitCol",
   "meaning": "per-unit map column (100 entries)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitCol",
   "meaning": "unit column array; unitCol[0] = $FF marks 'unit template not yet loaded' for $ECF3",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "unitCol",
   "meaning": "Per-unit map column (100 entries).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "unitCol",
   "meaning": "per-unit map column (100 entries); $F671 = player comcen (unit 49), $F672 = first AI unit, $F6A3 = AI comcen (unit 99)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "unitColumn",
   "meaning": "100-entry array of unit map columns; $F6A3 is unit 99 (the AI comcen)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "unitColTable",
   "meaning": "100-byte per-unit map column array (units 0-49 player, 50-99 AI).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "F6A4": [
  {
   "name": "unitRow",
   "meaning": "per-unit map row array (100 entries, $F6A4-$F707); compared with $92 at $25A9",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_high_FBB8_FBB8",
   "unit": "game/highMemoryFBB8"
  },
  {
   "name": "unitYTable",
   "meaning": "map row of each unit (100 entries)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitY",
   "meaning": "Unit map row (100 entries).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitY",
   "meaning": "unit array: map row",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitRow",
   "meaning": "map row of each unit",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitRow",
   "meaning": "per-unit map row",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitRow",
   "meaning": "per-unit map row",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitY",
   "meaning": "unit array: map row",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitRow",
   "meaning": "per-unit map row",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitRow",
   "meaning": "Per-unit map row (100 entries); source of the row parameter of the halt command.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitRow",
   "meaning": "map row of each unit",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitRow",
   "meaning": "per-unit map row",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitRow",
   "meaning": "Per-unit map row (100 entries).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "unitRow",
   "meaning": "per-unit map row; $F6D5 = player comcen, $F6D6 = first AI unit, $F707 = AI comcen",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "unitRow",
   "meaning": "100-entry array of unit map rows; $F707 is unit 99",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "unitRowTable",
   "meaning": "100-byte per-unit map row array.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "33": [
  {
   "name": "spriteFlashPhase",
   "meaning": "frame counter bits 0/1 rotated into bits 7/6 by the IRQ; bit 7 gates the two-colour sprite flashing",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteFlashPhase",
   "meaning": "Scratch used only inside rasterIrqHandler: zp_45 rotated right twice so bit 7 mirrors bit 0 of the frame counter; BIT-tested to alternate two-nibble sprite colours every other frame.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "38": [
  {
   "name": "irqInProgress",
   "meaning": "re-entrancy guard of rasterIrqHandler (nonzero while the handler runs with interrupts enabled)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "irqInProgress",
   "meaning": "Re-entry guard for rasterIrqHandler. Set on entry, cleared just before irqExit; because the handler does a CLI half-way through, a second raster IRQ can fire and is turned into an immediate exit by this flag. Initialised to 0 by installRasterIrq.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "39": [
  {
   "name": "textWindowTop",
   "meaning": "text window top row used by the $C000 text routines ($C06A copies it to zp_40)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowTop",
   "meaning": "text window first row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowTop",
   "meaning": "Text engine ($C000 overlay): top row of the text window.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowTop",
   "meaning": "text window top row (set to 1 by clearOverviewWindow)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowTop",
   "meaning": "text window top row (9 for the repair menu)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowTop",
   "meaning": "Top row of the text window; set to 9 for the REPAIR WHICH panel (sub_C06A copies it into textCursorRow when it clears the window).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "windowTopRow",
   "meaning": "Text window top row (home row used by clearTextWindow)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "3B": [
  {
   "name": "textWindowLeft",
   "meaning": "text window left column; $C099 positions the cursor at zp_3B+zp_3F",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowLeft",
   "meaning": "text window left column",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowLeft",
   "meaning": "Text engine: left column of the text window.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowLeft",
   "meaning": "text window left column (1)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowLeft",
   "meaning": "text window left column (3 for the repair menu)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowLeft",
   "meaning": "Left screen column of the text window; 3 for the REPAIR WHICH panel.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowLeft",
   "meaning": "text window left column ($C000 text engine)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "textWindowLeft",
   "meaning": "text window left column (28 for the OFF/ON and STANDARD/CUSTOM menus, 29 for the value editors, 30 for the recycler menu; decremented once more before the menu loop)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "textWindowLeft",
   "meaning": "left column of the text window (31 for the DRONE ALERT text)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "textWindowLeft",
   "meaning": "Text engine left column (10 for the STATS tab).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "textWindowLeft",
   "meaning": "Left column of the text window added to textCursorCol by sub_C099; set to $1F (31) for the two-line DRONE ALERT panel and $0A (10) for the STATS tab labels.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "windowLeftColumn",
   "meaning": "Text window left column, added to the cursor column to get the screen column",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "3C": [
  {
   "name": "textWindowRight",
   "meaning": "text window right column ($24)",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowWidth",
   "meaning": "text window width in characters",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowWidth",
   "meaning": "Text engine: window width in characters (wraps when textCursorCol reaches it).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowWidth",
   "meaning": "text window width (20)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowWidth",
   "meaning": "text window width; number of colour bytes toggled per menu row and the centring width passed through D_90EF",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowWidth",
   "meaning": "text window width in characters (7 for the repair menu); also the width of the menu highlight bar",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowWidth",
   "meaning": "Text window width in characters (7 here); the menu engine also uses it as the number of colour cells EORed for the highlight bar of a menu row.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textWindowWidth",
   "meaning": "text window width; also the width of the menu highlight bar",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "textWindowWidth",
   "meaning": "text window width = width of the menu highlight bar (5 value editor / OFF-ON, 10 STANDARD/CUSTOM, 6 recycler) and centring width for sub_C121",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "windowWidth",
   "meaning": "Text window width in characters; also the default centring width",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "3F": [
  {
   "name": "textCursorCol",
   "meaning": "cursor column relative to the window left edge",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textColumn",
   "meaning": "text cursor column relative to the window",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textCursorCol",
   "meaning": "Text engine: cursor column relative to textWindowLeft.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "magnifyDestColumn",
   "meaning": "Reused text-cursor byte: left column of the magnified output (fillColumn - half width)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textColumn",
   "meaning": "text cursor column (set to 0 for the row highlight, 4 for the GROUP header)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textCursorCol",
   "meaning": "Text cursor column relative to the window left edge; set to 10 for the repair message line.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textCursorCol",
   "meaning": "text cursor column relative to the window (4 for the map-name field)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "textCursorCol",
   "meaning": "text cursor column relative to the window",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "textCursorCol",
   "meaning": "text cursor column relative to the window (1 for the value)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "textCursorCol",
   "meaning": "text cursor column relative to the window left edge",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "textCursorCol",
   "meaning": "Text cursor column relative to textWindowLeft (9 = US column, 14 = THEM column).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "textCursorCol",
   "meaning": "Text cursor column relative to the text window left edge; sub_C099 forms the absolute column as zp_3F + zp_3B before each caption is printed.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "cursorColumn",
   "meaning": "Cursor column relative to the window left edge",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "40": [
  {
   "name": "textCursorRow",
   "meaning": "cursor row",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textRow",
   "meaning": "text cursor row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textCursorRow",
   "meaning": "Text engine: absolute cursor row.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "magnifyDestRow",
   "meaning": "Reused text-cursor byte: top row of the magnified output (fillRow - half height)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textRow",
   "meaning": "text cursor row (15 = first menu row)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textCursorRow",
   "meaning": "text cursor row; copied to menuFirstRow after printing the menu title",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textCursorRow",
   "meaning": "Absolute text cursor row; set to 20 for the repair message line.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textCursorRow",
   "meaning": "Absolute text cursor row maintained by the $C000 text engine; read at $655F after the two-CR header so that menuFirstRow gets the row of the first menu item (13).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textCursorRow",
   "meaning": "text cursor row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "textCursorRow",
   "meaning": "text cursor row; copied into menuFirstRow",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "textCursorRow",
   "meaning": "text cursor row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "textCursorRow",
   "meaning": "Text cursor row (STATS rows 9-20).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "textCursorRow",
   "meaning": "Text cursor row passed to calcCellPointersXY by sub_C099.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "cursorRow",
   "meaning": "Cursor row (absolute screen row 0-24)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "41": [
  {
   "name": "flag41",
   "meaning": "set to 1 by showMainMenu, cleared by sub_1FD0 (battle start); menu-vs-battle state flag",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "screenClearedFlag",
   "meaning": "Set to 1 by the screen clear (sub_2D41) and 0 by the main-loop init; never read anywhere (write-only).",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "flag41",
   "meaning": "Set to 1 by initBitmapScreen (menu-vs-battle state flag per the 0800 survey)",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "screenBuiltFlag",
   "meaning": "Set to 1 by the screen builders (buildDroneScreen, sub_7A32); never read",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "flag41",
   "meaning": "set to 1 by initRadarScreen (menu-vs-battle / screen-built flag, write-mostly)",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "textStateSpare",
   "meaning": "Part of the 12-byte text state saved to $92B6; written elsewhere ($1555, $1FDF, $2D46, $7112, $7A4F) but not read by this chunk",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "42": [
  {
   "name": "messageAttribute",
   "meaning": "text attribute used while a message is displayed",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textPadColour",
   "meaning": "colour used for the padding spaces of centred text ($C13F)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "messageTextColour",
   "meaning": "text colour used while a message is displayed (set to 4 in orderGroupNewPosition)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textPadColour",
   "meaning": "colour of the centring padding; kept equal to zp_3D by the menus",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textPadColour",
   "meaning": "colour the text engine installs in zp_3D while printing a centred/padded string (printStringCentred); set to 4 by orderGroupNewPosition",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "messageAttribute",
   "meaning": "text attribute used while a message is displayed (set to 9 with zp_3D in mapNameEntryScreen)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "messageTextColour",
   "meaning": "text colour used while a message is displayed",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "textPadColour",
   "meaning": "colour of centring padding, kept equal to textColour (8)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "padColour",
   "meaning": "Colour used for the blanks printed by the centring pad in printStringAtPointer",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "45": [
  {
   "name": "frameCounter",
   "meaning": "low byte of the 24-bit frame counter incremented by the IRQ (zp_45-zp_47)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameCounter",
   "meaning": "24-bit frame counter (zp_45/zp_46/zp_47) incremented by the raster IRQ at $11D1.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameCounter",
   "meaning": "Low byte of the free-running 24-bit frame counter incremented once per raster IRQ; its low bits drive the flash phase and all the divide-by-4/8/16 timers in updateFrameTimers.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameCounter",
   "meaning": "IRQ frame counter; bit 0 drives the blink in blinkDroneCountBox",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "46": [
  {
   "name": "frameCounterMid",
   "meaning": "middle byte of the frame counter",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameCounterMid",
   "meaning": "middle byte of the frame counter; used to rate-limit the 'OPPONENT READY FOR NEW GAME.' message",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameCounterMid",
   "meaning": "Middle byte of the 24-bit frame counter.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "47": [
  {
   "name": "frameCounterHi",
   "meaning": "high byte of the frame counter",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameCounterHi",
   "meaning": "High byte of the 24-bit frame counter.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "48": [
  {
   "name": "fillPtr",
   "meaning": "pointer (lo) used for memory fills (resetGameVariables) and screen writes",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapCellPtr",
   "meaning": "16-bit pointer (zp_48/zp_49) to a cell of the 40x40 map at $F000, set by readMapCell and friends.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapPtr",
   "meaning": "pointer into the 40x40 battlefield map at $F000 (lo)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fillPtr",
   "meaning": "Pointer (lo) used by initBitmapScreen to clear the bitmap and by initSpritePointers",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapCellPtr",
   "meaning": "pointer (lo) to the current map cell set by sub_25FA and advanced by 40 per row in collectUnitsInRadius (same zp as fillPtr); drawShotLineSprite reuses it as a step counter",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapCellPtr",
   "meaning": "pointer (lo) into the $F000 battlefield map set by sub_25FA (X = column, Y = row); $49 = hi",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapCellPtr",
   "meaning": "pointer (lo, $49 hi) to the map cell computed by sub_25FA; written through with Y=0 to place or clear a unit",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapPtr",
   "meaning": "pointer (lo) into the $F000 map while drawing the overview",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapCellPtr",
   "meaning": "pointer into the $F000 map set by sub_25FA/sub_25C7; the generator addresses neighbours with offsets 0/1/2/$28/$2A/$50/$51/$52 from (x-1,y-1)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "mapCellPtr",
   "meaning": "pointer (lo) to the map cell returned by readMapCell; the river generator writes tiles and markers through it",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "genericPtr",
   "meaning": "pointer (lo): defaults table row in applyGameTypeDefaults, film directory page in the film menus, map cell pointer from readMapCell",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "wordPtr",
   "meaning": "in this chunk: pointer (lo) to the ON/OFF word copied by showToggleStateMessage (general fill/map pointer elsewhere)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "mapCellPtr",
   "meaning": "pointer (lo) into the $F000 map, set by getViewportOriginPointer and advanced by 40 per radar row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "fillPointer",
   "meaning": "16-bit current address of the block-fill loops driven by advanceFillPointer (with zp_49)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "mapCellPtr",
   "meaning": "pointer to the current map cell returned by readMapCell; scoreTargetBlock reads offsets +1, +$28 and +$29 through it to cover a 2x2 block",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "templateGroupPtr",
   "meaning": "Pointer to the 50 group ids of the selected plan inside the loaded template ($0200 + 150*plan).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "fillPointer",
   "meaning": "16-bit source/destination pointer (with $49) driven by advanceFillPointer ($C3F9); used here as the font pointer in invertTextFonts and as the running map row address in buildMapRowAddressTable",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ],
 "49": [
  {
   "name": "fillPtrHi",
   "meaning": "pointer (hi)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapCellPtrHi",
   "meaning": "High byte of mapCellPtr.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapPtrHi",
   "meaning": "pointer into the battlefield map (hi)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fillPtrHi",
   "meaning": "Pointer (hi) of fillPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapCellPtrHi",
   "meaning": "map cell pointer (hi)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapPtrHi",
   "meaning": "pointer (hi) of mapPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapCellPtrHi",
   "meaning": "high byte of mapCellPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "mapCellPtrHi",
   "meaning": "pointer (hi) of mapCellPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "genericPtrHi",
   "meaning": "high byte of genericPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "wordPtrHi",
   "meaning": "high byte of wordPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "mapCellPtrHi",
   "meaning": "pointer (hi) into the map",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "fillPointerHi",
   "meaning": "high byte of fillPointer",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ],
 "4A": [
  {
   "name": "fillEndPtr",
   "meaning": "end address (lo) compared by $C3F9",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupLoopIndex",
   "meaning": "unit loop counter (99..0) in moveGroupKeepingFormation",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte4A",
   "meaning": "unit loop counter (99..0) of moveGroupKeepingFormation; the other loops here use Y or a self-modified immediate instead",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "filmEntryPtr",
   "meaning": "pointer (lo) to the selected 16-byte film directory entry in runSaveFilmMenu/printFilmSlotEntry",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "fillLimit",
   "meaning": "16-bit last address of the block-fill loops (with zp_4B)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "templateColPtr",
   "meaning": "Pointer to the plan's 50 destination columns (templateGroupPtr + 50).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "fillLimit",
   "meaning": "16-bit last address compared by advanceFillPointer (with $4B); set to $9C6D by invertTextFonts",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ],
 "4B": [
  {
   "name": "fillEndPtrHi",
   "meaning": "end address (hi)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "filmEntryPtrHi",
   "meaning": "high byte of filmEntryPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "fillLimitHi",
   "meaning": "high byte of fillLimit",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ],
 "5D": [
  {
   "name": "joyDirection",
   "meaning": "joystick port 2 direction bits, active low: bit0 up, bit1 down, bit2 left, bit3 right; $0F = centred (also $0F when a key < $80 is held or a key event is pending)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joystickState",
   "meaning": "joystick direction bits (active low, $0F = centred; bit 2 left, bit 3 right)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joystickDirection",
   "meaning": "CIA1 port A bits 0-3 (active low) sampled by the raster IRQ at $1212; $0F = centred. Indexes joystickDirectionTable.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joyDirection",
   "meaning": "joystick direction bits, $0F = centred (waitFramesOrInput exits when it changes)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joystickDirection",
   "meaning": "joystick nibble, active low ($0F centred); the menu loop uses only up (bit 0) and down (bit 1) by ORing with $0C",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joyDirection",
   "meaning": "Joystick 2 direction nibble latched by the IRQ, active low ($0F = centred). Forced to $0F for one frame whenever a keypress is pending.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joyDirection",
   "meaning": "joystick direction bits (active low, $0F centred) polled by the IRQ",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "joyDirection",
   "meaning": "joystick direction bits, active low, $0F centred; the value editor ORs with $0C and maps up/down to +1/-1",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "joystickDirection",
   "meaning": "Joystick port 2 direction bits (active low, $0F centred); index into joystickDirectionTable $1FA6.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "5F": [
  {
   "name": "joyFireReleased",
   "meaning": "1 when the fire button is up, 0 when pressed (CIA bit 4)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "isFireReleased",
   "meaning": "1 = fire button not pressed, 0 = pressed (CIA1 port A bit 4), written by the IRQ at $120E.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joyFireUp",
   "meaning": "Joystick 2 fire state latched by the IRQ: 0 = button down, 1 = released.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "60": [
  {
   "name": "joyDirectionCopy",
   "meaning": "second copy of joyDirection written by the IRQ at the same time (separate consumer, probably the comm/transmit side)",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joyDirectionCopy",
   "meaning": "Second copy of joyDirection written by the IRQ at $1216. No reader appears anywhere in the traced code - an unconsumed shadow (dead store).",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "61": [
  {
   "name": "joyFireReleasedCopy",
   "meaning": "second copy of joyFireReleased",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joyFireUpCopy",
   "meaning": "Second copy of joyFireUp written by the IRQ at $1210. Like zp_60 it has no reader in the traced code.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "66": [
  {
   "name": "currentKey",
   "meaning": "key code from the latest poll ($FF none; 0-3 = F1/F3/F5/F7, >= $80 PETSCII-like); forced to $FF while loading",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "requestedScreenId",
   "meaning": "screen to switch to (bit 7 = none); compared with currentScreenId by sub_06BA",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "requestedGameMode",
   "meaning": "Last key / game-mode request written by the IRQ key handler at $0E80/$0EA1: function keys F1/F3/F5/F7 give 0-3, which sub_06BA dispatches as game modes (0 = battlefield map, 1/2 = overlay screens, 3 = $141D); any other key (>= $80) or $FF means no mode change. zp_66 == 0 (F1) also ends the setup-phase main loop ('PRESS F 1 WHEN DONE').",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentKey",
   "meaning": "Current key as seen by the game loops ($FF = none). Set by pollKeyboardEvent, forced to $FF by the IRQ while $0BA7 is non-zero and after the abort hotkey. Codes 0-3 (F1/F3/F5/F7) request a screen change through sub_06BA.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentKey",
   "meaning": "key from the last poll: $FF none, 0-3 = F1/F3/F5/F7 (F1 flips the map, F5 symmetric random, F7 random in enterMapName); set to $FF by startGameSession",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "currentKey",
   "meaning": "Pending screen/mode change key from the IRQ key poller; a non-negative value aborts modal UI (checkUiContinue)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "currentKey",
   "meaning": "Key code from the module's keyboard entry ($E015); $FF = none. Value 0 is the code the display-toggle handler reacts to (the F1 key)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "requestedScreenId",
   "meaning": "last key from the IRQ poll: $FF = none, 0-3 = F1/F3/F5/F7 = requested screen; consumed by switchToRequestedScreen",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "6C": [
  {
   "name": "joyRepeatTimer",
   "meaning": "frame countdown (IRQ decrements zp_6C-zp_71): joystick auto-repeat delay",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joystickRepeatDelay",
   "meaning": "countdown during which joystick directions are ignored (set to $14 after a move)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scrollRepeatTimer",
   "meaning": "Frame countdown (decremented by the IRQ at $123D, first of six timers zp_6C-zp_71) gating joystick auto-repeat scrolling and the path-marker step rate.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joystickRepeatTimer",
   "meaning": "frame countdown gating joystick auto-repeat; 20 at menu start, 30 before stepping onto the last menu row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joyRepeatTimer",
   "meaning": "First of the six per-frame countdown timers zp_6C-zp_71 decremented by the IRQ; set by setJoystickRepeatDelay to 16 frames for the first auto-repeat step and 6 for later ones.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joyRepeatTimer",
   "meaning": "frame countdown gating joystick auto-repeat in the menus; also the 4-second wait of waitFramesServicingComm",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "joyRepeatTimer",
   "meaning": "frame countdown gating joystick auto-repeat in the value editor",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "joyRepeatTimer",
   "meaning": "Frame countdown gating joystick auto-repeat (10 then 4 frames on the missile screen, 11/40 on the drone screen)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "blinkRepeatTimer",
   "meaning": "IRQ frame countdown; on the drone screen it paces the LAUNCH/STRIKE box blink (11 frames idle, 40 in flight).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "6D": [
  {
   "name": "fireDebounceTimer",
   "meaning": "frame countdown set to 10 after a fire press",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fireDebounceTimer",
   "meaning": "Frame countdown; clicks are ignored while nonzero (set to 10 after a click or a scroll step).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fireDebounceTimer",
   "meaning": "10-frame debounce reloaded by checkFirePressed after a click; while non-zero both fire-test routines report 'not pressed'.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "6E": [
  {
   "name": "menuBlinkTimer",
   "meaning": "frame countdown (10) used by sub_16B7 to blink the highlighted menu item",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "countdownTimer6E",
   "meaning": "general frame countdown (tab blink period = 10, per-step delay of scrollViewToUnit)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "autoScrollDelayTimer",
   "meaning": "Frame countdown used by sub_1D4E to pace the animated viewport scroll toward a unit.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuBlinkTimer",
   "meaning": "20-frame countdown between highlight toggles of the selected menu row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuBlinkTimer",
   "meaning": "IRQ frame countdown (one of the six timers $6C-$71 decremented at $123D) used for the menu highlight/tab blink; attemptSystemRepair zeroes it so the tab blink restarts immediately after a repair.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuBlinkTimer",
   "meaning": "frame countdown (20) used to blink the menu highlight bar",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "menuBlinkTimer",
   "meaning": "frame countdown; when 0 the value editor toggles the row highlight",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "headingRereadDelay",
   "meaning": "IRQ frame countdown set to 20 after a joystick heading was accepted; the joystick is re-read for the drone only when it is 0.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "6F": [
  {
   "name": "countdownTimer6F",
   "meaning": "general frame countdown decremented by the IRQ",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "countdownTimer6F",
   "meaning": "frame countdown used for the repair marker blink duration (60)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "weaponLineBlinkTimer",
   "meaning": "Frame countdown (20) during which the WEAPON line of the info panel is left blank, making 'FIRED' blink.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "countdownTimer6F",
   "meaning": "IRQ frame countdown; blinkRepairMarker loads it with 60 (one second) to time the marker blink.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "buttonBlinkTimer",
   "meaning": "9-frame countdown for the XOR blink of the missile button (row 10 column 32) and count box",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "70": [
  {
   "name": "keyRepeatTimer",
   "meaning": "frame countdown (20) before a held key repeats",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "keyRepeatTimer",
   "meaning": "20-frame key auto-repeat timer maintained by pollKeyboardEvent and decremented by the IRQ; a held key produces a new event only after it expires.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unusedTimer70",
   "meaning": "one of the IRQ-decremented frame countdowns zp_6C-zp_71; written with 11 by resetDroneAlertPanel and never read anywhere (dead store)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "71": [
  {
   "name": "countdownTimer71",
   "meaning": "general frame countdown decremented by the IRQ",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  }
 ],
 "72": [
  {
   "name": "messageTimer72",
   "meaning": "countdown decremented every 4 frames; cleared when the message queue is reset or a chat message completes",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "messageTimer72",
   "meaning": "message countdown; cleared when 'RESUMED' is shown",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "messageHoldTimer",
   "meaning": "Timer decremented every 4 frames by updateFrameTimers and cleared to 0 by appendChatCharacter and by the $C000 message code; while non-zero the status-line message stays put ($C4F6 returns early).",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "messageHoldTimer",
   "meaning": "message hold-down counter; cleared together with persistentMessageId when cmdSessionControl shows RESUMED",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "messageTimer",
   "meaning": "status message countdown, cleared by showToggleStateMessage so the new message appears at once",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "messageTimer",
   "meaning": "Countdown (set to $3C, decremented every 4 frames by updateFrameTimers) for how long the current status message stays up; 0 = ready for the next message",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "messageTimer",
   "meaning": "Frames left before the current status-line message may be replaced; decremented every 4th frame by updateFrameTimers. 0 = show the next message now; handleMessageReviewKeys treats >= $23 as 'still fresh'.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "79": [
  {
   "name": "fireWasReleased",
   "meaning": "latch used by checkFirePressed to detect a new press (nonzero after the button was seen released)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fireButtonArmed",
   "meaning": "Set when the fire button was seen released; a press only counts as a click when armed (sub_0EB1/sub_0EEA). Cleared after each viewport scroll step.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fireArmedFlag",
   "meaning": "Fire-button edge latch: set when the button is seen released, cleared when a press is accepted, so checkFirePressed reports each press exactly once.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "7F": [
  {
   "name": "slowTimer7F",
   "meaning": "countdown decremented every 8 frames",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "timeoutTicks",
   "meaning": "tick countdown used as timeout while waiting for the comm module ($26 or $4B)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "timeoutTicks",
   "meaning": "tick countdown used as timeout while waiting for the comm module ($4B in completeExchange)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "slowTimer7F",
   "meaning": "Countdown decremented every 8 frames by updateFrameTimers; loaded and tested around $1AAE/$1B49/$5714 (link/setup sequencing).",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "AA": [
  {
   "name": "rasterIrqLine",
   "meaning": "raster line re-armed by the IRQ handler ($FA)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "irqRasterLine",
   "meaning": "Shadow of the raster line the handler re-arms VIC_RASTER with each interrupt; set to $FA by installRasterIrq.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "AB": [
  {
   "name": "irqVectorLo",
   "meaning": "IRQ handler address (lo) re-installed into $FFFE each interrupt",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "irqVectorLo",
   "meaning": "Shadow low byte of the IRQ vector; rasterIrqHandler writes it back to $FFFE every interrupt, so changing it retargets the next IRQ. Only ever set to $F1 (i.e. $10F1).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "AC": [
  {
   "name": "irqVectorHi",
   "meaning": "IRQ handler address (hi) re-installed into $FFFF each interrupt",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "irqVectorHi",
   "meaning": "Shadow high byte of the IRQ vector, written back to $FFFF each interrupt. Only ever set to $10.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "B1": [
  {
   "name": "gameRunState",
   "meaning": "$FF after resetGameVariables, 0 at game start; bit 7 set suppresses the cursor blink; compared with $FF in updateFrameTimers",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gamePhase",
   "meaning": "0 during battle; nonzero ($F0 menu, $FE/$FF) in setup/menu phases where the view is limited to a 20-row half of the map; $FF also triggers special comm handling",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gamePhaseFlags",
   "meaning": "0 = battle running (full map, mode dispatcher active); nonzero = pre-battle unit setup / other phases (viewport limited to the own half, overlay-A hook sub_8658 called, message 10 'SETUP UNITS. PRESS F 1 WHEN DONE.'). Bit 7 suppresses the mini-map redraw, bit 6 suppresses viewport/unit drawing.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gamePhaseFlags",
   "meaning": "0 while the battle is being played; $FF/$7F/$F0/$3F in setup, editor and replay phases. Bit 6 set suppresses unit drawing in renderMapTile",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gamePhase",
   "meaning": "0 during battle; nonzero in setup/menu phases (units do not scan or attack)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gamePhase",
   "meaning": "0 while the battle runs; nonzero in setup/film/menu phases (blitz cost and comcen sound are skipped)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gamePhase",
   "meaning": "0 = battle; nonzero = setup/menu phase (movement points fixed at 80, no combat, 50-unit command slices)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gamePhase",
   "meaning": "0 during the battle; non-zero during setup, where rotateGroupFormation clamps rows to the player's half of the map",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gamePhase",
   "meaning": "0 while the battle runs (film recording, comcen repair meaning of $9E); nonzero in side-selection/setup phases; cleared bit 7 by the map-seed command, set to $DF by cmdChooseSide",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gamePhase",
   "meaning": "0 battle (sub_40B2 ticks, unit checks), $FF setup phase (sub_8658 tick), other bits 6/7 = film/menu phases (sub_4DA9 tick)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gamePhase",
   "meaning": "Game phase / console context byte ($FF while the battle is running, other values during setup, film and console screens). In this chunk it halves the link-timeout tick rate and gates the cursor blink.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gamePhase",
   "meaning": "0 during the battle, non-zero during setup/side selection; rotateGroupFormation only clamps rows to the owner's half while it is non-zero, and sub_34D5 only applies its comcen special case when it is 0",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gamePhase",
   "meaning": "0 during the battle, nonzero during side selection/setup. The low two bits are the two players' outstanding phase-sync flags cleared by command $9E through getPauseClearMask; bit 7 is cleared by the map-seed command $8B/$A6; cmdChooseSide sets the whole byte to $DF",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gamePhase",
   "meaning": "0 during battle, nonzero in setup/menu phases; bit 7 set lets serviceLinkWhileGenerating call the command exchange",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "gamePhase",
   "meaning": "game phase flags: $FF at session start, bit 7 cleared by the map-seed command, bits 1-0 cleared by the $9E sync command, $7F during identity exchange, $3F during the unit setup phase, 0 when the battle starts (then DEC to $FF before the final sync, left for the comm tail)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "gamePhase",
   "meaning": "0 = battle; nonzero setup/menu phases ($BF custom setup, $FF after the map is chosen)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "gamePhase",
   "meaning": "0 battle; $7F during the game-start option dialogs (runGameStartOptions), $3F/$FF during unit placement where handleStarKeyToggle is polled",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "gamePhase",
   "meaning": "0 = battle running, non-zero = setup/other phases; bits 6/7 checked by checkUiContinue",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "gamePhase",
   "meaning": "0 = live battle, non-zero = setup / film / menu ($F0 on the main menu, $FF at start-up). handlePauseKey and updateComcenMoveSound only run when it is 0; exitIfGameEnded picks the exit route from it.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  },
  {
   "name": "gamePhase",
   "meaning": "non-zero during the setup/deployment phases; the trainer AI is completely idle then",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "0548": [
  {
   "name": "chatLineMarker",
   "meaning": "byte before the displayed chat line: $1E after a received character, $9E when reset ($C62A, initIrqAndInputState)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "messageDisplayState",
   "meaning": "message system state byte; $9E = idle/no message (set here and by $C62A)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "chatLineMarker",
   "meaning": "$9E written at film end to blank the status/chat line",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "statusLineBuffer",
   "meaning": "First byte of the status/message line rendered by the $C000 text engine (the line lives in the $0400-$07FF block that was copied from $6F00-$72FF). appendChatCharacter puts the glyph $1E there in front of an incoming chat message; $C62A resets it to $9E (empty).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "chatLineBuffer",
   "meaning": "Message 0 string - the received chat line ($0549 onwards, marker byte $0548, $9E = empty)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "chatLineMarker",
   "meaning": "First byte of message slot 0's text (the chat line). $9E terminates immediately = blank status line; $1E prints nothing but continues into the incoming chat text mirrored at $0549. showNextQueuedMessage tests its sign to decide whether a chat line is pending.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "0549": [
  {
   "name": "chatDisplayLine",
   "meaning": "36-byte copy of the incoming chat message with bit 7 set on every character",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "statusLineText",
   "meaning": "Text of the status line, $0549-$056C. Written by appendChatCharacter with every character plain except the newest, which carries bit 7 as the terminator.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "0B7D": [
  {
   "name": "inputLockoutTimer",
   "meaning": "frames during which the IRQ skips keyboard/joystick polling (set to $9E at init, $FF on abort)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "uiDelayTimer",
   "meaning": "key/menu delay countdown (set $14 by the menu selector, $1E after a keypress wait)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joystickLockoutTimer",
   "meaning": "Frame countdown; while nonzero the IRQ does not re-read the joystick (keeps old zp_5D/zp_5F). Set to 60 after selections, 30 by checkUserInterrupt.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "inputLockoutTimer",
   "meaning": "frames during which keys are ignored; set to 60 after the HOME key",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "uiDelayTimer",
   "meaning": "input lockout frames: $3C after a pause/timeout command, $20 after a setup-ready command",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "inputLockoutTimer",
   "meaning": "frames during which the IRQ keeps the old joystick state; 20 at menu start, cleared before a scroll step",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "inputLockoutTimer",
   "meaning": "Frame countdown that freezes keyboard and joystick sampling in the IRQ; set to $FF by the abort hotkey, $9E at start-up, $3C by $C48B and various menus.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "inputLockoutTimer",
   "meaning": "frames during which input is ignored: 60 ($3C) after a pause/timeout command, $20 after a setup-ready command",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "inputLockoutTimer",
   "meaning": "frames during which the IRQ ignores input; $FF while the game start handshake runs",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "inputLockoutTimer",
   "meaning": "frame countdown during which the IRQ ignores input; 30 after waitForSpaceKey",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "inputLockoutTimer",
   "meaning": "Frames during which joystick/keyboard input is ignored; set to $3C when checkUiContinue aborts",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "inputLockoutTimer",
   "meaning": "Frames during which joystick/keyboard input is ignored; set to $3C by consumeRunStopKey and by the score-line key.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  },
  {
   "name": "inputLockoutTimer",
   "meaning": "frames during which the IRQ ignores keyboard/joystick; runComcenStunnedBlackout keeps it non-zero for the whole blackout round and clears it afterwards",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "0B7E": [
  {
   "name": "setupByte0B7E",
   "meaning": "settings-block byte ($DB) read by the $EC00 sub-overlay at $ED89",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "programChecksum",
   "meaning": "result of verifyProgramChecksum (sum of $0402-$87FF EOR $B0). Sent to the opponent in the version-handshake packet ($E02A, comm build 2 at $ED89); the disk image holds the mastered value $DB but the code actually computes $D9",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "0B7F": [
  {
   "name": "ownerNameString",
   "meaning": "21-byte owner name built from $FFDE/$FFE8, bit-7 terminated at $0B93; printed in the setup summary",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "ownerNameString",
   "meaning": "Bit-7-terminated owner/player name printed at $1616-$161A immediately before statsTabLabels on the STATS tab (text window left 10, column 3).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "0B94": [
  {
   "name": "setupByte0B94",
   "meaning": "settings-block byte read by $5829/$58F2/$ED83",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "setupByte0B94",
   "meaning": "settings byte written to film header offset $2F and verified on film load (game/scenario version)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "filmFormatVersion",
   "meaning": "byte compared with film header byte $2F by sub_58DE",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0B95": [
  {
   "name": "mapSeed",
   "meaning": "3 bytes ($0B95-$0B97) that the map drawing code (sub_6F04 etc.) consumes; manualLookupCheck temporarily replaces them with the manual answer entry so the protection map can be drawn",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapSeed",
   "meaning": "3-byte map seed $0B95-$0B97 set by commands $8B/$A6",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapSeed",
   "meaning": "three-byte map generator seed $0B95-$0B97, written by commands $8B and $A6",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapSeed",
   "meaning": "3-byte map seed ($0B95-$0B97) copied to the scenario RNG before generation; its low 5 bits select the symmetric-map mode when all set",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "mapSeed",
   "meaning": "3-byte (24-bit) seed of the current battlefield ($0B95-$0B97); equals the 5-letter map name packed 5 bits per letter; sent to the opponent with command $8B/$A6 and copied to $5A-$5C by $EC2B before generating",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "mapSeed",
   "meaning": "3-byte map generator seed ($0B95-$0B97); film header bytes $2B-$2D",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0B9B": [
  {
   "name": "soloModeFlag",
   "meaning": "bit 7 set = no-opponent/solo mode: skips modem handling, disk-serial copy and link messages (initial $FF)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "filmPlaybackFlag",
   "meaning": "bit 7 set while a game film is being played back (commands are not queued, number keys set the replay speed)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "playModeFlags",
   "meaning": "Bit 7 = solo/practice mode (F8 cycles $80-$82; digits 1-9 set a speed in sub_40B2); bit 0 = local player side, used by sub_2A08 and overlay A ($7BE6) to patch the bcc/bcs opcodes that decide whether unit indices < 50 or >= 50 are 'ours'.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "replayViewMode",
   "meaning": "bit 7 = replay/observer mode active; bits 0-1 = viewpoint: 0 = player 0's side, 1 = player 1's side, 2 = see everything (cycled $80->$81->$82 by a key at $4108)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "soloModeFlag",
   "meaning": "bit 7 set = solo/film mode: no comcen system damage rolls, no blitz sound, messages via sub_C560 suppressed",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "filmPlaybackFlag",
   "meaning": "bit 7 = film playback; bits 0-1 = film view mode 0 US, 1 THEM, 2 BOTH (cycled by HOME)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "filmPlaybackFlag",
   "meaning": "bit 7 set: commands come from the recorded film instead of the comm link (WATCH FILM); also suppresses recording and sending",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "filmPlaybackFlag",
   "meaning": "bit 7 = film playback / solo observer mode: commands are read from the film instead of the link and nothing is queued; bits 0-1 = replay viewpoint ($82 = see everything)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "playModeFlags",
   "meaning": "bit 7 = solo/film mode: disables VOICE PAUSE and system repairs",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "filmPlaybackFlag",
   "meaning": "0 for a live modem or solo-trainer game, $82 while watching a game film. Bit 7 is what checkFirePressedInteractive and isPlayerInputAllowed test to lock out player input; checkDiskId also skips the disk-serial copy when it is set.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "filmPlaybackFlag",
   "meaning": "Bit 7 = film playback / solo observer mode; repairs are refused because commands must not be queued during playback.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "filmPlaybackFlag",
   "meaning": "bit 7 = game film playback; cmdSessionControl skips everything except the abort path and the sound gates flip",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "filmPlaybackFlag",
   "meaning": "Bit 7 = film playback / solo observer mode. Read indirectly through getSoloOrFlag923F at $658B: while it is set the menu still runs but no repair is attempted (a film must not change game state).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "soloModeFlag",
   "meaning": "bit 7 = solo/film mode: serviceLinkWhileGenerating does nothing",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "filmPlaybackFlag",
   "meaning": "bit 7 = play back the film in $D000 ($82 set by WATCH GAME FILM: bits 0-1 = view both sides)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "soloModeFlag",
   "meaning": "bit 7 = solo/film playback mode: showDroneKillBurst does nothing",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "playModeFlags",
   "meaning": "Bit 7 (solo / film playback) suppresses the DRONE ALERT message.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "playModeFlags",
   "meaning": "Bit 7 = solo trainer / film playback (sub_C560 silently drops a message when it is set, showDroneAlert $8214 does the same). Bits 0-1 = film view mode, 0 US / 1 THEM / 2 BOTH, cycled $80 -> $81 -> $82 -> $80 by the CLR/HOME key at $4108-$4116.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "filmPlaybackFlag",
   "meaning": "Bit 7 set during film playback / solo trainer; suppresses queueMessageUnlessFilm and part of checkUiContinue",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "playModeFlags",
   "meaning": "Bit 7 = solo trainer / film playback. handlePauseKey uses it to choose between the local pause loop and the network pause protocol.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  },
  {
   "name": "filmPlaybackFlag",
   "meaning": "bit 7 set during WATCH GAME FILM; the trainer AI is disabled then",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "playModeFlags",
   "meaning": "bit 7 = solo trainer / film playback: makes switchToRequestedScreen keep $92F9 and stops $4546 from entering the COMCEN blackout",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "0B9C": [
  {
   "name": "linkOptionFlag",
   "meaning": "bit 7 set skips comm module initialisation and the modem prompt at game start (related start-up option, exact meaning unknown)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commSessionState",
   "meaning": "0 = no session, $80 = linked session, $FF = link lost; also sent to the comm module by sub_56F1",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commSessionState",
   "meaning": "0 = in-game exchange; bit 7 set = linked but outside the battle, only the keep-alive at L_56B1 runs",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commSessionState",
   "meaning": "0 = no session, 2 = game session agreed (READY handshake), $80 = link up at menu level (pollMenuSession), $FF = link lost",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "0B9F": [
  {
   "name": "playerSide",
   "meaning": "this player's side index (0/1; $FE = not chosen yet); overlay code uses it EOR 1 as the opponent index",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "localPlayerIndex",
   "meaning": "0 or 1: which side the local machine plays (units 0-49 belong to player 0, 50-99 to player 1)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "playerSide",
   "meaning": "local player's side 0/1; nonzero reverses the scan list order; overlay A uses it to patch the side-test branches in this chunk",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "playerSide",
   "meaning": "local player's side 0/1; EOR 1 gives the opponent",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "playerSide",
   "meaning": "this player's side 0/1",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "localPlayerIndex",
   "meaning": "0 or 1: decides the merge order (player 0's commands first) and the initial value of currentCommandOrigin",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "playerSide",
   "meaning": "local player's side (0/1); EOR 1 = opponent; decided by cmdChooseSide (bit 7 = conflict, random)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "playerSide",
   "meaning": "local player's absolute side 0/1, decided by cmdChooseSide (bit 7 = tie, resolved randomly); EOR 1 gives the opponent",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "playerSide",
   "meaning": "local side 0/1 ($FE/$FF = not chosen: then negotiated over the link into zp_18); alternates every second game via firstPlayFlag; side 0 chooses and sends the map seed",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "playerSide",
   "meaning": "local side 0/1; film header byte 5",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "playerSide",
   "meaning": "local side 0/1; EOR 1 = opponent (index into the per-side tables $92A6/$92AC)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "playerSide",
   "meaning": "Local player's side 0/1; EOR 1 = opponent; selects STATS columns.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "localPlayerIndex",
   "meaning": "0 or 1: the side played on this machine. computeGameResult compares it with the index of the side that lost ($2965, $298F) to choose between the WE LOST / WE WON pair, and the score display at $C6CE-$C6DC prints scores[localPlayerIndex ^ 1] (US) before scores[localPlayerIndex] (THEM).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "localPlayerIndex",
   "meaning": "Which side the local player is (0/1)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "opponentPlayerIndex",
   "meaning": "Side index (0/1) of the OPPONENT, not of the local player: drawStatusScoreLine prints D_0B9F EOR 1 under 'US:' and D_0B9F under 'THEM:', and computeGameResult ($2965/$298F) shows 'WE LOST'/'WE WERE KNOCKED OUT' when the winner index equals it.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "0BA0": [
  {
   "name": "playerSideSetup",
   "meaning": "side chosen in the setup screens, copied to playerSide at game start",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "playerSideSetup",
   "meaning": "copy of playerSide taken after the side was settled (read back by startGameFromSetup)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0BA1": [
  {
   "name": "firstPlayFlag",
   "meaning": "nonzero = this player moves first ('1ST PLAY' in the setup summary)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "playsFirstFlag",
   "meaning": "nonzero: this side has '1ST PLAY' in asymmetric scenarios, else '2ND PLAY'",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "firstPlayFlag",
   "meaning": "nonzero = this side plays first; incremented each game start (wraps at 2 and flips the side), forced to 1 when the map, options or flip state changed; written to the film header +$2E bit 1",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "playsFirstFlag",
   "meaning": "nonzero = this side plays first; film header byte $2E bits 1-2",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0BA3": [
  {
   "name": "gameOptions",
   "meaning": "bits 0-2 = game type index into the names at $CCC8 (0 Scrimmage, 1 QB Sneak, 2 The Bomb, 3 Face-Off, 4 Sluggers, 5 Full War, 6 Defender), bit 6 = custom rules",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameTypeOptions",
   "meaning": "bits 0-2 game type (0 SCRIMAGE,1 QB SNEAK,2 THE BOMB,3 FACE-OFF,4 SLUGGERS,5 FULL WAR,6 DEFENDER,7 M...), bit 4 DAMAGE option on, bit 6 CUSTOM rules",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameTypeOptions",
   "meaning": "bits 0-2 game type, bit 3 = stunned units of any kind are halted (otherwise only the comcen), bit 4 = DAMAGE option",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameTypeOptions",
   "meaning": "game option bits; bit 3 tested by the halt routine sub_3FAC",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameTypeOptions",
   "meaning": "game type/options byte, set on both machines by command $A4",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameTypeOptions",
   "meaning": "bit 3 = unit/group option menus available (set in the per-game-type table at $78CA for Face-Off, Sluggers, Full War and Defender; clear for Scrimmage, QB Sneak, The Bomb)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameTypeOptions",
   "meaning": "Game type/option byte; bit 4 = DAMAGE option, the master switch that enables the whole comcen repair feature tested at $17DA.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameTypeOptions",
   "meaning": "game type and option bits, set on both machines by command $A4",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameTypeOptions",
   "meaning": "bits 0-2 game type; types 1 (QB SNEAK) and 6 (DEFENDER) select special map styles",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "gameTypeOptions",
   "meaning": "bits 0-2 game type (0 Scrimmage,1 QB Sneak,2 The Bomb,3 Face-Off,4 Sluggers,5 Full War,6 Defender), bit 3 unit options, bit 4 damage, bit 6 custom; bit 7 (inferred) = hide the map name: terminator becomes '!' and player 1 sees '#####'",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "gameTypeOptions",
   "meaning": "bits 0-2 game type, bit 3 UNIT MENUS option, bit 4 DAMAGE option, bit 6 custom rules, bit 7 custom map; film header byte $0C",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "gameTypeOptions",
   "meaning": "bits 0-2 game type (0 SCRIMAGE .. 6 DEFENDER, names at $CCC8), bit 3 UNIT MENUS, bit 4 DAMAGE (both edited by editOptionBitOnOff), bit 6 CUSTOM rules, bit 7 custom MAP (adds 'MAP,' to the custom summary)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "gameTypeOptions",
   "meaning": "bits 0-2 game type, bit 4 = DAMAGE option; without DAMAGE the AI ignores its comcen's energy",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "gameTypeOptions",
   "meaning": "Bits 0-2 = game type (0 SCRIMAGE .. 6 DEFENDER); read at $EC4A to choose the AI plan.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "0BA5": [
  {
   "name": "linkTypeFlag",
   "meaning": "bit 7 selects the comm module build (set = build 1 from track 35; clear = build 2, the Hayes modem build with the Answer/Originate prompt); compared with $E030 to detect a needed reload",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commBuildFlag",
   "meaning": "copy of D_E030; bit 7 set = comm build 1 (T35): no phone pick-up/hang-up prompts, no VOICE PAUSE item",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commBuildFlag",
   "meaning": "bit 7 = comm build 1 (T35) resident: its hooks $E0E8 (per unit) and $E079 (per round) are called by the engine",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commBuildFlag",
   "meaning": "bit 7 = comm build 1: pause bits cleared at once, side forced to 0 by cmdChooseSide",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commBuildFlag",
   "meaning": "bit 7 = comm build 1 resident; the menu loop then calls sub_EEB9 each setup-phase iteration",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commBuildFlag",
   "meaning": "bit 7 = comm build 1 (T35): VOICE PAUSE item absent/ignored",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "linkTypeFlag",
   "meaning": "Copy of the comm module's $E030; bit 7 selects comm build 1 (track 35, the solo-trainer AI opponent) instead of build 2 (modem/null modem). loadCommModule branches on it.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commBuildFlag",
   "meaning": "bit 7 = comm module build 1 (solo trainer / no real opponent): getPauseClearMask clears both pause bits at once and cmdChooseSide forces side 0",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commBuildFlag",
   "meaning": "bit 7 = comm build 1 (track 35) resident = the built-in TRAINER opponent (its name 'TRAINER' at $884E is used instead of a link exchange, all units are revealed, the $EC00 sub-overlay from track 29 is run at game start)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "soloTrainerFlag",
   "meaning": "bit 7 set = PRACTICE WITH SOLO TRAINER (comm module build 1 from track 35 + its $EC00 extension from track 29 act as the AI opponent); 0 = COMPETE WITH MODEM OPPONENT; film header byte 4",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "commBuildFlag",
   "meaning": "bit 7 = comm build 1 (T35, solo trainer) resident: sub_8378 then forces recycler mode NONE for both sides",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "commBuildFlag",
   "meaning": "Bit 7 selects comm-module build 1 (track 35); when set, showWaitingForOpponent suppresses the 'WAITING FOR OPPONENT...' message.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  },
  {
   "name": "linkTypeAtStart",
   "meaning": "Bit 7 set selects comm build 1 (T35) and, in this tail, the solo trainer behaviour: canned opponent name, no handshake, F1 battle-display toggle enabled",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "0BA6": [
  {
   "name": "prevLinkTypeFlag",
   "meaning": "previous value of linkTypeFlag; a change from set to clear triggers the 'GET OPPONENT ON PHONE' prompt",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "prevLinkTypeFlag",
   "meaning": "set to $C8 when the link is given up after a timeout",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "0BA7": [
  {
   "name": "loadingDepth",
   "meaning": "nonzero while disk loads are in progress (keys suppressed); bit 7 makes the IRQ return right after servicing the comm module",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "diskLoadNestCount",
   "meaning": "incremented around overlay reloads (sub_1030)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "diskLoadDepth",
   "meaning": "Nesting counter bumped with INC/DEC around every disk load and overlay swap. Non-zero makes the IRQ discard keypresses (currentKey forced to $FF). The IRQ also tests bit 7 at $112F to skip the whole game half of the interrupt, but no traced writer ever makes it negative, so that early exit appears unreachable.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "loadingDepth",
   "meaning": "incremented around the track-29 sub-overlay load so the IRQ suppresses input",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0BA8": [
  {
   "name": "resumeGameFlag",
   "meaning": "nonzero makes menu item 2 the default and alters many screens; probably 'a game is in progress / saved'",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "noGameInProgress",
   "meaning": "nonzero when no game is running (menus); 0 during a game",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameOverFlags",
   "meaning": "bit 7 = game ended by knockout; bits 5-6: $60 = both knocked out (draw), $40 = player 1 won, $00 = player 0 won",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "noGameInProgress",
   "meaning": "nonzero when no game runs; applyHitToUnit ORs in $80 | $20 (side-0 comcen destroyed) or $80 | $40 (side-1 comcen destroyed) when a comcen dies",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameEndReason",
   "meaning": "0 while a game is in progress; nonzero ends it: $08 clock expired, $A0/$C0 = comcen of side 0/1 knocked out (bit 5/6 from unitFlagsTable bit 5), also set by menus when no game runs",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "noGameInProgress",
   "meaning": "0 while a game runs; set to 8 when the clock runs out",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "noGameInProgress",
   "meaning": "non-zero when no game is running; film playback then only keeps the link alive",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameEndReason",
   "meaning": "0 while a game runs; 1 = aborted/no game, $08 time up, $A0/$C0 knockout; bit 0 selects the film end marker ($FF vs $FE), bit 7 blocks cmdAddEnergy",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameEndReason",
   "meaning": "nonzero = no game running / game over; the menu loop then skips the sub_C451 abort check",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "noGameInProgress",
   "meaning": "0 while a game is running; any nonzero value disables the repair action.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameEndReason",
   "meaning": "0 while a game is in progress; nonzero blocks missile strikes and energy repairs and makes an abort use the short countdown",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameEndReason",
   "meaning": "cleared (game in progress) at session start",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "noGameInProgress",
   "meaning": "0 while a game runs (cleared by startFilmPlayback)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "gameOverFlag",
   "meaning": "Nonzero when the game is over / no game running; ends a drone flight immediately.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "gameOverFlags",
   "meaning": "End-of-game reason read by computeGameResult ($28FC). Bit 7 set = knockout (comcen destroyed), clear = time ran out and points decide. On the knockout path bits 5-6 select the loser: $60 = both, so a draw; $40 or higher = side 1 lost; below $40 = side 0 lost.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "noGameInProgress",
   "meaning": "Non-zero while no game is running (also carries the knock-out result bits); handlePauseKey returns immediately when it is set.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  },
  {
   "name": "gameEndReason",
   "meaning": "non-zero once the game has ended; stops the AI",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "gameOverFlags",
   "meaning": "non-zero ends the blackout loop early (game over / no game running)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "0BA9": [
  {
   "name": "setupName0BA9",
   "meaning": "6 bytes (shifted spaces by default) saved/restored around the manual check and copied from $0BAF by sub_1C87; a setup text field",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentSettingsText",
   "meaning": "6-byte text field ($A0 padded) of the current game settings, restored from $0BAF after a film",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapIdText",
   "meaning": "5-character base-32 map id ($0BA9-$0BAD, alphabet at $890C) written from the seed by sub_7850 at the start of generation",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "mapName",
   "meaning": "5 ASCII characters of the map name ($0BA9-$0BAD) from the 32-letter alphabet; spaces when empty; '#####' for player 1 when the name is hidden",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "settingsText",
   "meaning": "6-byte settings text; film header bytes 6-$0B; sub_86C3 writes CUSTOM into the $0332 copy",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "mapIdText",
   "meaning": "Map id string (bit 7 terminated) printed on the MAP ID row of the STATS tab.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "0BAF": [
  {
   "name": "setupName0BAF",
   "meaning": "6-byte backup of setupName0BA9",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingSettingsText",
   "meaning": "6-byte pending copy of currentSettingsText",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapNameBackup",
   "meaning": "6-byte copy of mapName+terminator kept with prevMapSeed; restored when the same seed is used again",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0C8A": [
  {
   "name": "perSideValueTable",
   "meaning": "2 bytes ($31,$63) indexed by player side in sub_3F57 and others",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastUnitOfSide",
   "meaning": "2 bytes $31,$63: index of the last unit (the comcen) of side 0 and side 1; units 0-49 belong to side 0, 50-99 to side 1",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenIndexBySide",
   "meaning": "unit index of each side's COMCEN: $31 (side 0), $63 (side 1)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenUnitBySide",
   "meaning": "2 bytes {$31,$63} = unit index of each side's COMCEN (49 and 99)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastUnitOfSide",
   "meaning": "$31,$63: comcen index of side 0/1 (used by the $9E comcen-repair handler)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenIndexBySide",
   "meaning": "two bytes $31,$63 - the COMCEN unit index of side 0 and side 1",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenUnitBySide",
   "meaning": "{$31,$63}: comcen unit index of side 0 and side 1, copied to $9236 by patchOwnSideBranches",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "10EF": [
  {
   "name": "spriteEnableScratch",
   "meaning": "copy of the sprite enable shadow written by the IRQ each frame",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  }
 ],
 "10FF": [
  {
   "name": "irqBrkVector",
   "meaning": "operand of the JMP taken when the interrupt was caused by BRK (normally irqExit $1293)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  }
 ],
 "131A": [
  {
   "name": "cursorBlinkTimer",
   "meaning": "30-frame countdown for blinkCursorColor",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  }
 ],
 "1412": [
  {
   "name": "menuFlag1412",
   "meaning": "set to $7F by enterMainMenu; not read inside this chunk",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "loopCounter1412",
   "meaning": "loop counter used by repairTabAction (marker loops)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "systemMarkerLoopIndex",
   "meaning": "Scratch byte used by repairTabAction as the 3..0 loop counter of the marker erase/redraw loops. The only other write is the $7F stored by enterMainMenu at $141F, which is never read - dead code. A better name than the current menuFlag1412.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "1413": [
  {
   "name": "menuLinkStateCopy",
   "meaning": "copy of linkState ($922A) taken when the menu panel was drawn; a bit-7 change triggers a redraw",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastStunFlagShown",
   "meaning": "copy of D_922A taken when the REPAIR tab was drawn; bit 7 compared to detect stun changes",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  }
 ],
 "9000": [
  {
   "name": "vicCtrl1Shadow",
   "meaning": "shadow of $D011 copied by the IRQ",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "vicControl1Shadow",
   "meaning": "shadow of $D011 (low 3 bits = vertical fine scroll)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "vicCtrl1Shadow",
   "meaning": "Shadow of $D011 ($3B = bitmap mode) written by initBitmapScreen",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "vicCtrl1Shadow",
   "meaning": "Shadow of VIC_CTRL1 ($D011), copied to the VIC in the first phase of every IRQ (together with $9001) so it is updated even while a disk load blocks the rest of the handler.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "9001": [
  {
   "name": "vicCtrl2Shadow",
   "meaning": "shadow of $D016",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "vicD016Shadow",
   "meaning": "Shadow of VIC $D016 copied by the IRQ ($1121); $D8 = multicolour bitmap, 40 columns.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "vicControl2Shadow",
   "meaning": "shadow of $D016 (low 3 bits = horizontal fine scroll, bit 4 = multicolour)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "vicCtrl2Shadow",
   "meaning": "Shadow of $D016; bit 4 (multicolour) is tested by the character drawer sub_2AC8 to decide whether colour RAM must be written as well",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "vicCtrl2Shadow",
   "meaning": "Shadow of VIC_CTRL2 ($D016).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "vicD016Shadow",
   "meaning": "Shadow of $D016; bit 4 (multicolour) selects the multicolour font at $98BE and colour-RAM output, otherwise the hires font at $9A96 and screen-RAM output",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "vicCtrl2Shadow",
   "meaning": "shadow of VIC $D016; bit 4 (multicolour) selects which of the two fonts inverted by this chunk the $C000 plotter uses",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ],
 "9002": [
  {
   "name": "vicMemPtrShadow",
   "meaning": "shadow of $D018",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "vicMemPtrShadow",
   "meaning": "Shadow of $D018 ($38: screen $8C00, bitmap $A000)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "vicMemPtrShadow",
   "meaning": "Shadow of VIC_MEM_PTR ($D018) - bitmap/screen base selection.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "9003": [
  {
   "name": "borderColorShadow",
   "meaning": "shadow of $D020",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "borderColorShadow",
   "meaning": "Shadow of $D020",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "vicBorderShadow",
   "meaning": "Shadow of the border colour ($D020).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "borderColorShadow",
   "meaning": "shadow of $D020; set to $0C for the radar screen",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "borderColorShadow",
   "meaning": "VIC border colour shadow copied to $D020 by the raster IRQ; queueAiCommand sets it to 6 to signal a command-queue overflow",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "9004": [
  {
   "name": "bgColorShadow",
   "meaning": "shadow of $D021",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "vicBackgroundShadow",
   "meaning": "Shadow of VIC $D021 (IRQ $1143).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "bgColorShadow",
   "meaning": "Shadow of $D021",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "bgColorShadow",
   "meaning": "shadow of $D021, set to 7 by drawOverviewMap",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "vicBg0Shadow",
   "meaning": "Shadow of background colour 0 ($D021); $9005 and $9006 shadow $D022/$D023 for multicolour bitmap mode.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "bgColorShadow",
   "meaning": "shadow of $D021; set to $0C for the radar screen",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9005": [
  {
   "name": "bgColor1Shadow",
   "meaning": "shadow of $D022",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  }
 ],
 "9006": [
  {
   "name": "bgColor2Shadow",
   "meaning": "shadow of $D023",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  }
 ],
 "9008": [
  {
   "name": "spriteColorShadow",
   "meaning": "8 bytes -> $D027-$D02E; values >= $10 hold two colours in the nibbles that alternate every other frame pair (flashing)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteColorShadow",
   "meaning": "8 sprite colour shadows ($9008-$900F) copied to $D027+ by the IRQ; $9009 = sprite 1 (path marker).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteColorShadow",
   "meaning": "8 sprite colour shadows ($D027-$D02E); cursor shape setters write 0 (sprite 0) or $0C",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteColorShadow",
   "meaning": "sprite 0 colour = 1 for the drone marker",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteColorShadow",
   "meaning": "Eight sprite colour shadows $9008-$900F. A value below $10 is written straight to $D027+n; $10 and above packs two colours in the two nibbles and the IRQ alternates between them every other frame to make the sprite flash.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteColourShadow",
   "meaning": "shadow of sprite 0's colour register; set to 1 for the drone marker",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteColorShadow",
   "meaning": "Sprite colour shadows; $9008 sprite 0 = 1 white, $900A sprite 2 = 0 black reticle, $900B sprite 3 = $0B/$06 line shadow, $900D sprite 5 = 2 red silhouette.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9010": [
  {
   "name": "spriteXShadow",
   "meaning": "8 sprite X positions (low byte), copied to $D000.. plus the fine-scroll X offset",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteXShadow",
   "meaning": "8 sprite X shadows ($9010-$9017); $9011 = sprite 1.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteXShadow",
   "meaning": "8 sprite X positions written by positionCursorSprite",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteXShadow",
   "meaning": "sprite X shadows; sprite 0 = radar cursor",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteXShadow",
   "meaning": "sprite 0 X = drone screen position",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteXShadow",
   "meaning": "Eight sprite X-position shadows $9010-$9017, written to $D000+2n with the fine-scroll bias from $92F4 added.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteXShadow",
   "meaning": "shadow of sprite 0's X coordinate = drone screen position",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteXShadow",
   "meaning": "Sprite X shadows; $9010 sprite 0 (heading line, $37), $9012 sprite 2 (reticle, $AC), $9013 sprite 3 (line shadow, $38), $9015 sprite 5 (drone silhouette, $38).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9018": [
  {
   "name": "spriteYShadow",
   "meaning": "8 sprite Y positions, copied to $D001.. plus the fine-scroll Y offset",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteYShadow",
   "meaning": "8 sprite Y shadows ($9018-$901F); $9019 = sprite 1.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteYShadow",
   "meaning": "8 sprite Y positions written by positionCursorSprite",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteYShadow",
   "meaning": "sprite Y shadows",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteYShadow",
   "meaning": "sprite 0 Y = drone screen position",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteYShadow",
   "meaning": "Eight sprite Y-position shadows $9018-$901F, written to $D001+2n with the fine-scroll bias from $92F5 (minus 3) added.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteYShadow",
   "meaning": "shadow of sprite 0's Y coordinate",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteYShadow",
   "meaning": "Sprite Y shadows; $9018 sprite 0 ($64), $901A sprite 2 ($88), $901B sprite 3 ($65), $901D sprite 5 ($64).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9020": [
  {
   "name": "spriteXMsbShadow",
   "meaning": "shadow of $D010",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteXMsbShadow",
   "meaning": "Shadow of VIC $D010.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteXMsbShadow",
   "meaning": "shadow of $D010; bit 2 set puts the cursor sprite at X=256",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteMsbXShadow",
   "meaning": "Shadow of VIC_SPR_HI_X ($D010).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteXMsbShadow",
   "meaning": "Shadow of VIC $D010; showUnitPictureSprites ORs in $C0 to give sprites 6 and 7 X coordinates above 255.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  },
  {
   "name": "spriteXMsbShadow",
   "meaning": "shadow of VIC $D010; cleared on every screen switch, set to $C4 when entering the battlefield map",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "9022": [
  {
   "name": "spriteEnableShadow",
   "meaning": "shadow of $D015",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteEnableShadow",
   "meaning": "Shadow of VIC $D015 (sub_31A3 enables, sub_32EA disables sprite 1).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteEnableShadow",
   "meaning": "Shadow of $D015; set through enableSprites, cleared by initSpritePointers/clearCurrentScreenWindows",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteEnableShadow",
   "meaning": "shadow of $D015; sub_31A3 sets bits, the routines here clear bits 1/3/4 directly",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteEnableShadow",
   "meaning": "sprite enable shadow; bits 3/4 cleared when marker timers $91C8/$923E expire",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteEnableShadow",
   "meaning": "shadow of $D015; bit 0 cleared by cmdDroneMove",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteEnableShadow",
   "meaning": "Shadow of VIC_SPR_ENA ($D015); the IRQ also copies it to spriteEnableScratch ($10EF).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteEnableShadow",
   "meaning": "shadow of $D015; cmdDroneMove clears bit 0 before deciding whether the drone is visible",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteEnableShadow",
   "meaning": "cleared after the unit setup phase to hide the cursor sprites",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "spriteEnableShadow",
   "meaning": "shadow of VIC $D015 (cleared after the recycler is placed)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "spriteEnableShadow",
   "meaning": "shadow of $D015; cleared after the recycler has been located",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "spriteEnableShadow",
   "meaning": "Shadow of $D015; the drone screen enables sprites 0+3 ($09), sprite 2 (4) and sprite 5 via loadDirectionShapeSprite.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "spriteEnableShadow",
   "meaning": "Shadow of VIC $D015. handlePauseKey snapshots it before clearing the screen for a pause and re-ORs it through enableSprites afterwards.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  },
  {
   "name": "spriteEnableShadow",
   "meaning": "software shadow of VIC $D015; cleared here together with the register",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  },
  {
   "name": "spriteEnableShadow",
   "meaning": "shadow of VIC $D015, cleared together with the register on a screen switch",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "9023": [
  {
   "name": "spriteXExpandShadow",
   "meaning": "shadow of $D01D",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteXExpandShadow",
   "meaning": "Shadow of VIC $D01D.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteXExpandShadow",
   "meaning": "Shadow of $D01D; bits 0-1 set for the formation sprites 0/1",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteXExpandShadow",
   "meaning": "shadow of $D01D (bit 2 set for the cursor sprite)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteExpandXShadow",
   "meaning": "Shadow of VIC_SPR_EXP_X ($D01D).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteXExpandShadow",
   "meaning": "shadow of VIC $D01D, cleared on a screen switch",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "9024": [
  {
   "name": "spriteYExpandShadow",
   "meaning": "shadow of $D017",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteYExpandShadow",
   "meaning": "Shadow of VIC $D017.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteYExpandShadow",
   "meaning": "Shadow of $D017; bits 0-1 set for the formation sprites 0/1",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteYExpandShadow",
   "meaning": "shadow of $D017",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteExpandYShadow",
   "meaning": "Shadow of VIC_SPR_EXP_Y ($D017).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteYExpandShadow",
   "meaning": "shadow of VIC $D017, cleared on a screen switch",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "90E9": [
  {
   "name": "lastKeyCode",
   "meaning": "key code seen on the previous poll (repeat detection), $FF none",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastKeyCode",
   "meaning": "Raw code of the key reported last frame; pollKeyboardEvent compares against it to implement auto-repeat.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "90EA": [
  {
   "name": "newKeyEvent",
   "meaning": "key code of a fresh press/repeat this frame, $FF none; consumers reset it to $FF",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentKeyCode",
   "meaning": "Key currently held as reported by $E015 ($FF = none); D_90E9 is the previous value and zp_70 the key repeat timer (IRQ key handler $0E56).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "newKeyEvent",
   "meaning": "key code $20 injected by the setup-ready command",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "newKeyEvent",
   "meaning": "One-shot 'a new printable key was pressed this frame' code ($FF = none). Consumed by handleChatSend and the $C000 line editor, which clear it back to $FF.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "newKeyEvent",
   "meaning": "pending key code from the keyboard scanner; command $9E injects $20 (SPACE) to release a 'press space' wait loop",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "newKeyEvent",
   "meaning": "fresh key press this frame ($FF none); enterMapName consumes it and resets it to $FF",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "newKeyEvent",
   "meaning": "Raw key code of the most recent new key press, produced by pollKeyboardEvent in the IRQ; $FF = none. handleTextEntryKey is the consumer for typed text.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "90EE": [
  {
   "name": "joyIdleCounter",
   "meaning": "counts frames with the stick centred and fire released (saturates at $FF); reset while the stick is deflected",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "idleFrameCounter",
   "meaning": "Frames since the joystick was last moved or fire pressed (IRQ $1227-$1235, saturates at $FF). 15 frames -> info panel, 90 frames -> path preview.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "idleFrameCounter",
   "meaning": "frames since the last joystick activity; the group editor shows the hover info panel after 30",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "inputIdleFrames",
   "meaning": "Counts frames with the joystick centred and the fire button up, saturating at $FF; any input resets it to 0. Read by the map/console loops ($2042, $21C0, $24F3, $63E9) to time out prompts.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "90F9": [
  {
   "name": "menuSelection",
   "meaning": "main-menu item index 0-2",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuTabIndex",
   "meaning": "selected tab of the in-game menu screen: 0 STATS, 1 REPAIR, 2 MISC",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  }
 ],
 "90FB": [
  {
   "name": "currentScreen",
   "meaning": "active screen selected by F1/F3/F5/F7 key codes 0-3 (0 = battlefield, 1 = overlay screen $7587, 2 = overlay screen $7FE6, 3 = main menu)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentScreenId",
   "meaning": "active screen: 0 battlefield, 1 ($7587), 2 ($7FE6), 3 STATS/REPAIR/MISC menu",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentGameMode",
   "meaning": "Game mode currently running (compared with zp_66 by sub_06BA); 0 = battlefield map.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentScreen",
   "meaning": "Active screen id 0-3 (0 battlefield, 1 overlay-B screen, 2 overlay-B screen drawn by $821F, 3 menu) dispatched by clearCurrentScreenWindows",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentScreenId",
   "meaning": "0 = battlefield (shots are drawn only then)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentScreen",
   "meaning": "0 = battlefield view; haltAllUnitsAndReveal redraws only then",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentScreenId",
   "meaning": "0 = battlefield screen",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentScreen",
   "meaning": "0 battlefield (viewport + overview updated), 1 overlay-B map screen (drawScreen1Cell, drone sprite)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentScreen",
   "meaning": "Currently displayed screen (0 = battlefield map, 3 = main menu, others for setup/console); the IRQ only blinks the cursor colour while it is 0.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentScreen",
   "meaning": "active screen id; cmdDroneMove only touches sprite 0 while it is 1 (the drone/radar screen)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentScreen",
   "meaning": "set to 3 (menu screen) at session start",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "currentScreen",
   "meaning": "active screen id (0 = battlefield)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "currentScreen",
   "meaning": "0 = battlefield screen; sub_8364 forces it to 0",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "currentScreenId",
   "meaning": "Active screen; >= 3 (menu screen) moves the message line from row 23 to row 22",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "currentScreen",
   "meaning": "Active screen id (0 = battlefield, 1 = map/radar overlay, 2 = unit setup, 3 = menu). handlePauseKey skips the window clear on screen 3 and only redraws the overview/viewport on screen 0.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  },
  {
   "name": "currentScreen",
   "meaning": "active screen id; 1 = the overlay-B radar/drone screen. Only then does the drone weave.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  },
  {
   "name": "currentScreenId",
   "meaning": "screen currently running: 0 battlefield map, 1 missile screen ($7587), 2 drone screen ($7FE6), 3 console/main menu",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "90FC": [
  {
   "name": "countdown90FC",
   "meaning": "per-frame countdown timer",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "inputHoldTimer",
   "meaning": "Frame countdown (IRQ $12E5); while nonzero the per-frame housekeeping/input is skipped in the cursor loops.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "stepCountdown",
   "meaning": "frames until the next engine step (decremented by the IRQ); reloaded from stepIntervalFrames",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameTimer90FC",
   "meaning": "General per-frame countdown decremented by updateFrameTimers; used around $21B8/$2302/$41BD.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameGateCounter",
   "meaning": "non-zero means the current frame's work is not finished; the blackout loop spins on it before calling updateGameFrame",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "9163": [
  {
   "name": "countdown9163",
   "meaning": "countdown decremented every 4 frames",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "exchangeThrottleTimer",
   "meaning": "reloaded with 10 after each packet exchange and decremented every 4 frames by the IRQ; serviceCommandExchange does nothing while it is non-zero",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "linkPollDelay",
   "meaning": "countdown (4-frame units) set to 10 after each processed packet; pollOpponentLink is idle while nonzero (not in film mode)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "exchangeRetryTimer",
   "meaning": "Countdown decremented every 4 frames; reloaded with 10 at $4E64 when a command exchange completes, so it paces the link packet exchange ($4DB1 tests it).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "91C8": [
  {
   "name": "countdown91C8",
   "meaning": "per-frame countdown timer",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "shotLineTimer",
   "meaning": "frames the shot-line sprite 3 stays visible (set to 8; $415A disables the sprite when it reaches 0)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "marker3Timer",
   "meaning": "countdown; sprite 3 is switched off when it reaches 0",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameTimer91C8",
   "meaning": "Per-frame countdown decremented by updateFrameTimers, loaded at $3B1E and tested at $415A.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "91C9": [
  {
   "name": "joyCenteredMask",
   "meaning": "cleared by the IRQ while the stick is centred, set to $7F by setJoystickRepeatDelay; masks joyHoldCounter",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scrollRepeatMask",
   "meaning": "$7F while the joystick is held, 0 after it returns to centre (IRQ $1220); makes the first auto-repeat delay long (16) and later ones short (6) in sub_0D49.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joyCenteredMask",
   "meaning": "$7F while the stick is held, 0 after it returned to centre; masks joyHoldCounter for the auto-repeat delay",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joyCenteredMask",
   "meaning": "Auto-repeat mask ANDed into joyHoldCounter; the IRQ clears it to 0 whenever the stick is centred, and setJoystickRepeatDelay reloads it with $7F.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "91CA": [
  {
   "name": "joyHoldCounter",
   "meaning": "number of consecutive auto-repeat steps for a held joystick direction",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scrollRepeatCounter",
   "meaning": "Incremented per scroll step by sub_0D49 and masked with scrollRepeatMask.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joyHoldCounter",
   "meaning": "consecutive auto-repeat steps of a held direction (first step 16 frames, later 6)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  }
 ],
 "9223": [
  {
   "name": "counter9223",
   "meaning": "incremented every 8 frames while negative (saturates at $FF)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "opponentWaitTimer",
   "meaning": "nonzero while waiting for the opponent's data for the current command slice ($80 + elapsed/8 frames); postpones the engine step",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "exchangeTimeoutCounter",
   "meaning": "cleared to 0 when a packet arrives; counts up every 8 frames while negative (IRQ) and is evaluated by $5A14 (link lost at $FF)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "linkTimeoutCounter",
   "meaning": "0 after a packet arrived; checkLinkTimeout sets bit 7 and the IRQ then increments it every 8 frames ($E1 = PHONE TROUBLE, $FF = give up/reconnect)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "linkTimeoutCounter",
   "meaning": "Packet-exchange watchdog. checkLinkTimeout ($5A14) ORs in bit 7 to arm it; updateFrameTimers then counts it up towards $FF every 8 frames (16 while zp_B1 = $FF); a completed exchange clears it at $4E69. Reaching $E1 triggers the retry/'TRY AGAIN' path.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "922A": [
  {
   "name": "linkState",
   "meaning": "bit 7 = link/modem session state used to choose menu text colours and default item; bits 6/4-5 set in the $0709 screen code",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenStunStatus",
   "meaning": "bits 0-3 stun counter of the comcen (from D_F960), bit 7 set = stunned; bit 6 set during the knock-out sequence",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "linkState",
   "meaning": "Bit 6 set blocks enableSprites (sprites stay off)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenStunStatus",
   "meaning": "bit 6 set while the comcen is knocked out (no sprite enabling, no shot drawing)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenStunStatus",
   "meaning": "own comcen stun count (bits 0-3) with bit 7 when stunned; $FF triggers the knock-out round L_0709, bit 6 set during it",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "linkState",
   "meaning": "bit 6 = knock-out sequence running: drawMapCell does nothing",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenStunStatus",
   "meaning": "bit 7 = comcen stunned; aborts the repair menu from inside toggleMenuRowHighlight",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenStunStatus",
   "meaning": "Own comcen stun state, recomputed at $4549 as (unitStunTable[comcenUnit] & $0F) with bit 7 ORed in when nonzero; $FF triggers the knock-out sequence. repairTabAction only offers the stun repair while the low nibble is below 8.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenStunStatus",
   "meaning": "Comcen stun status; bit 7 set = comcen stunned. Not read by this chunk directly, but toggleMenuRowHighlight checks it on every blink while selectedUnit is $FF and forces this menu to exit if the comcen gets stunned while it is open.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenStunnedFlag",
   "meaning": "Bit 7 set makes the console status area print msgComcenStunned ($8742) at column 5 ($17C4-$17D4). Bit 6 is tested separately at $40BA on the non-film path of the same input routine.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "comcenStatus",
   "meaning": "command-centre status: bits 0-3 stun counter, bit 7 stunned; value $FF triggers the blackout, which sets bit 6 (blocks sprite enabling) and finally masks with $8F",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "9234": [
  {
   "name": "countdown9234",
   "meaning": "per-frame countdown that stops at 0 or $80",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewRedrawTimer",
   "meaning": "countdown; when it reads $80 (expired) the battlefield view is redrawn and it is cleared",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameTimer9234",
   "meaning": "Per-frame countdown decremented by updateFrameTimers except when it holds the sentinel value $80, which freezes it.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewRedrawTimer",
   "meaning": "Set to $8F by drawShotLineInViewport: the battlefield view is redrawn (erasing the line) when it counts down to $80.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9236": [
  {
   "name": "currentMapIndex",
   "meaning": "index into the table at $F9C4 (low 6 bits of the entry identify the map/setup)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenUnit",
   "meaning": "index of the player's COMCEN unit (indexes the unit tables)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "revealedEnemyUnit",
   "meaning": "Unit index that may be inspected/drawn even though it is not ours (set by overlay A $7BF5 and the comm module).",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenUnit",
   "meaning": "index of the local player's COMCEN (49 or 99)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenUnit",
   "meaning": "index of the local player's comcen unit (49 or 99)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "ownComcenUnit",
   "meaning": "index of the player's comcen",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenUnit",
   "meaning": "index of the local player's comcen (49/99)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenUnit",
   "meaning": "index of the player's comcen; it gets no JOIN GROUP/target items and cannot be added to or removed from a group",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenUnit",
   "meaning": "Index of the local player's COMCEN unit (49 for side 0, 99 for side 1); used to index unitEnergyTable/unitCol/unitRow/unitDisplayFlags and as the unit parameter of the $80 and $9A commands issued here.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenUnit",
   "meaning": "index of the local player's COMCEN ($31 or $63)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenUnit",
   "meaning": "index of the local player's comcen (49 or 99) set by patchOwnSideBranches",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "comcenUnit",
   "meaning": "Index of the own comcen unit: 49 for side 0, 99 for side 1 (from D_0C8A); its position is the centre of both overlay-B screens",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "comcenUnit",
   "meaning": "index of the player's comcen (49 or 99); the radar grid is centred on it at grid cell (10,8)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "comcenUnit",
   "meaning": "Index of the player's comcen unit (49 for side 0, 99 for side 1; EOR $52 gives the other comcen).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "ownComcenUnit",
   "meaning": "Unit index of the local side's command centre (49 or 99); used by updateComcenMoveSound to test its idle flag.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  },
  {
   "name": "comcenUnit",
   "meaning": "Unit index of the comcen the radar grid is centred on; isUnitOnAiRadar flips it to $63 and back to $31",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "comcenUnitIndex",
   "meaning": "unit index that mapCellToRadarGrid centres the radar window on; normally $31 (= 49, the human's comcen). This chunk deliberately leaves it alone, so $EEE1 tests visibility on the human's radar (the module's own $EE00 image instead sets it to $63 around its call).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "923E": [
  {
   "name": "countdown923E",
   "meaning": "per-frame countdown timer",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "markerTimer",
   "meaning": "countdown gating updateMarkerSprite; never set nonzero in this build (only by dead code)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "marker4Timer",
   "meaning": "countdown; sprite 4 is switched off when it reaches 0",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameTimer923E",
   "meaning": "Per-frame countdown decremented by updateFrameTimers, loaded at $3407 and tested at $341F/$416E.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "9248": [
  {
   "name": "lastTypedKey",
   "meaning": "last key code >= $80 (PETSCII-like, shift-translated); menus poll it; $FF none",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastKeyPressed",
   "meaning": "last key as ASCII | $80 ($A0 = space, $B1-$B9 digits), $FF = none",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastKeyCode",
   "meaning": "Last key pressed ($FF = none); PETSCII with bit 7 set ($A0 space, $B1-$B9 digits, $AA '*'), F1/F3/F5/F7 = $00-$03.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastKeyPressed",
   "meaning": "last typed key code: $B1-$B9 digits, $8C = CLR/HOME (keyboard table $0D67), $FF none",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastTypedKey",
   "meaning": "Last printable key (PETSCII|$80) typed; used by the $C000 text/menu code and cleared to $FF when a key is consumed.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastKeyCode",
   "meaning": "last key pressed, $A0 = space, $FF = none",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "lastKeyCode",
   "meaning": "last typed key with bit 7 set ($AA = '*'), $FF = none; consumed by handleStarKeyToggle",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "lastKeyPressed",
   "meaning": "Last typed key code with bit 7 set ($B1-$B9 = digits 1-9, $8C = CLR/HOME, $8E seen at $C699), $FF = none. Consumed and reset to $FF by the film replay key handler at $40C5-$40E8 and $40F7-$4100.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "lastTypedKey",
   "meaning": "Last typed key code; $A0 (SPACE) aborts modal UI, $8B/$8E drive the message review handler",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "lastKeyCode",
   "meaning": "Last typed key code (ASCII|$80 or a special code), $FF = consumed. Codes used in this chunk: $8B CRSR up/down = message history, $8E CRSR left/right = score line, $93 RUN/STOP = pause.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "9251": [
  {
   "name": "messageQueueHead",
   "meaning": "message queue index (9-entry queue at $9249-$9251 managed by $C566/$C6EF)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "messageQueueHead",
   "meaning": "Write index of the 8-entry status-message queue at $9249-$9250; compared against messageQueueTail ($9252) to decide whether a message is pending. The comparison at $1400 in appendChatCharacter discards its result (dead code).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "messageQueueHead",
   "meaning": "message queue index; showToggleStateMessage copies it to the tail to flush the queue",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "messageQueueHead",
   "meaning": "Write index (next free slot) of messageQueue, 0-7",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "messageQueueHead",
   "meaning": "Insert index of the message ring; queue is empty when it equals messageQueueTail.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "9252": [
  {
   "name": "messageQueueTail",
   "meaning": "message queue index",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "messageQueueTail",
   "meaning": "Read index of the status-message queue at $9249-$9250, advanced by the $C000 message pump ($C624).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "messageQueueTail",
   "meaning": "message queue index (8-entry queue at $9249)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "messageQueueTail",
   "meaning": "Read index (next message to show) of messageQueue, 0-7",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "messageQueueTail",
   "meaning": "Read index of the message ring; temporarily rewound by handleMessageReviewKeys while scrolling back through old messages.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "9253": [
  {
   "name": "chatInputBuffer",
   "meaning": "36-byte buffer of the incoming chat message",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "incomingChatBuffer",
   "meaning": "36-byte buffer $9253-$9276 holding the chat message being received from the opponent, characters stored without bit 7.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "chatTextBuffer",
   "meaning": "raw characters of the chat line being typed; mirrored into $0549.. (with bit 7 on the last one) by $13EF so the message printer can display it",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "9277": [
  {
   "name": "chatInputLength",
   "meaning": "length/cursor of the incoming chat message; $80 = message complete",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "incomingChatLength",
   "meaning": "Number of characters currently in incomingChatBuffer; $80 marks a completed message (RETURN received) so the next character starts a new one.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "chatInputLength",
   "meaning": "chat input length, cleared by waitForSpaceKey",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "chatTextLength",
   "meaning": "Number of received chat characters buffered at $9253; non-zero makes the status line show the chat line",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "9278": [
  {
   "name": "chatFlag9278",
   "meaning": "set to $9E when chat typing mode is entered",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "outgoingLineBuffer",
   "meaning": "36-byte line-edit buffer $9278-$929B, filled by the $C000 editor $C7D5 with a trailing cursor glyph $9E. Shared between the outgoing chat message and the SAVE GAME FILM name entry, which is why handleChatSend stands down while $92E6 is non-zero.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textInputBuffer",
   "meaning": "line buffer edited by the key handler sub_C7D5 (terminator $9E); holds the film name in runSaveFilmMenu",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "typedLineBuffer",
   "meaning": "Locally typed chat / name entry line, bit-7 terminated, drawn by drawTypedChatLine",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "textEntryBuffer",
   "meaning": "36-byte line buffer used by handleTextEntryKey for chat text and film names; always terminated with $9E. Note the scroll loop at $C81D reads one byte past the end ($929C = chatState).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "929C": [
  {
   "name": "chatState",
   "meaning": "chat typing state: 0 idle, $C0 typing, $7F pending, bit 7 = pending send",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "chatState",
   "meaning": "reset to 0 by pause/resume/ready commands",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "chatState",
   "meaning": "Chat/typing state: 0 = idle (link down), bit 7 set = typing a message to the opponent, bit 6 = the chat line is displayed, bits 0-5 = number of characters typed so far (max 35). Also poked by the $C000 message code ($C49C/$C4BA/$C4D1) and by $1BD3/$29AD/$5224/$5279/$7DBF.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "chatState",
   "meaning": "chat input state; reset by the pause/resume/ready commands",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "chatState",
   "meaning": "chat typing state, cleared by waitForSpaceKey",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "chatState",
   "meaning": "bit 7 set = chat text entry in progress; handleStarKeyToggle ignores keys then",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "chatState",
   "meaning": "Chat/typing state: bit 7 = typing mode, bit 6 = the incoming chat line is being peeked at (key $8B held), positive non-zero = the typed line must be redrawn once",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "chatState",
   "meaning": "non-zero while the player is typing a chat line; blocks screen switching",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "92A0": [
  {
   "name": "rawKeyCode",
   "meaning": "raw result of the comm module key poll ($E015)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rawKeyCode",
   "meaning": "Unfiltered key code returned by scanKeyboard this frame (before repeat filtering); read by the $C000 cursor-key handler at $C4B1.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rawKeyCode",
   "meaning": "Raw key code of the key currently held, stored by the IRQ key poller ($0E59)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "92A1": [
  {
   "name": "messageState",
   "meaning": "message-line state (0 none, 1 loading notice, $21 modem prompt) passed to $C6FF after a retried load",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "persistentMessageId",
   "meaning": "id of the status message kept on screen (0 = none)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "statusMessageNumber",
   "meaning": "Number of the status-bar message last requested (10 = 'SETUP UNITS. PRESS F 1 WHEN DONE.').",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentMessageId",
   "meaning": "id of the message currently displayed; $9E = none",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "persistentMessageId",
   "meaning": "status message kept on screen: $0B waiting for opponent, $19 timeout, $9E none",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "messageState",
   "meaning": "Status-line state byte; set to 1 by suspendCommForDiskAccess (drive busy notice) and cleared by resumeCommAfterDiskAccess.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "persistentMessageId",
   "meaning": "status message that stays on screen: $19 TIMEOUT, 0 = none",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "messageState",
   "meaning": "persistent message id: $1A while player 1 waits for the map seed; cleared by the wait routines",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "statusMessageId",
   "meaning": "id of the status message on screen ($1E = dynamic slot used for the INSERT DISK / CUSTOM prompts, 0 = none)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "persistentMessageId",
   "meaning": "id of the status-line message that stays/returns after transient messages (0 none, $0A 'SETUP UNITS...', $1E the dynamic slot used by this code)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "persistentMessageId",
   "meaning": "Message id the status line falls back to when the queue drains; $9E = none. computeGameResult ($28F1) stores the chosen result id ($11-$15) here, queues it, then resets it to $9E.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "persistentMessageId",
   "meaning": "Message id redisplayed when the queue runs dry ($9E = none)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "persistentMessageId",
   "meaning": "Message id the status line falls back to when the queue is empty; 0 = blank, negative = draw the CLICKS/US/THEM score line instead.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "92C7": [
  {
   "name": "retryCounter",
   "meaning": "manual-check tries left; cleared at game start",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  }
 ],
 "92C8": [
  {
   "name": "spriteScrollActive",
   "meaning": "nonzero = add the fine-scroll offsets from $92F4/$92F5 to the sprite positions",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "screenShakeTimer",
   "meaning": "frames of screen shake left (set to 8 on an explosion at $3B48); also bounds the random scroll offsets",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteShakeFrames",
   "meaning": "set to 8 when the local comcen is hit; while nonzero the IRQ adds the fine-scroll offsets to the sprite positions (screen shake)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteScrollActive",
   "meaning": "nonzero: apply the fine-scroll shake (sub_29C1) once per step and decrement",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteScrollEnable",
   "meaning": "When zero the IRQ writes the sprite shadows at their nominal positions; when non-zero it adds the fine-scroll offsets from $92F4/$92F5 so sprites track the smooth-scrolled tactical view.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "92C9": [
  {
   "name": "counter92C9",
   "meaning": "incremented every 8 frames while positive, saturating at $80",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameEndFlag",
   "meaning": "nonzero once the game is over/aborted (bit 7 = return to menu pending); aborts waits",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pauseTimeoutCounter",
   "meaning": "Counts up every 8 frames while 0 < value < $80 (IRQ $12C1); nonzero blocks the hover info and interrupts displays (sub_C451).",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameEndCountdown",
   "meaning": "set to $78 by endGame; nonzero = game over sequence running (aborts waits, ticks in sub_4094)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameEndFlag",
   "meaning": "nonzero once the game is over; aborts runGameFrames/waitFramesOrInput",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameEndCountdown",
   "meaning": "positive value counts up every 8 frames to $80 and then ends the game/returns to the menu: $62/$71 after an abort command, $7D after film end",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameEndCountdown",
   "meaning": "nonzero = game-over sequence running; the menu loop exits with the last item",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameEndCountdown",
   "meaning": "Counter incremented every 8 frames by updateFrameTimers while it is between 1 and $7F, stopping once it reaches $80; used to time the end-of-game sequence.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameEndFlag",
   "meaning": "Nonzero once the game-over sequence is running; blinkRepairMarker and runGameFrames abort their waits on it.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameEndCountdown",
   "meaning": "end-of-game countdown started by an abort: $62 when no game is running, $71 when one is",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameEndCountdown",
   "meaning": "Non-zero while the game is ending; makes checkUiContinue abort every modal UI",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "gameEndFlag",
   "meaning": "Bit 7 set when the game must be torn down; exitIfGameEnded turns it into a jump back to the main menu.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  },
  {
   "name": "pauseTimeoutCounter",
   "meaning": "non-zero aborts the blackout loop (pause/timeout in progress)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "92D6": [
  {
   "name": "setupIdSnapshot",
   "meaning": "4 bytes: map id and $9241-$9243 copied by snapshotSetupId, compared by selectDefaultMenuItem",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenStatusSnapshot",
   "meaning": "4-byte copy of comcenSystemStatus (energy in [0]) taken when the REPAIR tab was last used; differences auto-select the REPAIR tab",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenStatusSnapshot",
   "meaning": "first byte (energy) preset to 50 at session start",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "92E4": [
  {
   "name": "shiftKeyFlag",
   "meaning": "$80 when a shift key is held (from scanKeyboard)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "shiftHeldFlag",
   "meaning": "$80 when SHIFT (either key) was held during the last scan, else 0. Used for the shifted-key translation and, with $92E5, for the SHIFT+C=+F7 abort hotkey.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "92E5": [
  {
   "name": "commodoreKeyFlag",
   "meaning": "$80 when the C= key is held",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commodoreHeldFlag",
   "meaning": "$80 when the C= key was held during the last scan, else 0.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "92E6": [
  {
   "name": "chatDisabled",
   "meaning": "nonzero disables chat typing",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "filmNameEntryLength",
   "meaning": "Non-zero while the SAVE GAME FILM slot name is being typed into the shared line buffer $9278; handleChatSend refuses to start typing a chat message while it is set.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "filmNameLength",
   "meaning": "number of characters typed into textInputBuffer for the film name (0 = keep the old name)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "92F0": [
  {
   "name": "diskIdIndex",
   "meaning": "diskIdTable index passed to checkDiskId (0 game disk, 2 data disk)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "diskIdIndex",
   "meaning": "Index (0 = 'OZ' game disk, 2 = 'EA' data disk) of the id checkDiskId last verified; also used to decide whether the disk serial should be copied to $FFDE.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "diskIdIndex",
   "meaning": "0 after the game disk was identified, 2 after a data disk (enables the 3-page film directory)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "92F4": [
  {
   "name": "scrollFineX",
   "meaning": "low 3 bits = sprite X fine-scroll offset",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentXScroll",
   "meaning": "horizontal fine scroll value last written by shakeScreenScroll",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteScrollX",
   "meaning": "Fine horizontal scroll of the tactical view; the IRQ adds its low three bits to every sprite X shadow.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "92F5": [
  {
   "name": "scrollFineY",
   "meaning": "low 3 bits minus 3 = sprite Y fine-scroll offset",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentYScroll",
   "meaning": "vertical fine scroll value last written by shakeScreenScroll",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteScrollY",
   "meaning": "Fine vertical scroll of the tactical view; the IRQ adds (value & 7) - 3 to every sprite Y shadow.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "92F8": [
  {
   "name": "frameTick92F8",
   "meaning": "incremented every frame by updateFrameTimers",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameTick92F8",
   "meaning": "Free-running frame tick incremented by updateFrameTimers; overlay B uses it (with a self-modified compare at $7807) as the clock for the missile-flight animation and resets it at $77CF.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  }
 ],
 "92FB": [
  {
   "name": "frameTick92FB",
   "meaning": "incremented every frame by updateFrameTimers",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameTick92FB",
   "meaning": "Free-running frame tick incremented by updateFrameTimers; overlay B's updateRadarSweep ($7894) uses it as the radar sweep clock and restartRadarSweep ($78D3) resets it.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameTick92FB",
   "meaning": "free-running frame counter; reset to 0 by restartRadarSweep and compared with radarSweepNextTick to pace the sweep (2 frames per step)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "92FE": [
  {
   "name": "linkTypeAtStart",
   "meaning": "copy of linkTypeFlag taken at game start",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "linkTypeAtStart",
   "meaning": "copy of linkTypeFlag taken at game start; bit 7 set makes haltStunnedUnit ignore side-1 units",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "linkTypeAtStart",
   "meaning": "copy of soloTrainerFlag at game start; film header byte 4",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "E011": [
  {
   "name": "commAnswerOriginate",
   "meaning": "comm module flag: 0 = answer ('A'), 1 = originate ('O')",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "isOriginateMode",
   "meaning": "0 = answer mode ('ATQ0V1X1A'), 1 = originate/dial mode ('ATQ0V1X1D'). Set by the 'A'/'O' hot keys ($C1/$CF) and by main at $0CDE; also selects the CIA2_PRB and CIA2_SDR patterns in configureUserPortLines.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "modemModeIndex",
   "meaning": "0 = answer, 1 = originate/dial; indexes modemInitStringPtrLoTable at $EBFE",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "modemAnswerOriginateFlag",
   "meaning": "0 = answer, 1 = originate, written by waitForAnswerOriginateKey ($0CDE); unused by the trainer build",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "E013": [
  {
   "name": "commKeyScanVector",
   "meaning": "operand of the JMP at $E012; the game points it at scanKeyboard ($0DB7)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commKeyVector",
   "meaning": "Two-byte vector inside the comm module ($E013/$E014) that its $E012/$E015 entries jump through; initCommModule points it at scanKeyboard ($0DB7).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "keyboardHookVector",
   "meaning": "The two operand bytes of the JMP at $E012. Main patches them to $0DB7 (scanKeyboard) at $0F4B/$0F50, so the comm module calls back into the game's keyboard scanner.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "keyboardScanVector",
   "meaning": "operand of the JMP at $E012; patched to scanKeyboard ($0DB7) by initCommModule",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "E030": [
  {
   "name": "commBuildFlag",
   "meaning": "bit 7 identifies which comm build is resident; copied to linkTypeFlag",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commModuleFlags",
   "meaning": "comm module capability byte, bit 7 set in build 1 (T35) = no modem handshake prompts",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commBuildId",
   "meaning": "Identifies which $E000 module is resident: $00 in this (T34) modem build, $FF in the T35 build. Copied into $0BA5 and tested all over main to decide whether the modem prompts apply.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "commBuildId",
   "meaning": "$FF in this build; the game copies it to linkTypeFlag $0BA5 and tests bit 7 to decide whether the resident comm module is build 1 (track 35 = solo trainer) or build 2 (track 34 = modem)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "E031": [
  {
   "name": "commNmiVector",
   "meaning": "2-byte NMI vector copy kept by the comm module, set to irqRti",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "nmiChainVector",
   "meaning": "Address the comm NMI handler chains to when the interrupt was not from CIA2. Its file value is $E010 (a lone RTI byte in the module's constant pool); main overwrites it with $1298 at $0BF3/$0BFB.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "commNmiVector",
   "meaning": "$E031/$E032 = the module's NMI vector, initially $E010 (an RTI); rewritten to irqRti ($1298) by initInterruptVectors",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "E039": [
  {
   "name": "commRequest",
   "meaning": "request byte to the comm module: $C0 = pause, 0 = resume",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commRequest",
   "meaning": "Request byte written by suspendCommModule ($C0 = pause) / resumeCommModule (0).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "nmiSuspendRequest",
   "meaning": "Written by the game ($0F67 = $C0 suspend, $0F78 = 0 resume) to ask the comm module to stop or restart the CIA2 NMI around disk access.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "commSuspendRequest",
   "meaning": "$C0 = the game asks the module to pause (before disk access), 0 = resume; written by suspendCommModule/resumeCommModule",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "E03A": [
  {
   "name": "commStatus",
   "meaning": "bit 7 set while the comm module is paused (acknowledge)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commAckState",
   "meaning": "Acknowledge byte polled by suspend/resumeCommModule; bit 7 set means the module has stopped touching the hardware.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "nmiSuspendAck",
   "meaning": "Acknowledgement of nmiSuspendRequest: $80 = NMI disabled, $00 = running. The game spins on it at $0F6A/$0F7B.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "commSuspendAck",
   "meaning": "acknowledge byte polled by the game; the trainer copies commSuspendRequest into it every frame so the handshake completes instantly",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "E03B": [
  {
   "name": "commLinkActive",
   "meaning": "nonzero when the comm link is up (pause/resume and chat only then)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commLinkActive",
   "meaning": "$FF while the link is open (set by sub_E003 X=0, cleared by X>=1)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commLinkActive",
   "meaning": "non-zero while the comm link is open; the exchange is skipped otherwise",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commLinkActive",
   "meaning": "nonzero while the link is open",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commActiveFlag",
   "meaning": "Non-zero when the communications module is running a link; guards the suspend/resume handshake and the whole chat send path.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commLinkActive",
   "meaning": "nonzero = link already open (skip openCommLink)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "isLinkActive",
   "meaning": "Non-zero once openCommLink has installed the NMI and started the UART.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "isLinkActive",
   "meaning": "$FF while the (pretend) link is up; tested all over the game before touching the comm module",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "E02E": [
  {
   "name": "commPendingCount",
   "meaning": "comm module counter checked before sending chat (must be 0 / < $10)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commTxCount",
   "meaning": "Number of bytes queued in the comm module's 16-byte transmit ring at $E091. handleChatSend requires it to be 0 before opening typing mode and force-terminates the message when it reaches $10 (full).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "hostOutCount",
   "meaning": "Number of bytes currently in the 16-byte host-output ring at $E091.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "FFDE": [
  {
   "name": "ownerName1",
   "meaning": "10-byte first name field copied from the BAM (offset $AB) of the game disk",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "diskSerial",
   "meaning": "Ten bytes copied from BAM offset $AB (disk name/id) by checkDiskId; the link protocol compares them so both players must run the same disk.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "ownerNames",
   "meaning": "20 bytes ($FFDE own name, $FFE8 opponent name) copied into the film header at +$11",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "playerNames",
   "meaning": "2 x 10 bytes ($FFDE own name from the BAM, $FFE8 opponent/trainer name); film header bytes $11-$24",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "ownPlayerName",
   "meaning": "Ten-character owner name read from the disk BAM, sent to the peer during the handshake",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "FFE8": [
  {
   "name": "ownerName2",
   "meaning": "10-byte second name field (written by overlay code)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_0800",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "opponentPlayerName",
   "meaning": "Ten-character opponent name received during the handshake ('TRAINER   ' in solo mode, 'ENEMY' when the peer's name is blank)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "3E": [
  {
   "name": "textFillChar",
   "meaning": "character used by the clear/space routines ($C06A, $C0EF)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textFillChar",
   "meaning": "Text engine: character used to clear lines/windows.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textFillChar",
   "meaning": "text engine fill character, zeroed before each menu is drawn",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textFillChar",
   "meaning": "fill character for window clears (0 here)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textFillChar",
   "meaning": "Fill character used by the clear routines; clearRepairMessage prints 20 of it. It is 0 (blank) while the repair menus are up.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textFillChar",
   "meaning": "Glyph used to blank cells by the window/line clear routines; zeroed (space) before this menu is drawn.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textFillChar",
   "meaning": "fill glyph used by $C0EF: $25 for the five underline glyphs below the map-name field, restored to 0",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "blankGlyph",
   "meaning": "Glyph index used as the fill/blank character by the clear and pad routines",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "21": [
  {
   "name": "loopCounter21",
   "meaning": "loop counter (tab index loop in drawMenuTabBar)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "magnifyHalfHeight",
   "meaning": "ceil(windowHeight/2) during magnifyWindow2x",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scanUnit",
   "meaning": "unit whose sight/fire range is being scanned",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "neighbourMask",
   "meaning": "8-bit mask of surrounding cells that belong to the terrain being contoured (index into the $0200 rule table)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "statsSidePass",
   "meaning": "drawStatsTab: 1 = counting own side (column 9), 0 = opponent (column 14).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "aiSumHi21",
   "meaning": "high byte of the destination-row sum; deepest weapon-range penetration in assessThreatAroundAiComcen",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "maxRangeSurplus",
   "meaning": "Largest (weaponRange - distance) found by the threat scan; returned in X and used by callers as 'something is in range of us'",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  }
 ],
 "54": [
  {
   "name": "mapCellValue",
   "meaning": "map byte of the cell being drawn by sub_5D1A",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapCellValue",
   "meaning": "map byte of the cell being redrawn by drawMapCell/drawViewportCell/drawScreen1Cell",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapCellValue",
   "meaning": "cell byte passed to drawScreen1Cell ($80|unit or terrain)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "savedA",
   "meaning": "A saved by saveRegsAXY / restored by restoreRegsAXY",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "55": [
  {
   "name": "mapCellX",
   "meaning": "map column of the cell being drawn",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapCellX",
   "meaning": "column of that cell",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapCellX",
   "meaning": "column of the cell passed to drawScreen1Cell",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "savedX",
   "meaning": "X saved by saveRegsAXY",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "56": [
  {
   "name": "mapCellY",
   "meaning": "map row of the cell being drawn",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapCellY",
   "meaning": "row of that cell",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapCellY",
   "meaning": "row of the cell passed to drawScreen1Cell",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "savedY",
   "meaning": "Y saved by saveRegsAXY",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "73": [
  {
   "name": "cmdParam0",
   "meaning": "first parameter byte of an outgoing command (unit index or reason code)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commandUnitArg",
   "meaning": "Unit index argument of a unit command sent with sub_561F/sub_560A (bit 7 = whole group).",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam0",
   "meaning": "first parameter of an outgoing command (unit index)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam0",
   "meaning": "first parameter of a received command (unit index for setBoomerTargetCmd)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam0",
   "meaning": "first parameter of the command being sent or executed (normally the unit index)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam0",
   "meaning": "first parameter of the current command (unit, group id, reason, side, ...)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam0",
   "meaning": "first parameter of an outgoing command: unit index for the unit menu, group key (FB54 & $3C) for the group menus",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam0",
   "meaning": "first parameter of an outgoing command (reason 5 for the voice-pause $8C)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam0",
   "meaning": "First parameter of an outgoing command; here the comcen unit index for the $80 halt command.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam0",
   "meaning": "first parameter of the command being queued or executed - the unit index for $80/$81/$98, the group key for $89/$9B",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam0",
   "meaning": "first argument byte of the command being executed (unit index, group id byte, side, reason, option kind); also reused as the unit loop counter by cmdSelfDestruct",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam0",
   "meaning": "command parameter 0: mapSeed byte 0 for the $8B/$A6 map-seed command",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "cmdParam0",
   "meaning": "first parameter of an outgoing command (game type for $A4, recycler mode for $9C)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "cmdParam0",
   "meaning": "first parameter of an outgoing command (recycler mode for $9C)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "cmdParam0",
   "meaning": "First parameter byte of the outgoing network command built by queueCmd2; sendPauseCommand puts the pause reason (3 or 4) here.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "74": [
  {
   "name": "cmdParam1",
   "meaning": "second parameter byte of an outgoing command (unit x / unit index)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam1",
   "meaning": "second command parameter (destination column / attacker unit)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam1",
   "meaning": "second command parameter (normally x / map column, or the unit for $88/$A3)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam1",
   "meaning": "second parameter (column / unit / kind)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam1",
   "meaning": "second command parameter (selected unit for formation command $A3)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam1",
   "meaning": "Second command parameter: map column for $80, comcen unit index for $9A.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam1",
   "meaning": "second command parameter: map column for $81/$98, unit index for $88/$A3",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam1",
   "meaning": "second argument byte (map column, unit index, option kind)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam1",
   "meaning": "command parameter 1: mapSeed byte 1",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "cmdParam1",
   "meaning": "second command parameter (recycler column for $9C)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "cmdParam1",
   "meaning": "second command parameter (option kind for $A5, drones for $90, x for $9C)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "cmdParam1",
   "meaning": "Command x parameter; for applyDroneBlast the top-left column of the 2x2 blast",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "cmdParam1",
   "meaning": "second byte of an outgoing command: strike column",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "cmdParam1",
   "meaning": "Second byte of an outgoing command; getDroneMapPosition stores the drone column here.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "75": [
  {
   "name": "cmdParam2",
   "meaning": "third parameter byte of an outgoing command (unit y / amount)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam2",
   "meaning": "third command parameter (destination row / target unit)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam2",
   "meaning": "third parameter of a received command (target unit index for setBoomerTargetCmd)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam2",
   "meaning": "third command parameter (normally y / map row, or the group bits / rotation mode)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam2",
   "meaning": "third parameter (row / target / value)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam2",
   "meaning": "third command parameter (formation option 0-2 for command $A3)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam2",
   "meaning": "Third command parameter: map row for $80, energy amount for $9A.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam2",
   "meaning": "third command parameter: map row for $81/$98, group key for $88, rotation mode 0-2 for $A3",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam2",
   "meaning": "third argument byte (map row, target unit, option value)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdParam2",
   "meaning": "command parameter 2: mapSeed byte 2",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "cmdParam2",
   "meaning": "third command parameter (recycler row for $9C)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "cmdParam2",
   "meaning": "third command parameter (option value for $A5, missiles for $90, y for $9C)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "cmdParam2",
   "meaning": "Command y parameter; for applyDroneBlast the top-left row of the 2x2 blast",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "cmdParam2",
   "meaning": "third byte of an outgoing command: strike row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "cmdParam2",
   "meaning": "Third byte of an outgoing command; getDroneMapPosition stores the drone row here.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "7B": [
  {
   "name": "currentMessageId",
   "meaning": "id of the status-line message currently shown (bit 7 = none)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "statusBarMessage",
   "meaning": "Index of the message shown in the bottom status bar by L_C5D7 (table $0500/$0524).",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "shownMessageId",
   "meaning": "Message id currently on the status line; bit 7 set = the line is free",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "pendingMessageId",
   "meaning": "Message id that the message service should print on the next pass; bit 7 set suppresses the reprint. Written by showStatusMessage, handleMessageReviewKeys and the score-line key path.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "A2": [
  {
   "name": "stringPtr",
   "meaning": "pointer to the bit-7-terminated string being printed by $C133/$C137 (lo), $A3 hi",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "stringPtr",
   "meaning": "Pointer (lo at $A2, hi at $A3) to the bit-7-terminated string being drawn; printString ($C133) does sta zp_A2 / sty zp_A3 on entry. Every caption in this chunk is passed as A = low byte, Y = $86 or $87.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "stringPointer",
   "meaning": "Pointer to the string being printed (with zp_A3); left just past the terminator so the next string can follow",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "B6": [
  {
   "name": "screenPtr",
   "meaning": "pointer into screen RAM ($8C00 based) computed from row/column tables (lo), $B7 hi",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "screenColorPtr",
   "meaning": "Screen-RAM (bitmap colour) address of the current character cell (zp_B6/zp_B7).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "screenCellPtr",
   "meaning": "screen RAM ($8C00 based) address of the current character cell (lo); +$4C00 gives colour RAM",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "screenPtr",
   "meaning": "Pointer (lo) to the colour byte of the cell in screen RAM $8C00 (row table $9382/$939B + column); +$4C00 reaches colour RAM",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "screenPtr",
   "meaning": "pointer (lo) to the cell in screen RAM $8C00 (+$4C00 = colour RAM)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "screenPtr",
   "meaning": "screen RAM pointer of the current text cell (set by sub_C097); the row highlight EORs (zp_B6),y",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "screenCellPtr",
   "meaning": "pointer (lo, hi in $B7) to the colour byte of the text cursor cell; enterMapName writes $11 there to show the cursor block",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "screenCellPtr",
   "meaning": "screen RAM pointer of the current text cell; runSaveFilmMenu writes the cursor block $11 through it",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "colourCellPtr",
   "meaning": "Screen RAM (drone screen) or colour RAM (screen 1 caller) address of the current radar row (lo); indexed by the column in Y",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "colourRamPtr",
   "meaning": "in this chunk: pointer (lo) into colour RAM $D800 for the cell row (screen row table hi byte + $4C)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "screenPtr",
   "meaning": "Screen RAM row pointer (lo) used by drawShotLineInViewport to whiten the viewport colour nibbles.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "colourPointer",
   "meaning": "Colour destination of the current cell: screen RAM $8C00 + row*40 + col, or colour RAM ($D800 + ...) in multicolour mode",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "BE": [
  {
   "name": "commPauseFlags",
   "meaning": "comm/pause related flag byte (bit 7 cleared after connecting, bit 0 toggled by $C70A)",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commPauseFlags",
   "meaning": "Nonzero skips clearing the message window in clearCurrentScreenWindows",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pauseState",
   "meaning": "pause/ready bits: 3 after a timeout/pause (both players must resume), bit 0/1 cleared by the own/opponent's resume or setup-ready command, $80 = disconnect pending; 0 = running",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pauseState",
   "meaning": "pause/disconnect state: bit 0 own pause, bit 1 opponent pause (cleared by getPauseClearMask), 3 after a timeout, $80 after a hang-up",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pauseState",
   "meaning": "Non-zero while the session is paused; keeps modal UI alive in checkUiContinue",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "commPauseFlags",
   "meaning": "Pause state: 0 = running, 1 = paused by RUN/STOP, bit 7 = voice pause (hang up, talk, reconnect). Toggled by handlePauseKey and by the $8C command handlers; cleared by voicePauseReconnect and endGameToMainMenu.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  },
  {
   "name": "pauseState",
   "meaning": "non-zero while the game is paused; suppresses the trainer's turn",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "F0": [
  {
   "name": "scrollDeltaX",
   "meaning": "applied horizontal scroll delta (0/1/2 = -1/0/+1 after the +1 in scrollViewBitmap)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  }
 ],
 "F1": [
  {
   "name": "scrollDeltaY",
   "meaning": "applied vertical scroll delta (0/1/2 = -1/0/+1)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  }
 ],
 "F2": [
  {
   "name": "bitmapRowStride",
   "meaning": "signed 16-bit stride added between bitmap rows while scrolling (lo), $F3 hi (+320 or -320)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  }
 ],
 "F4": [
  {
   "name": "screenRowStride",
   "meaning": "signed 16-bit stride between screen rows while scrolling (lo), $F5 hi (+40/-40)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  }
 ],
 "F6": [
  {
   "name": "scrollRowCount",
   "meaning": "number of character rows to copy while scrolling (8 or 10)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  }
 ],
 "F7": [
  {
   "name": "redrawCellCount",
   "meaning": "cells left to redraw after a scroll (5 or 7)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  }
 ],
 "F8": [
  {
   "name": "viewRowLimit",
   "meaning": "exclusive map-row limit for the view centre (40, or 20 in setup phase)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  }
 ],
 "0BA2": [
  {
   "name": "pendingPlaysFirstFlag",
   "meaning": "copy of playsFirstFlag restored after film playback",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingFirstPlayFlag",
   "meaning": "copy of firstPlayFlag made at game start",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0BA4": [
  {
   "name": "pendingGameTypeOptions",
   "meaning": "pending copy of gameTypeOptions, $FF = none",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingGameTypeOptions",
   "meaning": "previous gameTypeOptions; a difference at game start sets firstPlayFlag = 1",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "pendingGameTypeOptions",
   "meaning": "previous/pending copy of gameTypeOptions; a difference sets playsFirstFlag",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "1535": [
  {
   "name": "tabActionTable",
   "meaning": "3 JSR addresses for the fire action of tabs 0..2: $158F (STATS), $17DA (REPAIR), $1A49 (MISC)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  }
 ],
 "2A2A": [
  {
   "name": "fillColourValue",
   "meaning": "colour byte written by the rectangle fill sub_2A64",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "drawColourByte",
   "meaning": "screen RAM colour byte (two nybbles) used by plotGlyph and fillScreenColourRect",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fillColourValue",
   "meaning": "Current colour byte for the rectangle fill (sub_2A64) and for characters whose table colour is 0; combined with the existing colour byte through the self-modified opcode at $2B00/$2A85 (LDA/ORA/EOR/CMP selected by sub_2A47)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fillColourValue",
   "meaning": "Colour byte used by fillScreenColourRect; drawSystemMarker parks the marker colour here and re-loads it for each of the five 1x1 fills.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fillColourValue",
   "meaning": "fixed colour byte for glyph/fill drawing ($30 for the missile digits)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "drawColourByte",
   "meaning": "Current colour byte for fills/characters ($AA static colour).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "2A2D": [
  {
   "name": "fillRow",
   "meaning": "screen row for sub_2A64 / sub_2B18",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "windowRow",
   "meaning": "character row of the current window / block origin",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fillRow",
   "meaning": "Top screen row (0-24) of the current window / block / fill rectangle; set from windowTopTable by drawWindowFrame",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fillRow",
   "meaning": "Screen row (0-24) of the fill/window origin used by fillScreenColourRect; stepped by drawSystemMarker to build the plus shape.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "windowRow",
   "meaning": "screen row of the window / fill origin for fillScreenColourRect and drawCharBlock",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "windowRow",
   "meaning": "Row of the fill/XOR rectangle origin (12 LAUNCH box, 16 STRIKE box, 6 camera window).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "2A2E": [
  {
   "name": "fillColumn",
   "meaning": "screen column for sub_2A64 / sub_2B18",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "windowCol",
   "meaning": "character column of the current window / block origin ($2A2F is an unreferenced filler byte)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fillColumn",
   "meaning": "Left screen column (0-39) of the current window / block / fill rectangle; set from windowLeftTable by drawWindowFrame",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fillColumn",
   "meaning": "Screen column (0-39) of the fill/window origin used by fillScreenColourRect.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "windowCol",
   "meaning": "screen column of the window / fill origin",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "windowCol",
   "meaning": "Column of the fill/XOR rectangle origin (34 for the buttons, 13 for the camera window).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9065": [
  {
   "name": "outgoingCmdRing",
   "meaning": "40-byte ring buffer of outgoing command bytes (sub_55EF)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "outgoingCmdRing",
   "meaning": "40-byte ring of outgoing command bytes ($9065-$908C), $FF after the last byte",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "908D": [
  {
   "name": "outgoingCmdWriteIdx",
   "meaning": "write index into outgoingCmdRing",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "outgoingCmdWriteIdx",
   "meaning": "write index into outgoingCmdRing (0-39)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "908E": [
  {
   "name": "outgoingCmdReadIdx",
   "meaning": "read index into outgoingCmdRing",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "outgoingCmdReadIdx",
   "meaning": "read index into outgoingCmdRing (next byte to send)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "90EF": [
  {
   "name": "textCentreWidth",
   "meaning": "nonzero: next string is centred in this many columns ($C137)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textHighlightMode",
   "meaning": "Text engine flag: when nonzero strings are printed with the zp_42 colour (sub_C137); cleared before printing the info panel.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textCentreWidth",
   "meaning": "nonzero = next string is centred in this many columns; cleared before the menus",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textCentreWidth",
   "meaning": "text engine centring width, cleared before printing menu text",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textCentreWidth",
   "meaning": "Text engine centring width: when non-zero sub_C137 pads the string to that many columns using the zp_42 colour. Cleared at $6532 so the menu text is printed left-aligned inside the narrow window.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textCentreWidth",
   "meaning": "nonzero = the next string is centred in this many columns (13 for the banner words, zp_3C via sub_C121)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "textCentreWidth",
   "meaning": "Field width for centring the next string (0 = no centring); cleared again by printStringAtPointer",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "90F8": [
  {
   "name": "selectedUnit",
   "meaning": "index of the currently selected unit ($F0/$FE/$FF = none)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedUnit",
   "meaning": "Index 0-99 of the unit currently under the cursor / selected for commands ($F0 = none in some phases).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedUnit",
   "meaning": "Index (0-99) of the selected unit; buildGroupFormationSprite uses its destination position as the box origin",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedUnit",
   "meaning": "index of the currently selected unit",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedUnit",
   "meaning": "index of the unit the player has selected (subject of the group commands)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedUnit",
   "meaning": "selected unit; during side selection it holds the locally chosen side",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedUnit",
   "meaning": "unit the menus act on; runGroupEditor rewrites it with the unit under the cursor (bit 7 set = none or enemy); $FF in the repair menu",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedUnit",
   "meaning": "selected unit index; the menu selector treats $FF as 'repair menu' (blinks the comcen system marker) and $FE as a plain menu with no unit",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedUnit",
   "meaning": "unit index the player currently has selected (the group leader when a group is selected); $FF marks the repair-menu mode elsewhere",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedUnit",
   "meaning": "currently selected unit; during side selection it holds our own side pick so cmdChooseSide can compare it with the opponent's",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedUnit",
   "meaning": "Unit index the menus act on. The value $FF written here is a sentinel meaning 'repair menu': toggleMenuRowHighlight ($60EE) tests for it and, while the selection is 0..3, blinks the comcen system marker with the row (and aborts the menu when the comcen becomes stunned). It is re-stored on every pass because the selector overwrites it via D_91D7.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedUnit",
   "meaning": "reused as the version-check result of the identity exchange: preset to $DB, $ED43 stores $E022 EOR $E023; a mismatch shows 'OPPONENT USING NON-STANDARD GAME.'",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "selectedUnit",
   "meaning": "selected unit index ($FE = none); reused as the slot-number counter by drawFilmDirectoryPage",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "selectedUnit",
   "meaning": "unit the menus act on; $FE = plain menu without a unit (set by beginValueEdit and sub_8364)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "91D4": [
  {
   "name": "menuItemCount",
   "meaning": "number of items for the menu selector sub_5FD1/sub_65A0",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuItemCount",
   "meaning": "number of items in the current menu",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuItemCount",
   "meaning": "number of items in the current menu",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuItemCount",
   "meaning": "Number of rows in the current menu; zeroed at $6537 and incremented by each addMenuItem call, ending at 5.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuItemCount",
   "meaning": "number of items for the menu selector",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "menuItemCount",
   "meaning": "number of rows of the current menu (2 for OFF/ON and STANDARD/CUSTOM, 3 for the recycler mode menu)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "91D5": [
  {
   "name": "menuSelectedItem",
   "meaning": "current/selected menu item (also the repair system index 0..3)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuSelectedItem",
   "meaning": "index of the highlighted / chosen menu row",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuSelectedItem",
   "meaning": "current/selected menu row; $81 while waiting for the voice-pause handshake (bit 7 cleared by voicePauseReconnect)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuSelectedItem",
   "meaning": "Highlighted/selected menu row; while the REPAIR tab is up it doubles as the comcen system index 0..3 ($04 = the (EXIT) item), which is what drawSystemMarker/toggleSystemMarker/attemptSystemRepair operate on.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuSelectedItem",
   "meaning": "Currently highlighted / chosen menu row. In this menu it doubles as the comcen system index 0..3 (ENERGY, BATTLE, RADAR, DRONE) used by toggleSystemMarker and attemptSystemRepair; 4 is the (EXIT) row. Pre-set to 0 at $653A.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuSelectedItem",
   "meaning": "current/selected menu item; filmSlotToTrack overwrites it with the disk track",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "menuSelectedItem",
   "meaning": "highlighted/chosen menu row; the value editors keep it at 0 (single row)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "91D6": [
  {
   "name": "menuFirstRow",
   "meaning": "screen row of the first menu item",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuFirstRow",
   "meaning": "screen row of menu item 0 (15 for the unit/group menus, 13 for the join-group list)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuFirstRow",
   "meaning": "screen row of the first menu item",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuFirstRow",
   "meaning": "Screen row of menu row 0; taken from textCursorRow after the header is printed (13 here, so the items sit on rows 13..17).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuFirstRow",
   "meaning": "screen row of the first menu item",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "menuFirstRow",
   "meaning": "screen row of menu row 0 (the value editors use the row below the label)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "920A": [
  {
   "name": "groupBoxActive",
   "meaning": "bit 7 set while a group/formation box around the cursor is active",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "miniMapZoomFlags",
   "meaning": "Bit 7 = mini-map shows a zoomed sub-rectangle (group view, set near $319A) defined by D_922E-D_9231; affects sprite mapping (sub_3023) and scroll limits.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupBoxActive",
   "meaning": "Bit 7 set while a group formation box is attached to the cursor (set by buildGroupFormationSprite, cleared at $223E)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "920E": [
  {
   "name": "statsNeedRefresh",
   "meaning": "nonzero: STATS tab must be redrawn (sub_8518)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "infoPanelRefreshRequest",
   "meaning": "Bit 7 set asks the info panel to reprint the status lines; cleared by showUnitInfoPanel.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "statsNeedRefresh",
   "meaning": "set to $80 after a command slice was applied",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "statsNeedRefresh",
   "meaning": "nonzero requests a screen refresh after a command slice; the screen-1 loop then calls sub_7688 (which clears it) and showDroneKillBurst",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "exchangeStepFlag",
   "meaning": "Bit 7 set (at $41F8) after each merged command exchange is applied; the drone screen consumes it to advance the camera one step, the STATS tab clears it as its refresh request.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "922E": [
  {
   "name": "groupBoxLeftOffset",
   "meaning": "signed column offset of the group box left edge from the cursor",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "zoomWidth",
   "meaning": "Width of the zoomed mini-map rectangle (set in sub_30C0).",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupBoxRightOffset",
   "meaning": "Column offset (>= 0) of the group box right edge from the selected unit / cursor column (max member column - selected unit column). NOTE: the 163A survey swapped this with $922F; sub_4B5E stores the maximum column in zp_1B which buildGroupFormationSprite copies here",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "922F": [
  {
   "name": "groupBoxRightOffset",
   "meaning": "signed column offset of the group box right edge",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "zoomOriginX",
   "meaning": "Left map column of the zoomed mini-map rectangle.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupBoxLeftOffset",
   "meaning": "Column offset (<= 0, signed) of the group box left edge from the cursor column; mapCellToSpriteXY adds it to position the formation sprite and buildGroupFormationSprite subtracts it from member columns",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "9230": [
  {
   "name": "groupBoxTopOffset",
   "meaning": "signed row offset of the group box top edge",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "zoomHeight",
   "meaning": "Height of the zoomed mini-map rectangle.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupBoxBottomOffset",
   "meaning": "Row offset (>= 0) of the group box bottom edge from the cursor row (keepGroupBoxInsideMap scrolls up when centre + this >= limit)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "9231": [
  {
   "name": "groupBoxBottomOffset",
   "meaning": "signed row offset of the group box bottom edge",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "zoomOriginY",
   "meaning": "Top map row of the zoomed mini-map rectangle.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupBoxTopOffset",
   "meaning": "Row offset (<= 0, signed) of the group box top edge from the cursor row; added to the row by mapCellToSpriteXY",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "9241": [
  {
   "name": "comcenSystemStatus",
   "meaning": "4 bytes: status of the comcen energy (0) and three systems (1-3); 0 ok, $40 degraded, $80 damaged",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenSystemStatus",
   "meaning": "4 bytes: [0] energy status, [1..3] status of the three comcen systems, $80 = damaged",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenSystemStatus",
   "meaning": "Four status bytes shown on the comcen schematic: [0] energy (0 = 100%, $40 = 21..99%, $80 = below 21%, recomputed by printEnergyValue), [1..3] the three comcen systems (0 = ok, $80 = damaged, set only by rollComcenSystemDamage, cleared only by attemptSystemRepair). Values $40 therefore only ever occur for entry 0.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenSystemStatus",
   "meaning": "Four-byte comcen system status array indexed by the same 0..3 value this menu leaves in menuSelectedItem ($80 = damaged, 0 = ok); written by attemptSystemRepair on a successful repair.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "92B6": [
  {
   "name": "savedTextWindow",
   "meaning": "12-byte save area for zp_39-zp_44 (sub_C41B/sub_C40E)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "savedTextWindow",
   "meaning": "12-byte save area for zp_39-zp_44 written by sub_C41B and restored by sub_C40E around the repair message printing.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "savedTextWindow",
   "meaning": "12-byte save area for zp_39-zp_44 used by saveTextWindow/restoreTextWindow (single slot, not nestable)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "92FD": [
  {
   "name": "defaultMainMenuItem",
   "meaning": "initial main-menu item (0 COMPETE, 1 PRACTICE, 2 WATCH FILM)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "defaultMainMenuItem",
   "meaning": "initial main-menu item; set to 2 (WATCH GAME FILM) after a film save/load",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "E01D": [
  {
   "name": "commTxState",
   "meaning": "comm module transmit state byte (0 = nothing pending; $C0 set by sub_E003 default path)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commExchangeState",
   "meaning": "comm module exchange state: bit 6 = a complete packet has been received and the exchange may be processed; sub_E000 X=1 marks the outgoing packet pending, X=2 clears it after processing",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commExchangeState",
   "meaning": "comm module exchange state: bit 0 = send requested, bit 5 = sending, bit 6 = exchange complete (own packet sent and opponent's received), bit 7 = packet received",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commExchangeState",
   "meaning": "bit 6 polled at $7B64: a complete packet from the opponent has arrived (both sides finished setup)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "exchangeFlags",
   "meaning": "Packet exchange state. Bits 0-1 count 'send requested' / 'receive in progress', bit 3 idle marker, bit 5 = own packet has been sent, bit 6 = exchange complete (the flag the game waits on), bit 7 = opponent packet received.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "commStatusFlags",
   "meaning": "Comm module status byte; bit 6 = message received / exchange complete (polled by waitForLinkMessage and by the game at $4E4B, $56C0, $7B64)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "packetExchangeStatus",
   "meaning": "bit 7 = busy, bit 6 = exchange complete. The trainer always writes $C0 so the game never blocks. Note the initial value $00 doubles as a BRK opcode in the game's BRK routing",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "E03C": [
  {
   "name": "commLineStatus",
   "meaning": "bit 6 = connected (carrier), bit 7 = line busy; $FF after link open",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commLineStatus",
   "meaning": "bit 6 = carrier present; lost carrier at menu level disconnects",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commLineStatus",
   "meaning": "bit 6 = carrier/connected: decides whether VOICE PAUSE sends the $8C request or reconnects directly",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "carrierStateDebounced",
   "meaning": "Debounced carrier state; bit 6 = carrier present (tested by main at $1B4B, $56B1, $65BC), bit 7 accumulates the sample-valid marker.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "linkStatusFlags",
   "meaning": "Handshake/carrier sample; bit 6 selects the real-modem timing paths (tested at E787 and E7E3)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "commCarrierStatus",
   "meaning": "$FF while the link is up; bit 6 is the carrier test in pollMenuSession, bit 7 the hang-up test",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "E03D": [
  {
   "name": "commStatusFlags2",
   "meaning": "bit 6 polled while waiting for a carrier; $FF after link open",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "carrierSample",
   "meaning": "Raw carrier sample taken each frame: $F8 = carrier (bit 6 set), $80 = no carrier.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "commLinkReady",
   "meaning": "$FF while the link is up; read at $1BAB when a session starts",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "F960": [
  {
   "name": "unitStunTable",
   "meaning": "per-unit byte whose low nibble is the stun counter (used for the comcen)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitStunDigState",
   "meaning": "Bits 0-3 = stun counter (STUN'D, unit not selectable), bits 5-6 = digging in progress (DIGGIN), bit 7 = DUG IN.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitStatusB",
   "meaning": "unit array: bit 7 set adds solid top and bottom lines to the unit tile; low nybble and bits 5-7 tested by the status panel",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitStunTable",
   "meaning": "per-unit stun counter (low nibble)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitStunTable",
   "meaning": "per-unit: bits 0-3 stun counter, bits 5-6 dig-in progress, bit 7 dug in",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitStunTable",
   "meaning": "bits 0-3 stun counter (decremented once per processed step), bits 5-7 dig-in progress (+$20 per stationary round, bit 7 = dug in)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitStunTable",
   "meaning": "per-unit: bits 0-3 stun counter (comcen repaired by $9E), bits 5-7 dig-in state ($20 digging, 0 cleared)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitStunTable",
   "meaning": "per-unit; bits 5-7 nonzero = digging in or dug in, then DIG OUT is offered instead of DIG IN",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitStunTable",
   "meaning": "per-unit: bits 0-3 stun counter (cleared for the COMCEN by command $9E), bits 5-7 dig-in progress",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitStunDig",
   "meaning": "per-unit stun counter in the low nibble; $F9C3 = AI comcen, read at $E198",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "unitStunDig",
   "meaning": "per-unit byte; the low nibble is the stun counter. A stunned target lets the drone detonate at 3/4 of the normal score threshold.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "F9C4": [
  {
   "name": "unitEnergyTable",
   "meaning": "per-unit energy, bits 0-5 = 0..50 (shown x2 as a percentage)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitEnergy",
   "meaning": "Bits 0-5 = energy in 2% units (info panel prints value*2 '%').",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitSpottedCounter",
   "meaning": "unit array: bit 7 set = unit is visible to the other side; low 6 bits a counter (initialised to 50)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitEnergy",
   "meaning": "per-unit: bits 0-5 energy 0-50, bit 6 spotted in the current scan cycle, bit 7 visible to the local player",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitEnergyTable",
   "meaning": "per-unit energy bits 0-5 (0..50 = 0..100%), bit 6 = seen, bit 7 = visible/spotted",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitEnergyTable",
   "meaning": "bits 0-5 energy 0-50, bit 6 = spotted this round, bit 7 = visible/drawn (sub_3576 shifts 6 -> 7 each round)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitEnergyTable",
   "meaning": "per-unit energy bits 0-5 (max 50), bit 7 spotted",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitEnergyTable",
   "meaning": "Per-unit energy array (100 entries): bits 0-5 = 0..50 (displayed as 0..100%), upper bits = spotted/visible flags. Masked with $3F when read for comcenEnergySnapshot.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitEnergyTable",
   "meaning": "per-unit energy in bits 0-5 (0-50), bits 6-7 visibility/spotted",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitEnergy",
   "meaning": "Per-unit energy; bit 7 = spotted by the other side (enemy units are drawn on the radar only when set)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "unitEnergy",
   "meaning": "per-unit energy in bits 0-5 (0-50) with spotted/visible bits 6-7; $F9F5 = player comcen (bit 7 tested as 'comcen spotted'), $FA27 = AI comcen",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "FB54": [
  {
   "name": "unitFlagsTable",
   "meaning": "per-unit flag byte (bits 2-6 group/type, bit 6/7 tested before ordering the comcen)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_163A",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitGroupFlags",
   "meaning": "Bit 0 = weapon reloading/fired, bit 1 = moving flag, bits 2-4 = group number (ALPHA..GROVER), bit 6 = member of a group.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitGroupFlags",
   "meaning": "unit array: bit 6 = member of a group, bits 2-5 = group id, bits 0-1 other flags",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitFlagsTable",
   "meaning": "Per-unit flags; bits 2-6 hold the group id (compared with selectedGroupId | $40 after AND $7C)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitStatusFlags",
   "meaning": "per-unit: bit 0 attacked, bit 1 hit (status refresh), bits 2-6 group/formation, bit 6 cleared by a move order, bit 7 unit is at its ordered destination",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDisplayFlags",
   "meaning": "per-unit: bit 0 fired this tick, bit 1 was hit this tick, bits 2-6 copy of unitFlagsTable bits 2-6 (group/side/membership), bit 7 idle/stationary",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitOrderFlags",
   "meaning": "bit 0 = reloading (fired, cleared on the unit's parity round), bit 1 = pinned/under fire (cleared each step), bits 2-5 group id, bit 6 = group membership ordered, bit 7 = stationary (copy of $F7D0 bit 7; enables DIG IN)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitLocalFlags",
   "meaning": "unit array: local mirror of the group bits (bits 2-5 id, bit 6 member) updated immediately by the UI and resynced from F708 by syncLocalGroupFlags; bit 7 = at destination ($34A4), bits 0-1 other flags",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDisplayFlags",
   "meaning": "per-unit display flags: bits 2-6 copy of unitFlagsTable, bit 7 idle",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDisplayFlags",
   "meaning": "per-unit; bit 7 idle/stationary (DIG items vs BLITZ items, watched by the menu loop), bit 6 group membership active, bits 2-4 group number, bit 5 side",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDisplayFlags",
   "meaning": "Per-unit local flag array; repairTabAction tests bit 7 (unit is stationary / at its destination) and bit 6 (group membership/order active) on the comcen to decide whether it must be halted with a $80 move-to-self command before repairing.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDisplayFlags",
   "meaning": "local/UI copy of the group bits (2-6) that the menus change immediately for feedback, plus bit 7 = unit is at its destination and bits 0-1 other per-unit UI flags; re-synced from unitFlagsTable by syncLocalGroupFlags",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDisplayFlags",
   "meaning": "per-unit display flags: bits 2-6 mirror unitGroupByte, bit 7 = idle",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitIdleFlags",
   "meaning": "Per-unit flag byte array; bit 7 = unit is stationary/idle (tested by updateComcenMoveSound), bit 6 = in a group, bits 2-4 = group number.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  },
  {
   "name": "unitGroupFlags",
   "meaning": "per-unit flags; bit 1 = moving. $FBB7 = AI comcen, sampled into $897B/$897C",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "BB": [
  {
   "name": "cursorTargetMode",
   "meaning": "0 = normal browsing; > 0 = choosing a target limited to the unit's range (crosshair, sub_3C20 on click); < 0 = free cell pick that returns the cell in D_91D8/D_91D9.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cursorModeFlag",
   "meaning": "Selects the cursor shape at $218F: 0 block/formation, positive cross, negative block",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cursorOrderMode",
   "meaning": "cursor/order state flag read by the battlefield input code ($218F, $6474); orderGroupNewPosition sets it to $80 (group order pending / group mode)",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cursorTargetMode",
   "meaning": "0 destination pick, 5 = boomer SET TARGET (range-limited crosshair), $80 = free cell pick / custom formation mode of runGroupEditor",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cursorTargetMode",
   "meaning": "mode of the target cursor loop: >0 attack/range cursor, 0 move order, <0 report the picked cell only. orderGroupNewPosition restores it to $80 on exit because the loop zeroes it when the pick is aborted",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cursorTargetMode",
   "meaning": "$FF = free map-cell pick for runTargetCursorLoop (recycler placement)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "cursorTargetMode",
   "meaning": "$FF = free cell pick for runTargetCursorLoop (recycler location)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "2F": [
  {
   "name": "viewportCellRow",
   "meaning": "Row counter 0-4 while drawing the viewport.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scanListLast",
   "meaning": "copy of the last list index returned by collectUnitsInRadius; read by the callers of scanVisibleEnemies ($43AD, $43DF, $3CA0)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "candidateCount",
   "meaning": "index of the last entry of the candidate lists at $0400/$0432/$0464 built by sub_37AA (count-1, $FF = none)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scanResultCount",
   "meaning": "count-1 of units found by the area scan sub_35C3/sub_37AA (negative = none); the hits are in $0400 (unit), $0432 (distance), $0464 (direction)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  }
 ],
 "B7": [
  {
   "name": "screenColorPtrHi",
   "meaning": "High byte of screenColorPtr.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "screenCellPtrHi",
   "meaning": "screen RAM address of the current character cell (hi)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "screenPtrHi",
   "meaning": "Pointer (hi) of screenPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "screenPtrHi",
   "meaning": "pointer (hi) of screenPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "colourCellPtrHi",
   "meaning": "High byte of colourCellPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "colourRamPtrHi",
   "meaning": "pointer (hi) into colour RAM",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "screenPtrHi",
   "meaning": "Screen RAM row pointer (hi).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "43": [
  {
   "name": "textRawMode",
   "meaning": "Text engine: bit 7 set = print character codes without the $987E translation.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textRawMode",
   "meaning": "text engine raw-code flag, cleared before each menu",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textRawMode",
   "meaning": "text engine flag cleared before printing menus",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "textRawMode",
   "meaning": "Text engine flag: bit 7 set prints character codes verbatim instead of translating them through the $987E table. Cleared here so the header and the item names print normally.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rawGlyphMode",
   "meaning": "Bit 7 set = printChar takes A as a glyph index and skips the ASCII translation and control-code handling",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "22": [
  {
   "name": "distPointAX",
   "meaning": "X of point A for the distance routine sub_3870 (here the prospective cursor cell).",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scanCol",
   "meaning": "collectUnitsInRadius: radius on entry, then the current map column of the scan",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupListIndex",
   "meaning": "loop index 0-6 over the group names while runJoinGroupMenu prints the list (reuse of the distance scratch byte)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "zpTargetCol",
   "meaning": "multi-use scratch: minimum destination column while scanning a group, then the chosen target column, then the direction-preference rank in the path search and the unit loop index in issueTrainerGroupOrders",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "cellColA",
   "meaning": "column argument of distanceSquared/cellDistance; also the group bounding box minimum column in sub_E531",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "cellColumn",
   "meaning": "Map column argument of distanceSquared ($3870) and of the queued $90 strike command",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "targetCol",
   "meaning": "Column of the point being ordered / cell column argument of distanceSquared.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "cellColA",
   "meaning": "column of the first point handed to cellDistance/distanceSquared.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "27": [
  {
   "name": "distPointBX",
   "meaning": "X of point B for sub_3870 (here the selected unit).",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scanCentreCol",
   "meaning": "centre column of the scan (distanceSquared reference)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "zpSearchOriginCol",
   "meaning": "column the direction search starts from (group centre or AI comcen)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "originCol",
   "meaning": "origin column for computeCellInDirection and cellDistance (usually the group centre or the AI comcen)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "centreColumn",
   "meaning": "Column of the reference point the threat scan and sub_E66C work around (usually the AI comcen or the current objective)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "centreCol",
   "meaning": "Second column argument of distanceSquared (unit destination column, or the drone column at $EF9E).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "originCol",
   "meaning": "column of the second (reference) point for cellDistance; always the drone's column here, and also the X argument of mapCellToRadarGrid.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "28": [
  {
   "name": "distPointBY",
   "meaning": "Y of point B for sub_3870.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scanCentreRow",
   "meaning": "centre row of the scan",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastRowCandidateIndex",
   "meaning": "site count - 1; wrap value for advanceRowCandidate",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "zpSearchOriginRow",
   "meaning": "row the direction search starts from",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "originRow",
   "meaning": "origin row for computeCellInDirection and cellDistance",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "centreRow",
   "meaning": "Row of that reference point",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "centreRow",
   "meaning": "Second row argument of distanceSquared.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "originRow",
   "meaning": "row of the second point for cellDistance / mapCellToRadarGrid.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "1FA6": [
  {
   "name": "joystickDirectionTable",
   "meaning": "16-entry table joystick nibble -> direction code: $FF invalid, 0 = N, 1 = S, 2 = E, 3 = W, 4 = NW, 5 = NE, 6 = SE, 7 = SW.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "joystickDirectionTable",
   "meaning": "16-entry joystick nibble -> direction code table ($FF = none) used by the group editor scroll handling",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  }
 ],
 "206B": [
  {
   "name": "ownSideBranchA",
   "meaning": "Opcode byte of the 'bcs' after 'cmp #$32' (unit index < 50 = own side); patched to $90/$B0 by overlay A ($7BE6) for player 2/1. Also copied to $750A at $10A2.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  }
 ],
 "2084": [
  {
   "name": "ownSideBranchB",
   "meaning": "Second patched own-side branch opcode (unit selection on fire click), patched at $7BE9.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  }
 ],
 "24E1": [
  {
   "name": "pathMarkerRepeatCount",
   "meaning": "Number of remaining unit-to-destination marker passes (starts at 5, then the marker blinks).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  }
 ],
 "24E2": [
  {
   "name": "pathMarkerX",
   "meaning": "Current map X of the animated mini-map path marker.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  }
 ],
 "24E3": [
  {
   "name": "pathMarkerY",
   "meaning": "Current map Y of the animated mini-map path marker.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  }
 ],
 "90F6": [
  {
   "name": "viewportOriginXSave",
   "meaning": "Viewport origin X saved by drawViewport while it walks zp_A5 through the cells.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "savedViewMapX",
   "meaning": "saved map viewport column while the viewport is being redrawn",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewOriginColSave",
   "meaning": "saved viewOriginCol while a single cell is drawn",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "savedViewOriginCol",
   "meaning": "View origin column saved while drawDroneRadarWindow walks zp_A5",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "viewOriginColSave",
   "meaning": "radar grid origin column saved while drawRadarMap walks zp_A5 through the cells; restored at the end",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "90F7": [
  {
   "name": "viewportOriginYSave",
   "meaning": "Viewport origin Y saved by drawViewport.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "savedViewMapY",
   "meaning": "saved map viewport row while the viewport is being redrawn",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "viewOriginRowSave",
   "meaning": "saved viewOriginRow while a single cell is drawn",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "savedViewOriginRow",
   "meaning": "View origin row saved while drawDroneRadarWindow walks zp_A6",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "viewOriginRowSave",
   "meaning": "radar grid origin row saved by drawRadarMap",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "90FA": [
  {
   "name": "combatUnitA",
   "meaning": "Unit index involved in the current combat event; the info panel refreshes while the shown unit equals it.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentUnit",
   "meaning": "index of the unit being processed by the engine",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentUnitIndex",
   "meaning": "Unit index (0-99) the comm module is servicing; loaded into Y at $EACF in the T35 build, where cpy #$32 splits the two sides. Relevant here only because it makes the 'sta $87CE,y' at $EB29/$EB53 address $8800+ rather than this chunk's strings.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "currentUnit",
   "meaning": "unit index processUnitStep is working on, read by trainerUnitStepHook",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "currentUnit",
   "meaning": "index of the unit the engine is stepping; read by updateAiUnitTracking",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "91CC": [
  {
   "name": "cursorSpriteSlot",
   "meaning": "Sprite number (0 or 1) that sub_3049/sub_2FFA operate on; normally 0 (cursor box), 1 while loading the marker shape.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cursorSpriteIndex",
   "meaning": "Number of the sprite currently used as cursor/marker (0 = main cursor, 1 = second cursor, 4 = blob, ...); bit 7 set = none (positionCursorSprite does nothing, plotting goes to spriteBuildBuffer). Set by drawCircleInSprite to its A argument",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "activeSpriteIndex",
   "meaning": "sprite number (0-7) that the sprite plotting/copy routines (sub_31AF, sub_3239, loadDirectionShapeSprite) target; bit 7 set = plot into the $9025 scratch shape",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cursorSpriteIndex",
   "meaning": "set to 0 by cmdDroneMove so sprite 0 is positioned",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "activeSpriteIndex",
   "meaning": "which sprite loadDirectionShapeSprite fills and enables; cmdDroneMove sets it to 0 for the drone",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "activeSpriteIndex",
   "meaning": "Target sprite of loadDirectionShapeSprite; 5 on the drone screen.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "91D7": [
  {
   "name": "infoPanelUnit",
   "meaning": "Unit index currently shown in the info panel, $FF = none.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "trackedUnit",
   "meaning": "unit the view follows while its menu/targeting is open ($FF = none); set from selectedUnit before updateGameFrame",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "infoPanelUnit",
   "meaning": "unit whose info panel is shown ($FF none); the menu loop keeps it equal to D_90F8",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  }
 ],
 "91D8": [
  {
   "name": "pickedCellX",
   "meaning": "Map X returned by runTargetCursorLoop in free-pick mode (zp_BB < 0).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "orderTargetX",
   "meaning": "map column of the cell chosen for a unit/group order (written at $21FD)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pickedCellX",
   "meaning": "map column of the cell picked by the target cursor loop in report-only mode (cursorTargetMode < 0)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pickedCellX",
   "meaning": "map column returned by runTargetCursorLoop (recycler position)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "pickedCellX",
   "meaning": "map column returned by runTargetCursorLoop in free-pick mode (recycler location)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "91D9": [
  {
   "name": "pickedCellY",
   "meaning": "Map Y returned by runTargetCursorLoop in free-pick mode.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "orderTargetY",
   "meaning": "map row of the cell chosen for a unit/group order (written at $2205)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pickedCellY",
   "meaning": "map row of the picked cell",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pickedCellY",
   "meaning": "map row returned by runTargetCursorLoop",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "pickedCellY",
   "meaning": "map row returned by runTargetCursorLoop in free-pick mode",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "91F0": [
  {
   "name": "combatUnitB",
   "meaning": "Second unit index of the current combat event (see combatUnitA).",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "chosenTargetUnit",
   "meaning": "unit index selected as firing target by the spotting/targeting code for the unit being updated; $FF = none",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fireTargetUnit",
   "meaning": "unit chosen as fire target for the current unit ($FF = none)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "chosenTargetUnit",
   "meaning": "unit index the engine picked as the fire target of the unit just processed ($FF none)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "9204": [
  {
   "name": "highlightedUnit",
   "meaning": "Unit drawn highlighted in the viewport (compared in sub_2772 at $2878); $FF = none.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedUnit",
   "meaning": "index of the unit drawn inverted (highlighted) on the map",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "highlightedUnit",
   "meaning": "unit drawn inverted on the map (set to the selected unit by orderGroupNewPosition)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "highlightedUnit",
   "meaning": "unit drawn highlighted in the viewport ($FF none); set to the selected unit by the unit menu",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "highlightedUnit",
   "meaning": "unit drawn inverted in the viewport; orderGroupNewPosition sets it to the unit being re-positioned",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  }
 ],
 "9205": [
  {
   "name": "secondaryHighlightUnit",
   "meaning": "Second highlighted unit for the viewport; $FF = none (negative also enables the destination-marker animation in the info panel).",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedGroupId",
   "meaning": "group id (bits 2-5 of D_FB54) whose members are highlighted when D_9209 = 2",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "highlightedGroupId",
   "meaning": "group whose members are drawn inverted; $FF = none",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "highlightedGroupKey",
   "meaning": "group key (FB54 & $3C) whose members are drawn highlighted in the viewport; $FF none",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "highlightedGroupId",
   "meaning": "group key whose members the viewport draws inverted, $FF = none; orderGroupNewPosition clears it so only the single unit is highlighted",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  }
 ],
 "9209": [
  {
   "name": "uiLevel",
   "meaning": "0 = browsing the map, 1 = unit selected (command menu), 2+ = group mode (zoomed mini-map). Selects the info-panel text colour via D_5E14.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectionMode",
   "meaning": "2 = a unit group is selected (members drawn inverted)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedGroupSize",
   "meaning": "Number of units in the current selection (1 for a single unit at $5E3D); >= 2 makes $219B build the formation sprite instead of the plain cursor",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "uiLevel",
   "meaning": "0 browsing, 1 single unit selected, 2 group selected; selects the info panel colour and makes the target cursor act on the whole group",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  }
 ],
 "9226": [
  {
   "name": "screenStateFlag9226",
   "meaning": "Cleared by redrawGameScreen; read only by overlay code ($740F).",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "radarBackgroundColour",
   "meaning": "Low colour nibble ORed into the screen RAM byte of every radar cell by drawRadarGlyph ($0A on the drone screen, 0 on screen 1)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "screenStateFlag9226",
   "meaning": "cleared by initRadarScreen; read only by overlay code ($740F)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9245": [
  {
   "name": "unitInspectLockFlags",
   "meaning": "Bit 7 set blocks inspecting/selecting units other than revealedEnemyUnit.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitInspectLockFlags",
   "meaning": "bit 7 = own comcen cloaked (unit inspection limited)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitInspectLockFlags",
   "meaning": "written only by updateComcenCloakLock: bit 7 set while the local COMCEN is cloaked, which restricts unit inspection in the console",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenCloakedFlag",
   "meaning": "Bit 7 = own comcen cloaked: disables the missile screen and ignores the drone launch button",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "unitInspectLockFlags",
   "meaning": "Bit 7 = own comcen cloaked; blocks drone launches.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "92C3": [
  {
   "name": "savedViewportX",
   "meaning": "Viewport origin X preserved across screens (saved every main-loop pass, also written by the $EC00 sub-overlay).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "savedViewOriginCol",
   "meaning": "Saved copy of the view origin column zp_A5",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "92C4": [
  {
   "name": "savedViewportY",
   "meaning": "Viewport origin Y preserved across screens.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "savedViewOriginRow",
   "meaning": "Saved copy of the view origin row zp_A6",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "92F3": [
  {
   "name": "setupPickupMode",
   "meaning": "Bit 7 = setup-phase toggle (flipped by the '*' key in overlay A sub_8658); when set, clicking an own unit sends command $91 (pick up / relocate unit).",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "setupPickupMode",
   "meaning": "cleared before the setup phase",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "customModeFlag",
   "meaning": "bit 7 = custom rules active ($0BA3 bit 6 at menu entry, toggled by '*' during setup, shown as CUSTOM ON/OFF)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "setupToggleFlag",
   "meaning": "bit 7 = state of the '*' key toggle handled by handleStarKeyToggle: CUSTOM rules on the scenario setup screen (mirrors D_0BA3 bit 6 << 1), DESTROY mode during unit placement (clicking an own unit sends $91 and clears it)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "92FC": [
  {
   "name": "revealAllUnitsFlag",
   "meaning": "Bit 7 set lets the info panel and viewport show enemy units (film replay / debug style mode).",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "revealAllFlag",
   "meaning": "bit 7 set: draw enemy units even when not spotted",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "revealAllFlag",
   "meaning": "bit 7: overview shows enemy units even when not spotted",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "revealAllUnitsFlag",
   "meaning": "copied from commBuildFlag: all units visible when playing the trainer",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "revealAllUnitsFlag",
   "meaning": "bit 7 = show all units; cleared at film start",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "revealAllUnits",
   "meaning": "Bit 7 set = beginner battle display, every enemy unit is drawn whether spotted or not (tested by renderMapTile at $284F)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "F708": [
  {
   "name": "unitOwnerFlags",
   "meaning": "Unit flags; bit 5 = owning side (compared in sub_3C20), bits 2-6 used as a group mask in sub_40B2.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitAnimFrame",
   "meaning": "unit array: bits 0-1 select one of 4 glyph variants (facing/animation frame); bit 7 used elsewhere",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitFlags",
   "meaning": "per-unit: bits 0-1 facing (0 N,1 S,2 E,3 W), bit 5 side, bit 7 first-strike bonus available (consumed by resolveAttack); bits 2-4/6 group-related",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitFlagsTable",
   "meaning": "per-unit flags: bits 0-1 facing (0 up,1 down,2 left,3 right), bits 2-4 group number, bit 5 side (1 = units 50-99), bit 6 group membership active, bit 7 blitz charge ready (attack bonus pending)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitStateFlags",
   "meaning": "bits 0-1 facing 0-3, bits 2-5 group id (bit 5 = side), bit 6 = member of a group, bit 7 = blitz charge (energy spent this round, boosts the next shot)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitGroupByte",
   "meaning": "unit array: bits 0-1 animation frame, bits 2-5 group id, bit 6 = member of a group. Authoritative (changed only by executed commands, so identical on both machines)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitFlagsTable",
   "meaning": "per-unit flags: bits 2-4 group, bit 5 side, bit 6 group membership active (& $7C compared with group|$40)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitFlagsTable",
   "meaning": "authoritative per-unit flags: bits 0-1 facing, bits 2-4 group number, bit 5 side, bit 6 group member, bit 7 pending first-strike. Only command executors change the group bits, so both machines stay in step",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitGroupByte",
   "meaning": "per-unit flags: bits 0-1 facing, bits 2-4 group index, bit 5 side, bit 6 in-group; group handlers compare (value & $7C) with groupByte | $40",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitFlags",
   "meaning": "per-unit flags; bit 6 = member of a group (tested at $E948 via $F73A = AI unit 0)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "F76C": [
  {
   "name": "unitTypeFlags",
   "meaning": "Bits 0-2 = unit type (0 GRUNT, 1 RIDER, 2 BOOMER, 3 SPY, 4 COMCEN), bits 3-4 = '*' status markers, bit 7 = unit dead/absent.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitType",
   "meaning": "unit array: bits 0-2 = unit type (0-4 have glyphs), bit 7 set = special state drawn white and inverted ($FF = removed)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitStatusTable",
   "meaning": "Per-unit byte (100 entries); bit 7 set = unit slot unused/dead, skipped by the group scans",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitType",
   "meaning": "per-unit: bits 0-2 type (0 GRUNT, 1 RIDER, 2 BOOMER, 3 SPY, 4 COMCEN per the names at $C964), bit 6 combat modifier (+2 dealt, -25% taken), bit 7 destroyed",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitTypeTable",
   "meaning": "per-unit type byte: bits 0-2 type (0 grunt, 1 rider, 2 boomer, 3 spy, 4 comcen; 5-7 drone/missile class), bit 6 dug in, bit 7 dead; $FF = empty slot",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitTypeTable",
   "meaning": "bits 0-2 unit type (2 = boomer/artillery, 3 = spy, 4 = comcen), bit 3/4 = scored the near/far enemy zone, bit 6 = fortified (dug in), bit 7 = dead ($FF = slot unused)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitType",
   "meaning": "unit array: bits 0-2 unit type, bit 7 set = unit removed/dead (skipped by all group loops)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitTypeTable",
   "meaning": "per-unit type (0-2 = type; 3 = spy cannot cloak/blitz), bit 6 dug in, bit 7 dead, $FF free slot",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitTypeTable",
   "meaning": "per-unit type byte; bit 7 = dead (menu exits / unit skipped)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitTypeTable",
   "meaning": "unit type 0-4 with bit 7 set for a dead/unused unit; every loop in this chunk skips units with bit 7 set",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitTypeTable",
   "meaning": "per-unit type in bits 0-2 (3 = SPY, 4 = COMCEN), bit 6 dug in/fortified, bit 7 dead, $FF = free slot",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitTypeTable",
   "meaning": "per-unit type byte, $FF = unit absent (set for removed units)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "unitTypeFlags",
   "meaning": "Per-unit byte: bits 0-2 type (0 GRUNT 1 RIDER 2 BOOMER 3 SPY 4 COMCEN), bit 7 = dead/unused; counted by drawStatsTab.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "unitType",
   "meaning": "per-unit type byte: bits 0-2 type (0 grunt, 1 rider, 2 boomer, 3 spy, 4 comcen), bits 3-4 zone-scored markers, bit 7 = dead/absent; $F79E = AI unit 0",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "unitTypeFlags",
   "meaning": "100-entry array of unit type (bits 0-2) and flags; bit 7 set = unit dead / not in play",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  }
 ],
 "F7D0": [
  {
   "name": "unitDestX",
   "meaning": "Destination map column; bit 7 set = no destination.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestCol",
   "meaning": "Per-unit destination/ordered map column (100 entries, low 7 bits; bit 7 is a flag copied into unitFlagsTable at $3DF2); selected instead of unitCol by sub_4B5E when Y bit 7 is set and used for the formation sprite",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestCol",
   "meaning": "per-unit ordered destination column, bit 7 set while a move is pending",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestCol",
   "meaning": "per-unit destination column (bits 0-5); bit 7 set = idle (no move order, destination reached)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestCol",
   "meaning": "bits 0-5 destination column, bit 7 = stationary (destination reached)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestX",
   "meaning": "unit array: destination column in bits 0-6, bit 7 set = stationary (destination equals position)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestCol",
   "meaning": "per-unit destination column, bit 7 = idle",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestCol",
   "meaning": "destination column of each unit; bit 7 is the at-destination/stationary flag, so all readers mask with $7F",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestCol",
   "meaning": "per-unit destination column, bit 7 = idle",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestCol",
   "meaning": "per-unit ordered destination column in bits 0-5; $F802 = AI unit 0, $F833 = AI comcen",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "F834": [
  {
   "name": "unitDestY",
   "meaning": "Destination map row; bit 7 set = unit is repairing (info panel shows REPAIR).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestRow",
   "meaning": "Per-unit destination/ordered map row (100 entries, low 7 bits)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestRow",
   "meaning": "per-unit ordered destination row (bit 7 flag)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestRow",
   "meaning": "per-unit destination row (bits 0-5); bit 7 = resting/recovering energy flag",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestRow",
   "meaning": "bits 0-5 destination row, bit 7 = repairing (gained energy while stationary)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestY",
   "meaning": "unit array: destination row in bits 0-6 (bit 7 used as a flag elsewhere)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestRow",
   "meaning": "per-unit destination row",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestRow",
   "meaning": "destination row of each unit (bit 7 used the same way)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestRow",
   "meaning": "per-unit destination row",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDestRow",
   "meaning": "per-unit ordered destination row in bits 0-5; $F866 = AI unit 0, $F897 = AI comcen",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "F898": [
  {
   "name": "unitCloakFlags",
   "meaning": "Bit 7 = CLOAKED.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitStatusA",
   "meaning": "unit array: bit 7 set makes the unit glyph draw on alternate scanlines only (damaged/disrupted look)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitAltCol",
   "meaning": "per-unit secondary column (bits 0-5) with flags: bit 6 toggled by MISC-menu commands $84/$85, bit 7 = unit inactive/hidden (does not scan or attack, harder to spot)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitWaypointCol",
   "meaning": "per-unit next waypoint column (bits 0-5); bit 6 = cloaking ordered (CLOAKING ON), bit 7 = cloak active (unit hidden, does not spot or fire)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitPathCol",
   "meaning": "bits 0-5 logical (path) column, which may lie on another unit's cell; bits 6-7 = cloaked",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitWaypointCol",
   "meaning": "per-unit waypoint column; bits 6-7 = cloak ordered/active",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitWaypointCol",
   "meaning": "per-unit; bit 6 = cloaking ordered, toggled locally by the CLOAKING ON / NO CLOAKING items and sent as $82-$85",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitWaypointCol",
   "meaning": "per-unit path column; bits 6-7 = cloak ordered/active",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitCloakFlags",
   "meaning": "Per-unit; bit 7 = cloaked (drawn half-tone on the radar)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "F8FC": [
  {
   "name": "unitBlitzFlags",
   "meaning": "Bit 7 = BLITZ mode.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitAltRow",
   "meaning": "per-unit secondary row (bits 0-5); bit 6 toggled by commands $95/$96; bit 7 set = unit is moving (enables the first-strike energy sacrifice)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitWaypointRow",
   "meaning": "per-unit next waypoint row (bits 0-5); bit 6 = blitz ordered (SET BLITZ), bit 7 = blitz in effect while moving",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitPathRow",
   "meaning": "bits 0-5 logical (path) row; bits 6-7 = blitz mode (cleared when the unit becomes stationary)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitWaypointRow",
   "meaning": "per-unit waypoint row; bits 6-7 = blitz ordered/active",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitWaypointRow",
   "meaning": "per-unit; bit 6 = blitz ordered, toggled locally by SET BLITZ / BLITZ OFF and sent as $93-$96",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitWaypointRow",
   "meaning": "per-unit path row; bits 6-7 = blitz ordered/active",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "FA28": [
  {
   "name": "unitMoveState",
   "meaning": "Movement state/counter: bit 7 or value >= 10 with FB54 bit 1 changes the ACTION line (MOVE/PINNED).",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitSuppression",
   "meaning": "per-unit signed value reduced by each hit (clamped at -127); read by the status display and movement cost code",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitMovePoints",
   "meaning": "per-unit movement point accumulator (signed, starts at 10): a step costs the terrain value from D_405E (+8 diagonal), being hit subtracts the damage (pinned), blitz adds 25",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitMovePoints",
   "meaning": "signed movement-point balance: +rate per round, -terrain cost per step, 15 when blocked/stationary, 100 on reaching a unit, 80 in setup",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  }
 ],
 "FA8C": [
  {
   "name": "unitDrawFlags",
   "meaning": "Bit 7 alters the tile colour of the unit in the viewport (sub_2772 $286F).",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitFlashFlag",
   "meaning": "unit array: bit 7 set draws the unit in white (colour 1)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitHitEffect",
   "meaning": "per-unit effect code: $80|damage after a hit, $85 when destroyed, 0 none",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitPinnedTimer",
   "meaning": "per-unit: bit 7 set with the hit amount (max $14) when the unit was pinned by fire; 0 = free; $85 for a knocked-out comcen",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitHitFlashTimer",
   "meaning": "nonzero while a hit/explosion is shown on the unit's cell: low nibble counts down per step, then the cell is redrawn",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  }
 ],
 "FAF0": [
  {
   "name": "unitTerrainUnder",
   "meaning": "Map byte (terrain code) saved under the unit; used for movement range/cost look-ups (D_077E -> D_35BB -> D_35B0).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitTerrainUnder",
   "meaning": "unit array: terrain byte of the map cell the unit stands on (the cell itself holds $80|unit); accessed as D_FA70,y with y = $80|unit in renderMapTile",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitTerrainCell",
   "meaning": "map cell value that the unit stands on (the map itself holds $80|unit); $FF for dead units",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitUnderCell",
   "meaning": "per-unit saved map byte of the cell the unit stands on (restored when it leaves); $FF = unit is not on the map",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitSavedTerrain",
   "meaning": "terrain byte under the unit, written back when it leaves; negative ($FF) = unit not on the map",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitUnderCell",
   "meaning": "per-unit terrain byte under the unit, $FF = absent",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "unitTerrainUnder",
   "meaning": "Terrain cell byte under each unit; shown instead of hidden enemy or dead units",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "unitUnderCell",
   "meaning": "per-unit saved map byte of the cell the unit stands on; used as the terrain index in buildUnitThreatTables",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "BFB0": [
  {
   "name": "mapRowAddrLo",
   "meaning": "40-entry low-byte table of map row addresses ($F000 + 40*row), built by the start-up code at $034A.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapRowAddrLo",
   "meaning": "40 low bytes of the $F000 map row addresses, built at $034A and read by readMapCell at $25FC",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ],
 "BFD8": [
  {
   "name": "mapRowAddrHi",
   "meaning": "High bytes of the map row address table (built at $0345).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapRowAddrHi",
   "meaning": "40 high bytes of the map row addresses, built at $0345 and read at $2603; the pair occupies $BFB0-$BFFF, the tail of the VIC bank above the bitmap ($A000-$BF3F)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ],
 "8A00": [
  {
   "name": "spriteData",
   "meaning": "Sprite images, 64 bytes each: $8A00 sprite 0 (mini-map cursor box / crosshair), $8A40 sprite 1 (path marker), $8A80 sprite 2 (expanded viewport cursor frame), $8B40+ unit picture sprites 5-7.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spriteShapes",
   "meaning": "Shape data of sprites 0-7 ($8A00-$8BFF, 64 bytes each; sprite pointers $28-$2F). Sprite 0/1 = cursors and formation ghost, sprite 3 ($8AC0) = path/route line sprite",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "sprite0Data",
   "meaning": "sprite 0 shape (64 bytes); sprite n data lives at $8A00+64*n (pointers $A8+n in the $8C00 screen)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "sprite0Data",
   "meaning": "Sprite 0 shape; receives a copy of the heading line from sprite 3.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "04C8": [
  {
   "name": "tileBuildBuffer",
   "meaning": "32-byte buffer where sub_2772 assembles one 2x2-character map tile before drawViewport copies it into the bitmap.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_1FA6",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tileBuffer",
   "meaning": "32-byte buffer holding one 2x2 character map tile: $04C8 top-left glyph, $04D0 top-right, $04D8 bottom-left, $04E0 bottom-right (8 bitmap bytes each)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tileBuffer",
   "meaning": "32-byte 2x2-character tile built by renderMapTile and copied to the bitmap by drawViewportCell",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tileBuffer",
   "meaning": "32-byte buffer holding one 2x2-character map tile ($04C8 top-left, $04D0 top-right, $04D8 bottom-left, $04E0 bottom-right) built by renderMapTile and copied to the bitmap by drawViewport",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "1B": [
  {
   "name": "textRowCounter",
   "meaning": "row counter / bottom row during block draws (drawCharBlock, fillScreenColourRect)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameBottomRow",
   "meaning": "In drawFrameBox: row of the bottom frame edge; row counter in the fill/block routines; group id compare value in buildGroupFormationSprite",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "firstDeadUnit",
   "meaning": "recycleDeadUnits: index of the first dead unit of the current type found (freed when a second one is found); elsewhere direction scratch",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte1B",
   "meaning": "scratch: direction of the chosen fire target; list index into unitBaseOrder while building the processing order",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupSumDyHi",
   "meaning": "in this chunk: high byte of the summed dy or max x of the group bounds",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte1B",
   "meaning": "general scratch: high byte of the summed row delta, max column of the group box",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rleRunLength",
   "meaning": "drawComcenSchematic: run length of the current RLE run (0 = 256).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "divRemainderLo",
   "meaning": "Low byte of the division remainder; in the 2x glyph magnifier it is the source row offset (0 = top half, 4 = bottom half)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "zpThreatBalanceHi",
   "meaning": "high byte of zpThreatBalanceLo",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiScoreHi",
   "meaning": "high byte of the threat total; highest player unit index found in scoreTargetBlock",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "bestClusterUnit",
   "meaning": "Unit index found by sub_E9A6 in the 2x2 block it is scoring",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "tempGroupIndex",
   "meaning": "Group down-counter of the averaging loop at $EE72.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "checksumCarryCount",
   "meaning": "verifyProgramChecksum: count of ADC carries, added to the sum at the end",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "1C": [
  {
   "name": "textColCounter",
   "meaning": "column counter during block draws",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "frameTileCode",
   "meaning": "Tile character repeated by drawFrameRow/drawFrameColumn (column counter in sub_2B18)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "targetTerrainClass",
   "meaning": "terrain class (0-8) of the cell under the attack target",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scanUnitIndex",
   "meaning": "recycleDeadUnits: unit index cursor running from the side's comcen downwards",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupMemberCount",
   "meaning": "in this chunk: member count (averageGroupMoveVector) or max y of the group bounds",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scratchCounter1C",
   "meaning": "runRepairWhichMenu: item-id loop counter 13..16; unpackRleTo0200: run length of the current RLE run",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte1C",
   "meaning": "general scratch: member count in averageGroupMoveVector (the divisor), max row of the group box",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scratchCounter1C",
   "meaning": "Scratch byte; in this routine the item-id loop counter that walks 13..16 (ENERGY, BATTLE, RADAR, DRONE) while calling addMenuItem.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "divRemainderMid",
   "meaning": "Middle byte of the division remainder",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "zpGroupMemberCount",
   "meaning": "number of live members found by averageGroupPosition ($E882)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiBestScore",
   "meaning": "per-unit influence radius while scanning in isGroupDestinationTooRisky; member count in computeGroupCentroid; running best block score in scoreTargetBlock",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "droneTargetBestScore",
   "meaning": "Running best 2x2 cluster score while scanning for a drone target; initialised from $8979",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "tempGroupCount",
   "meaning": "Member count of the group currently being averaged (divisor for divideSigned16by8).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "aiBestScore",
   "meaning": "running best block score for scoreTargetBlock; $FF is the 'nothing found yet' sentinel (bit 7 set makes any non-negative score win), 0 means 'accept any non-negative score'.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "1D": [
  {
   "name": "blockDataIndex",
   "meaning": "index into the character data while drawCharBlock runs",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "attackerTerrainClass",
   "meaning": "terrain class of the cell under the attacker",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scanUnitsLeft",
   "meaning": "recycleDeadUnits: number of units of the side still to scan (50 down to 1)",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupBoundsMode",
   "meaning": "bit 7 set = computeGroupBounds uses destinations instead of positions; followGroupMovement keeps the unit index here",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "tempByte1D",
   "meaning": "general scratch: bounds mode of computeGroupBounds (bit 7 set = use destinations instead of positions, taken from Y on entry); followGroupMovement parks the unit index here across the averaging call",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "siteCandidateRow",
   "meaning": "row currently being tried for a hill site",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "divRemainderHi",
   "meaning": "High byte of the division remainder; in the magnifier it is the nibble mask $F0 (left half) or $0F (right half)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "zpUnpenetratedCount",
   "meaning": "number of group members that have not yet scored both territory zones; non-zero makes the group fall back to its own half",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiBestTargetUnit",
   "meaning": "best target unit index found by scoreTargetBlock ($FF = none); also a member counter in sub_E531",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "droneTargetBestUnit",
   "meaning": "Unit index of the best drone target found so far, $FF = none",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupActiveCount",
   "meaning": "Count of units of a group that still count as attackers; produced by sub_E531, only read by the leftover fragment at $EFB3 in this file.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "aiBestTargetUnit",
   "meaning": "highest player unit index found in the winning block by scoreTargetBlock; written but not read by this chunk.",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "77": [
  {
   "name": "ownUnitColour",
   "meaning": "colour RAM value for the local player's units (6 = blue, set at $7900)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "ownUnitColour",
   "meaning": "colour of the local player's units, set to 6 (blue) at game start",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "78": [
  {
   "name": "enemyUnitColour",
   "meaning": "colour RAM value for the opponent's units (2 = red, set at $78F7)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "enemyUnitColour",
   "meaning": "colour of the opponent's units, set to 2 (red) at game start",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "B2": [
  {
   "name": "glyphSrcPtr",
   "meaning": "pointer to the glyph / shape data being copied (lo)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "glyphPtr",
   "meaning": "Pointer (lo) to the 8-byte glyph source: $9426 + char*8 for characters, a bitmap cell during magnification, $9C6E + tile*8 for terrain tiles",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rleDestPtr",
   "meaning": "unpackRleTo0200 destination pointer low byte ($00)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "blobStackPtr",
   "meaning": "growTerrainBlob stack index into $0400/$0450/$04A0 (max $50 entries)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "glyphPtr",
   "meaning": "Pointer (lo) to the 8-byte glyph at $9C6E + glyph*8 set by setTerrainTilePtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "glyphPointer",
   "meaning": "Pointer to the 8 bytes of the glyph being drawn (with zp_B3)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "B3": [
  {
   "name": "glyphSrcPtrHi",
   "meaning": "pointer to the glyph / shape data being copied (hi)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "glyphPtrHi",
   "meaning": "Pointer (hi) of glyphPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rleDestPtrHi",
   "meaning": "unpackRleTo0200 destination pointer high byte ($02, $03)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "blobCellsPlaced",
   "meaning": "cells painted so far by growTerrainBlob",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "glyphPtrHi",
   "meaning": "High byte of glyphPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "B8": [
  {
   "name": "blockDataPtr",
   "meaning": "pointer to character block data for drawCharBlock / sub_2C7F (lo)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "blockPtr",
   "meaning": "Pointer (lo) to the frame tile set (drawFrameBox), the width/height/chars block (sub_2B18) or the source cell colour byte (magnify)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "screenRamPtr",
   "meaning": "in paintRadarSweepRow: pointer (lo) into screen RAM $8C00 (multicolour colour cells) for the sweep row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "B9": [
  {
   "name": "blockDataPtrHi",
   "meaning": "pointer to character block data (hi)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "blockPtrHi",
   "meaning": "Pointer (hi) of blockPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "screenRamPtrHi",
   "meaning": "pointer (hi) into screen RAM",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "2808": [
  {
   "name": "tileCellByte",
   "meaning": "self-modified operand: the map cell byte being rendered (bit 7 = unit present)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  }
 ],
 "2840": [
  {
   "name": "ownSideBranchOpcode",
   "meaning": "opcode of 'cpy #50' branch: $90 (BCC) when the local player is 0, $B0 (BCS) when 1; written by sub_7BC6",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  }
 ],
 "2846": [
  {
   "name": "visibilityBranchOpcode",
   "meaning": "opcode of the branch that skips the enemy-visibility test; copy of $2840, inverted for the other replay viewpoint (sub_2A08)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  }
 ],
 "28B8": [
  {
   "name": "unitInvertMask",
   "meaning": "self-modified EOR operand: $FF inverts the unit glyph (selected unit / selected group / D_F76C bit 7), 0 normal",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  }
 ],
 "28C0": [
  {
   "name": "unitHalfScanFlag",
   "meaning": "self-modified operand: $FF draws only every other scanline of the unit glyph (D_F898 bit 7), 0 draws all",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  }
 ],
 "2A29": [
  {
   "name": "useFixedColourFlag",
   "meaning": "non-zero: plotGlyph writes the fixed colour D_2A2A; zero: use the per-character colour table D_93B4 (falling back to D_2A2A for entries of 0)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "colourOverride",
   "meaning": "Nonzero = colour byte forced for every character drawn by sub_2AC8 (ignores charColourTable $93B4); 0 = use the per-character default colour, and when that is 0 too, fillColourValue through the patchable colour operation",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "useFixedColourFlag",
   "meaning": "nonzero: glyph plotters use the fixed colour $2A2A; set to $FF around drawTallGlyph in drawMissileCount",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "colourOverride",
   "meaning": "Nonzero = fixed colour for drawn characters; set to $AA during the drone camera iris animation.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "2A2B": [
  {
   "name": "blockFillFlag",
   "meaning": "non-zero: drawCharBlock fills the block with D_2A2C instead of the data characters",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "useFillChar",
   "meaning": "Nonzero makes sub_2B18 draw fillChar in every cell of the block instead of the characters from the source string",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "2A2C": [
  {
   "name": "blockFillChar",
   "meaning": "character used by drawCharBlock when blockFillFlag is set",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fillChar",
   "meaning": "Character code used by sub_2B18 when useFillChar is set",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "2AF3": [
  {
   "name": "bitmapDrawOpcode",
   "meaning": "patched instruction: LDA/ORA/EOR/CMP (zp_B2),y selecting the bitmap draw mode",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  }
 ],
 "2B00": [
  {
   "name": "colourDrawOpcode",
   "meaning": "patched instruction: LDA/ORA/EOR/CMP D_2A2A selecting the colour draw mode in plotGlyph",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  }
 ],
 "2A85": [
  {
   "name": "fillColourOpcode",
   "meaning": "patched instruction: LDA/ORA/EOR/CMP D_2A2A selecting the colour draw mode in fillScreenColourRect",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  }
 ],
 "9221": [
  {
   "name": "windowWidth",
   "meaning": "width in characters of the current window / character block",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "windowWidth",
   "meaning": "Width in cells of the current window/block (from windowWidthTable or the block header); drawFrameBox and magnifyWindow2x decrement it in place",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "9222": [
  {
   "name": "windowHeight",
   "meaning": "height in characters of the current window / character block",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "windowHeight",
   "meaning": "Height in cells of the current window/block; decremented in place by drawFrameBox",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "92A2": [
  {
   "name": "playerPointsLo",
   "meaning": "two bytes: low byte of each player's point total (index = player); rewritten with the result points at game over",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "sideScoreLo",
   "meaning": "low bytes of two 16-bit scores indexed by the side whose comcen was hit (0/1): each comcen hit adds 2 x damage; read by the score display ($2906, $5845, $C6E6)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pointsAgainstSide",
   "meaning": "two 16-bit scores (lo bytes, hi at $92A4) indexed by side: index i holds the points scored by side i EOR 1 (zone entries, damage)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "playerPointsLo",
   "meaning": "per-side 16-bit score low bytes (film header $D00D-$D010, command $A5 kind 0)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "sideScoreLo",
   "meaning": "two-byte-per-side 16-bit score low bytes, indexed by the side being scored against; command $A5 kind 0 seeds it with handicap points",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "damageAgainstSideLo",
   "meaning": "Two-entry (index = side 0/1) low byte of the running 16-bit damage total scored AGAINST that side; high byte at $92A4. It is indexed by the VICTIM: $3B79-$3B8B sets X = 0 for units 0-49 and X = 1 for units 50-99 and adds 2 * damage to $92A2,x. So $92A2[i] is effectively the score belonging to side i^1, which is why the display prints $92A2[localPlayerIndex ^ 1] as 'us'.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "playerPointsLo",
   "meaning": "Low bytes of the two players' point totals ($92A2 = side 0, $92A3 = side 1); printed by printPlayerPoints.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "92A4": [
  {
   "name": "playerPointsHi",
   "meaning": "two bytes: high byte of each player's point total",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "sideScoreHi",
   "meaning": "high bytes of the two 16-bit scores",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pointsAgainstSideHi",
   "meaning": "high bytes of pointsAgainstSide",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "playerPointsHi",
   "meaning": "per-side score high bytes",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "sideScoreHi",
   "meaning": "high bytes of sideScoreLo",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "damageAgainstSideHi",
   "meaning": "High bytes of damageAgainstSideLo (index = side 0/1).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "playerPointsHi",
   "meaning": "High bytes of the two players' point totals ($92A4/$92A5).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "92A6": [
  {
   "name": "playerOptionLevel",
   "meaning": "two bytes, one per player: setup option displayed as FULL/HALF/NONE (0/1/2), read from the setup data at $7C60 and used when units are deployed ($3E3F)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_2762",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "recyclerMode",
   "meaning": "2 bytes, one per side: recycler setting from the setup screen (FULL/HALF/NONE): 0 = every dead unit is recycled, 1 = two dead units of a type yield one, 2 = no recycler (coordinates $FF)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "recyclerMode",
   "meaning": "per-side recycler setting set by command $9C",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "recyclerMode",
   "meaning": "per-side recycler mode from command $9C (values >= 2 carry no coordinates)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "recyclerMode",
   "meaning": "per side: 0 FULL, 1 HALF, 2 NONE",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "recyclerModeBySide",
   "meaning": "2 bytes: recycler setting per side, 0 FULL, 1 HALF, 2 NONE (words at $0652); chosen in sub_8378 and sent with command $9C",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "recyclerMode",
   "meaning": "2 bytes indexed by absolute side: 0 FULL, 1 HALF, 2 NONE (STATS row RECYCLER via printOptionWord).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "2E0D": [
  {
   "name": "magnifyWidthOdd",
   "meaning": "$FF when the window being magnified has an odd width (middle column drawn half size), else 0",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "2E0E": [
  {
   "name": "magnifyHeightOdd",
   "meaning": "$FF when the window being magnified has an odd height, else 0",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "3237": [
  {
   "name": "spritePlotTemp",
   "meaning": "Scratch of plotSpritePixel: X column, then bit index, then the row for the row*3 multiply",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "3238": [
  {
   "name": "circlePointIndex",
   "meaning": "Loop index 7..0 over the 8 octant points in plotCircleOctantPoints",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "32DA": [
  {
   "name": "circlePointX",
   "meaning": "8 sprite X coordinates of the current circle step (filled by computeCircleOctantPoints)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "32E2": [
  {
   "name": "circlePointY",
   "meaning": "8 sprite Y coordinates of the current circle step",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "9025": [
  {
   "name": "spriteBuildBuffer",
   "meaning": "64-byte off-screen sprite shape buffer ($9025-$9064): plotSpritePixel targets it when the sprite number is negative; buildGroupFormationSprite composes the formation there and copies it to sprite 0; overlay B uses it too ($70A6-$70CC)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lanePermutation",
   "meaning": "4 bytes of the sprite build buffer reused by shuffleLaneColumns as a permutation of 0..3",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "91F2": [
  {
   "name": "circleStepsLeft",
   "meaning": "Remaining circle steps (entries of the dx/dy lists) in drawCircleInSprite",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "91F3": [
  {
   "name": "spritePlotMode",
   "meaning": "Pixel operation for plotSpritePixel/drawCircleInSprite: $80 = toggle (EOR), $40 = set (ORA), 0 = clear (AND)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spritePlotMode",
   "meaning": "pixel operation of sub_31AF: $00 clear (AND), $40 set (ORA), $80 toggle (EOR)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spritePlotMode",
   "meaning": "mode for the sprite shape plotter sub_31AF: $40 = set pixels, $80 = toggle (EOR), 0 = clear pixels",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  }
 ],
 "9206": [
  {
   "name": "selectedGroupId",
   "meaning": "Group/formation id of the selected unit group; ORed with $40 and compared against unitFlagsTable & $7C to find the members (sub_4B5E, buildGroupFormationSprite)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedGroupBits",
   "meaning": "group id of the currently selected group in F708/FB54 format (bits 2-5, i.e. id * 4)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedGroupKey",
   "meaning": "group key of the group being commanded/edited: group number in bits 2-4 plus side bit 5 (FB54 & $3C); OR $40 gives the membership compare value",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedGroupId",
   "meaning": "group id (unitGroupFlags & $3C) of the selected group",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "selectedGroupId",
   "meaning": "group key the group UI is working on: bits 2-4 group number 0-6 (ALPHA..GROVER), bit 5 side (set for unit indices >= 50). Every membership test in the game is (flags & $7C) == selectedGroupId | $40",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  }
 ],
 "8AC0": [
  {
   "name": "pathSpriteShape",
   "meaning": "Shape of sprite 3, the path/route line drawn by drawLineInPathSprite and plotPathSpritePixel",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "sprite3Data",
   "meaning": "sprite 3 shape: shot line drawn by drawShotLineSprite on the overview map",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "sprite3Data",
   "meaning": "Sprite 3 shape; the heading line is drawn here by drawLineInPathSprite.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9E6E": [
  {
   "name": "cursorShapeSquare",
   "meaning": "18-byte (6 rows x 3 bytes) hollow 6x6 square sprite shape, the unit-selected cursor",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "9E80": [
  {
   "name": "cursorShapeCross",
   "meaning": "18-byte thick plus/cross sprite shape (used when zp_BB > 0)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "9E92": [
  {
   "name": "cursorShapeBlock",
   "meaning": "18-byte solid 4x4 block sprite shape, the plain battlefield cursor",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "9C6E": [
  {
   "name": "terrainTileGlyphs",
   "meaning": "8-byte terrain tile definitions indexed by map byte - $40 (pointer computed by setTerrainTilePtr, expanded 2x at $27C2)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "9F37": [
  {
   "name": "circleDxPtrLo",
   "meaning": "10 low bytes of pointers to the dx lists of circle radii 1-10 (lists live at $9F69+)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "9F41": [
  {
   "name": "circleDxPtrHi",
   "meaning": "10 high bytes (all $9F) of the circle dx list pointers",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "9F4B": [
  {
   "name": "circleDyPtrLo",
   "meaning": "10 low bytes of pointers to the dy lists of circle radii 1-10",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "9F55": [
  {
   "name": "circleDyPtrHi",
   "meaning": "10 high bytes (all $9F) of the circle dy list pointers",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "9F5F": [
  {
   "name": "circleStepCount",
   "meaning": "10 bytes: number of (dx,dy) entries minus 1 for each circle radius",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "9810": [
  {
   "name": "tallDigitPairs",
   "meaning": "10 pairs of character codes ($49/$4A ...) forming the two-row tall digits 0-9 drawn by sub_2BB7",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "C008": [
  {
   "name": "pixelBitMasks",
   "meaning": "8-byte table $80,$40,...,$01 in the $C000 overlay used by the sprite plotters to select the bit for column & 7",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "20": [
  {
   "name": "magnifyHalfWidth",
   "meaning": "ceil(windowWidth/2) during magnifyWindow2x",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scanMode",
   "meaning": "bit 7 clear = spotting pass, set = attack-target pass (X on entry to scanVisibleEnemies)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "statsUnitType",
   "meaning": "drawStatsTab: unit type currently counted (0 GRUNT .. 3 SPY).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "aiSumLo20",
   "meaning": "destination-row sum in computeGroupCentroid; number of threatening units in assessThreatAroundAiComcen",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "threatUnitCount",
   "meaning": "Number of enemy units counted by the sub_EBEF threat scan (0 selects the 'no direction' return path at $EC7C)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  }
 ],
 "7E": [
  {
   "name": "textState7E",
   "meaning": "Cleared by initBitmapScreen; only otherwise touched by the $C000 text renderer ($C187 read, $C1DD write)",
   "scope": "zp",
   "confidence": "low",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "decimalValueHigh",
   "meaning": "High (third) byte of the 24-bit value passed to printDecimal; cleared by the conversion",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "98": [
  {
   "name": "spriteRowPtr",
   "meaning": "Pointer (lo) to the sprite shape byte row being plotted by plotSpritePixel",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spritePlotPtr",
   "meaning": "pointer (lo) into sprite data used by the sprite pixel plotter sub_31AF; loadDirectionShapeSprite uses it as the copy source pointer",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "99": [
  {
   "name": "spriteRowPtrHi",
   "meaning": "Pointer (hi) for plotSpritePixel ($8A or $90)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "spritePlotPtrHi",
   "meaning": "sprite data pointer (hi)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "AD": [
  {
   "name": "circleDxPtr",
   "meaning": "Pointer (lo) to the dx list of the current circle radius",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "AE": [
  {
   "name": "circleDxPtrHi",
   "meaning": "Pointer (hi) of circleDxPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "AF": [
  {
   "name": "circleDyPtr",
   "meaning": "Pointer (lo) to the dy list of the current circle radius",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "B0": [
  {
   "name": "circleDyPtrHi",
   "meaning": "Pointer (hi) of circleDyPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_2BDD",
   "unit": "game/mainProgram0800"
  }
 ],
 "1E": [
  {
   "name": "losHeight",
   "meaning": "running terrain height along the line of sight (checkLineOfSightStep); in resolveAttack the target's terrain height",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "siteRetryCount",
   "meaning": "remaining column tries (20) for the current site row",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "decimalDigitIndex",
   "meaning": "Index of the most significant digit in the decimal buffer at $92DA",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "zpGroupAvgDestCol",
   "meaning": "average ordered destination column of the group (from averageGroupPosition)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "groupDestCentreCol",
   "meaning": "average destination column produced by computeGroupCentroid; cleared as a threat accumulator in assessThreatAroundAiComcen",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "droneScanDirection",
   "meaning": "Compass direction 0-7 of the ray walked when looking for a drone target ((safestDirection + 4) & 7)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupCentreColWork",
   "meaning": "Group centre column returned by sub_E531/sub_E882.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "1F": [
  {
   "name": "losRise",
   "meaning": "$80 until the line of sight went uphill, then the last positive height step; a fall afterwards blocks the view",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "siteStartRowIndex",
   "meaning": "row-candidate index at which the search for the current site started (detects a full cycle)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "divBitCounter",
   "meaning": "Bit counter ($18 = 24) of the divide-by-10 loop",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "zpGroupAvgDestRow",
   "meaning": "average ordered destination row of the group",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "groupDestCentreRow",
   "meaning": "average destination row from computeGroupCentroid; total threat weight in assessThreatAroundAiComcen",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "droneScanRadius",
   "meaning": "Radius 4..14 along that ray; in sub_EBEF the same location is the saturating total threat weight",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupCentreRowWork",
   "meaning": "Group centre row returned by sub_E531/sub_E882; the opening destination row is derived from it at $ED25.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "23": [
  {
   "name": "scanStartCol",
   "meaning": "first (clipped) map column of the scan square",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "zpSearchDistance",
   "meaning": "minimum destination row while scanning a group; distance accepted so far in the path search",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiScratch23",
   "meaning": "Direction index of the blur loop; in runAiMissileTurn the accumulated reluctance score of the anti-drone shot",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  }
 ],
 "25": [
  {
   "name": "scanColCount",
   "meaning": "number of columns to scan per row (2r+1, clipped at the left edge)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "zpGroupMaxDestRow",
   "meaning": "maximum ordered destination row of the group",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "26": [
  {
   "name": "scanRowCount",
   "meaning": "number of rows left to scan",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "zpChosenDirection",
   "meaning": "compass direction 0..7 selected by the path search",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "chosenDirection",
   "meaning": "direction slot selected from the direction fan by the move search at $E435",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "29": [
  {
   "name": "scanListIndex",
   "meaning": "index of the last entry written to scanUnitList ($FF = empty)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rowCandidateIndex",
   "meaning": "index into the candidate row table $0325",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "zpReplanDelay",
   "meaning": "number of game-clock units before the group is reconsidered (minimum 4, capped by trainerReplanUrgency)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "missileReloadValue",
   "meaning": "Half the range to the drone; loaded into $8970 as the AI's missile reload delay",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  }
 ],
 "6B": [
  {
   "name": "targetDirection",
   "meaning": "direction code (0 N, 1 S, 2 E, 3 W) of the unit's current target; candidates in another direction get +1 distance in scanVisibleEnemies; written from scanDirectionList by the target pickers ($3CD3, $3CEE, $4420, $4446) and copied into $F708 bits 0-1 (facing) at $4464",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitFacing",
   "meaning": "facing/aim direction of the unit being processed: 0 up, 1 down, 2 left, 3 right (stored in unitFlagsTable bits 0-1); set to the direction of the chosen target",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentUnitFacing",
   "meaning": "facing/heading 0-3 of the unit being processed; stored into bits 0-1 of unitStateFlags",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "blobDirectionFlags",
   "meaning": "growTerrainBlob: bits 0-3 = N/W/E/S already tried for the current stack cell, bit 6 = 2x2 alignment check required (from $0304)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0200": [
  {
   "name": "spriteShapeBuffer",
   "meaning": "$0200-$03FF: 8 x 64-byte sprite shapes produced by the RLE decompressor sub_67CB (sources $FD70, $FE24 in overlay B, unit pictures $CD52.. for sub_CD0A); loadDirectionShapeSprite reads shape A from $0200+64*A. Overwrites the saved setup block loaded at boot.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "contourRuleTable",
   "meaning": "256-byte table indexed by neighbourMask: 0 = nothing, $01-$7F = contour tile to place (minus $0324), $80-$8F = randomly erase the neighbour in bits 0-2, $90-$FE = erase that neighbour or else fill, $FF = fill with the interior tile. Loaded from track 18 sector 15 by sub_10BB; the page is also used as BAM buffer and sprite shape buffer elsewhere",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "bamBuffer",
   "meaning": "BAM sector read by checkDiskId; a data disk must have bytes $0203-$028F all zero",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0400": [
  {
   "name": "scanUnitList",
   "meaning": "up to 50 unit indices found by collectUnitsInRadius ($FF = entry rejected); reuses the copy-protection table area",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "candidateUnitList",
   "meaning": "work list (up to 50 entries) of enemy unit indices found by sub_37AA within a radius; entries set to $FF by sub_35C3 when line of sight fails. Overlays the copy-protection answer table, which is only needed at start-up",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scratchList0400",
   "meaning": "100-byte scratch: per-group member counts (rebuildGroupTables), unit list of the area scan, and ordering marks while building the processing order (0 unordered, 1 stacked, 5 ordered)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "blobStackCol",
   "meaning": "80-entry column stack of growTerrainBlob (overwrites the copy-protection answer table)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "unitThreatRadius",
   "meaning": "100-byte scratch table (reuses the copy-protection answer area): influence radius of every unit, built by buildUnitThreatTables; also reused for the 8 smoothed direction buckets at $EC87",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "threatRingScratch",
   "meaning": "8 bytes of the $0400 page (the start-up copy-protection answer table, free once the game runs and cleared every round by finishRound) used as the unsmoothed copy of the direction ring during the blur",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "scratchPage0400",
   "meaning": "start of the 256-byte multi-purpose scratch page; at boot it is $B0, the constant XORed into the checksum. After boot the whole page is reused: 100-entry unit scan list / group member counts (main), 80-entry blob column stack (map generator, ovl A $71CA), threat radius table (comm module $E6EC/$EC87)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "0432": [
  {
   "name": "scanDistSqList",
   "meaning": "squared distance of each scanUnitList entry from the scanning unit",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "candidateDistList",
   "meaning": "parallel to candidateUnitList: dx^2 + dy^2 distance metric (table D_3860, saturating at $FF)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scanResultDistance",
   "meaning": "distance of each unit found by the area scan (parallel to $0400)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scanDistanceList",
   "meaning": "scratch list parallel to $0400: squared distance of each scanned unit; also the blob row stack area of the map generator",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "0464": [
  {
   "name": "scanDirectionList",
   "meaning": "principal direction code (0 N,1 S,2 E,3 W) from the scanning unit to each list entry",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "candidateDirList",
   "meaning": "parallel to candidateUnitList: direction 0-3 (up/down/left/right) from the viewing unit to the candidate (sub_375D)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scanResultDirection",
   "meaning": "direction toward each unit found by the area scan (parallel to $0400); also the pending-unit stack while building the processing order",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitThreatWeight",
   "meaning": "100-byte scratch table parallel to unitThreatRadius: strength weight of each unit",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupDestColHigh",
   "meaning": "12 high bytes of the 16-bit destination-column sums; lives in the $0400 page that held the copy-protection lists and is free once the game runs.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "scanDirectionList",
   "meaning": "scratch parallel to $0400 (direction 0-3 to each scanned unit); also the 12-entry high-byte half of a 16-bit statistics accumulator whose low bytes are at $88D2 (sub-overlay $EC00, $ED95/$EDF5)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "077E": [
  {
   "name": "terrainClassTable",
   "meaning": "128-entry table indexed by map cell value (0-$7F): terrain class 0-8 (blank $40 -> 0, $41-$54 -> 5, $55-$5C -> 1, $5D-$60 -> 2, $61-$64 -> 4, $65-$66 -> 6, $67/$6C -> 7, $68-$6B/$6F-$72 -> 8, $6D -> 3, $6E -> 5); the class indexes terrainHeightTable and terrainSightBonusTable",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "terrainClassTable",
   "meaning": "terrain byte -> movement/terrain class (0-8) used to index terrainMoveCostTable, $38B3 and $35BB; $40 (blank) -> 0, $41-$54 -> 5, $55-$5C -> 1, $5D-$60 -> 2, $61-$64 -> 4, $67 -> 7",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "terrainClassTable",
   "meaning": "map cell value -> terrain class: $40 -> 0 plain, $21-$34 -> 3 lower contour, $41-$54 -> 5 upper contour, $55-$5C -> 1 river, $5D-$60 -> 2 forest, $61-$64 -> 4 lower hill, $65-$66 -> 6 summit, $67 -> 7 recycler; classes 3-6 have heights 1-4 in $38B3",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "8A80": [
  {
   "name": "sprite2Data",
   "meaning": "sprite 2 shape: battlefield centre cursor box / crosshair, or the comcen cross marker in overlay B",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "sprite2Data",
   "meaning": "Sprite 2 shape; receives the 64-byte targeting reticle from $FF93 at launch.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "8B40": [
  {
   "name": "sprite5Data",
   "meaning": "sprite 5 shape (direction shape when $91CC = 5; first of the 3-sprite unit picture copied by sub_CD0A)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitPictureSpriteShapes",
   "meaning": "192-byte shape area of sprites 5, 6 and 7 (pointers $2D/$2E/$2F in VIC bank 2); filled by showUnitPictureSprites with the three animation frames of a unit type.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "900A": [
  {
   "name": "sprite2ColorShadow",
   "meaning": "colour of sprite 2 (0 black cursor, $0A comcen marker)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "900B": [
  {
   "name": "sprite3ColorShadow",
   "meaning": "colour of sprite 3 (1 white for the shot line)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "900C": [
  {
   "name": "sprite4ColorShadow",
   "meaning": "colour of sprite 4 ($12 = flashing white/red comcen rings)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "9012": [
  {
   "name": "sprite2XShadow",
   "meaning": "sprite 2 X position (low byte)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "sprite2XShadow",
   "meaning": "sprite 2 X position = radar target cursor (cross marker) on screen 1",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9013": [
  {
   "name": "sprite3XShadow",
   "meaning": "sprite 3 X position",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "9014": [
  {
   "name": "sprite4XShadow",
   "meaning": "sprite 4 X position",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "901A": [
  {
   "name": "sprite2YShadow",
   "meaning": "sprite 2 Y position",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "sprite2YShadow",
   "meaning": "sprite 2 Y position = radar target cursor",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "901B": [
  {
   "name": "sprite3YShadow",
   "meaning": "sprite 3 Y position",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "901C": [
  {
   "name": "sprite4YShadow",
   "meaning": "sprite 4 Y position",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "91DC": [
  {
   "name": "boomerTargetTable",
   "meaning": "per-Boomer ordered target unit ($80|unit, 0 = none), indexed by the Boomer's ordinal among type-2 units (sub_3C5D)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "boomerTargetTable",
   "meaning": "one byte per boomer (ordinal among type-2 units, up to 20 entries $91DC-$91EF): bit 7 = target locked, bits 0-6 = target unit index; 0 = no target",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "boomerTargetTable",
   "meaning": "per-boomer assigned target entries read/cleared through sub_3C74",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "boomerTargetTable",
   "meaning": "per-boomer target lock, cleared by cmdClearTarget",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "boomerTargetTable",
   "meaning": "per-BOOMER locked target (index = BOOMER ordinal from getBoomerTarget); 0 = none, else target | $80",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "91F1": [
  {
   "name": "scanRange",
   "meaning": "sight/fire range (0-10) computed by scanVisibleEnemies; the callers then reuse it as the best candidate distance",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "chosenTargetDist",
   "meaning": "distance metric of chosenTargetUnit (also the search radius passed to sub_37AA by sub_35C3)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "fireTargetDistance",
   "meaning": "distance of the chosen fire target (minimum search)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  }
 ],
 "9208": [
  {
   "name": "scoreDirtyCounter",
   "meaning": "incremented whenever a side scores; $C519 redraws the score line when it differs from $91CB",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "scoreChangeCounter",
   "meaning": "incremented whenever a score changes (status display refresh)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastShownClock",
   "meaning": "Value of gameClock the last time the status line clock was drawn",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "9227": [
  {
   "name": "scanUnitTerrain",
   "meaning": "map cell value under the scanning unit",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentUnitTerrain",
   "meaning": "terrain byte saved under the current unit (used to redraw its vacated cell)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  }
 ],
 "9228": [
  {
   "name": "scanUnitType",
   "meaning": "type (0-4) of the scanning unit",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentUnitType",
   "meaning": "type 0-7 of the current unit (0 grunt?, 1 rider?, 2 boomer/artillery, 3 spy, 4 comcen)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  }
 ],
 "9229": [
  {
   "name": "scanUnitTerrainHeight",
   "meaning": "terrain height under the scanning unit; nonzero lets the unit see over friendly units",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "923D": [
  {
   "name": "attackerType",
   "meaning": "type of the attacking unit in resolveAttack (2 = Boomer selects dotted shot line and sound 11)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "attackerType",
   "meaning": "Type of the attacking unit; 2 (Boomer) makes the viewport shot line dotted (3 steps per pixel).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9240": [
  {
   "name": "hitDamage",
   "meaning": "damage currently being applied to a unit (reduceUnitEnergy/recordUnitHit/applyHitToUnit)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_32DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "damageAmount",
   "meaning": "amount of the current hit being applied (parameter of sub_3BDF / the damage routine); compared with random & $3F for system damage",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  }
 ],
 "7D": [
  {
   "name": "unusedByte7D",
   "meaning": "written with 0 by animateRingBurst and never read anywhere (dead store)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  }
 ],
 "3CF1": [
  {
   "name": "ringAnimStep",
   "meaning": "current radius index of animateRingBurst",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  }
 ],
 "3D56": [
  {
   "name": "comcenSystemIndex",
   "meaning": "loop index 3..1 of rollComcenSystemDamage",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  }
 ],
 "67CA": [
  {
   "name": "currentSoundId",
   "meaning": "id of the sound effect currently playing in the $C89A sound driver (0 = none); applyBlitzStepCost only starts sound $16 when this is 0, $14 or $16",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentSoundId",
   "meaning": "id of the sound effect currently playing (compared with $14/$16 before starting the comcen sounds)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  }
 ],
 "9009": [
  {
   "name": "sprite1ColourShadow",
   "meaning": "colour shadow of sprite 1 ($9008+1); $12 = two alternating colours (white/red flashing) for the ring burst",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  }
 ],
 "91F4": [
  {
   "name": "groupStatusTable",
   "meaning": "16 bytes, one per group (unitFlagsTable bits 2-5): bits 3-5 = terrain class of the slowest member, bits 0-2 = type-derived value; rebuilt each tick by sub_48DD, cleared by initAllUnits",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupMoveTable",
   "meaning": "16 entries (group 0-7 per side, bit 3 = side): bits 0-2 slowest speed class, bits 3-5 worst terrain class, bits 6-7 group flags",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupStatusTable",
   "meaning": "16 bytes per group id: bit 7 = group cloaked, bit 6 = group blitz (set/cleared by the group cloak/blitz commands)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupStatusTable",
   "meaning": "16 entries indexed by (groupByte & $3C) >> 2 = group index 0-6 plus 8 for side 1; bit 7 = group cloaked, bit 6 = group blitzing",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "920D": [
  {
   "name": "screenStateFlags920D",
   "meaning": "flag byte cleared by endGame and on screen switches (bit 0 set at $52EB); exact meaning unknown",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "droneStateFlags",
   "meaning": "drone control flags: bit 6 = drone under local control (moves are sent every exchange), bit 7 gates $8E, bit 5 cleared by own detonation, bit 0 set when a drone move is executed; bits 0-5 cleared when the opponent's drone finishes",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "droneStateFlags",
   "meaning": "drone control state. Bit 6 = the drone is under local control (sendOutgoingPacket then steers it and queues $8D); bit 7 gates the $8E detonate and the missile marker path in $33C8; bit 0 is set when the opponent's drone position arrives; cmdDroneArrived clears bits 6-7 and a remote release/detonate clears bits 0-5",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "droneStateFlags",
   "meaning": "drone control flags; bit 0 set = a drone is active (DRONE ALERT panel flashes); cleared to 0 by showDroneKillBurst",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "droneStateFlags",
   "meaning": "Bit 7 = own drone in flight, bit 6 = steered locally (sendOutgoingPacket emits $8D moves), bit 5 = strike ($8E) pending, bit 0 set by cmdDroneMove. Set to $C0 at launch, &= $3F when the flight ends.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "droneStateFlags",
   "meaning": "bit 0 = a drone is in the air; buildTrainerReplyPacket uses it to swap the $EE00 sub-overlay in and out",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "droneStateFlags",
   "meaning": "Drone state: bit 0 a drone move command is in flight, bit 6 the drone is being steered locally, bit 7 a drone is active",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "droneStateFlags",
   "meaning": "drone control flags; bit 0 = a drone move has been executed, i.e. a drone is in the air. The comm module tests it at $E09E/$E0C1 to decide whether to keep this page resident; commands $8E/$8F emitted here make the engine clear it.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "923B": [
  {
   "name": "recycledUnit",
   "meaning": "index of the unit most recently recycled on the local side (set by recycleDeadUnits, read at $417B)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingRedrawUnit",
   "meaning": "unit to redraw next frame ($FF = none)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingRedrawUnit",
   "meaning": "unit to redraw in the tick loop ($417B); $FF = none; set by own cloak on/off commands (and by recycleDeadUnits)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingRedrawUnit",
   "meaning": "unit the tick loop must redraw ($417B); set by own single-unit cloak on/off commands",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingRedrawUnit",
   "meaning": "reset to $FF (none) at session start",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "9242": [
  {
   "name": "unitDrawOverride",
   "meaning": "bit 7 set changes how units are drawn (sub_5BDA skips the type/side lookup); set by overlay A, cleared by haltAllUnitsAndReveal",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDrawOverride",
   "meaning": "bit 7 set: overview draws the terrain under units instead of unit blobs",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitDrawOverride",
   "meaning": "cleared before the unit setup phase",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "unitDrawOverride",
   "meaning": "$FE written by resetSelectionAndView",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "unitDrawOverride",
   "meaning": "set to $FE by sub_8364 together with selectedUnit while the viewport is hidden",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "9243": [
  {
   "name": "radarEventFlag9243",
   "meaning": "nonzero enables a random event in overlay B screen 1 ($7604); cleared at game end",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "missileJamFlag",
   "meaning": "Nonzero ($80, set with the DAMAGE option when a drone blast hits the own comcen): missile launches succeed only on a random coin flip and the radar sweep is disturbed",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "comcenRadarStatus",
   "meaning": "comcenSystemStatus[2] = RADAR system of the comcen (0 ok, nonzero damaged): a damaged radar randomly blanks whole sweeps and no longer keeps spotted enemy cells lit; only ever cleared by the main program ($3FED), damage is presumably written through the indexed base $9241",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9244": [
  {
   "name": "radarEventFlag9244",
   "meaning": "nonzero enables a random event in overlay B screen 2 ($804D); cleared at game end",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "droneJamFlag",
   "meaning": "Nonzero ($80, same cause): drone launches succeed only 1 in 4 and the radar is randomly blanked to $AA each turn",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "droneSystemStatus",
   "meaning": "comcenSystemStatus[3] ($9241+3): $80 when the comcen's drone system was damaged by a hit (sub_3D57). Then only 1 in 4 launch presses succeeds and half of the camera steps show static.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "92A8": [
  {
   "name": "recyclerCol",
   "meaning": "2 bytes, one per side: map column of the side's recycler, bit 7 set = none",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "recyclerCol",
   "meaning": "per-side column of the RECYCLER (unit respawn point), bit 7 = none",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "recyclerCol",
   "meaning": "per-side recycler column",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "recyclerCol",
   "meaning": "per-side recycler map column",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "recyclerCol",
   "meaning": "2 bytes: recycler column per side, $FF (none) at session start",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "recyclerCol",
   "meaning": "per side map column of the recycler, bit 7 = none",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "92AA": [
  {
   "name": "recyclerRow",
   "meaning": "2 bytes, one per side: map row of the side's recycler",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "recyclerRow",
   "meaning": "per-side row of the RECYCLER",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "recyclerRow",
   "meaning": "per-side recycler row",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "recyclerRow",
   "meaning": "per-side recycler map row",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "recyclerRow",
   "meaning": "per side map row of the recycler",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "92F9": [
  {
   "name": "viewStateFlags92F9",
   "meaning": "flag byte cleared by endGame and when a screen is switched; set by overlay B ($79EB) with bit 5 and a toggled bit 4",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_3C50",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "strikeStateFlags",
   "meaning": "state of the special weapon/strike sequence: $80 set by command $92, cleared by $97, $40 set by $8E, bit 4 set by an own $92",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "droneFlags",
   "meaning": "0 at launch ($97); $80 = drone arrived/finished (seen $92 in either packet), $40 = detonated ($8E), $10 = own arrival executed; bits 4-5 block further drone moves",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "droneFlags",
   "meaning": "drone life cycle: 0 at launch ($97), bit 7 = arrived/finished (set by either merger on seeing $92), $40 = detonated ($8E), bit 4 = own arrival, bits 4-5 block further $8D processing",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "droneFlags",
   "meaning": "drone lifecycle flags: 0 while in flight; bit 0 set by checkStrikeHitsDrone when a missile hits the drone; bit 4 (own $92 executed) consumed and turned into bit 5 by showDroneKillBurst",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "droneFlags",
   "meaning": "drone lifecycle flags; bit 7 = the drone has arrived or been destroyed (set by the packet merger when a $92 is seen). While it is set this routine emits nothing at all.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  },
  {
   "name": "droneStrikeFlags",
   "meaning": "drone/special-weapon state flags; cleared on a screen switch unless $0B9B bit 7 is set",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "0410": [
  {
   "name": "groupWorstTerrainCost",
   "meaning": "16-byte scratch: highest terrain cost among a group's members",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupWorstTerrainCost",
   "meaning": "16-byte scratch (inside the scratch page): worst terrain cost of each group ($48ED/$4942/$493D)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "0420": [
  {
   "name": "groupBestRate",
   "meaning": "16-byte scratch: lowest movement rate among a group's members",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupBestRate",
   "meaning": "16-byte scratch: slowest movement rate of each group ($48F2/$4960/$495B)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "0430": [
  {
   "name": "groupMoveTableOld",
   "meaning": "16-byte copy of groupMoveTable from the previous round (bits 6-7 are carried over)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "groupMoveTableOld",
   "meaning": "16-byte scratch: previous-round copy of the group move table ($48E2/$4978)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "406E": [
  {
   "name": "unitMovedFlag",
   "meaning": "set by moveUnitToCell when the current unit changed cells; L_436F then redraws the vacated cell",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  }
 ],
 "8726": [
  {
   "name": "msgShowModeText",
   "meaning": "4-character slot in message 6 'SHOW ****' that receives US/THEM/BOTH",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "showMessageViewWord",
   "meaning": "The four '*' bytes inside the msgShowViewTemplate string ($8721 'SHOW ****'). The film-view key handler copies filmViewWordTable[(playModeFlags & 3) * 4] here 4 bytes at a time ($4120 lda filmViewWordTable,y / $4123 sta $8726,x) before queueing message id 6.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "8730": [
  {
   "name": "msgSpeedDigit",
   "meaning": "digit slot of message 7 'SPEED *'",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "speedMessageDigit",
   "meaning": "The single '*' byte of msgSpeedTemplate ($872A 'SPEED *'). $40DE stores the raw key code of the pressed digit ($B1-$B9) here; because bit 7 is set the digit is both the character and the string terminator. Message id 7 is queued immediately after at $40E3.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "86FB": [
  {
   "name": "filmViewModeNames",
   "meaning": "3 x 4-byte names US/THEM/BOTH (bit-7 terminated) in overlay B",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  }
 ],
 "90FD": [
  {
   "name": "unitProcessOrder",
   "meaning": "100-byte list of unit indices in this round's processing order (rebuilt by finishRound)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitProcessOrder",
   "meaning": "100-byte unit processing order list",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "9162": [
  {
   "name": "unitOrderIndex",
   "meaning": "position in unitProcessOrder of the next unit to process (0-99, 100 = round complete)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitOrderIndex",
   "meaning": "index of the next unit in the processing order; the blackout loop copies it into the CMP operand at $073E and runs until it wraps back to that value",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "9164": [
  {
   "name": "unitBaseOrder",
   "meaning": "100-byte base order of unit indices, randomly shuffled two entries at a time (lock-step RNG)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitBaseOrder",
   "meaning": "100-byte base unit order list",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "91CB": [
  {
   "name": "roundsRemaining",
   "meaning": "game clock in rounds, counts down to 0 (game over on points); its parity against the unit index decides which units fire/reload this round",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameClock",
   "meaning": "game clock value that counts down during the battle (game ends on time when it reaches 0, $4606); film records are stamped with it",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameClock",
   "meaning": "game time counter (counts down to 0 = time up); stored in film time stamps and the film header",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "roundsRemaining",
   "meaning": "game clock; $FF during setup, then roundsByGameType[type] when the battle starts",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "gameClock",
   "meaning": "game length in rounds (from gameClockByTypeTable)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "gameClock",
   "meaning": "Game clock shown on the status line; a change triggers the clock refresh in updateStatusMessage",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "gameClockClicks",
   "meaning": "Game clock in 'clicks' (the unit shown as CLICKS: on the status line); also the film time stamp. Printed by drawStatusScoreLine.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  },
  {
   "name": "gameClock",
   "meaning": "rounds remaining, counting down; the AI uses it as the time base for all its replan deadlines",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "roundsRemaining",
   "meaning": "game clock, counts down; used as a time stamp by the AI",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "gameClock",
   "meaning": "Rounds remaining, counts down; stamped into aiOrderClockStamp",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "gameClock",
   "meaning": "Rounds remaining (counts down). Read here to build the per-unit activation clocks and the phase-change clock, then set back to $FF because the real clock is only started later at $7B86.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "91DA": [
  {
   "name": "currentUnitCol",
   "meaning": "column of the current unit before its step (cell to redraw); also the cursor column in followDirectedUnit",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "blobSpreadH",
   "meaning": "growTerrainBlob: probability (x/256) of growing west/east (reuses currentUnitCol)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "91DB": [
  {
   "name": "currentUnitRow",
   "meaning": "row of the current unit before its step",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "blobSpreadV",
   "meaning": "growTerrainBlob: probability (x/256) of growing north/south (reuses currentUnitRow)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "922C": [
  {
   "name": "currentUnitCloakBits",
   "meaning": "bits 6-7 (cloak flags) of the current unit's $F898 entry, re-applied when the path column is rewritten",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  }
 ],
 "923A": [
  {
   "name": "pendingRedrawGroup",
   "meaning": "group id whose members must be redrawn (bit 6 added for the compare with $F708 & $7C); $FF = none",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingRedrawGroup",
   "meaning": "group id whose members must be redrawn by the tick loop ($418D); $FF = none; set by own group-cloak commands",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingRedrawGroup",
   "meaning": "group whose members the tick loop must redraw ($418D); set by own group cloak on/off commands",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingRedrawGroup",
   "meaning": "reset to $FF (none) at session start",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "923C": [
  {
   "name": "commandSlice",
   "meaning": "index of the current 10-unit (setup: 50-unit) command time slice within the round = unitOrderIndex / 10",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameClockSubTick",
   "meaning": "sub-division (0-15) of the game clock unit, computed at $41EB; film records are due when both it and gameClock match",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "exchangeCounter",
   "meaning": "tick/10 quotient at the last command exchange; its low nibble is stored in film time stamps and must match on playback",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "923F": [
  {
   "name": "unitsFrozenFlags",
   "meaning": "bit 7 = units are not processed (set with $C0 at game end)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameOverFlags",
   "meaning": "$C0 when the game ends (abort command, film end, computeGameResult)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameStateFlags923F",
   "meaning": "set to $C0 at game-over/knockout points ($28F9, $524E, $5A03); bit 6 blocks system repairs and fire actions in sub_0ED5",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameOverFlags",
   "meaning": "Set to $C0 when a game ends or a film finishes; bit 6 is what checkFirePressedInteractive / isPlayerInputAllowed test to refuse further player commands.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameOverFlags",
   "meaning": "set to $C0 when the opponent aborts the session (also written by the film end and computeGameResult)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "gameOverFlags",
   "meaning": "Set to $C0 when the game ends (computeGameResult, the $8C abort command, end of film). Bit 6 is the half tested by getSoloOrFlag923F, so once the game is over the REPAIR menu becomes inert.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "92AC": [
  {
   "name": "scoreValueBySide",
   "meaning": "per-side points awarded when one of that side's units enters the enemy zone (0 = no territory scoring)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "setupValue92AC",
   "meaning": "per-side setup value written by command $A5 kind 1 into the other side's entry",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "terrainPointsBySide",
   "meaning": "per-side TERRAIN PTS setting (0-2) written by command $A5 kind 1 into the entry of the side opposite the sender; read at $47A9 when a unit penetrates the enemy half, doubled in the deepest zone",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "terrainPointsBySide",
   "meaning": "per side TERRAIN PTS option (0-2, defaults table row 0)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "terrainPointsBySide",
   "meaning": "2 bytes: 'TERRAIN PTS' (0..2) per side, edited by editTerrainPoints for the opponent index and sent as $A5 kind 1",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "territoryPoints",
   "meaning": "2 bytes indexed by absolute side: STATS row TER PTS shows [playerSide EOR 1] under US and [playerSide] under THEM (the entry written by each player's own $A5 kind-1 setup command).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "zoneScoreValue",
   "meaning": "Points awarded for entering the enemy zone (0 = territory scoring off). When 0 the AI marks all its units as 'zone done' so it does not chase territory.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "92B2": [
  {
   "name": "comcenRevealTimer",
   "meaning": "per-side rounds (6) during which a comcen that launched radar/drone stays visible",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  }
 ],
 "92B4": [
  {
   "name": "comcenSpeedClassOwn",
   "meaning": "speed class (index into speedClassRateTable) of the own comcen",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenMobility",
   "meaning": "2 bytes [0 own, 1 opponent] set by command $A5 kind >= 2; value 4 makes $34D5 refuse to move the comcen",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenSpeedClass",
   "meaning": "two bytes [0 = own, 1 = opponent]: COMCEN SPEED setting 0-4 from command $A5 kind 3; the value 4 makes $34E1 pin the COMCEN in place",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "comcenSpeedClass",
   "meaning": "2 bytes (own, opponent): setup value 0-3 translated through comcenSpeedClassMap at battle start",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "comcenSpeedClass",
   "meaning": "2 bytes (own, opponent): COMCEN SPEED class (defaults table row 2, later mapped through $78BF)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "comcenSpeedSetting",
   "meaning": "2 bytes per side: 'COMCEN SPEED' option 0..3 at setup time (sent as $A5 kind 3), later replaced by the speed class looked up at $7B6E",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "comcenSpeedClass",
   "meaning": "2 bytes [0 own, 1 opponent]: comcen mobility class 0-5, shown on the STATS row COMSPEED through comSpeedDisplayTable.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "92B5": [
  {
   "name": "comcenSpeedClassOpponent",
   "meaning": "speed class of the opponent's comcen",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  }
 ],
 "92C2": [
  {
   "name": "unitActivityFlag",
   "meaning": "set when a unit changed its path position or dug in this round; in the setup phase the round counter only advances when set",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "turnActivityFlags",
   "meaning": "bit 0 set whenever a command is executed (also set to 1 at $44D5); tested by the clock code at $45F4",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "exchangeActivityFlag",
   "meaning": "bit 0 set whenever a command is executed in an exchange; in the setup phase the game clock only advances on exchanges with activity",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "92E3": [
  {
   "name": "stepIntervalFrames",
   "meaning": "frames between engine steps (4 at game start; film speed keys set 9 - digit)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "stepIntervalFrames",
   "meaning": "frames per engine step, 4 at game start",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "stepIntervalFrames",
   "meaning": "Frames between engine steps. The film replay speed keys set it to 8 - (key - $B1), i.e. 9 - digit for digits 1-9 (fastest = 0 for '9'), then display 'SPEED n'.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "92EF": [
  {
   "name": "lastQuarterRound",
   "meaning": "roundsRemaining value at which 'LAST QTR WARNING!' is issued (total/4)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastQuarterRound",
   "meaning": "preset to 50, then roundsByGameType/4 when the battle starts",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "92FA": [
  {
   "name": "lastQuarterFlag",
   "meaning": "$80 after the last-quarter warning",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastQuarterFlag",
   "meaning": "$FF written by startFilmPlayback",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "clockDisplayEnable",
   "meaning": "Checked by updateStatusMessage (must be negative) before the status line falls back to the clock/score display",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "F688": [
  {
   "name": "unitStateFlagsAlias",
   "meaning": "unitStateFlags ($F708) addressed with a map byte (unit|$80) as index: $F688 + ($80+i) = $F708 + i",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  }
 ],
 "F6EC": [
  {
   "name": "unitTypeTableAlias",
   "meaning": "unitTypeTable ($F76C) indexed by map byte (unit|$80)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitTypeAlias",
   "meaning": "= unitTypeTable indexed with $80|unit",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "F818": [
  {
   "name": "unitPathColAlias",
   "meaning": "unitPathCol ($F898) indexed by map byte (unit|$80)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitWaypointColAlias",
   "meaning": "= unitWaypointCol indexed with $80|unit",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "FA70": [
  {
   "name": "unitSavedTerrainAlias",
   "meaning": "unitSavedTerrain ($FAF0) indexed by map byte (unit|$80)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_403B",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitUnderCellAlias",
   "meaning": "= unitTerrainUnder ($FAF0) indexed with the raw cell byte $80|unit",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "unitUnderCellAlias",
   "meaning": "unitUnderCell indexed by map byte ($80|unit)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "unitUnderCellAlias",
   "meaning": "terrain under a unit indexed by the raw map byte ($80|unit); sub_8378 uses it so a cell occupied by a unit counts as its underlying terrain when checking for a plain ($40) recycler site",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "69": [
  {
   "name": "mergedCommandLength",
   "meaning": "fill index of turnCommandBuffer while the own and received packets are merged; becomes turnCommandCount / player1CommandStart",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mergeFillIndex",
   "meaning": "number of bytes already placed in the merged command buffer $908F while the two packets are merged",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "6A": [
  {
   "name": "packetReadIndex",
   "meaning": "index of the next command inside the packet being merged (set by advancePacketCursor)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "packetScanIndex",
   "meaning": "read offset into the packet being merged ($E020 received / $E027 own)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "76": [
  {
   "name": "isRemoteCommand",
   "meaning": "bit 7 set while a command issued by the remote player is merged or executed (its coordinates are mirrored with 39 - v by $4FE4); 0 for own commands",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdFromOpponent",
   "meaning": "bit 7 set while the command being executed came from the opponent's packet (coordinates must be mirrored, 'own' side effects skipped); also $80 while the received packet is merged",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdFromOpponent",
   "meaning": "bit 7 set while a command received from the opponent is executed (its coordinates were mirrored with 39-v by $4FE4); rotateGroupFormation uses it to decide which half of the map the setup-phase clamp applies to",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdFromOpponent",
   "meaning": "bit 7 set while the command currently being executed came from the opponent's packet; drives coordinate mirroring, the side index and the 'own action' side effects (redraw requests, sounds)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "4C46": [
  {
   "name": "groupMessagePtrLo",
   "meaning": "self-modified lda operand: low byte of the string printed by orderGroupNewPosition ($99 -> $CB99 'RE-POSITIONED')",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  }
 ],
 "4C48": [
  {
   "name": "groupMessagePtrHi",
   "meaning": "self-modified ldy operand: high byte of that string pointer ($CB)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  }
 ],
 "4B0F": [
  {
   "name": "groupCheckSubject",
   "meaning": "self-modified ldy operand: unit index or $80 (order target) tested by isPointWithinGroupRange",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  }
 ],
 "4B80": [
  {
   "name": "groupKeyOperand",
   "meaning": "self-modified cmp operand: group id bits | $40 matched against FB54 & $7C in computeGroupBounds",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  }
 ],
 "4C79": [
  {
   "name": "groupBitsOperand",
   "meaning": "self-modified ora operand: F708 & $7C copied into FB54 by syncLocalGroupFlags",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  }
 ],
 "4DE9": [
  {
   "name": "filmTimeStampByte",
   "meaning": "self-modified lda operand: second byte of the film record being examined (low nibble sub-tick, high nibble clock delta)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  }
 ],
 "4DF4": [
  {
   "name": "filmLastClock",
   "meaning": "self-modified adc operand: clock value (D_91CB) of the last film record played; base for the 1-byte delta time stamps (also written by the overlay at $7914 when playback starts)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "filmLastClock",
   "meaning": "self-modified operand of the film time-stamp code, reset to 0 at game start",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "4E20": [
  {
   "name": "filmCommandStart",
   "meaning": "self-modified ldy operand: offset of the first command byte inside the current film record",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  }
 ],
 "4EEF": [
  {
   "name": "commandHandlerVector",
   "meaning": "2-byte operand of the JSR at $4EEE, loaded from commandHandlerTable for every executed command",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  }
 ],
 "67C8": [
  {
   "name": "currentSoundId",
   "meaning": "id of the sound currently playing; sound $15 is stopped when a packet exchange completes",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "voiceSoundIds",
   "meaning": "3 bytes: sound id currently playing on each SID voice ($67C8 voice 0); $15 is checked before showing PHONE TROUBLE",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "sndVoiceSoundId",
   "meaning": "3 bytes: id of the sound playing on voice 0/1/2, 0 when the voice's script has ended; polled by $1AEC, $4E56, $5A4B and the sound queue ($C8D0); $67CA (voice 2) is checked by sub_C8FA for $14/$16",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "sndVoiceSoundId",
   "meaning": "Sound id currently playing on SID voice 0/1/2 ($67C8/$67C9/$67CA), maintained by the sound player; requestSound consults it for priority and updateComcenMoveSound checks voice 2 for $14/$16.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "908F": [
  {
   "name": "turnCommandBuffer",
   "meaning": "40-byte buffer ($908F-$90B6) holding the merged commands of one exchange: player 0's commands first, then player 1's; also filled from film records during playback",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mergedCmdBuffer",
   "meaning": "merged command buffer of one exchange ($908F-$90B6, 40 bytes): side 0's packet followed by side 1's; also the film record payload",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mergedCmdBuffer",
   "meaning": "merged command buffer for one exchange (40 bytes): side 0's commands followed by side 1's; the source for both the dispatcher and the game film",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "90B7": [
  {
   "name": "turnCommandCursor",
   "meaning": "index of the command currently executed in turnCommandBuffer; the parameter fetchers at $4FC4/$4FD0/$4FF7 advance it",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mergedCmdReadIdx",
   "meaning": "read index into mergedCmdBuffer while the dispatcher executes commands; handlers advance it past their arguments",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mergedCmdReadIdx",
   "meaning": "read index into mergedCmdBuffer; every handler in this chunk advances it past its own arguments (via readCmdArgs3/readCmdArgs2/readCmdArg1 or an explicit INC for the 1-byte commands)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "929D": [
  {
   "name": "lastOrderedGroupBits",
   "meaning": "group bits of the group that last received a NEW POSITION ($98) order",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingGroupOrderId",
   "meaning": "group id (FB54 & $3C) of the group currently being given MOVE TO GOAL orders",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingRepositionGroup",
   "meaning": "group key of the formation repositions ($98 commands) still being processed by the engine",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastRepositionedGroup",
   "meaning": "group id (unitGroupFlags & $3C) of the unit(s) most recently given a re-position order ($98) at $4C25; $FF at game reset ($78E1)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastOrderedGroupBits",
   "meaning": "group key of the most recent NEW POSITION order ($FF at game start); waitForGroupRepositionsDone ($65E7) only waits when the group it is about to edit matches this key",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "lastOrderedGroupBits",
   "meaning": "reset to $FF at session start",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "929E": [
  {
   "name": "pendingGroupOrderCount",
   "meaning": "number of $98 orders to lastOrderedGroupBits that have been sent but not yet executed (incremented on send, decremented by setGroupMemberDestination)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingGroupOrderCount",
   "meaning": "number of outstanding $98 orders for pendingGroupOrderId; decremented when each is executed",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingRepositionCount",
   "meaning": "number of outstanding $98 repositions of pendingRepositionGroup; sub_65E7 waits for 0 before the group is moved or edited",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingRepositionCount",
   "meaning": "number of re-position orders sent for lastRepositionedGroup that the engine has not yet executed (incremented at $4C1F, decremented at $4C65 - a region mis-typed as data)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingGroupOrderCount",
   "meaning": "number of $98 orders for lastOrderedGroupBits that have been queued locally but not yet executed by the lock-step exchange; incremented by orderGroupNewPosition and decremented by setGroupMemberDestination",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4987",
   "unit": "game/mainProgram0800"
  }
 ],
 "929F": [
  {
   "name": "pendingOutgoingCmdCount",
   "meaning": "number of own commands queued but not yet executed; decremented when an own command (or own $8D) is processed",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingOwnCmdCount",
   "meaning": "number of own commands queued but not yet executed; incremented by the queue routines, decremented when an own command is executed",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingOwnCmdCount",
   "meaning": "Count of own commands queued but not yet executed; incremented by queueCmdBytePending/queueCmd3/queueCmd4 for each command this chunk queues.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingOwnCmdCount",
   "meaning": "number of own commands queued but not yet executed; decremented by the dispatcher and by the mergers",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "pendingOwnCmdCount",
   "meaning": "after the setup phase the game loop is run until it reaches 0 (all own setup commands sent)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "92EB": [
  {
   "name": "turnCommandCount",
   "meaning": "number of valid bytes in turnCommandBuffer for the current exchange (temporarily the raw film header byte during playback)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mergedCmdLength",
   "meaning": "total length of the merged command buffer of the current exchange (film record length nibble)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "92EC": [
  {
   "name": "player1CommandStart",
   "meaning": "index in turnCommandBuffer where player 1's commands begin; stored in the high nibble of the film record header",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mergedCmdSplitIdx",
   "meaning": "offset in mergedCmdBuffer where side 1's commands begin (film record high nibble)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "92ED": [
  {
   "name": "currentCommandOrigin",
   "meaning": "$80 while commands of the remote player are executed, 0 for own commands; copied to zp_76",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mergedCmdOwnerFlag",
   "meaning": "$80 while the commands being executed belong to side 1; toggled at the split index; copied to cmdFromOpponent after XOR with the local side",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "92EE": [
  {
   "name": "currentCommandLength",
   "meaning": "length of the last command looked up by getCommandLength; used as the byte copy counter",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "cmdLength",
   "meaning": "length (1-4 bytes incl. the command byte) of the current command code from cmdLengthTable ($4D78), via $4D9F",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "currentCommandLength",
   "meaning": "length of the command last looked up by getCommandLength; used as the byte counter in queueAiCommand and injectAiCommandsIntoPacket",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "92F6": [
  {
   "name": "reportedCursorX",
   "meaning": "x of the position carried by the $8D report command (set from the opponent's $8D by $52C8; own $8D re-centres the view on it)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "droneCol",
   "meaning": "current map column of the drone (command $8D)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "droneCol",
   "meaning": "last reported drone map column (also used by mergeOutgoingPacket to re-centre the view)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "droneCol",
   "meaning": "Drone position (column). On the drone screen it is kept as the radar origin (drone column - 6; sub_81FE adds 6 before it is sent in $8D/$8E and the own $8D copies it to zp_A5); cmdDroneMove on screen 1 stores the plain cell",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "droneCol",
   "meaning": "map column of the drone (top-left of its 2x2 cells), from command $8D",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "droneViewOriginCol",
   "meaning": "Map column of the left edge of the 14x12 drone camera window; the drone itself is at +6 (sent in $8D/$8E).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "droneViewOriginCol",
   "meaning": "Drone camera window origin column; the drone's own cell is this + 6",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  }
 ],
 "92F7": [
  {
   "name": "reportedCursorY",
   "meaning": "y of the position carried by the $8D report command",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "droneRow",
   "meaning": "current map row of the drone",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "droneRow",
   "meaning": "last reported drone map row",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "droneRow",
   "meaning": "Drone position (row), stored as radar origin (drone row - 5) on the drone screen",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "droneRow",
   "meaning": "map row of the drone (top-left of its 2x2 cells)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "droneViewOriginRow",
   "meaning": "Map row of the top edge of the drone camera window; the drone is at +5.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "droneViewOriginRow",
   "meaning": "Drone camera window origin row; the drone's own cell is this + 5",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  }
 ],
 "E01E": [
  {
   "name": "incomingPacketLength",
   "meaning": "number of command bytes received in incomingPacketBuffer",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commRxLength",
   "meaning": "length of the received packet in $E020..",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commRxLength",
   "meaning": "length of the received packet",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rxPacketLength",
   "meaning": "Number of bytes of the opponent's packet currently in $E020.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "rxMessageLength",
   "meaning": "Length of the message in the receive buffer $E020",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "receivedPacketLength",
   "meaning": "number of valid bytes in receivedPacketBuffer; the trainer fills it from its own order queue",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "rxPacketLength",
   "meaning": "length of the packet 'received' from the opponent; the trainer AI writes it itself",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "incomingPacketLength",
   "meaning": "Number of command bytes in the packet the engine believes it received from the opponent (read by mergeIncomingPacket at $4F05/$4F58); in this build the AI writes it directly",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "commIncomingPacketLen",
   "meaning": "Length of the packet the comm module presents as 'received from the opponent'. Cleared by initTrainerAi and set to 4 by the drone code, which writes fake opponent commands into $E020-$E023.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "rxPacketLength",
   "meaning": "number of valid bytes in the fake 'received' packet; the trainer AI writes it itself (1, 3 or 4 here). Also called receivedPacketLength in the comm-module survey.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "E01F": [
  {
   "name": "outgoingPacketLength",
   "meaning": "number of command bytes in outgoingPacketBuffer (set by $562B, max 4)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commTxLength",
   "meaning": "length of the outgoing packet in $E027..",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "txPacketLength",
   "meaning": "Number of bytes of the local packet in $E027 that the transmitter should send.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "txMessageLength",
   "meaning": "Length of the message to send from $E027",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "outgoingPacketLength",
   "meaning": "length of the local player's packet, set by sendOutgoingPacket; cleared by request X=3 and by closing the link",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "E020": [
  {
   "name": "incomingPacketBuffer",
   "meaning": "7-byte buffer of the command bytes received from the opponent in the last exchange",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commRxBuffer",
   "meaning": "received packet (up to 7 bytes), merged into mergedCmdBuffer; $8C and $8D are executed immediately",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commRxBuffer",
   "meaning": "received packet; cmdDroneMove reads its heading/col/row directly out of it",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rxPacketBuffer",
   "meaning": "7-byte buffer holding the command packet received from the opponent (read by mergeIncomingPacket at $4F05).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "receivedPacketBuffer",
   "meaning": "7-byte buffer $E020-$E026 holding the opponent's command bytes; in the trainer build it is written directly by the AI ($EAA6, $EDCC, $EE32, $EE99, ...)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "rxPacketBuffer",
   "meaning": "buffer holding the opponent's command bytes for the current exchange (max 7 bytes used here)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "incomingPacketBuffer",
   "meaning": "Command bytes of the opponent's packet ($4F14/$4F33/$4F47, cmdDroneMove $52C8); fabricated here by the AI",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "rxPacketBuffer",
   "meaning": "buffer of the opponent's command bytes for this exchange ($E020-$E026). This chunk fills it with $8F; $8E,col,row; or $8D,heading,col,row. Also called receivedPacketBuffer.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "E027": [
  {
   "name": "outgoingPacketBuffer",
   "meaning": "7-byte buffer of the command bytes sent in the last exchange (merged back as the own half of the turn)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4987",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commTxBuffer",
   "meaning": "outgoing packet (up to 7 bytes) filled by sendOutgoingPacket / buildReadyPacket",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "commTxBuffer",
   "meaning": "copy of the packet just sent; mergeOutgoingPacket passes its $8C reason byte to cmdSessionControl",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "txPacketBuffer",
   "meaning": "7-byte buffer holding the local command packet (filled by sendOutgoingPacket at $562B).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "outgoingPacketBuffer",
   "meaning": "7-byte buffer $E027-$E02D holding the local player's command bytes (filled by sendOutgoingPacket $568C)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "BC": [
  {
   "name": "filmPtr",
   "meaning": "pointer (lo) into the game-film buffer in the RAM under the I/O area ($D000-$DFEF); read/written through readFilmByte/writeFilmByte",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "ioRamPtr",
   "meaning": "pointer (lo) used by sub_5874/sub_5893 to read/write RAM under the I/O area; $D000 = film header",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "BD": [
  {
   "name": "filmPtrHi",
   "meaning": "pointer (hi) of filmPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "ioRamPtrHi",
   "meaning": "high byte of ioRamPtr",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "07BE": [
  {
   "name": "terrainColourIndex",
   "meaning": "33-entry table indexed by terrain index (cell-$20 or cell-$40) giving an index into D_07EA",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "07EA": [
  {
   "name": "terrainColourTable",
   "meaning": "colour-RAM values for terrain classes (also used by renderMapTile)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "0B9D": [
  {
   "name": "mapKindFlag",
   "meaning": "0 after command $8B, 1 after command $A6 (two kinds of map seed, probably standard vs custom/edited map)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapKindFlag",
   "meaning": "0 after command $8B, 1 after command $A6 (flipped/custom map variant)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "mapFlipFlag",
   "meaning": "0/1: the chosen map is shown rotated 180 degrees (F1 TO FLIP in the map-name screen); XORed with playerSide to decide whether the generated map is rotated, sent as command $A6 instead of $8B, recorded in the film header (+$2E bit 0)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "mapOrientationFlag",
   "meaning": "0/1: side for which the map at $F000 is currently oriented; rotated 180 degrees (sub_EC58) when it differs from playerSide; film header byte $2E bit 0",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "562A": [
  {
   "name": "outPacketFill",
   "meaning": "work byte: number of bytes already placed in the outgoing packet by sendOutgoingPacket",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "90B8": [
  {
   "name": "overviewCharBuffer",
   "meaning": "8-byte character bitmap being assembled for the overview map",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "glyphScratchBuffer",
   "meaning": "8-byte copy of the glyph being magnified by plotGlyphDoubleSize",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "920B": [
  {
   "name": "droneHeading",
   "meaning": "heading 0-7 of the remotely controlled drone, sent as first byte of command $8D",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "droneHeading",
   "meaning": "Heading 0-7 clockwise from north of the own drone (set from the joystick at launch via D_7FCE, turned one step per exchange by steerDrone); also selects the direction sprite shape",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "droneHeading",
   "meaning": "Drone heading 0-7 clockwise from north; index of the aircraft silhouette (sprite 5) and first byte of command $8D. Set from joyDirToHeadingTable at launch, turned one step per exchange by $731E.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "droneHeading",
   "meaning": "Current heading 0-7 of the drone under local control",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  }
 ],
 "922B": [
  {
   "name": "overviewUnitMask",
   "meaning": "pixel mask (AND) applied to the unit pattern on the overview for side 0 units; complemented for side 1 (set by overlay B $7909)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "overviewUnitPattern",
   "meaning": "$55 = multicolour pixel pattern used for own units on the overview map (EOR $FF for enemy units in sub_5BDA)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "9237": [
  {
   "name": "screen1DrawParam0",
   "meaning": "set to $0B by drawScreen1Cell before calling overlay B's cell drawer (probably a colour)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "radarOwnUnitColour",
   "meaning": "Colour used by getRadarCellGlyph for own units (D_9224 on the drone screen, $0B from drawScreen1Cell)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "screen1OwnUnitColour",
   "meaning": "colour RAM value (set to $0B) used by the screen-1 cell classifier sub_74C3 for units of the player's side",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9238": [
  {
   "name": "screen1DrawParam1",
   "meaning": "set to 1 by drawScreen1Cell",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "radarEnemyUnitColour",
   "meaning": "Colour for spotted enemy units (D_9225 on the drone screen, 1 from drawScreen1Cell)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "screen1SpottedEnemyColour",
   "meaning": "colour RAM value (set to 1 = white) used by sub_74C3 for spotted enemy units; paintRadarSweepRow keeps cells with this nibble lit while the radar is intact",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9239": [
  {
   "name": "screen1DrawParam2",
   "meaning": "set to $0B by drawScreen1Cell",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "radarTerrainColour",
   "meaning": "Colour for terrain and ghost blips (8 on the drone screen, $0B from drawScreen1Cell)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "screen1TerrainColour",
   "meaning": "colour RAM value (set to $0B) used by sub_74C3 for terrain cells and for unspotted (noise-drawn) enemy units",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "92AE": [
  {
   "name": "dronesLeft",
   "meaning": "2 bytes [0 own, 1 opponent]: drones remaining (set by $90 during setup, decremented by $97 drone launch)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "dronesLeft",
   "meaning": "two bytes [0 = own, 1 = opponent]: drones still available; set by command $90 during setup, decremented by command $97",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "droneCount",
   "meaning": "2 bytes (own, opponent): DRONES option (defaults table row 3; THE BOMB adds one)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "dronesLeft",
   "meaning": "2 bytes (own, opponent): number of drones, 'DRONES' custom option 0..9, sent as the x byte of command $90 during setup",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "dronesLeft",
   "meaning": "2 bytes: drones remaining, [0] own, [1] opponent (STATS row DRONES).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "dronesLeftOwn",
   "meaning": "Drones the local (human) side has left; the AI uses 4x and 2x this value as a missile-conservation term",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  }
 ],
 "92B0": [
  {
   "name": "missilesLeft",
   "meaning": "2 bytes [0 own, 1 opponent]: missiles remaining (set by $90 during setup, decremented by a $90 strike)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "missilesLeft",
   "meaning": "two bytes [0 = own, 1 = opponent]: missiles still available; set by command $90 during setup, decremented by a battle-phase $90",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "missileCount",
   "meaning": "2 bytes (own, opponent): MISSILES option (defaults table row 4)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "missilesLeft",
   "meaning": "2 bytes: number of missiles, 'MISSILES' custom option 0..36, sent as the y byte of command $90 during setup",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "missilesLeft",
   "meaning": "2 bytes: missiles remaining, [0] own, [1] opponent (STATS row MISSILES).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "missilesLeft",
   "meaning": "2 bytes, [0] own (the human) and [1] opponent: missiles remaining. Only index 0 is read here, as the gate for the evasive weave (inference: no missiles left means the player cannot shoot the drone down).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "92F1": [
  {
   "name": "filmEndPtr",
   "meaning": "end pointer of the film: $DFF0 recording limit, actual end after finalizeFilm / on playback",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "92F2": [
  {
   "name": "filmEndPtrHi",
   "meaning": "high byte of filmEndPtr",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "D000": [
  {
   "name": "filmBuffer",
   "meaning": "game film / saved game in the RAM under I/O: $D000/1 film length, $D002/3 stream start offset ($0368), $D004 id byte, $D00D-$D010 final scores, $D02F setupByte0B94, $D030/$D031 game clock at start/end, $D033 header checksum, $D034-$D047 copy of $92A2-$92B5, $D048-$D367 copy of $F640-$F95F, $D368-$DFEF command records",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "filmHeader",
   "meaning": "game film in RAM under I/O: header bytes 4 link type, 5 side, 6-$0B settings text, $0C game type options, $11-$24 names, $2B-$2D map seed, $2E orientation/plays-first, $2F version, $33 checksum of 0-$32, $34.. saved scores/unit state; 16 pages saved per slot",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "E033": [
  {
   "name": "commCia2IcrMask",
   "meaning": "CIA2 ICR enable mask used by the comm module, restored after every RAM-under-I/O access",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "ciaIcrMaskShadow",
   "meaning": "Shadow of the CIA2 interrupt enable mask currently in force: $92 = FLAG (RXD start bit) + timer B, $83 = timer A + timer B while a character is being received, $7F = everything off.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "cia2IcrMask",
   "meaning": "$7F - the CIA2 interrupt mask that readFilmByte/copyPageUnderIo restore after banking the I/O area out",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "F944": [
  {
   "name": "unitEnergyAlias",
   "meaning": "= unitEnergyTable indexed with $80|unit",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "5E14": [
  {
   "name": "uiLevelTextColourTable",
   "meaning": "3 colour bytes ($09,$05,$04) for the info panel text indexed by uiLevel D_9209: 0 browsing, 1 unit selected, 2 group",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  }
 ],
 "602B": [
  {
   "name": "menuIdleFlagSnapshot",
   "meaning": "self-modified operand of LDA # at L_602A: FB54 | 1 of the menu's unit when the menu was built (bit 7 = idle); 0 disables the watch. A change of bit 7 makes the menu loop return $80 so the DIG/BLITZ items are rebuilt",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  }
 ],
 "60BB": [
  {
   "name": "menuSlowRowIndex",
   "meaning": "self-modified operand of CMP # at L_60BA: item index (count - 2) before which a 30-frame delay is inserted when moving down; $FF = none",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  }
 ],
 "60BF": [
  {
   "name": "menuMoveDirection",
   "meaning": "self-modified operand of LDY # at L_60BE: last joystick move direction ($FF up, $01 down)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  }
 ],
 "612E": [
  {
   "name": "menuLoopMode",
   "meaning": "mode of the map highlight update inside the menu loop: $FF (unit menu) or $01 (group menu) = animate the selected unit's path marker; $00/$20 (join-group picker) = side bit ORed into the candidate group key (selection * 4) that is highlighted in the viewport",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  }
 ],
 "63E2": [
  {
   "name": "ownSideBranchOpcode63E2",
   "meaning": "branch opcode after CMP #$32 in the group editor, patched by overlay A ($7BCF) to BCC/BCS so that own units are index < 50 or >= 50 depending on the player side",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  }
 ],
 "91CE": [
  {
   "name": "menuItemIds",
   "meaning": "list of item ids of the current menu (up to 6 bytes, $91CE-$91D3), written by addMenuItem and read back by the menu loop to return the chosen item",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuItemIdList",
   "meaning": "item ids of the current menu in display order (filled by addMenuItem $5FB1, read by the selector to return the id of the chosen row)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuItemIdList",
   "meaning": "Item ids of the current menu in display order, filled by addMenuItem; here {13,14,15,16,12} = ENERGY, BATTLE, RADAR, DRONE, (EXIT).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "9207": [
  {
   "name": "unusedFlag9207",
   "meaning": "written with $FF by runJoinGroupMenu and runGroupEditor, never read anywhere (dead store)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  }
 ],
 "92C5": [
  {
   "name": "menuBlinkState",
   "meaning": "0 while the selected menu row is drawn highlighted (colours EORed), 1 while drawn normally; toggled every 20 frames",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_5E14",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "menuHighlightOn",
   "meaning": "1 while the menu highlight bar is drawn (toggled by sub_60EA)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "menuBlinkState",
   "meaning": "0 while the menu row is drawn highlighted, 1 normal; editValueWithJoystick sets 1 before redrawing the value",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "D8": [
  {
   "name": "sndVoice0ScriptPtr",
   "meaning": "sound script read pointer of SID voice 0 (lo); zp_D9 hi. Indexed by voice*2 from the sound driver",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "D9": [
  {
   "name": "sndVoice0ScriptPtrHi",
   "meaning": "sound script pointer of voice 0, high byte",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "DA": [
  {
   "name": "sndVoice1ScriptPtr",
   "meaning": "sound script pointer of SID voice 1 (lo/hi at $DA/$DB)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "DB": [
  {
   "name": "sndVoice1ScriptPtrHi",
   "meaning": "sound script pointer of voice 1, high byte",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "DC": [
  {
   "name": "sndVoice2ScriptPtr",
   "meaning": "sound script pointer of SID voice 2 (lo/hi at $DC/$DD)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "DD": [
  {
   "name": "sndVoice2ScriptPtrHi",
   "meaning": "sound script pointer of voice 2, high byte",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "6792": [
  {
   "name": "sndVoiceDuration",
   "meaning": "ticks left before the next script command, per voice (index voice*2; 0 = voice idle). First of eight 6-byte state rows at $6792-$67C1",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "6793": [
  {
   "name": "sndVoiceControl",
   "meaning": "SID control/waveform byte written to $D404+7*voice each tick (index voice*2)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "6798": [
  {
   "name": "sndLoopCounterA",
   "meaning": "script loop counter A (command 2, tested by loop command $46), index voice*2",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "6799": [
  {
   "name": "sndLoopCounterB",
   "meaning": "script loop counter B (command 3, tested by loop command $47), index voice*2",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "679E": [
  {
   "name": "sndSpareSlot4",
   "meaning": "script slot for command 4 (unused by the shipped scripts), index voice*2",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "679F": [
  {
   "name": "sndSpareSlot5",
   "meaning": "script slot for command 5 (unused by the shipped scripts), index voice*2",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "67A4": [
  {
   "name": "sndVoiceFreq",
   "meaning": "16-bit SID frequency per voice ($67A4 lo, $67A5 hi, index voice*2); advanced by sndVoiceFreqDelta every tick",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "67AA": [
  {
   "name": "sndVoicePulse",
   "meaning": "16-bit pulse width per voice ($67AA lo, $67AB hi, index voice*2)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "67B0": [
  {
   "name": "sndVoiceAdsr",
   "meaning": "attack/decay ($67B0) and sustain/release ($67B1) per voice, index voice*2",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "67B6": [
  {
   "name": "sndVoiceFreqDelta",
   "meaning": "signed 16-bit frequency increment added every tick (slide), index voice*2",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "67BC": [
  {
   "name": "sndVoicePulseDelta",
   "meaning": "signed 16-bit pulse-width increment added every tick, index voice*2",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "67C2": [
  {
   "name": "sndVolume",
   "meaning": "value written to SID_VOLUME every tick; set to $0C by startSound, 0 silences the driver",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "67C3": [
  {
   "name": "sndLastByte",
   "meaning": "last script byte fetched (BIT-tested for bits 7/6)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "67C4": [
  {
   "name": "sndScratch",
   "meaning": "scratch: loop slot offset in runSoundScript, sound*2 in startSound",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "67C5": [
  {
   "name": "sndInitLock",
   "meaning": "voice loop counter of startSound (3..0); nonzero makes soundPlayerTick return early",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "67C6": [
  {
   "name": "sndCurrentSound",
   "meaning": "id of the sound most recently started",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "67C7": [
  {
   "name": "sndStartVoiceIndex",
   "meaning": "voice number 0-2 being initialised by startSound",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  }
 ],
 "9246": [
  {
   "name": "rleMarkerByte",
   "meaning": "escape byte of the RLE stream being unpacked (first byte of the block)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rleMarkerByte",
   "meaning": "Escape byte of the RLE stream being unpacked ($13 for the comcen picture).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9247": [
  {
   "name": "rleFlag9247",
   "meaning": "cleared by unpackRleTo0200 and set by the overlay-B unpacker at $8634; purpose unknown",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_main_0800_652E",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "rleFlag9247",
   "meaning": "Cleared by both RLE unpackers; no reader found.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "87FF": [
  {
   "name": "overlayVariantFlag",
   "meaning": "Bit 7 selects which $6F00-$87FF overlay variant should be resident (set = A, clear = B); read by reloadOverlayForVariant and loadOverlayBIfNeeded.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_0DB7",
   "unit": "game/mainProgram0800"
  },
  {
   "name": "overlayVariantFlag",
   "meaning": "last byte of the $6F00 overlay: $00 in variant A, $FF in variant B; bit 7 is tested by the main program ($1036, $1080) to decide whether variant B must be (re)loaded",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "overlayVariantFlag",
   "meaning": "Overlay identity byte, the last byte of the $6F00-$87FF overlay: $FF in variant B, $00 in variant A (verified against extracted/runtimeImage_A.bin / _B.bin). Read at $1036 (bmi -> loadOverlayA, i.e. ensureOverlayA) and $1080 (bpl -> loadOverlayB, i.e. ensureOverlayB) so the main program can tell which variant is resident and skip a redundant disk load.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "17D8": [
  {
   "name": "comcenEnergySnapshot",
   "meaning": "Local copy of the comcen energy (unitEnergyTable[comcenUnit] & $3F, 0..50) taken when the REPAIR tab is drawn and refreshed after every repair attempt; the energy repair size is computed from it because the queued $9A command has not been executed yet.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "17D9": [
  {
   "name": "repairAmount",
   "meaning": "Energy points added by the last successful energy repair: (50 - comcenEnergySnapshot)/8 + 1; sent as the second parameter of command $9A.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "1889": [
  {
   "name": "blinkPhase",
   "meaning": "0/1 blink phase shared by the menu tab blink ($16B7) and the comcen system marker blink; 0 = element visible, 1 = element blanked.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "188A": [
  {
   "name": "showOkMarkers",
   "meaning": "Nonzero while the REPAIR WHICH menu is open: makes drawSystemMarker paint a green marker for undamaged systems too instead of erasing them.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "18A4": [
  {
   "name": "drawSystemMarkerIndex",
   "meaning": "Immediate operand of the LDY at $18A3, self-modified by drawSystemMarker so the system index survives the call to printEnergyValue.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_17DA",
   "unit": "game/mainProgram0800"
  }
 ],
 "908C": [
  {
   "name": "outgoingCmdRingLast",
   "meaning": "last byte of the outgoing command ring; used only at $534F as an index base so that D_908C,readIdx+3 addresses mergedCmdBuffer[readIdx]",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_main_new_4FC4",
   "unit": "game/mainProgram0800"
  }
 ],
 "2A": [
  {
   "name": "mapGenItemIndex",
   "meaning": "current hill site index (pickHillSummitSites/paintHillSummits) or remaining forest cluster count (placeForestClusters)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "zpGroupDeltaCol",
   "meaning": "column offset added to every group member's ordered destination",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "groupShiftCol",
   "meaning": "Column delta from the group's current centre to its new destination, computed by sub_E5FF and added to every member's destination.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "2B": [
  {
   "name": "laneIndex",
   "meaning": "index into the shuffled lane column table $0329 (3..0); in placeForestClusters the remaining seeds per cluster",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "zpGroupDeltaRow",
   "meaning": "row offset added to every group member's ordered destination",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "comcenStunnedPenalty",
   "meaning": "$00 normally, $FF when the AI comcen is stunned ($F9C3 & $0F != 0); its bit 7 adds one to the anti-drone reluctance score",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "groupShiftRow",
   "meaning": "Row delta from the group's current centre to its new destination.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "2D": [
  {
   "name": "contourScanRow",
   "meaning": "row of the cell being examined by applyContourTiles",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "2E": [
  {
   "name": "contourScanCol",
   "meaning": "column of the cell being examined by applyContourTiles",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "A7": [
  {
   "name": "blobOriginCol",
   "meaning": "column where the current blob starts; also the scan column in growLowerHillsBesideSlopes",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "A8": [
  {
   "name": "blobOriginRow",
   "meaning": "row where the current blob starts; also the scan row in growLowerHillsBesideSlopes",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0301": [
  {
   "name": "blobTile",
   "meaning": "base tile code painted by growTerrainBlob ($65 summit, $61 lower hill, $5D forest); randomised by sub_7872 using tileVariantMaskTable",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0304": [
  {
   "name": "blobFlags",
   "meaning": "OR-ed into each blob cell's direction flags; bit 6 ($40) = only paint where the aligned 2x2 block is blank/forest",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0307": [
  {
   "name": "hillSizeTable",
   "meaning": "8 bytes: blob size (2..5) of each hill summit; entries count+i mirror entries i",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "030F": [
  {
   "name": "riverCol",
   "meaning": "river generator (sub_7548): current column",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "genCol",
   "meaning": "map generator: current column (river cursor x, 0-39)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0310": [
  {
   "name": "riverRow",
   "meaning": "river generator: current row",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "genRow",
   "meaning": "map generator: current row (river cursor y)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0311": [
  {
   "name": "riverDirection",
   "meaning": "river generator: direction code of the current segment (tile = $55 + code)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "riverCurSegment",
   "meaning": "river segment type of the cell just written (0-7); selects the move direction in advanceRiverPosition",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0312": [
  {
   "name": "contourInteriorTile",
   "meaning": "interior tile of the current applyContourTiles pass (also reused by the river generator as the next direction code)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "genTileType",
   "meaning": "map generator scratch: next river segment type (0-7, tile = $55 + type) in generateRiver; the terrain code being placed in the fill pass sub_73EE",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0314": [
  {
   "name": "hillSiteColTable",
   "meaning": "8 bytes: column of each hill summit site",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "031C": [
  {
   "name": "hillSiteRowTable",
   "meaning": "8 bytes: row of each hill summit site (initially a copy of the candidate rows)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0324": [
  {
   "name": "contourTileOffset",
   "meaning": "subtracted from contourRuleTable values: 0 for the summit pass (contour $41-$54), $20 for the lower-hill pass (contour $21-$34)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0325": [
  {
   "name": "siteCandidateRowTable",
   "meaning": "4 candidate rows (7..14 for the top half, 39-row for the mirrored bottom half)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0329": [
  {
   "name": "laneColumnOrder",
   "meaning": "4 lane columns (6,15,25,34) in random order",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "032D": [
  {
   "name": "symmetricMapFlags",
   "meaning": "bit 7 = map seed low 5 bits all set (map code ends in '='): generate a point-symmetric map; bit 6 = river crossed the centre (set by sub_759E), requires the centre patch",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "mapSymmetryFlags",
   "meaning": "bit 7 = symmetric map (seed low 5 bits = $1F, name ends in '='): river on row 20 and the bottom half is the 180-degree mirror of the top half; bit 6 = the river jogs from row 20 to row 19 at the centre (fixed up at $6F7F)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "032E": [
  {
   "name": "mapStyleFlags",
   "meaning": "from the game type: $80 for QB SNEAK (no river, at most one hill, many forests), $40 for DEFENDER (always a river, four hills, few forests), 0 otherwise",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "mapGenGameFlags",
   "meaning": "map generator game-type flags set by generateBattlefieldMap: $80 = QB Sneak (no river, fewer features), $40 = Defender (river always present, fixed feature count), 0 otherwise",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0450": [
  {
   "name": "blobStackRow",
   "meaning": "80-entry row stack of growTerrainBlob",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "blobStackRow",
   "meaning": "80-entry row stack of the terrain-blob grower in overlay A ($71D1/$72C0/$71F0)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "04A0": [
  {
   "name": "blobStackFlags",
   "meaning": "80-entry direction-flag stack of growTerrainBlob",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "aiGroupCentreRowHigh",
   "meaning": "12 high bytes of the current-row sums. The file's symbol writeSectorToDisk for $04A0 is an address collision with the $0400 block's loader code and is wrong here.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "statAccumHi3",
   "meaning": "12-entry high-byte half of the fourth statistics accumulator (low bytes $88F6), $ED9E/$EE33; also the blob direction-flag stack of the map generator. NOTE: XREF.txt names this label writeSectorToDisk, which is a stale name borrowed from the 1541 drive code",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "0770": [
  {
   "name": "tileVariantMaskTable",
   "meaning": "indexed by tile - $40 (tiles below $40 use tile - $20): mask AND-ed with a random byte and added to the base tile by sub_7872 (e.g. $65 -> 1, $61/$5D/$55/$59 -> 3)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "F30B": [
  {
   "name": "mapCell19x19",
   "meaning": "map cell column 19 row 19; set to river tile $55 when a symmetric map's river crosses the centre",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "F333": [
  {
   "name": "mapCell19x20",
   "meaning": "map cell column 19 row 20; set to river tile $58 for the centre patch",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "F334": [
  {
   "name": "mapCell20x20",
   "meaning": "map cell column 20 row 20; blanked to $40 for the centre patch",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_6F00",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0313": [
  {
   "name": "mapNameLength",
   "meaning": "number of characters typed so far into the map-name field (0-5)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0B98": [
  {
   "name": "prevMapSeed",
   "meaning": "3-byte copy of the map seed of the previous game; if unchanged the saved map name backup $0BAF is restored, otherwise firstPlayFlag is set to 1",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0B9E": [
  {
   "name": "prevMapFlipFlag",
   "meaning": "mapFlipFlag of the previous game; a change sets firstPlayFlag",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0BAE": [
  {
   "name": "mapNameTerminator",
   "meaning": "byte after the name: $8D (CR, end) while printing, '!'|$80 when the name is hidden, otherwise the previous value with bit 7 set",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "57C3": [
  {
   "name": "filmLastRecordedClock",
   "meaning": "self-modified operand (L_57C2+1) holding the clock value of the last film record, reset to 0 at game start",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "9224": [
  {
   "name": "overlayBParam9224",
   "meaning": "set to $0B at game start; read only by overlay B ($7421, copied to $9237)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "ownUnitRadarColour",
   "meaning": "Colour nibble for own units on the drone radar ($0B, written by overlay A at $7904)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9225": [
  {
   "name": "overlayBParam9225",
   "meaning": "set to 9 at game start; read only by overlay B ($7427, copied to $9238)",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_6F00_A_74EE",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "enemyUnitRadarColour",
   "meaning": "Colour nibble for spotted enemy units on the drone radar ($09, written by overlay A at $78FB)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "0500": [
  {
   "name": "messagePtrTableLo",
   "meaning": "low bytes of the status message string pointers (message $1E is patched dynamically here)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "messagePtrLo",
   "meaning": "low bytes of the status message pointer table indexed by message id (high bytes at $0524); slot $1E is rewritten at run time by sub_C924 from this overlay",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "messagePtrLoTable",
   "meaning": "Low bytes of the 36-entry message string address table, indexed by message id $00-$23. Ids 2,5,6,7,8,$0C,$0D,$0E,$0F,$11,$12,$13,$14,$15,$16 point into this chunk. Entries $1E,$20,$21 are $FF/$FF placeholders filled at run time by sub_C924 (X = id, A = lo, Y = hi).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "messagePtrLoTable",
   "meaning": "Low bytes of the 36 status message string pointers (ids 0-$23); copy of overlay $7000, some slots rewritten at run time by $C924",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "messageTextLo",
   "meaning": "Low bytes of the 36 status-message text pointers (ids $00-$23), indexed by message id; written by setMessageSlot for the run-time slots.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "0524": [
  {
   "name": "messagePtrTableHi",
   "meaning": "high bytes of the status message string pointers",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "messagePtrHiTable",
   "meaning": "High bytes of messagePtrLoTable, same 36 entries. The message display code fetches $0500,x / $0524,x and prints the string through printString ($C133).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "messagePtrHiTable",
   "meaning": "High bytes of the 36 status message string pointers",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "messageTextHi",
   "meaning": "High bytes of the 36 status-message text pointers; $0524+$24 = $0548, which is the text of slot 0 itself.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "58C3": [
  {
   "name": "copySrcPtr",
   "meaning": "self-modified source operand of copyPageUnderIo sub_58B2 (lo/hi at $58C3/$58C4)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "58C6": [
  {
   "name": "copyDstPtr",
   "meaning": "self-modified destination operand of copyPageUnderIo (lo/hi at $58C6/$58C7)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "7FA1": [
  {
   "name": "filmDirectoryPage",
   "meaning": "current film directory page 0-2 (10 slots each)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "7FA2": [
  {
   "name": "filmMenuMode",
   "meaning": "0 = SAVE GAME FILM, 1 = LOAD GAME FILM (main-menu item - 3)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "8450": [
  {
   "name": "recyclerMenuEnabled",
   "meaning": "nonzero makes chooseRecycler show the FULL/HALF/NONE menu (set by the options editor at $8564/$859C)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "customSetupChosen",
   "meaning": "nonzero while custom options are being set at game start: first D_0BA3 & $40, then the STANDARD(0)/CUSTOM(1) menu choice; sub_8378 only offers the recycler mode menu when it is set",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "892C": [
  {
   "name": "sideDefaultsTableSide0",
   "meaning": "5 rows x 5 columns (column = game-type class): terrain points, recycler mode, comcen speed class, drones, missiles for side 0; in the $8800 strings overlay",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "8945": [
  {
   "name": "sideDefaultsTableSide1",
   "meaning": "same layout as $892C for side 1",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "9220": [
  {
   "name": "loopCounter9220",
   "meaning": "generic down counter (10 directory entries in drawFilmDirectoryPage)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "droneFuel",
   "meaning": "Remaining flight time of the own drone: 100 at launch, decremented once per command exchange by sub_8279, 0 = drone expires ($8F sent); displayed by drawDroneFuelDigits; preset to 100 or 0 at screen entry depending on drones left",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "droneFuel",
   "meaning": "Flight time left of the current drone: 100 at launch, -1 per lock-step exchange (advanceDroneCamera); shown by sub_6F99 as a three-digit read-out whose colour changes below 21 and below 11 and alternates on even values (blink); 0 forces releaseDroneControl. Before a launch it shows 100 if drones remain, else 0.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "92C6": [
  {
   "name": "setupPhaseMode",
   "meaning": "from setupPhaseModeTable: 0 = no unit setup phase (SCRIMAGE, QB SNEAK), 1 = both sides set up (THE BOMB..FULL WAR), $80 = only side 0 sets up (DEFENDER); tested by $C470",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  },
  {
   "name": "setupUiStopFlag",
   "meaning": "Written at $7C4A; together with gamePhase it decides in checkSetupUiContinue whether the setup-phase UI may continue",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "EFB6": [
  {
   "name": "insertDiskNameField",
   "meaning": "4 characters inside 'INSERT DATA DISK AND PRESS SPACE.' ($EFAF, comm-module tail from track 34) overwritten with DATA/GAME",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "F79D": [
  {
   "name": "unitTypeComcen0",
   "meaning": "unitTypeTable entry of unit 49 (side 0 COMCEN), restored to type 4 in THE BOMB",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_7C29",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "844F": [
  {
   "name": "editValue",
   "meaning": "value being edited by editValueWithJoystick (initialised by beginValueEdit from X, read back by the option editors)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0332": [
  {
   "name": "customSummaryText",
   "meaning": "text buffer (in the $0200 setup block area) that receives 'CUSTOM:[MAP,] MENUS xxx, DAMAGE xxx.' built by showCustomSummaryMessage; registered as message slot $1E",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0636": [
  {
   "name": "optionSummaryTemplate",
   "meaning": "23-byte template ' MENUS XXXX DAMAGE XXX.' (bit-7 terminated) whose XXXX/XXX fields are filled with OFF,/ON, by appendOnOffWord",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "064D": [
  {
   "name": "wordOff",
   "meaning": "'OFF' (bit-7 terminated), followed by 'ON' at $0650 and the option words FULL/HALF/NONE at $0652",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "0650": [
  {
   "name": "wordOn",
   "meaning": "'ON' (bit-7 terminated); 3 bytes from here (O, N|$80, F) are copied over the OFF in the CUSTOM/DESTROY message",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "EFDF": [
  {
   "name": "customOnOffWord",
   "meaning": "the 'OFF' inside 'CUSTOM OFF' ($EFD8, T34 $EC00 sub-overlay), overwritten with ON/OFF by showToggleStateMessage while the setup screen uses it",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "EFEA": [
  {
   "name": "destroyOnOffWord",
   "meaning": "the 'OFF' inside 'DESTROY OFF' ($EFE2), overwritten with ON/OFF by showToggleStateMessage during unit placement",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_A_844F",
   "unit": "game/mapGenerator6F00"
  }
 ],
 "5E": [
  {
   "name": "joystickSteerDirection",
   "meaning": "Direction code 0-7 (N,S,E,W,NW,NE,SE,SW) of the last joystick steer input on the drone screen (set by sub_82A4, cleared at launch); steerDrone maps it to a heading through D_7FCE",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "droneJoyHeading",
   "meaning": "Desired drone direction in joystickDirectionTable order (0 N, 1 S, 2 E, 3 W, 4 NW, 5 NE, 6 SE, 7 SW); converted with joyDirToHeadingTable to the clockwise heading $920B by launchDrone and the steering routine $731E.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "67": [
  {
   "name": "gridCursorCol",
   "meaning": "Missile-screen cursor column in the 21x17 grid (0-20, comcen at 10); sprite 2 follows it",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "radarCursorCol",
   "meaning": "grid column (0-20) of the missile target cursor on the radar screen; starts on the comcen cell (10)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "68": [
  {
   "name": "gridCursorRow",
   "meaning": "Missile-screen cursor row in the 21x17 grid (0-16, comcen at 8)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "radarCursorRow",
   "meaning": "grid row (0-16) of the missile target cursor; starts on the comcen cell (8)",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "7134": [
  {
   "name": "radarWipeColour",
   "meaning": "Colour used to blank the radar area by clearRadarWindowGrey/Black ($BB or 0); nonzero also makes drawRadarIrisWipe pause 3 game frames per frame",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "7271": [
  {
   "name": "savedDroneHeading",
   "meaning": "Copy of droneHeading taken by playDroneLostAnimation; never read (dead)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "7395": [
  {
   "name": "radarGlyphSavedY",
   "meaning": "Y register (column offset) saved and restored by drawRadarGlyph",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "7551": [
  {
   "name": "zoomStepCounter",
   "meaning": "Loop counter 3..0 of playStrikeZoomAnimation (also sets the delay (n+3)*2 frames)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "7585": [
  {
   "name": "missileScreenLocked",
   "meaning": "Bit 7 set = missile screen inactive (Scrimmage game type has no comcen, or the own comcen is cloaked per D_9245); only frame updates and screen switching run",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "7586": [
  {
   "name": "cursorMovedFlag",
   "meaning": "0 until the joystick has moved the missile cursor, then $7F; fire is ignored while 0",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "920F": [
  {
   "name": "radarGhostCountdown",
   "meaning": "Counts unspotted enemy units drawn on the drone radar; when it underflows the next one is drawn as a noise ghost and it is reseeded with RNG & 7",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9210": [
  {
   "name": "radarNoiseMasks",
   "meaning": "16 bytes of $FF with 4 random bits cleared each (buildRadarNoiseMasks); ANDed over glyph rows for the ghost/static effect",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "radarNoiseMaskTable",
   "meaning": "16 random AND masks ($FF with four random bits cleared, built by sub_727A) used to draw unspotted enemy units as noisy radar blips",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "922D": [
  {
   "name": "radarHalfToneFlag",
   "meaning": "$FF when the unit being drawn is cloaked (unitCloakFlags bit 7): drawRadarGlyph blanks even rows and copies only odd rows; cleared after each glyph",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9232": [
  {
   "name": "missilesLeftDisplay",
   "meaning": "Screen-1 working copy of the own missile count (from D_92B0), decremented on each launch and shown by sub_7ABA",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "radarMissilesLeft",
   "meaning": "screen-1 working copy of the player's missiles left (loaded from missilesLeft[0] $92B0 at $758C, decremented when FIRE is used, shown by drawMissileCount, blinks below 2)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9233": [
  {
   "name": "dronesLeftDisplay",
   "meaning": "Screen-2 working copy of the own drone count (from D_92AE), decremented on launch and shown by drawDroneCountDigit",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "dronesLeftDisplay",
   "meaning": "Drone screen's copy of dronesLeft[own] ($92AE): reloaded on every (re)entry, decremented locally at launch, printed by sub_7036; 0 blocks launching, < 2 makes the drone box blink.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "C44E": [
  {
   "name": "decimalHundreds",
   "meaning": "Hundreds digit produced by sub_C428 (binary to 3 decimal digits)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "decimalHundreds",
   "meaning": "Hundreds digit produced by splitDecimalDigits",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "C44F": [
  {
   "name": "decimalTens",
   "meaning": "Tens digit produced by sub_C428",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "decimalTensDigit",
   "meaning": "tens digit produced by sub_C428 (binary to two decimal digits)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "decimalTens",
   "meaning": "Tens digit produced by splitDecimalDigits",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "C450": [
  {
   "name": "decimalOnes",
   "meaning": "Ones digit produced by sub_C428",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "decimalOnesDigit",
   "meaning": "ones digit produced by sub_C428",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "decimalOnes",
   "meaning": "Ones digit produced by splitDecimalDigits",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "0799": [
  {
   "name": "unitGlyphBaseTable",
   "meaning": "First 5 entries: base glyph index per unit type for the 1-character map glyph set at $9C6E ($2C grunt, $30 rider, $34 boomer, $38 spy, $3C comcen), plus the facing 0-3 from unitFlags bits 0-1",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "7FCE": [
  {
   "name": "joyDirToHeadingTable",
   "meaning": "8 bytes: joystick direction code (N,S,E,W,NW,NE,SE,SW) -> clockwise heading 0-7 (0,4,2,6,7,1,3,5)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_6F00",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "7891": [
  {
   "name": "radarSweepStep",
   "meaning": "current step (0-30) of the radar sweep on screen 1; for steps 0-16 it is the grid row being lit, rows step-14 are darkened; 31 restarts the sweep",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "7892": [
  {
   "name": "radarSweepRow",
   "meaning": "grid row currently handed to paintRadarSweepRow; equal to radarSweepStep for the lit row, step-14 for the row being darkened",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "7893": [
  {
   "name": "radarSweepDarkFlag",
   "meaning": "$FF = the current sweep shows nothing (set randomly at sweep restart while the radar system $9243 is damaged), 0 = normal",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "7898": [
  {
   "name": "radarSweepNextTick",
   "meaning": "self-modified immediate operand of the CMP at $7897: value of frameTick92FB at which the next sweep step is due (advanced by 2 per step, reset to 0 with the tick)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "7AB9": [
  {
   "name": "missileCountColour",
   "meaning": "colour byte of the 2x2 missile count box: $30 normal, $03 inverted; toggled by blinkMissileCount when fewer than 2 missiles are left",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "7B43": [
  {
   "name": "droneAlertColour",
   "meaning": "colour byte of the DRONE ALERT panel interior while flashing: toggles between $02 and $20 (EOR $22); reset to 2 when the panel is inactive",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9011": [
  {
   "name": "sprite1XShadow",
   "meaning": "sprite 1 X position (ring burst sprite); set to the drone cell centre by showDroneKillBurst",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "9019": [
  {
   "name": "sprite1YShadow",
   "meaning": "sprite 1 Y position",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7891",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "FF93": [
  {
   "name": "droneReticleSprite",
   "meaning": "64-byte sprite shape of the drone camera reticle (four corner brackets, centre ticks), copied into sprite 2 by launchDrone.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "681B": [
  {
   "name": "rleSrcPtr",
   "meaning": "Self-modified operand (lo/hi at $681B/$681C) of fetchRleByte; drawComcenSchematic sets it to $7C6F.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "82B5": [
  {
   "name": "shotViewLeft",
   "meaning": "drawShotLineInViewport: viewport left column (rebased, later x8 = fat pixels).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "82B6": [
  {
   "name": "shotViewRight",
   "meaning": "viewport left column + 7 (exclusive right bound, cells).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "82B7": [
  {
   "name": "shotViewTop",
   "meaning": "viewport top row (rebased, later x8).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "82B8": [
  {
   "name": "shotViewBottom",
   "meaning": "viewport top row + 5 (exclusive bottom bound, cells).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "82B9": [
  {
   "name": "shotEndpointVisibility",
   "meaning": "-1 attacker inside the viewport, +2 target inside (1 = both, 0 = neither -> nothing drawn).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "82BA": [
  {
   "name": "shotColShift",
   "meaning": "Offset added to all columns when the view origin column is negative.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "82BB": [
  {
   "name": "shotRowShift",
   "meaning": "Offset added to all rows when the view origin row is negative.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "82BC": [
  {
   "name": "shotDashCountdown",
   "meaning": "Starts at 4: steps with a positive value are not plotted (gap at the shooter), 0 plots the diamond marker, negative plots normally.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_6F00_B_7C6E",
   "unit": "game/comcenScreens6F00"
  }
 ],
 "0612": [
  {
   "name": "messageSoundTable",
   "meaning": "36-entry sound-effect number per message id, $80 = silent. For this chunk's ids: 2 -> 3, 5 -> 4, 6 -> 3, 7 -> 3, 8 -> 4, $0C-$0F -> 4, $11 -> $0E, $12 -> $0D, $13 -> $80 (silent), $14 -> $0E, $15 -> $0D, $16 -> $19.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "messageSoundTable",
   "meaning": "Sound number per message id; a negative entry ($80) means the message is silent",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "messageSoundIdTable",
   "meaning": "36-byte table: sound id to play when message id X is shown; $80 = silent. Read by sub_C557 on behalf of showStatusMessage and the message service.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "9249": [
  {
   "name": "messageQueue",
   "meaning": "8-entry priority-sorted ring of pending message ids managed by sub_C566, with head at $9251 and tail at $9252. Higher id sorts ahead (cmp $9249,y / bcs).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_6F00_B_868C",
   "unit": "game/comcenScreens6F00"
  },
  {
   "name": "messageQueue",
   "meaning": "8-entry ring buffer of pending message ids ($9249-$9250)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "messageQueue",
   "meaning": "8-entry ring of pending status-message ids ($FF = empty), filled priority-sorted by sub_C566 and drained by showNextQueuedMessage; also walked backwards by the message-history scrollback.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "44": [
  {
   "name": "doubleSizeText",
   "meaning": "Bit 7 set = characters are drawn 2x2 cells (plotGlyphDoubleSize) and the cursor advances two columns / two rows",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "4E": [
  {
   "name": "fillCounter",
   "meaning": "16-bit counter incremented by advanceFillPointer (with zp_4F); no code in the disassembly reads it",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "fillCounter",
   "meaning": "16-bit counter (with $4F) that advanceFillPointer increments as a side effect; not initialised or read by this chunk",
   "scope": "zp",
   "confidence": "medium",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ],
 "7A": [
  {
   "name": "pendingMessageId",
   "meaning": "Message id waiting to be shown; bit 7 set = already shown / nothing pending",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "currentMessageId",
   "meaning": "Status-line message currently selected; $80 = idle (nothing queued), bit 7 set on other values marks 'already displayed'. Set by showNextQueuedMessage and the message service at $C4EF.",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "92DA": [
  {
   "name": "decimalDigitBuffer",
   "meaning": "9-byte buffer holding the decimal digits produced by convertToDecimalDigits (digits at +1..+8, stored as $B0 + digit)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "987E": [
  {
   "name": "asciiToGlyphTable",
   "meaning": "64-byte translation table ASCII $20-$5F -> glyph index 0-$3B used by printChar",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "98BE": [
  {
   "name": "fontMulticolour",
   "meaning": "60 glyphs of 8 bytes ($98BE-$9A95) drawn in multicolour mode; all pixels use bit pair 11 so the colour comes from colour RAM",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "fontMulticolour",
   "meaning": "59 x 8 bytes ($1D8) of multicolour / double-width glyphs for the $C000 overlay text plotter (base used when $9001 bit 4 = multicolour); inverted in place by invertTextFonts",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ],
 "9A96": [
  {
   "name": "fontHires",
   "meaning": "The same 60 glyphs in single-bit-per-pixel form, used when $D016 bit 4 is clear",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  },
  {
   "name": "fontHires",
   "meaning": "59 x 8 bytes ($1D8) of hires glyphs, the same character set as fontMulticolour at single width; base used when $9001 bit 4 is clear (plotter adds $01D8 at $C215/$C21B); inverted in place by invertTextFonts",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ],
 "C22B": [
  {
   "name": "glyphDrawOpcode",
   "meaning": "Opcode byte of the glyph fetch inside plotGlyph, patched by setBitmapDrawMode ($2A41) to LDA/ORA/EOR/CMP (zp),Y",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C000",
   "unit": "game/textEngineC000"
  }
 ],
 "065E": [
  {
   "name": "strStatusScoreLine",
   "meaning": "Three consecutive bit-7-terminated strings used by drawStatusScoreLine: '   CLICKS:' ($065E), '    US:' ($0668), '   THEM:' ($066F).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "900D": [
  {
   "name": "spriteColourShadow",
   "meaning": "Shadow of VIC $D027-$D02E; $900D/$900E/$900F are sprites 5/6/7 and are set to 1 (white) for the unit picture.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "9015": [
  {
   "name": "spriteXShadow",
   "meaning": "Shadow of the sprite X registers ($9010+n); $9015/$9016/$9017 receive $EC/$0C/$2C so sprites 5-7 sit at X = 236, 268 and 300 in the info panel.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "901D": [
  {
   "name": "spriteYShadow",
   "meaning": "Shadow of the sprite Y registers ($9018+n); $901D-$901F receive $A2 = 162 for the unit picture row.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "92E7": [
  {
   "name": "soundRequestSlots",
   "meaning": "Four sound request slots $92E7-$92EA ($FF = empty). Slots 0-2 correspond to SID voices 0-2, slot 3 to whole-chip (three-voice) sounds. Written by requestSound, drained by dequeueSoundRequest from the raster IRQ.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "985E": [
  {
   "name": "asciiGlyphTableBase",
   "meaning": "$987E minus $20, so that D_985E,x indexes the ASCII($20-$5F)-to-glyph table directly by character code. Entry $3B means 'no glyph in the game font' and is used by handleTextEntryKey to reject backslash, caret and underscore.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "C630": [
  {
   "name": "messageReviewIndex",
   "meaning": "State byte of the message-history scrollback embedded in the code: bit 7 = review mode active, bits 0-6 = the queue read index to restore when it ends.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "C54B": [
  {
   "name": "suppressMessageSoundFlag",
   "meaning": "Self-modified operand of the LDA #$04 at $C54A in the message service: negative means 'do not play this message's sound', and it is reset to $04 after every message. Patched by showNextQueuedMessage ($FF) and handleMessageReviewKeys ($80).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_C000_C5EE",
   "unit": "game/textEngineC000"
  }
 ],
 "E02F": [
  {
   "name": "remoteIdentityByte",
   "meaning": "Byte from the opponent's identity packet, stored by the $EC00 sub-overlay at $EDAD.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "peerCommBuildId",
   "meaning": "The opponent's $E030 comm-build identifier as received during the handshake",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "E03F": [
  {
   "name": "modemCommandPhase",
   "meaning": "Counts down while a canned modem command string is being sent and the line is settling; $FF while a string is queued, $8D right after the RETURN hot key. $E003 X>=3 spins until it is 1 or 0.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "modemReplyTimer",
   "meaning": "Countdown started after a modem command is sent; on expiry a CR is faked into the game's receive queue. $FF while a command string is being emitted",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "E040": [
  {
   "name": "connectionPhase",
   "meaning": "0 = down, 1 = modem command/terminal mode, 2 = connect handshake, 3 = connected (the packet layer only runs at 3).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "commState",
   "meaning": "Link phase: 0 idle, 1 modem-command/terminal mode, 2 byte sync, 3 packet protocol running",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "E041": [
  {
   "name": "skipModemInitString",
   "meaning": "Non-zero suppresses the ATQ0V1X1A/D init string in openCommLink. Only ever set by the RETURN hot key (DEC at $E366) so that RETURN re-opens the link without redialling.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E042": [
  {
   "name": "uartRestartRequest",
   "meaning": "1 = re-evaluate the connection phase on the next tick (set by restartUart), $FF = force the phase back to 0 (set by stopCommNmi).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E043": [
  {
   "name": "carrierDebounceCount",
   "meaning": "Counts consecutive carrier samples that disagree with $E03C; a new state is accepted at $F0 (240) samples, about 4-5 seconds.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E044": [
  {
   "name": "txPaceCounter",
   "meaning": "Per-character pacing counter used outside terminal mode; reloaded from $E058 (1 at 300 baud, 2 at 1200) each time a character starts.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E045": [
  {
   "name": "carrierPollTimer",
   "meaning": "Free-running counter primed with $92 that periodically re-arms the carrier sample; also zeroed by the NMI whenever a character is received (i.e. an activity watchdog).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E046": [
  {
   "name": "lastHotkeyCode",
   "meaning": "Last key code seen by the hot-key dispatcher, used to suppress auto-repeat while the Commodore key is held.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E047": [
  {
   "name": "linkErrorCount",
   "meaning": "Incremented by the framed error hook at $E403 on every framing / stop-bit / overflow error; inspected by the connect handshake at $E7FA and cleared at $E814.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "resyncRequest",
   "meaning": "Non-zero forces the state machine back into the sync phase; cleared at E814",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "E048": [
  {
   "name": "modemOptionFlags",
   "meaning": "Constant $CC. Bit 7 = a Hayes modem is present, so AT command strings may be sent (queueModemCommandString returns immediately when it is clear); bit 6 = enable the 'CONNECT 1200' auto baud switch in the terminal loop.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E04A": [
  {
   "name": "carrierOverrideFlags",
   "meaning": "XOR mask / option byte used when sampling DCD. Bit 7 makes the module use the byte itself as the carrier sample (force carrier), bit 6 (toggled by the 'C' hot key with mask $E04B = $40) skips the CIA2_PRB read, bit 4 inverts the DCD polarity.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E04B": [
  {
   "name": "carrierOverrideMask",
   "meaning": "Constant $40, the bit the 'C' hot key toggles in $E04A.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E04C": [
  {
   "name": "dtrToggleMask",
   "meaning": "Constant $24 (PB2 = DTR and PB5), the bits the 'H' hot key toggles in CIA2_PRB.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E04D": [
  {
   "name": "userPortDdrb",
   "meaning": "Constant $26 written to CIA2_DDRB: PB1 (RTS), PB2 (DTR) and PB5 are outputs, PB0 (RXD) and PB4 (DCD) are inputs.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E04E": [
  {
   "name": "userPortPrbHangUp",
   "meaning": "Constant $22 written to CIA2_PRB to drop DTR (hang up) while leaving RTS asserted.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E04F": [
  {
   "name": "userPortPrbTable",
   "meaning": "Two bytes ($26,$26) indexed by isOriginateMode giving the idle CIA2_PRB value (RTS + DTR asserted). Both entries are identical in this build.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E051": [
  {
   "name": "ciaSdrPatternTable",
   "meaning": "Four bytes ($27,$2F,$3F,$37) indexed by ((3 AND isOriginateMode) EOR baudIndex) and written to CIA2_SDR while CIA2 timer A shifts them out on SP2. Purpose not established.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E055": [
  {
   "name": "baudIndex",
   "meaning": "Offset into baudParameterTable: 0 = 300 baud, 3 = 1200 baud. Set by the '3'/'1' hot keys and by the CONNECT-1200 auto-detect.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E056": [
  {
   "name": "bitPeriodLo",
   "meaning": "Low byte of the CIA timer latch that defines one bit time ($50 for 300 baud, $53 for 1200).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E057": [
  {
   "name": "bitPeriodHi",
   "meaning": "High byte of the bit-time latch ($0D50 = 3408 cycles = 300 baud, $0353 = 851 cycles = 1200 baud).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E058": [
  {
   "name": "txPaceReload",
   "meaning": "Reload value for txPaceCounter: 1 at 300 baud, 2 at 1200 baud.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E059": [
  {
   "name": "baudParameterTable",
   "meaning": "Two 3-byte entries {$50,$0D,$01} = 300 baud and {$53,$03,$02} = 1200 baud, copied to $E056-$E058 by loadBaudParameters.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E05F": [
  {
   "name": "linkStatusBorderTable",
   "meaning": "Eight bytes ($00,$0E,$1D,$0D,$14,$04,$1A,$0A) indexed by connectionPhase*2 + carrier. The value is shifted right to give the border colour; the shifted-out bit decides whether the border is painted at all, so terminal mode (index 2/3) always shows light blue = no carrier / blue = carrier, and the other phases only paint while the Commodore key is held.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E07D": [
  {
   "name": "completedExchangeCount",
   "meaning": "16-bit counter of completed packet exchanges, incremented at $E283. No code anywhere in the image reads it.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E07F": [
  {
   "name": "txHeaderExtraBits",
   "meaning": "ORed into the outgoing packet header at $E20F. Never written, so it is always 0 - reserved or dead.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E080": [
  {
   "name": "rxHeaderByte",
   "meaning": "The header byte of the packet currently being received (bit 7 = chat payload, low 3 bits = length, $70-$7F = code-download packet).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E081": [
  {
   "name": "rxFsmState",
   "meaning": "State of receivePacketFsm: 0 = expect header, 1/2 = collecting payload, bit 7 set = collecting chat bytes.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E082": [
  {
   "name": "rxBytesRemaining",
   "meaning": "Payload bytes still to be read for the packet in progress.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E083": [
  {
   "name": "txFsmState",
   "meaning": "State of sendPacketFsm: 0 = idle, bit 0 = sending the payload, bit 1 = waiting for the transmitter to drain, bit 7 = the frame in flight was a chat frame (no exchange acknowledgement).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E084": [
  {
   "name": "txByteIndex",
   "meaning": "Index of the next byte of txPacketBuffer to hand to sendLinkByte.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E085": [
  {
   "name": "sendRequestCount",
   "meaning": "Number of outstanding 'send my packet' requests posted through $E000 with X=1.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E086": [
  {
   "name": "hostInRing",
   "meaning": "8-byte ring of bytes waiting to be handed to the game through $E006 (chat characters from the opponent, or modem responses echoed in terminal mode).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E08E": [
  {
   "name": "txChatBurstCount",
   "meaning": "Number of chat bytes (max 3) being packed into the frame currently being built.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E08F": [
  {
   "name": "hostOutReadIndex",
   "meaning": "Read index of the host-output ring; decrements and wraps from 0 to 15.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E090": [
  {
   "name": "hostOutWriteIndex",
   "meaning": "Write index of the host-output ring; decrements and wraps from 0 to 15.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E091": [
  {
   "name": "hostOutRing",
   "meaning": "16-byte ring of bytes the game wants to send ($E009), or the reversed modem command string loaded by queueModemCommandString.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E0A1": [
  {
   "name": "hostInCount",
   "meaning": "Number of bytes in hostInRing (max 8).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E0A2": [
  {
   "name": "hostInReadIndex",
   "meaning": "Read index of hostInRing.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E0A3": [
  {
   "name": "hostInWriteIndex",
   "meaning": "Write index of hostInRing.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E0A4": [
  {
   "name": "frameInFlightFlag",
   "meaning": "Set by the frame layer ($E8A8) when a frame has been handed to the transmitter; cleared together with $E0A6 by runModemStateMachine once the UART transmit queue has drained.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "packetTxBusy",
   "meaning": "Incremented when the packet layer starts building an outgoing message, cleared at E775; gates the transmit path",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "E0A5": [
  {
   "name": "txPendingCount",
   "meaning": "Number of bytes queued in or being shifted out of the software UART; incremented by queueByteForTransmit and decremented by the NMI when a character completes.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "pendingTxByteCount",
   "meaning": "Number of bytes still to be clocked out of the UART transmit ring; incremented by sub_E73C, decremented by the CIA2 interrupt handler",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "E0A6": [
  {
   "name": "packetInFlightCount",
   "meaning": "Set to (packet length + 1) when sendPacketFsm hands a frame over, and cleared when the transmitter drains; while non-zero the packet layer will not start another frame or acknowledge the exchange. Always addressed as $E0A5,X with X = $E0A8 = 1.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "packetTxEnable",
   "meaning": "Gate for the packet layer's data-transmit path; only ever cleared (E778) in either comm build, so that path is dead code",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "E0A7": [
  {
   "name": "serviceLock",
   "meaning": "Re-entrancy lock for serviceCommTick: normally 1, decremented to 0 for the duration of the body, so an interrupt that re-enters simply returns.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E0A8": [
  {
   "name": "packetSlotIndex",
   "meaning": "Constant $01 used as the index into $E0A5,X so that the packet layer works on $E0A6. Deliberately excluded from the block that clearProtocolVars zeroes.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E414": [
  {
   "name": "uartTxCount",
   "meaning": "Bytes in the raw UART transmit ring (max 22).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E415": [
  {
   "name": "uartTxReadIndex",
   "meaning": "Read index of the UART transmit ring (wraps 0 -> 21).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E416": [
  {
   "name": "uartTxWriteIndex",
   "meaning": "Write index of the UART transmit ring.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E417": [
  {
   "name": "uartTxRing",
   "meaning": "22-byte transmit FIFO drained one character at a time by the NMI transmitter.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E42D": [
  {
   "name": "uartRxCount",
   "meaning": "Bytes in the raw UART receive ring (max 20).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "rxRingCount",
   "meaning": "Number of bytes waiting in the UART receive ring $E430 (capacity 20); read at E9D9 to see whether a whole packet has arrived",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "E42E": [
  {
   "name": "uartRxReadIndex",
   "meaning": "Read index of the UART receive ring.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E42F": [
  {
   "name": "uartRxWriteIndex",
   "meaning": "Write index of the UART receive ring, advanced by the NMI receiver.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E430": [
  {
   "name": "uartRxRing",
   "meaning": "20-byte receive FIFO filled by the NMI receiver.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E444": [
  {
   "name": "arqTxCount",
   "meaning": "Bytes in the frame-layer transmit ring (max 18).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E445": [
  {
   "name": "arqTxReadIndex",
   "meaning": "Read index of the frame-layer transmit ring.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E446": [
  {
   "name": "arqTxWriteIndex",
   "meaning": "Write index of the frame-layer transmit ring.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E447": [
  {
   "name": "arqTxRing",
   "meaning": "18-byte queue of payload bytes the packet layer has produced and the frame layer at $E8xx has not yet packed into a frame.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E459": [
  {
   "name": "arqRxCount",
   "meaning": "Bytes in the frame-layer receive ring (max 18); also tested by the frame layer at $EA73.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "inboundMessageRingCount",
   "meaning": "Number of bytes in the ring $E45C that carries decoded messages up to the game; a packet is only accepted while this is 0",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "E45A": [
  {
   "name": "arqRxReadIndex",
   "meaning": "Read index of the frame-layer receive ring.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E45B": [
  {
   "name": "arqRxWriteIndex",
   "meaning": "Write index of the frame-layer receive ring.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E45C": [
  {
   "name": "arqRxRing",
   "meaning": "18-byte queue of validated payload bytes the frame layer has delivered for the packet layer to read.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E5BB": [
  {
   "name": "txCharActive",
   "meaning": "Non-zero while a character is being shifted out by the NMI transmitter.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E5BC": [
  {
   "name": "txBitCountReload",
   "meaning": "Number of bit times per character, always 9 (start bit plus 8 data bits; the stop bit is the idle level).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E5BD": [
  {
   "name": "txBitCounter",
   "meaning": "Bits left in the character being transmitted; going negative asks for the next character.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E5BE": [
  {
   "name": "txShiftRegister",
   "meaning": "Character being shifted out LSB first onto PA2 (TXD).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E5BF": [
  {
   "name": "rxBitCounter",
   "meaning": "Receive bit counter: $83 = waiting for the start bit to be validated, then 9 down to 0 for the data bits and the stop bit.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "E5C0": [
  {
   "name": "rxShiftRegister",
   "meaning": "Character being assembled from CIA2_PRB bit 0 (RXD), LSB first.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "EB00": [
  {
   "name": "rxIdleWatchdog",
   "meaning": "Cleared by the NMI whenever a character is received and counted up by the frame layer; reaching $10 forces the connect handshake to restart.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "linkStallCounter",
   "meaning": "Counts consecutive service ticks with no protocol progress; 16 forces a full resync. Also cleared at $E716 by the CIA2 interrupt handler when a character frame starts",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EB01": [
  {
   "name": "txDelayCounter",
   "meaning": "Negative countdown loaded from a bit-7-set byte pulled out of the host-output ring; while it is negative the terminal loop sends nothing, which implements the Hayes guard times embedded in the '+++'/ATH0 string.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "modemDelayCounter",
   "meaning": "Negative countdown for the guard times embedded in the modem command strings (a string byte with bit 7 set is loaded here); while negative no further command characters are sent",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EB02": [
  {
   "name": "lastTerminalChar",
   "meaning": "Previous character received in terminal mode; compared with '0' when a CR arrives to recognise a 'CONNECT 1200' result line.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  },
  {
   "name": "lastTerminalChar",
   "meaning": "Last character delivered to the game in terminal mode; used to spot the modem result '0' immediately before a CR",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EB03": [
  {
   "name": "remoteCodeBuffer",
   "meaning": "Scratch buffer that packets with header $70-$7F are copied into and that a zero-length $7x packet then calls with JSR. Its file content is a single RTS, so the facility is inert unless the peer uses it.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "EBFD": [
  {
   "name": "hangUpStringOffset",
   "meaning": "Page-$EB offset ($D6) of the reversed '+++ / ATH0' hang-up string used by hangUpModem.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "EBFE": [
  {
   "name": "initStringOffsetTable",
   "meaning": "Two page-$EB offsets indexed by isOriginateMode: $E3 = 'ATQ0V1X1A' (answer), $F0 = 'ATQ0V1X1D' (dial/originate).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E000",
   "unit": "game/modemDriverE000"
  }
 ],
 "EAB7": [
  {
   "name": "syncFfCount",
   "meaning": "Rounds of the byte-sync phase; 10 or more restarts the sync",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EAB8": [
  {
   "name": "zeroRunLength",
   "meaning": "Number of consecutive $00 bytes received (maintained by sub_E743); two or more mean the peer wants a resync",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EAB9": [
  {
   "name": "rxPacketRemaining",
   "meaning": "Payload bytes still expected for the packet being assembled; non-zero means receivePacket resumes mid-packet",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EABA": [
  {
   "name": "packetByteCounter",
   "meaning": "Scratch loop counter used while building or copying a packet",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EABB": [
  {
   "name": "rxControlByte",
   "meaning": "Control byte of the packet currently being dispatched",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EABC": [
  {
   "name": "lastAcceptedControlByte",
   "meaning": "Control byte of the last accepted data packet",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EABD": [
  {
   "name": "rxSeqCounter",
   "meaning": "Two-bit receive sequence number; incremented for every accepted data packet and echoed in outgoing packets",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EABE": [
  {
   "name": "txSeqCounter",
   "meaning": "Two-bit transmit sequence number embedded in bits 2-3 of the command byte of each queued message",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EABF": [
  {
   "name": "resendRequest",
   "meaning": "Set to 1 by a lone $00 byte and to the outstanding message index by a high ack byte; makes the transmitter resend",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EAC0": [
  {
   "name": "ackPending",
   "meaning": "Non-zero when an acknowledgement packet ($EC) has to be sent; set on a received $FF and on an accepted data packet, cleared after any packet is transmitted",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EAC1": [
  {
   "name": "sendFfFlag",
   "meaning": "Would make the transmitter emit a raw $FF; never set anywhere in the image, so the branch at E872 is dead",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EAC2": [
  {
   "name": "sendZeroFlag",
   "meaning": "Decremented (to $FF) on a checksum failure; makes the transmitter emit a raw $00 nak on the next tick",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EAC3": [
  {
   "name": "txRingIndex",
   "meaning": "Current read/write index into the 35-byte transmit message ring $EAC7",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EAC4": [
  {
   "name": "txRingNextIndex",
   "meaning": "Ring index just past the message that was queued last",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EAC5": [
  {
   "name": "txCurrentMsgIndex",
   "meaning": "Ring index of the newest queued message (bit 7 = slot in use)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EAC6": [
  {
   "name": "txOldestUnackedIndex",
   "meaning": "Ring index of the oldest unacknowledged message (bit 7 = slot in use); the two slots give a window of two outstanding messages",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EAEA": [
  {
   "name": "packetLength",
   "meaning": "Number of bytes currently in packetBuffer (excluding the trailing checksum while building)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EAFE": [
  {
   "name": "packetLoopTemp",
   "meaning": "Scratch byte used to preserve loop indices across subroutine calls in the packet code",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "EAFF": [
  {
   "name": "rxLengthCodeByte",
   "meaning": "The raw length-code byte of the packet being received, re-stored as packetBuffer[0] for the checksum",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T34_E7CA",
   "unit": "game/modemDriverE000"
  }
 ],
 "E2BC": [
  {
   "name": "trainerUnitScanIndex",
   "meaning": "loop counter of scanTrainerUnitsForOrders, stored as a data byte inside the code stream at $E2BC",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "8800": [
  {
   "name": "trainerUnitReplanClock",
   "meaning": "50-byte table $8800-$8831 indexed by AI unit ordinal (absolute unit - 50): the value of the down-counting game clock $91CB at which the AI may reconsider that unit. $8831 is the AI comcen's own entry",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiUnitOrderRound",
   "meaning": "50 bytes, one per AI unit (indexed as $8800+i with i = unit-50, or $87CE+unit): the roundsRemaining value stamped when the unit was last given an order; compared with $91CB to decide whether an order has expired",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiOrderClockStamp",
   "meaning": "50-byte array $8800-$8831 indexed by AI unit index - 50: the value of the game clock $91CB when that unit last received an objective. Read as $87CE,unit with the absolute unit index. chooseAiDroneTarget re-stamps entries 0-45 when it commits to an all-out attack. Overlays the pre-game message text 'GET OPPONENT ON PHONE AND...', which is dead once a battle starts",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiUnitNextActionClock",
   "meaning": "50 bytes: game-clock value at which AI unit 50+i may act again; written by rebuildAiGroupTables as clock - groupDelay and compared with $91CB by the resident AI ($E2E0, $E4DE). Physically the low half of the 100-entry array whose upper half ($8832) holds group ids, so the entries for units 0-49 are free for this use. Overwrites the message strings loaded at $8800.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "checksumScratch",
   "meaning": "receives the raw checksum sum at boot; overwritten later when the message strings are loaded to $8800-$89FF",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "8832": [
  {
   "name": "trainerUnitGroup",
   "meaning": "50-byte table $8832-$8863 indexed by AI unit ordinal: the AI's internal group id ($FF = dead/none, 0 = the comcen's group)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiUnitGroup",
   "meaning": "50 bytes, one per AI unit ($8832+i, or $8800+unit for unit 50-99): the AI group the unit belongs to; loaded from the play template by the $EC00 track-29 setup overlay and re-assigned at $EB4D",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiUnitObjective",
   "meaning": "50-byte array $8832-$8863 indexed by AI unit index - 50 giving the objective id assigned to that unit; the same bytes are addressed as $8800,unit with the absolute index 50-99 elsewhere in the module",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiUnitGroup",
   "meaning": "50 bytes: group id (0-11) of AI unit 50+i, copied from the deployment template; $FF marks a dead/removed unit. The resident AI addresses the same bytes as $8800+absoluteUnitIndex.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8864": [
  {
   "name": "trainerUnitPenetrated",
   "meaning": "50-byte table $8864-$8895 indexed by AI unit ordinal; bit 7 set by trainerUnitStepHook once the unit has scored both territory zones (unitTypeTable bits 3 and 4). $8895 is the comcen entry",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiUnitDeepFlag",
   "meaning": "50 bytes ($8864+i, or $8832+unit): bit 7 set by sub_E0E8 when the unit's unitType zone-score bits 3 and 4 are both set (unit has reached the opponent's end zone); read at $EB34 and $E579",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiUnitZoneDone",
   "meaning": "50 bytes: per AI unit, bit 7 set = unit is not counted as an attacker any more (set to $80 for all units when the zone-score option $92AC is 0, and by the resident AI at $E0FA once a unit has scored both zones). sub_E531 counts the units with bit 7 clear.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8896": [
  {
   "name": "trainerCommandStage",
   "meaning": "$8896-$8899 stage one command before it is appended to the queue (command byte, unit, column, row); the same 8 bytes $8896-$889D are reused as the direction-preference list built by buildDirectionPreference and as the directional threat histogram of scanEnemyThreat",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiCommandStaging",
   "meaning": "8-byte scratch area: command byte + up to 3 parameters for queueAiCommand, and also the 8-slot per-direction array used by buildDirectionFan and assessThreatAroundAiComcen",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "threatByDirection",
   "meaning": "8-byte ring of accumulated enemy pressure per compass direction, filled by sub_EBEF and smoothed in place at $ECC5; the same 8 bytes double as the AI's command staging buffer in sub_EA46",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiCommandScratch",
   "meaning": "Command byte + arguments assembled before sub_EA46 appends them to the AI command queue; $EC73 builds $A5,$00,$14 (setup option 0 = 20 points) for the DEFENDER game type.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "88A0": [
  {
   "name": "trainerCommandQueue",
   "meaning": "50-byte FIFO $88A0-$88D1 of command bytes the AI has generated; flushTrainerCommandQueue moves whole commands from here into receivedPacketBuffer, at most 7 bytes per exchange",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiCommandQueue",
   "meaning": "the AI's outgoing command byte queue, up to 50 bytes; filled by queueAiCommand and drained into the fake received packet by injectAiCommandsIntoPacket",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiCommandQueue",
   "meaning": "50-byte queue of command bytes the trainer AI still has to inject into the comm module's incoming packet buffer (filled by sub_EA46, drained by sub_EA7B).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "88D2": [
  {
   "name": "trainerGroupTargetCol",
   "meaning": "per-group target column (12 entries $88D2-$88DD)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "groupTargetCol",
   "meaning": "per-group (12 entries) target map column",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupDestCol",
   "meaning": "12 bytes: destination column of each AI group (16-bit sum during rebuildAiGroupTables, high bytes in $0464, then divided by the member count).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "88DE": [
  {
   "name": "trainerGroupTargetRow",
   "meaning": "per-group target row (12 entries $88DE-$88E9)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "groupTargetRow",
   "meaning": "per-group (12 entries) target map row",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupDestRow",
   "meaning": "12 bytes: destination row of each AI group (high bytes of the sum in $0478).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "88EA": [
  {
   "name": "trainerGroupCentreCol",
   "meaning": "per-group average current column",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "groupCentreCol",
   "meaning": "per-group average column of the living members (written by sub_E531 from computeGroupCentroid)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "objectiveColumn",
   "meaning": "Map column of each AI objective (12-entry table, parallel with $88D2/$88DE/$88F6/$8902/$890E/$891A/$8926/$8932/$893E)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupCentreCol",
   "meaning": "12 bytes: average current column of each AI group (high bytes of the sum in $048C).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "88F6": [
  {
   "name": "trainerGroupCentreRow",
   "meaning": "per-group average current row",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "groupCentreRow",
   "meaning": "per-group average row of the living members",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "objectiveRow",
   "meaning": "Map row of each AI objective",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupCentreRow",
   "meaning": "12 bytes: average current row of each AI group (high bytes of the sum in $04A0).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8902": [
  {
   "name": "trainerGroupSize",
   "meaning": "per-group live member count (0 = empty)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "groupUnitCount",
   "meaning": "per-group number of living members (0 = group wiped out)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "objectiveWeight",
   "meaning": "Per-objective weight/threat threshold; summed in chooseAiDroneTarget and copied into $8956 as the threat threshold of the current objective",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupUnitCount",
   "meaning": "12 bytes: number of live units in each AI group; used as the divisor for the four averages and returned by sub_E531.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "890E": [
  {
   "name": "trainerGroupBoxLeft",
   "meaning": "per-group distance from the average destination to the leftmost member destination",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "groupSpanLeft",
   "meaning": "per-group extent of the formation to the left of its centre (centre column - minimum column)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupBoxLeft",
   "meaning": "12 bytes: centre column minus leftmost destination column of the group (bounding-box extent, written by sub_E531, used by sub_E5FF to keep the group on the map).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "891A": [
  {
   "name": "trainerGroupBoxTop",
   "meaning": "per-group distance from the average destination to the topmost member destination",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "groupSpanUp",
   "meaning": "per-group extent above the centre (centre row - minimum row)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupBoxTop",
   "meaning": "12 bytes: centre row minus topmost destination row of the group.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8926": [
  {
   "name": "trainerGroupBoxRight",
   "meaning": "per-group distance from the average destination to the rightmost member destination, plus 1",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "groupSpanRight",
   "meaning": "per-group extent to the right of the centre (maximum column - centre + 1)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupBoxRight",
   "meaning": "12 bytes: rightmost destination column minus centre column, plus 1.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8932": [
  {
   "name": "trainerGroupBoxBottom",
   "meaning": "per-group distance from the average destination to the bottom member destination, plus 1",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "groupSpanDown",
   "meaning": "per-group extent below the centre (maximum row - centre + 1)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupBoxBottom",
   "meaning": "12 bytes: bottommost destination row minus centre row, plus 1.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "893E": [
  {
   "name": "trainerGroupFlags",
   "meaning": "per-group flag byte; bit 0 mirrors the direction preference, bit 5 = large group in contact, bit 6 = group is committed to the attack, bit 7 = group has seen the enemy comcen. The low nibble survives, the high nibble is cleared when the group is re-planned",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "groupFlags",
   "meaning": "per-group flag byte: bit 0 mirrors the direction fan, bit 5 set when the group commits to the enemy comcen ($E3C3), bit 6 = group has contact with the player comcen ($EB1F), bit 7 = group disabled/suspended, low nibble also used as a mode",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "objectiveFlags",
   "meaning": "Per-objective flag byte; bit 5 doubles its weight and bit 6 marks it committed. chooseAiDroneTarget ORs bit 6 into every entry when it launches the all-out attack",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupStartDelay",
   "meaning": "12 bytes: random 0-15 delay in rounds before each AI group starts moving; subtracted from the game clock to form aiUnitNextActionClock. Group 0 and the guard group get no delay.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "894A": [
  {
   "name": "directionPreferenceOffsetTable",
   "meaning": "8 bytes read by buildDirectionPreference ($E7F4) as the compass offsets of the eight search ranks; no traced code ever writes it, and it overlaps the already-consumed side-1 game-type defaults table in the $8800 strings overlay, so its value is 02 02 02 01 01 02 02 02",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "dirFanOffsetTable",
   "meaning": "8 direction offsets used by buildDirectionFan. NEVER written by any code in the disassembly, so at run time it holds whatever the $8800 message-string block left there ($02,$02,$02,$01,$01,$02,$02,$02) - probably a bug",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiHeadingSearchOrder",
   "meaning": "8 bytes built by buildAiHeadingSearchOrder: heading offsets 0,+/-1,+/-1,+/-2,+/-2,+/-3,+/-3,+/-4 (mod 8) with random signs, used to try alternative headings.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8952": [
  {
   "name": "trainerCommandQueueLength",
   "meaning": "number of bytes currently in trainerCommandQueue",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiCmdQueueLength",
   "meaning": "number of bytes currently in aiCommandQueue; cleared at $EC17 by the track-29 setup overlay",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiCommandQueueLen",
   "meaning": "Number of bytes currently in aiCommandQueue; cleared by initTrainerAi.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8953": [
  {
   "name": "trainerLastGroupId",
   "meaning": "highest group id in use; incremented when a newly spotted unit gets its own group ($EB46)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "lastAiGroupId",
   "meaning": "highest group id in use; incremented at $EB46 when a deep spy unit gets its own group",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "objectiveCountMinus1",
   "meaning": "Index of the last AI objective (set by the track 29 $EC00 setup overlay)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiHighestGroup",
   "meaning": "Index of the highest AI group in use ($0A for plans 0/1, $04 for plans 2-4), from groupCountByPlanTable.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8954": [
  {
   "name": "trainerGroupCount",
   "meaning": "number of AI groups (trainerLastGroupId + 1), set by the trainer setup overlay at $ECC6",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupCount",
   "meaning": "number of pre-planned AI groups (lastAiGroupId+1). Only ever written by the track-29 setup overlay ($ECC6); read here at $E6A5, $E960, $EB3C",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "objectiveCount",
   "meaning": "Number of AI objectives ($8953 + 1)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupCount",
   "meaning": "aiHighestGroup + 1 = number of AI groups (11 or 5).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8955": [
  {
   "name": "trainerCurrentGroup",
   "meaning": "group id the planner is currently working on",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "currentAiGroup",
   "meaning": "group id the AI is currently thinking about; selects the per-group tables in almost every routine of this module",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiCurrentGroup",
   "meaning": "Group index sub_E531 last measured; shiftAiGroupDestinations uses it to select the units to move.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8956": [
  {
   "name": "trainerGroupValueLo",
   "meaning": "16-bit combat value of the current group ($8956 low, $8957 high), summed from unitCombatValueTable",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "groupStrengthLo",
   "meaning": "16-bit sum (with $8957) of unitTypeStrengthTable over the current group's living members; compared against the enemy threat total by isGroupDestinationTooRisky",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "objectiveThreatThreshold",
   "meaning": "Threat level the sub_EBEF scan must reach before the AI reacts around the current objective; default 3",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  }
 ],
 "8958": [
  {
   "name": "trainerGroupDeltaCol",
   "meaning": "column delta from the group's average destination to the clamped new target (copy of zp_2A)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "895A": [
  {
   "name": "trainerGroupHasSlowUnits",
   "meaning": "$FF when the current group contains a GRUNT (type 0) or BOOMER (type 2); doubles the replan delay",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "895B": [
  {
   "name": "trainerAdvanceDistance",
   "meaning": "how far the path search may advance this turn (5 for the comcen group, otherwise the distance class to the target)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "895D": [
  {
   "name": "trainerAssaultGroup",
   "meaning": "id of the group the AI is currently attacking with; $FF = none, bit 7 set = pending/invalid. Chosen by $E83C",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "focusGroupId",
   "meaning": "the group the AI comcen escorts; bit 7 set = suspended, $FF = none",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "currentObjective",
   "meaning": "Index of the objective the AI is currently working on, bit 7 set = none",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiMainGroupIndex",
   "meaning": "Index of the AI's main/spearhead group from mainGroupByPlanTable ($FF = none for plans 0/1, else 4). Group 0 is ordered to the same point as this group at $ED49.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "895E": [
  {
   "name": "trainerAssaultCol",
   "meaning": "average column of the assault group (or the AI comcen column when there is none)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "focusGroupCol",
   "meaning": "column of focusGroupId's centre (AI comcen column when there is no focus group)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "895F": [
  {
   "name": "trainerAssaultRow",
   "meaning": "average row of the assault group",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "focusGroupRow",
   "meaning": "row of focusGroupId's centre",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "8960": [
  {
   "name": "trainerBestDirection",
   "meaning": "compass direction 0..7 returned by scanEnemyThreat ($EBEF) as the least threatened / most promising heading",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "comcenEscapeDirection",
   "meaning": "direction 0-7 returned by assessThreatAroundAiComcen as the least threatened one, or 8 = no threat (index 8 of dirToColDelta/dirToRowDelta is the (0,0) entry)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "8961": [
  {
   "name": "trainerEnemyProximity",
   "meaning": "largest 'closeness' value found by scanEnemyThreat; 0 means no enemy unit is near",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "comcenThreatDepth",
   "meaning": "largest (weapon range - distance) over the player units threatening the AI comcen; used as the retreat distance",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "8962": [
  {
   "name": "trainerThreatScore",
   "meaning": "threat score from scanEnemyThreat, compared with trainerRecallThreshold to decide whether the assault group must come home",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "comcenThreatScore",
   "meaning": "overall danger around the AI comcen, (total threat - threat count)/2; compared with $897A at $E29A",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "8963": [
  {
   "name": "trainerDefendMode",
   "meaning": "0 = attack this round, $FF = defend (enemies close and either no assault group or the comcen was just hit)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "comcenFleeFlag",
   "meaning": "$FF when the comcen is in weapon range, the focus group is active and the comcen is not already moving (retreat path at $E266); 0 otherwise",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "8964": [
  {
   "name": "trainerHomeGroup",
   "meaning": "id of the group that always guards the AI comcen; picked randomly (or $FF) by the trainer setup overlay at $ECA3",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "homeDefenceGroupId",
   "meaning": "group id reserved for defending the AI comcen (random 1-3, or $FF for play 1); excluded from the comcen-contact retasking at $EB17/$EB41",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGuardGroupIndex",
   "meaning": "Group told to stay on the AI comcen: $FF (none) for plan 1, otherwise a random 1-3. Its units also get no start delay.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8965": [
  {
   "name": "trainerActionBudget",
   "meaning": "how much the AI may do this round: the AI comcen's energy (unitEnergy[99] AND $3F), or a flat 50 when the DAMAGE option is off; below 20 the AI only repairs",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "comcenProjectedEnergy",
   "meaning": "AI comcen energy as it will be after the $9A repair commands still sitting in the queue have executed; reloaded from unitEnergy[99] each round at $E195",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "8966": [
  {
   "name": "trainerScratchByte",
   "meaning": "scratch: holds the current group's flag byte during decideTrainerGroupAction, and is used as a negative 'a report command was already queued' marker by the drone/missile code",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiScratchFlag",
   "meaning": "multi-purpose byte: the play/scenario index 0-4 during setup, a copy of groupFlags at $E31E (bits 6/7 tested), and a bit-7 abort flag for injectAiCommandsIntoPacket",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiActionBudget",
   "meaning": "Reset to 0 at $EA7D each exchange and decremented by every AI action; once negative no further orders are generated and the queued-command flush at $EA83 is skipped",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiPlanIndex",
   "meaning": "0-4, selects the AI's deployment template and group layout: 0 SCRIMAGE/THE BOMB, 1 QB SNEAK, 2 FACE-OFF, 3 SLUGGERS, 4 FULL WAR/DEFENDER. Reused as scratch by the resident AI after the game starts.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8968": [
  {
   "name": "trainerOrdersThisRound",
   "meaning": "number of command bytes the AI has generated this round; the planner stops at 4-5 to keep the packets small",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiCommandsThisRound",
   "meaning": "commands the AI has issued in the current round; cleared at $E176, incremented by queueAiCommand, capped at 3-5 by the callers",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiPacketCostCounter",
   "meaning": "Accumulated cost of the AI's activity this round (each missile strike adds 2); compared with 5 at $E205",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiCommandsThisRound",
   "meaning": "Counter of commands the AI has queued; the resident AI stops issuing orders once it reaches 4-5 ($E205, $E2C2). Incremented by sub_EA46, so $EC85 decrements it again after the DEFENDER setup command.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8969": [
  {
   "name": "trainerRallyCol",
   "meaning": "column of the current objective, a random cell near the human comcen",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiObjectiveCol",
   "meaning": "Column of the AI's current objective, set by sub_E12F to the human comcen's column ($F671 = unitCol[49]) jittered by -2..+2 and clipped to 0-39.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "896A": [
  {
   "name": "trainerRallyRow",
   "meaning": "row of the current objective",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiObjectiveRow",
   "meaning": "Row of the AI's current objective (unitRow[49] jittered the same way).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "896B": [
  {
   "name": "trainerRallyCountdown",
   "meaning": "rounds left before a new rally point is chosen (reloaded with 5)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiObjectiveTimer",
   "meaning": "Rounds until the AI picks a new objective (reloaded with 5 at $E18D); cleared here.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "896C": [
  {
   "name": "trainerEnemyComcenSeen",
   "meaning": "latched copy of unitEnergy[49] once its bit 7 (spotted) is set; scales the random spread of the rally point",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiObjectiveMode",
   "meaning": "Flag tested with BIT at $E15C that widens the jitter applied to the objective; cleared here, set from $F9F5 at $E17E.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "896D": [
  {
   "name": "trainerReplanUrgency",
   "meaning": "upper bound (in game-clock units) on how long the current group may go without being reconsidered: 5 normally, 3 when in contact, 2 for the home group",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "maxOrderStep",
   "meaning": "upper bound on how far a group may be ordered in one command (2, 3 or 5 depending on the AI mode); applied at $E4C7",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "896E": [
  {
   "name": "trainerAlertClock",
   "meaning": "the game clock at the last defensive alert. Because the clock counts down, every later comparison $896E >= $91CB is true, so in practice this is a latch meaning 'the AI has been forced onto the defensive at least once'",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "comcenEmergencyStamp",
   "meaning": "roundsRemaining stamped when the AI comcen last had to run away ($E269); used as a tie-breaker in isGroupDestinationTooRisky and at $E3E2",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiPhaseChangeClock",
   "meaning": "Clock value (start clock minus a random 32-95 rounds, only for plans >= 2) at which the resident AI changes behaviour; compared with $91CB at $E3E2 and $E762.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8971": [
  {
   "name": "trainerComcenDamage",
   "meaning": "low nibble of unitStunTable[99] ($F9C3) = the AI comcen's damaged-systems count; $0F stops the AI completely for that round, 1-7 costs 16 budget points",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "comcenStunCounter",
   "meaning": "low nibble of unitStunDig[99]: how long the AI comcen is stunned; 0 = fit",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "8976": [
  {
   "name": "trainerDroneTarget",
   "meaning": "AI unit ordinal the drone is flying at; $FF = no drone mission",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiStrikeTargetUnit",
   "meaning": "player unit index chosen as the drone/missile target by the scan at $EF9B ($FF = none); when it is $31 (the player comcen) the group code at $E3AA attacks directly",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiDroneTargetUnit",
   "meaning": "Unit index the AI's next drone is aimed at, $FF = none. Set by chooseAiDroneTarget, consumed by launchAiDrone",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiDroneTargetUnit",
   "meaning": "Unit index the drone is flying at ($EE19 in the drone module); set to $FF (none) by initTrainerAi.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "trainerDroneTarget",
   "meaning": "index of the human player's unit the drone is flying at ($FF = no mission); picked by the target scan in the module's own $EE00 image and used here to fetch the aim cell.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "8977": [
  {
   "name": "trainerDroneTimer",
   "meaning": "drone state: bit 7 set means the track 29 s4-5 drone-flight sub-overlay is resident at $EE00; positive values are a countdown to the next drone action, 0 allows a launch",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "eeOverlayState",
   "meaning": "state of the swappable $EE00 page: bit 7 = the track-29 drone sub-overlay is resident (the AI think routines are skipped), otherwise a 3..0 countdown after the module's own $EE00 code has been reloaded",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiDroneOverlayState",
   "meaning": "$FF = the track 29 s4-5 drone-flying overlay is resident at $EE00; 0-$15 = countdown before the next launch is allowed (launchAiDrone requires 0 and sets 3, chooseAiDroneTarget sets $15). Ticked down at $E0E1",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiSubOverlayState",
   "meaning": "Which $EE00 sub-overlay the comm build 1 has resident: 0 = none/idle, 3 = counted down after loading T35 s14-15, $FF = the drone module T29 s4-5 is loaded ($E0AB-$E0DC). Cleared here.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "trainerDroneTimer",
   "meaning": "state of the swappable $EE00 page: bit 7 set = this track-29 s4-5 drone image is resident (the module's other AI think routines are skipped); not touched by this chunk but it is what gates the JSR $EE00 at $E0A5.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "897A": [
  {
   "name": "trainerRecallThreshold",
   "meaning": "threat score above which the assault group is recalled to defend the comcen (initialised to 5 at $EC44)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "comcenThreatThreshold",
   "meaning": "threat score above which the comcen reacts ($E29A). Set to 5 by the track-29 setup overlay ($EC44) and never written by this module",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiRegroupThreshold",
   "meaning": "Constant 5 stored by initTrainerAi and compared with a computed value at $E29A before the AI regroups its main group.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "897B": [
  {
   "name": "trainerComcenHit",
   "meaning": "$FF when unitLocalFlags[99] bit 1 showed a hit on the AI comcen during this pass",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiComcenStateSaved",
   "meaning": "Comcen-related AI state copied to $897C at $E106 when unit 99 becomes visible; cleared here.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "897C": [
  {
   "name": "trainerComcenHitRecent",
   "meaning": "sticky version of trainerComcenHit (this pass or the previous one); forces defend mode and makes the path search cautious",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "comcenIsMoving",
   "meaning": "$FF when the AI comcen's unitGroupFlags bit 1 (moving) was set at the last check; a stationary comcen makes every threat count one point more",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiAggressionFlag",
   "meaning": "Bit 7 read at $EC40 (adds one to every unit's threat weight when clear) and at $E1F3; paired with $897B in the comcen-exposure code at $E106",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiComcenState",
   "meaning": "Comcen-related AI state derived from unitStatusFlags[99] ($FBB7) at $E109-$E119; cleared here.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "F671": [
  {
   "name": "humanComcenCol",
   "meaning": "unitCol[49] = the human player's comcen column (unit 49 is side 0's comcen)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "F6D5": [
  {
   "name": "humanComcenRow",
   "meaning": "unitRow[49] = the human player's comcen row",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "F6A3": [
  {
   "name": "trainerComcenCol",
   "meaning": "unitCol[99] = the AI comcen's column (unit 99 is side 1's comcen)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "F707": [
  {
   "name": "trainerComcenRow",
   "meaning": "unitRow[99] = the AI comcen's row",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "F79E": [
  {
   "name": "trainerUnitType",
   "meaning": "unitTypeTable + 50, indexed by AI unit ordinal; bit 7 = dead, bits 0-2 = type",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiUnitType",
   "meaning": "unitTypeTable + 50: type of AI unit 50+i, bit 7 = removed from play.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "F802": [
  {
   "name": "trainerUnitDestCol",
   "meaning": "unitDestCol + 50 (bits 0-5), indexed by AI unit ordinal",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiUnitDestCol",
   "meaning": "unitDestCol + 50: destination column of AI unit 50+i (bit 7 = no destination/idle).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "F866": [
  {
   "name": "trainerUnitDestRow",
   "meaning": "unitDestRow + 50 (bits 0-5), indexed by AI unit ordinal",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiUnitDestRow",
   "meaning": "unitDestRow + 50: destination row of AI unit 50+i.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "F833": [
  {
   "name": "trainerComcenDestCol",
   "meaning": "unitDestCol[99], the AI comcen's ordered destination column",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiComcenDestCol",
   "meaning": "unitDestCol[99]; bits 6-7 cleared for THE BOMB, and used as the guard group's rally point.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "F897": [
  {
   "name": "trainerComcenDestRow",
   "meaning": "unitDestRow[99]",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiComcenDestRow",
   "meaning": "unitDestRow[99], rally row of the guard group.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "F73A": [
  {
   "name": "trainerUnitGroupByte",
   "meaning": "unitGroupByte + 50: the real in-game group bits of the AI's units (bit 6 = in a group, bits 2-5 = group id)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  }
 ],
 "F9C3": [
  {
   "name": "trainerComcenStatus",
   "meaning": "unitStunTable[99]; low nibble = damaged comcen systems",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "comcen99StunState",
   "meaning": "$F960 + $63: the stun/dug-in byte of unit 99 (the AI comcen); low nibble non-zero = stunned",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  }
 ],
 "F9F5": [
  {
   "name": "humanComcenEnergy",
   "meaning": "unitEnergy[49]; bits 0-5 energy, bit 7 = spotted. The AI tests bit 7 to know whether it has located the human comcen",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "comcen49Visibility",
   "meaning": "$F9C4 + $31: the energy/visibility byte of unit 49 (the human comcen); bit 7 set = currently visible (set by ageUnitVisibility $3576)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  }
 ],
 "FA27": [
  {
   "name": "trainerComcenEnergy",
   "meaning": "unitEnergy[99]; bits 0-5 are the AI comcen's energy, used as the AI's per-round action budget",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "comcen99Visibility",
   "meaning": "$F9C4 + $63: the energy/visibility byte of unit 99 (the AI comcen); bits 0-1 are used as a small jitter in the anti-drone decision",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  }
 ],
 "FBB7": [
  {
   "name": "trainerComcenLocalFlags",
   "meaning": "unitLocalFlags[99]; bit 1 is set by recordUnitHit when the comcen takes a hit",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E000",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiComcenStatusFlags",
   "meaning": "unitStatusFlags[99] ($FB54 + 99); set to $20 for the FULL WAR/DEFENDER plans.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8897": [
  {
   "name": "aiCmdParam0",
   "meaning": "first parameter of the staged command (unit index for $80/$81/$9A)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "8898": [
  {
   "name": "aiCmdParam1",
   "meaning": "second parameter (mirrored column for $80/$81, energy amount for $9A)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "8899": [
  {
   "name": "aiCmdParam2",
   "meaning": "third parameter (mirrored row for $80/$81)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "8957": [
  {
   "name": "groupStrengthHi",
   "meaning": "high byte of groupStrength",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "8967": [
  {
   "name": "comcenActionBudget",
   "meaning": "number of command slots left for the AI comcen this round (3 - aiCommandsThisRound)",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiGroupLoopIndex",
   "meaning": "Down-counter over the AI groups while initTrainerAi hands out the opening destinations.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8972": [
  {
   "name": "comcenAttackerUnit",
   "meaning": "index of the first human combat unit seen firing at the AI comcen ($FF = none); used by the drone/missile code at $EDF6",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "missileTargetUnit",
   "meaning": "Unit index the AI is currently shooting missiles at, $FF = none",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiDroneTargetCandidate",
   "meaning": "Unit index the AI is considering for a drone strike, $FF = none; initialised to $FF here.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8979": [
  {
   "name": "strikeScoreThreshold",
   "meaning": "minimum block score a bombardment target must beat; reduced by 25 percent after each successful pick ($EFBA)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "droneTargetValueThreshold",
   "meaning": "Minimum 2x2 cluster value a drone target must reach; multiplied by 0.75 every time a target is accepted, so the AI becomes less selective as the game goes on. Initialised by the track 29 $EC00 setup overlay ($EC3F/$ECCF there)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiDroneScoreThresholdWork",
   "meaning": "Working copy of aiDroneScoreThreshold that the drone code decays to 3/4 when no good target is found ($EFC1).",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "EA45": [
  {
   "name": "aiCmdQueuePeak",
   "meaning": "high-water mark of the AI command queue length; written and compared only inside queueAiCommand and never read anywhere else - a leftover statistic",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_E68A",
   "unit": "game/trainerAiE000"
  }
 ],
 "896F": [
  {
   "name": "lastDroneHeading",
   "meaning": "Copy of droneHeading $920B taken at the end of every AI missile turn; if the drone has turned since then the anti-drone shot gets a $40 accuracy penalty. Also overwritten with the heading byte the AI puts in its own $8D launch command",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiDroneHeading",
   "meaning": "Current heading (0-7) of the AI's drone.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "aiDroneHeading",
   "meaning": "clockwise heading 0-7 (0 N, 1 NE, 2 E, ... 7 NW) of the trainer AI's drone; the AI's counterpart of the player's droneHeading $920B. Turned one step per exchange by steerDroneAndEmitMove.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "8970": [
  {
   "name": "missileReloadTimer",
   "meaning": "Counts down once per exchange; while it is not negative the AI may not fire another missile",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiDroneHoldTimer",
   "meaning": "Counter decremented by the resident AI while a drone run is in progress ($EE10/$EE29); cleared here.",
   "scope": "abs",
   "confidence": "low",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8973": [
  {
   "name": "aiDroneStepsLeft",
   "meaning": "Set to $64 (100) when the AI launches a drone; the track 29 $EE00 drone overlay counts it down and emits $8F (drone released) when it runs out",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiDroneFuel",
   "meaning": "flight steps left for the AI's drone; set to $64 (100, the same as the player's drone fuel display) when the drone is launched by the module's own $EE00 image, decremented once per exchange here; going negative sends command $8F (drone released).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "8974": [
  {
   "name": "aiDroneColumn",
   "meaning": "Current/launch map column of the AI's drone (initialised to the AI comcen column)",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiDroneCol",
   "meaning": "Current column of the AI's drone.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "aiDroneCol",
   "meaning": "map column of the AI's drone (may go negative or past 39 - nothing clamps it).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "8975": [
  {
   "name": "aiDroneRow",
   "meaning": "Current/launch map row of the AI's drone",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  },
  {
   "name": "aiDroneRow",
   "meaning": "Current row of the AI's drone.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "aiDroneRow",
   "meaning": "map row of the AI's drone.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "92AF": [
  {
   "name": "dronesLeftOpponent",
   "meaning": "Drones the computer opponent has left; chooseAiDroneTarget returns immediately when it is 0",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  }
 ],
 "92B1": [
  {
   "name": "missilesLeftOpponent",
   "meaning": "Missiles the computer opponent has left",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_E000_T35_EC82",
   "unit": "game/trainerAiE000"
  }
 ],
 "8863": [
  {
   "name": "aiComcenGroup",
   "meaning": "aiUnitGroup[49] = group of unit 99, the AI comcen; forced to 0 on the THE BOMB path ($EC60).",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "8978": [
  {
   "name": "aiDroneScoreThreshold",
   "meaning": "Score a candidate cell must beat before the AI commits its drone; $FF (never) for plans 0/1, 8 or 12 otherwise, from aiPlanParamTable.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "aiDroneDetonateThreshold",
   "meaning": "minimum scoreTargetBlock value at which the AI detonates its drone; loaded at $ECCC from the 5-byte skill table at $EC0A ($FF,$FF,$08,$0C,$0C indexed by the play/skill level $8966), so at the two easiest levels the drone only detonates when it actually reaches its target cell.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EE00_T29_EE00",
   "unit": "game/droneAiEE00"
  }
 ],
 "0478": [
  {
   "name": "aiGroupDestRowHigh",
   "meaning": "12 high bytes of the destination-row sums.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "statAccumHi1",
   "meaning": "12-entry high-byte half of the second 16-bit statistics accumulator (low bytes $88DE), cleared at $ED98 and incremented at $EE0B",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "048C": [
  {
   "name": "aiGroupCentreColHigh",
   "meaning": "12 high bytes of the current-column sums.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  },
  {
   "name": "statAccumHi2",
   "meaning": "12-entry high-byte half of the third statistics accumulator (low bytes $88EA), $ED9B/$EE1F",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_table_0400_0400",
   "unit": "game/scratchAndMessages0400"
  }
 ],
 "F672": [
  {
   "name": "aiUnitCol",
   "meaning": "unitCol + 50: current column of AI unit 50+i.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "F6D6": [
  {
   "name": "aiUnitRow",
   "meaning": "unitRow + 50: current row of AI unit 50+i.",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "F76B": [
  {
   "name": "aiComcenFlags",
   "meaning": "unitFlagsTable[99]; set to $21 (side bit 5 + facing 1) for the FULL WAR/DEFENDER plans.",
   "scope": "abs",
   "confidence": "medium",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "4C": [
  {
   "name": "templateRowPtr",
   "meaning": "Pointer to the plan's 50 destination rows (templateGroupPtr + 100).",
   "scope": "zp",
   "confidence": "high",
   "chunk": "game_ovl_EC00_T29_EC00",
   "unit": "game/trainerSetupEC00"
  }
 ],
 "02A2": [
  {
   "name": "bamDiskIdLo",
   "meaning": "second use of this page: when checkDiskId ($0FB1) reads the BAM (track 18 sector 0) into $0200 this byte is BAM offset $A2, the first disk-id character ('O' for the game disk, 'E' for a data disk). It is contourRuleTable index $A2 the rest of the time",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ],
 "02A3": [
  {
   "name": "bamDiskIdHi",
   "meaning": "BAM offset $A3, the second disk-id character ('Z' / 'A'), compared by checkDiskId at $0FE0",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ],
 "02AB": [
  {
   "name": "bamDiskSerial",
   "meaning": "BAM offset $AB, 10 bytes copied to $FFDE-$FFE7 at $0FF3 for the game disk in modem mode; used by the link protocol as the disk serial",
   "scope": "abs",
   "confidence": "high",
   "chunk": "game_setup_0200_0200",
   "unit": "game/tileRules0200"
  }
 ]
}