Search code examples
arraysassemblymasm32-bitpalindrome

MASM Why doesn't decrementing a register find the next value in an array?


I'm testing to see if an entered string is a palindrome by taking the string, moving it into a character array and comparing first and last elements of the char array to each other to test. I can get the first element of the array to find the second character easily, but to find the last acceptable value and decrement that, it doesn't find the next character in the array. So if the corrected/cleaned char array looks like:

['A']['B']['C']['D']['A']

ebx will go from 'A' -> 'B' but edi will not change from 'A' -> 'D'

Why will ebx change characters but edi only subtracts 1 from it's register value? What can I do to have edi change character value? Thanks!

C++ code: (just in case)

#include <iostream>
#include <cstring>
#include <sstream>

using namespace std;

extern"C"
{
char stringClean(char *, int);
char isPalindrome(char *, int);
}

int main()
{
int pal = 0;
const int SIZE = 30;
string candidate = "";
char strArray[SIZE] = { '\0' };

cout << "enter a string to be tested: ";
getline(cin, candidate);


int j = 0;
for (int i = 0; i < candidate.length(); i++)        //getting rid of garbage before entering into array
{
    if (candidate[i] <= 'Z' && candidate[i] >= 'A' || candidate[i] <= 'z' && candidate[i] >= 'a')
    {
        strArray[j] = candidate[i];
        j++;
    }
}

if (int pleaseWork = stringClean(strArray, SIZE) == 0)
    pal = isPalindrome(strArray, SIZE);

if (pal == 1)
    cout << "Your string is a palindrome!" << endl;
else
    cout << "Your string is NOT a palindrome!" << endl;

system("pause");
return 0;
}

masm code:

.686
.model flat
.code

_isPalindrome PROC ; named _test because C automatically prepends an underscode, it is needed to interoperate
    push ebp
    mov ebp,esp ; stack pointer to ebp

    mov ebx,[ebp+8] ; address of first array element
    mov ecx,[ebp+12] ; number of elements in array
    mov ebp,0
    mov edx,0
    mov eax,0
    push edi    ;save this
    push ebx    ;save this

    mov edi, ebx    ;make a copy of first element in array
    add edi, 29     ;move SIZE-1 (30 - 1 = 29) elements down to, HOPEFULLY, the last element in array

    mov bl, [ebx]
    mov dl, [edi]

    cmp dl, 0           ;checks if last element is null
    je nextElement      ;if null, find next
    jne Comparison      ;else, start comparing at Comparison:

nextElement:
    dec edi             ;finds next element
    mov dl, [edi]       ;move next element into lower edx
    cmp dl, 0           ;checks if new element is mull
    je nextElement      ;if null, find next
    jne Comparison      ;else, start comparing at Comparison:

Comparison:
    cmp bl,dl           ;compares first element and last REAL element
    je testNext         ;jump to textNext: for further testing

    mov eax,1           ;returns 1 (false) because the test failed
    jne allDone         ;jump to allDoneNo because it's not a palindrome

testNext:
    dec edi     ;finds last good element -1 --------THIS ISN'T DOING the right thing
    inc ebx             ;finds second element

    cmp ebx, edi        ;checks if elements are equal because that has tested all elements
    je allDone          

    ;mov bl,[ebx]       ;move incremented ebx into bl
    ;mov dl,[edi]       ;move decremented edi into dl
    jmp Comparison      ;compare newly acquired elements



allDone:
    xor eax, eax
    mov ebp, eax

    pop edi
    pop edx
    pop ebp
    ret
_isPalindrome ENDP

END 

Solution

  • I haven't tested your code, but looking at it I noticed some possible problems.

    Why will ebx change characters

    It seems that way, but it's not what you tried to reach. You commented out the lines reading the characters from memory/the array after the initial phase (see below). So in fact, you did change the character in EBX, but not the way you expected (and supposedly wanted). With INC EBX you increased the char-value from 'A'(65dec) to 'B'(66dec). That 'B' is also the second char of the string is merely a coincidence. Try changing the string from ABCDA to ARRCD or something and you'd still get a 'B' on the second round. So EBX does indeed change.

    ...
    ;mov bl,[ebx]       ;move incremented ebx into bl
    ;mov dl,[edi]       ;move decremented edi into dl
    jmp Comparison      ;compare newly acquired elements
    ...
    

    but edi only subtracts 1 from it's register value? What can I do to have edi change character value?

    Yes. That's what your code does and it's correct. Uncomment the above line containing [edi] and the char pointed at by EDI will be loaded into the lower byte of EDX = DL.

    The problem with your code is that you are using EBX both as a pointer and (char)value. Loading the next char into EBX will destroy the pointer and your programm is likely to crash with ACCESS_VIOLATION in the next iteration or show random behaviour which would be hard to debug.

    Separate pointers from values like you have done with EDI/EDX (EDI=pointer to char, EDX(DL)=char value.

    Another problem is: your code will only work for strings with an odd length.

    testNext:
        dec edi         ; !!!
        inc ebx         ; !!!
    cmp ebx, edi        ; checks if elements are equal because that has tested all elements
    je allDone  
    

    So you are increasing and decreasing the (should be) pointers and then comparing them. Now consider this case of an even-length-string:

      ABBA
      ^  ^   (EBX(first) and EDI(second))
    => dec both =>
      ABBA
       ^^    (EBX(first) and EDI(second))
    => dec both =>
      ABBA
       ^^    (EDI(first) and EBX(second))
    => dec both =>
      ABBA
      ^  ^   (EDI(first) and EBX(second))
    => dec both =>
      ABBA
     ^    ^  (EDI(first) and EBX(second)) 
    ... 
    

    => Problem! Won't terminate, condition EBX=EDI will never be met* Possible solution: Add an A(Above = Greater for unsigned values) to the jump

    ...
    cmp ebx, edi
    jae allDone