{ "_comment": "generated by tools/mergeSurvey.py from survey/*.json - do not edit by hand", "notes": { "FC50": { "unit": "game/highMemoryFBB8", "block": [ "multicolorFillPatterns: byteTable, 4 bytes. $00,$55,$AA,$FF: colour index 0-3 replicated into all four pixel pairs of a bitmap byte; indexed by plotColorIndex" ] }, "FC54": { "unit": "game/highMemoryFBB8", "block": [ "multicolorPixelMasks: byteTable, 4 bytes. $C0,$30,$0C,$03: bit mask of multicolour pixel pair (X & 3) within a bitmap byte" ] }, "FD70": { "unit": "game/highMemoryFBB8", "block": [ "arrowGlyphTable: sprite, 177 bytes. Leading byte $01 followed by eight 22-byte records: a 2-byte header ($01,$16 for the first, $01,$2D for the rest) and seven 3-byte rows holding an 8x7 arrow glyph in the middle byte (last row only 2 bytes). Glyphs are arrows for the 8 compass directions in clockwise order: N ($08,$1C,$7F,$08,$08,$08,$1C), NE, E, SE, S, SW, W, NW. No static reference to this table exists anywhere in the disassembly (no $FD7x/$FE/$FF pointers); it may be unused leftover data." ] }, "FE24": { "unit": "game/highMemoryFBB8", "block": [ "droneShapesRle: sprite, 367 bytes. RLE-compressed set of eight 24x21 sprites, first byte $05 = the escape marker. enterDroneScreen ($7FE6, overlay variant B) does LDA #$24 / LDY #$FE / JSR unpackRleTo0200, giving eight 64-byte shapes at $0200-$03FF that loadDirectionShapeSprite copies into the sprite data. The shapes are a top-down aeroplane silhouette (the drone) in the eight compass headings N, NE, E, SE, S, SW, W, NW. The stream ends with the run $05,$0E,$00 at $FF90-$FF92, which pads the buffer out to 512 bytes." ] }, "FF93": { "unit": "game/highMemoryFBB8", "block": [ "targetingReticleSprite: sprite, 63 bytes. One complete uncompressed 24x21 sprite: four corner brackets with a centre cross - the drone screen's targeting reticle. Copied into sprite 2 by overlay B at $80EB (LDA $FF93,Y) when the drone screen is built, so the drone starts over the comcen with this frame drawn around it. It ends exactly at $FFD1, the last byte of the relocated high-memory block." ] }, "FBB8": { "routine": [ "initLineStepper - Bresenham/DDA line set-up between the current cell ($93,$92) = (col,row) and the target cell ($95,$94). Computes |dRow| -> $8F, |dCol| -> $90, the step signs $96/$97 (+1 or -1), the major-axis length -> $8E and $91 (steps left), clears both error accumulators ($8C/$8D) and preloads the minor-axis accumulator with major/2. Used by the unit movement / path code in the main program ($1D57, $2512, $306F, $3771, $3A82, $42EC, $4559), by the comm module ($E80E, $E96F) and by overlay B to draw a shot/route line in the zoom window ($83A4).", "In: $92 = start row, $93 = start column, $94 = end row, $95 = end column", "Out: A = $91 = number of steps (major axis length, Z set if zero); $8C,$8D = 0 / major/2; $8E = major length; $8F,$90 = |dRow|,|dCol|; $96,$97 = row/col step (+1/-1)" ], "unit": "game/highMemoryFBB8" }, "FBFA": { "routine": [ "stepLine - Advances the line started by initLineStepper by one cell: adds |dRow| to $8C and |dCol| to $8D, and whenever an accumulator reaches the major length it wraps it and moves $92 (row) / $93 (col) by the step sign, recording the applied delta in $90F4 / $90F5. $A9 ends up 0 for an orthogonal step and 1 for a diagonal step (the major axis always advances); if no steps are left nothing moves and $A9 = 0. Returns the remaining step count so callers loop with BEQ.", "In: line state $8C-$8E,$8F,$90,$91,$92,$93,$96,$97", "Out: A = $91 (steps remaining, Z = line finished); $92/$93 advanced; $90F4 = row delta applied (-1/0/+1), $90F5 = column delta applied; $A9 = 1 if diagonal step, 0 otherwise" ], "unit": "game/highMemoryFBB8" }, "FC58": { "routine": [ "plotFatPixel - Plots a 'fat' multicolour pixel (one 2x2 hires block) of colour A (0-3) at X = 0..159, Y = 0..95 into the $A000 bitmap by drawing bitmap rows 2Y and 2Y+1 via plotBitmapPixelRow. X and Y are saved in self-modifying code and restored before returning. Only called from overlay B ($83E6 and the 2x2 marker at sub_84F1) to draw a shot/route line in the zoom window. The clip test is wrong: it returns early only when X >= 160 AND Y >= 96 (two BCCs into the plot path), so an out-of-range Y with a valid X is not rejected.", "In: A = colour index 0-3, X = fat pixel column 0-159, Y = fat pixel row 0-95", "Out: bitmap byte(s) modified; X and Y preserved; $FC4D-$FC4F, $B4/$B5 clobbered" ], "unit": "game/highMemoryFBB8" }, "FC74": { "routine": [ "plotBitmapPixelRow - Inner routine of plotFatPixel: for bitmap row Y (0-191) and multicolour column X (0-159) computes the byte address bitmapRowAddr[Y>>3] + (Y&7) + colOffset[X>>2] using the tables at $9300/$9319 (row lo/hi, 25 entries, $A000 + 320*row) and $9332/$935A (8*column lo/hi, 40 entries), then merges the 2-bit colour ($FC50[colour] & $FC54[X&3]) into the byte. Colour index must already be in $FC4D.", "In: X = pixel column 0-159, Y = bitmap row 0-191, $FC4D = colour index", "Out: one bitmap byte updated; $B4/$B5 = byte address; $FC4E = colour bits, $FC4F = keep mask; A,Y clobbered, X preserved" ], "unit": "game/highMemoryFBB8" }, "FCCD": { "routine": [ "multiply8x8 - Unsigned 8x8 -> 16 bit multiply by shift-and-add (8 iterations). Used by overlay A ($814A, $846B: value * Y for the option/limit editor) and the $EC00 sub-overlay ($ECD4: A * $96).", "In: A = multiplicand, Y = multiplier", "Out: A = Y = product high byte, $18 = product low byte, $19 = product high byte, $1A clobbered, X = 0" ], "unit": "game/highMemoryFBB8" }, "FCEC": { "routine": [ "divideSigned16by8 - Divides the signed 16-bit value Y:A (Y = high byte) by the unsigned 8-bit divisor X. A negative dividend is negated first, the quotient is negated afterwards (the remainder is that of the absolute value). Callers: $41E4 (timer mod 10/50 test), the comm module ($E8EA-$E90D) and the $EE00 sub-overlay ($EE86-$EEB6).", "In: X = divisor, A = dividend low, Y = dividend high (sign)", "Out: A = quotient (low 8 bits, sign restored), Y = remainder (of |dividend|), $18 = divisor, $19 clobbered" ], "unit": "game/highMemoryFBB8" }, "FD0A": { "routine": [ "divideUnsigned16by8 - Unsigned 16/8 restoring division, 8 iterations: dividend high byte in A, low byte in $19, divisor in $18. Entered directly from divideSigned16by8 for non-negative dividends. Quotient bits beyond 8 are lost.", "In: A = dividend high byte, $19 = dividend low byte, $18 = divisor", "Out: A = quotient (low byte), Y = remainder, $19 = quotient, X = 0" ], "unit": "game/highMemoryFBB8" }, "FD1E": { "routine": [ "addRandomJitter - Returns base value A plus a random offset +/- 2*k, where k is the number of consecutive 1 bits at the bottom of a scenario random byte (geometric distribution) capped at Y, and the sign comes from bit 7 of $5B. Y = 0 returns A unchanged. A negative (bit 7 set) result is clamped to 0. Overlay A's scenario generator uses it to scatter unit/terrain positions around a centre (callers $7036, $738B, $7398, which retry while the result is >= 40).", "In: A = base value, Y = maximum offset count; uses scenario RNG $5A-$5C", "Out: A = jittered value (0..127); $18, $19 clobbered; RNG state advanced", "(confidence: medium)" ], "unit": "game/highMemoryFBB8" }, "FD4A": { "routine": [ "nextScenarioRandom - 24-bit shift-register pseudo random generator on $5A/$5B/$5C, returns $5A EOR $5B. This generator is the scenario/map seed: it is zeroed at $7731, seeded from the game RNG at $77E7 or from the typed 5-character MAP ID code (5 bits per character via the 32-entry alphabet at $890C, $77F7-$7821), and consumed only by overlay A's battlefield/unit generator and $4805. Same algorithm as nextGameRandom.", "In: $5A-$5C RNG state", "Out: A = random byte, $5A-$5C advanced, C = 0" ], "unit": "game/highMemoryFBB8" }, "FD5D": { "routine": [ "nextGameRandom - 24-bit shift-register pseudo random generator on $57/$58/$59, returns $57 EOR $58. Seeded with the constants $49,$19,$02 by the start-up code at $0309-$0313, so both machines of a modem game run the same deterministic sequence; used throughout the main program, overlays, the sound code ($C862) and the comm module. NOTE: this is not an IRQ handler (docs/overview.md calls $FD5D-$FD6F the IRQ handler region; the IRQ vector actually points to $1298 and later $10F1).", "In: $57-$59 RNG state", "Out: A = random byte, $57-$59 advanced, C = 0" ], "unit": "game/highMemoryFBB8" }, "FE21": { "unit": "game/highMemoryFBB8", "block": [ "aircraftShapeTable: sprite, 367 bytes. Eight records, each a 2-3 byte header ($01,$17,$00,$05,$05,$0A / $05,$15 / $05,$18 / $05,$18 / $05,$15 / $05,$18 / $05,$11,$00 / $05,$12) followed by 14-15 rows of 3 bytes (24 px wide) that draw a top-down aeroplane/drone silhouette in 8 orientations (up, diagonal, left, diagonal, down, diagonal, right, diagonal); e.g. rows $02,$38,$80 / $0F,$FF,$E0 x3 / $08,$7C,$20 are the wings. The record framing is not fully resolved (header length and row alignment differ between records) and nothing references the table; probably stale data from the mastering system or an unused earlier unit graphic." ] }, "FF90": { "unit": "game/highMemoryFBB8", "block": [ "unknownShapeTail: unknown, 66 bytes. Starts with the same $05,$xx header pattern ($05,$0E) but the following bytes ($08,$78,$08,$08,..,$49,$00,$49) look like 8-pixel-wide character rows rather than 24-px rows; the sector continues past $FFD1 with stale bytes that resemble the end of the $8800 message block (track 29 s9-10) and were not moved by the loader, so this block is most likely unused leftover memory." ] } } }