Распределение памяти под локальные данные

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

19.4.1. Распределение памяти для постоянных локальных переменных

Borland Pascal позволяет ассемблерным программам резервировать пространство для данных в сегменте глобальных данных(DATA илиDSEG). В тексте ассемблерной программы такое выделение данных происходит с помощью директив типаDB, DW и т. п.), например:

DATA SEGMENT PUBLIC AInt DW ? AByte DB ?

DATA ENDS

Переменные, размещенные в глобальном сегменте данных, имеют два важных ограничения. Первое из этих ограничений заключается в том, что такие переменные являются внутренними (private) по отношению к ассемблерному модулю, поэтому из модуля Borland Pascal они недоступны (однако в Pascal-программу можно передать

указатель на эти данные). Второе ограничение — эти переменные нельзя инициализировать при объявлении. Например, оператор

AInt DW 42h

не будет порождать ошибки при трансляции и компоновке, однако во время работы программы заначение AInt при первом обращении будет неопределенным.

Эти ограничения можно обойти, применяя типизированные константы Borland Pascal с последующим их объявлением в ассемблерном модуле директивойEXTRN.

19.4.2. Распределение памяти для временных локальных переменных

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

В следующем примере процедура AsmProc резервирует место для двух перемен-н-ых целого типа а и Ь:

CODE SEGMENT

ASSUME cs:CODE AsmProc PROC FAR ,'procedure AsmProc(i:integer);

PUBLIC AsmProc

LOCAL a:word, b:word == LocalSpace ; а находится по адресу [bp-2],

; b - [bp-4] i EQU WORD PTR [BP+6]

push bp

mov bp.sp

sub sp, LocalSpace .•уменьшение SP для размещения локальных переменных

mov ax,42

mov a,ax

xor ax,ax

mov b,ax .•инициализация локальных переменных

mov sp.bp

pop bp

ret 2 AsmProc ENDP CODE ENDS

END

Оператор

LOCAL a:word, b:word = LocalSpace

