Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
123
Добавлен:
02.05.2014
Размер:
108.54 Кб
Скачать

17

Основы автоматизации вычислительного процесса

Ранее были даны определения алгоритма и программы (см. лекция 1). Алгоритм может быть записан в разных формах: словесной, формульной, табличной, графической или на языке программирования. Не останавливаясь на особенностях форм записи алгоритма, отметим, что языки, представляющие алгоритм в виде последовательности читаемых программистом (не двоично-кодированных) команд, называются алгоритмическими языками. Алгоритмические языки подразделяются на машинно-ориентированные, процедурно-ориентированные, объектно-ориентированные и проблемно-ориентированные.

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

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

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

Трансляторы бывают двух видов: трансляторы-компиляторы и трансляторы-интерпретаторы.

Компиляторы при трансляции переводят на машинный язык сразу всю программу и затем хранят ее в памяти машины в двоичных кодах. Интерпретаторы каждый раз при исполнении программы заново переводят в машинные коды каждую макрокоманду и передают ее для непосредственного выполнения компьютеру. В памяти интерпретируемые программы хранятся в виде исходных макрокоманд и потому в любой момент читаемы человеком. Откомпилированные программы в памяти ЭВМ практически человеком не читаемы. Но их можно вызвать в специальную программу-отладчик (DEBUG и его разновидности), который переведет эту программу на язык Ассемблера и, тем самым, сделает ее "человекочитаемой" (это еще один довод в пользу изучения языка ассемблер).

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

Машинная команда состоит из двух частей: операционной и адресной.

КОП

Адреса

Рисунок 1. Функциональные части машинной команды

Операционная часть команды (КОП - код операции) - это группа разрядов в команде, предназначенная для записи аббревиатуры выполняемого действия.

Адресная часть команды (адреса) - это группа разрядов в команде, в которых записываются чаще всего идентификаторы данных или имена регистров Ml 111, хранящих информацию, или метки машинных команд. Часто эту часть называют адресами операндов, т.е. чисел, участвующих в операции.

По количеству записываемых адресов (обозначаются а1, а2, а3,...) команды делят на безадресные, одно-, двух- и трехадресные.

В трехадресной команде (рис. 2) а1 и а2 - адреса первого и второго чисел, участвующих в операции, а3 - адрес ячейки памяти или регистра, куда следует поместить результат выполнения операции.

КОП

al

а2

а3

Рисунок 2. Типовая структура трехадресной команды

Двухадресная команда имеет структуру, представленную на рисунке 3. В таких командах, как правило, al — адрес ячейки или регистра, где расположено первое число, и куда после завершения операции должен быть записан результат выполнения команды (обратите внимание: при этом "старое" значение первого числа изменяется). Второе участвующее в операции число хранится в а2.

КОП

а1

а2

Рисунок 3. Типовая структура двухадресной команды

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

КОП

а1

Рисунок 4. Типовая структура одноадресной команды

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

Итак, команды представляют краткую нотацию (запись) системы команд.

Синтаксис команд Ассемблера.

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

Команда может включать до 4-х полей следующего вида:

[метка:] мнемокод [операнд] [; комментарий]

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

GETCOUNT: MOV CX.DX ;инициализация счетчика, поместить содержимое регистра DX в регистр СХ

Пример команды с полем мнемокода:

PUSHF

Эта команда сохраняет в стеке содержимое регистра флагов.

Следует отметить, что Ассемблер не различает прописные и строчные буквы.

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

- двоичной, как последовательность цифр 0 и 1 , заканчивающихся буквой В, например, 10111010В;

- десятичной (в привычной десятичной системе счисления с необязательной буквой D на конце), например, 129D или просто 129;

- шестнадцатеричной, как последовательность цифр от 0 до 9 и букв от А до F, заканчивающаяся буквой Н. Первым символом может быть только цифра от 0 до 9, например, ОЕ23Н (в данном случае первая цифра информирует ассемблер о том, что Е23 число, а не идентификатор или

переменная);

