; ======================================================================
; CS617 Chapter 06 - FUN / Brainfuck Application UI Gate r0.08
; PB-2000C / AI-1000 standalone language-card ROM
;
; PURPOSE OF THIS SPRINT
;   Present the complete B-level application with one consistent 32-column UI.
;   Preserve the accepted interpreter/editor/library semantics while making
;   rows 1-3 the content surface and row 4 the mode/action surface.
;
; VALIDATION BOUNDARY
;   Source is model-produced and has NOT yet been assembled by the
;   authoritative HD61 ASSEMBLER Rev. 0.45 workflow. Runtime PASS must come
;   back from Alex's assembler/emulator/hardware, not from this source alone.
;
; ARCHITECTURE
;   one resumable BF_STEP engine
;   runtime bracket scans (no jump table)
;   source in RAM, independent of display
;   execution state in RAM, independent of application shell
;
; RAM partition under UA=&H14 (32 KiB application budget)
;   2000-23FF  1 KiB workspace
;   2400-3FFF  7 KiB source
;   4000-5FFF  8 KiB reserve
;   6000-9FFF 16 KiB tape
;;
; UA=&H14 discipline:
;   IZ     -> bank 0 : ROM-resident test source and text
;   IX/IY  -> bank 1 : workspace, editable source, tape
;   PC     -> bank 0 : application ROM
; Never dereference SRC_BASE/TAPE_BAS through IZ under this mapping.
;
; accepted r0.04 interpreter commands:
;   > < + - . [ ]
;   non-command characters ignored
;   , returns WAIT; RUN_APP resolves it through C-ROM CRTKY
; ======================================================================

        ORG     0
        LEVEL   0

OFFCHK: EQU     &H04EF
WORK1:  EQU     &H0CE6
SETBK:  EQU     &H0276
CRTKY:  EQU     &H0460
EDCSR:  EQU     &H08CB
CSREN:  EQU     &H22E0
CSRDIS: EQU     &H22FA
KEYBS:  EQU     &H08
KEYDEL: EQU     &H11
KEYRT:  EQU     &H1C
KEYLT:  EQU     &H1D
KEYUP:  EQU     &H1E
KEYDN:  EQU     &H1F
KEYMENU:EQU     &H9C
KEYF1:  EQU     &HFC
KEYF2:  EQU     &HFD
KEYF3:  EQU     &HFE
KEYF4:  EQU     &HFF
SYSCLR: EQU     &H3169
SETLCD: EQU     &H328F
SET4LN: EQU     &H28D8
KEYMD:  EQU     &H09E4
SSPTP:  EQU     &H0FEF
USPTP:  EQU     &H0CE2
SSPBT:  EQU     &H0EEE
RCS:    EQU     SSPBT+16

; ------------------------------ RAM map -------------------------------
WS_PC:   EQU    &H2000      ; word: next source character to examine
WS_LEN:  EQU    &H2002      ; word: source length
WS_DP:   EQU    &H2004      ; word: data pointer 0..16383
WS_STAT: EQU    &H2006      ; byte: execution status
WS_ERR:  EQU    &H2007      ; byte: error code
WS_OUT:  EQU    &H2008      ; byte: last output byte
WS_FAIL: EQU    &H2009      ; byte: self-test failure count
WS_TIDX: EQU    &H200A      ; byte: self-test display index
WS_OUTLN:EQU    &H200B      ; byte: rolling output length, 0..96
WS_DIDX: EQU    &H200C      ; byte: draw-loop index
WS_DPTR: EQU    &H200E      ; word: draw-loop RAM pointer
OUT_BASE:EQU    &H2010      ; 96-byte rolling visible-output window
OUT_CAP: EQU    96
OUT_LAST:EQU    &H206F      ; OUT_BASE + 95
WS_ECUR: EQU    &H2070      ; word: edit cursor offset 0..WS_LEN
WS_VIEW: EQU    &H2072      ; word: first source byte in viewport
WS_ESTAT:EQU    &H2074      ; byte: 0 normal, 1 source full
ED_SPAN: EQU    96          ; rows 1-3, 32 visible chars each

SRC_BASE:EQU    &H2400
SRC_CAP: EQU    &H1C00      ; 7168 bytes
TAPE_BAS:EQU    &H6000
TAPE_SZ: EQU    &H4000      ; 16384 bytes
TAPE_LAST:EQU   &H3FFF

ST_RUN:  EQU    0
ST_DONE: EQU    1
ST_ERR:  EQU    2
ST_WAIT: EQU    3
ST_MENU: EQU    4

ER_LEFT: EQU    1
ER_RIGHT:EQU    2
ER_FBR:  EQU    3
ER_BBR:  EQU    4
ER_TIME: EQU    5

; -------------------------- Language header ---------------------------
        DB      &H43,&H4D
        DB      &HFF,&HFF,&HFF,&HFF
        DB      &H01,&H04,&H10,&H10
        DB      &HFF,&HFF,&HFF,&HFF
        DB      &HFF,&HFF,&HFF,&HFF
        DB      &HFF,&HFF,&HFF,&HFF
        DB      &HFF,&HFF,&HFF,&HFF
        DB      &HFF
        DB      &H03
        DB      &HC0
        DB      &HFF,&HFF,&HFF

; ======================================================================
; MIGRATED KEYBOARD SUPPORT - validated keystone lineage
; RCS/SYSCAL and INKEY remain BOOT-ROM-derived support, not new r0.04 logic.
; ======================================================================
; ======================================================================
; PROVENANCE REGION B0 - MIGRATED BOOT ROM BANK-CALL BRIDGE
; ORG 0C20 RCS_TOP..RCS_END and ORG 0C50 SYSCAL are migrated from
; BOOT ROM Rev. 0.02a.  They are NOT NEW keystone APPLICATION CODE.
;
; The template at 0C20 is copied to RAM at RCS (&H0EFE) by MAIN.
; SYSCAL temporarily selects the internal language ROM, invokes the
; firmware routine whose address is in $28/$29, restores ROM-card
; selection value 3, and returns.
; ======================================================================
        ORG     &H0C20

RCS_TOP:
        GST     UA,$6
        PHS     $6

        LDW     $6,SETBK-1
        PHSW    $7

        PST     UA,&H54
        JP      $28

_SYSCAL:
        PST     UA,&H45
        LDW     $6,&H0C10
        ST      $31,($6)

        CAL     RCS

        PST     UA,&H45
        LDW     $6,&H0C10
        LD      $28,3
        ST      $28,($6)

        PST     UA,&H54
        RTN
