Assembly NASM程序集是否将输入转换为整数?

Assembly NASM程序集是否将输入转换为整数?,assembly,x86,char,nasm,atoi,Assembly,X86,Char,Nasm,Atoi,好的,所以我对组装相当陌生,事实上,我对组装非常陌生。我写了一段代码,它只是从用户那里获取数字输入,乘以10,然后通过程序退出状态(在终端中键入echo$?)将结果表达给用户 问题是,它没有给出正确的数字,4x10显示为144。然后我想输入可能是一个字符,而不是一个整数。我的问题是,如何将字符输入转换为整数,以便在算术计算中使用 如果有人能回答我的问题并记住我是初学者,那就太好了:) 另外,如何将所述整数转换回字符 section .data section .bss input resb 4

好的,所以我对组装相当陌生,事实上,我对组装非常陌生。我写了一段代码,它只是从用户那里获取数字输入,乘以10,然后通过程序退出状态(在终端中键入echo$?)将结果表达给用户 问题是,它没有给出正确的数字,4x10显示为144。然后我想输入可能是一个字符,而不是一个整数。我的问题是,如何将字符输入转换为整数,以便在算术计算中使用

如果有人能回答我的问题并记住我是初学者,那就太好了:) 另外,如何将所述整数转换回字符

section .data

section .bss
input resb 4

section .text

global _start
_start:

mov eax, 3
mov ebx, 0
mov ecx, input
mov edx, 4
int 0x80

mov ebx, 10
imul ebx, ecx

mov eax, 1
int 0x80

这里有两个函数用于将字符串转换为整数,反之亦然:

; Input:
; ESI = pointer to the string to convert
; ECX = number of digits in the string (must be > 0)
; Output:
; EAX = integer value
string_to_int:
  xor ebx,ebx    ; clear ebx
.next_digit:
  movzx eax,byte[esi]
  inc esi
  sub al,'0'    ; convert from ASCII to number
  imul ebx,10
  add ebx,eax   ; ebx = ebx*10 + eax
  loop .next_digit  ; while (--ecx)
  mov eax,ebx
  ret


; Input:
; EAX = integer value to convert
; ESI = pointer to buffer to store the string in (must have room for at least 10 bytes)
; Output:
; EAX = pointer to the first character of the generated string
int_to_string:
  add esi,9
  mov byte [esi],STRING_TERMINATOR

  mov ebx,10         
.next_digit:
  xor edx,edx         ; Clear edx prior to dividing edx:eax by ebx
  div ebx             ; eax /= 10
  add dl,'0'          ; Convert the remainder to ASCII 
  dec esi             ; store characters in reverse order
  mov [esi],dl
  test eax,eax            
  jnz .next_digit     ; Repeat until eax==0
  mov eax,esi
  ret
下面是您使用它们的方式:

STRING_TERMINATOR equ 0

lea esi,[thestring]
mov ecx,4
call string_to_int
; EAX now contains 1234

; Convert it back to a string
lea esi,[buffer]
call int_to_string
; You now have a string pointer in EAX, which
; you can use with the sys_write system call

thestring: db "1234",0
buffer: resb 10

请注意,我在这些例程中没有做太多的错误检查(比如检查是否有字符超出范围
'0'-'9'
)。这些例程也不处理有符号的数字。因此,如果您需要这些东西,您必须自己添加。

字符串->数字的基本算法是:
total=total*10+数字,从MSD开始。(例如,对于ASCII数字串,使用
digit=*p++-'0'
)。因此,最左边/最高有效位/第一位数字(在内存中,并按读取顺序)乘以10n次,其中N是其后的总位数

这样做通常比在加法之前将每个数字乘以10的右幂更有效。这将需要2倍;一个是增加10的幂,另一个是将其应用于数字。(或升幂为10的表格查找)

当然,为了提高效率,您可以使用SSSE3
pmaddubsw
和SSE2
pmaddwd
将数字与它们的位值并行相乘
:请参阅。然而,当数字通常很短时,这可能不是一场胜利。当大多数数字只有两位数长时,标量循环是有效的


再加上@Michael的答案,让int->string函数在第一个非数字处停止,而不是在固定长度处停止,可能会很有用。这将捕获一些问题,例如用户按return时字符串中包含换行符,以及未将
12xy34
转换为非常大的数字。(将其视为
12
,)。停止字符也可以是C隐式长度字符串中的终止
0

