如何反转数字的十进制数字?

时间:2019-11-30 20:12:30

标签: assembly x86-16 emu8086

我想从汇编中的数组中反转一个数字。

如果数字大于-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


1 个答案:

答案 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.phphttps://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。

答案1

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

答案2

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