📄 strstack.asm
字号:
; STRSTACK.ASM
; (c) 1989, 1990 Ashok P. Nadkarni
;
; Functions to implement the string stack object for CMDEDIT. Small Model only.
;
INCLUDE common.inc
INCLUDE general.inc
STRSTACK_ASM EQU 1
CSEG SEGMENT PARA PUBLIC 'CODE'
CSEG ENDS
DGROUP GROUP CSEG
INCLUDE buffers.inc
CSEG SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:DGROUP, DS:DGROUP, SS:DGROUP, ES:DGROUP
EXTRN stre_cmp:PROC
;+
; FUNCTION : strstk_init
;
; Initializes a $STRING_STACK descriptor. Note that buffer will contain
; overhead as well as the actual data itself.
;
; Parameters:
; BX := address of descriptor
; AX := address of buffer
; CX := buffer size
;
; Returns:
; AX = 0 if success
; else -1
;
; Register CX destroyed.
;-
strstk_init proc near
cmp cx,2 ;Need at least 2 bytes in buffer (sentinel)
jb @strstk_init_90 ;
cmp cx,32767 ; and LESS than 32767 (Need to represent
jnc @strstk_init_90 ; + and - differences between first location
; and first location beyond buffer in 16 bits)
mov [bx].low_end,ax ;starting address
add cx,ax ;Location after last addressable byte of buffer
;Various functions assume byte beyond last
;buffer location is addressable without
;segment wraparound.
jc @strstk_init_90 ;Segment wraparound.
dec cx ;CX->Last byte in buffer
mov [bx].high_end,cx
call near ptr strstk_reset
xor ax,ax ;success
jmp short @strstk_init_99
@strstk_init_90: ;Buffer too small or
mov ax,-1 ; overflow (add ax,cx instruction)
@strstk_init_99:
ret
strstk_init endp
;+
; FUNCTION : strstk_reset
;
; Resets a specified stack to its initial state. The sentinel at the
; bottom of the stack is assumed to be already there.
;
; Parameters :
; BX := address of buffer descriptor
;
; Returns :
; Nothing.
;
; Registers destroyed:
; AX
;-
strstk_reset proc near
mov ax,[bx].low_end
xchg ax,bx
mov word ptr [bx],0 ;header & trailer for sentinel null string
xchg ax,bx
inc ax ;
mov [bx].top,ax ;Stack top (sentinel null)
mov [bx].cur,ax ;Current string (sentinel null)
mov [bx].savecur,ax
mov ax,[bx].high_end ;AX->last byte of buffer
inc ax ;AX->first byte beyond buffer
mov [bx].topmark,ax ;No markers
ret
strstk_reset endp
;+
; FUNCTION: strstk_save_cur
;
; This function saves the cur pointer in the cursave field of the
; descriptor structure. It can then be restored with the
; strstk_restore_cur call. It is the caller's responsibility to
; ensure that the stack does not change in the meanwhile in such a
; way as to invalidate the pointer into it. Generally do not do
; anything except move the cur pointer around to access different
; strings.
;
; Parameters :
; BX := address of buffer descriptor
;
; Returns :
; Nothing.
;
; Registers destroyed:
; AX
strstk_save_cur proc near
mov ax,[bx].cur
mov [bx].savecur,ax
ret
strstk_save_cur endp
;+
; FUNCTION: strstk_restore_cur
;
; This function restores the cur pointer from the cursave field of the
; descriptor structure where it was stored through the
; strstk_save_cur call. It is the caller's responsibility to
; ensure that the stack does not change in the meanwhile in such a
; way as to invalidate the pointer into it. Generally do not do
; anything except move the cur pointer around to access different
; strings. Also, make sure you do a strstk_save_cur before every
; strstk_restore_cur.
;
; Parameters :
; BX := address of buffer descriptor
;
; Returns :
; Nothing.
;
; Registers destroyed:
; AX
strstk_restore_cur proc near
mov ax,[bx].savecur
mov [bx].cur,ax
ret
strstk_restore_cur endp
;+
; FUNCTION : strstk_space
;
; Returns the available space in the buffer. This is 2 less than the
; actual number of bytes remaining since we need space for a header
; and a trailer for at least one string.
;
; Parameters:
; BX := address of buffer descriptor
;
; Returns:
; CF := 1 if no space in stack even for a header or trailer
; 0 otherwise
; AX := Available space (length of max string that can be fitted)
; if CF = 0, if CF = 1, AX is indeterminate
;-
strstk_space proc near
mov ax,[bx].topmark ;Points BEYOND last usable byte
sub ax,[bx].top ;actual space in buffer + 1
;Need to deduct 1+2(for header/trailer)
sub ax,3 ;Don't use multiple 'dec ax' here
; since it does not set CF.
ret
strstk_space endp
;+
; FUNCTION : strstk_settop
;
; Sets the current string pointer to point to the newest (top
; of stack) string.
;
; Parameters:
; BX := address of buffer descriptor
;
; Returns:
; Nothing.
;
; Register AX destroyed.
;-
strstk_settop proc near
mov ax,[bx].top
mov [bx].cur,ax
ret
strstk_settop endp
;+
; FUNCTION : strstk_setbot
;
; Sets the current string pointer to point to the oldest (bottom
; of stack) string. If the stack is not empty, the null string at
; the bottom is ignored.
;
; Parameters:
; BX := address of buffer descriptor
;
; Returns:
; Nothing.
;
; Registers AX,DX destroyed.
;-
strstk_setbot proc near
mov ax,[bx].low_end
inc ax
cmp ax,[bx].top ;Is stack empty ?
je @strstk_setbot_99 ;Yes
inc ax ;Point ax to header of oldest string
xchg ax,si ;Store in SI
mov dl,[si] ;get length of string
xor dh,dh
add si,dx ;Point SI to last byte in string
inc si ;Point SI at trailer
xchg ax,si ;Restore SI and set AX to point to trailer
@strstk_setbot_99:
mov [bx].cur,ax ;Set current pointer
ret
strstk_setbot endp
;+
; FUNCTION : strstk_kill
;
; Deletes the "current" string from the stack. All strings above it
; are moved up. "current" string is updated to point to the string
; above the deleted string unless the topmost string was deleted
; in which case it is set to top of stack.
;
; Any markers pointing at the deleted string are updated to
; point to the new "current" string. Any markers pointing above
; the deleted string updated to keep pointing to their
; respective strings even after the latter are moved down.
; Naturally, the marks below the deleted string do not change.
;
; Parameters:
; BX := address of buffer descriptor
;
; Returns:
; Nothing
;
; Registers AX,CX,DX destroyed
;-
strstk_kill proc near
@save si,di
mov si,[bx].cur
mov ax,[bx].low_end ;When current points to the sentinel string,
inc ax ; exit without deleting it
cmp ax,si ;Sentinel current ?
je @strstk_kill_99 ;Yes, exit
mov cx,[bx].top ;topmost occupied location
mov dx,cx ;remember it
sub cx,si ;Num bytes to be moved into vacated positions
xor ax,ax
mov di,si ;di will be used to point to the header of
; the deleted string.
lodsb ;Get the string length into AX.
;At the same time SI now points to header
;of the string following the condemned string.
inc ax
sub di,ax ;di now points to the condemned string header.
inc ax
push ax ;Remember how many bytes are to be removed
sub dx,ax ;Top of stack is now (length of string + 2)
mov [bx].top,dx ; below the original top
jcxz @strstk_kill_50 ;If # bytes to be moved is 0, then the
;deleted string was at the top of the stack.
push di ;remember header position
rep movsb ;Copy CX bytes down into vacated positions
pop si ;Header position of new "current" string
; (same location as header of old current)
lodsb ;SI points to first byte of string
xor ah,ah ; and AL = length of new current string.
add ax,si ;AX now points to trailer of new "current"
jmp short @strstk_kill_90 ;exit
@strstk_kill_50: ;Deleted element was top of stack
xchg ax,dx ;ax = top, we want cur to be == top
@strstk_kill_90:
xchg ax,[bx].cur ;cur = top, ax = old current
pop cx ;Restore the removed byte count
; (counterpart of the 'push ax' above)
IF WANT_MARKERS
call strstk_update_marks ;Update marks for strings that were moved
; ax == old cur, cx == displacement
ENDIF
@strstk_kill_99:
@restore
ret
strstk_kill endp
;+
; FUNCTION : strstk_push
;
; Pushes a string onto the top of the stack. If the force flag parameter
; is set to any value other than 0, one or more strings at the bottom
; of the stack are deleted to make room fir the new string. If the force
; flag is 0, then an error is returned if there is not sufficient room
; in the stack. An error is also returned if the string is bigger than
; stack size. The stack is left unaltered for both error conditions.
;
; Parameters:
; BX := address of buffer stack descriptor
; AL := Length of string
; CX := force flag
; DX := address of string to be pushed
;
; Returns:
; Carry flag is set if error (not enough stack space), else it is clear.
;
; Registers AX,CX,DX destroyed.
;-
strstk_push proc near
@save si,di
xor ah,ah ;Clear high byte of length.
mov si,dx ;SI := source string
push ax ;save length
jcxz @strstk_push_10 ;Jump if force flag is 0
call strstk_makespace ;Make sure enough space, else make space
pop cx ;restore length
jc @strstk_push_99 ;Error return by strstk_makespace
jmp short @strstk_push_20 ;Everything OK, go push string
@strstk_push_10:
call near ptr strstk_space ;Find out how much space is left
pop cx ;Restore string length
jb @strstk_push_99 ;Not even enough for header/trailer,
; error return
cmp ax,cx ;Enough space on stack ?
jb @strstk_push_99 ;Nope, error return
@strstk_push_20: ;OK, copy string onto stack
mov di,[bx].top ;DI := last occupied location on stack
inc di ;DI := address of header for new string
mov ax,cx ;Get length into AL
stosb ;Store length in header
rep movsb ;Copy string
mov [bx].top,di ;New top of stack is trailer of topmost string
mov [bx].cur,di ;Ditto for current string
stosb ;Store length in trailer
clc ;Success return
@strstk_push_99:
@restore
ret
strstk_push endp
;+
; FUNCTION : strstk_fwd_match
;
; Searches towards the top of the stack, starting from the string
; above the current string looking for a string that has the specified
; pattern as a prefix. If the pattern length is 0, then the match is
; universal and the new current string is simply the one immediately
; above the current one. The function can thus be used to move the
; cur pointer up the stack one string at a time. If the current
; string is at the top of the stack, the cur pointer remains
; unchanged.
;
; Parameters:
; BX := address of buffer stack descriptor
; AX := Address of pattern
; CX := Length of pattern
; Returns:
; CF = 1 if no match or if at top of stack
; = 0 if success
;
; Registers AX,CX destroyed.
;-
strstk_fwd_match proc near
@save si,di
mov si,[bx].cur ;SI:=current ptr
mov di,AX ;Pattern address
@strstk_fwd_match_10:
cmp si,[bx].top ;Are we at top of stack ?
je @strstk_fwd_match_90 ;Yes, error exit
inc si ;SI:=point to next header
lodsb
xor ah,ah ;AX := string length
cmp ax,cx ;Is the pattern longer than string ?
jnb @strstk_fwd_match_70 ;If not go try a match
;Yes, then try next string
add si,ax ;Point to trailer
jmp short @strstk_fwd_match_10 ;and loop back
@strstk_fwd_match_70: ;OK, see if pattern is the string prefix
push cx ;Remember pattern length
push ax ;Remember string length
call near ptr stre_cmp ;Check if pattern is a prefix
pop ax ;Restore string length
mov cx,ax ;Temp storage
lahf ;Save value returned by stre_cmp
add si,cx ;SI := trailer
pop cx ;Restore pattern length
sahf ;Restore stre_cmp result
jne @strstk_fwd_match_10 ;No match, loop back
clc ;No errors
jmp short @strstk_fwd_match_99
@strstk_fwd_match_90:
stc ;Error return
@strstk_fwd_match_99:
mov [bx].cur,si
@restore
ret
strstk_fwd_match endp
;+
; FUNCTION : strstk_bck_match
;
; Searches towards the bottom of the stack, starting from the string
; below the current string looking for a string that has the specified
; pattern as a prefix. If the pattern length is 0, then the match is
; universal and the new current string is simply the one immediately
; below the current one. The function can thus be used to move the
; cur pointer down the stack one string at a time. If the current
; string is at the bottom of the stack, the cur pointer remains
; unchanged.
;
; Parameters:
; BX := address of buffer stack descriptor
; AX := Address of pattern
; CX := Length of pattern
; Returns:
; CF = 1 if no match or if at bottom of stack
; = 0 if success
;
; Registers AX,CX destroyed.
;-
strstk_bck_match proc near
@save si,di
push bp
mov bp,sp
sub sp,2
sentinel EQU <word ptr [bp-2]>
push ax ;remember pattern address
mov di,[bx].low_end ;Buffer bottom
inc di
mov si,[bx].cur ;SI:=current pointer
cmp si,di ;At stack bottom ? ( low_end + 1 == cur)
je @strstk_bck_match_90 ;If so exit,
; (the 'push AX' is cleaned up by unlink)
mov sentinel,di ;remember sentinel value
pop di ;Restore pattern address
; Prime for loop below
xor ah,ah
mov al,[si] ;AX<-length of current string
sub si,ax ;SI->start of string
@strstk_bck_match_9:
; Loop begin. SI points to the first byte of string last compared. This
; cannot be the sentinel string.
dec si ;SI->header of current string
dec si ;SI->trailer of previous string
@strstk_bck_match_10:
; At this point SI points to the trailer of string to try'n'match
cmp si,sentinel ;Are we at bottom ?
je @strstk_bck_match_90 ;Yes, exit
mov al,[si] ;String length
xor ah,ah
sub si,ax ;Point to first byte of string
cmp ax,cx ;Is the pattern longer than string ?
jb @strstk_bck_match_9 ;Yes, then try next string
;OK, try see if pattern is the string prefix
push cx ;Remember pattern length
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -