Szukaj…


Uwagi

Konwersja ciągów znaków na liczby całkowite jest jednym z typowych zadań.

Tutaj pokażemy, jak przekonwertować ciąg dziesiętny na liczby całkowite.

Kod Psuedo, aby to zrobić to:

function string_to_integer(str):
    result = 0
    for (each characters in str, left to right):
        result = result * 10
        add ((code of the character) - (code of character 0)) to result
    return result

Radzenie sobie z ciągami szesnastkowymi jest nieco trudniejsze, ponieważ kody znaków zwykle nie są ciągłe w przypadku wielu typów znaków, takich jak cyfry (0–9) i alfabety (af i AF). Kody znaków są zazwyczaj ciągłe, gdy mamy do czynienia tylko z jednym typem znaków (tutaj zajmiemy się cyframi), więc będziemy mieli do czynienia tylko ze środowiskami, w których kody znaków dla cyfr są ciągłe.

Montaż IA-32, GAS, konwencja wywoływania cdecl

# make this routine available outside this translation unit
.globl string_to_integer

string_to_integer:
    # function prologue
    push %ebp
    mov %esp, %ebp
    push %esi

    # initialize result (%eax) to zero
    xor %eax, %eax
    # fetch pointer to the string
    mov 8(%ebp), %esi

    # clear high bits of %ecx to be used in addition
    xor %ecx, %ecx
    # do the conversion
string_to_integer_loop:
    # fetch a character
    mov (%esi), %cl
    # exit loop when hit to NUL character
    test %cl, %cl
    jz string_to_integer_loop_end
    # multiply the result by 10
    mov $10, %edx
    mul %edx
    # convert the character to number and add it
    sub $'0', %cl
    add %ecx, %eax
    # proceed to next character
    inc %esi
    jmp string_to_integer_loop
string_to_integer_loop_end:

    # function epilogue
    pop %esi
    leave
    ret

Ten kod w stylu GAS konwertuje ciąg dziesiętny podany jako pierwszy argument, który jest wypychany na stos przed wywołaniem tej funkcji, na liczbę całkowitą i zwraca go przez %eax . Wartość %esi jest zapisywana, ponieważ jest rejestrem zapisującym adresata i jest używana.

Przepełnienie / zawijanie i nieprawidłowe znaki nie są sprawdzane w celu uproszczenia kodu.

W języku C można użyć tego kodu w ten sposób (zakładając, że unsigned int i wskaźniki mają długość 4 bajtów):

#include <stdio.h>

unsigned int string_to_integer(const char* str);

int main(void) {
    const char* testcases[] = {
        "0",
        "1",
        "10",
        "12345",
        "1234567890",
        NULL
    };
    const char** data;
    for (data = testcases; *data != NULL; data++) {
        printf("string_to_integer(%s) = %u\n", *data, string_to_integer(*data));
    }
    return 0;
}

Uwaga: w niektórych środowiskach dwa string_to_integer w kodzie asemblera muszą zostać zmienione na _string_to_integer (dodaj podkreślenie), aby umożliwić działanie z kodem C.

MS-DOS, funkcja TASM / MASM do odczytu 16-bitowej liczby całkowitej bez znaku

Odczytaj 16-bitową liczbę całkowitą bez znaku z wejścia.

Ta funkcja korzysta z usługi przerwań Int 21 / AH = 0Ah do odczytu zbuforowanego ciągu.
Zastosowanie buforowanego ciągu pozwoliło użytkownikowi sprawdzić, co wpisał przed przekazaniem go do programu w celu przetworzenia.
Odczytywanych jest maksymalnie sześć cyfr (ponieważ 65535 = 2 16 - 1 ma sześć cyfr).

Oprócz wykonania standardowej konwersji liczb na liczby ta funkcja wykrywa również nieprawidłowe dane wejściowe i przepełnienie (liczba zbyt duża, aby zmieściła się w 16 bitach).

Zwróć wartości

Funkcja zwraca liczbę odczytaną w AX . Flagi ZF , CF , OF informują, czy operacja zakończyła się powodzeniem, czy nie i dlaczego.

Błąd TOPÓR ZF CF Z
Żaden 16-bitowa liczba całkowita Zestaw Nie ustawiony Nie ustawiony
Niepoprawne dane wejściowe Częściowo przekonwertowana liczba do ostatniej napotkanej poprawnej cyfry Nie ustawiony Zestaw Nie ustawiony
Przelewowy 7fffh Nie ustawiony Zestaw Zestaw

