### Author Topic: StrToInt Function  (Read 6224 times)

#### RagingGrim

• Jr. Member
• Posts: 28
##### StrToInt Function
« on: January 07, 2015, 05:36:22 PM »
I'm back and on vacation!

I'm using my phone as a hotspot and we really don't have the best connectivity so I rushed my post in notepad - sorry if I messed up the tags or anything
I wrote a function to convert a string ( ACII characters ) to an integer ( unsigned integer... Although using neg for signed is easy enough :p )
At school we use strtoint ( delphi ) a lot and I thought it'd be fun to get a look at how it's done. Due to the lack of internet connection I kinda guessed my way through this
Any advise / ideas? ^^

Quick note though , this isn't for a project or homework assignment ( I'm afraid our education system is far below teaching actual programming ^^ ) - we had to create a geocash program for our project this year which made up 25 % of our final score XD All it had to do was read from a text file , some database manipulation and a few images which in delphi really isn't hard at all. I'm doing the assembly purely because I like it and I think the experience is good ^^

Code: [Select]
`BITS 32extern __cprintfextern __getchglobal mainsection .dataintFormat db "%d",0AStr db "12334",0section .textmain:mov ebx,AStrcall strToInt ;"1"=00110001 ;1=1 -> 0000000001push eaxpush intFormatcall __cprintfcall __getch;eax contains the strlength and will contain the resulting int;ebx contains the stringstrToInt: call strlenmov edi,10 ;Mul Valuemov edx,eax ;save strlength in edxxor ecx,ecx ;countermov eax,[ebx+0] ;first char in ebxand eax,0b00001111 ;Convert From ASCII to INT;strToInt_Loop:inc ecxCMP ecx,edxJE strToInt_Donemov esi,[ebx+ecx]and esi,0b00001111push edx ;save value to stackmul edipop edx ;load value from stackadd eax,esiJMP strToInt_LoopstrToInt_Done:RETstrlen:mov eax,0strlen_loop:inc eaxCMP byte [eax + ebx],0JNE strlen_loopRET`
I was also wondering if my code is "spaghetti code" ; I mean do the jumps make sense? I tried to use descriptive labels ^^

Thanks in advance
« Last Edit: January 07, 2015, 05:38:55 PM by RagingGrim »

#### Frank Kotler

• NASM Developer
• Hero Member
• Posts: 2667
• Country:
##### Re: StrToInt Function
« Reply #1 on: January 17, 2015, 07:28:52 AM »
Hi RagingGrim,

Sorry for the delayed reply. I've been having a tough time getting "in the mood". Getting way behind in my personal correspondence, too (as you know). I'll try to get caught up...

Your meaningful labels are great! Your jumps are more or less as they need to be, but I wonder "how do we end?". After printing the result and waiting for a key, you appear to "fall through" into your StrToInt subroutine again. When this again gets to "ret" it's going to try to "ret" to your format string (which you probably should have removed from the stack?). Doesn't it crash? (you can tell I haven't tried this yet - still planning to...)

I'm also concerned that your strlen routine is going to fail if it's passed a zero-length string. You don't, so it isn't a problem, but I think that could be more robust. I don't actually use "strlen" when converting a string to an integer. I just scan down the string looking for a decimal digit, and stop on anythng that isn't - a terminating zero, a terminating linefeed (which Linux likes to give us), or any non-digit character.

Code: [Select]
`...    push buffer    call atoi    add esp, 4 ; "remove" parameter from stack    mov [number], eax ; save the result...;--------------------atoi:    push ebx        mov edx, [esp + 8]  ; pointer to string    xor ebx, ebx ; assume not negative        cmp byte [edx], '-'    jnz notneg    inc ebx ; indicate negative    inc edx ; move past the '-'notneg:    xor eax, eax        ; clear "result".top:    movzx ecx, byte [edx]    inc edx    cmp ecx, byte '0'    jb .done    cmp ecx, byte '9'    ja .done        ; we have a valid character - multiply    ; result-so-far by 10, subtract '0'    ; from the character to convert it to    ; a number, and add it to result.        lea eax, [eax + eax * 4]    lea eax, [eax * 2 + ecx - '0']    jmp short .top.done:    test ebx, ebx    jz notminus    neg eaxnotminus:    pop ebx    ret;------------------------`
This isn't so great, either. I think doing the arithmetic with the two "lea"s is cute, but "lea" doesn't touch flags so there's no way I can check for overflow. Doing it the way you do, you could check carry flag (for unsigned) or overflow flag (for signed) after  "mul" and "add". You don't, but you could. You don't check for a non-digit character... but you could.

If we were to detect an overflow or a non digit character (might want to skip leading spaces, and '-', maybe '+') what would we do? Can't return -1 - that could be a valid result. Set the carry flag maybe - and check it after each call? Doesn't play nicely with C (if we care). Best thing might be to pass, as a parameter, an address to put "status" or "error" - we could distinguish between overflow and "bad character" and instruct  the user accordingly. Easier to ignore it. I'm curious what Delphi does in these situations.

(on another topic - in case I don't get to it - "port" is a word - big-endian)

Best,
Frank

#### RagingGrim

• Jr. Member
• Posts: 28
##### Re: StrToInt Function
« Reply #2 on: January 17, 2015, 11:48:14 AM »
I'm still working through your reply XD as for falling into the strtoint function again the file i was posting from is just a temporary text file i use for creating and saving functions so I apologise for not exiting properly ^^
I'll definitely look at the string functions again because I'm going to need them pretty soon

We have this contest coming up that's hosted by our regional municapility ( regeering -> Usually my afrikaans to english is pretty good but I'm not sure about that particular word ) and I thought since we're allowed to submit practically anything I'd do something in nasm. The olympiades we have are all more math based and we're only allowed to use java , delphi and python ( not that I have anything against the languages it's just when I started with C and was introduced to memory allocation and pointers it just blew my mind on so many levels ). I was thinking along the lines of a RAT (remote access tool) although in this scenario I have a server and a few clients connecting to it. The server is sends commands to the clients which in turn execute something and return a value to the server.

