将字符串参数传递给 PROC

Posted

技术标签:

【中文标题】将字符串参数传递给 PROC【英文标题】:Passing string parameter to a PROC 【发布时间】:2019-08-03 13:41:58 【问题描述】:

我想调用一个函数,它将执行从大写到小写转换为用户键入的字符串,保留特殊字符。这部分有效,但仅适用于前 4 个字符,之后的所有内容都会被截断。我相信是因为我将参数定义为 DWORD:

我尝试过使用PAGEPARABYTE。前两个不起作用,字节表示类型不匹配。

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

原型:

  upperToLower PROTO,
    source: dword,
    auxtarget: dword

调用:

    invoke upperToLower, offset buffer, offset target

缓冲区参数为:buffer db 128 DUP(?)

如何打印整个字符串,而不仅仅是前 4 个字符?

【问题讨论】:

【参考方案1】:

为什么只打印 4 个字符?您将字符串写入控制台:

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

sizeof auxtarget 参数是auxtarget 的大小,它是DWORD(4 个字节),因此您要求只打印 4 个字节。您需要传递字符串的长度。您可以通过在 EAX 中获取结束地址并从中减去 source 指针来轻松做到这一点。结果将是您遍历的字符串的长度。

修改代码为:

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

遵循 C 调用约定的代码版本,使用源缓冲区和目标缓冲区,测试指针以确保它们不是 NULL,使用类似的 @ 进行转换987654321@如下:

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

采用一个参数并将源字符串更改为大写的版本可以这样完成:

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

观察

执行字符串转换的通用upperToLower 函数通常不会执行打印本身。您通常会调用 upperToLower 来仅进行转换,然后在单独的调用中将字符串输出到显示器。

【讨论】:

能否请您指导我如何提高效率? @Wolfeius :我在最新更新中使用不同的技术提供了几个示例 谢谢你。我需要一些时间来理解正在发生的所有事情,但我会尝试在这个程序的下一个部分中使用我认为我正确的一些部分。谢谢!

以上是关于将字符串参数传递给 PROC的主要内容,如果未能解决你的问题,请参考以下文章

为啥我们将字符串数组作为参数传递给 main() 方法,为啥不传递任何集合类型或包装类型或原始类型?

如何将命令行参数传递给 unix/linux 系统上正在运行的进程?

通过 url 将参数传递给 sql server 报告服务

将主要参数传递给方法[关闭]

将 xml 字符串参数传递给 SQL Server 存储过程

如何将参数传递给 JSP 组件?