{
 "chunk": "data_data_9300",
 "unit": "game/data_9300",
 "routines": [],
 "variables": [],
 "dataBlocks": [
  {
   "addr": "9300",
   "length": 25,
   "type": "byteTable",
   "name": "bitmapRowLoTable",
   "description": "Low bytes of the 25 bitmap character-row addresses $A000 + 320*row (values cycle $00,$40,$80,$C0). Read as bitmapRowLoTable,Y (Y = row 0-24) by calcCellPointersXY ($2AA3) and by plotBitmapPixelRow ($FC80) in the high-memory block; overlay B reads it biased by 3 ($9303, rows 3+) at $7BDA/$7C38."
  },
  {
   "addr": "9319",
   "length": 25,
   "type": "byteTable",
   "name": "bitmapRowHiTable",
   "description": "High bytes of the same 25 row addresses: $A0,$A1,$A2,$A3,$A5,$A6,$A7,$A8,$AA,... (step $140 per row). Read at $2AAC and $FC87; biased alias $931C used by overlay B."
  },
  {
   "addr": "9332",
   "length": 40,
   "type": "byteTable",
   "name": "bitmapColLoTable",
   "description": "Low bytes of column*8 for the 40 bitmap columns (0,8,...,312 mod 256). Read at $2AA7 and $FCAE; biased alias $9335 used by overlay B at $7BDD/$7C3B."
  },
  {
   "addr": "935A",
   "length": 40,
   "type": "byteTable",
   "name": "bitmapColHiTable",
   "description": "High bytes of column*8: $00 for columns 0-31, $01 for columns 32-39. Read at $2AAF and $FCB5."
  },
  {
   "addr": "9382",
   "length": 25,
   "type": "byteTable",
   "name": "screenRowLoTable",
   "description": "Low bytes of the 25 screen-RAM row addresses $8C00 + 40*row (0,$28,$50,...). Read by fillScreenColourRect ($2A71), calcCellPointersXY ($2AB6) and both $6F00 overlays at $82C1."
  },
  {
   "addr": "939B",
   "length": 25,
   "type": "byteTable",
   "name": "screenRowHiTable",
   "description": "High bytes of the screen-RAM row addresses: $8C x7, $8D x6, $8E x7, $8F x5. Read at $2A6C, $2ABB and $82C6 in both $6F00 overlays. Colour RAM is reached by adding $4C to the high byte."
  },
  {
   "addr": "93B4",
   "length": 114,
   "type": "byteTable",
   "name": "fontColorTable",
   "description": "Default screen-RAM colour byte for each of the 114 gameFont glyphs, same index as gameFont. Read by plotGlyph at $2AD6 only when textDrawParams ($2A29) is zero; a zero entry makes the plotter fall back to the explicit colour $2A2A. Values are packed nibble pairs, e.g. $BC, $FC, $6E for the diagonal shading glyphs $30-$37 and $BB/$FF for the heavy bevel glyphs $6A-$71."
  },
  {
   "addr": "9426",
   "length": 912,
   "type": "charset",
   "name": "gameFont",
   "description": "114 glyphs of 8x8 pixels, 8 bytes each, addressed as $9426 + code*8 by plotGlyph ($2AC8). This is not a text font: $00 blank, $01-$05 solid, $06-$13 single-pixel corner/edge pieces, $14-$2F the chunky diagonal / wedge shading set, $30-$33 the two halves of a tall 'N' and 'S' (stacked vertically) and $34-$37 a goalpost 'H' and a comb 'E' (two cells side by side), $38 a small hollow diamond, $39-$48 thin frame pieces, diagonals and the four double-line corners, $49-$56 the top and bottom halves of the ten tall digits 0-9 (see tallDigitGlyphPairTable), $57-$59 three narrow glyphs that together form a 24-pixel strip of small letters and are not reached from any table found so far, $5A-$5E vertical dither patterns, $5F-$71 the heavy raised-bevel frame set. Text on the battlefield screens is drawn with the separate 59-glyph sets at $98BE/$9A96, not with this one."
  },
  {
   "addr": "97B6",
   "length": 90,
   "type": "byteTable",
   "name": "unreferencedGlyphCodeBlock",
   "description": "90 bytes of valid gameFont glyph codes ($00,$02,$04,$05,$07,$14-$2F,$30-$38,$45-$48) sitting between gameFont and tallDigitGlyphPairTable. No traced code reads it: XREF.txt has no label anywhere in $9426-$980F, no instruction in any .s file carries an immediate $97 or $98 high byte, and a scan of the whole 64K runtime image finds no little-endian pointer into $97B6-$980F. Rendering it as 10x9, 9x10, 15x6, 6x15, 5x18 or 18x5 glyph grids, as 45 vertically stacked glyph pairs (the tallDigitGlyphPairTable format) and as ten 3x3 window frame sets (the $9824 format) all produce incoherent art, so the exact structure is unresolved; the value range says it is a screen-code layout of some kind, most likely leftover from an earlier build. (confidence: low on the interpretation, high on the fact that nothing reads it)"
  },
  {
   "addr": "9810",
   "length": 20,
   "type": "byteTable",
   "name": "tallDigitGlyphPairTable",
   "description": "Ten 2-byte pairs, indexed by drawTallGlyph ($2BB7) as $9810 + symbol*2. The first code is drawn at row zp_18 and the second one row lower, so each pair forms a 8x16 pixel tall digit: 0=($49,$4A), 1=($4B,$4C), 2=($4D,$4E), 3=($4D,$4F), 4=($50,$51), 5=($52,$4F), 6=($52,$53), 7=($54,$55), 8=($56,$53), 9=($56,$51). Used for the big numbers on the console panels."
  },
  {
   "addr": "9824",
   "length": 90,
   "type": "byteTable",
   "name": "windowFrameTileSets",
   "description": "Ten 9-byte frame tile sets, each nine gameFont codes in row-major order (topLeft, top, topRight, left, centre, right, bottomLeft, bottom, bottomRight), selected through windowFramePtrTable ($2C49) and drawn by drawWindowFrame ($2BDD group) / drawFrameBox. $9824 = thin single box, $982D = thin box built from the $39-$40 pieces, $9836 = octagon with diagonal corners, $983F = solid box with diagonal shadow corners, $9848 = all $03 (solid fill), $9851 = all $00 (blank/erase), $985A = 50 percent dither box, $9863/$986C = heavy raised bevel with a dithered outer edge, $9875 = heavy solid bevel."
  },
  {
   "addr": "987E",
   "length": 64,
   "type": "byteTable",
   "name": "asciiToGlyphTable",
   "description": "Character code $20-$5F -> gameFont glyph index, 64 entries. printChar ($C01A) reads it as asciiToGlyphTable,Y with Y = char - $20 ($C02A). The chat-line editor at $C80A reads exactly the same table through the biased base $985E with the raw character code in X and rejects the character when the entry is $3B, which is why XREF lists a second label $985E for it. Layout: space -> 0, digits '0'-'9' -> $01-$0A, 'A'-'Z' -> $0B-$24, punctuation to the small symbol glyphs, '[' -> $28, ']' -> $29, '\\', '^', '_' -> $3B (not typeable)."
  },
  {
   "addr": "98BE",
   "length": 472,
   "type": "charset",
   "name": "consoleFontMulticolour",
   "description": "59 glyphs of 8x8, 8 bytes each: the multicolour / double-width character set of the $C000 text plotter, selected when bit 4 of the $D016 shadow $9001 is set ($C20B). Every pixel is drawn as the bit pair %11 (byte values $00,$03,$0C,$0F,$30,$33,$3C,$3F). Order: 0 blank, 1-10 digits 0-9, 11-36 letters A-Z, 37-58 punctuation and arrows. Stored positive on disk and inverted in place at boot by invertTextFonts ($031B), which EORs $98BE-$9C6D with $FF."
  },
  {
   "addr": "9A96",
   "length": 472,
   "type": "charset",
   "name": "consoleFontHires",
   "description": "The same 59 glyphs in single-width hires form ($9A96 = $98BE + $1D8), used when $9001 bit 4 is clear. Also inverted at boot by invertTextFonts."
  },
  {
   "addr": "9C6E",
   "length": 352,
   "type": "charset",
   "name": "terrainTileShapes",
   "description": "44 one-bit 8x8 terrain tiles, 8 bytes each, addressed as $9C6E + index*8 by setTerrainTilePtr ($2DBD). The index is mapCell - $40, or mapCell - $20 when the cell is below $40 ($27B2), so the lower contour codes $21-$34 and the upper contour codes $41-$54 share tiles $01-$14. Tiles: $00 blank ground, $01-$14 contour/ridge line segments, $15-$18 the four quadrants of a small round blob and $19-$1C its filled forms (river, cells $55-$5C), $1D-$20 dense noise (forest, $5D-$60), $21-$24 sparse dots (lower hill, $61-$64), $25-$26 very sparse dots (summit, $65-$66), $27 a boxed cross (the recycler, $67), $28-$2B four checkerboard blocks (end-zone/goal cells $68-$6B). The main program doubles each nybble through nybbleToMulticolourTable to draw them 16x16 in the tactical view; overlay B's drawRadarGlyph ($7396) uses the same base for the radar screens."
  },
  {
   "addr": "9DCE",
   "length": 160,
   "type": "charset",
   "name": "unitSymbolShapes",
   "description": "20 one-bit 8x8 unit symbols continuing the $9C6E table at indices $2C-$3F: five unit types x four facings, matching unitGlyphBaseTable at $0799 ($2C GRUNT, $30 RIDER, $34 BOOMER, $38 SPY, $3C COMCEN, plus unitFlags AND 3). They are the small single-cell symbols; the big 16x16 multicolour versions live at $6C5D in the main program."
  },
  {
   "addr": "9E6E",
   "length": 36,
   "type": "sprite",
   "name": "spareCursorShapes",
   "description": "Two 18-byte sprite fragments in the same format as cursorBlockShape: six 24-pixel rows drawing a 6x6 hollow box ($9E6E) and a 6x6 plus/cross ($9E80). Nothing in the disassembly reads them - they look like alternative cursor shapes kept next to the one that is used. (confidence: medium)"
  },
  {
   "addr": "9E92",
   "length": 18,
   "type": "sprite",
   "name": "cursorBlockShape",
   "description": "18 bytes = six 3-byte sprite rows drawing a small solid 4x4 block. setCursorShapeBlock ($2FFA) copies exactly $12 bytes from here into the top of sprite $91CC's shape ($8A00 + n*64) for the plain battlefield cursor."
  },
  {
   "addr": "9EA4",
   "length": 63,
   "type": "sprite",
   "name": "cursorBoxSprite",
   "description": "One complete 24x21 sprite (21 rows of 3 bytes): an 11x9 hollow rectangle centred in the sprite. showCursorBoxSprite ($3306) copies it into sprite 2 with X counting $3E..0; it is the normal battlefield centre cursor."
  },
  {
   "addr": "9EE3",
   "length": 63,
   "type": "sprite",
   "name": "crosshairSprite",
   "description": "One complete 24x21 sprite: a diamond crosshair with a vertical stem above and below and horizontal ticks. showCrosshairSprite ($3302) copies it into sprite 2 by loading X with $7D (= $9EA4 + $3F)."
  },
  {
   "addr": "9F22",
   "length": 21,
   "type": "sprite",
   "name": "comcenCrossMarkerShape",
   "description": "21 bytes = seven 3-byte sprite rows drawing a plus/cross marker. showComcenCrossMarker ($3383) copies it (LDY #$14, 21 bytes) into rows 7-13 of sprite 2 at $8A95 and colours it light red; overlay B calls it at $75B6 to mark the comcen on the strategic grid."
  },
  {
   "addr": "9F37",
   "length": 10,
   "type": "byteTable",
   "name": "circleDxPtrLoTable",
   "description": "Low bytes of the ten dx point-list pointers used by drawCircleInSprite ($3239), indexed by radius-1: $9F69,$9F6F,$9F73,$9F79,$9F7F,$9F8B,$9F97,$9FA1,$9FAF,$9FBF. Read at $323D into zp_AD."
  },
  {
   "addr": "9F41",
   "length": 10,
   "type": "byteTable",
   "name": "circleDxPtrHiTable",
   "description": "High bytes of the dx pointers, all $9F. Read at $3242 into zp_AE."
  },
  {
   "addr": "9F4B",
   "length": 10,
   "type": "byteTable",
   "name": "circleDyPtrLoTable",
   "description": "Low bytes of the ten dy point-list pointers: $9F6C,$9F71,$9F76,$9F7C,$9F85,$9F91,$9F9C,$9FA8,$9FB7,$9FC8. Read at $3247 into zp_AF."
  },
  {
   "addr": "9F55",
   "length": 10,
   "type": "byteTable",
   "name": "circleDyPtrHiTable",
   "description": "High bytes of the dy pointers, all $9F. Read at $324C into zp_B0."
  },
  {
   "addr": "9F5F",
   "length": 10,
   "type": "byteTable",
   "name": "circleStepCountTable",
   "description": "Highest point index of each circle radius 1-10: 2,1,2,2,5,5,4,6,7,8. drawCircleInSprite loads it into circleStepsLeft ($91F2) at $3251 and counts down to 0, so radius 1 plots 3 points (a filled 3x3 dot) and radius 10 plots 9."
  },
  {
   "addr": "9F69",
   "length": 104,
   "type": "byteTable",
   "name": "circlePointDeltaLists",
   "description": "The ten dx lists and ten dy lists themselves, interleaved so that each radius' dx list is immediately followed by its dy list (radius 1: dx $9F69 = 00,01,01 / dy $9F6C = 00,00,01; radius 10: dx $9FBF = 0A,0A,0A,09,09,08,07,07,08 / dy $9FC8 = 00,01,02,03,04,05,06,07,06). computeCircleOctantPoints reflects each (dx,dy) into the eight octants around the sprite centre (12,10)."
  },
  {
   "addr": "9FD1",
   "length": 47,
   "type": "unknown",
   "name": "graphicsBlockTail",
   "description": "47 bytes to the end of track 28 sector 12, not referenced by anything. Byte-for-byte a 50 percent dither: alternating $FF/$00 pairs whose phase flips every eight bytes, with a handful of stray values ($3F,$33,$C0,$F3,$30). The identical pattern appears in the tail of the $8800 block ($897F-$89FF, track 29 sector 10), so both are almost certainly the same stale bitmap fragment left in the mastering machine's memory. (confidence: medium)"
  }
 ],
 "misclassified": [],
 "insights": [
  "The block loaded from track 28 sectors 0-12 is the game's entire static graphics library, laid out with no gaps at all: six address tables ($9300-$93B3), fontColorTable, gameFont, one unreferenced 90-byte glyph-code block, the tall-digit pairs, the window frame sets, the ASCII map, the two 59-glyph console fonts, 44 terrain tiles + 20 unit symbols, five sprite shapes, the circle geometry, and 47 bytes of stale dither at the very end.",
  "Cell addressing for the whole game comes from here: calcCellPointersXY ($2A9F/$2AAC) builds zp_B4/B5 = $A000 + 320*row + 8*col from bitmapRowLo/Hi + bitmapColLo/Hi and zp_B6/B7 = $8C00 + 40*row + col from screenRowLo/Hi; colour RAM is the same pointer with $4C added to the high byte.",
  "There are two independent character sets in this one block. gameFont ($9426, 114 glyphs, colours in $93B4) is a tile/frame set used by the main program's plotGlyph; the console text plotter in the $C000 overlay uses the 59-glyph pair at $98BE (multicolour) and $9A96 (hires), which the boot-time routine invertTextFonts ($031B) EORs with $FF so the shipped disk holds them as positives.",
  "asciiToGlyphTable is addressed from two different bases: $987E with (char - $20) by printChar, and $985E with the raw character code by the chat-line editor at $C80A. Entry value $3B means 'this character has no glyph' and is how the editor rejects a keystroke.",
  "Terrain tiles and unit symbols share one 8-bytes-per-shape table at $9C6E: indices $00-$2B are the 44 terrain tiles (mapCell - $40, or mapCell - $20 below $40, so both contour rings draw the same art) and $2C-$3F are the 20 unit symbols (unitGlyphBaseTable $0799 + facing). That is why overlay B's radar routine can plot units and terrain through the same setTerrainTilePtr call.",
  "drawCircleInSprite is a full 8-way-symmetric Bresenham circle stored as data: 4 pointer tables + a step count + 20 short delta lists ($9F37-$9FD0) give radii 1-10, which the game uses for unit range rings and the blob markers on the drone/missile screens.",
  "$97B6-$980F (90 bytes of gameFont codes) is dead data - proven by the absence of any label, any immediate $97/$98 and any pointer into the range in the whole 64K image.",
  "graphicsData9300.s carries no names for anything past fontColorTable/gameFont; every block from $97B6 to $9FFF listed above is currently anonymous ($9800/$9C00 only have labels because the boot-time title colour RAM and screen RAM lived at the same addresses - boot/titleColorRam9800 and boot/titleScreenRam9C00 - which is why XREF.txt attributes $9800/$9C00 to two units at once).",
  "The file header says '$9300-$9FFF - screen address tables, fonts, terrain and unit graphics'; that is right, but it hides that the block also carries all ten window frame tile sets, the ASCII translation table, four complete sprites and the whole circle-drawing geometry."
 ]
}
