如何在 32 位汇编中正确打印用户输入的正确位置?

问题描述 投票:0回答:1

我目前正在终端中开发一个简单的“关于我”部分。我正在使用 NASM 程序集并在 Oracle VM 中运行 Ubuntu。我目前在打印用户输入和标签时遇到问题。
所需的输出应该是 label:userinput 但发生的只是 ascii-art 和标签遵循正确的光标位置,但用户输入要么分散,要么不在标签旁边。

这是迄今为止我的汇编代码:

section .data
    clearScreen db 27, '[2J', 0          ; ANSI code to clear screen
    asciiArt1 db 27, '[5;4H', "   _   _                 _                    ", 10, \
              27, '[6;4H', "  /_\ | |__   ___  _   _| |_   _ __ ___   ___ ", 10, \
              27, '[7;4H', " //_\\| '_ \ / _ \| | | | __| | '_ ` _ \ / _ \", 10, \
              27, '[8;4H', "/  _  \ |_) | (_) | |_| | |_  | | | | | |  __/", 10, \
              27, '[9;4H', "\_/ \_/_.__/ \___/ \__,_|\__| |_| |_| |_|\___|", 10, 0

    cursorNameLabel db 27, '[8;65H'      ; Adjusted position for the name label
    nameLabel db 'Name: ', 0
    
    cursorEmailLabel db 27, '[12;4H'    ; Adjusted position for the email label
    emailLabel db 'Email: ', 0

    cursorBottom db 27, '[40;1H'

section .bss
    name resb 20     ; Reserve 20 bytes for the name
    email resb 25    ; Reserve 25 bytes for the email

section .text
    global _start

_start:
    call clearTheScreen
    call userInputs
    call displayAsciiArt
    call displayInfo
    call exitProgram

clearTheScreen:
    mov eax, 4
    mov ebx, 1
    mov ecx, clearScreen
    mov edx, 4
    int 0x80
    ret

userInputs:
    ; Input for name
    mov eax, 4
    mov ebx, 1
    mov ecx, nameLabel
    mov edx, 6          ; Length for the name label
    int 0x80

    mov eax, 3
    mov ebx, 0
    mov ecx, name
    mov edx, 20          ; Max length for the name
    int 0x80

    ; Input for email
    mov eax, 4
    mov ebx, 1
    mov ecx, emailLabel
    mov edx, 8          
    int 0x80

    mov eax, 3
    mov ebx, 0
    mov ecx, email
    mov edx, 25          ; Max length for the email
    int 0x80
    ret

displayAsciiArt:
    mov eax, 4
    mov ebx, 1
    mov ecx, asciiArt1
    mov edx, 300         ; Total length of ASCII art
    int 0x80
    ret

displayInput:
    ; Input parameters: 
    ; ecx = label address, edx = buffer address, ebx = cursor position
    mov eax, 4          ; sys_write
    mov ebx, 1          ; file descriptor (stdout)

    ; Move cursor
    mov ecx, [esp + 4]   ; Load cursor position from stack
    mov edx, 32          ; Length for cursor
    int 0x80            ; Write cursor position

    ; Print label
    mov ecx, [esp + 8]   ; Load the label address
    mov edx, 32          ; Length of the label
    int 0x80            ; Write label

    ; Print input
    mov ecx, [esp + 12]  ; Load the input address
    mov edx, 20         ; Display name input length (or change to 25 for email)
    int 0x80            ; Write input
    ret

displayInfo:
    ; Display Name
    push cursorNameLabel ; Push cursor position for name
    push nameLabel       ; Push name label
    push name            ; Push name address
    call displayInput    ; Call displayInput
    add esp, 12         ; Clean up stack

    ; Display Email
    push cursorEmailLabel; Push cursor position for email
    push emailLabel      ; Push email label
    push email           ; Push email address
    call displayInput    ; Call displayInput
    add esp, 12         ; Clean up stack
    ret