- литералом, строка букв, цифр и других символов, заключенная в

кавычки или апострофы.

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

Операнды команды могут задаваться по-разному:

1) неявно на микропроцессорном уровне. Такие команды работают либо с регистрами, либо с флагами и в конкретной команде объект определяется кодом команды, например:

PUSHF - команда пересылки флагов в стек. Источник (откуда берутся

данные) и приемник (куда пересылаются) определяются кодом

команды;

или CLD - обнуление флага направления DF;

2) явно в самой команде (непосредственный операнд), например:

MOV АХ,0;

3) одним из регистров общего назначения (РОН) или сегментным регистром;

4) идентификатором переменной (меткой переменной, описанной в сегменте данных). В этом случае операнд располагается в ОЗУ. При косвенной адресации адрес-смещение переменной в ОЗУ помещается в РОН или индексный регистр и в команде используется ссылка на этот регистр;

5) номером порта ввода-вывода. В этом случае номер задается шестнадцатеричной константой;

6) регистром стека, когда используются команды работы со стеком и в этом случае операнд либо заносится в вершину стека, либо считывается из вершины стека.

Меткой является последовательность букв и цифр, после которой ставится двоеточие. Нельзя использовать в качестве меток имена регистров и мнемокоды, кроме того метка должна начинаться с буквы, но может содержать цифры и спец символы: ?, @, /, _,$ и точку, однако точка может быть только первым символом метки.

Важной особенностью машинных команд является то, что они не могут манипулировать одновременно 2-мя операндами, находящимися в ОЗУ. Это означает, что в команде только 1 операнд может указывать на ячейку ОЗУ, 2-ой д.б. либо регистром, либо непосредственно указывать значение. По этой причине возможны следующие сочетания операндов в команде:

регистр - регистр;

регистр - память;

память - регистр;

регистр - непосредственный операнд;

память - непосредственный операнд.

Для команд характерно, что если в них присутствуют 2 операнда, то 1-ый является приемником, а 2-ой -источником, иначе говоря результат операции сохраняется по 1-ому адресу, вот почему 1-ый операнд никогда не может быть непосредственным операндом или иначе говоря, константой. Таблица 5.1. ПСЕВДООПЕРАТОРЫ ИЛИ ДИРЕКТИВЫ

Псевдооператор

Формат и Функция

1

2

Определения данных

DB

[имя] DB выражение [,…….]

определяет переменную или присваивает ячейке памяти начальное значение. Резервирует 1 или более байт (при наличии запятых)

DW

[имя] DW выражение [,…….]

аналогично предыдущему резервирует двухбайтовые слова

DD

[имя] DD выражение [,…….]

Резервирует 4-х байтовые двойные слова

Псевдооператор

Формат и Функция

1

2

Определения сегмента или процедуры

SEGMENT

Имя_cer SEGMENT [тип_выравнивания (подгонки)] [тип_связи] [‘класс’]

. . . .

Имя_cer ENDS

Определяет границы сегмента программы. Обязательно содержит начало описания Имя_cer SEGMENT и окончание описания Имя_cer ENDS

ASSUME

ASSUME регистр_cer: Имя_cer [,…….]

Или ASSUME регистр_cer: NOTHING

Cообщает Ассемблеру, какой регистр сегмента связан с соответствующим сегментом программы. Оператор ASSUME регистр_cer: NOTHING отменяет действие всех предыдущих операторов ASSUME для данного регистра

PROC

Имя PROC [NEAR] или Имя PROC FAR

. . . .

. . . .

RET

имя ENDP

Присваивает имя последовательности операторов. Должно иметь начало PROC и окончание ENDP

Управление трансляцией

END

END [метка точки входа]

Отмечает конец исходной программы

Внешние ссылки

PUBLIC

PUBLIC идентификатор

Делает определенный ранее идентификатор доступным другим модулям программы, которые впоследствии должны быть присоединены к данному модулю

EXTERN

EXTERN имя: тип [, ….]

Указывает, что имя определено в другом модуле программы