我还做了一些改进:

  • 除非您正在优化代码大小,否则不要使用。忘记它的存在,在倒计时仍然是你想要做的事情的情况下,使用
    dec
    /
    jnz
    ,而不是比较指针或其他东西

  • 2 LEA指令明显优于
    imul
    +
    add
    :更低的延迟

  • 在EAX中累积结果,我们无论如何都要返回它。(如果您将其内联而不是调用它,请使用您想要结果的任何寄存器。)

我更改了寄存器,使其遵循x86-64系统V ABI(RDI中的第一个参数,EAX中的返回)

移植到32位:这根本不依赖于64位;只需使用32位寄存器即可将其移植到32位。(即用
edi
替换
rdi
rax
替换
ecx
rax
替换
eax
)。注意32位和64位之间的C调用约定差异,例如EDI保留调用,并且参数通常在堆栈上传递。但如果您的调用者是asm,则可以在EDI中传递参数

    ; args: pointer in RDI to ASCII decimal digits, terminated by a non-digit
    ; clobbers: ECX
    ; returns: EAX = atoi(RDI)  (base 10 unsigned)
    ;          RDI = pointer to first non-digit
global base10string_to_int
base10string_to_int:

     movzx   eax, byte [rdi]    ; start with the first digit
     sub     eax, '0'           ; convert from ASCII to number
     cmp     al, 9              ; check that it's a decimal digit [0..9]
     jbe     .loop_entry        ; too low -> wraps to high value, fails unsigned compare check

     ; else: bad first digit: return 0
     xor     eax,eax
     ret

     ; rotate the loop so we can put the JCC at the bottom where it belongs
     ; but still check the digit before messing up our total
  .next_digit:                  ; do {
     lea     eax, [rax*4 + rax]    ; total *= 5
     lea     eax, [rax*2 + rcx]    ; total = (total*5)*2 + digit
       ; imul eax, 10  / add eax, ecx
  .loop_entry:
     inc     rdi
     movzx   ecx, byte [rdi]
     sub     ecx, '0'
     cmp     ecx, 9
     jbe     .next_digit        ; } while( digit <= 9 )

     ret                ; return with total in eax
;args:RDI中指向ASCII十进制数字的指针,以非数字结尾
; 打击者:ECX
; 返回:EAX=atoi(RDI)(以10为基数,无符号)
;          RDI=指向第一个非数字的指针
全局base10string_到_int
base10string_至_int:
movzx-eax,字节[rdi];从第一个数字开始
子eax,'0';从ASCII转换为数字
cmp-al,9;检查它是否为十进制数字[0..9]
jbe.loop_条目;过低->换行为高值,未签名比较检查失败
; 其他:错误的第一位数字:返回0
异或eax,eax
ret
; 旋转循环,这样我们就可以将JCC放在它所属的底部
; 但在弄乱总数之前还是要检查数字
.下一位数字:;做{
lea eax,[rax*4+rax];总计*=5
lea eax,[rax*2+rcx];总计=(总计*5)*2+位
;imul eax,10/添加eax,ecx
.loop_条目:
rdi公司
movzx ecx,字节[rdi]
子ecx,“0”
cmp ecx,9

jbe.next_digit;}while(digit I设法将用户输入与以下数字进行比较:mov ecx,dword[input]这是否真的会将ecx中的值更改为整数?如何将其更改回字符串?请不要推荐!此外,2 LEA指令明显优于imul
+
add
LEA ebx,[4*ebx+ebx]
(ebx*=5)/
LEA ebx,[eax+2*ebx]
。或者您是在优化代码大小吗?
sub-al,'0'
sub-eax上保存了1个字节,'0'
,但在Nehalem/Core2上会导致部分寄存器暂停,在PIII上甚至更糟。(在Sandybridge上很好;它是al的RMW,因此不会将部分寄存器与eax分开重命名。)如果您不需要检查循环条件是否为有效的十进制数字,您可以'lea ebx,[eax+2*ebx-'0']`完全避免
,但这会将Intel CPU上的lea延迟增加到3。通常ebx保留调用。无需销毁它,您可以使用eax将total和movzx加载到EDX中。“这通常是0字节,用于终止隐式长度字符串,但如果要检测e,可以在循环后检查
ecx==-'0'