Данное пособие предназначено для обучения студентов специальности «Информатика и управление в технических системах» по односеместровой дисциплине «Электронно-вычислительные машины и вычислительные системы». Основной целью данного курса является получение студентами знаний и навыков по использованию и построению системных управляющих программ для однопрограммных вычислительных систем.
Вычислительной системой
Алгоритм
аппаратурой
Несмотря на то, что ВС в составе двух названных подсистем (аппаратура и прикладные программы) в принципе пригодна для решения задач по переработке информации, практическое применение такой системы ограничивается лишь простейшими задачами. Это обусловлено тем, что разработка даже простой (в смысле решаемой задачи) прикладной программы на “голой” аппаратуре представляет собой очень трудоёмкий процесс. Уменьшение трудоёмкости прикладного программирования возможно по следующим направлениям:
1) изменение среды выполнения прикладной программы, приводящее к её упрощению;
2) предоставление разработчику прикладной программы возможности разрабатывать не реальную, а виртуальную (кажущуюся) программу.
однопрограммной ВС
В настоящем пособии рассматриваются вопросы организации однопрограммных ВС. Особое внимание в ходе этого рассмотрения уделяется интерфейсам между всеми тремя подсистемами ВС – прикладными программами, системными программами, аппаратурой. Значительное место в пособии занимают также принципы организации аппаратуры и системного программного обеспечения.
В качестве примера реализации аппаратуры и операционной системы для однопрограммной ВС в данном пособии рассматриваются центральный процессор Intel 8086 (сокращенно – i8086) и операционная система MS-DOS. Данный выбор обусловлен, во-первых, тем, что MS-DOS является самой распространенной однопрограммной операционной системой. Во-вторых, эта система разрабатывалась для совместного использования с процессором i8086, который аппаратно имитируется всеми последующими процессорами фирмы Intel. Выбор примера реализации однопрограммной ВС позволяет, во-первых, вести изложение на достаточно конкретном уровне. Во-вторых, многие результаты изложения могут быть перенесены на другие однопрограммные ВС, а в некоторой степени и на мультипрограммные системы.
Для изучения данного пособия требуется наличие первоначальных знаний и навыков по программированию на языке ассемблера для процессора i8086. Для их получения рекомендуются пособия автора по курсу “Информатика” [1, 2], но можно пользоваться и другими доступными учебными пособиями по этому языку программирования.
Принципы разработки алгоритмов и программ для решения прикладных задач
... систем, разнообразных прикладных программ, драйверов устройств, приложений для встраиваемых систем, высокопроизводительных ... Язык СИ :учебн. пособие. - СПб.: БХВ-Петербург, ... по которому, наоборот, выполнение команд циклической части прекращается, после команд циклической части (см. рис.1.23). Рис 1.7. Алгоритм ... Тем не менее, C++ не является в строгом смысле надмножеством C; множество программ, ...
Так как пособие ориентировано, прежде всего, на получение студентами знаний и навыков по разработке управляющих программ, в нем имеется достаточно большое (9) число законченных программ, а также фрагменты программ. Почти все эти программы получены автором пособия и являются рабочими (не считая драйверов для гипотетических устройств).
В завершении данного пособия приведены методические указания по выполнению лабораторных и контрольных работ. Целью выполнения лабораторных работ является получение практических навыков по программированию операций ввода-вывода на уровне аппаратных интерфейсов (на уровне портов).
Программирование ведется на языке ассемблера для процессора i8086 в среде операционной системы MS-DOS. В качестве устройств ввода-вывода используются экран и клавиатура.
Целью выполнения контрольной работы №1 является закрепление основных определений и теоретических положений данного курса. Данная контрольная работа выполняется в диалоге с компьютерной контролирующей программой.
Целью выполнения контрольной работы №2 является развитие навыков программирования на ассемблере задач, описание которых приведено в настоящем пособии.
Изучение курса “ЭВМ и ВС” заканчивается получением зачета и сдачей компьютерного экзамена. Для получения зачета требуется успешно выполнить две лабораторные и две контрольные работы, подтвердив это соответствующими отчетами. Отчеты по всем лабораторным и контрольным работам предоставляются в ТМЦДО в виде файлов на дискете.
При выполнении второй контрольной работы, а также при выполнении первой лабораторной работы используется номер варианта (от 1 до 20).
Этот номер рассчитывается по формуле:
V = (20 x K) div 100,
где V – искомый номер варианта (при V = 0 выбирается номер варианта 20);
- K – значение двух последних цифр пароля (число от 00 до 99);
- div – целочисленное деление (после деления отбрасывается дробная часть).
1.
ФУНКЦИИ ОДНОПРОГРАММНОЙ СИСТЕМЫ
1.1. Виртуальная машина пользователя прикладной программы
интерфейсом
Как видно из рис.1, однопрограммная ВС предоставляет в распоряжение своего пользователя две виртуальные (кажущиеся) ЭВМ:
1) виртуальную машину пользователя прикладной программы;
2) виртуальную машину пользователя для запуска программ.
виртуальной машиной пользователя прикладной программы
Рис.1. Подсистемы ВС
системными обрабатывающими программами
программисты
Виртуальная прикладная программа
Лингвистический процессор
транслятора–ассемблера
ВМ_П_СП – виртуальная машина пользователя системы программирования;
- ВМ_ПП – виртуальная машина прикладной программы.
Рис.2. Виртуальная машина пользователя системы программирования
Интерпретатор
Стандартная подпрограмма
Предоставляя программисту возможность работать с виртуальной машиной, сама система программирования “выполняется” на ВМ, аналогичной той, на которой выполняется прикладная программа (ВМ_ПП).
Это обусловлено тем, что и прикладная программа, и система программирования являются машинными программами. Более того, с точки зрения самой ВС, между ними нет принципиальной разницы, так как и та и другая программы относятся к классу обрабатывающих программ. Рассмотрение ВМ_ПП отложим до следующего раздела, а пока рассмотрим еще одну разновидность системных обрабатывающих программ – утилиты.
Утилиты
1) перенос данных с одного периферийного устройства на другое или перенос данных в пределах одного устройства. Примеры: программа копирования данных на магнитном диске; программа ввода данных с клавиатуры терминала на диск; программа распечатки информации на диске;
2) программы изменения расположения данных. Это различные программы сортировки;
3) программы для изменения представления данных. Сюда относятся
редакторы, которые осуществляют редактирование виртуальных программ и других текстов, а также программы перекодировки данных для согласования программ, использующих разные кодировки, или для обеспечения секретности.
Примером сложной утилиты является Norton Commander. Эта утилита переносит с диска на экран информацию, содержащуюся в любом каталоге любого логического диска. По запросу пользователя она выводит на экран файловую структуру любого логического диска. Кроме того, она предоставляет пользователю удобный язык управления операционной системой MS-DOS за счет того, что она переносит имя исполняемого файла программы из позиции экрана, отмеченной пользователем с помощью псевдокурсора (псевдокурсор – светящийся прямоугольник) в то место памяти, откуда это имя может взять интерпретатор команд ОС.
пользователями–непрограммистами
1.2. Виртуальные машины для запуска программ
Для того чтобы пользователь мог работать на виртуальной машине, предоставляемой прикладной программой (утилитой или системой программирования), эту программу необходимо запустить. Для этого следует выполнить действия:
1) создать исполняемую ВМ_ПП. То есть для прикладной программы должны быть выделены аппаратные ресурсы (время ЦП, пространство ОП, ПУ) в объёме не менее минимально-необходимого для выполнения этой программы. Иными словами, ВМ_ПП должна опираться (отображаться) на реальную аппаратуру, достаточную для выполнения программы;
2) загрузить в эту ВМ требуемую прикладную программу, утилиту или систему программирования;
Инициирование
управления операционной системой
интерпретатором команд
виртуальной машине пользователя для запуска программ
ВМ_П_З – ВМ пользователя для запуска программ
ВМ_П_ПП – ВМ пользователя прикладной программы
ВМ_ПП – ВМ прикладной программы
Рис.3. Виртуальные машины пользователя в однопрограммной ВС
После того, как пользователь запустил прикладную программу (утилиту или систему программирования), он работает на ВМ, предоставляемой данной программой. После завершения прикладной программы пользователь опять оказывается на ВМ_П_З. Подобная смена виртуальных машин происходит до завершения текущего сеанса работы пользователя с ВС.
командными файлами
1.3. Виртуальная машина прикладной программы
Предоставление разработчику прикладной программы возможности разрабатывать не реальную, а виртуальную программу многократно уменьшает трудоемкость разработки. Другим важным направлением снижения трудоемкости прикладного программирования является изменение среды выполнения прикладной программы, приводящее к её упрощению. (Понятно, что чем проще машинная программа, тем проще её разработка.)
виртуальную машину прикладной программы
Системные вызовы Машинные команды
ВМ_ПП предоставляет в распоряжение прикладной программы виртуальные устройства:
1) виртуальный ЦП. Несмотря на то, что в прикладной программе присутствуют только машинные команды реального ЦП, эффект от выполнения некоторых из них многократно усиливается за счёт вызова ими (при их выполнении на ЦП) системных подпрограмм. Другим отличием виртуального ЦП от реального является то, что он обслуживает нашу прикладную программу непрерывно (реальный ЦП может отвлекаться от нашей программы, выполняя другие программы);
2) виртуальная ОП. Объём этой памяти может многократно превосходить объём реальной ОП, предоставляемой в распоряжение программы. Кроме того, способ указания (способ адресации) ячеек виртуальной ОП обычно отличается от соответствующего способа для реальной ОП;
3) виртуальная ВП. Такая память имеет структуру, существенно отличную от структуры реальной ВП. Благодаря этому пользоваться виртуальной ВП намного удобнее;
4) виртуальные ПУ. Если наша программа работает с периферийным устройством путём вызова соответствующей системной подпрограммы, то эта подпрограмма и представляет для программы виртуальное ПУ.
системными вызовами
1) вызовы BIOS;
2) вызовы операционной системы (ОС).
Рис.5. Уточнение виртуальной машины прикладной программы
Подпрограммы BIOS (базовая система ввода-вывода) являются своеобразным “продолжением аппаратуры”. Они ”зашиты” в постоянное запоминающее устройство (ПЗУ) и могут выполняться сразу же после включения питания машины. Это свойство широко используется: одной из подпрограмм BIOS является начальный загрузчик, инициирующий программное обеспечение ВС. Другие подпрограммы BIOS могут использоваться в процессе своего выполнения прикладными и системными программами для работы с ПУ.
1.4. Структура аппаратных средств
Обязательной подсистемой любой ВС является аппаратура. В однопрограммной системе возможно применение одной из следующих структур организации аппаратных средств:
1) с общим ЦП;
2) с общей ОП;
3) с общей шиной.
общая шина
ОШ
Рис. 6. Структура аппаратуры ВС с общей шиной
Центральный процессор
КОП |
Операнд 1 |
Операнд 2 |
Рис.7. Структура машинной команды
Оперативная память
Периферийные устройства ( ПУ)
|
ОП
Рис. 8. Структура ОП
Устройство внешней памяти
1) обмен информацией между ЦП и ВП выполняется во много раз медленнее, чем между ЦП и ОП;
2) ЦП не может выполнять команды, записанные в ВП. Для выполнения этих команд их необходимо предварительно переписать в ОП;
3) информация на носителе ВП сохраняется и после выключения питания.
В недавнем прошлом емкость носителей ВП многократно превосходила емкость ОП. В настоящее время это выполняется не для всех типов носителей.
Интерфейсное устройство
2. ЦЕНТРАЛЬНЫЙ ПРОЦЕССОР
2.1. Архитектура процессора i8086
В данном разделе рассматривается применение в качестве ЦП микропроцессора i8086. Данный процессор обеспечивает выполнение программ в однопрограммной ВС, обладая всеми необходимыми для этого ресурсами. Знакомство с этими ресурсами начнем с рассмотрения архитектуры (структуры) процессора (рис.9).
Адрес/данные Управление 2
(20 контактов) (16 контактов)
+5 в Земля Синхр-я
ОШ
Рис. 9. Структура микропроцессора i8086
логика управления
Регистры данных
Очередь команд
указатели
базовый регистр ВР
Регистр флагов FLAGS
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
OF |
DF |
IF |
TF |
SF |
ZF |
AF |
PF |
CF |
Рис.10. Регистр флагов микропроцессора i8086
Условные флажки
1) SF – флажок знака . Равен старшему биту результата. Т.к. в дополнительном коде старший бит отрицательных чисел содержит 1, а у положительных он равен 0, то SF показывает знак предыдущего результата;
2) ZF – флаг нуля . Устанавливается в 1 при получении нулевого результата и сбрасывается в 0, если результат не равен 0;
3) PF – флажок паритета . Устанавливается в 1, если младшие 8 битов результата содержат четное число единиц, в противном случае он сбрасывается в 0;
4) CF – флажок переноса . При сложении (вычитании) устанавливается в 1, если возникает перенос (заем) в старший бит (из старшего бита).
Обычно данный флаг используется не по прямому назначению, а как признак возврата из подпрограммы: если подпрограмма (процедура или обработчик прерываний) завершилась успешно, то она возвращает CF=0, а если с ошибкой, то CF=1;
5) AF – флажок вспомогательного переноса . Устанавливается в 1, если при сложении (вычитании) возникает перенос (заем) из бита 3. Флаг предназначен только для двоично-десятичной арифметики;
6) OF – флажок переполнения . Устанавливается в 1, если знаковый бит изменился в той ситуации, когда этого не должно было произойти.
Пусть, например, машинная команда ADD (здесь и везде далее используются ассемблерные мнемоники машинных команд) выполнила следующее сложение:
0010 0011 0100 0101
+ 0011 0010 0001 1001
0101 0101 0101 1110
тогда после ее выполнения получаются состояния флажков:
SF = 0, ZF = 0, PF = 0, CF = 0, AF = 0, OF = 0.
Если ADD выполнила сложение:
0101 0100 0011 1001
+ 0100 0101 0110 1010
1001 1001 1010 0011
то флажки принимают состояния:
SF = 1, ZF = 0, PF = 1, CF = 0, AF = 1, OF = 1.
Флажки управления
1) DF – флажок направления . Он используется при выполнении команд, обрабатывающих цепочки – последовательности ячеек памяти. Если флаг сброшен, цепочка обрабатывается с первого элемента, имеющего наименьший адрес. Иначе цепочка обрабатывается от наибольшего адреса к наименьшему;
2) IF – флажок разрешения прерываний . Когда установлен этот флажок, ЦП выполняет маскируемые прерывания. Иначе эти прерывания игнорируются;
2) TF – флажок трассировки . Если этот флажок установлен, то после выполнения каждой машинной команды, ЦП генерирует внутреннее аппаратное прерывание (прерывание номер 1).
Дескриптор
Последний блок ЦП образуют сегментные регистры CS, SS, DS и ES. Данные регистры используются для адресации ячеек ОП.
2.2. Адресация памяти
Применение сегментных регистров вызвано стремлением расширить объем программно адресуемого пространства ОП. Дело в том, что 20-и проводная шина адреса (входит в состав ОШ) позволяет адресовать до 1 млн ячеек (байтов) ОП, так как 2 20 » 1 млн. Но все регистры в ЦП 16-битные. Ни одного 20-битного нет. Максимальный объем памяти, который можно адресовать с помощью 16 бит, составляет 64К (1К = 1024).
Рассмотрим как получаются 20-битные адреса.
параграфами
Адрес байта (16) ОП
00000
00010
Первый
00020 сегмент
» » Второй
сегмент
10000
Третий
сегмент
10010
10020
Рис. 11. Разбиение ОП на параграфы
Указатель команды IP содержит относительный адрес (смещение) адресуемой ячейки относительно начала сегмента. Допустим, что это число 100h. Физический адрес R адресуемой ячейки ЦП получает непосредственно перед обращением к ОП путем суммирования содержимого регистра CS, умноженного на 16 (10h), с содержимым регистра IP:
R = (CS) x 10h + (IP).
Например, пусть (CS) = 0002h, а (IP) = 0100h, тогда R = 2h x 10h + +100h = 120h (рис.12).
4 бита
IP
(16 бит)
CS
(16 бит)
20-битная шина
|
адреса содержит
физич. адрес
Рис.12. Получение физического адреса
В пределах текущего сегмента IP может обращаться к любой ячейке ОП, имеющей смещение относительно начала сегмента 0 ¸2 16 -1, т.е. 0 ¸ 65535. Число 216 = 65536 = 10000h = 64K называется длиной сегмента.
Имея в своем распоряжении четыре сегментных регистра, программа (а точнее – выполняющий ее ЦП) использует их по разному: CS – для адресации машинных команд, SS – для адресации ячеек стека, DS – для основной адресации данных, ES – для дополнительной адресации данных. Поэтому в любой момент времени любая машинная программа имеет в своем распоряжении четыре логических сегмента ОП размером 64K. Каждый из этих логических сегментов играет строго определенную роль при выполнении команд машинной программы:
сегмент кода
сегмент данных
дополнительный сегмент данных
сегмент стека
Перечисленные логические сегменты как бы «высвечиваются» ЦП из одно-мегабайтового адресного пространства ОП. Причем они могут пересекаться друг с другом, или вообще совпадать. Выполняя запись в сегментные регистры (например, с помощью команды MOV) нового содержимого, программа выполняет замену «высвечиваемых» сегментов.
Для того, чтобы можно было обращаться к любой ячейке логического сегмента, в распоряжении программы имеется регистр, содержащий смещение искомой ячейки относительно начала сегмента. Это:
1) для сегмента кода – IP;
2) для сегмента данных – BX, SI, DI;
3) для дополнительного сегмента данных – BX, DI;
4) для сегмента стека – SP.
Таким образом, логический адрес любой ячейки ОП представляет собой пару:
(S, L) = ((регистр сегмента) , (регистр смещения)),
где S – начальный адрес сегмента (номер параграфа);
- L – смещение ячейки относительно начала сегмента;
- (регистр) – содержимое регистра.
адресом-сегментом
динамическим преобразованием адреса
2.3. Алгоритм работы процессора
На рис.13 приведен алгоритм выполнения машинных команд в ЦП. Он представляет собой упрощенный вариант работы реального процессора i8086, в котором некоторые этапы выполняются во времени не последовательно, а параллельно.
Данный алгоритм представляет собой бесконечный цикл, который инициируется сразу же после включения питания. На одной итерации алгоритма ЦП выполняется одна машинная команда. Это выполнение начинается с определения реального адреса команды в ОП. Этот адрес выдается из ЦП на шину адреса. Получив его, ОП помещает следующую команду на шину данных, и ЦП вводит команду в очередь команд. Пока дешифрируется эта команда — определяется ее длина в байтах и производится увеличение содержимого IP на эту длину, в результате чего IР адресует следующую машинную команду. После этого цикл повторяется.
Последовательная выборка команд из памяти и их выполнение продолжаются до тех пор, пока очередной командой, поступившей из ОП на ЦП, не окажется команда перехода. Команды перехода позволяют изменить естественный порядок следования машинных команд. Они делятся на команды безусловного и команды условного перехода.
Команды безусловного перехода
Команда близкого перехода
да нет
да
нет
нет
да
Рис.13. Алгоритм работы ЦП
Команда дальнего перехода
Кроме команд JMP безусловный переход выполняют команды CALL, RET, INT, IRET. Эти команды мы рассмотрим чуть позже, а пока лишь отметим, что мнемоникам CALL и RET соответствуют по две машинные команды, одна из которых выполняет близкий, а другая – дальний безусловный переход. Команды INT и IRET выполняют только дальние переходы.
Команды условных переходов
2.4. Работа со стеком
стеком
Для небольших программ, получаемых в результате загрузки com-файлов стек программы создается автоматически, без какого-либо участия программиста. В этом случае данные стека располагаются в том же самом сегменте памяти объемом 64К, что и код программы, а также ее данные.
Допустим, что для нашей программы ОС выделила область (сегмент) ОП начиная с параграфа N 20h (рис.14).
Регистр CS содержит число 20h и представляет собой указатель на начало этой области. Одновременно с назначением области ОП для размещения программы ОС назначает сегмент для размещения стека, который будет обслуживать нашу программу. Специальный регистр SS , называемый регистром сегмента стека , является указателем на начало этой области. Так как для размещения стека ОС выделяет ту же область, что и для команд программы, то первоначальное содержимое SS совпадает с содержимым CS.
Реал.адрес (16) ОП
|
» »
CS, SS
00200
|
Команды
программы
IP
SP
Стек программы
10200
» »
Так как запись команд программы в сегмент памяти происходит с его начала, то данные стека должны находиться в конце сегмента. Для лучшего использования памяти стек “растет” в сторону меньших адресов. Смещение вершины относительно начала сегмента хранится в регистре SP , который называется указателем стека . Первоначально операционная система помещает в SP максимально возможное число без знака, т.е. FFFF или чуть меньшее число. В результате SP указывает на «дно» стека. При добавлении слова данных в стек содержимое SP уменьшается на 2, а при исключении слова из стека – увеличивается на 2.
Если программа будет получена в результате загрузки exe-файла, то программист обязан сам задать в виртуальной программе на ассемблере предельный размер стека программы. Область этого объема будет зарезервирована транслятором исключительно для размещения стека программы. «Дно» стека будет находиться на старшей границе этой области, а «расти» стек будет в сторону младшей границы.
Коль скоро программа обязательно имеет свой индивидуальный стек, то она может использовать его в качестве особой области памяти для своих внутренних потребностей. Особенностью стека, присущей ему по определению, является то, что над ним допустимы только две операции: 1) добавление слова в вершину стека; 2) выборка слова из вершины стека. Несмотря на то, что никто не мешает выполнять программе операции с внутренними ячейками стека (используя базовый регистр ВР), этим следует пользоваться осторожно и только при необходимости.
Для выполнения операций со стеком в программе используются две специальные команды. Включение слова данных в стек выполняет машинная команда PUSH a, а исключение слова из стека выполняет команда РОР a, где a — адрес ячейки памяти (ОП или регистровой памяти), содержимое которой следует включить в стек, или в которую следует считать слово данных из стека.
Многоцелевое использование одного и того же стека аппаратурой ЦП, операционной системой и прикладной программой делает необходимым повышенное внимание при работе с ним: каждое записанное в стек слово должно быть вовремя извлечено оттуда.
2.5. Процедуры
подпрограммами
1) существенное уменьшение трудоемкости программирования за счет возможности выполнять разработку программы не целиком, а по частям;
2) существенное сокращение памяти для размещения программы, так как выделив в подпрограмму многократно повторяющийся участок кода программы, достаточно выделить память лишь для одной подпрограммы, инициируя ее из различных мест программы;
3) уменьшение трудоемкости программирования за счет того, что одну и ту же подпрограмму можно использовать не в одной, а в нескольких программах. (Данное преимущество обсуждалось ранее при рассмотрении стандартных подпрограмм.)
Подпрограммы бывают двух типов: процедуры и обработчики программных прерываний. Процедуры рассматриваются в данном параграфе, а обработчики прерываний – в следующем.
Процедура
Процедура
a
Рис.15. Вызов процедуры и возврат из нее
Подобно команде JMP, команды CALL и RET выполняют или близкие переходы, когда вызывающая программа и процедура находятся в одном сегменте кода, или дальние переходы — программа и процедура находятся в разных сегментах. В первом случае адрес перехода a представляет собой единственное число – смещение первой команды процедуры относительно начала сегмента (новое значение регистра IP).
Во втором случае это пара чисел: (CS, IP).
При вызове процедуры и возврате из нее необходимо выполнить следующие три требования:
1) при вызове процедуры необходимо запомнить адрес следующей команды, чтобы можно было впоследствии осуществить возврат в нужное место вызывающей программы;
2) используемые процедурой регистры необходимо запомнить до изменения их содержимого, а перед самым выходом из процедуры — восстановить;
3) процедура должна иметь возможность выполнять обмен данными с вызывающей ее программой.
Первое требование реализуют команды CALL и RET. При близком вызове команда CALL помещает адрес следующей за ней команды (находится в IP) в программный стек, а RET извлекает этот адрес из стека и помещает его в указатель команды IP. При дальнем вызове CALL помещает в стек, а RET извлекает оттуда не одно, а два слова. – содержимое регистров CS и IP.
Свойство стека «пришел первым, ушел последним» идеально подходит для реализации вложенных вызовов процедур (рис.16), так как оно обеспечивает размещение в стеке адреса возврата вызывающей процедуры раньше, а его выборку позже, чем соответствующие операции с адресом возврата вызываемой процедуры.
Основная программа a 1 Процедура 1
|
a 2
Требование восстановления содержимого регистров, используемых в вызывающей программе, также выполняется с помощью стека. В начале процедуры ее команды PUSH помещают в стек содержимое запоминаемых регистров, а в конце ее команды РОР извлекают из стека запомненные слова и помещают их в регистры. Порядок регистров при восстановлении обратный их порядку при запоминании.
входными параметрами
1)
2) программный стек;
3) другие области ОП.
Если передаваемых данных немного, то достаточно регистров данных, большее количество данных может быть передано с помощью стека. Если же этих данных слишком много, то они помещаются в область ОП, а начальный адрес этой области записывается или в регистр данных, или в стек.
виртуальные программные процедуры
логической процедуры
2.6. Прерывания
2.6.1. Внешние аппаратные прерывания
Прерыванием
Допустим, что на ЦП выполняется прикладная программа, вошедшая в бесконечный цикл вследствие ошибки программирования. Тогда для прекращения выполнения этой программы достаточно нажать ту комбинацию клавиш, которая специально предназначена для этой цели в используемой ВС. Например, в MS-DOS это одновременное нажатие клавиш <Ctrl> и <C>. Следствием этого является выдача интерфейсным устройством клавиатуры аппаратного сигнала прерывания, который поступает в ЦП по ОШ. Процессор прерывает выполнение зациклившейся программы и начинает выполнять обработчик прерываний клавиатуры. Эта подпрограмма распознает код нажатой комбинации клавиш (<Ctrl>&<C>
- и обеспечивает завершение программы. (Обработчик прерываний клавиатуры прекращает выполнение текущей программы не сам, а использует для этого программное прерывание INT 23h.)
Таймер
внешних аппаратных прерываний
реальное время
Немаскируемые прерывания
2.6.2. Исключения
внутренние аппаратные прерывания
отладчиком
В завершение примера отметим, что в i8086 отсутствуют специальные команды для установки и сброса флага TF. Поэтому для выполнения требуемого действия отладчик должен: 1) с помощью команды PUSHF записать (скопировать) содержимое FLAGS в стек; 2) скорректировать слово в вершине стека, установив или сбросив бит 8; 3) с помощью команды POPF выбрать слово из вершины стека и поместить его в регистр FLAGS.
2.6.3. Программные прерывания
программные прерывания
Более того, следует четко представлять себе, что в однопрограммной ВС не существует иного способа вызова из прикладной программы системных управляющих подпрограмм (ОС или BIOS), кроме применения команды программного прерывания INT.
диспетчер функций
Как и процедура, обработчик программного прерывания имеет входные и (или) выходные параметры. Для их передачи используются регистры данных и, возможно, стек программы. Использование стека менее предпочтительно, так как входные параметры будут в нем «пригружены» адресом возврата и содержимым регистра FLAGS. А выходные параметры должны быть «пригружены» этими же данными самим обработчиком, так как иначе адрес возврата не будет найден. Несмотря на то, что перечисленные трудности устраняются программой с помощью простых команд PUSH и POP, на их выполнение требуются дополнительные затраты времени ЦП.
Команды программного прерывания INT могут применяться не только для реализации системных вызовов, но и для реализации вызова прикладных подпрограмм. Реализация прикладной подпрограммы не в виде процедуры, а в виде обработчика прерываний целесообразна только тогда, когда эта подпрограмма вызывается не из одной, а из нескольких прикладных программ. Только в этом случае удается сократить размер прикладных программ за счет того, что в их код не будет включен код подпрограммы. (При использовании процедуры, наоборот, ее код должен быть включен в код каждой программы, использующей ее.) Реализация подобного вызова подпрограмм требует, чтобы они были резидентны, то есть постоянно находились бы в ОП. В противном случае выполнение каждой прикладной программы, использующей подпрограмму, должно было бы предваряться инициализацией соответствующего обработчика прерываний, что очень неудобно. Вопросы разработки резидентных программ будут рассмотрены в п.3. Там же будет приведен пример обработчика программных прерываний.
Наряду с процедурами обработчики программных прерываний относятся к подпрограммам. Поэтому команда вызова такой подпрограммы INT очень похожа на команду дальнего вызова процедуры CALL, а команда завершения обработчика прерываний IRET – на команду возврата из дальней процедуры – RET. Работа данных команд будет рассмотрена далее совместно с другими этапами общего алгоритма обработки прерываний.
2.6.4. Алгоритм выполнения прерывания
номер прерывания
00h — Деление на нуль
01h — Трассировка
02h — Немаскируемое прерывание
08h — Таймер
09h — Клавиатура
10h-1Fh Прерывания BIOS
20h-3Fh Прерывания MS-DOS
таблица векторов прерываний
Адрес (16) ОП
00000 вектор 0
00004 вектор 1
00008 вектор 2
|
|
4*N
вектор N
4*N+4
- kkk……..ююююююююююююю
003FC
вектор 255
00400
Рис.17. Таблица векторов прерываний
Обработка прерывания производится совместными усилиями аппаратуры ЦП и программы обработчика прерываний. Общий алгоритм обработки любого типа прерываний:
1) поступление сигнала прерывания в ЦП. При этом сигнал маскируемого внешнего аппаратного прерывания поступает на вход INTR процессора, сигнал немаскируемого внешнего аппаратного прерывания — на вход NMI, а сигнал исключения или программного прерывания поступает на внутреннюю линию процессора;
2) собственно прерывание. Оно выполняется ЦП по окончанию выполнения текущей машинной команды и включает действия:
- текущее содержимое FLAGS, CS и IP помещается в стек (рис.18).
Последние два слова представляют собой адрес возврата в прерванную программу;
- считывание с ОШ или с внутренней шины процессора 8-битного числа – номера прерывания N;
- копирование вектора прерываний с номером N в регистры IP и CS. В результате эти регистры содержат стартовый адрес обработчика прерываний;
- сбрасываются в нуль флажки IF и TF в регистре FLAGS. В результате запрещаются все маскируемые внешние прерывания (кроме того, которое уже обрабатывается), а также запрещаются исключения «трассировка»;
- Рис.18. Перемещение содержимого регистров при прерывании с номером N
3) начальный этап программной обработки прерывания. Он выполняется обработчиком прерываний и включает действия:
- разрешение маскируемых прерываний с помощью команды STI. В результате важные маскируемые прерывания, например от таймера, не будут более откладываться и вызовут прерывание текущего обработчика прерываний точно так, как прерывается обыкновенная программа;
- сохранение в программном стеке содержимого тех регистров, с которыми будет работать программа обработчика;
- запись в сегментный регистр данных DS значения, которое соответствует адресу-сегменту данных обработчика прерываний.
Это выполняется потому, что в результате прерывания из всех сегментных регистров только CS «переключается» на обработчик прерываний, а остальные сегментные регистры по-прежнему адресуют данные прерванной программы;
4) действия, определяемые типом прерывания. Эти действия обработчик прерываний выполняет сам, или он обращается за помощью к другим подпрограммам ОС, вызывая их как обычные процедуры командами CALL или используя для их вызова команды программного прерывания INT;
5) завершение обработки прерывания. Сюда относятся действия:
- восстановление содержимого регистров, запомненного ранее в стеке;
— возврат из прерывания. Его выполняет команда IRET, завершающая программу обработчика прерываний. При попадании этой команды на ЦП его аппаратура выталкивает из стека в регистры IP, CS и FLAGS прежнее их содержимое. В результате следующей выполняемой на ЦП командой будет очередная команда прерванной программы.
В дальнейшем мы не раз вернемся к данному алгоритму, используя его для написания программ обработчиков прерываний, а также для получения алгоритма выполнения маскируемых внешних прерываний (применение дополнительной аппаратуры потребует уточнения некоторых этапов рассмотренного алгоритма).
В заключение заметим, что среди трех типов прерываний (программные, исключения, внешние аппаратные) программные прерывания — наименее, а внешние аппаратные – наиболее приоритетные прерывания. Это означает, что обработчики программных прерываний могут прерываться сигналами исключений и сигналами внешних прерываний так, как прерываются обыкновенные прикладные программы. Обработчики исключений, в свою очередь, могут прерываться сигналами внешних аппаратных прерываний. Что касается обработчиков внешних прерываний, то, как будет показано в п.5.3, они могут прерываться сигналами других, более приоритетных внешних прерываний. (Чтобы такие прерывания были действительно возможны, прерываемые обработчики прерываний должны предварительно выполнить команду STI.)
3. ВЫПОЛНЕНИЕ ПРИКЛАДНЫХ ПРОГРАММ В СРЕДЕ MS-DOS
3.1. Получение прикладной программы
среды программирования
Исходный Объектные Загрузочный Машин.
модуль модули модуль прог-ма
Библиотеки
- управляющее воздействие
- передача данных
Рис.19. Общая схема создания и выполнения программы
исходной
объектный программный модуль
Адрес
внешние
Внешние выходные
При получении объектного модуля вместо внутренних меток и внешних выходных меток транслятор проставляет в машинные команды, использующие соответствующие адреса, или смещение относительно текущего содержимого указателя команд IР, или смещение относительно начала сегмента ОП, в котором находится соответствующий программный объект. В первом из этих вариантов расчет смещения относительно начала сегмента памяти производится в момент выполнения данной машинной команды на ЦП (см. п.2.2).
Что касается внешних входных меток, то обработать их транслятор не может. Он ничего не знает о размещении соответствующих программных объектов в памяти, так как имеет в своем распоряжении единственный исходный модуль, в котором этих объектов нет. Дальнейшее преобразование программы выполняет системная программа, называемая редактором связей.
Редактор связей (компоновщик)
В операционной системе MS-DOS допускается существование двух типов загрузочных модулей – типа com и типа exe (тип модуля совпадает с расширением имени файла, содержащего загрузочный модуль).
Загрузочный модуль типа com применяется для создания небольших машинных программ (объемом не более 64K), а типа exe – для создания любых программ. Основное различие между этими загрузочными модулями заключается в том, что com–файл содержит готовую машинную программу, не требующую “доводки” при загрузке в память, а exe–файл содержит “заготовку” машинной программы, а также служебную информацию, используемую загрузчиком для настройки программы.
загрузчик
Настройка
3.2. Структура прикладной программы
3.2.1. Префикс программного сегмента
префиксом программного сегмента
Рис.20. Структура PSP
Для наглядного знакомства со структурой PSP удобно использовать отладчик Debug. Допустим, что мы ранее получили загрузочный модуль программы с именем Prob.com. Тогда для анализа PSP вызовем Debug следующей командой MS-DOS:
Debug Prob.com parametr,
где вместо слова parametr может быть записана любая символьная строка (в коде ASCII), содержащая входные параметры нашей прикладной программы. Эти параметры совершенно не интересуют ни Debug, ни тем более MS-DOS, а используются лишь для передачи в программу какой-то исходной информации. (Кстати, если рассматривать в качестве программы сам Debug, то в качестве его параметра выступает имя исследуемой программы, то есть Prob.com.)
После того как мы запустили Debug описанным выше способом, с помощью команды Debug – d 0 получим на экране дамп начальной части PSP. В результате на экран будут выведены первые 128 байтов PSP. Для вывода на экран каждых следующих 128 байтов достаточно просто ввести команду d.
В первых двух байтах PSP находится код машинной команды INT 20h. Чтобы убедиться в этом, введем команду Debug – u 0 . Команда программного прерывания выполняет возврат в ОС при завершении программы. Сама же эта команда получает управление следующим образом: во-первых, сразу после загрузки программы в память загрузчик помещает в ее стек 16-битное нулевое слово (0000h).
Во-вторых, если в конце главной подпрограммы прикладной программы стоит команда RET, а тип главной подпрограммы – NEAR, то при попадании RET на ЦП в качестве адреса возврата из стека будет выбран 0 и, следовательно, следующей исполняемой командой будет INT 20h. Следует отметить, что это не единственный способ возвращения из программы в ОС. Например, вместо RET в главной подпрограмме можно записать оператор INT 20h. В этом случае управление из программы возвращается в ОС непосредственно, минуя PSP. Другой способ возврата – запись в конце главной подпрограммы команды INT 21h (функция 4Ch).
Этот способ является наиболее предпочтительным, так как он позволяет возвращать из программы в ОС код возврата (в регистре AL), информирующий операционную систему о причине завершения программы.
В следующих двух байтах PSP находится верхняя граница (адрес-сегмент) ОП. Обычно она равна величине A000h. Мы еще вернемся к этой величине, когда в дальнейшем будем рассматривать распределение ОП.
В пяти байтах PSP, начиная с 05h, находится вызов диспетчера функций MS-DOS. Данный вызов представляет собой команду дальнего вызова процедуры. С помощью команды Debug – u 5 мы можем получить на экране, например, следующее:
CALL F01D:FEF0 ,
где F01D – адрес-сегмент, а FEF0 – адрес-смещение первой команды диспетчера функций MS-DOS. (Соответствующий реальный адрес интересен тем, что он находится за пределами первого мегабайта. Мы вернемся к нему в вопросе о распределении памяти.)
Для того чтобы обратиться из программы к MS-DOS (с целью получения помощи), достаточно поместить в нее команду CALL 5. В результате следующей командой, исполняемой на ЦП, будет команда вызова диспетчера функций. (Данный способ обращения к MS-DOS менее предпочтителен по сравнению с командой программного прерывания INT 21h.)
В двенадцати байтах PSP, начиная с 0Ah, содержатся копии векторов прерываний с номерами 22h, 23h, 24h. Эти прерывания MS-DOS использует для управления выполнением программы. При этом прерывание 22h используется для вызова той подпрограммы, которая должна получить управление при завершении прикладной программы. Прерывание 23h происходит при нажатии клавиш <Ctrl>&<C>. (MS-DOS использует для обработки этой комбинации специальный обработчик.) Причинами прерывания 24h являются аппаратные ошибки при работе с ПУ. Некоторые из этих ошибок: 1) попытка записи на защищенный диск; 2) нет бумаги на принтере; 3) неизвестное устройство. Хранение копий векторов перечисленных прерываний обусловлено тем, что в случае, если прикладная программа изменяет содержимое векторов (с целью выполнить свою обработку прерываний), то после ее завершения MS-DOS восстанавливает прежнее содержимое векторов, используя копии из PSP.
По адресу 16h находится адрес-сегмент PSP родительской программы. Назначение этого поля будет понятно из п.3.4. Следующее поле PSP начинается по адресу 18h и имеет длину 20 байтов. В нем размещена таблица логических номеров файлов, которая будет рассмотрена
Блок окружения
<имя переменной> = <первое значение>; … ;<последнее значение>00h
После последней переменной окружения записывается не один, а два нулевых байта (0000h).
Пример переменной окружения:
- PATH = C:\WINDOWS; C:\WINDOWS\COMMAND00h
Данная переменная задает те каталоги (директории), в которых могут находиться используемые программой файлы. В том случае, если на вход программы поступает не имя-путь файла, а лишь завершающая часть этого имени, то для получения имени-пути программа должна последовательно просмотреть заданные значения переменной окружения PATH. В случае обнаружения в очередном каталоге искомого файла имя-путь каталога соединяется с именем файла в единое имя-путь файла.
Допустим, что с помощью команды Debug – d 0 мы прочитали значение адреса-сегмента блока окружения, равное 2065. Тогда для получения на экране содержимого блока окружения следует воспользоваться командой d 2065:0 . Первоначальное содержимое своего блока окружения программа получает «в наследство» от той программы, которая создает данную программу, используя системный вызов EXEC (INT 21h, функция 4Bh).
Данный вызов будет рассматриваться позже. А сейчас лишь заметим, что программа может не только читать свои переменные окружения, но и вносить в них изменения.
По адресу 5Ch находятся два блока управления файлами, используемые при работе с линейными файловыми структурами. Так как эти структуры сейчас полностью вытеснены иерархическими файловыми структурами, соответствующее поле PSP может использоваться для хранения любой другой информации. Например, учитывая, что, подобно блоку окружения данные блоки управления передаются «по наследству», родительская программа может помещать в них исходные данные для дочерней программы. Другой способ передачи исходных данных в программу заключается в использовании «хвоста» команды.
Хвост команды
Пример. Следующая простая com-программа выводит на экран свои параметры (хвост команды).
;
; Программа выводит на экран свои параметры
; ————————————————————
;
ASSUME CS:_Text
_Text SEGMENT PUBLIC ‘CODE’
ORG 80h ; Следующий байт имеет адрес-смещение 80h
Lparam DB ? ; Длина хвоста команды
Param DB ? ; Первый байт хвоста
ORG 100h
Start: XOR CX, CX ; Обнуление CX
MOV CL, Lparam ; CL ß Длина хвоста
CMP CX, 0 ; Хвост отсутствует ?
JZ Exit ; Если да
MOV AH, 0Eh ; 0Eh – функция вывода символа
MOV BH, 0 ; Нулевая страница экрана
XOR SI, SI ; SI ß 0
Next: MOV AL, Param[SI] ; Вывод следующего
INT 10h ; символа
INC SI ;
- LOOP Next ; Повторить для след. символа
Exit: MOV AX, 4C00h ; Возврат в DOS с
INT 21h ; кодом завершения 0
_Text ENDS
END Start
В данной программе для вывода символа на экран используется системный вызов BIOS – INT 10h (функция 0Eh ).
Предварительно программа помещает код выводимого символа в регистр AL.
3.2.2. Программа типа com
Такая программа получается в результате загрузки com-файла, которая сводится к простому переписыванию содержимого этого файла с диска в ОП.
На рис.21 приведено содержимое сегментных регистров и регистров-указателей IP и SP после загрузки com-программы в момент передачи ей управления. Собственно передача управления заключается в записи в CS и IP тех значений, которые соответствуют первой исполняемой команде программы. В этот момент все сегментные регистры содержат одно и тоже – начальный адрес (а точнее – номер начального параграфа) области ОП, в которую загружена программа. Так как программа начинается с PSP, то следовательно, номер начального параграфа PSP и содержится в сегментных регистрах CS, DS, ES и SS.
Рис.21. Результат загрузки com-программы
Первоначальное содержимое указателя команд IP задает адрес-смещение первой исполняемой команды программы. Для com-программ это всегда число 100h, так как первая исполняемая команда начинается сразу же после завершения PSP. Что касается указателя стека SP, то он содержит адрес-смещение вершины стека, в который при загрузке программы уже было помещено одно нулевое слово. По мере того, как в стек будут записываться новые слова данных, его вершина будет приближаться к области памяти, занимаемой командами и данными программы. В случае наложения стека на другие данные программы дальнейшее ее продолжение будет или ошибочным, или вообще невозможным.
Исходная программа на ассемблере, ориентированная на получение com-программы, имеет следующие особенности:
1) первому исполнительному оператору программы предшествует псевдооператор ORG 100h , который обеспечивает требуемое смещение соответствующей машинной команды относительно начала программы;
2) операнд псевдооператора END в конце исходной программы в качестве точки входа задает первый исполнительный оператор программы;
3) исходная программа не имеет виртуального сегмента стека. Отсутствие такого сегмента не означает, что в машинной программе не будет стека (любая машинная программа обязательно имеет стек), а лишь говорит о том, что стек будет создан автоматически, то есть без участия программиста;
4) исходная программа не должна содержать исполнительных операторов, выполняющих действия с сегментными адресами. (Такие адреса должны настраиваться загрузчиком в зависимости от размещения машинной программы в ОП, а настройка сом-файла не производится.) Например, недопустимы следующие операторы:
- MOV AX, _Data ; _Data – сегмент данных
MOV SI, SEG Buff ; SI ß адрес-сегмент переменной Buff
Пример исходной программы, ориентированной на получение загрузочного модуля типа com:
; Программа выводит сообщение “Здравствуйте” на экран
; ————————————————————————-
;
- cr EQU 0Dh ; Код ASCII возврата каретки
lf EQU 0Ah ; Код ASCII перевода строки
ASSUME CS:_Text
_Text SEGMENT PUBLIC ‘CODE’
ORG 100h
Start: JMP Begin ; Переход через данные
Msg DB ‘Здравствуйте’, cr, lf, ‘$’ ; Выводимое сообщение
Begin: MOV DX, OFFSET Msg ; DX ß адрес сообщения
MOV AH, 9 ; Вывод строки
INT 21h ; на экран
MOV AX, 4C00h ; Возврат в DOS с
INT 21h ; кодом завершения 0
_Text ENDS
END Start
Эта исходная программа включает единственный виртуальный сегмент – сегмент кода _Text. Данные, обрабатываемые программой, размещены в сегменте _Text так, чтобы они не могли попасть на ЦП в качестве исполняемых команд, что неизбежно привело бы к сбою в работе процессора. Считается хорошим тоном при написании com-программы на ассемблере располагать ее данные в начале программы, так как это упрощает трансляцию. Первый исполнительный оператор JMP «перепрыгивает» через эти данные на исполнительную часть программы.
Для вывода строки на экран программа использует системный вызов INT 21h (функция 9 ).
Перед применением вызова программа помещает в регистры DS и DX соответственно адрес-сегмент и адрес-смещение для младшего байта выводимой строки. (В нашей программе DS уже содержит требуемый адрес и поэтому не загружается.) Выводимая строка обязательно должна заканчиваться символом “$”. Сама строка может содержать любые другие символы, в том числе и управляющие. В программе используются управляющие символы 0Dh (возврат каретки) и 0Ah (перевод строки).
3.2.3. Программа типа exe
В отличие от com-программы, загружаемой всегда в один сегмент ОП (объемом 64K), программа типа exe может занимать несколько таких сегментов. Следствием этого является наличие в программе команд, выполняющих запись в регистры адресов-сегментов, требующих настройки во время загрузки.
заголовок
Свою работу по созданию exe-файла редактор связей начинает с того, что последовательно записывает в свою память код (команды), данные и стек будущей программы. Порядок расположения этих частей программы, в отличие от com-файла, может быть любым. Их содержимое редактор связей считывает из доступных ему объектных модулей программы. Выполняя размещение программы в памяти, редактор связей обращается с ее адресами так, как будто программа загружена в ОП начиная с условного адреса 00000h. Поэтому считается, что часть программы (код, данные или стек), размещаемая первой, имеет начальный логический адрес 0000h:0000h. Если, например, вторая часть программы начинается с условного реального адреса 000E7h, то ему соответствует начальный логический адрес 000Eh:0007h. Применение таких условных адресов позволяет редактору связей выполнить запись численных значений для всех внешних адресов, оставшихся не проставленными после трансляции.
Исключение составляют команды, выполняющие запись в регистры значений адресов-сегментов. К таким командам относятся, во-первых, команды дальнего перехода JMP и CALL (они выполняют запись адреса-сегмента в регистр CS), а во-вторых, команды MOV, выполняющие запись адресов-сегментов в регистры данных (из регистров данных другие команды MOV переписывают значения в регистры сегментов DS, ES или SS).
Например, следующие два оператора исходной программы выполняют запись в регистр DS того адреса-сегмента, который соответствует точке исходной программы с меткой Msg:
MOV DX, SEG Msg
MOV DS, DX
Первый оператор MOV транслируется в трех-, а второй – в двухбайтовую машинную команду. При этом второй и третий байты первой команды используются для размещения адреса-сегмента, загружаемого в регистр DX.
Так как адреса-сегменты зависят от фактического размещения будущей программы в ОП, которое редактор связей «не знает», то он не может проставить численные значения адресов-сегментов в поля машинных команд. Тем не менее он выполняет значительную подготовку для будущей простановки этих значений загрузчиком. Для этого, во-первых, редактор связей помещает в эти поля условные адреса-сегменты, полученные в предположении, что программа загружается в память начиная с нулевого адреса.
таблицу настройки
Сама настройка адресов-сегментов программы производится загрузчиком сразу же после размещения программы (в том числе и PSP) в ОП. Для этого загрузчик последовательно просматривает таблицу настройки, считывая из нее указатель на очередную последовательность из двух байтов программы, требующую настройки. А затем он прибавляет к их содержимому (то есть к условному адресу-сегменту) номер того параграфа ОП, начиная с которого программа загружена фактически.
После того как программа загружена, загрузчик передает ей управление. На рис.22 приведено содержимое сегментных регистров и указателя стека SP в момент передачи управления exe-программе. В этот момент сегментные регистры данных содержат номер начального параграфа PSP. Благодаря этому программа может использовать полезную информацию, содержащуюся в PSP. (Запомнив где-то первоначальное значение этих регистров, программа обычно записывает в них новое значение, указывающее на начало области данных программы.) Что касается CS, то в него загрузчик помещает начальный номер параграфа не PSP, а области кода программы. Аналогично содержимое SS указывает на границу области стека программы.
Исходная программа на ассемблере, ориентированная на получение exe-программы, обязательно имеет, кроме виртуальных сегментов кода и данных, виртуальный сегмент стека. Порядок записи этих сегментов в программе может быть различным. В качестве точки входа в программу (операнд оператора END) может быть задан любой исполнительный оператор программы.
Рис.22. Результат загрузки exe-программы
Пример. Следующая exe-программа выполняет то же самое, что и предыдущая com-программа.
; Программа выводит сообщение “Здравствуйте” на экран
; ————————————————————————-
;
- cr EQU 0Dh ; Код ASCII возврата каретки
lf EQU 0Ah ; Код ASCII перевода строки
ASSUME CS:_Text, DS:_Data, SS:_Stack
_Text SEGMENT PUBLIC ‘CODE’ ; Сегмент кода
Start: MOV AX, _Data ; Сделаем сегмент
MOV DS, AX ; данных адресуемым
MOV DX, OFFSET Msg ; DX ß адрес сообщения
MOV AH, 9 ; Вывод строки
INT 21h ; на экран
MOV AX, 4C00h ; Возврат в DOS с
INT 21h ; кодом завершения 0
_Text ENDS
_Data SEGMENT PUBLIC ‘DATA’ ; Сегмент данных
Msg DB ‘Здравствуйте’, cr, lf, ‘$’ ; Выводимая строка
_Data ENDS
_Stack SEGMENT STACK ‘STACK’ ; Сегмент стека
DB 20 DUP (?)
_Stack ENDS
END Start
В данной исходной программе три виртуальных сегмента – сегмент кода _Text, сегмент данных _Data и сегмент стека _Stack. Обратите внимание, что в начале программы в регистр DS загружается адрес-сегмент, соответствующий началу данных программы. Это делается для того, чтобы фактическое размещение данных в памяти никак не зависило от размещения в ней PSP. (Для com-программы адрес-смещение данных вычисляется относительно начала PSP.)
3.3. Распределение памяти
Оперативная память – ресурс, требуемый для выполнения любой программы. В рассматриваемой ВС (MS-DOS + i8086) объем адресуемого пространства ОП составляет 1 Мбайт. Только часть этого объема (640К) соответствует реальной ОП (рис.23).
Остальная часть адресов используется для работы с другими видами памяти (ПЗУ и видеопамять).
Благодаря общему адресному пространству работа с этими видами памяти выполняется командами ЦП аналогично ОП (за исключением того, что в ПЗУ нельзя записывать).
Рис.23. Распределение адресного пространства ОП
Как видно из рис.23, общий объем адресуемой памяти составляет не ровно 1Мбайт, а немного больше (почти на 64К).
Дело в том, что если поместить в регистр сегмента предельно большое значение (FFFFh), то за счет содержимого регистра-смещения можно превысить число FFFFFh, которое является номером старшего байта в первом мегабайте памяти. Предельная величина адреса получается суммированием чисел FFFF0h и FFFFh и равна 10FFEFh. Адреса за пределами 1 Мбайта соответствуют реальной ОП, которую MS-DOS обычно использует для своих дополнительных нужд. Основная часть ОП занимаемой ОС находится в начале адресного пространства.
статическим распределением памяти
Статическое распределение памяти для exe- и com-программ выполняется MS-DOS различно. Несмотря на то, что для размещения com-программы требуется всего один блок памяти в 64К, ОС распределяет ей всю память, свободную на момент создания программы. Для exe-программы MS-DOS статически распределяет лишь первоначально необходимый объем ОП, определяемый длиной программы (включая PSP).
Что касается динамического распределения памяти, то может возникнуть вопрос о том, зачем оно вообще нужно в однопрограммной системе, каковой является MS-DOS. Первой причиной его применения является то, что даже при выполнении единственной прикладной программы ее потребности в памяти для хранения своих данных могут превышать объем памяти, которая может быть предоставлена программе. Поэтому программа может выполнять освобождение областей памяти, занимаемых одними данными, для того, чтобы позже запрашивать области памяти для других своих данных.
Вторая причина применения динамического распределения состоит в том, что MS-DOS имеет зачатки мультипрограммных операционных систем, позволяя нескольким прикладным программам одновременно находиться в ОП. Это происходит, во-первых, тогда, когда одна прикладная программа запускается по запросу другой прикладной программы. А во-вторых, прикладная программа может выполнять какие-то системные функции, дублируя или дополняя подпрограммы ОС, и поэтому должна находиться в памяти и во время выполнения других прикладных программ. Подобные запуски прикладных программ будут рассмотрены в следующих подразделах, а пока лишь заметим, что для их реализации требуется, чтобы одна прикладная программа добровольно отказывалась бы от своей неиспользуемой памяти с целью обеспечения будущей загрузки другой программы (программ).
Динамическое распределение памяти выполняется MS-DOS по запросу самой прикладной программы. Для этого прикладная программа передает в ОС один из трех системных вызовов, рассматриваемых далее.
INT 21h (функция 48h) – выдать программе область памяти требуемого размера. Для передачи размера области, измеряемого в параграфах, используется регистр BX. В случае успеха MS-DOS передает в регистре AX адрес-сегмент выделенной области памяти. Иначе – устанавливает флаг переноса FC=1.
Пример. Для создания своего буфера программа запрашивает область памяти длиной 500h параграфов:
- Bufseg DW ? ; Адрес-сегмент новой области
MOV AH, 48h ; Номер функции
MOV BX, 500h ; Размер области (в параграфах)
INT 21h ; Запрос памяти
JC Error ; Переход при ошибке
MOV Bufseg, AX ; Сохранить адрес-сегмент области
INT 21h (функция 49h ) — освободить область памяти, выделенную ранее динамически программе с помощью системного вызова INT 21h (функция 48h).
Адрес-сегмент области программа передает в регистре ES. В случае ошибки MS-DOS устанавливает флаг переноса FC=1.
Пример. Программа освобождает область памяти, адрес-сегмент которой находится в слове Bufseg:
- Bufseg DW ? ; Адрес-сегмент области
MOV AH, 49h ; Номер функции
MOV ES, Bufseg ; Адрес-сегмент области
INT 21h ; Освобождение памяти
JC Error ; Переход при ошибке
INT 21h (функция 4Ah ) – изменить (увеличить или уменьшить) область памяти, принадлежащую программе. В регистре ES программа передает адрес-сегмент модифицируемой области, а в регистре BX – новый размер области памяти в параграфах. В случае ошибки MS-DOS устанавливает флаг переноса FC=1.
Пример. Программа просит уменьшить размер области, выделенной ранее (в примере для функции 48h) до 200h параграфов (8К):
- Bufseg DW ? ; Адрес-сегмент области
MOV AH, 4Ah ; Номер функции
MOV BX, 200h ; Новый размер области
MOV ES, Bufseg ; Адрес-сегмент области
INT 21h ; Изменить размер области
JC Error ; Переход при ошибке
Перейдем к рассмотрению принципов реализации динамического распределения памяти в MS-DOS. Во-первых, вся ОП в системе разбита на области неодинаковой длины. Это разбиение со временем может изменяться, так как одни области могут разбиваться, а другие (соседние), наоборот, могут соединяться. Любая прикладная программа занимает минимум две области ОП. В одной из них находится сама программа (PSP, код, данные и стек), а во второй области находится блок окружения программы (размер этой области кратен 512 байтам).
Число областей ОП, занимаемых программой, увеличивается в результате системных вызовов INT 21h (функция 48h).
блока управления области
1) в байте 0 – символ M , если область не последняя; Z , если область последняя в ОП;
2) в байтах 1 и 2 – адрес-сегмент PSP программы, являющейся владельцем данной области ОП. Если область свободна, то 0000h;
3) в байтах 3 и 4 – длина области в параграфах;
4) байты 5-15 – зарезервированы.
В-третьих, динамический запрос программы на получение дополнительной памяти MS-DOS выполняет одним из трех способов:
1) двигаясь в сторону больших адресов, последовательно просматривает блоки управления областей до тех пор, пока не встретится область памяти, не меньшая требуемой. Начальный адрес-сегмент этой области возвращается в программу, а ее излишек оформляется в новую свободную область. При отсутствии требуемой области устанавливается флаг FC;
2) последовательно просматриваются все свободные области памяти с целью нахождения минимальной области, которая не меньше требуемой;
3) отличается от способа 1 тем, что просмотр областей производится от больших адресов к меньшим.
Обычно MS-DOS ищет требуемую область, пользуясь первым способом. Но с помощью специального системного вызова программа может запросить у ОС другой алгоритм поиска.
Следует отметить, что при выполнении системных вызовов распределения памяти MS-DOS «попутно» проверяет целостность всей цепочки блоков управления памятью. Поэтому нельзя портить содержимое этих блоков. Иначе ОС завершит работу системы, выдав соответствующее сообщение на экран.
В последующих разделах будут приведены примеры программ, содержащих системные вызовы для динамического распределения памяти.
3.4. Запуск прикладных программ
Работа интерпретатора команд MS-DOS основана на использовании системного вызова EXEC — INT 21h (функция 4Bh , подфункция 00h ).
Именно этот вызов обеспечивает загрузку заданного com- или exe-файла, его настройку (для exe-файла), выполнение полученной машинной программы, а затем возврат в ту точку программы ИК, откуда был сделан этот системный вызов.
Применив вызов EXEC в своей прикладной программе, мы сможем запустить (загрузить и выполнить) любую другую прикладную или системную обрабатывающую программу. В результате наша прикладная программа начнет выполнять некоторые функции ИК, а при некоторой доработке сможет даже заменить его. В этом нет ничего удивительного, так как основная часть ОС (ядро ОС) рассматривает ИК как обычную обрабатывающую программу (лингвистический процессор).
Коль скоро вызов EXEC так важен, то внимательно рассмотрим его.
Во-первых, обратим внимание на то, что отношение между «родительской» и «дочерней» программами логически эквивалентно отношению между программой и подпрограммой, вызываемой с помощью команды CALL или команды INT. Подобно подпрограмме, дочерняя программа является по отношению к родительской программе логической процедурой (см. прил.1), так как на время выполнения дочерней программы выполнение родительской программы приостанавливается, а затем продолжается с той команды, которая расположена в программе сразу же после вызова EXEC.
Во-вторых, подобно обычным программным процедурам, дочерние программы могут быть вложенными. То есть дочерняя программа сама может содержать вызов EXEC, порождающий другую программу, и так далее.
В-третьих, прежде, чем выдавать вызов EXEC, родительская программа должна позаботиться о том, чтобы имелась в наличии свободная память, достаточная для размещения дочерней программы. С этой целью родительская программа должна освободиться от излишков памяти, ранее выделенной ей, используя системные вызовы, рассмотренные в п.3.3.
В-четвертых, запуск программы с помощью вызова EXEC предполагает наличие между родительской и дочерней программами не только управляющего, но и информационного взаимодействия. При этом информация может быть передана как в прямом направлении – от родительской программы к дочерней, так и в обратном направлении.
Передача информации к дочерней программе выполняется с помощью ее PSP, который создается при выполнении вызова EXEC. Если информация небольшая, то она может быть размещена в «хвосте». Большой объем информации (до 32 К) может быть размещен в блоке окружения программы. Кроме того, для передачи данных на вход дочерней программы могут использоваться два поля PSP, предназначенные для хранения устаревших типов блоков управления файлами.
Для того, чтобы выполнение вызова EXEC привело к созданию требуемого PSP, в момент вызова регистры должны содержать следующие данные:
DS : DX – (адрес-сегмент : адрес-смещение) символьной строки, содержащей имя com- или exe-файла, подлежащего загрузке. Сразу же после имени файла должен находиться нулевой байт (00h);
ES : BX — (адрес-сегмент : адрес-смещение) блока параметров.
Блок параметров
1) 2 байта — адрес-сегмент блока окружения. Если эти байты содержат 0, то дочерняя программа получает копию блока окружения родительской программы;
2) 4 байта — адрес-смещение и адрес-сегмент символьной строки, содержащей «хвост» команды. Эта строка заканчивается символом «возврат каретки» (0Dh), который не учитывается при подсчете длины «хвоста»;
3) 8 байтов — адрес-смещение и адрес-сегмент двух блоков управления файлами (по 4 байта).
В простейшем случае, когда ни «хвост», ни блоки управления файлами не используются для передачи информации на вход дочерней программы, а блок окружения такой же, как и у родительской программы, блок параметров может быть оформлен в программе как последовательность 14-и байтов, заполненных нулями.
В случае ошибочного завершения вызова EXEC в родительскую программу возвращается флаг FC=1, а в регистре AX возвращается код ошибки.
В случае успешного запуска и выполнения дочерней программы, она может передать в родительскую программу любой объем информации, используя для этого ту область ОП, адрес которой был получен ей ранее в составе другой информации, переданной от родительской программы.
Пример. Следующая программа выполняет роль простейшего интерпретатора команд MS-DOS. Она запрашивает у пользователя имя файла, содержащего загрузочный модуль требуемой программы, а затем эту программу запускает. При этом могут запускаться только такие программы, которые не требуют от родительской программы каких-то входных данных. После запуска дочерней программы (успешного или неуспешного) на экран опять выводится приглашение ввести имя файла. Для прекращения работы программы достаточно вместо набора имени файла нажать клавишу <Enter>.
; Программа запускает другие программы
; ——————————————————
;
ASSUME CS:_Text
_Text SEGMENT PUBLIC ‘CODE’
ORG 100h ; Следующий байт имеет адрес-смещение 100h
Start: JMP Begin
; Данные программы
Buff DB 81 ; Максимальная длина имени файла
Lname DB 0 ; Фактическая длина имени файла
Namefile DB 81 DUP (0) ; Здесь размещается имя файла
Msg1 DB ‘Ошибка распределения памяти’, 0Dh, 0Ah, ‘$’
Msg2 DB ‘Введите имя com или exe-файла’, 0Dh, 0Ah, ‘$’
Msg3 DB ‘Ошибка запуска программы’, 0Dh, 0Ah, ‘$’
Blocpar DB 14 DUP (0) ; Блок параметров
; Освобождение лишней памяти
Begin: MOV SP, OFFSET Lprog ; SP ß новая база стека
MOV AH, 4Ah ; Функция изменения области памяти
MOV BX, (Lprog + 0Fh)/16 ; Новый размер области памяти
INT 21h ; Изменение размера области
JNC M1 ; Переход при отсутствии ошибки
MOV AH, 09h ; Функция вывода строки
MOV DX, OFFSET Msg1 ; DX ß адрес сообщения о ошибке
INT 21h ; Вывод строки
JMP Exit ; Переход на завершение программы
; Ввод имени файла
M1: MOV AH, 09h ; Функция вывода строки
MOV DX, OFFSET Msg2 ; DX ß адрес строки-приглашения
INT 21h ; Вывод строки
MOV AH, 0Ah ; Функция ввода строки
MOV DX, OFFSET Buff ; DX ß адрес-смещение буфера ввода
INT 21h ; Ввод строки
MOV AH, 2 ; Функция вывода символа
MOV DL, 0Ah ; DL ß символ перевода строки
INT 21h ; Вывод символа
CMP Lname, 0 ; Имя файла отсутствует ?
JZ Exit ; Если да
; Запуск новой программы
XOR BX, BX ; BX ß 0
MOV BL, Lname ; BL ß длина имени файла
MOV Namefile[BX], 0 ; Запись нуля после имени файла
MOV DX, OFFSET Namefile ; DX ß смещение имени файла
MOV BX, OFFSET Blocpar ; BX ß смещение блока параметров
MOV AX, 4B00h ; Функция и подфункция запуска
INT 21h ; Запуск дочерней программы
JNC M2 ; Переход при отсутствии ошибки
MOV AH, 9 ; Функция вывода строки
MOV DX, OFFSET Msg3 ; Адрес строки-сообщения о ошибке
INT 21h ; Вывод строки
M2: JMP M1 ; Повторение для нового файла
; Завершение программы
Exit: MOV AX, 4C00h ; Возврат в DOS с
INT 21h ; кодом завершения 0
; Определение области стека
Stek DW 64 DUP (?) ; Новая область стека
Lprog EQU $ — Start + 100h ; Длина программы (включая PSP)
_Text ENDS
END Start
Приведенная выше программа выполняет обмен с экраном и клавиатурой, используя следующие системные вызовы MS-DOS:
INT 21h (функция 9 ) — вывод строки. Данный вызов уже использовался в п.3.2;
INT 21h (функция 2 ) — вывод символа. Перед применением вызова программа помещает в регистр DL код выводимого символа. В программе данный системный вызов используется для перевода строки, что исключает наложение на экране строк, принадлежащих родительской и дочерней программам;
INT 21h (функция 0Ah ) — ввод с клавиатуры символьной строки. В качестве последнего символа строки вводится символ 0Dh (возврат каретки).
(Этот символ помещается в водимую строку драйвером клавиатуры при нажатии клавиши <Enter>.) Перед применением вызова программа помещает в регистры DS и DX соответственно адрес-сегмент и адрес-смещение для младшего байта буфера, в который должна быть введена строка. Данный буфер имеет три поля:
1) младший байт содержит максимальное число вводимых символов. Запись этого числа выполняет сама программа;
2) второй байт содержит число фактически введенных символов. Запись в этот байт выполняет MS-DOS;
3) остальные байты буфера содержат введенную строку, заканчивающуюся кодом 0Dh.
Первый исполнительный оператор JMP «перепрыгивает» через данные программы на ее фрагмент, выполняющий возврат в систему «лишней» памяти. Вспомним, что для com-программы MS-DOS выделяет всю ОП, оставшуюся после загрузки самой ОС. Так как наша программа использует лишь небольшую часть этой памяти, то она желает сообщить об этом системе. Простейший вариант этого – оставить за программой те 64К, в которые первоначально загружается com-программа. Но наша программа делает больше: она создает свой новый стек, разместив его сразу за своим кодом, а от всей остальной памяти отказывается.
Само освобождение памяти выполняет ранее рассмотренный системный вызов INT 21h (функция 4Ah).
При этом количество запрашиваемых параграфов памяти определяется в результате целочисленного деления длины программы (константа Lprog), увеличенной на 15 (0Fh), на длину одного параграфа – 16.
После того как память освобождена, программа выводит на экран просьбу ввести имя загружаемого файла. Если искомый файл находится в текущем каталоге, то достаточно ввести имя файла и расширение имени (com или exe).
Иначе – следует ввести имя-путь файла. Если файл находится на другом логическом диске, то имя-путь должно предваряться именем этого диска, например: C:\DISTANT\PR1.COM .
Введенное имя файла передается на вход системного вызова INT 21h (функция 4Bh, подфункция 0).
Этому предшествует небольшая корректировка имени: в последний байт (с кодом 0Dh) записывается число 0.
Интересно отметить, что данная программа может запускать сама себя. В результате создается новый экземпляр программы, обладающий всеми ее свойствами. Вы можете проверить практически это и другие свойства данного простейшего интерпретатора команд.
3.5. Резидентные программы
Резидентная программа
Для того, чтобы сделать свою программу резидентной, достаточно воспользоваться системным вызовом INT 27h . Этот вызов завершает выполнение программы, оставив ее в памяти. Перед применением вызова программа должна поместить в регистр DX адрес-смещение для байта, расположенного сразу же после резидентной программы.
После своего создания резидентная программа занимает место в памяти, но ничего не делает (не выполняется на ЦП).
Это продолжается до тех пор, пока программа не будет инициирована (запущена).
Единственно возможным источником такого инициирования является поступление в ЦП сигнала прерывания. Поэтому резидентная программа обязательно является обработчиком прерываний.
Вспомним (см. п.2.6.4), что обязательным условием запускаемости обработчика прерываний является размещение его стартового адреса в векторе прерываний, номер которого совпадает с номером прерывания. Для такого размещения следует использовать системный вызов INT 21h (функция 25h ).
Перед применением вызова программа должна поместить в регистр AL номер прерывания, а в регистры DS и DX соответственно адрес-сегмент и адрес-смещение для обработчика прерываний.
Выбор номера прерывания зависит от того, какую функцию будет выполнять обработчик прерываний. Если выполняемая им функция уже реализована в системе, то новый обработчик прерываний должен ориентироваться на уже используемый номер прерывания. Иначе для новой функции необходимо использовать и новый номер прерывания. Вначале рассмотрим второй случай.
Для выбора свободного номера прерывания можно использовать отладчик Debug. С помощью его команды D 0:0 просмотрим начало области памяти, занимаемой таблицей векторов прерываний. (Для продолжения просмотра достаточно вводить команду D .) В этой области выберем любую последовательность из четырех нулевых байтов, номер младшего из которых делится нацело на четыре. Тогда частное от такого деления и есть искомый номер прерывания. Например, пусть выбранная последовательность четырех байтов начинается с номера 200h = 512. Тогда искомый номер прерывания n = 200h/4 = 80h = 128.
Пример. Сделаем резидентной простую com-программу, рассмотренную в п.3.2.2. Для ее инициирования будем использовать программное прерывание с номером 80h. Текст программы на ассемблере:
; Резидентная программа выводит сообщение “Здравствуйте” на экран
; ——————————————————————————————
; Вызов программы: команда INT 80h
;
- cr EQU 0Dh ; Код ASCII возврата каретки
lf EQU 0Ah ; Код ASCII перевода строки
ASSUME CS:_Text
_Text SEGMENT PUBLIC ‘CODE’
ORG 2Ch
Blocokr DW ? ; Адрес блока окружения
ORG 100h
Start: JMP Init ; Переход на инициализацию
; Обработчик прерываний
Msg DB ‘Здравствуйте’, cr, lf, ‘$’ ; Выводимое сообщение
Begin: STI ; Разрешить маскируемые прерывания
PUSH AX ; Сохранение содержимого
PUSH DX ;
- PUSH DS ; в стеке
MOV AX, CS ; DS будет адресовать данные
MOV DS, AX ; резидентной программы
MOV DX, OFFSET Msg ; DX ß адрес строки-сообщения
MOV AH, 9 ; Вывод строки
INT 21h ; на экран
POP DS ; Восстановление содержимого
POP DX ;
- POP AX ; из стека
IRET ; Возврат из прерывания
; Инициализационная часть программы
Init: MOV AX, 2580h ; Запись стартового адреса обработчика
MOV DX, OFFSET Begin ; в вектор прерываний
INT 21h ; с номером 80h
MOV AH, 49h ; Освобождение области памяти,
MOV ES, Blocokr ; занимаемой
INT 21h ; блоком окружения
MOV DX, OFFSET Init ; Возврат в DOS, оставшись
INT 27h ; резидентным
_Text ENDS
END Start
Данная программа состоит из двух основных частей: 1) обработчик прерываний (резидентная часть); 2) инициализационная часть. Инициализационная часть нерезидентна по той причине, что она выполняется всего один раз и нет смысла постоянно держать ее в памяти. При этом следует учитывать, что так как резидентная программа находится в ОП во время выполнения других программ, то занимаемую ею память следует минимизировать.
Другим средством уменьшения затрат памяти, которое применено в программе, является освобождение области, выделенной для блока окружения программы. (Вспомним, что такая область статически назначается любой программе, и что ее размер кратен 512 байтам.) Подобное освобождение возможно потому, что наш обработчик прерываний (как и любой другой) эту область не использует.
Обратим внимание, что первые команды обработчика прерываний запоминают в стеке, а последние команды извлекают из стека содержимое тех регистров, в которые выполняется запись в программе обработчика. Среди этих регистров есть и регистр сегмента данных DS, в который обработчик записывает новое значение, совпадающее с содержимым регистра CS. Это обусловлено тем, что при вызове обработчика (через прерывание) новое значение записывается лишь в единственный сегментный регистр CS (это значение берется из вектора прерываний).
Остальные регистры сегментов (DS, SS и ES) сохраняют свое значение, полученное ранее в вызывающей программе. Поэтому для того, чтобы программа обработчика могла использовать свои данные, она помещает в DS соответствующий адрес-сегмент (для com-программ он совпадает с содержимым CS).
Интересно добавить, что так как содержимое регистров SS и SP поступает в обработчик прерываний из вызывающей программы без изменений, то обработчик использует тот же самый стек, что и программа. Это свойство позволяет достаточно просто выполнять информационный обмен между программой и обработчиком, используя стек.
перехват прерываний
Для чтения прежнего содержимого вектора прерываний используется системный вызов INT 21h , функция 35h . Перед выполнением вызова программа должна поместить в регистр AL номер прерывания. В результате выполнения вызова регистры ES и BX содержат соответственно адрес-сегмент и адрес-смещение, находящиеся в векторе прерываний. Далее эти адреса могут быть переписаны для сохранения в любое место ОП.
Инициирование прежнего обработчика прерываний может потребоваться в двух случаях. Во-первых, в том случае, если новый обработчик прерываний не заменяет, а лишь расширяет функции, выполнявшиеся прежним обработчиком. При этом после того, как новый обработчик выполнится, он должен передать управление старому обработчику. Во-вторых, часто обработчик прерываний должен находиться в ОП не до конца работы системы. В этом случае перед тем, как освободить память, необходимо восстановить старый обработчик прерываний. Это нетрудно сделать с помощью системного вызова INT 21h, функция 25h. Рассмотрим реализацию первого случая.
Пример. Следующая резидентная программа “дополняет” обработку прерывания с номером 0. Системный обработчик данного прерывания-исключения прекращает выполнение текущей программы, если ее команда выполнила деление на 0. Наш обработчик спрашивает пользователя о желании завершить программу. В случае нажатия клавиши y (или Y) выполнение программы прекращается. При нажатии любого другого символа выполнение программы продолжается.
; Резидентная программа выполняет обработку исключения 0 (деление на 0)
; ————————————————————————————————
;
- cr EQU 0Dh ; Код ASCII возврата каретки
lf EQU 0Ah ; Код ASCII перевода строки
ASSUME CS:_Text
_Text SEGMENT PUBLIC ‘CODE’
ORG 2Ch
Blocokr DW ? ; Адрес блока окружения
ORG 100h
Start: JMP Init ; Переход на инициализацию
; Обработчик прерываний
Msg DB ‘Завершить программу ?’, cr, lf, ‘$’ ; Выводимое сообщение
Old DD ? ; Прежнее содержимое вектора пр-й
Begin: STI ; Разрешить маскируемые прерывания
PUSH AX ; Сохранение содержимого
PUSH DX ;
- PUSH DS ; в стеке
PUSHF ; Сохранение регистра флагов
MOV AX, CS ; DS будет адресовать данные
MOV DS, AX ; резидентной программы
MOV DX, OFFSET Msg ; DX ß адрес строки-сообщения
MOV AH, 9 ; Вывод строки
INT 21h ; на экран
MOV AH,1 ; Ввод символа с
INT 21h ; клавиатуры
OR AL, 20h ; Перевод символа на нижний
CMP AL, ‘y’ ; Введен символ “y” ?
JNZ Exit ; Если нет — то выход из прерывания
POPF ; Восстановление регистра флагов
POP DS ; Восстановление содержимого
POP DX ;
- POP AX ; из стека
JMP Old ; Вызов прежнего обработчика
Exit: MOV AH, 2 ; Перевод
MOV DL, lf ;
- INT 21h ; экрана
POPF ; Восстановление регистра флагов
POP DS ; Восстановление содержимого
POP DX ;
- POP AX ; из стека
IRET ; Возврат из прерывания
; Инициализационная часть программы
Init: MOV AX, 3500h ; Сохранение прежнего содержимого
INT 21h ; вектора 0 в регистрах ES и BX
MOV WORD PTR Old, BX ; А затем
MOV WORD PTR Old+2, ES ; в поле Old
MOV AX, 2500h ; Запись стартового адреса обработчика
MOV DX, OFFSET Begin ; в вектор прерываний
INT 21h ; с номером 0
MOV AH, 49h ; Освобождение области памяти,
MOV ES, Blocokr ; занимаемой
INT 21h ; блоком окружения
MOV DX, OFFSET Init ; Возврат в DOS, оставшись
INT 27h ; резидентным
_Text ENDS
END Start
Как и предыдущая программа, данная программа состоит из инициализационной и резидентной частей. Инициализационная часть начинает свою работу с сохранения прежнего содержимого вектора прерываний 0 в двойном слове ОП с меткой Old. Затем она помещает в вектор 0 стартовый адрес нашего обработчика прерываний и сокращает объем занимаемой им памяти. В завершение инициализационная часть выполняет возврат в MS-DOS, оставив в памяти обработчик прерываний.
Обработчик прерываний (резидентная часть) запускается в результате прерывания-исключения с номером 0. В начале своего выполнения он разрешает маскируемые внешние прерывания, а также сохраняет в стеке используемые им регистры. Обратите внимание, что среди сохраняемых регистров находится регистр флагов. Это делается для того, чтобы прежний обработчик прерываний получил такое содержимое этого регистра, которое установила ранее программа. (На тот случай, если некоторые флаги используются для передачи информации на вход обработчика.)
Далее резидентная часть выводит на экран вопрос о желании прекратить выполнение программы, выполнившей деление на 0. Ответ пользователя (один символ) переводится в строчный символ. Такой перевод выполняется установкой единственного бита 5, которым отличаются коды строчной и прописной одноименных букв. Если пользователь не желает прекращать выполнение программы, то выполняется возврат из прерывания.
Иначе управление передается системному обработчику. Это делается с помощью команды JMP, выполняющей дальний безусловный переход на стартовый адрес этого обработчика. Закономерен вопрос: как системный обработчик возвратит управление (если пожелает) в прерванную программу? Ответ: обычным способом, то есть с помощью команды IRET. Это объясняется тем, что системный обработчик «наследует» от нашего обработчика стек прерванной программы, в который был помещен ранее адрес возврата (в момент прерывания).
Для проверки работоспособности рассмотренного обработчика прерываний необходимо: после того как приведенная выше программа будет выполнена (сделав обработчик прерываний резидентным), запустить на выполнение простейшую прикладную программу, выполняющую деление на нуль в бесконечном цикле.
Если резидентная программа полностью заменяет ОС в обработке каких-то типов прерываний, то по истечении какого-то времени у пользователя (или у прикладной программы) может возникнуть желание вернуться к системной обработке этих прерываний. Для этого следует выполнить следующие три действия:
1) закрыть файлы, открытые резидентной программой;
2) восстановить векторы прерываний так, чтобы они указывали на системные обработчики;
3) освободить память, занимаемую резидентной программой.
Перечисленные действия должна выполнить сама резидентная программа при получении ею команды о завершении. Например, если резидентная программа выполняет обработку прерываний от клавиатуры, то код одной из клавиш (комбинации клавиш) может использоваться в качестве такой команды.
Для восстановления векторов прерываний можно использовать рассмотренные ранее системные вызовы INT 21h (функции 35h и 25h).
Первый из этих вызовов позволяет запомнить, а второй – восстановить прежнее содержимое вектора прерывания.
Для освобождения памяти, занимаемой резидентной программой, можно воспользоваться системным вызовом INT 21h (функция 49h), который требует задания в регистре ES адреса-сегмента области резидентной программы. Так как адрес-сегмент резидентной программы находится во время ее выполнения в регистре CS, следующий фрагмент программы показывает, как можно осуществить такой вызов:
- PUSH CS ; Копирование CS
POP ES ; в ES
MOV AH, 49h ; Освобождение
INT 21h ; памяти
- . . . . . ;
- Восстановление
IRET ; Возврат из прерывания
4.ФАЙЛОВАЯ ОРГАНИЗАЦИЯ ИНФОРМАЦИИ
4.1. Файлы
секторами
Подобно тому, как пронумерованы (имеют реальный адрес) все ячейки (байты) ОП, пронумерованы также все секторы носителя ВП. Пользуясь номером требуемого сектора, любая прикладная или системная программа может выполнить чтение или запись этого сектора, обратившись к драйверу соответствующего устройства ВП. Драйверы позволяют программам выполнять информационный обмен не только с устройствами ВП, но и с устройствами ввода-вывода. Вопросы построения драйверов будут рассмотрены в п.5, а пока перечислим недостатки выполняемого ими информационного обслуживания программ:
1) если объем информации на носителе достаточно велик, то вся работа по поиску нужных секторов в этом объеме ложится на соответствующую системную или прикладную программу;
2) секторы часто неудобны для обработки их в программе;
3) при смене типа ПУ, используемого для ввода или вывода информации данного вида, приходится вносить существенные изменения в программу из-за влияния интерфейсов соответствующих драйверов. Примером является вывод выходных данных программы в одном случае на принтер, а в другом – на экран.
Первые два недостатка относятся только к устройствам ВП, а последнее – ко всем ПУ. Для устранения перечисленных трудностей информационное обслуживание программ в большинстве ОС выполняется с помощью файлов. Далее будем различать физические и логические файлы.
Физический
Рис.24. Информационное обслуживание программ
физическими записями
разделами
кластерами
Простое имя файла –
полное имя файла
Логический файл
номер логического файла
3) возможно другое разбиение файла на записи;
4) логический файл не связан с конкретным носителем информации.
Перечисленные отличия существенно упрощают работу программы с файлом. Первое из них упрощает идентификацию файла при выполнении операций над ним, а последнее отличие делает излишними переделки программы при смене обрабатываемых ею физических файлов. Перейдем к рассмотрению второго отличия.
логические записи
ключ
Информационное обслуживание программ и пользователей на уровне логических записей, а также на уровне полей этих записей выполняют СУБД – системы управления базами данных . Эти системные обрабатывающие программы пользуются услугами подпрограмм ОС по работе с файлами и поэтому могут рассматриваться в качестве надстройки над этими подпрограммами (рис.25).
4.2. Файловые системы
4.2.1. Структура файловой системы
файловой системой
Рис.25. Информационное обслуживание программ с помощью СУБД
Например, файловые системы, поддерживаемые (обслуживаемые) MS-DOS, имеют тип FAT . Этот тип объединяет три родственных типа файловых систем: FAT12, FAT16 и FAT32 (последняя поддерживается только MS-DOS версии 7.0).
атрибутами файла
1)
2) состав атрибутов файла и место их хранения;
5) длина простого имени файла.
Из перечисленных характеристик длина простого имени файла была рассмотрена ранее. Перейдем к рассмотрению других характеристик.
. Линейная структура
а) линейная структура
б) иерархическая (древовидная) структура
в) иерархическая структура с пересечениями
- каталог
- файл данных
Рис.26. Основные типы структур ФС
Иерархическая
текущий каталог
Иерархическая структура с пересечениями
ФС типа FAT имеют иерархическую структуру. На рис.27 приведен пример такой структуры. В ней корневой каталог имеет имя ‘\’ (обратный слеш), а имя каждого следующего каталога завершается этим символом.
Пример имени-пути:
\DISTANT\IVANOV\PROB1.ASM
Нетрудно заметить, что другой файл с таким же простым именем PROB1.ASM, но расположенный в другом каталоге, имеет другое имя-путь:
\DISTANT\PETROV\PROB1.ASM
Допустим, что текущим каталогом является DISTANT, тогда последний файл может быть указан с помощью промежуточного имени:
PETROV\PROB1.ASM
Что касается структуры каталога, то этот файл состоит из 32-байтовых логических записей. В FAT12 и в FAT16 одна такая запись соответствует одному файлу. В ней находятся: простое имя файла (собственно имя и расширение имени); номер первого кластера логического диска, занимаемого данным файлом; атрибуты файла. В FAT32 каждому файлу соответствуют несколько 32-байтовых записей каталога. Структура первой из них аналогична записи каталога в FAT12 и в FAT16, а в последующих записях содержится длинное (до 255 байтов) простое имя файла. Таким образом, один и тот же файл может обрабатываться как системными вызовами, предназначенными для работы с короткими, так и системными вызовами для работы с длинными именами файлов.
c:
USER1 USER2
- каталог
- файл данных
Рис.27. Пример иерархической структуры ФС типа FAT
4.2.2. Атрибуты файла
Любая ФС содержит для каждого из своих файлов его атрибуты. Они используются самой ОС, а также пользователями и обрабатывающими программами. Наименьшее число атрибутов имеют файлы в ФС, поддерживаемых однопользовательскими однопрограммными системами. Например, в FAT файл имеет атрибуты:
1) дата и время последней модификации файла;
2) текущий размер файла;
3) флаг “каталог”;
4) флаг “только для чтения”;
5) флаг “скрытый файл”;
6) флаг “системный файл”;
7) флаг “архивный файл”.
Атрибуты файла могут находиться или в записи каталога, соответствующей файлу, или в отдельной таблице (в части таблицы).
Как уже говорилось, в FAT атрибуты файла содержатся в 32-байтовой записи каталога, соответствующей этому файлу.
Различные ФС различным образом распределяют пространство носителя ВП (или его раздела) между своими элементами (файлами и таблицами).
логического форматирования
В зависимости от типа ФС некоторые из ее элементов дублируются для повышения надежности: в случае повреждения одной области носителя может использоваться копия этой информации, расположенная в другой области. (Дублирование не спасает от программной порчи информации. Так как изменение информации на носителе программой синхронно выполняется для всех копий этой информации.)
На рис.28 приведено распределение пространства носителя ВП для ФС типа FAT.
Пространство ВП, выделенное в конкретной ФС для размещения файлов (в том числе, для размещения каталогов), может распределяться между ними двумя основными способами: 1) непрерывными разделами; 2) в виде совокупности блоков памяти, необязательно соседних друг с другом. Непрерывное распределение имеет серьезные недостатки:
статически
внешняя фрагментация памяти
Перечисленные недостатки привели к тому, что в настоящее время непрерывное распределение почти не используется для ФС на магнитных дисках. Оно применяется лишь для магнитных лент и лазерных дисков, так как пространство этих носителей распределяется между файлами только статически. (А для лазерных дисков отсутствует и внешняя фрагментация памяти.)
Что касается файлов на магнитных дисках, то память для них назначается динамически блоками, в качестве которых используются кластеры. Размер кластера для носителя (раздела) фиксируется при выполнении логического форматирования и составляет от 512 байт (1 сектор) до 64 К (128 секторов).
Размер кластера зависит как от размера носителя (раздела), так и от типа ФС.
Например, в FAT12, FAT16 и FAT32 для кодирования номера кластера используются соответственно 12, 16 и 32 бита (отсюда различия в названиях ФС).
Поэтому максимальный номер кластера в FAT12 равен 4095, в FAT16 – 65535, в FAT32 – 4 Г. Размер кластера для каждого носителя (раздела) выбирается с таким расчетом, чтобы произведение размера кластера на его максимальный номер не было меньше, чем размер носителя (раздела).
Иначе на носителе будут неадресуемые кластеры.
внутренней фрагментации
Основным следствием из двух названных факторов является то, что FAT12 используется для небольших дисков (не более 16 Мбайт), что позволяет не делать кластеры более 4 Кбайт. FAT16 целесообразна для дисков не более 512 Мбайт. Для больших дисков целесообразна FAT32, которая позволяет для дисков емкостью до 8 Гбайт ограничиться размером кластера 4 Кбайт. Для больших дисков приходится применять размер кластера 8, 16 или 32 Кбайт.
При размещении файла в непрерывном пространстве ВП информация о его местоположении сводится к двум численным параметрам: номер первого кластера файла и длина файла (в кластерах).
Но при размещении файла в разрывном пространстве надо знать, где находится каждый его кластер. В различных ФС такая информация содержится:
1) в записи каталога;
2) в самих кластерах файла;
3) в специальной таблице.
Первый из этих способов предполагает, что запись каталога, соответствующая файлу, содержит перечень номеров всех его кластеров. Недостаток очевиден: размер записи каталога должен зависить от размера файла, что очень неудобно. Поэтому в записи каталога обычно хранится только номер самого первого кластера файла или указатель на место, где этот номер хранится.
Второй способ предполагает, что предыдущий кластер файла содержит номер следующего кластера. В результате все кластеры файла связаны в единый список. Считав из каталога номер первого кластера файла, по цепочке нетрудно прочитать номера всех остальных его кластеров. Основным недостатком данного способа является то, что для доступа к кластеру, расположенному в середине файла, необходимо прочитать все предыдущие кластеры.
Третий способ предполагает, что номера кластеров файла содержатся в специальной таблице. Данный способ отличается от предыдущего тем, что поиск нужного кластера ОС производит не путем считывания многих кластеров носителя, а путем просмотра содержимого одного и того же кластера (в котором находится таблица).
Реализация такого метода в MS-DOS основана на использовании таблицы размещения файлов FAT (File Allocation Table), название которой используется в качестве названия всей ФС. Таблица FAT имеет столько 12, 16- или 32-битных элементов, сколько кластеров носителя могут распределяться между файлами. Иными словами, FAT представляет собой уменьшенную модель распределяемой части носителя. Ее наличие позволяет размещать файл в разрывной области ВП. Для этого каждому файлу ставится в соответствие вспомогательный линейный связанный список, построенный из элементов таблицы FAT.
Вспомним, что одно из полей записи каталога, описывающей файл, содержит его начальный адрес. Этот адрес представляет собой номер первого кластера файла и, следовательно, номер соответствующего элемента в таблице FAT. Содержимым этого элемента является номер следующего элемента связанного списка, который совпадает с номером следующего кластера файла. Если элемент FAT-таблицы соответствует последнему кластеру файла, то он содержит специальное число (FFFh, FFFFh или FFFFFFFFh).
4.2.5. Объединение файловых систем
В ВС обычно имеется несколько (разнотипных и однотипных) устройств ВП. На каждом из установленных носителей ВП имеется своя ФС. Более того, общепринято, что на каждом разделе (логическом диске) может располагаться своя собственная ФС. Причем ФС, располагаемые на одном носителе, могут быть как однотипными, так и разнотипными. Единственное требование: все ФС должны поддерживаться используемой ОС.
монтированием
текущего логического диска
Второй из указанных подходов (монтирование ФС) широко используется в многопользовательских системах. При этом отметим, что несмотря на объединение деревьев ФС, работа с каждой из ФС производится в соответствии с ее типом, то есть разными подпрограммами ОС.
4.3. Операции над файлами
4.3.1. Создание и открытие файла
Работа с любым файлом начинается с его создания. Оно выполняется ОС по соответствующей просьбе со стороны программы. Во время создания файла добавляется новая запись в “родительский” каталог, заданный программой. В нее помещается простое имя файла, а также другая информация, соответствующая типу используемой ФС. Например, при включении файла в систему FAT, в запись каталога помещаются атрибуты нового файла. (Многие другие файловые системы размещают атрибуты файла не в каталоге, а в своих таблицах.)
Кроме того, при создании файла в ФС со статическим распределением ВП файлу выделяется необходимое пространство на носителе. В ФС с динамическим распределением памяти (к которым относится и FAT) память файлу при его создании не выделяется, а его длина принимается нулевой.
открытие файла
Программа, по запросу которой ОС выполняет открытие файла, должна сообщить в системном запросе не только имя открываемого физического файла, но и тип последующей работы с ним (чтение или запись).
В качестве результата открытия файла в программу возвращается логический номер файла. Рассмотрим теперь внутреннюю работу ОС при открытии файла.
Во-первых, во время открытия файла ОС создает для работы с ним столько буферов, сколько типов операций информационного обмена с ним будут выполняться. Поэтому при открытии файла и на чтение и на запись ему выделяются два буфера, а при открытии только на чтение – один. Размер системных буферов кратен одному сектору и обычно равен одному кластеру.
Во-вторых, ОС помещает БУФ (блок управления файлом ) в свою таблицу открытых файлов . БУФ содержит атрибуты файла (переписываются из каталога), а также указатели на системные буферы для работы с ним.
таблицу логических номеров файлов
Для программы, выполняемой в среде MS-DOS, таблица логических номеров файлов содержится в PSP программы, начиная с адреса-смещения 18h. Размер этой таблицы равен 20 байтов (по одному байту на номер файла).
Поэтому общее число файлов, открытых в программе, не может превышать 20.
Из этих номеров первые пять MS-DOS использует сама, открывая для программы (до начала ее выполнения) следующие файлы:
0 – стандартное устройство ввода (обычно клавиатура);
1 – стандартное устройство вывода (обычно экран);
2 – устройство вывода сообщений об ошибках (экран);
3 – последовательный порт (обычно COM1);
4 – параллельный порт (обычно LPT1).
Нетрудно заметить, что все перечисленные файлы являются файлами-устройствами. Приняв их во внимание, максимальное число файлов, открытых самой программой, не может превышать число 15. При этом следует учесть, что программа наследует от родительской программы все файлы, открытые ею к моменту создания новой программы. Другим ограничителем числа открытых файлов является строка FILES=N в командном файле конфигурации системы Config.sys. Число N задает предельное суммарное число файлов, открытых во всей системе. Именно столько строк имеет таблица открытых файлов. Последнее ограничение нетрудно скорректировать, используя любой текстовый редактор.
Программа
Таблица логич. номеров файлов
Таблица открытых файлов
ОС
Носитель ВП
I – имя физического файла
L – логический номер файла
K — номер БУФ
Рис.29. Результат операции открытия файла
В отличие от таблицы логических номеров, таблица открытых файлов является внутренней таблицей MS-DOS, к которой прикладные программы не имеют непосредственного доступа. Работу с этой таблицей выполняют только подпрограммы ОС, инициируемые в результате системных вызовов. Для создания и открытия файлов используются следующие системные вызовы MS-DOS: INT 21h (функция 3Сh) и INT 21h (функция 3Dh).
INT 21h (функция 3Ch ) – создание и открытие файла. Данный вызов создает и открывает новый файл. Если указанный файл уже существует, то он усекается до нулевой длины. Перед применением вызова программа помещает в какую-то область ОП символьную строку, представляющую собой имя создаваемого файла. Эта строка должна заканчиваться нулевым байтом. При этом имя файла может быть задано в любой из допустимых форм: имя-путь, предваряемое именем логического диска; имя-путь; промежуточное имя файла; простое имя.
В регистры DS и DX программа должна поместить соответственно адрес-сегмент и адрес-смещение области памяти с именем файла. В регистр CX должна быть помещена битовая строка, задающая особые свойства создаваемого файла. При этом установка каждого бита задает одно особое свойство (атрибут) файла. Например, бит1=1 требует, чтобы файл был скрытым (его имя не должно выводиться на экран при выполнении утилиты DIR).
Если создается обыкновенный файл, то все биты-атрибуты должны быть нулевыми.
При успешном завершении системного вызова в программу возвращается флаг FC=0. При этом в регистре AX возвращается логический номер файла.
INT 21h (функция 3Dh ) – открытие существующего файла. Перед применением вызова программа задает в регистрах DS и DX адрес имени открываемого файла (аналогично функции 3Ch).
В регистр AL она помещает режим доступа к файлу . Младшие биты 0-1 режима доступа задают допустимые операции с открытым файлом:
00 – открыть для чтения;
01 – открыть для записи;
10 – открыть для чтения и для записи.
При успешном завершении системного вызова в программу возвращается флаг FC=0, а в регистре AX передается логический номер файла.
4.3.2. Операции чтения и записи
указателем файла
Сразу после открытия файла его указатель содержит 0. Используя специальный системный вызов, программа может записать в эту переменную любое другое значение, что позволяет выполнять информационный обмен с внутренней частью файла напрямую, не просматривая его предыдущих байтов. В результате завершения операции чтения или записи указатель файла содержит номер байта, расположенного сразу же за строкой, обработанной командой. Поэтому, если программа выполняет последовательное чтение (запись) всего файла, отдельная установка его указателя не требуется.
Каждому открытому файлу всегда соответствует только один указатель, несмотря на то что файл может быть одновременно открыт в нескольких программах. (Вспомним, что однопрограммная система допускает одновременное нахождение в ОП нескольких программ, одни из которых находятся между собой в отношении «предок – потомок», а другие являются резидентными.) Наследование открытых файлов дочерней программой, а также наличие единых указателей файлов делают файлы удобным средством информационного обмена между родительской и дочерней программами в том случае, если объем передаваемой информации достаточно велик.
Прикладной буфер
вызовы MS-DOS выполняют установку указателя файла, а также используются для информационного обмена с ним: INT 21h (функция 42h), INT 21h (функция 3Fh), INT 21h (функция 40h).
INT 21h (функция 42h ) – установить указатель файла. Перед применением вызова программа помещает в регистр AL тип требуемого перемещения указателя файла:
0 – перемещение относительно начала файла;
1 – перемещение относительно текущего положения указателя;
2 – перемещение относительно конца файла.
Величина перемещения (в байтах) представляет собой число со знаком, которое программа должна поместить в пару регистров: CX (старшая часть величины перемещения) и DX (младшая часть).
При успешном завершении системного вызова в программу возвращается флаг FC=0, а в регистрах CX и DX возвращается новое значение указателя файла (число байтов от начала файла).
Примечание
Этот вызов часто используется для определения длины файла: достаточно выполнить вызов с CX=0, DX=0, AL=2, тогда в CX:DX будет возвращена длина файла в байтах.
INT 21h (функция 3Fh ) – выполнить чтение из файла. Чтение начинается с той позиции, на которую ранее был установлен указатель файла. Перед применением вызова программа помещает в регистр BX логический номер файла, в регистр CX – число байтов, читаемых из файла, а в регистры DS и DX — соответственно адрес-сегмент и адрес-смещение начала прикладного буфера, в который должно быть выполнено чтение.
При успешном завершении системного вызова в программу возвращается флаг FC=0, а в регистре AX возвращается число фактически считанных байтов. Указатель файла установлен на первый несчитанный байт.
Примечание
INT 21h (функция 40h ) – выполнить запись в файл. Запись начинается с той позиции, на которую ранее был установлен указатель файла. Перед применением вызова программа помещает в регистр BX логический номер файла, в регистр CX – число байтов, записываемых в файл, а в регистры DS и DX — соответственно адрес-сегмент и адрес-смещение прикладного буфера, содержимое которого должно быть записано в файл.
При успешном завершении системного вызова в программу возвращается флаг FC=0, а в регистре AX возвращается число фактически записанных байтов. Указатель файла установлен на байт, следующий за последним записанным байтом.
4.3.3. Закрытие и уничтожение файла
Закрытие файла
В принципе, все файлы, открытые программой, закрываются при ее завершении с помощью системных вызовов, выполняющих возврат в ОС (а точнее – в родительскую программу).
Но следующие два фактора способствуют тому, чтобы программа сама выполняла закрытие файла. Во-первых, так как суммарное число открытых файлов ограничено, то освобождение логического номера файла в результате его закрытия позволяет использовать этот номер для другого файла. Во-вторых, так как при закрытии файла производится сброс буфера записи на диск, то это гарантирует правильность данных в файле при аварийном завершении программы.
Удаление файла
Применительно к FAT удаление файла выполняется следующим образом. Во-первых, первый символ имени файла в родительском каталоге заменяется кодом E5h. Во-вторых, все элементы таблицы FAT, соответствующие кластерам файла, помечаются как свободные. Что касается самих кластеров файла, то их содержимое не меняется до тех пор, пока данный кластер не будет распределен другому файлу.
вызовы MS-DOS используются для закрытия и удаления файлов: INT 21h (функция 3Eh) и INT 21h (функция 41h)
INT 21h (функция 3Eh ) – закрытие файла. Перед применением вызова программа помещает в регистр BX логический номер закрываемого файла.
При успешном завершении системного вызова в программу возвращается флаг FC=0.
INT 21h (функция 41h ) – удалить файл. Перед применением вызова программа помещает в регистры DS и DX соответственно адрес-сегмент и адрес-смещение имени файла (оно задается аналогично функции 3Ch).
При успешном завершении системного вызова в программу возвращается флаг FC=0.
4.3.4. Пример программы
выполняет копирование файла. Имя копируемого файла содержится в хвосте команды MS-DOS, запускающей данную программу. Имя файла-копии программа запрашивает у пользователя сама.
; Программа копирует файл
; ———————————-
; Имя копируемого файла – в хвосте программы, имя копии вводится с клавиатуры
;
- cr EQU 0Dh ; Код ASCII возврата каретки
lf EQU 0Ah ; Код ASCII перевода строки
ASSUME CS:_Text
_Text SEGMENT PUBLIC ‘CODE’
ORG 80h ; Следующий байт имеет адрес-смещение 80h
Lparam DB ? ; Длина хвоста команды
Param DB ? ; Первый байт хвоста
ORG 100h
Start: JMP Begin ; Переход через данные
; Данные программы
Msg1 DB ‘Нет имени файла’, cr, lf, ‘$’ ; Выводимое сообщение
Msg2 DB ‘Копируемый файл отсутствует’, cr, lf, ‘$’ ; —//—
Msg3 DB ‘Введите имя файла-копии’, cr, lf, ‘$’ ; —//—
Msg4 DB ‘Ошибка создания файла’, cr, lf, ‘$’ ; —//—
Msg5 DB ‘Ошибка чтения файла’, cr, lf, ‘$’ ; —//—
Msg6 DB ‘Ошибка записи в файл’, cr, lf, ‘$’ ; —//—
Msg7 DB ‘Копирование выполнено’, cr, lf, ‘$’ ; —//—
Lname0 DB 81 ; Максимальная длина имени файла
Lname DB 0 ; Фактическая длина имени файла
Namefile DB 81 DUP (0) ; Здесь размещается имя файла
Bufer DB 512 DUP(?) ; Буфер для копирования
Lognum1 DW ? ; Логический номер файла 1
Lognum2 DW ? ; Логический номер файла 2
; Команды программы
Begin:
; Поиск и открытие копируемого файла
MOV CL, Lparam ; CL ß Длина хвоста
CMP CL, 0 ; Хвост отсутствует ?
JNZ M1 ; Если хвост есть
MOV DX, OFFSET Msg1 ; Вывод сообщения
MOV AH, 9 ; об отсутствии имени
INT 21h ; копируемого файла
JMP Exit ; На завершение программы
M1: XOR BX, BX ; BX ß 0
MOV BL, Lparam ; BL ß длина хвоста команды
MOV Param[BX], 0 ; Хвост завершается нулем
MOV AH, 3Dh ; Открытие
MOV DX, OFFSET Param+1 ; копируемого
MOV AL, 0 ; файла
INT 21h ; на чтение
JNC M2 ; Если успешно
MOV DX, OFFSET Msg2 ; Вывод сообщения
MOV AH, 9 ; об отсутствии
INT 21h ; копируемого файла
JMP Exit ; На завершение программы
M2: MOV Lognum1, AX ; Сохранение логич. номера файла
; Создание и открытие файла-копии
MOV DX, OFFSET Msg3 ; Вывод сообщения с просьбой
MOV AH, 9 ; ввести имя
INT 21h ; файла-копии
MOV AH, 0Ah ; Функция ввода строки
MOV DX, OFFSET Lname0 ; DX ß адрес-смещение буфера ввода
INT 21h ; Ввод строки
XOR BX, BX ; BX ß 0
MOV BL, Lname ; BX ß длина имени файла
MOV Namefile[BX], 0 ; Запись нулевого байта
MOV AH, 3Ch ; Создание
MOV DX, OFFSET Namefile ; и
MOV CX, 0 ; открытие
INT 21h ; файла
JNC M3 ; Если успешно
MOV DX, OFFSET Msg4 ; Вывод сообщения
MOV AH, 9 ; об ошибке
INT 21h ; создания файла
JMP Exit ; На завершение программы
M3: MOV Lognum2, AX ; Сохранение логич. номера файла
; Копирование файла
M4: MOV AH, 3Fh ; Функция чтения из файла
MOV BX, Lognum1 ; BX ß логич. номер читаемого файла
MOV CX, 512 ; CX ß число читаемых байтов
MOV DX, OFFSET Bufer ; DX ß адрес-смещение буфера
INT 21h ; Чтение из файла в буфер
JNC M5 ; Если успешно
MOV DX, OFFSET Msg5 ; Вывод сообщения
MOV AH, 9 ; об ошибке
INT 21h ; чтения файла
JMP Exit ; На завершение программы
M5: MOV BX, Lognum2 ; BX ß логич. номер файла-копии
MOV CX, AX ; CX ß число записываемых байтов
MOV AH, 40h ; Функция записи в файл
MOV DX, OFFSET Bufer ; DX ß адрес-смещение буфера
INT 21h ; Запись из буфера в файл
JNC M6 ; Если успешно
MOV DX, OFFSET Msg6 ; Вывод сообщения
MOV AH, 9 ; об ошибке
INT 21h ; записи в файл
JMP Exit ; На завершение программы
M6: CMP AX, 512 ; Конец файла?
JZ M4 ; Если нет
; Завершение программы
MOV DX, OFFSET Msg7 ; Вывод сообщения
MOV AH, 9 ; о завершении
INT 21h ; копирования
Exit: MOV AX, 4C00h ; Возврат в DOS с
INT 21h ; кодом завершения 0
_Text ENDS
END Start
Данная программа сначала открывает копируемый файл. Его имя должно поступить в программу в качестве «хвоста» (за исключением первого байта «хвоста», содержащего пробел).
Логический номер файла, полученный в результате открытия, сохраняется для последующего чтения из файла. Далее создается новый файл, имя которого вводится с клавиатуры. Одновременно этот файл открывается для последующей записи в него.
Собственно копирование выполняется циклически. За одну итерацию цикла одна логическая запись (512 байтов) считывается из исходного файла в буфер программы Bufer, а затем переписывается из него в выходной файл. После этого число фактически записанных байтов сравнивается с числом 512. При неравенстве этих чисел делается вывод о достижении конца файла, и копирование прекращается.
Обратим внимание, что программа не выполняет закрытие файлов. Это объясняется, во-первых, небольшим количеством файлов, открываемых в программе. Во-вторых, вскоре после последней операции записи в файл программа завершается.
Рассмотренная программа пригодна для копирования любых обычных (не каталогов) файлов. Например, она может копировать себя. Допустим, что мы поместили загрузочный модуль программы в файл copir.com, тогда следующая команда выполняет подобное копирование:
copir.com copir.com
Полученному файлу-копии можно дать любое имя (например, copir1.com) и содержащаяся в нем программа также может выполнять копирование (проверьте это).
4.3.5. Другие операции
Рассмотренные выше системные вызовы для работы с файлами являются основными, но не единственными. Кратко перечислим некоторые другие функции системного вызова INT 21h, используемые в MS-DOS для работы с обычными файлами (не каталогами), имеющими короткие двенадцатибайтовые имена:
43h – получить или установить атрибуты файла;
56h – переименовать файл;
57h – получить или установить дату и время создания файла.
Кроме того, MS-DOS предоставляет отдельные системные вызовы для работы с логическими дисками и с каталогами:
0Eh – установить текущий логический диск;
19h – получить текущий логический диск;
39h – создать каталог;
3Ah – удалить каталог;
3Bh – установить текущий каталог;
47h – получить текущий каталог.
MS-DOS версии 7.0 предоставляет также системные вызовы для работы с файлами в FAT32, используя их длинные имена. Так как эти же файлы имеют и короткие двенадцатибайтовые имена, то с ними могут работать и ранее рассмотренные системные вызовы.
5. УПРАВЛЕНИЕ ПЕРИФЕРИЙНЫМИ УСТРОЙСТВАМИ
5.1. Введение
Никакая прикладная или системная обрабатывающая программа не может выполняться без операций с ПУ. Кроме стандартных устройств ввода-вывода (например, экран и клавиатура) и внешней памяти (дисководы), существует огромное количество нестандартных периферийных устройств. Подобные устройства используются, например, для управления технологическими процессами. Сюда относятся различные датчики (устройства ввода), а также различные задвижки и вентили (устройства вывода).
Как отмечалось в п.1.3, для работы со стандартными ПУ программа может использовать системные подпрограммы ОС и BIOS, используя для этого соответствующие системные вызовы. При этом программе предоставляется возможность работать с ПУ не только как с устройствами, но и как с файлами. Подобные системные вызовы широко использовались нами в рассмотренных ранее примерах программ, заметно сокращая время программирования.
К сожалению, системных вызовов часто бывает недостаточно для управления ПУ по следующим причинам. Во-первых, время выполнения драйверов BIOS, и особенно MS-DOS, слишком велико. Во-вторых, для многих типов ПУ, например, для нестандартных, соответствующие системные драйверы отсутствуют. Следствием этого является то, что многие прикладные программы выполняют управление ПУ на аппаратном уровне, то есть фактически используют свои собственные драйверы. Далее мы рассмотрим вопросы построения таких драйверов.
Буферный регистр данных
Регистр состояния и управления
1) если b 0 = 1, то разрешается работа ПУ по вводу (выводу) единицы информации;
Рис.30. Пример регистра состояния и управления RS
2) b 1 –b2 используются для уточнения операции (функции), выполняемой устройством;
3) если b 3 = 1, то в ЦП может быть выдан сигнал прерывания.
биты состояния ПУ
Биты b 4 –b6 содержат информацию о том, произошла ли ошибка при выполнении последней операции ввода-вывода. Нулевое содержимое всех этих битов сообщает об отсутствии ошибки, иначе – эти биты содержат код ошибки.
контроллер
портами
В i8086 используется другой подход, при котором существуют два адресных пространства – одно для ячеек ОП, а второе – для портов. При этом адрес порта – число в диапозоне от 0 до 65535, а сам порт представляет собой 8–битный регистр. Следствием наличия двух адресных пространств является использование для работы с портами специальных команд ЦП: IN для ввода из порта и OUT – для вывода в порт.
Одной командой IN можно или ввести в регистр AL байт, или ввести в регистр AX 16-битное слово. Во втором случае ввод осуществляется из двух портов, имеющих смежные адреса, причем порт с меньшим адресом содержит младший вводимый байт. Адрес порта в команде IN можно задать двумя способами: 1) в виде константы; 2) в качестве содержимого регистра DX. Первый из этих способов позволяет использовать в команде IN лишь младшие адреса портов (от 0 до 255).
Второй способ позволяет задавать любые порты (от 0 до 65535).
Примеры:
- IN AL, 50h ; AL ß (50h)
IN AX, 50h ; AX ß (51h : 50h)
IN AL, DX ; AL ß ((DX))
IN AX, DX ; AX ß ((DX)+1 : (DX))
Одна команда OUT позволяет вывести (скопировать) байт из регистра AL в заданный порт или вывести слово из регистра AX в два порта со смежными адресами. Адрес порта задается аналогично команде IN с той лишь разницей, что теперь порт является получателем и должен записываться в качестве первого операнда. Примеры:
- OUT 50h, AL ; (50h) ß (AL)
OUT 50h, AX ; (51h : 50h) ß (AX)
OUT DX, AL ; ((DX)) ß (AL)
OUT DX, AX ; ((DX)+1 : (DX)) ß (AX)
Любой драйвер должен выполнять три основные функции:
1) подготовка ПУ и, возможно, самого драйвера к последующим операциям по обмену данными;
2) инициирование очередной операции ПУ по вводу (выводу) единицы информации;
3) обеспечение завершения очередной операции ввода-вывода.
Реализация перечисленных функций в значительной степени зависит от принципа ввода-вывода, принятого при разработке драйвера. Различают следующие принципы: синхронный, асинхронный с прерываниями, прямой доступ в память, асинхронный с общей памятью.
5.2. Синхронный ввод-вывод
При синхронном вводе-выводе драйвер ожидает завершения текущей операции ввода-вывода активно, занимая в процессе ожидания ЦП. В результате этого нет никакого физического параллелизма между ЦП, с одной стороны, и контроллером и устройством – с другой. Реализацию синхронного принципа рассмотрим на следующем гипотетическом примере. (Реальный пример потребовал бы рассмотрения второстепенных технических деталей, присущих конкретному реальному ПУ.)
Пусть требуется разработать драйвер для управления работой считывателя перфоленты. Структура RS (адрес порта — 90h) приведена на рис.30. Код вводимого символа помещается в RD (адрес порта 91h).
Результаты разработки драйвера рассматриваются в следующей последовательности: 1) логическая схема; 2) программа на ассемблере.
Логическая схема
S – код символа;
- R – признак результата (0 – успешно;
- 1 – ошибка);
b 0 – бит разрешения работы;
b 7 — бит готовности.
Рис.31. Логическая схема синхронного драйвера
Вспомним, что инициирование резидентной подпрограммы возможно только через прерывание. Так как подпрограмму “Ввести символ” вызывают прикладные программы, то они должны использовать для этого команду программного прерывания INT. В качестве номера прерывания (этот номер совпадает с операндом команды INT и с номером вектора прерываний) можно взять любой неиспользуемый в системе номер, например, EEh.
Запрет аппаратных прерываний от управляемого ПУ выполняется потому, что при синхронном вводе-выводе они не нужны. Для выполнения такого запрета в нашем примере достаточно сбросить бит b 3 в регистре RS (см. рис.30).
(На практике для этого можно выдать команду маскирования в программируемый контроллер прерываний (ПКП), который рассматривается в п.5.3.1.)
Логическая процедура «Ввести символ» обслуживает запросы прикладных и системных программ по вводу символов с перфоленты. После своего вызова эта логическая процедура устанавливает в единицу бит b 0 в RS и тем самым инициирует устройство ввода, которое продвигает перфоленту на один шаг. В ожидании завершения этой операции опрашивается в цикле бит b7 в RS. Затем, если нет ошибки, код символа переписывается из RD в S. После этого следует возврат управления в прикладную программу.
Программа
; Синхронный драйвер ввода символов с перфоленты
; ———————————————————————
; Содержит: 1) п/п-у инициализации (вызов из MS-DOS)
; 2) п/п-у вывода символа (вызов командой INT 0EEh)
;
ASSUME CS:_Text
_Text SEGMENT PUBLIC ‘CODE’
ORG 2Ch
Blocokr DW ? ; Адрес блока окружения
ORG 100h
Start: JMP Init ; Переход на инициализацию
;
; Ввод символа — обработчик программного прерывания с номером EEh
; ——————————————————————————————
; Выходы: флаг CF – признак результата (0 – успешно; 1 – ошибка)
; AL – код символа
Begin: STI ; Разрешить маскируемые прерывания
PUSH AX ; Сохранение содержимого
PUSH DS ;
- MOV AX, CS ; DS будет адресовать данные
MOV DS, AX ; резидентной программы
IN AL, 90h ; Чтение RS в AL
OR AL, 00000001b ; Запуск
OUT 90h, AL ;
- Povt: IN AL, 90h ; Чтение RS в AL
TEST AL, 10000000b ; Символ готов?
JZ Povt ; Нет
TEST AL, 01110000b ; Ошибка есть?
JNZ Error ; Да
IN AL, 91h ; Прием символа из RD
CLC ; Сбрасывает флаг переноса CF
JMP Exit
Error: STC ; Устанавливает флаг переноса CF
Exit: POP DS ; Восстановление содержимого
POP AX ;
- IRET ; Возврат в программу из прерывания
; Инициализационная часть программы
Init: MOV AX, 25EEh ; Запись стартового адреса обработчика
MOV DX, OFFSET Begin ; в вектор прерываний
INT 21h ; с номером EEh
IN AL, 90h ; Чтение RS в AL
AND AL, 11110111h ; Запрет прерываний
OUT 90h, AL ;
- MOV AH, 49h ; Освобождение области памяти,
MOV ES, Blocokr ; занимаемой
INT 21h ; блоком окружения
MOV DX, OFFSET Init ; Возврат в DOS, оставшись
INT 27h ; резидентным
_Text ENDS
END Start
Инициализационная часть этой программы выполняется в результате запуска программы из MS-DOS. Так как эта часть нерезидентна, то она размещена в конце программы. В начале своего выполнения она помещает стартовый адрес резидентной части в выбранный вектор прерываний, а затем запрещает прерывания от устройства ввода перфоленты, сбросив бит 3 в RS. Далее производится подготовка резидентной части путем освобождения памяти, занимаемой блоком окружения программы. После этого инициализационная часть выполняет возврат в MS-DOS, сопровождаемый просьбой сделать резидентной начало программы (до метки Init).
Резидентная часть программы инициируется в результате программного прерывания с номером EEh. Выполнение этой части начинается так же, как начинается любой обработчик прерываний: 1) разрешение маскируемых прерываний; 2) сохранение содержимого используемых регистров в стеке; 3) запись в DS адреса-сегмента данных резидентной части. После этого производится запуск устройства ввода перфоленты (установкой бита 0 в RS).
Далее в цикле опрашивается бит 7 в RS до тех пор, пока он не будет установлен в 1. В зависимости от наличия ошибки, перед возвратом в прикладную программу сбрасывается или устанавливается флаг FC.
5.3. Асинхронный ввод-вывод с прерываниями
5.3.1. Контроллер прерываний
При асинхронном вводе-выводе с прерываниями драйвер ожидает завершения текущей операции ввода-вывода пассивно, не занимая для этого ЦП. Поэтому программа, сделавшая запрос на ввод-вывод, может выполнять в это же время (то есть параллельно) какую-то другую свою работу на ЦП. При завершении операции ввода-вывода ИУ выдает в ЦП сигнал прерывания.
программируемый контроллер прерываний
Контроллер i8259A имеет 8 входов – IRQ0, IRQ1…IRQ7, позволяющих принимать запросы на прерывание от восьми ИУ:
- IRQ0 – таймер;
- IRQ1 – клавиатура;
- IRQ2 – второй контроллер прерываний;
- IRQ3 – последовательный порт COM2;
- IRQ4 – последовательный порт COM1;
- IRQ5 – параллельный порт LPT2;
- IRQ6 – гибкий диск;
- IRQ7 – параллельный порт LPT1.
Данные входы имеют приоритеты: чем меньше номер входа, тем приоритет выше. Поэтому самыми приоритетными являются сигналы прерываний от таймера. Приоритет сигнала используется контроллером прерываний для определения возможности направления этого сигнала в ЦП. При этом, если в контроллер одновременно поступили несколько сигналов прерываний, то из них в ЦП будет отправлен тот сигнал, приоритет которого выше.
Кроме того, если ЦП уже занят обработкой какого-то прерывания, то новый сигнал будет отправлен контроллером прерываний только в том случае, если его приоритет выше, чем у того прерывания, обработчик которого выполняется на ЦП. (Вспомним, что для того, чтобы новое прерывание действительно прервало обработку старого прерывания, дополнительно требуется, чтобы в начале обработчика старого прерывания стояла команда STI.)
Номер прерывания, соответствующий конкретному ИУ, можно определить следующим образом: к базовому номеру, соответствующему контроллеру прерываний (8), следует прибавить номер входа в этот контроллер. Например, номер прерывания от клавиатуры: N = 8 + 1 = 9.
Как и любое ИУ, контроллер прерываний имеет порты, которые используются программами (драйверами) для управления им. Эти порты имеют адреса 20h и 21h. Порт 21h используется, в частности, для маскирования (то есть для запрета) прерываний от устройств требуемого типа. Для этого требуется записать единицу в тот бит порта, который соответствует номеру входа для устройства. Например, следующие две команды выполняют маскирование прерываний от клавиатуры:
- MOV AL, 00000010b ; Вход для клавиатуры — IRQ1
OUT 21h, AL
В конце обработчика внешних прерываний следует разрешить обработку прерываний с более низкими приоритетами. Для этого достаточно выполнить две команды:
- MOV AL, 20h ; Число 20h используется для двух
OUT 20h, AL ; разных величин
Существуют другие команды управления контроллером прерываний, позволяющие размаскировать прерывания от любого ИУ (независимо от маскирования в ЦП), а также изменять приоритеты ИУ. Аналогичные команды используются и для управления вторым (ведомым) контроллером прерываний.
Ведомый контроллер подсоединяется к входу IRQ2 ведущего контроллера. Поэтому ИУ, обслуживаемые ведомым контроллером, выдают сигналы прерываний с приоритетами, расположенными между приоритетами клавиатуры и последовательного порта COM2. Для управления ведомым контроллером используются порты A0h и A1h, аналогичные портам 20h и 21h для ведущего контроллера. Базовый номер прерываний, соответствующий ведомому контроллеру прерываний, равен 70h.
Перечислим два типа устройств, обслуживаемых ведомым контроллером прерываний (с указанием его входов):
- IRQ13 – математический сопроцессор;
- IRQ14 – жесткий магнитный диск.
5.3.2.
Алгоритм обработки прерываний
Использование при выдаче внешних маскируемых прерываний контроллера прерываний требует уточнения алгоритма обработки прерываний, рассмотренного в п.2.6.4 :
1) поступление сигнала прерывания в ЦП. Для этого должны быть выполнены действия:
- ИУ посылает сигнал прерывания в i8259A;
- если данный тип прерываний незамаскирован, i8259A выдает сигнал прерывания на вход INTR процессора;
2) собственно прерывание. Оно выполняется ЦП по окончанию выполнения текущей машинной команды и включает действия:
- ЦП возвращает в ИУ сигнал подтверждения;
- ИУ передает по ОШ в ЦП номер прерывания N;
- ЦП помещает текущее содержимое FLAGS, CS и IP в программный стек;
- в IP загружается слово ОП, имеющее адрес 4*N, а в CS – 4*N +2;
- ЦП запрещает маскируемые прерывания путем сброса в нуль флажка IF в регистре FLAGS;
3) начальный этап программной обработки прерывания. Он выполняется обработчиком прерываний и включает действия:
- разрешение маскируемых прерываний с помощью команды STI;
- сохранение в программном стеке содержимого тех регистров, с которыми будет работать программа обработчика;
- запись в сегментный регистр данных DS значения, которое соответствует адресу-сегменту данных обработчика прерываний;
4) действия обработчика прерываний, определяемые типом прерывания;
5) завершение обработки прерывания. Сюда относятся действия:
- запись обработчиком прерываний кода 20h в порт 20h;
- восстановление содержимого регистров, запомненного ранее в стеке;
- выполнение обработчика прерываний завершается командой IRET, которая извлекает из стека прежнее содержимое IP, CS и FLAGS, тем самым возвращая управление в прерванную программу.
5.3.3.
Пример драйвера
В качестве примера опять рассмотрим драйвер ввода с перфоленты. Для связи с ИУ используются порты 90h (RS) и 91h (RD).
Структура RS приведена на рис.30. Допустим, что устройству ввода с перфоленты соответствует вектор прерываний с номером 60h. Кроме того, предположим, что контроллер данного устройства соединен с входом IRQ5 ведущего контроллера прерываний.
Логическая схема
S – код символа;
- R – признак результата (0 – успешно, 1 — символа пока нет);
- N – число занятых позиций в буфере;
b 0 – бит разрешения работы;
b 3 – бит разрешения прерываний.
Рис.32. Логическая схема асинхронного драйвера с прерываниями
Логическая процедура “Инициализация” без параметров. Она выполняет три действия: 1) делает остальные модули драйвера резидентными; 2) заполняет вектор прерываний с номером EEh (для последующего инициирования резидентной процедуры «Ввести символ»; 3) заполняет вектор прерываний с номером 60h (для последующего инициирования обработчика прерываний от перфоленты).
Логическая процедура «Ввести символ» обслуживает запросы прикладных и системных программ по вводу символов с перфоленты. Обработчик прерываний перфоленты – логический процесс без параметров. Рассмотрим совместную работу перечисленных модулей по логической схеме.
После своего вызова логическая процедура «Ввести символ» проверяет значение N. Если N = 0 (в буфере ничего нет), то устанавливаются в «1» биты b 0 и b3 RS. После этого данная логическая процедура выполняет возврат в вызвавший ее модуль, то есть в прикладную программу, с признаком результата R = 1.
Значение b 0 = 1 инициирует работу контроллера (вход 1) по вводу очередного символа. Контроллер помещает код символа в RD (сбрасывая при этом бит b0 RS) и оказывается в состоянии “вход 2”. Так как бит разрешения прерывания b3 = 1, то контроллер инициируется по данному входу и выдает сигнал прерывания. Обработчик прерываний считывает код символа из RD в буфер и увеличивает значение N на единицу. Если при этом буфер оказывается полным (N = NMAX ), то управление сразу возвращается прерванной программе. Иначе сначала установкой b0 = 1 инициируется работа контроллера по вводу следующего символа.
Если при вызове логической процедуры «Ввести символ» 0<N<N MAX , то из буфера выбирается очередной символ и устанавливается R = 0. Если N=NMAX , то перед выполнением данных действий устанавливается b0 =1 (этот бит не был установлен обработчиком прерываний).
очередь
Пусть имеется область памяти длиной десять ячеек, предназначенная для размещения буфера (рис.33).
Кроме того, есть две ячейки ОП, являющиеся указателями: F указывает на очередную пустую ячейку буфера, в которую можно занести новый элемент (символ), а S указывает на ячейку, содержащую первый из еще не взятых элементов. Цикличность буфера заключается в том, что при достижении максимального граничного адреса (на рис.33 этот адрес равен 9) указатель корректируется так, что вместо 9 он будет содержать 0 (то есть будет указывать на начало области памяти).
Рис.33. Циклический буфер и указатели
Заметим, что если указатель «взять» движется в сторону больших адресов (на рис.33 вниз) и становится равным указателю «положить», то очевидно, что буфер пуст, и счетчик элементов равен нулю. А если указатель «положить» «догонит» указатель «взять», то буфер полон и счетчик элементов равен максимально возможному числу (10).
Программа
; Асинхронный драйвер ввода символов с перфоленты
; ———————————————————————
; Содержит: 1) п/п-а инициализации (вызов из MS-DOS)
; 2) п/п-а вывода символа (вызов командой INT 0EEh)
; 3) обработчик прерываний перфоленты (инициируется аппаратно)
;
ASSUME CS:_Text
_Text SEGMENT PUBLIC ‘CODE’
Nmax EQU 20 ; Емкость буфера = 20 символов
ORG 2Ch
Blocokr DW ? ; Адрес блока окружения
ORG 100h
Start: JMP Init ; Переход на инициализацию
; Определение данных
N DB 0 ; Счетчик символов в буфере
S DW 0 ; Указатель «взять»
F DW 0 ; Указатель «положить»
Buf DB Nmax DUP (0) ; Буфер
; Ввод символа — обработчик программного прерывания с номером EEh
; ——————————————————————————————
; Выходы: флаг CF – признак результата (0 – успешно; 1 – символа пока нет)
; AL – код символа
Begin: STI ; Разрешить маскируемые прерывания
PUSH AX ; Сохранение
PUSH BX ; содержимого
PUSH DS ; регистров в стеке
MOV AX, CS ; DS будет адресовать данные
MOV DS, AX ; резидентной программы
CMP N, 0 ; Буфер пуст ?
JNZ Simv ; Если не пуст, переход
IN AL, 90h ; Чтение RS в AL
OR AL, 00001001b ; Запуск устройства
OUT 90h, AL ; и разрешение прерываний от п/л
STC ; Установка флага переноса CF
JMP Exit1
Simv: CMP N, Nmax ; Буфер полон ?
JNZ M1 ; Если не полон, переход
IN AL, 90h ; Чтение RS в AL
OR AL, 00000001b ; Запуск
OUT 90h, AL ; устройства
M1: MOV BX, S ; Запись в AL
MOV AL, Buf[BX] ; символа из буфера
DEC N ; Уменьшение счетчика символов на 1
CMP S, Nmax –1 ; S указывает на конец буфера ?
JZ M2 ; Если да, то переход
INC S ; Увеличение S на 1
JMP M3
M2: MOV S, 0 ; S ß 0
M3: CLC ; Сбрасывает флаг переноса CF
Exit1: POP DS ; Восстановление содержимого
POP BX ;
- POP AX ; из стека
IRET ; Возврат в программу из прерывания
;
; Обработчик аппаратных прерываний с номером 60h от устройства ввода перфоленты
; ————————————————————————————————————-
Obrpr: STI ; Разрешить маскируемые прерывания
PUSH AX ; Сохранение
PUSH BX ; содержимого
PUSH DS ;
- MOV AX, CS ; DS будет адресовать данные
MOV DS, AX ; резидентной программы
IN AL, 91h ; Прием символа из RD в AL
MOV BX, F ; Запись символа из
MOV Buf[BX], AL ; AL в буфер
INC N ; Увеличение счетчика символов на 1
CMP F, Nmax –1 ; F указывает на конец буфера ?
JZ L1 ; Если да, то переход
INC F ; Увеличение F на 1
JMP L2
L1: MOV F, 0 ; F ß 0
L2: CMP N, Nmax ; Буфер полон ?
JZ Exit2 ; Если да, то переход
IN AL, 90h ; Чтение RS в AL
OR AL, 00000001b ; Запуск
OUT 90h, AL ;
- Exit2: MOV AL, 20h ; Разрешение менее приоритетных
OUT 20h, AL ; прерываний
POP DS ; Восстановление содержимого
POP BX ;
- POP AX ; из стека
IRET ; Возврат в программу из прерывания
;
; Инициализационная часть программы
Init: MOV AX, 25EEh ; Запись стартового адреса обработчика
MOV DX, OFFSET Begin ; в вектор прерываний
INT 21h ; с номером EEh
MOV AX, 2560h ; Запись стартового адреса обработчика
MOV DX, OFFSET Obrpr ; в вектор прерываний
INT 21h ; с номером 60h
MOV AH, 49h ; Освобождение области памяти,
MOV ES, Blocokr ; занимаемой
INT 21h ; блоком окружения
MOV DX, OFFSET Init ; Возврат в DOS, оставшись
INT 27h ; резидентным
_Text ENDS
END Start
Приведенная программа состоит из трех модулей, предваряемых данными, совместно используемыми этими модулями. Первый по порядку модуль начинается с метки Begin и представляет собой программную реализацию логической процедуры «Ввести символ». Данный модуль выполнен как резидентный обработчик программных прерываний с номером EEh. (Напомним, что в качестве этого номера разработчик может взять любой неиспользуемый номер.)
Второй модуль начинается с метки Obrpr и представляет собой программную реализацию логического процесса «Обработчик прерываний перфоленты». Этот модуль выполнен как резидентный обработчик внешних аппаратных прерываний с номером 60h.
Третий модуль начинается с метки Init и представляет собой программную реализацию логической процедуры “Инициализация”. Этот модуль выполнен как фрагмент программы, инициируемый при запуске программы из MS-DOS.
5.4. Прямой доступ в память
В рассмотренных выше драйверах для передачи каждого байта данных между ИУ и ОП приходится выполнять несколько машинных команд. Если в программе требуется выполнить ввод-вывод не одного, а целой последовательности байтов, то затраты времени ЦП на их передачу будут весьма существенны. Например, обмен данными с магнитным диском производится посекторно (см. п.4.1), то есть блоками по 512 байт.
контроллера прямого доступа в память (ПДП)
каналом ПДП
0 – предназначен для «освежения» памяти. Он постоянно восстанавливает заряд ячеек ОП;
1 – свободен;
2 – обмен с контроллером гибких дисков;
3 – обмен с контроллером жестких дисков.
регистры управления
Автоинициализация
Тип передачи
1) режим одиночной передачи – для передачи каждого байта в ОП (из ОП) контроллер ПДП запрашивает доступ к ОШ у ЦП;
2) режим блочной передачи – запрос на занятие ОШ делается один раз для всего блока;
3) режим передачи по требованию – контроллер ПДП ожидает подготовку байта контроллером устройства и делает запрос на занятие ОШ;
4) каскадный режим – используется в том случае, если ЭВМ имеет более одного контроллера ПДП.
Рис.34. Структура
Регистр масок
Рис.35. Структура порта для доступа к регистру масок
Регистры канала
регистр начального адреса
регистр страницы
регистр счетчика –
Номера портов, используемых для доступа к регистрам канала, зависят от номера канала. Например, запись в регистры канала 2 производится через следующие 8-битные порты: регистр начального адреса – порт 4h, регистр страницы – порт 81h, регистр счетчика – порт 5h. Так как регистры начального адреса и счетчика 16-битные, то запись в них через 8-битные порты имеет следующую особенность.
Специальный регистр, называемый «защелкой», направляет содержимое 8-битного порта в младший или старший байт 16-битного регистра канала. Перед записью младшего байта в порт, соответствующий регистру канала, необходимо записать какое-нибудь значение в порт 0Ch, что приведет к сбросу «защелки». После записи младшего байта в регистр канала, установка «защелки» производится автоматически, и следующий байт из порта переписывается в качестве старшего байта регистра канала.
Управление контроллером ПДП рассмотрим на примере использования его для информационного обмена с контроллером гибких дисков. Соответствующая логическая схема приведена на рис.36. (На этой схеме не показаны внутренние модули драйвера дисковода, а также опущены взаимосвязи между драйвером и контроллером дисковода.)
Драйвер дисковода предоставляет прикладным и системным программам возможность выполнять информационный обмен с установленным на дисковод магнитным диском. В результате одного обращения к драйверу производится чтение или запись одного сектора диска, то есть блока из 512 байтов, имеющего номер L (0 < L < LMAX ).
Получив от прикладной программы запрос на чтение или запись сектора, драйвер дисковода вызывает логическую процедуру «Инициализация канала 2», входящую в состав драйвера ПДП. Данная процедура имеет два входных параметра: «Начальный адрес прикладного буфера» и «Тип операции» (0 – чтение, 1 – запись).
В процессе выполнения процедуры производится запись в регистры управления и в регистры канала 2. В частности, в регистры начального адреса и страницы помещается начальный адрес прикладного буфера, а в регистр счетчика – число передаваемых байтов, уменьшенное на единицу.
После завершения инициализации канал 2 контроллера ПДП переходит в состояние «Вход 2», ожидая прихода от контроллера дисковода команды «Начать передачу». Эта команда инициирует работу контроллера ПДП по побайтовой передаче данных между контроллером дисковода и ОП.
V – тип операции (0 – чтение, 1 – запись)
An – начальный адрес прикладного буфера
L – номер логического сектора
Рис.36. Логическая схема применения ПДП для информационного
обмена с дисководом
Для передачи очередного байта в ОП (при чтении) или из ОП (при записи) контроллер ПДП выдает в ЦП запрос на занятие ОШ. Далее, при получении согласия он выводит содержимое регистров адреса и страницы на шину адреса, а в контроллер дисковода передает сигнал о том, чтобы тот вывел на шину данных очередной передаваемый байт (при чтении) или считал байт с шины данных (при записи).
После этого контроллер ПДП увеличивает (или уменьшает) на единицу содержимое регистра адреса и уменьшает на единицу счетчик байтов. При достижении счетчиком нуля контроллер ПДП или выдает в ЦП сигнал прерывания, или сообщает о завершении передачи в контроллер дисковода.
Логическая процедура «Инициализация» выполняет две функции: 1) делает процедуры инициализации каналов резидентными; 2) заполняет векторы прерываний для доступа к этим резидентным процедурам. Программная реализация логической процедуры «Инициализация» аналогична реализации соответствующих процедур, рассмотренных ранее (см. п. 5.2 и 5.3).
Логическая процедура «Инициализация канала 2» включает следующие шаги:
Шаг 1. В зависимости от типа операции (чтение или запись) сделать запись в регистр режима. При этом следует учесть следующие характеристики режима:
- автоинициализация выключена;
- текущий адрес при обмене увеличивается;
- тип передачи одиночный.
Шаг 2. Вычисление полного 20-битного адреса буфера в ОП и загрузка этого адреса в регистр начального адреса и в регистр страницы. Для вычисления полного адреса необходимо просуммировать адрес-сегмент, предварительно умноженный на 16, с адресом-смещением. Полученные четыре старших бита адреса записываются в регистр страницы, а остальные 16 битов – в регистр адреса.
Шаг 3. Запись в регистр счетчика числа байтов, предназначенных для пересылки, уменьшенного на единицу, то есть числа 511.
Шаг 4. Запись в регистр маски контроллера ПДП числа, разрешающего работу канала 2.
Шаг 5. Возврат из процедуры.
Ниже приведен фрагмент программы драйвера ПДП, выполняющий инициализацию канала 2. Так как данный фрагмент резидентен, то он вызывается через программное прерывание. В качестве номера прерывания выбрано число EDh.
; Подготовка канала 2 ПДП к чтению-записи сектора гибкого диска – обработчик
; программного прерывания с номером EDh
; ———————————————————————————————————
; Входы: DX:BX – адрес буфера (сегмент:смещение)
; АL – тип операции (0 – чтение, 1 – запись)
;
- STI ; Разрешить маскируемые прерывания
PUSH AX ; Сохранение
PUSH BX ; содержимого
PUSH CX ;
- PUSH BP ; в стеке
CMP АL, 0 ; Операция – чтение ?
JNZ Sap ; Нет
MOV AL, 01000110b ; Задание режима для
OUT 0Bh, AL ; чтения диска
JMP Adr
Sap: MOV AL, 01001010b ; Задание режима для
OUT 0Bh, AL ; записи на диск
Adr: MOV AX, DX ; Адрес-сегмент à AX
MOV BP, 16
MUL BP ; (AX)*(BP) à DX:AX
ADD BX, AX ; Младшие 16 бит адреса à BX
MOV CX, 0 ; 0 à CX
ADC DX, CX ; Старшие биты адреса à DX
MOV AL, DL ; Запись в
OUT 81h, AL ;
- OUT 0Ch, AL ; Сброс «защелки»
MOV AL, BL ; Запись младшего байта
OUT 4h, AL ; в регистр начального адреса
MOV AL, BH ; Запись старшего байта
OUT 4h, AL ; в регистр начального адреса
OUT 0Ch, AL ; Сброс «защелки»
MOV AX, 511 ; Запись младшего байта
OUT 5h, AL ; счетчика байтов
MOV AL, AH ; Запись старшего байта
OUT 5h, AL ; счетчика байтов
MOV AL, 2 ; Размаскирование
OUT 0Ah, AL ; канала 2
POP BP ; Восстановление
POP CX ; содержимого
POP BX ; регистров
POP AX ; из стека
IRET ; Возврат в программу из прерывания
5.5. Асинхронный вывод с общей памятью
5.5.1. Видеоадаптер
Принцип асинхронного вывода с общей памятью заключается в том, что ЦП и ИУ имеют доступ к общей области памяти, используя ее в качестве буфера для информационного обмена между ними. При этом операции записи и чтения с общей памятью ЦП и ИУ производят асинхронно, то есть независимо друг от друга. Данный принцип используется широко для управления экраном (дисплеем).
Видеопамять
Видеоадаптер (в том числе и CGA) имеет два принципиально разных режима работы – текстовый и графический. Для каждого из этих режимов CGA имеет несколько форматов. Примером формата для текстового режима является формат 80*25 – 25 строк по 80 символов в каждой строке.
Центральным модулем CGA является контроллер экрана CRT . Он устанавливает и поддерживает режим работы экрана, выполняет основную работу по интерпретации кодов ASCII, а также управляет курсором. Кроме контроллера экрана, CGA имеет порты ввода-вывода, ПЗУ с матрицей знаков, видеопамять, а также 18 регистров управления (рис.37).
Рис.37. Структура адаптера дисплея
В зависимости от выполняемых функций все 18 управляющих регистра контроллера можно разделить на две группы:
1) регистры, фиксирующие горизонтальные и вертикальные параметры экрана;
2)
К первой группе относятся первые десять регистров с номерами от 0 до 9. Они устанавливаются один раз при задании режима работы экрана. (Это делает BIOS при включении ЭВМ в сеть.) Далее будем считать, что к началу выполнения любых программ на ЦП экран установлен в текстовый режим 80*25.
Регистры визуализации приведены в таблице 1. Они 8-битные. Некоторые из них связаны в пары, чтобы хранить 16-битовые величины. Все регистры визуализации можно разделить на три группы:
1) регистры управления курсором;
2) регистры светового пера;
3) сканирования. Этот адрес представляет собой порядковый номер в видеопамяти того символа, который будет выводиться в верхнем левом углу экрана. Например, это может быть номер 0.
Таблица 1
Регистры визуализации
Номер регистра |
Назначение регистра |
Операции |
10 |
начало курсора |
запись |
11 |
конец курсора |
запись |
12 |
начальный адрес сканир-я (ст.) |
запись |
13 |
начальный адрес сканир-я (мл.) |
запись |
14 |
адрес курсора (ст.) |
чтение/запись |
15 |
адрес курсора (мл.) |
чтение/запись |
16 |
световое перо (ст.) |
чтение |
17 |
световое перо (мл.) |
чтение |
Что касается портов, то их в CGA два (см. рис.37):
1) порт с адресом 3D4h, называемый также индексным регистром RI;
2) порт с адресом 3D5h, называемый также
Регистр RI используется для задания требуемого управляющего регистра. А регистр RD используется для записи нового содержимого того управляющего регистра, на который указывает RI. Ниже будет рассмотрено применение этих портов для управления курсором.
5.5.2. Видеопамять
Видеопамять в CGA имеет размер 16 Кбайт и начинается по адресу B8000h (соответствующий логический адрес — B800h:0000h).
Каждый символ занимает в видеопамяти два байта. В младшем из этих байтов содержится код ASCII символа (информация о том, что выводить), а в старшем байте – атрибуты символа (информация о том, как выводить).
Структура байта атрибутов приведена на рис.38. В таблице 2 приведены цвета, которые можно получить, задавая комбинации битов красного, зеленого и синего цветов, а также интенсивности.
дисплейной
Рис.38. Структура байта атрибутов
Таблица 2
Генерация цвета
R |
G |
B |
I |
Цвета |
0 |
0 |
0 |
0 |
черный |
0 |
0 |
1 |
0 |
синий |
0 |
1 |
0 |
0 |
зеленый |
0 |
1 |
1 |
0 |
голубой |
1 |
0 |
0 |
0 |
красный |
1 |
0 |
1 |
0 |
сиреневый |
1 |
1 |
0 |
0 |
коричневый |
1 |
1 |
1 |
0 |
белый |
0 |
0 |
0 |
1 |
серый |
0 |
0 |
1 |
1 |
ярко — синий |
0 |
1 |
0 |
1 |
ярко — зеленый |
0 |
1 |
1 |
1 |
ярко — голубой |
1 |
0 |
0 |
1 |
ярко — красный |
1 |
0 |
1 |
1 |
ярко — сиреневый |
1 |
1 |
0 |
1 |
желтый |
1 |
1 |
1 |
1 |
белый (повышен. интенсивности) |
Рис.39. Содержимое видеопамяти
обратным ходом луча
Для того чтобы вывести символ с определенными атрибутами в заданную позицию экрана, необходимо выполнить последовательность действий:
1) рассчитать смещение L относительно начала видеопамяти;
2) записать по адресу B800:L код ASCII выводимого символа;
3) записать по адресу B800:L +1 байт атрибутов выводимого символа.
Для расчета L можно воспользоваться формулой:
L = (80*Y T + XT )*2,
где X T ,YT – текущие координаты (столбец и строка) курсора.
5.5.3. Управление курсором
управление курсором
Общепринято, что курсор должен указывать на то место экрана, куда будет выведен следующий символ. Если мы используем системные вызовы MS-DOS или BIOS, то их подпрограммы сами устанавливают курсор в нужное положение. Но если мы программируем на уровне портов, то должны сами позаботиться об этом. Так как за отображение курсора на экране отвечает CRT, то для управления курсором необходимо выполнить действия по программированию этого контроллера.
Для того чтобы курсор был виден на экране, его координаты могут меняться в пределах 25 строк (0…24) и 80 столбцов (0…79), то есть в пределах экрана. При этом положение курсора содержится в регистрах 14 и 15 (см. табл.1) как число от 0 до 1999, что соответствует 2000 (25*80) позициям экрана. Если содержимое регистров 14 и 15 изменить, то положение курсора также изменится. Для этого достаточно выполнить действия:
1) поместить в RI запрос на использование регистра 15;
2) поместить в RD младший байт позиции курсора;
3) поместить в RI запрос на использование регистра 14;
4) поместить в RD старший байт позиции курсора.
После выполнения этих действий курсор будет немедленно перемещен контроллером на заданную позицию экрана.
Форма курсора может меняться – от тонкой линии до максимального размера, отводимого под символ. Это обеспечивается за счет того, что курсор строится из коротких горизонтальных отрезков, верхний из которых называется начальной строкой курсора, а нижний – конечной строкой. В CGA для каждого символа (а, следовательно, и для курсора) отводятся только 8 строк, пронумерованных начиная сверху от 0 до 7. Значения начальной и конечной строк содержатся соответственно в управляющих регистрах 10 и 11. Запись в эти регистры выполняется точно также, как и изложенная выше запись в регистры 14 и 15. (Заметим, что интересный эффект получается при задании начальной строки больше конечной.)
5.5.4. Логическая схема
На рис. 40 приведена логическая схема простейшего драйвера экрана. Этот драйвер позволяет любой системной или прикладной программе вывести на экран символ с заданным цветом. Драйвер состоит из двух логических процедур, доступных извне драйвера: «Инициализация экрана», «Вывод символа». Кроме того, драйвер включает внутреннюю процедуру «Установка курсора», а также две структуры данных — переменные X T иYT , содержащие текущие координаты курсора. Использование этих переменных позволяет повысить скорость пересчета новых координат курсора при обработке символов “Возврат каретки” и “Перевод строки”.
S – код символа
М – цвет символа (0 < M < 7)
X T ,YT – текущие координаты курсора
Рис.40. Логическая схема драйвера экрана
Логическая процедура “Инициализация экрана” не имеет параметров. Она выполняет первоначальную подготовку экрана (через его адаптер) к последующей работе с ним, а также выполняет первоначальную подготовку самого драйвера. Алгоритм этой логической процедуры включает шаги:
Шаг 1. Очистка используемой (нулевой) видеостраницы путем заполнения ее символами пробела с фоном требуемого цвета.
Шаг 2. Задание нулевой видеостраницы в качестве отображаемой на экране. Для этого в регистры начального адреса сканирования (см. табл.1) следует поместить 0.
Шаг 3. Задание максимальной толщины курсора.
Шаг 4. Запись нулевых значений в переменные X T иYT .
Шаг 5. Установка курсора в начальную позицию экрана.
Шаг 6. Объявление резидентным модуля “Вывод символа”.
В том случае, если драйвер экрана предназначен для обслуживания одной-двух прикладных программ, инициируемых достаточно редко, шаг 6 отсутствует. Так как в этом случае нецелесообразно постоянно держать драйвер в ОП. Гораздо лучше объединить его и прикладную программу в единый загрузочный модуль.
Логическая процедура “Вывод символа” имеет два входных параметра: S – код символа, М – цвет символа (0 < M < 7).
Двоичное представление числа M определяет цвет в соответствии с таблицей 2. Например, M=2=010b задает зеленый цвет символов. Алгоритм данной логической процедуры:
Шаг 1. Если символ S есть «Возврат каретки» или «Перевод строки», то переход на шаг 3.
Шаг 2. Запись символа и его атрибутов в видеопамять.
Шаг 3. Запись новых значений в переменные X T и YT .
Шаг 4. Установка следующей позиции курсора.
Внутренняя процедура «Установка курсора» не имеет параметров. Она выполняет установку курсора в ту позицию, которая соответствует значениям переменных X T и YT .
Программная реализация данного драйвера выполняется студентами во время первой лабораторной работы.
6. ЛАБОРАТОРНЫЕ РАБОТЫ
Введение
В процессе выполнения данных лабораторных работ студенты должны получить навык по программированию драйверов для управления ПУ. При этом в качестве ПУ рассматриваются наиболее доступные и распространенные устройства – экран и клавиатура.
При выполнении лабораторных работ требуется доступ к MS-DOS, запускаемой в среде операционной системы Windows 95 или 98. А также требуется наличие системных программ: 1) Tasm – транслятор ассемблера; 2) Tlink – редактор связей; 3) Debug – отладчик.
Результаты лабораторных работ оформляются в виде файлов, пересылаемых в ТМЦДО на дискете. Все исходные программы (файлы с расширением asm) должны быть снабжены программными комментариями. При этом особое внимание следует уделить вводным комментариям, поясняющим назначение и интерфейсы программных модулей.
Лабораторная работа №1 , ПРОГРАММИРОВАНИЕ ДРАЙВЕРА ЭКРАНА
Задание
Требуется реализовать программно нерезидентный драйвер экрана с видеоадаптером CGA, логическая схема которого приведена на рис.40. Кроме того, требуется разработать прикладную программу, выполняющую вывод на экран ваших фамилии и имени, и, возможно, другой информации (по вашему усмотрению), используя для вывода на экран ваш драйвер. Цвет символов, выводимых на экран, а также цвет фона должны быть выбраны из таблицы 3 в зависимости от номера вашего варианта.
Таблица 3
К |
Цвет символов |
Цвет фона |
1 |
черный |
зеленый |
2 |
синий |
коричневый |
3 |
зеленый |
красный |
4 |
голубой |
черный |
5 |
красный |
сиреневый |
6 |
сиреневый |
зеленый |
7 |
коричневый |
голубой |
8 |
белый |
черный |
9 |
серый |
ярко-красный |
10 |
ярко-синий |
ярко-зеленый |
11 |
ярко-зеленый |
ярко-белый |
12 |
ярко-голубой |
ярко-красный |
13 |
ярко-красный |
желтый |
14 |
ярко-сиреневый |
ярко-синий |
15 |
желтый |
ярко-голубой |
16 |
ярко-белый |
желтый |
17 |
серый |
ярко-красный |
18 |
ярко-синий |
серый |
19 |
ярко-зеленый |
ярко-синий |
20 |
ярко-голубой |
желтый |
Результат выполнения работы оформляется в виде двух файлов с расширением asm (исходные тексты прикладной программы и драйвера) и одного файла с расширением com (загрузочный модуль прикладной программы и драйвера).
Все файлы должны быть помещены в каталог LAB1.