Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

лабораторные на Pasca (Кудрявцев)l

.pdf
Скачиваний:
37
Добавлен:
18.03.2015
Размер:
1.63 Mб
Скачать

Встроенный ассемблер

353

 

 

dw X

{Размещает смещение переменной X}

dd Proc

{Размещает FAR-адрес процедуры Proc}

Данные, определяемые директивами Dx, всегда размещаются в те- кущем кодовом сегменте. Разместить таким образом данные в сег- менте данных (т.е. определить константу или типизированную кон- станту) невозможно для этого используются стандартные средст- ва Турбо Паскаля. Более того, директивы не могут снабжаться име- нами, а поэтому использовать размещаемые с их помощью данные не так то просто. В следующем примере на экран выводится тек- стовое сообщение. Для этого используется функция 9 вызова DOS, в соответствии с которой в регистрах DS:DX должен содержаться адрес текстовой строки, а сама строка должна заканчиваться сим- волом «$»:

asm

jmp @NextCode

{Обходим фрагмент данных}

@:

 

db 'Текстовая строка,13,10,'$'

 

@NextCode:

{Сохраняем DS}

push ds

push cs

{DS = CS}

pop ds

mov dx, OFFSET @

{DS:DX - адрес строки}

mov ah, 9

{АН - код функции вывода}

int 21h

{Выводим строку}

pop ds

{Восстанавливаем DS}

end;

 

Обратите внимание на использование регистра DS. В соответ- ствии с требованиями функции 9, он должен содержать сегмент вы- водимой строки. В нашем случае строка располагается в кодовом сегменте, поэтому мы вынуждены сначала сохранить значение DS в стеке, а затем восстановить его. Если бы мы этого не сделали, по завершении ассемблерного оператора регистр DS указывал бы на сегмент кода и была бы потеряна связь программы Турбо Паскаля с глобальными переменными и константами.

Ассемблерные подпрограммы

Ассемблерные подпрограммы это процедуры и функции, объявленные с директивой Assembler. В таких подпрограммах ис-

354

Приложение

 

 

полняемая часть не содержит begin... end и состоит из единствен- ного ассемблерного оператора asm... end. Например:

Function LongMul(X,Y:Integer):LongInt; Assembler; asm

mov ax, X

imul Y {DX/AX содержат "длинный" результат} end;

При компиляции ассемблерных подпрограмм выполняется ряд оп- тимизаций кода, в том числе:

параметры-значения строкового типа, а также длиной в 1, 2 и 4 байта не копируются во временную память, т.е. внутри подпро- граммы они считаются параметрами-переменными;

компилятор не создает переменную @Result для результата функции, и ссылка на эту переменную в ассемблерной функции недопустима;

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

кового типа, для них разрешается использовать ссылку на

@Result;

генерируются следующие команды на входе в подпрограмму и на ее выходе:

push bp

{Сохраняется ВР}

mov bp, sp

{ВР содержит текущую границу стека}

sub sp, Locals

{Резервируется часть стека для размещения локальных переменных}

……

{Восстанавливается граница стека}

mov sp, bp

pop bp

{Восстанавливается ВР}

ret Params

{Из стека удаляются параметры подпрограммы и осуществляется

 

выход из нее}

Здесь Locals общая длина в байтах всех объявленных в подпро- грамме локальных переменных, а Params длина (в байтах) всех формальных параметров. Если Locals и Params равны нулю, вход- ной код не создается, а выходной содержит единственную инструк-

цию RET.

Все локальные переменные Турбо Паскаль размещает в стеке. Это относится как к обычным, так и к ассемблерным подпрограммам.

Для ссылки на локальные переменные используется адресация по базе, задаваемой парой DS:ВР, поэтому при входе в процедуру все- гда создается так называемый локальный стек: в регистр ВР поме-

Встроенный ассемблер

355

 

 

щается текущая граница стека, а сама эта граница смещается вверх на суммарную длину всех локальных переменных, чтобы работа со стеком внутри подпрограммы не разрушила локальные перемен- ные. Например:

Procedure ....;

Assembler;

var

 

X: Word;

 

Y: Byte;

 

asm

 

mov X, ax

{Компилируется в mov [BP-2], ax}

mov ah,Y

{Компилируется в mov ah,[BP-3]}

end;

 

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

длиной 1 байт (Byte, Char и т.п.) – в регистре AL;

длиной 2 байта (Integer, Word) – в регистре АХ;

длиной 4 байта (Pointer, Longint) в регистрах DX (старшее сло- во) и АХ (младшее слово);

