Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Программирование [игнор отключен] [закрыт для гостей] / IntToPChar на ASM для DELPHI / 25 сообщений из 54, страница 1 из 3
05.01.2017, 18:36
    #39379770
Midgard90
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Всем доброго времени суток!
Существуют ли программисты, помнящие ASM? ))
Столкнулись с проблемой: в Delphi нет подобной функции, есть IntToStr, но на выходе string. В большом проекте нужен именно PChar. На С++ это itoa, atoi.
Сделал костыль из стандартной IntToStr путём комментирования функции задела строки и дёргаю в наглую после выхода из неё по указателю шмат памяти:

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
function IntToText(Buffer: PChar; MaxLen: Cardinal; Value: Integer): Integer;

  procedure __Int32ToStr(Value: Integer);
  asm
    PUSH    ESI
    MOV     ESI, ESP
    SUB     ESP, 16
    XOR     ECX, ECX       // base: 0 for signed decimal
    PUSH    EDX            // result ptr
    XOR     EDX, EDX       // zero filled field width: 0 for no leading zeros
    CALL    __CvtInt
    MOV     EDX, ESI
    POP     EAX            // result ptr
    ADD     ESP, 16
    POP     ESI
  end;
var
  Pter : PChar;
  Length : integer;
begin
  __Int32ToStr(Value);

  asm
    mov Pter, EAX
    mov Length , ECX
  end;
  if Length <= MaxLen then begin
    Move(Pter^,Buffer^,Length);
    Result := Length;
  end
  else begin
    Result := -1;
    exit;
  end;
end;



Тоже самое для int64 и overlode. Если кому пригодиться, могу скинуть для int64. Функцию __CvtInt тоже пришлось перетянуть из сорцов делфи). Мне не понравилось это решение.

Нашёл 2 исходника на асм по такой же задаче для int и int64, единственное - использовались регистры ax,bx,cx... и т д. и генерился 16 битный код. Могу скинуть также оба исходника и ссыль на них.
Так вот суть вопроса: найденая вставочка int в дельфе отрабатывает на ура, быстрее чем костыльный вариант. Но с int64 возникли трудности. я не мастер asmа, но чую не так я записываю в стек или перемещаю указатель.
Вот исходник со статьи:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
use16                       ;Генерировать 16-битный код
org 100h                    ;Программа начинается с адреса 100h
    jmp start               ;Перепрыгнуть данные
;-------------------------------------------------------------------------------
; Данные
a       dq 1600000000000000
b       dq 33000000
c       dq 12345678901234567890
s_a     db 'a = $'
s_b     db 'b = $'
s_c     db 'c = $'
endline db 13,10,'$'
s_pak   db 'Press any key...$'
;-------------------------------------------------------------------------------
; Код
start:
    mov di,s_a
    call print_str          ;Вывод строки 'a = '
    mov bx,a
    call print_qword_udec   ;Вывод числа a
    call print_endline      ;CR+LF
 
    mov di,s_b
    call print_str          ;Вывод строки 'b = '
    mov bx,b
    call print_qword_udec   ;Вывод числа b
    call print_endline      ;CR+LF
 
    mov di,s_c
    call print_str          ;Вывод строки 'c = '
    mov bx,c
    call print_qword_udec   ;Вывод числа c
    call print_endline      ;CR+LF
 
    mov di,s_pak
    call print_str          ;Вывод строки 'Press any key...'
    mov ah,8                ;\
    int 21h                 ;/ Ввод символа без эха
    mov ax,4C00h            ;\
    int 21h                 ;/ Завершение программы
 
;-------------------------------------------------------------------------------
;Процедура вывода учетверённого слова на консоль в десятичном виде (без знака)
; BX - адрес qword
print_qword_udec:
    push di
    sub sp,22               ;Выделение места в стеке для буфера
    mov di,sp               ;DI = адрес буфера
    push word[bx+6]         ;\
    push word[bx+4]         ; \
    push word[bx+2]         ; / Помещение qword в стек
    push word[bx]           ;/
    call qword_to_udec_str  ;Преобразование qword в строку
    mov byte[di],'$'        ;Добавление символа конца строки
    mov di,sp               ;DI = адрес буфера со строкой
    call print_str          ;Вывод строки на консоль
    add sp,22               ;Восстановление указателя стека
    pop di
    ret
 
;-------------------------------------------------------------------------------
;Процедура преобразования учетверённого слова
;в строку в десятичном виде (без знака)
;Вход: qword в стеке (младший байт по младшему адресу!)
;  DI - буфер для строки (20 символов). Значение регистра не сохраняется.
qword_to_udec_str:
    push bp                 ;Сохранение BP
    mov bp,sp               ;BP=SP
    push ax                 ;Сохранение используемых регистров
    push cx
    push bx
    push si
 
    xor cx,cx               ;Обнуление CX
    mov bx,10               ;В BX делитель (10 для десятичной системы)
 
