News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Code only works for 3 byte inputs.

Started by Rain Dog, January 24, 2005, 05:50:52 AM

Previous topic - Next topic

Rain Dog

This code is created to take input in the form of '5 5 +'

it works successfully up to the input '999 999 +'


as soon as i input a 4th digit, it fails.

What i gather is that my op1 variable is not being correctly assigned the string from the szMid function call.

Perhaps I am using the function incorrectly. I think the comments in the code should explain clearly enough what it is i am trying to do.


    .486                                    ; create 32 bit code
    .model flat, stdcall                    ; 32 bit memory model

include \masm32\include\windows.inc
    include \masm32\macros\macros.asm       ; MASM support macros

  ; -----------------------------------------------------------------
  ; include files that have MASM format prototypes for function calls
  ; -----------------------------------------------------------------
    include \masm32\include\masm32.inc
    include \masm32\include\kernel32.inc
  ; ------------------------------------------------
  ; Library files that have definitions for function
  ; exports and tested reliable prebuilt code.
  ; ------------------------------------------------
    includelib \masm32\lib\masm32.lib
    includelib \masm32\lib\kernel32.lib

    .data
    buf db 640h dup(0)  ;define array of 100 bytes for user input
    buflen dd 640h ;Define max length of buffer
    .code
   

start:

call main
print "Press any key to continue..."

;StdIn takes buffer:DWORD, buf length:DWORD
invoke StdIn, DWORD ptr[buf], buflen
    exit
   
main proc

LOCAL numBytes ;variable to store the number of bytes the token is
LOCAL op1
LOCAL op2
LOCAL operator
LOCAL result
;input() is a macro for getting input from std input
mov DWORD ptr[buf], input("Enter the string:")
invoke szLen, DWORD ptr[buf]
mov numBytes, eax

push numBytes ;push length of string
push DWORD ptr[buf] ;push address of string
push 20h ;push character we want to look for
call FindFirstOf
;ECX now contains index of the first ' '. Allocate new string

;we wish for length + 1
;bytes to store string.
inc ecx ;Incrememnt ecx (final length of substr)
mov [numBytes], ecx ;Assign ecx to numBytes
mov eax, alloc(numBytes) ;Allocate numBytes and assign address
;to variable eax

mov op1, eax ;assign eax to op1 so that op1 is now
;the address of the new byte array
;call szMid and pass in starting location and ending location
;szMid source:DWORD, dest:DWORD, startpos:DWORD, length:DWORD
sub numBytes, 1
;decrement numBytes, we only need the right
;number of bytes copied from the main input
invoke szMid, DWORD ptr[buf], ADDR [op1], 0, numBytes

;now we have zero terminated string residing in op1

;we should increment buf so that it points to next non-space char
inc numBytes
mov eax, numBytes
mov edx, DWORD ptr[buf]
add edx, eax
mov DWORD ptr[buf], edx

;Repeat procedure for next operand.
invoke szLen, DWORD ptr[buf]
mov numBytes, eax
push eax
push DWORD ptr[buf]
push 20h
call FindFirstOf
inc ecx ;increment ecx, it contains location of space
;which is also length of token

mov [numBytes], ecx
mov eax, alloc(numBytes) ;allocate array of ecx bytes, assign address to eax
mov op2, eax ;move eax to op2
sub numBytes, 1 ;decrease num bytes to copy to op2
invoke szMid, DWORD ptr[buf], ADDR op2, 0, numBytes

;we should increment buf so that it points to next non-space char
inc numBytes
mov eax, numBytes
mov edx, DWORD ptr[buf]
add edx, eax
mov DWORD ptr[buf], edx

;buf now contains our operator!
mov eax, DWORD ptr[buf]
mov operator, eax
;mov operator, DWORD ptr[buf]

;print to console representation of action
print ADDR op1
print operator
print ADDR op2
print chr$("=")


mov op1, sval(ADDR op1) ;convert operand 1 to integer
mov op2, sval(ADDR op2) ;do the same for operand 2

mov edx, DWORD ptr[operator]

mov al, BYTE ptr[edx] ;switch/case statement for operator
cmp al, 2BH
je addition
cmp al, 2DH
je subtraction
cmp al, 2AH
je multiply
cmp al, 2FH
je divide

;perform the requested action
addition:
mov eax, op1
mov ebx, op2
add eax, ebx
jmp display

subtraction:
mov eax, op1
mov ebx, op2
sub eax, ebx
jmp display

multiply:
mov eax, op1
mov ebx, op2
mul ebx
jmp display

divide:
cmp op2, 0
je dividebyzero
mov eax, op1
mov ebx, op2
div ebx
jmp display

dividebyzero:
print chr$("Divide by zero error")
print chr$(13, 0)
jmp return1

