; ======================================================================
; CS617 CHAPTER 02 - SORTING
; UI-only r0.19 candidate; successor to independently audited r0.18.
; Only displayed wording/centering and these source comments change.
; r0.19 requires owner assembler build and emulator check; NOT stable yet.
; Target: PB-2000C only. AI-1000 compatibility is NOT established.
;
; Changes relative to preserved accepted r0.16:
;   - automatic centered 32x4 splash, visible r0.17, sampled TM timer
;   - cold-only setup/data generation and warm BRK entry, without reload
;   - provisional distinct ID 64 10 at BOTH known identity locations
;   - READN r003 raw-edit/return contract with FK accept -> setup dispatcher
;   - keep sorter, instrumentation, VIEW and the r0.16 BIDW $7 repair
;
; r0.17 was a local untested engineering proposal; r0.16 is preserved
; byte-for-byte in the historical Experience ZIP.
; r0.15 was falsified by n=500,m=2,REVERSE; do NOT remove that regression.
;
; Source stewardship:
;   Sol-maintained baseline. Human changes must be returned with rationale.
;
; Provenance:
;   Standalone header/vector, direct keyboard scanner, KEYSCAN table,
;   display initialization, PRINTZ, and EXE/release discipline are adapted
;   from accepted CS617 Chapter 01 / U32 CALC experience. Application logic
;   beginning at ORG &H0E80 is maintained as Chapter 02 source.
;
; Translated function-key codes inherited from KEYSCAN:
;   FK1 = &HFC
;   FK2 = &HFD
;   FK3 = &HFE
;   FK4 = &HFF
; ======================================================================

        ORG     0
        LEVEL   0

OFFCHK: EQU     &H04EF

FK1:    EQU     &HFC
FK2:    EQU     &HFD
FK3:    EQU     &HFE
FK4:    EQU     &HFF
KEYBS:  EQU     &H08
KEYEXE: EQU     &H0D
KEYUP:  EQU     &H1E
KEYDN:  EQU     &H1F
KEYDEL: EQU     &H11
KEYINS: EQU     &H12
KEYRT:  EQU     &H1C
KEYLT:  EQU     &H1D
KEYMENU:EQU     &H9C
RN_EXE: EQU     0
RN_MENU:EQU     1
RN_BRK: EQU     2
RN_FK1: EQU     3
RN_FK2: EQU     4
RN_FK3: EQU     5
RN_FK4: EQU     6
B_OK:   EQU     0
B_FULL: EQU     1
B_EDGE: EQU     2

; Sprint 01/02 setup state is now allocated as real objects after the code.
; This keeps all mutable application state inside the mirrored RAM image.
PAT_ORD:EQU     0
PAT_REV:EQU     1
PAT_SAME:EQU    2

ERR_NONE:EQU    0
ERR_N:  EQU     1
ERR_M:  EQU     2
ERR_CAP:EQU     3
ERR_UNQ:EQU     4

DATA_BYTES:EQU  &H2000

; ----------------------------------------------------------------------
; Language-card header.
; ----------------------------------------------------------------------
        DB      &H43,&H4D
        DB      &HFF,&HFF,&HFF,&HFF
        DB      &H01,&H04,&H64,&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

; ======================================================================
; COMPLETE SAME-ADDRESS ROM -> RAM MIRROR
;
; &H0C12 is the card-side lower boundary used for this Chapter 02 mirror.
; &H0C10-&H0C11 are the low-level ROM-select / gate-array register area and
; are deliberately excluded.
;
; Everything from COPY_START through COPY_END-1 is assembled in ROM bank 0
; and copied to bank-1 RAM at the identical 16-bit addresses.
; ======================================================================
        ORG     &H0C12

COPY_START:

; ----------------------------------------------------------------------
; BOOT-ROM-derived direct keyboard scanner.
;
; Returns:
;   $0  = translated key code, 0 if none
;   $17 = nonzero if a translated key was found
;
; The Chapter 01 scanner form, without its calculator-local red-shift.
; Here physical BRK dispatches to APP_ENTRY, not cold initialization.
; ----------------------------------------------------------------------
        ORG     &H0C60

INKEY:
        CAL     FW_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,APP_ENTRY

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

; KEYSCAN is part of the mirrored image, so under UA=&H55 IX reads it
; directly from bank-1 RAM. No ROM table hop is required here.
        PRE     IX,KEYSCAN
        LDD     $0,(IX+$4)
        LDD     $0,(IX+$3)
        JR      IKEY50

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

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

IKEYRET:
; The Chapter 02 application consumes $0/$17 directly and does not need the
; historical WORK1 side effect used by the original language environment.
        RTN

; ----------------------------------------------------------------------
; Canonical standalone vector table.
; ----------------------------------------------------------------------
        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      WARM_TRAMP
        JP      DEFRT
        JP      DEFRT
        JP      DEFRT
        JP      DEFRT
        DB      &H64
        DB      &H10

TRAP:   RTNI

POFF:   OFF
        OFF
        OFF
        OFF
        JR      POFF

; BRK vector trampoline: this code is mirrored at identical addresses in
; ROM and RAM. A bank-safe switch, rather than a naked JP into bank-1 RAM,
; is required if firmware invokes the vector under a ROM-bank UA setting.
; Precondition: cold entry has completed the initial ROM->RAM copy.
WARM_TRAMP:
        PRE     SS,&H0100
        PST     UA,&H55
        JP      APP_ENTRY

; ----------------------------------------------------------------------
; BOOT ROM key translation table.
; 11 scan rows x 12 entries.
; ----------------------------------------------------------------------
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

; ======================================================================
; CHAPTER 02 APPLICATION
; ======================================================================
        ORG     &H0E80

MAIN:
; ----------------------------------------------------------------------
; ROM bootstrap.
;
; MAIN executes from the language-card ROM before the mirror exists. Keep
; firmware calls here direct under the accepted standalone UA=&H14 state.
; After LCD initialization, jump forward to the ROM-only LOADER.
; ----------------------------------------------------------------------
        PRE     SS,&H0100

        LD      $30,1
        LD      $31,0
        PSR     SZ,0
        PSR     SY,30
        PSR     SX,31

        PST     UA,&H14
        CAL     OFFCHK
        PPO     &HC0

        LDW     $0,&H12B5
BOOT_DELAY:
        SBW     $0,$SY
        JR      NZ,BOOT_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

        JP      LOADER

; ----------------------------------------------------------------------
; RAM application entry.
;
; COLD entry after LOADER: one-time initialization, generation, splash.
; Only a new card boot is permitted to reach RAM_ENTRY.
; All application code/state and DATA run from same-address bank-1 RAM.
; ----------------------------------------------------------------------
RAM_ENTRY:
; Verify the empirically established RAM execution state before touching
; application state. UA=&H55 implies PC is executing from bank-1 RAM.
        GST     UA,$0
        SBC     $0,&H55
        JR      Z,RAM_OK

RAM_HALT:
        JR      RAM_HALT

RAM_OK:
        CAL     INIT_SETUP

; From this point onward a visible main-screen configuration always has
; matching validated binary values in WS_NVAL / WS_MVAL.
        CAL     VALIDATE_CFG
        AN      $4,$4
        JR      NZ,RAM_HALT

; Materialize the default configuration immediately so VIEW has meaningful
; data even before the user enters SETUP. Later sort paths still regenerate
; immediately before sorting, preserving the experiment boundary.
        CAL     GEN_DATA
        CAL     CLEAR_ALL
        CAL     SHOW_SPLASH
        CAL     WAIT_SPLASH
        JP      APP_ENTRY

; Warm application entry: BRK and initial cold start converge here. No
; INIT_SETUP, GEN_DATA, ROM->RAM copy, splash or timed wait on warm BRK.
; Reset scratch register/stack/display context, but preserve configuration,
; measured counters, and all active DATA bytes for VIEW after BRK.
APP_ENTRY:
        PRE     SS,&H0100
        LD      $30,1
        LD      $31,0
        PSR     SZ,0
        PSR     SY,30
        PSR     SX,31
        GST     UA,$0
        SBC     $0,&H55
        JP      NZ,RAM_HALT
        JP      SHOW_MENU

; ----------------------------------------------------------------------
; Main screen: current configuration plus the accepted 4x8 FK legend.
; ----------------------------------------------------------------------
SHOW_MENU:
        CAL     CLEAR_ALL
        CAL     DRAW_CFG

        LD      $6,&H60
        PRE     IZ,TXT_FKEYS
        CAL     PRINTZ

MENU_LOOP:
        CAL     INKEY

        AN      $17,$17
        JR      Z,MENU_LOOP

        LD      $12,$0

        SBC     $12,FK1
        JR      Z,DO_FK1

        SBC     $12,FK2
        JR      Z,DO_FK2

        SBC     $12,FK3
        JR      Z,DO_FK3

        SBC     $12,FK4
        JR      Z,DO_FK4

        CAL     WAIT_RELEASE
        JR      MENU_LOOP

DO_FK1:
; Do not let the main-screen FK1 press leak into the setup dispatcher.
        CAL     WAIT_RELEASE
        JP      SETUP_SCREEN

DO_FK2:
; Permanent Chapter 02 invariant: every sort request regenerates its input
; immediately before the measured algorithm.
        CAL     WAIT_RELEASE
        CAL     GEN_DATA
        CAL     SHOW_BSORT
        CAL     CLEAR_CNTS
        CAL     BUBBLE_SORT

; Validation deliberately uses the non-counting comparator entry point.
        CAL     VALID_SORT
        AN      $0,$0
        JP      NZ,SHOW_BFAIL

        CAL     SHOW_BREPORT
        JP      SHOW_MENU

