 
*  EE1/BFMGR                  Apr / 13 / 95
*--------------------------------------------------
*  1x128 2x64 4x32 8x16 Kb V/ERAM Edit/Ring version
 
*------------------------------------------------
*  Null blank line records, pointer >0000
*  128 Kb virtual buffer addressed as words
*  All text records word aligned
*  First byte is normal inclusive length byte
*  Records padded out to word boundary
*  0-64 Kb in VRAM, >0001 - >7FFF line pointers
*  64-128 Kb in ERAM, >8000 - >FFFF in line table
 
*  BLWP Vector Table
*--------------------------------------------
*  Standard buffer manager services to editor
 
INSTLN DATA BUFMWS,INSRT0     Insert a line
UPDTLN DATA BUFMWS,UPDATL     Update a line
GETLN  DATA BUFMWS,GTTLIN     Fetch and unsquish a line
DELTLN DATA BUFMWS,DELETL     Delete a single line
DLBLOC DATA BUFMWS,DBLC00     Delete contiguous block of lines
CPBLOC DATA BUFMWS,CPBL00     Copy block of lines to end of buffer
 
*  Text Buffer segment manager BLWP vector table
*-----------------------------------------------------------
*  Text buffer segments mapped as 128Kb virtual linear space
*  Maximum 32 segments allowed
*  >0000 pointer reserved for blank lines
*  >0001 - >FFFF word addressing into buffer
*  Segment entries are word counts
*  Mask by word count -- >0FFF for 8 Kb segment
*  MAKSEG sets up buffer structure in Entry code
 
INISEG DATA SEGWSP,INIS00     Initialize text buffer
FNDSEG DATA SEGWSP,FNDS00     Find segment containing line pointer
GETSEG DATA SEGWSP,GETS00     Get first segment with enough room
TMPSEG DATA SEGWSP,TMPS00     Set up temporary table for CPBLOC
CPYSEG DATA SEGWSP,CPYS00     Search copy temp segments for enough room
UPDSEG DATA SEGWSP,UPDS00     Update segment table for successful CPBLOC
FRESEG DATA SEGWSP,FRES00     Return occupancy info for SD
RNGSEG DATA SEGWSP,RNGS00     Switch ring file segment limits
 
*  VDP ERAM handlers
 
BLKRDA DATA EVRWSP,BLKR00     VRDA from R8
BLKWDA DATA EVRWSP,BLKW00     VWDA from R9
VRAM   DATA EVRWSP,VRAM00     Restore VRAM
 
*  Buffer segment size data and equates
 
TXBITS EQU  11                # of 1 bits in segment size mask (words)
 
*  Segment manager workspace -- preloaded
*----------------------------------------
*  R5-R8 general free registers
*  R0-R3 must be retained as configuration data
 
SEGWSP EQU  $
       DATA TXBITS            R0  - # 1 bits in segment word size mask
       DATA >0                R1  - current start segment offset
       DATA >0                R2  - current end segment offset
EVRAMF DATA >0                R3  - flag for ERAM not present
       DATA >0                R4  - current line length from caller
       DATA >0                R5  - segment index - max in R12
       DATA >0                R6  - max segment size available
       DATA >0                R7  - current segment check index
       DATA >0                R8  - table head pointer
TXSF   DATA >0                R9  - current segment top limit
TXSM   DATA >0                R10 - segment size/mask
TXSN   DATA >0                R11 - free use and for BL
       DATA >0                R12 - max segment index
       DATA >0,>0,>0          For BLWP
 
*  Limit table for ring boundaries
*--------------------------------------------------
*  LIMTAB initialized for 8-file ring with 2 x SEG#
*  Adjusted by MAKSEG
 
LIMTAB DATA >00,>08,>10,>18   Values for R2 in 8-file ring
       DATA >20,>28,>30,>38   Entry code matvches to segment size
       DATA >40               Final comparison value
LNTSAV DATA LTABM1            End of line table
LNTLEN DATA RNFEND            Length to save - to be adjusted
FRETAB DATA 655,327           Big/Small
       DATA 327,163,82        Dual/Quad/Octet  size/100
 
*  VDP Bank 3 Storage address tables - to be adjusted for mode
 
LTSTOR DATA >00A0,>08A0,>10A0,>18A0
       DATA >20A0,>28A0,>30A0,>38A0
 
*  Segment content table
*-----------------------
*  Allow for 32 entries each
*  Ring files use various subsets
 
TXSEGS DATA >0001,>0800,>1000,>1800     Segment max occupancy
       DATA >2000,>2800,>3000,>3800     Stored in VRAM
       DATA >4000,>4800,>5000,>5800
       DATA >6000,>6800,>7000,>7800
       DATA >8000,>8800,>9000,>9800     Stored in EVRAM
       DATA >A000,>A800,>B000,>B800
       DATA >C000,>C800,>D000,>D800
       DATA >E000,>E800,>F000,>F800
 
