Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
uchebnik.docx
Скачиваний:
0
Добавлен:
10.01.2020
Размер:
557.61 Кб
Скачать

2.2.5 Технология программирования

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

К настоящему моменту наиболее популярными и жизнеспособными оказались две технологии программирования: структурная и объектно-ориентированная.

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

Структурное программирование

Структурное программирование – методология программирования, базирующаяся на системном подходе к анализу, проектированию и реализации программного обеспечения. Ее основу составляет концепция модульного программирования.

Концепцию модульного программирования можно сформулировать в виде нескольких понятий и положений. Основа концепции модульного программирования – модуль, который является продуктом процесса разбиения большой задачи на ряд более мелких функционально самостоятельных подзадач. Этот процесс называется функциональной декомпозицией задачи. Каждый модуль в функциональной декомпозиции представляет собой «черный ящик» с одним входом и одним выходом. Модули связаны между собой только входными и выходными данными.

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

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

Процедуры в языке ассемблера

В Ассемблере для оформления процедур как отдельных объектов с у шествуют специальные директивы PROC/ENDP и машинная команда RET. Если сравнивать процедуры и макрокоманды, то можно сказать следующее [7-8]:

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

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

Возможные варианты размещения процедур в программе:

  • в начале программы (до первой исполняемой команды);

  • в конце программы (после команды, возвращающей управление операционной системе);

  • промежуточный вариант – тело процедуры располагается внутри другой процедуры или основной программы (в этом случае необходимо предусмотреть обход процедуры с помощью команды безусловного перехода JMP);

  • в другом модуле.

Главная цель таких вариантов размещения – не допустить несанкционированной передачи управления коду процедуры. Три первых варианта относятся к случаю, когда процедуры находятся в одном сегменте кода. Что же касается последнего варианта, то он предполагает, что процедуры находятся в разных модулях. А это дает нам возможность говорить уже не об одном модуле, а о нескольких. Для реализации одной общей задачи эти модули должны быть связаны между собой по управлению и по данным.

Так как отдельный модуль в соответствии с концепцией модульного программирования — это функционально автономный объект, то он ничего не должен знать о внутреннем устройстве других модулей, и наоборот, другим модулям также ничего не должно быть известно о внутреннем устройстве данного модуля. Однако должны быть какие-то средства, с помощью которых можно связать модули. В качестве аналогии можно привести организацию связи (интерфейс) монитора и видеокарты через стандартный интерфейс. Связь унифицирована, то есть известно через какой контакт какой сигнал передается. Та же идея лежит и в организации связи модулей. Внутреннее устройство модулей может совершенствоваться, они вообще могут в следующих версиях писаться на другом языке, но в процессе их объединения в единый исполняемый модуль этих особенностей не должно быть заметно. Таким образом, каждый модуль должен иметь такие средства, с помощью которых он извещал бы транслятор о том, что некоторый объект (процедура, переменная) видимы вне этого модуля. И наоборот, нужно объяснить транслятору, что некоторый объект находится вне данного модуля. Это позволит транслятору правильно сформировать машинные команды, оставив некоторые их поля не заполненными. Позднее, на этапе компоновки, программа TLINK (TASM) или программа компоновки языка высокого уровня произведут настройку модулей и разрешат все внешние ссылки в присоединяемых модулях.

Для того, чтобы объявить о подобного рода объектах, видимых извне, программа должна использовать две директивы TASM: EXTERN и PUBLIC. Директива EXTERN предназначена для объявления некоторого имени, внешнего по отношению к данному модулю. Это имя в другом модуле должно быть объявлено в директиве PUBLIC. Директива PUBLIC предназначена для объявления некоторого имени, определенного в этом модуле, видимым в других модулях. Синтаксис этих директив следующий:

EXTERN имя:тип, ..., имя:тип

PUBLIC имя, ..., имя

Здесь имя — идентификатор, определенный в другом модуле. В качестве идентификатора могут выступать:

  • имена переменных, определенных директивами типа DB, DW и т. д.;

  • имена процедур;

  • имена констант, определенных операторами = и EQU.

Аргумент «тип» определяет тип идентификатора. Указание типа необходимо для того, чтобы транслятор правильно сформировал соответствующую машинную команду. Действительные адреса вычисляются на этапе редактирования, когда будут разрешаться внешние ссылки. Возможные значения типа определяются допустимыми типами объектов для этих директив:

  • если имя – это имя переменной, то тип может принимать значения BYTE, WORD, DWORD, PWORD, FWORD, QWORD и TBYTE;

  • если имя – это имя процедуры, то тип может принимать значения near или far;

  • если имя – это имя константы, то тип должен быть abs.

Покажем принцип использования директив EXTRN и PUBLIC на схеме связи модулей 1 и 2.

.model small

.stack 256

.data

.code

my_proc_l proc

my_proc_1 endp

my_proc__2 proc

my_proc__2 endp

; объявляем процедуру my_proc_l видимой извне

public my_proc_1

start:

mov ax,@data

end start

/Модуль 2

.model small

.stack 256

.data

.code

extrn my_proc_l ;объявляем процедуру my_proc_1 внешней

start:

mov ax,@data

call my_proc_l; вызов процедуры my_proc_l из модуля 1

end start

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

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

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

Переменная – это нечто, размещенное в регистре или ячейке памяти, что может в дальнейшем подвергаться изменению.

Константа – информационный объект простого типа, значение которого никогда не изменяется.

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

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

На практике используются следующие варианты передачи аргументов в модуль (процедуру):

  • через регистры;

  • через общую область памяти;

  • через стек (используется при вызове API-функций в программах для Windows);

  • с помощью директив EXTERN и PUBLIC.

Передача аргументов через регистры

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

Недостатки этого способа:

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

  • необходимость постоянно помнить о том, какая информация, в каком регистре находится;

  • ограничение размера передаваемых данных размерами регистра

В последнем случае, если размер данных превышает 8, 16 или 32 бита, то передачу данных посредством регистров произвести нельзя и передавать приходится не сами данные, а указатели на них.

Возврат результата из процедуры

В отличие от языков высокого уровня, в языке ассемблера нет отдельных понятий для процедуры и функции. Организация возврата результата из процедуры полностью ложится на программиста. Если исходить из того, что получение результата – частный случай передачи аргументов, то программисту доступны три варианта возврата значений из процедуры:

С использованием регистров. Ограничения здесь те же, что и при передаче данных – это небольшое количество доступных регистров и их фиксированный размер. Функции DOS используют именно этот способ. Из рассматриваемых здесь трех вариантов данный способ является наиболее быстрым, поэтому его есть смысл задействовать для организации критичных по времени вызова процедур с малым количеством аргументов.

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

С использованием стека. Здесь, подобно передаче аргументов в процедуру через стек, также используется регистр SР. При этом возможны следующие варианты:

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

  • предварительное помещение в стек наряду с передаваемыми аргументами фиктивных аргументов с целью резервирования места для возвращаемого значения (в этом варианте «процедура, конечно же, не должна пытаться очистить стек командой RET, эту операцию придется делать в вызывающей программе, например, командой POP).

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

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

Одним из вариантов моделей открытой среды является модель OSE (Open System Environment), предложенная комитетом IEEE POSIX. На основе этой модели национальный институт стандартов и технологии США выпустил документ "Application Portability Profile (APP). The U.S. Government's Open System Environment Profile OSE/1 Version 2.0", который определяет рекомендуемые для федеральных учреждений США спецификации в области информационных технологий, обеспечивающие мобильность системного и прикладного программного обеспечения. Все ведущие производители компьютеров и программного обеспечения в США в настоящее время придерживаются требований этого документа.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]