DO_FK3:
; Shell uses the identical experiment boundary as Bubble: regenerate the
; configured input, clear only the measured counters, sort, assert, report.
        CAL     WAIT_RELEASE
        CAL     GEN_DATA
        CAL     SHOW_SSORT
        CAL     CLEAR_CNTS
        CAL     SHELL_SORT

        CAL     VALID_SORT
        AN      $0,$0
        JP      NZ,SHOW_SFAIL

        CAL     SHOW_SREPORT
        JP      SHOW_MENU

DO_FK4:
        JP      VIEW_SCREEN

; ======================================================================
; SPRINT 03 - READ-ONLY DATA VIEW
; ======================================================================
;
; VIEW is intentionally not a general record browser. It is a small
; observability surface for deterministic-generation and later sort results.
;
; Four records are shown, one per physical display row:
;
;   0001: FF FF FF FF FF FF FF FF FF
;
; The record number is 1-based decimal and always four digits. At most the
; leftmost nine bytes are shown; wider records are deliberately not scrolled
; horizontally. Cursor Up/Down move the window by one record. EXE returns.
;
; VIEW never generates, edits, compares, or sorts DATA.
; ----------------------------------------------------------------------
VIEW_SCREEN:
; Do not let the main-menu FK4 event leak into the viewer.
        CAL     WAIT_RELEASE

        PRE     IX,WS_VTOP
        SBW     $20,$20
        STW     $20,(IX+$SX)

        PRE     IX,WS_VPTR
        LDW     $20,DATA
        STW     $20,(IX+$SX)

        CAL     DRAW_VIEW

VIEW_LOOP:
        CAL     INKEY
        AN      $17,$17
        JR      Z,VIEW_LOOP

        LD      $12,$0

        SBC     $12,KEYUP
        JR      Z,VIEW_UP

        SBC     $12,KEYDN
        JR      Z,VIEW_DOWN

        SBC     $12,KEYEXE
        JR      Z,VIEW_EXIT

        CAL     WAIT_RELEASE
        JR      VIEW_LOOP

VIEW_EXIT:
        CAL     WAIT_RELEASE
        JP      SHOW_MENU

VIEW_UP:
; Returns from the scrolling handlers use JP, not JR. These routines are
; large enough that later source growth can exceed the IM7 relative range.
        CAL     WAIT_RELEASE

        PRE     IX,WS_VTOP
        LDW     $20,(IX+$SX)
        ANCW    $20,$20
        JP      Z,VIEW_LOOP

        SBW     $20,$SY
        STW     $20,(IX+$SX)

        PRE     IX,WS_MVAL
        LDW     $22,(IX+$SX)

        PRE     IX,WS_VPTR
        LDW     $24,(IX+$SX)
        SBW     $24,$22
        STW     $24,(IX+$SX)

        CAL     DRAW_VIEW
        JP      VIEW_LOOP

VIEW_DOWN:
        CAL     WAIT_RELEASE

; Allow a one-record scroll while the next top record is still < n.
        PRE     IX,WS_VTOP
        LDW     $20,(IX+$SX)

        PRE     IX,WS_NVAL
        LDW     $22,(IX+$SX)

        LDW     $24,$20
        ADW     $24,$SY
        SBCW    $24,$22
        JP      NC,VIEW_LOOP

        PRE     IX,WS_VTOP
        STW     $24,(IX+$SX)

        PRE     IX,WS_MVAL
        LDW     $22,(IX+$SX)

        PRE     IX,WS_VPTR
        LDW     $20,(IX+$SX)
        ADW     $20,$22
        STW     $20,(IX+$SX)

        CAL     DRAW_VIEW
        JP      VIEW_LOOP

; ----------------------------------------------------------------------
; DRAW_VIEW
;
; Uses the persisted top-record index and byte pointer. Four rows are drawn
; or fewer if the window reaches the end of DATA's active record population.
; ----------------------------------------------------------------------
DRAW_VIEW:
        CAL     CLEAR_ALL

        PRE     IX,WS_NVAL
        LDW     $20,(IX+$SX)

        PRE     IX,WS_VTOP
        LDW     $22,(IX+$SX)

; $20 = records remaining from this top position.
        SBW     $20,$22

; Displayed record numbers are 1-based.
        ADW     $22,$SY

        PRE     IX,WS_VPTR
        LDW     $24,(IX+$SX)

        PRE     IX,WS_MVAL
        LDW     $26,(IX+$SX)

        LD      $28,4
        LD      $29,0

DV_LOOP:
        ANCW    $20,$20
        RTN     Z

        AN      $28,$28
        RTN     Z

        LD      $6,$29
        CAL     VIEW_LINE

        SBW     $20,$SY
        ADW     $22,$SY
        ADW     $24,$26
        SB      $28,$30
        AD      $29,&H20
        JR      DV_LOOP

; ----------------------------------------------------------------------
; VIEW_LINE
;
; Inputs:
;   $22/$23 = 1-based record number
;   $24/$25 = address of first byte of record
;   $26/$27 = m
;   $6      = firmware display position for this row
;
; A complete 32-byte display row is first assembled in WS_VLINE, then emitted
; through the accepted PRINT_RAM/PUTC path.
; ----------------------------------------------------------------------
VIEW_LINE:
        PRE     IX,WS_VLINE

; Four decimal digits, zero padded.
        LDW     $8,$22

        LDW     $10,&H03E8
        CAL     DEC_ST

        LDW     $10,&H0064
        CAL     DEC_ST

        LDW     $10,&H000A
        CAL     DEC_ST

        LD      $16,$8
        AD      $16,&H30
        STI     $16,(IX+$SX)

        LD      $16,":"
        STI     $16,(IX+$SX)

; Number of visible bytes = min(m,9).
        LD      $13,9
        AN      $27,$27
        JR      NZ,VL_COUNT

        LD      $11,$26
        SBC     $11,10
        JR      NC,VL_COUNT

        LD      $13,$26

VL_COUNT:
        LDW     $14,$24
        LD      $18,5

VL_BYTES:
        AN      $13,$13
        JR      Z,VL_FILL

        LD      $16,&H20
        STI     $16,(IX+$SX)

        LD      $10,($14)
        ADW     $14,$SY
        CAL     HEX_ST

        AD      $18,3
        SB      $13,$30
        JR      VL_BYTES

VL_FILL:
        SBC     $18,32
        JR      NC,VL_PRINT

        LD      $16,&H20
        STI     $16,(IX+$SX)
        AD      $18,$SY
        JR      VL_FILL

VL_PRINT:
        PRE     IX,WS_VLINE
        LD      $13,32
        JP      PRINT_RAM

; Store one zero-padded decimal place into the current IX output position.
; Input: $8/$9 remainder, $10/$11 place value.
DEC_ST:
        LD      $12,0

DS_LOOP:
        SBCW    $8,$10
        JR      C,DS_OUT

        SBW     $8,$10
        AD      $12,$SY
        JR      DS_LOOP

DS_OUT:
        LD      $16,$12
        AD      $16,&H30
        STI     $16,(IX+$SX)
        RTN

; Store one byte as two uppercase hexadecimal digits.
; Input: $10 = byte. Output advances IX by two bytes.
HEX_ST:
        LD      $11,0

HS_LOOP:
        SBC     $10,&H10
        JR      C,HS_HIGH

        SB      $10,&H10
        AD      $11,$SY
        JR      HS_LOOP

HS_HIGH:
        LD      $16,$11
        CAL     HEX_ASCII
        STI     $16,(IX+$SX)

        LD      $16,$10
        CAL     HEX_ASCII
        STI     $16,(IX+$SX)
        RTN

HEX_ASCII:
        AN      $16,&H0F
        AD      $16,&H30
        SBC     $16,&H3A
        RTN     C
        AD      $16,&H07
        RTN

; ----------------------------------------------------------------------
; Sprint 01 SETUP screen.
;
;   FK1 = edit n
;   FK2 = edit m
;   FK3 = cycle ORDERED -> REVERSE -> SAME -> ORDERED
;   FK4 = done / return to main screen
;
; READN's r0.03 raw overwrite/append behavior applies to the five cells;
; LEFT/RIGHT, one-shot INS-space, DEL/BS, EXE and FK1..FK4 are recognized.
; FK presses finish raw editing and return distinct RN_FKx reasons, which the
; caller then maps to the existing setup handler. MENU cancels the staged edit.
; Decimal interpretation remains in VALIDATE_CFG/ PARSE_DEC.  No unvalidated
; raw text may enter the sorter; FK4 enforces the validation gate.
; ----------------------------------------------------------------------
SETUP_SCREEN:
        CAL     DRAW_SETUP

SETUP_LOOP:
        CAL     INKEY
        AN      $17,$17
        JR      Z,SETUP_LOOP

        LD      $12,$0

        SBC     $12,FK1
        JR      Z,SU_N

        SBC     $12,FK2
        JR      Z,SU_M

        SBC     $12,FK3
        JR      Z,SU_PAT

        SBC     $12,FK4
        JR      Z,SU_DONE

        CAL     WAIT_RELEASE
        JR      SETUP_LOOP

SU_N:
        CAL     WAIT_RELEASE
        CAL     EDIT_N
        JP      SU_ROUTE

SU_M:
        CAL     WAIT_RELEASE
        CAL     EDIT_M
        JP      SU_ROUTE

; READN returns the reason *without* executing any application action.
; FKx is precisely EXE followed by the matching action in SETUP.
; Only the caller validates before actions that use configuration data.
SU_ROUTE:
        SBC     $8,RN_FK1
        JP      Z,SU_N
        SBC     $8,RN_FK2
        JP      Z,SU_M
        SBC     $8,RN_FK3
        JP      Z,SU_PAT
        SBC     $8,RN_FK4
        JP      Z,SU_DONE