*  Purge all buffer segments and reset line table
*------------------------------------------------
*  Adjust for word addressing
*  Overlap with temp segment table rewrite
*  Buffer parameters set in MakSeg in entry code
 
INIS00 LI   R7,TBL            Start of line # table
       MOV  R7,@TBLND         Load R1 of BFMWS
       CLR  @EOF              Reset EoF line count
       MOV  R2,R7             Copy start of next segment
       S    R1,R7             Length of block to clear
       LI   R11,TXSEGS        Point at segment table area
       A    R1,R11            Offset to current block
INIS20 SZC  R10,*R11+         Kill the added bits
       DECT R7                Count off
       JGT  INIS20            Not done
       MOV  R1,R1             Includes segment #0 ?
       JNE  TMPS00            No
       INC  @TXSEGS           Adjust first as null pointer reserved
 
*  Set temp segment table for trial block-copy
*---------------------------------------------
*  Might as well copy the lot
 
TMPS00 EQU  $
       LI   R6,TXSEGS         Segment occupied table
       LI   R7,TXSEGT         Projected occupied table
TMPS10 MOV  R12,R11           Copy segment count
TMPS20 MOV  *R6+,*R7+         Write over this temp table
       DECT R11               Done ?
       JGT  TMPS20            No
       RTWP
 
*  Copy updated temp segment table back to regular table
*-------------------------------------------------------
*  Done after successful block copy
 
UPDS00 EQU  $
       LI   R6,TXSEGT         Updated temporary table pointer
       LI   R7,TXSEGS         Standard segment table
       JMP  TMPS10            Use existing code
 
*  FiND SEGment containing specified line pointer
*--------------------------------------------------
*  Assumes null pointer check already done
*  Line pointer is word address in caller's R8
*  Returns pointer to current segment content in caller's R2
*  Leaves current segment top at TXSF (R9)
 
FNDS00 EQU  $
       LI   R8,TXSEGS         Regular segment table head
       MOV  @R8+R8(R13),R9    Text buffer pointer from caller's R8
       MOV  R9,R11            Save at TXSN for MOVMEM
       SOC  R10,R9            Mask up to segment limit in TXSF
       MOV  R9,R7             Copy
       SRL  R7,0              Isolate segment #
       SLA  R7,1              Make word index as for table search
       JMP  GETS30            Write to caller's R2 and exit
 
*  GET SEGment pointers in regular segment table
*  CoPY SEGment pointers in temporary segment table
*--------------------------------------------------------
*  Call with line length (in words) to be allocated in R9
*  Immediate return if none available -> memful  error
*  Success - step over error exit
*  Set caller R2 to point to end of segment value
 
CPYS00 EQU  $
       LI   R8,TXSEGT         Temp table head
       JMP  GETS05            Use find seg code
 
GETS00 EQU  $
       LI   R8,TXSEGS         Regular segment table head
GETS05 MOV  R1,R7             Initialize index to block start
       MOV  @R6+R6(R13),R11   Fetch line length
GETS10 C    R7,R2             All checked ?
       JHE  GETS99            No room in the inn
       MOV  R8,R6             Copy table base
       A    R7,R6             Index into current fill table
       MOV  *R6,R5            Fetch the entry
       MOV  R5,R9             Copy for use
       SOC  R10,R9            Mask up to max segment limit
       A    R11,R5            Construct anticipated end
       JOC  GETS99            Catch wrap from last segment
       C    R5,R9             Check against limit for segment
       JL   GETS20            Adequate
       INCT R7                Next segment
       JMP  GETS10            Loop on it
 
GETS20 INCT R14               Step over error return
GETS30 A    R8,R7             Construct absolute pointer
GETS40 MOV  R7,@R2+R2(R13)    Segment pointer to caller's R2
GETS99 RTWP
 
*  Find total FREe SEGment size
*--------------------------------------------------------
*  Returns % estimate in caller's R9, actual words in R10
 
FRES00 EQU  $
       MOV  R1,R7             Initialize segment data pointer
       CLR  R9                Use to accumulate freesize
FRES10 C    R7,R2             Done ?
       JHE  FRES20            Yes
       MOV  @TXSEGS(R7),R11   Fetch top pointer for this segment
       MOV  R11,R8            Copy for use
       SOC  R10,R8            Mask to top of segment
       S    R11,R8            Figure words left
       A    R8,R9             Accumulate total
       INCT R7                Next segment
       JMP  FRES10            Go do it
 
FRES20 MOV  R9,R7             For division
       MOV  R3,R11            Copy mode ID
       SRA  R11,8             Convert to word index  -1,0,1,2,3
       INC  R11               0,1,2,3,4 <-> B/S/2/4/8
       SLA  R11,1             Offset by words   4,6,8
       MOV  @FRETAB(R11),R11  Fetch buffer 1% size
       CLR  R6                Clear MSW
       DIV  R11,R6            Now calculate % for 128 Kb buffer