типа Real в регистрах DX, BX, АХ (старшее слово в DX, млад- шее в АХ);

вещественных типов Single, Double, Extended, Comp в регистре

ST(0) сопроцессора;

строкового типа во временной области памяти, на которую ссылается @Result.

Использование языка ассемблера в программах на Turbo Pascal 7.0

Turbo Pascal позволяет писать отдельные части программы (под- программы или части подпрограмм) на языке ассемблера. Здесь возможны четыре варианта.

Во-первых, можно написать подпрограмму на языке ассемблера, скомпилировать ее отдельно компилятором TASM (Turbo Assembler) с получением объектного файла, а затем скомпоновать его с основ- ной программой, написанной на Turbo Pascal, используя при этом директиву компилятора {$L <имя файла>}, где <имя файла> – имя файла с подпрограммой на ассемблере, и директиву external.

356

Приложение

 

 

Во-вторых, используя встроенный ассемблер пакета Turbo Pascal, отдельные части текста программы можно написать непосредст- венно на языке ассемблера, заключив их в операторные скобки

asm...end.

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

В-четвертых, небольшую подпрограмму можно написать непо- средственно в кодах процессора, используя оператор или директиву

inline.

При написании отдельных частей программы на языке ассемблера следует иметь в виду, что необходимосохранить содержимое реги- стров ВР, SP, SS и DS. Если их необходимоизменить, то исходные значения следует запомнить, а затем восстановить. Остальные ре- гистры можно безболезненно изменять.

Основным вопросом стыковки программы с подпрограммой, напи- санной на ассемблере, является передача параметров в подпро- грамму и обратно. Именно этому вопросу и будет здесь уделено основное внимание.

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

Использование компилятора TASM

Как правило, этот вариант применяется, когда та или иная про- грамма имеет большой размер и ее целесообразно и написать, и скомпилировать отдельно, используя компилятор TASM. В этом

случае можно использовать все возможности языка и компилятора

TASM.

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

Основная программа, использующая подпрограмму, написанную на языке ассемблера, содержит инициализированный массив, в кото-

Встроенный ассемблер

357

 

 

ром будет определяться максимальное число, а сама программа вы- водит на экран значение максимального числа из этого массива:

Program EXAMPLE1;

Const N=7;

 

{Размер массива}

Massiv: Array [1… n] of Integer = (1, 2, 3, 2, 17, 7, 2);

{Исходный массив}

{$L SUBR}

{Подключение файла SUBR.OBJ}

Function Max (var Mas; N: Integer): Integer; External; Begin

WriteLn('Максимальное число массива равно: ', Max(Massiv, N)); ReadLn

End.

Используя стандартную модель памяти, подпрограмму, опреде- ляющую максимальное число из массива, можно написать следую- щим образом:

CODE

SEGMENT

BYTE

PUBLIC

;внешний идентификатор

 

ASSUME

CS:CODE

 

 

PUBLIC

Max

 

;адрес первого параметра

AdrMas

EQU

DWORD

PTR[BP+6]

N

EQU

WORD

PTR[BP+4]

;второй параметр

Max

 

PROC

NEAR

;сохранение регистра ВР

 

PUSH

ВР

 

 

MOV

BP, SP

 

;указатель стека

 

LDS

SI, AdrMas

 

;адрес массива

 

XOR

AX, AX

 

;0 - в регистр АХ

 

MOV

BX, 8001h

 

;минимальное целое число

 

MOV

CX, N

 

;число элементов массива

 

CMP

CX, AX

 

;сравнение с 0

 

JLE

M3

 

;0 или отрицательное число

M1:

LODSW

 

 

;загрузка элемента массива

 

CMP

AX, BX

;сравнение с текущим максимумом

 

JLE

M2

 

;не больше

 

MOV

BX, AX

 

;новое максимальное число

M2:

LOOP

M1

 

;цикл

M3:

MOV

AX, BX

 

;результат функции

 

POP

ВР

 

;восстановление регистра

ВР

RET

6

 

;возврат из подпрограммы

Max

 

ENDP

 

 

CODE

ENDS

 

 

 

 

END

 

 

 

358

Приложение

 

 

По приведенной подпрограмме следует сделать следующие замеча- ния.

Первые две команды сохранение регистра ВР и загрузка в него указателя стека являются типичными командами, с помощью ко-

торых можно установить доступ к передаваемым параметрам через регистр ВР.

