News:

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

First attempt at no .While or .IF

Started by joemc, March 30, 2010, 07:33:14 AM

Previous topic - Next topic

joemc

I wanted to try to write something without using HL sytanx
below is a short section of code that parses the command line.  Any feedback of how horrible it is, or general pointers please.  It is working code, i tried to make code readable and as short as possible since it is not time critical

I need the location of 2nd C and the length to the last t:
"C:\app.exe" "C:\test.txt"
This is the command line when test.txt is dragged onto app.exe  (edit: WRONG)


so i came up with

  LOCAL FileNameBegin:DWORD
  LOCAL FileNameLength:DWORD
 
 
  ;------------------------------------------------------------------------------------------------                         
  ;  Section - Parse Command Line
  ;  At end of section FileNameBegin will be a pointer to the begining of the filename
  ;    and FileNameLength will contain it's length. eax,ecx,edx are trashed
  ; ecx = counter for quotes
  ; eax = pointer to current character in string
  ; edx = current character
  ;------------------------------------------------------------------------------------------------                         
 
  invoke GetCommandLine   
  xor ecx,ecx             
  mov FileNameLength,ecx 
  jmp pcBegin           
  pcError:     
    print "Command line not valid. Fully qualified name in quotes is required. ",13,10
    ret
  pcTick:
    inc ecx
    cmp ecx,4
    jne @f
      sub eax,FileNameBegin
      mov FileNameLength,eax
      JMP pcEnd     
    @@:
    inc eax                   ; Advance pointer
    cmp ecx, 3
    jne pcBegin
      mov FileNameBegin,eax
  pcBegin:
    movzx edx,byte ptr [eax]  ; edx == char pointed to by eax
    test edx,edx           
    jz pcError                ; check for null character
    cmp edx,'"'   
    je pcTick                 ; check for quote
    inc eax                   ; Advance pointer
    jmp pcBegin
  pcEnd:

  ; end Section
  ;------------------------------------------------------------------------------------------------



edit:
Above is incorrect,  the quotes are only there if a space is involved, and i was testing from desktop. Basically code is pointless on it's own. But code does work if both test.txt and app.exe are on desktop on XP.  Advice on it's structure is more important to me than it's usefulness.

Suppose you are given a null terminated string that contains two quoted sections and you want the location of the second without the quotes and it's length:
"String One is not that important" "But i need this one please!"

edit again:
:)  i suppose instead of comparing to 4 and than comparing to 3 i could check if less than 3 than check if equal to 4.
or start at 4 and count down to zero might make it simpler

Astro

#1
Hi,

"String One is not that important" "But i need this one please!"

include \masm32\include\masm32rt.inc

.data
    string BYTE 34,"String One is not that important",34,32,34,"But i need this one please!",34,0
    l DWORD 0
    startpos DWORD 0

.code

start:

    push ebx ; save ebx

    xor ebx,ebx ; how many quotes did we encounter?
    xor ecx,ecx ; position in string
    xor edx,edx ; position of start of second string

@@:
    mov edx,[offset string+ecx*1]
    and edx,0FFh
    inc ecx

    cmp dl,34 ; is it a quote?
    jne @B ; not a quote

    inc ebx

    cmp ebx,3 ; is it the 3rd quote? If so we are at the second string
    je SOS
    jmp @B

SOS:
    sub ecx,1
    push ecx ; store the start position
    add ecx,1

@@:
    mov edx,[offset string+ecx*1]
    and edx,0FFh
    inc ecx

    cmp dl,0 ; are we at the end yet?
    je EOS
    jne @B

EOS:
    sub ecx,3 ; remove the length of the null, the last quote, and the fact we are +1 after the end of the string
    pop edx
    sub ecx,edx ; subtract edx (start pos) from ecx (end pos) to get the length. Result in ecx

    mov l,ecx
    mov startpos,edx

    print "The length of the string is "
    print str$(l),13,10
    print "The start of the second string is at character "
    print str$(startpos),13,10

    pop ebx ; restore ebx

    ret

end start


Tested.

Best regards,
Robin.

cobold

Using the library functions GetCL and szLen you could solve it like this:

INCLUDE \masm32\include\masm32rt.inc

.CODE
start:
    print chr$(13,10)
   
    call ParseCommandLine
    cmp eax, TRUE
    je _okay
_not_ok:
    print "failure"
    invoke ExitProcess,1
_okay:
    print "okay"
    invoke ExitProcess,0

   
; -------------------------------------------------------------------------
; ParseCommandLine - PROC
;
;   IN:     ----
;   OUT:    eax == FALSE    no parameter specified
;           eax == TRUE     valid parameter
; or if you like
;           mov eax, FileNameBegin to return the address of the parameter   
; -------------------------------------------------------------------------

ParseCommandLine PROC uses ecx edx

    LOCAL szPara1[MAX_PATH] :BYTE   ; value of parameter1
    LOCAL FileNameBegin     :DWORD  ; pointer to parameter1
    LOCAL FileNameLength    :DWORD
; -------------------------------------------------------------------------
; first, lets check if any parameter is given
; -------------------------------------------------------------------------
    invoke GetCL, 1, ADDR szPara1   ; get 1st parameter
    cmp eax, 1                      ; eax == 1 if 1st parameter exists
    jne _nopara1
   
; -------------------------------------------------------------------------
; Does parameter begin with a quote?
; -------------------------------------------------------------------------
;
; no need to check this, because GETCL strips the quotes
; if only one quote is given it will return FALSE


; -------------------------------------------------------------------------
; if parameter given, get its length and get its pointer (FileNameBegin)
; -------------------------------------------------------------------------
    lea eax,szPara1
    mov FileNameBegin, eax

    invoke szLen, ADDR szPara1
    mov FileNameLength, eax
   
    print "Parameter: "
    print FileNameBegin, " Length: "
    print str$(FileNameLength), " - Address: "
    print hex$(FileNameBegin),13,10

    mov eax, TRUE   ; alternatively: mov eax, FileNameBegin
                    ; to return address of FileName to caller
    jmp _exit

_wrongpara1:
    print "Command line not valid. Fully qualified name in quotes is required. ",13,10
    mov eax, FALSE
    jmp _exit

_nopara1:
    print "No parameter specified.", 13,10
    mov eax,FALSE
   
_exit:
    ret
ParseCommandLine ENDP

end start

hutch--

Joe,

Usually the trick to do this type of work where you are working with double quotes is to have 2 loops, the first scans for the opening quote, if it finds it in branches to a second loop that scans for the closing quote. If the second loop find a zero before the closing quote you exit on an error. With a bit more effort you can do the same thing from the first loop with single quotes then branch to a different loop that handles the single closing quote. In either loop that you branch to you can if you want write the contents between the quotes to a seperate memory buffer for later use.

This technique gtives you alternate quotes so you can have "This is 'a' test" or 'This is "a" test'.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

joemc

So maybe more like this :)



include \masm32\include\masm32rt.inc

NextQuote MACRO
  LOCAL startloop,outloop
 
  startloop:                 
    movzx edx,byte ptr [eax]  ; edx = char pointed to by eax
    inc eax                     ; advance pointer
    cmp edx,22h               
    je outloop                ; jump out if "
    test edx,edx
    jnz startloop             ; jump start if not null
    xor eax,eax               ; make eax null if char null
   outloop:
   
   
ENDM
 
.data
TheString db 22h,"String One is not that important",22h,20h,22h,"But i need this one please!",22h,0

.code
start:

call main
inkey
exit

main proc
  LOCAL quoted:DWORD
  LOCAL command:LPSTR
  LOCAL pStart:DWORD
  LOCAL nLength:DWORD
 
  mov eax,OFFSET TheString

  NextQuote
  test eax,eax
  jz Error

  NextQuote
  test eax,eax
  jz Error
 
  NextQuote
  test eax,eax
  jz Error
 
  mov pStart,eax
 
  NextQuote
  test eax,eax
  jz Error
  sub eax,pStart
  dec eax
  mov nLength,eax
 
 
  print pStart
  print str$(nLength)
  ret
 
  Error:
   
    print "String malformed",13,10
    ret 
main endp

end start
 

joemc


sinsi

Light travels faster than sound, that's why some people seem bright until you hear them.

hutch--

Joe,

Have a play with this one. Its a test algo that seems to be working properly.


IF 0  ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
                      Build this template with "CONSOLE ASSEMBLE AND LINK"
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    include \masm32\include\masm32rt.inc

    get_quoted PROTO :DWORD,:DWORD,:DWORD


    .data
      txt1 db 34,"Test 1",34,"xxx",34,"Test 2",34,"yyy",34,"Test 3",34,"zzz",34,"Test 4",34,"???",0
      txt2 db 64 dup (0)

    .code

start:
   
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    call main
    exit

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

main proc

    LOCAL cloc  :DWORD                  ; current location pointer

    mov cloc, 0                         ; start at offset 0 in source

  lbl0:
    mov cloc, rv(get_quoted,ADDR txt1,ADDR txt2,cloc)
    .if cloc == 0
      print "finished",13,10
      jmp lbl1
    .else
      print OFFSET txt2,13,10
    .endif
    inkey
    jmp lbl0

  lbl1:

    ret

main endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

get_quoted proc src:DWORD,dst:DWORD,cloc:DWORD

    push esi
    push edi

    xor edx, edx                    ; zero flag
    mov esi, src
    mov edi, dst
    add esi, cloc                   ; add current offset to ESI
    mov ecx, esi                    ; store start in ECX
    sub esi, 1

  scanloop:
    add esi, 1
    movzx eax, BYTE PTR [esi]
    test eax, eax
    jz theend
    cmp eax, 34                     ; test for double quote
    je dqlp
    jmp scanloop

  dqlp:
    mov edx, 1                      ; use EDX as flag
    mov BYTE PTR [edi], al          ; write the 1st quote
    add edi, 1
  @@:
    add esi, 1
    movzx eax, BYTE PTR [esi]
    mov [edi], al
    add edi, 1
    test eax, eax
    jz dqerr
    cmp al, 34
    jne @B
    jmp outlp

  sqlp:

  outlp:
    mov BYTE PTR [edi], 0           ; terminate buffer
    test edx, edx
    jz theend
    sub esi, ecx
    add cloc, esi
    mov eax, cloc
    add eax, 1
    jmp quit

  dqerr:
    mov eax, -1
    jmp quit

  theend:
    xor eax, eax

  quit:
    pop edi
    pop esi

    ret

