'===========================================================================
' Subject: REVERSE INSTR ROUTINE              Date: 01-28-97 (19:59)       
'  Author: Brian Mclaughlin                   Code: ASM                    
'  Origin: comp.lang.basic.misc             Packet: ASMCODE.ABC
'===========================================================================
; RINSTR.ASM  - assemble using MASM 5.1 or compatible assembler
;             - for use with QB4.x only
;             - written by Brian McLaughlin. Released to public domain.
;
;
; This FUNCTION acts as a reverse INSTR routine - it looks for the *LAST*
; instance of a string within another string.  RInstr% returns the position of
; the first character of the last instance of Search$ in Target$, as an offset
; from the *START* of the Target$.  
;
; For example, if the last instance of Search$ starts at the first character 
; of Target$, RInstr% returns 1. If no instance of the Search$ is found in 
; Target$, then RInstr% returns zero.  A zero is returned if Target$ is null,
; if Search$ is null, or if Search$ is longer than the Target$.
;
; Declare it:  DECLARE FUNCTION RInstr% (SearchFor$, Target$)
;
; Example:
;           FullPath$ = "D:\DIR1\DIR2\DIR3\FILENAME.EXT"
;           LastSlash% = RINSTR%("\", FullPath$)  'returns 18

.MODEL MEDIUM, BASIC
.CODE

RInstr PROC FAR USES ES DI SI, Search:WORD, Target:WORD
         Mov BX, Search      ;  BX = address of Search$ descriptor
         Mov CX, [BX]        ;  CX = length of Search$
         Jcxz NotFound       ; if Search$ is null, exit with 0
         Mov DX, CX          ; DX = length of Search$
         Mov SI, [BX+2]      ; SI = address of first char of Search$
         Mov BX, Target      ;  BX = address of Target$ descriptor
         Mov CX, [BX]        ; CX = length of Target$
         Jcxz NotFound       ; if Target is a null, exit with zero
         Mov DI, [BX+2]      ; DI = offset of first byte of Target$
         Add DI, CX          ; point DI one byte past end of Target$
         Sub DI, DX          ; point DI at last "possible" byte of Target$
         Mov AL, Byte Ptr [SI] ; AL = value of first char of Search$
         Push DS             ; assign value of DS to ES
         Pop ES              ; using the stack as the vehicle
         Sub CX, DX          ; we may not need to scan all of Target
         Js  NotFound        ; if CX < 0 then Search$ longer than Target$
         Inc CX              ; adjust to get proper number of bytes to scan
ScanStr:
         Std                 ; set direction flag for backward scan
         Repne Scasb         ; scan for a match to AL at ES:DI
         Jnz NotFound        ; zero flag cleared if no match found
         Push SI             ; save the necessary registers before
         Push DI             ; they're destroyed by Cmpsb, and note that
         Push CX             ; ES:DI points one byte prior to matching char
         Cld                 ; now we'll search forward again
         Mov CX, DX          ; for the length of Search$
         Inc DI              ; but first, re-point ES:DI at matching char
         Repe Cmpsb          ; do the comparison
         Pop CX              ; restore earlier loop counter
         Pop DI              ; and point everything back as it was
         Pop SI
         Jnz ScanStr         ; zero flag set means Cmpsb failed to match
         Inc CX              ; Scasb has decremented CX past correct byte
         Mov AX, CX          ; put the placement of first char into AX
Exit:
         Cld                 ; ALWAYS clear the direction flag before return
         Ret                 ; AX holds returned value
NotFound:
         Xor AX, AX          ; return a zero in AX
         Jmp SHORT Exit
RInstr ENDP
       END
