Конференция "Прочее" » Передача параметров функции
 
  • dmk © (31.05.16 17:01) [20]
    >видя что ты его используешь, компилер тебя подстраховывает

    хмм ... у меня нет такого. Не сохранил, получи AV или висяк.
    Где-то в настройках?
  • SergP © (31.05.16 17:03) [21]

    >
    > EBX обьектный регистр, который нужно сохранять, видя что
    > ты его используешь, компилер тебя подстраховывает


    Да, я его использовал, но я сам его сохранял при этом.  
    Ну даже пусть он таким образом меня подстраховывает,
    но зачем в конце функции мне портить результат, который был в eax, командой mov eax,ebx ?
  • Rouse_ © (31.05.16 17:05) [22]

    > dmk ©   (31.05.16 17:01) [20]
    > >видя что ты его используешь, компилер тебя подстраховывает
    >
    > хмм ... у меня нет такого. Не сохранил, получи AV или висяк.
    >
    > Где-то в настройках?

    Хм, на ХЕ10 тоже не сгенерировалось, а вчера на домашней (ХЕ4) был такой код
  • Rouse_ © (31.05.16 17:07) [23]

    > SergP ©   (31.05.16 17:03) [21]

    Вариантов много, от ошибки генерации кода (судя по всему) до преднамеренно вставляемой инструкции (доки нужно смотреть)
  • SergP © (31.05.16 17:23) [24]

    > Rouse_ ©   (31.05.16 17:07) [23]
    >
    >
    > > SergP ©   (31.05.16 17:03) [21]
    >
    > Вариантов много, от ошибки генерации кода (судя по всему)
    > до преднамеренно вставляемой инструкции (доки нужно смотреть)


    Но вот в твоем коде [6] оно такого не вставило...

    Получается, что после того как написал что-то на асме, нужно ставить бряк, и в окне CPU-View всегда проверять что там компилятор намутил, и если он сделал мне такой подарок, переписывать код по-другому и снова проверять, и так далее, пока его добавления не перестанут противоречить логике работы моего кода?
  • Rouse_ © (31.05.16 17:24) [25]
    Вполне вероятно что действительно ошибка, сейчас пересмотрел свои исходные коды, там есть у меня всяческие подстраховки плана:

     function GetSPAddrFromPEB(var Len: Integer): Pointer;
     asm
     {$IFDEF WIN32}
       mov edx, FS:[$30]
       movzx ecx, word ptr [edx + $1F0]
       mov [eax], ecx
       mov eax, [edx + $1F4]
     {$ELSE}
       //  mov rdx, GS:[$60]
       // в старых версиях плывет кодогенерация при работе с сегментами
       // поэтому пропишем опкоды напрямую
       DB $65, $48, $8B, $14, $25, $60, $00, $00, 00
       movzx rax, word ptr [rdx + $2E8]
       mov word ptr [rcx], ax
       mov rax, [rdx + $2F0]
     {$ENDIF}
     end;
  • Rouse_ © (31.05.16 17:24) [26]

    > SergP ©   (31.05.16 17:23) [24]
    > Получается, что после того как написал что-то на асме, нужно
    > ставить бряк, и в окне CPU-View всегда проверять что там
    > компилятор намутил, и если он сделал мне такой подарок,
    > переписывать код по-другому и снова проверять, и так далее,
    >  пока его добавления не перестанут противоречить логике
    > работы моего кода?

    Конечно, проверять всегда надо - а как по другому-то?
  • Rouse_ © (31.05.16 17:27) [27]
    Есть другой вариант.
    Оформляй все свои асм блоки как процедуры - тогда получишь чистый код - без пролога и эпилога - тно вызов таких функций нудно контролировать самому
  • Rouse_ © (31.05.16 18:44) [28]

    > dmk ©   (31.05.16 17:01) [20]
    > >видя что ты его используешь, компилер тебя подстраховывает
    >
    > хмм ... у меня нет такого. Не сохранил, получи AV или висяк.
    >


    Да, еще раз перепроверил под ХЕ4

    // пролог
    005B1FD4: 55                                         PUSH EBP
    005B1FD5: 8B EC                                      MOV EBP, ESP
    005B1FD7: 53                                         PUSH EBX

    // тело функции
    005B1FD8: 52                                         PUSH EDX
    005B1FD9: 51                                         PUSH ECX
    005B1FDA: 53                                         PUSH EBX
    005B1FDB: 8B 4C 24 10                                MOV ECX, [ESP+0x10]
    005B1FDF: 8B 5C 24 14                                MOV EBX, [ESP+0x14]
    005B1FE3: 48                                         DEC EAX
    005B1FE4: 3B 5A 04                                   CMP EBX, [EDX+0x4]
    005B1FE7: 75 04                                      JNZ 0x5B1FED
    005B1FE9: 3B 0A                                      CMP ECX, [EDX]
    005B1FEB: 74 06                                      JZ 0x5B1FF3
    005B1FED: 83 C2 16                                   ADD EDX, 0x16
    005B1FF0: 48                                         DEC EAX
    005B1FF1: 79 F1                                      JNS 0x5B1FE4
    005B1FF3: 5B                                         POP EBX
    005B1FF4: 59                                         POP ECX
    005B1FF5: 5A                                         POP EDX

    // эпилог
    005B1FF6: 8B C3                                      MOV EAX, EBX << оть это весьма странно, видимо инициализация результата
    005B1FF8: 5B                                         POP EBX
    005B1FF9: 5D                                         POP EBP
    005B1FFA: C2 08 00                                   RET 0x8



    Здесь явно кривая кодогенерация
  • Rouse_ © (31.05.16 18:46) [29]
    Судя по тому что в ХЕ10 такого не наблюдается - значит был на CodeCentral багрепорт и это было исправлено.
  • Pavia © (31.05.16 23:20) [30]
    1) Читай справку Parameter passing.
    Первые 3 параметра передаются через EAX, EDX, и ECX,  остальные через стек.

    2)

    > push ebp;    // Зачем делать то, что я не просил?

    Это нужно, для отладчика. Принудительно отключается так
    procedure Foo(); registers;
    assemble
    asm
    end;
    Передача параметров через регистры и убираем begin.


    > mov eax,ebx; //Зачем оно мне result портит?

    Он вам портит не resul, а eax.
    result это локальная переменная в стеке.
    При разработке компилятора так проще сделать.


    > и еще вопрос: Вроде для таких вещей можно использовать команду
    > loop, если бы она работала с 32-разрядным регистром.
    >
    > Но вот по информации в инете не могу понять: какой все-таки
    > регистр использует команда loop :  CX или ECX?


    У инструкции, ака мнемоники 'loop' есть несколько кодов. По умолчанию в Delphi идет 32 битный или 64 битный в зависимости от настрое компилятора.
    В старом паскале был 16 битный.

    Для 16 - CX, для 32 , ECX

    Код можно принудительно переключить приставкой.
    Обычно db 66h, но для Loop db 67h

    > Что подразумевается под атрибутом размера адреса?

    Есть несколько приставок основных две приставка изменить_размер_данных и приставка изменить_размер_адреса
    db 66h и db 67h


    > Чем отличаются команды JE от JZ и JNE от JNZ?

    Ничем. Помните про луп, тут обратный случай мнемоники разные, а код один.


    > и еще: будет ли LEA edx,[edx+step] в данном случае быстрее?

    В данном одинаково.


    > ?
    > и как в будущем предугадать такие козни компилятора?

    Алгоритм генерации стековых рамок довольно сложный.

    Вот из RTL паскаля 7, что лежало на 13 дискете.


    *******************************************************
    ; *       *
    ; * MACROS      *
    ; *       *
    ; *******************************************************

    LOCALS @@

    ; Public variable definition macro

    VAR MACRO Symbol,SType,Count
     PUBLIC Symbol
     Symbol LABEL SType
     IF Count
       DB SType * Count DUP(?)
     ENDIF
    ENDM

    ; Parameter definition macro

    ARG MACRO Symbol,SType,Count
     LOCAL Offset
     @AP = @AP + SType * Count
     Offset = @AP
     Symbol EQU (SType PTR [BP+@AF-Offset])
    ENDM

    @AP = 0
    @AF = 0

    ; Local variables definition macro

    LOC MACRO Symbol,SType,Count
     LOCAL Offset
     @LP = @LP + SType * Count
     Offset = @LP
     Symbol EQU (SType PTR [BP+@LF-Offset])
    ENDM

    @LP = 0
    @LF = 0

    ; Stack frame modifiers

    sfFar  EQU 01H  ;FAR frame
    sfMarkBP EQU 02H  ;Make saved BP odd
    sfSaveDS EQU 04H  ;Save DS at [BP-2]
    sfInitDS EQU 08H  ;Init DS using SS

    ; Default stack frame type

    sfDefault = 0

    ; Stack frame types

       IF WindowsVersion
    WINFAR  EQU sfFar+sfMarkBP+sfSaveDS
       ELSE
    WINFAR  EQU sfFar
       ENDIF

    ; Entry code generation macro

    ENTRY MACRO FrameType
     IFB <FrameType>
       @SF = sfDefault
     ELSE
       IFIDNI <FrameType>,<NEAR>
         @SF = 0
       ELSE
         IFIDNI <FrameType>,<FAR>
           @SF = sfFar
         ELSE
           @SF = FrameType
         ENDIF
       ENDIF
     ENDIF
     IF @SF AND sfMarkBP
       INC BP
     ENDIF
     PUSH BP
     MOV BP,SP
     IF @SF AND sfFar
       @AF = @AP + 6
     ELSE
       @AF = @AP + 4
     ENDIF
     IF @SF AND sfSaveDS
       PUSH DS
       @LF = -2
     ELSE
       @LF = 0
     ENDIF
     IF @LP
       SUB SP,@LP
     ENDIF
     IF @SF AND sfInitDS
       PUSH DS
       PUSH SS
       POP DS
     ENDIF
    ENDM

    ; Exit code generation macro

    EXIT MACRO ArgSize
     IF @SF AND sfInitDS
       POP DS
     ENDIF
     IF @LF - @LP
       MOV SP,BP
     ENDIF
     POP BP
     IF @SF AND sfMarkBP
       DEC BP
     ENDIF
     IFNB <ArgSize>
       @AP = ArgSize
     ENDIF
     IF @SF AND sfFar
       RETF @AP
     ELSE
       RETN @AP
     ENDIF
     @AP = 0
     @LP = 0
    ENDM

  • Rouse_ © (31.05.16 23:46) [31]
    Эммм...
    Pavia, знаешь чем теория отличается от практики? :)

    Зы: а так в принципе... Ну ты старался :)
  • Pavia © (01.06.16 08:53) [32]

    > Pavia, знаешь чем теория отличается от практики? :)

    Знаю.

    Просто вчера не смог вспомнить где лежит нужная теория. Сегодня вспомнил.
    Полная теория лежит в бумажном руководстве пользователя, а не в хелпе.


    > Да, я его использовал, но я сам его сохранял при этом.  
    >
    > Ну даже пусть он таким образом меня подстраховывает,
    > но зачем в конце функции мне портить результат, который
    > был в eax, командой mov eax,ebx ?

    Да это косяк компилятора. Но программист виноват не меньше.

    Assembly procedures and functions


    > Unless a function returns a string, variant, or interface
    > reference, the compiler doesn't allocate a function result
    > variable; a reference to the @Result symbol is an error.
    >  For strings, variants, and interfaces, the caller always
    > allocates an @Result pointer.
    >


    Пока вы в тексте  не напишете @Result компилятор думает, что его нет.
    По идее он должен был выкинуть mov eax, ebx. Но не выкинул.

    Так что пишите         mov @Result,eax и будет всё тип топ.
  • Pavia © (01.06.16 09:03) [33]
    Сейчас свой кодогенератор делаю. Так вот теории нету! Не понятно как гарантировать его безошибочность.
    Пока что остановился на следующим пишу частные случае потом буду обобщать на общие. А после тестировать. Но как известно тестирование не гарантирует, что не будет ошибок. Особенно тут с неизвестной выходной функцией.
  • Rouse_ © (01.06.16 10:36) [34]
    Ну тогда у тебя не правильные выводы, практически все.
    Я не знаю где ты такую документацию читал


    > Первые 3 параметра передаются через EAX, EDX, и ECX,  остальные
    > через стек.

    не верно, если параметр не влазит в регистр он идет принудительно через стек


    > Это нужно, для отладчика.

    не верно. Отладчику на это сугубо фиолетово, да и программа как правильно работает без отладчика. Это нужно для локальных переменных и параметров.


    > Принудительно отключается так
    > procedure Foo(); registers;
    > assemble
    > asm
    > end;


    не верно.

    procedure Foo(); register; assembler;
    var
     Tst: DWORD;
    asm
     mov eax, 10
     mov Tst, eax
    end;


    выдаст вот такой код:

    004184AC: 55                                         PUSH EBP
    004184AD: 8B EC                                      MOV EBP, ESP
    004184AF: 51                                         PUSH ECX
    004184B0: B8 0A 00 00 00                             MOV EAX, 0xA
    004184B5: 89 45 FC                                   MOV [EBP-0x4], EAX
    004184B8: 59                                         POP ECX
    004184B9: 5D                                         POP EBP
    004184BA: C3                                         RET




    > Он вам портит не resul, а eax.
    > result это локальная переменная в стеке.

    не верно, в данном случае Result это именно EAX, а не какая-то переменная на стеке (при чем тут вообще стек)


    > Код можно принудительно переключить приставкой.
    > Обычно db 66h, но для Loop db 67h


    не верно, префикс 0х66 т.н. OperandSizeOverride используется для модификации инструкции (insw->insd/iret->iretd и т.п.) а для модификации регистров используется префикс AddressSizeOverride = $67

    вот полный список префиксов:

    // служебные префиксы
    // см. "2.2. SUMMARY OF INSTRUCTION PREFIXES"
    // =============================================================================
    type
     TPrefixData = record
       Value: Integer;
       Description: string;
     end;

    const
     // Lock and repeat prefixes:
     pfxLock = $F0;
     // The LOCK prefix can be prepended only to the following instructions and only to those forms
     // of the instructions where the destination operand is a memory operand: ADD, ADC, AND,
     // BTC, BTR, BTS, CMPXCHG, CMPXCH8B, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR,
     // XADD, and XCHG

     pfxRepn = $F2;
     pfxRep = $F3;

     // Segment override prefixes:
     pfxCSSegmentOverride = $2E;
     pfxSSSegmentOverride = $36;
     pfxDSSegmentOverride = $3E;
     pfxESSegmentOverride = $26;
     pfxFSSegmentOverride = $64;
     pfxGSSegmentOverride = $65;

     // Operand-size override prefix
     // (when used with the escape opcode 0FH, this
     // is treated as a mandatory prefix for some SIMD instructions)
     pfxOperandSizeOverride = $66;
     pfxEscapeOpcode = $0F;

     // Address-size override prefix
     pfxAddressSizeOverride = $67;

     pfxFPU_BFHTRange = $BF;
     pfxFPUOpcodeRange = [$D8..$DF];

     Prefixes: array [0..8] of TPrefixData = (
      (Value: pfxLock; Description: 'lock'),
      (Value: pfxRepn; Description: 'repn'),
      (Value: pfxRep; Description: 'rep'),
      (Value: pfxCSSegmentOverride; Description: 'cs:'),
      (Value: pfxSSSegmentOverride; Description: 'ss:'),
      (Value: pfxDSSegmentOverride; Description: 'ds:'),
      (Value: pfxESSegmentOverride; Description: 'es:'),
      (Value: pfxFSSegmentOverride; Description: 'fs:'),
      (Value: pfxGSSegmentOverride; Description: 'gs:')
     );




    > Алгоритм генерации стековых рамок довольно сложный.

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


    > Так что пишите         mov @Result,eax и будет всё тип топ.

    и получишь на выходе замечательную но бессмысленную инструкцию
    MOV EAX, EAX

    Вот как-то так вкратце.
  • Игорь Шевченко © (01.06.16 12:05) [35]
  • SergP © (01.06.16 15:01) [36]

    > У инструкции, ака мнемоники 'loop' есть несколько кодов.
    >  По умолчанию в Delphi идет 32 битный или 64 битный в зависимости
    > от настрое компилятора.
    > В старом паскале был 16 битный.
    >
    > Для 16 - CX, для 32 , ECX
    >
    > Код можно принудительно переключить приставкой.
    > Обычно db 66h, но для Loop db 67h


    Так это приставка действует только на следующую за ней команду loop или переключает глобально?
    И почему через db? Ей, что, забыли придумать отдельную мнемонику?
  • Rouse_ © (01.06.16 15:27) [37]

    >
    > Так это приставка действует только на следующую за ней команду
    > loop или переключает глобально?

    да, это префикс для идущей за ней инструкции, модифицирующий ее поведение по умолчанию


    > И почему через db? Ей, что, забыли придумать отдельную мнемонику?

    может и есть, но я не в курсе. (может какой нить "short loop" или еще как нибудь)
  • Pavia © (01.06.16 16:18) [38]

    > Так это приставка действует только на следующую за ней команду
    > loop или переключает глобально?
    > И почему через db? Ей, что, забыли придумать отдельную мнемонику?
    >

    Да, только на следующую. Для приставки отдельную мнемонику не стали делать. Обычно используется PTR или суффикс d, но вот loop это исключение. Интел толком не описывал. Сейчас вроде придерживатся борладского стиля. Но для каждого компилятора надо выяснять отдельно.


    > The loop instructions for the 80386 processor can either
    > use CX or ECX as the counting
    > register. The standard LOOP, LOOPE, LOOPZ, LOOPNE, and LOOPNZ
    > mnemonics
    > from Intel select the counting register based on whether
    > the current code segment is a 32-bit segment (when using
    > ECX) or a 16-bit segment (when using CX).
    > Turbo Assembler has special instructions that increase the
    > flexibility of the LOOP
    > feature. The LOOPW, LOOPWE, LOOPWZ, LOOPWNE, and LOOPWNZ
    > instructions
    > use CX as the counting register, regardless of the size
    > of the current segment. Similarly, the LOOPD, LOOPDE, LOOPDZ,
    >  LOOPDNE, and LOOPDNZ instructions use ECX as the counting
    > register
  • Pavia © (01.06.16 16:41) [39]

    > > Это нужно, для отладчика.
    >
    > не верно. Отладчику на это сугубо фиолетово, да и программа
    > как правильно работает без отладчика. Это нужно для локальных
    > переменных и параметров.

    Отладчику не всё равно! Смотри скриншот. XE5 Настройки стандартные.
    http://s33.postimg.org/p7tfcr7jj/image.png
 
Конференция "Прочее" » Передача параметров функции
Есть новые Нет новых   [134432   +19][b:0][p:0.006]