;===============================
;
;          ZSAM.ASM
;
;===============================




; ***************************************************
; *  ZSAM - Z-DOS KEYED SEQUENTIAL ACCESS METHOD    *
; *  Copyright 1988 by R. GREHAN, BYTE MAGAZINE     *
; *  >>> 8088 version <<<                           *
; *  >>> Interfaces to Borland's Turbo C <<<        *
; *  You may use this product for educational, non- *
; *  commercial use only.                           *
; ***************************************************
;
;
; *** FUNCTION CODES FOLLOW
; 0 - REWIND                   6 - WRITE DATA REC
; 1 - READ KEY RECORD          7 - APPEND DATA REC
; 2 - READ NEXT KEYED REC.     8 - DELETE KEYED REC
; 3 - READ NEXT DATA REC       9 - DELETE KEY
; 4 - CREATE KEYED REC        10 - DELETE DATA REC
; 5 - INSERT KEY              11 - REWIND DATA REC SET

; *** ERROR CODES
ERR_KNF         EQU     200     ;KEY NOT FOUND
ERR_KAE         EQU     201     ;KEY ALREADY EXISTS
ERR_EOF         EQU     202     ;END OF FILE
ERR_EOS         EQU     203     ;END OF RECORD SET
ERR_EMF         EQU     204     ;EMPTY FILE
ERR_RSD         EQU     205     ;RECORD SET DELETED
ERR_DPI         EQU     206     ;DATA POINTER INVALID
ERR_KPI         EQU     207     ;KEY POINTER INVALID
ERR_FNZ         EQU     208     ;FILE NOT ZSAM
ERR_DNC         EQU     209     ;DATA FILE NOT COMPLEX
ERR_SOV         EQU     210     ;STACK OVERFLOW
;
KNODE_LEN       EQU     512     ;Key node length for this version

        DOSSEG                  ;Order segments acc'd to Intel
        .MODEL  SMALL           ;Use Small Model
;
; Though this version of ZSAM is meant to work with small-model
; TURBO-C, it does have all the necessary code (I hope!) to allow
; for the caller to use a separate data segment.  So, ZSAM
; puts the caller's data segment into ES and the local data
; segment in DS.
;
; **********************
; * Uninitialized data *
; **********************
        .DATA?
;
; >>> LOCAL STORAGE <<<
; KEY FILE Header
KFTYPE          DB      ?       ;Key file type
                                ;"K"=Standard key file
                                ;"O"=Keyonly file
ROOTSC          DW      ?       ;Root sector number
NOKEYSL         DW      ?       ;Number of keys in index file
NOKEYSH         DB      ?
KAVSEC          DW      ?       ;Head of key avail list
NXKYSC          DW      ?       ;Next free key sector in file
KEYLEN          DB      ?       ;Keylength
MAXKS           DB      ?       ;Max no. keys per sector
ACKYSC          DW      ?       ;Actual key sector
CUKYSC          DW      ?       ;Current key number
CUKYOF          DB      ?       ;Current key offset
IFLAG           DB      ?       ;At interstice flag
STKCNT          DB      ?       ;Stack count
PSTACK          LABEL   WORD    ;Word access to pseudo stack
PSTACKB         DB      40 DUP (?) ;Pseudo stack
KSPAC           EQU     $-OFFSET KFTYPE
;
; DATA FILE Header
DFTYPE          DB      ?       ;Data file type
                                ;"S"=Simple data file
                                ;"C"=Complex data file
RECLEN          DW      ?       ;Record length
NORECSL         DW      ?       ;Number of records in file
NORECSH         DB      ?
DAVLO           DW      ?       ;Available list
DAVHI           DB      ?
NXDALO          DW      ?       ;Next free record
NXDAHI          DB      ?
DRINTF          DB      ?       ;Data rec interstice flag
CUDALO          DW      ?       ;Current sector
CUDAHI          DB      ?
HDDALO          DW      ?       ;Head record
HDDAHI          DB      ?
TLDALO          DW      ?       ;Tail record
TLDAHI          DB      ?
FWPTLO          DW      ?       ;Forward pointer
FWPTHI          DB      ?
BKPTLO          DW      ?       ;Back pointer
BKPTHI          DB      ?
DSPAC           EQU     $-OFFSET DFTYPE
;
; TRANSFER
; ZSAM IS CALLED WITH    err = zsam(zp);  where zp = *zpacket;
; struct zpacket {
;        int funct;
;        char *keybuf;
;        char *datbuf;
;        char *key;
;        char *rec;
;        char *keyhead;
;        char *dathead;
;        }

PACKSIZ         EQU     14      ;Structure size
FUNCT           DW      ?       ;Function number
KBFPTR          DW      ?       ;Key file buffer pointer
DBFPTR          DW      ?       ;Data file buffer pointer
KEYPTR          DW      ?       ;Key$ pointer
RECPTR          DW      ?       ;Rec$ pointer
KTRANPTR        DW      ?       ;Pointer to keyfile header inf.
DTRANPTR        DW      ?       ;Pointer to datafile header inf.
;
; WORKING STORAGE
ERRNO           DW      ?       ;Error code
SPLTFG          DB      ?       ;Split flag
INSTFG          DB      ?       ;Insert flag
POPSC           DW      ?       ;Popped sector for delete
POPKYOF         DB      ?       ;Popped offset for delete
FLTKYBFW        LABEL   WORD
FLTKYBF         DB      72 DUP (?) ;Floating key buffer
KEYCNT          DB      ?       ;Key count
WORKYW          LABEL   WORD
WORKY           DB      574 DUP (?) ;Working key buffer
PACKPTR         DW      ?       ;Packet pointer holding

;*******************
; Initialized data
;*******************
                .DATA
;
; JUMP VECTOR TABLE
JVECTS          DW      OFFSET REWF             ;Rewind file
                DW      OFFSET READKR           ;Read keyed record
                DW      OFFSET READNKR          ;Read next keyed record
                DW      OFFSET READNDR          ;Read next data record
                DW      OFFSET CRKY             ;Create keyed record
                DW      OFFSET CRKY             ;Insert key
                DW      OFFSET WRITEDR          ;Write data record
                DW      OFFSET APPENDR          ;Append data record
                DW      OFFSET DELEKR           ;Delete keyed record
                DW      OFFSET DELE             ;Delete key
                DW      OFFSET DELEDR           ;Delete data record
                DW      OFFSET REWDR            ;Rewind data set

                .CODE
;
; External functions
        EXTRN   _zgetks:PROC
        EXTRN   _zputks:PROC
        EXTRN   _zgetdr:PROC
        EXTRN   _zputdr:PROC

;
; ***************************************
; * FOLLOWING IS THE OUTER ROUTINE THAT *
; * HANDLES THE ENTRY TO AND EXIT FROM  *
; * ZSAM.                               *
; ***************************************
                PUBLIC  _zsam
_zsam           PROC
                PUSH    BP                      ;**For C **
                MOV     BP,SP                   ;Get stack ptr
                PUSH    ES                      ;Save their ES
                PUSH    SI                      ;..and SI
                PUSH    DI                      ;..and DI
                MOV     AX,DS
                MOV     ES,AX                   ;Their DS is our ES
;**ADD CODE HERE TO INITIALIZE LOCAL DATA
;**SEGMENT IF NECESSARY.
                MOV     SI,[BP+4]               ;Structure pnter - source
                MOV     PACKPTR,SI
                MOV     DI,OFFSET FUNCT         ;Dest.
                MOV     CX,PACKSIZ              ;# bytes
                MOV     DX,ES                   ;Swap ES,DS
                MOV     BX,DS
                MOV     ES,BX
                MOV     DS,DX
                REP     MOVSB
                MOV     ES,DX                   ;Restore
                MOV     DS,BX
