【问题标题】:convert decimal to ASCII / binary in assembly在汇编中将十进制转换为 ASCII / 二进制
【发布时间】:2016-10-16 22:03:15
【问题描述】:

我正在尝试将给定的十进制数转换为 32 个字符的二进制数,但我的代码一直给我错误的答案

我正在尝试转换为二进制的数字

aLength     db  21

我使用的变量

two         dq      2
tempString  resb    33

我试图将 aLength (21) 除以 2 32 次并将所有余数堆叠起来,但它不起作用

这是我的代码

mov rax,0
mov eax,byte[aLength]
mov rcx,32

lp2:
mov rdx,0
div qword[two]
push rdx
loop lp2
mov rsi,tempString
mov rcx,2

lp3:
pop rax
add al,"0"
mov byte[rbx],al
inc rbx
loop lp3
mov byte[rbx],NULL

【问题讨论】:

  • 发现的第一件事:mov eax, DWORD [aLength]aLength 是一个字节。 rbx 指向...?第二个循环只有 2 次迭代。我已经回答了你的补充问题(bin -> dec),你可以完全重用那里的概念。如果你想简化你的代码。
  • 我看到代码已经被编辑了,但是假设rax已经被归零,不应该是mov al,[aLength]吗?您希望转换的数字保存在单个字节中,由aLength db 21 定义
  • 十进制数21 由汇编器自动转换为二进制。您需要做的就是从eax 的一端滚动位(不是rax,因为您只需要32 位)。不需要除法。
  • #1 : mov eax,byte[aLength] 应该是 mov al,byte[aLength]。 #2:在lp2循环之后,mov rsi,tempString应该是mov rbx,tempString,因为lp3循环使用rbx。 #3:mov rcx,2 之前的 lp3: 应该是 mov rcx,32
  • 永远不要使用div除以2。使用shr or sar。余数是移出到 CF 中的位。

标签: assembly binary decimal ascii


【解决方案1】:

debug你的代码在一起。

调试是您应该必须自己做的事情,因为编写代码只是故事的一半。
老实说,我认为这个问题会被否决,因为它实际上是相反的,似乎人们希望它得到回答。

现在,您问了很多类似的问题4,这表明完全缺乏调试技能。
因此,我们不会告诉您代码中的易于发现和调试错误,而是进行调试,所以也许您会学到一些东西。

我们将使用 GDB1。我从您的代码2 开始,制作了一个针对 ELF64 的可汇编版本,并在 Cygwin 上使用 gcc 编译了目标文件。


让我们首先检查我们的值是否正确加载。
逐步执行前两个指令,即设置 RAX 的指令。

   ┌───────────────────────────────────────────────────────────────────────────┐
B+ │0x1004010e0 <WinMain>           mov    $0x0,%eax                           │
   │0x1004010e5 <WinMain+5>         mov    0xf25(%rip),%eax        # 0x10040201│
   │0x1004010eb <WinMain+11>        mov    $0x20,%ecx                          │
   │0x1004010f0 <lp2>               mov    $0x0,%edx                           │
  >│0x1004010f5 <lp2+5>             divq   0xf15(%rip)        # 0x100402011 <tw│
   │0x1004010fc <lp2+12>            push   %rdx                                │
   │0x1004010fd <lp2+13>            loop   0x1004010f0 <lp2>                   │
   │0x1004010ff <lp2+15>            movabs $0x100407000,%rsi                   │
   │0x100401109 <lp2+25>            mov    $0x2,%ecx                           │
   │0x10040110e <lp3>               pop    %rax                                │
   │0x10040110f <lp3+1>             add    $0x30,%al                           │
   │0x100401111 <lp3+3>             mov    %al,(%rbx)                          │
   │0x100401113 <lp3+5>             inc    %rbx                                │
   │0x100401116 <lp3+8>             loop   0x10040110e <lp3>                   │
   │0x100401118 <lp3+10>            movb   $0x0,(%rbx)                         │
   │0x10040111b <lp3+13>            retq                                       │
   └───────────────────────────────────────────────────────────────────────────┘
native Thread 5100.0x9e4 In: lp2                          L??   PC: 0x1004010f5
(gdb) si 4
0x00000001004010f5 in lp2 ()
(gdb) i r rax rcx rdx
rax            0x215    533
rcx            0x20     32
rdx            0x0      0
(gdb)

Shippity 商店!这里发生了什么?
RCXRDX 看起来不错,但 RAX 却不行!当然 533 与 21有很大不同

在整整 10 分钟 之后,我们终于发现第二条指令 正在从 aLength 加载一个 DWORD,它是一个 BYTE,所以我们是在 RAX 中添加一些垃圾。

所以我们更正那行3

mov al, BYTE [aLength]

我们再次重复之前的调试步骤:

(gdb) i r rax rcx rdx
rax            0x15     21
rcx            0x20     32
rdx            0x0      0

很好!
现在我们执行循环的第一次迭代


   ┌───────────────────────────────────────────────────────────────────────────┐
   │0x1004010e5 <WinMain+5>         mov    0xf25(%rip),%al        # 0x100402010│
   │0x1004010eb <WinMain+11>        mov    $0x20,%ecx                          │
  >│0x1004010f0 <lp2>               mov    $0x0,%edx                           │
   │0x1004010f5 <lp2+5>             divq   0xf15(%rip)        # 0x100402011 <tw│
   │0x1004010fc <lp2+12>            push   %rdx                                │
   │0x1004010fd <lp2+13>            loop   0x1004010f0 <lp2>                   │
   │0x1004010ff <lp2+15>            movabs $0x100407000,%rsi                   │
   │0x100401109 <lp2+25>            mov    $0x2,%ecx                           │
   │0x10040110e <lp3>               pop    %rax                                │
   │0x10040110f <lp3+1>             add    $0x30,%al                           │
   │0x100401111 <lp3+3>             mov    %al,(%rbx)                          │
   │0x100401113 <lp3+5>             inc    %rbx                                │
   │0x100401116 <lp3+8>             loop   0x10040110e <lp3>                   │
   │0x100401118 <lp3+10>            movb   $0x0,(%rbx)                         │
   │0x10040111b <lp3+13>            retq                                       │
   └───────────────────────────────────────────────────────────────────────────┘
native Thread 4236.0x12e0 In: lp2                         L??   PC: 0x1004010f0
rdx            0x0      0
(gdb) si 3
0x00000001004010f0 in lp2 ()
(gdb) i r rax rcx rdx
rax            0xa      10
rcx            0x1f     31
rdx            0x1      1
(gdb)

一切看起来都不错:RAX 减半,RCX 是 32 减一,RDX 是 21 的 lsb,也就是 1。
让我们检查一下堆栈上是否有效地存在这个。

A syntax error in expression, near `%rsp'.
(gdb) x/1dg $rsp
0xffffcb20:     1

不错!


由于循环看起来没问题,我们现在可以跳出它并检查部分结果

   ┌───────────────────────────────────────────────────────────────────────────┐
   │0x1004010dc <__gcc_deregister_frame+12> nop                                │
   │0x1004010dd <__gcc_deregister_frame+13> nop                                │
   │0x1004010de <__gcc_deregister_frame+14> nop                                │
   │0x1004010df <__gcc_deregister_frame+15> nop                                │
B+ │0x1004010e0 <WinMain>                   mov    $0x0,%eax                   │
   │0x1004010e5 <WinMain+5>                 mov    0xf25(%rip),%al        # 0x1│
   │0x1004010eb <WinMain+11>                mov    $0x20,%ecx                  │
   │0x1004010f0 <lp2>                       mov    $0x0,%edx                   │
   │0x1004010f5 <lp2+5>                     divq   0xf15(%rip)        # 0x10040│
   │0x1004010fc <lp2+12>                    push   %rdx                        │
   │0x1004010fd <lp2+13>                    loop   0x1004010f0 <lp2>           │
  >│0x1004010ff <lp2+15>                    movabs $0x100407000,%rsi           │
   │0x100401109 <lp2+25>                    mov    $0x2,%ecx                   │
   │0x10040110e <lp3>                       pop    %rax                        │
   │0x10040110f <lp3+1>                     add    $0x30,%al                   │
   └───────────────────────────────────────────────────────────────────────────┘
native Thread 4236.0x12e0 In: lp2                         L??   PC: 0x1004010ff
(gdb) p/u *(unsigned long long (*)[32])$rsp
$3 = {0 <repeats 27 times>, 1, 0, 1, 0, 1}
(gdb)

寄存器肯定没问题,所以我们只检查推送的值。
正如 GDB 告诉我们的,数字 21 已正确转换为 0..010101。


我们现在再次调试下一个循环的第一次迭代

   ┌───────────────────────────────────────────────────────────────────────────┐
  >│0x10040110e <lp3>               pop    %rax                                │
   │0x10040110f <lp3+1>             add    $0x30,%al                           │
   │0x100401111 <lp3+3>             mov    %al,(%rbx)                          │
   │0x100401113 <lp3+5>             inc    %rbx                                │
   │0x100401116 <lp3+8>             loop   0x10040110e <lp3>                   │
   │0x100401118 <lp3+10>            movb   $0x0,(%rbx)                         │
   │0x10040111b <lp3+13>            retq                                       │
   │0x10040111c <lp3+14>            nopl   0x0(%rax)                           │
   │0x100401120 <__cxa_atexit>      jmpq   *0x6fbe(%rip)        # 0x1004080e4 <│
   │0x100401126 <__cxa_atexit+6>    nop                                        │
   │0x100401127 <__cxa_atexit+7>    nop                                        │
   │0x100401128 <__cxa_atexit+8>    nop                                        │
   │0x100401129 <__cxa_atexit+9>    nop                                        │
   │0x10040112a <__cxa_atexit+10>   nop                                        │
   │0x10040112b <__cxa_atexit+11>   nop                                        │
   └───────────────────────────────────────────────────────────────────────────┘
native Thread 4236.0x12e0 In: lp3                         L??   PC: 0x10040110e
0x0000000100401116 in lp3 ()
(gdb) si
0x000000010040110e in lp3 ()
(gdb) i r rsi rax rbx rcx
rsi            0x100407000      4299190272
rax            0x30     48
rbx            0x285541 2643265
rcx            0x1      1
(gdb)

哦,快!
RSI没有增加! RCX 在一次迭代后也为 1。 RAX 很好。

经过整整 10 分钟 令人沮丧的思考后,我们意识到我们在循环中使用的是 EBX,而不是 RSI,我们设置了 RCX 到 2 而不是 32!

我们解决了这些问题:

mov rbx, tempString
mov rcx, 32

最后我们尝试运行程序直到结束。
完成后,我们检查写入的字符串:

(gdb) x/4xg 0x100407000
0x100407000 <tempString>:       0x3030303030303030      0x3030303030303030
0x100407010 <tempString+16>:    0x3030303030303030      0x3130313031303030

考虑到字节序

30 30 30 30 30 30 30 30   30 30 30 30 30 30 30 30   30 30 30 30 30 30 30 30   30 30 30 31 30 32 30 31

确认程序的正确性。


Here again,您的程序可以使用与使用 CF 相同的技巧来简化:

 movzx ebx, BYTE [REL aLength]      ;EBX = Byte to convert
 mov rcx, 32                        ;RCX = Bits left to convert
 mov rdi, tempString                ;RDI = Pointer to output string

 xor eax, eax
 mov al, '0'                        ;RAX = Aux value

_convert:

 shr eax, 1                         ;Get rid of RAX bit 0

 shl ebx, 1                         ;Set CF to the current msb of EBX
 rcl eax, 1                         ;Shift into RAX the CF

 stosb                              ;Store ASCII digit

 sub rcx, 1                         ;Repeat
ja _convert

 mov BYTE [rdi], cl         ;Write NULL TERM

1 因为乞丐不能挑食。 This cheatsheet 将很有用。
2 原始的,而不是可疑的补丁。
3 实现将字节加载到RAX。
4 正如我在评论中解释的那样,你们中的一个问题本质上是这个问题的补充,可以非常直接地重复使用。

【讨论】:

  • 我喜欢你的“十分钟刮脑袋”和“另外整整十分钟令人沮丧的思考”。就我个人而言,我不认为在这样一段微不足道的代码上使用调试器可以简化新手的工作。对于受过训练的人来说,OP 代码的所有问题都在尖叫!只是检查代码的古老艺术发生了什么?
  • @SepRoland 是的。如果您查看 cmets,您会发现每个人都确实已经发现了错误。但是由于 OP 向网站充斥着这样的问题(本质上是为我调试这段代码),我认为引导式调试会话会有所帮助:)
猜你喜欢
  • 2014-05-24
  • 2014-07-20
  • 2015-06-02
  • 2015-02-04
  • 1970-01-01
  • 2012-06-02
  • 2012-04-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多