; EXE finishes the editor; MENU cancels the staged edit.  In either case
; remain at SETUP instead of committing an invalid configuration to DATA.
        JP      SETUP_SCREEN

SU_PAT:
        CAL     WAIT_RELEASE
        CAL     CYCLE_PAT
        JP      SETUP_SCREEN

SU_DONE:
        CAL     WAIT_RELEASE
        CAL     VALIDATE_CFG
        AN      $4,$4
        JR      NZ,SU_BAD

; Successful SETUP owns regeneration of the configured input. VIEW can now
; inspect those bytes immediately after returning to the main menu.
        CAL     GEN_DATA
        JP      SHOW_MENU

SU_BAD:
        CAL     SHOW_ERROR
        JP      SETUP_SCREEN

; ======================================================================
; CHAPTER 02 LOCAL READN: r0.03 CONTRACT ADAPTATION
;
; EDIT_N/M stage a five-byte raw edit; MENU discards the staging buffer.
; READN receives $0=base, $2=len, $4=cap (words), $6=LCD origin, and
; returns $0=base, $2=new len, $8=RN_EXE/RN_MENU/RN_FK1..RN_FK4.
; The function-key identity is the termination reason, not a hidden action.
;
; The B_* buffer primitives below are preserved from accepted standalone
; READN r0.03 source (cs617/tst/readn); app uses direct scanner because
; its existing RAM mirror at 0C12..COPY_END overlaps the C-ROM bridge at
; RCS=0EFE.  This derivative does NOT claim the native LEDTP/C-ROM cursor
; service was transplanted or separately tested.  Local LCD redraw instead
; restores the covered glyph during its narrow text-cursor blink.
; ======================================================================
EDIT_N:
        LDW     $20,WS_NBUF
        LDW     $22,RN_BUF
        CAL     RN_COPY5
        PRE     IX,WS_NLEN
        LD      $2,(IX+$SX)
        LD      $3,0
        LDW     $0,RN_BUF
        LDW     $4,5
        LD      $6,3
        CAL     READN
        PRE     IX,RN_REASON
        LD      $8,(IX+$SX)
        SBC     $8,RN_MENU
        JR      Z,EN_RET
; EXE and FKx both accept raw text, even when temporarily invalid.
        LDW     $20,RN_BUF
        LDW     $22,WS_NBUF
        CAL     RN_COPY5
        PRE     IX,RN_LEN
        LD      $2,(IX+$SX)
        PRE     IX,WS_NLEN
        ST      $2,(IX+$SX)
EN_RET:
        PRE     IX,RN_REASON
        LD      $8,(IX+$SX)
        RTN

EDIT_M:
        LDW     $20,WS_MBUF
        LDW     $22,RN_BUF
        CAL     RN_COPY5
        PRE     IX,WS_MLEN
        LD      $2,(IX+$SX)
        LD      $3,0
        LDW     $0,RN_BUF
        LDW     $4,5
        LD      $6,&H23
        CAL     READN
        PRE     IX,RN_REASON
        LD      $8,(IX+$SX)
        SBC     $8,RN_MENU
        JR      Z,EM_RET
        LDW     $20,RN_BUF
        LDW     $22,WS_MBUF
        CAL     RN_COPY5
        PRE     IX,RN_LEN
        LD      $2,(IX+$SX)
        PRE     IX,WS_MLEN
        ST      $2,(IX+$SX)
EM_RET:
        PRE     IX,RN_REASON
        LD      $8,(IX+$SX)
        RTN

; $20/$21 source, $22/$23 destination; fixed five-byte copy.
; Direct indexed-word accesses are already accepted in the sorter core.
RN_COPY5:
        LD      $13,5
RC5_LOOP:
        LD      $16,($20)
        ST      $16,($22)
        ADW     $20,$SY
        ADW     $22,$SY
        SB      $13,$SY
        JR      NZ,RC5_LOOP
        RTN

READN:
        PRE     IX,RN_BASE
        STW     $0,(IX+$SX)
        PRE     IX,RN_LEN
        STW     $2,(IX+$SX)
        PRE     IX,RN_CAP
        STW     $4,(IX+$SX)
        PRE     IX,RN_DBASE
        ST      $6,(IX+$SX)
        SBW     $20,$20
        PRE     IX,RN_POS
        STW     $20,(IX+$SX)
        PRE     IX,RN_REASON
        ST      $31,(IX+$SX)
        CAL     RN_REFRESH

RN_LOOP:
        CAL     INKEY
        AN      $17,$17
        JP      Z,RN_IDLE
        LD      $12,$0
        SBC     $12,KEYEXE
        JP      Z,RN_EXIT_EXE
        SBC     $12,KEYMENU
        JP      Z,RN_EXIT_MENU
        SBC     $12,FK1
        JP      Z,RN_EXIT_FK1
        SBC     $12,FK2
        JP      Z,RN_EXIT_FK2
        SBC     $12,FK3
        JP      Z,RN_EXIT_FK3
        SBC     $12,FK4
        JP      Z,RN_EXIT_FK4
        SBC     $12,KEYINS
        JP      Z,RN_INSERT
        SBC     $12,KEYLT
        JP      Z,RN_LEFT
        SBC     $12,KEYRT
        JP      Z,RN_RIGHT
        SBC     $12,KEYDEL
        JP      Z,RN_DELETE
        SBC     $12,KEYBS
        JP      Z,RN_BACKSP
        SBC     $12,&H20
        JP      C,RN_IGNORE
        SBC     $12,&H7F
        JP      NC,RN_IGNORE
        LD      $16,$12
        CAL     RN_LOADBUF
        CAL     B_OVR
        JP      RN_EDITED

RN_INSERT:
        CAL     RN_LOADBUF
        LD      $16,&H20
        CAL     B_INS
        AN      $8,$8
        JR      NZ,RN_EDITED
        SBW     $6,$SY
        JP      RN_EDITED
RN_DELETE:
        CAL     RN_LOADBUF
        CAL     B_DEL
        JP      RN_EDITED
RN_BACKSP:
        CAL     RN_LOADBUF
        CAL     B_BS
        JP      RN_EDITED
RN_LEFT:
        CAL     RN_LOADBUF
        CAL     B_LEFT
        JP      RN_EDITED
RN_RIGHT:
        CAL     RN_LOADBUF
        CAL     B_RIGHT
        JP      RN_EDITED

RN_EDITED:
        CAL     RN_SAVEBUF
        CAL     WAIT_RELEASE
        CAL     RN_REFRESH
        JP      RN_LOOP
RN_IGNORE:
        CAL     WAIT_RELEASE
        JP      RN_LOOP
RN_IDLE:
        CAL     RN_BLINK
        JP      RN_LOOP

RN_EXIT_EXE:
        LD      $8,RN_EXE
        JR      RN_EXIT
RN_EXIT_MENU:
        LD      $8,RN_MENU
        JR      RN_EXIT
RN_EXIT_FK1:
        LD      $8,RN_FK1
        JR      RN_EXIT
RN_EXIT_FK2:
        LD      $8,RN_FK2
        JR      RN_EXIT
RN_EXIT_FK3:
        LD      $8,RN_FK3
        JR      RN_EXIT
RN_EXIT_FK4:
        LD      $8,RN_FK4
RN_EXIT:
        PRE     IX,RN_REASON
        ST      $8,(IX+$SX)
        CAL     RN_PAINT       ; return with no underline left on screen
        CAL     WAIT_RELEASE
        PRE     IX,RN_BASE
        LDW     $0,(IX+$SX)
        PRE     IX,RN_LEN
        LDW     $2,(IX+$SX)
        PRE     IX,RN_REASON
        LD      $8,(IX+$SX)
        RTN

RN_LOADBUF:
        PRE     IX,RN_BASE
        LDW     $0,(IX+$SX)
        PRE     IX,RN_LEN
        LDW     $2,(IX+$SX)
        PRE     IX,RN_CAP
        LDW     $4,(IX+$SX)
        PRE     IX,RN_POS
        LDW     $6,(IX+$SX)
        RTN
RN_SAVEBUF:
        PRE     IX,RN_LEN
        STW     $2,(IX+$SX)
        PRE     IX,RN_POS
        STW     $6,(IX+$SX)
        RTN

; Paint only the five editable cells; do not overwrite setup headings or
; function-key legends.  Cursor uses the existing native LCD cell renderer.
RN_PAINT:
        PRE     IX,RN_DBASE
        LD      $6,(IX+$SX)
        PRE     IX,RN_CAP
        LDW     $18,(IX+$SX)
RP_CLEAR:
        ANCW    $18,$18
        JR      Z,RP_TEXT
        LD      $16,&H20
        CAL     PUTC
        SBW     $18,$SY
        JR      RP_CLEAR
RP_TEXT:
        PRE     IX,RN_BASE
        LDW     $20,(IX+$SX)
        PRE     IX,RN_LEN
        LDW     $18,(IX+$SX)
        PRE     IX,RN_DBASE
        LD      $6,(IX+$SX)
RP_LOOP:
        ANCW    $18,$18
        RTN     Z
        PRE     IX,$20
        LD      $16,(IX+$SX)
        CAL     PUTC
        ADW     $20,$SY
        SBW     $18,$SY
        JR      RP_LOOP

RN_REFRESH:
        CAL     RN_PAINT
        PRE     IX,WS_CUR
        LD      $0,1
        ST      $0,(IX+$SX)
        PRE     IX,WS_TICK
        ST      $31,(IX+$SX)
        JP      RN_CURSOR

; A narrow underscore overlay with exact restoration of the covered cell.
; The native C-ROM LEDTP-backed underline is intentionally NOT claimed.
RN_CURSOR:
        PRE     IX,RN_POS
        LDW     $10,(IX+$SX)
        PRE     IX,RN_DBASE
        LD      $6,(IX+$SX)
        AD      $6,$10
        PRE     IX,WS_CUR
        LD      $8,(IX+$SX)
        AN      $8,$8
        JR      Z,RNC_ORIG
        LD      $16,&H5F
        JP      PUTC