; Get transfer information.  First look at FUNCT and see
; how much to actually transfer
                MOV     AX,FUNCT
                CMP     AL,3                    ;RD NEXT DATA?
                JE      ZSAM0
                CMP     AL,6                    ;WRITE DATA?
                JE      ZSAM0
                CMP     AL,7                    ;APPEND DATA?
                JE      ZSAM0
                CMP     AL,10                   ;DELETE DATA?
                JGE     ZSAM0
                MOV     SI,KTRANPTR             ;Source
                MOV     DI,OFFSET KFTYPE        ;Dest.
                MOV     CX,KSPAC                ;# bytes
                MOV     DS,DX                   ;Swap ES & DS again
                MOV     ES,BX
                REP     MOVSB
                MOV     DS,BX
                MOV     ES,DX
                JMP     SHORT ZSAM0A
;Set KTRANPTR to -1 to show we don't need to send anything back
ZSAM0:          MOV     WORD PTR KTRANPTR,0FFFFH
;Transfer data file information (if data file is used)
ZSAM0A:         CMP     BYTE PTR KFTYPE,'O'
                JE      ZSAM1                   ;Jump if not
                CMP     AL,0                    ;REWIND?
                JE      ZSAM1
                CMP     AL,9                    ;DELETE KEY?
                JE      ZSAM1
                MOV     SI,DTRANPTR             ;Source
                MOV     DI,OFFSET DFTYPE        ;Dest.
                MOV     CX,DSPAC
                MOV     DS,DX                   ;Swap ES & DS again!
                MOV     ES,BX
                REP     MOVSB
                MOV     DS,BX
                MOV     ES,DX
                JMP     SHORT ZSAM2
; Set DTRANPTR to -1 to show we don't need to send data file info
; back to the caller.
ZSAM1:          MOV     WORD PTR DTRANPTR,0FFFFH
;Vector to routine
ZSAM2:          MOV     WORD PTR ERRNO,0        ;Clear any old errors
                MOV     BX,FUNCT                ;Get function
                ADD     BX,BX                   ;Byte addr
                CALL    CS:WORD PTR JVECTS[BX]
;All done - go home
; Do we need to send key info back?
                MOV     DI,KTRANPTR
                INC     DI                      ;DI=FFFF?
                JZ      ZSAM3                   ;Jump if no info
                DEC     DI
                MOV     SI,OFFSET KFTYPE
                MOV     CX,KSPAC
                REP     MOVSB
; Do we need to send data info back?
ZSAM3:          MOV     DI,DTRANPTR
                INC     DI                      ;DI=FFFF?
                JZ      ZSAM4                   ;Jump if no info
                DEC     DI
                MOV     SI,OFFSET DFTYPE
                MOV     CX,DSPAC
                REP     MOVSB
; Send back error code
ZSAM4:          MOV     AX,ERRNO
                MOV     BX,ES
                MOV     DS,BX                   ;Put back DS
                POP     DI                      ;...and other saved regs
                POP     SI
                POP     ES
                POP     BP
                RET                             ;Small model short return
_zsam           ENDP
;
; ****************************************
; *    REWIND KEY FILE MAIN ROUTINE      *
; ****************************************
REWF            PROC    NEAR
                XOR     AX,AX                   ;Clear AX
                MOV     CUKYOF,AL               ;Zero key pointer
                MOV     CUKYSC,AX               ;Zero sector
                MOV     STKCNT,AL               ;Zero stack
                RET
REWF            ENDP
;
; ****************************************
; * REWIND DATA REC SET MAIN ROUTINE     *
; ****************************************
REWDR           PROC    NEAR
; Must be a complex data file
                CMP     BYTE PTR DFTYPE,'C'
                JZ      REWDR1
                MOV     WORD PTR ERRNO,ERR_DNC
                RET
;Set current to head and turn on data int. flag
REWDR1:         MOV     AL,HDDAHI
                MOV     CUDAHI,AL
                MOV     AX,HDDALO
                MOV     CUDALO,AX
                MOV     BYTE PTR DRINTF,1
                RET
REWDR           ENDP
;
; ****************************************
; *     READ KEYED RECORD MAIN ROUTINE   *
; ****************************************
READKR          PROC    NEAR
                CALL    SRCH                    ;Look for the key
                CMP     WORD PTR ERRNO,0                ;Found?
                JZ      READKR1
                XOR     AX,AX
                MOV     CUDALO,AX               ;Show invalid data
                MOV     CUDAHI,AL
                RET
READKR1:        CMP     BYTE PTR KFTYPE,'O'     ;Keyonly file?
                JE      READKRX                 ;Skip data record read
                CALL    RDDATAR                 ;Read data record
READKRX:        RET                             ;Go home
READKR          ENDP
;
; ****************************************
; *  READ NEXT KEYED RECORD MAIN ROUTINE *
; ****************************************
READNKR         PROC    NEAR
                CALL    SSK                     ;Seek successor
                CMP     WORD PTR ERRNO,0                ;Hit eof?
                JZ      READNKR1
;Show invalid data
                XOR     AX,AX
                MOV     CUDALO,AX
                MOV     CUDAHI,AL
                RET
;Copy the key into into zsam->key
READNKR1:       CALL    CALKYO                  ;Get key offset
                MOV     SI,BX                   ;In SI
                ADD     SI,KBFPTR               ;Source
                MOV     DI,KEYPTR               ;Dest.
                MOV     CL,KEYLEN               ;# bytes
                XOR     CH,CH
                PUSH    DS                      ;DS=ES
                PUSH    ES
                POP     DS
                REP     MOVSB
                POP     DS                      ;Restore
;Read data record
                CMP     BYTE PTR KFTYPE,'O'     ;Keyonly file?
                JE      READNKRX                ;Skip data read if so.
                CALL    RDDATAR
READNKRX:       RET
READNKR         ENDP
;
; *****************************************
; *  READ NEXT DATA RECORD MAIN ROUTINE   *
; *****************************************
READNDR         PROC    NEAR
;Make sure we have a complex data file
                CMP     BYTE PTR DFTYPE,'C'
                JZ      READNDR1
                MOV     WORD PTR ERRNO,ERR_DNC
                RET
;Check for valid data pointer
READNDR1:       MOV     AL,CUDAHI
                XOR     AH,AH
                OR      AX,CUDALO
                JNZ     READNDR2
                MOV     WORD PTR ERRNO,ERR_DPI
                RET
;See if we are at interstice -- just clear DRINTF if so
READNDR2:       MOV     AH,FWPTHI               ;Get forward link
                MOV     BX,FWPTLO
                CMP     BYTE PTR DRINTF,0
                JE      READNDR3
                MOV     BYTE PTR DRINTF,0
                JMP     SHORT READNDR5
;Looks ok -- fetch next record if possible
READNDR3:       OR      AH,AH                   ;Are we at tail?
                JNZ     READNDR5
                OR      BX,BX
                JNZ     READNDR5
;We are at end of set - do a rewind
                CALL    REWDR
                MOV     WORD PTR ERRNO,ERR_EOS
                RET
;Passed all tests -- read the record
READNDR5:       CALL    RDCDATR
                RET
READNDR         ENDP
;
; ***************************************
; *    APPEND DATA RECORD MAIN ROUTINE  *
; ***************************************
APPENDR         PROC    NEAR
; Must be complex data file
                CMP     BYTE PTR DFTYPE,'C'
                JZ      APPENDR1
                MOV     BYTE PTR ERRNO,ERR_DNC
                RET