FRES30 MOV  R6,@R9+R9(R13)    Copy to callers R9
       RTWP
 
*  RiNGSEGment limit switch
*-----------------------------------
*  R1,R2 preloaded for single buffer
 
RNGS00 EQU  $
       MOV  @R13+R13(R13),R11 Check caller R13
       SLA  R11,1             Make word offset
       MOV  @LIMTAB(R11),R1   Lower limit
       MOV  @LIMTAB+2(R11),R2 Upper limit
       RTWP
 
*-------------------------------*
*  Routines for Buffer Manager  *
*-------------------------------*
 
H8E    BYTE >8E               Bank register 14
HAD    BYTE >AD               V/E register  45
       EVEN
 
*  EVRAM/VRAM utilities
*--------------------------------------------
*  BLKRDA sets up V/ERAM block reads from *R8
*  BLKWDA sets up V/E block write to *R9
*  FAC area not used to avoid FS fast code
*  Word address sets V- or E-RAM
*------------------------------------------
*  VRAM restores VRAM in bank #0
 
VRAM00 EQU  $
       CLR  R8                Force VBank #0
       CLR  R9                Use for VR45 VRAM reset & VBank #0
       CLR  R12               Read
       JMP  LINR30            Bypass VRAM bank offset
 
BLKR00 EQU  $                 Set EVRAM read to R8 pointer
       MOV  @R8+R8(R13),R9    Fetch R8 source pointer
BLKR10 CLR  R12               Read mask
       JMP  LINR20            Continue
 
BLKW00 EQU  $                 Set EVRAM write to R9 pointer
B40    EQU  $+2               Also serves as ERAM bit in VR45
       LI   R12,>4000         Write mask
       MOV  @R9+R9(R13),R9    Fetch R9 destination pointer
LINR20 LI   R10,>4000         E/V masks
       LI   R7,>0400          VRAM bank offset
       MOV  R9,R8             Copy for isolating VBank
       SLA  R8,1              Strip V/E bit and make 64K byte address
       SRL  R8,6              Isolate 16 K VBank # in MSByte
       SLA  R9,1              Byte address in either of 2 64K spaces
       JOC  LINR40            Carry bit ->> ERAM
       AB   R7,R8             Adjust for VRAM banks 4-7
LINR30 CLR  R10               Must be VRAM
LINR40 LI   R11,VDPWA         Byte saver
       MOVB R10,*R11          Set V/ERAM flag bit
       SLA  R9,2              Use delay slot to strip VB #
       MOVB @HAD,*R11         V/ERAM set to VR#45
       SRL  R9,2              Leave as 16 K (14 bit) address
       MOVB R8,*R11           Write offset value
       SOCB R12,R9            Use delay slot to set R/W bit
       MOVB @H8E,*R11         VBank switch to VR14
       BL   @SETRDA           Set read/write address
       RTWP
 
*  BL services called by buffer manager & others
*-----------------------------------------------
*  Convert line # to ASCII
*-------------------------
 
BINDEC EQU  $
       LI   R12,8            Number of digits x 2
NXDIG  CLR  R8               Initialize for DIV
       DIV  @TENM2(R12),R8   R9 has the #
       SLA  R8,8             Byte adjust
       AI   R8,>3000         Convert to ASCII
       MOVB R8,*R10+         Move to output
       DECT R12              Count of digit pointer
       JGT  NXDIG
       MOVB @DSPACE,*R10+    Spaces after #
       MOVB @DSPACE,*R10+
       RT
 
*  SQUISH -- uses R0,R3,R6-R12 -- returns R3,R6
*--------------------------------------------------------
*  8-bit RLE encoding of line for buffer storage
*-------------------------------------------------------
*  E/W - byte followed by >FF => repeat byte follows
*  Repeat byte shows # chars to repeat after overwriting
*  Trailing blanks eliminated
 
SQUISH EQU  $
       MOV  @BUFADR,R8        Fetch 80 byte line buffer pointer
       MOV  R8,R7             Copy for use
       AI   R7,80             Compute and of line buffer
       LI   R3,SQSBUF         Set pointer to squish buffer
       MOV  R3,R9             Copy as destination pointer
       SETO R0                Initialize comparison
       SETO R12               Establish >FF MSB for flag and counting
 
*  Character processing
 
SQS010 SETO R6                Flag no repeats
SQS020 C    R8,R7             All the line buffer read ?
       JHE  SQS110            Yes
       MOVB *R8+,R10          Fetch a byte from line to R10 temp
       CB   R10,R12           Byte >FF replaced by >20
       JNE  SQS030            No problem
       MOVB @DSPACE,R10       Replace with blank
SQS030 MOV  R6,R6             Check repeat status
       JEQ  SQS070            1 repeat current
       JGT  SQS080            Multiple repeats
 
*  No repeats so far
 
       CB   R10,R0            Check against last byte
       JEQ  SQS060            Now a repeat