ZF można wykorzystać do szybkiego odróżnienia ważnych i nieprawidłowych danych wejściowych od siebie.

Stosowanie

call read_uint16
jo _handle_overflow            ;Number too big (Optional, the test below will do)
jnz _handle_invalid            ;Number format is invalid

;Here AX is the number read

Kod

;Returns:
  ;
  ;If the number is correctly converted:
  ;   ZF = 1, CF = 0, OF = 0
  ;   AX = number
  ;
  ;If the user input an invalid digit:
  ;   ZF = 0, CF = 1, OF = 0
  ;   AX = Partially converted number
  ;
  ;If the user input a number too big
  ;   ZF = 0, CF = 1, OF = 1
  ;   AX = 07fffh
  ;
  ;ZF/CF can be used to discriminate valid vs invalid inputs
  ;OF can be used to discrimate the invalid inputs (overflow vs invalid digit)
  ;
  read_uint16:
    push bp   
    mov bp, sp

    ;This code is an example in Stack Overflow Documentation project.
    ;x86/Converting Decimal strings to integers


    ;Create the buffer structure on the stack

    sub sp, 06h                ;Reserve 6 byte on the stack (5 + CR)
    push 0006h                 ;Header

    push ds
    push bx
    push cx
    push dx

    ;Set DS = SS

    mov ax, ss
    mov ds, ax                           
                  

    ;Call Int 21/AH=0A

    lea dx, [bp-08h]            ;Address of the buffer structure
    mov ah, 0ah
    int 21h

    ;Start converting

    lea si, [bp-06h]
    xor ax, ax
    mov bx, 10
    xor cx, cx

   _r_ui16_convert:

    ;Get current char

    mov cl, BYTE PTR [si]
    inc si

    ;Check if end of string

    cmp cl, CR_CHAR
    je _r_ui16_end                      ;ZF = 1, CF = 0, OF = 0
   
    ;Convert char into digit and check

    sub cl, '0'
    jb _r_ui16_carry_end                ;ZF = 0, CF = 1, OF = X -> 0
    cmp cl, 9
    ja _r_ui16_carry_end                ;ZF = 0, CF = 0 -> 1, OF = X -> 0


    ;Update the partial result (taking care of overflow)

    ;AX = AX * 10
    mul bx

    ;DX:AX = DX:AX + CX
    add ax, cx
    adc dx, 0
       
    test dx, dx
   jz _r_ui16_convert            ;No overflow
   
    ;set OF and CF
    mov ax, 8000h
    dec ax                           
    stc

   jmp _r_ui16_end                      ;ZF = 0, CF = 1, OF = 1
    
   _r_ui16_carry_end:

    or bl, 1                ;Clear OF and ZF
    stc                     ;Set carry
 
    ;ZF = 0, CF = 1, OF = 0

   _r_ui16_end:
    ;Don't mess with flags hereafter!

    pop dx
    pop cx
    pop bx
    pop ds

    mov sp, bp

    pop bp
    ret

    CR_CHAR EQU 0dh

Portowanie NASM

Aby przenieść kod do NASM, usuń słowo kluczowe PTR z dostępu do pamięci (np. mov cl, BYTE PTR [si] staje się mov cl, BYTE [si] )

MS-DOS, funkcja TASM / MASM do drukowania 16-bitowej liczby w postaci binarnej, czwartorzędowej, ósemkowej, szesnastkowej

Wydrukuj liczbę w postaci binarnej, czwartorzędowej, ósemkowej, szesnastkowej i potęgi ogólnej dwóch

Wszystkie zasady, które są potęgami dwójki, tak jak binarnego (2: 1), czwartorzędowych (2, 2), ósemkowej (2, 3), szesnastkowe (2 4) zasady, mają całkowitą liczbą bitów na cyfrę 1.
Tak więc, aby odzyskać każdą cyfrę 2 cyfry, po prostu łamiemy grupę wprowadzającą liczby n bitów, zaczynając od LSb (po prawej).
Na przykład dla czwartorzędowej bazy dzielimy liczbę 16-bitową na grupy po dwa bity. Istnieje 8 takich grup.
Nie cała moc dwóch zasad ma integralną liczbę grup, która mieści 16 bitów; na przykład, ósemkowa zasada ma 5 grup po 3 bity, które odpowiadają 3,5 • = 15 bitów na 16, pozostawiając częściową grupę 1 bit 3 .