INCLUDE

INCLUDE файл вставляет содержимое указанного файла в текущий файл исходной программы

Псевдооператор

Формат и Функция

1

2

Определение идентификаторов

EQU

Имя EQU текст или

Имя EQU числовое_выражение

Постоянно присваивает идентификатору имя текст или числовое_выражение

Имя = числовое_выражение

Числовое_выражение присваивается идентификатору имя, но может быть переприсвоено

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

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

В директиве SEGMENT тип_выравнивания указывает, на границе байта (BYTE) слова (WORD ), параграфа (PARA) или другой должен быть установлен адрес начала сегмента. Выравнивание на границу параграфа означает, что адрес начала сегмента д.б. кратен 16, слова - кратен 2.

Тип_связи (комбинирования сегментов) указывает, как нужно комбинировать сегменты различных модулей, имеющие одно и то же имя. По умолчанию предполагается PRIVATE, что означает, что сегмент не будет объединяться с сегментами с таким же именем вне данного модуля. PUBLIC заставляет компоновщик объединить все сегменты с одинаковым именем. Объединенный сегмент будет непрерывным, все адреса данных или команд будут определяется относительно начала этого сегмента. COMMON - все сегменты с одним именем располагаются по одному адресу (перекрываются).

'класс' заключенная в кавычки строка символов определяет назначение сегмента и помогает компоновщику определить порядок следования сегментов внутри программы. Формально этот порядок может быть любым, но лучше, если все сегменты кода (содержат команды обработки) объединяются в общую группу. Заключенное в кавычки слово класс может принимать следующие значения: code, stack, data, что означает соответственно сегмент кода (команд программы), сегмент стека и сегмент данных.

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

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

Ниже приводится таблица примеров определения переменных в программе на ассемблере.

Таблица 5.2. Примеры определения переменных.

Имя

Директива определения

значения

комментарий

DB – определение байтов

BYTE1

DB

?

Без указания значения

BYTE2

DB

48

Десятичная константа

BYTE3

DB

30Н

Шестнадцатеричная константа

BYTE4

DB

01111010В

Двоичная константа

BYTE5

DB

10 DUP(0)

Десять нулевых значений

BYTE6

DB

‘Input simbol’

Строка символов

BYTE7

DB

‘12345’

Строка цифровых символов

BYTE8

DB

01, ‘Jan’, 02, ‘Feb’, 03, ‘mar’, 04, . . . .

Таблица месяцев года, состоит из числовых значений номера месяца и его сокращенного названия

DW – определение слов (16 бит=2 байта)

WORD1

DW

0fff0h

Шестнадцатеричная константа

WORD2

DW

01111010В

Двоичная константа

WORD3

DW

BYTE3

Константа адреса переменной

WORD4

DW

2,3,4,5,6

Таблица из 5 констант

WORD5

DW

8 DUP(0)

Восемь нулевых значений

DD – определение двойных слов (32 бита = 4 байта)

DWORD1

DD

?

Без значения

DWORD2

DD

41562

Десятичное значение

DWORD3

DD

48Н, 24Н, СА

3 шестнадцатеричные константы

DWORD4

DD

WORD1

Адрес слова в виде сегмент:смещение

DWORD5

DD

BYTE8-BYTE5

Разность между адресами переменных (количество байт)

Загрузка и выполнение программ на компьютере

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

  1. управление файлами. ОС поддерживает структуру файлов и папок (файловая система) на дисках компьютера. Это физический уровень хранения информации. Прикладные программы создают, изменяют информацию в файлах, но ОС управляет их размещением в дисковой памяти;

  2. ввод/вывод информации. Прикладные программы запрашивают ввод исходных данных у системы и предоставляют результаты обработки системе с помощью прерываний. Низкоуровневые операции ввода/вывода (канальные программы) ОС выполняет без вмешательства программиста;

  3. загрузка программ. Когда пользователь или прикладная программа запрашивают выполнение другой программы, загрузчик (ОС) выполняет последовательность шагов, описываемую ниже, для получения доступа к программе на диске, размещения ее в оперативной памяти и начала выполнения;

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

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

