639 lines
No EOL
29 KiB
JSON
Generated
639 lines
No EOL
29 KiB
JSON
Generated
{
|
|
"chunk": "game_ovl_6F00_A_6F00",
|
|
"unit": "game/ovl_6F00_A",
|
|
"routines": [
|
|
{
|
|
"addr": "6F04",
|
|
"name": "generateBattlefieldMap",
|
|
"summary": "Entry point of the random battlefield generator (variant A of the $6F00 overlay). Derives the map-style flags at $032E from the game type in $0BA3 bits 0-2 (type 1 QB SNEAK -> $80, type 6 DEFENDER -> $40, else 0), sets $032D bit 7 when the low 5 bits of the map seed $0B95 are all set (= map code ending in '=', produces a point-symmetric map), shows status message 1 (sub_C853), blanks the 40x40 map at $F000 to $40 (sub_EC00), copies the 3-byte map seed $0B95-$0B97 into the scenario RNG state $5A-$5C (sub_EC2B) and falls into runMapGeneratorPasses. Called by manualLookupCheck ($09E0, to show the map the player must identify), by the map/setup screens of overlay A ($76AD, $76FF, $7A02, $7D23).",
|
|
"inputs": "$0BA3 bits 0-2 game type, $0B95-$0B97 map seed, $EC00 sub-overlay (T34 s12-15) present, contour rule table at $0200 (T18 s15) present",
|
|
"outputs": "$032D, $032E; map at $F000-$F63F filled; $5A-$5C RNG state; $0BA9-$0BAD map-id text; $0300-$032C generator parameters; clobbers $0400-$04EF and $9025-$9028",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "6F36",
|
|
"name": "runMapGeneratorPasses",
|
|
"summary": "Body of the map generator (fall-through from generateBattlefieldMap; the JSR at $7125 listed in XREF belongs to variant B). Pass order: encode the seed in A/Y/X as the 5-character map id at $0BA9 (sub_7850); carve a river (sub_7548, tiles $55-$5C, marks a keep-clear zone with $80/$81); pick hill summit sites (pickHillSummitSites); paint the summits (paintHillSummits, tile $65); turn $80 markers back into $40 (sub_74FE); applyContourTiles($65) with $0324=0 draws the upper-slope contour $41-$54; grow lower hills $61 beside the slopes (growLowerHillsBesideSlopes); clear $81 markers; applyContourTiles($61) with $0324=$20 draws the lower contour $21-$34; place forest clusters (placeForestClusters, tiles $5D-$60). For symmetric maps ($032D bit 7) the top half is then mirrored onto the bottom half (sub_EC54) and, if the river crossed the centre ($032D bit 6), cells (19,19)/(19,20)/(20,20) are patched to $55/$58/$40 so the two river halves join. Ends with jmp sub_C6EF (clear the status message). serviceLinkWhileGenerating is called between passes.",
|
|
"inputs": "A/Y/X = map seed bytes (from sub_EC2B), $032D, $032E, RNG $5A-$5C",
|
|
"outputs": "battlefield map $F000-$F63F, $0BA9-$0BAD map id text, $0300-$032C, map cells $F30B/$F333/$F334",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "6F91",
|
|
"name": "serviceLinkWhileGenerating",
|
|
"summary": "Keeps the two-player link alive during the long map generation: unless $0B9B bit 7 (solo/film mode) is set, and if zp_B1 bit 7 (non-battle phase flag) is set, jumps to serviceCommandExchange ($4DA9). Otherwise returns immediately. Called four times from runMapGeneratorPasses and from $773A.",
|
|
"inputs": "$0B9B bit 7, zp_B1 bit 7",
|
|
"outputs": "side effects of sub_4DA9 (message system / command exchange)",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "6F9E",
|
|
"name": "pickHillSummitSites",
|
|
"summary": "Chooses how many hill summits to place in the top half of the map and where. Count = (rnd&3)+(rnd&1), re-rolled to 1..4 if it came out 1, forced to 4 for map style $40 (DEFENDER) and masked to 0/1 for map style $80 (QB SNEAK); stored in $0300. For each site a blob size 2..5 goes to $0307[i] (mirrored into $0307[count+i]) and four candidate rows 7..14 go to $0325/$031C[0..3]. findBlankSitesOnLanes then resolves the sites; afterwards $0300 is doubled, the candidate rows are mirrored to 39-row (sub_3FA3) and findBlankSitesOnLanes runs again for the bottom half, so every summit has a point-mirrored twin. Exits early (count 0) via L_7088 with $0300 = 0.",
|
|
"inputs": "$032E bits 6/7, RNG",
|
|
"outputs": "$0300 total site count, $0307[0..7] sizes, $0314[i] columns, $031C[i] rows, $0325[0..3] rows, $0329[0..3] lane order; zp_28-zp_2B, zp_18",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "701C",
|
|
"name": "findBlankSitesOnLanes",
|
|
"summary": "Resolves site positions for entries zp_2A .. $0300-1: shuffles the four lane columns (shuffleLaneColumns) and, for each site, takes the next lane (zp_2B cycling 3..0) and the next candidate row (zp_29 cycling zp_28..0), tries up to 20 jittered columns (addRandomJitter, +/-6, must be < 40) until the map cell is blank ($40); if a row yields nothing it tries the other candidate rows and finally drops the site (dec $0300). Column goes to $0314[i], row to $031C[i]. Also entered by fall-through for the mirrored second pass. On exit $0300 = number of sites actually placed (zp_2A).",
|
|
"inputs": "zp_2A first site index, $0300 requested count, zp_28 last row-candidate index, $0325[] candidate rows",
|
|
"outputs": "$0314[]/$031C[] site coordinates, $0300 corrected count, $0329[] shuffled lanes, zp_1D/zp_1E/zp_1F/zp_29/zp_2B",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "708E",
|
|
"name": "advanceLaneIndex",
|
|
"summary": "zp_2B = zp_2B - 1, wrapping from 0 back to 3 (cycles through the four lane columns in $0329).",
|
|
"inputs": "zp_2B",
|
|
"outputs": "zp_2B",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "7097",
|
|
"name": "advanceRowCandidate",
|
|
"summary": "zp_29 = zp_29 - 1, wrapping from 0 back to zp_28 (cycles through the candidate rows in $0325).",
|
|
"inputs": "zp_29, zp_28",
|
|
"outputs": "zp_29",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "70A0",
|
|
"name": "shuffleLaneColumns",
|
|
"summary": "Builds a random permutation of 0..3 in $9025-$9028 (identity, then four random swaps) and stores laneColumnTable[perm[y]] (6, 15, 25, 34) into $0329-$032C so hills are spread over the four vertical lanes of the map in random order.",
|
|
"inputs": "RNG, laneColumnTable $6F00",
|
|
"outputs": "$0329-$032C, $9025-$9028 clobbered, zp_18",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "70D9",
|
|
"name": "paintHillSummits",
|
|
"summary": "For every site i < $0300 grows a blob of $0307[i] (2..5) cells of summit tile $65 (random variant $65/$66) at ($0314[i], $031C[i]) via growTerrainBlob with horizontal spread $C8/256 and vertical spread $20/256, $0304 = 0 (no 2x2 constraint). Does nothing when $0300 is 0.",
|
|
"inputs": "$0300, $0307[], $0314[], $031C[]",
|
|
"outputs": "map cells; $0301 = $65, $0304 = 0, zp_2A, zp_A7/zp_A8",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "710A",
|
|
"name": "growLowerHillsBesideSlopes",
|
|
"summary": "Scans every interior cell (x,y in 1..38). For a blank cell whose N, W, E or S neighbour is an upper-slope contour tile (terrain class 5, tiles $41-$54) it grows, with probability 1/4 (N,S) or 1/2 (W,E) per neighbour, a 5-cell blob of lower-hill tile $61 ($61-$64 variants) from that cell (growTerrainBlob, spreads $40/$10). This widens each summit into a hill body before the second contour pass. Skips entirely when $0300 is 0 (no hills).",
|
|
"inputs": "$0300, map, terrainClassTable $077E, RNG",
|
|
"outputs": "map cells; $0301 = $61, $0304 = 0, zp_A7/zp_A8 scan position",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "71B0",
|
|
"name": "growTerrainBlob",
|
|
"summary": "Generic random blob painter. Starting at (zp_A7, zp_A8) it paints blobTile $0301 (randomised through sub_7872) on up to A blank ($40) cells using a depth-first walk with an explicit stack at $0400 (col) / $0450 (row) / $04A0 (direction flags), max 80 entries. From each cell it tries N, W, E, S once each (flag bits 0-3 of zp_6B), accepting a neighbour when it is inside the map, blank and a random byte is below $91DB (vertical) or $91DA (horizontal); a new cell never grows back toward the origin row/column (bits preset from its position relative to zp_A7/zp_A8). With $0304 bit 6 set a cell is only painted if checkAlignedBlockForForest allows it. Returns when A cells are placed, the stack is exhausted or full. Note: overwrites $0400-$04EF, the page that also holds the copy-protection answer table.",
|
|
"inputs": "A = cells to place, X = horizontal spread (0-255), Y = vertical spread, zp_A7/zp_A8 origin, $0301 tile, $0304 flags",
|
|
"outputs": "map cells; $91DA/$91DB, zp_18, zp_1A, zp_6B, zp_A5/zp_A6, zp_B2 stack pointer, zp_B3 placed count, $0400-$04EF",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "72F6",
|
|
"name": "checkAlignedBlockForForest",
|
|
"summary": "For the blob cell (zp_A5, zp_A6) looks at the 2x2-aligned block containing it (coordinates with bit 0 cleared) and returns C = 0 only if all four cells are terrain class 0 (blank) or 2 (forest $5D-$60); otherwise C = 1. On success zp_48/49 are re-pointed at the original cell. Forest blobs are therefore confined to aligned 2x2 blocks that contain nothing but blank/forest.",
|
|
"inputs": "zp_A5/zp_A6, map, terrainClassTable",
|
|
"outputs": "C flag, zp_48/49, X/Y clobbered",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "7340",
|
|
"name": "placeForestClusters",
|
|
"summary": "Scatters forest ($0301 = $5D, variants $5D-$60, $0304 = $40 so growth respects 2x2 alignment). Cluster count-1 = (rnd&7)+2 normally (1/32 chance of no forest at all when rnd&$1F = 0), fixed 9 for map style $80 (QB SNEAK), and additionally masked with 3 for style $40 (DEFENDER). Each cluster picks a centre (rnd&$1F)+4 in both axes and makes 6 seeds jittered around it (addRandomJitter +/-8 columns, +/-4 rows, retried while >= 40), each seed painted by paintForestClusterSeeds. Also stores $41 into $0302, which nothing reads (dead store).",
|
|
"inputs": "$032E, RNG",
|
|
"outputs": "map cells; $0301, $0302, $0304, zp_2A cluster counter, zp_2B seed counter, zp_92-zp_95",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "73AD",
|
|
"name": "paintForestClusterSeeds",
|
|
"summary": "Paints a 4x2 block of forest blob seeds around (zp_93, zp_92): calls paintForestBlobAt at (x,y), (x+1,y), (x+1,y+1), (x,y+1), (x-2,y), (x-1,y), (x-1,y+1) and falls into it for (x-2,y+1), stepping zp_92/zp_93 in between.",
|
|
"inputs": "zp_93 column, zp_92 row",
|
|
"outputs": "map cells; zp_92/zp_93 restored to start values; zp_A7/zp_A8",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "73D4",
|
|
"name": "paintForestBlobAt",
|
|
"summary": "If zp_93 (column) and zp_92 (row) are both < 40, copies them to zp_A7/zp_A8 and grows a 4-cell blob with growTerrainBlob (spreads $DC/$DC, tile $0301, flags $0304). Out-of-range coordinates are ignored.",
|
|
"inputs": "zp_92/zp_93, $0301/$0304",
|
|
"outputs": "map cells; zp_A7/zp_A8",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "73EE",
|
|
"name": "applyContourTiles",
|
|
"summary": "Contour/edge pass for the hill terrain whose interior tile is A ($65 summit or $61 lower hill). zp_8E = terrain class of A, zp_91 = zp_8E-1 = class of its contour ring. Scans all 1600 cells: a cell of class zp_91 is erased to $40 and re-evaluated; cells of class >= zp_8E and river cells (class 1) are left alone; every other cell goes through applyContourRuleAtCell, which may paint a contour tile (rule value - $0324), fill the cell with the interior tile, or erase a neighbour; when it returns C = 1 the scan backs up two rows/columns and continues. Called with A = $65 ($0324 = 0 -> contour $41-$54) and A = $61 ($0324 = $20 -> contour $21-$34).",
|
|
"inputs": "A = interior tile, $0324 contour offset, contour rule table $0200, terrainClassTable, map",
|
|
"outputs": "map cells; $0312 = interior tile, zp_8E, zp_91, zp_2D/zp_2E scan row/col, zp_8C/zp_8D, zp_21",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "7444",
|
|
"name": "applyContourRuleAtCell",
|
|
"summary": "Builds an 8-bit mask (zp_21) of the neighbours of cell (zp_2E, zp_2D) whose terrain class is >= zp_8E (bit order NW,N,NE,W,E,SW,S,SE from neighbourOffsetTable/neighbourBitTable), clears bits that point off the map, and looks the mask up in the contour rule table at $0200: 0 = leave the cell; $01-$7F = paint tile (value - $0324) with a random variant (sub_7872) at the cell, C = 0; $FF = fill the cell with the interior tile $0312 and return C = 1 (rescan from two cells back); $80-$FE = irregular pattern: with a 50% random bit (taken from zp_8D, refilled every 7 calls) erase the neighbour indexed by the low 3 bits if it is of class zp_8E (C = 1), otherwise fill the cell when the value is >= $90 (C = 1) or leave it (C = 0). On C = 1 zp_2E/zp_2D are decremented by 2 (floor 0).",
|
|
"inputs": "zp_2E/zp_2D cell, zp_48 pointer to (x-1,y-1), zp_8E, $0200 table, $0324, $0312",
|
|
"outputs": "C flag, map cells, zp_2D/zp_2E, zp_21, zp_18, zp_8C/zp_8D",
|
|
"confidence": "medium"
|
|
}
|
|
],
|
|
"variables": [
|
|
{
|
|
"addr": "18",
|
|
"scope": "zp",
|
|
"name": "blobCellsToPlace",
|
|
"meaning": "in this chunk: number of cells growTerrainBlob still has to paint (also scratch in pickHillSummitSites and applyContourRuleAtCell)",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "1A",
|
|
"scope": "zp",
|
|
"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",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "1D",
|
|
"scope": "zp",
|
|
"name": "siteCandidateRow",
|
|
"meaning": "row currently being tried for a hill site",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "1E",
|
|
"scope": "zp",
|
|
"name": "siteRetryCount",
|
|
"meaning": "remaining column tries (20) for the current site row",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "1F",
|
|
"scope": "zp",
|
|
"name": "siteStartRowIndex",
|
|
"meaning": "row-candidate index at which the search for the current site started (detects a full cycle)",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "21",
|
|
"scope": "zp",
|
|
"name": "neighbourMask",
|
|
"meaning": "8-bit mask of surrounding cells that belong to the terrain being contoured (index into the $0200 rule table)",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "28",
|
|
"scope": "zp",
|
|
"name": "lastRowCandidateIndex",
|
|
"meaning": "site count - 1; wrap value for advanceRowCandidate",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "29",
|
|
"scope": "zp",
|
|
"name": "rowCandidateIndex",
|
|
"meaning": "index into the candidate row table $0325",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "2A",
|
|
"scope": "zp",
|
|
"name": "mapGenItemIndex",
|
|
"meaning": "current hill site index (pickHillSummitSites/paintHillSummits) or remaining forest cluster count (placeForestClusters)",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "2B",
|
|
"scope": "zp",
|
|
"name": "laneIndex",
|
|
"meaning": "index into the shuffled lane column table $0329 (3..0); in placeForestClusters the remaining seeds per cluster",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "2D",
|
|
"scope": "zp",
|
|
"name": "contourScanRow",
|
|
"meaning": "row of the cell being examined by applyContourTiles",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "2E",
|
|
"scope": "zp",
|
|
"name": "contourScanCol",
|
|
"meaning": "column of the cell being examined by applyContourTiles",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "48",
|
|
"scope": "zp",
|
|
"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)",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "49",
|
|
"scope": "zp",
|
|
"name": "mapCellPtrHi",
|
|
"meaning": "high byte of mapCellPtr",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "5A",
|
|
"scope": "zp",
|
|
"name": "scenarioSeed0",
|
|
"meaning": "scenario RNG state byte 0; loaded from mapSeed $0B95 by sub_EC2B at the start of generation",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "6B",
|
|
"scope": "zp",
|
|
"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)",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "8C",
|
|
"scope": "zp",
|
|
"name": "randomBitsLeft",
|
|
"meaning": "applyContourRuleAtCell: uses left in the random bit buffer zp_8D before a new random byte is drawn",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "8D",
|
|
"scope": "zp",
|
|
"name": "randomBitBuffer",
|
|
"meaning": "random byte shifted right one bit per irregular-pattern decision",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "8E",
|
|
"scope": "zp",
|
|
"name": "contourInteriorClass",
|
|
"meaning": "terrain class of the interior tile being contoured (6 for $65, 4 for $61); neighbours of class >= this count as inside",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "91",
|
|
"scope": "zp",
|
|
"name": "contourRingClass",
|
|
"meaning": "contourInteriorClass - 1 = class of the contour tiles that get erased and redrawn",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "92",
|
|
"scope": "zp",
|
|
"name": "forestSeedRow",
|
|
"meaning": "row of the current forest seed (jittered from zp_94)",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "93",
|
|
"scope": "zp",
|
|
"name": "forestSeedCol",
|
|
"meaning": "column of the current forest seed (jittered from zp_95)",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "94",
|
|
"scope": "zp",
|
|
"name": "forestClusterRow",
|
|
"meaning": "row of the current forest cluster centre (4..35)",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "95",
|
|
"scope": "zp",
|
|
"name": "forestClusterCol",
|
|
"meaning": "column of the current forest cluster centre (4..35)",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "A5",
|
|
"scope": "zp",
|
|
"name": "blobCurCol",
|
|
"meaning": "column of the blob cell being expanded (reuses the view-origin column variable); input to sub_25C7",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "A6",
|
|
"scope": "zp",
|
|
"name": "blobCurRow",
|
|
"meaning": "row of the blob cell being expanded",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "A7",
|
|
"scope": "zp",
|
|
"name": "blobOriginCol",
|
|
"meaning": "column where the current blob starts; also the scan column in growLowerHillsBesideSlopes",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "A8",
|
|
"scope": "zp",
|
|
"name": "blobOriginRow",
|
|
"meaning": "row where the current blob starts; also the scan row in growLowerHillsBesideSlopes",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "B1",
|
|
"scope": "zp",
|
|
"name": "gamePhase",
|
|
"meaning": "0 during battle, nonzero in setup/menu phases; bit 7 set lets serviceLinkWhileGenerating call the command exchange",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "B2",
|
|
"scope": "zp",
|
|
"name": "blobStackPtr",
|
|
"meaning": "growTerrainBlob stack index into $0400/$0450/$04A0 (max $50 entries)",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "B3",
|
|
"scope": "zp",
|
|
"name": "blobCellsPlaced",
|
|
"meaning": "cells painted so far by growTerrainBlob",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "0200",
|
|
"scope": "abs",
|
|
"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",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "0300",
|
|
"scope": "abs",
|
|
"name": "hillSiteCount",
|
|
"meaning": "number of hill summit sites (both halves); 0 = no hills",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "0301",
|
|
"scope": "abs",
|
|
"name": "blobTile",
|
|
"meaning": "base tile code painted by growTerrainBlob ($65 summit, $61 lower hill, $5D forest); randomised by sub_7872 using tileVariantMaskTable",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "0302",
|
|
"scope": "abs",
|
|
"name": "unusedBlobTile0302",
|
|
"meaning": "written with $41 by placeForestClusters, never read (dead store)",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "0304",
|
|
"scope": "abs",
|
|
"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",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "0307",
|
|
"scope": "abs",
|
|
"name": "hillSizeTable",
|
|
"meaning": "8 bytes: blob size (2..5) of each hill summit; entries count+i mirror entries i",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "030F",
|
|
"scope": "abs",
|
|
"name": "riverCol",
|
|
"meaning": "river generator (sub_7548): current column",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "0310",
|
|
"scope": "abs",
|
|
"name": "riverRow",
|
|
"meaning": "river generator: current row",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "0311",
|
|
"scope": "abs",
|
|
"name": "riverDirection",
|
|
"meaning": "river generator: direction code of the current segment (tile = $55 + code)",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "0312",
|
|
"scope": "abs",
|
|
"name": "contourInteriorTile",
|
|
"meaning": "interior tile of the current applyContourTiles pass (also reused by the river generator as the next direction code)",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "0314",
|
|
"scope": "abs",
|
|
"name": "hillSiteColTable",
|
|
"meaning": "8 bytes: column of each hill summit site",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "031C",
|
|
"scope": "abs",
|
|
"name": "hillSiteRowTable",
|
|
"meaning": "8 bytes: row of each hill summit site (initially a copy of the candidate rows)",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "0324",
|
|
"scope": "abs",
|
|
"name": "contourTileOffset",
|
|
"meaning": "subtracted from contourRuleTable values: 0 for the summit pass (contour $41-$54), $20 for the lower-hill pass (contour $21-$34)",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "0325",
|
|
"scope": "abs",
|
|
"name": "siteCandidateRowTable",
|
|
"meaning": "4 candidate rows (7..14 for the top half, 39-row for the mirrored bottom half)",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "0329",
|
|
"scope": "abs",
|
|
"name": "laneColumnOrder",
|
|
"meaning": "4 lane columns (6,15,25,34) in random order",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "032D",
|
|
"scope": "abs",
|
|
"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",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "032E",
|
|
"scope": "abs",
|
|
"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",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "0400",
|
|
"scope": "abs",
|
|
"name": "blobStackCol",
|
|
"meaning": "80-entry column stack of growTerrainBlob (overwrites the copy-protection answer table)",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "0450",
|
|
"scope": "abs",
|
|
"name": "blobStackRow",
|
|
"meaning": "80-entry row stack of growTerrainBlob",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "04A0",
|
|
"scope": "abs",
|
|
"name": "blobStackFlags",
|
|
"meaning": "80-entry direction-flag stack of growTerrainBlob",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "0770",
|
|
"scope": "abs",
|
|
"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)",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "077E",
|
|
"scope": "abs",
|
|
"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",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "0B95",
|
|
"scope": "abs",
|
|
"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",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "0B9B",
|
|
"scope": "abs",
|
|
"name": "soloModeFlag",
|
|
"meaning": "bit 7 = solo/film mode: serviceLinkWhileGenerating does nothing",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "0BA3",
|
|
"scope": "abs",
|
|
"name": "gameTypeOptions",
|
|
"meaning": "bits 0-2 game type; types 1 (QB SNEAK) and 6 (DEFENDER) select special map styles",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "0BA9",
|
|
"scope": "abs",
|
|
"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",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "9025",
|
|
"scope": "abs",
|
|
"name": "lanePermutation",
|
|
"meaning": "4 bytes of the sprite build buffer reused by shuffleLaneColumns as a permutation of 0..3",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "91DA",
|
|
"scope": "abs",
|
|
"name": "blobSpreadH",
|
|
"meaning": "growTerrainBlob: probability (x/256) of growing west/east (reuses currentUnitCol)",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "91DB",
|
|
"scope": "abs",
|
|
"name": "blobSpreadV",
|
|
"meaning": "growTerrainBlob: probability (x/256) of growing north/south (reuses currentUnitRow)",
|
|
"confidence": "high"
|
|
},
|
|
{
|
|
"addr": "F30B",
|
|
"scope": "abs",
|
|
"name": "mapCell19x19",
|
|
"meaning": "map cell column 19 row 19; set to river tile $55 when a symmetric map's river crosses the centre",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "F333",
|
|
"scope": "abs",
|
|
"name": "mapCell19x20",
|
|
"meaning": "map cell column 19 row 20; set to river tile $58 for the centre patch",
|
|
"confidence": "medium"
|
|
},
|
|
{
|
|
"addr": "F334",
|
|
"scope": "abs",
|
|
"name": "mapCell20x20",
|
|
"meaning": "map cell column 20 row 20; blanked to $40 for the centre patch",
|
|
"confidence": "medium"
|
|
}
|
|
],
|
|
"dataBlocks": [
|
|
{
|
|
"addr": "6F00",
|
|
"length": 4,
|
|
"type": "byteTable",
|
|
"name": "laneColumnTable",
|
|
"description": "$06,$0F,$19,$22: the four lane columns (6, 15, 25, 34) along which hill summits are placed; shuffled into $0329 by shuffleLaneColumns. Labelled sub_6F00 only because variant B has code here."
|
|
},
|
|
{
|
|
"addr": "71AB",
|
|
"length": 5,
|
|
"type": "byteTable",
|
|
"name": "unusedBitTable71AB",
|
|
"description": "$08,$04,$02,$01,$00 - not referenced by any code in variant A (the $71AC reference in XREF is variant B code)."
|
|
},
|
|
{
|
|
"addr": "74EE",
|
|
"length": 8,
|
|
"type": "byteTable",
|
|
"name": "neighbourOffsetTable",
|
|
"description": "$00,$01,$02,$28,$2A,$50,$51,$52: offsets of the 8 neighbours (NW,N,NE,W,E,SW,S,SE) from a pointer at (x-1,y-1). The disassembler split it at L_74F3 because of a variant-B branch target; it continues past the chunk end."
|
|
},
|
|
{
|
|
"addr": "74F6",
|
|
"length": 8,
|
|
"type": "byteTable",
|
|
"name": "neighbourBitTable",
|
|
"description": "$01..$80: bit for each neighbour in neighbourMask (just past the chunk end, used by applyContourRuleAtCell)."
|
|
}
|
|
],
|
|
"misclassified": [
|
|
{
|
|
"addr": "6F00",
|
|
"length": 4,
|
|
"actual": "data",
|
|
"evidence": "labelled sub_6F00 but it is the 4-byte lane column table read by LDA sub_6F00,X at $70CF; the JSR $6F00 at $7122 exists only in overlay variant B."
|
|
},
|
|
{
|
|
"addr": "71AB",
|
|
"length": 5,
|
|
"actual": "data",
|
|
"evidence": "bytes between the RTS at $71AA and sub_71B0, unreferenced in variant A (correctly emitted as .byte)."
|
|
}
|
|
],
|
|
"insights": [
|
|
"Overlay A $6F00-$74ED is the random battlefield generator. Pipeline (runMapGeneratorPasses): seed -> 5-char map id; river ($55-$5C) with $80/$81 keep-clear markers; hill summits ($65/$66) on 4 lanes with point-mirrored twins; upper contour ring ($41-$54) from the $0200 rule table; lower hills ($61-$64) grown beside the ring; lower contour ring ($21-$34); forest clusters ($5D-$60) in 2x2-aligned blocks; optional point mirroring of the whole map (sub_EC54) when the seed's low 5 bits are $1F (map code ends in '=').",
|
|
"Terrain classes (table $077E) line up with the hill model: class 0 plain ($40), 3 lower contour ($21-$34, height 1), 4 lower hill ($61-$64, height 2), 5 upper contour ($41-$54, height 3), 6 summit ($65-$66, height 4), 1 river ($55-$5C, 8 directional pieces), 2 forest ($5D-$60), 7 recycler ($67). Colour table $07EA gives class 1 cyan, class 2 green, classes 3-6 orange, class 0 yellow.",
|
|
"The 256-byte table at $0200 (track 18 sector 15, loaded by sub_10BB) is a neighbourhood -> tile rule table for drawing contour rings: bit i set = neighbour i (NW,N,NE,W,E,SW,S,SE) is higher terrain; entries are a contour tile, $FF = fill the cell, or $80+n = randomly delete neighbour n (>= $90 also fills). The same page is reused as BAM buffer and sprite shape buffer, so it must be intact when generateBattlefieldMap runs.",
|
|
"Map cell values with bit 7 set are treated as occupied everywhere in the generator; the river generator uses $80 (wide zone) and $81 (near ring) as temporary keep-clear markers that sub_74FE converts back to $40 after the summit pass and after the lower-hill pass respectively.",
|
|
"Game type affects the map: $0BA3 & 7 = 1 (QB SNEAK) -> $032E = $80: no river, 0-1 hill, 10 forest clusters; = 6 (DEFENDER) -> $032E = $40: river always, 4 hills, 1-4 forest clusters; other types: river 7/8 of the time, 0-4 hills, 3-10 clusters (1/32 chance of none).",
|
|
"growTerrainBlob ($71B0) is a reusable DFS blob painter with its stack in $0400/$0450/$04A0 - the copy-protection answer table at $0401 is destroyed by it, which is harmless because manualLookupCheck reads its answer before calling generateBattlefieldMap and the check is disabled anyway.",
|
|
"Several sub_ labels inside this chunk are entry points only in overlay variant B (sub_6F00, sub_6F89, sub_6FF5, sub_7036, sub_7061, sub_708D, sub_7135, sub_7163, sub_7204, sub_7221, sub_731E, sub_7396, sub_741C, sub_74C3); in variant A they fall in the middle of routines. The main program's calls to $731E ($5639), $74C3 ($5DF2) and $7396 ($5DF5) therefore require variant B to be loaded.",
|
|
"The map id text at $0BA9-$0BAD is the 24-bit seed $0B95-$0B97 written 5 bits per character (last character = seed bits 0-4) through the 32-character alphabet ' ABCDEFGHIJKLMNOPQRSTUVWXYZ+-*/=' at $890C (sub_7850); the same seed is what manualLookupCheck swaps in from the $0401 table to show the player a map from the manual."
|
|
]
|
|
} |