SQS040 MOVB R10,R0            Still none - renew last byte
SQS050 INC  R9                Where to write byte
       MOVB R10,*R9           Write temp to output
       JMP  SQS020            Next byte
 
*  Repeat handler
 
SQS060 CLR  R6                Flag 1 repeat
       JMP  SQS050            Write byte anyway
SQS070 CB   R10,R0            One so far - check against last
       JNE  SQS090            Repeat broken
       MOVB R12,*R9+          Flag 2 repeats in record stream
       MOVB R6,*R9            Initialize count at 0
       INC  R6                Start multi flag
       JMP  SQS020            Go get another input byte
 
*  Multiple repeats
 
SQS080 CB  R10,R0             Compare to last
       JEQ SQS100             Repeat continues
SQS090 SETO R6                Flag restart - repeat broken
       JMP  SQS040            Write and recycle
SQS100 SB   R12,*R9           Repeat continues - count one more
       JMP  SQS020            Next input byte
 
*  Exit - trim trailing blanks
 
SQS110 CB   @-1(R9),R12       Was last char flagged as repeat ?
       JNE  SQS120            No
       CB   @-2(R9),@DSPACE   Was it also a space ?
       JNE  SQS120            No
       AI   R9,-3             Omit last 3 bytes
SQS120 S    R3,R9             Chars written
       MOV  R9,R6             Copy for the record
       JEQ  SQS130            Null - must be all blanks
       INC  R9                Include length byte itself
       INCT R6                Round up for word count
       SRL  R6,1              Make word count
SQS130 SLA  R9,8              Byte adjust
       MOVB R9,*R3            Write to length byte
       RT
 
*  MOVMEM -- Deletes line/block from Edit Buffer in V/ERAM
*---------------------------------------------------------
*  Uses unrolled R/W loops for speed
*  Adjust all table entries above deleted block
*  Line pointer is not deleted for sake of UPDATE
*  Check before entry for null moves  R8,R6,R10
*------------------------------------------------
*  Takes input from R8 pointer, R10 length
*  Assumes all operations in current segment
*  VDP operations are always byte transfers
*  Uses R0, R3, R6, R8-9, R15SAV
*  Updates *R2
 
RDRETN EQU  2*INBLEN+RDBLOK   Address of final RT of in-line read
WRRETN EQU  2*INBLEN+WRBLOK   In-line Vwrite block RT
LTRETN EQU  2*INBLEN+LTBLOK   In-line CPU shuffle RT
 
MOVMEM EQU  $
       MOV  R11,@MOVM99+2     Establish return
       BLWP @FNDSEG           Locate segment with R8
       MOV  *R2,R6            *R2 = top of current segment
       MOV  R8,R9             Start address of block --> Daddr
       A    R10,R8            Saddr brackets block to remove
       S    R8,R6             Find # words to shuffle down
       JEQ  MOVM60            Saddr at top - just truncate
 
*  Move down by whole blocks worth of in-line code
 
       MOV  R15,@R15SAV       Need a register !!
MOVM10 MOV  R6,R6             Was last block all ?
       JEQ  MOVM40            Yes
       LI   R3,INBLEN-1/2     Let Assm do the round-down
       C    R6,R3             Compare to inline block size
       JHE  MOVM20            More than one block required
       MOV  R6,R3             If not use the lesser #
 
*  Move current sub-block
 
MOVM20 S    R3,R6             Subtract from length to move
       BLWP @BLKRDA           Set V/EBank RDA from R8
       A    R3,R8             Update source (upper) pointer
       NEG  R3                Index back from final RT
       MOV  R3,R11            Copy for use
       SLA  R11,2             Make word index & 2 bytes at a time
       LI   R0,BYTVBF         Set CPU buffer address
       LI   R15,VDPRD         Set up for read
       BL   @RDRETN(R11)      Work back into inline code
       BLWP @BLKWDA           Set V/Ebank WDA from R9
       S    R3,R9             Update target (lower) pointer
       SLA  R3,2              Instruction per byte, 2 at a time
       LI   R0,BYTVBF         CPU buffer
       LI   R15,VDPWD         Now for write back to VDP
       BL   @WRRETN(R3)       Index back into in-line code
       JMP  MOVM10            Go see if more to be done
 
*  Decrement line table pointers by length
*  Pointer checked for current segment & above deleted block
*  Put loop code in PAD for extra speed (10-15%)
 
MOVM40 LI   R11,MOVMCL
MOVM50 MOV  @MMCD-2(R11),@FAC-2(R11)
       DECT R11
       JGT  MOVM50
       MOV  @TXSF,R6          Highest in current segment
       MOV  @TXSN,R9          Retrieve current start pointer
       LI   R8,TBL            Start at top of table
       BL   @FAC              Execute routine in fast memory
       MOV  @R15SAV,R15       Restore for RTWP
MOVM60 S    R10,*R2           Delete last line simply
MOVM99 B    @>0               Update ptr, no move
 
