📄 strstack.asm
字号:
call near ptr stre_cmp ;Check if pattern is a prefix
pop cx
jne @strstk_bck_match_9 ;Not a prefix, go try next one
xor ax,ax
mov al,-1[si] ;AX<-length of matched string
add si,ax ;Point SI to trailer of matched string
; Successful match, return with CF clear.
; Carry flag is guaranteed clear since no o'flow is possible in
; the add si,ax instruction. Hence comment out the following clc.
; clc
jmp short @strstk_bck_match_99
@strstk_bck_match_90:
stc ;Error return
@strstk_bck_match_99:
mov [bx].cur,si ;Update current string pointer
mov sp,bp
pop bp
@restore
ret
strstk_bck_match endp
;+
; FUNCTION : strstk_fwd_find,strstk_bck_find
;
; Searches towards the top/bottom of the stack, starting from the string
; above/below the current string looking for a string that is the same
; as the specified one. If the current string is at the top/bottom
; the stack, the cur pointer remains unchanged.
;
; Parameters:
; BX := address of buffer stack descriptor
; AX := Address of string
; CX := Length of string
; Returns:
; CF = 1 if no match or if at top/bottom of stack
; = 0 if success
;
; Registers AX,CX,DX destroyed.
;-
strstk_find proc near
strstk_fwd_find LABEL near
push di ;Save di
push si ;and si
mov di,offset CSEG:strstk_fwd_match
jmp short @strstk_find_8
strstk_bck_find LABEL near
push di ;Save di
push si ;and si
mov di,offset CSEG:strstk_bck_match
@strstk_find_8:
mov si,ax ;Save string address in SI
@strstk_find_10:
; Loop start
mov ax,si ;AX->string
push cx ;Save string length
call di ;Look for prefix
pop cx ;Restore string length
jc @strstk_find_99 ;Not found
push cx
xor cx,cx ;Don't want to copy, just need size
call near ptr strstk_copy ;AX<-length of current string
pop cx ;Restore passed length
cmp ax,cx ;Strings match ?
jne @strstk_find_10 ;Lengths not same, keep looking
clc ;Strings same
@strstk_find_99:
pop si
pop di
ret
strstk_find endp
;+
; FUNCTION : strstk_makespace
;
; Deletes enough strings from the bottom of the stack to make room
; for a string of the length specified in AX. If the stack size is
; smaller than the requested size, the stack is left unchanged and
; an error indication returned. If there is already enough room in
; the stack, the stack is left unchanged. In both cases the current
; string ptr is updated to point to the top of the stack. Note that
; any marks, if present, reduce the space that is available.
;
; The routine is not very efficient since it keeps calling strstk_kill
; rather than deleting the required number in one shot. But it is
; more compact.
; Parameters:
; BX := pointer to buffer descriptor
; AX := requested length
; Returns:
; CF = 1 if error (requested length to large for stack)
; = 0 success
; Also changes current string pointer to point to top of stack.
; Registers AX,CX,DX destroyed.
;-
strstk_makespace proc near
@save si,di
xchg ax,si ;Save requested length in SI
call strstk_size ;AX := size of largest string that can
; fit in an empty stack
cmp ax,si ;Smaller than requested length ?
jb @strstk_makespace_99 ;Yes, error exit
call strstk_setbot ;Set current string to bottommost
;Now keep iterating until enough strings have been deleted. Since we
;have already checked that the stack is large enough, the loop below
;is guaranteed to terminate.
@strstk_makespace_10:
; Note that the strstk_space routine returns a 0 if there are less than
; the 2 bytes required for header/trailer. The carry flag must be
; checked to distinguish this from the situation where there is room
; for a null string (exactly 2 bytes available)
call near ptr strstk_space ;AX := available space
jnc @strstk_makespace_70 ;Jump if > 2 bytes available
@strstk_makespace_65:
call strstk_kill ;If not delete one more
jmp short @strstk_makespace_10 ;And keep trying
@strstk_makespace_70:
cmp ax,si ;Enough space available ?
jb @strstk_makespace_65 ;Yes, exit
@strstk_makespace_99:
call near ptr strstk_settop
@restore
ret
strstk_makespace endp
;+
; FUNCTION : strstk_copy
;
; Returns a copy of the current stack entry. Stack is unchanged.
; If the stack is empty, a 0 length string is returned.
; If the user buffer is not large enough, as many characters as
; possible are copied into it. AX reflects the length of the actual
; string and not just the copied part and the carry flag is set.
; Thus this routine can also be used to find the length of the current
; string by passing it a zero length buffer.
;
; Parameters:
; BX := pointer to buffer descriptor
; AX := starting address of location into which the string
; is to be copied
; CX := size of buffer
;
; Returns:
; AX = length of current string (ACTUAL LENGTH, NOT COPIED LENGTH)
; CF = 0 if the user buffer is large enough
; = 1 if user buffer too small
; As many chars as possible are returned in the use buffer.
; Register CX destroyed.
;-
strstk_copy proc near
@save si,di
mov di,ax ;Destination buffer
mov si,[bx].cur ;SI := trailer of current string
mov al,[si]
xor ah,ah ;AX:=length of current string
sub si,ax ;SI->first byte of string
cmp cx,ax ;User buffer large enough ?
jb @strstk_copy_5 ;No, error return
;Note empty stack case automatically handled.
mov cx,ax ;CX<-length of string to copy
@strstk_copy_5:
pushf ;Save carry flags
; CX == number of bytes to copy
rep movsb
popf ;Restore CF
@restore
; Return AX = length of actual string
ret
strstk_copy endp
;+
; FUNCTION : strstk_size
;
; Returns the maximum size string that can fit in the buffer if all
; strings are deleted (but not markers).
;
; Parameters:
; BX := buffer descriptor address
;
; Returns:
; AX := Max string size for buffer if all strings are deleted
;
; All registers (except AX) are preserved.
;-
strstk_size proc near
mov ax,[bx].topmark ;Address beyond last available
; for strings
sub ax,[bx].low_end ;bottom of buffer (sentinel header)
sub ax,4 ;Need 2 for header trailer + 2 for
; sentinel
jnb @strstk_size_99 ;
xor ax,ax ;No space !
@strstk_size_99:
ret
strstk_size endp
;+
; FUNCTION : strstk_prefix
;
; Compares the passed string to check if it is a prefix of the
; current string in the stack.
;
; Parameters:
; BX := buffer descriptor address
; AX := address of string
; CX := length of string
;
; Returns:
; ZF = 1 if string is prefix
; 0 if not
;
; Register(s) destroyed:
;-
strstk_prefix proc near
@save si,di
xchg di,ax ;DI->string
mov si,[bx].cur ;SI->trailer byte of stack element
xor ah,ah ;Clear high byte
mov al,[si] ;AX<-length of string
cmp ax,cx ;Passed string longer ?
jb @strstk_prefix_90 ;No, return `not prefix'
sub si,ax ;SI->start of string
call near ptr stre_cmp
; ZF is set if match occurs, 0 otherwise
jmp short @strstk_prefix_99
@strstk_prefix_90:
inc al ;Set ZF=0 (no match)
@strstk_prefix_99:
@restore
ret
strstk_prefix endp
;+
; FUNCTION : strstk_compare
;
; Compares the passed string against the current string in the stack.
;
; Parameters:
; BX := buffer descriptor address
; AX := address of string
; CX := length of string
;
; Returns:
; ZF = 1 if strings equal
; 0 if not equal.
;
; Register(s) destroyed:
;-
strstk_compare proc near
@save si,di
xchg di,ax ;DI->string
mov si,[bx].cur ;SI->trailer byte of stack element
xor ah,ah ;Clear high byte
mov al,[si] ;AX<-length of string
cmp ax,cx ;Same length?
jne @strstk_compare_99 ;No, return `not equal'
jcxz @strstk_compare_99 ;Equal (zero length), ZF
; already set
sub si,ax ;SI->start of string
call near ptr stre_cmp ;Case-insensitive compare
; ZF is set if match occurs, 0 otherwise
@strstk_compare_99:
@restore
ret
strstk_compare endp
IF WANT_MARKERS
;+
; FUNCTION : strstk_update_markers
;
; This routine is called to update the markers that point to various
; strings in the stack. This is necessary when strings are deleted
; causing the string positions to change.
;
; Parameters:
; BX := address of stack descriptor
; AX := The address of the old (deleted) current string
; CX := Number of bytes by which the strings were displaced.
;
; Returns:
; Nothing.
; Registers AX is destroyed.
;-
strstk_update_marks proc near
@save si,di
xchg ax,si ;Store old current address in SI
mov di,[bx].topmark ;DI will iterate through the marks
@strstk_update_marks_20:
cmp di,[bx].high_end
jnc @strstk_update_marks_99 ;All done
mov ax,[di] ;Old address of marked string
cmp si,ax ;Compare with address of deleted string
jne @strstk_update_marks_40 ;
mov ax,[bx].cur ;Mark pointed to deleted string so
jmp short @strstk_update_marks_50 ;point it to new current
@strstk_update_marks_40:
jg @strstk_update_marks_50 ;If mark pointed BELOW, no change
sub ax,cx ;Else subtract the displacement
@strstk_update_marks_50:
stosw ;Store new value back, increment DI to point
jmp short @strstk_update_marks_20 ; to next mark and loop
@strstk_update_marks_99:
@restore
ret
strstk_update_marks endp
;+
; FUNCTION : strstk_mark_cur
;
; Changes the topmost mark to point to the current string. If no marks
; exist, one is created.
;
; Parameters:
; BX := address of descriptor.
;
; Returns:
; CF = 1 if error (no room), else 0.
; Register AX is destroyed.
;-
strstk_mark_cur proc near
call strstk_kill_mark
call strstk_push_mark
ret
strstk_mark_cur endp
;+
; FUNCTION : strstk_kill_mark
;
; Deletes the topmost mark on the stack.
;
; Parameters:
; BX := address of buffer descriptor
;
; Returns:
; CF = 1 if stack was empty, else 0.
; Register AX is destroyed.
;-
strstk_kill_mark proc near
mov ax,[bx].high_end ;Highest location of buffer
cmp ax,[bx].topmark ;Marker stack empty ?
jb @strstk_kill_mark_99 ;Yes
dec [bx].topmark ;Remove topmost mark by updating
dec [bx].topmark ; top of stack ptr
@strstk_kill_mark_99:
ret
strstk_kill_mark endp
;+
; FUNCTION : strstk_push_mark
;
; Pushes a marker to the current string onto the marker stack.
;
; Parameters:
; BX := address of descriptor
;
; Returns:
; CF = 1 if error (no room), else 0.
; Register AX is destroyed.
;-
strstk_push_mark proc near
@save di
mov di,[bx].topmark ;Point to topmost mark
dec di
dec di ;Next location to push mark
cmp di,[bx].top ;Enough room ?
jle @strstk_push_mark_90 ;Sorry, return error
mov [bx].topmark,di ;New stack top
mov ax,[bx].cur ;Address of current string
stosw ;Store in top marker
;Carry is clear
jmp short @strstk_push_mark_99
@strstk_push_mark_90:
stc ;Set error
@strstk_push_mark_99:
@restore
strstk_push_mark endp
;+
; FUNCTION : strstk_pop_mark
;
; Pops the topmost mark off the stack and set the current ptr
; to point to its associated string.
;
; Parameters:
; BX := address of buffer descriptor
;
; Returns:
; CF = 1 if no marks, else 0.
; Register AX is destroyed.
;-
strstk_pop_mark proc near
call strstk_goto_mark
call strstk_kill_mark
ret
strstk_pop_mark endp
;+
; FUNCTION : strstk_goto_mark
;
; Sets the current string pointer to the string associated with the
; topmost marker on the marker stack.
;
; Parameters:
; BX := address of buffer descriptor
;
; Returns:
; CF = 1 if no markers, else 0.
; Register AX is destroyed.
;-
strstk_goto_mark proc near
mov ax,[bx].high_end
cmp ax,[bx].topmark
jb @strstk_goto_mark_99 ;No marks
mov ax,[bx].topmark
mov [bx].cur,ax ;Update current string ptr
@strstk_goto_mark_99:
ret
strstk_goto_mark endp
;+
; FUNCTION : strstk_purge_marks
;
; Deletes all marks that point to the sentinel string. The marker
; stack is compacted.
;
; Parameters:
; BX := address of buffer descriptor
;
; Returns:
; Nothing.
; Register(s) AX,CX are destroyed.
;-
strstk_purge_marks proc near
@save si,di
mov cx,[bx].low_end
inc cx ;CX = sentinel string trailer
mov di,[bx].high_end
inc di
mov si,di
@strstk_purge_marks_10: ;SI = last marker location examined
;DI = last marker location stored
cmp si,[bx].topmark ;Checked all markers ?
je @strstk_purge_marks_99 ;Yes, exit
dec si
dec si ;Next location to check
mov ax,[si] ;Marker value
cmp ax,cx ;Points to sentinel ?
jg @strstk_purge_marks_10 ;No, go check next
dec di
dec di ;DI = next location to store
mov [di],ax ;Store marker
jmp short @strstk_purge_marks_10
@strstk_purge_marks_99:
mov [bx].topmark,di ;Store new top of marker stack
@restore
ret
strstk_purge_marks endp
ENDIF ;WANT_MARKERS
CSEG ENDS
END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -