На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила ЧаВо (FAQ) разделов Паскаля
В этом разделе разрешено создавать только темы, в которых описано РЕШЕНИЕ какой-либо общей проблемы, или описание какого-либо аспекта языка Паскаль.
Обсуждение уже созданных тем разрешено, но только конструктивное, например указание на ошибку или уточнение имеющегося текста.

Также читать Требования к оформлению статей
Модераторы: volvo877, Romtek
  
> Внешние процедуры или функции , Что это такое, и как использовать
    Внешние процедуры (функции)



    С помощью внешних процедур (функций) можно осуществить вызов из программы процедур или функций, написанных на языке ассемблера. Ассемблер обеспечивает компиляцию программ, написанных на машинно-ориентированном языке программирования низкого уровня. В Турбо Паскале есть собственный встроенный ассемблер см. гл.12). В этом разделе речь идет о программах, написанных и откомпилированных с помощью внешнего ассемблера, такого как, например, ассемблер фирмы MicroSoft или Turbo Assembler фирмы Borland.

    Машинно-ориентированный язык ассемблера предоставляет квалифицированному программисту богатейшие возможности использования всех особенностей архитектуры ПК. Ассемблерные программы выполняются значительно быстрее и занимают меньший объем памяти, чем программы, написанные на Турбо Паскале, однако низкий уровень языка ассемблера существенно снижает производительность труда программиста и резко усложняет отладку программ. Как правило, на языке ассемблера пишутся сравнительно небольшие фрагменты программ, в которых используются недоступные из Турбо Паскаля особенности архитектуры ПК.

    Внешняя процедура (функция) в программе, написанной на Турбо Паскале, объявляется своим заголовком, за которым следует стандартная директива EXTERNAL, например:

    ExpandedWrap disabled
      Function LoCase (ch : char):char; external;


    ExpandedWrap disabled
      Procedure Swapping (var a,b; N:word); external;


    Как видно из этих примеров, тело внешней процедуры (функции) отсутствует - его заменяет директива EXTERNAL. Для подключения ассемблерной программы необходимо предварительно ее откомпилировать и получить объектный файл с расширением OBJ, содержащий перемещаемый код ассемблерной программы. Непосредственно перед описанием внешней процедуры (функции) в тело основной программы вставляется директива компилятора {$L<имя файла>}, где <имя файла> - имя OBJ-файла. Диск и каталог, в котором следует искать этот файл, если он не обнаружен в текущем каталоге, указываются опцией OPTIONS/DIRECTORIES/OBJECT DIRECTORIES (см. прил. 1).

    Перед передачей управления внешней процедуре (функции) программа помещает параметры обращения в программный стек в том порядке, как они перечислены в заголовке процедуры (функции). Ассемблерная процедура должна сохранить регистры ВР, SP, SS и DS центрального процессора в самом начале своей работы и восстановить содержимое этих регистров перед возвратом управления в программу. Остальные регистры можно не сохранять и соответственно не восстанавливать.

    Параметры могут передаваться по ссылке или по значению. Если параметр передается по ссылке, в стек помещается указатель, содержащий абсолютный адрес параметра, если по значению - в стек помещается сам параметр, точнее - его значение. Все параметры-переменные, т.е. параметры, объявленные в заголовке с предшествующим словом VAR, всегда передаются по ссылке. Параметры-значения могут передаваться по ссылке или по значению в зависимости от длины внутреннего представления соответствующего параметра. В общем случае используется следующее правило: если длина внутреннего представления параметра-значения составляет 1, 2 или 4 байта, он передается своим значением, т.е. его значение помещается в стек. Точно так же через стек передаются и все вещественные данные длиной в 4, 6, 8 и 10 байт (в версии 4.0 эти данные передаются через стек сопроцессора 8087/80287, начиная с версии 5.0 -через стек центрального процессора 8086/80486). Во всех остальных случаях, если длина внутреннего представления больше 4 байт, соответствующий параметр передается по ссылке.

    Ассемблерные функции в зависимости от длины внутреннего представления результата должны возвращать его через регистры центрального процессора или сопроцессора по следующим правилам:

    длиной в 1 байт - в регистре AL;
    длиной в 2 байта - в регистре АХ;
    длиной в 4 байта - в регистрах DX:AX (старшее слово в DX);
    тип REAL (6 байт) - в регистрах DX:BX:AX;
    типы SINGLE, DOUBLE, EXTENDED и СОМР - через стек сопроцессора 8087/80486;
    указатели - в регистрах DX:AX (сегмент в DX);
    строки возвращаются по ссылке: адрес начала строки помещается в DX:AX (сегмент в DX).

    Все ассемблерные процедуры должны размещаться в сегменте с именем CODE или CSEG, или с именем, оканчивающимся на _ТЕХТ; инициализированные локальные Переменные помещаются в сегмент с именем CONST или с именем, оканчивающимся на _DATA. Все другие локальные переменные необходимо размещать в сегменте с именем DATA или DSEG, или с именем, оканчивающимся на _BSS. Любые другие объявления сегментов игнорируются. Все имена, объявленные в интерфейсной части модулей программы, написанной на Турбо Паскале, становятся доступны ассемблерной процедуре (функции) после их объявления директивой EXTRN. Точно так же все имена ассемблерных процедур и функций, которые должны быть доступны программе на Турбо Паскале, следует объявлять директивой PUBLIC.

    Ozzя ©
      < Связь Паскаля с Ассемблером >

      Посвящается Тиkи.



      Связь Паскаля с Ассемблером реализуется как многомодульная программа, где главный модуль написан на Паскале, а вспомогательный на языке Ассемблера. Обычно, в Ассемблерном модуле реализуется часть алгоритма программы, которую выполнить в Паскале трудно или данная часть критична к времени выполнения.
      В Ассемблерном модуле пишутся процедуры которые будут соответствовать процедурам или функциям Паскаля. После чего Ассемблерные модули транслируются в .obj файлы и линкуются. Линкование выполняет сам компилятор Паскаля, следовательно, правило интерфейса определяется языком, на котором написан главный модуль (т.е. Паскалем).


      [ Адресация. ]

      В TP используются far и near процедуры

      Ближняя адресация используются для:
      • вызова процедур, расположенных в главном модуле.
      • вызова вложенных процедур
      • вызова процедур, описанных в implementation и не описанных в interface.


      Дальняя адресация используются для:
      • Процедур, описанных в секции Interface {$F+}
        (после этой директивы все процедуры будут оформлены как дальние до директивы {$F-})
      • или для процедур, описанных с использованием директивы FAR.
      для любых процедур с явно указанной директивой.
      В пределах одного сегмента на Ассемблере можно использовать как дальнюю так и ближнюю адресацию. При этом работа программы не изменится, однако в стеке адрес возврата будет состоять из двух слов: сегмент+смещение. Если используется несколько сегментов, то процедуры должны быть описаны как дальние.


      [ Передача параметров. ]

      Параметры погружаются в стек в порядке их описания (в отличие от Си).
      Выход из подпрограммы осуществляется командой ret data (т.е стек очищает
      Ассемблерный модуль, опять таки, в отличие от Си), где data - кол-во байт,
      переданных подпрограмме в качестве параметров.
      Ссылки (указатели) в Паскале всегда задаются 2 словами (сегмент:смещение).
      По ссылке передаются все параметры переменные; массивы и записи, если их размер
      превышает 4 байта. Множества и строки всегда передаются по ссылке.
      В стек погружаются:
      • Тип boolean как байт с фиксированным значением 0 или 1.
      • byte, char, shortint как байт.
      • Перечисляемый тип, как слово.
      • integer или word как 2 байта.
      • longint как 2 слова.
      • real как 3 слова.

      [ Возврат значений функциями. ]

      Ассемблерные функции должны следующим образом возвращать результат
      своей работы:
      * длиной 1 байт (Byte, Char и т.п.) - в регистре AL;
      * длиной 2 байта (Integer, Word) - в регистре АХ;
      * длиной 4 байта (Pointer, Longlnt) - в регистрах DX (старшее слово или
      сегм.) и в АХ (младшее слово или смещ.);
      * типа Real - в регистрах DX, BX, АХ (старшее слово - в DX, младшее в АХ);
      * вещественных типов Single, Double, Extended, Comp - в регистре ST(0) со-
      процессора;
      * строкового типа - во временной области памяти, на которую ссылает-
      ся один из параметров в стеке. Подробнее о строках мы поговорим в разделе
      "Обработка строк".
      Интересное замечание: в книге В.В. Фаронова "ТР 7.0" написано, что
      строки должны возвращаться по ссылке в DX:AX. Лично мне это не понятно:
      куда я должен записать строку чтобы возвратить на нее ссылку? Возможно, это
      опечатка... Я же работал со строками как ребята из Борланда, но об
      этом чуть позже ;)

      [ Подключение asm модуля. ]

      Подключение asm модуля имеет ряд особенностей:
      ASM модуль может содержать сегмент данных со стандартными именами :
      CODE (CSEG) - сегмент с п/п, DATA(DSEG) - лок. переменными, CONST -
      типизированные констранты. Сегмент кода объединяется с основным сегментом
      кода паскалевской программы, аналогично включается сегмент данных. Сегмент
      стека объявлять не требуется т.к. будет использоваться сегмент Паскаля.
      Переменные сегмента данных являются локальными для asm модуля, то
      есть паскалевской программе они не известны. Даже если определить их
      конкретные значения db, dw, то в результирующей программе они не опреде-
      лены.
      В паскалевской программе asm процедуры описываются директивой
      external. Транслируя asm модуль, результат - .obj - файл. В начале паска-
      левской программы записывается директива подключения .obj - модуля:

      ExpandedWrap disabled
        Program lab5;
        Uses crt;
        Type
            tmas = array[1..10] of integer;
        Var
            a,b: tmas;
            i,j: byte;
            na,n: word;
         
        {$L obrab.obj}
         
        {$F+} {все процедуры после этой директивы будут иметь дальнюю адресацию}
        Procedure M_ch_zr (Var x:tmas; n:word); external;
        Function max_cl (Var x:tmas; n:word):integer; external;
        {$F-}
         
        {Процедуры ввода-вывода массивов}
        Begin { main }
          in_massiv (a,na,'A')
          in_massiv (b,nb,'B');
          clrscr;
          out_massiv (a,na,'Исходный массив A');
          Writeln ('Максимальный элемент -',max_el(A,nA));
          M_ch_zr (A,na);
          Out_mas (A,na,'Преобразованный массив A')
        End. { /main }




      А вот и сам модуль:
      ExpandedWrap disabled
        CODE      SEGMENT
        PUBLIC    M_ch_Zr
        ASSUME    CS:CODE
        M_ch_zr   PROC      FAR            ;замена <0 на 0
                  PUSH      CX DI BP ES
                  mov       BP,SP
        ;    В данный момент в стек выглядит следующим образом:
        ;           │        │
        ;           ├────────┤
        ;           │ ES     │bp+0
        ;           ├────────┤
        ;           │ BP     │ +2
        ;           ├────────┤
        ;           │ DI     │ +4
        ;           ├────────┤
        ;           │ CX     │ +6
        ;           ├────────┤
        ;  адрес   ┌┤смещение│ +8
        ;  возврата│├────────┤
        ;          └┤сегмент │ +10
        ;           ├────────┤
        ;           │  n     │ +12
        ;           ├────────┤
        ;  адрес   ┌┤смещение│ +14
        ;  x       │├────────┤
        ;          └┤сегмент │ +16
        ;           └────────┘
        ;
                  mov       CX,[BP+12]     ; n
                  les       DI,[BP+14]     ; адрес X
        c:        mov       AX,es:[DI]
        ; при трансляции перед этой командой ставится префикс изменения сег-
        ; мента адресации. При отсутствии ES:  адресация  автоматически  де-
        ; лается DS:[DI]. Префикс действует только на одну команду.
                  cmp       AX,0
                  jge       M
                  mov       word PTR ES:[DI],0
        M:        add       DI,2
                  loop      C
                  pop       ES BP DI CX
                  ret       6
        M_ch_zr   ENDP
        CODE      ENDS
                  END
        ; Как видите, этот модуль практически ничем не отличается от обычной программы
        ; на Ассемблере, но отличия все же есть: это, во-первых, строго заданные имена
        ; сегментов и, во-вторых, вконце подуля стоит END без параметров. Это нужно
        ; помнить!

      Ранее отмечено, что процедуры asm модуля попадают в сегмент кода
      паскалевской программы, поэтому использование команды les является избы-
      точным, т.к. в этом случае ES=DS.

      ExpandedWrap disabled
        Max_el    PROC      FAR
                  push      CX DI BP ES
                  mov       BP,SP
                  mov       CX,[BP+12]
                  mov       DI,[BP+14]
                  mov       AX,DI
        C1:       cmp       AX,[DI]
                  jge       M1
                  mov       AX,[DI]
        M1:       add       [DI],2
                  loop      C1
                  pop       ES BP DI CX
                  ret       6
        Max_el    ENDP




      По завершению процедуры в AX - max_el, который и возвращается в ка-
      честве результата функции в паскалевскую программу.

      ExpandedWrap disabled
             start     stop      length    name      class
             00000h    00343h    00344h    lab5      code
               от        до      всего
             00350h    00962h    00613h    crt       code
             --------------------------    system    code
             01390h    ----------------    data      data
                 глобальный сегмент данных pas программы
             01660h    0505fh    04000h    stack     stack
                                  16 Kb
             05660h    05660h    00000h    heap      heap

      Динамические переменные, т.к. куча не используется.


      [ Особенность обработки матриц. ]

      Обработка матриц в asm модуле имеет свои "тонкости". Рассмотрим это
      подробнее:
      Пусть, в Паскалевской программе матрица объявлена след. образом:

      ExpandedWrap disabled
        Program Matrix_asm;
        Uses CRT;
        Const
          max_n = 10;
          max_m = 5;
        Type
          TMatrix = array[1..max_n, 1..max_m]  of integer;
        Var
          Matrix : TMatrix;
          m,n    : word;
        Begin
          n:=0;
          m:=0;
          repeat
            ClrScr;
            write('Введите размеры матрицы n, m');
            readln(n,m);
          until (n>0 and n <= max_n) and (m>0 and m <= max_m);
          { Здесь осуществляется ввод матрицы }
                . . .
          { Здесь осуществляется обработка матрицы }
                    . . .
          { Здесь осуществляется вывод матрицы }
        End.




      При таком объявлении матрицы наша переменная (Matrix) в памяти
      будет занимать такой участок:

      	    1        n     max_n
      	     ╔══╤═ . ═╗ . ─┬──┐
      	     ╟──┼─ . ─╢ . ─┼──┤
      	       . . . . . . . .
      	   m ╚══╧═ . ═╝ . ─┼──┤
      	       . . . . . . . .
      	     ├──┼─ . ─┼ . ─┼──┤
      	max_m└──┴─ . ─┴ . ─┴──┘


      На данном рисунке двойной линией обозначено реально используемое
      пространство матрицы, а одинарной - полный объем памяти, занимаемый
      матрицей.
      Таким образом, чтобы обратиться ко второму эл-ту третей строки
      нужно использовать смещение: "((3-1) * max_n + (2-1)) * 2", а не
      "3 * n + 2", как может показаться на первый взгляд. В конце нужно
      умножить на размер элемента в байтах (в нашем случае это 2 байта - раз-
      мер типа integer). Это следует учитывать при обработ ке матриц. Для того
      чтобы корректно обработать матрицу в процедуру обработки нужно передавать
      не только адрес матрицы, n и m но и длину строки (в нашем случае это max_n).

      { Т.е. процедура должна быть описана так: }
      Procedure Do_Something(Var Matr: TMatrix; n,m,max_n: word); external;

      { А вызов, соответственно, делать так: }
      Do_Something(Matrix, n,m,max_n);



      [ Обработка строк. ]

      Обычно функция возвращает одно скалярное значение. Но в TP есть исключе-
      ние. Это тип string, который является структурированным (массивом символов),
      однако с другой стороны имеются средства обработки как скаляра (операция
      присвоения, операции сравнения и др.).

      Рассмотрим следующий пример из состава TASM:

      ExpandedWrap disabled
        ; Turbo Assembler example. Copyright (c) 1993 By Borland International, Inc.
        ;
        ; HEX.ASM
        ;
        ; Usage: Run tasm on this file and link with hex.pas
         
        CODE      SEGMENT
                  ASSUME cs:CODE,ds:NOTHING
         
        ; Parameters (+2 because of push bp)
         
        byteCount EQU BYTE PTR  ss:[bp+6]   ; а это параметры
        num       EQU DWORD PTR ss:[bp+8]       ; функции HexStr.
         
        ; Function result address (+2 because of push bp)
         
        resultPtr EQU DWORD PTR ss:[bp+12]      ; это результат функции
         
        HexStr    PROC FAR
                  PUBLIC HexStr
         
                  push bp
                  mov bp,sp          ;get pointer into stack
                  les di,resultPtr   ;get address of function result
                  mov dx,ds          ;save Turbo's DS in DX
                  lds si,num         ;get number address
                  mov al,byteCount   ;how many bytes?
                  xor ah,ah          ;make a word
                  mov cx,ax          ;keep track of bytes in CX
                  add si,ax          ;start from MS byte of number
                  dec si
                  shl ax,1           ;how many digits? (2/byte)
                  cld                ;store # digits (going forward)
         
        ; Важно!!! Не забывайте записывать длину строки в нулевой байт!
                  stosb              ;in destination string's length byte
         
        HexLoop:
                  std                ;scan number from MSB to LSB
                  lodsb              ;get next byte
                  mov ah,al          ;save it
                  shr al,1           ;extract high nibble
                  shr al,1
                  shr al,1
                  shr al,1
                  add al,90h         ;special hex conversion sequence
                  daa                ;using ADDs and DAA's
                  adc al,40h
                  daa                ;nibble now converted to ASCII
                  cld                ;store ASCII going up
                  stosb
                  mov al,ah          ;repeat conversion for low nibble
                  and al,0Fh
                  add al,90h
                  daa
                  adc al,40h
                  daa
                  stosb
                  loop HexLoop       ;keep going until done
                  mov ds,dx          ;restore Turbo's DS
                  pop bp
                  ret 6              ;parameters take 6 bytes
        HexStr    ENDP
        CODE      ENDS
                  END





      ExpandedWrap disabled
        { Turbo Assembler example. Copyright (c) 1993 By Borland International, Inc. }
         
        { Use with hex.asm }
         
        Program HexTest;
        Var
          num : Word;
         
        Function HexStr (Var num; byteCount : Byte) : string; far; external;
         
        {$L HEX.OBJ}
         
        Begin
          num := $face;
          Writeln('The Converted Hex String is"',HexStr(num,sizeof(num)),'"');
        End.



      Не вникаясь в алгоритм функции рассмотрим как выглядит стек при
      вызове HexStr(num,sizeof(num)).

                    ВР+0              │ старый ВР    │
                                      ├──────────────┤
                    ВР+2,4            ├ адр. возвр.  ┤  (4 байта)
                                      ├──────────────┤
                    ВР+6              │ sizeof(num)  │ ─┐
                                      ├──────────────┤  │  Все параметры
                    ВР+8        адрес┌┤  смещение    │  ├  данной функции
                                 num │├──────────────┤  │  занимают 6 байт
                    ВР+10            └┤  сегмент     │ ─┘
                                      ├──────────────┤
                    ВР+12       адрес┌┤  смещение    │  Это поле в стек
                                врем.│├──────────────┤  поместил сам компилятор.
                    ВР+14       поля └┤  сегмент     │  При расчете параметра ret
                                      └──────────────┘  это поле не учитывать!
                                                        Именно поэтому в примере
                                                        стоит ret 6, а не ret 8.


      Итак, что же это все значит? Ну, по порядку:
      В стеке лежит адрес возврата и адрес обрабатываемого поля num. Тут
      все понятно... Но вот что за адрес временного поля? На самом деле, компиля-
      тор помещает в стек адрес поля памяти, куда следует записать результат.
      После работы подпрограммы компилятор сам позаботится о копировании
      этого поля в результирующую строку.

      [ Макростредства в помощь ]

      Ведь не зря ребята в Борланде парились создавая макросредства в
      своем ассемблере ;-). Скажем им спасибо и посмотрим как же их можно использо-
      вать в нашем случае. Для примера опять возьмем исходник от Борланда:

      ExpandedWrap disabled
        ; Turbo Assembler example. Copyright (c) 1993 By Borland International, Inc.
        ;
        ; ENVSTR.ASM - Example program to scan the DOS environment
        ;
        ; Usage: Run tasm on this file and link with envstr.pas
         
                  .MODEL  large,PASCAL
        ; Эта директива создает сегменты по умолчанию и соответствующие им
        ; выражения ASSUME и GROUP. В кач-ве параметра указана модель памяти,
        ; полностью соответствующая Паскалю.
                  .DATA
        ; А тут мы начали сегмент данных.
                  EXTRN prefixSeg : WORD  ;gives location of PSP
        ; А тут одной строкой объявили адрес PSP.
                  .CODE
        ; А вот и сегмент кода.
        EnvString PROC FAR  EnvVar:DWORD  RETURNS EnvVal:DWORD
        ; Тут мы объявили дальнюю функцию с параметром EnvVar размером в 4 байта
        ; (сегм. + смещ.) и указали что EnvVal тогоже размера будет
        ; нашой переменной, куда мы запишем результат ;)
                  PUBLIC  EnvString
        ; А это для того чтобы Паскаль смог "увидеть" нашу функцию.
        ; Это что-то типа объявления функций в интерфейсе модуля ;)
        ; И еще, как видите, в начале подпрограммы команды "push bp" и
        ; "mov bp,sp" писать не нужно. За вас это сделает компилятор ;)
                  cld                     ;work upward
                  mov     es,[prefixSeg]  ;look at PSP
                  mov     es,es:[2Ch]     ;ES:DI points at environment
                  xor     di,di           ;which is paragraph-aligned
                  mov     bp,sp           ;find the parameter address
                  lds     si,EnvVar       ;which is right above the return address
                  ASSUME  ds:NOTHING
                  lodsb              ;look at length
                  or      al,al      ;is it zero?
                  jz      RetNul     ;if so, return
                  mov     ah,al      ;otherwise, save in AH
                  mov     dx,si      ;DS:DX contains pointer to first parm character
                  xor     al,al      ;make a zero
        Compare:
                  mov     ch,al      ;we want ch=0 for next count, if any
                  mov     si,dx      ;get back pointer to string sought
                  mov     cl,ah      ;get length
                  mov     si,dx      ;get pointer to string sought
                  repe    cmpsb      ;compare bytes
                  jne     Skip       ;if compare fails, try next string
                  cmp     byte PTR es:[di],'='
                                     ;compare succeeded; is next char '='
                  jne     NoEqual    ;if not, still no match
        Found:
                  mov     ax,es      ;make DS:SI point to string we found
                  mov     ds,ax
                  mov     si,di
                  inc     si         ;get past the equal (=) sign
                  les     bx,EnvVal  ;get address of function result
                  mov     di,bx      ;put it in ES:DI
                  inc     di         ;get past the length byte
                  mov     cl,255     ;set up a maximum length
        CopyLoop:
                  lodsb              ;get a byte
                  or      al,al      ;zero test
                  jz      Done       ;if zero, we're done
                  stosb              ;put it in the result
                  loop    CopyLoop   ;move up to 255 bytes
        Done:     not     cl         ;we've been decrementing CL from
                                     ; 255 during save
                  mov     es:[bx],cl           ;save the length
                  mov     ax,@DATA
        ; А тут мы а АХ поместили сегмента данных ;)
                  mov     ds,ax                ;restore DS
                  ASSUME  ds:@DATA
                  ret
        ; Как видите, тут ret без параметров ;)
        ; А знаете почему? А потому что компилятор сам сюда напишет все что
        ; нужно: восстановит ВР и напишет retf с нужным параметром в зависимости
        ; от входных параметров. В нашем случае выход будет таким:
        ; "pop bp" + "retf 4".
                  ASSUME  ds:NOTHING
        Skip:
                  dec     di         ;check for null from this char on
        NoEqual:
                  mov     cx,7FFFh   ;search a long way if necessary
                  sub     cx,di      ;environment never >32K
                  jbe     RetNul     ;if we're past end, leave
                  repne   scasb      ;look for the next null
                  jcxz    RetNul               ;exit if not found
                  cmp     byte PTR es:[di],al  ;second null in a row?
                  jne     Compare              ;if not, try again
        RetNul:
                  les     di,EnvVal            ;get address of result
                  stosb                        ;store a zero there
                  mov     ax,@DATA
                  mov     ds,ax                ;restore DS
                  ASSUME  ds:@DATA
                  ret
        ; Как видите, тут тоже ret без параметров(см. выше) ;)
        EnvString ENDP
                  END
        ; И еще, как вы заметили, ребята из Борланда не сильно беспокоятся о регистрах.
        ; Точнее беспокоятся только о DS, SS и SP, а о ВР заботится сам компилятор при
        ; входе и выходе из п/п(только при использовании ключевого слова PASCAL в объяв-
        ; лении п/п или при указании модели памяти!). Т.е. Все остальные регистры к мо-
        ; менту выхода из п/п могут иметь произвольные значения ;). Исключение состав-
        ; ляют только функции, которые возвращают результат в некоторых регистрах.




      ExpandedWrap disabled
        { Turbo Assembler example. Copyright (c) 1993 By Borland International, Inc. }
         
        { Use with envstr.asm }
         
        Program EnvTest;
        { program looks for environment strings }
         
        Var
          EnvVariable : string;
          EnvValue : string;
         
        Function EnvString(s:string) : string; far; external;
        {$L ENVSTR.OBJ}
        Begin
          EnvVariable := 'PROMPT';
          EnvValue := EnvString(EnvVariable);
          if EnvValue='' then EnvValue := '*** not found ***';
          Writeln('Environment Variable:',EnvVariable,'Value:',EnvValue);
        End.



      [ Резюме ]

      Ну что ж, вспомним все, о чем говорилось в этой статье...
      Asm модули, в основном, используются для увеличения производительности
      программы.
      В одном модуле можно использовать как ближнюю, так и дальнюю адресацию,
      но если в модуле используются межсегментные вызовы, то такие п/п должны иметь
      дальнюю адресацию. Кстати, при дальней адресации значение CS при выходе из п/п
      может быть произвольным. Э то связано с тем, что при такой адресации адрес
      возврата состоит из 4 байт (сегм.+смещ.).
      Параметры передаются в стек в порядке их описания. Адреса всегда переда-
      ются дальними. По ссылке передаются все параметры переменные; массивы и записи,
      если их размер превышает 4 байта. Множества и строки всегда передаются по ссылке.
      Стек чистит сама п/п использую ret data.
      Функции возвращают свои значения в регистрах (см. "Возврат значений функ-
      циями"). Исключение чение составляют строки - их сохраняют во времееной облас-
      ти памяти.
      Asm модули практически не отличаются от обычных программ на ЯА. Модули
      должны иметь строго определенные имена сегментов (CODE, DATA, STACK). Эти
      сегменты будут объеденены с Паскалевскими. В конце модуля стоит END без пара-
      метров. Возможны следующие варианты оформления:

      Пусть дана такая процедура:

      procedure proc_name(Var a: byte; b: integer; c: longint);


      Тогода:

      ExpandedWrap disabled
        CODE      SEGMENT
        PUBLIC    proc_name
        ASSUME    CS:CODE
        proc_name PROC      FAR
                  PUBLIC  proc_name
                  push bp ds ss sp
                  mov  bp,sp
        a         equ  [bp+12]    ;   Это
        b         equ  [bp+10]    ;     параметры
        c         equ  [bp+6]     ;       процедуры
         . . .
                  pop  sp ss ds bp
                  ret  10     ; 10 - кол-во переданных байт
        proc_name ENDP
        CODE      ENDS
                  END
         
        ;     Процедуру можно было объявить и так:
         
        proc_name PROC FAR PASCAL, a:dword,b:word,c:dword ; Это параметры
                  PUBLIC  proc_name
                  push ds ss sp
         . . .
                  pop  sp ss ds
                  ret ; без параметров
        proc_name endp
         
        ;          или
         
                  .MODEL  large,PASCAL
                  .DATA
         . . .          
                  .CODE
        proc_name PROC FAR  a:dword,b:word,c:dword ; Это параметры процедуры
                  PUBLIC  proc_name
                  push ds ss sp
         . . .
                  pop  sp ss ds
                  ret ; без параметров
        proc_name ENDP
                  END



      [ Приложение А. Работа с множествами. ]

      Многих давно итересует как же на самом деле работают множества и какова
      их внутренняя структура...
      Итак, максимальный кол-во эл-тов в мн-ве 256 (т.е от 0 до 255) хотя,
      мн-во может быть пустым ([]). Каждому элементу мн-ва соответствует 1 бит
      (1 - элемент присутствует в мн-ве, 0 - отсутствует). Таким образом мак-
      симальный объем памяти, занимаемый множеством не может превышать 32 байта
      (256 бит, по биту на элемент). Однако, как вы знаете, минимальный объем
      памяти равен 1 байту т.е. даже если вы обявите мн-во как "X: set of 1..1" то
      его размер все равно будет равен 1 байту и более того, вы даже можете
      сделать такое:

      b:=5;
      Include(X,n);



      и это будет работать... ;)
      Из всего вышеперечисленного следует что вся работа над множествами
      состоит из логических операций над памятью. Пример:

      Пусть заданы мн-ва "X,Y: set of 0..7;"
      Тогда операция "X:=[];" на самом деле обнуляет участок памяти с мно-
      жеством т.е. это будет "0000 0000".
      Операция "Include(X,3);" будет выглядеть так:

      old X 0000 0000
      or
      [3] 0001 0000
      =
      new X 0001 0000

      Y:=X+[2..4,7]



      X 0001 0000
      or
      [2..4,7] 0011 1001
      =
      Y 0011 1001

      X:=X*Y



      old X 0001 0000
      and
      Y 0011 1001
      =
      new X 0001 0000

      X:=X-[2..3];



      А тут немного сложнее: для начала инвертируем [2..3], а потом
      умножаем:

      [2..3] 0011 0000
      not [2..3] 1100 1111

      old X 0001 0000
      *
      not [2..3] 1100 1111
      =
      new X 0000 0000

      И в заключение приведу пример работы с множествами в asm модуле:

      ExpandedWrap disabled
        ; File name: "Prime_m.asm"
        ; Работа с множествами (решето Эратосфена).
        ; Данный файл является asm модулем к программе "Prime_a.pas"
                .MODEL  large,PASCAL
                .DATA
        n1  dw  ?
        next    dw  2
        BytesInSet dw   ?
                .CODE
        PUBLIC CalcPrime
        CalcPrime proc far
            push    bp
            mov bp,sp
        Set_Adr equ [bp+8]
        N   equ [bp+6]
            mov si,Set_Adr   ; si - адрес исходного мн-ва
         
            mov ax,N
            mov dl,8
            div dl
            sub dl,ah
            add N,dl
            dec byte ptr N
            mov dx,N         ; dx=N
         
            mov cx,dx
            shr cx,3
            inc cx
            mov BytesInSet,cx   ; BytesInSet=SizeOf(Set);
         
            mov di,si
        c:  mov byte ptr [di],0 ; Set:=[];
            inc di
            loop    c
            mov byte ptr [si],2 ; Set:=[1]
         
            sub sp,BytesInSet   ; Создадим вспомогательное мн-во в стеке
            mov di,sp       ; di - адрес нового мн-ва
            push    di
            mov cx,BytesInSet
        c1: mov byte ptr [di],0FFh ; Set:=[0..N];
            inc di
            loop    c1
            pop di
            mov byte ptr [di],11111100b    ; Set:=[2..N];
        ccc:                      ; While Set <> []
            mov al,0
            cld
            mov cx,BytesInSet
            push    di si
            repe    scasb
            pop si di
            je  m_end
            push    next
            pop n1
        m_wh:                     ; while n1 do begin<=N
            cmp n1,dx
            ja  m_endd
            mov ax,n1
            call    Excl
            mov ax,next
            add n1,ax
            jmp m_wh
        m_endd:                   ; end { while n1<=N }
            mov ax,next
            call    Incl
         
        m_rep:                    ; repeat
            inc next
            mov ax,next
            Call    In_s
            jnz m_end_rep
            cmp next,dx
            ja  m_end_rep
            jmp m_rep
         
        m_end_rep:                ; until (next in Set) or (next > N)
         
            jmp ccc
         
        m_end:
            add sp,BytesInSet   ; Удалим вспомогательное мн-во в стеке
            pop bp
            ret 6
        CalcPrime endp
        ; - - - - - - - - - -
        Incl    proc    near
        ; Set:=set+[al]
            push    si bx cx
            xor ah,ah
            mov bx,ax
            shr bx,3
            add si,bx
            shl bx,3
            sub ax,bx
            mov bx,1
            mov cl,al
            shl bx,cl
            or  [si],bx
            pop cx bx si
            ret
        Incl    endp
        ; - - - - - - - - - -
        Excl    proc    near
        ; Set:=set-[al]
            push    di bx cx
            xor ah,ah
            mov bx,ax
            shr bx,3
            add di,bx
            shl bx,3
            sub ax,bx
            mov bx,1
            mov cl,al
            shl bx,cl
            not bx
            and [di],bx
            pop cx bx di
            ret
        Excl    endp
        ; - - - - - - - - - - - - -
        In_s    proc    near
        ; [al] in Set ?
            push    di bx cx
            xor ah,ah
            mov bx,ax
            shr bx,3
            add di,bx
            shl bx,3
            sub ax,bx
            mov bx,1
            mov cl,al
            shl bx,cl
            and bx,[di]
            pop cx bx di
            ret
        In_s    endp
                end




      ExpandedWrap disabled
        {
         File name: "Prime_a.pas"
         Работа с множествами (решето Эратосфена).
         Данный файл компилируется вместе с "Prime_m.obj"
        }
        Program Prime_a;
        Const
          N=255;
        Type
          SetOfNumb = set of 1..N;
        Var
          i : word;
          PrimeSet   : SetOfNumb;
        {$L prime_m.obj}
        Procedure CalcPrime(Var SetOfNumb; n:word); far; external;
        Begin
          CalcPrime(PrimeSet,N);
          for i:=1 to N do
            if i in PrimeSet then
              Write(i:8);
          WriteLn;
        End.




      [ THE END ]

      Ну что, вроде все что я хотел сказать... Всем удачи ;) До встречи.


      (С) Лабинский Николай aka e-moe. ДонНТУ 2004-2005.
      I love Borland.

      <
      Написано по мотивам конспекта лекций по "СП" Теплинского С.В, а также,
      книги Фаронова В.В. "Turbo Pascal 7.0" и, естественно, книги П.Абеля "ЯА"
      >

      При написании этой стати использовались:
      * Пиратские копии bp.exe, tasm.exe и td.exe. ;)
      * И зарегистрированный FAR. %)

      Last one modified at: GMT+2 23:31:50 9.01.2005

      Это сообщение было перенесено сюда или объединено из темы "Linking Asm to Pascal"
      Сообщение отредактировано: Romtek -
      0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
      0 пользователей:


      Рейтинг@Mail.ru
      [ Script execution time: 0,0448 ]   [ 17 queries used ]   [ Generated: 23.04.24, 15:41 GMT ]