*  Position independent loop code to go in PAD at FAC
*  R5,R6 preloaded with current segment shift limits
 
MMCD   C    *R8,R9            TBL Addr > LN Addr ?
       JL   MOVMC1            No
       C    *R8,R6            In current segment ?
       JH   MOVMC1            No
       S    R10,*R8           Yes, it was moved so adjust
MOVMC1 DECT R8                Move down the table
       C    R8,R1             End of table ? (TBLND)
       JHE  MMCD              No
       RT
MOVMCL EQU  $-MMCD
 
*  ChecKLineLEN -- extract current line data from buffer
*--------------------------------------------------------
*  R7 accumulates line table offset for blocks (CKLN20)
*  R10 accumulates word length entry for blocks
*  Expects R4 with line table pointer
*  Returns R8 line pointer in VDP Edit buffer (word index)
*  Returns R6 with record length as wordcount as word
*  Returns R0 with actual byte count as word
*  Returns R0,R6 null for blank line
*  Leaves with VDPRD after length byte
*-------------------------------------------
 
CKLLEN EQU  $
       CLR  R6                Ready for blank line
       CLR  R0                Byte count out also
       MOV  *R4,R8            R8 -> Edit buffer pointer
       JEQ  CKLN30            Null pointer => all blank
       BLWP @BLKRDA           Set read address in V/ERAM from R8
       MOVB @VDPRD,R6         Fetch the length byte
       SRL  R6,8              Adjust to count
       MOV  R6,R0             Save actual byte count
       INC  R6                For rounding up
       SRL  R6,1              Make a word count
       A    R6,R10            Cumulative text buffer block (words)
CKLN30 INCT R7                Line table block
       RT
 
*  LiNe INITial - get direct line table pointer
*----------------------------------------------
*  LNINIT resets line/block totals in R7/R10
*  Takes input from global data LINENO
*  Increments global to next line
*  R4 returns absolute table pointer
 
LNINIT EQU  $                 Entry to start block functions
       CLR  R7                Initialize line table block size
       CLR  R10               Initialize Edit buffer block size
LNTPOS EQU  $                 Cares about single line only
       INC  @LINENO           Advance main line pointer
       MOV  @LINENO,R4        Copy to find pointer
       MOV  R4,R9             For marker check & line # display
       SLA  R4,1              Step by words
       NEG  R4                Compute actual table address
       AI   R4,TBL            Table start growing downwards
       RT
 
*---------------------------------------------*
*  BLWP Buffer Manager service routine codes  *
*  Called directly from Editor code           *
*---------------------------------------------*
 
*  CPBLOC -- Copy a block of lines after buffer end
*--------------------------------------------------
*  Tentative copy made to test buffer availability
*  New entries after existing with temp pointers
*  CpySeg returns first available segment for copy
*  If inadequate room do not update
*  Caller handles marker shift
 
CPBL00 EQU  $
       MOV  @R13+R13(R13),R7  Copy block size
       A    R7,R7             Make byte count in table
       MOV  R1,R9             Disposable copy of Tblnd
       MOV  R1,R3             For use as temp pointer for copy
       S    R7,R9             Extend to proposed line table size
       C    R9,@LNTSAV        Check line # table space
       JLE  J2ERRM            At or exceed limit
       BLWP @TMPSEG           Refresh temp segment table
       BL   @LNINIT           Set source pointers of first line
       JMP  CPBL25            Go into main loop - at least 1 line
 
CPBL20 DEC  @R13+R13(R13)     Count off the line
       JEQ  CPBL70            Last one already done
CPBL25 BL   @CKLLEN           Entry from R4 direct table pointer
CPBL30 DECT R3                Table pointer after end
       DECT R4                Point to next line in block
       MOV  R8,*R3            Was tested line all-blank ?
       JEQ  CPBL20            Yes - do next line
       BLWP @CPYSEG           Get first segment  (R2 TXSEGT pointer)
       JMP  J2ERRM            No room - abandon hope now
       MOV  *R2,*R3           New line pointer is end of segment
       MOV  *R2,R9            Copy for VWrite set
       A    R6,*R2            Adjust to new end pointer
 
*  Read and write line to copy after existing contents
 
       LI   R11,BYTVBF        Set CPU buffer for line transfer
       BLWP @BLKRDA           Reset read address from R8
       MOV  R6,R0             Copy count for write back
CPBL50 MOVB @VDPRD,*R11+      Read even byte from VDP
       MOVB @VDPRD,*R11+      Read odd byte
       DEC  R6                Count off words
       JGT  CPBL50            More
CPBL55 BLWP @BLKWDA           Set V/ERAM write address from R9
       LI   R11,BYTVBF        Back to buffer start
CPBL60 MOVB *R11+,@VDPWD      Write even byte
       MOVB *R11+,@VDPWD      Write odd byte
       DEC  R0                Tick off
       JGT  CPBL60            More
       JMP  CPBL20            Do it all again
 