; Ok - do append - find a free data record
APPENDR1:       CALL    FREDT
; Now set up record -- fix link information first
                MOV     SI,OFFSET FWPTLO
                XOR     AX,AX
                MOV     [SI],AX                 ;End of set marker
                MOV     [SI+2],AL
                MOV     AX,TLDALO
                MOV     WORD PTR [SI+3],AX
                MOV     AL,TLDAHI
                MOV     BYTE PTR [SI+5],AL
                MOV     CX,6                    ;# bytes
                MOV     AH,CUDAHI
                MOV     BX,CUDALO
                PUSH    AX
                PUSH    BX                      ;Save
                XOR     AL,AL                   ;Offset 0
                MOV     DX,DS                   ;Our segment
                CALL    WDATR                   ;Write pointer info
;Pointer info written - now write the data record
                POP     BX                      ;Restore record number
                POP     AX
                MOV     AL,6                    ;Offset past pointers
                MOV     SI,RECPTR
                MOV     DX,ES                   ;Caller's segment
                MOV     CX,RECLEN               ;# of bytes
                CALL    WDATR
;Fix links - tail first
                MOV     BX,TLDALO
                MOV     CX,CUDALO
                MOV     TLDALO,CX
                MOV     AH,TLDAHI
                MOV     CL,CUDAHI
                MOV     TLDAHI,CL
                MOV     CX,3                    ;Only writing a pointer
                PUSH    CX
                MOV     SI,OFFSET CUDALO
                PUSH    SI
                XOR     AL,AL                   ;Offset 0
                MOV     DX,DS                   ;Our segment
                CALL    WDATR
;Now fix head
                MOV     AH,HDDAHI
                MOV     BX,HDDALO
                POP     SI                      ;Restore buffer
                POP     CX                      ;Restore count
                MOV     DX,DS
                MOV     AL,3                    ;Offset 3 within record
                CALL    WDATR
;Now set inter flag
                MOV     BYTE PTR DRINTF,0       ;Show at record
                RET
APPENDR         ENDP
;
; ****************************************
; *    WRITE DATA RECORD MAIN ROUTINE    *
; ****************************************
WRITEDR         PROC    NEAR
; Make sure we have a valid data pointer
                CMP     BYTE PTR DRINTF,0
                JNZ     WRITEDR1
                MOV     AH,CUDAHI
                MOV     BX,CUDALO
                OR      AH,AH
                JNZ     WRITEDR2
                OR      BX,BX
                JNZ     WRITEDR2
WRITEDR1:       MOV     WORD PTR ERRNO,ERR_DPI
                RET
;Data pointer valid -- check file type
WRITEDR2:       XOR     AL,AL           ;Assume simple
                CMP     BYTE PTR DFTYPE,'S'
                JZ      WRITEDR3
;Complex file type
                MOV     AL,6            ;Offset for complex file
WRITEDR3:       MOV     SI,RECPTR       ;Data
                MOV     CX,RECLEN       ;# bytes
                MOV     DX,ES           ;Segment
                CALL    WDATR           ;Do it
WRITEDR4:       RET
WRITEDR         ENDP
;
;
; ****************************************
; *    DELETE KEYED RECORD MAIN ROUTINE  *
; ****************************************
DELEKR          PROC    NEAR
; First delete the key
                CALL    DELE
; See if key delete was successful
                CMP     WORD PTR ERRNO,0
                JE      DELEKR1
                RET
; Ok -- see what kind of file
DELEKR1:        CMP     BYTE PTR DFTYPE,'C'
                JZ      DELEKR2
; Simple file type -- just return data record to avail list
                CALL    RDAVL
                RET
; Complex file type -- delete record set
DELEKR2:        CALL    DELERS
                RET
DELEKR          ENDP
;
; ****************************************
; *    DELETE DATA RECORD MAIN ROUTINE   *
; ****************************************
DELEDR          PROC    NEAR
;First check for complex file type
                CMP     BYTE PTR DFTYPE,'C'
                JE      DELEDR0
                MOV     WORD PTR ERRNO,ERR_DNC
                RET
; Now make sure we have a valid data pointer
DELEDR0:        CMP     BYTE PTR DRINTF,0
                JNZ     DELEDRA
                MOV     AL,CUDAHI
                XOR     AH,AH
                MOV     CX,AX
                MOV     BX,CUDALO
                OR      AX,BX
                JNZ     DELEDRB
DELEDRA:        MOV     WORD PTR ERRNO,ERR_DPI
                RET
;See if we are about to delete the first guy
DELEDRB:        MOV     AX,CX
                CMP     AL,HDDAHI
                JZ      DELEDRC
DELDRJP:        JMP     DELEDR2
DELEDRC:        CMP     BX,HDDALO
                JNZ     DELDRJP
;It is first -- see if also last
                CMP     AL,TLDAHI
                JNZ     DELEDR1
                CMP     BX,TLDALO
                JNZ     DELEDR1
;We are deleting last record in set -- delete the entire
;record set and return a RECORD SET DELETED error.
                CALL    DELERS
                MOV     WORD PTR ERRNO,ERR_RSD
                RET
;Not deleting entire set -- move second record into first
;and delete second
DELEDR1:        PUSH    BX
                PUSH    AX              ;Save current
                MOV     BX,FWPTLO
                MOV     AH,FWPTHI
                MOV     DI,OFFSET WORKY ;Use working key
                PUSH    DI              ;Save
                MOV     CX,RECLEN
                PUSH    CX              ;Save
                MOV     AL,6            ;Offset 6
                MOV     DX,DS           ;Segment
                CALL    RDATR
                POP     CX              ;Restore count
                POP     SI              ;Restore offset
                POP     AX              ;Get current
                POP     BX
                MOV     AL,6
                MOV     DX,DS
                CALL    WDATR
                MOV     AX,FWPTLO       ;Set up for normal delete
                MOV     CUDALO,AX
                MOV     AL,FWPTHI
                MOV     CUDAHI,AL
                CLD
                MOV     SI,OFFSET WORKY
                MOV     DI,OFFSET FWPTLO
                MOV     CX,6
                PUSH    ES
                PUSH    DS
                POP     ES              ;DS=ES
                REP     MOVSB
                POP     ES
; Return record to avail list -- save current
DELEDR2:        MOV     AX,FWPTLO
                PUSH    AX              ;Save current
                MOV     AL,FWPTHI
                PUSH    AX
                CALL    RDAVL
;FiX pointer of preceding record
                MOV     SI,OFFSET FWPTLO
                MOV     DX,DS
                MOV     CX,3
                MOV     BX,BKPTLO
                MOV     AH,BKPTHI
                XOR     AL,AL
                CALL    WDATR
;Fix pointer of following record
                MOV     SI,OFFSET BKPTLO
                MOV     DX,DS
                MOV     CX,3
                MOV     AH,FWPTHI
                MOV     BX,FWPTLO
                MOV     AL,3            ;Offset of 3
                CALL    WDATR
;Reset current -- set DRINTF
                POP     AX
                MOV     CUDAHI,AL
                POP     BX
                MOV     CUDALO,BX
                MOV     BYTE PTR DRINTF,1
;Did we kill tail record?
                CMP     AL,TLDAHI
                JNZ     DELEDR3
                CMP     BX,TLDALO
                JNZ     DELEDR3
;We did -- new tail is BWPT
                MOV     AX,BKPTLO
                MOV     TLDALO,AX
                MOV     AL,BKPTHI
                MOV     TLDAHI,AL
                XOR     AX,AX
                MOV     CUDALO,AX
                MOV     CUDAHI,AL
                MOV     WORD PTR ERRNO,ERR_EOS