Algorytm jest prosty, izolujemy każdą grupę za pomocą zmiany, po której następuje operacja AND .
Ta procedura działa dla każdego rozmiaru grup lub, innymi słowy, dla dowolnej podstawowej potęgi dwóch.

Aby wyświetlić cyfry we właściwej kolejności, funkcja zaczyna się od wyodrębnienia najbardziej znaczącej grupy (najbardziej na lewo), dlatego ważne jest, aby wiedzieć: a) ile bitów D to grupa, i b) pozycja bitu S, gdzie najbardziej na lewo grupa się zaczyna.
Wartości te są wstępnie obliczane i przechowywane w starannie spreparowanych stałych.

Parametry

Parametry należy wcisnąć na stos.
Każdy z nich ma 16-bitową szerokość.
Są pokazane w kolejności push.

Parametr Opis
N. Liczba do konwersji
Baza Podstawa do użycia wyrażona za pomocą stałych BASE2 , BASE4 , BASE8 i BASE16
Drukuj wiodące zera Jeśli zero, nie są drukowane nieistotne zera, w przeciwnym razie są. Liczba 0 jest jednak drukowana jako „0”

Stosowanie

push 241
push BASE16
push 0
call print_pow2              ;Prints f1

push 241
push BASE16
push 1
call print_pow2              ;Prints 00f1

push 241
push BASE2
push 0
call print_pow2              ;Prints 11110001

Uwaga dla użytkowników TASM : Jeśli wstawisz stałe zdefiniowane za pomocą EQU po kodzie, który ich używa, włącz wieloprzebieg z flagą /m TASM, w przeciwnym razie uzyskasz przesłonięcie potrzeb w zakresie odniesienia do przodu .

Kod

;Parameters (in order of push):
;
;number
;base (Use constants below)
;print leading zeros
print_pow2:
 push bp
 mov bp, sp

 push ax
 push bx
 push cx
 push dx
 push si
 push di

 ;Get parameters into the registers

 ;SI = Number (left) to convert
 ;CH = Amount of bits to shift for each digit (D)
 ;CL = Amount od bits to shift the number (S)
 ;BX = Bit mask for a digit

 mov si, WORD PTR [bp+08h]
 mov cx, WORD PTR [bp+06h]            ;CL = D, CH = S

 ;Computes BX = (1 << D)-1
 
 mov bx, 1
 shl bx, cl
 dec bx

 xchg cl, ch              ;CL = S, CH = D

_pp2_convert:
 mov di, si
 shr di, cl
 and di, bx         ;DI = Current digit

 or WORD PTR [bp+04h], di             ;If digit is non zero, [bp+04h] will become non zero
                      ;If [bp+04h] was non zero, result is non zero
 jnz _pp2_print                       ;Simply put, if the result is non zero, we must print the digit

 
 ;Here we have a non significant zero
 ;We should skip it BUT only if it is not the last digit (0 should be printed as "0" not
 ;an empty string)

 test cl, cl
 jnz _pp_continue


_pp2_print:
 ;Convert digit to digital and print it

 
 mov dl, BYTE PTR [DIGITS + di]
 mov ah, 02h
 int 21h


_pp_continue:
 ;Remove digit from the number

 sub cl, ch
jnc _pp2_convert

 pop di
 pop si
 pop dx
 pop cx
 pop bx
 pop ax

 pop bp
 ret 06h

Dane

This data must be put in the data segment, the one reached by `DS`.

DIGITS    db    "0123456789abcdef"

;Format for each WORD is S D where S and D are bytes (S the higher one)
;D = Bits per digit  --> log2(BASE)
;S = Initial shift count --> D*[ceil(16/D)-1]

BASE2    EQU    0f01h
BASE4    EQU    0e02h
BASE8    EQU    0f03h
BASE16    EQU    0c04h

Portowanie NASM

Aby przenieść kod do NASM, usuń słowo kluczowe PTR z dostępu do pamięci (np. mov si, WORD PTR [bp+08h] staje się mov si, WORD PTR [bp+08h] )

Rozszerzenie funkcji

Funkcję można łatwo rozszerzyć na dowolną bazę do 2 255 , chociaż każda baza powyżej 2 16 wydrukuje tę samą cyfrę, ponieważ liczba to tylko 16 bitów.