.lp1:                       ;Цикл получения остатков от деления
    xor dx,dx
    mov si,8                ;SI = смещение слова для деления
 
.div64:                     ;Цикл 64-битного деления
    mov ax,[bp+si+2]        ;Загрузка слова в AX
    div bx                  ;AX = (DX:AX)/10; DX = остаток
    mov [bp+si+2],ax        ;Сохранение результата деления слова
    sub si,2                ;Уменьшение смещения на 2
    jnz .div64              ;Переход к началу цикла, если SI != 0
 
    add dl,'0'              ;Преобразование остатка в код символа
    push dx                 ;Сохранение в стеке
    inc cx                  ;Увеличение счетчика символов
    or ax,[bp+6]            ;\
    or ax,[bp+8]            ; > Проверка частного на равенство 0 ;)
    or ax,[bp+10]           ;/
    jnz .lp1                ;Переход к началу цикла, если частное не 0.
 
.lp2:                       ;Цикл извлечения символов из стека
    pop dx                  ;Восстановление символа из стека
    mov [di],dl             ;Сохранение символа в буфере
    inc di                  ;Инкремент адреса буфера
    loop .lp2               ;Команда цикла
 
    pop si                  ;Восстановление сохранённых регистров
    pop bx
    pop cx
    pop ax
    leave                   ;Восстановление SP и BP
    ret 8                   ;Возврат с выталкиванием 8 байт из стека



Моя переделка заключалась в замене всех основных регистров на 32х битные, то есть ax на eax, bx на ebx и т д. так как 16 битный дельфа строить отказывается. Если надо скину переделку. Дельфа зацикливается при делении числа на 10 и записи остатка. Пока стек не кончится)) где я косячу? Данный код работает в asm - проверял.
...
Рейтинг: 0 / 0
05.01.2017, 18:53
    #39379777
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
компилятор дельфи такой страшный код генерит, что на асме писать нужно?
...
Рейтинг: 0 / 0
05.01.2017, 19:02
    #39379784
Midgard90
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Нет) Можно и обойтись средствами Делфи. Важна скорость, это ключевой момент. большие объёмы данных, много расчётов. Поэтому прибегли к АСМ. Для примера нагрузочных тестов 100 000 000 преобразований на каждом ядре:
Функциями дельфи - профит -0,38, процент скорости - 30%
Костыльный вариант - профит +0,75, процент скорости - 81%
Самодел из взятых исходников - профит -0,95, процент скорости - 90%

Это если чисто int.
...
Рейтинг: 0 / 0
05.01.2017, 19:29
    #39379795
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Midgard90Но с int64 возникли трудности. я не мастер asmа, но чую не так я записываю в стек или перемещаю указатель.

Дельфи x64 использует Microsoft's Win64 calling convention
соответственно первые 4 параметра - RCX,RDX,R8,R9
...
Рейтинг: 0 / 0
05.01.2017, 19:30
    #39379796
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
...
Рейтинг: 0 / 0
05.01.2017, 20:08
    #39379821
Midgard90
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Спасибо за инфу, но немного не то. Речь идёт о Delphi x32 (Delphi 7). Судя по их исходникам с asm в те года 64е регистры были мечтой)
...
Рейтинг: 0 / 0
05.01.2017, 20:20
    #39379826
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Midgard90,

так где лыжи не едут - в x86 ?

что надо то - int64 в pchar?
...
Рейтинг: 0 / 0
05.01.2017, 20:44
    #39379837
Midgard90
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Да, всё верно.
Лыжи не ежут в старой Delphi 7, в асмовой вставке int64ToPChar. IntToPChar сработала на ура, сложностей не было - число ложится в регистр как есть.
int64ToPChar - не как есть, как я понял это указатель в стеке, или не указатель, инфы не нарыл. видать из за этого я не правильно кладу старшую и младшую части, и из за этого соответственно кривит сам цикл деления на 10
...
Рейтинг: 0 / 0
05.01.2017, 21:40
    #39379854
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Midgard90int64ToPChar - не как есть, как я понял это указатель в стеке, или не указатель, инфы не нарыл. видать из за этого я не правильно кладу старшую и младшую части, и из за этого соответственно кривит сам цикл деления на 10

какой бы мусор на регистры не попал - зацикливаться ничего не должно,
на выходе максимум 19 цифр.

а команда div у интела изрядно косая.
ничего хорошего из лобового деления edx:eax на 10 не получится
...
Рейтинг: 0 / 0
05.01.2017, 21:52
    #39379860
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
сколь неприятна процедура деления int64 на x86 - можно увидеть - http://www.jbox.dk/sanos/source/lib/lldiv.asm.html
...
Рейтинг: 0 / 0
06.01.2017, 00:20
    #39379894