CPBL70 BLWP @UPDSEG           Update permanent segment table
       NEG  R7                Trick to add using delete code
       JMP  DLBL20            Do it
 
*  Delete a block from Edit buffer and also from line table
*----------------------------------------------------------
*  Decrement line count in R12 of caller
*  EoF checked before entry in line # parsing
*  Marker adjusted by caller
*  Save absolute table pointer - overwrite LNINIT save
*  Checks line ahead for contiguous block in same segment
*  Exits on end of buffer block with caller's R12 non-zero
*  DLLSUB in main handles re-entry in this case
*  Block line/text size accumulated in R7/R10
 
DBLC00 EQU  $                 Multi-line delete
       BL   @LNINIT           Get initial line info
       MOV  R4,R9             Save absolute table pointer
       CLR  R5                Clear non-blank flag
DBLC10 BL   @CKLLEN           Progressive line info
       MOV  R5,R5             Any non-blank so far ?
       JNE  DBLC20            Yes - already have buffer start data
       MOV  R8,R5             No - try this for size then
       JEQ  DBLC20            Yet another blank line
       BLWP @FNDSEG           Locate current segment
       MOV  R2,R3             Save segment fill data pointer
DBLC20 DEC  @R12+R12(R13)     Count off one line in caller
       JEQ  DBLC60            Delete block exhausted - remove entries
       DECT R4                Prospective next line table pointer
       MOV  R5,R5             All blanks so far ?
       JEQ  DBLC10            Another try for start pointer
       MOV  *R4,R8            Speculative check on next line ?
       JEQ  DBLC10            Blank - no need for checks
       BLWP @FNDSEG           Which segment is it in ?
       C    R2,R3             Same segment ?
       JNE  DBLC60            No - do this block now
       MOV  R5,R11            Start pointer - non-null by here
       A    R10,R11           Advance by accumulated length
       C    R11,R8            Check against next line start
       JEQ  DBLC10            Match, count it for real
 
*  Exit - last of contiguous block or whole delete
 
DBLC60 MOV  R9,R4             Restore line table block start pointer
       MOV  R5,R8             Restore start pointer in text buffer
       JNE  DELTB             Text buffer to move - segment known
       JMP  DELTC             No text to delete, only line table
J2ERRM JMP  MEMFUL            Intermediate jump
 
*  Delete a line from Edit buffer and also from line table
*---------------------------------------------------------
*  Single line only (<f-3> key)
*  Code also used by block delete
 
DELETL EQU  $                 Single-line delete
       BL   @LNINIT           Convert line# to address
       C    R9,@MKTBL         Check marker
       JHE  DELTA             No change
       DEC  @MKTBL            Drop the pointer
DELTA  BL   @CKLLEN           Now get buffer data
       MOV  R8,R8             Was it an all-blank
       JEQ  DELTC             Yes - skip mass-move
 
*  Delete block from edit buffer, then line table and adjust
 
DELTB  BL   @MOVMEM           Delete line from Edit buffer
DELTC  LI   R9,DELT1L                 Code block
DELTC1 MOV  @DELT10-2(R9),@FAC-2(R9)  Move to PAD
       DECT R9                        For speed on long loops
       JGT  DELTC1                    Finish the transfer
       MOV  R4,R9             Move line address to DADDR
       MOV  R4,R8             Set up SADDR for move
       S    R7,R8             Source start address for block
       BL   @FAC              Execute code placed at FAC
