В Ассемблере для оформления процедур как отдельных объектов существуют спе- циальные директивы РRОС/ЕNDР и машинная команда RЕТ (см. главу 10). Если срав- нивать процедуры и макрокоманды, то можно сказать следующее. Процедуры, так же как и макрокоманды, могут быть активизированы в любом месте программы. Процедурам, так же как и макрокомандам, могут быть переданы некоторые аргу- менты. Это позволяет, имея одну копию кода в памяти, изменять ее для каждого конкретного случая использования, хотя по гибкости процедуры уступают макро- командам. В главе 10 нами были рассмотрены возможные варианты размещения процедур в программе: ⅝ в начале программы (до первой исполняемой команды); ® в конце программы (после команды, возвращающей управление операционной системе); Ш промежуточный вариант — тело процедуры располагается внутри другой про- цедуры или основной программы (в этом случае необходимо предусмотреть обход процедуры с помощью команды безусловного перехода JMP); * в другом модуле. Главная цель таких вариантов размещения — не допустить несанкционирован- ной передачи управления коду процедуры. Три первых варианта относятся к слу- чаю, когда процедуры находятся в одном сегменте кода. Мы их достаточно подробно обсудили. Что же касается последнего варианта, то он предполагает, что процеду- ры находятся в разных модулях. А это дает нам возможность говорить уже не об одном модуле, а о нескольких. Для реализации одной общей задачи эти модули должны быть связаны между собой по управлению и по данным. Если мы разбе- ремся с тем, как организовать такую связь, то фактически сможем выполнить функ- циональную декомпозицию любой большой программы на нужное количество более мелких. В первой части главы мы рассмотрим, как организуется связь по управлению и по данным между модулями на ассемблере, а во второй части — меж- ду модулями на ассемблере и на языках высокого уровня (Pascal и C/C++). Сначала необходимо отметить один общий для всех этих трех языков момент. Так как отдельный модуль в соответствии с концепцией модульного программи- рования — это функционально автономный объект, то он ничего не должен знать о внутреннем устройстве других модулей, и наоборот, другим модулям также ни- чего не должно быть известно о внутреннем устройстве данного модуля. Однако должны быть какие-то средства, с помощью которых можно связать модули. В ка- честве аналогии можно привести организацию связи (интерфейс) телевизора и ви- деомагнитофона через разъем типа «скарт». Связь унифицирована, то есть извест- но, что один контакт предназначен для видеосигнала, другой — для передачи звука и т. д. Телевизор и видеомагнитофон могут быть разными, но связь между ними одинакова. Та же идея лежит и в организации связи модулей. Внутреннее устрой- ство модулей может совершенствоваться, они вообще могут в следующих версиях писаться на другом языке, но в процессе их объединения в единый исполняемый модуль этих особенностей не должно быть заметно. Таким образом, каждый мо- дуль должен иметь такие средства, с помощью которых он извещал бы транслятор о том, что некоторый объект (процедура, переменная) видимым вне этого модуля. И наоборот, нужно объяснить транслятору, что некоторый объект находится вне данного модуля. Это позволит транслятору правильно сформировать машинные команды, оставив некоторые их поля не заполненными. Позднее, на этапе компо- новки, программа TLINK (TASM) или программа компоновки языка высокого уровня произведут настройку модулей и разрешат все внешние ссылки в объеди- няемых модулях. Для того чтобы объявить о подобного рода объектах, видимых извне, програм- ма должна использовать две директивы TASM: EXTRN и PUBLIC. Директива EXTRN предназначена для объявления некоторого имени внешним по отношению к дан- ному модулю. Это имя в другом модуле должно быть объявлено в директиве PUBLIC. Директива PUBLIC предназначена для объявления некоторого имени, определен- ного в этом модуле и видимого в других модулях. Синтаксис этих директив следу- ющий: extrn имя: тип. . . . имя: тип public имя......... имя Здесь имя — идентификатор, определенный в другом модуле. В качестве иден- тификатора могут выступать: имена переменных, определенныхдирективами типа DB, DW и т. д.; ж имена процедур; ® имена констант, определенных операторами = и EQU. Аргумент тип определяет тип идентификатора. Указание типа необходимо для того, чтобы транслятор правильно сформировал соответствующую машинную ко- манду. Действительные адреса вычисляются на этапе редактирования, когда бу- дут разрешаться внешние ссылки. Возможные значения типа определяются допу- стимыми типами объектов для этих директив: в если имя — это имя переменной, то тип может принимать значения BYTE, WORD,