Параметры передаются в подпрограмму следующим образом. Па- раметры-значения размером в один байт передаются одним 16- разрядным словом, причем информативным является младший байт, параметры-значения в 2 байта передаются одним 16- разрядным словом, в 4 байта двумя 16-разрядными словами, па- раметры-значения типа Real передаются тремя 16-разрядными сло- вами, все остальные параметры-значения (в том числе и 3- байтовые) передаются своими полными адресами. Из этого правила есть некоторые исключения: параметры-переменные и параметры- константы всегда передаются своими полными адресами.

Т.к. в подпрограмме первый параметр является параметром- переменной, то он передается своим адресом, с помощью которого в дальнейшем и извлекаются элементы массива. Второй параметр подпрограммы параметр-значение, и он передается своим значе- нием. Первый параметр находится по адресу ВР+6, а второй ВР+4. Указанные смещения определяются наличием в стеке адреса воз- врата (при ближней адресации - 2 байта), размещенным в стеке зна- чением регистра ВР (2 байта) и для первого параметра - размером второго параметра (2 байта).

Если подпрограмма является подпрограммой-функцией, то возвра-

щаемый параметр передается различным образом в зависимости от своего размера. Параметр размером в байт передается в регистре AL, параметр размером в 2 байта - в регистре АХ, параметр разме- ром в 4 байта - в регистрах DX (старшая часть или адрес сегмента) и АХ (младшая часть или смещение), параметры размером в 6 байтов (типа Real) - в регистрах DX (старшая часть), ВХ (средняя часть) и АХ (младшая часть). Параметры других вещественных типов пере- даются в нулевом элементе стека сопроцессора ST(0). Если функция возвращает значение типа string, то при обращении к функции ре- зервируется память для размещения возвращаемой строки, а адрес

Встроенный ассемблер

359

 

 

этой области размещается в стеке выше всех передаваемых пара- метров.

В рассматриваемом примере возвращаемый параметр типа Integer, и он возвращается в регистре АХ.

При возвращении из подпрограммы в команде RET записан аргу- мент 6 для удаления из стека передаваемых параметров, которые в данном примере имеют именно этот размер.

Turbo Assembler предполагает и другое оформление подпрограмм, используемых затем в программах, написанных на языке Паскаль. Для этого используется специальная модель памяти Large (боль- шая), задаваемая в виде:

.MODEL Large, PASCAL.

Она позволяет несколько упростить оформление входа в подпро- грамму и выхода из нее. Подпрограмма дополняется необходимы- ми командами на этапе компиляции.

Пример. Вариант предыдущей подпрограммы, использующий спе- циальную модель памяти.

 

.MODEL

Large, PASCAL

;специальная модель памяти

 

.CODE

 

;внешний идентификатор

 

PUBLIC

MAX

Max

 

PROC

NEAR mas:DWORD, N:WORD

 

LDS

SI, MAS

;адрес массива

 

XOR

AX, AX

;0 - в регистр АХ

 

MOV

BX,8001H

;минимальное целое число

 

MOV

CX, N

;число элементов массива

 

CMP

CX, AX

;сравнение с 0

 

JLE

@@3

;0 или отрицательное число

@@1

LODSW

 

;загрузка элементов массива

 

CMP

AX, BX

;сравнение с текущим максимумом

 

JLE

@@2

;не больше

 

MOV

BX, AX

;новое максимальное число

@@2 LOOP

@@1

;цикл

@@3

MOV

AX, BX

;результат функции

MAX

RET

ENDP

;возврат из подпрограммы

END

 

 

 

 

360

Приложение

 

 

В этом примере не сохраняется и не восстанавливается регистр ВР

эти операции добавляются к программе на этапе компиляции. Не указывается также и размер передаваемых параметров они при выходе из подпрограммы удаляются автоматически. В строке, где начинается описание подпрограммы (начинается с имени подпро- граммы Мах), необходимо перечислить все передаваемые пара- метры в том же порядке, как они заданы в заголовке, написанном на языке Паскаль с указанием их размеров (о размерах передавае- мых параметров см. выше).

Здесь показана также возможность использования в подпрограммах локальных меток, начинающихся символами @@.

В подпрограмме, написанной на языке ассемблера, можно исполь- зовать подпрограммы, написанные на языке Паскаль. Несколько

модифицированная подпрограмма определения максимального элемента массива, которая в случае недопустимого числа элементов массива (0 или отрицательное число) вызывает подпрограмму, на- писанную на языке Паскаль для выдачи сообщения, приведена в следующем примере.

Пример. Модифицированный вариант программы, в котором под- программа, написанная на языке ассемблера, в случае недопусти- мого числа элементов массива (равно 0 или отрицательное) вызы- вает подпрограмму, написанную на языке Паскаль, выводящую со- ответствующее сообщение.

