Intel x86 Assembly Language & Microarchitecture
Konvertera decimalsträngar till heltal
Sök…
Anmärkningar
Att konvertera strängar till heltal är en av vanliga uppgifter.
Här visar vi hur man konverterar decimalsträngar till heltal.
Psuedo-kod för att göra detta är:
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
Att hantera hexadecimala strängar är lite svårare eftersom teckenkoder vanligtvis inte är kontinuerliga när man hanterar flera teckentyper som siffror (0-9) och alfabet (af och AF). Teckenkoder är vanligtvis kontinuerliga när man bara hanterar en typ av tecken (vi tar upp siffror här), så vi kommer att ta itu med endast miljöer där teckenkoder för siffror är kontinuerliga.
IA-32-montering, GAS, cdecl-samtalskonvention
# 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
Denna GAS-stilkod konverterar decimalsträng som ges som första argument, som skjuts på bunten innan du kallar denna funktion, för att heltala och returnera den via %eax . Värdet på %esi sparas eftersom det är callee-save register och används.
Överflöde / inslagning och ogiltiga tecken kontrolleras inte för att göra koden enkel.
I C kan denna kod användas så här (förutsatt att inte unsigned int och pekare är 4-byte långa):
#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;
}
Obs: i vissa miljöer string_to_integer två string_to_integer i monteringskoden ändras till _string_to_integer (lägg till understreck) för att låta det fungera med C-kod.
MS-DOS, TASM / MASM-funktion för att läsa ett 16-bitars osignerat heltal
Läs ett 16-bitars osignerat heltal från input.
Denna funktion använder avbrottstjänsten Int 21 / AH = 0Ah för att läsa en buffrad sträng.
Användningen av en buffrad sträng låter användaren granska vad de hade skrivit innan den skickades till programmet för bearbetning.
Upp till sex siffror läses (eftersom 65535 = 2 16 - 1 har sex siffror).
Förutom att utföra standardomvandlingen från siffror till nummer upptäcker denna funktion också ogiltig inmatning och överflöde (nummer för stort för att passa 16 bitar).
Returnera värden
Funktionen returnerar numret som läses i AX . Flaggorna ZF , CF , OF berättar om operationen har lyckats eller inte och varför.
| Fel | YXA | ZF | CF | AV |
|---|---|---|---|---|
| Ingen | 16-bitars heltal | Uppsättning | Inte inställd | Inte inställd |
| Felaktig input | Det delvis konverterade numret upp till den sista giltiga siffran | Inte inställd | Uppsättning | Inte inställd |
| Svämma över | 7fffh | Inte inställd | Uppsättning | Uppsättning |
ZF kan användas för att snabbt skilja ifrån giltiga kontra ogiltiga ingångar.
Användande
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
Koda
;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-porting
För att porta koden till NASM, ta bort PTR nyckelordet från minnesåtkomst (t.ex. mov cl, BYTE PTR [si] blir mov cl, BYTE [si] )
MS-DOS, TASM / MASM-funktion för att skriva ut ett 16-bitarsnummer i binär, kvartär, oktal, hex
Skriv ut ett nummer i binär, kvartär, oktal, hexadecimal och en generell effekt av två
Alla baser som har en effekt av två, som den binära (2 1 ), kvartära (2 2 ), oktala (2 3 ), hexadecimala (2 4 ) baser, har ett integrerat antal bitar per siffra 1 .
För att hämta varje siffra 2 i en siffra bryter vi helt enkelt antalet introgrupp med n bitar som börjar från LSb (höger).
Till exempel för den kvartära basen bryter vi ett 16-bitarsantal i grupper om två bitar. Det finns 8 av sådana grupper.
Inte all kraft i två baser har ett integrerat antal grupper som passar 16 bitar; till exempel har den oktala basen 5 grupper om 3 bitar som står för 3 5 = 15 bitar av 16, vilket lämnar en partiell grupp av 1 bit 3 .
Algoritmen är enkel, vi isolerar varje grupp med en skift följt av en AND- operation.
Den här proceduren fungerar för alla storlekar i grupperna eller med andra ord för alla baskrafter på två.
För att visa siffrorna i rätt ordning börjar funktionen med att isolera den mest betydelsefulla gruppen (den längst till vänster), varigenom det är viktigt att veta: a) hur många bitar D en grupp är och b) bitpositionen S där den längst till vänster grupp startar.
Dessa värden är förberäknade och lagras i noggrant utformade konstanter.
parametrar
Parametrarna måste tryckas på stacken.
Var och en är 16-bitars bred.
De visas i ordningsföljd.
| Parameter | Beskrivning |
|---|---|
| N | Antalet som ska konverteras |
| Bas | Basen som ska användas uttrycktes med hjälp av konstanterna BASE2 , BASE4 , BASE8 och BASE16 |
| Skriv ut ledande nollor | Om noll skrivs inga icke-signifikanta nollor, annars är de det. Men numret 0 skrivs ut som "0" |
Användande
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
Notera att TASM användare: Om du sätter konstanter som definieras med EQU efter koden som använder dem för, aktivera multi-pass med /m flagga TASM eller du får Framåt referens behöver override.
Koda
;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
Data
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-porting
För att porta koden till NASM, ta bort PTR-nyckelordet från minnesåtkomst (t.ex. mov si, WORD PTR [bp+08h] blir mov si, WORD PTR [bp+08h] )
Utöka funktionen
Funktionen kan enkelt utökas till valfri bas upp till 2 255 , även om varje bas över 2 16 kommer att skriva ut samma siffra som antalet endast är 16 bitar.
Så här lägger du till en bas:
- Definiera en ny konstant
BASExdär x är 2 n .
Den nedre byten, benämnd D , är D = n .
Den övre byten, med namnet S , är positionen, i bitar, för den högre gruppen. Det kan beräknas som S = n · (⌈16 / n ⌉ - 1). - Lägg till nödvändiga siffror i strängen
DIGITS.
Exempel: lägga till bas 32
Vi har D = 5 och S = 15, så vi definierar BASE32 EQU 0f05h .
Vi lägger sedan till ytterligare 16 siffror: DIGITS db "0123456789abcdefghijklmnopqrstuv" .
Som det borde vara klart kan siffrorna ändras genom att redigera DIGITS strängen.
1 Om B är en bas, har den B- siffror per definition. Antalet bitar per siffra är alltså log 2 ( B ). För effekt på två baser förenklar detta att logga 2 (2 n ) = n som är ett heltal per definition.
2 I detta sammanhang antas implicit att basen som beaktas är en kraft av två bas 2 n .
3 För att en bas B = 2 n ska ha ett integrerat antal bitgrupper måste det vara så n | 16 ( n delar 16). Eftersom den enda faktorn i 16 är 2, måste det vara att n i sig är en kraft av två. Så B har formen 2 2 k eller motsvarande log 2 ( log 2 ( B )) måste vara ett heltal.
MS-DOS, TASM / MASM, funktion för att skriva ut ett 16-bitarstal i decimal
Skriv ut ett 16-bitars osignerat nummer i decimal
Avbrottstjänsten Int 21 / AH = 02h används för att skriva ut siffrorna.
Standardomvandlingen från nummer till siffror utförs med div instruktionen, utdelningen är initialt den högsta kraften hos tio passande 16 bitar (10 4 ) och den reduceras till lägre effekt vid varje iteration.
parametrar
Parametrarna visas i ordningsföljd.
Var och en är 16 bitar.
| Parameter | Beskrivning |
|---|---|
| siffra | Det 16-bitars osignerade numret som ska skrivas ut i decimal |
| visa ledande nollor | Om 0 inga signifikanta nollor skrivs ut, annars är de det. Siffran 0 skrivs alltid ut som "0" |
Användande
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
Koda
;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-porting
För att porta koden till NASM, ta bort PTR nyckelordet från minnesåtkomst (t.ex. mov ax, WORD PTR [bp+06h] blir mov ax, WORD [bp+06h] )