RNC_ORIG:
        PRE     IX,RN_LEN
        LDW     $12,(IX+$SX)
        SBCW    $10,$12
        JR      NC,RNC_SPACE
        PRE     IX,RN_BASE
        LDW     $14,(IX+$SX)
        ADW     $14,$10
        PRE     IX,$14
        LD      $16,(IX+$SX)
        JP      PUTC
RNC_SPACE:
        LD      $16,&H20
        JP      PUTC

RN_BLINK:
        PRE     IX,WS_TICK
        LD      $0,(IX+$SX)
        AD      $0,$SY
        SBC     $0,128
        JR      Z,RNB_TOGGLE
        ST      $0,(IX+$SX)
        RTN
RNB_TOGGLE:
        LD      $0,0
        ST      $0,(IX+$SX)
        PRE     IX,WS_CUR
        LD      $1,(IX+$SX)
        AN      $1,$1
        JR      Z,RNB_ON
        LD      $1,0
        ST      $1,(IX+$SX)
        JP      RN_CURSOR
RNB_ON:
        LD      $1,1
        ST      $1,(IX+$SX)
        JP      RN_CURSOR

; ----------------------------------------------------------------------
; Generic fixed-buffer byte operations.
; Contract:
;   $0/$1 base, $2/$3 len, $4/$5 cap, $6/$7 pos, $16 input byte
;   $8 status: B_OK/B_FULL/B_EDGE
; ----------------------------------------------------------------------

; Overwrite at cursor.  At end, append if capacity permits.
B_OVR:
        LDW     $10,$6
        SBCW    $10,$2
        JR      C,BO_REPLACE
        JR      Z,BO_APPEND
        LD      $8,B_EDGE
        RTN

BO_REPLACE:
        LDW     $10,$0
        ADW     $10,$6
        PRE     IX,$10
        ST      $16,(IX+$SX)
        ADW     $6,$SY
        LD      $8,B_OK
        RTN

BO_APPEND:
        LDW     $10,$2
        SBCW    $10,$4
        JR      C,BO_APOK
        LD      $8,B_FULL
        RTN
BO_APOK:
        LDW     $10,$0
        ADW     $10,$2
        PRE     IX,$10
        ST      $16,(IX+$SX)
        ADW     $2,$SY
        ADW     $6,$SY
        LD      $8,B_OK
        RTN

; Insert at cursor. Shift suffix right one byte.
B_INS:
        LDW     $10,$2
        SBCW    $10,$4
        JR      C,BI_CUR
        LD      $8,B_FULL
        RTN
BI_CUR:
        LDW     $10,$6
        SBCW    $10,$2
        JR      Z,BI_APPEND
        JR      C,BI_SHIFT
        LD      $8,B_EDGE
        RTN

BI_SHIFT:
; destination=base+len, source=destination-1, stop=base+pos
        LDW     $10,$0
        ADW     $10,$2
        LDW     $12,$10
        SBW     $12,$SY
        LDW     $14,$0
        ADW     $14,$6
BI_LOOP:
        PRE     IX,$12
        LD      $9,(IX+$SX)
        PRE     IX,$10
        ST      $9,(IX+$SX)
        LDW     $8,$12
        SBCW    $8,$14
        JR      Z,BI_PUT
        SBW     $12,$SY
        SBW     $10,$SY
        JR      BI_LOOP

BI_APPEND:
        LDW     $14,$0
        ADW     $14,$2
BI_PUT:
        PRE     IX,$14
        ST      $16,(IX+$SX)
        ADW     $2,$SY
        ADW     $6,$SY
        LD      $8,B_OK
        RTN

; Delete byte at cursor.
B_DEL:
        LDW     $10,$6
        SBCW    $10,$2
        JR      C,BD_HAVE
        LD      $8,B_EDGE
        RTN
BD_HAVE:
; count=len-pos-1, destination=base+pos, source=destination+1
        LDW     $10,$2
        SBW     $10,$6
        SBW     $10,$SY
        LDW     $12,$0
        ADW     $12,$6
        LDW     $14,$12
        ADW     $14,$SY
BD_LOOP:
        ANCW    $10,$10
        JR      Z,BD_DONE
        PRE     IX,$14
        LD      $9,(IX+$SX)
        PRE     IX,$12
        ST      $9,(IX+$SX)
        ADW     $14,$SY
        ADW     $12,$SY
        SBW     $10,$SY
        JR      BD_LOOP
BD_DONE:
        SBW     $2,$SY
        LD      $8,B_OK
        RTN

B_BS:
        ANCW    $6,$6
        JR      NZ,BB_GO
        LD      $8,B_EDGE
        RTN
BB_GO:
        SBW     $6,$SY
        JP      B_DEL

B_LEFT:
        ANCW    $6,$6
        JR      NZ,BL_GO
        LD      $8,B_EDGE
        RTN
BL_GO:
        SBW     $6,$SY
        LD      $8,B_OK
        RTN

B_RIGHT:
        LDW     $10,$6
        SBCW    $10,$2
        JR      C,BR_GO
        LD      $8,B_EDGE
        RTN
BR_GO:
        ADW     $6,$SY
        LD      $8,B_OK
        RTN


; ======================================================================
; SPRINT 02 - DECIMAL CONVERSION AND CONFIGURATION VALIDATION
; ======================================================================

; ----------------------------------------------------------------------
; VALIDATE_CFG
;
; Output:
;   $4 = ERR_NONE on success, otherwise ERR_*.
;
; On success:
;   WS_NVAL = n as unsigned 32-bit value, low word first
;   WS_MVAL = m as unsigned 32-bit value, low word first
;
; Validation order:
;   1. n is nonempty and 1..8192
;   2. m is nonempty and 1..8192
;   3. n*m <= 8192
;   4. ORDERED/REVERSE uniqueness feasibility
;
; Since valid n,m are each <=8192, the uniqueness bound only needs a special
; case for m=1. For m>=2, 256^m-1 >= 65535, already above DATA_BYTES.
; SAME is deliberately exempt from uniqueness.
; ----------------------------------------------------------------------
VALIDATE_CFG:
; n
        PRE     IX,WS_NLEN
        LD      $13,(IX+$SX)
        PRE     IX,WS_NBUF
        CAL     PARSE_DEC
        AN      $4,$4
        JR      NZ,VC_NERR

        PRE     IX,WS_NVAL
        STM     $15,(IX+$SX),4

; m
        PRE     IX,WS_MLEN
        LD      $13,(IX+$SX)
        PRE     IX,WS_MBUF
        CAL     PARSE_DEC
        AN      $4,$4
        JR      NZ,VC_MERR

        PRE     IX,WS_MVAL
        STM     $15,(IX+$SX),4

; product = n * m using accepted Chapter 01 unsigned multiply core.
        PRE     IX,WS_NVAL
        LDM     $15,(IX+$SX),4
        PRE     IX,WS_MVAL
        LDM     $5,(IX+$SX),4
        CAL     UMUL32

; Product must be <= 8192 (&H00002000).
        ANCW    $17,$17
        JR      NZ,VC_CAPER

        LDW     $5,&H2001
        SBCW    $15,$5
        JR      NC,VC_CAPER

; SAME allows duplicates, so uniqueness is irrelevant.
        PRE     IX,WS_PAT
        LD      $9,(IX+$SX)
        SBC     $9,PAT_SAME
        JR      Z,VC_OK

; For ORDERED/REVERSE only m=1 needs an additional feasibility check.
        PRE     IX,WS_MVAL
        LDM     $15,(IX+$SX),4
        ANCW    $17,$17
        JR      NZ,VC_OK
        SBCW    $15,$SY
        JR      NZ,VC_OK

; m=1: sequence starts at 1, so n must be <= 255.
        PRE     IX,WS_NVAL
        LDM     $15,(IX+$SX),4
        LDW     $5,256
        SBCW    $15,$5
        JR      C,VC_OK
        JR      VC_UNQER

VC_NERR:
        LD      $4,ERR_N
        JR      VC_STORE

VC_MERR:
        LD      $4,ERR_M
        JR      VC_STORE

VC_CAPER:
        LD      $4,ERR_CAP
        JR      VC_STORE

VC_UNQER:
        LD      $4,ERR_UNQ
        JR      VC_STORE

VC_OK:
        LD      $4,ERR_NONE

VC_STORE:
        PRE     IX,WS_ERR
        ST      $4,(IX+$SX)
        RTN

; ----------------------------------------------------------------------
; PARSE_DEC
;
; Entry:
;   IX  -> ASCII decimal buffer
;   $13 = length, 0..5
;
; Output:
;   $15-$18 = unsigned value
;   $4 = 0 if value is 1..8192, 1 otherwise
;
; Digits are already guaranteed by the Sprint 01 editor. The accepted Chapter
; 01 UDECAPP primitive performs value = value*10 + digit.
; ----------------------------------------------------------------------
PARSE_DEC:
        AN      $13,$13
        JR      Z,PD_BAD

        SBW     $15,$15
        SBW     $17,$17
        LD      $12,0

PD_LOOP:
        SBC     $12,$13
        JR      NC,PD_RANGE

        LD      $5,(IX+$12)
; READN is RAW: numeric interpretation/validation is caller-owned.
        SBC     $5,&H30
        JP      C,PD_BAD
        SBC     $5,&H3A
        JP      NC,PD_BAD
        SB      $5,&H30
        CAL     UDECAPP
        AN      $4,$4
        JR      NZ,PD_BAD

        AD      $12,$SY
        JR      PD_LOOP