RCS_END:

        ORG     &H0C50

SYSCAL:
        PST     UA,&H55
        JP      RCS+(_SYSCAL-RCS_TOP)

; ======================================================================
; PROVENANCE REGION B - MIGRATED BOOT ROM CODE
; ORG 0C60 INKEY is migrated from BOOT ROM Rev. 0.02a.
; NOT NEW keystone APPLICATION CODE.
;
; BOOT ROM style INKEY routine.
;
; Returns:
;   $0  = translated key code, or 0 if no key
;   $17 = nonzero if a translated key was found
;
; The original BOOT ROM restores UA=&H54 after table access.  This
; experiment preserves that routine behavior and restores UA=&H14 in the
; caller immediately after every INKEY call, because keystone executes and
; reads its strings from the standalone-ROM context used by helloROM.s.
; ----------------------------------------------------------------------
        ORG     &H0C60

INKEY:
        CAL     OFFCHK
        LD      $17,$31
        LD      $0,&H4B

IKEYLP:
        PST     IA,$0

        GRE     KY,$1
        GRE     KY,$1

        DID     $2
        ANCW    $1,$1
        JR      Z,IKEY40

        SBC     $0,&H41
        JP      Z,&H0001

IKEY10:
        LD      $3,$31

IKEY20:
        BIDW    $2
        JR      C,IKEY30
        AD      $3,$30,IKEY20

IKEY30:
        LD      $5,$0
        SB      $5,$30
        BIU     $5
        BIU     $5
        LD      $4,$5
        BIU     $4
        BIU     $4
        SB      $4,$5

        PST     UA,&H44
        PRE     IX,KEYSCAN
        LDD     $0,(IX+$4)
        LDD     $0,(IX+$3)
        PST     UA,&H54
        JR      IKEY50

IKEY40:
        SB      $0,$30
        JR      LNZ,IKEYLP
        LD      $0,$31

IKEY50:
        AN      $0,$0
        JR      Z,IKEYRET
        LD      $17,$30

IKEYRET:
        LDW     $15,WORK1
        ST      $0,($15)
        RTN


; ------------------------- Canonical vectors --------------------------
        ORG     &H0D80
        JP      MAIN
        JP      TRAP
        JP      &H01AA
        JP      &H01BC
        JP      TRAP
        JP      TRAP
        JP      TRAP
        JP      &H00E3
DEFRT:  RTN
        DB      &HFF,&HFF
        RTN
        DB      &HFF,&HFF
        JP      DEFRT
        JP      POFF
        JP      DEFRT
        JP      DEFRT
        JP      MAIN
        JP      DEFRT
        JP      DEFRT
        JP      DEFRT
        JP      DEFRT
        DB      &H11
        DB      &H10
TRAP:   RTNI
POFF:   OFF
        OFF
        OFF
        OFF
        JR      POFF

; ======================================================================
; PROVENANCE REGION D - MIGRATED BOOT ROM DATA
; KEYSCAN is the BOOT ROM key translation table: 11 scan rows x 12 entries.
; NOT NEW keystone APPLICATION DATA.
; ======================================================================
KEYSCAN:
KSCAN01:
        DB      0,0,0,0,0,0,0,1,0,0,0,0

KSCAN02:
        DB      "W","Q","A",0,"Z",0,0,0,0,0,"'",7

KSCAN03:
        DB      "R","E","D","S","C","X",0,0,&HFC,0,&H29,&H28

KSCAN04:
        DB      "Y","T","G","F","B","V",0,0,&HFD,0,&H5D,&H5B

KSCAN05:
        DB      "I","U","J","H","M","N",0,0,&HFE,0,&H90,&H7C

KSCAN06:
        DB      "P","O","L","K",&H20,",",0,0,&HFF,0,&H93,&H94

KSCAN07:
        DB      0,"=",":",";",0,0,0,0,&H95,0,0,&H9B

KSCAN08:
        DB      "5","4","2","1",0,"0",0,"7",0,0,0,0

KSCAN09:
        DB      "-","6","+","3",&H0D,".",0,"9",0,0,0,0

KSCAN10:
        DB      &H9D,&H1C,"/",&H08,0,0,0,"*",0,0,&H9C,&H11

KSCAN11:
        DB      &H1F,&H1D,&H0C,0,0,0,0,"8",0,0,&H1E,&H12


; ======================================================================
; APPLICATION / UI HARNESS
; ======================================================================
        ORG     &H0E80

MAIN:
        PRE     SS,&H0100
        LD      $30,1
        LD      $31,0
        PSR     SZ,0
        PSR     SY,30
        PSR     SX,31
        PST     UA,&H14

; Install the validated BOOT-derived bank-call bridge used since r0.04.
        PRE     IX,RCS_TOP
        PRE     IY,RCS_END-1
        PRE     IZ,RCS
        PST     UA,&H44
        BUP
        PST     UA,&H14

; Establish the validated C-ROM application keyboard runtime.
        PRE     SS,SSPTP
        PRE     US,USPTP
        LDW     $28,SYSCLR
        CAL     SYSCAL
        PST     UA,&H14
        LDW     $28,SETLCD
        CAL     SYSCAL
        PST     UA,&H14
        LDW     $28,SET4LN
        CAL     SYSCAL
        PST     UA,&H14

        CAL     SCREEN_INIT

; Deterministic English/uppercase startup; CRTKY owns later modifier state.
        PST     UA,&H54
        PRE     IX,KEYMD
        ST      $31,(IX+$SX)
        PST     UA,&H14

; Start with a useful editable specimen, then enter the application shell.
        PRE     IZ,S_HELLO
        CAL     LOAD_SRC
        CAL     SHOW_SPLASH

APP_HOME:
        CAL     CUR_OFF
        CAL     SHOW_HOME
AH_WAIT:
        LDW     $28,CRTKY
        CAL     SYSCAL
        PST     UA,&H14
        LD      $12,$0
        SBC     $12,KEYF1
        JP      Z,APP_EDIT
        SBC     $12,KEYF2
        JP      Z,APP_RUN
        SBC     $12,KEYF3
        JP      Z,APP_LIB
        JP      AH_WAIT

APP_EDIT:
        CAL     ED_INIT
        CAL     ED_RUN
        CAL     CUR_OFF
        JP      APP_HOME

APP_LIB:
        CAL     CUR_OFF
        CAL     SHOW_LIB