ставит в соответствие^ идентификатору д значение [BP-2J, идентификатору b — значение [ВР-4|, идентификатору LocalSpace - число 4 (общий размер области локальных переменных в байтах) для дальнейшего использования в процедуре.

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

; sub sp, LocalSpace — оператор не нужен mov ах,42 push ax xor ax,ax push ax

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

Если разрабатываемая программа будет работать только на моделях процессоров не-ниже 8018Г), то is ней можно использовать еще один метод инициализации локальных переменных, заключающийся в применении командыPUSH с непосредственным операндом. Кроме того, регистр BP можно сохранять не в стеке, а в каком-либо неиспользуемом в процедуре роистре, если таковой имеется.

19.5. Примеры применения ассемблерных подпрограмм в Borland Pascal

 

19.5.1. Многоцелевая подпрограмма представления чисел в виде шестнадцатеричных цифр

 

Эта подпрограмма выполняет преобразование параметра num в строку шестнадцатеричных цифр длиной (ByteCount*2). Для повышения скорости работы процедуры при преобразовании каждой тетрады (4 бита) байта в ней использовалась последовательность команд ADD-DAA-ADC-DAA.

HexStr написана на ассемблере как FAR-процедура, поэтому, если в тексте программы на Borland Pascal явно не указан тип FAR, то либо ее объявление должно находиться в интерфейсной секции модуля, либо при компиляции необходимо указывать директиву компилятора $F+.

Ассемблерный код процедуры (файл HEX.ASM):

 

CODE SEGMENT

ASSUME cs:CODE,ds:NOTHING

; Параметры (+2 для учета помещения в стек Ьр)

byteCount EQU BYTE PTR ss:[bp+6]

num EQU DWORD PTR ss:[bp+8]

; Адрес результата функции (+2 для учета помещения в стек Ьр)

resultPtr EQU DWORD PTR ss:[bp+12]

HexStr PROC FAR

PUBLIC HexStr

push bp

mov bp,sp ;попучить указатель на стек

;считывает по указанному адресу из памяти двойное слово,

;содержащие полный адрес, и загружает младшую половину в указанный регистр,

; а старшую в ES

les di,resultPtr ;получить адрес области результата функции

mov dx,ds ;сохранить DS Borland Pascal в DX

;считывает по указанному адресу из памяти двойное слово,

;содержащие полный адрес, и загружает младшую половину в указанный регистр,

; а старшую в DS

lds si,num ;получить адрес числа

mov al,byteCount ; количество байтов

xor ah,ah

mov cx,ax

add si,ax ; начать со старшего байта числа

dec si

; сдвиг влево всех битов операнда на 1 бит

shl ax,1 ;сколько цифр ? (2/байт)

; сброс флага направления DF в регистре флагов

cld ;запомнить номер цифры

hexloop:

; запись байта из АХ в строку данных

stosb ;запись длины строки результата HexLoop:

; установка флага направления DF в регистре флагов, определяя обратное ;направление выполнение строковых операций (по убыванию адресов элементов ;строки)

std ; просмотр числа от старшего ; байта к младшему

;загрузка байта из строки

lodsb ; получить следующий байт

mov ah,al ;сохранить его

;логический сдвиг вправо битов операнда на 1 бит

shr al,1 ; выделить старшую тетраду

shr al,1

shr al,1

shr al,1

add al,90h ;алгоритм преобразования

; корректировка операции слодения , если результат сложения превышает 99,

; то возникает перенос и устанавливается флаг CF

daa

; Сложение оперантов, прибавляя к результату значения флага CF

adc al,40h

daa ;тетрада преобразована в ASCII

; сброс флага направления DF в регистре флагов

cld

stosb

mov al,ah ;Преобразование младшей тетрады

and al,0Fh

add al,90h

daa

adc al,40h

daa

stosb

loop HexLoop

mov ds,dx ;восстановление DS

pop bp

ret 6 ;длина параметров 6 байт

HexStr ENDP

CODE ENDS

END

Программа на Borland Pascal, использующая данную функцию, которая нaxодится в объектном файле HEX.OBJ, может иметь следующий вид:

 

program HexTest;

var num: Word;

 

function HexStr (var num; ByteCount:byte):string; far; external;

{$L HEX}

 

begin

num:=12345;

Writeln(' Шестнадцатеричный вид числа ',num, '=',HexStr(num,SizeOf(num)));

end.

 

18.1.9. Соглашения о вызовах, принятых в Pascal

Как уже упоминалось раньше, при вызове функции в C++ выполняется помещение передаваемых параметров в стек в направлении слева направо, а по возвращению из нее — очистка стека от этих параметров. Помимо этих соглашений, Borland C++ поддерживает соглашения языка Pascal, согласно которым параметры передаются в процедуру слева направо, а стек очищает вызываемая функция. Включение соглашений языка Pascal достигается применением переключателя командной строки -р или ключевого слова PASCAL.

Пример использования соглашений языка Pascal в ассемблерных функциях:

'; Вызов: TEST (i,|, k);

i equ 8 j equ 6 k equ 4

.MODELL small .CODE PUBLIC TEST TEST PROC push bp mov bp.sp mov ax,[bp+i] add ax,[bp+j] sub ax,[bp+k] pop bp

ret 6 [возврат и очистка стека TEST ENDP END

На рис. 18.4 приведен вид стека после выполнения команды MOV BP,SP.

 

 

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

Причиной, по которой могут использоваться соглашения языка Pascal, является уменьшение размера кода и увеличение скорости выполнения, так как обычный код C++ генерирует дополнительные команды вида ADD SP, n для очистки стека после выполнения процедуры.

 

Рис. 18.4. Вид стека при использовании соглашений языка Pascal.

 

 
 



php"; ?>