PD_RANGE:
; High word nonzero is automatically > 8192.
        ANCW    $17,$17
        JR      NZ,PD_BAD

; Zero is illegal.
        ANCW    $15,$15
        JR      Z,PD_BAD

; Accept values < 8193, i.e. <= 8192.
        LDW     $5,&H2001
        SBCW    $15,$5
        JR      C,PD_OK

PD_BAD:
        LD      $4,1
        RTN

PD_OK:
        LD      $4,0
        RTN

; ----------------------------------------------------------------------
; Validation error screen.
; ----------------------------------------------------------------------
SHOW_ERROR:
        CAL     CLEAR_ALL

        LD      $6,$SX
        PRE     IZ,TXT_BADSET
        CAL     PRINTZ

        PRE     IX,WS_ERR
        LD      $9,(IX+$SX)

        SBC     $9,ERR_N
        JR      Z,SE_N

        SBC     $9,ERR_M
        JR      Z,SE_M

        SBC     $9,ERR_CAP
        JR      Z,SE_CAP

        PRE     IZ,TXT_EUNQ
        JR      SE_MSG

SE_N:
        PRE     IZ,TXT_EN
        JR      SE_MSG

SE_M:
        PRE     IZ,TXT_EM
        JR      SE_MSG

SE_CAP:
        PRE     IZ,TXT_ECAP

SE_MSG:
        LD      $6,&H20
        CAL     PRINTZ

        LD      $6,&H60
        PRE     IZ,TXT_EXERET
        CAL     PRINTZ

        CAL     WAIT_EXE
        RTN

; ======================================================================
; SPRINT 03 - DETERMINISTIC DATA GENERATION
; ======================================================================
;
; GEN_DATA consumes only a configuration already accepted by VALIDATE_CFG.
; It writes exactly n*m active bytes beginning at DATA and does not touch the
; remainder of the 8192-byte DATA object.
;
; Because n*m <= 8192 and n >= 1, every valid n is <=8192. Therefore the
; integer sequence value always fits in 16 bits. That lets us encode arbitrary
; m-byte keys without imposing any extra maximum on m:
;
;   m = 1 : low byte only (validation already guarantees n <= 255)
;   m >=2 : (m-2) leading zero bytes, then high byte, then low byte
;
; This is exactly the required unsigned binary MSB-first representation of
; sequence values 1..n under the Chapter 02 capacity bound.
;
; Register plan:
;   IX       current DATA write pointer
;   $20/$21  records remaining
;   $22/$23  m
;   $24/$25  current sequence value (low/high)
;   $26/$27  bytes remaining inside current record
;   $28      pattern
;   $29      scratch / SAME fill byte
;
; On return IX points one byte past the active n*m region. No later code may
; rely on that incidental return value; DATA + n*m remains the semantic end.
; ----------------------------------------------------------------------
GEN_DATA:
        PRE     IX,DATA

        PRE     IZ,WS_NVAL
        LDM     $20,(IZ+$SX),2

        PRE     IZ,WS_MVAL
        LDM     $22,(IZ+$SX),2

        PRE     IZ,WS_PAT
        LD      $28,(IZ+$SX)

; SAME is a direct byte fill and is deliberately exempt from sequence logic.
        LD      $29,$28
        SBC     $29,PAT_SAME
        JP      Z,GEN_SAME

; ORDERED begins at 1. REVERSE begins at n. Both then use exactly the same
; per-record encoder, which guarantees that REVERSE is the same population.
        LDW     $24,1
        LD      $29,$28
        SBC     $29,PAT_REV
        JR      NZ,GEN_SEQ_REC
        LDW     $24,$20

GEN_SEQ_REC:
        ANCW    $20,$20
        RTN     Z

; Copy m to the per-record byte counter.
        LDW     $26,$22

; m=1 is special only because there is no high-byte slot.
        SBW     $26,$SY
        JR      Z,GEN_SEQ_ONE

; m>=2: after the two subtractions $26/$27 = m-2 leading zero bytes.
        SBW     $26,$SY

GEN_SEQ_ZERO:
        ANCW    $26,$26
        JR      Z,GEN_SEQ_TAIL

        STI     $31,(IX+$SX)
        SBW     $26,$SY
        JR      GEN_SEQ_ZERO

GEN_SEQ_TAIL:
; $24/$25 is a little-endian register pair; emit high then low for an MSB-first
; record representation in memory.
        STI     $25,(IX+$SX)
        STI     $24,(IX+$SX)
        JR      GEN_SEQ_NEXT

GEN_SEQ_ONE:
        STI     $24,(IX+$SX)

GEN_SEQ_NEXT:
        SBW     $20,$SY

        LD      $29,$28
        SBC     $29,PAT_REV
        JR      Z,GEN_SEQ_DEC

        ADW     $24,$SY
        JP      GEN_SEQ_REC

GEN_SEQ_DEC:
        SBW     $24,$SY
        JP      GEN_SEQ_REC

GEN_SAME:
        LD      $29,&HFF

GEN_SAME_REC:
        ANCW    $20,$20
        RTN     Z

        LDW     $26,$22

GEN_SAME_BYTE:
        STI     $29,(IX+$SX)
        SBW     $26,$SY
        JR      NZ,GEN_SAME_BYTE

        SBW     $20,$SY
        JR      GEN_SAME_REC

; ======================================================================
; SPRINT 04 - COMPARATOR, COUNTERS, VALIDATOR, BUBBLE
; ======================================================================
;
; Comparator contract
; -------------------
; Inputs:
;   $20/$21 = left record address
;   $22/$23 = right record address
;   $24/$25 = key width m
;
; Return:
;   $0 = &HFF  left < right
;   $0 = &H00  left = right
;   $0 = &H01  left > right
;
; CMP_SORT counts one key comparison and every executed key-data SBC.
; CMP_RAW performs the identical lexicographic comparison without touching
; either counter; VALID_SORT uses this entry so validation is excluded from
; the measured sorting counters.
;
; The only instruction comparing key data against key data is:
;
;       SBC     $10,$11
;
; SBC is the HD61700 "subtract check": it changes flags without storing the
; subtraction result. Carry means borrow, therefore unsigned left < right.
; ----------------------------------------------------------------------

CLEAR_CNTS:
        SBW     $2,$2
        SBW     $4,$4

        PRE     IX,WS_KCMP
        STM     $2,(IX+$SX),4

        PRE     IX,WS_ICMP
        STM     $2,(IX+$SX),4
        RTN

INC_KCMP:
        PRE     IX,WS_KCMP
        LDM     $2,(IX+$SX),4
        ADW     $2,$SY
        JR      NC,IK_STORE
        ADW     $4,$SY
IK_STORE:
        STM     $2,(IX+$SX),4
        RTN

INC_ICMP:
        PRE     IX,WS_ICMP
        LDM     $2,(IX+$SX),4
        ADW     $2,$SY
        JR      NC,IL_STORE
        ADW     $4,$SY
IL_STORE:
        STM     $2,(IX+$SX),4
        RTN

CMP_SORT:
        CAL     INC_KCMP
        LD      $19,1
        JP      CMP_CORE

CMP_RAW:
        LD      $19,0

CMP_CORE:
        LDW     $26,$24

CMP_LOOP:
        ANCW    $26,$26
        JR      Z,CMP_EQ

        LD      $10,($20)
        LD      $11,($22)

; Count immediately before the one key-data check instruction. Counter
; maintenance may alter flags, but the following SBC establishes fresh flags.
        AN      $19,$19
        JR      Z,CMP_DO
        CAL     INC_ICMP

CMP_DO:
        SBC     $10,$11
        JR      C,CMP_LT
        JR      NZ,CMP_GT

        ADW     $20,$SY
        ADW     $22,$SY
        SBW     $26,$SY
        JR      CMP_LOOP

CMP_LT:
        LD      $0,&HFF
        RTN

CMP_GT:
        LD      $0,1
        RTN

CMP_EQ:
        LD      $0,0
        RTN

; ----------------------------------------------------------------------
; SWAP_REC
;
; In-place byte-wise exchange. No m-byte temporary record is required for
; Bubble: one byte from each record is enough because comparison is complete
; before the swap begins.
;
; Inputs:
;   $20/$21 = left record
;   $22/$23 = right record
;   $24/$25 = m
; ----------------------------------------------------------------------
SWAP_REC:
        ANCW    $24,$24
        RTN     Z

SWAP_LOOP:
        LD      $10,($20)
        LD      $11,($22)
        ST      $11,($20)
        ST      $10,($22)

        ADW     $20,$SY
        ADW     $22,$SY
        SBW     $24,$SY
        JR      NZ,SWAP_LOOP
        RTN

; ----------------------------------------------------------------------
; BUBBLE_SORT
;
; Optimized Bubble sort:
;   - adjacent comparisons only
;   - swap iff left > right
;   - unsorted end shrinks by one record per pass
;   - terminate after a pass with no swaps
;
; Working registers retained across CMP_SORT:
;   $8/$9    m
;   $12/$13  current left record
;   $14/$15  current right record
;   $16/$17  comparisons remaining in this pass
;
; WS_BEND is the shrinking pass length; WS_BSWAP records whether the pass
; performed any swap.
; ----------------------------------------------------------------------
BUBBLE_SORT:
        PRE     IX,WS_NVAL
        LDW     $18,(IX+$SX)
        SBW     $18,$SY
        ANCW    $18,$18
        RTN     Z

        PRE     IX,WS_BEND
        STW     $18,(IX+$SX)

BUB_PASS:
        PRE     IX,WS_BSWAP
        ST      $31,(IX+$SX)

        PRE     IX,WS_MVAL
        LDW     $8,(IX+$SX)

        LDW     $12,DATA
        LDW     $14,$12
        ADW     $14,$8

        PRE     IX,WS_BEND
        LDW     $16,(IX+$SX)

