Intel x86 Assembly Language & Microarchitecture
Konwersja ciągów dziesiętnych na liczby całkowite
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ę:
- Zdefiniuj nową stałą
BASExgdzie 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). - 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] )