{
 "chunk": "game_textEngineC000_C000",
 "unit": "game/textEngineC000",
 "unitLabels": {
  "game/textEngineC000": {
   "C1F0": "plotGlyphCell",
   "C44E": "decimalHundreds",
   "C44F": "decimalTens",
   "C450": "decimalOnes"
  }
 },
 "notes": {
  "C000": {
   "unit": "game/textEngineC000",
   "block": [
    "Six small look-up tables that the boot loader kept here; only bitMaskTable_C008 is still used by the",
    "game.  The stale labels bootEntry/bootLoaderStatus/bootLoadSectors belong to boot/fastLoaderC000,",
    "which occupies the same addresses in the other build of this page."
   ],
   "line": "bitPairHighTable: a 2-bit value shifted up into bits 7-6 ($00,$40,$80,$C0); unreferenced in this overlay"
  },
  "C003": {
   "unit": "game/textEngineC000",
   "line": "fourth entry of bitPairHighTable (the label here is the boot loader's, not the text engine's)"
  },
  "C004": {
   "unit": "game/textEngineC000",
   "line": "solidColourByteTable: byte that paints a whole multicolour bitmap row in colour 0,1,2 or 3; unreferenced here"
  },
  "C008": {
   "unit": "game/textEngineC000",
   "line": "bitMaskTable_C008: single-bit masks $80..$01 indexed by bit number; read as L_C008,x by the sprite bit plotter at $30B9 (ORA = set) and $3217 (LDA = test/EOR)"
  },
  "C010": {
   "unit": "game/textEngineC000",
   "line": "pixelPairMaskTable: the four multicolour pixel-pair masks of a bitmap byte; unreferenced here"
  },
  "C014": {
   "unit": "game/textEngineC000",
   "line": "pixelPairClearMaskTable: complements of pixelPairMaskTable (clear one pixel pair); unreferenced here"
  },
  "C018": {
   "unit": "game/textEngineC000",
   "line": "two orphan bytes ($A9,$8D = 'lda #$8D' in the boot loader build); nothing in the overlay reads them"
  },
  "C01A": {
   "unit": "game/textEngineC000",
   "routine": [
    "printChar - the game's single character output routine.  Unless raw-glyph mode is on it strips the",
    "bit-7 string terminator, sends codes below $20 to handleControlChar and translates $20-$5F through",
    "the 64-entry ASCII-to-glyph table at $987E, then draws the glyph and advances the cursor.  A, X and",
    "Y are preserved, so callers can print inside their own loops.",
    "In:  A = character ($20-$5F ASCII, $08/$0D control, or a raw glyph index when rawGlyphMode zp_43",
    "     bit 7 is set); text window zp_39-zp_44; cursor cell pointers zp_B4-zp_B7",
    "Out: one glyph drawn into the bitmap plus its colour byte, cursor advanced (zp_3F/zp_40 and",
    "     zp_B2-zp_B7 updated); A/X/Y restored from zp_54-zp_56",
    "Called from: printStringAtPointer ($C16E/$C174), printDecimalDigits ($C1E7), drawTypedChatLine",
    "     ($C4E9) and ~15 sites in the main program and the $6F00 overlay"
   ],
   "line": "stash A/X/Y in zp_54-zp_56 so printing never disturbs the caller's registers"
  },
  "C01D": {
   "unit": "game/textEngineC000",
   "line": "raw glyph mode? (zp_43 bit 7 set = A is already a glyph number)"
  },
  "C01F": {
   "unit": "game/textEngineC000",
   "line": "yes - plot A unchanged, no ASCII translation and no control codes"
  },
  "C021": {
   "unit": "game/textEngineC000",
   "line": "drop bit 7, the marker that ends a string"
  },
  "C023": {
   "unit": "game/textEngineC000",
   "line": "below the first printable character?"
  },
  "C025": {
   "unit": "game/textEngineC000",
   "line": "$00-$1F is a control code (CR or backspace)"
  },
  "C027": {
   "unit": "game/textEngineC000",
   "line": "make it 0-based; carry is set on this path so exactly $20 is subtracted"
  },
  "C029": {
   "unit": "game/textEngineC000",
   "line": "use the character as the table index"
  },
  "C02A": {
   "unit": "game/textEngineC000",
   "line": "asciiToGlyphTable[c-$20] = glyph number 0-$3B (the table only covers $20-$5F)"
  },
  "C02D": {
   "unit": "game/textEngineC000",
   "line": "draw the glyph and step the cursor one cell to the right"
  },
  "C030": {
   "unit": "game/textEngineC000",
   "routine": [
    "restoreRegsAXY - reloads A, X and Y from the zp_54/zp_55/zp_56 save slots filled by saveRegsAXY.",
    "Tail of printChar; the viewport renderer borrows the same triple to recover a cell coordinate.",
    "In:  zp_54 (A), zp_55 (X), zp_56 (Y)",
    "Out: A, X and Y restored; N/Z reflect A",
    "Called from: printChar ($C03A), map viewport code $5CA1/$5D11/$5D17/$5DE7"
   ],
   "line": "the caller's Y"
  },
  "C032": {
   "unit": "game/textEngineC000",
   "line": "the caller's X"
  },
  "C034": {
   "unit": "game/textEngineC000",
   "line": "A last, so N/Z describe the character just printed"
  },
  "C036": {
   "unit": "game/textEngineC000",
   "line": "back to the caller"
  },
  "C037": {
   "unit": "game/textEngineC000",
   "line": "$00-$1F: handle carriage return / backspace"
  },
  "C03A": {
   "unit": "game/textEngineC000",
   "line": "then restore A/X/Y and return"
  },
  "C03D": {
   "unit": "game/textEngineC000",
   "routine": [
    "handleControlChar - the only two control codes printChar understands: $0D carriage return (falls",
    "into printNewline) and $08 backspace (step back one column, draw the blank glyph 0 over that cell,",
    "step back onto it again).  Every other code below $20 is ignored, which is how the $1E marker at",
    "the head of the chat line prints as nothing.",
    "In:  A = control code",
    "Out: cursor moved, possibly one cell erased, cell pointers recomputed",
    "Called from: printChar ($C037)"
   ],
   "line": "carriage return?"
  },
  "C03F": {
   "unit": "game/textEngineC000",
   "line": "no - test for backspace"
  },
  "C041": {
   "unit": "game/textEngineC000",
   "routine": [
    "printNewline - carriage return: cursor column back to 0 and the row down by one (by two when",
    "double-size text is selected), then recompute the cell pointers.  The window is never scrolled:",
    "the compare against the window bottom at $C04D-$C051 branches to the next instruction either way.",
    "In:  window/cursor state zp_39-zp_44",
    "Out: zp_3F = 0, zp_40 advanced, zp_B4-zp_B7 recomputed",
    "Called from: handleControlChar (fall through), plotCharAndAdvance ($C0D1) and ~20 sites in the",
    "     main program and the $6F00 overlay"
   ],
   "line": "column 0 ="
  },
  "C043": {
   "unit": "game/textEngineC000",
   "line": "back to the left edge of the window"
  },
  "C045": {
   "unit": "game/textEngineC000",
   "line": "down one text row"
  },
  "C047": {
   "unit": "game/textEngineC000",
   "line": "double-size characters? (zp_44 bit 7)"
  },
  "C049": {
   "unit": "game/textEngineC000",
   "line": "no - one row is enough"
  },
  "C04B": {
   "unit": "game/textEngineC000",
   "line": "a double-size glyph is two rows tall, so skip a second row"
  },
  "C04D": {
   "unit": "game/textEngineC000",
   "line": "vestigial: the new cursor row ..."
  },
  "C04F": {
   "unit": "game/textEngineC000",
   "line": "... compared against the window bottom row ..."
  },
  "C051": {
   "unit": "game/textEngineC000",
   "line": "... but both outcomes continue at $C053, so a window never scrolls"
  },
  "C053": {
   "unit": "game/textEngineC000",
   "line": "recompute the bitmap/colour pointers of the new cursor cell"
  },
  "C056": {
   "unit": "game/textEngineC000",
   "line": "backspace?"
  },
  "C058": {
   "unit": "game/textEngineC000",
   "line": "no - ignore this control code"
  },
  "C05A": {
   "unit": "game/textEngineC000",
   "line": "step the cursor one column back"
  },
  "C05C": {
   "unit": "game/textEngineC000",
   "line": "point the cell pointers at it"
  },
  "C05F": {
   "unit": "game/textEngineC000",
   "line": "glyph 0 is the blank glyph"
  },
  "C061": {
   "unit": "game/textEngineC000",
   "line": "erase that cell (printing leaves the cursor one column further right again)"
  },
  "C064": {
   "unit": "game/textEngineC000",
   "line": "so step back onto the erased cell"
  },
  "C066": {
   "unit": "game/textEngineC000",
   "line": "and recompute its cell pointers"
  },
  "C069": {
   "unit": "game/textEngineC000",
   "line": "unknown control code: do nothing"
  },
  "C06A": {
   "unit": "game/textEngineC000",
   "routine": [
    "clearTextWindow - homes the cursor to the top left of the current window and falls into",
    "clearToWindowBottom, so the whole window is filled with the blank glyph zp_3E in the colour zp_3D.",
    "The standard 'clear this panel' call of the whole game.",
    "In:  window zp_39-zp_3E",
    "Out: window filled, cursor left at its top left cell",
    "Called from: main program $1572/$1596/$1768/$261F/$2648/$26B5/$5E11/$6552/$659D"
   ],
   "line": "column 0 ="
  },
  "C06C": {
   "unit": "game/textEngineC000",
   "line": "left edge of the window"
  },
  "C06E": {
   "unit": "game/textEngineC000",
   "line": "first row of the window ="
  },
  "C070": {
   "unit": "game/textEngineC000",
   "line": "cursor row"
  },
  "C072": {
   "unit": "game/textEngineC000",
   "line": "compute the cell pointers of that top-left cell"
  },
  "C075": {
   "unit": "game/textEngineC000",
   "routine": [
    "clearToWindowBottom - fills with the blank glyph zp_3E from the cursor cell down to the window",
    "bottom row (exclusive), then puts the cursor back where it started.",
    "In:  cursor zp_3F/zp_40, window zp_3A/zp_3C, blank glyph zp_3E, colour zp_3D",
    "Out: cells filled; cursor unchanged on exit",
    "Called from: clearTextWindow (fall through), main program $24B5"
   ],
   "line": "stop when the cursor reaches the row below the window"
  },
  "C077": {
   "unit": "game/textEngineC000",
   "line": "into the shared fill loop"
  },
  "C07A": {
   "unit": "game/textEngineC000",
   "routine": [
    "clearToEndOfLine - the same fill loop with the stop row set to cursor row + 1, i.e. it blanks the",
    "rest of the current line only.  Used to pad the status bar after a message and by the overlay",
    "screens.  Both entries patch the stop row into the CMP operand at $C08F+1.",
    "In:  cursor zp_3F/zp_40, window width zp_3C, blank glyph zp_3E, colour zp_3D",
    "Out: rest of the line filled; cursor restored; the CMP operand at $C090 is rewritten",
    "Called from: clearMessageLine ($C3A5), printOnMessageLine ($C3F6), drawStatusScoreLine ($C6DF),",
    "     $6F00 overlay $776D/$8049/$811C/$8471"
   ],
   "line": "current row ..."
  },
  "C07C": {
   "unit": "game/textEngineC000",
   "line": "clear carry for the +1"
  },
  "C07D": {
   "unit": "game/textEngineC000",
   "line": "... plus one: stop as soon as the fill wraps onto the next line"
  },
  "C07F": {
   "unit": "game/textEngineC000",
   "line": "patch the stop row into the CMP at $C08F"
  },
  "C082": {
   "unit": "game/textEngineC000",
   "line": "remember the cursor row ..."
  },
  "C084": {
   "unit": "game/textEngineC000",
   "line": "... on the stack"
  },
  "C085": {
   "unit": "game/textEngineC000",
   "line": "and the cursor column ..."
  },
  "C087": {
   "unit": "game/textEngineC000",
   "line": "... too, both are restored when the fill is done"
  },
  "C088": {
   "unit": "game/textEngineC000",
   "line": "the current blank/fill glyph"
  },
  "C08A": {
   "unit": "game/textEngineC000",
   "line": "print it; the cursor advances and wraps to the next row at the window edge"
  },
  "C08D": {
   "unit": "game/textEngineC000",
   "line": "which row is the cursor on now?"
  },
  "C08F": {
   "unit": "game/textEngineC000",
   "line": "patched: stop row (window bottom, or cursor row + 1 for clearToEndOfLine)"
  },
  "C091": {
   "unit": "game/textEngineC000",
   "line": "keep filling while the cursor is still above the stop row"
  },
  "C093": {
   "unit": "game/textEngineC000",
   "line": "restore the starting column ..."
  },
  "C094": {
   "unit": "game/textEngineC000",
   "line": "put the cursor column back"
  },
  "C096": {
   "unit": "game/textEngineC000",
   "line": "... and the starting row (falls into setCursorRow, which stores it)"
  },
  "C097": {
   "unit": "game/textEngineC000",
   "routine": [
    "setCursorRow - cursor row = A, then recompute the cell pointers (falls into",
    "updateCursorPointers).  The usual 'move to row A' call; the column is left alone.",
    "In:  A = absolute screen row",
    "Out: zp_40 = A, zp_B4-zp_B7 recomputed",
    "Called from: clearToWindowBottom (fall through) and ~15 sites in the main program and the $6F00",
    "     overlay"
   ],
   "line": "cursor row = A"
  },
  "C099": {
   "unit": "game/textEngineC000",
   "routine": [
    "updateCursorPointers - recomputes the bitmap and colour pointers of the cursor cell: absolute",
    "column = cursor column + window left edge, row = cursor row, through calcCellPointersXY ($2AA3).",
    "In multicolour mode ($D016 shadow $9001 bit 4 set) it adds $4C to the high byte, turning the",
    "$8C00 video-matrix pointer into a $D800 colour-RAM pointer.  X and Y survive in self-modified",
    "immediate operands ($C0B9 and $C0BB) instead of on the stack.",
    "In:  zp_3F cursor column, zp_40 cursor row, zp_3B window left, $9001",
    "Out: zp_B4/zp_B5 = bitmap cell address, zp_B6/zp_B7 = colour cell address; X/Y preserved",
    "Called from: everywhere in this file plus ~30 sites in the main program and both $6F00 overlays"
   ],
   "line": "preserve X in the LDX operand at $C0B9"
  },
  "C09C": {
   "unit": "game/textEngineC000",
   "line": "preserve Y in the LDY operand at $C0BB"
  },
  "C09F": {
   "unit": "game/textEngineC000",
   "line": "clear carry for the column add"
  },
  "C0A0": {
   "unit": "game/textEngineC000",
   "line": "cursor column is relative to the window ..."
  },
  "C0A2": {
   "unit": "game/textEngineC000",
   "line": "... so add the window's left edge to get the screen column"
  },
  "C0A4": {
   "unit": "game/textEngineC000",
   "line": "X = screen column"
  },
  "C0A5": {
   "unit": "game/textEngineC000",
   "line": "Y = screen row (rows are absolute already)"
  },
  "C0A7": {
   "unit": "game/textEngineC000",
   "line": "-> zp_B4/B5 = $A000 + row*320 + col*8, zp_B6/B7 = $8C00 + row*40 + col"
  },
  "C0AA": {
   "unit": "game/textEngineC000",
   "line": "shadow of $D016 ..."
  },
  "C0AD": {
   "unit": "game/textEngineC000",
   "line": "... bit 4 = multicolour bitmap mode?"
  },
  "C0AF": {
   "unit": "game/textEngineC000",
   "line": "hires: the colour byte belongs in the video matrix at $8C00, leave the pointer alone"
  },
  "C0B1": {
   "unit": "game/textEngineC000",
   "line": "multicolour: the colour byte belongs in colour RAM ..."
  },
  "C0B3": {
   "unit": "game/textEngineC000",
   "line": "clear carry for the add"
  },
  "C0B4": {
   "unit": "game/textEngineC000",
   "line": "... so add $4C00 ($8C00 -> $D800)"
  },
  "C0B6": {
   "unit": "game/textEngineC000",
   "line": "zp_B6/zp_B7 now addresses colour RAM"
  },
  "C0B8": {
   "unit": "game/textEngineC000",
   "line": "patched by $C099: the caller's X"
  },
  "C0BA": {
   "unit": "game/textEngineC000",
   "line": "patched by $C09C: the caller's Y"
  },
  "C0BC": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C0BD": {
   "unit": "game/textEngineC000",
   "line": "dead: unreachable 'lda #$04 / rts' left over from an earlier build of this page"
  },
  "C0BF": {
   "unit": "game/textEngineC000",
   "line": "dead (see $C0BD)"
  },
  "C0C0": {
   "unit": "game/textEngineC000",
   "routine": [
    "plotCharAndAdvance - draws glyph A at the cursor cell, then advances the cursor one column (two",
    "in double-size mode), wrapping to the next line when it reaches the window width, and recomputes",
    "the cell pointers.",
    "In:  A = glyph number, window/cursor state zp_39-zp_44",
    "Out: glyph drawn, cursor advanced, zp_B4-zp_B7 updated",
    "Called from: printChar ($C02D), handleControlChar ($C061), the window fill loop ($C08A),",
    "     printFillChars ($C0F6)"
   ],
   "line": "draw the glyph into the cell the cursor points at"
  },
  "C0C3": {
   "unit": "game/textEngineC000",
   "line": "one column to the right"
  },
  "C0C5": {
   "unit": "game/textEngineC000",
   "line": "double-size characters? (zp_44 bit 7)"
  },
  "C0C7": {
   "unit": "game/textEngineC000",
   "line": "no"
  },
  "C0C9": {
   "unit": "game/textEngineC000",
   "line": "a double-size glyph is two cells wide"
  },
  "C0CB": {
   "unit": "game/textEngineC000",
   "line": "the new cursor column ..."
  },
  "C0CD": {
   "unit": "game/textEngineC000",
   "line": "... has it reached the right edge of the window?"
  },
  "C0CF": {
   "unit": "game/textEngineC000",
   "line": "no - stay on this line"
  },
  "C0D1": {
   "unit": "game/textEngineC000",
   "line": "wrap to column 0 of the next line"
  },
  "C0D4": {
   "unit": "game/textEngineC000",
   "line": "recompute the cell pointers for the new cursor position"
  },
  "C0D7": {
   "unit": "game/textEngineC000",
   "routine": [
    "setFullScreenWindow - resets the text window to the whole screen: top row 0, left column 0,",
    "bottom $19 (row 25, exclusive) and width $28 (40 columns).",
    "In:  none",
    "Out: zp_39 = 0, zp_3A = $19, zp_3B = 0, zp_3C = $28",
    "Called from: saveTextWindow ($C425), main program $1647/$2364/$2622/$2645/$2D41, $6F00 overlay",
    "     $6FF5/$70B3/$70DD/$7A78/$7B2B/$7B44"
   ],
   "line": "row 0 ="
  },
  "C0D9": {
   "unit": "game/textEngineC000",
   "line": "top of the window"
  },
  "C0DB": {
   "unit": "game/textEngineC000",
   "line": "column 0 ="
  },
  "C0DD": {
   "unit": "game/textEngineC000",
   "line": "left edge of the window"
  },
  "C0DF": {
   "unit": "game/textEngineC000",
   "line": "row 25 (exclusive) ="
  },
  "C0E1": {
   "unit": "game/textEngineC000",
   "line": "bottom of the window, i.e. all 25 rows"
  },
  "C0E3": {
   "unit": "game/textEngineC000",
   "line": "40 columns ="
  },
  "C0E5": {
   "unit": "game/textEngineC000",
   "line": "full screen width"
  },
  "C0E7": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C0E8": {
   "unit": "game/textEngineC000",
   "routine": [
    "saveRegsAXY - copies A, X and Y into the zp_54/zp_55/zp_56 scratch triple so restoreRegsAXY can",
    "put them back.  There is only one slot, so the pair must not be nested.",
    "In:  A, X, Y",
    "Out: zp_54 = A, zp_55 = X, zp_56 = Y",
    "Called from: printChar ($C01A), map viewport code $5C81"
   ],
   "line": "A save slot"
  },
  "C0EA": {
   "unit": "game/textEngineC000",
   "line": "X save slot"
  },
  "C0EC": {
   "unit": "game/textEngineC000",
   "line": "Y save slot"
  },
  "C0EE": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C0EF": {
   "unit": "game/textEngineC000",
   "routine": [
    "printFillChars - prints the current blank glyph zp_3E A times through plotCharAndAdvance; a count",
    "of 0 does nothing.  Used to pad fields, to draw the left pad of a centred string and to draw",
    "horizontal bars.",
    "In:  A = repeat count, zp_3E = fill glyph, zp_3D = colour",
    "Out: A cells filled, cursor advanced past them",
    "Called from: printStringAtPointer ($C15B), main program $15BD/$15CE/$15EC/$165E/$166E/$1728/",
    "     $1733/$19DB, $6F00 overlay $7727/$7F12/$7F1E/$8623"
   ],
   "line": "count of 0?"
  },
  "C0F1": {
   "unit": "game/textEngineC000",
   "line": "then there is nothing to draw"
  },
  "C0F3": {
   "unit": "game/textEngineC000",
   "line": "keep the remaining count on the stack"
  },
  "C0F4": {
   "unit": "game/textEngineC000",
   "line": "the blank/fill glyph"
  },
  "C0F6": {
   "unit": "game/textEngineC000",
   "line": "draw it and advance the cursor"
  },
  "C0F9": {
   "unit": "game/textEngineC000",
   "line": "get the count back"
  },
  "C0FA": {
   "unit": "game/textEngineC000",
   "line": "set carry so the subtract does not borrow"
  },
  "C0FB": {
   "unit": "game/textEngineC000",
   "line": "one cell done"
  },
  "C0FD": {
   "unit": "game/textEngineC000",
   "line": "keep going until the count runs out"
  },
  "C0FF": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C100": {
   "unit": "game/textEngineC000",
   "routine": [
    "printTableEntry - prints entry X of a table of fixed 8-byte, bit-7-terminated strings whose base",
    "address is passed in A (low) / Y (high): computes base + X*8 with a 16-bit shift and tail-calls",
    "printString.  Used for the unit type names ($C964), group names ($C92C), menu items and the",
    "STANDARD/CUSTOM words.",
    "In:  A/Y = table base, X = entry index",
    "Out: the string is printed at the cursor; zp_18/zp_19/zp_1A clobbered",
    "Called from: main program $15C9/$15D9/$22CD/$23B5/$242C/$2486/$5FCE/$6168/$6352, $6F00 overlay",
    "     $879C"
   ],
   "line": "table base, low byte"
  },
  "C102": {
   "unit": "game/textEngineC000",
   "line": "zero ..."
  },
  "C104": {
   "unit": "game/textEngineC000",
   "line": "clear the high byte of the offset being built"
  },
  "C106": {
   "unit": "game/textEngineC000",
   "line": "table base, high byte"
  },
  "C108": {
   "unit": "game/textEngineC000",
   "line": "A = entry index"
  },
  "C109": {
   "unit": "game/textEngineC000",
   "line": "index * 2 ..."
  },
  "C10A": {
   "unit": "game/textEngineC000",
   "line": "... carrying into the offset high byte"
  },
  "C10C": {
   "unit": "game/textEngineC000",
   "line": "index * 4 ..."
  },
  "C10D": {
   "unit": "game/textEngineC000",
   "line": "... carrying into the offset high byte"
  },
  "C10F": {
   "unit": "game/textEngineC000",
   "line": "index * 8 - the table entries are 8 bytes each"
  },
  "C110": {
   "unit": "game/textEngineC000",
   "line": "... and once more"
  },
  "C112": {
   "unit": "game/textEngineC000",
   "line": "clear carry for the 16-bit add"
  },
  "C113": {
   "unit": "game/textEngineC000",
   "line": "base low + offset low ..."
  },
  "C115": {
   "unit": "game/textEngineC000",
   "line": "= pointer low byte"
  },
  "C117": {
   "unit": "game/textEngineC000",
   "line": "offset high ..."
  },
  "C119": {
   "unit": "game/textEngineC000",
   "line": "... plus base high"
  },
  "C11B": {
   "unit": "game/textEngineC000",
   "line": "Y = pointer high byte"
  },
  "C11C": {
   "unit": "game/textEngineC000",
   "line": "A = pointer low byte"
  },
  "C11E": {
   "unit": "game/textEngineC000",
   "line": "print the bit-7 terminated string found there"
  },
  "C121": {
   "unit": "game/textEngineC000",
   "routine": [
    "printStringCentred - sets the centring field width to the current window width and tail-calls",
    "printString, so the string is padded on the left to sit centred in the window.  A survives the",
    "store across the stack.",
    "In:  A/Y = string pointer, zp_3C = window width",
    "Out: $90EF = window width (cleared again by printStringAtPointer); string printed centred",
    "Called from: main program $4C49/$5F5D/$638B/$6452/$64A8/$64B8/$64FA, $6F00 overlay $7714/$7EB0/",
    "     $8346/$83A4/$83E9/$85ED/$8780"
   ],
   "line": "keep the pointer low byte"
  },
  "C122": {
   "unit": "game/textEngineC000",
   "line": "the window width ..."
  },
  "C124": {
   "unit": "game/textEngineC000",
   "line": "... is the field the string is centred in"
  },
  "C127": {
   "unit": "game/textEngineC000",
   "line": "pointer low byte back"
  },
  "C128": {
   "unit": "game/textEngineC000",
   "line": "print it centred"
  },
  "C12B": {
   "unit": "game/textEngineC000",
   "routine": [
    "printNextStringCentred - like printStringCentred, but continues with the string that follows the",
    "one just printed: zp_A2/zp_A3 already points past the previous terminator, so this prints the next",
    "line of a multi-string block, centred.",
    "In:  zp_A2/zp_A3 = string pointer, zp_3C = window width",
    "Out: $90EF = window width; the next string is printed centred",
    "Called from: main program $6503, $6F00 overlay $83EC"
   ],
   "line": "the window width ..."
  },
  "C12D": {
   "unit": "game/textEngineC000",
   "line": "... is the field to centre in"
  },
  "C130": {
   "unit": "game/textEngineC000",
   "line": "keep printing where the previous string ended"
  },
  "C133": {
   "unit": "game/textEngineC000",
   "routine": [
    "printString - sets the string pointer zp_A2/zp_A3 from A/Y and falls into printStringAtPointer.",
    "The main text output call of the game; strings end with a character that has bit 7 set.",
    "In:  A = pointer low, Y = pointer high",
    "Out: string printed at the cursor, zp_A2/zp_A3 left just past the terminator",
    "Called from: ~25 sites in the main program, ~20 in the $6F00 overlays, and $C11E/$C128/$C3F3/",
    "     $C6C0 in this file"
   ],
   "line": "string pointer, low byte"
  },
  "C135": {
   "unit": "game/textEngineC000",
   "line": "string pointer, high byte"
  },
  "C137": {
   "unit": "game/textEngineC000",
   "routine": [
    "printStringAtPointer - prints the bit-7-terminated string at zp_A2/zp_A3.  When the centring width",
    "$90EF is non-zero it first measures the string and prints (width - length)/2 blanks in the pad",
    "colour zp_42 (the text colour is saved across the pad), then prints every character through",
    "printChar, terminator included, and finally clears $90EF so centring is a one-shot setting.",
    "In:  zp_A2/zp_A3 = string, $90EF = field width or 0, zp_42 = pad colour, zp_3E = blank glyph",
    "Out: text drawn, zp_A2/zp_A3 past the terminator, $90EF = 0; the SBC operand at $C159 is patched",
    "Called from: printString (fall through), printNextStringCentred ($C130), drawStatusScoreLine",
    "     ($C6CB/$C6D6), main program $1A2C/$1A3D/$1A45/$5EA9, $6F00 overlay $8276/$82E5/$8590/$8596/$863C"
   ],
   "line": "is a centring width set?"
  },
  "C13A": {
   "unit": "game/textEngineC000",
   "line": "no - print left aligned at the cursor"
  },
  "C13C": {
   "unit": "game/textEngineC000",
   "line": "save the text colour ..."
  },
  "C13E": {
   "unit": "game/textEngineC000",
   "line": "... across the padding"
  },
  "C13F": {
   "unit": "game/textEngineC000",
   "line": "the pad blanks use their own colour"
  },
  "C141": {
   "unit": "game/textEngineC000",
   "line": "the pad colour is the text colour while the blanks are drawn"
  },
  "C143": {
   "unit": "game/textEngineC000",
   "block": [
    "Measure the string: count characters until one with bit 7 set (the terminator) has been counted."
   ],
   "line": "start at the first character"
  },
  "C145": {
   "unit": "game/textEngineC000",
   "line": "X counts the characters"
  },
  "C147": {
   "unit": "game/textEngineC000",
   "line": "next character of the string"
  },
  "C149": {
   "unit": "game/textEngineC000",
   "line": "keep it for the bit-7 test"
  },
  "C14A": {
   "unit": "game/textEngineC000",
   "line": "strip the terminator bit (the value itself is not needed)"
  },
  "C14C": {
   "unit": "game/textEngineC000",
   "line": "next character position"
  },
  "C14D": {
   "unit": "game/textEngineC000",
   "line": "count this character, terminator included"
  },
  "C14E": {
   "unit": "game/textEngineC000",
   "line": "test the original character ..."
  },
  "C14F": {
   "unit": "game/textEngineC000",
   "line": "... loop until bit 7 was set"
  },
  "C151": {
   "unit": "game/textEngineC000",
   "line": "patch the length into the SBC at $C158"
  },
  "C154": {
   "unit": "game/textEngineC000",
   "line": "field width ..."
  },
  "C157": {
   "unit": "game/textEngineC000",
   "line": "set carry for the subtract"
  },
  "C158": {
   "unit": "game/textEngineC000",
   "line": "patched: string length including the terminator"
  },
  "C15A": {
   "unit": "game/textEngineC000",
   "line": "half of the slack: only the left side is padded"
  },
  "C15B": {
   "unit": "game/textEngineC000",
   "line": "print that many blanks in the pad colour"
  },
  "C15E": {
   "unit": "game/textEngineC000",
   "line": "restore the text colour for the string itself"
  },
  "C15F": {
   "unit": "game/textEngineC000",
   "line": "the string itself uses the caller's colour again"
  },
  "C161": {
   "unit": "game/textEngineC000",
   "block": [
    "Print loop: fetch a character, advance the pointer, print it; the character with bit 7 set is",
    "printed as well and ends the string."
   ],
   "line": "always read at offset 0 ..."
  },
  "C163": {
   "unit": "game/textEngineC000",
   "line": "... the character the pointer points at"
  },
  "C165": {
   "unit": "game/textEngineC000",
   "line": "advance the string pointer ..."
  },
  "C167": {
   "unit": "game/textEngineC000",
   "line": "no carry into the pointer high byte"
  },
  "C169": {
   "unit": "game/textEngineC000",
   "line": "... across a page boundary if need be"
  },
  "C16B": {
   "unit": "game/textEngineC000",
   "line": "test bit 7 without disturbing A"
  },
  "C16C": {
   "unit": "game/textEngineC000",
   "line": "bit 7 set: this is the last character"
  },
  "C16E": {
   "unit": "game/textEngineC000",
   "line": "print it"
  },
  "C171": {
   "unit": "game/textEngineC000",
   "line": "and go on with the next"
  },
  "C174": {
   "unit": "game/textEngineC000",
   "line": "print the terminating character too (printChar masks its bit 7 off)"
  },
  "C177": {
   "unit": "game/textEngineC000",
   "line": "centring is a one-shot setting ..."
  },
  "C179": {
   "unit": "game/textEngineC000",
   "line": "... so clear it for the next string"
  },
  "C17C": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C17D": {
   "unit": "game/textEngineC000",
   "routine": [
    "printDecimal - prints an unsigned 24-bit value in decimal without leading zeros: converts it into",
    "the digit buffer at $92DA and then prints the digits.",
    "In:  A = value low, Y = value middle, zp_7E = value high",
    "Out: digits printed; $92DA filled, zp_7E cleared, zp_18-zp_1F clobbered",
    "Called from: drawStatusScoreLine ($C6C8), printPlayerPoints ($C6EC), main program $1739/$22DA/",
    "     $23CE/$618D, $6F00 overlay $7EDC/$846E/$862A"
   ],
   "line": "value -> ASCII digits in $92DA"
  },
  "C180": {
   "unit": "game/textEngineC000",
   "line": "print them, most significant first"
  },
  "C183": {
   "unit": "game/textEngineC000",
   "routine": [
    "convertToDecimalDigits - converts the 24-bit value in A (low) / Y (middle) / zp_7E (high) to",
    "decimal by repeated division by 10: a 24-iteration shift/subtract loop leaves the quotient in",
    "zp_18-zp_1A and the digit in zp_1B.  Digits are stored as $B0 + digit (ASCII with bit 7 set)",
    "backwards from the end of the buffer at $92DA, so zp_1E ends up pointing at the most significant",
    "one.  A value of 0 still produces one digit.",
    "In:  A/Y/zp_7E = 24-bit value",
    "Out: $92DA+1..$92DA+8 digits, zp_1E = index of the first digit, zp_7E = 0, zp_18-zp_1F clobbered",
    "Called from: printDecimal ($C17D)"
   ],
   "line": "dividend, low byte"
  },
  "C185": {
   "unit": "game/textEngineC000",
   "line": "dividend, middle byte"
  },
  "C187": {
   "unit": "game/textEngineC000",
   "line": "the caller's high byte ..."
  },
  "C189": {
   "unit": "game/textEngineC000",
   "line": "... completes the 24-bit dividend"
  },
  "C18B": {
   "unit": "game/textEngineC000",
   "line": "the digit buffer holds 8 digits ($92DA+1..+8) ..."
  },
  "C18D": {
   "unit": "game/textEngineC000",
   "line": "... and is filled from its last slot backwards"
  },
  "C18F": {
   "unit": "game/textEngineC000",
   "block": [
    "Outer loop: one division by 10 per pass, lowest digit first, until the quotient is zero."
   ],
   "line": "clear the 24-bit remainder ..."
  },
  "C191": {
   "unit": "game/textEngineC000",
   "line": "remainder, low byte"
  },
  "C193": {
   "unit": "game/textEngineC000",
   "line": "remainder, middle byte"
  },
  "C195": {
   "unit": "game/textEngineC000",
   "line": "remainder, high byte"
  },
  "C197": {
   "unit": "game/textEngineC000",
   "line": "24 bits of dividend to shift through"
  },
  "C199": {
   "unit": "game/textEngineC000",
   "line": "bit counter for this division"
  },
  "C19B": {
   "unit": "game/textEngineC000",
   "line": "the first quotient bit shifts in as 0"
  },
  "C19C": {
   "unit": "game/textEngineC000",
   "block": [
    "Restoring division by 10: shift dividend:remainder left one bit, subtract 10 from the remainder",
    "and keep the result only if it did not borrow; the carry becomes the next quotient bit."
   ],
   "line": "shift the dividend left, its top bit ..."
  },
  "C19E": {
   "unit": "game/textEngineC000",
   "line": "dividend, middle byte"
  },
  "C1A0": {
   "unit": "game/textEngineC000",
   "line": "... entering the remainder"
  },
  "C1A2": {
   "unit": "game/textEngineC000",
   "line": "remainder, low byte"
  },
  "C1A4": {
   "unit": "game/textEngineC000",
   "line": "remainder, middle byte"
  },
  "C1A6": {
   "unit": "game/textEngineC000",
   "line": "remainder, high byte"
  },
  "C1A8": {
   "unit": "game/textEngineC000",
   "line": "no borrow into the trial subtraction"
  },
  "C1A9": {
   "unit": "game/textEngineC000",
   "line": "trial subtraction: remainder - 10, low byte"
  },
  "C1AB": {
   "unit": "game/textEngineC000",
   "line": "10 = the divisor"
  },
  "C1AD": {
   "unit": "game/textEngineC000",
   "line": "hold the result in Y ..."
  },
  "C1AE": {
   "unit": "game/textEngineC000",
   "line": "middle byte"
  },
  "C1B0": {
   "unit": "game/textEngineC000",
   "line": "only the borrow"
  },
  "C1B2": {
   "unit": "game/textEngineC000",
   "line": "... and in X ..."
  },
  "C1B3": {
   "unit": "game/textEngineC000",
   "line": "high byte"
  },
  "C1B5": {
   "unit": "game/textEngineC000",
   "line": "only the borrow"
  },
  "C1B7": {
   "unit": "game/textEngineC000",
   "line": "it borrowed: this quotient bit is 0, keep the old remainder"
  },
  "C1B9": {
   "unit": "game/textEngineC000",
   "line": "no borrow: the subtraction stands ..."
  },
  "C1BB": {
   "unit": "game/textEngineC000",
   "line": "middle byte of the reduced remainder"
  },
  "C1BD": {
   "unit": "game/textEngineC000",
   "line": "... and the carry makes this quotient bit a 1"
  },
  "C1BF": {
   "unit": "game/textEngineC000",
   "line": "one of the 24 bits done"
  },
  "C1C1": {
   "unit": "game/textEngineC000",
   "line": "24 shifts in all"
  },
  "C1C3": {
   "unit": "game/textEngineC000",
   "line": "shift the last quotient bit in: zp_18-zp_1A = value / 10"
  },
  "C1C5": {
   "unit": "game/textEngineC000",
   "line": "quotient, middle byte"
  },
  "C1C7": {
   "unit": "game/textEngineC000",
   "line": "quotient, high byte"
  },
  "C1C9": {
   "unit": "game/textEngineC000",
   "line": "remainder 0-9 = this decimal digit"
  },
  "C1CB": {
   "unit": "game/textEngineC000",
   "line": "clear carry for the add"
  },
  "C1CC": {
   "unit": "game/textEngineC000",
   "line": "+$B0 = '0'..'9' with bit 7 set (printChar masks it off again)"
  },
  "C1CE": {
   "unit": "game/textEngineC000",
   "line": "the current end of the digit buffer"
  },
  "C1D0": {
   "unit": "game/textEngineC000",
   "line": "store the digit there"
  },
  "C1D3": {
   "unit": "game/textEngineC000",
   "line": "the next digit goes one slot to the left"
  },
  "C1D5": {
   "unit": "game/textEngineC000",
   "line": "is the quotient ..."
  },
  "C1D7": {
   "unit": "game/textEngineC000",
   "line": "... zero in all ..."
  },
  "C1D9": {
   "unit": "game/textEngineC000",
   "line": "... three bytes?"
  },
  "C1DB": {
   "unit": "game/textEngineC000",
   "line": "no - divide again for the next digit"
  },
  "C1DD": {
   "unit": "game/textEngineC000",
   "line": "A is 0 here, so this also clears the caller's high byte zp_7E"
  },
  "C1DF": {
   "unit": "game/textEngineC000",
   "line": "point zp_1E at the most significant digit"
  },
  "C1E1": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C1E2": {
   "unit": "game/textEngineC000",
   "routine": [
    "printDecimalDigits - prints the digit buffer at $92DA from index zp_1E up to and including index",
    "8, i.e. the significant digits produced by convertToDecimalDigits.",
    "In:  zp_1E = index of the first digit, buffer $92DA",
    "Out: the digits are printed at the cursor",
    "Called from: printDecimal ($C180)"
   ],
   "line": "start at the most significant digit"
  },
  "C1E4": {
   "unit": "game/textEngineC000",
   "line": "digit as '0'+d with bit 7 set"
  },
  "C1E7": {
   "unit": "game/textEngineC000",
   "line": "print it"
  },
  "C1EA": {
   "unit": "game/textEngineC000",
   "line": "next digit"
  },
  "C1EB": {
   "unit": "game/textEngineC000",
   "line": "index 8 is the last (units) digit"
  },
  "C1ED": {
   "unit": "game/textEngineC000",
   "line": "loop over the remaining digits"
  },
  "C1EF": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C1F0": {
   "unit": "game/textEngineC000",
   "routine": [
    "plotGlyphCell - draws glyph A into the bitmap cell at zp_B4/zp_B5 and writes the colour byte zp_3D",
    "through zp_B6/zp_B7.  The glyph source is $98BE + A*8 in multicolour mode or $9A96 + A*8 in hires",
    "mode: two fonts $1D8 bytes apart with the same shapes, one drawn in bit pairs %11 (colour from",
    "colour RAM) and one in single bits (colour from the video matrix).  With double-size text selected",
    "it hands the glyph to plotGlyphDoubleSize instead.  The copy loop at $C227 reads the bitmap byte",
    "that is already there and the following opcode at $C22B is patched by setBitmapDrawMode ($2A41) to",
    "LDA/ORA/EOR/CMP (zp),Y, giving replace / OR / EOR / transparent drawing.",
    "In:  A = glyph number, zp_B4-zp_B7 cell pointers, zp_3D colour, zp_44 double-size flag, $9001",
    "Out: 8 bitmap bytes and one colour byte written, zp_B2/zp_B3 = glyph pointer; A preserved",
    "Called from: plotCharAndAdvance ($C0C0)"
   ],
   "line": "keep the glyph number - it is handed back to the caller"
  },
  "C1F1": {
   "unit": "game/textEngineC000",
   "line": "zero ..."
  },
  "C1F3": {
   "unit": "game/textEngineC000",
   "line": "clear the glyph pointer ..."
  },
  "C1F5": {
   "unit": "game/textEngineC000",
   "line": "... before shifting the offset into it"
  },
  "C1F7": {
   "unit": "game/textEngineC000",
   "line": "glyph * 2 ..."
  },
  "C1F8": {
   "unit": "game/textEngineC000",
   "line": "... carrying into the pointer high byte"
  },
  "C1FA": {
   "unit": "game/textEngineC000",
   "line": "glyph * 4 ..."
  },
  "C1FB": {
   "unit": "game/textEngineC000",
   "line": "... carrying into the pointer high byte"
  },
  "C1FD": {
   "unit": "game/textEngineC000",
   "line": "glyph * 8 - a glyph is 8 bytes"
  },
  "C1FE": {
   "unit": "game/textEngineC000",
   "line": "... carrying into the pointer high byte"
  },
  "C200": {
   "unit": "game/textEngineC000",
   "line": "clear carry for the base add"
  },
  "C201": {
   "unit": "game/textEngineC000",
   "line": "add the multicolour font base $98BE ..."
  },
  "C203": {
   "unit": "game/textEngineC000",
   "line": "glyph pointer, low byte"
  },
  "C205": {
   "unit": "game/textEngineC000",
   "line": "offset high byte ..."
  },
  "C207": {
   "unit": "game/textEngineC000",
   "line": "... high byte"
  },
  "C209": {
   "unit": "game/textEngineC000",
   "line": "glyph pointer, high byte"
  },
  "C20B": {
   "unit": "game/textEngineC000",
   "line": "shadow of $D016 ..."
  },
  "C20E": {
   "unit": "game/textEngineC000",
   "line": "... bit 4 = multicolour bitmap?"
  },
  "C210": {
   "unit": "game/textEngineC000",
   "line": "multicolour: the $98BE font is the right one"
  },
  "C212": {
   "unit": "game/textEngineC000",
   "line": "hires: add $01D8 to reach the identical ..."
  },
  "C214": {
   "unit": "game/textEngineC000",
   "line": "clear carry for the add"
  },
  "C215": {
   "unit": "game/textEngineC000",
   "line": "low byte of $01D8 ..."
  },
  "C217": {
   "unit": "game/textEngineC000",
   "line": "glyph pointer, low byte"
  },
  "C219": {
   "unit": "game/textEngineC000",
   "line": "... hires font at $9A96 (59 glyphs further on)"
  },
  "C21B": {
   "unit": "game/textEngineC000",
   "line": "high byte of $01D8 ..."
  },
  "C21D": {
   "unit": "game/textEngineC000",
   "line": "glyph pointer, high byte"
  },
  "C21F": {
   "unit": "game/textEngineC000",
   "line": "double-size text? (zp_44 bit 7)"
  },
  "C221": {
   "unit": "game/textEngineC000",
   "line": "no - draw it at normal size"
  },
  "C223": {
   "unit": "game/textEngineC000",
   "line": "restore the glyph number for the caller"
  },
  "C224": {
   "unit": "game/textEngineC000",
   "line": "the 2x2 magnifier draws it instead"
  },
  "C227": {
   "unit": "game/textEngineC000",
   "block": [
    "Copy the 8 rows of the glyph into the cell, bottom row first."
   ],
   "line": "8 bitmap rows, counted down"
  },
  "C229": {
   "unit": "game/textEngineC000",
   "line": "read the bitmap byte that is already there ..."
  },
  "C22B": {
   "unit": "game/textEngineC000",
   "line": "... patched by setBitmapDrawMode ($2A41) to LDA/ORA/EOR/CMP (zp),Y = replace/OR/EOR/transparent"
  },
  "C22D": {
   "unit": "game/textEngineC000",
   "line": "store the combined row back into the bitmap"
  },
  "C22F": {
   "unit": "game/textEngineC000",
   "line": "next row up"
  },
  "C230": {
   "unit": "game/textEngineC000",
   "line": "loop over all 8 rows of the cell"
  },
  "C232": {
   "unit": "game/textEngineC000",
   "line": "Y = 0: the cell's single colour byte"
  },
  "C233": {
   "unit": "game/textEngineC000",
   "line": "dead load - nothing patches this instruction, so the colour is always replaced"
  },
  "C235": {
   "unit": "game/textEngineC000",
   "line": "the text colour ..."
  },
  "C237": {
   "unit": "game/textEngineC000",
   "line": "... into colour RAM (multicolour) or the video matrix (hires)"
  },
  "C239": {
   "unit": "game/textEngineC000",
   "line": "hand the glyph number back"
  },
  "C23A": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C23B": {
   "unit": "game/textEngineC000",
   "block": [
    "pixelDoubleTable: 16 bytes, indexed by a nibble; each entry is that nibble with every pixel",
    "doubled ($1 -> $03, $F -> $FF).  Read by expandGlyphNibbles for the 2x magnifier."
   ],
   "line": "nibble $0-$3 doubled"
  },
  "C23F": {
   "unit": "game/textEngineC000",
   "line": "nibble $4-$B doubled (the label here is the boot loader's)"
  },
  "C247": {
   "unit": "game/textEngineC000",
   "line": "nibble $C-$D doubled"
  },
  "C249": {
   "unit": "game/textEngineC000",
   "line": "nibble $E-$F doubled"
  },
  "C24B": {
   "unit": "game/textEngineC000",
   "routine": [
    "plotGlyphDoubleSize - draws the glyph at zp_B2/zp_B3 magnified 2x2 into four character cells",
    "starting at zp_B4/zp_B5 (top-left, +8 top-right, +$138 more = one bitmap row down for bottom-left,",
    "+8 bottom-right) and colours them through colourDoubleSizeCell.  The 8 source bytes are copied to",
    "the work buffer $90B8 first; each quadrant expands 4 source rows and one nibble of each row.  The",
    "mask zp_BA suppresses quadrants (bit 0 top cells, bit 1 bottom cells, bit 2 left cells, bit 3 right",
    "cells) so a zoom rectangle can keep half-size edges - note zp_BA is only ever written by",
    "magnifyWindow2x, so double-size text uses whatever mask it left behind.  A, X and Y are preserved",
    "in self-modified operands ($C301, $C303, $C305).",
    "In:  zp_B2/zp_B3 = glyph, zp_B4-zp_B7 = destination cell, zp_3D = colour, zp_BA = quadrant mask",
    "Out: up to 4 cells (32 bitmap bytes) and their colour bytes written; $90B8-$90BF and zp_1A-zp_1D",
    "     clobbered; the operands at $C33A/$C33B/$C33D/$C33E are rewritten as it goes",
    "Called from: plotGlyphCell ($C224), magnifyWindow2x ($2E7B/$2EA5/$2EEA/$2F14)"
   ],
   "line": "preserve A in the LDA operand at $C305"
  },
  "C24E": {
   "unit": "game/textEngineC000",
   "line": "preserve Y in the LDY operand at $C303"
  },
  "C251": {
   "unit": "game/textEngineC000",
   "line": "preserve X in the LDX operand at $C301"
  },
  "C254": {
   "unit": "game/textEngineC000",
   "line": "8 glyph rows ..."
  },
  "C256": {
   "unit": "game/textEngineC000",
   "line": "... read from the font ..."
  },
  "C258": {
   "unit": "game/textEngineC000",
   "line": "... into the work buffer, where the expander can index them freely"
  },
  "C25B": {
   "unit": "game/textEngineC000",
   "line": "next row up"
  },
  "C25C": {
   "unit": "game/textEngineC000",
   "line": "until all 8 rows are in the buffer"
  },
  "C25E": {
   "unit": "game/textEngineC000",
   "line": "source rows 0-3 = the top half of the glyph"
  },
  "C260": {
   "unit": "game/textEngineC000",
   "line": "row offset into the work buffer"
  },
  "C262": {
   "unit": "game/textEngineC000",
   "block": [
    "Point the two STA operands in expandGlyphNibbles at the top-left cell: $C33A gets the cell address",
    "and $C33D that address + 1, so every expanded row is written to two consecutive bitmap rows."
   ],
   "line": "destination cell, low byte ..."
  },
  "C264": {
   "unit": "game/textEngineC000",
   "line": "... into the STA operand at $C33A"
  },
  "C267": {
   "unit": "game/textEngineC000",
   "line": "clear carry for the +1"
  },
  "C268": {
   "unit": "game/textEngineC000",
   "line": "the next bitmap row of the same cell ..."
  },
  "C26A": {
   "unit": "game/textEngineC000",
   "line": "... into the STA operand at $C33D"
  },
  "C26D": {
   "unit": "game/textEngineC000",
   "line": "destination cell, high byte ..."
  },
  "C26F": {
   "unit": "game/textEngineC000",
   "line": "... into the STA at $C33B"
  },
  "C272": {
   "unit": "game/textEngineC000",
   "line": "carry of the low byte"
  },
  "C274": {
   "unit": "game/textEngineC000",
   "line": "... and $C33E"
  },
  "C277": {
   "unit": "game/textEngineC000",
   "line": "quadrant mask ..."
  },
  "C279": {
   "unit": "game/textEngineC000",
   "line": "... bit 0 = skip the top pair of cells"
  },
  "C27B": {
   "unit": "game/textEngineC000",
   "line": "top row suppressed - jump straight to the bottom half"
  },
  "C27D": {
   "unit": "game/textEngineC000",
   "line": "top-left cell: the high nibble of source rows 0-3"
  },
  "C280": {
   "unit": "game/textEngineC000",
   "block": [
    "Step both destination addresses on by 8 bytes = one character cell to the right."
   ],
   "line": "clear carry for the 16-bit add"
  },
  "C281": {
   "unit": "game/textEngineC000",
   "line": "first destination ..."
  },
  "C284": {
   "unit": "game/textEngineC000",
   "line": "... plus 8 = the next cell"
  },
  "C286": {
   "unit": "game/textEngineC000",
   "line": "back into the STA operand"
  },
  "C289": {
   "unit": "game/textEngineC000",
   "line": "its high byte ..."
  },
  "C28C": {
   "unit": "game/textEngineC000",
   "line": "carry into the high byte"
  },
  "C28E": {
   "unit": "game/textEngineC000",
   "line": "back into the STA operand"
  },
  "C291": {
   "unit": "game/textEngineC000",
   "line": "second destination ... (relies on the carry left clear above)"
  },
  "C294": {
   "unit": "game/textEngineC000",
   "line": "... plus 8"
  },
  "C296": {
   "unit": "game/textEngineC000",
   "line": "back into the second STA operand"
  },
  "C299": {
   "unit": "game/textEngineC000",
   "line": "its high byte ..."
  },
  "C29C": {
   "unit": "game/textEngineC000",
   "line": "carry of the low byte"
  },
  "C29E": {
   "unit": "game/textEngineC000",
   "line": "back into the STA operand"
  },
  "C2A1": {
   "unit": "game/textEngineC000",
   "line": "quadrant mask ..."
  },
  "C2A3": {
   "unit": "game/textEngineC000",
   "line": "... top pair of cells still enabled?"
  },
  "C2A5": {
   "unit": "game/textEngineC000",
   "line": "no"
  },
  "C2A7": {
   "unit": "game/textEngineC000",
   "line": "top-right cell: the low nibble of source rows 0-3"
  },
  "C2AA": {
   "unit": "game/textEngineC000",
   "line": "source rows 4-7 = the bottom half of the glyph"
  },
  "C2AC": {
   "unit": "game/textEngineC000",
   "line": "row offset into the work buffer"
  },
  "C2AE": {
   "unit": "game/textEngineC000",
   "block": [
    "Step both destinations on by $138; together with the +8 above that is +320 = one character row",
    "down, i.e. the bottom-left cell."
   ],
   "line": "clear carry for the 16-bit add"
  },
  "C2AF": {
   "unit": "game/textEngineC000",
   "line": "first destination ..."
  },
  "C2B2": {
   "unit": "game/textEngineC000",
   "line": "... plus $138 ..."
  },
  "C2B4": {
   "unit": "game/textEngineC000",
   "line": "back into the STA operand"
  },
  "C2B7": {
   "unit": "game/textEngineC000",
   "line": "its high byte ..."
  },
  "C2BA": {
   "unit": "game/textEngineC000",
   "line": "... = +320 with the +8 above: one character row down"
  },
  "C2BC": {
   "unit": "game/textEngineC000",
   "line": "back into the STA operand"
  },
  "C2BF": {
   "unit": "game/textEngineC000",
   "line": "clear carry for the second 16-bit add"
  },
  "C2C0": {
   "unit": "game/textEngineC000",
   "line": "second destination ..."
  },
  "C2C3": {
   "unit": "game/textEngineC000",
   "line": "... plus $138 ..."
  },
  "C2C5": {
   "unit": "game/textEngineC000",
   "line": "back into the STA operand"
  },
  "C2C8": {
   "unit": "game/textEngineC000",
   "line": "its high byte ..."
  },
  "C2CB": {
   "unit": "game/textEngineC000",
   "line": "... = one character row down"
  },
  "C2CD": {
   "unit": "game/textEngineC000",
   "line": "back into the STA operand"
  },
  "C2D0": {
   "unit": "game/textEngineC000",
   "line": "quadrant mask ..."
  },
  "C2D2": {
   "unit": "game/textEngineC000",
   "line": "... bit 1 = skip the bottom pair of cells"
  },
  "C2D4": {
   "unit": "game/textEngineC000",
   "line": "bottom row suppressed - go and colour what was drawn"
  },
  "C2D6": {
   "unit": "game/textEngineC000",
   "line": "bottom-left cell: the high nibble of source rows 4-7"
  },
  "C2D9": {
   "unit": "game/textEngineC000",
   "line": "clear carry for the 16-bit add"
  },
  "C2DA": {
   "unit": "game/textEngineC000",
   "line": "first destination ..."
  },
  "C2DD": {
   "unit": "game/textEngineC000",
   "line": "... plus 8 = the bottom-right cell"
  },
  "C2DF": {
   "unit": "game/textEngineC000",
   "line": "back into the STA operand"
  },
  "C2E2": {
   "unit": "game/textEngineC000",
   "line": "its high byte ..."
  },
  "C2E5": {
   "unit": "game/textEngineC000",
   "line": "carry of the low byte"
  },
  "C2E7": {
   "unit": "game/textEngineC000",
   "line": "back into the STA operand"
  },
  "C2EA": {
   "unit": "game/textEngineC000",
   "line": "second destination ... (again relying on the clear carry)"
  },
  "C2ED": {
   "unit": "game/textEngineC000",
   "line": "... plus 8"
  },
  "C2EF": {
   "unit": "game/textEngineC000",
   "line": "back into the second STA operand"
  },
  "C2F2": {
   "unit": "game/textEngineC000",
   "line": "its high byte ..."
  },
  "C2F5": {
   "unit": "game/textEngineC000",
   "line": "carry of the low byte"
  },
  "C2F7": {
   "unit": "game/textEngineC000",
   "line": "back into the STA operand"
  },
  "C2FA": {
   "unit": "game/textEngineC000",
   "line": "bottom-right cell: the low nibble of source rows 4-7"
  },
  "C2FD": {
   "unit": "game/textEngineC000",
   "line": "write the colour byte into all four cells"
  },
  "C300": {
   "unit": "game/textEngineC000",
   "line": "patched by $C251: the caller's X"
  },
  "C302": {
   "unit": "game/textEngineC000",
   "line": "patched by $C24E: the caller's Y"
  },
  "C304": {
   "unit": "game/textEngineC000",
   "line": "patched by $C24B: the caller's A (the glyph number)"
  },
  "C306": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C307": {
   "unit": "game/textEngineC000",
   "routine": [
    "plotGlyphLeftHalf - quadrant helper of plotGlyphDoubleSize for a left-hand cell: returns at once",
    "when the mask suppresses left cells, otherwise selects the high nibble of each source row and runs",
    "the expander.",
    "In:  zp_BA quadrant mask, zp_1B = source row offset (0 top half, 4 bottom half)",
    "Out: 8 bitmap bytes of one cell written; zp_1D = $F0",
    "Called from: plotGlyphDoubleSize ($C27D top-left, $C2D6 bottom-left)"
   ],
   "line": "quadrant mask ..."
  },
  "C309": {
   "unit": "game/textEngineC000",
   "line": "... bit 2 = left-hand cells suppressed?"
  },
  "C30B": {
   "unit": "game/textEngineC000",
   "line": "no - draw it"
  },
  "C30D": {
   "unit": "game/textEngineC000",
   "line": "suppressed: nothing to do"
  },
  "C30E": {
   "unit": "game/textEngineC000",
   "line": "the high nibble is the left half of the glyph row"
  },
  "C310": {
   "unit": "game/textEngineC000",
   "line": "$F0 = keep the high nibble of every source row"
  },
  "C312": {
   "unit": "game/textEngineC000",
   "line": "expand it into the destination cell"
  },
  "C315": {
   "unit": "game/textEngineC000",
   "routine": [
    "plotGlyphRightHalf - the same for a right-hand cell: returns at once when the mask suppresses",
    "right cells, otherwise selects the low nibble of each source row and falls into the expander.",
    "In:  zp_BA quadrant mask, zp_1B = source row offset",
    "Out: 8 bitmap bytes of one cell written; zp_1D = $0F",
    "Called from: plotGlyphDoubleSize ($C2A7 top-right, $C2FA bottom-right)"
   ],
   "line": "quadrant mask ..."
  },
  "C317": {
   "unit": "game/textEngineC000",
   "line": "... bit 3 = right-hand cells suppressed?"
  },
  "C319": {
   "unit": "game/textEngineC000",
   "line": "no - draw it"
  },
  "C31B": {
   "unit": "game/textEngineC000",
   "line": "suppressed: nothing to do"
  },
  "C31C": {
   "unit": "game/textEngineC000",
   "line": "the low nibble is the right half of the glyph row"
  },
  "C31E": {
   "unit": "game/textEngineC000",
   "line": "$0F = keep the low nibble of every source row"
  },
  "C320": {
   "unit": "game/textEngineC000",
   "routine": [
    "expandGlyphNibbles - inner loop of the 2x magnifier: takes 4 glyph rows from the work buffer $90B8",
    "starting at offset zp_1B, masks each with zp_1D (shifting the high nibble down), doubles its pixels",
    "horizontally through pixelDoubleTable and stores the result to two bitmap rows one after the other",
    "(the self-modified absolute addresses at $C33A and $C33D), doubling it vertically as well.",
    "In:  $90B8 glyph rows, zp_1B = 0 or 4, zp_1D = $F0 or $0F, destination operands $C33A/$C33D",
    "Out: 8 bitmap bytes of one cell written; zp_1A = destination row counter",
    "Called from: plotGlyphLeftHalf ($C312), plotGlyphRightHalf (fall through)"
   ],
   "line": "first source row: 0 for the top half, 4 for the bottom half"
  },
  "C322": {
   "unit": "game/textEngineC000",
   "line": "zero ..."
  },
  "C324": {
   "unit": "game/textEngineC000",
   "line": "destination row within the cell = 0"
  },
  "C326": {
   "unit": "game/textEngineC000",
   "line": "one row of the glyph"
  },
  "C329": {
   "unit": "game/textEngineC000",
   "line": "keep only the half being magnified"
  },
  "C32B": {
   "unit": "game/textEngineC000",
   "line": "was that the high nibble ($F0 is negative)?"
  },
  "C32D": {
   "unit": "game/textEngineC000",
   "line": "no - the low nibble is already in place"
  },
  "C32F": {
   "unit": "game/textEngineC000",
   "line": "shift the high nibble down, 1 ..."
  },
  "C330": {
   "unit": "game/textEngineC000",
   "line": "... 2 ..."
  },
  "C331": {
   "unit": "game/textEngineC000",
   "line": "... 3 ..."
  },
  "C332": {
   "unit": "game/textEngineC000",
   "line": "... 4 places, into bits 0-3"
  },
  "C333": {
   "unit": "game/textEngineC000",
   "line": "index the doubling table with the nibble"
  },
  "C334": {
   "unit": "game/textEngineC000",
   "line": "the same 4 pixels, each one twice as wide"
  },
  "C337": {
   "unit": "game/textEngineC000",
   "line": "X = destination row within the cell"
  },
  "C339": {
   "unit": "game/textEngineC000",
   "line": "patched destination: cell address + X"
  },
  "C33C": {
   "unit": "game/textEngineC000",
   "line": "patched destination: cell address + 1 + X - the same row again, doubling vertically"
  },
  "C33F": {
   "unit": "game/textEngineC000",
   "line": "next source row"
  },
  "C340": {
   "unit": "game/textEngineC000",
   "line": "two destination rows ..."
  },
  "C342": {
   "unit": "game/textEngineC000",
   "line": "... per source row"
  },
  "C344": {
   "unit": "game/textEngineC000",
   "line": "destination row counter"
  },
  "C346": {
   "unit": "game/textEngineC000",
   "line": "8 bitmap rows = 4 source rows"
  },
  "C348": {
   "unit": "game/textEngineC000",
   "line": "loop over the 4 source rows"
  },
  "C34A": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C34B": {
   "unit": "game/textEngineC000",
   "routine": [
    "colourDoubleSizeCell - writes the colour byte zp_3D into the colour cells of the 2x2 block just",
    "drawn: the top pair unless the mask suppresses top cells, then the pointer is advanced by 40 (one",
    "screen row) and the bottom pair is written unless the mask suppresses bottom cells.",
    "In:  zp_B6/zp_B7 = colour cell pointer, zp_3D = colour, zp_BA = quadrant mask",
    "Out: up to 4 colour bytes written; zp_B6/zp_B7 advanced by one screen row",
    "Called from: plotGlyphDoubleSize ($C2FD)"
   ],
   "line": "redundant - colourCellPair sets Y itself"
  },
  "C34D": {
   "unit": "game/textEngineC000",
   "line": "quadrant mask ..."
  },
  "C34F": {
   "unit": "game/textEngineC000",
   "line": "... bit 0 = top cells suppressed?"
  },
  "C351": {
   "unit": "game/textEngineC000",
   "line": "yes - try the bottom pair"
  },
  "C353": {
   "unit": "game/textEngineC000",
   "line": "colour the top-left and top-right cells"
  },
  "C356": {
   "unit": "game/textEngineC000",
   "line": "quadrant mask ..."
  },
  "C358": {
   "unit": "game/textEngineC000",
   "line": "... bit 1 = bottom cells suppressed?"
  },
  "C35A": {
   "unit": "game/textEngineC000",
   "line": "yes - nothing more to colour"
  },
  "C35C": {
   "unit": "game/textEngineC000",
   "line": "clear carry for the +40"
  },
  "C35D": {
   "unit": "game/textEngineC000",
   "line": "advance the colour pointer ..."
  },
  "C35F": {
   "unit": "game/textEngineC000",
   "line": "... by 40 = one character row down"
  },
  "C361": {
   "unit": "game/textEngineC000",
   "line": "colour pointer, low byte"
  },
  "C363": {
   "unit": "game/textEngineC000",
   "line": "no carry - colour the bottom pair"
  },
  "C365": {
   "unit": "game/textEngineC000",
   "line": "carry into the high byte, then fall into colourCellPair"
  },
  "C367": {
   "unit": "game/textEngineC000",
   "routine": [
    "colourCellPair - writes zp_3D into the two horizontally adjacent colour cells at (zp_B6),0 and",
    "(zp_B6),1, skipping the left one when the mask suppresses left cells and the right one when it",
    "suppresses right cells.",
    "In:  zp_B6/zp_B7, zp_3D = colour, zp_BA = quadrant mask",
    "Out: up to 2 colour bytes written",
    "Called from: colourDoubleSizeCell ($C353 and the fall-through at $C363/$C365)"
   ],
   "line": "the left cell of the pair"
  },
  "C369": {
   "unit": "game/textEngineC000",
   "line": "quadrant mask ..."
  },
  "C36B": {
   "unit": "game/textEngineC000",
   "line": "... bit 2 = left cells suppressed?"
  },
  "C36D": {
   "unit": "game/textEngineC000",
   "line": "yes - skip it"
  },
  "C36F": {
   "unit": "game/textEngineC000",
   "line": "the text colour ..."
  },
  "C371": {
   "unit": "game/textEngineC000",
   "line": "... into the left cell"
  },
  "C373": {
   "unit": "game/textEngineC000",
   "line": "the right cell of the pair"
  },
  "C374": {
   "unit": "game/textEngineC000",
   "line": "quadrant mask ..."
  },
  "C376": {
   "unit": "game/textEngineC000",
   "line": "... bit 3 = right cells suppressed?"
  },
  "C378": {
   "unit": "game/textEngineC000",
   "line": "yes - skip it"
  },
  "C37A": {
   "unit": "game/textEngineC000",
   "line": "the text colour ..."
  },
  "C37C": {
   "unit": "game/textEngineC000",
   "line": "... into the right cell"
  },
  "C37E": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C37F": {
   "unit": "game/textEngineC000",
   "routine": [
    "waitFrames - busy-waits A frames by polling the raster register: per frame it waits until",
    "$D012 reads $F0 (line 240, in the lower border) and then until the raster leaves that line.",
    "Returns at once for A = 0.  Used for the pauses in the boot and menu sequences, in sound effects",
    "and in the modem dialogue.",
    "In:  A = number of frames",
    "Out: returns A frames later with A = 0",
    "Called from: main program $0CAF/$0F58/$1028/$1BA8/$1C14/$1C65/$409E/$5A3C, $6F00 overlay $7E76/",
    "     $80BB, modem driver $EE03"
   ],
   "line": "a count of 0 ..."
  },
  "C381": {
   "unit": "game/textEngineC000",
   "line": "... means no wait at all"
  },
  "C383": {
   "unit": "game/textEngineC000",
   "line": "keep the remaining frame count on the stack"
  },
  "C384": {
   "unit": "game/textEngineC000",
   "line": "current raster line ..."
  },
  "C387": {
   "unit": "game/textEngineC000",
   "line": "... is it line $F0 (240), below the visible screen?"
  },
  "C389": {
   "unit": "game/textEngineC000",
   "line": "no - keep polling"
  },
  "C38B": {
   "unit": "game/textEngineC000",
   "line": "wait while the raster is still on line $F0 ..."
  },
  "C38E": {
   "unit": "game/textEngineC000",
   "line": "... one whole frame has passed once it moves on"
  },
  "C390": {
   "unit": "game/textEngineC000",
   "line": "frame count back"
  },
  "C391": {
   "unit": "game/textEngineC000",
   "line": "set carry so the subtract does not borrow"
  },
  "C392": {
   "unit": "game/textEngineC000",
   "line": "one frame done"
  },
  "C394": {
   "unit": "game/textEngineC000",
   "line": "wait for the rest"
  },
  "C396": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C397": {
   "unit": "game/textEngineC000",
   "routine": [
    "waitRasterBelowScreen - spins until the raster is at line $E0 (224) or below, i.e. in the lower",
    "border, so the caller can rewrite the bitmap without tearing.",
    "In:  none",
    "Out: returns with A = the raster line, which is >= $E0",
    "Called from: main program $3CFA"
   ],
   "line": "current raster line"
  },
  "C39A": {
   "unit": "game/textEngineC000",
   "line": "below the visible screen (line 224)?"
  },
  "C39C": {
   "unit": "game/textEngineC000",
   "line": "not yet - keep spinning"
  },
  "C39E": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C39F": {
   "unit": "game/textEngineC000",
   "routine": [
    "clearMessageLine - blanks the status bar at the bottom of the screen: saves the caller's text",
    "window, selects the one-row message window, clears it and restores the window.",
    "In:  $90FB current screen, $9001 display mode",
    "Out: the status line is filled with the blank glyph; the text window is restored from $92B6",
    "Called from: showNextQueuedMessage ($C61B), promptPressButtonWhenReady ($C86A), main program",
    "     $102B/$10D9/$1B96/$2DDB/$576A, $6F00 overlay $81F1/$8792"
   ],
   "line": "stash the caller's text window"
  },
  "C3A2": {
   "unit": "game/textEngineC000",
   "line": "switch to the one-row status bar"
  },
  "C3A5": {
   "unit": "game/textEngineC000",
   "line": "blank it"
  },
  "C3A8": {
   "unit": "game/textEngineC000",
   "line": "and put the caller's window back"
  },
  "C3AB": {
   "unit": "game/textEngineC000",
   "routine": [
    "setMessageLineWindow - defines the text window as the one-row status bar: left column 2, width",
    "$24 (36 columns, i.e. columns 2-37), cursor at its left end, row $17 (23) - or $16 (22) on the",
    "console/main-menu screen, where the bar sits one row higher.  Text and pad colour become 0 in",
    "multicolour mode (colour RAM nibble) and 7 in hires mode (video matrix byte).",
    "In:  $90FB current screen (0 map, 1 missile, 2 drone, 3 console/menu), $9001 display mode",
    "Out: zp_3A-zp_3D, zp_3F, zp_40 and zp_42 set; cell pointers recomputed",
    "Called from: clearMessageLine ($C3A2), printOnMessageLine ($C3EC), drawStatusScoreLine ($C6B9)"
   ],
   "line": "36 columns wide ..."
  },
  "C3AD": {
   "unit": "game/textEngineC000",
   "line": "= the window width"
  },
  "C3AF": {
   "unit": "game/textEngineC000",
   "line": "... starting at column 2, so the bar covers columns 2-37"
  },
  "C3B1": {
   "unit": "game/textEngineC000",
   "line": "= the window's left edge"
  },
  "C3B3": {
   "unit": "game/textEngineC000",
   "line": "cursor to the left end of the bar"
  },
  "C3B5": {
   "unit": "game/textEngineC000",
   "line": "= the cursor column"
  },
  "C3B7": {
   "unit": "game/textEngineC000",
   "line": "row 23 on the battlefield, missile and drone screens"
  },
  "C3B9": {
   "unit": "game/textEngineC000",
   "line": "which screen is on show?"
  },
  "C3BC": {
   "unit": "game/textEngineC000",
   "line": "3 = the console / main menu screen"
  },
  "C3BE": {
   "unit": "game/textEngineC000",
   "line": "no - keep row 23"
  },
  "C3C0": {
   "unit": "game/textEngineC000",
   "line": "on the console screen the bar sits one row higher, on row 22"
  },
  "C3C2": {
   "unit": "game/textEngineC000",
   "line": "the window is that single row ..."
  },
  "C3C4": {
   "unit": "game/textEngineC000",
   "line": "... and the cursor starts on it"
  },
  "C3C6": {
   "unit": "game/textEngineC000",
   "line": "colour 7 for the hires screens"
  },
  "C3C8": {
   "unit": "game/textEngineC000",
   "line": "shadow of $D016 ..."
  },
  "C3CB": {
   "unit": "game/textEngineC000",
   "line": "... bit 4 = multicolour bitmap?"
  },
  "C3CD": {
   "unit": "game/textEngineC000",
   "line": "hires - keep 7"
  },
  "C3CF": {
   "unit": "game/textEngineC000",
   "line": "multicolour: colour RAM nibble 0"
  },
  "C3D1": {
   "unit": "game/textEngineC000",
   "line": "text colour ..."
  },
  "C3D3": {
   "unit": "game/textEngineC000",
   "line": "... and the colour of the padding blanks"
  },
  "C3D5": {
   "unit": "game/textEngineC000",
   "line": "compute the cell pointers of the first cell of the bar"
  },
  "C3D8": {
   "unit": "game/textEngineC000",
   "routine": [
    "showMessageString - prints the bit-7-terminated string A/Y centred in the 36-column status bar and",
    "restores the caller's text window.  The standard 'put this text in the status bar' call, used for",
    "disk errors, queued messages and the clock line.",
    "In:  A/Y = string pointer",
    "Out: the status line is rewritten; the text window is restored",
    "Called from: updateStatusMessage ($C53C), showMessageById ($C5EB), promptPressButtonWhenReady",
    "     ($C85F), main program $0816/$1023"
   ],
   "line": "centre across all 36 columns of the bar"
  },
  "C3DA": {
   "unit": "game/textEngineC000",
   "line": "draw the string there"
  },
  "C3DD": {
   "unit": "game/textEngineC000",
   "line": "and put the caller's window back"
  },
  "C3E0": {
   "unit": "game/textEngineC000",
   "routine": [
    "printOnMessageLine - worker behind showMessageString: remembers the string pointer in the operands",
    "at $C3F0/$C3F2, takes the centring width from X, saves the text window, selects the status bar,",
    "prints the string and pads the rest of the line.  It leaves the window set to the status bar, so",
    "the caller must restore it.",
    "In:  A/Y = string pointer, X = centring width (0 = left aligned)",
    "Out: the status line is drawn; the text window is still the message window on return",
    "Called from: showMessageString ($C3DA), drawTypedChatLine ($C4E0)"
   ],
   "line": "patch the pointer low byte into the LDA at $C3EF"
  },
  "C3E3": {
   "unit": "game/textEngineC000",
   "line": "and the high byte into the LDY at $C3F1"
  },
  "C3E6": {
   "unit": "game/textEngineC000",
   "line": "X = the field width to centre in, 0 = left aligned"
  },
  "C3E9": {
   "unit": "game/textEngineC000",
   "line": "stash the caller's text window"
  },
  "C3EC": {
   "unit": "game/textEngineC000",
   "line": "switch to the status bar"
  },
  "C3EF": {
   "unit": "game/textEngineC000",
   "line": "patched: string pointer low"
  },
  "C3F1": {
   "unit": "game/textEngineC000",
   "line": "patched: string pointer high"
  },
  "C3F3": {
   "unit": "game/textEngineC000",
   "line": "print it (centred if a width was given)"
  },
  "C3F6": {
   "unit": "game/textEngineC000",
   "line": "pad the rest of the bar with blanks"
  },
  "C3F9": {
   "unit": "game/textEngineC000",
   "routine": [
    "advanceFillPointer - loop helper of the block fill/clear routines: compares the 16-bit pointer",
    "zp_48/zp_49 with the last address zp_4A/zp_4B (carry set once the pointer has reached it), then",
    "steps the pointer and a 16-bit counter that nothing ever reads.  Callers store a byte and loop",
    "while the carry is clear.",
    "In:  zp_48/zp_49 = current address, zp_4A/zp_4B = last address",
    "Out: C = 1 when the pointer had reached the limit; zp_48/zp_49 and zp_4E/zp_4F incremented",
    "Called from: memory clear $0C44, the setup block at $0333, modem driver $EC16/$EC8B"
   ],
   "line": "16-bit compare of the fill pointer ..."
  },
  "C3FB": {
   "unit": "game/textEngineC000",
   "line": "low bytes ..."
  },
  "C3FD": {
   "unit": "game/textEngineC000",
   "line": "... against the last address to fill ..."
  },
  "C3FF": {
   "unit": "game/textEngineC000",
   "line": "... leaving carry set once it has been reached"
  },
  "C401": {
   "unit": "game/textEngineC000",
   "line": "step to the next byte ..."
  },
  "C403": {
   "unit": "game/textEngineC000",
   "line": "no carry into the high byte"
  },
  "C405": {
   "unit": "game/textEngineC000",
   "line": "... across a page boundary if need be"
  },
  "C407": {
   "unit": "game/textEngineC000",
   "line": "count the bytes filled ..."
  },
  "C409": {
   "unit": "game/textEngineC000",
   "line": "no carry into the high byte"
  },
  "C40B": {
   "unit": "game/textEngineC000",
   "line": "... in a 16-bit counter nothing ever reads"
  },
  "C40D": {
   "unit": "game/textEngineC000",
   "line": "carry tells the caller whether to stop"
  },
  "C40E": {
   "unit": "game/textEngineC000",
   "routine": [
    "restoreTextWindow - copies the 12 saved bytes at $92B6 back into zp_39-zp_44 (window geometry,",
    "colours, cursor position and mode flags) and recomputes the cell pointers.  Counterpart of",
    "saveTextWindow; there is only one save slot, so the pair cannot be nested.",
    "In:  $92B6-$92C1",
    "Out: zp_39-zp_44 restored, zp_B4-zp_B7 recomputed",
    "Called from: clearMessageLine ($C3A8), showMessageString ($C3DD), drawTypedChatLine ($C4EC),",
    "     handlePauseKey ($C761/$C79A), drawStatusScoreLine ($C6E2), main program $173C/$19D3/$19DE"
   ],
   "line": "12 bytes: zp_39 up to zp_44"
  },
  "C410": {
   "unit": "game/textEngineC000",
   "line": "byte of the saved window state ..."
  },
  "C413": {
   "unit": "game/textEngineC000",
   "line": "... back into the zero-page text variables"
  },
  "C415": {
   "unit": "game/textEngineC000",
   "line": "next byte"
  },
  "C416": {
   "unit": "game/textEngineC000",
   "line": "all 12 of them"
  },
  "C418": {
   "unit": "game/textEngineC000",
   "line": "recompute the cell pointers for the restored cursor"
  },
  "C41B": {
   "unit": "game/textEngineC000",
   "routine": [
    "saveTextWindow - saves zp_39-zp_44 (the whole text engine state) into the single slot at $92B6 and",
    "then hands the caller a full-screen window.  Used before drawing the status bar, the repair window",
    "and anything else that must not disturb the caller's window.",
    "In:  zp_39-zp_44",
    "Out: $92B6-$92C1 = the saved state; the window is reset to 0,0,25,40",
    "Called from: clearMessageLine ($C39F), printOnMessageLine ($C3E9), drawStatusScoreLine ($C6B6),",
    "     handlePauseKey ($C75B/$C791), main program $16FF/$19E1"
   ],
   "line": "12 bytes: zp_39 up to zp_44"
  },
  "C41D": {
   "unit": "game/textEngineC000",
   "line": "text engine variable ..."
  },
  "C41F": {
   "unit": "game/textEngineC000",
   "line": "... into the save slot"
  },
  "C422": {
   "unit": "game/textEngineC000",
   "line": "next byte"
  },
  "C423": {
   "unit": "game/textEngineC000",
   "line": "all 12 of them"
  },
  "C425": {
   "unit": "game/textEngineC000",
   "line": "leave the caller with a full-screen window"
  },
  "C428": {
   "unit": "game/textEngineC000",
   "routine": [
    "splitDecimalDigits - splits the byte in A into three decimal digits by repeated subtraction:",
    "units into decimalOnes, tens into decimalTens, hundreds into decimalHundreds.  The digits are used",
    "as indices for drawTallGlyph by the drone fuel gauge and the missile counter.",
    "In:  A = value 0-255",
    "Out: decimalHundreds/decimalTens/decimalOnes ($C44E-$C450); X clobbered",
    "Called from: $6F00 overlay $6F9C (drone fuel) and $7ABD (missile count)"
   ],
   "line": "X will count the tens"
  },
  "C42A": {
   "unit": "game/textEngineC000",
   "line": "no borrow into the first subtraction"
  },
  "C42B": {
   "unit": "game/textEngineC000",
   "line": "one more ten ..."
  },
  "C42C": {
   "unit": "game/textEngineC000",
   "line": "... subtract it"
  },
  "C42E": {
   "unit": "game/textEngineC000",
   "line": "keep going until it borrows"
  },
  "C430": {
   "unit": "game/textEngineC000",
   "line": "put the last ten back: A = value mod 10"
  },
  "C432": {
   "unit": "game/textEngineC000",
   "line": "the units digit"
  },
  "C435": {
   "unit": "game/textEngineC000",
   "line": "A = value / 10"
  },
  "C436": {
   "unit": "game/textEngineC000",
   "line": "X will count the hundreds"
  },
  "C438": {
   "unit": "game/textEngineC000",
   "line": "no borrow into the first subtraction"
  },
  "C439": {
   "unit": "game/textEngineC000",
   "line": "one more hundred ..."
  },
  "C43A": {
   "unit": "game/textEngineC000",
   "line": "... subtract ten from the tens value"
  },
  "C43C": {
   "unit": "game/textEngineC000",
   "line": "keep going until it borrows"
  },
  "C43E": {
   "unit": "game/textEngineC000",
   "line": "put the last ten back: A = tens digit"
  },
  "C440": {
   "unit": "game/textEngineC000",
   "line": "the tens digit"
  },
  "C443": {
   "unit": "game/textEngineC000",
   "line": "A = value / 100"
  },
  "C444": {
   "unit": "game/textEngineC000",
   "line": "dead leading-zero test: ..."
  },
  "C446": {
   "unit": "game/textEngineC000",
   "line": "... A is already 0 on this path ..."
  },
  "C448": {
   "unit": "game/textEngineC000",
   "line": "... and both branches continue at $C44A"
  },
  "C44A": {
   "unit": "game/textEngineC000",
   "line": "the hundreds digit"
  },
  "C44D": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C44E": {
   "unit": "game/textEngineC000",
   "block": [
    "The three digits produced by splitDecimalDigits, stored in the code page itself."
   ],
   "line": "hundreds digit; read by the drone fuel display ($6FD6)"
  },
  "C44F": {
   "unit": "game/textEngineC000",
   "line": "tens digit; read at $6FDE (fuel) and $7AD5 (missiles left)"
  },
  "C450": {
   "unit": "game/textEngineC000",
   "line": "units digit; read at $6FE6 (fuel) and $7ADD (missiles left)"
  },
  "C451": {
   "unit": "game/textEngineC000",
   "routine": [
    "checkUiContinue - the shared 'may this modal panel keep running' test used by the menu selector,",
    "the unit info panel and the group editor.  Returns A = 1 to carry on.  It returns A = 0 and arms a",
    "60-frame input lockout when the end-of-game sequence has started (and we are not watching a film),",
    "when a function key has asked for another screen, when SPACE was typed, or when the setup-phase",
    "test at checkSetupUiContinue says stop; a pause or gamePhase bits 6/7 keep the panel alive.",
    "In:  $0B9B film flag, $92C9 end-of-game counter, zp_BE pause state, zp_B1 game phase,",
    "     zp_66 requested screen key, $9248 last typed key",
    "Out: A = 1 continue / 0 abort; $0B7D = $3C (60 frames of input lockout) on abort",
    "Called from: main program $21D9/$2367/$4BE4/$6079/$622B/$63CB/$6400/$64BE/$65F7"
   ],
   "line": "watching a game film?"
  },
  "C454": {
   "unit": "game/textEngineC000",
   "line": "yes - the film drives everything, skip the end/pause tests"
  },
  "C456": {
   "unit": "game/textEngineC000",
   "line": "is the end-of-game sequence running?"
  },
  "C459": {
   "unit": "game/textEngineC000",
   "line": "yes - close the panel"
  },
  "C45B": {
   "unit": "game/textEngineC000",
   "line": "paused or disconnected?"
  },
  "C45D": {
   "unit": "game/textEngineC000",
   "line": "yes - keep the panel on screen"
  },
  "C45F": {
   "unit": "game/textEngineC000",
   "line": "game phase ..."
  },
  "C461": {
   "unit": "game/textEngineC000",
   "line": "... bits 6/7 mark the setup and menu phases ..."
  },
  "C463": {
   "unit": "game/textEngineC000",
   "line": "... where the panel always stays up"
  },
  "C465": {
   "unit": "game/textEngineC000",
   "line": "has a function key asked for another screen? ($FF = none)"
  },
  "C467": {
   "unit": "game/textEngineC000",
   "line": "yes - close the panel"
  },
  "C469": {
   "unit": "game/textEngineC000",
   "line": "last typed key ..."
  },
  "C46C": {
   "unit": "game/textEngineC000",
   "line": "... SPACE ($20 with bit 7 set)?"
  },
  "C46E": {
   "unit": "game/textEngineC000",
   "line": "yes - SPACE closes the panel"
  },
  "C470": {
   "unit": "game/textEngineC000",
   "routine": [
    "checkSetupUiContinue - tail of checkUiContinue, also called on its own by the map main loop during",
    "the setup phase: the panel stays up while the battle is running (phase 0), while this game type has",
    "no setup phase, or while only side 0 deploys and we are side 0; otherwise it stays up only when",
    "both handshake bits of gamePhase are set.",
    "In:  zp_B1 game phase, $92C6 setup phase mode (0 none, 1 both sides, $80 side 0 only), $0B9F side",
    "Out: A = 1 continue / 0 abort; $0B7D = $3C on abort",
    "Called from: checkUiContinue (fall through), main program $202D"
   ],
   "line": "game phase: 0 while the battle runs"
  },
  "C472": {
   "unit": "game/textEngineC000",
   "line": "during the battle the panel stays up"
  },
  "C474": {
   "unit": "game/textEngineC000",
   "line": "does this game type have a unit setup phase?"
  },
  "C477": {
   "unit": "game/textEngineC000",
   "line": "no - stay up"
  },
  "C479": {
   "unit": "game/textEngineC000",
   "line": "positive: both sides deploy their units"
  },
  "C47B": {
   "unit": "game/textEngineC000",
   "line": "$80 = only side 0 deploys ..."
  },
  "C47E": {
   "unit": "game/textEngineC000",
   "line": "... and we are side 0, so stay up"
  },
  "C480": {
   "unit": "game/textEngineC000",
   "line": "game phase ..."
  },
  "C482": {
   "unit": "game/textEngineC000",
   "line": "... low two bits are the setup handshake ..."
  },
  "C484": {
   "unit": "game/textEngineC000",
   "line": "... both sides ready?"
  },
  "C486": {
   "unit": "game/textEngineC000",
   "line": "no - close the panel and wait"
  },
  "C488": {
   "unit": "game/textEngineC000",
   "line": "A = 1: the caller's panel may keep running"
  },
  "C48A": {
   "unit": "game/textEngineC000",
   "line": "back to the caller's panel loop"
  },
  "C48B": {
   "unit": "game/textEngineC000",
   "line": "60 frames ..."
  },
  "C48D": {
   "unit": "game/textEngineC000",
   "line": "... during which the IRQ ignores keyboard and joystick"
  },
  "C490": {
   "unit": "game/textEngineC000",
   "line": "A = 0: the caller must leave its panel"
  },
  "C492": {
   "unit": "game/textEngineC000",
   "line": "back to the caller's panel loop"
  },
  "C493": {
   "unit": "game/textEngineC000",
   "routine": [
    "serviceChatDisplay - first half of updateStatusMessage: it owns the status bar while the player is",
    "typing to the opponent.  A positive non-zero chatState means the line has just been sent, so the",
    "typed line is drawn one last time and the bar is handed back to the message system.  While typing",
    "(bit 7 set) holding CRSR up/down ($8B) peeks at the line the opponent is typing (message 0) and",
    "releasing it redraws our own; bit 6 of chatState means 'the bar no longer shows the typed buffer'.",
    "In both typing cases two bytes are pulled off the stack so that updateStatusMessage returns to its",
    "own caller and never touches the message queue.",
    "In:  $929C chat state (bit 7 typing, bit 6 bar dirty, bits 0-5 characters typed), $92A0 held key,",
    "     zp_7A pending message id",
    "Out: $929C updated, status bar redrawn, zp_7A/zp_7B/zp_72 updated; may discard updateStatusMessage's",
    "     return address",
    "Called from: updateStatusMessage ($C4F0)"
   ],
   "line": "chat state"
  },
  "C496": {
   "unit": "game/textEngineC000",
   "line": "no chat activity - carry on with the ordinary message handling"
  },
  "C498": {
   "unit": "game/textEngineC000",
   "line": "bit 7 set: still typing"
  },
  "C49A": {
   "unit": "game/textEngineC000",
   "line": "the line has been sent ..."
  },
  "C49C": {
   "unit": "game/textEngineC000",
   "line": "... so leave typing mode"
  },
  "C49F": {
   "unit": "game/textEngineC000",
   "line": "show the finished line once more"
  },
  "C4A2": {
   "unit": "game/textEngineC000",
   "line": "the pending message id ..."
  },
  "C4A4": {
   "unit": "game/textEngineC000",
   "line": "... without its 'consumed' bit ..."
  },
  "C4A6": {
   "unit": "game/textEngineC000",
   "line": "... is what the bar now shows"
  },
  "C4A8": {
   "unit": "game/textEngineC000",
   "line": "$81 = bit 7 'nothing pending', but not the fully idle value $80"
  },
  "C4AA": {
   "unit": "game/textEngineC000",
   "line": "so the message system sees nothing pending"
  },
  "C4AC": {
   "unit": "game/textEngineC000",
   "line": "expire the display timer ..."
  },
  "C4AE": {
   "unit": "game/textEngineC000",
   "line": "... so the next queued message can take the bar"
  },
  "C4B0": {
   "unit": "game/textEngineC000",
   "line": "return normally: updateStatusMessage carries on with the queue"
  },
  "C4B1": {
   "unit": "game/textEngineC000",
   "line": "which key is being held?"
  },
  "C4B4": {
   "unit": "game/textEngineC000",
   "line": "CRSR up/down = 'show me the other side's line'"
  },
  "C4B6": {
   "unit": "game/textEngineC000",
   "line": "not held - make sure our own line is on the bar"
  },
  "C4B8": {
   "unit": "game/textEngineC000",
   "line": "remember that the bar no longer shows the typed buffer (A is still chatState)"
  },
  "C4BA": {
   "unit": "game/textEngineC000",
   "line": "bit 6 = the bar is not showing the typed line"
  },
  "C4BD": {
   "unit": "game/textEngineC000",
   "line": "message 0 = the incoming chat line at $0548"
  },
  "C4BF": {
   "unit": "game/textEngineC000",
   "line": "print it on the bar"
  },
  "C4C2": {
   "unit": "game/textEngineC000",
   "line": "and take the bar for the chat"
  },
  "C4C5": {
   "unit": "game/textEngineC000",
   "line": "chat state ..."
  },
  "C4C8": {
   "unit": "game/textEngineC000",
   "line": "... bit 6: is the bar showing something else?"
  },
  "C4CA": {
   "unit": "game/textEngineC000",
   "line": "no - the typed line is already there"
  },
  "C4CC": {
   "unit": "game/textEngineC000",
   "line": "clear the flag ..."
  },
  "C4CF": {
   "unit": "game/textEngineC000",
   "line": "clear bit 6 ..."
  },
  "C4D1": {
   "unit": "game/textEngineC000",
   "line": "... the bar shows the typed line again"
  },
  "C4D4": {
   "unit": "game/textEngineC000",
   "line": "... and put the typed line back on the bar"
  },
  "C4D7": {
   "unit": "game/textEngineC000",
   "line": "discard updateStatusMessage's return address ..."
  },
  "C4D8": {
   "unit": "game/textEngineC000",
   "line": "... so that while typing the bar belongs to the chat alone"
  },
  "C4D9": {
   "unit": "game/textEngineC000",
   "line": "nothing to do"
  },
  "C4DA": {
   "unit": "game/textEngineC000",
   "routine": [
    "drawTypedChatLine - draws the line being typed to the opponent: the bit-7-terminated buffer at",
    "$9278 is printed left aligned on the status bar, then the colour is set to $11 and one blank is",
    "printed as the typing cursor (on the hires menu screen the video-matrix byte $11 paints that whole",
    "cell white, so the blank shows up as a solid block).",
    "In:  $9278 typed line buffer (terminated with $9E, which prints nothing)",
    "Out: the status bar shows the typed line with a cursor; zp_3D = $11; the text window is restored",
    "Called from: serviceChatDisplay ($C49F, $C4D4)"
   ],
   "line": "the typed line buffer at $9278 ..."
  },
  "C4DC": {
   "unit": "game/textEngineC000",
   "line": "$9278, high byte"
  },
  "C4DE": {
   "unit": "game/textEngineC000",
   "line": "... printed left aligned, no centring"
  },
  "C4E0": {
   "unit": "game/textEngineC000",
   "line": "draw it on the status bar"
  },
  "C4E3": {
   "unit": "game/textEngineC000",
   "line": "$11 = white in both nibbles ..."
  },
  "C4E5": {
   "unit": "game/textEngineC000",
   "line": "... so the next cell reads as a solid block"
  },
  "C4E7": {
   "unit": "game/textEngineC000",
   "line": "a space (bit 7 set, printChar strips it)"
  },
  "C4E9": {
   "unit": "game/textEngineC000",
   "line": "print it as the typing cursor"
  },
  "C4EC": {
   "unit": "game/textEngineC000",
   "line": "put the caller's text window back"
  },
  "C4EF": {
   "unit": "game/textEngineC000",
   "routine": [
    "updateStatusMessage - per-frame service of the status bar, called from every polling loop.  It",
    "runs the chat display and the message-review keys, returns while the current message still has",
    "time on the clock, and otherwise retires that message (showNextQueuedMessage), takes the pending",
    "id, prints its string from the pointer tables at $0500/$0524, restarts the $3C-tick timer and",
    "plays the message's sound.  With nothing pending it either shows an arrived chat line or, in the",
    "last quarter of a battle, refreshes the CLICKS/US/THEM line whenever the game clock has ticked.",
    "In:  zp_72 timer, zp_7A pending id, zp_7B shown id, $9277 chat length, $92FA last-quarter flag,",
    "     zp_B1 phase, $91CB game clock, $9208 last drawn clock, tables $0500/$0524/$0612",
    "Out: status bar updated, zp_72 = $3C, zp_7A marked consumed, $9208 = clock; the sound-suppress",
    "     operand at $C54B is rewritten",
    "Called from: main program $0CCA/$1B9A/$1BDC/$40EB/$4131/$4DA9/$5716/$573E"
   ],
   "line": "spare byte; nothing patches it"
  },
  "C4F0": {
   "unit": "game/textEngineC000",
   "line": "chat first - while typing it pops our return address and takes the bar"
  },
  "C4F3": {
   "unit": "game/textEngineC000",
   "line": "CRSR keys: message scrollback and the score line"
  },
  "C4F6": {
   "unit": "game/textEngineC000",
   "line": "how long has the message on the bar still got?"
  },
  "C4F8": {
   "unit": "game/textEngineC000",
   "line": "expired - time for the next one"
  },
  "C4FA": {
   "unit": "game/textEngineC000",
   "line": "still on display: leave the bar alone"
  },
  "C4FB": {
   "unit": "game/textEngineC000",
   "line": "the message currently on the bar"
  },
  "C4FD": {
   "unit": "game/textEngineC000",
   "line": "bit 7 set: the bar is free, nothing to retire"
  },
  "C4FF": {
   "unit": "game/textEngineC000",
   "line": "retire it and pick the next one out of the queue"
  },
  "C502": {
   "unit": "game/textEngineC000",
   "line": "the id the queue handed us ..."
  },
  "C504": {
   "unit": "game/textEngineC000",
   "line": "... is now the one on display"
  },
  "C506": {
   "unit": "game/textEngineC000",
   "line": "a real message id (bit 7 clear) - print it"
  },
  "C508": {
   "unit": "game/textEngineC000",
   "line": "have chat characters arrived from the opponent?"
  },
  "C50B": {
   "unit": "game/textEngineC000",
   "line": "yes - the bar shows the chat line"
  },
  "C50D": {
   "unit": "game/textEngineC000",
   "line": "$92FA bit 7: has the last quarter started?"
  },
  "C510": {
   "unit": "game/textEngineC000",
   "line": "no - leave the bar blank"
  },
  "C512": {
   "unit": "game/textEngineC000",
   "line": "game phase: only during the battle ..."
  },
  "C514": {
   "unit": "game/textEngineC000",
   "line": "... is the clock/score line drawn"
  },
  "C516": {
   "unit": "game/textEngineC000",
   "line": "the game clock (clicks left) ..."
  },
  "C519": {
   "unit": "game/textEngineC000",
   "line": "... against the value last drawn"
  },
  "C51C": {
   "unit": "game/textEngineC000",
   "line": "unchanged - nothing to redraw"
  },
  "C51E": {
   "unit": "game/textEngineC000",
   "line": "remember the clock we are about to draw"
  },
  "C521": {
   "unit": "game/textEngineC000",
   "line": "redraw '  CLICKS:n   US:n   THEM:n'"
  },
  "C524": {
   "unit": "game/textEngineC000",
   "line": "positive: characters are still arriving, keep the line live"
  },
  "C526": {
   "unit": "game/textEngineC000",
   "line": "the incoming line is complete: the bar keeps ..."
  },
  "C528": {
   "unit": "game/textEngineC000",
   "line": "... the last id ..."
  },
  "C52A": {
   "unit": "game/textEngineC000",
   "line": "... as the one on display"
  },
  "C52C": {
   "unit": "game/textEngineC000",
   "line": "the chat text has been taken ..."
  },
  "C52E": {
   "unit": "game/textEngineC000",
   "line": "... so clear the incoming counter"
  },
  "C531": {
   "unit": "game/textEngineC000",
   "line": "X is negative here, which suppresses the sound at $C54A"
  },
  "C534": {
   "unit": "game/textEngineC000",
   "line": "message 0 = the chat line, whose text pointer is $0548"
  },
  "C536": {
   "unit": "game/textEngineC000",
   "line": "message text pointer, low byte ($0500 + id)"
  },
  "C539": {
   "unit": "game/textEngineC000",
   "line": "message text pointer, high byte ($0524 + id)"
  },
  "C53C": {
   "unit": "game/textEngineC000",
   "line": "print it centred on the status bar"
  },
  "C53F": {
   "unit": "game/textEngineC000",
   "line": "$3C ticks (the timer counts down every 4th frame)"
  },
  "C541": {
   "unit": "game/textEngineC000",
   "line": "start the display timer"
  },
  "C543": {
   "unit": "game/textEngineC000",
   "line": "the id just printed ..."
  },
  "C545": {
   "unit": "game/textEngineC000",
   "line": "... in Y for the sound look-up"
  },
  "C546": {
   "unit": "game/textEngineC000",
   "line": "set bit 7 = consumed, nothing pending any more"
  },
  "C548": {
   "unit": "game/textEngineC000",
   "line": "store the marked id back"
  },
  "C54A": {
   "unit": "game/textEngineC000",
   "line": "patched flag: negative = play no sound for this message"
  },
  "C54C": {
   "unit": "game/textEngineC000",
   "line": "suppressed"
  },
  "C54E": {
   "unit": "game/textEngineC000",
   "line": "play the sound this message id is given in $0612"
  },
  "C551": {
   "unit": "game/textEngineC000",
   "line": "re-arm the flag positive ..."
  },
  "C553": {
   "unit": "game/textEngineC000",
   "line": "... so the next message sounds again ($04 is just a positive value)"
  },
  "C556": {
   "unit": "game/textEngineC000",
   "line": "done for this frame"
  },
  "C557": {
   "unit": "game/textEngineC000",
   "routine": [
    "playMessageSound - plays the sound effect assigned to message id Y: the sound number comes from",
    "the per-message table at $0612 and a negative entry ($80) means the message is silent.",
    "In:  Y = message id 0-$23, table $0612",
    "Out: the sound is queued through playSound ($C89A)",
    "Called from: updateStatusMessage ($C54E), showStatusMessage ($C701)"
   ],
   "line": "sound id assigned to this message"
  },
  "C55A": {
   "unit": "game/textEngineC000",
   "line": "$80 = a silent message"
  },
  "C55C": {
   "unit": "game/textEngineC000",
   "line": "queue the sound effect"
  },
  "C55F": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C560": {
   "unit": "game/textEngineC000",
   "routine": [
    "queueMessageUnlessFilm - queues message id A like queueMessage, but drops it silently while a game",
    "film is being replayed.  Used for the combat messages ('UNIT RECYCLED', 'COMCEN STUNNED!', ...).",
    "In:  A = message id, $0B9B bit 7 = film playback",
    "Out: the message is queued, or ignored during film playback",
    "Called from: main program $36F1/$3B3D/$3EA5/$3F8E"
   ],
   "line": "is a game film being replayed?"
  },
  "C563": {
   "unit": "game/textEngineC000",
   "line": "no - queue the message normally"
  },
  "C565": {
   "unit": "game/textEngineC000",
   "line": "during a replay combat messages are dropped"
  },
  "C566": {
   "unit": "game/textEngineC000",
   "routine": [
    "queueMessage - inserts message id A into the 8-entry ring at $9249, keeping it sorted so that",
    "higher ids are shown first: it claims the slot after the head, scans from the tail for the first",
    "entry that is not larger than A, shifts the entries in between one slot forward and drops the new",
    "id in.  A full ring silently discards the message.  If the insert lands on the tail the display",
    "timer is zeroed so the message appears at once, and a status bar that was free is marked busy so",
    "that updateStatusMessage pops the queue on its next pass.  Y is preserved.",
    "In:  A = message id, $9249-$9250 ring, $9251 head (write index), $9252 tail (read index)",
    "Out: the ring is updated; zp_72 and zp_7B possibly updated; operands at $C5B0, $C5B4 and $C5BB",
    "     are rewritten",
    "Called from: queueMessageUnlessFilm ($C563), handlePauseKey ($C730), showWaitingForOpponent",
    "     ($C7D1), ~14 sites in the main program, the $6F00 overlays, $0743 and the modem driver"
   ],
   "line": "patch the new id into the LDA at $C5B3"
  },
  "C569": {
   "unit": "game/textEngineC000",
   "line": "preserve Y in the LDY operand at $C5BA"
  },
  "C56C": {
   "unit": "game/textEngineC000",
   "line": "head = the slot after the last queued message"
  },
  "C56F": {
   "unit": "game/textEngineC000",
   "line": "one on, wrapping at 8"
  },
  "C572": {
   "unit": "game/textEngineC000",
   "line": "would that run into the tail, i.e. is the ring full?"
  },
  "C575": {
   "unit": "game/textEngineC000",
   "line": "no - there is room"
  },
  "C577": {
   "unit": "game/textEngineC000",
   "line": "(A is never used again - leftover)"
  },
  "C579": {
   "unit": "game/textEngineC000",
   "line": "ring full: drop this message"
  },
  "C57B": {
   "unit": "game/textEngineC000",
   "line": "claim the new slot as the head"
  },
  "C57E": {
   "unit": "game/textEngineC000",
   "line": "start at the entry that will be shown next"
  },
  "C581": {
   "unit": "game/textEngineC000",
   "block": [
    "Find the insertion slot: the queue runs from the tail to the head in descending id order, so the",
    "new id goes in front of the first entry that is not larger than it."
   ],
   "line": "compare the new id with this entry"
  },
  "C584": {
   "unit": "game/textEngineC000",
   "line": "new id is the larger - insert here"
  },
  "C586": {
   "unit": "game/textEngineC000",
   "line": "next entry"
  },
  "C589": {
   "unit": "game/textEngineC000",
   "line": "reached the head?"
  },
  "C58C": {
   "unit": "game/textEngineC000",
   "line": "no - keep looking"
  },
  "C58E": {
   "unit": "game/textEngineC000",
   "line": "nothing smaller found: append at the end of the queue"
  },
  "C591": {
   "unit": "game/textEngineC000",
   "line": "patch the insertion slot into the CPY at $C5AF"
  },
  "C594": {
   "unit": "game/textEngineC000",
   "line": "is the new message going to the front of the queue?"
  },
  "C597": {
   "unit": "game/textEngineC000",
   "line": "no"
  },
  "C599": {
   "unit": "game/textEngineC000",
   "line": "then expire the display timer ..."
  },
  "C59B": {
   "unit": "game/textEngineC000",
   "line": "... so it appears as soon as possible"
  },
  "C59D": {
   "unit": "game/textEngineC000",
   "line": "start at the head"
  },
  "C5A0": {
   "unit": "game/textEngineC000",
   "block": [
    "Open the insertion slot: walk backwards from the head, copying each entry one slot forward, until",
    "the insertion slot is reached."
   ],
   "line": "the entry before it ..."
  },
  "C5A3": {
   "unit": "game/textEngineC000",
   "line": "... is read ..."
  },
  "C5A6": {
   "unit": "game/textEngineC000",
   "line": "step forward again ..."
  },
  "C5A9": {
   "unit": "game/textEngineC000",
   "line": "... and written one slot forward"
  },
  "C5AC": {
   "unit": "game/textEngineC000",
   "line": "step back"
  },
  "C5AF": {
   "unit": "game/textEngineC000",
   "line": "patched: the insertion slot"
  },
  "C5B1": {
   "unit": "game/textEngineC000",
   "line": "keep shifting until it is free"
  },
  "C5B3": {
   "unit": "game/textEngineC000",
   "line": "patched: the new message id"
  },
  "C5B5": {
   "unit": "game/textEngineC000",
   "line": "into the slot that was opened for it"
  },
  "C5B8": {
   "unit": "game/textEngineC000",
   "line": "(A is never used again - leftover)"
  },
  "C5BA": {
   "unit": "game/textEngineC000",
   "line": "patched: the caller's Y"
  },
  "C5BC": {
   "unit": "game/textEngineC000",
   "line": "what is on the status bar?"
  },
  "C5BE": {
   "unit": "game/textEngineC000",
   "line": "a message is on display; it will pop the queue when its timer expires"
  },
  "C5C0": {
   "unit": "game/textEngineC000",
   "line": "the bar is free: clear that bit ..."
  },
  "C5C2": {
   "unit": "game/textEngineC000",
   "line": "... so updateStatusMessage pops the queue on its next pass"
  },
  "C5C4": {
   "unit": "game/textEngineC000",
   "line": "was the bar completely idle ($80)?"
  },
  "C5C6": {
   "unit": "game/textEngineC000",
   "line": "then show the new message immediately"
  },
  "C5C8": {
   "unit": "game/textEngineC000",
   "line": "done"
  },
  "C5C9": {
   "unit": "game/textEngineC000",
   "routine": [
    "decMessageIndex - steps the message-queue index in Y back one slot, wrapping from 0 to 7.",
    "In:  Y = ring index 0-7",
    "Out: Y = (Y - 1) mod 8",
    "Called from: queueMessage ($C58E/$C5A0/$C5AC), handleMessageReviewKeys ($C66A)"
   ],
   "line": "one slot back"
  },
  "C5CA": {
   "unit": "game/textEngineC000",
   "line": "still inside the ring"
  },
  "C5CC": {
   "unit": "game/textEngineC000",
   "line": "wrapped past 0: the ring has 8 slots"
  },
  "C5CE": {
   "unit": "game/textEngineC000",
   "line": "Y = the previous slot"
  },
  "C5CF": {
   "unit": "game/textEngineC000",
   "routine": [
    "incMessageIndex - steps the message-queue index in Y on one slot, wrapping from 7 to 0.",
    "In:  Y = ring index 0-7",
    "Out: Y = (Y + 1) mod 8",
    "Called from: queueMessage ($C56F/$C586/$C5A6), showNextQueuedMessage ($C621)"
   ],
   "line": "one slot on"
  },
  "C5D0": {
   "unit": "game/textEngineC000",
   "line": "the ring has 8 slots"
  },
  "C5D2": {
   "unit": "game/textEngineC000",
   "line": "still inside it"
  },
  "C5D4": {
   "unit": "game/textEngineC000",
   "line": "wrap back to the first slot"
  },
  "C5D6": {
   "unit": "game/textEngineC000",
   "line": "Y = the next slot"
  },
  "C5D7": {
   "unit": "game/textEngineC000",
   "routine": [
    "redrawStatusMessage - puts the status bar back after a screen has been rebuilt: the message that",
    "was on display (its id with the 'bar free' bit masked off), or message 0 (the chat line) when chat",
    "text is buffered, or else the next message from the queue.",
    "In:  zp_7B shown message id, $9277 incoming chat length",
    "Out: the status bar is redrawn",
    "Called from: main program $1562/$268C, $6F00 overlays $7131/$7A68"
   ],
   "line": "the message that was on the bar"
  },
  "C5D9": {
   "unit": "game/textEngineC000",
   "line": "a real id - draw it again"
  },
  "C5DB": {
   "unit": "game/textEngineC000",
   "line": "otherwise consider message 0, the chat line"
  },
  "C5DD": {
   "unit": "game/textEngineC000",
   "line": "is any chat text buffered?"
  },
  "C5E0": {
   "unit": "game/textEngineC000",
   "line": "no - take the next message from the queue instead"
  },
  "C5E2": {
   "unit": "game/textEngineC000",
   "line": "strip the 'bar is free' bit to get the plain id"
  },
  "C5E4": {
   "unit": "game/textEngineC000",
   "routine": [
    "showMessageById - looks message id A up in the pointer tables at $0500 (low) / $0524 (high) and",
    "tail-calls showMessageString, so the text appears centred on the status bar straight away without",
    "going through the queue.",
    "In:  A = message id 0-$23",
    "Out: the status bar is redrawn; X = the message id",
    "Called from: serviceChatDisplay ($C4BF), redrawStatusMessage (fall through at $C5E2 and the branch",
    "     at $C619), showStatusMessageNoSound ($C707)"
   ],
   "line": "X = message id"
  },
  "C5E5": {
   "unit": "game/textEngineC000",
   "line": "text pointer low byte, 36 messages"
  },
  "C5E8": {
   "unit": "game/textEngineC000",
   "line": "text pointer high byte"
  },
  "C5EB": {
   "unit": "game/textEngineC000",
   "line": "print it centred on the status bar"
  }
 },
 "dataTypes": {
  "C000": [
   "byte",
   4
  ],
  "C004": [
   "byte",
   4
  ],
  "C008": [
   "byte",
   8
  ],
  "C010": [
   "byte",
   4
  ],
  "C014": [
   "byte",
   4
  ],
  "C018": [
   "byte",
   2
  ],
  "C23B": [
   "byte",
   16
  ]
 },
 "zpComments": {
  "3A": "text engine: row just below the text window; the window clears fill while the cursor row is less than this",
  "3E": "text engine: glyph number used as the blank/fill character by the clear, pad and backspace routines (glyph 0 = blank)",
  "43": "text engine: bit 7 set = printChar takes A as a glyph number, skipping the ASCII translation and the control codes",
  "54": "A save slot of saveRegsAXY/restoreRegsAXY ($C0E8/$C030), so printChar can preserve the caller's registers",
  "72": "frames the status-line message stays up ($3C when a message is shown), decremented every 4th frame by the IRQ; 0 = ready for the next message",
  "7A": "id of the message the status bar should print next; bit 7 set = already consumed, $80 = fully idle",
  "7B": "id of the message currently on the status bar; bit 7 set = the bar is free",
  "7E": "high (third) byte of the 24-bit value printDecimal converts; cleared by convertToDecimalDigits",
  "BA": "quadrant suppression mask of plotGlyphDoubleSize: bit 0 skip the top cells, bit 1 the bottom, bit 2 the left, bit 3 the right"
 }
}