A few nice and fun functions to implement that I thought of while my teacher was discussing exam papers XD
1.) Tracking the mouse movement to determine whether a user is active on the station or not ( got the idea from malware )
2.) Disabling/Enabling the shutdown of certain workstations ( using the registry )
3.) Process monitor that kills any processes that are in a blacklist
4.) Disabling/Enabling the network ( Although this seems impractical because obviously i'd lose connection to the client :/ )
Change that to network functions XD
5.) Some basic info about a client.
6.) A scripting engine for the client.

Then it hit me that if I made the server and had let's say ten clients connected to it I'd need 10 sockets for each client and well it'd slow down the program . I'm not THAT new to socket programming so I immediately thought of using threading to solve the problem. The idea was that I'd spawn one thread for every client connected. This is where things started to get interesting.

I started writing on the project yesterday and already managed to implement a server which handles a single connection and prints whatever is sent to it . Today I'm looking into the winapi's heaprealloc , heapalloc and heapfree functions aswell as createthread. I'm not familiar with any of the heap functions but I think I get them . As for createthread ... Obviously in delphi the arguments are exactly the same similar my only problem is that I can't figure out where my address is that I pass. Being new to assembly and all. I thought it might have something to do with the esi,ebp and esp register but I'm still not certain. I think I'll solve that in an hour or so though ^^

This is what I have thusfar!