AL_WAIT:
        LDW     $28,CRTKY
        CAL     SYSCAL
        PST     UA,&H14
        LD      $12,$0
        SBC     $12,KEYMENU
        JP      Z,APP_HOME
        SBC     $12,KEYF1
        JP      Z,LIB_OUTA
        SBC     $12,KEYF2
        JP      Z,LIB_NLOOP
        SBC     $12,KEYF3
        JP      Z,LIB_ECHO
        SBC     $12,KEYF4
        JP      Z,LIB_HELLO
        JP      AL_WAIT

LIB_OUTA:
        PRE     IZ,S_OUTA
        JP      LIB_LOAD
LIB_NLOOP:
        PRE     IZ,S_NEST
        JP      LIB_LOAD
LIB_ECHO:
        PRE     IZ,S_ECHO
        JP      LIB_LOAD
LIB_HELLO:
        PRE     IZ,S_HELLO
LIB_LOAD:
        CAL     LOAD_SRC
        JP      APP_EDIT

APP_RUN:
        CAL     CUR_OFF
        CAL     BF_RESET
        CAL     OUT_INIT
        PRE     IZ,ST_RUN32
        CAL     STAT_ROW
        CAL     RUN_APP

        PRE     IX,WS_STAT
        LD      $0,(IX+$SX)
        SBC     $0,ST_MENU
        JP      Z,APP_HOME
        SBC     $0,ST_DONE
        JP      Z,AR_DONE

; Any non-menu non-done terminal state is shown as an error.
        PRE     IZ,ST_ERR32
        CAL     STAT_ROW
        CAL     WAIT_BACK
        JP      APP_HOME

AR_DONE:
        PRE     IZ,ST_DONE32
        CAL     STAT_ROW
        CAL     WAIT_BACK
        JP      APP_HOME

; F4 and MENU are equivalent visible/back application actions here.
WAIT_BACK:
WB_LOOP:
        LDW     $28,CRTKY
        CAL     SYSCAL
        PST     UA,&H14
        LD      $12,$0
        SBC     $12,KEYMENU
        RTN     Z
        SBC     $12,KEYF4
        RTN     Z
        JP      WB_LOOP

SHOW_SPLASH:
        CAL     CUR_OFF
        LD      $6,&H00
        PRE     IZ,SPL1
        CAL     ROM32
        LD      $6,&H20
        PRE     IZ,SPL2
        CAL     ROM32
        LD      $6,&H40
        PRE     IZ,SPL3
        CAL     ROM32
        LD      $6,&H60
        PRE     IZ,SPL4
        CAL     ROM32
        LD      $18,40
SS_DLY:
        PHS     $18
        CAL     SHORT_DELAY
        PPS     $18
        SB      $18,$SY
        JR      NZ,SS_DLY
        RTN

SHOW_HOME:
        LD      $6,&H00
        PRE     IZ,HOME1
        CAL     ROM32
        LD      $6,&H20
        PRE     IZ,HOME2
        CAL     ROM32
        LD      $6,&H40
        PRE     IZ,BLANK32
        CAL     ROM32
        LD      $6,&H60
        PRE     IZ,HOME4
        CAL     ROM32
        RTN

SHOW_LIB:
        LD      $6,&H00
        PRE     IZ,LIB1
        CAL     ROM32
        LD      $6,&H20
        PRE     IZ,LIB2
        CAL     ROM32
        LD      $6,&H40
        PRE     IZ,BLANK32
        CAL     ROM32
        LD      $6,&H60
        PRE     IZ,LIB4
        CAL     ROM32
        RTN

; 32-column application furniture. Every string is exactly one LCD row.
SPL1:   DB "        CS617 CHAPTER 06        "
SPL2:   DB "           BRAINFUCK            "
SPL3:   DB "            PB-2000C            "
SPL4:   DB "             r0.08              "
HOME1:  DB "CS617 CH06 - BRAINFUCK          "
HOME2:  DB "SRC 7168B   TAPE 16384B         "
BLANK32:DB "                                "
HOME4:  DB "[edit  ][run   ][lib   ][      ]"
LIB1:   DB "LIBRARY - LOAD TO EDITOR        "
LIB2:   DB "SELECT EXAMPLE                  "
LIB4:   DB "[outa  ][nloop ][echo  ][hello ]"
ST_EDIT:DB "Mode: EDIT      [clear ][back  ]"
ST_FULL:DB "Mode: FULL      [clear ][back  ]"
ST_RUN32:DB "Mode: RUN               [abort ]"
ST_INPUT:DB "Mode: INPUT             [abort ]"
ST_DONE32:DB "Mode: DONE              [back  ]"
ST_ERR32: DB "Mode: ERROR             [back  ]"

; ======================================================================
; r0.07 SOURCE EDITOR - accepted buffer semantics, 32-column viewport
; ======================================================================
; Source is one linear byte sequence. Rows are viewport presentation only.
; Generic insert/delete/move primitives live in ../lib/textbuf_edit.inc.
ED_INIT:
        PRE     IX,WS_ECUR
        LDW     $0,0
        STW     $0,(IX+$SX)
        PRE     IX,WS_VIEW
        STW     $0,(IX+$SX)
        PRE     IX,WS_ESTAT
        ST      $0,(IX+$SX)
        CAL     ED_DRAW
        CAL     CUR_ON
        RTN

ED_RUN:
ED_LOOP:
; C-ROM CRTKY supplies application-visible decoded keys, including shifted
; printable characters. This is the same keyboard layer accepted in r0.04.
        LDW     $28,CRTKY
        CAL     SYSCAL
        PST     UA,&H14
        LD      $12,$0

        SBC     $12,KEYMENU
        RTN     Z
        SBC     $12,KEYF4
        RTN     Z
        SBC     $12,KEYF3
        JP      Z,ED_CLEAR

; Editor backbone uses absolute JP so handler growth cannot exceed JR range.
        SBC     $12,KEYLT
        JP      Z,ED_LEFT
        SBC     $12,KEYRT
        JP      Z,ED_RIGHT
        SBC     $12,KEYUP
        JP      Z,ED_UP
        SBC     $12,KEYDN
        JP      Z,ED_DOWN
        SBC     $12,KEYBS
        JP      Z,ED_BS
        SBC     $12,KEYDEL
        JP      Z,ED_DEL