Midgard90
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Мысль понял, спасибо за совет!)
Буду курить данный пример. Ещё раз благодарю за отзывчивость!

Но вот кстати!) В исходных сорцах Делфи 7 похожий алгоритм на тот, что я нарыл. За исключением что я в стек кидаю остатки, а там как я понял в память сразу пишут. Но тоже div))
...
Рейтинг: 0 / 0
06.01.2017, 00:27
    #39379898
Aleksandr Sharahov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Midgard90,

1. Вместо деления но 10 можно умножать на обратный.
2. Вместо деления на 10 можно делить на 100 и брать из таблицы сразу 2 цифры.
3. Можно длинное число разбить на 2 куска и разбираться с каждым отдельно.
4. Можно срисовать код с современной Delphi и не мучиться.
...
Рейтинг: 0 / 0
06.01.2017, 07:52
    #39379941
Пётр Седов
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Midgard90, может сначала попробовать Pascal-код?
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
function int32_to_text(buf: pchar; buf_len: integer; value: integer): integer;
const
  buf2_len = 11; // самое длинное, что может быть -- это '-2147483648' = -pow(2, 31)
var
  n: cardinal;
  buf2: array [0 .. buf2_len - 1] of char;
  p, result_len: integer;
begin
  // считаем abs(value)
  if value >= 0 then begin
    n := value;
  end else begin
    n := cardinal(-(value + 1)) + 1; // единицы, чтобы не было переполнения, когда value = -2147483648
  end;

  p := buf2_len;
  while n >= 10 do begin
    dec(p);
    buf2[p] := chr(ord('0') + n mod 10);
    n := n div 10;
  end;
  dec(p);
  buf2[p] := chr(ord('0') + n);

  if value < 0 then begin
    dec(p);
    buf2[p] := '-';
  end;

  result_len := buf2_len - p;
  if result_len <= buf_len then begin
    move(buf2[p], buf^, result_len);
    result := result_len;
  end else begin
    result := -1;
  end;
end;

function int64_to_text(buf: pchar; buf_len: integer; value: int64): integer;
const
  buf2_len = 20; // самое длинное, что может быть -- это '-9223372036854775808' = -pow(2, 63)
var
  n: int64; // в Delphi 7 нет uint64
  buf2: array [0 .. buf2_len - 1] of char;
  p, result_len: integer;
  part: cardinal;
begin
  // считаем abs(value)
  if value >= 0 then begin
    n := value;
  end else begin
    // hardcode для минимального значения, потому что не можем сделать '-value'
    if value = -9223372036854775807 - 1 then begin
      result_len := 20;
      if result_len <= buf_len then begin
        move('-9223372036854775808', buf^, result_len);
        result := result_len;
      end else begin
        result := -1;
      end;
      exit;
    end;
    n := -value;
  end;

  p := buf2_len;

  // разбиваем 64-битное число на части, чтобы почти всю работу сделать 32-битными операциями

  // берём первую часть
  part := n mod 1000000000;
  n := n div 1000000000;

  // пишем первую часть
  while part >= 10 do begin
    dec(p);
    buf2[p] := chr(ord('0') + part mod 10);
    part := part div 10;
  end;
  dec(p);
  buf2[p] := chr(ord('0') + part);

  if n <> 0 then begin // если что-то осталось
    // дописываем первую часть, чтобы было ровно 9 цифр
    while p > buf2_len - 9 do begin
      dec(p);
      buf2[p] := '0';
    end;

    // берём вторую часть
    part := n mod 1000000000;
    n := n div 1000000000;

    // пишем вторую часть
    while part >= 10 do begin
      dec(p);
      buf2[p] := chr(ord('0') + part mod 10);
      part := part div 10;
    end;
    dec(p);
    buf2[p] := chr(ord('0') + part);

    if n <> 0 then begin // если что-то осталось
      // дописываем вторую часть, чтобы было ровно 9 цифр
      while p > buf2_len - 18 do begin
        dec(p);
        buf2[p] := '0';
      end;

      // пишем третью часть (всего одна цифра)
      assert(n <= 9);
      dec(p);
      buf2[p] := chr(ord('0') + cardinal(n));
    end;
  end;

  if value < 0 then begin
    dec(p);
    buf2[p] := '-';
  end;

  result_len := buf2_len - p;
  if result_len <= buf_len then begin
    move(buf2[p], buf^, result_len);
    result := result_len;
  end else begin
    result := -1;
  end;
end;

Если скорость не устроит, тогда уже переписывать на ассемблере.

Изопропилкомпилятор дельфи такой страшный код генерит, что на асме писать нужно?Если Delphi-йский компилятор для кода типа «n mod 10 ... n div 10» генерирует 2 инструкции div, то да :).