DLBL20 A    R7,R1             TBLND = TBLND + 2x(# lines)
       SRA  R7,1              # lines deleted (or added)
       S    R7,@EOF           Uncount lines
       JMP  SUBXIT            Reset VRAM and exit
 
*  Code to move to PAD for faster execution
*-----------------------------------------------
*  Simple loop -- move 1 entry in 5 instructions
*  Double loop -- 1 in 4
*  Asymptotic best -- 1 in 3  -- total unroll
*  Does not matter if overshoots
 
DELT10 MOV  *R8,*R9           Shift entry
       DECT R8                Source down one
       DECT R9                Target down one
       MOV  *R8,*R9           Double cycle in line
       DECT R8                Gives 2 words in 8 instructions
       DECT R9
       MOV  *R8,*R9           Triple cycle in line
       DECT R8                Gives 3 words in 11 instructions
       DECT R9
       MOV  *R8,*R9           Quad cycle in line
       DECT R8                Gives 4 words in 14 instructions
       DECT R9
       C    R8,R1             At current end of table ?
       JHE  DELT10            Not yet
       RT
DELT1L EQU $-DELT10
 
*  INSerTLiNe - insert new line in buffer
*----------------------------------------
*  Appends new line to end of segment
*  Inserts new entry in line table
*  Uses GETSEG to find lowest usable segment
*  SQUISH loads R3 as pointer and R6 with length
 
INSRT0 EQU  $
       C    R1,@LNTSAV        Check line # table space
       JLE  MEMFUL            At or exceed limit
       BL   @LNINIT           Use to get insert pointer
       C    R9,@MKTBL         Check marker
       JHE  INSR10            Have to move marker ?
       INC  @MKTBL            Yes
INSR10 BL   @CKLLEN           Get line data from buffer
       BL   @SQUISH           Squish from line buffer
       MOV  R6,R9             Load for segment selection
       JEQ  INSR20            All-blank - no worry
       BLWP @GETSEG           Make sure enough room for line
       JMP  MEMFUL            No room anywhere
 
*  Shuffle Line # table down for new entry
 
INSR20 INC  @EOF              Count the line
       MOV  R1,R8             TBLND -> SADDR for move
       DECT R1                Bump TBLND for inserted line
       MOV  R1,R9             New DADDR
       MOV  R4,R12            Disposable copy of insert position
       S    R9,R12            Block size to shift
       JEQ  INST30            Incoming must be new last line
INSR30 LI   R11,2*INBLEN      One word shift per instruction
       C    R12,R11           How many to go ?
       JLT  INSR40            Less than a full block
       S    R11,R12           Remove block length
       BL   @LTBLOK           Shift a whole block worth (>1FF entries)
       JMP  INSR30            Back for next
INSR40 MOV  R12,R11           Copy remaining length
       NEG  R11               Index back from RT in block code
       BL   @LTRETN(R11)      Enter part of block code
 
*  Write in the new pointer - need R2,R6
 
INST30 MOV  R6,*R4            Check if a blank line while at it
       JEQ  SUBXIT            Nothing to change in text buffer
INST35 MOV  *R2,*R4           Segment fill becomes newline text pointer
       MOV  *R2,R9            Also target for data move
       A    R6,*R2            Update segment fill pointer
 
*  Update also replaces line here - assumes SQSBUF source
 
INST40 LI   R0,SQSBUF         Inline code uses R0 for immediate SADDR
       BLWP @BLKWDA           Set write address in V/ERAM from R9
       MOV  R15,@R15SAV       Save register
       SLA  R6,2              Convert to bytes & to word index
       NEG  R6                Back from end of inline code
       LI   R15,VDPWD         Set up for write
       BL   @WRRETN(R6)       Index into inline code MOVB *R0+,*R15
       MOV  @R15SAV,R15       Restore register
 
*  Various exits - all reset VRAM
 
SUBXIT CLR  *R13              Clear error flag
       JMP  SUBXIU
ERRBFM SETO *R13              Flag buffer full in caller's R0
SUBXIU BLWP @VRAM             Reset VRAM
       RTWP
 
*  Line table or memory buffer full error exit
*------------------------------------------------
*  Does not currently distinguish between these
 
MEMFUL BLWP @VRAM             Restore VRAM
       LWPI SCNWS             Reload main WS
       B    @DSRERR           Abort
 
*  UPDaTeLiNe to modify existing line in Edit buffer
*---------------------------------------------------
*  New length = old ->> no moves, else
*  Delete old line from the Edit buffer
*  Inserted new at the end.
*  Either way, update line table entry
 
UPDATL EQU  $
       BL   @SQUISH           Compact update line  R6 -> new length
       MOV  R6,R7             Preserve new length over Cklin call
       BL   @LNTPOS           Get line table direct pointer
       MOV  *R4,R8            Buffer pointer for old line
       JNE  UPDL10            Old line has content
       MOV  R7,R11            New line all-blank also ?
       JEQ  SUBXIT            No changes to make then
       JMP  UPDL30            Go directly to new insert
 
*  Old line not blank
 
UPDL10 BLWP @BLKRDA           Set read address in V/ERAM from R8
       MOVB @VDPRD,R6         Fetch the length byte
       SRL  R6,8              Adjust to byte count
       INC  R6                Round up
       SRL  R6,1              Word count
       C    R7,R6             New length = Old length ?
       JNE  UPDL20            Different - delete before add new
       MOV  R8,R9             Set up R9 for BLKWDA
       JMP  INST40            Same - write over previous
 
*  New line different so delete old first
 
UPDL20 MOV  R6,R10            Single line is the block
       BL   @MOVMEM           Delete old line from segment with R8
UPDL30 MOV  R7,R9             Retrieve and check new length again
       JNE  UPDL40            It was non-blank
       CLR  *R4               Now set pointer to null for blank
       JMP  SUBXIT            Leave now
 
UPDL40 MOV  R9,R6             Copy for pointer updating
       BLWP @GETSEG           Check for fit - returns R2 seg fill ptr
       JMP  MEMFUL            No room anywhere
       JMP  INST35            Add replacement line to this segment
 
*  GETLiNe unsquishes line from Edit buffer to target with line #
*----------------------------------------------------------------
*  Target CPU address in screen image is @BUFADR
*  Partial CKLIN code in-lined here for speed
 
GTTLIN EQU  $
       BL   @LNTPOS           From LINENO to R4
       C    R4,R1             End of file?
       JL   ERRBFM            Yes, line # out of range
       CLR  R6                Cover blank line bypass
       MOV  *R4,R8            R8 -> Edit buffer pointer
       MOV  R8,R3             Save a copy
       JEQ  GTTL10            Null pointer => all blank
       BLWP @BLKRDA           Set read address in V/ERAM from R8
       MOVB @VDPRD,R6         Fetch the length byte
       SRL  R6,8              Adjust to byte count
 
*  Write line number in first 6 bytes
 
GTTL10 MOV  @BUFADR,R10       Start of 86 byte line
       BL   @BINDEC           Write line# (in R9) into first 6 bytes
       MOV  R10,R9            6 bytes later after line #
       MOV  R9,R7             Construct end pointer (next line start)
       AI   R7,80             80 char lines
       MOV  R7,@BUFADR        Update buffer address for screen handler
       SETO R11               Cheap quick comparison byte
       MOV  R6,R6             Test CKLIN return
       JNE  GTTL30            Start on line
 
*  Fill all or rest of line with implicit blanks
 
GTTL20 C    R9,R7             Has it quit before buffer full ?
       JHE  SUBXIT            No - already filled
       MOVB @DSPACE,*R9+      Fill the rest with spaces
       JMP  GTTL20            Loop on the fill
 
*  8-bit unsquish - check source and target lengths
 
GTTL30 MOV  R3,R8             SADDR = LNADDR in Edit Buffer
       INC  R8                Advance to first char after length byte
       A    R3,R6             Point to byte after squished record
GTTL40 C    R8,R6             Too far ?
       JHE  GTTL20            Yes - fill rest with blanks
       INC  R8                Count in VDP storage
       MOVB @VDPRD,*R9        Get character and write to target
       CB   *R9+,R11          Both VDP and buffer pointers advanced
       JEQ  GTTL60            Repeat flagged
GTTL50 C    R9,R7             Don't overrun target buffer
       JL   GTTL40            Quit while ahead
       JMP  SUBXIT            Get another byte
 
*  Repeat characters
 
GTTL60  DECT R9               Back up to byte before repeat flag
        MOVB *R9+,R0          Collect last real text byte
        MOVB R0,*R9+          Write over repeat flag
        INC  R8               Advance VDP pointer
        MOVB @VDPRD,R10       Get repeat count
        SRL  R10,8            Make word count
GTTL70  MOVB R0,*R9+          Write to target buffer
        DEC  R10              Count off
        JOC  GTTL70           More to go
        JMP  GTTL50           Check buffer
 
*  MoveLine pointer shuffle - direct BL in SCNWS
*-----------------------------------------------
*  Used in Move and Copy Lines as final cleanup
 
MVLINS EQU  $
       LI   R12,TBL           Convert line #s to table pointers
       SLA  R6,1              x 2
       SLA  R8,1
       SLA  R9,1
       NEG  R6                Table grows downward
       NEG  R8
       NEG  R9
       A    R12,R6            Offset for start
       A    R12,R8
       A    R12,R9
MVLN10 MOV  R8,R10            Copy middle for use
       MOV  R9,R12            Copy upper for use
       S    R6,R10            Length of lower segment
       JEQ  MVLN40            None left
       S    R8,R12            Length of upper segment
       JEQ  MVLN40            None left
       C    R10,R12           Which is larger ?
       JLT  MVLN30            Lower is - swap this part first
 
MVLN20 DECT R8                Adjust lower pointer
       DECT R9                Adjust upper pointer
       MOV  *R8,R14           Working copy
       MOV  *R9,*R8           Transfer word
       MOV  R14,*R9           Copy over where it came from
       DECT R12               Count off
       JGT  MVLN20            Not done yet
       JMP  MVLN10            Now it is
 
MVLN30 MOV  *R6,R14           Copy to temp
       MOV  *R8,*R6+          Transfer & adjust pointer
       MOV  R14,*R8+          Copy temp and adjust pointer
       DECT R10               Count off
       JGT  MVLN30            More
       JMP  MVLN10
MVLN40 RT
 
*  Delete block and adjust marker
*--------------------------------
*  Called from DL and Reformat
*  DLBLOC works by contiguous blocks
 
DLLSUB EQU  $
       LI   R9,MKTBL          Byte saver
       C    R15,*R9           Start above ?
       JH   DLS10             Yes - no action
       JEQ  DLS08             Cancel marker - line gone
       C    R14,*R9           End above marker then ?
       JHE  DLS08             Cancel marker
       S    R12,*R9           Excise block then
       JMP  DLS10
 
DLS08  MOV  R15,*R9           Reset marker to start of block
DLS10  MOV  R15,@LINENO       Starting line #
       BLWP @DLBLOC           Delete by contiguous blocks
       MOV  R12,R12           Check if any more blocks left
       JNE  DLS10             More to do
       RT
 
R15SAV DATA >BBBB             Temp register save
 
*------------------------------------------------------------
 
