2023 lines
No EOL
63 KiB
JSON
2023 lines
No EOL
63 KiB
JSON
{
|
|
"chunk": "game_trainerSetupEC00_EC00",
|
|
"unit": "game/trainerSetupEC00",
|
|
"unitLabels": {
|
|
"game/trainerSetupEC00": {
|
|
"EC54": "checkForTheBombGameType",
|
|
"EC91": "usePlanFour",
|
|
"ECD4": "buildPlanRecordPointers",
|
|
"ECF3": "copyPlanRecordLoop",
|
|
"ED32": "rollGroupTargetRowJitter",
|
|
"EE4A": "checkGroupStartsAtOnce",
|
|
"EE55": "applyGroupStartDelay",
|
|
"EEB9": "storeGroupCentreRow",
|
|
"EEDD": "storeHeadingOffset",
|
|
"EF10": "stripIdleBitFromDestCol",
|
|
"EF27": "staleDroneSteerTail",
|
|
"EFAB": "staleDroneTargetPickerTail",
|
|
"EFD0": "msgDataDiskName",
|
|
"EFD4": "msgGameDiskName",
|
|
"EFD8": "msgCustomOff",
|
|
"EFF5": "overlayPageTailBytes"
|
|
}
|
|
},
|
|
"notes": {
|
|
"EC00": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"Three parallel 5-byte tables indexed by the AI plan number 0-4 that initTrainerAi derives",
|
|
"from the game type and keeps in $8966. They are read together at $ECB9-$ECD1."
|
|
],
|
|
"line": "plan 0-4 -> spearhead group id: none for the two 11-group plans, group 4 for the 5-group plans"
|
|
},
|
|
"EC05": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "plan 0-4 -> highest group index: 10 (11 groups) for plans 0/1, 4 (5 groups) for plans 2-4"
|
|
},
|
|
"EC0A": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "plan 0-4 -> drone detonation score threshold: $FF (unreachable score = never detonate) for plans 0/1, 8 or 12 for plans 2-4"
|
|
},
|
|
"EC0F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"routine": [
|
|
"initTrainerAi - Builds the whole opening plan of the solo trainer (the computer opponent, side 1 =",
|
|
"units 50-99). Clears the AI's scalar work area in the $8800 block, picks a first objective near the",
|
|
"human comcen, turns the game type ($0BA3 bits 0-2) into a plan number 0-4, loads the five 150-byte",
|
|
"opening plays from track 29 sectors 6-8 into $0200-$04FF, copies the chosen play's 50 group ids and",
|
|
"50 destination cells into $8832 and unitDestCol/unitDestRow of units 50-99, re-aims every group at a",
|
|
"random point for the bigger plans, rebuilds the per-group tables and works out the round at which the",
|
|
"AI changes gear ($896E).",
|
|
"In: $0BA3 game type bits 0-2; $91CB game clock (the game length in rounds, put there by",
|
|
" setGameClockForType at $7B1A); $92AC TERRAIN PTS setting of side 1; unit arrays unitFlagsTable",
|
|
" $F708, unitTypeTable $F76C, unitDestColTable $F7D0, unitDestRowTable $F834; the three plan",
|
|
" tables at $EC00/$EC05/$EC0A; the disk, through setLoadDest/loadSectors; nextGameRandom",
|
|
"Out: $0200-$04FF overwritten with the playbook; the AI work area $8800-$8979 initialised; group ids",
|
|
" $8832-$8863 and unitDestCol/unitDestRow of units 50-99 set; unitFlagsTable[99]=$21 and",
|
|
" unitDisplayFlags[99]=$20 for plans 4 (FULL WAR / DEFENDER); unitDestCol[99] bits 6-7 cleared for",
|
|
" THE BOMB; $E01E=0; one $A5,00,$14 setup command queued for DEFENDER; $91CB left at $FF;",
|
|
" zp_18-zp_2B and zp_48-zp_4D clobbered",
|
|
"Called from: $7B30 in game/mapGenerator6F00 - the only call, made once, right after $7B24 loads this",
|
|
" overlay (track 29 sectors 0-3) and immediately before $7B33 puts the modem-driver tail (track 34",
|
|
" sectors 12-15) back over it. Everything in this file therefore runs exactly once, in a solo game"
|
|
],
|
|
"block": [
|
|
"Clear the trainer's scalar variables. The whole $8800-$89FF page pair still holds the modem",
|
|
"status strings loaded from track 29 s9-10, which are dead in a solo game, so every AI variable",
|
|
"has to be written before it can be read. X = 0 for the flags and counters, then X = $FF for the",
|
|
"index variables whose 'none' value is $FF."
|
|
],
|
|
"line": "X = 0: the value for every counter and flag cleared below"
|
|
},
|
|
"EC11": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "no fake incoming packet is pending yet (the comm module's rx packet length)"
|
|
},
|
|
"EC14": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "'human comcen has been spotted' flag = 0; randomiseCoordinate ($E14A) widens its aim while this is clear"
|
|
},
|
|
"EC17": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "AI command-queue length = 0: the queue at $88A0 is empty"
|
|
},
|
|
"EC1A": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "safest compass direction from the last threat sweep round the AI comcen = 0"
|
|
},
|
|
"EC1D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "rounds left before the AI re-picks its rally point = 0, so it re-picks on the first round"
|
|
},
|
|
"EC20": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "missile-pass scratch of the resident $EC00 tail = 0"
|
|
},
|
|
"EC23": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "commands queued by the AI this round = 0"
|
|
},
|
|
"EC26": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "drone state = 0: no drone airborne and the $EE00 drone-flying overlay is not resident ($FF marks it resident)"
|
|
},
|
|
"EC29": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "previous-round 'AI comcen destroyed' latch = 0"
|
|
},
|
|
"EC2C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "'AI comcen destroyed' flag tested by planTrainerRound = 0"
|
|
},
|
|
"EC2F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = $FF: the 'none' value for the index variables that follow"
|
|
},
|
|
"EC30": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "no spearhead group yet for the AI comcen to escort"
|
|
},
|
|
"EC33": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "no drone target unit picked"
|
|
},
|
|
"EC36": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "no player unit is being hunted for having shot at the AI comcen"
|
|
},
|
|
"EC39": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "no home-defence group yet; a real one is chosen at $EC9C unless the plan is QB SNEAK"
|
|
},
|
|
"EC3C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "drone detonation score threshold = $FF (never); replaced from aiPlanParamTable at $ECCC"
|
|
},
|
|
"EC3F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "and its decaying working copy = $FF"
|
|
},
|
|
"EC42": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "comcen threat threshold"
|
|
},
|
|
"EC44": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the resident AI only reacts to pressure on its own comcen once the measured threat reaches 5 ($E29A)"
|
|
},
|
|
"EC47": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "really trainerAiE000:pickTrainerRallyPoint ($E12F) - the modem driver's name for this address is a collision; it stores a random cell near the human comcen in $8969/$896A as the AI's first objective"
|
|
},
|
|
"EC4A": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"Turn the game type into an AI plan number 0-4 (kept in $8966):",
|
|
" 0 SCRIMAGE -> plan 0, 1 QB SNEAK -> plan 1 (no home-defence group),",
|
|
" 2 THE BOMB -> plan 0 with no playbook at all (only the two comcens exist),",
|
|
" 3 FACE-OFF -> plan 2, 4 SLUGGERS -> plan 3, 5 FULL WAR -> plan 4,",
|
|
" 6 DEFENDER -> plan 4 plus a queued point award for the defending computer."
|
|
],
|
|
"line": "the game options byte written by the setup screens"
|
|
},
|
|
"EC4D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "bits 0-2 = game type 0 SCRIMAGE, 1 QB SNEAK, 2 THE BOMB, 3 FACE-OFF, 4 SLUGGERS, 5 FULL WAR, 6 DEFENDER"
|
|
},
|
|
"EC4F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "Y = game type"
|
|
},
|
|
"EC50": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "is the game type below 2?"
|
|
},
|
|
"EC52": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "SCRIMAGE and QB SNEAK use the plan number that equals the game type"
|
|
},
|
|
"EC54": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "branch if the game type is 3 or higher; fall through only for game type 2 = THE BOMB"
|
|
},
|
|
"EC56": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"THE BOMB: both sides own nothing but their comcen, so there is no formation to deploy.",
|
|
"Free the AI comcen to move and pretend it is the whole of group 0."
|
|
],
|
|
"line": "ordered destination column of the AI comcen, unitDestCol[99]"
|
|
},
|
|
"EC59": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "keep the 0-39 column only, clearing the idle bit 7 so the engine treats the comcen as still moving"
|
|
},
|
|
"EC5B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "store it back"
|
|
},
|
|
"EC5E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "group 0"
|
|
},
|
|
"EC60": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "group id of AI slot 49 = unit 99, the AI comcen: the only live AI unit in THE BOMB"
|
|
},
|
|
"EC63": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "highest group index in use = 0"
|
|
},
|
|
"EC66": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "AI plan number = 0"
|
|
},
|
|
"EC69": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "skip the playbook load and the group scatter entirely and go straight to the table rebuild"
|
|
},
|
|
"EC6C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"Game types 3-6. Y is biased down by one so that FACE-OFF and SLUGGERS map straight onto",
|
|
"plans 2 and 3, while FULL WAR and DEFENDER both end up on plan 4."
|
|
],
|
|
"line": "Y = gameType - 1: FACE-OFF -> 2, SLUGGERS -> 3, FULL WAR -> 4, DEFENDER -> 5"
|
|
},
|
|
"EC6D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "is the biased type below 4?"
|
|
},
|
|
"EC6F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "FACE-OFF -> plan 2, SLUGGERS -> plan 3"
|
|
},
|
|
"EC71": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "FULL WAR (biased type 4) -> plan 4, without the extra point award"
|
|
},
|
|
"EC73": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"DEFENDER only: stage command $A5 (cmdSetupOption, $55A5) with kind 0 and value 20 and queue it",
|
|
"as if the remote player had sent it, so the handler adds 20 x 10 = 200 purchase points to the",
|
|
"AI's side score - the handicap that pays for the defending computer's extra force."
|
|
],
|
|
"line": "command byte $A5 = cmdSetupOption"
|
|
},
|
|
"EC75": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "byte 0 of the AI's 3-byte command scratch"
|
|
},
|
|
"EC78": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "argument 1 = kind 0: 'add points to the sender's side'"
|
|
},
|
|
"EC7A": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "byte 1 of the command scratch; the msg... label here is a $8800 message-string address collision"
|
|
},
|
|
"EC7D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "argument 2 = 20 points"
|
|
},
|
|
"EC7F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "byte 2 of the command scratch"
|
|
},
|
|
"EC82": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "append the staged 3-byte command to the AI's outgoing queue at $88A0"
|
|
},
|
|
"EC85": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "undo the 'commands queued this round' bump queueAiCommand made: this is a setup option, not a battle order"
|
|
},
|
|
"EC88": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"FULL WAR and DEFENDER: reset the AI comcen's own flags. unitDisplayFlags mirrors bits 2-6 of",
|
|
"unitFlagsTable, so $21/$20 means facing 1, side bit 5 set (side 1), no group membership, and",
|
|
"the idle bit 7 clear - i.e. the comcen counts as still moving (probably so it relocates during",
|
|
"these two long games)."
|
|
],
|
|
"line": "$21 = facing 1 (bits 0-1), group id 0 (bits 2-4), side 1 (bit 5), not in a group (bit 6 clear)"
|
|
},
|
|
"EC8A": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "unitFlagsTable[99] - the AI comcen"
|
|
},
|
|
"EC8D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$20 = the same value with the facing bits stripped, which is what the engine mirrors into the display flags"
|
|
},
|
|
"EC8E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "unitDisplayFlags[99]: bit 7 (arrived/idle) clear, bit 6 (group order active) clear"
|
|
},
|
|
"EC91": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "FULL WAR and DEFENDER both use plan 4"
|
|
},
|
|
"EC93": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"Record the plan and choose the home-defence group: a random group 1, 2 or 3 that will be parked",
|
|
"on the AI comcen instead of joining the attack. QB SNEAK deliberately keeps none back."
|
|
],
|
|
"line": "remember the plan number 0-4 for the rest of the setup and for the resident AI"
|
|
},
|
|
"EC96": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$FF = 'no home-defence group'"
|
|
},
|
|
"EC98": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "is this plan 1 (QB SNEAK)?"
|
|
},
|
|
"EC9A": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "QB SNEAK throws everything forward: leave the guard group as $FF"
|
|
},
|
|
"EC9C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "roll for a guard group"
|
|
},
|
|
"EC9F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "keep the low two bits: 0-3"
|
|
},
|
|
"ECA1": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "reject 0 - group 0 is the one that follows the spearhead, so it is never the guard; re-roll"
|
|
},
|
|
"ECA3": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "home-defence group id 1-3"
|
|
},
|
|
"ECA6": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"Load the AI playbook: track 29 sectors 6-8 -> $0200-$04FF, over the terrain tile rules (which the",
|
|
"map generator has already finished with) and the copy-protection answer table. The 768 bytes hold",
|
|
"five 150-byte records, one per plan: 50 group ids, then 50 destination columns, then 50 destination",
|
|
"rows, all indexed by AI slot 0-49 = unit 50-99. The caller has already bumped diskLoadDepth ($7B17)."
|
|
],
|
|
"line": "destination low byte = $00"
|
|
},
|
|
"ECA8": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "destination high byte = $02, i.e. load to $0200"
|
|
},
|
|
"ECAA": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "tell the fast loader where the next sectors go"
|
|
},
|
|
"ECAD": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "track 29"
|
|
},
|
|
"ECAF": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "first sector 6"
|
|
},
|
|
"ECB1": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "three sectors = 768 bytes = the five opening plays"
|
|
},
|
|
"ECB3": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "pull them in over $0200-$04FF"
|
|
},
|
|
"ECB6": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"Copy the plan's fixed parameters out of the three tables at the top of this overlay."
|
|
],
|
|
"line": "Y = plan number, the index into all three 5-byte tables (and still in Y when multiply8x8 is called at $ECD4)"
|
|
},
|
|
"ECB9": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "spearhead group for this plan"
|
|
},
|
|
"ECBC": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the group the AI comcen escorts and that group 0 is told to follow; $FF for plans 0/1"
|
|
},
|
|
"ECBF": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "highest group index used by this plan"
|
|
},
|
|
"ECC2": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "store it"
|
|
},
|
|
"ECC5": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "+1"
|
|
},
|
|
"ECC6": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "number of groups (11 or 5), the loop limit the resident AI uses at $E6A5/$E960/$EB3C"
|
|
},
|
|
"ECC9": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "drone detonation score threshold for this plan"
|
|
},
|
|
"ECCC": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the fixed threshold"
|
|
},
|
|
"ECCF": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "and the working copy that the drone code decays to 3/4 as it gets desperate"
|
|
},
|
|
"ECD2": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"Point three zero-page pointers at the three 50-byte arrays of the selected 150-byte plan record:",
|
|
" mapCellPtr ($48) -> group ids, fillEndPtr ($4A) -> destination columns, planRowPtr ($4C) -> rows.",
|
|
"Base = $0200 + 150 * planNumber."
|
|
],
|
|
"line": "150 = the size of one plan record"
|
|
},
|
|
"ECD4": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "A x Y = 150 x plan number; returns the product low byte in A and the high byte in Y"
|
|
},
|
|
"ECD7": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "low byte of the record's offset into the playbook"
|
|
},
|
|
"ECD9": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "add $100 ..."
|
|
},
|
|
"ECDA": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... and another $100, because the playbook was loaded at $0200"
|
|
},
|
|
"ECDB": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "mapCellPtr now points at the record's 50 group ids"
|
|
},
|
|
"ECDD": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clear carry for the add below"
|
|
},
|
|
"ECDE": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "+50 bytes: past the group ids"
|
|
},
|
|
"ECE0": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "no page crossing"
|
|
},
|
|
"ECE2": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "carry into the high byte"
|
|
},
|
|
"ECE3": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "fillEndPtr points at the record's 50 destination columns"
|
|
},
|
|
"ECE5": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "high byte of that pointer"
|
|
},
|
|
"ECE7": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clear carry again"
|
|
},
|
|
"ECE8": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "+50 more bytes: past the destination columns"
|
|
},
|
|
"ECEA": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "no page crossing"
|
|
},
|
|
"ECEC": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "carry into the high byte"
|
|
},
|
|
"ECED": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "planRowPtr points at the record's 50 destination rows"
|
|
},
|
|
"ECEF": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "high byte of that pointer"
|
|
},
|
|
"ECF1": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"Copy the plan into the live arrays, AI slot 49 down to 0 (units 99 down to 50)."
|
|
],
|
|
"line": "49 = the last AI slot; the loop counts down to 0"
|
|
},
|
|
"ECF3": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "group id of this unit in the chosen opening play"
|
|
},
|
|
"ECF5": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$8832 + slot = the AI's own group-id array (the resident AI addresses it as $8800 + absolute unit index)"
|
|
},
|
|
"ECF8": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "destination column from the play"
|
|
},
|
|
"ECFA": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "unitDestCol[50 + slot]"
|
|
},
|
|
"ECFD": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "destination row from the play"
|
|
},
|
|
"ECFF": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "unitDestRow[50 + slot]"
|
|
},
|
|
"ED02": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "previous slot"
|
|
},
|
|
"ED03": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "loop until all 50 AI units have their group and destination"
|
|
},
|
|
"ED05": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "which plan is this?"
|
|
},
|
|
"ED08": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "plans 2, 3 and 4 re-aim every group; plans 0 and 1 keep the playbook's own destinations"
|
|
},
|
|
"ED0A": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "SCRIMAGE / QB SNEAK: nothing more to place, go and rebuild the tables"
|
|
},
|
|
"ED0C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"Plans 2-4 only: scatter the groups. Every group from the highest down to 1 is given a random",
|
|
"target - any column 4-35, and a row in an 8-row band 7 rows above the group's own average ordered",
|
|
"row - and the whole formation is translated there. Group 0 is handled separately afterwards: it",
|
|
"is sent to the same point as the spearhead group."
|
|
],
|
|
"line": "highest group index of this plan"
|
|
},
|
|
"ED0F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "group loop counter, counting down to 0"
|
|
},
|
|
"ED12": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "Y = the group being placed"
|
|
},
|
|
"ED15": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "measure group Y ($E531): member count, combat value, bounding box, centre and average ordered cell; C=1 if the group is empty (then zp_1F below is stale, but the group has no members to move either)"
|
|
},
|
|
"ED18": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "roll a column"
|
|
},
|
|
"ED1B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "0-31 ..."
|
|
},
|
|
"ED1D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clear carry for the add below"
|
|
},
|
|
"ED1E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... + 4 = column 4-35, keeping the formation clear of both map edges"
|
|
},
|
|
"ED20": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "zp_22 = wanted formation centre column"
|
|
},
|
|
"ED22": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "and remember it as group Y's target column"
|
|
},
|
|
"ED25": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "zp_1F = the group's average ordered row, from computeTrainerGroupStats"
|
|
},
|
|
"ED27": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "is that row above 16?"
|
|
},
|
|
"ED29": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "no clamp needed"
|
|
},
|
|
"ED2B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "floor it at 15 so the band computed below cannot go negative"
|
|
},
|
|
"ED2D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "prepare to subtract"
|
|
},
|
|
"ED2E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "start the 8-row band 7 rows before the group's own row"
|
|
},
|
|
"ED30": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "base row of the band"
|
|
},
|
|
"ED32": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "roll a row offset"
|
|
},
|
|
"ED35": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "0-7 rows of jitter inside the band"
|
|
},
|
|
"ED37": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clear carry for the add"
|
|
},
|
|
"ED38": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "base + jitter"
|
|
},
|
|
"ED3A": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "zp_24 = wanted formation centre row"
|
|
},
|
|
"ED3C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "and remember it as group Y's target row"
|
|
},
|
|
"ED3F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clip the point to the map and shift every member of the group by the same delta"
|
|
},
|
|
"ED42": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "next group down"
|
|
},
|
|
"ED45": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "counter went negative: every group has been placed"
|
|
},
|
|
"ED47": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "still above group 0: keep scattering"
|
|
},
|
|
"ED49": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"The counter has reached group 0. Group 0 is the follow-up wave: instead of a random point it is",
|
|
"ordered to the spearhead group's target, so the two arrive together."
|
|
],
|
|
"line": "spearhead group id for this plan"
|
|
},
|
|
"ED4C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$FF = no spearhead (only possible for plans 0/1, which never get here): fall back to a random point"
|
|
},
|
|
"ED4E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the spearhead group's target column"
|
|
},
|
|
"ED51": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "and its target row"
|
|
},
|
|
"ED54": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "group 0"
|
|
},
|
|
"ED56": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "order group 0 to the same cell"
|
|
},
|
|
"ED59": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"Finally station the home-defence group on the AI comcen's own destination."
|
|
],
|
|
"line": "home-defence group id"
|
|
},
|
|
"ED5C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$FF: QB SNEAK and THE BOMB keep nobody back"
|
|
},
|
|
"ED5E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "AI comcen's ordered column, unitDestCol[99]"
|
|
},
|
|
"ED61": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "strip the idle bit 7 to leave a plain 0-39 column"
|
|
},
|
|
"ED63": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = target column, as orderAiGroupToPoint expects"
|
|
},
|
|
"ED64": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "AI comcen's ordered row, unitDestRow[99]"
|
|
},
|
|
"ED67": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "strip the idle bit"
|
|
},
|
|
"ED69": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "order the guard group (still in Y) onto the comcen"
|
|
},
|
|
"ED6C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"Common tail for every plan: rebuild the per-group bookkeeping, then decide when the AI changes",
|
|
"gear. gameClock counts DOWN from the game length, so subtracting 32-95 gives the clock value the",
|
|
"game will reach after that many rounds; the resident AI compares $91CB against $896E at $E3E5 and",
|
|
"$E765. For plans 0/1 the value is the full game length, which makes those comparisons true for the",
|
|
"whole game."
|
|
],
|
|
"line": "build the group averages, the per-unit activation clocks and the heading search order"
|
|
},
|
|
"ED6F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "roll the delay before the AI changes gear"
|
|
},
|
|
"ED72": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "0-63 ..."
|
|
},
|
|
"ED74": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... with bit 5 forced on: 32-95 rounds"
|
|
},
|
|
"ED76": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "keep it"
|
|
},
|
|
"ED78": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the game length in rounds (setGameClockForType at $7B1A left it here for us: 63/63/31/127/127/254/254 by game type)"
|
|
},
|
|
"ED7B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "which plan?"
|
|
},
|
|
"ED7E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "plans 0 and 1 ..."
|
|
},
|
|
"ED80": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... use the whole game length unchanged"
|
|
},
|
|
"ED82": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "prepare to subtract"
|
|
},
|
|
"ED83": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "plans 2-4: the switch happens once 32-95 rounds have been played"
|
|
},
|
|
"ED85": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the clock value at which the AI's second phase begins"
|
|
},
|
|
"ED88": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$FF = 'clock not running'"
|
|
},
|
|
"ED8A": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "put the game clock back the way the setup screen left it at $7AF2; the real countdown is loaded again at $7B86 once play starts"
|
|
},
|
|
"ED8D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "back to $7B33, which immediately reloads the modem-driver tail over this overlay"
|
|
},
|
|
"ED8E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"routine": [
|
|
"rebuildAiGroupTables - Recomputes all of the trainer's per-group and per-unit bookkeeping from the",
|
|
"current unit arrays. Clears the twelve group slots and gives each a random 0-15 round start delay,",
|
|
"then walks AI slots 49-0 accumulating each live unit's ordered destination and current cell into its",
|
|
"group's 16-bit sums, stamping the unit's activation clock, flagging units that already stand on their",
|
|
"destination, and dropping dead units out of their group. Finally it divides the four sums by the",
|
|
"member count so the group arrays hold averages: $88D2/$88DE = average ordered destination, $88EA/$88F6",
|
|
"= centre of the group as it stands now.",
|
|
"In: $8832 group id per AI slot, $8953 highest group, $8964 home-defence group, $8966 plan number,",
|
|
" $91CB game length, $92AC TERRAIN PTS of side 1, unitColTable+50 $F672, unitRowTable+50 $F6D6,",
|
|
" unitTypeTable+50 $F79E, unitDestColTable+50 $F802, unitDestRowTable+50 $F866, nextGameRandom",
|
|
"Out: group arrays $88D2/$88DE/$88EA/$88F6 (averages), $8902 member count, $893E start delay and the",
|
|
" matching sum high bytes at $0464/$0478/$048C/$04A0, 12 entries each; $8800-$8831 activation",
|
|
" clock per AI slot; $8864-$8895 zone flag per AI slot; $8832 entries of dead units set to $FF;",
|
|
" bit 7 of unitDestCol set for units already at their destination; zp_18/1A/1B/1C/22/24/27/28 used",
|
|
"Called from: $ED6C in initTrainerAi (this file) - the only caller in this overlay"
|
|
],
|
|
"block": [
|
|
"Clear the twelve group slots. The 16-bit sums are split: the low bytes live in the $8800 block",
|
|
"next to the other group arrays, the high bytes in the $0400 page (which held the copy-protection",
|
|
"answer table and the scan lists and is free once a game is running). The D_0464/D_0478/D_048C/",
|
|
"writeSectorToDisk labels are address collisions from the $0400 unit and mean nothing here."
|
|
],
|
|
"line": "refresh the random left/right heading search order at $894A"
|
|
},
|
|
"ED91": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "group slots 11 down to 0 (only 0..$8953 are really used, but all twelve are cleared)"
|
|
},
|
|
"ED93": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the value written into all nine group arrays below"
|
|
},
|
|
"ED95": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "high byte of the group's ordered-column sum"
|
|
},
|
|
"ED98": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "high byte of the ordered-row sum"
|
|
},
|
|
"ED9B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "high byte of the current-column sum"
|
|
},
|
|
"ED9E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$04A0: high byte of the current-row sum (the writeSectorToDisk label is a $0400-page collision)"
|
|
},
|
|
"EDA1": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "member count"
|
|
},
|
|
"EDA4": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "low byte of the ordered-column sum, which becomes the average"
|
|
},
|
|
"EDA7": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "low byte of the ordered-row sum"
|
|
},
|
|
"EDAA": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "low byte of the current-column sum"
|
|
},
|
|
"EDAD": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "low byte of the current-row sum"
|
|
},
|
|
"EDB0": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "roll this group's start delay"
|
|
},
|
|
"EDB3": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "0-15 rounds"
|
|
},
|
|
"EDB5": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the group waits that many rounds before it starts obeying its orders"
|
|
},
|
|
"EDB8": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "next group down"
|
|
},
|
|
"EDB9": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "loop over all twelve group slots"
|
|
},
|
|
"EDBB": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"Walk the 50 AI slots (unit 50+slot), 49 down to 0. Each live unit contributes to its group's four",
|
|
"running sums and gets its own activation clock and idle flag; each dead one is unhooked from its",
|
|
"group."
|
|
],
|
|
"line": "49 = the last AI slot"
|
|
},
|
|
"EDBD": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "zp_18 = the AI slot being processed"
|
|
},
|
|
"EDBF": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "Y = AI slot"
|
|
},
|
|
"EDC1": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$92AC = the TERRAIN PTS setting of side 1 (the AI), 0-3, set by the setup screens"
|
|
},
|
|
"EDC4": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "terrain points are switched on"
|
|
},
|
|
"EDC6": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "switched off: force the flag on for every unit"
|
|
},
|
|
"EDC8": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "with points on the value is 1-3, so bit 7 is clear and the flag ends up 0"
|
|
},
|
|
"EDCA": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "per-unit zone flag; computeTrainerGroupStats ($E579) counts the members whose bit 7 is CLEAR, i.e. the ones that still have a zone bonus to go and earn"
|
|
},
|
|
"EDCD": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "type byte of this AI unit"
|
|
},
|
|
"EDD0": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "bit 7 = dead; branch for a live unit"
|
|
},
|
|
"EDD2": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$FF = 'in no group'"
|
|
},
|
|
"EDD4": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "a dead unit is unhooked from its group so no later pass counts it"
|
|
},
|
|
"EDD7": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "leftover: A is never used, the jump target only decrements the slot counter"
|
|
},
|
|
"EDD9": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "on to the next AI slot"
|
|
},
|
|
"EDDC": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"Live unit: add its ordered destination and its current cell to its group's four 16-bit sums, and",
|
|
"leave both cells in the zero page for the cellDistance test further down."
|
|
],
|
|
"line": "X = the unit's group id"
|
|
},
|
|
"EDDF": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "one more member in that group"
|
|
},
|
|
"EDE2": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "ordered destination column"
|
|
},
|
|
"EDE5": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "strip the idle bit 7 to leave a plain 0-39 column"
|
|
},
|
|
"EDE7": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "zp_27 = point B for the cellDistance call at $EE5C"
|
|
},
|
|
"EDE9": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clear carry before the 16-bit add"
|
|
},
|
|
"EDEA": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "add into the group's ordered-column sum ..."
|
|
},
|
|
"EDED": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... low byte"
|
|
},
|
|
"EDF0": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "A = 0: only the carry is being added on"
|
|
},
|
|
"EDF2": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "propagate the carry ..."
|
|
},
|
|
"EDF5": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... into the high byte at $0464"
|
|
},
|
|
"EDF8": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "ordered destination row"
|
|
},
|
|
"EDFB": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "strip the idle bit"
|
|
},
|
|
"EDFD": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "zp_28 = row of point B"
|
|
},
|
|
"EDFF": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clear carry before the 16-bit add"
|
|
},
|
|
"EE00": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "add into the group's ordered-row sum ..."
|
|
},
|
|
"EE03": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... low byte"
|
|
},
|
|
"EE06": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "A = 0: only the carry is being added on"
|
|
},
|
|
"EE08": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "propagate the carry ..."
|
|
},
|
|
"EE0B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... into the high byte at $0478"
|
|
},
|
|
"EE0E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the unit's current column, unitCol[50 + slot]"
|
|
},
|
|
"EE11": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "zp_22 = point A for cellDistance"
|
|
},
|
|
"EE13": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clear carry before the 16-bit add"
|
|
},
|
|
"EE14": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "add into the group's current-column sum ..."
|
|
},
|
|
"EE17": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... low byte"
|
|
},
|
|
"EE1A": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "high byte at $048C ..."
|
|
},
|
|
"EE1D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... plus the carry"
|
|
},
|
|
"EE1F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "store it back"
|
|
},
|
|
"EE22": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the unit's current row, unitRow[50 + slot]"
|
|
},
|
|
"EE25": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "zp_24 = row of point A"
|
|
},
|
|
"EE27": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clear carry before the 16-bit add"
|
|
},
|
|
"EE28": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "add into the group's current-row sum ..."
|
|
},
|
|
"EE2B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... low byte"
|
|
},
|
|
"EE2E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "high byte at $04A0 ..."
|
|
},
|
|
"EE31": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... plus the carry"
|
|
},
|
|
"EE33": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "store it back"
|
|
},
|
|
"EE36": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"Stamp the unit's activation clock. The clock counts down, so subtracting the group's 0-15 round",
|
|
"delay produces the clock value at which this unit wakes up. SCRIMAGE (plan 0) starts everybody at",
|
|
"once, and so do group 0 and the home-defence group in every plan."
|
|
],
|
|
"line": "Y = 0 = 'stagger the groups'"
|
|
},
|
|
"EE38": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "which plan?"
|
|
},
|
|
"EE3B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "any plan but 0 staggers the start"
|
|
},
|
|
"EE3D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "plan 0 (SCRIMAGE): Y = $FF = 'no staggered start'"
|
|
},
|
|
"EE3E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "zp_1A holds the marker; only its bit 7 is tested"
|
|
},
|
|
"EE40": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "Y = the AI slot again"
|
|
},
|
|
"EE42": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = its group id"
|
|
},
|
|
"EE45": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the full game length in rounds - the clock value at the moment play starts"
|
|
},
|
|
"EE48": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "test bit 7 of the plan-0 marker without disturbing A"
|
|
},
|
|
"EE4A": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "plan 0: this unit is active from round one"
|
|
},
|
|
"EE4C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "is this the home-defence group?"
|
|
},
|
|
"EE4F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the guard group never waits - it has to be in place from the start"
|
|
},
|
|
"EE51": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "is this group 0?"
|
|
},
|
|
"EE53": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "group 0 follows the spearhead and never waits either"
|
|
},
|
|
"EE55": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "prepare to subtract"
|
|
},
|
|
"EE56": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "otherwise subtract the group's 0-15 round delay from the clock"
|
|
},
|
|
"EE59": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$8800 + slot = the clock value at which this unit starts obeying orders (the msg... label is a $8800 message-string address collision)"
|
|
},
|
|
"EE5C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"A unit that is already standing on its ordered destination gets the idle bit set, so the engine",
|
|
"will not spend movement points shuffling it."
|
|
],
|
|
"line": "distance between the unit's cell (zp_22/zp_24) and its ordered destination (zp_27/zp_28)"
|
|
},
|
|
"EE5F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "redundant - cellDistance already returns with the flags set from its own TXA"
|
|
},
|
|
"EE61": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "still has somewhere to go"
|
|
},
|
|
"EE63": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "its ordered column"
|
|
},
|
|
"EE66": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "set bit 7 = 'arrived / idle'"
|
|
},
|
|
"EE68": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "store it back"
|
|
},
|
|
"EE6B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "next AI slot down"
|
|
},
|
|
"EE6D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the counter went negative: all 50 slots done"
|
|
},
|
|
"EE6F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "round again (too far for a branch)"
|
|
},
|
|
"EE72": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"Turn the four 16-bit sums of every group 0..$8953 into averages by dividing by the member count.",
|
|
"A group with no members divides by zero here; the quotient is meaningless but the group has no",
|
|
"units for anything to act on either."
|
|
],
|
|
"line": "highest group index in use"
|
|
},
|
|
"EE75": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "zp_1B = the group being averaged, counting down to 0"
|
|
},
|
|
"EE77": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = group index"
|
|
},
|
|
"EE79": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "how many members it has"
|
|
},
|
|
"EE7C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "zp_1C = the divisor, reloaded before each of the four divisions"
|
|
},
|
|
"EE7E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "high byte of the ordered-column sum"
|
|
},
|
|
"EE81": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "low byte"
|
|
},
|
|
"EE84": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = divisor"
|
|
},
|
|
"EE86": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "Y:A / X"
|
|
},
|
|
"EE89": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = group index again"
|
|
},
|
|
"EE8B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the group's average ordered column"
|
|
},
|
|
"EE8E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "high byte of the ordered-row sum"
|
|
},
|
|
"EE91": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "low byte"
|
|
},
|
|
"EE94": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = divisor"
|
|
},
|
|
"EE96": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "Y:A / X"
|
|
},
|
|
"EE99": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = group index"
|
|
},
|
|
"EE9B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the group's average ordered row"
|
|
},
|
|
"EE9E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "high byte of the current-column sum"
|
|
},
|
|
"EEA1": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "low byte"
|
|
},
|
|
"EEA4": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = divisor"
|
|
},
|
|
"EEA6": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "Y:A / X"
|
|
},
|
|
"EEA9": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = group index"
|
|
},
|
|
"EEAB": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the group's current centre column"
|
|
},
|
|
"EEAE": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "high byte of the current-row sum at $04A0"
|
|
},
|
|
"EEB1": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "low byte"
|
|
},
|
|
"EEB4": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = divisor"
|
|
},
|
|
"EEB6": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "Y:A / X"
|
|
},
|
|
"EEB9": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = group index"
|
|
},
|
|
"EEBB": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the group's current centre row"
|
|
},
|
|
"EEBE": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "next group down"
|
|
},
|
|
"EEC0": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "loop until group 0 has been averaged"
|
|
},
|
|
"EEC2": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "done"
|
|
},
|
|
"EEC3": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"routine": [
|
|
"buildAiHeadingSearchOrder - Fills the 8-byte table at $894A with the order in which the resident AI",
|
|
"tries headings: 0 first, then +/-1, +/-2, +/-3 and finally 4 (the opposite heading), every value",
|
|
"folded into 0-7. The sign of each pair is decided by a coin toss, so a given game has a consistent",
|
|
"random left-or-right bias. Because -4 == +4 mod 8 the last entry is always 4, and the first entry is",
|
|
"always 0 (its 'negate' at $EED4 acts on a magnitude that is still zero).",
|
|
"In: nextGameRandom",
|
|
"Out: $894A-$8951 = eight heading offsets; zp_18 scratch; A and Y clobbered",
|
|
"Called from: $ED8E in rebuildAiGroupTables (this file) - the only caller in this overlay"
|
|
],
|
|
"line": "start from a zero heading offset"
|
|
},
|
|
"EEC5": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "zp_18 = the running heading offset, starting at 0"
|
|
},
|
|
"EEC7": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "Y = 1 writes the first entry, $8949+1 = $894A; the loop runs Y = 1..8"
|
|
},
|
|
"EEC9": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "A = the entry number"
|
|
},
|
|
"EECA": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "A = Y/2 = the magnitude of this pair, and C = 1 on the second (odd) entry of each pair"
|
|
},
|
|
"EECB": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "second entry of a pair: reuse the magnitude with the opposite sign"
|
|
},
|
|
"EECD": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "first entry of a pair: adopt the new magnitude 0, 1, 2, 3 or 4"
|
|
},
|
|
"EECF": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "toss a coin ..."
|
|
},
|
|
"EED2": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... heads (bit 7 clear): take this pair positive first, tails: fall through and negate"
|
|
},
|
|
"EED4": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "negate the current magnitude ..."
|
|
},
|
|
"EED6": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... one's complement ..."
|
|
},
|
|
"EED8": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "prepare the +1 of the two's complement negate"
|
|
},
|
|
"EED9": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... plus one"
|
|
},
|
|
"EEDB": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "keep the signed offset"
|
|
},
|
|
"EEDD": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the signed offset, -4..+4"
|
|
},
|
|
"EEDF": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clear carry for the bias below"
|
|
},
|
|
"EEE0": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "bias by 8 so a negative offset stays positive ..."
|
|
},
|
|
"EEE2": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... and fold it into a heading 0-7"
|
|
},
|
|
"EEE4": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$8949+Y, i.e. $894A..$8951"
|
|
},
|
|
"EEE7": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "next entry"
|
|
},
|
|
"EEE8": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "eight entries written?"
|
|
},
|
|
"EEEA": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "keep going while Y is below 9"
|
|
},
|
|
"EEEC": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "done"
|
|
},
|
|
"EEED": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"routine": [
|
|
"orderAiGroupToPoint - Orders AI group Y to move so that its formation centre ends up on column X,",
|
|
"row A. The two coordinates are stashed by patching the immediate operands of the two LDA # at",
|
|
"$EEF8 and $EEFC, because computeTrainerGroupStats clobbers A, X and Y. An empty group is left",
|
|
"alone. Falls into shiftAiGroupDestinations.",
|
|
"In: X = target column, A = target row, Y = group index; $8832 group ids, the unit arrays",
|
|
"Out: self-modifies $EEF9 and $EEFD (the operand bytes); $8955 = Y; the group's target ($88D2/$88DE)",
|
|
" and the unitDestCol/unitDestRow of every member are updated; returns with C=1 and nothing",
|
|
" changed when the group has no live member",
|
|
"Called from: $ED56 and $ED69 in initTrainerAi (this file)"
|
|
],
|
|
"block": [
|
|
"Self-modifying code: $EEED writes the target column into the operand byte $EEF9 (of the LDA #",
|
|
"at $EEF8) and $EEF0 writes the target row into $EEFD (of the LDA # at $EEFC). The $FF operands",
|
|
"shown in the listing are just what the overlay was assembled with."
|
|
],
|
|
"line": "park the target column in the operand byte of the LDA # at $EEF8"
|
|
},
|
|
"EEF0": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "park the target row in the operand byte of the LDA # at $EEFC"
|
|
},
|
|
"EEF3": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "measure group Y ($E531): member count, bounding box, centre and average ordered cell; clobbers A/X/Y"
|
|
},
|
|
"EEF6": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "C=1 means the group has no live member - nothing to order"
|
|
},
|
|
"EEF8": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "operand patched at $EEED = the target column"
|
|
},
|
|
"EEFA": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "zp_22 = wanted formation centre column"
|
|
},
|
|
"EEFC": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "operand patched at $EEF0 = the target row"
|
|
},
|
|
"EEFE": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "zp_24 = wanted formation centre row"
|
|
},
|
|
"EF00": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"routine": [
|
|
"shiftAiGroupDestinations - Moves a whole AI group without breaking its formation. The wanted centre",
|
|
"in zp_22/zp_24 is first clipped so the group's bounding box still fits on the 40x40 map and stored as",
|
|
"the group's target; the resulting shift is then added to the ordered destination of every unit whose",
|
|
"group id matches $8955, so the formation is translated as a block. The AND #$3F on the way also",
|
|
"clears the idle bit 7, which restarts any member that had already arrived.",
|
|
"In: zp_22/zp_24 = wanted centre, $8955 = the group (set by computeTrainerGroupStats), $8832 group",
|
|
" ids, unitDestColTable+50 $F802, unitDestRowTable+50 $F866, the group's box $890E/$891A/$8926/$8932",
|
|
"Out: $88D2/$88DE = the group's clipped target, unitDestCol/unitDestRow of every member shifted,",
|
|
" zp_2A/zp_2B = the shift applied, zp_22/zp_24 clipped, $8958",
|
|
"Called from: $ED3F in initTrainerAi and by falling out of orderAiGroupToPoint (both in this file)"
|
|
],
|
|
"line": "really trainerAiE000:clampGroupTargetToMap ($E5FF) - the modem driver's name for this address is a collision. It clips zp_22/zp_24 so the whole formation box stays on the map, stores the result as the group's target in $88D2/$88DE and returns the shift in zp_2A (columns) and zp_2B (rows)"
|
|
},
|
|
"EF03": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "AI slots 49 down to 0"
|
|
},
|
|
"EF05": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "group id of this AI unit"
|
|
},
|
|
"EF08": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the group computeTrainerGroupStats last measured"
|
|
},
|
|
"EF0B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "not a member: skip it"
|
|
},
|
|
"EF0D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "its ordered column"
|
|
},
|
|
"EF10": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "keep the 0-39 column, dropping the idle bit 7 so the unit is set moving again"
|
|
},
|
|
"EF12": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clear carry for the shift add"
|
|
},
|
|
"EF13": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "apply the group's column shift"
|
|
},
|
|
"EF15": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "store the new ordered column"
|
|
},
|
|
"EF18": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "its ordered row"
|
|
},
|
|
"EF1B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "keep the 0-39 row only"
|
|
},
|
|
"EF1D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clear carry for the shift add"
|
|
},
|
|
"EF1E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "apply the group's row shift"
|
|
},
|
|
"EF20": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "store the new ordered row"
|
|
},
|
|
"EF23": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "next AI slot"
|
|
},
|
|
"EF24": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "loop over all 50"
|
|
},
|
|
"EF26": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "done (also the early exit for an empty group)"
|
|
},
|
|
"EF27": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"routine": [
|
|
"staleDroneSteerTail - DEAD CODE in this overlay. From $EF27 to $EFFF the track 29 sector 0-3 image",
|
|
"is byte for byte identical to the track 29 sector 4-5 drone-flying overlay (game/droneAiEE00), and",
|
|
"these bytes are the tail of that overlay's steerDroneAndEmitMove, whose real entry point is $EF0F.",
|
|
"Nothing in this file reaches $EF27 - the routine above ends with an RTS at $EF26 - and the whole page",
|
|
"is thrown away again by $7B33 a few instructions after initTrainerAi returns.",
|
|
"What the code does where it is live: turns the drone heading $896F by the step in X (C=1 forces one",
|
|
"step anticlockwise), converts the new heading into a column/row delta through headingToDirectionTable",
|
|
"($7316), diagonalDirectionTable ($1FB2) and directionDeltaTable ($20F0), advances the drone by one",
|
|
"cell, and then writes a 4-byte command $8D (cmdDroneMove: heading, column, row) straight into the",
|
|
"comm module's INCOMING packet buffer at $E020 with $E01E = 4 - i.e. the trainer hands the engine a",
|
|
"packet as though the remote player had sent it. The coordinates are pre-mirrored so that the",
|
|
"engine's mirror-and-decrement of a remote $8D lands them back on the right cell.",
|
|
"In: X = heading change, C = 1 forces -1, $896F drone heading, $8974/$8975 drone column/row",
|
|
"Out: $896F, $8974/$8975 advanced one cell, $E020-$E023 = $8D + heading + mirrored column/row,",
|
|
" $E01E = 4",
|
|
"Called from: nothing in this file (live at $EF0F-$EF9D in game/droneAiEE00)"
|
|
],
|
|
"line": "carry clear: use the turn amount the caller put in X"
|
|
},
|
|
"EF29": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "carry set: turn one step anticlockwise"
|
|
},
|
|
"EF2B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "A = the signed turn"
|
|
},
|
|
"EF2C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clear carry for the heading add"
|
|
},
|
|
"EF2D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "add it to the current drone heading"
|
|
},
|
|
"EF30": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "wrap into 0-7 (8 compass points)"
|
|
},
|
|
"EF32": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the new heading"
|
|
},
|
|
"EF35": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "Y = heading 0-7"
|
|
},
|
|
"EF38": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$7316 in the $6F00 overlay: heading -> direction code (0-1 row only, 2-3 column only, 4-7 diagonal)"
|
|
},
|
|
"EF3B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "Y = row delta, assume 0"
|
|
},
|
|
"EF3D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = column delta, assume 0"
|
|
},
|
|
"EF3F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "is the code a diagonal?"
|
|
},
|
|
"EF41": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "codes 4-7 move on both axes"
|
|
},
|
|
"EF43": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "is the code a pure-column move?"
|
|
},
|
|
"EF45": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "codes 2-3 move along the column only"
|
|
},
|
|
"EF47": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "codes 0-1: row only"
|
|
},
|
|
"EF48": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$20F0: direction code -> signed delta"
|
|
},
|
|
"EF4B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "Y = row delta"
|
|
},
|
|
"EF4C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "on to the position update"
|
|
},
|
|
"EF4F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "codes 2-3"
|
|
},
|
|
"EF50": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$20F0: direction code -> signed delta"
|
|
},
|
|
"EF53": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = column delta"
|
|
},
|
|
"EF54": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "on to the position update"
|
|
},
|
|
"EF57": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "codes 4-7"
|
|
},
|
|
"EF58": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$1FB2: packs the two component codes of a diagonal into the two nibbles"
|
|
},
|
|
"EF5B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "keep the packed pair"
|
|
},
|
|
"EF5C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "low nibble = the row component"
|
|
},
|
|
"EF5E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "index the delta table with it"
|
|
},
|
|
"EF5F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$20F0: -> signed delta"
|
|
},
|
|
"EF62": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "Y = row delta"
|
|
},
|
|
"EF63": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "get the packed pair back"
|
|
},
|
|
"EF64": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "shift the high nibble down ..."
|
|
},
|
|
"EF65": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "..."
|
|
},
|
|
"EF66": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "..."
|
|
},
|
|
"EF67": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... = the column component"
|
|
},
|
|
"EF68": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "index the delta table with it"
|
|
},
|
|
"EF69": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$20F0: -> signed delta"
|
|
},
|
|
"EF6C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = column delta"
|
|
},
|
|
"EF6D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "A = column delta"
|
|
},
|
|
"EF6E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clear carry for the position add"
|
|
},
|
|
"EF6F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "current drone column"
|
|
},
|
|
"EF72": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "advance it one cell"
|
|
},
|
|
"EF75": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "X = the new column, for mirrorBlockCornerForOpponent"
|
|
},
|
|
"EF76": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "A = row delta"
|
|
},
|
|
"EF77": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clear carry for the position add"
|
|
},
|
|
"EF78": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "current drone row"
|
|
},
|
|
"EF7B": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "advance it one cell"
|
|
},
|
|
"EF7E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "Y = the new row, for mirrorBlockCornerForOpponent"
|
|
},
|
|
"EF7F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "command $8D = cmdDroneMove"
|
|
},
|
|
"EF81": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "byte 0 of the comm module's INCOMING packet buffer: the trainer forges the packet the engine is about to read"
|
|
},
|
|
"EF84": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the drone's heading"
|
|
},
|
|
"EF87": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "clear carry for the add"
|
|
},
|
|
"EF88": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "+4 ..."
|
|
},
|
|
"EF8A": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... modulo 8 = the same heading seen from the other end of the mirrored map"
|
|
},
|
|
"EF8C": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "byte 1 of the packet"
|
|
},
|
|
"EF8F": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "$EA38: X,Y = 39-(X+1), 39-(Y+1), pre-compensating the mirror-and-decrement the engine applies to a remote $8D"
|
|
},
|
|
"EF92": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "byte 2 of the packet = mirrored column"
|
|
},
|
|
"EF95": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "byte 3 of the packet = mirrored row"
|
|
},
|
|
"EF98": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "packet length 4"
|
|
},
|
|
"EF9A": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "tell the game a 4-byte packet has arrived; mergeIncomingPacket ($4F05) will execute it"
|
|
},
|
|
"EF9D": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "done"
|
|
},
|
|
"EF9E": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"routine": [
|
|
"measureDroneDistanceToTarget - DEAD in this overlay (nothing here calls it), but a complete 13-byte",
|
|
"routine: it copies the drone's cell $8974/$8975 into the reference point zp_27/zp_28 and tail-jumps",
|
|
"to cellDistance ($E82A), which returns the integer distance 0-16 between the cell in zp_22/zp_24 and",
|
|
"the drone. Y is left untouched, which is what lets the live caller keep a unit index in Y across the",
|
|
"call. Rendered as .byte because no reachable code in this file references it; it disassembles as",
|
|
"LDA $8974 / STA $27 / LDA $8975 / STA $28 / JMP $E82A and is the same routine as droneDistanceToCell",
|
|
"at $EF9E in game/droneAiEE00.",
|
|
"In: zp_22/zp_24 = cell to measure against, $8974/$8975 = drone cell",
|
|
"Out: A = X = distance 0-16, zp_27/zp_28 = drone cell, Y preserved",
|
|
"Called from: nothing in this file ($EE26 and $EEFC in game/droneAiEE00)"
|
|
],
|
|
"line": "AD 74 89 = LDA $8974 (drone column), 85 27 = STA zp_27, AD 75 89 = LDA $8975 (drone row)"
|
|
},
|
|
"EFA6": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "85 ..."
|
|
},
|
|
"EFA7": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... 28 = STA zp_28, then 4C 2A E8 = JMP cellDistance ($E82A)"
|
|
},
|
|
"EFAB": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"routine": [
|
|
"staleDroneTargetPickerTail - 34 stale bytes, unreachable and unusable here. In the solo trainer's",
|
|
"own $EC00 tail (track 35 sectors 12-15) and in the drone overlay (track 29 sectors 4-5) - both of",
|
|
"which carry these exact bytes - they are the end of the drone's target picker: bump the candidate",
|
|
"index zp_1F, loop back to $EF9B for the next of 15 candidate blocks, then adopt the best player unit",
|
|
"found (zp_1D) as the drone's target $8976 and lower the detonation threshold $8979 to three quarters",
|
|
"so the drone will settle for a weaker target next time round. In THIS image the BCC at $EFB1 branches",
|
|
"to $EF9B, which is the middle of the STA $E01E at $EF9A, so the fragment could not run even if",
|
|
"something jumped to it. In the modem driver's tail (track 34 sectors 12-15) the same addresses hold",
|
|
"the middle of the message 'INSERT DATA DISK AND PRESS SPACE.' instead. The bytes only survive so that",
|
|
"the shared strings from $EFCD on land at their fixed addresses.",
|
|
"In: zp_1D best candidate unit, zp_1F candidate index, $8979",
|
|
"Out: $8976 drone target, $8979 reduced to 3/4, zp_18 scratch",
|
|
"Called from: nothing - dead in this overlay"
|
|
],
|
|
"line": "next candidate block"
|
|
},
|
|
"EFAD": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "how many have been scored"
|
|
},
|
|
"EFAF": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "15 candidate blocks per pass"
|
|
},
|
|
"EFB1": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "branches to $EF9B - a real instruction only in the images where this fragment is live; here it is the middle of the STA $E01E at $EF9A"
|
|
},
|
|
"EFB3": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the best player unit found by the scoring pass"
|
|
},
|
|
"EFB5": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "bit 7 set = no candidate at all"
|
|
},
|
|
"EFB7": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "adopt it as the drone's target unit"
|
|
},
|
|
"EFBA": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the current detonation score threshold"
|
|
},
|
|
"EFBD": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "/2 ..."
|
|
},
|
|
"EFBE": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "... /4"
|
|
},
|
|
"EFBF": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "keep a quarter of it"
|
|
},
|
|
"EFC1": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the threshold again"
|
|
},
|
|
"EFC4": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "prepare to subtract"
|
|
},
|
|
"EFC5": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "threshold - threshold/4 = three quarters of it"
|
|
},
|
|
"EFC7": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "underflow: leave the threshold alone"
|
|
},
|
|
"EFC9": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "lower the bar so the drone will accept a weaker target next time"
|
|
},
|
|
"EFCC": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "done"
|
|
},
|
|
"EFCD": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"block": [
|
|
"$EFCD-$EFFE is byte for byte identical in all three images that can occupy this page (the trainer",
|
|
"setup overlay here, the solo trainer's own tail from track 35 and the modem driver's tail from track",
|
|
"34), and only the very last byte at $EFFF differs. That is deliberate: the setup screens in overlay",
|
|
"A print these strings by absolute address and must not care which image is resident. Only the T34",
|
|
"image, which $7B33 restores immediately after this overlay has run, also carries the 'INSERT xxxx",
|
|
"DISK AND PRESS SPACE.' template that begins at $EFAF - here those bytes are the stale code above, so",
|
|
"the three characters below are all that is left of that message."
|
|
],
|
|
"line": "tail of 'INSERT xxxx DISK AND PRESS SPACE.' - the terminator '.' has bit 7 set"
|
|
},
|
|
"EFD0": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "'DATA' - the 4-character disk name promptInsertDataDisk ($81B2) patches into the message template at $EFB6"
|
|
},
|
|
"EFD4": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "'GAME' - the disk name promptInsertGameDisk ($81A5) uses instead"
|
|
},
|
|
"EFD8": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "'CUSTOM OFF' - installed as message slot $1E by overlay A; its ON/OFF patch points at the 'OFF' at $EFDF"
|
|
},
|
|
"EFE2": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "'DESTROY OFF' - message slot $1E for the DESTROY toggle; the ON/OFF patch points at the 'OFF' at $EFEA"
|
|
},
|
|
"EFED": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "'ACCEPTED' - printed centred on row 15 by overlay A when the fire button confirms an edited value"
|
|
},
|
|
"EFF5": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "unreferenced trailing bytes, the same in every image of this page"
|
|
},
|
|
"EFFD": {
|
|
"unit": "game/trainerSetupEC00",
|
|
"line": "the last byte is $FF here and in the track 35 trainer tail, $00 in the track 34 modem tail"
|
|
}
|
|
},
|
|
"dataTypes": {
|
|
"EC00": [
|
|
"byte",
|
|
5
|
|
],
|
|
"EC05": [
|
|
"byte",
|
|
5
|
|
],
|
|
"EC0A": [
|
|
"byte",
|
|
5
|
|
],
|
|
"EFCD": [
|
|
"text",
|
|
3
|
|
],
|
|
"EFD0": [
|
|
"text",
|
|
4
|
|
],
|
|
"EFD4": [
|
|
"text",
|
|
4
|
|
],
|
|
"EFD8": [
|
|
"text",
|
|
10
|
|
],
|
|
"EFE2": [
|
|
"text",
|
|
11
|
|
],
|
|
"EFED": [
|
|
"text",
|
|
8
|
|
],
|
|
"EFF5": [
|
|
"byte",
|
|
11
|
|
]
|
|
},
|
|
"zpComments": {
|
|
"48": "pointer (lo) to a cell of the $F000 battlefield map, set by readMapCell ($25FA); also the running pointer of the block fill/copy loops, and in the trainer setup overlay the pointer to the 50 group ids of the selected opening play inside the playbook at $0200"
|
|
}
|
|
} |