{
 "_comment": "generated by tools/mergeSurvey.py from survey/*.json - do not edit by hand",
 "notes": {
  "EC0F": {
   "routine": [
    "initTrainerAi - Only entry point of this sub-overlay; called once from overlay A at $7B30 right after the overlay was loaded (T29 s0-3) and just before $7B33 restores the comm tail. It builds the whole opening plan of the solo-trainer computer opponent (side 1, units 50-99): clears the AI work area in the $8800 block, picks an objective near the human comcen (sub_E12F), selects a plan index 0-4 from the game type ($0BA3 & 7), loads the 5 x 150 byte deployment template from track 29 s6-8 into $0200-$04FF, copies the plan's 50 group ids to $8832 and its 50 destination cells to unitDestCol+50/unitDestRow+50, gives every AI group a random destination, then calls rebuildAiGroupTables and computes the phase-change clock $896E.",
    "In:  $0BA3 game type bits 0-2, $91CB game clock (set by setGameClockForType at $7B1A), $92AC zone-score setting, unit arrays $F640/$F6A4/$F708/$F76C/$F7D0/$F834, tables at $EC00/$EC05/$EC0A, disk (loadSectors/setLoadDest), nextGameRandom",
    "Out: $0200-$04FF overwritten with the deployment template; AI work area $8800-$8979 initialised; unitDestCol/unitDestRow of units 50-99 set; unitFlagsTable[99]=$21 and unitStatusFlags[99]=$20 for plans 4; $F7D0[99] bits 6-7 cleared for THE BOMB; $E01E=0; one $A5 00 $14 setup command queued for DEFENDER; $91CB restored to $FF; zp_18-zp_2B and zp_48-zp_4D clobbered"
   ],
   "unit": "game/trainerSetupEC00"
  },
  "ED8E": {
   "routine": [
    "rebuildAiGroupTables - Recomputes the per-group bookkeeping of the AI's 12 groups. First calls buildAiHeadingSearchOrder, then zeroes the 12-entry group arrays ($88D2/$88DE/$88EA/$88F6/$8902 and their high-byte halves at $0464/$0478/$048C/$04A0) and gives every group a random start delay 0-15 in $893E. It then walks the 50 AI units, drops dead ones ($8832=$FF), accumulates each unit's destination and current cell into its group's 16-bit sums, sets the per-unit zone flag $8864, stores the unit's activation clock in $8800 (game clock minus the group delay, except group 0 and the guard group), and marks a unit that already stands on its destination by setting bit 7 of unitDestCol. Finally it divides all four sums by the member count so $88D2/$88DE hold the group's average destination and $88EA/$88F6 its average position.",
    "In:  $8832 group ids, $8864, $8953 highest group, $8964 guard group, $8966 plan index, $91CB clock, $92AC, unit arrays $F672/$F6D6 (col/row of units 50-99), $F802/$F866 (dest col/row), $F79E (types)",
    "Out: $0464/$0478/$048C/$04A0 (12 bytes each), $88D2/$88DE/$88EA/$88F6/$8902/$893E, $8800-$8831 activation clocks, $8864-$8895 flags, bit 7 of $F802 entries; zp_18/zp_1A/zp_1B/zp_1C/zp_22/zp_24/zp_27/zp_28 clobbered"
   ],
   "unit": "game/trainerSetupEC00"
  },
  "EEC3": {
   "routine": [
    "buildAiHeadingSearchOrder - Fills the 8-byte table at $894A with heading offsets 0, +/-1, +/-1, +/-2, +/-2, +/-3, +/-3, +/-4 taken mod 8, the sign of each pair being chosen at random. The resident AI walks this table to try headings in order of increasing deviation from the wanted direction, with a random left/right bias per game.",
    "In:  nextGameRandom",
    "Out: $894A-$8951; zp_18 scratch, A/Y clobbered",
    "(confidence: medium)"
   ],
   "unit": "game/trainerSetupEC00"
  },
  "EEED": {
   "routine": [
    "orderAiGroupToPoint - Orders AI group Y to move so that its formation centre ends up at column X, row A. Patches the two immediate operands at $EEF9/$EEFD with X and A, calls sub_E531 to measure the group (returns C=1 and does nothing when the group is empty), stores the point in zp_22/zp_24 and falls into shiftAiGroupDestinations. Called from initTrainerAi to make group 0 follow the main group and to send the guard group to the AI comcen.",
    "In:  X = target column, A = target row, Y = group index, $8832 group ids",
    "Out: self-modifies $EEF9/$EEFD; group destination arrays and unitDestCol/unitDestRow of the group's units updated; C=1 (untouched) when the group has no live unit"
   ],
   "unit": "game/trainerSetupEC00"
  },
  "EF00": {
   "routine": [
    "shiftAiGroupDestinations - Tail of orderAiGroupToPoint, also called directly at $ED3F. Calls sub_E5FF, which clips the wanted point (zp_22/zp_24) so the group's bounding box stays on the 40x40 map, stores the result as the group destination ($88D2/$88DE) and returns the delta from the group's current centre in zp_2A/zp_2B. It then adds that delta to unitDestCol/unitDestRow of every unit whose group id matches $8955, so the whole group translates while keeping its formation (the AND #$3F also clears the idle/mirror bits).",
    "In:  zp_22/zp_24 wanted centre, $8955 current group (set by sub_E531), $8832, $F802/$F866",
    "Out: $88D2/$88DE for the group, unitDestCol+50/unitDestRow+50 of its members, zp_2A/zp_2B"
   ],
   "unit": "game/trainerSetupEC00"
  },
  "EF27": {
   "routine": [
    "stepAiDroneAndQueueMove - Drone-steering step of the trainer AI, shown as .byte in this file. Turns the drone heading $896F by X steps (carry set forces -1), looks the heading up in headingToDirectionTable ($7316 in the $6F00 overlay) and converts it through directionDeltaTable ($20F0) / diagonalDirectionTable ($1FB2) into dx/dy, advances the drone position $8974/$8975 by one cell, then fabricates a 4-byte command $8D (drone move: heading+4 mod 8, column, row) in the comm module's INCOMING packet buffer $E020-$E023 and sets $E01E = 4, i.e. the trainer feeds the engine a packet as if the remote player had sent it. Coordinates are mirrored with sub_EA38 first. These bytes are identical to $EF27 in game/ovl_EE00_T29, where the routine is live; in this overlay nothing reaches it, so here it is a dead copy.",
    "In:  X = heading change, C = 1 forces -1, $896F heading, $8974/$8975 drone column/row",
    "Out: $896F, $8974/$8975 advanced, $E020=$8D, $E021=heading+4, $E022/$E023 = mirrored column/row, $E01E=4",
    "(confidence: medium)"
   ],
   "unit": "game/trainerSetupEC00"
  },
  "EF9E": {
   "routine": [
    "measureDroneDistanceToTarget - Copies the drone position $8974/$8975 into zp_27/zp_28 and jumps into sub_E82A, which returns the distance bucket (0-$10, via distanceSquared and squaresTable) between the cell in zp_22/zp_24 and the drone. Shown as .byte here; live in game/ovl_EE00_T29 where it is called from $EE26 and $EEFC.",
    "In:  zp_22/zp_24 target cell, $8974/$8975 drone position",
    "Out: A = distance bucket, zp_27/zp_28 set, X clobbered"
   ],
   "unit": "game/trainerSetupEC00"
  },
  "EFAB": {
   "routine": [
    "decayDroneScoreThreshold - 34-byte code fragment (shown as .byte) shared byte for byte with the $EE00/$EC00 tails of the other builds: increments zp_1F, loops back to $EF9B while it is below $0F, then stores zp_1D into $8976 and reduces $8979 to three quarters of itself. In the T35 comm build the loop entry $EF9B is a real instruction; with this file's instruction alignment the BCC at $EFB1 lands inside the operand of the STA $E01E at $EF9A, so the fragment is stale code left over from the shared source module and is not usable here.",
    "In:  zp_1D, zp_1F, $8979",
    "Out: $8976, $8979 (x 3/4), zp_18",
    "(confidence: low)"
   ],
   "unit": "game/trainerSetupEC00"
  },
  "EC00": {
   "unit": "game/trainerSetupEC00",
   "block": [
    "mainGroupByPlanTable: byteTable, 5 bytes. Per AI plan 0-4: index of the main/spearhead group ($FF, $FF, 4, 4, 4) stored in $895D."
   ]
  },
  "EC05": {
   "unit": "game/trainerSetupEC00",
   "block": [
    "groupCountByPlanTable: byteTable, 5 bytes. Per AI plan 0-4: highest group index ($0A, $0A, 4, 4, 4) stored in $8953; $8954 gets this value + 1."
   ]
  },
  "EC0A": {
   "unit": "game/trainerSetupEC00",
   "block": [
    "aiPlanParamTable: byteTable, 5 bytes. Per AI plan 0-4: drone score threshold ($FF, $FF, 8, $0C, $0C) copied to both $8978 and $8979."
   ]
  },
  "EFCD": {
   "unit": "game/trainerSetupEC00",
   "block": [
    "msgSpaceTailFragment: text, 3 bytes. 'CE' + '.'|$80 - the tail of the string 'AND PRESS SPACE.' that the T34 comm tail keeps at this address; only the last three bytes survive in this variant, which is why the shared block starts here."
   ]
  },
  "EFD0": {
   "unit": "game/trainerSetupEC00",
   "block": [
    "msgDataGameCustomOff: text, 18 bytes. 'DATAGAMECUSTOM OF'+'F'|$80; almost certainly addressed at two offsets ('DATAGAME' at $EFD0 and 'CUSTOM OFF' at $EFD8), only the final F carries the bit-7 terminator."
   ]
  },
  "EFE2": {
   "unit": "game/trainerSetupEC00",
   "block": [
    "msgDestroyOff: text, 11 bytes. 'DESTROY OF'+'F'|$80."
   ]
  },
  "EFED": {
   "unit": "game/trainerSetupEC00",
   "block": [
    "msgAccepted: text, 8 bytes. 'ACCEPTE'+'D'|$80."
   ]
  },
  "EFF5": {
   "unit": "game/trainerSetupEC00",
   "block": [
    "overlayTailPadding_EFF5: byteTable, 11 bytes. $C0,$DF then four $00,$FF pairs and a trailing byte ($FF here, $00 in the T34 tail). Purpose unknown; the same bytes end every $EC00/$EE00 overlay variant."
   ]
  }
 }
}