использоваться. При модульном программировании программист не ин-
тересуется тем, какие регистры он использует в настоящий момент,
пока вызываемый модуль копирует его параметры в стек и сохраняет
весь набор регистров на входе. Эти особенности создают возмож-
ность сначала использовать приемы модульного программирования для
повышения скорости кодирования и затем переработки программы для
удаления "узких" мест.
Для областей, чувствительных к скорости работы, лучшей реко-
мендацией является выбор основной ветви программы. Если модуль
упоминается только в чувствительной к скорости работы программной
секции, то он может быть включен в "ветвь" внутри вызывающего
модуля. Если другие секции используют модуль, то они могут быть
скопированы в вызывающий модуль в необходимое место. В связи с
тем, что основной вызывающий модуль станет большим, необходимо
вставить комментарии в его тело, помечающие включаемый модуль как
блок его владельца. Будущие читатели смогут затем прочитать ком-
ментарии для определения функций модуля и пропустить его мимо для
возобновления чтения основного кода.
Правила модульного программирования
Наиболее существенные концепции модульного программирования
можно изложить в виде следующих правил:
- Разделение и подчинение. Разделите проблему на узкие функ-
циональные задачи, каждая из которых независима от других, исклю-
чая необходимые для нее параметры;
- Один вход - один выход. Модуль должен иметь только одну
точку входа, в которую передается управление при всех вызовах.
Возвращать управление модуль должен в ту точку в программном по-
токе, из которой было передано управление (адрес возврата может
- 2-5 -
быть модифицирован, как обсуждается в следующем разделе по пере-
даче параметров);
- "KISS-принцип"(Keep It Simple,Stuiped - делай все попроще,
дурачок). Избегайте сложности при кодировании модуля. Используйте
сложную логику только при условии хорошего документирования, объ-
ясняющего каждый шаг и способ его проектирования;
- Упрятывание подробностей. Ограничивайте подробности исполь-
зования регистров, структуры локальных данных и т.д. для внутрен-
них модулей. Не допускается реализацию модуля перебрасывать в ос-
тавшуюся часть программы;
- Если модуль использует особую переменную, сделайте так,
чтобы переменная была документируемым параметром. Документируйте
все действия, чтобы модуль имел глобальные данные;
- Планируйте обнаружение ошибок и действия, которые должны
быть предприняты при возникновении ошибки. Ответственность за
обработку исключительных ситуаций, как известно, должна возла-
гаться на конкретные модули. Обычно модули нижнего уровня переда-
ют отчеты об ошибках в вызывающий модуль. Ответственность за при-
нятие решений по этим ошибкам обычно резервируется за модулями
верхнего уровня.
Справочная литература
То, что представлено в данном разделе, является кратким вве-
дением в концепции структурного программирования и модульного
проектирования. За неимением места, мы не могли полностью обсу-
дить данный предмет. Однако, по этой проблеме доступно огромное
количество литературы. Если Ваша цель состоит в том, чтобы стать
профессионалом в области программного обеспечения, то купите не-
которые из этих книг и прочитайте их. Представленные ниже книги
являются классическими работами по данному предмету и отражают
узкие примеры превосходных работ на доступном профессиональном
уровне:
DeMarco, T. Structured Analysis and System Specification. New
York: Yourdon,1978.
Kane,G.,D.Hawkins and L.Leventhal.68000 Assembly Language
Programming,Berkeley:Osborne/McGraw-Hill,1981.
Tausworthe, R.C. Standardized Development of Computer
Software. Part 1. Englewood Cliffs, N.J.:Prentice-Hall,
1977.
Yourdon, E.U., and L.L.Constantine. Structured Design.
Englewood Cliffs, N.J.:Prentice-Hall,1977.
Yourdon, E.U. Techniques of Program Structure and Design.
Englewood Cliffs, N.J.:Prentice-Hall,1975.
Реализация модульных программ на языке Ассемблер
До сих пор мы вели абстрактный разговор о модулях, передаче
параметров и других подобных термах. Теперь наступило время на-
чать рассмотрение отношения этой информации к конкретному миру
языка Ассемблер в среде MS-DOS, макроассемблера MASM и микропро-
цессора 8086.
Модули в среде MASM лучше всего поддерживаются с помощью ди-
рективы PROC. Мы будем использовать ее все время в качестве мето-
да определения точек входа и выхода программы. Теперь расширим
ее использование для определения границ конкретных модулей.
Директива PROC используется MASM для определения метки в програм-
- 2-6 -
ме, поэтому дадим этой метке либо атрибут near (близкий) или ат-
рибут far (далекий). Этот атрибут используется для генерации как
правильных типов инструкции CALL, так и правильных типов инструк-
ции RET. Подробное представление этих типов инструкций приводится
далее в разделе "Типы кодирования". Здесь нас прежде всего ин-
тересует то, что директива PROC является удобным способом обозна-
чения блока программы с одним уникальным входом и постоянным вы-
ходом, которые образуют основу модуля.
Определение параметра, аргумента, переменной константы
Мы "вращались" вокруг слов параметр, аргумент и переменная
подобно шарикам для настольного тенниса. Большей частью эти слова
имели взаимозаменяемые значения. Теперь необходимо определить
различия между ними (хотя некоторые из них несомненно будут тре-
бовать уточнения). После этой главы мы будем всегда возвращаться
к правильным способам, однако, сейчас необходимо пояснить основ-
ные понятия и положения.
Словарным понятием "параметр" является "элемент характеристи-
ки". В общем смысле параметр представляет собой ссылку на любую
часть данных, используемую модулем, которая в целом не содержится
внутри этого модуля. Почему добавлены слова "ссылка на"? Потому,
что параметр это не сами данные, и, даже, не адрес данных. Скорее
всего, параметр это местодержатель (элемент характеристики). Нап-
ример, рассмотрим уравнение Y+1. Нельзя написать модуль для оцен-
ки этого уравнения, потому что Y не является конкретным значени-
ем! Y есть параметр, который заменяется действительным значением
во время оценки этого уравнения. Действительное значение называ-
ется "аргументом".
Мы еще не определили понятие "переменная". Строго говоря, это
нечто, размещенное в регистре или ячейке памяти и содержащее пор-
цию данных, которые могут в дальнейшем изменяться. В предыдущем
примере Y также является переменной, поскольку он изменяется в
зависимости от требуемых обстоятельств. Поэтому аргументы автома-
тически являются переменными (но не наоборот).
Таким образом, если объект данных может изменяться, то это
переменная. Если же эта переменная требуется в модуле для выпол-
нения возложенной на него задачи, то она в то же время является
параметром. Аргумент - это действительное значение, которое при-
нимает переменная при вызове модуля.
Нам также необходимо рассмотреть специальный случай "констан-
ты". Константа - это объект данных, значение которого никогда не
изменяется. В языке Ассемблер константы могут появляться в двух
случаях. Они могут быть частью непосредственных данных для инс-
трукций (например, mov al,4) или они могут быть размещены в памя-
ти подобно другим данным. Когда константа помещена в память, она
отличается от переменной исключительно тем, что "только читает-
ся" и никогда не записывается.
Может ли параметр также быть константой? Если константа явля-
ется типом памяти, то определенно да. Однако, при попытке непос-
редственного использования константы данных в качестве параметра
могут возникнуть определенные проблемы. Непосредственные данные в
подпрограмме передавать сами себя не могут. Непосредственные дан-
ные должны содержаться в чем-либо: в регистре, ячейке памяти или
в стеке. В языках высокого уровня о преобразовании констант с
целью их размещения заботится компилятор. В языке Ассемблер это
должен делать сам программист.
- 2-7 -
Параметры и модули
Мы определили, что параметры представляют собой какие-либо
данные, требуемые модулем для выполнения возложенной на него за-
дачи, и которые размещаются вне модуля. Мы также определили, что
параметры определяют и переменные. Таким образом, вырисовывается
второе большое преимущество модулей. В связи с тем, что входными
данными для модуля являются переменные, то они могут быть измене-
ны для подходящего конкретного случая. Тем самым, модулям прида-
ется больше общности, позволяя им быть повторно-используемыми в
любом месте любой программы.
В действительности, параметры являются необязательными компо-
нентами модульного программирования. Можно иметь модуль, который
не принимает внешние параметры, а функционирует исключительно с
внутренними данными. Простая программа выработки звукового сигна-
ла консоли не имеет параметров. Более общим примером является
простая программа для чтения чисел с клавиатуры. Хотя программа
чтения числа будет возвращать значение, программе не нужен ника-
кой аргумент, передаваемый для нее.
Объединяя требующиеся входные параметры и вырабатываемые вы-
ходные значения, можно сформировать следующие четыре группы моду-
лей:
1. Модули, не принимающие входные параметры и не вырабатываю-
щие выходные значения.
2. Модули, принимающие входные параметры и не вырабатывающие
выходные значения.
3. Модули, не принимающие входные параметры и вырабатывающие
выходные значения.
4. Модули, принимающие входные параметры и вырабатывающие
выходные значения.
Обычно первые две группы модулей, не вырабатывающие выходные
данные, называются подпрограммами, а последние два типа, выраба-
тывающие выходные данные, функциями. Заметим, что различие произ-
водится в зависимости от того, требуют ли модули входных парамет-
ров, хотя как программист вы интуитивно осознаете различие.
Опции передачи параметров