Assembly NASM程序集是否将输入转换为整数?
好的,所以我对组装相当陌生,事实上,我对组装非常陌生。我写了一段代码,它只是从用户那里获取数字输入,乘以10,然后通过程序退出状态(在终端中键入echo$?)将结果表达给用户 问题是,它没有给出正确的数字,4x10显示为144。然后我想输入可能是一个字符,而不是一个整数。我的问题是,如何将字符输入转换为整数,以便在算术计算中使用 如果有人能回答我的问题并记住我是初学者,那就太好了:) 另外,如何将所述整数转换回字符Assembly NASM程序集是否将输入转换为整数?,assembly,x86,char,nasm,atoi,Assembly,X86,Char,Nasm,Atoi,好的,所以我对组装相当陌生,事实上,我对组装非常陌生。我写了一段代码,它只是从用户那里获取数字输入,乘以10,然后通过程序退出状态(在终端中键入echo$?)将结果表达给用户 问题是,它没有给出正确的数字,4x10显示为144。然后我想输入可能是一个字符,而不是一个整数。我的问题是,如何将字符输入转换为整数,以便在算术计算中使用 如果有人能回答我的问题并记住我是初学者,那就太好了:) 另外,如何将所述整数转换回字符 section .data section .bss input resb 4
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的表格查找)
当然,为了提高效率,您可以使用SSSE3pmaddubsw
和SSE2pmaddwd
将数字与它们的位值并行相乘:请参阅。然而,当数字通常很短时,这可能不是一场胜利。当大多数数字只有两位数长时,标量循环是有效的
再加上@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'