display:

mov result, eax

invoke dw2hex, result, ADDR buf

print ADDR buf
print chr$(13,10)
jmp return1



return1:
ret
main endp

;returns -1 if not found, otherwise returns  0 based location of character in
;ECX
FindFirstOf proc
;esp + 12 = character to look for
;esp + 16 = dword ptr to address of string to search
;esp + 20 = length of string

LOCAL strlength :DWORD
mov eax, DWORD ptr[esp+20] ;move string length to eax
mov strlength, eax ;move length of string in eax to strlegnth
mov ecx, 0 ;we will use ECX to store our index
mov     edx,DWORD PTR[esp+16] ;set edx to start of string
back:
cmp ecx, strlength ;test if we are at last char of string
je done ;if we are, we did not find character
mov     al,BYTE PTR[EDX + ecx] ;move char at edx to al
cmp     al,BYTE ptr[esp+12] ;esp is the character we want to look for.
    je     found ;if not same, go to start of loop
    inc ecx ;set index to the next position in str
    jmp back ;it was the same, so go to found
done:
    mov ecx, -1 ;not found
found:
add esp, 20 ;balance stack
ret
FindFirstOf endp

;Returns 1 in EAX if string is Hexadecimal
IsHex proc


ret

IsHex endp

end start





MichaelW

The input macro creates a new 128-byte buffer each time it is called, and it returns the offset address of that buffer in EAX. You might want to take a look at the source code in \masm32\macros\macros.asm

Also, the count value preceding the DUP operator specifies the number of array elements, so for an array of 100 bytes you should use something like:

buf db 100 dup(0)


eschew obfuscation

thomasantony

Hi,
    Just so you know

buf db 640h dup(0)  ;define array of 100 bytes for user input
    buflen dd 640h;Define max length of buffer

Doesn';t declare a 100 byte array as 640h = 1600 decimal . 100 in hex is 64h.

Thomas Antony :U
There are 10 types of people in the world. Those who understand binary and those who don't.


Programmer's Directory. Submit for free

Rain Dog

right. I had changed it to the larger value after thinking that it might have had something to do with the error, but it did not,

Rain Dog

still hoping for some help here.


THanks

MichaelW

This statement is storing the address of the input string in the first four bytes of a byte variable.

mov DWORD ptr[buf], input("Enter the string:")

It would be a little less confusing if you stored the address in a dword with a reasonable name.

Your FindFirstOf procedure is not handling the stack correctly. You cannot remove the parameters from the stack by adding to ESP within the procedure because doing so will remove the return address from the stack. MASM is adding:

push ebp
mov ebp,esp

To the start of your procedure, so at that point your stack, relative to ESP, contains:
[ESP+16] length of string
[ESP+12] address of string
[ESP+8] character to look for
[ESP+4] return address
[ESP+0] preserved EBP
Then after MASM subtracts 4 from ESP to make room for your local variable:
[ESP+20] length of string
[ESP+16] address of string
[ESP+12] character to look for
[ESP+8] return address
[ESP+4] preserved EBP
So obviously adding 20 to ESP cannot have the desired effect. Instead, you should change your return instruction to:

ret 12

Which will cause the processor to add 12 to ESP after the return.

Also, for some reason, if the return instruction does not have an operand MASM leaves out the LEAVE instruction that should precede the return instruction. I think this may be a side effect of using the LOCAL directive in a procedure with no declared parameters. At least until you get a handle on all of this, I think it would be easier to use declared parameters.

Beyond that, your FindFirstOf procedure appears to work OK, but there is some sort of problem further down in the program code.

eschew obfuscation

Rain Dog

I managed to finally get it to work and I am excited.


I realized that the alloc function creates an array, and then returns the address of that array. I then store that pointer into a DWORD variable, and it makes sense to me now.


ASM takes quite a different train of thought than programming with a high level language. My preferred language is C++ but for work i am forced to program T-SQL, C# and *gasp* vbscript so I am not exactly used to having to think like that especially with those languages that do everything for you. I had a friend say that programming vb was as simple as typing "Computer, i want you to add 3 numbers together and show me the result in a messagebox" and then deleting all the words that the compiler underlined in read.

Quite true with even .NET too.


Here is the source code that I used to generate a working program.



    .486                                    ; create 32 bit code
    .model flat, stdcall                    ; 32 bit memory model

include \masm32\include\windows.inc
    include \masm32\macros\macros.asm       ; MASM support macros

  ; -----------------------------------------------------------------
  ; include files that have MASM format prototypes for function calls
  ; -----------------------------------------------------------------
    include \masm32\include\masm32.inc
    include \masm32\include\kernel32.inc
  ; ------------------------------------------------
  ; Library files that have definitions for function
  ; exports and tested reliable prebuilt code.
  ; ------------------------------------------------
    includelib \masm32\lib\masm32.lib
    includelib \masm32\lib\kernel32.lib

    .data
    ;buf db 640h dup(0)  ;define array of 100 bytes for user input
    buflen dd 640h ;Define max length of buffer
    buf dd ?
    .code
   

