
Быстрый поиск
Введите фрагмент названия статьи для поиска
Преобразование числа в строку с разделением на разряды
17.01.2016 | Категория: Образ мышления: Assembler | Автор: ManHunter

Преобразование числа в строку с разделением на разряды
Если вы работаете с большими десятичными числами, то наверняка согласитесь, что число с разделением на разряды (то есть с группировкой по три символа: тысячи, миллионы и так далее) воспринимается гораздо лучше, чем просто последовательность цифр. Так проще выявлять ошибки или, например, с одного взгляда можно оценить порядок числа.
Читать статью целиком »
Просмотров: 5622 | Комментариев: 9
Преобразование вещественного числа в строку на Ассемблере
14.03.2014 | Категория: Образ мышления: Assembler | Автор: ManHunter
Ранее я выкладывал статью о том, как на Ассемблере перевести строку в вещественное число, теперь настало время произвести обратную операцию, то есть перевести вещественное число в строку. В отличие от целых чисел, где достаточно нескольких строк кода, с преобразованием вещественных чисел пришлось повозиться. В результате появилась следующая функция. За основу взята аналогичная функция из пакета MASM, но со значительными доработками.Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Функция перевода вещественного числа в строку
- ;------------------------------------------------------------
- ; Портирование с MASM, доработка и оптимизация - ManHunter
- ; Параметры:
- ; lpFloat - указатель на вещественное число TBYTE
- ; lpResult - указатель на строку-приемник результата
- ;------------------------------------------------------------
- proc FloatToString lpFloat:DWORD, lpResult:DWORD
- ; Локальные переменные
- local digits_count:DWORD
- local tmp:DWORD
- local old_cw:WORD
- local new_cw:WORD
- local saved_float:TBYTE
- local tmp1 rb 12h
- local tmp2 rb 12h
- ; Сохранить все регистры
- pusha
- ; Указатель на строку-приемник
- mov edi,[lpResult]
- ; Это ноль?
- mov esi,[lpFloat]
- cmp dword [esi],0
- jne loc_not_zero
- cmp dword [esi+4],0
- jne loc_not_zero
- cmp word [esi+8],0x8000
- je loc_minus_zero
- cmp word [esi+8],0
- jne loc_not_zero
- ; Записать в строку ноль
- mov al,'0'
- stosb
- jmp loc_ret
- loc_minus_zero:
- ; Записать в строку минус ноль
- mov ax,'-0'
- stosw
- jmp loc_ret
- loc_not_zero:
- ; Denormalized?
- mov ax,word [esi+8]
- and ax,7F80h
- or ax,ax
- jnz loc_not_denorm
- cmp dword [esi],0
- jne loc_denorm
- cmp dword [esi+4],0
- je loc_not_denorm
- loc_denorm:
- mov esi,szDen
- movsd
- movsd
- movsd
- jmp loc_ret
- loc_not_denorm:
- ; Infinity или NaN?
- mov ax,word [esi+8]
- and ax,7F80h
- cmp ax,7F80h
- jne loc_not_inf_nan
- ; Это Infinity?
- cmp dword [esi],0
- jne loc_nan
- cmp dword [esi+4],80000000h
- jne loc_nan
- loc_infinity:
- mov esi,szInf
- movsd
- movsb
- loc_plus_minus:
- movsd
- mov esi,[lpFloat]
- test byte [esi+9],80h
- jz loc_ret
- mov esi,[lpResult]
- mov byte[esi],'-'
- jmp loc_ret
- loc_nan:
- mov esi,szNan
- jmp loc_plus_minus
- loc_not_inf_nan:
- ; Скопировать число в локальную переменную
- push edi
- mov esi,[lpFloat]
- lea edi,[saved_float]
- movsd
- movsd
- movsw
- pop edi
- ; Число отрицательное?
- cmp dword [saved_float+6],0
- jge loc_not_signed
- ; Привести число к абсолютному значению
- and byte [saved_float+9],7Fh
- ; Записать в строку минус
- mov al,'-'
- stosb
- loc_not_signed:
- ; Проверить число на наличие дробной части и
- ; подсчитать количество цифр в нем
- fclex
- ; Сохранить управляющее слово
- fstcw [old_cw]
- ; Установить управляющее слово
- mov [new_cw],0000001001111111b
- fldcw [new_cw]
- lea esi,[saved_float]
- fld tbyte [esi]
- fld st
- ; Выделить мантиссу и порядок
- fxtract
- fstp st
- fldlg2
- ; Получить количество цифр в числе
- fmulp st1,st
- fistp [digits_count]
- ; Если цифр больше 16, то число отображается в
- ; нормализованном виде с мантиссой и экспонентой
- cmp [digits_count],10h
- jnb loc_not_integer
- ; У числа есть дробная часть?
- fld st
- frndint
- fcomp st1
- fstsw ax
- test ah,01000000b
- ; Да, отображать число с дробной частью
- jz loc_not_integer
- ; Целое число без дробной части и экспоненты
- lea eax,[tmp1]
- fbstp [eax]
- ; Перевести BCD-число в строку
- push edi
- lea esi,[tmp1+8]
- lea edi,[tmp2]
- mov ecx, 9
- @@:
- std
- xor eax,eax
- lodsb
- cld
- rol ax,12
- rol ah,4
- add ax,'00'
- stosw
- loop @b
- pop edi
- ; Пропустить лидирующий ноль
- mov eax,11h
- mov ecx,[digits_count]
- sub eax,ecx
- inc ecx
- lea esi,[tmp2+eax]
- cmp byte [esi],'0'
- jne @f
- inc esi
- dec ecx
- @@:
- ; Перенести полученное число из временного буфера
- rep movsb
- jmp loc_clear_stack
- loc_not_integer:
- mov eax,10h
- sub eax,[digits_count]
- ; Преобразовать число в целое до 16 разрядов
- mov ecx,eax
- cmp eax,0
- jge @f
- neg eax
- @@:
- ; Для чисел больше 0 корректировка округления в сторону 0
- mov [new_cw],0000101001111111b
- cmp ecx,0
- jge @f
- mov [new_cw],0000011001111111b
- @@:
- ; Установить управляющее слово
- fldcw [new_cw]
- ; Возвести 10 в степень количества цифр
- mov [tmp],eax
- fild [tmp]
- fld [float2]
- fyl2x
- fld1
- fld st1
- fprem
- f2xm1
- faddp
- fscale
- ; Почистить стек
- fxch st1
- fstp st
- ; Если число меньше 0, то умножить, иначе разделить
- cmp ecx,0
- jge @f
- fdivp st1,st
- jmp loc_rounded
- @@:
- fmulp st1,st
- loc_rounded:
- ; Полученное значение меньше 1.0e16 ?
- fcom [float1]
- fstsw ax
- test ah,1
- jz @f
- fmul [float2]
- dec [digits_count]
- @@:
- ; Целое число без дробной части и экспоненты
- lea eax,[tmp1]
- fbstp [eax]
- ; Перевести BCD-число в строку
- push edi
- lea esi,[tmp1+8]
- lea edi,[tmp2]
- mov ecx, 9
- @@:
- std
- xor eax,eax
- lodsb
- cld
- rol ax,12
- rol ah,4
- add ax,'00'
- stosw
- loop @b
- pop edi
- ; Числу требуется мантисса и экспонента?
- lea esi,[tmp2+1]
- mov ecx,[digits_count]
- cmp ecx,-0Fh
- jl loc_mantiss_and_exponent
- cmp ecx,10h
- jg loc_mantiss_and_exponent
- ; Заполнить дробную часть числа
- inc ecx
- cmp ecx,0
- jg @f
- mov ax,'0.'
- stosw
- neg ecx
- mov al,'0'
- rep stosb
- mov ecx,10h
- jmp loc_fraction_filled
- @@:
- rep movsb
- mov al,'.'
- stosb
- mov ecx,10h
- sub ecx,[digits_count]
- loc_fraction_filled:
- rep movsb
- jmp @f
- loc_clear_fraction:
- ; Удалить завершающие нули дробной части
- dec edi
- @@:
- cmp byte [edi-1],'0'
- jz loc_clear_fraction
- cmp byte [edi-1],'.'
- jnz @f
- dec edi
- @@:
- jmp loc_clear_stack
- loc_mantiss_and_exponent:
- ; Дробная часть мантиссы
- movsb
- mov al,'.'
- stosb
- movsd
- movsd
- movsw
- ; Удалить завершающие нули дробной части
- @@:
- cmp byte [edi-1],'0'
- jne @f
- cmp byte [edi-2],'.'
- je @f
- dec edi
- jmp @b
- @@:
- ; Символ и знак экспоненты
- mov al,'e'
- stosb
- mov al,'+'
- mov ebx,[digits_count]
- cmp ebx, 0
- jge @f
- mov al,'-'
- neg ebx
- @@:
- stosb
- ; Значение экспоненты
- mov eax,ebx
- mov ecx,10
- mov ebx,4
- @@:
- dec ebx
- xor edx,edx
- div ecx
- add dl,'0'
- mov [tmp1+ebx],dl
- or ebx,ebx
- jnz @b
- ; Пропустить лидирующие нули экспоненты
- mov ecx,4
- lea esi,[tmp1]
- @@:
- lodsb
- cmp al,'0'
- jne @f
- dec ecx
- jmp @b
- @@:
- dec esi
- rep movsb
- loc_clear_stack:
- ; Восстановить управляющее слово
- fldcw [old_cw]
- loc_ret:
- ; Окончание строки
- mov al,0
- stosb
- ; Восстановить все регистры
- popa
- ret
- float1 dq 1.0e16
- float2 dq 10.0
- szInf db '+Infinity'
- szNan db '+NaN'
- szDen db 'Denormalized'
- endp
Читать статью целиком »
Просмотров: 11962 | Комментариев: 27
Оптимизация умножения на константу на Ассемблере
05.08.2013 | Категория: Образ мышления: Assembler | Автор: ManHunter
В предыдущей статье по оптимизации мы рассматривали замену команд деления, как это описано в "AMD Athlon Processor x86 Code Optimization Guide". Но в этом же мануале описана оптимизация умножения на константу. Тут нет универсальных алгоритмов, по которым можно было бы однозначно определить нужную последовательность команд замены, как в случае с делением. Для каждой константы набор команд определяется на основании их "весовых коэффициентов" и количества тактов процессора, необходимых для их выполнения. В качестве примера приведена вот такая табличка оптимизации умножения с 2 до 32. Ее всегда можно посмотреть и в самом мануале, но лучше пусть будет под рукой.Code (Assembler) : Убрать нумерацию
- ; Умножение на 2
- add reg1, reg1 ; 1 такт процессора
- ; Умножение на 3
- lea reg1, [reg1+reg1*2] ; 2 такта процессора
- ; Умножение на 4
- shl reg1, 2 ; 1 такт процессора
- ; Умножение на 5
- lea reg1, [reg1+reg1*4] ; 2 такта процессора
- ; Умножение на 6
- lea reg1, [reg1+reg1*2] ; 3 такта процессора
- add reg1, reg1
- ; Умножение на 7:
- mov reg2, reg1 ; 2 такта процессора
- shl reg1, 3
- sub reg1, reg2
- ; Умножение на 8:
- shl reg1, 3 ; 1 такт процессора
- ; Умножение на 9:
- lea reg1, [reg1+reg1*8] ; 2 такта процессора
- ; Умножение на 10:
- lea reg1, [reg1+reg1*4] ; 3 такта процессора
- add reg1, reg1
- ; Умножение на 11:
- lea reg2, [reg1+reg1*8] ; 3 такта процессора
- add reg1, reg1
- add reg1, reg2
- ; Умножение на 12:
- lea reg1, [reg1+reg1*2] ; 3 такта процессора
- shl reg1, 2
- ; Умножение на 13:
- lea reg2, [reg1+reg1*2] ; 3 такта процессора
- shl reg1, 4
- sub reg1, reg2
- ; Умножение на 14:
- lea reg2, [reg1+reg1] ; 3 такта процессора
- shl reg1, 4
- sub reg1, reg2
- ; Умножение на 15:
- lea reg1, [reg1+reg1*2] ; 4 такта процессора
- lea reg1, [reg1+reg1*4]
- ; Умножение на 16:
- shl reg1, 4 ; 1 такт процессора
- ; Умножение на 17:
- mov reg2, reg1 ; 2 такта процессора
- shl reg1, 4
- add reg1, reg2
- ; Умножение на 18:
- lea reg1, [reg1+reg1*8] ; 3 такта процессора
- add reg1, reg1
- ; Умножение на 19:
- lea reg2, [reg1+reg1*2] ; 3 такта процессора
- shl reg1, 4
- add reg1, reg2
- ; Умножение на 20:
- lea reg1, [reg1+reg1*4] ; 3 такта процессора
- shl reg1, 2
- ; Умножение на 21:
- lea reg2, [reg1+reg1*4] ; 3 такта процессора
- shl reg1, 4
- add reg1, reg2
- ; Умножение на 22:
- lea reg2, [reg1+reg1*4] ; 4 такта процессора
- add reg1, reg1
- lea reg1, [reg1+reg2*4]
- ; Умножение на 23:
- lea reg2, [reg1+reg1*8] ; 3 такта процессора
- shl reg1, 5
- sub reg1, reg2
- ; Умножение на 24:
- lea reg1, [reg1+reg1*2] ; 3 такта процессора
- shl reg1, 3
- ; Умножение на 25:
- lea reg1, [reg1+reg1*4] ; 4 такта процессора
- lea reg1, [reg1+reg1*4]
- ; Умножение на 26:
- lea reg2, [reg1+reg1*2] ; 4 такта процессора
- add reg1, reg1
- lea reg1, [reg1+reg2*8]
- ; Умножение на 27:
- lea reg1, [reg1+reg1*2] ; 4 такта процессора
- lea reg1, [reg1+reg1*8]
- ; Умножение на 28:
- lea reg2, [reg1*4] ; 3 такта процессора
- shl reg1, 5
- sub reg1, reg2
- ; Умножение на 29:
- lea reg2, [reg1+reg1*2] ; 3 такта процессора
- shl reg1, 5
- sub reg1, reg2
- ; Умножение на 30:
- lea reg2, [reg1+reg1] ; 3 такта процессора
- shl reg1, 5
- sub reg1, reg2
- ; Умножение на 31:
- mov reg2, reg1 ; 2 такта процессора
- shl reg1, 5
- sub reg1, reg2
- ; Умножение на 32:
- shl reg1, 5 ; 1 такт процессора
Читать статью целиком »
Просмотров: 8603 | Комментариев: 7
Замена деления умножением на Ассемблере
07.07.2013 | Категория: Образ мышления: Assembler | Автор: ManHunter

Замена деления умножением на Ассемблере
При написании на Ассемблере алгоритмов, использующих деление на константу, операции деления можно заменять на операции умножения. Зачем это надо? Дело в том, что процессор выполняет операцию умножения в несколько раз быстрее, чем операцию деления. К примеру, на умножение тратится 5-9 тактов процессора (знаковое умножение) или 4-8 (беззнаковое умножение), тогда как для деления необходимо 22-47 тактов (знаковое деление) или 17-41 тактов (беззнаковое деление). В высокооптимизированных алгоритмах, таких как шифрование, математические расчеты, архивация больших объемов данных, подсчет контрольных сумм и других подобных задачах, экономия каждого такта процессора может дать значительный прирост общей скорости работы программы.
Читать статью целиком »
Просмотров: 14968 | Комментариев: 4
Преобразование строки в вещественное число на Ассемблере
31.05.2012 | Категория: Образ мышления: Assembler | Автор: ManHunter
Про преобразование строки в обычное число я уже писал, теперь задача посложнее - надо преобразовать строку в вещественное число с плавающей запятой (float). Для этого я написал вот такую функцию преобразования:Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Функция преобразования строки в вещественное число
- ; by ManHunter / PCL
- ; http://www.manhunter.ru
- ;
- ; Параметры:
- ; lpStr - указатель на исходную строку в формате ASCIIZ
- ; lpResult - указатель на переменную-приемник значения
- ; На выходе:
- ; EAX = 1 - строка успешно преобразована
- ; EAX = 0 - строка не может быть преобразована в число
- ;
- ; Примечание: переменная-приемник может иметь размер DWORD
- ; или QWORD, в зависимости от этого должна изменяться и
- ; функция (см. комментарии в конце кода). По умолчанию
- ; считается, что переменная имеет размер DWORD
- ;------------------------------------------------------------
- proc string2float lpStr:DWORD, lpResult:DWORD
- ; Локальные переменные
- locals
- dot dd ? ; Указатель на дробную часть
- exp dd ? ; Указатель на экспоненту
- digit dd ? ; Цифра
- endl
- pusha
- ; Проверка строки на валидность
- mov [digit],1
- mov [exp],0
- mov [dot],0
- mov esi,[lpStr]
- ; Минус или плюс может быть только в начале
- cmp byte [esi],'-'
- je @f
- cmp byte [esi],'+'
- jne .loc_chk_loop
- @@:
- inc esi
- ; После знака не может быть нуля
- cmp byte [esi],0
- je .loc_chk_error
- .loc_chk_loop:
- ; В строке должны быть цифр, экспонента и не более одной точки
- lodsb
- or al,al
- jz .loc_chk_complete
- cmp al,'e'
- je .loc_chk_exp
- cmp al,'E'
- je .loc_chk_exp
- cmp al,'.'
- je .loc_chk_dot
- cmp al,'0'
- jb .loc_chk_error
- cmp al,'9'
- ja .loc_chk_error
- jmp .loc_chk_loop
- .loc_chk_dot:
- ; Точка в строке уже есть?
- cmp [dot],0
- ; Строка имеет некорректный формат
- jne .loc_chk_error
- ; Экспонента уже есть?
- cmp [exp],0
- ; Строка имеет некорректный формат
- jne .loc_chk_error
- ; Указатель на дробную часть
- mov [dot],esi
- jmp .loc_chk_loop
- .loc_chk_exp:
- ; Экспонента уже есть?
- cmp [exp],0
- ; Строка имеет некорректный формат
- jne .loc_chk_error
- ; Указатель на начало экспоненты
- mov [exp],esi
- ; Сразу после экспоненты не может быть нуля
- cmp byte [esi],0
- je .loc_chk_error
- ; После экспоненты может быть знак
- cmp byte [esi],'-'
- je @f
- cmp byte [esi],'+'
- jne .loc_chk_loop
- @@:
- inc esi
- ; Сразу после минуса не может быть нуля
- cmp byte [esi],0
- je .loc_chk_error
- ; Проверить следующий символ
- jmp .loc_chk_loop
- .loc_chk_error:
- ; Строка не является числом
- mov [digit],0
- jmp .loc_ret
- .loc_chk_complete:
- ; Инициализация сопроцессора
- finit
- ; Начальное значение числа
- fldz
- ; Множитель и делитель
- mov [digit],10
- fild dword [digit]
- ; Запись значений до запятой
- mov esi,[lpStr]
- ; В начале строки минус?
- cmp byte [esi],'-'
- je @f
- cmp byte [esi],'+'
- jne .loc_before_dot
- @@:
- inc esi
- ; Преобразование числа до запятой
- .loc_before_dot:
- lodsb
- ; Конец строки?
- or al,al
- jz .loc_complete
- cmp al,'.'
- je .loc_complete_before_dot
- cmp al,'e'
- je .loc_exp
- cmp al,'E'
- je .loc_exp
- ; Очередная цифра
- sub al,'0'
- movzx eax,al
- mov [digit],eax
- ; Записать
- fild dword [digit]
- fxch st2
- fmul st0,st1
- fxch st2
- fadd st2,st0
- ffree st0 ; Почистить стек
- fincstp
- jmp .loc_before_dot
- ; Преобразование дробной части числа
- .loc_complete_before_dot:
- ; Дробная часть есть?
- cmp [dot],0
- je .loc_complete_after_dot
- ; Экспонента есть?
- cmp [exp],0
- je @f
- ; Указатель на начало экспоненты
- mov esi,[exp]
- jmp .loc_start_after_dot
- @@:
- ; Иначе перенести указатель на конец строки
- xor ecx,ecx
- dec ecx
- xor eax,eax
- mov edi,esi
- repne scasb
- mov esi,edi
- .loc_start_after_dot:
- std
- dec esi
- dec esi
- ; Дробная часть
- fldz
- fxch st1
- .loc_after_dot:
- lodsb
- ; Конец дробной части?
- cmp al,'.'
- je .loc_complete_after_dot
- ; Очередная цифра
- sub al,'0'
- movzx eax,al
- mov [digit],eax
- ; Записать
- fild dword [digit]
- fadd st2,st0
- fxch st2
- fdiv st0,st1
- fxch st2
- ffree st0 ; Почистить стек
- fincstp
- jmp .loc_after_dot
- .loc_complete_after_dot:
- ; Сбросить флаг направления
- cld
- ffree st0 ; Почистить стек
- fincstp
- ; Сложить дробную и целую часть
- fadd st1,st0
- .loc_exp:
- ; Экспонента есть?
- cmp [exp],0
- je .loc_complete
- ; Получить значение экспоненты
- xor ecx,ecx
- mov esi,[exp]
- ; В начале строки минус?
- cmp byte [esi],'-'
- je @f
- cmp byte [esi],'+'
- jne .loc_start_exp
- @@:
- inc esi
- .loc_start_exp:
- lodsb
- or al,al
- jz .loc_end_exp
- sub al,'0'
- movzx eax,al
- imul ecx,10
- add ecx,eax
- jmp .loc_start_exp
- .loc_end_exp:
- or ecx,ecx
- jz .loc_complete
- ffree st0 ; Почистить стек
- fincstp
- mov [digit],10
- fild dword [digit]
- ; Делить или умножать?
- mov esi,[exp]
- cmp byte [esi],'-'
- je .loc_exp_divide
- .loc_exp_multiple:
- fmul st1,st0
- loop .loc_exp_multiple
- jmp .loc_complete
- .loc_exp_divide:
- fdiv st1,st0
- loop .loc_exp_divide
- .loc_complete:
- ffree st0 ; Почистить стек
- fincstp
- ; В начале строки минус?
- mov esi,[lpStr]
- cmp byte [esi],'-'
- jne @f
- ; Изменить знак числа
- fchs
- @@:
- ; Записать значение в ячейку памяти
- mov eax,[lpResult]
- ; Если требуется повышенная точность, то приемник
- ; должен иметь размер QWORD, а следующую команду
- ; надо заменить на fstp qword [eax]
- fstp dword [eax]
- ; Успешное преобразование
- mov [digit],1
- .loc_ret:
- popa
- ; Результат преобразования
- mov eax,[digit]
- ret
- endp
Читать статью целиком »
Просмотров: 9566 | Комментариев: 13