Основная программа, содержащая подпрограмму на языке Паскаль, будетиметь следующий вид:

Program EXAMPLE2;

Сonst N = 7;

 

{Размер массива}

Massiv: array [1..n] of Integer = (1, 2, 3, 2, 17, 7, 2) ;

{Исходный массив}

($L SUBR)

{ Подключение файла SUBR.OBJ}

Function Max (var Mas; N: Integer): Integer; External; Procedure ErrorReport (N: Integer);

begin WriteLn;

WriteLn('Недопустимое число элементов: ', N); ReadLn

end; begin

Встроенный ассемблер

361

 

 

WriteLn('Максимальное число массива равно: ', Max(Massiv, N)); ReadLn

end.

Подпрограмма, написанная на языке ассемблера, будет в этом слу- чае иметь следующий вид:

 

.MODEL

Large, PASCAL

;специальная модель памяти

 

.CODE

 

;внешняя подпрограмма

 

EXTRN

ErrorReport: Near

 

PUBLIC

Max

;внешний идентификатор

Max

 

PROC NEAR Mas: DWORD, N: Word

 

 

 

;передаваемые параметры

 

LDS

SI, Mas

;адрес массива

 

XOR

AX, AX

;0 - в регистр AX

 

MOV

BX, 8001h

;минимальное целое число

 

MOV

CX, N

;число элементов массива

 

CMP

CX, AX

;сравнение с 0

 

JG

@@1

;допустимое число

 

PUSH

BX

;сохранение регистра BX

 

PUSH

CX

;передаваемый параметр

 

CALL

ErrorReport

;обращение к подпрограмме

 

POP

BX

;восстановление регистра BX

 

JMP

@@3

;на завершение

@@1:

LODSW

 

;загрузка элемента массива

 

CMP

AX, BX

;сравнение с текущим максимумом

 

JLE

@@2

;не больше

 

MOV

BX, AX

;новое максимальное число

@@2: LOOP

@@1

;цикл

@@3:

MOV

AX, BX

;результат функции

Max

RET

ENDP

;возврат из подпрограммы

END

 

 

 

 

Перед обращением к подпрограмме, написанной на языке Паскаль, в стек в соответствующем порядке следует поместить передавае- мые параметры. В данном случае такой параметр один - число эле- ментов массива.

Т.к. подпрограмма, написанная на языке Паскаль, не гарантирует сохранение регистров AX, BX, CX и DX, то в случае необходимости сохранения их значений следует перед обращением к подпрограм- ме, написанной на языке Паскаль, сохранить в стеке значения соот- ветствующих регистров, а после возвращения из подпрограммы -

362

Приложение

 

 

восстановить их. В данном примере сохраняется содержимое реги- стра BX, в котором записано минимальное целое число.

При написании программ, содержащих отдельные части, написан- ные на языках ассемблера и Паскаль, следует обращать внимание на способ адресации (дальний far или ближний near). Здесь су- ществует следующее правило: если подпрограмма объявляется в интерфейсной части какого-либо модуля, то она должна иметь дальнюю адресацию, в других случаях (подпрограмма объявляется в файле, содержащем основную программу, или в исполнительной части модуля) следует использовать ближнюю адресацию.

И еще одно замечание: внешнюю подпрограмму нельзя объявлять внутри другой подпрограммы.

Использование встроенного ассемблера

Начиная с версии 6.0 Turbo Pascal содержит встроенный ассемблер, позволяющий писать отдельные части программ на языке ассемб- лера. Встроенный ассемблер обладает многими возможностями языка Turbo Assembler, но приспособлен к использованию в про- граммах, написанных на языке Паскаль (позволяет использовать идентификаторы программы, написанные на языке Паскаль, ком- ментарии, имеющие такой же вид, как в языке Паскаль, позволяет воспользоваться встроенным отладчиком для пошагового выполне- ния программы, контроля содержимого регистров и параметров программы и т.д.).

Так же как и Turbo Аssembler, встроенный ассемблер предполагает использование ряда предопределенных стандартных идентифика- торов, имеющих специальное назначение. Если в программе будет введен идентификатор с таким же именем, но имеющий другое на- значение, в частях программы, написанных на встроенном ассемб- лере, будет отдано предпочтение стандартному назначению этого идентификатора. Перечень стандартных идентификаторов встроен- ного ассемблера приведен в приложении В.

Наряду с возможностью использования идентификаторов языка Паскаль встроенный ассемблер использует три дополнительных идентификатора:

@Code текущий кодовый сегмент (используется только с опе- ратором SEG);