Intel x86 Assembly Language & Microarchitecture
Decimale tekenreeksen omzetten naar gehele getallen
Zoeken…
Opmerkingen
Strings converteren naar gehele getallen is een van de meest voorkomende taken.
Hier laten we zien hoe u decimale tekenreeksen kunt converteren naar gehele getallen.
Psuedo-code om dit te doen is:
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
Omgaan met hexadecimale tekenreeksen is een beetje moeilijker omdat tekencodes meestal niet continu zijn bij het omgaan met meerdere tekensoorten, zoals cijfers (0-9) en alfabetten (af en AF). Tekencodes zijn meestal ononderbroken wanneer het om slechts één type tekens gaat (we behandelen hier cijfers), dus we behandelen alleen omgevingen waarin tekencodes voor cijfers ononderbroken zijn.
IA-32 assemblage, GAS, cdecl oproepconventie
# 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
Deze GAS-stijl code converteert decimale tekenreeksen als eerste argument, die op de stapel worden gepusht voordat deze functie wordt %eax , naar geheel getal en retourneert het via %eax . De waarde van %esi wordt opgeslagen omdat het callee-save register is en wordt gebruikt.
Overloop / omloop en ongeldige tekens worden niet gecontroleerd om de code eenvoudig te maken.
In C kan deze code als volgt worden gebruikt (ervan uitgaande dat unsigned int en aanwijzers 4 bytes lang zijn):
#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;
}
Opmerking: in sommige omgevingen moeten twee string_to_integer in de string_to_integer worden gewijzigd in _string_to_integer (onderstrepingsteken toevoegen) om het met C-code te laten werken.
MS-DOS, TASM / MASM-functie om een 16-bits geheel getal zonder teken te lezen
Lees een 16-bits geheel getal zonder teken van invoer.
Deze functie gebruikt de interrupt-service Int 21 / AH = 0Ah voor het lezen van een gebufferde string.
Door het gebruik van een gebufferde string kon de gebruiker bekijken wat hij had getypt voordat hij deze aan het programma doorvoerde voor verwerking.
Maximaal zes cijfers worden gelezen (als 65535 = 2 16 - 1 heeft zes cijfers).
Naast het uitvoeren van de standaardconversie van cijfer naar nummer , detecteert deze functie ook ongeldige invoer en overloop (aantal te groot om 16 bits te passen).
Retourwaarden
De functie retourneert het nummer dat is gelezen in AX . De vlaggen ZF , CF , OF vertellen of de operatie succesvol is voltooid of niet en waarom.
| Fout | BIJL | ZF | CF | VAN |
|---|---|---|---|---|
| Geen | Het 16-bits gehele getal | reeks | Niet ingesteld | Niet ingesteld |
| Ongeldige invoer | Het gedeeltelijk geconverteerde nummer, tot het laatst aangetroffen geldige cijfer | Niet ingesteld | reeks | Niet ingesteld |
| Overloop | 7fffh | Niet ingesteld | reeks | reeks |
De ZF kan worden gebruikt om geldige versus ongeldige ingangen snel uit elkaar te houden.
Gebruik
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
Code
;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
NASM-porten
Als u de code wilt overdragen naar NASM, verwijdert u het PTR trefwoord uit geheugentoegangen (bijv. mov cl, BYTE PTR [si] wordt mov cl, BYTE [si] )
MS-DOS, TASM / MASM-functie voor het afdrukken van een 16-bits getal in binair, quaternair, octaal, hex
Druk een getal af in binair, quaternair, octaal, hexadecimaal en een algemene macht van twee
Alle basen die een macht van twee zijn, zoals de binaire (2 1 ), quaternaire (2 2 ), octale (2 3 ), hexadecimale (2 4 ) basen, hebben een integraal aantal bits per cijfer 1 .
Dus om elk cijfer 2 van een cijfer op te halen, breken we eenvoudig de nummer-introgroep van n bits beginnend bij de LSb (rechts).
Voor de quaternaire basis breken we bijvoorbeeld een 16-bits getal in groepen van twee bits. Er zijn 8 van dergelijke groepen.
Niet alle macht van twee basen heeft een integraal aantal groepen dat op 16 bits past; de octale basis heeft bijvoorbeeld 5 groepen van 3 bits die goed zijn voor 3-5 = 15 bits van de 16, waardoor een gedeeltelijke groep van 1 bit 3 overblijft.
Het algoritme is eenvoudig, we isoleren elke groep met een shift gevolgd door een AND- bewerking.
Deze procedure werkt voor elke grootte van de groepen of, met andere woorden, voor elke basismacht van twee.
Om de cijfers in de juiste volgorde weer te geven, begint de functie door de meest significante groep (de meest linkse) te isoleren, daarom is het belangrijk om te weten: a) hoeveel bits D een groep is en b) de bitpositie S waar de meest linkse groep begint.
Deze waarden worden vooraf berekend en opgeslagen in zorgvuldig vervaardigde constanten.
parameters
De parameters moeten op de stapel worden geplaatst.
Elke is 16-bit breed.
Ze worden getoond in volgorde van push.
| Parameter | Beschrijving |
|---|---|
| N | Het om te zetten getal |
| Baseren | De te gebruiken basis uitgedrukt met behulp van de constanten BASE2 , BASE4 , BASE8 en BASE16 |
| Voorloopnullen afdrukken | Als nul geen niet-significante nullen worden afgedrukt, anders zijn ze. Het nummer 0 wordt echter afgedrukt als "0" |
Gebruik
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
Opmerking voor TASM-gebruikers : als u de met EQU gedefinieerde constanten achter de code plaatst die ze gebruikt, schakelt u multi-pass in met de vlag /m van TASM, anders krijgt u verwijzing naar voorrang negeren .
Code
;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
Gegevens
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
NASM-porten
Als u de code wilt overdragen naar NASM, verwijdert u het PTR-trefwoord uit geheugentoegangen (bijv. mov si, WORD PTR [bp+08h] wordt mov si, WORD PTR [bp+08h] )
Uitbreiding van de functie
De functie kan eenvoudig worden uitgebreid tot elke basis tot 2 255 , hoewel elke basis boven 2 16 hetzelfde cijfer zal afdrukken omdat het nummer slechts 16 bits is.
Een basis toevoegen:
- Definieer een nieuwe constante
BASExwaarbij x 2 n is .
De lagere byte, genaamd D , is D = n .
De bovenste byte, genaamd S , is de positie, in bits, van de hogere groep. Het kan worden berekend als S = n · (⌈16 / n ⌉ - 1). - Voeg de benodigde cijfers toe aan de tekenreeks
DIGITS.
Voorbeeld: base 32 toevoegen
We hebben D = 5 en S = 15, dus definiëren we BASE32 EQU 0f05h .
We voegen vervolgens nog zestien cijfers toe: DIGITS db "0123456789abcdefghijklmnopqrstuv" .
Zoals duidelijk moet zijn, kunnen de cijfers worden gewijzigd door de DIGITS reeks te bewerken.
1 Als B een basis is, heeft deze B cijfers per definitie. Het aantal bits per cijfer is dus log 2 ( B ). Voor macht van twee basen vereenvoudigt dit log 2 (2 n ) = n, wat per definitie een geheel getal is.
2 In deze context wordt impliciet aangenomen dat de beschouwde basis een macht is van twee basis 2 n .
3 Voor een basis B = 2 n om een integraal aantal bitgroepen te hebben, moet het zijn dat n | 16 ( n deelt 16). Omdat de enige factor in 16 2 is, moet het zijn dat n zelf een macht van twee is. Dus B heeft de vorm 2 2 k of equivalent log 2 ( log 2 ( B )) moet een geheel getal zijn.
MS-DOS, TASM / MASM, functie om een 16-bits getal in decimalen af te drukken
Druk een 16-bits niet-ondertekend nummer af in decimalen
De interrupt-service Int 21 / AH = 02h wordt gebruikt om de cijfers af te drukken.
De standaard conversie van nummer cijfer wordt uitgevoerd met div instructie, het deeltal aanvankelijk de hoogste macht van tien fitting 16 bits (10 4) en het wordt gereduceerd tot krachten bij elke iteratie verlagen.
parameters
De parameters worden getoond in volgorde van push.
Elk is 16 bits.
| Parameter | Beschrijving |
|---|---|
| aantal | Het 16-bits niet-ondertekende nummer dat in decimalen moet worden afgedrukt |
| Toon voorloopnullen | Als 0 geen niet-significante nullen worden afgedrukt, zijn ze dat wel. Het nummer 0 wordt altijd afgedrukt als "0" |
Gebruik
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
Code
;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
NASM-porten
Om de code naar NASM te porten, verwijdert u het PTR trefwoord uit geheugentoegangen (bijv. mov ax, WORD PTR [bp+06h] wordt mov ax, WORD [bp+06h] )