;Go home
DELEDR3:        RET
DELEDR          ENDP
;
; ****************************************
; *    DELETE RECORD SET                 *
; ****************************************
DELERS          PROC    NEAR
;Attach current avail list to tail.
                MOV     SI,OFFSET DAVLO
                MOV     DX,DS                   ;Our segment
                MOV     CX,3                    ;Write 1 pointer
                XOR     AL,AL                   ;Offset 0 into record
                MOV     BX,TLDALO               ;Record is current
                MOV     AH,TLDAHI
                CALL    WDATR
;Head is now start of list
                MOV     AX,HDDALO
                MOV     DAVLO,AX
                MOV     AL,HDDAHI
                MOV     DAVHI,AL
;Show we no longer have a current data record
                XOR     AX,AX
                MOV     CUDALO,AX
                MOV     CUDAHI,AL
;Decrement number of records
                SUB     WORD PTR NORECSL,1
                SBB     BYTE PTR NORECSH,0
                RET
DELERS          ENDP
;
; ****************************************
; *    SEEK KEY ROUTINE                  *
; ****************************************
;
SRCH            PROC    NEAR
                MOV     AX,NOKEYSL              ;Get # keys
                OR      AL,NOKEYSH
                OR      AH,AL
                JNZ     SRCH1
; Empty file
                MOV     WORD PTR ERRNO,ERR_EMF  ;Set error
                CALL    REWF                    ;Rewind
                RET                             ;Go home
; File not empty
SRCH1:          XOR     AL,AL                   ;Clear AL
                MOV     STKCNT,AL
                MOV     IFLAG,AL                ;Clear stack
                MOV     AX,ROOTSC               ;Get root sector
SRCH2:          MOV     CUKYSC,AX               ;Move 'er in!
                CALL    GETKS                   ;Get key sector
; Scan current key page
                CALL    QSCKP
                JNZ     SRCH3                   ;No match?
                RET
; Come here if no match
SRCH3:          CALL    CALKYO                  ;Calc. key off. (in BX)
                ADD     BX,KBFPTR               ;Pointer to key buff.
                MOV     AX,ES:[BX-2]
                OR      AX,AX                   ;Zero?
                JNZ     SRCH4
; Pointer empty
                MOV     BYTE PTR IFLAG,1
                MOV     WORD PTR ERRNO,ERR_KNF
                RET
; Seek down tree
SRCH4:          PUSH    AX                      ;Save pointer
                CALL    PUSHS                   ;Push pseudo stack
                POP     AX
                CMP     WORD PTR ERRNO,ERR_SOV  ;Stack overflow?
                JNZ     SRCH2
                RET                             ;Return if stack overflow
SRCH            ENDP
;
; ****************************************
; * SEEK INORDER SUCCESSOR KEY ROUTINE   *
; ****************************************
;
SSK             PROC    NEAR
                MOV     AX,NOKEYSL              ;Get # keys
                OR      AL,NOKEYSH              ;Empty?
                OR      AL,AH
                JNZ     SSK1
; Empty file - set error
                MOV     WORD PTR ERRNO,ERR_EMF
                CALL    REWF                    ;Rewind file
                RET
; File not empty - see if rewound
SSK1:           MOV     AX,CUKYSC               ;Get curr. key sect
                OR      AX,AX                   ;Zero?
                JNZ     SSK2                    ;Jump if not
                MOV     AX,ROOTSC
                JMP     SHORT SSK6
;
SSK2:           CALL    GETKS                   ;Get the key sector
SSK2A:          MOV     BL,IFLAG                ;Check interstice
                OR      BL,BL
                JZ      SSK5
; Roving pointer is at a key pointer  - last one?
                MOV     BX,KBFPTR
                MOV     AH,ES:[BX]              ;Get keycount
                CMP     AH,CUKYOF               ;Last key pointer?
                JZ      SSK4
; Not last - point to next key
                MOV     BYTE PTR IFLAG,0
                RET
; Last key pointer - try to pop
SSK4:           CALL    POPS
                JZ      SSK7                    ;Stack empty?
                JMP     SHORT SSK2
; At key - incr. to next pointer
SSK5:           INC     BYTE PTR CUKYOF         ;Incr. offset
                MOV     BYTE PTR IFLAG,1
SSK5A:          CALL    CALKYO                  ;Cal. offset (BX)
                MOV     SI,KBFPTR
                MOV     AX,ES:[BX+SI-2]         ;Get pointer
; See if next pointer zero
                OR      AX,AX
                JZ      SSK2A                   ;Yup
                PUSH    AX                      ;Save keypointer
                CALL    PUSHS                   ;Push pseudo stack
                POP     AX                      ;Restore keypointer
                CMP     WORD PTR ERRNO,ERR_SOV  ;Stack oveflow?
                JNZ     SSK6
                RET
SSK6:           MOV     BYTE PTR CUKYOF,0
                MOV     BYTE PTR IFLAG,1
                MOV     CUKYSC,AX               ;Set sector
                CALL    GETKS                   ;Go git it
                JMP     SHORT SSK5A
; EOF - Rewind
SSK7:           CALL    REWF                    ;Rewind
                MOV     WORD PTR ERRNO,ERR_EOF
                RET
SSK             ENDP

;***********************************************
;*     CREATE KEY ROUTINE                      *
;***********************************************
CRKY            PROC    NEAR
                MOV     BYTE PTR SPLTFG,0       ;Clear split flag
;See if file is empty
                MOV     AX,NOKEYSL              ;See if # keys 0
                OR      AL,NOKEYSH
                OR      AL,AH
                JNZ     CRKY1
;File empty - make a root
                CALL    FREKY                   ;Get a free key page
                MOV     AX,CUKYSC
                MOV     ROOTSC,AX               ;New root
                JMP     SHORT CRKY2
;File not empty - search for key
CRKY1:          CALL    SRCH                    ;Search
                MOV     AX,ERRNO                        ;Get errorcode
                CMP     AX,ERR_KNF              ;Key not found?
                JZ      CRKY2
                CMP     AX,ERR_SOV              ;Stack overflow?
                JZ      CRKY1A                  ;Bail out if so.
;Key found or some other error
                OR      AX,AX
                JNZ     CRKY1A
                MOV     WORD PTR ERRNO,ERR_KAE
                MOV     CUDALO,AX               ;Clear data ptr
                MOV     CUDAHI,AL
CRKY1A:         RET
;Key not found - proceed with create
CRKY2:          CALL    LODKY                   ;Move key into floating buff.
                ADD     WORD PTR NOKEYSL,1
                ADC     BYTE PTR NOKEYSH,0      ;Increment number of keys
;If keyonly file or insert function - skip having to create
;a data record.
                CMP     BYTE PTR KFTYPE,'O'     ;Keyonly file?
                JNZ     CRKY2B
CRKY2A:         JMP     CRKY7
CRKY2B:         CMP     WORD PTR FUNCT,5        ;Insert?
                JZ      CRKY2A
;Set up data
                CALL    FREDT                   ;Get a free data page
                ADD     WORD PTR NORECSL,1      ;Increment # of records
                ADC     BYTE PTR NORECSH,0
                XOR     AX,AX
                CMP     BYTE PTR DFTYPE,'S'
                JE      CRKY3
;Complex data file -- write out the pointers.
                MOV     FWPTLO,AX
                MOV     FWPTHI,AL
                MOV     BX,CUDALO
                MOV     TLDALO,BX
                MOV     HDDALO,BX
                MOV     BKPTLO,BX
                MOV     AH,CUDAHI
                MOV     TLDAHI,AH
                MOV     HDDAHI,AH
                MOV     BKPTHI,AH
                MOV     SI,OFFSET FWPTLO
                MOV     CX,6
                MOV     DX,DS
                CALL    WDATR                   ;Write out pointers
                MOV     AL,6
;Now write data record
CRKY3:          MOV     SI,RECPTR
                MOV     CX,RECLEN
                MOV     BX,CUDALO
                MOV     AH,CUDAHI
                MOV     DX,ES
                CALL    WDATR
