Модульное программирование

       Содержание 

       Введение 3

       1 Постановка задачи 5

       2 Модульное программирование 6

       3 Описание программы 11

       Заключение 16

       Список  литературы 17

       Приложение  А 18

       Приложение  Б 19

       Введение

 

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

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

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

       Основная  цель модульного подхода — простота и ясность реализуемых решений.

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

       Для достижения поставленной цели необходимо решить следующие задачи:

    • определить метод решения задачи
    • определить подходящие для использования в работе средства ассемблирования и команды транслятора
    • разработать алгоритм решения задачи
    • реализовать разработанный алгоритм в виде программы

       1 Постановка задачи

 

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

       2 Модульное программирование

 

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

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

       Модуль  – это программа, обладающая тремя  основными атрибутами:

    1. Он выполняет одну или несколько функций;
    2. Модуль реализует некоторую логику (алгоритм);
    3. Используется в одном или нескольких контекстах.

       При этом функция – это то, что  делает модуль, а не то, как он это  делает. А вот логика характеризует, как модуль выполняет свои функции. Контекст описывает конкретное применение.

       Принципы  модульного программирования позволяют  получать программные комплексы  минимальной сложности. Эти  принципы следующие:

       а) усиление внутренних связей в каждом модуле (иначе принцип называется повышением прочности модуля);

       б) ослабление взаимосвязи между модулями (иначе этот принцип называется ослаблением  сцепления модулей).

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

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

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

       Однако  на практике при реализации больших  проектов процедуры и функции  оказываются недостаточно удобным  средством по следующим причинам:

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

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

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

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

       EXTERN  имя: тип    имя: тип

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

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

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

       В нашем случае в качестве идентификатора выступают имена процедур.

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

    • если имя — это имя переменной, то тип может принимать значения BYTE, WORD, DWORD, PWORD, FWORD, QWORD и TBYTE;
    • если имя — это имя процедуры, то тип может принимать значения NEAR или FAR;
    • если имя — это имя константы, то тип должен быть ABS.

       Следовательно, тип идентификатора в нашем случае может принимать значения NEAR или FAR.

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

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

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

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

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

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

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

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

       -   через регистры

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

       -    через стек;

       Для разработки алгоритма решения данной задачи мы будем использовать вариант  передачи аргументов в модуль через  стек.

                                                                                   
 
 
 
 
 
 

       3 Описание программы

 

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

       …

       CALL COPY

       …

            CALL PRINT

       …

       С помощью директивы EXTRN можно указать ассемблеру, что ссылка на  процедуры COPY и PRINT имеет атрибут FAR,  т.е. они определены  в другом ассемблерном  модуле, вспомогательном.

       EXTRN COPY:FAR

       EXTRN PRINT:FAR

       Процедуры COPY и PRINT содержат директиву PUBLIC, которая указывает ассемблеру и компоновщику, что вспомогательный модуль должен "знать" адрес процедур COPY и PRINT.

       PUBLIC COPY, PRINT

       …

       Во  вспомогательном модуле будут находиться процедуры COPY(процедура копирования двух строк в буфер) и PRINT(процедура вывода полученной строки кодом), которые получают параметры через стек. Передача аргументов через стек при вызове процедур используется наиболее часто. Суть этого способа заключается в том, что вызывающая процедура самостоятельно заносит в стек передаваемые данные, после чего обращается к вызываемой процедуре. При передаче управления процедуре процессор автоматически записывает на вершину стека два (для процедур типа near) или четыре (для процедур типа far) байта. Эти байты являются адресом возврата в вызывающую программу. Если перед передачей управления процедуре командой CALL в стек были записаны переданные процедуре данные или указатели на них, то они окажутся под адресом возврата. Для произвольного доступа к данным в стеке архитектура процессора имеет специальный регистр ЕВР\ВР (Base Point — указатель базы). Так же как и для регистра ESP\SP, обращение к ЕВР\ВР автоматически предполагает работу с сегментом стека. Перед использованием этого регистра для доступа к данным стека содержимое стека необходимо правильно инициализировать, что предполагает формирование в нем адреса, который бы указывал непосредственно на переданные данные. Для этого в начало процедуры рекомендуется включить дополнительный фрагмент кода. Он имеет свое название — пролог процедуры.

       COPY PROC

       ;начало  пролога

           PUSH ВР

           MOV BP,SP

       ;конец  пролога

       Код пролога состоит всего из двух команд. Первая команда push bp сохраняет содержимое ВР в стеке с тем, чтобы исключить порчу находящегося в нем значения в вызываемой процедуре. Вторая команда пролога mov bp,sp настраивает ВР на вершину стека. После этого можно не волноваться о том, что содержимое SP перестанет быть актуальным, и осуществлять прямой доступ к содержимому стека. Что обычно и делается. Для процедур типа far эти значения необходимо скорректировать еще на 2, так как при вызове процедуры дальнего типа в стек записывается полный адрес — содержимое CS и IP.

       Конец процедуры также должен быть оформлен особым образом, обеспечивая корректный возврат из процедуры. Фрагмент кода, выполняющего такие действия, имеет свое название — эпилог процедуры.           Код эпилога должен восстановить контекст программы в точке вызова процедуры из вызывающей программы. При этом, в частности, нужно откорректировать содержимое стека, убрав из него ставшие ненужными аргументы, передававшиеся в процедуру. Это можно сделать несколькими способами:

    • можно использовать последовательность из п команд pop xx. Лучше всего это делать в вызывающей программе сразу после возврата управления из процедуры.
    • можно откорректировать регистр указателя стека SP на величину 2 • п, например, командой add sp,NN, где NN = 2 • п, а п — количество аргументов. Это также лучше делать после возврата управления вызывающей процедуре.
    • можно использовать машинную команду RET n в качестве последней исполняемой команды в процедуре. Здесь n — количество байтов, на которое нужно увеличить содержимое регистра ESP\SP после того, как со стека будут сняты составляющие адреса возврата. Видно, что этот способ аналогичен предыдущему, но выполняется процессором автоматически.

       В нашем случае выход из процедуры  осуществляется командой RETF,  возвращающей стек в исходное состояние.

       ;выход  с освобождением стека

               RETF 16

       Кроме того, в процедуре COPY мы определим локальную переменную [ВР-2], типа WORD и будем ее использовать. Сначала обнулим нашу локальную переменную.

       MOV WORD PTR [ВР-2],0

       Затем поместим в неё первую строку  STOKA1 и загрузим в буфер. После мы поместим в неё вторую строку STROKA2  и только в процедуре PRINT загрузим её в буфер.

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

       В последнем шаге, мы оттранслируем оба модуля  loc и loc1. Результаты трансляции приведены в соответствии с Рисунком 1 и Рисунком 2.

       

       Рисунок 1 -  Основной модуль 

       

       Рисунок 2 - Вспомогательный модуль

       В результате на диске образуются два объектных модуля: loc .OBJ и loc1.OBJ . Теперь дело за редактором связей. В командной строке укажем: LINKloc+loc1. После этого отвечаем на вопросы как обычно. При просьбе указать имя МАР-файла, указываем loc (впрочем, имя не имеет никакого значения). В результате компоновки образуется загрузочный модуль loc.exe и loc.map, а также происходит вывод на экран полученной строки, результаты вывода данной строки приведены на Рисунке 3.

       

       Рисунок 3 – Результат вывода на экран  полученной строки

       В этом файле содержатся сведения о  сегментах (начальный и конечный адрес, длина, имя, класс), скомпонованных в один модуль в том порядке, как они помещены в него (начиная с младших адресов). Обращаем Ваше внимание, что вначале идут сегменты модуля loc, а затем сегменты модуля loc1. Именно так, как мы указали модули в командной строке. Если бы в командной строке было LINKloc1+loc, то порядок сегментов был бы обратный. При этом сначала запустился бы модуль loc1, что привело бы к катастрофе. Итак, главный модуль должен быть первым, порядок остальных модулей уже не так важен. 
 
 

       Заключение

 

       Нам удалось разработать алгоритм решения  задачи, использующей два модуля, для  написания программы конкатенации двух строк. Нами было написано два модуля, один из которых основной, а другой вспомогательный. Связывание этих модулей происходит с помощью директив EXTRN и PUBLIC. В качестве идентификаторов в модулях выступают имена процедур COPY и PRINT, типа FAR.

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

       Алгоритм  решения данной задачи разработан и  оформлен в виде программы и представлен в приложении данной работы. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

       Список  литературы

 
       
  1. Пирогов В.Ю., ASSEMBLER. Учебный курс.- М.: Издатель Молгачева С.В., Издательство Нолидж, 2001. - 848 с., ил.
  2. Юров В. И., Assembler. Учебник для вузов. 2-е изд. — СПб.: Питер, 2003. —637 с.: ил.
  3. http://www.studcode.ru/archiv/razrabotka-struktury/
  4. http://thelib.ru/books/abel_piter/assembler_i_programmirovanie_dlya_ibm_pc-read.html

       Приложение  А