BUB_INNER:
        ANCW    $16,$16
        JP      Z,BUB_ENDPASS

        LDW     $20,$12
        LDW     $22,$14
        LDW     $24,$8
        CAL     CMP_SORT

; Comparator returns +1 only for left > right.
        SBC     $0,1
        JR      NZ,BUB_NOSWAP

        PRE     IX,WS_BSWAP
        ST      $30,(IX+$SX)

        LDW     $20,$12
        LDW     $22,$14
        LDW     $24,$8
        CAL     SWAP_REC

BUB_NOSWAP:
        ADW     $12,$8
        ADW     $14,$8
        SBW     $16,$SY
        JP      BUB_INNER

BUB_ENDPASS:
; Early exit if the complete pass made no swaps.
        PRE     IX,WS_BSWAP
        LD      $0,(IX+$SX)
        AN      $0,$0
        RTN     Z

; The largest remaining record is now at the current end.
        PRE     IX,WS_BEND
        LDW     $18,(IX+$SX)
        SBW     $18,$SY
        STW     $18,(IX+$SX)
        ANCW    $18,$18
        RTN     Z
        JP      BUB_PASS

; ======================================================================
; SPRINT 05 - SHELL SORT
; ======================================================================
;
; Conventional gapped insertion with the required halving sequence:
;
;   gap = floor(n / 2)
;   gap = floor(gap / 2)
;   ...
;   gap = 1
;
; For each i in [gap,n), the current key stays untouched while the scan finds
; its final insertion position. If movement is required, the records between
; that position and i are shifted one gap at a time.
;
; Movement is performed one byte-column at a time:
;   - save one byte of A[i]
;   - shift that byte through the gapped predecessor records
;   - store the saved byte at the insertion position
;   - repeat for all m byte positions
;
; This is still whole-record gapped insertion, but it needs only one byte of
; temporary data and therefore imposes no arbitrary maximum key width.
;
; Persistent registers during a gap pass:
;   $6/$7    byte stride = gap * m
;   $8/$9    m
;   $12/$13  address of current A[i]
;   $14/$15  current record index i
;
; CMP_SORT does not modify those registers.
; ----------------------------------------------------------------------
SHELL_SORT:
; n < 2 needs no gap and no comparison.
; LDW $6 uses the $6/$7 word, while BIDW names the HIGH register of its
; shift pair. Therefore BIDW $7 is required to halve that same 16-bit word.
        PRE     IX,WS_NVAL
        LDW     $6,(IX+$SX)
        BIDW    $7
        ANCW    $6,$6
        RTN     Z

        PRE     IX,WS_SGAP
        STW     $6,(IX+$SX)

SH_GAP:
; Compute byte stride = gap * m with the accepted Chapter 01 multiply core.
; n*m <= DATA_BYTES guarantees the 16-bit low word is sufficient here.
        PRE     IX,WS_SGAP
        LDW     $15,(IX+$SX)
        SBW     $17,$17

        PRE     IX,WS_MVAL
        LDW     $5,(IX+$SX)
        SBW     $7,$7

        CAL     UMUL32

; Reload the stable per-pass registers after UMUL32.
        LDW     $6,$15

        PRE     IX,WS_MVAL
        LDW     $8,(IX+$SX)

        PRE     IX,WS_SGAP
        LDW     $14,(IX+$SX)

; A[gap] begins exactly one stride from DATA.
        LDW     $12,DATA
        ADW     $12,$6

SH_NEXT_I:
; Stop this gap pass when i >= n.
        PRE     IX,WS_NVAL
        LDW     $20,(IX+$SX)
        SBCW    $14,$20
        JP      NC,SH_NEXT_GAP

; Find the insertion position without altering the current key at A[i].
; $16/$17 = current candidate insertion address
; $28/$29 = current candidate insertion index
        LDW     $16,$12
        LDW     $28,$14

SH_SCAN:
; If pos < gap there is no predecessor in this gapped subsequence.
        PRE     IX,WS_SGAP
        LDW     $26,(IX+$SX)
        SBCW    $28,$26
        JR      C,SH_FOUND

; Compare A[pos-gap] with the untouched key A[i].
        LDW     $20,$16
        SBW     $20,$6
        LDW     $22,$12
        LDW     $24,$8
        CAL     CMP_SORT

; Continue scanning only while predecessor > key.
        SBC     $0,1
        JR      NZ,SH_FOUND

        PRE     IX,WS_SGAP
        LDW     $26,(IX+$SX)
        SBW     $28,$26
        SBW     $16,$6
        JP      SH_SCAN

SH_FOUND:
; If the insertion position is still i, no movement is required.
        SBCW    $16,$12
        JR      Z,SH_ADVANCE

; Rotate the gapped records one byte-column at a time.
; $20/$21 = byte address in original A[i]
; $22/$23 = matching byte address at insertion position
; $28/$29 = bytes remaining
        LDW     $20,$12
        LDW     $22,$16
        LDW     $28,$8

SH_BYTE:
; Save this byte of the original key before any predecessor overwrites it.
        LD      $10,($20)
        LDW     $24,$20

SH_SHIFT:
        SBCW    $24,$22
        JR      Z,SH_STORE

        LDW     $26,$24
        SBW     $26,$6
        LD      $11,($26)
        ST      $11,($24)
        LDW     $24,$26
        JR      SH_SHIFT

SH_STORE:
        ST      $10,($22)

        ADW     $20,$SY
        ADW     $22,$SY
        SBW     $28,$SY
        JR      NZ,SH_BYTE

SH_ADVANCE:
; Physical record i+1 is always m bytes after physical record i, regardless
; of how the previous key was inserted within its gapped subsequence.
        ADW     $14,$SY
        ADW     $12,$8
        JP      SH_NEXT_I

SH_NEXT_GAP:
; floor(gap/2). BIDW $7 shifts the word held by $6/$7.
; Using BIDW $6 would instead shift $5/$6 and leave the gap high byte stale.
        PRE     IX,WS_SGAP
        LDW     $6,(IX+$SX)
        BIDW    $7
        STW     $6,(IX+$SX)
        ANCW    $6,$6
        JP      NZ,SH_GAP
        RTN

; ----------------------------------------------------------------------
; VALID_SORT
;
; Verify adjacent A[i-1] <= A[i] using CMP_RAW so validation contributes
; nothing to KEY-CMP or INST-CMP.
;
; Return $0 = 0 valid, 1 failure.
; ----------------------------------------------------------------------
VALID_SORT:
        PRE     IX,WS_NVAL
        LDW     $16,(IX+$SX)
        SBW     $16,$SY
        JR      Z,VAL_OK

        PRE     IX,WS_MVAL
        LDW     $8,(IX+$SX)

        LDW     $12,DATA
        LDW     $14,$12
        ADW     $14,$8

VAL_LOOP:
        LDW     $20,$12
        LDW     $22,$14
        LDW     $24,$8
        CAL     CMP_RAW

; Only left > right invalidates ascending order.
        SBC     $0,1
        JR      Z,VAL_FAIL

        ADW     $12,$8
        ADW     $14,$8
        SBW     $16,$SY
        JP      NZ,VAL_LOOP

VAL_OK:
        LD      $0,0
        RTN

VAL_FAIL:
        LD      $0,1
        RTN

; ----------------------------------------------------------------------
; Fixed-width decimal formatting for report counters.
;
; Counters are stored as little-endian uint32. FMT_COUNT writes exactly ten
; decimal digits to WS_DEC10, zero padded. This keeps the report deterministic
; and avoids a second variable-length numeric UI path.
; ----------------------------------------------------------------------
FMT_COUNT:
        LDM     $20,(IX+$SX),4
        PRE     IX,WS_DEC10

        LDW     $24,&HCA00
        LDW     $26,&H3B9A
        CAL     FMT_DIG

        LDW     $24,&HE100
        LDW     $26,&H05F5
        CAL     FMT_DIG

        LDW     $24,&H9680
        LDW     $26,&H0098
        CAL     FMT_DIG

        LDW     $24,&H4240
        LDW     $26,&H000F
        CAL     FMT_DIG

        LDW     $24,&H86A0
        LDW     $26,&H0001
        CAL     FMT_DIG

        LDW     $24,&H2710
        LDW     $26,&H0000
        CAL     FMT_DIG

        LDW     $24,&H03E8
        LDW     $26,&H0000
        CAL     FMT_DIG

        LDW     $24,&H0064
        LDW     $26,&H0000
        CAL     FMT_DIG

        LDW     $24,&H000A
        LDW     $26,&H0000
        CAL     FMT_DIG

        LDW     $24,&H0001
        LDW     $26,&H0000
        CAL     FMT_DIG
        RTN

; One decimal digit by repeated subtraction. Because FMT_COUNT proceeds from
; 10^9 downward, each digit requires at most nine subtractions.
FMT_DIG:
        LD      $28,0

FD_LOOP:
; Compare uint32 remainder ($22:$20) with divisor ($26:$24).
        SBCW    $22,$26
        JR      C,FD_OUT
        JR      NZ,FD_SUB

        SBCW    $20,$24
        JR      C,FD_OUT

FD_SUB:
        SBW     $20,$24
        JR      NC,FD_HI
        SBW     $22,$SY
FD_HI:
        SBW     $22,$26
        AD      $28,$SY
        JR      FD_LOOP

FD_OUT:
        LD      $16,$28
        AD      $16,&H30
        STI     $16,(IX+$SX)
        RTN