start:

call main
print "Press any key to continue..."

;StdIn takes buffer:DWORD, buf length:DWORD
invoke StdIn, buf, buflen
    exit
   
main proc

LOCAL numBytes ;variable to store the number of bytes the token is
LOCAL op1
LOCAL op2
LOCAL operator
LOCAL result
;input() is a macro for getting input from std input
mov buf, input("Enter the string:")
invoke szLen, buf
mov numBytes, eax

push numBytes ;push length of string
push buf ;push address of string
push 20h ;push character we want to look for
call FindFirstOf
;ECX now contains index of the first ' '. Allocate new string

;we wish for length + 1
;bytes to store string.
inc ecx ;Incrememnt ecx (final length of substr)
mov [numBytes], ecx ;Assign ecx to numBytes
mov eax, alloc(numBytes) ;Allocate numBytes and assign address
;to variable eax

mov op1, eax ;assign eax to op1 so that op1 is now
;the address of the new byte array
;call szMid and pass in starting location and ending location
;szMid source:DWORD, dest:DWORD, startpos:DWORD, length:DWORD
sub numBytes, 1
;decrement numBytes, we only need the right
;number of bytes copied from the main input
invoke szMid, buf, op1, 0, numBytes

;now we have zero terminated string residing in op1

;we should increment buf so that it points to next non-space char
inc numBytes
mov eax, numBytes
mov edx, buf
add edx, eax
mov buf, edx

;Repeat procedure for next operand.
invoke szLen, buf
mov numBytes, eax
push eax
push buf
push 20h
call FindFirstOf
inc ecx ;increment ecx, it contains location of space
;which is also length of token

mov [numBytes], ecx
mov eax,alloc(numBytes) ;allocate array of ecx bytes, assign address to eax
mov op2, eax ;move eax to op2
sub numBytes, 1 ;decrease num bytes to copy to op2
invoke szMid, buf, op2, 0, numBytes

;we should increment buf so that it points to next non-space char
inc numBytes
mov eax, numBytes
mov edx, buf
add edx, eax
mov buf, edx

;buf now contains our operator!
mov eax, buf
mov operator, eax
;mov operator, DWORD ptr[buf]

;print to console representation of action
print op1
print operator
print op2
print chr$("=")


mov op1, sval(op1) ;convert operand 1 to integer
mov op2, sval(op2) ;do the same for operand 2

mov edx, DWORD ptr[operator]

mov al, BYTE ptr[edx] ;switch/case statement for operator
cmp al, 2BH
je addition
cmp al, 2DH
je subtraction
cmp al, 2AH
je multiply
cmp al, 2FH
je divide

;perform the requested action
addition:
mov eax, op1
mov ebx, op2
add eax, ebx
jmp display

subtraction:
mov eax, op1
mov ebx, op2
sub eax, ebx
jmp display

multiply:
mov eax, op1
mov ebx, op2
mul ebx
jmp display

divide:
cmp op2, 0
je dividebyzero
mov eax, op1
mov ebx, op2
div ebx
jmp display

dividebyzero:
print chr$("Divide by zero error")
print chr$(13, 0)
jmp return1

display:

mov result, eax

invoke dw2hex, result, buf

print buf
print chr$(13,10)
jmp return1



return1:
ret
main endp

;returns -1 if not found, otherwise returns  0 based location of character in
;ECX
FindFirstOf proc
;esp + 12 = character to look for
;esp + 16 = dword ptr to address of string to search
;esp + 20 = length of string

LOCAL strlength :DWORD
mov eax, DWORD ptr[esp+20] ;move string length to eax
mov strlength, eax ;move length of string in eax to strlegnth
mov ecx, 0 ;we will use ECX to store our index
mov     edx,DWORD PTR[esp+16] ;set edx to start of string
back:
cmp ecx, strlength ;test if we are at last char of string
je done ;if we are, we did not find character
mov     al,BYTE PTR[EDX + ecx] ;move char at edx to al
cmp     al,BYTE ptr[esp+12] ;esp is the character we want to look for.
    je     found ;if not same, go to start of loop
    inc ecx ;set index to the next position in str
    jmp back ;it was the same, so go to found
done:
    mov ecx, -1 ;not found
found:
ret 12 ;balance stack
FindFirstOf endp

;Returns 1 in EAX if string is Hexadecimal
IsHex proc


ret

IsHex endp

end start