Теперь подробнее о загрузке программ. Включение питания компьютера приводит к переходу процессора в состояние запуска, очистке памяти (записи нулей в ячейки ОЗУ), проверки четности памяти (фактически минимальная адресуемая единица памяти – байт – содержит не 8, а 9 битов; дополнительный бит является битом четности и принимает такое состояние, чтобы в 9 битах было четное количество двоичных единиц). Кроме того процессор загружает в регистр CS значение FFFF[0]H, и смещение 0 в регистр IP, настраивая компьютер на выполнение программы BIOS, находящейся в ПЗУ по адресу FFFF0H. Эта часть BIOS содержит набор процедур, которые проверяют разные части компьютера , чтобы опознать и инициализировать устройства, присоединенные к нему. После этого BIOS создает 2 области данных:

  1. таблицу векторов прерываний, начинающуюся с 0 абсолютного адреса ОЗУ и содержащую 256 4-байтовых адреса программ обработки прерываний (более подробно этот вопрос рассматривается в теме “команды прерывания”);

  2. область данных BIOS, начинающуюся с адреса 40[0]Н. В этой области отображается состояние присоединенных к компьютеру устройств.

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

При выполнении команды прерывания в прикладной программе возможны 2 основных сценария реализации прерывания:

  1. прикладная программа обращается к ОС, которая после анализа команды обращается к BIOS, а уже BIOS непосредственно работает с аппаратурой;

  2. прикладная программа обращается в BIOS напрямую (например, при работе с дисплеем или клавиатурой).

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

Отработав, системные файлы приводят компьютер в состояние ожидания запросов пользователя. Одним из таких запросов является выполнение прикладной программы. Существуют 2 типа исполняемых файлов – СОМ и ЕХЕ. Программы типа СОМ состоят из единственного сегмента, содержащего код, данные и стек. Такие программы создаются для утилит или резидентных программ (программ, остающихся в оперативной памяти при выполнении других программ). На таких программах мы останавливаться не будем. Прикладные программы пользователя, как правило, хранятся в виде ЕХЕ-файлов. Они состоят из отдельных сегментов кода, данных и стека. При вызове на выполнение ЕХЕ-программы загрузчик выполняет следующие шаги:

  1. находит программу на диске;

  2. создает 256-байтовый (100Н) префикс сегмента программы (PSP) в доступной оперативной памяти на границе параграфа ( с начального адреса, кратного 16);

  3. загружает программу в память, непосредственно после PSP;

  4. загружает адрес PSP в регистры DS и ES;

  5. загружает адрес сегмента кода в регистр CS и устанавливает регистр IP в 0 (это состояние соответствует первой инструкции программы, подлежащей выполнению);

  6. загружает адрес стека в регистр SS и записывает в регистр SP значение, равное размеру стека;

  7. передает управление исполняемой программе, конкретно по адресу CS:IP (адрес записан в виде сегмент:смещение).

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

Поясним вопросы адресации инструкций (команд программы) и данных на примерах.

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

Смещение команды

Команда

Комментарий

0000

PUSH DS

поместить в стек номер блока адреса возврата

0001

MOV AX,0

обнулить регистр АХ

0004

PUSH AX

поместить в стек значение адреса возврата=0

0005

MOV AX,DSEG

инициировать адрес сегмента данных

0008

MOV DS,AX

Пусть после загрузки регистр кода CS инициализирован значением 05ВЕ[0]Н.

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

Смещение переменной

Идентификатор (имя) переменной

Описание переменной

Комментарий

0000

CHAR

DB ?

Неинициализированный байт

0001

SOURCE

DB 10,20,30

Вектор десятичных значений

0004

DEST

DW 3 DUP(?)

Зарезервированные слова

000А

MESS

DB ‘Name? ,$’

Текст запроса

0010

REZ

DD ?

Зарезервированное двойное слово

Соседние файлы в папке Лекции по ассемблеру