;Move offsets to floating buffer
CRKY7:          MOV     SI,2+OFFSET FLTKYBF
                MOV     AL,KEYLEN
                CBW
                ADD     SI,AX
                MOV     AX,CUDALO
                MOV     [SI],AX
                MOV     AL,CUDAHI
                MOV     [SI+2],AL
;Move key buffer to working key buffer
CRKY8:          CALL    BUTOWO
                PUSH    ES                      ;Save
                PUSH    DS
                POP     ES                      ;ES==DS
;Insert key - first open gap
                MOV     AL,WORKY
                MOV     KEYCNT,AL               ;Save key count
                CALL    CALKYO                  ;Calc key offset
                SUB     BX,2                    ;Pnt to leftkeypntr
                MOV     CX,KNODE_LEN
                SUB     CX,BX                   ;Count in CX
                ADD     BX,OFFSET WORKY         ;Source in BX
                PUSH    BX                      ;Save for later
                MOV     SI,BX
                MOV     AL,KEYLEN
                ADD     AL,5
                CBW
                ADD     BX,AX                   ;Calc dest.
                MOV     DI,BX                   ;Set up
                STD                             ;Decrement pointers
                ADD     SI,CX                   ;Fixup pointers
                DEC     SI
                ADD     DI,CX
                DEC     DI
                REP     MOVSB
;Put key in gap
                MOV     SI,OFFSET FLTKYBF       ;Source
                POP     DI                      ;Dest
                MOV     CL,KEYLEN
                ADD     CL,7                    ;# bytes
                XOR     CH,CH
                CLD                             ;Direction now increment
                REP     MOVSB
;Incr keycount
                INC     BYTE PTR KEYCNT
                POP     ES                      ;Restore ES
;Did we overrun?
                MOV     AL,KEYCNT
                CMP     AL,MAXKS
                JA      CRKY8A                  ;No split
                JMP     CRKY11
;Overrun has occured, must split
;Get center key
CRKY8A:         MOV     BYTE PTR SPLTFG,1       ;Set flag
                SHR     AL,1                    ;Keycount/2
                MOV     CUKYOF,AL
                CALL    CALKYO                  ;Get offset
                PUSH    BX
                ADD     BX,OFFSET WORKY         ;True addr
                MOV     SI,BX
                MOV     DI,2+OFFSET FLTKYBF     ;Dest.
                MOV     AL,KEYLEN
                ADD     AL,3
                CBW
                MOV     CX,AX                   ;Count
                PUSH    ES                      ;Save again
                PUSH    DS
                POP     ES
                CLD
                REP     MOVSB
                POP     ES                      ;Restore
;Set leftkeypointer to current sector
                MOV     AX,CUKYSC               ;Get sector
                MOV     FLTKYBFW,AX
;Fix new keycount
                MOV     AL,KEYCNT
                SHR     AL,1
                MOV     WORKY,AL
;Move working buffer out
                CALL    WOTOBU
                CALL    PUTKS                   ;Put key sector
;Create a new key sector
                CALL    FREKY
;Mov sector no. to rightkey pointer of floating buff
                MOV     BL,KEYLEN
                XOR     BH,BH
                ADD     BX,5+OFFSET FLTKYBF
                MOV     AX,CUKYSC
                MOV     [BX],AX
;Move new sector to buffer
                POP     SI                      ;Source
                MOV     AL,KEYLEN
                ADD     AL,3
                CBW
                ADD     SI,AX                   ;Source addr
                MOV     CX,574
                SUB     CX,SI
                ADD     SI,OFFSET WORKY
                MOV     DI,KBFPTR
                INC     DI                      ;Skip past count
                CLD
                REP     MOVSB
;Save new keycount
                MOV     AL,KEYCNT
                SHR     AL,1                    ;keycnt/2
                JC      CRKY9                   ;Odd?
                DEC     AL
CRKY9:          MOV     DI,KBFPTR
                MOV     ES:BYTE PTR [DI],AL     ;Save it
                CALL    PUTKS                   ;Put key sector
;Try to pop
                CALL    POPS
                JZ      CRKY10
;You can - load sector & do this again
                CALL    GETKS
                JMP     CRKY8
;Unable to POP - make a root
CRKY10:         CALL    FREKY                   ;Create a free key page
                MOV     AX,CUKYSC
                MOV     ROOTSC,AX               ;Set new root sector
                JMP     CRKY8
;Exit - save increment
CRKY11:         MOV     AL,KEYCNT
                MOV     WORKY,AL
                CALL    WOTOBU                  ;Move to buffer
                MOV     WORD PTR ERRNO,0                ;Clear error
                CALL    PUTKS                   ;Write mod. sector
;See if split occured
                MOV     AL,SPLTFG
                OR      AL,AL
                JZ      CRKY12
                CALL    SRCH                    ;Do search
                RET
;No split - not on interstice
CRKY12:         MOV     BYTE PTR IFLAG,0
                RET
CRKY            ENDP

;***********************************************
;*      DELETE KEY ROUTINE MAIN ALGORITHM       *
;***********************************************
DELE            PROC    NEAR
;Is file empty?
                MOV     AX,NOKEYSL
                OR      AL,NOKEYSH
                OR      AL,AH
                JNZ     DELE1
;
                MOV     WORD PTR ERRNO,ERR_EMF
                RET
;Is the key on the current sector?
DELE1:          MOV     AX,CUKYSC
                OR      AL,CUKYOF
                OR      AL,AH
                JZ      DELE2
                CALL    GETKS
                CALL    QSCKP
                JZ      DELE2A
DELE2:          CALL    SRCH                    ;Oh, well, do a search
                CMP     WORD PTR ERRNO,0                ;Found?
                JZ      DELE2A
                RET
DELE2A:         SUB     WORD PTR NOKEYSL,1
                SBB     BYTE PTR NOKEYSH,0
;Get data record info.
                CALL    GETDAPO
                PUSH    BX
                ADD     BX,KBFPTR
                PUSH    BX                      ;Save for later
                MOV     AX,ES:[BX]
                MOV     CUDALO,AX               ;Get data sector
                MOV     AL,ES:[BX+2]
                MOV     CUDAHI,AL               ;Get data offset
;Look at left and right keypointer-see if either is zero.
;If so, we're at leaves and we're ok.  Else we've gotta do tricky stuff
DELE3:          POP     SI                      ;Retrieve data ptr
DELE3A:         ADD     SI,3
                MOV     BX,ES:[SI]              ;Right pointer
                SUB     SI,5
                MOV     AL,KEYLEN
                CBW
                SUB     SI,AX
                MOV     AX,ES:[SI]              ;Left pointer
                OR      AX,AX
                JZ      DELE4
                OR      BX,BX
                JZ      DELE4
                JMP     DELE8
;One or both pointers zero-merge.  Then squash key node at left
;key pointer.
DELE4:          OR      AX,BX                   ;Pointer in AX
DELE4A:         MOV     ES:[SI],AX              ;Put back in left
                ADD     SI,2                    ;Point to key
;Squash the key
                POP     DI
                ADD     DI,5
                MOV     BX,KBFPTR
DELE4B:         MOV     AH,ES:[DI+BX]
                MOV     ES:[SI],AH
                INC     SI
                INC     DI
                CMP     DI,512
                JNE     DELE4B
                DEC     BYTE PTR ES:[BX]        ;Decr. # of keys
                JNZ     DELE6
;All keys removed from sector.  Save leftmost keypointer, pop up a level,
;and stick it in father node.
                MOV     AX,ES:[BX+1]
                PUSH    AX
                CALL    RKYAV                   ;Put sector on avail list
                MOV     AX,NOKEYSL              ;File empty?
                OR      AL,NOKEYSH
                OR      AL,AH
                JZ      DELE5
;Not empty-try to pop
                CALL    POPS
                JZ      DELE5                   ;Jump if unable
                CALL    GETKS                   ;Get DAD
                CALL    CALKYO                  ;Calc offset
                ADD     BX,KBFPTR               ;Get true address
                POP     AX                      ;Get saved ptr
                MOV     ES:[BX-2],AX            ;Store
                CALL    PUTKS                   ;Restore
                JMP     SHORT DELE5A
;File empty or at root
DELE5:          POP     AX                      ;Get saved ptr
                MOV     ROOTSC,AX
;Set pointers
DELE5A:         CALL    SRCH
                MOV     WORD PTR ERRNO,0                ;Clear error
                RET
;Keys NOT all removed from sector. See if we can coalesce
DELE6:          MOV     AL,MAXKS
                SHR     AL,1                    ;Maxkeys/2
                MOV     AH,ES:[BX]              ;# keys on node
                CALL    PUTKS                   ;Write back out
                CMP     AH,AL
                JGE     DELE5A
;Sector IS candidate for coalesce-pop and check
                MOV     KEYCNT,AH               ;Save keycount
                CALL    POPS                    ;Pop to father
                JZ      DELE5A                  ;At root-forget it
                MOV     AX,CUKYSC
                MOV     POPSC,AX                ;Save father sector
                CALL    GETKS                   ;Get father
                CALL    BUTOWO                  ;Save in working buff
;Check left sibling
                CMP     BYTE PTR CUKYOF,0       ;At leftmost?
                JZ      DELE6A
                DEC     BYTE PTR CUKYOF
                CALL    CHK_COA
                JZ      DELE7
;Check right sibling
                INC     BYTE PTR CUKYOF
DELE6A:         MOV     AL,BYTE PTR WORKY       ;Get keycount
                CMP     AL,CUKYOF               ;At rightmost?
                JNZ     DELE6B
DELE6X:         JMP     DELE5A                  ;Give up
DELE6B:         CALL    CHK_COA
                JNZ     DELE6X
;OK!! Cukyof points to father to coalesce-first get left sibling
DELE7:          MOV     AL,CUKYOF
                MOV     POPKYOF,AL              ;Save offset
                CALL    CALKYO
                ADD     BX,OFFSET WORKY
                MOV     AX,[BX-2]               ;Left sibling
                PUSH    AX                      ;Save for later
                PUSH    BX                      ;Save offset
                MOV     CUKYSC,AX
                CALL    GETKS
                POP     SI
                PUSH    SI
                MOV     DI,KBFPTR
                MOV     AL,ES:[DI]
                MOV     CUKYOF,AL
                CALL    CALKYO
                ADD     DI,BX                   ;Calc. true dest.
                MOV     CL,KEYLEN
                XOR     CH,CH
                CLD
                REP     MOVSB
;Get right sibling
                POP     BX                      ;Get offset
                MOV     AL,KEYLEN
                CBW
                ADD     BX,AX
                ADD     BX,3
                MOV     AX,[BX]
                MOV     CUKYSC,AX
                CALL    BUTOWO                  ;Left to working
                CALL    GETKS
;Attach right sibling
                MOV     SI,KBFPTR
                MOV     CL,ES:[SI]
                PUSH    CX
                INC     SI                      ;Source
                INC     BYTE PTR CUKYOF
                CALL    CALKYO
                SUB     BL,2                    ;Back up to pointer
                ADD     BX,OFFSET WORKY         ;Dest
                MOV     DI,BX
                MOV     CUKYOF,CL
                CALL    CALKYO                  ;Calc true length
                MOV     CX,BX
                DEC     CX
                MOV     DX,ES                   ;Swap DS and ES
                MOV     BX,DS
                MOV     ES,BX
                MOV     DS,DX
                REP     MOVSB                   ;And move
                MOV     ES,DX
                MOV     DS,BX
                POP     AX                      ;Restore count
                ADD     AL,BYTE PTR WORKY
                INC     AL
                MOV     BYTE PTR WORKY,AL       ;New byte count
;Put right sibling on avail
                CALL    RKYAV
;Move out left sibling
                CALL    WOTOBU
                POP     AX
                PUSH    AX
                MOV     CUKYSC,AX
                CALL    PUTKS
;Restore father node, delete DAD and repeat
                MOV     AX,POPSC
                MOV     CUKYSC,AX
                CALL    GETKS
                MOV     AL,POPKYOF
                MOV     CUKYOF,AL
                CALL    CALKYO
                MOV     SI,BX
                SUB     SI,2
                ADD     SI,KBFPTR
                POP     AX
                CALL    GETDAPO
                PUSH    BX
                JMP     DELE4A
;
;Neither pointer zero - replace key with inorder successor
DELE8:          CALL    BUTOWO          ;Save in working buffer
                MOV     AX,CUKYSC       ;Get current sector
                PUSH    AX              ;And save
                CALL    CALKYO          ;Calc offset
                ADD     BX,OFFSET WORKY ;True address of key
                PUSH    BX              ;Save upcoming dest.
; Find inorder successor
                CALL    SSK
; Move successor to deleted slot
                CALL    CALKYO          ;Get offset
                MOV     SI,KBFPTR
                ADD     SI,BX           ;Source
                POP     DI              ;Dest.
                MOV     CL,KEYLEN       ;# bytes
                ADD     CL,3
                XOR     CH,CH
                MOV     DX,ES           ;Move from caller's segment to...
                MOV     BX,DS           ;...ours.
                MOV     ES,BX
                MOV     DS,DX
                REP     MOVSB           ;Move it.
                MOV     ES,DX
                MOV     DS,BX
; Rotate working & key buffer
                CALL    ROT
; Write out modified sector
                MOV     AX,CUKYSC
                POP     BX
                PUSH    AX
                MOV     CUKYSC,BX
                CALL    PUTKS
; Rotate again
                POP     AX
                MOV     CUKYSC,AX
                CALL    ROT
; Set pointers and reenter to handle removal of inorder successor
                CALL    GETDAPO
                MOV     SI,KBFPTR
                ADD     SI,BX
                JMP     DELE3A
DELE            ENDP
;
; **********************************************
; *    VARIOUS AND USEFUL SUBROUTINES          *
; **********************************************
;

;
; RDDATAR - READ IN A DATA RECORD
;
RDDATAR         PROC    NEAR
                CALL    GETDAPO                 ;Get data pointer
                ADD     BX,KBFPTR               ;Set it up
                MOV     AH,ES:[BX+2]            ;Get CUDAHI
                MOV     CUDAHI,AH
                MOV     HDDAHI,AH
                MOV     BX,ES:[BX]
                MOV     HDDALO,BX
                MOV     CUDALO,BX
                CMP     BYTE PTR DFTYPE,'S'     ;Simple data file?
                JE      RDDATAR1
; Complex data file - this is head record, so set the pointers
                CALL    RDCDATR                 ;Read complex
                MOV     AX,BKPTLO               ;Set tail pointer
                MOV     TLDALO,AX
                MOV     AL,BKPTHI
                MOV     TLDAHI,AL
                MOV     BYTE PTR DRINTF,0       ;Not at interstice
RDDATARX:       RET
; Simple data file
RDDATAR1:       MOV     CX,RECLEN               ;# Bytes
                MOV     DX,ES                   ;Segment
                MOV     DI,RECPTR
                XOR     AL,AL
                CALL    RDATR
                RET
RDDATAR         ENDP
;
; RDCDATR - Read complex data record
;
RDCDATR         PROC    NEAR
                PUSH    AX
                PUSH    BX                      ;Save
