1

I want to call a function that will perform upper to lower case conversion to a user typed string, preserving the especial characters. This part works, but only for the first 4 characters, everything after that just gets truncated. I believe it is because I have defined the parameters as DWORD:

I have tried using PAGE, PARA and BYTE. The first two don't work and with byte says type missmatch.

upperToLower proc, source:dword, auxtarget:dword
  mov eax, source       ;Point to string
  mov ebx, auxtarget     ; point to destination
  L1:
  mov dl, [eax]            ; Get a character from buffer
    cmp byte ptr [eax], 0                  ; End of string? (not counters)
    je printString             ; if true, jump to printString
    cmp dl, 65                 ; 65 == 'A'
    jl notUpper                ; if less, it's not uppercase
    cmp dl, 90                 ; 90 == 'Z'
    jg notUpper                ; if greater, it's not uppercase
    xor dl, 100000b            ; XOR to change upper to lower
    mov [ebx], dl      ; add char to target
    inc eax                    ; Move counter up
    inc ebx                    ; move counter up
    jmp L1                     ; loop

    notUpper:                  ; not uppercase
    mov [ebx], dl      ; copy the letter
    inc eax            ;next letter
    inc ebx
    jmp L1

    printString:
    invoke WriteConsoleA,   consoleOutHandle, auxtarget, sizeof auxtarget, bytesWritten,    0
    ret
upperToLower endp

The PROTO:

  upperToLower PROTO,
    source: dword,
    auxtarget: dword

Invoke:

    invoke upperToLower, offset buffer, offset target

The buffer parameter is: buffer db 128 DUP(?)

How can I get printed the whole string, and not just the first 4 characters?

Wolfeius
  • 303
  • 2
  • 14

1 Answers1

2

Why are only 4 characters being printed? You write the string to the console with:

invoke WriteConsoleA,   consoleOutHandle, auxtarget, sizeof auxtarget, bytesWritten,    0

The sizeof auxtarget parameter is the size of auxtarget which is a DWORD (4 bytes) thus you are asking to only print 4 bytes. You need to pass the length of the string. You can easily do so by taking the ending address in EAX and subtracting the source pointer from it. The result would be the length of the string you traversed.

Modify the code to be:

printString:
sub eax, source
invoke WriteConsoleA,   consoleOutHandle, auxtarget, eax, bytesWritten,    0

A version of your code that follows the C call convention, uses both a source and destination buffer, tests for the pointers to make sure they aren't NULL, does the conversion using a similar method described by Peter Cordes is as follows:

upperToLower proc uses edi esi, source:dword, dest:dword 
    ; uses ESI EDI is used to tell assembler we are clobbering two of
    ; the cdecl calling convetions non-volatile registers. See:
    ; https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl
    mov esi, source            ; ESI = Pointer to string
    test esi, esi              ; Is source a NULL pointer?
    jz done                    ;     If it is then we are done

    mov edi, dest              ; EDI = Pointer to string
    test edi, edi              ; Is dest a NULL pointer?
    jz done                    ;     If it is then we are done

    xor edx, edx               ; EDX = 0 = current character index into the strings

    jmp getnextchar            ; Jump into loop at point of getting next character

  charloop:
    lea ecx, [eax - 'A']       ; cl = al-'A', and we do not care about the rest
                               ;     of the register

    cmp cl, 25                 ; if(c >= 'A' && c <= 'Z') c += 0x20;
    lea ecx, [eax + 20h]       ; without affecting flags
    cmovna eax, ecx            ; take the +0x20 version if it was in the 
                               ;     uppercase range to start with
    mov [edi + edx], al        ; Update character in destination string
    inc edx                    ; Go to next character

  getnextchar:
    movzx eax, byte ptr [esi + edx]
                               ; mov al, [esi + edx] leaving high garbage in EAX is ok
                               ;     too, but this avoids a partial-register stall
                               ;     when doing the mov+sub
                               ;     in one instruction with LEA
    test eax, eax              ; Is the character NUL(0) terminator?
    jnz charloop               ;     If not go back and process character

  printString:
    ; EDI = source, EDX = length of string

    invoke WriteConsoleA, consoleOutHandle, edi, edx, bytesWritten, 0
    mov edx, sizeof buffer
  done:
    ret
upperToLower endp

A version that takes one parameter and changes the source string to upper case could be done this way:

upperToLower proc, source:dword
    mov edx, source            ; EDX = Pointer to string
    test edx, edx              ; Is it a NULL pointer?
    jz done                    ;     If it is then we are done

    jmp getnextchar            ; Jump into loop at point of getting next character

  charloop:
    lea ecx, [eax - 'A']       ; cl = al-'A', and we do not care about the rest
                               ;     of the register

    cmp cl, 25                 ; if(c >= 'A' && c <= 'Z') c += 0x20;
    lea ecx, [eax + 20h]       ; without affecting flags
    cmovna eax, ecx            ; take the +0x20 version if it was in the
                               ;     uppercase range to start with
    mov [edx], al              ; Update character in string
    inc edx                    ; Go to next character

  getnextchar:
    movzx eax, byte ptr [edx]  ; mov al, [edx] leaving high garbage in EAX is ok, too,
                               ;     but this avoids a partial-register stall 
                               ;     when doing the mov+sub in one instruction with LEA
    test eax, eax              ; Is the character NUL(0) terminator?
    jnz charloop               ;     If not go back and process character

  printString:
    sub edx, source            ; EDX-source=length
    invoke WriteConsoleA, consoleOutHandle, source, edx, bytesWritten, 0
  done:
    ret
upperToLower endp

Observations

  • A generic upperToLower function that does the string conversion would normally not do the printing itself. You'd normally call upperToLower to do the conversion only, then you'd output the string to the display in a separate call.
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • Could you please give me some guidance on how to make it more efficient? – Wolfeius Aug 03 '19 at 14:04
  • 1
    @Wolfeius : I've provided a couple of examples in my latest update using a different technique – Michael Petch Aug 03 '19 at 16:31
  • 1
    Thank you for that. It will take me some time to understand everything that's going on, but I will try to use the few bits I think I got right in the next bits of this program. Thanks! – Wolfeius Aug 04 '19 at 09:14