Code: [Select]
`;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>;THIS IS AN EXAMPLE TO HANDLE A SINGLE CLIENT ONLY;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<BITS 32extern _WSAStartupextern _socketextern _bindextern _listenextern _HeapReAllocextern _GetProcessHeapextern _HeapAllocextern _HeapFreeextern _acceptextern _recvextern _ExitProcessextern __cprintfextern _CreateThreadextern _bindextern _ExitWindowsExextern _WSAGetLastErrorextern __getchextern _htonsglobal mainsection .bssWSADATA resb 400 ;Checked in Csockaddr_inA resb 16 ;Checked in CrcvBuffer resb 200clientSockets resd 1section .data;ERRORSerrFormat db "ERRORCODE : %d",0ah,0;ERRORS END;DWORDcounterGeneral dd 0counterSocket dd 0ProcessHeap dd 0socketServer dd 0;DWORD END;BYTESstrFormat db "%s",0strConnected db "Client Connected!",0ah,0strShutdown db "Shutdown",0;BYTES ENDsection .textmain:call _GetProcessHeapmov dword [ProcessHeap],eax;1.) Init WSApush dword WSADATApush 514 ;MakeWord(2,2)call _WSAStartupCMP eax,byte 0JNE lblErrExit ;2.) Create A Socketpush 6 ; TCPpush 1 ; SOCK_STREAMpush 2 ; AF_INETcall _socketcmp eax,-1 ;SOCK_ERRORJE lblErrExitmov dword [socketServer],eax;3.) Bind The Socketmov word [sockaddr_inA+0],2 ;AF_INETpush 968call _htonsmov dword [sockaddr_inA+2],eaxmov dword [sockaddr_inA+6],0x0 ;ALL IP'spush 16push sockaddr_inApush dword [socketServer]call _bindCMP eax,0JNE lblErrExit;4.) Listen For A Connectionpush 0push dword [socketServer]call _listenCMP eax,0JNE lblErrExit;5.1) Spawn A Thread;5.2.) Accept A Connectionpush 0push 0push 0push lblAcceptpush 9push 0call _CreateThread;5.3) Create a dynamic array for clientSockets;LPVOID WINAPI HeapAlloc(  _In_  HANDLE hHeap,  _In_  DWORD dwFlags,  _In_  SIZE_T dwBytespush 1push 0push dword [ProcessHeap]call _HeapAllocCMP eax,0JE lblErrExitlblAccept:push 0push 0push dword [socketServer]call _acceptcmp eax,-1 JE lblErrExitmov edx,dword [Counter]add edx,4mov dword [clientSockets+edx],eax ;Save Client Handlepush 0push 0push 0push lblRecvpush 9push 0call _CreateThreadpush strConnectedpush strFormatcall __cprintfadd esp , 8JMP lblAccept;6.) Recv datalblRecv:push 0push 255push rcvBuffermov edx,dword [Counter]push dword [socketClient+edx]call _recvcmp eax,0JE lblErrExitcmp eax,-1JE lblErrExitpush rcvBufferpush strFormatcall __cprintfmov ebx, rcvBuffermov ecx, strShutdowncall strCmpcmp eax,-1JE lblShutdownadd esp,8mov dword [rcvBuffer],""JMP lblRecvlblErrExit:call _WSAGetLastErrorpush eaxpush errFormatcall __cprintfadd esp , 8call __getchcall _ExitProcessstrCmp: ;Compare ebx to ecx;>>Setup for the loopmov edx,-1;We Want To Start By Comparing AStr[0] to BStr[0]mov eax,-1strCmp_Loop:inc edx ;Increment Indexmov ah,[ebx+edx]CMP ah,[ecx+edx]JNE strCmp_NEQUCMP ah,0JE strCmp_DoneJMP strCmp_LoopstrCmp_NEQU:mov eax,edxstrCmp_Done:RETlblShutdown:push 0push 0x00000001 ;Shutdowncall _ExitWindowsEx`

I'll take a short break and quickly have a look see at what delphi does with the strtoint function. Great idea on setting the carry flag :/ I felt a little lost when you said that bit about the negative 1. What do you mean it doesn't play well with C though? ^^

