Поиск…


замечания

Преобразование строк в целые числа является одной из общих задач.

Здесь мы покажем, как преобразовать десятичные строки в целые числа.

Код Psuedo для этого:

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

Работа с шестнадцатеричными строками немного сложнее, потому что коды символов обычно не являются непрерывными при работе с несколькими типами символов, такими как цифры (0-9) и алфавиты (af и AF). Коды символов обычно непрерывны при работе только с одним типом символов (мы будем иметь дело с цифрами здесь), поэтому мы будем иметь дело только с средами, в которых символьные коды для цифры являются непрерывными.

Сборка IA-32, GAS, соглашение о вызове 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

Этот код в стиле GAS преобразует десятичную строку, указанную как первый аргумент, который помещается в стек перед вызовом этой функции, в целое число и возвращает его через %eax . Значение %esi сохраняется, так как оно является регистром регистрации и используется.

Переполнение / обертка и недопустимые символы не проверяются, чтобы сделать код простым.

В C этот код может быть использован как это (при условии, что unsigned int и указатели имеют длину 4 байта):

#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;
}

Примечание: в некоторых средах два string_to_integer в коде сборки должны быть изменены на _string_to_integer (добавить символ подчеркивания), чтобы позволить ему работать с кодом C.

Функция MS-DOS, TASM / MASM для чтения 16-разрядного целых чисел без знака

Прочитайте 16-разрядное целое число без знака от ввода.

Эта функция использует службу прерываний Int 21 / AH = 0Ah для чтения буферизованной строки.
Использование буферизованной строки позволяет пользователю просмотреть то, что они набрали, прежде чем передать его в программу для обработки.
Чтение до шести цифр (как 65535 = 2 16 - 1 имеет шесть цифр).

Помимо выполнения стандартного преобразования от числа к числу эта функция также обнаруживает недопустимый ввод и переполнение (число слишком велико, чтобы соответствовать 16 битам).

Возвращаемые значения

Функция возвращает число, считанное в AX . Флаги ZF , CF , OF указывают, успешно ли выполнена операция или нет, и почему.

ошибка AX ZF CF О
Никто 16-разрядное целое число Задавать Не установлен Не установлен
Неправильный ввод Частично преобразованное число, до последней действительной цифры Не установлен Задавать Не установлен
перелив 7FFFH Не установлен Задавать Задавать

ZF можно использовать для быстрого указания допустимых и недействительных входов.

использование

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

Код

;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

Чтобы портировать код в NASM, удалите ключевое слово PTR из доступа к памяти (например, mov cl, BYTE PTR [si] становится mov cl, BYTE [si] )

MS-DOS, TASM / MASM для печати 16-разрядного числа в двоичном, четвертичном, восьмеричном, шестнадцатеричном

Распечатайте число в двоичном, четвертичном, восьмеричном, шестнадцатеричном и общей мощности двух

Все основания, которые являются степенью двух, как двоичные (2 1 ), четвертичные (2 2 ), восьмеричные (2 3 ), шестнадцатеричные (2 4 ) основания, имеют целое число бит на цифру 1 .
Таким образом, чтобы получить каждую цифру 2 цифры, мы просто разбиваем число intro group из n бит, начиная с LSb (справа).
Например, для четвертичной базы мы разбиваем 16-битное число в группах по два бита. Есть 8 таких групп.
Не все мощности двух оснований имеют целое число групп, которые соответствуют 16 бит; например, восьмеричная база имеет 5 групп из 3 бит, которые составляют 3 · 5 = 15 бит из 16, оставляя частичную группу из 1 бит 3 .

Алгоритм прост, мы изолировать каждую группу со сдвигом с последующей операции И.
Эта процедура работает для каждого размера групп или, другими словами, для любой базовой мощности двух.

Чтобы показать цифры в правильном порядке, функция начинается с выделения наиболее значимой группы (самой левой), поэтому важно знать: а) сколько бит D является группой и b) битную позицию S, где самая левая начинается группа.
Эти значения предварительно вычисляются и хранятся в тщательно обработанных константах.

параметры

Параметры должны быть сдвинуты в стек.
Каждый из них имеет ширину 16 бит.
Они показаны в порядке толчка.

параметр Описание
N Число конвертируемых
База База для использования, выраженная с использованием констант BASE2 , BASE4 , BASE8 и BASE16
Печатать ведущие нули Если в нуле нет неосновных нулей, в противном случае они будут. Число 0 печатается как «0», хотя

использование

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

Примечание для пользователей TASM : если вы поместите константы, определенные с EQU после кода, который их использует, включите многопроходный с флагом /m TASM или вы получите переопределение ссылок на Forward .

Код

;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

Данные

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

Чтобы портировать код в NASM, удалите ключевое слово PTR из доступа к памяти (например, mov si, WORD PTR [bp+08h] становится mov si, WORD PTR [bp+08h] ).

Расширение функции

Функция может быть легко расширена до любой базы до 2 255 , хотя каждое основание выше 2 16 будет печатать тот же номер, что и число, всего 16 бит.

Чтобы добавить базу:

  1. Определите новую константу BASEx где x равно 2 n .
    Нижний байт, названный D , равен D = n .
    Верхний байт, названный S , представляет собой позицию в битах более высокой группы. Его можно рассчитать как S = n · (⌈16 / n ⌉ - 1).
  2. Добавьте необходимые цифры в строку DIGITS .

Пример: добавление базы 32

Мы имеем D = 5 и S = 15, поэтому мы определяем BASE32 EQU 0f05h .
Затем мы добавим еще шестнадцать цифр: DIGITS db "0123456789abcdefghijklmnopqrstuv" .

Как и должно быть ясно, цифры можно изменить, отредактировав строку DIGITS .


1 Если B является базой, то у него есть B цифр для каждого определения. Таким образом, количество бит на цифру составляет log 2 ( B ). Для мощности двух баз это упрощает log 2 (2 n ) = n, который по определению является целым числом.

2 В этом контексте подразумевается, что рассматриваемая база является степенью двух базовых 2 n .

3 Для того чтобы база B = 2 n имела целое число бит-групп, должно быть, что n | 16 ( n делит 16). Поскольку единственным фактором в 16 является 2, должно быть, что n само является степенью двух. Таким образом, B имеет вид 2 2 k или, что то же самое, log 2 ( log 2 ( B )) должен быть целым числом.

MS-DOS, TASM / MASM, функция для печати 16-разрядного числа в десятичном формате

Печать 16-разрядного беззнакового числа в десятичном формате

Служба прерываний Int 21 / AH = 02h используется для печати цифр.
Стандартное преобразование из числа в цифру выполняется с помощью команды div , дивиденд изначально является наивысшей степенью десяти фитингов 16 бит (10 4 ), и на каждой итерации он уменьшается до более низких мощностей.

параметры

Параметры отображаются в порядке нажатия.
Каждый из них имеет 16 бит.

параметр Описание
число 16-разрядное число без знака для печати в десятичной форме
показать ведущие нули Если 0 нет неосновных нулей, иначе они есть. Число 0 всегда печатается как «0»,

использование

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

Код

;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

Чтобы портировать код в NASM, удалите ключевое слово PTR из доступа к памяти (например, mov ax, WORD PTR [bp+06h] станет mov ax, WORD [bp+06h] ).



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow