Господа. Есть тупой вопрос. Я только начинаю в asm вникать... Так вот, написал я функцию перевода int в string... всё работает, в переменную S , благополучно помещаюься готовые символы, но поместить это в Result не удаётся ...в чемто затык Code (Text): function i2s(Value:Integer):String; var s:array [0..18] of char; begin Fillchar(s,18,0); asm lea edi,s mov eax,Value xor ebx,ebx mov edx,0 mov ecx,10 @main: div ecx push edx xor edx,edx inc ebx cmp eax,0 jnz @main @cvt: pop eax add eax,$30 mov byte ptr [edi],al inc edi dec ebx test ebx,ebx jnz @cvt end; // end asm Result := s; // затык собственно тут, вылазит эксэпшн, то есть в S уже символы готовы. end; // end begin
Code (Text): function IntToStr(Value:Integer):String; asm XOR ECX, ECX PUSH ECX ADD ESP, -0Ch PUSH EBX LEA EBX, [ESP + 15 + 4] PUSH EDX CMP EAX, ECX PUSHFD JGE @@1 NEG EAX @@1: MOV CL, 10 @@2: DEC EBX CDQ IDIV ECX ADD DL, 30h MOV [EBX], DL TEST EAX, EAX JNZ @@2 POPFD JGE @@3 DEC EBX MOV byte ptr [EBX], '-' @@3: POP EAX MOV EDX, EBX CALL System.@LStrFromPChar POP EBX ADD ESP, 10h end; function StrToInt(Value: string): Integer; asm XCHG EDX, EAX XOR EAX, EAX TEST EDX, EDX JZ @@exit XOR ECX, ECX MOV CL, [EDX] INC EDX CMP CL, '-' PUSHFD JE @@0 @@1: CMP CL, '+' JNE @@2 @@0: MOV CL, [EDX] INC EDX @@2: SUB CL, '0' CMP CL, '9'-'0' JA @@fin LEA EAX, [EAX+EAX*4] // LEA EAX, [ECX+EAX*2] // JMP @@0 @@fin: POPFD JNE @@exit NEG EAX @@exit: end;
K10, спасибо. я это тоже видел в SysUtils.pas. Я просто хотел собственным способом конвертировать (((=
так тебе впору познакомиться с великолепными работами Владимира Кладова, который Delphi + asm уважает очень. Проект KOL + MCK самое то, а то что ты делаешь там есть, только вот насчет именно в asm не помню. Рекомендую http://www.kolnmck.ru/lessons/faq.shtml
что то да, как-то грамоздко смотртся такое преобразование Int2Str: Code (Text): function Int2Str( Value : Integer ) : String; var Buf : array[ 0..15 ] of Char; Dst : PChar; Minus : Boolean; D: DWORD; begin Dst := @Buf[ 15 ]; Dst^ := #0; Minus := False; if Value < 0 then begin Value := -Value; Minus := True; end; D := Value; repeat Dec( Dst ); Dst^ := Char( (D mod 10) + Byte( '0' ) ); D := D div 10; until D = 0; if Minus then begin Dec( Dst ); Dst^ := '-'; end; Result := Dst; end;
binary Затык в том, что нужно сохранять\восстанавливать регистры ebx\edi\esi, т.к. сама дельфя этого перед асм-вставками не делает, полагаясь на кодера
binary mov eax,Value - ну нужно, т.к. при fastcall параметр уже будет в EAX Fillchar(s,18,0); - зачем, если буфер потом заполняется? mov edx,0 - XOR EDX, EDX pop eax add eax,$30 mov byte ptr [edi],al inc edi dec ebx test ebx,ebx // не нужно, .т.к DEC уже изменяет флаги jnz @cvt лучше что-то вроде // ESI - указатель на s // ECX - количесво символов (которое в EBX) POP EAX ADD EAX, '0' STOSB EAX Самое главное: В процедуре можно свободно оперировать регистрами EAX, EDX и ECX. Другие регистры (в твоем случае EBX и EDI) нужно в начале процедуры сохранять в стеке и перед выходом восстанавливать. Result := s; - скорее всего нужно SetString(s, <количесво_символов>) Вобще, зачем у тебя элементы сохраняются в стеке, потом вытаскиваются? Не лучше в одном цикле производить деление, преобразовывать в цифры и помещать в s ?
Кстати вопрос: стоит ли заводить временный буфер of char, если можно сделать SetLength(Result,19), использовать её как буфер и после окончания асм-вставки укоротить при помощи SetLength(Result,ebx_который_надо где_то_сохранить)?
K10 Для чисто асм-функций да, а для асм-вставок не факт. В частности в данном варианте регистр eax перед FillChar "сам по себе" не сохраняется и соотв-но после FillChar в нем будет мусор. Именно из-за этих штучек лучше юзать не вставки, а нормальные асм-функции, чтобы не мешать пас- и асм-код Сумневаюсь, тогда уж rep movsb + add esp Можно, но о быстродействии как я понимаю тут речь вообще не идет Конечно лучше, но символы формируются с конца и напрямую их в s не засунешь, нужно использовать ту же хитрость, что и в IntToStr - писать от конца к началу, но тогда от "собственного способа" практически ничего не останется CyberManiac Стоит, по той же причине - символы формируются с конца, поэтому по любому нужно будет их перемещать - тут без разницы что в пределах одного буфера, что из одного в другой, но зато с временным буфером ничего урезать не нужно
leo Копирование предложенным способом потребует вычисления длины ASCIIZ-строки, что само по себе зло и мерзость, плюс опять же создание AnsiString нужной длины и копирование в неё данных. Отрезание хвоста короткой строки - операция более быстрая, требует только изменения счётчика символов в строке и установки завершающего нулевого символа для совместимости с WinAPI.
leo Ну да, я просто всегда делал полностью асм-функции... так заюзать тот же stosb с движением от конца? CyberManiac Да, точно, наверно можно даже не укорачивать, а просто поставить 0 в конце, поюзается да освободится вся целиком
дак я пробовал кнешно. по непонятным мне причинам, все те же манипуляции с Result-om что и с о временным буфером результатов не дают, Result не меняется ну никак, а буффер меняется (= Самое интересное, что если Result, например Pointer , то всё прекрасно работает, суну сразу в Result в асме, без всякого идусского 'Result:=s' по поводу скорости... ну кнешн, я хотелбы чтобы в итоге работало быстрее чем System.IntToStr (((= господа, пасибо всем ! А то я право уже за йадом пошёл в аптеку, или пат паровоз бы кинулся (=
CyberManiac Каким это предложенным и кем ? Подсчет результирующей длины делается в процессе получения символов и его можно заюзать в SetString\LStrFromPCharLen Во-первых, в пред.посте ты не упоминал о shortstring. Во-вторых, если в проге по умолчанию юзаются длинные строки, то один фиг при дальнейшем присваивании рез-та, например ShowMessage(I2S(i)); произойдет неявное преобразование короткой строки к AnsiString Кто-ж, спорит. Но кроме обрезки еще нуна переместить символы, т.к. их кол-во заранее не известно и первый полученный символ, он же последний в строке, нужно писать куда-то в конец этой строки. Или посоветуешь дополнить спереди пробелами, а затем вызвать TrimLeft Или может предлагаешь пойти на грязный хак и обрезать не хвост, а голову (дописать перед первым символом байт длины и вернуть указатель на первый символ) ? Но в данном варианте, когда строка возращается через Result этот трюк не пройдет, тогда нужно юзать или var-параметр или же передавать в функцию указатель на буфер и возвращать pShortString, которая будет указывать куда-то внутрь этого буфера (тогда можно и константную AnsiString с-имитировать c RefCount=-1)
binary Размечтался, борманы все же не зря свой хлеб столько лет кушают Зря ты вообще со строками в асме связался, т.к. практически все строковые функции в дельфях - это несуществующие перегруженные функции, вместо которых в коде в зависимости от типа параметров подставляются недокументированные функции из system.pas типа LStrXXX
PS: Если речь идет о повышении скорости, то нужно не мелочевками с буферами заниматься, а устранить главный супертормоз - операцию деления. Можно "по простому" заменить деление умножением на константу (см.пример MyIntToStr), а можно и суперразвернутый алгоритм из мануала AMD позаимствовать (см."примерчик"), если не испугаешься А, Y_Mur опередил со своей гипер(пупер)ссылкой
Брррр, мааать чесная . Как сказало бы подсказка в The Elder Scrolls III - Morrowind : "Вам необходимо поспать, чтобы обдумать полученные знания " %) ...блин точно, затык был в том что я не сохранял\восстонавливал ебх и едх