Aby dodać bazę:

  1. Zdefiniuj nową stałą BASEx gdzie x wynosi 2 n .
    Dolny bajt, o nazwie D , to D = n .
    Górny bajt, o nazwie S , jest pozycją, w bitach, wyższej grupy. Można go obliczyć jako S = n · (⌈16 / n ⌉ - 1).
  2. Dodaj niezbędne cyfry do ciągu DIGITS .

Przykład: dodanie podstawy 32

Mamy D = 5 i S = 15, więc definiujemy BASE32 EQU 0f05h .
Następnie dodajemy szesnaście kolejnych cyfr: DIGITS db "0123456789abcdefghijklmnopqrstuv" .

Jak powinno być jasne, cyfry można zmienić, edytując ciąg DIGITS .


1 Jeśli B jest bazą, to ma definicję B cyfr na definicję. Liczba bitów na cyfrę wynosi zatem log 2 ( B ). Dla mocy dwóch zasad upraszcza to logarytm 2 (2 n ) = n, który z definicji jest liczbą całkowitą.

2 W tym kontekście domyślnie przyjmuje się, że rozważana zasada jest potęgą dwóch zasad 2 n .

3 Aby baza B = 2 n miała całkowitą liczbę grup bitów, musi to być n | 16 ( n dzieli 16). Ponieważ jedynym czynnikiem w 16 jest 2, musi być tak, że sama n jest potęgą dwóch. Zatem B ma postać 2 2 k lub równoważnie log 2 ( log 2 ( B )) musi być liczbą całkowitą.

MS-DOS, TASM / MASM, funkcja drukowania 16-bitowej liczby dziesiętnej

Wydrukuj 16-bitową liczbę bez znaku w systemie dziesiętnym

Do wydrukowania cyfr używana jest usługa przerwania Int 21 / AH = 02h .
Średnia przeliczanie liczby z cyfrą jest wykonywana z div instrukcji, dzielna jest początkowo najwyższa moc dziesięciu oprawki 16 4 bitów (10) i redukuje się w celu obniżenia mocy przy każdej iteracji.

Parametry

Parametry są pokazane w kolejności push.
Każdy ma 16 bitów.

Parametr Opis
numer 16-bitowa liczba bez znaku do wydrukowania w systemie dziesiętnym
pokaż zera wiodące Jeśli 0 nie są drukowane nieistotne zera, w przeciwnym razie są. Liczba 0 jest zawsze drukowana jako „0”

Stosowanie

push 241
push 0
call print_dec          ;prints 241

push 56
push 1
call print_dec          ;prints 00056

push 0
push 0
call print_dec          ;prints 0

Kod

;Parameters (in order of push):
;
;number
;Show leading zeros
print_dec:
 push bp
 mov bp, sp

 push ax
 push bx
 push cx
 push dx

 ;Set up registers:
 ;AX = Number left to print
 ;BX = Power of ten to extract the current digit
 ;DX = Scratch/Needed for DIV
 ;CX = Scratch

 mov ax, WORD PTR [bp+06h]
 mov bx, 10000d
 xor dx, dx

_pd_convert:     
 div bx                           ;DX = Number without highmost digit, AX = Highmost digit
 mov cx, dx                       ;Number left to print

 ;If digit is non zero or param for leading zeros is non zero
 ;print the digit
 or WORD PTR [bp+04h], ax
 jnz _pd_print

 ;If both are zeros, make sure to show at least one digit so that 0 prints as "0"
 cmp bx, 1
 jne _pd_continue

_pd_print:

 ;Print digit in AL

 mov dl, al
 add dl, '0'
 mov ah, 02h
 int 21h

_pd_continue:
 ;BX = BX/10
 ;DX = 0

 mov ax, bx
 xor dx, dx
 mov bx, 10d
 div bx
 mov bx, ax

 ;Put what's left of the number in AX again and repeat...
 mov ax, cx

 ;...Until the divisor is zero
 test bx, bx
jnz _pd_convert

 pop dx
 pop cx
 pop bx
 pop ax

 pop bp
 ret 04h

Portowanie NASM

Aby przenieść kod do NASM, usuń słowo kluczowe PTR z dostępu do pamięci (np. mov ax, WORD PTR [bp+06h] staje się mov ax, WORD [bp+06h] )



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow