; ======================================================================
; CS617 reusable linear text-buffer editing primitives
; File: textbuf_edit.inc
;
; STATUS: r0.05 candidate. Must be Rev. 0.45 assembled and target-validated
; through a consuming application before promotion beyond Chapter 06.
;
; PROVENANCE / PRINCIPLED BORROW
;   Architectural donor: Casio OM-52C C-ROM text editor (Piotr disassembly).
;     44FE  full C text-editor entry
;     45C0  insert bytes into edited file
;     461E  delete bytes from edited file
;     48FC  cursor right
;     49BE  cursor left
;     4A39  DEL
;     4A58  backspace
;
;   The C-ROM routines are NOT copied byte-for-byte here. They are coupled to
;   the C file manager, free-space workspace, file expansion, EDTOP display
;   buffers, menus/search, and other C runtime state. This include preserves
;   the useful editor contracts while adapting them to a caller-owned fixed
;   linear byte buffer.
;
;   Prior CS617 Chapter 02 field-editing experience also established the value
;   of explicit cursor state and safe no-op behavior at editing boundaries.
;
; CALLER INVARIANTS
;   SY addresses a word value 0001h (standard CS617 convention).
;   IX accesses writable RAM in the caller's current UA mapping.
;   0 <= cursor <= length <= capacity.
;
; REGISTER CONTRACT FOR BUFFER OPERATIONS
;   $0/$1   buffer base address
;   $2/$3   current length (updated on successful edit)
;   $4/$5   capacity
;   $6/$7   cursor offset  (updated by insert/backspace/move)
;   $16     byte to insert (TB_INS1 only)
;
; RETURN STATUS IN $8
;   0 = success
;   1 = full (insert refused, buffer unchanged)
;   2 = edge/invalid operation (buffer unchanged)
;
; Clobbers $8-$15 and IX unless otherwise noted.
; All labels are <=16 characters for HD61 ASSEMBLER Rev. 0.45.
; ======================================================================

TB_OK:   EQU     0
TB_FULL: EQU     1
TB_EDGE: EQU     2

; Insert $16 at cursor. Shift suffix right by one.
TB_INS1:
; Refuse at capacity before considering the append-at-end fast path.
        LDW     $10,$2
        SBCW    $10,$4
        JR      C,TBI_CUR
        LD      $8,TB_FULL
        RTN

TBI_CUR:
; Reject invalid cursor > length; cursor==length is ordinary append.
        LDW     $10,$6
        SBCW    $10,$2
        JR      Z,TBI_APPEND
        JR      C,TBI_SHIFT
        LD      $8,TB_EDGE
        RTN

TBI_SHIFT:
; $10/$11 = destination = base + length
; $12/$13 = source      = destination - 1
; $14/$15 = insertion address = base + cursor
        LDW     $10,$0
        ADW     $10,$2
        LDW     $12,$10
        SBW     $12,$SY
        LDW     $14,$0
        ADW     $14,$6

TBI_LOOP:
        PRE     IX,$12
        LD      $9,(IX+$SX)
        PRE     IX,$10
        ST      $9,(IX+$SX)

        LDW     $8,$12
        SBCW    $8,$14
        JR      Z,TBI_PUT

        SBW     $12,$SY
        SBW     $10,$SY
        JR      TBI_LOOP

TBI_APPEND:
        LDW     $14,$0
        ADW     $14,$2

TBI_PUT:
        PRE     IX,$14
        ST      $16,(IX+$SX)
        ADW     $2,$SY
        ADW     $6,$SY
        LD      $8,TB_OK
        RTN

; Delete byte at cursor. Cursor remains at the same logical offset.
TB_DEL1:
; Need cursor < length.
        LDW     $10,$6
        SBCW    $10,$2
        JR      C,TBD_HAVE
        LD      $8,TB_EDGE
        RTN

TBD_HAVE:
; count = length - cursor - 1
        LDW     $10,$2
        SBW     $10,$6
        SBW     $10,$SY

; destination = base + cursor
; source      = destination + 1
        LDW     $12,$0
        ADW     $12,$6
        LDW     $14,$12
        ADW     $14,$SY

TBD_LOOP:
        ANCW    $10,$10
        JR      Z,TBD_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      TBD_LOOP

TBD_DONE:
        SBW     $2,$SY
        LD      $8,TB_OK
        RTN

; Backspace: move cursor left once, then delete there.
TB_BS1:
        ANCW    $6,$6
        JR      NZ,TBB_GO
        LD      $8,TB_EDGE
        RTN
TBB_GO:
        SBW     $6,$SY
        JP      TB_DEL1

; Move cursor left one byte, bounded at zero.
TB_LEFT:
        ANCW    $6,$6
        JR      NZ,TBL_GO
        LD      $8,TB_EDGE
        RTN
TBL_GO:
        SBW     $6,$SY
        LD      $8,TB_OK
        RTN

; Move cursor right one byte, bounded at length.
TB_RIGHT:
        LDW     $10,$6
        SBCW    $10,$2
        JR      C,TBR_GO
        LD      $8,TB_EDGE
        RTN
TBR_GO:
        ADW     $6,$SY
        LD      $8,TB_OK
        RTN

; Normalize a linear viewport so cursor is visible.
; Input:
;   $6/$7   cursor
;   $10/$11 current viewport start
;   $12/$13 viewport span (>0)
; Output:
;   $10/$11 normalized viewport start
; Clobbers $8/$9,$14/$15.
TB_NORMV:
; cursor before viewport -> viewport = cursor
        LDW     $14,$6
        SBCW    $14,$10
        JR      C,TBN_BEFORE

; cursor < viewport+span -> already visible
        LDW     $14,$10
        ADW     $14,$12
        LDW     $8,$6
        SBCW    $8,$14
        RTN     C

; Cursor is at/after first invisible position.
; viewport = cursor - span + 1
        LDW     $10,$6
        SBW     $10,$12
        ADW     $10,$SY
        RTN

TBN_BEFORE:
        LDW     $10,$6
        RTN
