x64 software conventions будем считать что указатель на начало Числовой строки подлежащие конвертированию расположен в RCX.x64 битный код при x32 битной адресации. Такой способ адресации позволяет использовать преимущества обоих диалектов. Для установки указанного режима необходимо указать директиву /LARGEADDRESSAWARE:NO в линковщику.BUFF_STR equ esp - xmmword * 4
DWORD или INT для тех кому более привычен синтаксис СРР которые не имеют своего собственного отображения в памяти, а все время своего существования размещаются в регистре с которым они ассоциированы, при этом некоторые «переменны» являются по сути «объединениями» и размещаются в одних и тех же регистрах присутствуя в них на разных этапах исполнения программы:; псевдонимы регистров
CUR_CHAR equ ecx ; абсолютная позиция текущего символа
DOT_CHAR equ edx ; относительная позиция символа точки
HASH_STR equ r8d ; хеш символов Числовой строки
END_CHAR equ HASH_STR ; относительная позиция последнего символа
N_Z_CHAR equ r9d ; относительная позиция символ не нулевого числа
OFF_CHAR equ N_Z_CHAR ; смешение дробной части относительно начала Числовой строки
END_FRAC equ r10d ; относительное положение последнего символа Числовой строки
EXP_CHAR equ END_FRAC ; текущий относительный символ строки Экспоненты
LEN_NUMB equ r11d ; длина значимой части Числа
LEN_CELL equ LEN_NUMB ; длина целой части Числа
HASH_MUL equ ebx ; значение экспоненты в десятичной системе
MANT_ARG equ r8 ; мантисса аргумент множителя
LOGB_ARG equ r9d ; порядок аргумента множителя
MANT_MUL equ r10 ; мантисса множителя
LOGB_MUL equ r11d ; порядок множителя
SIMD команды не допускают непосредственной размещения данных в инструкциях секции кода, что вынуждает создавать секцию данных:.data
Xmm_HT byte 10h dup (09h)
Xmm_CR byte 10h dup (0Dh)
Xmm_SP byte 10h dup (20h)
Xmm_SL byte 10h dup ('/')
Xmm_30 byte 10h dup ('0')
Xmm_39 byte 10h dup ('9')
Xmm_0001 word 8 dup (010Ah)
Xmm_0010 dword 4 dup (10064h)
Xmm_0100 qword 2 dup (100002710h)
Mask_001 word 0044h, 0944h, 0D44h, 2044h, 0046h, 0946h, 0D46h, 2046h
Mask_010 word 0064h, 0964h, 0D64h, 2064h, 0066h, 0966h, 0D66h, 2066h
Mul_0001 qword 0E8D4A51000h
Plus word 2B00h
; тестовая строка
string byte ' ', 0Dh, 0Ah, '+-0098765432109876540.09876e-0248 '
Назначение определенных констант будет пояснено ниже в ходе выполнения процедуры.
ХММ3 самими собой в результате чего все байт ХММ3 принимают значение -1.— уменьшаем указатель адреса первого символа в
CUR_CHAR на длину ХММ регистра.— увеличиваем указатель адреса первого символа в
CUR_CHAR на длину ХММ регистра. pcmpeqb xmm3, xmm3
sub CUR_CHAR, xmmword
@@: add CUR_CHAR, xmmword
Таким образом при начальном входе в цикл обработки указатель текущего символа будет установлен на начало строки, при последующих входах в цикл он будет смещаться на длину
ХММ-регистра, то есть на 15 байт. Такой способ организации начала цикла, при котором инкремент расположен в начале цикла, позволяет значительно упростить выход из цикла сведя его к команде проверки условия и условному переходу. В противном случае при размещении команда инкремента и проверки условий в конце цикла эти инструкции конфликтовали бы в части изменения флагов процессора что избыточно усложнило бы выход из цикла.ХММ0 и копируем ее в ХММ1/ХММ2 получая три копии строки.— сравниваем три копии строки содержащиеся в регистрах с тремя строками размещенными в памяти, равномерно заполненными символами
пробел/табуляция/возврат каретки— складываем полученные результаты в регистр
ХММ0.movdqu xmm0,[CUR_CHAR]
movdqa xmm1, xmm0
movdqa xmm2, xmm0
pcmpeqb xmm0, xmmword ptr Xmm_SP
pcmpeqb xmm1, xmmword ptr Xmm_HT
pcmpeqb xmm2, xmmword ptr Xmm_CR
paddb xmm0, xmm1
paddb xmm0, xmm2
В результате байты регистра
ХММ0 равные любому из трех символов обобщенного пробела принимают значение -1 а не равные 0. Векторное сравнение позволяет многократно повысить скорость сканирования строки не только за счет параллельного сравнения но и за счет исключения множества условных переходов характерных для «классических» способов.PTEST выполняет операцию AND над байтами ХММ0 и ХММ3 и в случае если все байты результата установлены в -1 устанавливаем флаг переноса CF=1.— если флаг переноса
CF=1 то следовательно в сканируемой строке отсутствуют символы отличные от обобщенного пробела и необходимо вернуться в начало цикла.ptest xmm0, xmm3
jc @b ; повторный пропуск обобщенного пробела
В результате сканирование строки продолжается до тех пор пока не будет найден символ отличный от обобщенного пробела. Скорость сканирования можно дополнительно увеличить если разместить строки равномерно заполненные символами
пробел/табуляция/возврат каретки в старших регистрах SIMD, но в соответствии с соглашением вызова х64 это потребует предварительно сохранить их значение в память, а при выходе из функции восстановить, что учитывая ожидаемое время сканирования в один проход будет не оправданно.ХММ0 в EAX, теперь все биты соответствующие символам обобщенного пробела установлены в значение 1.— инвертируем
EAX, теперь биты соответствующие символам не равным обобщенному пробелу установлен в значение 1.— сканируем биты регистра
EAX от младшего к старшему в поиске первого бита установлено в значение 1, и результат равный номеру бита, помещаем в этот же регистр.— добавляем значение
EAX к CUR_CHAR и получаем указатель на первый символ отличный от обобщенного пробела.pmovmskb eax, xmm0
not eax
bsf eax, eax
add CUR_CHAR, eax
ZF=1 если сочетание двух первых символов следующих после символа обобщенного пробела равно сочетанию символов новая строка.— устанавливаем младший байт регистра
EAX в значение 1 если флаг нуля ZF=1 и в значение 0 при всех остальных вариантах.— складываем значение
EAX и CUR_CHAR и получаем указатель на первый символ отличный от обобщенного пробела с учетом сочетания символов новой строки.cmp word ptr[CUR_CHAR - byte], 0A0Dh
setz al
add CUR_CHAR, eax
Таким образом если сочетание двух первых символов следующих после символа обобщенного пробела равно сочетанию символов
новая строка то второй символ после обобщённого пробела будет проигнорирован, в противном случае он будет подвергнут дальнейшему анализу.EAX одновременно расширяя его до двойного слова.— устанавливаем флаг нуля
ZF=1 если значение EAX равно 0.— если флаг нуля
ZF=1 то следовательно имеет место обрыв строки и необходимо выйти из процедуры вернув код ошибки:movzx eax, byte ptr[CUR_CHAR]
test al, al
jz ErrorExit ; обрыв строки
AL с символом плюс.— устанавливаем
AL в значение 1 если AL равен символу плюс и 0 при любом другом значении символа.— добавляем значение
EAX к CUR_CHAR.cmp al, '+'
setz al
add CUR_CHAR, eax
В результате если текущий символ равен символу
плюс то позиция текущего символа будет смещена на следующий символ, во всех остальных случаях символ будет подвергнут повторному анализу.минус.— устанавливаем значение
AL в 1 если текущий символ равен символу минус и 0 при любом другом значении.— добавляем значение
EAX к CUR_CHAR.— добавляем значение
EAX к регистру ESP.cmp byte ptr[CUR_CHAR], '-'
setz al
add CUR_CHAR, eax
add esp, eax
В результате если текущий символ равен символу
минус то позиция текущего символа будет смещена на следующий символ, а значение регистра стека ESP увеличено на 1, во всех остальных случаях символ будет подвергнут повторному анализу, а значение регистра стека останется без изменений. Прямое изменение значения регистра указателя стека ESP считается крайне опасным действием чреватым непредсказуемыми ошибками, но я практикую «агрессивный» подход и считаю что не бывает «плохого» или «хорошего» кода, бывают хорошие и плохи программисты, хорошие пишут так как будто никаких правил нет вообще но результат при этом такой как будто они соблюдают их все, а плохие они просто плохие.ХММ0.— копируем старшую часть Числовой строки в
ХММ1 и ХММ2.— сравниваем регистр
ХММ0 со строкой в памяти равномерно заполненной символами 9.— копируем регистр
ХММ0 в регистр ХММ1.movdqu xmm0,[CUR_CHAR + xmmword]
movdqa xmm2, xmm0
movdqa xmm3, xmm0
pcmpgtb xmm0, xmmword ptr Xmm_39
movdqa xmm1, xmm0
В результат получаем две копии строки в регистрах
ХММ0 и ХММ1 в которых все байты символов которые были больше символа 9 установлены в значение -1, а все остальные в значение 0.ХММ2 со строкой в памяти равномерно заполненной символами косая черта, в результате чего все байты регистра ХММ2 содержавшие символы больше и равно символу 0 установлены в значение -1, а меньше в значение 0. — командой
PANDN инвертируем баты регистра ХММ0 и выполняем логическую операцию AND над байтами ХММ0 и ХММ2 помещая результат в регистр ХММ0.pcmpgtb xmm2, xmmword ptr Xmm_SL
pandn xmm0, xmm2
В результате все байты регистра
ХММ0 содержащие символы в диапазоне от 0 включительно до 9 включительно, то есть цифры, принимают значения -1 а все остальные 0.ХММ3 со строкой в памяти равномерно заполненной символами 0, в результате чего все байты регистра ХММ3 содержавшие символы больше и равно символу 1 установлены в значение -1, а меньше в значение 0. — командой
PANDN инвертируем баты регистра ХММ1 и выполняем логическую операцию AND над байтами ХММ1 и ХММ3 помещая результат в регистр ХММ1.pcmpgtb xmm3, xmmword ptr Xmm_30
pandn xmm1, xmm3
В результате все байты регистра
ХММ1 содержащие символы в диапазоне от 1 включительно до 9 включительно, то есть значащие цифры, принимают значения -1 а все остальные 0.ХММ0 в регистр HASH_STR.— копируем старшие биты байтов регистра
ХММ1 в регистр N_Z_CHARpmovmskb HASH_STR, xmm0
pmovmskb N_Z_CHAR, xmm1
В результате младшие 16 бит регистра
HASH_STR соответствуют 16 старшим байтам Числовой строки, при этом биты соответствующие символам содержащим цифры принимают значения 1 а все остальные 0, а младшие 16 бит регистра N_Z_CHAR соответствуют 16 старшим байтам Числовой строки, при этом биты соответствующие символам содержащим значащие числа, принимают значения 1 а все остальные 0.movdqu xmm0,[CUR_CHAR]
movdqa xmm2, xmm0
movdqa xmm3, xmm0
pcmpgtb xmm0, xmmword ptr Xmm_39
movdqa xmm1, xmm0
pcmpgtb xmm2, xmmword ptr Xmm_SL
pcmpgtb xmm3, xmmword ptr Xmm_30
pandn xmm0, xmm2
pandn xmm1, xmm3
ХММ0 в EAX.— сдвигаем младшие 16 бит
HASH_STR в старшую часть HASH_STR.— складываем значение
HASH_STR и EAX.— копируем старшие биты байтов регистра
ХММ1 в EAX.— сдвигаем младшие 16 бит
N_Z_CHAR в старшую часть N_Z_CHAR.— складываем значение
N_Z_CHAR и EAX.pmovmskb eax, xmm0
shl HASH_STR, xmmword
add HASH_STR, eax
pmovmskb eax, xmm1
shl N_Z_CHAR, xmmword
add N_Z_CHAR, eax
В результате
HASH_STR содержит хеш Числовой строки в котором биты соответствующие символам цифр установлены в значение 1 а в се остальные в 0, при этом номера битов соответствуют номерам символов от начала строки начиная с нуля, а N_Z_CHAR содержит хеш Числовой строки в котором биты символов соответствующие значащих цифр установлены в значение 1, а все остальные в 0, при этом номер бита соответствуют номерам символов от начала строки начиная с нуля.HASH_STR от младшего бита к старшему в поисках первого бита равного 1, результат помещаем в EAX и устанавливаем флаг нуля ZF=1 если все биты равны нулю.— если флаг нуля
ZF=1 то значит строка не содержит ни одного символа цифры и необходимо выйти из процедуры вернув код ошибки.— устанавливаем флаг нуля
ZF=0 если полученный результат отличен от нуля.— если флаг нуля
ZF=0 то значит первый символ строки не является цифрой и необходимо выйти из процедуры вернув код ошибки.bsf eax, HASH_STR
jz ErrorExit
test eax, eax
jnz ErrorExit
В результат проверяем содержит ли Числовой строки хотя бы один символ
цифры и является ли первый символ Числовой строки цифрой. Особенностью данного участка кода в нестандартном поведении инструкции BSF которая проявляется в работе с флагом нуля, а именно если при сканирование первым битом установленным в значение 1 окажется бит с порядковым номером 0 то BSF установит значение регистра назначения в 0 но при этом установит флаг нуля ZF=0 как будто в регистре содержится число отличное от нуля, если же инструкция не обнаружит ни одного бита в значении 1, то регистр назначение не будет подвергнут изменению а флаг нуля будет установлен в ZF=1.HASH_STR в результате чего теперь каждый бит установленный в 1 сигнализирует о символе НЕ цифре.— сканируем
HASH_STR от младшего бита к старшему, результат помещаем в DOT_CHAR и устанавливаем флаг нуля ZF=1 если все биты HASH_STR равны нулю.— если флаг нуля
ZF=1 то значит строка не содержит ни одного символа отличного от цифры и необходимо выйти из процедуры вернув код ошибки.— сравниваем символ отличный от
цифры с символом точка и устанавливаем флаг ZF=0 если они не равны.— если флаг нуля
ZF=0 то значит первый символ отличный от цифры не равен символу точка и необходимо выйти из процедуры вернув код ошибки.not HASH_STR
bsf DOT_CHAR, HASH_STR
jz ErrorExit
cmp byte ptr[CUR_CHAR + DOT_CHAR], '.'
jnz ErrorExit
N_Z_CHAR в EAX— сканируем
N_Z_CHAR от младшего бита к старшему и помещаем результат в этот же регистр.— сохраняем в память строку из четырех нулей
0000 по адресу на 1 (один) байт меньше адреса указанного в BUFF_STR.— сохраняем в регистр
ХММ0 старшую часть строку символов начинающийся с первого символа значащей цифры, на который указывает N_Z_CHAR, игнорирую таким образом ведущие нули.— сохраняем в память старшую часть строки символов по адресу указанному в
BUFF_STR.— сохраняем в регистр
ХММ0 младшую часть строки символов на которую указывает N_Z_CHAR со смещение в 16 байт.— сохраняем в память младшую часть строки символов начиная с первого символа
значащей цифры по адресу указанному в BUFF_STR со смещение в 16 байт.mov eax, N_Z_CHAR
bsf N_Z_CHAR, N_Z_CHAR
mov dword ptr[BUFF_STR - byte], 30303030h
movdqu xmm0,[CUR_CHAR + N_Z_CHAR]
movdqu [BUFF_STR + 00000000], xmm0
movdqu xmm0,[CUR_CHAR + N_Z_CHAR + xmmword]
movdqu [BUFF_STR + 00000000 + xmmword], xmm0
В результате сохраняем в память строку из 32 символов начиная с первого символа
значащей цифры на которую указывает N_Z_CHAR по адресу указанному в BUFF_STR. При этом указанная строка может содержать символ точки и иные символы не относящиеся к цифрам.DOT_CHAR в регистр ХММ0.— загружаем младшую часть Числовой строку, следующую сразу после точки, на которую указывает
DOT_CHAR со смещение 16 байт от начала Числовой строки в регистр ХММ1.movdqu xmm0,[CUR_CHAR + DOT_CHAR + byte]
movdqu xmm1,[CUR_CHAR + DOT_CHAR + byte + xmmword]
HASH_STR бит указанный в DOT_CHAR удаляя его из хеша, теперь при следующем сканировании бит указывающий на точку будет проигнорирован.— сканируем
HASH_STR от младшего бита к старшему помещая результат в этот же регистр устанавливая флаг нуля ZF=1 если все биты равны нулю.— если флаг нуля
ZF=1 то значит дробная часть строки не имеет корректного окончания и необходимо выйти из процедуры вернув код ошибки.btr HASH_STR, DOT_CHAR
bsf END_FRAC, HASH_STR
jz ErrorExit
В результате в
EXP_CHAR находиться указатель на первый символ экспоненты или окончание Числа относительно начала Числа.END_FRAC и N_Z_CHAR и устанавливаем флаг переполнения CF=1 если N_Z_CHAR больше END_FRAC.— копируем
END_FRAC в N_Z_CHAR если CF=1.cmp END_FRAC, N_Z_CHAR
cmovc N_Z_CHAR, END_FRAC
В результате если и целая и дробная часть Числа состоят из одних нулей а первый символ
значащей цифры находиться за пределами дробной и целой части числа, о чем свидетельствует факт того что N_Z_CHAR больше END_FRAC, то присваиваем N_Z_CHAR значение END_FRAC то есть указателя на первый символ экспоненты или окончания числа.N_Z_CHAR и DOT_CHAR и если N_Z_CHAR меньше DOT_CHAR, то есть первая значащая цифра расположен раньше точки, что означает что у числа существует целая часть, устанавливаем флаг переноса CF=1.— копируем в
LEN_NUMB указатель на первый символ экспоненты или окончания Числа содержащийся в END_FRAC.— вычитаем из
LEN_NUMB указатель на первую значащую цифру содержащуюся в N_Z_CHAR и флаг переноса CF.cmp N_Z_CHAR, DOT_CHAR
mov LEN_NUMB, END_FRAC
sbb LEN_NUMB, N_Z_CHAR
В результате в
LEN_NUMB содержится значение количества цифр Числа начиная с первой значащей цифры без учета символа точки, то есть исключительно количество символов соответствующих цифрам, символ точки в подсчете не учитывается даже если число пересекает точку.DOT_CHAR значение N_Z_CHAR и устанавливаем флаг знака SF=0, если полученное число положительное.— помещаем в
OFF_CHAR число 20 равное количеству символов которое будет в дальнейшем использованы для создания мантиссы.— Если флаг знака
SF=0 то значит число имеет целой части и необходимо скопировать DOT_CHAR в OFF_CHAR.— сохраняем в память старшую часть строки следующей сразу за символом
точки со смещением указанным в OFF_CHAR по адресу указанному в BUFF_STR.— сохраняем в памяти младшую часть строки следующей сразу за символом
точки со смещением указанным в OFF_CHAR плюс 16 байт, по адресу указанному в BUFF_STRsub DOT_CHAR, N_Z_CHAR
mov OFF_CHAR, xmmword + dword
cmovns OFF_CHAR, DOT_CHAR
movdqu xmmword ptr[BUFF_STR + OFF_CHAR + 0000000], xmm0
movdqu xmmword ptr[BUFF_STR + OFF_CHAR + xmmword], xmm1
В результате если число имеет целую часть то в
OFF_CHAR помещается длина целой части, в противном случае в OFF_CHAR помещается длина Числа по умолчанию равная 20 байтам. Таким образом таким образом если у числа есть целая и дробная часть они будут склеены в единую строку с удалением символа «точки» между ними, если число имеет только целую или только дробную часть, то строка символов начинающаяся после точки будет сохранена за пределами сканируемой строки и таким образом проигнорирована.ХММ2 строку символов ноль.— сохраняем в память строку символов
ноль со смещением указанным в LEN_NUMB по адресу указанному в BUFF_STR.— сохраняем в память строку символов
ноль со смещением указанным в LEN_NUMB плюс 16 байт, по адресу указанному в BUFF_STR.— помещаем в
LEN_CELL удвоенное значение DOT_CHAR то есть удвоенную длину целой части числа.movdqu xmm2, xmmword ptr Xmm_30
movdqu xmmword ptr[BUFF_STR + LEN_NUMB + 0000000], xmm2
movdqu xmmword ptr[BUFF_STR + LEN_NUMB + xmmword], xmm2
lea LEN_CELL, [DOT_CHAR * 2]
В результат все «мусорные» символы Числовой строки, находящиеся после последнего символа
цифры, на который указывает LEN_NUMB будут заменены символами нуля. Таким образом в памяти будет сформирована единая непрерывная строка начинающаяся с первого значащего символа, содержащая все значимые цифры и дополненная нуля в случае если значимая часть Числа меньше 20 символов. Кроме того в LEN_CELL будет помещена удвоенная сумма знаком между символом точки и первой значащей цифрой.N_Z_CHAR.— загружаем в младшее двойное слова регистра
ХММ0 четыре символа начиная с символа который следует за последним символом дробной части Числовой строки и на который указывает END_FRAC.— копируем два символа на которые указывает
END_FRAC в четыре младших слова регистра ХММ0 и получаем четыре копии пары символов окончания числа.— копируем два символа на которые указывает
END_FRAC во все слова регистра ХММ0 и получаем восемь копии копий пары символов окончания числа.— копируем
ХММ0 в ХММ1 и получаем шестнадцать копий пары символов окончания числа.xor N_Z_CHAR, N_Z_CHAR
movd xmm0, dword ptr[CUR_CHAR + END_FRAC]
pshuflw xmm0, xmm0, 0
pshufd xmm0, xmm0, 0
movdqa xmm1, xmm0
ХММ0 со строкой символов в памяти содержащей восемь вариантов окончания Числовой строки и устанавливаем значение совпадающих слов в -1 а в остальных случаях в 0.— сравниваем слова регистра
ХММ1 со строкой символов в памяти содержащей восемь вариантов окончания Числовой строки и устанавливаем значение совпадающих слов в -1 а в остальных случаях в 0.— складываем значение
ХММ0 и ХММ1 и помещаем результат в регистр ХММ0.— сравниваем байты регистра
ХММ1 самими собой в результате чего все байт ХММ1 принимают значение -1.— командой
PTEST выполняет операцию AND над словами ХММ0 и ХММ1 и если хотя бы одно слово установлены в -1 устанавливаем флаг нуля ZF=0.— если флаг нуля ZF=0 то значит число не имеет записи о значении экспоненты и необходимо миновать участок кода связанный с ее дешифровкой.
pcmpeqw xmm0, Mask_001
pcmpeqw xmm1, Mask_010
paddw xmm0, xmm1
pcmpeqb xmm1, xmm1
ptest xmm0, xmm1
jnz @f
EDX два символа начиная с символа который следует за последним символом дробной части Числовой строки и на который указывает END_FRAC.— устанавливаем флаг нуля
ZF=1 если младший байт регистра EDX равен 0.— если флаг нуля
ZF=1[/INLINE то значит число не имеет записи о значении экспоненты и необходимо миновать участок кода связанный с ее дешифровкой.
movzx edx, word ptr[CUR_CHAR + END_FRAC]
test dl, dl
jz @f
5 в регистре EDX в результате чего если в регистре содержатся символы строчных букв они будут преобразованы в прописные.— устанавливаем флаг нуля
ZF=0 если значение младшего байт регистра EDX НЕ равно значению символа Е.— если флаг нуля
ZF=0 то значит числовая строка содержит критическую ошибку в оформлении и необходимо выйти из процедуры вернув код ошибки.btr edx, 5
cmp dl,'E'
jnz ErrorExit
HASH_STR бит указанный в EXP_CHAR удаляя его из хеша, теперь при следующем сканировании бит указывающий на символ экспоненты Е будет проигнорирован.— увеличиваем значение
EXP_CHAR на 1 перемещая указатель на следующий символ экспоненты.— сбрасываем в
HASH_STR бит указанный в EXP_CHAR удаляя его из хеша, теперь при следующем сканировании бит указывающий на символ знака экспоненты плюс/минус будет проигнорирован и устанавливаем флаг переноса CF=1 если значение бита было 0 что означает что знак экспоненты отсутствовал.— если флаг переноса
CF=1 то значит символ знака экспоненты отсутствует в Числовой строке и необходимо загрузить в младшее слово регистра EDX символ плюс содержащийся в константе Plus.— складываем значение указателя на текущий символ экспоненты
EXP_CHAR с флагом переноса CF=1 для учета позиции символа знака экспоненты при ее наличии.btr HASH_STR, EXP_CHAR
inc EXP_CHAR
btr HASH_STR, EXP_CHAR
cmovnc dx, Plus
adc EXP_CHAR, 0
Таким образом если числовая строка не содержит знака экспоненты то для обработки будет принудительно загружен символ
плюс при этом позиция текущего символа останется на месте. В случае наличия наличия знака экспоненты указатель на текущий символ будет перемещен на следующий символ после символа знака.ZF=1 если значение регистра DH равно символу плюс.— устанавливаем регистр
DL в значение 1 если флаг нуля ZF=1.— устанавливаем флаг нуля
ZF=1 если значение регистра DH равно символу минус.— устанавливаем регистра
DH в значение 1 если флаг нуля ZF=1.— копируем значение бита номер
8 регистра DX во флаг переноса CF.— складываем
LEN_CELL и флаг переноса CF.— устанавливаем флаг нуля
ZF=1 если значение регистр EDX равно 0.— если флаг нуля
cmp dh,'+'
setz dl
cmp dh,'-'
setz dh
bt dx, 8
adc LEN_CELL, 0
Таким образом информация о знаке экспоненты сохраняется в нулевом бите
LEN_CELL, учитывая что LEN_CELL изначально хранит удвоенное значение количество символов между символом точки и первым символом значащего числа то его нулевой бит всегда имеет нулевое значение и загрузка в него символа знака экспоненты не исказит значение.EAX в N_Z_CHAR восстанавливая значение N_Z_CHAR ранее сохраненное в EAX в пункте 4.4.3.— обнуляем регистр
EAX.— устанавливаем в
EAX бит номер которого указан в EXP_CHAR и который соответствует номеру символа следующего сразу после знака экспоненты если он есть или символа экспоненты если знак экспоненты отсутствует.— складываем значение регистра
EAX и целое числа, размером в двойное слово, со значением -1 и получаем в EAX значение в котором все биты соответствующие символам до символа указанного в EXP_CHAR принимают значение 1 а после значение 0.— инвертируем значение
EAX теперь все биты установлены в 1 соответствуют символам следующим после знака экспоненты не включая его.—
mov N_Z_CHAR, eax
xor eax, eax
bts eax, EXP_CHAR
add eax, -1
not eax
and N_Z_CHAR, eax
bsf N_Z_CHAR, N_Z_CHAR
movdqu xmm0,[CUR_CHAR + N_Z_CHAR]
bsf END_CHAR, END_CHAR
jz ErrorExit
movzx eax, byte ptr[CUR_CHAR + END_CHAR]
cmp eax, 20h
ja ErrorExit
add rdx,(1 + 1 shl 09h + 1 shl 0Dh + 1 shl 20h)
bt rdx, rax
jnc ErrorExit
sub N_Z_CHAR, END_CHAR
cmp N_Z_CHAR, -4
; jnc ErrorExit
@@: cmp byte ptr[BUFF_STR + 00000000 + xmmword + dword - byte],'5'
mov dword ptr[BUFF_STR + 00000000 + xmmword + dword - byte],'0000'
movd dword ptr[BUFF_STR + N_Z_CHAR + xmmword + qword - byte], xmm0
; вычисление экспоненты и младшей части Числа #region
movdqu xmm0,[BUFF_STR + 0000000 - byte]
movdqu xmm1,[BUFF_STR + xmmword - byte]
psubb xmm0, xmm2
psubb xmm1, xmm2
pmaddubsw xmm1, xmmword ptr Xmm_0001
pmaddwd xmm1, xmmword ptr Xmm_0010
; #endregion
; вычисление множителя #region
movd rax, xmm1
sbb rax, -1
movd xmm1, eax
shr rax, 20h
movd xmm2, rbx
mov ebx, eax
neg eax
sar LEN_CELL, 1
cmovc ebx, eax
add ebx, LEN_CELL
mov eax, ebx
neg eax
cmovns ebx, eax
mov rax, 0A000000000000000h
mov MANT_ARG, 0CCCCCCCCCCCCCCCCh
cmovs MANT_ARG, rax
mov eax, 3
mov LOGB_ARG, -3
cmovs LOGB_ARG, eax
mov MANT_MUL, 1
mov LOGB_MUL, 0
shr HASH_MUL, 1
cmovc MANT_MUL, MANT_ARG
cmovc LOGB_MUL, LOGB_ARG
@@: jz @f
mov rax, MANT_ARG
mul rax
bt rdx, 3Fh
setnc cl
adc LOGB_ARG, LOGB_ARG
shld rdx, rax, cl
mov MANT_ARG, rdx
shr HASH_MUL, 1
jnc @b
mov rax, rdx
mul MANT_MUL
bt rdx, 3Fh
setnc cl
adc LOGB_MUL, LOGB_ARG
shld rdx, rax, cl
mov MANT_MUL, rdx
test HASH_MUL, HASH_MUL
jmp @b
@@: movd rbx, xmm2
; #endregion
; вычисление целой части Числа #region
psubb xmm0, xmm2
pmaddubsw xmm0, xmmword ptr Xmm_0001
pmaddwd xmm0, xmmword ptr Xmm_0010
pmulld xmm0, xmmword ptr Xmm_0100
phaddd xmm0, xmm0
movd eax, xmm0
imul rax, Mul_0001
pextrd edx, xmm0, 1
imul rdx, 02710h
add rax, rdx
movd edx, xmm1
add rax, rdx
bsr rcx, rax
add LOGB_MUL, ecx
inc cl
shrd rax, rax, cl
; #endregion
; вычисление числа #region
mul MANT_MUL
bt rdx, 3Fh
setnc cl
adc LOGB_MUL, 3FFh
shld rdx, rax, cl
shl rdx, 1
shrd rdx, r11, 11
shrd rdx, rsp, 1
btr esp, 0
movd xmm0, rdx
; #endregion
ret
ErrorExit: ; аварийный выход #region
mov ecx, -1
pcmpeqb xmm1, xmm1
psllq xmm1, 52 + 1
psrlq xmm1, 1
ret
; #endregion
gsaw
Сначала подумал «вот! краткость сестра таланта». Потом догадался кликнуть на ссылки, а там оказывает запрятан код. Особенно понравилось.
4.13. Выход из процедуры.
ret4.8. Вычисление чего-то зачем-то
; #regionK-ILYA-V Автор
Глянул в блокнот, там сейчас 602 строки, нужно наверное еще строк 300-400 до полного счастья. Буду рад любой помощи.
Место выхода имеет смысл обозначить как можно более видимым, посредством расположения его на верху списка.
Рад что вам понравилось.