{ "chunk": "data_main_0800", "unit": "game/main_0800", "routines": [], "variables": [], "dataBlocks": [ { "addr": "1DA2", "length": 66, "type": "byteTable", "name": "scrollViewParamTables", "description": "Twenty-two parallel 3-byte tables (one entry per direction: 0 = -1, 1 = 0, 2 = +1) that parameterise scrollViewBitmap ($1DE4), which shifts the 7x5-cell tactical view one cell without redrawing it. In order: $1DA2 bitmap copy start index ($5F,$00,$00), $1DA5 end index ($FF,$70,$60), $1DA8 the DEY/INY opcode patched into both copy loops ($88,$C8,$C8), $1DAB/$1DAE bitmap source and destination byte offsets (0,0,$10 / $10,0,0), $1DB1/$1DB4 bitmap source address lo/hi ($AAC8,$A208,$A488), $1DB7/$1DBA bitmap destination lo/hi ($AD48,$A208,$A208), $1DBD/$1DC0 bitmap row stride lo/hi ($FEC0 = -320 up, $0140 = +320), $1DC3 rows to copy (8,10,8), $1DC6/$1DC9 screen copy start/end index, $1DCC/$1DCF screen source/destination column offsets, $1DD2/$1DD5 screen source lo/hi ($8D59,$8C41,$8C91), $1DD8/$1DDB screen destination lo/hi ($8DA9,$8C41,$8C41) and $1DDE/$1DE1 screen row stride lo/hi ($FFD8 = -40 up, $0028 = +40). Colour RAM is handled by adding $4C to the screen high byte." }, { "addr": "2BDD", "length": 27, "type": "byteTable", "name": "windowLeftTable", "description": "Left screen column of the 27 predefined windows 0-26, read by drawWindowFrame with X = window index. 0 = the overview panel (column 0), 1 = the tactical view (column $18), 2 = the status panel, 5-$16 the $6F00 overlay screens, $18/$19 the main menu frames." }, { "addr": "2BF8", "length": 27, "type": "byteTable", "name": "windowTopTable", "description": "Top screen row of windows 0-26." }, { "addr": "2C13", "length": 27, "type": "byteTable", "name": "windowWidthTable", "description": "Width in character cells including the frame: window 0 is $16 = 22 wide (the 20x20 overview map plus its frame), window 1 is $10 = 16 (the 7x5-cell tactical view at 2 cells per map cell plus frame), window $18 is the full 40 columns." }, { "addr": "2C2E", "length": 27, "type": "byteTable", "name": "windowHeightTable", "description": "Height in character cells of windows 0-26 (window 0 = $16 = 22, window 1 = $0C = 12)." }, { "addr": "2C49", "length": 54, "type": "addrTable", "name": "windowFramePtrTable", "description": "27 little-endian pointers selecting each window's 9-byte frame tile set out of the ten sets at $9824-$987D in the graphics block. Windows 0-3 use the four decorated sets $985A/$9863/$986C/$9875, the rest reuse $9824 (thin box), $982D, $9836 (octagon), $983F (shadowed), $9848 (solid) and $9851 (blank/erase)." }, { "addr": "5A59", "length": 33, "type": "byteTable", "name": "overviewPatternLo", "description": "Low bytes of the overview-map pattern pointer for terrain index $00-$20 (33 entries), read by buildOverviewQuadrant at $5C40 with Y = mapCell - $40, or mapCell - $20 when the cell is below $40, clamped below $21 ($5BC3). Index 0 (blank ground) -> $5A9B; indices 1-8 cycle through the four contour-edge patterns $5A9F/$5AA3/$5AA7/$5AAB; 9-$0C -> $5AAF; $0D-$14 -> $5AB3; $15-$18 (river cells $55-$58) -> $5AC7/$5ACB/$5ACF/$5AD3; $19-$1C -> $5AC3; $1D-$20 (forest cells $5D-$60) -> $5ABB." }, { "addr": "5A7A", "length": 33, "type": "byteTable", "name": "overviewPatternHi", "description": "High bytes of the same 33 pointers - all $5A, because every pattern lives in this block." }, { "addr": "5A9B", "length": 60, "type": "byteTable", "name": "overviewPatterns", "description": "Fifteen patterns of four bitmap scanline bytes each. The overview draws each 2x2 block of map cells as one 8x8 character, so a pattern carries both horizontal halves as nibble pairs ($00/$33/$CC/$FF) and the quadrant mask picks the half that belongs to the cell. Contents: $5A9B 00 00 00 00 (blank), $5A9F 00 00 33 33, $5AA3 00 CC 33 33, $5AA7 33 00 00 00, $5AAB 33 CC 00 00 (the four contour edges), $5AAF 33 33 33 33, $5AB3 00 FF 00 00, $5AB7 FF FF FF FF (a live unit), $5ABB 33 CC 33 CC (forest checker), $5ABF FF 00 FF 00 (a cloaked unit), $5AC3 CC FF FF 33, $5AC7 33 FF FF FF, $5ACB CC FF FF FF, $5ACF FF FF FF 33, $5AD3 FF FF FF CC (the four river blob quadrants). $5AB7 and $5ABF are the only two not reachable through overviewPatternLo/Hi - buildOverviewQuadrant uses them directly for units." }, { "addr": "6782", "length": 5, "type": "byteTable", "name": "sidVoiceRegOffsetTable", "description": "SID register offset per voice, indexed by voice*2 so the driver can use the same X for its own state and for the chip: $00, (pad), $07, (pad), $0E." }, { "addr": "6787", "length": 11, "type": "byteTable", "name": "sndCommandSlotTable", "description": "Sound-script command 0-10 -> byte offset of its state slot from sndVoiceState ($6792): 0 duration ($00, also ends processing), 1 control/waveform ($01), 2 loop counter A ($06), 3 loop counter B ($07), 4/5 spare ($0C/$0D), 6 frequency ($12, 2 bytes), 7 pulse width ($18, 2), 8 AD/SR ($1E, 2), 9 frequency delta ($24, 2), 10 pulse delta ($2A, 2). runSoundScript adds voice*2 to the entry and treats every slot from $12 up as 16-bit ($66DF CPY #$12)." }, { "addr": "6792", "length": 48, "type": "byteTable", "name": "sndVoiceState", "description": "Work RAM, zero on disk: eight 6-byte rows of per-voice sound state (duration, control, loop counters, spares, frequency, pulse width, ADSR, and the two deltas), cleared per voice by startSound." }, { "addr": "67C2", "length": 6, "type": "byteTable", "name": "sndDriverVars", "description": "Work RAM, zero on disk: sndVolume, sndLastByte ($67C3, the current script byte), sndScratch, sndInitLock, sndCurrentSound, sndStartVoiceIndex." }, { "addr": "67C8", "length": 3, "type": "byteTable", "name": "sndVoiceSoundId", "description": "Work RAM, zero on disk: the sound id currently playing on each of the three voices (0 = free). $67CA is polled by the text engine at $C909/$C913 before it plays a UI beep." }, { "addr": "6831", "length": 174, "type": "wordTable", "name": "soundPointerTable", "description": "29 sound effects x three little-endian voice-script addresses (6 bytes per sound, $0000 = the sound does not use that voice). Sound $00 points all three voices at the 60-tick silence script $68DF (stop everything) and $1A/$1B/$1C silence voice 0/1/2 individually. Multi-voice effects are $06 ($6980/$69C9/$68DF), $0D ($6AA7/$6AB5/$6AC3), $0E ($6ADC/$6AEC/$6AFC), $15 ($68E7/$68F0/$68DF) and $19 ($6BFB/$6C15/$6C1B). Sounds $01-$07 and $0C use voice 0, $08-$0B voice 1 and $0F-$18 voice 2. messageSoundTable ($0612) picks the sound played with each status message." }, { "addr": "68DF", "length": 854, "type": "byteTable", "name": "soundScriptData", "description": "The bytecode for all the voice scripts, in the format interpreted by runSoundScript ($66C7): a byte $00-$0A stores the following one or two bytes into the state slot named by sndCommandSlotTable (slot 0 = duration ends the frame, duration 0 ends the voice); $40-$7F is a loop whose low six bits select a counter slot and which is followed by a 16-bit target address; $80-$FF adds the scenario random byte zp_5A to the frequency low byte (noise jitter). $68DF is the shared 60-tick silence script and $68E3 ('01 00 00 00') is the common end-of-sound target that the many '46 E3 68' jumps branch to." }, { "addr": "6C35", "length": 40, "type": "addrTable", "name": "unitGlyphPointerTable", "description": "20 little-endian pointers to the 32-byte unit glyphs that follow, indexed by unitType*4 + facing - the caller computes (unitFlags AND 3) + unitGlyphBaseTable[$0799 + unitType] - $2C and doubles it." }, { "addr": "6C5D", "length": 640, "type": "charset", "name": "unitGlyphShapes", "description": "20 multicolour unit glyphs of 16x16 pixels, 32 bytes each, stored as four 8-byte character cells in the order top-left, top-right, bottom-left, bottom-right. Every lit pixel is the bit pair %11, so the byte values are combinations of $00/$03/$0C/$30/$C0/$33/$3C/$F0/$FC/$FF. The 20 shapes are five unit types x four facings, all clearly recognisable when rendered: 0-3 GRUNT (a stubby figure with a head, up/down/left/right), 4-7 RIDER (a mounted figure with a wide base), 8-11 BOOMER (a heavy block with a protruding barrel), 12-15 SPY (a small chevron/arrow), 16-19 COMCEN (a large domed block). These are the big glyphs used in the 16x16-per-cell tactical view; the single-cell versions are the 8x8 shapes at $9DCE." }, { "addr": "6EDD", "length": 35, "type": "unknown", "name": "mainProgramPadding", "description": "$6EDD-$6EFF, all zero: filler to the end of the main program image (track 22-27). Unreferenced." } ], "misclassified": [], "insights": [ "There are no message strings in any of the pure-data tails of the main program - all of the game's text lives in the $0400 block, the $8800 block, the $C000 overlay and the $6F00 overlays.", "The 20 big unit glyphs at $6C5D and the 20 small ones at $9DCE are the same five types x four facings, reached through the same index arithmetic (unitGlyphBaseTable $0799 + facing): the tactical view uses the 16x16 multicolour pair table at $6C35, the overview map and the radar screens use the 8x8 one-bit shapes at $9C6E + code*8.", "The sound system is a three-voice bytecode interpreter: 29 sounds x 3 voice scripts in a pointer table ($6831), 854 bytes of script ($68DF-$6C34), an 11-entry command/slot map ($6787), a 3-entry SID register offset table ($6782) and 54 bytes of work RAM ($6792-$67C7). Scripts can loop with two counters and add RNG jitter to the frequency, which is how the engine gets varied explosion noises out of so little data.", "scrollViewBitmap never redraws the tactical view when the cursor moves one cell: it block-copies the bitmap, screen RAM and colour RAM by one cell using self-modified loops whose 22 parameters (including the DEY/INY opcode itself) come from the table group at $1DA2, then fills in only the newly exposed column and/or row.", "The 27 windows the game can draw are fully data-driven: left/top/width/height at $2BDD/$2BF8/$2C13/$2C2E and a frame style pointer at $2C49 that selects one of ten 9-glyph tile sets in the graphics block. Window 0 is the 22x22 overview frame and window 1 the 16x12 tactical frame.", "The strategic overview compresses two map cells per character in each axis, so each of the 33 terrain indices maps to a 4-byte pattern that already contains both horizontal halves; only 15 distinct patterns exist ($5A9B-$5AD6), including a solid $FF blob for a live unit and an $FF/$00 stripe for a cloaked one, which are the only two the index table never points at.", "$6792-$67CA is described in the listing as five byteTables; on disk it is all zero and at run time it is the sound driver's work RAM (eight 6-byte voice-state rows plus nine scalars), not table data. The individual D_6793..D_67CA labels are its fields.", "The $1DA2-$1DE3 tables are named individually in the listing but they are one column-store: 22 parallel arrays indexed by the same direction value 0-2, all consumed by the single routine scrollViewBitmap.", "$6C35 is listed as pointing at 'the 32-byte unit glyphs below' with only the first target named (unitGlyphShapes); the other 19 targets ($6C7D..$6EBD) have no labels at all." ] }