; ----------------------------------------------------------------------
; Bubble report.
;
; Four physical rows contain every successful-run field required by WHAT:
;
;   BUBBLE N=<n> M=<m>
;   INPUT=<pattern> ORDER=PASS
;   KEY-CMP=<10-digit count>
;   INST-CMP=<10-digit count> EXE
; ----------------------------------------------------------------------
SHOW_BREPORT:
        CAL     CLEAR_ALL

        LD      $6,$SX
        PRE     IZ,TXT_BREP0
        CAL     PRINTZ
        PRE     IX,WS_NLEN
        LD      $13,(IX+$SX)
        PRE     IX,WS_NBUF
        CAL     PRINT_RAM
        PRE     IZ,TXT_MSEP
        CAL     PRINTZ
        PRE     IX,WS_MLEN
        LD      $13,(IX+$SX)
        PRE     IX,WS_MBUF
        CAL     PRINT_RAM

        LD      $6,&H20
        PRE     IZ,TXT_IREP
        CAL     PRINTZ
        CAL     PRINT_PAT
        PRE     IZ,TXT_ORDERPASS
        CAL     PRINTZ

        LD      $6,&H40
        PRE     IZ,TXT_KEYCMP
        CAL     PRINTZ
        PRE     IX,WS_KCMP
        CAL     FMT_COUNT
        PRE     IX,WS_DEC10
        LD      $13,10
        CAL     PRINT_RAM

        LD      $6,&H60
        PRE     IZ,TXT_INSTCMP
        CAL     PRINTZ
        PRE     IX,WS_ICMP
        CAL     FMT_COUNT
        PRE     IX,WS_DEC10
        LD      $13,10
        CAL     PRINT_RAM
        PRE     IZ,TXT_REXE
        CAL     PRINTZ

        CAL     WAIT_EXE
        RTN

SHOW_BFAIL:
        CAL     CLEAR_ALL

        LD      $6,$SX
        PRE     IZ,TXT_BUBBLE
        CAL     PRINTZ

        LD      $6,&H20
        PRE     IZ,TXT_ORDERFAIL
        CAL     PRINTZ

        LD      $6,&H60
        PRE     IZ,TXT_PH3
        CAL     PRINTZ

        CAL     WAIT_EXE
        JP      SHOW_MENU

; ----------------------------------------------------------------------
; Shell report. Deliberately mirrors the accepted Bubble report so the
; experimental display changes only the algorithm name and measured values.
; ----------------------------------------------------------------------
SHOW_SREPORT:
        CAL     CLEAR_ALL

        LD      $6,$SX
        PRE     IZ,TXT_SREP0
        CAL     PRINTZ
        PRE     IX,WS_NLEN
        LD      $13,(IX+$SX)
        PRE     IX,WS_NBUF
        CAL     PRINT_RAM
        PRE     IZ,TXT_MSEP
        CAL     PRINTZ
        PRE     IX,WS_MLEN
        LD      $13,(IX+$SX)
        PRE     IX,WS_MBUF
        CAL     PRINT_RAM

        LD      $6,&H20
        PRE     IZ,TXT_IREP
        CAL     PRINTZ
        CAL     PRINT_PAT
        PRE     IZ,TXT_ORDERPASS
        CAL     PRINTZ

        LD      $6,&H40
        PRE     IZ,TXT_KEYCMP
        CAL     PRINTZ
        PRE     IX,WS_KCMP
        CAL     FMT_COUNT
        PRE     IX,WS_DEC10
        LD      $13,10
        CAL     PRINT_RAM

        LD      $6,&H60
        PRE     IZ,TXT_INSTCMP
        CAL     PRINTZ
        PRE     IX,WS_ICMP
        CAL     FMT_COUNT
        PRE     IX,WS_DEC10
        LD      $13,10
        CAL     PRINT_RAM
        PRE     IZ,TXT_REXE
        CAL     PRINTZ

        CAL     WAIT_EXE
        RTN

SHOW_SFAIL:
        CAL     CLEAR_ALL

        LD      $6,$SX
        PRE     IZ,TXT_SHELL
        CAL     PRINTZ

        LD      $6,&H20
        PRE     IZ,TXT_ORDERFAIL
        CAL     PRINTZ

        LD      $6,&H60
        PRE     IZ,TXT_PH3
        CAL     PRINTZ

        CAL     WAIT_EXE
        JP      SHOW_MENU

; ----------------------------------------------------------------------
; Cycle deterministic input pattern.
; ----------------------------------------------------------------------
CYCLE_PAT:
        PRE     IX,WS_PAT
        LD      $9,(IX+$SX)
        AD      $9,$SY
        SBC     $9,3
        JR      C,CP_STORE
        LD      $9,0
CP_STORE:
        ST      $9,(IX+$SX)
        RTN

; ----------------------------------------------------------------------
; Default setup state for Sprint 01:
;   n = 16
;   m = 1
;   input = ORDERED
; ----------------------------------------------------------------------
INIT_SETUP:
        PRE     IX,WS_NBUF
        LD      $0,"1"
        ST      $0,(IX+0)
        LD      $0,"6"
        ST      $0,(IX+1)

        PRE     IX,WS_NLEN
        LD      $0,2
        ST      $0,(IX+$SX)

        PRE     IX,WS_MBUF
        LD      $0,"1"
        ST      $0,(IX+0)

        PRE     IX,WS_MLEN
        LD      $0,1
        ST      $0,(IX+$SX)

        PRE     IX,WS_PAT
        LD      $0,PAT_ORD
        ST      $0,(IX+$SX)

        PRE     IX,WS_CUR
        LD      $0,1
        ST      $0,(IX+$SX)

        PRE     IX,WS_TICK
        LD      $0,0
        ST      $0,(IX+$SX)
        PRE     IX,WS_EDIT
        ST      $0,(IX+$SX)
        PRE     IX,WS_EPOS
        ST      $0,(IX+$SX)
        PRE     IX,WS_NEW
        ST      $0,(IX+$SX)

        SBW     $0,$0
        SBW     $2,$2
        PRE     IX,WS_NVAL
        STM     $0,(IX+$SX),4
        PRE     IX,WS_MVAL
        STM     $0,(IX+$SX),4

        PRE     IX,WS_ERR
        ST      $0,(IX+$SX)
        RTN

; ----------------------------------------------------------------------
; Draw current configuration on rows 1-3.
; ----------------------------------------------------------------------
DRAW_CFG:
; Row 1: n
        LD      $6,$SX
        PRE     IZ,TXT_N
        CAL     PRINTZ

        PRE     IX,WS_NLEN
        LD      $13,(IX+$SX)
        PRE     IX,WS_NBUF
        CAL     PRINT_RAM

; Row 2: m
        LD      $6,&H20
        PRE     IZ,TXT_M
        CAL     PRINTZ

        PRE     IX,WS_MLEN
        LD      $13,(IX+$SX)
        PRE     IX,WS_MBUF
        CAL     PRINT_RAM

; Row 3: input pattern
        LD      $6,&H40
        PRE     IZ,TXT_INPUT
        CAL     PRINTZ
        CAL     PRINT_PAT
        RTN

DRAW_SETUP:
        CAL     CLEAR_ALL
        CAL     DRAW_CFG
        LD      $6,&H60
        PRE     IZ,TXT_SETKEYS
        CAL     PRINTZ
        RTN

; ----------------------------------------------------------------------
; Editor cursor.
;
; Default Chapter 02 UI policy: unless a screen explicitly requires another
; style, editable fields use the small blinking '_' cursor convention already
; established by accepted PB-2000C projects.
;
; The cursor begins at position 0 of the active field. While it blinks over an
; existing digit, the OFF phase restores that digit rather than drawing space.
; ----------------------------------------------------------------------
; The prior chapter-local editor cursor code was retired in favor of
; READN and remains byte-preserved in the r0.16 Experience archive.

; Input:
;   IX = buffer base
;   $13 = number of bytes
;   $6 = current display position
PRINT_RAM:
        AN      $13,$13
        RTN     Z
        LD      $12,0
PR_LOOP:
        SBC     $12,$13
        RTN     NC
        LD      $16,(IX+$12)
        CAL     PUTC
        AD      $12,$SY
        JR      PR_LOOP

PRINT_PAT:
        PRE     IX,WS_PAT
        LD      $9,(IX+$SX)

        AN      $9,$9
        JR      Z,PP_ORD

        SBC     $9,PAT_REV
        JR      Z,PP_REV

        PRE     IZ,TXT_SAME
        JP      PRINTZ

PP_ORD:
        PRE     IZ,TXT_ORDERED
        JP      PRINTZ

PP_REV:
        PRE     IZ,TXT_REVERSE
        JP      PRINTZ

; ----------------------------------------------------------------------
; Placeholder page remainder for algorithms not yet implemented.
; ----------------------------------------------------------------------
SHOW_PH_REST:
        LD      $6,&H20
        PRE     IZ,TXT_PH1
        CAL     PRINTZ

        LD      $6,&H40
        PRE     IZ,TXT_PH2
        CAL     PRINTZ

        LD      $6,&H60
        PRE     IZ,TXT_PH3
        CAL     PRINTZ

        CAL     WAIT_EXE
        RTN

; ======================================================================
; DISPLAY / KEYBOARD SUPPORT
; ======================================================================

SHOW_SPLASH:
        LD      $6,$SX
        PRE     IZ,TXT_SPL0
        CAL     PRINTZ

        LD      $6,&H20
        PRE     IZ,TXT_SPL1
        CAL     PRINTZ

        LD      $6,&H40
        PRE     IZ,TXT_SPL2
        CAL     PRINTZ

        LD      $6,&H60
        PRE     IZ,TXT_SPL3
        CAL     PRINTZ
        RTN

; Based on Chapter 01 r002 (Tiny TM-sampling precedent): wait for three
; stabilized TM transitions, rather than a CPU-speed-dependent delay loop.
; OFFCHK must be invoked through the RAM-safe firmware bridge. No EXE.
WAIT_SPLASH:
        CAL     SPLASH_TM
        LD      $22,$8
        LD      $23,0