Aleksandr Sharahov3. Можно длинное число разбить на 2 куска и разбираться с каждым отдельно.Мне удалось разбить только на 3 части.
...
Рейтинг: 0 / 0
06.01.2017, 09:10
    #39379947
Aleksandr Sharahov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Пётр СедовЕсли Delphi-йский компилятор для кода типа «n mod 10 ... n div 10» генерирует 2 инструкции div, то да :).
если немного подумать, а не переводить стрелки на компилятор, то легко понять, что есть алгоритм с одним делением:
d:=n div 10;
r:=n - d * 10;



Пётр СедовМне удалось разбить только на 3 части.
если проявить смекалку, например, перейти к беззнаковым числам и поработать со старшими битами и с обратными числами, то удается получить 2 части.
...
Рейтинг: 0 / 0
06.01.2017, 11:15
    #39379995
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Пётр Седов
Код: pascal
1.
2.
// если что-то осталось
      // дописываем вторую часть, чтобы было ровно 9 цифр


этого можно избежать изначально заполнив весь буфер нулями
...
Рейтинг: 0 / 0
06.01.2017, 11:48
    #39380024
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Aleksandr Sharahovесли проявить смекалку
многое можно сделать - без делений в цикле обойтись, например.
...
Рейтинг: 0 / 0
06.01.2017, 12:25
    #39380035
Dima T
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
ИзопропилAleksandr Sharahovесли проявить смекалку
многое можно сделать - без делений в цикле обойтись, например.
Можно пример как обойтись вообще без делений в цикле?
...
Рейтинг: 0 / 0
06.01.2017, 12:26
    #39380036
Midgard90
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Пётр Седов,

Благодарю! Примерно тоже самое и было сперва на дельфях писано, медленно) Но идея разбить на 2 куска и вызвать процедурку для простого уже intа - мне кажется будет выигрышной. Главное не запутаться с переходящим, дёрнуть заранее знак и склеить в конце 2 массива и их длины. сегодня попробую, вечером кину код)

Всех благодарю за советы!
...
Рейтинг: 0 / 0
06.01.2017, 13:27
    #39380060
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Dima TИзопропилпропущено...

многое можно сделать - без делений в цикле обойтись, например.
Можно пример как обойтись вообще без делений в цикле?
поделить можно один раз, а затем умножать
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
void itoa_x(char *buf, uint32_t val)
{
	double tmp = val / 1e9;
	for (size_t i = 0; i < 10; i++)
	{
		int digit = (int)tmp;
		buf[i] = '0' + (char)digit;
		tmp = (tmp - digit) * 10.0;
	}
}



дальше - переход к фиксированной точке, разделение на группы и т д.
...
Рейтинг: 0 / 0
06.01.2017, 13:42
    #39380064
Dima T
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Не работает, погрешности преобразования double мешают
Код: sql
1.
itoa_x(11) => '0000000010'


Тест
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
void test() {
	char buf[11] = {0};
	for(int i = 1; i < 1000000000; i++) {
		itoa_x(buf, i);
		if(atoi(buf) != i) {
			printf("itoa_x(%d) => '%s'\n", i, buf);
			break;
		}
	}
}

...
Рейтинг: 0 / 0
06.01.2017, 13:51
    #39380067
Dima T
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Если погрешность добавить, то вроде работает
Код: plaintext
1.
		tmp = (tmp - digit + 1e-10) * 10.0;
...
Рейтинг: 0 / 0
06.01.2017, 14:13
    #39380076
Dima T
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Преобразования double-int тоже не легкая операция.
Затестил время, деление целых почти вдвое быстрее.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
void itoa_d(char *buf, uint32_t val) {
	for (size_t i = 10; i--; )	{
		int v = val / 10;
		buf[i] = '0' + (char)(val - v * 10);
		val = v;
	}
}
...
Рейтинг: 0 / 0
06.01.2017, 14:27
    #39380080
Midgard90
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Ребята не гоните коней)))
вещественные не нужны) и есть функция в дельфях вроде FloatToPChar если не ошибаюсь))
...
Рейтинг: 0 / 0
06.01.2017, 14:29
    #39380082
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Dima T,

я же сразу сказал, что это только направление, реально нужно на фиксированную точку перевести
придётся сделать ))
...
Рейтинг: 0 / 0
06.01.2017, 15:05
    #39380086
Aleksandr Sharahov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
IntToPChar на ASM для DELPHI
Dima TМожно пример как обойтись вообще без делений в цикле?

1. Заменить деление умножением, умножать на обратный.
2. Заменить деление вычитанием, делить в столбик.
...
Рейтинг: 0 / 0
Форумы / Программирование [игнор отключен] [закрыт для гостей] / IntToPChar на ASM для DELPHI / 25 сообщений из 54, страница 1 из 3
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]