Searching forward using scasb requires setting the direction flag DF to 0. The cld instruction does that. What it further requires is that you load the AL register with the character to be found, and the RDI register with the address of the first character of the string.
Searching backward using scasb requires setting the direction flag DF to 1. The std instruction does that. What it also requires is that you load the AL register with the character to be found, and the RDI register with the address of the last character of the string. Then the formula to use is:
RDI = AddressFirstChar + LengthString - 1
And if we plan to use the repne prefix, we also need to load the RCX register with the length of the string. You can write:
std ; Set DF to 1 (backward direction)
mov al, byte_to_find ; Load byte to find in AL
mov ecx, 13 ; Load length of the string in RCX
lea rdi, [source + rcx - 1] ; Load address of last character in RDI
jne NotFound ; Jump if not equal (byte not found)
jmp ByteFound ; Jump if equal (byte found)
NotFound:
You don't need both these jumps. Because the zero flag ZF has but two states, either it's 0 or 1, you can jump to ByteFound if ZF=1, and just fall-through in NotFound if ZF=0.
SearchLoop:
scasb ; Compare AL with byte at [RSI]
jne NotFound ; Jump if not equal (byte not found)
jmp ByteFound ; Jump if equal (byte found)
Although you named this 'SearchLoop', there's no actual loop here! After scasb has finished comparing the single byte in AL to the byte in memory at es:[rdi] you immediately jump to one of the two possible exits on the procedure. You need to repeat the scasb instruction either through prefixing this instruction with the repne prefix (REPeat while Not Equal):
repne scasb
je ByteFound
NotFound:
or else by writing a normal loop:
SearchLoop:
scasb
je ByteFound
dec rcx
jnz SearchLoop
NotFound:
NotFound:
mov rax, 0 ; Set RAX to 0 (byte not found)
jmp Done
ByteFound:
mov rax, rdi ; Store the address of the found byte in RAX
dec rax ; Adjust RAX to point to the byte before the found byte
jmp Done
Done:
ret
Some cleaning is required here, but especially your comment 'Adjust RAX to point to the byte before the found byte' is noteworthy.
I don't know why you would want to return the address before the find, but if that is indeed what you need then know that RDI is already pointing to before the found byte, so you wouldn't need that dec rax! And should you need to point at the find itself, you would rather have to increment the value:
NotFound:
xor eax, eax ; Set RAX to 0 (byte not found)
ret
ByteFound:
lea rax, [rdi + 1] ; Set RAX to point to the found byte
ret
Summary
Searching backward
std ; Set DF to 1 (backward direction)
mov al, byte_to_find ; Byte to find in AL
mov ecx, 13 ; Length of the string in RCX
lea rdi, [source + rcx - 1] ; Address of last character in RDI
repne scasb
cld ; (*)
je ByteFound
NotFound:
xor eax, eax ; Set RAX to 0 (byte not found)
ret
ByteFound:
lea rax, [rdi + 1] ; Set RAX to point to the find
ret
(*) Whenever you need to use DF=1 locally, you should always return the direction flag to the clear state. Everybody expects to find DF=0, so we shouldn't have to bother using cld all the time! See The probability of selected EFLAGS bits.
Searching forward
; Save to assume DF=0 (forward direction)
mov al, byte_to_find ; Byte to find in AL
mov ecx, 13 ; Length of the string in RCX
lea rdi, source ; Address of first character in RDI
repne scasb
lea rax, [rdi - 1] ; Optimistically set RAX to point to the find
je Done ; Byte found
NotFound:
xor eax, eax ; Set RAX to 0 (byte not found)
Done:
ret
scasbscans for one element only, it should be prefixed byrepne.scasbis wrong, it doesn't usersi. It usesdi/edi/rdidepending on mode.