fb3.nes Disassembly

                   ; Generated by 6502bench SourceGen v1.10.0
                   ; https://6502bench.com/
                   ; 
                   ; This project is https://github.com/micahcowan/fbdasm
                   ; 
                   ; This file was generated from
                   ;   git commit df27caef7aa2641f2bcc809858dd3b4de59410d8
                   ;   from Wed Jan 7 08:15:17 2026 -0800
                   ; 
                   ; You may use this disassembly for any purpose.
                   ; 
                   ; If you distribute this work (whether original or modified), you must
                   ; credit me: micah@addictivecode.org
                   ; 
                   ; In this disassembly I use the term "actor" to refer to what the Family BASIC
                   ; manual mostly calls an "animated character" and the Family BASIC language
                   ; itself calls a "sprite". "Sprite" is ambiguous because in BASIC means a 2x2-
                   ; tile animated character, while NES/Famicom hardware and associated
                   ; documentation considers a "sprite" to be just a single one of those 2x2 tiles.
                   ; "animated character" is far too long for variable names (as is AnimChar), and
                   ; "char" is far too easily confused with its text-related meaning.
                   ; 
                   ; Unfortunately I am not consistent with this terminology, and frequently fer to
                   ; "actors" as "sprites". Meanwhile, actual Famicom sprites, I try to refer to as
                   ; "sprite tile" or "tile", to make it clear I'm talking about an 8x8-pixel
                   ; hardware sprite, and not the FB notion of a sprite (animated character).
                   DTU__ON         .eq     0      {const}
                   EH_NO_HANDLER   .eq     $00    {const}
                   ENV_USED        .eq     $00    {const}          ; value used with zpEnvelope to indicate envelope in use for given voice
                   err_NF          .eq     $00    {const}          ; "NF" - NEXT without FOR
                   NC_NoCmd        .eq     0      {const}
                   SHARP_UNUSED    .eq     $00    {const}
                   DTU__KEYBOARD_WO_SETSUZOKU .eq 1      {const}
                   EH_HANDLER_DEFINED .eq  $01    {const}
                   err_SN          .eq     $01    {const}          ; "SN" - Syntax Error
                   NC_FlushOutp    .eq     1      {const}
                   CAS_HDR_PROG    .eq     $02    {const}          ; Identity byte in header for prog saves to cassette tape.
                   DTU__DENGEN_WO  .eq     2      {const}
                   EH_IN_HANDLER   .eq     $02    {const}
                   err_RG          .eq     $02    {const}          ; "RG" - RETURN without GOSUB
                   HMARGIN         .eq     2      {const}          ; horizontal margin size (in tiles/chars)
                   NC_BufferCurLine .eq    2      {const}
                   SHARP_USED      .eq     $02    {const}
                   TYP_INT         .eq     $02    {const}          ; For zpArgTyp. Integer word.
                   CAS_HDR_SCREEN  .eq     $03    {const}          ; Identity byte in header for screen saves to cassette tape.
                   DTU__BACKUP_SWITCH_WO .eq 3      {const}
                   err_OD          .eq     $03    {const}          ; "OD" - Out of Data
                   key_Stop        .eq     $03    {const}          ; Control-C, (but ^C not always equiv to STOP)
                   NC_CopyCharsLeft .eq    3      {const}
                   TYP_STR         .eq     $03    {const}          ; For zpArgTyp. String.
                   VMARGIN         .eq     3      {const}          ; certical margin size (in tiles/chars)
                   CHAN_MASK_TRI   .eq     4      {const}
                   DTU__OFF_NI_SHITE_KUDASAI .eq 4      {const}
                   err_IL          .eq     $04    {const}          ; "IL" - Illegal value
                   NC_ClearToEndOfRow .eq  4      {const}
                   NUM_PALETTES    .eq     4      {const}          ; number of palettes in a palette set
                   BGT_MODESEL_ITEMS .eq   5      {const}          ; how many menu items in BGTOOL mode-select menu
                   DTU__NI_SHITE_KUDASAI .eq 5      {const}
                   err_OV          .eq     $05    {const}          ; "OV" - Overflow
                   NC_GetCharUnderCursor .eq 5      {const}
                   DTU__SHITE_KUDASAI .eq  6      {const}
                   err_OM          .eq     $06    {const}          ; "OM" - Out of Memory
                   NC_SetBgPalette .eq     6      {const}
                   err_UL          .eq     $07    {const}          ; "UL" - Undefined Line number
                   key_Del         .eq     $08    {const}          ; Control-H / Backspace
                   NC_ShowCursor   .eq     8      {const}
                   NUM_ACTORS      .eq     8      {const}          ; number of animated characters ("actors") in Family BASIC
                   SpAtt_DEFMOVE   .eq     $08    {const}          ; bit 3. SET: actor has a MOVE defined
                   TILE_PIXELS     .eq     8      {const}          ; pixels in a tile (either dimension)
                   err_DD          .eq     $09    {const}          ; "DD" - Duplicate Definition
                   NC_RestoreCharAtCursor .eq 9      {const}
                   err_DZ          .eq     $0a    {const}          ; "DZ" - Division by Zero
                   NC_PlayMusic    .eq     10     {const}
                   err_TM          .eq     $0b    {const}          ; "TM" - Type Mismatch
                   key_Home        .eq     $0b    {const}          ; Control-K
                   NC_PpuMaskUpdate .eq    11     {const}
                   tok_LNUM_AFTER  .eq     $0b    {const}          ; indicates a line number (word) follows this byte
                   err_ST          .eq     $0c    {const}          ; "ST" - String Too long
                   key_Cls         .eq     $0c    {const}          ; Control-L
                   NC_PpuMaskClear .eq     12     {const}
                   SELMD_FRST_TILEPOS .eq  12     {const}
                   tok_LLINK_AFTER .eq     $0c    {const}          ; indicates a direct link to a line follows - only used during a RENUM
                   BGT_SELECT_NUM_TROWS .eq 13     {const}         ; how many tile rows in BGTOOL SELECT mode's tile menu
                   key_Enter       .eq     $0d    {const}          ; Control-M
                   NC_InsertChar   .eq     13     {const}
                   err_CC          .eq     $0e    {const}          ; "CC" - Cannot Continue
                   NC_CopyRowDown  .eq     14     {const}
                   DEFAULT_PPUCTRL .eq     $10    {const}
                   SpAtt_ANIMATE   .eq     $10    {const}          ; bit 4. SET: actor is animated
                   err_TP          .eq     $11    {const}          ; "TP" - Tape read error
                   tok_HEX_AFTER   .eq     $11    {const}          ; indicates a number (word) follows this byte, originally hexadecimal
                   err_NR          .eq     $12    {const}          ; "NR" - No Resume
                   key_Insert      .eq     $12    {const}          ; Control-R
                   tok_NUM_AFTER   .eq     $12    {const}          ; indicates a number (word) follows this byte, originally decimal
                   err_RE          .eq     $13    {const}          ; "RE" - RESUME without ERROR
                   err_NB          .eq     $14    {const}          ; "NB" - No BG Data
                   BGT_ROWS        .eq     21     {const}          ; number of rows in BGTOOL (excluding bottom status area)
                   err_UP          .eq     $15    {const}          ; "UP" - Unprintable Error
                   NOTE_VAL_OCTAVE .eq     24     {const}
                   ROWS            .eq     24     {const}
                   key_CtrlZ       .eq     $1a    {const}          ; Control-Z (clear to end of screen)
                   SELMD_LAST_TILEPOS .eq  26     {const}
                   key_Escape      .eq     $1b    {const}          ; Control-[
                   COLS            .eq     28     {const}
                   key_CursorRight .eq     $1c    {const}          ; Control-\
                   key_CursorLeft  .eq     $1d    {const}          ; Control-]
                   key_CursorUp    .eq     $1e    {const}          ; Control-^
                   MAP_ROWS        .eq     30     {const}          ; number of rows in a PPU nametable (240px / 8px tiles)
                   key_CursorDown  .eq     $1f    {const}          ; Control-_
                   key_Space       .eq     $20    {const}
                   SpAtt_BEHIND    .eq     $20    {const}          ; bit 5. SET: behind bg; RESET: in front
                   STRLEN_MAX      .eq     32     {const}
                   PLAY_INDEX_V1   .eq     33     {const}
                   ENV_UNUSED      .eq     $30    {const}          ; value used with zpEnvelope to indicate envelope not used for given voice
                   PLAY_INDEX_V2   .eq     66     {const}
                   BU_AVAIL_MARK   .eq     $4c    {const}
                   BU_RESTORED_MARK .eq    $59    {const}
                   CURS_FLASH_TM   .eq     128    {const}          ; how long to hide/show the cursor when flashing
                   TOKEN_START     .eq     $80    {const}
                   TYP_ARRAY_FLAG  .eq     $80    {const}          ; Flag bit indicating array type.
                   tok_THEN        .eq     $85    {const}
                   tok_LIST        .eq     $86    {const}
                   tok_SYSTEM      .eq     $87    {const}
                   tok_TO          .eq     $88    {const}
                   tok_STEP        .eq     $89    {const}
                   tok_SPRITE      .eq     $8a    {const}
                   tok_PRINT       .eq     $8b    {const}
                   tok_NEXT        .eq     $8d    {const}
                   tok_DATA        .eq     $91    {const}
                   tok_REM         .eq     $95    {const}
                   tok_ON          .eq     $9a    {const}
                   tok_OFF         .eq     $9b    {const}
                   tok_MOVE        .eq     $a1    {const}
                   TileId_emptyBox .eq     $b0    {const}
                   tok_AUTO        .eq     $b5    {const}
                   tok_DELETE      .eq     $b6    {const}
                   tok_RENUM       .eq     $b7    {const}
                   tok_ERROR       .eq     $bc    {const}
                   tok_RESUME      .eq     $bd    {const}
                   tok_ABS         .eq     $ca    {const}
                   SPR_NullTile    .eq     $ef    {const}
                   tok_OP_XOR      .eq     $ef    {const}
                   tok_OP_OR       .eq     $f0    {const}
                   VPIXELS         .eq     240    {const}          ; number of pixels in a PPU nametable, vertically
                   tok_OP_AND      .eq     $f1    {const}
                   tok_OP_NOT      .eq     $f2    {const}
                   tok_OP_NE       .eq     $f3    {const}
                   tok_OP_EQ       .eq     $f6    {const}
                   tok_OP_PLUS     .eq     $f9    {const}
                   tok_OP_MINUS    .eq     $fa    {const}
                   tok_OP_MOD      .eq     $fb    {const}
                   tok_OP_DIV      .eq     $fc    {const}
                   FOR_STK_MARKER  .eq     $fd    {const}          ; special CPU stack marker for FOR-loop data
                   TileId_whiteBox .eq     $fd    {const}
                   tok_OP_MUL      .eq     $fd    {const}
                   GOSUB_STK_MARKER .eq    $ff    {const}          ; special CPU stack marker for RETURN data
                   VRAM_screen0    .eq     $2000  {const}
                   VRAM_screen1    .eq     $2400  {const}
                   PPU_PALETTE_RAM .eq     $3f00  {const}
                   zpFRETOP        .eq     $03    {addr/2}         ; Pointer to just past the end of free space.
                   zpTXTTAB        .eq     $05    {addr/2}         ; Pointer to the start of the BASIC program code. Always set to $6006.
                   zpTXTEND        .eq     $07    {addr/2}         ; Ponter to just past the program end (zpTXTEND - zpTXTTAB = prog len)
                   zpTXTPTR        .eq     $09    {addr/2}         ; Points at the next char of BASIC program to be processed.
                   zpRunLineNum    .eq     $0b    {addr/2}         ; When BASIC running, it's the current line number, or #$FFFF if it's direct from the command prompt. When inserting or deleting lines, this var instead points at the next line of BASIC program after the one to be ins/del
                   zpRunTxtNxtLnk  .eq     $0d    {addr/2}         ; When BASIC running, it's a pointer at the next line (starting at its "offset" byte)
                   zpInsLineNum    .eq     $0f    {addr/2}         ; Line number to give newly-inserted line
                   zpContRunLineNum .eq    $11    {addr/2}         ; Saved zpRunLineNum for CONT restore
                   zpContRunTxtNxtL .eq    $13    {addr/2}         ; Saved zpRunTxtNxtLnk for CONT restore
                   zpContTxtThisCmd .eq    $15    {addr/2}         ; Saved zpTxtThisCmd for CONT restore
                   zpSavedStack    .eq     $17                     ; Saved SP
                   zpModifierKeyDn .eq     $18                     ; Non-zero when modifier key is pressed
                   zpMisc          .eq     $19    {addr/4}         ; Used for a wide variety of things, mainly temporary pointers
                   zpVARTAB        .eq     $1d    {addr/2}         ; Start of variable storage
                   zpVAREND        .eq     $1f    {addr/2}         ; Just past end of variable storage
                   zpGpStrIdx      .eq     $21                     ; Index for temp string storage in genPurposeBuf
                   zp22            .eq     $22                     ; Written to once, in NEW/_Reset. Never used.
                   zpStoppedFlag   .eq     $24                     ; Non-zero if STOPped; zero otherwise.
                   zpPokeAddr      .eq     $25    {addr/2}         ; stores addr to POKE to
                   zpArgTyp        .eq     $27                     ; Designates the type of data in zpWAccum.
                   zpWAccum        .eq     $28    {addr/4}         ; Like a 16-bit Accumulator. Forms both the first operand, and the result, of various 16-bit integer operations. Or: byte Len followed by word ptr, for strings.
                   zpWParam        .eq     $2c    {addr/3}         ; Like a 16-bit Data register. forms the second operand of various binary operators
                   zpExprStkIdx    .eq     $30                     ; Current position in exprParseStk
                   zpPpuCtrlVal    .eq     $32
                   zpPpuMaskVal    .eq     $33
                   zpMulScratch    .eq     $34    {addr/8}         ; Used for multiplication scratch space. Only the first four are ever actually used!
                   zpTmp3C         .eq     $3c
                   zpNumStrTmp     .eq     $3d    {addr/5}         ; Holds str output from HexStrFromWord and DecStrFromWord
                   zpFindLNum      .eq     $44    {addr/2}
                   zpTmp46         .eq     $46    {addr/1}         ; various miscellaneous uses
                   zpLineStartRow  .eq     $47                     ; the screen row at which current typed line starts (only set AFTER typing is finished)
                   zpCH            .eq     $48                     ; Current cursor column number
                   zpCV            .eq     $49                     ; Current cursor row number
                   zpCursTileAddr  .eq     $4a    {addr/2}         ; PPU address of tile under cursor
                   zpInsertMode    .eq     $4c                     ; Zero = overwrite, non-zero = insert
                   zpTokenizePtr   .eq     $4d    {addr/2}
                   zpPpuScreenBasePage .eq $4f                     ; The VRAM page used for the current screen we're UPDATING (may not be the one displayed)!
                   zpKanaHeld      .eq     $50                     ; Non-zero (#$FF) if KANA key is still being held down from when it was first detected. Used to prevent rapid toggling.
                   zpListOrDelete  .eq     $51                     ; Non-zero (#$FF) if we're in DELETE; zero if we're in LIST
                   zpOutputStr     .eq     $52    {addr/2}         ; Pointer to null-term'd string to be queued for output
                   zpTokTblScn     .eq     $54    {addr/2}         ; Used to scan thru tbl_KeywordTokens
                   zpRandom        .eq     $56    {addr/2}         ; Used for RAND
                   zpTxtThisCmd    .eq     $58    {addr/2}         ; The start of the currently-running command. Restored to zpTXTPTR on RESUME or CONT.
                   zpNewLineOffset .eq     $5a                     ; Offset to next line for not-yet-placed, tokenized line
                   zpTxtLink       .eq     $5b    {addr/2}         ; Points to the start of the current program line (including the 3-byte preamble)
                   zpNmiAnimActCtr .eq     $5d                     ; Counts how many actors are not currently animated, and turns off zpNmiAnimate if it reaches zero
                   zpUnusedStopFlg .eq     $5e                     ; Causes STOP to be returned by ReadChar when set, but is never actually set by anything AFAICT
                   zpSavedRts      .eq     $5f    {addr/2}         ; FOR, GOSUB, other cmds pull return from stack, save here
                   zpCursorChar    .eq     $61                     ; The character code of the current cursor
                   zpOutTxtCurPos  .eq     $62
                   zpNMICMD        .eq     $63                     ; Which command the NMI handler should execute. 1 + the command's index in jtbl_NMICMD.
                   zpOutTxtStartPos .eq    $64
                   zpPaletBgOrSpr  .eq     $65                     ; zero when we're setting bg palette colors, non-zero when setting sprite palette
                   zpRkimMidMacro  .eq     $66                     ; Non-zero (#$FF) if ReaKeyInsMacros ($B945) should insert next char from a fkey definition (next char is at $480 + ($67)) rather than read from keyboard.
                   zpFnKeyDfnOff   .eq     $67                     ; Offset from $480 into cur fn key definition string
                   zpFnKeyHeld     .eq     $68                     ; Non-zero ($FF) if a function key is still being held down from when it was first detected. Used to prevent continuous handling while pressed.
                   zpFnKeyCode     .eq     $6a                     ; Pressed function key (#$F0-$F7, for keys F1-F8).
                   zpSavedCH       .eq     $6b
                   zpSavedCV       .eq     $6c
                   zpKeyWhichKey   .eq     $6d                     ; Within ReadKeyboard, which key of the row in $6E was found pressed. Outside ReadKeyboard, when the mapped char code is > #$F0, its value will often be stored here while further code determines if a function key was pressed.
                   zpKeyWhichRow   .eq     $6e                     ; which key matrix row/byte of key has a non-modifier key down
                   zpCtrlKeyDn     .eq     $6f                     ; Non-zero (#$FF) when control key is pressed
                   zpShiftKeyDn    .eq     $70                     ; Non-zero (#$FF) when (either) shift key is pressed
                   zpGrphKeyDn     .eq     $71                     ; Non-zero (#$FF) when GRPH key is pressed
                   zpKanaFlag      .eq     $72                     ; Non-zero (#$FF) when kana mode is active
                   zpCharUnderCurs .eq     $73                     ; The character currently under cursor
                   zpCursFlashCtr  .eq     $74                     ; Delay count-down for when cursor should show/hide
                   zpHeldKeyChar   .eq     $75                     ; Holds the char value of a key being held down
                   zpLineBufIdx    .eq     $76                     ; Current write position into lineBuffer ($500)
                   zpKyRptLongCtr  .eq     $77                     ; Key-repeat: delay count-down to first key repeat on long-press
                   zpKyNotRpt      .eq     $78                     ; Key-repeat: #$01 = waiting on first key repeat, #$00 = on subsequent repeats
                   zpKyRptShtCtr   .eq     $79                     ; Key-repeat: delay count-down for repeating key past the first, longer pause.
                   zpKeyRepeating  .eq     $7b                     ; Key-repeat: #$FF = key is actively repeating, #$00 = key not repeating
                   zpBgGotFlag     .eq     $7c                     ; zero-page copy of bgGotFlag from battery RAM
                   zp7D            .eq     $7d
                   zp7E            .eq     $7e
                   zpForLimitVal   .eq     $80    {addr/2}         ; the limiting value that will cause an exit from FOR statement
                   zpForStepVal    .eq     $82    {addr/2}         ; the step value by which to increment the index variable for a FOR statement
                   zpForNxtLnLnk   .eq     $84    {addr/2}         ; link to the line of BASIC after the current FOR loop
                   zpLNumStart     .eq     $86    {addr/2}         ; Starting line number of a line-number range
                   zpLNumEnd       .eq     $88    {addr/2}         ; Ending line number of a line-number range
                   zpVarName       .eq     $8a    {addr/2}         ; Two-letter variable name
                   zpVarData       .eq     $8c    {addr/2}         ; pointer to selected variable data (deep, if var is array)
                   zpVARPTR        .eq     $8e    {addr/2}         ; pointer to start of selected variable
                   zpVarType       .eq     $90                     ; Often indicates type of a found variable. Other misc uses also occur.
                   zpCasCksum      .eq     $91    {addr/2}         ; Checksum for cassette data, also used for misc counting
                   zpTraceOnFlag   .eq     $97                     ; Flag for whether to trace line numbers (TRON)
                   zpSavedTxtChar  .eq     $98
                   zpToken         .eq     $99
                   zpSavedTXTPTR   .eq     $9a    {addr/2}         ; Saved val of zpTXTPTR
                   zpPaletteNum    .eq     $9c
                   zpBackupRstrd   .eq     $9e                     ; Set to #$59 if BASIC was restored from battery backup
                   zpLoadQFlag     .eq     $9f                     ; True if we're in "LOAD?" rather than "LOAD"
                   zpNxtAutoLNum   .eq     $a0    {addr/2}         ; Next line number to use (for AUTO)
                   zpAutoLNumIncr  .eq     $a2    {addr/2}         ; How much to auto-increment each line number (from AUTO)
                   zpActorNum      .eq     $a4                     ; Current work actor
                   zpDefMovIdx     .eq     $a5                     ; cur actor # times 8, for indexing defMovS* vars
                   zpNmiDefMovIdx  .eq     $a6                     ; (NMI) cur actor # times 8, for indexing defMovS* vars
                   zpNmiCurMTbl    .eq     $a7    {addr/2}         ; (NMI) address of the mbtl for current actor
                   zpNmiActorNum   .eq     $a9                     ; Current work actor, used by NMI
                   zpNmiAnimNxtFr  .eq     $ab                     ; While zpNmiAnimate is non-zero, this flag toggles every frame. Z = Handle NMI subcmds; NZ = Handle MOVEs
                   zpNmiAnimate    .eq     $ac                     ; This flag indicates that animated characters are active (via MOVE)
                   zpCasBufPtr     .eq     $ad    {addr/2}         ; Start of data to send to cassette
                   zpCasEndPtr     .eq     $af    {addr/2}         ; End of data to send to cassette
                   zpVerfFlag      .eq     $b1                     ; If set, VERIFIES rather than LOADs a program. Used for "LOAD?".
                   zpNextDataPtr   .eq     $b2    {addr/2}         ; Pointer to first content byte of line to start search for next DATA to READ
                   zpNextDataIdx   .eq     $b4                     ; Char index into line at zpNextDataPtr to start from
                   zpErrCode       .eq     $b5
                   zpUnusedErrVar  .eq     $b6                     ; This var is only ever used in one place, where it's set to zero.
                   zpErrLineNum    .eq     $b7    {addr/2}         ; Line number where an error happened
                   zpTempo         .eq     $b9    {addr/3}         ; tempo setting for each of the three voices
                   zpVolume        .eq     $bc    {addr/3}         ; volume level for each of the three voices
                   zpOctave        .eq     $bf    {addr/3}         ; octave # for each of the three voices
                   zpSharpVal      .eq     $c2                     ; whether to sharp next note. 2 = yes, 0 = no
                   zpNoteLen       .eq     $c3    {addr/3}         ; note length for each voice
                   zpPlayVoxIdx    .eq     $c6    {addr/3}         ; start index into genPurposeBuf for each voice
                   zpPlayStatus    .eq     $c9                     ; <0: error. >0: active voices mask. ==0: done playing.
                   zpCurVoice      .eq     $ca                     ; the voice the NMI music player is working on
                   zpNoteRealLen   .eq     $cb    {addr/3}         ; tempo-adjusted note length for each of the three voices
                   zpCurVoxMask    .eq     $ce                     ; SND_CHN mask for the current sound channel
                   zpEnvelope      .eq     $cf    {addr/3}         ; envelope setting for each voice. ENV_UNUSED (#$30) when no envelope, zero when using volume as envelope
                   zpDuty          .eq     $d2    {addr/3}         ; cycle duty for each voice.
                   zpNmiCurFrCnts  .eq     $d5    {addr/2}         ; (NMI) Pointer to vector being used for current actor's anim frame counts, for each facing
                   zpNmiNumFrames  .eq     $d7                     ; (NMI) Holds the number of anim frames for the actor's cur facing (in high nybble)
                   zpNmiCurSprSht  .eq     $d9    {addr/2}         ; (NMI) Pointer to sheet of sprites for each facing
                   zpNmiSprTileMp  .eq     $db    {addr/2}         ; (NMI) Pointer to the 2x2 tilemap to display for current actor
                   zpNmiSprShtOff  .eq     $dd                     ; (NMI) Value to add to each tile ID in a sprite sheet, to get IDs for this actor.
                   zpKanaCursChr   .eq     $e0                     ; character/tile ID to use for cursor when kana active
                   zpNonKCursChr   .eq     $e1                     ; character/tile ID to use for cursor when kana NOT active
                   zpAutoEnabled   .eq     $e2                     ; non-zero (=10) when AUTO active; zero means inactive
                   zpKeyClickFlg   .eq     $e3                     ; non-zero = key clicks on
                   zpVScroll0      .eq     $e4                     ; Vertical scroll used when SCREEN 0 is displayed
                   zpVScroll1      .eq     $e5                     ; Vertical scroll used when SCREEN 1 is displayed; updated when $F0 is set
                   zpErrHandStat   .eq     $e7                     ; #$00 - no error, #$01 - error, not resumable, #$02 - error, resumable
                   zpErrHandLNum   .eq     $e8    {addr/2}         ; line number to jump to when error occurs
                   zpHaveFNameArg  .eq     $ea                     ; true when LOAD was given a filename arg
                   zpNmiTrampoline .eq     $ed    {addr/3}         ; Contains a JMP instruction to the real NMI handler. It's set to: JMP $8971
                   zpCurScreenNum  .eq     $f0                     ; the _displayed_ SCREEN (0 or 1). Kept in sync with zpPpuScreenBasePage
                   zpGameNum       .eq     $f3                     ; Game number (GAME cmd)
                   zpRsmTxtThisCmd .eq     $f6    {addr/2}         ; Saved zpTxtThisCmd for RESUME restore
                   zpRsmRunTxtNxtL .eq     $f8    {addr/2}         ; Saved zpRunTxtNxtLnk for RESUME restore
                   zpRsmRunLineNum .eq     $fa    {addr/2}         ; Saved zpRunLineNum for RESUME restore
                   oamBuffer       .eq     $0200  {addr/256}       ; Region for PPU sprite data DMA
                   oamSprYPos      .eq     $0200
                   oamSprTileId    .eq     $0201
                   oamSprAttr      .eq     $0202
                   oamSprXPos      .eq     $0203
                   tokenizeBuffer  .eq     $0300  {addr/256}
                   exprParseStk    .eq     $0400  {addr/128}       ; expression evaluation stack
                   fnKey1_DfnBuf   .eq     $0480  {addr/16}        ; Current definitions of the FN-key shortcuts, each 16 chars
                   lineBuffer      .eq     $0500  {addr/256}       ; NMICMD_BufferCurLine copies the full current, potentially multi-row line to here
                   bgtCH           .eq     $0505                   ; horiz. pos of tile cursor, in BGTOOL mode
                   bgtCV           .eq     $0506                   ; vert. pos of tile cursor, in BGTOOL mode
                   bgtMenuTilesBuf .eq     $0520  {addr/90}        ; saved tiles from behind BGTOOL mode-select menu
                   isRowWrappedArray0 .eq  $0601  {addr/24}        ; Marks which visible screen rows are wrapped from previous line, if SCREEN 0.
                   isRowWrappedArray1 .eq  $061b  {addr/24}        ; Marks which visible screen rows are wrapped from previous line, if SCREEN 1.
                   kbdDataAry      .eq     $0634  {addr/9}         ; Key down bitmap. See loc $B9A1 for content details
                   sprTwoByTwoFlags .eq    $063d  {addr/8}         ; 1x1-tile (#$00) vs 2x2 (#$01), for each actor.
                   sprAttrFlags    .eq     $0645  {addr/8}         ; Sprite tile attributes for the 8 actors. See SpAtt_* for flag values.
                   sprTileMaps     .eq     $0680  {addr/32}        ; Sprite tile maps for the 8 actors (4 tiles/actor)
                   defMovSFacing   .eq     $06c0                   ; Actor MOVE direction (swapped with type, relative to DEF MOVE arg order)
                   defMovSActorFrm .eq     $06c1                   ; Low nybble: MOVE character type (Mario, Lady, Star Killer, etc)A; high nybble: which frame of animation frame we're on
                   defMovSSpdCtr   .eq     $06c2                   ; Actor MOVE speed counter
                   defMovSMotionCtr .eq    $06c3                   ; Actor MOVE total movement remaining
                   defMovSSpeed    .eq     $06c4                   ; Actor MOVE total movement remaining
                   defMovSMotion   .eq     $06c5                   ; Actor MOVE total movement remaining
                   defMovSPosX     .eq     $06c6                   ; Actor MOVE, current X position
                   defMovSPosY     .eq     $06c7                   ; Actor MOVE, current Y position
                   genPurposeBuf   .eq     $0700  {addr/256}       ; user directive messages (at least) get writen out to here
                   PPUCTRL         .eq     $2000                   ;W VPHB SINN  various
                   PPUMASK         .eq     $2001                   ;W BGRs bMmG  various
                   PPUSTATUS       .eq     $2002                   ;R VSO- ----  various; read resets $2005/2006
                   OAMADDR         .eq     $2003                   ;W OAM read/write address
                   PPUSCROLL       .eq     $2005                   ;WW fine scroll position (two writes: X,Y)
                   PPUADDR         .eq     $2006                   ;WW PPU read/write address (two writes: MSB, LSB)
                   PPUDATA         .eq     $2007                   ;RW PPU data read/write
                   SQ1_VOL         .eq     $4000                   ;DDLC VVVV
                   SQ1_SWEEP       .eq     $4001                   ;EPPP NSSS
                   SQ1_LO          .eq     $4002                   ;TTTT TTTT
                   SQ1_HI          .eq     $4003                   ;LLLL LTTT
                   SQ2_SWEEP       .eq     $4005                   ;EPPP NSSS
                   DMC_FREQ        .eq     $4010                   ;IL-- RRRR
                   OAMDMA          .eq     $4014                   ;AAAA AAAA  OAM DMA high address
                   SND_CHN         .eq     $4015                   ;W:---D NT21 R:IF-D NT21
                   JOY1            .eq     $4016                   ;joystick 1 data (R) and joystick strobe (W)
                   JOY2            .eq     $4017                   ;joystick 2 data (R) and frame counter (W)
                   bbBackupAvail   .eq     $6001                   ; non-zero if program saved to battery backup
                   bbTXTEND        .eq     $6002                   ; backed-up TXTEND (end of program text)
                   bgGotFlag       .eq     $6004                   ; non-zero if bg data is saved at $6C00
                   bgGetVScroll    .eq     $6005                   ; saved vscroll for saved screen data
                   basicProgram    .eq     $6006                   ; The start of the BASIC program in memory
                   bgGetRam        .eq     $6c00  {addr/1024}      ; region to save bg data with BGGET
                   memoryTop       .eq     $6fff                   ; What FRETOP is set to by default

                   ; ROM Header
                                   .addrs  NA                      ;(auto-generated)
0000: 4e 45 53 1a+                 .bulk   $4e,$45,$53,$1a,$02,$01,$03,$00,$00,$00,$00,$00,$00,$00,$00,$00
                                   .adrend ↑ NA

                   ; Code start
                                   .addrs  $8000
8000: 4c ba 80                     jmp     _Reset

8003: 41 65        tbl_GameTXTENDs .dd2    $6541                   ;End of "GAME 0" in mem (prog len + $6006)
8005: 8a 6a                        .dd2    $6a8a                   ;End of "GAME 1"
8007: 8c 6c                        .dd2    $6c8c                   ;End of "GAME 2"
8009: f8 6c                        .dd2    $6cf8                   ;End of "GAME 3"
800b: 00 d4        tbl_GameTexts   .dd2    txtGame0
800d: fe db                        .dd2    txtGame1
800f: 82 e6                        .dd2    txtGame2
8011: 08 f3                        .dd2    txtGame3
                   tbl_NoteValToPdLen
8013: ae 0e                        .dd2    $0eae
8015: 4e 0e                        .dd2    $0e4e
8017: f3 0d                        .dd2    $0df3
8019: 9e 0d                        .dd2    $0d9e
801b: 4d 0d                        .dd2    $0d4d
801d: 01 0d                        .dd2    $0d01
801f: b9 0c                        .dd2    $0cb9
8021: 75 0c                        .dd2    $0c75
8023: 35 0c                        .dd2    $0c35
8025: f8 0b                        .dd2    $0bf8
8027: bf 0b                        .dd2    $0bbf
8029: 89 0b                        .dd2    $0b89
802b: 57 0b                        .dd2    $0b57
802d: 27 0b                        .dd2    $0b27
802f: f9 0a                        .dd2    $0af9
8031: cf 0a                        .dd2    $0acf
8033: a6 0a                        .dd2    $0aa6
8035: 80 0a                        .dd2    $0a80
8037: 5c 0a                        .dd2    $0a5c
8039: 3a 0a                        .dd2    $0a3a
803b: 1a 0a                        .dd2    $0a1a
803d: fc 09                        .dd2    $09fc
803f: df 09                        .dd2    $09df
8041: c4 09                        .dd2    $09c4
8043: ab 09                        .dd2    $09ab
8045: 93 09                        .dd2    $0993
8047: 7c 09                        .dd2    $097c
8049: 67 09                        .dd2    $0967
804b: 52 09                        .dd2    $0952
804d: 3f 09                        .dd2    $093f
804f: 2d 09                        .dd2    $092d
8051: 1c 09                        .dd2    $091c
8053: 0c 09                        .dd2    $090c
8055: fd 08                        .dd2    $08fd
8057: ee 08                        .dd2    $08ee
8059: e1 08                        .dd2    $08e1
805b: d4 08                        .dd2    $08d4
805d: c8 08                        .dd2    $08c8
805f: bd 08                        .dd2    $08bd
8061: b2 08                        .dd2    $08b2
8063: a8 08                        .dd2    $08a8
8065: 9f 08                        .dd2    $089f
8067: 96 08                        .dd2    $0896
8069: 8d 08                        .dd2    $088d
806b: 85 08                        .dd2    $0885
806d: 7e 08                        .dd2    $087e
806f: 76 08                        .dd2    $0876
8071: 70 08                        .dd2    $0870
8073: 69 08                        .dd2    $0869
8075: 63 08                        .dd2    $0863
8077: 5e 08                        .dd2    $085e
8079: 58 08                        .dd2    $0858
807b: 53 08                        .dd2    $0853
807d: 4f 08                        .dd2    $084f
807f: 4a 08                        .dd2    $084a
8081: 46 08                        .dd2    $0846
8083: 42 08                        .dd2    $0842
8085: 3e 08                        .dd2    $083e
8087: 3a 08                        .dd2    $083a
8089: 37 08                        .dd2    $0837
808b: 34 08                        .dd2    $0834
808d: 31 08                        .dd2    $0831
808f: 2e 08                        .dd2    $082e
8091: 2b 08                        .dd2    $082b
8093: 29 08                        .dd2    $0829
8095: 27 08                        .dd2    $0827
8097: 24 08                        .dd2    $0824
8099: 22 08                        .dd2    $0822
809b: 20 08                        .dd2    $0820
809d: 1e 08                        .dd2    $081e
809f: 1c 08                        .dd2    $081c
80a1: 1b 08                        .dd2    $081b
80a3: 01 02 03 04+ tbl_NoteLengths .bulk   $01,$02,$03,$04,$06,$08,$0c,$10
80ab: 18 20                        .bulk   $18,$20
                   ; (note names begin with 'A', but values start at 'C' and wrap around)
                   tbl_NoteNameToValue
80ad: 12                           .dd1    18
80ae: 16                           .dd1    22
80af: 00                           .dd1    0
80b0: 04                           .dd1    4
80b1: 08                           .dd1    8
80b2: 0a                           .dd1    10
80b3: 0e                           .dd1    14
                   tbl_OctaveBaseNoteVals
80b4: 00                           .dd1    0
80b5: 18                           .dd1    24
80b6: 30                           .dd1    48
80b7: 48                           .dd1    72
80b8: 60                           .dd1    96
80b9: 78                           .dd1    120

                   vZpKeyRdCtr     .var    $28    {addr/1}

80ba: a9 00        _Reset          lda     #$00
80bc: 8d 00 20                     sta     PPUCTRL
80bf: 8d 01 20                     sta     PPUMASK                 ;turn off rendering
80c2: 20 2b b4                     jsr     WaitForVBlank
80c5: 20 2b b4                     jsr     WaitForVBlank
80c8: a9 4c                        lda     #$4c                    ;JMP opcode
80ca: 85 ed                        sta     zpNmiTrampoline
80cc: a9 71                        lda     #<NMI_DefaultHandler
80ce: 85 ee                        sta     zpNmiTrampoline+1
80d0: a9 89                        lda     #>NMI_DefaultHandler
80d2: 85 ef                        sta     zpNmiTrampoline+2       ;set default NMI handler to NMI_DefaultHandler
80d4: 20 ab b3                     jsr     InitPpuApu              ;init various PPU, APU stuff
80d7: 20 a7 b4                     jsr     DoCLS_1                 ;clear bg screen 1
80da: a9 20                        lda     #>VRAM_screen0
80dc: 85 4f                        sta     zpPpuScreenBasePage     ;send screen updates to screen 0
80de: a9 1e                        lda     #30
80e0: 85 28                        sta     vZpKeyRdCtr
80e2: 20 45 b9     @keyRdLp        jsr     ReadKeyInsMacros        ;read keyboard
80e5: f0 1b                        beq     @haveKbd                ;null char returned? -> done checking
80e7: c6 28                        dec     vZpKeyRdCtr             ;else, either key being held, or kbd not connected
80e9: d0 f7                        bne     @keyRdLp                ;-> loop until key-down reported for 30 checks
80eb: a9 05                        lda     #$05
80ed: 85 48                        sta     zpCH
80ef: a9 04                        lda     #$04
80f1: 85 49                        sta     zpCV                    ;LOCATE 5,4
80f3: a9 01                        lda     #DTU__KEYBOARD_WO_SETSUZOKU
80f5: 20 0d 82                     jsr     DirectTheUser
80f8: a9 06                        lda     #DTU__SHITE_KUDASAI
80fa: 20 0d 82                     jsr     DirectTheUser           ;ask the user to please connect the keyboard
80fd: 20 45 b9     @waitKbdConn    jsr     ReadKeyInsMacros        ;read keyboard
8100: d0 fb                        bne     @waitKbdConn            ;-> loop until null character returned (kbd connected)
8102: 20 f3 81     @haveKbd        jsr     CheckBackupSwitch       ;is the backup switch on?
8105: 90 1a                        bcc     @buSwOff                ;off -> good to go!
8107: 20 ab b4                     jsr     DoCLS                   ;on: this locks the battery-backed RAM, which we don't want
810a: a9 01                        lda     #$01
810c: 85 48                        sta     zpCH
810e: a9 04                        lda     #$04
8110: 85 49                        sta     zpCV                    ;LOCATE 1,4
8112: a9 03                        lda     #DTU__BACKUP_SWITCH_WO
8114: 20 0d 82                     jsr     DirectTheUser
8117: a9 04                        lda     #DTU__OFF_NI_SHITE_KUDASAI
8119: 20 0d 82                     jsr     DirectTheUser           ;ask the user to turn off the cart's backup switch
811c: 20 f3 81     @waitBuSwOff    jsr     CheckBackupSwitch       ;test backup switch
811f: b0 fb                        bcs     @waitBuSwOff            ;-> loop until it's off
8121: a9 00        @buSwOff        lda     #$00
8123: 85 7c                        sta     zpBgGotFlag             ;clear zpBgGotFlag
8125: a5 9e                        lda     zpBackupRstrd
8127: c9 59                        cmp     #BU_RESTORED_MARK       ;did we already restore program from battery backup?
8129: f0 2f                        beq     HotStart                ;yes -> skip restoration, then
812b: ad 01 60                     lda     bbBackupAvail
812e: c9 4c                        cmp     #BU_AVAIL_MARK          ;does battery-backed RAM indicate a program is backed-up?
8130: d0 4e                        bne     RstNoBackupReady        ;no ->
8132: 20 a7 b4                     jsr     DoCLS_1                 ;clear screen 1
8135: ad 04 60                     lda     bgGotFlag
8138: 85 7c                        sta     zpBgGotFlag             ;note whether we have BG data backed-up
813a: ad 02 60                     lda     bbTXTEND                ;copy battery-backed TXTEND to the real one
813d: 85 07                        sta     zpTXTEND
813f: ad 03 60                     lda     bbTXTEND+1
8142: 85 08                        sta     zpTXTEND+1
8144: a9 06                        lda     #<basicProgram          ;copy (hard-coded) basicProgram start to TXTTAB
8146: 85 05                        sta     zpTXTTAB
8148: a9 60                        lda     #>basicProgram
814a: 85 06                        sta     zpTXTTAB+1
814c: a9 00                        lda     #$00
814e: aa                           tax
814f: 9d 00 60     @delBuVarsLp    sta     $6000,x                 ;delete the backup vars, now that they're restored
8152: e8                           inx                             ;(avoids them getting out of date, forces user to run BACKUP cmd again)
8153: e0 05                        cpx     #$05
8155: d0 f8                        bne     @delBuVarsLp            ;-> loop until backup vars deleted (5 bytes)
8157: 20 a6 97                     jsr     Clear                   ;clear BASIC vars
815a: 20 62 86     HotStart        jsr     InitVars                ;init zero-page vars
815d: 20 27 b3                     jsr     PrintHotStart           ;print the "HOT START" message
8160: 20 6b b5                     jsr     EnsureSOL               ;CRLF
8163: a5 7c                        lda     zpBgGotFlag             ;have we got bg data backed-up, too?
8165: f0 11                        beq     @basic                  ;no -> start up BASIC
8167: a9 7b                        lda     #<str_BG                ;yes, bg backed up: indicate it
8169: 85 52                        sta     zpOutputStr
816b: a9 81                        lda     #>str_BG
816d: 85 53                        sta     zpOutputStr+1
816f: 20 5a 89                     jsr     PrintString             ;print "[BG]"
8172: 20 6b b5                     jsr     EnsureSOL               ;CRLF
8175: 20 06 b2                     jsr     RestoreBackupBgData     ;restore the backed-up bg data
8178: 4c 6a 82     @basic          jmp     CmdFn_END               ;enter BASIC

817b: 5b 42 47 5d+ str_BG          .zstr   “[BG]”

                   • Clear variables

                   RstNoBackupReady
8180: 20 a7 b4                     jsr     DoCLS_1                 ;clear screen 1
8183: a9 59                        lda     #BU_RESTORED_MARK
8185: 85 9e                        sta     zpBackupRstrd           ;no backup avail, so mark it "restored" to avoid future check
8187: a9 00                        lda     #$00
8189: 8d 04 60                     sta     bgGotFlag               ;if no backup, no backed-up BG data
818c: 4c 5e 82                     jmp     RstNoBackupStartBasic

                   ; SYSTEM command; also BACKUP.
                   CmdFn_SYSTEMorBACKUP
818f: a5 33                        lda     zpPpuMaskVal
8191: 29 ef                        and     #$ef
8193: 85 33                        sta     zpPpuMaskVal            ;disable sprites
8195: 8d 01 20                     sta     PPUMASK
8198: a9 4c                        lda     #$4c                    ;JMP opcode?
819a: 8d 01 60                     sta     bbBackupAvail           ;idk why we're JMPing to the null byte
819d: a5 07                        lda     zpTXTEND                ; at the end of the BASIC prog, but anyway
819f: 8d 02 60                     sta     bbTXTEND
81a2: a5 08                        lda     zpTXTEND+1
81a4: 8d 03 60                     sta     $6003
81a7: 20 ab b4                     jsr     DoCLS                   ;clear the screen
81aa: a9 02                        lda     #$02
81ac: 85 48                        sta     zpCH
81ae: a9 04                        lda     #$04
81b0: 85 49                        sta     zpCV                    ;LOCATE 2,4
81b2: a9 03                        lda     #DTU__BACKUP_SWITCH_WO
81b4: 20 0d 82                     jsr     DirectTheUser
81b7: a9 00                        lda     #DTU__ON
81b9: 20 0d 82                     jsr     DirectTheUser
81bc: a9 05                        lda     #DTU__NI_SHITE_KUDASAI
81be: 20 0d 82                     jsr     DirectTheUser           ;print "Please turn on the backup switch" (in Japanese)
81c1: 20 45 b9     @whileBuSwOff   jsr     ReadKeyInsMacros
81c4: c9 03                        cmp     #$03                    ;check if STOP key is pressed
81c6: f0 92                        beq     HotStart                ;pressed? -> BASIC hot start
81c8: 20 f3 81                     jsr     CheckBackupSwitch       ;is backup switch on?
81cb: 90 f4                        bcc     @whileBuSwOff           ;no -> loop until it is, or until STOP key pressed
81cd: 20 ab b4                     jsr     DoCLS                   ;clear screen
81d0: a9 05                        lda     #$05
81d2: 85 48                        sta     zpCH
81d4: a9 04                        lda     #$04
81d6: 85 49                        sta     zpCV                    ;LOCATE 5,4
81d8: a9 02                        lda     #DTU__DENGEN_WO
81da: 20 0d 82                     jsr     DirectTheUser
81dd: a9 04                        lda     #DTU__OFF_NI_SHITE_KUDASAI
81df: 20 0d 82                     jsr     DirectTheUser           ;print "Please turn off power" (in Japanese)
81e2: 20 45 b9     @stopLp         jsr     ReadKeyInsMacros
81e5: c9 03                        cmp     #$03                    ;STOP key pressed?
81e7: d0 f9                        bne     @stopLp                 ;no -> just keep checking for it
81e9: 4c ba 80                     jmp     _Reset                  ;yes -> cold-start BASIC

                   ; Sets the current VRAM address from the A (high) and X (low) registers.
81ec: 8d 06 20     PpuGoToAX       sta     PPUADDR
81ef: 8e 06 20                     stx     PPUADDR
81f2: 60                           rts

                   ; Checks whether the physical battery-backup switch on the back of the Family
                   ; BASIC cartridge is on or off. Carry is unset while switch is off, set if on
                   CheckBackupSwitch
81f3: ad 00 60                     lda     $6000
81f6: 48                           pha                             ;save whatever's at location $6000 (cartridge RAM start)
81f7: a9 19                        lda     #$19
81f9: 8d 00 60                     sta     $6000                   ;try writing to it
81fc: cd 00 60                     cmp     $6000                   ;do we read back the value we just wrote?
81ff: f0 06                        beq     @swOff                  ;yes -> backup switch is off
8201: 68                           pla                             ;no: backup switch is on
8202: 8d 00 60                     sta     $6000                   ;restore original val at $6000
8205: 38                           sec                             ;set carry (switch on)
8206: 60                           rts                             ;and return

8207: 68           @swOff          pla
8208: 8d 00 60                     sta     $6000                   ;restore original val at $6000
820b: 18                           clc                             ;clear carry (switch is off)
820c: 60                           rts                             ;and return

                   ; Tell the user to do something
                   ; (turn backup switch on/off, power off, connect keyboard)
820d: 0a           DirectTheUser   asl     A                       ;multiply message index by 2, for table indexing
820e: aa                           tax
820f: bd 50 82                     lda     tbl_DirectTheUserStrs,x ;load the message string
8212: 85 52                        sta     zpOutputStr
8214: bd 51 82                     lda     tbl_DirectTheUserStrs+1,x
8217: 85 53                        sta     zpOutputStr+1
8219: 4c 5a 89                     jmp     PrintString             ;and output it

821c: 20 4f 4e 00  strON           .zstr   “ ON”
                   strKEYBOARD_WO_SETSUZOKU
8220: 66 2d aa 2d+                 .bulk   $66,$2d,$aa,$2d,$a5,$20,$8d,$20,$6d,$71,$a0,$67,$00
822d: a4 8c 9a 8c+ strDENGEN_WO    .bulk   $a4,$8c,$9a,$8c,$20,$8d,$00
                   strBACKUP_SWITCH_WO
8234: a6 96 67 60+                 .bulk   $a6,$96,$67,$60,$96,$ad,$20,$6c,$61,$96,$70,$20,$8d,$00
                   strOFF_NI_SHITE_KUDASAI
8242: 20                           .dd1    $20                     ;Falls thru to next string start
8243: 4f                           .dd1    $4f
8244: 46                           .dd1    $46
8245: 46                           .dd1    $46
                   strNI_SHITE_KUDASAI
8246: 20 75                        .bulk   $20,$75                 ;Falls thru to next string start
                   strSHITE_KUDASAI
8248: 20 6b 72 67+                 .bulk   $20,$6b,$72,$67,$a1,$6a,$61,$00
                   tbl_DirectTheUserStrs
8250: 1c 82                        .dd2    strON
8252: 20 82                        .dd2    strKEYBOARD_WO_SETSUZOKU
8254: 2d 82                        .dd2    strDENGEN_WO
8256: 34 82                        .dd2    strBACKUP_SWITCH_WO
8258: 42 82                        .dd2    strOFF_NI_SHITE_KUDASAI
825a: 46 82                        .dd2    strNI_SHITE_KUDASAI
825c: 48 82                        .dd2    strSHITE_KUDASAI

                   RstNoBackupStartBasic
825e: a2 ff                        ldx     #$ff
8260: 9a                           txs                             ;initialize stack pointer register
8261: 20 62 86                     jsr     InitVars                ;clear zero-page vars
8264: 20 bc 84                     jsr     ResetTxtVarPtrs         ;initialize basic start/end (empty)
8267: 20 f7 b2                     jsr     PrintStartupText        ;print BASIC startup text
826a: 20 c0 88     CmdFn_END       jsr     ClearStoppedFlag        ;clear "stopped" flag
826d: 85 e7                        sta     zpErrHandStat           ;store zero (EH_NO_HANDLER)
                   ResetAndEnterDirectMode
826f: a9 00                        lda     #$00
8271: 85 e2                        sta     zpAutoEnabled           ;turn off auto-increment and erase its data
8273: 85 a2                        sta     zpAutoLNumIncr
8275: 85 a3                        sta     zpAutoLNumIncr+1
8277: a2 ff                        ldx     #$ff                    ;clear the CPU stack
8279: 9a                           txs
827a: 8a                           txa                             ;and push #$FFFF (as a limit to NEXT/RETURN stack searches?)
827b: 48                           pha
827c: 48                           pha
827d: 20 66 b7                     jsr     DeactivateKanaMode      ;ensure we're in ASCII typing mode
                   SaveStackAndDirectMode
8280: ba                           tsx
8281: 86 17                        stx     zpSavedStack
8283: 58                           cli
                   ; Loop over user input lines, obeying commands and entering numbered program
                   ; code lines.
8284: a6 17        DirectModeLoop  ldx     zpSavedStack            ;restore stack ptr from zpSavedStack
8286: 9a                           txs
8287: 20 6b b5                     jsr     EnsureSOL
828a: 20 ed b2                     jsr     PrintOK
828d: a5 24                        lda     zpStoppedFlag           ;STOPped?
828f: f0 05                        beq     @skipPd                 ;no -> skip period
8291: a9 2e                        lda     #‘.’                    ;yes: print period after "OK", to indicate it was a STOP
8293: 20 75 b5                     jsr     PrintOneChar            ; (and can be CONTinued)
8296: 20 6b b5     @skipPd         jsr     EnsureSOL               ;another CRLF
8299: 20 cc 82     DML_skipOK      jsr     DirectMode_handleAuto   ;auto-prefix line number, if active
829c: 20 04 bd                     jsr     ReadLine                ;read a line of the user's input
829f: c9 03                        cmp     #$03                    ;STOP typed during line input?
82a1: f0 1e                        beq     DML_cancelAuto          ;yes -> ignore line and reprompt
82a3: 20 a4 8b                     jsr     InitLineBufferScanning  ;point TXTPTR at lineBuffer
82a6: 20 b2 90                     jsr     GetTxtChar
82a9: 20 bf 90                     jsr     TxtSkipSpaces
82ac: 29 ff                        and     #$ff                    ;empty line?
82ae: f0 e9                        beq     DML_skipOK              ;yes -> reprompt
82b0: 20 c8 90                     jsr     IsDigit                 ;start with a digit?
82b3: b0 09                        bcs     @directExec             ;no -> directly execute commands
82b5: 20 e5 84                     jsr     ProcessNumberedLine     ;yes -> handle the numbered line
82b8: 20 a6 97                     jsr     Clear                   ;clear variables (we may have just trampled variable space)
82bb: 4c 99 82                     jmp     DML_skipOK

82be: 4c 34 83     @directExec     jmp     ExecuteDirectModeLine

82c1: a5 e2        DML_cancelAuto  lda     zpAutoEnabled
82c3: f0 d4                        beq     DML_skipOK
82c5: a9 00                        lda     #$00
82c7: 85 e2                        sta     zpAutoEnabled
82c9: 4c 84 82                     jmp     DirectModeLoop

                   DirectMode_handleAuto
82cc: a5 e2                        lda     zpAutoEnabled           ;AUTO active?
82ce: f0 55                        beq     rts_8325                ;no -> nothing to do; return
82d0: a5 a0                        lda     zpNxtAutoLNum           ;copy the next auto linenum to zpFindLNum
82d2: 85 44                        sta     zpFindLNum
82d4: a5 a1                        lda     zpNxtAutoLNum+1
82d6: 85 45                        sta     zpFindLNum+1
82d8: 20 40 85                     jsr     FindLineNum             ;and search for that line number
82db: d0 20                        bne     DML_noSuchLine          ;no such line? -> don't need to handle it then
82dd: 20 30 94                     jsr     SaveTXTPTR              ;save TXTPTR
82e0: a5 5b                        lda     zpTxtLink
82e2: 85 09                        sta     zpTXTPTR                ;set found line as new TXTPTR
82e4: a5 5c                        lda     zpTxtLink+1
82e6: 85 0a                        sta     zpTXTPTR+1
82e8: 20 ee 87                     jsr     PrintLineNumAndDetok    ;print the line we found
82eb: 20 39 94                     jsr     RestoreTXTPTR           ;restore saved TXTPTR
82ee: a6 49                        ldx     zpCV                    ;get CV (which is one past the just-printed line)
82f0: ca                           dex                             ;decrement (on the printed line)
82f1: ca                           dex                             ;decrement again (just above the printed line)
82f2: 20 37 b6     @findLineStart  jsr     DoesRowWrap             ;is this a row that wrapped to next line?
82f5: f0 03                        beq     @found                  ;no -> found end of prev line
82f7: ca                           dex                             ;yes, wraps. Back up one and see if *that* wraps
82f8: 10 f8                        bpl     @findLineStart
82fa: e8           @found          inx                             ;incr, from end of prev line, to start of our printed line
82fb: 86 49                        stx     zpCV                    ;and save as current row
82fd: a5 a0        DML_noSuchLine  lda     zpNxtAutoLNum           ;zCH is guaranteed to be 0 already,
82ff: 85 28                        sta     zpWAccum                ; since printed line ended with CRLF
8301: a5 a1                        lda     zpNxtAutoLNum+1
8303: 85 29                        sta     zpWAccum+1              ;copy auto linenum to WAccum
8305: 20 3e 91                     jsr     PosDecStrFromWord       ;convert to decimal
8308: 20 d6 90                     jsr     FindFirstNonzeroDigit   ;skip leading zeroes
830b: 20 e0 87                     jsr     QueueOutpNumStrTmp      ;queue it for output
830e: a9 20                        lda     #$20                    ;plus a SPACE character
8310: 20 4d 89                     jsr     QueueCharForOutput
8313: 20 5d 89                     jsr     PrintOutBuf             ;print out linenum + space.
                   IncrementAutoLNum
8316: 18                           clc                             ;finally, increment auto linenum for next time
8317: a5 a0                        lda     zpNxtAutoLNum
8319: 65 a2                        adc     zpAutoLNumIncr
831b: 85 a0                        sta     zpNxtAutoLNum
831d: a5 a1                        lda     zpNxtAutoLNum+1
831f: 65 a3                        adc     zpAutoLNumIncr+1
8321: b0 03                        bcs     DeactivateAutoAndError  ;overflowed? -> deactivate AUTO and error.
8323: 85 a1                        sta     zpNxtAutoLNum+1
8325: 60           rts_8325        rts

                   DeactivateAutoAndError
8326: a9 00                        lda     #$00
8328: 85 e2                        sta     zpAutoEnabled
832a: 4c f2 8e                     jmp     ErrorOverflow

                   ; This routine used to flag that we are in direct mode, and there is no line
                   ; number for the executing BASIC code.
                   StrikeRunLineNum
832d: a9 ff                        lda     #$ff
832f: 85 0b                        sta     zpRunLineNum
8331: 85 0c                        sta     zpRunLineNum+1
8333: 60                           rts

                   ExecuteDirectModeLine
8334: 20 5b 92                     jsr     Tokenize                ; tokenize the line
8337: a9 89                        lda     #<dmFakeNextLine
8339: 85 0d                        sta     zpRunTxtNxtLnk          ;Set "next line link" to an arbitrary location that starts with zero (reads as end of program)
833b: a9 bb                        lda     #>dmFakeNextLine
833d: 85 0e                        sta     zpRunTxtNxtLnk+1
833f: a9 00                        lda     #<tokenizeBuffer        ;point TXTPTR at tokenizeBuffer
8341: 85 09                        sta     zpTXTPTR                ; so we execute from there
8343: a9 03                        lda     #>tokenizeBuffer
8345: 85 0a                        sta     zpTXTPTR+1
8347: 20 2d 83                     jsr     StrikeRunLineNum
                   ; From here on down, this routine is used both by ExecuteDirectModeLine
                   ; (directly above), and also by a running basic program (for each command)
834a: 20 55 88     ExecuteCommand  jsr     StopIfStopKey
834d: 20 cd 8b                     jsr     ExecCheckMem
8350: 20 bf 90                     jsr     TxtSkipSpaces
8353: 48                           pha
8354: a5 09                        lda     zpTXTPTR                ;save this spot as start of the current command
8356: 85 58                        sta     zpTxtThisCmd
8358: a5 0a                        lda     zpTXTPTR+1
835a: 85 59                        sta     zpTxtThisCmd+1
835c: 20 8c 90                     jsr     TxtPtrIncr
835f: 68                           pla                             ;got null char (end of line)?
8360: f0 52                        beq     skipToNextLine_83B4     ;yes -> skip to next
8362: c9 3a                        cmp     #‘:’                    ;got colon?
8364: f0 e4                        beq     ExecuteCommand          ;yes -> reset zpTxtThisCmd to here
8366: 48                           pha
8367: 20 fd 83                     jsr     MaybePrintTrace         ;print line number trace, if TRON is active
836a: 68                           pla
836b: c9 27                        cmp     #‘'’                    ;got apostrophe (REM)?
836d: f0 45                        beq     skipToNextLine_83B4     ;yes -> skip line
836f: c9 80                        cmp     #$80                    ;got a command token?
8371: b0 44                        bcs     execCmdByTok_83B7       ;yes -> execute it
8373: 20 c4 a7                     jsr     IsAlpha                 ;got an alphabetic character?
8376: b0 39                        bcs     errSyntax_83B1          ;no -> error
8378: 48                           pha
8379: 20 a6 90                     jsr     TxtPtrDecr              ;backup TXTPTR to the start of the var name
837c: 68                           pla
837d: 20 76 a5                     jsr     EvalVarNameOrSynErr     ;find or create the variable for this name
8380: a5 90                        lda     zpVarType               ;push the var type
8382: 48                           pha
8383: a5 8c                        lda     zpVarData               ;push the var data ptr
8385: 48                           pha
8386: a5 8d                        lda     zpVarData+1
8388: 48                           pha
8389: 20 d9 86                     jsr     SkipEqualsOrErr         ;demand a '=' here
838c: 20 83 a2                     jsr     FrmEval                 ;evaluate the value
838f: 68                           pla
8390: 85 8d                        sta     zpVarData+1             ;pull var data ptr and restore
8392: 68                           pla
8393: 85 8c                        sta     zpVarData
8395: 68                           pla
8396: 85 90                        sta     zpVarType               ;pull var type and restore
8398: a0 00                        ldy     #$00
839a: 29 0f                        and     #$0f
839c: c9 03                        cmp     #TYP_STR                ;is var base type a string?
839e: f0 1f                        beq     ExecCmd_SetStringVar    ;yes -> handle as string
83a0: 20 0d aa                     jsr     ErrorUnlessInteger      ;ensure value for int is int!
83a3: ad 00 04                     lda     exprParseStk            ;copy expression result to var data
83a6: 91 8c                        sta     (zpVarData),y
83a8: ad 01 04                     lda     exprParseStk+1
83ab: c8                           iny
83ac: 91 8c                        sta     (zpVarData),y
83ae: 4c 4a 83                     jmp     ExecuteCommand          ;continue executing successive commands

83b1: 4c 91 84     errSyntax_83B1  jmp     ErrorSyntax

                   skipToNextLine_83B4
83b4: 4c 69 84                     jmp     ExecSkipToNextLine

                   execCmdByTok_83B7
83b7: 4c 29 84                     jmp     ExecuteCommandByToken

                   ErrorStringTooLong
83ba: a9 0c                        lda     #err_ST
83bc: 4c 37 b2                     jmp     PrintErrorAndStop

                   ExecCmd_SetStringVar
83bf: 20 10 aa                     jsr     ErrorUnlessString       ;ensure value for string var is string!
83c2: 20 cb 83                     jsr     CpyExprStrToStrVar      ;copy the string value to our variable data space
83c5: 4c 4a 83                     jmp     ExecuteCommand          ;continue executing successive commands

83c8: 91 8c        @setEmptyAndRts sta     (zpVarData),y
83ca: 60                           rts

                   CpyExprStrToStrVar
83cb: a0 00                        ldy     #$00
83cd: ad 00 04                     lda     exprParseStk            ;examine string length
83d0: f0 f6                        beq     @setEmptyAndRts         ;zero? -> just copy the length only
83d2: c9 20                        cmp     #32                     ;len >= 32?
83d4: b0 e4                        bcs     ErrorStringTooLong      ;yes -> error, too long
83d6: 91 8c                        sta     (zpVarData),y           ;save length to variable
83d8: ad 01 04                     lda     exprParseStk+1          ;copy str content ptr into zero-page pointers for indexing
83db: 85 19                        sta     zpMisc
83dd: ad 02 04                     lda     exprParseStk+2
83e0: 85 1a                        sta     zpMisc+1
83e2: c8                           iny
83e3: 98                           tya
83e4: 18                           clc
83e5: 65 8c                        adc     zpVarData
83e7: 85 8c                        sta     zpVarData
83e9: a9 00                        lda     #$00
83eb: 65 8d                        adc     zpVarData+1
83ed: 85 8d                        sta     zpVarData+1             ;set copy dest ptr to one past zpVarData (i.e. contents, not strlen)
83ef: ae 00 04                     ldx     exprParseStk
83f2: a0 00                        ldy     #$00
83f4: b1 19        @cpyLp          lda     (zpMisc),y
83f6: 91 8c                        sta     (zpVarData),y
83f8: c8                           iny
83f9: ca                           dex
83fa: d0 f8                        bne     @cpyLp
83fc: 60           rts_83FC        rts

                   ; Print line number trace, if active
83fd: a5 97        MaybePrintTrace lda     zpTraceOnFlag           ;return if trace is off, or if we're in direct mode
83ff: f0 fb                        beq     rts_83FC
8401: a9 ff                        lda     #$ff
8403: c5 0b                        cmp     zpRunLineNum
8405: d0 04                        bne     @doTrace
8407: c5 0c                        cmp     zpRunLineNum+1
8409: f0 f1                        beq     rts_83FC
840b: a9 23        @doTrace        lda     #‘#’
840d: 20 4d 89                     jsr     QueueCharForOutput      ;queue a '#'
8410: a5 0b                        lda     zpRunLineNum
8412: 85 28                        sta     zpWAccum
8414: a5 0c                        lda     zpRunLineNum+1
8416: 85 29                        sta     zpWAccum+1
8418: 20 3e 91                     jsr     PosDecStrFromWord       ;queue the line number
841b: 20 d6 90                     jsr     FindFirstNonzeroDigit
841e: 20 e0 87                     jsr     QueueOutpNumStrTmp
8421: a9 20                        lda     #$20                    ;queue a SPACE
8423: 20 4d 89                     jsr     QueueCharForOutput
8426: 4c 5d 89                     jmp     PrintOutBuf             ;now print the queue

                   ExecuteCommandByToken
8429: 38                           sec
842a: e9 80                        sbc     #$80                    ;remove high bit from token
842c: c9 44                        cmp     #$44                    ;was it a function token, rather than command token?
842e: b0 61                        bcs     ErrorSyntax             ;yes -> syntax error
8430: 0a                           asl     A                       ;double the value (for jump table index)
8431: aa                           tax
8432: bd bf ce                     lda     jtbl_Commands,x         ;put address of command handler into zpMisc
8435: 85 19                        sta     zpMisc
8437: bd c0 ce                     lda     jtbl_Commands+1,x
843a: 85 1a                        sta     zpMisc+1
843c: ba                           tsx
843d: 86 17                        stx     zpSavedStack            ;save stack pointer
843f: 20 9c 84                     jsr     IsEndOfCmd              ;load next non-space char in A, and set Z iff end of command
8442: 20 3e a3                     jsr     JmpIndirectZpMisc       ;execute the command
8445: 4c 4a 83                     jmp     ExecuteCommand          ;continue executing successive commands

                   ; Enters DirectModeLoop. First checks if we have a resumable error AND were
                   ; executing a direct command, and gives a "NR" (No Resume) error if so.
8448: a5 e7        FinishExecution lda     zpErrHandStat
844a: c9 02                        cmp     #EH_IN_HANDLER
844c: f0 03                        beq     @errHandNeverResmd
                   @enterDirectMode
844e: 4c 80 82                     jmp     SaveStackAndDirectMode

                   @errHandNeverResmd
8451: 20 59 84                     jsr     @chkDirect              ;are we in direct mode (no line num)?
8454: b0 f8                        bcs     @enterDirectMode        ;yes -> just go to direct mode prompt
8456: 4c 1b 95                     jmp     ErrorNoResume           ;no: error, bc we handled an error but didn't resume after

8459: a9 ff        @chkDirect      lda     #$ff
845b: c5 0b                        cmp     zpRunLineNum
845d: d0 06                        bne     @notDirect
845f: c5 0c                        cmp     zpRunLineNum+1
8461: d0 02                        bne     @notDirect
8463: 38                           sec
8464: 60                           rts

8465: 18           @notDirect      clc
8466: 60                           rts

8467: 68           CmdFn_DATAorREM pla                             ;pull return addr (we'll jump directly)
8468: 68                           pla
                   ; Also used by apostrophe ('), and to skip the remainder of a false IF statement
                   ExecSkipToNextLine
8469: a5 0d                        lda     zpRunTxtNxtLnk          ;replace zpTXTPTR with ptr to start of next line
846b: 85 09                        sta     zpTXTPTR
846d: a5 0e                        lda     zpRunTxtNxtLnk+1
846f: 85 0a                        sta     zpTXTPTR+1
                   ; Also used by RUN. Update line linkage info before running a line
8471: 20 b2 90     ExecuteLine     jsr     GetTxtChar              ;get offset from here to start of next line
8474: f0 d2                        beq     FinishExecution         ;zero (last line of prog)? -> completed execution
8476: 18                           clc
8477: 65 09                        adc     zpTXTPTR                ;add offset to current TXTPTR
8479: 85 0d                        sta     zpRunTxtNxtLnk          ;and store in zpRunTxtNxtLnk as ptr to start of next line
847b: a9 00                        lda     #$00
847d: 65 0a                        adc     zpTXTPTR+1
847f: 85 0e                        sta     zpRunTxtNxtLnk+1
8481: c8                           iny
8482: b1 09                        lda     (zpTXTPTR),y            ;save current line's line number
8484: 85 0b                        sta     zpRunLineNum
8486: c8                           iny
8487: b1 09                        lda     (zpTXTPTR),y
8489: 85 0c                        sta     zpRunLineNum+1
848b: 20 10 9f                     jsr     TxtPtrAdvThree          ;skip TXTPTR past offset and line number
848e: 4c 4a 83                     jmp     ExecuteCommand          ;and execute the first command

                   ; Emit a syntax error (code [SN]) and stop execution. In addition to being
                   ; called directly from various places, this function is also in the jump table
                   ; for the keywords THEN, TO, STEP, and OFF, since these are all marker tokens
                   ; and not real commands. (ON can start legit commands, so it does not go here.)
8491: a9 01        ErrorSyntax     lda     #err_SN
8493: ae                           ldx ▼   $04a9
                   ErrorIllegalValue
8494: a9 04                        lda     #err_IL
8496: 4c 37 b2                     jmp     PrintErrorAndStop

                   AdvThenIsEndOfCmd
8499: 20 8c 90                     jsr     TxtPtrIncr
                   ; Z is set, if and only if we're at the end of the command (no more args)
849c: 20 bf 90     IsEndOfCmd      jsr     TxtSkipSpaces
849f: 29 ff                        and     #$ff
84a1: f0 02                        beq     @rts
84a3: c9 3a                        cmp     #‘:’
84a5: 60           @rts            rts

                   ClearStoppedTraceAndError
84a6: 20 c0 88                     jsr     ClearStoppedFlag
84a9: 85 97                        sta     zpTraceOnFlag           ;storing zero
84ab: 85 e7        ClearErrorFlags sta     zpErrHandStat           ;clear error/resume flags
84ad: 85 b5                        sta     zpErrCode
84af: 85 b6                        sta     zpUnusedErrVar
84b1: 85 b7                        sta     zpErrLineNum
84b3: 85 b8                        sta     zpErrLineNum+1
84b5: 60                           rts

84b6: 20 bc 84     CmdFn_NEW       jsr     ResetTxtVarPtrs         ;reset pointers to BASIC text and vars
84b9: 4c 6f 82                     jmp     ResetAndEnterDirectMode ;reset auto-incr and enter direct-mode prompt

84bc: 20 a6 84     ResetTxtVarPtrs jsr     ClearStoppedTraceAndError
84bf: 8d 06 60                     sta     basicProgram            ;clear offset and line number
84c2: 8d 07 60                     sta     basicProgram+1
84c5: 8d 08 60                     sta     basicProgram+2
84c8: a2 06                        ldx     #$06
84ca: 86 05                        stx     zpTXTTAB                ;set TXTTAB to $6006
84cc: e8                           inx
84cd: 86 07                        stx     zpTXTEND                ;set TXTEND one byte past that ($6007)
84cf: e8                           inx
84d0: 86 1d                        stx     zpVARTAB                ;set VARTAB one past that ($6008)
84d2: e8                           inx
84d3: 86 1f                        stx     zpVAREND                ;and VAREND one past VARTAB ($6009)
84d5: e8                           inx
84d6: 86 21                        stx     zpGpStrIdx              ;;set $21,$22 to byte after that (NEVER USED!)
84d8: a9 60                        lda     #$60
84da: 85 06                        sta     zpTXTTAB+1              ;set high bytes (#$60) for all of the above
84dc: 85 08                        sta     zpTXTEND+1
84de: 85 1e                        sta     zpVARTAB+1
84e0: 85 20                        sta     zpVAREND+1
84e2: 85 22                        sta     zp22
84e4: 60                           rts

                   ; Handles a numbered line. If the line consists only of a number, then delete
                   ; the indicated line; otherwise, tokenize the line and insert it where it
                   ; belongs.
                   ProcessNumberedLine
84e5: 20 c0 88                     jsr     ClearStoppedFlag        ;we're about to mess with line ordering; disallow CONT
84e8: 20 2d 83                     jsr     StrikeRunLineNum
84eb: 20 fd 90                     jsr     ParseDecimalWordFromTxt ;get the typed line number
84ee: a9 ff                        lda     #$ff
84f0: c5 28                        cmp     zpWAccum                ;line num = 65535?
84f2: d0 06                        bne     @proceed                ;no
84f4: c5 29                        cmp     zpWAccum+1              ;maybe... check high byte too
84f6: d0 02                        bne     @proceed                ;less than 65535
84f8: f0 9a                        beq     ErrorIllegalValue       ; = 65535, not allowed

84fa: a5 28        @proceed        lda     zpWAccum
84fc: 85 a0                        sta     zpNxtAutoLNum           ;record line number for AUTO
84fe: a5 29                        lda     zpWAccum+1
8500: 85 a1                        sta     zpNxtAutoLNum+1
8502: 20 16 83                     jsr     IncrementAutoLNum
8505: 20 b2 90                     jsr     GetTxtChar              ;get a char
8508: c9 20                        cmp     #$20                    ;is it a space?
850a: d0 03                        bne     @notSp                  ;no -> stick with this char
850c: 20 98 90                     jsr     TxtPtrIncrAndGetChar    ;yes: get the next char
850f: 29 ff        @notSp          and     #$ff                    ;just a line number, no content?
8511: f0 61                        beq     TxtDeleteLineWAccum     ;yes -> just delete the line if it exists
8513: a5 28                        lda     zpWAccum                ;no: put line number in zpFindLNum
8515: 85 44                        sta     zpFindLNum
8517: a5 29                        lda     zpWAccum+1
8519: 85 45                        sta     zpFindLNum+1
851b: 20 5b 92                     jsr     Tokenize                ;and tokenize the contents via zpTokenizePtr into tokenizeBuffer
                   ; Record tokenized length in zpWAccum
851e: 38                           sec
851f: a5 4d                        lda     zpTokenizePtr
8521: e9 00                        sbc     #<tokenizeBuffer        ;subtracting #$00... silly!
8523: 85 28                        sta     zpWAccum
8525: a5 4e                        lda     zpTokenizePtr+1
8527: e9 03                        sbc     #>tokenizeBuffer
8529: 85 29                        sta     zpWAccum+1
                   ;  
852b: a9 02                        lda     #$02
852d: 18                           clc
852e: 65 28                        adc     zpWAccum
8530: 85 5a                        sta     zpNewLineOffset
8532: a5 44                        lda     zpFindLNum
8534: 85 0f                        sta     zpInsLineNum
8536: a5 45                        lda     zpFindLNum+1
8538: 85 10                        sta     zpInsLineNum+1
853a: 20 7c 85                     jsr     TxtDeleteLine           ;delete any existing line w same #
853d: 4c be 85                     jmp     TxtInsertLine           ;then insert ours

                   ; Find the position for the line number zpFindLNum within the BASIC program,
                   ; leaving zpTxtLink pointing at the closest less-than-or-equal numbered line.
                   ; Z flag is set iff an exact line-number match was found.
                   vZpInsDelNxtLnk .var    $0b    {addr/2}         ;link to next line after the one being inserted/deleted

8540: a5 05        FindLineNum     lda     zpTXTTAB                ;Point zpTxtLink at program start
8542: 85 5b                        sta     zpTxtLink
8544: a5 06                        lda     zpTXTTAB+1
8546: 85 5c                        sta     zpTxtLink+1
8548: a0 00        FLN_nextLine    ldy     #$00
854a: b1 5b                        lda     (zpTxtLink),y           ;read offset to next ln. != 0?
854c: d0 03                        bne     @notDone                ;not zero -> continue to process
854e: a9 ff                        lda     #$ff                    ;zero: out of lines. Indicate not exact, and return.
8550: 60           rts_8550        rts

8551: a0 02        @notDone        ldy     #$02
8553: a5 45                        lda     zpFindLNum+1
8555: d1 5b                        cmp     (zpTxtLink),y           ;compare line num high bytes
8557: d0 07                        bne     @hiDiff                 ;high bytes differ -> check how 
8559: 88                           dey                             ;high bytes same, check low
855a: a5 44                        lda     zpFindLNum
855c: d1 5b                        cmp     (zpTxtLink),y
855e: f0 f0                        beq     rts_8550                ;line numbers equal -> return
8560: 90 ee        @hiDiff         bcc     rts_8550                ;sought line # < cur line #? -> also return
                   ; Fast-forward to next BASIC line
8562: a0 00                        ldy     #$00
8564: 18                           clc
8565: b1 5b                        lda     (zpTxtLink),y           ;get offset to next line
8567: 65 5b                        adc     zpTxtLink               ;add to pointer for current line
8569: 85 5b                        sta     zpTxtLink               ;save as new pointer
856b: a5 5c                        lda     zpTxtLink+1             ;(handle high bytes too)
856d: 69 00                        adc     #$00
856f: 85 5c                        sta     zpTxtLink+1
8571: 4c 48 85                     jmp     FLN_nextLine            ;-> loop back around and check the new line pointer

                   ; Delete designated line of BASIC code
                   TxtDeleteLineWAccum
8574: a5 28                        lda     zpWAccum
8576: 85 44                        sta     zpFindLNum
8578: a5 29                        lda     zpWAccum+1
857a: 85 45                        sta     zpFindLNum+1
857c: 20 40 85     TxtDeleteLine   jsr     FindLineNum             ;found line number?
857f: d0 cf                        bne     rts_8550                ;no -> nothing to do
                   TxtDeleteFoundLine
8581: a0 00                        ldy     #$00
8583: b1 5b                        lda     (zpTxtLink),y           ;get offset to following line
8585: 85 3c                        sta     zpTmp3C                 ;record length of line to delete
8587: 18                           clc
8588: 65 5b                        adc     zpTxtLink               ;add to line link to get start of next line
858a: 85 0b                        sta     vZpInsDelNxtLnk
858c: a5 5c                        lda     zpTxtLink+1
858e: 69 00                        adc     #$00
8590: 85 0c                        sta     vZpInsDelNxtLnk+1
                   ; Starting from the next line, copy back over the deleted line, including
                   ; offsets, moving all forward lines back by the amount of space this line used
                   ; to occupy.
                   ; Not a terribly fast copy: checks source pointer against program end on _every_
                   ; byte written.
8592: a0 00        CopyLinesBack   ldy     #$00
8594: b1 0b                        lda     (vZpInsDelNxtLnk),y
8596: 91 5b                        sta     (zpTxtLink),y
8598: e6 0b                        inc     vZpInsDelNxtLnk         ;increment copy source
859a: d0 02                        bne     @noHi
859c: e6 0c                        inc     vZpInsDelNxtLnk+1
859e: e6 5b        @noHi           inc     zpTxtLink               ;increment copy dest
85a0: d0 02                        bne     @noHi2
85a2: e6 5c                        inc     zpTxtLink+1
85a4: a5 5c        @noHi2          lda     zpTxtLink+1
85a6: c5 08                        cmp     zpTXTEND+1              ;have we reached the program end?
85a8: d0 e8                        bne     CopyLinesBack           ;no -> copy next byte
85aa: a5 5b                        lda     zpTxtLink               ;maybe: check low byte, too
85ac: c5 07                        cmp     zpTXTEND
85ae: d0 e2                        bne     CopyLinesBack           ;not the end-> copy next byte
85b0: 38                           sec                             ;source ptr reached end; move end back to dest ptr
85b1: a5 07                        lda     zpTXTEND
85b3: e5 3c                        sbc     zpTmp3C                 ;subtract length of deleted line from prog end
85b5: 85 07                        sta     zpTXTEND
85b7: a5 08                        lda     zpTXTEND+1
85b9: e9 00                        sbc     #$00
85bb: 85 08                        sta     zpTXTEND+1
85bd: 60                           rts

                   ; Make room for a newly-tokenized line to be inserted into the program... then
                   ; insert it!
85be: a5 07        TxtInsertLine   lda     zpTXTEND                ;get the current program end (low byte)
85c0: 18                           clc
85c1: 65 5a                        adc     zpNewLineOffset         ;add the length of the new line (including header)
85c3: 85 28                        sta     zpWAccum                ;save in WAccum
85c5: a5 08                        lda     zpTXTEND+1              ;add any carry to high byte
85c7: 69 00                        adc     #$00
85c9: 85 29                        sta     zpWAccum+1              ;save that too
85cb: 20 b6 8b                     jsr     CheckFreeSpaceOrError   ;ensure we have space for it
85ce: 20 40 85                     jsr     FindLineNum             ;find where this line goes
85d1: a5 07                        lda     zpTXTEND                ;get current program end (low byte)
85d3: 85 0b                        sta     vZpInsDelNxtLnk         ;save it
85d5: 18                           clc
85d6: 65 5a                        adc     zpNewLineOffset         ;add length of the new line to it
85d8: 85 19                        sta     zpMisc                  ;and save in zpMisc
85da: a5 08                        lda     zpTXTEND+1              ;get program end (high byte)
85dc: 85 0c                        sta     vZpInsDelNxtLnk+1       ;save it
85de: 69 00                        adc     #$00                    ;add the carry from adding new line's len
85e0: 85 1a                        sta     zpMisc+1                ;save in zpMisc high byte
85e2: a0 00        @backCpyLp      ldy     #$00                    ;starting with the byte past the end of final line
85e4: b1 0b                        lda     (vZpInsDelNxtLnk),y     ; and its terminator, copy that byte from the old location,
85e6: 91 19                        sta     (zpMisc),y              ; new-line-length bytes forward to new loc
85e8: 20 4a 86                     jsr     TIL_DecrOldLocPtr       ;--oldPtr
85eb: 20 56 86                     jsr     TIL_DecrNewLocPtr       ;--newPtr
85ee: a5 5b                        lda     zpTxtLink
85f0: c5 0b                        cmp     vZpInsDelNxtLnk         ;have we reached the spot we'll insert the line at?
85f2: d0 ee                        bne     @backCpyLp              ;no -> loop, copy, decrement, until we reach it
85f4: a5 5c                        lda     zpTxtLink+1             ;check the high byte too
85f6: c5 0c                        cmp     vZpInsDelNxtLnk+1       ;high bytes match?
85f8: d0 e8                        bne     @backCpyLp              ;no -> not quite there yet, keep copying
85fa: b1 0b                        lda     (vZpInsDelNxtLnk),y     ;yes, made it. Copy this last byte forward
85fc: 91 19                        sta     (zpMisc),y
85fe: a0 00                        ldy     #$00
8600: a5 5a                        lda     zpNewLineOffset
8602: 91 5b                        sta     (zpTxtLink),y           ;now copy the new line's length as the offset to next line
8604: c8                           iny
8605: a5 0f                        lda     zpInsLineNum
8607: 91 5b                        sta     (zpTxtLink),y           ;and copy its line number
8609: c8                           iny
860a: a5 10                        lda     zpInsLineNum+1
860c: 91 5b                        sta     (zpTxtLink),y           ;(high byte, too)
860e: c8                           iny
860f: 18                           clc
8610: a9 03                        lda     #$03
8612: 65 5b                        adc     zpTxtLink
8614: 85 5b                        sta     zpTxtLink               ;advance write ptr by 3 (skipping next-line offset, and line #)
8616: a9 00                        lda     #$00
8618: 65 5c                        adc     zpTxtLink+1
861a: 85 5c                        sta     zpTxtLink+1             ;and handle any carry for the ptr's high byte
861c: a5 5a                        lda     zpNewLineOffset
861e: 38                           sec
861f: e9 03                        sbc     #$03                    ;adjust the offset value from here to next line, by same amount
8621: aa                           tax                             ;and store in X register
8622: a0 00                        ldy     #$00
8624: a9 00                        lda     #<tokenizeBuffer
8626: 85 4d                        sta     zpTokenizePtr           ;point zpTokenizePtr at the tokenizeBuffer
8628: a9 03                        lda     #>tokenizeBuffer
862a: 85 4e                        sta     zpTokenizePtr+1
862c: b1 4d        @lineCpyLp      lda     (zpTokenizePtr),y       ;copy from tokenizeBuffer (new line)
862e: 91 5b                        sta     (zpTxtLink),y           ; to new line location
8630: c8                           iny                             ;incr copy index
8631: ca                           dex                             ;decr remaining chars
8632: d0 f8                        bne     @lineCpyLp              ;chars left? -> keep looping/copying
8634: 18                           clc
8635: a5 07                        lda     zpTXTEND
8637: 65 5a                        adc     zpNewLineOffset         ;add the new line length to program end,
8639: 85 07                        sta     zpTXTEND                ; and save new value
863b: a9 00                        lda     #$00
863d: 65 08                        adc     zpTXTEND+1
863f: 85 08                        sta     zpTXTEND+1              ;handle the carry, too
8641: a0 00                        ldy     #$00
8643: 98                           tya
8644: 91 07                        sta     (zpTXTEND),y            ;write final null byte after program end. And another after that.
8646: c8                           iny
8647: 91 07                        sta     (zpTXTEND),y
8649: 60                           rts                             ;-> done

                   TIL_DecrOldLocPtr
864a: a5 0b                        lda     vZpInsDelNxtLnk         ;get low byte of old-loc ptr
864c: f0 03                        beq     @oldLowZero             ;is it zero? -> decrement both high and low bytes
864e: c6 0b                        dec     vZpInsDelNxtLnk         ;no, decrement it
8650: 60                           rts                             ;-> and return

8651: c6 0b        @oldLowZero     dec     vZpInsDelNxtLnk         ;decr low
8653: c6 0c                        dec     vZpInsDelNxtLnk+1       ;decr high
8655: 60                           rts                             ;-> return

                   TIL_DecrNewLocPtr
8656: a5 19                        lda     zpMisc                  ;get low byte of new-loc ptr
8658: f0 03                        beq     @newLowZero             ;zero? -> decrement bth high and low bytes
865a: c6 19                        dec     zpMisc                  ;else just decr low byte
865c: 60                           rts                             ;-> and return

865d: c6 19        @newLowZero     dec     zpMisc                  ;decr low
865f: c6 1a                        dec     zpMisc+1                ;decr high
8661: 60                           rts                             ;-> return

8662: a9 04        InitVars        lda     #$04
8664: 85 b9                        sta     zpTempo                 ;initialize tempo for each musical voice
8666: 85 ba                        sta     zpTempo+1
8668: 85 bb                        sta     zpTempo+2
866a: a9 08                        lda     #$08
866c: 85 c3                        sta     zpNoteLen               ;set a default note length for each musical voice
866e: 85 c4                        sta     zpNoteLen+1
8670: 85 c5                        sta     zpNoteLen+2
8672: a9 03                        lda     #$03
8674: 85 bf                        sta     zpOctave                ;set a default octave for each musical voice
8676: 85 c0                        sta     zpOctave+1              ; (dotted half-note, according to manual)
8678: 85 c1                        sta     zpOctave+2
867a: a9 0f                        lda     #$0f
867c: 85 bc                        sta     zpVolume                ;set default volume (max) for each musical voice
867e: 85 bd                        sta     zpVolume+1
8680: 85 be                        sta     zpVolume+2
8682: a9 30                        lda     #$30
8684: 85 cf                        sta     zpEnvelope              ;turn off envelope for each musical voice
8686: 85 d0                        sta     zpEnvelope+1
8688: 85 d1                        sta     zpEnvelope+2
868a: a9 02                        lda     #$02                    ;set duty cycle (50%) for each musical voice
868c: 85 d2                        sta     zpDuty
868e: 85 d3                        sta     zpDuty+1
8690: 85 d4                        sta     zpDuty+2
8692: 20 a6 84                     jsr     ClearStoppedTraceAndError ;clear stop/trace/error flags
8695: 85 4c                        sta     zpInsertMode            ;clear auto-insert mode
8697: 85 57                        sta     zpRandom+1
8699: a9 ff                        lda     #<memoryTop
869b: 85 03                        sta     zpFRETOP                ;reset FRETOP
869d: a9 03                        lda     #$03
869f: 85 56                        sta     zpRandom                ;reset RAND PRNG to start at #$0003
86a1: 85 e3                        sta     zpKeyClickFlg           ;turn keyclicks on
86a3: a9 6f                        lda     #>memoryTop
86a5: 85 04                        sta     zpFRETOP+1
86a7: 20 e8 ae                     jsr     ClearOam
86aa: 20 94 b1                     jsr     ClearSpriteData
86ad: 4c ab b3                     jmp     InitPpuApu

                   • Clear variables

86b0: 20 a6 97     CmdFn_RUN       jsr     Clear                   ;flush away any variables
86b3: 20 c0 88                     jsr     ClearStoppedFlag        ;clear various flags
86b6: 20 ab 84                     jsr     ClearErrorFlags
86b9: 20 94 b1                     jsr     ClearSpriteData
86bc: 20 e8 ae                     jsr     ClearOam
86bf: 20 9c 84                     jsr     IsEndOfCmd              ;do we have an arg?
86c2: d0 12                        bne     gotoCmd_86D6            ;yes -> GOTO that line
86c4: a2 00                        ldx     #$00                    ;reset the stack pointer
86c6: 9a                           txs
86c7: a9 ff                        lda     #$ff
86c9: 48                           pha                             ;indicate limit of NEXT, RETURN stack searches?
86ca: 48                           pha
86cb: a5 05                        lda     zpTXTTAB                ;set TXTPTR to start of program
86cd: 85 09                        sta     zpTXTPTR
86cf: a5 06                        lda     zpTXTTAB+1
86d1: 85 0a                        sta     zpTXTPTR+1
86d3: 4c 71 84                     jmp     ExecuteLine             ;-> and execute first line

86d6: 4c c6 96     gotoCmd_86D6    jmp     CmdFn_GOTO

86d9: 20 bf 90     SkipEqualsOrErr jsr     TxtSkipSpaces
86dc: c9 f6                        cmp     #tok_OP_EQ
86de: d0 03                        bne     errSyn_86E3
86e0: 4c 8c 90                     jmp     TxtPtrIncr

86e3: 4c 91 84     errSyn_86E3     jmp     ErrorSyntax

                   ; The two BASIC commands that accept line number ranges as arguments - LIST, and
                   ; DELETE - use this routine to read their parameters
                   ReadLineNumRange
86e6: 08                           php                             ;push processor flags
86e7: a2 00                        ldx     #$00
86e9: 86 86                        stx     zpLNumStart
86eb: 86 87                        stx     zpLNumStart+1
86ed: ca                           dex
86ee: 86 88                        stx     zpLNumEnd
86f0: 86 89                        stx     zpLNumEnd+1
86f2: 28                           plp                             ;pop processor flags
86f3: f0 62                        beq     rts_8757                ;no args? -> nothing to do, exit
86f5: c9 2c                        cmp     #‘,’
86f7: f0 32                        beq     RdLNRng_secondArg
86f9: c9 2d                        cmp     #‘-’
86fb: f0 2e                        beq     RdLNRng_secondArg       ;comma or dash? -> handle second arg
86fd: c9 2e                        cmp     #‘.’                    ;period?
86ff: d0 0b                        bne     RdLNRng_firstArg        ;no -> handle first arg
8701: a5 11                        lda     zpContRunLineNum        ;period found: use STOPped line
8703: 85 86                        sta     zpLNumStart             ; as the start
8705: 85 88                        sta     zpLNumEnd               ; and end (for now), of the range
8707: a5 12                        lda     zpContRunLineNum+1      ;high byte
8709: 4c 1a 87                     jmp     RdLNRng_period          ;-> set high bytes, and accept possible 2nd arg

                   RdLNRng_firstArg
870c: c9 0b                        cmp     #tok_LNUM_AFTER         ;line num prefix token?
870e: d0 d3                        bne     errSyn_86E3             ;no -> error
8710: 20 98 90                     jsr     TxtPtrIncrAndGetChar    ;read first byte of line number
8713: 85 86                        sta     zpLNumStart             ;use as both start and end of line range
8715: 85 88                        sta     zpLNumEnd
8717: 20 98 90                     jsr     TxtPtrIncrAndGetChar    ;get high byte of line-num first argument
871a: 85 87        RdLNRng_period  sta     zpLNumStart+1           ;and set start+end high bytes too
871c: 85 89                        sta     zpLNumEnd+1
871e: 20 99 84                     jsr     AdvThenIsEndOfCmd       ;get next byte. Is it the end of the command?
8721: f0 34                        beq     rts_8757                ;yes -> done parsing args
8723: c9 2c                        cmp     #‘,’                    ;else, got a comma or dash?
8725: f0 04                        beq     RdLNRng_secondArg       ;yes -> process 2nd arg
8727: c9 2d                        cmp     #‘-’
8729: d0 b8                        bne     errSyn_86E3             ;else -> error
                   RdLNRng_secondArg
872b: 20 99 84                     jsr     AdvThenIsEndOfCmd       ;end of cmd after comma-or-dash?
872e: f0 21                        beq     RdLNRng_progEndIsEnd    ;yes -> set end of range to end of program
8730: c9 2e                        cmp     #‘.’                    ;else, got a period as 2nd arg?
8732: d0 09                        bne     @lnum                   ;no -> expect line number
8734: a5 11                        lda     zpContRunLineNum        ;got a period. Set CONT line as end of range.
8736: 85 88                        sta     zpLNumEnd
8738: a5 12                        lda     zpContRunLineNum+1
873a: 4c 49 87                     jmp     @hiByt                  ;-> and finish up args processing

873d: c9 0b        @lnum           cmp     #tok_LNUM_AFTER         ;got the line-num token prefix?
873f: d0 a2                        bne     errSyn_86E3             ;no -> error
8741: 20 98 90                     jsr     TxtPtrIncrAndGetChar    ;get line-number 2nd arg
8744: 85 88                        sta     zpLNumEnd               ;and set as end-of-range
8746: 20 98 90                     jsr     TxtPtrIncrAndGetChar
8749: 85 89        @hiByt          sta     zpLNumEnd+1
874b: 20 8c 90                     jsr     TxtPtrIncr
874e: 4c 57 87                     jmp     rts_8757                ;done processing args

                   RdLNRng_progEndIsEnd
8751: a9 ff                        lda     #$ff                    ;set end of line-num range to maximum value (end of program)
8753: 85 88                        sta     zpLNumEnd
8755: 85 89                        sta     zpLNumEnd+1
8757: 60           rts_8757        rts                             ;done processing args

8758: f0 89        CmdFn_DELETE    beq     errSyn_86E3             ;no arg? -> error
875a: a2 ff                        ldx     #$ff
875c: 86 51                        stx     zpListOrDelete          ;set DELETE mode
875e: 20 e6 86                     jsr     ReadLineNumRange
8761: 20 6e 87                     jsr     DoListOrDelete          ;Perform the DELETE
8764: 4c a6 97                     jmp     Clear                   ;-> Clear variables, and end command.

8767: 20 e6 86     CmdFn_LIST      jsr     ReadLineNumRange
876a: a9 00                        lda     #$00
876c: 85 51                        sta     zpListOrDelete          ;set LIST mode
                   ; The two commands that both operate on line-number ranges - LIST, and DELETE -
                   ; both use this routine to perform their actions. The value of the variable
                   ; zpListOrDelete ($51) is used to determine what actual action to perform on
                   ; each line within the range.
876e: 20 30 94     DoListOrDelete  jsr     SaveTXTPTR              ;save the original TXTPTR away
8771: a5 86                        lda     zpLNumStart             ;save "start" line num as line to find
8773: 85 44                        sta     zpFindLNum
8775: a5 87                        lda     zpLNumStart+1
8777: 85 45                        sta     zpFindLNum+1
8779: 20 40 85                     jsr     FindLineNum             ;...and then find it.
877c: a0 00                        ldy     #$00
877e: b1 5b                        lda     (zpTxtLink),y           ;read offset to next line num. Are we at end of prog?
8780: f0 5b                        beq     LoD_RestoreTxtPtrAndReturn ;yes -> restore TXTPTR and exit LIST or DELETE.
8782: a5 5b                        lda     zpTxtLink
8784: 85 09                        sta     zpTXTPTR                ;set TXTPTR start of found line linkage;
8786: a5 5c                        lda     zpTxtLink+1             ; we'll use it to scan through the range
8788: 85 0a                        sta     zpTXTPTR+1
878a: a0 00        LoD_mainLp      ldy     #$00
878c: b1 09                        lda     (zpTXTPTR),y            ;end of program?
878e: f0 4d                        beq     LoD_RestoreTxtPtrAndReturn ;yes -> restore TXTPTR and exit LIST or DELETE.
8790: c8                           iny
8791: b1 09                        lda     (zpTXTPTR),y            ;read current line num
8793: 85 28                        sta     zpWAccum                ;save in WAccum
8795: c8                           iny
8796: b1 09                        lda     (zpTXTPTR),y
8798: 85 29                        sta     zpWAccum+1
879a: a5 88                        lda     zpLNumEnd               ;set WParam to end-of-range line num
879c: 85 2c                        sta     zpWParam
879e: a5 89                        lda     zpLNumEnd+1
87a0: 85 2d                        sta     zpWParam+1
87a2: 20 e2 8f                     jsr     WordCompare             ;compare current line num, with range's end line num
87a5: f0 02                        beq     @notPastEnd             ;cur > end?
87a7: b0 34                        bcs     LoD_RestoreTxtPtrAndReturn ;yes -> restore TXTPTR and exit LIST or DELETE.
87a9: a5 51        @notPastEnd     lda     zpListOrDelete          ;are we doing LIST?
87ab: f0 0e                        beq     ListCurLine             ;yes -> LIST the current line
                   ; Delete one line of the range
87ad: a5 09                        lda     zpTXTPTR                ;no: we'll DELETE it
87af: 85 5b                        sta     zpTxtLink               ;Copy current line linkage to zpTxtLink
87b1: a5 0a                        lda     zpTXTPTR+1
87b3: 85 5c                        sta     zpTxtLink+1
87b5: 20 81 85                     jsr     TxtDeleteFoundLine      ;and delete the line
87b8: 4c 8a 87                     jmp     LoD_mainLp              ;iterate again for next line in range

87bb: 20 ee 87     ListCurLine     jsr     PrintLineNumAndDetok
87be: 20 c4 87                     jsr     PauseOnEsc
87c1: 4c 8a 87                     jmp     LoD_mainLp              ;iterate again for next line in range

                   ; Pause listing (within LIST or FIND) if the Escape key is pressed.
                   ; Honor STOP key if that's pressed.
87c4: 20 d7 87     PauseOnEsc      jsr     ReadKeyMaybeStop        ;read from keyboard
87c7: f0 0d                        beq     @rts                    ;no key pressed? -> return to list next line
87c9: c9 1b                        cmp     #$1b                    ;ESCAPE key?
87cb: d0 09                        bne     @rts                    ;no -> return to list next line
87cd: 20 d7 87     @escPause       jsr     ReadKeyMaybeStop        ;pause LISTing until some key other than ESCAPE is pressed
87d0: f0 fb                        beq     @escPause
87d2: c9 1b                        cmp     #$1b
87d4: f0 f7                        beq     @escPause
87d6: 60           @rts            rts                             ;return to iterating over lines to LIST

                   ReadKeyMaybeStop
87d7: 20 55 88                     jsr     StopIfStopKey
87da: 4c 45 b9                     jmp     ReadKeyInsMacros

                   LoD_RestoreTxtPtrAndReturn
87dd: 4c 39 94                     jmp     RestoreTXTPTR

                   QueueOutpNumStrTmp
87e0: a9 3d                        lda     #zpNumStrTmp            ;point zpOutputStr at zpNumStrTmp, as source
87e2: 18                           clc                             ; for output queuing
87e3: 65 46                        adc     zpTmp46                 ;add index to start position
87e5: 85 52                        sta     zpOutputStr             ;(carry isn't needed, the string is up to 6 chars max)
87e7: a9 00                        lda     #>zpNumStrTmp
87e9: 85 53                        sta     zpOutputStr+1
87eb: 4c 3c 89                     jmp     QueueOutputStr          ;queue the output string

                   ; Print line number and the detokenized contents of the line. Used by
                   ; DirectModeLoop when AUTO is active, and by LIST, and FIND.
                   PrintLineNumAndDetok
87ee: a9 00                        lda     #$00
87f0: 85 62                        sta     zpOutTxtCurPos
87f2: a0 01                        ldy     #$01
87f4: b1 09                        lda     (zpTXTPTR),y            ;put the line number at zpTXTPTR into WAccum
87f6: 85 28                        sta     zpWAccum
87f8: c8                           iny
87f9: b1 09                        lda     (zpTXTPTR),y
87fb: 85 29                        sta     zpWAccum+1
87fd: 20 3e 91                     jsr     PosDecStrFromWord
8800: 20 d6 90                     jsr     FindFirstNonzeroDigit
8803: 20 e0 87                     jsr     QueueOutpNumStrTmp      ;queue up the line number to be printed
8806: a9 20                        lda     #$20
8808: 20 4d 89                     jsr     QueueCharForOutput      ;and add a SPACE character afterward
880b: a9 03                        lda     #$03
880d: 18                           clc
880e: 65 09                        adc     zpTXTPTR                ;set up zpTokenizePtr to detokenize from the line's contents
8810: 85 4d                        sta     zpTokenizePtr
8812: a9 00                        lda     #$00
8814: 65 0a                        adc     zpTXTPTR+1
8816: 85 4e                        sta     zpTokenizePtr+1
8818: 20 42 94                     jsr     DetokenizeLine          ;detokenize from zpTokenizePtr, into lineBuffer
881b: 20 ad 8b                     jsr     PointOutStrAtLineBufStart ;point zpOutputStr at lineBuffer
881e: 20 5a 89                     jsr     PrintString             ;print our queued line number plus detokenized stuff in lineBuffer
8821: a5 4d                        lda     zpTokenizePtr
8823: 85 09                        sta     zpTXTPTR                ;(useless setting of zpTXTPTR; we throw it away a few lines below)
8825: a5 4e                        lda     zpTokenizePtr+1
8827: 85 0a                        sta     zpTXTPTR+1
8829: 20 6f b5                     jsr     DoCRLF                  ;carriage return + new line. (AUTO will have to back us back up.)
882c: a9 01                        lda     #$01
882e: 18                           clc
882f: 65 4d                        adc     zpTokenizePtr           ;zpTokenizePtr now points at null terminator at end of line
8831: 85 09                        sta     zpTXTPTR                ;set zpTXTPTR to next byte after that
8833: a9 00                        lda     #$00                    ; (the "line offset" byte of next line)
8835: 65 4e                        adc     zpTokenizePtr+1
8837: 85 0a                        sta     zpTXTPTR+1
8839: 60                           rts

883a: a9 05        CheckForStopKey lda     #$05
883c: 8d 16 40                     sta     JOY1                    ;write $05 to keyboard: reset row/col scanning
883f: 48                           pha                             ;stall 21 cycles (~12 ns)
8840: 68                           pla
8841: 48                           pha
8842: 68                           pla
8843: 48                           pha
8844: 68                           pla
8845: a9 04                        lda     #$04
8847: 8d 16 40                     sta     JOY1                    ;write $04: prep read for column 0 of a new row
884a: a9 06                        lda     #$06
884c: 8d 16 40                     sta     JOY1                    ;welect second col, first row
884f: ad 17 40                     lda     JOY2                    ;read key column state
8852: 29 10                        and     #$10                    ;mask out all but STOP key
8854: 60                           rts

8855: 20 3a 88     StopIfStopKey   jsr     CheckForStopKey
8858: f0 03                        beq     Stop
885a: 60                           rts

885b: 68           CmdFn_STOP      pla                             ;pull return address off of stack -
885c: 68                           pla                             ; we're not returning!
885d: a9 00        Stop            lda     #NC_NoCmd
885f: 85 63                        sta     zpNMICMD                ;reset NMI sub-command (stopping music, etc)
8861: 20 45 b9                     jsr     ReadKeyInsMacros
8864: d0 f7                        bne     Stop                    ;wait until no key is actively being pressed
8866: 20 14 be                     jsr     SetPpuCtrlAndEnableNmi
8869: 20 83 88                     jsr     Stop_saveContext
886c: a9 ff                        lda     #$ff                    ;set stopped flag
886e: 85 24                        sta     zpStoppedFlag
8870: c5 0b                        cmp     zpRunLineNum            ;are we executing in direct mode?
8872: d0 07                        bne     @skipClear              ;no, low byte doesn't match -> skip flag clear
8874: c5 0c                        cmp     zpRunLineNum+1
8876: d0 03                        bne     @skipClear              ;no, high byte doesn't match -> skip flag clear
8878: 20 c0 88                     jsr     ClearStoppedFlag        ;nevermind, re-clear the stopped flag
887b: 4c d8 b2     @skipClear      jmp     Stop_PrintBreakBeepExit

                   ErrorCannotContinue
887e: a9 0e                        lda     #err_CC
8880: 4c 37 b2                     jmp     PrintErrorAndStop

                   Stop_saveContext
8883: a5 0b                        lda     zpRunLineNum            ;save current running context for later restoration via CONT cmd
8885: 85 11                        sta     zpContRunLineNum
8887: a5 0c                        lda     zpRunLineNum+1
8889: 85 12                        sta     zpContRunLineNum+1
888b: a5 0d                        lda     zpRunTxtNxtLnk
888d: 85 13                        sta     zpContRunTxtNxtL
888f: a5 0e                        lda     zpRunTxtNxtLnk+1
8891: 85 14                        sta     zpContRunTxtNxtL+1
8893: a5 58                        lda     zpTxtThisCmd
8895: 85 15                        sta     zpContTxtThisCmd
8897: a5 59                        lda     zpTxtThisCmd+1
8899: 85 16                        sta     zpContTxtThisCmd+1
889b: 60                           rts

889c: a5 24        CmdFn_CONT      lda     zpStoppedFlag           ;have context saved from STOP?
889e: f0 de                        beq     ErrorCannotContinue     ;no -> error
88a0: a5 15                        lda     zpContTxtThisCmd        ;yes -> restore running context from previous STOP
88a2: 85 09                        sta     zpTXTPTR
88a4: a5 16                        lda     zpContTxtThisCmd+1
88a6: 85 0a                        sta     zpTXTPTR+1
88a8: 20 f1 9e     @skipLp         jsr     SkipToken
88ab: 20 9c 84                     jsr     IsEndOfCmd
88ae: d0 f8                        bne     @skipLp                 ;skip tokens until we're at end of command (colon or EOL)
88b0: a5 13                        lda     zpContRunTxtNxtL
88b2: 85 0d                        sta     zpRunTxtNxtLnk
88b4: a5 14                        lda     zpContRunTxtNxtL+1
88b6: 85 0e                        sta     zpRunTxtNxtLnk+1
88b8: a5 11                        lda     zpContRunLineNum
88ba: 85 0b                        sta     zpRunLineNum
88bc: a5 12                        lda     zpContRunLineNum+1
88be: 85 0c                        sta     zpRunLineNum+1
                   ClearStoppedFlag
88c0: a9 00                        lda     #$00
88c2: 85 24                        sta     zpStoppedFlag
88c4: 60                           rts

88c5: f0 72        CmdFn_PRINT     beq     doCRLF_8939             ;no args? -> CRLF and return
88c7: 20 83 a2                     jsr     FrmEval                 ;evaluate argument
88ca: a5 27                        lda     zpArgTyp
88cc: c9 03                        cmp     #$03                    ;is it string?
88ce: f0 16                        beq     @strArg                 ;yes -> handle string arg
88d0: ad 00 04                     lda     exprParseStk
88d3: 85 28                        sta     zpWAccum                ;else, copy integer value to WAccum
88d5: ad 01 04                     lda     exprParseStk+1
88d8: 85 29                        sta     zpWAccum+1
88da: 20 98 91                     jsr     DecStrFromWord          ;convert to decimal string
88dd: 20 e0 87                     jsr     QueueOutpNumStrTmp      ;queue it for output
                   PRINT_outputThenNextArg
88e0: 20 5d 89                     jsr     PrintOutBuf             ;tell NMI handler to output the buffer
88e3: 4c 19 89                     jmp     PRINT_afterArg          ;-> and handle any more args

88e6: ad 00 04     @strArg         lda     exprParseStk            ;get string length
88e9: f0 2e                        beq     PRINT_afterArg          ;empty string? -> already done with string then!
88eb: a0 00                        ldy     #$00
88ed: ad 01 04                     lda     exprParseStk+1          ;point zpMisc at string
88f0: 85 19                        sta     zpMisc
88f2: ad 02 04                     lda     exprParseStk+2
88f5: 85 1a                        sta     zpMisc+1
88f7: b1 19        @strCpyLp       lda     (zpMisc),y              ;get a char from string
88f9: c9 20                        cmp     #$20                    ;is it a printable character?
88fb: b0 02                        bcs     @prAble
88fd: a9 00                        lda     #$00                    ;if not, replace with null char
88ff: 99 00 05     @prAble         sta     lineBuffer,y            ;copy the char into lineBuffer
8902: c8                           iny                             ;move to next character
8903: ce 00 04                     dec     exprParseStk            ;decrement string length
8906: d0 ef                        bne     @strCpyLp               ;strlen != 0? -> loop around to copy next character
8908: a9 00                        lda     #$00
890a: 99 00 05                     sta     lineBuffer,y            ;null-terminate lineBuffer
890d: 20 ad 8b                     jsr     PointOutStrAtLineBufStart
8910: 20 3c 89                     jsr     QueueOutputStr          ;queue contents of lineBuffer to be printed
8913: 4c e0 88                     jmp     PRINT_outputThenNextArg ;-> print queued output, handle next arg

                   ; dead/unreachable code
8916: 20 8c 90                     jsr     TxtPtrIncr
                   ; end unreachable
8919: 20 9c 84     PRINT_afterArg  jsr     IsEndOfCmd              ;no args remain?
891c: f0 1b                        beq     doCRLF_8939             ;yes -> CRLF then return
891e: c9 3b                        cmp     #‘;’                    ;else, got semicolon?
8920: f0 09                        beq     @semicolon              ;yes ->
8922: c9 2c                        cmp     #‘,’                    ;else, got comma?
8924: d0 0d                        bne     @notComma               ;no -> just print as another arg (if any)
8926: a9 09                        lda     #$09                    ;(TAB)
8928: 20 75 b5                     jsr     PrintOneChar            ;print TAB
892b: 20 99 84     @semicolon      jsr     AdvThenIsEndOfCmd       ;move past semicolon
892e: f0 08                        beq     rts_8938                ;out of args? just end (no CRLF on semicolon or comma)
8930: 4c 19 89                     jmp     PRINT_afterArg          ;else -> handle whatever else there is

8933: 20 9c 84     @notComma       jsr     IsEndOfCmd
8936: d0 8d                        bne     CmdFn_PRINT             ;(always) -> handle more args
8938: 60           rts_8938        rts

8939: 4c 6f b5     doCRLF_8939     jmp     DoCRLF

                   ; Copies the null-terminated string pointed at by zpOutputStr ($52)
                   ; to genPurposeBuf ($700), queueing for output
893c: a0 00        QueueOutputStr  ldy     #$00
893e: a6 62                        ldx     zpOutTxtCurPos
8940: b1 52        @lp             lda     (zpOutputStr),y
8942: f0 f4                        beq     rts_8938                ;-> rts
8944: 20 4d 89                     jsr     QueueCharForOutput
8947: c8                           iny
8948: 4c 40 89                     jmp     @lp

                   QueueNullForOutput
894b: a9 00                        lda     #$00
                   QueueCharForOutput
894d: a6 62                        ldx     zpOutTxtCurPos
894f: 9d 00 07                     sta     genPurposeBuf,x
8952: e6 62                        inc     zpOutTxtCurPos
8954: f0 01                        beq     @skip
8956: 60                           rts

8957: c6 62        @skip           dec     zpOutTxtCurPos          ;zpOutTxtCurPos <- #$FF
8959: 60                           rts

                   ; Copy a string to the output buffer, set zpNMICMD to NC_FlushOutp, and wait for
                   ; NMI handler to finish copying to the screen.
895a: 20 3c 89     PrintString     jsr     QueueOutputStr
                   ; NUL-terminate the output buffer, and wait for NMI to print it.
                   ; On exit, Z is guaranteed to be set.
895d: 20 4b 89     PrintOutBuf     jsr     QueueNullForOutput
8960: a5 62                        lda     zpOutTxtCurPos
8962: f0 0c                        beq     @rts                    ;-> rts
8964: a9 01                        lda     #NC_FlushOutp           ; -> NMICMD_FlushQueuedOutput
8966: a2 00                        ldx     #$00
8968: 86 64                        stx     zpOutTxtStartPos        ;reset output to go to start of buffer (after we've printed it) (fall through!)
                   ; Set NMICMD and wait for NMI handler to finish processing it.
                   WaitForNmiSubcommand
896a: 85 63                        sta     zpNMICMD
896c: a5 63        @lp             lda     zpNMICMD
896e: d0 fc                        bne     @lp
8970: 60           @rts            rts

                   NMI_DefaultHandler
8971: 48                           pha
8972: a9 00                        lda     #<oamBuffer
8974: 8d 03 20                     sta     OAMADDR
8977: a9 02                        lda     #>oamBuffer             ;Trigger DMA transfer of CPU page $02 to VRAM page $00
8979: 8d 14 40                     sta     OAMDMA
897c: a5 ab                        lda     zpNmiAnimNxtFr          ;are we running animations this frame?
897e: d0 0e                        bne     animAndTog_898E         ;yes -> go animate
8980: a5 ac                        lda     zpNmiAnimate            ;no: Are animations active?
8982: f0 04                        beq     @ckCmd                  ;no -> just handle any active NMI subcommand
8984: a9 ff                        lda     #$ff                    ;yes: we'll handle animations NEXT frame.
8986: 85 ab                        sta     zpNmiAnimNxtFr          ;set the flag, then handle NMI subcmds for this frame
8988: a5 63        @ckCmd          lda     zpNMICMD                ;active command?
898a: d0 05                        bne     NmiHandleCmd            ;yes -> go handle it
898c: 68                           pla
898d: 40                           rti

898e: 4c a3 c8     animAndTog_898E jmp     AnimateAndToggleNextFrame

                   ; Set up sub-command address, then jump to it
8991: aa           NmiHandleCmd    tax
8992: ca                           dex                             ;NC constants are one greater than cmd index
8993: 8a                           txa
8994: 0a                           asl     A                       ;mul by 2 for jtbl index
8995: aa                           tax
8996: bd a3 89                     lda     jtbl_NMICMD,x
8999: 85 1b                        sta     zpMisc+2                ;store handler pointer
899b: bd a4 89                     lda     jtbl_NMICMD+1,x
899e: 85 1c                        sta     zpMisc+3
89a0: 6c 1b 00                     jmp     (zpMisc+2)              ;and jump to it

89a3: 0c 8a        jtbl_NMICMD     .dd2    NMICMD_FlushQueuedOutput
89a5: 87 8a                        .dd2    NMICMD_BufferCurLine
89a7: c3 8a                        .dd2    NMICMD_CopyCharsLeft
89a9: fa 89                        .dd2    NMICMD_ClearToEndOfRow
89ab: d2 89                        .dd2    NMICMD_GetCharUnderCursor
89ad: f2 8a                        .dd2    NMICMD_SetBgPalette
89af: 30 8b                        .dd2    NMICMD_SetSprPalette
89b1: e4 89                        .dd2    NMICMD_ShowCursor
89b3: f2 89                        .dd2    NMICMD_RestoreCharAtCursor
89b5: 91 a0                        .dd2    NMICMD_PlayMusic
89b7: c4 89                        .dd2    NMICMD_PpuMaskUpdate
89b9: c1 89                        .dd2    NMICMD_PpuMaskClear
89bb: 3e 8b                        .dd2    NMICMD_InsertChar
89bd: 74 8b                        .dd2    NMICMD_CopyRowDown_ph0
89bf: 8c 8b                        .dd2    NMICMD_CopyRowDown_ph1

                   NMICMD_PpuMaskClear
89c1: a9 00                        lda     #$00
89c3: ae                           ldx ▼   $33a5
                   NMICMD_PpuMaskUpdate
89c4: a5 33                        lda     zpPpuMaskVal
89c6: 8d 01 20                     sta     PPUMASK
                   NMICMD_DeactivateSubcmd
89c9: a9 00                        lda     #NC_NoCmd
89cb: 85 63                        sta     zpNMICMD
89cd: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi
89d0: 68                           pla
89d1: 40                           rti

                   NMICMD_GetCharUnderCursor
89d2: 20 d8 89                     jsr     NmiGetCharUnderCursor
89d5: 4c c9 89                     jmp     NMICMD_DeactivateSubcmd

                   NmiGetCharUnderCursor
89d8: 20 2e b5                     jsr     PpuGoToCursor
89db: ad 07 20                     lda     PPUDATA
89de: ad 07 20                     lda     PPUDATA
89e1: 85 73                        sta     zpCharUnderCurs
89e3: 60                           rts

                   NMICMD_ShowCursor
89e4: 20 d8 89                     jsr     NmiGetCharUnderCursor
89e7: 20 2e b5                     jsr     PpuGoToCursor
89ea: a5 61                        lda     zpCursorChar
89ec: 8d 07 20     @setCurs        sta     PPUDATA
89ef: 4c c9 89                     jmp     NMICMD_DeactivateSubcmd

                   NMICMD_RestoreCharAtCursor
89f2: 20 2e b5                     jsr     PpuGoToCursor
89f5: a5 73                        lda     zpCharUnderCurs
89f7: 4c ec 89                     jmp     @setCurs

                   ; Erases from cursor to end of current row
                   NMICMD_ClearToEndOfRow
89fa: 20 2e b5                     jsr     PpuGoToCursor
89fd: a6 48                        ldx     zpCH                    ;set X reg to current cursor x pos
89ff: a9 20                        lda     #key_Space
8a01: 8d 07 20     @blankLp        sta     PPUDATA                 ;write a space to location in VRAM
8a04: e8                           inx                             ;next char position
8a05: e0 1c                        cpx     #COLS                   ;did we reach the end of the line?
8a07: d0 f8                        bne     @blankLp                ;no -> loop until we do
8a09: 4c ba 8a                     jmp     NmiEndCmdCleanupAndExit ;-> finish up

                   NMICMD_FlushQueuedOutput
8a0c: 20 f4 b4                     jsr     CalcCursorTilemapPos
8a0f: a6 64                        ldx     zpOutTxtStartPos
8a11: 20 2e b5                     jsr     PpuGoToCursor
8a14: bd 00 07     @outLp          lda     genPurposeBuf,x         ;grab a char from out buf
8a17: f0 66                        beq     QueueOutpDeactivate     ;NUL -> buf is done
8a19: 8d 07 20                     sta     PPUDATA                 ;not NUL; copy char to screen 
8a1c: e6 48                        inc     zpCH                    ;advance the cursor
8a1e: a9 1c                        lda     #COLS
8a20: c5 48                        cmp     zpCH                    ;have we reached the row end?
8a22: f0 0f                        beq     @wrap                   ;yes -> handle wraparound to next row
8a24: a9 0e                        lda     #$0e
8a26: c5 48                        cmp     zpCH                    ;are we at 15th column?
8a28: f0 03                        beq     @susp                   ;yes -> print rest next NMI
8a2a: e8                           inx
8a2b: d0 e7                        bne     @outLp                  ;(always) loop back to output next char
                   ; Done printing for this frame.
                   ; Save our progress, and continue next NMI
8a2d: e8           @susp           inx
8a2e: 86 64                        stx     zpOutTxtStartPos
8a30: 4c be 8a                     jmp     NmiSameCmdCleanupAndExit

                   ; We've reached the end of the screen line: wrap around to next column
8a33: a9 00        @wrap           lda     #$00
8a35: 85 48                        sta     zpCH
8a37: 8a                           txa
8a38: 48                           pha
8a39: a6 49                        ldx     zpCV
8a3b: a9 ff                        lda     #$ff
8a3d: 20 45 b6                     jsr     MarkWrappedRow
8a40: 68                           pla
8a41: aa                           tax
8a42: e6 49                        inc     zpCV
8a44: a9 18                        lda     #ROWS
8a46: c5 49                        cmp     zpCV
8a48: f0 06                        beq     @scroll
8a4a: e8                           inx
8a4b: 86 64                        stx     zpOutTxtStartPos
8a4d: 4c be 8a                     jmp     NmiSameCmdCleanupAndExit

                   ; Output cursor has gone to a row below the displayed screen, so
                   ; scroll the screen.
8a50: e8           @scroll         inx
8a51: 86 64                        stx     zpOutTxtStartPos
8a53: 20 48 b7                     jsr     CursorHome
8a56: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi
8a59: a5 32                        lda     zpPpuCtrlVal
8a5b: 8d 00 20                     sta     PPUCTRL
8a5e: 20 f4 b4                     jsr     CalcCursorTilemapPos
8a61: 20 2b b4                     jsr     WaitForVBlank
8a64: 20 2e b5                     jsr     PpuGoToCursor
8a67: a2 1c                        ldx     #COLS
8a69: a9 20                        lda     #$20                    ;SPACE character
8a6b: 8d 07 20     @lp             sta     PPUDATA
8a6e: ca                           dex
8a6f: d0 fa                        bne     @lp
8a71: 86 63                        stx     zpNMICMD                ;deactivate command (#NC_NoCmd)
8a73: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi
8a76: 20 d4 b5                     jsr     ScrollScreenOneRowNoErase
8a79: a9 01                        lda     #NC_FlushOutp
8a7b: 85 63                        sta     zpNMICMD
8a7d: 68                           pla                             ;pull accum (pushed by NMI_DefaultHandler)
8a7e: 40                           rti

                   ; Indicate end of screen outputting, reset output buffer, cleanup, and exit.
                   QueueOutpDeactivate
8a7f: 85 63                        sta     zpNMICMD
8a81: 85 62                        sta     zpOutTxtCurPos
8a83: 85 64                        sta     zpOutTxtStartPos
8a85: f0 37                        beq     NmiSameCmdCleanupAndExit

                   ; Copies the current row, through to the end of the line, into lineBuffer.
                   ; (Copies multiple rows if the line is wrapped.)
                   ; This command is invoked when handling CR on input, to get a copy of the line
                   ; for input processing.
                   NMICMD_BufferCurLine
8a87: a9 00                        lda     #$00
8a89: 85 48                        sta     zpCH                    ;move to start of current row
8a8b: 20 f4 b4                     jsr     CalcCursorTilemapPos    ;calculate VRAM addr for cursor
8a8e: 20 2e b5                     jsr     PpuGoToCursor           ;tell PPU to start reading from there
8a91: a0 1c                        ldy     #COLS                   ;set Y to # of chars in a row
8a93: ad 07 20                     lda     PPUDATA                 ;fake read from PPU (prime the pump!)
8a96: a6 76                        ldx     zpLineBufIdx            ;get current index into line buffer
8a98: ad 07 20     @bufCpyLp       lda     PPUDATA                 ;read a byte from the screen
8a9b: 9d 00 05                     sta     lineBuffer,x            ;store in the lineBuffer
8a9e: e8                           inx                             ;incr lineBuffer idx
8a9f: e0 ff                        cpx     #$ff                    ;line buffer full?
8aa1: f0 10                        beq     @done                   ;yes -> finished buffering
8aa3: 88                           dey                             ;one less char to copy from row
8aa4: d0 f2                        bne     @bufCpyLp               ;more to copy? -> loop back for next char
8aa6: 86 76                        stx     zpLineBufIdx            ;update lineBuffer's index past where we copied to
8aa8: a6 49                        ldx     zpCV
8aaa: 20 37 b6                     jsr     DoesRowWrap             ;does the current row line-wrap to the next?
8aad: f0 04                        beq     @done                   ;line doesn't wrap -> done; terminate active NMICMD
8aaf: e6 49                        inc     zpCV                    ;line wraps: copy next row on next NMI
8ab1: d0 0b                        bne     NmiSameCmdCleanupAndExit ;(always) -> finish up until then
8ab3: a9 ff        @done           lda     #$ff
8ab5: ae a9 00                     ldx     a:$00a9                 ;(NOOP: disabled LDA #$00, but nothing jumps to it.)
8ab8: 85 64                        sta     zpOutTxtStartPos
                   NmiEndCmdCleanupAndExit
8aba: a9 00                        lda     #NC_NoCmd
                   NmiSetCmdCleanupAndExit
8abc: 85 63                        sta     zpNMICMD                ;turn our command indicator off
                   ; Many NMICMD handlers jump to here when done with the current frame
                   NmiSameCmdCleanupAndExit
8abe: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi
8ac1: 68                           pla                             ;pull pushed A-reg value
8ac2: 40                           rti                             ;-> and return from interrupt

                   ; Shifts the contents of a row on screen, left.
                   ; 
                   ; Parameters:
                   ;   zpCursTileAddr ($4a) {addr/2}: start of copy region (end is end of the row)
                   ;   zpMisc         ($19) {addr/2}: destination to copy to
                   ; 
                   ; Only ever used by Backspace, to shift line contents past the cursor over to
                   ; the left by one column. Thus zpMisc will hold the address of the current
                   ; cursor position, and zpCursTileAddr will hold the address of one past that.
                   NMICMD_CopyCharsLeft
8ac3: 20 2e b5                     jsr     PpuGoToCursor           ;go to (one past) current location in VRAM
8ac6: ad 07 20                     lda     PPUDATA                 ;initial, fake read
8ac9: a2 00                        ldx     #$00
8acb: a4 48                        ldy     zpCH
8acd: ad 07 20     @cpyLp          lda     PPUDATA                 ;copy from cursor to end of row, to lineBuffer
8ad0: 9d 00 05                     sta     lineBuffer,x
8ad3: e8                           inx
8ad4: c8                           iny
8ad5: c0 1c                        cpy     #COLS
8ad7: d0 f4                        bne     @cpyLp
8ad9: a5 1a                        lda     zpMisc+1                ;go to (true) current position
8adb: a6 19                        ldx     zpMisc
8add: 20 ec 81                     jsr     PpuGoToAX
8ae0: a2 00                        ldx     #$00
8ae2: a4 48                        ldy     zpCH
8ae4: bd 00 05     @cpyBackLp      lda     lineBuffer,x            ;copy the lineBuffer back out to PPUDATA.
8ae7: 8d 07 20                     sta     PPUDATA
8aea: e8                           inx
8aeb: c8                           iny
8aec: c0 1c                        cpy     #COLS
8aee: d0 f4                        bne     @cpyBackLp
8af0: f0 c8                        beq     NmiEndCmdCleanupAndExit ;done!

                   ; Sets background palette number at genPurposeBuf ($200),
                   ; to values at $201-$204.
                   NMICMD_SetBgPalette
8af2: a9 3f                        lda     #>PPU_PALETTE_RAM
8af4: 8d 06 20                     sta     PPUADDR
8af7: ad 00 07                     lda     genPurposeBuf           ;set addr to PPU palette RAM + ((palette num from genPurposeBuf) * 4)
8afa: 0a                           asl     A
8afb: 0a                           asl     A
8afc: 8d 06 20     NmiWritePalette sta     PPUADDR
8aff: ad 01 07                     lda     genPurposeBuf+1         ;write the palette color values
8b02: 8d 07 20                     sta     PPUDATA
8b05: ad 02 07                     lda     genPurposeBuf+2
8b08: 8d 07 20                     sta     PPUDATA
8b0b: ad 03 07                     lda     genPurposeBuf+3
8b0e: 8d 07 20                     sta     PPUDATA
8b11: ad 04 07                     lda     genPurposeBuf+4
8b14: 8d 07 20                     sta     PPUDATA
8b17: a9 3f                        lda     #>PPU_PALETTE_RAM       ;set addr to PPU palette RAM start
8b19: 8d 06 20                     sta     PPUADDR
8b1c: a9 00                        lda     #<PPU_PALETTE_RAM
8b1e: 8d 06 20                     sta     PPUADDR
8b21: 8d 06 20                     sta     PPUADDR                 ;then to #$0000?
8b24: 8d 06 20                     sta     PPUADDR
8b27: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi
8b2a: 20 14 be                     jsr     SetPpuCtrlAndEnableNmi
8b2d: 4c c9 89                     jmp     NMICMD_DeactivateSubcmd

                   ; Sets sprite palette number at genPurposeBuf ($200),
                   ; to values at $201-$204.
                   NMICMD_SetSprPalette
8b30: a9 3f                        lda     #>PPU_PALETTE_RAM
8b32: 8d 06 20                     sta     PPUADDR                 ;set addr to PPU palette RAM + 16 (for sprite palettes) + ((palette num from genPurposeBuf) * 4)
8b35: ad 00 07                     lda     genPurposeBuf
8b38: 0a                           asl     A
8b39: 0a                           asl     A
8b3a: 09 10                        ora     #$10
8b3c: d0 be                        bne     NmiWritePalette

                   ; Inserts designated character into the current row, shifting all chars past the
                   ; cursor, to the right by one. The char that was previously at the end of the
                   ; row but shifted off, is saved for subsequent calls.
                   vZpInsChar      .var    $b0    {addr/1}         ;the character to insert

                   NMICMD_InsertChar
8b3e: 20 2e b5                     jsr     PpuGoToCursor           ;go to cursor
8b41: a4 48                        ldy     zpCH
8b43: a2 01                        ldx     #$01
8b45: a5 b0                        lda     vZpInsChar              ;put char-to-be-inserted at start of lineBuffer
8b47: 8d 00 05                     sta     lineBuffer              ; (set by caller)
8b4a: ad 07 20                     lda     PPUDATA                 ;initial fake read
8b4d: ad 07 20     @cpyLp          lda     PPUDATA                 ;copy char to lineBuffer
8b50: 9d 00 05                     sta     lineBuffer,x
8b53: e8                           inx
8b54: c8                           iny
8b55: c0 1c                        cpy     #COLS                   ;loop until we reach end of row
8b57: d0 f4                        bne     @cpyLp
8b59: 85 b0                        sta     vZpInsChar              ;last char will be re-inserted in following line (in subsequent call)
8b5b: 20 2e b5                     jsr     PpuGoToCursor           ;return to cursor
8b5e: a2 00                        ldx     #$00
8b60: a4 48                        ldy     zpCH
8b62: bd 00 05     @insCpyLp       lda     lineBuffer,x            ;copy chars back out to screen,
8b65: 8d 07 20                     sta     PPUDATA                 ; with inserted char at start
8b68: e8                           inx                             ; (everything else shifts right)
8b69: c8                           iny
8b6a: c0 1c                        cpy     #COLS
8b6c: d0 f4                        bne     @insCpyLp               ;loop until we reach end of row
                   NmiSetVScrollAndDeactivate
8b6e: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi
8b71: 4c c9 89                     jmp     NMICMD_DeactivateSubcmd ;done!

                   • Clear variables

                   NMICMD_CopyRowDown_ph0
8b74: 20 2e b5                     jsr     PpuGoToCursor           ;go to cursor position in VRAM
8b77: a2 00                        ldx     #$00                    ;(I think CH must be 0?)
8b79: ad 07 20                     lda     PPUDATA                 ;fake read
8b7c: ad 07 20     @cpyLp          lda     PPUDATA                 ;get char from screen
8b7f: 9d 00 05                     sta     lineBuffer,x            ;copy to line buffer
8b82: e8                           inx
8b83: e0 1c                        cpx     #COLS                   ;loop until we reach the same position on next row
8b85: d0 f5                        bne     @cpyLp
8b87: a9 0f                        lda     #$0f                    ;next phase: copy back out to next row, on next frame
8b89: 4c bc 8a                     jmp     NmiSetCmdCleanupAndExit

                   NMICMD_CopyRowDown_ph1
8b8c: e6 49                        inc     zpCV                    ;move cursor down one row
8b8e: 20 f4 b4                     jsr     CalcCursorTilemapPos    ;calculate addr for new position
8b91: 20 2e b5                     jsr     PpuGoToCursor           ;and go there
8b94: a2 00                        ldx     #$00
8b96: bd 00 05     @cpyLp          lda     lineBuffer,x            ;copy from line buffer to screen
8b99: 8d 07 20                     sta     PPUDATA
8b9c: e8                           inx
8b9d: e0 1c                        cpx     #COLS
8b9f: d0 f5                        bne     @cpyLp                  ;copy 28 chars out (a row's worth)
8ba1: 4c 6e 8b                     jmp     NmiSetVScrollAndDeactivate ;done!

                   ; Point zpTxtPtr at lineBuffer ($500)
                   InitLineBufferScanning
8ba4: a9 00                        lda     #<lineBuffer
8ba6: 85 09                        sta     zpTXTPTR
8ba8: a9 05                        lda     #>lineBuffer
8baa: 85 0a                        sta     zpTXTPTR+1
8bac: 60                           rts

                   PointOutStrAtLineBufStart
8bad: a9 00                        lda     #<lineBuffer
8baf: 85 52                        sta     zpOutputStr
8bb1: a9 05                        lda     #>lineBuffer
8bb3: 85 53                        sta     zpOutputStr+1
8bb5: 60           rts_8BB5        rts

                   CheckFreeSpaceOrError
8bb6: a5 03                        lda     zpFRETOP
8bb8: 38                           sec
8bb9: e9 04                        sbc     #$04                    ;subtract 4 from end of free space
8bbb: 85 2c                        sta     zpWParam
8bbd: a5 04                        lda     zpFRETOP+1
8bbf: e9 00                        sbc     #$00
8bc1: 85 2d                        sta     zpWParam+1
8bc3: 20 e2 8f                     jsr     WordCompare             ;and compare what's in WAccum to that
8bc6: 90 ed                        bcc     rts_8BB5                ;less than zpFRETOP - 4? -> we're fine. Return.
                   ErrorOutOfMemory
8bc8: a9 06                        lda     #err_OM                 ;ERROR: insufficient memory!
8bca: 4c 37 b2                     jmp     PrintErrorAndStop

                   ; Check that we have enough stack space (20 bytes) to execute a line of BASIC.
                   ; Error if we do not.
8bcd: ba           ExecCheckMem    tsx
8bce: e0 14                        cpx     #$14
8bd0: 90 f6                        bcc     ErrorOutOfMemory
8bd2: 60           rts_8BD2        rts

8bd3: a9 00        AUTO_errSyn     lda     #$00
8bd5: 85 e2                        sta     zpAutoEnabled
8bd7: 4c 91 84                     jmp     ErrorSyntax

8bda: 08           CmdFn_AUTO      php                             ;push processor flags
8bdb: 48                           pha                             ;push accum
8bdc: a9 0a                        lda     #10
8bde: 85 e2                        sta     zpAutoEnabled           ;enable auto-increment
8be0: 85 a0                        sta     zpNxtAutoLNum           ;set next linenum to auto-prefix as 10, by default
8be2: 85 a2                        sta     zpAutoLNumIncr          ;increment by 10 by default
8be4: a9 00                        lda     #$00
8be6: 85 a1                        sta     zpNxtAutoLNum+1         ;set high bytes line num, increment
8be8: 85 a3                        sta     zpAutoLNumIncr+1
8bea: 68                           pla                             ;pull original accum
8beb: 28                           plp                             ;pull proc flags (from arg test)
8bec: f0 e4                        beq     rts_8BD2                ;no args? Done! (defaults above were enough)
8bee: c9 2c                        cmp     #‘,’                    ;is next char a comma?
8bf0: f0 16                        beq     @argTwo                 ;yes -> skip first arg, handle next
8bf2: c9 0b                        cmp     #$0b                    ;got line-number arg indicator byte?
8bf4: d0 dd                        bne     AUTO_errSyn             ;no -> error out
8bf6: 20 8c 90                     jsr     TxtPtrIncr              ;move past line-number prefix token
8bf9: 20 88 90                     jsr     TxtPtrGetCharAndIncr    ;get first byte of line num
8bfc: 85 a0                        sta     zpNxtAutoLNum           ;set it as first line num to prefix
8bfe: 20 88 90                     jsr     TxtPtrGetCharAndIncr    ;get 2nd byte
8c01: 85 a1                        sta     zpNxtAutoLNum+1         ;set as high byte for next line num
8c03: 20 9c 84                     jsr     IsEndOfCmd              ;any more args?
8c06: f0 ca                        beq     rts_8BD2                ;no -> done!
8c08: 20 14 ac     @argTwo         jsr     SkipCommaOrSynErr       ;yes: skip expected comma
8c0b: 20 bf 90                     jsr     TxtSkipSpaces           ;and any spaces
8c0e: c9 0b                        cmp     #$0b                    ;got line-number arg indicator byte?
8c10: d0 c1                        bne     AUTO_errSyn             ;no -> error out
8c12: 20 8c 90                     jsr     TxtPtrIncr              ;copy "line number" as auto-increment amount
8c15: 20 88 90                     jsr     TxtPtrGetCharAndIncr
8c18: 85 a2                        sta     zpAutoLNumIncr
8c1a: 20 88 90                     jsr     TxtPtrGetCharAndIncr
8c1d: 85 a3                        sta     zpAutoLNumIncr+1
8c1f: 60                           rts                             ;done!

                   RENUM_SetCharAndIncr
8c20: a0 00                        ldy     #$00
8c22: 91 09                        sta     (zpTXTPTR),y
8c24: 4c 8c 90                     jmp     TxtPtrIncr

8c27: 4c 91 84     errSyn_8C27     jmp     ErrorSyntax

                   restoreTxtPtr_8C2A
8c2a: 4c 39 94                     jmp     RestoreTXTPTR

8c2d: 4c 94 84     errIllVal_8C2D  jmp     ErrorIllegalValue

                   vZpNewStartLNum .var    $7c    {addr/2}         ;new starting line number
                   vZpOldStartLNum .var    $7e    {addr/2}         ;old starting line number
                   vZpRenumIncrAmt .var    $80    {addr/2}         ;amount to increment each new line number by.
                   vZpRenumNextLNum .var   $84    {addr/2}         ;next line number to write

8c30: 08           CmdFn_RENUM     php                             ;push processor flags
8c31: 48                           pha                             ;push accum (char at TXTPTR)
8c32: a9 0a                        lda     #10
8c34: 85 7c                        sta     vZpNewStartLNum
8c36: 85 80                        sta     vZpRenumIncrAmt
8c38: a9 00                        lda     #$00
8c3a: 85 7d                        sta     vZpNewStartLNum+1
8c3c: 85 7e                        sta     vZpOldStartLNum
8c3e: 85 7f                        sta     vZpOldStartLNum+1
8c40: 85 81                        sta     vZpRenumIncrAmt+1
8c42: 68                           pla                             ;pull accum
8c43: 28                           plp                             ;pull flags
8c44: f0 57                        beq     RENUM_checkNewGeOld     ;no args? -> stick to the defaults
8c46: c9 2c                        cmp     #‘,’                    ;got comma?
8c48: f0 16                        beq     @arg2                   ;yes -> skip 1st arg, check 2nd
8c4a: c9 0b                        cmp     #tok_LNUM_AFTER         ;got line-num prefix token?
8c4c: d0 d9                        bne     errSyn_8C27             ;no? -> error
8c4e: 20 8c 90                     jsr     TxtPtrIncr              ;yes. Skip the prefix token.
8c51: 20 88 90                     jsr     TxtPtrGetCharAndIncr    ;get new-start line num low byte
8c54: 85 7c                        sta     vZpNewStartLNum         ;and store
8c56: 20 88 90                     jsr     TxtPtrGetCharAndIncr    ;low byte
8c59: 85 7d                        sta     vZpNewStartLNum+1
8c5b: 20 9c 84                     jsr     IsEndOfCmd              ;more args?
8c5e: f0 3d                        beq     RENUM_checkNewGeOld     ;no -> everything else uses defaults
8c60: 20 14 ac     @arg2           jsr     SkipCommaOrSynErr       ;skip comma
8c63: 20 bf 90                     jsr     TxtSkipSpaces           ;and spaces
8c66: c9 2c                        cmp     #‘,’                    ;another comma?
8c68: f0 16                        beq     @arg3                   ;yes -> skip 2nd arg
8c6a: c9 0b                        cmp     #tok_LNUM_AFTER         ;else, line-num prefix token?
8c6c: d0 b9                        bne     errSyn_8C27             ;no -> error
8c6e: 20 8c 90                     jsr     TxtPtrIncr              ;move past token
8c71: 20 88 90                     jsr     TxtPtrGetCharAndIncr    ;get byte
8c74: 85 7e                        sta     vZpOldStartLNum         ;set low byte of starting (old) line number
8c76: 20 88 90                     jsr     TxtPtrGetCharAndIncr    ;'nother byte
8c79: 85 7f                        sta     vZpOldStartLNum+1       ;set high byte of starting (old) line number
8c7b: 20 9c 84                     jsr     IsEndOfCmd              ;end of command?
8c7e: f0 1d                        beq     RENUM_checkNewGeOld     ;yes -> leave increment amt at default of 10
8c80: 20 14 ac     @arg3           jsr     SkipCommaOrSynErr       ;skip expected comma
8c83: 20 bf 90                     jsr     TxtSkipSpaces           ;and spaces
8c86: c9 0b                        cmp     #tok_LNUM_AFTER         ;got line-num prefix token?
8c88: d0 9d                        bne     errSyn_8C27             ;no -> error
8c8a: 20 8c 90                     jsr     TxtPtrIncr              ;move past token byte
8c8d: 20 88 90                     jsr     TxtPtrGetCharAndIncr    ;get byte
8c90: 85 80                        sta     vZpRenumIncrAmt         ;set as low byte of increment amt
8c92: 20 88 90                     jsr     TxtPtrGetCharAndIncr    ;'nother byte
8c95: 85 81                        sta     vZpRenumIncrAmt+1       ;set as high byte of increment amt
8c97: d0 04                        bne     RENUM_checkNewGeOld     ;high byte nonzero? Yes -> continue
8c99: a5 80                        lda     vZpRenumIncrAmt         ;high byte zero. Low byte zero?
8c9b: f0 90                        beq     errIllVal_8C2D          ;yes -> error. Can't have a zero increment!
                   RENUM_checkNewGeOld
8c9d: a5 7d                        lda     vZpNewStartLNum+1
8c9f: c5 7f                        cmp     vZpOldStartLNum+1
8ca1: f0 02                        beq     @cmpLo                  ;is new start lnum (hi byte) > old start lnum (hi byte)?
8ca3: b0 0c                        bcs     RENUM_argsDone          ;yes -> everything's great
8ca5: a5 7c        @cmpLo          lda     vZpNewStartLNum         ;else, is new start lnum (lo byte) >= old start lnum (lo byte)?
8ca7: c5 7e                        cmp     vZpOldStartLNum         ;BUG! Permits high byte lower, as long as low byte is highger!
8ca9: b0 06                        bcs     RENUM_argsDone          ; This can result in repeated line numbers, and/or lower coming after higher
8cab: 4c 94 84                     jmp     ErrorIllegalValue

                   restoreTxtPtr_8CAE
8cae: 4c 2a 8c                     jmp     restoreTxtPtr_8C2A

8cb1: 20 30 94     RENUM_argsDone  jsr     SaveTXTPTR              ;save away curent TXTPTR
8cb4: 20 b1 8d                     jsr     ErrorIfNewLNumsOverflow ;go check whether new line numbers
8cb7: a5 05                        lda     zpTXTTAB                ; will overflow, and error if so
8cb9: 85 09                        sta     zpTXTPTR
8cbb: a5 06                        lda     zpTXTTAB+1              ;point TXTPTR at first line of BASIC
8cbd: 85 0a                        sta     zpTXTPTR+1
8cbf: 20 b2 90                     jsr     GetTxtChar              ;get offset to 2nd line in program
8cc2: f0 ea                        beq     restoreTxtPtr_8CAE      ;BASIC program empty? -> nothing to do; return
8cc4: 20 10 9f                     jsr     TxtPtrAdvThree          ;skip over offset and line num
                   @RENUM_scanCodeForLNums
8cc7: 20 b2 90                     jsr     GetTxtChar              ;get a byte of line content
8cca: f0 0a                        beq     @advLine                ;end of line? -> advance to next
8ccc: c9 0b                        cmp     #tok_LNUM_AFTER         ;got a line num prefix token?
8cce: f0 11                        beq     @lnumTokFound
8cd0: 20 f1 9e                     jsr     SkipToken               ;else skip this byte
8cd3: 4c c7 8c                     jmp     @RENUM_scanCodeForLNums ; and check next

8cd6: 20 98 90     @advLine        jsr     TxtPtrIncrAndGetChar    ;are we at prog end?
8cd9: f0 3c                        beq     RENUM_doRenum           ;yes -> done scanning for line numbers in content
8cdb: 20 10 9f                     jsr     TxtPtrAdvThree
8cde: 4c c7 8c                     jmp     @RENUM_scanCodeForLNums

8ce1: a5 09        @lnumTokFound   lda     zpTXTPTR                ;push TXTPTR lo, hi
8ce3: 48                           pha
8ce4: a5 0a                        lda     zpTXTPTR+1
8ce6: 48                           pha
8ce7: 20 8c 90                     jsr     TxtPtrIncr              ;skip line-num prefix token byte
8cea: 20 88 90                     jsr     TxtPtrGetCharAndIncr    ;save following line num as line-to-find
8ced: 85 44                        sta     zpFindLNum
8cef: 20 88 90                     jsr     TxtPtrGetCharAndIncr
8cf2: 85 45                        sta     zpFindLNum+1
8cf4: 20 40 85                     jsr     FindLineNum             ;find the indicated line num
8cf7: f0 06                        beq     @match                  ;exact match? -> leave zpTxtLink as FindLineNum set it
8cf9: a9 ff                        lda     #$ff                    ;else (not found): invalidate zpTxtLink
8cfb: 85 5b                        sta     zpTxtLink
8cfd: 85 5c                        sta     zpTxtLink+1
8cff: 68           @match          pla                             ;restore TXTPTR from stack
8d00: 85 0a                        sta     zpTXTPTR+1
8d02: 68                           pla
8d03: 85 09                        sta     zpTXTPTR
8d05: a9 0c                        lda     #tok_LLINK_AFTER        ;save over line-number token with line-link token,
8d07: 20 20 8c                     jsr     RENUM_SetCharAndIncr    ; so we can scan over them again
8d0a: a5 5b                        lda     zpTxtLink               ; and replace with updated line number
8d0c: 20 20 8c                     jsr     RENUM_SetCharAndIncr
8d0f: a5 5c                        lda     zpTxtLink+1
8d11: 20 20 8c                     jsr     RENUM_SetCharAndIncr
8d14: 4c c7 8c                     jmp     @RENUM_scanCodeForLNums ;return to scanning for more line numbers

8d17: a5 7e        RENUM_doRenum   lda     vZpOldStartLNum         ;set the (original) first line of renumber range
8d19: 85 44                        sta     zpFindLNum              ; as the line to search for
8d1b: a5 7f                        lda     vZpOldStartLNum+1
8d1d: 85 45                        sta     zpFindLNum+1
8d1f: 20 40 85                     jsr     FindLineNum             ;and find it
8d22: a5 5b                        lda     zpTxtLink
8d24: 85 09                        sta     zpTXTPTR                ;point TXTPTR at the start of the found line
8d26: a5 5c                        lda     zpTxtLink+1
8d28: 85 0a                        sta     zpTXTPTR+1
8d2a: 20 b2 90     RENUM_renumLoop jsr     GetTxtChar              ;read the line's offset byte
8d2d: f0 2d                        beq     RENUM_replaceLineLinks  ;end of prog? -> replace line links with updated line numbers
8d2f: 48                           pha                             ;push offset to next line
8d30: 20 8c 90                     jsr     TxtPtrIncr
8d33: a5 7c                        lda     vZpNewStartLNum         ;write renumbered line number over old one
8d35: 20 20 8c                     jsr     RENUM_SetCharAndIncr
8d38: a5 7d                        lda     vZpNewStartLNum+1
8d3a: 20 20 8c                     jsr     RENUM_SetCharAndIncr
8d3d: 68                           pla                             ;pull offset to next line
8d3e: 38                           sec
8d3f: e9 03                        sbc     #$03                    ;subtract 3 (bc we just advanced by 3)
8d41: 18                           clc
8d42: 65 09                        adc     zpTXTPTR                ;advance TXTPTR to start of next line
8d44: 85 09                        sta     zpTXTPTR
8d46: a9 00                        lda     #$00
8d48: 65 0a                        adc     zpTXTPTR+1
8d4a: 85 0a                        sta     zpTXTPTR+1
8d4c: 18                           clc
8d4d: a5 80                        lda     vZpRenumIncrAmt         ;advance next line number by increment
8d4f: 65 7c                        adc     vZpNewStartLNum
8d51: 85 7c                        sta     vZpNewStartLNum
8d53: a5 81                        lda     vZpRenumIncrAmt+1
8d55: 65 7d                        adc     vZpNewStartLNum+1
8d57: 85 7d                        sta     vZpNewStartLNum+1
8d59: 4c 2a 8d                     jmp     RENUM_renumLoop         ;loop around to renumber this next line

                   RENUM_replaceLineLinks
8d5c: a5 05                        lda     zpTXTTAB                ;point TXTPTR at start of BASIC program once again
8d5e: 85 09                        sta     zpTXTPTR
8d60: a5 06                        lda     zpTXTTAB+1
8d62: 85 0a                        sta     zpTXTPTR+1
8d64: 20 10 9f                     jsr     TxtPtrAdvThree          ;skip offset, line number
                   RENUM_linkReplLoop
8d67: 20 b2 90                     jsr     GetTxtChar              ;get a byte from line content
8d6a: f0 0d                        beq     @advLine                ;end of line? -> scan next line
8d6c: c9 0c                        cmp     #tok_LLINK_AFTER        ;got a line-link prefix byte (that we wrote earlier)?
8d6e: f0 14                        beq     @linkFound              ;yes -> replace with updated line num
8d70: 20 f1 9e                     jsr     SkipToken               ;else, skip this byte
8d73: 4c 67 8d                     jmp     RENUM_linkReplLoop      ;and check the next

                   restoreTxtPtr_8D76
8d76: 4c 39 94                     jmp     RestoreTXTPTR

8d79: 20 98 90     @advLine        jsr     TxtPtrIncrAndGetChar    ;are we at prog end?
8d7c: f0 f8                        beq     restoreTxtPtr_8D76      ;yes -> restore TXTPTR, then all done
8d7e: 20 10 9f                     jsr     TxtPtrAdvThree          ;skip offset and line-num bytes
8d81: 4c 67 8d                     jmp     RENUM_linkReplLoop      ;and return to scanning for line links to replace

8d84: a0 00        @linkFound      ldy     #$00
8d86: a9 0b                        lda     #tok_LNUM_AFTER
8d88: 91 09                        sta     (zpTXTPTR),y            ;replace line-link token byte with line-num token
8d8a: c8                           iny
8d8b: b1 09                        lda     (zpTXTPTR),y
8d8d: 85 5b                        sta     zpTxtLink               ;read the line link into zpTxtLink
8d8f: c8                           iny
8d90: b1 09                        lda     (zpTXTPTR),y
8d92: 85 5c                        sta     zpTxtLink+1
8d94: c9 ff                        cmp     #$ff
8d96: d0 0c                        bne     @writeNewLNum
8d98: a5 5b                        lda     zpTxtLink
8d9a: c9 ff                        cmp     #$ff                    ;invalid link (orig line num invalid)?
8d9c: d0 06                        bne     @writeNewLNum           ;no -> safe to use real line num
                   @skipLNumAndIterate
8d9e: 20 10 9f                     jsr     TxtPtrAdvThree          ;skip over line link
8da1: 4c 67 8d                     jmp     RENUM_linkReplLoop      ;and iterate to find/replace more

8da4: 88           @writeNewLNum   dey                             ;back up a byte
8da5: b1 5b                        lda     (zpTxtLink),y           ;overwrite line link with its new number
8da7: 91 09                        sta     (zpTXTPTR),y
8da9: c8                           iny
8daa: b1 5b                        lda     (zpTxtLink),y
8dac: 91 09                        sta     (zpTXTPTR),y
8dae: 4c 9e 8d                     jmp     @skipLNumAndIterate     ;-> go back to finding/replacing line links

                   ErrorIfNewLNumsOverflow
8db1: a5 7c                        lda     vZpNewStartLNum         ;save new first line number, to "next" line number
8db3: 85 84                        sta     vZpRenumNextLNum
8db5: a5 7d                        lda     vZpNewStartLNum+1
8db7: 85 85                        sta     vZpRenumNextLNum+1
8db9: a5 7e                        lda     vZpOldStartLNum         ;save old first line num of range, as line to find
8dbb: 85 44                        sta     zpFindLNum
8dbd: a5 7f                        lda     vZpOldStartLNum+1
8dbf: 85 45                        sta     zpFindLNum+1
8dc1: 20 40 85                     jsr     FindLineNum             ;find it!
8dc4: a5 5b                        lda     zpTxtLink               ;point TXTPTR at first byte of found line (the offset to next)
8dc6: 85 09                        sta     zpTXTPTR
8dc8: a5 5c                        lda     zpTxtLink+1
8dca: 85 0a                        sta     zpTXTPTR+1
8dcc: 20 b2 90     @lnumLp         jsr     GetTxtChar              ;get the offset byte. At prog end?
8dcf: f0 1d                        beq     rts_8DEE                ;yes -> return
8dd1: 18                           clc
8dd2: 65 09                        adc     zpTXTPTR                ;advance TXTPTR to start of following line
8dd4: 85 09                        sta     zpTXTPTR
8dd6: a9 00                        lda     #$00
8dd8: 65 0a                        adc     zpTXTPTR+1
8dda: 85 0a                        sta     zpTXTPTR+1
8ddc: 18                           clc
8ddd: a5 80                        lda     vZpRenumIncrAmt         ;advance "next line num" by increment amount
8ddf: 65 84                        adc     vZpRenumNextLNum
8de1: 85 84                        sta     vZpRenumNextLNum
8de3: a5 81                        lda     vZpRenumIncrAmt+1
8de5: 65 85                        adc     vZpRenumNextLNum+1
8de7: 85 85                        sta     vZpRenumNextLNum+1      ;did we overflow a new line number?
8de9: b0 04                        bcs     errIllVal_8DEF          ;yes -> error
8deb: 4c cc 8d                     jmp     @lnumLp                 ;else, loop back and check another line-num increment

8dee: 60           rts_8DEE        rts

8def: 4c 94 84     errIllVal_8DEF  jmp     ErrorIllegalValue

                   ; Sets Z flag iff zpWAccum contains (+/-)32768 (#$8000)
                   ; Sets C if and only if zpWAccum is negative
                   • Clear variables

8df2: a5 29        Cmp32768        lda     zpWAccum+1
8df4: c9 80                        cmp     #$80
8df6: f0 01                        beq     @ckLo
8df8: 60                           rts

8df9: a5 28        @ckLo           lda     zpWAccum
8dfb: 38                           sec
8dfc: 60                           rts

8dfd: 38           WParamNegate    sec
8dfe: a9 00                        lda     #$00
8e00: e5 2c                        sbc     zpWParam
8e02: 85 2c                        sta     zpWParam
8e04: a9 00                        lda     #$00
8e06: e5 2d                        sbc     zpWParam+1
8e08: 85 2d                        sta     zpWParam+1
8e0a: 60                           rts

                   SetIntAndSubtract
8e0b: a9 02                        lda     #$02
8e0d: 85 27                        sta     zpArgTyp
                   ; This routine, together with the Add routine it wraps, is the most ridiculously
                   ; overcoded and overcomplicated "subtract two words" implementation I have ever
                   ; laid eyes on. Could save a ton of code space by just using WordSubtract and
                   ; checking for overflow afterward. Always negates the first operand (except when
                   ; it's -32,768 and therefore can't be) before passing to the Add sub, and
                   ; therefore always negates the result as well. Add, itself, may well negate the
                   ; first operand and the result, meaning that both WAccum and the result from adc
                   ; or sbc may undergo multiple negations (and even if they only undergo it once,
                   ; it's unnecessary and ineffecient).
8e0f: 20 0d aa     Subtract        jsr     ErrorUnlessInteger
8e12: 20 f2 8d                     jsr     Cmp32768
8e15: f0 29                        beq     Sub_WAccumIs32768       ;zpWAccum = -32768? -> ensure zpWParam is 0
8e17: 20 32 8e                     jsr     WAccumNegate
8e1a: a5 29                        lda     zpWAccum+1
8e1c: 30 11                        bmi     @haveNegArg
8e1e: a5 2d                        lda     zpWParam+1
8e20: 30 0d                        bmi     @haveNegArg
8e22: 20 f7 8e                     jsr     WordAdd
8e25: 20 f2 8d                     jsr     Cmp32768
8e28: f0 15                        beq     rts_8E3F
8e2a: 90 06                        bcc     WAccumNegate
8e2c: 4c f2 8e                     jmp     ErrorOverflow

8e2f: 20 ac 8e     @haveNegArg     jsr     Add
8e32: 38           WAccumNegate    sec
8e33: a9 00                        lda     #$00
8e35: e5 28                        sbc     zpWAccum
8e37: 85 28                        sta     zpWAccum
8e39: a9 00                        lda     #$00
8e3b: e5 29                        sbc     zpWAccum+1
8e3d: 85 29                        sta     zpWAccum+1
8e3f: 60           rts_8E3F        rts

                   Sub_WAccumIs32768
8e40: a5 2d                        lda     zpWParam+1
8e42: 10 06                        bpl     @errOverfl              ;BUG: produces OV ERROR even if WParam is zero lol
8e44: 20 fd 8d                     jsr     WParamNegate
8e47: 4c ac 8e                     jmp     Add

8e4a: 4c f2 8e     @errOverfl      jmp     ErrorOverflow

                   CopyWParamToWAccum
8e4d: a5 2c                        lda     zpWParam
8e4f: 85 28                        sta     zpWAccum
8e51: a5 2d                        lda     zpWParam+1
8e53: 85 29                        sta     zpWAccum+1
8e55: a5 2e                        lda     zpWParam+2
8e57: 85 2a                        sta     zpWAccum+2
8e59: 60           rts_8E59        rts

                   ConcatenateStrings
8e5a: a6 21                        ldx     zpGpStrIdx
8e5c: 86 4a                        stx     zpCursTileAddr
8e5e: a5 28                        lda     zpWAccum                ;strlen == 0?
8e60: f0 eb                        beq     CopyWParamToWAccum      ;yes -> just use other operand then
8e62: 18                           clc
8e63: a4 2c                        ldy     zpWParam                ;param len == 0?
8e65: f0 f2                        beq     rts_8E59                ;yes -> nothing to do, return
8e67: 65 2c                        adc     zpWParam                ;add the two strlens
8e69: c9 20                        cmp     #$20                    ; >= 32?
8e6b: b0 dd                        bcs     @errOverfl              ;yes -> too big to fit in a string var
8e6d: 85 4b                        sta     zpCursTileAddr+1
8e6f: a5 29                        lda     zpWAccum+1              ;copy strptr to $19.1A
8e71: 85 19                        sta     zpMisc
8e73: a5 2a                        lda     zpWAccum+2
8e75: 85 1a                        sta     zpMisc+1
8e77: a0 00                        ldy     #$00
8e79: b1 19        @cpyWaccumOutb  lda     (zpMisc),y              ;copy zpWAccum str to outbuf
8e7b: 9d 00 07                     sta     genPurposeBuf,x
8e7e: e8                           inx
8e7f: c8                           iny
8e80: c6 28                        dec     zpWAccum
8e82: d0 f5                        bne     @cpyWaccumOutb
8e84: a5 2d                        lda     zpWParam+1              ;copy PARAM str to outbuf (after ACC)
8e86: 85 19                        sta     zpMisc
8e88: a5 2e                        lda     zpWParam+2
8e8a: 85 1a                        sta     zpMisc+1
8e8c: a0 00                        ldy     #$00
8e8e: b1 19        @cpyParamOutb   lda     (zpMisc),y
8e90: 9d 00 07                     sta     genPurposeBuf,x
8e93: c8                           iny
8e94: e8                           inx
8e95: c6 2c                        dec     zpWParam
8e97: d0 f5                        bne     @cpyParamOutb
8e99: 86 21                        stx     zpGpStrIdx
8e9b: a5 4b                        lda     zpCursTileAddr+1
8e9d: 85 28                        sta     zpWAccum
8e9f: a5 4a                        lda     zpCursTileAddr
8ea1: 85 29                        sta     zpWAccum+1
8ea3: a9 07                        lda     #$07
8ea5: 85 2a                        sta     zpWAccum+2
8ea7: 60                           rts

8ea8: a9 02        SetIntAndAdd    lda     #TYP_INT
8eaa: 85 27                        sta     zpArgTyp
                   ; This routine is the most ridiculously overcoded and overcomplicated "add two
                   ; words" implementation I have ever laid eyes on. Could save a ton of code space
                   ; by just using WordAdd and checking for overflow afterward. It branches on
                   ; positivity for each argument, maneuvering with negation to ensure it performs
                   ; an operation on two positive operands. John Brooks (jbrooks@blueshiftinc.com)
                   ; points out that this is a common strategy for multiplication and division, but
                   ; quite useless for addition/subtraction. Such a waste!
8eac: a5 27        Add             lda     zpArgTyp
8eae: c9 03                        cmp     #TYP_STR                ;are we working with strs?
8eb0: f0 a8                        beq     ConcatenateStrings      ;yes -> concatenate them
8eb2: a5 29                        lda     zpWAccum+1              ;no: we are working with integer words
8eb4: 30 1b                        bmi     AddInt_WAccumIsNeg      ;WAccum neg -> handle that case
8eb6: a5 2d                        lda     zpWParam+1              ;WParam neg?
8eb8: 30 06                        bmi     ParamNegateThenSubtract ;yes -> handle
8eba: 20 f7 8e                     jsr     WordAdd                 ;both args positive, just add
8ebd: 30 33                        bmi     ErrorOverflow           ;and error if the result is neg
8ebf: 60                           rts

                   ParamNegateThenSubtract
8ec0: 20 fd 8d                     jsr     WParamNegate
8ec3: 38           WordSubtract    sec
8ec4: a5 28                        lda     zpWAccum
8ec6: e5 2c                        sbc     zpWParam
8ec8: 85 28                        sta     zpWAccum
8eca: a5 29                        lda     zpWAccum+1
8ecc: e5 2d                        sbc     zpWParam+1
8ece: 85 29                        sta     zpWAccum+1
8ed0: 60                           rts

                   AddInt_WAccumIsNeg
8ed1: a5 2d                        lda     zpWParam+1
8ed3: 30 09                        bmi     AddInt_BothNeg
8ed5: 20 32 8e                     jsr     WAccumNegate
8ed8: 20 c3 8e                     jsr     WordSubtract
8edb: 4c 32 8e                     jmp     WAccumNegate

8ede: 20 32 8e     AddInt_BothNeg  jsr     WAccumNegate
8ee1: 20 fd 8d                     jsr     WParamNegate
8ee4: 20 f7 8e                     jsr     WordAdd
8ee7: 20 f2 8d                     jsr     Cmp32768
8eea: f0 05                        beq     @rts
8eec: b0 04                        bcs     ErrorOverflow
8eee: 4c 32 8e                     jmp     WAccumNegate

8ef1: 60           @rts            rts

8ef2: a9 05        ErrorOverflow   lda     #err_OV
8ef4: 4c 37 b2                     jmp     PrintErrorAndStop

                   ; Adds $2C.2D to $28.29, leaves in $28.29.
8ef7: 18           WordAdd         clc
8ef8: a5 2c                        lda     zpWParam
8efa: 65 28                        adc     zpWAccum
8efc: 85 28                        sta     zpWAccum
8efe: a5 2d                        lda     zpWParam+1
8f00: 65 29                        adc     zpWAccum+1
8f02: 85 29                        sta     zpWAccum+1
8f04: 60                           rts

                   ; Clears the scratch space used by WordMultiply. The first byte ($34) is never
                   ; actually cleared (not necessary), and the last four ($38 thru $3B) are never
                   ; actually used by anything.
                   ClearMultiplyScratch
8f05: a9 00                        lda     #$00
8f07: a2 08                        ldx     #$08
8f09: 95 34        @clearLp        sta     zpMulScratch,x
8f0b: ca                           dex
8f0c: d0 fb                        bne     @clearLp                ;-> write eight "zero" bytes
8f0e: 60                           rts                             ;-> done

                   MultiplyAndCheckPosNeg
8f0f: a5 29                        lda     zpWAccum+1              ;is WAccum negative?
8f11: 30 11                        bmi     @wAccNeg                ;yes -> check WPararm
8f13: a5 2d                        lda     zpWParam+1              ;else is WAccum negative?
8f15: 30 11                        bmi     @mulExpectNeg           ;yes -> expect negative result
8f17: 20 35 8f     @mulExpectPos   jsr     WordMultiplyAsWord
8f1a: 20 3d 8f                     jsr     IsWAccumZero            ;WAccum == 0?
8f1d: f0 24                        beq     rts_8F43                ;yes? -> done
8f1f: a5 29                        lda     zpWAccum+1              ;is result negative?
8f21: 30 cf                        bmi     ErrorOverflow           ;yes -> overflow error
8f23: 60                           rts

8f24: a5 2d        @wAccNeg        lda     zpWParam+1              ;WAccum is neg. Is Wparam also negative?
8f26: 30 ef                        bmi     @mulExpectPos           ;yes -> expect pos result
8f28: 20 35 8f     @mulExpectNeg   jsr     WordMultiplyAsWord
8f2b: 20 3d 8f                     jsr     IsWAccumZero            ;WAccum == 0?
8f2e: f0 13                        beq     rts_8F43                ;yes -> done
8f30: a5 29                        lda     zpWAccum+1              ;is result positive?
8f32: 10 be                        bpl     ErrorOverflow           ;yes -> overflow error
8f34: 60                           rts

                   ; Multiply, and cause an overflow error (OV) if it doesn't fit in a word.
                   ; Multiplies the contents of $28.29 by $2C.2D, leaving the results in $28.29.
                   ; 
                   ; BUG: won't cause overflow if (result MOD (2 ** 24)) fits in a word. for
                   ; example, multiplying 4095 * 4095 produces overflow, but multiplying 4097
                   ; * 4097 does NOT. This is because it checks for overflow in $36, but not
                   ; in $37.
                   WordMultiplyAsWord
8f35: 20 44 8f                     jsr     WordMultiplyWithTypeCheck
8f38: a5 36                        lda     zpMulScratch+2
8f3a: d0 b6                        bne     ErrorOverflow
8f3c: 60                           rts

8f3d: a5 28        IsWAccumZero    lda     zpWAccum
8f3f: d0 02                        bne     rts_8F43
8f41: a5 29                        lda     zpWAccum+1
8f43: 60           rts_8F43        rts

                   ; Multiplies the contents of $28.29 by $2C.2D leaving the results in $28.29.
                   WordMultiplyWithTypeCheck
8f44: 20 0d aa                     jsr     ErrorUnlessInteger
8f47: 20 05 8f     WordMultiply    jsr     ClearMultiplyScratch    ;clear scratch-space values
8f4a: 20 7d 8f                     jsr     EnsureWAccParmPositive  ;make sure WAccum, WParam hold positive values
8f4d: 48                           pha                             ;push whether we need to negate after bc of sign changes
8f4e: a2 10                        ldx     #$10                    ;set X reg to sixteen (bits)
8f50: 18                           clc
8f51: 66 29        @multLoop       ror     zpWAccum+1              ;shift WAccum right by one
8f53: 66 28                        ror     zpWAccum                ;was the bit set?
8f55: 90 0d                        bcc     @zeroBit                ;no -> skip adding param
8f57: 18                           clc                             ;yes, bit was set: must add param here
8f58: a5 36                        lda     zpMulScratch+2
8f5a: 65 2c                        adc     zpWParam
8f5c: 85 36                        sta     zpMulScratch+2          ;add WParam to 2 bytes (16 bits) left of result space
8f5e: a5 37                        lda     zpMulScratch+3
8f60: 65 2d                        adc     zpWParam+1
8f62: 85 37                        sta     zpMulScratch+3          ;add the carry to a byte left of that
8f64: 66 37        @zeroBit        ror     zpMulScratch+3          ;now shift all result bits right one (this will happen 16 times in all)
8f66: 66 36                        ror     zpMulScratch+2
8f68: 66 35                        ror     zpMulScratch+1
8f6a: 66 34                        ror     zpMulScratch
8f6c: ca                           dex                             ;one shift done. More remain?
8f6d: d0 e2                        bne     @multLoop               ;-> loop for a total of 16 shifts
8f6f: a5 34                        lda     zpMulScratch            ;result is in zpMulScratch+0,+1
8f71: 85 28                        sta     zpWAccum                ;copy to WParam as the result
8f73: a5 35                        lda     zpMulScratch+1
8f75: 85 29                        sta     zpWAccum+1
                   WMul_PullAndObeyNegFlag
8f77: 68                           pla                             ;pull negation flag. Do we need to negate?
8f78: f0 22                        beq     rts_8F9C                ;no -> we're done
8f7a: 4c 32 8e                     jmp     WAccumNegate            ;yes: negate accum and return

                   vZpNegateResult .var    $34    {addr/1}

                   EnsureWAccParmPositive
8f7d: a9 00                        lda     #$00
8f7f: 85 34                        sta     vZpNegateResult         ;start with assumption we won't negate the result
8f81: a5 29                        lda     zpWAccum+1              ;is WAccum negative?
8f83: 10 07                        bpl     @wpPos                  ;no -> move on to WParam
8f85: 20 32 8e                     jsr     WAccumNegate            ;yes: make it positive
8f88: a9 ff                        lda     #$ff
8f8a: 85 34                        sta     vZpNegateResult         ;mark that we should negate the result
8f8c: a5 2d        @wpPos          lda     zpWParam+1              ;is WParam negative?
8f8e: 10 0a                        bpl     @ldaAndRts              ;no -> we're done!
8f90: 20 fd 8d                     jsr     WParamNegate            ;yes: negate WParam
8f93: a9 ff                        lda     #$ff
8f95: 38                           sec
8f96: e5 34                        sbc     vZpNegateResult         ;toggle result negation/reversesetting
8f98: 85 34                        sta     vZpNegateResult
8f9a: a5 34        @ldaAndRts      lda     vZpNegateResult         ;this var will be destroyed after; load into accum so caller can push to stack
8f9c: 60           rts_8F9C        rts

                   ; Divide WAccum by WParam. Waccum+2,3 will hold remainder/modulus
                   • Clear variables

8f9d: 20 0d aa     WordDivide      jsr     ErrorUnlessInteger
8fa0: 20 7d 8f                     jsr     EnsureWAccParmPositive
8fa3: 48                           pha                             ;push on stack, whether we should negate result after
8fa4: a9 00                        lda     #$00
8fa6: 85 2a                        sta     zpWAccum+2
8fa8: 85 2b                        sta     zpWAccum+3
8faa: a2 10                        ldx     #16                     ;loop over 16 bits
8fac: 06 28        @divLoop        asl     zpWAccum
8fae: 26 29                        rol     zpWAccum+1
8fb0: 26 2a                        rol     zpWAccum+2
8fb2: 26 2b                        rol     zpWAccum+3
8fb4: a5 2a                        lda     zpWAccum+2
8fb6: 38                           sec                             ;LONG DIVISION in binary!
8fb7: e5 2c                        sbc     zpWParam                ;perform a throwaway subtraction of WParam
8fb9: a5 2b                        lda     zpWAccum+3              ; from the shortest initial portion of WAccum
8fbb: e5 2d                        sbc     zpWParam+1              ; that we can subtract from
8fbd: 90 0f                        bcc     @noSbc                  ;carry clear (borrow set)? -> can't subtract, try a longer portion
8fbf: a5 2a                        lda     zpWAccum+2              ;no borrow: perform subtract for real!
8fc1: 38                           sec
8fc2: e5 2c                        sbc     zpWParam
8fc4: 85 2a                        sta     zpWAccum+2
8fc6: a5 2b                        lda     zpWAccum+3
8fc8: e5 2d                        sbc     zpWParam+1
8fca: 85 2b                        sta     zpWAccum+3
8fcc: e6 28                        inc     zpWAccum
8fce: ca           @noSbc          dex
8fcf: d0 db                        bne     @divLoop
8fd1: 4c 77 8f                     jmp     WMul_PullAndObeyNegFlag

                   ; Exactly the same as WordSubtract. Useless duplicate routine.
                   WordSubtractClone
8fd4: 38                           sec
8fd5: a5 28                        lda     zpWAccum
8fd7: e5 2c                        sbc     zpWParam
8fd9: 85 28                        sta     zpWAccum
8fdb: a5 29                        lda     zpWAccum+1
8fdd: e5 2d                        sbc     zpWParam+1
8fdf: 85 29                        sta     zpWAccum+1
8fe1: 60                           rts

                   ; Compare WAccum to WParam
8fe2: a5 29        WordCompare     lda     zpWAccum+1
8fe4: c5 2d                        cmp     zpWParam+1
8fe6: f0 01                        beq     @cmpLow
8fe8: 60                           rts

8fe9: a5 28        @cmpLow         lda     zpWAccum
8feb: c5 2c                        cmp     zpWParam
8fed: 60                           rts

                   ; Compare two operands, in WAccum and WParam. We'll refer to contents of WAccum
                   ; as "A", and WParam as "B".
                   vZpStrLenA      .var    $4a    {addr/1}
                   vZpStrLenB      .var    $4b    {addr/1}

8fee: a5 27        Compare         lda     zpArgTyp
8ff0: c9 03                        cmp     #TYP_STR                ;args string type?
8ff2: f0 41                        beq     CompareStrings          ;yes -> compare strings
8ff4: a5 29                        lda     zpWAccum+1              ;is A negative?
8ff6: 30 23                        bmi     Cmp_AIsNegative         ;yes -> check B
8ff8: a5 2d                        lda     zpWParam+1              ;is B negative?
8ffa: 30 0c                        bmi     Cmp_AGreaterThanB       ;yes -> A isn't, so A > B
8ffc: a5 29                        lda     zpWAccum+1              ;no: both are pos
8ffe: c5 2d                        cmp     zpWParam+1              ;cmp A, B high bytes
9000: f0 0a                        beq     Cmp_LoBytes1
9002: b0 04                        bcs     Cmp_AGreaterThanB
9004: 18           Cmp_ALessThanB  clc                             ;clear carry for "less than"
9005: a9 ff                        lda     #$ff                    ;indicate unequal
9007: 60                           rts

                   Cmp_AGreaterThanB
9008: 38                           sec                             ;set carry for "greater than or equal"
9009: a9 ff                        lda     #$ff                    ;indicate unequal
900b: 60                           rts

900c: a5 28        Cmp_LoBytes1    lda     zpWAccum                ;this routine exactly identical to Cmp_LoBytes
900e: c5 2c                        cmp     zpWParam
9010: f0 05                        beq     Cmp_Equal
9012: b0 f4                        bcs     Cmp_AGreaterThanB
9014: 4c 04 90                     jmp     Cmp_ALessThanB

9017: 38           Cmp_Equal       sec                             ;set carry for "greater than or equal"
9018: a9 00                        lda     #$00                    ;and load zero to signify equality
901a: 60                           rts

901b: a5 2d        Cmp_AIsNegative lda     zpWParam+1              ;is B positive?
901d: 10 e5                        bpl     Cmp_ALessThanB          ;yes -> A < B
901f: a5 29                        lda     zpWAccum+1              ;compare A, B high bytes
9021: c5 2d                        cmp     zpWParam+1
9023: f0 05                        beq     Cmp_LoBytes             ;equal? -> compare low bytes
9025: b0 e1                        bcs     Cmp_AGreaterThanB       ;A high byte > B high byte? -> A > B
9027: 4c 04 90                     jmp     Cmp_ALessThanB          ;else -> A < B

902a: a5 28        Cmp_LoBytes     lda     zpWAccum
902c: c5 2c                        cmp     zpWParam                ;compare A, B low bytes
902e: f0 e7                        beq     Cmp_Equal               ;equal? -> A == B
9030: b0 d6                        bcs     Cmp_AGreaterThanB       ;A low byte > B low byte? -> A > B
9032: 4c 04 90                     jmp     Cmp_ALessThanB          ;else -> A < B

                   ; Compare two strings. Comments in this area refer to first operand as `a$`, 2nd
                   ; as `b$`. This does not actually refer to real variable names.
9035: a5 29        CompareStrings  lda     zpWAccum+1
9037: 85 19                        sta     zpMisc
9039: a5 2a                        lda     zpWAccum+2
903b: 85 1a                        sta     zpMisc+1
903d: a5 2d                        lda     zpWParam+1
903f: 85 1b                        sta     zpMisc+2
9041: a5 2e                        lda     zpWParam+2
9043: 85 1c                        sta     zpMisc+3
9045: a5 28                        lda     zpWAccum                ;str len 1st operand
9047: f0 28                        beq     CmpStr_LenAZero         ;len(a$) == 0? -> check len(b$)
9049: 85 4a                        sta     vZpStrLenA              ;store len(a$)
904b: f0 bb                        beq     Cmp_AGreaterThanB       ;NEVER branches - they forgot to compare len(b$) first!
904d: a5 2c                        lda     zpWParam                ;get len(b$) - should've come before instr above!
904f: 85 4b                        sta     vZpStrLenB              ;store it
9051: a0 00                        ldy     #$00
9053: b1 19        @strCmpLp       lda     (zpMisc),y              ;get char from a$
9055: d1 1b                        cmp     (zpMisc+2),y            ;cmp char from b$
9057: 90 ab                        bcc     Cmp_ALessThanB          ;a$[i] < b$[i]? -> a$ < b$
9059: f0 03                        beq     @charEq                 ;(this and next instr should've been single bne)
905b: 4c 08 90                     jmp     Cmp_AGreaterThanB       ;not less, not eq -> a$ > b$

905e: c8           @charEq         iny                             ;bump next char to check
905f: c6 4a                        dec     vZpStrLenA              ;decrement len(a$)
9061: f0 07                        beq     @lenAZero               ;len(a$) == 0? -> check len(b$)
9063: c6 4b                        dec     vZpStrLenB              ;decrement len(b$)
9065: f0 a1                        beq     Cmp_AGreaterThanB       ;len(b$) == 0? -> a$ > b$
9067: 4c 53 90                     jmp     @strCmpLp               ;neither are zero; loop back around to check next char

906a: c6 4b        @lenAZero       dec     vZpStrLenB              ;decrement len(b$)
906c: f0 a9                        beq     Cmp_Equal               ;zero, too? -> strs equal
906e: 4c 04 90                     jmp     Cmp_ALessThanB          ;;else -> a$ < b$

9071: a5 2c        CmpStr_LenAZero lda     zpWParam                ;str len 2nd operand
9073: f0 a2                        beq     Cmp_Equal               ;len(b$) == 0? -> they're both equal
9075: 4c 04 90                     jmp     Cmp_ALessThanB

                   ; stores A at zpTXTPTR, then increments zpTXTPTR.
                   ; If, after incrementing, zpTXTPTR has wraooed to next page, tehn it is
                   ; decremented to point at the last byte of preceding page, and null-terminated.
                   • Clear variables

                   TxtPtrStoreAndAdv
9078: a0 00                        ldy     #$00
907a: 91 09                        sta     (zpTXTPTR),y
907c: e6 09                        inc     zpTXTPTR
907e: f0 01                        beq     @overfl
9080: 60                           rts

9081: c6 09        @overfl         dec     zpTXTPTR
9083: a9 00                        lda     #$00
9085: 91 09                        sta     (zpTXTPTR),y
9087: 60                           rts

                   TxtPtrGetCharAndIncr
9088: a0 00                        ldy     #$00
908a: b1 09                        lda     (zpTXTPTR),y
908c: e6 09        TxtPtrIncr      inc     zpTXTPTR
908e: f0 03                        beq     @hi
9090: 29 ff                        and     #$ff
9092: 60                           rts

9093: e6 0a        @hi             inc     zpTXTPTR+1
9095: 29 ff                        and     #$ff
9097: 60                           rts

                   TxtPtrIncrAndGetChar
9098: a0 00                        ldy     #$00
909a: e6 09                        inc     zpTXTPTR
909c: f0 03                        beq     @hi
909e: b1 09                        lda     (zpTXTPTR),y
90a0: 60                           rts

90a1: e6 0a        @hi             inc     zpTXTPTR+1
90a3: b1 09                        lda     (zpTXTPTR),y
90a5: 60                           rts

90a6: a5 09        TxtPtrDecr      lda     zpTXTPTR                ;is low byte zero?
90a8: f0 03                        beq     @lowZero                ;yes -> decr both bytes
90aa: c6 09                        dec     zpTXTPTR                ;no: decr just low byte
90ac: 60                           rts                             ;and return

90ad: c6 09        @lowZero        dec     zpTXTPTR                ;decr lo
90af: c6 0a                        dec     zpTXTPTR+1              ;decr hi
90b1: 60                           rts                             ;-> return

                   ; Get the next program character (doesn't increment)
90b2: a0 00        GetTxtChar      ldy     #$00
90b4: b1 09                        lda     (zpTXTPTR),y
90b6: 60                           rts

                   ; dead/unreachable code
90b7: a0 00        SetTxtCharDEAD  ldy     #$00
90b9: 91 09                        sta     (zpTXTPTR),y
90bb: 60                           rts

                   TxtIncAndSkipSpaces
90bc: 20 8c 90                     jsr     TxtPtrIncr
90bf: a0 00        TxtSkipSpaces   ldy     #$00
90c1: b1 09                        lda     (zpTXTPTR),y
90c3: c9 20                        cmp     #$20
90c5: f0 f5                        beq     TxtIncAndSkipSpaces
90c7: 60                           rts

                   ; Clears carry if A is a digit, sets otherwise
90c8: c9 30        IsDigit         cmp     #‘0’
90ca: 90 06                        bcc     @secRts
90cc: c9 3a                        cmp     #‘:’                    ;'9' + 1
90ce: b0 02                        bcs     @secRts                 ;better: bcs $90D3
90d0: 18                           clc
90d1: 60                           rts

90d2: 38           @secRts         sec
90d3: 60           rts_90D3        rts

90d4: e6 46        @nxtDig         inc     zpTmp46
                   FindFirstNonzeroDigit
90d6: a6 46                        ldx     zpTmp46
90d8: b5 3d                        lda     zpNumStrTmp,x           ;did we get null terminator byte?
90da: f0 05                        beq     @null                   ;yes -> skipped too many, back up one so we have '0'
90dc: c9 30                        cmp     #$30                    ;no; do we have a '0' digit?
90de: f0 f4                        beq     @nxtDig                 ;yes -> move past it and check next
90e0: 60                           rts                             ;no: we found first non-zero digit then. Return.

90e1: c6 46        @null           dec     zpTmp46                 ;back up one so we have '0'
90e3: 60                           rts

                   ParsePosOrNegDecWFromTxt
90e4: 20 bf 90                     jsr     TxtSkipSpaces
90e7: c9 2d                        cmp     #‘-’                    ;Is cur char a '-'?
90e9: d0 42                        bne     ParsePosDecWFromTxt     ;no -> then it better be a string of digits. Parse them.
90eb: 20 8c 90                     jsr     TxtPtrIncr              ;yes, then advance TXTPTR
90ee: 20 e4 90                     jsr     ParsePosOrNegDecWFromTxt ;and call ourselves recursively to get a (possibly re-negated) number
90f1: 4c 32 8e                     jmp     WAccumNegate            ;negating afterward.

                   ; dead/unreachable code
90f4: 20 2d 91                     jsr     ParsePosDecWFromTxt
90f7: 20 f2 8d                     jsr     Cmp32768
90fa: f0 3f                        beq     errOverfl
90fc: 60                           rts

                   ; Parses a decimal number at zpTxtPtr into zpWAccum, leaving zpTXTPTR after the
                   ; parsed number.
                   ParseDecimalWordFromTxt
90fd: a9 02                        lda     #TYP_INT
90ff: 85 27                        sta     zpArgTyp
9101: a9 00                        lda     #$00
9103: 85 28                        sta     zpWAccum                ;start with zero in WAccum
9105: 85 29                        sta     zpWAccum+1
9107: a9 0a        @digitsLoop     lda     #$0a
9109: 85 2c                        sta     zpWParam
910b: a9 00                        lda     #$00
910d: 85 2d                        sta     zpWParam+1
910f: a0 00                        ldy     #$00
9111: b1 09                        lda     (zpTXTPTR),y
9113: 20 c8 90                     jsr     IsDigit
9116: b0 bb                        bcs     rts_90D3                ;not a digit? -> done
9118: 48                           pha                             ;push the digit
9119: 20 35 8f                     jsr     WordMultiplyAsWord      ;multiply WAccum by 10 (will be zero the first time!)
911c: 68                           pla                             ;pull digit
911d: 38                           sec
911e: e9 30                        sbc     #‘0’                    ;subtract value of '0' char, now 0-9
9120: 85 2c                        sta     zpWParam                ;store in WParam
9122: 20 f7 8e                     jsr     WordAdd                 ;and perform add
9125: b0 14                        bcs     errOverfl               ;overflow? -> error
9127: 20 8c 90                     jsr     TxtPtrIncr              ;advance to next digit
912a: 4c 07 91                     jmp     @digitsLoop             ;and loop around

                   ; Parse a decimal word at TXTPTR, and throw an overflow error if it overflowed
                   ; to negative. As a special exception, a value of 32,768 is allowed (so that
                   ; later code can check if it was preceded with '-').
                   ParsePosDecWFromTxt
912d: 20 fd 90                     jsr     ParseDecimalWordFromTxt
9130: a5 29                        lda     zpWAccum+1
9132: 30 01                        bmi     @ck32768
9134: 60                           rts

9135: 20 f2 8d     @ck32768        jsr     Cmp32768
9138: d0 01                        bne     errOverfl
913a: 60                           rts

913b: 4c f2 8e     errOverfl       jmp     ErrorOverflow

                   ; Converts a (POSITIVE) word (at WAccum) into a null-terminated string of five
                   ; decimal digits in ASCII, stored at zpNumStrTmp.
                   • Clear variables
                   vZpDigitIdx     .var    $46    {addr/1}         ;index into zpNumStrTmp to store next digit
                   vZpDigitChar    .var    $47    {addr/1}         ;character holding the digit value for current decimal place

                   PosDecStrFromWord
913e: a9 00                        lda     #$00
9140: 85 46                        sta     vZpDigitIdx
9142: a5 46        PosDecStrMidStr lda     vZpDigitIdx
9144: 48                           pha
9145: a9 10                        lda     #$10                    ;put 10,000 into zpWParam
9147: 85 2c                        sta     zpWParam
9149: a9 27                        lda     #$27
914b: 85 2d                        sta     zpWParam+1
914d: 20 8a 91                     jsr     DecStr_findPlaceValue   ;find digit for the ten-thousands place
9150: a9 e8                        lda     #$e8                    ;put 1,000 into zpWParam
9152: 85 2c                        sta     zpWParam
9154: a9 03                        lda     #$03
9156: 85 2d                        sta     zpWParam+1
9158: 20 8a 91                     jsr     DecStr_findPlaceValue   ;find digit for the thousands place
915b: a9 64                        lda     #$64                    ;put 100 into zpWParam
915d: 85 2c                        sta     zpWParam
915f: a9 00                        lda     #$00
9161: 85 2d                        sta     zpWParam+1
9163: 20 8a 91                     jsr     DecStr_findPlaceValue   ;find digit for the hundreds place
9166: a9 0a                        lda     #$0a                    ;put 10 into zpWParam
9168: 85 2c                        sta     zpWParam
916a: 20 8a 91                     jsr     DecStr_findPlaceValue   ;find digit for the tens place
916d: a5 28                        lda     zpWAccum
916f: 18                           clc
9170: 69 30                        adc     #$30
9172: 20 83 91                     jsr     DecStr_putAndAdv        ;finally, store the remaining ones digit
9175: a9 00                        lda     #$00
9177: 20 83 91                     jsr     DecStr_putAndAdv        ;and null-terminate the string
917a: 68                           pla
917b: 85 46                        sta     vZpDigitIdx             ;restore vZpDigitIdx to what it was at start (usually #$00)
917d: 60                           rts

917e: 20 f7 8e     DecStr_putDigit jsr     WordAdd                 ;we subtracted too far; add it back in
9181: a5 47                        lda     vZpDigitChar            ;put the digit, and advance to next
                   DecStr_putAndAdv
9183: a6 46                        ldx     vZpDigitIdx
9185: 95 3d                        sta     zpNumStrTmp,x
9187: e6 46                        inc     vZpDigitIdx
9189: 60                           rts

                   DecStr_findPlaceValue
918a: a9 30                        lda     #‘0’                    ;start with the character '0'
918c: 85 47                        sta     vZpDigitChar
918e: 20 d4 8f     @divLp          jsr     WordSubtractClone       ;did current place value "go into" WAccum? (division)
9191: 90 eb                        bcc     DecStr_putDigit         ;no -> put char and return for next place value
9193: e6 47                        inc     vZpDigitChar            ;yes: increment digit
9195: 4c 8e 91                     jmp     @divLp                  ;and see if it "goes into" WAccum again

                   ; Converts a (possibly negative) word (at WAccum) into a null-terminated string
                   ; of five decimal digits in ASCII, stored at zpNumStrTmp. Will be preceded
                   ; either by a space (positive), or '-' (negative)
9198: a9 00        DecStrFromWord  lda     #$00
919a: 85 46                        sta     vZpDigitIdx
919c: a0 20                        ldy     #$20                    ;SPACE char
919e: a5 29                        lda     zpWAccum+1              ;is WAccum positive (or zero)?
91a0: 10 05                        bpl     @pos                    ;yes -> keep prefix as SPACE char
91a2: 20 32 8e                     jsr     WAccumNegate            ;no: negate WAccum
91a5: a0 2d                        ldy     #‘-’                    ; and prefix with '-' instead
91a7: 98           @pos            tya
91a8: 48                           pha                             ;push the prefix
91a9: 20 42 91                     jsr     PosDecStrMidStr         ;convert the positive decimal
91ac: 20 d6 90                     jsr     FindFirstNonzeroDigit   ;advance to first non-'0' digit
91af: c6 46                        dec     vZpDigitIdx             ;then backup one from there
91b1: 68                           pla                             ;pull prefix char
91b2: 20 83 91                     jsr     DecStr_putAndAdv        ;and store it as the prefix
91b5: c6 46                        dec     vZpDigitIdx             ;then back up so it's at the start
91b7: 60                           rts

                   ; Converts a value from $0-$F to a hex digit in ASCII.
                   • Clear variables

91b8: c9 0a        HexFromNybble   cmp     #10                     ;A reg >= 10?
91ba: b0 03                        bcs     @needLet                ;yes -> need a hex letter
91bc: 69 30                        adc     #‘0’                    ;no: decimal digit is fine
91be: 60                           rts                             ;-> return

91bf: 18           @needLet        clc                             ;(convert to hex letter-digit)
91c0: 69 37                        adc     #$37                    ;#$0A + #$37 = #$41 ('A'); #$0F + #$37 = #$46 ('F')
91c2: 60                           rts                             ;-> return

                   ; Converts a byte into two hex digits in ASCII, storing them at $3D + x.
91c3: 48           HexFromByte     pha                             ;push the byte
91c4: 29 f0                        and     #$f0                    ;grab the high nybble, and shift it into low
91c6: 4a                           lsr     A
91c7: 4a                           lsr     A
91c8: 4a                           lsr     A
91c9: 4a                           lsr     A
91ca: 20 b8 91                     jsr     HexFromNybble           ;convert to hex
91cd: 95 3d                        sta     zpNumStrTmp,x           ;store in the tmp string
91cf: e8                           inx                             ;incr out-string index
91d0: 68                           pla                             ;pull original byte
91d1: 29 0f                        and     #$0f                    ;grab the low nybble
91d3: 20 b8 91                     jsr     HexFromNybble           ;convert to hex
91d6: 95 3d                        sta     zpNumStrTmp,x           ;store in string
91d8: e8                           inx                             ;incr out-string index to next char
91d9: 60                           rts                             ;-> return

                   ; Converts a word (at WAccum) into a null-terminated string of four hexadecimal
                   ; digits in ASCII, stored at zpNumStrTmp.
91da: a2 00        HexStrFromWord  ldx     #$00
91dc: 86 46                        stx     zpTmp46
91de: a5 29                        lda     zpWAccum+1
91e0: 20 c3 91                     jsr     HexFromByte             ;convert WAccum high byte, store in string
91e3: a5 28                        lda     zpWAccum
91e5: 20 c3 91                     jsr     HexFromByte             ;convert WAccum low byte
91e8: a9 00                        lda     #$00
91ea: 95 3d                        sta     zpNumStrTmp,x           ;add null terminating char to number string
91ec: 60           rts_91EC        rts                             ;-> return

                   ; Converts a hex string at zpTXTPTR, to a binary word in zpWAccum.
91ed: a9 02        WordFromHex     lda     #TYP_INT
91ef: 85 27                        sta     zpArgTyp                ;exprssion result type will be integer
91f1: a2 00                        ldx     #$00
91f3: a9 00                        lda     #$00
91f5: 85 28                        sta     zpWAccum                ;zero out WAccum
91f7: 85 29                        sta     zpWAccum+1
91f9: 20 b2 90     @chrLp          jsr     GetTxtChar              ;get hex string char
91fc: 38                           sec
91fd: e9 30                        sbc     #‘0’                    ;subtract value of ASCII code for '0'
91ff: 90 eb                        bcc     rts_91EC                ;char val was < '0'? -> return; not a hex str
9201: c9 0a                        cmp     #10
9203: 90 0b                        bcc     @decDig                 ;result < 10? -> is a decimal digit, handle that
9205: 38                           sec                             ;else, is a hex letter-digit (A-F)
9206: e9 07                        sbc     #$07                    ;subtract 7 more from ASCII code
9208: c9 0a                        cmp     #10                     ;result < 10?
920a: 90 e0                        bcc     rts_91EC                ;yes -> return; char val was not hex
920c: c9 10                        cmp     #16                     ;result >= 16?
920e: b0 dc                        bcs     rts_91EC                ;yes -> return; char val was not hex
9210: 48           @decDig         pha                             ;push the value to CPU stack
9211: 20 24 92                     jsr     @shiftResultLeft        ;multiply running result by 16
9214: 68                           pla                             ;pull translated hex digit's value
9215: 85 2c                        sta     zpWParam                ;set WParam to it
9217: a9 00                        lda     #$00
9219: 85 2d                        sta     zpWParam+1
921b: 20 f7 8e                     jsr     WordAdd                 ;add to running value in WAccum
921e: 20 8c 90                     jsr     TxtPtrIncr              ;advance to next char in string
9221: 4c f9 91                     jmp     @chrLp                  ;-> loop back to handle next character

                   @shiftResultLeft
9224: 8a                           txa
9225: 48                           pha                             ;push X reg to CPU stack
9226: a9 10                        lda     #$10
9228: 85 2c                        sta     zpWParam
922a: a9 00                        lda     #$00
922c: 85 2d                        sta     zpWParam+1
922e: 20 35 8f                     jsr     WordMultiplyAsWord      ;multiply running result by 16 (shifting existing digits over one base16 place)
9231: 68                           pla
9232: aa                           tax                             ;pull and restore X reg
9233: 60                           rts                             ;->return

                   TokAdvAndgetChar
9234: 20 40 92                     jsr     TokAdvance
9237: a0 00        TokGetChar      ldy     #$00
9239: b1 4d                        lda     (zpTokenizePtr),y
923b: 60                           rts

                   TokGetCharAndAdv
923c: a0 00                        ldy     #$00
923e: b1 4d                        lda     (zpTokenizePtr),y
9240: e6 4d        TokAdvance      inc     zpTokenizePtr
9242: d0 11                        bne     @flagsRts
9244: e6 4e                        inc     zpTokenizePtr+1
9246: d0 0d                        bne     @flagsRts
                   ; Store two NULs in a row at the end of tokenizeBuffer
9248: a9 00        TokBufTerminate lda     #$00
924a: 20 4d 92                     jsr     TokStoreCharAndAdv
                   TokStoreCharAndAdv
924d: a0 00                        ldy     #$00
924f: 91 4d                        sta     (zpTokenizePtr),y
9251: e6 4d                        inc     zpTokenizePtr
9253: f0 03                        beq     errOverfl_9258          ;exceeded our buffer? -> error
9255: 29 ff        @flagsRts       and     #$ff
9257: 60                           rts

9258: 4c f2 8e     errOverfl_9258  jmp     ErrorOverflow

                   ; Tokenize the line pointed at by zpTXTPTR ($09.$0A), to tokenizeBuffer ($300)
925b: a9 00        Tokenize        lda     #<tokenizeBuffer
925d: 85 4d                        sta     zpTokenizePtr
925f: a9 03                        lda     #>tokenizeBuffer
9261: 85 4e                        sta     zpTokenizePtr+1
9263: 20 1e 93     TokenizeMain    jsr     TokCpySpacesFromTxt
9266: 29 ff                        and     #$ff
9268: f0 de        tokMainNoCpySpc beq     TokBufTerminate         ;NUL? -> finish up
926a: c9 3f                        cmp     #‘?’                    ;shorthand for PRINT
926c: f0 41                        beq     @tokQMark
926e: c9 22                        cmp     #‘"’
9270: f0 46                        beq     TokStorAndCopyUntilQuo
9272: c9 27                        cmp     #‘'’                    ;shorthand for (non-buggy) REM
9274: f0 3f                        beq     TokStorAndCopyRest
9276: c9 2e                        cmp     #‘.’
9278: f0 0e                        beq     TokOther
927a: c9 26                        cmp     #‘&’
927c: f0 7b                        beq     TokenizeHexNum
927e: 20 c8 90                     jsr     IsDigit
9281: 90 4b                        bcc     TokenizeNumber
9283: 20 e6 93                     jsr     TokenizeKeyword
9286: 90 2a                        bcc     storeTokHndlArgs
                   ; We didn't recognize anything special.
                   ; Skip high-bit-set bytes, skip control chars, and store everything
                   ; else verbatim.
9288: 20 8c 90     TokOther        jsr     TxtPtrIncr
928b: c9 80                        cmp     #$80
928d: b0 d4                        bcs     TokenizeMain            ;skip high bytes without storing
928f: c9 20                        cmp     #$20
9291: 90 d0                        bcc     TokenizeMain            ;skip control chars w/o store
9293: c9 40                        cmp     #$40
9295: 90 12                        bcc     @storePunct
                   ; Copy presumed var name out to tokenizeBuffer, verbatim 
9297: 20 4d 92     @lp             jsr     TokStoreCharAndAdv
929a: a0 00                        ldy     #$00
929c: b1 09                        lda     (zpTXTPTR),y
929e: 20 bc a7                     jsr     IsAlNum
92a1: b0 c0                        bcs     TokenizeMain
92a3: 20 8c 90                     jsr     TxtPtrIncr
92a6: 4c 97 92                     jmp     @lp

92a9: 20 4d 92     @storePunct     jsr     TokStoreCharAndAdv
92ac: 4c 63 92                     jmp     TokenizeMain

92af: 4c 4c 93     @tokQMark       jmp     TokQMark

                   storeTokHndlArgs
92b2: 4c 53 93                     jmp     StoreTokenAndHandleArgs

                   TokStorAndCopyRest
92b5: a0 00                        ldy     #$00
92b7: ae                           ldx ▼   $22a0
                   TokStorAndCopyUntilQuo
92b8: a0 22                        ldy     #$22
92ba: 84 99                        sty     zpToken
                   TokStorAndCopyUntilTok
92bc: 20 4d 92                     jsr     TokStoreCharAndAdv
92bf: 20 8c 90                     jsr     TxtPtrIncr
92c2: 20 b2 90     TokCopyUntilTok jsr     GetTxtChar
92c5: f0 a1                        beq     tokMainNoCpySpc         ; -> TokBufTerminate
92c7: c5 99                        cmp     zpToken
92c9: d0 f1                        bne     TokStorAndCopyUntilTok
92cb: 4c 88 92                     jmp     TokOther

92ce: a9 12        TokenizeNumber  lda     #tok_NUM_AFTER
92d0: 48                           pha
92d1: 20 2d 91                     jsr     ParsePosDecWFromTxt
92d4: a5 29                        lda     zpWAccum+1              ;did num fit in a single byte?
92d6: d0 0d                        bne     tokNumWord              ;no -> process word
92d8: a5 28                        lda     zpWAccum
92da: c9 0a                        cmp     #$0a                    ;low byte > 9?
92dc: b0 07                        bcs     tokNumWord              ;yes -> process as word
92de: aa                           tax                             ;no: store as single byte
92df: 68                           pla                             ;pop the num tok, won't use it
92e0: e8                           inx                             ;inc so we can't get #$00
92e1: 8a                           txa
92e2: 4c 17 93                     jmp     tokStorAndCont

92e5: 20 f2 8d     tokNumWord      jsr     Cmp32768                ; = 32768?
92e8: f0 03                        beq     @is32768
92ea: 4c 0c 93                     jmp     @storeWordAndCont

92ed: a5 99        @is32768        lda     zpToken
92ef: c9 fa                        cmp     #tok_OP_MINUS           ;this num preceded by '-'?
92f1: d0 03                        bne     @errOverfl
92f3: 4c 0c 93                     jmp     @storeWordAndCont

92f6: 4c f2 8e     @errOverfl      jmp     ErrorOverflow

92f9: 20 8c 90     TokenizeHexNum  jsr     TxtPtrIncr              ;skip the '&'
92fc: 20 b2 90                     jsr     GetTxtChar
92ff: c9 48                        cmp     #‘H’                    ;got an (optional) 'H'?
9301: d0 03                        bne     @noH
9303: 20 8c 90                     jsr     TxtPtrIncr              ;yes -> skip it
9306: a9 11        @noH            lda     #tok_HEX_AFTER
9308: 48                           pha                             ;push hex-num prefix token
9309: 20 ed 91                     jsr     WordFromHex             ;convert hex string to binary number
                   @storeWordAndCont
930c: 68                           pla                             ;pull token byte
930d: 20 4d 92                     jsr     TokStoreCharAndAdv      ;store it in tokenized line
9310: a5 28                        lda     zpWAccum
9312: 20 4d 92                     jsr     TokStoreCharAndAdv      ;store low byte of converted num
9315: a5 29                        lda     zpWAccum+1
9317: 20 4d 92     tokStorAndCont  jsr     TokStoreCharAndAdv      ;and high byte
931a: 4c 63 92                     jmp     TokenizeMain            ;-> return to tokenize loop

931d: 60           rts_931D        rts

                   TokCpySpacesFromTxt
931e: 20 b2 90                     jsr     GetTxtChar
9321: c9 20                        cmp     #$20                    ;space?
9323: d0 f8                        bne     rts_931D                ;no -> rts
9325: 20 4d 92                     jsr     TokStoreCharAndAdv      ;yes: store and check next
9328: 20 8c 90                     jsr     TxtPtrIncr
932b: 4c 1e 93                     jmp     TokCpySpacesFromTxt

                   ; Entered when we just processed a REM keyword into a token.
                   ; Copies the rest of the line verbatim, to the tokenizeBuffer.
                   ; 
                   ; This has a BUG! If the katakana "small yo" (ョ) character appears anywhere
                   ; within the REM statement, then the verbatim copying _ends_, and BASIC returns
                   ; to trying to convert keywords into tokens, corrupting the REM statement's
                   ; contents. This happens because this character has the same value as the token
                   ; used for REM, and a typo means the copier is told to stop at the next REM
                   ; character, instead of at NUL (#$00).
                   ; 
                   ; Many Japanese words contain this character. I kind of can't believe searching
                   ; for this bug didn't come up with any results, as users back in the day must
                   ; have run across it. Maybe they didn't understand how the REM line was getting
                   ; corrupted, as it would only show up when you think to LIST it back out again.
                   ; Or maybe hardly anyone used REM directly, but used the single-quot (`)
                   ; instead.
                   ; 
                   ; Note that the single-quot (') shorthand for REM, handled separately at $9272,
                   ; does _not_ exhibit this bug.
932e: a0 00        TokRemCopyToEnd ldy     #$00                    ;BUG: this should be lda
9330: 85 99                        sta     zpToken                 ;...or else this should be sty
9332: 4c c2 92                     jmp     TokCopyUntilTok

                   TokenizeLineRanges
9335: 20 1e 93                     jsr     TokCpySpacesFromTxt
9338: 20 c8 90                     jsr     IsDigit
933b: 90 43                        bcc     TokenizeLineNumber
933d: c9 2c                        cmp     #‘,’
933f: f0 5c                        beq     tokAndNum
9341: c9 2d                        cmp     #‘-’
9343: f0 58                        beq     tokAndNum
9345: c9 2e                        cmp     #‘.’
9347: f0 54                        beq     tokAndNum
9349: 4c 63 92     tokenizeMain    jmp     TokenizeMain

934c: a9 8b        TokQMark        lda     #tok_PRINT
934e: 85 99                        sta     zpToken
9350: 20 8c 90                     jsr     TxtPtrIncr              ;weird that it falls through here. Exits eventually at $9376.
                   StoreTokenAndHandleArgs
9353: a5 99                        lda     zpToken
9355: 20 4d 92                     jsr     TokStoreCharAndAdv
9358: c9 95                        cmp     #tok_REM
935a: f0 d2                        beq     TokRemCopyToEnd
935c: c9 86                        cmp     #tok_LIST
935e: f0 d5                        beq     TokenizeLineRanges
9360: c9 b6                        cmp     #tok_DELETE
9362: f0 d1                        beq     TokenizeLineRanges
9364: c9 b5                        cmp     #tok_AUTO
9366: f0 cd                        beq     TokenizeLineRanges
9368: c9 b7                        cmp     #tok_RENUM
936a: f0 c9                        beq     TokenizeLineRanges
936c: c9 bd                        cmp     #tok_RESUME
936e: f0 08                        beq     @numOrCont
9370: c9 91                        cmp     #tok_DATA
9372: f0 3a                        beq     TokDataArgs
9374: c9 87                        cmp     #tok_SYSTEM             ;is it a command that takes a line number arg?
9376: b0 d1                        bcs     tokenizeMain            ;no -> return to normal tokenizing
9378: 20 1e 93     @numOrCont      jsr     TokCpySpacesFromTxt     ;yes -> parse line number if it's there
937b: 20 c8 90                     jsr     IsDigit
937e: b0 c9                        bcs     tokenizeMain
                   TokenizeLineNumber
9380: 20 fd 90                     jsr     ParseDecimalWordFromTxt
9383: a9 0b                        lda     #$0b
9385: 20 4d 92                     jsr     TokStoreCharAndAdv
9388: a5 28                        lda     zpWAccum
938a: 20 4d 92                     jsr     TokStoreCharAndAdv
938d: a5 29                        lda     zpWAccum+1
938f: 20 4d 92                     jsr     TokStoreCharAndAdv
9392: 20 1e 93                     jsr     TokCpySpacesFromTxt
9395: c9 2c        @delimAndNum    cmp     #‘,’
9397: f0 04                        beq     tokAndNum
9399: c9 2d                        cmp     #‘-’
939b: d0 ac                        bne     tokenizeMain
939d: 20 4d 92     tokAndNum       jsr     TokStoreCharAndAdv
93a0: 20 8c 90                     jsr     TxtPtrIncr
93a3: 20 1e 93                     jsr     TokCpySpacesFromTxt
93a6: 20 c8 90                     jsr     IsDigit
93a9: 90 d5                        bcc     TokenizeLineNumber
93ab: 4c 95 93                     jmp     @delimAndNum

93ae: 20 1e 93     TokDataArgs     jsr     TokCpySpacesFromTxt
93b1: c9 3a                        cmp     #‘:’
93b3: f0 94                        beq     tokenizeMain
93b5: 29 ff                        and     #$ff
93b7: f0 90        @txtEnd         beq     tokenizeMain
93b9: c9 22                        cmp     #‘"’
93bb: f0 09                        beq     @dquo
93bd: 20 4d 92     @dquoOut        jsr     TokStoreCharAndAdv
93c0: 20 8c 90                     jsr     TxtPtrIncr
93c3: 4c ae 93                     jmp     TokDataArgs

93c6: 20 4d 92     @dquo           jsr     TokStoreCharAndAdv
93c9: 20 8c 90                     jsr     TxtPtrIncr
93cc: 20 1e 93                     jsr     TokCpySpacesFromTxt
93cf: c9 22                        cmp     #‘"’
93d1: f0 ea                        beq     @dquoOut
93d3: 29 ff                        and     #$ff
93d5: f0 e0                        beq     @txtEnd
93d7: 4c c6 93                     jmp     @dquo

93da: a0 00        TokTblGetChar   ldy     #$00
93dc: b1 54                        lda     (zpTokTblScn),y
93de: 60           rts_93DE        rts

93df: e6 54        IncrTokTblScn   inc     zpTokTblScn
93e1: d0 fb                        bne     rts_93DE                ; -> rts
93e3: e6 55                        inc     zpTokTblScn+1
93e5: 60                           rts

                   ; Returns with carry clear if keyword matched; carry set otherwise
93e6: a9 ab        TokenizeKeyword lda     #<tbl_KeywordTokens
93e8: 85 54                        sta     zpTokTblScn
93ea: a9 cc                        lda     #>tbl_KeywordTokens
93ec: 85 55                        sta     zpTokTblScn+1
93ee: 20 da 93     @nxtKwd         jsr     TokTblGetChar
93f1: aa                           tax
93f2: e8                           inx                             ;did we get #$ff (end-of-table byte)?
93f3: f0 36                        beq     kwdNotFnd               ;yes -> return false (load txt char, set carry, rts)
93f5: 85 99                        sta     zpToken
93f7: 20 df 93                     jsr     IncrTokTblScn
93fa: 20 30 94                     jsr     SaveTXTPTR
                   ; Loop over keyword from tbl_KeywordTokens, and word at zpTXTPTR, to see if
                   ; they're a match
93fd: 20 b2 90     @cmpKwd         jsr     GetTxtChar
9400: 85 98                        sta     zpSavedTxtChar
9402: 20 da 93                     jsr     TokTblGetChar
9405: c5 98                        cmp     zpSavedTxtChar
9407: d0 0d                        bne     @ckForAbbrv
9409: 20 8c 90                     jsr     TxtPtrIncr              ;chars matched, incr and check next
940c: 20 df 93                     jsr     IncrTokTblScn
940f: 20 da 93                     jsr     TokTblGetChar
9412: 10 e9                        bpl     @cmpKwd
9414: 18           @retTrue        clc                             ;reached token byte of next kwd, so keyword matched!
9415: 60                           rts                             ;return true (zpToken has the token)

9416: 20 88 90     @ckForAbbrv     jsr     TxtPtrGetCharAndIncr
9419: c9 2e                        cmp     #‘.’                    ;did user abbreviate the keyword?
941b: f0 f7                        beq     @retTrue                ;yes -> we matched, return true
941d: 20 39 94                     jsr     RestoreTXTPTR
9420: 20 da 93     @skipToNextKwd  jsr     TokTblGetChar
9423: 30 c9                        bmi     @nxtKwd
9425: 20 df 93                     jsr     IncrTokTblScn
9428: 4c 20 94                     jmp     @skipToNextKwd

942b: 20 b2 90     kwdNotFnd       jsr     GetTxtChar
942e: 38                           sec
942f: 60                           rts

9430: a5 09        SaveTXTPTR      lda     zpTXTPTR
9432: 85 9a                        sta     zpSavedTXTPTR
9434: a5 0a                        lda     zpTXTPTR+1
9436: 85 9b                        sta     zpSavedTXTPTR+1
9438: 60                           rts

9439: a5 9a        RestoreTXTPTR   lda     zpSavedTXTPTR
943b: 85 09                        sta     zpTXTPTR
943d: a5 9b                        lda     zpSavedTXTPTR+1
943f: 85 0a                        sta     zpTXTPTR+1
9441: 60                           rts

                   ; Detokenizes from zpTokenizePtr to lineBuffer (via zpTXTPTR)
                   ; Caller must take care to save/restore previous value of zpTXTPTR
9442: 20 a4 8b     DetokenizeLine  jsr     InitLineBufferScanning  ;point zpTXTPTR at start of lineBuffer
                   DetokenizeLineCont
9445: 20 37 92                     jsr     TokGetChar              ;get the char pointed at by zpTokenizePtr
9448: f0 17                        beq     Detok_finish            ;null terminator? -> copy that to zpTXTPTR and return
944a: c9 20                        cmp     #$20                    ;possible leading byte for number token?
944c: 90 18                        bcc     DetokNumber             ;yes -> translate it
944e: c9 80                        cmp     #$80                    ;non-token, low-value character?
9450: 90 5c                        bcc     Detok_checkHandleDQuot  ;yes -> handle possible situations for that
9452: 48                           pha                             ;no, must be keyword token. Push token byte
9453: 20 de 94                     jsr     Detok_keyword           ;expand the token to a keyword
9456: 20 40 92                     jsr     TokAdvance
9459: 68                           pla
945a: c9 95                        cmp     #tok_REM                ;was it the token for REM?
945c: f0 72                        beq     Detok_copyRemaining     ;yes -> copy all remaining chars verbatim
945e: 4c 45 94                     jmp     DetokenizeLineCont      ;no -> continue detokenizing

9461: a9 00        Detok_finish    lda     #$00                    ;unnecessary: we only arrive here if A is already #$00
9463: 4c 78 90                     jmp     TxtPtrStoreAndAdv

9466: c9 0b        DetokNumber     cmp     #11                     ;token >= 11?
9468: b0 0e                        bcs     DetokNumberWord         ;yes -> not a single-byte token
946a: 85 28                        sta     zpWAccum                ;no: single-byte token; translate to decimal 0-9
946c: c6 28                        dec     zpWAccum                ;decrement byte to get real value
946e: a9 00                        lda     #$00
9470: 85 29                        sta     zpWAccum+1              ;expand byte to word
9472: 20 40 92                     jsr     TokAdvance              ;advance past our single-byte number token
9475: 4c 9b 94                     jmp     DetokDecimalNum         ;and detokenize it

9478: 48           DetokNumberWord pha                             ;push token byte
9479: 20 34 92                     jsr     TokAdvAndgetChar        ;move number contents into WAccum, and move zpTokenizePtr past them
947c: 85 28                        sta     zpWAccum
947e: 20 34 92                     jsr     TokAdvAndgetChar
9481: 85 29                        sta     zpWAccum+1
9483: 20 40 92                     jsr     TokAdvance
9486: 68                           pla                             ;pull token byte
9487: c9 11                        cmp     #tok_HEX_AFTER          ;is it for a hex number?
9489: d0 10                        bne     DetokDecimalNum         ;no -> detokenize as decimal
948b: a9 26                        lda     #‘&’                    ;yes: store "&H"
948d: 20 78 90                     jsr     TxtPtrStoreAndAdv
9490: a9 48                        lda     #‘H’
9492: 20 78 90                     jsr     TxtPtrStoreAndAdv
9495: 20 da 91                     jsr     HexStrFromWord          ;and then output the number as hex
9498: 4c 9e 94                     jmp     CopyNumToTxtPtr

949b: 20 3e 91     DetokDecimalNum jsr     PosDecStrFromWord       ;get decimal for word at WAccum
949e: 20 d6 90     CopyNumToTxtPtr jsr     FindFirstNonzeroDigit   ;skip to first non-zero digit
94a1: a6 46                        ldx     zpTmp46
94a3: b5 3d        @cpyNumLp       lda     zpNumStrTmp,x           ;get digit
94a5: f0 9e                        beq     DetokenizeLineCont      ;end of number? ->
94a7: 20 78 90                     jsr     TxtPtrStoreAndAdv       ;copy out to zpTXTPTR and advance to next
94aa: e8                           inx
94ab: 4c a3 94                     jmp     @cpyNumLp               ;loop around to copy next digit

                   Detok_checkHandleDQuot
94ae: c9 22                        cmp     #‘"’                    ;double-quote char?
94b0: d0 17                        bne     Detok_checkHandleApos   ;no -> maybe it's an apostrophe
94b2: 20 d8 94     @cpy            jsr     Detok_copyOneChar       ;yes, is quote. Copy until we see another one
94b5: 20 37 92                     jsr     TokGetChar              ;get char
94b8: c9 22                        cmp     #$22                    ;is terminating double-quote?
94ba: f0 07                        beq     Detok_copyCharContinue  ;yes -> copy it and continue normal detokenizing
94bc: 29 ff                        and     #$ff                    ;terminating null '\0'?
94be: d0 f2                        bne     @cpy                    ;no -> loop around, keep copying
94c0: 4c 45 94                     jmp     DetokenizeLineCont      ;yes -> finish up detok

                   Detok_copyCharContinue
94c3: 20 d8 94                     jsr     Detok_copyOneChar
94c6: 4c 45 94                     jmp     DetokenizeLineCont

                   Detok_checkHandleApos
94c9: c9 27                        cmp     #‘'’                    ;apostrophe (REM shorthand)?
94cb: d0 f6                        bne     Detok_copyCharContinue  ;no -> copy the single character and continue
94cd: 20 d8 94     @cpyLp          jsr     Detok_copyOneChar       ;copy chars until '\0'
                   Detok_copyRemaining
94d0: 20 37 92                     jsr     TokGetChar
94d3: d0 f8                        bne     @cpyLp
94d5: 4c 45 94                     jmp     DetokenizeLineCont      ;finish out detok

                   Detok_copyOneChar
94d8: 20 3c 92                     jsr     TokGetCharAndAdv
94db: 4c 78 90                     jmp     TxtPtrStoreAndAdv

94de: 85 99        Detok_keyword   sta     zpToken
94e0: a9 ab                        lda     #<tbl_KeywordTokens     ;set zpTokTblScan to the start of the tokens table
94e2: 85 54                        sta     zpTokTblScn
94e4: a9 cc                        lda     #>tbl_KeywordTokens
94e6: 85 55                        sta     zpTokTblScn+1
94e8: 20 da 93                     jsr     TokTblGetChar           ;get char from table
94eb: c9 ff        @chkToken       cmp     #$ff                    ;did we reach end of tokens table?
94ed: f0 20                        beq     rts_950F                ;don't replace token with anything
94ef: c5 99                        cmp     zpToken                 ;is this the token we're looking for?
94f1: f0 0b                        beq     Detok_copyKeyword       ;yes -> copy the keyword out
94f3: 20 df 93     @notOurKwd      jsr     IncrTokTblScn           ;no, not our keyword
94f6: 20 da 93                     jsr     TokTblGetChar           ;skip chars until next token byte
94f9: 10 f8                        bpl     @notOurKwd
94fb: 4c eb 94                     jmp     @chkToken               ;got token byte -> check if it's ours

                   Detok_copyKeyword
94fe: 20 df 93                     jsr     IncrTokTblScn           ;move past token byte
9501: 20 da 93     @cpyKwd         jsr     TokTblGetChar           ;get a keyword character
9504: 30 09                        bmi     rts_950F                ;reached next token byte? -> done
9506: 20 78 90                     jsr     TxtPtrStoreAndAdv       ;copy out to zpTXTPTR
9509: 20 df 93                     jsr     IncrTokTblScn           ;move to next keyword character
950c: 4c 01 95                     jmp     @cpyKwd                 ;loop until keyword has been copied

950f: 60           rts_950F        rts

9510: 20 6e a2     CmdFn_ERROR     jsr     EvalByteInteger
9513: 4c 37 b2                     jmp     PrintErrorAndStop

                   ErrorResumeWithoutErrHandl
9516: a9 13                        lda     #err_RE
9518: 4c 37 b2                     jmp     PrintErrorAndStop

951b: a9 00        ErrorNoResume   lda     #EH_NO_HANDLER
951d: 85 e7                        sta     zpErrHandStat
951f: a9 12                        lda     #err_NR
9521: 4c 37 b2                     jmp     PrintErrorAndStop

9524: 4c 91 84     errSyntax_9524  jmp     ErrorSyntax

9527: 08           CmdFn_RESUME    php                             ;push CPU flags
9528: a6 e7                        ldx     zpErrHandStat           ;get err handler status
952a: f0 ea                        beq     ErrorResumeWithoutErrHandl ;no error handler installed? -> error about that
952c: e0 01                        cpx     #EH_HANDLER_DEFINED     ;handler installed, but not called?
952e: f0 eb                        beq     ErrorNoResume           ;yes -> error about that
9530: 28                           plp                             ;else, pull flags. Did we get an arg?
9531: f0 20                        beq     RESUME_restoreContext   ;no -> restore and exit
9533: c9 0b                        cmp     #tok_LNUM_AFTER         ;else, is arg a line num?
9535: f0 12                        beq     RESUME_goToLineArg      ;yes -> handle
9537: c9 8d                        cmp     #tok_NEXT               ;is it the NEXT token?
9539: d0 e9                        bne     errSyntax_9524          ;no -> no supported args given: ERROR
953b: 20 53 95                     jsr     RESUME_restoreContext   ;yes: restore context (and return to next instr)
953e: 20 9c 84     @skipCmd        jsr     IsEndOfCmd              ;now skip the original offending command (skip to the NEXT one)
9541: f0 2c                        beq     rts_956F                ;no -> done
9543: 20 f1 9e                     jsr     SkipToken               ;skip the arg, whatever it is
9546: 4c 3e 95                     jmp     @skipCmd

                   RESUME_goToLineArg
9549: 20 f0 96                     jsr     FindLineFromArg         ;we're exiting the handler, so go back to "installed" state
954c: a9 01                        lda     #EH_HANDLER_DEFINED
954e: 85 e7                        sta     zpErrHandStat           ;and go to the asked-for line
9550: 4c c9 96                     jmp     GoTo

                   RESUME_restoreContext
9553: a9 01                        lda     #EH_HANDLER_DEFINED     ;we're exiting the handler, so go back to "installed" state
9555: 85 e7                        sta     zpErrHandStat
9557: a5 f6                        lda     zpRsmTxtThisCmd
9559: 85 09                        sta     zpTXTPTR
955b: a5 f7                        lda     zpRsmTxtThisCmd+1
955d: 85 0a                        sta     zpTXTPTR+1
955f: a5 f8                        lda     zpRsmRunTxtNxtL
9561: 85 0d                        sta     zpRunTxtNxtLnk
9563: a5 f9                        lda     zpRsmRunTxtNxtL+1
9565: 85 0e                        sta     zpRunTxtNxtLnk+1
9567: a5 fa                        lda     zpRsmRunLineNum
9569: 85 0b                        sta     zpRunLineNum
956b: a5 fb                        lda     zpRsmRunLineNum+1
956d: 85 0c                        sta     zpRunLineNum+1
956f: 60           rts_956F        rts

                   restoreTxtPtr_9570
9570: 4c 39 94                     jmp     RestoreTXTPTR

9573: 20 7d a2     CmdFn_FIND      jsr     EvalString              ;evaluate string argument (to expr parse stack)
9576: 20 30 94                     jsr     SaveTXTPTR              ;save away current TXTPTR
9579: a5 05                        lda     zpTXTTAB
957b: 85 09                        sta     zpTXTPTR                ;reset TXTPTR to start of program
957d: a5 06                        lda     zpTXTTAB+1
957f: 85 0a                        sta     zpTXTPTR+1
9581: 20 b2 90     FIND_linesLoop  jsr     GetTxtChar              ;are we at program end?
9584: f0 ea                        beq     restoreTxtPtr_9570      ;restore original TXTPTR and exit
9586: 18                           clc
9587: a5 09                        lda     zpTXTPTR
9589: 69 03                        adc     #$03                    ;set zpTokenizePtr to TXTPTR + 3 (skipping next-line offset, line num bytes)
958b: 85 4d                        sta     zpTokenizePtr
958d: a9 00                        lda     #$00
958f: 65 0a                        adc     zpTXTPTR+1
9591: 85 4e                        sta     zpTokenizePtr+1
9593: a5 09                        lda     zpTXTPTR
9595: 48                           pha                             ;push value of TXTPTR to CPU stack
9596: a5 0a                        lda     zpTXTPTR+1
9598: 48                           pha
9599: 20 42 94                     jsr     DetokenizeLine          ;detokenize this line into lineBuffer
959c: 68                           pla                             ; (wouldn't it be more efficient to tokenize FIND contents and search for that instead?)
959d: 85 0a                        sta     zpTXTPTR+1              ;pull TXTPTR back off stack (detokenize probably overwrote)
959f: 68                           pla
95a0: 85 09                        sta     zpTXTPTR
95a2: ad 00 04                     lda     exprParseStk            ;put FIND arg string info into WParam
95a5: 85 2c                        sta     zpWParam
95a7: ad 01 04                     lda     exprParseStk+1
95aa: 85 2d                        sta     zpWParam+1
95ac: ad 02 04                     lda     exprParseStk+2
95af: 85 2e                        sta     zpWParam+2
95b1: a9 00                        lda     #<lineBuffer            ;put detokenized current line info into WAccum+1
95b3: 85 29                        sta     zpWAccum+1
95b5: a9 05                        lda     #>lineBuffer
95b7: 85 2a                        sta     zpWAccum+2
95b9: a0 00                        ldy     #$00                    ;init Y reg
95bb: b1 29        @countLp        lda     (zpWAccum+1),y          ;determine length of detokenized line,
95bd: f0 03                        beq     @lineEnd                ; by counting chars in Y reg, until we reach a terminating null
95bf: c8                           iny                             ;increment Y
95c0: d0 f9                        bne     @countLp                ;(always) -> loop, keep counting chars
95c2: 84 28        @lineEnd        sty     zpWAccum                ;save Y-reg (character count) as strlen for WAccum
95c4: 20 e6 95                     jsr     CheckInStr              ;check whether user str is contained within detokenized line
95c7: f0 09                        beq     @nextLine               ;no match? -> advance and check next line
95c9: 20 ee 87                     jsr     PrintLineNumAndDetok    ;match; print the line # and (re-)detokenized contents
95cc: 20 c4 87                     jsr     PauseOnEsc              ;pause if Esc is being pressed
95cf: 4c 81 95                     jmp     FIND_linesLoop          ;-> check for matches in next line

95d2: 20 b2 90     @nextLine       jsr     GetTxtChar              ;get offset to next line of BASIC
95d5: 18                           clc
95d6: 65 09                        adc     zpTXTPTR                ;add to TXTPTR (point it at next line)
95d8: 85 09                        sta     zpTXTPTR
95da: a9 00                        lda     #$00
95dc: 65 0a                        adc     zpTXTPTR+1
95de: 85 0a                        sta     zpTXTPTR+1
95e0: 4c 81 95                     jmp     FIND_linesLoop          ;-> and loop back around to check for matches

                   CIS_matchFailedRetZero
95e3: a9 00                        lda     #$00
95e5: 60                           rts

                   ; Checks if the string identified by WParam is contained within the string
                   ; identified by WAccum. Returns the index to the first occurrance, if found, or
                   ; zero if not found.
                   vZpHayStkRemn   .var    $19    {addr/1}
                   vZpNeedleRemn   .var    $1b    {addr/1}         ;how many chars left to haystack we can index
                   vZpHayStkIdx    .var    $6b    {addr/1}
                   vZpNeedleIdx    .var    $6c    {addr/1}

95e6: a9 00        CheckInStr      lda     #$00
95e8: 85 6b                        sta     vZpHayStkIdx            ;initialize indices into haystack and needle strings
95ea: 85 6c                        sta     vZpNeedleIdx
95ec: a5 28                        lda     zpWAccum                ;get haystack string length
95ee: 85 19                        sta     vZpHayStkRemn           ;save as how many characters left in haystack, to match
95f0: c5 2c                        cmp     zpWParam                ;are there fewer chars remaining to haystack than are in needle?
95f2: 90 ef                        bcc     CIS_matchFailedRetZero  ;yes -> search failed, match impossible now
                   CIS_tryNextHayStkChar
95f4: a5 2c                        lda     zpWParam                ;get needle string length
95f6: 85 1b                        sta     vZpNeedleRemn           ;save as how many chars left in needle, to compare
95f8: a5 6b                        lda     vZpHayStkIdx            ;get current index into needle
95fa: 48                           pha                             ; and push to CPU stack
95fb: a4 6b        @matchLp        ldy     vZpHayStkIdx
95fd: b1 29                        lda     (zpWAccum+1),y          ;get char from haystack
95ff: a4 6c                        ldy     vZpNeedleIdx
9601: d1 2d                        cmp     (zpWParam+1),y          ;does it match char in needle?
9603: d0 10                        bne     @restartNextHayStkChar  ;no -> reset search at next haystack char
9605: c6 1b                        dec     vZpNeedleRemn           ;decrement # chars remaining in check in needle
9607: f0 07                        beq     @matchSucc              ;out of needle chars? -> return the index!
9609: e6 6b                        inc     vZpHayStkIdx            ;increment the character indices
960b: e6 6c                        inc     vZpNeedleIdx
960d: 4c fb 95                     jmp     @matchLp                ;loop back around to check next needle/haystack chars

9610: 68           @matchSucc      pla                             ;pull index to start of match within haystack
9611: 18                           clc
9612: 69 01                        adc     #$01                    ;add one to it (cvt from 0-based to 1-based indexing)
9614: 60                           rts                             ;return

                   @restartNextHayStkChar
9615: 68                           pla                             ;pull (aborted) start of match in haystack
9616: 85 6b                        sta     vZpHayStkIdx            ;set as haystack index again
9618: e6 6b                        inc     vZpHayStkIdx            ;and increment to start searching again at next char
961a: a9 00                        lda     #$00
961c: 85 6c                        sta     vZpNeedleIdx            ;reset needle index to zero
961e: c6 19                        dec     vZpHayStkRemn           ;one less haystack char remaining to be checked
9620: f0 c1                        beq     CIS_matchFailedRetZero  ; == 0? -> no more matches possible; indicate match failure
9622: 4c f4 95                     jmp     CIS_tryNextHayStkChar   ;else, restart matching at the new haystack position

                   ; Turn keyclicks on or off. Pulls some funny tricks to reuse the same ON/OFF
                   ; check logic as CmdFn_TR (just below)
                   • Clear variables
                   vZpAmClick      .var    $74    {addr/1}         ;flag to indicate whether we're handling "CLICK ON/OFF" or "TR ON/OFF"

9625: a0 ff        CmdFn_CLICK     ldy     #$ff                    ;indicate we're CLICK
9627: 85 74                        sta     vZpAmClick
9629: 30 04                        bmi     OnOff                   ;check arg for ON/OFF

                   ; For TRON, TROFF commands. Pulls some funny tricks to let CmdFn_CLICK (above)
                   ; reuse the same ON/OFF check logic
962b: a0 00        CmdFn_TR        ldy     #$00                    ;indicate we're TR--
962d: 84 74                        sty     vZpAmClick
962f: c9 9a        OnOff           cmp     #tok_ON                 ;check arg for ON/OFF
9631: f0 07                        beq     @on                     ;ON? -> load #$FF
9633: c9 9b                        cmp     #tok_OFF                ;OFF?
9635: d0 6c                        bne     errSyn_96A3             ;neither ON nor OFF -> error
9637: a9 00                        lda     #$00                    ;OFF. Load #$00
9639: 2c                           bit ▼   $ffa9                   ;skip next instr
963a: a9 ff        @on             lda     #$ff
963c: a4 74                        ldy     vZpAmClick              ;are we TR--?
963e: f0 03                        beq     @tr                     ;yes -> store in the tr flag
9640: 85 e3                        sta     zpKeyClickFlg           ;otherwise, store in the key-click flag
9642: 2c                           bit ▼   __                      ;skip next instr
9643: 85 97        @tr             sta     zpTraceOnFlag
9645: 4c 8c 90                     jmp     TxtPtrIncr

                   • Clear variables

9648: aa           CmdFn_GOSUB     tax                             ;move acc value to X-reg for a bit
9649: 68                           pla
964a: 85 5f                        sta     zpSavedRts              ;pull return address and save it
964c: 68                           pla
964d: 85 60                        sta     zpSavedRts+1
964f: 8a                           txa
9650: 20 f0 96                     jsr     FindLineFromArg         ;find the line number for the argument
9653: 20 56 9b                     jsr     SkipLineNumArgs         ;and skip TXTPTR past it
9656: a5 09                        lda     zpTXTPTR
9658: 48                           pha                             ;push this TXTPTR addr
9659: a5 0a                        lda     zpTXTPTR+1
965b: 48                           pha
965c: a5 0b                        lda     zpRunLineNum
965e: 48                           pha                             ;push the current line number
965f: a5 0c                        lda     zpRunLineNum+1
9661: 48                           pha
9662: a5 0d                        lda     zpRunTxtNxtLnk
9664: 48                           pha                             ;push the link to the following line
9665: a5 0e                        lda     zpRunTxtNxtLnk+1
9667: 48                           pha
9668: a9 00                        lda     #>GOSUB_STK_MARKER
966a: 48                           pha                             ;push special #$FF00 marker indicating a GOSUB
966b: a9 ff                        lda     #GOSUB_STK_MARKER
966d: 48                           pha
966e: a5 60                        lda     zpSavedRts+1
9670: 48                           pha                             ;now re-push our return address, so the return still works
9671: a5 5f                        lda     zpSavedRts
9673: 48                           pha
9674: 4c c9 96                     jmp     GoTo                    ;and jump to the line in argument

                   ErrorReturnWithoutGosub
9677: a9 02                        lda     #err_RG
9679: 4c 37 b2                     jmp     PrintErrorAndStop

967c: 68           RETURN_noArg    pla                             ;pull up our return address and save aside
967d: 85 5f                        sta     zpSavedRts
967f: 68                           pla
9680: 85 60                        sta     zpSavedRts+1
9682: 68                           pla                             ;pull up next byte
9683: c9 ff                        cmp     #GOSUB_STK_MARKER       ;does it match our marker byte?
9685: d0 f0                        bne     ErrorReturnWithoutGosub ;no -> error. Most recent stack-y thing wasn't GOSUB.
9687: 68                           pla                             ;next marker byte match?
9688: d0 ed                        bne     ErrorReturnWithoutGosub ;no -> error
968a: 68                           pla                             ;yes: we can RETURN
968b: 85 0e                        sta     zpRunTxtNxtLnk+1        ;pull up link to next BASIC line, at time of GOSUB
968d: 68                           pla                             ; and set it
968e: 85 0d                        sta     zpRunTxtNxtLnk
9690: 68                           pla                             ;pull up line number we GOSUB'd from
9691: 85 0c                        sta     zpRunLineNum+1          ;and set that
9693: 68                           pla
9694: 85 0b                        sta     zpRunLineNum
9696: 68                           pla                             ;pull BASIC program text location, and set
9697: 85 0a                        sta     zpTXTPTR+1
9699: 68                           pla
969a: 85 09                        sta     zpTXTPTR
                   RestoreReturnAddrAndRts
969c: a5 60                        lda     zpSavedRts+1
969e: 48                           pha                             ;finally, plop this routine's return address back down
969f: a5 5f                        lda     zpSavedRts
96a1: 48                           pha
96a2: 60                           rts                             ;and return to it

96a3: 4c 91 84     errSyn_96A3     jmp     ErrorSyntax

96a6: f0 d4        CmdFn_RETURN    beq     RETURN_noArg            ;no args? -> normal return
96a8: 68                           pla                             ;have a linenum arg. Pull stack items, but don't restore
96a9: 85 5f                        sta     zpSavedRts              ;first, pull up our own return addr
96ab: 68                           pla
96ac: 85 60                        sta     zpSavedRts+1
96ae: 68                           pla
96af: c9 ff                        cmp     #$ff                    ;did we get the GOSUB mark?
96b1: d0 c4                        bne     ErrorReturnWithoutGosub ;no -> error
96b3: 68                           pla                             ;2nd byte of GOSUB mark?
96b4: d0 c1                        bne     ErrorReturnWithoutGosub ;no -> error
96b6: ba                           tsx
96b7: 8a                           txa
96b8: 18                           clc
96b9: 69 06                        adc     #$06
96bb: aa                           tax
96bc: 9a                           txs                             ;pop 6 state items off the stack, ignoring them
96bd: a5 60                        lda     zpSavedRts+1
96bf: 48                           pha                             ;plop our return addr back down
96c0: a5 5f                        lda     zpSavedRts
96c2: 48                           pha
96c3: 20 9c 84                     jsr     IsEndOfCmd              ;and fall through to GOTO.
96c6: 20 f0 96     CmdFn_GOTO      jsr     FindLineFromArg
96c9: a0 00        GoTo            ldy     #$00
96cb: b1 5b                        lda     (zpTxtLink),y           ;get offset to next line
96cd: 18                           clc
96ce: 65 5b                        adc     zpTxtLink               ;add to current line ptr
96d0: 85 0d                        sta     zpRunTxtNxtLnk
96d2: a9 00                        lda     #$00
96d4: 65 5c                        adc     zpTxtLink+1
96d6: 85 0e                        sta     zpRunTxtNxtLnk+1        ;and store in zpRunTxtNxtLnk
96d8: c8                           iny
96d9: b1 5b                        lda     (zpTxtLink),y           ;save line number as current
96db: 85 0b                        sta     zpRunLineNum
96dd: c8                           iny
96de: b1 5b                        lda     (zpTxtLink),y
96e0: 85 0c                        sta     zpRunLineNum+1
96e2: 18                           clc
96e3: a9 03                        lda     #$03                    ;add 3 to start of the line (past offset, line num), and set that as TXTPTR
96e5: 65 5b                        adc     zpTxtLink
96e7: 85 09                        sta     zpTXTPTR
96e9: a9 00                        lda     #$00
96eb: 65 5c                        adc     zpTxtLink+1
96ed: 85 0a                        sta     zpTXTPTR+1
96ef: 60                           rts                             ;get to executin'!

                   ; Verifies that token at TXTPTR is a line number, and parses it.
                   ; Does not consume it (leaves TXTPTR where it is).
96f0: c9 0b        FindLineFromArg cmp     #tok_LNUM_AFTER         ;is arg designated as line number?
96f2: d0 af                        bne     errSyn_96A3             ;no -> error
96f4: a0 01                        ldy     #$01
96f6: b1 09                        lda     (zpTXTPTR),y            ;set zpFindLNum = arg
96f8: 85 44                        sta     zpFindLNum
96fa: c8                           iny
96fb: b1 09                        lda     (zpTXTPTR),y
96fd: 85 45                        sta     zpFindLNum+1
96ff: 20 40 85                     jsr     FindLineNum             ;and find the indicated line
9702: d0 01                        bne     ErrorUndefLineNum       ;error if it doesn't exist
9704: 60           rts_9704        rts

                   ErrorUndefLineNum
9705: a9 07                        lda     #err_UL
9707: 4c 37 b2                     jmp     PrintErrorAndStop

970a: 20 83 a2     CmdFn_CALL      jsr     FrmEval                 ;evaluate argument
970d: 6c 00 04                     jmp     (exprParseStk)          ;then, just jump thru the zp ptr to the expr parse stack!

9710: 4c 94 84     errIll_9710     jmp     ErrorIllegalValue

9713: 4c 20 bc     readChar_9713   jmp     ReadChar

                   vZpPauseCtr     .var    $74    {addr/1}

9716: f0 fb        CmdFn_PAUSE     beq     readChar_9713           ;no arg? -> just pause until char press
9718: a9 00                        lda     #$00
971a: 85 74                        sta     vZpPauseCtr
971c: 20 83 a2                     jsr     FrmEval                 ;eval integer argument
971f: 20 0d aa                     jsr     ErrorUnlessInteger
9722: ad 01 04                     lda     exprParseStk+1
9725: 30 e9                        bmi     errIll_9710             ;negative number? -> error
9727: d0 05                        bne     @notZero                ;do pause
9729: ad 00 04                     lda     exprParseStk            ;zero. Is low byte also zero?
972c: f0 d6                        beq     rts_9704                ;yes -> no pause then
972e: ee 01 04     @notZero        inc     exprParseStk+1          ;incr high byte
9731: 20 55 88     @pauseLp        jsr     StopIfStopKey           ;check for and handle STOP key during PAUSE
9734: c6 74                        dec     vZpPauseCtr
9736: d0 f9                        bne     @pauseLp                ;loop around STOP check 256 times
9738: ce 00 04                     dec     exprParseStk            ;decrement low byte user arg,
973b: d0 f4                        bne     @pauseLp                ;loop for another 256
973d: ad 01 04                     lda     exprParseStk+1          ;low byte zero
9740: f0 05                        beq     @rts                    ;(NEVER) all zero -> exit PAUSE
9742: ce 01 04                     dec     exprParseStk+1          ;high byte not zero, decrement and do 65k more iterations
9745: d0 ea                        bne     @pauseLp                ; unless dec went to zero, in which case return
9747: 60           @rts            rts

                   • Clear variables

9748: 20 6e a2     CmdFn_LOCATE    jsr     EvalByteInteger         ;eval first arg
974b: c9 1c                        cmp     #COLS                   ; >= num cols?
974d: b0 c1                        bcs     errIll_9710             ;yes -> too big
974f: 85 6b                        sta     zpSavedCH               ;save
9751: 20 6b a2                     jsr     EvalCommaThenByteInt    ;comma, then second arg
9754: c9 18                        cmp     #ROWS                   ; >= num rows
9756: b0 b8                        bcs     errIll_9710             ;yes -> too big
9758: 85 6c                        sta     zpSavedCV               ;save
975a: 4c 5f b6                     jmp     RestoreCursorPos        ;"restore" saved pos to actual cursor pos

975d: 20 83 a2     CmdFn_POKE      jsr     FrmEval                 ;eval arg (addr)
9760: ad 00 04                     lda     exprParseStk
9763: 85 25                        sta     zpPokeAddr              ;and save it to zero page
9765: ad 01 04                     lda     exprParseStk+1
9768: 85 26                        sta     zpPokeAddr+1
976a: a0 00                        ldy     #$00
976c: 84 46                        sty     zpTmp46                 ;init write index to 0
976e: 20 6b a2     @wrLoop         jsr     EvalCommaThenByteInt    ;comma, then write val arg
9771: a4 46                        ldy     zpTmp46
9773: 91 25                        sta     (zpPokeAddr),y          ;write val to requested addr
9775: e6 46                        inc     zpTmp46                 ;increment addr to next byte
9777: 20 9c 84                     jsr     IsEndOfCmd              ;another arg?
977a: d0 f2                        bne     @wrLoop                 ;yes -> write arg to next byte
977c: 60                           rts                             ;no: done

977d: 68           IF_noEval       pla                             ;pull up return addr from CPU stack
977e: 68                           pla
977f: 4c 69 84                     jmp     ExecSkipToNextLine      ;and skip to next line (skips colon stmts too)

9782: 20 83 a2     CmdFn_IF        jsr     FrmEval                 ;evaluate condition expression
9785: ad 00 04     __              lda     exprParseStk
9788: 0d 01 04                     ora     exprParseStk+1
978b: f0 f0                        beq     IF_noEval               ;cond == zero? -> don't evaluate THEN
978d: 20 9c 84                     jsr     IsEndOfCmd              ;does the cmd end after the condition?
9790: f0 61                        beq     errSyn_97F3             ;yes -> syntax error
9792: c9 85                        cmp     #tok_THEN               ;got a THEN token?
9794: f0 04                        beq     @gotThen                ;yes -> handle
9796: 60                           rts

9797: 4c c6 96     goto_9797       jmp     CmdFn_GOTO

979a: 20 99 84     @gotThen        jsr     AdvThenIsEndOfCmd       ;after the THEN, does the IF cmd end?
979d: f0 be                        beq     CmdFn_POKE              ;yes -> a very roundabout way to a syntax error...
979f: c9 0b                        cmp     #$0b                    ;else, did we get a line number?
97a1: f0 f4                        beq     goto_9797               ;yes -> GOTO it
97a3: 60                           rts                             ;else, return and execute following command (same as no THEN)

97a4: d0 25        CmdFn_CLEAR     bne     ClearAndSet             ;got arg? -> handle
97a6: a5 08        Clear           lda     zpTXTEND+1              ;set zpVARTAB = zpTXTEND + 1
97a8: 85 1e                        sta     zpVARTAB+1
97aa: a6 07                        ldx     zpTXTEND
97ac: e8                           inx
97ad: d0 02                        bne     @vtNoHi
97af: e6 1e                        inc     zpVARTAB+1
97b1: 86 1d        @vtNoHi         stx     zpVARTAB
97b3: a5 1e                        lda     zpVARTAB+1              ;set zpVAREND = zpVARTAB + 1
97b5: 85 20                        sta     zpVAREND+1
97b7: a6 1d                        ldx     zpVARTAB
97b9: e8                           inx
97ba: d0 02                        bne     @veNoHi
97bc: e6 20                        inc     zpVAREND+1
97be: 86 1f        @veNoHi         stx     zpVAREND
97c0: a0 00                        ldy     #$00
97c2: 98                           tya
97c3: 91 1d                        sta     (zpVARTAB),y            ;set bytes at both those locations to 0
97c5: c8                           iny
97c6: 91 1d                        sta     (zpVARTAB),y
97c8: 4c e3 9e                     jmp     RestoreFromStart

97cb: 20 a6 97     ClearAndSet     jsr     Clear                   ;do the normal CLEAR stuff first, then handle arg
97ce: 20 83 a2                     jsr     FrmEval                 ;got back from main CLEAR. Evaluate the arg.
97d1: 20 0d aa                     jsr     ErrorUnlessInteger      ;make sure it's an int
97d4: ad 01 04                     lda     exprParseStk+1
97d7: c9 70                        cmp     #$70                    ;arg >= #$7000?
97d9: b0 1b                        bcs     errIllVal_97F6          ;yes -> error. That's past available RAM.
97db: c5 20                        cmp     zpVAREND+1              ;arg < zpVAREND? (in the high bytes)
97dd: 90 17                        bcc     errIllVal_97F6          ;yes -> error. We're already using more space than that.
97df: d0 07                        bne     @veNotEq                ;arg > zpVAREND? -> skip next check
97e1: ad 00 04                     lda     exprParseStk            ;high bytes were exactly equal
97e4: c5 1f                        cmp     zpVAREND                ;check low bytes
97e6: 90 0e                        bcc     errIllVal_97F6          ;arg < zpVAREND? -> error. Below already-used space
97e8: ad 00 04     @veNotEq        lda     exprParseStk            ;we're good: set the end of BASIC "Free space" to the arg.
97eb: 85 03                        sta     zpFRETOP
97ed: ad 01 04                     lda     exprParseStk+1
97f0: 85 04                        sta     zpFRETOP+1
97f2: 60                           rts

97f3: 4c 91 84     errSyn_97F3     jmp     ErrorSyntax

97f6: 4c 94 84     errIllVal_97F6  jmp     ErrorIllegalValue

                   errTypeMism_97F9
97f9: 4c ff a9                     jmp     ErrorTypeMismatch

                   vZpForIdxVarPtr .var    $7c    {addr/2}         ;pointer to the value of the index variable
                   vZpForLineNum   .var    $86    {addr/2}
                   vZpForSvTXTPTR  .var    $88    {addr/2}

97fc: 68           CmdFn_FOR       pla
97fd: 85 5f                        sta     zpSavedRts
97ff: 68                           pla
9800: 85 60                        sta     zpSavedRts+1            ;pull up return address, save it aside
9802: 20 bf 90                     jsr     TxtSkipSpaces
9805: 20 76 a5                     jsr     EvalVarNameOrSynErr     ;evaluate index var
9808: a5 90                        lda     zpVarType
980a: c9 02                        cmp     #TYP_INT                ;index var is int?
980c: d0 eb                        bne     errTypeMism_97F9        ;no -> error! type mismatch
980e: a5 8c                        lda     zpVarData
9810: 85 7c                        sta     vZpForIdxVarPtr         ;save pointer to index var data
9812: 48                           pha                             ;and push it (two bytes) onto CPU stack
9813: a5 8d                        lda     zpVarData+1
9815: 85 7d                        sta     vZpForIdxVarPtr+1
9817: 48                           pha
9818: 20 d9 86                     jsr     SkipEqualsOrErr         ;skip '=' that follows index var
981b: 20 83 a2                     jsr     FrmEval                 ;evaluate initial value for index (BUG: not type checked!)
981e: 68                           pla                             ;pull index var data poiner from stack, restore it
981f: 85 8d                        sta     zpVarData+1
9821: 68                           pla
9822: 85 8c                        sta     zpVarData
9824: a0 00                        ldy     #$00
9826: ad 00 04                     lda     exprParseStk            ;copy initializer value to index var
9829: 91 8c                        sta     (zpVarData),y
982b: ad 01 04                     lda     exprParseStk+1
982e: c8                           iny
982f: 91 8c                        sta     (zpVarData),y
9831: 20 bf 90                     jsr     TxtSkipSpaces
9834: c9 88                        cmp     #tok_TO                 ;do we have TO token next?
9836: d0 bb                        bne     errSyn_97F3             ;no -> syntax error
9838: 20 8c 90                     jsr     TxtPtrIncr
983b: 20 83 a2                     jsr     FrmEval                 ;evaluate limiting expression (BUG: not type checked!)
983e: ad 00 04                     lda     exprParseStk
9841: 85 80                        sta     zpForLimitVal           ;save the limiting value
9843: ad 01 04                     lda     exprParseStk+1
9846: 85 81                        sta     zpForLimitVal+1
9848: a9 01                        lda     #$01                    ;store default STEP value (1) at expr stack top
984a: 8d 00 04                     sta     exprParseStk
984d: a9 00                        lda     #$00
984f: 8d 01 04                     sta     exprParseStk+1
9852: 20 9c 84                     jsr     IsEndOfCmd              ;end of FOR command?
9855: f0 0a                        beq     @noStep                 ;yes -> stick with default STEP value
9857: c9 89                        cmp     #tok_STEP               ;got a STEP token?
9859: d0 98                        bne     errSyn_97F3             ;no -> syntax error
985b: 20 8c 90                     jsr     TxtPtrIncr
985e: 20 83 a2                     jsr     FrmEval                 ;evaluate STEP value
9861: ad 00 04     @noStep         lda     exprParseStk
9864: 85 82                        sta     zpForStepVal            ;save STEP value (whether given or default)
9866: ad 01 04                     lda     exprParseStk+1
9869: 85 83                        sta     zpForStepVal+1
986b: a5 09                        lda     zpTXTPTR                ;save TXTPTR for pushing to stack
986d: 85 88                        sta     vZpForSvTXTPTR
986f: a5 0a                        lda     zpTXTPTR+1
9871: 85 89                        sta     vZpForSvTXTPTR+1
9873: a5 0d                        lda     zpRunTxtNxtLnk
9875: 48                           pha                             ;push link to next line of BASIC (2 bytes)
9876: a5 0e                        lda     zpRunTxtNxtLnk+1
9878: 48                           pha
9879: a5 0b                        lda     zpRunLineNum            ;push current line number (2 bytes)
987b: 48                           pha
987c: a5 0c                        lda     zpRunLineNum+1
987e: 48                           pha
987f: 4c df 98                     jmp     FOR_NEXT_PushStuffAndRts ;push more context, restore return value, and return to it

                   ErrorNextWithoutFor
9882: a9 00                        lda     #err_NF
9884: 4c 37 b2                     jmp     PrintErrorAndStop

9887: 68           CmdFn_NEXT      pla                             ;pull up caller routine's return address and save it aside
9888: 85 5f                        sta     zpSavedRts
988a: 68                           pla
988b: 85 60                        sta     zpSavedRts+1
988d: 68                           pla                             ;pull up next byte
988e: c9 fd                        cmp     #FOR_STK_MARKER         ;is it a FOR marker byte?
9890: d0 f0                        bne     ErrorNextWithoutFor     ;no -> error
9892: 68                           pla                             ;pull up second marker byte
9893: d0 ed                        bne     ErrorNextWithoutFor     ;not match? -> error
9895: 68                           pla
9896: 85 89                        sta     vZpForSvTXTPTR+1        ;pull up TXTPTR value for just-after the FOR statement
9898: 68                           pla
9899: 85 88                        sta     vZpForSvTXTPTR
989b: 68                           pla
989c: 85 83                        sta     zpForStepVal+1          ;pull up STEP increment value
989e: 68                           pla
989f: 85 82                        sta     zpForStepVal
98a1: 68                           pla
98a2: 85 81                        sta     zpForLimitVal+1         ;pull up FOR limit expression value
98a4: 68                           pla
98a5: 85 80                        sta     zpForLimitVal
98a7: 68                           pla
98a8: 85 7d                        sta     vZpForIdxVarPtr+1       ;pull up pointer to the index var's value
98aa: 68                           pla
98ab: 85 7c                        sta     vZpForIdxVarPtr
98ad: 68                           pla
98ae: 85 87                        sta     vZpForLineNum+1         ;pull line number
98b0: 68                           pla
98b1: 85 86                        sta     vZpForLineNum
98b3: 68                           pla
98b4: 85 85                        sta     zpForNxtLnLnk+1         ;pull link to the next line of BASIC after FOR statement
98b6: 68                           pla
98b7: 85 84                        sta     zpForNxtLnLnk
98b9: a0 00                        ldy     #$00
98bb: b1 7c                        lda     (vZpForIdxVarPtr),y     ;copy index var's value into WAccum
98bd: 85 28                        sta     zpWAccum
98bf: c8                           iny
98c0: b1 7c                        lda     (vZpForIdxVarPtr),y
98c2: 85 29                        sta     zpWAccum+1
98c4: 20 04 99                     jsr     NEXT_doStep
98c7: f0 02                        beq     @eq                     ;limit expr less than index var?
98c9: 90 36                        bcc     restoreRtnAddrAndRts_9901 ;yes -> FOR loop is done!
98cb: a5 84        @eq             lda     zpForNxtLnLnk           ;push link to line of BASIC after current FOR statement
98cd: 48                           pha
98ce: 85 0d                        sta     zpRunTxtNxtLnk          ;and set it as current next line
98d0: a5 85                        lda     zpForNxtLnLnk+1
98d2: 48                           pha
98d3: 85 0e                        sta     zpRunTxtNxtLnk+1
98d5: a5 86                        lda     vZpForLineNum           ;push line number of FOR statement
98d7: 48                           pha
98d8: 85 0b                        sta     zpRunLineNum            ;and set as current line number
98da: a5 87                        lda     vZpForLineNum+1
98dc: 48                           pha
98dd: 85 0c                        sta     zpRunLineNum+1
                   FOR_NEXT_PushStuffAndRts
98df: a5 7c                        lda     vZpForIdxVarPtr         ;push the pointer to the contents of the index variable (2 bytes)
98e1: 48                           pha
98e2: a5 7d                        lda     vZpForIdxVarPtr+1
98e4: 48                           pha
98e5: a5 80                        lda     zpForLimitVal           ;push the FOR limitnig value (2 bytes)
98e7: 48                           pha
98e8: a5 81                        lda     zpForLimitVal+1
98ea: 48                           pha
98eb: a5 82                        lda     zpForStepVal            ;push the STEP increment value (2 bytes)
98ed: 48                           pha
98ee: a5 83                        lda     zpForStepVal+1
98f0: 48                           pha
98f1: a5 88                        lda     vZpForSvTXTPTR          ;push the TXTPTR value for just-after the FOR statement (2 bytes)
98f3: 48                           pha
98f4: 85 09                        sta     zpTXTPTR                ;and also restore it as current TXTPTR value
98f6: a5 89                        lda     vZpForSvTXTPTR+1
98f8: 48                           pha
98f9: 85 0a                        sta     zpTXTPTR+1
98fb: a9 00                        lda     #>FOR_STK_MARKER        ;push the FOR marker bytes (2)
98fd: 48                           pha
98fe: a9 fd                        lda     #FOR_STK_MARKER
9900: 48                           pha
                   restoreRtnAddrAndRts_9901
9901: 4c 9c 96                     jmp     RestoreReturnAddrAndRts ;-> now restore the saved return address, and return to it

9904: a5 83        NEXT_doStep     lda     zpForStepVal+1          ;is the STEP value negative?
9906: 30 0c                        bmi     @negStep                ;yes -> handle negative STEP (no reversal)
9908: 20 1a 99                     jsr     @doStep                 ;evaluate STEP
990b: 20 ee 8f                     jsr     Compare                 ;compare updated index var to limit
990e: b0 02                        bcs     @clcRts                 ;reverse the carry bit/result of comparison
9910: 38                           sec
9911: 60                           rts                             ;and return

9912: 18           @clcRts         clc
9913: 60                           rts

9914: 20 1a 99     @negStep        jsr     @doStep                 ;advance index var by STEP val
9917: 4c ee 8f                     jmp     Compare                 ;-> compare new index var to limit value, and return

991a: a5 82        @doStep         lda     zpForStepVal            ;copy step value to WParam
991c: 85 2c                        sta     zpWParam
991e: a5 83                        lda     zpForStepVal+1
9920: 85 2d                        sta     zpWParam+1
9922: 20 a8 8e                     jsr     SetIntAndAdd            ;add step to index var
9925: a0 00                        ldy     #$00
9927: a5 28                        lda     zpWAccum
9929: 91 7c                        sta     (vZpForIdxVarPtr),y     ;set that as new value for index var (I += STEP)
992b: c8                           iny
992c: a5 29                        lda     zpWAccum+1
992e: 91 7c                        sta     (vZpForIdxVarPtr),y
9930: a5 80                        lda     zpForLimitVal           ;load limit value into WParam (for Compare)
9932: 85 2c                        sta     zpWParam
9934: a5 81                        lda     zpForLimitVal+1
9936: 85 2d                        sta     zpWParam+1
9938: 60           rts_9938        rts                             ;-> return (Compare will be next)

9939: 4c 91 84     errSyntasx_9939 jmp     ErrorSyntax

                   • Clear variables
                   vZpStrLen       .var    $1b    {addr/1}
                   vZpSkipChars    .var    $52    {addr/1}
                   vZpStartCV      .var    $7e    {addr/1}
                   vZpStartCH      .var    $80    {addr/1}

993c: f0 fb        CmdFn_INPUT     beq     errSyntasx_9939         ;end of cmd? -> error
993e: c9 22                        cmp     #‘"’                    ;got a double-quote?
9940: d0 0b                        bne     @prQMark                ;no -> skip handling it then
9942: 20 51 9a                     jsr     INPUT_printStr          ;else, print the string
9945: c9 2c                        cmp     #‘,’                    ;got following comma?
9947: f0 09                        beq     @skipQMark              ;yes -> skip printing question mark
9949: c9 3b                        cmp     #‘;’                    ;else got semicolon?
994b: d0 ec                        bne     errSyntasx_9939         ;no -> error
994d: a9 3f        @prQMark        lda     #‘?’
994f: 20 75 b5                     jsr     PrintOneChar            ;print '?'
9952: a5 49        @skipQMark      lda     zpCV                    ;save current cursor position at start of input
9954: 85 7e                        sta     vZpStartCV
9956: a5 48                        lda     zpCH
9958: 85 80                        sta     vZpStartCH
995a: 20 6c bd                     jsr     INPUT_readLine          ;read the input line from user
995d: 20 55 88                     jsr     StopIfStopKey           ;honor stop key if it's pressed
9960: a9 00                        lda     #$00
9962: 85 52                        sta     vZpSkipChars
9964: a5 7e        @adjForScrlLp   lda     vZpStartCV
9966: c5 47                        cmp     zpLineStartRow          ;does our notion of starting row match the input buffer routine's?
9968: f0 0b                        beq     @atStartRow             ;yes -> only have to deal with the one line
996a: c6 7e                        dec     vZpStartCV              ;no: screen scrolled since we started typing
996c: 18                           clc
996d: a9 1c                        lda     #COLS
996f: 65 52                        adc     vZpSkipChars            ;vZpSkipChars += COLS
9971: 85 52                        sta     vZpSkipChars            ; (for each line we scrolled)
9973: d0 ef                        bne     @adjForScrlLp           ;(always) -> check if we scrolled another line, too
9975: a5 80        @atStartRow     lda     vZpStartCH
9977: 18                           clc
9978: 65 52                        adc     vZpSkipChars
997a: 85 52                        sta     vZpSkipChars            ;also skip the number of chars up until
997c: 20 bf 90     INPUT_getVar    jsr     TxtSkipSpaces           ; the column input started at.
997f: 20 76 a5                     jsr     EvalVarNameOrSynErr     ;find (or create) the variable named next (after optional string literal)
9982: 20 30 94                     jsr     SaveTXTPTR              ;save current TXTPTR
9985: a5 52                        lda     vZpSkipChars
9987: 85 09                        sta     zpTXTPTR                ;skip the determined # of chars at start of lineBuffer
9989: a9 05                        lda     #>lineBuffer
998b: 85 0a                        sta     zpTXTPTR+1              ;and point TXTPTR at it
998d: a5 90                        lda     zpVarType               ;get the type of the variable we parsed
998f: 29 0f                        and     #$0f                    ;(basic type, if array)
9991: c9 03                        cmp     #TYP_STR                ;string type?
9993: f0 40                        beq     INPUT_stringVar         ;yes -> handle string
9995: 20 2d ab                     jsr     READ_parseNumber        ;no, parse a number
9998: a5 09                        lda     zpTXTPTR                ;save current TXTPTR
999a: 85 52                        sta     vZpSkipChars            ; so it gets restored when we loop back around
999c: a0 00                        ldy     #$00
999e: a5 28                        lda     zpWAccum
99a0: 91 8c                        sta     (zpVarData),y           ;save parsed number into variable
99a2: c8                           iny
99a3: a5 29                        lda     zpWAccum+1
99a5: 91 8c                        sta     (zpVarData),y
                   INPUT_checkNextArg
99a7: 20 39 94                     jsr     RestoreTXTPTR           ;restore TXTPTR to point at INPUT args again
99aa: 20 bf 90                     jsr     TxtSkipSpaces
99ad: c9 2c                        cmp     #‘,’                    ;got a comma?
99af: d0 87                        bne     rts_9938                ;no -> done with INPUT
99b1: 20 8c 90                     jsr     TxtPtrIncr              ; (doesn't check if there's leftover data in the string!)
99b4: 20 30 94                     jsr     SaveTXTPTR              ;yes. Save TXTPTR to just-past the comma
99b7: a5 52                        lda     vZpSkipChars            ;point TXTPTR back into the input string again
99b9: 85 09                        sta     zpTXTPTR
99bb: a9 05                        lda     #>lineBuffer
99bd: 85 0a                        sta     zpTXTPTR+1
99bf: a0 00                        ldy     #$00
99c1: b1 09                        lda     (zpTXTPTR),y
99c3: c9 2c                        cmp     #‘,’                    ;is next char of string a comma?
99c5: d0 08                        bne     @restoreAndPrQMark      ;no -> print question mark and return to evaluating BASIC
99c7: e6 52                        inc     vZpSkipChars            ;move past the string's comma
99c9: 20 39 94                     jsr     RestoreTXTPTR           ;return TXTPTR to BASIC so we can parse var name
99cc: 4c 7c 99                     jmp     INPUT_getVar            ;-> and loop back around to assign stuff to that next var

                   @restoreAndPrQMark
99cf: 20 39 94                     jsr     RestoreTXTPTR           ;restore TXTPTR to point into BASIC program again
99d2: 4c 4d 99                     jmp     @prQMark                ;print question mark, and return

99d5: 20 bf 90     INPUT_stringVar jsr     TxtSkipSpaces           ;skip leading spaces in input string
99d8: a9 00                        lda     #$00
99da: 85 1b                        sta     vZpStrLen               ;initialize str len for str var arg
99dc: a8                           tay                             ;init Y reg
99dd: aa                           tax                             ;init X reg
99de: b1 09        @cpyGenPBufLp   lda     (zpTXTPTR),y            ;read a char from input string
99e0: f0 10                        beq     @cpyStrVar              ;end of string? -> copy to var
99e2: c9 2c                        cmp     #‘,’
99e4: f0 0c                        beq     @cpyStrVar              ;comma? -> also copy out var
99e6: 9d 00 07                     sta     genPurposeBuf,x         ;else store in genPurposeBuf
99e9: e6 1b                        inc     vZpStrLen               ;advance string length
99eb: e8                           inx                             ;advance to next char in input str
99ec: 20 8c 90                     jsr     TxtPtrIncr              ;and in genPurposeBuf
99ef: 4c de 99                     jmp     @cpyGenPBufLp           ;loop for next character in input line

99f2: a5 1b        @cpyStrVar      lda     vZpStrLen               ;set copied string in genPurposeBuf
99f4: 8d 00 04                     sta     exprParseStk            ; as top of expr stack
99f7: a9 00                        lda     #<genPurposeBuf
99f9: 8d 01 04                     sta     exprParseStk+1
99fc: a9 07                        lda     #>genPurposeBuf
99fe: 8d 02 04                     sta     exprParseStk+2
9a01: 20 cb 83                     jsr     CpyExprStrToStrVar      ;...and then copy the string on stack, into the INPUT var
9a04: a5 09                        lda     zpTXTPTR
9a06: 85 52                        sta     vZpSkipChars            ;move input string past string we processed
9a08: 4c a7 99                     jmp     INPUT_checkNextArg      ;-> see if there are more INPUT args

                   • Clear variables

9a0b: 4c 91 84     errSyntax_9A0B  jmp     ErrorSyntax

9a0e: 20 76 a5     CmdFn_SWAP      jsr     EvalVarNameOrSynErr     ;evaluate variable name
9a11: a5 90                        lda     zpVarType
9a13: 29 0f                        and     #$0f                    ;push base type of var
9a15: 48                           pha
9a16: a5 8c                        lda     zpVarData               ;push pointer to variable data
9a18: 48                           pha
9a19: a5 8d                        lda     zpVarData+1
9a1b: 48                           pha
9a1c: 20 14 ac                     jsr     SkipCommaOrSynErr       ;comma comes next
9a1f: 20 bf 90                     jsr     TxtSkipSpaces
9a22: 20 76 a5                     jsr     EvalVarNameOrSynErr     ;parse second var name
9a25: 68                           pla
9a26: 85 1a                        sta     zpMisc+1                ;save first var's data ptr to zpMisc
9a28: 68                           pla
9a29: 85 19                        sta     zpMisc
9a2b: a5 90                        lda     zpVarType
9a2d: 29 0f                        and     #$0f
9a2f: 85 90                        sta     zpVarType               ;strip off any "array" bit on 2nd var's type
9a31: 68                           pla                             ;pull first var type
9a32: c5 90                        cmp     zpVarType               ;compare to 2nd. Match?
9a34: d0 18                        bne     errTypeMismatch_9A4E    ;no -> error
9a36: aa                           tax
9a37: c9 02                        cmp     #TYP_INT                ;int? use "2" as size of data to swap
9a39: f0 02                        beq     @int
9a3b: a2 21                        ldx     #33                     ;else use 33 as str data length
9a3d: a0 00        @int            ldy     #$00
9a3f: b1 8c        @cpyLp          lda     (zpVarData),y           ;get a byte from var 2
9a41: 48                           pha                             ;push
9a42: b1 19                        lda     (zpMisc),y              ;get a byte from var 1
9a44: 91 8c                        sta     (zpVarData),y           ;copy to var 2
9a46: 68                           pla                             ;pull byte from var 1
9a47: 91 19                        sta     (zpMisc),y              ;write to var 2
9a49: c8                           iny                             ;next byte
9a4a: ca                           dex                             ;done copying?
9a4b: d0 f2                        bne     @cpyLp                  ;no -> copy next byte
9a4d: 60                           rts                             ;yes. Done!

                   errTypeMismatch_9A4E
9a4e: 4c ff a9                     jmp     ErrorTypeMismatch

9a51: 20 8c 90     INPUT_printStr  jsr     TxtPtrIncr
9a54: a0 00                        ldy     #$00
9a56: b1 09                        lda     (zpTXTPTR),y            ;get char
9a58: f0 b1                        beq     errSyntax_9A0B          ;terminating null? -> missing var after, so syntax err
9a5a: c9 22                        cmp     #‘"’                    ;got terminating double-quote?
9a5c: f0 06                        beq     @strDone                ;yes -> done
9a5e: 20 75 b5                     jsr     PrintOneChar            ;else, print the char
9a61: 4c 51 9a                     jmp     INPUT_printStr          ;and loop around for next char in str

9a64: 20 99 84     @strDone        jsr     AdvThenIsEndOfCmd       ;is cmd end?
9a67: f0 a2                        beq     errSyntax_9A0B          ;yes -> missing var after, so syntax err
9a69: 4c 8c 90                     jmp     TxtPtrIncr              ;-> move past closing dquot, and return to INPUT/LINPUT

9a6c: f0 9d        CmdFn_LINPUT    beq     errSyntax_9A0B          ;cmd end? -> syntax error
9a6e: c9 22                        cmp     #‘"’                    ;got a double-quote?
9a70: d0 0b        @noStr          bne     @noStrOrDone
9a72: 20 51 9a                     jsr     INPUT_printStr
9a75: c9 2c                        cmp     #‘,’                    ;require following ',' or ';' (and then var)
9a77: f0 04                        beq     @noStrOrDone
9a79: c9 3b                        cmp     #‘;’
9a7b: d0 8e                        bne     errSyntax_9A0B
9a7d: 20 04 bd     @noStrOrDone    jsr     ReadLine                ;read the line (into lineBuffer)
9a80: 20 55 88                     jsr     StopIfStopKey           ;check for and honor STOP key occurrance during input
9a83: 20 bf 90                     jsr     TxtSkipSpaces
9a86: 20 76 a5                     jsr     EvalVarNameOrSynErr     ;process the following string var name
9a89: a5 90                        lda     zpVarType               ;get base type of var
9a8b: 29 0f                        and     #$0f
9a8d: c9 03                        cmp     #TYP_STR                ;is it string?
9a8f: d0 bd                        bne     errTypeMismatch_9A4E    ;no -> error
9a91: a2 00                        ldx     #$00
9a93: a0 00                        ldy     #$00
9a95: bd 00 05                     lda     lineBuffer,x            ;get first char of input line
9a98: c9 20                        cmp     #$20                    ;space char?
9a9a: d0 07                        bne     @countLen               ;no -> 
9a9c: e8                           inx                             ;yes, space
9a9d: bd 00 05                     lda     lineBuffer,x            ;just a single space is the whole string?
9aa0: f0 0e                        beq     @cpyVar                 ;yes -> copy empty str to var
9aa2: ca                           dex                             ;no -> move back to include space char in copying
9aa3: bd 00 05     @countLen       lda     lineBuffer,x            ;get char from input line
9aa6: f0 08                        beq     @cpyVar                 ;end of line/str? -> len is in Y reg; copy to var
9aa8: 9d 00 07                     sta     genPurposeBuf,x         ;copy into genPurposeBuf (but never use??)
9aab: e8                           inx
9aac: c8                           iny
9aad: 4c a3 9a                     jmp     @countLen               ;-> loop for next char in input line

9ab0: 8c 00 04     @cpyVar         sty     exprParseStk            ;set Y as str len
9ab3: a9 00                        lda     #<lineBuffer
9ab5: 8d 01 04                     sta     exprParseStk+1          ;point str-to-copy at lineBuffer
9ab8: a9 05                        lda     #>lineBuffer
9aba: 8d 02 04                     sta     exprParseStk+2
9abd: 4c cb 83                     jmp     CpyExprStrToStrVar      ;-> perform the copy

                   ; Not actually a CmdFn. This is just CmdFn_ON followed by an ERROR token.
9ac0: 20 99 84     CmdFn_ON_ERROR  jsr     AdvThenIsEndOfCmd
9ac3: c9 80                        cmp     #$80                    ;followed by GOTO token?
9ac5: d0 33                        bne     errSyntax_9AFA          ;no -> syntax error
9ac7: 20 99 84                     jsr     AdvThenIsEndOfCmd       ;skip past GOTO, and any spaces
9aca: a0 00                        ldy     #$00
9acc: b1 09                        lda     (zpTXTPTR),y            ;do we have a line number?
9ace: c9 0b                        cmp     #$0b
9ad0: d0 28                        bne     errSyntax_9AFA          ;no -> syntax error
9ad2: c8                           iny
9ad3: b1 09                        lda     (zpTXTPTR),y            ;is line num low byte zero?
9ad5: d0 0a                        bne     @handleLNum             ;no -> handle line num
9ad7: c8                           iny
9ad8: b1 09                        lda     (zpTXTPTR),y            ;get lnum high byte
9ada: 85 e9                        sta     zpErrHandLNum+1         ;save as handler addr
9adc: d0 03                        bne     @handleLNum             ;lnum high byte zero? no -> handle line num
9ade: 85 e7                        sta     zpErrHandStat           ;storing 0 (EH_NO_HANDLER)
9ae0: 60                           rts

9ae1: 20 9c 84     @handleLNum     jsr     IsEndOfCmd              ;skip spaces
9ae4: 20 f0 96                     jsr     FindLineFromArg         ;find the designated line number (or error)
9ae7: a0 01                        ldy     #$01
9ae9: b1 09                        lda     (zpTXTPTR),y            ;store the line num arg as error handler line num
9aeb: 85 e8                        sta     zpErrHandLNum
9aed: c8                           iny
9aee: b1 09                        lda     (zpTXTPTR),y
9af0: 85 e9                        sta     zpErrHandLNum+1
9af2: 20 56 9b                     jsr     SkipLineNumArgs
9af5: a9 01                        lda     #EH_HANDLER_DEFINED     ;indicate we've got an error handler installed
9af7: 85 e7                        sta     zpErrHandStat
9af9: 60                           rts                             ;and done!

9afa: 4c 91 84     errSyntax_9AFA  jmp     ErrorSyntax

9afd: c9 bc        CmdFn_ON        cmp     #tok_ERROR              ;followed by an ERROR token?
9aff: f0 bf                        beq     CmdFn_ON_ERROR          ;yes -> do ON ERROR
9b01: 20 6e a2                     jsr     EvalByteInteger         ;else, BASIC-style switch statement. Evaluate the int expr
9b04: 20 bf 90                     jsr     TxtSkipSpaces
9b07: 48                           pha                             ;and push next token to CPU stack
9b08: 20 8c 90                     jsr     TxtPtrIncr
9b0b: ae 00 04                     ldx     exprParseStk            ;was the expr zero?
9b0e: f0 3e                        beq     ON_skipArgsThenPlaAndRet ;yes -> just skip the list of line numbers, "GOTO" following cmd.
9b10: 20 9c 84     @argLp          jsr     IsEndOfCmd              ;out of line num args?
9b13: f0 3c                        beq     pla_rts_9B51            ;yes -> pull token and return
9b15: c9 0b                        cmp     #tok_LNUM_AFTER         ;did we get a line num token?
9b17: d0 e1                        bne     errSyntax_9AFA          ;no -> error
9b19: ca                           dex                             ;decrement expr value (= how many args to skip)
9b1a: f0 0e                        beq     @hit                    ;decremented expr at zero? -> go to this line
9b1c: 20 10 9f                     jsr     TxtPtrAdvThree          ;else we skip this arg
9b1f: 20 9c 84                     jsr     IsEndOfCmd              ;end of cmd?
9b22: f0 2d                        beq     pla_rts_9B51            ;yes -> done. Pull token and rts
9b24: 20 14 ac                     jsr     SkipCommaOrSynErr       ;parse following comma
9b27: 4c 10 9b                     jmp     @argLp                  ;-> loop to next line-num arg

9b2a: 68           @hit            pla
9b2b: c9 bd                        cmp     #tok_RESUME             ;got "RESUME" token?
9b2d: f0 19                        beq     ON_rsm                  ;yes -> exec RESUME cmd
9b2f: 38                           sec
9b30: e9 80                        sbc     #TOKEN_START            ;else, subtract token start
9b32: c9 05                        cmp     #$05                    ; <= tok_THEN? (GOTO, GOSUB, RUN, RETURN, RESTORE, THEN)
9b34: b0 c4                        bcs     errSyntax_9AFA          ;no -> error (other commands disallowed)
9b36: 0a                           asl     A                       ;token value (- #$80) * 2 -> index into commands table
9b37: aa                           tax
9b38: bd bf ce                     lda     jtbl_Commands,x
9b3b: 85 19                        sta     zpMisc                  ;copy command addr to zpMisc
9b3d: bd c0 ce                     lda     jtbl_Commands+1,x
9b40: 85 1a                        sta     zpMisc+1
9b42: 20 bf 90                     jsr     TxtSkipSpaces
9b45: 6c 19 00                     jmp     (zpMisc)                ;-> execute (line number at TXTPTR will be the arg)

9b48: 20 9c 84     ON_rsm          jsr     IsEndOfCmd              ;set CPU flags for whether we're at end of cmd
9b4b: 4c 27 95                     jmp     CmdFn_RESUME            ;-> RESUME

                   ON_skipArgsThenPlaAndRet
9b4e: 20 56 9b                     jsr     SkipLineNumArgs         ;skip remaining line-num args
9b51: 68           pla_rts_9B51    pla                             ;pull token
9b52: 60           rts_9B52        rts                             ;and return

                   ; Skips TXTPTR past a range of line numbers, whether comma-separated,
                   ; or ranged with '-'
                   SkipKnownLineNumArgs
9b53: 20 10 9f                     jsr     TxtPtrAdvThree          ;we know we're at line num arg: skip it
9b56: 20 9c 84     SkipLineNumArgs jsr     IsEndOfCmd              ;check for more line num args
9b59: f0 f7                        beq     rts_9B52                ;end of cmd? -> rts
9b5b: c9 0b                        cmp     #$0b                    ;another line num arg?
9b5d: f0 f4                        beq     SkipKnownLineNumArgs    ;yes -> skip that
9b5f: 20 14 ac                     jsr     SkipCommaOrSynErr       ;no -> must be comma. Skip that, then arg.
9b62: 4c 56 9b                     jmp     SkipLineNumArgs

9b65: 4c 94 84     errIll_9B65     jmp     ErrorIllegalValue

                   vZpStrPtr       .var    $19    {addr/2}
                   vZpStrAvail     .var    $90    {addr/1}

9b68: c9 86        CmdFn_KEY       cmp     #$86                    ;LIST token?
9b6a: f0 45                        beq     CmdFn_KEYLIST
9b6c: 20 6e a2                     jsr     EvalByteInteger         ;get numeric arg
9b6f: f0 f4                        beq     errIll_9B65             ;zero? -> ill val error
9b71: c9 09                        cmp     #$09
9b73: b0 f0                        bcs     errIll_9B65             ; >= 9? -> ill val error
9b75: 48                           pha                             ;push arg (fkey num)
9b76: 20 14 ac                     jsr     SkipCommaOrSynErr       ;skip required comma
9b79: 20 7d a2                     jsr     EvalString              ;evaluate string
9b7c: 68                           pla                             ;pull fkey num
9b7d: 38                           sec
9b7e: e9 01                        sbc     #$01                    ;decrement (F1 -> 0 index)
9b80: 0a                           asl     A
9b81: 0a                           asl     A
9b82: 0a                           asl     A
9b83: 0a                           asl     A                       ;mul by 16 for indexing macro defs
9b84: aa                           tax
9b85: ad 00 04                     lda     exprParseStk            ;did user define empty string?
9b88: f0 21                        beq     KEY_nullTerminate       ;yes -> just null terminate and return
9b8a: a0 00                        ldy     #$00
9b8c: ad 01 04                     lda     exprParseStk+1          ;copy ptr to string contents
9b8f: 85 19                        sta     vZpStrPtr
9b91: ad 02 04                     lda     exprParseStk+2
9b94: 85 1a                        sta     vZpStrPtr+1
9b96: a9 0f                        lda     #$0f                    ;max str len
9b98: 85 90                        sta     vZpStrAvail
9b9a: b1 19        @cpyLp          lda     (vZpStrPtr),y           ;get a char from user string
9b9c: 9d 80 04                     sta     fnKey1_DfnBuf,x         ;copy to macro definition
9b9f: e8                           inx                             ;incr src and dst
9ba0: c8                           iny
9ba1: ce 00 04                     dec     exprParseStk            ;one less byte of str available
9ba4: f0 05                        beq     KEY_nullTerminate       ;out of room? -> finish up
9ba6: c6 90                        dec     vZpStrAvail
9ba8: d0 f0                        bne     @cpyLp                  ;otherwise loop to copy next char
9baa: 60                           rts

                   KEY_nullTerminate
9bab: a9 00                        lda     #$00
9bad: 9d 80 04                     sta     fnKey1_DfnBuf,x
9bb0: 60                           rts

                   ; Not actually found in jtbl_Commands. It's CmdFn_KEY when followed by
                   ; LIST token.
                   • Clear variables
                   vZpFKeyNum      .var    $74    {addr/1}
                   vZpFKeyDigit    .var    $90    {addr/1}

9bb1: 20 8c 90     CmdFn_KEYLIST   jsr     TxtPtrIncr              ;move ptr past LIST token
9bb4: a9 30                        lda     #‘0’
9bb6: 85 90                        sta     vZpFKeyDigit
9bb8: a9 00                        lda     #$00
9bba: 85 74                        sta     vZpFKeyNum
9bbc: a9 46        @eachFKeyLp     lda     #‘F’
9bbe: 20 4d 89                     jsr     QueueCharForOutput      ;queue output: 'F'
9bc1: e6 90                        inc     vZpFKeyDigit
9bc3: a5 90                        lda     vZpFKeyDigit
9bc5: 20 4d 89                     jsr     QueueCharForOutput      ;queue output: fn key number
9bc8: a9 20                        lda     #$20
9bca: 20 4d 89                     jsr     QueueCharForOutput      ;queue output: SPACE char
9bcd: a5 74                        lda     vZpFKeyNum
9bcf: 0a                           asl     A
9bd0: 0a                           asl     A
9bd1: 0a                           asl     A
9bd2: 0a                           asl     A                       ;multiply fkey num by 16
9bd3: a8                           tay
9bd4: b9 80 04     @prKeyDefLp     lda     fnKey1_DfnBuf,y         ;get char from fkey macro string
9bd7: f0 21                        beq     @cpyDone                ;null char -> done printing
9bd9: c9 20                        cmp     #$20                    ;is it a printable character?
9bdb: 90 07                        bcc     @escCtrl                ;no -> escape control. Example: Ctrl-M -> "(M)"
9bdd: 20 4d 89                     jsr     QueueCharForOutput      ;yes: queue character for printing
9be0: c8                           iny
9be1: 4c d4 9b                     jmp     @prKeyDefLp             ;loop until null or control

9be4: 48           @escCtrl        pha                             ;push the control char
9be5: a9 28                        lda     #‘(’
9be7: 20 4d 89                     jsr     QueueCharForOutput      ;queue up paren
9bea: 68                           pla                             ;pull control char
9beb: 18                           clc
9bec: 69 40                        adc     #$40                    ;shift it into the printable region
9bee: 20 4d 89                     jsr     QueueCharForOutput      ;and queue to print
9bf1: a9 29                        lda     #‘)’                    ;closing paren
9bf3: 20 4d 89                     jsr     QueueCharForOutput
9bf6: c8                           iny
9bf7: 4c d4 9b                     jmp     @prKeyDefLp             ;go back to printing chars

9bfa: 20 5d 89     @cpyDone        jsr     PrintOutBuf             ;print queued chars
9bfd: 20 6f b5                     jsr     DoCRLF                  ;CRLF
9c00: e6 74                        inc     vZpFKeyNum
9c02: a5 74                        lda     vZpFKeyNum
9c04: c9 08                        cmp     #$08                    ;done 8 fkeys?
9c06: 90 b4                        bcc     @eachFKeyLp             ;no -> do next fkey
9c08: 60                           rts                             ;yes: done

                   • Clear variables

9c09: 4c 91 84     errSyn_9C09     jmp     ErrorSyntax

                   vZpNewVarName   .var    $7c    {addr/2}
                   vZpNewVarMaxIdx .var    $7e    {addr/2}
                   vZpNewVarAryDimens .var $82    {addr/1}         ;number of dimens in new array var
                   vZpNewVarType   .var    $84    {addr/1}

9c0c: 20 c4 a7     CmdFn_DIM       jsr     IsAlpha                 ;is next char alphabetic?
9c0f: b0 f8                        bcs     errSyn_9C09             ;no, can't be a varname then -> error
9c11: 85 7c                        sta     vZpNewVarName
9c13: a9 02                        lda     #TYP_INT
9c15: 85 84                        sta     vZpNewVarType
9c17: a9 00                        lda     #$00
9c19: 85 7d                        sta     vZpNewVarName+1
9c1b: 20 98 90                     jsr     TxtPtrIncrAndGetChar    ;get next char
9c1e: 20 bc a7                     jsr     IsAlNum                 ;alphanumeric?
9c21: b0 05                        bcs     @notAlNum               ;no -> not part of name
9c23: 85 7d                        sta     vZpNewVarName+1         ;yes, it's the 2nd char of var name; store it
9c25: 20 8c 90                     jsr     TxtPtrIncr              ;(note: DIM can't accept oversized names,
9c28: 20 bf 90     @notAlNum       jsr     TxtSkipSpaces           ; unlike normal assignments)
9c2b: c9 24                        cmp     #‘$’                    ;next char == '$'
9c2d: d0 05                        bne     @notStr                 ;no -> don't update type
9c2f: e6 84                        inc     vZpNewVarType           ;mark the new type as str
9c31: 20 99 84                     jsr     AdvThenIsEndOfCmd
9c34: c9 28        @notStr         cmp     #‘(’                    ;next char == '('?
9c36: d0 d1                        bne     errSyn_9C09             ;no -> error (DIM in FB can only declare arrays)
9c38: a9 00                        lda     #$00
9c3a: 85 82                        sta     vZpNewVarAryDimens      ;set # dimens to 1 (value: 0), until we hear otherwise
9c3c: 20 65 a2                     jsr     AdvThenEvalByteInt      ;eval specified max index
9c3f: 85 7e                        sta     vZpNewVarMaxIdx         ;and save it
9c41: 20 bf 90                     jsr     TxtSkipSpaces
9c44: c9 29                        cmp     #‘)’                    ;closed paren?
9c46: f0 0e                        beq     @closeP                 ;yes -> no second index
9c48: e6 82                        inc     vZpNewVarAryDimens      ;no, it's 2-dimensional. Increment # dimens
9c4a: 20 6b a2                     jsr     EvalCommaThenByteInt    ;parse comma and then another index
9c4d: 85 7f                        sta     vZpNewVarMaxIdx+1       ;and store it
9c4f: 20 9c 84                     jsr     IsEndOfCmd              ;check if we're at end of command (and then ignore result??)
9c52: c9 29                        cmp     #‘)’                    ;NOW closed paren?
9c54: d0 b3                        bne     errSyn_9C09             ;no -> error
9c56: 20 8c 90     @closeP         jsr     TxtPtrIncr
9c59: 20 70 9c                     jsr     DIM_ReserveArrayMem     ;reserve space for the new array
9c5c: 20 9c 84                     jsr     IsEndOfCmd              ;end of cmd?
9c5f: f0 09                        beq     @rts                    ;yes -> exit
9c61: 20 14 ac                     jsr     SkipCommaOrSynErr       ;parse a separating comma
9c64: 20 bf 90                     jsr     TxtSkipSpaces           ;and any spaces
9c67: 4c 0c 9c                     jmp     CmdFn_DIM               ;then process the next declaration

9c6a: 60           @rts            rts

                   ErrorDuplicateDefinitions
9c6b: a9 09                        lda     #err_DD
9c6d: 4c 37 b2                     jmp     PrintErrorAndStop

                   DIM_ReserveArrayMem
9c70: 20 50 9d                     jsr     FindVar_7C_7D_84        ;can we find a matching var?
9c73: b0 f6                        bcs     ErrorDuplicateDefinitions ;yes -> error duplicate var
9c75: 20 d6 9c                     jsr     DisallowBothIdxAtMax
9c78: a9 01                        lda     #$01                    ;set multiplicand to 1
9c7a: 85 2c                        sta     zpWParam
9c7c: 20 ce a7                     jsr     ClearOperandHiBytes
9c7f: a5 7e                        lda     vZpNewVarMaxIdx
9c81: 85 28                        sta     zpWAccum                ;set multiplier to size of dimen 1 (max idx + 1)
9c83: e6 28                        inc     zpWAccum
9c85: d0 02                        bne     @waNoOverfl
9c87: e6 29                        inc     zpWAccum+1              ;(incr to get # elems from max idx)
9c89: a5 82        @waNoOverfl     lda     vZpNewVarAryDimens      ;have only one dimen?
9c8b: f0 0a                        beq     @oneDim                 ;yes -> leave multiplicand where it is
9c8d: a5 7f                        lda     vZpNewVarMaxIdx+1       ;no, multiply the # elems for both
9c8f: 85 2c                        sta     zpWParam
9c91: e6 2c                        inc     zpWParam                ;(increment to get # elems)
9c93: d0 02                        bne     @oneDim
9c95: e6 2d                        inc     zpWParam+1
9c97: 20 47 8f     @oneDim         jsr     WordMultiply
9c9a: 20 04 a7                     jsr     GetSizeOfElementType
9c9d: 85 2c                        sta     zpWParam
9c9f: a9 00                        lda     #$00
9ca1: 85 2d                        sta     zpWParam+1
9ca3: 20 47 8f                     jsr     WordMultiply            ;then multiply the result by the size of the array's element type
9ca6: a9 05                        lda     #$05
9ca8: 85 2c                        sta     zpWParam                ;add space for var name and type info
9caa: a5 82                        lda     vZpNewVarAryDimens      ;only one dimen?
9cac: f0 02                        beq     @oneDim1                ;yes -> we're good
9cae: e6 2c                        inc     zpWParam                ;no, we need one more byte of space for type info (for 2nd max idx)
9cb0: 20 f7 8e     @oneDim1        jsr     WordAdd                 ;(do the add)
9cb3: b0 1e                        bcs     errOutOfMem_9CD3        ;(OOM if so indicated)
9cb5: a5 8c                        lda     zpVarData
9cb7: 85 19                        sta     zpMisc
9cb9: 85 2c                        sta     zpWParam
9cbb: a5 8d                        lda     zpVarData+1
9cbd: 85 1a                        sta     zpMisc+1
9cbf: 85 2d                        sta     zpWParam+1
9cc1: 20 f7 8e                     jsr     WordAdd                 ;now add that to the variable location (end of var table)
9cc4: b0 0d                        bcs     errOutOfMem_9CD3        ;overflow past #$FFFF? -> OOM error
9cc6: a5 04                        lda     zpFRETOP+1
9cc8: 85 2d                        sta     zpWParam+1
9cca: a5 03                        lda     zpFRETOP
9ccc: 85 2c                        sta     zpWParam
9cce: 20 e2 8f                     jsr     WordCompare             ;did we overflow past zpFRETOP?
9cd1: 90 0e                        bcc     DIM_InitializeArrayVar
                   errOutOfMem_9CD3
9cd3: 4c c8 8b                     jmp     ErrorOutOfMemory

                   DisallowBothIdxAtMax
9cd6: a9 ff                        lda     #$ff
9cd8: c5 7e                        cmp     vZpNewVarMaxIdx
9cda: d0 04                        bne     @rts
9cdc: c5 7f                        cmp     vZpNewVarMaxIdx+1
9cde: f0 f3                        beq     errOutOfMem_9CD3
9ce0: 60           @rts            rts

                   DIM_InitializeArrayVar
9ce1: a0 00                        ldy     #$00
9ce3: a5 90                        lda     zpVarType               ;store var type (including array flag)
9ce5: 91 8c                        sta     (zpVarData),y
9ce7: c8                           iny
9ce8: a5 8a                        lda     zpVarName               ;store var name first char
9cea: 91 8c                        sta     (zpVarData),y
9cec: c8                           iny
9ced: a5 8b                        lda     zpVarName+1             ;store var name 2nd char
9cef: 91 8c                        sta     (zpVarData),y
9cf1: c8                           iny
9cf2: a5 82                        lda     vZpNewVarAryDimens      ;store # dimens
9cf4: 91 8c                        sta     (zpVarData),y
9cf6: c8                           iny
9cf7: a5 7e                        lda     vZpNewVarMaxIdx         ;store first max idx
9cf9: 91 8c                        sta     (zpVarData),y
9cfb: a5 82                        lda     vZpNewVarAryDimens      ;just one dimen?
9cfd: f0 05                        beq     @oneDim2                ;yes -> skip storing a 2nd idx
9cff: c8                           iny                             ;no, store 2nd max idx
9d00: a5 7f                        lda     vZpNewVarMaxIdx+1
9d02: 91 8c                        sta     (zpVarData),y
9d04: a5 28        @oneDim2        lda     zpWAccum                ;set the new end of the vars table
9d06: 85 1f                        sta     zpVAREND
9d08: a5 29                        lda     zpWAccum+1
9d0a: 85 20                        sta     zpVAREND+1
9d0c: 18                           clc
9d0d: a2 05                        ldx     #$05                    ;5 bytes past var start: that's where the contents begin
9d0f: a5 82                        lda     vZpNewVarAryDimens      ;is it one-dimensional?
9d11: f0 01                        beq     @oneDim3                ;yes -> stick with 5
9d13: e8                           inx                             ;no: actually we need 6 to include the 2nd max idx
9d14: 8a           @oneDim3        txa
9d15: 65 19                        adc     zpMisc                  ;add the offset
9d17: 85 19                        sta     zpMisc
9d19: a9 00                        lda     #$00
9d1b: 65 1a                        adc     zpMisc+1
9d1d: 85 1a                        sta     zpMisc+1
9d1f: a0 00        @ClearElemsLp   ldy     #$00                    ;Initialize all the array contents to zeroes
9d21: a9 00                        lda     #$00
9d23: 91 19                        sta     (zpMisc),y              ;set zero
9d25: e6 19                        inc     zpMisc                  ;increment scanning ptr
9d27: d0 02                        bne     @noCarry
9d29: e6 1a                        inc     zpMisc+1
9d2b: a5 1a        @noCarry        lda     zpMisc+1
9d2d: c5 20                        cmp     zpVAREND+1              ;at var table end?
9d2f: d0 ee                        bne     @ClearElemsLp           ;no -> next loop iteration
9d31: a5 19                        lda     zpMisc
9d33: c5 1f                        cmp     zpVAREND                ;check high byte too
9d35: d0 e8                        bne     @ClearElemsLp
9d37: a0 00                        ldy     #$00
9d39: a9 00                        lda     #$00
9d3b: 91 19                        sta     (zpMisc),y              ;set next 3 bytes after vars end to 0, too
9d3d: c8                           iny
9d3e: 91 19                        sta     (zpMisc),y
9d40: c8                           iny
9d41: 91 19                        sta     (zpMisc),y
9d43: 60                           rts                             ;and return

                   ; Not in jtbl_Commands: in reality, this is just "SAVE S", not "SAVES".
                   • Clear variables

9d44: 20 8c 90     CmdFn_SAVES     jsr     TxtPtrIncr
9d47: 20 52 9e                     jsr     GetFileNameArg
9d4a: 20 9d 9e                     jsr     FileNameCpyToLineBuffer
9d4d: 4c 0d c5                     jmp     SaveBackgroundMap

                   ; Transfer vars from $7C, $7D, and $84 to the spots that FindVar uses for the
                   ; search key, then call it
                   FindVar_7C_7D_84
9d50: a5 7c                        lda     zpBgGotFlag
9d52: 85 8a                        sta     zpVarName
9d54: a5 7d                        lda     zp7D
9d56: 85 8b                        sta     zpVarName+1
9d58: a5 84                        lda     zpForNxtLnLnk
9d5a: 09 80                        ora     #$80
9d5c: 85 90                        sta     zpVarType
9d5e: 20 0f a7                     jsr     FindVar
9d61: 4c 08 a6                     jmp     CpyVarPtrToVarData

                   ; BASIC PROGRAM HEADER FORMAT:
                   ; 
                   ; byte 0:        #$02
                   ; bytes 1 - 17:  null-term'd filename (no len byte)
                   ; bytes 18 -19:  program size in bytes
                   ; bytes 20 - 21: pointer to prog start (zpTXTTAB)
                   ; bytes 22-127:  (unused) 
9d64: c9 53        CmdFn_SAVE      cmp     #‘S’
9d66: f0 dc                        beq     CmdFn_SAVES             ;divert to save screen, not prog
9d68: 20 52 9e                     jsr     GetFileNameArg
9d6b: 20 9d 9e                     jsr     FileNameCpyToLineBuffer
9d6e: a9 02                        lda     #CAS_HDR_PROG
9d70: 8d 00 05                     sta     lineBuffer
9d73: a5 05                        lda     zpTXTTAB
9d75: 8d 14 05                     sta     lineBuffer+20
9d78: 85 2c                        sta     zpWParam
9d7a: a5 06                        lda     zpTXTTAB+1
9d7c: 8d 15 05                     sta     lineBuffer+21
9d7f: 85 2d                        sta     zpWParam+1
9d81: a5 07                        lda     zpTXTEND
9d83: 85 28                        sta     zpWAccum
9d85: a5 08                        lda     zpTXTEND+1
9d87: 85 29                        sta     zpWAccum+1
9d89: 20 0b 8e                     jsr     SetIntAndSubtract       ;get prog len from prog start and end
9d8c: a5 28                        lda     zpWAccum
9d8e: 8d 12 05                     sta     lineBuffer+18
9d91: a5 29                        lda     zpWAccum+1
9d93: 8d 13 05                     sta     lineBuffer+19
9d96: 20 9f 9d                     jsr     Print_WRITING_fileName
9d99: 20 1f bf                     jsr     CasSendHeader
9d9c: 4c 30 bf                     jmp     CasSendData

                   Print_WRITING_fileName
9d9f: a9 67                        lda     #<strWRITING
9da1: 85 52                        sta     zpOutputStr
9da3: a9 b3                        lda     #>strWRITING
9da5: 85 53                        sta     zpOutputStr+1
9da7: 20 5a 89                     jsr     PrintString
9daa: f0 18                        beq     PrintFileName           ;always branches
                   Print_LOADING_fileName
9dac: a9 70                        lda     #<strLOADING
9dae: 85 52                        sta     zpOutputStr
9db0: a9 b3                        lda     #>strLOADING
9db2: 85 53                        sta     zpOutputStr+1
9db4: 20 5a 89                     jsr     PrintString
9db7: f0 0b                        beq     PrintFileName           ;always branches
                   Print_SKIP_fileName
9db9: a9 79                        lda     #<strSKIP
9dbb: 85 52                        sta     zpOutputStr
9dbd: a9 b3                        lda     #>strSKIP
9dbf: 85 53                        sta     zpOutputStr+1
9dc1: 20 5a 89                     jsr     PrintString
9dc4: a9 01        PrintFileName   lda     #<lineBuffer+1
9dc6: 85 52                        sta     zpOutputStr
9dc8: a9 05                        lda     #>lineBuffer
9dca: 85 53                        sta     zpOutputStr+1
9dcc: 20 5a 89                     jsr     PrintString
9dcf: 4c 6f b5                     jmp     DoCRLF

9dd2: 20 b9 9d     LoadSkipFile    jsr     Print_SKIP_fileName
9dd5: 4c f5 9d                     jmp     LD_skipped

                   ; Not in jtbl_Commands: in reality, this is just "LOAD S", not "LOADS".
9dd8: 20 8c 90     CmdFn_LOADS     jsr     TxtPtrIncr
9ddb: 20 52 9e                     jsr     GetFileNameArg
9dde: 4c 65 c5                     jmp     LoadBackgroundMap

9de1: a2 00        CmdFn_LOAD      ldx     #$00
9de3: 86 9f                        stx     zpLoadQFlag
9de5: c9 53                        cmp     #‘S’
9de7: f0 ef                        beq     CmdFn_LOADS
9de9: c9 8b                        cmp     #$8b                    ;"LOAD?"?... actually literally the PRINT token
9deb: d0 05                        bne     @notLOADQ               ;no -> leave flag at zero
9ded: c6 9f                        dec     zpLoadQFlag             ; = #$ff
9def: 20 8c 90                     jsr     TxtPtrIncr
9df2: 20 52 9e     @notLOADQ       jsr     GetFileNameArg
9df5: 20 53 be     LD_skipped      jsr     CasRcvHeader
9df8: b0 53                        bcs     ErrorTapeRead
9dfa: 20 b1 9e                     jsr     LoadCheckFilename       ;does filename match our arg (if any)?
9dfd: b0 d3                        bcs     LoadSkipFile            ;no -> skip this file and try the next
9dff: ad 00 05                     lda     lineBuffer
9e02: c9 02                        cmp     #CAS_HDR_PROG           ;is it a Program header?
9e04: d0 cc                        bne     LoadSkipFile            ;no; BG header or other. Skip that, too.
9e06: 20 ac 9d                     jsr     Print_LOADING_fileName  ;yes, this is it! Tell user the good news
9e09: a5 9f                        lda     zpLoadQFlag             ;"LOAD?"?
9e0b: f0 06                        beq     LoadProgram             ;no -> load program
9e0d: 20 1c be                     jsr     CasVrfyProgram
9e10: b0 3b                        bcs     ErrorTapeRead
9e12: 60                           rts

9e13: a5 05        LoadProgram     lda     zpTXTTAB                ;don't trust header's load destination; override with TXTTAB
9e15: 8d 14 05                     sta     lineBuffer+20
9e18: a5 06                        lda     zpTXTTAB+1
9e1a: 8d 15 05                     sta     lineBuffer+21
9e1d: 20 24 be                     jsr     CasRcvFileData
9e20: b0 2b                        bcs     ErrorTapeRead
9e22: a5 ad                        lda     zpCasBufPtr             ;Wherever we finished loading, wll, that's the program
9e24: 85 07                        sta     zpTXTEND                ; end. We should mark it.
9e26: a5 ae                        lda     zpCasBufPtr+1
9e28: 85 08                        sta     zpTXTEND+1
9e2a: 20 46 9e                     jsr     @nullByteToCasBuf       ;put NUL byte there
9e2d: 20 ce bd                     jsr     IncrCasBufPtr
9e30: a5 ad                        lda     zpCasBufPtr
9e32: 85 1d                        sta     zpVARTAB                ;next byte is start of vars
9e34: a5 ae                        lda     zpCasBufPtr+1
9e36: 85 1e                        sta     zpVARTAB+1
9e38: 20 46 9e                     jsr     @nullByteToCasBuf       ;NUL there
9e3b: 20 ce bd                     jsr     IncrCasBufPtr
9e3e: a5 ad                        lda     zpCasBufPtr
9e40: 85 1f                        sta     zpVAREND                ;next byte end of vars
9e42: a5 ae                        lda     zpCasBufPtr+1
9e44: 85 20                        sta     zpVAREND+1
                   @nullByteToCasBuf
9e46: a0 00                        ldy     #$00                    ;NUL there
9e48: a9 00                        lda     #$00
9e4a: 91 ad                        sta     (zpCasBufPtr),y
9e4c: 60                           rts

9e4d: a9 11        ErrorTapeRead   lda     #err_TP
9e4f: 4c 37 b2                     jmp     PrintErrorAndStop

                   ; Used by SAVE, LOAD, etc. Checks if there's a string argument, and if so,
                   ; copies it (temporarily) to upper half of lineBuffer. Otherwise, sets that area
                   ; to 16 blanks (space characters). (It gets copied to (almost) the start of
                   ; lineBuffer, later)
9e52: 20 8a 9e     GetFileNameArg  jsr     FileNameInit
9e55: a9 00                        lda     #$00
9e57: 85 ea                        sta     zpHaveFNameArg
9e59: 20 9c 84                     jsr     IsEndOfCmd              ;is there an arg?
9e5c: f0 2b                        beq     @rts                    ;no -> nothing to do; return
9e5e: 20 7d a2                     jsr     EvalString              ;yes: expect prog name str
9e61: ad 01 04                     lda     exprParseStk+1
9e64: 85 19                        sta     zpMisc
9e66: ad 02 04                     lda     exprParseStk+2
9e69: 85 1a                        sta     zpMisc+1
9e6b: ad 00 04                     lda     exprParseStk            ;is the string empty?
9e6e: f0 19                        beq     @rts                    ;yes -> return
9e70: a0 00                        ldy     #$00
9e72: a2 00                        ldx     #$00
9e74: b1 19        @cpyName        lda     (zpMisc),y              ;copy eval'd str as filename
9e76: 9d 80 05                     sta     lineBuffer+128,x
9e79: e8                           inx
9e7a: c8                           iny
9e7b: ce 00 04                     dec     exprParseStk
9e7e: d0 f4                        bne     @cpyName
9e80: a9 00                        lda     #$00
9e82: 9d 80 05                     sta     lineBuffer+128,x
9e85: a9 01                        lda     #$01
9e87: 85 ea                        sta     zpHaveFNameArg
9e89: 60           @rts            rts

                   ; Initialize the temporary file name area of lineBuffer (+128) to 16 spaces,
                   ; followed by the null terminator
9e8a: a2 00        FileNameInit    ldx     #$00
9e8c: a0 10                        ldy     #$10
9e8e: a9 20                        lda     #$20
9e90: 9d 80 05     @setSpcLp       sta     lineBuffer+128,x
9e93: e8                           inx
9e94: 88                           dey
9e95: d0 f9                        bne     @setSpcLp
9e97: a9 00                        lda     #$00
9e99: 9d 80 05                     sta     lineBuffer+128,x
9e9c: 60                           rts

                   ; Copy file name (stored from lineBuffer + 128), to LineBuffer + 1
                   FileNameCpyToLineBuffer
9e9d: a2 00                        ldx     #$00
9e9f: a0 10                        ldy     #$10
9ea1: bd 80 05     @namCpy         lda     lineBuffer+128,x
9ea4: 9d 01 05                     sta     lineBuffer+1,x
9ea7: e8                           inx
9ea8: 88                           dey
9ea9: d0 f6                        bne     @namCpy
9eab: a9 00                        lda     #$00
9ead: 9d 01 05                     sta     lineBuffer+1,x
9eb0: 60                           rts

                   LoadCheckFilename
9eb1: a5 ea                        lda     zpHaveFNameArg          ;did LOAD get a filename arg?
9eb3: f0 10                        beq     @success                ;no -> just report true
9eb5: a2 00                        ldx     #$00                    ;yes: compare
9eb7: a0 10                        ldy     #$10                    ;16 char max filename
9eb9: bd 01 05     @cmpLp          lda     lineBuffer+1,x
9ebc: dd 80 05                     cmp     lineBuffer+128,x
9ebf: d0 06                        bne     @failure
9ec1: e8                           inx
9ec2: 88                           dey
9ec3: d0 f4                        bne     @cmpLp
9ec5: 18           @success        clc
9ec6: 60                           rts

9ec7: 38           @failure        sec
9ec8: 60                           rts

9ec9: f0 18        CmdFn_RESTORE   beq     RestoreFromStart        ;no arg? restore from start of prog
9ecb: 20 f0 96                     jsr     FindLineFromArg
9ece: 20 56 9b                     jsr     SkipLineNumArgs
9ed1: a9 03                        lda     #$03
9ed3: 18                           clc
9ed4: 65 5b                        adc     zpTxtLink               ;add 3 to found line pointer (skipping line linkage and number)
9ed6: 85 b2                        sta     zpNextDataPtr           ;and set that as next place to start future DATA searches
9ed8: a9 00                        lda     #$00
9eda: 65 5c                        adc     zpTxtLink+1             ;add any carry
9edc: 85 b3        ResFinish       sta     zpNextDataPtr+1         ;set high byte of DATA link
9ede: a9 00                        lda     #$00
9ee0: 85 b4                        sta     zpNextDataIdx           ;clear char index
9ee2: 60                           rts

                   ; Reset DATA pointer to start of first BASIC line.
                   RestoreFromStart
9ee3: a5 05                        lda     zpTXTTAB
9ee5: 18                           clc
9ee6: 69 03                        adc     #$03                    ;init DATA search to begin from first line of program (past initial line)link
9ee8: 85 b2                        sta     zpNextDataPtr
9eea: a9 00                        lda     #$00
9eec: 65 06                        adc     zpTXTTAB+1
9eee: 4c dc 9e                     jmp     ResFinish               ;(just above)

                   ; Skips the next token.
                   ; 
                   ; BUG: does NOT skip entire comments. The DATA token, for instance, will be
                   ; recognized if it occurs within a comment (as a Japanese "small e" ェ)
9ef1: 20 b2 90     SkipToken       jsr     GetTxtChar
9ef4: c9 80                        cmp     #$80                    ;have a token?
9ef6: b0 15                        bcs     @incrTxtPtrAndReturn    ;just increment to skip it
9ef8: c9 12                        cmp     #tok_NUM_AFTER          ;have a decimal number?
9efa: f0 14                        beq     TxtPtrAdvThree          ;skip it
9efc: c9 11                        cmp     #tok_HEX_AFTER          ;have a hex number?
9efe: f0 10                        beq     TxtPtrAdvThree          ;skip it
9f00: c9 0b                        cmp     #tok_LNUM_AFTER         ;have a line number?
9f02: f0 0c                        beq     TxtPtrAdvThree          ;skip it
9f04: c9 22                        cmp     #‘"’                    ;double-quote?
9f06: f0 15                        beq     READ_skipStrLit         ;skip the string literal
9f08: 20 c4 a7                     jsr     IsAlpha                 ;is it an alphabetic character?
9f0b: 90 1e                        bcc     SkipVarName             ;skip a var name
                   @incrTxtPtrAndReturn
9f0d: 4c 8c 90                     jmp     TxtPtrIncr              ;everything else, skip one char

                   ; Advance zpTXTPTR by 3
9f10: 20 8c 90     TxtPtrAdvThree  jsr     TxtPtrIncr
9f13: 20 8c 90                     jsr     TxtPtrIncr
9f16: 4c 8c 90                     jmp     TxtPtrIncr

9f19: 68           @eosFnd         pla                             ;restore A
9f1a: 4c 8c 90                     jmp     TxtPtrIncr

9f1d: 48           READ_skipStrLit pha
9f1e: 20 98 90     @skipStrLp      jsr     TxtPtrIncrAndGetChar
9f21: c9 22                        cmp     #‘"’                    ;have double-quote?
9f23: f0 f4                        beq     @eosFnd                 ;skipt it then return (we're done!)
9f25: 29 ff                        and     #$ff                    ;have end-of-line?
9f27: d0 f5                        bne     @skipStrLp              ;no -> skip char, check next
9f29: 68                           pla                             ;yes: restore A and return
9f2a: 60                           rts                             ;we've successfully skipped string

                   ; Skips over a variable name. This does not include any trailing '$', just
                   ; alphanumeric strings.
9f2b: 48           SkipVarName     pha
9f2c: 20 98 90     @skipAlNumLp    jsr     TxtPtrIncrAndGetChar
9f2f: 20 bc a7                     jsr     IsAlNum
9f32: 90 f8                        bcc     @skipAlNumLp
9f34: 68                           pla
9f35: 60                           rts

                   vZpStrLen       .var    $1b    {addr/1}
                   vZpEOsMark      .var    $3c    {addr/1}

9f36: 20 76 a5     CmdFn_READ      jsr     EvalVarNameOrSynErr
9f39: 20 30 94                     jsr     SaveTXTPTR              ;save away current text position
9f3c: a5 b2                        lda     zpNextDataPtr           ;load up TXTPTR with where we left off from previous READs
9f3e: 85 09                        sta     zpTXTPTR
9f40: a5 b3                        lda     zpNextDataPtr+1
9f42: 85 0a                        sta     zpTXTPTR+1
9f44: a5 b4                        lda     zpNextDataIdx           ;were we in the middle of a line?
9f46: d0 26                        bne     READ_midDataStmt        ;yes -> handle
9f48: 20 b2 90     @dataCmdSeekLp  jsr     GetTxtChar              ;loop until found DATA statement, or end of program
9f4b: f0 0a                        beq     @foundEol               ;end of line? set up scanning for next one
9f4d: c9 91                        cmp     #tok_DATA
9f4f: f0 16                        beq     READ_foundDataCmd       ;got DATA? -> read from it
9f51: 20 f1 9e                     jsr     SkipToken
9f54: 4c 48 9f                     jmp     @dataCmdSeekLp

9f57: 20 98 90     @foundEol       jsr     TxtPtrIncrAndGetChar    ;adv and get char
9f5a: f0 06                        beq     ErrorOutOfData          ;a second '\0'? -> we're at the prog end and out of data; error out.
9f5c: 20 10 9f                     jsr     TxtPtrAdvThree          ;not '\0'. Skip past "next" offset, and line number.
9f5f: 4c 48 9f                     jmp     @dataCmdSeekLp          ;go back to looking for a DATA cmd

9f62: a9 03        ErrorOutOfData  lda     #err_OD
9f64: 4c 37 b2                     jmp     PrintErrorAndStop

                   READ_foundDataCmd
9f67: a9 ff                        lda     #$ff                    ;not really sure what the point of this is.
9f69: 85 b4                        sta     zpNextDataIdx           ;just to invalidate curent value I guess?
9f6b: 20 f1 9e                     jsr     SkipToken               ;skip the DATA token we found
                   READ_midDataStmt
9f6e: a5 90                        lda     zpVarType               ;get the type of the var we're looking for
9f70: 29 0f                        and     #$0f                    ;strip the array bit, if present
9f72: c9 03                        cmp     #TYP_STR                ;is it a string?
9f74: f0 52                        beq     READ_seekStrings        ;yes -> handle that
9f76: 20 9c 84                     jsr     IsEndOfCmd              ;no -> we're seeking ints then
9f79: f0 e7                        beq     ErrorOutOfData          ;did we run out of data? -> error out
9f7b: 20 2d ab                     jsr     READ_parseNumber
9f7e: a0 00                        ldy     #$00
9f80: a5 28                        lda     zpWAccum
9f82: 91 8c                        sta     (zpVarData),y           ;copy number from WAccum into the variable's data
9f84: a5 29                        lda     zpWAccum+1
9f86: c8                           iny
9f87: 91 8c                        sta     (zpVarData),y
9f89: 20 9c 84                     jsr     IsEndOfCmd              ;are we at the end of the DATA command?
9f8c: f0 15                        beq     READ_setupNextDataCmdSeek ;yes -> go find next DATA stmt
9f8e: 20 bf 90                     jsr     TxtSkipSpaces
9f91: c9 2c                        cmp     #‘,’                    ;no: check if next char (after spaces) is a comma
9f93: f0 03                        beq     READ_gotDataComma       ;comma? -> see if we can start from here next time
9f95: 4c ff a9                     jmp     ErrorTypeMismatch       ;not comma: error!

                   READ_gotDataComma
9f98: 20 8c 90                     jsr     TxtPtrIncr
9f9b: 20 9c 84                     jsr     IsEndOfCmd              ;are we at the end of the DATA cmd, after a comma (empty field)?
9f9e: f0 c2                        beq     ErrorOutOfData          ;yes -> error!
9fa0: 4c a7 9f                     jmp     READ_setupNextReadLoc   ;no: we found next READ's field to use

                   READ_setupNextDataCmdSeek
9fa3: a9 00                        lda     #$00                    ;Next READ, search for a DATA statement starting from here.
9fa5: 85 b4                        sta     zpNextDataIdx
                   READ_setupNextReadLoc
9fa7: a5 09                        lda     zpTXTPTR                ;Next READ, use the value we're at now.
9fa9: 85 b2                        sta     zpNextDataPtr           ;save current TXTPTR to zpNextDataPtr
9fab: a5 0a                        lda     zpTXTPTR+1
9fad: 85 b3                        sta     zpNextDataPtr+1
9faf: 20 39 94                     jsr     RestoreTXTPTR           ;and restore TXTPTR to just after the READ variable
9fb2: 20 bf 90                     jsr     TxtSkipSpaces
9fb5: c9 2c                        cmp     #‘,’                    ;comma? -> handle next var
9fb7: f0 01                        beq     READ_gotReadComma
9fb9: 60           rts_9FB9        rts

                   READ_gotReadComma
9fba: 20 8c 90                     jsr     TxtPtrIncr
9fbd: 20 bf 90                     jsr     TxtSkipSpaces
9fc0: 20 c4 a7                     jsr     IsAlpha                 ;got alphabetic char?
9fc3: b0 f4                        bcs     rts_9FB9                ;no -> allow the spurious trailing comma, and finish cmd
9fc5: 4c 36 9f                     jmp     CmdFn_READ              ;yes -> start over again for new variable

                   READ_seekStrings
9fc8: a9 00                        lda     #$00
9fca: 85 1b                        sta     vZpStrLen               ;set string length to zero
9fcc: aa                           tax
9fcd: a9 2c                        lda     #‘,’                    ;set comma as end-of-string to seek
9fcf: 85 3c                        sta     vZpEOsMark
9fd1: 20 9c 84                     jsr     IsEndOfCmd              ;we're not at the end of the command, are we?
9fd4: f0 8c                        beq     ErrorOutOfData
9fd6: c9 22                        cmp     #‘"’                    ;do we have a double-quote?
9fd8: d0 05                        bne     @seekEosLp              ;no -> skip skipping it
9fda: 85 3c                        sta     vZpEOsMark              ;set dbl quot as end-of-string to seek
9fdc: 20 8c 90                     jsr     TxtPtrIncr
9fdf: b1 09        @seekEosLp      lda     (zpTXTPTR),y            ;are we at end of the line?
9fe1: f0 22                        beq     READ_eocStoreStrVar     ;yes -> store the string var
9fe3: c5 3c                        cmp     vZpEOsMark              ;EOS mark (comma or dquot)?
9fe5: f0 0c                        beq     @eosFnd                 ;yes -> store the string var, and possibly skip a comma
9fe7: 9d 00 07                     sta     genPurposeBuf,x         ;otherwise, copy char into tmp buffer
9fea: e6 1b                        inc     vZpStrLen               ;incr string len
9fec: e8                           inx
9fed: 20 8c 90                     jsr     TxtPtrIncr              ;incr TXTPTR
9ff0: 4c df 9f                     jmp     @seekEosLp              ;and loop back to check that

9ff3: 20 8c 90     @eosFnd         jsr     TxtPtrIncr
9ff6: 20 9c 84                     jsr     IsEndOfCmd              ;end of the command?
9ff9: f0 0a                        beq     READ_eocStoreStrVar     ;yes -> go and store string var
9ffb: c9 2c                        cmp     #‘,’                    ;comma?
9ffd: d0 0a                        bne     READ_storeStrVar        ;no -> go and store string var
9fff: 20 8c 90                     jsr     TxtPtrIncr              ;yes: skip TXTPTR past comma character
a002: 4c 09 a0                     jmp     READ_storeStrVar        ;-> go and store string var

                   READ_eocStoreStrVar
a005: a9 00                        lda     #$00                    ;mark that we're not in the middle of a line for next READ
a007: 85 b4                        sta     zpNextDataIdx
                   READ_storeStrVar
a009: a5 1b                        lda     vZpStrLen               ;move str info onto expression stack
a00b: 8d 00 04                     sta     exprParseStk
a00e: a9 00                        lda     #<genPurposeBuf
a010: 8d 01 04                     sta     exprParseStk+1
a013: a9 07                        lda     #>genPurposeBuf
a015: 8d 02 04                     sta     exprParseStk+2
a018: 20 cb 83                     jsr     CpyExprStrToStrVar      ;and call the fn that will copy that to the var
a01b: 4c a7 9f                     jmp     READ_setupNextReadLoc

                   • Clear variables
                   vZpPlayPtr      .var    $91    {addr/2}

a01e: 20 7d a2     CmdFn_PLAY      jsr     EvalString              ;parse following string
a021: ad 00 04                     lda     exprParseStk            ;is the string empty?
a024: f0 60                        beq     rts_A086                ;yes -> nothing to do, then
a026: a5 09                        lda     zpTXTPTR
a028: 48                           pha                             ;push TXTPTR to CPU stack
a029: a5 0a                        lda     zpTXTPTR+1
a02b: 48                           pha
a02c: ad 01 04                     lda     exprParseStk+1          ;point TXTPTR at the arg string
a02f: 85 09                        sta     zpTXTPTR
a031: ad 02 04                     lda     exprParseStk+2
a034: 85 0a                        sta     zpTXTPTR+1
a036: a9 07                        lda     #>genPurposeBuf         ;point vZpPlayPtr at the general-purpose buffer
a038: 85 92                        sta     vZpPlayPtr+1
a03a: a9 00                        lda     #<genPurposeBuf
a03c: 85 91                        sta     vZpPlayPtr
a03e: 85 c6                        sta     zpPlayVoxIdx
a040: 20 41 a2                     jsr     PLAY_copyVoiceToGenPurpBuf ;copy one voice from the arg string into genPurposeBuf
a043: a9 21                        lda     #PLAY_INDEX_V1
a045: 85 91                        sta     vZpPlayPtr              ;advance buffer pointer to start of next voice
a047: 85 c7                        sta     zpPlayVoxIdx+1
a049: 20 41 a2                     jsr     PLAY_copyVoiceToGenPurpBuf ;copy next voice from the arg string into genPurposeBuf
a04c: a9 42                        lda     #PLAY_INDEX_V2
a04e: 85 91                        sta     vZpPlayPtr              ;advance buffer pointer to start of next voice
a050: 85 c8                        sta     zpPlayVoxIdx+2
a052: 20 41 a2                     jsr     PLAY_copyVoiceToGenPurpBuf ;copy final voice from the arg string into genPurposeBuf
a055: a9 01                        lda     #$01
a057: 85 cb                        sta     zpNoteRealLen           ;initialize note lengths (1, bc will be decremented before check)
a059: 85 cc                        sta     zpNoteRealLen+1
a05b: 85 cd                        sta     zpNoteRealLen+2
a05d: a9 7f                        lda     #$7f
a05f: 8d 01 40                     sta     SQ1_SWEEP               ;initialize (and disable) the APU sweep registers
a062: 8d 05 40                     sta     SQ2_SWEEP
a065: a9 07                        lda     #$07
a067: 85 c9                        sta     zpPlayStatus            ;record all three voices as active
a069: 8d 15 40                     sta     SND_CHN                 ;disable DMA and noise channels, enable triangle and the square waves
a06c: a9 0a                        lda     #NC_PlayMusic
a06e: 85 63                        sta     zpNMICMD                ;tell NMI handler to start playing the music
a070: 20 55 88     @playLp         jsr     StopIfStopKey           ;Stop if STOP is pressed
a073: a5 c9                        lda     zpPlayStatus            ;check status of player
a075: 30 10                        bmi     @playError              ; < 0? -> error occurred, handle
a077: d0 f7                        bne     @playLp                 ; > 0? -> still playing, keep looping
a079: a9 00                        lda     #NC_NoCmd               ;else, done playing
a07b: 85 63                        sta     zpNMICMD                ;disable NMI command
a07d: 8d 15 40                     sta     SND_CHN                 ;disable all sound channels
a080: 68                           pla                             ;restore TXTPTR to point at BASIC
a081: 85 0a                        sta     zpTXTPTR+1
a083: 68                           pla
a084: 85 09                        sta     zpTXTPTR
a086: 60           rts_A086        rts                             ;-> done!

a087: a9 00        @playError      lda     #NC_NoCmd               ;reset NMI handler command
a089: 85 63                        sta     zpNMICMD
a08b: 8d 15 40                     sta     SND_CHN                 ;turn off all sound channels
a08e: 4c 91 84                     jmp     ErrorSyntax             ; -> syntax error

                   NMICMD_PlayMusic
a091: 8a                           txa                             ;push X (value is 18)
a092: 48                           pha
a093: 98                           tya                             ;push Y
a094: 48                           pha
a095: a9 c0                        lda     #$c0
a097: 8d 17 40                     sta     JOY2                    ;synchronize APU frame counter
a09a: 20 a3 a0                     jsr     PM_FeedTheVoices
a09d: 68                           pla                             ;pull Y
a09e: a8                           tay
a09f: 68                           pla                             ;pull X
a0a0: aa                           tax
a0a1: 68                           pla                             ;pull A (pushed at start of NMI_DefaultHandler)
a0a2: 40                           rti                             ;-> exit NMI handling

                   PM_FeedTheVoices
a0a3: a9 02                        lda     #2
a0a5: 85 ca                        sta     zpCurVoice              ;start with third voice (voice 2)
a0a7: a9 04                        lda     #CHAN_MASK_TRI          ;(the triangle channel)
a0a9: 85 ce                        sta     zpCurVoxMask
a0ab: a5 c9        @voiceLp        lda     zpPlayStatus            ;did a syntax error occur?
a0ad: 30 09                        bmi     rts_A0B8                ;yes -> exit NMI so it can be handled by PLAY command
a0af: 20 b9 a0                     jsr     PM_FeedCurVoice         ;update this voice's state for where we are in the music
a0b2: 46 ce                        lsr     zpCurVoxMask
a0b4: c6 ca                        dec     zpCurVoice              ;next (lower) voice number
a0b6: 10 f3                        bpl     @voiceLp                ;-> loop back to handle voice, until all three handled
a0b8: 60           rts_A0B8        rts                             ;-> return and exit NMI handler

a0b9: a5 c9        PM_FeedCurVoice lda     zpPlayStatus
a0bb: 25 ce                        and     zpCurVoxMask            ;is the current voice still playing?
a0bd: f0 f9                        beq     rts_A0B8                ;no -> return, handle next voice
a0bf: a6 ca                        ldx     zpCurVoice
a0c1: d6 cb                        dec     zpNoteRealLen,x         ;is this voice currently playing a note or rest?
a0c3: d0 f3                        bne     rts_A0B8                ;yes -> return, nothing to process
a0c5: b5 c6                        lda     zpPlayVoxIdx,x
a0c7: 85 91                        sta     vZpPlayPtr              ;set play ptr to next PLAY char for this voice
a0c9: a9 00                        lda     #SHARP_UNUSED
a0cb: 85 c2                        sta     zpSharpVal              ;deactivate sharp by default
                   PM_DoCurPlayChar
a0cd: a0 00                        ldy     #$00
a0cf: b1 91                        lda     (vZpPlayPtr),y          ;get cur PLAY char. End of PLAY string for cur voice?
a0d1: d0 11                        bne     PM_ProcessCurPlayChar   ;not end -> handle the char
a0d3: a5 c9                        lda     zpPlayStatus
a0d5: 38                           sec
a0d6: e5 ce                        sbc     zpCurVoxMask
a0d8: 85 c9                        sta     zpPlayStatus
a0da: 8d 15 40                     sta     SND_CHN                 ;remove this channel's mask from zpPlayStatus
a0dd: a6 ca                        ldx     zpCurVoice
a0df: a5 91                        lda     vZpPlayPtr
a0e1: 95 c6                        sta     zpPlayVoxIdx,x          ;update this voice's buffer index (so it stays pointing at null char)
a0e3: 60                           rts                             ;-> and return, handle next voice

                   PM_ProcessCurPlayChar
a0e4: e6 91                        inc     vZpPlayPtr              ;increment PLAY ptr (next char)
a0e6: c9 20                        cmp     #$20                    ;was the play character a SPACE?
a0e8: f0 e3                        beq     PM_DoCurPlayChar        ;yes -> do nothing, handle next char
a0ea: c9 4d        PM_isCharM      cmp     #‘M’                    ;got 'M' (envelope)?
a0ec: d0 18                        bne     PM_isCharO              ;no -> check next
a0ee: b1 91                        lda     (vZpPlayPtr),y          ;get M arg
a0f0: f0 79                        beq     PM_SetSyntaxError       ;no arg? -> error
a0f2: a2 30                        ldx     #ENV_UNUSED             ;default (unused) value, assuming arg is '0'
a0f4: c9 30                        cmp     #‘0’
a0f6: f0 06                        beq     @setEnv
a0f8: c9 31                        cmp     #‘1’
a0fa: d0 6f                        bne     PM_SetSyntaxError       ;neither 0 nor 1? -> error
a0fc: a2 00                        ldx     #ENV_USED               ;mark envelope as used
a0fe: e6 91        @setEnv         inc     vZpPlayPtr              ;advance to next PLAY char
a100: a4 ca                        ldy     zpCurVoice
a102: 96 cf                        stx     zpEnvelope,y            ;set env use/don't use for this voice
a104: 10 c7                        bpl     PM_DoCurPlayChar        ;-> handle next PLAY char
a106: c9 4f        PM_isCharO      cmp     #‘O’                    ;got 'O' (octave)?
a108: d0 15                        bne     PM_isCharT              ;no -> check next
a10a: b1 91                        lda     (vZpPlayPtr),y          ;get O arg
a10c: f0 5d                        beq     PM_SetSyntaxError       ;no arg? -> error
a10e: e6 91                        inc     vZpPlayPtr              ;advance to next char
a110: c9 36                        cmp     #‘6’                    ; >= '6'?
a112: b0 57                        bcs     PM_SetSyntaxError       ;yes -> error
a114: 38                           sec
a115: e9 30                        sbc     #‘0’                    ;convert from ASCII digit, to binary number
a117: 90 52                        bcc     PM_SetSyntaxError       ; < 0? -> error
a119: a6 ca                        ldx     zpCurVoice
a11b: 95 bf                        sta     zpOctave,x              ;set the octave value for cur voice
a11d: 10 ae                        bpl     PM_DoCurPlayChar        ;-> handle next PLAY char
a11f: c9 54        PM_isCharT      cmp     #‘T’                    ;got 'T' (tempo)?
a121: d0 17                        bne     PM_isCharV              ;no -> check next
a123: b1 91                        lda     (vZpPlayPtr),y          ;get T arg
a125: f0 44                        beq     PM_SetSyntaxError       ;no arg? -> error
a127: e6 91                        inc     vZpPlayPtr              ;advance to next PLAY char
a129: c9 39                        cmp     #‘9’                    ;arg char too high to be a digit?
a12b: b0 3e                        bcs     PM_SetSyntaxError       ;yes -> error
a12d: 38                           sec
a12e: e9 30                        sbc     #‘0’                    ;convert from ASCII digit, to binary number
a130: f0 39                        beq     PM_SetSyntaxError       ; <= 0? -> error
a132: 90 37                        bcc     PM_SetSyntaxError
a134: a6 ca                        ldx     zpCurVoice
a136: 95 b9                        sta     zpTempo,x               ;set tempo for current voice
                   pmDoCurPlayChar_A138
a138: 10 93                        bpl     PM_DoCurPlayChar        ;-> handle next PLAY char
a13a: c9 56        PM_isCharV      cmp     #‘V’                    ;got 'V' (volume, or envelope length)?
a13c: d0 32                        bne     PM_isCharY              ;no -> check next
a13e: b1 91                        lda     (vZpPlayPtr),y          ;get first char of V arg
a140: f0 29                        beq     PM_SetSyntaxError       ;no arg? -> error
a142: e6 91                        inc     vZpPlayPtr              ;advance next PLAY char
a144: c9 3a                        cmp     #‘:’
a146: b0 23                        bcs     PM_SetSyntaxError       ; > '9'? -> error
a148: 38                           sec
a149: e9 30                        sbc     #$30                    ;convert from ASCII digit, to binary number
a14b: 90 1e                        bcc     PM_SetSyntaxError       ; < 0? -> error
a14d: c9 01                        cmp     #1                      ;was first char a '1'?
a14f: d0 14                        bne     @setVol                 ;no -> just a single-digit arg for V, then
a151: b1 91                        lda     (vZpPlayPtr),y          ;else, check for a 2nd digit
a153: c9 36                        cmp     #‘6’                    ;char value >= '6'?
a155: b0 0c                        bcs     @setVolOne              ;yes -> not part of our arg; so, just '1'
a157: 38                           sec
a158: e9 30                        sbc     #$30                    ;convert from ASCII code, to binary number of digit
a15a: 90 07                        bcc     @setVolOne              ;char < '0'? -> not part of arg, so just '1'
a15c: 18                           clc
a15d: 69 0a                        adc     #$0a                    ;the first '1' was a tens digit. Add ten to value from 2nd digit.
a15f: e6 91                        inc     vZpPlayPtr              ;advance to next PLAY char
a161: d0 02                        bne     @setVol                 ;-> set the volume
a163: a9 01        @setVolOne      lda     #$01
a165: a6 ca        @setVol         ldx     zpCurVoice
a167: 95 bc                        sta     zpVolume,x              ;set volume (or envelope) to user-specified level
a169: 10 cd                        bpl     pmDoCurPlayChar_A138    ;-> handle next PLAY char
                   PM_SetSyntaxError
a16b: a9 80                        lda     #$80
a16d: 85 c9                        sta     zpPlayStatus
a16f: 60                           rts

a170: c9 59        PM_isCharY      cmp     #‘Y’                    ;got 'Y' (duty)?
a172: d0 16                        bne     PM_isCharSharp          ;no -> check next
a174: b1 91                        lda     (vZpPlayPtr),y          ;get Y arg
a176: c9 34                        cmp     #‘4’                    ; >= '4'?
a178: b0 f1                        bcs     PM_SetSyntaxError       ;yes -> error
a17a: 38                           sec
a17b: e9 30                        sbc     #‘0’                    ;convert from ASCII code, to binary number
a17d: 90 ec                        bcc     PM_SetSyntaxError       ; <= 0? -> error
a17f: e6 91                        inc     vZpPlayPtr              ;advance to next PLAY char
a181: 4a                           lsr     A
a182: 6a                           ror     A
a183: 6a                           ror     A                       ;shift left 3 places, for use as value in APU volume register
a184: a6 ca                        ldx     zpCurVoice
a186: 95 d2                        sta     zpDuty,x                ;save as "duty" value
a188: 10 ae                        bpl     pmDoCurPlayChar_A138    ;-> handle next PLAY char
a18a: c9 23        PM_isCharSharp  cmp     #‘#’                    ;got '#'?
a18c: d0 06                        bne     PM_isCharR              ;no -> check next
a18e: a9 02                        lda     #SHARP_USED
a190: 85 c2                        sta     zpSharpVal              ;mark next note name as sharp
a192: 10 a4                        bpl     pmDoCurPlayChar_A138    ;-> handle next PLAY char

a194: c9 52        PM_isCharR      cmp     #‘R’                    ;got 'R'? (rest)
a196: d0 14                        bne     PM_isCharNoteName       ;no -> check next
a198: 20 0f a2                     jsr     PM_calcNoteLen
a19b: a5 ca                        lda     zpCurVoice              ;take the current voice #
a19d: 0a                           asl     A                       ;multiply by 4, to get an APU register index
a19e: 0a                           asl     A
a19f: a8                           tay                             ;set as the Y-reg value
a1a0: a9 90                        lda     #$90                    ;silence value for square channels
a1a2: c0 08                        cpy     #$08                    ;are we on the triangle channel?
a1a4: d0 02                        bne     @setVol                 ;no -> keep square silence value
a1a6: a9 00                        lda     #$00                    ;yes -> use silence value for triangle channel
a1a8: 99 00 40     @setVol         sta     SQ1_VOL,y               ;set silence for current voice's sound channel
a1ab: 60                           rts                             ;-> return

                   PM_isCharNoteName
a1ac: c9 41                        cmp     #‘A’                    ;char >= 'A'?
a1ae: 90 bb                        bcc     PM_SetSyntaxError       ;no -> syntax error (unrecognized char)
a1b0: c9 48                        cmp     #‘H’                    ;char > 'G'?
a1b2: b0 b7                        bcs     PM_SetSyntaxError       ;yes -> syntax error
a1b4: 48                           pha                             ;push char value
a1b5: 20 0f a2                     jsr     PM_calcNoteLen          ;get the note length, adjusted by tempo
a1b8: a6 ca                        ldx     zpCurVoice              ;get the current voice number
a1ba: 8a                           txa
a1bb: 0a                           asl     A                       ;multiply by 4, for APU register index
a1bc: 0a                           asl     A
a1bd: a8                           tay
a1be: c9 08                        cmp     #$08                    ;is this the triangle channel?
a1c0: d0 17                        bne     @square                 ;no -> handle for squarewave channel
a1c2: b5 cb                        lda     zpNoteRealLen,x         ;get the tempo-adjusted note length
a1c4: 38                           sec
a1c5: e9 01                        sbc     #$01                    ;subtract 1
a1c7: c9 0e                        cmp     #14                     ;less than 14?
a1c9: 90 02                        bcc     @notMax                 ;yes -> just use the value
a1cb: a9 0e                        lda     #14                     ;else, use max of 14
a1cd: 0a           @notMax         asl     A                       ;shift left 2 places
a1ce: 0a                           asl     A
a1cf: b4 cf                        ldy     zpEnvelope,x            ;are we using "envelope" for the triangle channel?
a1d1: f0 02                        beq     @keepEnv                ;yes, using -> use our note-len-based value
a1d3: a9 81                        lda     #$81                    ;no, not using: set control flag, and set counter reload to 1
a1d5: a0 08        @keepEnv        ldy     #$08
a1d7: d0 06                        bne     @setVol

a1d9: b5 bc        @square         lda     zpVolume,x              ;merge the duty, envelope, and volume values
a1db: 15 d2                        ora     zpDuty,x
a1dd: 15 cf                        ora     zpEnvelope,x
a1df: 99 00 40     @setVol         sta     SQ1_VOL,y               ;write duty, envelope, and volume to APU register
a1e2: 68                           pla                             ;pull the note name ('A' to 'G')
a1e3: 38                           sec
a1e4: e9 41                        sbc     #‘A’                    ;convert A-G -> 0-6
a1e6: a8                           tay                             ;set as Y-reg value
a1e7: b5 bf                        lda     zpOctave,x              ;get voice's current octave
a1e9: aa                           tax
a1ea: bd b4 80                     lda     tbl_OctaveBaseNoteVals,x ;get base note value for the current octave
a1ed: 18                           clc
a1ee: 79 ad 80                     adc     tbl_NoteNameToValue,y   ;add to that, the value for the note name
a1f1: 65 c2                        adc     zpSharpVal              ;add 2 if it's sharped
a1f3: aa                           tax                             ;save note value in X reg
a1f4: a5 ca                        lda     zpCurVoice              ;get current voice
a1f6: 0a                           asl     A                       ;times 4 for APU register index
a1f7: 0a                           asl     A
a1f8: a8                           tay                             ;save in Y reg
a1f9: c9 08                        cmp     #$08                    ;triangle channel?
a1fb: d0 05                        bne     @sq                     ;no -> use note value as-is for square waves
a1fd: 8a                           txa                             ;yes, triangle: add one octave to get the actual note we'll use
a1fe: 18                           clc
a1ff: 69 18                        adc     #NOTE_VAL_OCTAVE
a201: aa                           tax
a202: bd 13 80     @sq             lda     tbl_NoteValToPdLen,x    ;get the period length for the note value,
a205: 99 02 40                     sta     SQ1_LO,y                ; and set the appropriate APU registers to trigger
a208: bd 14 80                     lda     tbl_NoteValToPdLen+1,x  ; our note's pitch
a20b: 99 03 40                     sta     SQ1_HI,y
a20e: 60                           rts                             ;-> return

a20f: b1 91        PM_calcNoteLen  lda     (vZpPlayPtr),y          ;load the next char after note name (or 'R')
a211: f0 13                        beq     @parseDone              ;no char after? -> done parsing
a213: c9 3a                        cmp     #‘:’                    ; > '9'?
a215: b0 0f                        bcs     @parseDone              ;yes -> not a note length, done parsing
a217: 38                           sec
a218: e9 30                        sbc     #‘0’                    ;convert from ASCII digit code, to binary number
a21a: 90 0a                        bcc     @parseDone              ; < 0? -> not a note length, done parsing
a21c: aa                           tax
a21d: bd a3 80                     lda     tbl_NoteLengths,x       ;convert number to a note length (from table)
a220: a6 ca                        ldx     zpCurVoice
a222: 95 c3                        sta     zpNoteLen,x             ;set note length for this voice
a224: e6 91                        inc     vZpPlayPtr              ;advance to next PLAY char
a226: 20 ce a7     @parseDone      jsr     ClearOperandHiBytes
a229: a6 ca                        ldx     zpCurVoice
a22b: b5 c3                        lda     zpNoteLen,x             ;stick note length into WAccum (single byte)
a22d: 85 28                        sta     zpWAccum
a22f: b5 b9                        lda     zpTempo,x
a231: 85 2c                        sta     zpWParam                ;stick voice's tempo mark into WParam (single byte)
a233: 20 47 8f                     jsr     WordMultiply            ;multiply these
a236: a5 28                        lda     zpWAccum
a238: a6 ca                        ldx     zpCurVoice
a23a: 95 cb                        sta     zpNoteRealLen,x         ;set it as the "real" note length
a23c: a5 91                        lda     vZpPlayPtr
a23e: 95 c6                        sta     zpPlayVoxIdx,x          ;update the PLAY buffer index for this voice
a240: 60                           rts                             ;-> and return

                   PLAY_copyVoiceToGenPurpBuf
a241: ad 00 04                     lda     exprParseStk            ;end of string?
a244: f0 12                        beq     @nullTerm               ;yes -> null-terminate and return
a246: ce 00 04                     dec     exprParseStk            ;decrement string length
a249: 20 88 90                     jsr     TxtPtrGetCharAndIncr    ;get a char from the string (and increment pointer)
a24c: c9 3a                        cmp     #‘:’                    ;colon? -> null-terminate and return
a24e: f0 08                        beq     @nullTerm
a250: a0 00                        ldy     #$00
a252: 91 91                        sta     (vZpPlayPtr),y          ;copy it into general-purpose buffer
a254: e6 91                        inc     vZpPlayPtr              ;advance genPurposeBuf pointer
a256: d0 e9                        bne     PLAY_copyVoiceToGenPurpBuf ;(always) -> loop around to copy next character
a258: a0 00        @nullTerm       ldy     #$00
a25a: a9 00                        lda     #$00
a25c: 91 91                        sta     (vZpPlayPtr),y          ;terminate with a null char
a25e: 60                           rts

                   • Clear variables

a25f: 4c 91 84     errSyn_A25F     jmp     ErrorSyntax

a262: 4c 94 84     errIllVal_A262  jmp     ErrorIllegalValue

                   AdvThenEvalByteInt
a265: 20 8c 90                     jsr     TxtPtrIncr
a268: 4c 6e a2                     jmp     EvalByteInteger

                   EvalCommaThenByteInt
a26b: 20 14 ac                     jsr     SkipCommaOrSynErr
a26e: 20 83 a2     EvalByteInteger jsr     FrmEval
a271: 20 0d aa                     jsr     ErrorUnlessInteger
a274: ad 01 04                     lda     exprParseStk+1          ;anything in the high byte?
a277: d0 e9                        bne     errIllVal_A262          ;We're looking for values that fit in a byte
a279: ad 00 04                     lda     exprParseStk
a27c: 60                           rts

a27d: 20 83 a2     EvalString      jsr     FrmEval
a280: 4c 10 aa                     jmp     ErrorUnlessString

                   ; Evaluate the formula at the current location in the BASIC program tokenized
                   ; text.
a283: a9 00        FrmEval         lda     #$00
a285: 85 27                        sta     zpArgTyp
a287: 85 30                        sta     zpExprStkIdx
a289: a9 80                        lda     #$80
a28b: 85 21                        sta     zpGpStrIdx
a28d: 20 9b a2                     jsr     Expression
a290: a5 27                        lda     zpArgTyp
a292: f0 cb                        beq     errSyn_A25F
a294: 60                           rts

                   EnsureIntAndSkipToken
a295: 20 0d aa                     jsr     ErrorUnlessInteger
a298: 4c 8c 90                     jmp     TxtPtrIncr

                   ; The formula evaluator does a recursive parser thing, instead of operator
                   ; precedence. Most of these parsers are in page $A2. One first calls Expression
                   ; (this fn), which calls ExprOR, which calls ExprAND, etc, until it gets about 9
                   ; calls deep and finally hits ExprUnaryOrLiteral, which processes unary ops,
                   ; vars, and literals, and then if it finds a binary op it sets the accumulator
                   ; to that op's token code, and falls back up the chain, letting one of the
                   ; recursive handlers deal with it. This technique is easier to code, but not
                   ; terribly efficient.
                   ; 
                   ; Expression itself (this fn) directly handles XOR expressions (the lowest-
                   ; precedence binary op), delegating to ExprOr (and below) for any higher-
                   ; precedence op than XOR.
a29b: 20 be a2     Expression      jsr     ExprOr                  ;process more tightly-bound operations
a29e: c9 ef        AgainXor        cmp     #tok_OP_XOR             ;we at an XOR token?
a2a0: f0 01                        beq     @xor                    ;yes -> handle
a2a2: 60                           rts                             ;no: all done evaluating expressions!

a2a3: 20 95 a2     @xor            jsr     EnsureIntAndSkipToken   ;expect int (boolean)
a2a6: 20 be a2                     jsr     ExprOr                  ;eval 2nd operand
a2a9: 20 c1 a3                     jsr     IntPullWParamGetWAccum  ;expect that to be an int too, and prep args
a2ac: a5 28                        lda     zpWAccum
a2ae: 45 2c                        eor     zpWParam                ;XOR the low bytes
a2b0: 85 28                        sta     zpWAccum
a2b2: a5 29                        lda     zpWAccum+1
a2b4: 45 2d                        eor     zpWParam+1              ;XOR the high bytes
a2b6: 85 29                        sta     zpWAccum+1
a2b8: 20 d8 a7                     jsr     ReplaceExprStkTopWAccum ;replace top stack item
a2bb: 4c 9e a2                     jmp     AgainXor                ;and go evaluate any further expressions

                   ; Parse an OR expression, or an expression of higher precedence.
a2be: 20 e1 a2     ExprOr          jsr     ExprAnd                 ;process more tightly-bound operations
a2c1: c9 f0        AgainOr         cmp     #tok_OP_OR              ;we at an OR token?
a2c3: f0 01                        beq     @or                     ;yes -> handle
a2c5: 60                           rts                             ;no OR; wind our way up to lower-precedence ops

a2c6: 20 95 a2     @or             jsr     EnsureIntAndSkipToken   ;expect int (boolean)
a2c9: 20 e1 a2                     jsr     ExprAnd                 ;eval 2nd operand
a2cc: 20 c1 a3                     jsr     IntPullWParamGetWAccum  ;expect that to be an int too, and prep args
a2cf: a5 28                        lda     zpWAccum
a2d1: 05 2c                        ora     zpWParam                ;OR the low bytes
a2d3: 85 28                        sta     zpWAccum
a2d5: a5 29                        lda     zpWAccum+1
a2d7: 05 2d                        ora     zpWParam+1              ;OR the high bytes
a2d9: 85 29                        sta     zpWAccum+1
a2db: 20 d8 a7                     jsr     ReplaceExprStkTopWAccum ;replace top stack item
a2de: 4c c1 a2                     jmp     AgainOr                 ;and go check for a proceeding OR expression

                   ; Parse an AND expression, or an expression of higher precedence.
a2e1: 20 04 a3     ExprAnd         jsr     ExprRelOp               ;process more tightly-bound operations
a2e4: c9 f1        AgainAnd        cmp     #tok_OP_AND             ;we at an AND token?
a2e6: f0 01                        beq     @and                    ;yes -> handle
a2e8: 60                           rts                             ;no AND; wind our way up to lower-precedence ops

a2e9: 20 95 a2     @and            jsr     EnsureIntAndSkipToken   ;expect int (boolean)
a2ec: 20 04 a3                     jsr     ExprRelOp               ;eval 2nd operand
a2ef: 20 c1 a3                     jsr     IntPullWParamGetWAccum  ;expect that to be an int too, and prep args
a2f2: a5 28                        lda     zpWAccum
a2f4: 25 2c                        and     zpWParam                ;AND the low bytes
a2f6: 85 28                        sta     zpWAccum
a2f8: a5 29                        lda     zpWAccum+1
a2fa: 25 2d                        and     zpWParam+1              ;AND the high bytes
a2fc: 85 29                        sta     zpWAccum+1
a2fe: 20 d8 a7                     jsr     ReplaceExprStkTopWAccum ;replace top stack item
a301: 4c e4 a2                     jmp     AgainAnd                ;and go check for a proceeding AND expression

                   ; Parse a relational expression (<>, >=, <=, =, >, <) or an expression
                   ; of higher precedence.
a304: 20 41 a3     ExprRelOp       jsr     ExprPlusMinus           ;process more tightly-bound operations
a307: c9 f3        AgainRelOp      cmp     #tok_OP_NE              ;is the next byte a token greater or equal to '<>'?
a309: b0 01                        bcs     @maybeRel               ;yes, check more
a30b: 60           rts_A30B        rts                             ;no rel op; wind our way up to lower-precedence ops

a30c: c9 f9        @maybeRel       cmp     #tok_OP_PLUS            ;is it a token >= '+'?
a30e: b0 fb                        bcs     rts_A30B                ;yes -> not a rel op
a310: 38                           sec
a311: e9 f3                        sbc     #tok_OP_NE              ;subtract '<>' tok val and multiply by 2
a313: 0a                           asl     A                       ; to get an index
a314: aa                           tax
a315: bd 6e a8                     lda     jtbl_RelOps,x           ;load and push ptr to this op's handling routine
a318: 48                           pha
a319: bd 6f a8                     lda     jtbl_RelOps+1,x
a31c: 48                           pha
a31d: a5 27                        lda     zpArgTyp                ;also push type of 1st operand
a31f: 48                           pha
a320: 20 8c 90                     jsr     TxtPtrIncr              ;move past operator token
a323: 20 41 a3                     jsr     ExprPlusMinus           ;and eval 2nd operand (as any more tightly-bound expr)
a326: 68                           pla                             ;pull and compare 1st operand type
a327: c5 27                        cmp     zpArgTyp                ;diff from 2nd operand's type?
a329: d0 45                        bne     errTypeMism_A370        ;yes -> error
a32b: 20 c4 a3                     jsr     PullWParamGetWAccum     ;get args into WAccum, WParam
a32e: 68                           pla
a32f: 85 1a                        sta     zpMisc+1                ;pull handler routine and place in zpMisc
a331: 68                           pla
a332: 85 19                        sta     zpMisc
a334: 20 3e a3                     jsr     JmpIndirectZpMisc       ;execute!
a337: a2 02                        ldx     #TYP_INT                ;no matter what type args had, result is int (0 or -1)
a339: 86 27                        stx     zpArgTyp
a33b: 4c 07 a3                     jmp     AgainRelOp              ;check for any more rel ops to eval

                   JmpIndirectZpMisc
a33e: 6c 19 00                     jmp     (zpMisc)

                   ; Parse an addition or subtraction expression, or an expression of higher
                   ; precedence.
a341: 20 73 a3     ExprPlusMinus   jsr     ExprMod                 ;process more tightly-bound operations
a344: c9 f9        AgainPlusMinus  cmp     #tok_OP_PLUS            ;next token == '+'?
a346: f0 05                        beq     @add                    ;yes -> handle add
a348: c9 fa                        cmp     #tok_OP_MINUS           ;else, next token == '-'?
a34a: f0 0a                        beq     @sub                    ;yes -> handle subtract
a34c: 60                           rts                             ;no add or subtract; wind our way up to lower-precedence ops

a34d: 20 5f a3     @add            jsr     @prepArgs               ;get args from stack into WAccum, WParam
a350: 20 d5 a7                     jsr     AddAndReplaceExpr       ;do the add
a353: 4c 44 a3                     jmp     AgainPlusMinus          ;and any other add/subtract operations that immediately follow

a356: 20 5f a3     @sub            jsr     @prepArgs               ;get args from stack into WAccum, WParam
a359: 20 ef a7                     jsr     SubtractAndReplaceExpr  ;do the subtract
a35c: 4c 44 a3                     jmp     AgainPlusMinus          ;and any other add/subtract operations that immediately follow

a35f: a5 27        @prepArgs       lda     zpArgTyp                ;get/push arg type before evaluating 2nd arg expr
a361: 48                           pha
a362: 20 8c 90                     jsr     TxtPtrIncr              ;move past '+' or '-' operator
a365: 20 73 a3                     jsr     ExprMod                 ;eval 2nd operand (as any more tightly-bound expr)
a368: 68                           pla                             ;pull type of preceding operand
a369: c5 27                        cmp     zpArgTyp                ;do operand types differ?
a36b: d0 03                        bne     errTypeMism_A370        ;yes -> error
a36d: 4c c4 a3                     jmp     PullWParamGetWAccum     ;get operands into WAccum, WParam, and return

                   errTypeMism_A370
a370: 4c ff a9                     jmp     ErrorTypeMismatch

                   ; Parse a modulo expression, or an expression of higher precedence.
a373: 20 92 a3     ExprMod         jsr     ExprMulDiv              ;process more tightly-bound operations
a376: c9 fb        AgainMod        cmp     #tok_OP_MOD             ;is the next token a MOD?
a378: f0 01                        beq     @mod                    ;yes -> perform modulo op
a37a: 60                           rts                             ;no mod; wind our way up to lower-precedence ops

a37b: a5 27        @mod            lda     zpArgTyp                ;get/push arg type before evaluating 2nd arg expr
a37d: 48                           pha
a37e: 20 8c 90                     jsr     TxtPtrIncr              ;move past MOD operator
a381: 20 92 a3                     jsr     ExprMulDiv              ;eval 2nd operand (as any more tightly-bound expr)
a384: 68                           pla                             ;pull type of preceding operand
a385: c5 27                        cmp     zpArgTyp                ;do operand types differ?
a387: d0 e7                        bne     errTypeMism_A370        ;yes -> error
a389: 20 c4 a3                     jsr     PullWParamGetWAccum     ;get operands into WAccum, WParam
a38c: 20 0e a8                     jsr     ModAndReplaceExpr       ;perform modulo
a38f: 4c 76 a3                     jmp     AgainMod                ;and any other modulo operations that immediately follow

                   ; Parse a multiplication or division expression, or an expression of higher
                   ; precedence.
a392: 20 90 a4     ExprMulDiv      jsr     ExprLiteralUnaryOrFn    ;parse literal, unary, or function expr
a395: c9 fc        AgainMulDiv     cmp     #tok_OP_DIV             ;is the next token a '/'?
a397: f0 05                        beq     @div                    ;yes -> perform division
a399: c9 fd                        cmp     #tok_OP_MUL             ;or maybe it's a '*'?
a39b: f0 0a                        beq     @mul                    ;yes -> perform multiplication
a39d: 60                           rts                             ;no mul or div; wind our way up to lower-precedence ops

a39e: 20 b0 a3     @div            jsr     @prepArgs               ;get args from stack into WAccum, WParam
a3a1: 20 fb a7                     jsr     DivAndReplaceExpr       ;perform the division
a3a4: 4c 95 a3                     jmp     AgainMulDiv             ;perform any immediately following mul or div

a3a7: 20 b0 a3     @mul            jsr     @prepArgs               ;get args from stack into WAccum, WParam
a3aa: 20 f5 a7                     jsr     MulAndReplaceExpr       ;perform the multiplication
a3ad: 4c 95 a3                     jmp     AgainMulDiv             ;perform any immediately following mul or div

a3b0: a5 27        @prepArgs       lda     zpArgTyp                ;get/push arg type before evaluating 2nd arg expr
a3b2: 48                           pha
a3b3: 20 8c 90                     jsr     TxtPtrIncr              ;move past '/' operator
a3b6: 20 90 a4                     jsr     ExprLiteralUnaryOrFn    ;eval 2nd operand
a3b9: 68                           pla                             ;pull 1st arg's type and compare
a3ba: c5 27                        cmp     zpArgTyp                ;do operand types differ?
a3bc: d0 b2                        bne     errTypeMism_A370        ;yes -> error
a3be: 4c c4 a3                     jmp     PullWParamGetWAccum     ;get operands into WAccum, WParam

                   IntPullWParamGetWAccum
a3c1: 20 0d aa                     jsr     ErrorUnlessInteger
                   PullWParamGetWAccum
a3c4: a6 30                        ldx     zpExprStkIdx            ;get parse stack position
a3c6: e0 7d                        cpx     #$7d                    ;less than 3 from the end?
a3c8: b0 3d                        bcs     errIll_A407             ;yes -> error... shouldn't this be out of memory,
a3ca: ca                           dex                             ; instead of illegal? also we're literally about to
a3cb: ca                           dex                             ; reduce the stack size by 3, so why do we even
a3cc: ca                           dex                             ; need this check?
a3cd: 86 30                        stx     zpExprStkIdx            ;pull last result off of stack, and place in WParam
a3cf: bd 00 04                     lda     exprParseStk,x
a3d2: 85 2c                        sta     zpWParam
a3d4: bd 01 04                     lda     exprParseStk+1,x
a3d7: 85 2d                        sta     zpWParam+1
a3d9: bd 02 04                     lda     exprParseStk+2,x
a3dc: 85 2e                        sta     zpWParam+2
a3de: ca                           dex
a3df: ca                           dex
a3e0: ca                           dex
a3e1: bd 00 04                     lda     exprParseStk,x          ;get preceding result off expr stack, place in WAccum
a3e4: 85 28                        sta     zpWAccum                ;doesn't pull - caller will typically replace
a3e6: bd 01 04                     lda     exprParseStk+1,x        ; with its result
a3e9: 85 29                        sta     zpWAccum+1
a3eb: bd 02 04                     lda     exprParseStk+2,x
a3ee: 85 2a                        sta     zpWAccum+2
a3f0: 60                           rts

                   ; Evaluate the following unary or literal expression, and then copy the
                   ; evaluated result into WAccum
                   EvalUnaryIntoWAccum
a3f1: 20 8c 90                     jsr     TxtPtrIncr              ;advance to next token char
a3f4: 20 90 a4                     jsr     ExprLiteralUnaryOrFn    ;parse whatever's there
a3f7: a6 30                        ldx     zpExprStkIdx
a3f9: ca                           dex
a3fa: ca                           dex
a3fb: ca                           dex
a3fc: bd 00 04                     lda     exprParseStk,x
a3ff: 85 28                        sta     zpWAccum                ;move evaluation result into zpWAccum
a401: bd 01 04                     lda     exprParseStk+1,x        ;(result still remains on expression stack)
a404: 85 29                        sta     zpWAccum+1
a406: 60                           rts

a407: 4c 94 84     errIll_A407     jmp     ErrorIllegalValue

a40a: 20 f1 a3     DoExprNegate    jsr     EvalUnaryIntoWAccum     ;get following atomic expression into WAccum
a40d: 20 32 8e                     jsr     WAccumNegate            ;and negate it
a410: a5 28                        lda     zpWAccum                ;copy result to parsed-expression stack
a412: 9d 00 04                     sta     exprParseStk,x
a415: a5 29                        lda     zpWAccum+1
a417: 9d 01 04                     sta     exprParseStk+1,x
a41a: 4c bf 90                     jmp     TxtSkipSpaces

a41d: 20 f1 a3     DoExprNot       jsr     EvalUnaryIntoWAccum
a420: a9 ff                        lda     #$ff
a422: 38                           sec
a423: fd 00 04                     sbc     exprParseStk,x
a426: 9d 00 04                     sta     exprParseStk,x
a429: a9 ff                        lda     #$ff
a42b: 38                           sec
a42c: fd 01 04                     sbc     exprParseStk+1,x
a42f: 9d 01 04                     sta     exprParseStk+1,x
a432: 4c 0d a5                     jmp     SetIntTypAndSkipSpc

                   vZpSavX         .var    $19    {addr/1}
                   vZpSavY         .var    $90    {addr/1}

a435: 20 8c 90     ParseStrLit     jsr     TxtPtrIncr
a438: a0 00                        ldy     #$00
a43a: a6 21                        ldx     zpGpStrIdx
a43c: b1 09        @cpyLp          lda     (zpTXTPTR),y            ;copy literal contents into genPurposeBuf as temp str
a43e: f0 13                        beq     @eol                    ;end of line? -> done copying
a440: c9 22                        cmp     #‘"’                    ;end of string literal found?
a442: f0 0c                        beq     @afterCopy              ;yes -> package up for exprParseStk
a444: 9d 00 07                     sta     genPurposeBuf,x
a447: e8                           inx
a448: c8                           iny
a449: c0 20                        cpy     #$20                    ;32 chars reached?
a44b: f0 ba                        beq     errIll_A407             ;yes -> illegal
a44d: 4c 3c a4                     jmp     @cpyLp

a450: 20 8c 90     @afterCopy      jsr     TxtPtrIncr
a453: 86 19        @eol            stx     vZpSavX
a455: 84 90                        sty     vZpSavY
a457: 98                           tya
a458: 18                           clc
a459: 65 09                        adc     zpTXTPTR
a45b: 85 09                        sta     zpTXTPTR                ;advance zpTXTPTR past the end of string literal
a45d: a9 00                        lda     #$00
a45f: 65 0a                        adc     zpTXTPTR+1
a461: 85 0a                        sta     zpTXTPTR+1
a463: a6 30                        ldx     zpExprStkIdx
a465: a5 90                        lda     vZpSavY                 ;push str len to parse stack
a467: 9d 00 04                     sta     exprParseStk,x
a46a: a5 21                        lda     zpGpStrIdx              ;push pointer to tmp string on parse stack
a46c: 9d 01 04                     sta     exprParseStk+1,x
a46f: a9 07                        lda     #>genPurposeBuf
a471: 9d 02 04                     sta     exprParseStk+2,x
a474: e8                           inx
a475: e8                           inx
a476: e8                           inx
a477: 86 30                        stx     zpExprStkIdx            ;update stack pointer past newly-pushed stuff
a479: a5 19                        lda     vZpSavX
a47b: 85 21                        sta     zpGpStrIdx              ;update temp string index past what we copied in
a47d: a9 03                        lda     #TYP_STR                ;indicate cur expr is string
a47f: 85 27                        sta     zpArgTyp
a481: 4c bf 90                     jmp     TxtSkipSpaces

                   • Clear variables

a484: 4c 91 84     errSyn_A484     jmp     ErrorSyntax

a487: 4c 1d a4     doExprNot_A487  jmp     DoExprNot

                   doExprNegate_A48A
a48a: 4c 0a a4                     jmp     DoExprNegate

a48d: 20 8c 90     txtPtrIncr_A48D jsr     TxtPtrIncr
                   ; Handles literals, unary operators (-, +, NOT, etc), and literals.
                   ExprLiteralUnaryOrFn
a490: 20 9c 84                     jsr     IsEndOfCmd              ;is this the end of the command ('\0' or ':')?
a493: f0 ef                        beq     errSyn_A484             ;yes -> syntax error (we're expecting an op or atom!)
a495: c9 0b                        cmp     #11                     ;have a single-digit number?
a497: 90 7b                        bcc     ParseSingleDigitNum
a499: c9 fa                        cmp     #tok_OP_MINUS           ;unary '-'?
a49b: f0 ed                        beq     doExprNegate_A48A
a49d: c9 f2                        cmp     #tok_OP_NOT             ;NOT unary op?
a49f: f0 e6                        beq     doExprNot_A487
a4a1: c9 12                        cmp     #tok_NUM_AFTER          ;decimal number?
a4a3: f0 51                        beq     ParseNumLiteral
a4a5: c9 11                        cmp     #tok_HEX_AFTER          ;hex number?
a4a7: f0 4d                        beq     ParseNumLiteral
a4a9: c9 f9                        cmp     #tok_OP_PLUS            ;unary '+'?
a4ab: f0 e0                        beq     txtPtrIncr_A48D         ;yes -> (no effect - just incr zpTXTPTR!)
a4ad: c9 28                        cmp     #‘(’                    ;parenthesized expression?
a4af: f0 39                        beq     DoExprParens
a4b1: c9 22                        cmp     #‘"’                    ;string literal?
a4b3: f0 80                        beq     ParseStrLit
a4b5: c9 41                        cmp     #‘A’                    ;punctuation or raw digits?
a4b7: 90 2b                        bcc     errSyn_A4E4             ;matched -> syntax error
a4b9: c9 5b                        cmp     #‘[’                    ;'A' thru 'Z'?
a4bb: 90 6f                        bcc     DoExprVarName           ;yes -> handle (presumed) var name
a4bd: c9 a1                        cmp     #tok_MOVE               ;MOVE fn?
a4bf: f0 26                        beq     exprMove_A4E7           ;-> eval that
a4c1: c9 ca                        cmp     #tok_ABS
a4c3: 90 1f                        bcc     errSyn_A4E4             ;error unless we have a token >= ABS but < XOR
a4c5: c9 ef                        cmp     #tok_OP_XOR             ;(BASIC built-in functions)
a4c7: b0 1b                        bcs     errSyn_A4E4
a4c9: 38                           sec
a4ca: e9 ca                        sbc     #tok_ABS                ;subtract value of tok_ABS
a4cc: 0a                           asl     A                       ;and double for pointer index
a4cd: aa                           tax
a4ce: bd 41 cf                     lda     jtbl_Functions,x        ;lookup the function, evaluate it, and return
a4d1: 85 19                        sta     zpMisc
a4d3: bd 42 cf                     lda     jtbl_Functions+1,x
a4d6: 85 1a                        sta     zpMisc+1
a4d8: 20 8c 90                     jsr     TxtPtrIncr              ;move TXTPTR past function token
a4db: 20 bf 90                     jsr     TxtSkipSpaces
a4de: 20 3e a3                     jsr     JmpIndirectZpMisc
a4e1: 4c bf 90                     jmp     TxtSkipSpaces

a4e4: 4c 91 84     errSyn_A4E4     jmp     ErrorSyntax

a4e7: 4c 8e a8     exprMove_A4E7   jmp     ExprMove

a4ea: 20 8c 90     DoExprParens    jsr     TxtPtrIncr
a4ed: 20 9b a2                     jsr     Expression
a4f0: 20 7d a8                     jsr     SkipRParenOrSynErr
a4f3: 4c bf 90                     jmp     TxtSkipSpaces

a4f6: a6 30        ParseNumLiteral ldx     zpExprStkIdx
a4f8: a0 01                        ldy     #$01
a4fa: b1 09                        lda     (zpTXTPTR),y
a4fc: 9d 00 04                     sta     exprParseStk,x          ;move numeric literal directly onto parse stack
a4ff: c8                           iny                             ;(not including leading token byte)
a500: e8                           inx
a501: b1 09                        lda     (zpTXTPTR),y
a503: 9d 00 04                     sta     exprParseStk,x
a506: e8                           inx                             ;increment index ("push" the literal onto the expr stack)
a507: e8                           inx
a508: 86 30                        stx     zpExprStkIdx
a50a: 20 10 9f                     jsr     TxtPtrAdvThree          ;advance zpTXTPTR past the literal
                   SetIntTypAndSkipSpc
a50d: a9 02                        lda     #TYP_INT
a50f: 85 27                        sta     zpArgTyp
a511: 4c bf 90                     jmp     TxtSkipSpaces

                   ParseSingleDigitNum
a514: a6 30                        ldx     zpExprStkIdx
a516: 9d 00 04                     sta     exprParseStk,x          ;store the digit into the parse buffer
a519: de 00 04                     dec     exprParseStk,x          ;and decrement to the true number value
a51c: a9 00                        lda     #$00
a51e: 9d 01 04                     sta     exprParseStk+1,x        ;store the high byte (#$00)
a521: e8                           inx
a522: e8                           inx
a523: e8                           inx
a524: 86 30                        stx     zpExprStkIdx            ;incr buffer index past number, and terminating null?
a526: 20 8c 90                     jsr     TxtPtrIncr              ;advance to next byte of tokenized line
a529: 4c 0d a5                     jmp     SetIntTypAndSkipSpc

a52c: 20 7f a5     DoExprVarName   jsr     EvalVarName
a52f: a6 30                        ldx     zpExprStkIdx
a531: a5 90                        lda     zpVarType               ;get the variable data's type
a533: 29 0f                        and     #$0f                    ;(ignore whether or not it's from an array)
a535: c9 03                        cmp     #TYP_STR                ;is it a string?
a537: f0 21                        beq     DEVN_exprStr            ;yes -> do string stuff
a539: a0 00                        ldy     #$00                    ;no: we have an int
a53b: b1 8c                        lda     (zpVarData),y           ;copy int onto parse stack
a53d: 9d 00 04                     sta     exprParseStk,x
a540: c8                           iny
a541: b1 8c                        lda     (zpVarData),y
a543: 9d 01 04                     sta     exprParseStk+1,x
a546: c8                           iny
a547: b1 8c                        lda     (zpVarData),y           ;and the... type ID byte of the var that follows it?
a549: 9d 02 04                     sta     exprParseStk+2,x        ;I suppose it's just a padding byte to make the size come out right
                   DEVN_advExprAndRts
a54c: e8                           inx
a54d: e8                           inx
a54e: e8                           inx
a54f: 86 30                        stx     zpExprStkIdx            ;advance parse stack index past the evaluated var data
a551: a5 90                        lda     zpVarType               ;and set var type as current expr value's type
a553: 29 0f                        and     #$0f
a555: 85 27                        sta     zpArgTyp
a557: 4c bf 90                     jmp     TxtSkipSpaces           ;skip spaces and return

a55a: a0 00        DEVN_exprStr    ldy     #$00
a55c: b1 8c                        lda     (zpVarData),y
a55e: 9d 00 04                     sta     exprParseStk,x          ;strlen -> parse stack
a561: a9 01                        lda     #$01
a563: 18                           clc
a564: 65 8c                        adc     zpVarData               ;then pointer at str contents
a566: 9d 01 04                     sta     exprParseStk+1,x
a569: a9 00                        lda     #$00
a56b: 65 8d                        adc     zpVarData+1
a56d: 9d 02 04                     sta     exprParseStk+2,x
a570: 4c 4c a5                     jmp     DEVN_advExprAndRts      ;advance the parse stack index and return

a573: 4c 91 84     errSyn_A573     jmp     ErrorSyntax

                   ; Given a var name at zpTXTPTR, looks it up and points zpVarData at its
                   ; contents.
                   zpArrayVarIdx   .var    $19    {addr/2}
                   vZpMaxIdx       .var    $4a    {addr/2}         ;The max indices for an array var's dimensions
                   vZpSkipVarBaseType .var $73    {addr/1}         ;underlying (non-array) base type for the array var we're skipping
                   vZpAryNumDimens .var    $74    {addr/1}         ;dimensionality of array var, -1 (0 or 1)

                   EvalVarNameOrSynErr
a576: 20 c4 a7                     jsr     IsAlpha
a579: b0 f8                        bcs     errSyn_A573
a57b: a2 00                        ldx     #$00
a57d: 86 30                        stx     zpExprStkIdx            ;empty the parse stack
a57f: a2 00        EvalVarName     ldx     #$00
a581: 86 8b                        stx     zpVarName+1             ;clear variable name second byte
a583: 85 8a                        sta     zpVarName               ;store var name first char (we already got)
a585: 20 98 90                     jsr     TxtPtrIncrAndGetChar    ;get next char from line
a588: 20 bc a7                     jsr     IsAlNum                 ;is it alphanumeric?
a58b: b0 0a                        bcs     @nonAlNum               ;no -> look into that
a58d: 85 8b                        sta     zpVarName+1             ;yes, store as var name 2nd char
a58f: 20 98 90     @alNumLp        jsr     TxtPtrIncrAndGetChar    ;loop over any remaining alphanumeric chars, ignoring
a592: 20 bc a7                     jsr     IsAlNum
a595: 90 f8                        bcc     @alNumLp
a597: a9 02        @nonAlNum       lda     #TYP_INT
a599: 85 90                        sta     zpVarType
a59b: 20 bf 90                     jsr     TxtSkipSpaces
a59e: c9 24                        cmp     #‘$’
a5a0: d0 07                        bne     @notStr
a5a2: a9 03                        lda     #TYP_STR
a5a4: 85 90                        sta     zpVarType
a5a6: 20 8c 90                     jsr     TxtPtrIncr
a5a9: 20 bf 90     @notStr         jsr     TxtSkipSpaces
a5ac: c9 28                        cmp     #‘(’                    ;array index?
a5ae: f0 70                        beq     EvalArrayVar            ;yes -> handle array var index
a5b0: 20 0f a7                     jsr     FindVar                 ;find the var (string or int)
a5b3: b0 45                        bcs     EVN_varFound            ;found -> get its data
a5b5: a0 00                        ldy     #$00                    ;not found. Make one!
a5b7: a5 90                        lda     zpVarType               ;(we're at end of vars tab now)
a5b9: 91 8e                        sta     (zpVARPTR),y            ;store variable type
a5bb: c8                           iny
a5bc: a5 8a                        lda     zpVarName               ;store variable name
a5be: 91 8e                        sta     (zpVARPTR),y
a5c0: c8                           iny
a5c1: a5 8b                        lda     zpVarName+1
a5c3: 91 8e                        sta     (zpVARPTR),y
a5c5: c8                           iny
a5c6: a9 00                        lda     #$00
a5c8: 91 8e                        sta     (zpVARPTR),y            ;store zeroes to next three bytes
a5ca: c8                           iny                             ;(appropriate for both int AND str!)
a5cb: 91 8e                        sta     (zpVARPTR),y
a5cd: c8                           iny
a5ce: 91 8e                        sta     (zpVARPTR),y            ;(third byte indicates vars tab end, if INT)
a5d0: a5 90                        lda     zpVarType
a5d2: c9 02                        cmp     #TYP_INT
a5d4: f0 0c                        beq     @int                    ;int? ->
a5d6: a9 00                        lda     #$00                    ;string. Clear its contents to zero.
a5d8: a2 21                        ldx     #$21
a5da: c8           @clearStrLp     iny
a5db: 91 8e                        sta     (zpVARPTR),y
a5dd: ca                           dex
a5de: d0 fa                        bne     @clearStrLp
a5e0: a9 21                        lda     #$21
a5e2: 18           @int            clc
a5e3: 69 03                        adc     #$03                    ;add size of var type, name, and data to VARPTR
a5e5: 65 8e                        adc     zpVARPTR
a5e7: 85 28                        sta     zpWAccum
a5e9: a9 00                        lda     #$00
a5eb: 65 8f                        adc     zpVARPTR+1
a5ed: 85 29                        sta     zpWAccum+1
a5ef: 20 b6 8b                     jsr     CheckFreeSpaceOrError   ;error if we have insufficient free space for new var
a5f2: a5 28                        lda     zpWAccum
a5f4: 85 1f                        sta     zpVAREND                ;record the new vars table end location
a5f6: a5 29                        lda     zpWAccum+1
a5f8: 85 20                        sta     zpVAREND+1
a5fa: 18           EVN_varFound    clc
a5fb: a9 03                        lda     #$03
a5fd: 65 8e                        adc     zpVARPTR                ;add 3 to VARPTR to skip type and name
a5ff: 85 8c                        sta     zpVarData               ;and store result in zpVarData
a601: a9 00                        lda     #$00
a603: 65 8f                        adc     zpVARPTR+1
a605: 85 8d                        sta     zpVarData+1
a607: 60                           rts

                   CpyVarPtrToVarData
a608: a5 8e                        lda     zpVARPTR
a60a: 85 8c                        sta     zpVarData
a60c: a5 8f                        lda     zpVARPTR+1
a60e: 85 8d                        sta     zpVarData+1
a610: 60           rts_A610        rts

                   ParseIntExprOrTMErr
a611: 20 9b a2                     jsr     Expression
a614: a9 02                        lda     #$02
a616: c5 27                        cmp     zpArgTyp
a618: f0 f6                        beq     rts_A610
a61a: 4c ff a9     errTypMism_A61A jmp     ErrorTypeMismatch

a61d: 4c bd a6     singleIdxAry    jmp     EvalSingleIdxAryVar

a620: a5 8a        EvalArrayVar    lda     zpVarName
a622: 48                           pha
a623: a5 8b                        lda     zpVarName+1
a625: 48                           pha
a626: a5 90                        lda     zpVarType
a628: 09 80                        ora     #TYP_ARRAY_FLAG         ;high bit = "array" flag?
a62a: 48                           pha
a62b: 20 8c 90                     jsr     TxtPtrIncr
a62e: 20 11 a6                     jsr     ParseIntExprOrTMErr     ;evaluate int expression for array index
a631: a6 30                        ldx     zpExprStkIdx
a633: ca                           dex
a634: ca                           dex
a635: ca                           dex
a636: bd 00 04                     lda     exprParseStk,x          ;save away first byte of idx
a639: 48                           pha
a63a: 20 f2 a6                     jsr     ParsStkPopIdx           ;verify idx fits in 1 byte, and pop from parse stack
a63d: 90 de                        bcc     singleIdxAry            ;only one index? -> handle
                   ; If we reach here, there's a second index provided for this array
a63f: 20 14 ac                     jsr     SkipCommaOrSynErr
a642: 20 11 a6                     jsr     ParseIntExprOrTMErr     ;evaluate int expr for 2nd index
a645: a6 30                        ldx     zpExprStkIdx
a647: ca                           dex
a648: ca                           dex
a649: ca                           dex
a64a: bd 00 04                     lda     exprParseStk,x          ;save away first byte of 2nd idx
a64d: 48                           pha
a64e: 20 f2 a6                     jsr     ParsStkPopIdx           ;verify idx fits in 1 byte, and pop from parse stack
a651: b0 c7                        bcs     errTypMism_A61A         ;didn't end with ')'? -> error!
a653: 20 8c 90                     jsr     TxtPtrIncr              ;advance TXTPTR past closing parenthesis
a656: 68                           pla
a657: 85 1b                        sta     zpMisc+2                ;restore 2nd index (to $1B)
a659: 68                           pla
a65a: 85 19                        sta     zpArrayVarIdx           ;restore 1st index (to $19)
a65c: 68                           pla
a65d: 85 90                        sta     zpVarType               ;restore var type
a65f: 68                           pla
a660: 85 8b                        sta     zpVarName+1             ;restore 2nd byte of variable name
a662: 68                           pla
a663: 85 8a                        sta     zpVarName               ;restore 1st byte of var name
a665: 20 0f a7                     jsr     FindVar                 ;find the matching variable
a668: 90 50                        bcc     errIllVal_A6BA          ;didn't find? -> error IL (arrays must be DIM'd before use)
a66a: a0 03                        ldy     #$03
a66c: b1 8e                        lda     (zpVARPTR),y            ;is the found array var 2-dimensional?
a66e: f0 4a                        beq     errIllVal_A6BA          ;no -> error (we're looking for 2-dimensional)
a670: c8                           iny
a671: b1 8e                        lda     (zpVARPTR),y            ;get max idx for first dimen
a673: c5 19                        cmp     zpArrayVarIdx           ;less than the specified 1st index?
a675: 90 43                        bcc     errIllVal_A6BA          ;yes -> error
a677: aa                           tax                             ;save max idx to x
a678: c8                           iny
a679: b1 8e                        lda     (zpVARPTR),y            ;get max idx for second dimen
a67b: c5 1b                        cmp     zpMisc+2                ;less than the specified 2nd index?
a67d: 90 3b                        bcc     errIllVal_A6BA          ;yes -> error
a67f: 20 ce a7                     jsr     ClearOperandHiBytes
a682: e8                           inx                             ;max idx, +1 -> num elements in 1st dim
a683: d0 02                        bne     @not256
a685: e6 29                        inc     zpWAccum+1
a687: 86 28        @not256         stx     zpWAccum
a689: a5 1b                        lda     zpMisc+2
a68b: 85 2c                        sta     zpWParam
a68d: 20 47 8f                     jsr     WordMultiply            ;times value for 2nd index
a690: a5 19                        lda     zpArrayVarIdx
a692: 85 2c                        sta     zpWParam
a694: 20 a8 8e                     jsr     SetIntAndAdd            ;and add the value of the 1st index
a697: 20 04 a7                     jsr     GetSizeOfElementType
a69a: 85 2c                        sta     zpWParam
a69c: 20 47 8f                     jsr     WordMultiply            ;multiply by size of element type
a69f: a9 06                        lda     #$06                    ;add size of two-dimension array variable preamble
                   EAV_getArrayElem
a6a1: 85 2c                        sta     zpWParam
a6a3: 20 a8 8e                     jsr     SetIntAndAdd            ;do the preamble add
a6a6: a5 8e                        lda     zpVARPTR
a6a8: 85 2c                        sta     zpWParam
a6aa: a5 8f                        lda     zpVARPTR+1
a6ac: 85 2d                        sta     zpWParam+1
a6ae: 20 a8 8e                     jsr     SetIntAndAdd            ;add to the current pointer into var tab (pointing at found var)
a6b1: a5 28                        lda     zpWAccum                ;save result as ptr to the element value
a6b3: 85 8c                        sta     zpVarData
a6b5: a5 29                        lda     zpWAccum+1
a6b7: 85 8d                        sta     zpVarData+1
a6b9: 60                           rts

a6ba: 4c 94 84     errIllVal_A6BA  jmp     ErrorIllegalValue

                   EvalSingleIdxAryVar
a6bd: 20 8c 90                     jsr     TxtPtrIncr
a6c0: 68                           pla
a6c1: 85 19                        sta     zpArrayVarIdx           ;restore the index value
a6c3: 68                           pla
a6c4: 85 90                        sta     zpVarType               ;this will have high-bit set now!
a6c6: 68                           pla
a6c7: 85 8b                        sta     zpVarName+1             ;restore var name
a6c9: 68                           pla
a6ca: 85 8a                        sta     zpVarName
a6cc: 20 0f a7                     jsr     FindVar
a6cf: 90 e9                        bcc     errIllVal_A6BA          ;didn't find -> error IL (arrays must be DIM'd before use)
a6d1: a0 03                        ldy     #$03
a6d3: b1 8e                        lda     (zpVARPTR),y            ;is the found array var 2-dimensional?
a6d5: d0 e3                        bne     errIllVal_A6BA          ;yes -> error (we're looking for single!)
a6d7: c8                           iny
a6d8: b1 8e                        lda     (zpVARPTR),y            ;get max array index
a6da: c5 19                        cmp     zpArrayVarIdx           ;is it less than the index used in the expression?
a6dc: 90 dc                        bcc     errIllVal_A6BA          ;yes -> error
a6de: a5 19                        lda     zpArrayVarIdx
a6e0: 85 28                        sta     zpWAccum
a6e2: 20 04 a7                     jsr     GetSizeOfElementType
a6e5: 85 2c                        sta     zpWParam
a6e7: 20 ce a7                     jsr     ClearOperandHiBytes
a6ea: 20 47 8f                     jsr     WordMultiply            ;multiply index by size of elements
a6ed: a9 05                        lda     #$05                    ;add size of single-dimension array variable preamble
a6ef: 4c a1 a6                     jmp     EAV_getArrayElem

                   ; Pops the evaluated array index off the parse stack, erroring if the index
                   ; value exceeds one byte (too big to be an index!). Returns with carry clear if
                   ; and only if next token is a closing parenthesis (')')
a6f2: bd 01 04     ParsStkPopIdx   lda     exprParseStk+1,x        ;second index byte is #$00?
a6f5: d0 c3                        bne     errIllVal_A6BA          ;no -> too big! error
a6f7: 86 30                        stx     zpExprStkIdx            ;save our backed-up stack index
a6f9: 20 bf 90                     jsr     TxtSkipSpaces
a6fc: c9 29                        cmp     #‘)’                    ;next char == ')'?
a6fe: d0 02                        bne     @retFalse               ;no -> return false
a700: 18                           clc                             ;yes, return true
a701: 60                           rts

a702: 38           @retFalse       sec
a703: 60                           rts

                   GetSizeOfElementType
a704: a5 90                        lda     zpVarType
a706: 29 0f                        and     #$0f
a708: c9 02        GetSizeOfType   cmp     #TYP_INT
a70a: f0 02                        beq     @rts                    ;INT? type #$02, has size #$02... -> just return!
a70c: a9 21                        lda     #33                     ;strings are 33 bytes total. 1 len, 31 content, 1 terminating null
a70e: 60           @rts            rts

                   ; Finds variable record from zpVarName ($8A.8B) and zpVarType ($90, temp use).
                   ; Unlike most other routines in Family BASIC, exiting with carry set indicates
                   ; success, reset indicates failure.
a70f: a5 1d        FindVar         lda     zpVARTAB
a711: 85 8e                        sta     zpVARPTR
a713: a5 1e                        lda     zpVARTAB+1
a715: 85 8f                        sta     zpVARPTR+1
a717: a0 00        @findVarLoop    ldy     #$00
a719: b1 8e                        lda     (zpVARPTR),y            ;end of variables table?
a71b: f0 36                        beq     @retFail                ;yes -> FAIL
a71d: c5 90                        cmp     zpVarType               ;matches our sought variable type?
a71f: d0 10                        bne     @skipVar                ;no -> skip to next var
a721: c8                           iny
a722: b1 8e                        lda     (zpVARPTR),y
a724: c5 8a                        cmp     zpVarName               ;first letter of var name matches?
a726: d0 09                        bne     @skipVar                ;no -> skip to next var
a728: c8                           iny
a729: b1 8e                        lda     (zpVARPTR),y
a72b: c5 8b                        cmp     zpVarName+1             ;second char of var name matches?
a72d: d0 02                        bne     @skipVar                ;no -> skip to next var
a72f: 38                           sec                             ;var matched- return success (carry set)!
a730: 60                           rts

a731: a0 00        @skipVar        ldy     #$00                    ;skipVar
a733: b1 8e                        lda     (zpVARPTR),y            ;is it an array type?
a735: 30 1e                        bmi     @skipAryVar             ;yes -> handle skipping array
a737: 20 08 a7                     jsr     GetSizeOfType           ;no, get basic type size
a73a: 18                           clc
a73b: 69 03                        adc     #$03                    ;add the 3 bytes for type and name
a73d: 65 8e                        adc     zpVARPTR                ;and add to variables table index to skip the var
a73f: 85 8e                        sta     zpVARPTR
a741: a9 00                        lda     #$00
a743: 65 8f                        adc     zpVARPTR+1
a745: 85 8f                        sta     zpVARPTR+1
                   @contIfNotVarsEnd
a747: a5 1f                        lda     zpVAREND                ;did we reach end of var table?
a749: c5 8e                        cmp     zpVARPTR
a74b: d0 ca                        bne     @findVarLoop            ;no -> check next var for a match
a74d: a5 20                        lda     zpVAREND+1              ;low byte matched.
a74f: c5 8f                        cmp     zpVARPTR+1              ;does high byte?
a751: d0 c4                        bne     @findVarLoop            ;no -> check next var for a match
a753: 18           @retFail        clc                             ;yes: end of vars with no match. Return failure (carry clear)
a754: 60                           rts

a755: 29 0f        @skipAryVar     and     #$0f
a757: 85 73                        sta     vZpSkipVarBaseType      ;record array's element type (stripping array flag)
a759: a0 03                        ldy     #$03
a75b: b1 8e                        lda     (zpVARPTR),y
a75d: 85 74                        sta     vZpAryNumDimens         ;number of array dimensions, minus one.
a75f: c8                           iny
a760: b1 8e                        lda     (zpVARPTR),y
a762: 85 4a                        sta     vZpMaxIdx               ;max index of array
a764: c8                           iny
a765: b1 8e                        lda     (zpVARPTR),y            ;max index of 2nd dimension of array (if 2 dimension)
a767: 85 4b                        sta     vZpMaxIdx+1             ;(otherwise, this is first byte of first element!)
a769: 20 ce a7                     jsr     ClearOperandHiBytes     ;clear WParam+1, WAccum+1
a76c: 85 2c                        sta     zpWParam                ;clear low byte of WParam as well
a76e: a5 4a                        lda     vZpMaxIdx
a770: 85 28                        sta     zpWAccum                ;1st dimension max index goes into WAccum
a772: a5 74                        lda     vZpAryNumDimens         ;one-dimensional array, or two?
a774: f0 04                        beq     @oneDim                 ;one -> leave WParam zero (ought to skip the first multiply, but doesn't)
a776: a5 4b                        lda     vZpMaxIdx+1             ;two: max idx of 2nd dimension -> WParam
a778: 85 2c                        sta     zpWParam
a77a: e6 28        @oneDim         inc     zpWAccum                ;increment WParam (max idx -> number-of-elements)
a77c: d0 02                        bne     @waHi
a77e: e6 29                        inc     zpWAccum+1
a780: e6 2c        @waHi           inc     zpWParam                ;increment WAccum (max idx -> number-of-elements)
a782: d0 02                        bne     @wpHi
a784: e6 2d                        inc     zpWParam+1
a786: 20 47 8f     @wpHi           jsr     WordMultiply            ;multiply them (now have total number of elements)
a789: a5 73                        lda     vZpSkipVarBaseType
a78b: 20 08 a7                     jsr     GetSizeOfType           ;get element type size
a78e: 85 2c                        sta     zpWParam                ;store in WParam
a790: a9 00                        lda     #$00
a792: 85 2d                        sta     zpWParam+1
a794: 20 47 8f                     jsr     WordMultiply            ;multiply
a797: a9 05                        lda     #$05                    ;total number of bytes for the elements
a799: 85 2c                        sta     zpWParam                ; plus 5 for array type and name, and max index value
a79b: a5 74                        lda     vZpAryNumDimens
a79d: f0 02                        beq     @oneDim2
a79f: e6 2c                        inc     zpWParam                ;...make that 6, for the two max index entries
a7a1: 20 a8 8e     @oneDim2        jsr     SetIntAndAdd            ;(do that add)
a7a4: a5 8e                        lda     zpVARPTR
a7a6: 85 2c                        sta     zpWParam
a7a8: a5 8f                        lda     zpVARPTR+1
a7aa: 85 2d                        sta     zpWParam+1
a7ac: 20 a8 8e                     jsr     SetIntAndAdd            ;now add to our index into the variables table
a7af: a5 28                        lda     zpWAccum                ;and put result back into index
a7b1: 85 8e                        sta     zpVARPTR
a7b3: a5 29                        lda     zpWAccum+1
a7b5: 85 8f                        sta     zpVARPTR+1
a7b7: 4c 47 a7                     jmp     @contIfNotVarsEnd       ;we succesfully skipped the array var! Check the next.

a7ba: 38           @secAndRts      sec
a7bb: 60                           rts

                   ; clears carry if A is alphanumeric, sets otherwise
                   • Clear variables

a7bc: c9 30        IsAlNum         cmp     #$30                    ; < '0' (punctuation, or control chars)?
a7be: 90 fa                        bcc     @secAndRts
a7c0: c9 3a                        cmp     #$3a                    ; digit?
a7c2: 90 08                        bcc     @clcAndRts
                   ; clears carry if A is alphabetic, sets otherwise
a7c4: c9 41        IsAlpha         cmp     #$41                    ; additional punctuation?
a7c6: 90 f2                        bcc     @secAndRts
a7c8: c9 5b                        cmp     #$5b                    ; another set of punctuation?
a7ca: b0 ee                        bcs     @secAndRts
a7cc: 18           @clcAndRts      clc
a7cd: 60                           rts

                   ClearOperandHiBytes
a7ce: a9 00                        lda     #$00
a7d0: 85 2d                        sta     zpWParam+1
a7d2: 85 29                        sta     zpWAccum+1
a7d4: 60                           rts

                   AddAndReplaceExpr
a7d5: 20 ac 8e                     jsr     Add
                   ; Copies WAccum to the expression parse stack, replacing the previous top
                   ; exprssion on the stack
                   ReplaceExprStkTopWAccum
a7d8: a6 30                        ldx     zpExprStkIdx
a7da: ca                           dex
a7db: ca                           dex
a7dc: ca                           dex
a7dd: a5 28                        lda     zpWAccum
a7df: 9d 00 04                     sta     exprParseStk,x
a7e2: a5 29                        lda     zpWAccum+1
a7e4: 9d 01 04                     sta     exprParseStk+1,x
a7e7: a5 2a                        lda     zpWAccum+2
a7e9: 9d 02 04                     sta     exprParseStk+2,x
a7ec: 4c bf 90                     jmp     TxtSkipSpaces

                   SubtractAndReplaceExpr
a7ef: 20 0f 8e                     jsr     Subtract
a7f2: 4c d8 a7                     jmp     ReplaceExprStkTopWAccum

                   MulAndReplaceExpr
a7f5: 20 0f 8f                     jsr     MultiplyAndCheckPosNeg
a7f8: 4c d8 a7                     jmp     ReplaceExprStkTopWAccum

                   DivAndReplaceExpr
a7fb: a5 2c                        lda     zpWParam                ;check for division by zero
a7fd: d0 04                        bne     @notZero
a7ff: a5 2d                        lda     zpWParam+1
a801: f0 06                        beq     ErrorDivisionByZero
a803: 20 9d 8f     @notZero        jsr     WordDivide
a806: 4c d8 a7                     jmp     ReplaceExprStkTopWAccum

                   ErrorDivisionByZero
a809: a9 0a                        lda     #err_DZ
a80b: 4c 37 b2                     jmp     PrintErrorAndStop

                   ModAndReplaceExpr
a80e: a5 29                        lda     zpWAccum+1
a810: 48                           pha
a811: 20 fb a7                     jsr     DivAndReplaceExpr       ;perform division
a814: a5 2a                        lda     zpWAccum+2              ;move remainder to WAccum
a816: 85 28                        sta     zpWAccum
a818: a5 2b                        lda     zpWAccum+3
a81a: 85 29                        sta     zpWAccum+1
a81c: 68                           pla                             ;was the divisor positive?
a81d: 10 0a                        bpl     @chkRslt                ;yes -> check result
a81f: a5 29                        lda     zpWAccum+1
a821: 30 03                        bmi     @replStkTop
a823: 20 32 8e     @negate         jsr     WAccumNegate
a826: 4c d8 a7     @replStkTop     jmp     ReplaceExprStkTopWAccum

a829: a5 29        @chkRslt        lda     zpWAccum+1              ;is result positive?
a82b: 10 f9                        bpl     @replStkTop             ;yes -> leave result alone
a82d: 30 f4                        bmi     @negate                 ;no: negate then close up

a82f: 20 ee 8f     OpFn_NotEq      jsr     Compare                 ;compare A and B
a832: d0 09                        bne     RelOp_ReturnTrue        ;unequal? -> return true
                   RelOp_ReturnFalse
a834: a9 00                        lda     #$00                    ;load 0 (false)
                   RelOp_RetTrueOrFalse
a836: 85 28                        sta     zpWAccum                ;store acc to both bytes of WAccum
a838: 85 29                        sta     zpWAccum+1
a83a: 4c d8 a7                     jmp     ReplaceExprStkTopWAccum ;and replace as result at top of expr parse stack

                   RelOp_ReturnTrue
a83d: a9 ff                        lda     #$ff                    ;load -1 (true)
a83f: 4c 36 a8                     jmp     RelOp_RetTrueOrFalse

a842: 20 ee 8f     OpFn_LtEq       jsr     Compare                 ;compare A and B
a845: f0 f6                        beq     RelOp_ReturnTrue        ;equal? -> true
a847: 90 f4                        bcc     RelOp_ReturnTrue        ;A < B? -> also true
a849: 4c 34 a8                     jmp     RelOp_ReturnFalse       ;else -> false

a84c: 20 ee 8f     OpFn_GrEq       jsr     Compare                 ;compare A and B
a84f: b0 ec                        bcs     RelOp_ReturnTrue        ;A >= B? true
a851: 4c 34 a8                     jmp     RelOp_ReturnFalse       ;else -> false

a854: 20 ee 8f     OpFn_Equal      jsr     Compare                 ;compare A and B
a857: f0 e4                        beq     RelOp_ReturnTrue        ;A == B? -> true
a859: 4c 34 a8                     jmp     RelOp_ReturnFalse       ;else -> false

                   OpFn_GreaterThan
a85c: 20 ee 8f                     jsr     Compare                 ;compare A and B
a85f: f0 d3                        beq     RelOp_ReturnFalse       ;A == B? -> false
a861: b0 da                        bcs     RelOp_ReturnTrue        ;else A >= B? -> true
a863: 4c 34 a8                     jmp     RelOp_ReturnFalse       ;otherwise -> false

a866: 20 ee 8f     OpFn_LessThan   jsr     Compare                 ;compare A and B
a869: 90 d2                        bcc     RelOp_ReturnTrue        ;A < B? -> true
a86b: 4c 34 a8                     jmp     RelOp_ReturnFalse       ;else -> false

                   ; Jump table for relative/comparative operators
a86e: 2f a8        jtbl_RelOps     .dd2    OpFn_NotEq              ; <>
a870: 4c a8                        .dd2    OpFn_GrEq               ; >=
a872: 42 a8                        .dd2    OpFn_LtEq               ; <=
a874: 54 a8                        .dd2    OpFn_Equal              ; =
a876: 5c a8                        .dd2    OpFn_GreaterThan        ; >
a878: 66 a8                        .dd2    OpFn_LessThan           ; <

                   SkipLParenOrSynErr
a87a: a2 28                        ldx     #‘(’
a87c: 2c                           bit ▼   $29a2
                   SkipRParenOrSynErr
a87d: a2 29                        ldx     #‘)’
a87f: 86 75                        stx     zpHeldKeyChar
a881: 20 bf 90                     jsr     TxtSkipSpaces
a884: c5 75                        cmp     zpHeldKeyChar
a886: d0 49                        bne     errSyn_A8D1
a888: 4c 8c 90                     jmp     TxtPtrIncr

a88b: 4c 94 84     errIllVal_A88B  jmp     ErrorIllegalValue

a88e: 20 8c 90     ExprMove        jsr     TxtPtrIncr
a891: 20 04 aa                     jsr     EvalParenByteOrError    ;evaluate MOVE arg
a894: a4 28                        ldy     zpWAccum
a896: c0 08                        cpy     #$08                    ;arg >= 8?
a898: b0 f1                        bcs     errIllVal_A88B          ;yes -> error
a89a: b9 45 06                     lda     sprAttrFlags,y          ;get DEF MOVE data on specified sprite
a89d: 29 10                        and     #$10                    ;select whether movement is active
a89f: f0 02                        beq     @zero                   ;zero? -> keep that value
a8a1: a9 ff                        lda     #$ff                    ;non-zero, convert to value (-1)
a8a3: 85 28        @zero           sta     zpWAccum                ;save that value to WAccum (as word)
a8a5: 85 29                        sta     zpWAccum+1
a8a7: 20 7e a9                     jsr     SkipRParenSetParseStkInt ;and save it as the parse stack top
a8aa: 4c bf 90                     jmp     TxtSkipSpaces

a8ad: a9 00        ZeroWAccum      lda     #$00
a8af: 85 28                        sta     zpWAccum
                   ; (Only "truncates" bc caller always sets A-reg to zero)
                   TruncWAccumToByte
a8b1: 85 29                        sta     zpWAccum+1
a8b3: 85 2a                        sta     zpWAccum+2
a8b5: 60                           rts

                   ; Skips over a parentheses at TXTPTR, or errors if no paren.
                   ; Evaluates the parenthesized expression, and copies the three bytes to WAccum
                   EvalParenExprOrError
a8b6: 20 7a a8                     jsr     SkipLParenOrSynErr
a8b9: 20 9b a2                     jsr     Expression
a8bc: a6 30                        ldx     zpExprStkIdx
a8be: ca                           dex
a8bf: bd 00 04                     lda     exprParseStk,x
a8c2: 85 2a                        sta     zpWAccum+2
a8c4: ca                           dex
a8c5: bd 00 04                     lda     exprParseStk,x
a8c8: 85 29                        sta     zpWAccum+1
a8ca: ca                           dex
a8cb: bd 00 04                     lda     exprParseStk,x
a8ce: 85 28                        sta     zpWAccum
a8d0: 60                           rts

a8d1: 4c 91 84     errSyn_A8D1     jmp     ErrorSyntax

                   ; dead/unreachable code
a8d4: a6 30                        ldx     zpExprStkIdx
a8d6: a5 28                        lda     zpWAccum
a8d8: 9d 00 04                     sta     exprParseStk,x
a8db: e8                           inx
a8dc: a5 29                        lda     zpWAccum+1
a8de: 9d 00 04                     sta     exprParseStk,x
a8e1: e8                           inx
a8e2: e8                           inx
a8e3: 86 30                        stx     zpExprStkIdx
a8e5: 60                           rts

a8e6: a5 49        FnFn_CSRLIN     lda     zpCV
a8e8: 85 28                        sta     zpWAccum                ;load cursor y pos into low byte of WAccum
                   PushWAccByteToParseStk
a8ea: a9 00                        lda     #$00
a8ec: 85 29                        sta     zpWAccum+1              ;zero out high byte of WAccum
                   PushWAccIntToParseStk
a8ee: e6 30                        inc     zpExprStkIdx            ;create space on parse stack for a "pushed" return value
a8f0: e6 30                        inc     zpExprStkIdx
a8f2: e6 30                        inc     zpExprStkIdx
                   SetParseStkTopToWAccInt
a8f4: a9 02                        lda     #TYP_INT
a8f6: 85 27                        sta     zpArgTyp                ;set current type as int
                   SetParseStkTopToWAcc
a8f8: a6 30                        ldx     zpExprStkIdx
a8fa: ca                           dex
a8fb: ca                           dex
a8fc: ca                           dex
a8fd: a5 28                        lda     zpWAccum                ;copy 3 bytes from WAcccum over the top item of the parse stack
a8ff: 9d 00 04                     sta     exprParseStk,x
a902: a5 29                        lda     zpWAccum+1
a904: 9d 01 04                     sta     exprParseStk+1,x
a907: a5 2a                        lda     zpWAccum+2
a909: 9d 02 04                     sta     exprParseStk+2,x
a90c: 60                           rts                             ;done

a90d: a5 b5        FnFn_ERR        lda     zpErrCode
a90f: 85 28                        sta     zpWAccum                ;load last error's code into WAccum
a911: 4c ea a8                     jmp     PushWAccByteToParseStk  ;and return it via expr parse stack

a914: a5 b7        FnFn_ERL        lda     zpErrLineNum
a916: 85 28                        sta     zpWAccum                ;get line num from last err into WAccum
a918: a5 b8                        lda     zpErrLineNum+1
a91a: 85 29                        sta     zpWAccum+1
a91c: 4c ee a8                     jmp     PushWAccIntToParseStk   ;and return it via the expr parse stack

a91f: 20 25 a9     FnFn_FRE        jsr     AmtFreeMemToWAccum      ;get the amount of free memory into WAccum
a922: 4c ee a8                     jmp     PushWAccIntToParseStk   ;and push that to parse stack

                   AmtFreeMemToWAccum
a925: 38                           sec
a926: a5 03                        lda     zpFRETOP                ;save (FRETOP - VAREND) to WAccum
a928: e5 1f                        sbc     zpVAREND
a92a: 85 28                        sta     zpWAccum
a92c: a5 04                        lda     zpFRETOP+1
a92e: e5 20                        sbc     zpVAREND+1
a930: 85 29                        sta     zpWAccum+1
a932: 60                           rts

a933: 20 0a aa     FnFn_POS        jsr     EvalParenIntOrError     ;read (and discard) dummy integer argument
a936: a5 48                        lda     zpCH                    ;copy cursor x pos to low byte of WAccum
                   SetWacByteSkipRPSetParseStkInt
a938: 85 28                        sta     zpWAccum                ;set Waccum low byte to what's in A reg
a93a: a9 00                        lda     #$00
a93c: 85 29                        sta     zpWAccum+1              ;zero out the high byte of WAccum
a93e: 4c 7e a9                     jmp     SkipRParenSetParseStkInt ;skip right paren, and return WAccum in expr stack

                   GetActorDefMoveIdxFromArg
a941: 20 04 aa                     jsr     EvalParenByteOrError    ;get actor # arg into WAccum (as byte)
                   GetActorDefMoveIdx
a944: a5 28                        lda     zpWAccum                ;read actor # from WAccum (byte)
a946: 20 44 af                     jsr     ErrUnlessZeroThruSeven  ;ensure it's a value < 8
a949: 0a                           asl     A                       ;multiply by 8, for MOVE dfn indexing
a94a: 0a                           asl     A
a94b: 0a                           asl     A
a94c: aa                           tax
a94d: 60                           rts                             ;and return in X-reg

                   ; Parses an open paren (or errors), then parses an integer and errors unless its
                   ; value is in the range 0 thru 7 (inclusive - sprite actor number). The parsed
                   ; value is in both A reg and X reg upon (successful) exit.
                   EvalParenZeroThruSevenOrError
a94e: 20 04 aa                     jsr     EvalParenByteOrError
a951: a5 28                        lda     zpWAccum
a953: 20 44 af                     jsr     ErrUnlessZeroThruSeven
a956: aa                           tax
a957: 60                           rts

a958: 20 41 a9     FnFn_XPOS       jsr     GetActorDefMoveIdxFromArg ;get index from actor-num arg
a95b: bd c6 06                     lda     defMovSPosX,x           ;grab X pos
a95e: 4c 38 a9                     jmp     SetWacByteSkipRPSetParseStkInt ;-> and return it

a961: 20 41 a9     FnFn_YPOS       jsr     GetActorDefMoveIdxFromArg ;get index from actor-num arg
a964: bd c7 06                     lda     defMovSPosY,x           ;grab Y pos
a967: 4c 38 a9                     jmp     SetWacByteSkipRPSetParseStkInt ;-> and return it

a96a: 20 41 a9     FnFn_VCT        jsr     GetActorDefMoveIdxFromArg ;get index from actor-num arg
a96d: bd c0 06                     lda     defMovSFacing,x         ;grab facing
a970: 4c 38 a9                     jmp     SetWacByteSkipRPSetParseStkInt ;-> and return it

a973: 20 0a aa     FnFn_PEEK       jsr     EvalParenIntOrError     ;eval parenthesized int arg
a976: a0 00                        ldy     #$00
a978: b1 28                        lda     (zpWAccum),y            ;peek at specified addr
a97a: 85 28                        sta     zpWAccum
a97c: 84 29                        sty     zpWAccum+1              ;save result to WAccum
                   SkipRParenSetParseStkInt
a97e: 20 7d a8                     jsr     SkipRParenOrSynErr
a981: 4c f4 a8                     jmp     SetParseStkTopToWAccInt

a984: 20 0a aa     FnFn_ABS        jsr     EvalParenIntOrError     ;eval parenthesized arg
a987: 20 f2 8d                     jsr     Cmp32768                ;is the value -32,768?
a98a: f0 0d                        beq     errOverfl_A999          ;yes -> can't negate, won't fit
a98c: a5 29                        lda     zpWAccum+1
a98e: 30 03                        bmi     @neg                    ;negative? -> negate it to positive
a990: 4c 7e a9                     jmp     SkipRParenSetParseStkInt ;-> else just set expr value as-is

a993: 20 32 8e     @neg            jsr     WAccumNegate            ;negate
a996: 4c 7e a9                     jmp     SkipRParenSetParseStkInt ;-> then set as our value on parse stack

a999: 4c f2 8e     errOverfl_A999  jmp     ErrorOverflow

a99c: 20 0a aa     FnFn_SGN        jsr     EvalParenIntOrError     ;parse parenthesized int argument
a99f: a5 29                        lda     zpWAccum+1              ;is it negative?
a9a1: 30 09                        bmi     @neg                    ;yes -> handle; else, is it positive?
a9a3: d0 10                        bne     @pos                    ;yes -> handle
a9a5: a5 28                        lda     zpWAccum                ;(check low byte pos too, if high byte is zero)
a9a7: d0 0c                        bne     @pos
a9a9: a9 00                        lda     #$00                    ;zero: set Waccum to zero
a9ab: 2c                           bit ▼   $ffa9
a9ac: a9 ff        @neg            lda     #$ff                    ;neg: set Waccum to -1
a9ae: 85 28                        sta     zpWAccum
                   @wrHiBhyteAndFinish
a9b0: 85 29                        sta     zpWAccum+1
a9b2: 4c 7e a9                     jmp     SkipRParenSetParseStkInt ;-> skip closing parenthesis, and return WAccum

a9b5: a9 01        @pos            lda     #$01                    ;pos: set Waccum to 1
a9b7: 85 28                        sta     zpWAccum
a9b9: a9 00                        lda     #$00                    ;load zero for high byte
a9bb: f0 f3                        beq     @wrHiBhyteAndFinish

a9bd: 20 0a aa     FnFn_RND        jsr     EvalParenIntOrError     ;get parenthesized int arg (max ret val)
a9c0: a5 28                        lda     zpWAccum
a9c2: 48                           pha
a9c3: a5 29                        lda     zpWAccum+1
a9c5: 48                           pha                             ;push the max val to CPU stack
a9c6: a5 56                        lda     zpRandom
a9c8: 85 28                        sta     zpWAccum
a9ca: a5 57                        lda     zpRandom+1              ;take previous PRNG result
a9cc: 85 29                        sta     zpWAccum+1              ;and place in WAccum
a9ce: a9 83                        lda     #$83
a9d0: 85 2c                        sta     zpWParam
a9d2: a9 03                        lda     #$03
a9d4: 85 2d                        sta     zpWParam+1              ;multiply by 899
a9d6: 20 44 8f                     jsr     WordMultiplyWithTypeCheck
a9d9: a9 7f                        lda     #$7f
a9db: 25 29                        and     zpWAccum+1              ;clear away sign bit (max 32,767)
a9dd: 85 29                        sta     zpWAccum+1
a9df: 85 57                        sta     zpRandom+1              ;store the result as the new value for zpRandom
a9e1: a5 28                        lda     zpWAccum
a9e3: 85 56                        sta     zpRandom
a9e5: 68                           pla                             ;pull the user's arg, and stick into WParam
a9e6: 85 2d                        sta     zpWParam+1
a9e8: 68                           pla
a9e9: 85 2c                        sta     zpWParam
a9eb: 20 44 8f                     jsr     WordMultiplyWithTypeCheck ;multiply by the (7-bit) random number we got
a9ee: 06 29                        asl     zpWAccum+1              ;then shift the result (including overflow) one bit left
a9f0: 26 36                        rol     zpMulScratch+2
a9f2: 26 37                        rol     zpMulScratch+3
a9f4: a5 36                        lda     zpMulScratch+2
a9f6: 85 28                        sta     zpWAccum                ;store the 16-bit overflow
a9f8: a5 37                        lda     zpMulScratch+3          ; in WAccum (it's guaranteed to be
a9fa: 85 29                        sta     zpWAccum+1              ; less than the user's number)
a9fc: 4c 7e a9                     jmp     SkipRParenSetParseStkInt ;read past the closing paren, set top of parse stack to our return value

                   ErrorTypeMismatch
a9ff: a9 0b                        lda     #err_TM
aa01: 4c 37 b2                     jmp     PrintErrorAndStop

                   ; Same as EvalParenIntOrError, below, but additionally error if the int value
                   ; doesn't fit in a single byte.
                   EvalParenByteOrError
aa04: 20 0a aa                     jsr     EvalParenIntOrError
aa07: 4c 80 ab                     jmp     ErrIfWAccumIsWord

                   ; If we're at a parenthesized integer expression, evaluate it and place in
                   ; WAccum. If not, ErrorTypeMismatch.
                   EvalParenIntOrError
aa0a: 20 b6 a8                     jsr     EvalParenExprOrError
                   ErrorUnlessInteger
aa0d: a9 02                        lda     #TYP_INT
aa0f: ae                           ldx ▼   $03a9
                   ErrorUnlessString
aa10: a9 03                        lda     #TYP_STR
aa12: c5 27                        cmp     zpArgTyp
aa14: d0 e9                        bne     ErrorTypeMismatch
aa16: 60                           rts

                   EvalParenStrOrError
aa17: 20 b6 a8                     jsr     EvalParenExprOrError
aa1a: 4c 10 aa                     jmp     ErrorUnlessString

aa1d: 20 17 aa     FnFn_LEN        jsr     EvalParenStrOrError     ;get parenthesized string
                   TruncWAccumParseRParen
aa20: a9 00                        lda     #$00
aa22: 20 b1 a8                     jsr     TruncWAccumToByte       ;clear WAccum bytes except lowest
aa25: 4c 7e a9                     jmp     SkipRParenSetParseStkInt ;-> finish up

aa28: 20 17 aa     FnFn_ASC        jsr     EvalParenStrOrError     ;get parenthesized string arg
aa2b: a5 28                        lda     zpWAccum                ;empty string?
aa2d: f0 f1                        beq     TruncWAccumParseRParen  ;yes -> return zero
aa2f: a0 00                        ldy     #$00
aa31: b1 29                        lda     (zpWAccum+1),y          ;read first char of string
aa33: 85 28                        sta     zpWAccum                ;save its value in zpWAccum
aa35: 4c 20 aa                     jmp     TruncWAccumParseRParen  ;-> clear other bytes of WAccum and read closing paren. Done!

aa38: 4c 94 84     errIllVal_AA38  jmp     ErrorIllegalValue

aa3b: 20 56 b6     FnFn_SCR_STR    jsr     SaveCursorPos           ;save CH/CV
aa3e: 20 04 aa                     jsr     EvalParenByteOrError    ;read first arg; x pos
aa41: a5 28                        lda     zpWAccum
aa43: c9 1c                        cmp     #COLS                   ; >= # columns?
aa45: b0 f1                        bcs     errIllVal_AA38          ;yes -> error
aa47: 85 48                        sta     zpCH                    ;else, save as cursor x pos (for now)
aa49: 20 3b ac                     jsr     CommaThenIntExprToWAccum ;read 2nd arg; y pos
aa4c: 20 80 ab                     jsr     ErrIfWAccumIsWord       ;ensure only uses one byte
aa4f: a5 28                        lda     zpWAccum
aa51: c9 18                        cmp     #ROWS                   ; >= # rows?
aa53: b0 e3                        bcs     errIllVal_AA38          ;yes -> error
aa55: 85 49                        sta     zpCV                    ;else, save as cursor y pos (for now)
aa57: 20 bf 90                     jsr     TxtSkipSpaces
aa5a: c9 29                        cmp     #‘)’                    ;do we have closing paren?
aa5c: f0 12                        beq     @getTileId              ;yes -> we're getting the tile ID/character at that pos
aa5e: 20 3b ac                     jsr     CommaThenIntExprToWAccum ;else get 3rd arg
aa61: a5 28                        lda     zpWAccum                ; == 0?
aa63: f0 0b                        beq     @getTileId              ;yes -> same as omitted. We're getting tile ID/character
aa65: 20 8d b0                     jsr     ErrUnlessZeroOrOne      ;if it's not zero, ensure it's one
aa68: 20 b0 c4                     jsr     GetTileQuadPaletteNum   ;go and fetch the palette number for this location
aa6b: a5 9c                        lda     zpPaletteNum
aa6d: 4c 7a aa                     jmp     @finish                 ;-> restore cursor pos and return the value

aa70: 20 f4 b4     @getTileId      jsr     CalcCursorTilemapPos    ;calculate address in VRAM of tile at x, y
aa73: a9 05                        lda     #NC_GetCharUnderCursor
aa75: 20 6a 89                     jsr     WaitForNmiSubcommand    ;get the char/tile ID for that
aa78: a5 73                        lda     zpCharUnderCurs
aa7a: 85 28        @finish         sta     zpWAccum                ;save value to WAccum
aa7c: 20 5f b6                     jsr     RestoreCursorPos        ;restore original CH/CV
aa7f: 4c 85 aa                     jmp     MakeSingleCharStrFromWAcc ;make a string out of the char found, and return that via expr parse stack

aa82: 20 04 aa     FnFn_CHR_STR    jsr     EvalParenByteOrError    ;get parenthesized byte-sized int arg
                   MakeSingleCharStrFromWAcc
aa85: a5 28                        lda     zpWAccum                ;get its value into A reg
                   ReturnNewSingleCharStrFromAReg
aa87: 20 94 aa                     jsr     MakeSingleCharStrFromAReg
                   SetParseStkTopToWAccStr
aa8a: a9 03                        lda     #TYP_STR
aa8c: 85 27                        sta     zpArgTyp                ;set var type = string
aa8e: 20 7d a8                     jsr     SkipRParenOrSynErr      ;read closing paren
aa91: 4c f8 a8                     jmp     SetParseStkTopToWAcc    ;return our value by setting the top value of the expression stack

                   MakeSingleCharStrFromAReg
aa94: a6 21                        ldx     zpGpStrIdx
aa96: 9d 00 07                     sta     genPurposeBuf,x         ;store value in A-reg to genPurposeBuf
aa99: e6 21                        inc     zpGpStrIdx              ;increment index to reflect our consumption of a single byte
aa9b: a9 01                        lda     #$01
aa9d: 85 28                        sta     zpWAccum                ;set first byte of WAccum to 1 (string length)
aa9f: 86 29                        stx     zpWAccum+1              ;point the next two bytes at the space
aaa1: a9 07                        lda     #>genPurposeBuf         ; in genPurposeBuf we just wrote to
aaa3: 85 2a                        sta     zpWAccum+2
aaa5: 60                           rts                             ;return

                   vZpStrLen       .var    $90    {addr/1}

aaa6: 20 0a aa     FnFn_STR_STR    jsr     EvalParenIntOrError     ;eval parenthesized int
aaa9: 20 98 91                     jsr     DecStrFromWord          ;convert int to decimal string
aaac: 20 d6 90     CopyDecOrHexStr jsr     FindFirstNonzeroDigit   ;skip leading zeroes
aaaf: a9 00                        lda     #$00
aab1: 85 90                        sta     vZpStrLen               ;start with strlen of 0
aab3: a4 21                        ldy     zpGpStrIdx
aab5: a6 46                        ldx     zpTmp46                 ;get the position of first non-zero digit into X
aab7: b5 3d        @cpyLp          lda     zpNumStrTmp,x           ;get char from decimal str
aab9: f0 0a                        beq     @copyDone               ;null terminator? -> done copying
aabb: 99 00 07                     sta     genPurposeBuf,y         ;else write char to genPurposeBuf
aabe: e8                           inx
aabf: c8                           iny
aac0: e6 90                        inc     vZpStrLen               ;increment strlen
aac2: 4c b7 aa                     jmp     @cpyLp                  ;-> copy next character

aac5: a5 21        @copyDone       lda     zpGpStrIdx              ;load original index into genPurposeBuf (str start)
aac7: 84 21                        sty     zpGpStrIdx              ;save new index
aac9: 85 29                        sta     zpWAccum+1              ;save str start low byte
aacb: a9 07                        lda     #>genPurposeBuf
aacd: 85 2a                        sta     zpWAccum+2              ;save str start high byte (genPurposeBuf)
aacf: a5 90                        lda     vZpStrLen
aad1: 85 28                        sta     zpWAccum                ;save strlen
aad3: 4c 8a aa                     jmp     SetParseStkTopToWAccStr ;-> wrap things up

aad6: 20 0a aa     FnFn_HEX_STR    jsr     EvalParenIntOrError     ;eval parenthesized int
aad9: 20 da 91                     jsr     HexStrFromWord          ;convert int to hex string
aadc: 4c ac aa                     jmp     CopyDecOrHexStr         ;proceed as for STR$

                   • Clear variables
                   vZpStrLen       .var    $74    {addr/1}

aadf: 20 17 aa     FnFn_VAL        jsr     EvalParenStrOrError     ;parse parenthesized string arg
aae2: a5 28                        lda     zpWAccum                ;empty string?
aae4: f0 27                        beq     VAL_returnZero          ;yes -> return as zero
aae6: 20 30 94                     jsr     SaveTXTPTR              ;save TXTPTR
aae9: a5 28                        lda     zpWAccum
aaeb: 85 74                        sta     vZpStrLen               ;use string length as a copy countdown
aaed: a2 00                        ldx     #$00
aaef: a0 00                        ldy     #$00
aaf1: b1 29        @cpyLp          lda     (zpWAccum+1),y          ;copy string contents into LineBuffer
aaf3: 9d 00 05                     sta     lineBuffer,x
aaf6: e8                           inx
aaf7: c8                           iny
aaf8: c6 74                        dec     vZpStrLen               ;decr chars to copy. == 0?
aafa: d0 f5                        bne     @cpyLp                  ;no -> loop and copy next character
aafc: a9 00                        lda     #$00
aafe: 9d 00 05                     sta     lineBuffer,x            ;null-terminate the string
ab01: 20 a4 8b                     jsr     InitLineBufferScanning  ;point TXTPTR at LineBuffer
ab04: 20 2d ab                     jsr     READ_parseNumber        ;parse a number from the string into WAccum
ab07: 20 39 94                     jsr     RestoreTXTPTR           ;restore TXTPTR to past the argument
ab0a: 4c 7e a9                     jmp     SkipRParenSetParseStkInt ;skip past right-paren, and return the value in WAccum onto parse stack

ab0d: 20 ad a8     VAL_returnZero  jsr     ZeroWAccum
ab10: 4c 7e a9                     jmp     SkipRParenSetParseStkInt

                   • Clear variables

                   ParseAndNegateDecWord
ab13: 20 8c 90                     jsr     TxtPtrIncr              ;advance txtptr
ab16: 20 bf 90                     jsr     TxtSkipSpaces           ;and skip any spaces
ab19: 20 c8 90                     jsr     IsDigit                 ;cur char a digit?
ab1c: b0 06                        bcs     @recurse                ;no -> look for another '-', '+', or (especially) '&'
ab1e: 20 e4 90                     jsr     ParsePosOrNegDecWFromTxt ;parse a (possibly re-negated) number
ab21: 4c 32 8e                     jmp     WAccumNegate            ;and negate it, returning.

ab24: 20 2d ab     @recurse        jsr     READ_parseNumber
ab27: 4c 32 8e                     jmp     WAccumNegate

ab2a: 20 8c 90     @advAndRedo     jsr     TxtPtrIncr
                   READ_parseNumber
ab2d: a0 00                        ldy     #$00
ab2f: b1 09                        lda     (zpTXTPTR),y
ab31: c9 2d                        cmp     #‘-’                    ;cur char == '-'?
ab33: f0 de                        beq     ParseAndNegateDecWord   ;yes -> parse and negate a number
ab35: c9 2b                        cmp     #‘+’                    ;cur char == '+'?
ab37: f0 f1                        beq     @advAndRedo             ;yes -> ignore, advance, and restart these checks
ab39: c9 26                        cmp     #‘&’                    ;cur char == '&'?
ab3b: f0 0c                        beq     READ_parseHexNum
ab3d: 20 e4 90                     jsr     ParsePosOrNegDecWFromTxt ;<- it'll be positive, bc we already checked for '-'
ab40: 20 f2 8d                     jsr     Cmp32768                ;and therefore, if we get 32768, it's an overflow
ab43: f0 01                        beq     errOverfl_AB46          ;(overflow? -> error)
ab45: 60                           rts

ab46: 4c f2 8e     errOverfl_AB46  jmp     ErrorOverflow

                   READ_parseHexNum
ab49: 20 8c 90                     jsr     TxtPtrIncr
ab4c: b1 09                        lda     (zpTXTPTR),y
ab4e: c9 48                        cmp     #‘H’
ab50: d0 03                        bne     @noH                    ;skip optional 'H', and parse a hex number
ab52: 20 8c 90                     jsr     TxtPtrIncr
ab55: 4c ed 91     @noH            jmp     WordFromHex

ab58: 20 9c 84     FnFn_INKEY_STR  jsr     IsEndOfCmd              ;any chars after INKEY$?
ab5b: f0 0d                        beq     @argOmitted             ;no -> omitted arg, return active keyboard char
ab5d: c9 28                        cmp     #‘(’                    ;have a left paren?
ab5f: d0 09                        bne     @argOmitted             ;no -> omitted arg, return active keyboard char
ab61: 20 b6 a8                     jsr     EvalParenExprOrError    ;yes, it's a left parenthesis. Move past it, and a dummy expression.
ab64: 20 20 bc                     jsr     ReadChar                ;read char from keyboard, waiting for keypress
ab67: 4c 87 aa                     jmp     ReturnNewSingleCharStrFromAReg ;return that value as (single-char) string

ab6a: 20 45 b9     @argOmitted     jsr     ReadKeyInsMacros        ;read any curently-pressed keys
ab6d: f0 0b                        beq     @retEmpty               ;nothing pressed? -> return empty string
ab6f: 20 94 aa                     jsr     MakeSingleCharStrFromAReg ;else make single-char string from the keypress
ab72: 20 ee a8     @pushStr        jsr     PushWAccIntToParseStk   ;push the string in WAccum to expr parse stack
ab75: a9 03                        lda     #TYP_STR                ;and set the type accordingly
ab77: 85 27                        sta     zpArgTyp
ab79: 60                           rts

ab7a: 20 ad a8     @retEmpty       jsr     ZeroWAccum
ab7d: 4c 72 ab                     jmp     @pushStr

                   ; Illegal Value error if WAccum's high byte is non-zero
                   ErrIfWAccumIsWord
ab80: a5 29                        lda     zpWAccum+1
ab82: d0 06                        bne     errIllVal_AB8A
ab84: 60                           rts

                   EnsureLtStrLenMax
ab85: c9 20                        cmp     #STRLEN_MAX
ab87: b0 01                        bcs     errIllVal_AB8A
ab89: 60                           rts

ab8a: 4c 94 84     errIllVal_AB8A  jmp     ErrorIllegalValue

                   ReadLEFTorRIGHTstrArgs
ab8d: 20 17 aa                     jsr     EvalParenStrOrError     ;eval left paren, string argument
ab90: a5 28                        lda     zpWAccum
ab92: 48                           pha                             ;push strlen
ab93: a5 29                        lda     zpWAccum+1
ab95: 48                           pha                             ;and two pointer bytes (to the string data)
ab96: a5 2a                        lda     zpWAccum+2
ab98: 48                           pha
ab99: 20 3b ac                     jsr     CommaThenIntExprToWAccum ;eval comma, integer substr len
ab9c: 20 80 ab                     jsr     ErrIfWAccumIsWord       ;ensure it's byte-sized
ab9f: a5 28                        lda     zpWAccum
aba1: 20 85 ab                     jsr     EnsureLtStrLenMax       ;and also < max strlen
aba4: 68                           pla
aba5: 85 1a                        sta     zpMisc+1                ;pull the str ptr bytes,
aba7: 68                           pla                             ; and stuff them into zpMisc
aba8: 85 19                        sta     zpMisc
abaa: 68                           pla                             ;pull orig string length (leaving in A-reg)
abab: 60                           rts                             ;return (strptr in zpMisc, strlen in A-reg, and substr len in WAccum)

                   ; dead/unreachable code
abac: 4c 91 84                     jmp     ErrorSyntax

abaf: 20 17 aa     FnFn_INSTR      jsr     EvalParenStrOrError     ;parse first string arg (haystack)
abb2: a6 28                        ldx     zpWAccum                ;load strlen into X reg
abb4: a0 00                        ldy     #$00
abb6: b1 29        @cpyLp          lda     (zpWAccum+1),y          ;get a char from string
abb8: 99 00 05                     sta     lineBuffer,y            ;copy into lineBuffer
abbb: c8                           iny
abbc: ca                           dex                             ;copied whole string?
abbd: d0 f7                        bne     @cpyLp                  ;no -> loop back, copy next char
abbf: a5 28                        lda     zpWAccum
abc1: 48                           pha                             ;push the string length
abc2: 20 1e ac                     jsr     CommaThenExprToWAccum   ;parse comma, then 2nd string arg (needle)
abc5: 20 10 aa                     jsr     ErrorUnlessString
abc8: a5 28                        lda     zpWAccum
abca: 85 2c                        sta     zpWParam                ;copy string info from WAccum to WParam
abcc: a5 29                        lda     zpWAccum+1
abce: 85 2d                        sta     zpWParam+1
abd0: a5 2a                        lda     zpWAccum+2
abd2: 85 2e                        sta     zpWParam+2
abd4: 68                           pla                             ;pull string length
abd5: 85 28                        sta     zpWAccum                ;store in WAccum byte 0
abd7: a9 00                        lda     #<lineBuffer
abd9: 85 29                        sta     zpWAccum+1              ;copy ptr to lineBuffer (holding haystack string) into WAccum+1,2
abdb: a9 05                        lda     #>lineBuffer
abdd: 85 2a                        sta     zpWAccum+2
abdf: 20 e6 95                     jsr     CheckInStr              ;perform the matching
abe2: 85 28                        sta     zpWAccum                ;save index result into zpWAccum
abe4: a9 00                        lda     #$00                    ;(clearing high byte)
abe6: 85 29                        sta     zpWAccum+1              ;(and ancilliary bytes)
abe8: 85 2a                        sta     zpWAccum+2
abea: 4c 7e a9                     jmp     SkipRParenSetParseStkInt ;skip closing paren, and return our value via expr parse stack

                   • Clear variables
                   vZpStrLen       .var    $4a    {addr/1}
                   vZpCpyCountDwn  .var    $90    {addr/1}

abed: 20 8d ab     FnFn_LEFT_STR   jsr     ReadLEFTorRIGHTstrArgs  ;read args
abf0: 85 4a                        sta     vZpStrLen               ;save orig str len
abf2: a5 28                        lda     zpWAccum                ;substr len == 0?
abf4: f0 16                        beq     LEFT_RIGHT_returnEmptyStr ;yes -> return empty str
abf6: c5 4a                        cmp     vZpStrLen               ;substr len >= orig str len?
abf8: b0 0b                        bcs     LEFT_RIGHT_returnOrigStr ;yes -> just return orig str
                   @retFromSetOrigStr
abfa: a5 19                        lda     zpMisc
abfc: 85 29                        sta     zpWAccum+1              ;just set return string pointer to orig:
abfe: a5 1a                        lda     zpMisc+1                ; this is LEFT$ so we can just adjust the returned string's length
                   LEFT_RIGHT_finishUp
ac00: 85 2a                        sta     zpWAccum+2              ; (conveniently already correct, and at the right addr)
ac02: 4c 8a aa                     jmp     SetParseStkTopToWAccStr ;return WAccum via expr parse stack

                   LEFT_RIGHT_returnOrigStr
ac05: a5 4a                        lda     vZpStrLen
ac07: 85 28                        sta     zpWAccum                ;set orig str len as substr len
ac09: 4c fa ab                     jmp     @retFromSetOrigStr      ;just return orig str, substr with same len is just orig str

                   LEFT_RIGHT_returnEmptyStr
ac0c: 85 29                        sta     zpWAccum+1              ;clear WAccum+1 (+2 will clear after jmp below)
ac0e: 4c 00 ac                     jmp     LEFT_RIGHT_finishUp     ;clear WAccum+2 and return on expr parse stack

ac11: 4c 91 84     SC_errSyn       jmp     ErrorSyntax

                   SkipCommaOrSynErr
ac14: 20 bf 90                     jsr     TxtSkipSpaces
ac17: c9 2c                        cmp     #‘,’
ac19: d0 f6                        bne     SC_errSyn
ac1b: 4c 8c 90                     jmp     TxtPtrIncr

                   CommaThenExprToWAccum
ac1e: 20 14 ac                     jsr     SkipCommaOrSynErr       ;demand a comma here
ac21: 20 9b a2                     jsr     Expression              ;parse an expression
ac24: a6 30                        ldx     zpExprStkIdx            ;pull the expression value, and
ac26: ca                           dex                             ; save it to zpWAccum
ac27: bd 00 04                     lda     exprParseStk,x
ac2a: 85 2a                        sta     zpWAccum+2
ac2c: ca                           dex
ac2d: bd 00 04                     lda     exprParseStk,x
ac30: 85 29                        sta     zpWAccum+1
ac32: ca                           dex
ac33: bd 00 04                     lda     exprParseStk,x
ac36: 85 28                        sta     zpWAccum
ac38: 86 30                        stx     zpExprStkIdx            ;save new reduced index (effecting the "pull" from expr stack)
ac3a: 60                           rts                             ;done

                   CommaThenIntExprToWAccum
ac3b: 20 1e ac                     jsr     CommaThenExprToWAccum
ac3e: 4c 0d aa                     jmp     ErrorUnlessInteger

ac41: 20 8d ab     FnFn_RIGHT_STR  jsr     ReadLEFTorRIGHTstrArgs  ;read args
ac44: 85 4a                        sta     vZpStrLen               ;save str arg's length
ac46: a5 28                        lda     zpWAccum                ;get substr length
ac48: f0 c2                        beq     LEFT_RIGHT_returnEmptyStr ; == 0? -> return empty str
ac4a: c5 4a                        cmp     vZpStrLen               ; substr len >= orig str len?
ac4c: b0 b7                        bcs     LEFT_RIGHT_returnOrigStr ;yes -> just return the contents of original string
ac4e: 38                           sec
ac4f: a5 4a                        lda     vZpStrLen               ;else, subtract substr len from full str len
ac51: e5 28                        sbc     zpWAccum
ac53: 18                           clc
ac54: 65 19                        adc     zpMisc                  ;add to str ptr
ac56: 85 19                        sta     zpMisc
ac58: a9 00                        lda     #$00
ac5a: 65 1a                        adc     zpMisc+1                ;ptr high byte, too
ac5c: 85 1a                        sta     zpMisc+1
ac5e: a5 28                        lda     zpWAccum
ac60: 85 90                        sta     vZpCpyCountDwn          ;copy substr len to countdown var
ac62: a6 21                        ldx     zpGpStrIdx
ac64: a0 00                        ldy     #$00
ac66: b1 19        @cpyLp          lda     (zpMisc),y              ;read char from orig str (at shifted position)
ac68: 9d 00 07                     sta     genPurposeBuf,x         ;copy into general-purpose buffer
ac6b: e8                           inx
ac6c: c8                           iny
ac6d: c6 90                        dec     vZpCpyCountDwn          ;decrement # chars to copy. == 0?
ac6f: d0 f5                        bne     @cpyLp                  ;no -> loop around, copy next char
ac71: a5 21                        lda     zpGpStrIdx              ;point WAccum at general-purpose buffer
ac73: 85 29                        sta     zpWAccum+1              ; location we copied to
ac75: 86 21                        stx     zpGpStrIdx
ac77: a9 07                        lda     #>genPurposeBuf
ac79: 85 2a                        sta     zpWAccum+2
ac7b: 4c 8a aa                     jmp     SetParseStkTopToWAccStr ;-> return the copied string via expr parse stack

ac7e: 4c 94 84     errIllVal_AC7E  jmp     ErrorIllegalValue

                   • Clear variables
                   vZpStrPtr       .var    $19    {addr/2}
                   vZpSubStrStartIdx .var  $1b    {addr/1}
                   vZpSubStrLen    .var    $1c    {addr/1}
                   vZpCpyCountDwn  .var    $90    {addr/1}

ac81: 20 17 aa     FnFn_MID_STR    jsr     EvalParenStrOrError     ;read paren, string arg
ac84: a5 28                        lda     zpWAccum
ac86: 48                           pha                             ;push the string info
ac87: a5 29                        lda     zpWAccum+1
ac89: 48                           pha
ac8a: a5 2a                        lda     zpWAccum+2
ac8c: 48                           pha
ac8d: 20 3b ac                     jsr     CommaThenIntExprToWAccum ;next read start index arg
ac90: 20 80 ab                     jsr     ErrIfWAccumIsWord       ;ensure it's < 256
ac93: a5 28                        lda     zpWAccum                ; == 0?
ac95: f0 e7                        beq     errIllVal_AC7E          ;yes -> invalid index
ac97: 20 85 ab                     jsr     EnsureLtStrLenMax       ; >= 32? also invalid index
ac9a: 38                           sec
ac9b: e9 01                        sbc     #$01                    ;sub 1 (cvt from 1-based index to 0-based)
ac9d: 48                           pha                             ;push the index
ac9e: 20 3b ac                     jsr     CommaThenIntExprToWAccum ;read substr length arg
aca1: 20 80 ab                     jsr     ErrIfWAccumIsWord       ;ensure < 256
aca4: a5 28                        lda     zpWAccum                ; == 0?
aca6: f0 50                        beq     MID_pullArgsRetEmptyStr ;yes -> pull up args, return empty string
aca8: 20 85 ab                     jsr     EnsureLtStrLenMax       ;ensure substr len < max strlen
acab: 85 1c                        sta     vZpSubStrLen            ;save the substr len
acad: 68                           pla                             ;pull the start index
acae: 85 1b                        sta     vZpSubStrStartIdx       ;and save that too
acb0: 68                           pla                             ;also the original string's ptr
acb1: 85 1a                        sta     vZpStrPtr+1
acb3: 68                           pla
acb4: 85 19                        sta     vZpStrPtr
acb6: 68                           pla                             ;pull orig str's len
acb7: c5 1b                        cmp     vZpSubStrStartIdx       ; <= start index?
acb9: f0 41                        beq     MID_retEmptyStr         ;yes -> return empty string
acbb: 90 3f                        bcc     MID_retEmptyStr
acbd: 38                           sec
acbe: e5 1b                        sbc     vZpSubStrStartIdx       ;orig str len - substr start idx
acc0: c5 1c                        cmp     vZpSubStrLen            ; < substr len?
acc2: 90 2f                        bcc     MID_setSubStrLen        ;yes -> set remaining len as the new substr len
                   MID_setLenJmpBack
acc4: a5 1b                        lda     vZpSubStrStartIdx       ; (and return back here)
acc6: 18                           clc
acc7: 65 19                        adc     vZpStrPtr               ;add start idx to str ptr
acc9: 85 19                        sta     vZpStrPtr               ; and save
accb: a9 00                        lda     #$00
accd: 65 1a                        adc     vZpStrPtr+1             ;(high byte, too)
accf: 85 1a                        sta     vZpStrPtr+1
acd1: a5 1c                        lda     vZpSubStrLen
acd3: 85 28                        sta     zpWAccum                ;save substr len to WAccum
acd5: 85 90                        sta     vZpCpyCountDwn          ;and use as # chars to copy
acd7: a6 21                        ldx     zpGpStrIdx
acd9: a0 00                        ldy     #$00
acdb: b1 19        @cpyLp          lda     (vZpStrPtr),y           ;get a byte from str
acdd: 9d 00 07                     sta     genPurposeBuf,x         ;copy to gen-purpose str buf
ace0: e8                           inx                             ;incr X-reg (genPurpose idx)
ace1: c8                           iny                             ;incr Y-reg (source str idx)
ace2: c6 90                        dec     vZpCpyCountDwn          ;decrement chars-to-copy. Done copying?
ace4: d0 f5                        bne     @cpyLp                  ;not yet -> iterate over remaining chars
ace6: a5 21                        lda     zpGpStrIdx              ;done copying.
ace8: 85 29                        sta     zpWAccum+1              ;save ptr into genPurposeBuf as str ptr, to WAccum
acea: 86 21                        stx     zpGpStrIdx              ;update index to free space in genPurposeBuf
acec: a9 07                        lda     #>genPurposeBuf
acee: 85 2a                        sta     zpWAccum+2
acf0: 4c 8a aa                     jmp     SetParseStkTopToWAccStr ;-> return new substr on expression stack

                   MID_setSubStrLen
acf3: 85 1c                        sta     vZpSubStrLen
acf5: 4c c4 ac                     jmp     MID_setLenJmpBack       ;-> "return"

                   MID_pullArgsRetEmptyStr
acf8: 68                           pla                             ;pull args off stack
acf9: 68                           pla
acfa: 68                           pla
acfb: 68                           pla
acfc: 20 ad a8     MID_retEmptyStr jsr     ZeroWAccum              ;zero the WAccum
acff: 4c 8a aa                     jmp     SetParseStkTopToWAccStr ;-> return empty string

                   • Clear variables
                   vZpPadBtns      .var    $90    {addr/1}         ;Holds state of gamepad buttons

ad02: 4c 94 84     errIllVal_AD02  jmp     ErrorIllegalValue

                   EvalLParenPadNumAndGetBtns
ad05: 20 04 aa                     jsr     EvalParenByteOrError    ;read left paren, then byte-size int arg
                   ; Get the state of buttons (including d-pad) as a set of 8 boolean bits, into
                   ; $90. WAccum indicates which gamepad to read.
ad08: a5 28        GetButtons      lda     zpWAccum                ;get byte-size val from WAccum
ad0a: f0 07                        beq     @zero                   ; == 0? -> skip ahead a bit
ad0c: c9 01                        cmp     #$01                    ;else, eq 1?
ad0e: d0 f2                        bne     errIllVal_AD02          ;neither 0 nor 1 -> error
ad10: a9 17                        lda     #<JOY2                  ;we'll read from JOY2
ad12: ae                           ldx ▼   $16a9                   ;(skip next instr)
ad13: a9 16        @zero           lda     #<JOY1                  ;we'll read from JOY1
ad15: 85 19                        sta     zpMisc
ad17: a9 40                        lda     #>JOY1
ad19: 85 1a                        sta     zpMisc+1
ad1b: a9 05                        lda     #$05
ad1d: 8d 16 40                     sta     JOY1                    ;tell controllers to load shfit registers
ad20: ea                           nop                             ;wait a bit
ad21: ea                           nop
ad22: a9 04                        lda     #$04
ad24: 8d 16 40                     sta     JOY1                    ;lock shift registers
ad27: ea                           nop                             ;wait a bit
ad28: ea                           nop
ad29: a2 08                        ldx     #$08                    ;set X-reg as counter for 8 single-bit reads
ad2b: a0 00                        ldy     #$00
ad2d: b1 19        @joyRdLp        lda     (zpMisc),y              ;read from gamepad
ad2f: 4a                           lsr     A                       ;shift into carry
ad30: 26 90                        rol     vZpPadBtns              ;shift from carry onto gamepad buttons-state var
ad32: ea                           nop                             ;wait a bit before next gamepad read
ad33: ea                           nop
ad34: ca                           dex                             ;decr bit counter. Did we record 8 bits/buttons?
ad35: d0 f6                        bne     @joyRdLp                ;no -> loop around for next button state
ad37: 60                           rts                             ;yes: done

ad38: 20 05 ad     FnFn_STICK      jsr     EvalLParenPadNumAndGetBtns ;parse gamepad-num arg, and get buttons state for it
ad3b: 20 08 ad                     jsr     GetButtons              ;re-read the buttons (why?!)
ad3e: a5 90                        lda     vZpPadBtns
ad40: 29 0f                        and     #$0f                    ;mask out everything but d-pad buttons
                   ST_setWAccumAndSaveExpr
ad42: 85 28                        sta     zpWAccum                ;save this val to WAccum
ad44: 4c 7e a9                     jmp     SkipRParenSetParseStkInt ;-> and return as our result

ad47: 20 05 ad     FnFn_STRIG      jsr     EvalLParenPadNumAndGetBtns ;parse gamepad-num arg, and get buttons state for it
ad4a: a5 90                        lda     vZpPadBtns
ad4c: 4a                           lsr     A                       ;shift off the d-pad state, leaving
ad4d: 4a                           lsr     A                       ; other buttons in the lower four bits
ad4e: 4a                           lsr     A
ad4f: 4a                           lsr     A
ad50: 4c 42 ad                     jmp     ST_setWAccumAndSaveExpr ;-> and return the result

                   ; shifts A left 4 times, effectively multiplying it by 16
                   • Clear variables

ad53: 0a           MulABy16        asl     A
ad54: 0a                           asl     A
ad55: 0a                           asl     A
ad56: 0a                           asl     A
ad57: 60                           rts

ad58: 4c 94 84     errIllVal_AD58  jmp     ErrorIllegalValue

ad5b: c9 2c        CmdFn_GAME      cmp     #‘,’                    ;start with comma (2nd arg)?
ad5d: f0 2b                        beq     GAME_mapOnly            ;yes -> go handle
ad5f: 20 6e a2                     jsr     EvalByteInteger         ;else parse first arg
ad62: c9 04                        cmp     #$04                    ; >= 4?
ad64: b0 f2                        bcs     errIllVal_AD58          ;yes -> error
ad66: 85 f3                        sta     zpGameNum               ;save arg as "game number"
ad68: 20 fe ad                     jsr     CmdFn_CLS_Scr1          ;clear SCREEN 1
ad6b: 20 9c 84                     jsr     IsEndOfCmd              ;no more args?
ad6e: f0 0f                        beq     @oneArg                 ;correct -> skip handling arg 2
ad70: 20 14 ac                     jsr     SkipCommaOrSynErr       ;read expected comma
ad73: 20 6e a2                     jsr     EvalByteInteger         ;parse arg 2. Des it equal zero?
ad76: f0 07                        beq     @oneArg                 ;yes -> same as no 2nd arg
ad78: c9 01                        cmp     #$01
ad7a: d0 dc                        bne     errIllVal_AD58
ad7c: 20 d1 ad                     jsr     GAME_loadBgMap          ;load up the background map
ad7f: 20 62 86     @oneArg         jsr     InitVars
ad82: 20 96 ad                     jsr     GAME_loadGame
ad85: a9 00                        lda     #$00
ad87: 4c b0 86                     jmp     CmdFn_RUN

ad8a: 20 14 ac     GAME_mapOnly    jsr     SkipCommaOrSynErr       ;skip over comma (can't fail: we know it's there)
ad8d: 20 6e a2                     jsr     EvalByteInteger         ;parse "second" arg
ad90: f0 03                        beq     @rts                    ;"GAME ,0"? -> done (nothing to do).
ad92: 20 d1 ad                     jsr     GAME_loadBgMap          ;just load the background map
ad95: 60           @rts            rts                             ; then exit (no game)

ad96: a5 05        GAME_loadGame   lda     zpTXTTAB
ad98: 85 19                        sta     zpMisc                  ;init write pointer to start of TXTTAB
ad9a: a5 06                        lda     zpTXTTAB+1
ad9c: 85 1a                        sta     zpMisc+1
ad9e: a5 f3                        lda     zpGameNum               ;recall game num
ada0: 0a                           asl     A                       ;multiply by 2 for indexing
ada1: aa                           tax
ada2: bd 03 80                     lda     tbl_GameTXTENDs,x       ;load TXTEND value for this game
ada5: 85 07                        sta     zpTXTEND                ;and store it as the current one
ada7: bd 04 80                     lda     tbl_GameTXTENDs+1,x
adaa: 85 08                        sta     zpTXTEND+1
adac: bd 0b 80                     lda     tbl_GameTexts,x         ;init read ptr to start of the selected game
adaf: 85 1b                        sta     zpMisc+2
adb1: bd 0c 80                     lda     tbl_GameTexts+1,x
adb4: 85 1c                        sta     zpMisc+3
adb6: a0 00                        ldy     #$00
adb8: b1 1b        @cpyLp          lda     (zpMisc+2),y            ;load a byte from in-ROM game
adba: 91 19                        sta     (zpMisc),y              ;store it in TXTTAB
adbc: e6 1b                        inc     zpMisc+2                ;increment source ptr
adbe: d0 02                        bne     @noHiSrc
adc0: e6 1c                        inc     zpMisc+3
adc2: e6 19        @noHiSrc        inc     zpMisc                  ;increment dest ptr
adc4: d0 02                        bne     @noHiDst
adc6: e6 1a                        inc     zpMisc+1
adc8: a9 70        @noHiDst        lda     #(>memoryTop)+1
adca: c5 1a                        cmp     zpMisc+1                ;copy to end of RAM at $7000 (not to TXTEND!)
adcc: d0 ea                        bne     @cpyLp
adce: 4c a6 97                     jmp     Clear

add1: a9 00        GAME_loadBgMap  lda     #<gameBackgroundMap     ;prep background map for reading
add3: 85 19                        sta     zpMisc
add5: a9 d0                        lda     #>gameBackgroundMap
add7: 85 1a                        sta     zpMisc+1
add9: 20 59 b5                     jsr     AwaitVBlankAndClearPpuMask ;wait until safe to do PPU ops. Disable rendering.
addc: a9 24                        lda     #>VRAM_screen1
adde: 8d 06 20                     sta     PPUADDR
ade1: a9 00                        lda     #<VRAM_screen1
ade3: 8d 06 20                     sta     PPUADDR
ade6: a0 00                        ldy     #$00
ade8: b1 19        @cpyLp          lda     (zpMisc),y              ;read a byte from built-in map
adea: 8d 07 20                     sta     PPUDATA                 ;write byte to screen 1
aded: c8                           iny                             ;advance read idx. Finished reading one page?
adee: d0 f8                        bne     @cpyLp                  ;no -> loop around copy next byte
adf0: e6 1a                        inc     zpMisc+1                ;increment source page num
adf2: a9 d4                        lda     #$d4
adf4: c5 1a                        cmp     zpMisc+1                ;are we at RAM page $D4?
adf6: d0 f0                        bne     @cpyLp                  ;no -> copy another page to VRAM
adf8: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi ;yes: ensure NMI enabled
adfb: 4c 62 b5                     jmp     AwaitVBlankAndUpdatePpuMask ;and re-enable rendering. Return

adfe: a9 01        CmdFn_CLS_Scr1  lda     #$01
ae00: 85 28                        sta     zpWAccum
ae02: 4c 13 ae                     jmp     cls_fakedArg

                   @cls_noScrSwNeeded
ae05: 68                           pla
ae06: 68                           pla
ae07: 4c ab b4     doCls           jmp     DoCLS

ae0a: f0 fb        CmdFn_CLS       beq     doCls                   ;no args ->
ae0c: 20 6e a2                     jsr     EvalByteInteger         ;get int arg
ae0f: c9 02                        cmp     #$02                    ; < 2?
ae11: b0 32                        bcs     errIllVal_ae45          ;no -> err
ae13: a5 49        cls_fakedArg    lda     zpCV
ae15: 48                           pha                             ;push orig cursor Y pos
ae16: a5 4f                        lda     zpPpuScreenBasePage
ae18: 48                           pha                             ;push orig upd scr
ae19: a5 28                        lda     zpWAccum
ae1b: f0 0e                        beq     @swScr0                 ;arg is 0? -> switch to that
ae1d: a9 24                        lda     #>VRAM_screen1          ;no: switch to scr 1
ae1f: c5 4f                        cmp     zpPpuScreenBasePage     ;already updating scr 1?
ae21: f0 e2                        beq     @cls_noScrSwNeeded      ;yes -> leave alone, discard saved pos
ae23: 85 4f                        sta     zpPpuScreenBasePage     ;no -> do the switch
ae25: a9 00                        lda     #$00
ae27: 85 e5                        sta     zpVScroll1              ;and clear scroll pos
ae29: f0 0c                        beq     @doCls

ae2b: a9 20        @swScr0         lda     #>VRAM_screen0
ae2d: c5 4f                        cmp     zpPpuScreenBasePage     ;already updating scr 0?
ae2f: f0 d4                        beq     @cls_noScrSwNeeded      ;yes -> leave alone, discard saved pos
ae31: 85 4f                        sta     zpPpuScreenBasePage     ;no -> do the switch
ae33: a9 00                        lda     #$00
ae35: 85 e4                        sta     zpVScroll0              ;and clear scroll pos
ae37: 20 ab b4     @doCls          jsr     DoCLS
ae3a: 68                           pla
ae3b: 85 4f                        sta     zpPpuScreenBasePage     ;restore original update screen
ae3d: 68                           pla
ae3e: 85 49                        sta     zpCV                    ;and original cursor Y pos
ae40: 60                           rts

ae41: 20 3c b5     nmiOnAndRts     jsr     NmiSetVScrollAndEnableNmi
ae44: 60                           rts

ae45: 4c 94 84     errIllVal_ae45  jmp     ErrorIllegalValue

ae48: 20 6e a2     CmdFn_SCREEN    jsr     EvalByteInteger         ;get int arg.
ae4b: f0 04                        beq     @got0or1                ;== 0? ->
ae4d: c9 01                        cmp     #$01                    ;== 1?
ae4f: d0 f4                        bne     errIllVal_ae45
ae51: 29 ff        @got0or1        and     #$ff
ae53: 85 f0                        sta     zpCurScreenNum
ae55: f0 31                        beq     @useScr0                ;SCREEN 0? ->
ae57: a9 24                        lda     #>VRAM_screen1          ;SCREEN 1. Save base addr high byte.
ae59: 85 4f                        sta     zpPpuScreenBasePage
ae5b: a5 32                        lda     zpPpuCtrlVal
ae5d: 09 01                        ora     #$01                    ;Tell PPU to switch active screen to 1
                   @setPpuCtrlAndCont
ae5f: 85 32                        sta     zpPpuCtrlVal
ae61: 20 2b b4                     jsr     WaitForVBlank
ae64: 20 14 be                     jsr     SetPpuCtrlAndEnableNmi
ae67: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi
ae6a: 20 48 b7                     jsr     CursorHome              ;LOCATE 0,0
ae6d: 20 9c 84                     jsr     IsEndOfCmd              ;done with args?
ae70: f0 cf                        beq     nmiOnAndRts             ;yes -> finish up
ae72: 20 14 ac                     jsr     SkipCommaOrSynErr
ae75: 20 6e a2                     jsr     EvalByteInteger         ;get next int arg
ae78: f0 09                        beq     @secondSrc0             ;== 0? ->
ae7a: c9 01                        cmp     #$01                    ;or == 1?
ae7c: d0 c7                        bne     errIllVal_ae45          ;neither? -> illegal!
ae7e: a9 24                        lda     #>VRAM_screen1          ;== 1. send updates, only, to 1.
ae80: 85 4f                        sta     zpPpuScreenBasePage
ae82: 60                           rts

ae83: a9 20        @secondSrc0     lda     #>VRAM_screen0          ;send updates, only, to scr 0.
ae85: 85 4f                        sta     zpPpuScreenBasePage
ae87: 60                           rts

ae88: a9 20        @useScr0        lda     #>VRAM_screen0          ;SCREEN 0. Save base addr high byte.
ae8a: 85 4f                        sta     zpPpuScreenBasePage
ae8c: a5 32                        lda     zpPpuCtrlVal
ae8e: 29 fe                        and     #$fe                    ;Tell PPU to switch active screen to 0
ae90: 4c 5f ae                     jmp     @setPpuCtrlAndCont

ae93: 4c 94 84     errIllVal_AE93  jmp     ErrorIllegalValue

                   BgtFile_GoToScreen0
ae96: a9 00                        lda     #$00
ae98: 85 f0                        sta     zpCurScreenNum          ;set SCREEN 0
ae9a: a9 20                        lda     #>VRAM_screen0
ae9c: 85 4f                        sta     zpPpuScreenBasePage
ae9e: a5 32                        lda     zpPpuCtrlVal
aea0: 29 fe                        and     #$fe                    ;mask out bg base addr, so SCREEN 0 is displayed by PPU
aea2: 85 32                        sta     zpPpuCtrlVal
aea4: 4c 14 be                     jmp     SetPpuCtrlAndEnableNmi  ;-> enact updated PPUCTRL, then return

                   ; dead/unreachable code
aea7: a9 01                        lda     #$01
aea9: 85 f0                        sta     zpCurScreenNum
aeab: a9 24                        lda     #>VRAM_screen1
aead: 85 4f                        sta     zpPpuScreenBasePage
aeaf: a5 32                        lda     zpPpuCtrlVal
aeb1: 09 01                        ora     #$01
aeb3: 85 32                        sta     zpPpuCtrlVal
aeb5: 4c 14 be                     jmp     SetPpuCtrlAndEnableNmi

aeb8: 20 6e a2     CmdFn_FILTER    jsr     EvalByteInteger         ;read arg
aebb: c9 08                        cmp     #$08                    ; >= 8?
aebd: b0 d4                        bcs     errIllVal_AE93          ;yes -> error
aebf: 20 53 ad                     jsr     MulABy16                ;shift the arg val left 5 places
aec2: 0a                           asl     A
aec3: 85 75                        sta     zpHeldKeyChar           ;store in some handy var
aec5: a5 33                        lda     zpPpuMaskVal            ;load PPUMASK value
aec7: 29 1f                        and     #$1f                    ;mask out the filter bits
aec9: 05 75                        ora     zpHeldKeyChar           ;merge with our shifted arg
aecb: 85 33                        sta     zpPpuMaskVal            ;save PPUMASK value
aecd: 8d 01 20                     sta     PPUMASK                 ;and update it in the PPU
aed0: 60                           rts                             ;done!

aed1: 20 6e a2     CmdFn_CGEN      jsr     EvalByteInteger         ;get argument
aed4: c9 04                        cmp     #$04                    ; >= 4?
aed6: b0 bb                        bcs     errIllVal_AE93          ;yes -> error
aed8: 0a                           asl     A                       ;shift the value left 3 places:
aed9: 0a                           asl     A                       ; from bits 0 and 1 to bits 3 and 4.
aeda: 0a                           asl     A
aedb: 85 74                        sta     zpCursFlashCtr          ;save away for a tick
aedd: a5 32                        lda     zpPpuCtrlVal            ;get the PPUCTRL value
aedf: 29 e7                        and     #$e7                    ;mask away everything but bits 3 and 4
aee1: 05 74                        ora     zpCursFlashCtr          ;now combine with the value we got form user arg
aee3: 85 32                        sta     zpPpuCtrlVal            ;save
aee5: 4c 14 be                     jmp     SetPpuCtrlAndEnableNmi  ;and update

                   ; Clear all sprite tiles and attrs, move positions off-screen
aee8: a2 00        ClearOam        ldx     #$00
aeea: a9 ef                        lda     #$ef
aeec: 9d 00 02     @lp             sta     oamBuffer,x
aeef: e8                           inx
aef0: d0 fa                        bne     @lp
aef2: 60                           rts

                   vZpColorCntUp   .var    $02    {addr/1}         ;count down to write palette colors
                   vZpColorCntDown .var    $7c    {addr/1}         ;count up, the actual color num to set

aef3: a2 00        CmdFn_PALET     ldx     #$00
aef5: 86 65                        stx     zpPaletBgOrSpr          ;start with assumption: bg sprite
aef7: c9 42                        cmp     #‘B’                    ;first arg 'B'?
aef9: f0 08                        beq     @bgPal                  ;yes -> user said bg palette, our guess was right!
aefb: c9 53                        cmp     #‘S’                    ;first arg 'S'?
aefd: d0 4d                        bne     errSyn_AF4C             ;no -> err
aeff: a9 10                        lda     #$10                    ;set sprite palette offset
af01: 85 65                        sta     zpPaletBgOrSpr
af03: 20 65 a2     @bgPal          jsr     AdvThenEvalByteInt      ;which palette num?
af06: c9 04                        cmp     #$04                    ; >= 4?
af08: b0 3f                        bcs     errIllVal_AF49          ;yes -> error
af0a: 48                           pha                             ;push palette num
af0b: a9 04                        lda     #$04
af0d: 85 02                        sta     vZpColorCntUp           ;four colors in a palette
af0f: a9 00                        lda     #$00
af11: 85 7c                        sta     vZpColorCntDown         ;start with color #0
af13: 20 6b a2     @colorLp        jsr     EvalCommaThenByteInt    ;read comma, then user's color arg
af16: a4 7c                        ldy     vZpColorCntDown
af18: 99 3d 00                     sta     zpNumStrTmp,y           ;write color into arbitrary buf
af1b: e6 7c                        inc     vZpColorCntDown         ;incr color #
af1d: c6 02                        dec     vZpColorCntUp           ;did we do 4?
af1f: d0 f2                        bne     @colorLp                ;no -> loop to next color
af21: 68                           pla                             ;pull the palette num
af22: 8d 00 07                     sta     genPurposeBuf
af25: a5 3d                        lda     zpNumStrTmp             ;copy color choices into genPurposeBuf
af27: 8d 01 07                     sta     genPurposeBuf+1         ; (why didn't we just write there
af2a: a5 3e                        lda     zpNumStrTmp+1           ; in the first place?!)
af2c: 8d 02 07                     sta     genPurposeBuf+2
af2f: a5 3f                        lda     zpNumStrTmp+2
af31: 8d 03 07                     sta     genPurposeBuf+3
af34: a5 40                        lda     zpNumStrTmp+3
af36: 8d 04 07                     sta     genPurposeBuf+4
af39: a0 06                        ldy     #NC_SetBgPalette        ;select "set bg palette" cmd
af3b: a6 65                        ldx     zpPaletBgOrSpr          ;what did user actually ask for?
af3d: f0 01                        beq     @bg                     ;bg? -> good call then
af3f: c8                           iny                             ;spr -> #NC_SetSprPalette it is
af40: 98           @bg             tya
af41: 4c 6a 89                     jmp     WaitForNmiSubcommand    ;set the palette

                   ErrUnlessZeroThruSeven
af44: c9 08                        cmp     #$08
af46: b0 01                        bcs     errIllVal_AF49
af48: 60                           rts

af49: 4c 94 84     errIllVal_AF49  jmp     ErrorIllegalValue

af4c: 4c 91 84     errSyn_AF4C     jmp     ErrorSyntax

                   ; Selects a preset set-of-four-pallets, to save as current pallet.
                   ; First arg is bg pallet set (0 or 1), 2nd is sprite pallet set (0, 1 or 2).
                   • Clear variables
                   vZpPaletteSetPtr .var   $52    {addr/2}
                   vZpNmiCmd       .var    $73    {addr/1}         ;which palette-setting NMI subcmd we'll use
                   vZpColorCpyCtr  .var    $74    {addr/1}
                   vZpPaletteCpyIdx .var   $75    {addr/1}         ;iterates the selected palette set as copy source

af4f: c9 2c        CmdFn_CGSET     cmp     #‘,’                    ;got comma?
af51: f0 25                        beq     @arg2                   ;skip arg one, go to arg two
af53: 20 6e a2                     jsr     EvalByteInteger         ;evaluate arg one
af56: f0 04                        beq     @argGood                ;error unless it's zero or one
af58: c9 01                        cmp     #$01
af5a: d0 ed                        bne     errIllVal_AF49
af5c: 0a           @argGood        asl     A                       ;multiply by two, for index into table
af5d: aa                           tax
af5e: bd a4 cf                     lda     tbl_CgsetBgPaletteSets,x ;get the pallet-set pointer from table
af61: 85 52                        sta     vZpPaletteSetPtr
af63: bd a5 cf                     lda     tbl_CgsetBgPaletteSets+1,x
af66: 85 53                        sta     vZpPaletteSetPtr+1
af68: a9 00                        lda     #$00
af6a: 85 65                        sta     zpPaletBgOrSpr          ;indicate we're doing background palette set
af6c: 20 93 af                     jsr     CGSET_SetPaletteSet     ;and set it
af6f: 20 9c 84                     jsr     IsEndOfCmd
af72: f0 1e                        beq     @rts
af74: c9 2c                        cmp     #$2c
af76: d0 d4                        bne     errSyn_AF4C
af78: 20 65 a2     @arg2           jsr     AdvThenEvalByteInt      ;evaluate 2nd arg
af7b: c9 03                        cmp     #$03                    ;error unless 0, 1, or 2
af7d: b0 ca                        bcs     errIllVal_AF49
af7f: 0a                           asl     A                       ;multiply by two, for index into table
af80: aa                           tax
af81: bd a8 cf                     lda     tbl_CgsetSprPaletteSets,x ;get the pallet pointer from table
af84: 85 52                        sta     vZpPaletteSetPtr
af86: bd a9 cf                     lda     tbl_CgsetSprPaletteSets+1,x
af89: 85 53                        sta     vZpPaletteSetPtr+1
af8b: a9 01                        lda     #$01
af8d: 85 65                        sta     zpPaletBgOrSpr          ;indicate we're doing sprite palette set
af8f: 20 93 af                     jsr     CGSET_SetPaletteSet     ;and set it
af92: 60           @rts            rts                             ;done

                   CGSET_SetPaletteSet
af93: a9 00                        lda     #$00
af95: 8d 00 07                     sta     genPurposeBuf           ;PPU palette number (start at zero)
af98: 85 75                        sta     vZpPaletteCpyIdx        ;index for copying from palette set
af9a: a9 04        @palSetLp       lda     #$04
af9c: a2 01                        ldx     #$01                    ;just after the PPU palette number we saved for the NMI handler
af9e: 85 74                        sta     vZpColorCpyCtr          ;set to count of 4 colors to copy
afa0: a4 75                        ldy     vZpPaletteCpyIdx
afa2: b1 52        @palCpyLp       lda     (vZpPaletteSetPtr),y    ;load color from palette
afa4: 9d 00 07                     sta     genPurposeBuf,x         ;save color to genPurposeBuf
afa7: e8                           inx
afa8: c8                           iny
afa9: c6 74                        dec     vZpColorCpyCtr
afab: d0 f5                        bne     @palCpyLp               ;repeat until total of 4 colors copied
afad: 84 75                        sty     vZpPaletteCpyIdx        ;save index into palette set (we're at start of next palette to copy)
afaf: a9 06                        lda     #NC_SetBgPalette
afb1: 85 73                        sta     vZpNmiCmd               ;default to setting bg palette set; might change before use
afb3: a5 65                        lda     zpPaletBgOrSpr          ;was bg palettes correct?
afb5: f0 02                        beq     @doBg                   ;yes -> leave NMI cmd alone
afb7: e6 73                        inc     vZpNmiCmd               ;no -> use #NC_SetSprPalette instead
afb9: a5 73        @doBg           lda     vZpNmiCmd
afbb: 20 6a 89                     jsr     WaitForNmiSubcommand    ;invoke/wait for NMI palette-setting handler
afbe: ee 00 07                     inc     genPurposeBuf           ;increment which palette num we're on
afc1: ad 00 07                     lda     genPurposeBuf
afc4: c9 04                        cmp     #$04                    ;did we copy 4 palettes?
afc6: d0 d2                        bne     @palSetLp               ;no -> loop around to set next palette
afc8: 60                           rts                             ;that's the full set - we're done

                   ; Not a real CmdFn. CmdFn_SPRITE when followed by ON token.
                   • Clear variables

afc9: a5 33        CmdFn_SPRITE_ON lda     zpPpuMaskVal
afcb: 09 10                        ora     #$10                    ;add "sprites enabled" flag to PPUMASK
afcd: 85 33        @savePpu        sta     zpPpuMaskVal
afcf: 8d 01 20                     sta     PPUMASK
afd2: 4c 8c 90                     jmp     TxtPtrIncr              ;advance TXTPTR past ON/OFF token

                   ; Not a real CmdFn. CmdFn_SPRITE when followed by OFF token.
                   CmdFn_SPRITE_OFF
afd5: a5 33                        lda     zpPpuMaskVal
afd7: 29 ef                        and     #$ef                    ;mask out "sprites enabled" flag from PPUMASK
afd9: 4c cd af                     jmp     @savePpu                ;and save it

afdc: 68           SpriteErase     pla                             ;get sprite number
afdd: 20 53 ad                     jsr     MulABy16                ; *= 16
afe0: aa                           tax                             ;and transfer to X reg
afe1: a0 04                        ldy     #$04
afe3: a9 ef        @clrSprLp       lda     #SPR_NullTile           ;repeat four times (one for each sprite tile in actor):
afe5: 9d 00 02                     sta     oamSprYPos,x            ;move ypos off-screen
afe8: 9d 03 02                     sta     oamSprXPos,x            ;move xpos off-screen
afeb: 9d 01 02                     sta     oamSprTileId,x          ;wset tile ID to empty tile
afee: e8                           inx
afef: e8                           inx
aff0: e8                           inx
aff1: e8                           inx
aff2: 88                           dey
aff3: d0 ee                        bne     @clrSprLp
aff5: 60                           rts                             ;SPRITE command is finished disabling the sprite.

                   ; dead/unreachable code
aff6: 4c 94 84                     jmp     ErrorIllegalValue

aff9: 4c 91 84                     jmp     ErrorSyntax

                   vZpXPos         .var    $19    {addr/1}
                   vZpXOff         .var    $1a    {addr/1}         ;offset to add to X pos for a given tile
                   vZpYPos         .var    $1b    {addr/1}
                   vZpYOff         .var    $1c    {addr/1}         ;offset to add to Y pos for a given tile
                   vZpSpriteNum    .var    $6d    {addr/1}
                   vZpTileCtr      .var    $74    {addr/1}
                   vZpTwoByTwo     .var    $90    {addr/1}         ;Whether the sprite is 2x2 tiles (1), or 1x1 tiles (0)

affc: c9 9a        CmdFn_SPRITE    cmp     #tok_ON
affe: f0 c9                        beq     CmdFn_SPRITE_ON
b000: c9 9b                        cmp     #tok_OFF
b002: f0 d1                        beq     CmdFn_SPRITE_OFF
b004: 20 6e a2                     jsr     EvalByteInteger         ;get sprite num
b007: 20 44 af                     jsr     ErrUnlessZeroThruSeven  ;complain if not 0-7
b00a: 48                           pha                             ;push
b00b: 20 9c 84                     jsr     IsEndOfCmd              ;is that all there is?
b00e: f0 cc                        beq     SpriteErase             ;yes -> disable the sprite
b010: 20 6b a2                     jsr     EvalCommaThenByteInt    ;no, then expect a comma followed by an int (x pos)
b013: 48                           pha                             ;push x pos
b014: 20 6b a2                     jsr     EvalCommaThenByteInt    ;expect another comma and int (y pos)
b017: 85 1b                        sta     vZpYPos                 ;store it
b019: 68                           pla
b01a: 85 19                        sta     vZpXPos                 ;store x pos
b01c: 68                           pla
b01d: 85 6d                        sta     vZpSpriteNum            ;and sprite num
b01f: aa                           tax                             ;and move sprite num into X reg
b020: bd 3d 06                     lda     sprTwoByTwoFlags,x
b023: 85 90                        sta     vZpTwoByTwo             ;save info on whether this sprite is 1x1 or 2x2
b025: a5 6d                        lda     vZpSpriteNum            ;get sprite num
b027: 0a                           asl     A                       ; *= 4
b028: 0a                           asl     A
b029: aa                           tax                             ;save as X-reg
b02a: 0a                           asl     A
b02b: 0a                           asl     A                       ; *= 4 again (so, * 16 total)
b02c: 48                           pha                             ;push that for later
b02d: a8                           tay                             ;and also set Y-reg
b02e: a9 04                        lda     #$04
b030: 85 74                        sta     vZpTileCtr              ;counter for 4 tiles of the sprite character
b032: bd 80 06     @tileCpy        lda     sprTileMaps,x           ;copy tile ID from sprite char definition
b035: 99 01 02                     sta     oamSprTileId,y          ;copy to its spot in the OAM buffer
b038: e8                           inx                             ;increment one for source
b039: c8                           iny                             ;and four for dest (OAM buffer)
b03a: c8                           iny
b03b: c8                           iny
b03c: c8                           iny
b03d: c6 74                        dec     vZpTileCtr              ;decrement tile counter
b03f: d0 f1                        bne     @tileCpy                ;tiles left to copy? -> loop until we did 4
b041: 68                           pla                             ;pull sprite # times 16
b042: a8                           tay                             ;reset Y-reg to that again
b043: a5 1b                        lda     vZpYPos
b045: 99 00 02                     sta     oamSprYPos,y            ;copy Y position to OAM
b048: a5 19                        lda     vZpXPos
b04a: 99 03 02                     sta     oamSprXPos,y            ;and X position
b04d: a5 90                        lda     vZpTwoByTwo             ;is this a 2x2 sprite character?
b04f: d0 01                        bne     @setOtherTiles          ;yes -> set X and Y positions for those tiles, too
b051: 60                           rts                             ;no: done!

b052: a9 00        @setOtherTiles  lda     #$00                    ;top-right tile
b054: 85 1c                        sta     vZpYOff
b056: a9 08                        lda     #$08
b058: 85 1a                        sta     vZpXOff
b05a: 20 6e b0                     jsr     @setOneTile
b05d: a9 00                        lda     #$00                    ;bottom-left tile
b05f: 85 1a                        sta     vZpXOff
b061: a9 08                        lda     #$08
b063: 85 1c                        sta     vZpYOff
b065: 20 6e b0                     jsr     @setOneTile
b068: a9 08                        lda     #$08                    ;bottom-right tile
b06a: 85 1a                        sta     vZpXOff
b06c: 85 1c                        sta     vZpYOff
b06e: c8           @setOneTile     iny
b06f: c8                           iny
b070: c8                           iny
b071: c8                           iny
b072: a5 19                        lda     vZpXPos
b074: 18                           clc
b075: 65 1a                        adc     vZpXOff
b077: 99 03 02                     sta     oamSprXPos,y
b07a: a5 1b                        lda     vZpYPos
b07c: 18                           clc
b07d: 65 1c                        adc     vZpYOff
b07f: 99 00 02                     sta     oamSprYPos,y
b082: 60           rts_B082        rts                             ;done!

                   • Clear variables

b083: c9 a1        DEF_maybeMove   cmp     #tok_MOVE
b085: d0 03                        bne     errSyn_B08A
b087: 4c 5b ca                     jmp     CmdFn_DEFMOVE

b08a: 4c 91 84     errSyn_B08A     jmp     ErrorSyntax

                   ErrUnlessZeroOrOne
b08d: c9 01                        cmp     #$01
b08f: f0 f1                        beq     rts_B082
b091: 29 ff                        and     #$ff
b093: f0 ed                        beq     rts_B082
b095: 4c 94 84     errIllVal_B095  jmp     ErrorIllegalValue

                   • Clear variables
                   vZpSprAttr      .var    $19    {addr/1}         ;Value to use for OAM sprite attributes bitflags
                   vZpTilesPtr     .var    $1b    {addr/2}         ;Pointer to user-specified tiles (the assigned string from user)
                   vZpArgCntr      .var    $6d    {addr/1}         ;counter for loading DEF SPRITE args
                   vZpTwoByTwo     .var    $ad    {addr/1}         ;Holds the value of the 2nd parenthesized arg: character construction

b098: c9 8a        CmdFn_DEF       cmp     #tok_SPRITE             ;next token SPRITE?
b09a: d0 e7                        bne     DEF_maybeMove           ;no -> check if it's MOVE then (error otherwise)
b09c: 20 65 a2                     jsr     AdvThenEvalByteInt      ;get the sprite number
b09f: 20 44 af                     jsr     ErrUnlessZeroThruSeven  ;ensure it's 0-7
b0a2: 48                           pha                             ;push it
b0a3: 20 14 ac                     jsr     SkipCommaOrSynErr       ;skip a comma
b0a6: 20 7a a8                     jsr     SkipLParenOrSynErr      ;and a paren
b0a9: a9 04                        lda     #$04
b0ab: 85 6d                        sta     vZpArgCntr              ;counter to loop around fetching last 4 args
b0ad: 20 6e a2                     jsr     EvalByteInteger         ;eval 1st parenthesized arg: color combo, 0 to 3
b0b0: c9 04                        cmp     #$04
b0b2: b0 e1                        bcs     errIllVal_B095          ; >= 4? -> error
b0b4: 48                           pha
b0b5: 20 6b a2     @argLp          jsr     EvalCommaThenByteInt    ;get another arg
b0b8: 48                           pha                             ;push it
b0b9: c6 6d                        dec     vZpArgCntr
b0bb: d0 f8                        bne     @argLp                  ;do 3 more times. Now have 5 args on stack in reverse order
b0bd: 20 7d a8                     jsr     SkipRParenOrSynErr      ;ensure closing paren
b0c0: 20 d9 86                     jsr     SkipEqualsOrErr         ;ensure equal sign
b0c3: 68                           pla                             ;grab last arg: Y-axis inversion
b0c4: 20 8d b0                     jsr     ErrUnlessZeroOrOne      ;we're expecting zero or one
b0c7: 6a                           ror     A                       ;move 1 to high-bit instead (yflip attr bit)
b0c8: 6a                           ror     A
b0c9: 29 80                        and     #$80                    ;<- this is redundant: we already knew it was 0 or 1
b0cb: 85 19                        sta     vZpSprAttr              ;save
b0cd: 68                           pla                             ;pull 4th paren arg: X-axis inversion
b0ce: 20 8d b0                     jsr     ErrUnlessZeroOrOne      ;ensure zero or one
b0d1: 6a                           ror     A                       ;move 1 to bit 6 instead (#$01 -> #$40)
b0d2: 6a                           ror     A
b0d3: 6a                           ror     A
b0d4: 29 40                        and     #$40                    ;<- redundant
b0d6: 05 19                        ora     vZpSprAttr              ;merge x-flip attr with y-flip attr
b0d8: 85 19                        sta     vZpSprAttr
b0da: 68                           pla                             ;pull 3rd paren arg: display priority
b0db: 20 8d b0                     jsr     ErrUnlessZeroOrOne      ;ensure zero or one
b0de: 6a                           ror     A                       ;move 1 to bit 5 instead (#$01 -> #$20)
b0df: 6a                           ror     A
b0e0: 6a                           ror     A
b0e1: 6a                           ror     A
b0e2: 29 20                        and     #$20                    ;<- redundant
b0e4: 05 19                        ora     vZpSprAttr              ;merge the flag
b0e6: 85 19                        sta     vZpSprAttr
b0e8: 68                           pla                             ;pull 2nd paren arg: construction pattern
b0e9: 20 8d b0                     jsr     ErrUnlessZeroOrOne      ;ensure 0 or 1
b0ec: 85 ad                        sta     vZpTwoByTwo             ;save
b0ee: 68                           pla                             ;pull 1st paren arg: color preset
b0ef: 29 03                        and     #$03                    ;<- redundant; we checked <= 3 aleady
b0f1: 05 19                        ora     vZpSprAttr              ;merge with other sprite attrs
b0f3: 85 19                        sta     vZpSprAttr
b0f5: 68                           pla                             ;pull sprite number
                   vZpSpriteNum    .var    $6d    {addr/1}

b0f6: 85 6d                        sta     vZpSpriteNum            ;get the sprite num
b0f8: 20 53 ad                     jsr     MulABy16                ; *= 16
b0fb: a0 04                        ldy     #$04
b0fd: aa                           tax
b0fe: a9 ef        @oamClrLp       lda     #$ef                    ;for each of the four tiles of this sprite:
b100: 9d 00 02                     sta     oamSprYPos,x            ;move y-pos off-screen
b103: 9d 03 02                     sta     oamSprXPos,x            ;move x-pos off-screen
b106: a5 19                        lda     vZpSprAttr
b108: 9d 02 02                     sta     oamSprAttr,x            ;save sprite attrs from args
b10b: a9 20                        lda     #$20                    ;BUG. SPACE char? Actually Lady face char. This ought to be $EF for empty sprite tile (overwritten later anyway)
b10d: 9d 01 02                     sta     oamSprTileId,x          ;save sprite tile ID
b110: e8                           inx
b111: e8                           inx
b112: e8                           inx
b113: e8                           inx
b114: 88                           dey
b115: d0 e7                        bne     @oamClrLp               ;do the same for the other 3 tiles
b117: 20 7d a2                     jsr     EvalString              ;evaluate the expected string after the "="
b11a: ad 01 04                     lda     exprParseStk+1
b11d: 85 1b                        sta     vZpTilesPtr             ;copy string contents ptr from parse stack to ZP pointer var
b11f: ad 02 04                     lda     exprParseStk+2
b122: 85 1c                        sta     vZpTilesPtr+1
b124: a0 00                        ldy     #$00
b126: a5 6d                        lda     vZpSpriteNum            ;get sprite num
b128: 0a                           asl     A
b129: 0a                           asl     A                       ; *= 4
b12a: aa                           tax
b12b: ad 00 04                     lda     exprParseStk
b12e: f0 10                        beq     @emptyString
b130: b1 1b        @tileCpy        lda     (vZpTilesPtr),y         ;copy tile ID
b132: 9d 80 06                     sta     sprTileMaps,x           ;to data for this sprite
b135: e8                           inx
b136: c8                           iny
b137: c0 04                        cpy     #$04                    ;copied 4 tiles?
b139: f0 10                        beq     @tileCpyDone            ;-> done copying
b13b: ce 00 04                     dec     exprParseStk            ;decrement string length. Ran out of string chars?
b13e: d0 f0                        bne     @tileCpy                ;no -> loop back around for another copy from string
b140: a9 20        @emptyString    lda     #$20                    ;yes. Copy Lady face tile (s/b $EF instead)
b142: 9d 80 06     @cpyLadyTiles   sta     sprTileMaps,x           ; over the remaining tiles.
b145: e8                           inx
b146: c8                           iny
b147: c0 04                        cpy     #$04
b149: d0 f7                        bne     @cpyLadyTiles
b14b: a6 6d        @tileCpyDone    ldx     vZpSpriteNum            ;Finally, save whether this is a 8x8 or 16x16 actor
b14d: a5 ad                        lda     vZpTwoByTwo
b14f: 9d 3d 06                     sta     sprTwoByTwoFlags,x
b152: 60           rts_B152        rts

                   ; Copy contents of screen 1 to screen 0.
                   ; If screen 1 is the active screen, do nothing.
                   • Clear variables
                   vZpScr0Page     .var    $74    {addr/1}

b153: a5 f0        CmdFn_VIEW      lda     zpCurScreenNum
b155: d0 fb                        bne     rts_B152                ;not SCREEN 0? -> nothing to do.
b157: a9 20                        lda     #$20
b159: 85 74                        sta     vZpScr0Page             ;set VRAM copy dest page to start of screen 0
b15b: a9 0c                        lda     #NC_PpuMaskClear
b15d: 20 6a 89                     jsr     WaitForNmiSubcommand    ;clear PPUMASK
b160: a2 00                        ldx     #$00
b162: a5 74        @cpyLp          lda     vZpScr0Page
b164: 18                           clc
b165: 69 04                        adc     #$04
b167: 20 ec 81                     jsr     PpuGoToAX               ;set PPUADDR to screen 1 (copy source) page
b16a: ad 07 20                     lda     PPUDATA                 ;first faked read
b16d: ad 07 20                     lda     PPUDATA                 ;read the character/tile ID
b170: 48                           pha                             ;push
b171: a5 74                        lda     vZpScr0Page
b173: 20 ec 81                     jsr     PpuGoToAX               ;set PPUADDR to same page in screen 0 (dest)
b176: 68                           pla                             ;pull tile ID
b177: 8d 07 20                     sta     PPUDATA                 ;and write to screen 0
b17a: e8                           inx                             ;next byte of vRAM page
b17b: d0 e5                        bne     @cpyLp                  ;page not done? -> loop
b17d: e6 74                        inc     vZpScr0Page             ;page done; increment page number
b17f: a5 74                        lda     vZpScr0Page
b181: c9 24                        cmp     #$24                    ;did we finish copying to VRAM page #$23?
b183: d0 dd                        bne     @cpyLp                  ;no -> copy next page
b185: a5 e5                        lda     zpVScroll1              ;yes: copy scr 1 vert scroll to scr 0
b187: 85 e4                        sta     zpVScroll0
b189: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi ;and update
b18c: 20 14 be                     jsr     SetPpuCtrlAndEnableNmi
b18f: a9 0b                        lda     #NC_PpuMaskUpdate
b191: 4c 6a 89                     jmp     WaitForNmiSubcommand    ;restore PPUMASK, and return

                   • Clear variables

b194: a2 3d        ClearSpriteData ldx     #$3d
b196: a9 00                        lda     #$00
b198: 9d 00 06     @sprClrLp       sta     $0600,x                 ;clear sprites' 2x2 vs 1x1 data
b19b: e8                           inx
b19c: d0 fa                        bne     @sprClrLp
b19e: a2 00                        ldx     #$00
b1a0: a9 ef                        lda     #SPR_NullTile
b1a2: 9d 80 06     @tileClrLp      sta     sprTileMaps,x           ;set all actor tilemaps to empty tile
b1a5: e8                           inx
b1a6: e0 20                        cpx     #32                     ;8 actors * 4 tiles
b1a8: d0 f8                        bne     @tileClrLp
b1aa: a2 c0                        ldx     #192
b1ac: a9 00                        lda     #$00
b1ae: 9d 00 06     @clrSprFacing   sta     defMovSFacing-192,x
b1b1: e8                           inx
b1b2: d0 fa                        bne     @clrSprFacing
b1b4: 60                           rts

b1b5: 4c c8 8b     errOOM_B1B5     jmp     ErrorOutOfMemory

b1b8: 20 a6 97     CmdFn_BGGET     jsr     Clear                   ;clear vars
b1bb: a5 20                        lda     zpVAREND+1
b1bd: c9 6c                        cmp     #>bgGetRam              ;is there room after program for bg data?
b1bf: b0 f4                        bcs     errOOM_B1B5             ;no -> OOM error
b1c1: a9 0c                        lda     #NC_PpuMaskClear
b1c3: 20 6a 89                     jsr     WaitForNmiSubcommand    ;turn off rendering
b1c6: a9 00                        lda     #<bgGetRam
b1c8: 85 19                        sta     zpMisc                  ;point zpMisc for storing the background data
b1ca: a9 6c                        lda     #>bgGetRam
b1cc: 85 1a                        sta     zpMisc+1
b1ce: a9 24                        lda     #>VRAM_screen1          ;set PPUADDR to SCREEN 1 in RAM
b1d0: 8d 06 20                     sta     PPUADDR
b1d3: a9 00                        lda     #<VRAM_screen1
b1d5: 8d 06 20                     sta     PPUADDR
b1d8: ad 07 20                     lda     PPUDATA                 ;faked first read
b1db: a0 00                        ldy     #$00
b1dd: a2 04                        ldx     #$04                    ; # pages to copy
b1df: ad 07 20     @cpyLp          lda     PPUDATA                 ;read byte from background data
b1e2: 91 19                        sta     (zpMisc),y              ;write it to (battery-backed) RAM
b1e4: c8                           iny                             ;did we read a full page?
b1e5: d0 f8                        bne     @cpyLp                  ;no -> keep copying
b1e7: e6 1a                        inc     zpMisc+1                ;copied page, incr write page
b1e9: ca                           dex                             ;copied four pages?
b1ea: d0 f3                        bne     @cpyLp                  ;no -> copy next page
b1ec: a5 e5                        lda     zpVScroll1              ;yes: done copying
b1ee: 8d 05 60                     sta     bgGetVScroll            ;save vscroll for saved screen
b1f1: a9 ff                        lda     #$ff                    ;indicate screen 1 is saved in RAM
b1f3: 8d 04 60     UpdateBgGotFlag sta     bgGotFlag
b1f6: a9 0b                        lda     #NC_PpuMaskUpdate
b1f8: 20 6a 89                     jsr     WaitForNmiSubcommand    ;restore rendering
b1fb: 60                           rts

b1fc: a9 14        ErrorNoBgData   lda     #err_NB
b1fe: 4c 37 b2                     jmp     PrintErrorAndStop

b201: ad 04 60     CmdFn_BGPUT     lda     bgGotFlag               ;flag indicate we have saved bg data?
b204: f0 f6                        beq     ErrorNoBgData           ;no? nothing to put then, is there? -> error
                   RestoreBackupBgData
b206: a9 0c                        lda     #NC_PpuMaskClear
b208: 20 6a 89                     jsr     WaitForNmiSubcommand    ;disable rendering
b20b: a9 6c                        lda     #>bgGetRam
b20d: 85 1a                        sta     zpMisc+1                ;point zpMisc at the saved bg data
b20f: a9 00                        lda     #<bgGetRam
b211: 85 19                        sta     zpMisc
b213: a0 00                        ldy     #$00
b215: a2 04                        ldx     #$04                    ; # pages to copy
b217: a9 24                        lda     #>VRAM_screen1
b219: 8d 06 20                     sta     PPUADDR
b21c: a9 00                        lda     #<VRAM_screen1
b21e: 8d 06 20                     sta     PPUADDR                 ;set PPUADDR to addr of SCREEN 1 data
b221: b1 19        @cpyLp          lda     (zpMisc),y              ;read a byte from RAM-saved BG data
b223: 8d 07 20                     sta     PPUDATA                 ;copy to VRAM
b226: c8                           iny                             ;have we copied a full page?
b227: d0 f8                        bne     @cpyLp                  ;no -> copy next byte
b229: e6 1a                        inc     zpMisc+1                ;increment RAM page to read from
b22b: ca                           dex                             ;did we copy 4 pages?
b22c: d0 f3                        bne     @cpyLp                  ;no -> copy next page
b22e: ad 05 60                     lda     bgGetVScroll            ;yes, done copying
b231: 85 e5                        sta     zpVScroll1              ;restore saved vscroll
b233: a9 00                        lda     #$00
b235: f0 bc                        beq     UpdateBgGotFlag         ;(ALWAYS) -> clear bgGotFlag and restore PPU rendering

                   ; Prints the two-character error code, saves running context, and returns to
                   ; direct mode.
                   PrintErrorAndStop
b237: c9 16                        cmp     #22                     ;if the error code >= 
b239: 90 02                        bcc     @errCodeGood
b23b: a9 15                        lda     #err_UP
b23d: 85 b5        @errCodeGood    sta     zpErrCode               ;save error code for BASIC
b23f: a6 0b                        ldx     zpRunLineNum
b241: 86 b7                        stx     zpErrLineNum            ;save cur line # as error line
b243: 86 fa                        stx     zpRsmRunLineNum         ;and also resume line
b245: a6 0c                        ldx     zpRunLineNum+1
b247: 86 b8                        stx     zpErrLineNum+1
b249: 86 fb                        stx     zpRsmRunLineNum+1
b24b: a6 e7                        ldx     zpErrHandStat           ;is an error handler installed?
b24d: f0 2a                        beq     PrintError_print        ;no -> print error and exit
b24f: e0 02                        cpx     #EH_IN_HANDLER          ;are we currently in error handler?
b251: f0 26                        beq     PrintError_print        ;yes -> print new error and exit
b253: a5 58                        lda     zpTxtThisCmd            ;else: we should call the error handler instead of erroring
b255: 85 f6                        sta     zpRsmTxtThisCmd         ;save away all the resumable context
b257: a5 59                        lda     zpTxtThisCmd+1
b259: 85 f7                        sta     zpRsmTxtThisCmd+1
b25b: a5 0d                        lda     zpRunTxtNxtLnk
b25d: 85 f8                        sta     zpRsmRunTxtNxtL
b25f: a5 0e                        lda     zpRunTxtNxtLnk+1
b261: 85 f9                        sta     zpRsmRunTxtNxtL+1
b263: a5 e8                        lda     zpErrHandLNum
b265: 85 44                        sta     zpFindLNum
b267: a5 e9                        lda     zpErrHandLNum+1
b269: 85 45                        sta     zpFindLNum+1
b26b: 20 40 85                     jsr     FindLineNum             ;find error-handler line
b26e: e6 e7                        inc     zpErrHandStat           ;from 1 (EH_NO_HANDLER) to 2 (EH_IN_HANDLER)
b270: a6 17                        ldx     zpSavedStack            ;restore the saved stack
b272: 9a                           txs
b273: 20 c9 96                     jsr     GoTo                    ;-> go to the erorr handler and execute
b276: 4c 4a 83                     jmp     ExecuteCommand

                   PrintError_print
b279: 48                           pha
b27a: 20 6b b5                     jsr     EnsureSOL               ;move cursor to start of new line (if it isn't already)
b27d: 68                           pla
b27e: a2 00                        ldx     #$00                    ;clear the stack
b280: 9a                           txs
b281: 0a                           asl     A                       ;multiply error code by 2 for indexing
b282: a8                           tay
b283: a9 3f                        lda     #‘?’
b285: 20 4d 89                     jsr     QueueCharForOutput      ;queue a '?'
b288: b9 7f b3                     lda     tbl_TwoCharErrCodes,y
b28b: 20 4d 89                     jsr     QueueCharForOutput      ;get the two-char code
b28e: b9 80 b3                     lda     tbl_TwoCharErrCodes+1,y
b291: 20 4d 89                     jsr     QueueCharForOutput      ;and queue that
b294: a9 60                        lda     #<strERROR
b296: 85 52                        sta     zpOutputStr
b298: a9 b3                        lda     #>strERROR
b29a: 85 53                        sta     zpOutputStr+1
b29c: 20 3c 89                     jsr     QueueOutputStr          ;queue " ERROR"
b29f: 20 c0 88                     jsr     ClearStoppedFlag        ;clear stopped flag
                   ; Code below this line is also used by STOP
                   StopOrErr_PrintBeepExit
b2a2: a9 ff                        lda     #$ff                    ;are we in direct mode?
b2a4: c5 0c                        cmp     zpRunLineNum+1
b2a6: d0 04                        bne     @INmsg                  ;low byte doesn't match -> not direct
b2a8: c5 0b                        cmp     zpRunLineNum
b2aa: f0 0e                        beq     @skipIN                 ;yes -> skip "IN [line #]" message
b2ac: a9 55        @INmsg          lda     #<strIN
b2ae: 85 52                        sta     zpOutputStr
b2b0: a9 b3                        lda     #>strIN
b2b2: 85 53                        sta     zpOutputStr+1
b2b4: 20 3c 89                     jsr     QueueOutputStr          ;queue for output: " IN "
b2b7: 20 c3 b2                     jsr     @prLineNum              ;queue the line number
b2ba: 20 5d 89     @skipIN         jsr     PrintOutBuf             ;print queued STOP/ERROR message
b2bd: 20 d0 bc                     jsr     CmdFn_BEEP              ;beep
b2c0: 4c 84 82                     jmp     DirectModeLoop          ;go to command prompt

b2c3: a5 0b        @prLineNum      lda     zpRunLineNum            ;get line number low byte
b2c5: 85 11                        sta     zpContRunLineNum        ;save it away
b2c7: 85 28                        sta     zpWAccum                ;and store in WAccum
b2c9: a5 0c                        lda     zpRunLineNum+1          ;ditto for high byte
b2cb: 85 12                        sta     zpContRunLineNum+1
b2cd: 85 29                        sta     zpWAccum+1
                   QueueWordAsDecimalOutput
b2cf: 20 3e 91                     jsr     PosDecStrFromWord       ;translate to decimal string
b2d2: 20 d6 90                     jsr     FindFirstNonzeroDigit   ;skip leading zeroes
b2d5: 4c e0 87                     jmp     QueueOutpNumStrTmp      ;and queue for output

                   Stop_PrintBreakBeepExit
b2d8: 20 6b b5                     jsr     EnsureSOL               ;move cursor to start of a line (unless it is already)
b2db: a9 00                        lda     #$00
b2dd: 85 62                        sta     zpOutTxtCurPos
b2df: a9 5a                        lda     #<strBREAK
b2e1: 85 52                        sta     zpOutputStr
b2e3: a9 b3                        lda     #>strBREAK
b2e5: 85 53                        sta     zpOutputStr+1
b2e7: 20 3c 89                     jsr     QueueOutputStr          ;queue for output: "BREAK"
b2ea: 4c a2 b2                     jmp     StopOrErr_PrintBeepExit

b2ed: a9 4f        PrintOK         lda     #‘O’
b2ef: 20 75 b5                     jsr     PrintOneChar
b2f2: a9 4b                        lda     #‘K’
b2f4: 4c 75 b5                     jmp     PrintOneChar

                   PrintStartupText
b2f7: a9 7b                        lda     #<strNS_HUDSON
b2f9: 85 52                        sta     zpOutputStr
b2fb: a9 cf                        lda     #>strNS_HUDSON
b2fd: 85 53                        sta     zpOutputStr+1
b2ff: 20 5a 89                     jsr     PrintString             ;print "NS-HUBASIC V3.0 "
b302: 20 6b b5                     jsr     EnsureSOL               ;next line
b305: a9 8c                        lda     #<strCOPYRIGHT_MSG
b307: 85 52                        sta     zpOutputStr
b309: a9 cf                        lda     #>strCOPYRIGHT_MSG
b30b: 85 53                        sta     zpOutputStr+1
b30d: 20 5a 89                     jsr     PrintString             ;print "(C) NINTENDO/SHARP/HUDSON"
b310: 20 6b b5                     jsr     EnsureSOL               ;next line
b313: 20 25 a9     @bytesFree      jsr     AmtFreeMemToWAccum
b316: 20 cf b2                     jsr     QueueWordAsDecimalOutput
b319: 20 5d 89                     jsr     PrintOutBuf
b31c: a9 49                        lda     #<strBYTES_FREE
b31e: 85 52                        sta     zpOutputStr
b320: a9 b3                        lda     #>strBYTES_FREE
b322: 85 53                        sta     zpOutputStr+1
b324: 4c 5a 89                     jmp     PrintString             ;-> print "BYTES FREE", then return

b327: a9 38        PrintHotStart   lda     #<strBASIC_HOT_START
b329: 85 52                        sta     zpOutputStr
b32b: a9 b3                        lda     #>strBASIC_HOT_START
b32d: 85 53                        sta     zpOutputStr+1
b32f: 20 5a 89                     jsr     PrintString             ;print "BASIC HOT START "
b332: 20 6b b5                     jsr     EnsureSOL               ;next line
b335: 4c 13 b3                     jmp     @bytesFree              ;-> print how many bytes free, then return

                   strBASIC_HOT_START
b338: 42 41 53 49+                 .zstr   “BASIC HOT START ”
b349: 20 42 59 54+ strBYTES_FREE   .zstr   “ BYTES FREE”
b355: 20 49 4e 20+ strIN           .zstr   “ IN ”
b35a: 42 52 45 41+ strBREAK        .zstr   “BREAK”
b360: 20 45 52 52+ strERROR        .zstr   “ ERROR”
b367: 57 52 49 54+ strWRITING      .zstr   “WRITING ”
b370: 4c 4f 41 44+ strLOADING      .zstr   “LOADING ”
b379: 53 4b 49 50+ strSKIP         .zstr   “SKIP ”
                   tbl_TwoCharErrCodes
b37f: 4e 46                        .str    “NF”                    ;NEXT without FOR
b381: 53 4e                        .str    “SN”                    ;Syntax Error
b383: 52 47                        .str    “RG”                    ;RETURN without GOSUB
b385: 4f 44                        .str    “OD”                    ;Out of Data
b387: 49 4c                        .str    “IL”                    ;Illegal value
b389: 4f 56                        .str    “OV”                    ;Overflow
b38b: 4f 4d                        .str    “OM”                    ;Out of Memory
b38d: 55 4c                        .str    “UL”                    ;Undefined Line number
b38f: 53 4f                        .str    “SO”                    ;Subscript Out of range
b391: 44 44                        .str    “DD”                    ;Duplicate Definition
b393: 44 5a                        .str    “DZ”                    ;Division by Zero
b395: 54 4d                        .str    “TM”                    ;Type Mismatch
b397: 53 54                        .str    “ST”                    ;String Too long
b399: 46 54                        .str    “FT”                    ;Formula Too complex
b39b: 43 43                        .str    “CC”                    ;Cannot Continue
b39d: 55 46                        .str    “UF”                    ;Undefined Function
b39f: 4d 4f                        .str    “MO”                    ;Missing Operand
b3a1: 54 50                        .str    “TP”                    ;Tape read error
b3a3: 4e 52                        .str    “NR”                    ;No Resume. Given on RESUME without error handler being called, or on program exit when error handled without resume.
b3a5: 52 45                        .str    “RE”                    ;RESUME without ERROR. RESUME called but no ON ERROR installed.
b3a7: 4e 42                        .str    “NB”                    ;No BG Data
b3a9: 55 50                        .str    “UP”                    ;Unprintable Error

b3ab: a9 10        InitPpuApu      lda     #DEFAULT_PPUCTRL
b3ad: 85 32                        sta     zpPpuCtrlVal            ;when used below (SetPpuCtrlAndEnableNmi), sets bg to use separate tiles from sprites; everything else defaults
b3af: a9 0e                        lda     #$0e
b3b1: 85 33                        sta     zpPpuMaskVal            ;if used, will disable sprites, enable background
b3b3: 20 59 b5                     jsr     AwaitVBlankAndClearPpuMask ;disables all rendering
b3b6: a9 00                        lda     #$00
b3b8: 85 f5                        sta     $f5
b3ba: 85 f0                        sta     zpCurScreenNum
b3bc: 85 e4                        sta     zpVScroll0
b3be: 85 e5                        sta     zpVScroll1
b3c0: 85 63                        sta     zpNMICMD
b3c2: 85 ab                        sta     zpNmiAnimNxtFr
b3c4: 85 ac                        sta     zpNmiAnimate
b3c6: 85 62                        sta     zpOutTxtCurPos
b3c8: 85 72                        sta     zpKanaFlag
b3ca: 85 50                        sta     zpKanaHeld
b3cc: 85 18                        sta     zpModifierKeyDn
b3ce: 8d 10 40                     sta     DMC_FREQ
b3d1: 8d 01 40                     sta     SQ1_SWEEP
b3d4: 8d 05 40                     sta     SQ2_SWEEP
b3d7: 8d 15 40                     sta     SND_CHN
b3da: 85 4c                        sta     zpInsertMode
b3dc: 85 66                        sta     zpRkimMidMacro
b3de: 85 67                        sta     zpFnKeyDfnOff
b3e0: a9 4f                        lda     #$4f
b3e2: 8d 17 40                     sta     JOY2                    ;initialize the APU frame counter
b3e5: 20 20 b4                     jsr     InitPromptChars
b3e8: 20 14 be                     jsr     SetPpuCtrlAndEnableNmi
b3eb: a9 20                        lda     #>VRAM_screen0
b3ed: 85 4f                        sta     zpPpuScreenBasePage     ;set SCREEN 0 as the current screen
b3ef: 20 ab b4                     jsr     DoCLS                   ;clear and activate SCREEN 0
b3f2: 20 36 b4                     jsr     ResetPaletteSets        ;reset palette sets to their startup values
b3f5: a2 00                        ldx     #$00
b3f7: 8a                           txa
b3f8: a0 80                        ldy     #$80
b3fa: 9d 80 04     @fkeyClrLp      sta     fnKey1_DfnBuf,x         ;clear the function-key definitions
b3fd: e8                           inx
b3fe: 88                           dey
b3ff: d0 f9                        bne     @fkeyClrLp
b401: a2 00                        ldx     #$00
                   ; initialize the fn key definitions to their boot-up defaults
b403: a5 67        @fkeyDefsLp     lda     zpFnKeyDfnOff           ;<- this is being as a counter of function keys, not offsets
b405: c9 08                        cmp     #$08                    ;did we do 8 fn key definition?
b407: f0 2c                        beq     rts_B435                ;yes -> we're done
b409: 20 53 ad                     jsr     MulABy16                ;convert fnkey # to an index into the defns area
b40c: a8                           tay                             ;and move to the Y reg
b40d: bd 4a b4     @defnCpyLp      lda     FnKeyDefaultDefs,x      ;load a character from the hard-coded default
b410: f0 08                        beq     @nxtFnKey               ;terminating null char? -> finished with this def, on to the next
b412: 99 80 04                     sta     fnKey1_DfnBuf,y         ;store char in assigned definition space
b415: e8                           inx                             ;incr source idx
b416: c8                           iny                             ;incr dest idx
b417: 4c 0d b4                     jmp     @defnCpyLp              ;(always) -> on to copying next character

b41a: e8           @nxtFnKey       inx
b41b: e6 67                        inc     zpFnKeyDfnOff
b41d: 4c 03 b4                     jmp     @fkeyDefsLp

b420: a9 fd        InitPromptChars lda     #TileId_whiteBox        ;"white box" tile character
b422: 85 e0                        sta     zpKanaCursChr           ;(set as cursor when KANA mode active)
b424: a9 5f                        lda     #‘_’                    ;"underscore" tile character
b426: 85 e1                        sta     zpNonKCursChr           ;(set as normal cursor)
b428: 4c 66 b7                     jmp     DeactivateKanaMode      ;initialize to normal (non-KANA) mode

b42b: ad 02 20     WaitForVBlank   lda     PPUSTATUS               ;read from PPUSTATUS until vblank flag is clear, to clear it
b42e: 30 fb                        bmi     WaitForVBlank
b430: ad 02 20     @waitLp         lda     PPUSTATUS               ;now read from it until it's set again.
b433: 10 fb                        bpl     @waitLp                 ;then we know we've hit vblank
b435: 60           rts_B435        rts

                   ResetPaletteSets
b436: a9 be                        lda     #<cgsetBgPalSet1
b438: 85 52                        sta     zpOutputStr
b43a: a9 cf                        lda     #>cgsetBgPalSet1        ;setup bg palette set no. 1
b43c: 85 53                        sta     zpOutputStr+1
b43e: a9 00                        lda     #$00
b440: 85 65                        sta     zpPaletBgOrSpr          ;select writing bg palette set
b442: 20 93 af                     jsr     CGSET_SetPaletteSet     ;set the palette set
b445: e6 65                        inc     zpPaletBgOrSpr          ;select writing spr palette set
b447: 4c 93 af                     jmp     CGSET_SetPaletteSet     ;set the palette set (using the bg set for both bg and spr)

                   ; These strings are used to set the default FN key shorthands.
                   FnKeyDefaultDefs
b44a: 47 41 4d 45+                 .zstr   “GAME 0”,$0d
b452: 47 41 4d 45+                 .zstr   “GAME 1”,$0d
b45a: 47 41 4d 45+                 .zstr   “GAME 2,1”,$0d
b464: 47 41 4d 45+                 .zstr   “GAME 3”,$0d
b46c: 53 50 52 49+                 .zstr   “SPRITE ”
b474: 4c 4f 41 44+                 .zstr   “LOAD”,$0d
b47a: 4c 49 53 54+                 .zstr   “LIST”,$0d
b480: 52 55 4e 0d+                 .zstr   “RUN”,$0d

                   ClearScreenAndAttrs
b485: a0 04                        ldy     #$04
b487: a2 c0                        ldx     #$c0                    ;#$c0 first iteration, #$00 (256) iters 2-4.
b489: a9 20                        lda     #$20                    ;SPC char
b48b: 8d 07 20     @lpSpaces       sta     PPUDATA
b48e: ca                           dex
b48f: d0 fa                        bne     @lpSpaces               ;end of inner loop
b491: 88                           dey
b492: d0 f7                        bne     @lpSpaces               ;end outer loop. 960x (30 rows * 30 cols, w margins)
                   ClearScreenAttrs
b494: a2 c0                        ldx     #$c0
b496: a5 4f                        lda     zpPpuScreenBasePage
b498: 18                           clc
b499: 69 03                        adc     #$03
b49b: 20 ec 81                     jsr     PpuGoToAX               ;go to the update screen's attribute table location
b49e: a9 00                        lda     #$00
b4a0: 8d 07 20     @clrAttr        sta     PPUDATA                 ;and clear it
b4a3: e8                           inx
b4a4: d0 fa                        bne     @clrAttr
b4a6: 60                           rts

b4a7: a9 24        DoCLS_1         lda     #>VRAM_screen1
b4a9: 85 4f                        sta     zpPpuScreenBasePage
b4ab: a9 0c        DoCLS           lda     #NC_PpuMaskClear
b4ad: 20 6a 89                     jsr     WaitForNmiSubcommand    ;Set PPUMASK = 0
b4b0: 20 0e be                     jsr     SetPpuCtrl
b4b3: 20 59 b5                     jsr     AwaitVBlankAndClearPpuMask
b4b6: a5 4f                        lda     zpPpuScreenBasePage
b4b8: a2 00                        ldx     #$00
b4ba: 20 ec 81                     jsr     PpuGoToAX               ;Set PPU cur addr to start of update screen
b4bd: 20 85 b4                     jsr     ClearScreenAndAttrs
b4c0: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi
b4c3: 20 14 be                     jsr     SetPpuCtrlAndEnableNmi
b4c6: a9 0b                        lda     #NC_PpuMaskUpdate
b4c8: 20 6a 89                     jsr     WaitForNmiSubcommand    ;Restore PPUMASK
b4cb: 20 48 b7                     jsr     CursorHome              ;LOCATE 0,0 (also sets A = 0)
b4ce: a4 4f                        ldy     zpPpuScreenBasePage
b4d0: c0 20                        cpy     #>VRAM_screen0          ;upd scr == 0?
b4d2: f0 05                        beq     @scrIs0                 ;yes ->
b4d4: 85 e5                        sta     zpVScroll1              ;upd scr == 1, clear zpVScroll1
b4d6: 4c db b4                     jmp     @skip

b4d9: 85 e4        @scrIs0         sta     zpVScroll0              ;upd scr == -, clear zpVScroll0
b4db: aa           @skip           tax
b4dc: a4 4f                        ldy     zpPpuScreenBasePage
b4de: c0 20                        cpy     #>VRAM_screen0          ;upd scr == 0?
b4e0: f0 09                        beq     @clrScrInf0             ;yes -> clear that screen's info
b4e2: 9d 1a 06     @clrScrInf1     sta     isRowWrappedArray1-1,x  ;no -> clear screen 1's info
b4e5: e8                           inx
b4e6: e0 1a                        cpx     #$1a
b4e8: d0 f8                        bne     @clrScrInf1
b4ea: 60                           rts

b4eb: 9d 00 06     @clrScrInf0     sta     isRowWrappedArray0-1,x
b4ee: e8                           inx
b4ef: e0 1a                        cpx     #$1a
b4f1: d0 f8                        bne     @clrScrInf0
b4f3: 60                           rts

                   CalcCursorTilemapPos
b4f4: a9 20                        lda     #$20
b4f6: 85 28                        sta     zpWAccum
b4f8: a9 00                        lda     #$00
b4fa: 85 29                        sta     zpWAccum+1
b4fc: 85 2d                        sta     zpWParam+1
b4fe: a5 e4                        lda     zpVScroll0
b500: a4 4f                        ldy     zpPpuScreenBasePage
b502: c0 20                        cpy     #>VRAM_screen0
b504: f0 02                        beq     @scr0
b506: a5 e5                        lda     zpVScroll1
b508: 4a           @scr0           lsr     A
b509: 4a                           lsr     A
b50a: 4a                           lsr     A
b50b: 18                           clc
b50c: 65 49                        adc     zpCV
b50e: 69 03                        adc     #VMARGIN
b510: c9 1e                        cmp     #MAP_ROWS
b512: 90 02                        bcc     @notOverfl
b514: e9 1e                        sbc     #MAP_ROWS
b516: 85 2c        @notOverfl      sta     zpWParam
                   ; At this point, zpPARAM16 ($2C.2D) = ((zpVScroll/8) + CV + 3) MOD 30), or the
                   ; current row number relative to the actual start of the tilemap in VRAM (not
                   ; just "top of screen", like zpCV).
                   ; 
                   ; We set the multiplier to 32, or the size of a row on the screen (including
                   ; margins)
b518: 20 47 8f                     jsr     WordMultiply
b51b: 18                           clc
b51c: a5 48                        lda     zpCH
b51e: 69 02                        adc     #$02
b520: 65 28                        adc     zpWAccum
b522: 85 4a                        sta     zpCursTileAddr
b524: a9 00                        lda     #$00
b526: 65 29                        adc     zpWAccum+1
b528: 18                           clc
b529: 65 4f                        adc     zpPpuScreenBasePage
b52b: 85 4b                        sta     zpCursTileAddr+1
b52d: 60                           rts

                   ; Sets the current VRAM address to the cursor's current tile position
b52e: a5 4b        PpuGoToCursor   lda     zpCursTileAddr+1
b530: 8d 06 20                     sta     PPUADDR
b533: a5 4a                        lda     zpCursTileAddr
b535: 8d 06 20                     sta     PPUADDR
b538: 60                           rts

                   NmiWrTileSetVScrollAndEnableNmi
b539: 8d 07 20                     sta     PPUDATA
                   ; Set the vertical scroll position from either zpVScroll0 or zpVScroll1,
                   ; depending on the value of $F0
                   NmiSetVScrollAndEnableNmi
b53c: 20 14 be                     jsr     SetPpuCtrlAndEnableNmi
b53f: a5 f0                        lda     zpCurScreenNum          ;which screen is active?
b541: f0 0b                        beq     @scr0                   ;screen 0? -> update scroll for that
b543: a9 00                        lda     #$00                    ;otherwise, update scroll for scr 1
b545: 8d 05 20                     sta     PPUSCROLL
b548: a5 e5                        lda     zpVScroll1
b54a: 8d 05 20                     sta     PPUSCROLL
b54d: 60                           rts

b54e: a9 00        @scr0           lda     #$00
b550: 8d 05 20                     sta     PPUSCROLL
b553: a5 e4                        lda     zpVScroll0
b555: 8d 05 20                     sta     PPUSCROLL
b558: 60                           rts

                   AwaitVBlankAndClearPpuMask
b559: 20 2b b4                     jsr     WaitForVBlank
b55c: a9 00                        lda     #$00
b55e: 8d 01 20                     sta     PPUMASK
b561: 60                           rts

                   AwaitVBlankAndUpdatePpuMask
b562: 20 2b b4                     jsr     WaitForVBlank
b565: a5 33                        lda     zpPpuMaskVal
b567: 8d 01 20                     sta     PPUMASK
b56a: 60           rts_B56A        rts

                   ; Go to start of next line... unless we're already at the start of a line.
b56b: a5 48        EnsureSOL       lda     zpCH
b56d: f0 fb                        beq     rts_B56A
b56f: a9 00        DoCRLF          lda     #$00                    ;<- here's the CR
b571: 85 48                        sta     zpCH
b573: a9 0a                        lda     #$0a                    ;<- and here's the LF
b575: c9 20        PrintOneChar    cmp     #$20                    ;is it a printable character?
b577: b0 23                        bcs     OutputChar
b579: c9 08                        cmp     #$08                    ;backspace (ctrl-H)
b57b: f0 10                        beq     @leaveInsertAlone
b57d: c9 01                        cmp     #$01                    ;insert space (ctrl-A)
b57f: f0 0c                        beq     @leaveInsertAlone
b581: c9 12                        cmp     #$12                    ;toggle insert/overwrite (ctrl-R)
b583: f0 08                        beq     @leaveInsertAlone
b585: c9 09                        cmp     #$09                    ;tab (ctrl-I)
b587: f0 04                        beq     @leaveInsertAlone
b589: a0 00                        ldy     #$00                    ;all other controls disable insert mode
b58b: 84 4c                        sty     zpInsertMode
                   @leaveInsertAlone
b58d: 0a                           asl     A
b58e: aa                           tax
b58f: bd be b8                     lda     jtbl_CtrlCharHandlers,x
b592: 85 28                        sta     zpWAccum
b594: bd bf b8                     lda     jtbl_CtrlCharHandlers+1,x
b597: 85 29                        sta     zpWAccum+1
b599: 6c 28 00                     jmp     (zpWAccum)

b59c: a4 4c        OutputChar      ldy     zpInsertMode
b59e: f0 07                        beq     @overwr
b5a0: 48                           pha
b5a1: a9 12                        lda     #$12                    ;control-R
b5a3: 20 75 b5                     jsr     PrintOneChar
b5a6: 68                           pla
b5a7: 20 4d 89     @overwr         jsr     QueueCharForOutput
b5aa: 4c 5d 89                     jmp     PrintOutBuf

                   ; Advance the cursor to the start of the next row.
                   ; Does exactly the same thing as CursorDown.
                   CursorToNextRowStart
b5ad: e6 49                        inc     zpCV
b5af: a5 49                        lda     zpCV
b5b1: c9 18                        cmp     #ROWS
b5b3: f0 12                        beq     ScrollScreenUpOneRow
                   ; Handler for control chars that don't do anything when input.
b5b5: a9 00        ControlCharNOOP lda     #$00
b5b7: 60                           rts

                   ; Advance the cursor to the start of the next line, skipping over any wrapped
                   ; rows in the current one.
                   CursorToNextLineStart
b5b8: 20 4c b7                     jsr     CarriageReturn
b5bb: a6 49        @findStart      ldx     zpCV
b5bd: 20 37 b6                     jsr     DoesRowWrap
b5c0: f0 eb                        beq     CursorToNextRowStart
b5c2: e6 49                        inc     zpCV
b5c4: 4c bb b5                     jmp     @findStart

                   ; Scrolls the screen up by one row, leaving the cursor on the first row of the
                   ; (new) last row).
                   ; 
                   ; Erases the top line just before scrolling it up (so it can become a future new
                   ; line when we scroll down to it in future).
                   ScrollScreenUpOneRow
b5c7: c6 7e                        dec     zp7E
b5c9: 20 48 b7                     jsr     CursorHome
b5cc: 20 f4 b4                     jsr     CalcCursorTilemapPos
                   ; Tell NMI handler to erase to end of row (cursor is at home row, so erase
                   ; entire top row)
b5cf: a9 04                        lda     #NC_ClearToEndOfRow
b5d1: 20 6a 89                     jsr     WaitForNmiSubcommand
                   ; Scrolls the screen WITHOUT clearing any lines. Unlike many other systems,
                   ; Family Basic doesn't scroll by copying lines upward: the tilemap is vertically
                   ; 'mirrored' (repeated), so we have only to adjust the PPU vertical scroll
                   ; register. Very fast scrolls!
                   ; 
                   ScrollScreenOneRowNoErase
b5d4: a5 32                        lda     zpPpuCtrlVal
b5d6: 8d 00 20                     sta     PPUCTRL
b5d9: a5 e4                        lda     zpVScroll0
b5db: a4 4f                        ldy     zpPpuScreenBasePage
b5dd: c0 20                        cpy     #>VRAM_screen0
b5df: f0 02                        beq     @scr0
b5e1: a5 e5                        lda     zpVScroll1
b5e3: 18           @scr0           clc
b5e4: 69 08                        adc     #TILE_PIXELS
b5e6: c9 f0                        cmp     #VPIXELS
b5e8: 90 02                        bcc     @notOvfl
b5ea: a9 00                        lda     #$00
b5ec: a4 4f        @notOvfl        ldy     zpPpuScreenBasePage
b5ee: c0 20                        cpy     #<VRAM_screen0+32
b5f0: f0 05                        beq     @scr0
b5f2: 85 e5                        sta     zpVScroll1
b5f4: 4c f9 b5                     jmp     @scr1

b5f7: 85 e4        @scr0           sta     zpVScroll0
b5f9: 20 2b b4     @scr1           jsr     WaitForVBlank
b5fc: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi
b5ff: a0 18                        ldy     #ROWS
b601: a2 00                        ldx     #$00
b603: a5 4f                        lda     zpPpuScreenBasePage
b605: c9 20                        cmp     #>VRAM_screen0
b607: f0 13                        beq     @scrZLp
b609: bd 1c 06     @scrOneLp       lda     isRowWrappedArray1+1,x
b60c: 9d 1b 06                     sta     isRowWrappedArray1,x
b60f: e8                           inx
b610: 88                           dey
b611: d0 f6                        bne     @scrOneLp
b613: 98                           tya
b614: 8c 32 06                     sty     isRowWrappedArray1+23
b617: 8c 33 06                     sty     $0633
b61a: f0 12                        beq     @setLoc                 ;always branches
b61c: bd 02 06     @scrZLp         lda     isRowWrappedArray0+1,x
b61f: 9d 01 06                     sta     isRowWrappedArray0,x
b622: e8                           inx
b623: 88                           dey
b624: d0 f6                        bne     @scrZLp
b626: a9 00                        lda     #$00
b628: 8d 18 06                     sta     isRowWrappedArray0+23
b62b: 8d 19 06                     sta     $0619
b62e: 85 48        @setLoc         sta     zpCH
b630: a9 17                        lda     #ROWS-1
b632: 85 49                        sta     zpCV
b634: 4c 14 be                     jmp     SetPpuCtrlAndEnableNmi

                   ; Returns whether the row identified in register X wraps to the next row
b637: a5 4f        DoesRowWrap     lda     zpPpuScreenBasePage
b639: c9 20                        cmp     #>VRAM_screen0
b63b: f0 04                        beq     @scr0
b63d: bd 1b 06                     lda     isRowWrappedArray1,x
b640: 60                           rts

b641: bd 01 06     @scr0           lda     isRowWrappedArray0,x
b644: 60                           rts

                   ; Record that a given row was wrapped, and does not begin a new line of its own.
b645: 48           MarkWrappedRow  pha
b646: a5 4f                        lda     zpPpuScreenBasePage
b648: c9 20                        cmp     #>VRAM_screen0
b64a: f0 05                        beq     @scr0
b64c: 68                           pla
b64d: 9d 1b 06                     sta     isRowWrappedArray1,x
b650: 60                           rts

b651: 68           @scr0           pla
b652: 9d 01 06                     sta     isRowWrappedArray0,x
b655: 60                           rts

b656: a5 48        SaveCursorPos   lda     zpCH
b658: 85 6b                        sta     zpSavedCH
b65a: a5 49                        lda     zpCV
b65c: 85 6c                        sta     zpSavedCV
b65e: 60                           rts

                   RestoreCursorPos
b65f: a5 6b                        lda     zpSavedCH
b661: 85 48                        sta     zpCH
b663: a5 6c                        lda     zpSavedCV
b665: 85 49                        sta     zpCV
b667: 60                           rts

                   ; Invoked by Control-D
                   ResetGraphicSettings
b668: 20 59 b5                     jsr     AwaitVBlankAndClearPpuMask ;clear PPU mask
b66b: a9 20                        lda     #>VRAM_screen0
b66d: 85 4f                        sta     zpPpuScreenBasePage     ;set SCREEN 0 in zp vars
b66f: a9 00                        lda     #$00
b671: 85 f0                        sta     zpCurScreenNum
b673: a9 10                        lda     #$10
b675: 85 32                        sta     zpPpuCtrlVal
b677: 20 14 be                     jsr     SetPpuCtrlAndEnableNmi  ;and switch to it
b67a: a9 0e                        lda     #$0e
b67c: 8d 01 20                     sta     PPUMASK                 ;reasonable PPUMASK default settings
b67f: 85 33                        sta     zpPpuMaskVal
b681: 20 36 b4                     jsr     ResetPaletteSets        ;set bg and spr palettes from bg palette set 1
b684: 20 94 b4                     jsr     ClearScreenAttrs
b687: 85 4c                        sta     zpInsertMode            ;turn off auto-insert
b689: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi ;make sure vert scroll is up to date
b68c: 4c 62 b5                     jmp     AwaitVBlankAndUpdatePpuMask

                   ; Clears to end of current (possibly multi-row) line
                   ClearToEndOfLine
b68f: 20 56 b6                     jsr     SaveCursorPos
b692: 20 f4 b4                     jsr     CalcCursorTilemapPos
b695: a9 04                        lda     #NC_ClearToEndOfRow
b697: 20 6a 89     @rowLp          jsr     WaitForNmiSubcommand
b69a: a6 49                        ldx     zpCV
b69c: 20 37 b6                     jsr     DoesRowWrap
b69f: f0 0d                        beq     restoreCursPos_B6AE
b6a1: e6 49                        inc     zpCV
b6a3: 20 4c b7                     jsr     CarriageReturn
b6a6: 20 f4 b4                     jsr     CalcCursorTilemapPos
b6a9: a9 04                        lda     #$04
b6ab: 4c 97 b6                     jmp     @rowLp

                   restoreCursPos_B6AE
b6ae: 4c 5f b6                     jmp     RestoreCursorPos

                   ; Control-Z
                   ClearToEndOfScreen
b6b1: 20 56 b6                     jsr     SaveCursorPos           ;save cursor pos
b6b4: 20 f4 b4                     jsr     CalcCursorTilemapPos    ;calc VRAM addr for cursor pos
b6b7: a9 04        @clrLp          lda     #NC_ClearToEndOfRow
b6b9: 20 6a 89                     jsr     WaitForNmiSubcommand    ;clear from cursor to end of row
b6bc: a6 49                        ldx     zpCV
b6be: a9 00                        lda     #$00
b6c0: 20 45 b6                     jsr     MarkWrappedRow          ;mark this row doesn't wrap to next
b6c3: 85 48                        sta     zpCH
b6c5: e6 49                        inc     zpCV                    ;set cursor to start of next row
b6c7: e8                           inx
b6c8: e0 18                        cpx     #ROWS                   ;did we just move past the last row of screen?
b6ca: f0 e2                        beq     restoreCursPos_B6AE     ;yes -> restore cursor pos and return
b6cc: 20 f4 b4                     jsr     CalcCursorTilemapPos    ;no -> calc VRAM addr for new pos
b6cf: 4c b7 b6                     jmp     @clrLp                  ;and loop 'round to clear this row

                   ; dead/unreachable code
b6d2: a6 49                        ldx     zpCV
b6d4: ca                           dex
b6d5: 8a                           txa
b6d6: aa                           tax
b6d7: e8                           inx
b6d8: 20 37 b6                     jsr     DoesRowWrap
b6db: 60                           rts

b6dc: 4c 6f b5                     jmp     DoCRLF

b6df: a5 48        HandleTabKey    lda     zpCH
b6e1: c9 15                        cmp     #21
b6e3: b0 0b                        bcs     @ts3
b6e5: c9 0e                        cmp     #14
b6e7: b0 0a                        bcs     @ts2
b6e9: c9 07                        cmp     #7
b6eb: b0 09                        bcs     @ts1
b6ed: a9 07                        lda     #$07
b6ef: ec                           cpx ▼   $1ca9
b6f0: a9 1c        @ts3            lda     #28
b6f2: ec                           cpx ▼   $15a9
b6f3: a9 15        @ts2            lda     #21
b6f5: ec                           cpx ▼   $0ea9
b6f6: a9 0e        @ts1            lda     #14
b6f8: 38                           sec
b6f9: e5 48                        sbc     zpCH
b6fb: 85 62                        sta     zpOutTxtCurPos
b6fd: aa                           tax
b6fe: a9 20                        lda     #$20
b700: 9d 00 07     @spcLp          sta     genPurposeBuf,x
b703: ca                           dex
b704: 10 fa                        bpl     @spcLp
b706: 4c 5d 89                     jmp     PrintOutBuf

                   ToggleInsertOrOverwriteMode
b709: a9 ff                        lda     #$ff
b70b: 45 4c                        eor     zpInsertMode
b70d: 85 4c                        sta     zpInsertMode
b70f: 60                           rts

b710: a5 49        CursorUp        lda     zpCV
b712: f0 02                        beq     @rts
b714: c6 49                        dec     zpCV
b716: 60           @rts            rts

                   ; Exactly the same as LineFeed, _except_ in the case when the cursor moves past
                   ; the end of the screen. In that case, LineFeed will leave cursor at the start
                   ; of the new line; CursorDown will leave cursor at the original column.
b717: a5 48        CursorDown      lda     zpCH
b719: 48                           pha
b71a: 20 51 b7                     jsr     LineFeed
b71d: 68                           pla
b71e: 85 48                        sta     zpCH
b720: 60                           rts

b721: a5 48        CursorLeft      lda     zpCH                    ;already at leftmost column?
b723: f0 03                        beq     @chkUp                  ;yes -> check if we can go up a row
b725: c6 48                        dec     zpCH                    ;no, so just back up a column.
b727: 60                           rts

b728: a5 49        @chkUp          lda     zpCV                    ;already at topmost row?
b72a: f0 06                        beq     @rts                    ;yes -> nothing to do, exit
b72c: a9 1b                        lda     #$1b                    ;no, so go up a row and to the rightmost column
b72e: 85 48                        sta     zpCH
b730: c6 49                        dec     zpCV
b732: 60           @rts            rts

b733: a5 48        CursorRight     lda     zpCH
b735: c9 1b                        cmp     #$1b                    ;already in rightmost column?
b737: f0 03                        beq     @ckNext                 ;yes -> check if we can go to next row
b739: e6 48                        inc     zpCH                    ;no, just go right a column
b73b: 60                           rts

b73c: 20 4c b7     @ckNext         jsr     CarriageReturn
b73f: a5 49                        lda     zpCV
b741: c9 17                        cmp     #$17                    ;are we on the last row?
b743: f0 02                        beq     @rts                    ;yes -> nothing to do, exit
b745: e6 49                        inc     zpCV                    ;no, just drop down a row
b747: 60           @rts            rts

b748: a9 00        CursorHome      lda     #$00
b74a: 85 49                        sta     zpCV
b74c: a9 00        CarriageReturn  lda     #$00
b74e: 85 48                        sta     zpCH
b750: 60                           rts

b751: a6 49        LineFeed        ldx     zpCV
b753: e8                           inx                             ;increment row
b754: e0 18                        cpx     #ROWS                   ;past last row?
b756: d0 03                        bne     @nextRow                ;no -> store new row and exit
b758: 4c c7 b5                     jmp     ScrollScreenUpOneRow    ;yes

b75b: 86 49        @nextRow        stx     zpCV
b75d: 60                           rts

                   ActivateKanaMode
b75e: a9 ff                        lda     #$ff
b760: 85 72                        sta     zpKanaFlag
b762: a5 e1                        lda     zpNonKCursChr
b764: d0 06                        bne     @setCurs
                   DeactivateKanaMode
b766: a9 00                        lda     #$00
b768: 85 72                        sta     zpKanaFlag
b76a: a5 e0                        lda     zpKanaCursChr
b76c: 85 61        @setCurs        sta     zpCursorChar
b76e: 60                           rts

                   ; Move cursor left one (unless at start of line), and shift all chars forward
                   ; from the cursor, back by one, across all screen rows occupied by the (possibly
                   ; wrapped) line.
                   ; 
                   ; If cursor is at start of the line, still shifts all chars forward, performing
                   ; a forward-delete instead of backspace.
b76f: a6 48        Backspace       ldx     zpCH
b771: f0 05                        beq     @bsFromSol
b773: c6 48                        dec     zpCH
b775: 4c 88 b7                     jmp     @dontGoUpRow

b778: a6 49        @bsFromSol      ldx     zpCV                    ;backspacing from start of line
b77a: f0 0c                        beq     @dontGoUpRow            ;backspacing from home position? -> can't back up higher
b77c: ca                           dex
b77d: 20 37 b6                     jsr     DoesRowWrap             ;check preceding row's wrap status
b780: f0 06                        beq     @dontGoUpRow            ;doesn't wrap -> not same line; not backing up
b782: c6 49                        dec     zpCV                    ;wraps.
b784: a9 1b                        lda     #COLS-1                 ;back up to last column of preceding row.
b786: 85 48                        sta     zpCH
b788: 20 56 b6     @dontGoUpRow    jsr     SaveCursorPos           ;save cursor pos
                   BS_ShiftRowLeftOneCol
b78b: a5 48                        lda     zpCH
b78d: c9 1b                        cmp     #COLS-1                 ;on last char of row?
b78f: f0 1b                        beq     @skip                   ;yes -> skip
b791: 20 f4 b4                     jsr     CalcCursorTilemapPos
b794: a5 4a                        lda     zpCursTileAddr          ;pass backspaced position
b796: 85 19                        sta     zpMisc                  ; as parameter to upcoming NMI subcmd
b798: a5 4b                        lda     zpCursTileAddr+1
b79a: 85 1a                        sta     zpMisc+1
b79c: e6 48                        inc     zpCH                    ;go forward a char
b79e: 20 f4 b4                     jsr     CalcCursorTilemapPos    ;recalc CalcCursorTilemapPos
b7a1: c6 48                        dec     zpCH                    ;go back to our backspaced position
b7a3: a9 03                        lda     #NC_CopyCharsLeft
b7a5: 20 6a 89                     jsr     WaitForNmiSubcommand    ;copy this row back one char
b7a8: a9 1b                        lda     #COLS-1
b7aa: 85 48                        sta     zpCH
b7ac: a6 49        @skip           ldx     zpCV
b7ae: 20 37 b6                     jsr     DoesRowWrap             ;does the present row wrap to the next?
b7b1: d0 11                        bne     @backshiftNextRow       ;yes -> give next row same treatment
b7b3: 20 f4 b4                     jsr     CalcCursorTilemapPos    ;calculate position of last character of last row we moved
b7b6: 20 2b b4                     jsr     WaitForVBlank           ;wait for PPU ops to be safe
b7b9: 20 2e b5                     jsr     PpuGoToCursor           ;go to that location
b7bc: a9 20                        lda     #$20                    ;overwrite character at this position with SPACE
b7be: 20 39 b5                     jsr     NmiWrTileSetVScrollAndEnableNmi
b7c1: 4c 5f b6                     jmp     RestoreCursorPos        ;restore cursor position

                   @backshiftNextRow
b7c4: e6 49                        inc     zpCV                    ;incr row
b7c6: a9 ff                        lda     #$ff                    ;set x pos to last column
b7c8: 85 48                        sta     zpCH
b7ca: 20 d5 b7                     jsr     BS_moveStartCharBack    ;move first char of next row to here (last char of preceding row)
b7cd: e6 49                        inc     zpCV                    ;CV just got dec'd; re-increment
b7cf: 20 4c b7                     jsr     CarriageReturn          ;move cursor to start of the row
b7d2: 4c 8b b7                     jmp     BS_ShiftRowLeftOneCol   ;shift this forward row back one

                   ; This routine has some generalized handling, but is only used in very specific
                   ; conditions (always grabs first char on next line). Special setup before
                   ; calling is also performed to make it do that. It's all pretty silly.
                   BS_moveStartCharBack
b7d5: e6 48                        inc     zpCH                    ;bump cursor x
b7d7: 20 f4 b4                     jsr     CalcCursorTilemapPos    ;calc position
b7da: 20 2b b4                     jsr     WaitForVBlank           ;wait until it's safe for PPU ops
b7dd: 20 2e b5                     jsr     PpuGoToCursor           ;go to bumped cursor position
b7e0: ad 07 20                     lda     PPUDATA                 ;fake first PPU read
b7e3: ad 07 20                     lda     PPUDATA                 ;get the char value at bumped loc
b7e6: 48                           pha                             ;push it
b7e7: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi ;make sure vert scroll is up-to-date
b7ea: c6 48                        dec     zpCH                    ;back up one char (back to original)
b7ec: a5 48                        lda     zpCH
b7ee: c9 ff                        cmp     #$ff                    ;did we wrap to prev line?
b7f0: d0 06                        bne     @notEndOfPrev           ;(NEVER) no -> skip adjust
b7f2: a9 1b                        lda     #COLS-1                 ;yes: fix #$ff to #COLS-1
b7f4: 85 48                        sta     zpCH
b7f6: c6 49                        dec     zpCV                    ;and decrement row
b7f8: 20 f4 b4     @notEndOfPrev   jsr     CalcCursorTilemapPos    ;calc new position
b7fb: 20 2b b4                     jsr     WaitForVBlank           ;wait again for safe PPU ops
b7fe: 20 2e b5                     jsr     PpuGoToCursor           ;and go there
b801: 68                           pla                             ;pull char we got from start
b802: 4c 39 b5                     jmp     NmiWrTileSetVScrollAndEnableNmi ;and put it on last char of prev line

                   vZpCpyLimitRow  .var    $91    {addr/1}
                   vZpInsChar      .var    $b0    {addr/1}

b805: 20 56 b6     InsertSpaceChar jsr     SaveCursorPos           ;save cursor pos
b808: a9 20                        lda     #$20                    ;SPACE char
b80a: 85 b0                        sta     vZpInsChar              ;set as char to insert
b80c: 20 f4 b4     @shiftRowsLp    jsr     CalcCursorTilemapPos    ;update cursor VRAM addr
b80f: a9 0d                        lda     #NC_InsertChar
b811: 20 6a 89                     jsr     WaitForNmiSubcommand    ;insert the char, shifting others right
b814: a6 49                        ldx     zpCV
b816: 20 37 b6                     jsr     DoesRowWrap             ;does the current row wrap to the next?
b819: f0 0b                        beq     InsSpC_checkFinalWrap   ;no -> check if we just wrapped
b81b: e6 49                        inc     zpCV                    ;yes: go to next row
b81d: 20 4c b7                     jsr     CarriageReturn
b820: 4c 0c b8                     jmp     @shiftRowsLp            ;and insert the shifted-off char onto next line

                   restoreCursorPos_B823
b823: 4c 5f b6                     jmp     RestoreCursorPos

                   InsSpC_checkFinalWrap
b826: a5 b0                        lda     vZpInsChar              ;get the shifted-off char from this row
b828: c9 20                        cmp     #$20                    ;was it a SPACE char?
b82a: f0 f7                        beq     restoreCursorPos_B823   ;yes -> don't need to re-insert. Restore curs, return
b82c: a5 49                        lda     zpCV                    ;no; gotta wrap it to next row
b82e: c9 17                        cmp     #ROWS-1                 ;are we on the last row?
b830: f0 61                        beq     InsSpC_scrollAndInsert  ;yes -> got to scroll screen
b832: 20 4c b7                     jsr     CarriageReturn          ;no, don't need to scroll
b835: a4 49                        ldy     zpCV
b837: c8                           iny                             ;<- (could skip that by `cpy #ROWS-2` instead...)
b838: c0 17                        cpy     #ROWS-1                 ;is next row the last one?
b83a: f0 26                        beq     @haveRoom               ;yes -> skip making room for a new row
b83c: a5 49                        lda     zpCV
b83e: 85 91                        sta     vZpCpyLimitRow          ;set current row for limit of down-copies
b840: a9 16                        lda     #ROWS-2                 ;start at penultimate row
b842: 85 49                        sta     zpCV
b844: 20 f4 b4     @rowMoveLp      jsr     CalcCursorTilemapPos    ;calc cursor VRAM addr
b847: a5 49                        lda     zpCV
b849: 48                           pha                             ;push y pos
b84a: a9 0e                        lda     #NC_CopyRowDown
b84c: 20 6a 89                     jsr     WaitForNmiSubcommand    ;copy this row down to next
b84f: 68                           pla                             ;pull y pos
b850: 85 49                        sta     zpCV
b852: aa                           tax
b853: 20 37 b6                     jsr     DoesRowWrap             ;copy "row wrapped" mark down one, too
b856: e8                           inx
b857: 20 45 b6                     jsr     MarkWrappedRow
b85a: c6 49                        dec     zpCV                    ;go up one row
b85c: a5 49                        lda     zpCV
b85e: c5 91                        cmp     vZpCpyLimitRow          ;did we finish making room for new row?
b860: d0 e2                        bne     @rowMoveLp              ;no -> copy this row down, too
b862: a6 49        @haveRoom       ldx     zpCV
b864: a9 ff                        lda     #$ff                    ;mark that we wrapped to new row (that's how we got here!)
b866: 20 45 b6                     jsr     MarkWrappedRow
b869: e6 49                        inc     zpCV                    ;move to new row
b86b: e8                           inx
b86c: a9 00                        lda     #$00
b86e: 20 45 b6                     jsr     MarkWrappedRow          ;mark that new row doesn't wrap (it's new!)
b871: a2 17                        ldx     #ROWS-1
b873: 20 45 b6                     jsr     MarkWrappedRow          ;mark that final row of screen also doesn't wrap (nowhere to go)
b876: a5 6b                        lda     zpSavedCH
b878: 48                           pha                             ;push original cursor pos, and auto-insert flag
b879: a5 6c                        lda     zpSavedCV
b87b: 48                           pha
b87c: a5 4c                        lda     zpInsertMode
b87e: 48                           pha
b87f: a9 05                        lda     #$05                    ;"print" Control-E (clear to end of row)
b881: 20 75 b5                     jsr     PrintOneChar
b884: 68                           pla                             ;pull auto-insert flag
b885: 85 4c                        sta     zpInsertMode            ;restore it
b887: a9 0d                        lda     #NC_InsertChar
b889: 20 6a 89                     jsr     WaitForNmiSubcommand    ;insert the shifted-off char (uh... blank row, we should directly set it instead)
b88c: 68                           pla
b88d: 85 49                        sta     zpCV
b88f: 68                           pla
b890: 85 48                        sta     zpCH                    ;restore cursor pos
b892: 60                           rts                             ;and return

                   InsSpC_scrollAndInsert
b893: a5 6b                        lda     zpSavedCH               ;push saved cursor x pos
b895: 48                           pha
b896: a6 6c                        ldx     zpSavedCV               ;decrement cursor y pos unless at top of screen
b898: f0 01                        beq     @noDec
b89a: ca                           dex
b89b: 8a           @noDec          txa
b89c: 48                           pha                             ;and push
b89d: 20 c7 b5                     jsr     ScrollScreenUpOneRow    ;scroll screen up by one row
b8a0: a9 17                        lda     #ROWS-1                 ;set cursor to start of (new) last row
b8a2: 85 49                        sta     zpCV
b8a4: a9 00                        lda     #$00
b8a6: 85 48                        sta     zpCH
b8a8: 20 f4 b4                     jsr     CalcCursorTilemapPos    ;recalc cursor VRAM addr
b8ab: a9 0d                        lda     #NC_InsertChar          ;insert new char (uh... new row, shouldn't we just
b8ad: 20 6a 89                     jsr     WaitForNmiSubcommand    ; set it directly?)
b8b0: a2 16                        ldx     #$16                    ;set X-reg to preceding row
b8b2: a9 ff                        lda     #$ff
b8b4: 20 45 b6                     jsr     MarkWrappedRow          ;mark that preceding row wrapped to this one
b8b7: 68                           pla
b8b8: 85 49                        sta     zpCV                    ;restore cursor position (but up one row, bc of scroll)
b8ba: 68                           pla
b8bb: 85 48                        sta     zpCH
b8bd: 60                           rts

                   jtbl_CtrlCharHandlers
b8be: b5 b5                        .dd2    ControlCharNOOP
b8c0: 09 b7                        .dd2    ToggleInsertOrOverwriteMode ;Control-A
b8c2: b5 b5                        .dd2    ControlCharNOOP
b8c4: b8 b5                        .dd2    CursorToNextLineStart   ;STOP (Control-C)
b8c6: 68 b6                        .dd2    ResetGraphicSettings    ;Control-D
b8c8: 8f b6                        .dd2    ClearToEndOfLine        ;Control-E
b8ca: b5 b5                        .dd2    ControlCharNOOP
b8cc: d0 bc                        .dd2    CmdFn_BEEP              ;Control-G
b8ce: 6f b7                        .dd2    Backspace               ;BACKSPACE (Control-H)
b8d0: df b6                        .dd2    HandleTabKey            ;TAB (Control-I)
b8d2: 51 b7                        .dd2    LineFeed                ;LF (Control-J)
b8d4: 48 b7                        .dd2    CursorHome              ;HOME (Control-K)
b8d6: ab b4                        .dd2    DoCLS                   ;CLR/Shift-HOME (Control-L)
b8d8: 4c b7                        .dd2    CarriageReturn          ;CR (Control-M)
b8da: b5 b5                        .dd2    ControlCharNOOP
b8dc: b5 b5                        .dd2    ControlCharNOOP
b8de: b5 b5                        .dd2    ControlCharNOOP
b8e0: b5 b5                        .dd2    ControlCharNOOP
b8e2: 05 b8                        .dd2    InsertSpaceChar         ;INSERT (Control-R)
b8e4: b5 b5                        .dd2    ControlCharNOOP
b8e6: b5 b5                        .dd2    ControlCharNOOP
b8e8: b5 b5                        .dd2    ControlCharNOOP
b8ea: 5e b7                        .dd2    ActivateKanaMode        ;Control-V
b8ec: 66 b7                        .dd2    DeactivateKanaMode      ;Control-W
b8ee: b5 b5                        .dd2    ControlCharNOOP
b8f0: b5 b5                        .dd2    ControlCharNOOP
b8f2: b1 b6                        .dd2    ClearToEndOfScreen      ;Control-Z
b8f4: b5 b5                        .dd2    ControlCharNOOP
b8f6: 33 b7                        .dd2    CursorRight             ;Control-\
b8f8: 21 b7                        .dd2    CursorLeft              ;Control-]
b8fa: 10 b7                        .dd2    CursorUp                ;Control-^
b8fc: 17 b7                        .dd2    CursorDown              ;Control-_

                   • Clear variables

b8fe: a9 00        Rkim_returnNull lda     #$00
b900: 60                           rts

b901: 85 6d        Rkim_maybeFkey  sta     zpKeyWhichKey           ;save char code that is > #$F0. Might be fn key, might be graphical tile.
b903: 20 99 ba                     jsr     GetFnKey                ;if it's a function key, find out which.
b906: b0 05                        bcs     Rkim_handleFkey         ;fn key? -> set up reads from macro definition
b908: a5 6d                        lda     zpKeyWhichKey           ;not a fn key, so return the char code as-is
b90a: 29 ff                        and     #$ff
b90c: 60                           rts

b90d: a5 6d        Rkim_handleFkey lda     zpKeyWhichKey
b90f: a6 68                        ldx     zpFnKeyHeld             ;has this fn key already been processed, and just not released yet?
b911: d0 eb                        bne     Rkim_returnNull         ;yes -> return null char
b913: 85 6a                        sta     zpFnKeyCode             ;save the char code as the fn key that was pressed
b915: a9 04                        lda     #$04
b917: 85 74                        sta     zpCursFlashCtr          ;set a counter to 4
b919: 20 58 b9     @lp4            jsr     ReadKeyboard            ;scan keyboard again
b91c: c5 6a                        cmp     zpFnKeyCode             ;is it still the same function key code?
b91e: d0 25                        bne     ReadKeyInsMacros        ;no -> restart our whole key-reading thing
b920: c6 74                        dec     zpCursFlashCtr
b922: d0 f5                        bne     @lp4                    ;loop until the function key has been scanned 4 times since when it was first detected
b924: 38                           sec
b925: e9 f0                        sbc     #$f0                    ;subtract the #$f0, leaving 0-7
b927: 20 53 ad                     jsr     MulABy16                ;multiply by 16 to get offset into fnkey defs
b92a: 85 67                        sta     zpFnKeyDfnOff           ;store that offset
b92c: a9 ff                        lda     #$ff
b92e: 85 66                        sta     zpRkimMidMacro          ;indicate that a not-already-handled fkey was typed
                   Rkim_typeFromMacro
b930: a6 67                        ldx     zpFnKeyDfnOff
b932: bd 80 04                     lda     fnKey1_DfnBuf,x         ;is the fkey definition exhausted?
b935: f0 06                        beq     Rkim_cancelFkeyAndReread ;yes -> cancel fkey and re-scan for keypress
b937: e8                           inx                             ;else, point offset at next char in macro dfn
b938: 86 67                        stx     zpFnKeyDfnOff
b93a: 29 ff                        and     #$ff                    ;and return this current char from it
b93c: 60                           rts

                   Rkim_cancelFkeyAndReread
b93d: a9 00                        lda     #$00
b93f: 85 66                        sta     zpRkimMidMacro
b941: a2 ff                        ldx     #$ff
b943: 86 68                        stx     zpFnKeyHeld
                   ; Read a keyboard character and return it. If a function key is typed, insert
                   ; its macro definition one character at a time on successive calls.
                   ReadKeyInsMacros
b945: a5 66                        lda     zpRkimMidMacro          ;are we typing from an fkey macro dfn?
b947: d0 e7                        bne     Rkim_typeFromMacro      ;yes -> type next char from that
b949: 20 58 b9                     jsr     ReadKeyboard            ;read a char from keyboard
b94c: f0 07                        beq     @nullChar               ;null? -> finish out
b94e: c9 f0                        cmp     #$f0                    ;possible fkey?
b950: b0 af                        bcs     Rkim_maybeFkey          ;yes -> investigate further
b952: 29 ff                        and     #$ff                    ;else return the kbd char
b954: 60                           rts

b955: 85 68        @nullChar       sta     zpFnKeyHeld             ;Mark that no fkey is currently pressed
b957: 60                           rts

                   ; Kbd reading routine.
                   ; See https://www.nesdev.org/wiki/Family_BASIC_Keyboard#Hardware_interface for
                   ; details on how keyboard reading works.
b958: a9 05        ReadKeyboard    lda     #$05
b95a: 8d 16 40                     sta     JOY1                    ;Write $05 to keyboard: reset row/col scanning
b95d: ea                           nop
b95e: ea                           nop
b95f: ea                           nop
b960: ea                           nop
b961: ea                           nop
b962: ea                           nop
b963: a2 00                        ldx     #$00
b965: a9 04        @kbdLp          lda     #$04
b967: 8d 16 40                     sta     JOY1                    ;Write $04: prep read for column 0 of a new row
b96a: a0 0a                        ldy     #$0a
b96c: 88           @dlyLp          dey
b96d: d0 fd                        bne     @dlyLp
b96f: ea                           nop
b970: ea                           nop
                   ;                     bit: 7       0
                   ; Read col 0 scan results: ---x xxx-
                   ; 
                   ; row 0: ]   [   RET F8
                   ;     1: ;   :   @   F7
                   ;     2: K   L   O   F6
                   ;     3: J   U   I   F5
                   ;     4: H   G   Y   F4
                   ;     5: D   R   T   F3
                   ;     6: A   S   W   F2
                   ;     7: CTR Q   ESC F1
                   ;     8: LFT RGT UP  CLR
                   ; 
b971: ad 17 40                     lda     JOY2
b974: 4a                           lsr     A                       ;Move to occupy the low nybble
b975: 29 0f                        and     #$0f
b977: 9d 34 06                     sta     kbdDataAry,x            ;...and store in KbdData + <<ROW>>
b97a: a9 06                        lda     #$06
b97c: 8d 16 40                     sta     JOY1                    ;Write $06: prep read for column 1, same row
b97f: a0 0a                        ldy     #$0a
b981: 88           @dlyLp          dey
b982: d0 fd                        bne     @dlyLp
b984: ea                           nop
b985: ea                           nop
                   ; 
                   ;                     bit: 7       0
                   ; Read col 1 scan results: ---x xxx-
                   ; 
                   ; row 0: STP \   RSH KANA
                   ;     1: ^   -   /   _
                   ;     2: 0   P   ,   .
                   ;     3: 8   9   N   M
                   ;     4: 6   7   V   B
                   ;     5: 4   5   C   F
                   ;     6: 3   E   Z   X
                   ;     7: 2   1   GRP LSH
                   ;     8: INS DEL SPC DWN
                   ; 
b986: ad 17 40                     lda     JOY2
b989: 2a                           rol     A                       ;Move to occupy the high nybble
b98a: 2a                           rol     A
b98b: 2a                           rol     A
b98c: 29 f0                        and     #$f0
b98e: 1d 34 06                     ora     kbdDataAry,x            ;Combine with low nybble (cols 0 and 1 now combined)
b991: 49 ff                        eor     #$ff                    ;Invert the bits, so that 1 = pressed
b993: a0 08                        ldy     #$08                    ;And then store them in reversed order
b995: 0a           @rvsLp          asl     A
b996: 7e 34 06                     ror     kbdDataAry,x
b999: 88                           dey
b99a: d0 f9                        bne     @rvsLp
b99c: e8                           inx
b99d: e0 09                        cpx     #$09
b99f: d0 c4                        bne     @kbdLp                  ;Loop around to handle the next keyboard row
                   ; We now have in kbdDataAry:
                   ; 
                   ; bit    7   6   5   4   3    2   1   0
                   ; 
                   ; kbdDataAry
                   ;   + 0: F8  RET [   ]   KANA RSH \   STP
                   ;   + 1: F7  @   :   ;   _    /   -   ^
                   ;   + 2: F6  O   L   K   .    ,   P   0
                   ;   + 3: F5  I   U   J   M    N   9   8
                   ;   + 4: F4  Y   G   H   B    V   7   6
                   ;   + 5: F3  T   R   D   F    C   5   4
                   ;   + 6: F2  W   S   A   X    Z   E   3
                   ;   + 7: F1  ESC Q   CTR LSH  GRP 1   2
                   ;   + 8: CLR UP  RGT LFT DWN  SPC DEL INS
b9a1: a9 00                        lda     #$00
b9a3: 85 6f                        sta     zpCtrlKeyDn
b9a5: 85 70                        sta     zpShiftKeyDn
b9a7: 85 71                        sta     zpGrphKeyDn
b9a9: ad 3b 06                     lda     kbdDataAry+7
b9ac: 29 10                        and     #$10                    ;is control key down?
b9ae: d0 2b                        bne     RdKey_handleControl
b9b0: ad 3b 06                     lda     kbdDataAry+7
b9b3: 29 08                        and     #$08                    ;is left-shift down?
b9b5: d0 44                        bne     RdKey_handleLShift
b9b7: ad 34 06                     lda     kbdDataAry
b9ba: 29 04                        and     #$04                    ;is right-shift down?
b9bc: d0 45                        bne     RdKey_handleRShift
b9be: ad 3b 06                     lda     kbdDataAry+7
b9c1: 29 04                        and     #$04                    ;is GRPH key down?
b9c3: d0 4f                        bne     RdKey_handleGrph
b9c5: 20 e9 b9                     jsr     RdKey_keyFindRecMap     ;if no modifiers, just map the key to a character
b9c8: d0 03                        bne     @nonNull                ;non-null char? ->
b9ca: 85 18                        sta     zpModifierKeyDn         ;null char. Set "special key pressed" flag to zero
b9cc: 60                           rts

b9cd: a4 18        @nonNull        ldy     zpModifierKeyDn         ;if modifier or kana key is down, return null
b9cf: d0 07                        bne     @retNull
b9d1: a4 50                        ldy     zpKanaHeld
b9d3: d0 03                        bne     @retNull
b9d5: 29 ff                        and     #$ff                    ;else, return mapped character code
b9d7: 60                           rts

b9d8: a9 00        @retNull        lda     #$00
b9da: 60                           rts

                   RdKey_handleControl
b9db: a9 ff                        lda     #$ff                    ;set control-key, modifier-key flags
b9dd: 85 6f                        sta     zpCtrlKeyDn
b9df: 85 18                        sta     zpModifierKeyDn
                   RdKey_maskModsFindRecMap
b9e1: ad 3b 06                     lda     kbdDataAry+7
b9e4: 29 e3                        and     #$e3                    ;mask out left-shift, control, graph keys
b9e6: 8d 3b 06                     sta     kbdDataAry+7
                   ; Find the pressed non-modifier key, record it, and map it, modifying it as
                   ; necessary by any active modifier keys. The mapped character will be returned.
                   ; If multiple non-modifier keys are pressed simultaneously, only the first found
                   ; is reported.
                   RdKey_keyFindRecMap
b9e9: a2 00                        ldx     #$00
b9eb: a0 09                        ldy     #$09
b9ed: bd 34 06     @keyLp          lda     kbdDataAry,x            ;search for other keys down
b9f0: d0 2b                        bne     RdKey_keyRecMap         ;key? -> record and modify
b9f2: e8                           inx
b9f3: 88                           dey
b9f4: d0 f7                        bne     @keyLp                  ;no keys this row? check next
b9f6: 85 6d                        sta     zpKeyWhichKey           ;If we get here, no non-modifier keys were down.
b9f8: 85 50                        sta     zpKanaHeld
b9fa: 60                           rts

                   RdKey_handleLShift
b9fb: a9 ff                        lda     #$ff                    ;set shift-key, modifier-key flags
b9fd: 85 18                        sta     zpModifierKeyDn
b9ff: 85 70                        sta     zpShiftKeyDn
ba01: d0 de                        bne     RdKey_maskModsFindRecMap

                   RdKey_handleRShift
ba03: a9 ff                        lda     #$ff                    ;set shift-key, modifier-key flags
ba05: 85 18                        sta     zpModifierKeyDn
ba07: 85 70                        sta     zpShiftKeyDn
ba09: ad 34 06                     lda     kbdDataAry
ba0c: 29 fb                        and     #$fb                    ;mask out right-shift (why is this only done here, and not for other modifiers?)
ba0e: 8d 34 06                     sta     kbdDataAry
ba11: 4c e1 b9                     jmp     RdKey_maskModsFindRecMap

                   RdKey_handleGrph
ba14: a9 ff                        lda     #$ff                    ;set modifier-key, special-key flags
ba16: 85 71                        sta     zpGrphKeyDn
ba18: 85 18                        sta     zpModifierKeyDn
ba1a: 4c e1 b9                     jmp     RdKey_maskModsFindRecMap

ba1d: 86 6e        RdKey_keyRecMap stx     zpKeyWhichRow           ;save which key matrix row we found a key down
ba1f: a0 00                        ldy     #$00
ba21: 18                           clc
ba22: 4a           @findKey        lsr     A                       ;loop over bits in row until we find which key
ba23: b0 04                        bcs     RdKey_mapKey            ;found -> translate and modify it
ba25: c8                           iny
ba26: 4c 22 ba                     jmp     @findKey

ba29: 8a           RdKey_mapKey    txa
ba2a: 0a                           asl     A                       ;multiply row by 8
ba2b: 0a                           asl     A
ba2c: 0a                           asl     A
ba2d: 84 6d                        sty     zpKeyWhichKey           ;save which bit,
ba2f: 18                           clc
ba30: 65 6d                        adc     zpKeyWhichKey           ; and add that (0-7) to A
ba32: aa                           tax                             ;X now holds a keycode
ba33: a5 6f                        lda     zpCtrlKeyDn             ;is control being pressed?
ba35: d0 22                        bne     RdKey_mapCtrlChar       ;yes -> modify
ba37: a5 70                        lda     zpShiftKeyDn            ;else is shift being pressed?
ba39: d0 34                        bne     RdKey_mapShiftedChar    ;yes -> modify
ba3b: a5 71                        lda     zpGrphKeyDn             ;else is GRPH key down?
ba3d: d0 38                        bne     RdKey_mapGrphModChar    ;yes -> modify
ba3f: a5 72                        lda     zpKanaFlag              ;else is kana mode active?
ba41: d0 3d                        bne     RdKey_mapKanaChar       ;yes -> map
ba43: bd a9 ba                     lda     tbl_KeyMap,x            ;else map normally
ba46: c9 ff                        cmp     #$ff                    ;was KANA key pressed?
ba48: f0 03                        beq     RdKey_maybeActivateKana ;yes -> check if we should handle
ba4a: 29 ff                        and     #$ff                    ;return mapped char
ba4c: 60                           rts

                   RdKey_maybeActivateKana
ba4d: c5 50                        cmp     zpKanaHeld              ;did we already handle kana key being pressed?
ba4f: f0 05                        beq     RdKey_returnNull        ;yes -> return null
ba51: 85 50                        sta     zpKanaHeld              ;no -> indicate we've handled it
ba53: 20 5e b7                     jsr     ActivateKanaMode        ;activate KANA mode
                   RdKey_returnNull
ba56: a9 00                        lda     #$00                    ;then return null
ba58: 60                           rts

                   RdKey_mapCtrlChar
ba59: bd a9 ba                     lda     tbl_KeyMap,x            ;translate key code to char code (~ASCII)
ba5c: c9 20                        cmp     #$20                    ;is the looked-up key already a control?
ba5e: b0 03                        bcs     @notCtrlYet             ;no -> modify
ba60: 29 ff                        and     #$ff                    ;yes -> return it unmodified
ba62: 60                           rts

ba63: 38           @notCtrlYet     sec
ba64: e9 40                        sbc     #$40                    ;subtract 'A'-1
ba66: 90 ee                        bcc     RdKey_returnNull        ;underflow? -> return null
ba68: c9 20                        cmp     #$20                    ;still outside range of control chars?
ba6a: b0 ea                        bcs     RdKey_returnNull        ;yes -> return null
                   RdKey_returnAsIs
ba6c: 29 ff                        and     #$ff                    ;else: return the control char
ba6e: 60                           rts

                   RdKey_mapShiftedChar
ba6f: a5 72                        lda     zpKanaFlag              ;is kana flag on?
ba71: d0 13                        bne     RdKey_mapShiftedKanaChar ;yes -> handle shifted kana
ba73: bd f1 ba                     lda     tbl_ShiftedKeyMap,x     ;else return shifted non-kana key
ba76: 60                           rts

                   RdKey_mapGrphModChar
ba77: a5 72                        lda     zpKanaFlag              ;is kana mode active?
ba79: d0 01                        bne     @grphOk                 ;yes -> map grph char
ba7b: 60                           rts                             ;no -> return null (graph means nothing outside kana mode)

ba7c: bd c9 bb     @grphOk         lda     tbl_GrphKeyMap,x
ba7f: 60                           rts

                   RdKey_mapKanaChar
ba80: bd 39 bb                     lda     tbl_KanaKeyMap,x
ba83: 4c 89 ba                     jmp     @kana

                   RdKey_mapShiftedKanaChar
ba86: bd 81 bb                     lda     tbl_ShiftedKanaKeyMap,x
ba89: c9 ff        @kana           cmp     #$ff                    ;was KANA key pressed while in KANA mode?
ba8b: d0 df                        bne     RdKey_returnAsIs        ;no -> return the mapped char
ba8d: c5 50                        cmp     zpKanaHeld              ;did we already handle this same KANA press?
ba8f: f0 c5                        beq     RdKey_returnNull        ;yes -> return null
ba91: 85 50                        sta     zpKanaHeld              ;no: mark that we've proceesed it
ba93: 20 66 b7                     jsr     DeactivateKanaMode      ;and deactivate kana mode
ba96: a9 00                        lda     #$00                    ;then return null
ba98: 60           rts_BA98        rts

                   ; Scan through the captured keyboard matrix (kbdDataAry) for function
                   ; keypresses.
                   ; If found, carry will be set, and the X register will contain (8 - fnKey). If
                   ; not found, carry will be unset.
                   ; 
                   ; Example: if the pressed function key is F8, X will be 8 - 8 = 0. If it is F1,
                   ; X will be 8 - 1 = 7.
ba99: a0 08        GetFnKey        ldy     #$08
ba9b: a2 00                        ldx     #$00
ba9d: bd 34 06     @fkyLp          lda     kbdDataAry,x
baa0: 0a                           asl     A
baa1: b0 f5                        bcs     rts_BA98
baa3: e8                           inx
baa4: 88                           dey
baa5: d0 f6                        bne     @fkyLp
baa7: 18                           clc
baa8: 60                           rts

                   ; Keymap of non-modifier keys.
                   ; 
                   ; The four keymaps below are used to decode from a key code to a character code.
                   ; tbl_KeyMap is a map of unmodified character codes (note that all the modifier
                   ; keys in it are mapped to #$00); and the keymaps after that are used in various
                   ; situations when the shift key or GRPH key are down, or when kana mode is
                   ; activated.
                   ; 
                   ; The keycode is represented as 8 * BYTE_NUM + BIT_NUM where the key was found,
                   ; in kbdDataAry. BYTE_NUM is stored in zpKeyWhichRow ($6E), and BIT_NUM in
                   ; zpKeyWhichKey ($6D).
                   ; 
                   ; Keycode values:
                   ; 
                   ;           +1  +2  +3    +4  +5  +6  +7
                   ;     0: STP \   RSH KANA  ]   [   RET F8
                   ;     8: ^   -   /   _     ;   :   @   F7
                   ;    16: 0   P   ,   .     K   L   O   F6
                   ;    24: 8   9   N   M     J   U   I   F5
                   ;    32: 6   7   V   B     H   G   Y   F4
                   ;    40: 4   5   C   F     D   R   T   F3
                   ;    48: 3   E   Z   X     A   S   W   F2
                   ;    56: 2   1   GRP LSH   CTR Q   ESC F1.
                   ;    64: INS DEL SPC DWN   LFT RGT UP  CLR
                   ; 
                   ; NOTE: Function keys are translated to $F0-$F7... but! other keys _also_ can
                   ; translate to those values (especially from the GRPH key). Therefore, when the
                   ; character code is at or above $F0, one must examine the keycode or key matrix
                   ; bitmap to distinguish whether it was graphical characters, or a function key.
                   ; (Note: although KANA key maps to $FF, it is never returned from ReadKeyboard;
                   ; it is handled internally, and then the null character is returned.)
baa9: 03 5c 00 ff+ tbl_KeyMap      .bulk   $03,$5c,$00,$ff,$b3,$b2,$0d,$f7
bab1: 5e 2d 2f 00+                 .bulk   $5e,$2d,$2f,$00,$3b,$3a,$40,$f6
bab9: 30 50 2c 2e+                 .bulk   $30,$50,$2c,$2e,$4b,$4c,$4f,$f5
bac1: 38 39 4e 4d+                 .bulk   $38,$39,$4e,$4d,$4a,$55,$49,$f4
bac9: 36 37 56 42+                 .bulk   $36,$37,$56,$42,$48,$47,$59,$f3
bad1: 34 35 43 46+                 .bulk   $34,$35,$43,$46,$44,$52,$54,$f2
bad9: 33 45 5a 58+                 .bulk   $33,$45,$5a,$58,$41,$53,$57,$f1
bae1: 32 31 00 00+                 .bulk   $32,$31,$00,$00,$00,$51,$1b,$f0
bae9: 12 08 20 1f+                 .bulk   $12,$08,$20,$1f,$1d,$1c,$1e,$0b
                   ; Keymap for shift + key.
                   ; Since Family BASIC does not support lowercase, this deos not insert anything
                   ; when a letter key is typed (hence all the zero values).
                   tbl_ShiftedKeyMap
baf1: 03 00 00 00+                 .bulk   $03,$00,$00,$00,$00,$00,$0d,$18
baf9: 00 3d 3f 5f+                 .bulk   $00,$3d,$3f,$5f,$2b,$2a,$00,$17
bb01: 00 00 3c 3e+                 .bulk   $00,$00,$3c,$3e,$00,$00,$00,$11
bb09: 28 29 00 00+                 .bulk   $28,$29,$00,$00,$00,$00,$00,$10
bb11: 26 27 00 00+                 .bulk   $26,$27,$00,$00,$00,$00,$00,$0f
bb19: 24 25 00 00+                 .bulk   $24,$25,$00,$00,$00,$00,$00,$0e
bb21: 23 00 00 00+                 .bulk   $23,$00,$00,$00,$00,$00,$00,$06
bb29: 22 21 00 00+                 .bulk   $22,$21,$00,$00,$00,$00,$1b,$02
bb31: 12 08 20 1f+                 .bulk   $12,$08,$20,$1f,$1d,$1c,$1e,$0c
bb39: 03 88 00 ff+ tbl_KanaKeyMap  .bulk   $03,$88,$00,$ff,$b1,$8a,$0d,$f7
bb41: 87 86 8d 8c+                 .bulk   $87,$86,$8d,$8c,$82,$2d,$89,$f6
bb49: 78 7d 85 8b+                 .bulk   $78,$7d,$85,$8b,$80,$81,$7c,$f5
bb51: 76 77 83 84+                 .bulk   $76,$77,$83,$84,$7f,$7a,$7b,$f4
bb59: 74 75 72 73+                 .bulk   $74,$75,$72,$73,$7e,$6e,$79,$f3
bb61: 63 64 71 6d+                 .bulk   $63,$64,$71,$6d,$6c,$68,$69,$f2
bb69: 62 67 6f 70+                 .bulk   $62,$67,$6f,$70,$6a,$6b,$66,$f1
bb71: 61 60 00 00+                 .bulk   $61,$60,$00,$00,$00,$65,$1b,$f0
bb79: 12 08 20 1f+                 .bulk   $12,$08,$20,$1f,$1d,$1c,$1e,$0b
                   tbl_ShiftedKanaKeyMap
bb81: 03 00 00 ff+                 .bulk   $03,$00,$00,$ff,$5d,$5b,$0d,$f7
bb89: 00 00 00 00+ dmFakeNextLine  .bulk   $00,$00,$00,$00,$00,$00,$00,$f6 ;<- this location used as a fake "next line"
bb91: 00 af 95 00+                 .bulk   $00,$af,$95,$00,$00,$00,$ae,$f5 ; when executing in direct mode
bb99: 00 00 93 94+                 .bulk   $00,$00,$93,$94,$00,$ac,$ad,$f4
bba1: 00 00 00 00+                 .bulk   $00,$00,$00,$00,$00,$00,$ab,$f3
bba9: 91 92 96 00+                 .bulk   $91,$92,$96,$00,$00,$00,$00,$f2
bbb1: 90 00 00 00+                 .bulk   $90,$00,$00,$00,$00,$00,$00,$f1
bbb9: 8f 8e 00 00+                 .bulk   $8f,$8e,$00,$00,$00,$00,$1b,$f0
bbc1: 12 08 20 1f+                 .bulk   $12,$08,$20,$1f,$1d,$1c,$1e,$0c
                   ; Only applies when kana mode is active and GRPH key is pressed
bbc9: 03 fe 00 00+ tbl_GrphKeyMap  .bulk   $03,$fe,$00,$00,$00,$00,$0d,$f7
bbd1: fd f2 00 00+                 .bulk   $fd,$f2,$00,$00,$00,$00,$00,$f6
bbd9: f1 aa 00 00+                 .bulk   $f1,$aa,$00,$00,$00,$00,$a9,$f5
bbe1: ef f0 00 00+                 .bulk   $ef,$f0,$00,$00,$00,$a7,$a8,$f4
bbe9: ed ee a4 a5+                 .bulk   $ed,$ee,$a4,$a5,$00,$a0,$a6,$f3
bbf1: eb ec a3 9f+                 .bulk   $eb,$ec,$a3,$9f,$9e,$9a,$9b,$f2
bbf9: ea 99 a1 a2+                 .bulk   $ea,$99,$a1,$a2,$9c,$9d,$98,$f1
bc01: e9 e8 00 00+                 .bulk   $e9,$e8,$00,$00,$00,$97,$1b,$f0
bc09: 12 08 20 1f+                 .bulk   $12,$08,$20,$1f,$1d,$1c,$1e,$0b

bc11: 20 f4 b4     ShowCursor      jsr     CalcCursorTilemapPos
bc14: a9 08                        lda     #NC_ShowCursor
bc16: d0 05                        bne     @waitNmi

bc18: 20 f4 b4     HideCursor      jsr     CalcCursorTilemapPos
bc1b: a9 09                        lda     #NC_RestoreCharAtCursor
bc1d: 4c 6a 89     @waitNmi        jmp     WaitForNmiSubcommand

                   ; Read a character from the keyboard.
                   ; 
                   ; Flashes the cursor when nothing is being pressed, and handles key-repeat
                   ; timings.
                   ; Since it uses ReadKeyInsMacro, it will also honor F-keys.
bc20: a9 00        ReadChar        lda     #$00
bc22: 85 5e                        sta     zpUnusedStopFlg         ;clear the STOP flag that is never set to anything else
bc24: 20 45 b9                     jsr     ReadKeyInsMacros        ;read keyboard
bc27: f0 46                        beq     RC_awaitChar            ;no key? -> await with cursor flash
bc29: a6 66                        ldx     zpRkimMidMacro          ;else, are we in an fkey macro read?
bc2b: d0 25                        bne     RdChr_RetChar           ;yes -> set and return key from that
bc2d: c5 75                        cmp     zpHeldKeyChar           ;else, is the key we got the same one we got last read?
bc2f: d0 3e                        bne     RC_awaitChar            ;no -> new char, handle via await
bc31: a5 7b                        lda     zpKeyRepeating          ;has this key reached the rapid-repeat phase?
bc33: d0 20                        bne     RC_shortRepeatingKey    ;yes -> skip the longer first wait for repeat
bc35: 20 11 bc                     jsr     ShowCursor              ;show the cursor while we wait for keypress to exceed repeat timer
bc38: 20 45 b9     @frstKeyHoldLp  jsr     ReadKeyInsMacros        ;read key character
bc3b: f0 2f                        beq     RC_hideCursAwaitChar    ;no key? -> restart await, with flashing cursor
bc3d: c5 75                        cmp     zpHeldKeyChar           ;is it the held key?
bc3f: d0 2b                        bne     RC_hideCursAwaitChar    ;no -> new cbar, handle via await
bc41: c6 77                        dec     zpKyRptLongCtr          ;decrement long-wait keypress timer
bc43: d0 f3                        bne     @frstKeyHoldLp          ;loop until timer expires
bc45: c6 78                        dec     zpKyNotRpt              ;<- this is only ever #01
bc47: d0 ef                        bne     @frstKeyHoldLp          ;<- so this never rebranches
bc49: a9 ff                        lda     #$ff
bc4b: 85 7b                        sta     zpKeyRepeating          ;we reached first repeat, so indicate that
bc4d: 4c c3 bc                     jmp     RC_clickAndReturnChar   ;and click, return repeated char

                   RdChr_RetStopChar
bc50: a9 03                        lda     #$03
bc52: 85 75        RdChr_RetChar   sta     zpHeldKeyChar
bc54: 60                           rts

                   RC_shortRepeatingKey
bc55: 20 45 b9                     jsr     ReadKeyInsMacros
bc58: f0 15                        beq     RC_awaitChar
bc5a: c5 75                        cmp     zpHeldKeyChar
bc5c: d0 11                        bne     RC_awaitChar
bc5e: c6 79                        dec     zpKyRptShtCtr
bc60: d0 f3                        bne     RC_shortRepeatingKey
bc62: a9 20                        lda     #$20
bc64: 85 79                        sta     zpKyRptShtCtr
bc66: 20 11 bc                     jsr     ShowCursor              ;show cursor for a frame right before hiding again
bc69: 4c c3 bc                     jmp     RC_clickAndReturnChar

                   RC_hideCursAwaitChar
bc6c: 20 18 bc                     jsr     HideCursor              ;not really sure what one frame of hidden cursor accomplishes
                   ; Await keypress (may have already got one, but point is key-repeating is
                   ; reset).
                   ; Flashes cursor while waiting.
bc6f: a9 00        RC_awaitChar    lda     #$00                    ;reset repeat-key settings
bc71: 85 7b                        sta     zpKeyRepeating
bc73: 85 77                        sta     zpKyRptLongCtr
bc75: a9 20                        lda     #$20
bc77: 85 79                        sta     zpKyRptShtCtr
bc79: a9 01                        lda     #$01
bc7b: 85 78                        sta     zpKyNotRpt
bc7d: 20 11 bc                     jsr     ShowCursor              ;show the cursor at current tile position
bc80: a9 80                        lda     #CURS_FLASH_TM
bc82: 85 74                        sta     zpCursFlashCtr          ;set a count-down for when we should hide the cursor
bc84: 20 45 b9     @showLp         jsr     ReadKeyInsMacros        ;Read a key
bc87: d0 25                        bne     RC_recheckThenClickAndReturnChar ;got one? -> recheck, click, and return
bc89: c6 74                        dec     zpCursFlashCtr
bc8b: d0 f7                        bne     @showLp                 ;loop around Rkim until we get a key, or timer expires
bc8d: 20 18 bc                     jsr     HideCursor              ;timer expired; hide the cursor
bc90: a5 5e                        lda     zpUnusedStopFlg         ;check for STOP flag (no one ever sets)
bc92: d0 bc                        bne     RdChr_RetStopChar       ;and return STOP char if set (never)
bc94: a9 80                        lda     #CURS_FLASH_TM
bc96: 85 74                        sta     zpCursFlashCtr          ;reset count-down for when we should _show_ cursor again
bc98: 20 45 b9     @hideLp         jsr     ReadKeyInsMacros        ;Read a key
bc9b: d0 11                        bne     RC_recheckThenClickAndReturnChar ;got one? -> recheck, click, and return
bc9d: c6 74                        dec     zpCursFlashCtr
bc9f: d0 f7                        bne     @hideLp                 ;loop around Rkim until we get a key, or timer expires
bca1: a5 5e                        lda     zpUnusedStopFlg         ;check for STOP flag (no one ever sets)
bca3: d0 ab                        bne     RdChr_RetStopChar       ;and return STOP char if set (never)
bca5: 4c 6f bc                     jmp     RC_awaitChar            ;timer expired -> loop around to show-cursor side of routine

                   RC_hideCursAwaitChar1
bca8: 20 18 bc                     jsr     HideCursor              ;not really sure what one frame of hidden cursor accomplishes
bcab: 4c 6f bc                     jmp     RC_awaitChar            ;also $BC6C does this same thing already

                   RC_recheckThenClickAndReturnChar
bcae: 85 75                        sta     zpHeldKeyChar           ;set the new char as the "held" char, in case it's held for repeats
bcb0: a5 66                        lda     zpRkimMidMacro          ;was a function-key macro invoked?
bcb2: d0 0f                        bne     RC_clickAndReturnChar   ;yes -> skip re-check, we'd get a different char!
bcb4: a9 01                        lda     #$01
bcb6: 85 74                        sta     zpCursFlashCtr
bcb8: 20 45 b9     @nonLp          jsr     ReadKeyInsMacros        ;check keyboard again
bcbb: c5 75                        cmp     zpHeldKeyChar           ;was same key still held?
bcbd: d0 e9                        bne     RC_hideCursAwaitChar1   ;no-> start keypress check over again
bcbf: c6 74                        dec     zpCursFlashCtr
bcc1: d0 f5                        bne     @nonLp                  ;we set $74 to #$01 before the first dec, so this never loops
                   RC_clickAndReturnChar
bcc3: 20 18 bc                     jsr     HideCursor
bcc6: a5 e3                        lda     zpKeyClickFlg           ;is keyclick on?
bcc8: f0 03                        beq     @skpClick               ;no -> skip click
bcca: 20 d5 bc                     jsr     KeyClick                ;make the key-click sound
bccd: a5 75        @skpClick       lda     zpHeldKeyChar           ;and return the key we got
bccf: 60                           rts

bcd0: a9 f0        CmdFn_BEEP      lda     #$f0
bcd2: 85 19                        sta     zpMisc                  ;set beep length short
bcd4: 2c                           bit ▼   $01a9                   ;(bypass next instr)
bcd5: a9 01        KeyClick        lda     #$01                    ;set beep length itty-bitty, teenie-weenie
bcd7: 85 19                        sta     zpMisc
bcd9: a9 01                        lda     #$01
bcdb: 8d 15 40                     sta     SND_CHN                 ;enable pulse channel 1
bcde: a9 c0                        lda     #$c0
bce0: 8d 17 40                     sta     JOY2                    ;sync APU frame counter
bce3: a9 ef                        lda     #$ef
bce5: 8d 00 40                     sta     SQ1_VOL                 ;set volume fairly high
bce8: a9 00                        lda     #$00
bcea: 8d 01 40                     sta     SQ1_SWEEP               ;no sweep
bced: a9 5f                        lda     #$5f
bcef: 8d 02 40                     sta     SQ1_LO                  ;set pitch (E5?)
bcf2: a0 00                        ldy     #$00
bcf4: 8c 03 40                     sty     SQ1_HI
bcf7: 88           @waitLp         dey
bcf8: d0 fd                        bne     @waitLp                 ;delay loop, 256 iterations
bcfa: c6 19                        dec     zpMisc                  ;beep length: was set at top of fn
bcfc: d0 f9                        bne     @waitLp                 ;not done playing? -> loop another 256
bcfe: a9 00                        lda     #$00
bd00: 8d 15 40                     sta     SND_CHN                 ;done: disable the sound channel
bd03: 60                           rts                             ;and return

                   ; Read a line of user input into lineBuffer
bd04: a9 00        ReadLine        lda     #$00
bd06: 85 76                        sta     zpLineBufIdx            ;set character index to start
bd08: 85 63                        sta     zpNMICMD                ;deactivate any active NMI commands
bd0a: 20 20 bc     @readLineChars  jsr     ReadChar
bd0d: c9 0d                        cmp     #$0d
bd0f: f0 1e                        beq     RL_handleCR
bd11: c9 03                        cmp     #$03
bd13: f0 0a                        beq     RL_handleStopOrCtrlD
bd15: c9 04                        cmp     #$04
bd17: f0 06                        beq     RL_handleStopOrCtrlD
bd19: 20 75 b5                     jsr     PrintOneChar
bd1c: 4c 0a bd                     jmp     @readLineChars          ;loop and read more chars

                   RL_handleStopOrCtrlD
bd1f: 20 75 b5                     jsr     PrintOneChar            ;process the control char
bd22: a6 76                        ldx     zpLineBufIdx
bd24: a9 00                        lda     #$00                    ;abort line-reading
bd26: 9d 01 05                     sta     lineBuffer+1,x
bd29: a9 03                        lda     #$03
bd2b: 9d 00 05                     sta     lineBuffer,x
bd2e: 60                           rts

                   ; Handle CR character from input
                   ; First: back up rows until we're not on a wrapped one (that is, we're at the
                   ; first row of a possible multi-row line)
bd2f: a5 49        RL_handleCR     lda     zpCV                    ;get current row
bd31: f0 0c                        beq     @lineStart              ;Exit loop if we're on the topmost line
bd33: aa                           tax
bd34: ca                           dex                             ;check preceding row
bd35: 20 37 b6                     jsr     DoesRowWrap             ;does that row wrap to this?
bd38: f0 05                        beq     @lineStart              ;no -> we found our line start
bd3a: c6 49                        dec     zpCV                    ;yes; go to that row and re-check
bd3c: 4c 2f bd                     jmp     RL_handleCR             ;Row is wrapped: loop back to preceding row

bd3f: a5 49        @lineStart      lda     zpCV
bd41: 85 47                        sta     zpLineStartRow          ;set the line row start
bd43: a9 02                        lda     #NC_BufferCurLine
bd45: 20 6a 89                     jsr     WaitForNmiSubcommand    ;Fetch the on-screen, possibly-wrapped line from PPU into lineBuffer
bd48: a5 64                        lda     zpOutTxtStartPos        ;<- NMICMD_BufferCurLine always sets before deactivating
bd4a: d0 0a                        bne     @skpTrm                 ;<- so this always branches ->
bd4c: a6 76                        ldx     zpLineBufIdx
bd4e: ca                           dex
bd4f: a9 00                        lda     #$00
bd51: 9d 00 05                     sta     lineBuffer,x
bd54: 86 76                        stx     zpLineBufIdx
bd56: a6 76        @skpTrm         ldx     zpLineBufIdx
bd58: ca           @stripLp        dex                             ;strip trailing spaces
bd59: 8a                           txa
bd5a: f0 07                        beq     @brk
bd5c: bd 00 05                     lda     lineBuffer,x
bd5f: c9 20                        cmp     #$20
bd61: f0 f5                        beq     @stripLp
bd63: e8           @brk            inx
bd64: a9 00                        lda     #$00                    ;set null terminator where we found the first trailing space
bd66: 9d 00 05                     sta     lineBuffer,x
bd69: 4c b8 b5                     jmp     CursorToNextLineStart   ;do the actual CRLF

bd6c: a6 49        INPUT_readLine  ldx     zpCV                    ;get curent row
bd6e: f0 06                        beq     @topRow                 ;top row? -> skip next bit
bd70: ca                           dex
bd71: a9 00                        lda     #$00
bd73: 20 45 b6                     jsr     MarkWrappedRow          ;else, disconnect this row from the preceding one (if it was wrapped, make it... not)
bd76: a9 00        @topRow         lda     #$00
bd78: 85 76                        sta     zpLineBufIdx            ;reset line bufer
bd7a: 85 63                        sta     zpNMICMD                ;deactivate any active NMI command
bd7c: 20 20 bc     @rdLp           jsr     ReadChar                ;read a character
bd7f: c9 0d                        cmp     #$0d                    ;is it carriage-return (Enter)?
bd81: f0 0e                        beq     INPUT_RL_handleCR       ;yes -> finish up
bd83: c9 03                        cmp     #$03
bd85: f0 98                        beq     RL_handleStopOrCtrlD
bd87: c9 04                        cmp     #$04                    ;is it STOP, or Ctrl-D?
bd89: f0 94                        beq     RL_handleStopOrCtrlD    ;yes -> handle it
bd8b: 20 75 b5                     jsr     PrintOneChar            ;otherwise print the char to screen and advance
bd8e: 4c 7c bd                     jmp     @rdLp                   ;-> loop back to read next character

                   ; Does basically same thing as RL_handleCR, not sure why we need another sub
                   ; to do the same thing. Slurps the user-typed input into lineBuffer, and removes
                   ; trailing spaces.
                   INPUT_RL_handleCR
bd91: a5 49                        lda     zpCV                    ;get current row #
bd93: f0 0c                        beq     @recordInpLine          ;first row of screen? doesn't wrap -> proceed
bd95: aa                           tax
bd96: ca                           dex
bd97: 20 37 b6                     jsr     DoesRowWrap             ;else, does the preceding row wrap to this one?
bd9a: f0 05                        beq     @recordInpLine          ;yes -> proceed
bd9c: c6 49                        dec     zpCV                    ;else go up one row
bd9e: 4c 91 bd                     jmp     INPUT_RL_handleCR       ;and repeat the check

bda1: a5 49        @recordInpLine  lda     zpCV                    ;we're at the starting row for the line input
bda3: 85 47                        sta     zpLineStartRow          ;save it as such
bda5: a9 02                        lda     #NC_BufferCurLine
bda7: 20 6a 89                     jsr     WaitForNmiSubcommand    ;copy this line (this row and any it wraps to) into lineBuffer
bdaa: a5 64                        lda     zpOutTxtStartPos        ;did NMICMD_BufferCurLine set $64 to non-zero? (always true)
bdac: d0 0a                        bne     @remTrailSpc            ;(always) -> remove trailing spaces
bdae: a6 76                        ldx     zpLineBufIdx            ;(never): delete final buffered character
bdb0: ca                           dex
bdb1: a9 00                        lda     #$00
bdb3: 9d 00 05                     sta     lineBuffer,x
bdb6: 86 76                        stx     zpLineBufIdx
                   ; remove trailing spaces from lineBuffer
bdb8: a6 76        @remTrailSpc    ldx     zpLineBufIdx            ;work from end of lineBuffer, back toward its start
bdba: ca           @remLp          dex                             ;back up by one character
bdbb: 8a                           txa
bdbc: f0 07                        beq     @lpDone                 ;at start of line? -> done backing up
bdbe: bd 00 05                     lda     lineBuffer,x            ;get the char
bdc1: c9 20                        cmp     #$20                    ;is it a space?
bdc3: f0 f5                        beq     @remLp                  ;yes -> loop back around until we get non-space
bdc5: e8           @lpDone         inx
bdc6: a9 00                        lda     #$00
bdc8: 9d 00 05                     sta     lineBuffer,x            ;null-terminate at this spot (removing the spaces)
bdcb: 4c b8 b5                     jmp     CursorToNextLineStart   ;-> move cursor to following line, and return

bdce: e6 ad        IncrCasBufPtr   inc     zpCasBufPtr
bdd0: d0 02                        bne     rts_BDD4
bdd2: e6 ae                        inc     zpCasBufPtr+1
bdd4: 60           rts_BDD4        rts

bdd5: e6 91        IncrOnesCounter inc     zpCasCksum
bdd7: d0 fb                        bne     rts_BDD4
bdd9: e6 92                        inc     zpCasCksum+1
bddb: 60                           rts

                   ; Z is set iff zpCasBufPtr has reached zpCasEndPtr, and we're done sending the
                   ; buffer.
bddc: a5 ad        IsCasDataDone   lda     zpCasBufPtr
bdde: c5 af                        cmp     zpCasEndPtr
bde0: d0 f2                        bne     rts_BDD4
bde2: a5 ae                        lda     zpCasBufPtr+1
bde4: c5 b0                        cmp     zpCasEndPtr+1
bde6: 60                           rts

bde7: ad 14 05     CasBufSetData   lda     lineBuffer+20
bdea: 85 ad                        sta     zpCasBufPtr
bdec: 18                           clc
bded: 6d 12 05                     adc     lineBuffer+18
bdf0: 85 af                        sta     zpCasEndPtr
bdf2: ad 15 05                     lda     lineBuffer+21
bdf5: 85 ae                        sta     zpCasBufPtr+1
bdf7: 6d 13 05                     adc     lineBuffer+19
bdfa: 85 b0                        sta     zpCasEndPtr+1
bdfc: 60                           rts

bdfd: a9 00        CasBufSetHeader lda     #<lineBuffer
bdff: 85 ad                        sta     zpCasBufPtr
be01: a9 05                        lda     #>lineBuffer
be03: 85 ae                        sta     zpCasBufPtr+1
be05: a9 80                        lda     #<lineBuffer+128
be07: 85 af                        sta     zpCasEndPtr
be09: a9 05                        lda     #>lineBuffer
be0b: 85 b0                        sta     zpCasEndPtr+1
be0d: 60                           rts

                   ; NOTE: Vblank NMI will be DISABLED, unless the high bit is set in zpPpuCtrlVal
be0e: a5 32        SetPpuCtrl      lda     zpPpuCtrlVal
be10: 8d 00 20                     sta     PPUCTRL
be13: 60                           rts

                   SetPpuCtrlAndEnableNmi
be14: a5 32                        lda     zpPpuCtrlVal
be16: 09 80                        ora     #$80
be18: 8d 00 20                     sta     PPUCTRL
be1b: 60                           rts

be1c: 20 0e be     CasVrfyProgram  jsr     SetPpuCtrl              ;disable NMI
be1f: a9 01                        lda     #$01
be21: 4c 29 be                     jmp     CLP_vrfyEntry

                   ; Load a file (whether program, or bg data) from cassette (after having
                   ; processed the header)
                   • Clear variables
                   vZpLoopCtr      .var    $91    {addr/1}

be24: 20 0e be     CasRcvFileData  jsr     SetPpuCtrl              ;disable NMI
be27: a9 00                        lda     #$00
be29: 85 b1        CLP_vrfyEntry   sta     zpVerfFlag
be2b: a9 00                        lda     #$00
be2d: 85 92                        sta     zpCasCksum+1
be2f: 20 e7 bd                     jsr     CasBufSetData           ;set up cassette loading to go where header says
                   ; Read a data announce/tape mark
                   ; (skipping everything that isn't that)
                   ; 
                   ; String of 20ones, first
be32: a9 14        CasRcvDataAnn   lda     #20
be34: 85 91                        sta     vZpLoopCtr
be36: 20 e3 be     @lpTwentyOnes   jsr     CassetteRcvBit          ;read a bit
be39: b0 0d                        bcs     @errIfSTOPped           ;STOP pressed? -> re-enable NMI, then TP error
be3b: d0 f5                        bne     CasRcvDataAnn           ;zero bit? -> Not announce mark yet, check next
be3d: c6 91                        dec     vZpLoopCtr              ;got a "one" bit, see if we get 20 total
be3f: d0 f5                        bne     @lpTwentyOnes           ;not 20 yet? get another "one"
                   ; String of 20 zeroes, next
be41: a9 14                        lda     #20
be43: 85 91                        sta     vZpLoopCtr              ;read a bit
be45: 20 e3 be     @lpTwentyZeroes jsr     CassetteRcvBit          ;STOP pressed? -> re-enable NMI, then TP error
be48: b0 71        @errIfSTOPped   bcs     casLdEnableNmiRetErr    ;one bit? -> Not announce mark yet, check next
be4a: f0 e6                        beq     CasRcvDataAnn           ;got a zero bit, see if we get 20 total
be4c: c6 91                        dec     vZpLoopCtr              ;not 20 yet? get another zero
be4e: d0 f5                        bne     @lpTwentyZeroes
be50: 4c 7b be                     jmp     CasRcvJustBeforeAnnEnd  ;Got the announce. Go get the program data

be53: 20 0e be     CasRcvHeader    jsr     SetPpuCtrl              ;presumably to disable NMI
be56: a9 00                        lda     #$00
be58: 85 b1                        sta     zpVerfFlag
be5a: 20 fd bd                     jsr     CasBufSetHeader         ;set up for loading a header
                   ; Read a header announce/tape mark
                   ; (skipping everything that isn't that)
                   ; 
                   ; String of 40 ones, first
be5d: a9 28        CasRcvHdrAnn    lda     #40
be5f: 85 91                        sta     vZpLoopCtr
be61: 20 e3 be     @lpFortyOnes    jsr     CassetteRcvBit          ;read a bit
be64: b0 55                        bcs     casLdEnableNmiRetErr    ;STOP pressed? -> re-enable NMI, then TP error
be66: d0 f5                        bne     CasRcvHdrAnn            ;zero bit? -> Not announce mark yet, check next
be68: c6 91                        dec     vZpLoopCtr              ;got a "one" bit, see if we get 40 total
be6a: d0 f5                        bne     @lpFortyOnes            ;not 40 yet? get another "one"
                   ; String of 40 zeroes, next
be6c: a9 28                        lda     #40
be6e: 85 91                        sta     vZpLoopCtr
be70: 20 e3 be     @lpFortyZeroes  jsr     CassetteRcvBit          ;read a bit
be73: b0 46                        bcs     casLdEnableNmiRetErr    ;STOP pressed? -> re-enable NMI, then TP error
be75: f0 e6                        beq     CasRcvHdrAnn            ;one bit? -> Not announce mark yet, check next
be77: c6 91                        dec     vZpLoopCtr              ;got a zero bit, see if we get 40 total
be79: d0 f5                        bne     @lpFortyZeroes          ;not 40 yet? get another zero
                   ; Header announce mark successfully read!
                   • Clear variables

                   CasRcvJustBeforeAnnEnd
be7b: a9 00                        lda     #$00
be7d: 85 91                        sta     zpCasCksum              ;receive first (higher-order) byte for checksum
be7f: 85 92                        sta     zpCasCksum+1
be81: 20 c9 be                     jsr     CasSkipNextHigh         ;finish up the zero cycle
be84: 20 c9 be                     jsr     CasSkipNextHigh         ;and skip the following cycle (a final "one")
be87: 20 f7 be     @rdHdrPayload   jsr     CassetteRcvByte         ;we're on low; this will skip a start bit before read
be8a: b0 2f                        bcs     casLdEnableNmiRetErr
be8c: a6 b1                        ldx     zpVerfFlag
be8e: d0 07                        bne     @verify
be90: a0 00                        ldy     #$00
be92: 91 ad                        sta     (zpCasBufPtr),y
be94: 4c 9d be                     jmp     @load

be97: a0 00        @verify         ldy     #$00                    ;verify the byte matches, rather than load
be99: d1 ad                        cmp     (zpCasBufPtr),y
be9b: d0 1e                        bne     casLdEnableNmiRetErr    ;not matched -> TP error
be9d: 20 ce bd     @load           jsr     IncrCasBufPtr           ;prep next byte save loc
bea0: 20 dc bd                     jsr     IsCasDataDone           ;did we reach end?
bea3: d0 e2                        bne     @rdHdrPayload           ;no -> read next byte
bea5: a5 91                        lda     zpCasCksum
bea7: 85 2c                        sta     zpWParam
bea9: a5 92                        lda     zpCasCksum+1
beab: 85 2d                        sta     zpWParam+1
bead: 20 f7 be                     jsr     CassetteRcvByte         ;get first (higher-order) byte of checksum
beb0: c5 2d                        cmp     zpWParam+1              ;does it match our high byte?
beb2: d0 07                        bne     casLdEnableNmiRetErr    ;no -> TP error!
beb4: 20 f7 be                     jsr     CassetteRcvByte         ;get second (lower-order) byte of checksum
beb7: c5 2c                        cmp     zpWParam                ;does it match our low byte?
beb9: f0 07                        beq     @success                ;yes -> re-enable NMI and return success
                   casLdEnableNmiRetErr
bebb: 20 14 be                     jsr     SetPpuCtrlAndEnableNmi
bebe: a9 02                        lda     #$02
bec0: 38           rtnFalse_BEC0   sec
bec1: 60                           rts

bec2: 20 14 be     @success        jsr     SetPpuCtrlAndEnableNmi
bec5: a9 00                        lda     #$00
bec7: 18                           clc
bec8: 60                           rts

                   ; If the incoming cassette signal is low, skip that.
                   ; If the incoming cassete signal is high, skips THAT.
                   ; Was going to call it "CasSkipOneCycle" - which it may do - but the way it's
                   ; typically used, it will more often just skip a high signal, so the name would
                   ; be misleading.
bec9: 20 3a 88     CasSkipNextHigh jsr     CheckForStopKey
becc: f0 f2                        beq     rtnFalse_BEC0
bece: ad 16 40                     lda     JOY1
bed1: 29 02                        and     #$02
bed3: f0 f4                        beq     CasSkipNextHigh         ;loop while cassette signal is low
bed5: 20 3a 88     @lpWhileHi      jsr     CheckForStopKey
bed8: f0 e6                        beq     rtnFalse_BEC0
beda: ad 16 40                     lda     JOY1
bedd: 29 02                        and     #$02
bedf: d0 f4                        bne     @lpWhileHi              ;loop while cassette signal is high
bee1: 18                           clc                             ;return true/success
bee2: 60                           rts

                   ; Rather unintuitively, this routine with Z set if it's a "1" bit, and Z reset
                   ; if it's a "0" bit.
                   ; 
                   ; If called when signal is low, will skip a full cycle, otherwise just skip
                   ; high.
                   ; It then waits 623 cycles (~348 ns) and checks signal state (this number of
                   ; cycles was chosen so that it should fall right where you could determine
                   ; whether this is a long pulse or a short one, distinguishing the bit value.
                   ; 
                   ; Also checks for the STOP key being pressed. If pressed, aborts early with
                   ; carry set; otherwisse carry will be reset.
bee3: 20 c9 be     CassetteRcvBit  jsr     CasSkipNextHigh
bee6: b0 d8                        bcs     rtnFalse_BEC0           ;STOP pressed? -> return false
bee8: a0 4e                        ldy     #$4e                    ;we're def at start of low signal from cassette
beea: 84 75                        sty     zpHeldKeyChar
beec: c6 75        @waitLp         dec     zpHeldKeyChar
beee: d0 fc                        bne     @waitLp                 ;77 * 8 + 7 = 623 cycles (~348 ns)
bef0: ad 16 40                     lda     JOY1
bef3: 29 02                        and     #$02                    ;signal level to accum
bef5: 18                           clc                             ;return true/success
bef6: 60                           rts

                   vZpByteVal      .var    $19    {addr/1}

bef7: a9 08        CassetteRcvByte lda     #$08                    ;prepare to read 8 bits
bef9: 85 74                        sta     zpCursFlashCtr
befb: a9 00                        lda     #$00
befd: 85 19                        sta     vZpByteVal
beff: a5 19        @nextBit        lda     vZpByteVal
bf01: 18                           clc
bf02: 65 19                        adc     vZpByteVal              ;equiv: asl vZpByteVal (so why not do that?)
bf04: 85 19                        sta     vZpByteVal
bf06: 20 e3 be                     jsr     CassetteRcvBit
bf09: b0 b5                        bcs     rtnFalse_BEC0           ;STOPped? -> return early
bf0b: d0 05                        bne     @zero                   ;zero? -> don't modify the byte
bf0d: 20 d5 bd                     jsr     IncrOnesCounter         ;one. Stick it in the low bit.
bf10: e6 19                        inc     vZpByteVal
bf12: c6 74        @zero           dec     zpCursFlashCtr
bf14: d0 e9                        bne     @nextBit
bf16: 20 c9 be                     jsr     CasSkipNextHigh         ;finish cycle (CassetteRcvBit will skip start bit)
bf19: b0 a5                        bcs     rtnFalse_BEC0           ;another opportunutiy to handle STOP
bf1b: a5 19                        lda     vZpByteVal
bf1d: 18                           clc                             ;return true, accum has the byte read
bf1e: 60                           rts

                   • Clear variables

bf1f: 20 0e be     CasSendHeader   jsr     SetPpuCtrl
bf22: 20 fd bd                     jsr     CasBufSetHeader
bf25: a9 28                        lda     #40                     ;Prog header starts with 40 high bits followed by 40 zeroes
bf27: 85 91                        sta     zpCasCksum
bf29: a9 00                        lda     #$00
bf2b: 85 92                        sta     zpCasCksum+1
bf2d: 4c 3e bf                     jmp     CassetteSend

bf30: 20 0e be     CasSendData     jsr     SetPpuCtrl              ;I think this is to disable NMI?
bf33: 20 e7 bd                     jsr     CasBufSetData
bf36: a9 14                        lda     #20                     ;Prog data starts with 20 high bits followed by 20 zeroes
bf38: 85 91                        sta     zpCasCksum
bf3a: a9 00                        lda     #$00
bf3c: 85 92                        sta     zpCasCksum+1
bf3e: a9 20        CassetteSend    lda     #32
bf40: 85 19                        sta     zpMisc
bf42: a9 4e                        lda     #78                     ;if this had been a #79, we'd send 20,000, probably the intended amt
bf44: 85 1a                        sta     zpMisc+1
bf46: 20 a9 bf     @twenThouZeroLp jsr     CassetteSendZero        ;send 77 * 256 + 32 = 19,744 zeroes
bf49: c6 19                        dec     zpMisc
bf4b: d0 f9                        bne     @twenThouZeroLp
bf4d: c6 1a                        dec     zpMisc+1
bf4f: d0 f5                        bne     @twenThouZeroLp
bf51: a5 91                        lda     zpCasCksum
bf53: 85 19                        sta     zpMisc
                   ; After the (not quite) 20,000 zeroes, we send an announce string of N ones and
                   ; N zeroes, where N is 40 for the header, and 20 for the packet that contains
                   ; the actual payload (BASIC program code, or page of BG graphics)
bf55: 20 b3 bf     @onesLp         jsr     CassetteSendOne         ;send a string of ones
bf58: c6 19                        dec     zpMisc
bf5a: d0 f9                        bne     @onesLp
bf5c: 20 a9 bf     @zeroesLp       jsr     CassetteSendZero        ;send a string of zeroes
bf5f: c6 91                        dec     zpCasCksum
bf61: d0 f9                        bne     @zeroesLp
bf63: 20 b3 bf                     jsr     CassetteSendOne
bf66: a9 00                        lda     #$00
bf68: 85 91                        sta     zpCasCksum
bf6a: 85 92                        sta     zpCasCksum+1
bf6c: a0 00        @sendBuffer     ldy     #$00                    ;now send the designated data buffer
bf6e: b1 ad                        lda     (zpCasBufPtr),y
bf70: 20 8d bf                     jsr     CassetteSendByte
bf73: 20 ce bd                     jsr     IncrCasBufPtr
bf76: 20 dc bd                     jsr     IsCasDataDone
bf79: d0 f1                        bne     @sendBuffer
bf7b: a5 91                        lda     zpCasCksum              ;and a checksum (count of the ones in all bytes)
bf7d: 48                           pha
bf7e: a5 92                        lda     zpCasCksum+1
bf80: 20 8d bf                     jsr     CassetteSendByte
bf83: 68                           pla
bf84: 20 8d bf                     jsr     CassetteSendByte
bf87: 20 b3 bf                     jsr     CassetteSendOne         ;send a final "one" bit at the end
bf8a: 4c 14 be                     jmp     SetPpuCtrlAndEnableNmi

                   ; Sends a one (start bit), followed by eight bits from the accumulator, most-
                   ; significant bit first.
                   ; 
                   ; Instead of using the ASL operator, the author uses CLC and ADC to add a zero-
                   ; page value to itself, doubling it each iteration.
                   CassetteSendByte
bf8d: 20 b3 bf                     jsr     CassetteSendOne
bf90: a2 08                        ldx     #$08
bf92: 85 19        @shftLp         sta     zpMisc
bf94: 18                           clc
bf95: 65 19                        adc     zpMisc
bf97: b0 07                        bcs     @sndOne
bf99: 20 a9 bf                     jsr     CassetteSendZero
bf9c: ca           @dex            dex
bf9d: d0 f3                        bne     @shftLp
bf9f: 60                           rts

bfa0: 20 b3 bf     @sndOne         jsr     CassetteSendOne
bfa3: 20 d5 bd                     jsr     IncrOnesCounter
bfa6: 4c 9c bf                     jmp     @dex

                   ; Sends the shorter (higher-freq) off/on cycle, indicating a zero (reset bit)
                   CassetteSendZero
bfa9: 48                           pha
bfaa: a9 34                        lda     #$34
bfac: 85 1b                        sta     zpMisc+2
bfae: 85 1c                        sta     zpMisc+3
bfb0: 4c ba bf                     jmp     CassetteSendBit

                   ; Sends the longer (lower-freq) off/on cycle, indicating a one (set bit)
bfb3: 48           CassetteSendOne pha
bfb4: a9 6a                        lda     #$6a
bfb6: 85 1b                        sta     zpMisc+2
bfb8: 85 1c                        sta     zpMisc+3
                   ; zpMisc+2 specifies how long to send low signal,
                   ; zpMisc+3 specifies how long to send high signal
bfba: a9 04        CassetteSendBit lda     #$04
bfbc: 8d 16 40                     sta     JOY1
bfbf: c6 1c        @loLp           dec     zpMisc+3
bfc1: d0 fc                        bne     @loLp
bfc3: a9 ff                        lda     #$ff
bfc5: 8d 16 40                     sta     JOY1
bfc8: c6 1b        @hiLp           dec     zpMisc+2
bfca: d0 fc                        bne     @hiLp
bfcc: 68                           pla
bfcd: 60                           rts

                   vZpBgtTileBufIdx .var   $3d    {addr/1}         ;index into buffer for saving tiles from behind BGTOOL mode-select menu
                   vZpRowsRemain   .var    $74    {addr/1}
                   vZpBgtMenuItemIdx .var  $75    {addr/1}
                   vZpBgToolModeId .var    $7c    {addr/1}
                   vZpBgtSelectCurTileRow .var $7d    {addr/1}     ;which row of tiles is showing in the tile SELECT menu
                   vZpTileIdTmp    .var    $7e    {addr/1}
                   vZpBgtCurRowSelTileNum .var $7f    {addr/1}     ;index to which of the tiles in current tile row is the active tile for insertion
                   vZpBgtCurPaletteNum .var $88    {addr/1}        ;currently-selected palette num for inserted tiles

bfce: 20 a7 b4     BgtMode_CLEAR   jsr     DoCLS_1                 ;clear SCREEN 1, restart BGTOOL
bfd1: 20 36 b4     CmdFn_BGTOOL    jsr     ResetPaletteSets        ;reset the palette sets to their hard-coded defaults
bfd4: a0 08                        ldy     #$08
bfd6: a2 00                        ldx     #$00
bfd8: 8a                           txa
bfd9: 9d 45 06     @attrClrLp      sta     sprAttrFlags,x          ;clear sprite tile attrs for the 8 actors.
bfdc: e8                           inx
bfdd: 88                           dey
bfde: d0 f9                        bne     @attrClrLp              ;-> loop until 8 actors' sprite attrs cleared
bfe0: a9 24                        lda     #>VRAM_screen1
bfe2: 85 4f                        sta     zpPpuScreenBasePage     ;set SCREEN 1 as current screen
bfe4: a9 00                        lda     #0
bfe6: 85 48                        sta     zpCH
bfe8: a9 15                        lda     #21
bfea: 85 49                        sta     zpCV                    ;put cursor at x:0,y:21
bfec: a9 1a                        lda     #key_CtrlZ              ;Control-Z
bfee: 20 75 b5                     jsr     PrintOneChar            ;invokes Control-Z's ClearToEndOfScreen function
bff1: a9 01                        lda     #$01                    ; (clearing from 0,21 to end of screen: last 4 rows)
bff3: 85 f0                        sta     zpCurScreenNum          ;set the other SCREEN 1 zp var
bff5: 20 e8 ae                     jsr     ClearOam                ;clear all sprite info from OAM DMA area
bff8: 20 2b b4                     jsr     WaitForVBlank           ;wait for vblank
bffb: a5 32                        lda     zpPpuCtrlVal
bffd: 09 09                        ora     #$09
bfff: 85 32                        sta     zpPpuCtrlVal            ;set screen 1 for PPU, and set BG to use BG tiles
c001: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi
c004: 20 14 be                     jsr     SetPpuCtrlAndEnableNmi  ;enact the PPUCTRL changes
c007: 20 c9 af                     jsr     CmdFn_SPRITE_ON         ;turn sprites on
c00a: a9 00                        lda     #$00
c00c: 8d 05 05                     sta     bgtCH                   ;move BGTOOL cursor to 0,0
c00f: 8d 06 05                     sta     bgtCV
c012: 85 88                        sta     vZpBgtCurPaletteNum     ;use BG palette 0
c014: 8d 04 05                     sta     lineBuffer+4            ;???
c017: 4c e8 c2                     jmp     BgtStartupSelectMode    ;-> done with init, enter SELECT mode

                   ; Copy BGTOOL's cursor coords to global (text) cursor coords
                   BgtCursorToGlobalCursor
c01a: ad 05 05                     lda     bgtCH
c01d: 85 48                        sta     zpCH
c01f: ad 06 05                     lda     bgtCV
c022: 85 49                        sta     zpCV
c024: 60                           rts

                   BgtSetCursorAndReadChar
c025: 85 e0                        sta     zpKanaCursChr
c027: 85 e1                        sta     zpNonKCursChr
c029: 20 66 b7                     jsr     DeactivateKanaMode
c02c: 4c 20 bc                     jmp     ReadChar

                   ; Move the tile cursor (box sprite) according to the cursor key that was typed.
                   BgtHandleCursorKeys
c02f: c9 1c                        cmp     #key_CursorRight
c031: f0 0d                        beq     BgtCursorRight
c033: c9 1d                        cmp     #key_CursorLeft
c035: f0 16                        beq     BgtCursorLeft
c037: c9 1e                        cmp     #key_CursorUp
c039: f0 22                        beq     BgtCursorUp
c03b: c9 1f                        cmp     #key_CursorDown
c03d: f0 2e                        beq     BgtCursorDown
c03f: 60                           rts

c040: ad 05 05     BgtCursorRight  lda     bgtCH
c043: c9 1b                        cmp     #COLS-1                 ;cursor on last column?
c045: f0 21                        beq     BgtCursStartOfNextLn    ;yes -> cursor to start of next screen row
c047: ee 05 05                     inc     bgtCH                   ;else, move cursor right
c04a: a9 00        BgtNullKey      lda     #$00                    ;zero register A, to prevent further key handling
c04c: 60                           rts

c04d: ad 05 05     BgtCursorLeft   lda     bgtCH                   ;cursor on first column?
c050: f0 06                        beq     BgtCursEndOfPrevLn      ;yes -> cursor to end of prev row
c052: ce 05 05                     dec     bgtCH                   ;else, move cursor left
c055: 4c 4a c0                     jmp     BgtNullKey              ;-> done

                   BgtCursEndOfPrevLn
c058: a9 1b                        lda     #COLS-1
c05a: 8d 05 05                     sta     bgtCH                   ;move cursor to last column
c05d: ad 06 05     BgtCursorUp     lda     bgtCV                   ;cursor already at top line?
c060: f0 e8                        beq     BgtNullKey              ;-> done (nothing to do)
c062: ce 06 05                     dec     bgtCV                   ;else move cursor up
c065: 4c 4a c0                     jmp     BgtNullKey              ;-> done

                   BgtCursStartOfNextLn
c068: a9 00                        lda     #$00
c06a: 8d 05 05                     sta     bgtCH                   ;move cursor to start of line
c06d: ad 06 05     BgtCursorDown   lda     bgtCV
c070: c9 14                        cmp     #BGT_ROWS-1             ;cursor already on bottom line?
c072: f0 d6                        beq     BgtNullKey              ;yes -> done (nothing to do)
c074: ee 06 05                     inc     bgtCV                   ;else move cursor down one row
c077: d0 d1                        bne     BgtNullKey              ;(always) -> done
                   ; 
                   BgtModeSelectMenu
c079: 20 d5 af                     jsr     CmdFn_SPRITE_OFF        ;turn off sprites
c07c: 20 05 c1                     jsr     BgtSaveTilesBehindMenu  ;save the tiles behind where menu will be drawn
c07f: 20 48 b7                     jsr     CursorHome              ;LOCATE 0,0
c082: 20 5c c2                     jsr     BgtDisplayModeSelectMenu ;display the mode-select menu
c085: 20 48 b7                     jsr     CursorHome              ;LOCATE 0,0
c088: a9 3e        BgtMenu_Prompt  lda     #‘>’
c08a: 20 25 c0                     jsr     BgtSetCursorAndReadChar ;set '>' as prompt at 0,0 and read a character
c08d: c9 1e                        cmp     #key_CursorUp           ;handle menu keypresses
c08f: f0 3e                        beq     BgtModeSel_PrvMenuItem
c091: c9 1f                        cmp     #key_CursorDown
c093: f0 43                        beq     BgtModeSel_NxtMenuItem
c095: c9 20                        cmp     #key_Space
c097: f0 49                        beq     BgtModeSel_Select
c099: c9 03                        cmp     #key_Stop
c09b: f0 02                        beq     BgtModeSel_ExitTool
c09d: d0 e9                        bne     BgtMenu_Prompt          ;no match? -> reprompt

                   BgtModeSel_ExitTool
c09f: 20 08 c1                     jsr     BgtRestoreTilesBehindMenu ;restore tiles that were behind mode-select menu
c0a2: 20 4c b7                     jsr     CarriageReturn
c0a5: a9 15                        lda     #21
c0a7: 85 49                        sta     zpCV                    ;LOCATE 0,21
c0a9: a9 1a                        lda     #key_CtrlZ
c0ab: 20 75 b5                     jsr     PrintOneChar            ;print ^Z (clear to end of screen)
c0ae: 20 20 b4                     jsr     InitPromptChars         ;restore BASIC prompt cursor chars
c0b1: a9 20                        lda     #>VRAM_screen0
c0b3: 85 4f                        sta     zpPpuScreenBasePage     ;set SCREEN 0 base page
c0b5: 20 36 b4                     jsr     ResetPaletteSets        ;reset palette set definitions
c0b8: 20 ab b4                     jsr     DoCLS                   ;clear the screen (we just did this with Ctrl-Z?)
c0bb: 20 94 b1                     jsr     ClearSpriteData         ;clear sprite definitions
c0be: 20 e8 ae                     jsr     ClearOam                ;hide all sprites, clear attributes
c0c1: a9 10                        lda     #DEFAULT_PPUCTRL
c0c3: 85 32                        sta     zpPpuCtrlVal
c0c5: a9 00                        lda     #$00
c0c7: 85 f0                        sta     zpCurScreenNum          ;set SCREEN 0
c0c9: 20 14 be                     jsr     SetPpuCtrlAndEnableNmi  ;activate new PPUCTRL value, enable NMI
c0cc: 4c 6a 82                     jmp     CmdFn_END               ;-> return to BASIC

                   BgtModeSel_PrvMenuItem
c0cf: a5 49                        lda     zpCV                    ;already at top menu item?
c0d1: f0 b5                        beq     BgtMenu_Prompt          ;yes -> just reprompt
c0d3: c6 49                        dec     zpCV                    ;else go up a row
c0d5: 4c 88 c0                     jmp     BgtMenu_Prompt          ;-> reprompt

                   BgtModeSel_NxtMenuItem
c0d8: a5 49                        lda     zpCV
c0da: c9 05                        cmp     #BGT_MODESEL_ITEMS      ;already at bottom menu item?
c0dc: f0 aa                        beq     BgtMenu_Prompt          ;yes -> just reprompt
c0de: e6 49                        inc     zpCV                    ;else go down a row
c0e0: d0 a6                        bne     BgtMenu_Prompt          ;-> reprompt
                   ; 
                   BgtModeSel_Select
c0e2: a5 49                        lda     zpCV                    ;get cursor row # (= menu item #)
c0e4: 0a                           asl     A                       ;times 2 for jump-table index
c0e5: aa                           tax
c0e6: bd f3 c0                     lda     jtbl_BgtModeEntryPoints,x ;get entry point for selected mode
c0e9: 85 19                        sta     zpMisc
c0eb: bd f4 c0                     lda     jtbl_BgtModeEntryPoints+1,x
c0ee: 85 1a                        sta     zpMisc+1
c0f0: 6c 19 00                     jmp     (zpMisc)                ;-> and jump to entry point

                   jtbl_BgtModeEntryPoints
c0f3: e5 c2                        .dd2    BgtMode_SELECT
c0f5: be c5                        .dd2    BgtMode_COPY
c0f7: bb c5                        .dd2    BgtMode_MOVE
c0f9: ce bf                        .dd2    BgtMode_CLEAR
c0fb: 97 c6                        .dd2    BgtMode_FILE
c0fd: 7d c1                        .dd2    BgtMode_CHAR

                   bgtMenu_SvRstWrapup
c0ff: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi
c102: 4c 62 b5                     jmp     AwaitVBlankAndUpdatePpuMask

                   BgtSaveTilesBehindMenu
c105: a9 ff                        lda     #$ff
c107: ae                           ldx ▼   a:$00a9
                   BgtRestoreTilesBehindMenu
c108: a9 00                        lda     #$00
c10a: 8d 00 05                     sta     lineBuffer              ;using this location to indicate save vs restore
c10d: 20 59 b5                     jsr     AwaitVBlankAndClearPpuMask ;turn off screen rendering
c110: a9 09                        lda     #9
c112: 85 74                        sta     vZpRowsRemain           ;number of rows to save/restore = 9
c114: a9 00                        lda     #$00
c116: 85 3d                        sta     vZpBgtTileBufIdx        ;start buffer index at 0
c118: 20 48 b7                     jsr     CursorHome              ;LOCATE 0,0
                   bgtMenu_SvRstLoop
c11b: 20 f4 b4                     jsr     CalcCursorTilemapPos
c11e: 20 2e b5                     jsr     PpuGoToCursor           ;set current VRAM addr to cursor position
c121: ad 00 05                     lda     lineBuffer              ;"restore" mode?
c124: f0 03                        beq     @skipFake               ;yes -> skip faked PPUDATA read
c126: ad 07 20                     lda     PPUDATA                 ;else, fake a read from PPU
c129: a0 0a        @skipFake       ldy     #10                     ;number of cols to save/restore per row = 10
c12b: a6 3d                        ldx     vZpBgtTileBufIdx
c12d: ad 00 05     @nextTile       lda     lineBuffer              ;"restore" mode?
c130: f0 09                        beq     @restore                ;yes -> restore buffered tile
c132: ad 07 20                     lda     PPUDATA                 ;else, get next tile from VRAM
c135: 9d 20 05                     sta     bgtMenuTilesBuf,x       ;save to buffer
c138: 4c 41 c1                     jmp     @nextIter

c13b: bd 20 05     @restore        lda     bgtMenuTilesBuf,x       ;get the original tile ID from the save/restore buffer
c13e: 8d 07 20                     sta     PPUDATA                 ;write it to VRAM
c141: e8           @nextIter       inx                             ;incr index into tile buffer
c142: 88                           dey                             ;decr remaining rows to copy
c143: d0 e8                        bne     @nextTile               ;rows left? -> save or restore next row
c145: 86 3d                        stx     vZpBgtTileBufIdx        ;save buffer index
c147: c6 74                        dec     vZpRowsRemain           ;one fewer row to save/restore
c149: f0 b4                        beq     bgtMenu_SvRstWrapup     ;out of rows? -> done
c14b: e6 49                        inc     zpCV                    ;move cursor to next screen row
c14d: d0 cc                        bne     bgtMenu_SvRstLoop       ;-> loop to handle next row
                   ; 
                   BgtUpdateCursorAndPrintPos
c14f: 20 e8 ae                     jsr     ClearOam                ;clear all sprite data
c152: a9 b0                        lda     #TileId_emptyBox        ;empty box tile ID
c154: 8d 01 02                     sta     oamSprTileId            ;set that as sprite graphic
c157: a9 00                        lda     #$00
c159: 8d 02 02                     sta     oamSprAttr              ;clear sprite attrs
c15c: ad 05 05                     lda     bgtCH                   ;get bgtool cursor X position
c15f: 18                           clc
c160: 69 02                        adc     #HMARGIN                ;add 2 for margin
c162: 0a                           asl     A
c163: 0a                           asl     A
c164: 0a                           asl     A                       ;times 8 (tiles -> pixels)
c165: 8d 03 02                     sta     oamSprXPos              ;set as cursor sprite x pos
c168: ad 06 05                     lda     bgtCV                   ;get bgtool cursor Y position
c16b: 18                           clc
c16c: 69 03                        adc     #VMARGIN                ;add 3 for vert margin
c16e: 0a                           asl     A
c16f: 0a                           asl     A
c170: 0a                           asl     A                       ;times 8 (tiles -> pixels)
c171: 8d 00 02                     sta     oamSprYPos              ;set as cursor sprite y pos
c174: ce 00 02                     dec     oamSprYPos              ;subtract one (for framing? idk)
c177: 4c cc c1                     jmp     BgtPrintCursorPos

                   modeSelMenu_C17A
c17a: 4c 79 c0                     jmp     BgtModeSelectMenu

c17d: 20 20 b4     BgtMode_CHAR    jsr     InitPromptChars         ;restore text prompt cursor chars
c180: 20 08 c1                     jsr     BgtRestoreTilesBehindMenu ;restore the tiles from before mode-select menu
c183: a9 05                        lda     #$05
c185: 20 76 c2                     jsr     BgtSetAndPrintCurrentMode ;print that we're in CHAR mode
c188: 20 d5 af                     jsr     CmdFn_SPRITE_OFF        ;turn off sprites (using normal prompt cursor chars instead)
c18b: 20 1a c0                     jsr     BgtCursorToGlobalCursor ;copy BGTOOL cursor pos to global (text) cursor
c18e: 20 56 b6     BgtChar_Prompt  jsr     SaveCursorPos           ;save the new cursor pos
c191: a5 48                        lda     zpCH                    ;copy global (text) cursor pos to BGTOOL cursor pos
c193: 8d 05 05                     sta     bgtCH
c196: a5 49                        lda     zpCV
c198: 8d 06 05                     sta     bgtCV
c19b: 20 cc c1                     jsr     BgtPrintCursorPos       ;update cursor position display
c19e: 20 5f b6                     jsr     RestoreCursorPos        ;restore cursor pos (changed during display update)
c1a1: 20 20 bc                     jsr     ReadChar                ;read keyboard (with flashing cursor prompt)
c1a4: c9 0d                        cmp     #key_Enter              ;handle special keys:
c1a6: f0 0f                        beq     BgtChar_Enter
c1a8: c9 1b                        cmp     #key_Escape
c1aa: f0 ce                        beq     modeSelMenu_C17A
c1ac: 90 e0                        bcc     BgtChar_Prompt
c1ae: 20 75 b5                     jsr     PrintOneChar            ;no special key? then echo it here (draw to background)
c1b1: 20 c0 c1                     jsr     BgtChar_MaybeFixCV      ;if we exceeded last row, return to it
c1b4: 4c 8e c1                     jmp     BgtChar_Prompt          ;-> prompt for another input key

c1b7: 20 6f b5     BgtChar_Enter   jsr     DoCRLF                  ;go to start of next line
c1ba: 20 c0 c1                     jsr     BgtChar_MaybeFixCV
c1bd: 4c 8e c1                     jmp     BgtChar_Prompt          ;-> prompt for next input

                   BgtChar_MaybeFixCV
c1c0: a5 49                        lda     zpCV
c1c2: c9 15                        cmp     #BGT_ROWS               ;cursor y pos >= last BG row?
c1c4: b0 01                        bcs     @fix                    ;yes -> fix
c1c6: 60                           rts                             ;else -> return

c1c7: a9 14        @fix            lda     #$14
c1c9: 85 49                        sta     zpCV                    ;set y pos to last BG row
c1cb: 60                           rts                             ;-> return

                   BgtPrintCursorPos
c1cc: 20 4c b7                     jsr     CarriageReturn
c1cf: a9 16                        lda     #22
c1d1: 85 49                        sta     zpCV                    ;LOCATE 0,22
c1d3: a9 58                        lda     #‘X’
c1d5: 20 4d 89                     jsr     QueueCharForOutput
c1d8: a9 3a                        lda     #‘:’
c1da: 20 4d 89                     jsr     QueueCharForOutput
c1dd: 20 5d 89                     jsr     PrintOutBuf             ;print "X:"
c1e0: 20 6b b5                     jsr     EnsureSOL               ;CRLF (start of next line)
c1e3: a9 59                        lda     #‘Y’
c1e5: 20 4d 89                     jsr     QueueCharForOutput
c1e8: a9 3a                        lda     #‘:’
c1ea: 20 4d 89                     jsr     QueueCharForOutput
c1ed: 20 5d 89                     jsr     PrintOutBuf             ;print "Y:"
c1f0: ad 05 05                     lda     bgtCH
c1f3: 85 28                        sta     zpWAccum                ;put cursor x pos into WAccum
c1f5: a9 02                        lda     #2
c1f7: 85 48                        sta     zpCH
c1f9: a9 16                        lda     #22
c1fb: 85 49                        sta     zpCV                    ;LOCATE 2,22
c1fd: 20 0b c2                     jsr     @prCursPos              ;print cursor x pos
c200: ad 06 05                     lda     bgtCV                   ;put cursor y pos into WAccum
c203: 85 28                        sta     zpWAccum
c205: a9 02                        lda     #2
c207: 85 48                        sta     zpCH
c209: e6 49                        inc     zpCV                    ;LOCATE 2,23
c20b: 20 ce a7     @prCursPos      jsr     ClearOperandHiBytes     ;set WAccum high byte to 0
c20e: 20 3e 91                     jsr     PosDecStrFromWord       ;convert WAccum (cursor position) to decimal string
c211: a9 3d                        lda     #zpNumStrTmp            ;point output string at decimal string
c213: 18                           clc
c214: 69 03                        adc     #$03                    ;...plus 3, so we get the final 2 digits (skips 3 leading zeroes)
c216: 85 52                        sta     zpOutputStr
c218: a9 00                        lda     #>zpNumStrTmp
c21a: 85 53                        sta     zpOutputStr+1
c21c: 4c 5a 89                     jmp     PrintString             ;-> print the coordinate, and return

c21f: 4d 4f 44 45+ strMODE         .zstr   “MODE  ”
c226: 53 45 4c 45+ strSELECT       .zstr   “SELECT”
c22d: 4d 4f 56 45+ strMOVE         .zstr   “MOVE  ”
c234: 43 4f 50 59+ strCOPY         .zstr   “COPY  ”
c23b: 43 48 41 52+ strCHAR         .zstr   “CHAR  ”
c242: 43 4c 45 41+ strCLEAR        .zstr   “CLEAR ”
c249: 46 49 4c 45+ strFILE         .zstr   “FILE  ”
                   tbl_BgToolModeStrs
c250: 26 c2                        .dd2    strSELECT
c252: 34 c2                        .dd2    strCOPY
c254: 2d c2                        .dd2    strMOVE
c256: 42 c2                        .dd2    strCLEAR
c258: 49 c2                        .dd2    strFILE
c25a: 3b c2                        .dd2    strCHAR

                   BgtDisplayModeSelectMenu
c25c: a9 00                        lda     #$00
c25e: 85 75                        sta     vZpBgtMenuItemIdx       ;start with first menu item
c260: a9 20        @menuLp         lda     #$20
c262: 20 4d 89                     jsr     QueueCharForOutput      ;print SPACE char
c265: a5 75                        lda     vZpBgtMenuItemIdx
c267: 20 93 c2                     jsr     BgtPrintModeStr         ;print the menu item string
c26a: 20 6f b5                     jsr     DoCRLF                  ;move cursor to start of next line
c26d: e6 75                        inc     vZpBgtMenuItemIdx       ;advance menu item
c26f: a5 75                        lda     vZpBgtMenuItemIdx
c271: c9 06                        cmp     #$06                    ;printed six menu items?
c273: d0 eb                        bne     @menuLp                 ;no -> loop and print next item
c275: 60                           rts

                   ; Store the mode id in register A, and print the mode at bottom of screen.
                   BgtSetAndPrintCurrentMode
c276: 85 7c                        sta     vZpBgToolModeId
                   ; Print the currently-selected BGTOOL mode at the bottom of the screen.
                   BgtPrintCurrentMode
c278: a9 05                        lda     #5
c27a: 85 48                        sta     zpCH
c27c: a9 16                        lda     #22
c27e: 85 49                        sta     zpCV                    ;LOCATE 5,22
c280: a9 1f                        lda     #<strMODE
c282: 85 52                        sta     zpOutputStr
c284: a9 c2                        lda     #>strMODE
c286: 85 53                        sta     zpOutputStr+1
c288: 20 5a 89                     jsr     PrintString             ;print "MODE" at 5,22
c28b: a9 05                        lda     #$05
c28d: 85 48                        sta     zpCH
c28f: e6 49                        inc     zpCV                    ;LOCATE 5,23
c291: a5 7c                        lda     vZpBgToolModeId         ;get the current mode index
                   ; Print the name of the BGTOOL mode whose ID is in the A register.
c293: 0a           BgtPrintModeStr asl     A                       ;times 2 for use as a string-table index
c294: aa                           tax
c295: bd 50 c2                     lda     tbl_BgToolModeStrs,x
c298: 85 52                        sta     zpOutputStr
c29a: bd 51 c2                     lda     tbl_BgToolModeStrs+1,x
c29d: 85 53                        sta     zpOutputStr+1           ;point zpOutputStr at the indexed string
c29f: 4c 5a 89                     jmp     PrintString             ;and print it

                   ; the starting tile ID for each row of 8 tiles that can be viewed in BGTOOL's
                   ; SELECT mode. (Skips "character" tiles; hence the leap to $b8 after the row
                   ; that starts with $18 - $20 would be the space character)
                   tbl_BgSelectTileRowStarts
c2a2: 00 08 10 18+                 .bulk   $00,$08,$10,$18,$b8,$c0,$c8,$d0
c2aa: d8 e0 e8 f0+                 .bulk   $d8,$e0,$e8,$f0,$f8

                   BgtSELECT_NextPalette
c2af: 20 b5 c2                     jsr     BgtIncrPaletteNumAndDisplay ;select next palette in palette set
c2b2: 4c 03 c3                     jmp     BgtSelectModeContinue

                   BgtIncrPaletteNumAndDisplay
c2b5: 20 53 c3                     jsr     BgtIncrPaletteNumAndSet
c2b8: a5 48                        lda     zpCH                    ;push CH, CV
c2ba: 48                           pha
c2bb: a5 49                        lda     zpCV
c2bd: 48                           pha
c2be: a9 0a                        lda     #10
c2c0: 85 48                        sta     zpCH
c2c2: a9 16                        lda     #22
c2c4: 85 49                        sta     zpCV                    ;locate 10,22
c2c6: a5 88                        lda     vZpBgtCurPaletteNum     ;get the currently-selected palette number
c2c8: 09 30                        ora     #$30                    ;add #$30 to convert to ASCII digit
c2ca: 20 75 b5                     jsr     PrintOneChar            ;print the digit (after "MODE") at bottom of screen
c2cd: 68                           pla
c2ce: 85 49                        sta     zpCV                    ;pull & restore CH, CV
c2d0: 68                           pla
c2d1: 85 48                        sta     zpCH
c2d3: 60                           rts

                   BgtSELECT_DeleteTile
c2d4: 20 56 b6                     jsr     SaveCursorPos           ;save global cursor pos
c2d7: 20 1a c0                     jsr     BgtCursorToGlobalCursor ;set it to bgt cursor pos
c2da: a9 20                        lda     #key_Space
c2dc: 20 75 b5                     jsr     PrintOneChar            ;write a space over this location
c2df: 20 5f b6                     jsr     RestoreCursorPos        ;restore global cursor pos
c2e2: 4c 03 c3                     jmp     BgtSelectModeContinue   ;-> done

                   ; BGTOOL SELECT mode, entry from mode-select menu
c2e5: 20 08 c1     BgtMode_SELECT  jsr     BgtRestoreTilesBehindMenu
                   ; entry from BGTOOL command, or via CLEAR mode selection
                   BgtStartupSelectMode
c2e8: 20 c9 af                     jsr     CmdFn_SPRITE_ON         ;turn sprites on
c2eb: a9 00                        lda     #$00
c2ed: 85 7f                        sta     vZpBgtCurRowSelTileNum  ;currently-selected tile idx = 0
c2ef: 85 7d                        sta     vZpBgtSelectCurTileRow  ;current tile row = 0
c2f1: 20 76 c2                     jsr     BgtSetAndPrintCurrentMode ;print that we're in SELECT mode
c2f4: a9 03                        lda     #$03
c2f6: 85 88                        sta     vZpBgtCurPaletteNum     ;set current palette to 3 bc we'll increment before displaying
c2f8: 20 b5 c2                     jsr     BgtIncrPaletteNumAndDisplay ;increment (wraparound to 0) and set for tile menu
c2fb: a9 0c                        lda     #12
c2fd: 85 48                        sta     zpCH
c2ff: a9 17                        lda     #23
c301: 85 49                        sta     zpCV                    ;LOCATE 12,23 (useless, immediately relocated in next subrtn)
                   BgtSelectModeContinue
c303: 20 08 c4                     jsr     BgtCalcAndDisplayTileSelectRow ;show the current set of bg tiles
c306: 20 56 b6     @prompt         jsr     SaveCursorPos
c309: 20 4f c1                     jsr     BgtUpdateCursorAndPrintPos ;show the cursor box, and print its position
c30c: 20 5f b6                     jsr     RestoreCursorPos        ;LOCATE 12,23 (just under the first SELECT-menu tile)
c30f: a9 5e                        lda     #‘^’
c311: 20 25 c0                     jsr     BgtSetCursorAndReadChar ;set '^' as the kbd cursor and prompt for keyboard input
c314: 20 2f c0                     jsr     BgtHandleCursorKeys
c317: a2 00                        ldx     #$00                    ;start with index 0
c319: dd 35 c3     @whatKeyLp      cmp     tbl_bgtSelModeKeys,x    ;into SELECT's recognized-keys table
c31c: f0 07                        beq     @handleKey              ;got one? -> handle key
c31e: e8                           inx                             ;else, index++
c31f: e0 08                        cpx     #$08                    ;out of acceptable keys in table?
c321: d0 f6                        bne     @whatKeyLp              ;no -> loop to check next acceptable key value
c323: f0 e1                        beq     @prompt

c325: 8a           @handleKey      txa
c326: 0a                           asl     A                       ;multiply key index times 2, for jump-table index
c327: aa                           tax
c328: bd 3d c3                     lda     jtbl_bgtSelectKeyHandlers,x ;get the address to jump to
c32b: 85 19                        sta     zpMisc
c32d: bd 3e c3                     lda     jtbl_bgtSelectKeyHandlers+1,x
c330: 85 1a                        sta     zpMisc+1
c332: 6c 19 00                     jmp     (zpMisc)                ;-> then do the jump

                   ; A table of the recognized input keys in BGTOOL's SELECT mode
                   tbl_bgtSelModeKeys
c335: 1b                           .dd1    key_Escape
c336: 0d                           .dd1    key_Enter
c337: 0c                           .dd1    key_Cls
c338: 0b                           .dd1    key_Home
c339: 08                           .dd1    key_Del
c33a: 12                           .dd1    key_Insert
c33b: 20                           .dd1    key_Space
c33c: 44                           .dd1    ‘D’
                   jtbl_bgtSelectKeyHandlers
c33d: 7d c3                        .dd2    BgtSELECT_ModeMenu      ;Esc
c33f: af c2                        .dd2    BgtSELECT_NextPalette   ;Enter
c341: 94 c3                        .dd2    BgtSELECT_TileMenuPrevRow ;Shift-Home
c343: 9d c3                        .dd2    BgtSELECT_TileMenuNextRow ;Home
c345: a7 c3                        .dd2    BgtSELECT_TileMenuNextTile ;Backspace (DEL)
c347: b5 c3                        .dd2    BgtSELECT_TileMenuPrevTile ;Ins
c349: c3 c3                        .dd2    BgtSELECT_WriteTile     ;Space
c34b: d4 c2                        .dd2    BgtSELECT_DeleteTile    ;D

c34d: a9 00        @wrapAround     lda     #$00
c34f: 85 88                        sta     vZpBgtCurPaletteNum
c351: f0 08                        beq     @wrapCont

                   BgtIncrPaletteNumAndSet
c353: e6 88                        inc     vZpBgtCurPaletteNum     ;increment the palette number
c355: a5 88                        lda     vZpBgtCurPaletteNum
c357: c9 04                        cmp     #NUM_PALETTES           ;did we advance past the last palette #?
c359: f0 f2                        beq     @wrapAround             ;yes -> wrap around to zero and then continue
c35b: 0a           @wrapCont       asl     A
c35c: 0a                           asl     A
c35d: 05 88                        ora     vZpBgtCurPaletteNum     ;copy for the top two of the quadrants covered in an attr byte
c35f: 48                           pha                             ;then push that value to CPU stack
c360: 20 2b b4                     jsr     WaitForVBlank           ;wait for vblank so it's safe to mess with PPU
c363: a9 27                        lda     #$27
c365: a2 f3                        ldx     #$f3                    ;go to $27F3 in VRAM, corresponding to
c367: 20 ec 81                     jsr     PpuGoToAX               ; the palette ids for SELECT mode's displayed tiles
c36a: 68                           pla
c36b: aa                           tax
c36c: 29 fc                        and     #$fc                    ;make a version that covers just the top-right quadrant
c36e: 8d 07 20                     sta     PPUDATA                 ;store that as the first attr (this avoids setting color on the text
c371: 8a                           txa                             ; to the left of the tile menu)
c372: a2 04                        ldx     #$04
c374: 8d 07 20     @cpyLp          sta     PPUDATA                 ;copy the full attribute byte, for the next 4 bytes
c377: ca                           dex
c378: d0 fa                        bne     @cpyLp
c37a: 4c 3c b5                     jmp     NmiSetVScrollAndEnableNmi ;-> done

                   ; When ESC is typed while in SELECT mode
                   BgtSELECT_ModeMenu
c37d: a9 0c                        lda     #12
c37f: 85 48                        sta     zpCH
c381: a9 16                        lda     #22
c383: 85 49                        sta     zpCV                    ;LOCATE 5,22
c385: a9 05                        lda     #$05                    ;Control-E
c387: 20 75 b5                     jsr     PrintOneChar            ;print ^E (clear to end of line - erases menu tiles)
c38a: e6 49                        inc     zpCV                    ;LOCATE 5,23
c38c: a9 05                        lda     #$05                    ;Control-E
c38e: 20 75 b5                     jsr     PrintOneChar            ;print ^E (clear to end of line - erases ^ prompt)
c391: 4c 79 c0                     jmp     BgtModeSelectMenu       ;-> present mode-select menu

                   BgtSELECT_TileMenuPrevRow
c394: a5 7d                        lda     vZpBgtSelectCurTileRow  ;get current tiles-row index
c396: f0 02                        beq     bgtSelModeCont_C39A     ;first row? -> done; nothing to do
c398: c6 7d                        dec     vZpBgtSelectCurTileRow  ;else decr row
                   bgtSelModeCont_C39A
c39a: 4c 03 c3                     jmp     BgtSelectModeContinue   ;-> continue SELECT mode

                   BgtSELECT_TileMenuNextRow
c39d: a5 7d                        lda     vZpBgtSelectCurTileRow  ;get current tiles-row index
c39f: c9 0c                        cmp     #BGT_SELECT_NUM_TROWS-1 ;on last row of tiles?
c3a1: f0 f7                        beq     bgtSelModeCont_C39A     ;yes -> done; nothing to do
c3a3: e6 7d                        inc     vZpBgtSelectCurTileRow  ;else go to next tile row
c3a5: d0 f3                        bne     bgtSelModeCont_C39A     ;(always) -> done (redisplay)
                   ; 
                   BgtSELECT_TileMenuNextTile
c3a7: a5 48                        lda     zpCH                    ;get cursor position
c3a9: c9 1a                        cmp     #SELMD_LAST_TILEPOS     ;cursor at last tile of row?
c3ab: f0 ed                        beq     bgtSelModeCont_C39A     ;yes -> done; nothing to do
c3ad: e6 48                        inc     zpCH                    ;else advance cursor by two
c3af: e6 48                        inc     zpCH
c3b1: e6 7f                        inc     vZpBgtCurRowSelTileNum  ;and tile selection by one
c3b3: d0 e5                        bne     bgtSelModeCont_C39A     ;(always) -> done (redisplay)
                   ; 
                   BgtSELECT_TileMenuPrevTile
c3b5: a5 48                        lda     zpCH
c3b7: c9 0c                        cmp     #SELMD_FRST_TILEPOS     ;cursor at first tile of row?
c3b9: f0 df                        beq     bgtSelModeCont_C39A     ;yes -> done; nothing to do
c3bb: c6 48                        dec     zpCH                    ;else back up cursor two spaces
c3bd: c6 48                        dec     zpCH
c3bf: c6 7f                        dec     vZpBgtCurRowSelTileNum  ;select previous tile
c3c1: 10 d7                        bpl     bgtSelModeCont_C39A     ;(always) -> done (redisplay)
                   ; 
                   BgtSELECT_WriteTile
c3c3: 20 56 b6                     jsr     SaveCursorPos           ;save prompt cursor location
c3c6: 20 1a c0                     jsr     BgtCursorToGlobalCursor ;copy bgtCH,bgtCV to CH,CV
c3c9: 20 f4 b4                     jsr     CalcCursorTilemapPos    ;calculate VRAM addr for new position
c3cc: a6 7d                        ldx     vZpBgtSelectCurTileRow  ;get tile-menu row #
c3ce: bd a2 c2                     lda     tbl_BgSelectTileRowStarts,x ;get starting tile ID for row
c3d1: 18                           clc
c3d2: 65 7f                        adc     vZpBgtCurRowSelTileNum  ;add which tile in row is selected
c3d4: 85 73                        sta     zpCharUnderCurs         ;save as the tile ID to write
c3d6: a9 09                        lda     #NC_RestoreCharAtCursor
c3d8: 20 6a 89                     jsr     WaitForNmiSubcommand    ;write the tile to screen
c3db: a5 88                        lda     vZpBgtCurPaletteNum
c3dd: 85 9c                        sta     zpPaletteNum            ;set the palette at cursor's current quadrant,
c3df: 20 ff c4                     jsr     COLOR_SetPalette        ; to current SELECT palette choice
c3e2: ee 05 05                     inc     bgtCH                   ;after writing tile, advance BGTOOL cursor to next tile cell
c3e5: ad 05 05                     lda     bgtCH
c3e8: c9 1c                        cmp     #COLS                   ;did we go past the end of the screen?
c3ea: f0 06                        beq     @cursNextRow            ;yes -> move cursor to first cell of next row
c3ec: 20 5f b6     @rstCursAndDone jsr     RestoreCursorPos        ;restore prompt cursor location
c3ef: 4c 03 c3                     jmp     BgtSelectModeContinue   ;-> return to SELECT kbd prompt

c3f2: a9 00        @cursNextRow    lda     #$00
c3f4: 8d 05 05                     sta     bgtCH                   ;put bgtool cursor at start of line
c3f7: ee 06 05                     inc     bgtCV                   ;advance bgtool cursor to next line
c3fa: ad 06 05                     lda     bgtCV
c3fd: c9 15                        cmp     #BGT_ROWS               ;moved past the end of BGTOOL screen?
c3ff: d0 eb                        bne     @rstCursAndDone         ;no -> done
c401: a9 14                        lda     #BGT_ROWS-1             ;yes -> lock it to last row of BGTOOL
c403: 8d 06 05                     sta     bgtCV
c406: 10 e4                        bpl     @rstCursAndDone         ;-> done

                   BgtCalcAndDisplayTileSelectRow
c408: 20 56 b6                     jsr     SaveCursorPos           ;save cursor position
c40b: a9 0c                        lda     #12
c40d: 85 48                        sta     zpCH
c40f: a9 16                        lda     #22
c411: 85 49                        sta     zpCV                    ;LOCATE 12, 22
c413: a6 7d                        ldx     vZpBgtSelectCurTileRow  ;get the current tile row for displaying
c415: bd a2 c2                     lda     tbl_BgSelectTileRowStarts,x ;load the starting tile ID for that row
c418: 85 7e                        sta     vZpTileIdTmp            ;and save it
c41a: a0 08                        ldy     #$08
c41c: a2 00                        ldx     #$00
c41e: a5 7e        @tilesLp        lda     vZpTileIdTmp            ;get tile ID
c420: 9d 10 05                     sta     lineBuffer+16,x         ;store in buffer
c423: e8                           inx                             ;increment dst buffer index
c424: a9 20                        lda     #$20                    ;SPACE character
c426: 9d 10 05                     sta     lineBuffer+16,x         ;store that after row tile, in buffer
c429: e8                           inx                             ;incr dst buf index
c42a: e6 7e                        inc     vZpTileIdTmp            ;next tile ID for row
c42c: 88                           dey                             ;one fewer tile remaining
c42d: d0 ef                        bne     @tilesLp                ;-> loop until 8 tiles done
c42f: 20 f4 b4                     jsr     CalcCursorTilemapPos
c432: a2 00                        ldx     #$00
c434: a0 08                        ldy     #$08
c436: 20 2b b4                     jsr     WaitForVBlank           ;wait for next vblank so it's safe to write to VRAM
c439: 20 2e b5                     jsr     PpuGoToCursor           ;LOCATE 12, 13
c43c: bd 10 05     @writeLp        lda     lineBuffer+16,x         ;copy the tile row buffer out to screen
c43f: 8d 07 20                     sta     PPUDATA
c442: e8                           inx
c443: a9 20                        lda     #$20
c445: 8d 07 20                     sta     PPUDATA
c448: e8                           inx
c449: 88                           dey
c44a: d0 f0                        bne     @writeLp
c44c: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi
c44f: 4c 5f b6                     jmp     RestoreCursorPos        ;-> restore cursor position, then done

                   ; Given a screen tile position in CH, CV, and a palette num in zpPaletteNum
                   ; ($9C), returns the byte value that holds palette nums for the 4x4 tiles
                   ; holding that location, and an inverted mask for the palette num of the
                   ; sekected 2x2 tile quad within that byte in zpMisc+1 ($1A). Also bit-shifts
                   ; zpPaletteNum to align with that bit position.
                   ; 
                   ; See https://www.nesdev.org/wiki/PPU_attribute_tables for how palette selection
                   ; for a tile quad works in the PPU.
                   • Clear variables

                   GetTileQuadPaletteVal
c452: e6 48                        inc     zpCH                    ;xpos += 2 (left margin)
c454: e6 48                        inc     zpCH
c456: a5 e4                        lda     zpVScroll0              ;get the scroll value for the active screen
c458: a6 4f                        ldx     zpPpuScreenBasePage
c45a: e0 20                        cpx     #>VRAM_screen0
c45c: f0 02                        beq     @scr0
c45e: a5 e5                        lda     zpVScroll1
c460: 4a           @scr0           lsr     A                       ;divide vert. scroll amt by 8 (convert pixels to tiles)
c461: 4a                           lsr     A
c462: 4a                           lsr     A
c463: 18                           clc
c464: 65 49                        adc     zpCV                    ;add Y pos + 3 (upper margin)
c466: 69 03                        adc     #$03
c468: c9 1e                        cmp     #30                     ;modulo 30
c46a: 90 02                        bcc     @lt30
c46c: e9 1e                        sbc     #30                     ;we now have Y, relative to start of screen in memory
c46e: 85 49        @lt30           sta     zpCV                    ;save adjusted Y
c470: 29 fc                        and     #$fc                    ;strip off lowest two bits (now multiple of 4)
c472: 0a                           asl     A                       ;and mul by 2 (now multiple of 8)
c473: 85 4a                        sta     zpCursTileAddr
c475: a5 48                        lda     zpCH
c477: 4a                           lsr     A
c478: 4a                           lsr     A                       ;add xpos/4
c479: 18                           clc
c47a: 65 4a                        adc     zpCursTileAddr
c47c: 69 c0                        adc     #$c0                    ;add to location of start of screen 0 or 1's
c47e: 85 4a                        sta     zpCursTileAddr          ; attribute table in VRAM (either $23C0 or $27C0)
c480: a5 4f                        lda     zpPpuScreenBasePage
c482: 69 03                        adc     #$03                    ;and save as the VRAM address
c484: 85 4b                        sta     zpCursTileAddr+1        ;(we have the byte addr, but which pair of bits is palette num for our quad?)
c486: a9 02                        lda     #$02
c488: 25 49                        and     zpCV                    ;((ypos / 2) mod 2) * 2 [upper vs lower half of byte]
c48a: 85 19                        sta     zpMisc
c48c: a5 48                        lda     zpCH
c48e: 29 03                        and     #$03
c490: 4a                           lsr     A                       ;+ ((xpos / 2) mod 2) [pair of bits within that byte half]
c491: 18                           clc
c492: 65 19                        adc     zpMisc
c494: 85 19                        sta     zpMisc                  ;save as our bit-pair position
c496: a9 fc                        lda     #$fc                    ;binary 11111100, to mask out just our palette num
c498: 85 1a                        sta     zpMisc+1
c49a: 06 19                        asl     zpMisc                  ;bit position *= 2
c49c: a6 19                        ldx     zpMisc
c49e: f0 08                        beq     @shiftDone              ;bit pos zero? -> nothing to shift
c4a0: 06 9c        @shLftLp        asl     zpPaletteNum            ;shift our palette-num val left one bit
c4a2: 38                           sec
c4a3: 26 1a                        rol     zpMisc+1                ;shift our palettes mask left one bit
c4a5: ca                           dex                             ;decrement bit position
c4a6: d0 f8                        bne     @shLftLp                ;not yet at bit position? -> shift left again
c4a8: a9 05        @shiftDone      lda     #NC_GetCharUnderCursor  ;zpPaletteNum aligned with its palette nun, and zpMisc holds aligned mask
c4aa: 20 6a 89                     jsr     WaitForNmiSubcommand    ;get the palettes byte at the VRAM loc we calc'd
c4ad: a5 73                        lda     zpCharUnderCurs
c4af: 60                           rts                             ;and return it

                   ; Calls GetTileQuadPaletteVal, and then shifts the value over to the right as
                   ; needed, so we have the actual number value.
                   GetTileQuadPaletteNum
c4b0: 20 52 c4                     jsr     GetTileQuadPaletteVal   ;get the palette number for this tile quad
c4b3: 85 9c                        sta     zpPaletteNum
c4b5: a5 1a                        lda     zpMisc+1
c4b7: 49 ff                        eor     #$ff                    ;invert bitmask for palette num
c4b9: 25 9c                        and     zpPaletteNum            ;mask out everything but our palette num
c4bb: 85 9c                        sta     zpPaletteNum
c4bd: a4 19                        ldy     zpMisc
c4bf: f0 05                        beq     @rts
c4c1: 46 9c        @shftPalLp      lsr     zpPaletteNum            ;shift palette number right until it's
c4c3: 88                           dey                             ; in the appropriate range
c4c4: d0 fb                        bne     @shftPalLp
c4c6: 60           @rts            rts                             ;and return it

c4c7: 4c 94 84     errIll_C4C7     jmp     ErrorIllegalValue

                   ; COLOR x, y, N
                   ; 
                   ; Selects not a color, but a palette, numbered from the currently-selectded
                   ; palette set (as specified by CGSET). Associates it with the group of four
                   ; (2x2) background tiles which contains the location given by x, y.
c4ca: a5 48        CmdFn_COLOR     lda     zpCH
c4cc: 48                           pha                             ;push cursor position to stack
c4cd: a5 49                        lda     zpCV
c4cf: 48                           pha
c4d0: 20 6e a2                     jsr     EvalByteInteger         ;get 1st arg (x pos)
c4d3: c9 1c                        cmp     #COLS                   ;>= num screen cols?
c4d5: b0 f0                        bcs     errIll_C4C7             ;yes -> error
c4d7: 48                           pha                             ;push Xpos arg
c4d8: 20 6b a2                     jsr     EvalCommaThenByteInt    ;get 2nd arg
c4db: ad 00 04                     lda     exprParseStk
c4de: c9 18                        cmp     #ROWS                   ;>= num screen rows?
c4e0: b0 e5                        bcs     errIll_C4C7             ;yes -> error
c4e2: 85 49                        sta     zpCV                    ;save as cursor Y
c4e4: 68                           pla
c4e5: 85 48                        sta     zpCH                    ;pull 1st arg (x) and set as cursor X
c4e7: 20 6b a2                     jsr     EvalCommaThenByteInt    ;final arg (color palette num)
c4ea: ad 00 04                     lda     exprParseStk
c4ed: c9 04                        cmp     #$04                    ;>= 4?
c4ef: b0 d6                        bcs     errIll_C4C7             ;yes -> error
c4f1: 29 03                        and     #$03                    ;<- redundant
c4f3: 85 9c                        sta     zpPaletteNum            ;save palette num
c4f5: 20 ff c4                     jsr     COLOR_SetPalette
c4f8: 68                           pla
c4f9: 85 49                        sta     zpCV
c4fb: 68                           pla
c4fc: 85 48                        sta     zpCH                    ;restore original cursor pos
c4fe: 60                           rts

                   COLOR_SetPalette
c4ff: 20 52 c4                     jsr     GetTileQuadPaletteVal
c502: 25 1a                        and     zpMisc+1                ;mask out the palette for our tile quad
c504: 05 9c                        ora     zpPaletteNum            ;replace it with user-specified palette num
c506: 85 73                        sta     zpCharUnderCurs
c508: a9 09                        lda     #NC_RestoreCharAtCursor
c50a: 4c 6a 89                     jmp     WaitForNmiSubcommand    ;store the new value, and end command

                   ; BACKGROUND SCREEN HEADER FORMAT:
                   ; 
                   ; byte 0:        #$03
                   ; bytes 1 - 17:  null-term'd filename (no len byte)
                   ; byte 18 - 19:  size of following packet's payload (always #$0100)
                   ; bytes 20 - 21: address of genPurposeBuf (#$0700)
                   ; byte 22:       vScroll1
                   ; bytes 23-127:  (unused) 
                   vZpVramCpyPage  .var    $5f    {addr/2}

                   SaveBackgroundMap
c50d: a9 03                        lda     #CAS_HDR_SCREEN         ;START HEADER CREATION
c50f: 8d 00 05                     sta     lineBuffer              ;Indicate this header precedes screen data
c512: a9 00                        lda     #$00
c514: 8d 14 05                     sta     lineBuffer+20
c517: a9 07                        lda     #$07
c519: 8d 15 05                     sta     lineBuffer+21
c51c: a5 e5                        lda     zpVScroll1
c51e: 8d 16 05                     sta     lineBuffer+22
c521: a2 00                        ldx     #$00
c523: 8e 12 05                     stx     lineBuffer+18
c526: e8                           inx
c527: 8e 13 05                     stx     lineBuffer+19           ;END HEADER CREATION
c52a: a9 00                        lda     #<VRAM_screen1
c52c: 85 5f                        sta     vZpVramCpyPage
c52e: a9 24                        lda     #>VRAM_screen1
c530: 85 60                        sta     vZpVramCpyPage+1
c532: 20 9f 9d                     jsr     Print_WRITING_fileName
                   ; Sends the header, then a payload with one page (256 bytes) of tiles
                   ; from screen 1.
                   ; 
                   ; Very expensive, but can't be helped: we have to wait for a vblank before we
                   ; can safely access the screen data, and I guess we can't send a second page
                   ; without risking vblank ending and getting garbage data. After we send a page,
                   ; we must await vblank again, and we can't do that _and_ send the cassette
                   ; signals at the same time. So, individual header + payload it is.
                   ; 
                   ; Header is exactly the same each time.
c535: 20 59 b5     @sendNextPage   jsr     AwaitVBlankAndClearPpuMask ;wait for next vblank, then disable screen rendering
c538: a5 60                        lda     vZpVramCpyPage+1
c53a: a6 5f                        ldx     vZpVramCpyPage
c53c: 20 ec 81                     jsr     PpuGoToAX               ;go to the VRAM address we'll be copying from
c53f: ad 07 20                     lda     PPUDATA                 ;fake PPU read
c542: a2 00                        ldx     #$00
c544: ad 07 20     @cpyTiles       lda     PPUDATA                 ;read a tile ID
c547: 9d 00 07                     sta     genPurposeBuf,x         ;save it to buffer
c54a: e8                           inx                             ;next tile
c54b: d0 f7                        bne     @cpyTiles               ;-> loop until 256 tiles read
c54d: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi
c550: 20 62 b5                     jsr     AwaitVBlankAndUpdatePpuMask ;restore screen rendering
c553: 20 1f bf                     jsr     CasSendHeader           ;send the header for this page of the BG data
c556: 20 30 bf                     jsr     CasSendData             ;send the data packet for this page
c559: e6 60                        inc     vZpVramCpyPage+1        ;advance to next VRAM page
c55b: a5 60                        lda     vZpVramCpyPage+1
c55d: c9 28                        cmp     #(>VRAM_screen1)+4      ;sent all VRAM pages for the bg screen?
c55f: d0 d4                        bne     @sendNextPage           ;no -> loop until 4 pages sent
c561: 60           rts_C561        rts

                   • Clear variables
                   vZpVramAddr     .var    $5f    {addr/2}         ;Where to send loaded screen 1 data

c562: 20 b9 9d     LBM_SkipFile    jsr     Print_SKIP_fileName
                   LoadBackgroundMap
c565: a9 00                        lda     #$00                    ;set up dest ptr: first page of screen 1
c567: 85 5f                        sta     vZpVramAddr
c569: a9 24                        lda     #$24
c56b: 85 60                        sta     vZpVramAddr+1
c56d: 20 53 be     LBM_readAPage   jsr     CasRcvHeader            ;find and read a header
c570: b0 ef                        bcs     rts_C561                ;error? -> rts
c572: ad 00 05                     lda     lineBuffer              ;get header type
c575: c9 03                        cmp     #CAS_HDR_SCREEN         ;is it a graphics chunk?
c577: d0 e9                        bne     LBM_SkipFile            ;no -> skip next 'til next header
c579: 20 b1 9e                     jsr     LoadCheckFilename       ;yes, it's a BG save. Does filename match?
c57c: b0 e4                        bcs     LBM_SkipFile            ;no -> skip to next header
c57e: 20 ac 9d                     jsr     Print_LOADING_fileName  ;match! (or missing.) Tell user we'll load it.
c581: ad 16 05                     lda     lineBuffer+22
c584: 85 e5                        sta     zpVScroll1              ;set vert scroll from header val
c586: 20 24 be                     jsr     CasRcvFileData          ;load the BG data
c589: b0 d6                        bcs     rts_C561
c58b: 20 59 b5                     jsr     AwaitVBlankAndClearPpuMask ;wait until next vblank so we can safely write VRAM
c58e: a5 60                        lda     vZpVramAddr+1
c590: a6 5f                        ldx     vZpVramAddr
c592: 20 ec 81                     jsr     PpuGoToAX               ;tell PPU: writes go to screen 1
c595: a2 00                        ldx     #$00
c597: bd 00 07     @cpyToVram      lda     genPurposeBuf,x         ;transfer a page of data (one quarter-screen) to screen 1
c59a: 8d 07 20                     sta     PPUDATA
c59d: e8                           inx
c59e: d0 f7                        bne     @cpyToVram              ;loop until page sent
c5a0: 20 3c b5                     jsr     NmiSetVScrollAndEnableNmi ;re-enable NMI on vblank
c5a3: 20 62 b5                     jsr     AwaitVBlankAndUpdatePpuMask
c5a6: e6 60                        inc     vZpVramAddr+1           ;we'll set PPU to the succeeding page
c5a8: a5 60                        lda     vZpVramAddr+1
c5aa: c9 28                        cmp     #$28                    ;are we at the end of screen 1?
c5ac: d0 bf                        bne     LBM_readAPage           ;no -> read next page of screen data
c5ae: 60                           rts                             ;yes: return!

                   • Clear variables
                   vZpDestCursPalette .var $88    {addr/1}         ;color palette # for the destination cursor
                   vZpDestCursTileId .var  $91    {addr/1}         ;tile id for the move/copy destination cursor sprite
                   vZpDestStateOn  .var    $ae    {addr/1}         ;true if we're the destination cursor is active
                   vZpAmMove       .var    $af    {addr/1}         ;flag var for whether we're MOVE or COPY

                   BgtMvCpy_GoToModeSelect
c5af: a9 00                        lda     #$00
c5b1: 85 ae                        sta     vZpDestStateOn          ;turn off dest mode
c5b3: a9 f8                        lda     #$f8
c5b5: 8d 10 02                     sta     oamBuffer+16            ;move dest cursor sprite offscreen
c5b8: 4c 79 c0                     jmp     BgtModeSelectMenu       ;go to mode-select menu

c5bb: a9 02        BgtMode_MOVE    lda     #$02
c5bd: ae                           ldx ▼   $01a9
c5be: a9 01        BgtMode_COPY    lda     #$01
c5c0: 85 7c                        sta     zpBgGotFlag             ;(unused)
c5c2: aa                           tax
c5c3: ca                           dex
c5c4: 86 af                        stx     vZpAmMove               ;set flag for whether we're MOVE or COPY
c5c6: 20 08 c1                     jsr     BgtRestoreTilesBehindMenu ;restore the tiles that were behind the mode-select menu
c5c9: 20 c9 af                     jsr     CmdFn_SPRITE_ON         ;turn on sprites
c5cc: a9 00                        lda     #$00
c5ce: 85 ae                        sta     vZpDestStateOn          ;we're in "source" state
c5d0: 20 78 c2                     jsr     BgtPrintCurrentMode     ;update display of what MODE this is
                   BgtMvCpy_KbdPrompt
c5d3: 20 4f c1                     jsr     BgtUpdateCursorAndPrintPos ;redraw cursor sprite and coords
c5d6: 20 49 c6                     jsr     BgtCpyMvUpdateDestCursor ;update/redraw destination cursor sprite
c5d9: a9 20                        lda     #key_Space
c5db: 20 25 c0                     jsr     BgtSetCursorAndReadChar ;prompt for a char (use a blank prompt char (no flashing))
c5de: 20 2f c0                     jsr     BgtHandleCursorKeys     ;handle arrow keys
c5e1: c9 1b                        cmp     #key_Escape             ;handle other keys:
c5e3: f0 ca                        beq     BgtMvCpy_GoToModeSelect
c5e5: c9 12                        cmp     #key_Insert
c5e7: f0 30                        beq     BgtCpyMv_PickupTile
c5e9: c9 08                        cmp     #key_Del
c5eb: f0 02                        beq     BgtCpyMv_DropTile
c5ed: d0 e4                        bne     BgtMvCpy_KbdPrompt      ;unrecognized key? -> reprompt

                   BgtCpyMv_DropTile
c5ef: a5 ae                        lda     vZpDestStateOn          ;in "destination" state?
c5f1: f0 e0                        beq     BgtMvCpy_KbdPrompt      ;no -> nothing to do; reprompt
c5f3: 20 56 b6                     jsr     SaveCursorPos           ;save global cursor pos
c5f6: 20 1a c0                     jsr     BgtCursorToGlobalCursor ;bgt cursor pos to global cursor pos
c5f9: 20 f4 b4                     jsr     CalcCursorTilemapPos    ;get VRAM addr for cursor location
c5fc: a5 91                        lda     vZpDestCursTileId
c5fe: 85 73                        sta     zpCharUnderCurs
c600: a9 09                        lda     #NC_RestoreCharAtCursor
c602: 20 6a 89                     jsr     WaitForNmiSubcommand    ;write the source tile's ID to the current tile position
c605: a5 88                        lda     vZpDestCursPalette
c607: 85 9c                        sta     zpPaletteNum
c609: 20 ff c4                     jsr     COLOR_SetPalette        ;write the source tile's palette to the current tile quad
c60c: 20 5f b6                     jsr     RestoreCursorPos        ;restore global cursor pos
c60f: a5 af                        lda     vZpAmMove               ;are we MOVE?
c611: f0 04                        beq     @reprompt               ;no -> just reprompt then
c613: a9 00                        lda     #$00
c615: 85 ae                        sta     vZpDestStateOn          ;MOVE completed, turn off "destination" state
c617: f0 ba        @reprompt       beq     BgtMvCpy_KbdPrompt

                   BgtCpyMv_PickupTile
c619: 20 56 b6                     jsr     SaveCursorPos           ;save global cursor
c61c: 20 1a c0                     jsr     BgtCursorToGlobalCursor ;copy bgt cursor to global cursor pos
c61f: 20 f4 b4                     jsr     CalcCursorTilemapPos    ;and calc the VRAM addr for that spot
c622: a9 05                        lda     #NC_GetCharUnderCursor
c624: 20 6a 89                     jsr     WaitForNmiSubcommand    ;get the tile that's under the cursor
c627: a5 73                        lda     zpCharUnderCurs
c629: 85 91                        sta     vZpDestCursTileId       ;and copy it as the destination cursor sprite graphic
c62b: a5 af                        lda     vZpAmMove               ;are we MOVE?
c62d: f0 09                        beq     @amCopy                 ;no, copy -> skip erasing existing tile
c62f: a9 20                        lda     #key_Space              ;yes, move
c631: 85 73                        sta     zpCharUnderCurs
c633: a9 09                        lda     #NC_RestoreCharAtCursor
c635: 20 6a 89                     jsr     WaitForNmiSubcommand    ;write space char to current tile pos (erasing existing)
c638: a9 ff        @amCopy         lda     #$ff
c63a: 85 ae                        sta     vZpDestStateOn          ;turn on destination state
c63c: 20 b0 c4                     jsr     GetTileQuadPaletteNum   ;copy tile's palette to dest cursor palette
c63f: a5 9c                        lda     zpPaletteNum
c641: 85 88                        sta     vZpDestCursPalette
c643: 20 5f b6                     jsr     RestoreCursorPos        ;restore global cursor
c646: 4c d3 c5                     jmp     BgtMvCpy_KbdPrompt      ;-> go back to COPY/MOVE input prompt

                   BgtCpyMvUpdateDestCursor
c649: a5 ae                        lda     vZpDestStateOn          ;are we in "source" state?
c64b: f0 28                        beq     BgtCpyMvHideDestCursor  ;yes -> hide the dest cursor sprite
c64d: a5 91                        lda     vZpDestCursTileId
c64f: 8d 11 02                     sta     oamSprTileId+16
c652: a5 88                        lda     vZpDestCursPalette
c654: 29 0f                        and     #$0f                    ;get the sprite palette for dest cursor
c656: 8d 12 02                     sta     oamSprAttr+16           ;and set for dest cursor sprite
c659: ad 05 05                     lda     bgtCH                   ;get bgt cursor x pos
c65c: 18                           clc
c65d: 69 02                        adc     #HMARGIN                ;add 2 for the margin
c65f: 0a                           asl     A                       ;times 8 (tiles -> pixels)
c660: 0a                           asl     A
c661: 0a                           asl     A
c662: 8d 13 02                     sta     oamSprXPos+16           ;save as dest cursor x pos
c665: ad 06 05                     lda     bgtCV                   ;get bgt cursor y pos
c668: 18                           clc
c669: 69 03                        adc     #VMARGIN                ;add 3 for margin
c66b: 0a                           asl     A                       ;times 8 (tiles -> pixels)
c66c: 0a                           asl     A
c66d: 0a                           asl     A
c66e: 8d 10 02                     sta     oamSprYPos+16           ;save as dest cursor y pos
c671: ce 10 02                     dec     oamSprYPos+16           ;decrement by one (for framing?)
c674: 60                           rts                             ;-> done

                   BgtCpyMvHideDestCursor
c675: a9 f8                        lda     #248
c677: 8d 10 02                     sta     oamSprYPos+16           ;move dest sprite offscreen to hide it
c67a: 60                           rts

                   strSAVE_OR_LOAD_Q
c67b: 53 41 56 45+                 .zstr   “SAVE(S),LOAD(L)?”
c68c: 46 49 4c 45+ strFILE_NAME    .zstr   “FILE NAME"”

                   • Clear variables

c697: 20 08 c1     BgtMode_FILE    jsr     BgtRestoreTilesBehindMenu ;restore tiles from before menu display
c69a: 20 4c b7                     jsr     CarriageReturn
c69d: a9 16                        lda     #22
c69f: 85 49                        sta     zpCV                    ;LOCATE 0,22
c6a1: a9 1a                        lda     #key_CtrlZ
c6a3: 20 75 b5                     jsr     PrintOneChar            ;print Ctrl-Z, clearing to end of screen (rows 22,23)
c6a6: 20 96 ae                     jsr     BgtFile_GoToScreen0     ;activate SCREEN 0
c6a9: 20 20 b4                     jsr     InitPromptChars         ;restore normal text prompt characters
c6ac: 20 ab b4                     jsr     DoCLS                   ;clear screen 0
c6af: a9 7b                        lda     #<strSAVE_OR_LOAD_Q
c6b1: 85 52                        sta     zpOutputStr
c6b3: a9 c6                        lda     #>strSAVE_OR_LOAD_Q
c6b5: 85 53                        sta     zpOutputStr+1
c6b7: 20 5a 89                     jsr     PrintString             ;print "SAVE OR LOAD?"
c6ba: 20 20 bc                     jsr     ReadChar                ;prompt for keyboard char
c6bd: c9 53                        cmp     #‘S’
c6bf: f0 06                        beq     BgtFile_Save
c6c1: c9 4c                        cmp     #‘L’
c6c3: f0 0e                        beq     BgtFile_Load
c6c5: d0 09                        bne     bgtoolRestart_C6D0

c6c7: 20 75 b5     BgtFile_Save    jsr     PrintOneChar            ;echo the typed 'S'
c6ca: 20 df c6                     jsr     BgtFile_PromptFilename  ;prompt for the filename
c6cd: 20 0d c5                     jsr     SaveBackgroundMap       ;save the background out to tape
                   bgtoolRestart_C6D0
c6d0: 4c d1 bf                     jmp     CmdFn_BGTOOL            ;-> restart BGTOOL

c6d3: 20 75 b5     BgtFile_Load    jsr     PrintOneChar            ;echo the typed 'L'
c6d6: 20 df c6                     jsr     BgtFile_PromptFilename  ;prompt for the filename
c6d9: 20 65 c5                     jsr     LoadBackgroundMap       ;load the background from tape
c6dc: 4c d0 c6                     jmp     bgtoolRestart_C6D0      ;-> restart BGTOOL

                   BgtFile_PromptFilename
c6df: 20 6f b5                     jsr     DoCRLF                  ;cursor to start of next line
c6e2: a9 8c                        lda     #<strFILE_NAME
c6e4: 85 52                        sta     zpOutputStr
c6e6: a9 c6                        lda     #>strFILE_NAME
c6e8: 85 53                        sta     zpOutputStr+1
c6ea: 20 5a 89                     jsr     PrintString             ;print "FILENAME", followed by double-quote char
c6ed: 20 04 bd                     jsr     ReadLine                ;read line of input from user
c6f0: ad 00 05                     lda     lineBuffer
c6f3: c9 03                        cmp     #key_Stop               ;did we get STOP key?
c6f5: f0 0d                        beq     @abort                  ;yes -> abort load/save, restart BGTOOL in SELECT mode
c6f7: 20 a4 8b                     jsr     InitLineBufferScanning  ;point TXTPTR at lineBuffer
c6fa: a9 09                        lda     #$09                    ;lineBuffer includes "FILE NAME" in it;
c6fc: 85 09                        sta     zpTXTPTR                ; skip over that prompt (but not double-quote char)
c6fe: 20 52 9e                     jsr     GetFileNameArg          ;get the filename into the "cassette header" area of buffer
c701: 4c 9d 9e                     jmp     FileNameCpyToLineBuffer ;-> and copy that back to the start of lineBuffer (erasing "FILE NAME" prompt and dquot)

c704: 68           @abort          pla                             ;pull up caller subrtn's return address
c705: 68                           pla
c706: 4c d0 c6                     jmp     bgtoolRestart_C6D0      ;-> and restart BGTOOL

c709: 39 c7        tbl_MoveTables  .dd2    mtbl_SideProfiles       ;Mario
c70b: 39 c7                        .dd2    mtbl_SideProfiles       ;Lady
c70d: 45 c7                        .dd2    mtbl_StaticFacing       ;Fighter Fly
c70f: 4b c7                        .dd2    mtbl_EightDir           ;Achilles
c711: 51 c7                        .dd2    mtbl_Penguin            ;Penguin
c713: 3f c7                        .dd2    mtbl_TwoFrame           ;Fireball
c715: 4b c7                        .dd2    mtbl_EightDir           ;Car
c717: 45 c7                        .dd2    mtbl_StaticFacing       ;Spinner
c719: 57 c7                        .dd2    mtbl_Spaceships         ;Star Killer
c71b: 57 c7                        .dd2    mtbl_Spaceships         ;Starship
c71d: 45 c7                        .dd2    mtbl_StaticFacing       ;Explosion
c71f: 45 c7                        .dd2    mtbl_StaticFacing       ;Smiley
c721: 5d c7                        .dd2    mtbl_Laser              ;Laser
c723: 3f c7                        .dd2    mtbl_TwoFrame           ;Shell Creeper
c725: 45 c7                        .dd2    mtbl_StaticFacing       ;Side Stepper
c727: 3f c7                        .dd2    mtbl_TwoFrame           ;Nitpicker
                   tbl_ActorSprShtOffsets
c729: 00                           .dd1    0                       ;Mario
c72a: 1c                           .dd1    28                      ;Lady (added to tile IDs from ss_Mario)
c72b: 00                           .dd1    0                       ;Fighter Fly
c72c: 00                           .dd1    0                       ;Achilles
c72d: 00                           .dd1    0                       ;Penguin
c72e: 00                           .dd1    0                       ;Fireball
c72f: 38                           .dd1    56                      ;Car (added to tile IDs from ss_Achilles)
c730: 58                           .dd1    88                      ;Spinner (added to tile IDs from ss_FighterFly)
c731: 00                           .dd1    0                       ;Star Killer
c732: 0c                           .dd1    12                      ;Starship (added to tile IDs from ss_StarKiller)
c733: 78                           .dd1    120                     ;Explosion (added to tile IDs from ss_FighterFly)
c734: 20                           .dd1    32                      ;Smiley (added to tile IDs from ss_FighterFly)
c735: 00                           .dd1    0                       ;Laser
c736: 48                           .dd1    72                      ;Shell Creeper (added to tile IDs from ss_Fireball)
c737: 88                           .dd1    136                     ;Side Stepper (added to tile IDs from ss_FighterFly)
c738: 58                           .dd1    88                      ;Nitpicker (added to tile IDs from ss_Fireball)
                   ; Mario and Lady
                   mtbl_SideProfiles
c739: 63 c7                        .dd2    fftbl_SideProfiles      ;animation frame counts
c73b: 99 c7                        .dd2    ss_Mario
c73d: 63 c7                        .dd2    fftbl_SideProfiles      ;tile flips
                   ; Fireball, Shell Creeper, Nitpicker
c73f: 6c c7        mtbl_TwoFrame   .dd2    fftbl_NoFlipsTwoFrames  ;animation frame counts (always two)
c741: 05 c8                        .dd2    ss_Fireball
c743: 7e c7                        .dd2    fftbl_OnlyHFlips_TwoFOneSt ;tile flips (just horizontally)
                   ; Fighter Fly, Spinner, Explosion, Smiley, Side Stepper
                   mtbl_StaticFacing
c745: 6c c7                        .dd2    fftbl_NoFlipsTwoFrames  ;animation frame counts (always two)
c747: ab c7                        .dd2    ss_FighterFly
c749: 6c c7                        .dd2    fftbl_NoFlipsTwoFrames  ;tile flips (never flip)
                   ; Achilles and Car
c74b: 6c c7        mtbl_EightDir   .dd2    fftbl_NoFlipsTwoFrames  ;animation frame counts (always two)
c74d: bd c7                        .dd2    ss_Achilles
c74f: 75 c7                        .dd2    fftbl_AllFlipsNoAnim    ;tile flips (every direction)
                   ; Penguin
c751: 7e c7        mtbl_Penguin    .dd2    fftbl_OnlyHFlips_TwoFOneSt ;animation frame cnts (always two, except when stationary)
c753: cf c7                        .dd2    ss_Penguin
c755: 7e c7                        .dd2    fftbl_OnlyHFlips_TwoFOneSt ;tile flips (horizontal only)
                   ; Star Killer and Starship
c757: 75 c7        mtbl_Spaceships .dd2    fftbl_AllFlipsNoAnim    ;animation frame counts (always one: no animation!)
c759: e1 c7                        .dd2    ss_StarKiller
c75b: 75 c7                        .dd2    fftbl_AllFlipsNoAnim    ;tile flips (every direction)
                   ; Laser (identical to spaceships, except spritesheets)
c75d: 75 c7        mtbl_Laser      .dd2    fftbl_AllFlipsNoAnim    ;animation frame counts (always one: no animation!)
c75f: f3 c7                        .dd2    ss_Laser
c761: 75 c7                        .dd2    fftbl_AllFlipsNoAnim    ;tile flips (every direction)
                   ; The following four tables are pointed at by the first and last entries of the
                   ; mtbl_* tables above, and are indexed by the 9 possible sprite movement
                   ; directions.
                   ; 
                   ; The high nybbles represent the horizontal and vertical "flip" to use with
                   ; tiles in the actor's sprite sheets (bits 7 and 8 of the sprite object's sprite
                   ; attr flags (in sprAttrFlags).
                   ; 
                   ; The low nybble indicates how many frames of animation exist for that
                   ; facing/posture.
                   ; 
                   ; The structure of the mtbl_* vars allows mix-and-match between using the flip
                   ; bits of one row, and the number-of-frames from a different row. For instance,
                   ; both the spaceships, and the sprites I've dubbed "EightDir" (Car, Achilles),
                   ; can truly point in eight different directions (I'm not counting "stationary",
                   ; here), and both use three separate sprite sheets in total, flipping and
                   ; reordering the tiles of those three to make up the other five. So, they both
                   ; use the same row to specify their "flip" bits for the various orientations.
                   ; However, they differ in that Car and Achilles each have two frames of
                   ; animation for each facing, while the spaceships only have one. Therefore, they
                   ; use _different_ rows to specify the number of frames.
                   ; 
                   ; Using "fftbl_" as the prefix for these tables; "ff" for "flips-n'-frames".
                   ; 
                   ; fftbl_SideProfiles, the first of these, is used only by Mario and Lady, and is
                   ; used for both the flips, and the animation frame counts.
                   fftbl_SideProfiles
c763: 01 02 41 43+                 .bulk   $01,$02,$41,$43,$41,$02,$01,$03,$01
                   ; fftbl_NoFlipsTwoFrames: When used for animation frame counts, specifies two
                   ; frames. When used for tile flips, never flips any tiles. Both regardless fo
                   ; direction, including stationary.
                   fftbl_NoFlipsTwoFrames
c76c: 02 02 02 02+                 .bulk   $02,$02,$02,$02,$02,$02,$02,$02,$02
                   ; fftbl_AllFlipsNoAnim: flips in all directions, all sprite sheets
                   ; have one frame
                   fftbl_AllFlipsNoAnim
c775: 01 01 41 41+                 .bulk   $01,$01,$41,$41,$c1,$81,$81,$01,$01
                   ; fftbl_OnlyHFlips_TwoFOneSt: Only does horizontal flips. Two frames for all
                   ; animations, except no animation on "stationary" direction.
                   fftbl_OnlyHFlips_TwoFOneSt
c77e: 01 02 42 42+                 .bulk   $01,$02,$42,$42,$42,$02,$02,$02,$02
                   ; Translation table for direction -> deltaX, deltaY. Indexed by sprite's
                   ; movement direction (all actor types):
c787: 00           tbl_moveDeltaX  .dd1    0                       ;dir 0 (stationary)
c788: 00           tbl_moveDeltaY  .dd1    0
c789: 00                           .dd1    0                       ;dir 1 (directly upward)
c78a: fe                           .dd1    -2
c78b: 02                           .dd1    2                       ;dir 2 (up/right)
c78c: fe                           .dd1    -2
c78d: 02                           .dd1    2                       ;dir 3 (right)
c78e: 00                           .dd1    0
c78f: 02                           .dd1    2                       ;dir 4 (right/down)
c790: 02                           .dd1    2
c791: 00                           .dd1    0                       ;dir 5 (down)
c792: 02                           .dd1    2
c793: fe                           .dd1    -2                      ;dir 6 (left/down)
c794: 02                           .dd1    2
c795: fe                           .dd1    -2                      ;dir 7 (left)
c796: 00                           .dd1    0
c797: fe                           .dd1    -2                      ;dur 8 (left/up
c798: fe                           .dd1    -2
                   ; The ss_* tables below are sprite sheets, describing one 2x2 tilemap of a
                   ; sprite for each possible facing value for a "DEF MOVE"-defined sprite.
                   ; 
                   ; Most of these sheets are used for multiple actor (character) types, but the
                   ; sprites listed correspond to just one specific actor. The other actors will
                   ; add their value from tbl_ActorSprShtOffsets to each tile ID, to derive arrive
                   ; at their own sprite tiles.
                   ; 
                   ; If a given facing has multiple facing frames, these are typically derived by
                   ; adding 4 to each tile ID for each additional frame. However, for the up or
                   ; down animations for Penguin, Mario, and Lady, actor-specific code is used to
                   ; handle the second frame, and directly specify the pointer to the 2x2 tilemap
                   ; to be used (a horizontal mirror of the first frame).
c799: 37 c8        ss_Mario        .dd2    spr_MarioStat
c79b: 33 c8                        .dd2    spr_MarioClimb
c79d: 3f c8                        .dd2    spr_MarioJumpRt
c79f: 2f c8                        .dd2    spr_MarioRight
c7a1: 3f c8                        .dd2    spr_MarioJumpRt
c7a3: 33 c8                        .dd2    spr_MarioClimb
c7a5: 3b c8                        .dd2    spr_MarioJumpLt
c7a7: 2b c8                        .dd2    spr_MarioLeft
c7a9: 3b c8                        .dd2    spr_MarioJumpLt
c7ab: 47 c8        ss_FighterFly   .dd2    spr_FighterFly
c7ad: 47 c8                        .dd2    spr_FighterFly
c7af: 47 c8                        .dd2    spr_FighterFly
c7b1: 47 c8                        .dd2    spr_FighterFly
c7b3: 47 c8                        .dd2    spr_FighterFly
c7b5: 47 c8                        .dd2    spr_FighterFly
c7b7: 47 c8                        .dd2    spr_FighterFly
c7b9: 47 c8                        .dd2    spr_FighterFly
c7bb: 47 c8                        .dd2    spr_FighterFly
c7bd: 53 c8        ss_Achilles     .dd2    spr_AchillesLeft
c7bf: 4b c8                        .dd2    spr_AchillesUp
c7c1: 5f c8                        .dd2    spr_AchillesUpRt
c7c3: 57 c8                        .dd2    spr_AchillesRight
c7c5: 67 c8                        .dd2    spr_AchillesDownRt
c7c7: 4f c8                        .dd2    spr_AchillesDown
c7c9: 63 c8                        .dd2    spr_AchillesDownLt
c7cb: 53 c8                        .dd2    spr_AchillesLeft
c7cd: 5b c8                        .dd2    spr_AchillesUpLt
c7cf: 8b c8        ss_Penguin      .dd2    spr_PenguinDown
c7d1: 8f c8                        .dd2    spr_PenguinUp
c7d3: 97 c8                        .dd2    spr_PenguinRight
c7d5: 97 c8                        .dd2    spr_PenguinRight
c7d7: 97 c8                        .dd2    spr_PenguinRight
c7d9: 8b c8                        .dd2    spr_PenguinDown
c7db: 93 c8                        .dd2    spr_PenguinLeft
c7dd: 93 c8                        .dd2    spr_PenguinLeft
c7df: 93 c8                        .dd2    spr_PenguinLeft
c7e1: 6b c8        ss_StarKiller   .dd2    spr_StKillUp
c7e3: 6b c8                        .dd2    spr_StKillUp
c7e5: 7f c8                        .dd2    spr_StKillUpRt
c7e7: 77 c8                        .dd2    spr_StKillRight
c7e9: 87 c8                        .dd2    spr_StKillDownRt
c7eb: 6f c8                        .dd2    spr_StKillDown
c7ed: 83 c8                        .dd2    spr_StKillDownLt
c7ef: 73 c8                        .dd2    spr_StKillLeft
c7f1: 7b c8                        .dd2    spr_StKillUpLt
c7f3: 1f c8        ss_Laser        .dd2    spr_LaserUp
c7f5: 1f c8                        .dd2    spr_LaserUp
c7f7: 27 c8                        .dd2    spr_LaserUpLeft
c7f9: 23 c8                        .dd2    spr_LaserLeft
c7fb: 27 c8                        .dd2    spr_LaserUpLeft
c7fd: 1f c8                        .dd2    spr_LaserUp
c7ff: 27 c8                        .dd2    spr_LaserUpLeft
c801: 23 c8                        .dd2    spr_LaserLeft
c803: 27 c8                        .dd2    spr_LaserUpLeft
c805: 17 c8        ss_Fireball     .dd2    spr_FballLeft
c807: 17 c8                        .dd2    spr_FballLeft
c809: 1b c8                        .dd2    spr_FballRight
c80b: 1b c8                        .dd2    spr_FballRight
c80d: 1b c8                        .dd2    spr_FballRight
c80f: 17 c8                        .dd2    spr_FballLeft
c811: 17 c8                        .dd2    spr_FballLeft
c813: 17 c8                        .dd2    spr_FballLeft
c815: 17 c8                        .dd2    spr_FballLeft
                   ; spr_* quads below are sprite tilemaps; individual frames used in the sprite
                   ; sheets above. Given in decimal to faciliate correlation with the Family BASIC
                   ; manual (which gives sprite tiles in decimal).
c817: 70           spr_FballLeft   .dd1    112
c818: 71                           .dd1    113
c819: 72                           .dd1    114
c81a: 73                           .dd1    115
c81b: 71           spr_FballRight  .dd1    113
c81c: 70                           .dd1    112
c81d: 73                           .dd1    115
c81e: 72                           .dd1    114
c81f: d4           spr_LaserUp     .dd1    212
c820: ef                           .dd1    239
c821: ef                           .dd1    239
c822: ef                           .dd1    239
c823: d0           spr_LaserLeft   .dd1    208
c824: ef                           .dd1    239
c825: ef                           .dd1    239
c826: ef                           .dd1    239
c827: d2           spr_LaserUpLeft .dd1    210
c828: ef                           .dd1    239
c829: ef                           .dd1    239
c82a: ef                           .dd1    239
c82b: 00           spr_MarioLeft   .dd1    0
c82c: 01                           .dd1    1
c82d: 02                           .dd1    2
c82e: 03                           .dd1    3
c82f: 01           spr_MarioRight  .dd1    1
c830: 00                           .dd1    0
c831: 03                           .dd1    3
c832: 02                           .dd1    2
c833: 14           spr_MarioClimb  .dd1    20
c834: 15                           .dd1    21
c835: 16                           .dd1    22
c836: 17                           .dd1    23
c837: 18           spr_MarioStat   .dd1    24
c838: 19                           .dd1    25
c839: 1a                           .dd1    26
c83a: 1b                           .dd1    27
c83b: 0c           spr_MarioJumpLt .dd1    12
c83c: 0d                           .dd1    13
c83d: 0e                           .dd1    14
c83e: 0f                           .dd1    15
c83f: 0d           spr_MarioJumpRt .dd1    13
c840: 0c                           .dd1    12
c841: 0f                           .dd1    15
c842: 0e                           .dd1    14
c843: 15           spr_MarioClimb1 .dd1    21                      ;2nd frame of climbing up/down. Horizontal mirror of spr_MarioClimb
c844: 14                           .dd1    20
c845: 17                           .dd1    23
c846: 16                           .dd1    22
c847: 38           spr_FighterFly  .dd1    56
c848: 39                           .dd1    57
c849: 3a                           .dd1    58
c84a: 3b                           .dd1    59
c84b: 50           spr_AchillesUp  .dd1    80
c84c: 51                           .dd1    81
c84d: 52                           .dd1    82
c84e: 53                           .dd1    83
                   spr_AchillesDown
c84f: 52                           .dd1    82
c850: 53                           .dd1    83
c851: 50                           .dd1    80
c852: 51                           .dd1    81
                   spr_AchillesLeft
c853: 40                           .dd1    64
c854: 41                           .dd1    65
c855: 42                           .dd1    66
c856: 43                           .dd1    67
                   spr_AchillesRight
c857: 41                           .dd1    65
c858: 40                           .dd1    64
c859: 43                           .dd1    67
c85a: 42                           .dd1    66
                   spr_AchillesUpLt
c85b: 48                           .dd1    72
c85c: 49                           .dd1    73
c85d: 4a                           .dd1    74
c85e: 4b                           .dd1    75
                   spr_AchillesUpRt
c85f: 49                           .dd1    73
c860: 48                           .dd1    72
c861: 4b                           .dd1    75
c862: 4a                           .dd1    74
                   spr_AchillesDownLt
c863: 4a                           .dd1    74
c864: 4b                           .dd1    75
c865: 48                           .dd1    72
c866: 49                           .dd1    73
                   spr_AchillesDownRt
c867: 4b                           .dd1    75
c868: 4a                           .dd1    74
c869: 49                           .dd1    73
c86a: 48                           .dd1    72
c86b: a0           spr_StKillUp    .dd1    160
c86c: a1                           .dd1    161
c86d: a2                           .dd1    162
c86e: a3                           .dd1    163
c86f: a2           spr_StKillDown  .dd1    162
c870: a3                           .dd1    163
c871: a0                           .dd1    160
c872: a1                           .dd1    161
c873: 98           spr_StKillLeft  .dd1    152
c874: 99                           .dd1    153
c875: 9a                           .dd1    154
c876: 9b                           .dd1    155
c877: 99           spr_StKillRight .dd1    153
c878: 98                           .dd1    152
c879: 9b                           .dd1    155
c87a: 9a                           .dd1    154
c87b: 9c           spr_StKillUpLt  .dd1    156
c87c: 9d                           .dd1    157
c87d: 9e                           .dd1    158
c87e: 9f                           .dd1    159
c87f: 9d           spr_StKillUpRt  .dd1    157
c880: 9c                           .dd1    156
c881: 9f                           .dd1    159
c882: 9e                           .dd1    158
                   spr_StKillDownLt
c883: 9e                           .dd1    158
c884: 9f                           .dd1    159
c885: 9c                           .dd1    156
c886: 9d                           .dd1    157
                   spr_StKillDownRt
c887: 9f                           .dd1    159
c888: 9e                           .dd1    158
c889: 9d                           .dd1    157
c88a: 9c                           .dd1    156
c88b: 68           spr_PenguinDown .dd1    104
c88c: 69                           .dd1    105
c88d: 6a                           .dd1    106
c88e: 6b                           .dd1    107
c88f: 6c           spr_PenguinUp   .dd1    108
c890: 6d                           .dd1    109
c891: 6e                           .dd1    110
c892: 6f                           .dd1    111
c893: 60           spr_PenguinLeft .dd1    96
c894: 61                           .dd1    97
c895: 62                           .dd1    98
c896: 63                           .dd1    99
                   spr_PenguinRight
c897: 61                           .dd1    97
c898: 60                           .dd1    96
c899: 63                           .dd1    99
c89a: 62                           .dd1    98
c89b: 6d           spr_PenguinUp1  .dd1    109                     ;2nd frame of upward walk. Horizontal mirror of spr_PenguinUp.
c89c: 6c                           .dd1    108
c89d: 6f                           .dd1    111
c89e: 6e                           .dd1    110
                   spr_PenguinDown1
c89f: 69                           .dd1    105                     ;2nd frame of downward walk. Horizontal mirror of spr_PenguinDown.
c8a0: 68                           .dd1    104
c8a1: 6b                           .dd1    107
c8a2: 6a                           .dd1    106

                   AnimateAndToggleNextFrame
c8a3: 8a                           txa                             ;push X
c8a4: 48                           pha
c8a5: 98                           tya                             ;push Y
c8a6: 48                           pha
c8a7: 20 b4 c8                     jsr     Animate                 ;run the animations!
c8aa: 68                           pla
c8ab: a8                           tay                             ;pull Y
c8ac: 68                           pla
c8ad: aa                           tax                             ;pull X
c8ae: a9 00                        lda     #$00
c8b0: 85 ab                        sta     zpNmiAnimNxtFr          ;indicate next frame should do NMI subcmds, not animate
c8b2: 68                           pla
c8b3: 40                           rti                             ;return from interrupt

c8b4: a2 00        Animate         ldx     #$00
c8b6: 86 a9                        stx     zpNmiActorNum           ;start with sprite #0
c8b8: a9 08                        lda     #$08
c8ba: 85 5d                        sta     zpNmiAnimActCtr         ;start with assumption that all 8 sprites are animated
c8bc: a6 a9        @sprLoop        ldx     zpNmiActorNum
c8be: bd 45 06                     lda     sprAttrFlags,x
c8c1: 29 10                        and     #SpAtt_ANIMATE          ;is this sprite animated?
c8c3: d0 26                        bne     AnimateOne              ;yes -> handle
c8c5: c6 5d                        dec     zpNmiAnimActCtr         ;no: decrement animated sprites counter
c8c7: f0 0a                        beq     @disableAnims           ;# animated sprites == 0? -> disable animation flag
c8c9: e8                           inx                             ;(useless)
c8ca: e6 a9        AnimateCont     inc     zpNmiActorNum
c8cc: a5 a9                        lda     zpNmiActorNum
c8ce: c9 08                        cmp     #$08                    ;have we done all 8 sprites yet?
c8d0: d0 ea                        bne     @sprLoop                ;no -> go to next sprite
c8d2: 60                           rts                             ;return

c8d3: a9 00        @disableAnims   lda     #$00
c8d5: 85 ac                        sta     zpNmiAnimate
c8d7: 60                           rts

c8d8: bd c5 06     MoveExpired     lda     defMovSMotion,x         ;reset total movement remaining
c8db: 9d c3 06                     sta     defMovSMotionCtr,x
c8de: a6 a9                        ldx     zpNmiActorNum
c8e0: bd 45 06                     lda     sprAttrFlags,x
c8e3: 29 ef                        and     #$ef
c8e5: 9d 45 06                     sta     sprAttrFlags,x          ;stop animation for this sprite
c8e8: 4c ca c8                     jmp     AnimateCont             ;continue on to next sprite

                   ; Handle animation of one sprite
c8eb: a5 a9        AnimateOne      lda     zpNmiActorNum           ;sprite number...
c8ed: 0a                           asl     A
c8ee: 0a                           asl     A
c8ef: 0a                           asl     A                       ;...times eight...
c8f0: 85 a6                        sta     zpNmiDefMovIdx
c8f2: aa                           tax                             ;...gives offset to start of this sprite's defMovS* vars
c8f3: de c2 06                     dec     defMovSSpdCtr,x         ;decrement speed counter. Is it zero?
c8f6: d0 d2                        bne     AnimateCont             ;no -> skip animating this sprite this frame (throttle speed)
c8f8: bd c4 06                     lda     defMovSSpeed,x          ;yes: reset the counter to user-specified speed value
c8fb: 9d c2 06                     sta     defMovSSpdCtr,x
c8fe: de c3 06                     dec     defMovSMotionCtr,x      ;decrement movement counter. Is it zero?
c901: f0 d5                        beq     MoveExpired             ;yes -> reached max motion; stop animating this sprite
c903: bd c1 06                     lda     defMovSActorFrm,x       ;no: get MOVE actor id and frame number
c906: 29 0f                        and     #$0f                    ;mask for the actor id nybble (removing frame number)
c908: a8                           tay
c909: b9 29 c7                     lda     tbl_ActorSprShtOffsets,y
c90c: 85 dd                        sta     zpNmiSprShtOff
c90e: 98                           tya
c90f: 0a                           asl     A                       ;multiply actor id * 2, to get entry in tbl_MoveTables
c910: a8                           tay
c911: b9 09 c7                     lda     tbl_MoveTables,y        ;get the pointer entry into zpNmiCurMTbl
c914: 85 a7                        sta     zpNmiCurMTbl
c916: b9 0a c7                     lda     tbl_MoveTables+1,y
c919: 85 a8                        sta     zpNmiCurMTbl+1
c91b: bd c0 06                     lda     defMovSFacing,x         ;get the sprite's direction
c91e: 0a                           asl     A                       ; * 2 for pointer entry
c91f: a8                           tay
c920: b9 87 c7                     lda     tbl_moveDeltaX,y        ;look up sprite's horizontal motion
c923: 18                           clc
c924: 7d c6 06                     adc     defMovSPosX,x
c927: 9d c6 06                     sta     defMovSPosX,x           ;and add it to sprite's horizontal position (move the sprite)
c92a: 18                           clc
c92b: b9 88 c7                     lda     tbl_moveDeltaY,y        ;look up sprite's vertical motion
c92e: 7d c7 06                     adc     defMovSPosY,x
c931: 9d c7 06                     sta     defMovSPosY,x           ;and add it to sprite's vert position (move the sprite)
c934: a0 00                        ldy     #$00
c936: b1 a7                        lda     (zpNmiCurMTbl),y        ;get a pointer to this sprite's frame counts vector
c938: 85 d5                        sta     zpNmiCurFrCnts
c93a: c8                           iny
c93b: b1 a7                        lda     (zpNmiCurMTbl),y
c93d: 85 d6                        sta     zpNmiCurFrCnts+1
c93f: bd c0 06                     lda     defMovSFacing,x         ;get sprite's facing again
c942: a8                           tay
c943: b1 d5                        lda     (zpNmiCurFrCnts),y      ;use that to get frame count for cur facing's animation
c945: 29 0f                        and     #$0f                    ;mask out h/v tile flip info, we just want frame cnt
c947: 20 53 ad                     jsr     MulABy16                ;move low nybble to high
c94a: 29 ff                        and     #$ff                    ;(for comparison below?)
c94c: 85 d7                        sta     zpNmiNumFrames          ;and save
c94e: f0 20                        beq     AnimOne_skipAnim        ;(never branches! should've compared to #$01 first)
c950: bd c1 06                     lda     defMovSActorFrm,x       ;get the sprite's actor id and frame num
c953: 29 f0                        and     #$f0                    ;mask out the id, just want frm num (high nybble)
c955: 18                           clc
c956: 69 10                        adc     #$10                    ;increment frame count
c958: c5 d7                        cmp     zpNmiNumFrames          ;did we exceed maximum frame count for this facing?
c95a: d0 02                        bne     @noRst                  ;no -> don't reset
c95c: a9 00                        lda     #$00                    ;yes: reset frame num to zero
c95e: 48           @noRst          pha                             ;push frame number
c95f: a9 0f                        lda     #$0f
c961: 3d c1 06                     and     defMovSActorFrm,x
c964: 9d c1 06                     sta     defMovSActorFrm,x       ;mask out sprite's frame num, and save
c967: 68                           pla
c968: 48                           pha
c969: 1d c1 06                     ora     defMovSActorFrm,x
c96c: 9d c1 06                     sta     defMovSActorFrm,x       ;save our new frame num in sprite's info
c96f: 68                           pla
                   AnimOne_skipAnim
c970: 4a                           lsr     A
c971: 4a                           lsr     A                       ;Multiply by 4 and save in zpNmiNumFrames
c972: 85 d7                        sta     zpNmiNumFrames          ; which is now an offset to add to tile IDs, to get cur frame's tilemap
c974: a0 02                        ldy     #$02
c976: b1 a7                        lda     (zpNmiCurMTbl),y        ;fetch a pointer to the facings spritsheet for cur spr
c978: 85 d9                        sta     zpNmiCurSprSht
c97a: c8                           iny
c97b: b1 a7                        lda     (zpNmiCurMTbl),y
c97d: 85 da                        sta     zpNmiCurSprSht+1
c97f: bd c0 06                     lda     defMovSFacing,x         ;get facing
c982: 0a                           asl     A
c983: a8                           tay
c984: b1 d9                        lda     (zpNmiCurSprSht),y      ;index spritesheet by the facing
c986: 85 db                        sta     zpNmiSprTileMp          ;save pointer to the 2x2 tilemap
c988: c8                           iny
c989: b1 d9                        lda     (zpNmiCurSprSht),y
c98b: 85 dc                        sta     zpNmiSprTileMp+1
c98d: bd c0 06                     lda     defMovSFacing,x         ;get facing yet again
c990: c9 01                        cmp     #$01                    ;is it facing #1 (straight up)?
c992: f0 04                        beq     @oneOrFive
c994: c9 05                        cmp     #$05                    ;is it facing #1 (up) or #5 (down)?
c996: d0 50                        bne     AnimOne_ordinaryAnim    ;no -> handle normally
c998: bd c1 06     @oneOrFive      lda     defMovSActorFrm,x       ;yes: get actor id
c99b: 29 0f                        and     #$0f                    ;mask out frame num
c99d: f0 23                        beq     AnimOne_marioOrLady
c99f: c9 01                        cmp     #$01
c9a1: f0 1f                        beq     AnimOne_marioOrLady     ;id 0 (Mario) or 1 (Lady)? -> handle specially
c9a3: c9 04                        cmp     #$04
c9a5: d0 41                        bne     AnimOne_ordinaryAnim    ;NOT id 4 (penguin)? -> handle normally
c9a7: a5 d7                        lda     zpNmiNumFrames          ;(If we're here, we are the penguin)
c9a9: f0 33                        beq     AnimOne_frameZero       ;anim frame 0? -> no alterations
c9ab: bd c0 06                     lda     defMovSFacing,x         ;get facing
c9ae: c9 05                        cmp     #$05                    ;facing 5 (down)?
c9b0: f0 08                        beq     AnimOne_penguinDown     ;yes -> deal with that
c9b2: a9 9b                        lda     #<spr_PenguinUp1        ;no: we're facing 1 (up)
c9b4: 85 db                        sta     zpNmiSprTileMp          ;set the tilemap ptr value directly
c9b6: a9 c8                        lda     #>spr_PenguinUp1
c9b8: d0 12                        bne     AnimOne_setTMapHiByt

                   AnimOne_penguinDown
c9ba: a9 9f                        lda     #<spr_PenguinDown1      ;set the tilemap ptr value directly
c9bc: 85 db                        sta     zpNmiSprTileMp
c9be: a9 c8                        lda     #>spr_PenguinDown1
c9c0: d0 0a                        bne     AnimOne_setTMapHiByt

                   AnimOne_marioOrLady
c9c2: a5 d7                        lda     zpNmiNumFrames
c9c4: f0 18                        beq     AnimOne_frameZero       ;anim frame 0? -> no alterations
c9c6: a9 43                        lda     #<spr_MarioClimb1       ;2nd frame: use alternate (h. mirrored) tilemap
c9c8: 85 db                        sta     zpNmiSprTileMp          ;(doesn't matter if we're climbing up or down)
c9ca: a9 c8                        lda     #>spr_MarioClimb1
                   AnimOne_setTMapHiByt
c9cc: 85 dc                        sta     zpNmiSprTileMp+1
c9ce: a4 a9                        ldy     zpNmiActorNum
c9d0: a9 40                        lda     #$40                    ;ensure horizontal tile flipping is on for mirrored sprite
c9d2: 19 45 06                     ora     sprAttrFlags,y
c9d5: 99 45 06                     sta     sprAttrFlags,y
c9d8: a9 00                        lda     #$00                    ;clear tile ID offsets for animation frame; we took care of it
c9da: 85 d7                        sta     zpNmiNumFrames
c9dc: f0 0a                        beq     AnimOne_ordinaryAnim

                   AnimOne_frameZero
c9de: a4 a9                        ldy     zpNmiActorNum
c9e0: b9 45 06                     lda     sprAttrFlags,y
c9e3: 29 3f                        and     #$3f                    ;clear tile flipping (frame zero of up/down anim, so not mirrored)
c9e5: 99 45 06                     sta     sprAttrFlags,y
                   AnimOne_ordinaryAnim
c9e8: a5 a9                        lda     zpNmiActorNum
c9ea: 20 53 ad                     jsr     MulABy16                ;get index into DMA buffer for this sprite number
c9ed: aa                           tax
c9ee: a0 00                        ldy     #$00
c9f0: b1 db                        lda     (zpNmiSprTileMp),y      ;get tile 0 ID
c9f2: 18                           clc
c9f3: 65 dd                        adc     zpNmiSprShtOff          ;add offset for this actor (character)
c9f5: 65 d7                        adc     zpNmiNumFrames          ;and the offset for the anim frame
c9f7: 9d 81 02                     sta     oamBuffer+129,x         ;save to PPU DMA area
c9fa: c8                           iny
c9fb: b1 db                        lda     (zpNmiSprTileMp),y      ;..repeat for tile 1 ID...
c9fd: 18                           clc
c9fe: 65 dd                        adc     zpNmiSprShtOff
ca00: 65 d7                        adc     zpNmiNumFrames
ca02: 9d 85 02                     sta     oamBuffer+133,x
ca05: c8                           iny
ca06: b1 db                        lda     (zpNmiSprTileMp),y      ;..repeat for tile 2 ID...
ca08: 18                           clc
ca09: 65 dd                        adc     zpNmiSprShtOff
ca0b: 65 d7                        adc     zpNmiNumFrames
ca0d: 9d 89 02                     sta     oamBuffer+137,x
ca10: c8                           iny
ca11: b1 db                        lda     (zpNmiSprTileMp),y      ;..repeat for (final) tile 3 ID...
ca13: 18                           clc
ca14: 65 dd                        adc     zpNmiSprShtOff
ca16: 65 d7                        adc     zpNmiNumFrames
ca18: 9d 8d 02                     sta     oamBuffer+141,x
ca1b: a4 a9                        ldy     zpNmiActorNum
ca1d: b9 45 06                     lda     sprAttrFlags,y          ;get the sprite attrs
ca20: 9d 82 02                     sta     oamBuffer+130,x         ;and copy to each of the four sprite tiles
ca23: 9d 86 02                     sta     oamBuffer+134,x
ca26: 9d 8a 02                     sta     oamBuffer+138,x
ca29: 9d 8e 02                     sta     oamBuffer+142,x
ca2c: a4 a6                        ldy     zpNmiDefMovIdx
ca2e: b9 c6 06                     lda     defMovSPosX,y
ca31: 9d 83 02                     sta     oamBuffer+131,x         ;copy sprite X position to tile 0 X pos
ca34: 9d 8b 02                     sta     oamBuffer+139,x         ;and tile 2 X pos
ca37: 18                           clc
ca38: 69 08                        adc     #$08                    ;add 8
ca3a: 9d 87 02                     sta     oamBuffer+135,x         ;and save that as tile 1 X pos
ca3d: 9d 8f 02                     sta     oamBuffer+143,x         ;and tile 3
ca40: b9 c7 06                     lda     defMovSPosY,y
ca43: 9d 80 02                     sta     oamBuffer+128,x         ;copy sprite Y position to tile 0 Y pos
ca46: 9d 84 02                     sta     oamBuffer+132,x         ;and tile 2 Y pos
ca49: 18                           clc
ca4a: 69 08                        adc     #$08                    ;add 8
ca4c: 9d 88 02                     sta     oamBuffer+136,x         ;and save that as tile 1 Y pos
ca4f: 9d 8c 02                     sta     oamBuffer+140,x         ;and tile 3
ca52: 4c ca c8                     jmp     AnimateCont             ;Done with this sprite! On to the next.

ca55: 4c 94 84     errIllVal_CA55  jmp     ErrorIllegalValue

ca58: 4c 91 84     errSyn_CA58     jmp     ErrorSyntax

                   ; Not in jtbl_Commands: in reality, this is the "DEF" command when immediately
                   ; followed by the "MOVE" token. CmdFn_DEF jumps to here in that case.
                   vZpSprMvTbl     .var    $19    {addr/2}
                   vZpSprFacingFlipTbl .var $1b    {addr/2}
                   vZpSavedX       .var    $ad    {addr/1}

ca5b: 20 8c 90     CmdFn_DEFMOVE   jsr     TxtPtrIncr
ca5e: 20 7a a8                     jsr     SkipLParenOrSynErr      ;parse '('
ca61: 20 6e a2                     jsr     EvalByteInteger         ;get sprite #
ca64: 20 44 af                     jsr     ErrUnlessZeroThruSeven  ;demand it be less than 8
ca67: 20 7d a8                     jsr     SkipRParenOrSynErr      ;parse ')'
ca6a: ad 00 04                     lda     exprParseStk            ;sprite #
ca6d: aa                           tax
ca6e: a9 e7                        lda     #$e7
ca70: 3d 45 06                     and     sprAttrFlags,x
ca73: 9d 45 06                     sta     sprAttrFlags,x          ;clear sprite attr flags SpAtt_ANIMATE and SpAtt_DEFMOVE
ca76: 8a                           txa
ca77: 85 a4                        sta     zpActorNum              ;store sprite number
ca79: 0a                           asl     A
ca7a: 0a                           asl     A
ca7b: 0a                           asl     A
ca7c: 85 a5                        sta     zpDefMovIdx             ;store offset for defMovS* vars
ca7e: 20 d9 86                     jsr     SkipEqualsOrErr         ;parse '='
ca81: 20 bf 90                     jsr     TxtSkipSpaces
ca84: c9 8a                        cmp     #tok_SPRITE             ;parse 'SPRITE'
ca86: d0 d0                        bne     errSyn_CA58             ;(or error)
ca88: 20 8c 90                     jsr     TxtPtrIncr
ca8b: 20 7a a8                     jsr     SkipLParenOrSynErr      ;parse '('
ca8e: 20 7d cb                     jsr     DefMvEvalFirstArg       ;get first arg after SPRITE
ca91: c9 10                        cmp     #$10
ca93: b0 c0                        bcs     errIllVal_CA55          ;>= 16? -> error
ca95: 9d c1 06                     sta     defMovSActorFrm,x       ;first arg is charactor/actor id
ca98: 20 75 cb                     jsr     DefMvEvalNextArg        ;2nd arg
ca9b: 86 ad                        stx     vZpSavedX               ;save X away (...isn't it already saved in zpDefMovIdx?)
ca9d: 85 28                        sta     zpWAccum                ;save 2nd arg to WAccum
ca9f: c9 09                        cmp     #$09                    ;<= 8?
caa1: 90 13                        bcc     @argFits                ;yes -> leave arg val alone
caa3: a9 08                        lda     #$08
caa5: 85 2c                        sta     zpWParam
caa7: a9 00                        lda     #$00
caa9: 85 29                        sta     zpWAccum+1
caab: 85 2d                        sta     zpWParam+1
caad: 20 0e a8                     jsr     ModAndReplaceExpr       ;get value of (arg MOD 8)
cab0: a5 28                        lda     zpWAccum
cab2: d0 02                        bne     @argFits
cab4: e6 28                        inc     zpWAccum                ;add one if MOD gave zero (forcing to 1..7)
cab6: a5 28        @argFits        lda     zpWAccum
cab8: a6 ad                        ldx     vZpSavedX
caba: 9d c0 06                     sta     defMovSFacing,x         ;save (possibly force-fitted) 2nd arg as facing
cabd: bd c1 06                     lda     defMovSActorFrm,x       ;get character/actor
cac0: 0a                           asl     A                       ;multiply times 2
cac1: a8                           tay
cac2: b9 09 c7                     lda     tbl_MoveTables,y        ;Multiple indirections! We're doing something like:
cac5: 85 19                        sta     vZpSprMvTbl             ; moveTables[actor id]->[2]->[facing]
cac7: b9 0a c7                     lda     tbl_MoveTables+1,y      ;index tbl_MoveTables by actor id
caca: 85 1a                        sta     vZpSprMvTbl+1           ; to get movement tables for this actor
cacc: a0 04                        ldy     #$04                    ;indicate actor-specific table by [2] (bytes #4 and #5)
cace: b1 19                        lda     (vZpSprMvTbl),y
cad0: 85 1b                        sta     vZpSprFacingFlipTbl
cad2: c8                           iny
cad3: b1 19                        lda     (vZpSprMvTbl),y
cad5: 85 1c                        sta     vZpSprFacingFlipTbl+1
cad7: bd c0 06                     lda     defMovSFacing,x         ;get the movement direction
cada: a8                           tay
cadb: b1 1b                        lda     (vZpSprFacingFlipTbl),y ;and index by that
cadd: 29 f0                        and     #$f0                    ;mask to obtain just horizontal and vertical flip
cadf: a4 a4                        ldy     zpActorNum
cae1: 48                           pha
cae2: b9 45 06                     lda     sprAttrFlags,y          ;clear horizontal and vertical flip in sprite's flags
cae5: 29 3f                        and     #$3f
cae7: 99 45 06                     sta     sprAttrFlags,y
caea: 68                           pla
caeb: 19 45 06                     ora     sprAttrFlags,y          ;and insert the ones we got from the table lookups
caee: 99 45 06                     sta     sprAttrFlags,y
caf1: 20 84 cb                     jsr     DefMvMaybeParseRParen   ;are we at ')'?
caf4: b0 71                        bcs     DefMvFinish             ;yes -> finish up
caf6: 20 75 cb                     jsr     DefMvEvalNextArg        ;no: get 3rd arg (speed)
caf9: b0 0b                        bcs     @arg4                   ;empty arg (comma)? -> skip this one
cafb: 9d c2 06                     sta     defMovSSpdCtr,x         ;set frames-til-move
cafe: 9d c4 06                     sta     defMovSSpeed,x          ;and initializer for same
cb01: 20 84 cb                     jsr     DefMvMaybeParseRParen   ;are we at ')'?
cb04: b0 61                        bcs     DefMvFinish             ;yes -> finish up
cb06: 20 75 cb     @arg4           jsr     DefMvEvalNextArg        ;no: get 4th arg (movement distance)
cb09: b0 0d                        bcs     @arg5                   ;empty arg (comma)? -> skip this one
cb0b: 69 01                        adc     #$01                    ;increment
cb0d: 9d c3 06                     sta     defMovSMotionCtr,x      ;set as how far we have left to move
cb10: 9d c5 06                     sta     defMovSMotion,x         ;and initializer for same
cb13: 20 84 cb                     jsr     DefMvMaybeParseRParen   ;are we at ')'?
cb16: b0 4f                        bcs     DefMvFinish             ;yes -> finish up
cb18: 20 75 cb     @arg5           jsr     DefMvEvalNextArg        ;no: get 5th arg (display priority)
cb1b: b0 24                        bcs     @arg6                   ;empty arg (comma)? -> skip this one
cb1d: a0 00                        ldy     #$00                    ;"fore" (empty) flag
cb1f: ad 00 04                     lda     exprParseStk
cb22: 20 8d b0                     jsr     ErrUnlessZeroOrOne
cb25: 29 ff                        and     #$ff
cb27: f0 02                        beq     @fore                   ;zero? -> skip next instr
cb29: a0 20                        ldy     #SpAtt_BEHIND           ;"behind" flag
cb2b: a6 a4        @fore           ldx     zpActorNum
cb2d: bd 45 06                     lda     sprAttrFlags,x
cb30: 29 df                        and     #$df                    ;clear fore/behind flag in sprite attr flags
cb32: 9d 45 06                     sta     sprAttrFlags,x
cb35: 98                           tya
cb36: 1d 45 06                     ora     sprAttrFlags,x          ;store new fore/behind flag in sprite attr flags
cb39: 9d 45 06                     sta     sprAttrFlags,x
cb3c: 20 84 cb                     jsr     DefMvMaybeParseRParen   ;are we at ')'?
cb3f: b0 26                        bcs     DefMvFinish             ;yes -> finish up
cb41: 20 75 cb     @arg6           jsr     DefMvEvalNextArg        ;no: get 6th arg (color palette #)
cb44: c9 04                        cmp     #$04
cb46: b0 2a                        bcs     errIllVal_CB72          ;-> error if arg >= 4
cb48: a6 a4                        ldx     zpActorNum
cb4a: 48                           pha                             ;push arg
cb4b: bd 45 06                     lda     sprAttrFlags,x
cb4e: 29 fc                        and     #$fc
cb50: 9d 45 06                     sta     sprAttrFlags,x          ;clear palette info from sprite attr flags
cb53: 68                           pla                             ;pull arg
cb54: 1d 45 06                     ora     sprAttrFlags,x          ;combine with other attr flags
cb57: 9d 45 06                     sta     sprAttrFlags,x          ;set combined flags
cb5a: 20 7d a8                     jsr     SkipRParenOrSynErr      ;must end with ')' here
cb5d: a9 78                        lda     #120
cb5f: a6 a5                        ldx     zpDefMovIdx
cb61: 9d c6 06                     sta     defMovSPosX,x           ;center sprite in the middle of the screen
cb64: 9d c7 06                     sta     defMovSPosY,x           ;(only if all 6 SPRITE arguments were provided)
cb67: a6 a4        DefMvFinish     ldx     zpActorNum
cb69: bd 45 06                     lda     sprAttrFlags,x
cb6c: 09 08                        ora     #SpAtt_DEFMOVE          ;indicate DEF MOVE completed
cb6e: 9d 45 06                     sta     sprAttrFlags,x
cb71: 60                           rts

cb72: 4c 94 84     errIllVal_CB72  jmp     ErrorIllegalValue

                   DefMvEvalNextArg
cb75: 20 14 ac                     jsr     SkipCommaOrSynErr       ;parse comma
cb78: 20 92 cb                     jsr     DefMvSecIfEmptyArg      ;is it an empty arg?
cb7b: b0 13                        bcs     DefMvSecAndRts          ;yes -> return with carry set
                   DefMvEvalFirstArg
cb7d: 20 6e a2                     jsr     EvalByteInteger         ;no: parse byte arg
cb80: a6 a5                        ldx     zpDefMovIdx             ;reload x
cb82: 18                           clc                             ;and return with carry unset
cb83: 60                           rts

                   DefMvMaybeParseRParen
cb84: 20 bf 90                     jsr     TxtSkipSpaces
cb87: c9 29                        cmp     #‘)’                    ;did we get a ')'?
cb89: f0 02                        beq     DefMvAdvTxtPtrAndSetCarry ;yes -> move past it, set carry
cb8b: 18                           clc                             ;no: clear carry
cb8c: 60                           rts                             ;return

                   DefMvAdvTxtPtrAndSetCarry
cb8d: 20 8c 90                     jsr     TxtPtrIncr
cb90: 38           DefMvSecAndRts  sec
cb91: 60                           rts

                   DefMvSecIfEmptyArg
cb92: 20 bf 90                     jsr     TxtSkipSpaces
cb95: c9 2c                        cmp     #‘,’
cb97: f0 f7                        beq     DefMvSecAndRts
cb99: 18                           clc
cb9a: 60                           rts

                   • Clear variables

cb9b: 20 6e a2     CmdFn_POSITION  jsr     EvalByteInteger         ;get first arg
cb9e: 20 44 af                     jsr     ErrUnlessZeroThruSeven  ;ensure 0-7
cba1: 0a                           asl     A                       ;multiply by 8
cba2: 0a                           asl     A
cba3: 0a                           asl     A
cba4: 85 a5                        sta     zpDefMovIdx
cba6: 20 75 cb                     jsr     DefMvEvalNextArg        ;get next arg
cba9: 9d c6 06                     sta     defMovSPosX,x           ;set actor's new X pos (in its DEF MOVE data)
cbac: 20 75 cb                     jsr     DefMvEvalNextArg
cbaf: 9d c7 06                     sta     defMovSPosY,x           ;set actor's new Y pos (in its DEF MOVE data)
cbb2: 60                           rts                             ;return

cbb3: 20 6e a2     CmdFn_MOVE      jsr     EvalByteInteger         ;evaluate argument
cbb6: 20 44 af                     jsr     ErrUnlessZeroThruSeven  ;ensure it's 0 thru 7
cbb9: aa                           tax
cbba: bd 45 06                     lda     sprAttrFlags,x          ;load sprite flags
cbbd: 29 08                        and     #SpAtt_DEFMOVE          ;check if DEF MOVE was defined for this sprite
cbbf: f0 08                        beq     @skipArg                ;no -> do nothing for this arg
cbc1: a9 10                        lda     #SpAtt_ANIMATE
cbc3: 1d 45 06                     ora     sprAttrFlags,x
cbc6: 9d 45 06                     sta     sprAttrFlags,x          ;set the SpAtt_ANIMATE flag for this move;
cbc9: 20 9c 84     @skipArg        jsr     IsEndOfCmd              ;NMI handler will start animating it
cbcc: f0 06                        beq     @animsOn                ;end of command? -> close up
cbce: 20 14 ac                     jsr     SkipCommaOrSynErr       ;otherwise, we should have a comma here. Skip it.
cbd1: 4c b3 cb                     jmp     CmdFn_MOVE              ;and evaluate next arg

cbd4: a9 ff        @animsOn        lda     #$ff
cbd6: 85 ac                        sta     zpNmiAnimate            ;tell NMI hanlder there are animations to perform
cbd8: 60           rts_CBD8        rts

                   vZpEraOrCut     .var    $75    {addr/1}         ;#$FF if ERA; #$00 if CUT

cbd9: a9 ff        CmdFn_ERA       lda     #$ff                    ;ERA mode
cbdb: ae                           ldx ▼   a:$00a9                 ;(disable next instr)
cbdc: a9 00        CmdFn_CUT       lda     #$00                    ;CUT mode
cbde: 85 75                        sta     vZpEraOrCut             ;save mode
cbe0: 20 6e a2                     jsr     EvalByteInteger         ;eval arg
cbe3: 20 44 af                     jsr     ErrUnlessZeroThruSeven  ;ensure it's 0-7
cbe6: a8                           tay
cbe7: b9 45 06                     lda     sprAttrFlags,y
cbea: 29 ef                        and     #$ef
cbec: 99 45 06                     sta     sprAttrFlags,y          ;disable animation for the sprite
cbef: a5 75                        lda     vZpEraOrCut             ;check ERA or CUT
cbf1: f0 03                        beq     @skipEra                ;CUT? -> skip erase
cbf3: 20 97 cc                     jsr     EraseSprite
cbf6: 20 9c 84     @skipEra        jsr     IsEndOfCmd              ;any other args?
cbf9: f0 dd                        beq     rts_CBD8                ;no -> done
cbfb: 20 14 ac                     jsr     SkipCommaOrSynErr       ;skip a comma
cbfe: a5 75                        lda     vZpEraOrCut
cc00: f0 da                        beq     CmdFn_CUT               ;and loop to start of command again
cc02: d0 d5                        bne     CmdFn_ERA

                   • Clear variables
                   vZpActorNum     .var    $74    {addr/1}

cc04: 20 4e a9     FnFn_CRASH      jsr     EvalParenZeroThruSevenOrError ;parse left paren, and actor number
cc07: a5 28                        lda     zpWAccum                ;<- UNNECESSARY, we already have the value in both A reg and X reg.
cc09: 85 74                        sta     vZpActorNum             ;save actor num aside
cc0b: bd 45 06                     lda     sprAttrFlags,x
cc0e: 29 08                        and     #SpAtt_DEFMOVE          ;does actor has a DEF MOVE set?
cc10: f0 43                        beq     CRASH_retMinusTwo       ;no -> return -2 (as mandated by the manual)
cc12: 20 44 a9                     jsr     GetActorDefMoveIdx      ;multiply by 8 to get index to DEF MOVE data
cc15: bd c6 06                     lda     defMovSPosX,x
cc18: 85 6b                        sta     zpSavedCH               ;grab and save actor's X pos
cc1a: bd c7 06                     lda     defMovSPosY,x
cc1d: 85 6c                        sta     zpSavedCV               ;grab and save actor's Y pos
cc1f: a9 00                        lda     #$00
cc21: 85 28                        sta     zpWAccum                ;set current actor-to-check to actor #0
cc23: a0 08                        ldy     #NUM_ACTORS             ;set Y reg to num actors (for count-down)
cc25: a5 74        CRASH_actorLoop lda     vZpActorNum
cc27: c5 28                        cmp     zpWAccum                ;is the requested actor # same as one we're checking?
cc29: f0 1c                        beq     CRASH_nextActor         ;yes -> skip this one (can't "crash" with self)
cc2b: 20 44 a9                     jsr     GetActorDefMoveIdx      ;else, multiply check-actor # by 8 to get index
cc2e: bd c6 06                     lda     defMovSPosX,x           ;get X pos
cc31: 38                           sec
cc32: e5 6b                        sbc     zpSavedCH               ;subtract user-specified actor's X pos from that
cc34: 20 5d cc                     jsr     CRASH_cmpAbs16          ;compare absolute value with 16
cc37: b0 0e                        bcs     CRASH_nextActor         ; >= 16? -> no collision. Next actor
cc39: bd c7 06                     lda     defMovSPosY,x           ;get Y pos of check-actor
cc3c: 38                           sec
cc3d: e5 6c                        sbc     zpSavedCV               ;subtract our actor's Y pos
cc3f: 20 5d cc                     jsr     CRASH_cmpAbs16          ;compare absolute value with 16
cc42: b0 03                        bcs     CRASH_nextActor         ; >= 16? -> no collision, check next actor
cc44: 4c 7e a9                     jmp     SkipRParenSetParseStkInt ;-> collision! Return this actor's number.

cc47: e6 28        CRASH_nextActor inc     zpWAccum                ;incr actor-to-check's number
cc49: 88                           dey                             ;decr Y (count-down # actors)
cc4a: d0 d9                        bne     CRASH_actorLoop         ;actors remaining to check? -> check next
cc4c: a9 ff                        lda     #$ff                    ;set WAccum to -1
cc4e: 85 28                        sta     zpWAccum
cc50: 85 29        @retMinus       sta     zpWAccum+1              ;set WAccum high byte
cc52: 4c 7e a9                     jmp     SkipRParenSetParseStkInt ;parse closing paren, return WAccum via expr parse stack

                   CRASH_retMinusTwo
cc55: a9 fe                        lda     #$fe                    ;set WAccum to -2
cc57: 85 28                        sta     zpWAccum
cc59: a9 ff                        lda     #$ff
cc5b: d0 f3                        bne     @retMinus               ;(always) -> parse closing paren, and return WAccum

cc5d: b0 0a        CRASH_cmpAbs16  bcs     @pos                    ;positive difference? -> skip negation
cc5f: 85 75                        sta     zpHeldKeyChar           ;save original difference
cc61: a9 ff                        lda     #-1                     ;<- could've set this to zero, and skipped the clc/adc at end
cc63: 38                           sec
cc64: e5 75                        sbc     zpHeldKeyChar           ;subtract difference from -1
cc66: 18                           clc
cc67: 69 01                        adc     #$01                    ;add zero so it's subtraction from zero (straight negation)
cc69: c9 10        @pos            cmp     #$10                    ;have positive. Cmp with 16, and return
cc6b: 60           rts_CC6B        rts

                   • Clear variables

cc6c: 20 6e a2     CmdFn_CAN       jsr     EvalByteInteger         ;get actor # arg
cc6f: 20 44 af                     jsr     ErrUnlessZeroThruSeven  ;ensure < 8
cc72: 48                           pha                             ;push actor num
cc73: a8                           tay
cc74: a9 00                        lda     #$00
cc76: 99 45 06                     sta     sprAttrFlags,y          ;clear actor's sprite flags (including animation)
cc79: 20 97 cc                     jsr     EraseSprite             ;erase the sprite from screen
cc7c: 68                           pla                             ;pull actor # again
cc7d: 0a                           asl     A
cc7e: 0a                           asl     A
cc7f: 0a                           asl     A                       ;multiply by 8, for indexing
cc80: aa                           tax
cc81: a9 00                        lda     #$00
cc83: 9d c0 06                     sta     defMovSFacing,x         ;clear facing and position
cc86: 9d c6 06                     sta     defMovSPosX,x
cc89: 9d c7 06                     sta     defMovSPosY,x
cc8c: 20 9c 84                     jsr     IsEndOfCmd              ;more args?
cc8f: f0 da                        beq     rts_CC6B                ;no -> done!
cc91: 20 14 ac                     jsr     SkipCommaOrSynErr       ;more args: ensure comma first.
cc94: 4c 6c cc                     jmp     CmdFn_CAN               ; then loop back to the top!

cc97: 98           EraseSprite     tya
cc98: 20 53 ad                     jsr     MulABy16
cc9b: a8                           tay
cc9c: a9 f8                        lda     #$f8
cc9e: 99 80 02                     sta     oamBuffer+128,y         ;move the actor's sprites off-screen so they're hidden
cca1: 99 84 02                     sta     oamBuffer+132,y
cca4: 99 88 02                     sta     oamBuffer+136,y
cca7: 99 8c 02                     sta     oamBuffer+140,y
ccaa: 60                           rts

                   tbl_KeywordTokens
ccab: 80                           .dd1    $80
ccac: 47 4f 54 4f                  .str    “GOTO”
ccb0: 81                           .dd1    $81
ccb1: 47 4f 53 55+                 .str    “GOSUB”
ccb6: 82                           .dd1    $82
ccb7: 52 55 4e                     .str    “RUN”
ccba: 83                           .dd1    $83
ccbb: 52 45 54 55+                 .str    “RETURN”
ccc1: 84                           .dd1    $84
ccc2: 52 45 53 54+                 .str    “RESTORE”
ccc9: 85                           .dd1    $85
ccca: 54 48 45 4e                  .str    “THEN”
ccce: 86                           .dd1    $86
cccf: 4c 49 53 54                  .str    “LIST”
ccd3: 87                           .dd1    $87
ccd4: 53 59 53 54+                 .str    “SYSTEM”                ;all commands above this line take line number arguments
ccda: 88                           .dd1    $88
ccdb: 54 4f                        .str    “TO”
ccdd: 89                           .dd1    $89
ccde: 53 54 45 50                  .str    “STEP”
cce2: 8a                           .dd1    $8a
cce3: 53 50 52 49+                 .str    “SPRITE”
cce9: 8b                           .dd1    $8b
ccea: 50 52 49 4e+                 .str    “PRINT”
ccef: 8c                           .dd1    $8c
ccf0: 46 4f 52                     .str    “FOR”
ccf3: 8d                           .dd1    $8d
ccf4: 4e 45 58 54                  .str    “NEXT”
ccf8: 8e                           .dd1    $8e
ccf9: 50 41 55 53+                 .str    “PAUSE”
ccfe: 8f                           .dd1    $8f
ccff: 49 4e 50 55+                 .str    “INPUT”
cd04: 90                           .dd1    $90
cd05: 4c 49 4e 50+                 .str    “LINPUT”
cd0b: 91                           .dd1    $91
cd0c: 44 41 54 41                  .str    “DATA”
cd10: 92                           .dd1    $92
cd11: 49 46                        .str    “IF”
cd13: 93                           .dd1    $93
cd14: 52 45 41 44                  .str    “READ”
cd18: 94                           .dd1    $94
cd19: 44 49 4d                     .str    “DIM”
cd1c: 95                           .dd1    $95
cd1d: 52 45 4d                     .str    “REM”
cd20: 96                           .dd1    $96
cd21: 53 54 4f 50                  .str    “STOP”
cd25: 97                           .dd1    $97
cd26: 43 4f 4e 54                  .str    “CONT”
cd2a: 98                           .dd1    $98
cd2b: 43 4c 53                     .str    “CLS”
cd2e: 99                           .dd1    $99
cd2f: 43 4c 45 41+                 .str    “CLEAR”
cd34: 9a                           .dd1    $9a
cd35: 4f 4e                        .str    “ON”
cd37: 9b                           .dd1    $9b
cd38: 4f 46 46                     .str    “OFF”
cd3b: 9c                           .dd1    $9c
cd3c: 43 55 54                     .str    “CUT”
cd3f: 9d                           .dd1    $9d
cd40: 4e 45 57                     .str    “NEW”
cd43: 9e                           .dd1    $9e
cd44: 50 4f 4b 45                  .str    “POKE”
cd48: 9f                           .dd1    $9f
cd49: 43 47 53 45+                 .str    “CGSET”
cd4e: a0                           .dd1    $a0
cd4f: 56 49 45 57                  .str    “VIEW”
cd53: a1                           .dd1    $a1
cd54: 4d 4f 56 45                  .str    “MOVE”
cd58: a2                           .dd1    $a2
cd59: 45 4e 44                     .str    “END”
cd5c: a3                           .dd1    $a3
cd5d: 50 4c 41 59                  .str    “PLAY”
cd61: a4                           .dd1    $a4
cd62: 42 45 45 50                  .str    “BEEP”
cd66: a5                           .dd1    $a5
cd67: 4c 4f 41 44                  .str    “LOAD”
cd6b: a6                           .dd1    $a6
cd6c: 53 41 56 45                  .str    “SAVE”
cd70: a7                           .dd1    $a7
cd71: 50 4f 53 49+                 .str    “POSITION”
cd79: a8                           .dd1    $a8
cd7a: 4b 45 59                     .str    “KEY”
cd7d: a9                           .dd1    $a9
cd7e: 43 4f 4c 4f+                 .str    “COLOR”
cd83: aa                           .dd1    $aa
cd84: 44 45 46                     .str    “DEF”
cd87: ab                           .dd1    $ab
cd88: 43 47 45 4e                  .str    “CGEN”
cd8c: ac                           .dd1    $ac
cd8d: 53 57 41 50                  .str    “SWAP”
cd91: ad                           .dd1    $ad
cd92: 43 41 4c 4c                  .str    “CALL”
cd96: ae                           .dd1    $ae
cd97: 4c 4f 43 41+                 .str    “LOCATE”
cd9d: af                           .dd1    $af
cd9e: 50 41 4c 45+                 .str    “PALET”
cda3: b0                           .dd1    $b0
cda4: 45 52 41                     .str    “ERA”
cda7: b1                           .dd1    $b1
cda8: 54 52                        .str    “TR”
cdaa: b2                           .dd1    $b2
cdab: 46 49 4e 44                  .str    “FIND”
cdaf: b3                           .dd1    $b3
cdb0: 47 41 4d 45                  .str    “GAME”
cdb4: b4                           .dd1    $b4
cdb5: 42 47 54 4f+                 .str    “BGTOOL”
cdbb: b5                           .dd1    $b5
cdbc: 41 55 54 4f                  .str    “AUTO”
cdc0: b6                           .dd1    $b6
cdc1: 44 45 4c 45+                 .str    “DELETE”
cdc7: b7                           .dd1    $b7
cdc8: 52 45 4e 55+                 .str    “RENUM”
cdcd: b8                           .dd1    $b8
cdce: 46 49 4c 54+                 .str    “FILTER”
cdd4: b9                           .dd1    $b9
cdd5: 43 4c 49 43+                 .str    “CLICK”
cdda: ba                           .dd1    $ba
cddb: 53 43 52 45+                 .str    “SCREEN”
cde1: bb                           .dd1    $bb
cde2: 42 41 43 4b+                 .str    “BACKUP”
cde8: bc                           .dd1    $bc
cde9: 45 52 52 4f+                 .str    “ERROR”
cdee: bd                           .dd1    $bd
cdef: 52 45 53 55+                 .str    “RESUME”
cdf5: be                           .dd1    $be
cdf6: 42 47 50 55+                 .str    “BGPUT”
cdfb: bf                           .dd1    $bf
cdfc: 42 47 47 45+                 .str    “BGGET”
ce01: c0                           .dd1    $c0
ce02: 43 41 4e                     .str    “CAN”
ce05: ef                           .dd1    $ef
ce06: 58 4f 52                     .str    “XOR”
ce09: f0                           .dd1    $f0
ce0a: 4f 52                        .str    “OR”
ce0c: f1                           .dd1    $f1
ce0d: 41 4e 44                     .str    “AND”
ce10: f2                           .dd1    $f2
ce11: 4e 4f 54                     .str    “NOT”
ce14: f3                           .dd1    $f3
ce15: 3c 3e                        .str    “<>”
ce17: f4                           .dd1    $f4
ce18: 3e 3d                        .str    “>=”
ce1a: f5                           .dd1    $f5
ce1b: 3c 3d                        .str    “<=”
ce1d: f6                           .dd1    $f6
ce1e: 3d                           .dd1    ‘=’
ce1f: f7                           .dd1    $f7
ce20: 3e                           .dd1    ‘>’
ce21: f8                           .dd1    $f8
ce22: 3c                           .dd1    ‘<’
ce23: f9                           .dd1    $f9
ce24: 2b                           .dd1    ‘+’
ce25: fa                           .dd1    $fa
ce26: 2d                           .dd1    ‘-’
ce27: fb                           .dd1    $fb
ce28: 4d 4f 44                     .str    “MOD”
ce2b: fc                           .dd1    $fc
ce2c: 2f                           .dd1    ‘/’
ce2d: fd                           .dd1    $fd
ce2e: 2a                           .dd1    ‘*’
ce2f: ca                           .dd1    $ca                     ;all keywords below this line are built-in functions
ce30: 41 42 53                     .str    “ABS”
ce33: cb                           .dd1    $cb
ce34: 41 53 43                     .str    “ASC”
ce37: cc                           .dd1    $cc
ce38: 53 54 52 24                  .str    “STR$”
ce3c: cd                           .dd1    $cd
ce3d: 46 52 45                     .str    “FRE”
ce40: ce                           .dd1    $ce
ce41: 4c 45 4e                     .str    “LEN”
ce44: cf                           .dd1    $cf
ce45: 50 45 45 4b                  .str    “PEEK”
ce49: d0                           .dd1    $d0
ce4a: 52 4e 44                     .str    “RND”
ce4d: d1                           .dd1    $d1
ce4e: 53 47 4e                     .str    “SGN”
ce51: d2                           .dd1    $d2
ce52: 53 50 43                     .str    “SPC”
ce55: d3                           .dd1    $d3
ce56: 54 41 42                     .str    “TAB”
ce59: d4                           .dd1    $d4
ce5a: 4d 49 44 24                  .str    “MID$”
ce5e: d5                           .dd1    $d5
ce5f: 53 54 49 43+                 .str    “STICK”
ce64: d6                           .dd1    $d6
ce65: 53 54 52 49+                 .str    “STRIG”
ce6a: d7                           .dd1    $d7
ce6b: 58 50 4f 53                  .str    “XPOS”
ce6f: d8                           .dd1    $d8
ce70: 59 50 4f 53                  .str    “YPOS”
ce74: d9                           .dd1    $d9
ce75: 56 41 4c                     .str    “VAL”
ce78: da                           .dd1    $da
ce79: 50 4f 53                     .str    “POS”
ce7c: db                           .dd1    $db
ce7d: 43 53 52 4c+                 .str    “CSRLIN”
ce83: dc                           .dd1    $dc
ce84: 43 48 52 24                  .str    “CHR$”
ce88: dd                           .dd1    $dd
ce89: 48 45 58 24                  .str    “HEX$”
ce8d: de                           .dd1    $de
ce8e: 49 4e 4b 45+                 .str    “INKEY$”
ce94: df                           .dd1    $df
ce95: 52 49 47 48+                 .str    “RIGHT$”
ce9b: e0                           .dd1    $e0
ce9c: 4c 45 46 54+                 .str    “LEFT$”
cea1: e1                           .dd1    $e1
cea2: 53 43 52 24                  .str    “SCR$”
cea6: e2                           .dd1    $e2
cea7: 49 4e 53 54+                 .str    “INSTR”
ceac: e3                           .dd1    $e3
cead: 43 52 41 53+                 .str    “CRASH”
ceb2: e4                           .dd1    $e4
ceb3: 45 52 52                     .str    “ERR”
ceb6: e5                           .dd1    $e5
ceb7: 45 52 4c                     .str    “ERL”
ceba: e6                           .dd1    $e6
cebb: 56 43 54                     .str    “VCT”
cebe: ff                           .dd1    $ff
cebf: c6 96        jtbl_Commands   .dd2    CmdFn_GOTO
cec1: 48 96                        .dd2    CmdFn_GOSUB
cec3: b0 86                        .dd2    CmdFn_RUN
cec5: a6 96                        .dd2    CmdFn_RETURN
cec7: c9 9e                        .dd2    CmdFn_RESTORE
cec9: 91 84                        .dd2    ErrorSyntax
cecb: 67 87                        .dd2    CmdFn_LIST
cecd: 8f 81                        .dd2    CmdFn_SYSTEMorBACKUP
cecf: 91 84                        .dd2    ErrorSyntax
ced1: 91 84                        .dd2    ErrorSyntax
ced3: fc af                        .dd2    CmdFn_SPRITE
ced5: c5 88                        .dd2    CmdFn_PRINT
ced7: fc 97                        .dd2    CmdFn_FOR
ced9: 87 98                        .dd2    CmdFn_NEXT
cedb: 16 97                        .dd2    CmdFn_PAUSE
cedd: 3c 99                        .dd2    CmdFn_INPUT
cedf: 6c 9a                        .dd2    CmdFn_LINPUT
cee1: 67 84                        .dd2    CmdFn_DATAorREM
cee3: 82 97                        .dd2    CmdFn_IF
cee5: 36 9f                        .dd2    CmdFn_READ
cee7: 0c 9c                        .dd2    CmdFn_DIM
cee9: 67 84                        .dd2    CmdFn_DATAorREM
ceeb: 5b 88                        .dd2    CmdFn_STOP
ceed: 9c 88                        .dd2    CmdFn_CONT
ceef: 0a ae                        .dd2    CmdFn_CLS
cef1: a4 97                        .dd2    CmdFn_CLEAR
cef3: fd 9a                        .dd2    CmdFn_ON
cef5: 91 84                        .dd2    ErrorSyntax
cef7: dc cb                        .dd2    CmdFn_CUT
cef9: b6 84                        .dd2    CmdFn_NEW
cefb: 5d 97                        .dd2    CmdFn_POKE
cefd: 4f af                        .dd2    CmdFn_CGSET
ceff: 53 b1                        .dd2    CmdFn_VIEW
cf01: b3 cb                        .dd2    CmdFn_MOVE
cf03: 6a 82                        .dd2    CmdFn_END
cf05: 1e a0                        .dd2    CmdFn_PLAY
cf07: d0 bc                        .dd2    CmdFn_BEEP
cf09: e1 9d                        .dd2    CmdFn_LOAD
cf0b: 64 9d                        .dd2    CmdFn_SAVE
cf0d: 9b cb                        .dd2    CmdFn_POSITION
cf0f: 68 9b                        .dd2    CmdFn_KEY
cf11: ca c4                        .dd2    CmdFn_COLOR
cf13: 98 b0                        .dd2    CmdFn_DEF
cf15: d1 ae                        .dd2    CmdFn_CGEN
cf17: 0e 9a                        .dd2    CmdFn_SWAP
cf19: 0a 97                        .dd2    CmdFn_CALL
cf1b: 48 97                        .dd2    CmdFn_LOCATE
cf1d: f3 ae                        .dd2    CmdFn_PALET
cf1f: d9 cb                        .dd2    CmdFn_ERA
cf21: 2b 96                        .dd2    CmdFn_TR
cf23: 73 95                        .dd2    CmdFn_FIND
cf25: 5b ad                        .dd2    CmdFn_GAME
cf27: d1 bf                        .dd2    CmdFn_BGTOOL
cf29: da 8b                        .dd2    CmdFn_AUTO
cf2b: 58 87                        .dd2    CmdFn_DELETE
cf2d: 30 8c                        .dd2    CmdFn_RENUM
cf2f: b8 ae                        .dd2    CmdFn_FILTER
cf31: 25 96                        .dd2    CmdFn_CLICK
cf33: 48 ae                        .dd2    CmdFn_SCREEN
cf35: 8f 81                        .dd2    CmdFn_SYSTEMorBACKUP    ;tok_BACKUP
cf37: 10 95                        .dd2    CmdFn_ERROR
cf39: 27 95                        .dd2    CmdFn_RESUME
cf3b: 01 b2                        .dd2    CmdFn_BGPUT
cf3d: b8 b1                        .dd2    CmdFn_BGGET
cf3f: 6c cc                        .dd2    CmdFn_CAN
cf41: 84 a9        jtbl_Functions  .dd2    FnFn_ABS
cf43: 28 aa                        .dd2    FnFn_ASC
cf45: a6 aa                        .dd2    FnFn_STR_STR
cf47: 1f a9                        .dd2    FnFn_FRE
cf49: 1d aa                        .dd2    FnFn_LEN
cf4b: 73 a9                        .dd2    FnFn_PEEK
cf4d: bd a9                        .dd2    FnFn_RND
cf4f: 9c a9                        .dd2    FnFn_SGN
cf51: 91 84                        .dd2    ErrorSyntax             ;SPC, only allowed in PRINT
cf53: 91 84                        .dd2    ErrorSyntax             ;TAB, only allowed in PRINT
cf55: 81 ac                        .dd2    FnFn_MID_STR
cf57: 38 ad                        .dd2    FnFn_STICK
cf59: 47 ad                        .dd2    FnFn_STRIG
cf5b: 58 a9                        .dd2    FnFn_XPOS
cf5d: 61 a9                        .dd2    FnFn_YPOS
cf5f: df aa                        .dd2    FnFn_VAL
cf61: 33 a9                        .dd2    FnFn_POS
cf63: e6 a8                        .dd2    FnFn_CSRLIN
cf65: 82 aa                        .dd2    FnFn_CHR_STR
cf67: d6 aa                        .dd2    FnFn_HEX_STR
cf69: 58 ab                        .dd2    FnFn_INKEY_STR
cf6b: 41 ac                        .dd2    FnFn_RIGHT_STR
cf6d: ed ab                        .dd2    FnFn_LEFT_STR
cf6f: 3b aa                        .dd2    FnFn_SCR_STR
cf71: af ab                        .dd2    FnFn_INSTR
cf73: 04 cc                        .dd2    FnFn_CRASH
cf75: 0d a9                        .dd2    FnFn_ERR
cf77: 14 a9                        .dd2    FnFn_ERL
cf79: 6a a9                        .dd2    FnFn_VCT
cf7b: 4e 53 2d 48+ strNS_HUDSON    .zstr   “NS-HUBASIC V3.0 ”
                   strCOPYRIGHT_MSG
cf8c: b4 20 4e 49+                 .zstr   $b4,“ NINTENDO/SHARP/HUDSON”
                   tbl_CgsetBgPaletteSets
cfa4: ae cf                        .dd2    cgsetBgPalSet0
cfa6: be cf                        .dd2    cgsetBgPalSet1
                   tbl_CgsetSprPaletteSets
cfa8: ce cf                        .dd2    cgsetSprPalSet0
cfaa: de cf                        .dd2    cgsetSprPalSet1
cfac: ee cf                        .dd2    cgsetSprPalSet2
cfae: 0f 2c 15 07  cgsetBgPalSet0  .bulk   $0f,$2c,$15,$07
cfb2: 0f 27 21 12                  .bulk   $0f,$27,$21,$12
cfb6: 0f 29 36 17                  .bulk   $0f,$29,$36,$17
cfba: 0f 30 26 07                  .bulk   $0f,$30,$26,$07
cfbe: 0f 30 21 02  cgsetBgPalSet1  .bulk   $0f,$30,$21,$02         ;defailt set
cfc2: 0f 30 27 18                  .bulk   $0f,$30,$27,$18
cfc6: 0f 30 27 16                  .bulk   $0f,$30,$27,$16
cfca: 0f 29 36 17                  .bulk   $0f,$29,$36,$17
cfce: 0f 36 16 02  cgsetSprPalSet0 .bulk   $0f,$36,$16,$02
cfd2: 0f 27 30 19                  .bulk   $0f,$27,$30,$19
cfd6: 0f 35 25 17                  .bulk   $0f,$35,$25,$17
cfda: 0f 30 27 16                  .bulk   $0f,$30,$27,$16
cfde: 0f 30 16 01  cgsetSprPalSet1 .bulk   $0f,$30,$16,$01         ;default set
cfe2: 0f 10 00 01                  .bulk   $0f,$10,$00,$01
cfe6: 0f 30 29 09                  .bulk   $0f,$30,$29,$09
cfea: 0f 30 16 07                  .bulk   $0f,$30,$16,$07
cfee: 0f 30 26 12  cgsetSprPalSet2 .bulk   $0f,$30,$26,$12
cff2: 0f 30 15 12                  .bulk   $0f,$30,$15,$12
cff6: 0f 30 12 16                  .bulk   $0f,$30,$12,$16
cffa: 0f 30 26 19                  .bulk   $0f,$30,$26,$19
cffe: ff ff                        .bulk   $ff,$ff
                   ; The built-in background map for use with "GAME n,1"
                   gameBackgroundMap
d000: 20 20 20 20+                 .bulk   $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$32,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$34,$20,$20,$20,$20,$d7,$20,$d7,$20,$d7,$20,$20,$20,$20
                                    +      $20,$20,$c0,$c0,$c0,$d2,$20,$20,$20,$20,$20,$39,$20,$20,$20,$20
                                    +      $20,$c0,$c0,$c0,$ed,$ed,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$20,$20
                                    +      $20,$20,$20,$20,$20,$d2,$20,$20,$c0,$c0,$c0,$c0,$c0,$c0,$d2,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$e3,$20,$d2,$20,$20,$20,$20,$20,$20,$20,$20,$d2,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$e3,$20,$20,$38,$20,$20,$20
                                    +      $20,$20,$c0,$c0,$c0,$d2,$20,$20,$20,$20,$20,$e3,$20,$20,$d2,$20
                                    +      $20,$20,$d7,$20,$c0,$c0,$c0,$d2,$c0,$c0,$c0,$c0,$c0,$c0,$20,$20
                                    +      $20,$20,$20,$20,$20,$d2,$20,$20,$20,$20,$c0,$c0,$c0,$c0,$d2,$20
                                    +      $20,$c0,$c0,$c0,$c0,$20,$20,$d2,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$d2,$20,$d7,$31,$20,$20,$20,$20,$20,$d2,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$d2,$20,$20,$20,$e3,$20,$20,$20,$20
                                    +      $20,$20,$c0,$d2,$c0,$c0,$c0,$c0,$c0,$d2,$20,$20,$20,$20,$d2,$20
                                    +      $20,$20,$e3,$20,$20,$20,$20,$d2,$20,$20,$c0,$c0,$c0,$c0,$20,$20
                                    +      $20,$20,$20,$d2,$20,$20,$20,$20,$20,$d2,$20,$20,$20,$20,$d2,$c0
                                    +      $c0,$c0,$c0,$c0,$c0,$20,$20,$d2,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$d2,$20,$20,$20,$20,$20,$d2,$20,$20,$20,$20,$d2,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$d2,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$35,$d2,$20,$20,$20,$e3,$20,$d2,$20,$20,$20,$20,$d2,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$d2,$20,$20,$20,$20,$d7,$20,$20,$20
                                    +      $20,$20,$c0,$c0,$ed,$ed,$c0,$c0,$c0,$c0,$c0,$d2,$20,$20,$d2,$20
                                    +      $20,$20,$e3,$20,$20,$37,$20,$d2,$20,$20,$20,$d2,$c0,$c0,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$d2,$20,$20,$d2,$20
                                    +      $20,$c0,$c0,$c0,$c0,$c0,$c0,$d2,$20,$20,$20,$d2,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$d2,$20,$20,$d2,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$d2,$20,$20,$20,$20
                                    +      $20,$20,$20,$d7,$20,$20,$20,$20,$20,$20,$20,$d2,$20,$20,$d2,$20
                                    +      $33,$d7,$20,$20,$20,$d7,$20,$20,$20,$d7,$20,$d2,$e3,$e3,$20,$20
                                    +      $20,$20,$c0,$c0,$c0,$d2,$20,$20,$20,$20,$20,$d2,$20,$20,$d2,$c0
                                    +      $c0,$c0,$ed,$ed,$c0,$c0,$ed,$ed,$c0,$c0,$c0,$d2,$c0,$c0,$20,$20
                                    +      $20,$20,$20,$20,$20,$d2,$20,$20,$20,$20,$20,$d2,$20,$20,$d2,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$d2,$20,$20,$20,$20
                                    +      $20,$20,$20,$36,$20,$d2,$20,$20,$20,$30,$20,$d2,$20,$20,$20,$20
                                    +      $20,$e3,$20,$20,$20,$e3,$20,$20,$20,$e3,$20,$d2,$20,$20,$20,$20
                                    +      $20,$20,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0
                                    +      $c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
                                    +      $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$03,$00,$00,$19,$0a,$00
                                    +      $00,$c0,$00,$00,$08,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$30
                                    +      $c0,$01,$00,$00,$30,$e0,$30,$00,$00,$00,$00,$00,$04,$04,$00,$00
                                    +      $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
                   ; "GAME 0" built-in BASIC program, tokenized
d400: 1d 0a 00 a3+ txtGame0        .bulk   $1d,$0a,$00,$a3,$22,$4d,$31,$56,$39,$59,$32,$54,$33,$3a,$4d,$31
                                    +      $56,$37,$59,$31,$54,$33,$3a,$4d,$31,$54,$33,$22,$00,$18,$14,$00
                                    +      $a3,$22,$4f,$33,$45,$35,$47,$3a,$4f,$33,$43,$35,$43,$3a,$4f,$32
                                    +      $47,$35,$47,$22,$00,$18,$1e,$00,$a3,$22,$4f,$34,$43,$4f,$33,$42
                                    +      $34,$41,$31,$3a,$43,$43,$3a,$23,$46,$23,$46,$22,$00,$19,$28,$00
                                    +      $a3,$22,$47,$34,$41,$31,$3a,$46,$34,$46,$31,$3a,$4f,$33,$44,$34
                                    +      $4f,$32,$42,$31,$22,$00,$17,$32,$00,$a3,$22,$42,$34,$4f,$34,$43
                                    +      $37,$3a,$46,$34,$45,$37,$3a,$47,$34,$43,$37,$22,$00,$40,$3c,$00
                                    +      $98,$3a,$ae,$20,$09,$2c,$12,$0a,$00,$3a,$51,$f6,$cf,$28,$11,$16
                                    +      $40,$29,$20,$f1,$20,$11,$04,$00,$3a,$8f,$22,$50,$4c,$41,$59,$20
                                    +      $3f,$20,$59,$2f,$4e,$3d,$3e,$22,$2c,$59,$24,$3a,$92,$20,$59,$24
                                    +      $f6,$22,$4e,$22,$20,$85,$20,$51,$f6,$12,$e7,$03,$00,$16,$46,$00
                                    +      $98,$3a,$b0,$01,$2c,$02,$2c,$03,$2c,$04,$2c,$05,$2c,$06,$2c,$07
                                    +      $2c,$08,$00,$12,$50,$00,$8a,$20,$9a,$3a,$9f,$01,$2c,$01,$3a,$55
                                    +      $f6,$11,$15,$00,$00,$16,$5a,$00,$af,$42,$20,$01,$2c,$12,$0f,$00
                                    +      $2c,$12,$30,$00,$2c,$12,$30,$00,$2c,$55,$00,$1d,$64,$00,$41,$24
                                    +      $f6,$22,$41,$49,$49,$49,$49,$49,$49,$49,$49,$49,$49,$49,$49,$47
                                    +      $49,$47,$47,$47,$47,$48,$22,$00,$15,$6e,$00,$42,$24,$f6,$22,$48
                                    +      $48,$42,$48,$42,$42,$42,$45,$42,$45,$45,$45,$22,$00,$12,$78,$00
                                    +      $4b,$24,$f6,$22,$41,$42,$43,$44,$45,$46,$47,$48,$49,$22,$00,$12
                                    +      $82,$00,$49,$24,$f6,$22,$41,$42,$43,$47,$48,$49,$44,$45,$46,$22
                                    +      $00,$1f,$8c,$00,$98,$3a,$ae,$0a,$2c,$12,$0a,$00,$3a,$8b,$22,$53
                                    +      $54,$41,$52,$54,$20,$21,$21,$22,$3a,$8e,$12,$50,$00,$3a,$98,$00
                                    +      $13,$96,$00,$58,$f6,$12,$0d,$00,$3a,$59,$f6,$12,$16,$00,$3a,$49
                                    +      $f6,$01,$00,$14,$a0,$00,$81,$0b,$dc,$00,$3a,$ac,$41,$24,$2c,$42
                                    +      $24,$3a,$81,$0b,$dc,$00,$00,$19,$aa,$00,$92,$20,$49,$f6,$01,$85
                                    +      $ac,$4b,$24,$2c,$49,$24,$3a,$49,$f6,$02,$3a,$80,$0b,$a0,$00,$00
                                    +      $0d,$b4,$00,$81,$0b,$36,$01,$3a,$81,$0b,$ae,$01,$00,$0f,$be,$00
                                    +      $92,$20,$51,$f3,$12,$e7,$03,$85,$0b,$3c,$00,$00,$08,$c8,$00,$80
                                    +      $0b,$46,$00,$00,$05,$d2,$00,$a2,$00,$12,$dc,$00,$4c,$f6,$ce,$28
                                    +      $41,$24,$29,$3a,$8c,$50,$f6,$02,$88,$4c,$00,$1a,$e6,$00,$92,$28
                                    +      $cf,$28,$11,$16,$40,$29,$20,$f1,$20,$11,$04,$00,$29,$f6,$51,$20
                                    +      $85,$0b,$e6,$00,$00,$07,$f0,$00,$4b,$f6,$50,$00,$0d,$fa,$00,$92
                                    +      $20,$49,$f6,$01,$85,$0b,$0e,$01,$00,$0b,$04,$01,$4b,$f6,$4c,$f9
                                    +      $02,$fa,$50,$00,$17,$0e,$01,$43,$f6,$e2,$28,$4b,$24,$2c,$d4,$28
                                    +      $41,$24,$2c,$4b,$2c,$02,$29,$29,$fa,$02,$00,$10,$18,$01,$56,$f6
                                    +      $43,$fc,$04,$3a,$48,$f6,$43,$20,$fb,$04,$00,$1f,$22,$01,$58,$f6
                                    +      $58,$f9,$48,$f9,$04,$fd,$28,$48,$f6,$03,$29,$3a,$59,$f6,$59,$f9
                                    +      $56,$f9,$04,$fd,$28,$56,$f6,$03,$29,$00,$12,$2c,$01,$ae,$58,$2c
                                    +      $59,$3a,$8b,$22,$cf,$22,$3b,$3a,$8d,$3a,$83,$00,$17,$36,$01,$59
                                    +      $f6,$12,$16,$00,$3a,$53,$f6,$01,$3a,$45,$f6,$12,$0d,$00,$3a,$54
                                    +      $f6,$01,$00,$17,$40,$01,$ae,$12,$12,$00,$2c,$12,$14,$00,$3a,$8b
                                    +      $22,$53,$43,$4f,$52,$45,$3a,$22,$3b,$00,$1a,$4a,$01,$92,$28,$cf
                                    +      $28,$11,$16,$40,$29,$20,$f1,$20,$11,$04,$00,$29,$f6,$51,$20,$85
                                    +      $0b,$4a,$01,$00,$15,$54,$01,$54,$f6,$54,$f9,$02,$3a,$ae,$12,$18
                                    +      $00,$2c,$12,$14,$00,$3a,$8b,$54,$00,$2c,$5e,$01,$58,$f6,$58,$f9
                                    +      $02,$3a,$92,$e1,$28,$58,$2c,$59,$29,$f6,$22,$20,$22,$f1,$20,$58
                                    +      $f5,$45,$20,$85,$20,$ae,$20,$58,$2c,$59,$3a,$8b,$22,$cf,$22,$3a
                                    +      $80,$0b,$4a,$01,$00,$12,$68,$01,$59,$f6,$59,$fa,$02,$3a,$92,$59
                                    +      $f8,$02,$85,$0b,$86,$01,$00,$12,$72,$01,$81,$0b,$9a,$01,$3a,$92
                                    +      $58,$f6,$fa,$02,$85,$0b,$68,$01,$00,$08,$7c,$01,$80,$0b,$5e,$01
                                    +      $00,$24,$86,$01,$92,$53,$f6,$01,$85,$53,$f6,$12,$0d,$00,$3a,$45
                                    +      $f6,$12,$1a,$00,$3a,$59,$f6,$12,$14,$00,$3a,$81,$0b,$9a,$01,$3a
                                    +      $80,$0b,$4a,$01,$00,$05,$90,$01,$83,$00,$33,$9a,$01,$58,$f6,$fa
                                    +      $02,$3a,$8c,$43,$f6,$53,$20,$88,$45,$3a,$92,$e1,$28,$43,$2c,$59
                                    +      $29,$f6,$22,$cf,$22,$f1,$20,$e1,$28,$43,$f9,$02,$2c,$59,$29,$f6
                                    +      $22,$20,$22,$85,$20,$58,$f6,$43,$3a,$43,$f6,$45,$00,$07,$a4,$01
                                    +      $8d,$3a,$83,$00,$38,$ae,$01,$57,$f6,$11,$34,$00,$3a,$8c,$4e,$f6
                                    +      $01,$88,$02,$3a,$aa,$a1,$28,$4e,$29,$f6,$8a,$28,$4e,$2c,$04,$f9
                                    +      $05,$fd,$4e,$2c,$02,$2c,$02,$2c,$01,$2c,$01,$29,$3a,$a7,$4e,$2c
                                    +      $12,$ec,$00,$fd,$4e,$2c,$12,$c8,$00,$3a,$8d,$00,$1a,$b8,$01,$92
                                    +      $28,$cf,$28,$11,$16,$40,$29,$20,$f1,$20,$11,$04,$00,$29,$f6,$51
                                    +      $20,$85,$0b,$b8,$01,$00,$15,$c2,$01,$54,$f6,$54,$f9,$02,$3a,$ae
                                    +      $12,$18,$00,$2c,$12,$14,$00,$3a,$8b,$54,$00,$14,$cc,$01,$a1,$01
                                    +      $2c,$02,$3a,$92,$e3,$28,$01,$29,$f6,$02,$85,$0b,$e0,$01,$00,$08
                                    +      $d6,$01,$80,$0b,$b8,$01,$00,$38,$e0,$01,$ae,$12,$0a,$00,$2c,$12
                                    +      $0a,$00,$3a,$8b,$22,$50,$45,$52,$46,$45,$43,$54,$22,$3a,$8c,$4e
                                    +      $f6,$01,$88,$12,$64,$00,$3a,$af,$42,$20,$01,$2c,$07,$2c,$11,$30
                                    +      $00,$2c,$11,$30,$00,$2c,$55,$3a,$ac,$55,$2c,$57,$3a,$8d,$00,$1c
                                    +      $ea,$01,$a3,$22,$4d,$31,$56,$32,$59,$31,$54,$32,$3a,$4d,$31,$56
                                    +      $31,$30,$59,$31,$54,$32,$3a,$54,$32,$22,$00,$20,$f4,$01,$a3,$22
                                    +      $4f,$33,$45,$33,$47,$23,$44,$47,$44,$47,$23,$44,$47,$3a,$4f,$31
                                    +      $23,$42,$35,$42,$23,$41,$42,$3a,$52,$22,$00,$1f,$fe,$01,$a3,$22
                                    +      $45,$47,$4f,$34,$43,$4f,$33,$45,$3a,$59,$32,$4f,$32,$23,$42,$36
                                    +      $45,$33,$3a,$4f,$31,$43,$35,$43,$22,$00,$18,$08,$02,$a3,$22,$23
                                    +      $47,$41,$4f,$34,$44,$43,$3a,$46,$35,$23,$46,$3a,$23,$43,$23,$43
                                    +      $22,$00,$1d,$12,$02,$a3,$22,$4f,$33,$42,$23,$41,$42,$4f,$34,$41
                                    +      $3a,$46,$33,$4f,$33,$44,$46,$41,$3a,$44,$36,$46,$33,$22,$00,$1e
                                    +      $1c,$02,$a3,$22,$47,$4f,$33,$41,$4f,$34,$46,$4f,$33,$42,$3a,$45
                                    +      $35,$44,$56,$31,$35,$3a,$47,$41,$23,$41,$42,$22,$00,$23,$26,$02
                                    +      $a3,$22,$23,$42,$33,$47,$54,$33,$46,$47,$45,$31,$44,$56,$31,$32
                                    +      $45,$37,$3a,$43,$39,$3a,$23,$42,$35,$54,$33,$47,$43,$37,$22,$00
                                    +      $1a,$30,$02,$af,$42,$20,$01,$2c,$12,$0f,$00,$2c,$12,$30,$00,$2c
                                    +      $12,$30,$00,$2c,$12,$15,$00,$3a,$83,$00,$00,$53,$44,$24,$f3,$22
                                    +      $20,$22,$20,$85,$20,$4b,$44,$f6,$06,$20,$80,$20,$0b,$24,$04,$00
                                    +      $1a,$10,$04,$92,$20,$53,$55,$24,$f3,$22,$20,$22,$20,$85,$20,$4b
                                    +      $44,$f6,$02,$20,$80,$20,$0b,$24,$04,$00,$09,$1a,$04,$80,$20,$0b
                                    +      $f2,$03,$00,$09,$24,$04,$27,$fe,$62,$9b,$67,$00,$37,$2e,$04,$aa
                                    +      $20,$a1,$28,$46,$29,$f6,$8a,$20,$28,$12,$0e,$00,$2c,$4b,$44,$2c
                                    +      $02,$2c,$05,$2c,$01,$2c,$04,$29,$3a,$a7,$20,$46,$2c,$4b,$58,$fd
                                    +      $09,$f9,$12,$0c,$00,$2c,$4b,$59,$fd,$09,$f9,$12,$10,$00,$3a,$a1
                                    +      $20,$46,$00,$0c,$38,$04,$27,$fe,$a8,$71,$65,$96,$6f,$3f,$00,$23
                                    +      $42,$04,$92,$20,$e3,$28,$08,$29,$f4,$01,$20,$85,$20,$a3,$20,$22
                                    +      $4f,$31,$42,$32,$41,$47,$46,$45,$44,$43,$22,$3a,$80,$20,$0b,$60
                                    +      $04,$00,$07,$4c,$04,$27,$fe,$fe,$00,$09,$56,$04,$80,$20,$0b,$d6
                                    +      $01,$00,$0b,$60,$04,$27,$fe,$fe,$fe,$64,$8b,$87,$00,$21,$6a,$04
                                    +      $8c,$20,$4a,$58,$f6,$07,$20,$88,$20,$09,$3a,$93,$20,$53,$24,$3a
                                    +      $ae,$20,$0a,$2c,$4a,$58,$3a,$8b,$20,$53,$24,$3a,$8d,$00,$07,$74
                                    +      $04,$8a,$20,$9b,$00,$12,$7e,$04,$91,$20,$22,$fd,$fe,$fe,$fe,$fe
                                    +      $fe,$fe,$fe,$fe,$fd,$22,$00,$12,$88,$04,$91,$20,$22,$fe,$fd,$20
                                    +      $64,$6b,$7e,$61,$20,$fd,$fe,$22,$00,$12,$92,$04,$91,$20,$22,$fd
                                    +      $fe,$fe,$fe,$fe,$fe,$fe,$fe,$fe,$fd,$22,$00,$16,$9c,$04,$53,$f6
                                    +      $d6,$28,$01,$29,$3a,$92,$20,$53,$f6,$01,$20,$80,$20,$0b,$9c,$04
                                    +      $00,$0d,$a6,$04,$92,$20,$53,$f6,$02,$20,$85,$20,$82,$00,$05,$b0
                                    +      $04,$a2,$00,$00,$f6,$58,$4c,$fd,$09,$f9,$12,$0c,$00,$3a,$59,$f6
                                    +      $59,$4c,$fd,$09,$f9,$08,$3a,$a7,$20,$08,$2c,$58,$2c,$59,$3a,$83
                                    +      $00,$32,$70,$03,$ae,$20,$58,$42,$2c,$59,$4c,$fa,$02,$3a,$8b,$20
                                    +      $22,$20,$22,$3a,$52,$f6,$52,$f9,$02,$3a,$57,$f6,$57,$f9,$12,$0f
                                    +      $00,$3a,$ae,$20,$12,$0e,$00,$2c,$12,$17,$00,$3a,$8b,$20,$57,$3b
                                    +      $3a,$83,$00,$5d,$7a,$03,$a0,$3a,$49,$f6,$01,$3a,$46,$f6,$01,$3a
                                    +      $4b,$f6,$01,$3a,$43,$f6,$01,$3a,$52,$f6,$01,$3a,$5a,$f6,$12,$30
                                    +      $00,$3a,$ae,$20,$12,$0a,$00,$2c,$12,$15,$00,$3a,$8b,$22,$54,$49
                                    +      $4d,$45,$22,$3b,$54,$3a,$ae,$20,$07,$2c,$12,$16,$00,$3a,$8b,$22
                                    +      $48,$49,$2d,$53,$43,$4f,$52,$45,$22,$3b,$48,$3a,$ae,$20,$0a,$2c
                                    +      $12,$17,$00,$3a,$8b,$22,$53,$43,$4f,$52,$45,$22,$3b,$57,$3b,$00
                                    +      $05,$84,$03,$83,$00,$14,$8e,$03,$27,$63,$86,$2d,$20,$6b,$95,$87
                                    +      $20,$78,$20,$ad,$8a,$99,$86,$80,$00,$2c,$98,$03,$92,$20,$e5,$f6
                                    +      $12,$30,$02,$20,$85,$20,$58,$4c,$f6,$fa,$12,$1b,$00,$fd,$28,$58
                                    +      $4c,$f8,$02,$29,$3a,$81,$20,$0b,$66,$03,$3a,$a1,$20,$08,$3a,$bd
                                    +      $20,$0b,$76,$02,$00,$2d,$a2,$03,$92,$20,$e5,$f6,$12,$86,$01,$20
                                    +      $85,$20,$58,$4c,$f6,$fa,$12,$1b,$00,$fd,$28,$58,$4c,$f8,$01,$29
                                    +      $3a,$81,$20,$0b,$66,$03,$3a,$53,$f6,$53,$31,$3a,$bd,$20,$0b,$26
                                    +      $02,$00,$14,$ac,$03,$92,$20,$59,$4c,$f7,$12,$17,$00,$20,$85,$20
                                    +      $bd,$20,$0b,$94,$02,$00,$1b,$b6,$03,$92,$20,$58,$4c,$f7,$12,$1b
                                    +      $00,$20,$85,$20,$58,$4c,$f6,$01,$3a,$81,$20,$0b,$66,$03,$3a,$bd
                                    +      $00,$1b,$c0,$03,$92,$20,$58,$4c,$f8,$01,$20,$85,$20,$58,$4c,$f6
                                    +      $12,$1b,$00,$3a,$81,$20,$0b,$66,$03,$3a,$bd,$00,$18,$ca,$03,$92
                                    +      $20,$58,$42,$f7,$12,$1b,$00,$20,$85,$20,$58,$42,$f6,$01
                   ; "GAME 1" built-in BASIC program, tokenized
dbfe: 15 64 00 99+ txtGame1        .bulk   $15,$64,$00,$99,$3a,$ab,$20,$03,$3a,$9f,$20,$02,$2c,$02,$3a,$ba
                                    +      $20,$01,$2c,$02,$00,$17,$6e,$00,$8a,$20,$9a,$3a,$a3,$20,$22,$56
                                    +      $31,$35,$54,$31,$3a,$56,$31,$35,$54,$31,$22,$00,$16,$78,$00,$92
                                    +      $20,$e1,$28,$02,$2c,$03,$29,$f3,$22,$20,$22,$20,$80,$20,$0b,$f0
                                    +      $00,$00,$09,$82,$00,$27,$fe,$81,$61,$8a,$00,$25,$8c,$00,$8c,$20
                                    +      $4a,$58,$f6,$02,$20,$88,$20,$12,$15,$00,$20,$89,$20,$05,$3a,$8c
                                    +      $20,$4a,$59,$f6,$03,$20,$88,$20,$12,$14,$00,$20,$89,$20,$04,$00
                                    +      $24,$96,$00,$ae,$20,$4a,$58,$2c,$4a,$59,$3a,$8b,$20,$22,$e8,$ed
                                    +      $ed,$ed,$e8,$22,$3a,$92,$20,$4a,$59,$f7,$12,$13,$00,$20,$80,$20
                                    +      $0b,$b4,$00,$00,$17,$a0,$00,$ae,$20,$4a,$58,$2c,$4a,$59,$f9,$02
                                    +      $3a,$8b,$20,$22,$ee,$20,$20,$20,$ee,$22,$00,$17,$aa,$00,$ae,$20
                                    +      $4a,$58,$2c,$4a,$59,$f9,$03,$3a,$8b,$20,$22,$ee,$20,$20,$20,$ee
                                    +      $22,$00,$07,$b4,$00,$8d,$3a,$8d,$00,$2f,$be,$00,$8c,$20,$53,$f6
                                    +      $06,$20,$88,$20,$12,$11,$00,$20,$89,$20,$04,$3a,$ae,$20,$02,$2c
                                    +      $53,$3a,$8b,$20,$22,$ec,$22,$3a,$ae,$20,$12,$19,$00,$2c,$53,$3a
                                    +      $8b,$20,$22,$eb,$22,$3a,$8d,$00,$10,$c8,$00,$ae,$20,$02,$2c,$03
                                    +      $3a,$8b,$20,$22,$ef,$22,$3b,$00,$22,$d2,$00,$8c,$20,$53,$f6,$01
                                    +      $20,$88,$20,$05,$3a,$8b,$20,$22,$ed,$ed,$ed,$ea,$22,$3b,$3a,$8d
                                    +      $3a,$8b,$20,$22,$ed,$ed,$ed,$f0,$22,$00,$12,$dc,$00,$ae,$20,$02
                                    +      $2c,$12,$14,$00,$3a,$8b,$20,$22,$f1,$22,$3b,$00,$22,$e6,$00,$8c
                                    +      $20,$53,$f6,$01,$20,$88,$20,$05,$3a,$8b,$20,$22,$ed,$ed,$ed,$e9
                                    +      $22,$3b,$3a,$8d,$3a,$8b,$20,$22,$ed,$ed,$ed,$f2,$22,$00,$0a,$f0
                                    +      $00,$27,$fe,$73,$88,$82,$78,$00,$27,$fa,$00,$ba,$20,$01,$2c,$01
                                    +      $3a,$a0,$3a,$8b,$20,$22,$65,$61,$a0,$62,$6c,$88,$3f,$20,$20,$20
                                    +      $41,$3d,$6e,$78,$7e,$7e,$20,$20,$20,$42,$3d,$6c,$88,$22,$00,$16
                                    +      $04,$01,$4b,$f6,$d6,$28,$01,$29,$3a,$92,$20,$4b,$f6,$01,$20,$80
                                    +      $20,$0b,$04,$01,$00,$0d,$0e,$01,$92,$20,$4b,$f6,$05,$20,$85,$20
                                    +      $b4,$00,$15,$18,$01,$a3,$20,$22,$4f,$33,$43,$31,$44,$45,$3a,$4f
                                    +      $33,$45,$31,$46,$47,$22,$00,$22,$22,$01,$50,$53,$24,$f6,$22,$31
                                    +      $32,$33,$34,$35,$36,$37,$38,$39,$22,$3a,$50,$4c,$f6,$ce,$28,$50
                                    +      $53,$24,$29,$3a,$50,$53,$f6,$02,$00,$0e,$2c,$01,$8c,$20,$53,$f6
                                    +      $02,$20,$88,$20,$50,$4c,$00,$28,$36,$01,$20,$53,$58,$f6,$03,$f9
                                    +      $d0,$28,$12,$18,$00,$29,$3a,$53,$59,$f6,$03,$f9,$d0,$28,$12,$12
                                    +      $00,$29,$3a,$53,$24,$f6,$e1,$28,$53,$58,$2c,$53,$59,$29,$00,$21
                                    +      $40,$01,$20,$92,$20,$53,$24,$f6,$22,$20,$22,$20,$f0,$20,$e2,$28
                                    +      $50,$53,$24,$2c,$53,$24,$29,$f7,$01,$20,$80,$20,$0b,$36,$01,$00
                                    +      $18,$4a,$01,$ae,$20,$53,$58,$2c,$53,$59,$3a,$8b,$20,$d4,$28,$50
                                    +      $53,$24,$2c,$53,$2c,$02,$29,$00,$05,$54,$01,$8d,$00,$0a,$5e,$01
                                    +      $27,$fe,$7c,$8c,$6c,$62,$00,$2c,$68,$01,$50,$58,$f6,$12,$0d,$00
                                    +      $fd,$09,$f9,$12,$0c,$00,$3a,$50,$59,$f6,$03,$fd,$09,$f9,$12,$10
                                    +      $00,$3a,$43,$f6,$04,$3a,$54,$f6,$01,$3a,$46,$57,$f6,$01,$3a,$46
                                    +      $f6,$01,$00,$1d,$72,$01,$4b,$4e,$f6,$03,$3a,$57,$f6,$12,$db,$00
                                    +      $3a,$4b,$58,$f6,$01,$3a,$4b,$59,$f6,$01,$3a,$4b,$44,$f6,$01,$00
                                    +      $28,$7c,$01,$ae,$20,$01,$2c,$01,$3a,$8b,$20,$22,$20,$20,$20,$8b
                                    +      $74,$78,$65,$9e,$22,$3b,$43,$3b,$22,$20,$20,$20,$20,$20,$20,$20
                                    +      $73,$67,$72,$8c,$22,$3b,$54,$00,$28,$86,$01,$aa,$20,$a1,$28,$08
                                    +      $29,$f6,$8a,$20,$28,$05,$2c,$01,$2c,$03,$2c,$05,$2c,$01,$2c,$01
                                    +      $29,$3a,$a7,$20,$08,$2c,$50,$58,$2c,$50,$59,$3a,$a1,$20,$08,$00
                                    +      $0d,$90,$01,$94,$20,$45,$28,$4b,$4e,$fa,$02,$29,$00,$0a,$9a,$01
                                    +      $27,$fe,$65,$75,$a4,$88,$00,$10,$a4,$01,$8c,$20,$46,$f6,$01,$20
                                    +      $88,$20,$4b,$4e,$fa,$02,$00,$1b,$ae,$01,$aa,$20,$a1,$28,$46,$29
                                    +      $f6,$8a,$28,$12,$0e,$00,$2c,$02,$2c,$03,$2c,$05,$2c,$01,$2c,$04
                                    +      $29,$00,$43,$b8,$01,$53,$58,$f6,$02,$f9,$d0,$28,$12,$1a,$00,$29
                                    +      $3a,$53,$59,$f6,$12,$0a,$00,$f9,$d0,$28,$12,$0a,$00,$29,$3a,$92
                                    +      $20,$e1,$28,$53,$58,$2c,$53,$59,$29,$f6,$22,$20,$22,$20,$f0,$20
                                    +      $e1,$28,$53,$58,$2c,$53,$59,$fa,$02,$29,$f6,$22,$20,$22,$20,$80
                                    +      $20,$0b,$b8,$01,$00,$1d,$c2,$01,$a7,$20,$46,$2c,$53,$58,$fd,$09
                                    +      $f9,$12,$0c,$00,$2c,$53,$59,$fd,$09,$f9,$12,$18,$00,$3a,$a1,$20
                                    +      $46,$00,$05,$cc,$01,$8d,$00,$0e,$d6,$01,$27,$fe,$fe,$fe,$81,$61
                                    +      $8c,$fe,$fe,$fe,$00,$0e,$e0,$01,$27,$fe,$fe,$fe,$fe,$fe,$ae,$8c
                                    +      $98,$8c,$00,$0e,$f4,$01,$27,$fe,$66,$2d,$61,$8c,$2f,$62,$9b,$67
                                    +      $00,$24,$fe,$01,$53,$f6,$01,$3a,$53,$58,$f6,$01,$3a,$53,$59,$f6
                                    +      $01,$3a,$4b,$f6,$d5,$28,$01,$29,$3a,$92,$20,$4b,$f6,$01,$20,$80
                                    +      $20,$0b,$4e,$02,$00,$14,$08,$02,$92,$20,$4b,$f6,$02,$20,$85,$20
                                    +      $53,$f6,$04,$3a,$53,$58,$f6,$02,$00,$15,$12,$02,$92,$20,$4b,$f6
                                    +      $03,$20,$85,$20,$53,$f6,$08,$3a,$53,$58,$f6,$fa,$02,$00,$14,$1c
                                    +      $02,$92,$20,$4b,$f6,$05,$20,$85,$20,$53,$f6,$06,$3a,$53,$59,$f6
                                    +      $02,$00,$15,$26,$02,$92,$20,$4b,$f6,$09,$20,$85,$20,$53,$f6,$02
                                    +      $3a,$53,$59,$f6,$fa,$02,$00,$23,$2b,$02,$50,$58,$f6,$28,$d7,$28
                                    +      $08,$29,$fa,$12,$0c,$00,$29,$fc,$09,$3a,$50,$59,$f6,$28,$d8,$28
                                    +      $08,$29,$fa,$12,$10,$00,$29,$fc,$09,$00,$15,$30,$02,$53,$24,$f6
                                    +      $e1,$28,$50,$58,$f9,$53,$58,$2c,$50,$59,$f9,$53,$59,$29,$00,$18
                                    +      $3a,$02,$92,$20,$53,$f6,$01,$20,$f0,$20,$53,$24,$f6,$22,$20,$22
                                    +      $20,$80,$20,$0b,$4e,$02,$00,$34,$44,$02,$aa,$20,$a1,$28,$08,$29
                                    +      $f6,$8a,$20,$28,$05,$2c,$53,$2c,$03,$2c,$05,$2c,$01,$2c,$01,$29
                                    +      $3a,$a7,$20,$08,$2c,$50,$58,$fd,$09,$f9,$12,$0c,$00,$2c,$50,$59
                                    +      $fd,$09,$f9,$12,$10,$00,$3a,$a1,$20,$08,$00,$0c,$4e,$02,$27,$fe
                                    +      $6c,$62,$9d,$7a,$8a,$62,$00,$1a,$58,$02,$92,$20,$53,$24,$f3,$d4
                                    +      $28,$50,$53,$24,$2c,$50,$53,$2c,$02,$29,$20,$80,$20,$0b,$8a,$02
                                    +      $00,$18,$62,$02,$a3,$20,$22,$4f,$34,$43,$30,$23,$43,$44,$23,$44
                                    +      $45,$46,$23,$46,$47,$23,$47,$22,$00,$1b,$6c,$02,$ae,$20,$50,$58
                                    +      $f9,$53,$58,$2c,$50,$59,$f9,$53,$59,$3a,$8b,$20,$dc,$28,$12,$c7
                                    +      $00,$29,$3b,$00,$17,$76,$02,$54,$f6,$54,$f9,$12,$0a,$00,$3a,$ae
                                    +      $20,$12,$15,$00,$2c,$01,$3a,$8b,$20,$54,$00,$31,$80,$02,$50,$53
                                    +      $f6,$50,$53,$f9,$02,$3a,$92,$20,$50,$53,$f7,$50,$4c,$20,$85,$20
                                    +      $a3,$20,$22,$4f,$34,$43,$33,$44,$45,$46,$47,$3a,$4f,$34,$45,$33
                                    +      $46,$47,$41,$42,$22,$3a,$80,$20,$0b,$60,$04,$00,$0a,$8a,$02,$27
                                    +      $fe,$8b,$74,$64,$67,$00,$2d,$94,$02,$92,$20,$d6,$28,$01,$29,$f3
                                    +      $09,$20,$f0,$20,$e2,$28,$50,$53,$24,$2c,$53,$24,$29,$f3,$01,$20
                                    +      $f0,$20,$43,$f8,$02,$20,$f0,$20,$46,$57,$f6,$02,$20,$80,$20,$0b
                                    +      $b2,$02,$00,$28,$9e,$02,$a3,$20,$22,$4f,$32,$45,$32,$43,$22,$3a
                                    +      $57,$24,$f6,$53,$24,$3a,$ae,$20,$50,$58,$f9,$53,$58,$2c,$50,$59
                                    +      $f9,$53,$59,$3a,$8b,$20,$dc,$28,$57,$29,$00,$18,$a8,$02,$46,$57
                                    +      $f6,$02,$3a,$43,$f6,$43,$fa,$02,$3a,$ae,$20,$09,$2c,$01,$3a,$8b
                                    +      $20,$43,$00,$0c,$b2,$02,$27,$fe,$fe,$fe,$fe,$fe,$65,$75,$00,$18
                                    +      $bc,$02,$46,$f6,$46,$f9,$02,$3a,$92,$20,$46,$f7,$4b,$4e,$fa,$02
                                    +      $20,$85,$20,$46,$f6,$01,$00,$0c,$c6,$02,$27,$fe,$6b,$a7,$89,$72
                                    +      $88,$3f,$00,$18,$d0,$02,$4b,$44,$f6,$e6,$28,$46,$29,$3a,$92,$20
                                    +      $4b,$44,$f7,$01,$20,$80,$20,$0b,$f8,$02,$00,$22,$da,$02,$92,$20
                                    +      $a1,$28,$46,$29,$f3,$01,$20,$85,$20,$a3,$20,$22,$4f,$30,$43,$30
                                    +      $45,$47,$43,$45,$47,$22,$3a,$80,$20,$0b,$38,$04,$00,$0c,$e4,$02
                                    +      $27,$fe,$6b,$a7,$89,$64,$8b,$87,$00,$0b,$ee,$02,$4b,$44,$f6,$45
                                    +      $28,$46,$29,$00,$0a,$f8,$02,$27,$fe,$9c,$7a,$95,$62,$00,$1f,$02
                                    +      $03,$4b,$58,$f6,$28,$d7,$28,$46,$29,$fa,$12,$0c,$00,$29,$fc,$09
                                    +      $3a,$4a,$58,$f6,$d1,$28,$50,$58,$fa,$4b,$58,$29,$00,$1f,$0c,$03
                                    +      $4b,$59,$f6,$28,$d8,$28,$46,$29,$fa,$12,$10,$00,$29,$fc,$09,$3a
                                    +      $4a,$59,$f6,$d1,$28,$50,$59,$fa,$4b,$59,$29,$00,$4c,$16,$03,$53
                                    +      $4b,$24,$f6,$e1,$28,$4b,$58,$2c,$4b,$59,$29,$3a,$53,$52,$24,$f6
                                    +      $e1,$28,$4b,$58,$f9,$02,$2c,$4b,$59,$29,$3a,$53,$4c,$24,$f6,$e1
                                    +      $28,$4b,$58,$fa,$02,$2c,$4b,$59,$29,$3a,$53,$44,$24,$f6,$e1,$28
                                    +      $4b,$58,$2c,$4b,$59,$f9,$02,$29,$3a,$53,$55,$24,$f6,$e1,$28,$4b
                                    +      $58,$2c,$4b,$59,$fa,$02,$29,$00,$09,$20,$03,$27,$fe,$8b,$74,$3f
                                    +      $00,$14,$2a,$03,$92,$20,$53,$4b,$24,$f3,$dc,$28,$57,$29,$20,$80
                                    +      $20,$0b,$52,$03,$00,$38,$34,$03,$aa,$20,$a1,$28,$46,$29,$f6,$8a
                                    +      $20,$28,$12,$0e,$00,$2c,$01,$2c,$02,$2c,$12,$c8,$00,$2c,$02,$2c
                                    +      $03,$29,$3a,$a7,$20,$46,$2c,$4b,$58,$fd,$09,$f9,$12,$0c,$00,$2c
                                    +      $4b,$59,$fd,$09,$f9,$12,$10,$00,$3a,$a1,$20,$46,$00,$1d,$3e,$03
                                    +      $45,$28,$46,$29,$f6,$4b,$44,$3a,$ae,$20,$4b,$58,$2c,$4b,$59,$3a
                                    +      $8b,$20,$57,$24,$3a,$46,$57,$f6,$01,$00,$09,$48,$03,$80,$20,$0b
                                    +      $38,$04,$00,$0d,$52,$03,$27,$fe,$fe,$fe,$64,$61,$65,$68,$88,$00
                                    +      $28,$5c,$03,$92,$28,$53,$52,$24,$f9,$53,$4c,$24,$f3,$22,$20,$20
                                    +      $22,$20,$f1,$20,$53,$44,$24,$f9,$53,$55,$24,$f3,$22,$20,$20,$22
                                    +      $29,$20,$80,$20,$0b,$8e,$03,$00,$0c,$66,$03,$27,$fe,$fe,$70,$95
                                    +      $67,$6b,$8c,$00,$44,$70,$03,$92,$28,$4b,$44,$f6,$04,$20,$f1,$20
                                    +      $4a,$58,$f6,$02,$29,$f0,$28,$4b,$44,$f6,$08,$20,$f1,$20,$4a,$58
                                    +      $f6,$fa,$02,$29,$f0,$28,$4b,$44,$f6,$06,$20,$f1,$20,$4a,$59,$f6
                                    +      $02,$29,$f0,$28,$4b,$44,$f6,$02,$20,$f1,$20,$4a,$59,$f6,$fa,$02
                                    +      $29,$20,$80,$20,$0b,$24,$04,$00,$0a,$7a,$03,$27,$fe,$79,$8c,$72
                                    +      $8c,$00,$1c,$84,$03,$4b,$44,$f6,$28,$4b,$44,$f9,$05,$29,$f9,$09
                                    +      $fd,$28,$4b,$44,$f7,$04,$29,$3a,$80,$20,$0b,$24,$04,$00,$0a,$8e
                                    +      $03,$27,$fe,$fe,$a8,$8c,$66,$00,$09,$98,$03,$27,$fe,$61,$68,$88
                                    +      $00,$1c,$a2,$03,$92,$20,$4a,$58,$fd,$4a,$59,$f3,$01,$20,$f1,$20
                                    +      $d0,$28,$03,$29,$f6,$02,$20,$80,$20,$0b,$c0,$03,$00,$21,$ac,$03
                                    +      $92,$20,$4a,$58,$f6,$02,$20,$f1,$20,$53,$52,$24,$f3,$22,$20,$22
                                    +      $20,$85,$20,$4b,$44,$f6,$04,$3a,$80,$20,$0b,$24,$04,$00,$22,$b6
                                    +      $03,$92,$20,$4a,$58,$f6,$fa,$02,$20,$f1,$20,$53,$4c,$24,$f3,$22
                                    +      $20,$22,$20,$85,$20,$4b,$44,$f6,$08,$3a,$80,$20,$0b,$24,$04,$00
                                    +      $21,$c0,$03,$92,$20,$4a,$59,$f6,$02,$20,$f1,$20,$53,$44,$24,$f3
                                    +      $22,$20,$22,$20,$85,$20,$4b,$44,$f6,$06,$3a,$80,$20,$0b,$24,$04
                                    +      $00,$22,$ca,$03,$92,$20,$4a,$59,$f6,$fa,$02,$20,$f1,$20,$53,$55
                                    +      $24,$f3,$22,$20,$22,$20,$85,$20,$4b,$44,$f6,$02,$3a,$80,$20,$0b
                                    +      $24,$04,$00,$0a,$d4,$03,$27,$fe,$61,$68,$74,$61,$00,$0c,$de,$03
                                    +      $53,$f6,$d0,$28,$05,$29,$f9,$02,$00,$19,$e8,$03,$9a,$20,$53,$20
                                    +      $80,$20,$0b,$f2,$03,$2c,$0b,$fc,$03,$2c,$0b,$06,$04,$2c,$0b,$10
                                    +      $04,$00,$1a,$f2,$03,$92,$20,$53,$52,$24,$f3,$22,$20,$22,$20,$85
                                    +      $20,$4b,$44,$f6,$04,$20,$80,$20,$0b,$24,$04,$00,$1a,$fc,$03,$92
                                    +      $20,$53,$4c,$24,$f3,$22,$20,$22,$20,$85,$20,$4b,$44,$f6,$08,$20
                                    +      $80,$20,$0b,$24,$04,$00,$1a,$06,$04,$92,$20,$53,$44,$24,$f3,$22
                                    +      $20,$22,$20,$85,$20,$4b,$44,$f6,$06,$20,$80,$20,$0b,$24,$04,$00
                                    +      $1a,$10,$04,$92,$20,$53,$55,$24,$f3,$22,$20,$22,$20,$85,$20,$4b
                                    +      $44,$f6,$02,$20,$80,$20,$0b,$24,$04,$00,$09,$1a,$04,$80,$20,$0b
                                    +      $f2,$03,$00,$09,$24,$04,$27,$fe,$62,$9b,$67,$00,$37,$2e,$04,$aa
                                    +      $20,$a1,$28,$46,$29,$f6,$8a,$20,$28,$12,$0e,$00,$2c,$4b,$44,$2c
                                    +      $02,$2c,$05,$2c,$01,$2c,$04,$29,$3a,$a7,$20,$46,$2c,$4b,$58,$fd
                                    +      $09,$f9,$12,$0c,$00,$2c,$4b,$59,$fd,$09,$f9,$12,$10,$00,$3a,$a1
                                    +      $20,$46,$00,$0c,$38,$04,$27,$fe,$a8,$71,$65,$96,$6f,$3f,$00,$23
                                    +      $42,$04,$92,$20,$e3,$28,$08,$29,$f4,$01,$20,$85,$20,$a3,$20,$22
                                    +      $4f,$31,$42,$32,$41,$47,$46,$45,$44,$43,$22,$3a,$80,$20,$0b,$60
                                    +      $04,$00,$07,$4c,$04,$27,$fe,$fe,$00,$09,$56,$04,$80,$20,$0b,$d6
                                    +      $01,$00,$0b,$60,$04,$27,$fe,$fe,$fe,$64,$8b,$87,$00,$21,$6a,$04
                                    +      $8c,$20,$4a,$58,$f6,$07,$20,$88,$20,$09,$3a,$93,$20,$53,$24,$3a
                                    +      $ae,$20,$0a,$2c,$4a,$58,$3a,$8b,$20,$53,$24,$3a,$8d,$00,$07,$74
                                    +      $04,$8a,$20,$9b,$00,$12,$7e,$04,$91,$20,$22,$fd,$fe,$fe,$fe,$fe
                                    +      $fe,$fe,$fe,$fe,$fd,$22,$00,$12,$88,$04,$91,$20,$22,$fe,$fd,$20
                                    +      $64,$6b,$7e,$61,$20,$fd,$fe,$22,$00,$12,$92,$04,$91,$20,$22,$fd
                                    +      $fe,$fe,$fe,$fe,$fe,$fe,$fe,$fe,$fd,$22,$00,$16,$9c,$04,$53,$f6
                                    +      $d6,$28,$01,$29,$3a,$92,$20,$53,$f6,$01,$20,$80,$20,$0b,$9c,$04
                                    +      $00,$0d,$a6,$04,$92,$20,$53,$f6,$02,$20,$85,$20,$82,$00,$05,$b0
                                    +      $04,$a2,$00,$00
                   ; "GAME 2" built-in BASIC program, tokenized
e682: 09 0a 00 9f+ txtGame2        .bulk   $09,$0a,$00,$9f,$20,$02,$2c,$01,$00,$0d,$14,$00,$9a,$20,$bc,$20
                                    +      $80,$20,$0b,$8e,$03,$00,$48,$1e,$00,$57,$f6,$46,$f6,$4b,$f6,$54
                                    +      $f6,$52,$f6,$5a,$f6,$4e,$49,$f6,$4e,$4e,$f6,$58,$45,$f6,$59,$45
                                    +      $f6,$58,$4e,$f6,$59,$4e,$f6,$58,$42,$f6,$58,$4c,$f6,$59,$4c,$f6
                                    +      $53,$32,$f6,$53,$31,$f6,$53,$f6,$47,$f6,$56,$f6,$58,$f6,$59,$f6
                                    +      $49,$f6,$44,$3a,$48,$f6,$01,$3a,$53,$24,$f6,$47,$24,$00,$1c,$28
                                    +      $00,$af,$42,$20,$04,$2c,$12,$0d,$00,$2c,$12,$30,$00,$2c,$12,$36
                                    +      $00,$2c,$12,$16,$00,$3a,$8a,$20,$9a,$00,$2b,$32,$00,$4b,$24,$f6
                                    +      $dc,$28,$12,$18,$00,$29,$3a,$52,$24,$f6,$dc,$28,$12,$19,$00,$29
                                    +      $3a,$48,$24,$f6,$dc,$28,$12,$1a,$00,$29,$3a,$55,$24,$f6,$dc,$28
                                    +      $12,$1b,$00,$29,$00,$21,$3c,$00,$aa,$20,$8a,$20,$01,$2c,$28,$01
                                    +      $2c,$02,$2c,$01,$2c,$02,$2c,$01,$29,$f6,$52,$24,$f9,$4b,$24,$f9
                                    +      $55,$24,$f9,$48,$24,$00,$2b,$46,$00,$4b,$24,$f6,$dc,$28,$12,$0c
                                    +      $00,$29,$3a,$52,$24,$f6,$dc,$28,$12,$0d,$00,$29,$3a,$48,$24,$f6
                                    +      $dc,$28,$12,$0e,$00,$29,$3a,$55,$24,$f6,$dc,$28,$12,$0f,$00,$29
                                    +      $00,$21,$50,$00,$aa,$20,$8a,$20,$02,$2c,$28,$01,$2c,$02,$2c,$01
                                    +      $2c,$02,$2c,$01,$29,$f6,$52,$24,$f9,$4b,$24,$f9,$55,$24,$f9,$48
                                    +      $24,$00,$21,$5a,$00,$aa,$20,$8a,$20,$03,$2c,$28,$01,$2c,$02,$2c
                                    +      $01,$2c,$01,$2c,$01,$29,$f6,$4b,$24,$f9,$52,$24,$f9,$48,$24,$f9
                                    +      $55,$24,$00,$2b,$64,$00,$4b,$24,$f6,$dc,$28,$12,$14,$00,$29,$3a
                                    +      $52,$24,$f6,$dc,$28,$12,$15,$00,$29,$3a,$48,$24,$f6,$dc,$28,$12
                                    +      $16,$00,$29,$3a,$55,$24,$f6,$dc,$28,$12,$17,$00,$29,$00,$21,$6e
                                    +      $00,$aa,$20,$8a,$20,$04,$2c,$28,$01,$2c,$02,$2c,$01,$2c,$01,$2c
                                    +      $01,$29,$f6,$4b,$24,$f9,$52,$24,$f9,$48,$24,$f9,$55,$24,$00,$37
                                    +      $78,$00,$4b,$24,$f6,$dc,$28,$12,$e3,$00,$29,$3a,$52,$24,$f6,$dc
                                    +      $28,$12,$d7,$00,$29,$3a,$48,$24,$f6,$dc,$28,$12,$d2,$00,$29,$3a
                                    +      $55,$24,$f6,$dc,$28,$12,$c0,$00,$29,$3a,$94,$20,$51,$28,$04,$29
                                    +      $2c,$44,$28,$04,$29,$00,$0d,$82,$00,$27,$6b,$95,$66,$20,$6d,$96
                                    +      $72,$61,$00,$36,$8c,$00,$54,$f6,$12,$20,$03,$3a,$57,$f6,$01,$3a
                                    +      $4e,$4e,$f6,$02,$3a,$58,$f6,$12,$74,$00,$3a,$59,$f6,$12,$7f,$00
                                    +      $3a,$a7,$20,$08,$2c,$58,$2c,$59,$3a,$81,$20,$0b,$7a,$03,$3a,$56
                                    +      $f6,$01,$3a,$80,$20,$0b,$1c,$02,$00,$16,$96,$00,$27,$6f,$61,$80
                                    +      $20,$73,$20,$73,$67,$72,$8c,$20,$78,$20,$79,$8c,$72,$61,$00,$36
                                    +      $a0,$00,$54,$f6,$54,$fa,$02,$3a,$92,$20,$54,$20,$fb,$20,$12,$0a
                                    +      $00,$f6,$01,$20,$85,$20,$ae,$20,$12,$0e,$00,$2c,$12,$15,$00,$3a
                                    +      $8b,$20,$54,$3b,$22,$20,$20,$22,$3a,$92,$20,$54,$f6,$01,$20,$85
                                    +      $20,$0b,$94,$02,$00,$1d,$aa,$00,$92,$20,$5a,$f6,$12,$3a,$00,$20
                                    +      $85,$20,$92,$20,$52,$f7,$12,$0f,$00,$20,$85,$20,$81,$20,$0b,$7a
                                    +      $03,$00,$11,$b4,$00,$27,$75,$6f,$75,$6f,$20,$78,$20,$ad,$8a,$99
                                    +      $86,$80,$00,$13,$be,$00,$49,$f6,$fa,$28,$49,$f9,$02,$29,$fd,$28
                                    +      $49,$f3,$4e,$4e,$29,$00,$13,$c8,$00,$58,$4e,$f6,$d7,$28,$49,$29
                                    +      $3a,$59,$4e,$f6,$d8,$28,$49,$29,$00,$50,$d2,$00,$92,$20,$58,$4e
                                    +      $f8,$12,$0c,$00,$20,$f0,$20,$58,$4e,$f7,$12,$de,$00,$20,$f0,$20
                                    +      $59,$4e,$f7,$12,$bc,$00,$20,$85,$20,$58,$4e,$f6,$d0,$28,$12,$1b
                                    +      $00,$29,$fd,$09,$f9,$12,$0c,$00,$3a,$59,$4e,$f6,$08,$3a,$a7,$20
                                    +      $49,$2c,$58,$4e,$2c,$59,$4e,$3a,$44,$f6,$01,$3a,$51,$28,$49,$29
                                    +      $f6,$06,$3a,$80,$20,$0b,$18,$01,$00,$1b,$dc,$00,$58,$45,$f6,$28
                                    +      $58,$4e,$fa,$09,$29,$fc,$09,$3a,$59,$45,$f6,$28,$59,$4e,$fa,$03
                                    +      $29,$fc,$09,$00,$0f,$e6,$00,$47,$24,$f6,$e1,$28,$58,$45,$2c,$59
                                    +      $45,$29,$00,$0a,$f0,$00,$44,$f6,$51,$28,$49,$29,$00,$39,$fa,$00
                                    +      $92,$20,$47,$24,$f3,$55,$24,$20,$85,$20,$92,$20,$59,$4c,$f3,$59
                                    +      $45,$20,$f0,$20,$47,$24,$f3,$48,$24,$20,$85,$20,$44,$f6,$06,$3a
                                    +      $51,$28,$49,$29,$f6,$04,$fa,$05,$fd,$28,$58,$f8,$58,$4e,$29,$3a
                                    +      $80,$20,$0b,$0e,$01,$00,$12,$04,$01,$92,$20,$44,$28,$49,$29,$f6
                                    +      $44,$20,$85,$20,$0b,$2c,$01,$00,$23,$0e,$01,$58,$4e,$f6,$58,$45
                                    +      $fd,$09,$f9,$12,$0c,$00,$3a,$59,$4e,$f6,$59,$45,$fd,$09,$f9,$08
                                    +      $3a,$a7,$20,$49,$2c,$58,$4e,$2c,$59,$4e,$00,$2e,$18,$01,$aa,$20
                                    +      $a1,$28,$49,$29,$f6,$8a,$28,$12,$0b,$00,$2c,$44,$2c,$03,$2c,$12
                                    +      $ff,$00,$f9,$12,$fb,$00,$fd,$28,$44,$f6,$06,$29,$29,$3a,$a1,$20
                                    +      $49,$3a,$44,$28,$49,$29,$f6,$44,$00,$10,$22,$01,$27,$a8,$71,$65
                                    +      $87,$20,$78,$20,$79,$8c,$72,$61,$00,$43,$2c,$01,$4e,$49,$f6,$e3
                                    +      $28,$08,$29,$3a,$92,$20,$4e,$49,$f7,$fa,$02,$20,$85,$20,$92,$20
                                    +      $ca,$28,$d8,$28,$4e,$49,$29,$fa,$d8,$28,$08,$29,$29,$f8,$12,$0e
                                    +      $00,$20,$f1,$20,$ca,$28,$d7,$28,$4e,$49,$29,$fa,$d7,$28,$08,$29
                                    +      $29,$f8,$12,$0e,$00,$20,$85,$20,$0b,$94,$02,$00,$0f,$36,$01,$92
                                    +      $20,$46,$f6,$02,$20,$85,$20,$0b,$0c,$03,$00,$10,$40,$01,$27,$7e
                                    +      $87,$64,$20,$78,$20,$ad,$8a,$99,$86,$80,$00,$21,$4a,$01,$53,$31
                                    +      $f6,$53,$3a,$53,$f6,$d5,$28,$01,$29,$3a,$92,$20,$53,$f7,$03,$20
                                    +      $85,$20,$53,$f6,$04,$fa,$28,$53,$f7,$08,$29,$00,$11,$54,$01,$58
                                    +      $f6,$d7,$28,$08,$29,$3a,$59,$f6,$d8,$28,$08,$29,$00,$17,$5e,$01
                                    +      $58,$4c,$f6,$58,$fc,$09,$fa,$02,$3a,$59,$4c,$f6,$28,$59,$fa,$03
                                    +      $29,$fc,$09,$00,$19,$68,$01,$9a,$20,$53,$20,$80,$20,$0b,$26,$02
                                    +      $2c,$0b,$26,$02,$2c,$0b,$e0,$01,$2c,$0b,$fe,$01,$00,$05,$72,$01
                                    +      $27,$00,$0f,$7c,$01,$56,$f6,$01,$3a,$4b,$f6,$01,$3a,$9c,$20,$08
                                    +      $00,$32,$86,$01,$92,$20,$e1,$28,$58,$4c,$2c,$59,$4c,$29,$f6,$22
                                    +      $20,$22,$20,$85,$20,$92,$20,$e1,$28,$58,$4c,$2c,$59,$4c,$fa,$02
                                    +      $29,$f3,$48,$24,$20,$85,$20,$81,$20,$0b,$66,$03,$3a,$80,$20,$0b
                                    +      $ee,$02,$00,$2f,$90,$01,$58,$42,$f6,$58,$4c,$f9,$28,$53,$31,$f6
                                    +      $02,$29,$fa,$28,$53,$31,$f6,$03,$29,$3a,$92,$20,$e1,$28,$58,$42
                                    +      $2c,$59,$4c,$fa,$02,$29,$f6,$52,$24,$20,$85,$20,$81,$20,$0b,$70
                                    +      $03,$00,$22,$9a,$01,$92,$20,$e1,$28,$58,$4c,$2c,$59,$4c,$fa,$02
                                    +      $29,$f6,$4b,$24,$20,$85,$20,$47,$f6,$fa,$12,$0f,$00,$3a,$80,$20
                                    +      $0b,$02,$03,$00,$44,$a4,$01,$92,$20,$cb,$28,$e1,$28,$58,$4c,$2c
                                    +      $59,$4c,$fa,$02,$29,$29,$f6,$5a,$20,$85,$20,$ae,$20,$58,$4c,$2c
                                    +      $59,$4c,$fa,$02,$3a,$8b,$20,$22,$20,$22,$3a,$5a,$f6,$5a,$f9,$02
                                    +      $3a,$57,$f6,$57,$f9,$12,$1e,$00,$3a,$ae,$20,$12,$0e,$00,$2c,$12
                                    +      $17,$00,$3a,$8b,$20,$57,$3b,$00,$12,$ae,$01,$92,$20,$d6,$28,$01
                                    +      $29,$f3,$09,$20,$85,$20,$0b,$d6,$01,$00,$19,$b8,$01,$92,$20,$e1
                                    +      $28,$58,$4c,$fa,$02,$2c,$59,$4c,$29,$f6,$55,$24,$20,$85,$20,$0b
                                    +      $f8,$02,$00,$17,$c2,$01,$92,$20,$e1,$28,$58,$4c,$2c,$59,$4c,$29
                                    +      $f6,$55,$24,$20,$85,$20,$0b,$f8,$02,$00,$19,$cc,$01,$92,$20,$e1
                                    +      $28,$58,$4c,$f9,$02,$2c,$59,$4c,$29,$f6,$55,$24,$20,$85,$20,$0b
                                    +      $f8,$02,$00,$09,$d6,$01,$80,$20,$0b,$a0,$00,$00,$13,$e0,$01,$56
                                    +      $f6,$06,$3a,$47,$24,$f6,$e1,$28,$58,$4c,$2c,$59,$4c,$29,$00,$25
                                    +      $ea,$01,$92,$20,$47,$24,$f3,$48,$24,$20,$85,$20,$92,$20,$47,$24
                                    +      $f3,$55,$24,$20,$85,$20,$53,$f6,$01,$3a,$56,$f6,$01,$3a,$80,$20
                                    +      $0b,$ee,$02,$00,$09,$f4,$01,$80,$20,$0b,$08,$02,$00,$15,$fe,$01
                                    +      $56,$f6,$02,$3a,$47,$24,$f6,$e1,$28,$58,$4c,$2c,$59,$4c,$fa,$02
                                    +      $29,$00,$17,$08,$02,$92,$20,$47,$24,$f3,$48,$24,$20,$85,$20,$53
                                    +      $f6,$01,$3a,$80,$20,$0b,$7c,$01,$00,$1c,$12,$02,$92,$20,$53,$31
                                    +      $f6,$01,$20,$f0,$20,$4b,$f7,$01,$20,$85,$20,$81,$20,$0b,$66,$03
                                    +      $3a,$4b,$f6,$01,$00,$28,$1c,$02,$aa,$20,$a1,$28,$08,$29,$f6,$8a
                                    +      $28,$01,$2c,$56,$2c,$02,$2c,$05,$29,$3a,$a1,$20,$08,$3a,$8a,$20
                                    +      $53,$32,$3a,$8e,$20,$06,$3a,$80,$20,$0b,$a0,$00,$00,$20,$26,$02
                                    +      $56,$f6,$05,$fd,$53,$fa,$02,$3a,$4b,$f6,$02,$fa,$28,$4b,$f9,$28
                                    +      $4b,$f7,$04,$29,$29,$fd,$28,$53,$f6,$53,$31,$29,$00,$25,$30,$02
                                    +      $47,$24,$f6,$e1,$28,$58,$4c,$f9,$02,$f9,$03,$fd,$28,$53,$f6,$03
                                    +      $29,$2c,$59,$4c,$29,$3a,$53,$24,$f6,$e1,$28,$58,$4c,$2c,$59,$4c
                                    +      $29,$00,$33,$3a,$02,$58,$42,$f6,$58,$4c,$f9,$02,$f9,$03,$fd,$28
                                    +      $53,$f6,$02,$29,$3a,$92,$20,$4b,$f7,$03,$20,$85,$20,$92,$20,$e1
                                    +      $28,$58,$42,$2c,$59,$4c,$fa,$02,$29,$f6,$52,$24,$20,$85,$20,$81
                                    +      $20,$0b,$70,$03,$00,$21,$44,$02,$92,$20,$e1,$28,$58,$4c,$2c,$59
                                    +      $4c,$fa,$02,$29,$f6,$52,$24,$20,$85,$20,$58,$42,$f6,$58,$4c,$3a
                                    +      $81,$20,$0b,$70,$03,$00,$38,$4e,$02,$92,$20,$47,$24,$f3,$55,$24
                                    +      $20,$f1,$20,$47,$24,$f3,$48,$24,$20,$85,$20,$58,$f6,$58,$f9,$07
                                    +      $fd,$28,$53,$24,$f3,$22,$20,$22,$29,$fd,$28,$28,$53,$f6,$02,$29
                                    +      $fa,$28,$53,$f6,$03,$29,$29,$3a,$80,$20,$0b,$ee,$02,$00,$19,$58
                                    +      $02,$92,$20,$53,$24,$f6,$22,$20,$22,$20,$85,$20,$53,$f6,$53,$31
                                    +      $3a,$80,$20,$0b,$ee,$02,$00,$32,$62,$02,$92,$20,$d6,$28,$01,$29
                                    +      $f6,$09,$20,$85,$20,$92,$20,$47,$24,$f6,$55,$24,$20,$f0,$20,$53
                                    +      $24,$f6,$55,$24,$20,$f0,$20,$e1,$28,$58,$42,$2c,$59,$4c,$29,$f6
                                    +      $55,$24,$20,$85,$20,$0b,$f8,$02,$00,$2f,$6c,$02,$4e,$49,$f6,$e3
                                    +      $28,$08,$29,$3a,$92,$20,$4e,$49,$f7,$fa,$02,$20,$85,$20,$92,$20
                                    +      $ca,$28,$d7,$28,$4e,$49,$29,$fa,$d7,$28,$08,$29,$29,$f8,$12,$0e
                                    +      $00,$20,$85,$20,$0b,$94,$02,$00,$2a,$76,$02,$92,$20,$4b,$f8,$05
                                    +      $20,$85,$20,$aa,$20,$a1,$28,$08,$29,$f6,$8a,$28,$01,$2c,$56,$2c
                                    +      $05,$fa,$4b,$2c,$12,$ff,$00,$29,$3a,$a1,$20,$08,$3a,$8a,$20,$53
                                    +      $32,$00,$09,$80,$02,$80,$20,$0b,$a0,$00,$00,$10,$8a,$02,$27,$64
                                    +      $8b,$87,$20,$78,$20,$ad,$8a,$99,$86,$80,$00,$15,$94,$02,$8a,$20
                                    +      $53,$3a,$8a,$20,$53,$31,$3a,$8a,$20,$53,$32,$3a,$b0,$20,$08,$00
                                    +      $23,$9e,$02,$8c,$20,$49,$f6,$01,$20,$88,$20,$4e,$4e,$3a,$b0,$20
                                    +      $49,$3a,$a7,$20,$49,$2c,$01,$2c,$01,$3a,$44,$28,$49,$29,$f6,$01
                                    +      $3a,$8d,$00,$1a,$a8,$02,$ae,$20,$09,$2c,$0a,$3a,$8b,$22,$2a,$2a
                                    +      $2a,$2a,$2a,$2a,$2a,$2a,$2a,$2a,$2a,$2a,$2a,$22,$00,$1c,$b2,$02
                                    +      $ae,$20,$09,$2c,$12,$0a,$00,$3a,$8b,$22,$2a,$20,$47,$41,$4d,$45
                                    +      $20,$4f,$56,$45,$52,$20,$2a,$22,$00,$1c,$bc,$02,$ae,$20,$09,$2c
                                    +      $12,$0b,$00,$3a,$8b,$22,$2a,$2a,$2a,$2a,$2a,$2a,$2a,$2a,$2a,$2a
                                    +      $2a,$2a,$2a,$22,$00,$1e,$c6,$02,$92,$20,$57,$f7,$48,$20,$85,$20
                                    +      $48,$f6,$57,$3a,$ae,$20,$12,$0e,$00,$2c,$12,$16,$00,$3a,$8b,$20
                                    +      $48,$3b,$00,$12,$d0,$02,$92,$20,$d6,$28,$01,$29,$f6,$02,$20,$85
                                    +      $20,$0b,$8c,$00,$00,$09,$da,$02,$80,$20,$0b,$d0,$02,$00,$11,$e4
                                    +      $02,$27,$9d,$93,$8c,$ad,$20,$78,$20,$ad,$8a,$99,$86,$80,$00,$13
                                    +      $ee,$02,$47,$f6,$03,$3a,$59,$f6,$59,$f9,$04,$3a,$80,$20,$0b,$02
                                    +      $03,$00,$2a,$f8,$02,$47,$f6,$fa,$0a,$3a,$58,$f6,$58,$f9,$28,$03
                                    +      $fd,$4b,$f9,$07,$29,$fd,$28,$28,$53,$f6,$03,$29,$fa,$28,$53,$f6
                                    +      $02,$29,$29,$3a,$59,$f6,$59,$fa,$12,$0c,$00,$00,$3d,$02,$03,$46
                                    +      $f6,$02,$3a,$8a,$20,$53,$2c,$58,$2c,$59,$3a,$b0,$20,$08,$3a,$3a
                                    +      $8a,$20,$04,$3a,$a7,$20,$08,$2c,$58,$2c,$59,$3a,$53,$32,$f6,$53
                                    +      $3a,$4b,$f6,$4b,$fd,$28,$28,$53,$f6,$03,$29,$fa,$28,$53,$f6,$02
                                    +      $29,$29,$3a,$80,$20,$0b,$a0,$00,$00,$2c,$0c,$03,$4e,$49,$f6,$e3
                                    +      $28,$08,$29,$3a,$92,$20,$4e,$49,$f7,$fa,$02,$20,$85,$20,$92,$20
                                    +      $ca,$28,$d8,$28,$4e,$49,$29,$fa,$59,$29,$f8,$12,$0c,$00,$20,$85
                                    +      $20,$0b,$94,$02,$00,$2d,$16,$03,$47,$f6,$47,$f9,$04,$f9,$04,$fd
                                    +      $28,$47,$f7,$07,$29,$3a,$58,$f6,$58,$f9,$4b,$3a,$59,$f6,$59,$f9
                                    +      $47,$3a,$8a,$20,$53,$2c,$58,$2c,$59,$3a,$a7,$20,$08,$2c,$58,$2c
                                    +      $59,$00,$31,$20,$03,$58,$4c,$f6,$58,$fc,$09,$fa,$02,$3a,$59,$4c
                                    +      $f6,$28,$59,$fa,$03,$29,$fc,$09,$3a,$47,$24,$f6,$e1,$28,$58,$4c
                                    +      $2c,$59,$4c,$29,$3a,$53,$24,$f6,$e1,$28,$58,$4c,$2c,$59,$4c,$fa
                                    +      $02,$29,$00,$1d,$2a,$03,$92,$20,$47,$24,$f6,$55,$24,$20,$85,$20
                                    +      $81,$20,$0b,$5c,$03,$3a,$4b,$f6,$02,$3a,$80,$20,$0b,$1c,$02,$00
                                    +      $2b,$34,$03,$92,$20,$53,$24,$f6,$48,$24,$20,$85,$20,$81,$20,$0b
                                    +      $5c,$03,$3a,$8a,$20,$04,$2c,$58,$2c,$59,$3a,$8a,$20,$53,$32,$3a
                                    +      $53,$32,$f6,$04,$3a,$80,$20,$0b,$a0,$00,$00,$1d,$3e,$03,$92,$20
                                    +      $47,$24,$f6,$48,$24,$20,$85,$20,$81,$20,$0b,$5c,$03,$3a,$56,$f6
                                    +      $01,$3a,$80,$20,$0b,$1c,$02,$00,$09,$48,$03,$80,$20,$0b,$0c,$03
                                    +      $00,$10,$52,$03,$27,$53,$55,$42,$20,$78,$20,$ad,$8a,$99,$86,$80
                                    +      $00,$1b,$5c,$03,$81,$20,$0b,$66,$03,$3a,$46,$f6,$01,$3a,$4b,$f6
                                    +      $01,$3a,$8a,$20,$53,$2c,$58,$2c,$59,$3a,$83,$00,$25,$66,$03,$9c
                                    +      $20,$08,$3a,$58,$f6,$58,$4c,$fd,$09,$f9,$12,$0c,$00,$3a,$59,$f6
                                    +      $59,$4c,$fd,$09,$f9,$08,$3a,$a7,$20,$08,$2c,$58,$2c,$59,$3a,$83
                                    +      $00,$32,$70,$03,$ae,$20,$58,$42,$2c,$59,$4c,$fa,$02,$3a,$8b,$20
                                    +      $22,$20,$22,$3a,$52,$f6,$52,$f9,$02,$3a,$57,$f6,$57,$f9,$12,$0f
                                    +      $00,$3a,$ae,$20,$12,$0e,$00,$2c,$12,$17,$00,$3a,$8b,$20,$57,$3b
                                    +      $3a,$83,$00,$5d,$7a,$03,$a0,$3a,$49,$f6,$01,$3a,$46,$f6,$01,$3a
                                    +      $4b,$f6,$01,$3a,$43,$f6,$01,$3a,$52,$f6,$01,$3a,$5a,$f6,$12,$30
                                    +      $00,$3a,$ae,$20,$12,$0a,$00,$2c,$12,$15,$00,$3a,$8b,$22,$54,$49
                                    +      $4d,$45,$22,$3b,$54,$3a,$ae,$20,$07,$2c,$12,$16,$00,$3a,$8b,$22
                                    +      $48,$49,$2d,$53,$43,$4f,$52,$45,$22,$3b,$48,$3a,$ae,$20,$0a,$2c
                                    +      $12,$17,$00,$3a,$8b,$22,$53,$43,$4f,$52,$45,$22,$3b,$57,$3b,$00
                                    +      $05,$84,$03,$83,$00,$14,$8e,$03,$27,$63,$86,$2d,$20,$6b,$95,$87
                                    +      $20,$78,$20,$ad,$8a,$99,$86,$80,$00,$2c,$98,$03,$92,$20,$e5,$f6
                                    +      $12,$30,$02,$20,$85,$20,$58,$4c,$f6,$fa,$12,$1b,$00,$fd,$28,$58
                                    +      $4c,$f8,$02,$29,$3a,$81,$20,$0b,$66,$03,$3a,$a1,$20,$08,$3a,$bd
                                    +      $20,$0b,$76,$02,$00,$2d,$a2,$03,$92,$20,$e5,$f6,$12,$86,$01,$20
                                    +      $85,$20,$58,$4c,$f6,$fa,$12,$1b,$00,$fd,$28,$58,$4c,$f8,$01,$29
                                    +      $3a,$81,$20,$0b,$66,$03,$3a,$53,$f6,$53,$31,$3a,$bd,$20,$0b,$26
                                    +      $02,$00,$14,$ac,$03,$92,$20,$59,$4c,$f7,$12,$17,$00,$20,$85,$20
                                    +      $bd,$20,$0b,$94,$02,$00,$1b,$b6,$03,$92,$20,$58,$4c,$f7,$12,$1b
                                    +      $00,$20,$85,$20,$58,$4c,$f6,$01,$3a,$81,$20,$0b,$66,$03,$3a,$bd
                                    +      $00,$1b,$c0,$03,$92,$20,$58,$4c,$f8,$01,$20,$85,$20,$58,$4c,$f6
                                    +      $12,$1b,$00,$3a,$81,$20,$0b,$66,$03,$3a,$bd,$00,$18,$ca,$03,$92
                                    +      $20,$58,$42,$f7,$12,$1b,$00,$20,$85,$20,$58,$42,$f6,$01,$3a,$a1
                                    +      $08,$3a,$bd,$00,$18,$d4,$03,$92,$20,$58,$42,$f8,$01,$20,$85,$20
                                    +      $58,$42,$f6,$12,$1b,$00,$3a,$a1,$08,$3a,$bd,$00,$1d,$de,$03,$92
                                    +      $20,$58,$4c,$f9,$02,$f7,$12,$1b,$00,$20,$85,$20,$58,$4c,$f6,$01
                                    +      $3a,$81,$20,$0b,$66,$03,$3a,$bd,$00,$1d,$e8,$03,$92,$20,$58,$4c
                                    +      $fa,$02,$f8,$01,$20,$85,$20,$58,$4c,$f6,$12,$1b,$00,$3a,$81,$20
                                    +      $0b,$66,$03,$3a,$bd,$00,$15,$f2,$03,$92,$20,$58,$f8,$12,$0c,$00
                                    +      $20,$85,$20,$58,$f6,$12,$e4,$00,$3a,$bd,$00,$15,$fc,$03,$92,$20
                                    +      $58,$f7,$12,$e4,$00,$20,$85,$20,$58,$f6,$12,$0c,$00,$3a,$bd,$00
                                    +      $05,$06,$04,$a2,$00,$00
                   ; "GAME 3" built-in BASIC program, tokenized
f308: 13 0a 00 27+ txtGame3        .bulk   $13,$0a,$00,$27,$22,$41,$52,$45,$41,$20,$38,$38,$20,$56,$37,$2e
                                    +      $33,$22,$00,$37,$14,$00,$54,$f6,$4f,$f6,$55,$f6,$43,$f6,$48,$f6
                                    +      $44,$f6,$49,$f6,$4b,$f6,$4c,$f6,$4d,$f6,$4e,$f6,$50,$f6,$53,$f6
                                    +      $56,$f6,$58,$f6,$59,$f6,$4d,$41,$f6,$58,$30,$f6,$58,$31,$f6,$59
                                    +      $30,$f6,$59,$31,$f6,$50,$50,$f6,$51,$00,$1f,$1e,$00,$94,$20,$44
                                    +      $28,$02,$29,$2c,$56,$28,$02,$29,$2c,$46,$28,$09,$29,$2c,$53,$43
                                    +      $28,$02,$29,$2c,$53,$28,$02,$29,$00,$26,$28,$00,$4c,$f6,$0a,$3a
                                    +      $4d,$41,$f6,$03,$3a,$4e,$f6,$04,$3a,$56,$f6,$08,$3a,$43,$f6,$08
                                    +      $3a,$53,$28,$01,$29,$f6,$05,$3a,$53,$28,$02,$29,$f6,$05,$00,$1f
                                    +      $32,$00,$58,$30,$f6,$12,$46,$00,$3a,$59,$30,$f6,$12,$50,$00,$3a
                                    +      $58,$31,$f6,$12,$6e,$00,$3a,$59,$31,$f6,$12,$3c,$00,$00,$3d,$3c
                                    +      $00,$44,$28,$01,$29,$f6,$04,$3a,$56,$28,$01,$29,$f6,$03,$3a,$44
                                    +      $28,$02,$29,$f6,$08,$3a,$56,$28,$02,$29,$f6,$03,$3a,$46,$28,$01
                                    +      $29,$f6,$04,$3a,$46,$28,$02,$29,$f6,$04,$3a,$4d,$41,$f6,$4d,$41
                                    +      $f9,$02,$3a,$55,$f6,$4e,$3a,$4d,$f6,$02,$00,$32,$46,$00,$8a,$20
                                    +      $9a,$3a,$9f,$20,$02,$2c,$02,$3a,$a3,$22,$54,$31,$59,$32,$4d,$31
                                    +      $56,$39,$4f,$33,$52,$31,$22,$3a,$af,$53,$20,$02,$2c,$12,$0d,$00
                                    +      $2c,$12,$30,$00,$2c,$12,$26,$00,$2c,$12,$12,$00,$00,$2a,$50,$00
                                    +      $8c,$20,$49,$f6,$12,$0f,$00,$20,$88,$20,$12,$12,$00,$3a,$41,$24
                                    +      $f6,$41,$24,$f9,$e1,$28,$49,$2c,$12,$16,$00,$29,$3a,$8d,$3a,$48
                                    +      $f6,$d9,$28,$41,$24,$29,$00,$09,$5a,$00,$81,$20,$0b,$2e,$04,$00
                                    +      $2c,$64,$00,$a0,$3a,$8c,$50,$f6,$01,$88,$12,$c8,$00,$3a,$ae,$d0
                                    +      $28,$12,$1b,$00,$29,$2c,$d0,$28,$12,$17,$00,$29,$3a,$8b,$dc,$28
                                    +      $12,$cd,$00,$29,$3b,$3a,$8d,$3a,$50,$f6,$01,$00,$05,$6e,$00,$27
                                    +      $00,$2d,$78,$00,$49,$f6,$50,$f9,$07,$3a,$92,$20,$46,$28,$50,$29
                                    +      $20,$85,$20,$9a,$20,$46,$28,$50,$29,$20,$81,$20,$0b,$9e,$02,$2c
                                    +      $0b,$bc,$02,$2c,$0b,$a8,$02,$3a,$80,$20,$0b,$96,$00,$00,$17,$82
                                    +      $00,$92,$20,$4d,$f6,$50,$20,$85,$20,$81,$20,$0b,$86,$01,$3a,$80
                                    +      $20,$0b,$96,$00,$00,$09,$8c,$00,$81,$20,$0b,$d2,$00,$00,$12,$96
                                    +      $00,$92,$20,$a1,$28,$49,$29,$f6,$01,$20,$85,$20,$b0,$20,$49,$00
                                    +      $09,$a0,$00,$81,$20,$0b,$12,$02,$00,$09,$aa,$00,$81,$20,$0b,$20
                                    +      $03,$00,$16,$b4,$00,$9a,$20,$46,$28,$09,$29,$f9,$02,$20,$81,$20
                                    +      $0b,$ac,$03,$2c,$0b,$10,$04,$00,$0f,$be,$00,$50,$f6,$02,$fa,$50
                                    +      $3a,$80,$20,$0b,$78,$00,$00,$05,$c8,$00,$27,$00,$11,$d2,$00,$53
                                    +      $f6,$d5,$28,$50,$29,$3a,$54,$f6,$d6,$28,$50,$29,$00,$12,$dc,$00
                                    +      $92,$20,$54,$f3,$05,$20,$85,$20,$46,$28,$49,$29,$f6,$02,$00,$0f
                                    +      $e6,$00,$92,$20,$53,$f9,$54,$f6,$01,$20,$85,$20,$83,$00,$0c,$f0
                                    +      $00,$92,$53,$f6,$01,$85,$0b,$5e,$01,$00,$48,$fa,$00,$53,$f6,$fa
                                    +      $02,$fd,$28,$53,$f6,$09,$29,$fa,$04,$fd,$28,$53,$f6,$02,$29,$fa
                                    +      $06,$fd,$28,$53,$f6,$05,$29,$fa,$08,$fd,$28,$53,$f6,$03,$29,$fa
                                    +      $03,$fd,$28,$53,$f6,$0a,$29,$fa,$05,$fd,$28,$53,$f6,$06,$29,$fa
                                    +      $07,$fd,$28,$53,$f6,$07,$29,$fa,$09,$fd,$28,$53,$f6,$12,$0a,$00
                                    +      $29,$00,$0a,$04,$01,$51,$f6,$44,$28,$50,$29,$00,$0f,$0e,$01,$92
                                    +      $20,$53,$f6,$51,$20,$85,$20,$0b,$5e,$01,$00,$0f,$18,$01,$92,$20
                                    +      $51,$f7,$05,$20,$85,$20,$0b,$36,$01,$00,$19,$22,$01,$92,$20,$28
                                    +      $53,$f7,$51,$20,$f1,$20,$53,$f5,$51,$f9,$05,$29,$20,$85,$20,$0b
                                    +      $40,$01,$00,$09,$2c,$01,$80,$20,$0b,$4a,$01,$00,$18,$36,$01,$92
                                    +      $20,$28,$53,$f4,$51,$fa,$05,$20,$f1,$20,$53,$f8,$51,$29,$85,$20
                                    +      $0b,$4a,$01,$00,$0f,$40,$01,$51,$f6,$51,$f9,$02,$3a,$80,$20,$0b
                                    +      $54,$01,$00,$09,$4a,$01,$51,$f6,$51,$fa,$02,$00,$2d,$54,$01,$50
                                    +      $50,$f6,$fa,$02,$fd,$28,$51,$f7,$09,$29,$fa,$09,$fd,$28,$51,$f8
                                    +      $02,$29,$3a,$51,$f6,$fa,$51,$fd,$28,$50,$50,$f6,$01,$29,$f9,$50
                                    +      $50,$3a,$44,$28,$50,$29,$f6,$51,$00,$28,$5e,$01,$92,$20,$54,$20
                                    +      $f1,$05,$20,$85,$20,$92,$20,$46,$28,$49,$29,$20,$85,$20,$56,$28
                                    +      $50,$29,$f6,$56,$fa,$56,$28,$50,$29,$3a,$46,$28,$49,$29,$f6,$01
                                    +      $00,$26,$68,$01,$aa,$20,$a1,$28,$50,$29,$f6,$8a,$20,$28,$50,$f9
                                    +      $09,$2c,$44,$28,$50,$29,$2c,$56,$28,$50,$29,$2c,$12,$ff,$00,$2c
                                    +      $02,$29,$3a,$a1,$20,$50,$00,$4a,$72,$01,$92,$20,$54,$f7,$08,$20
                                    +      $85,$20,$aa,$20,$a1,$28,$49,$29,$f6,$8a,$28,$12,$0c,$00,$2c,$44
                                    +      $28,$50,$29,$2c,$02,$2c,$12,$50,$00,$2c,$01,$2c,$50,$29,$3a,$a7
                                    +      $20,$49,$2c,$28,$d7,$28,$50,$29,$f9,$05,$29,$fb,$12,$00,$01,$2c
                                    +      $28,$d8,$28,$50,$29,$f9,$05,$29,$fb,$12,$00,$01,$3a,$a1,$20,$49
                                    +      $00,$05,$7c,$01,$83,$00,$05,$86,$01,$27,$00,$13,$90,$01,$4b,$f6
                                    +      $4b,$fa,$02,$3a,$92,$20,$4b,$f7,$01,$20,$85,$20,$83,$00,$33,$9a
                                    +      $01,$54,$f6,$01,$3a,$44,$f6,$01,$3a,$58,$f6,$28,$d7,$28,$02,$fa
                                    +      $50,$29,$fa,$d7,$28,$50,$29,$29,$fc,$12,$0d,$00,$3a,$59,$f6,$28
                                    +      $d8,$28,$02,$fa,$50,$29,$fa,$d8,$28,$50,$29,$29,$fc,$12,$0d,$00
                                    +      $00,$0f,$a4,$01,$92,$20,$58,$f7,$01,$20,$85,$20,$44,$f6,$04,$00
                                    +      $0f,$ae,$01,$92,$20,$58,$f8,$01,$20,$85,$20,$44,$f6,$08,$00,$11
                                    +      $b8,$01,$92,$20,$59,$f7,$01,$20,$85,$20,$44,$f6,$44,$f9,$06,$00
                                    +      $1f,$c2,$01,$92,$20,$59,$f8,$01,$20,$85,$20,$44,$f6,$44,$f9,$02
                                    +      $3a,$92,$20,$58,$f8,$01,$20,$85,$20,$44,$f6,$12,$10,$00,$00,$13
                                    +      $cc,$01,$92,$20,$58,$20,$f1,$20,$59,$20,$85,$20,$44,$f6,$44,$fc
                                    +      $03,$00,$29,$d6,$01,$53,$f6,$03,$3a,$4f,$f6,$44,$fa,$44,$28,$50
                                    +      $29,$3a,$92,$28,$01,$f8,$4f,$20,$f1,$20,$4f,$f8,$06,$29,$f0,$20
                                    +      $4f,$f8,$fa,$04,$20,$85,$20,$53,$f6,$02,$00,$0f,$e0,$01,$92,$20
                                    +      $4f,$f6,$01,$20,$85,$20,$53,$f6,$01,$00,$17,$ea,$01,$92,$20,$56
                                    +      $28,$02,$fa,$50,$29,$f3,$56,$28,$50,$29,$20,$85,$20,$54,$f6,$05
                                    +      $00,$1f,$f4,$01,$92,$20,$a1,$28,$49,$29,$f6,$01,$20,$f0,$20,$d0
                                    +      $28,$12,$0a,$00,$29,$f6,$01,$20,$85,$20,$54,$f6,$54,$f9,$09,$00
                                    +      $15,$fe,$01,$92,$20,$58,$fd,$58,$f9,$59,$fd,$59,$f7,$12,$c8,$00
                                    +      $20,$85,$20,$83,$00,$10,$08,$02,$4b,$f6,$d0,$28,$4c,$29,$3a,$80
                                    +      $20,$0b,$dc,$00,$00,$05,$12,$02,$27,$00,$22,$1c,$02,$44,$f6,$e3
                                    +      $28,$01,$29,$3a,$92,$20,$44,$f7,$01,$20,$f1,$20,$44,$f8,$07,$20
                                    +      $85,$20,$4f,$f6,$01,$3a,$81,$20,$0b,$4e,$02,$00,$22,$26,$02,$44
                                    +      $f6,$e3,$28,$02,$29,$3a,$92,$20,$44,$f7,$02,$20,$f1,$20,$44,$f8
                                    +      $07,$20,$85,$20,$4f,$f6,$02,$3a,$81,$20,$0b,$4e,$02,$00,$22,$30
                                    +      $02,$44,$f6,$e3,$28,$07,$29,$3a,$92,$20,$44,$f7,$01,$20,$f1,$20
                                    +      $44,$f8,$07,$20,$85,$20,$4f,$f6,$07,$3a,$81,$20,$0b,$62,$02,$00
                                    +      $29,$3a,$02,$44,$f6,$e3,$28,$08,$29,$3a,$92,$20,$44,$f6,$01,$20
                                    +      $f0,$20,$28,$44,$f7,$02,$20,$f1,$20,$44,$f8,$07,$29,$85,$20,$4f
                                    +      $f6,$08,$3a,$81,$20,$0b,$62,$02,$00,$05,$44,$02,$83,$00,$6f,$4e
                                    +      $02,$58,$f6,$ca,$28,$d7,$28,$4f,$29,$fa,$d7,$28,$44,$29,$29,$3a
                                    +      $59,$f6,$ca,$28,$d8,$28,$4f,$29,$fa,$d8,$28,$44,$29,$29,$3a,$92
                                    +      $20,$58,$f8,$12,$0a,$00,$20,$f1,$20,$59,$f8,$12,$0a,$00,$20,$85
                                    +      $20,$92,$20,$a1,$28,$4f,$29,$20,$f1,$20,$a1,$28,$44,$29,$20,$f1
                                    +      $20,$46,$28,$44,$29,$f6,$01,$20,$f1,$20,$46,$28,$4f,$29,$f6,$01
                                    +      $20,$85,$20,$46,$28,$4f,$29,$f6,$02,$3a,$46,$28,$44,$29,$f6,$02
                                    +      $3a,$92,$20,$44,$f7,$02,$20,$85,$20,$0b,$8a,$02,$00,$05,$58,$02
                                    +      $83,$00,$5d,$62,$02,$58,$f6,$ca,$28,$d7,$28,$4f,$29,$fa,$d7,$28
                                    +      $44,$29,$fa,$05,$29,$3a,$59,$f6,$ca,$28,$d8,$28,$4f,$29,$fa,$d8
                                    +      $28,$44,$29,$fa,$05,$29,$3a,$92,$20,$58,$f8,$09,$20,$f1,$20,$59
                                    +      $f8,$09,$20,$85,$20,$92,$20,$a1,$28,$4f,$29,$20,$f1,$20,$a1,$28
                                    +      $44,$29,$20,$f1,$20,$46,$28,$44,$29,$f6,$01,$20,$85,$20,$b0,$20
                                    +      $4f,$3a,$46,$28,$44,$29,$f6,$02,$3a,$80,$20,$0b,$76,$02,$00,$05
                                    +      $6c,$02,$83,$00,$21,$76,$02,$92,$20,$44,$f8,$03,$20,$85,$20,$53
                                    +      $43,$28,$4f,$fa,$07,$29,$f6,$53,$43,$28,$4f,$fa,$07,$29,$f9,$12
                                    +      $0a,$00,$3a,$83,$00,$1c,$80,$02,$53,$43,$28,$4f,$fa,$07,$29,$f6
                                    +      $53,$43,$28,$4f,$fa,$07,$29,$f9,$02,$f9,$46,$28,$09,$29,$fd,$05
                                    +      $00,$2e,$8a,$02,$aa,$20,$a1,$28,$44,$29,$f6,$8a,$28,$12,$0a,$00
                                    +      $2c,$d0,$28,$0a,$29,$2c,$d0,$28,$04,$29,$f9,$05,$2c,$12,$0a,$00
                                    +      $29,$3a,$a1,$20,$44,$3a,$a3,$22,$4f,$34,$43,$22,$3a,$83,$00,$05
                                    +      $94,$02,$27,$00,$50,$9e,$02,$46,$28,$50,$29,$f6,$03,$3a,$aa,$20
                                    +      $a1,$28,$50,$29,$f6,$8a,$28,$12,$0a,$00,$2c,$44,$28,$50,$29,$2c
                                    +      $56,$28,$50,$29,$2c,$12,$46,$00,$fc,$56,$28,$50,$29,$2c,$01,$29
                                    +      $3a,$a1,$20,$50,$3a,$a3,$22,$4f,$22,$f9,$dd,$28,$50,$f9,$04,$29
                                    +      $f9,$22,$47,$47,$22,$3a,$53,$28,$50,$29,$f6,$53,$28,$50,$29,$fa
                                    +      $02,$3a,$83,$00,$32,$a8,$02,$92,$20,$50,$f6,$4d,$20,$f0,$20,$d6
                                    +      $28,$50,$29,$f9,$d5,$28,$50,$29,$20,$85,$20,$46,$28,$50,$29,$f6
                                    +      $01,$3a,$a7,$20,$50,$2c,$58,$30,$f9,$50,$fd,$58,$31,$2c,$59,$30
                                    +      $f9,$50,$fd,$59,$31,$00,$05,$b2,$02,$83,$00,$0e,$bc,$02,$92,$20
                                    +      $a1,$28,$50,$29,$20,$85,$20,$83,$00,$0e,$c6,$02,$46,$28,$50,$29
                                    +      $f6,$04,$3a,$b0,$20,$50,$00,$1d,$d0,$02,$92,$20,$53,$43,$28,$01
                                    +      $29,$f7,$48,$20,$f1,$20,$4d,$f3,$01,$20,$85,$20,$48,$f6,$53,$43
                                    +      $28,$01,$29,$00,$1d,$da,$02,$92,$20,$53,$43,$28,$02,$29,$f7,$48
                                    +      $20,$f1,$20,$4d,$f3,$02,$20,$85,$20,$48,$f6,$53,$43,$28,$02,$29
                                    +      $00,$0f,$e4,$02,$92,$20,$4c,$f8,$02,$20,$85,$20,$4c,$f6,$06,$00
                                    +      $51,$ee,$02,$ae,$20,$01,$2c,$12,$14,$00,$3a,$8b,$22,$53,$43,$4f
                                    +      $52,$45,$20,$22,$2c,$53,$43,$28,$01,$29,$2c,$53,$43,$28,$02,$29
                                    +      $2c,$22,$4c,$45,$56,$45,$4c,$22,$3a,$8b,$22,$53,$48,$49,$50,$53
                                    +      $22,$2c,$53,$28,$01,$29,$2c,$53,$28,$02,$29,$2c,$4c,$3b,$22,$20
                                    +      $22,$3a,$8b,$22,$48,$49,$2d,$53,$43,$4f,$52,$45,$22,$2c,$48,$2c
                                    +      $00,$10,$f8,$02,$92,$20,$53,$28,$50,$29,$f7,$01,$20,$85,$20,$83
                                    +      $00,$1e,$02,$03,$92,$20,$4d,$f6,$50,$20,$85,$20,$4c,$f6,$4c,$fa
                                    +      $02,$3a,$53,$28,$50,$29,$f6,$05,$3a,$80,$20,$0b,$e4,$02,$00,$23
                                    +      $0c,$03,$92,$20,$4d,$f6,$fa,$02,$20,$85,$20,$4d,$f6,$50,$3a,$8b
                                    +      $20,$50,$3b,$22,$20,$43,$4f,$4d,$22,$3a,$a3,$22,$45,$45,$22,$3a
                                    +      $83,$00,$34,$16,$03,$ae,$20,$12,$0a,$00,$2c,$12,$0a,$00,$3a,$8b
                                    +      $20,$22,$47,$41,$4d,$45,$20,$4f,$56,$45,$52,$22,$3a,$a3,$22,$4f
                                    +      $31,$43,$35,$43,$44,$45,$44,$36,$43,$34,$43,$35,$22,$3a,$8e,$20
                                    +      $12,$c8,$00,$3a,$82,$00,$05,$20,$03,$27,$00,$19,$2a,$03,$58,$f6
                                    +      $d7,$28,$50,$29,$fc,$09,$fa,$02,$3a,$59,$f6,$d8,$28,$50,$29,$fc
                                    +      $09,$fa,$03,$00,$25,$34,$03,$92,$20,$58,$f8,$01,$20,$f0,$20,$58
                                    +      $f7,$12,$1b,$00,$20,$f0,$20,$59,$f8,$01,$20,$f0,$20,$59,$f7,$12
                                    +      $17,$00,$20,$85,$20,$0b,$70,$03,$00,$15,$3e,$03,$92,$20,$e1,$28
                                    +      $58,$2c,$59,$29,$f3,$22,$cf,$22,$85,$20,$0b,$70,$03,$00,$11,$48
                                    +      $03,$53,$f6,$cb,$28,$e1,$28,$58,$2c,$59,$2c,$02,$29,$29,$00,$11
                                    +      $52,$03,$92,$20,$53,$f6,$50,$f9,$02,$20,$85,$20,$0b,$70,$03,$00
                                    +      $15,$5c,$03,$92,$20,$46,$28,$50,$29,$f6,$01,$20,$85,$20,$46,$28
                                    +      $50,$29,$f6,$02,$00,$05,$66,$03,$27,$00,$10,$70,$03,$92,$20,$a1
                                    +      $28,$49,$29,$f6,$01,$20,$85,$20,$83,$00,$21,$7a,$03,$58,$f6,$28
                                    +      $d7,$28,$49,$29,$fa,$12,$0c,$00,$29,$fc,$09,$3a,$59,$f6,$28,$d8
                                    +      $28,$49,$29,$fa,$12,$14,$00,$29,$fc,$09,$00,$23,$84,$03,$92,$20
                                    +      $58,$f8,$01,$20,$f0,$20,$58,$f7,$12,$1b,$00,$20,$f0,$20,$59,$f8
                                    +      $01,$20,$f0,$20,$59,$f7,$12,$17,$00,$20,$85,$20,$83,$00,$14,$8e
                                    +      $03,$92,$20,$e1,$28,$58,$2c,$59,$29,$f3,$22,$cf,$22,$20,$85,$20
                                    +      $83,$00,$1d,$98,$03,$53,$f6,$cb,$28,$e1,$28,$58,$2c,$59,$2c,$02
                                    +      $29,$29,$3a,$92,$20,$53,$f6,$50,$f9,$02,$20,$85,$20,$83,$00,$16
                                    +      $a2,$03,$b0,$20,$49,$3a,$ae,$20,$58,$2c,$59,$3a,$8b,$20,$22,$20
                                    +      $22,$3b,$3a,$83,$00,$05,$ac,$03,$27,$00,$23,$b6,$03,$8c,$20,$49
                                    +      $f6,$03,$20,$88,$20,$4d,$41,$3a,$92,$20,$a1,$28,$49,$29,$f0,$20
                                    +      $46,$28,$49,$29,$f6,$03,$20,$85,$20,$0b,$d4,$03,$00,$1a,$c0,$03
                                    +      $92,$20,$46,$28,$49,$29,$f6,$02,$20,$85,$20,$81,$20,$0b,$de,$03
                                    +      $3a,$80,$20,$0b,$d4,$03,$00,$28,$ca,$03,$aa,$20,$a1,$28,$49,$29
                                    +      $f6,$8a,$20,$28,$43,$2c,$d0,$28,$0a,$29,$2c,$d0,$28,$04,$29,$f9
                                    +      $43,$fc,$03,$2c,$12,$50,$00,$2c,$01,$29,$3a,$a1,$20,$49,$00,$07
                                    +      $d4,$03,$8d,$3a,$83,$00,$05,$de,$03,$27,$00,$2a,$e8,$03,$46,$28
                                    +      $49,$29,$f6,$01,$3a,$b0,$20,$49,$3a,$a7,$20,$49,$2c,$12,$78,$00
                                    +      $2c,$12,$78,$00,$3a,$92,$20,$55,$f8,$4d,$41,$20,$85,$20,$46,$28
                                    +      $49,$29,$f6,$03,$00,$21,$f2,$03,$55,$f6,$55,$fa,$02,$3a,$92,$20
                                    +      $55,$f6,$01,$20,$85,$20,$46,$28,$03,$29,$f6,$01,$3a,$46,$28,$09
                                    +      $29,$f6,$02,$3a,$83,$00,$63,$fc,$03,$92,$20,$55,$f8,$01,$20,$85
                                    +      $20,$55,$f6,$4e,$3a,$46,$28,$03,$29,$f6,$01,$3a,$46,$28,$04,$29
                                    +      $f6,$01,$3a,$46,$28,$05,$29,$f6,$01,$3a,$46,$28,$06,$29,$f6,$01
                                    +      $3a,$46,$28,$09,$29,$f6,$01,$3a,$43,$f6,$43,$fa,$03,$3a,$4c,$f6
                                    +      $4c,$fa,$02,$3a,$92,$20,$43,$f8,$03,$20,$85,$20,$43,$f6,$08,$3a
                                    +      $92,$20,$4d,$f7,$fa,$02,$20,$85,$20,$53,$28,$02,$fa,$4d,$29,$f6
                                    +      $53,$28,$02,$fa,$4d,$29,$f9,$02,$00,$05,$06,$04,$83,$00,$15,$10
                                    +      $04,$92,$20,$46,$28,$03,$29,$20,$85,$20,$49,$f6,$03,$3a,$80,$0b
                                    +      $de,$03,$00,$32,$1a,$04,$92,$20,$a1,$28,$03,$29,$f6,$01,$20,$85
                                    +      $20,$aa,$20,$a1,$28,$03,$29,$f6,$8a,$28,$03,$2c,$d0,$28,$09,$29
                                    +      $f9,$02,$2c,$d0,$28,$04,$29,$f9,$02,$2c,$12,$0a,$00,$2c,$01,$29
                                    +      $3a,$a1,$20,$03,$00,$05,$24,$04,$83,$00,$05,$2e,$04,$27,$00,$0b
                                    +      $38,$04,$a0,$3a,$84,$20,$0b,$2e,$04,$00,$25,$42,$04,$8c,$50,$f6
                                    +      $01,$88,$12,$c8,$00,$3a,$ae,$d0,$28,$12,$1b,$00,$29,$2c,$d0,$28
                                    +      $12,$17,$00,$29,$3a,$8b,$dc,$28,$12,$cd,$00,$29,$3a,$8d,$00,$17
                                    +      $4c,$04,$ae,$09,$2c,$04,$3a,$8b,$22,$53,$54,$41,$52,$20,$4b,$49
                                    +      $4c,$4c,$45,$52,$22,$00,$4c,$56,$04,$8c,$20,$50,$f6,$01,$20,$88
                                    +      $20,$02,$3a,$ae,$20,$12,$0a,$00,$2c,$12,$0a,$00,$f9,$50,$fd,$09
                                    +      $3a,$aa,$20,$a1,$28,$50,$29,$f6,$8a,$28,$50,$f9,$09,$2c,$44,$28
                                    +      $50,$29,$2c,$03,$2c,$12,$1e,$00,$29,$3a,$a7,$20,$50,$2c,$58,$30
                                    +      $f9,$50,$fd,$58,$31,$2c,$59,$30,$f9,$50,$fd,$59,$31,$3a,$a1,$20
                                    +      $50,$00,$10,$60,$04,$92,$20,$a1,$28,$50,$29,$20,$85,$20,$0b,$60
                                    +      $04,$00,$2c,$6a,$04,$93,$20,$41,$24,$3a,$8c,$20,$49,$f6,$02,$20
                                    +      $88,$20,$ce,$28,$41,$24,$29,$3a,$8b,$20,$d4,$28,$41,$24,$2c,$49
                                    +      $2c,$02,$29,$3b,$3a,$a3,$22,$43,$22,$3a,$8d,$3a,$8d,$00,$64,$74
                                    +      $04,$8c,$20,$50,$f6,$03,$20,$88,$20,$04,$3a,$ae,$20,$12,$0a,$00
                                    +      $2c,$12,$11,$00,$f9,$50,$3a,$93,$20,$41,$24,$3a,$8c,$20,$49,$f6
                                    +      $02,$20,$88,$20,$ce,$28,$41,$24,$29,$3a,$8b,$20,$d4,$28,$41,$24
                                    +      $2c,$49,$2c,$02,$29,$3b,$3a,$a3,$22,$43,$22,$3a,$8d,$3a,$8d,$3a
                                    +      $ae,$02,$2c,$12,$16,$00,$3a,$8b,$22,$7b,$6f,$87,$20,$a4,$20,$60
                                    +      $6e,$a7,$7e,$6c,$65,$28,$59,$2f,$4e,$29,$22,$3b,$3a,$4d,$f6,$fa
                                    +      $02,$00,$17,$7e,$04,$41,$24,$f6,$de,$28,$01,$29,$3a,$92,$41,$24
                                    +      $f6,$22,$4e,$22,$85,$4d,$f6,$02,$00,$0a,$88,$04,$8e,$12,$50,$00
                                    +      $3a,$83,$00,$2e,$92,$04,$91,$20,$22,$50,$4c,$41,$59,$45,$52,$20
                                    +      $31,$22,$2c,$22,$50,$4c,$41,$59,$45,$52,$20,$32,$22,$2c,$22,$20
                                    +      $20,$20,$4f,$52,$22,$2c,$22,$43,$4f,$4d,$50,$55,$54,$45,$52,$22
                                    +      $00,$00
fffa: ed 00        _Vectors6502    .dd2    zpNmiTrampoline
fffc: ba 80                        .dd2    _Reset
fffe: ba 80                        .dd2    _Reset
                                   .adrend ↑ $8000

                                   .addrs  NA                      ;(auto-generated)
0000: 00 00 07 1f+                 .incbin "FB3_CHRROM.bin'
                                   .adrend ↑ NA

Symbol Table

No exported symbols found.