WSP_LOOP:
        CAL     FW_OFFCHK
        CAL     SPLASH_TM
        SBC     $8,$22
        JR      Z,WSP_LOOP
        LD      $22,$8
        AD      $23,$SY
        SBC     $23,3
        JR      NZ,WSP_LOOP
        RTN

; Read TM twice until consecutive samples agree (no transition tear).
SPLASH_TM:
        GST     TM,$8
STM_AGAIN:
        LD      $9,$8
        GST     TM,$8
        SBC     $8,$9
        JR      NZ,STM_AGAIN
        LD      $9,0
        RTN

; Wait for a new EXE event.
; Non-EXE keys are ignored but must be released before polling resumes.
WAIT_EXE:
WE_WAIT:
        CAL     INKEY
        AN      $17,$17
        JR      Z,WE_WAIT

        SBC     $0,&H0D
        JR      Z,WE_EXE

WE_IGNORE:
        CAL     INKEY
        AN      $17,$17
        JR      NZ,WE_IGNORE
        JR      WE_WAIT

WE_EXE:
        CAL     INKEY
        AN      $17,$17
        JR      NZ,WE_EXE
        RTN

WAIT_RELEASE:
WR_LOOP:
        CAL     INKEY
        AN      $17,$17
        JR      NZ,WR_LOOP
        RTN

FW_OFFCHK:
; Hardware-proven Life-style firmware bridge:
; UA=&H54 keeps RAM data/index context while PC enters bank 0 firmware.
; Because this routine exists at the same address in ROM and RAM, execution
; crosses banks at the PST instructions without relocation.
        PST     UA,&H54
        CAL     OFFCHK
        PST     UA,&H55
        RTN

CLEAR_ALL:
        LD      $6,$SX
        LD      $8,128
        PST     UA,&H54
        CAL     &H018D
        PST     UA,&H55
        RTN

; ----------------------------------------------------------------------
; Firmware character output from RAM-resident application code.
; ----------------------------------------------------------------------
PUTC:
        LD      $7,$16
        LD      $0,$7
        DIU     $7
        DID     $0
        OR      $7,$SZ
        PST     UA,&H54
        CAL     &H0197
        PST     UA,&H55
        RTN

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

; ----------------------------------------------------------------------
; Static sorting-status feedback.
;
; Deliberately outside the measured sort boundary:
;   regenerate -> status screen -> CLEAR_CNTS -> algorithm
;
; No animation, progress polling, timing, DATA access, or counter access occurs
; here. The purpose is only to make a long-running sort visibly alive.
; ----------------------------------------------------------------------
SHOW_BSORT:
        CAL     CLEAR_ALL
        LD      $6,$SX
        PRE     IZ,TXT_BUBBLE
        CAL     PRINTZ
        JP      SHOW_SORTMSG

SHOW_SSORT:
        CAL     CLEAR_ALL
        LD      $6,$SX
        PRE     IZ,TXT_SHELL
        CAL     PRINTZ

SHOW_SORTMSG:
        LD      $6,&H20
        PRE     IZ,TXT_SORTING
        CAL     PRINTZ
        RTN

; ----------------------------------------------------------------------
; ROM strings.
; ----------------------------------------------------------------------
TXT_SPL0:
        DB      "         Xandrian Labs",0
TXT_SPL1:
        DB      "       CS617 - Chapter 02",0
TXT_SPL2:
        DB      "       Sorting Algorithms",0
TXT_SPL3:
        DB      "             r0.19",0

TXT_FKEYS:
        DB      "[setup ][bubble][shell ][view  ]",0
TXT_SETKEYS:
        DB      "[n     ][m     ][input ][done  ]",0

TXT_N:
        DB      "n: ",0
TXT_M:
        DB      "m: ",0
TXT_INPUT:
        DB      "Input: ",0

TXT_ORDERED:
        DB      "ORDERED",0
TXT_REVERSE:
        DB      "REVERSE",0
TXT_SAME:
        DB      "SAME",0

TXT_BADSET:
        DB      "Invalid setup",0
TXT_EN:
        DB      "n must be 1..8192",0
TXT_EM:
        DB      "m must be 1..8192",0
TXT_ECAP:
        DB      "n*m must be <=8192",0
TXT_EUNQ:
        DB      "m=1 requires n<=255",0
TXT_EXERET:
        DB      "Press EXE",0

TXT_BUBBLE:
        DB      "Bubble",0
TXT_SHELL:
        DB      "Shell",0
TXT_SORTING:
        DB      "Sorting...",0

TXT_BREP0:
        DB      "Bubble n=",0
TXT_SREP0:
        DB      "Shell n=",0
TXT_MSEP:
        DB      " m=",0
TXT_IREP:
        DB      "Input=",0
TXT_ORDERPASS:
        DB      " ORDER=PASS",0
TXT_ORDERFAIL:
        DB      "ORDER=FAIL",0
TXT_KEYCMP:
        DB      "KEY-CMP=",0
TXT_INSTCMP:
        DB      "INST-CMP=",0
TXT_REXE:
        DB      " EXE",0

TXT_PH1:
        DB      "DATA GENERATED",0
TXT_PH2:
        DB      "SORT NOT READY",0
TXT_PH3:
        DB      "Press EXE",0

; ======================================================================
; ACCEPTED CS617 CHAPTER 01 UNSIGNED ARITHMETIC CORES
;
; UADD32 and UMUL32 are accepted ROM-independent historical transplants from
; the Chapter 01 numeric library. UDECAPP is the accepted Chapter 01 decimal
; composition built on them. They are embedded here to keep the current build
; self-contained; no new arithmetic behavior is claimed in Sprint 03.
; ======================================================================

UADD32:
        ADW     $15,$5
        JR      NC,UADD_HI
        ADW     $17,$SY
UADD_HI:
        ADW     $17,$7
        RTN

UMUL32:
        LDM     $0,$15,4
        SBW     $15,$15
        SBW     $17,$17

M32_LOOP:
        SBW     $10,$10
        SBCW    $5,$10
        JR      NZ,M32_BIT
        SBCW    $7,$10
        RTN     Z

M32_BIT:
        ANC     $5,$SY
        JR      Z,M32_SHIFT
        ADW     $15,$SZ
        JR      NC,M32_ADDHI
        ADW     $17,$SY

M32_ADDHI:
        ADW     $17,$2

M32_SHIFT:
        BIUW    $0
        ROUW    $2
        BIDW    $8
        RODW    $6
        JR      M32_LOOP

UDECAPP:
        LD      $4,$5

; floor(UINT32_MAX/10) = &H19999999
        LDW     $5,&H9999
        LDW     $7,&H1999

        SBCW    $17,$7
        JR      C,DAPP_SAFE
        JR      NZ,DAPP_OVF

        SBCW    $15,$5
        JR      C,DAPP_SAFE
        JR      NZ,DAPP_OVF

        SBC     $4,6
        JR      C,DAPP_SAFE
        JR      DAPP_OVF

DAPP_SAFE:
        LDW     $5,10
        SBW     $7,$7
        CAL     UMUL32

        LD      $5,$4
        LD      $6,0
        SBW     $7,$7
        CAL     UADD32

        LD      $4,0
        RTN

DAPP_OVF:
        LD      $4,1
        RTN

; ======================================================================
; RAM-RESIDENT APPLICATION OBJECTS
;
; These labels are part of the mirrored image. In ROM they provide the source
; bytes for the loader; after mirroring the same addresses are writable bank-1
; RAM objects.
; ======================================================================

RN_BUF:
        DS      5
RN_BASE:
        DS      2
RN_LEN:
        DS      2
RN_CAP:
        DS      2
RN_POS:
        DS      2
RN_DBASE:
        DS      1
RN_REASON:
        DS      1

WS_NBUF:
        DS      5
WS_MBUF:
        DS      5
WS_NLEN:
        DS      1
WS_MLEN:
        DS      1
WS_PAT:
        DS      1
WS_CUR:
        DS      1
WS_TICK:
        DS      1
WS_EDIT:
        DS      1
WS_EPOS:
        DS      1
WS_NEW:
        DS      1
WS_NVAL:
        DS      4
WS_MVAL:
        DS      4
WS_ERR:
        DS      1

; Sprint 04/05 measurement and sort state.
WS_KCMP:
        DS      4
WS_ICMP:
        DS      4
WS_BEND:
        DS      2
WS_BSWAP:
        DS      1
WS_SGAP:
        DS      2
WS_DEC10:
        DS      10

; VIEW state and one complete 32-column render buffer.
WS_VTOP:
        DS      2
WS_VPTR:
        DS      2
WS_VLINE:
        DS      32

; ----------------------------------------------------------------------
; Chapter 02 sort-data storage.
;
; This is the real in-place array, not a ROM-only numerical alias.
; Sprint 05 r0.14 preserves exactly n*m active bytes at the start of DATA.
; Bytes beyond the active region are intentionally unspecified.
; ----------------------------------------------------------------------
DATA:
        DS      DATA_BYTES

DATA_END:

; End of complete same-address mirror image.
COPY_END:

; ======================================================================
; ROM-ONLY MIRROR LOADER
;
; Proven Life architecture:
;   UA=&H44 : IX/IY source in ROM bank 0, IZ destination in bank-1 RAM,
;             PC remains in ROM while BUP executes.
;   UA=&H55 : RAM/Pascal-like execution state.
; ======================================================================
LOADER:
        PST     UA,&H44
        PRE     IX,COPY_START
        PRE     IY,COPY_END-1
        PRE     IZ,COPY_START
        BUP

        PST     UA,&H55
        JP      RAM_ENTRY

        ORG     &HFFFF
        DB      0