;First read the pointer information
                MOV     CX,6                    ;Read 6 bytes
                MOV     DX,DS                   ;Our segment
                MOV     DI,OFFSET FWPTLO        ;Buffer
                XOR     AL,AL                   ;Offset 0 of record
                CALL    RDATR                   ;Read pointers
                POP     BX                      ;Restore
                POP     AX
                MOV     DX,ES                   ;His segment
                MOV     DI,RECPTR               ;His buffer
                MOV     AL,6                    ;Skip pointers
                MOV     CX,RECLEN               ;Read entire record
                CALL    RDATR                   ;Read
                RET
RDCDATR         ENDP
;
; CALKYO - Calculate a key's true byte offset (given CUKYOF).
; Result in BX.
;
CALKYO          PROC
                PUSH    AX                      ;Save AX
                MOV     AL,KEYLEN
                ADD     AL,5                    ;Overhead
                MUL     CUKYOF
                ADD     AX,3                    ;Init. offset
                MOV     BX,AX                   ;Into BX
                POP     AX                      ;Restore
                RET
CALKYO          ENDP
;
; LOAD KEY INTO FLOATING KEY BUFFER
;
LODKY           PROC    NEAR
                MOV     DI,OFFSET FLTKYBF       ;Source
                MOV     CX,72
                CALL    FILLZ                   ;Clear out buffer
                MOV     CL,KEYLEN
                XOR     CH,CH
;Now set up pointers
                MOV     SI,KEYPTR               ;Source
                MOV     DI,OFFSET FLTKYBF+2     ;Destination
                CLD
;Swap ES and DS
                PUSH    ES
                PUSH    DS
                POP     ES
                POP     DS
                REP     MOVSB
                PUSH    ES
                PUSH    DS
                POP     ES
                POP     DS
                RET
LODKY           ENDP
;
; READ DATA RECORD
; AH = Hi byte of record to read
; BX = Lo word of record to read
; AL = Offset into record to read from
; CX=# OF BYTES
; DX = SEGMENT OF MEMORY TO READ
; DI = OFFSET OF MEMORY TO READ
; ** SI is preserved
RDATR           PROC    NEAR
                PUSH    SI                      ;Save SI
                PUSH    ES                      ;Save ES
                PUSH    CX                      ;Save count for later
                PUSH    DX                      ;Save segment, too.
                PUSH    CX                      ;Push count
                MOV     CL,AH
                XOR     CH,CH
                PUSH    CX                      ;Push Hi
                PUSH    BX                      ;Push Lo
                XOR     AH,AH
                PUSH    AX                      ;Push offset
                MOV     AX,RECLEN
;If the file is complex, add 6 to record length
                CMP     BYTE PTR DFTYPE,'C'
                JNZ     RDATR1
                ADD     AX,6
RDATR1:         PUSH    AX
                CALL    _zgetdr                 ;Get the data record
                ADD     SP,10                   ;Clear stack
                POP     DX                      ;Restore segment
                POP     CX                      ;Restore count
                POP     ES                      ;Restore ES
                MOV     SI,DBFPTR               ;Source address
                PUSH    DS                      ;Save DS
                MOV     AX,ES
                MOV     DS,AX
                MOV     ES,DX
                CLD
                REP     MOVSB                   ;Copy the stuff
                MOV     ES,AX                   ;Restore
                POP     DS
                POP     SI                      ;Restore
                RET
RDATR           ENDP
;
; WRITE DATA RECORD
; AH = Hi byte of record to write
; BX = Lo word of record to write
; AL = Offset into record to write from
; CX=# OF BYTES
; DX = SEGMENT OF MEMORY TO WRITE
; SI = OFFSET OF MEMORY TO WRITE
; ** DI is preserved
WDATR           PROC    NEAR
                PUSH    DI                      ;Protect from C
                PUSH    ES
;First copy stuff into data buffer
                MOV     DI,DBFPTR
                PUSH    CX                      ;Save count
                PUSH    DS
                MOV     DS,DX
                REP     MOVSB
                POP     DS
                MOV     DL,AH
                XOR     DH,DH
                PUSH    DX                      ;Record hi part
                PUSH    BX                      ;Record Lo part
                XOR     AH,AH
                PUSH    AX                      ;Record offset
                MOV     AX,RECLEN               ;Record length
;If file is complex, add 6 to record length
                CMP     BYTE PTR DFTYPE,'C'
                JNE     WDATR1
                ADD     AX,6
WDATR1:         PUSH    AX
                CALL    _zputdr                 ;Do the write
                ADD     SP,10                   ;Clear stack
                POP     ES                      ;Restore ES
                POP     DI                      ;Restore DI
                RET
WDATR           ENDP
;
; CHK_COA
; Check to see if we can coalesce.  Assume father node is
; in WORKY area, and CUKYOF points to father key.
; Returns AX=0 if coalesce is possible, else returns AX<>0
;
CHK_COA         PROC    NEAR
                MOV     DI,KBFPTR               ;Get key buff pointer
; First check right sibling
                CALL    GETDAPO                 ;Point to data
                ADD     BL,3                    ;Get right key pointer
                MOV     AX,WORKYW[BX]
                OR      AX,AX                   ;Pointer zero?
                JZ      COAFL                   ;Fail if so
                MOV     CUKYSC,AX
                PUSH    BX                      ;Save BX
                CALL    GETKS                   ;Get sibling
                POP     BX                      ;Restor BX
                MOV     AL,ES:[DI]              ;Get keycount
                SUB     BL,5                    ;Back up to left...
                SUB     BL,KEYLEN               ;...key pointer
                MOV     CX,WORKYW[BX]           ;Get it
                OR      CX,CX
                JZ      COAFL
                MOV     CUKYSC,CX
                CALL    GETKS
                ADD     AL,ES:[DI]              ;Add in count
                INC     AL
                CMP     AL,MAXKS
                JLE     COAOK                   ;Awright!
COAFL:          OR      AL,1                    ;Show fail
                RET
COAOK:          XOR     AX,AX
                RET
CHK_COA         ENDP
;
; QSCKP - Quick scan of current key page.
; This routine searches a key node for an instance of zsam->key.
; Either finds it, or returns location of where it WOULD have
; been.
;
QSCKP           PROC    NEAR
                MOV     AX,DS                   ;Save DS
                MOV     BP,ES                   ;Save ES
                XOR     CH,CH                   ;Clear for later
                CLD                             ;Direction is incr.
                MOV     BX,KBFPTR
                MOV     DL,ES:[BX]              ;Get keycount
                MOV     BYTE PTR CUKYOF,0       ;Init at leftmost
                MOV     BYTE PTR IFLAG,0
; Scan
QSCKP2:         CALL    CALKYO                  ;Calc key offset
                MOV     SI,KBFPTR               ;Get pointer
                ADD     SI,BX                   ;Addr of key
                MOV     DI,KEYPTR               ;Get zsam->key addr
                MOV     CL,KEYLEN               ;Get key length
                MOV     DS,BP                   ;DS now ES too
                REPZ    CMPSB                   ;Compare
                MOV     DS,AX                   ;Restore DS
                JZ      QSCKP5
QSCKP3:         JNC     QSCKP4
; Come here if KEY$>KEY(CUKYOF)
                INC     BYTE PTR CUKYOF         ;Bump offset
                DEC     DL                      ;Dec count
                JNZ     QSCKP2
; At end of node or at interstice
QSCKP4:         OR      BYTE PTR IFLAG,1
QSCKP5:         RET
QSCKP           ENDP
;
; Push Pseudo stack
;
PUSHS           PROC    NEAR
                PUSH    SI                      ;Save
                MOV     AL,STKCNT               ;Get stack count
                CMP     AL,40                   ;Past limit?
                JZ      PUSHE                   ;Stack full error
                CBW                             ;Sign extend
                MOV     SI,AX
                MOV     AX,CUKYSC               ;Sector
                MOV     PSTACK[SI],AX           ;Push sector
                MOV     AL,CUKYOF               ;Offset/flag
                MOV     AH,IFLAG                ;Interstice
                MOV     PSTACK+2[SI],AX
;Push
                MOV     AX,SI
                ADD     AX,4                    ;Incr. stack ptr
                MOV     STKCNT,AL               ;New pointer
                POP     SI
                RET
;Stack overflow error.
PUSHE:          MOV     WORD PTR ERRNO,ERR_SOV
                POP     SI
                RET
PUSHS           ENDP
;
; Pop pseudo stack
;
POPS            PROC    NEAR
                XOR     AH,AH
                MOV     AL,STKCNT
                OR      AL,AL                   ;Empty?
                JNZ     POPS1
                RET                             ;Exit if so
POPS1:          SUB     AL,4                    ;Pop stack
                CBW                             ;Sign extend
                PUSH    SI                      ;Save
                MOV     STKCNT,AL               ;Set new
                MOV     SI,AX
                MOV     AX,PSTACK[SI]           ;Get sector
                MOV     CUKYSC,AX
                MOV     AX,PSTACK+2[SI]
                MOV     CUKYOF,AL               ;Offset/flag
                MOV     IFLAG,AH
                POP     SI
                OR      AH,1                    ;Show not zero
                RET
POPS            ENDP
;
; Get offset to data pointer
;
GETDAPO         PROC    NEAR
                CALL    CALKYO                  ;Point to key
                PUSH    AX                      ;Save AX
                MOV     AL,KEYLEN               ;Get key lenght
                CBW                             ;Word align
                ADD     BX,AX                   ;Add keylength
                POP     AX                      ;Restore
                RET
GETDAPO         ENDP

;
; MOVE KEY BUFFER TO WORKING BUFFER
; Assumes even number of bytes in buffer.
;
BUTOWO          PROC    NEAR
                MOV     SI,KBFPTR               ;Source
                MOV     DI,OFFSET WORKY         ;Destination
                MOV     CX,KNODE_LEN/2          ;Length
                CLD
                PUSH    ES                      ;Swap ES and DS
                PUSH    DS
                POP     ES
                POP     DS
                REP     MOVSW
                PUSH    ES
                PUSH    DS
                POP     ES
                POP     DS
                RET
BUTOWO          ENDP
;
; MOVE WORKING BUFFER TO KEY BUFFER
;
WOTOBU          PROC    NEAR
                MOV     SI,OFFSET WORKY         ;Source
                MOV     DI,KBFPTR               ;Destination
                MOV     CX,KNODE_LEN/2          ;Length
                CLD
                REP     MOVSW
                RET
WOTOBU          ENDP
;
; ROTATE KEY BUFFER AND WORKING KEY BUFFER
;
ROT             PROC    NEAR
                MOV     SI,OFFSET WORKY
                MOV     DI,KBFPTR
                MOV     CX,KNODE_LEN/2
ROT1:           MOV     AX,DS:[SI]
                MOV     DX,ES:[DI]
                MOV     DS:[SI],DX
                MOV     ES:[DI],AX
                INC     SI
                INC     SI
                INC     DI
                INC     DI
                LOOP    ROT1
                RET
ROT             ENDP
;
; GETKS - GET A KEY SECTOR
;
GETKS           PROC    NEAR
                PUSH    AX
                PUSH    ES
                MOV     AX,CUKYSC               ;Sector number
                CMP     AX,ACKYSC               ;Already in?
                JZ      GETKSX
                PUSH    AX
                MOV     ACKYSC,AX
                CALL    _zgetks                 ;Jump back
                POP     AX                      ;Clear stack
GETKSX:         POP     ES                      ;Restore ES
                POP     AX                      ;Restore AX
                RET
GETKS           ENDP
;
; PUTKS - Write a key sector
;
PUTKS           PROC    NEAR
                PUSH    AX
                PUSH    ES
                MOV     AX,CUKYSC
                MOV     ACKYSC,BP               ;Set actual sector
                PUSH    AX
                CALL    _zputks
                POP     AX                      ;Fix stack
                POP     ES
                POP     AX
                RET
PUTKS           ENDP
; FIND A FREE DATA PAGE
; *** SHOULD BE MADE SMART TO SEE FULL DISK ***
;
FREDT           PROC    NEAR
                MOV     CL,DAVHI
                XOR     CH,CH
                MOV     AH,CL
                OR      CX,DAVLO
                JZ      FREDT1
;Avail list not empty
                MOV     BX,DAVLO
                MOV     DI,OFFSET DAVLO         ;Read-to offset
                MOV     DX,DS                   ;Read-to segment
                MOV     CX,3                    ;3 bytes
                CALL    RDATR
                RET
;Avail list empty -- add new record to file
FREDT1:         MOV     BX,NXDALO               ;Set current
                MOV     CUDALO,BX
                MOV     CL,NXDAHI
                MOV     CUDAHI,CL
                ADD     BX,1
                ADC     CL,0
                MOV     NXDALO,BX
                MOV     NXDAHI,CL
                RET
FREDT           ENDP
;
; RETURN DATA RECORD TO AVAIL LIST
;
RDAVL           PROC    NEAR
                MOV     DX,DS                   ;Write-from segment
                MOV     SI,OFFSET DAVLO         ;Write-from offset
                MOV     CX,3                    ;3 Bytes
                MOV     AH,CUDAHI               ;Write current data record
                MOV     BX,CUDALO
                XOR     AL,AL                   ;Offset 0 into record
                CALL    WDATR
                MOV     AX,CUDALO               ;Attach links
                MOV     DAVLO,AX
                MOV     AL,CUDAHI
                MOV     DAVHI,AL
                RET
RDAVL           ENDP
;
; FIND A FREE KEY NODE
;
FREKY           PROC    NEAR
                MOV     AX,KAVSEC               ;Get avail list
                MOV     DI,KBFPTR
                OR      AX,AX                   ;List empty?
                JZ      FREKY2
;Pull off list
                MOV     CUKYSC,AX               ;Set sector
                CALL    GETKS                   ;Get it
                MOV     AX,ES:[DI]              ;Get avail link
                MOV     KAVSEC,AX               ;Set it
;Clear the buffer
FREKY1:         MOV     CX,KNODE_LEN
FREKY1A:        MOV     ES:BYTE PTR [DI],0
                INC     DI
                LOOP    FREKY1A
                MOV     BYTE PTR CUKYOF,0
                MOV     BYTE PTR IFLAG,1
                RET
;Get next sector ***THIS SHOULD CHECK DISK SPACE ***
FREKY2:         MOV     AX,NXKYSC
                INC     WORD PTR NXKYSC         ;Bump
                MOV     CUKYSC,AX
                JMP     SHORT FREKY1
FREKY           ENDP
;
; RETURN A KEY SECTOR TO THE AVAIL LIST
;
RKYAV           PROC    NEAR
                MOV     AX,KAVSEC               ;Get avail sector
                MOV     BX,KBFPTR               ;Point to buffer
                MOV     ES:[BX],AX              ;Set up link
                CALL    PUTKS                   ;Write out sector
                MOV     AX,CUKYSC
                MOV     KAVSEC,AX               ;Attack link
                RET
RKYAV           ENDP
;
; Fill a buffer with zeroes.
; DI - Buffer    CX - Count
;
FILLZ           PROC    NEAR
FILLZ1:         MOV     BYTE PTR [DI],0
                INC     DI
                LOOP    FILLZ1
                RET
FILLZ           ENDP
;
                END


