Find all the digits in a string
Did I make it unnecessarily complicated?


You can replace
find:
  scasb
  jz    found
  loop  find
  jmp   next

by the simpler
  repne scasb
  jne   next

The use of the SCASB instruction here depends on the direction flag DF being in the clear state (CLD). It is a safe assumption that this will be the case. Nonethesless prudent programmers will at least at program start include one CLD instruction.
If ever you find that you need to reverse the direction for the string primitives by the use of STD, you should always issue a restoring CLD as soon as possible (once the string operation has finished). Why is this? Well, just as you can expect the direction flag to be clear, any library functions that your program invokes expect the same thing. During normal program execution DF=0 is almost true 100% of the time. See.

No need to use the extra register EDI in:
mov   edi, ebx
inc   ebx
mov   [d + edi], al

Just write:
mov   [d + ebx], al
inc   ebx


The digits have well-known ASCII codes. Finding a digit is just a matter of comparing AL with the range 48 to 57. I find scanning that digits string a bit overkill, although at times that could be a good way. When the character codes aren't contiguous.
  mov   ecx, len
  jecxz end_prog
  xor   esi, esi
  xor   ebx, ebx
repeat_this:
  mov   al, [s + esi]
  cmp   al, '0'
  jb    next
  cmp   al, '9'
  ja    next
  mov   [d + ebx], al
  inc   ebx
next:   
  inc   esi
  loop  repeat_this

Clearing a register is best done using XOR &lt;reg&gt;,&lt;reg&gt;.



This shorter version uses the string primitives and avoids using the slow LOOP instruction:
    mov   ecx, len
    mov   esi, s
    mov   edi, d
    jmp   next
  repeat_this:
    lodsb
    cmp   al, '0'
    jb    next
    cmp   al, '9'
    ja    next
    stosb
  next:
    sub   ecx, 1  
    jnb   repeat_this