exitProgram:
    ; Move cursor to the bottom before exiting
    mov eax, 4
    mov ebx, 1
    mov ecx, cursorBottom
    mov edx, 7
    int 0x80

    mov eax, 1
    int 0x80

输出如下所示: 终端

我之前的代码较长,我决定将其缩短,以便于阅读。
我还有更多内容要打印,我首先使用 2 个用户输入进行测试。
我只需要 ascii-art、标签和用户输入即可相应打印。

ubuntu assembly x86 nasm 32-bit
1个回答
1
投票
cursorNameLabel db 27, '[8;65H'
nameLabel db 'Name: ', 0
cursorEmailLabel db 27, '[12;4H'
emailLabel db 'Email: ', 0

您的程序中这些消息的长度似乎有很多混乱!例如。对于打印 nameLabel,您使用 6(省略终止零),但对于打印 emailLabel,您使用 8(包括终止零)。无论如何,sys_write 都需要一个长度,所以最好不要提及终止零。

第一个错误是因为在 displayInput 中您使用了 32

 作为 
cursorNameLabelnameLabel 的长度,不可避免地,您将打印比您预期更多的内容,并带来灾难性的结果。

但是还有一个

第二个错误。您不尊重参数放置在堆栈上的顺序!

push cursorNameLabel ; Push cursor position for name push nameLabel ; Push name label push name ; Push name address call displayInput ; Call displayInput add esp, 12 ; Clean up stack

根据这些说明,

displayInput将在[esp+12]找到cursorNameLabel

,在
[esp+8]找到nameLabel
,在
[esp+4]找到name

甚至出现

第三个错误,因为虽然系统调用将保留 EBX,但您不应该相信 EAX 保留了其值!因此每次都用 4

 重新加载 EAX。事实上,除了包含结果(错误代码或其他)的 EAX 之外,所有通用寄存器都被保留。请参阅
https://en.wikibooks.org/wiki/X86_Assembly/Interface_with_Linux


现在“姓名”和“电子邮件”长度之间的微小差异又如何呢?为什么不让您的代码变得健壮并统一这些长度,以便它们相同并且您可以成功执行

displayInput 两次?因此,为“name”提供一个 25 字节的缓冲区,就像“email”一样,并在“name:”前面添加一个空格字符,这样它就变得与“email:”一样长。这也更好看...


displayInput: mov ebx, 1 ; file descriptor (stdout) mov ecx, [esp + 12] ; Load cursor position from stack mov edx, 7 ; Use identical lengths mov eax, 4 ; sys_write int 0x80 mov ecx, [esp + 8] ; Load the label address mov edx, 7 ; Use identical lengths mov eax, 4 ; sys_write int 0x80 mov ecx, [esp + 4] ; Load the input address mov edx, 25 ; Use identical lengths mov eax, 4 ; sys_write int 0x80 ret


消息在内存中相互跟随(它们是相邻的),因此没有理由单独输出消息。您可以合并他们的输出:

cursorNameLabel db 27, '[8;65H' nameLabel db ' Name: ' cursorEmailLabel db 27, '[12;4H' emailLabel db 'Email: ' ... name resb 25 email resb 25 ... push cursorNameLabel ; Push cursor position for name push name ; Push name address call displayInput ; Call displayInput add esp, 8 ; Clean up stack push cursorEmailLabel ; Push cursor position for email push Email ; Push email address call displayInput ; Call displayInput add esp, 8 ; Clean up stack ... displayInput: mov ebx, 1 ; file descriptor (stdout) mov ecx, [esp + 8] ; Load cursor position AND label (from .DATA) mov edx, 7+7 ; Use identical lengths mov eax, 4 ; sys_write int 0x80 mov ecx, [esp + 4] ; Load input address (from .BSS) mov edx, 25 ; Use identical lengths mov eax, 4 ; sys_write int 0x80 ret
    
© www.soinside.com 2019 - 2024. All rights reserved.