; Printable ASCII is inserted at cursor. Brainfuck ignores non-command bytes,
; so comments remain editable source exactly as the assignment requires.
        SBC     $12,&H20
        JR      C,ED_LOOP
        SBC     $12,&H7F
        JR      NC,ED_LOOP

        LDW     $0,SRC_BASE
        PRE     IX,WS_LEN
        LDW     $2,(IX+$SX)
        LDW     $4,SRC_CAP
        PRE     IX,WS_ECUR
        LDW     $6,(IX+$SX)
        LD      $16,$12
        CAL     TB_INS1
        CAL     ED_SAVE
        JP      ED_REFRESH

; F3 CLEAR empties the logical source instantly. The underlying RAM bytes need
; not be erased because WS_LEN defines the editable/executable source extent.
ED_CLEAR:
        LDW     $0,0
        PRE     IX,WS_LEN
        STW     $0,(IX+$SX)
        PRE     IX,WS_ECUR
        STW     $0,(IX+$SX)
        PRE     IX,WS_VIEW
        STW     $0,(IX+$SX)
        PRE     IX,WS_ESTAT
        ST      $31,(IX+$SX)
        JP      ED_REFRESH

ED_DEL:
        LDW     $0,SRC_BASE
        PRE     IX,WS_LEN
        LDW     $2,(IX+$SX)
        LDW     $4,SRC_CAP
        PRE     IX,WS_ECUR
        LDW     $6,(IX+$SX)
        CAL     TB_DEL1
        CAL     ED_SAVE
        JP      ED_REFRESH

ED_BS:
        LDW     $0,SRC_BASE
        PRE     IX,WS_LEN
        LDW     $2,(IX+$SX)
        LDW     $4,SRC_CAP
        PRE     IX,WS_ECUR
        LDW     $6,(IX+$SX)
        CAL     TB_BS1
        CAL     ED_SAVE
        JP      ED_REFRESH

ED_LEFT:
        PRE     IX,WS_LEN
        LDW     $2,(IX+$SX)
        PRE     IX,WS_ECUR
        LDW     $6,(IX+$SX)
        CAL     TB_LEFT
        PRE     IX,WS_ECUR
        STW     $6,(IX+$SX)
        JP      ED_REFRESH

ED_RIGHT:
        PRE     IX,WS_LEN
        LDW     $2,(IX+$SX)
        PRE     IX,WS_ECUR
        LDW     $6,(IX+$SX)
        CAL     TB_RIGHT
        PRE     IX,WS_ECUR
        STW     $6,(IX+$SX)
        JP      ED_REFRESH

; Up/down are Chapter 06 viewport policy, not generic buffer semantics.
; They move by one visible row (32 source characters), bounded to 0..len.
ED_UP:
        PRE     IX,WS_ECUR
        LDW     $6,(IX+$SX)
        LDW     $10,32
        LDW     $12,$6
        SBCW    $12,$10
        JR      C,EU_ZERO
        SBW     $6,$10
        JR      EU_STORE
EU_ZERO:
        LDW     $6,0
EU_STORE:
        PRE     IX,WS_ECUR
        STW     $6,(IX+$SX)
        JP      ED_REFRESH

ED_DOWN:
        PRE     IX,WS_ECUR
        LDW     $6,(IX+$SX)
        LDW     $10,32
        ADW     $6,$10
        PRE     IX,WS_LEN
        LDW     $2,(IX+$SX)
        LDW     $12,$6
        SBCW    $12,$2
        JR      C,EDN_STORE
        JR      Z,EDN_STORE
        LDW     $6,$2
EDN_STORE:
        PRE     IX,WS_ECUR
        STW     $6,(IX+$SX)
        JP      ED_REFRESH

; Store length/cursor returned by reusable buffer primitive.
; $8 is the primitive status: TB_FULL is preserved for status-row display.
ED_SAVE:
        PRE     IX,WS_LEN
        STW     $2,(IX+$SX)
        PRE     IX,WS_ECUR
        STW     $6,(IX+$SX)
        PRE     IX,WS_ESTAT
        ST      $8,(IX+$SX)
        RTN

ED_REFRESH:
        CAL     ED_NORM
        CAL     ED_DRAW
        JP      ED_LOOP

; Keep the cursor inside the 96-character linear viewport.
ED_NORM:
        PRE     IX,WS_ECUR
        LDW     $6,(IX+$SX)
        PRE     IX,WS_VIEW
        LDW     $10,(IX+$SX)
        LDW     $12,ED_SPAN
        CAL     TB_NORMV
        PRE     IX,WS_VIEW
        STW     $10,(IX+$SX)
        RTN

; Draw source[view:view+96] on rows 1..3. Row 4 is always mode/action.
ED_DRAW:
        PRE     IX,WS_VIEW
        LDW     $10,(IX+$SX)
        PRE     IX,WS_LEN
        LDW     $14,(IX+$SX)

        LD      $6,&H00
        CAL     ED_ROW
        LD      $6,&H20
        CAL     ED_ROW
        LD      $6,&H40
        CAL     ED_ROW

        PRE     IX,WS_ESTAT
        LD      $0,(IX+$SX)
        SBC     $0,TB_FULL
        JR      Z,EDD_FULL
        PRE     IZ,ST_EDIT
        CAL     STAT_ROW
        JR      EDD_CURSOR
EDD_FULL:
        PRE     IZ,ST_FULL
        CAL     STAT_ROW
EDD_CURSOR:
        CAL     ED_SETCUR
        RTN

; Draw 32 consecutive source positions from index $10/$11.
; Missing positions beyond source length are spaces.
ED_ROW:
        LD      $18,32
EDR_LOOP:
        LDW     $2,$10
        SBCW    $2,$14
        JR      C,EDR_HAVE
        LD      $16,&H20
        JR      EDR_PUT
EDR_HAVE:
        LDW     $2,SRC_BASE
        ADW     $2,$10
        PRE     IX,$2
        LD      $16,(IX+$SX)
EDR_PUT:
        CAL     PUTC
        ADW     $10,$SY
        SB      $18,$SY
        JR      NZ,EDR_LOOP
        RTN

; Map linear viewport offset 0..95 onto physical display rows 1..3 and
; synchronize C-ROM EDCSR. Cursor may sit on the first blank after the text.
ED_SETCUR:
        PRE     IX,WS_ECUR
        LDW     $10,(IX+$SX)
        PRE     IX,WS_VIEW
        LDW     $12,(IX+$SX)
        SBW     $10,$12

        LDW     $12,32
        LDW     $14,$10
        SBCW    $14,$12
        JR      C,ESC_ROW1

        LDW     $12,64
        LDW     $14,$10
        SBCW    $14,$12
        JR      C,ESC_ROW2

