我想从汇编中的数组中反转一个数字。
如果数字大于-50,我想反转。
如果是24,则变为42。
如果是-15,则变为-51
以下是我的代码的一部分
data segment
arr1 db 12,34,56,42
ends
stack segment
dw 128 dup(0)
ends
code segment
start:
mov ax,@data
mov ds,ax
mov si,offset arr1
push [si]
sort:
mov cx,-1
cmp [si],-50
jg reverse
inc si
jmp sort
reverse:
mov al,[si]
rol al,cl
mov [si],al
inc si
jmp sort
mov ax, 4c00h
int 21h
ends
end start
答案 0 :(得分:3)
对于要解决您的问题的例程,首先需要将要反转的数字转换为个位数。以相反的顺序读取这些单个数字,然后将其转换回整个数字。
要将任何数字转换为单个数字的数组,必须将数字除以10,直到商为0。每次除法时,余数就是要保存的数字,商就是如果不为零,则将新数字除以10。例如,如果我想将109转换为单个数字的数组,我可以这样做:
109 / 10 = quotient(10) , remainder(9) = 9 is the single digit of this round
10 / 10 = q(1) , r(0) = 0 is the single digit of this round
1 / 10 = q(0) , r(1) = 1 is the single digit of this round
在上面的示例中,如果我从上到下读取每个数字,我的数字就会倒过来。如果我从头到尾阅读,那是我的原始号码。
此代码通过保存符号(最后恢复)并使用除法和乘法部分的无符号绝对值来处理负数。
下面是我在汇编中编写的示例代码。在示例代码中,revN是您要查看的例程,printN只是一个支持例程。
该代码是在Linux上以NASM汇编语言和32位模式编写的,直接进行了int 0x80
系统调用。您可以使用https://www.tutorialspoint.com/compile_assembly_online.php或https://www.jdoodle.com/compile-assembler-nasm-online/对其进行测试。请注意,该代码将不会检查溢出。
感谢Peter Cordes对答案代码的反馈。有了反馈,我决定优化答案代码。尽管如此,我还是决定保留代码的第一个版本,并将revN的优化版本发布在第一个代码下,因为这样更容易理解第一个代码中的步骤。
对于第二版的revN,由于性能而使负数和正数取反,因此使用“ neg”指令代替“ not”和“ inc”。另外,使用“ xor r32,r32”代替“ mov r32,0”。除此之外,revN的第二个不会将数字转换为单位数chars并将其存储在数组中。而是将数字转换回数字,同时将数字解析为单个数字。这是因为在将109解析为数字时,首先是9,然后是0,然后是1。因此,如果在解析它们时将其转换回数字,则等于((((9 * 10)+ 0)* 10)+ 1。示例代码答案2仅包含revN例程。要立即对其进行测试,请将答案1的revN部分替换为答案2的revN。
section .data
sys_write equ 4 ;EAX
sys_exit equ 1
stdout equ 1 ;EBX
cKernal equ 0x80 ;INT
;printN CONST
min32 equ -2147483648 ;Minimum negative value for signed 32
printN_chunk equ 12
section .text
global _start ; Must be declared for using gcc
_start: ; Tell linker entry point
; Test 1
mov eax, 45 ; N to be reverse
mov edi, -15 ; If N <= to this don't reverse
call revN
; eax is N to print
mov edx, dword 10 ; EndL
call printN
; Test 2
mov eax, 0 ; N to be reverse
mov edi, -15 ; If N <= to this don't reverse
call revN
; eax is N to print
mov edx, dword 10 ; EndL
call printN
; Test 3
mov eax, -13 ; N to be reverse
mov edi, -15 ; If N <= to this don't reverse
call revN
; eax is N to print
mov edx, dword 10 ; EndL
call printN
; Test 4
mov eax, -18 ; N to be reverse
mov edi, -15 ; If N <= to this don't reverse
call revN
; eax is N to print
mov edx, dword 10 ; EndL
call printN
; Test 5
mov eax, -78945 ; N to be reverse
mov edi, -100000 ; If N <= to this don't reverse
call revN
; eax is N to print
mov edx, dword 10 ; EndL
call printN
; Test 6
mov eax, min32 ; N to be reverse
mov edi, min32 ; If N <= to this don't reverse
call revN
; eax is N to print
mov edx, dword 10 ; EndL
call printN
; Test 7
mov eax, 987542478 ; N to be reverse
mov edi, min32 ; If N <= to this don't reverse
call revN
; eax is N to print
mov edx, dword 10 ; EndL
call printN
; Exit
mov eax, sys_exit ;system call number (sys_exit)
int cKernal ;call kernel
revN:
; WARNING THIS DOES NOT CHECK FOR OVERFLOW
; EAX ARG Number to be reversed
; EDI ARG Number to be compared to
; if EAX <= EDI return value = EAX
; EAX is also the return value
; Each digit is stored as word, so
; so that they are exactly the size of
; an extended register
push ebp ; Save ebp
; EBP is used as pointer after len.
sub esp, 48 ; Set stack pointer.
; Reserve memory
; 40 = 10(n) * 4
; 4 = signbit
; 4 = len
; 48 byte
lea ebp, [esp+8] ; Set ebp to before signbit
cmp eax, edi ; Compare eax(arg1) to edi(arg2)
jle revN_exit ; If n <= edi return n
cmp eax, 0 ; Compare eax to zero
jl revN_2Str_nNeg ; If n is neg turn it to pos
je revN_exit ; If n is 0 return n
mov dword [esp+4], 0 ; If n not neg set sign bit to 0
; This is just to ensure
; accuracy, usually this doesn't have
; to be set as we haven't touch
; the stack.
revN_2Str:
; revN_2Str(in reverse)
; Register Ultilization:
; edx (div)
; Save eax (div)
; Save ebx (counter)
; Save ecx (divider)
; First number is converted to string in reverse
mov ebx, 0 ; Counter (Position counter)
; to get digit divide to 4.
mov ecx, 10 ; Divider
mov edx, 0 ; EDX:EAX /
revN_2Str_div:
idiv ecx ; EDX:EAX / 10 = EDX(r) : EAX(q)
mov [ebp + ebx], edx ; move the result to stack
; edx will always be leass than 10
mov edx, 0 ; reset edx
add ebx, 4 ; increase counter
cmp eax, 0 ; compare quotient to 0
jne revN_2Str_div ; reloop if quotient is not 0
; if Done copy the length
mov dword [esp], ebx ; to the stack location of esp
mov eax, dword [ebp] ; Move the first digit into eax
cmp dword [esp], 4 ; Compare len against 4
jle revN_exit ; If one digit or 4 byte we are done
mov ebx, 4 ; Set the counter for revN_toNum
revN_toNum:
; revN_toNum(Convert str2int32)
; This does not check for overflow
; Register Ultilization:
; eax (n to be return)
; edx = eax << 3
; Save ebx (counter)
mov edx, eax ; Copy eax to edx
shl eax, 1 ; Shift eax x 1 left = eax * 2
shl edx, 3 ; shift edx x 3 left = edx * 8
add eax, edx ; EAX + EDX = EAX * 10
add eax, dword [ebp + ebx] ; ADD another digit
add ebx, 4 ; increase counter
cmp ebx, dword [esp] ; check counter
jl revN_toNum ; reloop if counter < len
cmp dword [esp+4], 1 ; Check sign bit
je revN_toNum_nNeg ; If n is neg turn it to neg
revN_exit:
add esp, 48 ; reset stack pointer
pop ebp ; Restore ebp
ret
revN_2Str_nNeg:
; Turn n to negative
not eax ; Bitwise not
inc eax ; Add 1
mov dword [esp+4], 1 ; Set sign bit
jmp revN_2Str ; convert to str
revN_toNum_nNeg:
not eax ; Turn n to positive
inc eax ; Add 1
jmp revN_exit ; Jump to exit
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; Below is the printN function ;
; Use where can't use printf ;
; or similar function ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
printN:
; EAX ARG Number to print
; EDX ARG end of line < 255 or ending char
; before calling this
push ebp ; Push ebp onto the stack
mov ebp, esp ; Top of stack
sub esp, printN_chunk ; 12 = sign + 10 digits + endl
; edx (div)
; eax (div)
; ebx (counter)
; ecx (divider)
; esi (0/1)(pos/neg)
dec ebp ; ebp decrement 1
mov byte [ebp], dl ; Add end line
mov ebx, 1 ; Counter start at 1 (endl)
mov ecx, 10 ; Divider
cmp eax, 0 ; Compare eax to 0
jl printN_nNeg ; If n is neg
je printN_n0 ; If n is 0
mov esi, 0 ; Set positive flag
printN_start:
mov edx, 0 ; EDX:EAX /
printN_div:
idiv ecx ; EDX:EAX / 10 = EDX(r) : EAX(q)
or edx, 48 ; or to get the ascii code
dec ebp ; decrease ebp
mov byte [ebp], dl ; push to stack
inc ebx ; increase counter
mov edx, 0 ; reset edx
cmp eax, 0 ; compare quotient to 0
jne printN_div ; reloop if quotient is not 0
cmp esi, 0 ; Check if n positive
je printN_print ; If positive print
dec ebp ; ebp decrease
mov byte [ebp], '-' ; Else, add a neg sign
inc ebx ; increase counter
printN_print:
mov edx, ebx ; strlen = message length
mov ecx, ebp ; Message to write
mov ebx, stdout ; File descriptor (stdout)
mov eax, sys_write ; System call number (sys_write)
int cKernal ; Call kernel
printN_exit:
add esp, printN_chunk ; Reset esp
pop ebp ; Restored ebp
ret
printN_nNeg:
cmp eax, min32 ; Compare arg vs minimum neg value.
je printN_nMin ; If it is just print the neg value.
not eax ; Bitwise not
inc eax ; Add 1
mov esi, 1 ; Set neg flag
jmp printN_start ; Start
printN_n0:
dec ebp ; ebp decrease
mov byte [ebp], '0' ; push a 0
inc ebx ; increase counter
jmp printN_print ; jump to print
printN_nMin:
add ebx, 11 ; Add 11 chars to counter
; Then push min number on to the stack
; backward
sub ebp, 4
mov dword [ebp], dword "3648"
sub ebp, 4
mov dword [ebp], dword "4748"
sub ebp, 2
mov word [ebp], word "21"
sub ebp, 1
mov byte [ebp], byte "-"
jmp printN_print ; Jump to print
revN:
; INFO
; @DESCRIPTION - Reverse a number
;
; @NOTE - This routine can probably be optimized futher using
; a high performance base 10 division algorithm.
;
; @WARNING - THIS DOES NOT CHECK FOR OVERFLOW
; REG SAVE AND RESTORE
; edi
; esi
; ARGS
; eax - number to be reversed
; edi - Number to be compared to
; RETURNS
; eax - the reversed input number or the input number
; itself if "input eax <= input edi".
; REGISTER UTILIZATION
; edx (div)
; eax (div)
; ebx (negative flag)
; ecx (divider)
; edi (temporary answer)
; esi (temporary variable)
; END INFO
push edi ; SAVE edi
push esi ; SAVE esi
cmp eax, edi ; Compare eax(arg1) to edi(arg2)
jle revN_exit ; If n <= edi return n
cmp eax, 0 ; Compare eax to zero
jl revN_start_nNeg ; If n is neg turn it to positive
je revN_exit ; If n is 0 return n
xor ebx, ebx ; Set negative flag = 0
revN_start:
mov ecx, 10 ; Divider
xor edx, edx ; edx = 0
; This below stay outside of loop is to save calculation
; on the first digit and also on one digit input
idiv ecx ; EDX:EAX / 10 = EDX(r) : EAX(q)
mov edi, edx ; Move first digit into edi
cmp eax, 0 ; Compare quotient to 0
je revN_fin ; Finish if quotient is 0
revN_loop:
xor edx, edx ; Reset edx to 0
idiv ecx ; EDX:EAX / 10 = EDX(r) : EAX(q)
mov esi, edi ; Copy edi to esi
shl edi, 3 ; Shift edi x 3 left = eax * 8
shl esi, 1 ; shift esi x 1 left = esi * 2
add edi, esi ; EDI + ESI = EDI * 10
add edi, edx ; Add another digit
cmp eax, 0 ; Compare quotient to 0
jne revN_loop ; Reloop if quotient is not zero
revN_fin:
mov eax, edi ; Move temprary answer to
; return register.
cmp ebx, 1 ; Check negative flag
je revN_fin_nNeg ; If n was negative turn it
; to negative.
revN_exit:
pop esi ; RESTORE esi
pop edi ; RESTORE edi
ret
revN_start_nNeg:
neg eax ; Turn n to positive
mov ebx, 1 ; Set sign bit
jmp revN_start ; Jump to start
revN_fin_nNeg:
neg eax ; Turn n to negative
jmp revN_exit ; Jump to exit