Уважаемые теоретики/оптимизаторы! Не подскажете ли что путное по сабжу? Имеется, допустим, 0xFE(число в регистре), нужно получить '254',0. Все что мне в голову приходит выглядит чересчур тяжеловесным, вот к примеру Код (Text): xor edx, edx mov bl, 10 movzx ax, Number div bl xchg ah, dl rol edx, 8 div bl xchg ah, dl rol edx, 8 mov dl, al add edx, 303030h mov eax, DecString mov [eax], edx Наверняка есть способ умнее...
если число не больше байта, то самый короткий способ деления на 10 выглядит как AAM. Т.е. помещаешь в AL свое число, делаешь AAM и в результате имеешь в AL остаток от деления на 10. Теперь остается присобачить это к общему алгоритму
А вот так не подойдет ) mov ecx, 0feh mov ebx, 303030h @@loop: inc bl cmp bl, 3Ah jnz @f and bl, 0F0h inc bh cmp bh, 3Ah jnz @f and bh, 0F0h add ebx, 10000h @@: loop @@lop
Если важна скорость, то в данном случае можно использовать табличку - "всего-то" 256*4 байт. Заполнить например так: Код (Text): dummy dd 0 table rd 256 build_table: xor eax, eax xor edx, edx .l: lea ecx, [eax+303030h] bswap ecx mov [edx*4+table-1], ecx inc eax aaa cmp ah, 10 jc @f add eax, 0F600h @@: inc dl jnz .l Зато преобразование - одной командой Код (Text): mov eax, [eax*4+table] (можно сократить таблицу в 2 раза, если хранить только чётные числа) Для единичных преобразований это может быть медленно, если таблица не в кеше, но на больших объёмах должен буть хороший выигрыш.
Broken Sword Действительно, AAM - самый короткий способ деления на 10. Но это тоже самое деление, только специализированное. Поэтому код будет изящнее, но по скорости выигрыш не значительный (по Фогу для P3: AAM 15 тактов против 19 для DIV r8, а для P4 56 против 60). _Juicy > "Наверняка есть способ умнее..." Если оптимизировать быстродействие, то можно поизвращаться. К примеру, использовать поразрядное уравновешивание с весами Wi = 100,100,50,20,10,10. Одно сравнение выглядит так: Код (Text): Вариант 1 (поразрядное прибавление): ;mov bl,30h cmp al,Wi setb dl sub dl,1 and dl,Mi ;маска для 50: -5 = FBh, для 20: -2 = FEh, для остальных FFh, т.е. можно исключить sub bl,dl and dl,Wi sub al,dl Вариант 2: (поразрядное вычитание) ;mov bl,32h для сотен или 39h для десятков sub al,Wi sbb dl,dl and dl,Mi ;маска add bl,dl and dl,Wi add al,dl По скорости варианты 1 и 2 получаются эквивалентны, но с sbb получается покороче. Используя 6 таких сравнений получаем немалый объем кода (> 100 байт), но выигрываем по скорости особенно на P4. Wintest от bogrus'а дает такие цифры (число тактов): Код (Text): Метод P3 P4 --------- ---- ----- _Juicy 55 160 уравновеш 40 80 Но, по-видимому, это изврат... PS: Можно еще поизвращаться c анализом двоичных разрядов и сложением BCD (типа того как в теме 0000h - 9999h). Но пока ничего толкового не вижу.
Деление на 10 (метод изобрел Terje Mathisen, код А.Фог) с остатком, можно попробовать заменить таким куском: Код (Text): mov edx,eax ; eax = x < 10004h (сохраняем для вычисления остатка) lea ebx,[eax*2+eax+3] lea ecx,[eax*2+eax+3] shl ebx,4 mov eax,ecx shl ecx,8 add eax,ebx shl ebx,8 add eax,ecx add eax,ebx shr eax,17 ; eax = x/10 (частное) lea ecx,[eax*8+eax] add ecx,eax ; ecx = (x/10)*10 sub edx,ecx ; edx = x - (x/10)*10 (остаток) Но с таблицей конечно будет сверх быстро в потоке
Предлагаю такой вариант. Код (Text): .386 .model flat, stdcall option casemap:none include windows.inc include kernel32.inc include user32.inc includelib user32.lib includelib kernel32.lib .data buffer db 4 dup(0),0 format db "%x",0 .code start: mov eax,0 dec eax invoke wsprintf,offset buffer,offset format,eax invoke MessageBox,0,offset buffer,0,0 call ExitProcess end start
NoName А в чём приимущества такого метода ? Ещё вариант с табличкой, не очень быстрый, т.к. три умножения, но места требует меньше: (можно подоптимизировать его чуток, это просто рыба) Код (Text): al2ascii: xor ebx, ebx movzx eax, al mov cl, 0CDh mul cl mov edx, eax shr eax, 8+3 shr edx, 8+3-4 and edx, 0Fh mov bl, [al2ascii_table+edx] shl ebx, 8 mul cl mov edx, eax shr eax, 8+3 shr edx, 8+3-4 and edx, 0Fh mov bl, [al2ascii_table+edx] shl ebx, 8 mul cl shr eax, 8+3-4 and al, 0Fh mov bl, [al2ascii_table+eax] mov eax, ebx ret al2ascii_table db '0','1',00,'2','3','3','4',00,'5','6',00,'7','8','8','9',00
Вот ещё рыба. тоже табличка, но код по меньше немного. Умножений нет, но используется daa (да здравствует четвёртый пень! и партиальные регистры. На скорость не тестил, но думаю должно быть неплохо на P6/Athlon, если доработать немного. Вариантов тут много может быть. Код (Text): al2ascii: movzx ecx, al shr ecx, 4 and eax, 0Fh mov al, [eax+magic_table+16] and al, 3Fh mov dh, [ecx+magic_table+16] shr dh, 6 mov dl, [ecx+magic_table] add al, dl daa adc ah, dh mov edx, eax and eax, 0Fh shl eax, 16 mov al, dh shr dl, 4 mov ah, dl or eax, 303030h ret ; 0 1 2 3 4 5 6 7 8 9 a b c d e f magic_table db 00h,16h,32h,48h,64h,80h,96h,12h,28h,44h,60h,76h,92h,08h,24h,40h db 00h,01h,02h,03h,04h,05h,06h,47h,48h,49h,50h,51h,52h,93h,94h,95h ЗЫ: daa можно разложить: проверять значения нибблов, и прибавлять 6, если больше 10 (это легко делать без ветвлений). Но в районе 50ти будут ошибки, т.к. таким способом не удаётся учесть перенос между нибблами :-( Пока красивого решения я не вижу, поэтому оставил daa.
..... mov ax, 0feh mov bl, 100 div bl xchg ah, al shl eax, 8 xchg ah, al mov bl, 10 div bl xchg ah, al add eax, 303030h
Ну ежели пошли таблички, то вот довольно быстрый вариант с таблицей 16*4 = 64 байт. Используется сложение неупакованных BCD по аналогии с темой "0000h - 9999h". Код (Text): ;таблица - это BCD представление чисел i*16 = 0,16,32,48 ..., i = 0..15 HiBCD dd 0,0106h,0302h,0408h,0604h,0800h,0906h,010102h, dd 010208h,010404h,010600h,010706h,010902h,020008h,020204h,020400h movzx eax,byte [Number] mov ecx,eax ;преобразуем младшие 4 бита в BCD and eax,0Fh sub eax,0Ah sbb edx,edx ;x>=10: edx=0; x<10: edx=FFFFFFFh and edx,010Ah add edx,eax ;x>=10: dh=0, dl=(x-10); x<10: dh = 1, dl = x xor edx,100h ;инвертируем единичку в dh ;берем BCD старших разрядов из таблицы shr ecx,4 mov eax,[ecx*4+HiBCD] add eax,00F6F6F6h ;корректируем для учета переносов при сложении add eax,edx ;складываем старший и младший BCD ;корректируем переносы mov ecx,eax shr ecx,4 ;если переноса не было, то старший hex байта = F, иначе 0 and ecx,060606h and eax,0F0F0Fh sub eax,ecx ;там где не было переноса вычитается 6 or eax,303030h bswap eax ;0123 -> 3210 shr eax,8 ;0321
По-моему константу 00F6F6F6h можно сразу прибавить ко всем ячейкам таблицы. xor edx,100h поменять на xor dh,1 вместо eax (and eax,0Fh и sub eax,0Ah) использовать al лучше - это на байт меньше. Вообще, с таблицами можно много вариантов придумать, но у всех недостаток - чем быстрее, тем больше таблицу нада :-(
S_T_A_S_ Насчет константы 0F6F6F6h - ес-но. Мне просто лень было с этим возиться. А вот замечания насчет DH и AL, не столь очевидны. Размер мы конечно экономим (я кстати поначалу так и сделал), но при add edx,eax на P3 получаем приличный штраф - у меня получилось около 4 тактов, хотя это возможно вместе с изменением условий выравнивания\декодирования. Проблема парциальных регистров на P3 хорошо известна, но почему-то ей часто пренебрегают Раз речь зашла о "мелочах", то у меня встречное замечание. В исходной задаче требовалось сохранить результат в строке DecString. Спрашивается: зачем в алгоритмах с поразрядным вычислением результата накапливать байты в r32, а не сразу сохранять готовый байт в строке. ИМХО - лишние сдвиги и парциальные регистры, а блок сохранения простаивает, хотя мог бы работать параллельно (за счет буферизации все равно никакой записи в память не происходит - все байтики автоматом копятся в буфере записи). По поводу таблиц - это верно. В моем варианте с BCD можно уменьшить таблицу наполовину до 8*4=32 байт, но придется добавить дополнительное сравнение 5 младшых разрядов с 20, т.е. дополнительные байты и такты. А вообще-то думается вполне компромиссным вариантом является замена div на быстрое деление по Mathisen-Fog или еще как. Кстати, пытался я упростить\ускорить вариант Фога с учетом, того что число < 256 и можно использовать приближенное деление 13/128 или 26/256 с коррекцией на 1 по знаку остатка. Но заметно лучше не получается.
пример х=0 до 120, надо x/10, тогда eax=x and eax, 0xFFFFFFFE; или sar eax, 1; add eax, eax lea edx, [eax+4*eax] lea eax, [edx+8*eax] sar eax, 7 eax=x/10
а вообже не понимаю к чему такая погоня за скоростью в данной ситуации? чем плохо то, что уже созданно?