; row 3
        LDW     $12,64
        SBW     $10,$12
        LD      $6,&H40
        AD      $6,$10
        JP      SETCUR

ESC_ROW2:
        LDW     $12,32
        SBW     $10,$12
        LD      $6,&H20
        AD      $6,$10
        JP      SETCUR

ESC_ROW1:
        LD      $6,&H00
        AD      $6,$10
        JP      SETCUR

SETCUR:
        PST     UA,&H54
        PRE     IX,EDCSR
        ST      $6,(IX+$SX)
        PST     UA,&H14
        RTN

CUR_ON:
        LDW     $28,CSREN
        CAL     SYSCAL
        PST     UA,&H14
        RTN

CUR_OFF:
        LDW     $28,CSRDIS
        CAL     SYSCAL
        PST     UA,&H14
        RTN

; ======================================================================
; APPLICATION RUNNER
; ======================================================================
; RUN_APP owns application-level responsiveness. BF_STEP remains the single
; interpreter engine. MENU or visible F4 ABORT yields ST_MENU.
RUN_APP:
RA_LOOP:
        CAL     POLL_ABORT
        AN      $0,$0
        JR      Z,RA_STEP
        LD      $0,ST_MENU
        PRE     IX,WS_STAT
        ST      $0,(IX+$SX)
        RTN

RA_STEP:
        CAL     BF_STEP
        PRE     IX,WS_STAT
        LD      $0,(IX+$SX)
        AN      $0,$0
        JR      Z,RA_LOOP
        SBC     $0,ST_WAIT
        RTN     NZ

        CAL     WAIT_INPUT
        PRE     IX,WS_STAT
        LD      $0,(IX+$SX)
        SBC     $0,ST_MENU
        RTN     Z
        JR      RA_LOOP

; Nonblocking application abort check. Raw INKEY is intentionally used only
; for MENU/F4 control; full character semantics remain CRTKY-owned.
POLL_ABORT:
        CAL     INKEY
        PST     UA,&H14
        AN      $17,$17
        JR      Z,PA_NO
        SBC     $0,KEYMENU
        JR      Z,PA_YES
        SBC     $0,KEYF4
        JR      Z,PA_YES
PA_NO:
        LD      $0,0
        RTN
PA_YES:
        LD      $0,1
        RTN

; Blocking comma input through the validated application-level decoder.
; Row 4 advertises INPUT; MENU/F4 abandon the run instead of becoming data.
WAIT_INPUT:
        PRE     IZ,ST_INPUT
        CAL     STAT_ROW
        LDW     $28,CRTKY
        CAL     SYSCAL
        PST     UA,&H14
        LD      $14,$0
        SBC     $14,KEYMENU
        JR      Z,WI_MENU
        SBC     $14,KEYF4
        JR      Z,WI_MENU

        CAL     GET_CELL
        ST      $14,(IX+$SX)
        LD      $0,ST_RUN
        PRE     IX,WS_STAT
        ST      $0,(IX+$SX)
        PRE     IZ,ST_RUN32
        CAL     STAT_ROW
        RTN
WI_MENU:
        LD      $0,ST_MENU
        PRE     IX,WS_STAT
        ST      $0,(IX+$SX)
        RTN

; ======================================================================
; OUTPUT WINDOW - 3 visible rows x 32 columns; row 4 is mode/action
; ======================================================================
OUT_INIT:
        PRE     IX,WS_OUTLN
        LD      $0,0
        ST      $0,(IX+$SX)
        PRE     IX,OUT_BASE
        LD      $0,&H20
        LD      $20,OUT_CAP
OI_LOOP:
        STI     $0,(IX+$SX)
        SB      $20,$SY
        JR      NZ,OI_LOOP
        CAL     OUT_DRAW
        RTN

; Input $16 = byte to append. Retain the last 96 bytes.
OUT_CHAR:
        LD      $14,$16
        PRE     IX,WS_OUTLN
        LD      $4,(IX+$SX)
        SBC     $4,OUT_CAP
        JR      Z,OC_SHIFT

; Append into first free slot.
        LDW     $2,OUT_BASE
        AD      $2,$4
        PRE     IX,$2
        ST      $14,(IX+$SX)
        AD      $4,$SY
        PRE     IX,WS_OUTLN
        ST      $4,(IX+$SX)
        CAL     OUT_DRAW
        RTN

; Full window: shift 1..95 to 0..94 with BUP, then append at 95.
; Temporarily map IX/IY/IZ to RAM bank 1; PC remains in bank 0.
OC_SHIFT:
        PST     UA,&H54
        PRE     IX,OUT_BASE+1
        PRE     IY,OUT_LAST
        PRE     IZ,OUT_BASE
        BUP
        PST     UA,&H14
        PRE     IX,OUT_LAST
        ST      $14,(IX+$SX)
        CAL     OUT_DRAW
        RTN

OUT_DRAW:
        LDW     $2,OUT_BASE
        PRE     IX,WS_DPTR
        STW     $2,(IX+$SX)

        LD      $6,&H00
        CAL     DRAW32
        LD      $6,&H20
        CAL     DRAW32
        LD      $6,&H40
        CAL     DRAW32
        RTN

; Draw 32 bytes from RAM pointer WS_DPTR at firmware cursor $6.
DRAW32:
        PRE     IX,WS_DIDX
        LD      $0,0
        ST      $0,(IX+$SX)
D32_LOOP:
        PRE     IX,WS_DPTR
        LDW     $2,(IX+$SX)
        PRE     IX,$2
        LD      $16,(IX+$SX)
        CAL     PUTC

        PRE     IX,WS_DPTR
        LDW     $2,(IX+$SX)
        ADW     $2,$SY
        STW     $2,(IX+$SX)

        PRE     IX,WS_DIDX
        LD      $0,(IX+$SX)
        AD      $0,$SY
        ST      $0,(IX+$SX)
        SBC     $0,32
        JR      NZ,D32_LOOP
        RTN

; Draw exactly one 32-character ROM row. Caller supplies physical row in $6.
ROM32:
        LD      $20,32
R32_LOOP:
        LDI     $16,(IZ+$SX)
        PHS     $20
        CAL     PUTC
        PPS     $20
        SB      $20,$SY
        JR      NZ,R32_LOOP
        RTN

; Row 4 is the invariant mode/action surface.
STAT_ROW:
        LD      $6,&H60
        JP      ROM32

; Retained for dormant r0.03 self-test support below.
STATUS20:
        LD      $6,0
        LD      $20,20
S20_LOOP:
        LDI     $16,(IZ+$SX)
        PHS     $20
        CAL     PUTC
        PPS     $20
        SB      $20,$SY
        JR      NZ,S20_LOOP
        RTN

ST_AUTO: DB "AUTO OUTPUT: A      "
ST_ECHO: DB "TYPE ONE KEY TO ECHO"
ST_ABORT:DB "PRESS MENU TO ABORT "
ST_PASS: DB "I/O + MENU PASS     "
ST_FAIL: DB "I/O + MENU FAIL     "

TEST_INIT:
        PRE     IX,WS_FAIL
        LD      $0,0
        ST      $0,(IX+$SX)
        PRE     IX,WS_TIDX
        ST      $0,(IX+$SX)
        LD      $6,$SX
        PRE     IZ,TXT_TITLE
        CAL     PRINTZ
        LD      $6,&H20
        PRE     IZ,TXT_SELF
        CAL     PRINTZ
        RTN

RUN_TESTS:
; Loader witness: catches a dead/zero source length before testing interpreter semantics.
        PRE     IZ,S_LEN3
        CAL     LOAD_SRC
        CAL     CK_LEN3
        CAL     MARK

; Bank/copy witness: '+' must actually exist in RAM source buffer.
        PRE     IZ,S_PLUS
        CAL     LOAD_SRC
        CAL     CK_SRCPLUS
        CAL     MARK

        PRE     IZ,S_EMPTY
        CAL     LOAD_SRC
        CAL     BF_RESET
        CAL     RUN_CORE
        CAL     CK_DONE
        CAL     MARK

        PRE     IZ,S_COM
        CAL     LOAD_SRC
        CAL     BF_RESET
        CAL     RUN_CORE
        CAL     CK_DONE
        CAL     MARK

        PRE     IZ,S_DEC
        CAL     LOAD_SRC
        CAL     BF_RESET
        CAL     RUN_CORE
        CAL     CK_DEC
        CAL     MARK

        PRE     IZ,S_IDEC
        CAL     LOAD_SRC
        CAL     BF_RESET
        CAL     RUN_CORE
        CAL     CK_ZERO
        CAL     MARK

; Positive execution witness: a dead interpreter cannot pass this vacuously.
        PRE     IZ,S_PLUS
        CAL     LOAD_SRC
        CAL     BF_RESET
        CAL     RUN_CORE
        CAL     CK_ONE
        CAL     MARK

        PRE     IZ,S_MOVE
        CAL     LOAD_SRC
        CAL     BF_RESET
        CAL     RUN_CORE
        CAL     CK_MOVE
        CAL     MARK

        PRE     IZ,S_LEFT
        CAL     LOAD_SRC
        CAL     BF_RESET
        CAL     RUN_CORE
        LD      $9,ER_LEFT
        CAL     CK_ERROR
        CAL     MARK

        PRE     IZ,S_RIGHT
        CAL     LOAD_SRC
        CAL     BF_RESET
        LDW     $2,TAPE_LAST
        PRE     IX,WS_DP
        STW     $2,(IX+$SX)
        CAL     RUN_CORE
        LD      $9,ER_RIGHT
        CAL     CK_ERROR
        CAL     MARK

        PRE     IZ,S_LOOP
        CAL     LOAD_SRC
        CAL     BF_RESET
        CAL     RUN_CORE
        CAL     CK_ZERO
        CAL     MARK

        PRE     IZ,S_FBR
        CAL     LOAD_SRC
        CAL     BF_RESET
        CAL     RUN_CORE
        LD      $9,ER_FBR
        CAL     CK_ERROR
        CAL     MARK

        PRE     IZ,S_BBR
        CAL     LOAD_SRC
        CAL     BF_RESET
        CAL     RUN_CORE
        LD      $9,ER_BBR
        CAL     CK_ERROR
        CAL     MARK

        PRE     IZ,S_NEST
        CAL     LOAD_SRC
        CAL     BF_RESET
        CAL     RUN_CORE
        CAL     CK_NEST
        CAL     MARK

        PRE     IX,WS_FAIL
        LD      $0,(IX+$SX)
        AN      $0,$0
        JP      NZ,SHOW_FAIL
        LD      $6,&H60
        PRE     IZ,TXT_PASS
        CAL     PRINTZ
        RTN
SHOW_FAIL:
        LD      $6,&H60
        PRE     IZ,TXT_FAIL
        CAL     PRINTZ
        RTN

; $0=1 pass / 0 fail. Draw '.' or 'X' on row 3 and count failures.
MARK:
        PRE     IX,WS_TIDX
        LD      $9,(IX+$SX)
        LD      $6,&H40
        AD      $6,$9
        AN      $0,$0
        JR      Z,MARK_BAD
        LD      $16,&H2E
        JR      MARK_PUT
MARK_BAD:
        LD      $16,&H58
        PRE     IX,WS_FAIL
        LD      $10,(IX+$SX)
        AD      $10,$SY
        ST      $10,(IX+$SX)
MARK_PUT:
        CAL     PUTC
        PRE     IX,WS_TIDX
        LD      $9,(IX+$SX)
        AD      $9,$SY
        ST      $9,(IX+$SX)
        CAL     SHORT_DELAY
        RTN

SHORT_DELAY:
        LDW     $20,&H0800
SD_LOOP:
        SBW     $20,$SY
        JR      NZ,SD_LOOP
        RTN

; ======================================================================
; SOURCE LOADER / EXECUTION RESET
; ======================================================================
; Entry IZ = zero-terminated ROM source. Copies into ordinary RAM edit buffer.
LOAD_SRC:
        PRE     IX,SRC_BASE
        LDW     $20,0
LS_LOOP:
        LDI     $0,(IZ+$SX)
        AN      $0,$0
        JR      Z,LS_DONE
        STI     $0,(IX+$SX)
        ADW     $20,$SY
        JP      LS_LOOP
LS_DONE:
        PRE     IX,WS_LEN
        STW     $20,(IX+$SX)
        RTN

BF_RESET:
        PRE     IX,WS_PC
        LDW     $0,0
        STW     $0,(IX+$SX)
        PRE     IX,WS_DP
        STW     $0,(IX+$SX)
        PRE     IX,WS_STAT
        LD      $0,ST_RUN
        ST      $0,(IX+$SX)
        PRE     IX,WS_ERR
        ST      $0,(IX+$SX)
        PRE     IX,WS_OUT
        ST      $0,(IX+$SX)

; Deliberately boring full tape clear. Optimize only if evidence demands it.
        PRE     IX,TAPE_BAS
        LDW     $20,TAPE_SZ
        LD      $0,0
BT_CLR:
        STI     $0,(IX+$SX)
        SBW     $20,$SY
        JR      NZ,BT_CLR
        RTN

; Run until DONE / ERROR / WAIT. Budget prevents a broken test from hanging ROM.
RUN_CORE:
        LDW     $20,20000
RC_LOOP:
        CAL     BF_STEP
        PRE     IX,WS_STAT
        LD      $0,(IX+$SX)
        AN      $0,$0
        RTN     NZ
        SBW     $20,$SY
        JR      NZ,RC_LOOP
        LD      $0,ST_ERR
        ST      $0,(IX+$SX)
        PRE     IX,WS_ERR
        LD      $0,ER_TIME
        ST      $0,(IX+$SX)
        RTN

; ======================================================================
; BF_STEP - execute exactly one Brainfuck instruction.
; Ignored source characters are skipped internally and do not count as a step.
; ======================================================================
BF_STEP:
BF_FETCH:
        PRE     IX,WS_PC
        LDW     $2,(IX+$SX)
        PRE     IX,WS_LEN
        LDW     $4,(IX+$SX)
        LDW     $6,$2
        SBCW    $6,$4
        JP      Z,BF_DONE

        LDW     $6,SRC_BASE
        ADW     $6,$2
        PRE     IX,$6
        LD      $0,(IX+$SX)

; PC always advances arithmetically by one past the fetched source character.
        ADW     $2,$SY
        PRE     IX,WS_PC
        STW     $2,(IX+$SX)

        SBC     $0,&H3E          ; >
        JP      Z,BF_RIGHT
        SBC     $0,&H3C          ; <
        JP      Z,BF_LEFT
        SBC     $0,&H2B          ; +
        JP      Z,BF_PLUS
        SBC     $0,&H2D          ; -
        JP      Z,BF_MINUS
        SBC     $0,&H2E          ; .
        JP      Z,BF_DOT
        SBC     $0,&H2C          ; ,
        JP      Z,BF_COMMA
        SBC     $0,&H5B          ; [
        JP      Z,BF_LBR
        SBC     $0,&H5D          ; ]
        JP      Z,BF_RBR
        JP      BF_FETCH         ; comment / ignored character

BF_DONE:
        PRE     IX,WS_STAT
        LD      $0,ST_DONE
        ST      $0,(IX+$SX)
        RTN

BF_PLUS:
        CAL     GET_CELL
        AD      $8,$SY
        ST      $8,(IX+$SX)
        RTN

BF_MINUS:
        CAL     GET_CELL
        SB      $8,$SY
        ST      $8,(IX+$SX)
        RTN

BF_RIGHT:
        PRE     IX,WS_DP
        LDW     $2,(IX+$SX)
        LDW     $4,TAPE_LAST
        LDW     $6,$2
        SBCW    $6,$4
        JP      Z,ERR_RIGHT
        ADW     $2,$SY
        STW     $2,(IX+$SX)
        RTN

BF_LEFT:
        PRE     IX,WS_DP
        LDW     $2,(IX+$SX)
        ANCW    $2,$2
        JP      Z,ERR_LEFT
        SBW     $2,$SY
        STW     $2,(IX+$SX)
        RTN

BF_DOT:
        CAL     GET_CELL
        LD      $16,$8
        PRE     IX,WS_OUT
        ST      $8,(IX+$SX)
        CAL     OUT_CHAR
        RTN

BF_COMMA:
; PC already points after comma. RUN_APP resolves ST_WAIT through WAIT_INPUT,
; stores one decoded byte into the current cell, and resumes this exact state.
        PRE     IX,WS_STAT
        LD      $0,ST_WAIT
        ST      $0,(IX+$SX)
        RTN

BF_LBR:
        CAL     GET_CELL
        AN      $8,$8
        RTN     NZ

; Zero cell: scan forward from current PC. $10/$11 = depth, $12/$13 = scan.
; All counters use ADW/SBW with $SY=0001h; BIUW/BIDW are bit shifts.
        LDW     $10,1
        PRE     IX,WS_PC
        LDW     $12,(IX+$SX)
FL_SCAN:
        PRE     IX,WS_LEN
        LDW     $4,(IX+$SX)
        LDW     $6,$12
        SBCW    $6,$4
        JP      Z,ERR_FBR
        LDW     $6,SRC_BASE
        ADW     $6,$12
        PRE     IX,$6
        LD      $0,(IX+$SX)
        ADW     $12,$SY
        SBC     $0,&H5B
        JP      Z,FL_NEST
        SBC     $0,&H5D
        JP      Z,FL_CLOSE
        JP      FL_SCAN
FL_NEST:
        ADW     $10,$SY
        JP      FL_SCAN
FL_CLOSE:
        SBW     $10,$SY
        ANCW    $10,$10
        JP      NZ,FL_SCAN
        PRE     IX,WS_PC
        STW     $12,(IX+$SX)
        RTN

BF_RBR:
        CAL     GET_CELL
        AN      $8,$8
        RTN     Z

; Nonzero cell: scan backward before the current ].
        LDW     $10,1
        PRE     IX,WS_PC
        LDW     $12,(IX+$SX)
        SBW     $12,$SY              ; current ] index
BL_SCAN:
        ANCW    $12,$12
        JP      Z,BL_ZERO
        SBW     $12,$SY
BL_FETCH:
        LDW     $6,SRC_BASE
        ADW     $6,$12
        PRE     IX,$6
        LD      $0,(IX+$SX)
        SBC     $0,&H5D
        JP      Z,BL_NEST
        SBC     $0,&H5B
        JP      Z,BL_OPEN
        JP      BL_SCAN
BL_ZERO:
; Index zero is the last possible matching location. Examine it exactly once.
        LDW     $6,SRC_BASE
        PRE     IX,$6
        LD      $0,(IX+$SX)
        SBC     $0,&H5B
        JP      Z,BL_OPEN
        JP      ERR_BBR
BL_NEST:
        ADW     $10,$SY
        JP      BL_SCAN