I also thought I'd post delphi's version of it here if you'd like to have a look ^^
Code: [Select]
`function _ValLong(const s: string; var code: Integer): Longint;{\$IFDEF PUREPASCAL}var  I, Len, Digit: Integer;  Negative, Hex: Boolean;begin  // U-OK  I := 1;  code := -1;  Result := 0;  Negative := False;  Hex := False;  Len := Length(s);  while (I <= Len) and (s[I] = ' ') do    Inc(I);  if I > Len then    Exit;  case s[I] of    '\$',    'x',    'X':      begin        if I = Len then        begin          Code := I + 2; // Emulate Win32 _ValLong behaviour          Exit;        end;        Hex := True;        Inc(I);      end;    '0':      begin        Hex := (Len > I) and ((s[I+1] = 'X') or (s[I+1] = 'x'));        if Hex then          Inc(I, 2);      end;    '-':      begin        if I = Len then        begin          Code := I + 1; // Emulate Win32 _ValLong behaviour          Exit;        end;        Negative := True;        Inc(I);      end;    '+':      begin        if I = Len then        begin          Code := I + 1; // Emulate Win32 _ValLong behaviour          Exit;        end;        Inc(I);      end;  end;  if Hex then    while I <= Len do    begin      // check for overflow      if Result > (High(Result) shr 3) then      begin        code := I;        Exit;      end;      case s[I] of        '0'..'9': Result := Result * 16 + Ord(s[I]) - Ord('0');        'a'..'f': Result := Result * 16 + Ord(s[I]) - Ord('a') + 10;        'A'..'F': Result := Result * 16 + Ord(s[I]) - Ord('A') + 10;      else        code := I;        Exit;      end;      Inc(I);    end  else    while I <= Len do    begin      // check for overflow      if Result > (High(Result) div 10) then      begin        code := I;        Exit;      end;      Digit := Ord(s[I]) - Ord('0');      if (Digit < 0) or (Digit > 9) then begin         Code := I;         Exit;      end;      Result := Result * 10 + Ord(s[I]) - Ord('0');      Inc(I);    end;  if Negative then    Result := -Result;  code := 0;end;{\$ELSE !PUREPASCAL}asm{       FUNCTION _ValLong( s: string; VAR code: Integer ) : Longint;        }{     ->EAX     Pointer to string       }{       EDX     Pointer to code result  }{     <-EAX     Result                  }        PUSH    EBX        PUSH    ESI        PUSH    EDI        MOV     ESI,EAX        PUSH    EAX             { save for the error case       }        TEST    EAX,EAX        JE      @@empty        XOR     EAX,EAX        XOR     EBX,EBX        MOV     EDI,07FFFFFFFH / 10     { limit }@@blankLoop:        MOV     BX,[ESI]        ADD     ESI, 2        CMP     BX,' '        JE      @@blankLoop@@endBlanks:        MOV     CH,0        CMP     BX,'-'        JE      @@minus        CMP     BX,'+'        JE      @@plus@@checkDollar:        CMP     BX,'\$'        JE      @@dollar        CMP     BX, 'x'        JE      @@dollar        CMP     BX, 'X'        JE      @@dollar        CMP     BX, '0'        JNE     @@firstDigit        MOV     BX, [ESI]        ADD     ESI, 2        CMP     BX, 'x'        JE      @@dollar        CMP     BX, 'X'        JE      @@dollar        TEST    BX, BX        JE      @@endDigits        JMP     @@digLoop@@firstDigit:        TEST    BX,BX        JE      @@error@@digLoop:        SUB     BX,'0'        CMP     BX,9        JA      @@error        CMP     EAX,EDI         { value > limit ?       }        JA      @@overFlow        LEA     EAX,[EAX+EAX*4]        ADD     EAX,EAX        ADD     EAX,EBX         { fortunately, we can't have a carry    }        MOV     BX,[ESI]        ADD     ESI, 2        TEST    BX,BX        JNE     @@digLoop@@endDigits:        DEC     CH        JE      @@negate        TEST    EAX,EAX        JGE     @@successExit        JMP     @@overFlow@@empty:        ADD     ESI, 2        JMP     @@error@@negate:        NEG     EAX        JLE     @@successExit        JS      @@successExit           { to handle 2**31 correctly, where the negate overflows }@@error:@@overFlow:        POP     EBX        SUB     ESI,EBX        JMP     @@exit@@minus:        INC     CH@@plus:        MOV     BX,[ESI]        ADD     ESI, 2        JMP     @@checkDollar@@dollar:        MOV     EDI,0FFFFFFFH        MOV     BX,[ESI]        ADD     ESI, 2        TEST    BX,BX        JZ      @@empty@@hDigLoop:        CMP     BX,'a'        JB      @@upper        SUB     BX,'a' - 'A'@@upper:        SUB     BX,'0'        CMP     BX,9        JBE     @@digOk        SUB     BX,'A' - '0'        CMP     BX,5        JA      @@error        ADD     BX,10@@digOk:        CMP     EAX,EDI        JA      @@overFlow        SHL     EAX,4        ADD     EAX,EBX        MOV     BX,[ESI]        ADD     ESI, 2        TEST    BX,BX        JNE     @@hDigLoop        DEC     CH        JNE     @@successExit        NEG     EAX@@successExit:        POP     ECX                     { saved copy of string pointer  }        XOR     ESI,ESI         { signal no error to caller     }@@exit:        SHR     ESI, 1        MOV     [EDX],ESI        POP     EDI        POP     ESI        POP     EBXend;`
The strtoint function calls Val which is short for Validate ^^
And please no don't apologise for late replies or anything like that, you have absolutely no obligation to help people out here ( unless it's like a moral thing being the developer - I wouldn't know because I've never done anything that huge :p - ).

I think it's really great that you do though not a lot of people would go to this amount of trouble ^^
Oh and please call me Connor :p
« Last Edit: January 17, 2015, 11:57:20 AM by RagingGrim »