【问题标题】:Gas Assembly x86. converting char to binary string气体组装 x86。将char转换为二进制字符串
【发布时间】:2016-03-16 18:03:26
【问题描述】:

我是组装新手,我也在尝试了解我可以使用的工具。我必须为一个微型 C 程序创建一个可调用的汇编函数。目标是让用户按十六进制值输入字母 - 因此 A 为 41。然后 C 将此十六进制值作为无符号字符传递给汇编函数。下面是C代码

extern char *printbin(unsigned char);

int main(void)
{
  unsigned int x;

  printf("enter the character's ascii value in hex: \n");
  scanf("%x",&x);
  printf("The binary format for character %c is %s\n", 
                                 x,   printbin((unsigned char)x));
  return 0;
}

我的理解是在堆栈上应该有一个值 41 存储在一个字节中,因为在 C 字符中实际上也只是数字。这个假设是否正确。任务是将输入转换为两个 4 位二进制数字的输出字符串,每个数字用空格分隔,所以 0100 0001 我的第一个问题是如何将寄存器中的字节拆分为 4 位或仅 1 或4?我为汇编文件设置了一个骨架

.text
.globl _printbin

_printbin:

        pushl %ebp
        movl %esp, %ebp

        movl 4(%ebp), %ecx  #store the char in ecx register
        jmp end

donibble: #function to convert digit to binary

end:
        movl %ebp, %esp
        popl %ebp
        ret

但是我不知道如何继续抓取一半的字节。当我对它进行“工作”时,我可以只用 %el 抓取一个字节,而不是 %ecx ,但我不知道有任何类型的寄存器可以抓取少于 1 个字节的任何内容。任何帮助或想法将不胜感激。

更新的汇编代码

.text
.globl _printbin

_printbin:

        pushl %ebp
        movl %esp, %ebp

        movl 4(%ebp), %edx #grabs the hex byte
        movl $0, %eax   #sets $eax to 0

        shrb $4,%dl
        jmp donibble
        L2:
                incl %eax
        #       addl 0x20, %return string #add the space.
                movl 4(%ebp), %edx
                AND %dl, 0x0f 
                jmp donibble    
        L3:
                jmp end

donibble:
#Takes %dl and gets string and adds string to return String
        cmpb %dl, 0x0
        je Zero
        cmpb %dl, 0x1
        je One

Zero:
         #add 0000 to string
 jmp donibbleEnd
One: 
        #add 0001 to string

        donibbleEnd:
        #to send back to printbin
                cmpb $0, %eax
                je L2
                jmp L3


end:
        movl $returnString, %eax
        movl %ebp, %esp
        popl %ebp
        ret

.data

returnString:
        .asciz "1001 1100\n"

.end

有没有办法按照上面这个更新的结构添加到我的 $returnString 中?显然 addl "0000", $returnString 根本不起作用:)

【问题讨论】:

  • 没有。您将不得不像在 C 中那样做位shitfing 和掩码。 x >> 4 和 x & 0x0f 得到两半。
  • 你可以在装配时移动和遮罩吗?我只看到使用 addl 等命令而不是 + 号的基本数学
  • 您使用适当的说明进行转换和遮罩 :) 我把它留给您作为练习。您还可以使用汇编器的运算符对常量进行移位和屏蔽,但这不适用于此处。
  • 术语注释:它们是指令,而不是“命令”。班次:查看shl 和shr 指令。口罩:你有and、or、xor、not。
  • 感谢最后两个 cmets。我会尝试看看我能从这里完成什么。我应该能够屏蔽和移动这些位以将它们分开。我将把那个数字转换成二进制字符串。

标签: c assembly x86


【解决方案1】:

这是一个奇怪的函数原型。你应该返回一个指向静态缓冲区的指针吗?更典型的设计是调用者将指针传递给函数可以存储字符串的缓冲区。


执行此操作的常用方法是利用移位指令将CF 设置为移出的最后一位这一事实。然后,您可以使用setc 获取零或一,并将其​​转换为 ascii '0' 或 '1'。另一个技巧是adc $0 或adc $'0' 读取进位标志。

不过,复制一个寄存器并使用and 来获取低位也是可行的。

如果您遇到困难,请使用搜索。以前有过关于这个的问题。尝试在 和/或 标记中搜索。查看 x86 标签 wiki 以获得许多指向初学者资料和参考资料的好链接。

【讨论】:

    【解决方案2】:

    为了记录关闭这个。这是我结束的代码。我感谢大家的帮助。

    .text
    .globl _printbin
    
    _printbin:
    
        pushl %ebp
        movl %esp, %ebp
    
        movb 8(%ebp), %dl #grabs the hex byte
        movl $0, %eax   #sets $eax to 0
    
        shrb $4, %dl
        movl $returnString, %ecx
        pushl %ebx #free up ebx
        jmp donibble
    
    returnFromDonibble:
        incl %ecx
        movb 8(%ebp), %dl
        movb $0x0f, %al
        andb $15, %dl
        movb $0x39, %al
        cmpb (%ecx), %al
        je donibble
        jne end
    
    
    
    
    donibble:
    #Takes %dl and gets string and adds string to return String
        movb %dl, %bl
        movb $0x08, %al
        andb $8,%dl
        je zero
        jne one
    
        one:
            movb $0x31, (%ecx)
            incl %ecx
            jmp L2
        zero:
            movb $0x30, (%ecx)
            incl %ecx
            jmp L2
    L2:
        movb %bl, %dl
        movb $0x04, %al 
        andb $4, %dl
        je zero2
        jne one2
    
        one2:
            movb $0x31, (%ecx)
            incl %ecx
            jmp L3
        zero2:
            movb $0x30, (%ecx)
            incl %ecx
            jmp L3
    
    L3:
        movb %bl, %dl
        movb $0x02, %al
        andb $2, %dl
        je zero3
        jne one3
    
        one3:
            movb $0x31, (%ecx)
            incl %ecx
            jmp L4
        zero3:
            movb $0x30, (%ecx)
            incl %ecx
            jmp L4
    
    L4:
        movb %bl, %dl
        movb $0x01, %al
        andb $1, %dl
        je zero4
        jne one4
    
        one4:
            movb $0x31, (%ecx)
            incl %ecx
            jmp returnFromDonibble
        zero4:
            movb $0x30, (%ecx)
            inc %ecx
            jmp returnFromDonibble
    
    end:
        popl %ebx
        movl $returnString, %eax
        movl %ebp, %esp
        popl %ebp
        ret
    
    
    
    
    .data
    
    returnString:   .asciz "9999 9999"
    
    .end
    

    【讨论】:

    • 很高兴您能够整理出您的代码。 returnString 可以在 .bss 而不是 .data 部分中,因为初始值无关紧要(您总是完全覆盖它)。此外,您的 je zero3 / jne one3 对很愚蠢:只需使用一个可以通过或不通过的分支。注意,第二个分支的跳转目标是跟在它后面的insn,所以不管被取还是不取,它都会去同一个地方。另请注意,将%dl中你想要的位与and隔离后,可以shr $1, %dl/add $0x30, %dl,然后就不需要分支了。
    • incl %ecx 存在于分支的两侧,因此您应该将其拉出并放在分支重新连接的位置。再次删除L4 之前的jmp L4。你不需要跳过空白。
    • 您还可以使用移位循环遍历半字节中的位,因此您感兴趣的位始终位于您的寄存器的低位(或shl 的高位)重新换档。要做得更好,请利用移位指令将进位标志设置为刚移出的位这一事实。所以你可以使用shl $1, %al / adc $0x30, %a_zeroed_reg 什么的。
    • 初学者写巨量低效的asm代码很正常。只要您了解它的工作原理,您就是在学习,并且随着您了解的更多,您将掌握编写更高效代码的技巧。
    猜你喜欢
    • 2017-03-21
    • 2021-02-24
    • 1970-01-01
    • 1970-01-01
    • 2014-05-27
    • 2014-05-24
    • 1970-01-01
    相关资源
    最近更新 更多