BL_OPEN:
        SBW     $10,$SY
        ANCW    $10,$10
        JP      NZ,BL_SCAN
        ADW     $12,$SY              ; continue after matching [
        PRE     IX,WS_PC
        STW     $12,(IX+$SX)
        RTN

; Current cell -> $8; IX points at current RAM tape byte.
GET_CELL:
        PRE     IX,WS_DP
        LDW     $2,(IX+$SX)
        LDW     $4,TAPE_BAS
        ADW     $2,$4
        PRE     IX,$2
        LD      $8,(IX+$SX)
        RTN

ERR_LEFT:
        LD      $0,ER_LEFT
        JP      SET_ERROR
ERR_RIGHT:
        LD      $0,ER_RIGHT
        JP      SET_ERROR
ERR_FBR:
        LD      $0,ER_FBR
        JP      SET_ERROR
ERR_BBR:
        LD      $0,ER_BBR
        JP      SET_ERROR
SET_ERROR:
        PRE     IX,WS_ERR
        ST      $0,(IX+$SX)
        PRE     IX,WS_STAT
        LD      $0,ST_ERR
        ST      $0,(IX+$SX)
        RTN

; ======================================================================
; SELF-TEST CHECKS - return $0=1 PASS, $0=0 FAIL
; ======================================================================
TPASS:
        LD      $0,1
        RTN
TFAIL:
        LD      $0,0
        RTN

CK_LEN3:
        PRE     IX,WS_LEN
        LDW     $2,(IX+$SX)
        LDW     $4,3
        SBCW    $2,$4
        JP      Z,TPASS
        JP      TFAIL

CK_SRCPLUS:
        PRE     IX,WS_LEN
        LDW     $2,(IX+$SX)
        LDW     $4,1
        SBCW    $2,$4
        JP      NZ,TFAIL
        PRE     IX,SRC_BASE
        LD      $8,(IX+$SX)
        SBC     $8,&H2B
        JP      Z,TPASS
        JP      TFAIL

CK_ONE:
        CAL     CK_DONE
        AN      $0,$0
        JP      Z,TFAIL
        PRE     IX,TAPE_BAS
        LD      $8,(IX+$SX)
        SBC     $8,1
        JP      Z,TPASS
        JP      TFAIL

CK_DONE:
        PRE     IX,WS_STAT
        LD      $0,(IX+$SX)
        SBC     $0,ST_DONE
        JP      Z,TPASS
        JP      TFAIL

CK_DEC:
        CAL     CK_DONE
        AN      $0,$0
        JP      Z,TFAIL
        PRE     IX,TAPE_BAS
        LD      $8,(IX+$SX)
        SBC     $8,&HFF
        JP      Z,TPASS
        JP      TFAIL

CK_ZERO:
        CAL     CK_DONE
        AN      $0,$0
        JP      Z,TFAIL
        PRE     IX,TAPE_BAS
        LD      $8,(IX+$SX)
        AN      $8,$8
        JP      Z,TPASS
        JP      TFAIL

CK_MOVE:
        CAL     CK_DONE
        AN      $0,$0
        JP      Z,TFAIL
        PRE     IX,WS_DP
        LDW     $2,(IX+$SX)
        ANCW    $2,$2
        JP      NZ,TFAIL
        PRE     IX,TAPE_BAS
        LD      $8,(IX+1)
        SBC     $8,1
        JP      Z,TPASS
        JP      TFAIL

; input $9 = expected error
CK_ERROR:
        PRE     IX,WS_STAT
        LD      $0,(IX+$SX)
        SBC     $0,ST_ERR
        JP      NZ,TFAIL
        PRE     IX,WS_ERR
        LD      $0,(IX+$SX)
        SBC     $0,$9
        JP      Z,TPASS
        JP      TFAIL

CK_NEST:
        CAL     CK_DONE
        AN      $0,$0
        JP      Z,TFAIL
        PRE     IX,WS_OUT
        LD      $0,(IX+$SX)
        SBC     $0,&H41
        JP      Z,TPASS
        JP      TFAIL

; ======================================================================
; REUSABLE FIXED-BUFFER EDIT PRIMITIVES
; ======================================================================
#INCLUDE (../lib/textbuf_edit.inc)
#LIST

; ======================================================================
; DISPLAY - accepted Chapter 01 platform mechanism, unchanged in principle.
; ======================================================================
SCREEN_INIT:
        PST     UA,&H14
        CAL     OFFCHK
        PPO     &HC0
        LDW     $0,&H12B5
SI_DELAY:
        SBW     $0,$SY
        JR      NZ,SI_DELAY
        CAL     &H02D4
        PPO     &HC3
        LD      $0,&H0C
        LDW     $1,&H3000
        STLM    $0,3
        LD      $6,$SX
        LD      $8,128
        CAL     &H018D
        CAL     &H0304
        CAL     &H0306
        RTN

PUTC:
        LD      $7,$16
        LD      $0,$7
        DIU     $7
        DID     $0
        OR      $7,$SZ
        CAL     &H0197
        RTN

PRINTZ:
        LDI     $7,(IZ+$SX)
        LD      $0,$7
        DIU     $7
        DID     $0
        OR      $7,$SZ
        RTN     Z
        CAL     &H0197
        JR      PRINTZ

; ======================================================================
; TEST SOURCES - zero terminated ROM strings, copied to ordinary source RAM.
; ======================================================================
S_LEN3:  DB "ABC",0
S_PLUS:  DB "+",0
S_EMPTY: DB 0
S_COM:   DB "abc XYZ",0
S_DEC:   DB "-",0
S_IDEC:  DB "+-",0
S_MOVE:  DB ">+<",0
S_LEFT:  DB "<",0
S_RIGHT: DB ">",0
S_LOOP:  DB "+++[-]",0
S_FBR:   DB "[",0
S_BBR:   DB "+]",0
S_NEST:  DB "++[>++++[>++++++++<-]<-]>>+.",0
S_OUTA:  DB "++++++++[>++++++++<-]>+.",0
S_ECHO:  DB ",.",0
S_ABORT: DB "+[]",0

S_HELLO:
        DB "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+."
        DB "+++++++..+++.>++.<<+++++++++++++++.>.+++.------."
        DB "--------.>+.",0

TXT_TITLE:
        DB "CS617 BF EDITOR R005",0
TXT_SELF:
        DB "EDITOR GATE",0
TXT_PASS:
        DB "ALL 14 PASS - ENGINE GATE",0
TXT_FAIL:
        DB "FAIL - INSPECT TEST DOTS",0

        ORG     &HFFFF
        DB      0