get_quoted endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

end start
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

joemc

well i went ahead and wrote a CommandLineToArgvA.  The only two links i could find for a similar function were:
http://www2.alter.org.ua/en/docs/win/args/ which does a strange calculation for size. I am not sure if i understand it but does not seem correct.
and
lost url - which allocated memory during the loop for each argument


It its only version 1... It needs to be completely rewriten. It does not preserve registers and i want to go about it differently. But does everything MS CommandLineToArgvW does other than escape characters which i do not desire.
I also may only compare to space for whitespace. i also did /r /n and /t because that first link at the top did.


include \masm32\include\masm32rt.inc

.code
start:

call main
inkey
exit

CommandLineToArgvA proc lpCmdLine:DWORD, pNumArgs:DWORD
 
  xor ecx,ecx                         ; Character Needed Counter
  xor edx,edx                         ; Boolean state of in a word
  xor esi,esi                         ; Number of Arguments
  mov ebx,lpCmdLine                   ; Pointer to string
 
  jmp MainLoop3
  QuoteLoop1:
    inc esi
    xor edx,edx
    inc ebx
    push ebx
    jmp @f
  QuoteLoop2:
    inc ebx
    @@:
    movzx eax, byte ptr [ebx]
    cmp eax, 22h
    je @f
    inc ecx
    jmp QuoteLoop2
    @@:
    push ebx
    jmp MainLoop2
  MainLoop1:
    inc ecx
  MainLoop2:
    inc ebx
  MainLoop3:
    movzx eax, byte ptr [ebx]
    cmp eax, 22h
    je QuoteLoop1
    test edx,edx
    jz MainLoop4
    test eax,eax
    jz MainLoop5
    cmp eax, 20h
    je @F
    cmp eax, 09h   
    je @F
    cmp eax, 0Dh 
    je @F
    cmp eax, 0Ah
    je @F
    jmp MainLoop1   
    @@:
    xor edx,edx
    push  ebx
    jmp MainLoop2
  MainLoop4:
    test eax,eax
    jz MainLoop6
    cmp eax, 20h
    je MainLoop2
    cmp eax, 09h
    je MainLoop2
    cmp eax, 0Dh
    je MainLoop2
    cmp eax, 0Ah
    je MainLoop2
    inc esi
    inc edx
    push ebx
    jmp MainLoop1
  MainLoop5:
    push ebx
  MainLoop6:
 
  mov eax,pNumArgs
  mov [eax],esi
 
  mov eax,5             ; sizeof pointer + null terminate
  imul eax,esi          ; foreach argument
  add ecx, eax          ; added to size
 
  cmp ecx,0
  jg @f
  xor eax,eax
  ret         ; negative or zero size of memory
  @@:
 
  push ecx
  push 0
  call LocalAlloc
 
  test eax,eax
  jnz @f
  ret         ; Problem allocating memory
  @@:

  ; eax = new memory
  ; ebx = data pointer in new memory
  ; ecx
  ; edx
 
  mov ebx,esi      ; the amount of arguments
  shl ebx,2        ; multiplied by 4
  add ebx,eax      ; plus the new memory location
 
  ;preferably in the mainloop  need to push
  ;an extra dword for the destination and
  ;push the size instead of the end address
  ;and it will not require poping just
  ;modifying the destination address and calling
 
  mov eax,ebx ; make new memory pointer same as data pointer and work backwards
 
  DataLoop1:
  pop edx       ; End Address
  pop ecx       ; Start Address
  sub edx,ecx   ; Size of
 
  sub eax,4      ; write index
  mov [eax],ebx
 
  push esi
 
  invoke MemCopy,ecx,ebx,edx 
  pop esi
  dec esi
  add ebx,edx
  inc ebx
  mov [ebx],byte ptr 0;null terminate
  inc ebx
  test esi,esi
  jz @f
  jmp DataLoop1
  @@:
           
  ret
CommandLineToArgvA endp


main proc
  LOCAL argc:DWORD
  LOCAL ppargv:DWORD
 
  invoke GetCommandLine
  lea ecx,argc
  invoke CommandLineToArgvA,eax,ecx
  mov ppargv,eax
 
  print "Arguments : "
  print str$(argc),13,10
 
  mov ecx,0
  mov eax,ppargv
  .while ecx != argc
 
    pushad
      print str$(ecx)
      print " : "
    popad
    pushad
      print [eax+ecx*4],13,10
      print " ",13,10
    popad
 
  inc ecx
  .endw
 
  ret
main endp

end start