Листинг 1. Основной модуль

EXTRN COPY:FAR

EXTRN print:FAR

STE SEGMENT STACK

      DB 100 DUP(0)

STE ENDS 

DATA SEGMENT

STROKA1 DB "GRAND",0

STROKA2 DB "MOTHER",0

BUFER   DB 100 DUP(?)

DATA ENDS 

CODE SEGMENT

      ASSUME CS:CODE, DS:DATA, SS:STE

BEGIN

      MOV AX,DATA

      MOV DS,AX

     PUSH DS

     LEA AX,BUFER

     PUSH AX

     PUSH DS

     LEA AX,STROKA1

     PUSH AX

     PUSH DS

     LEA AX,STROKA2

     PUSH AX

     CALL COPY

     MOV AX,03

     INT 10H

     PUSH DS

     LEA AX,BUFER

     PUSH AX

     CALL PRINT

     MOV AX,4C00H

     INT 21H

CODE ENDS

     END BEGIN      
 
 

       Приложение Б

Листинг 2. Вспомогательный модуль

PUBLIC COPY, PRINT

COD SEGMENT

    ASSUME CS:COD

COPY PROC

    PUSH ВР

    MOV BP,SP

    SUB SP,2

    MOV WORD PTR [ВР-2],0

    LDS SI,[ВР+10]

    LDS DI,[ВР+14]

LO1:

    MOV AL,DS:[SI]

    CMP AL,0

    JZ ZER1

    MOV DS:[DI],AL

    INC DI

    INC SI

    INC WORD PTR [ВР-2]

    JMP SHORT L01

ZER1:

    LDS SI,[BP+6]

L02:

    MOV AL,DS:[SI]

    CMP AL,0

    JZ ZER2

    MOV DS:[DI],AL

    INC DI

    INC SI

    INC WORD PTR [ВР-2]

    JMP SHORT LO2

ZER2:

    MOV BYTE PTR DS:[DI],0

    MOV AX,[ВР-2]

    ADD SP,2

    POP ВР

    RETF 16

COPY ENDP

PRINT PROC

      PUSH ВР

      MOV ВР,SP

      LDS SI,[BP+б]

LO3:

      MOV DL,DS: [SI]

      CMP DL,0

      JZ ZER3

      MOV AH, 2

      INT 21H

      INC SI

      JMP SHORT L03

ZER3:

      POP BP

      RETF 4

PRINT ENDP

COD ENDS

      END

Модульное программирование