Search code examples
stringassemblyx86-64nasmcpu-registers

Codewars problem: "NASM: Does this string contain numbers?" Not sure why my code is failing with specific tests


I am trying to check whether a string contains digits, this code works for test cases:

expected output : input

0 : "This is sample test!"
0 : "I've just turned five!"
1 : "My favourite number is: 1337"
0 : "Lorem ipsum dolor sit amet. This text has no numbers!"
1 : "0"

But for a reason unknown to me, it fails on all of these tests in should_work_for_more_fixed_assertions:

"I <3 NASM"
"The NASM/C environment in Codewars does not use CW-2 as its testing framework."
"Hello W0rld!"
"Hello Wor1d!"
"H2llo World!"
"H3llo World!"
"Hello4World!"
... etc.

Here is my code:

global has_digits
section .text
has_digits:

  cmp rdi, 0            ; string
  je end                ; null pointer
  
  xor ax, ax            ; result = 0
  xor cx, cx            ; char = 0
  xor rsi, rsi          ; index = 0
  
  dec rsi               ; index--
  while:
    inc rsi             ; index++
    mov cx, [rdi+rsi]   ; char = string[index]
    
    cmp cx, 0           ; if char == 0:
    je end              ; break
    
    cmp cx, 48          ; if char < '0':
    jl while            ; continue
    
    cmp cx, 57          ; if char > '9':
    jg while            ; continue
    
    mov ax, 1           ; result = true
  
  end:
    ret                 ; return

It would be great if you could tell me what I am doing wrong, without giving a full solution, but any help would be appreciated. :)


Solution

  • Not sure why my code is failing with specific tests

    Let us turn that around and find out why 2 tests do report having found a number.

    1 : "My favourite number is: 1337"
    1 : "0"

    When your instruction mov cx, [rdi+rsi] ; char = string[index] supposedly retrieves a single character in the CL register, it also retrieves the following byte in the CH register. For the 2 strings that do report having found a number, it is the very last character in the string that is a digit and that effectively is followed by the zero-terminator byte, and hence processing CX instead of CL didn't pose a problem. The solution therefore is to read but a single byte from the string memory. Use the movzx ecx, byte [rdi+rsi] instruction that zero-extends the byte into the full RCX register for maximum efficiency.

    It would be great if you could tell me what I am doing wrong, without giving a full solution

    It's hardly a full solution this next code snippet:

    ; IN (rdi) OUT (rax) MOD (rcx,rdi)
    has_digits:
      xor   eax, eax        ; RAX:= false
      sub   rdi, 1
      jb    .end            ; null pointer
    .while:
      inc   rdi             ; index++
      movzx ecx, byte [rdi] ; char = string[index]
      test  ecx, ecx        ; if char == 0:
      jz    end             ; break
      sub   ecx, '0'
      cmp   ecx, 9          ; Anything outside of [0,9] is not a digit
      ja    .while
      inc   eax             ; RAX:= true, RCX=[0,9] (first digit found)
    .end:
      ret